pax_global_header00006660000000000000000000000064145646662300014526gustar00rootroot0000000000000052 comment=6a8797927c1ef9df1f56513b742f01de86f5167f ModemManager-1.23.4-dev/000077500000000000000000000000001456466623000147255ustar00rootroot00000000000000ModemManager-1.23.4-dev/.editorconfig000066400000000000000000000003231456466623000174000ustar00rootroot00000000000000root = true [*] end_of_line = lf trim_trailing_whitespace = true insert_final_newline = false [{*.c,*.h}] indent_style = space indent_size = 4 tab_width = 4 [meson.build] indent_size = 2 indent_style = space ModemManager-1.23.4-dev/.gitignore000066400000000000000000000000511456466623000167110ustar00rootroot00000000000000*~ *.bz2 *.pyc tags /.vscode /cscope.out ModemManager-1.23.4-dev/.gitlab-ci.yml000066400000000000000000000200231456466623000173560ustar00rootroot00000000000000include: - project: freedesktop/ci-templates ref: 290b79e0e78eab67a83766f4e9691be554fc4afd file: - templates/ubuntu.yml stages: - container prep - build .common_variables: variables: FDO_UPSTREAM_REPO: mobile-broadband/ModemManager FDO_DISTRIBUTION_VERSION: '20.04' FDO_DISTRIBUTION_TAG: '2023-01-03.1' FDO_DISTRIBUTION_PACKAGES: ca-certificates git gcc meson ninja-build gawk libgettextpo-dev libgirepository1.0-dev libglib2.0-dev libgudev-1.0-dev python3-dbus python3-gi autopoint xsltproc dbus gettext gtk-doc-tools libglib2.0-doc gobject-introspection python-is-python3 libsystemd-dev libpolkit-gobject-1-dev valac libdbus-1-dev bash-completion udev policykit-1 help2man LIBQMI_BRANCH: 'main' LIBQRTR_BRANCH: 'main' LIBMBIM_BRANCH: 'main' build container: extends: - .fdo.container-build@ubuntu - .common_variables stage: container prep only: - main - merge_requests - tags - schedules before_script: - export BUILD_MBIM=true - export BUILD_QMI=true - export BUILD_QRTR=true .prepare_deps: &prepare_deps - > if [ "$BUILD_MBIM" == "true" ]; then git clone --depth 1 --branch ${LIBMBIM_BRANCH} https://gitlab.freedesktop.org/mobile-broadband/libmbim.git pushd libmbim meson setup build --prefix=/usr -Dgtk_doc=false -Dintrospection=false -Dbash_completion=false ninja -C build ninja -C build install popd fi - > if [ "$BUILD_QRTR" == "true" ]; then git clone --depth 1 --branch ${LIBQRTR_BRANCH} https://gitlab.freedesktop.org/mobile-broadband/libqrtr-glib.git pushd libqrtr-glib meson setup build --prefix=/usr -Dgtk_doc=false -Dintrospection=false ninja -C build ninja -C build install popd fi - > if [ "$BUILD_QMI" == "true" ]; then git clone --depth 1 --branch ${LIBQMI_BRANCH} https://gitlab.freedesktop.org/mobile-broadband/libqmi.git pushd libqmi meson setup build --prefix=/usr -Dgtk_doc=false -Dintrospection=false -Dbash_completion=false -Dmbim_qmux=$BUILD_MBIM -Dqrtr=$BUILD_QRTR -Dcollection=basic ninja -C build ninja -C build install popd fi build-no-qmi: stage: build extends: - .fdo.distribution-image@ubuntu - .common_variables only: - main - merge_requests - tags - schedules script: - export BUILD_QRTR=false - export BUILD_QMI=false - *prepare_deps - meson setup build --prefix=/usr -Dwerror=true -Dgtk_doc=false -Dqmi=false -Dqrtr=false -Dmbim=true - ninja -C build - ninja -C build install build-qmi-qrtr-no-mbim: stage: build extends: - .fdo.distribution-image@ubuntu - .common_variables only: - main - merge_requests - tags - schedules script: - export BUILD_MBIM=false - *prepare_deps - meson setup build --prefix=/usr -Dwerror=true -Dgtk_doc=false -Dqmi=true -Dqrtr=true -Dmbim=false - ninja -C build - ninja -C build install build-qmi-no-qrtr-no-mbim: stage: build extends: - .fdo.distribution-image@ubuntu - .common_variables only: - main - merge_requests - tags - schedules script: - export BUILD_MBIM=false - export BUILD_QRTR=false - *prepare_deps - meson setup build --prefix=/usr -Dwerror=true -Dgtk_doc=false -Dqmi=true -Dqrtr=false -Dmbim=false - ninja -C build - ninja -C build install build-no-qmi-no-mbim: stage: build extends: - .fdo.distribution-image@ubuntu - .common_variables only: - main - merge_requests - tags - schedules script: - export BUILD_MBIM=false - export BUILD_QRTR=false - export BUILD_QMI=false - *prepare_deps - meson setup build --prefix=/usr -Dwerror=true -Dgtk_doc=false -Dqmi=false -Dqrtr=false -Dmbim=false - ninja -C build - ninja -C build test - ninja -C build install build-no-qmi-no-mbim-no-tests: stage: build extends: - .fdo.distribution-image@ubuntu - .common_variables only: - main - merge_requests - tags - schedules script: - export BUILD_MBIM=false - export BUILD_QRTR=false - export BUILD_QMI=false - *prepare_deps - meson setup build --prefix=/usr -Dwerror=true -Dgtk_doc=false -Dqmi=false -Dqrtr=false -Dmbim=false -Dtests=false - ninja -C build - ninja -C build install build-no-deprecated: stage: build extends: - .fdo.distribution-image@ubuntu - .common_variables only: - main - merge_requests - tags - schedules script: - export BUILD_MBIM=false - export BUILD_QRTR=false - export BUILD_QMI=false - *prepare_deps - CFLAGS="-DMM_DISABLE_DEPRECATED" meson setup build --prefix=/usr -Dwerror=true -Dgtk_doc=false -Dqmi=false -Dqrtr=false -Dmbim=false - ninja -C build - ninja -C build test - ninja -C build install build-no-suspend-resume: stage: build extends: - .fdo.distribution-image@ubuntu - .common_variables only: - main - merge_requests - tags - schedules script: - *prepare_deps - meson setup build --prefix=/usr -Dwerror=true -Dgtk_doc=false -Dsystemd_suspend_resume=false -Dpowerd_suspend_resume=false - ninja -C build - ninja -C build install build-suspend-resume-powerd: stage: build extends: - .fdo.distribution-image@ubuntu - .common_variables only: - main - merge_requests - tags - schedules script: - *prepare_deps - meson setup build --prefix=/usr -Dgtk_doc=false -Dsystemd_suspend_resume=false -Dpowerd_suspend_resume=true - ninja -C build - ninja -C build install build-single-plugins: stage: build extends: - .fdo.distribution-image@ubuntu - .common_variables only: - main - tags - schedules script: - *prepare_deps - for PLUGIN in $(grep "plugin_" meson_options.txt | awk -F"'" '{ print $2 }'); do meson setup build_$PLUGIN --prefix=/usr -Dwerror=true -Dgtk_doc=false -Dintrospection=false -Dbash_completion=false -Dauto_features=disabled -D$PLUGIN=enabled; ninja -C build_$PLUGIN; done build-single-plugins-builtin: stage: build extends: - .fdo.distribution-image@ubuntu - .common_variables only: - main - tags - schedules script: - *prepare_deps - for PLUGIN in $(grep "plugin_" meson_options.txt | awk -F"'" '{ print $2 }'); do meson setup build_$PLUGIN --prefix=/usr -Dgtk_doc=false -Dintrospection=false -Dbash_completion=false -Dauto_features=disabled -D$PLUGIN=enabled -Dbuiltin_plugins=true; ninja -C build_$PLUGIN; done build-debug: stage: build extends: - .fdo.distribution-image@ubuntu - .common_variables only: - main - merge_requests - tags - schedules script: - *prepare_deps - meson setup build --buildtype=debug --prefix=/usr -Dwerror=true -Dgtk_doc=true -Dpolkit=strict -Dsystemdsystemunitdir=/lib/systemd/system - ninja -C build - ninja -C build test - ninja -C build install - ninja -C build uninstall build-release: stage: build extends: - .fdo.distribution-image@ubuntu - .common_variables only: - main - merge_requests - tags - schedules script: - *prepare_deps - meson setup build --buildtype=release --prefix=/usr -Dwerror=true -Dgtk_doc=true -Dpolkit=strict -Dsystemdsystemunitdir=/lib/systemd/system - ninja -C build - ninja -C build test - ninja -C build install - ninja -C build uninstall build-release-builtin: stage: build extends: - .fdo.distribution-image@ubuntu - .common_variables only: - main - merge_requests - tags - schedules script: - *prepare_deps - meson setup build --buildtype=release --prefix=/usr -Dwerror=true -Dgtk_doc=true -Dpolkit=strict -Dsystemdsystemunitdir=/lib/systemd/system -Dbuiltin_plugins=true - ninja -C build - ninja -C build test - ninja -C build install - ninja -C build uninstall ModemManager-1.23.4-dev/AUTHORS000066400000000000000000000077351456466623000160110ustar00rootroot00000000000000 Current maintainers: Dan Williams Aleksander Morgado Previous maintainers: Tambet Ingo Other contributors: Ben Chan Daniele Palmas Carlo Lobrano Nathan Williams Lubomir Rintel Dylan Van Assche Andrew Lassalle Sven Schwermer Michal Mazur Iñigo Martínez Eric Caruso Teijo Kinnunen Giacinto Cifelli Torgny Johansson Akash Aggarwal Prakash Pabba Marco Bascetta Pavan Holla Riccardo Vangelisti Alexander Sack Frederic Martinsons Guido Günther Joel Selvaraj Loic Poulain Stephan Gerhold Yegor Yefremov Eric Shienbrood Freedom Liu Som_SP Bjørn Mork Elly Jones som Bob Ham Ivan Mikhanchuk Maxim Anisimov David McCullough Nagi Marupaka Prathmesh Prabhu Thieu Le Thomas Tuttle Jack Song Torsten Hilbrich Marius B. Kotsbak Michael Biebl Paul Bartell Alexey Orishko Alfonso Sánchez-Beato Amol Lad Andrey Skvortsov Christian Persch Louis-Alexis Eyraud QuectelDuke Thomas Sailer okaestne Colin Walters Franko Fang Jakub Sitnicki Jason Glasgow Jeroen Elebaut Matthew Starr Pablo Barciela Reinhard Speyerer Yunlian Jiang ZhangMingjie lvmaorui ori inbar Christian Taedcke Clayton Craft David Rochberg Fanice.luo Jiří Klimeš Krzysztof Drobinski Madhav Martyn Russell Thomas Haller Tomas Jura Ulrich Mohr Alexander Dahl Alexander Yashin Alyssa Ross Arnaud Ferraris Benoît Monin Colin Helliwell David Leonard Evangelos Ribeiro Tzaras Fanice Luo Graham Inggs Heiko Thiery Javier Viguera Jessy Diamond Exum Justin Standring Konrad Zapałowicz Lionel Landwerlin Maksim Salau Martin Pitt Matthew Stanger Milo Casagrande Nick Stevens Norbert Frese NorwayFun Piotr Drąg Teemu Ikonen Thomas Bechtold Vincent Palatin Yuri Chornoivan diekleinekuh Adrian Bunk Adrien Plazas Alexander Couzens Amit Mendapara Anders Jonsson Andika Triwidada Andrew Bird Angus Ainslie Anton Blanchard ArenM Arnd Hannemann Arto Jantunen Arun Raghavan Balázs Úr Baruch Siach Beniamino Galvani Benjamin Tissoires Brendan Peter Brian Norris Brian, Sam Bruce A. Johnson Bryan Duff Claude Paroz Dan Carpenter Daniele David (Pololu) David Castellanos David Herrmann David Härdeman David Khouya Dmitry Ivanyushin Dmitry Skorykh Dušan Kazik Enrico Mioso Eugene Crosser Evan Nemerson Fabrice Bellet Fabrice Fontaine Fangxiaozhi (Franko) Felipe Borges Felix Janda Florence Chan Florian Eckert Fran Dieguez Gerald Richter Greg Suarez Iain Lane Jakob Hauser Jarvis-Jiang-G Jason Simmons Jens Seidel Johny Mattsson Jordi Mas Julian Oes Jun Woo Lee Jürgen Benvenuti Khem Raj Kirill Buksha Kristian Sloth Lauszus Krzysztof Kotlenga Lech Perczak LiuQiFeng Luis A. Lozano Lukas Senger Lukas Voegl M. I. Spozta Marc Murphy Marcus Folkesson Mario Blättermann Mario Limonciello Mark-Jablonsky Matthew Via Michael Farrell Michał Sroczyński Mingjie Zhang Mohammed Sadiq Moo Murithi Borona Nathan Follens Nathan J. Williams Nicholas Smith Noel J. Bergman Oliver Kästner Oskar Enoksson Piotr Figiel Quentin.Li Rafael Fontenelle Roshan Pius Rukun Mao Sabri Unal Sam Spilsbury Sebastian Krzyszkowiak Shawn Guo Simon Deziel SunXinzhao Sławomir Bocheński Tabor Kelly Theodore A. Roth Thilo-Alexander Ginkel Thomas Dendale Thomas Grenman Thomas Voß Timo Jyrinki Ting-Yuan Huang Tom Bechtold Tom Goetz Tom Wimmenhove Tore Anderson Tyler Ujjwal Pande Ulrich Ölmann Undef Valentin Blot Ville Skyttä Vincent Untz Vitaly Gimly Walter Hagstrom Xeonacid Xiao Liyun Yaron Shahrabani Zrshuo Zhang amol.lad carlyin dchard emintufan goapunk guihkx kuonirat mozzwald mstanger poma root scootergrisen wi24rd wicadmin yparitcher Артемий Судаков ModemManager-1.23.4-dev/COPYING000066400000000000000000000432541456466623000157700ustar00rootroot00000000000000 GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 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 Lesser 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. Copyright (C) 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. 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. , 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 Lesser General Public License instead of this License. ModemManager-1.23.4-dev/COPYING.LIB000066400000000000000000000636421456466623000164000ustar00rootroot00000000000000 GNU LESSER GENERAL PUBLIC LICENSE Version 2.1, February 1999 Copyright (C) 1991, 1999 Free Software Foundation, Inc. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. [This is the first released version of the Lesser GPL. It also counts as the successor of the GNU Library Public License, version 2, hence the version number 2.1.] Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public Licenses are intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This license, the Lesser General Public License, applies to some specially designated software packages--typically libraries--of the Free Software Foundation and other authors who decide to use it. You can use it too, but we suggest you first think carefully about whether this license or the ordinary General Public License is the better strategy to use in any particular case, based on the explanations below. When we speak of free software, we are referring to freedom of use, 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 and use pieces of it in new free programs; and that you are informed that you can do these things. To protect your rights, we need to make restrictions that forbid distributors to deny you these rights or to ask you to surrender these rights. These restrictions translate to certain responsibilities for you if you distribute copies of the library or if you modify it. For example, if you distribute copies of the library, whether gratis or for a fee, you must give the recipients all the rights that we gave you. You must make sure that they, too, receive or can get the source code. If you link other code with the library, you must provide complete object files to the recipients, so that they can relink them with the library after making changes to the library and recompiling it. And you must show them these terms so they know their rights. We protect your rights with a two-step method: (1) we copyright the library, and (2) we offer you this license, which gives you legal permission to copy, distribute and/or modify the library. To protect each distributor, we want to make it very clear that there is no warranty for the free library. Also, if the library is modified by someone else and passed on, the recipients should know that what they have is not the original version, so that the original author's reputation will not be affected by problems that might be introduced by others. Finally, software patents pose a constant threat to the existence of any free program. We wish to make sure that a company cannot effectively restrict the users of a free program by obtaining a restrictive license from a patent holder. Therefore, we insist that any patent license obtained for a version of the library must be consistent with the full freedom of use specified in this license. Most GNU software, including some libraries, is covered by the ordinary GNU General Public License. This license, the GNU Lesser General Public License, applies to certain designated libraries, and is quite different from the ordinary General Public License. We use this license for certain libraries in order to permit linking those libraries into non-free programs. When a program is linked with a library, whether statically or using a shared library, the combination of the two is legally speaking a combined work, a derivative of the original library. The ordinary General Public License therefore permits such linking only if the entire combination fits its criteria of freedom. The Lesser General Public License permits more lax criteria for linking other code with the library. We call this license the "Lesser" General Public License because it does Less to protect the user's freedom than the ordinary General Public License. It also provides other free software developers Less of an advantage over competing non-free programs. These disadvantages are the reason we use the ordinary General Public License for many libraries. However, the Lesser license provides advantages in certain special circumstances. For example, on rare occasions, there may be a special need to encourage the widest possible use of a certain library, so that it becomes a de-facto standard. To achieve this, non-free programs must be allowed to use the library. A more frequent case is that a free library does the same job as widely used non-free libraries. In this case, there is little to gain by limiting the free library to free software only, so we use the Lesser General Public License. In other cases, permission to use a particular library in non-free programs enables a greater number of people to use a large body of free software. For example, permission to use the GNU C Library in non-free programs enables many more people to use the whole GNU operating system, as well as its variant, the GNU/Linux operating system. Although the Lesser General Public License is Less protective of the users' freedom, it does ensure that the user of a program that is linked with the Library has the freedom and the wherewithal to run that program using a modified version of the Library. The precise terms and conditions for copying, distribution and modification follow. Pay close attention to the difference between a "work based on the library" and a "work that uses the library". The former contains code derived from the library, whereas the latter must be combined with the library in order to run. GNU LESSER GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License Agreement applies to any software library or other program which contains a notice placed by the copyright holder or other authorized party saying it may be distributed under the terms of this Lesser General Public License (also called "this License"). Each licensee is addressed as "you". A "library" means a collection of software functions and/or data prepared so as to be conveniently linked with application programs (which use some of those functions and data) to form executables. The "Library", below, refers to any such software library or work which has been distributed under these terms. A "work based on the Library" means either the Library or any derivative work under copyright law: that is to say, a work containing the Library or a portion of it, either verbatim or with modifications and/or translated straightforwardly into another language. (Hereinafter, translation is included without limitation in the term "modification".) "Source code" for a work means the preferred form of the work for making modifications to it. For a library, 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 library. Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running a program using the Library is not restricted, and output from such a program is covered only if its contents constitute a work based on the Library (independent of the use of the Library in a tool for writing it). Whether that is true depends on what the Library does and what the program that uses the Library does. 1. You may copy and distribute verbatim copies of the Library's complete 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 distribute a copy of this License along with the Library. 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 Library or any portion of it, thus forming a work based on the Library, 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) The modified work must itself be a software library. b) You must cause the files modified to carry prominent notices stating that you changed the files and the date of any change. c) You must cause the whole of the work to be licensed at no charge to all third parties under the terms of this License. d) If a facility in the modified Library refers to a function or a table of data to be supplied by an application program that uses the facility, other than as an argument passed when the facility is invoked, then you must make a good faith effort to ensure that, in the event an application does not supply such function or table, the facility still operates, and performs whatever part of its purpose remains meaningful. (For example, a function in a library to compute square roots has a purpose that is entirely well-defined independent of the application. Therefore, Subsection 2d requires that any application-supplied function or table used by this function must be optional: if the application does not supply it, the square root function must still compute square roots.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Library, 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 Library, 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 Library. In addition, mere aggregation of another work not based on the Library with the Library (or with a work based on the Library) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may opt to apply the terms of the ordinary GNU General Public License instead of this License to a given copy of the Library. To do this, you must alter all the notices that refer to this License, so that they refer to the ordinary GNU General Public License, version 2, instead of to this License. (If a newer version than version 2 of the ordinary GNU General Public License has appeared, then you can specify that version instead if you wish.) Do not make any other change in these notices. Once this change is made in a given copy, it is irreversible for that copy, so the ordinary GNU General Public License applies to all subsequent copies and derivative works made from that copy. This option is useful when you wish to copy part of the code of the Library into a program that is not a library. 4. You may copy and distribute the Library (or a portion or derivative of it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you 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. If distribution of 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 satisfies the requirement to distribute the source code, even though third parties are not compelled to copy the source along with the object code. 5. A program that contains no derivative of any portion of the Library, but is designed to work with the Library by being compiled or linked with it, is called a "work that uses the Library". Such a work, in isolation, is not a derivative work of the Library, and therefore falls outside the scope of this License. However, linking a "work that uses the Library" with the Library creates an executable that is a derivative of the Library (because it contains portions of the Library), rather than a "work that uses the library". The executable is therefore covered by this License. Section 6 states terms for distribution of such executables. When a "work that uses the Library" uses material from a header file that is part of the Library, the object code for the work may be a derivative work of the Library even though the source code is not. Whether this is true is especially significant if the work can be linked without the Library, or if the work is itself a library. The threshold for this to be true is not precisely defined by law. If such an object file uses only numerical parameters, data structure layouts and accessors, and small macros and small inline functions (ten lines or less in length), then the use of the object file is unrestricted, regardless of whether it is legally a derivative work. (Executables containing this object code plus portions of the Library will still fall under Section 6.) Otherwise, if the work is a derivative of the Library, you may distribute the object code for the work under the terms of Section 6. Any executables containing that work also fall under Section 6, whether or not they are linked directly with the Library itself. 6. As an exception to the Sections above, you may also combine or link a "work that uses the Library" with the Library to produce a work containing portions of the Library, and distribute that work under terms of your choice, provided that the terms permit modification of the work for the customer's own use and reverse engineering for debugging such modifications. You must give prominent notice with each copy of the work that the Library is used in it and that the Library and its use are covered by this License. You must supply a copy of this License. If the work during execution displays copyright notices, you must include the copyright notice for the Library among them, as well as a reference directing the user to the copy of this License. Also, you must do one of these things: a) Accompany the work with the complete corresponding machine-readable source code for the Library including whatever changes were used in the work (which must be distributed under Sections 1 and 2 above); and, if the work is an executable linked with the Library, with the complete machine-readable "work that uses the Library", as object code and/or source code, so that the user can modify the Library and then relink to produce a modified executable containing the modified Library. (It is understood that the user who changes the contents of definitions files in the Library will not necessarily be able to recompile the application to use the modified definitions.) b) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (1) uses at run time a copy of the library already present on the user's computer system, rather than copying library functions into the executable, and (2) will operate properly with a modified version of the library, if the user installs one, as long as the modified version is interface-compatible with the version that the work was made with. c) Accompany the work with a written offer, valid for at least three years, to give the same user the materials specified in Subsection 6a, above, for a charge no more than the cost of performing this distribution. d) If distribution of the work is made by offering access to copy from a designated place, offer equivalent access to copy the above specified materials from the same place. e) Verify that the user has already received a copy of these materials or that you have already sent this user a copy. For an executable, the required form of the "work that uses the Library" must include any data and utility programs needed for reproducing the executable from it. However, as a special exception, the materials to be 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. It may happen that this requirement contradicts the license restrictions of other proprietary libraries that do not normally accompany the operating system. Such a contradiction means you cannot use both them and the Library together in an executable that you distribute. 7. You may place library facilities that are a work based on the Library side-by-side in a single library together with other library facilities not covered by this License, and distribute such a combined library, provided that the separate distribution of the work based on the Library and of the other library facilities is otherwise permitted, and provided that you do these two things: a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities. This must be distributed under the terms of the Sections above. b) Give prominent notice with the combined library of the fact that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 8. You may not copy, modify, sublicense, link with, or distribute the Library except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense, link with, or distribute the Library 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. 9. 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 Library or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Library (or any work based on the Library), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Library or works based on it. 10. Each time you redistribute the Library (or any work based on the Library), the recipient automatically receives a license from the original licensor to copy, distribute, link with or modify the Library 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 with this License. 11. 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 Library at all. For example, if a patent license would not permit royalty-free redistribution of the Library 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 Library. 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. 12. If the distribution and/or use of the Library is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Library 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. 13. The Free Software Foundation may publish revised and/or new versions of the Lesser 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 Library 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 Library does not specify a license version number, you may choose any version ever published by the Free Software Foundation. 14. If you wish to incorporate parts of the Library into other free programs whose distribution conditions are incompatible with these, 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 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE LIBRARY "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 LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. 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 LIBRARY 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 LIBRARY (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 LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), 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 Libraries If you develop a new library, and you want it to be of the greatest possible use to the public, we recommend making it free software that everyone can redistribute and change. You can do so by permitting redistribution under these terms (or, alternatively, under the terms of the ordinary General Public License). To apply these terms, attach the following notices to the library. 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. Copyright (C) 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 Street, Fifth Floor, Boston, MA 02110-1301 USA Also add information on how to contact you by electronic and paper mail. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the library, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the library `Frob' (a library for tweaking knobs) written by James Random Hacker. , 1 April 1990 Ty Coon, President of Vice That's all there is to it! ModemManager-1.23.4-dev/NEWS000066400000000000000000002110341456466623000154250ustar00rootroot00000000000000 ModemManager 1.22.0 ------------------------------------------- This is a new stable release of ModemManager. The following notes are directed to package maintainers: * This version now requires: ** libmbim >= 1.30.0 (for the optional MBIM support) ** libqmi >= 1.34.0 (for the optional QMI support) * The GNU autotools (autoconf/automake/libtool) support has been fully removed, this release requires the use of the meson build system. * Build: ** Flag types are now generated in their own files (e.g. 'mm-flags-types.h'). ** A new 'fuzzer' option is available to build LLVM LibFuzzer based unit testers. ** A new 'builtin_plugins' option is available to integrate all built plugins within the ModemManager daemon binary. This makes the daemon load much quicker as it doesn't dynamically load the plugins on runtime. The most important features and changes in this release are the following: * A new "MSG" (message) log verbosity level is introduced, which is also the new default one if none explicitly defined. This level takes the place of the old "INFO" level, as a level including the most important messages that should be logged without needing to be warnings or errors. The new "INFO" level is more verbose than "MSG" but less verbose than "DBG", and may be useful as default in systems where active debugging of WWAN related issues is required. E.g. all user operations triggered via DBus method calls are logged in "INFO" level. * Introduced the concept of "personal information" which should by default not be included in log messages. Enabling personal information in logs requires to run the daemon with the '--log-personal-info' option. This feature is mostly implemented for QMI and MBIM specific logs, but hasn't yet been included in generic daemon logs or when using the AT protocol. Changes and fixes related to this feature will be cherry-picked and included in the future stable branch updates. * API: ** Added new 'Modem.Physdev' property to unconditionally publish the physical modem device path. ** Updated the 'Modem.GetCellInfo()' method to include serving cell type and bandwidth information in LTE and 5GNR cells. ** New 'Modem3gpp.SetCarrierLock()' method to send the list of carrier network information to the modem. ** The 'SetLogging()' method allows a new "MSG" log level. * Core: ** The codebase has been updated to use GTask in all operations and there are no longer any deprecated methods from GLib being used. ** The port probing logic is now able to guess port type hints during runtime, e.g. based on the kernel driver in use or the wwan subsystem port 'type' attributes. ** Default probing and wait times have been increased, in order to cope with slow kernel events reporting new port additions. * Modem interface: ** Updated the logic loading unlock retries to allow restarting the check count based on the state reported by the device (e.g. if the SIM is detected but not fully ready during the last expected check, a new set of check retries is allowed). * 3GPP interface: ** Added support for packet service state updates via unsolicited indications. ** Added support for ignoring registration and packet service state updates if the interface has already been disabled. ** Updated initialization sequence to allow showing IMEI even if the device is locked. ** Logged available profiles in "INFO" level whenever they are listed. * Simple interface: ** Logged user provided settings during a connect attempt in "INFO" level. ** Added support to abort an ongoing connect operation at any step in the state machine, e.g. if the modem is going to be disabled. ** Added support to explicitly request attaching to packet service during the connection attempt. * Signal interface: ** Added logic to rate-limit printing the extended signal information with "INFO" level, even if the actual refresh rate is higher. * New udev tags: ** The 'ID_MM_REQUIRED' port-specific udev tag allows users to specify that the modem must be able to succesfully probe and use the given control port. ** The 'ID_MM_MAX_MULTIPLEXED_LINKS' udev tag allows user to specify the maximum amount of multiplexed links the modem supports. This tag may be set to 0 to fully disable multiplexing support in the device. * mmcli: ** New '--messaging-create-sms-with-text' action to create a text SMS from the contents of a given file. ** New '--3gpp-set-carrier-lock' action to send the carrier lock data to the device. ** Updated '--modem' to always allow using the modem physdev path for lookup. * SMS: ** Improved the parsing of 3GPP and CDMA SMS parts, fixing multiple different errors that lead to unexpected crashes on SMS reception. * MBIM: ** Updated device model loading logic to use an AT port if available. ** Added support for the carrier lock feature using the Google MBIM service. ** Implemented support for reporting serving cell type and bandwidth in the cell scan operation. ** Limited the amount of different APN types usable when MS extensions are unsupported. ** Updated packet service state info loading to use the values reported by the device instead of attempting to guess them. ** Updated IPv6 IP addressing logic to rely on whether the provided address is global or not to decide whether static or dynamic addressing is needed. * QMI: ** Implemented support for detecting and processing profile change indications, e.g. during a SIM card IMSI refresh operation. ** Implemented support for scanning LTE and 5GNR cells. ** Fixed operating mode update logic to avoid needing to receive indications, as old devices don't support them. ** Implemented multiplexing support for Qualcomm PCI devices using the MHI driver. ** Updated logic to read IPA tx/rx offload values from sysfs, making the IPA support work seamlessly in multiple different Qualcomm SoCs. ** Updated logic reading the SIM card operator identifier to avoid using the NAS service, as that sometimes involved actual network access. The new logic exclusively uses SIM card read operations (e.g. IMSI and EFad). ** Consolidated the logic processing registration state from responses and indications. * Plugins: ** cinterion: implemented multiple-SIM support. ** fibocom: disabled multiplexing in the NL668, which doesn't support it. ** huawei: updated time interface to use ^NWTIME. ** quectel: added port type hints for the EG12, EC21, EM060, EM061 and new EM120, EM160 and RM520 variants. The following features which were backported to 1.20.x releases are also present in ModemManager 1.22.0: ** build: new 'examples' option to allow disabling the installation of examples. ** mmcli: improved JSON output in network scan results. ** mmcli: improved JSON output when creating SMS messages. ** MBIM: ignored SIM related indications during a SIM slot switch operation. ** MBIM: updated capabilities loading to use Microsoft Extensions if available. ** MBIM: updated supported modes loading to use the CustomDataClass field contents. ** QMI: fixed processing and exposing PCOs. ** cinterion: improved management of the ^SWWAN? operation. ** telit: improved support for 5G modems. ** messaging: allowed Delete operation during enabling/disabling. ModemManager 1.20.0 ------------------------------------------- This is a new stable release of ModemManager. The following notes are directed to package maintainers: * This version now requires: ** libmbim >= 1.28.0 (for the optional MBIM support) ** libqmi >= 1.32.0 (for the optional QMI support) * There is no longer an upstream-provided source tarball, please use the signed git tag '1.20.0' instead to refer to the sources of this release. * This release supports meson as the primary build system. The GNU autotools support is considered deprecated and will no longer be used in future major releases. The 1.20.x releases will be the last ones including GNU autotools support. The most important features and changes in this release are the following: * API: ** New 'Modem.Sar' interface to allow the host to manage the SAR power level. ** New 'Modem.GetCellInfo()' method, that allows querying information about the current serving and neighboring cells. Currently including 'cell-type' and 'serving' fields for all cell types, plus additional type-specific fields: *** CDMA: 'nid', 'sid', 'base-station-id', 'ref-pn' and 'pilot-strength'. *** GSM: 'operator-id', 'lac', 'ci', 'timing-advance', 'arfcn', 'base-station-id' and 'rx-level'. *** UMTS: 'operator-id', 'lac', 'ci', 'frequency-fdd-ul', 'frequency-fdd-dl', 'frequency-tdd', 'uarfcn', 'psc', 'rscp', 'ecio' and 'path-loss'. *** TDSCDMA: 'operator-id', 'lac', 'ci', 'uarfcn', 'cell-parameter-id', 'timing-advance', 'rscp' and 'path-loss'. *** LTE: 'operator-id', 'tac', 'ci', 'physical-ci', 'earfcn', 'rsrp', 'rsrq' and 'timing-advance'. *** 5GNR: 'operator-id', 'tac', 'ci', 'physical-ci', 'nrarfcn', 'rsrp', 'rsrq', 'sinr' and 'timing-advance'. ** New 'access-type-preference', 'roaming-allowance', 'profile-name', 'profile-enabled' and 'profile-source' fields in the 'Bearer.Properties' property, that can also be used in both 'Modem.Simple.Connect()' and 'Modem.CreateBearer()'. ** New 'Modem.Modem3gpp.SetPacketServiceState()' method and 'Modem.Modem3gpp.PacketServiceState' property, which allow management the explicit attach or detach to packet service on the current registered network. ** New 'Modem.Modem3gpp.SetNr5gRegistrationSettings()' method and 'Modem.Modem3gpp.Nr5gRegistrationSettings' property, to allow management of 5G specific settings like 'mico-mode' or 'drx-cycle'. ** New 'start-date', 'uplink-speed' and 'downlnk-speed' fields in the 'Bearer.Stats' property. ** New 'Bearer.ReloadStatsSupported' property to indicate whether reloading ongoing stats is supported or not. ** New 'Modem.Modem3gppProfileManager.IndexField' property, to indicate which field is to be used as unique index in the profile management operations. ** New 'Sim.SimType', 'Sim.EsimStatus' and 'Sim.Removability' properties to improve the management of eSIM related operations. ** New 'Sim.Gid1' and 'Sim.Gid2' properties, which allow identifying SIM cards that should have different settings applied. ** New 'Modem.Signal.SetupThresholds() method, 'Modem.Signal.RssiThreshold' and 'Modem.Signal.ErrorRateThreshold' to allow configuring thresholds so that the modem emits indications whenever the signal quality values change based on those thresholds. For RSSI a delta amount of dBm can be given, and for error rate just a boolean to enable or disable the corresponding event. ** New 'error-rate' fields in the 'Modem.Signal.Cdma', 'Modem.Signal.Evdo', 'Modem.Signal.Gsm', 'Modem.Signal.Umts', 'Modem.Signal.Lte' and 'Modem.Signal.Nr5g' properties. * Core: ** Detecting an eSIM without an active profile in the current SIM slot will lead to the modem being in Failed state, in the same way as if the slot was for a physical SIM and no SIM was inserted. ** Default amount of AT probing attempts is updated to 6, to cope with modems that are slower to boot. ** New '--test-mbimex-profile-management' option in the daemon, to enable support for profile management operations using the Microsoft extensions. This is an optional feature because it requires using the 'apn-type' field as unique index, which not all users of the profile management API may expect. ** Implemented some initial support to automatically hide personal details (e.g. phone numbers, SMS contents...) from logs, right now only applicable to QMI and MBIM logs. Updated support for this feature will keep on being integrated in future 1.20.x updates. The new '--log-personal-info' option in the daemon allows to disable this feature completely. ** Added new filter match option for subsystem vendor id, required in several PCI based Qualcomm modules. ** QCDM port probing will no longer automatically run for all plugins, only in those that explicitly require it. ** Implemented support for suspend/resume detection based on ChromeOS' powerd daemon. ** Added Cat-M and NB-IoT LPWA access technologies. * Modem interface: ** On 3GPP+3GPP2 multimode devices, a missing SIM card will now force the modem into Failed state, as if it was a 3GPP-only device. * Simple interface: ** Explicitly wait for PS domain to be attached during a connection attempt. * 3GPP interface: ** Updated to report domain registration changes altogether whenever possible (e.g. when using the QMI or MBIM protocols). * MBIM: ** Implemented support to use the Microsoft-defined MBIM extensions v2 and v3 whenever supported by the device. The ModemManager daemon will negotiate which version to use with the modem, so that the highest version supported is enabled. This negotiation applies to the whole device, so any other user of the MBIM device will automatically start using the newly agreed version. ** Implemented current modes switching using MBIMEx v2.0 extensions. ** Trigger explicit disconnection if a connection attempt fails. ** Modem will be flagged as unusable if 10 consecutive MBIM requests timeout. ** Enabled multiplex support for devices in the WWAN subsystem. * QMI: ** Updated logic to by default prefer 'Signal Info' over the deprecated 'Signal Strength' operations. ** Updated logic to by default prefer 'System Info' over the deprecated 'Serving System' operations. ** Updated power state transition logic to ensure the new state is reached before returning success to the user. ** Implemented support for the 'Modem3gppProfileManager.Updated' signal using PDC refresh notifications. ** Modem will be flagged as unusable if 10 consecutive QMI requests timeout. ** Implemented DTMF support. ** Implemented support for automatic SIM IMSI switch detection under certain roaming scenarios. ** Updated the logic to move the PS domain state to 'registered' based on the DSD System Status indications. ** Updated endpoint type detection logic to be based on the net driver. ** Updated endpoint number selection to be based on the data port. ** Updated connection logic to allocate separate WDS clients per endpoint. ** Added support for PCO reporting. * plugins: ** quectel: Added FCC unlock support for the EM05-G. ** telit: Band management updated to prefer using QMI whenever available, as well as to detect the #BND command format automatically. ** telit: Added support for LPWA modems. ** fibocom: Implemented optional support to power down and up the modem during the update of the initial EPS bearer settings. ** fibocom: Implemented initial EPS bearer settings management in the MA510. ** intel: New generic plugin for Intel PCI devices with vid 0x8086. ** xmm: Prefer GNSS control ports explicitly tagged via udev tags. ** foxconn: Use the new FOX QMI service to load firmware version. ** cinterion: Added support for PCIe based devices. The following features which were backported to 1.18.x releases are also present in ModemManager 1.20.0: * core: added support for external FCC unlock scripts that the user must manually enable; automatic FCC unlocking will no longer happen. * core: added support for connection/disconnection notification dispatcher scripts, used primarily in the openwrt integration in order to report network initiated disconnects. * udev: fixed shell globing patterns in rules. * sms: default send timeout updated to 5min. * sms: merge WDP multipart CDMA WAP messages. * qmi: fixed capabilities management in multimode 4G/5G devices. * mbim: fixed network initiated disconnections by not comparing context type. * qcom-soc: plugin enabled by default in build. * foxconn: added support for T99W265 modems. * fibocom: added support for FM101 modems. * cinterion: added support for PLSx3w modems. * telit: added support for new LE910S1, LE910Cx and LN920 compositions. ModemManager 1.18.0 ------------------------------------------- This is a new stable release of ModemManager. The following notes are directed to package maintainers: * This version now requires: ** glib2 >= 2.56 ** libgudev >= 232 ** libmbim >= 1.26.0 (for the optional MBIM support) ** libqmi >= 1.30.2 (for the optional QMI support) ** libqrtr-glib >= 1.0.0 (for the optional QRTR support) * The ModemManager.service file for systemd integration provided in the sources is updated as follows: ** 'CAP_NET_ADMIN' is now required in the 'CapabilityBoundingSet' field. ** 'AF_NETLINK' and 'AF_QIPCRTR' are now required in the 'RestrictAddressFamilies' field. If the system where ModemManager is being integrated provides a custom systemd service configuration, these updates should be considered. * The LEGACY and PARANOID filter types that were allowed options in the '--filter-policy' option in the ModemManager daemon were deprecated in version 1.16.0 and have now been completely removed, along with the vid:pid blocklist of devices and the vid:pid greylist of RS232<->USB adapters. * Building from git no longer requires autoconf-archive, the needed AX_ macros are now shipped inside m4/. * In addition to building from a source release tarball, or building from git checkouts using the GNU autotools suite (autoconf/automake/libtool), this release includes the initial support for the meson build system. The meson port is not fully complete yet, as there are some missing things in the doc generation and test steps, but for system integration or development purposes, the port should be fully operational. This major release, including all its stable updates in the 1.18.x series, will be the last ones providing support for GNU autotools. The next major release will likely be a meson-only one, and will therefore not be based on a source release tarball any more, but on specific git tags instead. The most important features and changes in this release are the following: * Data session multiplexing can now be enabled in QMI and MBIM modems, e.g. so that multiple different APNs can be connected separately over a single network interface. The multiplexing is disabled by default in this release, except for cases where it's required (e.g. if non-multiplexed sessions aren't supported) like IPA based Qualcomm SoCs. Users can request the multiplexing support explicitly via settings when creating the connection bearer object. In order to allow easy testing of the multiplexing feature without requiring any additional change in the stack, a new '--test-multiplex-requested' option in the daemon allows to switch the default (when not explicitly given by the user) to attempt to use multiplexing. It is worth noting that when multiplexing is enabled, the data network interface used by the modem will be a virtual network interface created in runtime, and will therefore have a different name than the real network interface exposed by the modem. If there are additional settings in the system relying on the data network interface name (e.g. iptables rules), they may need to be updated. * The ModemManager daemon can run now in a 'quick suspend/resume' mode, in which no explicit data disconnection is triggered on suspend, and no explicit device re-probing from scratch is launched on resume. Instead, the daemon will try to refresh the state of all interfaces upon suspend, e.g. to see if the module keeps registered to the same operator, to see if it is still connected, and so on. This mode of operation is useful when the WWAN module stays awake while the host is suspended, and can be enabled with the '--test-quick-suspend-resume' option in the daemon. * API: ** New '3gppProfileManager' interface, providing operations on the list of connection profiles stored in the 3GPP module. This interface is implemented for all AT, QMI and MBIM protocols. ** New 'DisableFacilityLock()' method in the 3GPP interface, implemented for QMI and MBIM devices. ** The 'MaxBearers' property is now deprecated, as it didn't provide any additional information to what 'MaxActiveBearers' already provides. ** New 'MaxActiveMultiplexedBearers' property, listing how many bearers can be connected at the same time if multiplexing is enabled. ** New settings in the bearer properties, applicable to both the 'Simple.Connect()' and 'Modem.CreateBearer()' methods: *** 'multiplex': which allows the user to specify whether multiplexing should be avoided ('none'), whether it should be mandatory ('required') or whether it should be enabled if available or skipped if unavailable ('requested'). *** 'profile-id': which allows the user to request a connection attempt with an existing profile stored in the module. *** 'apn-type': which allows the user to specify the purpose of a given connection, e.g. the user could create a connection to an APN providing default internet connectivity and another one to an APN providing access to the MMS gateway. This setting may or may not be stored in the module itself, it depends on the type of module. ** New 'Multiplexed' boolean property in the Bearer object, specifying whether the bearer is connected through a multiplexed interface. ** New 'ConnectionError' property in the bearer object, specifying the last error reported by the module during a failed connection attempt or during a network-initiated disconnection. ** Updated the list of enum values in the MMMobileEquipmentError' type, according to the error codes defined in v17.1.0 of 3GPP TS 27.007. * Core: ** iconv() features support is detected at runtime, and logged when the daemon starts. ** Updated the base modem object to allow plugins to specify the types of data ports they support, based on the specific plugin implementations, e.g. so that a modem supporting only AT+PPP can ignore NET ports and vice versa. ** Added support for modems exposing control ports via QRTR channels. * Modem interface: ** The Dual SIM logic that would iterate over all slots during initialization is updated, so that we only report the information that we can gather without any explicit slot change. E.g. with QMI we can know whether there is a SIM in the non-active slot, and the ICCID of that SIM, but we cannot know the MCCMNC or the operator name of the SIM unless we change to that slot. We must not do slot changes arbitrarily like that, and so that logic is removed, even if we lose some of the information that we were providing in the interface. * Location interface: ** The multi-sentence NMEA trace support is updated to include additional possible trace types in addition to GSV (e.g. ALM, GSV, RTE, SFI) and also when coming from other constellations, not just GPS. * SIM: ** New 'PreferredNetworks' property and 'SetPreferredNetworks' method, implemented using '+CPOL' for generic AT modems and 'NAS Get/Set Preferred Networks' for QMI modems. Several different modules and plugins (e.g. Sierra Wireless EM7345, Telit LN930, SIM7070, all Option and Iridium devices...) have this feature explicitly disabled due to '+CPOL' not behaving properly (even crashing the module sometimes). * QMI: ** The logic that decides which data mode (802.3 or raw-ip) is used in modules managed by the qmi_wwan driver changes in this release. Until now, if a module reported itself as configured in 802.3 mode on boot, that mode would be the one used in normal operation. Due to the new multiplexing feature, this is no longer true, and if possible the daemon will always try to switch the module to raw-ip, and fallback to 802.3 only if raw-ip is unsupported. ** Enabled both AT and QMI indications for the messaging and voice interfaces so that new SMS and call events are reported via both channels. This solves issues seen in the Pinephone when waking up from suspend. ** Enabled network reject indications. ** If operator name not updated through standard indications, it will be explicitly queried with 'NAS Get Plmn Name'. ** Added support for transfer-route MT messages. ** Increased the QMI open timeout to 45s, as required by the newest modules. ** Implemented additional logic to read the status of the different facility locks in the module. ** Updated ICCID reading logic to parse it as hex instead of BCD. ** Improved handling of the MNC PCS digit in the operations involving MCCMNC. ** Automatically run the 'DPM Open Port' logic on IPA based setups to bind the hardware tx/rx endpoints with the logical ones in the QMI protocol. ** Implemented support for the Voice interface and its operations, not only standard voice call management, but also support for the supplementary services. Voice call management will be done completely using QMI, even if the new call indications are notified via AT URCs. * MBIM: ** Implemented support for Dual SIM in non-QMI MBIM devices, using the Microsoft Basic Connect Extensions service. ** Increased the timeout for the MBIM_CID_HOME_PROVIDER query to 30s. ** Updated to load model string using QMI over MBIM if available. ** Increased the MBIM open timeout to 45s, as required by the newest modules. * SMS: ** Defined a common timeout of 180s for all send operations. * libmm-glib: ** Updated with new methods and types to handle all the DBus API updates. ** Extended with additional methods in the Location3gpp object to get/set the full operator MCCMNC string, instead of integers without MNC PCS digit info. ** Extended the 'ModemLocation' interface with methods to get the signaled location updates; i.e. without requiring an explicit GetLocation(), and obviously only supported when location signaling is explicitly enabled. ** Updated the way the internal monitored properties are handled in the different types, now using some handy helper macros to share the same logic among all. * Plugins: ** zte: disabled CIND/CMER support. ** qcom-soc: added support for QRTR+IPA based setups. ** qcom-soc: added support for the WWAN subsystem instead of RPMSG. ** quectel: enabled QGPSXTRA by default when starting the GNSS engine. ** quectel: add support for EM120/160 PCIe modules. ** quectel: added Firehose update method. ** ublox: added additional URAT combinations. ** ublox: flagged UBANDSEL as unsupported in the SARA-R4 and -N4 modules. ** cinterion: added new custom MBIM based modem with shared reset operation. ** cinterion: ignored the MBIM Intel Firmware Update service completely. ** foxconn: added custom carrier config setup for the T77W968 module. The following features which were backported to 1.16.x releases are also present in ModemManager 1.18.0: * core: added support for the new 'WWAN' subsystem in Linux kernel 5.13, enabling PCIe-only modules. * core: The charset conversion methods rework, including the avoiding of the iconv() //TRANSLIT extension support, which isn't available in all libc implementations. * qmi: the logic managing allowed/preferred modes was fixed for multimode devices like the MC7304, making sure the acquisition order preference always had the same items. * serial: when modem is connected with AT+PPP, ignore forced disconnections, so that we don't take ownership of the PPP port before pppd has released it. * foxconn: added support for the T99W175 (SDX55) module, including built-in FCC unlock procedure. * foxconn: added new MBIM QDU firmware update method. ModemManager 1.16.0 ------------------------------------------- This is a new stable release of ModemManager. The following notes are directed to package maintainers: * This version now requires: ** libqmi >= 1.28.0 (for the optional QMI support) * The 1.16.x branch will be the last one supporting the 'LEGACY' and 'PARANOID' filter modes; standard distributions are advised to use the default 'STRICT' mode if they aren't using it already (i.e. running the daemon without any explicit '--filter-policy' option). * A new 'qcom-soc' plugin is implemented to be able to use ModemManager in Qualcomm SoCs like the MSM8916 or MSM8974. This plugin uses a combination of RPMSG based control ports plus BAM-DMUX based network ports. This plugin is disabled by default, even when `--enable-all-plugins` is used, and if wanted it must be explicitly enabled with `--enable-plugin-qcom-soc`. Systems targeting this kind of SoCs, like postmarketos, should enable it. Standard distributions may or may not include it, up to the targeted hardware in each distribution. * Gentoo's 'libelogind' library may now be used to detect the systemd suspend/resume support. The API is backwards compatible with the previous releases, the only updates are the following: * Modem interface: ** Updated the 'Ports' property so that it exposes all ports that are owned by the modem even if they are explicitly ignored and not used. ** New 'SimSlots' property that exposes the available SIM slots in the modem, including the SIM object paths in each of them if the cards are present. ** New 'PrimarySimSlot' property indicating which of the slots in the 'SimSlots' array is the one currently active. ** New 'SetPrimarySimSlot' method to select which SIM slot in the 'SimSlots' array should be considered active. When the switch happens, the modem will be fully re-probed. * Signal interface: ** New 'Nr5g' dictionary property including signal information for the 5GNR access technology. * SIM interface: ** New 'Active' boolean property, indicating whether the SIM object is the currently active one. ** New 'Eid' string property, indicating the EID of the card, if any. * New udev tags: ** New 'ID_MM_PORT_TYPE_QMI' tag to explicitly flag a port as being QMI, when there is no other way to guess the type of port; e.g. this tag is not needed for ports exposed by the qmi_wwan driver. ** New 'ID_MM_PORT_TYPE_MBIM' tag to explicitly flag a port as being MBIM, when there is no other way to guess the type of port; e.g. this tag is not needed for ports exposed by the cdc_mbim driver. The most important features and changes in this release are the following: * Implemented support for Multi SIM Single Standby support, for systems that have multiple SIM slots and they can select which of them (only one) is active at any given time. Currently implemented for QMI modems only. * If the modem enabling phase fails in a fatal way, an implicit disabling sequence is now run, in order to avoid leaving the modem in an inconsistent state. * If the connection attempt includes user/password information but no explicit authentication type, CHAP will now be used by default instead of PAP. * Full USB device removal events reported via udev are no longer used. The device removal logic relies exclusively on independent port removal events, as that logic is supported for all subsystems and kernel device backends (e.g. also working for non-USB devices and for systems without udev like OpenWRT). * Added support to monitor the 'rpmsg' subsystem, but only in plugins that explicitly require its use (e.g. the 'qcom-soc' plugin). * New options in the ModemManager daemon: ** Added new '--test-no-suspend-resume' option to disable the runtime suspend/resume support even if the daemon was built with it. ** Added new '--test-no-udev' option to disable the runtime udev support even if the daemon was built with it. * Serial: ** Also match OK or ERROR responses that are not at end of line. * SIM: ** Force reprobing the modem if a new SIM is detected in a modem that initially started in Failed state without SIM. ** Force reprobing the modem if the lock status cannot be read after sending SIM-PUK, so that it transitions to the Failed state. ** Force reprobing the modem if a PUK lock is discovered after sending SIM-PIN, so that it transitions to the Failed state. * QMI: ** The logic no longer depends on the service version reported by each client, the support for each feature is explicitly probed instead. ** Implemented SIM profile (eUICC) change detection. ** Support for QMI modems on kernels < 3.6 is dropped. Only kernels where the QMI control ports are exposed in the 'usbmisc' subsystem are supported. ** Implemented additional step in the connection logic to allow binding the WDS client to a given SIO port, required in the BAM-DMUX driver setup. ** Implemented support for the initial EPS bearer settings logic. ** Disabled explicit signal and access technology polling if indications have been correctly enabled. * MBIM: ** Enable SIM hot swap detection logic with QMI over MBIM. ** Allow plugins to specify explicitly that QMI over MBIM is not supported. * libmm-glib: ** Added missing APIs to get/set RM protocol in the Simple connect settings. * Plugins: ** gosuncn: new plugin, for now just with port type hints for the GM800. ** quectel: implemented GPS support with +QGPS. ** quectel: implemented custom time support check to prefer +CTZU=3 instead of +CTZU=1 so that the modem reports localtime instead of UTC in +CCLK. ** sierra: added support for XMM-specific features (e.g. EM7345). ** cinterion: implemented support for the initial EPS bearer settings logic. ** cinterion: added SIM hot swap support to AT-based modems. ** huawei: updated to avoid applying multiple port type hint methods. ** huawei: updated the ^GETPORTMODE logic so that we don't assume the hints in the response apply to specific USB interfaces. The following features which were backported to 1.14.x releases are also present in ModemManager 1.16.0: * location: allow CID only updates. * sms: allow sending/receiving UTF-16 as if it were UCS-2. * modem: don't consider charset setup failure as fatal. * QMI: fix reporting signal strength indications. * QMI: fix parsing of USSD indications with UTF-16 data. * QMI: run network registration with NAS Set System Selection Preference. * QMI: when connection aborted, ensure network handles are released. * MBIM: don't fail IPv4v6 connection attempt if only IPv4 succeeds. * cinterion: improve user/password handling in AT^SGAUTH calls. * cinterion: removed limitation to IPv4 only PDP contexts. * cinterion: configure the PLAS9 to send URCs correctly. * quectel: add support for MBIM devices. * telit: add initial delay for AT ports to become responsive. * udev: updated all AT/QCDM/GPS port type hints in all plugins to bind them to TTY ports only. ModemManager 1.14.0 ------------------------------------------- This is a new stable release of ModemManager. The following notes are directed to package maintainers: * This version requires: ** GLib/GObject/GIO >= 2.48.0 ** libmbim >= 1.24.0 (for the optional MBIM support) ** libqmi >= 1.26.0 (for the optional QMI support) * Build updated with several improvements: ** The build has been updated to use by default all warnings enabled by AX_COMPILER_FLAGS(), and therefore when building the release from a git checkout, autoconf-archive >= 2017.03.21 is now required. This new build dependency isn't required when building from the release tarball. ** Also when building from a git checkout, beware because by default --enable-compile-warnings=error is enabled, which implies -Werror. If you'd like to build from git and avoid -Werror, you should explicitly use --enable-compile-warnings=yes (to keep the warnings but without being errors), --enable-compile-warnings=no (to disable all the extra warnings enabled by default) or --disable-Werror (to unconditionally make all compiler warnings non-fatal). ** Users can now preselect which plugins to build and install during configure, with the --enable-plugin-[PLUGIN-NAME] options. The user can also build and install all except for some, just by using the --enable-all-plugins option followed by --disable-plugin-[PLUGIN-NAME]. By default all plugins are enabled, so all of them built and installed. This new set of options are useful for users building custom systems where they already know what kind of devices they're going to have, it isn't recommended for standard distributions. The only updates in the public API are the following: * Modem interface: ** Added support for AT-based and/or QMI-based 5G devices, which will report the new MM_MODEM_CAPABILITY_5GNR capability. ** Deprecated the MM_MODEM_CAPABILITY_LTE_ADVANCED capability, as it was never used in any implementation. * Bearer interface: ** Added additional 'attempts', 'failed-attempts', 'total-rx-bytes', 'total-tx-bytes' and 'total-duration' values in the 'Stats' property exposed by the Bearer objects. The most important features and changes in this release are the following: * The daemon switched to 'STRICT' filter mode by default. The old 'DEFAULT' mode is renamed to 'LEGACY' and is considered now deprecated. Under the 'STRICT' filter mode, the TTY blocklist and greylist are ignored, and the port probing mechanism uses its own heuristics to guess whether a given TTY is owned by a modem or not. * Added a new implicit whitelist rules applicable in 'STRICT' filter mode, so that all devices with a USB vid matching one of the vids allowed by the different installed plugins are implicitly allowed. * Updated daemon logging so that we always prefix the messages with a string identifying which modem the log refers to, making it easy to grep logs for one specific device if the system has more than one. * Updated the probing logic to make sure we don't attempt a re-probe when the device is gone. * Probing logic now allows new ports detected up to 1500ms since last port added, useful on OpenWrt setups where ports are notified one by one via hotplug events. * AT: ** Moved the charset definition logic to the initialization phase instead of the enabling phase, because the feature support checks may already require string processing based on the current charset. ** Updated manual registration operation to attempt using current charset (e.g. UCS2) if ASCII fails. * QMI: ** Devices using the LOC service for GNSS will now also setup the list of required NMEA traces before starting the engine. ** Implemented 3GPP USSD support using the Voice service. ** Update carrier code if registration changes from one roaming operator to another. ** Explicitly disable autoconnect during modem enabling phase, because it interferes with our connection management logic. ** Fallback to raw-ip if WDA Get Data Format requests arguments, as in most new 5G devices. ** Updated to always use the asynchronous close() operation. ** Handle disconnection indications during connection attempts. * MBIM: ** Update carrier code if registration changes from one roaming operator to another. ** Implement reset in Intel-based and Qualcomm-based devices. ** Avoid LTE attach config/status if unsupported. ** Updated to make sure all allocated QMI CIDs are released during shutdown. * SIM interface: ** Don't allow sending PIN/PUK if not required. * 3GPP interface: ** Fixed manual re-registration to the same operator. * CDMA interface: ** Don't allow multiple concurrent activation attempts. ** Disallow empty carrier code in automatic activation. * Bearer interface: ** Updated to avoid connection checks or stats updates while disconnecting. * libmm-glib: ** New 'mm_location_gps_nmea_get_traces()' method to retrieve a NULL terminated array of strings with all cached NMEA traces. ** Deprecated the 'mm_location_gps_nmea_build_full()' method. * mmcli: ** Added a new 'any' lookup keyword for the --modem and --sim options, useful when the system is only expected to have one single device. * Plugins: ** broadmobi: new plugin, right now just with port type hints for the BM818. ** foxconn: new plugin to support the T77W968 (both with and without eSIM). ** dell,dw5821e: added support for the DW5821e with eSIM variant. ** huawei: don't delay reporting network initiated disconnects. ** huawei: try to read port type hints from interface descriptions. ** huawei: avoid using the QCDM port during a voice call. ** cinterion: skip sim ready check for modules that don't support it. ** cinterion: implemented radio/band handling for LTE modems. ** cinterion: added Signal interface support bsaed on AT^SMONI. ** cinterion: added support for MBIM based devices like the PLS62-W. ** quectel: updated to detect SIM hot swap via +QUSIM URCs. ** fibocom: added support for QMI based devices like the FM150. ** ublox: ignore error when disconnecting last LTE bearer. ** ublox: implement support to enable and detect +UUDTMF URCs. ** ublox: added blocklist rules for GPS modules in the plugin itself. ** sierra: implement manual and automatic CDMA activation. ** novatel: implement manual and automatic CDMA activation. All the features and fixes which were backported to 1.12.x releases are also present in ModemManager 1.14.0. ModemManager 1.12.0 ------------------------------------------- This is a new stable release of ModemManager. The following notes are directed to package maintainers: * This version requires: ** libmbim >= 1.18.0 (for the optional MBIM support) ** libqmi >= 1.24.0 (for the optional QMI support) * The build allows to configure with '--with-at-command-via-dbus' in order to unconditionally enable the Modem.Command() method to allow running AT commands through ModemManager. This is not suggested for standard distributions, though. The most important features and changes in this release are the following: * Modem interface: ** Updated logic to avoid assuming that setting bands or modes is immediate, the daemon will now actively monitor for those updates to happen before returning success. * 3GPP interface: ** libmm-glib: deprecated the mm_pco_list_free() helper method. * Simple interface: ** api,libmm-glib: deprecated the 'subscription state' property. * Location interface: ** Fixed 'unknown' lat/long/alt numeric values. ** Added support for MSB A-GPS in addition to MSA A-GPS. * Voice interface: ** Improved voice call management with call id detection and tracking. ** Improved detailed call state transitions on generic modems that support call list polling. ** Added support for GSM supplementary services, including call waiting, call transfer, call deflection, multiparty calls... ** Added emergency call support, allowing voice call to emergency numbers even without SIM or with SIM-PIN locked. ** Deprecated all properties except for 'number' in the CreateCall() method. * Messaging interface: ** Updated to report SMS timestamps in correct ISO8601 format. * Bearer: ** Improved unused CID lookup to allow selecting non-sequential CIDs. ** Disabled all AT protocol based context monitoring when PPP is used for the connection, in order to properly sync with pppd, which should be the one detecting the disconnections (already in 1.10.6). * QMI: ** Improved support to list stored firmware images in Sierra devices. ** Added additional lock check retries on 'SIM not inserted' errors. ** Updated explicit registration attempt to report success only when the target requested network is registered. ** Added MSB A-GPS support. ** Implemented automatic carrier configuration selection using PDC service (already in 1.10.2). * mmcli: ** New machine-readable JSON output with '--output-json'. ** Updated to allow using the modem UID to specify SIM operations. * udev: ** New ID_MM_PORT_TYPE_AUDIO generic udev tag to identify ports that should be used for in-band audio. ** Removed support for the ID_MM_PLATFORM_DRIVER_PROBE udev tag, as it is no longer required given that the more generic explicit whitelist may be used to flag which devices should be probed. ** Renamed ID_MM_DEVICE_MANUAL_SCAN_ONLY to ID_MM_TTY_MANUAL_SCAN_ONLY, given that the tag only applies to TTYs. ** ID_MM_DEVICE_IGNORE is no longer used internally in ModemManager, and is instead targeted to users that want to explicitly ignore specific devices regardless of what filter type is in use (already in 1.10.6). * dbus: ** Updated to always report the registered MM_CORE_ERROR_CANCELLED error instead of the implicit G_IO_ERROR_CANCELLED ones generated by GLib. * GObject introspection: ** Fixed setup to explicitly skip all non-API methods. * Plugins: ** tplink: new plugin. ** dlink: new plugin. ** xmm: added MSB A-GPS support. ** dell,dw5821e: update to allow unmanaged GPS support on the TTY even when raw/NMEA GPS is enabled via QMI/LOC. ** quectel: updated to allow TTY-only devices. ** telit: added GPS support. ** telit: improved band management with #BND. ** simtech: added improved voice call support. ** simtech: added support for LTE devices. ** simtech: improved signal quality reporting logic. ** simtech: added GPS support for the SIM7000/SIM7600 family. ** cinterion: added support for time updates. ** cinterion: added improved voice call support. ** ublox: added improved voice call support. ** ublox: improved band management with UBANDSEL. All the features and fixes which were backported to 1.10.x releases are also present in ModemManager 1.12.0. ModemManager 1.10.0 ------------------------------------------- This is a new stable release of ModemManager. The following notes are directed to package maintainers: * This version requires: ** libmbim >= 1.18.0 (for the optional MBIM support) ** libqmi >= 1.22.0 (for the optional QMI support) The most important features and changes in this release are the following: * udev: ** Consolidated common tag names among all the supported plugins. E.g. ID_MM_PORT_TYPE_GPS, ID_MM_PORT_TYPE_AT_*, ID_MM_PORT_TYPE_QCDM... All these generic tags are included as symbols in the API, and compatibility will be maintained for these. Custom setups of ModemManager relying on previously available per-plugin udev tags may need to manually port them to this new generic subset. ** New tag to allow specifying flow control settings to use in serial ports. * Core: ** Avoid probing other protocols on TTYs tagged in udev with specific port type tags (e.g. avoid probing QCDM if a port is tagged as AT). This allows faster port probing and modem detection for known modem layouts. ** Implemented support to enable and handle +CGEV URCs for asynchronous connection state updates in AT-controlled devices. * Manager interface: ** New runtime daemon version reporting. ** New support for requesting device inhibition, e.g. so that ModemManager stops completely using a modem device until the inhibition is released. This feature is implemented to allow fwupd taking over of a device completely for as long as it needs during a firmware update. * Modem interface: ** All methods are always connected, even in Failed state. ** Allow parallel Enable()/Disable() calls. ** Deprecated redundant ListBearers() method, the read-only Bearers property is already showing the same information. * Bearer interface: ** New 'BearerType' property, e.g. to specify whether a bearer is the initial LTE default bearer or not. ** Deprecated 'number' field in bearer settings. Applications do not need to send the 'number' field in Bearer.Connect() or in Modem.Simple.Connect(), as the setting is totally ignored. * mmcli: ** New 'key-value' output, easier to parse by scripts than the default. ** Removed redundant '--location-get-XXX' actions, as the '--location-get' already reports the location information for all sources. ** Removed redundant '--simple-status' action, as the same information can be obtained through different mmcli operations. ** New manager '--inhibit-device' action and modem-specific '--inhibit', to allow requesting device inhibition. * 3GPP interface: ** New support for exposing the network reported Protocol Configuration Options (PCO), to be used instead of the new deprecated Subscription State property. ** New support for exposing the initial LTE default bearer status. ** New support for configuring the initial LTE default bearer settings. * Location interface: ** New LTE Tracking Area Code (TAC) in 3GPP location information. ** New support for injecting assistance data (e.g. Qualcomm XTRA) into the GNSS engine, useful when there is no mobile connection to use MSA A-GPS. * Firmware interface: ** Support for reporting firmware update support properties, e.g. specifying which update methods are supported. This information will be consumed by fwupd in order to allow upgrading firmware in devices managed by ModemManager. * Voice interface: ** Multiple improvements and fixes in the voice call management logic implemented with generic AT commands. ** Added AudioPort and AudioFormat properties to the Call object. ** Added new generic audio channel setup/cleanup handlers in the Call object. * QMI: ** New LOC service based GNSS support, including A-GPS setup via SUPL server. ** New support for the "extended" LTE band list. ** New support for reading IMSI and ICCID with the UIM service. * MBIM: ** Implemented support for processing Protocol Configuration Options using Microsoft-defined Basic Connect Extensions. ** Implemented support for LTE attach status and configuration using Microsoft-defined Basic Connect Extensions. ** Implemented support for the extended signal interface and for 3GPP location details using the AT&T specific service. ** Implemented support for 3GPP USSD operations using the standard USSD service. ** For Qualcomm-based MBIM devices, those with QMI-over-MBIM support, a whole new set of features is now available, including: QMI LOC/PDS location support, allowed/preferred mode management, frequency band selection, power management operations... * Plugins: ** xmm: new XMM plugin, with shared logic (allowed/preferred mode management, frequency band selection, power management operations, extended signal quality reporting, GPS/A-GPS...) for Intel XMM based devices. ** fibocom: new plugin, with support for generic MBIM and XMM-based devices. ** dell: added support for XMM-based devices, like the DW5820e. ** dell: added custom support for the DW5821e, including 'unmanaged' GPS and firmware update integration details. ** cinterion: new shared interface to include all logic shared between Option and Option/HSO devices. ** sierra-legacy: implemented connection monitoring support. ** u-blox: added support for extended call state transitions. ** u-blox: added CDC-ECM support for SARA/LISA-U2xx modems. ** altair-lte: migrated from SubscriptionState to PCO. All the features and fixes which were backported to 1.8.x releases are also present in ModemManager 1.10.0. ModemManager 1.8.0 ------------------------------------------- This is a new stable release of ModemManager. The following notes are directed to package maintainers: * This version requires: ** GLib 2.36.0 ** gettext 0.19.8 (for the optional rebuild of documentation) ** libmbim >= 1.16.0 (for the optional MBIM support) ** libqmi >= 1.20.0 (for the optional QMI support) ** libsystemd >= 209 or libsystemd-login >= 183 (for the optional suspend and resume support) ** libsystemd >= 209 (for the optional systemd journal support) ** polkit >= 0.97 (for the optional PolicyKit support) * This version no longer requires: ** intltool (replaced by new features in gettext) * Distributions using systemd should explicitly use the following configure options: ** '--with-systemd-suspend-resume': the only supported source for suspend and resume events is now systemd, so the previous '--with-suspend-resume=[]' option was renamed. ** '--with-systemd-journal' to use the new journal support. * Distributions wanting to avoid ModemManager poking TTY ports that isn't supposed to touch may start using the new 'STRICT' filter policy, given as an option to the ModemManager daemon (e.g. patching the default systemd service file provided). See below for more info about the filter policy and the side effects of using the STRICT approach: ** E.g. ModemManager --filter-policy=STRICT The most important features and changes in this release are the following: * New 'filter policy' setting in the ModemManager daemon to decide which ports are probed and how. Currently these levels are defined: ** WHITELIST-ONLY: Only devices or ports explicitly whitelisted with the new 'ID_MM_DEVICE_PROCESS' udev tag are probed. ** DEFAULT: All ports are allowed to be probed except for the ones explicitly greylisted as RS232 adapters or completely blocklisted. This is the default approach that was used until now, and the default as well in this release if a different one isn't requested. ** STRICT: The daemon defines a set of heuristics to try to detect modems and ports to probe. Only the TTY ports that are very very likely to be modem ports are probed, therefore completely avoiding the need of having a separate blocklist or RS232 adapter greylist. But note that this policy may end up ignoring some devices, like TTY controlled modems without an associated network port. ** PARANOID: This is equivalent to running the STRICT mode but also applying the blocklist and RS232 greylist filters explicitly. * Device 'naming'. This release includes logic to allow 'naming' devices with the ID_MM_PHYSDEV_UID udev tag, so that the names can then be used in e.g. mmcli and also exposed in the 'Device' property in the Modem interface. This new setup makes it possible to give the devices unique names that are kept across reboots. * Allow skipping the automatic scan for devices in the daemon with the new '--no-auto-scan' daemon option. Instead, the daemon may be called with the '--initial-kernel-events=[PATH]' option including a predefined list of ports or otherwise get the port additions reported during runtime with the mmcli --report-kernel-event=[] command. * Allow building and running without 'udev'. In this setup, the previously explained '--no-auto-scan' is enabled by default, so ports are not automatically detected .Note that this setup is not suggested for standard distributions: if udev is available in the system, it is the preferred method to manage the port addition and removal. * SIM hot swap. The core includes the needed logic to support SIM hot swap in the different devices, although for now it's only tested for Telit and MBIM modems. If a SIM hot swap is detected, the modem is flagged as failed and reprobed from scratch. * Connection status monitoring logic. In order to try to detect network initiated disconnections, a generic setup is provided to plugins so that they can implement explicit connection status checks that would be executed periodically. * New support for 3GPP CSFB states and operation modes. We now support registration states reported as "SMS only" or "CSFB not preferred", and provide APIs to set and get the "UE mode of operation for EPS". This version comes with the following new features: * Build and system: ** Updated the systemd service file with additional security related rules. ** Added support for systemd journal logging. ** Updated most of the code to use GTask instead of the deprecated GSimpleAsyncResult based operations. ** ChangeLog is built from git during the dist tarball generation. * New translations: Polish, Brazilian Portuguese, Slovak, Hungarian, Czech, Ukrainian, Swedish and Indonesian. * API: ** Defined additional GSM, UMTS and LTE frequency bands. ** The MMModemBand enumeration values (EUTRAN, UTRAN and CDMA) have been renamed to consolidate how they are defined. A compatibility layer has been provided to avoid breaking the API. ** New 'HardwareRevision' property in the Modem interface. ** New 'EpsUeModeOperation' property and 'SetEpsUeModeOperation' method in the Modem 3GPP interface. * Core: ** Updated libqcdm to load signal strength from QCDM EVDO Pilot Sets. ** Updated udev rules with new per-vendor rules for quicker processing. ** Explicitly ignored ports are never probed now, but they will be reported as owned by the device and exposed in the Ports property. ** New 'ID_MM_TTY_BAUDRATE' udev tag to specify the baudrate to use in RS232 TTY ports. ** If using UCS2, still assume that the operator name may be given in ASCII. ** Explicitly open QCDM ports anytime it's needed, instead of assuming they are open when enabled. ** Query supported ME event reporting options and automatically set the best choice based on the supported ones. ** Query supported flow control modems and automatically set the best choice based on the supported ones. ** Explicitly configure flow control settings on the TTY as soon as it is connected, only applicable for RS232 devices. ** Implemented generic unlock retries loading. * MBIM: ** Explicitly reprobe the modem when the mbim-proxy is detected dead. ** Workaround implemented to keep track of the PIN1 unlock retries as the protocol isn't very good allowing this. ** Load and expose HW revision. * QMI: ** Explicitly reprobe the modem when the qmi-proxy is detected dead. ** Load and expose HW revision. ** Detect port closed and forbid client allocation operations. ** New optional connection status monitoring support, enabled by default for the Netgear AC341U. * Location interface: ** Disabled by default for MBIM modems. * Messaging interface: ** Try decoding with UTF-16BE when UCS-2 reported. * Plugins: ** u-blox: new plugin, currently supporting the TOBY-L2, TOBY-L4, TOBY-R2, LARA-R2 and LISA-R2. ** quectel: new plugin, supporting generic AT/QMI based modems. ** cinterion: implemented support for devices exposing a WWAN network interface. ** cinterion: support changing modes in LTE capable devices. ** cinterion: added GPS support for devices controlled only with AT^SGPSC. ** cinterion: use ^SIND unsolicited messages for access tech reporting. ** huawei: implemented Signal interface. ** telit: added support for RS232, QMI and MBIM modems. ** novatel: cleaned up registration state and access tech reporting. ** novatel-lte: implemented unlock retries loading. ** dell: speed probing time up and reduce udev dependency. ** mbm: added GPS support for the Dell DW5560. The following features which were backported to 1.6.x releases are also present in ModemManager 1.8.0: * Build and System: ** Explicitly use -DGLIB_VERSION_MIN_REQUIRED=GLIB_VERSION_2_36 and -DGLIB_VERSION_MAX_ALLOWED=GLIB_VERSION_2_36 so that we don't get warnings for GLib features that are flagged as deprecated in newer versions. ** Dropped After=syslog.target rule in systemd service file. ** Added all ModemManager udev tags also in "bind" events. * Core: ** Improved detection of 4G-only modems. ** TTY is set as connected as soon as ATD replies. ** Removed the default ID_MM_PLATFORM_DRIVER_PROBE whitelist. * Signal interface: ** Report RSCP if available. ** New generic implementation of the Signal interface in AT-based devices using AT+CESQ. * QMI: ** Run FCC auth sequence if "InvalidTransition" is reported when going online. ** Added WDS reporting event support. * Plugins: ** huawei: let the E3372 run NDISDUP via TTY (when the exposed cdc-wdm is non-functional). ** telit: added support for the GE910. ModemManager 1.6.0 ------------------------------------------- This is a new stable release of ModemManager. * This version requires: ** GLib 2.36.0 ** gettext 0.19.3 ** libmbim >= 1.14.0 (for the optional MBIM support) ** libqmi >= 1.16.0 (for the optional QMI support) ** libsystemd >= 209 or libsystemd-login >= 183 (for the optional suspend and resume support) * For distributions using systemd, it is suggested that the new optional suspend/resume is explicitly requested during configure with the new '--with-suspend-resume=systemd' argument. This version comes with the following new features: * Core: ** Implemented support for suspend/resume detection, currently working when systemd is in use. Whenever the system is suspended, we'll flag the modems as invalid so that they are re-probed from scratch when the system is resumed. ** Added cancellation support for the probing operations. ** Reworked and simplified the serial port response processing. * Location interface: ** Added A-GPS support, currently available only for QMI based modems with PDS service. ** Added support for updating the default GPS refresh time. * Time interface: ** New default implementation for all AT-based modems. * Voice interface: ** New DBus interface to allow the management of voice calls, which currently assumes that the audio channel is setup out of ModemManager. * Bearer: ** New support for reporting statistics of the ongoing connection with a new 'Stats' property, currently available for QMI and MBIM based modems. * QMI: ** Implemented support for devices which only work in "raw IP" mode, like the Sierra MC7455. ** Implemented support for SIM related operations using the UIM service, as newer modems with multi-SIM capabilities don't suppor the legacy DMS UIM operations. ** Implemented support for detecting network-initiated disconnections. * MBIM: ** If online mode fails, try to use the 'DMS Set FCC Authentication' QMI message via the QMI-over-MBIM support, if supported by the device. * udev: ** Added new supported 'ID_MM_PORT_IGNORE' tag to allow fully ignoring ports specified by the user. * mmcli: ** Added command completion. ** Added new operations to use the Voice interface. ** Added new operations to manage the A-GPS settings. * Build: ** Added code coverage support. * Plugins: ** haier: new plugin to support the Haier CE81B. ** thuraya: new plugin for Thuraya satellite modems. ** sierra-legacy,sierra: the implementation for Sierra modems is now split into two different plugins: a 'legacy' one for the old PPP and DirectIP based modems and the standard one for the newer QMI and MBIM based ones. ** dell: new plugin for Dell rebranded devices from Novatel, Sierra or Ericsson. ** gobi: removed the plugin. All non-vendor specific QMI devices should now be managed by the generic plugin. ** mbm: dynamically load the list of supported modes. ** mbm: fixed several connection/disconnection issues. ** simtech: support QMI devices. ** huawei: implemented Voice call management support. ** huawei: use static IP addressing in NDISDUP capable devices if the AT^DHCP response provides the IP details. The following features which were backported to 1.4.x releases are also present in ModemManager 1.6.0: * MBIM: ** The mbim-proxy is used by default. ** Implemented support for disconnection status notification while connected. ** Disabled CDMA capabilities, until properly supported. * QMI: ** The qmi-proxy is used by default. ** If online mode fails, use 'DMS Set FCC Authentication', required by some rebranded Sierra modems (e.g. Dell branded ones). ** Implemented support for loading SIM operator id and name. ** Implemented power-cycle reset functionality. * Plugins: ** telit: added support for new devices, like HE910, UE910 and UL865. ** telit: implemented dynamic port identification. ** telit: implemented unlock retries loading. ** telit: implemented supported/current bands management. ** telit: implemented supported/current modes management. ** telit: implemented modem reset and power down. ** mbm: implemented GPS support for Ericsson HS2350 and H5321gw modems. ModemManager 1.4.0 ------------------------------------------- This is a new stable release of ModemManager. * This version requires libmbim >= 1.10.0. This version comes with the following updates in the interfaces: * Updated the logic around the IP configuration properties in the Bearer: ** Setting DHCP as IP method in the IPv6 settings means that SLAAC should be used to retrieve correct addressing and routing details. ** DHCP IP method may now be combined with an explicit static IP address, as IPv6 SLAAC may require the link-local address to be present. ** MTU is now also included in the IP configuration properties, if specified by the modem, and applicable to both DHCP and STATIC methods. * New 'OFF' power state, which fully switches off the modem device. After setting the modem in this state, no further use of it can be done. Currently available in Wavecom and Cinterion. * Location interface: new 'unmanaged GPS' support, which allows to start/stop the GPS module in the modem, while leaving the location information retrieval to other processes. Currently available in modems with independent GPS TTYs, like Option/HSO, Cinterion and Huawei. * New Test DBus interface: not to be installed, just for internal system tests. Other notable changes include: * MBIM: support for ZTE and Sequans Communications modems. * Ericsson MBM: Support for AT-capable /dev/cdc-wdm ports. * Huawei: improved support for Network time retrieval. * Huawei: implemented GPS support. * Huawei: support for /dev/cdc-wdm AT ports via the new huawei-cdc-ncm driver. * Cinterion: implemented GPS support. * Cinterion: implemented unlock retries loading. * Cinterion: gather port types for multi-tty devices. * Cinterion: custom wait for SIM readiness after SIM-PIN unlock. * Wavecom: custom wait for SIM readiness after SIM-PIN unlock. * Probing: new flag to identify hotplugged devices which don't need full reset. * Tests: internal refactor of the ports handling code, allowing test-driven virtual ports and system tests run during 'make check'. This new feature also comes with a new internal 'Test' DBus interface, as well as new --test-[*] options in the ModemManager program. * and many more fixes... ModemManager 1.2.0 ------------------------------------------- This is a new stable release of ModemManager. This version comes with the following updates in the interfaces: * Signal interface: new interface for extended signal quality information * OMA interface: new interface to expose the Device Management capabilities defined by the Open Mobile Alliance * Messaging interface: new 'Messages' property * Modem interface: new 'Bearers' property * 3GPP interface: new 'SubscriptionState' property Other notable changes include: * QMI: Implemented Manual CDMA activation logic * QMI: Implemented 3GPP2/CDMA SMS support * QMI: Added support for QMI modems in the ZTE, x22x and Cinterion plugins. * Huawei: multiple improvements and fixes for the ^NDISDUP support * Huawei: new mode/switching logic with ^SYSCFGEX for LTE-capable devices * Altair-LTE: set subscription state based on PCO * MediaTek: new 'mtk' plugin added for MediaTek devices * libmm-glib: Added GObject Introspection and Vala support * and many more fixes... ModemManager 1.0.0 ------------------------------------------- This is a new stable release of ModemManager. Notable changes include: * More flexible D-Bus API that accounts for the capabilities of modern devices * Native support for Gobi and QMI-based Qualcomm devices via libqmi * Native support for MBIM-based devices via libmbim * Preliminary support for GPS-based Location Services with some devices * More complete messaging API * New libmm-glib client library * New fully-featured command-line client (mmcli) * systemd integration * and much more... ModemManager-1.23.4-dev/README.md000066400000000000000000000047751456466623000162210ustar00rootroot00000000000000 # ModemManager ModemManager provides a unified high level API for communicating with mobile broadband modems, regardless of the protocol used to communicate with the actual device (Generic AT, vendor-specific AT, QCDM, QMI, MBIM...). ## Using ModemManager is a system daemon and is not meant to be used directly from the command line. However, since it provides a DBus API, it is possible to use 'dbus-send' commands or the new 'mmcli' command line interface to control it from the terminal. The devices are queried from udev and automatically updated based on hardware events, although a manual re-scan can also be requested to look for RS232 modems. ## Implementation ModemManager is a DBus system bus activated service (meaning it's started automatically when a request arrives). It is written in C, using glib and gio. Several GInterfaces specify different features that the modems support, including the generic MMIfaceModem3gpp and MMIfaceModemCdma which provide basic operations for 3GPP (GSM, UMTS, LTE) or CDMA (CDMA1x, EV-DO) modems. If a given feature is not available in the modem, the specific interface will not be exported in DBus. ## 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 port and to create custom MMBroadbandModem 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 are multiple fully working plugins in the plugins/ directory that can be used as an example for writing new plugins. Writing new plugins is highly encouraged! The plugin 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! ## License The ModemManager and mmcli binaries are both GPLv2+. The libmm-glib library is LGPLv2+. ## Code of Conduct Please note that this project is released with a Contributor Code of Conduct. By participating in this project you agree to abide by its terms, which you can find in the following link: https://www.freedesktop.org/wiki/CodeOfConduct CoC issues may be raised to the project maintainers at the following address: modemmanager-devel-owner@lists.freedesktop.org ModemManager-1.23.4-dev/RELEASING000066400000000000000000000064001456466623000161610ustar00rootroot00000000000000 The ModemManager releases are generated using meson. 0.1) For major releases: * Increment mm_minor_version and reset mm_micro_version. * Assuming API/ABI compatibility in libmm-glib, increment both mm_glib_lt_current and mm_glib_lt_age. 0.2) For stable branch releases: * Increment mm_micro_version. 1) Add signed tag: $ git tag -s 1.20.0 The description included in the signed tag could be: Release 1.20.0 2) Configure and build the whole project, making sure gtk-doc and introspection are enabled: $ meson setup build \ --prefix=/usr \ --buildtype=release \ -Dintrospection=true \ -Dgtk_doc=true $ ninja -C build 3) Run the test suite and install locally: $ ninja -C build test $ sudo ninja -C build install 4) Create directories for the manpages and gtk-doc documentation in freedesktop.org, and also update the 'latest' links: $ ssh fd.o [fd.o] $ cd ${ModemManager}/man/ [fd.o] $ rm latest [fd.o] $ mkdir -p ${VERSION} [fd.o] $ ln -s ${VERSION} latest [fd.o] $ cd ${ModemManager}/doc/ [fd.o] $ rm latest [fd.o] $ mkdir -p ${VERSION}/ModemManager [fd.o] $ mkdir -p ${VERSION}/libmm-glib [fd.o] $ ln -s ${VERSION} latest 5) Generate HTML for the manpages: $ roffit < docs/man/mmcli.1 > mmcli.1.html $ roffit < docs/man/ModemManager.8 > ModemManager.8.html 6) Upload manpages in HTML to freedesktop.org: $ scp *.html fd.o:${ModemManager}/man/${VERSION}/ 7) Copy documentation from /usr/share/gtk-doc and fix online references manually $ mkdir ModemManager-html $ cp -r /usr/share/gtk-doc/html/ModemManager/* ModemManager-html/ $ for FILE in $(ls ModemManager-html/*.html); do \ sed -i 's|]* href="\.\./glib/[^"]*| MANAGER #define "" MANAGER #define "" MANAGER #define "" /* Generated Header file do not edit */ /* * ModemManager Interface Specification * Version 0.6 * * 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 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 Street, Fifth Floor, * Boston, MA 02110-1301 USA. * * Copyright (C) 2008 - 2009 Novell, Inc. * Copyright (C) 2009 - 2012 Red Hat, Inc. * Copyright (C) 2011 - 2012 Google, Inc. * Copyright (C) 2015 - Riccardo Vangelisti riccardo.vangelisti@sadel.it * Copyright (C) 2015 - Marco Bascetta marco.bascetta@sadel.it */ #ifndef _MODEM_MANAGER_NAMES_H_ #define _MODEM_MANAGER_NAMES_H_ #define MM_DBUS_PATH "/org/freedesktop/ModemManager1" #define MM_DBUS_SERVICE "org.freedesktop.ModemManager1" /* Prefix for object paths */ #define MM_DBUS_MODEM_PREFIX MM_DBUS_PATH "/Modem" #define MM_DBUS_BEARER_PREFIX MM_DBUS_PATH "/Bearer" #define MM_DBUS_SIM_PREFIX MM_DBUS_PATH "/SIM" #define MM_DBUS_SMS_PREFIX MM_DBUS_PATH "/SMS" #define MM_DBUS_CALL_PREFIX MM_DBUS_PATH "/Call" /* Prefix for DBus errors */ #define MM_DBUS_ERROR_PREFIX "org.freedesktop.ModemManager1.Error" /************** * Interfaces * **************/ _ #define "" /****************************** * Methods/Signals/Properties * ******************************/ /* * Interface '' */ #endif /* _MODEM_MANAGER_NAMES_H_ */ ModemManager-1.23.4-dev/build-aux/mm-mkenums000077500000000000000000000732371456466623000206470ustar00rootroot00000000000000#!/usr/bin/env python3 # mm-mkenums is ported from glib-mkenums. # Same license applies. # If the code below looks horrible and unpythonic, do not panic. # # It is. # # This is a manual conversion from the original Perl script to # Python. Improvements are welcome. # from __future__ import print_function, unicode_literals import argparse import os import re import sys import tempfile import io import errno import codecs import locale VERSION_STR = '''glib-mkenums version 2.64.2 glib-mkenums comes with ABSOLUTELY NO WARRANTY. You may redistribute copies of glib-mkenums under the terms of the GNU General Public License which can be found in the GLib source package. Sources, examples and contact information are available at http://www.gtk.org''' # pylint: disable=too-few-public-methods class Color: '''ANSI Terminal colors''' GREEN = '\033[1;32m' BLUE = '\033[1;34m' YELLOW = '\033[1;33m' RED = '\033[1;31m' END = '\033[0m' def print_color(msg, color=Color.END, prefix='MESSAGE'): '''Print a string with a color prefix''' if os.isatty(sys.stderr.fileno()): real_prefix = '{start}{prefix}{end}'.format(start=color, prefix=prefix, end=Color.END) else: real_prefix = prefix print('{prefix}: {msg}'.format(prefix=real_prefix, msg=msg), file=sys.stderr) def print_error(msg): '''Print an error, and terminate''' print_color(msg, color=Color.RED, prefix='ERROR') sys.exit(1) def print_warning(msg, fatal=False): '''Print a warning, and optionally terminate''' if fatal: color = Color.RED prefix = 'ERROR' else: color = Color.YELLOW prefix = 'WARNING' print_color(msg, color, prefix) if fatal: sys.exit(1) def print_info(msg): '''Print a message''' print_color(msg, color=Color.GREEN, prefix='INFO') def get_rspfile_args(rspfile): ''' Response files are useful on Windows where there is a command-line character limit of 8191 because when passing sources as arguments to glib-mkenums this limit can be exceeded in large codebases. There is no specification for response files and each tool that supports it generally writes them out in slightly different ways, but some sources are: https://docs.microsoft.com/en-us/visualstudio/msbuild/msbuild-response-files https://docs.microsoft.com/en-us/windows/desktop/midl/the-response-file-command ''' import shlex if not os.path.isfile(rspfile): sys.exit('Response file {!r} does not exist'.format(rspfile)) try: with open(rspfile, 'r') as f: cmdline = f.read() except OSError as e: sys.exit('Response file {!r} could not be read: {}' .format(rspfile, e.strerror)) return shlex.split(cmdline) def write_output(output): global output_stream print(output, file=output_stream) # Python 2 defaults to ASCII in case stdout is redirected. # This should make it match Python 3, which uses the locale encoding. if sys.stdout.encoding is None: output_stream = codecs.getwriter( locale.getpreferredencoding())(sys.stdout) else: output_stream = sys.stdout # Some source files aren't UTF-8 and the old perl version didn't care. # Replace invalid data with a replacement character to keep things working. # https://bugzilla.gnome.org/show_bug.cgi?id=785113#c20 def replace_and_warn(err): # 7 characters of context either side of the offending character print_warning('UnicodeWarning: {} at {} ({})'.format( err.reason, err.start, err.object[err.start - 7:err.end + 7])) return ('?', err.end) codecs.register_error('replace_and_warn', replace_and_warn) # glib-mkenums.py # Information about the current enumeration flags = None # Is enumeration a bitmask? option_underscore_name = '' # Overridden underscore variant of the enum name # for example to fix the cases we don't get the # mixed-case -> underscorized transform right. option_lowercase_name = '' # DEPRECATED. A lower case name to use as part # of the *_get_type() function, instead of the # one that we guess. For instance, when an enum # uses abnormal capitalization and we can not # guess where to put the underscores. option_since = '' # User provided version info for the enum. seenbitshift = 0 # Have we seen bitshift operators? enum_prefix = None # Prefix for this enumeration enumname = '' # Name for this enumeration enumshort = '' # $enumname without prefix enumname_prefix = '' # prefix of $enumname enumindex = 0 # Global enum counter firstenum = 1 # Is this the first enumeration per file? entries = [] # [ name, val ] for each entry sandbox = None # sandbox for safe evaluation of expressions output = '' # Filename to write result into def parse_trigraph(opts): result = {} for opt in re.split(r'\s*,\s*', opts): opt = re.sub(r'^\s*', '', opt) opt = re.sub(r'\s*$', '', opt) m = re.search(r'(\w+)(?:=(.+))?', opt) assert m is not None groups = m.groups() key = groups[0] if len(groups) > 1: val = groups[1] else: val = 1 result[key] = val return result def parse_entries(file, file_name): global entries, enumindex, enumname, seenbitshift, flags looking_for_name = False while True: line = file.readline() if not line: break line = line.strip() # read lines until we have no open comments while re.search(r'/\*([^*]|\*(?!/))*$', line): line += file.readline() # strip comments w/o options line = re.sub(r'''/\*(?!<) ([^*]+|\*(?!/))* \*/''', '', line, flags=re.X) line = line.rstrip() # skip empty lines if len(line.strip()) == 0: continue if looking_for_name: m = re.match(r'\s*(\w+)', line) if m: enumname = m.group(1) return True # Handle include files m = re.match(r'\#include\s*<([^>]*)>', line) if m: newfilename = os.path.join("..", m.group(1)) newfile = io.open(newfilename, encoding="utf-8", errors="replace_and_warn") if not parse_entries(newfile, newfilename): return False else: continue m = re.match(r'\s*\}\s*(\w+)', line) if m: enumname = m.group(1) enumindex += 1 return 1 m = re.match(r'\s*\}', line) if m: enumindex += 1 looking_for_name = True continue m = re.match(r'''\s* (\w+)\s* # name (?:=( # value \s*\w+\s*\(.*\)\s* # macro with multiple args | # OR (?:[^,/]|/(?!\*))* # anything but a comma or comment ))?,?\s* (?:/\*< # options (([^*]|\*(?!/))*) >\s*\*/)?,? \s*$''', line, flags=re.X) if m: groups = m.groups() name = groups[0] value = None options = None if len(groups) > 1: value = groups[1] if len(groups) > 2: options = groups[2] if flags is None and value is not None and '<<' in value: seenbitshift = 1 if options is not None: options = parse_trigraph(options) if 'skip' not in options: entries.append((name, value, options.get('nick'))) else: entries.append((name, value)) elif re.match(r's*\#', line): pass else: print_warning('Failed to parse "{}" in {}'.format(line, file_name)) return False help_epilog = '''Production text substitutions: \u0040EnumName\u0040 PrefixTheXEnum \u0040enum_name\u0040 prefix_the_xenum \u0040ENUMNAME\u0040 PREFIX_THE_XENUM \u0040ENUMSHORT\u0040 THE_XENUM \u0040ENUMPREFIX\u0040 PREFIX \u0040enumsince\u0040 the user-provided since value given (mm-mkenums only) \u0040VALUENAME\u0040 PREFIX_THE_XVALUE \u0040valuenick\u0040 the-xvalue \u0040valuenum\u0040 the integer value (limited support, Since: 2.26) \u0040type\u0040 either enum or flags \u0040Type\u0040 either Enum or Flags \u0040TYPE\u0040 either ENUM or FLAGS \u0040filename\u0040 name of current input file \u0040basename\u0040 base name of the current input file (Since: 2.22) ''' # production variables: idprefix = "" # "G", "Gtk", etc symprefix = "" # "g", "gtk", etc, if not just lc($idprefix) fhead = "" # output file header fprod = "" # per input file production ftail = "" # output file trailer eprod = "" # per enum text (produced prior to value itarations) vhead = "" # value header, produced before iterating over enum values vprod = "" # value text, produced for each enum value vtail = "" # value tail, produced after iterating over enum values comment_tmpl = "" # comment template def read_template_file(file): global idprefix, symprefix, fhead, fprod, ftail, eprod, vhead, vprod, vtail, comment_tmpl tmpl = {'file-header': fhead, 'file-production': fprod, 'file-tail': ftail, 'enumeration-production': eprod, 'value-header': vhead, 'value-production': vprod, 'value-tail': vtail, 'comment': comment_tmpl, } in_ = 'junk' ifile = io.open(file, encoding="utf-8", errors="replace_and_warn") for line in ifile: m = re.match(r'\/\*\*\*\s+(BEGIN|END)\s+([\w-]+)\s+\*\*\*\/', line) if m: if in_ == 'junk' and m.group(1) == 'BEGIN' and m.group(2) in tmpl: in_ = m.group(2) continue elif in_ == m.group(2) and m.group(1) == 'END' and m.group(2) in tmpl: in_ = 'junk' continue else: sys.exit("Malformed template file " + file) if in_ != 'junk': tmpl[in_] += line if in_ != 'junk': sys.exit("Malformed template file " + file) fhead = tmpl['file-header'] fprod = tmpl['file-production'] ftail = tmpl['file-tail'] eprod = tmpl['enumeration-production'] vhead = tmpl['value-header'] vprod = tmpl['value-production'] vtail = tmpl['value-tail'] comment_tmpl = tmpl['comment'] parser = argparse.ArgumentParser(epilog=help_epilog, formatter_class=argparse.RawDescriptionHelpFormatter) parser.add_argument('--identifier-prefix', default='', dest='idprefix', help='Identifier prefix') parser.add_argument('--symbol-prefix', default='', dest='symprefix', help='Symbol prefix') parser.add_argument('--fhead', default=[], dest='fhead', action='append', help='Output file header') parser.add_argument('--ftail', default=[], dest='ftail', action='append', help='Output file footer') parser.add_argument('--fprod', default=[], dest='fprod', action='append', help='Put out TEXT every time a new input file is being processed.') parser.add_argument('--eprod', default=[], dest='eprod', action='append', help='Per enum text, produced prior to value iterations') parser.add_argument('--vhead', default=[], dest='vhead', action='append', help='Value header, produced before iterating over enum values') parser.add_argument('--vprod', default=[], dest='vprod', action='append', help='Value text, produced for each enum value.') parser.add_argument('--vtail', default=[], dest='vtail', action='append', help='Value tail, produced after iterating over enum values') parser.add_argument('--comments', default='', dest='comment_tmpl', help='Comment structure') parser.add_argument('--enums-only', default=False, action='store_true', dest='enumsonly', help='Only process enums, not flags') parser.add_argument('--flags-only', default=False, action='store_true', dest='flagsonly', help='Only process flags, not enums') parser.add_argument('--template', default='', dest='template', help='Template file') parser.add_argument('--output', default=None, dest='output') parser.add_argument('--version', '-v', default=False, action='store_true', dest='version', help='Print version information') parser.add_argument('args', nargs='*', help='One or more input files, or a single argument @rspfile_path ' 'pointing to a file that contains the actual arguments') # Support reading an rspfile of the form @filename which contains the args # to be parsed if len(sys.argv) == 2 and sys.argv[1].startswith('@'): args = get_rspfile_args(sys.argv[1][1:]) else: args = sys.argv[1:] options = parser.parse_args(args) if options.version: print(VERSION_STR) sys.exit(0) def unescape_cmdline_args(arg): arg = arg.replace('\\n', '\n') arg = arg.replace('\\r', '\r') return arg.replace('\\t', '\t') if options.template != '': read_template_file(options.template) idprefix += options.idprefix symprefix += options.symprefix enumsonly = options.enumsonly flagsonly = options.flagsonly # This is a hack to maintain some semblance of backward compatibility with # the old, Perl-based glib-mkenums. The old tool had an implicit ordering # on the arguments and templates; each argument was parsed in order, and # all the strings appended. This allowed developers to write: # # glib-mkenums \ # --fhead ... \ # --template a-template-file.c.in \ # --ftail ... # # And have the fhead be prepended to the file-head stanza in the template, # as well as the ftail be appended to the file-tail stanza in the template. # Short of throwing away ArgumentParser and going over sys.argv[] element # by element, we can simulate that behaviour by ensuring some ordering in # how we build the template strings: # # - the head stanzas are always prepended to the template # - the prod stanzas are always appended to the template # - the tail stanzas are always appended to the template # # Within each instance of the command line argument, we append each value # to the array in the order in which it appears on the command line. fhead = ''.join([unescape_cmdline_args(x) for x in options.fhead]) + fhead vhead = ''.join([unescape_cmdline_args(x) for x in options.vhead]) + vhead fprod += ''.join([unescape_cmdline_args(x) for x in options.fprod]) eprod += ''.join([unescape_cmdline_args(x) for x in options.eprod]) vprod += ''.join([unescape_cmdline_args(x) for x in options.vprod]) ftail = ftail + ''.join([unescape_cmdline_args(x) for x in options.ftail]) vtail = vtail + ''.join([unescape_cmdline_args(x) for x in options.vtail]) if options.comment_tmpl != '': comment_tmpl = unescape_cmdline_args(options.comment_tmpl) elif comment_tmpl == "": # default to C-style comments comment_tmpl = "/* \u0040comment\u0040 */" output = options.output if output is not None: (out_dir, out_fn) = os.path.split(options.output) out_suffix = '_' + os.path.splitext(out_fn)[1] if out_dir == '': out_dir = '.' fd, filename = tempfile.mkstemp(dir=out_dir) os.close(fd) tmpfile = io.open(filename, "w", encoding="utf-8") output_stream = tmpfile else: tmpfile = None # put auto-generation comment comment = comment_tmpl.replace('\u0040comment\u0040', 'This file is generated by glib-mkenums, do ' 'not modify it. This code is licensed under ' 'the same license as the containing project. ' 'Note that it links to GLib, so must comply ' 'with the LGPL linking clauses.') write_output("\n" + comment + '\n') def replace_specials(prod): prod = prod.replace(r'\\a', r'\a') prod = prod.replace(r'\\b', r'\b') prod = prod.replace(r'\\t', r'\t') prod = prod.replace(r'\\n', r'\n') prod = prod.replace(r'\\f', r'\f') prod = prod.replace(r'\\r', r'\r') prod = prod.rstrip() return prod def warn_if_filename_basename_used(section, prod): for substitution in ('\u0040filename\u0040', '\u0040basename\u0040'): if substitution in prod: print_warning('{} used in {} section.'.format(substitution, section)) if len(fhead) > 0: prod = fhead warn_if_filename_basename_used('file-header', prod) prod = replace_specials(prod) write_output(prod) def process_file(curfilename): global entries, flags, seenbitshift, enum_prefix firstenum = True try: curfile = io.open(curfilename, encoding="utf-8", errors="replace_and_warn") except IOError as e: if e.errno == errno.ENOENT: print_warning('No file "{}" found.'.format(curfilename)) return raise while True: line = curfile.readline() if not line: break line = line.strip() # read lines until we have no open comments while re.search(r'/\*([^*]|\*(?!/))*$', line): line += curfile.readline() # strip comments w/o options line = re.sub(r'''/\*(?!<) ([^*]+|\*(?!/))* \*/''', '', line) # ignore forward declarations if re.match(r'\s*typedef\s+enum.*;', line): continue m = re.match(r'''\s*typedef\s+enum\s*[_A-Za-z]*[_A-Za-z0-9]*\s* ({)?\s* (?:/\*< (([^*]|\*(?!/))*) >\s*\*/)? \s*({)?''', line, flags=re.X) if m: groups = m.groups() if len(groups) >= 2 and groups[1] is not None: options = parse_trigraph(groups[1]) if 'skip' in options: continue enum_prefix = options.get('prefix', None) flags = options.get('flags', None) if 'flags' in options: if flags is None: flags = 1 else: flags = int(flags) option_lowercase_name = options.get('lowercase_name', None) option_underscore_name = options.get('underscore_name', None) option_since = options.get('since', None) else: enum_prefix = None flags = None option_lowercase_name = None option_underscore_name = None option_since = None if option_lowercase_name is not None: if option_underscore_name is not None: print_warning("lowercase_name overridden with underscore_name") option_lowercase_name = None else: print_warning("lowercase_name is deprecated, use underscore_name") # Didn't have trailing '{' look on next lines if groups[0] is None and (len(groups) < 4 or groups[3] is None): while True: line = curfile.readline() if not line: print_error("Syntax error when looking for opening { in enum") if re.match(r'\s*\{', line): break seenbitshift = 0 entries = [] # Now parse the entries parse_entries(curfile, curfilename) # figure out if this was a flags or enums enumeration if flags is None: flags = seenbitshift if flags and enumsonly: continue elif not flags and flagsonly: continue # Autogenerate a prefix if enum_prefix is None: for entry in entries: if len(entry) < 3 or entry[2] is None: name = entry[0] if enum_prefix is not None: enum_prefix = os.path.commonprefix([name, enum_prefix]) else: enum_prefix = name if enum_prefix is None: enum_prefix = "" else: # Trim so that it ends in an underscore enum_prefix = re.sub(r'_[^_]*$', '_', enum_prefix) else: # canonicalize user defined prefixes enum_prefix = enum_prefix.upper() enum_prefix = enum_prefix.replace('-', '_') enum_prefix = re.sub(r'(.*)([^_])$', r'\1\2_', enum_prefix) fixed_entries = [] for e in entries: name = e[0] num = e[1] if len(e) < 3 or e[2] is None: nick = re.sub(r'^' + enum_prefix, '', name) nick = nick.replace('_', '-').lower() e = (name, num, nick) fixed_entries.append(e) entries = fixed_entries # Spit out the output if option_underscore_name is not None: enumlong = option_underscore_name.upper() enumsym = option_underscore_name.lower() enumshort = re.sub(r'^[A-Z][A-Z0-9]*_', '', enumlong) enumname_prefix = re.sub('_' + enumshort + '$', '', enumlong) elif symprefix == '' and idprefix == '': # enumname is e.g. GMatchType enspace = re.sub(r'^([A-Z][a-z]*).*$', r'\1', enumname) enumshort = re.sub(r'^[A-Z][a-z]*', '', enumname) enumshort = re.sub(r'([^A-Z])([A-Z])', r'\1_\2', enumshort) enumshort = re.sub(r'([A-Z][A-Z])([A-Z][0-9a-z])', r'\1_\2', enumshort) enumshort = enumshort.upper() enumname_prefix = re.sub(r'^([A-Z][a-z]*).*$', r'\1', enumname).upper() enumlong = enspace.upper() + "_" + enumshort enumsym = enspace.lower() + "_" + enumshort.lower() if option_lowercase_name is not None: enumsym = option_lowercase_name else: enumshort = enumname if idprefix: enumshort = re.sub(r'^' + idprefix, '', enumshort) else: enumshort = re.sub(r'/^[A-Z][a-z]*', '', enumshort) enumshort = re.sub(r'([^A-Z])([A-Z])', r'\1_\2', enumshort) enumshort = re.sub(r'([A-Z][A-Z])([A-Z][0-9a-z])', r'\1_\2', enumshort) enumshort = enumshort.upper() if symprefix: enumname_prefix = symprefix.upper() else: enumname_prefix = idprefix.upper() enumlong = enumname_prefix + "_" + enumshort enumsym = enumlong.lower() if option_since is not None: enumsince = option_since else: enumsince = "" if firstenum: firstenum = False if len(fprod) > 0: prod = fprod base = os.path.basename(curfilename) prod = prod.replace('\u0040filename\u0040', curfilename) prod = prod.replace('\u0040basename\u0040', base) prod = replace_specials(prod) write_output(prod) if len(eprod) > 0: prod = eprod prod = prod.replace('\u0040enum_name\u0040', enumsym) prod = prod.replace('\u0040EnumName\u0040', enumname) prod = prod.replace('\u0040ENUMSHORT\u0040', enumshort) prod = prod.replace('\u0040ENUMNAME\u0040', enumlong) prod = prod.replace('\u0040ENUMPREFIX\u0040', enumname_prefix) prod = prod.replace('\u0040enumsince\u0040', enumsince) if flags: prod = prod.replace('\u0040type\u0040', 'flags') else: prod = prod.replace('\u0040type\u0040', 'enum') if flags: prod = prod.replace('\u0040Type\u0040', 'Flags') else: prod = prod.replace('\u0040Type\u0040', 'Enum') if flags: prod = prod.replace('\u0040TYPE\u0040', 'FLAGS') else: prod = prod.replace('\u0040TYPE\u0040', 'ENUM') prod = replace_specials(prod) write_output(prod) if len(vhead) > 0: prod = vhead prod = prod.replace('\u0040enum_name\u0040', enumsym) prod = prod.replace('\u0040EnumName\u0040', enumname) prod = prod.replace('\u0040ENUMSHORT\u0040', enumshort) prod = prod.replace('\u0040ENUMNAME\u0040', enumlong) prod = prod.replace('\u0040ENUMPREFIX\u0040', enumname_prefix) prod = prod.replace('\u0040enumsince\u0040', enumsince) if flags: prod = prod.replace('\u0040type\u0040', 'flags') else: prod = prod.replace('\u0040type\u0040', 'enum') if flags: prod = prod.replace('\u0040Type\u0040', 'Flags') else: prod = prod.replace('\u0040Type\u0040', 'Enum') if flags: prod = prod.replace('\u0040TYPE\u0040', 'FLAGS') else: prod = prod.replace('\u0040TYPE\u0040', 'ENUM') prod = replace_specials(prod) write_output(prod) if len(vprod) > 0: prod = vprod next_num = 0 prod = replace_specials(prod) for name, num, nick in entries: tmp_prod = prod if '\u0040valuenum\u0040' in prod: # only attempt to eval the value if it is requested # this prevents us from throwing errors otherwise if num is not None: # use sandboxed evaluation as a reasonable # approximation to C constant folding inum = eval(num, {}, {}) # make sure it parsed to an integer if not isinstance(inum, int): sys.exit("Unable to parse enum value '%s'" % num) num = inum else: num = next_num tmp_prod = tmp_prod.replace('\u0040valuenum\u0040', str(num)) next_num = int(num) + 1 tmp_prod = tmp_prod.replace('\u0040VALUENAME\u0040', name) tmp_prod = tmp_prod.replace('\u0040valuenick\u0040', nick) if flags: tmp_prod = tmp_prod.replace('\u0040type\u0040', 'flags') else: tmp_prod = tmp_prod.replace('\u0040type\u0040', 'enum') if flags: tmp_prod = tmp_prod.replace('\u0040Type\u0040', 'Flags') else: tmp_prod = tmp_prod.replace('\u0040Type\u0040', 'Enum') if flags: tmp_prod = tmp_prod.replace('\u0040TYPE\u0040', 'FLAGS') else: tmp_prod = tmp_prod.replace('\u0040TYPE\u0040', 'ENUM') tmp_prod = tmp_prod.rstrip() write_output(tmp_prod) if len(vtail) > 0: prod = vtail prod = prod.replace('\u0040enum_name\u0040', enumsym) prod = prod.replace('\u0040EnumName\u0040', enumname) prod = prod.replace('\u0040ENUMSHORT\u0040', enumshort) prod = prod.replace('\u0040ENUMNAME\u0040', enumlong) prod = prod.replace('\u0040ENUMPREFIX\u0040', enumname_prefix) prod = prod.replace('\u0040enumsince\u0040', enumsince) if flags: prod = prod.replace('\u0040type\u0040', 'flags') else: prod = prod.replace('\u0040type\u0040', 'enum') if flags: prod = prod.replace('\u0040Type\u0040', 'Flags') else: prod = prod.replace('\u0040Type\u0040', 'Enum') if flags: prod = prod.replace('\u0040TYPE\u0040', 'FLAGS') else: prod = prod.replace('\u0040TYPE\u0040', 'ENUM') prod = replace_specials(prod) write_output(prod) for fname in sorted(options.args): process_file(fname) if len(ftail) > 0: prod = ftail warn_if_filename_basename_used('file-tail', prod) prod = replace_specials(prod) write_output(prod) # put auto-generation comment comment = comment_tmpl comment = comment.replace('\u0040comment\u0040', 'Generated data ends here') write_output("\n" + comment + "\n") if tmpfile is not None: tmpfilename = tmpfile.name tmpfile.close() try: os.unlink(options.output) except OSError as error: if error.errno != errno.ENOENT: raise error os.rename(tmpfilename, options.output) ModemManager-1.23.4-dev/build-aux/templates/000077500000000000000000000000001456466623000206155ustar00rootroot00000000000000ModemManager-1.23.4-dev/build-aux/templates/mm-daemon-enums-types.c.template000077700000000000000000000000001456466623000344372mm-enumflags-types.c.templateustar00rootroot00000000000000ModemManager-1.23.4-dev/build-aux/templates/mm-daemon-enums-types.h.template000077700000000000000000000000001456466623000344512mm-enumflags-types.h.templateustar00rootroot00000000000000ModemManager-1.23.4-dev/build-aux/templates/mm-enumflags-types.c.template000066400000000000000000000045461456466623000263360ustar00rootroot00000000000000/*** BEGIN file-header ***/ /*** END file-header ***/ /*** BEGIN file-production ***/ /* enumerations from "@filename@" */ /*** END file-production ***/ /*** BEGIN value-header ***/ static const G@Type@Value @enum_name@_values[] = { /*** END value-header ***/ /*** BEGIN value-production ***/ { @VALUENAME@, "@VALUENAME@", "@valuenick@" }, /*** END value-production ***/ /*** BEGIN value-tail ***/ { 0, NULL, NULL } }; GType @enum_name@_get_type (void) { static gsize g_define_type_id_initialized = 0; if (g_once_init_enter (&g_define_type_id_initialized)) { GType g_define_type_id = g_@type@_register_static (g_intern_static_string ("@EnumName@"), @enum_name@_values); g_once_init_leave (&g_define_type_id_initialized, g_define_type_id); } return g_define_type_id_initialized; } #if defined __@ENUMNAME@_IS_ENUM__ const gchar * @enum_name@_get_string (@EnumName@ val) { guint i; for (i = 0; @enum_name@_values[i].value_nick; i++) { if ((gint)val == @enum_name@_values[i].value) return @enum_name@_values[i].value_nick; } return NULL; } #endif #if defined __@ENUMNAME@_IS_FLAGS__ gchar * @enum_name@_build_string_from_mask (@EnumName@ mask) { guint i; gboolean first = TRUE; GString *str = NULL; for (i = 0; @enum_name@_values[i].value_nick; i++) { /* We also look for exact matches */ if (mask == @enum_name@_values[i].value) { if (str) g_string_free (str, TRUE); return g_strdup (@enum_name@_values[i].value_nick); } /* Build list with single-bit masks */ if (mask & @enum_name@_values[i].value) { guint c; gulong number = @enum_name@_values[i].value; for (c = 0; number; c++) number &= number - 1; if (c == 1) { if (!str) str = g_string_new (""); g_string_append_printf (str, "%s%s", first ? "" : ", ", @enum_name@_values[i].value_nick); if (first) first = FALSE; } } } return (str ? g_string_free (str, FALSE) : NULL); } #endif /*** END value-tail ***/ /*** BEGIN file-tail ***/ /*** END file-tail ***/ ModemManager-1.23.4-dev/build-aux/templates/mm-enumflags-types.h.template000066400000000000000000000012751456466623000263370ustar00rootroot00000000000000/*** BEGIN file-header ***/ #include G_BEGIN_DECLS /*** END file-header ***/ /*** BEGIN file-production ***/ /* enumerations from "@basename@" */ /*** END file-production ***/ /*** BEGIN value-header ***/ GType @enum_name@_get_type (void) G_GNUC_CONST; #define @ENUMPREFIX@_TYPE_@ENUMSHORT@ (@enum_name@_get_type ()) /* Define type-specific symbols */ #define __@ENUMNAME@_IS_@TYPE@__ #if defined __@ENUMNAME@_IS_ENUM__ const gchar *@enum_name@_get_string (@EnumName@ val); #endif #if defined __@ENUMNAME@_IS_FLAGS__ gchar *@enum_name@_build_string_from_mask (@EnumName@ mask); #endif /*** END value-header ***/ /*** BEGIN file-tail ***/ G_END_DECLS /*** END file-tail ***/ ModemManager-1.23.4-dev/build-aux/templates/mm-enums-types.c.template000066400000000000000000000022761456466623000255020ustar00rootroot00000000000000/*** BEGIN file-header ***/ /*** END file-header ***/ /*** BEGIN file-production ***/ /* enumerations from "@filename@" */ /*** END file-production ***/ /*** BEGIN value-header ***/ static const G@Type@Value @enum_name@_values[] = { /*** END value-header ***/ /*** BEGIN value-production ***/ { @VALUENAME@, "@VALUENAME@", "@valuenick@" }, /*** END value-production ***/ /*** BEGIN value-tail ***/ { 0, NULL, NULL } }; GType @enum_name@_get_type (void) { static gsize g_define_type_id_initialized = 0; if (g_once_init_enter (&g_define_type_id_initialized)) { GType g_define_type_id = g_@type@_register_static (g_intern_static_string ("@EnumName@"), @enum_name@_values); g_once_init_leave (&g_define_type_id_initialized, g_define_type_id); } return g_define_type_id_initialized; } const gchar * @enum_name@_get_string (@EnumName@ val) { guint i; for (i = 0; @enum_name@_values[i].value_nick; i++) { if ((gint)val == @enum_name@_values[i].value) return @enum_name@_values[i].value_nick; } return NULL; } /*** END value-tail ***/ /*** BEGIN file-tail ***/ /*** END file-tail ***/ ModemManager-1.23.4-dev/build-aux/templates/mm-enums-types.h.template000066400000000000000000000013301456466623000254750ustar00rootroot00000000000000/*** BEGIN file-header ***/ #include G_BEGIN_DECLS /*** END file-header ***/ /*** BEGIN file-production ***/ /* enumerations from "@basename@" */ /*** END file-production ***/ /*** BEGIN value-header ***/ GType @enum_name@_get_type (void) G_GNUC_CONST; #define @ENUMPREFIX@_TYPE_@ENUMSHORT@ (@enum_name@_get_type ()) /** * @enum_name@_get_string: * @val: a @EnumName@. * * Gets the nickname string for the #@EnumName@ specified at @val. * * Returns: (transfer none): a string with the nickname, or %NULL if not found. Do not free the returned value. */ const gchar *@enum_name@_get_string (@EnumName@ val); /*** END value-header ***/ /*** BEGIN file-tail ***/ G_END_DECLS /*** END file-tail ***/ ModemManager-1.23.4-dev/build-aux/templates/mm-errors-quarks.c.template000066400000000000000000000017431456466623000260270ustar00rootroot00000000000000/*** BEGIN file-header ***/ #include /*** END file-header ***/ /*** BEGIN file-production ***/ /* enumerations from "@filename@" */ /*** END file-production ***/ /*** BEGIN value-header ***/ #define ERROR_PREFIX @ENUMNAME@_DBUS_PREFIX static const GDBusErrorEntry @enum_name@_entries[] = { /*** END value-header ***/ /*** BEGIN value-production ***/ { @VALUENAME@, ERROR_PREFIX ".@valuenick@" }, /*** END value-production ***/ /*** BEGIN value-tail ***/ }; #undef ERROR_PREFIX GQuark @enum_name@_quark (void) { static volatile gsize quark_volatile = 0; if (!quark_volatile) g_dbus_error_register_error_domain ("@enum_name@_quark", &quark_volatile, @enum_name@_entries, G_N_ELEMENTS (@enum_name@_entries)); return (GQuark) quark_volatile; } /*** END value-tail ***/ /*** BEGIN file-tail ***/ /*** END file-tail ***/ ModemManager-1.23.4-dev/build-aux/templates/mm-errors-types.c.template000066400000000000000000000017031456466623000256610ustar00rootroot00000000000000/*** BEGIN file-header ***/ /*** END file-header ***/ /*** BEGIN file-production ***/ /* enumerations from "@filename@" */ /*** END file-production ***/ /*** BEGIN value-header ***/ /* @enum_name@_quark() implemented in mm-errors-quarks.c */ GType @enum_name@_get_type (void) { static gsize g_define_type_id_initialized = 0; if (g_once_init_enter (&g_define_type_id_initialized)) { static const G@Type@Value values[] = { /*** END value-header ***/ /*** BEGIN value-production ***/ { @VALUENAME@, "@VALUENAME@", "@valuenick@" }, /*** END value-production ***/ /*** BEGIN value-tail ***/ { 0, NULL, NULL } }; GType g_define_type_id = g_@type@_register_static (g_intern_static_string ("@EnumName@"), values); g_once_init_leave (&g_define_type_id_initialized, g_define_type_id); } return g_define_type_id_initialized; } /*** END value-tail ***/ /*** BEGIN file-tail ***/ /*** END file-tail ***/ ModemManager-1.23.4-dev/build-aux/templates/mm-errors-types.h.template000066400000000000000000000010441456466623000256640ustar00rootroot00000000000000/*** BEGIN file-header ***/ #include G_BEGIN_DECLS /*** END file-header ***/ /*** BEGIN file-production ***/ /* enumerations from "@basename@" */ /*** END file-production ***/ /*** BEGIN value-header ***/ GQuark @enum_name@_quark (void); /* implemented in mm-errors-quarks.c */ GType @enum_name@_get_type (void) G_GNUC_CONST; #define @ENUMNAME@ (@enum_name@_quark ()) #define @ENUMPREFIX@_TYPE_@ENUMSHORT@ (@enum_name@_get_type ()) /*** END value-header ***/ /*** BEGIN file-tail ***/ G_END_DECLS /*** END file-tail ***/ ModemManager-1.23.4-dev/build-aux/templates/mm-flags-types.c.template000066400000000000000000000040141456466623000254370ustar00rootroot00000000000000/*** BEGIN file-header ***/ /*** END file-header ***/ /*** BEGIN file-production ***/ /* enumerations from "@filename@" */ /*** END file-production ***/ /*** BEGIN value-header ***/ static const G@Type@Value @enum_name@_values[] = { /*** END value-header ***/ /*** BEGIN value-production ***/ { @VALUENAME@, "@VALUENAME@", "@valuenick@" }, /*** END value-production ***/ /*** BEGIN value-tail ***/ { 0, NULL, NULL } }; GType @enum_name@_get_type (void) { static gsize g_define_type_id_initialized = 0; if (g_once_init_enter (&g_define_type_id_initialized)) { GType g_define_type_id = g_@type@_register_static (g_intern_static_string ("@EnumName@"), @enum_name@_values); g_once_init_leave (&g_define_type_id_initialized, g_define_type_id); } return g_define_type_id_initialized; } gchar * @enum_name@_build_string_from_mask (@EnumName@ mask) { guint i; gboolean first = TRUE; GString *str = NULL; for (i = 0; @enum_name@_values[i].value_nick; i++) { /* We also look for exact matches */ if (mask == @enum_name@_values[i].value) { if (str) g_string_free (str, TRUE); return g_strdup (@enum_name@_values[i].value_nick); } /* Build list with single-bit masks */ if (mask & @enum_name@_values[i].value) { guint c; gulong number = @enum_name@_values[i].value; for (c = 0; number; c++) number &= number - 1; if (c == 1) { if (!str) str = g_string_new (""); g_string_append_printf (str, "%s%s", first ? "" : ", ", @enum_name@_values[i].value_nick); if (first) first = FALSE; } } } return (str ? g_string_free (str, FALSE) : NULL); } /*** END value-tail ***/ /*** BEGIN file-tail ***/ /*** END file-tail ***/ ModemManager-1.23.4-dev/build-aux/templates/mm-flags-types.h.template000066400000000000000000000014711456466623000254500ustar00rootroot00000000000000/*** BEGIN file-header ***/ #include G_BEGIN_DECLS /*** END file-header ***/ /*** BEGIN file-production ***/ /* enumerations from "@basename@" */ /*** END file-production ***/ /*** BEGIN value-header ***/ GType @enum_name@_get_type (void) G_GNUC_CONST; #define @ENUMPREFIX@_TYPE_@ENUMSHORT@ (@enum_name@_get_type ()) /** * @enum_name@_build_string_from_mask: * @mask: bitmask of @EnumName@ values. * * Builds a string containing a comma-separated list of nicknames for * each #@EnumName@ in @mask. * * Returns: (transfer full): a string with the list of nicknames, or %NULL if none given. The returned value should be freed with g_free(). */ gchar *@enum_name@_build_string_from_mask (@EnumName@ mask); /*** END value-header ***/ /*** BEGIN file-tail ***/ G_END_DECLS /*** END file-tail ***/ ModemManager-1.23.4-dev/build-aux/templates/mm-helper-enums-types.c.template000077700000000000000000000000001456466623000344532mm-enumflags-types.c.templateustar00rootroot00000000000000ModemManager-1.23.4-dev/build-aux/templates/mm-helper-enums-types.h.template000077700000000000000000000000001456466623000344652mm-enumflags-types.h.templateustar00rootroot00000000000000ModemManager-1.23.4-dev/build-aux/templates/mm-huawei-enums-types.c.template000077700000000000000000000000001456466623000344562mm-enumflags-types.c.templateustar00rootroot00000000000000ModemManager-1.23.4-dev/build-aux/templates/mm-huawei-enums-types.h.template000077700000000000000000000000001456466623000344702mm-enumflags-types.h.templateustar00rootroot00000000000000ModemManager-1.23.4-dev/build-aux/templates/mm-port-enums-types.c.template000077700000000000000000000000001456466623000341602mm-enumflags-types.c.templateustar00rootroot00000000000000ModemManager-1.23.4-dev/build-aux/templates/mm-port-enums-types.h.template000077700000000000000000000000001456466623000341722mm-enumflags-types.h.templateustar00rootroot00000000000000ModemManager-1.23.4-dev/build-aux/templates/mm-telit-enums-types.c.template000077700000000000000000000000001456466623000343152mm-enumflags-types.c.templateustar00rootroot00000000000000ModemManager-1.23.4-dev/build-aux/templates/mm-telit-enums-types.h.template000077700000000000000000000000001456466623000343272mm-enumflags-types.h.templateustar00rootroot00000000000000ModemManager-1.23.4-dev/build-aux/templates/mm-ublox-enums-types.c.template000077700000000000000000000000001456466623000343252mm-enumflags-types.c.templateustar00rootroot00000000000000ModemManager-1.23.4-dev/build-aux/templates/mm-ublox-enums-types.h.template000077700000000000000000000000001456466623000343372mm-enumflags-types.h.templateustar00rootroot00000000000000ModemManager-1.23.4-dev/cli/000077500000000000000000000000001456466623000154745ustar00rootroot00000000000000ModemManager-1.23.4-dev/cli/meson.build000066400000000000000000000017141456466623000176410ustar00rootroot00000000000000# SPDX-License-Identifier: GPL-2.0-or-later # Copyright (C) 2021 Iñigo Martinez sources = files( 'mmcli-bearer.c', 'mmcli.c', 'mmcli-call.c', 'mmcli-common.c', 'mmcli-manager.c', 'mmcli-modem-3gpp.c', 'mmcli-modem-3gpp-profile-manager.c', 'mmcli-modem-3gpp-ussd.c', 'mmcli-modem.c', 'mmcli-modem-cdma.c', 'mmcli-modem-firmware.c', 'mmcli-modem-location.c', 'mmcli-modem-messaging.c', 'mmcli-modem-oma.c', 'mmcli-modem-sar.c', 'mmcli-modem-signal.c', 'mmcli-modem-simple.c', 'mmcli-modem-time.c', 'mmcli-modem-voice.c', 'mmcli-output.c', 'mmcli-sim.c', 'mmcli-sms.c', ) deps = [libmm_glib_dep] if enable_udev deps += gudev_dep endif executable( 'mmcli', sources: sources, include_directories: top_inc, dependencies: deps, install: true, ) if enable_bash_completion install_data( 'mmcli-completion', install_dir: bash_completion_completionsdir, rename: 'mmcli', ) endif ModemManager-1.23.4-dev/cli/mmcli-bearer.c000066400000000000000000000444231456466623000202060ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * mmcli -- Control bearer status & access information from the command line * * 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, see . * * Copyright (C) 2011-2018 Aleksander Morgado */ #include "config.h" #include #include #include #include #include #include #define _LIBMM_INSIDE_MMCLI #include #include "mmcli.h" #include "mmcli-common.h" #include "mmcli-output.h" /* Context */ typedef struct { MMManager *manager; MMObject *object; GCancellable *cancellable; MMBearer *bearer; } Context; static Context *ctx; /* Options */ static gboolean info_flag; /* set when no action found */ static gboolean connect_flag; static gboolean disconnect_flag; static GOptionEntry entries[] = { { "connect", 'c', 0, G_OPTION_ARG_NONE, &connect_flag, "Connect a given bearer.", NULL }, { "disconnect", 'x', 0, G_OPTION_ARG_NONE, &disconnect_flag, "Disconnect a given bearer.", NULL }, { NULL } }; GOptionGroup * mmcli_bearer_get_option_group (void) { GOptionGroup *group; /* Status options */ group = g_option_group_new ("bearer", "Bearer options:", "Show bearer options", NULL, NULL); g_option_group_add_entries (group, entries); return group; } gboolean mmcli_bearer_options_enabled (void) { static guint n_actions = 0; static gboolean checked = FALSE; if (checked) return !!n_actions; n_actions = (connect_flag + disconnect_flag); if (n_actions == 0 && mmcli_get_common_bearer_string ()) { /* default to info */ info_flag = TRUE; n_actions++; } if (n_actions > 1) { g_printerr ("error: too many bearer actions requested\n"); exit (EXIT_FAILURE); } if (info_flag) mmcli_force_sync_operation (); checked = TRUE; return !!n_actions; } static void context_free (void) { if (!ctx) return; if (ctx->cancellable) g_object_unref (ctx->cancellable); if (ctx->bearer) g_object_unref (ctx->bearer); if (ctx->object) g_object_unref (ctx->object); if (ctx->manager) g_object_unref (ctx->manager); g_free (ctx); } void mmcli_bearer_shutdown (void) { context_free (); } static void print_bearer_info (MMBearer *bearer) { g_autoptr(MMBearerIpConfig) ipv4_config = NULL; g_autoptr(MMBearerIpConfig) ipv6_config = NULL; g_autoptr(MMBearerProperties) properties = NULL; g_autoptr(MMBearerStats) stats = NULL; g_autoptr(GError) connection_error = NULL; gint profile_id; gchar *profile_id_str; ipv4_config = mm_bearer_get_ipv4_config (bearer); ipv6_config = mm_bearer_get_ipv6_config (bearer); properties = mm_bearer_get_properties (bearer); stats = mm_bearer_get_stats (bearer); profile_id = mm_bearer_get_profile_id (bearer); connection_error = mm_bearer_get_connection_error (bearer); profile_id_str = (profile_id != MM_3GPP_PROFILE_ID_UNKNOWN) ? g_strdup_printf ("%d", profile_id) : NULL; mmcli_output_string (MMC_F_BEARER_GENERAL_DBUS_PATH, mm_bearer_get_path (bearer)); mmcli_output_string (MMC_F_BEARER_GENERAL_TYPE, mm_bearer_type_get_string (mm_bearer_get_bearer_type (bearer))); mmcli_output_string (MMC_F_BEARER_STATUS_CONNECTED, mm_bearer_get_connected (bearer) ? "yes" : "no"); mmcli_output_string_take (MMC_F_BEARER_STATUS_CONNECTION_ERROR_NAME, connection_error ? g_dbus_error_encode_gerror (connection_error) : NULL); mmcli_output_string (MMC_F_BEARER_STATUS_CONNECTION_ERROR_MESSAGE, connection_error ? connection_error->message : NULL); mmcli_output_string (MMC_F_BEARER_STATUS_SUSPENDED, mm_bearer_get_suspended (bearer) ? "yes" : "no"); mmcli_output_string (MMC_F_BEARER_STATUS_MULTIPLEXED, mm_bearer_get_multiplexed (bearer) ? "yes" : "no"); mmcli_output_string (MMC_F_BEARER_STATUS_INTERFACE, mm_bearer_get_interface (bearer)); mmcli_output_string_take (MMC_F_BEARER_STATUS_IP_TIMEOUT, g_strdup_printf ("%u", mm_bearer_get_ip_timeout (bearer))); mmcli_output_string_take (MMC_F_BEARER_STATUS_PROFILE_ID, profile_id_str); /* Properties */ { const gchar *apn = NULL; gchar *apn_type_str = NULL; const gchar *roaming = NULL; gchar *ip_family_str = NULL; const gchar *user = NULL; const gchar *password = NULL; const gchar *rm_protocol = NULL; gchar *allowed_auth_str = NULL; gchar *properties_profile_id_str = NULL; const gchar *access_type_preference_str = NULL; gchar *roaming_allowance_str = NULL; if (properties) { gint properties_profile_id; properties_profile_id = mm_bearer_properties_get_profile_id (properties); if (properties_profile_id != MM_3GPP_PROFILE_ID_UNKNOWN) properties_profile_id_str = g_strdup_printf ("%d", properties_profile_id); apn = mm_bearer_properties_get_apn (properties); apn_type_str = mm_bearer_apn_type_build_string_from_mask (mm_bearer_properties_get_apn_type (properties)); ip_family_str = mm_bearer_ip_family_build_string_from_mask (mm_bearer_properties_get_ip_type (properties)); allowed_auth_str = mm_bearer_allowed_auth_build_string_from_mask (mm_bearer_properties_get_allowed_auth (properties)); user = mm_bearer_properties_get_user (properties); password = mm_bearer_properties_get_password (properties); if (mm_bearer_get_bearer_type (bearer) != MM_BEARER_TYPE_DEFAULT_ATTACH) { roaming = mm_bearer_properties_get_allow_roaming (properties) ? "allowed" : "forbidden"; rm_protocol = mm_modem_cdma_rm_protocol_get_string (mm_bearer_properties_get_rm_protocol (properties)); } access_type_preference_str = mm_bearer_access_type_preference_get_string (mm_bearer_properties_get_access_type_preference (properties)); roaming_allowance_str = mm_bearer_roaming_allowance_build_string_from_mask (mm_bearer_properties_get_roaming_allowance (properties)); } mmcli_output_string_take (MMC_F_BEARER_PROPERTIES_PROFILE_ID, properties_profile_id_str); mmcli_output_string (MMC_F_BEARER_PROPERTIES_APN, apn); mmcli_output_string_take (MMC_F_BEARER_PROPERTIES_APN_TYPE, apn_type_str); mmcli_output_string (MMC_F_BEARER_PROPERTIES_ROAMING, roaming); mmcli_output_string_take (MMC_F_BEARER_PROPERTIES_IP_TYPE, ip_family_str); mmcli_output_string (MMC_F_BEARER_PROPERTIES_USER, user); mmcli_output_string (MMC_F_BEARER_PROPERTIES_PASSWORD, password); mmcli_output_string (MMC_F_BEARER_PROPERTIES_RM_PROTOCOL, rm_protocol); mmcli_output_string_list_take (MMC_F_BEARER_PROPERTIES_ALLOWED_AUTH, allowed_auth_str); mmcli_output_string (MMC_F_BEARER_PROPERTIES_ACCESS_TYPE_PREFERENCE, access_type_preference_str); mmcli_output_string_take (MMC_F_BEARER_PROPERTIES_ROAMING_ALLOWANCE, roaming_allowance_str); } /* IPv4 config */ { const gchar *method = NULL; const gchar *address = NULL; gchar *prefix = NULL; const gchar *gateway = NULL; const gchar **dns = NULL; gchar *mtu = NULL; if (ipv4_config) { method = mm_bearer_ip_method_get_string (mm_bearer_ip_config_get_method (ipv4_config)); if (mm_bearer_ip_config_get_method (ipv4_config) != MM_BEARER_IP_METHOD_UNKNOWN) { guint mtu_n; address = mm_bearer_ip_config_get_address (ipv4_config); prefix = g_strdup_printf ("%u", mm_bearer_ip_config_get_prefix (ipv4_config)); gateway = mm_bearer_ip_config_get_gateway (ipv4_config); dns = mm_bearer_ip_config_get_dns (ipv4_config); mtu_n = mm_bearer_ip_config_get_mtu (ipv4_config); if (mtu_n) mtu = g_strdup_printf ("%u", mtu_n); } } mmcli_output_string (MMC_F_BEARER_IPV4_CONFIG_METHOD, method); mmcli_output_string (MMC_F_BEARER_IPV4_CONFIG_ADDRESS, address); mmcli_output_string_take (MMC_F_BEARER_IPV4_CONFIG_PREFIX, prefix); mmcli_output_string (MMC_F_BEARER_IPV4_CONFIG_GATEWAY, gateway); mmcli_output_string_array (MMC_F_BEARER_IPV4_CONFIG_DNS, dns, FALSE); mmcli_output_string_take (MMC_F_BEARER_IPV4_CONFIG_MTU, mtu); } /* IPv6 config */ { const gchar *method = NULL; const gchar *address = NULL; gchar *prefix = NULL; const gchar *gateway = NULL; const gchar **dns = NULL; gchar *mtu = NULL; if (ipv6_config) { method = mm_bearer_ip_method_get_string (mm_bearer_ip_config_get_method (ipv6_config)); if (mm_bearer_ip_config_get_method (ipv6_config) != MM_BEARER_IP_METHOD_UNKNOWN) { guint mtu_n; address = mm_bearer_ip_config_get_address (ipv6_config); prefix = g_strdup_printf ("%u", mm_bearer_ip_config_get_prefix (ipv6_config)); gateway = mm_bearer_ip_config_get_gateway (ipv6_config); dns = mm_bearer_ip_config_get_dns (ipv6_config); mtu_n = mm_bearer_ip_config_get_mtu (ipv6_config); if (mtu_n) mtu = g_strdup_printf ("%u", mtu_n); } } mmcli_output_string (MMC_F_BEARER_IPV6_CONFIG_METHOD, method); mmcli_output_string (MMC_F_BEARER_IPV6_CONFIG_ADDRESS, address); mmcli_output_string_take (MMC_F_BEARER_IPV6_CONFIG_PREFIX, prefix); mmcli_output_string (MMC_F_BEARER_IPV6_CONFIG_GATEWAY, gateway); mmcli_output_string_array (MMC_F_BEARER_IPV6_CONFIG_DNS, dns, FALSE); mmcli_output_string_take (MMC_F_BEARER_IPV6_CONFIG_MTU, mtu); } /* Stats */ { guint64 start_date = 0; gchar *duration = NULL; gchar *bytes_rx = NULL; gchar *bytes_tx = NULL; gchar *attempts = NULL; gchar *failed_attempts = NULL; gchar *total_duration = NULL; gchar *total_bytes_rx = NULL; gchar *total_bytes_tx = NULL; gchar *uplink_speed = NULL; gchar *downlink_speed = NULL; if (stats) { guint64 val; start_date = mm_bearer_stats_get_start_date (stats); val = mm_bearer_stats_get_duration (stats); if (val) duration = g_strdup_printf ("%" G_GUINT64_FORMAT, val); val = mm_bearer_stats_get_rx_bytes (stats); if (val) bytes_rx = g_strdup_printf ("%" G_GUINT64_FORMAT, val); val = mm_bearer_stats_get_tx_bytes (stats); if (val) bytes_tx = g_strdup_printf ("%" G_GUINT64_FORMAT, val); val = mm_bearer_stats_get_attempts (stats); if (val) attempts = g_strdup_printf ("%" G_GUINT64_FORMAT, val); val = mm_bearer_stats_get_failed_attempts (stats); if (val) failed_attempts = g_strdup_printf ("%" G_GUINT64_FORMAT, val); val = mm_bearer_stats_get_total_duration (stats); if (val) total_duration = g_strdup_printf ("%" G_GUINT64_FORMAT, val); val = mm_bearer_stats_get_total_rx_bytes (stats); if (val) total_bytes_rx = g_strdup_printf ("%" G_GUINT64_FORMAT, val); val = mm_bearer_stats_get_total_tx_bytes (stats); if (val) total_bytes_tx = g_strdup_printf ("%" G_GUINT64_FORMAT, val); val = mm_bearer_stats_get_uplink_speed (stats); if (val) uplink_speed = g_strdup_printf ("%" G_GUINT64_FORMAT, val); val = mm_bearer_stats_get_downlink_speed (stats); if (val) downlink_speed = g_strdup_printf ("%" G_GUINT64_FORMAT, val); } if (start_date) mmcli_output_start_date (start_date); mmcli_output_string_take (MMC_F_BEARER_STATS_DURATION, duration); mmcli_output_string_take (MMC_F_BEARER_STATS_BYTES_RX, bytes_rx); mmcli_output_string_take (MMC_F_BEARER_STATS_BYTES_TX, bytes_tx); mmcli_output_string_take (MMC_F_BEARER_STATS_ATTEMPTS, attempts); mmcli_output_string_take (MMC_F_BEARER_STATS_FAILED_ATTEMPTS, failed_attempts); mmcli_output_string_take (MMC_F_BEARER_STATS_TOTAL_DURATION, total_duration); mmcli_output_string_take (MMC_F_BEARER_STATS_TOTAL_BYTES_RX, total_bytes_rx); mmcli_output_string_take (MMC_F_BEARER_STATS_TOTAL_BYTES_TX, total_bytes_tx); mmcli_output_string_take (MMC_F_BEARER_STATS_UPLINK_SPEED, uplink_speed); mmcli_output_string_take (MMC_F_BEARER_STATS_DOWNLINK_SPEED, downlink_speed); } mmcli_output_dump (); } static void connect_process_reply (gboolean result, const GError *error) { if (!result) { g_printerr ("error: couldn't connect the bearer: '%s'\n", error ? error->message : "unknown error"); exit (EXIT_FAILURE); } g_print ("successfully connected the bearer\n"); } static void connect_ready (MMBearer *bearer, GAsyncResult *result, gpointer nothing) { gboolean operation_result; GError *error = NULL; operation_result = mm_bearer_connect_finish (bearer, result, &error); connect_process_reply (operation_result, error); mmcli_async_operation_done (); } static void disconnect_process_reply (gboolean result, const GError *error) { if (!result) { g_printerr ("error: couldn't disconnect the bearer: '%s'\n", error ? error->message : "unknown error"); exit (EXIT_FAILURE); } g_print ("successfully disconnected the bearer\n"); } static void disconnect_ready (MMBearer *bearer, GAsyncResult *result, gpointer nothing) { gboolean operation_result; GError *error = NULL; operation_result = mm_bearer_disconnect_finish (bearer, result, &error); disconnect_process_reply (operation_result, error); mmcli_async_operation_done (); } static void get_bearer_ready (GObject *source, GAsyncResult *result, gpointer none) { ctx->bearer = mmcli_get_bearer_finish (result, &ctx->manager, &ctx->object); if (info_flag) g_assert_not_reached (); /* Request to connect the bearer? */ if (connect_flag) { g_debug ("Asynchronously connecting bearer..."); mm_bearer_connect (ctx->bearer, ctx->cancellable, (GAsyncReadyCallback)connect_ready, NULL); return; } /* Request to disconnect the bearer? */ if (disconnect_flag) { g_debug ("Asynchronously disconnecting bearer..."); mm_bearer_disconnect (ctx->bearer, ctx->cancellable, (GAsyncReadyCallback)disconnect_ready, NULL); return; } g_warn_if_reached (); } void mmcli_bearer_run_asynchronous (GDBusConnection *connection, GCancellable *cancellable) { /* Initialize context */ ctx = g_new0 (Context, 1); if (cancellable) ctx->cancellable = g_object_ref (cancellable); /* Get proper bearer */ mmcli_get_bearer (connection, mmcli_get_common_bearer_string (), cancellable, (GAsyncReadyCallback)get_bearer_ready, NULL); } void mmcli_bearer_run_synchronous (GDBusConnection *connection) { GError *error = NULL; /* Initialize context */ ctx = g_new0 (Context, 1); ctx->bearer = mmcli_get_bearer_sync (connection, mmcli_get_common_bearer_string (), &ctx->manager, &ctx->object); /* Request to get info from bearer? */ if (info_flag) { g_debug ("Printing bearer info..."); print_bearer_info (ctx->bearer); return; } /* Request to connect the bearer? */ if (connect_flag) { gboolean result; g_debug ("Synchronously connecting bearer..."); result = mm_bearer_connect_sync (ctx->bearer, NULL, &error); connect_process_reply (result, error); return; } /* Request to disconnect the bearer? */ if (disconnect_flag) { gboolean result; g_debug ("Synchronously disconnecting bearer..."); result = mm_bearer_disconnect_sync (ctx->bearer, NULL, &error); disconnect_process_reply (result, error); return; } g_warn_if_reached (); } ModemManager-1.23.4-dev/cli/mmcli-call.c000066400000000000000000000407011456466623000176540ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * mmcli -- Control call status & access information from the command line * * 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, see . * * Copyright (C) 2015 Riccardo Vangelisti * Copyright (C) 2015 Marco Bascetta */ #include "config.h" #include #include #include #include #include #include #define _LIBMM_INSIDE_MMCLI #include #include "mmcli.h" #include "mmcli-common.h" #include "mmcli-output.h" /* Context */ typedef struct { MMManager *manager; MMObject *object; GCancellable *cancellable; MMCall *call; } Context; static Context *ctx; /* Options */ static gboolean info_flag; /* set when no action found */ static gboolean start_flag; static gboolean accept_flag; static gchar *deflect_str; static gboolean join_multiparty_flag; static gboolean leave_multiparty_flag; static gboolean hangup_flag; static gchar *dtmf_request; static GOptionEntry entries[] = { { "start", 0, 0, G_OPTION_ARG_NONE, &start_flag, "Start the call.", NULL, }, { "accept", 0, 0, G_OPTION_ARG_NONE, &accept_flag, "Accept the incoming call", NULL, }, { "deflect", 0, 0, G_OPTION_ARG_STRING, &deflect_str, "Deflect the incoming call", "[NUMBER]", }, { "join-multiparty", 0, 0, G_OPTION_ARG_NONE, &join_multiparty_flag, "Join multiparty call", NULL, }, { "leave-multiparty", 0, 0, G_OPTION_ARG_NONE, &leave_multiparty_flag, "Leave multiparty call", NULL, }, { "hangup", 0, 0, G_OPTION_ARG_NONE, &hangup_flag, "Hang up the call", NULL, }, { "send-dtmf", 0, 0, G_OPTION_ARG_STRING, &dtmf_request, "Send specified DTMF tone", "[0-9A-D*#]" }, { NULL } }; GOptionGroup * mmcli_call_get_option_group (void) { GOptionGroup *group; /* Status options */ group = g_option_group_new ("call", "Call options:", "Show call options", NULL, NULL); g_option_group_add_entries (group, entries); return group; } gboolean mmcli_call_options_enabled (void) { static guint n_actions = 0; static gboolean checked = FALSE; if (checked) return !!n_actions; n_actions = (start_flag + accept_flag + !!deflect_str + join_multiparty_flag + leave_multiparty_flag + hangup_flag + !!dtmf_request); if (n_actions == 0 && mmcli_get_common_call_string ()) { /* default to info */ info_flag = TRUE; n_actions++; } if (n_actions > 1) { g_printerr ("error: too many call actions requested\n"); exit (EXIT_FAILURE); } if (info_flag) mmcli_force_sync_operation (); checked = TRUE; return !!n_actions; } static void context_free (void) { if (!ctx) return; if (ctx->cancellable) g_object_unref (ctx->cancellable); if (ctx->call) g_object_unref (ctx->call); if (ctx->object) g_object_unref (ctx->object); if (ctx->manager) g_object_unref (ctx->manager); g_free (ctx); } void mmcli_call_shutdown (void) { context_free (); } static void print_call_info (MMCall *call) { MMCallAudioFormat *audio_format; const gchar *encoding = NULL; const gchar *resolution = NULL; gchar *rate = NULL; audio_format = mm_call_peek_audio_format (call); mmcli_output_string (MMC_F_CALL_GENERAL_DBUS_PATH, mm_call_get_path (call)); mmcli_output_string (MMC_F_CALL_PROPERTIES_NUMBER, mm_call_get_number (call)); mmcli_output_string (MMC_F_CALL_PROPERTIES_DIRECTION, mm_call_direction_get_string (mm_call_get_direction (call))); mmcli_output_string (MMC_F_CALL_PROPERTIES_MULTIPARTY, mm_call_get_multiparty (call) ? "yes" : "no"); mmcli_output_string (MMC_F_CALL_PROPERTIES_STATE, mm_call_state_get_string (mm_call_get_state (call))); mmcli_output_string (MMC_F_CALL_PROPERTIES_STATE_REASON, mm_call_state_reason_get_string (mm_call_get_state_reason (call))); mmcli_output_string (MMC_F_CALL_PROPERTIES_AUDIO_PORT, mm_call_get_audio_port (call)); if (audio_format) { rate = g_strdup_printf ("%u", mm_call_audio_format_get_rate (audio_format)); encoding = mm_call_audio_format_get_encoding (audio_format); resolution = mm_call_audio_format_get_resolution (audio_format); } mmcli_output_string (MMC_F_CALL_AUDIO_FORMAT_ENCODING, encoding); mmcli_output_string (MMC_F_CALL_AUDIO_FORMAT_RESOLUTION, resolution); mmcli_output_string_take (MMC_F_CALL_AUDIO_FORMAT_RATE, rate); mmcli_output_dump (); } static void start_process_reply (gboolean result, const GError *error) { if (!result) { g_printerr ("error: couldn't start the call: '%s'\n", error ? error->message : "unknown error"); exit (EXIT_FAILURE); } g_print ("successfully started the call\n"); } static void start_ready (MMCall *call, GAsyncResult *result, gpointer nothing) { gboolean operation_result; GError *error = NULL; operation_result = mm_call_start_finish (call, result, &error); start_process_reply (operation_result, error); mmcli_async_operation_done (); } static void accept_process_reply (gboolean result, const GError *error) { if (!result) { g_printerr ("error: couldn't accept the call: '%s'\n", error ? error->message : "unknown error"); exit (EXIT_FAILURE); } g_print ("successfully accepted the call\n"); } static void accept_ready (MMCall *call, GAsyncResult *result, gpointer nothing) { gboolean operation_result; GError *error = NULL; operation_result = mm_call_accept_finish (call, result, &error); accept_process_reply (operation_result, error); mmcli_async_operation_done (); } static void deflect_process_reply (gboolean result, const GError *error) { if (!result) { g_printerr ("error: couldn't deflect the call: '%s'\n", error ? error->message : "unknown error"); exit (EXIT_FAILURE); } g_print ("successfully deflected the call\n"); } static void deflect_ready (MMCall *call, GAsyncResult *result, gpointer nothing) { gboolean operation_result; GError *error = NULL; operation_result = mm_call_deflect_finish (call, result, &error); deflect_process_reply (operation_result, error); mmcli_async_operation_done (); } static void join_multiparty_process_reply (gboolean result, const GError *error) { if (!result) { g_printerr ("error: couldn't join multiparty call: '%s'\n", error ? error->message : "unknown error"); exit (EXIT_FAILURE); } g_print ("successfully joined multiparty call\n"); } static void join_multiparty_ready (MMCall *call, GAsyncResult *result, gpointer nothing) { gboolean operation_result; GError *error = NULL; operation_result = mm_call_join_multiparty_finish (call, result, &error); join_multiparty_process_reply (operation_result, error); mmcli_async_operation_done (); } static void leave_multiparty_process_reply (gboolean result, const GError *error) { if (!result) { g_printerr ("error: couldn't leave multiparty call: '%s'\n", error ? error->message : "unknown error"); exit (EXIT_FAILURE); } g_print ("successfully left multiparty call\n"); } static void leave_multiparty_ready (MMCall *call, GAsyncResult *result, gpointer nothing) { gboolean operation_result; GError *error = NULL; operation_result = mm_call_leave_multiparty_finish (call, result, &error); leave_multiparty_process_reply (operation_result, error); mmcli_async_operation_done (); } static void hangup_process_reply (gboolean result, const GError *error) { if (!result) { g_printerr ("error: couldn't hang up the call: '%s'\n", error ? error->message : "unknown error"); exit (EXIT_FAILURE); } g_print ("successfully hung up the call\n"); } static void hangup_ready (MMCall *call, GAsyncResult *result, gpointer nothing) { gboolean operation_result; GError *error = NULL; operation_result = mm_call_hangup_finish (call, result, &error); hangup_process_reply (operation_result, error); mmcli_async_operation_done (); } static void send_dtmf_process_reply (gboolean result, const GError *error) { if (!result) { g_printerr ("error: couldn't send dtmf to call: '%s'\n", error ? error->message : "unknown error"); exit (EXIT_FAILURE); } g_print ("successfully send dtmf\n"); } static void send_dtmf_ready (MMCall *call, GAsyncResult *result, gpointer nothing) { gboolean operation_result; GError *error = NULL; operation_result = mm_call_send_dtmf_finish (call, result, &error); send_dtmf_process_reply (operation_result, error); mmcli_async_operation_done (); } static void get_call_ready (GObject *source, GAsyncResult *result, gpointer none) { ctx->call = mmcli_get_call_finish (result, &ctx->manager, &ctx->object); /* Setup operation timeout */ mmcli_force_operation_timeout (G_DBUS_PROXY (ctx->call)); if (info_flag) g_assert_not_reached (); /* Requesting to start the call? */ if (start_flag) { mm_call_start (ctx->call, ctx->cancellable, (GAsyncReadyCallback)start_ready, NULL); return; } /* Requesting to accept the call? */ if (accept_flag) { mm_call_accept (ctx->call, ctx->cancellable, (GAsyncReadyCallback)accept_ready, NULL); return; } /* Requesting to deflect the call? */ if (deflect_str) { mm_call_deflect (ctx->call, deflect_str, ctx->cancellable, (GAsyncReadyCallback)deflect_ready, NULL); return; } /* Requesting to join multiparty call? */ if (join_multiparty_flag) { mm_call_join_multiparty (ctx->call, ctx->cancellable, (GAsyncReadyCallback)join_multiparty_ready, NULL); return; } /* Requesting to leave multiparty call? */ if (leave_multiparty_flag) { mm_call_leave_multiparty (ctx->call, ctx->cancellable, (GAsyncReadyCallback)leave_multiparty_ready, NULL); return; } /* Requesting to hangup the call? */ if (hangup_flag) { mm_call_hangup (ctx->call, ctx->cancellable, (GAsyncReadyCallback)hangup_ready, NULL); return; } /* Requesting to send dtmf the call? */ if (dtmf_request) { mm_call_send_dtmf (ctx->call, dtmf_request, ctx->cancellable, (GAsyncReadyCallback)send_dtmf_ready, NULL); return; } g_warn_if_reached (); } void mmcli_call_run_asynchronous (GDBusConnection *connection, GCancellable *cancellable) { /* Initialize context */ ctx = g_new0 (Context, 1); if (cancellable) ctx->cancellable = g_object_ref (cancellable); /* Get proper call */ mmcli_get_call (connection, mmcli_get_common_call_string (), cancellable, (GAsyncReadyCallback)get_call_ready, NULL); } void mmcli_call_run_synchronous (GDBusConnection *connection) { GError *error = NULL; /* Initialize context */ ctx = g_new0 (Context, 1); ctx->call = mmcli_get_call_sync (connection, mmcli_get_common_call_string (), &ctx->manager, &ctx->object); /* Setup operation timeout: 2 minutes */ g_dbus_proxy_set_default_timeout (G_DBUS_PROXY (ctx->call), 2 * 60 * 1000); /* Request to get info from call? */ if (info_flag) { g_debug ("Printing call info..."); print_call_info (ctx->call); return; } /* Requesting to start the call? */ if (start_flag) { gboolean operation_result; operation_result = mm_call_start_sync (ctx->call, NULL, &error); start_process_reply (operation_result, error); return; } /* Requesting to accept the call? */ if (accept_flag) { gboolean operation_result; operation_result = mm_call_accept_sync (ctx->call, NULL, &error); accept_process_reply (operation_result, error); return; } /* Requesting to deflect the call? */ if (deflect_str) { gboolean operation_result; operation_result = mm_call_deflect_sync (ctx->call, deflect_str, NULL, &error); deflect_process_reply (operation_result, error); return; } /* Requesting to join multiparty call? */ if (join_multiparty_flag) { gboolean operation_result; operation_result = mm_call_join_multiparty_sync (ctx->call, NULL, &error); join_multiparty_process_reply (operation_result, error); return; } /* Requesting to leave multiparty call? */ if (leave_multiparty_flag) { gboolean operation_result; operation_result = mm_call_leave_multiparty_sync (ctx->call, NULL, &error); leave_multiparty_process_reply (operation_result, error); return; } /* Requesting to hangup the call? */ if (hangup_flag) { gboolean operation_result; operation_result = mm_call_hangup_sync (ctx->call, NULL, &error); hangup_process_reply (operation_result, error); return; } /* Requesting to send a dtmf? */ if (dtmf_request) { gboolean operation_result; operation_result = mm_call_send_dtmf_sync (ctx->call, dtmf_request, NULL, &error); send_dtmf_process_reply (operation_result, error); return; } g_warn_if_reached (); } ModemManager-1.23.4-dev/cli/mmcli-common.c000066400000000000000000001427401456466623000202370ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * mmcli -- Control modem status & access information from the command line * * 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, see . * * Copyright (C) 2011 Aleksander Morgado */ #include #include #define _LIBMM_INSIDE_MMCLI #include #include "mmcli-common.h" /******************************************************************************/ /* Manager */ MMManager * mmcli_get_manager_finish (GAsyncResult *res) { return g_task_propagate_pointer (G_TASK (res), NULL); } static void manager_new_ready (GDBusConnection *connection, GAsyncResult *res, GTask *task) { MMManager *manager; gchar *name_owner; GError *error = NULL; manager = mm_manager_new_finish (res, &error); if (!manager) { g_printerr ("error: couldn't create manager: %s\n", error ? error->message : "unknown error"); exit (EXIT_FAILURE); } name_owner = g_dbus_object_manager_client_get_name_owner (G_DBUS_OBJECT_MANAGER_CLIENT (manager)); if (!name_owner) { g_printerr ("error: couldn't find the ModemManager process in the bus\n"); exit (EXIT_FAILURE); } g_debug ("ModemManager process found at '%s'", name_owner); g_free (name_owner); g_task_return_pointer (task, manager, g_object_unref); g_object_unref (task); } void mmcli_get_manager (GDBusConnection *connection, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; task = g_task_new (connection, cancellable, callback, user_data); mm_manager_new (connection, G_DBUS_OBJECT_MANAGER_CLIENT_FLAGS_DO_NOT_AUTO_START, cancellable, (GAsyncReadyCallback)manager_new_ready, task); } MMManager * mmcli_get_manager_sync (GDBusConnection *connection) { MMManager *manager; gchar *name_owner; GError *error = NULL; manager = mm_manager_new_sync (connection, G_DBUS_OBJECT_MANAGER_CLIENT_FLAGS_DO_NOT_AUTO_START, NULL, &error); if (!manager) { g_printerr ("error: couldn't create manager: %s\n", error ? error->message : "unknown error"); exit (EXIT_FAILURE); } name_owner = g_dbus_object_manager_client_get_name_owner (G_DBUS_OBJECT_MANAGER_CLIENT (manager)); if (!name_owner) { g_printerr ("error: couldn't find the ModemManager process in the bus\n"); exit (EXIT_FAILURE); } g_debug ("ModemManager process found at '%s'", name_owner); g_free (name_owner); return manager; } /******************************************************************************/ /* Common to all objects */ #define ANY_OBJECT_STR "any" static void get_object_lookup_info (const gchar *str, const gchar *object_type, const gchar *object_prefix, gchar **object_path, gchar **modem_uid, gchar **modem_physdev, gboolean *find_any) { gboolean all_numeric; guint i; /* Empty string not allowed */ if (!str || !str[0]) { g_printerr ("error: no %s was specified\n", object_type); exit (EXIT_FAILURE); } /* User string may come in five ways: * a) full DBus path * b) object index * c) modem UID (for modem or SIM lookup only) * c) modem physdev path (for modem lookup only) * e) "any" string (for modem or SIM lookup only) */ *object_path = NULL; if (modem_uid) *modem_uid = NULL; if (modem_physdev) *modem_physdev = NULL; if (find_any) *find_any = FALSE; /* If match the DBus prefix, we have a DBus object path */ if (g_str_has_prefix (str, object_prefix)) { g_debug ("Assuming '%s' is the full %s path", str, object_type); *object_path = g_strdup (str); return; } /* If all numeric, we have the object index */ all_numeric = TRUE; for (i = 0; str[i]; i++) { if (!g_ascii_isdigit (str[i])) { all_numeric = FALSE; break; } } if (all_numeric) { g_debug ("Assuming '%s' is the %s index", str, object_type); *object_path = g_strdup_printf ("%s/%s", object_prefix, str); return; } /* If it matches the lookup keyword or any of its substrings, we have * to look for the first available object */ if ((find_any) && (g_ascii_strncasecmp (str, ANY_OBJECT_STR, strlen (str)) == 0)) { g_debug ("Will look for first available %s", object_type); *find_any = TRUE; return; } /* If starts with / we have the physdev path */ if (g_str_has_prefix (str, "/")) { g_debug ("Assuming '%s' is the %s physdev path", str, object_type); *modem_physdev = g_strdup (str); return; } /* Otherwise we have the UID */ if (modem_uid) { g_debug ("Assuming '%s' is the modem UID", str); *modem_uid = g_strdup (str); return; } /* If UID is not a valid input for the object type, error out */ g_printerr ("error: invalid %s string specified: '%s'\n", object_type, str); exit (EXIT_FAILURE); } /******************************************************************************/ /* Modem */ static MMObject * find_modem (MMManager *manager, const gchar *modem_path, const gchar *modem_uid, const gchar *modem_physdev, gboolean modem_any) { GList *modems; GList *l; MMObject *found = NULL; modems = g_dbus_object_manager_get_objects (G_DBUS_OBJECT_MANAGER (manager)); for (l = modems; l; l = g_list_next (l)) { MMObject *obj; MMModem *modem; obj = MM_OBJECT (l->data); modem = mm_object_get_modem (obj); if (!modem) continue; if (modem_any || (modem_path && g_str_equal (mm_object_get_path (obj), modem_path)) || (modem_uid && g_str_equal (mm_modem_get_device (modem), modem_uid)) || (modem_physdev && g_str_equal (mm_modem_get_physdev (modem), modem_physdev))) { found = g_object_ref (obj); break; } } g_list_free_full (modems, g_object_unref); if (!found) { g_printerr ("error: couldn't find modem\n"); exit (EXIT_FAILURE); } g_debug ("Modem found at '%s'\n", modem_path); return found; } typedef struct { gchar *modem_path; gchar *modem_uid; gchar *modem_physdev; gboolean modem_any; } GetModemContext; typedef struct { MMManager *manager; MMObject *object; } GetModemResults; static void get_modem_results_free (GetModemResults *results) { g_object_unref (results->manager); g_object_unref (results->object); g_free (results); } static void get_modem_context_free (GetModemContext *ctx) { g_free (ctx->modem_path); g_free (ctx->modem_uid); g_free (ctx->modem_physdev); g_free (ctx); } MMObject * mmcli_get_modem_finish (GAsyncResult *res, MMManager **o_manager) { GetModemResults *results; MMObject *obj; results = g_task_propagate_pointer (G_TASK (res), NULL); g_assert (results); if (o_manager) *o_manager = g_object_ref (results->manager); obj = g_object_ref (results->object); get_modem_results_free (results); return obj; } static void get_manager_ready (GDBusConnection *connection, GAsyncResult *res, GTask *task) { GetModemResults *results; GetModemContext *ctx; ctx = g_task_get_task_data (task); results = g_new (GetModemResults, 1); results->manager = mmcli_get_manager_finish (res); results->object = find_modem (results->manager, ctx->modem_path, ctx->modem_uid, ctx->modem_physdev, ctx->modem_any); g_task_return_pointer (task, results, (GDestroyNotify)get_modem_results_free); g_object_unref (task); } void mmcli_get_modem (GDBusConnection *connection, const gchar *str, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; GetModemContext *ctx; task = g_task_new (connection, cancellable, callback, user_data); ctx = g_new0 (GetModemContext, 1); get_object_lookup_info (str, "modem", MM_DBUS_MODEM_PREFIX, &ctx->modem_path, &ctx->modem_uid, &ctx->modem_physdev, &ctx->modem_any); g_assert (!!ctx->modem_path + !!ctx->modem_uid + !!ctx->modem_physdev + ctx->modem_any == 1); g_task_set_task_data (task, ctx, (GDestroyNotify) get_modem_context_free); mmcli_get_manager (connection, cancellable, (GAsyncReadyCallback)get_manager_ready, task); } MMObject * mmcli_get_modem_sync (GDBusConnection *connection, const gchar *str, MMManager **o_manager) { MMManager *manager; MMObject *found; gchar *modem_path = NULL; gchar *modem_uid = NULL; gchar *modem_physdev = NULL; gboolean modem_any = FALSE; manager = mmcli_get_manager_sync (connection); get_object_lookup_info (str, "modem", MM_DBUS_MODEM_PREFIX, &modem_path, &modem_uid, &modem_physdev, &modem_any); g_assert (!!modem_path + !!modem_uid + !!modem_physdev + modem_any == 1); found = find_modem (manager, modem_path, modem_uid, modem_physdev, modem_any); if (o_manager) *o_manager = manager; else g_object_unref (manager); g_free (modem_path); g_free (modem_uid); g_free (modem_physdev); return found; } /******************************************************************************/ /* Bearer */ typedef struct { gchar *bearer_path; MMManager *manager; GList *modems; MMObject *current; } GetBearerContext; typedef struct { MMManager *manager; MMObject *object; MMBearer *bearer; } GetBearerResults; static void get_bearer_results_free (GetBearerResults *results) { g_object_unref (results->manager); g_object_unref (results->object); g_object_unref (results->bearer); g_free (results); } static void get_bearer_context_free (GetBearerContext *ctx) { if (ctx->current) g_object_unref (ctx->current); if (ctx->manager) g_object_unref (ctx->manager); g_list_free_full (ctx->modems, g_object_unref); g_free (ctx->bearer_path); g_free (ctx); } MMBearer * mmcli_get_bearer_finish (GAsyncResult *res, MMManager **o_manager, MMObject **o_object) { GetBearerResults *results; MMBearer *obj; results = g_task_propagate_pointer (G_TASK (res), NULL); g_assert (results); if (o_manager) *o_manager = g_object_ref (results->manager); if (o_object) *o_object = g_object_ref (results->object); obj = g_object_ref (results->bearer); get_bearer_results_free (results); return obj; } static void look_for_bearer_in_modem (GTask *task); static MMBearer * find_bearer_in_list (GList *list, const gchar *bearer_path) { GList *l; for (l = list; l; l = g_list_next (l)) { MMBearer *bearer = MM_BEARER (l->data); if (g_str_equal (mm_bearer_get_path (bearer), bearer_path)) { g_debug ("Bearer found at '%s'\n", bearer_path); return g_object_ref (bearer); } } return NULL; } static void list_bearers_ready (MMModem *modem, GAsyncResult *res, GTask *task) { GetBearerContext *ctx; GetBearerResults *results; MMBearer *found; GList *bearers; GError *error = NULL; ctx = g_task_get_task_data (task); bearers = mm_modem_list_bearers_finish (modem, res, &error); if (error) { g_printerr ("error: couldn't list bearers at '%s': '%s'\n", mm_modem_get_path (modem), error->message); exit (EXIT_FAILURE); } found = find_bearer_in_list (bearers, ctx->bearer_path); g_list_free_full (bearers, g_object_unref); if (!found) { /* Not found, try with next modem */ look_for_bearer_in_modem (task); return; } /* Found! */ results = g_new (GetBearerResults, 1); results->manager = g_object_ref (ctx->manager); results->object = g_object_ref (ctx->current); results->bearer = found; g_task_return_pointer (task, results, (GDestroyNotify) get_bearer_results_free); g_object_unref (task); } static void look_for_bearer_in_modem_bearer_list (GTask *task) { GetBearerContext *ctx; MMModem *modem; ctx = g_task_get_task_data (task); g_assert (ctx->current); modem = mm_object_get_modem (ctx->current); mm_modem_list_bearers (modem, g_task_get_cancellable (task), (GAsyncReadyCallback)list_bearers_ready, task); g_object_unref (modem); } static void get_initial_eps_bearer_ready (MMModem3gpp *modem3gpp, GAsyncResult *res, GTask *task) { GetBearerContext *ctx; MMBearer *bearer; GetBearerResults *results; ctx = g_task_get_task_data (task); bearer = mm_modem_3gpp_get_initial_eps_bearer_finish (modem3gpp, res, NULL); if (!bearer) { look_for_bearer_in_modem_bearer_list (task); return; } /* Found! */ results = g_new (GetBearerResults, 1); results->manager = g_object_ref (ctx->manager); results->object = g_object_ref (ctx->current); results->bearer = bearer; g_task_return_pointer (task, results, (GDestroyNotify) get_bearer_results_free); g_object_unref (task); } static void look_for_bearer_in_modem_3gpp_eps_initial_bearer (GTask *task) { GetBearerContext *ctx; MMModem3gpp *modem3gpp; ctx = g_task_get_task_data (task); g_assert (ctx->current); modem3gpp = mm_object_get_modem_3gpp (ctx->current); if (!modem3gpp) { look_for_bearer_in_modem_bearer_list (task); return; } if (!g_strcmp0 (mm_modem_3gpp_get_initial_eps_bearer_path (modem3gpp), ctx->bearer_path)) mm_modem_3gpp_get_initial_eps_bearer (modem3gpp, g_task_get_cancellable (task), (GAsyncReadyCallback)get_initial_eps_bearer_ready, task); else look_for_bearer_in_modem_bearer_list (task); g_object_unref (modem3gpp); } static void look_for_bearer_in_modem (GTask *task) { GetBearerContext *ctx; MMModem *modem; ctx = g_task_get_task_data (task); if (!ctx->modems) { g_printerr ("error: couldn't find bearer at '%s': 'not found in any modem'\n", ctx->bearer_path); exit (EXIT_FAILURE); } /* Loop looking for the bearer in each modem found */ ctx->current = MM_OBJECT (ctx->modems->data); ctx->modems = g_list_delete_link (ctx->modems, ctx->modems); modem = mm_object_get_modem (ctx->current); /* Don't look for bearers in modems which are not fully initialized */ if (mm_modem_get_state (modem) < MM_MODEM_STATE_DISABLED) { g_debug ("Skipping modem '%s' when looking for bearers " "(not fully initialized)", mm_object_get_path (ctx->current)); look_for_bearer_in_modem (task); } else { g_debug ("Looking for bearer '%s' in modem '%s'...", ctx->bearer_path, mm_object_get_path (ctx->current)); look_for_bearer_in_modem_3gpp_eps_initial_bearer (task); } g_object_unref (modem); } static void get_bearer_manager_ready (GDBusConnection *connection, GAsyncResult *res, GTask *task) { GetBearerContext *ctx; ctx = g_task_get_task_data (task); ctx->manager = mmcli_get_manager_finish (res); ctx->modems = g_dbus_object_manager_get_objects (G_DBUS_OBJECT_MANAGER (ctx->manager)); if (!ctx->modems) { g_printerr ("error: couldn't find bearer at '%s': 'no modems found'\n", ctx->bearer_path); exit (EXIT_FAILURE); } look_for_bearer_in_modem (task); } void mmcli_get_bearer (GDBusConnection *connection, const gchar *str, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; GetBearerContext *ctx; task = g_task_new (connection, cancellable, callback, user_data); ctx = g_new0 (GetBearerContext, 1); get_object_lookup_info (str, "bearer", MM_DBUS_BEARER_PREFIX, &ctx->bearer_path, NULL, NULL, NULL); g_assert (ctx->bearer_path); g_task_set_task_data (task, ctx, (GDestroyNotify) get_bearer_context_free); mmcli_get_manager (connection, cancellable, (GAsyncReadyCallback)get_bearer_manager_ready, task); } MMBearer * mmcli_get_bearer_sync (GDBusConnection *connection, const gchar *str, MMManager **o_manager, MMObject **o_object) { MMManager *manager; GList *modems; GList *l; MMBearer *found = NULL; gchar *bearer_path = NULL; get_object_lookup_info (str, "bearer", MM_DBUS_BEARER_PREFIX, &bearer_path, NULL, NULL, NULL); g_assert (bearer_path); manager = mmcli_get_manager_sync (connection); modems = g_dbus_object_manager_get_objects (G_DBUS_OBJECT_MANAGER (manager)); if (!modems) { g_printerr ("error: couldn't find bearer at '%s': 'no modems found'\n", bearer_path); exit (EXIT_FAILURE); } for (l = modems; !found && l; l = g_list_next (l)) { GError *error = NULL; MMObject *object; MMModem *modem; MMModem3gpp *modem3gpp; object = MM_OBJECT (l->data); modem = mm_object_get_modem (object); modem3gpp = mm_object_get_modem_3gpp (object); /* Don't look for bearers in modems which are not fully initialized */ if (mm_modem_get_state (modem) < MM_MODEM_STATE_DISABLED) { g_debug ("Skipping modem '%s' when looking for bearers " "(not fully initialized)", mm_object_get_path (object)); goto next; } if (modem3gpp && !g_strcmp0 (mm_modem_3gpp_get_initial_eps_bearer_path (modem3gpp), bearer_path)) { found = mm_modem_3gpp_get_initial_eps_bearer_sync (modem3gpp, NULL, &error); if (!found) { g_printerr ("error: couldn't get initial EPS bearer object at '%s': '%s'\n", mm_modem_get_path (modem), error->message); exit (EXIT_FAILURE); } } else { GList *bearers; bearers = mm_modem_list_bearers_sync (modem, NULL, &error); if (error) { g_printerr ("error: couldn't list bearers at '%s': '%s'\n", mm_modem_get_path (modem), error->message); exit (EXIT_FAILURE); } found = find_bearer_in_list (bearers, bearer_path); g_list_free_full (bearers, g_object_unref); } if (found && o_object) *o_object = g_object_ref (object); next: g_clear_object (&modem); g_clear_object (&modem3gpp); } if (!found) { g_printerr ("error: couldn't find bearer at '%s': 'not found in any modem'\n", bearer_path); exit (EXIT_FAILURE); } g_list_free_full (modems, g_object_unref); g_free (bearer_path); if (o_manager) *o_manager = manager; else g_object_unref (manager); return found; } /******************************************************************************/ /* SIM */ typedef struct { gchar *sim_path; gchar *modem_uid; gboolean sim_any; MMManager *manager; MMObject *current; } GetSimContext; typedef struct { MMManager *manager; MMObject *object; MMSim *sim; } GetSimResults; static void get_sim_results_free (GetSimResults *results) { g_object_unref (results->manager); g_object_unref (results->object); g_object_unref (results->sim); g_free (results); } static void get_sim_context_free (GetSimContext *ctx) { if (ctx->current) g_object_unref (ctx->current); if (ctx->manager) g_object_unref (ctx->manager); g_free (ctx->modem_uid); g_free (ctx->sim_path); g_free (ctx); } MMSim * mmcli_get_sim_finish (GAsyncResult *res, MMManager **o_manager, MMObject **o_object) { GetSimResults *results; MMSim *obj; results = g_task_propagate_pointer (G_TASK (res), NULL); g_assert (results); if (o_manager) *o_manager = g_object_ref (results->manager); if (o_object) *o_object = g_object_ref (results->object); obj = g_object_ref (results->sim); get_sim_results_free (results); return obj; } static void list_sim_slots_ready (MMModem *modem, GAsyncResult *res, GTask *task) { g_autoptr(GPtrArray) sim_slots = NULL; GetSimContext *ctx; GetSimResults *results = NULL; guint i; GError *error = NULL; ctx = g_task_get_task_data (task); sim_slots = mm_modem_list_sim_slots_finish (modem, res, &error); if (error) { g_printerr ("error: couldn't list SIM slots at '%s': '%s'\n", mm_modem_get_path (modem), error->message); exit (EXIT_FAILURE); } for (i = 0; i < sim_slots->len; i++) { MMSim *sim; sim = MM_SIM (g_ptr_array_index (sim_slots, i)); if (sim && g_str_equal (mm_sim_get_path (sim), ctx->sim_path)) { /* Found! */ results = g_new (GetSimResults, 1); results->manager = g_object_ref (ctx->manager); results->object = g_object_ref (ctx->current); results->sim = g_object_ref (sim); break; } } if (results) { g_task_return_pointer (task, results, (GDestroyNotify) get_sim_results_free); g_object_unref (task); return; } g_printerr ("error: couldn't get additional SIM '%s' at '%s'\n", ctx->sim_path, mm_modem_get_path (modem)); exit (EXIT_FAILURE); } static void get_sim_ready (MMModem *modem, GAsyncResult *res, GTask *task) { GetSimContext *ctx; GetSimResults *results; MMSim *sim; GError *error = NULL; ctx = g_task_get_task_data (task); sim = mm_modem_get_sim_finish (modem, res, &error); if (error) { g_printerr ("error: couldn't get SIM '%s' at '%s': '%s'\n", ctx->sim_path, mm_modem_get_path (modem), error->message); exit (EXIT_FAILURE); } /* Found! */ results = g_new (GetSimResults, 1); results->manager = g_object_ref (ctx->manager); results->object = g_object_ref (ctx->current); results->sim = sim; g_task_return_pointer (task, results, (GDestroyNotify) get_sim_results_free); g_object_unref (task); } static void get_sim_manager_ready (GDBusConnection *connection, GAsyncResult *res, GTask *task) { GetSimContext *ctx; GList *l; GList *modems; ctx = g_task_get_task_data (task); ctx->manager = mmcli_get_manager_finish (res); modems = g_dbus_object_manager_get_objects (G_DBUS_OBJECT_MANAGER (ctx->manager)); if (!modems) { g_printerr ("error: couldn't find SIM at '%s': 'no modems found'\n", ctx->sim_path); exit (EXIT_FAILURE); } for (l = modems; l && !ctx->current; l = g_list_next (l)) { MMObject *object; MMModem *modem; const gchar *const *sim_slot_paths; object = MM_OBJECT (l->data); modem = mm_object_get_modem (object); sim_slot_paths = mm_modem_get_sim_slot_paths (modem); /* check if we can match the first object found */ if (ctx->sim_any) { g_assert (!ctx->sim_path); ctx->sim_path = g_strdup (mm_modem_get_sim_path (modem)); } /* check if modem UID matches */ else if (ctx->modem_uid) { if (g_str_equal (ctx->modem_uid, mm_modem_get_device (modem))) { g_assert (!ctx->sim_path); ctx->sim_path = g_strdup (mm_modem_get_sim_path (modem)); } else { g_object_unref (modem); continue; } } if (g_str_equal (ctx->sim_path, mm_modem_get_sim_path (modem))) { ctx->current = g_object_ref (object); mm_modem_get_sim (modem, g_task_get_cancellable (task), (GAsyncReadyCallback)get_sim_ready, task); } else if (sim_slot_paths) { guint i; for (i = 0; sim_slot_paths[i]; i++) { if (g_str_equal (ctx->sim_path, sim_slot_paths[i])) { ctx->current = g_object_ref (object); mm_modem_list_sim_slots (modem, g_task_get_cancellable (task), (GAsyncReadyCallback)list_sim_slots_ready, task); break; } } } g_object_unref (modem); } g_list_free_full (modems, g_object_unref); if (!ctx->current) { g_printerr ("error: couldn't find SIM\n"); exit (EXIT_FAILURE); } } void mmcli_get_sim (GDBusConnection *connection, const gchar *str, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; GetSimContext *ctx; task = g_task_new (connection, cancellable, callback, user_data); ctx = g_new0 (GetSimContext, 1); get_object_lookup_info (str, "SIM", MM_DBUS_SIM_PREFIX, &ctx->sim_path, &ctx->modem_uid, NULL, &ctx->sim_any); g_assert (!!ctx->sim_path + !!ctx->modem_uid + ctx->sim_any == 1); g_task_set_task_data (task, ctx, (GDestroyNotify) get_sim_context_free); mmcli_get_manager (connection, cancellable, (GAsyncReadyCallback)get_sim_manager_ready, task); } MMSim * mmcli_get_sim_sync (GDBusConnection *connection, const gchar *str, MMManager **o_manager, MMObject **o_object) { MMManager *manager; GList *modems; GList *l; MMSim *found = NULL; gchar *sim_path = NULL; gchar *modem_uid = NULL; gboolean sim_any = FALSE; get_object_lookup_info (str, "SIM", MM_DBUS_SIM_PREFIX, &sim_path, &modem_uid, NULL, &sim_any); g_assert (!!sim_path + !!modem_uid + sim_any == 1); manager = mmcli_get_manager_sync (connection); modems = g_dbus_object_manager_get_objects (G_DBUS_OBJECT_MANAGER (manager)); if (!modems) { g_printerr ("error: couldn't find SIM at '%s': 'no modems found'\n", sim_path); exit (EXIT_FAILURE); } for (l = modems; !found && l; l = g_list_next (l)) { GError *error = NULL; MMObject *object; MMModem *modem; const gchar *const *sim_slot_paths; object = MM_OBJECT (l->data); modem = mm_object_get_modem (object); sim_slot_paths = mm_modem_get_sim_slot_paths (modem); /* check if we can match the first object found */ if (sim_any) { g_assert (!sim_path); sim_path = g_strdup (mm_modem_get_sim_path (modem)); } /* check if modem UID matches */ else if (modem_uid) { if (g_str_equal (modem_uid, mm_modem_get_device (modem))) { g_assert (!sim_path); sim_path = g_strdup (mm_modem_get_sim_path (modem)); } else { g_object_unref (modem); continue; } } if (g_str_equal (sim_path, mm_modem_get_sim_path (modem))) { found = mm_modem_get_sim_sync (modem, NULL, &error); if (error) { g_printerr ("error: couldn't get SIM '%s' in modem '%s': '%s'\n", sim_path, mm_modem_get_path (modem), error->message); exit (EXIT_FAILURE); } if (found && o_object) *o_object = g_object_ref (object); } else if (sim_slot_paths) { guint i; for (i = 0; !found && sim_slot_paths[i]; i++) { if (g_str_equal (sim_path, sim_slot_paths[i])) { g_autoptr(GPtrArray) sim_slots = NULL; guint j; sim_slots = mm_modem_list_sim_slots_sync (modem, NULL, &error); if (error) { g_printerr ("error: couldn't get SIM slots in modem '%s': '%s'\n", mm_modem_get_path (modem), error->message); exit (EXIT_FAILURE); } for (j = 0; j < sim_slots->len; j++) { MMSim *sim; sim = MM_SIM (g_ptr_array_index (sim_slots, j)); if (sim && g_str_equal (sim_path, mm_sim_get_path (sim))) { found = g_object_ref (sim); if (o_object) *o_object = g_object_ref (object); } } } } } g_object_unref (modem); } if (!found) { g_printerr ("error: couldn't find SIM\n"); exit (EXIT_FAILURE); } g_list_free_full (modems, g_object_unref); g_free (sim_path); if (o_manager) *o_manager = manager; else g_object_unref (manager); return found; } /******************************************************************************/ /* SMS */ typedef struct { gchar *sms_path; MMManager *manager; GList *modems; MMObject *current; } GetSmsContext; typedef struct { MMManager *manager; MMObject *object; MMSms *sms; } GetSmsResults; static void get_sms_results_free (GetSmsResults *results) { g_object_unref (results->manager); g_object_unref (results->object); g_object_unref (results->sms); g_free (results); } static void get_sms_context_free (GetSmsContext *ctx) { if (ctx->current) g_object_unref (ctx->current); if (ctx->manager) g_object_unref (ctx->manager); g_list_free_full (ctx->modems, g_object_unref); g_free (ctx->sms_path); g_free (ctx); } MMSms * mmcli_get_sms_finish (GAsyncResult *res, MMManager **o_manager, MMObject **o_object) { GetSmsResults *results; MMSms *obj; results = g_task_propagate_pointer (G_TASK (res), NULL); g_assert (results); if (o_manager) *o_manager = g_object_ref (results->manager); if (o_object) *o_object = g_object_ref (results->object); obj = g_object_ref (results->sms); get_sms_results_free (results); return obj; } static void look_for_sms_in_modem (GTask *task); static MMSms * find_sms_in_list (GList *list, const gchar *sms_path) { GList *l; for (l = list; l; l = g_list_next (l)) { MMSms *sms = MM_SMS (l->data); if (g_str_equal (mm_sms_get_path (sms), sms_path)) { g_debug ("SMS found at '%s'\n", sms_path); return g_object_ref (sms); } } return NULL; } static void list_sms_ready (MMModemMessaging *modem, GAsyncResult *res, GTask *task) { GetSmsContext *ctx; GetSmsResults *results; MMSms *found; GList *sms_list; GError *error = NULL; ctx = g_task_get_task_data (task); sms_list = mm_modem_messaging_list_finish (modem, res, &error); if (error) { g_printerr ("error: couldn't list SMS at '%s': '%s'\n", mm_modem_messaging_get_path (modem), error->message); exit (EXIT_FAILURE); } found = find_sms_in_list (sms_list, ctx->sms_path); g_list_free_full (sms_list, g_object_unref); if (!found) { /* Not found, try with next modem */ look_for_sms_in_modem (task); return; } /* Found! */ results = g_new (GetSmsResults, 1); results->manager = g_object_ref (ctx->manager); results->object = g_object_ref (ctx->current); results->sms = found; g_task_return_pointer (task, results, (GDestroyNotify) get_sms_results_free); g_object_unref (task); } static void look_for_sms_in_modem (GTask *task) { GetSmsContext *ctx; MMModemMessaging *modem; ctx = g_task_get_task_data (task); if (!ctx->modems) { g_printerr ("error: couldn't find SMS at '%s': 'not found in any modem'\n", ctx->sms_path); exit (EXIT_FAILURE); } /* Loop looking for the sms in each modem found */ ctx->current = MM_OBJECT (ctx->modems->data); ctx->modems = g_list_delete_link (ctx->modems, ctx->modems); modem = mm_object_get_modem_messaging (ctx->current); if (!modem) { /* Current modem has no messaging capabilities, try with next modem */ look_for_sms_in_modem (task); return; } g_debug ("Looking for sms '%s' in modem '%s'...", ctx->sms_path, mm_object_get_path (ctx->current)); mm_modem_messaging_list (modem, g_task_get_cancellable (task), (GAsyncReadyCallback)list_sms_ready, task); g_object_unref (modem); } static void get_sms_manager_ready (GDBusConnection *connection, GAsyncResult *res, GTask *task) { GetSmsContext *ctx; ctx = g_task_get_task_data (task); ctx->manager = mmcli_get_manager_finish (res); ctx->modems = g_dbus_object_manager_get_objects (G_DBUS_OBJECT_MANAGER (ctx->manager)); if (!ctx->modems) { g_printerr ("error: couldn't find SMS at '%s': 'no modems found'\n", ctx->sms_path); exit (EXIT_FAILURE); } look_for_sms_in_modem (task); } void mmcli_get_sms (GDBusConnection *connection, const gchar *str, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; GetSmsContext *ctx; task = g_task_new (connection, cancellable, callback, user_data); ctx = g_new0 (GetSmsContext, 1); get_object_lookup_info (str, "SMS", MM_DBUS_SMS_PREFIX, &ctx->sms_path, NULL, NULL, NULL); g_task_set_task_data (task, ctx, (GDestroyNotify) get_sms_context_free); mmcli_get_manager (connection, cancellable, (GAsyncReadyCallback)get_sms_manager_ready, task); } MMSms * mmcli_get_sms_sync (GDBusConnection *connection, const gchar *str, MMManager **o_manager, MMObject **o_object) { MMManager *manager; GList *modems; GList *l; MMSms *found = NULL; gchar *sms_path = NULL; get_object_lookup_info (str, "SMS", MM_DBUS_SMS_PREFIX, &sms_path, NULL, NULL, NULL); manager = mmcli_get_manager_sync (connection); modems = g_dbus_object_manager_get_objects (G_DBUS_OBJECT_MANAGER (manager)); if (!modems) { g_printerr ("error: couldn't find SMS at '%s': 'no modems found'\n", sms_path); exit (EXIT_FAILURE); } for (l = modems; !found && l; l = g_list_next (l)) { GError *error = NULL; MMObject *object; MMModemMessaging *modem; GList *sms_list; object = MM_OBJECT (l->data); modem = mm_object_get_modem_messaging (object); /* If this modem doesn't implement messaging, continue to next one */ if (!modem) continue; sms_list = mm_modem_messaging_list_sync (modem, NULL, &error); if (error) { g_printerr ("error: couldn't list SMS at '%s': '%s'\n", mm_modem_messaging_get_path (modem), error->message); exit (EXIT_FAILURE); } found = find_sms_in_list (sms_list, sms_path); g_list_free_full (sms_list, g_object_unref); if (found && o_object) *o_object = g_object_ref (object); g_object_unref (modem); } if (!found) { g_printerr ("error: couldn't find SMS at '%s': 'not found in any modem'\n", sms_path); exit (EXIT_FAILURE); } g_list_free_full (modems, g_object_unref); g_free (sms_path); if (o_manager) *o_manager = manager; else g_object_unref (manager); return found; } /******************************************************************************/ /* Call */ typedef struct { gchar *call_path; MMManager *manager; GList *modems; MMObject *current; } GetCallContext; typedef struct { MMManager *manager; MMObject *object; MMCall *call; } GetCallResults; static void get_call_results_free (GetCallResults *results) { g_object_unref (results->manager); g_object_unref (results->object); g_object_unref (results->call); g_free (results); } static void get_call_context_free (GetCallContext *ctx) { if (ctx->current) g_object_unref (ctx->current); if (ctx->manager) g_object_unref (ctx->manager); g_list_free_full (ctx->modems, g_object_unref); g_free (ctx->call_path); g_free (ctx); } MMCall * mmcli_get_call_finish (GAsyncResult *res, MMManager **o_manager, MMObject **o_object) { GetCallResults *results; MMCall *obj; results = g_task_propagate_pointer (G_TASK (res), NULL); g_assert (results); if (o_manager) *o_manager = g_object_ref (results->manager); if (o_object) *o_object = g_object_ref (results->object); obj = g_object_ref (results->call); get_call_results_free (results); return obj; } static void look_for_call_in_modem (GTask *task); static MMCall * find_call_in_list (GList *list, const gchar *call_path) { GList *l; for (l = list; l; l = g_list_next (l)) { MMCall *call = MM_CALL (l->data); if (g_str_equal (mm_call_get_path (call), call_path)) { g_debug ("Call found at '%s'\n", call_path); return g_object_ref (call); } } return NULL; } static void list_calls_ready (MMModemVoice *modem, GAsyncResult *res, GTask *task) { GetCallContext *ctx; GetCallResults *results; MMCall *found; GList *call_list; GError *error = NULL; ctx = g_task_get_task_data (task); call_list = mm_modem_voice_list_calls_finish (modem, res, &error); if (error) { g_printerr ("error: couldn't list call at '%s': '%s'\n", mm_modem_voice_get_path (modem), error->message); exit (EXIT_FAILURE); } found = find_call_in_list (call_list, ctx->call_path); g_list_free_full (call_list, g_object_unref); if (!found) { /* Not found, try with next modem */ look_for_call_in_modem (task); return; } /* Found! */ results = g_new (GetCallResults, 1); results->manager = g_object_ref (ctx->manager); results->object = g_object_ref (ctx->current); results->call = found; g_task_return_pointer (task, results, (GDestroyNotify) get_call_results_free); g_object_unref (task); } static void look_for_call_in_modem (GTask *task) { GetCallContext *ctx; MMModemVoice *modem; ctx = g_task_get_task_data (task); if (!ctx->modems) { g_printerr ("error: couldn't find call at '%s': 'not found in any modem'\n", ctx->call_path); exit (EXIT_FAILURE); } /* Loop looking for the call in each modem found */ ctx->current = MM_OBJECT (ctx->modems->data); ctx->modems = g_list_delete_link (ctx->modems, ctx->modems); modem = mm_object_get_modem_voice (ctx->current); if (!modem) { /* Current modem has no messaging capabilities, try with next modem */ look_for_call_in_modem (task); return; } g_debug ("Looking for call '%s' in modem '%s'...", ctx->call_path, mm_object_get_path (ctx->current)); mm_modem_voice_list_calls (modem, g_task_get_cancellable (task), (GAsyncReadyCallback)list_calls_ready, task); g_object_unref (modem); } static void get_call_manager_ready (GDBusConnection *connection, GAsyncResult *res, GTask *task) { GetCallContext *ctx; ctx = g_task_get_task_data (task); ctx->manager = mmcli_get_manager_finish (res); ctx->modems = g_dbus_object_manager_get_objects (G_DBUS_OBJECT_MANAGER (ctx->manager)); if (!ctx->modems) { g_printerr ("error: couldn't find call at '%s': 'no modems found'\n", ctx->call_path); exit (EXIT_FAILURE); } look_for_call_in_modem (task); } void mmcli_get_call (GDBusConnection *connection, const gchar *str, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; GetCallContext *ctx; task = g_task_new (connection, cancellable, callback, user_data); ctx = g_new0 (GetCallContext, 1); get_object_lookup_info (str, "call", MM_DBUS_CALL_PREFIX, &ctx->call_path, NULL, NULL, NULL); g_task_set_task_data (task, ctx, (GDestroyNotify) get_call_context_free); mmcli_get_manager (connection, cancellable, (GAsyncReadyCallback)get_call_manager_ready, task); } MMCall * mmcli_get_call_sync (GDBusConnection *connection, const gchar *str, MMManager **o_manager, MMObject **o_object) { MMManager *manager; GList *modems; GList *l; MMCall *found = NULL; gchar *call_path = NULL; get_object_lookup_info (str, "call", MM_DBUS_CALL_PREFIX, &call_path, NULL, NULL, NULL); manager = mmcli_get_manager_sync (connection); modems = g_dbus_object_manager_get_objects (G_DBUS_OBJECT_MANAGER (manager)); if (!modems) { g_printerr ("error: couldn't find call at '%s': 'no modems found'\n", call_path); exit (EXIT_FAILURE); } for (l = modems; !found && l; l = g_list_next (l)) { GError *error = NULL; MMObject *object; MMModemVoice *voice; GList *call_list; object = MM_OBJECT (l->data); voice = mm_object_get_modem_voice (object); /* If doesn't implement voice, continue to next one */ if (!voice) continue; call_list = mm_modem_voice_list_calls_sync (voice, NULL, &error); if (error) { g_printerr ("error: couldn't list call at '%s': '%s'\n", mm_modem_voice_get_path (voice), error->message); exit (EXIT_FAILURE); } found = find_call_in_list (call_list, call_path); g_list_free_full (call_list, g_object_unref); if (found && o_object) *o_object = g_object_ref (object); g_object_unref (voice); } if (!found) { g_printerr ("error: couldn't find call at '%s': 'not found in any modem'\n", call_path); exit (EXIT_FAILURE); } g_list_free_full (modems, g_object_unref); g_free (call_path); if (o_manager) *o_manager = manager; else g_object_unref (manager); return found; } /******************************************************************************/ const gchar * mmcli_get_state_reason_string (MMModemStateChangeReason reason) { switch (reason) { case MM_MODEM_STATE_CHANGE_REASON_UNKNOWN: return "None or unknown"; case MM_MODEM_STATE_CHANGE_REASON_USER_REQUESTED: return "User request"; case MM_MODEM_STATE_CHANGE_REASON_SUSPEND: return "Suspend"; case MM_MODEM_STATE_CHANGE_REASON_FAILURE: return "Failure"; default: g_assert_not_reached (); } } /* Common options */ static gchar *modem_str; static gchar *bearer_str; static gchar *sim_str; static gchar *sms_str; static gchar *call_str; static GOptionEntry entries[] = { { "modem", 'm', 0, G_OPTION_ARG_STRING, &modem_str, "Specify modem by path, index, UID or 'any'. Shows modem information if no action specified.", "[PATH|INDEX|UID|any]" }, { "bearer", 'b', 0, G_OPTION_ARG_STRING, &bearer_str, "Specify bearer by path or index. Shows bearer information if no action specified.", "[PATH|INDEX]" }, { "sim", 'i', 0, G_OPTION_ARG_STRING, &sim_str, "Specify SIM card by path, index, UID or 'any'. Shows SIM card information if no action specified.", "[PATH|INDEX|UID|any]" }, { "sms", 's', 0, G_OPTION_ARG_STRING, &sms_str, "Specify SMS by path or index. Shows SMS information if no action specified.", "[PATH|INDEX]" }, { "call", 'o', 0, G_OPTION_ARG_STRING, &call_str, "Specify Call by path or index. Shows Call information if no action specified.", "[PATH|INDEX]" }, { NULL } }; GOptionGroup * mmcli_get_common_option_group (void) { GOptionGroup *group; /* Status options */ group = g_option_group_new ("common", "Common options", "Show common options", NULL, NULL); g_option_group_add_entries (group, entries); return group; } const gchar * mmcli_get_common_modem_string (void) { return modem_str; } const gchar * mmcli_get_common_bearer_string (void) { return bearer_str; } const gchar * mmcli_get_common_sim_string (void) { return sim_str; } const gchar * mmcli_get_common_sms_string (void) { return sms_str; } const gchar * mmcli_get_common_call_string (void) { return call_str; } gchar * mmcli_prefix_newlines (const gchar *prefix, const gchar *str) { GString *prefixed_string = NULL; const gchar *line_start = str; const gchar *line_end; do { gssize line_length; line_end = strchr (line_start, '\n'); if (line_end) line_length = line_end - line_start; else line_length = strlen (line_start); if (line_start[line_length - 1] == '\r') line_length--; if (line_length > 0) { if (prefixed_string) { /* If not the first line, add the prefix */ g_string_append_printf (prefixed_string, "\n%s", prefix); } else { prefixed_string = g_string_new (""); } g_string_append_len (prefixed_string, line_start, line_length); } line_start = (line_end ? line_end + 1 : NULL); } while (line_start != NULL); return (prefixed_string ? g_string_free (prefixed_string, FALSE) : g_strdup (str)); } ModemManager-1.23.4-dev/cli/mmcli-common.h000066400000000000000000000127031456466623000202370ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * mmcli -- Control modem status & access information from the command line * * 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, see . * * Copyright (C) 2011 Aleksander Morgado */ #ifndef _MMCLI_COMMON_H_ #define _MMCLI_COMMON_H_ #include #define _LIBMM_INSIDE_MMCLI #include void mmcli_get_manager (GDBusConnection *connection, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); MMManager *mmcli_get_manager_finish (GAsyncResult *res); MMManager *mmcli_get_manager_sync (GDBusConnection *connection); void mmcli_get_modem (GDBusConnection *connection, const gchar *str, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); MMObject *mmcli_get_modem_finish (GAsyncResult *res, MMManager **o_manager); MMObject *mmcli_get_modem_sync (GDBusConnection *connection, const gchar *str, MMManager **o_manager); void mmcli_get_bearer (GDBusConnection *connection, const gchar *str, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); MMBearer *mmcli_get_bearer_finish (GAsyncResult *res, MMManager **manager, MMObject **object); MMBearer *mmcli_get_bearer_sync (GDBusConnection *connection, const gchar *str, MMManager **manager, MMObject **object); void mmcli_get_sim (GDBusConnection *connection, const gchar *str, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); MMSim *mmcli_get_sim_finish (GAsyncResult *res, MMManager **manager, MMObject **object); MMSim *mmcli_get_sim_sync (GDBusConnection *connection, const gchar *str, MMManager **manager, MMObject **object); void mmcli_get_sms (GDBusConnection *connection, const gchar *str, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); MMSms *mmcli_get_sms_finish (GAsyncResult *res, MMManager **manager, MMObject **object); MMSms *mmcli_get_sms_sync (GDBusConnection *connection, const gchar *str, MMManager **manager, MMObject **object); void mmcli_get_call (GDBusConnection *connection, const gchar *str, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); MMCall *mmcli_get_call_finish (GAsyncResult *res, MMManager **manager, MMObject **object); MMCall *mmcli_get_call_sync (GDBusConnection *connection, const gchar *str, MMManager **manager, MMObject **object); const gchar *mmcli_get_state_reason_string (MMModemStateChangeReason reason); GOptionGroup *mmcli_get_common_option_group (void); const gchar *mmcli_get_common_modem_string (void); const gchar *mmcli_get_common_bearer_string (void); const gchar *mmcli_get_common_sim_string (void); const gchar *mmcli_get_common_sms_string (void); const gchar *mmcli_get_common_call_string (void); gchar *mmcli_prefix_newlines (const gchar *prefix, const gchar *str); #endif /* _MMCLI_COMMON_H_ */ ModemManager-1.23.4-dev/cli/mmcli-completion000066400000000000000000000130071456466623000206700ustar00rootroot00000000000000# mmcli(1) completion -*- shell-script -*- _mmcli() { local cur prev words cword split _init_completion -s || return case $prev in '-G'|'--set-logging') COMPREPLY=( $(compgen -W "[ERR,WARN,INFO,DEBUG]" -- $cur) ) return 0 ;; '-m'|'--modem') COMPREPLY=( $(compgen -W "[PATH|INDEX]" -- $cur) ) return 0 ;; '-b'|'--bearer') COMPREPLY=( $(compgen -W "[PATH|INDEX]" -- $cur) ) return 0 ;; '-i'|'--sim') COMPREPLY=( $(compgen -W "[PATH|INDEX]" -- $cur) ) return 0 ;; '-s'|'--sms') COMPREPLY=( $(compgen -W "[PATH|INDEX]" -- $cur) ) return 0 ;; '--factory-reset') COMPREPLY=( $(compgen -W "[CODE]" -- $cur) ) return 0 ;; '--command') COMPREPLY=( $(compgen -W "[COMMAND]" -- $cur) ) return 0 ;; '--create-bearer') COMPREPLY=( $(compgen -W "[key=value,...]" -- $cur) ) return 0 ;; '--delete-bearer') COMPREPLY=( $(compgen -W "[PATH|INDEX]" -- $cur) ) return 0 ;; '--set-current-capabilities') COMPREPLY=( $(compgen -W "[CAPABILITY1|CAPABILITY2...]" -- $cur) ) return 0 ;; '--set-allowed-modes') COMPREPLY=( $(compgen -W "[MODE1|MODE2...]" -- $cur) ) return 0 ;; '--set-preferred-mode') COMPREPLY=( $(compgen -W "[MODE]" -- $cur) ) return 0 ;; '--set-current-bands') COMPREPLY=( $(compgen -W "[BAND1|BAND2...]" -- $cur) ) return 0 ;; '--3gpp-register-in-operator') COMPREPLY=( $(compgen -W "[MCCMNC]" -- $cur) ) return 0 ;; '--3gpp-ussd-initiate') COMPREPLY=( $(compgen -W "[command]" -- $cur) ) return 0 ;; '--3gpp-ussd-respond') COMPREPLY=( $(compgen -W "[response]" -- $cur) ) return 0 ;; '--3gpp-disable-facility-lock') COMPREPLY=( $(compgen -W "[FACILITY,CONTROL_KEY]" -- $cur) ) return 0 ;; '--cdma-activate') COMPREPLY=( $(compgen -W "[CARRIER]" -- $cur) ) return 0 ;; '--cdma-activate-manual') COMPREPLY=( $(compgen -W "[key=value,...]" -- $cur) ) return 0 ;; '--cdma-activate-manual-with-prl-file') _filedir return 0 ;; '--simple-connect') COMPREPLY=( $(compgen -W "[key=value,...]" -- $cur) ) return 0 ;; '--location-set-supl-server') COMPREPLY=( $(compgen -W "[IP:PORT|URL]" -- $cur) ) return 0 ;; '--messaging-create-sms') COMPREPLY=( $(compgen -W "[key=value,...]" -- $cur) ) return 0 ;; '--messaging-create-sms-with-data') _filedir return 0 ;; '--messaging-create-sms-with-text') _filedir return 0 ;; '--messaging-delete-sms') COMPREPLY=( $(compgen -W "[PATH|INDEX]" -- $cur) ) return 0 ;; '--firmware-select') COMPREPLY=( $(compgen -W "[Unique-ID]" -- $cur) ) return 0 ;; '--signal-setup') COMPREPLY=( $(compgen -W "[Rate]" -- $cur) ) return 0 ;; '--oma-setup') COMPREPLY=( $(compgen -W "[FEATURE1|FEATURE2...]" -- $cur) ) return 0 ;; '--oma-start-client-initiated-session') COMPREPLY=( $(compgen -W "[Session-Type]" -- $cur) ) return 0 ;; '--oma-accept-network-initiated-session') COMPREPLY=( $(compgen -W "[Session-ID]" -- $cur) ) return 0 ;; '--oma-reject-network-initiated-session') COMPREPLY=( $(compgen -W "[Session-ID]" -- $cur) ) return 0 ;; '--pin') COMPREPLY=( $(compgen -W "[PIN]" -- $cur) ) return 0 ;; '--puk') COMPREPLY=( $(compgen -W "[PUK]" -- $cur) ) return 0 ;; '--change-pin') COMPREPLY=( $(compgen -W "[New-PIN]" -- $cur) ) return 0 ;; '--store-in-storage') COMPREPLY=( $(compgen -W "[Storage]" -- $cur) ) return 0 ;; '--create-file-with-data') _filedir return 0 ;; '--timeout') COMPREPLY=( $(compgen -W "[SECONDS]" -- $cur) ) return 0 ;; '-V'|'--version') return 0 ;; '-h'|'--help'|'--help-all'|'--help-manager'|'--help-common'|'--help-modem'|'--help-3gpp'|'--help-cdma'|'--help-simple'|'--help-location'|'--help-messaging'|'--help-time'|'--help-firmware'|'--help-signal'|'--help-oma'|'--help-sim'|'--help-bearer'|'--help-sms') return 0 ;; esac $split && return 0 if [[ $cur == -* ]]; then COMPREPLY=( $( compgen -W '$( _parse_help "$1" --help-all )' -- "$cur" ) ) [[ $COMPREPLY == *= ]] && compopt -o nospace return 0 fi } && complete -F _mmcli mmcli # ex: ts=4 sw=4 et filetype=sh ModemManager-1.23.4-dev/cli/mmcli-manager.c000066400000000000000000000437151456466623000203630ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * mmcli -- Control modem status & access information from the command line * * 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, see . * * Copyright (C) 2011 Google, Inc. * Copyright (C) 2011-2016 Aleksander Morgado */ #include "config.h" #include #include #include #include #include #include #if defined WITH_UDEV # include #endif #define _LIBMM_INSIDE_MMCLI #include "libmm-glib.h" #include "mmcli.h" #include "mmcli-common.h" #include "mmcli-output.h" /* Context */ typedef struct { MMManager *manager; GCancellable *cancellable; #if defined WITH_UDEV GUdevClient *udev; #endif } Context; static Context *ctx; /* Options */ static gboolean get_daemon_version_flag; static gboolean list_modems_flag; static gboolean monitor_modems_flag; static gboolean scan_modems_flag; static gchar *set_logging_str; static gchar *inhibit_device_str; static gchar *report_kernel_event_str; #if defined WITH_UDEV static gboolean report_kernel_event_auto_scan; #endif static GOptionEntry entries[] = { { "get-daemon-version", 'B', 0, G_OPTION_ARG_NONE, &get_daemon_version_flag, "Get ModemManager daemon version", NULL }, { "set-logging", 'G', 0, G_OPTION_ARG_STRING, &set_logging_str, "Set logging level in the ModemManager daemon", "[ERR,WARN,MSG,INFO,DEBUG]", }, { "list-modems", 'L', 0, G_OPTION_ARG_NONE, &list_modems_flag, "List available modems", NULL }, { "monitor-modems", 'M', 0, G_OPTION_ARG_NONE, &monitor_modems_flag, "List available modems and monitor additions and removals", NULL }, { "scan-modems", 'S', 0, G_OPTION_ARG_NONE, &scan_modems_flag, "Request to re-scan looking for modems", NULL }, { "inhibit-device", 'I', 0, G_OPTION_ARG_STRING, &inhibit_device_str, "Inhibit device given a unique device identifier", "[UID]" }, { "report-kernel-event", 'K', 0, G_OPTION_ARG_STRING, &report_kernel_event_str, "Report kernel event", "[\"key=value,...\"]" }, #if defined WITH_UDEV { "report-kernel-event-auto-scan", 0, 0, G_OPTION_ARG_NONE, &report_kernel_event_auto_scan, "Automatically report kernel events based on udev notifications", NULL }, #endif { NULL } }; GOptionGroup * mmcli_manager_get_option_group (void) { GOptionGroup *group; /* Status options */ group = g_option_group_new ("manager", "Manager options:", "Show manager options", NULL, NULL); g_option_group_add_entries (group, entries); return group; } gboolean mmcli_manager_options_enabled (void) { static guint n_actions = 0; static gboolean checked = FALSE; if (checked) return !!n_actions; n_actions = (get_daemon_version_flag + list_modems_flag + monitor_modems_flag + scan_modems_flag + !!set_logging_str + !!inhibit_device_str + !!report_kernel_event_str); #if defined WITH_UDEV n_actions += report_kernel_event_auto_scan; #endif if (n_actions > 1) { g_printerr ("error: too many manager actions requested\n"); exit (EXIT_FAILURE); } if (get_daemon_version_flag) mmcli_force_sync_operation (); else if (monitor_modems_flag) { if (mmcli_output_get () != MMC_OUTPUT_TYPE_HUMAN) { g_printerr ("error: modem monitoring not available in keyvalue output\n"); exit (EXIT_FAILURE); } mmcli_force_async_operation (); } else if (inhibit_device_str) mmcli_force_async_operation (); #if defined WITH_UDEV if (report_kernel_event_auto_scan) mmcli_force_async_operation (); #endif checked = TRUE; return !!n_actions; } static void context_free (void) { if (!ctx) return; #if defined WITH_UDEV if (ctx->udev) g_object_unref (ctx->udev); #endif if (ctx->manager) g_object_unref (ctx->manager); if (ctx->cancellable) g_object_unref (ctx->cancellable); g_free (ctx); } void mmcli_manager_shutdown (void) { context_free (); } static void inhibition_cancelled (GCancellable *cancellable) { GError *error = NULL; if (!mm_manager_uninhibit_device_sync (ctx->manager, inhibit_device_str, NULL, &error)) { g_printerr ("error: couldn't uninhibit device: '%s'\n", error ? error->message : "unknown error"); } else g_print ("successfully uninhibited device with uid '%s'\n", inhibit_device_str); mmcli_async_operation_done (); } static void inhibit_device_ready (MMManager *manager, GAsyncResult *result) { GError *error = NULL; if (!mm_manager_inhibit_device_finish (manager, result, &error)) { g_printerr ("error: couldn't inhibit device: '%s'\n", error ? error->message : "unknown error"); exit (EXIT_FAILURE); } g_print ("successfully inhibited device with uid '%s'\n", inhibit_device_str); g_print ("type Ctrl+C to abort this program and remove the inhibition\n"); g_cancellable_connect (ctx->cancellable, G_CALLBACK (inhibition_cancelled), NULL, NULL); } static void report_kernel_event_process_reply (gboolean result, const GError *error) { if (!result) { g_printerr ("error: couldn't report kernel event: '%s'\n", error ? error->message : "unknown error"); exit (EXIT_FAILURE); } g_print ("successfully reported kernel event\n"); } static void report_kernel_event_ready (MMManager *manager, GAsyncResult *result) { gboolean operation_result; GError *error = NULL; operation_result = mm_manager_report_kernel_event_finish (manager, result, &error); report_kernel_event_process_reply (operation_result, error); mmcli_async_operation_done (); } static MMKernelEventProperties * build_kernel_event_properties_from_input (const gchar *properties_string) { GError *error = NULL; MMKernelEventProperties *properties; properties = mm_kernel_event_properties_new_from_string (properties_string, &error); if (!properties) { g_printerr ("error: cannot parse properties string: '%s'\n", error->message); exit (EXIT_FAILURE); } return properties; } static void set_logging_process_reply (gboolean result, const GError *error) { if (!result) { g_printerr ("error: couldn't set logging level: '%s'\n", error ? error->message : "unknown error"); exit (EXIT_FAILURE); } g_print ("Successfully set logging level\n"); } static void set_logging_ready (MMManager *manager, GAsyncResult *result, gpointer nothing) { gboolean operation_result; GError *error = NULL; operation_result = mm_manager_set_logging_finish (manager, result, &error); set_logging_process_reply (operation_result, error); mmcli_async_operation_done (); } static void scan_devices_process_reply (gboolean result, const GError *error) { if (!result) { g_printerr ("error: couldn't request to scan devices: '%s'\n", error ? error->message : "unknown error"); exit (EXIT_FAILURE); } g_print ("successfully requested to scan devices\n"); } static void scan_devices_ready (MMManager *manager, GAsyncResult *result, gpointer nothing) { gboolean operation_result; GError *error = NULL; operation_result = mm_manager_scan_devices_finish (manager, result, &error); scan_devices_process_reply (operation_result, error); mmcli_async_operation_done (); } #define FOUND_ACTION_PREFIX " " #define ADDED_ACTION_PREFIX "(+) " #define REMOVED_ACTION_PREFIX "(-) " static void output_modem_info (MMObject *obj, const gchar *prefix) { gchar *extra; const gchar *manufacturer; const gchar *model; MMModem *modem; modem = mm_object_peek_modem (obj); if (!modem) return; manufacturer = mm_modem_get_manufacturer (modem); model = mm_modem_get_model (modem); extra = g_strdup_printf ("[%s] %s", manufacturer ? manufacturer : "manufacturer unknown", model ? model : "model unknown"); mmcli_output_listitem (MMC_F_MODEM_LIST_DBUS_PATH, prefix, mm_object_get_path (obj), extra); g_free (extra); } static void device_added (MMManager *manager, MMObject *modem) { output_modem_info (modem, ADDED_ACTION_PREFIX); mmcli_output_list_dump (MMC_F_MODEM_LIST_DBUS_PATH); } static void device_removed (MMManager *manager, MMObject *modem) { output_modem_info (modem, REMOVED_ACTION_PREFIX); mmcli_output_list_dump (MMC_F_MODEM_LIST_DBUS_PATH); } static void list_current_modems (MMManager *manager) { GList *modems; GList *l; modems = g_dbus_object_manager_get_objects (G_DBUS_OBJECT_MANAGER (ctx->manager)); for (l = modems; l; l = g_list_next (l)) output_modem_info ((MMObject *)(l->data), FOUND_ACTION_PREFIX); mmcli_output_list_dump (MMC_F_MODEM_LIST_DBUS_PATH); } static void cancelled (GCancellable *cancellable) { mmcli_async_operation_done (); } #if defined WITH_UDEV static void handle_uevent (GUdevClient *client, const char *action, GUdevDevice *device, gpointer none) { MMKernelEventProperties *properties; properties = mm_kernel_event_properties_new (); mm_kernel_event_properties_set_action (properties, action); mm_kernel_event_properties_set_subsystem (properties, g_udev_device_get_subsystem (device)); mm_kernel_event_properties_set_name (properties, g_udev_device_get_name (device)); mm_manager_report_kernel_event (ctx->manager, properties, NULL, NULL, NULL); g_object_unref (properties); } #endif static void get_manager_ready (GObject *source, GAsyncResult *result, gpointer none) { ctx->manager = mmcli_get_manager_finish (result); /* Setup operation timeout */ mmcli_force_operation_timeout (mm_manager_peek_proxy (ctx->manager)); /* Request to set log level? */ if (set_logging_str) { mm_manager_set_logging (ctx->manager, set_logging_str, ctx->cancellable, (GAsyncReadyCallback)set_logging_ready, NULL); return; } /* Request to scan modems? */ if (scan_modems_flag) { mm_manager_scan_devices (ctx->manager, ctx->cancellable, (GAsyncReadyCallback)scan_devices_ready, NULL); return; } /* Request to report kernel event? */ if (report_kernel_event_str) { MMKernelEventProperties *properties; properties = build_kernel_event_properties_from_input (report_kernel_event_str); mm_manager_report_kernel_event (ctx->manager, properties, ctx->cancellable, (GAsyncReadyCallback)report_kernel_event_ready, NULL); g_object_unref (properties); return; } #if defined WITH_UDEV if (report_kernel_event_auto_scan) { const gchar *subsys[] = { "tty", "usbmisc", "net", "rpmsg", "wwan", NULL }; guint i; ctx->udev = g_udev_client_new (subsys); g_signal_connect (ctx->udev, "uevent", G_CALLBACK (handle_uevent), NULL); for (i = 0; subsys[i]; i++) { GList *list, *iter; list = g_udev_client_query_by_subsystem (ctx->udev, subsys[i]); for (iter = list; iter; iter = g_list_next (iter)) { MMKernelEventProperties *properties; GUdevDevice *device; device = G_UDEV_DEVICE (iter->data); properties = mm_kernel_event_properties_new (); mm_kernel_event_properties_set_action (properties, "add"); mm_kernel_event_properties_set_subsystem (properties, subsys[i]); mm_kernel_event_properties_set_name (properties, g_udev_device_get_name (device)); mm_manager_report_kernel_event (ctx->manager, properties, NULL, NULL, NULL); g_object_unref (properties); } g_list_free_full (list, g_object_unref); } /* If we get cancelled, operation done */ g_cancellable_connect (ctx->cancellable, G_CALLBACK (cancelled), NULL, NULL); return; } #endif /* Request to monitor modems? */ if (monitor_modems_flag) { g_signal_connect (ctx->manager, "object-added", G_CALLBACK (device_added), NULL); g_signal_connect (ctx->manager, "object-removed", G_CALLBACK (device_removed), NULL); list_current_modems (ctx->manager); /* If we get cancelled, operation done */ g_cancellable_connect (ctx->cancellable, G_CALLBACK (cancelled), NULL, NULL); return; } /* Request to list modems? */ if (list_modems_flag) { list_current_modems (ctx->manager); mmcli_async_operation_done (); return; } /* Request to inhibit device? */ if (inhibit_device_str) { mm_manager_inhibit_device (ctx->manager, inhibit_device_str, ctx->cancellable, (GAsyncReadyCallback)inhibit_device_ready, NULL); return; } g_warn_if_reached (); } void mmcli_manager_run_asynchronous (GDBusConnection *connection, GCancellable *cancellable) { /* Initialize context */ ctx = g_new0 (Context, 1); if (cancellable) ctx->cancellable = g_object_ref (cancellable); /* Create a new Manager object asynchronously */ mmcli_get_manager (connection, cancellable, (GAsyncReadyCallback)get_manager_ready, NULL); } void mmcli_manager_run_synchronous (GDBusConnection *connection) { GError *error = NULL; if (monitor_modems_flag) { g_printerr ("error: monitoring modems cannot be done synchronously\n"); exit (EXIT_FAILURE); } #if defined WITH_UDEV if (report_kernel_event_auto_scan) { g_printerr ("error: monitoring udev events cannot be done synchronously\n"); exit (EXIT_FAILURE); } #endif /* Initialize context */ ctx = g_new0 (Context, 1); ctx->manager = mmcli_get_manager_sync (connection); /* Get daemon version? */ if (get_daemon_version_flag) { g_print ("ModemManager daemon %s running\n", mm_manager_get_version (ctx->manager)); return; } /* Setup operation timeout */ mmcli_force_operation_timeout (mm_manager_peek_proxy (ctx->manager)); /* Request to set log level? */ if (set_logging_str) { gboolean result; result = mm_manager_set_logging_sync (ctx->manager, set_logging_str, NULL, &error); set_logging_process_reply (result, error); return; } /* Request to scan modems? */ if (scan_modems_flag) { gboolean result; result = mm_manager_scan_devices_sync (ctx->manager, NULL, &error); scan_devices_process_reply (result, error); return; } /* Request to report kernel event? */ if (report_kernel_event_str) { MMKernelEventProperties *properties; gboolean result; properties = build_kernel_event_properties_from_input (report_kernel_event_str); result = mm_manager_report_kernel_event_sync (ctx->manager, properties, NULL, &error); report_kernel_event_process_reply (result, error); return; } /* Request to list modems? */ if (list_modems_flag) { list_current_modems (ctx->manager); return; } g_warn_if_reached (); } ModemManager-1.23.4-dev/cli/mmcli-modem-3gpp-profile-manager.c000066400000000000000000000316121456466623000237600ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * mmcli -- Control modem status & access information from the command line * * 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, see . * * Copyright (C) 2021 Aleksander Morgado * Copyright (C) 2021 Google, Inc. */ #include "config.h" #include #include #include #include #include #include #define _LIBMM_INSIDE_MMCLI #include #include "mmcli.h" #include "mmcli-common.h" #include "mmcli-output.h" /* Context */ typedef struct { MMManager *manager; GCancellable *cancellable; MMObject *object; MMModem3gppProfileManager *modem_3gpp_profile_manager; } Context; static Context *ctx; /* Options */ static gboolean status_flag; static gboolean list_flag; static gchar *set_str; static gchar *delete_str; static GOptionEntry entries[] = { { "3gpp-profile-manager-status", 0, 0, G_OPTION_ARG_NONE, &status_flag, "Show status of the profile management features.", NULL }, { "3gpp-profile-manager-list", 0, 0, G_OPTION_ARG_NONE, &list_flag, "List available profiles", NULL }, { "3gpp-profile-manager-set", 0, 0, G_OPTION_ARG_STRING, &set_str, "Create or update (if unique key given) a profile with the given settings.", "[\"key=value,...\"]" }, { "3gpp-profile-manager-delete", 0, 0, G_OPTION_ARG_STRING, &delete_str, "Delete the profile with the given unique key.", "[\"key=value,...\"]" }, { NULL } }; GOptionGroup * mmcli_modem_3gpp_profile_manager_get_option_group (void) { GOptionGroup *group; group = g_option_group_new ("3gpp-profile-manager", "3GPP profile management options:", "Show 3GPP profile management related options", NULL, NULL); g_option_group_add_entries (group, entries); return group; } gboolean mmcli_modem_3gpp_profile_manager_options_enabled (void) { static guint n_actions = 0; static gboolean checked = FALSE; if (checked) return !!n_actions; n_actions = (status_flag + list_flag + !!set_str + !!delete_str); if (n_actions > 1) { g_printerr ("error: too many 3GPP profile management actions requested\n"); exit (EXIT_FAILURE); } if (status_flag) mmcli_force_sync_operation (); checked = TRUE; return !!n_actions; } static void context_free (void) { if (!ctx) return; if (ctx->cancellable) g_object_unref (ctx->cancellable); if (ctx->modem_3gpp_profile_manager) g_object_unref (ctx->modem_3gpp_profile_manager); if (ctx->object) g_object_unref (ctx->object); if (ctx->manager) g_object_unref (ctx->manager); g_free (ctx); } static void ensure_modem_3gpp_profile_manager (void) { if (mm_modem_get_state (mm_object_peek_modem (ctx->object)) < MM_MODEM_STATE_ENABLED) { g_printerr ("error: modem not enabled yet\n"); exit (EXIT_FAILURE); } if (!ctx->modem_3gpp_profile_manager) { g_printerr ("error: modem has no 3GPP profile management capabilities\n"); exit (EXIT_FAILURE); } /* Success */ } void mmcli_modem_3gpp_profile_manager_shutdown (void) { context_free (); } static void print_status (void) { const gchar *index_field; index_field = mm_modem_3gpp_profile_manager_get_index_field (ctx->modem_3gpp_profile_manager); mmcli_output_string (MMC_F_3GPP_PROFILE_MANAGER_INDEX_FIELD, index_field); mmcli_output_dump (); } static void delete_process_reply (gboolean result, const GError *error) { if (error) { g_printerr ("error: couldn't delete profile: '%s'\n", error->message); exit (EXIT_FAILURE); } g_print ("successfully deleted the profile\n"); } static void delete_ready (MMModem3gppProfileManager *modem_3gpp_profile_manager, GAsyncResult *result) { gboolean operation_result; GError *error = NULL; operation_result = mm_modem_3gpp_profile_manager_delete_finish (modem_3gpp_profile_manager, result, &error); delete_process_reply (operation_result, error); mmcli_async_operation_done (); } static MM3gppProfile * delete_build_input (const gchar *str, GError **error) { /* Legacy command format, expecting just an integer */ if (!strchr (delete_str, '=')) { MM3gppProfile *profile; guint delete_int; if (!mm_get_uint_from_str (delete_str, &delete_int)) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS, "Failed parsing string as integer"); return NULL; } profile = mm_3gpp_profile_new (); mm_3gpp_profile_set_profile_id (profile, delete_int); return profile; } /* New command format, expecting a unique key value */ return mm_3gpp_profile_new_from_string (delete_str, error); } static void set_process_reply (MM3gppProfile *stored, const GError *error) { if (error) { g_printerr ("error: couldn't set profile: '%s'\n", error->message); exit (EXIT_FAILURE); } mmcli_output_profile_set (stored); mmcli_output_dump (); g_object_unref (stored); } static void set_ready (MMModem3gppProfileManager *modem_3gpp_profile_manager, GAsyncResult *result) { MM3gppProfile *stored; GError *error = NULL; stored = mm_modem_3gpp_profile_manager_set_finish (modem_3gpp_profile_manager, result, &error); set_process_reply (stored, error); mmcli_async_operation_done (); } static void list_process_reply (GList *result, const GError *error) { if (error) { g_printerr ("error: couldn't list profiles: '%s'\n", error->message); exit (EXIT_FAILURE); } mmcli_output_profile_list (result); mmcli_output_dump (); g_list_free_full (result, g_object_unref); } static void list_ready (MMModem3gppProfileManager *modem_3gpp_profile_manager, GAsyncResult *result) { GError *error = NULL; GList *profiles = NULL; mm_modem_3gpp_profile_manager_list_finish (modem_3gpp_profile_manager, result, &profiles, &error); list_process_reply (profiles, error); mmcli_async_operation_done (); } static void get_modem_ready (GObject *source, GAsyncResult *result, gpointer none) { ctx->object = mmcli_get_modem_finish (result, &ctx->manager); ctx->modem_3gpp_profile_manager = mm_object_get_modem_3gpp_profile_manager (ctx->object); /* Setup operation timeout */ if (ctx->modem_3gpp_profile_manager) mmcli_force_operation_timeout (G_DBUS_PROXY (ctx->modem_3gpp_profile_manager)); ensure_modem_3gpp_profile_manager (); if (status_flag) g_assert_not_reached (); /* Request to list? */ if (list_flag) { g_debug ("Asynchronously listing profiles..."); mm_modem_3gpp_profile_manager_list (ctx->modem_3gpp_profile_manager, ctx->cancellable, (GAsyncReadyCallback)list_ready, NULL); return; } /* Request to set? */ if (set_str) { GError *error = NULL; g_autoptr(MM3gppProfile) requested = NULL; g_debug ("Asynchronously setting profiles..."); requested = mm_3gpp_profile_new_from_string (set_str, &error); if (!requested) { g_printerr ("Error parsing profile string: '%s'\n", error->message); exit (EXIT_FAILURE); } mm_modem_3gpp_profile_manager_set (ctx->modem_3gpp_profile_manager, requested, ctx->cancellable, (GAsyncReadyCallback)set_ready, NULL); return; } /* Request to delete? */ if (delete_str) { g_autoptr(MM3gppProfile) profile = NULL; g_autoptr(GError) error = NULL; g_debug ("Asynchronously deleting profile..."); profile = delete_build_input (delete_str, &error); if (!profile) { g_printerr ("Error parsing profile string: '%s'\n", error->message); exit (EXIT_FAILURE); } mm_modem_3gpp_profile_manager_delete (ctx->modem_3gpp_profile_manager, profile, ctx->cancellable, (GAsyncReadyCallback)delete_ready, NULL); return; } g_warn_if_reached (); } void mmcli_modem_3gpp_profile_manager_run_asynchronous (GDBusConnection *connection, GCancellable *cancellable) { /* Initialize context */ ctx = g_new0 (Context, 1); if (cancellable) ctx->cancellable = g_object_ref (cancellable); /* Get proper modem */ mmcli_get_modem (connection, mmcli_get_common_modem_string (), cancellable, (GAsyncReadyCallback)get_modem_ready, NULL); } void mmcli_modem_3gpp_profile_manager_run_synchronous (GDBusConnection *connection) { GError *error = NULL; /* Initialize context */ ctx = g_new0 (Context, 1); ctx->object = mmcli_get_modem_sync (connection, mmcli_get_common_modem_string (), &ctx->manager); ctx->modem_3gpp_profile_manager = mm_object_get_modem_3gpp_profile_manager (ctx->object); /* Setup operation timeout */ if (ctx->modem_3gpp_profile_manager) mmcli_force_operation_timeout (G_DBUS_PROXY (ctx->modem_3gpp_profile_manager)); ensure_modem_3gpp_profile_manager (); /* Request to get location status? */ if (status_flag) { g_debug ("Printing profile management status..."); print_status (); return; } /* Request to list? */ if (list_flag) { GList *profiles; g_debug ("Synchronously listing profiles..."); mm_modem_3gpp_profile_manager_list_sync (ctx->modem_3gpp_profile_manager, ctx->cancellable, &profiles, &error); list_process_reply (profiles, error); return; } /* Request to set? */ if (set_str) { g_autoptr(MM3gppProfile) requested = NULL; MM3gppProfile *stored; g_debug ("Synchronously setting profile..."); requested = mm_3gpp_profile_new_from_string (set_str, &error); if (!requested) { g_printerr ("Error parsing profile string: '%s'\n", error->message); exit (EXIT_FAILURE); } stored = mm_modem_3gpp_profile_manager_set_sync (ctx->modem_3gpp_profile_manager, requested, ctx->cancellable, &error); set_process_reply (stored, error); return; } /* Request to delete? */ if (delete_str) { gboolean result; g_autoptr(MM3gppProfile) profile = NULL; g_debug ("Synchronously deleting profile..."); profile = delete_build_input (delete_str, &error); if (!profile) { g_printerr ("Error parsing profile string: '%s'\n", error->message); exit (EXIT_FAILURE); } result = mm_modem_3gpp_profile_manager_delete_sync (ctx->modem_3gpp_profile_manager, profile, ctx->cancellable, &error); delete_process_reply (result, error); return; } g_warn_if_reached (); } ModemManager-1.23.4-dev/cli/mmcli-modem-3gpp-ussd.c000066400000000000000000000250771456466623000216760ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * mmcli -- Control modem status & access information from the command line * * 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, see . * * Copyright (C) 2011 - 2021 Aleksander Morgado * Copyright (C) 2012 - 2021 Google, Inc. */ #include "config.h" #include #include #include #include #include #include #define _LIBMM_INSIDE_MMCLI #include #include "mmcli.h" #include "mmcli-common.h" #include "mmcli-output.h" /* Context */ typedef struct { MMManager *manager; GCancellable *cancellable; MMObject *object; MMModem3gppUssd *modem_3gpp_ussd; } Context; static Context *ctx; /* Options */ static gboolean ussd_status_flag; static gchar *ussd_initiate_str; static gchar *ussd_respond_str; static gboolean ussd_cancel_flag; static GOptionEntry entries[] = { { "3gpp-ussd-status", 0, 0, G_OPTION_ARG_NONE, &ussd_status_flag, "Show status of any ongoing USSD session", NULL }, { "3gpp-ussd-initiate", 0, 0, G_OPTION_ARG_STRING, &ussd_initiate_str, "Request a given modem to initiate a USSD session", "[command]" }, { "3gpp-ussd-respond", 0, 0, G_OPTION_ARG_STRING, &ussd_respond_str, "Request a given modem to respond to a USSD request", "[response]" }, { "3gpp-ussd-cancel", 0, 0, G_OPTION_ARG_NONE, &ussd_cancel_flag, "Request to cancel any ongoing USSD session", NULL }, { NULL } }; GOptionGroup * mmcli_modem_3gpp_ussd_get_option_group (void) { GOptionGroup *group; group = g_option_group_new ("3gpp-ussd", "3GPP USSD options:", "Show 3GPP USSD related options", NULL, NULL); g_option_group_add_entries (group, entries); return group; } gboolean mmcli_modem_3gpp_ussd_options_enabled (void) { static guint n_actions = 0; static gboolean checked = FALSE; if (checked) return !!n_actions; n_actions = (ussd_status_flag + !!ussd_initiate_str + !!ussd_respond_str + ussd_cancel_flag); if (n_actions > 1) { g_printerr ("error: too many 3GPP USSD actions requested\n"); exit (EXIT_FAILURE); } /* USSD initiate and respond will wait for URCs to get finished, so * these are truly async. */ if (ussd_initiate_str || ussd_respond_str) mmcli_force_async_operation (); if (ussd_status_flag) mmcli_force_sync_operation (); checked = TRUE; return !!n_actions; } static void context_free (void) { if (!ctx) return; if (ctx->cancellable) g_object_unref (ctx->cancellable); if (ctx->modem_3gpp_ussd) g_object_unref (ctx->modem_3gpp_ussd); if (ctx->object) g_object_unref (ctx->object); if (ctx->manager) g_object_unref (ctx->manager); g_free (ctx); } static void ensure_modem_3gpp_ussd (void) { if (mm_modem_get_state (mm_object_peek_modem (ctx->object)) < MM_MODEM_STATE_ENABLED) { g_printerr ("error: modem not enabled yet\n"); exit (EXIT_FAILURE); } if (!ctx->modem_3gpp_ussd) { g_printerr ("error: modem has no 3GPP USSD capabilities\n"); exit (EXIT_FAILURE); } /* Success */ } void mmcli_modem_3gpp_ussd_shutdown (void) { context_free (); } static void print_ussd_status (void) { mmcli_output_string (MMC_F_GENERAL_DBUS_PATH, mm_modem_3gpp_ussd_get_path (ctx->modem_3gpp_ussd)); mmcli_output_string (MMC_F_3GPP_USSD_STATUS, mm_modem_3gpp_ussd_session_state_get_string ( mm_modem_3gpp_ussd_get_state (ctx->modem_3gpp_ussd))); mmcli_output_string (MMC_F_3GPP_USSD_NETWORK_REQUEST, mm_modem_3gpp_ussd_get_network_request (ctx->modem_3gpp_ussd)); mmcli_output_string (MMC_F_3GPP_USSD_NETWORK_NOTIFICATION, mm_modem_3gpp_ussd_get_network_notification (ctx->modem_3gpp_ussd)); mmcli_output_dump (); } static void ussd_initiate_process_reply (gchar *result, const GError *error) { if (!result) { g_printerr ("error: couldn't initiate USSD session: '%s'\n", error ? error->message : "unknown error"); exit (EXIT_FAILURE); } g_print ("USSD session initiated; " "new reply from network: '%s'\n", result); g_free (result); } static void ussd_initiate_ready (MMModem3gppUssd *modem_3gpp_ussd, GAsyncResult *result) { gchar *operation_result; GError *error = NULL; operation_result = mm_modem_3gpp_ussd_initiate_finish (modem_3gpp_ussd, result, &error); ussd_initiate_process_reply (operation_result, error); mmcli_async_operation_done (); } static void ussd_respond_process_reply (gchar *result, const GError *error) { if (!result) { g_printerr ("error: couldn't send response in USSD session: '%s'\n", error ? error->message : "unknown error"); exit (EXIT_FAILURE); } g_print ("response successfully sent in USSD session; " "new reply from network: '%s'\n", result); g_free (result); } static void ussd_respond_ready (MMModem3gppUssd *modem_3gpp_ussd, GAsyncResult *result) { gchar *operation_result; GError *error = NULL; operation_result = mm_modem_3gpp_ussd_respond_finish (modem_3gpp_ussd, result, &error); ussd_respond_process_reply (operation_result, error); mmcli_async_operation_done (); } static void ussd_cancel_process_reply (gboolean result, const GError *error) { if (!result) { g_printerr ("error: couldn't cancel USSD session: '%s'\n", error ? error->message : "unknown error"); exit (EXIT_FAILURE); } g_print ("successfully cancelled USSD session\n"); } static void ussd_cancel_ready (MMModem3gppUssd *modem_3gpp_ussd, GAsyncResult *result) { gboolean operation_result; GError *error = NULL; operation_result = mm_modem_3gpp_ussd_cancel_finish (modem_3gpp_ussd, result, &error); ussd_cancel_process_reply (operation_result, error); mmcli_async_operation_done (); } static void get_modem_ready (GObject *source, GAsyncResult *result, gpointer none) { ctx->object = mmcli_get_modem_finish (result, &ctx->manager); ctx->modem_3gpp_ussd = mm_object_get_modem_3gpp_ussd (ctx->object); /* Setup operation timeout */ if (ctx->modem_3gpp_ussd) mmcli_force_operation_timeout (G_DBUS_PROXY (ctx->modem_3gpp_ussd)); ensure_modem_3gpp_ussd (); if (ussd_status_flag) g_assert_not_reached (); /* Request to initiate USSD session? */ if (ussd_initiate_str) { g_debug ("Asynchronously initiating USSD session..."); mm_modem_3gpp_ussd_initiate (ctx->modem_3gpp_ussd, ussd_initiate_str, ctx->cancellable, (GAsyncReadyCallback)ussd_initiate_ready, NULL); return; } /* Request to respond in USSD session? */ if (ussd_respond_str) { g_debug ("Asynchronously sending response in USSD session..."); mm_modem_3gpp_ussd_respond (ctx->modem_3gpp_ussd, ussd_respond_str, ctx->cancellable, (GAsyncReadyCallback)ussd_respond_ready, NULL); return; } /* Request to cancel USSD session? */ if (ussd_cancel_flag) { g_debug ("Asynchronously cancelling USSD session..."); mm_modem_3gpp_ussd_cancel (ctx->modem_3gpp_ussd, ctx->cancellable, (GAsyncReadyCallback)ussd_cancel_ready, NULL); return; } g_warn_if_reached (); } void mmcli_modem_3gpp_ussd_run_asynchronous (GDBusConnection *connection, GCancellable *cancellable) { /* Initialize context */ ctx = g_new0 (Context, 1); if (cancellable) ctx->cancellable = g_object_ref (cancellable); /* Get proper modem */ mmcli_get_modem (connection, mmcli_get_common_modem_string (), cancellable, (GAsyncReadyCallback)get_modem_ready, NULL); } void mmcli_modem_3gpp_ussd_run_synchronous (GDBusConnection *connection) { GError *error = NULL; /* Initialize context */ ctx = g_new0 (Context, 1); ctx->object = mmcli_get_modem_sync (connection, mmcli_get_common_modem_string (), &ctx->manager); ctx->modem_3gpp_ussd = mm_object_get_modem_3gpp_ussd (ctx->object); /* Setup operation timeout */ if (ctx->modem_3gpp_ussd) mmcli_force_operation_timeout (G_DBUS_PROXY (ctx->modem_3gpp_ussd)); ensure_modem_3gpp_ussd (); if (ussd_initiate_str) g_assert_not_reached (); if (ussd_respond_str) g_assert_not_reached (); /* Request to show USSD status? */ if (ussd_status_flag) { g_debug ("Printing USSD status..."); print_ussd_status (); return; } /* Request to cancel USSD session? */ if (ussd_cancel_flag) { gboolean result; g_debug ("Asynchronously cancelling USSD session..."); result = mm_modem_3gpp_ussd_cancel_sync (ctx->modem_3gpp_ussd, NULL, &error); ussd_cancel_process_reply (result, error); return; } g_warn_if_reached (); } ModemManager-1.23.4-dev/cli/mmcli-modem-3gpp.c000066400000000000000000000633611456466623000207200ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * mmcli -- Control modem status & access information from the command line * * 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, see . * * Copyright (C) 2011 - 2021 Aleksander Morgado * Copyright (C) 2012 - 2021 Google, Inc. */ #include "config.h" #include #include #include #include #include #include #define _LIBMM_INSIDE_MMCLI #include #include "mmcli.h" #include "mmcli-common.h" #include "mmcli-output.h" /* Context */ typedef struct { MMManager *manager; GCancellable *cancellable; MMObject *object; MMModem3gpp *modem_3gpp; } Context; static Context *ctx; /* Options */ static gboolean scan_flag; static gboolean register_home_flag; static gchar *register_in_operator_str; static gchar *set_eps_ue_mode_operation_str; static gchar *set_initial_eps_bearer_settings_str; static gchar *disable_facility_lock_str; static gchar *set_packet_service_state_str; static gchar *set_nr5g_registration_settings_str; static gchar *set_carrier_lock_str; static GOptionEntry entries[] = { { "3gpp-scan", 0, 0, G_OPTION_ARG_NONE, &scan_flag, "Scan for available networks in a given modem.", NULL }, { "3gpp-register-home", 0, 0, G_OPTION_ARG_NONE, ®ister_home_flag, "Request a given modem to register in its home network", NULL }, { "3gpp-register-in-operator", 0, 0, G_OPTION_ARG_STRING, ®ister_in_operator_str, "Request a given modem to register in the network of the given operator", "[MCCMNC]" }, { "3gpp-set-eps-ue-mode-operation", 0, 0, G_OPTION_ARG_STRING, &set_eps_ue_mode_operation_str, "Set the UE mode of operation for EPS", "[ps-1|ps-2|csps-1|csps-2]" }, { "3gpp-set-initial-eps-bearer-settings", 0, 0, G_OPTION_ARG_STRING, &set_initial_eps_bearer_settings_str, "Set the initial EPS bearer settings", "[\"key=value,...\"]" }, { "3gpp-disable-facility-lock", 0, 0, G_OPTION_ARG_STRING, &disable_facility_lock_str, "Disable facility personalization", "[facility,key]" }, { "3gpp-set-packet-service-state", 0, 0, G_OPTION_ARG_STRING, &set_packet_service_state_str, "Set packet service state", "[attached|detached]" }, { "3gpp-set-nr5g-registration-settings", 0, 0, G_OPTION_ARG_STRING, &set_nr5g_registration_settings_str, "Set 5GNR registration settings", "[\"key=value,...\"]" }, { "3gpp-set-carrier-lock", 0, 0, G_OPTION_ARG_STRING, &set_carrier_lock_str, "Carrier Lock", "[(Data)]" }, { NULL } }; GOptionGroup * mmcli_modem_3gpp_get_option_group (void) { GOptionGroup *group; group = g_option_group_new ("3gpp", "3GPP options:", "Show 3GPP related options", NULL, NULL); g_option_group_add_entries (group, entries); return group; } gboolean mmcli_modem_3gpp_options_enabled (void) { static guint n_actions = 0; static gboolean checked = FALSE; if (checked) return !!n_actions; n_actions = (scan_flag + register_home_flag + !!register_in_operator_str + !!set_eps_ue_mode_operation_str + !!set_initial_eps_bearer_settings_str + !!disable_facility_lock_str + !!set_packet_service_state_str + !!set_nr5g_registration_settings_str + !!set_carrier_lock_str); if (n_actions > 1) { g_printerr ("error: too many 3GPP actions requested\n"); exit (EXIT_FAILURE); } /* Scanning networks takes really a long time, so we do it asynchronously * always to avoid DBus timeouts */ if (scan_flag) mmcli_force_async_operation (); checked = TRUE; return !!n_actions; } static void context_free (void) { if (!ctx) return; if (ctx->cancellable) g_object_unref (ctx->cancellable); if (ctx->modem_3gpp) g_object_unref (ctx->modem_3gpp); if (ctx->object) g_object_unref (ctx->object); if (ctx->manager) g_object_unref (ctx->manager); g_free (ctx); } static void ensure_modem_enabled (void) { if (mm_modem_get_state (mm_object_peek_modem (ctx->object)) < MM_MODEM_STATE_ENABLED) { g_printerr ("error: modem not enabled yet\n"); exit (EXIT_FAILURE); } /* Success */ } static void ensure_modem_3gpp (void) { if (!ctx->modem_3gpp) { g_printerr ("error: modem has no 3GPP capabilities\n"); exit (EXIT_FAILURE); } /* Success */ } void mmcli_modem_3gpp_shutdown (void) { context_free (); } static void scan_process_reply (GList *result, const GError *error) { if (!result) { g_printerr ("error: couldn't scan networks in the modem: '%s'\n", error ? error->message : "unknown error"); exit (EXIT_FAILURE); } mmcli_output_scan_networks (result); mmcli_output_dump (); g_list_free_full (result, (GDestroyNotify) mm_modem_3gpp_network_free); } static void scan_ready (MMModem3gpp *modem_3gpp, GAsyncResult *result) { GList *operation_result; GError *error = NULL; operation_result = mm_modem_3gpp_scan_finish (modem_3gpp, result, &error); scan_process_reply (operation_result, error); mmcli_async_operation_done (); } static void register_process_reply (gboolean result, const GError *error) { if (!result) { g_printerr ("error: couldn't register the modem: '%s'\n", error ? error->message : "unknown error"); exit (EXIT_FAILURE); } g_print ("successfully registered the modem\n"); } static void register_ready (MMModem3gpp *modem_3gpp, GAsyncResult *result) { gboolean operation_result; GError *error = NULL; operation_result = mm_modem_3gpp_register_finish (modem_3gpp, result, &error); register_process_reply (operation_result, error); mmcli_async_operation_done (); } static void set_initial_eps_bearer_settings_process_reply (gboolean result, const GError *error) { if (!result) { g_printerr ("error: couldn't set initial EPS bearer properties: '%s'\n", error ? error->message : "unknown error"); exit (EXIT_FAILURE); } g_print ("Successfully set initial EPS bearer properties\n"); } static void set_initial_eps_bearer_settings_ready (MMModem3gpp *modem, GAsyncResult *res) { gboolean result; GError *error = NULL; result = mm_modem_3gpp_set_initial_eps_bearer_settings_finish (modem, res, &error); set_initial_eps_bearer_settings_process_reply (result, error); mmcli_async_operation_done (); } static void set_eps_ue_mode_operation_process_reply (gboolean result, const GError *error) { if (!result) { g_printerr ("error: couldn't set UE mode of operation for EPS: '%s'\n", error ? error->message : "unknown error"); exit (EXIT_FAILURE); } g_print ("successfully set UE mode of operation for EPS\n"); } static void set_eps_ue_mode_operation_ready (MMModem3gpp *modem, GAsyncResult *result) { gboolean operation_result; GError *error = NULL; operation_result = mm_modem_3gpp_set_eps_ue_mode_operation_finish (modem, result, &error); set_eps_ue_mode_operation_process_reply (operation_result, error); mmcli_async_operation_done (); } static void parse_eps_ue_mode_operation (MMModem3gppEpsUeModeOperation *uemode) { GError *error = NULL; *uemode = mm_common_get_eps_ue_mode_operation_from_string (set_eps_ue_mode_operation_str, &error); if (error) { g_printerr ("error: couldn't parse UE mode of operation for EPS: '%s'\n", error->message); exit (EXIT_FAILURE); } } static void disable_facility_lock_process_reply (gboolean result, const GError *error) { if (!result) { g_printerr ("error: couldn't disable facility lock: '%s'\n", error ? error->message : "unknown error"); exit (EXIT_FAILURE); } g_print ("successfully disabled facility lock\n"); } static gboolean disable_facility_lock_parse_input (const gchar *str, MMModem3gppFacility *out_facility, gchar **out_control_key) { g_auto(GStrv) properties = NULL; MMModem3gppFacility facility; properties = g_strsplit (str, ",", -1); if (!properties || !properties[0] || !properties[1]) return FALSE; /* Facilities is a bitmask, if 0 is returned we failed parsing */ facility = mm_common_get_3gpp_facility_from_string (properties[0], NULL); if (!facility) return FALSE; *out_facility = facility; *out_control_key = g_strdup (properties[1]); return TRUE; } static void disable_facility_lock_ready (MMModem3gpp *modem_3gpp, GAsyncResult *result, gpointer nothing) { gboolean operation_result; GError *error = NULL; operation_result = mm_modem_3gpp_disable_facility_lock_finish (modem_3gpp, result, &error); disable_facility_lock_process_reply (operation_result, error); mmcli_async_operation_done (); } static void set_packet_service_state_process_reply (gboolean result, const GError *error) { if (!result) { g_printerr ("error: couldn't set packet service state: '%s'\n", error ? error->message : "unknown error"); exit (EXIT_FAILURE); } g_print ("successfully set packet service state\n"); } static void set_packet_service_state_ready (MMModem3gpp *modem_3gpp, GAsyncResult *result, gpointer nothing) { gboolean operation_result; GError *error = NULL; operation_result = mm_modem_3gpp_set_packet_service_state_finish (modem_3gpp, result, &error); set_packet_service_state_process_reply (operation_result, error); mmcli_async_operation_done (); } static gboolean set_packet_service_state_parse_input (const gchar *str, MMModem3gppPacketServiceState *out_state) { MMModem3gppPacketServiceState state; state = mm_common_get_3gpp_packet_service_state_from_string (str, NULL); if (state == MM_MODEM_3GPP_PACKET_SERVICE_STATE_UNKNOWN) return FALSE; *out_state = state; return TRUE; } static void set_nr5g_registration_settings_process_reply (gboolean result, const GError *error) { if (!result) { g_printerr ("error: couldn't set 5GNR registration settings: '%s'\n", error ? error->message : "unknown error"); exit (EXIT_FAILURE); } g_print ("successfully set 5GNR registration settings\n"); } static void set_nr5g_registration_settings_ready (MMModem3gpp *modem_3gpp, GAsyncResult *result, gpointer nothing) { gboolean operation_result; GError *error = NULL; operation_result = mm_modem_3gpp_set_nr5g_registration_settings_finish (modem_3gpp, result, &error); set_nr5g_registration_settings_process_reply (operation_result, error); mmcli_async_operation_done (); } static void set_carrier_lock_process_reply (gboolean result, const GError *error) { if (!result) { g_printerr ("error: couldn't send carrier lock information: '%s'\n", error ? error->message : "unknown error"); exit (EXIT_FAILURE); } g_print ("successfully sent carrier lock information to modem\n"); } static void set_carrier_lock_ready (MMModem3gpp *modem_3gpp, GAsyncResult *result) { gboolean operation_result; g_autoptr(GError) error = NULL; operation_result = mm_modem_3gpp_set_carrier_lock_finish (modem_3gpp, result, &error); set_carrier_lock_process_reply (operation_result, error); mmcli_async_operation_done (); } static void get_modem_ready (GObject *source, GAsyncResult *result) { ctx->object = mmcli_get_modem_finish (result, &ctx->manager); ctx->modem_3gpp = mm_object_get_modem_3gpp (ctx->object); /* Setup operation timeout */ if (ctx->modem_3gpp) mmcli_force_operation_timeout (G_DBUS_PROXY (ctx->modem_3gpp)); ensure_modem_3gpp (); /* Request to disable facility lock */ if (disable_facility_lock_str) { g_autofree gchar *control_key = NULL; MMModem3gppFacility facility; if (!disable_facility_lock_parse_input (disable_facility_lock_str, &facility, &control_key)) { g_printerr ("Error parsing properties string.\n"); exit (EXIT_FAILURE); } g_debug ("Asynchronously disabling facility lock..."); mm_modem_3gpp_disable_facility_lock (ctx->modem_3gpp, facility, control_key, ctx->cancellable, (GAsyncReadyCallback)disable_facility_lock_ready, NULL); return; } /* Request to set carrier Lock */ if (set_carrier_lock_str) { gsize data_size = 0; g_autofree guint8 *data = NULL; GError *error = NULL; data = mm_utils_hexstr2bin (set_carrier_lock_str, -1, &data_size, &error); if (!data) { g_printerr ("Failed to read data from the input: %s\n", error->message); exit (EXIT_FAILURE); return; } mm_modem_3gpp_set_carrier_lock (ctx->modem_3gpp, data, (guint32)data_size, ctx->cancellable, (GAsyncReadyCallback)set_carrier_lock_ready, NULL); return; } ensure_modem_enabled (); /* Request to scan networks? */ if (scan_flag) { g_debug ("Asynchronously scanning for networks..."); mm_modem_3gpp_scan (ctx->modem_3gpp, ctx->cancellable, (GAsyncReadyCallback)scan_ready, NULL); return; } /* Request to register the modem? */ if (register_in_operator_str || register_home_flag) { g_debug ("Asynchronously registering the modem..."); mm_modem_3gpp_register (ctx->modem_3gpp, (register_in_operator_str ? register_in_operator_str : ""), ctx->cancellable, (GAsyncReadyCallback)register_ready, NULL); return; } /* Request to set UE mode of operation for EPS? */ if (set_eps_ue_mode_operation_str) { MMModem3gppEpsUeModeOperation uemode; parse_eps_ue_mode_operation (&uemode); g_debug ("Asynchronously setting UE mode of operation for EPS..."); mm_modem_3gpp_set_eps_ue_mode_operation (ctx->modem_3gpp, uemode, ctx->cancellable, (GAsyncReadyCallback)set_eps_ue_mode_operation_ready, NULL); return; } /* Request to set initial EPS bearer properties? */ if (set_initial_eps_bearer_settings_str) { GError *error = NULL; MMBearerProperties *config; config = mm_bearer_properties_new_from_string (set_initial_eps_bearer_settings_str, &error); if (!config) { g_printerr ("Error parsing properties string: '%s'\n", error->message); exit (EXIT_FAILURE); } g_debug ("Asynchronously setting initial EPS bearer properties..."); mm_modem_3gpp_set_initial_eps_bearer_settings (ctx->modem_3gpp, config, ctx->cancellable, (GAsyncReadyCallback)set_initial_eps_bearer_settings_ready, NULL); g_object_unref (config); return; } /* Request to set packet service state */ if (set_packet_service_state_str) { MMModem3gppPacketServiceState state; if (!set_packet_service_state_parse_input (set_packet_service_state_str, &state)) { g_printerr ("Error parsing packet service state string.\n"); exit (EXIT_FAILURE); } g_debug ("Asynchronously setting packet service state..."); mm_modem_3gpp_set_packet_service_state (ctx->modem_3gpp, state, ctx->cancellable, (GAsyncReadyCallback)set_packet_service_state_ready, NULL); return; } /* Request to set packet service state */ if (set_nr5g_registration_settings_str) { g_autoptr(MMNr5gRegistrationSettings) settings = NULL; GError *error = NULL; settings = mm_nr5g_registration_settings_new_from_string (set_nr5g_registration_settings_str, &error); if (!settings) { g_printerr ("Error parsing 5GNR registration settings string: %s\n", error->message); exit (EXIT_FAILURE); } g_debug ("Asynchronously setting 5GNR registration settings..."); mm_modem_3gpp_set_nr5g_registration_settings (ctx->modem_3gpp, settings, ctx->cancellable, (GAsyncReadyCallback)set_nr5g_registration_settings_ready, NULL); return; } g_warn_if_reached (); } void mmcli_modem_3gpp_run_asynchronous (GDBusConnection *connection, GCancellable *cancellable) { /* Initialize context */ ctx = g_new0 (Context, 1); if (cancellable) ctx->cancellable = g_object_ref (cancellable); /* Get proper modem */ mmcli_get_modem (connection, mmcli_get_common_modem_string (), cancellable, (GAsyncReadyCallback)get_modem_ready, NULL); } void mmcli_modem_3gpp_run_synchronous (GDBusConnection *connection) { GError *error = NULL; /* Initialize context */ ctx = g_new0 (Context, 1); ctx->object = mmcli_get_modem_sync (connection, mmcli_get_common_modem_string (), &ctx->manager); ctx->modem_3gpp = mm_object_get_modem_3gpp (ctx->object); /* Setup operation timeout */ if (ctx->modem_3gpp) mmcli_force_operation_timeout (G_DBUS_PROXY (ctx->modem_3gpp)); ensure_modem_3gpp (); if (scan_flag) g_assert_not_reached (); /* Request to remove carrier lock */ if (disable_facility_lock_str) { g_autofree gchar *control_key = NULL; MMModem3gppFacility facility; gboolean result; if (!disable_facility_lock_parse_input (disable_facility_lock_str, &facility, &control_key)) { g_printerr ("Error parsing properties string.\n"); exit (EXIT_FAILURE); } g_debug ("Synchronously disabling facility lock..."); result = mm_modem_3gpp_disable_facility_lock_sync (ctx->modem_3gpp, facility, control_key, NULL, &error); disable_facility_lock_process_reply (result, error); return; } /* Request to set carrier Lock */ if (set_carrier_lock_str) { gsize data_size = 0; g_autofree guint8 *data = NULL; gboolean result; data = mm_utils_hexstr2bin (set_carrier_lock_str, -1, &data_size, &error); if (!data) { g_printerr ("Failed to read data from the input: %s\n", error->message); exit (EXIT_FAILURE); return; } result = mm_modem_3gpp_set_carrier_lock_sync (ctx->modem_3gpp, data, (guint32)data_size, NULL, &error); set_carrier_lock_process_reply (result, error); return; } ensure_modem_enabled (); /* Request to register the modem? */ if (register_in_operator_str || register_home_flag) { gboolean result; g_debug ("Synchronously registering the modem..."); result = mm_modem_3gpp_register_sync ( ctx->modem_3gpp, (register_in_operator_str ? register_in_operator_str : ""), NULL, &error); register_process_reply (result, error); return; } /* Request to set UE mode of operation for EPS? */ if (set_eps_ue_mode_operation_str) { MMModem3gppEpsUeModeOperation uemode; gboolean result; parse_eps_ue_mode_operation (&uemode); g_debug ("Synchronously setting UE mode of operation for EPS..."); result = mm_modem_3gpp_set_eps_ue_mode_operation_sync (ctx->modem_3gpp, uemode, NULL, &error); set_eps_ue_mode_operation_process_reply (result, error); return; } /* Request to set initial EPS bearer properties? */ if (set_initial_eps_bearer_settings_str) { gboolean result; MMBearerProperties *config; config = mm_bearer_properties_new_from_string (set_initial_eps_bearer_settings_str, &error); if (!config) { g_printerr ("Error parsing properties string: '%s'\n", error->message); exit (EXIT_FAILURE); } g_debug ("Synchronously setting initial EPS bearer properties..."); result = mm_modem_3gpp_set_initial_eps_bearer_settings_sync (ctx->modem_3gpp, config, NULL, &error); set_initial_eps_bearer_settings_process_reply (result, error); g_object_unref (config); return; } /* Request to set packet service state */ if (set_packet_service_state_str) { gboolean result; MMModem3gppPacketServiceState state; if (!set_packet_service_state_parse_input (set_packet_service_state_str, &state)) { g_printerr ("Error parsing packet service state string.\n"); exit (EXIT_FAILURE); } g_debug ("Asynchronously setting packet service state..."); result = mm_modem_3gpp_set_packet_service_state_sync (ctx->modem_3gpp, state, NULL, &error); set_packet_service_state_process_reply (result, error); return; } if (set_nr5g_registration_settings_str) { g_autoptr(MMNr5gRegistrationSettings) settings = NULL; gboolean result; settings = mm_nr5g_registration_settings_new_from_string (set_nr5g_registration_settings_str, &error); if (!settings) { g_printerr ("Error parsing 5GNR registration settings string: %s\n", error->message); exit (EXIT_FAILURE); } g_debug ("Asynchronously setting 5GNR registration settings..."); result = mm_modem_3gpp_set_nr5g_registration_settings_sync (ctx->modem_3gpp, settings, NULL, &error); set_nr5g_registration_settings_process_reply (result, error); return; } g_warn_if_reached (); } ModemManager-1.23.4-dev/cli/mmcli-modem-cdma.c000066400000000000000000000247371456466623000207570ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * mmcli -- Control modem status & access information from the command line * * 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, see . * * Copyright (C) 2012 - Google Inc. */ #include "config.h" #include #include #include #include #include #include #define _LIBMM_INSIDE_MMCLI #include #include "mmcli.h" #include "mmcli-common.h" /* Context */ typedef struct { MMManager *manager; GCancellable *cancellable; MMObject *object; MMModemCdma *modem_cdma; } Context; static Context *ctx; /* Options */ static gchar *activate_str; static gchar *activate_manual_str; static gchar *activate_manual_with_prl_str; static GOptionEntry entries[] = { { "cdma-activate", 0, 0, G_OPTION_ARG_STRING, &activate_str, "Provision the modem to use with a given carrier using OTA settings.", "[CARRIER]" }, { "cdma-activate-manual", 0, 0, G_OPTION_ARG_STRING, &activate_manual_str, "Provision the modem with the given settings. 'spc', 'sid', 'mdn' and 'min' are mandatory, 'mn-ha-key' and 'mn-aaa-key' are optional.", "[\"key=value,...\"]" }, { "cdma-activate-manual-with-prl", 0, 0, G_OPTION_ARG_STRING, &activate_manual_with_prl_str, "Use the given file contents as data for the PRL.", "[File path]" }, { NULL } }; GOptionGroup * mmcli_modem_cdma_get_option_group (void) { GOptionGroup *group; group = g_option_group_new ("cdma", "CDMA options:", "Show CDMA related options", NULL, NULL); g_option_group_add_entries (group, entries); return group; } gboolean mmcli_modem_cdma_options_enabled (void) { static guint n_actions = 0; static gboolean checked = FALSE; if (checked) return !!n_actions; n_actions = (!!activate_str + !!activate_manual_str); if (n_actions > 1) { g_printerr ("error: too many CDMA actions requested\n"); exit (EXIT_FAILURE); } if (activate_manual_with_prl_str && !activate_manual_str) { g_printerr ("error: `--cdma-activate-manual-with-prl' must be given along " "with `--cdma-activate-manual'\n"); exit (EXIT_FAILURE); } checked = TRUE; return !!n_actions; } static void context_free (void) { if (!ctx) return; if (ctx->cancellable) g_object_unref (ctx->cancellable); if (ctx->modem_cdma) g_object_unref (ctx->modem_cdma); if (ctx->object) g_object_unref (ctx->object); if (ctx->manager) g_object_unref (ctx->manager); g_free (ctx); } static void ensure_modem_cdma (void) { if (mm_modem_get_state (mm_object_peek_modem (ctx->object)) < MM_MODEM_STATE_ENABLED) { g_printerr ("error: modem not enabled yet\n"); exit (EXIT_FAILURE); } if (!ctx->modem_cdma) { g_printerr ("error: modem has no CDMA capabilities\n"); exit (EXIT_FAILURE); } /* Success */ } void mmcli_modem_cdma_shutdown (void) { context_free (); } static void activate_process_reply (gboolean result, const GError *error) { if (!result) { g_printerr ("error: couldn't activate the modem: '%s'\n", error ? error->message : "unknown error"); exit (EXIT_FAILURE); } g_print ("successfully activated the modem\n"); } static void activate_ready (MMModemCdma *modem_cdma, GAsyncResult *result, gpointer nothing) { gboolean operation_result; GError *error = NULL; operation_result = mm_modem_cdma_activate_finish (modem_cdma, result, &error); activate_process_reply (operation_result, error); mmcli_async_operation_done (); } static void activate_manual_process_reply (gboolean result, const GError *error) { if (!result) { g_printerr ("error: couldn't manually activate the modem: '%s'\n", error ? error->message : "unknown error"); exit (EXIT_FAILURE); } g_print ("successfully activated the modem manually\n"); } static void activate_manual_ready (MMModemCdma *modem_cdma, GAsyncResult *result, gpointer nothing) { gboolean operation_result; GError *error = NULL; operation_result = mm_modem_cdma_activate_manual_finish (modem_cdma, result, &error); activate_manual_process_reply (operation_result, error); mmcli_async_operation_done (); } static MMCdmaManualActivationProperties * build_activate_manual_properties_from_input (const gchar *properties_string, const gchar *prl_file) { GError *error = NULL; MMCdmaManualActivationProperties *properties; properties = mm_cdma_manual_activation_properties_new_from_string (properties_string, &error); if (!properties) { g_printerr ("error: cannot parse properties string: '%s'\n", error->message); exit (EXIT_FAILURE); } if (prl_file) { gchar *path; GFile *file; gchar *contents; gsize contents_size; g_debug ("Reading data from file '%s'", prl_file); file = g_file_new_for_commandline_arg (prl_file); path = g_file_get_path (file); if (!g_file_get_contents (path, &contents, &contents_size, &error)) { g_printerr ("error: cannot read from file '%s': '%s'\n", prl_file, error->message); exit (EXIT_FAILURE); } g_free (path); g_object_unref (file); if (!mm_cdma_manual_activation_properties_set_prl (properties, (guint8 *)contents, contents_size, &error)) { g_printerr ("error: cannot set PRL: '%s'\n", error->message); exit (EXIT_FAILURE); } g_free (contents); } return properties; } static void get_modem_ready (GObject *source, GAsyncResult *result, gpointer none) { ctx->object = mmcli_get_modem_finish (result, &ctx->manager); ctx->modem_cdma = mm_object_get_modem_cdma (ctx->object); /* Setup operation timeout */ if (ctx->modem_cdma) mmcli_force_operation_timeout (G_DBUS_PROXY (ctx->modem_cdma)); ensure_modem_cdma (); /* Request to activate the modem? */ if (activate_str) { g_debug ("Asynchronously activating the modem..."); mm_modem_cdma_activate (ctx->modem_cdma, activate_str, ctx->cancellable, (GAsyncReadyCallback)activate_ready, NULL); return; } /* Request to manually activate the modem? */ if (activate_manual_str) { MMCdmaManualActivationProperties *properties; properties = build_activate_manual_properties_from_input (activate_manual_str, activate_manual_with_prl_str); g_debug ("Asynchronously manually activating the modem..."); mm_modem_cdma_activate_manual (ctx->modem_cdma, properties, ctx->cancellable, (GAsyncReadyCallback)activate_manual_ready, NULL); g_object_unref (properties); return; } g_warn_if_reached (); } void mmcli_modem_cdma_run_asynchronous (GDBusConnection *connection, GCancellable *cancellable) { /* Initialize context */ ctx = g_new0 (Context, 1); if (cancellable) ctx->cancellable = g_object_ref (cancellable); /* Get proper modem */ mmcli_get_modem (connection, mmcli_get_common_modem_string (), cancellable, (GAsyncReadyCallback)get_modem_ready, NULL); } void mmcli_modem_cdma_run_synchronous (GDBusConnection *connection) { GError *error = NULL; /* Initialize context */ ctx = g_new0 (Context, 1); ctx->object = mmcli_get_modem_sync (connection, mmcli_get_common_modem_string (), &ctx->manager); ctx->modem_cdma = mm_object_get_modem_cdma (ctx->object); /* Setup operation timeout */ if (ctx->modem_cdma) mmcli_force_operation_timeout (G_DBUS_PROXY (ctx->modem_cdma)); ensure_modem_cdma (); /* Request to activate the modem? */ if (activate_str) { gboolean result; g_debug ("Synchronously activating the modem..."); result = mm_modem_cdma_activate_sync ( ctx->modem_cdma, activate_str, NULL, &error); activate_process_reply (result, error); return; } /* Request to manually activate the modem? */ if (activate_manual_str) { MMCdmaManualActivationProperties *properties; gboolean result; properties = build_activate_manual_properties_from_input (activate_manual_str, activate_manual_with_prl_str); g_debug ("Synchronously manually activating the modem..."); result = mm_modem_cdma_activate_manual_sync ( ctx->modem_cdma, properties, NULL, &error); activate_manual_process_reply (result, error); g_object_unref (properties); return; } g_warn_if_reached (); } ModemManager-1.23.4-dev/cli/mmcli-modem-firmware.c000066400000000000000000000242621456466623000216600ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * mmcli -- Control modem status & access information from the command line * * 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, see . * * Copyright (C) 2012 Google, Inc. */ #include "config.h" #include #include #include #include #include #include #define _LIBMM_INSIDE_MMCLI #include #include "mmcli.h" #include "mmcli-common.h" #include "mmcli-output.h" /* Context */ typedef struct { MMManager *manager; GCancellable *cancellable; MMObject *object; MMModemFirmware *modem_firmware; } Context; static Context *ctx; /* Options */ static gboolean status_flag; static gboolean list_flag; static gchar *select_str; static GOptionEntry entries[] = { { "firmware-status", 0, 0, G_OPTION_ARG_NONE, &status_flag, "Show status of firmware management.", NULL }, { "firmware-list", 0, 0, G_OPTION_ARG_NONE, &list_flag, "List firmware images installed in a given modem", NULL }, { "firmware-select", 0, 0, G_OPTION_ARG_STRING, &select_str, "Select a given firmware image", "[Unique ID]" }, { NULL } }; GOptionGroup * mmcli_modem_firmware_get_option_group (void) { GOptionGroup *group; group = g_option_group_new ("firmware", "Firmware options:", "Show Firmware options", NULL, NULL); g_option_group_add_entries (group, entries); return group; } gboolean mmcli_modem_firmware_options_enabled (void) { static guint n_actions = 0; static gboolean checked = FALSE; if (checked) return !!n_actions; n_actions = (status_flag + list_flag + !!select_str); if (n_actions > 1) { g_printerr ("error: too many Firmware actions requested\n"); exit (EXIT_FAILURE); } if (status_flag) mmcli_force_sync_operation (); checked = TRUE; return !!n_actions; } static void context_free (void) { if (!ctx) return; if (ctx->cancellable) g_object_unref (ctx->cancellable); if (ctx->modem_firmware) g_object_unref (ctx->modem_firmware); if (ctx->object) g_object_unref (ctx->object); if (ctx->manager) g_object_unref (ctx->manager); g_free (ctx); } static void ensure_modem_firmware (void) { if (!ctx->modem_firmware) { g_printerr ("error: modem has no firmware capabilities\n"); exit (EXIT_FAILURE); } /* Success */ } void mmcli_modem_firmware_shutdown (void) { context_free (); } static void print_firmware_status (void) { MMFirmwareUpdateSettings *update_settings; gchar *method = NULL; const gchar **device_ids = NULL; const gchar *version = NULL; const gchar *fastboot_at = NULL; update_settings = mm_modem_firmware_peek_update_settings (ctx->modem_firmware); if (update_settings) { MMModemFirmwareUpdateMethod m; m = mm_firmware_update_settings_get_method (update_settings); if (m != MM_MODEM_FIRMWARE_UPDATE_METHOD_NONE) { method = mm_modem_firmware_update_method_build_string_from_mask (m); device_ids = mm_firmware_update_settings_get_device_ids (update_settings); version = mm_firmware_update_settings_get_version (update_settings); } if (m & MM_MODEM_FIRMWARE_UPDATE_METHOD_FASTBOOT) fastboot_at = mm_firmware_update_settings_get_fastboot_at (update_settings); } /* There's not much to print in this status info, and if the modem * does not support any firmware update method, we would just be returning * an empty response to the --firmware-status action. So, instead, just * return an error message explicitly when in human output type. * We can remove this error message as soon as there is some parameter * that will always be printed. */ if (!method && !fastboot_at && mmcli_output_get () == MMC_OUTPUT_TYPE_HUMAN) { g_printerr ("error: firmware status unsupported\n"); exit (EXIT_FAILURE); } mmcli_output_string_list_take (MMC_F_FIRMWARE_METHOD, method); mmcli_output_string_array (MMC_F_FIRMWARE_DEVICE_IDS, device_ids, TRUE); mmcli_output_string (MMC_F_FIRMWARE_VERSION, version); mmcli_output_string (MMC_F_FIRMWARE_FASTBOOT_AT, fastboot_at); mmcli_output_dump (); } static void list_process_reply (MMFirmwareProperties *selected, GList *result, const GError *error) { #undef VALIDATE_UNKNOWN #define VALIDATE_UNKNOWN(str) (str ? str : "unknown") if (error) { g_printerr ("error: couldn't list firmware images: '%s'\n", error->message); exit (EXIT_FAILURE); } mmcli_output_firmware_list (result, selected); mmcli_output_dump (); g_list_free_full (result, g_object_unref); g_clear_object (&selected); } static void list_ready (MMModemFirmware *modem, GAsyncResult *result, gpointer nothing) { GList *installed = NULL; MMFirmwareProperties *selected = NULL; GError *error = NULL; mm_modem_firmware_list_finish (modem, result, &selected, &installed, &error); list_process_reply (selected, installed, error); mmcli_async_operation_done (); } static void select_process_reply (gboolean result, const GError *error) { if (!result) { g_printerr ("error: couldn't select firmware image: '%s'\n", error ? error->message : "unknown error"); exit (EXIT_FAILURE); } g_print ("Successfully selected firmware image\n"); } static void create_ready (MMModemFirmware *modem, GAsyncResult *result) { gboolean res; GError *error = NULL; res = mm_modem_firmware_select_finish (modem, result, &error); select_process_reply (res, error); mmcli_async_operation_done (); } static void get_modem_ready (GObject *source, GAsyncResult *result) { ctx->object = mmcli_get_modem_finish (result, &ctx->manager); ctx->modem_firmware = mm_object_get_modem_firmware (ctx->object); /* Setup operation timeout */ if (ctx->modem_firmware) mmcli_force_operation_timeout (G_DBUS_PROXY (ctx->modem_firmware)); ensure_modem_firmware (); if (status_flag) g_assert_not_reached (); /* Request to list images? */ if (list_flag) { g_debug ("Asynchronously listing firmware images in modem..."); mm_modem_firmware_list (ctx->modem_firmware, ctx->cancellable, (GAsyncReadyCallback)list_ready, NULL); return; } /* Request to select a given image? */ if (select_str) { g_debug ("Asynchronously selecting firmware image in modem..."); mm_modem_firmware_select (ctx->modem_firmware, select_str, ctx->cancellable, (GAsyncReadyCallback)create_ready, NULL); return; } g_warn_if_reached (); } void mmcli_modem_firmware_run_asynchronous (GDBusConnection *connection, GCancellable *cancellable) { /* Initialize context */ ctx = g_new0 (Context, 1); if (cancellable) ctx->cancellable = g_object_ref (cancellable); /* Get proper modem */ mmcli_get_modem (connection, mmcli_get_common_modem_string (), cancellable, (GAsyncReadyCallback)get_modem_ready, NULL); } void mmcli_modem_firmware_run_synchronous (GDBusConnection *connection) { GError *error = NULL; /* Initialize context */ ctx = g_new0 (Context, 1); ctx->object = mmcli_get_modem_sync (connection, mmcli_get_common_modem_string (), &ctx->manager); ctx->modem_firmware = mm_object_get_modem_firmware (ctx->object); /* Setup operation timeout */ if (ctx->modem_firmware) mmcli_force_operation_timeout (G_DBUS_PROXY (ctx->modem_firmware)); ensure_modem_firmware (); /* Request to get firmware status? */ if (status_flag) { g_debug ("Printing firmware status..."); print_firmware_status (); return; } /* Request to list firmware images? */ if (list_flag) { GList *installed = NULL; MMFirmwareProperties *selected = NULL; g_debug ("Synchronously listing firmware images in modem..."); mm_modem_firmware_list_sync (ctx->modem_firmware, &selected, &installed, NULL, &error); list_process_reply (selected, installed, error); return; } /* Request to select a given image? */ if (select_str) { gboolean result; g_debug ("Synchronously selecting firmware image in modem..."); result = mm_modem_firmware_select_sync (ctx->modem_firmware, select_str, NULL, &error); select_process_reply (result, error); return; } g_warn_if_reached (); } ModemManager-1.23.4-dev/cli/mmcli-modem-location.c000066400000000000000000000742411456466623000216560ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * mmcli -- Control modem status & access information from the command line * * 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, see . * * Copyright (C) 2012 Google, Inc. * Copyright (C) 2012 Lanedo GmbH * Copyright (C) 2012-2019 Aleksander Morgado */ #include "config.h" #include #include #include #include #include #include #define _LIBMM_INSIDE_MMCLI #include #include "mmcli.h" #include "mmcli-common.h" #include "mmcli-output.h" /* Context */ typedef struct { MMManager *manager; GCancellable *cancellable; MMObject *object; MMModemLocation *modem_location; } Context; static Context *ctx; /* Options */ static gboolean status_flag; static gboolean enable_3gpp_flag; static gboolean disable_3gpp_flag; static gboolean enable_agps_msa_flag; static gboolean disable_agps_msa_flag; static gboolean enable_agps_msb_flag; static gboolean disable_agps_msb_flag; static gboolean enable_gps_nmea_flag; static gboolean disable_gps_nmea_flag; static gboolean enable_gps_raw_flag; static gboolean disable_gps_raw_flag; static gboolean enable_cdma_bs_flag; static gboolean disable_cdma_bs_flag; static gboolean enable_gps_unmanaged_flag; static gboolean disable_gps_unmanaged_flag; static gboolean set_enable_signal_flag; static gboolean set_disable_signal_flag; static gboolean get_flag; static gboolean monitor_flag; static gchar *set_supl_server_str; static gchar *inject_assistance_data_str; static gchar *set_gps_refresh_rate_str; static GOptionEntry entries[] = { { "location-status", 0, 0, G_OPTION_ARG_NONE, &status_flag, "Show status of location gathering.", NULL }, { "location-get", 0, 0, G_OPTION_ARG_NONE, &get_flag, "Get all available location information.", NULL }, { "location-monitor", 0, 0, G_OPTION_ARG_NONE, &monitor_flag, "Monitor all available location information.", NULL }, { "location-enable-3gpp", 0, 0, G_OPTION_ARG_NONE, &enable_3gpp_flag, "Enable 3GPP location gathering.", NULL }, { "location-disable-3gpp", 0, 0, G_OPTION_ARG_NONE, &disable_3gpp_flag, "Disable 3GPP location gathering.", NULL }, { "location-enable-agps-msa", 0, 0, G_OPTION_ARG_NONE, &enable_agps_msa_flag, "Enable MSA A-GPS location gathering.", NULL }, { "location-disable-agps-msa", 0, 0, G_OPTION_ARG_NONE, &disable_agps_msa_flag, "Disable MSA A-GPS location gathering.", NULL }, { "location-enable-agps-msb", 0, 0, G_OPTION_ARG_NONE, &enable_agps_msb_flag, "Enable MSB A-GPS location gathering.", NULL }, { "location-disable-agps-msb", 0, 0, G_OPTION_ARG_NONE, &disable_agps_msb_flag, "Disable MSB A-GPS location gathering.", NULL }, { "location-enable-gps-nmea", 0, 0, G_OPTION_ARG_NONE, &enable_gps_nmea_flag, "Enable NMEA-based GPS location gathering.", NULL }, { "location-disable-gps-nmea", 0, 0, G_OPTION_ARG_NONE, &disable_gps_nmea_flag, "Disable NMEA-based GPS location gathering.", NULL }, { "location-enable-gps-raw", 0, 0, G_OPTION_ARG_NONE, &enable_gps_raw_flag, "Enable raw GPS location gathering.", NULL }, { "location-disable-gps-raw", 0, 0, G_OPTION_ARG_NONE, &disable_gps_raw_flag, "Disable raw GPS location gathering.", NULL }, { "location-enable-cdma-bs", 0, 0, G_OPTION_ARG_NONE, &enable_cdma_bs_flag, "Enable CDMA base station location gathering.", NULL }, { "location-disable-cdma-bs", 0, 0, G_OPTION_ARG_NONE, &disable_cdma_bs_flag, "Disable CDMA base station location gathering.", NULL }, { "location-enable-gps-unmanaged", 0, 0, G_OPTION_ARG_NONE, &enable_gps_unmanaged_flag, "Enable unmanaged GPS location gathering.", NULL }, { "location-disable-gps-unmanaged", 0, 0, G_OPTION_ARG_NONE, &disable_gps_unmanaged_flag, "Disable unmanaged GPS location gathering.", NULL }, { "location-set-supl-server", 0, 0, G_OPTION_ARG_STRING, &set_supl_server_str, "Set SUPL server address", "[IP:PORT] or [FQDN:PORT]" }, { "location-inject-assistance-data", 0, 0, G_OPTION_ARG_FILENAME, &inject_assistance_data_str, "Inject assistance data in the GNSS module", "[PATH]" }, { "location-set-gps-refresh-rate", 0, 0, G_OPTION_ARG_STRING, &set_gps_refresh_rate_str, "Set GPS refresh rate in seconds, or 0 disable the explicit rate.", "[RATE]" }, { "location-set-enable-signal", 0, 0, G_OPTION_ARG_NONE, &set_enable_signal_flag, "Enable location update signaling in DBus property.", NULL }, { "location-set-disable-signal", 0, 0, G_OPTION_ARG_NONE, &set_disable_signal_flag, "Disable location update signaling in DBus property.", NULL }, { NULL } }; GOptionGroup * mmcli_modem_location_get_option_group (void) { GOptionGroup *group; group = g_option_group_new ("location", "Location options:", "Show Location options", NULL, NULL); g_option_group_add_entries (group, entries); return group; } #define any_location_setup_flag ( \ enable_3gpp_flag || disable_3gpp_flag || \ enable_agps_msa_flag || disable_agps_msa_flag || \ enable_agps_msb_flag || disable_agps_msb_flag || \ enable_gps_nmea_flag || disable_gps_nmea_flag || \ enable_gps_raw_flag || disable_gps_raw_flag || \ enable_cdma_bs_flag || disable_cdma_bs_flag || \ enable_gps_unmanaged_flag || disable_gps_unmanaged_flag || \ set_enable_signal_flag || set_disable_signal_flag) gboolean mmcli_modem_location_options_enabled (void) { static guint n_actions = 0; static gboolean checked = FALSE; if (checked) return !!n_actions; if ((enable_3gpp_flag && disable_3gpp_flag) || (enable_agps_msa_flag && disable_agps_msa_flag) || (enable_agps_msb_flag && disable_agps_msb_flag) || (enable_gps_nmea_flag && disable_gps_nmea_flag) || (enable_gps_raw_flag && disable_gps_raw_flag) || (enable_gps_unmanaged_flag && disable_gps_unmanaged_flag) || (enable_cdma_bs_flag && disable_cdma_bs_flag)) { g_printerr ("error: cannot enable and disable the same source\n"); exit (EXIT_FAILURE); } if (set_enable_signal_flag && set_disable_signal_flag) { g_printerr ("error: cannot enable and disable location signaling\n"); exit (EXIT_FAILURE); } n_actions = (status_flag + any_location_setup_flag + get_flag + monitor_flag + !!set_supl_server_str + !!inject_assistance_data_str + !!set_gps_refresh_rate_str); if (n_actions > 1) { g_printerr ("error: too many Location actions requested\n"); exit (EXIT_FAILURE); } if (monitor_flag) mmcli_force_async_operation (); else if (status_flag) mmcli_force_sync_operation (); checked = TRUE; return !!n_actions; } static void context_free (void) { if (!ctx) return; if (ctx->cancellable) g_object_unref (ctx->cancellable); if (ctx->modem_location) g_object_unref (ctx->modem_location); if (ctx->object) g_object_unref (ctx->object); if (ctx->manager) g_object_unref (ctx->manager); g_free (ctx); } static void ensure_modem_location (void) { if (!ctx->modem_location) { g_printerr ("error: modem has no location capabilities\n"); exit (EXIT_FAILURE); } /* Success */ } void mmcli_modem_location_shutdown (void) { context_free (); } static void print_location_status (void) { gchar *capabilities; gchar *enabled; gchar *gps_refresh_rate = NULL; const gchar *gps_supl_server = NULL; gchar *gps_assistance = NULL; const gchar **gps_assistance_servers = NULL; capabilities = (mm_modem_location_source_build_string_from_mask ( mm_modem_location_get_capabilities (ctx->modem_location))); enabled = (mm_modem_location_source_build_string_from_mask ( mm_modem_location_get_enabled (ctx->modem_location))); /* If GPS supported, show GPS refresh rate and supported assistance data */ if (mm_modem_location_get_capabilities (ctx->modem_location) & (MM_MODEM_LOCATION_SOURCE_GPS_RAW | MM_MODEM_LOCATION_SOURCE_GPS_NMEA)) { guint rate; MMModemLocationAssistanceDataType mask; rate = mm_modem_location_get_gps_refresh_rate (ctx->modem_location); gps_refresh_rate = g_strdup_printf ("%u", rate); /* If A-GPS supported, show SUPL server setup */ if (mm_modem_location_get_capabilities (ctx->modem_location) & (MM_MODEM_LOCATION_SOURCE_AGPS_MSA | MM_MODEM_LOCATION_SOURCE_AGPS_MSB)) gps_supl_server = mm_modem_location_get_supl_server (ctx->modem_location); mask = mm_modem_location_get_supported_assistance_data (ctx->modem_location); gps_assistance = mm_modem_location_assistance_data_type_build_string_from_mask (mask); /* If any assistance data supported, show server list */ if (mask != MM_MODEM_LOCATION_ASSISTANCE_DATA_TYPE_NONE) gps_assistance_servers = mm_modem_location_get_assistance_data_servers (ctx->modem_location); } mmcli_output_string_list_take (MMC_F_LOCATION_CAPABILITIES, capabilities); mmcli_output_string_list_take (MMC_F_LOCATION_ENABLED, enabled); mmcli_output_string (MMC_F_LOCATION_SIGNALS, mm_modem_location_signals_location (ctx->modem_location) ? "yes" : "no"); mmcli_output_string_take_typed (MMC_F_LOCATION_GPS_REFRESH_RATE, gps_refresh_rate, "seconds"); mmcli_output_string (MMC_F_LOCATION_GPS_SUPL_SERVER, gps_supl_server); mmcli_output_string_list_take (MMC_F_LOCATION_GPS_ASSISTANCE, gps_assistance); mmcli_output_string_array (MMC_F_LOCATION_GPS_ASSISTANCE_SERVERS, gps_assistance_servers, TRUE); mmcli_output_dump (); } static void setup_process_reply (gboolean result, const GError *error) { if (!result) { g_printerr ("error: couldn't setup location gathering: '%s'\n", error ? error->message : "unknown error"); exit (EXIT_FAILURE); } g_print ("successfully setup location gathering\n"); } static void setup_ready (MMModemLocation *modem_location, GAsyncResult *result) { gboolean operation_result; GError *error = NULL; operation_result = mm_modem_location_setup_finish (modem_location, result, &error); setup_process_reply (operation_result, error); mmcli_async_operation_done (); } static void set_supl_server_process_reply (gboolean result, const GError *error) { if (!result) { g_printerr ("error: couldn't set SUPL servert address: '%s'\n", error ? error->message : "unknown error"); exit (EXIT_FAILURE); } g_print ("successfully set SUPL server address\n"); } static void set_supl_server_ready (MMModemLocation *modem_location, GAsyncResult *result) { gboolean operation_result; GError *error = NULL; operation_result = mm_modem_location_set_supl_server_finish (modem_location, result, &error); set_supl_server_process_reply (operation_result, error); mmcli_async_operation_done (); } static gboolean parse_inject_assistance_data (guint8 **o_data, gsize *o_data_size) { gboolean result = FALSE; GFile *file = NULL; gchar *data; gsize data_size; GError *error = NULL; file = g_file_new_for_commandline_arg (inject_assistance_data_str); if (!g_file_load_contents (file, NULL, &data, &data_size, NULL, &error)) { g_printerr ("error: cannot load file contents: %s\n", error->message); goto out; } if (data_size == 0) { g_printerr ("error: file is empty\n"); goto out; } *o_data = (guint8 *)data; *o_data_size = data_size; result = TRUE; out: if (error) g_error_free (error); g_object_unref (file); return result; } static void inject_assistance_data_process_reply (gboolean result, const GError *error) { if (!result) { g_printerr ("error: couldn't inject assistance data: '%s'\n", error ? error->message : "unknown error"); exit (EXIT_FAILURE); } g_print ("successfully injected assistance data\n"); } static void inject_assistance_data_ready (MMModemLocation *modem_location, GAsyncResult *result) { gboolean operation_result; GError *error = NULL; operation_result = mm_modem_location_inject_assistance_data_finish (modem_location, result, &error); inject_assistance_data_process_reply (operation_result, error); mmcli_async_operation_done (); } static void set_gps_refresh_rate_process_reply (gboolean result, const GError *error) { if (!result) { g_printerr ("error: couldn't set GPS refresh rate: '%s'\n", error ? error->message : "unknown error"); exit (EXIT_FAILURE); } g_print ("successfully set GPS refresh rate\n"); } static void set_gps_refresh_rate_ready (MMModemLocation *modem_location, GAsyncResult *result) { gboolean operation_result; GError *error = NULL; operation_result = mm_modem_location_set_gps_refresh_rate_finish (modem_location, result, &error); set_gps_refresh_rate_process_reply (operation_result, error); mmcli_async_operation_done (); } static MMModemLocationSource build_sources_from_flags (void) { MMModemLocationSource sources; /* Base the new actions on the previously enabled sources */ sources = mm_modem_location_get_enabled (ctx->modem_location); if (enable_3gpp_flag) sources |= MM_MODEM_LOCATION_SOURCE_3GPP_LAC_CI; if (disable_3gpp_flag) sources &= ~MM_MODEM_LOCATION_SOURCE_3GPP_LAC_CI; if (enable_agps_msa_flag) sources |= MM_MODEM_LOCATION_SOURCE_AGPS_MSA; if (disable_agps_msa_flag) sources &= ~MM_MODEM_LOCATION_SOURCE_AGPS_MSA; if (enable_agps_msb_flag) sources |= MM_MODEM_LOCATION_SOURCE_AGPS_MSB; if (disable_agps_msb_flag) sources &= ~MM_MODEM_LOCATION_SOURCE_AGPS_MSB; if (enable_gps_nmea_flag) sources |= MM_MODEM_LOCATION_SOURCE_GPS_NMEA; if (disable_gps_nmea_flag) sources &= ~MM_MODEM_LOCATION_SOURCE_GPS_NMEA; if (enable_gps_raw_flag) sources |= MM_MODEM_LOCATION_SOURCE_GPS_RAW; if (disable_gps_raw_flag) sources &= ~MM_MODEM_LOCATION_SOURCE_GPS_RAW; if (enable_cdma_bs_flag) sources |= MM_MODEM_LOCATION_SOURCE_CDMA_BS; if (disable_cdma_bs_flag) sources &= ~MM_MODEM_LOCATION_SOURCE_CDMA_BS; if (enable_gps_unmanaged_flag) sources |= MM_MODEM_LOCATION_SOURCE_GPS_UNMANAGED; if (disable_gps_unmanaged_flag) sources &= ~MM_MODEM_LOCATION_SOURCE_GPS_UNMANAGED; return sources; } static gboolean build_signals_location_from_flags (void) { if (set_enable_signal_flag) return TRUE; if (set_disable_signal_flag) return FALSE; return mm_modem_location_signals_location (ctx->modem_location); } static void get_location_process_reply (MMLocation3gpp *location_3gpp, MMLocationGpsNmea *location_gps_nmea, MMLocationGpsRaw *location_gps_raw, MMLocationCdmaBs *location_cdma_bs, const GError *error) { gchar **nmea = NULL; gchar *mcc = NULL; gchar *mnc = NULL; gchar *lac = NULL; gchar *tac = NULL; gchar *cid = NULL; const gchar *gps_utc = NULL; gchar *gps_longitude = NULL; gchar *gps_latitude = NULL; gchar *gps_altitude = NULL; gchar *cdma_bs_longitude = NULL; gchar *cdma_bs_latitude = NULL; if (error) { g_printerr ("error: couldn't get location from the modem: '%s'\n", error ? error->message : "unknown error"); exit (EXIT_FAILURE); } if (location_3gpp) { const gchar *operator_code; operator_code = mm_location_3gpp_get_operator_code (location_3gpp); mcc = g_strndup (operator_code ? operator_code : "0", 3); mnc = g_strdup (operator_code ? operator_code + 3 : "0"); lac = g_strdup_printf ("%04lX", mm_location_3gpp_get_location_area_code (location_3gpp)); tac = g_strdup_printf ("%06lX", mm_location_3gpp_get_tracking_area_code (location_3gpp)); cid = g_strdup_printf ("%08lX", mm_location_3gpp_get_cell_id (location_3gpp)); } if (location_gps_nmea) nmea = mm_location_gps_nmea_get_traces (location_gps_nmea); if (location_gps_raw) { gps_utc = mm_location_gps_raw_get_utc_time (location_gps_raw); gps_longitude = g_strdup_printf ("%lf", mm_location_gps_raw_get_longitude (location_gps_raw)); gps_latitude = g_strdup_printf ("%lf", mm_location_gps_raw_get_latitude (location_gps_raw)); gps_altitude = g_strdup_printf ("%lf", mm_location_gps_raw_get_altitude (location_gps_raw)); } if (location_cdma_bs) { cdma_bs_longitude = g_strdup_printf ("%lf", mm_location_cdma_bs_get_longitude (location_cdma_bs)); cdma_bs_latitude = g_strdup_printf ("%lf", mm_location_cdma_bs_get_latitude (location_cdma_bs)); } mmcli_output_string_take (MMC_F_LOCATION_3GPP_MCC, mcc); mmcli_output_string_take (MMC_F_LOCATION_3GPP_MNC, mnc); mmcli_output_string_take (MMC_F_LOCATION_3GPP_LAC, lac); mmcli_output_string_take (MMC_F_LOCATION_3GPP_TAC, tac); mmcli_output_string_take (MMC_F_LOCATION_3GPP_CID, cid); mmcli_output_string_array_multiline_take (MMC_F_LOCATION_GPS_NMEA, nmea); mmcli_output_string (MMC_F_LOCATION_GPS_UTC, gps_utc); mmcli_output_string_take (MMC_F_LOCATION_GPS_LONG, gps_longitude); mmcli_output_string_take (MMC_F_LOCATION_GPS_LAT, gps_latitude); mmcli_output_string_take (MMC_F_LOCATION_GPS_ALT, gps_altitude); mmcli_output_string_take (MMC_F_LOCATION_CDMABS_LONG, cdma_bs_longitude); mmcli_output_string_take (MMC_F_LOCATION_CDMABS_LAT, cdma_bs_latitude); mmcli_output_dump (); g_clear_object (&location_3gpp); g_clear_object (&location_gps_nmea); g_clear_object (&location_gps_raw); g_clear_object (&location_cdma_bs); } static void cancelled (GCancellable *cancellable) { mmcli_async_operation_done (); } static void print_signaled_location (MMModemLocation *modem_location) { MMLocation3gpp *location_3gpp; MMLocationGpsNmea *location_gps_nmea; MMLocationGpsRaw *location_gps_raw; MMLocationCdmaBs *location_cdma_bs; location_3gpp = mm_modem_location_get_signaled_3gpp (modem_location); location_gps_nmea = mm_modem_location_get_signaled_gps_nmea (modem_location); location_gps_raw = mm_modem_location_get_signaled_gps_raw (modem_location); location_cdma_bs = mm_modem_location_get_signaled_cdma_bs (modem_location); get_location_process_reply (location_3gpp, location_gps_nmea, location_gps_raw, location_cdma_bs, NULL); } static void signaled_location_updated (MMModemLocation *modem_location) { print_signaled_location (modem_location); } static void get_location_ready (MMModemLocation *modem_location, GAsyncResult *result) { MMLocation3gpp *location_3gpp = NULL; MMLocationGpsNmea *location_gps_nmea = NULL; MMLocationGpsRaw *location_gps_raw = NULL; MMLocationCdmaBs *location_cdma_bs = NULL; GError *error = NULL; mm_modem_location_get_full_finish (modem_location, result, &location_3gpp, &location_gps_nmea, &location_gps_raw, &location_cdma_bs, &error); get_location_process_reply (location_3gpp, location_gps_nmea, location_gps_raw, location_cdma_bs, error); mmcli_async_operation_done (); } static void get_modem_ready (GObject *source, GAsyncResult *result, gpointer none) { ctx->object = mmcli_get_modem_finish (result, &ctx->manager); ctx->modem_location = mm_object_get_modem_location (ctx->object); /* Setup operation timeout */ if (ctx->modem_location) mmcli_force_operation_timeout (G_DBUS_PROXY (ctx->modem_location)); ensure_modem_location (); if (status_flag) g_assert_not_reached (); /* Request to setup location gathering? */ if (any_location_setup_flag) { g_debug ("Asynchronously setting up location gathering..."); mm_modem_location_setup (ctx->modem_location, build_sources_from_flags (), build_signals_location_from_flags (), ctx->cancellable, (GAsyncReadyCallback)setup_ready, NULL); return; } /* Request to get location from the modem? */ if (get_flag) { g_debug ("Asynchronously getting location from the modem..."); mm_modem_location_get_full (ctx->modem_location, ctx->cancellable, (GAsyncReadyCallback)get_location_ready, NULL); return; } /* Request to monitor location? */ if (monitor_flag) { print_signaled_location (ctx->modem_location); g_signal_connect (ctx->modem_location, "notify::location", G_CALLBACK (signaled_location_updated), NULL); /* If we get cancelled, operation done */ g_cancellable_connect (ctx->cancellable, G_CALLBACK (cancelled), NULL, NULL); return; } /* Request to set SUPL server? */ if (set_supl_server_str) { g_debug ("Asynchronously setting SUPL server..."); mm_modem_location_set_supl_server (ctx->modem_location, set_supl_server_str, ctx->cancellable, (GAsyncReadyCallback)set_supl_server_ready, NULL); return; } /* Request to inject assistance data? */ if (inject_assistance_data_str) { guint8 *data; gsize data_size; if (!parse_inject_assistance_data (&data, &data_size)) { g_printerr ("error: couldn't inject assistance data: invalid parameters given: '%s'\n", inject_assistance_data_str); exit (EXIT_FAILURE); } g_debug ("Asynchronously injecting assistance data..."); mm_modem_location_inject_assistance_data (ctx->modem_location, data, data_size, ctx->cancellable, (GAsyncReadyCallback)inject_assistance_data_ready, NULL); g_free (data); return; } /* Request to set GPS refresh rate? */ if (set_gps_refresh_rate_str) { guint rate; if (!mm_get_uint_from_str (set_gps_refresh_rate_str, &rate)) { g_printerr ("error: couldn't set GPS refresh rate: invalid rate given: '%s'\n", set_gps_refresh_rate_str); exit (EXIT_FAILURE); } g_debug ("Asynchronously setting GPS refresh rate..."); mm_modem_location_set_gps_refresh_rate (ctx->modem_location, rate, ctx->cancellable, (GAsyncReadyCallback)set_gps_refresh_rate_ready, NULL); return; } g_warn_if_reached (); } void mmcli_modem_location_run_asynchronous (GDBusConnection *connection, GCancellable *cancellable) { /* Initialize context */ ctx = g_new0 (Context, 1); if (cancellable) ctx->cancellable = g_object_ref (cancellable); /* Get proper modem */ mmcli_get_modem (connection, mmcli_get_common_modem_string (), cancellable, (GAsyncReadyCallback)get_modem_ready, NULL); } void mmcli_modem_location_run_synchronous (GDBusConnection *connection) { GError *error = NULL; /* Initialize context */ ctx = g_new0 (Context, 1); ctx->object = mmcli_get_modem_sync (connection, mmcli_get_common_modem_string (), &ctx->manager); ctx->modem_location = mm_object_get_modem_location (ctx->object); /* Setup operation timeout */ if (ctx->modem_location) mmcli_force_operation_timeout (G_DBUS_PROXY (ctx->modem_location)); ensure_modem_location (); /* Request to get location status? */ if (status_flag) { g_debug ("Printing location status..."); print_location_status (); return; } /* Request to setup location gathering? */ if (any_location_setup_flag) { gboolean result; g_debug ("Synchronously setting up location gathering..."); result = mm_modem_location_setup_sync (ctx->modem_location, build_sources_from_flags (), build_signals_location_from_flags (), NULL, &error); setup_process_reply (result, error); return; } /* Request to get location from the modem? */ if (get_flag) { MMLocation3gpp *location_3gpp = NULL; MMLocationGpsNmea *location_gps_nmea = NULL; MMLocationGpsRaw *location_gps_raw = NULL; MMLocationCdmaBs *location_cdma_bs = NULL; g_debug ("Synchronously getting location from the modem..."); mm_modem_location_get_full_sync (ctx->modem_location, &location_3gpp, &location_gps_nmea, &location_gps_raw, &location_cdma_bs, NULL, &error); get_location_process_reply (location_3gpp, location_gps_nmea, location_gps_raw, location_cdma_bs, error); return; } /* Request to set SUPL server? */ if (set_supl_server_str) { gboolean result; g_debug ("Synchronously setting SUPL server..."); result = mm_modem_location_set_supl_server_sync (ctx->modem_location, set_supl_server_str, NULL, &error); set_supl_server_process_reply (result, error); return; } /* Request to inject assistance data? */ if (inject_assistance_data_str) { gboolean result; guint8 *data; gsize data_size; if (!parse_inject_assistance_data (&data, &data_size)) { g_printerr ("error: couldn't inject assistance data: invalid parameters given: '%s'\n", inject_assistance_data_str); exit (EXIT_FAILURE); } g_debug ("Synchronously setting assistance data..."); result = mm_modem_location_inject_assistance_data_sync (ctx->modem_location, data, data_size, NULL, &error); inject_assistance_data_process_reply (result, error); g_free (data); return; } /* Request to set GPS refresh rate? */ if (set_gps_refresh_rate_str) { gboolean result; guint rate; if (!mm_get_uint_from_str (set_gps_refresh_rate_str, &rate)) { g_printerr ("error: couldn't set GPS refresh rate: invalid rate given: '%s'\n", set_gps_refresh_rate_str); exit (EXIT_FAILURE); } g_debug ("Synchronously setting GPS refresh rate..."); result = mm_modem_location_set_gps_refresh_rate_sync (ctx->modem_location, rate, NULL, &error); set_gps_refresh_rate_process_reply (result, error); return; } g_warn_if_reached (); } ModemManager-1.23.4-dev/cli/mmcli-modem-messaging.c000066400000000000000000000406021456466623000220150ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * mmcli -- Control modem status & access information from the command line * * 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, see . * * Copyright (C) 2012 Google, Inc. * Copyright (C) 2023 Tom Wimmenhove */ #include "config.h" #include #include #include #include #include #include #define _LIBMM_INSIDE_MMCLI #include #include "mmcli.h" #include "mmcli-common.h" #include "mmcli-output.h" /* Context */ typedef struct { GDBusConnection *connection; MMManager *manager; GCancellable *cancellable; MMObject *object; MMModemMessaging *modem_messaging; } Context; static Context *ctx; /* Options */ static gboolean status_flag; static gboolean list_flag; static gchar *create_str; static gchar *create_with_data_str; static gchar *create_with_text_str; static gchar *delete_str; static GOptionEntry entries[] = { { "messaging-status", 0, 0, G_OPTION_ARG_NONE, &status_flag, "Show status of messaging support.", NULL }, { "messaging-list-sms", 0, 0, G_OPTION_ARG_NONE, &list_flag, "List SMS messages available in a given modem", NULL }, { "messaging-create-sms", 0, 0, G_OPTION_ARG_STRING, &create_str, "Create a new SMS in a given modem", "[\"key=value,...\"]" }, { "messaging-create-sms-with-data", 0, 0, G_OPTION_ARG_FILENAME, &create_with_data_str, "Pass the given file as data contents when creating a new SMS", "[File path]" }, { "messaging-create-sms-with-text", 0, 0, G_OPTION_ARG_FILENAME, &create_with_text_str, "Pass the given file as message contents when creating a new SMS", "[File path]" }, { "messaging-delete-sms", 0, 0, G_OPTION_ARG_STRING, &delete_str, "Delete a SMS from a given modem", "[PATH|INDEX]" }, { NULL } }; GOptionGroup * mmcli_modem_messaging_get_option_group (void) { GOptionGroup *group; group = g_option_group_new ("messaging", "Messaging options:", "Show Messaging options", NULL, NULL); g_option_group_add_entries (group, entries); return group; } gboolean mmcli_modem_messaging_options_enabled (void) { static guint n_actions = 0; static gboolean checked = FALSE; if (checked) return !!n_actions; n_actions = (status_flag + list_flag + !!create_str + !!delete_str); if (n_actions > 1) { g_printerr ("error: too many Messaging actions requested\n"); exit (EXIT_FAILURE); } if (create_with_data_str && !create_str) { g_printerr ("error: `--messaging-create-with-data' must be given along " "with `--messaging-create-sms'\n"); exit (EXIT_FAILURE); } if (create_with_text_str && !create_str) { g_printerr ("error: `--messaging-create-with-text' must be given along " "with `--messaging-create-sms'\n"); exit (EXIT_FAILURE); } if (create_with_data_str && create_with_text_str) { g_printerr ("error: `--messaging-create-with-data' and " "`--messaging-create-with-text' cannot be used at the " "same time\n"); exit (EXIT_FAILURE); } if (status_flag) mmcli_force_sync_operation (); checked = TRUE; return !!n_actions; } static void context_free (void) { if (!ctx) return; if (ctx->cancellable) g_object_unref (ctx->cancellable); if (ctx->modem_messaging) g_object_unref (ctx->modem_messaging); if (ctx->object) g_object_unref (ctx->object); if (ctx->manager) g_object_unref (ctx->manager); if (ctx->connection) g_object_unref (ctx->connection); g_free (ctx); } static void ensure_modem_messaging (void) { if (mm_modem_get_state (mm_object_peek_modem (ctx->object)) < MM_MODEM_STATE_ENABLED) { g_printerr ("error: modem not enabled yet\n"); exit (EXIT_FAILURE); } if (!ctx->modem_messaging) { g_printerr ("error: modem has no messaging capabilities\n"); exit (EXIT_FAILURE); } /* Success */ } void mmcli_modem_messaging_shutdown (void) { context_free (); } static void get_file_contents (const gchar *filename, gchar **contents, gsize *contents_size) { GError *error = NULL; gchar *path; GFile *file; g_debug ("Reading data from file '%s'", filename); file = g_file_new_for_commandline_arg (filename); path = g_file_get_path (file); if (!g_file_get_contents (path, contents, contents_size, &error)) { g_printerr ("error: cannot read from file '%s': '%s'\n", filename, error->message); exit (EXIT_FAILURE); } g_free (path); g_object_unref (file); } static MMSmsProperties * build_sms_properties_from_input (const gchar *properties_string, const gchar *data_file, const gchar *text_file) { GError *error = NULL; MMSmsProperties *properties; properties = mm_sms_properties_new_from_string (properties_string, &error); if (!properties) { g_printerr ("error: cannot parse properties string: '%s'\n", error->message); exit (EXIT_FAILURE); } if (data_file) { gchar *contents; gsize contents_size; g_debug ("Reading data from file '%s'", data_file); get_file_contents (data_file, &contents, &contents_size); mm_sms_properties_set_data (properties, (guint8 *)contents, contents_size); g_free (contents); } if (text_file) { gchar *contents; gsize contents_size; if (mm_sms_properties_get_text(properties)) { g_printerr ("error: cannot use `--messaging-create-with-text': text " "has already been set using `--messaging-create-sms'\n"); exit (EXIT_FAILURE); } g_debug ("Reading message text from file '%s'", data_file); get_file_contents (text_file, &contents, &contents_size); if (!g_utf8_validate (contents, contents_size, NULL)) { g_printerr ("error: file '%s' contains invalid UTF-8\n", text_file); exit (EXIT_FAILURE); } mm_sms_properties_set_text (properties, contents); g_free (contents); } return properties; } static void print_messaging_status (void) { MMSmsStorage *supported = NULL; guint supported_len = 0; gchar *supported_str = NULL; mm_modem_messaging_get_supported_storages (ctx->modem_messaging, &supported, &supported_len); if (supported) supported_str = mm_common_build_sms_storages_string (supported, supported_len); g_free (supported); mmcli_output_string_take (MMC_F_MESSAGING_SUPPORTED_STORAGES, supported_str); mmcli_output_string (MMC_F_MESSAGING_DEFAULT_STORAGES, mm_sms_storage_get_string ( mm_modem_messaging_get_default_storage (ctx->modem_messaging))); mmcli_output_dump (); } static void output_sms_info (MMSms *sms) { gchar *extra; extra = g_strdup_printf ("(%s)", mm_sms_state_get_string (mm_sms_get_state (sms))); mmcli_output_listitem (MMC_F_SMS_LIST_DBUS_PATH, " ", mm_sms_get_path (sms), extra); g_free (extra); } static void list_process_reply (GList *result, const GError *error) { GList *l; if (error) { g_printerr ("error: couldn't list SMS: '%s'\n", error->message); exit (EXIT_FAILURE); } for (l = result; l; l = g_list_next (l)) output_sms_info (MM_SMS (l->data)); mmcli_output_list_dump (MMC_F_SMS_LIST_DBUS_PATH); } static void list_ready (MMModemMessaging *modem, GAsyncResult *result, gpointer nothing) { GList *operation_result; GError *error = NULL; operation_result = mm_modem_messaging_list_finish (modem, result, &error); list_process_reply (operation_result, error); mmcli_async_operation_done (); } static void create_process_reply (MMSms *sms, const GError *error) { if (!sms) { g_printerr ("error: couldn't create new SMS: '%s'\n", error ? error->message : "unknown error"); exit (EXIT_FAILURE); } mmcli_output_string (MMC_F_MESSAGING_CREATED_SMS, mm_sms_get_path (sms)); mmcli_output_dump (); g_object_unref (sms); } static void create_ready (MMModemMessaging *modem, GAsyncResult *result, gpointer nothing) { MMSms *sms; GError *error = NULL; sms = mm_modem_messaging_create_finish (modem, result, &error); create_process_reply (sms, error); mmcli_async_operation_done (); } static void delete_process_reply (gboolean result, const GError *error) { if (!result) { g_printerr ("error: couldn't delete SMS: '%s'\n", error ? error->message : "unknown error"); exit (EXIT_FAILURE); } g_print ("successfully deleted SMS from modem\n"); } static void delete_ready (MMModemMessaging *modem, GAsyncResult *result, gpointer nothing) { gboolean operation_result; GError *error = NULL; operation_result = mm_modem_messaging_delete_finish (modem, result, &error); delete_process_reply (operation_result, error); mmcli_async_operation_done (); } static void get_sms_to_delete_ready (GDBusConnection *connection, GAsyncResult *res) { MMSms *sms; MMObject *obj = NULL; sms = mmcli_get_sms_finish (res, NULL, &obj); if (!g_str_equal (mm_object_get_path (obj), mm_modem_messaging_get_path (ctx->modem_messaging))) { g_printerr ("error: SMS '%s' not owned by modem '%s'", mm_sms_get_path (sms), mm_modem_messaging_get_path (ctx->modem_messaging)); exit (EXIT_FAILURE); } mm_modem_messaging_delete (ctx->modem_messaging, mm_sms_get_path (sms), ctx->cancellable, (GAsyncReadyCallback)delete_ready, NULL); g_object_unref (sms); g_object_unref (obj); } static void get_modem_ready (GObject *source, GAsyncResult *result, gpointer none) { ctx->object = mmcli_get_modem_finish (result, &ctx->manager); ctx->modem_messaging = mm_object_get_modem_messaging (ctx->object); /* Setup operation timeout */ if (ctx->modem_messaging) mmcli_force_operation_timeout (G_DBUS_PROXY (ctx->modem_messaging)); ensure_modem_messaging (); if (status_flag) g_assert_not_reached (); /* Request to list SMS? */ if (list_flag) { g_debug ("Asynchronously listing SMS in modem..."); mm_modem_messaging_list (ctx->modem_messaging, ctx->cancellable, (GAsyncReadyCallback)list_ready, NULL); return; } /* Request to create a new SMS? */ if (create_str) { MMSmsProperties *properties; properties = build_sms_properties_from_input (create_str, create_with_data_str, create_with_text_str); g_debug ("Asynchronously creating new SMS in modem..."); mm_modem_messaging_create (ctx->modem_messaging, properties, ctx->cancellable, (GAsyncReadyCallback)create_ready, NULL); g_object_unref (properties); return; } /* Request to delete a given SMS? */ if (delete_str) { mmcli_get_sms (ctx->connection, delete_str, ctx->cancellable, (GAsyncReadyCallback)get_sms_to_delete_ready, NULL); return; } g_warn_if_reached (); } void mmcli_modem_messaging_run_asynchronous (GDBusConnection *connection, GCancellable *cancellable) { /* Initialize context */ ctx = g_new0 (Context, 1); if (cancellable) ctx->cancellable = g_object_ref (cancellable); ctx->connection = g_object_ref (connection); /* Get proper modem */ mmcli_get_modem (connection, mmcli_get_common_modem_string (), cancellable, (GAsyncReadyCallback)get_modem_ready, NULL); } void mmcli_modem_messaging_run_synchronous (GDBusConnection *connection) { GError *error = NULL; /* Initialize context */ ctx = g_new0 (Context, 1); ctx->object = mmcli_get_modem_sync (connection, mmcli_get_common_modem_string (), &ctx->manager); ctx->modem_messaging = mm_object_get_modem_messaging (ctx->object); /* Setup operation timeout */ if (ctx->modem_messaging) mmcli_force_operation_timeout (G_DBUS_PROXY (ctx->modem_messaging)); ensure_modem_messaging (); /* Request to get messaging status? */ if (status_flag) { g_debug ("Printing messaging status..."); print_messaging_status (); return; } /* Request to list the SMS? */ if (list_flag) { GList *result; g_debug ("Synchronously listing SMS messages..."); result = mm_modem_messaging_list_sync (ctx->modem_messaging, NULL, &error); list_process_reply (result, error); return; } /* Request to create a new SMS? */ if (create_str) { MMSms *sms; MMSmsProperties *properties; properties = build_sms_properties_from_input (create_str, create_with_data_str, create_with_text_str); g_debug ("Synchronously creating new SMS in modem..."); sms = mm_modem_messaging_create_sync (ctx->modem_messaging, properties, NULL, &error); g_object_unref (properties); create_process_reply (sms, error); return; } /* Request to delete a given SMS? */ if (delete_str) { gboolean result; MMSms *sms; MMObject *obj = NULL; sms = mmcli_get_sms_sync (connection, delete_str, NULL, &obj); if (!g_str_equal (mm_object_get_path (obj), mm_modem_messaging_get_path (ctx->modem_messaging))) { g_printerr ("error: SMS '%s' not owned by modem '%s'", mm_sms_get_path (sms), mm_modem_messaging_get_path (ctx->modem_messaging)); exit (EXIT_FAILURE); } result = mm_modem_messaging_delete_sync (ctx->modem_messaging, mm_sms_get_path (sms), NULL, &error); g_object_unref (sms); g_object_unref (obj); delete_process_reply (result, error); return; } g_warn_if_reached (); } ModemManager-1.23.4-dev/cli/mmcli-modem-oma.c000066400000000000000000000364131456466623000206210ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * mmcli -- Control modem status & access information from the command line * * 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, see . * * Copyright (C) 2013 Google, Inc. */ #include "config.h" #include #include #include #include #include #include #define _LIBMM_INSIDE_MMCLI #include #include "mmcli.h" #include "mmcli-common.h" #include "mmcli-output.h" /* Context */ typedef struct { MMManager *manager; GCancellable *cancellable; MMObject *object; MMModemOma *modem_oma; } Context; static Context *ctx; /* Options */ static gboolean status_flag; static gchar *setup_str; static gchar *start_str; static gchar *accept_str; static gchar *reject_str; static gboolean cancel_flag; static GOptionEntry entries[] = { { "oma-status", 0, 0, G_OPTION_ARG_NONE, &status_flag, "Current status of the OMA device management", NULL }, { "oma-setup", 0, 0, G_OPTION_ARG_STRING, &setup_str, "Setup OMA features", "[FEATURE1|FEATURE2...]" }, { "oma-start-client-initiated-session", 0, 0, G_OPTION_ARG_STRING, &start_str, "Start client initiated OMA DM session", "[Session type]" }, { "oma-accept-network-initiated-session", 0, 0, G_OPTION_ARG_STRING, &accept_str, "Accept network initiated OMA DM session", "[Session ID]" }, { "oma-reject-network-initiated-session", 0, 0, G_OPTION_ARG_STRING, &reject_str, "Reject network initiated OMA DM session", "[Session ID]" }, { "oma-cancel-session", 0, 0, G_OPTION_ARG_NONE, &cancel_flag, "Cancel current OMA DM session", NULL }, { NULL } }; GOptionGroup * mmcli_modem_oma_get_option_group (void) { GOptionGroup *group; group = g_option_group_new ("oma", "OMA options:", "Show OMA options", NULL, NULL); g_option_group_add_entries (group, entries); return group; } gboolean mmcli_modem_oma_options_enabled (void) { static guint n_actions = 0; static gboolean checked = FALSE; if (checked) return !!n_actions; n_actions = (status_flag + !!setup_str + !!start_str + !!accept_str + !!reject_str + cancel_flag); if (n_actions > 1) { g_printerr ("error: too many OMA actions requested\n"); exit (EXIT_FAILURE); } if (status_flag) mmcli_force_sync_operation (); checked = TRUE; return !!n_actions; } static void context_free (void) { if (!ctx) return; if (ctx->cancellable) g_object_unref (ctx->cancellable); if (ctx->modem_oma) g_object_unref (ctx->modem_oma); if (ctx->object) g_object_unref (ctx->object); if (ctx->manager) g_object_unref (ctx->manager); g_free (ctx); } static void ensure_modem_oma (void) { if (!ctx->modem_oma) { g_printerr ("error: modem has no OMA capabilities\n"); exit (EXIT_FAILURE); } /* Success */ } void mmcli_modem_oma_shutdown (void) { context_free (); } static void print_oma_status (void) { gchar *features_str; const MMOmaPendingNetworkInitiatedSession *pending_sessions; guint n_pending_sessions; const gchar *current_session_type = NULL; const gchar *current_session_state = NULL; GPtrArray *aux = NULL; features_str = mm_oma_feature_build_string_from_mask (mm_modem_oma_get_features (ctx->modem_oma)); /* Current session */ if (mm_modem_oma_get_session_type (ctx->modem_oma) != MM_OMA_SESSION_TYPE_UNKNOWN) { current_session_type = mm_oma_session_type_get_string (mm_modem_oma_get_session_type (ctx->modem_oma)); current_session_state = mm_oma_session_state_get_string (mm_modem_oma_get_session_state (ctx->modem_oma)); } /* If 1 or more pending sessions... */ if (mm_modem_oma_peek_pending_network_initiated_sessions (ctx->modem_oma, &pending_sessions, &n_pending_sessions) && n_pending_sessions > 0) { guint i; aux = g_ptr_array_new (); for (i = 0; i < n_pending_sessions; i++) { gchar *info; info = g_strdup_printf ("id: %u, type: %s", pending_sessions[i].session_id, mm_oma_session_type_get_string (pending_sessions[i].session_type)); g_ptr_array_add (aux, info); } g_ptr_array_add (aux, NULL); } mmcli_output_string_take (MMC_F_OMA_FEATURES, features_str); mmcli_output_string (MMC_F_OMA_CURRENT_TYPE, current_session_type); mmcli_output_string (MMC_F_OMA_CURRENT_STATE, current_session_state); mmcli_output_string_array_take (MMC_F_OMA_PENDING_SESSIONS, aux ? (gchar **) g_ptr_array_free (aux, FALSE) : NULL, TRUE); mmcli_output_dump (); } static void setup_process_reply (gboolean result, const GError *error) { if (!result) { g_printerr ("error: couldn't setup OMA features: '%s'\n", error ? error->message : "unknown error"); exit (EXIT_FAILURE); } g_print ("Successfully setup OMA features\n"); } static void setup_ready (MMModemOma *modem, GAsyncResult *result) { gboolean res; GError *error = NULL; res = mm_modem_oma_setup_finish (modem, result, &error); setup_process_reply (res, error); mmcli_async_operation_done (); } static void start_process_reply (gboolean result, const GError *error) { if (!result) { g_printerr ("error: couldn't start OMA session: '%s'\n", error ? error->message : "unknown error"); exit (EXIT_FAILURE); } g_print ("Successfully started OMA session\n"); } static void start_ready (MMModemOma *modem, GAsyncResult *result) { gboolean res; GError *error = NULL; res = mm_modem_oma_start_client_initiated_session_finish (modem, result, &error); start_process_reply (res, error); mmcli_async_operation_done (); } static void accept_process_reply (gboolean result, const GError *error) { if (!result) { g_printerr ("error: couldn't %s OMA session: '%s'\n", accept_str ? "accept" : "reject", error ? error->message : "unknown error"); exit (EXIT_FAILURE); } g_print ("Successfully %s OMA session\n", accept_str ? "accepted" : "rejected"); } static void accept_ready (MMModemOma *modem, GAsyncResult *result) { gboolean res; GError *error = NULL; res = mm_modem_oma_accept_network_initiated_session_finish (modem, result, &error); accept_process_reply (res, error); mmcli_async_operation_done (); } static void cancel_process_reply (gboolean result, const GError *error) { if (!result) { g_printerr ("error: couldn't cancel OMA session: '%s'\n", error ? error->message : "unknown error"); exit (EXIT_FAILURE); } g_print ("Successfully cancelled OMA session\n"); } static void cancel_ready (MMModemOma *modem, GAsyncResult *result) { gboolean res; GError *error = NULL; res = mm_modem_oma_cancel_session_finish (modem, result, &error); cancel_process_reply (res, error); mmcli_async_operation_done (); } static void get_modem_ready (GObject *source, GAsyncResult *result) { ctx->object = mmcli_get_modem_finish (result, &ctx->manager); ctx->modem_oma = mm_object_get_modem_oma (ctx->object); /* Setup operation timeout */ if (ctx->modem_oma) mmcli_force_operation_timeout (G_DBUS_PROXY (ctx->modem_oma)); ensure_modem_oma (); g_assert (!status_flag); /* Request to setup OMA features? */ if (setup_str) { GError *error = NULL; MMOmaFeature features; features = mm_common_get_oma_features_from_string (setup_str, &error); if (error) { g_printerr ("Error parsing OMA features string: '%s'\n", error->message); exit (EXIT_FAILURE); } g_debug ("Asynchronously setting up OMA features..."); mm_modem_oma_setup (ctx->modem_oma, features, ctx->cancellable, (GAsyncReadyCallback)setup_ready, NULL); return; } /* Request to start session? */ if (start_str) { GError *error = NULL; MMOmaSessionType session_type; session_type = mm_common_get_oma_session_type_from_string (start_str, &error); if (error) { g_printerr ("Error parsing OMA session type string: '%s'\n", error->message); exit (EXIT_FAILURE); } g_debug ("Asynchronously starting OMA session..."); mm_modem_oma_start_client_initiated_session (ctx->modem_oma, session_type, ctx->cancellable, (GAsyncReadyCallback)start_ready, NULL); return; } /* Request to accept or reject session? */ if (accept_str || reject_str) { guint session_id; if (!mm_get_uint_from_str (accept_str ? accept_str : reject_str, &session_id)) { g_printerr ("Error parsing OMA session id string: not a number"); exit (EXIT_FAILURE); } g_debug ("Asynchronously %s OMA session...", accept_str ? "accepting" : "rejecting"); mm_modem_oma_accept_network_initiated_session (ctx->modem_oma, session_id, !!accept_str, ctx->cancellable, (GAsyncReadyCallback)accept_ready, NULL); return; } /* Request to cancel a session? */ if (cancel_flag) { g_debug ("Asynchronously cancelling OMA session..."); mm_modem_oma_cancel_session (ctx->modem_oma, ctx->cancellable, (GAsyncReadyCallback)cancel_ready, NULL); return; } g_warn_if_reached (); } void mmcli_modem_oma_run_asynchronous (GDBusConnection *connection, GCancellable *cancellable) { /* Initialize context */ ctx = g_new0 (Context, 1); if (cancellable) ctx->cancellable = g_object_ref (cancellable); /* Get proper modem */ mmcli_get_modem (connection, mmcli_get_common_modem_string (), cancellable, (GAsyncReadyCallback)get_modem_ready, NULL); } void mmcli_modem_oma_run_synchronous (GDBusConnection *connection) { GError *error = NULL; /* Initialize context */ ctx = g_new0 (Context, 1); ctx->object = mmcli_get_modem_sync (connection, mmcli_get_common_modem_string (), &ctx->manager); ctx->modem_oma = mm_object_get_modem_oma (ctx->object); /* Setup operation timeout */ if (ctx->modem_oma) mmcli_force_operation_timeout (G_DBUS_PROXY (ctx->modem_oma)); ensure_modem_oma (); /* Request to get status? */ if (status_flag) { g_debug ("Printing OMA status..."); print_oma_status (); return; } /* Request to setup OMA features? */ if (setup_str) { gboolean result; MMOmaFeature features; features = mm_common_get_oma_features_from_string (setup_str, &error); if (error) { g_printerr ("Error parsing OMA features string: '%s'\n", error->message); exit (EXIT_FAILURE); } g_debug ("Synchronously setting up OMA features..."); result = mm_modem_oma_setup_sync (ctx->modem_oma, features, NULL, &error); setup_process_reply (result, error); return; } /* Request to start session? */ if (start_str) { gboolean result; MMOmaSessionType session_type; session_type = mm_common_get_oma_session_type_from_string (start_str, &error); if (error) { g_printerr ("Error parsing OMA session type string: '%s'\n", error->message); exit (EXIT_FAILURE); } g_debug ("Synchronously starting OMA session..."); result = mm_modem_oma_start_client_initiated_session_sync (ctx->modem_oma, session_type, NULL, &error); start_process_reply (result, error); return; } /* Request to accept or reject session? */ if (accept_str || reject_str) { gboolean result; guint session_id; if (!mm_get_uint_from_str (accept_str ? accept_str : reject_str, &session_id)) { g_printerr ("Error parsing OMA session id string: not a number"); exit (EXIT_FAILURE); } g_debug ("Synchronously %s OMA session...", accept_str ? "accepting" : "rejecting"); result = mm_modem_oma_accept_network_initiated_session_sync (ctx->modem_oma, session_id, !!accept_str, NULL, &error); accept_process_reply (result, error); return; } /* Request to cancel a session? */ if (cancel_flag) { gboolean result; g_debug ("Synchronously cancelling OMA session..."); result = mm_modem_oma_cancel_session_sync (ctx->modem_oma, NULL, &error); cancel_process_reply (result, error); return; } g_warn_if_reached (); } ModemManager-1.23.4-dev/cli/mmcli-modem-sar.c000066400000000000000000000235131456466623000206270ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * mmcli -- Control modem status & access information from the command line * * 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, see . * * Copyright (C) 2021 Fibocom Wireless Inc. */ #include "config.h" #include #include #include #include #include #include #define _LIBMM_INSIDE_MMCLI #include #include "mmcli.h" #include "mmcli-common.h" #include "mmcli-output.h" /* Context */ typedef struct { MMManager *manager; GCancellable *cancellable; MMObject *object; MMModemSar *modem_sar; } Context; static Context *ctx; /* Options */ static gboolean status_flag; static gboolean sar_enable_flag; static gboolean sar_disable_flag; static gint power_level_int = -1; static GOptionEntry entries[] = { { "sar-status", 0, 0, G_OPTION_ARG_NONE, &status_flag, "Current status of the SAR", NULL }, { "sar-enable", 0, 0, G_OPTION_ARG_NONE, &sar_enable_flag, "Enable dynamic SAR", NULL }, { "sar-disable", 0, 0, G_OPTION_ARG_NONE, &sar_disable_flag, "Disable dynamic SAR", NULL }, { "sar-set-power-level", 0, 0, G_OPTION_ARG_INT, &power_level_int, "Set current dynamic SAR power level for all antennas on the device", "[power level]" }, { NULL } }; GOptionGroup * mmcli_modem_sar_get_option_group (void) { GOptionGroup *group; group = g_option_group_new ("sar", "SAR options:", "Show SAR options", NULL, NULL); g_option_group_add_entries (group, entries); return group; } gboolean mmcli_modem_sar_options_enabled (void) { static guint n_actions = 0; static gboolean checked = FALSE; if (checked) return !!n_actions; n_actions = (status_flag + sar_enable_flag + sar_disable_flag + (power_level_int >= 0)); if (n_actions > 1) { g_printerr ("error: too many SAR actions requested\n"); exit (EXIT_FAILURE); } if (status_flag) mmcli_force_sync_operation (); checked = TRUE; return !!n_actions; } static void context_free (void) { if (!ctx) return; if (ctx->cancellable) g_object_unref (ctx->cancellable); if (ctx->modem_sar) g_object_unref (ctx->modem_sar); if (ctx->object) g_object_unref (ctx->object); if (ctx->manager) g_object_unref (ctx->manager); g_free (ctx); } static void ensure_modem_sar (void) { if (!ctx->modem_sar) { g_printerr ("error: modem has no SAR capabilities\n"); exit (EXIT_FAILURE); } /* Success */ } void mmcli_modem_sar_shutdown (void) { context_free (); } static void print_sar_status (void) { guint power_level = 0; gboolean sar_state; sar_state = mm_modem_sar_get_state (ctx->modem_sar); power_level = mm_modem_sar_get_power_level (ctx->modem_sar); mmcli_output_string (MMC_F_SAR_STATE, sar_state ? "yes" : "no"); mmcli_output_string_take (MMC_F_SAR_POWER_LEVEL, g_strdup_printf ("%d", power_level)); mmcli_output_dump (); } static void enable_process_reply (gboolean result, const GError *error) { if (!result) { g_printerr ("error: couldn't enable SAR: '%s'\n", error ? error->message : "unknown error"); exit (EXIT_FAILURE); } g_print ("Successfully enabled SAR\n"); } static void disable_process_reply (gboolean result, const GError *error) { if (!result) { g_printerr ("error: couldn't disable SAR: '%s'\n", error ? error->message : "unknown error"); exit (EXIT_FAILURE); } g_print ("Successfully disabled SAR\n"); } static void enable_ready (MMModemSar *modem, GAsyncResult *result) { gboolean res; GError *error = NULL; res = mm_modem_sar_enable_finish (modem, result, &error); enable_process_reply (res, error); mmcli_async_operation_done (); } static void disable_ready (MMModemSar *modem, GAsyncResult *result) { gboolean res; GError *error = NULL; res = mm_modem_sar_enable_finish (modem, result, &error); disable_process_reply (res, error); mmcli_async_operation_done (); } static void set_power_level_process_reply (gboolean result, const GError *error) { if (!result) { g_printerr ("error: couldn't set the SAR power level: '%s'\n", error ? error->message : "unknown error"); exit (EXIT_FAILURE); } g_print ("Successfully set the SAR power level\n"); } static void set_power_level_ready (MMModemSar *modem, GAsyncResult *result) { gboolean res; GError *error = NULL; res = mm_modem_sar_set_power_level_finish (modem, result, &error); set_power_level_process_reply (res, error); mmcli_async_operation_done (); } static void get_modem_ready (GObject *source, GAsyncResult *result) { ctx->object = mmcli_get_modem_finish (result, &ctx->manager); ctx->modem_sar = mm_object_get_modem_sar (ctx->object); /* Setup operation timeout */ if (ctx->modem_sar) mmcli_force_operation_timeout (G_DBUS_PROXY (ctx->modem_sar)); ensure_modem_sar (); g_assert (!status_flag); /* Request to enable SAR */ if (sar_enable_flag) { g_debug ("Asynchronously enabling SAR ..."); mm_modem_sar_enable (ctx->modem_sar, TRUE, ctx->cancellable, (GAsyncReadyCallback)enable_ready, NULL); return; } /* Request to disable SAR */ if (sar_disable_flag) { g_debug ("Asynchronously disabling SAR ..."); mm_modem_sar_enable (ctx->modem_sar, FALSE, ctx->cancellable, (GAsyncReadyCallback)disable_ready, NULL); return; } /* Request to set power level of SAR */ if (power_level_int >= 0) { g_debug ("Asynchronously starting set sar power level to %u ...", power_level_int); mm_modem_sar_set_power_level (ctx->modem_sar, power_level_int, ctx->cancellable, (GAsyncReadyCallback)set_power_level_ready, NULL); return; } g_warn_if_reached (); } void mmcli_modem_sar_run_asynchronous (GDBusConnection *connection, GCancellable *cancellable) { /* Initialize context */ ctx = g_new0 (Context, 1); if (cancellable) ctx->cancellable = g_object_ref (cancellable); /* Get proper modem */ mmcli_get_modem (connection, mmcli_get_common_modem_string (), cancellable, (GAsyncReadyCallback)get_modem_ready, NULL); } void mmcli_modem_sar_run_synchronous (GDBusConnection *connection) { GError *error = NULL; /* Initialize context */ ctx = g_new0 (Context, 1); ctx->object = mmcli_get_modem_sync (connection, mmcli_get_common_modem_string (), &ctx->manager); ctx->modem_sar = mm_object_get_modem_sar (ctx->object); /* Setup operation timeout */ if (ctx->modem_sar) mmcli_force_operation_timeout (G_DBUS_PROXY (ctx->modem_sar)); ensure_modem_sar (); /* Request to get status? */ if (status_flag) { g_debug ("Printing SAR status..."); print_sar_status (); return; } /* Request to enable SAR */ if (sar_enable_flag) { gboolean result; g_debug ("Synchronously enabling SAR ..."); result = mm_modem_sar_enable_sync (ctx->modem_sar, TRUE, ctx->cancellable, &error); enable_process_reply (result, error); return; } /* Request to disable SAR */ if (sar_disable_flag) { gboolean result; g_debug ("Synchronously disabling SAR ..."); result = mm_modem_sar_enable_sync (ctx->modem_sar, FALSE, ctx->cancellable, &error); disable_process_reply (result, error); return; } /* Request to set power level of SAR */ if (power_level_int >=0 ) { gboolean result; g_debug ("Synchronously starting set power level to %u ...", power_level_int); result = mm_modem_sar_set_power_level_sync (ctx->modem_sar, power_level_int, ctx->cancellable, &error); set_power_level_process_reply (result, error); return; } g_warn_if_reached (); } ModemManager-1.23.4-dev/cli/mmcli-modem-signal.c000066400000000000000000000412001456466623000213100ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * mmcli -- Control modem status & access information from the command line * * 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, see . * * Copyright (C) 2013 Aleksander Morgado */ #include "config.h" #include #include #include #include #include #include #define _LIBMM_INSIDE_MMCLI #include #include "mmcli.h" #include "mmcli-common.h" #include "mmcli-output.h" /* Context */ typedef struct { MMManager *manager; GCancellable *cancellable; MMObject *object; MMModemSignal *modem_signal; } Context; static Context *ctx; /* Options */ static gboolean get_flag; static gchar *setup_str; static gchar *setup_thresholds_str; static GOptionEntry entries[] = { { "signal-setup", 0, 0, G_OPTION_ARG_STRING, &setup_str, "Setup signal quality information polling, in seconds", "[Rate]" }, { "signal-setup-thresholds", 0, 0, G_OPTION_ARG_STRING, &setup_thresholds_str, "Setup signal quality information thresholds (allowed keys: rssi-threshold, error-rate-threshold)", "[\"key=value,...\"]" }, { "signal-get", 0, 0, G_OPTION_ARG_NONE, &get_flag, "Get all signal quality information", NULL }, { NULL } }; GOptionGroup * mmcli_modem_signal_get_option_group (void) { GOptionGroup *group; group = g_option_group_new ("signal", "Signal options:", "Show Signal options", NULL, NULL); g_option_group_add_entries (group, entries); return group; } gboolean mmcli_modem_signal_options_enabled (void) { static guint n_actions = 0; static gboolean checked = FALSE; if (checked) return !!n_actions; n_actions = (!!setup_str + !!setup_thresholds_str + get_flag); if (n_actions > 1) { g_printerr ("error: too many Signal actions requested\n"); exit (EXIT_FAILURE); } if (get_flag) mmcli_force_sync_operation (); checked = TRUE; return !!n_actions; } static void context_free (void) { if (!ctx) return; if (ctx->cancellable) g_object_unref (ctx->cancellable); if (ctx->modem_signal) g_object_unref (ctx->modem_signal); if (ctx->object) g_object_unref (ctx->object); if (ctx->manager) g_object_unref (ctx->manager); g_free (ctx); } static void ensure_modem_signal (void) { if (!ctx->modem_signal) { g_printerr ("error: modem has no extended signal capabilities\n"); exit (EXIT_FAILURE); } /* Success */ } void mmcli_modem_signal_shutdown (void) { context_free (); } static void print_signal_info (void) { MMSignal *signal; gdouble value; gchar *refresh_rate; gchar *rssi_threshold; gchar *error_rate_threshold; gchar *cdma1x_rssi = NULL; gchar *cdma1x_ecio = NULL; gchar *cdma1x_error_rate = NULL; gchar *evdo_rssi = NULL; gchar *evdo_ecio = NULL; gchar *evdo_sinr = NULL; gchar *evdo_io = NULL; gchar *evdo_error_rate = NULL; gchar *gsm_rssi = NULL; gchar *gsm_error_rate = NULL; gchar *umts_rssi = NULL; gchar *umts_rscp = NULL; gchar *umts_ecio = NULL; gchar *umts_error_rate = NULL; gchar *lte_rssi = NULL; gchar *lte_rsrp = NULL; gchar *lte_rsrq = NULL; gchar *lte_snr = NULL; gchar *lte_error_rate = NULL; gchar *nr5g_rsrp = NULL; gchar *nr5g_rsrq = NULL; gchar *nr5g_snr = NULL; gchar *nr5g_error_rate = NULL; refresh_rate = g_strdup_printf ("%u", mm_modem_signal_get_rate (ctx->modem_signal)); rssi_threshold = g_strdup_printf ("%u", mm_modem_signal_get_rssi_threshold (ctx->modem_signal)); error_rate_threshold = g_strdup_printf ("%s", mm_modem_signal_get_error_rate_threshold (ctx->modem_signal) ? "yes" : "no"); signal = mm_modem_signal_peek_cdma (ctx->modem_signal); if (signal) { if ((value = mm_signal_get_rssi (signal)) != MM_SIGNAL_UNKNOWN) cdma1x_rssi = g_strdup_printf ("%.2lf", value); if ((value = mm_signal_get_ecio (signal)) != MM_SIGNAL_UNKNOWN) cdma1x_ecio = g_strdup_printf ("%.2lf", value); if ((value = mm_signal_get_error_rate (signal)) != MM_SIGNAL_UNKNOWN) cdma1x_error_rate = g_strdup_printf ("%.2lf", value); } signal = mm_modem_signal_peek_evdo (ctx->modem_signal); if (signal) { if ((value = mm_signal_get_rssi (signal)) != MM_SIGNAL_UNKNOWN) evdo_rssi = g_strdup_printf ("%.2lf", value); if ((value = mm_signal_get_ecio (signal)) != MM_SIGNAL_UNKNOWN) evdo_ecio = g_strdup_printf ("%.2lf", value); if ((value = mm_signal_get_sinr (signal)) != MM_SIGNAL_UNKNOWN) evdo_sinr = g_strdup_printf ("%.2lf", value); if ((value = mm_signal_get_io (signal)) != MM_SIGNAL_UNKNOWN) evdo_io = g_strdup_printf ("%.2lf", value); if ((value = mm_signal_get_error_rate (signal)) != MM_SIGNAL_UNKNOWN) evdo_error_rate = g_strdup_printf ("%.2lf", value); } signal = mm_modem_signal_peek_gsm (ctx->modem_signal); if (signal) { if ((value = mm_signal_get_rssi (signal)) != MM_SIGNAL_UNKNOWN) gsm_rssi = g_strdup_printf ("%.2lf", value); if ((value = mm_signal_get_error_rate (signal)) != MM_SIGNAL_UNKNOWN) gsm_error_rate = g_strdup_printf ("%.2lf", value); } signal = mm_modem_signal_peek_umts (ctx->modem_signal); if (signal) { if ((value = mm_signal_get_rssi (signal)) != MM_SIGNAL_UNKNOWN) umts_rssi = g_strdup_printf ("%.2lf", value); if ((value = mm_signal_get_rscp (signal)) != MM_SIGNAL_UNKNOWN) umts_rscp = g_strdup_printf ("%.2lf", value); if ((value = mm_signal_get_ecio (signal)) != MM_SIGNAL_UNKNOWN) umts_ecio = g_strdup_printf ("%.2lf", value); if ((value = mm_signal_get_error_rate (signal)) != MM_SIGNAL_UNKNOWN) umts_error_rate = g_strdup_printf ("%.2lf", value); } signal = mm_modem_signal_peek_lte (ctx->modem_signal); if (signal) { if ((value = mm_signal_get_rssi (signal)) != MM_SIGNAL_UNKNOWN) lte_rssi = g_strdup_printf ("%.2lf", value); if ((value = mm_signal_get_rsrq (signal)) != MM_SIGNAL_UNKNOWN) lte_rsrq = g_strdup_printf ("%.2lf", value); if ((value = mm_signal_get_rsrp (signal)) != MM_SIGNAL_UNKNOWN) lte_rsrp = g_strdup_printf ("%.2lf", value); if ((value = mm_signal_get_snr (signal)) != MM_SIGNAL_UNKNOWN) lte_snr = g_strdup_printf ("%.2lf", value); if ((value = mm_signal_get_error_rate (signal)) != MM_SIGNAL_UNKNOWN) lte_error_rate = g_strdup_printf ("%.2lf", value); } signal = mm_modem_signal_peek_nr5g (ctx->modem_signal); if (signal) { if ((value = mm_signal_get_rsrq (signal)) != MM_SIGNAL_UNKNOWN) nr5g_rsrq = g_strdup_printf ("%.2lf", value); if ((value = mm_signal_get_rsrp (signal)) != MM_SIGNAL_UNKNOWN) nr5g_rsrp = g_strdup_printf ("%.2lf", value); if ((value = mm_signal_get_snr (signal)) != MM_SIGNAL_UNKNOWN) nr5g_snr = g_strdup_printf ("%.2lf", value); if ((value = mm_signal_get_error_rate (signal)) != MM_SIGNAL_UNKNOWN) nr5g_error_rate = g_strdup_printf ("%.2lf", value); } mmcli_output_string_take_typed (MMC_F_SIGNAL_REFRESH_RATE, refresh_rate, "seconds"); mmcli_output_string_take_typed (MMC_F_SIGNAL_RSSI_THRESHOLD, rssi_threshold, "dBm"); mmcli_output_string_take (MMC_F_SIGNAL_ERROR_RATE_THRESHOLD, error_rate_threshold); mmcli_output_string_take_typed (MMC_F_SIGNAL_CDMA1X_RSSI, cdma1x_rssi, "dBm"); mmcli_output_string_take_typed (MMC_F_SIGNAL_CDMA1X_ECIO, cdma1x_ecio, "dBm"); mmcli_output_string_take_typed (MMC_F_SIGNAL_CDMA1X_ERROR_RATE, cdma1x_error_rate, "%%"); mmcli_output_string_take_typed (MMC_F_SIGNAL_EVDO_RSSI, evdo_rssi, "dBm"); mmcli_output_string_take_typed (MMC_F_SIGNAL_EVDO_ECIO, evdo_ecio, "dB"); mmcli_output_string_take_typed (MMC_F_SIGNAL_EVDO_SINR, evdo_sinr, "dB"); mmcli_output_string_take_typed (MMC_F_SIGNAL_EVDO_IO, evdo_io, "dBm"); mmcli_output_string_take_typed (MMC_F_SIGNAL_EVDO_ERROR_RATE, evdo_error_rate, "%%"); mmcli_output_string_take_typed (MMC_F_SIGNAL_GSM_RSSI, gsm_rssi, "dBm"); mmcli_output_string_take_typed (MMC_F_SIGNAL_GSM_ERROR_RATE, gsm_error_rate, "%%"); mmcli_output_string_take_typed (MMC_F_SIGNAL_UMTS_RSSI, umts_rssi, "dBm"); mmcli_output_string_take_typed (MMC_F_SIGNAL_UMTS_RSCP, umts_rscp, "dBm"); mmcli_output_string_take_typed (MMC_F_SIGNAL_UMTS_ECIO, umts_ecio, "dB"); mmcli_output_string_take_typed (MMC_F_SIGNAL_UMTS_ERROR_RATE, umts_error_rate, "%%"); mmcli_output_string_take_typed (MMC_F_SIGNAL_LTE_RSSI, lte_rssi, "dBm"); mmcli_output_string_take_typed (MMC_F_SIGNAL_LTE_RSRQ, lte_rsrq, "dB"); mmcli_output_string_take_typed (MMC_F_SIGNAL_LTE_RSRP, lte_rsrp, "dBm"); mmcli_output_string_take_typed (MMC_F_SIGNAL_LTE_SNR, lte_snr, "dB"); mmcli_output_string_take_typed (MMC_F_SIGNAL_LTE_ERROR_RATE, lte_error_rate, "%%"); mmcli_output_string_take_typed (MMC_F_SIGNAL_5G_RSRQ, nr5g_rsrq, "dB"); mmcli_output_string_take_typed (MMC_F_SIGNAL_5G_RSRP, nr5g_rsrp, "dBm"); mmcli_output_string_take_typed (MMC_F_SIGNAL_5G_SNR, nr5g_snr, "dB"); mmcli_output_string_take_typed (MMC_F_SIGNAL_5G_ERROR_RATE, nr5g_error_rate, "%%"); mmcli_output_dump (); } static void setup_thresholds_process_reply (gboolean result, const GError *error) { if (!result) { g_printerr ("error: couldn't setup signal quality information thresholds: '%s'\n", error ? error->message : "unknown error"); exit (EXIT_FAILURE); } g_print ("Successfully setup signal quality information thresholds\n"); } static void setup_thresholds_ready (MMModemSignal *modem, GAsyncResult *result) { gboolean res; GError *error = NULL; res = mm_modem_signal_setup_thresholds_finish (modem, result, &error); setup_thresholds_process_reply (res, error); mmcli_async_operation_done (); } static void setup_process_reply (gboolean result, const GError *error) { if (!result) { g_printerr ("error: couldn't setup signal quality information polling: '%s'\n", error ? error->message : "unknown error"); exit (EXIT_FAILURE); } g_print ("Successfully setup signal quality information polling\n"); } static void setup_ready (MMModemSignal *modem, GAsyncResult *result) { gboolean res; GError *error = NULL; res = mm_modem_signal_setup_finish (modem, result, &error); setup_process_reply (res, error); mmcli_async_operation_done (); } static void get_modem_ready (GObject *source, GAsyncResult *result) { ctx->object = mmcli_get_modem_finish (result, &ctx->manager); ctx->modem_signal = mm_object_get_modem_signal (ctx->object); /* Setup operation timeout */ if (ctx->modem_signal) mmcli_force_operation_timeout (G_DBUS_PROXY (ctx->modem_signal)); ensure_modem_signal (); if (get_flag) g_assert_not_reached (); /* Request to setup? */ if (setup_str) { guint rate; if (!mm_get_uint_from_str (setup_str, &rate)) { g_printerr ("error: invalid rate value '%s'\n", setup_str); exit (EXIT_FAILURE); } g_debug ("Asynchronously setting up extended signal quality information retrieval..."); mm_modem_signal_setup (ctx->modem_signal, rate, ctx->cancellable, (GAsyncReadyCallback)setup_ready, NULL); return; } /* Request to setup threshold? */ if (setup_thresholds_str) { g_autoptr(MMSignalThresholdProperties) properties = NULL; g_autoptr(GError) error = NULL; properties = mm_signal_threshold_properties_new_from_string (setup_thresholds_str, &error); if (!properties) { g_printerr ("error: failed to parse properties string: '%s'\n", error->message); exit (EXIT_FAILURE); } g_debug ("Asynchronously setting up threshold values..."); mm_modem_signal_setup_thresholds (ctx->modem_signal, properties, ctx->cancellable, (GAsyncReadyCallback)setup_thresholds_ready, NULL); return; } g_warn_if_reached (); } void mmcli_modem_signal_run_asynchronous (GDBusConnection *connection, GCancellable *cancellable) { /* Initialize context */ ctx = g_new0 (Context, 1); if (cancellable) ctx->cancellable = g_object_ref (cancellable); /* Get proper modem */ mmcli_get_modem (connection, mmcli_get_common_modem_string (), cancellable, (GAsyncReadyCallback)get_modem_ready, NULL); } void mmcli_modem_signal_run_synchronous (GDBusConnection *connection) { GError *error = NULL; /* Initialize context */ ctx = g_new0 (Context, 1); ctx->object = mmcli_get_modem_sync (connection, mmcli_get_common_modem_string (), &ctx->manager); ctx->modem_signal = mm_object_get_modem_signal (ctx->object); /* Setup operation timeout */ if (ctx->modem_signal) mmcli_force_operation_timeout (G_DBUS_PROXY (ctx->modem_signal)); ensure_modem_signal (); /* Request to get signal info? */ if (get_flag) { print_signal_info (); return; } /* Request to set rate? */ if (setup_str) { guint rate; gboolean result; if (!mm_get_uint_from_str (setup_str, &rate)) { g_printerr ("error: invalid rate value '%s'\n", setup_str); exit (EXIT_FAILURE); } g_debug ("Synchronously setting up extended signal quality information retrieval..."); result = mm_modem_signal_setup_sync (ctx->modem_signal, rate, NULL, &error); setup_process_reply (result, error); return; } /* Request to setup threshold? */ if (setup_thresholds_str) { g_autoptr(MMSignalThresholdProperties) properties = NULL; gboolean result; properties = mm_signal_threshold_properties_new_from_string (setup_thresholds_str, &error); if (!properties) { g_printerr ("error: failed to parse properties string: '%s'\n", error->message); exit (EXIT_FAILURE); } g_debug ("Asynchronously setting up threshold values..."); result = mm_modem_signal_setup_thresholds_sync (ctx->modem_signal, properties, NULL, &error); setup_thresholds_process_reply (result, error); return; } g_warn_if_reached (); } ModemManager-1.23.4-dev/cli/mmcli-modem-simple.c000066400000000000000000000176641456466623000213450ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * mmcli -- Control modem status & access information from the command line * * 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, see . * * Copyright (C) 2011 Aleksander Morgado */ #include "config.h" #include #include #include #include #include #include #define _LIBMM_INSIDE_MMCLI #include #include "mmcli.h" #include "mmcli-common.h" /* Context */ typedef struct { MMManager *manager; GCancellable *cancellable; MMObject *object; MMModemSimple *modem_simple; } Context; static Context *ctx; /* Options */ static gchar *connect_str; static gboolean disconnect_flag; static GOptionEntry entries[] = { { "simple-connect", 0, 0, G_OPTION_ARG_STRING, &connect_str, "Run full connection sequence.", "[\"key=value,...\"]" }, { "simple-disconnect", 0, 0, G_OPTION_ARG_NONE, &disconnect_flag, "Disconnect all connected bearers.", NULL }, { NULL } }; GOptionGroup * mmcli_modem_simple_get_option_group (void) { GOptionGroup *group; group = g_option_group_new ("simple", "Simple options:", "Show Simple options", NULL, NULL); g_option_group_add_entries (group, entries); return group; } gboolean mmcli_modem_simple_options_enabled (void) { static guint n_actions = 0; static gboolean checked = FALSE; if (checked) return !!n_actions; n_actions = (!!connect_str + disconnect_flag); if (n_actions > 1) { g_printerr ("error: too many Simple actions requested\n"); exit (EXIT_FAILURE); } /* Simple connection may take really a long time, so we do it asynchronously * always to avoid DBus timeouts */ if (connect_str) mmcli_force_async_operation (); checked = TRUE; return !!n_actions; } static void context_free (void) { if (!ctx) return; if (ctx->cancellable) g_object_unref (ctx->cancellable); if (ctx->modem_simple) g_object_unref (ctx->modem_simple); if (ctx->object) g_object_unref (ctx->object); if (ctx->manager) g_object_unref (ctx->manager); g_free (ctx); } static void ensure_modem_simple (void) { if (!ctx->modem_simple) { g_printerr ("error: modem has no Simple capabilities\n"); exit (EXIT_FAILURE); } /* Success */ } void mmcli_modem_simple_shutdown (void) { context_free (); } static void connect_process_reply (MMBearer *result, const GError *error) { if (!result) { g_printerr ("error: couldn't connect the modem: '%s'\n", error ? error->message : "unknown error"); exit (EXIT_FAILURE); } g_print ("successfully connected the modem\n"); g_object_unref (result); } static void connect_ready (MMModemSimple *modem_simple, GAsyncResult *result, gpointer nothing) { MMBearer *operation_result; GError *error = NULL; operation_result = mm_modem_simple_connect_finish (modem_simple, result, &error); connect_process_reply (operation_result, error); mmcli_async_operation_done (); } static void disconnect_process_reply (gboolean result, const GError *error) { if (!result) { g_printerr ("error: couldn't disconnect all bearers in the modem: '%s'\n", error ? error->message : "unknown error"); exit (EXIT_FAILURE); } g_print ("successfully disconnected all bearers in the modem\n"); } static void disconnect_ready (MMModemSimple *modem_simple, GAsyncResult *result, gpointer nothing) { gboolean operation_result; GError *error = NULL; operation_result = mm_modem_simple_disconnect_finish (modem_simple, result, &error); disconnect_process_reply (operation_result, error); mmcli_async_operation_done (); } static void get_modem_ready (GObject *source, GAsyncResult *result, gpointer none) { ctx->object = mmcli_get_modem_finish (result, &ctx->manager); ctx->modem_simple = mm_object_get_modem_simple (ctx->object); /* Setup operation timeout */ if (ctx->modem_simple) mmcli_force_operation_timeout (G_DBUS_PROXY (ctx->modem_simple)); ensure_modem_simple (); /* Request to connect the modem? */ if (connect_str) { GError *error = NULL; MMSimpleConnectProperties *properties; g_debug ("Asynchronously connecting the modem..."); properties = mm_simple_connect_properties_new_from_string (connect_str, &error); if (!properties) { g_printerr ("Error parsing connect string: '%s'\n", error->message); exit (EXIT_FAILURE); } mm_modem_simple_connect (ctx->modem_simple, properties, ctx->cancellable, (GAsyncReadyCallback)connect_ready, NULL); g_object_unref (properties); return; } /* Request to disconnect all bearers in the modem? */ if (disconnect_flag) { g_debug ("Asynchronously disconnecting all bearers in the modem..."); mm_modem_simple_disconnect (ctx->modem_simple, NULL, ctx->cancellable, (GAsyncReadyCallback)disconnect_ready, NULL); return; } g_warn_if_reached (); } void mmcli_modem_simple_run_asynchronous (GDBusConnection *connection, GCancellable *cancellable) { /* Initialize context */ ctx = g_new0 (Context, 1); if (cancellable) ctx->cancellable = g_object_ref (cancellable); /* Get proper modem */ mmcli_get_modem (connection, mmcli_get_common_modem_string (), cancellable, (GAsyncReadyCallback)get_modem_ready, NULL); } void mmcli_modem_simple_run_synchronous (GDBusConnection *connection) { GError *error = NULL; /* Initialize context */ ctx = g_new0 (Context, 1); ctx->object = mmcli_get_modem_sync (connection, mmcli_get_common_modem_string (), &ctx->manager); ctx->modem_simple = mm_object_get_modem_simple (ctx->object); /* Setup operation timeout */ if (ctx->modem_simple) mmcli_force_operation_timeout (G_DBUS_PROXY (ctx->modem_simple)); ensure_modem_simple (); if (connect_str) g_assert_not_reached (); /* Request to disconnect all bearers in the modem? */ if (disconnect_flag) { gboolean result; g_debug ("Synchronously disconnecting all bearers in the modem..."); result = mm_modem_simple_disconnect_sync (ctx->modem_simple, NULL, NULL, &error); disconnect_process_reply (result, error); return; } g_warn_if_reached (); } ModemManager-1.23.4-dev/cli/mmcli-modem-time.c000066400000000000000000000163661456466623000210100ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * mmcli -- Control modem status & access information from the command line * * 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, see . * * Copyright (C) 2012 Google, Inc. */ #include "config.h" #include #include #include #include #include #include #define _LIBMM_INSIDE_MMCLI #include #include "mmcli.h" #include "mmcli-common.h" #include "mmcli-output.h" /* Context */ typedef struct { MMManager *manager; GCancellable *cancellable; MMObject *object; MMModemTime *modem_time; } Context; static Context *ctx; /* Options */ static gboolean time_flag; static GOptionEntry entries[] = { { "time", 0, 0, G_OPTION_ARG_NONE, &time_flag, "Get current network time", NULL }, { NULL } }; GOptionGroup * mmcli_modem_time_get_option_group (void) { GOptionGroup *group; group = g_option_group_new ("time", "Time options:", "Show Time options", NULL, NULL); g_option_group_add_entries (group, entries); return group; } gboolean mmcli_modem_time_options_enabled (void) { static guint n_actions = 0; static gboolean checked = FALSE; if (checked) return !!n_actions; n_actions = (time_flag); if (n_actions > 1) { g_printerr ("error: too many Time actions requested\n"); exit (EXIT_FAILURE); } checked = TRUE; return !!n_actions; } static void context_free (void) { if (!ctx) return; if (ctx->cancellable) g_object_unref (ctx->cancellable); if (ctx->modem_time) g_object_unref (ctx->modem_time); if (ctx->object) g_object_unref (ctx->object); if (ctx->manager) g_object_unref (ctx->manager); g_free (ctx); } static void ensure_modem_time (void) { if (mm_modem_get_state (mm_object_peek_modem (ctx->object)) < MM_MODEM_STATE_ENABLED) { g_printerr ("error: modem not enabled yet\n"); exit (EXIT_FAILURE); } if (!ctx->modem_time) { g_printerr ("error: modem has no time capabilities\n"); exit (EXIT_FAILURE); } /* Success */ } void mmcli_modem_time_shutdown (void) { context_free (); } static void get_network_time_process_reply (gchar *time_string, MMNetworkTimezone *timezone, const GError *error) { gchar *offset = NULL; gchar *dst_offset = NULL; gchar *leap_seconds = NULL; if (error) { g_printerr ("error: couldn't get current network time: '%s'\n", error->message); exit (EXIT_FAILURE); } if (timezone) { if (mm_network_timezone_get_offset (timezone) != MM_NETWORK_TIMEZONE_OFFSET_UNKNOWN) offset = g_strdup_printf ("%" G_GINT32_FORMAT, mm_network_timezone_get_offset (timezone)); if (mm_network_timezone_get_dst_offset (timezone) != MM_NETWORK_TIMEZONE_OFFSET_UNKNOWN) dst_offset = g_strdup_printf ("%" G_GINT32_FORMAT, mm_network_timezone_get_dst_offset (timezone)); if (mm_network_timezone_get_leap_seconds (timezone) != MM_NETWORK_TIMEZONE_LEAP_SECONDS_UNKNOWN) leap_seconds = g_strdup_printf ("%" G_GINT32_FORMAT, mm_network_timezone_get_leap_seconds (timezone)); } mmcli_output_string_take (MMC_F_TIME_CURRENT, time_string); mmcli_output_string_take (MMC_F_TIMEZONE_CURRENT, offset); mmcli_output_string_take (MMC_F_TIMEZONE_DST_OFFSET, dst_offset); mmcli_output_string_take (MMC_F_TIMEZONE_LEAP_SECONDS, leap_seconds); mmcli_output_dump (); } static void get_network_time_ready (MMModemTime *modem_time, GAsyncResult *result) { MMNetworkTimezone *timezone; gchar *time_string; GError *error = NULL; time_string = mm_modem_time_get_network_time_finish (modem_time, result, &error); timezone = mm_modem_time_get_network_timezone (modem_time); get_network_time_process_reply (time_string, timezone, error); mmcli_async_operation_done (); } static void get_modem_ready (GObject *source, GAsyncResult *result, gpointer none) { ctx->object = mmcli_get_modem_finish (result, &ctx->manager); ctx->modem_time = mm_object_get_modem_time (ctx->object); /* Setup operation timeout */ if (ctx->modem_time) mmcli_force_operation_timeout (G_DBUS_PROXY (ctx->modem_time)); ensure_modem_time (); /* Request to get network time from the modem? */ if (time_flag) { g_debug ("Asynchronously getting network time from the modem..."); mm_modem_time_get_network_time (ctx->modem_time, ctx->cancellable, (GAsyncReadyCallback)get_network_time_ready, NULL); return; } g_warn_if_reached (); } void mmcli_modem_time_run_asynchronous (GDBusConnection *connection, GCancellable *cancellable) { /* Initialize context */ ctx = g_new0 (Context, 1); if (cancellable) ctx->cancellable = g_object_ref (cancellable); /* Get proper modem */ mmcli_get_modem (connection, mmcli_get_common_modem_string (), cancellable, (GAsyncReadyCallback)get_modem_ready, NULL); } void mmcli_modem_time_run_synchronous (GDBusConnection *connection) { GError *error = NULL; /* Initialize context */ ctx = g_new0 (Context, 1); ctx->object = mmcli_get_modem_sync (connection, mmcli_get_common_modem_string (), &ctx->manager); ctx->modem_time = mm_object_get_modem_time (ctx->object); /* Setup operation timeout */ if (ctx->modem_time) mmcli_force_operation_timeout (G_DBUS_PROXY (ctx->modem_time)); ensure_modem_time (); /* Request to get network time from the modem? */ if (time_flag) { gchar *time_string; MMNetworkTimezone *timezone; g_debug ("Synchronously getting network time from the modem..."); time_string = mm_modem_time_get_network_time_sync (ctx->modem_time, NULL, &error); timezone = mm_modem_time_get_network_timezone (ctx->modem_time); get_network_time_process_reply (time_string, timezone, error); return; } g_warn_if_reached (); } ModemManager-1.23.4-dev/cli/mmcli-modem-voice.c000066400000000000000000000547571456466623000211650ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * mmcli -- Control modem status & access information from the command line * * 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, see . * * Copyright (C) 2012 Google, Inc. * Copyright (C) 2015 Riccardo Vangelisti */ #include "config.h" #include #include #include #include #include #include #define _LIBMM_INSIDE_MMCLI #include #include "mmcli.h" #include "mmcli-common.h" #include "mmcli-output.h" /* Context */ typedef struct { GDBusConnection *connection; MMManager *manager; GCancellable *cancellable; MMObject *object; MMModemVoice *modem_voice; } Context; static Context *ctx; /* Options */ static gboolean status_flag; static gboolean list_flag; static gchar *create_str; static gchar *delete_str; static gboolean hold_and_accept_flag; static gboolean hangup_and_accept_flag; static gboolean hangup_all_flag; static gboolean transfer_flag; static gboolean call_waiting_enable_flag; static gboolean call_waiting_disable_flag; static gboolean call_waiting_query_flag; static GOptionEntry entries[] = { { "voice-status", 0, 0, G_OPTION_ARG_NONE, &status_flag, "Show status of voice support.", NULL }, { "voice-list-calls", 0, 0, G_OPTION_ARG_NONE, &list_flag, "List calls available in a given modem", NULL }, { "voice-create-call", 0, 0, G_OPTION_ARG_STRING, &create_str, "Create a new call in a given modem", "[\"key=value,...\"]" }, { "voice-delete-call", 0, 0, G_OPTION_ARG_STRING, &delete_str, "Delete a call from a given modem", "[PATH|INDEX]" }, { "voice-hold-and-accept", 0, 0, G_OPTION_ARG_NONE, &hold_and_accept_flag, "Places all active calls in hold and accepts the next waiting or held call", NULL }, { "voice-hangup-and-accept", 0, 0, G_OPTION_ARG_NONE, &hangup_and_accept_flag, "Hangs up all active calls and accepts the next waiting or held call", NULL }, { "voice-hangup-all", 0, 0, G_OPTION_ARG_NONE, &hangup_all_flag, "Hangs up all ongoing (active, waiting, held) calls", NULL }, { "voice-transfer", 0, 0, G_OPTION_ARG_NONE, &transfer_flag, "Joins active and held calls and disconnects from them", NULL }, { "voice-enable-call-waiting", 0, 0, G_OPTION_ARG_NONE, &call_waiting_enable_flag, "Enables the call waiting network service", NULL }, { "voice-disable-call-waiting", 0, 0, G_OPTION_ARG_NONE, &call_waiting_disable_flag, "Disables the call waiting network service", NULL }, { "voice-query-call-waiting", 0, 0, G_OPTION_ARG_NONE, &call_waiting_query_flag, "Queries the status of the call waiting network service", NULL }, { NULL } }; GOptionGroup * mmcli_modem_voice_get_option_group (void) { GOptionGroup *group; group = g_option_group_new ("voice", "Voice options:", "Show Voice options", NULL, NULL); g_option_group_add_entries (group, entries); return group; } gboolean mmcli_modem_voice_options_enabled (void) { static guint n_actions = 0; static gboolean checked = FALSE; if (checked) return !!n_actions; n_actions = (status_flag + list_flag + !!create_str + !!delete_str + hold_and_accept_flag + hangup_and_accept_flag + hangup_all_flag + transfer_flag + call_waiting_enable_flag + call_waiting_disable_flag + call_waiting_query_flag); if (n_actions > 1) { g_printerr ("error: too many Voice actions requested\n"); exit (EXIT_FAILURE); } if (status_flag) mmcli_force_sync_operation (); checked = TRUE; return !!n_actions; } static void context_free (void) { if (!ctx) return; if (ctx->cancellable) g_object_unref (ctx->cancellable); if (ctx->modem_voice) g_object_unref (ctx->modem_voice); if (ctx->object) g_object_unref (ctx->object); if (ctx->manager) g_object_unref (ctx->manager); if (ctx->connection) g_object_unref (ctx->connection); g_free (ctx); } static void ensure_modem_voice (void) { if (!ctx->modem_voice) { g_printerr ("error: modem has no voice capabilities\n"); exit (EXIT_FAILURE); } /* Success */ } void mmcli_modem_voice_shutdown (void) { context_free (); } static MMCallProperties * build_call_properties_from_input (const gchar *properties_string) { GError *error = NULL; MMCallProperties *properties; properties = mm_call_properties_new_from_string (properties_string, &error); if (!properties) { g_printerr ("error: cannot parse properties string: '%s'\n", error->message); exit (EXIT_FAILURE); } return properties; } static void print_voice_status (void) { mmcli_output_string (MMC_F_VOICE_EMERGENCY_ONLY, mm_modem_voice_get_emergency_only (ctx->modem_voice) ? "yes" : "no"); mmcli_output_dump (); } static void output_call_info (MMCall *call) { gchar *extra; extra = g_strdup_printf ("%s (%s)", mm_call_direction_get_string (mm_call_get_direction (call)), mm_call_state_get_string (mm_call_get_state (call))); mmcli_output_listitem (MMC_F_CALL_LIST_DBUS_PATH, " ", mm_call_get_path (call), extra); g_free (extra); } static void call_waiting_query_process_reply (const GError *error, gboolean status) { if (error) { g_printerr ("error: couldn't query call waiting network service status: '%s'\n", error->message); exit (EXIT_FAILURE); } g_print ("call waiting service is %s\n", status ? "enabled" : "disabled"); } static void call_waiting_query_ready (MMModemVoice *modem, GAsyncResult *result, gpointer nothing) { GError *error = NULL; gboolean status = FALSE; mm_modem_voice_call_waiting_query_finish (modem, result, &status, &error); call_waiting_query_process_reply (error, status); mmcli_async_operation_done (); } static void call_waiting_setup_process_reply (const GError *error) { if (error) { g_printerr ("error: couldn't setup call waiting network service: '%s'\n", error->message); exit (EXIT_FAILURE); } g_print ("operation successful\n"); } static void call_waiting_setup_ready (MMModemVoice *modem, GAsyncResult *result, gpointer nothing) { GError *error = NULL; mm_modem_voice_call_waiting_setup_finish (modem, result, &error); call_waiting_setup_process_reply (error); mmcli_async_operation_done (); } static void transfer_process_reply (const GError *error) { if (error) { g_printerr ("error: couldn't hangup all: '%s'\n", error->message); exit (EXIT_FAILURE); } g_print ("operation successful\n"); } static void transfer_ready (MMModemVoice *modem, GAsyncResult *result, gpointer nothing) { GError *error = NULL; mm_modem_voice_transfer_finish (modem, result, &error); transfer_process_reply (error); mmcli_async_operation_done (); } static void hangup_all_process_reply (const GError *error) { if (error) { g_printerr ("error: couldn't hangup all: '%s'\n", error->message); exit (EXIT_FAILURE); } g_print ("operation successful\n"); } static void hangup_all_ready (MMModemVoice *modem, GAsyncResult *result, gpointer nothing) { GError *error = NULL; mm_modem_voice_hangup_all_finish (modem, result, &error); hangup_all_process_reply (error); mmcli_async_operation_done (); } static void hangup_and_accept_process_reply (const GError *error) { if (error) { g_printerr ("error: couldn't hangup and accept: '%s'\n", error->message); exit (EXIT_FAILURE); } g_print ("operation successful\n"); } static void hangup_and_accept_ready (MMModemVoice *modem, GAsyncResult *result, gpointer nothing) { GError *error = NULL; mm_modem_voice_hangup_and_accept_finish (modem, result, &error); hangup_and_accept_process_reply (error); mmcli_async_operation_done (); } static void hold_and_accept_process_reply (const GError *error) { if (error) { g_printerr ("error: couldn't hold and accept: '%s'\n", error->message); exit (EXIT_FAILURE); } g_print ("operation successful\n"); } static void hold_and_accept_ready (MMModemVoice *modem, GAsyncResult *result, gpointer nothing) { GError *error = NULL; mm_modem_voice_hold_and_accept_finish (modem, result, &error); hold_and_accept_process_reply (error); mmcli_async_operation_done (); } static void list_process_reply (GList *result, const GError *error) { GList *l; if (error) { g_printerr ("error: couldn't list call: '%s'\n", error->message); exit (EXIT_FAILURE); } for (l = result; l; l = g_list_next (l)) output_call_info (MM_CALL (l->data)); mmcli_output_list_dump (MMC_F_CALL_LIST_DBUS_PATH); } static void list_ready (MMModemVoice *modem, GAsyncResult *result, gpointer nothing) { GList *operation_result; GError *error = NULL; operation_result = mm_modem_voice_list_calls_finish (modem, result, &error); list_process_reply (operation_result, error); mmcli_async_operation_done (); } static void create_process_reply (MMCall *call, const GError *error) { if (!call) { g_printerr ("error: couldn't create new call: '%s'\n", error ? error->message : "unknown error"); exit (EXIT_FAILURE); } g_print ("Successfully created new call: %s\n", mm_call_get_path (call)); g_object_unref (call); } static void create_ready (MMModemVoice *modem, GAsyncResult *result, gpointer nothing) { MMCall *call; GError *error = NULL; call = mm_modem_voice_create_call_finish (modem, result, &error); create_process_reply (call, error); mmcli_async_operation_done (); } static void delete_process_reply (gboolean result, const GError *error) { if (!result) { g_printerr ("error: couldn't delete call: '%s'\n", error ? error->message : "unknown error"); exit (EXIT_FAILURE); } g_print ("successfully deleted call from modem\n"); } static void delete_ready (MMModemVoice *modem, GAsyncResult *result, gpointer nothing) { gboolean operation_result; GError *error = NULL; operation_result = mm_modem_voice_delete_call_finish (modem, result, &error); delete_process_reply (operation_result, error); mmcli_async_operation_done (); } static void get_call_to_delete_ready (GDBusConnection *connection, GAsyncResult *res) { MMCall *call; MMObject *obj = NULL; call = mmcli_get_call_finish (res, NULL, &obj); if (!g_str_equal (mm_object_get_path (obj), mm_modem_voice_get_path (ctx->modem_voice))) { g_printerr ("error: call '%s' not owned by modem '%s'", mm_call_get_path (call), mm_modem_voice_get_path (ctx->modem_voice)); exit (EXIT_FAILURE); } mm_modem_voice_delete_call (ctx->modem_voice, mm_call_get_path (call), ctx->cancellable, (GAsyncReadyCallback)delete_ready, NULL); g_object_unref (call); g_object_unref (obj); } static void get_modem_ready (GObject *source, GAsyncResult *result, gpointer none) { ctx->object = mmcli_get_modem_finish (result, &ctx->manager); ctx->modem_voice = mm_object_get_modem_voice (ctx->object); /* Setup operation timeout */ if (ctx->modem_voice) mmcli_force_operation_timeout (G_DBUS_PROXY (ctx->modem_voice)); ensure_modem_voice (); if (status_flag) g_assert_not_reached (); /* Request to list call? */ if (list_flag) { g_debug ("Asynchronously listing calls in modem..."); mm_modem_voice_list_calls (ctx->modem_voice, ctx->cancellable, (GAsyncReadyCallback)list_ready, NULL); return; } /* Request to create a new call? */ if (create_str) { MMCallProperties *properties; properties = build_call_properties_from_input (create_str); g_debug ("Asynchronously creating new call in modem..."); mm_modem_voice_create_call (ctx->modem_voice, properties, ctx->cancellable, (GAsyncReadyCallback)create_ready, NULL); g_object_unref (properties); return; } /* Request to delete a given call? */ if (delete_str) { mmcli_get_call (ctx->connection, delete_str, ctx->cancellable, (GAsyncReadyCallback)get_call_to_delete_ready, NULL); return; } /* Request to hold and accept? */ if (hold_and_accept_flag) { g_debug ("Asynchronously holding and accepting next call..."); mm_modem_voice_hold_and_accept (ctx->modem_voice, ctx->cancellable, (GAsyncReadyCallback)hold_and_accept_ready, NULL); return; } /* Request to hangup and accept? */ if (hangup_and_accept_flag) { g_debug ("Asynchronously hanging up and accepting next call..."); mm_modem_voice_hangup_and_accept (ctx->modem_voice, ctx->cancellable, (GAsyncReadyCallback)hangup_and_accept_ready, NULL); return; } /* Request to hangup all? */ if (hangup_all_flag) { g_debug ("Asynchronously hanging up all calls..."); mm_modem_voice_hangup_all (ctx->modem_voice, ctx->cancellable, (GAsyncReadyCallback)hangup_all_ready, NULL); return; } /* Request to transfer? */ if (transfer_flag) { g_debug ("Asynchronously transferring calls..."); mm_modem_voice_transfer (ctx->modem_voice, ctx->cancellable, (GAsyncReadyCallback)transfer_ready, NULL); return; } /* Request to enable call waiting? */ if (call_waiting_enable_flag) { g_debug ("Asynchronously enabling call waiting..."); mm_modem_voice_call_waiting_setup (ctx->modem_voice, TRUE, ctx->cancellable, (GAsyncReadyCallback)call_waiting_setup_ready, NULL); return; } /* Request to disable call waiting? */ if (call_waiting_disable_flag) { g_debug ("Asynchronously enabling call waiting..."); mm_modem_voice_call_waiting_setup (ctx->modem_voice, FALSE, ctx->cancellable, (GAsyncReadyCallback)call_waiting_setup_ready, NULL); return; } /* Request to query call waiting? */ if (call_waiting_query_flag) { g_debug ("Asynchronously querying call waiting status..."); mm_modem_voice_call_waiting_query (ctx->modem_voice, ctx->cancellable, (GAsyncReadyCallback)call_waiting_query_ready, NULL); return; } g_warn_if_reached (); } void mmcli_modem_voice_run_asynchronous (GDBusConnection *connection, GCancellable *cancellable) { /* Initialize context */ ctx = g_new0 (Context, 1); if (cancellable) ctx->cancellable = g_object_ref (cancellable); ctx->connection = g_object_ref (connection); /* Get proper modem */ mmcli_get_modem (connection, mmcli_get_common_modem_string (), cancellable, (GAsyncReadyCallback)get_modem_ready, NULL); } void mmcli_modem_voice_run_synchronous (GDBusConnection *connection) { GError *error = NULL; /* Initialize context */ ctx = g_new0 (Context, 1); ctx->object = mmcli_get_modem_sync (connection, mmcli_get_common_modem_string (), &ctx->manager); ctx->modem_voice = mm_object_get_modem_voice (ctx->object); /* Setup operation timeout */ if (ctx->modem_voice) mmcli_force_operation_timeout (G_DBUS_PROXY (ctx->modem_voice)); ensure_modem_voice (); /* Request to get voice status? */ if (status_flag) { g_debug ("Printing voice status..."); print_voice_status (); return; } /* Request to list the call? */ if (list_flag) { GList *result; g_debug ("Synchronously listing call..."); result = mm_modem_voice_list_calls_sync (ctx->modem_voice, NULL, &error); list_process_reply (result, error); return; } /* Request to create a new call? */ if (create_str) { MMCall *call; MMCallProperties *properties; properties = build_call_properties_from_input (create_str); g_debug ("Synchronously creating new call in modem..."); call = mm_modem_voice_create_call_sync (ctx->modem_voice, properties, NULL, &error); g_object_unref (properties); create_process_reply (call, error); return; } /* Request to delete a given call? */ if (delete_str) { gboolean result; MMCall *call; MMObject *obj = NULL; call = mmcli_get_call_sync (connection, delete_str, NULL, &obj); if (!g_str_equal (mm_object_get_path (obj), mm_modem_voice_get_path (ctx->modem_voice))) { g_printerr ("error: call '%s' not owned by modem '%s'", mm_call_get_path (call), mm_modem_voice_get_path (ctx->modem_voice)); exit (EXIT_FAILURE); } result = mm_modem_voice_delete_call_sync (ctx->modem_voice, mm_call_get_path (call), NULL, &error); g_object_unref (call); g_object_unref (obj); delete_process_reply (result, error); return; } /* Request to hold and accept? */ if (hold_and_accept_flag) { g_debug ("Synchronously holding and accepting call..."); mm_modem_voice_hold_and_accept_sync (ctx->modem_voice, NULL, &error); hold_and_accept_process_reply (error); return; } /* Request to hangup and accept? */ if (hangup_and_accept_flag) { g_debug ("Synchronously hanging up and accepting call..."); mm_modem_voice_hangup_and_accept_sync (ctx->modem_voice, NULL, &error); hangup_and_accept_process_reply (error); return; } /* Request to hangup all? */ if (hangup_all_flag) { g_debug ("Synchronously hanging up all calls..."); mm_modem_voice_hangup_all_sync (ctx->modem_voice, NULL, &error); hangup_all_process_reply (error); return; } /* Request to transfer? */ if (transfer_flag) { g_debug ("Synchronously transferring calls..."); mm_modem_voice_transfer_sync (ctx->modem_voice, NULL, &error); transfer_process_reply (error); return; } /* Request to enable call waiting? */ if (call_waiting_enable_flag) { g_debug ("Synchronously enabling call waiting..."); mm_modem_voice_call_waiting_setup_sync (ctx->modem_voice, TRUE, NULL, &error); call_waiting_setup_process_reply (error); return; } /* Request to disable call waiting? */ if (call_waiting_disable_flag) { g_debug ("Synchronously disabling call waiting..."); mm_modem_voice_call_waiting_setup_sync (ctx->modem_voice, FALSE, NULL, &error); call_waiting_setup_process_reply (error); return; } /* Request to query call waiting? */ if (call_waiting_query_flag) { gboolean status = FALSE; g_debug ("Synchronously querying call waiting status..."); mm_modem_voice_call_waiting_query_sync (ctx->modem_voice, NULL, &status, &error); call_waiting_query_process_reply (error, status); return; } g_warn_if_reached (); } ModemManager-1.23.4-dev/cli/mmcli-modem.c000066400000000000000000001500211456466623000200370ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * mmcli -- Control modem status & access information from the command line * * 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, see . * * Copyright (C) 2011-2018 Aleksander Morgado */ #include "config.h" #include #include #include #include #include #define _LIBMM_INSIDE_MMCLI #include #include "mmcli.h" #include "mmcli-common.h" #include "mmcli-output.h" /* Context */ typedef struct { GDBusConnection *connection; MMManager *manager; GCancellable *cancellable; MMObject *object; MMModem *modem; MMModem3gpp *modem_3gpp; MMModemCdma *modem_cdma; } Context; static Context *ctx; /* Options */ static gboolean info_flag; /* set when no action found */ static gboolean monitor_state_flag; static gboolean enable_flag; static gboolean disable_flag; static gboolean set_power_state_on_flag; static gboolean set_power_state_low_flag; static gboolean set_power_state_off_flag; static gboolean reset_flag; static gchar *factory_reset_str; static gchar *command_str; static gchar *create_bearer_str; static gchar *delete_bearer_str; static gchar *set_current_capabilities_str; static gchar *set_allowed_modes_str; static gchar *set_preferred_mode_str; static gchar *set_current_bands_str; static gint set_primary_sim_slot_int; static gboolean get_cell_info_flag; static gboolean inhibit_flag; static GOptionEntry entries[] = { { "monitor-state", 'w', 0, G_OPTION_ARG_NONE, &monitor_state_flag, "Monitor state of a given modem", NULL }, { "enable", 'e', 0, G_OPTION_ARG_NONE, &enable_flag, "Enable a given modem", NULL }, { "disable", 'd', 0, G_OPTION_ARG_NONE, &disable_flag, "Disable a given modem", NULL }, { "set-power-state-on", 0, 0, G_OPTION_ARG_NONE, &set_power_state_on_flag, "Set full power state in the modem", NULL }, { "set-power-state-low", 0, 0, G_OPTION_ARG_NONE, &set_power_state_low_flag, "Set low power state in the modem", NULL }, { "set-power-state-off", 0, 0, G_OPTION_ARG_NONE, &set_power_state_off_flag, "Power off the modem", NULL }, { "reset", 'r', 0, G_OPTION_ARG_NONE, &reset_flag, "Reset a given modem", NULL }, { "factory-reset", 0, 0, G_OPTION_ARG_STRING, &factory_reset_str, "Reset a given modem to its factory state", "[CODE]" }, { "command", 0, 0, G_OPTION_ARG_STRING, &command_str, "Send an AT command to the modem", "[COMMAND]" }, { "create-bearer", 0, 0, G_OPTION_ARG_STRING, &create_bearer_str, "Create a new packet data bearer in a given modem", "[\"key=value,...\"]" }, { "delete-bearer", 0, 0, G_OPTION_ARG_STRING, &delete_bearer_str, "Delete a data bearer from a given modem", "[PATH|INDEX]" }, { "set-current-capabilities", 0, 0, G_OPTION_ARG_STRING, &set_current_capabilities_str, "Set current modem capabilities.", "[CAPABILITY1|CAPABILITY2...]" }, { "set-allowed-modes", 0, 0, G_OPTION_ARG_STRING, &set_allowed_modes_str, "Set allowed modes in a given modem.", "[MODE1|MODE2...]" }, { "set-preferred-mode", 0, 0, G_OPTION_ARG_STRING, &set_preferred_mode_str, "Set preferred mode in a given modem (Must give allowed modes with --set-allowed-modes)", "[MODE]" }, { "set-current-bands", 0, 0, G_OPTION_ARG_STRING, &set_current_bands_str, "Set bands to be used by a given modem.", "[BAND1|BAND2...]" }, { "set-primary-sim-slot", 0, 0, G_OPTION_ARG_INT, &set_primary_sim_slot_int, "Switch to the selected SIM slot", "[SLOT NUMBER]" }, { "get-cell-info", 0, 0, G_OPTION_ARG_NONE, &get_cell_info_flag, "Get cell info", NULL }, { "inhibit", 0, 0, G_OPTION_ARG_NONE, &inhibit_flag, "Inhibit the modem", NULL }, { NULL } }; GOptionGroup * mmcli_modem_get_option_group (void) { GOptionGroup *group; /* Status options */ group = g_option_group_new ("modem", "Modem options:", "Show modem options", NULL, NULL); g_option_group_add_entries (group, entries); return group; } gboolean mmcli_modem_options_enabled (void) { static guint n_actions = 0; static gboolean checked = FALSE; if (checked) return !!n_actions; n_actions = (monitor_state_flag + enable_flag + disable_flag + set_power_state_on_flag + set_power_state_low_flag + set_power_state_off_flag + reset_flag + !!create_bearer_str + !!delete_bearer_str + !!factory_reset_str + !!command_str + !!set_current_capabilities_str + !!set_allowed_modes_str + !!set_preferred_mode_str + !!set_current_bands_str + (set_primary_sim_slot_int > 0) + get_cell_info_flag + inhibit_flag); if (n_actions == 0 && mmcli_get_common_modem_string ()) { /* default to info */ info_flag = TRUE; n_actions++; } if (set_preferred_mode_str) { if (!set_allowed_modes_str) { g_printerr ("error: setting preferred mode requires list of allowed modes\n"); exit (EXIT_FAILURE); } n_actions--; } if (n_actions > 1) { g_printerr ("error: too many modem actions requested\n"); exit (EXIT_FAILURE); } if (monitor_state_flag || inhibit_flag) mmcli_force_async_operation (); if (info_flag) mmcli_force_sync_operation (); checked = TRUE; return !!n_actions; } static void context_free (void) { if (!ctx) return; if (ctx->cancellable) g_object_unref (ctx->cancellable); if (ctx->modem) g_object_unref (ctx->modem); if (ctx->modem_3gpp) g_object_unref (ctx->modem_3gpp); if (ctx->modem_cdma) g_object_unref (ctx->modem_cdma); if (ctx->object) g_object_unref (ctx->object); if (ctx->manager) g_object_unref (ctx->manager); if (ctx->connection) g_object_unref (ctx->connection); g_free (ctx); } void mmcli_modem_shutdown (void) { context_free (); } static void inhibition_cancelled (GCancellable *cancellable, const gchar *uid) { GError *error = NULL; if (!mm_manager_uninhibit_device_sync (ctx->manager, uid, NULL, &error)) { g_printerr ("error: couldn't uninhibit device: '%s'\n", error ? error->message : "unknown error"); } else g_print ("successfully uninhibited device with uid '%s'\n", uid); mmcli_async_operation_done (); } static void inhibit_device_ready (MMManager *manager, GAsyncResult *result, gchar *uid) { GError *error = NULL; if (!mm_manager_inhibit_device_finish (manager, result, &error)) { g_printerr ("error: couldn't inhibit device: '%s'\n", error ? error->message : "unknown error"); exit (EXIT_FAILURE); } g_print ("successfully inhibited device with uid '%s'\n", uid); g_print ("type Ctrl+C to abort this program and remove the inhibition\n"); g_cancellable_connect (ctx->cancellable, G_CALLBACK (inhibition_cancelled), uid, g_free); } static void cancelled (GCancellable *cancellable) { mmcli_async_operation_done (); } static void print_bearer_short_info (MMBearer *bearer) { g_print ("\t%s\n", mm_bearer_get_path (bearer)); } static void print_modem_info (void) { gchar *supported_capabilities_string; MMModemCapability *capabilities = NULL; guint n_capabilities = 0; gchar *current_capabilities_string; gchar *access_technologies_string; MMModemModeCombination *modes = NULL; guint n_modes = 0; gchar *supported_modes_string; MMModemMode allowed_modes; gchar *allowed_modes_string = NULL; MMModemMode preferred_mode; gchar *preferred_mode_string = NULL; gchar *supported_bands_string; gchar *current_bands_string; gchar *supported_ip_families_string; gchar *unlock_retries_string; MMModemBand *bands = NULL; guint n_bands = 0; MMModemPortInfo *ports = NULL; guint n_ports = 0; gchar *ports_string; MMUnlockRetries *unlock_retries; guint signal_quality = 0; gboolean signal_quality_recent = FALSE; const gchar *sim_path; const gchar **bearer_paths; /* Strings in heap */ mm_modem_get_supported_capabilities (ctx->modem, &capabilities, &n_capabilities); supported_capabilities_string = mm_common_build_capabilities_string (capabilities, n_capabilities); g_free (capabilities); current_capabilities_string = mm_modem_capability_build_string_from_mask ( mm_modem_get_current_capabilities (ctx->modem)); access_technologies_string = mm_modem_access_technology_build_string_from_mask ( mm_modem_get_access_technologies (ctx->modem)); mm_modem_get_supported_modes (ctx->modem, &modes, &n_modes); supported_modes_string = mm_common_build_mode_combinations_string (modes, n_modes); g_free (modes); mm_modem_get_current_bands (ctx->modem, &bands, &n_bands); current_bands_string = mm_common_build_bands_string (bands, n_bands); g_free (bands); mm_modem_get_supported_bands (ctx->modem, &bands, &n_bands); supported_bands_string = mm_common_build_bands_string (bands, n_bands); g_free (bands); mm_modem_get_ports (ctx->modem, &ports, &n_ports); ports_string = mm_common_build_ports_string (ports, n_ports); mm_modem_port_info_array_free (ports, n_ports); if (mm_modem_get_current_modes (ctx->modem, &allowed_modes, &preferred_mode)) { allowed_modes_string = mm_modem_mode_build_string_from_mask (allowed_modes); preferred_mode_string = mm_modem_mode_build_string_from_mask (preferred_mode); } supported_ip_families_string = mm_bearer_ip_family_build_string_from_mask ( mm_modem_get_supported_ip_families (ctx->modem)); unlock_retries = mm_modem_get_unlock_retries (ctx->modem); unlock_retries_string = mm_unlock_retries_build_string (unlock_retries); g_object_unref (unlock_retries); signal_quality = mm_modem_get_signal_quality (ctx->modem, &signal_quality_recent); mmcli_output_string (MMC_F_GENERAL_DBUS_PATH, mm_modem_get_path (ctx->modem)); mmcli_output_string (MMC_F_GENERAL_DEVICE_ID, mm_modem_get_device_identifier (ctx->modem)); mmcli_output_string (MMC_F_HARDWARE_MANUFACTURER, mm_modem_get_manufacturer (ctx->modem)); mmcli_output_string (MMC_F_HARDWARE_MODEL, mm_modem_get_model (ctx->modem)); mmcli_output_string (MMC_F_HARDWARE_REVISION, mm_modem_get_revision (ctx->modem)); mmcli_output_string (MMC_F_HARDWARE_CARRIER_CONF, mm_modem_get_carrier_configuration (ctx->modem)); mmcli_output_string (MMC_F_HARDWARE_CARRIER_CONF_REV, mm_modem_get_carrier_configuration_revision (ctx->modem)); mmcli_output_string (MMC_F_HARDWARE_HW_REVISION, mm_modem_get_hardware_revision (ctx->modem)); mmcli_output_string_multiline (MMC_F_HARDWARE_SUPPORTED_CAPABILITIES, supported_capabilities_string); mmcli_output_string_multiline (MMC_F_HARDWARE_CURRENT_CAPABILITIES, current_capabilities_string); mmcli_output_string (MMC_F_HARDWARE_EQUIPMENT_ID, mm_modem_get_equipment_identifier (ctx->modem)); mmcli_output_string (MMC_F_SYSTEM_DEVICE, mm_modem_get_device (ctx->modem)); mmcli_output_string (MMC_F_SYSTEM_PHYSDEV, mm_modem_get_physdev (ctx->modem)); mmcli_output_string_array (MMC_F_SYSTEM_DRIVERS, (const gchar **) mm_modem_get_drivers (ctx->modem), FALSE); mmcli_output_string (MMC_F_SYSTEM_PLUGIN, mm_modem_get_plugin (ctx->modem)); mmcli_output_string (MMC_F_SYSTEM_PRIMARY_PORT, mm_modem_get_primary_port (ctx->modem)); mmcli_output_string_list (MMC_F_SYSTEM_PORTS, ports_string); mmcli_output_string_array (MMC_F_NUMBERS_OWN, (const gchar **) mm_modem_get_own_numbers (ctx->modem), FALSE); mmcli_output_string (MMC_F_STATUS_LOCK, mm_modem_lock_get_string (mm_modem_get_unlock_required (ctx->modem))); mmcli_output_string_list (MMC_F_STATUS_UNLOCK_RETRIES, unlock_retries_string); mmcli_output_state (mm_modem_get_state (ctx->modem), mm_modem_get_state_failed_reason (ctx->modem)); mmcli_output_string (MMC_F_STATUS_POWER_STATE, mm_modem_power_state_get_string (mm_modem_get_power_state (ctx->modem))); mmcli_output_string_list (MMC_F_STATUS_ACCESS_TECH, access_technologies_string); mmcli_output_signal_quality (mm_modem_get_state (ctx->modem), signal_quality, signal_quality_recent); mmcli_output_string_multiline (MMC_F_MODES_SUPPORTED, supported_modes_string); mmcli_output_string_take (MMC_F_MODES_CURRENT, g_strdup_printf ("allowed: %s; preferred: %s", allowed_modes_string, preferred_mode_string)); mmcli_output_string_list (MMC_F_BANDS_SUPPORTED, supported_bands_string); mmcli_output_string_list (MMC_F_BANDS_CURRENT, current_bands_string); mmcli_output_string_list (MMC_F_IP_SUPPORTED, supported_ip_families_string); /* 3GPP */ { const gchar *imei = NULL; gchar *facility_locks = NULL; const gchar *operator_code = NULL; const gchar *operator_name = NULL; const gchar *registration = NULL; const gchar *packet_service_state = NULL; const gchar *eps_ue_mode = NULL; GList *pco_list = NULL; const gchar *initial_eps_bearer_path = NULL; const gchar *initial_eps_bearer_apn = NULL; gchar *initial_eps_bearer_ip_family_str = NULL; const gchar *initial_eps_bearer_user = NULL; const gchar *initial_eps_bearer_password = NULL; const gchar *nr5g_registration_settings_mico_mode_str = NULL; const gchar *nr5g_registration_settings_drx_cycle_str = NULL; if (ctx->modem_3gpp) { imei = mm_modem_3gpp_get_imei (ctx->modem_3gpp); facility_locks = mm_modem_3gpp_facility_build_string_from_mask (mm_modem_3gpp_get_enabled_facility_locks (ctx->modem_3gpp)); operator_code = mm_modem_3gpp_get_operator_code (ctx->modem_3gpp); operator_name = mm_modem_3gpp_get_operator_name (ctx->modem_3gpp); registration = mm_modem_3gpp_registration_state_get_string (mm_modem_3gpp_get_registration_state (ctx->modem_3gpp)); packet_service_state = mm_modem_3gpp_packet_service_state_get_string (mm_modem_3gpp_get_packet_service_state (ctx->modem_3gpp)); eps_ue_mode = mm_modem_3gpp_eps_ue_mode_operation_get_string (mm_modem_3gpp_get_eps_ue_mode_operation (ctx->modem_3gpp)); pco_list = mm_modem_3gpp_get_pco (ctx->modem_3gpp); initial_eps_bearer_path = mm_modem_3gpp_get_initial_eps_bearer_path (ctx->modem_3gpp); if (mm_modem_get_current_capabilities (ctx->modem) & (MM_MODEM_CAPABILITY_LTE)) { MMBearerProperties *initial_eps_bearer_properties; initial_eps_bearer_properties = mm_modem_3gpp_peek_initial_eps_bearer_settings (ctx->modem_3gpp); if (initial_eps_bearer_properties) { initial_eps_bearer_apn = mm_bearer_properties_get_apn (initial_eps_bearer_properties); initial_eps_bearer_ip_family_str = mm_bearer_ip_family_build_string_from_mask (mm_bearer_properties_get_ip_type (initial_eps_bearer_properties)); initial_eps_bearer_user = mm_bearer_properties_get_user (initial_eps_bearer_properties); initial_eps_bearer_password = mm_bearer_properties_get_password (initial_eps_bearer_properties); } } if (mm_modem_get_current_capabilities (ctx->modem) & (MM_MODEM_CAPABILITY_5GNR)) { MMNr5gRegistrationSettings *nr5g_registration_settings; nr5g_registration_settings = mm_modem_3gpp_peek_nr5g_registration_settings (ctx->modem_3gpp); if (nr5g_registration_settings) { nr5g_registration_settings_mico_mode_str = mm_modem_3gpp_mico_mode_get_string (mm_nr5g_registration_settings_get_mico_mode (nr5g_registration_settings)); nr5g_registration_settings_drx_cycle_str = mm_modem_3gpp_drx_cycle_get_string (mm_nr5g_registration_settings_get_drx_cycle (nr5g_registration_settings)); } } } mmcli_output_string (MMC_F_3GPP_IMEI, imei); mmcli_output_string_list (MMC_F_3GPP_ENABLED_LOCKS, facility_locks); mmcli_output_string (MMC_F_3GPP_OPERATOR_ID, operator_code); mmcli_output_string (MMC_F_3GPP_OPERATOR_NAME, operator_name); mmcli_output_string (MMC_F_3GPP_REGISTRATION, registration); mmcli_output_string (MMC_F_3GPP_PACKET_SERVICE_STATE, packet_service_state); mmcli_output_string (MMC_F_3GPP_EPS_UE_MODE, eps_ue_mode); mmcli_output_string (MMC_F_3GPP_EPS_INITIAL_BEARER_PATH, g_strcmp0 (initial_eps_bearer_path, "/") != 0 ? initial_eps_bearer_path : NULL); mmcli_output_string (MMC_F_3GPP_EPS_BEARER_SETTINGS_APN, initial_eps_bearer_apn); mmcli_output_string_take (MMC_F_3GPP_EPS_BEARER_SETTINGS_IP_TYPE, initial_eps_bearer_ip_family_str); mmcli_output_string (MMC_F_3GPP_EPS_BEARER_SETTINGS_USER, initial_eps_bearer_user); mmcli_output_string (MMC_F_3GPP_EPS_BEARER_SETTINGS_PASSWORD, initial_eps_bearer_password); mmcli_output_string (MMC_F_3GPP_5GNR_REGISTRATION_MICO_MODE, nr5g_registration_settings_mico_mode_str); mmcli_output_string (MMC_F_3GPP_5GNR_REGISTRATION_DRX_CYCLE, nr5g_registration_settings_drx_cycle_str); mmcli_output_pco_list (pco_list); g_free (facility_locks); g_list_free_full (pco_list, g_object_unref); } /* CDMA */ { const gchar *meid = NULL; const gchar *esn = NULL; gchar *sid = NULL; gchar *nid = NULL; const gchar *registration_cdma1x = NULL; const gchar *registration_evdo = NULL; const gchar *activation = NULL; if (ctx->modem_cdma) { guint sid_n; guint nid_n; meid = mm_modem_cdma_get_meid (ctx->modem_cdma); esn = mm_modem_cdma_get_esn (ctx->modem_cdma); sid_n = mm_modem_cdma_get_sid (ctx->modem_cdma); if (sid_n != MM_MODEM_CDMA_SID_UNKNOWN) sid = g_strdup_printf ("%u", sid_n); nid_n = mm_modem_cdma_get_nid (ctx->modem_cdma); if (nid_n != MM_MODEM_CDMA_NID_UNKNOWN) nid = g_strdup_printf ("%u", nid_n); registration_cdma1x = mm_modem_cdma_registration_state_get_string (mm_modem_cdma_get_cdma1x_registration_state (ctx->modem_cdma)); registration_evdo = mm_modem_cdma_registration_state_get_string (mm_modem_cdma_get_evdo_registration_state (ctx->modem_cdma)); activation = mm_modem_cdma_activation_state_get_string (mm_modem_cdma_get_activation_state (ctx->modem_cdma)); } mmcli_output_string (MMC_F_CDMA_MEID, meid); mmcli_output_string (MMC_F_CDMA_ESN, esn); mmcli_output_string_take (MMC_F_CDMA_SID, sid); mmcli_output_string_take (MMC_F_CDMA_NID, nid); mmcli_output_string (MMC_F_CDMA_REGISTRATION_CDMA1X, registration_cdma1x); mmcli_output_string (MMC_F_CDMA_REGISTRATION_EVDO, registration_evdo); mmcli_output_string (MMC_F_CDMA_ACTIVATION, activation); } sim_path = mm_modem_get_sim_path (ctx->modem); mmcli_output_string (MMC_F_SIM_PATH, g_strcmp0 (sim_path, "/") != 0 ? sim_path : NULL); mmcli_output_sim_slots (mm_modem_dup_sim_slot_paths (ctx->modem), mm_modem_get_primary_sim_slot (ctx->modem)); bearer_paths = (const gchar **) mm_modem_get_bearer_paths (ctx->modem); mmcli_output_string_array (MMC_F_BEARER_PATHS, (bearer_paths && bearer_paths[0]) ? bearer_paths : NULL, TRUE); mmcli_output_dump (); g_free (ports_string); g_free (supported_ip_families_string); g_free (current_bands_string); g_free (supported_bands_string); g_free (access_technologies_string); g_free (supported_capabilities_string); g_free (current_capabilities_string); g_free (allowed_modes_string); g_free (preferred_mode_string); g_free (supported_modes_string); g_free (unlock_retries_string); } static void enable_process_reply (gboolean result, const GError *error) { if (!result) { g_printerr ("error: couldn't enable the modem: '%s'\n", error ? error->message : "unknown error"); exit (EXIT_FAILURE); } g_print ("successfully enabled the modem\n"); } static void enable_ready (MMModem *modem, GAsyncResult *result, gpointer nothing) { gboolean operation_result; GError *error = NULL; operation_result = mm_modem_enable_finish (modem, result, &error); enable_process_reply (operation_result, error); mmcli_async_operation_done (); } static void disable_process_reply (gboolean result, const GError *error) { if (!result) { g_printerr ("error: couldn't disable the modem: '%s'\n", error ? error->message : "unknown error"); exit (EXIT_FAILURE); } g_print ("successfully disabled the modem\n"); } static void disable_ready (MMModem *modem, GAsyncResult *result, gpointer nothing) { gboolean operation_result; GError *error = NULL; operation_result = mm_modem_disable_finish (modem, result, &error); disable_process_reply (operation_result, error); mmcli_async_operation_done (); } static void set_power_state_process_reply (gboolean result, const GError *error) { if (!result) { g_printerr ("error: couldn't set new power state in the modem: '%s'\n", error ? error->message : "unknown error"); exit (EXIT_FAILURE); } g_print ("successfully set new power state in the modem\n"); } static void set_power_state_ready (MMModem *modem, GAsyncResult *result, gpointer nothing) { gboolean operation_result; GError *error = NULL; operation_result = mm_modem_set_power_state_finish (modem, result, &error); set_power_state_process_reply (operation_result, error); mmcli_async_operation_done (); } static void reset_process_reply (gboolean result, const GError *error) { if (!result) { g_printerr ("error: couldn't reset the modem: '%s'\n", error ? error->message : "unknown error"); exit (EXIT_FAILURE); } g_print ("successfully reseted the modem\n"); } static void reset_ready (MMModem *modem, GAsyncResult *result, gpointer nothing) { gboolean operation_result; GError *error = NULL; operation_result = mm_modem_reset_finish (modem, result, &error); reset_process_reply (operation_result, error); mmcli_async_operation_done (); } static void factory_reset_process_reply (gboolean result, const GError *error) { if (!result) { g_printerr ("error: couldn't reset the modem to factory state: '%s'\n", error ? error->message : "unknown error"); exit (EXIT_FAILURE); } g_print ("successfully reseted the modem to factory state\n"); } static void factory_reset_ready (MMModem *modem, GAsyncResult *result, gpointer nothing) { gboolean operation_result; GError *error = NULL; operation_result = mm_modem_factory_reset_finish (modem, result, &error); factory_reset_process_reply (operation_result, error); mmcli_async_operation_done (); } static void command_process_reply (gchar *result, const GError *error) { if (!result) { g_printerr ("error: command failed: '%s'\n", error ? error->message : "unknown error"); exit (EXIT_FAILURE); } g_print ("response: '%s'\n", result); g_free (result); } static void command_ready (MMModem *modem, GAsyncResult *result, gpointer nothing) { gchar * operation_result; GError *error = NULL; operation_result = mm_modem_command_finish (modem, result, &error); command_process_reply (operation_result, error); mmcli_async_operation_done (); } static guint command_get_timeout (MMModem *modem) { gint timeout; /* If --timeout was given, it should already have been set in the proxy */ timeout = (g_dbus_proxy_get_default_timeout (G_DBUS_PROXY (modem)) / 1000) - 1; if (timeout <= 0) { g_printerr ("error: timeout is too short (%d)\n", timeout); exit (EXIT_FAILURE); } return (guint)timeout; } static void create_bearer_process_reply (MMBearer *bearer, const GError *error) { if (!bearer) { g_printerr ("error: couldn't create new bearer: '%s'\n", error ? error->message : "unknown error"); exit (EXIT_FAILURE); } g_print ("Successfully created new bearer in modem:\n"); print_bearer_short_info (bearer); g_object_unref (bearer); } static void create_bearer_ready (MMModem *modem, GAsyncResult *result, gpointer nothing) { MMBearer *bearer; GError *error = NULL; bearer = mm_modem_create_bearer_finish (modem, result, &error); create_bearer_process_reply (bearer, error); mmcli_async_operation_done (); } static void delete_bearer_process_reply (gboolean result, const GError *error) { if (!result) { g_printerr ("error: couldn't delete bearer: '%s'\n", error ? error->message : "unknown error"); exit (EXIT_FAILURE); } g_print ("successfully deleted bearer from modem\n"); } static void delete_bearer_ready (MMModem *modem, GAsyncResult *result, gpointer nothing) { gboolean operation_result; GError *error = NULL; operation_result = mm_modem_delete_bearer_finish (modem, result, &error); delete_bearer_process_reply (operation_result, error); mmcli_async_operation_done (); } static void get_bearer_to_delete_ready (GDBusConnection *connection, GAsyncResult *res) { MMBearer *bearer; MMObject *obj = NULL; bearer = mmcli_get_bearer_finish (res, NULL, &obj); if (!g_str_equal (mm_object_get_path (obj), mm_modem_get_path (ctx->modem))) { g_printerr ("error: bearer '%s' not owned by modem '%s'", mm_bearer_get_path (bearer), mm_modem_get_path (ctx->modem)); exit (EXIT_FAILURE); } mm_modem_delete_bearer (ctx->modem, mm_bearer_get_path (bearer), ctx->cancellable, (GAsyncReadyCallback)delete_bearer_ready, NULL); g_object_unref (bearer); g_object_unref (obj); } static void set_current_capabilities_process_reply (gboolean result, const GError *error) { if (!result) { g_printerr ("error: couldn't set current capabilities: '%s'\n", error ? error->message : "unknown error"); exit (EXIT_FAILURE); } g_print ("successfully set current capabilities in the modem\n"); } static void set_current_capabilities_ready (MMModem *modem, GAsyncResult *result, gpointer nothing) { gboolean operation_result; GError *error = NULL; operation_result = mm_modem_set_current_capabilities_finish (modem, result, &error); set_current_capabilities_process_reply (operation_result, error); mmcli_async_operation_done (); } static void parse_current_capabilities (MMModemCapability *capabilities) { GError *error = NULL; *capabilities = mm_common_get_capabilities_from_string (set_current_capabilities_str, &error); if (error) { g_printerr ("error: couldn't parse list of capabilities: '%s'\n", error->message); exit (EXIT_FAILURE); } } static void set_current_modes_process_reply (gboolean result, const GError *error) { if (!result) { g_printerr ("error: couldn't set current modes: '%s'\n", error ? error->message : "unknown error"); exit (EXIT_FAILURE); } g_print ("successfully set current modes in the modem\n"); } static void set_current_modes_ready (MMModem *modem, GAsyncResult *result, gpointer nothing) { gboolean operation_result; GError *error = NULL; operation_result = mm_modem_set_current_modes_finish (modem, result, &error); set_current_modes_process_reply (operation_result, error); mmcli_async_operation_done (); } static void parse_modes (MMModemMode *allowed, MMModemMode *preferred) { GError *error = NULL; *allowed = mm_common_get_modes_from_string (set_allowed_modes_str, &error); if (error) { g_printerr ("error: couldn't parse list of allowed modes: '%s'\n", error->message); exit (EXIT_FAILURE); } *preferred = (set_preferred_mode_str ? mm_common_get_modes_from_string (set_preferred_mode_str, &error) : MM_MODEM_MODE_NONE); if (error) { g_printerr ("error: couldn't parse preferred mode: '%s'\n", error->message); exit (EXIT_FAILURE); } } static void set_current_bands_process_reply (gboolean result, const GError *error) { if (!result) { g_printerr ("error: couldn't set current bands: '%s'\n", error ? error->message : "unknown error"); exit (EXIT_FAILURE); } g_print ("successfully set current bands in the modem\n"); } static void set_current_bands_ready (MMModem *modem, GAsyncResult *result, gpointer nothing) { gboolean operation_result; GError *error = NULL; operation_result = mm_modem_set_current_bands_finish (modem, result, &error); set_current_bands_process_reply (operation_result, error); mmcli_async_operation_done (); } static void parse_current_bands (MMModemBand **bands, guint *n_bands) { GError *error = NULL; mm_common_get_bands_from_string (set_current_bands_str, bands, n_bands, &error); if (error) { g_printerr ("error: couldn't parse list of bands: '%s'\n", error->message); exit (EXIT_FAILURE); } } static void set_primary_sim_slot_process_reply (gboolean result, const GError *error) { if (!result) { g_printerr ("error: couldn't request primary SIM switch: '%s'\n", error ? error->message : "unknown error"); exit (EXIT_FAILURE); } g_print ("successfully requested primary SIM switch in modem\n"); } static void set_primary_sim_slot_ready (MMModem *modem, GAsyncResult *result) { gboolean operation_result; g_autoptr(GError) error = NULL; operation_result = mm_modem_set_primary_sim_slot_finish (modem, result, &error); set_primary_sim_slot_process_reply (operation_result, error); mmcli_async_operation_done (); } static void get_cell_info_process_reply (GList *list, const GError *error) { if (!list) { g_printerr ("error: couldn't get cell info in the modem: '%s'\n", error ? error->message : "unknown error"); exit (EXIT_FAILURE); } mmcli_output_cell_info (list); mmcli_output_dump (); g_list_free_full (list, (GDestroyNotify) g_object_unref); } static void get_cell_info_ready (MMModem *modem, GAsyncResult *result) { GList *list; g_autoptr(GError) error = NULL; list = mm_modem_get_cell_info_finish (modem, result, &error); get_cell_info_process_reply (list, error); mmcli_async_operation_done (); } static void state_changed (MMModem *modem, MMModemState old_state, MMModemState new_state, MMModemStateChangeReason reason) { g_print ("\t%s: State changed, '%s' --> '%s' (Reason: %s)\n", mm_modem_get_path (modem), mm_modem_state_get_string (old_state), mm_modem_state_get_string (new_state), mmcli_get_state_reason_string (reason)); fflush (stdout); } static void device_removed (MMManager *manager, MMObject *object) { if (object != ctx->object) return; g_print ("\t%s: Removed\n", mm_object_get_path (object)); fflush (stdout); mmcli_async_operation_done (); } static void get_modem_ready (GObject *source, GAsyncResult *result, gpointer none) { ctx->object = mmcli_get_modem_finish (result, &ctx->manager); ctx->modem = mm_object_get_modem (ctx->object); ctx->modem_3gpp = mm_object_get_modem_3gpp (ctx->object); ctx->modem_cdma = mm_object_get_modem_cdma (ctx->object); /* Setup operation timeout */ if (ctx->modem) mmcli_force_operation_timeout (G_DBUS_PROXY (ctx->modem)); if (ctx->modem_3gpp) mmcli_force_operation_timeout (G_DBUS_PROXY (ctx->modem_3gpp)); if (ctx->modem_cdma) mmcli_force_operation_timeout (G_DBUS_PROXY (ctx->modem_cdma)); if (info_flag) g_assert_not_reached (); /* Request to monitor modems? */ if (monitor_state_flag) { MMModemState current; g_signal_connect (ctx->modem, "state-changed", G_CALLBACK (state_changed), NULL); g_signal_connect (ctx->manager, "object-removed", G_CALLBACK (device_removed), NULL); current = mm_modem_get_state (ctx->modem); g_print ("\t%s: Initial state, '%s'\n", mm_object_get_path (ctx->object), mm_modem_state_get_string (current)); /* If we get cancelled, operation done */ g_cancellable_connect (ctx->cancellable, G_CALLBACK (cancelled), NULL, NULL); return; } /* Request to enable the modem? */ if (enable_flag) { g_debug ("Asynchronously enabling modem..."); mm_modem_enable (ctx->modem, ctx->cancellable, (GAsyncReadyCallback)enable_ready, NULL); return; } /* Request to disable the modem? */ if (disable_flag) { g_debug ("Asynchronously disabling modem..."); mm_modem_disable (ctx->modem, ctx->cancellable, (GAsyncReadyCallback)disable_ready, NULL); return; } /* Request to full power the modem? */ if (set_power_state_on_flag) { g_debug ("Asynchronously setting full power..."); mm_modem_set_power_state (ctx->modem, MM_MODEM_POWER_STATE_ON, ctx->cancellable, (GAsyncReadyCallback)set_power_state_ready, NULL); return; } /* Request to low power the modem? */ if (set_power_state_low_flag) { g_debug ("Asynchronously setting low power..."); mm_modem_set_power_state (ctx->modem, MM_MODEM_POWER_STATE_LOW, ctx->cancellable, (GAsyncReadyCallback)set_power_state_ready, NULL); return; } /* Request to power off the modem? */ if (set_power_state_off_flag) { g_debug ("Asynchronously powering off..."); mm_modem_set_power_state (ctx->modem, MM_MODEM_POWER_STATE_OFF, ctx->cancellable, (GAsyncReadyCallback)set_power_state_ready, NULL); return; } /* Request to reset the modem? */ if (reset_flag) { g_debug ("Asynchronously reseting modem..."); mm_modem_reset (ctx->modem, ctx->cancellable, (GAsyncReadyCallback)reset_ready, NULL); return; } /* Request to reset the modem to factory state? */ if (factory_reset_str) { g_debug ("Asynchronously factory-reseting modem..."); mm_modem_factory_reset (ctx->modem, factory_reset_str, ctx->cancellable, (GAsyncReadyCallback)factory_reset_ready, NULL); return; } /* Request to send a command to the modem? */ if (command_str) { guint timeout; timeout = command_get_timeout (ctx->modem); g_debug ("Asynchronously sending a command to the modem (%u seconds timeout)...", timeout); mm_modem_command (ctx->modem, command_str, timeout, ctx->cancellable, (GAsyncReadyCallback)command_ready, NULL); return; } /* Request to create a new bearer? */ if (create_bearer_str) { GError *error = NULL; MMBearerProperties *properties; properties = mm_bearer_properties_new_from_string (create_bearer_str, &error); if (!properties) { g_printerr ("Error parsing properties string: '%s'\n", error->message); exit (EXIT_FAILURE); } g_debug ("Asynchronously creating new bearer in modem..."); mm_modem_create_bearer (ctx->modem, properties, ctx->cancellable, (GAsyncReadyCallback)create_bearer_ready, NULL); g_object_unref (properties); return; } /* Request to delete a given bearer? */ if (delete_bearer_str) { mmcli_get_bearer (ctx->connection, delete_bearer_str, ctx->cancellable, (GAsyncReadyCallback)get_bearer_to_delete_ready, NULL); return; } /* Request to set current capabilities in a given modem? */ if (set_current_capabilities_str) { MMModemCapability current_capabilities; parse_current_capabilities (¤t_capabilities); mm_modem_set_current_capabilities (ctx->modem, current_capabilities, ctx->cancellable, (GAsyncReadyCallback)set_current_capabilities_ready, NULL); return; } /* Request to set allowed modes in a given modem? */ if (set_allowed_modes_str) { MMModemMode allowed; MMModemMode preferred; parse_modes (&allowed, &preferred); mm_modem_set_current_modes (ctx->modem, allowed, preferred, ctx->cancellable, (GAsyncReadyCallback)set_current_modes_ready, NULL); return; } /* Request to set current bands in a given modem? */ if (set_current_bands_str) { MMModemBand *current_bands; guint n_current_bands; parse_current_bands (¤t_bands, &n_current_bands); mm_modem_set_current_bands (ctx->modem, current_bands, n_current_bands, ctx->cancellable, (GAsyncReadyCallback)set_current_bands_ready, NULL); g_free (current_bands); return; } /* Request to switch SIM? */ if (set_primary_sim_slot_int > 0) { mm_modem_set_primary_sim_slot (ctx->modem, set_primary_sim_slot_int, ctx->cancellable, (GAsyncReadyCallback)set_primary_sim_slot_ready, NULL); return; } /* Request to get cell info? */ if (get_cell_info_flag) { mm_modem_get_cell_info (ctx->modem, ctx->cancellable, (GAsyncReadyCallback)get_cell_info_ready, NULL); return; } /* Request to inhibit the modem? */ if (inhibit_flag) { gchar *uid; g_debug ("Asynchronously inhibiting modem..."); uid = mm_modem_dup_device (ctx->modem); mm_manager_inhibit_device (ctx->manager, uid, ctx->cancellable, (GAsyncReadyCallback)inhibit_device_ready, uid); return; } g_warn_if_reached (); } void mmcli_modem_run_asynchronous (GDBusConnection *connection, GCancellable *cancellable) { /* Initialize context */ ctx = g_new0 (Context, 1); if (cancellable) ctx->cancellable = g_object_ref (cancellable); ctx->connection = g_object_ref (connection); /* Get proper modem */ mmcli_get_modem (connection, mmcli_get_common_modem_string (), cancellable, (GAsyncReadyCallback)get_modem_ready, NULL); } void mmcli_modem_run_synchronous (GDBusConnection *connection) { GError *error = NULL; if (monitor_state_flag || inhibit_flag) g_assert_not_reached (); /* Initialize context */ ctx = g_new0 (Context, 1); ctx->object = mmcli_get_modem_sync (connection, mmcli_get_common_modem_string (), &ctx->manager); ctx->modem = mm_object_get_modem (ctx->object); ctx->modem_3gpp = mm_object_get_modem_3gpp (ctx->object); ctx->modem_cdma = mm_object_get_modem_cdma (ctx->object); /* Setup operation timeout */ if (ctx->modem) mmcli_force_operation_timeout (G_DBUS_PROXY (ctx->modem)); if (ctx->modem_3gpp) mmcli_force_operation_timeout (G_DBUS_PROXY (ctx->modem_3gpp)); if (ctx->modem_cdma) mmcli_force_operation_timeout (G_DBUS_PROXY (ctx->modem_cdma)); /* Request to get info from modem? */ if (info_flag) { g_debug ("Printing modem info..."); print_modem_info (); return; } /* Request to enable the modem? */ if (enable_flag) { gboolean result; g_debug ("Synchronously enabling modem..."); result = mm_modem_enable_sync (ctx->modem, NULL, &error); enable_process_reply (result, error); return; } /* Request to disable the modem? */ if (disable_flag) { gboolean result; g_debug ("Synchronously disabling modem..."); result = mm_modem_disable_sync (ctx->modem, NULL, &error); disable_process_reply (result, error); return; } /* Request to set full power state? */ if (set_power_state_on_flag) { gboolean result; g_debug ("Synchronously setting full power..."); result = mm_modem_set_power_state_sync (ctx->modem, MM_MODEM_POWER_STATE_ON, NULL, &error); set_power_state_process_reply (result, error); return; } /* Request to set low power state? */ if (set_power_state_low_flag) { gboolean result; g_debug ("Synchronously setting low power..."); result = mm_modem_set_power_state_sync (ctx->modem, MM_MODEM_POWER_STATE_LOW, NULL, &error); set_power_state_process_reply (result, error); return; } /* Request to power off? */ if (set_power_state_off_flag) { gboolean result; g_debug ("Synchronously powering off..."); result = mm_modem_set_power_state_sync (ctx->modem, MM_MODEM_POWER_STATE_OFF, NULL, &error); set_power_state_process_reply (result, error); return; } /* Request to reset the modem? */ if (reset_flag) { gboolean result; g_debug ("Synchronously reseting modem..."); result = mm_modem_reset_sync (ctx->modem, NULL, &error); reset_process_reply (result, error); return; } /* Request to reset the modem to factory state? */ if (factory_reset_str) { gboolean result; g_debug ("Synchronously factory-reseting modem..."); result = mm_modem_factory_reset_sync (ctx->modem, factory_reset_str, NULL, &error); factory_reset_process_reply (result, error); return; } /* Request to send a command to the modem? */ if (command_str) { gchar *result; guint timeout; timeout = command_get_timeout (ctx->modem); g_debug ("Synchronously sending command to modem (%u seconds timeout)...", timeout); result = mm_modem_command_sync (ctx->modem, command_str, timeout, NULL, &error); command_process_reply (result, error); return; } /* Request to create a new bearer? */ if (create_bearer_str) { MMBearer *bearer; MMBearerProperties *properties; properties = mm_bearer_properties_new_from_string (create_bearer_str, &error); if (!properties) { g_printerr ("Error parsing properties string: '%s'\n", error->message); exit (EXIT_FAILURE); } g_debug ("Synchronously creating new bearer in modem..."); bearer = mm_modem_create_bearer_sync (ctx->modem, properties, NULL, &error); g_object_unref (properties); create_bearer_process_reply (bearer, error); return; } /* Request to delete a given bearer? */ if (delete_bearer_str) { gboolean result; MMBearer *bearer; MMObject *obj = NULL; bearer = mmcli_get_bearer_sync (connection, delete_bearer_str, NULL, &obj); if (!g_str_equal (mm_object_get_path (obj), mm_modem_get_path (ctx->modem))) { g_printerr ("error: bearer '%s' not owned by modem '%s'", mm_bearer_get_path (bearer), mm_modem_get_path (ctx->modem)); exit (EXIT_FAILURE); } result = mm_modem_delete_bearer_sync (ctx->modem, mm_bearer_get_path (bearer), NULL, &error); g_object_unref (bearer); g_object_unref (obj); delete_bearer_process_reply (result, error); return; } /* Request to set capabilities in a given modem? */ if (set_current_capabilities_str) { gboolean result; MMModemCapability current_capabilities; parse_current_capabilities (¤t_capabilities); result = mm_modem_set_current_capabilities_sync (ctx->modem, current_capabilities, NULL, &error); set_current_capabilities_process_reply (result, error); return; } /* Request to set allowed modes in a given modem? */ if (set_allowed_modes_str) { MMModemMode allowed; MMModemMode preferred; gboolean result; parse_modes (&allowed, &preferred); result = mm_modem_set_current_modes_sync (ctx->modem, allowed, preferred, NULL, &error); set_current_modes_process_reply (result, error); return; } /* Request to set allowed bands in a given modem? */ if (set_current_bands_str) { gboolean result; MMModemBand *current_bands; guint n_current_bands; parse_current_bands (¤t_bands, &n_current_bands); result = mm_modem_set_current_bands_sync (ctx->modem, current_bands, n_current_bands, NULL, &error); g_free (current_bands); set_current_bands_process_reply (result, error); return; } /* Request to switch current SIM? */ if (set_primary_sim_slot_int > 0) { gboolean result; result = mm_modem_set_primary_sim_slot_sync (ctx->modem, set_primary_sim_slot_int, NULL, &error); set_primary_sim_slot_process_reply (result, error); return; } /* Request to get cell info? */ if (get_cell_info_flag) { GList *list; list = mm_modem_get_cell_info_sync (ctx->modem, NULL, &error); get_cell_info_process_reply (list, error); return; } g_warn_if_reached (); } ModemManager-1.23.4-dev/cli/mmcli-output.c000066400000000000000000002500471456466623000203070ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * mmcli -- Control modem status & access information from the command line * * 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, see . * * Copyright (C) 2018 Aleksander Morgado */ #include #include #define _LIBMM_INSIDE_MMCLI #include #include "mm-common-helpers.h" #include "mmcli-output.h" /******************************************************************************/ /* List of sections (grouped fields) displayed in the human-friendly output */ typedef struct { const gchar *name; } SectionInfo; static SectionInfo section_infos[] = { [MMC_S_MODEM_GENERAL] = { "General" }, [MMC_S_MODEM_HARDWARE] = { "Hardware" }, [MMC_S_MODEM_SYSTEM] = { "System" }, [MMC_S_MODEM_NUMBERS] = { "Numbers" }, [MMC_S_MODEM_STATUS] = { "Status" }, [MMC_S_MODEM_MODES] = { "Modes" }, [MMC_S_MODEM_BANDS] = { "Bands" }, [MMC_S_MODEM_IP] = { "IP" }, [MMC_S_MODEM_CELL_INFO] = { "Cell info" }, [MMC_S_MODEM_3GPP] = { "3GPP" }, [MMC_S_MODEM_3GPP_EPS] = { "3GPP EPS" }, [MMC_S_MODEM_3GPP_5GNR] = { "3GPP 5GNR" }, [MMC_S_MODEM_3GPP_SCAN] = { "3GPP scan" }, [MMC_S_MODEM_3GPP_USSD] = { "3GPP USSD" }, [MMC_S_MODEM_3GPP_PROFILE_MANAGER] = { "3GPP profile manager" }, [MMC_S_MODEM_CDMA] = { "CDMA" }, [MMC_S_MODEM_SIM] = { "SIM" }, [MMC_S_MODEM_BEARER] = { "Bearer" }, [MMC_S_MODEM_TIME] = { "Time" }, [MMC_S_MODEM_TIMEZONE] = { "Timezone" }, [MMC_S_MODEM_MESSAGING] = { "Messaging" }, [MMC_S_MODEM_SIGNAL] = { "Signal" }, [MMC_S_MODEM_SIGNAL_CDMA1X] = { "CDMA1x" }, [MMC_S_MODEM_SIGNAL_EVDO] = { "EV-DO" }, [MMC_S_MODEM_SIGNAL_GSM] = { "GSM" }, [MMC_S_MODEM_SIGNAL_UMTS] = { "UMTS" }, [MMC_S_MODEM_SIGNAL_LTE] = { "LTE" }, [MMC_S_MODEM_SIGNAL_5G] = { "5G" }, [MMC_S_MODEM_OMA] = { "OMA" }, [MMC_S_MODEM_OMA_CURRENT] = { "Current session" }, [MMC_S_MODEM_OMA_PENDING] = { "Pending sessions" }, [MMC_S_MODEM_LOCATION] = { "Location" }, [MMC_S_MODEM_LOCATION_3GPP] = { "3GPP" }, [MMC_S_MODEM_LOCATION_GPS] = { "GPS" }, [MMC_S_MODEM_LOCATION_CDMABS] = { "CDMA BS" }, [MMC_S_MODEM_FIRMWARE] = { "Firmware" }, [MMC_S_MODEM_FIRMWARE_FASTBOOT] = { "Fastboot settings" }, [MMC_S_MODEM_VOICE] = { "Voice" }, [MMC_S_MODEM_SAR] = { "SAR" }, [MMC_S_BEARER_GENERAL] = { "General" }, [MMC_S_BEARER_STATUS] = { "Status" }, [MMC_S_BEARER_PROPERTIES] = { "Properties" }, [MMC_S_BEARER_IPV4_CONFIG] = { "IPv4 configuration" }, [MMC_S_BEARER_IPV6_CONFIG] = { "IPv6 configuration" }, [MMC_S_BEARER_STATS] = { "Statistics" }, [MMC_S_CALL_GENERAL] = { "General" }, [MMC_S_CALL_PROPERTIES] = { "Properties" }, [MMC_S_CALL_AUDIO_FORMAT] = { "Audio format" }, [MMC_S_SMS_GENERAL] = { "General" }, [MMC_S_SMS_CONTENT] = { "Content" }, [MMC_S_SMS_PROPERTIES] = { "Properties" }, [MMC_S_SIM_GENERAL] = { "General" }, [MMC_S_SIM_PROPERTIES] = { "Properties" }, }; /******************************************************************************/ /* List of fields */ typedef struct { const gchar *key; const gchar *name; MmcS section; } FieldInfo; static FieldInfo field_infos[] = { [MMC_F_GENERAL_DBUS_PATH] = { "modem.dbus-path", "path", MMC_S_MODEM_GENERAL, }, [MMC_F_GENERAL_DEVICE_ID] = { "modem.generic.device-identifier", "device id", MMC_S_MODEM_GENERAL, }, [MMC_F_HARDWARE_MANUFACTURER] = { "modem.generic.manufacturer", "manufacturer", MMC_S_MODEM_HARDWARE, }, [MMC_F_HARDWARE_MODEL] = { "modem.generic.model", "model", MMC_S_MODEM_HARDWARE, }, [MMC_F_HARDWARE_REVISION] = { "modem.generic.revision", "firmware revision", MMC_S_MODEM_HARDWARE, }, [MMC_F_HARDWARE_CARRIER_CONF] = { "modem.generic.carrier-configuration", "carrier config", MMC_S_MODEM_HARDWARE, }, [MMC_F_HARDWARE_CARRIER_CONF_REV] = { "modem.generic.carrier-configuration-revision", "carrier config revision", MMC_S_MODEM_HARDWARE, }, [MMC_F_HARDWARE_HW_REVISION] = { "modem.generic.hardware-revision", "h/w revision", MMC_S_MODEM_HARDWARE, }, [MMC_F_HARDWARE_SUPPORTED_CAPABILITIES] = { "modem.generic.supported-capabilities", "supported", MMC_S_MODEM_HARDWARE, }, [MMC_F_HARDWARE_CURRENT_CAPABILITIES] = { "modem.generic.current-capabilities", "current", MMC_S_MODEM_HARDWARE, }, [MMC_F_HARDWARE_EQUIPMENT_ID] = { "modem.generic.equipment-identifier", "equipment id", MMC_S_MODEM_HARDWARE, }, [MMC_F_SYSTEM_DEVICE] = { "modem.generic.device", "device", MMC_S_MODEM_SYSTEM, }, [MMC_F_SYSTEM_PHYSDEV] = { "modem.generic.physdev", "physdev", MMC_S_MODEM_SYSTEM, }, [MMC_F_SYSTEM_DRIVERS] = { "modem.generic.drivers", "drivers", MMC_S_MODEM_SYSTEM, }, [MMC_F_SYSTEM_PLUGIN] = { "modem.generic.plugin", "plugin", MMC_S_MODEM_SYSTEM, }, [MMC_F_SYSTEM_PRIMARY_PORT] = { "modem.generic.primary-port", "primary port", MMC_S_MODEM_SYSTEM, }, [MMC_F_SYSTEM_PORTS] = { "modem.generic.ports", "ports", MMC_S_MODEM_SYSTEM, }, [MMC_F_NUMBERS_OWN] = { "modem.generic.own-numbers", "own", MMC_S_MODEM_NUMBERS, }, [MMC_F_STATUS_LOCK] = { "modem.generic.unlock-required", "lock", MMC_S_MODEM_STATUS, }, [MMC_F_STATUS_UNLOCK_RETRIES] = { "modem.generic.unlock-retries", "unlock retries", MMC_S_MODEM_STATUS, }, [MMC_F_STATUS_STATE] = { "modem.generic.state", "state", MMC_S_MODEM_STATUS, }, [MMC_F_STATUS_FAILED_REASON] = { "modem.generic.state-failed-reason", "failed reason", MMC_S_MODEM_STATUS, }, [MMC_F_STATUS_POWER_STATE] = { "modem.generic.power-state", "power state", MMC_S_MODEM_STATUS, }, [MMC_F_STATUS_ACCESS_TECH] = { "modem.generic.access-technologies", "access tech", MMC_S_MODEM_STATUS, }, [MMC_F_STATUS_SIGNAL_QUALITY_VALUE] = { "modem.generic.signal-quality.value", "signal quality", MMC_S_MODEM_STATUS, }, [MMC_F_STATUS_SIGNAL_QUALITY_RECENT] = { "modem.generic.signal-quality.recent", NULL, MMC_S_UNKNOWN, }, [MMC_F_MODES_SUPPORTED] = { "modem.generic.supported-modes", "supported", MMC_S_MODEM_MODES, }, [MMC_F_MODES_CURRENT] = { "modem.generic.current-modes", "current", MMC_S_MODEM_MODES, }, [MMC_F_BANDS_SUPPORTED] = { "modem.generic.supported-bands", "supported", MMC_S_MODEM_BANDS, }, [MMC_F_BANDS_CURRENT] = { "modem.generic.current-bands", "current", MMC_S_MODEM_BANDS, }, [MMC_F_IP_SUPPORTED] = { "modem.generic.supported-ip-families", "supported", MMC_S_MODEM_IP, }, [MMC_F_CELL_INFO] = { "modem.generic.cell-info", "cells", MMC_S_MODEM_CELL_INFO, }, [MMC_F_3GPP_IMEI] = { "modem.3gpp.imei", "imei", MMC_S_MODEM_3GPP, }, [MMC_F_3GPP_ENABLED_LOCKS] = { "modem.3gpp.enabled-locks", "enabled locks", MMC_S_MODEM_3GPP, }, [MMC_F_3GPP_OPERATOR_ID] = { "modem.3gpp.operator-code", "operator id", MMC_S_MODEM_3GPP, }, [MMC_F_3GPP_OPERATOR_NAME] = { "modem.3gpp.operator-name", "operator name", MMC_S_MODEM_3GPP, }, [MMC_F_3GPP_REGISTRATION] = { "modem.3gpp.registration-state", "registration", MMC_S_MODEM_3GPP, }, [MMC_F_3GPP_PACKET_SERVICE_STATE] = { "modem.3gpp.packet-service-state", "packet service state", MMC_S_MODEM_3GPP, }, [MMC_F_3GPP_PCO] = { "modem.3gpp.pco", "pco", MMC_S_MODEM_3GPP, }, [MMC_F_3GPP_EPS_UE_MODE] = { "modem.3gpp.eps.ue-mode-operation", "ue mode of operation", MMC_S_MODEM_3GPP_EPS, }, [MMC_F_3GPP_EPS_INITIAL_BEARER_PATH] = { "modem.3gpp.eps.initial-bearer.dbus-path", "initial bearer path", MMC_S_MODEM_3GPP_EPS, }, [MMC_F_3GPP_EPS_BEARER_SETTINGS_APN] = { "modem.3gpp.eps.initial-bearer.settings.apn", "initial bearer apn", MMC_S_MODEM_3GPP_EPS, }, [MMC_F_3GPP_EPS_BEARER_SETTINGS_IP_TYPE] = { "modem.3gpp.eps.initial-bearer.settings.ip-type", "initial bearer ip type", MMC_S_MODEM_3GPP_EPS, }, [MMC_F_3GPP_EPS_BEARER_SETTINGS_USER] = { "modem.3gpp.eps.initial-bearer.settings.user", "initial bearer user", MMC_S_MODEM_3GPP_EPS, }, [MMC_F_3GPP_EPS_BEARER_SETTINGS_PASSWORD] = { "modem.3gpp.eps.initial-bearer.settings.password", "initial bearer password", MMC_S_MODEM_3GPP_EPS, }, [MMC_F_3GPP_5GNR_REGISTRATION_MICO_MODE] = { "modem.3gpp.5gnr.registration-settings.mico-mode", "mico mode", MMC_S_MODEM_3GPP_5GNR, }, [MMC_F_3GPP_5GNR_REGISTRATION_DRX_CYCLE] = { "modem.3gpp.5gnr.registration-settings.drx-cycle", "drx cycle", MMC_S_MODEM_3GPP_5GNR, }, [MMC_F_3GPP_SCAN_NETWORKS] = { "modem.3gpp.scan-networks", "networks", MMC_S_MODEM_3GPP_SCAN, }, [MMC_F_3GPP_USSD_STATUS] = { "modem.3gpp.ussd.status", "status", MMC_S_MODEM_3GPP_USSD, }, [MMC_F_3GPP_USSD_NETWORK_REQUEST] = { "modem.3gpp.ussd.network-request", "network request", MMC_S_MODEM_3GPP_USSD, }, [MMC_F_3GPP_USSD_NETWORK_NOTIFICATION] = { "modem.3gpp.ussd.network-notification", "network notification", MMC_S_MODEM_3GPP_USSD, }, [MMC_F_3GPP_PROFILE_MANAGER_INDEX_FIELD] = { "modem.3gpp.profile-manager.index-field", "index field", MMC_S_MODEM_3GPP_PROFILE_MANAGER, }, [MMC_F_3GPP_PROFILE_MANAGER_LIST] = { "modem.3gpp.profile-manager.list", "list", MMC_S_MODEM_3GPP_PROFILE_MANAGER, }, [MMC_F_3GPP_PROFILE_MANAGER_SET] = { "modem.3gpp.profile-manager.set", "set", MMC_S_MODEM_3GPP_PROFILE_MANAGER, }, [MMC_F_CDMA_MEID] = { "modem.cdma.meid", "meid", MMC_S_MODEM_CDMA, }, [MMC_F_CDMA_ESN] = { "modem.cdma.esn", "esn", MMC_S_MODEM_CDMA, }, [MMC_F_CDMA_SID] = { "modem.cdma.sid", "sid", MMC_S_MODEM_CDMA, }, [MMC_F_CDMA_NID] = { "modem.cdma.nid", "nid", MMC_S_MODEM_CDMA, }, [MMC_F_CDMA_REGISTRATION_CDMA1X] = { "modem.cdma.cdma1x-registration-state", "registration cdma1x", MMC_S_MODEM_CDMA, }, [MMC_F_CDMA_REGISTRATION_EVDO] = { "modem.cdma.evdo-registration-state", "registration evdo", MMC_S_MODEM_CDMA, }, [MMC_F_CDMA_ACTIVATION] = { "modem.cdma.activation-state", "activation", MMC_S_MODEM_CDMA, }, [MMC_F_SIM_PATH] = { "modem.generic.sim", "primary sim path", MMC_S_MODEM_SIM, }, [MMC_F_SIM_PRIMARY_SLOT] = { "modem.generic.primary-sim-slot", NULL, MMC_S_MODEM_SIM, }, [MMC_F_SIM_SLOT_PATHS] = { "modem.generic.sim-slots", "sim slot paths", MMC_S_MODEM_SIM, }, [MMC_F_BEARER_PATHS] = { "modem.generic.bearers", "paths", MMC_S_MODEM_BEARER, }, [MMC_F_TIME_CURRENT] = { "modem.time.current", "current", MMC_S_MODEM_TIME, }, [MMC_F_TIMEZONE_CURRENT] = { "modem.timezone.current", "current", MMC_S_MODEM_TIMEZONE, }, [MMC_F_TIMEZONE_DST_OFFSET] = { "modem.time.dst-offset", "dst offset", MMC_S_MODEM_TIMEZONE, }, [MMC_F_TIMEZONE_LEAP_SECONDS] = { "modem.time.leap-seconds", "leap seconds", MMC_S_MODEM_TIMEZONE, }, [MMC_F_MESSAGING_SUPPORTED_STORAGES] = { "modem.messaging.supported-storages", "supported storages", MMC_S_MODEM_MESSAGING, }, [MMC_F_MESSAGING_DEFAULT_STORAGES] = { "modem.messaging.default-storages", "default storages", MMC_S_MODEM_MESSAGING, }, [MMC_F_MESSAGING_CREATED_SMS] = { "modem.messaging.created-sms", "created sms", MMC_S_MODEM_MESSAGING, }, [MMC_F_SIGNAL_REFRESH_RATE] = { "modem.signal.refresh.rate", "refresh rate", MMC_S_MODEM_SIGNAL, }, [MMC_F_SIGNAL_RSSI_THRESHOLD] = { "modem.signal.threshold.rssi", "rssi threshold", MMC_S_MODEM_SIGNAL, }, [MMC_F_SIGNAL_ERROR_RATE_THRESHOLD] = { "modem.signal.threshold.error-rate", "error rate threshold", MMC_S_MODEM_SIGNAL, }, [MMC_F_SIGNAL_CDMA1X_RSSI] = { "modem.signal.cdma1x.rssi", "rssi", MMC_S_MODEM_SIGNAL_CDMA1X, }, [MMC_F_SIGNAL_CDMA1X_ECIO] = { "modem.signal.cdma1x.ecio", "ecio", MMC_S_MODEM_SIGNAL_CDMA1X, }, [MMC_F_SIGNAL_CDMA1X_ERROR_RATE] = { "modem.signal.cdma1x.error-rate", "error rate", MMC_S_MODEM_SIGNAL_CDMA1X, }, [MMC_F_SIGNAL_EVDO_RSSI] = { "modem.signal.evdo.rssi", "rssi", MMC_S_MODEM_SIGNAL_EVDO, }, [MMC_F_SIGNAL_EVDO_ECIO] = { "modem.signal.evdo.ecio", "ecio", MMC_S_MODEM_SIGNAL_EVDO, }, [MMC_F_SIGNAL_EVDO_SINR] = { "modem.signal.evdo.sinr", "sinr", MMC_S_MODEM_SIGNAL_EVDO, }, [MMC_F_SIGNAL_EVDO_IO] = { "modem.signal.evdo.io", "io", MMC_S_MODEM_SIGNAL_EVDO, }, [MMC_F_SIGNAL_EVDO_ERROR_RATE] = { "modem.signal.evdo.error-rate", "error rate", MMC_S_MODEM_SIGNAL_EVDO, }, [MMC_F_SIGNAL_GSM_RSSI] = { "modem.signal.gsm.rssi", "rssi", MMC_S_MODEM_SIGNAL_GSM, }, [MMC_F_SIGNAL_GSM_ERROR_RATE] = { "modem.signal.gsm.error-rate", "error rate", MMC_S_MODEM_SIGNAL_GSM, }, [MMC_F_SIGNAL_UMTS_RSSI] = { "modem.signal.umts.rssi", "rssi", MMC_S_MODEM_SIGNAL_UMTS, }, [MMC_F_SIGNAL_UMTS_RSCP] = { "modem.signal.umts.rscp", "rscp", MMC_S_MODEM_SIGNAL_UMTS, }, [MMC_F_SIGNAL_UMTS_ECIO] = { "modem.signal.umts.ecio", "ecio", MMC_S_MODEM_SIGNAL_UMTS, }, [MMC_F_SIGNAL_UMTS_ERROR_RATE] = { "modem.signal.umts.error-rate", "error rate", MMC_S_MODEM_SIGNAL_UMTS, }, [MMC_F_SIGNAL_LTE_RSSI] = { "modem.signal.lte.rssi", "rssi", MMC_S_MODEM_SIGNAL_LTE, }, [MMC_F_SIGNAL_LTE_RSRQ] = { "modem.signal.lte.rsrq", "rsrq", MMC_S_MODEM_SIGNAL_LTE, }, [MMC_F_SIGNAL_LTE_RSRP] = { "modem.signal.lte.rsrp", "rsrp", MMC_S_MODEM_SIGNAL_LTE, }, [MMC_F_SIGNAL_LTE_SNR] = { "modem.signal.lte.snr", "s/n", MMC_S_MODEM_SIGNAL_LTE, }, [MMC_F_SIGNAL_LTE_ERROR_RATE] = { "modem.signal.lte.error-rate", "error rate", MMC_S_MODEM_SIGNAL_LTE, }, [MMC_F_SIGNAL_5G_RSRQ] = { "modem.signal.5g.rsrq", "rsrq", MMC_S_MODEM_SIGNAL_5G, }, [MMC_F_SIGNAL_5G_RSRP] = { "modem.signal.5g.rsrp", "rsrp", MMC_S_MODEM_SIGNAL_5G, }, [MMC_F_SIGNAL_5G_SNR] = { "modem.signal.5g.snr", "s/n", MMC_S_MODEM_SIGNAL_5G, }, [MMC_F_SIGNAL_5G_ERROR_RATE] = { "modem.signal.5g.error-rate", "error rate", MMC_S_MODEM_SIGNAL_5G, }, [MMC_F_OMA_FEATURES] = { "modem.oma.features", "features", MMC_S_MODEM_OMA, }, [MMC_F_OMA_CURRENT_TYPE] = { "modem.oma.current.type", "type", MMC_S_MODEM_OMA_CURRENT, }, [MMC_F_OMA_CURRENT_STATE] = { "modem.oma.current.state", "state", MMC_S_MODEM_OMA_CURRENT, }, [MMC_F_OMA_PENDING_SESSIONS] = { "modem.oma.pending-sessions", "sessions", MMC_S_MODEM_OMA_PENDING, }, [MMC_F_LOCATION_CAPABILITIES] = { "modem.location.capabilities", "capabilities", MMC_S_MODEM_LOCATION, }, [MMC_F_LOCATION_ENABLED] = { "modem.location.enabled", "enabled", MMC_S_MODEM_LOCATION, }, [MMC_F_LOCATION_SIGNALS] = { "modem.location.signals", "signals", MMC_S_MODEM_LOCATION, }, [MMC_F_LOCATION_GPS_REFRESH_RATE] = { "modem.location.gps.refresh-rate", "refresh rate", MMC_S_MODEM_LOCATION_GPS, }, [MMC_F_LOCATION_GPS_SUPL_SERVER] = { "modem.location.gps.supl-server", "a-gps supl server", MMC_S_MODEM_LOCATION_GPS, }, [MMC_F_LOCATION_GPS_ASSISTANCE] = { "modem.location.gps.assistance", "supported assistance", MMC_S_MODEM_LOCATION_GPS, }, [MMC_F_LOCATION_GPS_ASSISTANCE_SERVERS] = { "modem.location.gps.assistance-servers", "assistance servers", MMC_S_MODEM_LOCATION_GPS, }, [MMC_F_LOCATION_3GPP_MCC] = { "modem.location.3gpp.mcc", "operator mcc", MMC_S_MODEM_LOCATION_3GPP, }, [MMC_F_LOCATION_3GPP_MNC] = { "modem.location.3gpp.mnc", "operator mnc", MMC_S_MODEM_LOCATION_3GPP, }, [MMC_F_LOCATION_3GPP_LAC] = { "modem.location.3gpp.lac", "location area code", MMC_S_MODEM_LOCATION_3GPP, }, [MMC_F_LOCATION_3GPP_TAC] = { "modem.location.3gpp.tac", "tracking area code", MMC_S_MODEM_LOCATION_3GPP, }, [MMC_F_LOCATION_3GPP_CID] = { "modem.location.3gpp.cid", "cell id", MMC_S_MODEM_LOCATION_3GPP, }, [MMC_F_LOCATION_GPS_NMEA] = { "modem.location.gps.nmea", "nmea", MMC_S_MODEM_LOCATION_GPS, }, [MMC_F_LOCATION_GPS_UTC] = { "modem.location.gps.utc", "utc", MMC_S_MODEM_LOCATION_GPS, }, [MMC_F_LOCATION_GPS_LONG] = { "modem.location.gps.longitude", "longitude", MMC_S_MODEM_LOCATION_GPS, }, [MMC_F_LOCATION_GPS_LAT] = { "modem.location.gps.latitude", "latitude", MMC_S_MODEM_LOCATION_GPS, }, [MMC_F_LOCATION_GPS_ALT] = { "modem.location.gps.altitude", "altitude", MMC_S_MODEM_LOCATION_GPS, }, [MMC_F_LOCATION_CDMABS_LONG] = { "modem.location.cdma-bs.longitude", "longitude", MMC_S_MODEM_LOCATION_CDMABS, }, [MMC_F_LOCATION_CDMABS_LAT] = { "modem.location.cdma-bs.latitude", "latitude", MMC_S_MODEM_LOCATION_CDMABS, }, [MMC_F_FIRMWARE_LIST] = { "modem.firmware.list", "list", MMC_S_MODEM_FIRMWARE, }, [MMC_F_FIRMWARE_METHOD] = { "modem.firmware.method", "method", MMC_S_MODEM_FIRMWARE, }, [MMC_F_FIRMWARE_DEVICE_IDS] = { "modem.firmware.device-ids", "device ids", MMC_S_MODEM_FIRMWARE, }, [MMC_F_FIRMWARE_VERSION] = { "modem.firmware.version", "version", MMC_S_MODEM_FIRMWARE, }, [MMC_F_FIRMWARE_FASTBOOT_AT] = { "modem.firmware.fastboot.at", "at command", MMC_S_MODEM_FIRMWARE_FASTBOOT, }, [MMC_F_VOICE_EMERGENCY_ONLY] = { "modem.voice.emergency-only", "emergency only", MMC_S_MODEM_VOICE, }, [MMC_F_BEARER_GENERAL_DBUS_PATH] = { "bearer.dbus-path", "path", MMC_S_BEARER_GENERAL, }, [MMC_F_BEARER_GENERAL_TYPE] = { "bearer.type", "type", MMC_S_BEARER_GENERAL, }, [MMC_F_BEARER_STATUS_CONNECTED] = { "bearer.status.connected", "connected", MMC_S_BEARER_STATUS, }, [MMC_F_BEARER_STATUS_CONNECTION_ERROR_NAME] = { "bearer.status.connection-error.name", "connection error name", MMC_S_BEARER_STATUS, }, [MMC_F_BEARER_STATUS_CONNECTION_ERROR_MESSAGE] = { "bearer.status.connection-error.message", "connection error message", MMC_S_BEARER_STATUS, }, [MMC_F_BEARER_STATUS_SUSPENDED] = { "bearer.status.suspended", "suspended", MMC_S_BEARER_STATUS, }, [MMC_F_BEARER_STATUS_MULTIPLEXED] = { "bearer.status.multiplexed", "multiplexed", MMC_S_BEARER_STATUS, }, [MMC_F_BEARER_STATUS_INTERFACE] = { "bearer.status.interface", "interface", MMC_S_BEARER_STATUS, }, [MMC_F_BEARER_STATUS_IP_TIMEOUT] = { "bearer.status.ip-timeout", "ip timeout", MMC_S_BEARER_STATUS, }, [MMC_F_BEARER_STATUS_PROFILE_ID] = { "bearer.status.profile-id", "profile id", MMC_S_BEARER_STATUS, }, [MMC_F_BEARER_PROPERTIES_APN] = { "bearer.properties.apn", "apn", MMC_S_BEARER_PROPERTIES, }, [MMC_F_BEARER_PROPERTIES_APN_TYPE] = { "bearer.properties.apn-type", "apn type", MMC_S_BEARER_PROPERTIES, }, [MMC_F_BEARER_PROPERTIES_ROAMING] = { "bearer.properties.roaming", "roaming", MMC_S_BEARER_PROPERTIES, }, [MMC_F_BEARER_PROPERTIES_IP_TYPE] = { "bearer.properties.ip-type", "ip type", MMC_S_BEARER_PROPERTIES, }, [MMC_F_BEARER_PROPERTIES_ALLOWED_AUTH] = { "bearer.properties.allowed-auth", "allowed-auth", MMC_S_BEARER_PROPERTIES, }, [MMC_F_BEARER_PROPERTIES_USER] = { "bearer.properties.user", "user", MMC_S_BEARER_PROPERTIES, }, [MMC_F_BEARER_PROPERTIES_PASSWORD] = { "bearer.properties.password", "password", MMC_S_BEARER_PROPERTIES, }, [MMC_F_BEARER_PROPERTIES_PROFILE_ID] = { "bearer.properties.profile-id", "profile id", MMC_S_BEARER_PROPERTIES, }, [MMC_F_BEARER_PROPERTIES_NUMBER] = { "bearer.properties.number", "number", MMC_S_BEARER_PROPERTIES, }, [MMC_F_BEARER_PROPERTIES_RM_PROTOCOL] = { "bearer.properties.rm-protocol", "rm protocol", MMC_S_BEARER_PROPERTIES, }, [MMC_F_BEARER_PROPERTIES_ACCESS_TYPE_PREFERENCE] = { "bearer.properties.access-type-preference", "access type preference", MMC_S_BEARER_PROPERTIES, }, [MMC_F_BEARER_PROPERTIES_ROAMING_ALLOWANCE] = { "bearer.properties.roaming-allowance", "roaming allowance", MMC_S_BEARER_PROPERTIES, }, [MMC_F_BEARER_IPV4_CONFIG_METHOD] = { "bearer.ipv4-config.method", "method", MMC_S_BEARER_IPV4_CONFIG, }, [MMC_F_BEARER_IPV4_CONFIG_ADDRESS] = { "bearer.ipv4-config.address", "address", MMC_S_BEARER_IPV4_CONFIG, }, [MMC_F_BEARER_IPV4_CONFIG_PREFIX] = { "bearer.ipv4-config.prefix", "prefix", MMC_S_BEARER_IPV4_CONFIG, }, [MMC_F_BEARER_IPV4_CONFIG_GATEWAY] = { "bearer.ipv4-config.gateway", "gateway", MMC_S_BEARER_IPV4_CONFIG, }, [MMC_F_BEARER_IPV4_CONFIG_DNS] = { "bearer.ipv4-config.dns", "dns", MMC_S_BEARER_IPV4_CONFIG, }, [MMC_F_BEARER_IPV4_CONFIG_MTU] = { "bearer.ipv4-config.mtu", "mtu", MMC_S_BEARER_IPV4_CONFIG, }, [MMC_F_BEARER_IPV6_CONFIG_METHOD] = { "bearer.ipv6-config.method", "method", MMC_S_BEARER_IPV6_CONFIG, }, [MMC_F_BEARER_IPV6_CONFIG_ADDRESS] = { "bearer.ipv6-config.address", "address", MMC_S_BEARER_IPV6_CONFIG, }, [MMC_F_BEARER_IPV6_CONFIG_PREFIX] = { "bearer.ipv6-config.prefix", "prefix", MMC_S_BEARER_IPV6_CONFIG, }, [MMC_F_BEARER_IPV6_CONFIG_GATEWAY] = { "bearer.ipv6-config.gateway", "gateway", MMC_S_BEARER_IPV6_CONFIG, }, [MMC_F_BEARER_IPV6_CONFIG_DNS] = { "bearer.ipv6-config.dns", "dns", MMC_S_BEARER_IPV6_CONFIG, }, [MMC_F_BEARER_IPV6_CONFIG_MTU] = { "bearer.ipv6-config.mtu", "mtu", MMC_S_BEARER_IPV6_CONFIG, }, [MMC_F_BEARER_STATS_START_DATE] = { "bearer.stats.start-date", "start date", MMC_S_BEARER_STATS, }, [MMC_F_BEARER_STATS_DURATION] = { "bearer.stats.duration", "duration", MMC_S_BEARER_STATS, }, [MMC_F_BEARER_STATS_UPLINK_SPEED] = { "bearer.stats.uplink-speed", "uplink-speed", MMC_S_BEARER_STATS, }, [MMC_F_BEARER_STATS_DOWNLINK_SPEED] = { "bearer.stats.downlink-speed", "downlink-speed", MMC_S_BEARER_STATS, }, [MMC_F_BEARER_STATS_BYTES_RX] = { "bearer.stats.bytes-rx", "bytes rx", MMC_S_BEARER_STATS, }, [MMC_F_BEARER_STATS_BYTES_TX] = { "bearer.stats.bytes-tx", "bytes tx", MMC_S_BEARER_STATS, }, [MMC_F_BEARER_STATS_ATTEMPTS] = { "bearer.stats.attempts", "attempts", MMC_S_BEARER_STATS, }, [MMC_F_BEARER_STATS_FAILED_ATTEMPTS] = { "bearer.stats.failed-attempts", "attempts", MMC_S_BEARER_STATS, }, [MMC_F_BEARER_STATS_TOTAL_DURATION] = { "bearer.stats.total-duration", "total-duration", MMC_S_BEARER_STATS, }, [MMC_F_BEARER_STATS_TOTAL_BYTES_RX] = { "bearer.stats.total-bytes-rx", "total-bytes rx", MMC_S_BEARER_STATS, }, [MMC_F_BEARER_STATS_TOTAL_BYTES_TX] = { "bearer.stats.total-bytes-tx", "total-bytes tx", MMC_S_BEARER_STATS, }, [MMC_F_CALL_GENERAL_DBUS_PATH] = { "call.dbus-path", "path", MMC_S_CALL_GENERAL, }, [MMC_F_CALL_PROPERTIES_NUMBER] = { "call.properties.number", "number", MMC_S_CALL_PROPERTIES, }, [MMC_F_CALL_PROPERTIES_DIRECTION] = { "call.properties.direction", "direction", MMC_S_CALL_PROPERTIES, }, [MMC_F_CALL_PROPERTIES_MULTIPARTY] = { "call.properties.multiparty", "multiparty", MMC_S_CALL_PROPERTIES, }, [MMC_F_CALL_PROPERTIES_STATE] = { "call.properties.state", "state", MMC_S_CALL_PROPERTIES, }, [MMC_F_CALL_PROPERTIES_STATE_REASON] = { "call.properties.state-reason", "state reason", MMC_S_CALL_PROPERTIES, }, [MMC_F_CALL_PROPERTIES_AUDIO_PORT] = { "call.properties.audio-port", "audio port", MMC_S_CALL_PROPERTIES, }, [MMC_F_CALL_AUDIO_FORMAT_ENCODING] = { "call.audio-format.encoding", "encoding", MMC_S_CALL_AUDIO_FORMAT, }, [MMC_F_CALL_AUDIO_FORMAT_RESOLUTION] = { "call.audio-format.resolution", "resolution", MMC_S_CALL_AUDIO_FORMAT, }, [MMC_F_CALL_AUDIO_FORMAT_RATE] = { "call.audio-format.rate", "rate", MMC_S_CALL_AUDIO_FORMAT, }, [MMC_F_SMS_GENERAL_DBUS_PATH] = { "sms.dbus-path", "path", MMC_S_SMS_GENERAL, }, [MMC_F_SMS_CONTENT_NUMBER] = { "sms.content.number", "number", MMC_S_SMS_CONTENT, }, [MMC_F_SMS_CONTENT_TEXT] = { "sms.content.text", "text", MMC_S_SMS_CONTENT, }, [MMC_F_SMS_CONTENT_DATA] = { "sms.content.data", "data", MMC_S_SMS_CONTENT, }, [MMC_F_SMS_PROPERTIES_PDU_TYPE] = { "sms.properties.pdu-type", "pdu type", MMC_S_SMS_PROPERTIES, }, [MMC_F_SMS_PROPERTIES_STATE] = { "sms.properties.state", "state", MMC_S_SMS_PROPERTIES, }, [MMC_F_SMS_PROPERTIES_VALIDITY] = { "sms.properties.validity", "validity", MMC_S_SMS_PROPERTIES, }, [MMC_F_SMS_PROPERTIES_STORAGE] = { "sms.properties.storage", "storage", MMC_S_SMS_PROPERTIES, }, [MMC_F_SMS_PROPERTIES_SMSC] = { "sms.properties.smsc", "smsc", MMC_S_SMS_PROPERTIES, }, [MMC_F_SMS_PROPERTIES_CLASS] = { "sms.properties.class", "class", MMC_S_SMS_PROPERTIES, }, [MMC_F_SMS_PROPERTIES_TELESERVICE_ID] = { "sms.properties.teleservice-id", "teleservice id", MMC_S_SMS_PROPERTIES, }, [MMC_F_SMS_PROPERTIES_SERVICE_CATEGORY] = { "sms.properties.service-category", "service category", MMC_S_SMS_PROPERTIES, }, [MMC_F_SMS_PROPERTIES_DELIVERY_REPORT] = { "sms.properties.delivery-report", "delivery report", MMC_S_SMS_PROPERTIES, }, [MMC_F_SMS_PROPERTIES_MSG_REFERENCE] = { "sms.properties.message-reference", "message reference", MMC_S_SMS_PROPERTIES, }, [MMC_F_SMS_PROPERTIES_TIMESTAMP] = { "sms.properties.timestamp", "timestamp", MMC_S_SMS_PROPERTIES, }, [MMC_F_SMS_PROPERTIES_DELIVERY_STATE] = { "sms.properties.delivery-state", "delivery state", MMC_S_SMS_PROPERTIES, }, [MMC_F_SMS_PROPERTIES_DISCH_TIMESTAMP] = { "sms.properties.discharge-timestamp", "discharge timestamp", MMC_S_SMS_PROPERTIES, }, [MMC_F_SIM_GENERAL_DBUS_PATH] = { "sim.dbus-path", "path", MMC_S_SIM_GENERAL, }, [MMC_F_SIM_PROPERTIES_ACTIVE] = { "sim.properties.active", "active", MMC_S_SIM_PROPERTIES, }, [MMC_F_SIM_PROPERTIES_IMSI] = { "sim.properties.imsi", "imsi", MMC_S_SIM_PROPERTIES, }, [MMC_F_SIM_PROPERTIES_ICCID] = { "sim.properties.iccid", "iccid", MMC_S_SIM_PROPERTIES, }, [MMC_F_SIM_PROPERTIES_EID] = { "sim.properties.eid", "eid", MMC_S_SIM_PROPERTIES, }, [MMC_F_SIM_PROPERTIES_GID1] = { "sim.properties.gid1", "gid1", MMC_S_SIM_PROPERTIES, }, [MMC_F_SIM_PROPERTIES_GID2] = { "sim.properties.gid2", "gid2", MMC_S_SIM_PROPERTIES, }, [MMC_F_SIM_PROPERTIES_OPERATOR_ID] = { "sim.properties.operator-code", "operator id", MMC_S_SIM_PROPERTIES, }, [MMC_F_SIM_PROPERTIES_OPERATOR_NAME] = { "sim.properties.operator-name", "operator name", MMC_S_SIM_PROPERTIES, }, [MMC_F_SIM_PROPERTIES_EMERGENCY_NUMBERS] = { "sim.properties.emergency-numbers", "emergency numbers", MMC_S_SIM_PROPERTIES, }, [MMC_F_SIM_PROPERTIES_PREFERRED_NETWORKS] = { "sim.properties.preferred-networks", "preferred networks", MMC_S_SIM_PROPERTIES, }, [MMC_F_SIM_PROPERTIES_SIM_TYPE] = { "sim.properties.sim-type", "type", MMC_S_SIM_PROPERTIES, }, [MMC_F_SIM_PROPERTIES_ESIM_STATUS] = { "sim.properties.esim-status", "esim status", MMC_S_SIM_PROPERTIES, }, [MMC_F_SIM_PROPERTIES_REMOVABILITY] = { "sim.properties.removability", "removability", MMC_S_SIM_PROPERTIES, }, [MMC_F_SAR_STATE] = { "modem.sar.state", "enabled", MMC_S_MODEM_SAR, }, [MMC_F_SAR_POWER_LEVEL] = { "modem.sar.power-level", "power level", MMC_S_MODEM_SAR, }, [MMC_F_MODEM_LIST_DBUS_PATH] = { "modem-list", "modems", MMC_S_UNKNOWN, }, [MMC_F_SMS_LIST_DBUS_PATH] = { "modem.messaging.sms", "sms messages", MMC_S_UNKNOWN, }, [MMC_F_CALL_LIST_DBUS_PATH] = { "modem.voice.call", "calls", MMC_S_UNKNOWN, }, }; /******************************************************************************/ /* Output type selection */ static MmcOutputType selected_type = MMC_OUTPUT_TYPE_NONE; void mmcli_output_set (MmcOutputType type) { selected_type = type; } MmcOutputType mmcli_output_get (void) { return selected_type; } /******************************************************************************/ /* Generic output management */ typedef enum { VALUE_TYPE_SINGLE, VALUE_TYPE_MULTIPLE, VALUE_TYPE_LISTITEM, } ValueType; typedef struct { MmcF field; ValueType type; } OutputItem; typedef struct { OutputItem base; gchar *value; } OutputItemSingle; typedef struct { OutputItem base; gchar **values; gboolean multiline; gboolean is_object; } OutputItemMultiple; typedef struct { OutputItem base; gchar *prefix; gchar *value; gchar *extra; } OutputItemListitem; static GList *output_items; static void output_item_free (OutputItem *item) { switch (item->type) { case VALUE_TYPE_SINGLE: g_free (((OutputItemSingle *)item)->value); g_slice_free (OutputItemSingle, (OutputItemSingle *)item); break; case VALUE_TYPE_MULTIPLE: g_strfreev (((OutputItemMultiple *)item)->values); g_slice_free (OutputItemMultiple, (OutputItemMultiple *)item); break; case VALUE_TYPE_LISTITEM: g_free (((OutputItemListitem *)item)->prefix); g_free (((OutputItemListitem *)item)->value); g_free (((OutputItemListitem *)item)->extra); g_slice_free (OutputItemListitem, (OutputItemListitem *)item); break; default: g_assert_not_reached (); } } static gboolean filter_out_value (const gchar *value) { return (!g_strcmp0 (value, "unknown") || !g_strcmp0 (value, "none")); } static void output_item_new_take_single (MmcF field, gchar *value) { OutputItemSingle *item; item = g_slice_new0 (OutputItemSingle); item->base.field = field; item->base.type = VALUE_TYPE_SINGLE; if (filter_out_value (value)) g_free (value); else item->value = value; output_items = g_list_prepend (output_items, item); } static void output_item_new_take_multiple (MmcF field, gchar **values, gboolean multiline, gboolean is_object) { OutputItemMultiple *item; item = g_slice_new0 (OutputItemMultiple); item->base.field = field; item->base.type = VALUE_TYPE_MULTIPLE; item->multiline = multiline; item->is_object = is_object; if (values && (g_strv_length (values) == 1) && filter_out_value (values[0])) g_strfreev (values); else item->values = values; output_items = g_list_prepend (output_items, item); } static void output_item_new_take_listitem (MmcF field, gchar *prefix, gchar *value, gchar *extra) { OutputItemListitem *item; item = g_slice_new0 (OutputItemListitem); item->base.field = field; item->base.type = VALUE_TYPE_LISTITEM; item->prefix = prefix; item->value = value; item->extra = extra; output_items = g_list_prepend (output_items, item); } void mmcli_output_string_list (MmcF field, const gchar *str) { gchar **split; split = str ? g_strsplit (str, ",", -1) : NULL; if (split) { guint i; for (i = 0; split[i]; i++) g_strstrip (split[i]); } output_item_new_take_multiple (field, split, FALSE, FALSE); } void mmcli_output_string_list_take (MmcF field, gchar *str) { mmcli_output_string_list (field, str); g_free (str); } void mmcli_output_string_multiline (MmcF field, const gchar *str) { gchar **split; split = str ? g_strsplit (str, "\n", -1) : NULL; if (split) { guint i; for (i = 0; split[i]; i++) g_strstrip (split[i]); } output_item_new_take_multiple (field, split, TRUE, FALSE); } void mmcli_output_string_multiline_take (MmcF field, gchar *str) { mmcli_output_string_multiline (field, str); g_free (str); } void mmcli_output_string_array (MmcF field, const gchar **strv, gboolean multiline) { output_item_new_take_multiple (field, g_strdupv ((gchar **)strv), multiline, FALSE); } void mmcli_output_string_array_take (MmcF field, gchar **strv, gboolean multiline) { output_item_new_take_multiple (field, strv, multiline, FALSE); } void mmcli_output_string_array_multiline_take (MmcF field, gchar **strv) { gchar **merged; GPtrArray *pointers; merged = NULL; if (strv) { guint i; pointers = g_ptr_array_new (); for (i = 0; strv[i]; i++) { gchar **split; split = strv[i] ? g_strsplit (strv[i], "\n", -1) : NULL; if (split) { guint n; for (n = 0; split[n]; n++) { if (split[n][0]) { g_strstrip (split[n]); g_ptr_array_add (pointers, g_strdup (split[n])); } } g_strfreev (split); } } g_strfreev (strv); g_ptr_array_add (pointers, NULL); merged = (gchar **)g_ptr_array_free (pointers, FALSE); } output_item_new_take_multiple (field, merged, TRUE, FALSE); } void mmcli_output_string (MmcF field, const gchar *str) { output_item_new_take_single (field, g_strdup (str)); } void mmcli_output_string_take (MmcF field, gchar *str) { output_item_new_take_single (field, str); } void mmcli_output_string_take_typed (MmcF field, gchar *value, const gchar *type) { if (value && selected_type == MMC_OUTPUT_TYPE_HUMAN) { gchar *aux; aux = g_strdup_printf ("%s %s", value, type); g_free (value); output_item_new_take_single (field, aux); return; } output_item_new_take_single (field, value); } void mmcli_output_listitem (MmcF field, const gchar *prefix, const gchar *value, const gchar *extra) { output_item_new_take_listitem (field, g_strdup (prefix), g_strdup (value), g_strdup (extra)); } /******************************************************************************/ /* (Custom) Signal quality output */ void mmcli_output_signal_quality (MMModemState state, guint value, gboolean recent) { /* Merge value and recent flag in a single item in human output */ if (selected_type == MMC_OUTPUT_TYPE_HUMAN) { if (state >= MM_MODEM_STATE_ENABLED) output_item_new_take_single (MMC_F_STATUS_SIGNAL_QUALITY_VALUE, g_strdup_printf ("%u%% (%s)", value, recent ? "recent" : "cached")); else output_item_new_take_single (MMC_F_STATUS_SIGNAL_QUALITY_VALUE, NULL); return; } output_item_new_take_single (MMC_F_STATUS_SIGNAL_QUALITY_VALUE, g_strdup_printf ("%u", value)); output_item_new_take_single (MMC_F_STATUS_SIGNAL_QUALITY_RECENT, g_strdup_printf ("%s", recent ? "yes" : "no")); } /******************************************************************************/ /* (Custom) Bearer start date output */ void mmcli_output_start_date (guint64 value) { /* Merge value and recent flag in a single item in human output */ if (selected_type == MMC_OUTPUT_TYPE_HUMAN) { output_item_new_take_single (MMC_F_BEARER_STATS_START_DATE, mm_new_iso8601_time_from_unix_time (value, NULL)); return; } output_item_new_take_single (MMC_F_BEARER_STATS_START_DATE, g_strdup_printf ("%" G_GUINT64_FORMAT, value)); } /******************************************************************************/ /* (Custom) State output */ void mmcli_output_state (MMModemState state, MMModemStateFailedReason reason) { #define KNRM "\x1B[0m" #define KRED "\x1B[31m" #define KGRN "\x1B[32m" #define KYEL "\x1B[33m" if (selected_type == MMC_OUTPUT_TYPE_HUMAN) { if (state == MM_MODEM_STATE_FAILED) output_item_new_take_single (MMC_F_STATUS_STATE, g_strdup_printf (KRED "%s" KNRM, mm_modem_state_get_string (state))); else if (state == MM_MODEM_STATE_CONNECTED) output_item_new_take_single (MMC_F_STATUS_STATE, g_strdup_printf (KGRN "%s" KNRM, mm_modem_state_get_string (state))); else if (state == MM_MODEM_STATE_CONNECTING) output_item_new_take_single (MMC_F_STATUS_STATE, g_strdup_printf (KYEL "%s" KNRM, mm_modem_state_get_string (state))); else output_item_new_take_single (MMC_F_STATUS_STATE, g_strdup (mm_modem_state_get_string (state))) ; if (state == MM_MODEM_STATE_FAILED) output_item_new_take_single (MMC_F_STATUS_FAILED_REASON, g_strdup_printf (KRED "%s" KNRM, mm_modem_state_failed_reason_get_string (reason))); return; } output_item_new_take_single (MMC_F_STATUS_STATE, g_strdup (mm_modem_state_get_string (state))); output_item_new_take_single (MMC_F_STATUS_FAILED_REASON, (state == MM_MODEM_STATE_FAILED) ? g_strdup (mm_modem_state_failed_reason_get_string (reason)) : NULL); } /******************************************************************************/ /* (Custom) SIM slots output */ void mmcli_output_sim_slots (gchar **sim_slot_paths, guint primary_sim_slot) { guint i; if (selected_type != MMC_OUTPUT_TYPE_HUMAN || !sim_slot_paths) { mmcli_output_string_take (MMC_F_SIM_PRIMARY_SLOT, primary_sim_slot ? g_strdup_printf ("%u", primary_sim_slot) : NULL); mmcli_output_string_array_take (MMC_F_SIM_SLOT_PATHS, sim_slot_paths ? sim_slot_paths : NULL, TRUE); return; } /* Include SIM slot number in each item */ for (i = 0; sim_slot_paths[i]; i++) { gchar *aux; guint slot_number = i + 1; aux = g_strdup_printf ("slot %u: %s%s", slot_number, g_str_equal (sim_slot_paths[i], "/") ? "none" : sim_slot_paths[i], (primary_sim_slot == slot_number) ? " (active)" : ""); g_free (sim_slot_paths[i]); sim_slot_paths[i] = aux; } mmcli_output_string_array_take (MMC_F_SIM_SLOT_PATHS, sim_slot_paths, TRUE); } /******************************************************************************/ /* (Custom) Network scan output */ static gchar *json_strescape (const gchar *str); static gchar * build_network_info (MMModem3gppNetwork *network) { const gchar *operator_code; const gchar *operator_name; gchar *access_technologies; const gchar *availability; gchar *out; operator_code = mm_modem_3gpp_network_get_operator_code (network); operator_name = mm_modem_3gpp_network_get_operator_long (network); if (!operator_name) operator_name = mm_modem_3gpp_network_get_operator_short (network); access_technologies = mm_modem_access_technology_build_string_from_mask (mm_modem_3gpp_network_get_access_technology (network)); availability = mm_modem_3gpp_network_availability_get_string (mm_modem_3gpp_network_get_availability (network)); if (selected_type == MMC_OUTPUT_TYPE_HUMAN) { out = g_strdup_printf ("%s - %s (%s, %s)", operator_code ? operator_code : "code n/a", operator_name ? operator_name : "name n/a", access_technologies, availability); } else if (selected_type == MMC_OUTPUT_TYPE_JSON) { GString *str; gchar *escape; str = g_string_new (NULL); g_string_append_printf (str, "{"); escape = json_strescape (operator_code); g_string_append_printf (str, "\"operator-code\":\"%s\"", escape ? escape: "--"); g_free (escape); g_string_append_printf (str, ","); escape = json_strescape (operator_name); g_string_append_printf (str, "\"operator-name\":\"%s\"", escape ? escape: "--"); g_free (escape); g_string_append_printf (str, ","); escape = json_strescape (access_technologies); g_string_append_printf (str, "\"access-technologies\":\"%s\"", escape); g_free (escape); g_string_append_printf (str, ","); escape = json_strescape (availability); g_string_append_printf (str, "\"availability\":\"%s\"", escape); g_free (escape); g_string_append_printf (str, "}"); out = g_string_free (str, FALSE); } else { out = g_strdup_printf ("operator-code: %s, operator-name: %s, access-technologies: %s, availability: %s", operator_code ? operator_code : "--", operator_name ? operator_name : "--", access_technologies, availability); } g_free (access_technologies); return out; } void mmcli_output_scan_networks (GList *network_list) { gchar **networks = NULL; if (network_list) { GPtrArray *aux; GList *l; aux = g_ptr_array_new (); for (l = network_list; l; l = g_list_next (l)) g_ptr_array_add (aux, build_network_info ((MMModem3gppNetwork *)(l->data))); g_ptr_array_add (aux, NULL); networks = (gchar **) g_ptr_array_free (aux, FALSE); } /* When printing human result, we want to show some result even if no networks * are found, so we force a explicit string result. */ if (selected_type == MMC_OUTPUT_TYPE_HUMAN && !networks) output_item_new_take_single (MMC_F_3GPP_SCAN_NETWORKS, g_strdup ("n/a")); else output_item_new_take_multiple (MMC_F_3GPP_SCAN_NETWORKS, networks, TRUE, TRUE); } /******************************************************************************/ /* (Custom) Firmware list output */ static void build_firmware_info_human (GPtrArray *array, MMFirmwareProperties *props, gboolean selected) { g_ptr_array_add (array, g_strdup (mm_firmware_properties_get_unique_id (props))); g_ptr_array_add (array, g_strdup_printf (" current: %s", selected ? "yes" : "no")); if (mm_firmware_properties_get_image_type (props) == MM_FIRMWARE_IMAGE_TYPE_GOBI) { const gchar *aux; if ((aux = mm_firmware_properties_get_gobi_pri_version (props)) != NULL) g_ptr_array_add (array, g_strdup_printf (" gobi pri version: %s", aux)); if ((aux = mm_firmware_properties_get_gobi_pri_info (props)) != NULL) g_ptr_array_add (array, g_strdup_printf (" gobi pri info: %s", aux)); if ((aux = mm_firmware_properties_get_gobi_boot_version (props)) != NULL) g_ptr_array_add (array, g_strdup_printf (" gobi boot version: %s", aux)); if ((aux = mm_firmware_properties_get_gobi_pri_unique_id (props)) != NULL) g_ptr_array_add (array, g_strdup_printf (" gobi pri unique id: %s", aux)); if ((aux = mm_firmware_properties_get_gobi_modem_unique_id (props)) != NULL) g_ptr_array_add (array, g_strdup_printf (" gobi modem unique id: %s", aux)); } } static void build_firmware_info_keyvalue (GPtrArray *array, MMFirmwareProperties *props, gboolean selected) { GString *str; str = g_string_new (""); g_string_append_printf (str, "unique-id: %s", mm_firmware_properties_get_unique_id (props)); g_string_append_printf (str, ", current: %s", selected ? "yes" : "no"); if (mm_firmware_properties_get_image_type (props) == MM_FIRMWARE_IMAGE_TYPE_GOBI) { const gchar *aux; if ((aux = mm_firmware_properties_get_gobi_pri_version (props)) != NULL) g_string_append_printf (str, ", gobi-pri-version: %s", aux); if ((aux = mm_firmware_properties_get_gobi_pri_info (props)) != NULL) g_string_append_printf (str, ", gobi-pri-info: %s", aux); if ((aux = mm_firmware_properties_get_gobi_boot_version (props)) != NULL) g_string_append_printf (str, ", gobi-boot-version: %s", aux); if ((aux = mm_firmware_properties_get_gobi_pri_unique_id (props)) != NULL) g_string_append_printf (str, ", gobi-pri-unique id: %s", aux); if ((aux = mm_firmware_properties_get_gobi_modem_unique_id (props)) != NULL) g_string_append_printf (str, ", gobi-modem-unique id: %s", aux); } g_ptr_array_add (array, g_string_free (str, FALSE)); } void mmcli_output_firmware_list (GList *firmware_list, MMFirmwareProperties *selected) { gchar **firmwares = NULL; if (firmware_list) { GPtrArray *aux; GList *l; aux = g_ptr_array_new (); for (l = firmware_list; l; l = g_list_next (l)) { MMFirmwareProperties *props = (MMFirmwareProperties *)(l->data); gboolean current_selected; current_selected = (selected && g_str_equal (mm_firmware_properties_get_unique_id (props), mm_firmware_properties_get_unique_id (selected))); if (selected_type == MMC_OUTPUT_TYPE_HUMAN) build_firmware_info_human (aux, props, current_selected); else build_firmware_info_keyvalue (aux, props, current_selected); } g_ptr_array_add (aux, NULL); firmwares = (gchar **) g_ptr_array_free (aux, FALSE); } /* When printing human result, we want to show some result even if no firmwares * are found, so we force a explicit string result. */ if (selected_type == MMC_OUTPUT_TYPE_HUMAN && !firmwares) output_item_new_take_single (MMC_F_FIRMWARE_LIST, g_strdup ("n/a")); else output_item_new_take_multiple (MMC_F_FIRMWARE_LIST, firmwares, TRUE, FALSE); } /******************************************************************************/ /* (Custom) PCO list output */ void mmcli_output_pco_list (GList *pco_list) { GPtrArray *aux; GList *l; if (!pco_list) { output_item_new_take_single (MMC_F_3GPP_PCO, NULL); return; } aux = g_ptr_array_new (); for (l = pco_list; l; l = g_list_next (l)) { MMPco *pco; gchar *pco_data_hex; const guint8 *pco_data; gsize pco_data_size; pco = MM_PCO (l->data); pco_data = mm_pco_get_data (pco, &pco_data_size); pco_data_hex = (pco_data ? mm_utils_bin2hexstr (pco_data, pco_data_size) : NULL); if (selected_type == MMC_OUTPUT_TYPE_HUMAN) g_ptr_array_add (aux, g_strdup_printf ("%u: (%s) '%s'", mm_pco_get_session_id (pco), mm_pco_is_complete (pco) ? "complete" : "partial", pco_data_hex ? pco_data_hex : "")); else g_ptr_array_add (aux, g_strdup_printf ("session-id: %u, complete: %s, data: %s", mm_pco_get_session_id (pco), mm_pco_is_complete (pco) ? "yes" : "no", pco_data_hex ? pco_data_hex : "")); g_free (pco_data_hex); } g_ptr_array_add (aux, NULL); output_item_new_take_multiple (MMC_F_3GPP_PCO, (gchar **) g_ptr_array_free (aux, FALSE), TRUE, FALSE); } /******************************************************************************/ /* (Custom) Preferred networks output */ void mmcli_output_preferred_networks (GList *preferred_nets_list) { if (preferred_nets_list) { GPtrArray *aux; aux = g_ptr_array_new (); for (;preferred_nets_list; preferred_nets_list = g_list_next (preferred_nets_list)) { const MMSimPreferredNetwork *preferred_net; gchar *access_technologies; gchar *out; const gchar *operator_code; preferred_net = (const MMSimPreferredNetwork *)preferred_nets_list->data; operator_code = mm_sim_preferred_network_get_operator_code (preferred_net); access_technologies = mm_modem_access_technology_build_string_from_mask (mm_sim_preferred_network_get_access_technology (preferred_net)); if (selected_type == MMC_OUTPUT_TYPE_HUMAN) out = g_strdup_printf ("%s (%s)", operator_code, access_technologies); else out = g_strdup_printf ("operator-code: %s, access-technologies: %s", operator_code, access_technologies); g_ptr_array_add (aux, out); g_free (access_technologies); } g_ptr_array_add (aux, NULL); mmcli_output_string_array_take (MMC_F_SIM_PROPERTIES_PREFERRED_NETWORKS, (gchar **) g_ptr_array_free (aux, FALSE), TRUE); } } /******************************************************************************/ /* (Custom) Profile list output */ static void build_profile_human (GPtrArray *array, MM3gppProfile *profile) { g_autoptr(GPtrArray) profile_print = NULL; guint i; profile_print = mm_3gpp_profile_print (profile, TRUE); mm_common_str_array_human_keys (profile_print); for (i = 0; i < profile_print->len; i++) { /* First string is profile id always, all the remaining ones will be * indented some whitespaces right */ g_ptr_array_add (array, g_strdup_printf ("%s%s", i == 0 ? "" : " ", (const gchar *)g_ptr_array_index (profile_print, i))); } } static void build_profile_keyvalue (GPtrArray *array, MM3gppProfile *profile) { g_autoptr(GPtrArray) profile_print = NULL; guint i; GString *str; str = g_string_new (""); profile_print = mm_3gpp_profile_print (profile, TRUE); for (i = 0; i < profile_print->len; i++) { g_string_append_printf (str, "%s%s", i == 0 ? "" : ", ", (const gchar *)g_ptr_array_index (profile_print, i)); } g_ptr_array_add (array, g_string_free (str, FALSE)); } static void output_profile_list (MmcF field, GList *profile_list) { gchar **profiles = NULL; if (profile_list) { GPtrArray *aux; GList *l; aux = g_ptr_array_new (); for (l = profile_list; l; l = g_list_next (l)) { MM3gppProfile *profile = (MM3gppProfile *)(l->data); if (selected_type == MMC_OUTPUT_TYPE_HUMAN) build_profile_human (aux, profile); else build_profile_keyvalue (aux, profile); } g_ptr_array_add (aux, NULL); profiles = (gchar **) g_ptr_array_free (aux, FALSE); } /* When printing human result, we want to show some result even if no profiles * are found, so we force a explicit string result. */ if (selected_type == MMC_OUTPUT_TYPE_HUMAN && !profiles) output_item_new_take_single (field, g_strdup ("n/a")); else output_item_new_take_multiple (field, profiles, TRUE, FALSE); } void mmcli_output_profile_list (GList *profile_list) { output_profile_list (MMC_F_3GPP_PROFILE_MANAGER_LIST, profile_list); } void mmcli_output_profile_set (MM3gppProfile *profile) { GList *profile_list = NULL; profile_list = g_list_append (profile_list, profile); output_profile_list (MMC_F_3GPP_PROFILE_MANAGER_SET, profile_list); g_list_free (profile_list); } /******************************************************************************/ /* (Custom) Cell info output */ void mmcli_output_cell_info (GList *cell_info_list) { gchar **cell_infos = NULL; if (cell_info_list) { GPtrArray *aux; GList *l; aux = g_ptr_array_new (); for (l = cell_info_list; l; l = g_list_next (l)) g_ptr_array_add (aux, mm_cell_info_build_string (MM_CELL_INFO (l->data))); g_ptr_array_add (aux, NULL); cell_infos = (gchar **) g_ptr_array_free (aux, FALSE); } /* When printing human result, we want to show some result even if no networks * are found, so we force a explicit string result. */ if (selected_type == MMC_OUTPUT_TYPE_HUMAN && !cell_infos) output_item_new_take_single (MMC_F_CELL_INFO, g_strdup ("n/a")); else output_item_new_take_multiple (MMC_F_CELL_INFO, cell_infos, TRUE, FALSE); } /******************************************************************************/ /* Human-friendly output */ #define HUMAN_MAX_VALUE_LENGTH 60 static gint list_sort_human (const OutputItem *item_a, const OutputItem *item_b) { if (field_infos[item_a->field].section < field_infos[item_b->field].section) return -1; if (field_infos[item_a->field].section > field_infos[item_b->field].section) return 1; return item_a->field - item_b->field; } static void dump_output_human (void) { GList *l; MmcS current_section = MMC_S_UNKNOWN; guint longest_section_name = 0; guint longest_field_name = 0; output_items = g_list_sort (output_items, (GCompareFunc) list_sort_human); /* First pass to process */ for (l = output_items; l; l = g_list_next (l)) { OutputItem *item_l; guint aux; gboolean ignore = FALSE; item_l = (OutputItem *)(l->data); /* Post-process values */ if (item_l->type == VALUE_TYPE_SINGLE) { OutputItemSingle *single = (OutputItemSingle *)item_l; if (!single->value) ignore = TRUE; } else if (item_l->type == VALUE_TYPE_MULTIPLE) { OutputItemMultiple *multiple = (OutputItemMultiple *)item_l; if (!multiple->values) ignore = TRUE; } /* Compute max lengths */ if (!ignore) { aux = strlen (section_infos[field_infos[item_l->field].section].name); if (aux > longest_section_name) longest_section_name = aux; aux = strlen (field_infos[item_l->field].name); if (aux > longest_field_name) longest_field_name = aux; } } /* Second pass to print */ for (l = output_items; l; l = g_list_next (l)) { OutputItem *item_l; OutputItemSingle *single = NULL; OutputItemMultiple *multiple = NULL; item_l = (OutputItem *)(l->data); if (item_l->type == VALUE_TYPE_SINGLE) single = (OutputItemSingle *)item_l; else if (item_l->type == VALUE_TYPE_MULTIPLE) multiple = (OutputItemMultiple *)item_l; else g_assert_not_reached (); /* Ignore items without a value set */ if ((single && (!single->value || !single->value[0])) || (multiple && (!multiple->values || !g_strv_length (multiple->values)))) continue; /* Section change? */ if (field_infos[item_l->field].section != current_section) { current_section = field_infos[item_l->field].section; g_print (" %.*s\n", longest_section_name + longest_field_name + 4, "------------------------------------------------------------"); g_print (" %-*.*s | ", longest_section_name, longest_section_name, section_infos[field_infos[item_l->field].section].name); } else g_print (" %*.*s | ", longest_section_name, longest_section_name, ""); g_print ("%*.*s: ", longest_field_name, longest_field_name, field_infos[item_l->field].name); if (single) { gchar **split_lines; guint i; split_lines = g_strsplit (single->value, "\n", -1); for (i = 0; split_lines[i]; i++) { if (i != 0) { g_print (" %*.*s | %*.*s ", longest_section_name, longest_section_name, "", longest_field_name, longest_field_name, ""); } g_print ("%s\n", split_lines[i]); } g_strfreev (split_lines); } else if (multiple) { guint line_length = 0; guint n, i; n = multiple->values ? g_strv_length (multiple->values) : 0; for (i = 0; i < n; i++) { const gchar *value; guint value_length; value = multiple->values[i]; value_length = strlen (value) + ((i < (n - 1)) ? 2 : 0); if ((multiple->multiline && i != 0) || ((line_length + value_length) > HUMAN_MAX_VALUE_LENGTH)) { line_length = 0; g_print ("\n" " %*.*s | %*.*s ", longest_section_name, longest_section_name, "", longest_field_name, longest_field_name, ""); } else line_length += value_length; g_print ("%s%s", value, (!multiple->multiline && i < (n - 1)) ? ", " : ""); } g_print ("\n"); } } } static void dump_output_list_human (MmcF field) { GList *l; guint n; g_assert (field != MMC_F_UNKNOWN); /* First pass to process */ for (n = 0, l = output_items; l; l = g_list_next (l), n++) { OutputItem *item_l; OutputItemListitem *listitem; item_l = (OutputItem *)(l->data); g_assert (item_l->type == VALUE_TYPE_LISTITEM); listitem = (OutputItemListitem *)item_l; g_assert (listitem->value); /* All items must be of same type */ g_assert_cmpint (item_l->field, ==, field); } /* Second pass to print */ if (n == 0) { g_print ("No %s were found\n", field_infos[field].name); return; } for (l = output_items; l; l = g_list_next (l)) { OutputItemListitem *listitem; listitem = (OutputItemListitem *)(l->data); g_print ("%s%s %s\n", listitem->prefix, listitem->value, listitem->extra); } } /******************************************************************************/ /* Key-value output */ #define KEY_ARRAY_LENGTH_SUFFIX ".length" #define KEY_ARRAY_VALUE_SUFFIX ".value" static gint list_sort_keyvalue (const OutputItem *item_a, const OutputItem *item_b) { return item_a->field - item_b->field; } static void dump_output_keyvalue (void) { GList *l; guint longest_field_key = 0; output_items = g_list_sort (output_items, (GCompareFunc) list_sort_keyvalue); /* First pass to process */ for (l = output_items; l; l = g_list_next (l)) { OutputItem *item_l; OutputItemMultiple *multiple = NULL; guint key_length; item_l = (OutputItem *)(l->data); if (item_l->type == VALUE_TYPE_MULTIPLE) multiple = (OutputItemMultiple *)item_l; key_length = strlen (field_infos[item_l->field].key); /* when printing array contents, each item is given with an index, * e.g.: something.value[1] * The max length of the field will need to consider the array length * in order to accommodate the length of the index. */ if (multiple) { guint n; n = multiple->values ? g_strv_length (multiple->values) : 0; if (n > 0) { guint aux = n; key_length += ((strlen (KEY_ARRAY_VALUE_SUFFIX)) + 3); while ((aux /= 10) > 0) key_length++; } } if (key_length > longest_field_key) longest_field_key = key_length; } /* Second pass to print */ for (l = output_items; l; l = g_list_next (l)) { OutputItem *item_l; OutputItemSingle *single = NULL; OutputItemMultiple *multiple = NULL; item_l = (OutputItem *)(l->data); if (item_l->type == VALUE_TYPE_SINGLE) single = (OutputItemSingle *)item_l; else if (item_l->type == VALUE_TYPE_MULTIPLE) multiple = (OutputItemMultiple *)item_l; else g_assert_not_reached (); if (single) { gchar *escaped = NULL; if (single->value) escaped = g_strescape (single->value, NULL); g_print ("%-*.*s : %s\n", longest_field_key, longest_field_key, field_infos[item_l->field].key, escaped ? escaped : "--"); g_free (escaped); } else if (multiple) { guint n; n = multiple->values ? g_strv_length (multiple->values) : 0; if (n > 0) { guint i; gchar *new_key; new_key = g_strdup_printf ("%s" KEY_ARRAY_LENGTH_SUFFIX, field_infos[item_l->field].key); g_print ("%-*.*s : %u\n", longest_field_key, longest_field_key, new_key, n); g_free (new_key); for (i = 0; i < n; i++) { gchar *escaped = NULL; /* Printed indices start at 1 */ new_key = g_strdup_printf ("%s" KEY_ARRAY_VALUE_SUFFIX "[%u]", field_infos[item_l->field].key, i + 1); escaped = g_strescape (multiple->values[i], NULL); g_print ("%-*.*s : %s\n", longest_field_key, longest_field_key, new_key, escaped); g_free (escaped); g_free (new_key); } } else g_print ("%-*.*s : --\n", longest_field_key, longest_field_key, field_infos[item_l->field].key); } } } static void dump_output_list_keyvalue (MmcF field) { GList *l; guint key_length; guint n; gchar *new_key; g_assert (field != MMC_F_UNKNOWN); key_length = strlen (field_infos[field].key); /* First pass to process */ for (n = 0, l = output_items; l; l = g_list_next (l), n++) { OutputItem *item_l; OutputItemListitem *listitem; item_l = (OutputItem *)(l->data); g_assert (item_l->type == VALUE_TYPE_LISTITEM); listitem = (OutputItemListitem *)item_l; g_assert (listitem->value); /* All items must be of same type */ g_assert_cmpint (item_l->field, ==, field); } if (n > 0) { guint aux = n; key_length += ((strlen (KEY_ARRAY_VALUE_SUFFIX)) + 3); while ((aux /= 10) > 0) key_length++; } new_key = g_strdup_printf ("%s" KEY_ARRAY_LENGTH_SUFFIX, field_infos[field].key); g_print ("%-*.*s : %u\n", key_length, key_length, new_key, n); g_free (new_key); /* Second pass to print */ for (n = 0, l = output_items; l; l = g_list_next (l), n++) { OutputItemListitem *listitem; listitem = (OutputItemListitem *)(l->data); new_key = g_strdup_printf ("%s" KEY_ARRAY_VALUE_SUFFIX "[%u]", field_infos[field].key, n + 1); g_print ("%-*.*s : %s\n", key_length, key_length, new_key, listitem->value); g_free (new_key); } } /******************************************************************************/ /* JSON-friendly output */ static gchar * json_strescape (const gchar *str) { const gchar *p; const gchar *end; GString *output; gsize len; len = strlen (str); end = str + len; output = g_string_sized_new (len); for (p = str; p < end; p++) { if (*p == '\\' || *p == '"') { g_string_append_c (output, '\\'); g_string_append_c (output, *p); } else if ((*p > 0 && *p < 0x1f) || *p == 0x7f) { switch (*p) { case '\b': g_string_append (output, "\\b"); break; case '\f': g_string_append (output, "\\f"); break; case '\n': g_string_append (output, "\\n"); break; case '\r': g_string_append (output, "\\r"); break; case '\t': g_string_append (output, "\\t"); break; default: g_string_append_printf (output, "\\u00%02x", (guint)*p); break; } } else g_string_append_c (output, *p); } return g_string_free (output, FALSE); } static gint list_sort_by_keys (const OutputItem *item_a, const OutputItem *item_b) { return g_strcmp0 (field_infos[item_a->field].key, field_infos[item_b->field].key); } static void dump_output_json (void) { GList *l; MmcF current_field = MMC_F_UNKNOWN; gchar **current_path = NULL; guint cur_dlen = 0; output_items = g_list_sort (output_items, (GCompareFunc) list_sort_by_keys); g_print ("{"); for (l = output_items; l; l = g_list_next (l)) { OutputItem *item_l = (OutputItem *)(l->data); if (current_field != item_l->field) { guint new_dlen; guint iter = 0; gchar **new_path; new_path = g_strsplit (field_infos[item_l->field].key, ".", -1); new_dlen = g_strv_length (new_path) - 1; if (current_path) { guint min_dlen; min_dlen = MIN (cur_dlen, new_dlen); while (iter < min_dlen && g_strcmp0 (current_path[iter], new_path[iter]) == 0) iter++; g_strfreev (current_path); if (iter < min_dlen || new_dlen < cur_dlen) for (min_dlen = iter; min_dlen < cur_dlen; min_dlen++) g_print ("}"); g_print (","); } while (iter < new_dlen) g_print ("\"%s\":{", new_path[iter++]); cur_dlen = new_dlen; current_path = new_path; current_field = item_l->field; } else { g_print (","); } if (item_l->type == VALUE_TYPE_SINGLE) { OutputItemSingle *single = (OutputItemSingle *) item_l; gchar *escaped = NULL; if (single->value) escaped = json_strescape (single->value); g_print ("\"%s\":\"%s\"", current_path[cur_dlen], escaped ? escaped : "--"); g_free (escaped); } else if (item_l->type == VALUE_TYPE_MULTIPLE) { OutputItemMultiple *multiple = (OutputItemMultiple *) item_l; guint i, n; n = multiple->values ? g_strv_length (multiple->values) : 0; g_print ("\"%s\":[", current_path[cur_dlen]); for (i = 0; i < n; i++) { if (multiple->is_object) { g_print ("%s", multiple->values[i]); } else { gchar *escaped; escaped = json_strescape (multiple->values[i]); g_print ("\"%s\"", escaped); g_free (escaped); } if (i < n - 1) g_print (","); } g_print ("]"); } else g_assert_not_reached (); } while (cur_dlen--) g_print ("}"); g_print ("}\n"); g_strfreev (current_path); } static void dump_output_list_json (MmcF field) { GList *l; g_assert (field != MMC_F_UNKNOWN); g_print ("{\"%s\":[", field_infos[field].key); for (l = output_items; l; l = g_list_next (l)) { OutputItem *item_l; OutputItemListitem *listitem; item_l = (OutputItem *)(l->data); g_assert (item_l->type == VALUE_TYPE_LISTITEM); listitem = (OutputItemListitem *)item_l; g_assert (listitem->value); /* All items must be of same type */ g_assert_cmpint (item_l->field, ==, field); g_print ("\"%s\"", listitem->value); if (g_list_next (l)) g_print (","); } g_print ("]}\n"); } /******************************************************************************/ /* Dump output */ void mmcli_output_dump (void) { switch (selected_type) { case MMC_OUTPUT_TYPE_NONE: break; case MMC_OUTPUT_TYPE_HUMAN: dump_output_human (); break; case MMC_OUTPUT_TYPE_KEYVALUE: dump_output_keyvalue (); break; case MMC_OUTPUT_TYPE_JSON: dump_output_json (); break; default: g_assert_not_reached (); } g_list_free_full (output_items, (GDestroyNotify) output_item_free); output_items = NULL; fflush (stdout); } void mmcli_output_list_dump (MmcF field) { switch (selected_type) { case MMC_OUTPUT_TYPE_NONE: break; case MMC_OUTPUT_TYPE_HUMAN: dump_output_list_human (field); break; case MMC_OUTPUT_TYPE_KEYVALUE: dump_output_list_keyvalue (field); break; case MMC_OUTPUT_TYPE_JSON: dump_output_list_json (field); break; default: g_assert_not_reached (); } g_list_free_full (output_items, (GDestroyNotify) output_item_free); output_items = NULL; fflush (stdout); } ModemManager-1.23.4-dev/cli/mmcli-output.h000066400000000000000000000341471456466623000203150ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * mmcli -- Control modem status & access information from the command line * * 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, see . * * Copyright (C) 2018 Aleksander Morgado */ #ifndef MMCLI_OUTPUT_H #define MMCLI_OUTPUT_H #define _LIBMM_INSIDE_MMCLI #include /******************************************************************************/ /* List of sections (grouped fields) displayed in the human-friendly output */ typedef enum { MMC_S_UNKNOWN = -1, /* Modem object related sections */ MMC_S_MODEM_GENERAL = 0, MMC_S_MODEM_HARDWARE, MMC_S_MODEM_SYSTEM, MMC_S_MODEM_NUMBERS, MMC_S_MODEM_STATUS, MMC_S_MODEM_MODES, MMC_S_MODEM_BANDS, MMC_S_MODEM_IP, MMC_S_MODEM_CELL_INFO, MMC_S_MODEM_3GPP, MMC_S_MODEM_3GPP_EPS, MMC_S_MODEM_3GPP_5GNR, MMC_S_MODEM_3GPP_SCAN, MMC_S_MODEM_3GPP_USSD, MMC_S_MODEM_3GPP_PROFILE_MANAGER, MMC_S_MODEM_CDMA, MMC_S_MODEM_SIM, MMC_S_MODEM_BEARER, MMC_S_MODEM_TIME, MMC_S_MODEM_TIMEZONE, MMC_S_MODEM_MESSAGING, MMC_S_MODEM_SIGNAL, MMC_S_MODEM_SIGNAL_CDMA1X, MMC_S_MODEM_SIGNAL_EVDO, MMC_S_MODEM_SIGNAL_GSM, MMC_S_MODEM_SIGNAL_UMTS, MMC_S_MODEM_SIGNAL_LTE, MMC_S_MODEM_SIGNAL_5G, MMC_S_MODEM_OMA, MMC_S_MODEM_OMA_CURRENT, MMC_S_MODEM_OMA_PENDING, MMC_S_MODEM_LOCATION, MMC_S_MODEM_LOCATION_3GPP, MMC_S_MODEM_LOCATION_GPS, MMC_S_MODEM_LOCATION_CDMABS, MMC_S_MODEM_FIRMWARE, MMC_S_MODEM_FIRMWARE_FASTBOOT, MMC_S_MODEM_VOICE, MMC_S_MODEM_SAR, MMC_S_BEARER_GENERAL, MMC_S_BEARER_STATUS, MMC_S_BEARER_PROPERTIES, MMC_S_BEARER_IPV4_CONFIG, MMC_S_BEARER_IPV6_CONFIG, MMC_S_BEARER_STATS, MMC_S_CALL_GENERAL, MMC_S_CALL_PROPERTIES, MMC_S_CALL_AUDIO_FORMAT, MMC_S_SMS_GENERAL, MMC_S_SMS_CONTENT, MMC_S_SMS_PROPERTIES, MMC_S_SIM_GENERAL, MMC_S_SIM_PROPERTIES, } MmcS; /******************************************************************************/ /* List of fields */ typedef enum { MMC_F_UNKNOWN = -1, /* General section */ MMC_F_GENERAL_DBUS_PATH = 0, MMC_F_GENERAL_DEVICE_ID, /* Hardware section */ MMC_F_HARDWARE_MANUFACTURER, MMC_F_HARDWARE_MODEL, MMC_F_HARDWARE_REVISION, MMC_F_HARDWARE_CARRIER_CONF, MMC_F_HARDWARE_CARRIER_CONF_REV, MMC_F_HARDWARE_HW_REVISION, MMC_F_HARDWARE_SUPPORTED_CAPABILITIES, MMC_F_HARDWARE_CURRENT_CAPABILITIES, MMC_F_HARDWARE_EQUIPMENT_ID, /* System section */ MMC_F_SYSTEM_DEVICE, MMC_F_SYSTEM_PHYSDEV, MMC_F_SYSTEM_DRIVERS, MMC_F_SYSTEM_PLUGIN, MMC_F_SYSTEM_PRIMARY_PORT, MMC_F_SYSTEM_PORTS, /* Numbers section */ MMC_F_NUMBERS_OWN, /* Status section */ MMC_F_STATUS_LOCK, MMC_F_STATUS_UNLOCK_RETRIES, MMC_F_STATUS_STATE, MMC_F_STATUS_FAILED_REASON, MMC_F_STATUS_POWER_STATE, MMC_F_STATUS_ACCESS_TECH, MMC_F_STATUS_SIGNAL_QUALITY_VALUE, MMC_F_STATUS_SIGNAL_QUALITY_RECENT, /* Modes section */ MMC_F_MODES_SUPPORTED, MMC_F_MODES_CURRENT, /* Bands section */ MMC_F_BANDS_SUPPORTED, MMC_F_BANDS_CURRENT, /* IP section */ MMC_F_IP_SUPPORTED, /* Cell info section */ MMC_F_CELL_INFO, /* 3GPP section */ MMC_F_3GPP_IMEI, MMC_F_3GPP_ENABLED_LOCKS, MMC_F_3GPP_OPERATOR_ID, MMC_F_3GPP_OPERATOR_NAME, MMC_F_3GPP_REGISTRATION, MMC_F_3GPP_PACKET_SERVICE_STATE, MMC_F_3GPP_PCO, /* 3GPP EPS section */ MMC_F_3GPP_EPS_UE_MODE, MMC_F_3GPP_EPS_INITIAL_BEARER_PATH, MMC_F_3GPP_EPS_BEARER_SETTINGS_APN, MMC_F_3GPP_EPS_BEARER_SETTINGS_IP_TYPE, MMC_F_3GPP_EPS_BEARER_SETTINGS_USER, MMC_F_3GPP_EPS_BEARER_SETTINGS_PASSWORD, MMC_F_3GPP_5GNR_REGISTRATION_MICO_MODE, MMC_F_3GPP_5GNR_REGISTRATION_DRX_CYCLE, /* 3GPP scan section */ MMC_F_3GPP_SCAN_NETWORKS, /* 3GPP profile management section */ MMC_F_3GPP_PROFILE_MANAGER_INDEX_FIELD, MMC_F_3GPP_PROFILE_MANAGER_LIST, MMC_F_3GPP_PROFILE_MANAGER_SET, /* USSD section */ MMC_F_3GPP_USSD_STATUS, MMC_F_3GPP_USSD_NETWORK_REQUEST, MMC_F_3GPP_USSD_NETWORK_NOTIFICATION, /* CDMA section */ MMC_F_CDMA_MEID, MMC_F_CDMA_ESN, MMC_F_CDMA_SID, MMC_F_CDMA_NID, MMC_F_CDMA_REGISTRATION_CDMA1X, MMC_F_CDMA_REGISTRATION_EVDO, MMC_F_CDMA_ACTIVATION, /* SIM section */ MMC_F_SIM_PATH, MMC_F_SIM_PRIMARY_SLOT, MMC_F_SIM_SLOT_PATHS, /* Bearer section */ MMC_F_BEARER_PATHS, /* Time section */ MMC_F_TIME_CURRENT, MMC_F_TIMEZONE_CURRENT, MMC_F_TIMEZONE_DST_OFFSET, MMC_F_TIMEZONE_LEAP_SECONDS, /* Messaging section */ MMC_F_MESSAGING_SUPPORTED_STORAGES, MMC_F_MESSAGING_DEFAULT_STORAGES, MMC_F_MESSAGING_CREATED_SMS, /* Signal section */ MMC_F_SIGNAL_REFRESH_RATE, MMC_F_SIGNAL_RSSI_THRESHOLD, MMC_F_SIGNAL_ERROR_RATE_THRESHOLD, MMC_F_SIGNAL_CDMA1X_RSSI, MMC_F_SIGNAL_CDMA1X_ECIO, MMC_F_SIGNAL_CDMA1X_ERROR_RATE, MMC_F_SIGNAL_EVDO_RSSI, MMC_F_SIGNAL_EVDO_ECIO, MMC_F_SIGNAL_EVDO_SINR, MMC_F_SIGNAL_EVDO_IO, MMC_F_SIGNAL_EVDO_ERROR_RATE, MMC_F_SIGNAL_GSM_RSSI, MMC_F_SIGNAL_GSM_ERROR_RATE, MMC_F_SIGNAL_UMTS_RSSI, MMC_F_SIGNAL_UMTS_RSCP, MMC_F_SIGNAL_UMTS_ECIO, MMC_F_SIGNAL_UMTS_ERROR_RATE, MMC_F_SIGNAL_LTE_RSSI, MMC_F_SIGNAL_LTE_RSRQ, MMC_F_SIGNAL_LTE_RSRP, MMC_F_SIGNAL_LTE_SNR, MMC_F_SIGNAL_LTE_ERROR_RATE, MMC_F_SIGNAL_5G_RSRQ, MMC_F_SIGNAL_5G_RSRP, MMC_F_SIGNAL_5G_SNR, MMC_F_SIGNAL_5G_ERROR_RATE, /* OMA section */ MMC_F_OMA_FEATURES, MMC_F_OMA_CURRENT_TYPE, MMC_F_OMA_CURRENT_STATE, MMC_F_OMA_PENDING_SESSIONS, /* Location status section */ MMC_F_LOCATION_CAPABILITIES, MMC_F_LOCATION_ENABLED, MMC_F_LOCATION_SIGNALS, MMC_F_LOCATION_GPS_REFRESH_RATE, MMC_F_LOCATION_GPS_SUPL_SERVER, MMC_F_LOCATION_GPS_ASSISTANCE, MMC_F_LOCATION_GPS_ASSISTANCE_SERVERS, MMC_F_LOCATION_3GPP_MCC, MMC_F_LOCATION_3GPP_MNC, MMC_F_LOCATION_3GPP_LAC, MMC_F_LOCATION_3GPP_TAC, MMC_F_LOCATION_3GPP_CID, MMC_F_LOCATION_GPS_NMEA, MMC_F_LOCATION_GPS_UTC, MMC_F_LOCATION_GPS_LONG, MMC_F_LOCATION_GPS_LAT, MMC_F_LOCATION_GPS_ALT, MMC_F_LOCATION_CDMABS_LONG, MMC_F_LOCATION_CDMABS_LAT, /* Firmware status section */ MMC_F_FIRMWARE_LIST, MMC_F_FIRMWARE_METHOD, MMC_F_FIRMWARE_DEVICE_IDS, MMC_F_FIRMWARE_VERSION, MMC_F_FIRMWARE_FASTBOOT_AT, /* Voice section */ MMC_F_VOICE_EMERGENCY_ONLY, /* Bearer general section */ MMC_F_BEARER_GENERAL_DBUS_PATH, MMC_F_BEARER_GENERAL_TYPE, /* Bearer status section */ MMC_F_BEARER_STATUS_CONNECTED, MMC_F_BEARER_STATUS_CONNECTION_ERROR_NAME, MMC_F_BEARER_STATUS_CONNECTION_ERROR_MESSAGE, MMC_F_BEARER_STATUS_SUSPENDED, MMC_F_BEARER_STATUS_MULTIPLEXED, MMC_F_BEARER_STATUS_INTERFACE, MMC_F_BEARER_STATUS_IP_TIMEOUT, MMC_F_BEARER_STATUS_PROFILE_ID, /* Bearer properties section */ MMC_F_BEARER_PROPERTIES_APN, MMC_F_BEARER_PROPERTIES_APN_TYPE, MMC_F_BEARER_PROPERTIES_ROAMING, MMC_F_BEARER_PROPERTIES_IP_TYPE, MMC_F_BEARER_PROPERTIES_ALLOWED_AUTH, MMC_F_BEARER_PROPERTIES_USER, MMC_F_BEARER_PROPERTIES_PASSWORD, MMC_F_BEARER_PROPERTIES_PROFILE_ID, MMC_F_BEARER_PROPERTIES_NUMBER, MMC_F_BEARER_PROPERTIES_RM_PROTOCOL, MMC_F_BEARER_PROPERTIES_ACCESS_TYPE_PREFERENCE, MMC_F_BEARER_PROPERTIES_ROAMING_ALLOWANCE, MMC_F_BEARER_IPV4_CONFIG_METHOD, MMC_F_BEARER_IPV4_CONFIG_ADDRESS, MMC_F_BEARER_IPV4_CONFIG_PREFIX, MMC_F_BEARER_IPV4_CONFIG_GATEWAY, MMC_F_BEARER_IPV4_CONFIG_DNS, MMC_F_BEARER_IPV4_CONFIG_MTU, MMC_F_BEARER_IPV6_CONFIG_METHOD, MMC_F_BEARER_IPV6_CONFIG_ADDRESS, MMC_F_BEARER_IPV6_CONFIG_PREFIX, MMC_F_BEARER_IPV6_CONFIG_GATEWAY, MMC_F_BEARER_IPV6_CONFIG_DNS, MMC_F_BEARER_IPV6_CONFIG_MTU, MMC_F_BEARER_STATS_START_DATE, MMC_F_BEARER_STATS_DURATION, MMC_F_BEARER_STATS_UPLINK_SPEED, MMC_F_BEARER_STATS_DOWNLINK_SPEED, MMC_F_BEARER_STATS_BYTES_RX, MMC_F_BEARER_STATS_BYTES_TX, MMC_F_BEARER_STATS_ATTEMPTS, MMC_F_BEARER_STATS_FAILED_ATTEMPTS, MMC_F_BEARER_STATS_TOTAL_DURATION, MMC_F_BEARER_STATS_TOTAL_BYTES_RX, MMC_F_BEARER_STATS_TOTAL_BYTES_TX, MMC_F_CALL_GENERAL_DBUS_PATH, MMC_F_CALL_PROPERTIES_NUMBER, MMC_F_CALL_PROPERTIES_DIRECTION, MMC_F_CALL_PROPERTIES_MULTIPARTY, MMC_F_CALL_PROPERTIES_STATE, MMC_F_CALL_PROPERTIES_STATE_REASON, MMC_F_CALL_PROPERTIES_AUDIO_PORT, MMC_F_CALL_AUDIO_FORMAT_ENCODING, MMC_F_CALL_AUDIO_FORMAT_RESOLUTION, MMC_F_CALL_AUDIO_FORMAT_RATE, MMC_F_SMS_GENERAL_DBUS_PATH, MMC_F_SMS_CONTENT_NUMBER, MMC_F_SMS_CONTENT_TEXT, MMC_F_SMS_CONTENT_DATA, MMC_F_SMS_PROPERTIES_PDU_TYPE, MMC_F_SMS_PROPERTIES_STATE, MMC_F_SMS_PROPERTIES_VALIDITY, MMC_F_SMS_PROPERTIES_STORAGE, MMC_F_SMS_PROPERTIES_SMSC, MMC_F_SMS_PROPERTIES_CLASS, MMC_F_SMS_PROPERTIES_TELESERVICE_ID, MMC_F_SMS_PROPERTIES_SERVICE_CATEGORY, MMC_F_SMS_PROPERTIES_DELIVERY_REPORT, MMC_F_SMS_PROPERTIES_MSG_REFERENCE, MMC_F_SMS_PROPERTIES_TIMESTAMP, MMC_F_SMS_PROPERTIES_DELIVERY_STATE, MMC_F_SMS_PROPERTIES_DISCH_TIMESTAMP, MMC_F_SIM_GENERAL_DBUS_PATH, MMC_F_SIM_PROPERTIES_ACTIVE, MMC_F_SIM_PROPERTIES_IMSI, MMC_F_SIM_PROPERTIES_ICCID, MMC_F_SIM_PROPERTIES_EID, MMC_F_SIM_PROPERTIES_OPERATOR_ID, MMC_F_SIM_PROPERTIES_OPERATOR_NAME, MMC_F_SIM_PROPERTIES_EMERGENCY_NUMBERS, MMC_F_SIM_PROPERTIES_PREFERRED_NETWORKS, MMC_F_SIM_PROPERTIES_GID1, MMC_F_SIM_PROPERTIES_GID2, MMC_F_SIM_PROPERTIES_SIM_TYPE, MMC_F_SIM_PROPERTIES_ESIM_STATUS, MMC_F_SIM_PROPERTIES_REMOVABILITY, MMC_F_SAR_STATE, MMC_F_SAR_POWER_LEVEL, /* Lists */ MMC_F_MODEM_LIST_DBUS_PATH, MMC_F_SMS_LIST_DBUS_PATH, MMC_F_CALL_LIST_DBUS_PATH, } MmcF; /******************************************************************************/ /* Output type selection */ typedef enum { MMC_OUTPUT_TYPE_NONE, MMC_OUTPUT_TYPE_HUMAN, MMC_OUTPUT_TYPE_KEYVALUE, MMC_OUTPUT_TYPE_JSON } MmcOutputType; void mmcli_output_set (MmcOutputType type); MmcOutputType mmcli_output_get (void); /******************************************************************************/ /* Generic output management */ void mmcli_output_string (MmcF field, const gchar *str); void mmcli_output_string_take (MmcF field, gchar *str); void mmcli_output_string_list (MmcF field, const gchar *str); void mmcli_output_string_list_take (MmcF field, gchar *str); void mmcli_output_string_multiline (MmcF field, const gchar *str); void mmcli_output_string_multiline_take (MmcF field, gchar *str); void mmcli_output_string_array (MmcF field, const gchar **strv, gboolean multiline); void mmcli_output_string_array_take (MmcF field, gchar **strv, gboolean multiline); void mmcli_output_string_array_multiline_take (MmcF field, gchar **strv); void mmcli_output_string_take_typed (MmcF field, gchar *value, const gchar *type); void mmcli_output_listitem (MmcF field, const gchar *prefix, const gchar *value, const gchar *extra); /******************************************************************************/ /* Custom output management */ void mmcli_output_signal_quality (MMModemState state, guint value, gboolean recent); void mmcli_output_start_date (guint64 value); void mmcli_output_state (MMModemState state, MMModemStateFailedReason reason); void mmcli_output_sim_slots (gchar **sim_slot_paths, guint primary_sim_slot); void mmcli_output_scan_networks (GList *network_list); void mmcli_output_firmware_list (GList *firmware_list, MMFirmwareProperties *selected); void mmcli_output_pco_list (GList *pco_list); void mmcli_output_preferred_networks (GList *preferred_nets_list); void mmcli_output_profile_list (GList *profile_list); void mmcli_output_profile_set (MM3gppProfile *profile); void mmcli_output_cell_info (GList *cell_info_list); /******************************************************************************/ /* Dump output */ void mmcli_output_dump (void); void mmcli_output_list_dump (MmcF field); #endif /* MMCLI_OUTPUT_H */ ModemManager-1.23.4-dev/cli/mmcli-sim.c000066400000000000000000000452611456466623000175370ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * mmcli -- Control sim status & access information from the command line * * 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, see . * * Copyright (C) 2011-2018 Aleksander Morgado */ #include "config.h" #include #include #include #include #include #include #define _LIBMM_INSIDE_MMCLI #include #include "mmcli.h" #include "mmcli-common.h" #include "mmcli-output.h" /* Context */ typedef struct { MMManager *manager; MMObject *object; GCancellable *cancellable; MMSim *sim; } Context; static Context *ctx; /* Options */ static gboolean info_flag; /* set when no action found */ static gchar *pin_str; static gchar *puk_str; static gboolean enable_pin_flag; static gboolean disable_pin_flag; static gchar *change_pin_str; static gchar *set_preferred_networks_str; static GOptionEntry entries[] = { { "pin", 0, 0, G_OPTION_ARG_STRING, &pin_str, "Send PIN code to a given SIM.", "[PIN]" }, { "puk", 0, 0, G_OPTION_ARG_STRING, &puk_str, "Send PUK code to a given SIM (must send the new PIN with --pin).", "[PUK]" }, { "enable-pin", 0, 0, G_OPTION_ARG_NONE, &enable_pin_flag, "Enable PIN request in a given SIM (must send the current PIN with --pin).", NULL }, { "disable-pin", 0, 0, G_OPTION_ARG_NONE, &disable_pin_flag, "Disable PIN request in a given SIM (must send the current PIN with --pin).", NULL }, { "change-pin", 0, 0, G_OPTION_ARG_STRING, &change_pin_str, "Change the PIN in a given SIM (must send the current PIN with --pin).", "[New PIN]" }, { "sim-set-preferred-networks", 0, 0, G_OPTION_ARG_STRING, &set_preferred_networks_str, "Set preferred network list stored in a given SIM.", "[[MCCMNC[,access_tech]],...]" }, { NULL } }; GOptionGroup * mmcli_sim_get_option_group (void) { GOptionGroup *group; /* Status options */ group = g_option_group_new ("sim", "SIM options:", "Show SIM options", NULL, NULL); g_option_group_add_entries (group, entries); return group; } gboolean mmcli_sim_options_enabled (void) { static guint n_actions = 0; static gboolean checked = FALSE; if (checked) return !!n_actions; n_actions = (!!puk_str + enable_pin_flag + disable_pin_flag + !!change_pin_str + !!set_preferred_networks_str); if (n_actions == 1) { if (!pin_str && !set_preferred_networks_str) { g_printerr ("error: action requires also the PIN code\n"); exit (EXIT_FAILURE); } } else if (n_actions == 0) n_actions += !!pin_str; if (n_actions == 0 && mmcli_get_common_sim_string ()) { /* default to info */ info_flag = TRUE; n_actions++; } if (n_actions > 1) { g_printerr ("error: too many sim actions requested\n"); exit (EXIT_FAILURE); } if (info_flag) mmcli_force_sync_operation (); checked = TRUE; return !!n_actions; } static void context_free (void) { if (!ctx) return; if (ctx->cancellable) g_object_unref (ctx->cancellable); if (ctx->sim) g_object_unref (ctx->sim); if (ctx->object) g_object_unref (ctx->object); if (ctx->manager) g_object_unref (ctx->manager); g_free (ctx); } void mmcli_sim_shutdown (void) { context_free (); } static void print_sim_info (MMSim *sim) { GList *preferred_nets_list; const guint8 *gid1bin; gsize gid1bin_size; const guint8 *gid2bin; gsize gid2bin_size; mmcli_output_string (MMC_F_SIM_GENERAL_DBUS_PATH, mm_sim_get_path (sim)); mmcli_output_string (MMC_F_SIM_PROPERTIES_ACTIVE, mm_sim_get_active (sim) ? "yes" : "no"); mmcli_output_string (MMC_F_SIM_PROPERTIES_IMSI, mm_sim_get_imsi (sim)); mmcli_output_string (MMC_F_SIM_PROPERTIES_ICCID, mm_sim_get_identifier (sim)); mmcli_output_string (MMC_F_SIM_PROPERTIES_EID, mm_sim_get_eid (sim)); mmcli_output_string (MMC_F_SIM_PROPERTIES_OPERATOR_ID, mm_sim_get_operator_identifier (sim)); mmcli_output_string (MMC_F_SIM_PROPERTIES_OPERATOR_NAME, mm_sim_get_operator_name (sim)); mmcli_output_string_array (MMC_F_SIM_PROPERTIES_EMERGENCY_NUMBERS, (const gchar **) mm_sim_get_emergency_numbers (sim), FALSE); preferred_nets_list = mm_sim_get_preferred_networks (sim); mmcli_output_preferred_networks (preferred_nets_list); g_list_free_full (preferred_nets_list, (GDestroyNotify) mm_sim_preferred_network_free); gid1bin = mm_sim_get_gid1 (sim, &gid1bin_size); gid2bin = mm_sim_get_gid2 (sim, &gid2bin_size); mmcli_output_string_take (MMC_F_SIM_PROPERTIES_GID1, gid1bin ? mm_utils_bin2hexstr (gid1bin, gid1bin_size) : NULL); mmcli_output_string_take (MMC_F_SIM_PROPERTIES_GID2, gid2bin ? mm_utils_bin2hexstr (gid2bin, gid2bin_size) : NULL); mmcli_output_string (MMC_F_SIM_PROPERTIES_SIM_TYPE, mm_sim_type_get_string (mm_sim_get_sim_type (sim))); mmcli_output_string (MMC_F_SIM_PROPERTIES_ESIM_STATUS, mm_sim_esim_status_get_string (mm_sim_get_esim_status (sim))); mmcli_output_string (MMC_F_SIM_PROPERTIES_REMOVABILITY, mm_sim_removability_get_string (mm_sim_get_removability (sim))); mmcli_output_dump (); } static void send_pin_process_reply (gboolean result, const GError *error) { if (!result) { g_printerr ("error: couldn't send PIN code to the SIM: '%s'\n", error ? error->message : "unknown error"); exit (EXIT_FAILURE); } g_print ("successfully sent PIN code to the SIM\n"); } static void send_pin_ready (MMSim *sim, GAsyncResult *result, gpointer nothing) { gboolean operation_result; GError *error = NULL; operation_result = mm_sim_send_pin_finish (sim, result, &error); send_pin_process_reply (operation_result, error); mmcli_async_operation_done (); } static void send_puk_process_reply (gboolean result, const GError *error) { if (!result) { g_printerr ("error: couldn't send PUK code to the SIM: '%s'\n", error ? error->message : "unknown error"); exit (EXIT_FAILURE); } g_print ("successfully sent PUK code to the SIM\n"); } static void send_puk_ready (MMSim *sim, GAsyncResult *result, gpointer nothing) { gboolean operation_result; GError *error = NULL; operation_result = mm_sim_send_puk_finish (sim, result, &error); send_puk_process_reply (operation_result, error); mmcli_async_operation_done (); } static void enable_pin_process_reply (gboolean result, const GError *error) { if (!result) { g_printerr ("error: couldn't enable PIN code request in the SIM: '%s'\n", error ? error->message : "unknown error"); exit (EXIT_FAILURE); } g_print ("successfully enabled PIN code request in the SIM\n"); } static void enable_pin_ready (MMSim *sim, GAsyncResult *result, gpointer nothing) { gboolean operation_result; GError *error = NULL; operation_result = mm_sim_enable_pin_finish (sim, result, &error); enable_pin_process_reply (operation_result, error); mmcli_async_operation_done (); } static void disable_pin_process_reply (gboolean result, const GError *error) { if (!result) { g_printerr ("error: couldn't disable PIN code request in the SIM: '%s'\n", error ? error->message : "unknown error"); exit (EXIT_FAILURE); } g_print ("successfully disabled PIN code request in the SIM\n"); } static void disable_pin_ready (MMSim *sim, GAsyncResult *result, gpointer nothing) { gboolean operation_result; GError *error = NULL; operation_result = mm_sim_disable_pin_finish (sim, result, &error); disable_pin_process_reply (operation_result, error); mmcli_async_operation_done (); } static void change_pin_process_reply (gboolean result, const GError *error) { if (!result) { g_printerr ("error: couldn't change PIN code in the SIM: '%s'\n", error ? error->message : "unknown error"); exit (EXIT_FAILURE); } g_print ("successfully changed PIN code in the SIM\n"); } static void change_pin_ready (MMSim *sim, GAsyncResult *result, gpointer nothing) { gboolean operation_result; GError *error = NULL; operation_result = mm_sim_change_pin_finish (sim, result, &error); change_pin_process_reply (operation_result, error); mmcli_async_operation_done (); } static void parse_preferred_networks (GList **preferred_networks) { gchar **parts; GList *preferred_nets_list = NULL; GError *error = NULL; parts = g_strsplit (set_preferred_networks_str, ",", -1); if (parts) { guint i; for (i = 0; parts[i]; i++) { MMModemAccessTechnology access_tech = MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN; MMSimPreferredNetwork *preferred_net; const gchar *mccmnc; mccmnc = parts[i]; if (!mm_is_string_mccmnc (mccmnc)) { g_printerr ("error: couldn't parse MCCMNC for preferred network: '%s'\n", mccmnc); exit (EXIT_FAILURE); } /* if the next item is MCCMNC or is missing, omit the access technology */ if (parts[i + 1] && !mm_is_string_mccmnc (parts[i + 1])) { i++; access_tech = mm_common_get_access_technology_from_string (parts[i], &error); if (error) { g_printerr ("error: %s\n", error->message); g_error_free (error); exit (EXIT_FAILURE); } } preferred_net = mm_sim_preferred_network_new (); mm_sim_preferred_network_set_operator_code (preferred_net, mccmnc); mm_sim_preferred_network_set_access_technology (preferred_net, access_tech); preferred_nets_list = g_list_append (preferred_nets_list, preferred_net); } } g_strfreev (parts); *preferred_networks = preferred_nets_list; } static void set_preferred_networks_process_reply (gboolean result, const GError *error) { if (!result) { g_printerr ("error: couldn't set preferred networks: '%s'\n", error ? error->message : "unknown error"); exit (EXIT_FAILURE); } g_print ("successfully set preferred networks\n"); } static void set_preferred_networks_ready (MMSim *sim, GAsyncResult *result, gpointer nothing) { gboolean operation_result; GError *error = NULL; operation_result = mm_sim_set_preferred_networks_finish (sim, result, &error); set_preferred_networks_process_reply (operation_result, error); mmcli_async_operation_done (); } static void get_sim_ready (GObject *source, GAsyncResult *result, gpointer none) { ctx->sim = mmcli_get_sim_finish (result, &ctx->manager, &ctx->object); /* Setup operation timeout */ mmcli_force_operation_timeout (G_DBUS_PROXY (ctx->sim)); if (info_flag) g_assert_not_reached (); /* Requesting to enable PIN? */ if (enable_pin_flag) { mm_sim_enable_pin (ctx->sim, pin_str, ctx->cancellable, (GAsyncReadyCallback)enable_pin_ready, NULL); return; } /* Requesting to disable PIN? */ if (disable_pin_flag) { mm_sim_disable_pin (ctx->sim, pin_str, ctx->cancellable, (GAsyncReadyCallback)disable_pin_ready, NULL); return; } /* Requesting to change PIN? */ if (change_pin_str) { mm_sim_change_pin (ctx->sim, pin_str, /* current */ change_pin_str, /* new */ ctx->cancellable, (GAsyncReadyCallback)change_pin_ready, NULL); return; } /* Requesting to send PUK? */ if (puk_str) { mm_sim_send_puk (ctx->sim, puk_str, pin_str, ctx->cancellable, (GAsyncReadyCallback)send_puk_ready, NULL); return; } /* Requesting to set preferred networks? */ if (set_preferred_networks_str) { GList *preferred_networks = NULL; parse_preferred_networks (&preferred_networks); mm_sim_set_preferred_networks (ctx->sim, preferred_networks, ctx->cancellable, (GAsyncReadyCallback)set_preferred_networks_ready, NULL); g_list_free_full (preferred_networks, (GDestroyNotify) mm_sim_preferred_network_free); return; } /* Requesting to send PIN? (always LAST check!) */ if (pin_str) { mm_sim_send_pin (ctx->sim, pin_str, ctx->cancellable, (GAsyncReadyCallback)send_pin_ready, NULL); return; } g_warn_if_reached (); } void mmcli_sim_run_asynchronous (GDBusConnection *connection, GCancellable *cancellable) { /* Initialize context */ ctx = g_new0 (Context, 1); if (cancellable) ctx->cancellable = g_object_ref (cancellable); /* Get proper sim */ mmcli_get_sim (connection, mmcli_get_common_sim_string (), cancellable, (GAsyncReadyCallback)get_sim_ready, NULL); } void mmcli_sim_run_synchronous (GDBusConnection *connection) { GError *error = NULL; /* Initialize context */ ctx = g_new0 (Context, 1); ctx->sim = mmcli_get_sim_sync (connection, mmcli_get_common_sim_string (), &ctx->manager, &ctx->object); /* Setup operation timeout */ mmcli_force_operation_timeout (G_DBUS_PROXY (ctx->sim)); /* Request to get info from SIM? */ if (info_flag) { g_debug ("Printing sim info..."); print_sim_info (ctx->sim); return; } /* Requesting to enable PIN? */ if (enable_pin_flag) { gboolean operation_result; operation_result = mm_sim_enable_pin_sync (ctx->sim, pin_str, NULL, &error); enable_pin_process_reply (operation_result, error); return; } /* Requesting to disable PIN? */ if (disable_pin_flag) { gboolean operation_result; operation_result = mm_sim_disable_pin_sync (ctx->sim, pin_str, NULL, &error); disable_pin_process_reply (operation_result, error); return; } /* Requesting to change PIN? */ if (change_pin_str) { gboolean operation_result; operation_result = mm_sim_change_pin_sync (ctx->sim, pin_str, /* current */ change_pin_str, /* new */ NULL, &error); change_pin_process_reply (operation_result, error); return; } /* Requesting to send PUK? */ if (puk_str) { gboolean operation_result; operation_result = mm_sim_send_puk_sync (ctx->sim, puk_str, pin_str, NULL, &error); send_puk_process_reply (operation_result, error); return; } /* Requesting to set preferred networks? */ if (set_preferred_networks_str) { gboolean operation_result; GList *preferred_networks = NULL; parse_preferred_networks (&preferred_networks); operation_result = mm_sim_set_preferred_networks_sync (ctx->sim, preferred_networks, NULL, &error); g_list_free_full (preferred_networks, (GDestroyNotify) mm_sim_preferred_network_free); set_preferred_networks_process_reply (operation_result, error); return; } /* Requesting to send PIN? (always LAST check!) */ if (pin_str) { gboolean operation_result; operation_result = mm_sim_send_pin_sync (ctx->sim, pin_str, NULL, &error); send_pin_process_reply (operation_result, error); return; } g_warn_if_reached (); } ModemManager-1.23.4-dev/cli/mmcli-sms.c000066400000000000000000000320311456466623000175400ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * mmcli -- Control sms status & access information from the command line * * 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, see . * * Copyright (C) 2012 Google, Inc. */ #include "config.h" #include #include #include #include #include #include #define _LIBMM_INSIDE_MMCLI #include #include "mmcli.h" #include "mmcli-common.h" #include "mmcli-output.h" /* Context */ typedef struct { MMManager *manager; MMObject *object; GCancellable *cancellable; MMSms *sms; } Context; static Context *ctx; /* Options */ static gboolean info_flag; /* set when no action found */ static gboolean send_flag; static gboolean store_flag; static gchar *store_in_storage_str; static gchar *create_file_with_data_str; static GOptionEntry entries[] = { { "send", 0, 0, G_OPTION_ARG_NONE, &send_flag, "Send SMS.", NULL, }, { "store", 0, 0, G_OPTION_ARG_NONE, &store_flag, "Store the SMS in the device, at the default storage", NULL, }, { "store-in-storage", 0, 0, G_OPTION_ARG_STRING, &store_in_storage_str, "Store the SMS in the device, at the specified storage", "[Storage]", }, { "create-file-with-data", 0, 0, G_OPTION_ARG_STRING, &create_file_with_data_str, "Create a file with the data contents of the SMS.", "[File path]", }, { NULL } }; GOptionGroup * mmcli_sms_get_option_group (void) { GOptionGroup *group; /* Status options */ group = g_option_group_new ("sms", "SMS options:", "Show SMS options", NULL, NULL); g_option_group_add_entries (group, entries); return group; } gboolean mmcli_sms_options_enabled (void) { static guint n_actions = 0; static gboolean checked = FALSE; if (checked) return !!n_actions; n_actions = (send_flag + store_flag + !!store_in_storage_str + !!create_file_with_data_str); if (n_actions == 0 && mmcli_get_common_sms_string ()) { /* default to info */ info_flag = TRUE; n_actions++; } if (n_actions > 1) { g_printerr ("error: too many SMS actions requested\n"); exit (EXIT_FAILURE); } if (info_flag) mmcli_force_sync_operation (); if (create_file_with_data_str) mmcli_force_sync_operation (); checked = TRUE; return !!n_actions; } static void context_free (void) { if (!ctx) return; if (ctx->cancellable) g_object_unref (ctx->cancellable); if (ctx->sms) g_object_unref (ctx->sms); if (ctx->object) g_object_unref (ctx->object); if (ctx->manager) g_object_unref (ctx->manager); g_free (ctx); } void mmcli_sms_shutdown (void) { context_free (); } static void print_sms_info (MMSms *sms) { MMSmsPduType pdu_type; gchar *data = NULL; const guint8 *databin; gsize databin_size; gchar *validity = NULL; gchar *class = NULL; const gchar *delivery_report = NULL; gchar *message_reference = NULL; const gchar *delivery_state = NULL; databin = mm_sms_get_data (sms, &databin_size); if (databin) data = mm_utils_bin2hexstr (databin, databin_size); if (mm_sms_get_validity_type (sms) == MM_SMS_VALIDITY_TYPE_RELATIVE) validity = g_strdup_printf ("%u", mm_sms_get_validity_relative (sms)); if (mm_sms_get_class (sms) >= 0) class = g_strdup_printf ("%d", mm_sms_get_class (sms)); pdu_type = mm_sms_get_pdu_type (sms); if (pdu_type == MM_SMS_PDU_TYPE_SUBMIT) delivery_report = mm_sms_get_delivery_report_request (sms) ? "requested" : "not requested"; if (mm_sms_get_message_reference (sms) != 0) message_reference = g_strdup_printf ("%u", mm_sms_get_message_reference (sms)); if (mm_sms_get_delivery_state (sms) != MM_SMS_DELIVERY_STATE_UNKNOWN) delivery_state = mm_sms_delivery_state_get_string_extended (mm_sms_get_delivery_state (sms)); mmcli_output_string (MMC_F_SMS_GENERAL_DBUS_PATH, mm_sms_get_path (sms)); mmcli_output_string (MMC_F_SMS_CONTENT_NUMBER, mm_sms_get_number (sms)); mmcli_output_string (MMC_F_SMS_CONTENT_TEXT, mm_sms_get_text (sms)); mmcli_output_string_take (MMC_F_SMS_CONTENT_DATA, data); mmcli_output_string (MMC_F_SMS_PROPERTIES_PDU_TYPE, mm_sms_pdu_type_get_string (pdu_type)); mmcli_output_string (MMC_F_SMS_PROPERTIES_STATE, mm_sms_state_get_string (mm_sms_get_state (sms))); mmcli_output_string_take (MMC_F_SMS_PROPERTIES_VALIDITY, validity); mmcli_output_string (MMC_F_SMS_PROPERTIES_STORAGE, mm_sms_storage_get_string (mm_sms_get_storage (sms))); mmcli_output_string (MMC_F_SMS_PROPERTIES_SMSC, mm_sms_get_smsc (sms)); mmcli_output_string_take (MMC_F_SMS_PROPERTIES_CLASS, class); mmcli_output_string (MMC_F_SMS_PROPERTIES_TELESERVICE_ID, mm_sms_cdma_teleservice_id_get_string (mm_sms_get_teleservice_id (sms))); mmcli_output_string (MMC_F_SMS_PROPERTIES_SERVICE_CATEGORY, mm_sms_cdma_service_category_get_string (mm_sms_get_service_category (sms))); mmcli_output_string (MMC_F_SMS_PROPERTIES_DELIVERY_REPORT, delivery_report); mmcli_output_string (MMC_F_SMS_PROPERTIES_MSG_REFERENCE, message_reference); mmcli_output_string (MMC_F_SMS_PROPERTIES_TIMESTAMP, mm_sms_get_timestamp (sms)); mmcli_output_string (MMC_F_SMS_PROPERTIES_DELIVERY_STATE, delivery_state); mmcli_output_string (MMC_F_SMS_PROPERTIES_DISCH_TIMESTAMP, mm_sms_get_discharge_timestamp (sms)); mmcli_output_dump (); } static void create_file_with_data (MMSms *sms, const gchar *input_path_str) { GError *error = NULL; gchar *path; GFile *file; const guint8 *data; gsize data_size; file = g_file_new_for_commandline_arg (input_path_str); path = g_file_get_path (file); data = mm_sms_get_data (sms, &data_size); if (!data) { g_printerr ("error: couldn't create file: SMS has no data\n"); exit (EXIT_FAILURE); } if (!g_file_set_contents (path, (const gchar *)data, data_size, &error)) { g_printerr ("error: cannot write to file '%s': '%s'\n", input_path_str, error->message); exit (EXIT_FAILURE); } g_free (path); g_object_unref (file); } static void send_process_reply (gboolean result, const GError *error) { if (!result) { g_printerr ("error: couldn't send the SMS: '%s'\n", error ? error->message : "unknown error"); exit (EXIT_FAILURE); } g_print ("successfully sent the SMS\n"); } static void send_ready (MMSms *sms, GAsyncResult *result, gpointer nothing) { gboolean operation_result; GError *error = NULL; operation_result = mm_sms_send_finish (sms, result, &error); send_process_reply (operation_result, error); mmcli_async_operation_done (); } static void store_process_reply (gboolean result, const GError *error) { if (!result) { g_printerr ("error: couldn't store the SMS: '%s'\n", error ? error->message : "unknown error"); exit (EXIT_FAILURE); } g_print ("successfully stored the SMS\n"); } static void store_ready (MMSms *sms, GAsyncResult *result, gpointer nothing) { gboolean operation_result; GError *error = NULL; operation_result = mm_sms_store_finish (sms, result, &error); store_process_reply (operation_result, error); mmcli_async_operation_done (); } static void get_sms_ready (GObject *source, GAsyncResult *result, gpointer none) { ctx->sms = mmcli_get_sms_finish (result, &ctx->manager, &ctx->object); /* Setup operation timeout */ mmcli_force_operation_timeout (G_DBUS_PROXY (ctx->sms)); if (info_flag) g_assert_not_reached (); if (create_file_with_data_str) g_assert_not_reached (); /* Requesting to send the SMS? */ if (send_flag) { mm_sms_send (ctx->sms, ctx->cancellable, (GAsyncReadyCallback)send_ready, NULL); return; } /* Requesting to store the SMS? */ if (store_flag) { mm_sms_store (ctx->sms, MM_SMS_STORAGE_UNKNOWN, ctx->cancellable, (GAsyncReadyCallback)store_ready, NULL); return; } /* Requesting to store the SMS in a specific storage? */ if (store_in_storage_str) { MMSmsStorage storage; GError *error = NULL; storage = mm_common_get_sms_storage_from_string (store_in_storage_str, &error); if (error) { g_printerr ("error: couldn't store the SMS: '%s'\n", error->message); exit (EXIT_FAILURE); } mm_sms_store (ctx->sms, storage, ctx->cancellable, (GAsyncReadyCallback)store_ready, NULL); return; } g_warn_if_reached (); } void mmcli_sms_run_asynchronous (GDBusConnection *connection, GCancellable *cancellable) { /* Initialize context */ ctx = g_new0 (Context, 1); if (cancellable) ctx->cancellable = g_object_ref (cancellable); /* Get proper sms */ mmcli_get_sms (connection, mmcli_get_common_sms_string (), cancellable, (GAsyncReadyCallback)get_sms_ready, NULL); } void mmcli_sms_run_synchronous (GDBusConnection *connection) { GError *error = NULL; /* Initialize context */ ctx = g_new0 (Context, 1); ctx->sms = mmcli_get_sms_sync (connection, mmcli_get_common_sms_string (), &ctx->manager, &ctx->object); /* Setup operation timeout */ mmcli_force_operation_timeout (G_DBUS_PROXY (ctx->sms)); /* Request to get info from SMS? */ if (info_flag) { g_debug ("Printing SMS info..."); print_sms_info (ctx->sms); return; } /* Request to create a new file with the data from the SMS? */ if (create_file_with_data_str) { g_debug ("Creating file with SMS data..."); create_file_with_data (ctx->sms, create_file_with_data_str); return; } /* Requesting to send the SMS? */ if (send_flag) { gboolean operation_result; operation_result = mm_sms_send_sync (ctx->sms, NULL, &error); send_process_reply (operation_result, error); return; } /* Requesting to store the SMS? */ if (store_flag) { gboolean operation_result; operation_result = mm_sms_store_sync (ctx->sms, MM_SMS_STORAGE_UNKNOWN, NULL, &error); store_process_reply (operation_result, error); return; } /* Requesting to store the SMS in a specific storage? */ if (store_in_storage_str) { gboolean operation_result; MMSmsStorage storage; storage = mm_common_get_sms_storage_from_string (store_in_storage_str, &error); if (error) { g_printerr ("error: couldn't store the SMS: '%s'\n", error->message); exit (EXIT_FAILURE); } operation_result = mm_sms_store_sync (ctx->sms, storage, NULL, &error); store_process_reply (operation_result, error); return; } g_warn_if_reached (); } ModemManager-1.23.4-dev/cli/mmcli.c000066400000000000000000000417241456466623000167510ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * mmcli -- Control modem status & access information from the command line * * 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, see . * * Copyright (C) 2011-2023 Aleksander Morgado * Copyright (C) 2011 Google, Inc. */ #include "config.h" #include #include #include #include #include #include #define _LIBMM_INSIDE_MMCLI #include #include "mmcli.h" #include "mmcli-common.h" #include "mmcli-output.h" #define PROGRAM_NAME "mmcli" #define PROGRAM_VERSION PACKAGE_VERSION /* Globals */ static GMainLoop *loop; static GCancellable *cancellable; /* Main context */ static gboolean output_keyvalue_flag; static gboolean output_json_flag; static gboolean verbose_flag; static gboolean version_flag; static gboolean async_flag; static gint timeout = 30; /* by default, use 30s for all operations */ static GOptionEntry main_entries[] = { { "output-keyvalue", 'K', 0, G_OPTION_ARG_NONE, &output_keyvalue_flag, "Run action with machine-friendly key-value output", NULL }, { "output-json", 'J', 0, G_OPTION_ARG_NONE, &output_json_flag, "Run action with machine-friendly json output", NULL }, { "verbose", 'v', 0, G_OPTION_ARG_NONE, &verbose_flag, "Run action with verbose logs", NULL }, { "version", 'V', 0, G_OPTION_ARG_NONE, &version_flag, "Print version", NULL }, { "async", 'a', 0, G_OPTION_ARG_NONE, &async_flag, "Use asynchronous methods", NULL }, { "timeout", 0, 0, G_OPTION_ARG_INT, &timeout, "Timeout for the operation", "[SECONDS]" }, { NULL } }; /* Test context */ static gboolean test_session_flag; static GOptionEntry test_entries[] = { { "test-session", 0, 0, G_OPTION_ARG_NONE, &test_session_flag, "Run in session DBus", NULL }, { NULL } }; static GOptionGroup * test_get_option_group (void) { GOptionGroup *group; group = g_option_group_new ("test", "Test options:", "Show test options", NULL, NULL); g_option_group_add_entries (group, test_entries); return group; } static void signals_handler (int signum) { if (cancellable) { /* Ignore consecutive requests of cancellation */ if (!g_cancellable_is_cancelled (cancellable)) { g_printerr ("%s\n", "cancelling the operation...\n"); g_cancellable_cancel (cancellable); } return; } if (loop && g_main_loop_is_running (loop)) { g_printerr ("%s\n", "cancelling the main loop...\n"); g_main_loop_quit (loop); } } static void log_handler (const gchar *log_domain, GLogLevelFlags log_level, const gchar *message, gpointer user_data) { const gchar *log_level_str; time_t now; gchar time_str[64]; struct tm *local_time; now = time ((time_t *) NULL); local_time = localtime (&now); strftime (time_str, 64, "%d %b %Y, %H:%M:%S", local_time); switch (log_level) { case G_LOG_LEVEL_WARNING: log_level_str = "-Warning **"; break; case G_LOG_LEVEL_CRITICAL: case G_LOG_LEVEL_ERROR: log_level_str = "-Error **"; break; case G_LOG_LEVEL_DEBUG: log_level_str = "[Debug]"; break; case G_LOG_LEVEL_MESSAGE: case G_LOG_LEVEL_INFO: log_level_str = ""; break; case G_LOG_FLAG_FATAL: case G_LOG_LEVEL_MASK: case G_LOG_FLAG_RECURSION: default: g_assert_not_reached (); } g_print ("[%s] %s %s\n", time_str, log_level_str, message); } static void print_version_and_exit (void) { g_print (PROGRAM_NAME " " PROGRAM_VERSION "\n" "Copyright (2011 - 2023) Aleksander Morgado\n" "License GPLv2+: GNU GPL version 2 or later \n" "This is free software: you are free to change and redistribute it.\n" "There is NO WARRANTY, to the extent permitted by law.\n" "\n"); exit (EXIT_SUCCESS); } void mmcli_async_operation_done (void) { if (cancellable) { g_object_unref (cancellable); cancellable = NULL; } g_main_loop_quit (loop); } void mmcli_force_async_operation (void) { if (!async_flag) { g_debug ("Forcing request to be run asynchronously"); async_flag = TRUE; } } void mmcli_force_sync_operation (void) { if (async_flag) { g_debug ("Ignoring request to run asynchronously"); async_flag = FALSE; } } void mmcli_force_operation_timeout (GDBusProxy *proxy) { if (proxy) g_dbus_proxy_set_default_timeout (proxy, timeout * 1000); } gint main (gint argc, gchar **argv) { GDBusConnection *connection; GOptionContext *context; GError *error = NULL; setlocale (LC_ALL, ""); /* Setup option context, process it and destroy it */ context = g_option_context_new ("- Control and monitor the ModemManager"); g_option_context_add_group (context, mmcli_manager_get_option_group ()); g_option_context_add_group (context, mmcli_get_common_option_group ()); g_option_context_add_group (context, mmcli_modem_get_option_group ()); g_option_context_add_group (context, mmcli_modem_3gpp_get_option_group ()); g_option_context_add_group (context, mmcli_modem_3gpp_profile_manager_get_option_group ()); g_option_context_add_group (context, mmcli_modem_3gpp_ussd_get_option_group ()); g_option_context_add_group (context, mmcli_modem_cdma_get_option_group ()); g_option_context_add_group (context, mmcli_modem_simple_get_option_group ()); g_option_context_add_group (context, mmcli_modem_location_get_option_group ()); g_option_context_add_group (context, mmcli_modem_messaging_get_option_group ()); g_option_context_add_group (context, mmcli_modem_voice_get_option_group ()); g_option_context_add_group (context, mmcli_modem_time_get_option_group ()); g_option_context_add_group (context, mmcli_modem_firmware_get_option_group ()); g_option_context_add_group (context, mmcli_modem_sar_get_option_group ()); g_option_context_add_group (context, mmcli_modem_signal_get_option_group ()); g_option_context_add_group (context, mmcli_modem_oma_get_option_group ()); g_option_context_add_group (context, mmcli_sim_get_option_group ()); g_option_context_add_group (context, mmcli_bearer_get_option_group ()); g_option_context_add_group (context, mmcli_sms_get_option_group ()); g_option_context_add_group (context, mmcli_call_get_option_group ()); g_option_context_add_group (context, test_get_option_group ()); g_option_context_add_main_entries (context, main_entries, NULL); g_option_context_parse (context, &argc, &argv, NULL); g_option_context_free (context); if (version_flag) print_version_and_exit (); if (verbose_flag) g_log_set_handler (G_LOG_DOMAIN, G_LOG_LEVEL_MASK, log_handler, NULL); /* Setup output */ if (output_keyvalue_flag && output_json_flag) { g_printerr ("error: only one output type supported at the same time\n"); exit (EXIT_FAILURE); } if (output_keyvalue_flag) { if (verbose_flag) { g_printerr ("error: cannot set verbose output in keyvalue output type\n"); exit (EXIT_FAILURE); } mmcli_output_set (MMC_OUTPUT_TYPE_KEYVALUE); } else if (output_json_flag) { if (verbose_flag) { g_printerr ("error: cannot set verbose output in JSON output type\n"); exit (EXIT_FAILURE); } mmcli_output_set (MMC_OUTPUT_TYPE_JSON); } else { mmcli_output_set (MMC_OUTPUT_TYPE_HUMAN); } /* Setup signals */ signal (SIGINT, signals_handler); signal (SIGHUP, signals_handler); signal (SIGTERM, signals_handler); /* Setup dbus connection to use */ connection = g_bus_get_sync (test_session_flag ? G_BUS_TYPE_SESSION : G_BUS_TYPE_SYSTEM, NULL, &error); if (!connection) { g_printerr ("error: couldn't get bus: %s\n", error ? error->message : "unknown error"); exit (EXIT_FAILURE); } /* Create requirements for async options */ cancellable = g_cancellable_new (); loop = g_main_loop_new (NULL, FALSE); /* Manager options? */ if (mmcli_manager_options_enabled ()) { /* Ensure options from different groups are not enabled */ if (mmcli_modem_options_enabled ()) { g_printerr ("error: cannot use manager and modem options " "at the same time\n"); exit (EXIT_FAILURE); } if (async_flag) mmcli_manager_run_asynchronous (connection, cancellable); else mmcli_manager_run_synchronous (connection); } /* Sim options? */ else if (mmcli_sim_options_enabled ()) { if (async_flag) mmcli_sim_run_asynchronous (connection, cancellable); else mmcli_sim_run_synchronous (connection); } /* Bearer options? */ else if (mmcli_bearer_options_enabled ()) { if (async_flag) mmcli_bearer_run_asynchronous (connection, cancellable); else mmcli_bearer_run_synchronous (connection); } /* Sms options? */ else if (mmcli_sms_options_enabled ()) { if (async_flag) mmcli_sms_run_asynchronous (connection, cancellable); else mmcli_sms_run_synchronous (connection); } /* Call options? */ else if (mmcli_call_options_enabled ()) { if (async_flag) mmcli_call_run_asynchronous (connection, cancellable); else mmcli_call_run_synchronous (connection); } /* Modem 3GPP options? */ else if (mmcli_modem_3gpp_options_enabled ()) { if (async_flag) mmcli_modem_3gpp_run_asynchronous (connection, cancellable); else mmcli_modem_3gpp_run_synchronous (connection); } /* Modem 3GPP profile manager options? */ else if (mmcli_modem_3gpp_profile_manager_options_enabled ()) { if (async_flag) mmcli_modem_3gpp_profile_manager_run_asynchronous (connection, cancellable); else mmcli_modem_3gpp_profile_manager_run_synchronous (connection); } /* Modem 3GPP USSD options? */ else if (mmcli_modem_3gpp_ussd_options_enabled ()) { if (async_flag) mmcli_modem_3gpp_ussd_run_asynchronous (connection, cancellable); else mmcli_modem_3gpp_ussd_run_synchronous (connection); } /* Modem CDMA options? */ else if (mmcli_modem_cdma_options_enabled ()) { if (async_flag) mmcli_modem_cdma_run_asynchronous (connection, cancellable); else mmcli_modem_cdma_run_synchronous (connection); } /* Modem Simple options? */ else if (mmcli_modem_simple_options_enabled ()) { if (async_flag) mmcli_modem_simple_run_asynchronous (connection, cancellable); else mmcli_modem_simple_run_synchronous (connection); } /* Modem Location options? */ else if (mmcli_modem_location_options_enabled ()) { if (async_flag) mmcli_modem_location_run_asynchronous (connection, cancellable); else mmcli_modem_location_run_synchronous (connection); } /* Modem Messaging options? */ else if (mmcli_modem_messaging_options_enabled ()) { if (async_flag) mmcli_modem_messaging_run_asynchronous (connection, cancellable); else mmcli_modem_messaging_run_synchronous (connection); } /* Voice options? */ else if (mmcli_modem_voice_options_enabled ()) { if (async_flag) mmcli_modem_voice_run_asynchronous (connection, cancellable); else mmcli_modem_voice_run_synchronous (connection); } /* Modem Time options? */ else if (mmcli_modem_time_options_enabled ()) { if (async_flag) mmcli_modem_time_run_asynchronous (connection, cancellable); else mmcli_modem_time_run_synchronous (connection); } /* Modem Firmware options? */ else if (mmcli_modem_firmware_options_enabled ()) { if (async_flag) mmcli_modem_firmware_run_asynchronous (connection, cancellable); else mmcli_modem_firmware_run_synchronous (connection); } /* Modem SAR options? */ else if (mmcli_modem_sar_options_enabled ()) { if (async_flag) mmcli_modem_sar_run_asynchronous (connection, cancellable); else mmcli_modem_sar_run_synchronous (connection); } /* Modem Signal options? */ else if (mmcli_modem_signal_options_enabled ()) { if (async_flag) mmcli_modem_signal_run_asynchronous (connection, cancellable); else mmcli_modem_signal_run_synchronous (connection); } /* Modem Oma options? */ else if (mmcli_modem_oma_options_enabled ()) { if (async_flag) mmcli_modem_oma_run_asynchronous (connection, cancellable); else mmcli_modem_oma_run_synchronous (connection); } /* Modem options? * NOTE: let this check be always the last one, as other groups also need * having a modem specified, and therefore if -m is set, modem options * are always enabled. */ else if (mmcli_modem_options_enabled ()) { if (async_flag) mmcli_modem_run_asynchronous (connection, cancellable); else mmcli_modem_run_synchronous (connection); } /* No options? */ else { g_printerr ("error: no actions specified\n"); exit (EXIT_FAILURE); } /* Run loop only in async operations */ if (async_flag) g_main_loop_run (loop); if (mmcli_manager_options_enabled ()) { mmcli_manager_shutdown (); } else if (mmcli_modem_3gpp_options_enabled ()) { mmcli_modem_3gpp_shutdown (); } else if (mmcli_modem_3gpp_profile_manager_options_enabled ()) { mmcli_modem_3gpp_profile_manager_shutdown (); } else if (mmcli_modem_3gpp_ussd_options_enabled ()) { mmcli_modem_3gpp_ussd_shutdown (); } else if (mmcli_modem_cdma_options_enabled ()) { mmcli_modem_cdma_shutdown (); } else if (mmcli_modem_simple_options_enabled ()) { mmcli_modem_simple_shutdown (); } else if (mmcli_modem_location_options_enabled ()) { mmcli_modem_location_shutdown (); } else if (mmcli_modem_messaging_options_enabled ()) { mmcli_modem_messaging_shutdown (); } else if (mmcli_modem_voice_options_enabled ()) { mmcli_modem_voice_shutdown (); } else if (mmcli_modem_time_options_enabled ()) { mmcli_modem_time_shutdown (); } else if (mmcli_modem_firmware_options_enabled ()) { mmcli_modem_firmware_shutdown (); } else if (mmcli_modem_sar_options_enabled ()) { mmcli_modem_sar_shutdown (); } else if (mmcli_modem_signal_options_enabled ()) { mmcli_modem_signal_shutdown (); } else if (mmcli_modem_oma_options_enabled ()) { mmcli_modem_oma_shutdown (); } else if (mmcli_sim_options_enabled ()) { mmcli_sim_shutdown (); } else if (mmcli_bearer_options_enabled ()) { mmcli_bearer_shutdown (); } else if (mmcli_sms_options_enabled ()) { mmcli_sms_shutdown (); } else if (mmcli_call_options_enabled ()) { mmcli_call_shutdown (); } else if (mmcli_modem_options_enabled ()) { mmcli_modem_shutdown (); } if (cancellable) g_object_unref (cancellable); g_main_loop_unref (loop); g_object_unref (connection); return EXIT_SUCCESS; } ModemManager-1.23.4-dev/cli/mmcli.h000066400000000000000000000224631456466623000167550ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * mmcli -- Control modem status & access information from the command line * * 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, see . * * Copyright (C) 2011 Aleksander Morgado */ #include #ifndef __MMCLI_H__ #define __MMCLI_H__ /* Common */ void mmcli_async_operation_done (void); void mmcli_force_async_operation (void); void mmcli_force_sync_operation (void); void mmcli_force_operation_timeout (GDBusProxy *proxy); /* Manager group */ GOptionGroup *mmcli_manager_get_option_group (void); gboolean mmcli_manager_options_enabled (void); void mmcli_manager_run_asynchronous (GDBusConnection *connection, GCancellable *cancellable); void mmcli_manager_run_synchronous (GDBusConnection *connection); void mmcli_manager_shutdown (void); /* Modem group */ GOptionGroup *mmcli_modem_get_option_group (void); gboolean mmcli_modem_options_enabled (void); void mmcli_modem_run_asynchronous (GDBusConnection *connection, GCancellable *cancellable); void mmcli_modem_run_synchronous (GDBusConnection *connection); void mmcli_modem_shutdown (void); /* 3GPP group */ GOptionGroup *mmcli_modem_3gpp_get_option_group (void); gboolean mmcli_modem_3gpp_options_enabled (void); void mmcli_modem_3gpp_run_asynchronous (GDBusConnection *connection, GCancellable *cancellable); void mmcli_modem_3gpp_run_synchronous (GDBusConnection *connection); void mmcli_modem_3gpp_shutdown (void); /* 3GPP USSD group */ GOptionGroup *mmcli_modem_3gpp_ussd_get_option_group (void); gboolean mmcli_modem_3gpp_ussd_options_enabled (void); void mmcli_modem_3gpp_ussd_run_asynchronous (GDBusConnection *connection, GCancellable *cancellable); void mmcli_modem_3gpp_ussd_run_synchronous (GDBusConnection *connection); void mmcli_modem_3gpp_ussd_shutdown (void); /* 3GPP profile manager group */ GOptionGroup *mmcli_modem_3gpp_profile_manager_get_option_group (void); gboolean mmcli_modem_3gpp_profile_manager_options_enabled (void); void mmcli_modem_3gpp_profile_manager_run_asynchronous (GDBusConnection *connection, GCancellable *cancellable); void mmcli_modem_3gpp_profile_manager_run_synchronous (GDBusConnection *connection); void mmcli_modem_3gpp_profile_manager_shutdown (void); /* CDMA group */ GOptionGroup *mmcli_modem_cdma_get_option_group (void); gboolean mmcli_modem_cdma_options_enabled (void); void mmcli_modem_cdma_run_asynchronous (GDBusConnection *connection, GCancellable *cancellable); void mmcli_modem_cdma_run_synchronous (GDBusConnection *connection); void mmcli_modem_cdma_shutdown (void); /* Simple group */ GOptionGroup *mmcli_modem_simple_get_option_group (void); gboolean mmcli_modem_simple_options_enabled (void); void mmcli_modem_simple_run_asynchronous (GDBusConnection *connection, GCancellable *cancellable); void mmcli_modem_simple_run_synchronous (GDBusConnection *connection); void mmcli_modem_simple_shutdown (void); /* Location group */ GOptionGroup *mmcli_modem_location_get_option_group (void); gboolean mmcli_modem_location_options_enabled (void); void mmcli_modem_location_run_asynchronous (GDBusConnection *connection, GCancellable *cancellable); void mmcli_modem_location_run_synchronous (GDBusConnection *connection); void mmcli_modem_location_shutdown (void); /* Messaging group */ GOptionGroup *mmcli_modem_messaging_get_option_group (void); gboolean mmcli_modem_messaging_options_enabled (void); void mmcli_modem_messaging_run_asynchronous (GDBusConnection *connection, GCancellable *cancellable); void mmcli_modem_messaging_run_synchronous (GDBusConnection *connection); void mmcli_modem_messaging_shutdown (void); /* Voice group */ GOptionGroup *mmcli_modem_voice_get_option_group (void); gboolean mmcli_modem_voice_options_enabled (void); void mmcli_modem_voice_run_asynchronous (GDBusConnection *connection, GCancellable *cancellable); void mmcli_modem_voice_run_synchronous (GDBusConnection *connection); void mmcli_modem_voice_shutdown (void); /* Time group */ GOptionGroup *mmcli_modem_time_get_option_group (void); gboolean mmcli_modem_time_options_enabled (void); void mmcli_modem_time_run_asynchronous (GDBusConnection *connection, GCancellable *cancellable); void mmcli_modem_time_run_synchronous (GDBusConnection *connection); void mmcli_modem_time_shutdown (void); /* Firmware group */ GOptionGroup *mmcli_modem_firmware_get_option_group (void); gboolean mmcli_modem_firmware_options_enabled (void); void mmcli_modem_firmware_run_asynchronous (GDBusConnection *connection, GCancellable *cancellable); void mmcli_modem_firmware_run_synchronous (GDBusConnection *connection); void mmcli_modem_firmware_shutdown (void); /* SAR group */ GOptionGroup *mmcli_modem_sar_get_option_group (void); gboolean mmcli_modem_sar_options_enabled (void); void mmcli_modem_sar_run_asynchronous (GDBusConnection *connection, GCancellable *cancellable); void mmcli_modem_sar_run_synchronous (GDBusConnection *connection); void mmcli_modem_sar_shutdown (void); /* Signal group */ GOptionGroup *mmcli_modem_signal_get_option_group (void); gboolean mmcli_modem_signal_options_enabled (void); void mmcli_modem_signal_run_asynchronous (GDBusConnection *connection, GCancellable *cancellable); void mmcli_modem_signal_run_synchronous (GDBusConnection *connection); void mmcli_modem_signal_shutdown (void); /* Oma group */ GOptionGroup *mmcli_modem_oma_get_option_group (void); gboolean mmcli_modem_oma_options_enabled (void); void mmcli_modem_oma_run_asynchronous (GDBusConnection *connection, GCancellable *cancellable); void mmcli_modem_oma_run_synchronous (GDBusConnection *connection); void mmcli_modem_oma_shutdown (void); /* Bearer group */ GOptionGroup *mmcli_bearer_get_option_group (void); gboolean mmcli_bearer_options_enabled (void); void mmcli_bearer_run_asynchronous (GDBusConnection *connection, GCancellable *cancellable); void mmcli_bearer_run_synchronous (GDBusConnection *connection); void mmcli_bearer_shutdown (void); /* SIM group */ GOptionGroup *mmcli_sim_get_option_group (void); gboolean mmcli_sim_options_enabled (void); void mmcli_sim_run_asynchronous (GDBusConnection *connection, GCancellable *cancellable); void mmcli_sim_run_synchronous (GDBusConnection *connection); void mmcli_sim_shutdown (void); /* SMS group */ GOptionGroup *mmcli_sms_get_option_group (void); gboolean mmcli_sms_options_enabled (void); void mmcli_sms_run_asynchronous (GDBusConnection *connection, GCancellable *cancellable); void mmcli_sms_run_synchronous (GDBusConnection *connection); void mmcli_sms_shutdown (void); /* Call group */ GOptionGroup *mmcli_call_get_option_group (void); gboolean mmcli_call_options_enabled (void); void mmcli_call_run_asynchronous (GDBusConnection *connection, GCancellable *cancellable); void mmcli_call_run_synchronous (GDBusConnection *connection); void mmcli_call_shutdown (void); #endif /* __MMCLI_H__ */ ModemManager-1.23.4-dev/data/000077500000000000000000000000001456466623000156365ustar00rootroot00000000000000ModemManager-1.23.4-dev/data/ModemManager-icon.svg000066400000000000000000000156561456466623000216560ustar00rootroot00000000000000 image/svg+xml ModemManager-1.23.4-dev/data/ModemManager-interface-initialization-sequence-subclassed.dia000066400000000000000000001114401456466623000315710ustar00rootroot00000000000000 #Letter# ## ## ## #interface initialization# #Capabilities# #Manufacturer# #Model# #Revision# #Last step: export interface# #Generic manufacturer loading# #Generic model loading# #...# #MMIfaceModem# #MMBroadbandModem# #Vendor-specific capabilities loading# #MMBroadbandModemVendor# ModemManager-1.23.4-dev/data/ModemManager-interface-initialization-sequence-subclassed.png000066400000000000000000000760451456466623000316330ustar00rootroot00000000000000PNG  IHDRk&ԌsBITO pHYs IDATxw\(A8P"ƮQc`[kT4 VI,%c B"&v#VP@ @]q W27;;H*p>wHI@`:BDJ60gu PJe7[[[ >dٽD"VժU۴iqƢr=Jt^5k֌żuVM[la5k&HlP HICqw!ٚrW0R͛77o/^\?[ hر{nM| *>>nґ]Tkڴ?#{3""?}o6f"MHH()OM{Y^Nmmm٢6l{BBB͋/FDDۗ.7|ʩ|=s~~wjd󰬬 6jaÆ ,̔yٲew;v믿6ݻՌ3 )Hf̘aeeellܫW'Oh,p mmmM+7P*Ѵk2i|͛ծ]+%%E`sssǏojjZF "qss366vtt\plceمRܯ_?lIS@IJ3i&"ݻ7իm޼YL)vK*{7nfСrznݺ֭76nȶ>\,n9rJDb%ϨQd̛7OvfJӮɤi2ׯ4i؏MLL\xC mj*[[[ٚK~pp0VD"JʊUk_SɤT PJ j^^H$D|.R?^M-޽[XXo2e ԫW$є)Sfwdm;ILLLLL;Qzcyxxh8l?KMMݾ};,;3%Jid4ˋ\bEZZZNNξ}XswwOMM=|0_3`4uԑ=wըQm:qT*=~8YFwi@N&@!%R}=foSL\`Tk2i.TRR(%㼼<;;;CCCCCC{{LJJݻ7ǟ_effN6MѣGvwIKZXL]|54MFa7[juܬk׶hBUz P޾}PSɤ9PJž(PӀTRWsJJJ:uSΫWXn4h\111mJtƽyllci>u%Jid4͛7ZRGM155[ @ñ49XM&Ḿ|R*ŵDr/𺺺vvv}9{=gdd̛7AzzzdeeM4Zjfffي>zG"9|KKKCCCWWׇh,ͧDQhڋ4 GH$6m/͵MMM[h1cƌ;wh~aĉ|'mU@NuV}¯7pz )) )) )) )) )) )) ))LG™6mׅ> 'NY&RRٳ;w|m6: _X))(dk:u*-*U2No!%!%!%!%!%!%!%!%!%!%!%!%!%!%!%!%!%!%!%!%!%!%!%رc/_,(( "T*tPeO$B<:4t;ua˗T||Pf*&eOWowpp83RB29sl޼̏HC; 䣺yzp_ED"аCݻMvﯥ%*tJ7e ܗ#:$[sA>-+]d:uBBB>(^>|l.YNYH7ٷ=Þ= UipHI,]x ХK~RrP;Gj!\w⮹sͽΕ$F *մ ޽{!!!ŋ#""vl*do.[hiiw_=''g-Z011СÙ3g˗/o޼^ڵRRR>(.\بQ#CCC .X 33SMddd޽f̘#H}rssǏojjZF "qss366vtt\p쭬"E_ n coeU0ZZc7ozN]ؿl?+'gM=Ƙi&=ܕ#tʪWSl/M_Y)(,Tp_lWڵ/^h,p }6nէA^r*QT%vM&MsUv5ֽ7lLyxWp8hclj(61UvͰŻv?:]r4wܩS["ug۷oT*ȸrʕ+WN;u~W׏}ѩS~ktt =zUGGEjjj"##G=zرcW^D׾}4"^v>l׎K3u~ؽVnm?qm(%v;֯U+l׎ ݴUn>y~ܳ^nmۼ8qʼncԄ:+Kvp,YoNo5<ӏJӮɤi2{|t \?%9[5&>d19g pg/Zj֒ӂ͝;W*_^*VRlw)VaaaժUǍgkkk``бcGV+'߿ڵu?p k֬^f 3̷<+V>}5˖-4h;{pT1ǤR霑#ɓHܝ*Weƌkkk+WYaՔ)bk USe^ U56ෲA&ݿ<{V ޼wb#ۈ|'J)um ǒl͗ Qi>L&wާic`5(=5?Moaj2s'KNVstP5~+VݻǍW/(~i?A-U#u=Y?hZXAnӫWT)]k&"ٙtttغ2KZ`"˭z4e?mIn/rkVi]]n :d_^?VD)f|Ŏ%='צQӮɤi2zrzHdg8lmHG['/K )*ib999999sΕTg H}rW=zWHR?(0'mJ0]0B9ao9y9yG `K DjUn^\]@ussVK͋dNnӡZ<[U=;,mm祉i,-,-.+T*MymInߞfKRZZrz-[oj |2dfl',ؾSZv6KG<űر4WҨM]Id;en?~'S#G[ONJA)sܥbRy?&Lع_ tfg6޽[VxU>}*׬N:׮]$-ZȞ4r Tl~j%UB?YT@rNrggUFƗӞ׶MffDݪջvtyII&|'{ΈsPv+޹Z,iTO&9=g̸qYMS j8rT˗...X%֭[7iҤjժyxx(=͛K.m֬^v߂255rʦMKsssmmmSS-Z̘1S4geeuy5h@___OO~of(9::^zg݊]S)ѱݮW(^5IUժ7o;rDZUsp3rč[YjEDkzN׷yU3#Q=wuR]۴6׷432xǜj>{ffz=[i/;J44MF752ioMhki62!vl/PSURW&a* 0 0 0 0 0 0 0dܻwgϞBGP׽{͢%/x>9_/: ) ,bYe (m!THIKqf : 9iT??ûvoݺ\vmĉBGPy %-mm+BG}`լ$O!T*8 cff&t wpgRX6,Ӡx @[=du2335JݻwYYܜ >E޽+T~7!"H͛rʕ+nnnBGH>}8 t\zuva311)x_B^|RJIwM 0 0 0 0 0 0r : (+79N(*(wEr~BJZȑ#BGM6mΝBGP!% :7>..."H@>FJZ~)ٳRi9HI5IKFDDbG8>333>^$mݺVZm۶}@$-.]haa1vl`ݺu5jR+**jРAblE988YFM?PZ&MKR#ׇDGG,Z_|͛EEE{ <3>>ŋvvvl??;$$$&&&>>^};0={_ge H1*iW\QFE Rx>Ne5JEaR-[ԪUh̙Jرc͎W>~8U˽{tb```fflٲ`Vwލ7:;;[N}6{Nf❻23S"͛7oΝlMF& xD$$RRkkkV044Uqqqc[[[|׬YSUaaa]vDfffIII>>>v_qKFk}JT *3 }j3t22JݛM^^\eFFF&M^l/gB߿K.e+^*U*MrwX͚5ccc{5IСCGٳ´4~5j<{L+}'>{)t,+P Iۜܕf؜_r ;tgʭ[nܼ b222T"tUɓ'O0C-lSSS##'ӦMyKGӵ_ؿoG6 F5#'O 8P,lرcg\o@+O>: isRZZ]v-;}H$7o5ktCv֫V;=Xŋ7on``PV;w+W^X,:uD"a-Uc˲=Q~VDu՟ɭtUQu:UbOCgcMn:#cziΝ۵kwM6DP}ׯX,[eR7o${áCZj$]shQQQaaaAAA|e˖=|0,,,,,,""b&e}믿nܸ*ϟ?b V*Hd][eP~VvI۫x7O(aT}A@҇ҥK߿~EM4D"Shh(9%]ZnYu[ag|Rk)"rww/C>|ҥKn8pi_zϯZjkO2Of~PRXUlݻ~V9} IDATZcƌy-pBfvQӣw}GDkm+_zCDJcٳ'ZYp.͛'$ʙ/^d<ʩϞDEr rܽ{O"oݒp\~rk\w/_ e<|%(JPrCNt挄r8v݃;|Xqo뙃%塝;+ rߖ-%7qq;7lؿm^z͛%bv ~a˚5KZpF?? qQQKY|㒞<>}?J8.ѣ?/^,Mlv<<~;Wq3nEfI8.:|(wP͛GDK'NP:sQ"##HI]Sҫ7kk(E^fOc_zJ75i$$$$'''##cԩǏgDԭ[nݺ-Yt?U=եR5kv>rHVoccsرq}hlIN}.ML)xR.}yFBr~J4e֓^&妤Kƾx.}՝bc_fK!CXy7MؿwoVѫG{$7f]p܈;wʃmߺ%קgϖ͚?x ḯiF, ܡC}G$׮U+gGǤ'O$׼IɑkXu\Tj;9_=}*vv&ƩϞI8RJ ǙD"}QӫRK8NWWXMDyIIcԓXID:::,$"*UXHDx YHD&,e$"3SS2##Hlnrb"%!UpȺzu2 K֖DTޞDRF"rvtpܣ׉\FDukf#wqa#5WOqᡡDԸA DžQFX*ID͚4p܍牨EӦDԲY3 ] "-ZH8@"jת.9CDڴa)&}ٶ.X?N,VZXTTėvΝ;bN:Νc v*HACUSV, n߾W_߾{VvIڤNQ5?3,ػO!zh~^^⾪ %XJڶݗQ1'rKag;y{l޺oͺm~_sL3;oGOtrIO׾QRRr}Z^f߀-Sҥ#OD7LI17NIADٛ.("ڼu_JtАDutiÈhC)Rю݇Sҥ} &]{K{HD{W2%]ӵv4DJDz"@TJDAAA,%%sW\DvuK_h6?qSDt) ulӆBNp\֭(4 @qm[$%׺E "bv-5#k!Mэ%׬I"uႄ6jDDa!!kܠ?%..Dq劄֮MDlᰶ=qCqNj[\U&= p\M{{"p-ƆXjk]:J,&Ǐ%'67'"[435%"KOMMLtuuűFY/_>̲ٸլr%ggccokg)]^R3Krկ߼IټIre|ٺE:Tc6_[/g׮cik_WבCH8.-:zP߾Fd)찁'ʣ4Iq>0jԬiXj;eܸ3g?͛VFgŬ<ƌu˖򏾾Wb/޾~=+]rQQ[׮=w/+޸۶#GظGvp$+ڿ=_=}z| K},4 x{Ą؈+WbesM&ŋ_7o&Z˓_\s<p&-(CǗDOٳT*֭[PP[IK.!HWFD{aa!(,-r@T*ƟJR ss"bvU7%E"!|@KK.z\ |-@2lё="ikkxHWW-^*;zzl! ق9x&w`eS_/ge3Sϐjfت\̖ΟϗϞ͗z{e)S w|y|y!|y;?{^|٭{w>n;eVKVIÆldhXN~6BDzmѱ05Ũ^[PG[{Xn Fr%{ ׁQO>^zſ]d'nذ922rٲe?|ӧ޽< 6ˋəÆ [p!";>} iǻv277K6=yGKھXի3O!zhbذa>>>t+Oꤙ˗;wejVU_O_@OO@@O___@O_@@70З; 7jɓ'FѨQ#oyj׮hѢ޽{yF,K_Ç 4 Gۗ]KقlYKKK+ۦ^z޿ȑJΝ?~< r2iҤm۶Yo޽{+nuuu \G[{L~_%<] uZZ)[_T$ߘ1DZZZt`:56g\8aO8X3f޽{4hͿKllO?ԯ_?@ٛÇΜ9s٬>//oG!!C\R___Y%Kׯf͚.]D"??5k :tݺuzzzj,((Xdɾ}.\./\Y鎲68wOLL̙3{5Ei%m4ZʵkOrT͏ &߃O!zp=;uԟ~R_|҉YÑ^tqq4RR5ύOKIw|̥PiێfTNqƦSN/[DԵk͛7nٙ}Փ|,Uׯ߀.\N*RtپߴD:uw QOӓ|||a wI5yjMDQJCFPzZꡃ{XrƬc& W͍9m_dr_QӞ/K#6ZZZӦM:thq\lgoJyIUV˖-%I~.]Zk͹9M>YfBG|:v:SY- /////?WSToj_"t SCrss:rԄw[(@gnb"trAQ?E^JqG5⥈)JwW?Rsdz^~L (h\>߲e ̙3gww+,[+éq…-[\|t8p ޞ6l.!M}AQ. ٯNi(,,k׮"̬ F>zhʔ)'O411)D${ڵk*!@IKJk0 :tϞ=c?%w=N9))) سgՌhkk_Xٳgvvv%>0엵n x~|}}\[jFd?o@ %Usms5QWt 3Uf ԯ_//6l8o1GS6) +^JD7m46rR7lؐa㰪ʃAaaavvQrBP!% Ξ=kll,tӦcdTт$_WWJ=c)K:BPq!%u+eQ-,^8sw^K~\%t,&RR>@aaaffQ@fffBGPQ %tǔj3HIAHo]_=d߽{w8Q+mRϝ: 7̪hZk/ksss6(H~u{ZZxTZx!mЦmE fl,duKӿsppl׾pa@taܲ*ًΜ>hMV5oъ=h%ek3kbAcCDRT,  |\6o𩓇(%%98 ^oоCg,Tn:HF5TXXR^ĴlC4BiQQҭX"tRSSֲeFyбT6 rD 9MP!̜H9X" % -Ub3* ŅR,|&BE!P%RRR@dJD @J P%Rϊ:4vG'|Wб%S}}^zFFaAQn*f4Q#܅y|;@ؚu۾=Q(owoҙ3BGCJZ陛 (#tX,: H$BGDHI+WWד'O (7iҤm۶ EغukWvA(7)((: !%]~E$ A#]&3SX*ORVU26{l???T*t Pz"hڄ K|E%HL+l2y>Y*e ))('6l('J4hP/nnn/Tkn-6xdOOOGG*UXXX;wLzV|瓥PJgϞo>}ȨG+n#D gb2LLڴic``pܹ7o<}sÆ eOĐJSBߜ9sQQQ fff HMMe"h֭j200h۶zMV.Z|͚5JwT?+[FZZZDTTTtRGGG cfgg+mJe.ZhW_X,ݻwpp0۪T ޗ},)>D-[8995jʕ+{uqqaC?yɢϊdg"H$bzժUJS:]lɓ'}W\I | * 80%%ڵkD6`~===_xaggo?BCCSSS]]]'MfT*?SڭtG5._|͢""Z~}hhhHHHtttAAE6+sofQzZꋸXrv[rZ+5圜lrjjrnnzɖY8˹nJO'II|9O"AJ/X=Ž|9[?01 PUͽAX{_(00Çwss;ydpppjjj>}dQgy,[Çaaaaaa˗/g"_|yTTTDDDXXXPP>UMҥK߿~b* -[Y?/:tzwww'Om"H*n߾=00={8p X\̆ |혔dmmMD999,ݑۑCnԩsF)FXMHsV_~SNխ[8kݺI&m۶֬>۱~#yOwnM o:y6o7xȷS&}{-1ƍtuu333 M~ݹjMB }ǒǞ,Hhcc222R:4<,} d{svvhР=xٳgj"]3g7;jU W=zH{j\zC[t&e] uZZY}i8rT˗...X%uF}CEDD5JvSXXX׮]-,,D"YRRf!!׮]GQfMW^=!5{-TlVbKS"[X:r226!"C-'c1YZUe5ZNF2e#c"ochhVժ+-Wfm`hvrrb9l-, XhkcGDb_g^G IDATA__WwtpJ{Z$|ѬIR>[Sso*}Q6ҡɢϊdILLtrrbڵk'$$UMWbb#+))ߏ5KC=ٳgiii޽Sߕ>nkԨ"u15˶Yfll=ٰg,s0/Z~;<"Z}"?ݻ@"ZnяKڏ._w;Dg{"+7ݍVmUמDr/ã;wADnenDv_veܹ3::K.D{N:M|vh҄:t]FDym6` myV͉hmn{ Iٝ۵Sg9bll\y٣j{UJ{%UOU}V'mLL +?{N}vvv|b+66Nӂ1w܂lSSS##'O-V^wߩj[iӦļ~z?y &<~X"ܿСFS8OF۶lY>}͜9ǹ_ DIRJ{%UOU}V'˰aü㽼 >ÇO>=!!!!![KŎ㓘S}@J k׮s皘t҅_WSc֭AAA]tٳg5kVv:ub8wv5?mڴ}0tĈ$|.~`2888\~ݺu355Sݻ)wT{^IǓEUɲ`_|EÆ ϟ>;995nܸy=z(X5jhԨQfڷo["MNoNob6 cg8[qkpppક=Z)&{L~ڷ+jRҮ:-5Ki&;PR~|ӛӛ%2Y(ӧ''' '//??\>{(VZ-[H$[t@!%UEWwƍBG5//////+BCJ CJ CJ CJ CJ CJ CJ ï7U>: PBP   )ieҨQ#԰aÄ*6z+U2%[-U:dV\*GBGBHI?q;t}(K[6YpaK~\%t,Pm"tP&RRRRRRRRRRҕC22۷Y8A %!ժܢis6xM[}AJ B7l,dׯ[޾CM '+ 0龺UXbz.ݼw֯[mH$5ga@9BJ 0lVϻy ݹs3;-g_,TnHIAxlT&,|dJ`s* X"L % AB)H>HI[()))Tr X"| % _()g))T B)H>+:B$#ulS(Kݻ:oAwo KYQe5㨨BG&: ROۍ.teoʤoYBxo޹#tѾuKg)ie`nn"t\lllJJQT, 6466: zQRʡSN'O: PnҤI۶m:e׮][: zjpz)) )) )) )) )) )) )) )) ))|7o>aHI >+]]ݑ&}7}/J|)'P HI s`öma@sHID JRF*6h/TO6mtXPXXadOOOGG*UXXX;w\G}09P Ϟ=<}@Y8s&+T4!!M6Ν{ӧO===7l t\/PfϞ3g3??;??K$OOOXlmmj*}QQҥK-,,ƎJ*1]hW^]~}Xܻw`UJ$mݺVZm۶}A׭[WF ---օ-rpp077_fb"h˖-NNNzzz5r޽{]\\O<|Q5PTTԠAbـRSSπϟ444$$$::`ѢEpJ-YTrr"={yj(%唔d"z\+F=+QT#ᵫiO?v54=-Ui,"""BCCEDїuֿ匷oY{u;מO,w$1 PU? MMMuuu4iR/_|͢"!xa?>==}nnn'O NMMӧ=_T 4x`OO/^@ҙ\ti\\/^axP)!%򥥥5sLP?Ϛ5- 1߰aÁXX=~ǎ7ovtt477_z?/ל,i{.ϟ#M+tw\A  KDq' 5Ks_,˕!bZ8,2QUj"53\8dDu~\u>P[nz]CZCg"2'#/*O7ΝLƞ-[{J0mЦML\\<s5;;;Cx]Ǐ?qCϟ?V\:;;͝;",YlllU=~V$KK+W*nt+kkkaddt퇎/¯bժUKwb]a?/;PhhG}tɌ Rʃ (g211Qifl"37ߜ>}AM666111 4([[[VoooPq՗Qǽjw5Q?P:GM7Ai?|nj 36Pi?ˣcM;n`JS~0k?dG 8s]{Ҕχ 5Y9sfgbz"娑Y:u'Ei矋|sg+a4p:vvu{UN^}+V9AG}]=` iggհaXrN:uƍYYY_2($UrsLḶ i,2[&E0 !!!!!aرF6lXxxxNNΙ3g|}}+0Yׯ[pOU}w :s)tݼM6mժUǏ}vFFFxU)^o_LLLFFF@@@΢~^wlcc㸸Ç߯hܸqƍ+>DRiʔ)oѢE-'Ow pNNN5j޼y.]tzcbb2p@oJT/]["]rH qV']HjW@}\ʈP*#@eDRH I2")TF$ArrծŻ|%T:JKKS @Ο?v E$}9r_W ƍv ʅHdY.OUZ_Zʒmm{KڴlinfvAzjHkbve|׻|jׂ7O.Pq{TF$ʈP*#@eDR)QQ%6V@=@DRi]:z +,D.^0r-5jܬkH 5YXxvEF_luob\IF`lVU麵+Ddm۸ou]~~~*&LQLPPeVZ69^PPt3%RnDROY(-vK< P_"X"Y@$EPB)K<#](egERHxvIQYY(egn%R)DRT"RHx]Krr#]EYrtr122nܴo[7]KYjڴE'K~#9=-5+ZF[Բ23S.lIlN3lh?({.͙xp۰gON%';yCMkҸG[z_!PVO[[vک]v0'"#ߴeÞ=wrr;;;KK˚5kfff$$$l;|xÓr{k_@$}xxx/jW9H ]F3hР֭[o߾QF/_|{OtwtQ;Pz@eho޼*UFzѣG7n<*"fff_hQffw}|.6ʏKr*~Ԉ/'7/f͝w޽{_ti…%hΝ6mWk׵6Քr(CDRTyf[s;6nXvRc``0u3g۟6<7 A$@M9y7iȋAAAcNrZjeeu :!L5=ӧT\܇==c&%%UR%-+Gӯ(RC$@5 lY iҤIxxxǎ'۫W^Ν;5kyJߏa@y Դ,7vSi<ܩS ##Nmڷu>>>G-,,'ɣ"bkk;|w7n*}?ytNQСC۴iݦe|@!n#F/E*}@==?\DIeH @y۷K22ƍ!{FyGJΣ;֫WՔ\ F$ER7p+|tyƍ|RܹKJJKGED(琐G9!IQ64޽ŋK7V-VPWC'-^bO[0֭f{h*-aU(t#P(3ZVFDD4o|Ȑ!j*2MF=Uu}|be͛S"i<R#QquuJJK˜P9IQ,,,&Lp)aAA͇^F .VVVJ|HHH .]t޽{wرcǎ{nE ۴isي9kP4vvٮκy??UYٺ1Y M6}ŦGͣmۊH腈ҜPΈ(cfjԨp޼y۳gOtttnnԩS{?v `ffVxO<933:rH̘1ܹsaaa_|nΝ;ۗڵkbPE?#Ex_7Ǐnsܥ9yo[7\|IDN珠M Eԩ۷&$\DL?woz5IDΞ9wΔd9.lݩ)"~^ gܟ&"33ҕ>=qر,eST|["p:*JDĤވr%<22-}mIHJs$%')))ȵԄ\IMOOz577WD33HfVVZFF~~\q#3+KiξqfAAܾsJnNݜyyyJQ>~kH7g޸9m ~_^Lg^~ I ȣ"ڟRhE<(3{I-,,-[~zsɒ%ٳ7m$" .TbG^^ŋ̦boo[%Yvk׮]v ]V~ppp066?~'V{]6С"bÆSKnJE zȡDdws 詴]0k@KDϝٯkG9_ǧҞ>:=̜wM>ރH[Dfv뾾#"7n9`#D$uA~}!CHFVVߡCG}gf]>A#F4IDRӇSEZZP?Mk'O.")׮7/))'L>{G3WNߊHRr)S.\("II>dJ;pڴ˖HBR\Μd*OL6k֊u9sV_7oƍ"r%!oef…Ҟhі?Pj?IDAT[oW~\M~m+E[KLM޶hL̼q]~f唫"bii)%V8"ꎘU] C$EjQQQ'OT:ܔjmm}EnnJ<_===̶u={4kuǎE&&&:99)mBoVFFFKi,egz"£uH|zSID^l~vv"E={׮]GD5k7llDi_Ӻ4i.[[ۈHM;vzfMkiب^]j԰ eMq߰m-E^ffJannn[V^xeSڵMDֶiݺՍDıV  322{;z..F"RƦX[;98V&"VVuH K66='"浬ZUDj֨QjUy\___DLW735RdO?HdT)".`֭"r66oE[KqKzN"b׮]fffYYYzzz֭{<;sU} hcW1k9{˖-VtVZEFFV^-88]v}駇jӦ… 5kv<"jo&%%ԠA9{wTTԽy#G.^xŃ W +8((({nڵkppguiq.8{mۖ娠@*677W+RU__)((f`hwi4&իk4 LM5Mzff~~FV_F Fs5%%??FILJ/(mch$$׮h.]\PPXF-((pqrh4QQZ֭n]9jDZVUiFZ퉰0yI%j4f ?hZh<[?x^֭>\E_6mrsszedlܺFjz͛-}}|iŀ޽S][qc5|ꚍjܷoRrkY[ٯ_BRO?lkc3JBŠuk~֬q{K//]a1.-YAbb~\#̘u۶.-=?zow3g.-㉈۶h4][zso:to߾bu+((_={tW_}8a@/FTY>+W"""\]] >XקOΝ;1bǂ #""f̘/(c6l8r~mӿ?eÇWAu[Lr+VȐ!C<==&!xL׭_.-=}=ȦKII Btk㷆XK yݽ`iÆ O~T:~9s|qp\jӰa&N8b???===XWWW%8*Ǝ;bĈ{/+zW~5k:eʔ?E"ҷoɓ'߹#byTto!\*="X ソãSNOرcSիWEDR"]tҥݻ˻o_____mVڂ ,X2T_"MLZsh4=۵2ͦuSj 'nGMǎT 23cժܼ|FReK~y<*"m"r-7$''GFF7W"P6~O/[}">`~#ҐiޠJ> `.('""Ϭig6r~^m޼9==%~R{oW*ѧMIF[Y'HZTzܹT0")*KDΝ{Νҙ3gjځ]^Qk ӦMu"*J߿vj=72>IPfizzzgώȣbSݻwz-V;vV5L@$@-܆쑗׽{ɣ"4))w޹tR]I@GF'F76#Zۿ8??̘1.]z ޽;zW魚2Ֆ-ˢL|IPYS+7trpBӦMMwަMnذfUw< bcc.\v(޹s.Ϊ?7Ŷcƌ/M;h4ΐ2cƌ [:SҢ͝/2>66V݁D$&&Fװ-Hmbbbll7|p]ϟ?+"zz,--ËE5jذa999gΜm HHHHHH8pۛ.O"!O.[lܸq}UuoիO\\\׬Ytzzzfff.{秧ԩSsss}}}'MT~ʊ{>J(44ãi3]!J=IMsF)(((PFLmll^HYӺt3 sWpPPPP˶6J P*#,\C$ʈx>|Օ#B}?ïTrDRTΒDְnݺbGPx]~@*CIiӧOwtt477:thvv5jŊ1˗/5jh4YfYYYYXX999ʀw;zرwU5ܹs5NFSbgNFh"CC6mڜ={V7VЃFFF'55U˛:uj:u̙C2G$}͛7o߾}{쉎͝:u̟?ʕ6lO ,P޽ĉaaa3gT:g̘qܹа/B7=ZPP\j%Fs}vuȑߕ»[CڧOѣG_|vĉJW_}u={?iQ24m摒-?7^Vnnn.\PuѵN9w>{vrr9sF/".]/›D$))Iiggg*/Aj׮]\\Μ9SdQԪUKD^H*Kk=D$((HS@j ¨t]5:ydFFTRE鏏wqq)!@3>66VJ~~˗o޼yܹQQQ111ҶGEE7>NEv_>!Ct{>͇-"3mԨQÆ 9s振dgg\[oddd #Letter# ## ## #interface initialization# #Capabilities# #Manufacturer# #Model# #Revision# #Last step: export interface# #Generic capabilities loading# #Generic manufacturer loading# #Generic model loading# #Generic revision loading# #...# #MMIfaceModem# #MMBroadbandModem# ModemManager-1.23.4-dev/data/ModemManager-interface-initialization-sequence.png000066400000000000000000000471331456466623000275010ustar00rootroot00000000000000PNG  IHDRn5sBITO pHYs IDATxy\5 @k[+JAZR[j[\P[^ۊK]r\hAQY4 $,14 I$~'3g<39''dxEД7RVJ{DvZPد_?񸎨[~u t쨺u9A j]Assf@b Q/׮]u֢98U4; z~ x<A޽[eUVV~ײK=ZYYMdЅvS(Ɂ@QT}}͛7}ݿogajm=/1c(<l"%=I`#@d SRRbee%k4Bfɂ d+l۶mذaK5}O4ۄ@hkk;|0p,\Pu! R*\K*))Qs_,^X/{fӓKE*>y}왭-ǻ{ݻwywy~8k֬ǏC;u˜:L4BfsVWW̒#"WWWO>\pcqegg2TgH͎,鸯05%N```uu޽{BPt5*O{'M[#ғT}݅+wYr%!?:Kjni;$[3ulll腷nxDvvvD iAA^bkkK/f2wҘ%---ɑ=-FWfPJR)!G$̔S~ R2K:V:tmΝ;L]JIȥd"jlwy왍gϨ^Aܤvߏ1u%+kQ0;RD/a:4B"-Jf6[^YYG/5k\;DjvTfIjFFFjПe/=Fiikv'u>La6QԩSRӧ9+{;f"T\)<{1 z(\X__O/155U!M~ Tf`ViTkƇߓ}rO*W Y@Mi|円Ȏ՘J_xһw.Y^K}饗n޼ӬO>dܸqӔǏϞ=^|Fd:>q4B 'AnVBO:AfGetY:Vը?흞4m\5V$jh_O@a5yyu/Rǵ,յcO< ]b?oaa&HVH$]MIIaJ>lҤI6444777nܪU9Ĉ ߿Ǻuds̙3;:Muz1{4ccc9s>}Z͖; "u:*?//KRu).NOVF!t7ա# T R)+H T R)+F\7~7|(ãGV ,~ww׮]T.Du${Gj=}G0+Q_A`+%wrشΎZ @$ltH]cj:p-S-twJ0ƭ[L_;`RVJXA*`RVJXA*`RVJXA*`RVJX1rss:DLxyZ gt=c'Ϟ5ix #B+,,:իo&Q@0m[QQ!@w׎XA*`RVJXA*`ňΝZ.Q@璶}>\GWq͙iT56J=,: \ct#- w: P,<<\G[^jggu[jvJ{SSS{{{LLLAؠǂ.J;+Hw*q㆛:z"v_{(:TTYə9 ކsi=/۳g#ϟ0a۷ <ޞdiitRDBWرcAǭ ͛geeeaaT]]M7ښ0x?XE`Vzj+V(y+xΝiiiYnIRRҕ+WRSSD"h/!d[lQX_WJJ!dO?6lM~7n8rHC?. !;vP>h}R:BLLL)--uww߼~N9uTKKKgaaczH$rqqQ}q\Gѣ:zYƅouM=3R1ukGrg޾][[j.\dɒﷶ0ݿ_uGSSQ[|XSX8IWzǰ;cF0c޼ޚPukQCWRu~~>pʕ˖-ϗJ .DbnnnjjZZZ|rfyXXXdddQQQmmmLL0ko5%VVVzf /bʍ=\d̕la+Wz&VTT;99̙3ݺGZ`fTЕT<ّAAA- 鴅řM2e5kNL+׮,/${"t钲ː!.\ɹ{͛5B˨d)7ވ|rFF:aanĔ ٳaaa֪ԭx^}'K%vVPuWGƍ233333srr6mڤ6mTXXyYem*|KKKsss.]αGAw:]˓;( :t88XZYl!DDDPKzK6Ciq*\TPT*//wvv*33sڵٵCCCfS_(iV_c%OCm:BHJJG}aKKO?ߟÖ́O5RrKT*SGJ:-zf9]˭-OOmmm%BH]]]uou2).cںj<]6666EEZ֏=rssү7ʞwJ<]}N+(ʺ}WkH}qpqqQ=d++kSU^^N9FJiY[?ock2|ƍi3g%h^h袥K _*f'gswʩ&=2+ ݴiUB555eHDJoϼ<ծ'*֖뽲ub~i@١EJw_V\%6ܽ_^\)ՠ|N\^B_E`R)EQtZe#F dW>.(h*/VV]J~t~'HD"G{{zVdɒ8Sy'Je]OQI {nA)DYC>ه:;+ՏJue6&MV%*EVf^O/|O3={ڹ{mmh^s\;ή; _ w>]vرC$577?7oJeHEiV_5˨d))$$Ν;Hv ҭ43׏. R]633/kP67+͚[0?NdaaaereAɖ 釃,,n5Z:]t":6lpȑիW755֞>}YK44N%vVPuW[GJHHHTTH$DQQQ*Bhh(sj(66<66Kv+.IT;jmm+7puuMMMrѣ#""2$BԩS===G- }&w,e<YXX|_~eWO(sQQy9;(ƍӦM377wuu=p]MK^bie=_Yj}պu_cǏ9rd||Cwvv=zՌّݨQƎ~oߐn%GuZ 6O3f̘o;V⚱c%SӁ+WG65(['orQ-]G''';8X0so]v̘1#e>`kJ=ThoƂ_ܼJ>,..vttdwrrrΝ[TTĦÇ-G],&?Morttz饗7 侣%G1_ڻwٳmzʮ,Vq=N^Sq]V}GSSӅǑD5aUQQQ=ܻw [U玤 ) /^/5:uꔭ-!d׮]sݰaCq] +*~/Y:w$UH=Gs;sq333 6'ʯ,R 9PUUUPPTobÆ cv}.mRW xuu=*@MRW x0o*\]Y &R/}!ynM*[_=GV)EvUQQQQQk׮87xtR)+H  SRRJ\s<<qDo'u,Tp#51' g;(bJ _;@OLbJ ztU^Y^@*]!71ŔR)ى)/JA0SLI@qBQT[[QhS]M[Vc&CCC|֦ z%R~;qop͝ !hپ󹎂{>>%r,޷kJ{jkk12۵Rio|QłBܻwё(ÇطT R)+H T R)+H T l74G/C*ݸq Ȫ 0=({|t^ m)8{%%%aNz@['N Ug_`٧CR)(F);h)1bֻuNN̙35P/XE6C>(.zBwrrׯ3gҲ2:`٧CR)(ebbri'LMMkYa[EP||||3gݻݭ%B*wIIIk׮eΛ7"((^'LpmflCZͶ&$$888 jΝתx6vB5}پԱx_|񅳳sGuCѻ.(( 3X;9::Z fzȥRixx@ ضm+Wnݺ)8^5@_널 fE"Çmll֭[Ǭ:|ZZZuu+T~놢(=&%%]r%55H$)P7o޹sgZZZjjZZZVӺƖPLeFerev:ef_%*<{lMd܁LgϞUq;IϦV IDATN߱u?^pA,Μ9Ν3g,ԝqƼ̜M6˕EiӦœ̳g*lSJLL,--ʺtR-(Pϵk!>ĔۻkBHHH֏?(1c޽{)1)֖tY"|W֠\... #!!Y^PP@+**VӢ cmcUbj !|_%f !W)Ubj!ˣ'k3fBtLB7V) $JLM~5Bw/T'M%?Z%&BI9V%&N"rHܵ*15kRFF7!͛EyyyB233)$deeQ/B~gƌCo>bpB/WH++]] !MK*({qrp"yr/\*MPg_/){!̾EM Ӛs^^] # ;윟O:DY,][B"""(¬TYdɭ[뜜ŋˮ̜:u%dzx1JISSSYHQ3Rwww.@#մؘXX 262&ML,,,,BLML-,[ndD15ȔeZX 24֧ˆtL8pAաt333B,,~3p gϞ); vÆe#%T 5x{C[Dᮻc(kSwKyy3]vqq)++SyYYl}m*<]NNNti9+(jFFF[l[. >\SSZSSC<0`D"QX_axl D7Y龃ɜ4##C#=z4!$;=yR֤ .ޞteVdɒ8̓泩q}JISs[aw emr>Xf"ҟ5taV z ..E G4DbnnnjjZZZ|N7n%Iqq*ٰȢژzu~~~W_reRinn… ;/[H'ы))4hsÆ GYzu~~~SSSmmʻlj+{]EY3XBBBD"H$ QyhhhLLLYYYYYYtttv[^^^^^ۥ 4833)SL<{9{)SKͮYwByg)..חGFF/Z?ﯭcL47nN6WHןM }}c(kSw=<<Ə?~#Gǫ<>>yўӧOҎF5vX???ƣW]pu???o\:?CBBB=u,Xppprr3gf޺|Kƌ>ݝY:BȤ >XFau*yёE̝;H [+9xݸݻwcV ZuuZtULLLEEEiiillܹs{>%2 O_.2(pK*&&&|Hѱx2KH T R)+H T R)+ޠ(@/ׯQ:{Tp|||@Jo2t Qhӧ |@3c&!^u2h - 3Bnp ᑫcWu,}_;`RVJXA*`tgVkn~vJjR vOVxalj/~cPR)pi&Lq/:%T:{i&N18T@*~/Պk6}?W!ׯ{ijK:LJc#G0s_rmm͕˩s^ǔtR)p*\))>RTJA'(bJ zt‰)JAWML1%}T Bnb)) R!SR#HC)GXק\GM!Ϟ9`r6 4: <|(mi: ?03<_F*oq!\G} uZ`r@|_/y(dqp]شT6(@O \G@?~̾`֬YG: P,88899(tŋ>|8,,};+H T R)+H T R)+HVMmmTuA*=@ĔMVͧ~T[_u,x 244Xl'EN9HЍx<ȑ#)bP5bĈ3]\6}q:P+**Ýgii̙nݣl@*ebbri'LMM9aUtS]Keee>>>|>̙3  R^^RR0))iڵh@ R4<wVYx{qtt&L}vwaggg``@fkkkBB?x/ٹFzC]/vTXX8o<+++ j0g@ٙlnn^r%֭[*R^zUU !iii555AAAڍ7effffflڴ^iӦœ̳g2wܙ>=,- 99Yt(''^\R@i!\_Bɕ%_ !ʕ%t.B޽(W.((+755B!޽윜g̈́œ\GٳgUܖRE:|ZZZuu+:~v]$%%]r%55H$) pX, 9sϝ;W]]=gz=6^h"Ç666֭S,g2114777++ҥKj(Pϵk!>ĔۻkBHHHקG1 (jƌ{eS윗GsssB!] ˙tAvGiXX=uB#WB9UJ@*1ڌل9]%6ͱUbjʫ3!ߟS_y UbIS !OV)BRNU !'\%}&B~˕+JwWWlokf BHqqq? FFFC@tY"N뗔)檈R^^Kᮻu.T#Y-(}:rDbj:"=kj211%߹(B[[[MLL!<)2{S7ߟ1| F024p1=G;v~%肉 3WQ^a"H'+4tPf_ wcEَ233׮]][[K144T,gɉ.3-@aVm޼h˖-r+X.>\F{2=+w0"fddhpDG&dw;=zʎ/2Fa!!wie=bVdɒ8]Wק(յYiu5fB555555&X{kfzG=赸/BBBD"H$ ĔEGG3W\lٲ|Tpž;LՓ's -Kׯ_xcC,;$$$ѣ\''';8X0so׎d̘1#e>z˫ QAݼJ>,..vttd?>999s-**ɝ>|8,,lqp_dw"""vލY)h?LBLLLEEEiiillܹsGC/%knnλ~K*&&&rJ-A>ݼ(@/EEEEue7xXA*`RVJXA*`RVJXO̙{bwCJJAˬ֬Y?,--.]*Hbs=W[[l" B/at钧'wttܷoY-Atttss3SaϞ=|>„ oĔyaBMHH ݾ}Ϸ={s$\YQQǎvvvo`&$$888 --Ŋv]y+\DɼucBn** !yM\Yr3Ǫ*!7ɬRyTOOOyf{{.\ZTT$!.vܩ!=2L52pHCu7mTXXyYe* 114777++ҥK*9ڵkoUbJw5!$$$OAZ\\L/www/((E۷PXZZʴCvCgg缼<+ ?wvɂ Ӳб*15{널GS3gr䫔*15?ѓUb !_}sJLM{m&!cgԔWgB?_%&!˓BLS~_!JS|'BNxJLyL$xZ5)##ۛrM!EyzzB(z !?3EQcƌNO1|8!+Wkל 45%0ݣ5662^IJyQQDv/L%77WErv܅:cR4Ldײ&5YtY(gڑ LYt9//2ԡC!]kBHDDEQF\](,,,;;ёRZZԡrww9rw˵GmذO?][^^L]\\ʘU.455i852k°a} @03q@CV6  %Lxt !&[e_I ~!--'M|yk!~<@0l!IS<('SNupp$L6ɉk... "L>͍)צL1|!d𨭭}df9xpʕ:r >֏=rssү̻ $MaQQcD"Anq#DY#k׮Φ044W֬˝2ӈ~k`Vl_*-- E >\&L(LJtlӧO *VWY龃ɜ4##C#߽NOw ;X\SSZSSԗv܋wVJA'NxQBʕ+-[/JsssR;}􆆆>lcǎHHHȝ;wZZZȟڨ(H$BBBsE%%_;&N23FE clذȑ#Wojj=}4VYT !aaaEEE111WNaDbnnnjjZZZ|r~hhhLLLYYYYYYtttW[^^^^^A\A*nl2KAAA-QQQk~BN```PP|_ʭ]~Ǐ?r;P;Z2WIpƍiӦ8pZ}R'YwBPٛB!=2L5833)SL<Ou,HoS@A&@v IDAT:?̜y.3fLnnnvzHJJFҟ[ 3WÇG̝;[rᰰ~ߍؽ{7vZy 3hI@111k׮mnn;w. تwws۱i} Ri```bb" da*<(7:.׎XA*`RVJXA*`RVJXEEE]@pשּׂ7f7y͛7@]*JsH\GMٷs3sEA'͞]#slT|iK\GM[|x;癳ƮX@o\}+H T R)+H JA׮Dʱh[NUT<%orw }R)hN^Ρ_t_YnuGG v!^}ssBH1cƬX(uR+H T R)+H T R)+H T R)+H T R)+F\zȷ2(8s?aH! R%KTTTpN#HUV:݂x<t R)+H=kJXA*`RVJXA*`RVJXA*`RVJXA*`ň@=xĉ\G"C 4wԩSNqǐJABs{ +ps YːJA͘ڌ-nߺ!h^H}׎XA*`RVJK)ǿí*괶~}z,*.A*.MA|„J'Q_o {o4N"R)p N'Ѩ% _ej:`uEOjtB515%|k[c`iiֲNcPR=1e6J$&BމtR)pe[*\))>RTJA'(bJ zt‰)JAWML1%}T Bnb)) R!SR#HC)GkG=5븎BIAWVV|XtbhhuTڃZv\?؄T J{͛ڵkڸ~TӌVZu}R݁T R)+H T R)+H T R)+H T R)+H T R)hd wMJXA*m*,,7oEPPPuu5ҥK|>q߾}Y&S8ݔ߱r{{{bbҥK% mݺuȐ!VVVRiZ&HMD>Yn7ވ|rFF!(tA\wܙf .deeܽ{w}HM˔)S|ƍϝ;G/722*//guZ_4x۷̪O?fΝ,WC*m̜:u%dzx1<%%%55uرnnngϞN뗖o G1P(,++aT ڴp%Kܿ^y'OرcҥB^v/W޾#BHQQStT $HMMMKKK/_, sNKK !^gmm*\rٲeR477w…̪E1#&F\zL.9Q  :tGAAA_~%0..׷㧘U400 *..vss裏mNҲpw1tu???o\ѬgvC|>Q]RRjllY 3gf޺ R)+HЫww@ T ٍ7p t+R^{/)) @B*"_4K9993g`C!Boп^OSxϕ+WM]??C%''BÇڵV7nٴi~zz͛7UߝTҪWXATJUTT\ZTT$:Pk׮B}&V)=zD\ rEESvrrsrrB۷B!]vvvf2 !%%%̾Tt6UǏeDœ.͕ENmmm鲋Knn\e#UJ5&B۪K9>'gː!Cx㍿۷o2dSA@˙... *@@LLLfff]6;;bhhH/D...@^4ew_8?رL}6lM߯TŎNN0;,>M]?%oW_//,XL  4$$$**J$DRqwRuRN5,,,226&&.B*"##-ZDgwy'22rҤI9st}VG ̭@ׯ_1~Ǐ92>>^j<7\aڿ\\ٔ)S&O,_fɓB!f>OQ+p>r+PJXA*`ԅwwB*`RVJXA*`RVJXA*`RVJXA*`RVJXA*`ňT:guҞviA*9I@'`(צ: 2|RV{A ((p1.bϕkpZ$sh^R )@DJ "RH)D"R )@DJ "RO+ 'mֵ߮+!1 {o^Tk-^ wIENDB`ModemManager-1.23.4-dev/data/ModemManager-logo-square.png000066400000000000000000000254251456466623000231440ustar00rootroot00000000000000PNG  IHDR}sBIT|d pHYs B(xtEXtSoftwarewww.inkscape.org< IDATxyXUU,(ʌ (j9뫙Uf>e%f9ei9e>fVaT ADAT@8Lk}&\׾н^kֺ׽[F 8N"7p8.< pq8f 1\x`b +K, TthP1\xpc83.< pq8f 1'P*QWd[59;ƑF^M PVFGmP*=`ii9+Պi)!.,/)2f@۠]ks2Ak;RP6r:mNU!XԾz_h(h bBBخ]SAv<uQ36UU@j*{n G!rA:$S[M.zMތ. 0^&\sӡw&s'u\6^Ѕu={>sҡ\ƚa^tޘL[[ 22?lnnTXii;3萴W~ܿ?|?["]]s۷q95;Ŀ;ހ5NXXCYYI 2n5)e^^1!ܽK{Tp~~tQӲt9cA]Lo\N疦XÅgFj*W 3Mw04ۗ ㊹QQ/JTm[ch攖J?Ǥp$`Kb\l٢U68XxXG…@~/p߽ +PPJa%!d30{6dVvކv r7sEWhjY\]R_3]Bj"BBze^ ZCs]noYY9,,|}@5V(n9z}]l,e2'zlSZY{9zqX1 EQZ!=?`@co[%~ [x/Lc12Cn.gݤ+/]ekm-p&Z''c|:^3~ta]{4xe+[n.pnu*†wy$3WZ2 `V`TixitN nn׮|x³0ׯK<(|>jL&l)*jQxܹaYPAw8957uGP~ҭ>WW͹;!XJZ ,]J Iݝ ?'k|c\xd@+͚}x}ݾ쑑ǥoOb e9F XXM_MQRB'u{x9oIs>WUEcrLpu6n.Yb5H%2O^O.as:8zÅgJlmoh~sIҒEYn\xRPw,aa,\H3et7A?;wt[^s}n NJ%uz9`n{Z1M^B`eEXHQ]lꩨ ; /+v /PSZ*Ԑ"6s"#uny« iǎͫo-*Hw-HO>T<@«ǐǎ@)z]`x ^n (d& 9s |:ݼ*Ű3-<RO*BNtWOHݸCq4INԐ#Vrvh@]b3)\x6 HI͓'i(bjlس2zckM`K-MZ2Lزe OhqZfܻwϨu_n^eRb-]"5BX|66%4w̋iS+..V_QQa¾6646F^us~X\ptdzWW#0Pr16%<Ql<111(% bEs 0"r9ݴwՋӅ]?0<&UWp=YW6<|?̙tAjɜ7O4Cn.$Jb6ҒKbKVA|gR-,[ jc-ݛks랾cP߿OOOۍDnI_~x7bҥ^8f %&&']}p8FMOTO ۑS]/igڂr8FMW,,,ɓ'79_SSe˖aİ Gşq>::%dG^^ Q\\ wwwɐ4_"TWW#33W\ao# !׮]JBMM |||+RguuuɁJBtj * puus}:"""[[[\|PTS wҥV݀?CϞ=cӦMbƚ5kt/d2\ѽ{w̑@/UKoksz{=kccc)--%]vո&**J>sz9gP(W_%7o| 󎰰y֭[ّoTUUi}sW+++f[QQQLׯc gmmM_Nad24hOɍ74?IPPٳg5Gee%Yd d޿~P/ړqRҭ[7kp͊+4mllH^^F}RWTTĴ) E1w"矄B__~E{&3eZ{.޼yfϞ4gprr"uuuZ눉a~>Lzў+cw^c@yKp\PmĈZXlƎ˼fHNNf>vH =8xy{{{\R>br mqJh8OSVVsE'Ʈ7nhشYtq~ҥѭ[7,X@Xtzd2oߎdA.cΜ9ƍ7*X3Ϡ[na9ҧO󵵵(\x:P(7Ħ?8|r8Jӂ P:\]](_$!!!Fu;#:88Xr=]ve( H|XoFBڤU1&La4?44ӧO7T\ ЫH|7竫q@߶Xzx"VZ%.R)x:6wca)PI@Cbr3wbňdѷo_b!(7+$ f= ĉ#Gd"hW9H$iV{^W_y uȎJ|ᇂC1$ 99Y𼅅K&! ᘖv1hx(JA˧rtEW /^< 777AA;O"=瘄v#<Ls ^\\9"i۷OcEEE4g￯2倎Jjbę3gJ ;~!.\ X6zh<{_z%ߟapO<,ST4iJ$d߱y#q7:u f͒ZP*߿`YQQk׮-L6q!fyTT"""y}ԇlػw`޽{q9l۶ #FIn߾hѽjofBȑ#p֭u,XGDh e8c ={V,-- >}:OѺ3%;;w5G{d;u>SfyRR=j6o2ˇjULL 82aԨQppp@DD"""PRR$=<<(oºulk߾}طoy 0pqq73gZ N<111|`yQQVZիW#,, 􄧧',--qM4yyy ؎ٌ+ǎÇ#P0#cӳgO[@(--5 1O*>>>8qS\ wƜ9s nKSNu9VͥKֈ̝;O K,ŋ:>[XX`Æ HOOǿowB2=3n8deeaѩt>HJJ?I&! @p֖ymmQìp"##o!99RSSt K'''1Gɓu,;v`…X~=oiee@<Ә8q"O?aƍM`d}ҥ ֮] СCE\\_.K.G޽ѻwo :{6$sёќP:zbMd$9Y  dɩm۶aڴi:tdcʕ+GΝѵkW[I֭[̄ѵkWIu%%%(..ƽ{T*agg4 ,2{;wbԩ:ݧVl2|8Ə1&;;'ODnnnQRR77<KMMERRӡRpeؠ[n۷/&N0װB"((H&zچ@cƌEGi+$$ֱg"!!W^m8***www1&L0lTq5/^,ҒB׭[[n Y;v,!#F=t&TUUx|r2|pbcc#R $6m"ju!ΝK!lذ<#ڱ"3mvv6K^|E簶&ӦM#iii:/2zhImӇĐ(+V0۸vY`1ݻw'6l` kr}PPQT\{ilvލ[XX4Y6 ;|JcѢEؾ}'4I%]cV޽{W0$JCoZ7$$rkkkxxx[UB4pqq)x~~f]vᣏ>zRDdd$ gYFL=z'C=$t O:=Cc5ð?v5ӸN#3ɓO>IV\I{rBoNfϞ- ߿ 11d2L&MD>CrAOjkk|r)2gѬ>>>Mqqqa^+̙3ɥKSVVFbccɢEV4# IDATHIIFgΝ #G+W RWWG޽{Ɋ+Ș1cȇ~]Ҡ5W x7b5f۶m"ISYYIM͚5".M6;ws)*چX\.'B̙|___rIICTxwfz kDiƕDFFbҤI⋆x/ЧOݭ[7=]~zk'wc{9f uoL.Y 6 `Y]]PSS={0ofoe~PO]pO_޽͛7ѣHNN6KBcoArr2T*Q߲Ryzz๯*^{5u>wc\KӪ:6mĜ'988XO>{ァ=)S$K !&jm Z )VcǎiJ%͛g6E}BŰ#*++ΏZW^yE/CL&=JhfXdee锲Y h“dغuI`.)S0n88::6pttlp!Zth8<|РAx9!pmpbxqqqFXࣧ~z2X1ZL !O,Fff^@e>,aŊ2J#1UTT-΂ R!9^Kpufz &RƘ1cjws<==Ӹq5,,,eGmtX'i~l޼Nݻ!HJJBRRo׷u8)6|||.excm6Ç_i #:|ҫW/{H+Wl쌍7… *|љ*8qD\\d2xxx`̘10aF.0NSs}ܸq+tsv:[[[[w[M;83.< pq8f 1\xp{ih-Vi&c83.< pq8f3G\BIENDB`ModemManager-1.23.4-dev/data/ModemManager-logo-square.svg000066400000000000000000000203451456466623000231530ustar00rootroot00000000000000 image/svg+xml Modem Manager ModemManager-1.23.4-dev/data/ModemManager-logo-wide-text.png000066400000000000000000000432301456466623000235500ustar00rootroot00000000000000PNG  IHDR~QsBIT|d pHYs B(xtEXtSoftwarewww.inkscape.org< IDATxwXTǿ ,ADDPWػMQ[LbWcԟ]QbÊR.]"Ev޻ֆ<ѝ{f{gΜ#b!! AAT(  ?$AQ!'  PP>$;600LLTKK04|;BA l]&+MI((P33N譬JC$*_A{ ۀ1 '\O??;ӹ2ʕi Ao/P]D" -;D"Zpp| D"/;X̉1cLHe%V' >HH$ndĭ[Xp'%R)7KPfIIKKzuё >0H$b1`l 2xʜ/CCΦj]q1whğ DD @Zܓ= =Aj- DEqkږ_˃LƉ|f&w|y毅 x&''N'{ N1 x7hx'8g:++͍F 'BNN9Ŏ ⽃~rۏ Mשm# ( OJ@&@# !w' 9$z*U65/DFp7}OAsH 7"AN!'G$xzUJ0 2R? 2ANv3\\Ŝ*67ؼ@4@ܓzT'}F۞ @瑞$&ruȈ{S=b[ _{JGV@7 ƀb[x]029[Wb/rSBkޛ XHv!99cmLLn $6 DG:s".CAT$HCn}?<-,o@s33ӓvh =%4HM岶98pWI^`]x@>@es (Ƣ^=. A;$/`.S{6,{ NCCn<A+$/99RSʸF]JOEOR>s&I`9o{uQ&t|Iy BT  L‰:239OL,[_\ez Boի'Ç}4N bhԯ:[[} B-$"_} ŀ/ps @ng=ޣs 'p9pS+~ ?a h5~i8n e#(7K>= ߼yd$0gbEf ? fؘ!P&#(,uAD)Hbc^_ \zX]EJ`+W ]YS1nV (3$sOoB``,#yx:17-%ʤPx r@k w;:r{ܸ']sLj 8x!Ga+*) :e#> 230OpNq >#ۛ~Ⴐaa\~|`6",J텇ssKcie# T!Hy8u 8qBFFܶ:mINfnz񂻹QukA;ER6)nm[틋^۳Gp0-++.2e^GC_ѩQGN5g˗ uCв2pNCj.7 A֭[5IM֬~_\wmU-{ DŽ5aбvnq^ťUF*)R5YTJ dž ͮW/Yм9]`nS+4=AΐHW3oq<5!q[`L?Oź9MӓAΐ{r_܀6m~4g;sjƏKnccn;z /jF8ML{dGAB>d 0qz➴?KioC - j(gb¿ t}E*D̘L|9̀grEIv6iz]f' uík~xbqq"χ.sUh B'HW7ʽ{@˖@B CBV77r]/ |֭/Z1gmIQQ;^Bn.7E'h"Z VVV_"L:Ujժj?/e%,, ?`7Zn~ $j(**¾}Զڵ+~,X'O ߟ_ᄑO=½{J72<ĉD|~> ..7nmpBNlh"J5~;w ĉ}%'/'Kg%ׯ_W+j14ڶ寫WOг'y6­<9jKjnn%!xӛ7ow HI`` o[pa [ǿ?~H;Iy`P26 ur;ZOGKΝD;Çq!;6cXh~jl?Y8eУwo0` w =˗xdd=}*͊+9oϞ=& A$eĘ'KBBo߮XB+ $11ۄ KDL4 *? ɓ'*3g,'‚'>?WaHJJR)!A|}^:Ν5k(ǷZh}9֮]R޺uk5 ׯ/8p% 55(,,DݺuQ~}xxx~_>,--ד'OGѣG Cff&ԩz3m 0DDD ""%#F8vB:wݻGDD +++T^1by(..Ftt4&=z[n7yaʕ iiiشiϟFNN~GVZa̘1eʕ+AZZx)ZjWjԨ!h/ 8pBT ???ܹsqqqGbb"WWW 8ݺuLH$}6n߾!==iiiH$pqqQFT]g5_sʕ+;wưaPfM{Tu{{{dgg?@TT4tP nşP%;J$?HHH5jwO~a!!LVb999^J*,''Gŋھz*{`ZLJ5lPІall&Lc 6G&MԾO$eG/**buUfYYY+sb1bM:ek֬KKKs瘍޾%'''OL&w}|*6_TT<<=uvӄojלi{Ou<ݻ|:֭[CSNZkCnn.~9zK$ 2D/Μ9{-E,cܸqzC :oHOOW)_l~ʕ+9MTTFT ڤ|Hg eϞ=À\gggtcǎŐ!CP~}-aLMM鉾}"D;?$''4Mqq1lRsN:oڴ iii*˗/W/omme˖u/Q+{_~eƢ AAA R ...W̴ѣ8wB7nݺjժG򂻻; t_kqabb-Z_~]֭[/A,ׯVZ&h^mKm7gvNbׯWhN}5lؐpV\\$ fwfk8uvuy_K,lghhȆ yϢ"VZ%hd>$$DnÆ ͛7U?~+x^C) %KÇ7n0???kd`gg&M֯_ώ?ή_|||7|LMMϫR +((_6S%ܺu}5kBG1CCCv?B;]賳!`Gf?;w_E^^ f֭ӸTԥiVZll۶mߟ%&&[n-[!C=wܹ [d b;wfgf{aWd,99ݻcԩ|}}նevub6m%S?fbXD"Q#)) :T𼒥$uSˋ_ݻw1Ld2e'N`Wf=z`ǏWNB h "16|pv o RiSvm"mƍb1ۼyw6cx,_\@׮]c::h9sFF1B͐!CX^^>֖0sssV'O>|(h[&O>D3g*`NNN6BCCFB1~!411a 6|WV-Ecɓ'j.|DEEu0wu`cׯ_WcՂ}4lPmVV2㏂}t]U]>}&)!>>^FO2EM^4a!Y\\Z߽{NQHˎ+ӧOg ?9rDŖ?vXv8_^ЎKOO$yyyؘRJZm=E'Oj-dةS'އ۷ … v |LL Wy-^[R;wj |>}ӓFժUrRsuue `,;;[4 |qq1755eH$mKSr{{cƌ*vBll,.\R׮];1L1py޺`ܹZۚ;w.['pI={EaNʕ+Wcװh\0 ۅ 5>A˖-yu>=z`]JJJk׆J={ÛM65jT,/ދD3d,0m#G+C )8Ν;x9oцX,i=",$zᅦBYQQ -[hu<@FFou%uVwҥKu۷ש/M׉LF+Jfx˅~T6ZqPY(1l0G`nn[[[޺UFHԤR)GI9>z$;aÆ?߆?Va*O륽tKOѮ]2-X.L...prr}zLLL2@I<~XN[ 4-.ykaPU) .T U96l.} J`#-- (,,D"QnnCi6m[n*NSR ]Qä/;!`jj Iooo饿4[Ν;₯ Nқ@VVd2o]Y6Gf+w bܸqbikkjB<)t!> O}8;cǎEy*W^/Ƣ}x^K~g|gxmsz IDAT~CSooذaJ)+ÇѦMm>xZjܻwzҹ/!7{3"7nu6l7];w5_!D",,,`nnO >ݾ-xwhX,%d2 *Wl:} ]Wć SN:y:?u/_"$$u8x o>b\F]j(1+WVKKK3FGGG^GDDիkQ2o-[&f:h _*u~~~CZKJ*ruML~M0{)Ν;zqNM$wߩYu#sss,X}5 TiӦimЍ53[A=Luk֬Q>bbbMGR>yo`.փ<χ*Xw U{믿G n߾-Z޽{jm۷Fಕ 1c%XHǎdeek׮8zV7us%ebccvXVR) y뢢xglmӽ{wM6iK͛7Gy^x޽{^!;v'(oӧO޽{/^ضmu?\pKM1vXP6{pvvƬYX\\ǏcǎK044_|!X#FK,ӧq}B&˗HLLݻwqiݻK,qQ}ӭ[7ٳgcڵL&ÿ~]oʊ`ੌ oOHIIUPV-\Rm?bXmJB̚5 ǟpMTIpݻwkR]|L05jw}SN!00)))JH$HJJB`` Ξ=}aٲe6lݫoCk8ظq#z%D+Hm6l۶M'عs[lAaӦMشiD" yp!<|P͓'OTyO>ZЎN:A,> J$,\ .ꊌ jmѣGuO*UBÆ ajj44c l۶MԩS8uס|KW[ ߿oluaݺu2M#OOO@; Э[7L)m68ڵҥK:1T>>>jCY}XZZj|jMTTΟ?wp9ԩ]NNpe\dddGj$*??IIIe:o˥Qŀ0c [Np QLMMsN|˗/7|S~4ѴiS\|UV}}c?4pYGg.\x}թSW^Eh? |o_7n7nɓն[nΞ=8xzz" @~i&̙3G\WZjKKK=Jwj׮3gFԄZnU&M ** /L5j7obĈzMhCߐW0:wܹST"[ѣG[޽q]=zT[WիcΜ9r : Ǐ۷1j(Bb4nܸ a / &&3g*H$B;w 6iѢqqobrJ$$$ɓZ焨TV^hL6MY8###o"JQ9`e9044D xXH`z!"p:WWWsssq]޺ j=mGÇ򣸸'w^iBL DFF"** HLLj֬5k:tt!--MN 5ׯ#==O>Eff& ann XXX hڴ)ׯ[TT$I&ZpMLLnll ĝ;wxժUK8CfddFtt4bbbwwwԩSuA&MrѣG vPfMh3Ԏ@whx&GM}èMorxǘb>QZ_!;%R o)_KvP*cTھm<O|~ucH?bR^RПryZֳW^2>(A]BRY=;B)62|+OćOEJ[\`m s-?:*D ^CJJ &M(_~T*ٳaff& ƍ7:u{ 'Oi֬B-['OƣGp 0@%ړ'O~3Fa3** ]6>S0i$ Fϟ?Ǵiֳ}vSyiǎxB2;w֭[HKKz쩰w4UTA-дiSqC!99Yak_RR< xϘ={@"PN 6La._۷1ugT\~!ݺfz6o"mii w774j51=ڶi ۲b3LQ8pN*+s^;P՝pRO%0,=g.^DPhZ4iMD̉ǿaDʏfU[[L3>'O0pwqAu^Dw!d=05жY3Ԫ7>'N <^fgcѣpj ;w0pp@ڵ6L_zGXl,N$m4WMJed_27)k÷FK'_GDB"cƮQ?<ћ7yj!K{ǥ5鉻wC*cy2 ١_5nݪk׮W_}˲u@*pB5kqp7NanB۶myǯlsq <qㆂ'… aE6l(rJnN;v`޽[n7Ǝ~6>>2d|}}3fo&߷o_={V=zڵ+R兘٣6j/^ !>>9FFF}]a7`/aH4h~߼,@AAHVp㗜\cc*̲)S| eԡo?TZ{(L\G.]ˋabd{`!RBҥyK''7&u]S&Oǿ)zvmt/f^V]EװOSZru>}Z˗ѻVX-[w8~8?3gT|7nTQזy!%%E600ĉ!HիͯZuǎahhPdUw!ܹs Ѳ|||äIs1tP;w?ҥKadd'xv 99gΜAݺuqE^ 08|0z)x"'MRcΝ5{6|RD!uY WҌ5 ) xp.~X EEEXv-k8u*yF<Łݻ}|0<p998} p9>> 3331\^vt~DD:VGJ#s [ W5sGp0[^gnf}ޝ;55 Enj쌗x]kעP"зsg|6dN|Np~~8t& Oԏm)1=iYY=!Q /G޵wXa ԍ:J>ϣWBjVNmc/Y%cpɓsU¨v7|d$Z`kk6mᅲ?oٲE٬T[mڴS0p@ԬY .ٳgallŋ֭\\\`aaQhtpttT8g͚X!FMLLTlZXX(.ZT^^^8w&")) ׮]Ì3SGaΝׯ|}}ѫW/bŊ!TfQZ5#G=-F*b޽{1@ 1gc+U&?? -B5p,[uA6mp,[`ffGGG4m G}'$@&a033Sprtĸѣ<5lM["5-  -uy IpO4!) ]R%y{ʕG{8sGiV=uI%(ho, ho'{{tjg}bرϱcj+ F)5_-cժz5"bc1crT_7bcG;;4p~L&;TѺ5j;\*1!&9V4wwGcWWye+؉Qf;>=ߌyC*JֹJtWZAkZ n:t ݻ7~G̛7v(..ưa``xԸqclSx"?K ((K8{B|o[[[twwwZ ,ąbΜ9 e?VQ+WzÇabbիW?ӧѶm[ܹs?> q#Tlvnnn*)pSSS!Hm۶A$SNzf̘Ǐc֭ yWK+VĐ!CH388YYY6u*g-`W Z|^^ӑ nDtL :yyA*AP:kRBxԈX|9OϞ٭.^|Бб}{|>n܉nc8x07C^x8* nc~OȀL&Etl,6ҡ?R.jL= }|p'(_bCH/\@T'OW+0Æ!7?wF%KKh@;kWZ[C=NƋ<\ Bx\V}LtXcLb Wìw j(Պ#3';ΞXnnpZ5PͶ2fx\cX?n, DS$M t ҥK ###|w KH pvvF@@رVVVҿ+:u=z<[n֭[...j̙?'N2بtR~С={6N8!޽{={ӧrJxX2=)6668t萂3T*?KKѣ4y2'$|%J~Pyyӧ6.jYG]]_"ޛQ$@!Cp\|ڴ?6[V-Z-Μ?è쌮nz FR7LMдaC٦YZ5oRh*sZպ=0Gs"}) F%|}ain?x5m.-ZhW2wxRA ?Mڎ>/CR3\XTph'/Ā-0uS ^h^֮](Y+VP]¿}%<<UVU˗/# YYY8r^x!8%ŋ@~DDDmohhݻwÇ}OfAAVQ.,m۶mqIDFF޽{>} 00III8|0:t耺u}WK~G/'OD"A^T" addݻvXaB]WSgvlۆC>>XOjjk׬?GԣGի!3 X,ƉS1awq70ᑑ|Xn}Y__ʏ)-3S'pOzIP&9( (9S.\:OĝǑMCLBvQtjҸ aS?GTBjP~}.ٍK~8r=y)zB޹cw+W% ?֛wݛx?&ci޼9,--sN(S^pAAAݻ7mHaÆZLaddSSSMҏFaѢEXjonjCCC/E űgaΜ9Ȑ?@6m`ll~ ϟ?Ww!}vfl޼9,[\ᦩqpssÁq A0jHWͦM`bЀp][#վ=""#񗯯9Yؽo99 kktΞϟ«};8ߧGa``ϕv_\X]jS0_Mm6K_Y771-9$$'cZ*W}ӨV/_җ1vڅc„ VɃ'`֭6m gD5eKرc8UʡM,c㯿bAܵ+fϚ#33G Vr&N t~.cxP>d^Դ4lZ^^޻GD"}zt {/_4 Uh#sF4ŒoE֨)ߖ;}r=T }^9GrJ 6ۇO_(8&7//_DRj*ZF²w⒒4.fp0<6RNw b#C0s~ ]Aa!fnj--qmegEwX ұc[xM98?C_oE{ySc ^Hɓ… _۵k{*+++YFǛ͛7cWwFvT$1ZuZ 3<==qXIDAT>} 88#FPY`bb˗cj)ߞ={b׮]eڴi9s`Æ >} OJJʕ+1zhuD^XSSSܾtQ1_?\p}9~*uVR%L2ߕ -nnx =?3~|U~fA&aA^ѬI Rq+aO0E0oRƥR[ᦖ LpKP"kլ_W,ǬS%=}ѳf_ptđMPͭO+nƌST὜T_~K5 !!8z2^,oײ^=~l(2) f8pi[>./dkaرHƹ{p]\{ټ^[ hOG=izBP ZWOAZLɮBҸ+-8J.x*QņPmy>x-&5M_9inR|7|4J7oo x cY'H.M@OXD]!xd=ASjݎ1mBĈAu8Gic)SZ/x1;GJW;6hY9ڔG9e)WO՜&$Q^GDě BOAx AD  * $AQ!'  image/svg+xml MOBILE NETWORKING POWERED BY DBUS ModemManager ModemManager-1.23.4-dev/data/ModemManager-logo-wide.png000066400000000000000000000254111456466623000225670ustar00rootroot00000000000000PNG  IHDR{XsBIT|d pHYs B(xtEXtSoftwarewww.inkscape.org< IDATxwxǿBz#E`"A4T\ ^~( E)A!4( ҔtH酐ǹ$ٙͦ2g39}EF2AeCw {H Z$A-{ =AD Ğ , z AQ+DhdO 'hAm(/c|A;נՃMI8 Z_JJ{[i)PYiZ5{{ukU+ Ğ1rsț*bhrrWi8; f@r + |^PQ0xЇ.΀ ҲaEQK^御ee }**L)#ж-J#~  / BGU7 n n-+{1R3`oexz^^d'Ib_^nOT;;4 cD rGee@j*ߜ~ FJËpv}\/|u߿n=اRӠ &2GS3\|mATqF?s7ܼSs?o \ٳ '_Unzzq;|mP@n/OI2af76/~ ?AAXb)/Ha^qy3-_a=]ᓽZOO_-xþ6sϢXY=hH<}z:7i88pq?S˹_367$diݚ >V BMRb"]3WkeL]7M޻_$AD'$?@J%0b52BAh[7h9Hhpr&L^yŰ;F> {2ƚ-L{y!!ر܎.Źsߚ&έ[ U\ !Bt̙&ňv^P __aMVe$dH:6Mz4/LΝ*q4A-;A[Z%M;/OrV*+yv)];<ו -1!3_[M:vdA5i68]ii MWV.ֿ'^APBTV}'%+F#O B!rX\ l2p QXRX[ O޹c\=Ah}oo w% *SRSGߜVVָѨi:.o󬓏> <VYE;9HM޸!M%j5'x9#A!||x2!?xAcNN|·~i) L<Ahb_^l]pe:4r$_Vph$#؄lq1ܕS[Z,w87Ğ QAŅ^Kn.8{ޤo7}X-A4m 1\XX tUf&Ald)ߕBx™ikEkÚ5ms)^.L{{IIBODi{㸹qǑ#‚۳(1.e(,BC>nK? \}оp~}$Aд"}H!=n8|Gfm LbV"Zd2&HVB\\hyNNɝ;%&n摃X*#W r.۴G .` IOO/^rehlZ2hDԹ租~ŋV߭[!7I)*ӆYawwB8"ט+o!vvVl8uMVA5bVQjb5y%aQ6 pu5|?0hP}  T.CT[v>{-^Jbpʕhԋ'pIŋGQԙػ EMU-ZHo_6m`K'`!?44F ݇hӦ#ľSN:ul… /Z1A~~>en2ZtL!''jMaYYYHOO7QQQ???zo޼۷ˮ8y UpmdYc 0Ɛ;w HJJt71ISթ))))s1NqV\N ŤI`)̫ F7sźujտb=zߐy K.mpJ&Aii)ׯ#** qqq())J?\ŋEll,bbbtќVVVŰa+Gъ+pr 7nRFj|DLL u?8J;GHHqY鶛7opwwG1f}'O ddd J~%;;;}G0j( ?,X@iiiطoÑTST Cń 0rH( Ď0'OٳHII]P(ѣG!޽۷#""111} „ 0atW\ :t`*/_k#66チW_EEU7 o Ğ={p)$''#99='|Ǐǘ1cb܇UD7o߾ ֯_?c .,߼yw)x]cϘ1CBzj.xfkk̙ ]Kqq1[n2-mmݻUYJJ`}z;~lرLRjɉ}1***؁СCB:Ç^СCѣce˖ oذw~sncG|ʔ) gseݺu3ګnJ3%&& ~50af7of4~ >S6~xj֮]*++ ~1v]b $ݻ޽{={h;f3ZYtt`}cǎvB`k׮e#Fّ#G?$ =scz ~`ڵ3\\\ءC ~6qqq,00Vm܄ľ=&(x }mQF1___W*FM }aa!+wsscaEEܹy1&_GiW_H Q"ܼyZ;f/yVTT$>@PGL݄ľ͚5:mmm7|WoM7uSTLV~N1IsoGGG,YDoZZd["I`Mu֙wߡo߾HNN6|-9993f ֯_/z̙3gгgODEEժ-CwoM#??ǏuЛ!~gܒ0ӦMCFFn˗/۟%믿Fll0_)lR`ʔ)3i1 ҥ ̘رcXb`YII ƍ{A:s(..O?g1b<6D]cU1OOOGU8cFϟgf( cEVۭȾ 4lu;;;]ٛk c\p,))a~~~z2qrGW\1g1k,]B#{smϟv-SL1kzfr6x:#{F&L`[jnܸ\#ݣzHyK|I&U۟>/ӦM- 3<g'''DGGK^{5 8P7ih0~xIRÇk׮񁍍 8ܸqC7ЪU$39ZYY{ٳ't邔\zhM6 amm JU`mڴAaaA?-PN T*֬YB~~>֮]>͛7#55>Ric@nꪗ}ر#\\\F$$$ 55Uc֭5kIkFxyyAT"--_5|IC ыUhժ888 ==]w-R^V7o;~M_֭u;;;ddd &&ΝCqqg(RеkWAAAs"##qQAc͚5زe[m۶-ꊤ$t+k?q,77Ww\nn.stth;_`sŋ-lժUVI&ٽ{qJ͟?_Z щ4>//988յkW+XӧYNsrrbweI٤Ij`aaal ׯŋu{XDD{%߷o_k3ҿXFFw2777OWܑ=c^x[8qڽ/Dvv6}:~I):W">>^ٳg7ސq+Jlڴɠxv*XaIS뇗_~YA|Xp!ܹc|1x CXB䚯AU`UuUVɮ |hO?G TZU%77eGFWL>>z"/R`,4z7hY`ш#dĐ2YcǎFilPdfs7߄hAf:F߾}1qDMZ [i Q}֭EA,,,vZ!fV`rR~ zIDAT́i0\RLPmb=??}L1y:K],ygyČeƌ(//z{{6'iRW^+++AS6k}-SPVa6ƍemڴҥKyzꓢ"* bcc#TxQTcii{J9sYpttt۱c-Zd.c O,ݻ7= QZZjr>Hݤo&^|Z)SԺRٳJ|G< ΝCJ&W3gN]D흝 d|E83gY}}}aee%_~֭[d5+T۱k.YNj駟Ƙ1cD 3ik!\]]W~s!!}gǞԞ=zࡇ,Ŀ/ч1XXX ((H,++ cǎ5pC~„ (/O.\YY7"88X3gOm۶}1c>3KۇaÆ!<<耛ts7:$}1_gΜ1w׌FZ玎B+Do̞=5kn;w8w9bP_{5_^:uN:vaԨQ`iij[JJ ]?~\m8.8y`L:@ǎu9r~i{2dv)X6g;w}E@@\`۶mسg,/wy۶m |JJJB1zhܹ3K.4M6 ˖-tƍ///;QV~GN uE{ ~+: U*صkW5ϗ_~۶mùsD]G.m۶ƍ1sL㒓'ȮלGP`&jnȖ0ksDٳg٣ZvtU&A_YYio&[ƶm$M 3f+sfoܹY]r%FUmB}о}{%DHHDó~zٮ~R>ؼyMz-ӦM\܌=A-B`uI 8uj ۷ou'ЩSZe oVC777;vL2 XS!$$^D}Xlnb^k֬1y}@{k8qV  .HjRsׯ_ǖ-[D=g6&'w#:̙3?~ѢT*1n87Ɇǖ-[DfҥKe ŋӧשK… q)߿hHZ\D-[[n? .iӦ8~* 3gDbb"֯_???J%z)l޼jZr!9.]?~޽;Onp;wcĉ:bccq ̙3[|U(ڵ+mۆd,_'Nݖv؁cǎ5ы 0gΜoٳgvR)S`޼yF[(Xdq0 hƯ5Uquddd ҥ ν{$&&Eǎ 7oDjj*rrrwvvv]vxG̖Ց0F۷o#117n܀Z ;vTc7n@bb"P(tѡCTqqqCff&Ѷm[z!Ei.\[n!))I;χ7dkk ___<裲_$A4[lll'׮] 4@ oqf Z٢@- 'Yw߉k׮:H 'Y_Ȟ O?ʕ+eF6o.h)--5z> =hT =AO>>>>1c+LVرcxW1{l,?Y=n%A TTTڵk,_K.6Ϟ FDDziq|KϞ W_}eq- 'QQQ{ɓ'ĉ-*$A4:T*.\5k੧Q+ <8s v GG:iӁlA4zݻ'Ny&ܹZ,O?#G˫]-AFS94AKDs^>IAH Z$A-4lbAMinAu Z$A-{ =AD Ğ PН  X]}3IENDB`ModemManager-1.23.4-dev/data/ModemManager-logo-wide.svg000066400000000000000000000166161456466623000226110ustar00rootroot00000000000000 image/svg+xml ModemManager ModemManager-1.23.4-dev/data/ModemManager-states.dia000066400000000000000000001220551456466623000221570ustar00rootroot00000000000000 #Letter# #LOCKED# #INITIALIZED# #DISABLING# #SEARCHING# #REGISTERED# #DISCONNECTING# #CONNECTING# #CONNECTED# #ENABLED# #ENABLING# #INITIALIZING# #ENABLE# #CONNECT# #DISCONNECT# #DISABLE# #INITIALIZE# ModemManager-1.23.4-dev/data/ModemManager-states.png000066400000000000000000001125601456466623000222060ustar00rootroot00000000000000PNG  IHDR~FyysBITO pHYs IDATxw\SF ,j[RZk-:@Qbmں֍*CY2dCX?^@A8ߏn{psw/!$_L@DXzB4ң x~%’6BȨQ.]QW_}5j(Gϝ;~XSD*++7666ϓʡs҃N3#h!D,=!`AKBXzB4҃$_~DPgϞ;-aA2A$:҃dɈҝꖰ  .Xz,=HF u$#|:KXzpu$#A_p҃du$#AzPG`A2\# aAzPG`A2¹Xz,=HFXzPG`A2¹Xzpu$#|:KXz566I4JJB҃^b}AAA/^5Jz!Ν4iP('Kԭ`AmhѢvժU卍BPYYd666:tyܸqCUSS;Y 0!t瀺{%K/_7lٲ%++ RRRX,͉Kj'O~G-ԭaAm% [|#&&`9+M\j+&,͛7cAmֹCr -)n G=TTT.\(`06oLK>Qj*KK˗/_Z|}}O8AcJ;QjMM@ї,kÆ 4惺),=݂թxԩ惺#,=8̙3fYtPbѢEʁffft為%,=r|}} ))@bb" >>M4 x<ާ~ <>wx)S;wx2ApG322N>Mw"Hښ!ȃ*˥; ԚL@P¹yHNNqe|RGGG__\zC]]]ff&~V+ù9ùypvvc-{/:M]]]_NdP [tigxm۶}BPT !FFFG=ݵkW_}IGpYTTT f͚UYY)`JFFƾ}A[N߽{_?K{;쵩1b޽2d.ʜrWzDSS1cteǎn9je޽'Fo>iOQC{ZS%dee,^555/_//ԔDTtgуꚗwARO&&&֭;G=򠬬lllLw5} z!11qԨQ8鲪+++544ΥGQ<444Ζ]ȃkAAŋNIaddvZ)p#ʽ{; Ԛ/^TUUѝHOs=򐘘8b777tY555UUUꚚt#G qgˮL]]]]],z77K.ѝ*""p͚5t'SGX,8UWWӝHOs=򐘘8l077˗/ӝ jYmmmuu5Ʒ_pCCCCqqqYYY||<áVp8FX3fp8cǎ;wp8q[8͛g„ pup|}}௿p8~~~r8?3i$r Ù1uuuUUUo*VSS՝l|Kn Y:uĉt為%AKzǏqt ŀrqq9rHSSc'Nxqy&/,=\ɓӼQE_~7oޤGGpIff}CC8889rC:}4, mW2B-҃j̙?G 322#yj+W*++K{tҥXwPaAmr[+344@v6!j/,=ZKj `A#1! n `A& K5!$ .d ?x`@7M6ѝEk3CSS-ӝBOG~9Bweܲe Y Xzdl@-kXp!D,=!`AKBXzB4҃ h!D,=!`AKBXzzb{zz̙3]]]]]]gΜ.hFFƼy<<<&NxWKOO4hPdd$.At<dzׯlN>Q׆dgg rʕ+W gggSfee}g;sLBBš5k$0uԐӧS-v͛7 qD].~ɓ'O6rڴi?chh()SD5~?͛[l8p`{d|>kiim߾8An?~x|p-*}رc{ҥ+VڵuGG}$8A{o144”J;w;vprrhT<勤TQwxBRXXOzzz-~+WyxxK5 dMuW  0̙3-O0`hi~ ]8qbРA25 O>DSS9Ç?.zt***Fݻg{'~… Pρ'j> r8}vpww߿uՁnV[[_H\3fx̙3[E;`o֙3g :E]G={eccoܸz/mB zB4҃ h!D,=!`AKBXzB4҃ h!Ds]~! tgZDw = IMMm1B=y`2tg& †&Lw.or Н~N:{ sAN3#h!D,=RSSܥK:;300 v̅ ư3660aBppuDѣGL6$Qp6ϷCC#Fx$ GW""`u %ݹQ@I avq[HNPRaDc`ui/ĝ^;Cr3qucS+5ä<fmj2#vC=9} 8NFE $ $$(=PReu?& d[2CbM4fd7<{jj +y5==~^9-ũSl<} `h2ꦶtwpp,j3ŋO`;~!ٵBm&M+$l/Í4CQ` EE.yB;{__x 2aJ^~Ofv%U4π%/|a=upQEY{H !3z@ʉ,[odjj`RǏwo`ڴ^"RcyH4Zc8w8N7 a c ;ww8~JJ 4\\X [!q#nNubluIX2R@r)= +Z텅A@sBX+W ) zMGmزSpuwa ,:=z/ )}n#żiJtov.ҟ߇owGm%`0`ش _^!v!Bn==\ XwsxcTlmi7qL󯵗nCبu=wСt'.W_X>r;`2~FzpIJw߅J<nB]kfޖqNc#| *uYOIMMƎ'AUPW4pڗ5-fz QsO?nn=F5QOF  p$G.#%o0t=`ꛎYGKb 畞~ IDATb5 ^_@EPPbY4osS]㑱{ʟ?1JOc#п?DEYHF Ƈ'sߵ̫84jW}e u?=,] w97hpBmTV;9߻aO&!u3=i9:o8qA/j_UM=9_xaߎU)=yyg۶-)$?}jɧt:'M;8[[2q7ss;nǔY:ES,[a'L?ްIg$x:}}HMӂ9ʼncLRN̕};IF8ݬ2cor'Jƍ3׃P'PReg뫧gG[ P\@q1p8-.sLL&\p?;v@} lâEbP'PReb+'콛v>t] 1C8)fφʀ˅LqC$wqaiZK9k > aA8]ʨStE)P0/_rg:Gc0G~zp2TX[[?*DH^t,tGlQ JkNQ'ШܳA=+~;.W62EH^L>RRQ9&v6t-Uz|pxu%I`dA xAK!Br~IKbx}. عTj;oѝKpGɄ@Wl%QWW_);BJ?3;0d |ګ.ҝKWvs8ń%%%z B˗qSSӣGRiqccD\VV&744Lf޽iO^^___/WTTHuuuqeeX HUUUqmmǏj]]]iqMM(I3o؝ r:kppKF Aijj%%%BHCC(++B@UU"@MMRSSꄐ*$TTT6!ttt!G)))BHQQP5 !`ddDBHNNB?愐l$dff%<}lll!O<[[[BHzz:x_II*F|>bbQ*l60L>.555E(ѡ ϧX,>O-K{% {-j766b333PQQvKKKQ(VPVUU666V]MMMtxf[irz+VVVqqqjytB30 !bP端Nӝ}!OWV]]JM]NESu௵z' ))));uTAoFZ:iН )))t;* ']k%s $oF6 U]f=Ѓٷo߾}ҝ:%%%##Dd1, 7\hLNN>xggiӦѝzgPcg !\LNNNNN; $39F0TTg\;z\B̫a'+t,zܝzL@RUWW'%%Q!EeQv\hLLL~D]S||3Ny wt"oD@RihhѝzLMR.ojj:iҤgJl6;::ǁ}/q 9s<;Erb+?ߥ;eЬKKK>|ϟjjeeel6˗/owS4PɋYX-guSCݹʹVگ^^__ѣGGFFB;zh-[6dȐV:(=hll&ӷP;8}FZEXnO% 88bmڴI1##ٳgjjj"&&J\MOO"Ds%z5ޣrEExͯyҴx<:Erv3ˢ>r=>\䅖'_Q{]]]_.޸d.]*q•+WtF=z422j>ڵk0x`yv"l-sJݹ ޽{L3NEEfͪZ7fطoCYl۷;#1b޽^:HSS޾.ChXmmH1뇱<< ,߲g7|YAkimZQ+Um5,́04TnJMM;$',I3$̽ȟa޼ySwcKJJ4hP`` ݉ 1`!͛8ܕikk4o_ſz[@E.=,j 4r-1p@X(޾}d0@Zt%%%ooo񸱱1&&byyy5===޽!jӧ!$99 !ƭx6̠~߿?!ZÃB}pˋB8`Uoܸ>>>WGo Ve,-;¢& GIIIX,0bUUUQJJJ>>>Tkjjڵ@YYGGGGb}}}^zQEP |}ϩ U(}||LMMEfffv Qlii >>>ԺTlmm-mllckk >>>T;)@e*5>}PY㐧a0\L2>)H2Ѧ;NpKegXYAf&ݩ $ˋvM}Bޥ;Nk M5T KB]`AUzM2~a p"e(h,^Nw.]]غ<=:عll + ^B]]/xD:E7/=f ˜1*:vvUU,mz˧%t')Xt'aoOkk @G.W2_ҝHX濘Zͯ俺CZ@yv)݉t (=GIBʟѝHP|u[-yt')Hu[ZPk>!Δ5TTkt)Pi>!9PS\Mw"oxuC`>8A[_j^НțX)55PNwoX?7RgXyOw@NNΣG?&-xϞ=KOO---첳?~,[YYfee=yD322222c.'Olmmobb󝜜}122JMM-((b#@8\ ?y!=̟?}vX`!d֭pBBw}/&ҥK !| ,[yf !lڴ VXAٰa#|WzjBڵk`͚55ku!VBV\ 7n$,_6oL 얧 ~-!dɒ%eBȢE`֭ m!_~%ر?!U),,;w$̙3"""!f]vBfΜ {!̘1~gBȴi`޽ؿ?!dʔ)pABg}"|pȑ+Wsװa u AUѝEann>l0j%1ZŰaèU---[i++aÆQ+psVک[mll rV"VǛ˷+xaÆQ+S1xloo/ðaèUcGGGZŜ6Zݜ;99 63l0jhC7o>zѣGEٹ$דr7ݹtuϟ?^mQuA7oz+n`ϟ>|?@w"H˗_|f /ssÇ;::ҝ*99KRcǎ;v,Y|\8? tY/_466ƹ8Ο??rH=u5\xqȐ!8A|ȑԾҨkJJJwqq҃t}| Eٳ[ZZrqq)++ùPFMKe˖EGG\R(Frrr;$Ubbb~~+8ƌ3f@oq)lj>]kyy =f̘;wҝ*888**j4# !zz/4X1M4i}NNQ羶w"::cܾ UT۰)KK1cGn=2 d2uttBBB˗/x|!z444bQI\ѤIrrr_yw cȑ~}; 00媨5*::ZB{$$$DEE?!?QvXiig $}Zt3fݛZbQBCC]LG]]͈ESljhhlΝ͛~xT($/7o>f9sǽKӘ̙3[\ߞ5k͛7kkk%ڹsy šP(,((ؼygՍ2eJCCC.\:uj-]ܹsooۚ5$NȏM4=wcc23ův [ s⌍KJsYEbeeۗKJJ;v>00P__fҥKObcc333EgQs=JJJƻvjק%~wygM]yRŻhq3ǟ;wKP6lqr)g ;lW+,- `yz"/{]QF={Z.zjޮW_%&&Ԝ={h^TTf .**/,, ?~ٰa˗oܸ!ثWϟKtѾol2nܸk׮_zZ:p`ŬYUV @P̣zqd߾Aen~Fس'jPZ9_!eee={6%%v={ : ۯ]&ߟ)*1$7p8˖-}vQQQ9pY*++E#Fػwooܸq#|9も;pǏ2f))ر㔋MGKK{'yW.\ mƤSԤ߿ĉ7ol~o0d}}oرc^^^>.~qj;v71>?gj?ʺu0c +++@bdɒ%f>z24%/qܹwE>KJzr;;޽'LrBb&++;;;˳Ss7{6mjBBx)Yzc{=uС0j3,qsND KJJ:txZ0((࿏XZZ޾}tСvvvnݒ'.qqqgΜp:|s)O?B&333))GkkkЛr &ڵDT/|||pԃ˝0ay؜,=Hq9rȑtgZm6A&##quu;2@s=H\tiĉw;$… WA zĉ8޽鉥)#F1,Pkoߎs=Hddd$$$X[[ѝ jg}}9 q%__={Нj'O4hzⰶ!OWs///,=Hq\O׷cAӧ666;2//F JωxWta6)t{ IDAT/_NHHuss{(~qbbx\\\Ғ흝G888W<~ajjc>}ܿ;99ݿNNN|>_}㔔G!999--^"## !#|pBg}$P>|B-A}QBϯSgWMMEkmGGGS>|p|Ϗ{<ϏxlooG݃G=FԚ펎v>GmGm<ЧOQܷo_????ٙj~Q{ڸۋb-nnn~~~vvvЯ_????[[[߿Dlcc#ݽ=<<*++EqUUOOϚji4///@`iiI,,,|l++[n7ߦ:ߖA̫:V^)^jiϮ+(KB]Q}/o*س[};r 7Qr|KDE x?BͿYc~t&&(ԇoՐ fw7 7:к.Br%<4{9h|EN9t_z`x,5 (- ظ**ͦ3I,=AMMw_>D!>דo22n4 #ܶ< jd(h=k >9=]UPt0LZgj8s~AJ-m8zs/98&r8m'0Pif ܺ./dTߴIs9!j+C$lmPT+7BQ^pQg|pȃ"aPZ_C(ZYԝ$ ݉t*=<+H#7S,Jk׮=p@RR !ɢ8%%Y(⦦C۷Q744>|Zt^9rѣG8--I ڣGK555G}1ϯ>vؓ'O5{v۱cǞ>}XUUuر GGǏSqEE3338++_~Ⲳ_~%;;ޞ={foo_ZZz *~'?8''㕔Bȓ'O֖<#ppp !DIII}B F~Bݻw'!Ν;Mu 0rM4h![o۷ŋ2W!ac&k^TҝKga? ?&ﯤ$Y,(VVV X,UUU`buuuPQQР &UD(6ﯫK;===Qp@KKPݛ*FFFTq766&&&tuųO-O8k``OVK*zV "70ArTQ JƀiE]wEی9ciu]#A19(`ļ$"A2YD~~TW}3}twpՇNmrz&]u#FpttdWj;vW]w=֭[(++/,, Zn]n]B~MUU޽{'$$] PQQ:rʒ͍|=U>BZZڤI%$$ϝ;I IKK :}t]w,wa(?F3-W|||QQQzz#M7oܲe˲2~9ŋӓlB544||| o߾=`M*/0x-[T-Zx{{gdd>}$""c]w VJ]Νʹz۶m۶m[]w#--'!WzΜ9CgeeuYHKKӶU6]'++5G5cǎYꍛ-x´uAj={رڳgʛdff.Zr M}W![ZZW,Ҋc[^;i)ƍ7~&;;{۷jٲYAPU/^PSS+(( | hiiz.rt}!ڋʡ2{l.rf]~H/kΌ?}za70aĉ뺛0%%%)))}}I&eff*r=ΝhԩSVVVRtx"}#ᡨ(!!aeeuCOyy9 oRRҨQ\͛71)))'NXԜUsʈMgZK}!UgaYׯ_gZ K-seo:H[J\]]=::ʊi%,))))++uQ'Ǩ}eexCD~bʰp0CsQkly1Nǝ4iX#%%Lka5?|1F5i谹´wD1d#]J\UU5::ښi%,)---//ܹ3ZXj'G8&.Ǵf`s=,,uaێ?)S0:RRR=ӿo^==+Qdz:lG d:-qaZܽ{SNL+aNii)!ޞD<~‽Cś3-I\ K=9= oߔi9 Ý:u*zCݻwtgLka).>]1 wz>lG4xq)綝+dڋao"tKii)as=4ŹE{{ev[ӫULi_pŋK,ٴi!6ms6mԩSYY͛ݻgmm]ZZ*`߿ںd˖-<*..޲eÇnZ~葥eaa֭[?~liiYPP |??Ov1//ٳg;v 177?nnnۡCO>u!++k۶mq۶m ۷ JLL433 z⅙هo^~򥩩izz_zejjׯ_k.--MNJJj׮]jjjHHț7oLLLRRRBBBMLL޽{wq#3i'^]MԱwL>}ƌtYLLRZZ BHHHB HJJB ЫA|,!$77=avv6BȧO(**B>~@YY@UU@MM{TZZZw!$''h֬!͛7tuu !'O^tu !/_вeKBHbb"VZBջ !qqq!ϟ?жm[BȳgB>} ]v'O033#{3-!i'Oб#9ZbZ K#}C:^jUDiuFFI z1*2xCظ#(WCCfZ K#7Om У͛`T$'3ёAI9<ި r""z bP11Aȑ7ӛi5  =탎""r%RXgzu5AVC~СabjУ X /3Q0M? oL=leZ V GNi9 Q=.ĠAF߾gZ !^eeNF }Fz8SS$$L bA .T`<ЬZ6mDif ׯCRiA,CP'Ҵ$.GR^Rr+W#ۙN2jr|r..Z{5蓜\)n ǖ&w37]\=mg; 6ÑU> ͚ ,\WES/wo_?5yE;f?S ف\cqM7u\]TwɃ Os~zQQZ􍔒jP)zj-~pw/z$Xܢ}}B"^J)J:APֻM͕Q\. ek C>׎yz[ǯx#CQ秝ve|5:CU@N̙(c.I/+zݙbV<Ӳau585M %E,-,}޴ ^,x^iaF2jrs$J E4icǰe f† h۶.JMMݵkW]RX[[wڵ o>܂2bӕZxݙ&\IKK2sɕ+`5VϩLuxجnaږ͞|~e 2ool]FzhLAϞClތcQ }v&K]]Mk@lCynY.6 R+WqfʪU_ԙ GJ J {Ǔ綽rWVmL~FZYF^ϯ}UFHa :|ttʈKذ>>SSV8D=+1F^AA0#cX^ e~fVYj_aa^_qK Գo!~A>5,WW{%K )m۠iӐ^&aaߨSV\vfܑk.ƝZ =_#- Bi)6oF7qqm xyիRw&2MZ6Z#-{7^ŸAAhnn8zT6FDp00z4^dH1KZϻo5wf?:0H`COb~axHKE yyQ^ܹFI v쀑F‹fYHYw}~۲{qg;6| CC[lS/ gg`Pxر_*"4m`H$&2}mERN:OwzKFMS,? zj2Nœ'¼yEQEAE'N@ߥعmb$$0'i 3䛯]ۗ]`Cwbg5k[޽!+$!픖b.cp7o})uY=NŀiE z~Ν1cNƧOxvJK{wAԯ: $ yf4`]Æ˅Y΋?Ϯ{a>Қi!6غUPL 8Pτ,Lh4e_-t!&t);  KUĖ-_lK15eT KC =?͆ ,] 33[~ :3B** ;o0а2ȃ IDAT!ᴑqy.CNNSSٳZvwh۷$ͦL߽we&U"r#//ݲ 55vX$l9<@t4 Ȳ^̜M͛[\{ag7y@s/uGS?Xq-4\f U}? _sa||Ν:tsV==j _6ݻ3+TTf7k.Yӳc%{ۦr͈'G68hA#aӦrسg`i7Es瀩?waX7@{DVf̘^Wܯ,ŽzD~DKK *eRK>5oVN 4ic\\JQ b^R̩k_v#"^^~51qoV&*хM?ڲ̊#= Ͽp> b*"+QUiGtp8ZiݻhčU\]BCo&&z7NxR?"+|{wQCWWt߾-_>rf<TPP{-5:tWR &k(O~~Ç ӧo 9bȟ!wdzD/AAr8]*х-Zhܺ埕۽B/Cá;vȿ,Y2mWPխ7+-^@$].&UCcȵM> fzAq#6ie_m)4( òGKꪇf]wj^îDV#IǼ)ǸbS_ 7xtȍ݅% :z0-ꗇ =,,Up#凧ɗU8΢?1J4`CQP =ûwHMEr2JJPZR55()AEh&&pvfZ4wRN/>8x ԻEVBf^jzbcp!]C^0w.zs 2v3Fp8pu_^ouK U"t]ZM׮ɓo4e5˗+5kGKQFJw]}.dAxyV׿*F'!Θ=6@Caݺ/'1]b<[7ܼ Ϩ ??L,jB+SV0ۇcLR03Cǎֆ&Ф \pp,|#1aaxF `gipTVUUkGK)IC Q:X쿒K%YY'N'F8[UF@?CNy388 ݻM|_kػ7fذ)VV)w8;Ʉ^RTR(y?v=zTVE5z',  w]d0\߿ E \w) 0cTU퍤$` y\ ';Цgjee$8R&n013BFLVֳkҲտNF'3]D[_8;K,]*WT򓔔`VbFK9x,^GF]w4!r迚sGԽěY`9ٟTX\KejzTTRA?%V¥KވP/!_}S,-1e 22`mq*zfi$v7YMt; ?iuq-%+)6w縶1/6Y(t놃Uԭۗݻѭ`5 ލcZ*1p!:u“'00Ss;f,4&=/k.{p8Gv?o>6Υe%/m0Uk_ }e˰y36l۷(*ݻY_~~ذiiHKÆ ǒ%B162Ex{kx]`j3 XD1[*bMܢA4Uqek$d: ><7<xqqܗ[[ܻ n(UDF <ed0\E\U2iiU"Frq&!-M4$Hw}cmb!U`fͰ'orѩfzd׿چOnG` IN.VVm؛ 溷7+HITwI|/޵mťEMg_1Dӊ4bvL2L[/9s3޽W>PUP_1dXNG4jsF'']_aa!FFy9,ٗBQd4=z=}\!HT”)(-X}b}tnUO5tlߨD' ];<{a@BZ`o0Hk4$Sc픑ޭ}NsǹQtF=Wk?ܾ޽QXKő#laiph9 R+x3-TKZǏC?A@01}kD7C{*`v0M'#B8]n]gZN#RGC㿕-*2oޏORox k~iy鄀?"z 욫woZńg n_mY$#q۶ػ}A\L|_?i9uJJ8y ̩aa~6zp8O,{!r Q =ZZ02qq8CC|?GvRRZ|蘖6G988.X))) ,x䉽w.\ۿ}v…O>KNN^hѳg޼yhѢ[[ׯ_/^綶^ZdIll˗/,YgccŋKwܙW"22&&&>>>ׯ__~}AAk֯__XXضmp__ߢm^z׷M6aaa6l())iӦ͕+W6lPZZjddt7]tiƍ[xM![p¦M?~ܹs7o3008{-[ Μ9e.۪UӧOoݺI&FMv;6i̕a~~~-[BH^^999BHNNBHvv6MB())B233B>|@MM@]]@SS@[[[͚5#$''h޼9!$)) @KKɫWB^xUVDx[&hӦ !$&&1!ٳgLLL!/SSSBǏoߞC愐ر#!޽{,--g&ܾ}@Ν !7o`ccCq;;;BHdd$ɓ'oܸ1L8uV'O&l޼ԩS !7n0}tB/3fB֯_Oϼn:g&]ܹs !k֬0o5kB͛hj^9s!fC9s&___BȌ3lذ2m4t42e -[B&MϏ2qD $;@PP!d̘1 !GB9r$PBڵBQ={B B<==۷/44t_lZϞ=gϞVVVOG]]ݽmiip444l 111 777 I;r8---77;UĴ+ڮݡC111777ln^\\\GGյ}͚5ś7oN\.y=zh꺺V۵kruuu{A-ZMLL\m7iDOOťmllܤI}}=zs\}}...&LYIHKKtEOOohBZZ]v ߖ6557o.--mffF222ffff͚ȴo^ё222:tedd͝eee+mw;:99iiiѶmkjjZXX899U544,,,iRVWWmyyy+++cE[UUU^^;u$`w!$$dǎ]tL4lٲ}E8)))Ǐ`ZKC'22 bZ p.]УGS^./4&N8qD!#ǿbi$&&.^8>>ƦQ/`P+]hтi-, zDHGGGGGEBsOOO?w\^" VZj,K"""֮]׮];GAAlB< W^L+aNbb… \OM155es= ;v_6"Jyٳg{ʹ$HHHŲTMDDz2M6gs=, ӧJXp¸8[[[4tdee̺v˴ᄄ_6"Js=bƍ7n\];`III++#G]O>ٳ|Ϟ=7i8@@/VST_IKK4is~+m藕Yƍ۷owmEbbbM65777o=˷ϟߺukee~ѮZۿy {1cm zƌ%>>^CC')) ] jjjׯOMMMMMUSSKHHL7:5W"иВjz|m-cbb322N>Z/g̨9;//SN~ꕀ_TT~A'''R7{7DOQxzzZJkȐ!%|,Y2t/,##ϟ7^% -QF͟?kx///zyYPPPTTwmUyXxq_.--ӧ-Ahw o {hhhx*WbbbŒDz>0niiY\\\zoU?F5JZRMZZZqqqմ\+DDDptt-w$}РAQQQ^Ahw |oza9?ᓔUR^^ÇՋ^A_hoGpp0=jzjW2dA>̳gڶmKMo5WU)fjz>|8=Go$G^vm6+߸q㻶-^|I5?*m۶=Ν;uuMNN.,,s?ٴi=7U} (&ǏO>]롇5<6mZUwLyykUxEoUW+&Г4j(mmm.q͚~A5#555$$ٳ۬HrQF!zop8rrr۷3g+zvXXͤI233iW<~ѽ=<<<$$ŋ;\,/;~7sss'>>~ٱLki[XXtڵyLkaζmw:;\,yɓӒX#%%ellfhhȴNxxʕ+srrڷoϴᨪ:::lB<44_~:t`Z pg͚zjEnݚ5kƴ_6"Jy'Ǵ$HIIki- W._4dԜ\z~!wկ_?,`~Z: VVVl!uVmmm6as=,/_8qÃiI,‘633swwoժZ:aaa˖-a6XջtzXD 6 !g333'..nƌϟ?wttdZKCiӦ:u֭ZXyfmmmC^xqذa!E3tQ5bE9@II EQFP\\LQѣQ5fEyyy((((^o'??ˣ(j„ rss)8q"&M ;;ɓ'EQSLEQԩS|iӦ̤(jƌ222(9s&t(zm4fϞ 559sHII(jܹ޽{GQԼy} HNN(j…(Zhׯ_Sxb^(jɒ%^|IQҥKx񂢨e˖HLL(j(Zr%xVZ.]T{HкukMM-[ڵ ˗)ڽ{7K.Qg/^(pEQ{pytΝ;GQԁ={8}4EQp)> ɓE+Ϝ8qG8~8EQǎp1?ѣEBH^^999B=!$;;@ӦM !YYY!TTT!Gjjjtꄐ4l))) !o߾ЬY3BHrr2͛R򻜝ɫWB`[jEILL```@WBHll,6m3066&<{ !^AԔc۷'<|9!:vHwKKKBݻwX[[Bn߾ s΄!?!Q޿OqB_jsnݺɓ !7o0uTBƍL> `ƌ&[ٳ !k׮0w\BȚ5k^ !Vh"BȊ+,YB`  !iʕ_Bϟ`͚57c$B}R&O>>>KO6l 'M6B6 }R#'{B=  $;(z0AOdJ2BBB!@$44BVvE(vCAIH:pi1-..p\'...`KHHx<.K&-%%4i@ZZIHHAoxdԌݻx֭[pqqxFFFzx<^6mmۖ544hW^ZZZzC}i޼ }koG-[455п:ѿCCC8pP޶m[4h &?^$?433'СCǎ 2cǎC m,-- 6SN=|pz#FZ[[9r=m=ѱSNƌܹsgҥ m;[n666ƍBǏwuu0a'FOO~%Ho;9!".Ņi),,?nL?e\^msQƊ1-15Gs 1/Lk}D0@D:XX~}׷=>Kg1+.C߃BYRXX~2->&1z =͚U+|M_W]Q NdZ x~]t 3+@v8:XXgo_Z\D389AYϞ!!i),,5ha%eBTY4C (i),,5fEo{Fnf0f ލb/Ҷ : ?pMq goDq5 =VVhlƇ gFuEQݧ}E\ `zEz;䎑N;( |D68i,,ѫM^'ߤy9vmj0p~ٽqn8bzD.>EEGj*.\+jXXj@QI͜RϺ`[}ʱvLɫEDy#)3`BԌ}׷J5mQ lWL~a 9Dy /ѣߟi5,,Ւ[m7c^FS.0-nQ99^ 汷X:[1/æ|0f __TMbj8?1>"0V@|#G^^LKaaƞ( E텃243OЦ ޿_a=11^߂ӷo_z$Wv*,.vҥCz)L '{7ݱt)P˗geeˡ4p(,.tj}⤞sGLf#9?ZKvܼU޷_FԹ[}ԬM:v}q7k.{sQ^ffze9*U܍`mmwjʛy/KX"|BT߄TG~éSoGڅ<۷5 -Z`|+Я tiikrmr@8znPWovE%rtmwmvjȕ+XM`ߖ_~rw‚"c_T۔[g1S{oJp%ǹz!eRy$yG )G(,)豤Cr{~"J'rsuz⠵]GMZh?^'TUyOj\taUJ*VؤRܹ??:" ip:fFnoetn0y-.R5e=e%>Kwyy<:ϳNB֙4T_`fe'&7ge琩'/G=Uj>,;):an (]ŋ>&&{EE Í-*o uE^y0a.WUP[8}*DH:zw)y<~('#]8%h6&'y0rGT=u}]]m<e!QئcnT,֯`ME=2X2vXd85*-|WUT*\ //xy!=.e\t!5q$N<<4gehju}QX1#bs{.|zt hJ5JAhSRs! >dee Tl"e].uLjQV?=^i(j?"yYyBuY_=-bٮmK \-y5. =FIUQWС:^9Sx7QKx'O٢p׻-ާd* vjמUnDWhFI5TkxQYrH-y響J ,-63 ?*˫V.Vb BHBJLu 5O)j-+o$z (Uj|NUe۵}OkC^pU* Z@ObU|22?O?֝n^Ȟû/w3Di9c}r+7RJ~}[Q 07x$Ax!AFv:٨݇U戍>S𤲛b{Ufǚ=UV]z bRtD,}='fUqu&+MJO'eaf&i4':xo7'0vH$40@νzӑ@@͑\uXCK+ C.Dz>pP=Tp@P=Tp@P=,,d:[^ba l?'>s࿀fp<D x>TWaIENDB`ModemManager-1.23.4-dev/data/ModemManager.pc.in000066400000000000000000000003231456466623000211210ustar00rootroot00000000000000prefix=@prefix@ exec_prefix=@exec_prefix@ libdir=@libdir@ includedir=@includedir@ Name: ModemManager Description: Common headers provided by ModemManager Version: @VERSION@ Cflags: -I${includedir}/ModemManager ModemManager-1.23.4-dev/data/ModemManager.png000066400000000000000000000014611456466623000207020ustar00rootroot00000000000000PNG  IHDRĴl;tEXtSoftwareAdobe ImageReadyqe<IDATxڬKSaǟF1/G5#R6 .ru Dy!Ew0XJ7AnbThg%xf^{ 1<:փ5kUOREO]kt A5X׻YFPH xn;4yg"8 Zjby/$rq%LMd`^ar:rBKlrs7A7ñqT?`m]o ҥT \?UL+NLZ4nD,w ا xΏ gaޛ~2M Ԣ ͊mƴ7@<>0H(8SxbqLDMN6wGf"]GGz@ 8'j]xJx^^v_m95&r߈sMEhzNoh(55 b^ު~p9 3Og )Wӕ 1xF#L&2,W;&r5IKp8w-)(Wrv/l20, )EMd0ۋ6|/ ~) =c{x,`8D##fm{VBaJVkHlv{Y[$Imnjd2 %_Z9F?DwEh}}=QF?Ld+FIENDB`ModemManager-1.23.4-dev/data/ModemManager.service.in000066400000000000000000000010151456466623000221560ustar00rootroot00000000000000[Unit] Description=Modem Manager After=@MM_POLKIT_SERVICE@ Requires=@MM_POLKIT_SERVICE@ ConditionVirtualization=!container [Service] Type=dbus BusName=org.freedesktop.ModemManager1 ExecStart=@sbindir@/ModemManager StandardError=null Restart=on-abort CapabilityBoundingSet=CAP_SYS_ADMIN CAP_NET_ADMIN ProtectSystem=true ProtectHome=true PrivateTmp=true RestrictAddressFamilies=AF_NETLINK AF_UNIX AF_QIPCRTR NoNewPrivileges=true User=root [Install] WantedBy=multi-user.target Alias=dbus-org.freedesktop.ModemManager1.service ModemManager-1.23.4-dev/data/dispatcher-connection/000077500000000000000000000000001456466623000221215ustar00rootroot00000000000000ModemManager-1.23.4-dev/data/dispatcher-connection/99-log-event000066400000000000000000000007621456466623000242100ustar00rootroot00000000000000#!/bin/sh # SPDX-License-Identifier: CC0-1.0 # 2022 Aleksander Morgado # # Example connection info dispatcher script # # require program name and at least 4 arguments [ $# -lt 4 ] && exit 1 MODEM_PATH="$1" BEARER_PATH="$2" INTERFACE="$3" STATE="$4" MODEM_ID=$(basename "${MODEM_PATH}") BEARER_ID=$(basename "${BEARER_PATH}") # report in syslog the event logger -t "connection-dispatch" "modem${MODEM_ID}: bearer${BEARER_ID}: interface ${INTERFACE} ${STATE}" exit $? ModemManager-1.23.4-dev/data/dispatcher-connection/meson.build000066400000000000000000000014061456466623000242640ustar00rootroot00000000000000# SPDX-License-Identifier: GPL-2.0-or-later # Copyright (C) 2021 Aleksander Morgado # Shipped example connection info dispatcher mm_connectiondiravailable = mm_pkgdatadir / 'connection.available.d' # Directory for user-enabled scripts mm_connectiondiruser = mm_pkgsysconfdir / 'connection.d' # Directory for package-enabled tools mm_connectiondirpackage = mm_pkglibdir / 'connection.d' examples = files( '99-log-event', ) install_data( examples, install_mode: 'rwx------', install_dir: mm_connectiondiravailable, ) mkdir_cmd = 'mkdir -p ${DESTDIR}@0@' meson.add_install_script('sh', '-c', mkdir_cmd.format(mm_prefix / mm_connectiondiruser)) meson.add_install_script('sh', '-c', mkdir_cmd.format(mm_prefix / mm_connectiondirpackage)) ModemManager-1.23.4-dev/data/dispatcher-fcc-unlock/000077500000000000000000000000001456466623000220065ustar00rootroot00000000000000ModemManager-1.23.4-dev/data/dispatcher-fcc-unlock/105b000066400000000000000000000051321456466623000224010ustar00rootroot00000000000000#!/bin/sh # SPDX-License-Identifier: CC0-1.0 # 2021 Aleksander Morgado # 2022 Thilo-Alexander Ginkel # 2022 Bjørn Mork # # Foxconn SDX55 (Lenovo T99W175) FCC unlock operation # # The script will first try to unlock the WWAN modem using the new (hash-based) # v2 method and fall back to the old (v1) method if that fails # # require program name and at least 2 arguments [ $# -lt 2 ] && exit 1 # first argument is DBus path, not needed here shift # second and next arguments are control port names for PORT in "$@"; do # match port type in Linux 5.14 and newer grep -q MBIM "/sys/class/wwan/$PORT/type" 2>/dev/null && { MBIM_PORT=$PORT break } # match port name in Linux 5.13 echo "$PORT" | grep -q MBIM && { MBIM_PORT=$PORT break } done # fail if no MBIM port exposed [ -n "$MBIM_PORT" ] || exit 2 log_v2_failure() { echo "$1. Falling back to old unlock method" >&2 } FIRMWARE_VERSION=$(qmicli --device-open-proxy --device="/dev/$MBIM_PORT" \ --dms-foxconn-get-firmware-version=firmware-mcfg \ | grep "Version:" \ | grep -o "'.*'" \ | sed "s/'//g" \ | sed -e 's/\.[^.]*\.[^.]*$//') if [ -n "${FIRMWARE_VERSION}" ]; then FIRMWARE_APPS_VERSION=$(qmicli --device-open-proxy --device="/dev/$MBIM_PORT" \ --dms-foxconn-get-firmware-version=apps \ | grep "Version:" \ | grep -o "'.*'" \ | sed "s/'//g") if [ -n "${FIRMWARE_APPS_VERSION}" ]; then IMEI=$(qmicli --device-open-proxy --device="/dev/$MBIM_PORT" --dms-get-ids \ | grep "IMEI:" \ | grep -o "'.*'" \ | sed "s/'//g") if [ -n "${IMEI}" ]; then SALT="salt" # use a static salt for now MAGIC="foxc" HASH="${SALT}$(printf "%s%s%s%s%s" "${FIRMWARE_VERSION}" \ "${FIRMWARE_APPS_VERSION}" "${IMEI}" "${SALT}" "${MAGIC}" \ | md5sum \ | head -c 32)" else log_v2_failure "Could not determine SDX55 IMEI" fi else log_v2_failure "Could not determine SDX55 firmware apps version" fi else log_v2_failure "Could not determine SDX55 firmware version" fi UNLOCK_RESULT=1 if [ -n "${HASH}" ]; then qmicli --device-open-proxy --device="/dev/$MBIM_PORT" \ --dms-foxconn-set-fcc-authentication-v2="${HASH},48" UNLOCK_RESULT=$? if [ $UNLOCK_RESULT -ne 0 ]; then log_v2_failure "SDX55 FCC unlock v2 failed" fi fi if [ $UNLOCK_RESULT -ne 0 ]; then qmicli --device-open-proxy --device="/dev/$MBIM_PORT" \ --dms-foxconn-set-fcc-authentication=0 UNLOCK_RESULT=$? if [ $UNLOCK_RESULT -ne 0 ]; then echo "SDX55 FCC unlock v1 failed" >&2 fi fi exit $UNLOCK_RESULT ModemManager-1.23.4-dev/data/dispatcher-fcc-unlock/1199000066400000000000000000000014031456466623000223320ustar00rootroot00000000000000#!/bin/sh # SPDX-License-Identifier: CC0-1.0 # 2021 Aleksander Morgado # # Sierra Wireless FCC unlock mechanism # HP 820 G1 (EM7355), 03f0:4e1d # Dell DW5570 (MC8805), 413c:81a3 # Dell DW5808 (MC7355), 413c:81a8 # Lenovo-shipped EM7455, 1199:9079 # # require program name and at least 2 arguments [ $# -lt 2 ] && exit 1 # first argument is DBus path, not needed here shift # second and next arguments are control port names for PORT in "$@"; do # match port name echo "$PORT" | grep -q cdc-wdm && { CDC_WDM_PORT=$PORT break } done # fail if no cdc-wdm port exposed [ -n "$CDC_WDM_PORT" ] || exit 2 # run qmicli operation qmicli --device-open-proxy --device="/dev/$CDC_WDM_PORT" --dms-set-fcc-authentication exit $? ModemManager-1.23.4-dev/data/dispatcher-fcc-unlock/14c3000077500000000000000000000045131456466623000224110ustar00rootroot00000000000000#!/bin/bash # SPDX-License-Identifier: CC0-1.0 # 2023 Thilo-Alexander Ginkel # # Lenovo-shipped Fibocom FM350-GL (14c3:4d75) FCC unlock if [[ "$FCC_UNLOCK_DEBUG_LOG" == '1' ]]; then exec 3>&1 4>&2 trap 'exec 2>&4 1>&3' 0 1 2 3 exec 1>>/var/log/mm-fm350-fcc.log 2>&1 fi # require program name and at least 2 arguments [ $# -lt 2 ] && exit 1 # first argument is DBus path, not needed here shift # second and next arguments are control port names for PORT in "$@"; do # match port type in Linux 5.14 and newer grep -q AT "/sys/class/wwan/$PORT/type" 2>/dev/null && { AT_PORT=$PORT break } # match port name in Linux 5.13 echo "$PORT" | grep -q AT && { AT_PORT=$PORT break } done # fail if no AT port exposed [ -n "$AT_PORT" ] || exit 2 DEVICE=/dev/${AT_PORT} at_command() { exec 99<>"$DEVICE" echo -e "$1\r" >&99 read answer <&99 read answer <&99 echo "$answer" exec 99>&- } log() { echo "$1" } error() { echo "$1" >&2 } VENDOR_ID_HASH="3df8c719" for i in {1..9}; do log "Requesting challenge from WWAN modem (attempt #${i})" RAW_CHALLENGE=$(at_command "at+gtfcclockgen") CHALLENGE=$(echo "$RAW_CHALLENGE" | grep -o '0x[0-9a-fA-F]\+' | awk '{print $1}') if [ -n "$CHALLENGE" ]; then log "Got challenge from modem: $CHALLENGE" HEX_CHALLENGE=$(printf "%08x" "$CHALLENGE") COMBINED_CHALLENGE="${HEX_CHALLENGE}$(printf "%.8s" "${VENDOR_ID_HASH}")" RESPONSE_HASH=$(echo "$COMBINED_CHALLENGE" | xxd -r -p | sha256sum | cut -d ' ' -f 1) TRUNCATED_RESPONSE=$(printf "%.8s" "$RESPONSE_HASH") RESPONSE=$(printf "%d" "0x$TRUNCATED_RESPONSE") log "Sending response to WWAN modem: $RESPONSE" UNLOCK_RESPONSE=$(at_command "at+gtfcclockver=$RESPONSE") if [[ "$UNLOCK_RESPONSE" == "+GTFCCLOCKVER:"* ]]; then UNLOCK_RESULT=$(echo "$UNLOCK_RESPONSE" | grep -o '[0-9]\+') if [[ "$UNLOCK_RESULT" == "1" ]]; then log "FCC unlock succeeded" exit 0 else error "FCC unlock failed. Got result: $UNLOCK_RESULT" fi else error "Unlock failed. Got response: $UNLOCK_RESPONSE" fi else error "Failed to obtain FCC challenge. Got: ${RAW_CHALLENGE}" fi sleep 0.5 done exit 2 ModemManager-1.23.4-dev/data/dispatcher-fcc-unlock/1eac000066400000000000000000000013751456466623000225500ustar00rootroot00000000000000#!/bin/sh # SPDX-License-Identifier: CC0-1.0 # 2021 Aleksander Morgado # # Quectel EM120 FCC unlock operation # # require program name and at least 2 arguments [ $# -lt 2 ] && exit 1 # first argument is DBus path, not needed here shift # second and next arguments are control port names for PORT in "$@"; do # match port type in Linux 5.14 and newer grep -q MBIM "/sys/class/wwan/$PORT/type" 2>/dev/null && { MBIM_PORT=$PORT break } # match port name in Linux 5.13 echo "$PORT" | grep -q MBIM && { MBIM_PORT=$PORT break } done # fail if no MBIM port exposed [ -n "$MBIM_PORT" ] || exit 2 # run mbimcli operation mbimcli --device-open-proxy --device="/dev/$MBIM_PORT" --quectel-set-radio-state=on exit $? ModemManager-1.23.4-dev/data/dispatcher-fcc-unlock/2c7c000066400000000000000000000011471456466623000224720ustar00rootroot00000000000000#!/bin/sh # SPDX-License-Identifier: CC0-1.0 # 2022 Leah Oswald # # Queltec EM05-G FCC unlock mechanism # # require program name and at least 2 arguments [ $# -lt 2 ] && exit 1 # first argument is DBus path, not needed here shift # second and next arguments are control port names for PORT in "$@"; do # match port name echo "$PORT" | grep -q cdc-wdm && { CDC_WDM_PORT=$PORT break } done # fail if no cdc-wdm port exposed [ -n "$CDC_WDM_PORT" ] || exit 2 # run mbimcli operation mbimcli --device-open-proxy --device="/dev/$CDC_WDM_PORT" --quectel-set-radio-state=on exit $? ModemManager-1.23.4-dev/data/dispatcher-fcc-unlock/meson.build000066400000000000000000000022621456466623000241520ustar00rootroot00000000000000# SPDX-License-Identifier: GPL-2.0-or-later # Copyright (C) 2021 Aleksander Morgado # Shipped but disabled FCC unlock tools mm_fccunlockdiravailable = mm_pkgdatadir / 'fcc-unlock.available.d' # Directory for user-enabled tools mm_fccunlockdiruser = mm_pkgsysconfdir / 'fcc-unlock.d' # Directory for package-enabled tools mm_fccunlockdirpackage = mm_pkglibdir / 'fcc-unlock.d' examples = files( '105b', '1199', '14c3', '1eac', '2c7c', ) install_data( examples, install_mode: 'rwx------', install_dir: mm_fccunlockdiravailable, ) vidpids = { '105b:e0ab': '105b', '105b:e0c3': '105b', '03f0:4e1d': '1199', '1199:9079': '1199', '413c:81a3': '1199', '413c:81a8': '1199', '14c3:4d75': '14c3', '1eac:1001': '1eac', '2c7c:030a': '2c7c', } ln_cmd = 'ln -fs @0@ ${DESTDIR}@1@' foreach output, input: vidpids meson.add_install_script('sh', '-c', ln_cmd.format(input, mm_prefix / mm_fccunlockdiravailable / output)) endforeach mkdir_cmd = 'mkdir -p ${DESTDIR}@0@' meson.add_install_script('sh', '-c', mkdir_cmd.format(mm_prefix / mm_fccunlockdiruser)) meson.add_install_script('sh', '-c', mkdir_cmd.format(mm_prefix / mm_fccunlockdirpackage)) ModemManager-1.23.4-dev/data/its/000077500000000000000000000000001456466623000164355ustar00rootroot00000000000000ModemManager-1.23.4-dev/data/its/polkit.its000066400000000000000000000004651456466623000204650ustar00rootroot00000000000000 ModemManager-1.23.4-dev/data/its/polkit.loc000066400000000000000000000003031456466623000204320ustar00rootroot00000000000000 ModemManager-1.23.4-dev/data/meson.build000066400000000000000000000037261456466623000200100ustar00rootroot00000000000000# SPDX-License-Identifier: GPL-2.0-or-later # Copyright (C) 2021 Iñigo Martinez if enable_tests subdir('tests') endif service_conf = { 'sbindir': mm_prefix / mm_sbindir, 'MM_POLKIT_SERVICE': (enable_polkit ? 'polkit.service' : ''), } # DBus Service file if meson_version.version_compare('>=1.0.0') fs.copyfile( 'org.freedesktop.ModemManager1.conf.@0@polkit'.format(enable_polkit ? '' : 'no'), '@BASENAME@', install: true, install_dir: dbus_policy_dir, ) else configure_file( input: 'org.freedesktop.ModemManager1.conf.@0@polkit'.format(enable_polkit ? '' : 'no'), output: '@BASENAME@', copy: true, install_dir: dbus_policy_dir, ) endif # DBus Activation file configure_file( input: 'org.freedesktop.ModemManager1.service.in', output: '@BASENAME@', configuration: service_conf, install_dir: dbus_system_bus_services_dir, ) # systemd unit file if install_systemdunitdir configure_file( input: 'ModemManager.service.in', output: '@BASENAME@', configuration: service_conf, install_dir: systemd_systemdsystemunitdir, ) endif # Polkit if enable_polkit policy = 'org.freedesktop.ModemManager1.policy' # build file with translations, which we will include in dist i18n.merge_file( input: configure_file( input: policy + '.in.in', output: '@BASENAME@', configuration: policy_conf, ), output: '@BASENAME@', po_dir: po_dir, install: true, install_dir: polkit_gobject_policydir, ) endif if enable_gtk_doc # Logos logos_pngs = files( 'ModemManager-logo-square.png', 'ModemManager-logo-wide.png', 'ModemManager-logo-wide-text.png', ) # Diagrams diagrams_pngs = files( 'ModemManager-interface-initialization-sequence.png', 'ModemManager-interface-initialization-sequence-subclassed.png', 'ModemManager-states.png', ) endif # Icon install_data( 'ModemManager.png', install_dir: mm_datadir / 'icons/hicolor/22x22/apps', ) ModemManager-1.23.4-dev/data/mm-glib.pc.in000066400000000000000000000004441456466623000201150ustar00rootroot00000000000000prefix=@prefix@ exec_prefix=@exec_prefix@ libdir=@libdir@ includedir=@includedir@ Name: mm-glib Description: Library to control and monitor the ModemManager Version: @VERSION@ Requires: glib-2.0 gobject-2.0 gio-2.0 ModemManager Cflags: -I${includedir}/libmm-glib Libs: -L${libdir} -lmm-glib ModemManager-1.23.4-dev/data/org.freedesktop.ModemManager1.conf.nopolkit000066400000000000000000000006461456466623000260660ustar00rootroot00000000000000 ModemManager-1.23.4-dev/data/org.freedesktop.ModemManager1.conf.polkit000066400000000000000000000353201456466623000255260ustar00rootroot00000000000000 ModemManager-1.23.4-dev/data/org.freedesktop.ModemManager1.policy.in.in000066400000000000000000000071511456466623000256120ustar00rootroot00000000000000 ModemManager http://www.freedesktop.org/wiki/ModemManager ModemManager Control the Modem Manager daemon System policy prevents controlling the Modem Manager. no auth_admin Unlock and control a mobile broadband device System policy prevents unlocking or controlling the mobile broadband device. no @MM_DEFAULT_USER_POLICY@ Add, modify, and delete mobile broadband contacts System policy prevents adding, modifying, or deleting this device's contacts. no @MM_DEFAULT_USER_POLICY@ Send, save, modify, and delete text messages System policy prevents sending or manipulating this device's text messages. no @MM_DEFAULT_USER_POLICY@ Accept incoming voice calls or start outgoing voice calls. System policy prevents voice calls. no @MM_DEFAULT_USER_POLICY@ Query network time and timezone information System policy prevents querying network time information. no @MM_DEFAULT_USER_POLICY@ Enable and view geographic location and positioning information System policy prevents enabling or viewing geographic location information. no @MM_DEFAULT_USER_POLICY@ Query and utilize network information and services System policy prevents querying or utilizing network information and services. no @MM_DEFAULT_USER_POLICY@ Query and manage firmware on a mobile broadband device System policy prevents querying or managing this device's firmware. no auth_admin ModemManager-1.23.4-dev/data/org.freedesktop.ModemManager1.service.in000066400000000000000000000007231456466623000253440ustar00rootroot00000000000000# This D-Bus service activation file is only for systemd support since # an auto-activated ModemManager would be quite surprising for those people # who have MM installed but turned off. Thus the Exec path available to # D-Bus is /bin/false, but systemd knows the real Exec path due to the MM # systemd .service file. [D-BUS Service] Name=org.freedesktop.ModemManager1 Exec=@sbindir@/ModemManager User=root SystemdService=dbus-org.freedesktop.ModemManager1.service ModemManager-1.23.4-dev/data/tests/000077500000000000000000000000001456466623000170005ustar00rootroot00000000000000ModemManager-1.23.4-dev/data/tests/meson.build000066400000000000000000000007061456466623000211450ustar00rootroot00000000000000# SPDX-License-Identifier: GPL-2.0-or-later # Copyright (C) 2022 Aleksander Morgado test_plugin_dir = '' if not enable_builtin_plugins test_plugin_dir = '--test-plugin-dir="' + build_root + '/src/plugins"' endif test_conf = { 'abs_top_builddir': build_root, 'test_plugin_dir': test_plugin_dir, } configure_file( input: 'org.freedesktop.ModemManager1.service.in', output: '@BASENAME@', configuration: test_conf, )ModemManager-1.23.4-dev/data/tests/org.freedesktop.ModemManager1.service.in000066400000000000000000000003261456466623000265050ustar00rootroot00000000000000# This D-Bus service activation file is only TESTS [D-BUS Service] Name=org.freedesktop.ModemManager1 Exec=@abs_top_builddir@/src/ModemManager --test-session --no-auto-scan --test-enable @test_plugin_dir@ --debug ModemManager-1.23.4-dev/data/valgrind.suppressions000066400000000000000000000215101456466623000221420ustar00rootroot00000000000000# # Originally imported from the NetworkManager project # # IMPORTANT: these suppressions strongly depend on the used library version. # They probably don't work out-of-the-box on anything but Fedora, where they # are mainly tested. # # Make sure to install debug information, otherwise the suppression trace might # not match. On Fedora, try 'dnf debuginfo-install glib2'. { NSS_NoDB_Init Memcheck:Leak ... fun:NSS_NoDB_Init ... } { g_type_init_with_debug_flags Memcheck:Leak ... fun:g_type_init_with_debug_flags ... } { g_type_register_static Memcheck:Leak ... fun:g_type_register_static ... } { g_param_spec_boxed Memcheck:Leak ... fun:g_param_spec_boxed ... } { g_type_add_interface_static Memcheck:Leak ... fun:g_type_add_interface_static ... } { g_signal_type_cclosure_new Memcheck:Leak ... fun:g_malloc0 fun:g_closure_new_simple fun:g_signal_type_cclosure_new fun:g_signal_new ... } { dbus_g_value_types_init Memcheck:Leak fun:realloc fun:g_realloc fun:g_type_set_qdata fun:_dbus_g_value_types_init fun:dbus_g_bus_get ... } { type_iface_vtable_base_init_Wm Memcheck:Leak fun:malloc fun:g_malloc fun:g_memdup fun:type_iface_vtable_base_init_Wm fun:g_type_class_ref ... } { g_type_create_instance Memcheck:Leak fun:malloc fun:g_malloc fun:g_slice_alloc fun:g_slice_alloc0 fun:g_type_create_instance fun:g_object_constructor ... } { g_signal_new_class_handler Memcheck:Leak ... fun:g_closure_new_simple fun:g_cclosure_new fun:g_signal_new_class_handler ... } { _dl_init_g_type_register_fundamental Memcheck:Leak ... fun:g_type_register_fundamental ... fun:_dl_init obj:/*/ld-*.so } { _dl_init_g_malloc0 Memcheck:Leak fun:calloc fun:g_malloc0 ... fun:_dl_init obj:/*/ld-*.so } { all_gobject_init_ctor Memcheck:Leak ... fun:gobject_init_ctor ... } # The following suppressions were needed on fc20.armv7hl { _fun_malloc Memcheck:Leak match-leak-kinds: possible fun:malloc } { _fun_realloc Memcheck:Leak match-leak-kinds: possible fun:realloc } { _fun_calloc Memcheck:Leak match-leak-kinds: possible fun:calloc } { _glib_sigaction Memcheck:Param rt_sigaction(act->sa_flags) fun:__libc_sigaction fun:unref_unix_signal_handler_unlocked fun:g_child_watch_finalize fun:g_source_unref_internal fun:g_main_context_dispatch ... fun:g_main_loop_run ... } { # FIXME: dunny why this is needed. Clean up later. _dispatcher_test Memcheck:Leak match-leak-kinds: definite fun:malloc fun:g_malloc fun:g_slice_alloc fun:g_variant_new_from_bytes fun:g_variant_new_from_trusted fun:parse_dhcp fun:get_dispatcher_file ... fun:g_test_run_suite_internal fun:g_test_run_suite_internal fun:g_test_run_suite } { _gdbus_1 Memcheck:Leak match-leak-kinds: definite fun:malloc fun:g_malloc fun:g_slice_alloc fun:g_slice_alloc0 fun:get_dispatch fun:g_main_context_dispatch ... fun:g_main_loop_run fun:gdbus_shared_thread_func fun:g_thread_proxy fun:start_thread fun:clone } { _gdbus_2 Memcheck:Leak match-leak-kinds: definite fun:malloc fun:g_malloc fun:g_slice_alloc fun:g_slice_alloc0 fun:g_main_context_push_thread_default fun:gdbus_shared_thread_func fun:g_thread_proxy fun:start_thread fun:clone } { _gdbus_3 Memcheck:Leak match-leak-kinds: definite fun:calloc fun:g_malloc0 fun:_g_socket_read_with_control_messages fun:_g_dbus_worker_do_read_unlocked fun:_g_dbus_worker_do_read_cb fun:g_simple_async_result_complete fun:complete_in_idle_cb ... fun:g_main_context_dispatch ... fun:g_main_loop_run fun:gdbus_shared_thread_func fun:g_thread_proxy } { _gdbus_4 Memcheck:Leak match-leak-kinds: definite fun:calloc fun:g_malloc0 ... fun:g_slice_alloc fun:g_slice_alloc0 fun:g_main_context_push_thread_default fun:gdbus_shared_thread_func fun:g_thread_proxy fun:start_thread fun:clone } { _gdbus_5 Memcheck:Leak match-leak-kinds: definite ... fun:g_dbus_message_new_from_blob ... } { _gdbus_9 Memcheck:Leak match-leak-kinds: definite fun:malloc fun:g_malloc fun:g_slice_alloc fun:g_slice_alloc0 fun:get_dispatch fun:g_main_current_source fun:g_task_return fun:g_task_thread_pool_thread fun:g_thread_pool_thread_proxy fun:g_thread_proxy fun:start_thread fun:clone } { _gdbus_10 Memcheck:Leak match-leak-kinds: definite fun:malloc fun:g_malloc fun:g_slice_alloc fun:g_slice_alloc0 fun:g_system_thread_new fun:g_thread_new_internal ... fun:g_thread_pool_push ... fun:g_task_run_in_thread fun:g_async_initable_real_init_async fun:g_bus_get ... } { _gdbus_11 Memcheck:Leak match-leak-kinds: definite fun:calloc fun:g_malloc0 ... fun:g_slice_alloc fun:g_slice_alloc0 fun:get_dispatch fun:g_main_current_source fun:g_task_return fun:g_task_thread_pool_thread fun:g_thread_pool_thread_proxy fun:g_thread_proxy fun:start_thread } { _gdbus_12 Memcheck:Leak match-leak-kinds: definite fun:malloc fun:g_malloc fun:g_slice_alloc fun:g_error_new_valist fun:g_error_new fun:g_dbus_error_new_for_dbus_error fun:g_dbus_error_set_dbus_error fun:g_dbus_message_to_gerror fun:decode_method_reply fun:g_dbus_connection_call_sync_internal fun:g_dbus_proxy_call_sync_internal fun:g_dbus_proxy_call_sync } { _gdbus_15 Memcheck:Leak match-leak-kinds: definite fun:malloc fun:g_malloc fun:g_slice_alloc fun:g_hash_table_new_full fun:demarshal_map fun:_dbus_gvalue_demarshal fun:dbus_g_proxy_end_call_internal fun:dbus_g_proxy_end_call fun:get_permissions_reply fun:complete_pending_call_and_unlock fun:dbus_connection_dispatch fun:message_queue_dispatch } { _gdbus_16 Memcheck:Leak match-leak-kinds: definite fun:calloc fun:g_malloc0 fun:_g_dbus_worker_send_message fun:g_dbus_connection_send_message_unlocked fun:unsubscribe_id_internal fun:g_dbus_connection_signal_unsubscribe fun:g_dbus_proxy_finalize ... } { _gdbus_17 Memcheck:Leak match-leak-kinds: definite fun:calloc fun:g_malloc0 fun:thread_memory_from_self fun:g_slice_alloc fun:g_slice_alloc0 fun:g_main_context_push_thread_default fun:gdbus_shared_thread_func fun:g_thread_proxy fun:start_thread fun:clone } { _gdbus_18 Memcheck:Leak match-leak-kinds: definite fun:malloc fun:g_malloc fun:g_slice_alloc fun:g_slice_alloc0 fun:get_dispatch fun:g_main_dispatch fun:g_main_context_dispatch ... fun:g_main_loop_run fun:gdbus_shared_thread_func fun:g_thread_proxy fun:start_thread fun:clone } { _gdbus_f21_1 Memcheck:Leak match-leak-kinds: definite ... fun:_g_dbus_worker_send_message fun:g_dbus_connection_send_message_unlocked ... fun:g_dbus_proxy_finalize ... } { _gdbus_f23_1 Memcheck:Leak match-leak-kinds: definite fun:malloc fun:g_malloc fun:g_slice_alloc fun:g_slice_alloc0 fun:g_type_create_instance fun:g_object_new_internal fun:g_object_new* fun:g_object_new fun:g_task_new fun:_g_socket_read_with_control_messages fun:_g_dbus_worker_do_read_unlocked fun:_g_dbus_worker_do_read_cb fun:g_task_return_now ... fun:g_main_dispatch fun:g_main_context_dispatch fun:g_main_context_iterate.isra.* fun:g_main_loop_run fun:gdbus_shared_thread_func fun:g_thread_proxy fun:start_thread fun:clone } { _gdbus_f25_1 Memcheck:Leak match-leak-kinds: definite fun:malloc fun:g_malloc fun:g_slice_alloc fun:g_slice_alloc0 fun:g_system_thread_new fun:g_thread_new_internal fun:g_thread_new fun:g_get_worker_context fun:g_task_thread_pool_init fun:g_task_get_type fun:ensure_required_types ... fun:g_bus_get_sync } { _gdbus_f25_2 Memcheck:Leak match-leak-kinds: definite ... fun:g_dbus_proxy_new_sync fun:initable_init fun:async_init_thread fun:g_task_thread_pool_thread fun:g_thread_pool_thread_proxy fun:g_thread_proxy fun:start_thread fun:clone } { _gdbusobjectmanager_f25_1 Memcheck:Leak match-leak-kinds: definite fun:malloc fun:g_malloc fun:g_slice_alloc fun:g_slice_alloc0 fun:g_system_thread_new fun:g_thread_new_internal fun:g_thread_pool_start_thread.part.1 fun:g_thread_pool_start_thread fun:g_thread_pool_push fun:g_task_run_in_thread fun:g_async_initable_real_init_async fun:g_async_initable_new_valist_async fun:g_async_initable_new_async fun:g_dbus_object_manager_client_new_for_bus } { _btrfs_io_clone Memcheck:Param ioctl(generic) fun:ioctl fun:btrfs_reflink_with_progress ... } ModemManager-1.23.4-dev/docs/000077500000000000000000000000001456466623000156555ustar00rootroot00000000000000ModemManager-1.23.4-dev/docs/man/000077500000000000000000000000001456466623000164305ustar00rootroot00000000000000ModemManager-1.23.4-dev/docs/man/ModemManager.8000066400000000000000000000051211456466623000210540ustar00rootroot00000000000000.\" ModemManager(8) manual page .\" .\" Copyright (C) 2011 Aleksander Morgado .\" .TH MODEMMANAGER "8" "5 September 2014" .SH NAME ModemManager \- mobile broadband modem management daemon .SH SYNOPSIS \fBModemManager\fR [\fIOPTION\fR...] .SH DESCRIPTION ModemManager provides a unified high level API for communicating with mobile broadband modems, regardless of the protocol used to communicate with the actual device (Generic AT, vendor-specific AT, QCDM, QMI, MBIM...). ModemManager is a DBus-based system daemon and is not meant to be used directly from the command line. .SH APPLICATION OPTIONS .TP .B \-\-filter\-policy= Specify which ports are probed and how: .RS 9 .TP \fB'ALLOWLIST-ONLY'\fR Only devices or ports explicitly allowlisted with the 'ID_MM_DEVICE_PROCESS' udev tag are probed. .TP \fB'STRICT'\fR Only the TTY ports that are heuristically determined to be very likely to be modem ports are probed. Nay end up ignoring some devices. .RE .TP .B \-\-no\-auto\-scan Fully disable udev-based auto-scan looking for devices. .TP .B \-\-initial\-kernel\-events= Specify location of the file where the list of initial kernel events is available. The ModemManager daemon will process this file on startup. .TP .B \-\-debug Runs ModemManager with "DEBUG" log level and without daemonizing. This is useful for debugging, as it directs log output to the controlling terminal in addition to syslog. .TP .B \-V, \-\-version Print the ModemManager software version and exit. .TP .B \-h, \-\-help Show application options. .SH LOGGING OPTIONS .TP .B \-\-log\-level= Sets how much information ModemManager sends to the log destination (usually syslog's "daemon" facility). By default, only informational, warning, and error messages are logged. Given level must be one of "ERR", "WARN", "INFO" or "DEBUG". .TP .B \-\-log\-file= Specify location of the file where ModemManager will dump its log messages, instead of syslog. .TP .B \-\-log\-journal Output log message to the systemd journal. .TP .B \-\-log\-timestamps Include absolute timestamps in the log output. .TP .B \-\-log\-relative\-timestamps Include timestamps, relative to the start time of the daemon, in the log output. .SH TEST OPTIONS .TP .B \-\-test\-session Run the ModemManager daemon in the Session bus instead of the System bus. .TP .B \-\-test\-enable Enable the Test DBus interface in the daemon. .TP .B \-\-test\-plugin\-dir=[PATH] Specify an alternate directory where the daemon should look for vendor plugins. .SH AUTHOR Aleksander Morgado .SH SEE ALSO \fBmmcli\fR(1), \fBNetworkManager\fR(8) ModemManager-1.23.4-dev/docs/man/meson.build000066400000000000000000000002601456466623000205700ustar00rootroot00000000000000# SPDX-License-Identifier: GPL-2.0-or-later # Copyright (C) 2021 Iñigo Martinez mans = files( 'mmcli.1', 'ModemManager.8', ) install_man(mans) ModemManager-1.23.4-dev/docs/man/mmcli.1000066400000000000000000001000301456466623000176050ustar00rootroot00000000000000.\" mmcli(1) manual page .\" .\" Copyright (C) 2012 Martyn Russell .\" .\" Comment out '.nr' or set to 0 to eliminate WIDTH fiddlin' ! .nr half_xtra 4 .TH mmcli 1 "October 2012" GNU "User Commands" .SH NAME mmcli \- Control and monitor the ModemManager .SH SYNOPSIS \fBmmcli\fR [\fIOPTION\fR...] .SH DESCRIPTION ModemManager is a DBus-powered Linux daemon which provides a unified high level API for communicating with (mobile broadband) modems. It acts as a standard RIL (Radio Interface Layer) and may be used by different connection managers, like NetworkManager. Thanks to the built-in plugin architecture, ModemManager talks to very different kinds of modems with very different kinds of ports. In addition to the standard AT serial ports, Qualcomm-based QCDM and QMI ports are also supported. .SH HELP OPTIONS .TP .B \-h, \-\-help Show summary of options by group. .TP .B \-\-help\-all Show all groups and options. .TP .B \-\-help\-manager Show manager specific options. .TP .B \-\-help\-common Show common options. These are used for defining the device an option operates on. For example, modems, bearers, SIMs, SMS', etc. .TP .B \-\-help\-modem Show modem specific options. .TP .B \-\-help\-3gpp Show 3GPP specific options. .TP .B \-\-help\-cdma Show CDMA specific options. .TP .B \-\-help\-simple Show simple options. These are useful for getting connected or disconnected and understanding the state of things as fast as possible without worrying so much about the details. .TP .B \-\-help\-location Show location or positioning specific options. .TP .B \-\-help\-messaging Show messaging specific options. See also \fB\-\-help\-sms\fR which is related. .TP .B \-\-help\-time Show time specific options. .TP .B \-\-help\-firmware Show firmware specific options. .TP .B \-\-help\-oma Show OMA specific options. .TP .B \-\-help\-sim Show SIM card specific options. .TP .B \-\-help\-bearer Show bearer specific options. .TP .B \-\-help\-sms Show SMS specific options. See also \fB\-\-help\-messaging\fR which is related. .SH MANAGER OPTIONS .TP .B \-B, \-\-get\-daemon\-version Retrieve the version of the currently running ModemManager daemon. .TP .B \-G, \-\-set\-logging=[ERR|WARN|INFO|DEBUG] Set the logging level in ModemManager daemon. For debugging information you can supply \fBDEBUG\fR. Each value above \fBDEBUG\fR provides less detail. In most cases \fBERR\fR (for displaying errors) are the important messages. The default mode is \fBERR\fR. .TP .B \-L, \-\-list\-modems List available modems. .TP .B \-M, \-\-monitor\-modems List available modems and monitor modems added or removed. .TP .B \-S, \-\-scan-modems Scan for any potential new modems. This is only useful when expecting pure RS232 modems, as they are not notified automatically by the kernel. .TP .B \-I, \-\-inhibit\-device=[UID] Inhibit the specific device from being used by ModemManager. The \fBUID\fR that should be given is the value of the \fBDevice\fR property exposed by a given modem (i.e. equal to the \fBID_MM_PHYSDEV_UID\fR if one set, or otherwise equal to the full device sysfs path). This command will not exit right away, as that would implicitly remove the inhibition. The user must make sure to stop the mmcli process hitting Ctrl+C in order to un-inhibit the device. When a device is inhibited via this method, ModemManager will disable the modem (therefore stopping any ongoing connection) and will no longer use it until it is uninhibited. .TP .B \-\-report\-kernel\-event=['KEY1=VALUE1,KEY2=VALUE2,...'] Manually report kernel events, instead of relying on udev (e.g. if the daemon is running with \fB\-\-no\-auto\-scan\fR or if the system was built without udev support). The supported \fBKEY\fRs are: .RS 9 .TP \fB'action'\fR Action to report, one of 'add' or 'remove'. Required. .TP \fB'subsystem'\fR Subsystem of the specific port being reported, e.g. 'tty' (for serial ports), 'net' (for network interfaces), or 'usbmisc' (for cdc-wdm ports).. .TP \fB'name'\fR Name of the port being reported, e.g. 'ttyACM0', 'wwan0' or 'cdc-wdm0'. .TP \fB'uid'\fR The specific UID of the device, equivalent to the \fBID_MM_PHYSDEV_UID\fR udev tag. All ports reported with the same 'UID' value will be considered part of the same device, which may be useful for e.g. modems with multiple platform TTYs. .RE .TP .B \-\-report\-kernel\-event\-auto\-scan When built with udev support but the daemon is running with \fB\-\-no\-auto\-scan\fR, this method may be used to automatically report kernel events based on udev. This command will not exit right away. The user must make sure to stop the mmcli process hitting Ctrl+C in order to stopping monitoring for new events. .SH COMMON OPTIONS All options below take a \fBPATH\fR or \fBINDEX\fR argument. If no action is provided, the default information about the modem, bearer, etc. is shown instead. The \fBPATH\fR and \fBINDEX\fR are created automatically when the modem is plugged in. They can be found using \fBmmcli \-L\fR. This produces something like (for modems only): .RS 7 Found 1 modems: /org/freedesktop/ModemManager1/Modem/\fB4\fR .RE In this case, the \fBINDEX\fR is \fB4\fR and the \fBPATH\fR is the entire string above. However, for the bearers, SIMs and SMS cases, the \fBPATH\fR is slightly different. The \fIModem\fR is replaced with the object name in use, like \fIBearer\fR. For example: .RS 7 /org/freedesktop/ModemManager1/\fIBearer\fR/4 .RE .TP .B \-m, \-\-modem=[PATH|INDEX] Specify a modem. .TP .B \-b, \-\-bearer=[PATH|INDEX] Specify a bearer. .TP .B \-i, \-\-sim=[PATH|INDEX] Specify a SIM card. .TP .B \-s, \-\-sms=[PATH|INDEX] Specify an SMS. .SH MODEM OPTIONS All of the modem options below make use of the \fB\-\-modem\fR or \fB\-m\fR switch to specify the modem to act on. Some operations require a \fBMODE\fR. \fBMODE\fR can be any combination of the modes actually supported by the modem. In the perfect case, the following are possible: .Bd -literal -compact \fB'2G'\fR - 2G technologies, e.g. EDGE, CDMA1x \fB'3G'\fR - 3G technologies, e.g. HSPA, EV-DO \fB'4G'\fR - 4G technologies, e.g. LTE \fB'ANY'\fR - for all supported modes. .Ed .TP .B \-w, \-\-monitor\-state Monitor the state of a given modem. .TP .B \-e, \-\-enable Enable a given modem. This powers the antenna, starts the automatic registration process and in general prepares the modem to be connected. .TP .B \-d, \-\-disable Disable a given modem. This disconnects the existing connection(s) for the modem and puts it into a low power mode. .TP .B \-r, \-\-reset Resets the modem to the settings it had when it was power cycled. .TP .B \-\-factory\-reset=CODE Resets the modem to its original factory default settings. The \fBCODE\fR provided is vendor specific. Without the correct vendor code, it's unlikely this operation will succeed. This is not a common user action. .TP .B \-\-command=COMMAND Send an AT \fBCOMMAND\fR to the given modem. For example, \fBCOMMAND\fR could be 'AT+GMM' to probe for phone model information. This operation is only available when ModemManager is run in debug mode. .TP .B \-\-create\-bearer=['KEY1=VALUE1,KEY2=VALUE2,...'] Create a new packet data bearer for a given modem. The \fBKEY\fRs and some \fBVALUE\fRs are listed below: .RS 9 .TP \fB'apn'\fR Access Point Name. Required in 3GPP. .TP \fB'ip-type'\fR Addressing type. Given as a MMBearerIpFamily value (e.g. 'ipv4', 'ipv6', 'ipv4v6'). Optional in 3GPP and CDMA. .TP \fB'allowed-auth'\fR Authentication method to use. Given as a MMBearerAllowedAuth value (e.g. 'none|pap|chap|mschap|mschapv2|eap'). Optional in 3GPP. .TP \fB'user'\fR User name (if any) required by the network. Optional in 3GPP. .TP \fB'password'\fR Password (if any) required by the network. Optional in 3GPP. .TP \fB'allow-roaming'\fR Flag to tell whether connection is allowed during roaming, given as a boolean value (i.e 'yes' or 'no'). Optional in 3GPP. .TP \fB'rm-protocol'\fR Protocol of the Rm interface, given as a MMModemCdmaRmProtocol value (e.g. 'async', 'packet-relay', 'packet-network-ppp', 'packet-network-slip', 'stu-iii'). Optional in CDMA. .TP \fB'number'\fR Telephone number to dial. Required in POTS. .RE .TP .B \-\-delete\-bearer=[PATH|INDEX] Delete bearer from a given modem. .TP .B \-\-set\-allowed\-modes=[MODE1|MODE2|...] Set allowed modes for a given modem. For possible modes, see the beginning of this section. .TP .B \-\-set\-preferred\-mode=MODE Set the preferred \fBMODE\fR for the given modem. The \fBMODE\fR \fIMUST\fR be one of the allowed modes as set with the \fB\-\-set\-allowed\-modes\fR option. Possible \fBMODE\fR arguments are detailed at the beginning of this section. .TP .B \-\-set\-current\-bands=[BAND1|BAND2|...] Set bands to be used for a given modem. These are frequency ranges the modem should use. There are quite a number of supported bands and listing them all here would be quite extensive. For details, see the MMModemBand documentation. An example would be: 'egsm|dcs|pcs|g850' to select all the GSM frequency bands. .TP .B \-\-set\-primary\-sim\-slot=[SLOT] Request to switch the primary SIM slot. The given \fBSLOT\fR must be a valid slot number in the [1,N] range, where N is the amount of SIM slots available in the system. .TP .B \-\-inhibit Inhibit the specific modem from being used by ModemManager. This method is completely equivalent to \fB\-\-inhibit\-device\fR, with the only difference being that in this case, the modem must be managed by the daemon at the time the inhibition is requested. This command will not exit right away, as that would implicitly remove the inhibition. The user must make sure to stop the mmcli process hitting Ctrl+C in order to un-inhibit the device. When a device is inhibited via this method, ModemManager will disable the modem (therefore stopping any ongoing connection) and will no longer use it until it is uninhibited. .SH 3GPP OPTIONS The 3rd Generation Partnership Project (3GPP) is a collaboration between groups of telecommunications associations. These options pertain to devices which support 3GPP. Included are options to control USSD (Unstructured Supplementary Service Data) sessions. All of the 3GPP options below make use of the \fB\-\-modem\fR or \fB\-m\fR switch to specify the modem to act on. .TP .B \-\-3gpp\-scan Scan for available 3GPP networks. .TP .B \-\-3gpp\-register\-home Request a given modem to register in its home network. This registers with the default network(s) specified by the modem, .TP .B \-\-3gpp\-register\-in\-operator=MCCMNC Request a given modem to register on the network of the given \fBMCCMNC\fR (Mobile Country Code, Mobile Network Code) based operator. This code is used for GSM/LTE, CDMA, iDEN, TETRA and UMTS public land mobile networks and some satellite mobile networks. The ITU-T Recommendation E.212 defines mobile country codes. .TP .B \-\-3gpp\-ussd\-status Request the status of \fIANY\fR ongoing USSD session. .TP .B \-\-3gpp\-ussd\-initiate=COMMAND Request the given modem to initiate a USSD session with \fBCOMMAND\fR. For example, \fBCOMMAND\fR could be '*101#' to give your current pre-pay balance. .TP .B \-\-3gpp\-ussd\-respond=RESPONSE When initiating an USSD session, a \fRRESPONSE\fR may be needed by a network-originated request. This option allows for that. .TP .B \-\-3gpp\-ussd\-cancel Cancel an ongoing USSD session for a given modem. .TP .B \-\-3gpp\-disable\-facility\-lock=FACILITY,CONTROL_KEY Disable selected facility lock using provided control key. .RS 9 .TP \fB'FACILITY'\fR One of the following types of lock: .Bd -literal -compact \fB'net-pers'\fR - network personalization \fB'net-sub-pers'\fR - network subset personalization \fB'provider-pers'\fR - provider personalization \fB'corp-pers'\fR - corporate personalization .Ed .TP \fB'CONTROL_KEY'\fR Alphanumeric code to unlock facility. .RE .SH CDMA OPTIONS All CDMA (Code Division Multiple Access) options require the \fB\-\-modem\fR or \fB\-m\fR option. .TP .B \-\-cdma\-activate=CARRIER Activate the given modem using OTA (Over the Air) settings. The \fBCARRIER\fR is a code provided by the network for the default settings they provide. .SH SIMPLE OPTIONS All simple options must be used with \fB\-\-modem\fR or \fB\-m\fR. .TP .B \-\-simple\-connect=['KEY1=VALUE1,KEY2=VALUE2,...'] Run a full connection sequence using \fBKEY\fR / \fBVALUE\fR pairs. You can use the \fB\-\-create\-bearer\fR options, plus any of the following ones: .RS 9 .TP \fB'pin'\fR SIM-PIN unlock code. .TP \fB'operator-id'\fR ETSI MCC-MNC of a network to force registration. .RE .TP .B \-\-simple\-disconnect Disconnect \fIALL\fR connected bearers for a given modem. .SH LOCATION OPTIONS These options detail how to discover your location using Global Positioning System (GPS) or directly from your mobile network infrastructure (either 3GPP or 3GPP2). All location options must be used with \fB\-\-modem\fR or \fB\-m\fR. .TP .B \-\-location\-status Show the current status for discovering our location. .TP .B \-\-location\-get Show all location information available. .TP .B \-\-location\-enable\-3gpp Enable location discovery using the 3GPP network. .TP .B \-\-location\-disable\-3gpp Disable location discovery using the 3GPP network. .TP .B \-\-location\-enable\-agps-msa Enable A-GPS (MSA) support. This command does not implicitly start the GPS engine, it just specifies that A-GPS should be enabled when the engine is started. Therefore, the user should request enabling A-GPS before the raw or NMEA outputs are enabled with \fB\-\-location\-enable\-gps\-raw\fR or \fB\-\-location\-enable\-gps\-nmea\fR. .TP .B \-\-location\-disable\-agps-msa Disable A-GPS (MSA) support. .TP .B \-\-location\-enable\-agps-msb Enable A-GPS (MSB) support. This command does not implicitly start the GPS engine, it just specifies that A-GPS should be enabled when the engine is started. Therefore, the user should request enabling A-GPS before the raw or NMEA outputs are enabled with \fB\-\-location\-enable\-gps\-raw\fR or \fB\-\-location\-enable\-gps\-nmea\fR. .TP .B \-\-location\-disable\-agps-msb Disable A-GPS (MSB) support. .TP .B \-\-location\-enable\-gps\-nmea Enable location discovery using GPS and reported with NMEA traces. This command will start the GPS engine, if it isn't started already. .TP .B \-\-location\-disable\-gps\-nmea Disable location discovery using GPS and NMEA traces. If the raw output is not enabled at the same time, the GPS engine will be stopped. .TP .B \-\-location\-enable\-gps\-raw Enable location discovery using GPS and reported with raw (i.e. longitude/latitude) values. This command will start the GPS engine, if it isn't started already. .TP .B \-\-location\-disable\-gps\-raw Disable location discovery using GPS and raw values. If the NMEA output is not enabled at the same time, the GPS engine will be stopped. .TP .B \-\-location\-enable\-cdma-bs Enable location discovery using the 3GPP2 network. .TP .B \-\-location\-disable\-cdma-bs Disable location discovery using the 3GPP2 network. .TP .B \-\-location\-enable\-gps\-unmanaged Enable location discovery using GPS but without taking control of the NMEA tty port. This allows other programs, e.g. gpsd, to use the NMEA tty once the GPS engine has been enabled. .TP .B \-\-location\-disable\-gps\-unmanaged Disable location discovery using GPS and unmanaged port. .TP .B \-\-location\-set\-gps\-refresh\-rate=SEC Set the location refresh rate on the DBus interface to SEC seconds. If set to 0, the new location is published on the DBus interface as soon as ModemManager detects it. .TP .B \-\-location\-set\-supl\-server=[IP:PORT] or \-\-location\-set\-supl\-server=[FQDN:PORT] Configure the location of the A\-GPS SUPL server, either specifying the IP address (\fBIP:PORT\fR) or specifyng a fully qualified domain name (\fB[FQDN:PORT]\fR). .TP .B \-\-location\-inject\-assistance\-data=[PATH] Inject assistance data into the GNSS module, loaded from a local file at \fBPATH\fR. The assistance data should be in a format expected by the device, e.g. downloaded from the URLs exposed by the 'AssistanceDataServers' property. .TP .B \-\-location\-set\-enable\-signal Enable reporting location updates via DBus property signals. This is required if applications rely on listening to 'Location' property updates, instead of explicit queries with the policy-protected 'GetLocation' method. This DBus property signal updates are by default disabled. .TP .B \-\-location\-set\-disable\-signal Disable reporting location updates via DBus property signals. .SH MESSAGING OPTIONS All messaging options must be used with \fB\-\-modem\fR or \fB\-m\fR. .TP .B \-\-messaging\-status Show the status of the messaging support. .TP .B \-\-messaging\-list-sms List SMS messages available on a given modem. .TP .B \-\-messaging\-create-sms=['KEY1=VALUE1,...'] Create a new SMS on a given modem. \fBKEY\fRs can be any of the following: .RS 9 .TP \fB'number'\fR Number to which the message is addressed. .TP \fB'text'\fR Message text, in UTF-8. When sending, if the text is larger than the limit of the technology or modem, the message will be broken into multiple parts or messages. Note that text and data are never given at the same time. .TP \fB'smsc'\fR Indicates the SMS service center number. .TP \fB'validity'\fR Specifies when the SMS expires in the SMSC. .TP \fB'class'\fR 3GPP message class (0..3). .TP \fB'delivery-report-request'\fR Specifies whether delivery report is requested when sending the SMS ('yes' or 'no') .TP \fB'storage'\fR Specifies the storage where this message is kept. Storages may be 'sm', 'me', 'mt', 'sr', 'bm', 'ta'. .RE .TP .B \-\-messaging\-create\-sms\-with\-data=PATH Use \fBPATH\fR to a filename as the data to create a new SMS. .TP .B \-\-messaging\-create\-sms\-with\-text=PATH Use \fBPATH\fR to a filename as the message to create a new SMS. .TP .B \-\-messaging\-delete\-sms=[PATH|INDEX] Delete an SMS from a given modem. .SH TIME OPTIONS All time operations require the \fB\-\-modem\fR or \fB\-m\fR option. .TP .B \-\-time Display the current network time from the operator. This includes the timezone which is usually of importance. .SH VOICE OPTIONS All voice operations require the \fB\-\-modem\fR or \fB\-m\fR option. .TP .B \-\-voice\-list\-calls List calls managed (initiated, received, ongoing) on a given modem. .TP .B \-\-voice\-create-call=['KEY1=VALUE1,...'] Create a new outgoing call on a given modem. \fBKEY\fRs can be any of the following: .RS 9 .TP \fB'number'\fR Number to call. .RE .TP .B \-\-voice\-delete\-call=[PATH|INDEX] Delete a call from a given modem. .SH FIRMWARE OPTIONS All firmware options require the \fB\-\-modem\fR or \fB\-m\fR option. .TP .B \-\-firmware\-status Show firmware update specific details and properties. .TP .B \-\-firmware\-list List all the firmware images installed on a given modem. .TP .B \-\-firmware\-select=ID Select a firmware image from those installed on a given modem. A list of available firmware images can be seen using the \fB\-\-firmware\-list\fR option. The \fBID\fR provided is a \fIUNIQUE\fR identifier for the firmware. .SH SIGNAL OPTIONS All signal options require the \fB\-\-modem\fR or \fB\-m\fR option. .TP .B \-\-signal\-setup=[Rate] Setup extended signal quality information retrieval at the specified rate (in seconds). By default this is disabled (rate set to 0). .TP .B \-\-signal\-get Retrieve the last extended signal quality information loaded. .SH OMA OPTIONS All OMA options require the \fB\-\-modem\fR or \fB\-m\fR option. .TP .B \-\-oma\-status Show the status of the OMA device management subsystem. .TP .B \-\-oma\-start\-client\-initiated\-session=[SESSION TYPE] Request to start a client initiated session. The given session type must be one of: 'client\-initiated\-device\-configure' 'client\-initiated\-prl\-update' 'client\-initiated\-hands\-free\-activation' .TP .B \-\-oma\-accept\-network\-initiated\-session=[SESSION ID] Request to accept a network initiated session. .TP .B \-\-oma\-reject\-network\-initiated\-session=[SESSION ID] Request to reject a network initiated session. .TP .B \-\-oma\-cancel\-session Request to cancel current OMA session, if any. .SH SIM OPTIONS .TP .B \-\-pin=PIN Send \fBPIN\fR code to a given SIM card. .TP .B \-\-puk=PUK Send \fBPUK\fR code to a given SIM card. This must be used \fIWITH\fR \fB\-\-pin\fR. .TP .B \-\-enable\-pin Enable PIN request for a given SIM card. This must be used \fIWITH\fR \fB\-\-pin\fR. .TP .B \-\-disable\-pin Disable PIN request for a given SIM card. This must be used \fIWITH\fR \fB\-\-pin\fR. .TP .B \-\-change\-pin=PIN Change the PIN for a given SIM card. It will be set to \fBPIN\fR. This must be used \fIWITH\fR \fB\-\-pin\fR to supply the old PIN number. .SH BEARER OPTIONS All bearer options require the \fB\-\-bearer\fR or \fB\-b\fR option. .TP .B \-c, \-\-connect Connect to a given bearer. .TP .B \-x, \-\-disconnect Disconnect from a given bearer. .SH SMS OPTIONS All SMS options require the \fB\-\-sms\fR or \fB\-s\fR option. .TP .B \-\-send Send an SMS. .TP .B \-\-store This option will store the SMS in the default storage defined by the modem, which may be either modem-memory or SMS-memory. To know what the existing default storage is, see the \fB\-\-messaging\-status\fR option. .TP .B \-\-store\-in\-storage=STORAGE This option states which \fBSTORAGE\fR to use for SMS messages. Possible values for \fBSTORAGE\fR include: .RS 9 .TP \fB'sm'\fR SIM card storage area. .TP \fB'me'\fR Mobile equipment storage area. .TP \fB'mt'\fR Sum of SIM and Mobile equipment storages .TP \fB'sr'\fR Status report message storage area. .TP \fB'bm'\fR Broadcast message storage area. .TP \fB'ta'\fR Terminal adaptor message storage area. .RE .TP .B \-\-create\-file\-with\-data\=PATH This option takes an SMS that has \fIDATA\fR (not \fITEXT\fR) and will create a local file described by \fBPATH\fR and store the content of the SMS there. .SH CALL OPTIONS .TP .B \-\-start Initiate an outgoing call. .TP .B \-\-accept Accept an incoming call. .TP .B \-\-hangup Reject an incoming call or hangup an ongoing one. .TP .B \-\-send\-dtmf=[0\-9A\-D*#] Send a DTMF sequence through an ongoing call. .SH APPLICATION OPTIONS .TP .B \-J, \-\-output\-json Run action with machine-friendly JSON output, to be used e.g. by shell scripts that rely on mmcli operations. .TP .B \-K, \-\-output\-keyvalue Run action with machine-friendly key-value output, to be used e.g. by shell scripts that rely on mmcli operations. .TP .B \-v, \-\-verbose Perform actions with more details reported and/or logged. .TP .B \-V, \-\-version Returns the version of this program. .TP .B \-a, \-\-async Use asynchronous methods. This is purely a development tool and has no practical benefit to most user operations. .TP .B \-\-timeout=SECONDS Use \fBSECONDS\fR for the timeout when performing operations with this command. This option is useful when executing long running operations, like \fB\-\-3gpp\-scan\fR. .SH EXAMPLES .SS Send the PIN to the SIM card You'll need first to know which the proper path/index is for the SIM in your modem: .Bd -literal -compact $ mmcli -m 0 -K | grep "modem.generic.sim" | awk -F ": " '{ print $2 }' /org/freedesktop/ModemManager1/SIM/0 .Ed And after that, you can just use the SIM index: .Bd -literal -compact $ sudo mmcli -i 0 --pin=1234 successfully sent PIN code to the SIM .Ed .SS Simple connect and disconnect You can launch the simple connection process like: .Bd -literal -compact $ sudo mmcli -m 0 --simple-connect="pin=1234,apn=internet" successfully connected the modem .Ed Then, you can disconnect it like: .Bd -literal -compact $ sudo mmcli -m 0 --simple-disconnect successfully disconnected all bearers in the modem .Ed .SS 3GPP network scan Scanning for 3GPP networks may really take a long time, so a specific timeout must be given: .Bd -literal -compact $ sudo mmcli -m 0 --3gpp-scan --timeout=300 --------------------- 3GPP scan | networks: 21403 - Orange SP (gprs, unknown) | 21407 - Movistar (gprs, unknown) | 21404 - YOIGO (gprs, unknown) | 21401 - vodafone ES (gprs, unknown) .Ed .SS Creating a new SMS message & storing it Using the “sm” (SIM), you can do this using: .Bd -literal -compact $ sudo mmcli -m 0 --messaging-create-sms="text='Hello world',number='+1234567890'" Successfully created new SMS: /org/freedesktop/ModemManager1/SMS/21 (unknown) $ sudo mmcli -s 21 --store-in-storage="sm" successfully stored the SMS $ sudo mmcli -s 21 ------------------------------- General | dbus path: /org/freedesktop/ModemManager1/SMS/21 ------------------------------- Content | number: +1234567890 | text: Hello world ------------------------------- Properties | PDU type: submit | state: stored | smsc: unknown | validity: 0 | class: 0 | storage: sm | delivery report: not requested | message reference: 0 $ sudo mmcli -m 0 --messaging-status ---------------------------- Messaging | supported storages: sm, me | default storage: me .Ed .SS Sending binary SMS messages from files As you can see below, the important part is the \fB\-\-messaging\-create\-sms\-with\-data\fR and the \fBPATH\fR provided. .Bd -literal -compact $ sudo mmcli -m 0 \\ --messaging-create-sms="number='+1234567890'" \\ --messaging-create-sms-with-data=/path/to/your/file Successfully created new SMS: /org/freedesktop/ModemManager1/SMS/22 (unknown) $ sudo mmcli -s 22 --send successfully sent the SMS .Ed .SS Listing SMS messages When the receiver gets all the parts of the message, they can now recover the sent file with another \fBmmcli\fR command in their ModemManager setup: .Bd -literal -compact $> sudo mmcli -m 0 --messaging-list-sms /org/freedesktop/ModemManager1/SMS/0 (received) $> sudo mmcli -s 0 --create-file-with-data=/path/to/the/output/file .Ed .SS GPS location status You first need to check whether the modem has GPS-specific location capabilities. Note that we’ll assume the modem is exposed as index 0; if you have more than one modem, just use --list-modems to check the proper modem index: .Bd -literal -compact $ mmcli -m 0 --location-status ---------------------------- Location | capabilities: 3gpp-lac-ci, gps-raw, gps-nmea | enabled: none | signals: no .Ed The output says that the modem supports 3GPP Location area code/Cell ID, GPS raw and GPS-NMEA location sources. None is enabled yet, as we didn’t enable the modem, which we can do issuing: .Bd -literal -compact $ sudo mmcli -m 0 --enable successfully enabled the modem $ mmcli -m 0 --location-status ---------------------------- Location | capabilities: 3gpp-lac-ci, gps-raw, gps-nmea | enabled: 3gpp-lac-ci | signals: no .Ed .SS GPS location technology enabling We can start the GPS engine by enabling the RAW or NMEA GPS location sources: .Bd -literal -compact $ sudo mmcli -m 0 \\ --location-enable-gps-raw \\ --location-enable-gps-nmea successfully setup location gathering .Ed If we do check again the status, we’ll see the GPS-specific locations are enabled: .Bd -literal -compact $ mmcli -m 0 --location-status -------------------------------- Location | capabilities: 3gpp-lac-ci, gps-raw, gps-nmea | enabled: 3gpp-lac-ci, gps-raw, gps-nmea | signals: no .Ed .SS GPS location retrieval You can query all location information at the same time with a single command. If any of the specific outputs is not available, the corresponding section will be omitted from the output. .Bd -literal -compact $ sudo mmcli -m 0 --location-get ------------------------- 3GPP location | Mobile country code: 214 | Mobile network code: 3 | Location area code: 21071 | Cell ID: 7033737 ------------------------- GPS NMEA traces | $GPGGA,,,,,,0,,,,,,,,*66 | $GPRMC,,V,,,,,,,,,,N*53 | $GPGSA,A,1,,,,,,,,,,,,,,,*1E | $GPGSV,4,1,16,24,,,,29,,,,05,,,,18,,,*7A | $GPGSV,4,2,16,22,,,,14,,,,11,,,,17,,,*7B | $GPGSV,4,3,16,03,,,,12,,,,30,,,,13,,,*78 | $GPGSV,4,4,16,23,,,,15,,,,27,,,,07,,,*79 | $GPVTG,,T,,M,,N,,K,N*2C .Ed .SS A-GPS support If A-GPS is enabled before starting the GPS engine, and if a data connection is available in the modem, the configured SUPL servers may be used to obtain a faster initial position fix. Note that the GPS engine will not be started when just A-GPS capability is enabled. An explicit output (RAW or NMEA) is required to be enabled in order to start the GPS engine. .Bd -literal -compact $ mmcli -m 0 --location-status -------------------------------- Location | capabilities: 3gpp-lac-ci, gps-raw, gps-nmea, agps-msa, agps-msb | enabled: 3gpp-lac-ci | signals: no ----------------------------- GPS | refresh rate: 30 seconds | a-gps supl server: supl.google.com:7276 $ sudo mmcli -m 0 --location-enable-agps-msa successfully setup location gathering $ sudo mmcli -m 0 --location-enable-gps-nmea successfully setup location gathering $ sudo mmcli -m 0 --location-enable-gps-raw successfully setup location gathering .Ed .SS Injecting assistance data If the modem device does not have an ongoing connection (e.g. no mobile network coverage) but the system has other means to access the Internet (e.g. WiFi), the user may be able to download location assistance data and inject it in the module. E.g. If the device supports XTRA assistance data, the user may download it from one of the servers listed by ModemManager and manually inject it afterwards. The XTRA assistance data is usually valid for several days. .Bd -literal -compact $ mmcli -m 0 --location-status -------------------------------- Location | capabilities: 3gpp-lac-ci, gps-raw, gps-nmea, agps-msa, agps-msb | enabled: 3gpp-lac-ci | signals: no -------------------------------- GPS | refresh rate: 30 seconds | a-gps supl server: supl.google.com:7276 | supported assistance: xtra | assistance servers: https://xtrapath3.izatcloud.net/xtra3grcej.bin | https://xtrapath1.izatcloud.net/xtra3grcej.bin | https://xtrapath2.izatcloud.net/xtra3grcej.bin $ wget -q https://xtrapath3.izatcloud.net/xtra3grcej.bin $ sudo mmcli -m 0 --location-inject-assistance-data=./xtra3grcej.bin successfully injected assistance data $ sudo mmcli -m 0 --location-enable-gps-nmea successfully setup location gathering $ sudo mmcli -m 0 --location-enable-gps-raw successfully setup location gathering .Ed .SS Key-Value output Writing shell scripts that use mmcli to perform operations with the modem is easy when using the \fB\-\-output\-keyvalue\fR option. For example, you could gather all the main status information of the modem with a single call and then parse it to read single fields: .Bd -literal -compact $ STATUS=$(mmcli -m 0 --output-keyvalue) $ echo "${STATUS}" | grep "modem.generic.state " | awk -F ": " '{ print $2 }' failed $ echo "${STATUS}" | grep "modem.generic.state-failed-reason " | awk -F ": " '{ print $2 }' sim-missing .Ed .SH AUTHORS Written by Martyn Russell and Aleksander Morgado .SH SEE ALSO \fBModemManager\fR(8), \fBNetworkManager\fR(8) AT (http://en.wikipedia.org/wiki/AT_commands). 3GPP (http://en.wikipedia.org/wiki/3GPP). MCCMNC (http://en.wikipedia.org/wiki/Mobile_Network_Code). USSD (http://en.wikipedia.org/wiki/Unstructured_Supplementary_Service_Data). CDMA (http://en.wikipedia.org/wiki/Code_division_multiple_access). OTA (http://en.wikipedia.org/wiki/Over-the-air_programming). GPS (http://en.wikipedia.org/wiki/Global_Positioning_System) NMEA (http://en.wikipedia.org/wiki/NMEA_0183) ModemManager-1.23.4-dev/docs/reference/000077500000000000000000000000001456466623000176135ustar00rootroot00000000000000ModemManager-1.23.4-dev/docs/reference/api/000077500000000000000000000000001456466623000203645ustar00rootroot00000000000000ModemManager-1.23.4-dev/docs/reference/api/ModemManager-dbus-reference.xml000066400000000000000000000240471456466623000263400ustar00rootroot00000000000000 D-Bus Reference The <literal>org.freedesktop.ModemManager1</literal> bus name The D-Bus name org.freedesktop.ModemManager1 on the system bus is used by the ModemManager daemon. If this daemon isn't running, it will be started if D-Bus messages are sent to the name. Standard interfaces Please refer to the DBus specification at freedesktop.org for more information on how to use these standard interfaces.
org.freedesktop.DBus.Properties All objects (Manager, Modems, Bearers, SIMs, SMSs) exported at the org.freedesktop.ModemManager1 bus name implement the standard org.freedesktop.DBus.Properties interface. Objects implementing this interface provide a common way to query for property values and also a generic signal to get notified about changes in those properties.
org.freedesktop.DBus.Introspectable All objects (Manager, Modems, Bearers, SIMs, SMSs) exported at the org.freedesktop.ModemManager1 bus name implement the standard org.freedesktop.DBus.Introspectable interface. Objects implementing this interface will provide an XML-based description of the object and its interfaces.
org.freedesktop.DBus.ObjectManager The Manager object exported at the org.freedesktop.ModemManager1 bus name implements the standard org.freedesktop.DBus.ObjectManager interface. This interface, included in rev. 0.17 of the DBus specification, allows a generic way to control the addition and removal of Modem objects, as well as the addition and removal of interfaces in the given objects.
The <literal>/org/freedesktop/ModemManager1</literal> object The ModemManager process will export an object at the well-known path /org/freedesktop/ModemManager1. This object, which implements the standard org.freedesktop.DBus.ObjectManager, is responsible for managing the list of Modem objects. This object also controls any process-wide operation, such as the log level being used by the daemon. The <literal>/org/freedesktop/ModemManager/Modems</literal> objects Modem objects are exported in DBus with the following path base: /org/freedesktop/ModemManager1/Modems/#, where # indicates a unique unsigned integer which identifies the object. The Modem objects will export a generic Modem interface which includes common features and actions applicable to most modem types. This interface, among other actions, allows the management (creation, listing, deletion) of Bearer objects which can then be used to request the modem to get in connected state. Modem objects will also export the generic Simple interface. This interface provides an easy access to the most simple and common operations that may be performed with the modem, including connection and disconnection. Users of the Simple interface do not need to take care of getting the modem registered, and they also don't need to manage the creation of bearers themselves. All the logic required to get the modem connected or disconnected is handled by the Simple interface. Modems with specific 3GPP and/or CDMA capabilities will export modem type specific interfaces, like the 3GPP interface or the CDMA interface. The <literal>/org/freedesktop/ModemManager/Bearers</literal> objects Bearer objects are owned and managed by specific Modem objects. A single Modem may expose one or more Bearer objects, which can then be used to get the modem into connected state. The <literal>/org/freedesktop/ModemManager/SIMs</literal> objects Broadband modems usually need a SIM card to operate. Each Modem object will therefore expose up to one SIM object, which allows SIM-specific actions such as PIN unlocking. The <literal>/org/freedesktop/ModemManager/SMSs</literal> objects Modems implementing the Messaging interface will export one SMS object for each SMS stored in the device. The <literal>/org/freedesktop/ModemManager/Calls</literal> objects Modems implementing the Voice interface will export one Call object for each Call managed in the device.
ModemManager-1.23.4-dev/docs/reference/api/ModemManager-docs.xml000066400000000000000000000130311456466623000243660ustar00rootroot00000000000000 ]> ModemManager Reference Manual For ModemManager version &version; The latest version of this documentation can be found on-line at https://www.freedesktop.org/software/ModemManager/doc/latest/ModemManager/. Dan Williams
dcbw@redhat.com
Aleksander Morgado
aleksander@aleksander.es
2008 2009 2010 2011 2012 2013 2014 2015 2016 2017 2018 2019 2020 2021 2022 2023 The ModemManager Authors Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.3 or any later version published by the Free Software Foundation with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts. You may obtain a copy of the GNU Free Documentation License from the Free Software Foundation by visiting their Web site or by writing to:
The Free Software Foundation, Inc. 51 Franklin Street, Suite 500 Boston, MA 02110-1335 USA
Common types and definitions Common udev tag definitions Compatibility with older versions Index Index of deprecated symbols Index of new symbols in 1.0 Index of new symbols in 1.2 Index of new symbols in 1.4 Index of new symbols in 1.6 Index of new symbols in 1.8 Index of new symbols in 1.10 Index of new symbols in 1.12 Index of new symbols in 1.14 Index of new symbols in 1.16 Index of new symbols in 1.18 Index of new symbols in 1.20
ModemManager-1.23.4-dev/docs/reference/api/ModemManager-migration-reference.xml000066400000000000000000000151571456466623000273760ustar00rootroot00000000000000 Migrating from ModemManager 0.6 to ModemManager 1.0 ModemManager 1.0 is a new major version of ModemManager that breaks both API and ABI compared to previous versions. These changes allow better managing new types of devices (e.g. those with multiple capabilities), or those not based in AT commands for operation (e.g. QMI or MBIM modems). This section provides an introduction to the changes done in the DBus interface with respect to the main operations performed with modems through ModemManager.
Listing available modems The D-Bus name org.freedesktop.ModemManager1 on the system bus is the new name used by the ModemManager 1.0 daemon, and it implements several standard DBus interfaces, including the new org.freedesktop.DBus.ObjectManager interface, which allows to list available modem objects and get notifications where new ones are added or when existing ones are removed. There are therefore neither a custom method to enumerate devices as in the old 0.6 interface, nor custom signals to notify about added or removed modems. Modems which are found but are not usable will be flagged with a MM_MODEM_STATE_FAILED state in the State property, and a more detailed reason about the failure will be given in the StateFailedReason property. The most common case of failure is to have the SIM missing in a modem which requires one for operation.
PIN unlocking The process of PIN unlocking the modem is now performed with the SendPin() method in the org.freedesktop.ModemManager1.Sim interface. The path of the SIM object is specified in the Sim property of the org.freedesktop.ModemManager1.Modem interface. If the modem doesn't have a SIM, no object path will be given.
Connection and disconnection The process of requesting to connect or disconnect the modem is now split into two operations: creating a bearer with CreateBearer() in the org.freedesktop.ModemManager1.Modem interface and getting the bearer connected with Connect() in the org.freedesktop.ModemManager1.Bearer interface. These two steps are equivalent to the old Connect() method in the previous org.freedesktop.ModemManager.Modem interface. The old Disconnect() method in the previous org.freedesktop.ModemManager.Modem interface is therefore also applied in a per-bearer basis through the new Disconnect() in the org.freedesktop.ModemManager1.Bearer interface. This logic of splitting the connection logic allows ModemManager to create multiple bearers that may be connected to e.g. different access points (if the modem allows it).
Simple connection In order to simplify the whole sequence to get the modem connected, ModemManager still exposes a Simple interface, renamed as: org.freedesktop.ModemManager1.Modem.Simple . The Connect() method will create a single bearer with the parameters specified in the call and get it connected, while the Disconnect() method will disconnect all available bearers. One of the main differences with respect to the 0.6 interface, is that Connect() doesn't support to change allowed modes or bands. Instead, these operations should be done through the methods in the org.freedesktop.ModemManager1.Modem interface.
ModemManager-1.23.4-dev/docs/reference/api/ModemManager-overview.xml000066400000000000000000001350371456466623000253170ustar00rootroot00000000000000 ModemManager Overview Introduction ModemManager provides a unified high level API for communicating with mobile broadband modems, regardless of the protocol used to communicate with the actual device (Generic AT, vendor-specific AT, QCDM, QMI, MBIM...). Using ModemManager is a system daemon and is not meant to be used directly from the command line. However, since it provides a DBus API, it is possible to use 'dbus-send' commands or the new 'mmcli' command line interface to control it from the terminal. The devices are queried from udev and automatically updated based on hardware events, although a manual re-scan can also be requested to look for RS232 modems. Implementation ModemManager is a DBus system bus activated service (meaning it's started automatically when a request arrives). It is written in C, using glib and gio. Several GInterfaces specify different features that the modems support, including the generic MMIfaceModem3gpp and MMIfaceModemCdma which provice basic operations for 3GPP (GSM, UMTS, LTE) or CDMA (CDMA1x, EV-DO) modems. If a given feature is not available in the modem, the specific interface will not be exported in DBus. 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 port and to create custom MMBroadbandModem 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 are multiple fully working plugins in the plugins/ directory that can be used as an example for writing new plugins. Writing new plugins is highly encouraged! The plugin 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! Modem detection
Builds with udev support ModemManager requires udev-powered Linux kernels in order to get notified of possible available Modems. udev will report each of the ports found in the device, and ModemManager will consider for probing each of the ports marked with the ID_MM_CANDIDATE tag in udev. Aditionally, users of RS232-based devices may need to request additional manual scans via DBus, in order to detect modems that may have been connected to RS232 to USB adapters. In this case, udev just knows about the USB adapter being connected, not about the RS232 modem connected to the adapter, if any.
Builds without udev support When the udev daemon isn't available in the system, the ReportKernelEvent method in the Manager interface may be used to notify the ModemManager daemons of device addition and removals. When udev support is disabled in the build, the ID_MM_CANDIDATE tag and manual scan requests are still applicable. ModemManager has a built-in parser of udev rule files that is enabled when udev itself isn't available.
Modem filter ModemManager will not probe all TTYs, NET and cdc-wdm ports found in the system, as this may end up interfering e.g. with TTYs that have nothing to do with modem devices. The daemon comes with several predefined filter policies, each of them composed of one or more filter rules.
Filter rules The device filter in ModemManager defines the following independent filter rules. The predefined filter policies are based on one or more of these predefined filter rules. MM_FILTER_RULE_EXPLICIT_ALLOWLIST This filter allows users to manually tag devices and/or device ports with the ID_MM_DEVICE_PROCESS udev tag. If the filter finds this tag, the device and/or device ports will be automatically accepted and port probing will be allowed. $ sudo vim /lib/udev/rules.d/78-mm-allowlist-internal-modem.rules ACTION!="add|change|move", GOTO="mm_allowlist_internal_modem_end" ATTRS{idVendor}=="1199", ATTRS{idProduct}=="a001", ENV{ID_MM_DEVICE_PROCESS}="1" LABEL="mm_allowlist_internal_modem_end" // Apply new rules without reboot $ sudo udevadm control --reload $ sudo udevadm trigger MM_FILTER_RULE_EXPLICIT_BLACKLIST This filter allows users to manually tag devices and/or device ports with the ID_MM_DEVICE_IGNORE udev tag. If the filter finds this tag, the device and/or device ports will be automatically ignored and port probing will be never run on them. $ sudo vim /lib/udev/rules.d/78-mm-blacklist-internal-modem.rules ACTION!="add|change|move", GOTO="mm_blacklist_internal_modem_end" ATTRS{idVendor}=="1199", ATTRS{idProduct}=="a001", ENV{ID_MM_DEVICE_IGNORE}="1" LABEL="mm_blacklist_internal_modem_end" // Apply new rules without reboot $ sudo udevadm control --reload $ sudo udevadm trigger MM_FILTER_RULE_PLUGIN_ALLOWLIST This filter will automatically allowlist devices that are explicitly referenced by plugins, either with plugin-specific allowlist tags, with exact vid:pid matches, or just with vid matches. MM_FILTER_RULE_QRTR This filter will automatically flag as allowed all QRTR nodes that have been notified as being modem management capable. This filter rule is available since 1.18.0. MM_FILTER_RULE_VIRTUAL This filter will automatically flag as forbidden all ports exposed by virtual devices, like the 'lo' network interface or the tty0, tty1... virtual terminals. There is no reason to disable this filter, except for testing purposes. MM_FILTER_RULE_NET This filter will automatically flag as allowed all network ports exposed by devices. Unless there is a will to explicitly forbid network ports, this filter should always be enabled. MM_FILTER_RULE_USBMISC This filter will automatically flag as allowed all cdc-wdm ports exposed in the usbmisc subsystem. Unless there is a will to explicitly forbid the cdc-wdm ports exposed by qmi_wwan, cdc_mbim or huawei-cdc-ncm kernel drivers, this filter should always be enabled. MM_FILTER_RULE_RPMSG This filter will automatically flag as allowed all rpmsg ports exposed in the rpmsg subsystem. Unless there is a will to explicitly forbid the rpmsg ports, this filter should always be enabled. This filter rule is available since 1.16.0. MM_FILTER_RULE_WWAN This filter will automatically flag as allowed all wwan control ports exposed in the wwan subsystem. Unless there is a will to explicitly forbid the wwan control ports, this filter should always be enabled. This filter rule is available since 1.18.0. MM_FILTER_RULE_TTY If the MM_FILTER_RULE_TTY filter is disabled, no TTY port will be allowed. If this filter is enabled, TTY ports will only be allowed if the TTY-specific filters (defined next) allow it. MM_FILTER_RULE_TTY_PLATFORM_DRIVER If this filter is enabled, all platform TTY ports will be forbidden automatically. MM_FILTER_RULE_TTY_DRIVER If this filter is enabled, all TTY ports managed by modem-specific kernel drivers will be allowed automatically. FILTER_RULE_TTY_ACM_INTERFACE If this filter is enabled, all TTY ports managed by the cdc-acm kernel driver with class=2/subclass=2/protocol=1 (AT command capable ttyACM port) will be allowed automatically. MM_FILTER_RULE_TTY_WITH_NET If this filter is enabled, all TTY ports for devices that also expose a network interface port will be allowed automatically. MM_FILTER_RULE_TTY_DEFAULT_FORBIDDEN This rule is the implicit one defining what happens when a TTY port isn't explicitly accepted by any of the TTY-specific filters; i.e. the TTY port will be forbidden. The following filter rules have been deprecated and are no longer used. MM_FILTER_RULE_TTY_BLACKLIST Deprecated in 1.18.0. MM_FILTER_RULE_TTY_MANUAL_SCAN_ONLY Deprecated in 1.18.0.
Filter policies The predefined filter policies are: Allowlist only This is a policy where only the MM_FILTER_RULE_EXPLICIT_ALLOWLIST rule is enabled. # /usr/sbin/ModemManager --filter-policy=ALLOWLIST-ONLY Strict This is a policy where the following rules are enabled: MM_FILTER_RULE_EXPLICIT_ALLOWLIST MM_FILTER_RULE_EXPLICIT_BLOCKLIST MM_FILTER_RULE_PLUGIN_ALLOWLIST MM_FILTER_RULE_QRTR MM_FILTER_RULE_VIRTUAL MM_FILTER_RULE_NET MM_FILTER_RULE_USBMISC MM_FILTER_RULE_RPMSG MM_FILTER_RULE_TTY MM_FILTER_RULE_TTY_PLATFORM_DRIVER MM_FILTER_RULE_TTY_DRIVER MM_FILTER_RULE_TTY_ACM_INTERFACE MM_FILTER_RULE_TTY_WITH_NET MM_FILTER_RULE_TTY_DEFAULT_FORBIDDEN MM_FILTER_RULE_WWAN This policy is the default one when a different one is not explicitly selected. In this policy, all TTYs are forbidden except for the ones explicitly allowed by one of the TTY-specific rules. # /usr/sbin/ModemManager --filter-policy=STRICT Custom Any of the previously defined predefined policies may be modified rule per rule by explicitly enabling or disabling rules via environment variables. E.g. this would launch ModemManager with the Strict filter policy but with all net and cdc-wdm ports forbidden completely: # MM_FILTER_RULE_NET=0 \ MM_FILTER_RULE_USBMISC=0 \ /usr/sbin/ModemManager --filter-policy=STRICT E.g. this would launch ModemManager with the allowlist-only filter policy but also explicitly allowing all net and cdc-wdm ports. Note that in this case, all virtual net ports (e.g. 'lo') are also being allowed. # MM_FILTER_RULE_NET=1 \ MM_FILTER_RULE_USBMISC=1 \ /usr/sbin/ModemManager --filter-policy=ALLOWLIST-ONLY
Port probing Whenever a new device is detected by ModemManager, port probing process will get started, so that we can determine which kind of ports we have, and also which plugin we need to use for the specific device. Devices may expose one or more ports, and all of them will follow the same probing logic. The whole probing process, including pre-probing and post-probing filters, is implemented in the core ModemManager daemon. Plugins will just configure their own special needs in the probing process, so that only the steps required by the given plugin are executed. For example, plugins which do not support RS232 devices will not need AT-based vendor or product filters.
Pre-probing filters Pre-probing filters are those which control whether the probing, as requested by the specific plugin, takes place. Allowed vendor IDs Plugins can provide a list of udev-reported vendor IDs to be used as pre-probing filters. If the vendor ID reported by the device via udev is found in the list provided by the plugin, port probing will be launched as requested by the given plugin. This filter is specified by the MM_PLUGIN_ALLOWED_VENDOR_IDS property in the MMPlugin object provided by the plugin. Product IDs Plugins can provide a list of udev-reported pairs of vendor and product IDs to be used as pre-probing filters. If the vendor ID and product ID pair reported by the device via udev is found in the list of 'allowed' pairs provided by the plugin, port probing will be launched as requested by the given plugin. This additional filter should be used when the plugin is expected to work only with a given specific product of a given vendor. If the vendor ID and product ID pair reported by the device via udev is found in the list of 'forbidden' pairs provided by the plugin, port probing will not be launched by this plugin. This additional filter should be used when the plugin supports all devices of a given vendor except for some specific ones. These filters are specified by the MM_PLUGIN_ALLOWED_PRODUCT_IDS and MM_PLUGIN_FORBIDDEN_PRODUCT_IDS properties in the MMPlugin object provided by the plugin. Subsystems Plugins can specify which subsystems they expect, so that we filter out any port detected with a subsystem not listed by the plugin. This filter is specified by the MM_PLUGIN_ALLOWED_SUBSYSTEMS property in the MMPlugin object provided by the plugin. Drivers Plugins can specify which drivers they expect, so that we filter out any port detected being managed by a driver not listed by the plugin. Plugins can also specify which drivers they do not expect, so that we filter out any port detected being managed by a driver listed by the plugin. These filters are specified by the MM_PLUGIN_ALLOWED_DRIVERS and MM_PLUGIN_FORBIDDEN_DRIVERS properties in the MMPlugin object provided by the plugin. udev tags Plugins can provide a list of udev tags, so that we filter out any port detected which doesn't expose any of the given tags. This filter is specified by the MM_PLUGIN_ALLOWED_UDEV_TAGS property in the MMPlugin object provided by the plugin.
Probing sequence Whenever all pre-probing filters of a given plugin pass, ModemManager will run the probing sequence as requested by the specific plugin. The main purpose of the probing sequence step is to determine the type of port being probed, and also prepare the information required in any expected post-probing filter. Custom initialization This property allows plugins to provide an asynchronous method which will get executed as soon as the AT port gets opened. This method may be used for any purpose, like running an early command in the ports as soon as possible, or querying the modem for info about the port layout. This configuration is specified by the MM_PLUGIN_CUSTOM_INIT property in the MMPlugin object provided by the plugin. AT allowed This boolean property allows plugins to specify that they expect and support AT serial ports. This configuration is specified by the MM_PLUGIN_ALLOWED_AT property in the MMPlugin object provided by the plugin. Single AT expected This boolean property allows plugins to specify that they only expect and support one AT serial port. Whenever the first AT port is grabbed, any remaining AT probing in ports of the same device will get cancelled. This configuration is specified by the MM_PLUGIN_ALLOWED_SINGLE_AT property in the MMPlugin object provided by the plugin. Custom AT probing This property allows plugins to specify custom commands to check whether a port is AT or not. By default, the 'AT' command will be used if no custom one specified. This configuration is specified by the MM_PLUGIN_CUSTOM_AT_PROBE property in the MMPlugin object provided by the plugin. QCDM allowed This boolean property allows plugins to specify that they expect and support QCDM serial ports. This configuration is specified by the MM_PLUGIN_ALLOWED_QCDM property in the MMPlugin object provided by the plugin. Check Icera support This boolean property allows plugins to specify that they want to have the Icera support checks included in the probing sequence. They can afterwards get the result of the support check to decide whether they want to create a Icera-based modem object or not. This configuration is specified by the MM_PLUGIN_ICERA_PROBE property in the MMPlugin object provided by the plugin.
Post-probing filters Post-probing filters are required to identify whether a plugin can handle a given modem, in special cases where the information retrieved from udev is either not enough or wrong. This covers, for example, RS232 modems connected through a RS232 to USB converter, where udev-reported vendor ID is that of the converter, not the one of the modem. Allowed vendor strings Plugins can provide a list of vendor strings to be used as post-probing filters. If the vendor string reported by the device via AT commands is found in the list provided by the plugin, the plugin will report that it can handle this modem. This filter is specified by the MM_PLUGIN_ALLOWED_VENDOR_STRINGS property in the MMPlugin object provided by the plugin. Product strings Plugins can provide a list of pairs of vendor and product strings to be used as post-probing filters. If the vendor and product string pair reported by the device via AT commands is found in the 'allowed' list provided by the plugin, the plugin will report that it can handle this modem. This additional filter should be used when the plugin is expected to work only with a given specific product of a given vendor. If the vendor and product string pair reported by the device via AT commands is found in the 'forbidden list provided by the plugin, the plugin will report that it cannot handle this modem. This additional filter should be used when the plugin supports all devices of a given vendor, except for some specific ones. These filters are specified by the MM_PLUGIN_ALLOWED_PRODUCT_STRINGS and MM_PLUGIN_FORBIDDEN_PRODUCT_STRINGS properties in the MMPlugin object provided by the plugin. Icera support Plugins can specify that they only support Icera-based modems, or that they do not support any Icera-based modem. When either of this configurations is enabled, the Icera support checks will be included in the probing sequence, and the result of the check will help to determine whether the plugin supports the modem or not. This filter is specified by the MM_PLUGIN_ALLOWED_ICERA and MM_PLUGIN_FORBIDDEN_ICERA properties in the MMPlugin object provided by the plugin. Plugins which require post-probing filters will always be sorted last, and therefore they will be the last ones being checked for pre-probing filters. This is due to the fact that we need to assume that these plugins aren't able to determine port support just with pre-probing filters, as we want to avoid unnecessary probing sequences launched. Also note that the Generic plugin is anyway always the last one in the list.
Probing setup examples Probing setup for a plugin requiring udev-based vendor/product checks G_MODULE_EXPORT MMPlugin * mm_plugin_create (void) { static const gchar *subsystems[] = { "tty", NULL }; static const guint16 vendor_ids[] = { 0xabcd, 0 }; static const mm_uint16_pair product_ids[] = { { 0x1234, 0xffff } }; static const gchar *vendor_strings[] = { "vendor", NULL }; return MM_PLUGIN ( g_object_new (MM_TYPE_PLUGIN_IRIDIUM, MM_PLUGIN_NAME, "Example", /* Next items are pre-probing filters */ MM_PLUGIN_ALLOWED_SUBSYSTEMS, subsystems, MM_PLUGIN_ALLOWED_VENDOR_IDS, vendor_ids, MM_PLUGIN_ALLOWED_PRODUCT_IDS, product_ids, /* Next items are probing sequence setup */ MM_PLUGIN_ALLOWED_AT, TRUE, /* No post-probing filters */ NULL)); } Probing setup for a plugin requiring AT-probed vendor/product checks G_MODULE_EXPORT MMPlugin * mm_plugin_create (void) { static const gchar *subsystems[] = { "tty", NULL }; static const gchar *vendor_strings[] = { "vendor", NULL }; static const mm_str_pair product_strings[] = { "another-vendor", "product xyz" }; return MM_PLUGIN ( g_object_new (MM_TYPE_PLUGIN_IRIDIUM, MM_PLUGIN_NAME, "Example", /* Next items are pre-probing filters */ MM_PLUGIN_ALLOWED_SUBSYSTEMS, subsystems, /* Next items are probing sequence setup */ MM_PLUGIN_ALLOWED_AT, TRUE, /* Next items are post-probing filters */ MM_PLUGIN_VENDOR_STRINGS, vendor_strings, MM_PLUGIN_PRODUCT_STRINGS, product_strings, NULL)); } Probing setup for a plugin with custom initialization requirements static gboolean parse_custom_at (const gchar *response, const GError *error, GValue *result, GError **result_error) { if (error) { *result_error = g_error_copy (error); return FALSE; } /* Otherwise, done. And also report that it's an AT port. */ g_value_init (result, G_TYPE_BOOLEAN); g_value_set_boolean (result, TRUE); return TRUE; } static const MMPortProbeAtCommand custom_at_probe[] = { { "AT+SOMETHING", parse_custom_at }, { NULL } }; G_MODULE_EXPORT MMPlugin * mm_plugin_create (void) { static const gchar *subsystems[] = { "tty", NULL }; static const guint16 vendor_ids[] = { 0xabcd, 0 }; return MM_PLUGIN ( g_object_new (MM_TYPE_PLUGIN_EXAMPLE, MM_PLUGIN_NAME, "Example", /* Next items are pre-probing filters */ MM_PLUGIN_ALLOWED_SUBSYSTEMS, subsystems, MM_PLUGIN_ALLOWED_VENDOR_IDS, vendor_ids, /* Next items are probing sequence setup */ MM_PLUGIN_CUSTOM_AT_PROBE, custom_at_probe, MM_PLUGIN_ALLOWED_AT, TRUE, /* No post-probing filters */ NULL)); }
Modem object creation Once a port passes all probing filters of a given plugin, the plugin will grab the port. When the first port of a given device is grabbed, the plugin will create the required Modem object. While preparing to get the Modem object grab the specific port probed, udev-based port type hints can be used to specify AT port flags (e.g. if a port is to be considered primary, secondary or for PPP). Modem state machine Once all ports of a given modem have been probed and grabbed by a newly created Modem object, ModemManager will start the global state machine for the modem, as defined in the picture below.
ModemManager states
The state machine of a modem can be summarized in 5 main sequences: initialization, enabling, connection, disconnection and disabling.
Initialization The modem initialization sequence starts only when all ports have been probed and grabbed by a given plugin. This is done so that the proper AT port (that suggested to be Primary) is used as control port. The global initialization sequence is itself splitted into N per-interface initialization steps (being N the number of interfaces implemented by the modem object). The following list provides the steps required in the initialization sequence of a Broadband modem object. Modem interface initialization The Modem interface provides common actions and information available in the majority of the modems (including Broadband-specific items which won't be implemented by POTS modems). One of the key things done during the initialization of this interface is the checking of supported capabilities. Broadband modem objects are able to handle 3GPP-only, CDMA-only and mixed 3GPP+CDMA modems, but in order to properly handle the distinctions required in these, ModemManager first needs to know exactly which is the current set of capabilities. The other key step in this sequence involves checking the lock status of the modem and/or SIM . If the modem/SIM is found to be locked, the whole initialization sequence is halted and the modem is left in a locked state until unlocked by the user. Note, therefore, that modems that are locked will not expose additional feature-specific DBus interfaces until they get unlocked. It may be the case that some of the steps in the initialization of the Modem interface require the modem itself to be unlocked. If the modem is found locked during the first initialization attempt, as soon as it gets unlocked the initialization sequence will be re-executed. 3GPP interface initialization The 3GPP interface provides common actions and setup for modems which provide 3GPP capabilities. Therefore, this interface initialization sequence will only be run in 3GPP-enabled modems. CDMA interface initialization The CDMA interface provides common actions and setup for modems which provide CDMA capabilities. Therefore, this interface initialization sequence will only be run in CDMA-enabled modems. Additional feature-specific interface initializations Modems with additional features will export feature-specific interfaces, such as the Location or the Messaging ones. These interfaces also have their own initialization sequences, where the first step in the sequence is always the check of whether the given modem supports the given feature. In other words, modems will only end up exporting the interfaces for the features they support.
Enabling Modem enabling is the user-requested sequence with the sole purpose of bringing the modem to a state where it can get connected. As with the initialization sequence, the global enabling sequence is itself splitted into N per-interface enabling steps (being N the number of interfaces exported by the modem). Those interfaces implemented by the object but not supported by the modem will not be enabled. Modem interface enabling The sequence to enable the Modem interface takes care of different important steps, such as powering up the radio interface or configuring the best charset to use. 3GPP interface enabling Modems with 3GPP capabilities will enable the 3GPP interface as part of the global enabling sequence. This sequence involves setting up the automatic registration of the device in the network, as well as configuring 3GPP specific indicators and unsolicited message handlers. CDMA interface enabling Modems with CDMA capabilities will enable the CDMA interface as part of the global enabling sequence. This sequence involves setting up the periodic checks of registration in the CDMA network. Additional feature-specific interface enablings Each feature-specific interface will have its own enabling sequence, with operations which are directly related to the purpose of the interface. For example, enabling the Location interface will involve setting up the initial location information; and enabling the Messaging interface will involve loading the initial list of SMS available in the SIM or Modem.
Connection & disconnection Connecting the Modem is done through the Bearer objects. Once such an object is created, the user can request to get the given bearer connected. Broadband Modems will usually create Broadband Bearers. This kind of bearers can run either the CDMA connection sequence (if the modem has CDMA capabilities) or the 3GPP connection sequence (if the modem has 3GPP capabilities). For the special case of mixed 3GPP+CDMA modems, it is assumed that the plugin implementation needs to decide how the connection gets done. By default, anyway, the 3GPP sequence is used in this case. Modems which are both LTE (3GPP) and CDMA can hand over from LTE to CDMA transparently and automatically when no LTE network is available, even keeping the same IP address. When this happens, the modem will get notified about the access technology change, and ModemManager will update that information.
Disabling Users can disable the modems, which will bring them to a state where they are in low power mode (e.g. RF switched off) and not registered in any network. As with the initialization or enabling sequences, the global disabling sequence is itself splitted into N per-interface disabling steps (being N the number of interfaces exported by the modem). Those interfaces implemented by the object but not supported by the modem will not be disabled. The global disabling sequence will go on disabling the interfaces one by one, but starting with the interface which was last enabled during the enabling sequence, and backwards. This ensures that the Modem interface gets disabled last. Additional feature-specific interface disablings Each feature-specific interface will have its own disabling sequence, with operations which are directly related to the purpose of the interface. For example, disabling the Location interface will involve shutting down the location gathering; and disabling the Messaging interface will involve unexporting all SMS objects from DBus. CDMA interface disabling Modems with CDMA capabilities will disable the CDMA interface as part of the global disabling sequence. This sequence involves cancelling the periodic checks of registration in the CDMA network. 3GPP interface disabling Modems with 3GPP capabilities will disable the 3GPP interface as part of the global disabling sequence. This sequence involves, among other things, cleaning up 3GPP specific indicators and unsolicited message handlers. Modem interface disabling The sequence to disable the Modem interface takes care of different important steps, such as powering down the radio interface.
Plugin-specific Modems ModemManager plugins exist in order to handle all non-standard vendor-specific behaviour that needs to get supported. Plugins will provide their own Modem object implementations, usually subclassing the generic MMBroadbandModem object. As previously explained, this object implements every interface that may be exported by the Modem object in DBus; and then, depending on the per-interface support checks, the interface will end up being really exported or not. Each interface defines every step to be run during the initialization, enabling or disabling sequences. Then, the object implementing the interface may or may not provide the implementation of such step. By default, the generic MMBroadbandModem object implements already most of the steps in the interfaces providing common features:
Modem interface initialization sequence
Vendor-specific subclasses of MMBroadbandModem are then able to either provide their own implementation of a given step (in the image below, a custom implementation for capabilities checking); or even completely disable the step if they know that there is no way to run it (in the image below, revision string loading is removed).
Modem interface initialization sequence subclassed
These subclass-able steps are all implemented as standard GIO asynchronous functions, so subclassing a step involves implementing both the async method which receives the input arguments to the action and the corresponding _finish() method which provides the results of the action once the operation is ready. It is worth noting that these steps and the asynchronous methods implementing them don't assume that an AT port will be used to implement the real action. This means that any other kind of port may be really used (e.g. QCDM or QMI) in the implementation, or even that a static reply can be returned (e.g. Iridium modems will always report "Iridium" as current OperatorName).
ModemManager-1.23.4-dev/docs/reference/api/ModemManager-sections.txt000066400000000000000000000150361456466623000253130ustar00rootroot00000000000000
mm-version Version checks MM_MAJOR_VERSION MM_MINOR_VERSION MM_MICRO_VERSION MM_CHECK_VERSION
mm-enums Flags and Enumerations MMBearerType MMBearerIpFamily MMBearerIpMethod MMBearerAllowedAuth MMBearerMultiplexSupport MMBearerApnType MMBearerAccessTypePreference MMBearerRoamingAllowance MMBearerProfileSource MMCallDirection MMCallState MMCallStateReason MMFirmwareImageType MMModem3gppFacility MMModem3gppNetworkAvailability MMModem3gppSubscriptionState MMModem3gppRegistrationState MMModem3gppUssdSessionState MMModem3gppEpsUeModeOperation MMModem3gppPacketServiceState MMModem3gppMicoMode MMModem3gppDrxCycle MMModemAccessTechnology MMModemBand MMModemCapability MMModemCdmaActivationState MMModemCdmaRegistrationState MMModemCdmaRmProtocol MMModemContactsStorage MMModemLocationSource MMModemLocationAssistanceDataType MMModemLock MMModemMode MMModemState MMModemStateFailedReason MMModemStateChangeReason MMModemPowerState MMModemPortType MMModemFirmwareUpdateMethod MMOmaFeature MMOmaSessionState MMOmaSessionStateFailedReason MMOmaSessionType MMSmsPduType MMSmsState MMSmsDeliveryState MMSmsStorage MMSmsValidityType MMSmsCdmaTeleserviceId MMSmsCdmaServiceCategory MMSimType MMSimEsimStatus MMSimRemovability MMCellType MMServingCellType
mm-errors Errors MM_CORE_ERROR_DBUS_PREFIX MM_MOBILE_EQUIPMENT_ERROR_DBUS_PREFIX MM_CONNECTION_ERROR_DBUS_PREFIX MM_SERIAL_ERROR_DBUS_PREFIX MM_MESSAGE_ERROR_DBUS_PREFIX MM_CDMA_ACTIVATION_ERROR_DBUS_PREFIX MM_CARRIER_LOCK_ERROR_DBUS_PREFIX MMCoreError MMMobileEquipmentError MMConnectionError MMSerialError MMMessageError MMCdmaActivationError MMCarrierLockError
mm-compat MM_MODEM_BAND_U2100 MM_MODEM_BAND_U1900 MM_MODEM_BAND_U1800 MM_MODEM_BAND_U17IV MM_MODEM_BAND_U850 MM_MODEM_BAND_U800 MM_MODEM_BAND_U2600 MM_MODEM_BAND_U900 MM_MODEM_BAND_U17IX MM_MODEM_BAND_EUTRAN_I MM_MODEM_BAND_EUTRAN_II MM_MODEM_BAND_EUTRAN_III MM_MODEM_BAND_EUTRAN_IV MM_MODEM_BAND_EUTRAN_V MM_MODEM_BAND_EUTRAN_VI MM_MODEM_BAND_EUTRAN_VII MM_MODEM_BAND_EUTRAN_VIII MM_MODEM_BAND_EUTRAN_IX MM_MODEM_BAND_EUTRAN_X MM_MODEM_BAND_EUTRAN_XI MM_MODEM_BAND_EUTRAN_XII MM_MODEM_BAND_EUTRAN_XIII MM_MODEM_BAND_EUTRAN_XIV MM_MODEM_BAND_EUTRAN_XVII MM_MODEM_BAND_EUTRAN_XVIII MM_MODEM_BAND_EUTRAN_XIX MM_MODEM_BAND_EUTRAN_XX MM_MODEM_BAND_EUTRAN_XXI MM_MODEM_BAND_EUTRAN_XXII MM_MODEM_BAND_EUTRAN_XXIII MM_MODEM_BAND_EUTRAN_XXIV MM_MODEM_BAND_EUTRAN_XXV MM_MODEM_BAND_EUTRAN_XXVI MM_MODEM_BAND_EUTRAN_XXXIII MM_MODEM_BAND_EUTRAN_XXXIV MM_MODEM_BAND_EUTRAN_XXXV MM_MODEM_BAND_EUTRAN_XXXVI MM_MODEM_BAND_EUTRAN_XXXVII MM_MODEM_BAND_EUTRAN_XXXVIII MM_MODEM_BAND_EUTRAN_XXXIX MM_MODEM_BAND_EUTRAN_XL MM_MODEM_BAND_EUTRAN_XLI MM_MODEM_BAND_EUTRAN_XLII MM_MODEM_BAND_EUTRAN_XLIII MM_MODEM_BAND_EUTRAN_XLIV MM_MODEM_BAND_CDMA_BC0_CELLULAR_800 MM_MODEM_BAND_CDMA_BC1_PCS_1900 MM_MODEM_BAND_CDMA_BC2_TACS MM_MODEM_BAND_CDMA_BC3_JTACS MM_MODEM_BAND_CDMA_BC4_KOREAN_PCS MM_MODEM_BAND_CDMA_BC5_NMT450 MM_MODEM_BAND_CDMA_BC6_IMT2000 MM_MODEM_BAND_CDMA_BC7_CELLULAR_700 MM_MODEM_BAND_CDMA_BC8_1800 MM_MODEM_BAND_CDMA_BC9_900 MM_MODEM_BAND_CDMA_BC10_SECONDARY_800 MM_MODEM_BAND_CDMA_BC11_PAMR_400 MM_MODEM_BAND_CDMA_BC12_PAMR_800 MM_MODEM_BAND_CDMA_BC13_IMT2000_2500 MM_MODEM_BAND_CDMA_BC14_PCS2_1900 MM_MODEM_BAND_CDMA_BC15_AWS MM_MODEM_BAND_CDMA_BC16_US_2500 MM_MODEM_BAND_CDMA_BC17_US_FLO_2500 MM_MODEM_BAND_CDMA_BC18_US_PS_700 MM_MODEM_BAND_CDMA_BC19_US_LOWER_700 MM_MODEM_LOCATION_SOURCE_AGPS MM_MODEM_CAPABILITY_LTE_ADVANCED MM_MOBILE_EQUIPMENT_ERROR_GPRS_ACTIVATION_REJECTED_BY_GGSN_OR_GW MM_MOBILE_EQUIPMENT_ERROR_GPRS_ACTIVATION_REJECTED_UNSPECIFIED MM_MOBILE_EQUIPMENT_ERROR_GPRS_AND_NON_GPRS_SERVICES_NOT_ALLOWED MM_MOBILE_EQUIPMENT_ERROR_GPRS_CONDITIONAL_IE_ERROR MM_MOBILE_EQUIPMENT_ERROR_GPRS_CONGESTION MM_MOBILE_EQUIPMENT_ERROR_GPRS_FEATURE_NOT_SUPPORTED MM_MOBILE_EQUIPMENT_ERROR_GPRS_IE_NOT_IMPLEMENTED MM_MOBILE_EQUIPMENT_ERROR_GPRS_ILLEGAL_ME MM_MOBILE_EQUIPMENT_ERROR_GPRS_ILLEGAL_MS MM_MOBILE_EQUIPMENT_ERROR_GPRS_IMSI_UNKNOWN_IN_HLR MM_MOBILE_EQUIPMENT_ERROR_GPRS_IMSI_UNKNOWN_IN_VLR MM_MOBILE_EQUIPMENT_ERROR_GPRS_INSUFFICIENT_RESOURCES MM_MOBILE_EQUIPMENT_ERROR_GPRS_INVALID_MOBILE_CLASS MM_MOBILE_EQUIPMENT_ERROR_GPRS_LAST_PDN_DISCONNECTION_NOT_ALLOWED MM_MOBILE_EQUIPMENT_ERROR_GPRS_LAST_PDN_DISCONNECTION_NOT_ALLOWED_LEGACY MM_MOBILE_EQUIPMENT_ERROR_GPRS_LOCATION_NOT_ALLOWED MM_MOBILE_EQUIPMENT_ERROR_GPRS_MANDATORY_IE_ERROR MM_MOBILE_EQUIPMENT_ERROR_GPRS_MAXIMUM_NUMBER_OF_PDP_CONTEXTS_REACHED MM_MOBILE_EQUIPMENT_ERROR_GPRS_MISSING_OR_UNKNOWN_APN MM_MOBILE_EQUIPMENT_ERROR_GPRS_NETWORK_FAILURE MM_MOBILE_EQUIPMENT_ERROR_GPRS_NOT_AUTHORIZED_FOR_CSG MM_MOBILE_EQUIPMENT_ERROR_GPRS_NO_CELLS_IN_LOCATION_AREA MM_MOBILE_EQUIPMENT_ERROR_GPRS_OPERATOR_DETERMINED_BARRING MM_MOBILE_EQUIPMENT_ERROR_GPRS_PDP_AUTH_FAILURE MM_MOBILE_EQUIPMENT_ERROR_GPRS_PDP_CONTEXT_WITHOUT_TFT_ALREADY_ACTIVATED MM_MOBILE_EQUIPMENT_ERROR_GPRS_PLMN_NOT_ALLOWED MM_MOBILE_EQUIPMENT_ERROR_GPRS_REQUESTED_APN_NOT_SUPPORTED MM_MOBILE_EQUIPMENT_ERROR_GPRS_REQUEST_REJECTED_BCM_VIOLATION MM_MOBILE_EQUIPMENT_ERROR_GPRS_ROAMING_NOT_ALLOWED MM_MOBILE_EQUIPMENT_ERROR_GPRS_SEMANTICALLY_INCORRECT_MESSAGE MM_MOBILE_EQUIPMENT_ERROR_GPRS_SEMANTIC_ERRORS_IN_PACKET_FILTER MM_MOBILE_EQUIPMENT_ERROR_GPRS_SEMANTIC_ERROR_IN_TFT_OPERATION MM_MOBILE_EQUIPMENT_ERROR_GPRS_SERVICE_NOT_ALLOWED MM_MOBILE_EQUIPMENT_ERROR_GPRS_SERVICE_OPTION_NOT_SUBSCRIBED MM_MOBILE_EQUIPMENT_ERROR_GPRS_SERVICE_OPTION_NOT_SUPPORTED MM_MOBILE_EQUIPMENT_ERROR_GPRS_SERVICE_OPTION_OUT_OF_ORDER MM_MOBILE_EQUIPMENT_ERROR_GPRS_SYNTACTICAL_ERROR_IN_PACKET_FILTER MM_MOBILE_EQUIPMENT_ERROR_GPRS_SYNTACTICAL_ERROR_IN_TFT_OPERATION MM_MOBILE_EQUIPMENT_ERROR_GPRS_UNKNOWN_PDP_ADDRESS_OR_TYPE MM_MOBILE_EQUIPMENT_ERROR_GPRS_UNKNOWN_PDP_CONTEXT MM_MOBILE_EQUIPMENT_ERROR_GPRS_UNSPECIFIED_PROTOCOL_ERROR MM_MOBILE_EQUIPMENT_ERROR_GPRS_USER_AUTHENTICATION_FAILED MMModemBandDeprecated MMModemLocationSourceDeprecated MMModemCapabilityDeprecated MMMobileEquipmentErrorDeprecated MM_DEPRECATED
mm-tags Common udev tags ID_MM_CANDIDATE ID_MM_PHYSDEV_UID ID_MM_DEVICE_PROCESS ID_MM_DEVICE_IGNORE ID_MM_PORT_IGNORE ID_MM_PORT_TYPE_AT_PPP ID_MM_PORT_TYPE_AT_PRIMARY ID_MM_PORT_TYPE_AT_SECONDARY ID_MM_PORT_TYPE_AT_GPS_CONTROL ID_MM_PORT_TYPE_GPS ID_MM_PORT_TYPE_QCDM ID_MM_PORT_TYPE_AUDIO ID_MM_PORT_TYPE_QMI ID_MM_PORT_TYPE_MBIM ID_MM_TTY_BAUDRATE ID_MM_TTY_FLOW_CONTROL ID_MM_REQUIRED ID_MM_MAX_MULTIPLEXED_LINKS ID_MM_TTY_BLACKLIST ID_MM_TTY_MANUAL_SCAN_ONLY
ModemManager-1.23.4-dev/docs/reference/api/ModemManager.types000066400000000000000000000000001456466623000237740ustar00rootroot00000000000000ModemManager-1.23.4-dev/docs/reference/api/meson.build000066400000000000000000000046561456466623000225410ustar00rootroot00000000000000# SPDX-License-Identifier: GPL-2.0-or-later # Copyright (C) 2021 Iñigo Martinez doc_module = mm_name private_headers = [ 'ModemManager.h', 'ModemManager-names.h', ] mm_doc_path = mm_prefix / gnome.gtkdoc_html_dir(doc_module) version_xml = configure_file( input: 'version.xml.in', output: '@BASENAME@', configuration: version_conf, ) expand_content_files = [ 'ModemManager-dbus-reference.xml', 'ModemManager-migration-reference.xml', 'ModemManager-overview.xml', # FIXME: workaround because only strings can be included and not custom targets (gen_docs) generated_build_dir / 'mm-gdbus-doc-org.freedesktop.ModemManager1.Bearer.xml', generated_build_dir / 'mm-gdbus-doc-org.freedesktop.ModemManager1.Modem.Firmware.xml', generated_build_dir / 'mm-gdbus-doc-org.freedesktop.ModemManager1.Modem.Location.xml', generated_build_dir / 'mm-gdbus-doc-org.freedesktop.ModemManager1.Modem.Messaging.xml', generated_build_dir / 'mm-gdbus-doc-org.freedesktop.ModemManager1.Modem.Modem3gpp.ProfileManager.xml', generated_build_dir / 'mm-gdbus-doc-org.freedesktop.ModemManager1.Modem.Modem3gpp.Ussd.xml', generated_build_dir / 'mm-gdbus-doc-org.freedesktop.ModemManager1.Modem.Modem3gpp.xml', generated_build_dir / 'mm-gdbus-doc-org.freedesktop.ModemManager1.Modem.ModemCdma.xml', generated_build_dir / 'mm-gdbus-doc-org.freedesktop.ModemManager1.Modem.Oma.xml', generated_build_dir / 'mm-gdbus-doc-org.freedesktop.ModemManager1.Modem.Signal.xml', generated_build_dir / 'mm-gdbus-doc-org.freedesktop.ModemManager1.Modem.Simple.xml', generated_build_dir / 'mm-gdbus-doc-org.freedesktop.ModemManager1.Modem.Time.xml', generated_build_dir / 'mm-gdbus-doc-org.freedesktop.ModemManager1.Modem.xml', generated_build_dir / 'mm-gdbus-doc-org.freedesktop.ModemManager1.Sim.xml', generated_build_dir / 'mm-gdbus-doc-org.freedesktop.ModemManager1.Sms.xml', generated_build_dir / 'mm-gdbus-doc-org.freedesktop.ModemManager1.xml', ] gnome.gtkdoc( doc_module, main_xml: doc_module + '-docs.xml', src_dir: include_inc, ignore_headers: private_headers, include_directories: top_inc, gobject_typesfile: doc_module + '.types', dependencies: glib_deps, namespace: 'mm', scan_args: '--deprecated-guards="MM_DISABLE_DEPRECATED"', fixxref_args: '--html-dir=' + mm_doc_path, html_assets: logos_pngs + diagrams_pngs, content_files: version_xml, expand_content_files: expand_content_files, install: true, ) ModemManager-1.23.4-dev/docs/reference/api/version.xml.in000066400000000000000000000000121456466623000231710ustar00rootroot00000000000000@VERSION@ ModemManager-1.23.4-dev/docs/reference/libmm-glib/000077500000000000000000000000001456466623000216265ustar00rootroot00000000000000ModemManager-1.23.4-dev/docs/reference/libmm-glib/libmm-glib-docs.xml000066400000000000000000000307761456466623000253260ustar00rootroot00000000000000 ]> libmm-glib Reference Manual For libmm-glib version &version; The latest version of this documentation can be found on-line at https://www.freedesktop.org/software/ModemManager/doc/latest/libmm-glib/. Aleksander Morgado
aleksander@aleksander.es
2011 2012 2013 2014 2015 2016 2017 2018 2019 2020 2021 2022 2023 The ModemManager Authors Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.3 or any later version published by the Free Software Foundation with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts. You may obtain a copy of the GNU Free Documentation License from the Free Software Foundation by visiting their Web site or by writing to:
The Free Software Foundation, Inc. 51 Franklin Street, Suite 500 Boston, MA 02110-1335 USA
High level API Common enums and flags helpers The Manager object The Modem object
Generic interfaces
Simple interface support
USSD support
Profile management support
Location support
Messaging support
Time support
Firmware support
Extended signal information
OMA support
Voice support
SAR support
The Bearer object The SIM object The SMS object The Call object
Low level API Compatibility with older versions Object Hierarchy Index Index of deprecated symbols Index of new symbols in 1.0 Index of new symbols in 1.2 Index of new symbols in 1.4 Index of new symbols in 1.6 Index of new symbols in 1.8 Index of new symbols in 1.10 Index of new symbols in 1.12 Index of new symbols in 1.14 Index of new symbols in 1.16 Index of new symbols in 1.18 Index of new symbols in 1.20
ModemManager-1.23.4-dev/docs/reference/libmm-glib/libmm-glib-sections.txt000066400000000000000000003527431456466623000262450ustar00rootroot00000000000000
libmm-glib
mm-manager MMManager MMManager mm_manager_peek_proxy mm_manager_get_proxy mm_manager_new mm_manager_new_finish mm_manager_new_sync mm_manager_get_version mm_manager_scan_devices mm_manager_scan_devices_finish mm_manager_scan_devices_sync mm_manager_inhibit_device mm_manager_inhibit_device_finish mm_manager_inhibit_device_sync mm_manager_uninhibit_device mm_manager_uninhibit_device_finish mm_manager_uninhibit_device_sync mm_manager_set_logging mm_manager_set_logging_finish mm_manager_set_logging_sync mm_manager_report_kernel_event mm_manager_report_kernel_event_finish mm_manager_report_kernel_event_sync MMManagerClass MMManagerPrivate MM_IS_MANAGER MM_IS_MANAGER_CLASS MM_MANAGER MM_MANAGER_CLASS MM_MANAGER_GET_CLASS MM_TYPE_MANAGER mm_manager_get_type
mm-kernel-event-properties MMKernelEventProperties MMKernelEventProperties mm_kernel_event_properties_new mm_kernel_event_properties_get_action mm_kernel_event_properties_set_action mm_kernel_event_properties_get_name mm_kernel_event_properties_set_name mm_kernel_event_properties_get_subsystem mm_kernel_event_properties_set_subsystem mm_kernel_event_properties_get_uid mm_kernel_event_properties_set_uid mm_kernel_event_properties_new_from_string mm_kernel_event_properties_new_from_dictionary mm_kernel_event_properties_dup mm_kernel_event_properties_get_dictionary MMKernelEventPropertiesClass MMKernelEventPropertiesPrivate MM_KERNEL_EVENT_PROPERTIES MM_KERNEL_EVENT_PROPERTIES_CLASS MM_KERNEL_EVENT_PROPERTIES_GET_CLASS MM_IS_KERNEL_EVENT_PROPERTIES MM_IS_KERNEL_EVENT_PROPERTIES_CLASS MM_TYPE_KERNEL_EVENT_PROPERTIES mm_kernel_event_properties_get_type
mm-object MMObject MMObject mm_object_get_path mm_object_dup_path mm_object_peek_modem mm_object_get_modem mm_object_peek_modem_3gpp mm_object_get_modem_3gpp mm_object_peek_modem_3gpp_profile_manager mm_object_get_modem_3gpp_profile_manager mm_object_peek_modem_3gpp_ussd mm_object_get_modem_3gpp_ussd mm_object_peek_modem_cdma mm_object_get_modem_cdma mm_object_peek_modem_location mm_object_get_modem_location mm_object_peek_modem_messaging mm_object_get_modem_messaging mm_object_peek_modem_time mm_object_get_modem_time mm_object_peek_modem_firmware mm_object_get_modem_firmware mm_object_peek_modem_oma mm_object_get_modem_oma mm_object_peek_modem_simple mm_object_get_modem_simple mm_object_peek_modem_signal mm_object_get_modem_signal mm_object_peek_modem_voice mm_object_get_modem_voice mm_object_peek_modem_sar mm_object_get_modem_sar MMObjectClass MM_IS_OBJECT MM_IS_OBJECT_CLASS MM_OBJECT MM_OBJECT_CLASS MM_OBJECT_GET_CLASS MM_TYPE_OBJECT mm_object_get_type
mm-modem MMModem MMModem MMModemModeCombination MMModemPortInfo mm_modem_get_path mm_modem_dup_path mm_modem_get_state mm_modem_get_state_failed_reason mm_modem_get_power_state mm_modem_peek_supported_capabilities mm_modem_get_supported_capabilities mm_modem_get_current_capabilities mm_modem_get_manufacturer mm_modem_dup_manufacturer mm_modem_get_model mm_modem_dup_model mm_modem_get_revision mm_modem_dup_revision mm_modem_get_carrier_configuration mm_modem_dup_carrier_configuration mm_modem_get_carrier_configuration_revision mm_modem_dup_carrier_configuration_revision mm_modem_get_hardware_revision mm_modem_dup_hardware_revision mm_modem_get_drivers mm_modem_dup_drivers mm_modem_get_plugin mm_modem_dup_plugin mm_modem_get_primary_port mm_modem_dup_primary_port mm_modem_peek_ports mm_modem_get_ports mm_modem_get_device mm_modem_dup_device mm_modem_get_physdev mm_modem_dup_physdev mm_modem_get_equipment_identifier mm_modem_dup_equipment_identifier mm_modem_get_device_identifier mm_modem_dup_device_identifier mm_modem_get_unlock_required mm_modem_peek_unlock_retries mm_modem_get_unlock_retries mm_modem_get_max_bearers mm_modem_get_max_active_bearers mm_modem_get_max_active_multiplexed_bearers mm_modem_get_bearer_paths mm_modem_dup_bearer_paths mm_modem_get_own_numbers mm_modem_dup_own_numbers mm_modem_peek_supported_modes mm_modem_get_supported_modes mm_modem_get_current_modes mm_modem_peek_supported_bands mm_modem_get_supported_bands mm_modem_peek_current_bands mm_modem_get_current_bands mm_modem_get_supported_ip_families mm_modem_get_signal_quality mm_modem_get_access_technologies mm_modem_get_sim_path mm_modem_dup_sim_path mm_modem_get_sim mm_modem_get_sim_finish mm_modem_get_sim_sync mm_modem_get_sim_slot_paths mm_modem_dup_sim_slot_paths mm_modem_get_primary_sim_slot mm_modem_list_sim_slots mm_modem_list_sim_slots_finish mm_modem_list_sim_slots_sync mm_modem_set_primary_sim_slot mm_modem_set_primary_sim_slot_finish mm_modem_set_primary_sim_slot_sync mm_modem_enable mm_modem_enable_finish mm_modem_enable_sync mm_modem_disable mm_modem_disable_finish mm_modem_disable_sync mm_modem_set_power_state mm_modem_set_power_state_finish mm_modem_set_power_state_sync mm_modem_set_current_modes mm_modem_set_current_modes_finish mm_modem_set_current_modes_sync mm_modem_set_current_bands mm_modem_set_current_bands_finish mm_modem_set_current_bands_sync mm_modem_set_current_capabilities mm_modem_set_current_capabilities_finish mm_modem_set_current_capabilities_sync mm_modem_reset mm_modem_reset_finish mm_modem_reset_sync mm_modem_factory_reset mm_modem_factory_reset_finish mm_modem_factory_reset_sync mm_modem_list_bearers mm_modem_list_bearers_finish mm_modem_list_bearers_sync mm_modem_create_bearer mm_modem_create_bearer_finish mm_modem_create_bearer_sync mm_modem_delete_bearer mm_modem_delete_bearer_finish mm_modem_delete_bearer_sync mm_modem_get_cell_info mm_modem_get_cell_info_finish mm_modem_get_cell_info_sync mm_modem_command mm_modem_command_finish mm_modem_command_sync mm_modem_port_info_array_free MMModemClass MMModemPrivate MM_IS_MODEM MM_IS_MODEM_CLASS MM_MODEM MM_MODEM_CLASS MM_MODEM_GET_CLASS MM_TYPE_MODEM mm_modem_get_type
mm-unlock-retries MMUnlockRetries MMUnlockRetries MM_UNLOCK_RETRIES_UNKNOWN mm_unlock_retries_get MMUnlockRetriesForeachCb mm_unlock_retries_foreach mm_unlock_retries_build_string mm_unlock_retries_cmp mm_unlock_retries_new mm_unlock_retries_new_from_dictionary mm_unlock_retries_get_dictionary mm_unlock_retries_set mm_unlock_retries_unset MMUnlockRetriesClass MMUnlockRetriesPrivate MM_IS_UNLOCK_RETRIES MM_IS_UNLOCK_RETRIES_CLASS MM_TYPE_UNLOCK_RETRIES MM_UNLOCK_RETRIES MM_UNLOCK_RETRIES_CLASS MM_UNLOCK_RETRIES_GET_CLASS mm_unlock_retries_get_type
mm-cell-info MMCellInfo MMCellInfo mm_cell_info_get_cell_type mm_cell_info_get_serving mm_cell_info_set_cell_type mm_cell_info_set_serving mm_cell_info_build_string mm_cell_info_get_dictionary mm_cell_info_new_from_dictionary MM_CELL_INFO_GET_DICTIONARY_INSERT MM_CELL_INFO_BUILD_STRING_APPEND MM_CELL_INFO_NEW_FROM_DICTIONARY_NUM_SET MM_CELL_INFO_NEW_FROM_DICTIONARY_STRING_SET MMCellInfoClass MMCellInfoPrivate MM_IS_CELL_INFO MM_IS_CELL_INFO_CLASS MM_TYPE_CELL_INFO MM_CELL_INFO MM_CELL_INFO_CLASS MM_CELL_INFO_GET_CLASS mm_cell_info_get_type
mm-cell-info-cdma MMCellInfoCdma MMCellInfoCdma mm_cell_info_cdma_get_nid mm_cell_info_cdma_get_sid mm_cell_info_cdma_get_base_station_id mm_cell_info_cdma_get_ref_pn mm_cell_info_cdma_get_pilot_strength mm_cell_info_cdma_new_from_dictionary mm_cell_info_cdma_set_nid mm_cell_info_cdma_set_sid mm_cell_info_cdma_set_base_station_id mm_cell_info_cdma_set_ref_pn mm_cell_info_cdma_set_pilot_strength MMCellInfoCdmaClass MMCellInfoCdmaPrivate MM_IS_CELL_INFO_CDMA MM_IS_CELL_INFO_CDMA_CLASS MM_TYPE_CELL_INFO_CDMA MM_CELL_INFO_CDMA MM_CELL_INFO_CDMA_CLASS MM_CELL_INFO_CDMA_GET_CLASS mm_cell_info_cdma_get_type
mm-cell-info-gsm MMCellInfoGsm MMCellInfoGsm mm_cell_info_gsm_get_operator_id mm_cell_info_gsm_get_lac mm_cell_info_gsm_get_ci mm_cell_info_gsm_get_timing_advance mm_cell_info_gsm_get_arfcn mm_cell_info_gsm_get_base_station_id mm_cell_info_gsm_get_rx_level mm_cell_info_gsm_new_from_dictionary mm_cell_info_gsm_set_operator_id mm_cell_info_gsm_set_lac mm_cell_info_gsm_set_ci mm_cell_info_gsm_set_timing_advance mm_cell_info_gsm_set_arfcn mm_cell_info_gsm_set_base_station_id mm_cell_info_gsm_set_rx_level MMCellInfoGsmClass MMCellInfoGsmPrivate MM_IS_CELL_INFO_GSM MM_IS_CELL_INFO_GSM_CLASS MM_TYPE_CELL_INFO_GSM MM_CELL_INFO_GSM MM_CELL_INFO_GSM_CLASS MM_CELL_INFO_GSM_GET_CLASS mm_cell_info_gsm_get_type
mm-cell-info-umts MMCellInfoUmts MMCellInfoUmts mm_cell_info_umts_get_operator_id mm_cell_info_umts_get_lac mm_cell_info_umts_get_ci mm_cell_info_umts_get_frequency_fdd_ul mm_cell_info_umts_get_frequency_fdd_dl mm_cell_info_umts_get_frequency_tdd mm_cell_info_umts_get_uarfcn mm_cell_info_umts_get_psc mm_cell_info_umts_get_rscp mm_cell_info_umts_get_ecio mm_cell_info_umts_get_path_loss mm_cell_info_umts_new_from_dictionary mm_cell_info_umts_set_operator_id mm_cell_info_umts_set_lac mm_cell_info_umts_set_ci mm_cell_info_umts_set_frequency_fdd_ul mm_cell_info_umts_set_frequency_fdd_dl mm_cell_info_umts_set_frequency_tdd mm_cell_info_umts_set_uarfcn mm_cell_info_umts_set_psc mm_cell_info_umts_set_rscp mm_cell_info_umts_set_ecio mm_cell_info_umts_set_path_loss MMCellInfoUmtsClass MMCellInfoUmtsPrivate MM_IS_CELL_INFO_UMTS MM_IS_CELL_INFO_UMTS_CLASS MM_TYPE_CELL_INFO_UMTS MM_CELL_INFO_UMTS MM_CELL_INFO_UMTS_CLASS MM_CELL_INFO_UMTS_GET_CLASS mm_cell_info_umts_get_type
mm-cell-info-tdscdma MMCellInfoTdscdma MMCellInfoTdscdma mm_cell_info_tdscdma_get_operator_id mm_cell_info_tdscdma_get_lac mm_cell_info_tdscdma_get_ci mm_cell_info_tdscdma_get_uarfcn mm_cell_info_tdscdma_get_cell_parameter_id mm_cell_info_tdscdma_get_timing_advance mm_cell_info_tdscdma_get_rscp mm_cell_info_tdscdma_get_path_loss mm_cell_info_tdscdma_new_from_dictionary mm_cell_info_tdscdma_set_operator_id mm_cell_info_tdscdma_set_lac mm_cell_info_tdscdma_set_ci mm_cell_info_tdscdma_set_uarfcn mm_cell_info_tdscdma_set_cell_parameter_id mm_cell_info_tdscdma_set_timing_advance mm_cell_info_tdscdma_set_rscp mm_cell_info_tdscdma_set_path_loss MMCellInfoTdscdmaClass MMCellInfoTdscdmaPrivate MM_IS_CELL_INFO_TDSCDMA MM_IS_CELL_INFO_TDSCDMA_CLASS MM_TYPE_CELL_INFO_TDSCDMA MM_CELL_INFO_TDSCDMA MM_CELL_INFO_TDSCDMA_CLASS MM_CELL_INFO_TDSCDMA_GET_CLASS mm_cell_info_tdscdma_get_type
mm-cell-info-lte MMCellInfoLte MMCellInfoLte mm_cell_info_lte_get_operator_id mm_cell_info_lte_get_tac mm_cell_info_lte_get_ci mm_cell_info_lte_get_physical_ci mm_cell_info_lte_get_earfcn mm_cell_info_lte_get_rsrp mm_cell_info_lte_get_rsrq mm_cell_info_lte_get_timing_advance mm_cell_info_lte_new_from_dictionary mm_cell_info_lte_set_operator_id mm_cell_info_lte_set_tac mm_cell_info_lte_set_ci mm_cell_info_lte_set_physical_ci mm_cell_info_lte_set_earfcn mm_cell_info_lte_set_rsrp mm_cell_info_lte_set_rsrq mm_cell_info_lte_set_timing_advance MMCellInfoLteClass MMCellInfoLtePrivate MM_IS_CELL_INFO_LTE MM_IS_CELL_INFO_LTE_CLASS MM_TYPE_CELL_INFO_LTE MM_CELL_INFO_LTE MM_CELL_INFO_LTE_CLASS MM_CELL_INFO_LTE_GET_CLASS mm_cell_info_lte_get_type
mm-cell-info-nr5g MMCellInfoNr5g MMCellInfoNr5g mm_cell_info_nr5g_get_operator_id mm_cell_info_nr5g_get_tac mm_cell_info_nr5g_get_ci mm_cell_info_nr5g_get_physical_ci mm_cell_info_nr5g_get_nrarfcn mm_cell_info_nr5g_get_rsrp mm_cell_info_nr5g_get_rsrq mm_cell_info_nr5g_get_sinr mm_cell_info_nr5g_get_timing_advance mm_cell_info_nr5g_new_from_dictionary mm_cell_info_nr5g_set_operator_id mm_cell_info_nr5g_set_tac mm_cell_info_nr5g_set_ci mm_cell_info_nr5g_set_physical_ci mm_cell_info_nr5g_set_nrarfcn mm_cell_info_nr5g_set_rsrp mm_cell_info_nr5g_set_rsrq mm_cell_info_nr5g_set_sinr mm_cell_info_nr5g_set_timing_advance MMCellInfoNr5gClass MMCellInfoNr5gPrivate MM_IS_CELL_INFO_NR5G MM_IS_CELL_INFO_NR5G_CLASS MM_TYPE_CELL_INFO_NR5G MM_CELL_INFO_NR5G MM_CELL_INFO_NR5G_CLASS MM_CELL_INFO_NR5G_GET_CLASS mm_cell_info_nr5g_get_type
mm-modem-3gpp MMModem3gpp MMModem3gpp MMModem3gppNetwork mm_modem_3gpp_network_get_operator_code mm_modem_3gpp_network_get_operator_short mm_modem_3gpp_network_get_operator_long mm_modem_3gpp_network_get_access_technology mm_modem_3gpp_network_get_availability mm_modem_3gpp_network_free mm_modem_3gpp_get_path mm_modem_3gpp_dup_path mm_modem_3gpp_get_imei mm_modem_3gpp_dup_imei mm_modem_3gpp_get_operator_code mm_modem_3gpp_dup_operator_code mm_modem_3gpp_get_operator_name mm_modem_3gpp_dup_operator_name mm_modem_3gpp_get_enabled_facility_locks mm_modem_3gpp_get_registration_state mm_modem_3gpp_get_pco mm_modem_3gpp_get_eps_ue_mode_operation mm_modem_3gpp_get_initial_eps_bearer_path mm_modem_3gpp_dup_initial_eps_bearer_path mm_modem_3gpp_get_initial_eps_bearer mm_modem_3gpp_get_initial_eps_bearer_finish mm_modem_3gpp_get_initial_eps_bearer_sync mm_modem_3gpp_get_initial_eps_bearer_settings mm_modem_3gpp_peek_initial_eps_bearer_settings mm_modem_3gpp_get_packet_service_state mm_modem_3gpp_get_nr5g_registration_settings mm_modem_3gpp_peek_nr5g_registration_settings mm_modem_3gpp_register mm_modem_3gpp_register_finish mm_modem_3gpp_register_sync mm_modem_3gpp_scan mm_modem_3gpp_scan_finish mm_modem_3gpp_scan_sync mm_modem_3gpp_set_eps_ue_mode_operation mm_modem_3gpp_set_eps_ue_mode_operation_finish mm_modem_3gpp_set_eps_ue_mode_operation_sync mm_modem_3gpp_set_initial_eps_bearer_settings mm_modem_3gpp_set_initial_eps_bearer_settings_finish mm_modem_3gpp_set_initial_eps_bearer_settings_sync mm_modem_3gpp_disable_facility_lock mm_modem_3gpp_disable_facility_lock_finish mm_modem_3gpp_disable_facility_lock_sync mm_modem_3gpp_set_packet_service_state mm_modem_3gpp_set_packet_service_state_finish mm_modem_3gpp_set_packet_service_state_sync mm_modem_3gpp_set_nr5g_registration_settings mm_modem_3gpp_set_nr5g_registration_settings_finish mm_modem_3gpp_set_nr5g_registration_settings_sync mm_modem_3gpp_set_carrier_lock mm_modem_3gpp_set_carrier_lock_finish mm_modem_3gpp_set_carrier_lock_sync MMModem3gppClass MMModem3gppPrivate MM_IS_MODEM_3GPP MM_IS_MODEM_3GPP_CLASS MM_MODEM_3GPP MM_MODEM_3GPP_CLASS MM_MODEM_3GPP_GET_CLASS MM_TYPE_MODEM_3GPP MM_TYPE_MODEM_3GPP_NETWORK mm_modem_3gpp_get_type mm_modem_3gpp_network_get_type
mm-modem-3gpp-ussd MMModem3gppUssd MMModem3gppUssd mm_modem_3gpp_ussd_get_path mm_modem_3gpp_ussd_dup_path mm_modem_3gpp_ussd_get_state mm_modem_3gpp_ussd_get_network_request mm_modem_3gpp_ussd_dup_network_request mm_modem_3gpp_ussd_get_network_notification mm_modem_3gpp_ussd_dup_network_notification mm_modem_3gpp_ussd_initiate mm_modem_3gpp_ussd_initiate_finish mm_modem_3gpp_ussd_initiate_sync mm_modem_3gpp_ussd_respond mm_modem_3gpp_ussd_respond_finish mm_modem_3gpp_ussd_respond_sync mm_modem_3gpp_ussd_cancel mm_modem_3gpp_ussd_cancel_finish mm_modem_3gpp_ussd_cancel_sync MMModem3gppUssdClass MM_IS_MODEM_3GPP_USSD MM_IS_MODEM_3GPP_USSD_CLASS MM_MODEM_3GPP_USSD MM_MODEM_3GPP_USSD_CLASS MM_MODEM_3GPP_USSD_GET_CLASS MM_TYPE_MODEM_3GPP_USSD mm_modem_3gpp_ussd_get_type
mm-cdma-manual-activation-properties MMCdmaManualActivationProperties MMCdmaManualActivationProperties mm_cdma_manual_activation_properties_new mm_cdma_manual_activation_properties_get_spc mm_cdma_manual_activation_properties_set_spc mm_cdma_manual_activation_properties_get_sid mm_cdma_manual_activation_properties_set_sid mm_cdma_manual_activation_properties_get_mdn mm_cdma_manual_activation_properties_set_mdn mm_cdma_manual_activation_properties_get_min mm_cdma_manual_activation_properties_set_min mm_cdma_manual_activation_properties_get_mn_ha_key mm_cdma_manual_activation_properties_set_mn_ha_key mm_cdma_manual_activation_properties_get_mn_aaa_key mm_cdma_manual_activation_properties_set_mn_aaa_key mm_cdma_manual_activation_properties_get_prl mm_cdma_manual_activation_properties_peek_prl_bytearray mm_cdma_manual_activation_properties_get_prl_bytearray mm_cdma_manual_activation_properties_set_prl mm_cdma_manual_activation_properties_set_prl_bytearray mm_cdma_manual_activation_properties_new_from_string mm_cdma_manual_activation_properties_new_from_dictionary mm_cdma_manual_activation_properties_get_dictionary MMCdmaManualActivationPropertiesClass MMCdmaManualActivationPropertiesPrivate MM_CDMA_MANUAL_ACTIVATION_PROPERTIES MM_CDMA_MANUAL_ACTIVATION_PROPERTIES_CLASS MM_CDMA_MANUAL_ACTIVATION_PROPERTIES_GET_CLASS MM_IS_CDMA_MANUAL_ACTIVATION_PROPERTIES MM_IS_CDMA_MANUAL_ACTIVATION_PROPERTIES_CLASS MM_TYPE_CDMA_MANUAL_ACTIVATION_PROPERTIES mm_cdma_manual_activation_properties_get_type
mm-modem-cdma MMModemCdma MMModemCdma MM_MODEM_CDMA_NID_UNKNOWN MM_MODEM_CDMA_SID_UNKNOWN mm_modem_cdma_get_path mm_modem_cdma_dup_path mm_modem_cdma_get_esn mm_modem_cdma_dup_esn mm_modem_cdma_get_meid mm_modem_cdma_dup_meid mm_modem_cdma_get_nid mm_modem_cdma_get_sid mm_modem_cdma_get_cdma1x_registration_state mm_modem_cdma_get_evdo_registration_state mm_modem_cdma_get_activation_state mm_modem_cdma_activate mm_modem_cdma_activate_finish mm_modem_cdma_activate_sync mm_modem_cdma_activate_manual mm_modem_cdma_activate_manual_finish mm_modem_cdma_activate_manual_sync MMModemCdmaClass MM_IS_MODEM_CDMA MM_IS_MODEM_CDMA_CLASS MM_MODEM_CDMA MM_MODEM_CDMA_CLASS MM_MODEM_CDMA_GET_CLASS MM_TYPE_MODEM_CDMA mm_modem_cdma_get_type
mm-modem-location MMModemLocation MMModemLocation MM_LOCATION_LONGITUDE_UNKNOWN MM_LOCATION_LATITUDE_UNKNOWN MM_LOCATION_ALTITUDE_UNKNOWN mm_modem_location_get_path mm_modem_location_dup_path mm_modem_location_get_capabilities mm_modem_location_get_enabled mm_modem_location_get_gps_refresh_rate mm_modem_location_signals_location mm_modem_location_dup_supl_server mm_modem_location_get_supl_server mm_modem_location_get_supported_assistance_data mm_modem_location_dup_assistance_data_servers mm_modem_location_get_assistance_data_servers mm_modem_location_setup mm_modem_location_setup_finish mm_modem_location_setup_sync mm_modem_location_set_supl_server mm_modem_location_set_supl_server_finish mm_modem_location_set_supl_server_sync mm_modem_location_inject_assistance_data mm_modem_location_inject_assistance_data_finish mm_modem_location_inject_assistance_data_sync mm_modem_location_set_gps_refresh_rate mm_modem_location_set_gps_refresh_rate_finish mm_modem_location_set_gps_refresh_rate_sync mm_modem_location_get_3gpp mm_modem_location_get_3gpp_finish mm_modem_location_get_3gpp_sync mm_modem_location_peek_signaled_3gpp mm_modem_location_get_signaled_3gpp mm_modem_location_get_gps_nmea mm_modem_location_get_gps_nmea_finish mm_modem_location_get_gps_nmea_sync mm_modem_location_peek_signaled_gps_nmea mm_modem_location_get_signaled_gps_nmea mm_modem_location_get_gps_raw mm_modem_location_get_gps_raw_finish mm_modem_location_get_gps_raw_sync mm_modem_location_peek_signaled_gps_raw mm_modem_location_get_signaled_gps_raw mm_modem_location_get_cdma_bs mm_modem_location_get_cdma_bs_finish mm_modem_location_get_cdma_bs_sync mm_modem_location_peek_signaled_cdma_bs mm_modem_location_get_signaled_cdma_bs mm_modem_location_get_full mm_modem_location_get_full_finish mm_modem_location_get_full_sync MMModemLocationClass MMModemLocationPrivate MM_IS_MODEM_LOCATION MM_IS_MODEM_LOCATION_CLASS MM_MODEM_LOCATION MM_MODEM_LOCATION_CLASS MM_MODEM_LOCATION_GET_CLASS MM_TYPE_MODEM_LOCATION mm_modem_location_get_type
mm-location-3gpp MMLocation3gpp MMLocation3gpp mm_location_3gpp_get_mobile_country_code mm_location_3gpp_get_mobile_network_code mm_location_3gpp_get_operator_code mm_location_3gpp_get_location_area_code mm_location_3gpp_get_tracking_area_code mm_location_3gpp_get_cell_id mm_location_3gpp_get_string_variant mm_location_3gpp_new mm_location_3gpp_new_from_string_variant mm_location_3gpp_set_cell_id mm_location_3gpp_set_location_area_code mm_location_3gpp_set_tracking_area_code mm_location_3gpp_set_operator_code mm_location_3gpp_reset MMLocation3gppClass MMLocation3gppPrivate MM_IS_LOCATION_3GPP MM_IS_LOCATION_3GPP_CLASS MM_LOCATION_3GPP MM_LOCATION_3GPP_CLASS MM_LOCATION_3GPP_GET_CLASS MM_TYPE_LOCATION_3GPP mm_location_3gpp_get_type
mm-location-gps-nmea MMLocationGpsNmea MMLocationGpsNmea mm_location_gps_nmea_get_trace mm_location_gps_nmea_get_traces mm_location_gps_nmea_new mm_location_gps_nmea_new_from_string_variant mm_location_gps_nmea_add_trace mm_location_gps_nmea_get_string_variant MMLocationGpsNmeaClass MMLocationGpsNmeaPrivate MM_IS_LOCATION_GPS_NMEA MM_IS_LOCATION_GPS_NMEA_CLASS MM_LOCATION_GPS_NMEA MM_LOCATION_GPS_NMEA_CLASS MM_LOCATION_GPS_NMEA_GET_CLASS MM_TYPE_LOCATION_GPS_NMEA mm_location_gps_nmea_get_type
mm-location-gps-raw MMLocationGpsRaw MMLocationGpsRaw mm_location_gps_raw_get_utc_time mm_location_gps_raw_get_longitude mm_location_gps_raw_get_latitude mm_location_gps_raw_get_altitude mm_location_gps_raw_new mm_location_gps_raw_new_from_dictionary mm_location_gps_raw_get_dictionary mm_location_gps_raw_add_trace MMLocationGpsRawClass MMLocationGpsRawPrivate MM_IS_LOCATION_GPS_RAW MM_IS_LOCATION_GPS_RAW_CLASS MM_LOCATION_GPS_RAW MM_LOCATION_GPS_RAW_CLASS MM_LOCATION_GPS_RAW_GET_CLASS MM_TYPE_LOCATION_GPS_RAW mm_location_gps_raw_get_type
mm-location-cdma-bs MMLocationCdmaBs MMLocationCdmaBs mm_location_cdma_bs_get_latitude mm_location_cdma_bs_get_longitude mm_location_cdma_bs_get_dictionary mm_location_cdma_bs_new mm_location_cdma_bs_set mm_location_cdma_bs_new_from_dictionary MMLocationCdmaBsClass MMLocationCdmaBsPrivate MM_IS_LOCATION_CDMA_BS MM_IS_LOCATION_CDMA_BS_CLASS MM_LOCATION_CDMA_BS MM_LOCATION_CDMA_BS_CLASS MM_LOCATION_CDMA_BS_GET_CLASS MM_TYPE_LOCATION_CDMA_BS mm_location_cdma_bs_get_type
mm-modem-messaging MMModemMessaging MMModemMessaging mm_modem_messaging_get_path mm_modem_messaging_dup_path mm_modem_messaging_peek_supported_storages mm_modem_messaging_get_supported_storages mm_modem_messaging_get_default_storage mm_modem_messaging_create mm_modem_messaging_create_finish mm_modem_messaging_create_sync mm_modem_messaging_delete mm_modem_messaging_delete_finish mm_modem_messaging_delete_sync mm_modem_messaging_list mm_modem_messaging_list_finish mm_modem_messaging_list_sync MMModemMessagingClass MMModemMessagingPrivate MM_IS_MODEM_MESSAGING MM_IS_MODEM_MESSAGING_CLASS MM_MODEM_MESSAGING MM_MODEM_MESSAGING_CLASS MM_MODEM_MESSAGING_GET_CLASS MM_TYPE_MODEM_MESSAGING mm_modem_messaging_get_type
mm-modem-time MMModemTime MMModemTime mm_modem_time_get_path mm_modem_time_dup_path mm_modem_time_peek_network_timezone mm_modem_time_get_network_timezone mm_modem_time_get_network_time mm_modem_time_get_network_time_finish mm_modem_time_get_network_time_sync MMModemTimeClass MMModemTimePrivate MM_IS_MODEM_TIME MM_IS_MODEM_TIME_CLASS MM_MODEM_TIME MM_MODEM_TIME_CLASS MM_MODEM_TIME_GET_CLASS MM_TYPE_MODEM_TIME mm_modem_time_get_type
mm-network-timezone MMNetworkTimezone MMNetworkTimezone MM_NETWORK_TIMEZONE_OFFSET_UNKNOWN MM_NETWORK_TIMEZONE_LEAP_SECONDS_UNKNOWN mm_network_timezone_get_offset mm_network_timezone_get_dst_offset mm_network_timezone_get_leap_seconds mm_network_timezone_new mm_network_timezone_new_from_dictionary mm_network_timezone_set_dst_offset mm_network_timezone_set_leap_seconds mm_network_timezone_get_dictionary mm_network_timezone_set_offset MMNetworkTimezoneClass MMNetworkTimezonePrivate MM_IS_NETWORK_TIMEZONE MM_IS_NETWORK_TIMEZONE_CLASS MM_NETWORK_TIMEZONE MM_NETWORK_TIMEZONE_CLASS MM_NETWORK_TIMEZONE_GET_CLASS MM_TYPE_NETWORK_TIMEZONE mm_network_timezone_get_type
mm-modem-firmware MMModemFirmware MMModemFirmware mm_modem_firmware_get_path mm_modem_firmware_dup_path mm_modem_firmware_list mm_modem_firmware_list_finish mm_modem_firmware_list_sync mm_modem_firmware_select mm_modem_firmware_select_finish mm_modem_firmware_select_sync mm_modem_firmware_get_update_settings mm_modem_firmware_peek_update_settings MMModemFirmwarePrivate MMModemFirmwareClass MM_IS_MODEM_FIRMWARE MM_IS_MODEM_FIRMWARE_CLASS MM_MODEM_FIRMWARE MM_MODEM_FIRMWARE_CLASS MM_MODEM_FIRMWARE_GET_CLASS MM_TYPE_MODEM_FIRMWARE mm_modem_firmware_get_type
mm-firmware-properties MMFirmwareProperties MMFirmwareProperties mm_firmware_properties_get_image_type mm_firmware_properties_get_unique_id mm_firmware_properties_get_gobi_pri_version mm_firmware_properties_get_gobi_pri_info mm_firmware_properties_get_gobi_boot_version mm_firmware_properties_get_gobi_pri_unique_id mm_firmware_properties_get_gobi_modem_unique_id mm_firmware_properties_new mm_firmware_properties_new_from_dictionary mm_firmware_properties_get_dictionary mm_firmware_properties_set_gobi_pri_version mm_firmware_properties_set_gobi_pri_info mm_firmware_properties_set_gobi_boot_version mm_firmware_properties_set_gobi_pri_unique_id mm_firmware_properties_set_gobi_modem_unique_id MMFirmwarePropertiesClass MMFirmwarePropertiesPrivate MM_FIRMWARE_PROPERTIES MM_FIRMWARE_PROPERTIES_CLASS MM_FIRMWARE_PROPERTIES_GET_CLASS MM_IS_FIRMWARE_PROPERTIES MM_IS_FIRMWARE_PROPERTIES_CLASS MM_TYPE_FIRMWARE_PROPERTIES mm_firmware_properties_get_type
mm-firmware-update-settings MMFirmwareUpdateSettings MMFirmwareUpdateSettings mm_firmware_update_settings_get_fastboot_at mm_firmware_update_settings_get_method mm_firmware_update_settings_get_device_ids mm_firmware_update_settings_get_version mm_firmware_update_settings_get_variant mm_firmware_update_settings_new mm_firmware_update_settings_new_from_variant mm_firmware_update_settings_set_fastboot_at mm_firmware_update_settings_set_method mm_firmware_update_settings_set_device_ids mm_firmware_update_settings_set_version MMFirmwareUpdateSettingsClass MMFirmwareUpdateSettingsPrivate MM_FIRMWARE_UPDATE_SETTINGS MM_FIRMWARE_UPDATE_SETTINGS_CLASS MM_FIRMWARE_UPDATE_SETTINGS_GET_CLASS MM_IS_FIRMWARE_UPDATE_SETTINGS MM_IS_FIRMWARE_UPDATE_SETTINGS_CLASS MM_TYPE_FIRMWARE_UPDATE_SETTINGS mm_firmware_update_settings_get_type
mm-modem-oma MMModemOma MMModemOma MMOmaPendingNetworkInitiatedSession mm_modem_oma_get_path mm_modem_oma_dup_path mm_modem_oma_setup mm_modem_oma_setup_finish mm_modem_oma_setup_sync mm_modem_oma_start_client_initiated_session mm_modem_oma_start_client_initiated_session_finish mm_modem_oma_start_client_initiated_session_sync mm_modem_oma_accept_network_initiated_session mm_modem_oma_accept_network_initiated_session_finish mm_modem_oma_accept_network_initiated_session_sync mm_modem_oma_cancel_session mm_modem_oma_cancel_session_finish mm_modem_oma_cancel_session_sync mm_modem_oma_get_features mm_modem_oma_get_session_type mm_modem_oma_get_session_state mm_modem_oma_peek_pending_network_initiated_sessions mm_modem_oma_get_pending_network_initiated_sessions MMModemOmaClass MMModemOmaPrivate MM_IS_MODEM_OMA MM_IS_MODEM_OMA_CLASS MM_MODEM_OMA MM_MODEM_OMA_CLASS MM_MODEM_OMA_GET_CLASS MM_TYPE_MODEM_OMA mm_modem_oma_get_type
mm-modem-simple MMModemSimple MMModemSimple mm_modem_simple_get_path mm_modem_simple_dup_path mm_modem_simple_connect mm_modem_simple_connect_finish mm_modem_simple_connect_sync mm_modem_simple_disconnect mm_modem_simple_disconnect_finish mm_modem_simple_disconnect_sync mm_modem_simple_get_status mm_modem_simple_get_status_finish mm_modem_simple_get_status_sync MMModemSimpleClass MM_IS_MODEM_SIMPLE MM_IS_MODEM_SIMPLE_CLASS MM_MODEM_SIMPLE MM_MODEM_SIMPLE_CLASS MM_MODEM_SIMPLE_GET_CLASS MM_TYPE_MODEM_SIMPLE mm_modem_simple_get_type
mm-modem-sar MMModemSar MMModemSar mm_modem_sar_get_path mm_modem_sar_dup_path mm_modem_sar_enable mm_modem_sar_enable_finish mm_modem_sar_enable_sync mm_modem_sar_set_power_level mm_modem_sar_set_power_level_finish mm_modem_sar_set_power_level_sync mm_modem_sar_get_state mm_modem_sar_get_power_level MMModemSarClass MM_IS_MODEM_SAR MM_IS_MODEM_SAR_CLASS MM_MODEM_SAR MM_MODEM_SAR_CLASS MM_MODEM_SAR_GET_CLASS MM_TYPE_MODEM_SAR mm_modem_sar_get_type
mm-simple-connect-properties MMSimpleConnectProperties MMSimpleConnectProperties mm_simple_connect_properties_new mm_simple_connect_properties_get_pin mm_simple_connect_properties_set_pin mm_simple_connect_properties_get_operator_id mm_simple_connect_properties_set_operator_id mm_simple_connect_properties_get_apn mm_simple_connect_properties_set_apn mm_simple_connect_properties_get_apn_type mm_simple_connect_properties_set_apn_type mm_simple_connect_properties_get_allowed_auth mm_simple_connect_properties_set_allowed_auth mm_simple_connect_properties_get_user mm_simple_connect_properties_set_user mm_simple_connect_properties_get_password mm_simple_connect_properties_set_password mm_simple_connect_properties_get_ip_type mm_simple_connect_properties_set_ip_type mm_simple_connect_properties_get_profile_id mm_simple_connect_properties_set_profile_id mm_simple_connect_properties_get_allow_roaming mm_simple_connect_properties_set_allow_roaming mm_simple_connect_properties_get_rm_protocol mm_simple_connect_properties_set_rm_protocol mm_simple_connect_properties_get_multiplex mm_simple_connect_properties_set_multiplex mm_simple_connect_properties_get_bearer_properties mm_simple_connect_properties_new_from_dictionary mm_simple_connect_properties_new_from_string mm_simple_connect_properties_get_dictionary MMSimpleConnectPropertiesClass MMSimpleConnectPropertiesPrivate MM_IS_SIMPLE_CONNECT_PROPERTIES MM_IS_SIMPLE_CONNECT_PROPERTIES_CLASS MM_SIMPLE_CONNECT_PROPERTIES MM_SIMPLE_CONNECT_PROPERTIES_CLASS MM_SIMPLE_CONNECT_PROPERTIES_GET_CLASS MM_TYPE_SIMPLE_CONNECT_PROPERTIES mm_simple_connect_properties_get_type
mm-simple-status MMSimpleStatus MMSimpleStatus mm_simple_status_get_state mm_simple_status_get_signal_quality mm_simple_status_get_access_technologies mm_simple_status_get_current_bands mm_simple_status_get_3gpp_registration_state mm_simple_status_get_3gpp_operator_code mm_simple_status_get_3gpp_operator_name mm_simple_status_get_cdma_cdma1x_registration_state mm_simple_status_get_cdma_evdo_registration_state mm_simple_status_get_cdma_nid mm_simple_status_get_cdma_sid MM_SIMPLE_PROPERTY_3GPP_OPERATOR_CODE MM_SIMPLE_PROPERTY_3GPP_OPERATOR_NAME MM_SIMPLE_PROPERTY_3GPP_REGISTRATION_STATE MM_SIMPLE_PROPERTY_3GPP_SUBSCRIPTION_STATE MM_SIMPLE_PROPERTY_ACCESS_TECHNOLOGIES MM_SIMPLE_PROPERTY_CURRENT_BANDS MM_SIMPLE_PROPERTY_CDMA_CDMA1X_REGISTRATION_STATE MM_SIMPLE_PROPERTY_CDMA_EVDO_REGISTRATION_STATE MM_SIMPLE_PROPERTY_CDMA_NID MM_SIMPLE_PROPERTY_CDMA_SID MM_SIMPLE_PROPERTY_SIGNAL_QUALITY MM_SIMPLE_PROPERTY_STATE mm_simple_status_new mm_simple_status_new_from_dictionary mm_simple_status_get_dictionary MMSimpleStatusClass MMSimpleStatusPrivate MM_IS_SIMPLE_STATUS MM_IS_SIMPLE_STATUS_CLASS MM_SIMPLE_STATUS MM_SIMPLE_STATUS_CLASS MM_SIMPLE_STATUS_GET_CLASS MM_TYPE_SIMPLE_STATUS mm_simple_status_get_type
mm-modem-signal MMModemSignal MMModemSignal mm_modem_signal_get_path mm_modem_signal_dup_path mm_modem_signal_get_rate mm_modem_signal_get_rssi_threshold mm_modem_signal_get_error_rate_threshold mm_modem_signal_peek_cdma mm_modem_signal_get_cdma mm_modem_signal_peek_evdo mm_modem_signal_get_evdo mm_modem_signal_peek_gsm mm_modem_signal_get_gsm mm_modem_signal_peek_umts mm_modem_signal_get_umts mm_modem_signal_peek_lte mm_modem_signal_get_lte mm_modem_signal_peek_nr5g mm_modem_signal_get_nr5g mm_modem_signal_setup mm_modem_signal_setup_finish mm_modem_signal_setup_sync mm_modem_signal_setup_thresholds mm_modem_signal_setup_thresholds_finish mm_modem_signal_setup_thresholds_sync MMModemSignalPrivate MMModemSignalClass MM_IS_MODEM_SIGNAL MM_IS_MODEM_SIGNAL_CLASS MM_MODEM_SIGNAL MM_MODEM_SIGNAL_CLASS MM_MODEM_SIGNAL_GET_CLASS MM_TYPE_MODEM_SIGNAL mm_modem_signal_get_type
mm-signal MMSignal MMSignal MM_SIGNAL_UNKNOWN mm_signal_get_rssi mm_signal_get_rscp mm_signal_get_ecio mm_signal_get_sinr mm_signal_get_io mm_signal_get_rsrp mm_signal_get_rsrq mm_signal_get_snr mm_signal_get_error_rate mm_signal_new mm_signal_new_from_dictionary mm_signal_get_dictionary mm_signal_set_rssi mm_signal_set_rscp mm_signal_set_ecio mm_signal_set_sinr mm_signal_set_io mm_signal_set_rsrp mm_signal_set_rsrq mm_signal_set_snr mm_signal_set_error_rate MMSignalClass MMSignalPrivate MM_SIGNAL MM_SIGNAL_CLASS MM_SIGNAL_GET_CLASS MM_IS_SIGNAL MM_IS_SIGNAL_CLASS MM_TYPE_SIGNAL mm_signal_get_type
mm-signal-threshold-properties MMSignalThresholdProperties MMSignalThresholdProperties mm_signal_threshold_properties_new mm_signal_threshold_properties_get_rssi mm_signal_threshold_properties_set_rssi mm_signal_threshold_properties_get_error_rate mm_signal_threshold_properties_set_error_rate mm_signal_threshold_properties_new_from_dictionary mm_signal_threshold_properties_new_from_string mm_signal_threshold_properties_get_dictionary MMSignalThresholdPropertiesClass MMSignalThresholdPropertiesPrivate MM_SIGNAL_THRESHOLD_PROPERTIES MM_SIGNAL_THRESHOLD_PROPERTIES_CLASS MM_SIGNAL_THRESHOLD_PROPERTIES_GET_CLASS MM_IS_SIGNAL_THRESHOLD_PROPERTIES MM_IS_SIGNAL_THRESHOLD_PROPERTIES_CLASS MM_TYPE_SIGNAL_THRESHOLD_PROPERTIES mm_signal_threshold_properties_get_type
mm-modem-voice MMModemVoice MMModemVoice mm_modem_voice_get_path mm_modem_voice_dup_path mm_modem_voice_get_emergency_only mm_modem_voice_create_call mm_modem_voice_create_call_finish mm_modem_voice_create_call_sync mm_modem_voice_delete_call mm_modem_voice_delete_call_finish mm_modem_voice_delete_call_sync mm_modem_voice_list_calls mm_modem_voice_list_calls_finish mm_modem_voice_list_calls_sync mm_modem_voice_hangup_and_accept mm_modem_voice_hangup_and_accept_finish mm_modem_voice_hangup_and_accept_sync mm_modem_voice_hold_and_accept mm_modem_voice_hold_and_accept_finish mm_modem_voice_hold_and_accept_sync mm_modem_voice_hangup_all mm_modem_voice_hangup_all_finish mm_modem_voice_hangup_all_sync mm_modem_voice_transfer mm_modem_voice_transfer_finish mm_modem_voice_transfer_sync mm_modem_voice_call_waiting_query mm_modem_voice_call_waiting_query_finish mm_modem_voice_call_waiting_query_sync mm_modem_voice_call_waiting_setup mm_modem_voice_call_waiting_setup_finish mm_modem_voice_call_waiting_setup_sync MMModemVoiceClass MMModemVoicePrivate MM_IS_MODEM_VOICE MM_IS_MODEM_VOICE_CLASS MM_MODEM_VOICE MM_MODEM_VOICE_CLASS MM_MODEM_VOICE_GET_CLASS MM_TYPE_MODEM_VOICE mm_modem_voice_get_type
mm-bearer MMBearer MMBearer mm_bearer_get_path mm_bearer_dup_path mm_bearer_get_interface mm_bearer_dup_interface mm_bearer_get_connected mm_bearer_get_suspended mm_bearer_get_multiplexed mm_bearer_get_ip_timeout mm_bearer_get_bearer_type mm_bearer_get_profile_id mm_bearer_peek_ipv4_config mm_bearer_get_ipv4_config mm_bearer_peek_ipv6_config mm_bearer_get_ipv6_config mm_bearer_peek_properties mm_bearer_get_properties mm_bearer_peek_stats mm_bearer_get_stats mm_bearer_get_connection_error mm_bearer_peek_connection_error mm_bearer_get_reload_stats_supported mm_bearer_connect mm_bearer_connect_finish mm_bearer_connect_sync mm_bearer_disconnect mm_bearer_disconnect_finish mm_bearer_disconnect_sync MMBearerClass MMBearerPrivate MM_BEARER MM_BEARER_CLASS MM_BEARER_GET_CLASS MM_IS_BEARER MM_IS_BEARER_CLASS MM_TYPE_BEARER mm_bearer_get_type
mm-bearer-ip-config MMBearerIpConfig MMBearerIpConfig mm_bearer_ip_config_get_method mm_bearer_ip_config_get_address mm_bearer_ip_config_get_prefix mm_bearer_ip_config_get_dns mm_bearer_ip_config_get_gateway mm_bearer_ip_config_get_mtu mm_bearer_ip_config_get_dictionary mm_bearer_ip_config_new mm_bearer_ip_config_new_from_dictionary mm_bearer_ip_config_set_address mm_bearer_ip_config_set_dns mm_bearer_ip_config_set_gateway mm_bearer_ip_config_set_method mm_bearer_ip_config_set_prefix mm_bearer_ip_config_set_mtu mm_bearer_ip_config_dup MMBearerIpConfigClass MMBearerIpConfigPrivate MM_BEARER_IP_CONFIG MM_BEARER_IP_CONFIG_CLASS MM_BEARER_IP_CONFIG_GET_CLASS MM_IS_BEARER_IP_CONFIG MM_IS_BEARER_IP_CONFIG_CLASS MM_TYPE_BEARER_IP_CONFIG mm_bearer_ip_config_get_type
mm-bearer-stats MMBearerStats MMBearerStats mm_bearer_stats_get_duration mm_bearer_stats_get_start_date mm_bearer_stats_get_rx_bytes mm_bearer_stats_get_tx_bytes mm_bearer_stats_get_attempts mm_bearer_stats_get_failed_attempts mm_bearer_stats_get_total_duration mm_bearer_stats_get_total_rx_bytes mm_bearer_stats_get_total_tx_bytes mm_bearer_stats_get_uplink_speed mm_bearer_stats_get_downlink_speed mm_bearer_stats_get_dictionary mm_bearer_stats_new mm_bearer_stats_new_from_dictionary mm_bearer_stats_set_duration mm_bearer_stats_set_start_date mm_bearer_stats_set_rx_bytes mm_bearer_stats_set_tx_bytes mm_bearer_stats_set_attempts mm_bearer_stats_set_failed_attempts mm_bearer_stats_set_total_duration mm_bearer_stats_set_total_rx_bytes mm_bearer_stats_set_total_tx_bytes mm_bearer_stats_set_uplink_speed mm_bearer_stats_set_downlink_speed MMBearerStatsClass MMBearerStatsPrivate MM_BEARER_STATS MM_BEARER_STATS_CLASS MM_BEARER_STATS_GET_CLASS MM_IS_BEARER_STATS MM_IS_BEARER_STATS_CLASS MM_TYPE_BEARER_STATS mm_bearer_stats_get_type
mm-bearer-properties MMBearerProperties MMBearerProperties mm_bearer_properties_new mm_bearer_properties_get_apn mm_bearer_properties_set_apn mm_bearer_properties_get_apn_type mm_bearer_properties_set_apn_type mm_bearer_properties_get_allowed_auth mm_bearer_properties_set_allowed_auth mm_bearer_properties_get_user mm_bearer_properties_set_user mm_bearer_properties_get_password mm_bearer_properties_set_password mm_bearer_properties_get_ip_type mm_bearer_properties_set_ip_type mm_bearer_properties_get_profile_id mm_bearer_properties_set_profile_id mm_bearer_properties_get_profile_name mm_bearer_properties_set_profile_name mm_bearer_properties_get_allow_roaming mm_bearer_properties_set_allow_roaming mm_bearer_properties_get_rm_protocol mm_bearer_properties_set_rm_protocol mm_bearer_properties_get_multiplex mm_bearer_properties_set_multiplex mm_bearer_properties_get_access_type_preference mm_bearer_properties_set_access_type_preference mm_bearer_properties_get_roaming_allowance mm_bearer_properties_set_roaming_allowance mm_bearer_properties_new_from_dictionary mm_bearer_properties_new_from_string mm_bearer_properties_new_from_profile MMBearerPropertiesCmpFlags mm_bearer_properties_cmp mm_bearer_properties_consume_string mm_bearer_properties_consume_variant mm_bearer_properties_dup mm_bearer_properties_get_dictionary mm_bearer_properties_peek_3gpp_profile MMBearerPropertiesClass MMBearerPropertiesPrivate MM_BEARER_PROPERTIES MM_BEARER_PROPERTIES_CLASS MM_BEARER_PROPERTIES_GET_CLASS MM_IS_BEARER_PROPERTIES MM_IS_BEARER_PROPERTIES_CLASS MM_TYPE_BEARER_PROPERTIES mm_bearer_properties_get_type
mm-sim-preferred-network MMSimPreferredNetwork MMSimPreferredNetwork mm_sim_preferred_network_new mm_sim_preferred_network_get_operator_code mm_sim_preferred_network_set_operator_code mm_sim_preferred_network_get_access_technology mm_sim_preferred_network_set_access_technology mm_sim_preferred_network_free mm_sim_preferred_network_new_from_variant mm_sim_preferred_network_get_tuple mm_sim_preferred_network_list_get_variant mm_sim_preferred_network_list_new_from_variant mm_sim_preferred_network_list_copy mm_sim_preferred_network_list_free MM_TYPE_SIM_PREFERRED_NETWORK mm_sim_preferred_network_get_type
mm-sim MMSim MMSim mm_sim_get_path mm_sim_dup_path mm_sim_get_active mm_sim_get_identifier mm_sim_dup_identifier mm_sim_get_imsi mm_sim_dup_imsi mm_sim_get_eid mm_sim_dup_eid mm_sim_get_operator_identifier mm_sim_dup_operator_identifier mm_sim_get_operator_name mm_sim_dup_operator_name mm_sim_get_emergency_numbers mm_sim_dup_emergency_numbers mm_sim_get_preferred_networks mm_sim_get_gid1 mm_sim_dup_gid1 mm_sim_get_gid2 mm_sim_dup_gid2 mm_sim_get_sim_type mm_sim_get_esim_status mm_sim_get_removability mm_sim_send_pin mm_sim_send_pin_finish mm_sim_send_pin_sync mm_sim_send_puk mm_sim_send_puk_finish mm_sim_send_puk_sync mm_sim_enable_pin mm_sim_enable_pin_finish mm_sim_enable_pin_sync mm_sim_disable_pin mm_sim_disable_pin_finish mm_sim_disable_pin_sync mm_sim_change_pin mm_sim_change_pin_finish mm_sim_change_pin_sync mm_sim_set_preferred_networks mm_sim_set_preferred_networks_finish mm_sim_set_preferred_networks_sync MMSimClass MM_IS_SIM MM_IS_SIM_CLASS MM_SIM MM_SIM_CLASS MM_SIM_GET_CLASS MM_TYPE_SIM mm_sim_get_type
mm-sms MMSms MMSms mm_sms_get_path mm_sms_dup_path mm_sms_get_state mm_sms_get_pdu_type mm_sms_get_storage mm_sms_get_text mm_sms_dup_text mm_sms_get_data mm_sms_dup_data mm_sms_get_number mm_sms_dup_number mm_sms_get_smsc mm_sms_dup_smsc mm_sms_get_message_reference mm_sms_get_class mm_sms_get_teleservice_id mm_sms_get_service_category mm_sms_get_validity_type mm_sms_get_validity_relative mm_sms_get_timestamp mm_sms_dup_timestamp mm_sms_get_discharge_timestamp mm_sms_dup_discharge_timestamp mm_sms_get_delivery_state mm_sms_get_delivery_report_request mm_sms_send mm_sms_send_finish mm_sms_send_sync mm_sms_store mm_sms_store_finish mm_sms_store_sync MMSmsClass MM_IS_SMS MM_IS_SMS_CLASS MM_SMS MM_SMS_CLASS MM_SMS_GET_CLASS MM_TYPE_SMS mm_sms_get_type
mm-sms-properties MMSmsProperties MMSmsProperties mm_sms_properties_new mm_sms_properties_get_text mm_sms_properties_set_text mm_sms_properties_get_data mm_sms_properties_set_data mm_sms_properties_peek_data_bytearray mm_sms_properties_get_data_bytearray mm_sms_properties_set_data_bytearray mm_sms_properties_get_number mm_sms_properties_set_number mm_sms_properties_get_smsc mm_sms_properties_set_smsc mm_sms_properties_get_validity_type mm_sms_properties_get_validity_relative mm_sms_properties_set_validity_relative mm_sms_properties_get_class mm_sms_properties_set_class mm_sms_properties_get_delivery_report_request mm_sms_properties_set_delivery_report_request mm_sms_properties_get_teleservice_id mm_sms_properties_set_teleservice_id mm_sms_properties_get_service_category mm_sms_properties_set_service_category mm_sms_properties_get_dictionary mm_sms_properties_dup mm_sms_properties_new_from_dictionary mm_sms_properties_new_from_string MMSmsPropertiesClass MMSmsPropertiesPrivate MM_IS_SMS_PROPERTIES MM_IS_SMS_PROPERTIES_CLASS MM_SMS_PROPERTIES MM_SMS_PROPERTIES_CLASS MM_SMS_PROPERTIES_GET_CLASS MM_TYPE_SMS_PROPERTIES mm_sms_properties_get_type
mm-call MMCall MMCall mm_call_get_path mm_call_dup_path mm_call_get_number mm_call_dup_number mm_call_get_direction mm_call_get_state mm_call_get_state_reason mm_call_get_audio_port mm_call_dup_audio_port mm_call_get_audio_format mm_call_peek_audio_format mm_call_get_multiparty mm_call_start mm_call_start_finish mm_call_start_sync mm_call_accept mm_call_accept_finish mm_call_accept_sync mm_call_hangup mm_call_hangup_finish mm_call_hangup_sync mm_call_send_dtmf mm_call_send_dtmf_finish mm_call_send_dtmf_sync mm_call_deflect mm_call_deflect_finish mm_call_deflect_sync mm_call_join_multiparty mm_call_join_multiparty_finish mm_call_join_multiparty_sync mm_call_leave_multiparty mm_call_leave_multiparty_finish mm_call_leave_multiparty_sync MMCallClass MMCallPrivate MM_IS_CALL MM_IS_CALL_CLASS MM_CALL MM_CALL_CLASS MM_CALL_GET_CLASS MM_TYPE_CALL mm_call_get_type
mm-call-properties MMCallProperties MMCallProperties mm_call_properties_new mm_call_properties_get_number mm_call_properties_set_number mm_call_properties_get_dictionary mm_call_properties_dup mm_call_properties_new_from_dictionary mm_call_properties_new_from_string MMCallPropertiesClass MMCallPropertiesPrivate MM_IS_CALL_PROPERTIES MM_IS_CALL_PROPERTIES_CLASS MM_CALL_PROPERTIES MM_CALL_PROPERTIES_CLASS MM_CALL_PROPERTIES_GET_CLASS MM_TYPE_CALL_PROPERTIES mm_call_properties_get_type
mm-pco MMPco MMPco mm_pco_get_session_id mm_pco_is_complete mm_pco_get_data mm_pco_new mm_pco_from_variant mm_pco_to_variant mm_pco_set_session_id mm_pco_set_complete mm_pco_set_data mm_pco_list_add MMPcoClass MMPcoPrivate MM_IS_PCO MM_IS_PCO_CLASS MM_PCO MM_PCO_CLASS MM_PCO_GET_CLASS MM_TYPE_PCO mm_pco_get_type
mm-call-audio-format MMCallAudioFormat MMCallAudioFormat mm_call_audio_format_get_encoding mm_call_audio_format_get_resolution mm_call_audio_format_get_rate mm_call_audio_format_get_dictionary mm_call_audio_format_new mm_call_audio_format_new_from_dictionary mm_call_audio_format_set_encoding mm_call_audio_format_set_resolution mm_call_audio_format_set_rate mm_call_audio_format_dup MMCallAudioFormatClass MMCallAudioFormatPrivate MM_CALL_AUDIO_FORMAT MM_CALL_AUDIO_FORMAT_CLASS MM_CALL_AUDIO_FORMAT_GET_CLASS MM_IS_CALL_AUDIO_FORMAT MM_IS_CALL_AUDIO_FORMAT_CLASS MM_TYPE_CALL_AUDIO_FORMAT mm_call_audio_format_get_type
mm-3gpp-profile 3GPP profile MM_3GPP_PROFILE_ID_UNKNOWN MM3gppProfile mm_3gpp_profile_new mm_3gpp_profile_get_apn mm_3gpp_profile_set_apn mm_3gpp_profile_get_apn_type mm_3gpp_profile_set_apn_type mm_3gpp_profile_get_allowed_auth mm_3gpp_profile_set_allowed_auth mm_3gpp_profile_get_user mm_3gpp_profile_set_user mm_3gpp_profile_get_password mm_3gpp_profile_set_password mm_3gpp_profile_get_ip_type mm_3gpp_profile_set_ip_type mm_3gpp_profile_get_profile_id mm_3gpp_profile_set_profile_id mm_3gpp_profile_get_profile_name mm_3gpp_profile_set_profile_name mm_3gpp_profile_get_access_type_preference mm_3gpp_profile_set_access_type_preference mm_3gpp_profile_get_enabled mm_3gpp_profile_set_enabled mm_3gpp_profile_get_roaming_allowance mm_3gpp_profile_set_roaming_allowance mm_3gpp_profile_get_profile_source mm_3gpp_profile_set_profile_source mm_3gpp_profile_new_from_dictionary mm_3gpp_profile_new_from_string mm_3gpp_profile_consume_string mm_3gpp_profile_consume_variant mm_3gpp_profile_get_dictionary MM3gppProfileCmpFlags mm_3gpp_profile_cmp MM3gppProfileClass MM3gppProfilePrivate MM_3GPP_PROFILE MM_3GPP_PROFILE_CLASS MM_3GPP_PROFILE_GET_CLASS MM_IS_3GPP_PROFILE MM_IS_3GPP_PROFILE_CLASS MM_TYPE_3GPP_PROFILE mm_3gpp_profile_get_type
mm-modem-3gpp-profile-manager MMModem3gppProfileManager MMModem3gppProfileManager mm_modem_3gpp_profile_manager_get_path mm_modem_3gpp_profile_manager_dup_path mm_modem_3gpp_profile_manager_get_index_field mm_modem_3gpp_profile_manager_dup_index_field mm_modem_3gpp_profile_manager_list mm_modem_3gpp_profile_manager_list_finish mm_modem_3gpp_profile_manager_list_sync mm_modem_3gpp_profile_manager_set mm_modem_3gpp_profile_manager_set_finish mm_modem_3gpp_profile_manager_set_sync mm_modem_3gpp_profile_manager_delete mm_modem_3gpp_profile_manager_delete_finish mm_modem_3gpp_profile_manager_delete_sync MMModem3gppProfileManagerClass MM_IS_MODEM_3GPP_PROFILE_MANAGER MM_IS_MODEM_3GPP_PROFILE_MANAGER_CLASS MM_MODEM_3GPP_PROFILE_MANAGER MM_MODEM_3GPP_PROFILE_MANAGER_CLASS MM_MODEM_3GPP_PROFILE_MANAGER_GET_CLASS MM_TYPE_MODEM_3GPP_PROFILE_MANAGER mm_modem_3gpp_profile_manager_get_type
mm-nr5g-registration-settings MMNr5gRegistrationSettings MMNr5gRegistrationSettings mm_nr5g_registration_settings_new mm_nr5g_registration_settings_set_mico_mode mm_nr5g_registration_settings_get_mico_mode mm_nr5g_registration_settings_set_drx_cycle mm_nr5g_registration_settings_get_drx_cycle mm_nr5g_registration_settings_new_from_string mm_nr5g_registration_settings_new_from_dictionary mm_nr5g_registration_settings_get_dictionary mm_nr5g_registration_settings_cmp MMNr5gRegistrationSettingsClass MMNr5gRegistrationSettingsPrivate MM_IS_NR5G_REGISTRATION_SETTINGS MM_IS_NR5G_REGISTRATION_SETTINGS_CLASS MM_NR5G_REGISTRATION_SETTINGS MM_NR5G_REGISTRATION_SETTINGS_CLASS MM_NR5G_REGISTRATION_SETTINGS_GET_CLASS MM_TYPE_NR5G_REGISTRATION_SETTINGS mm_nr5g_registration_settings_get_type
mm-enums-types Flags and Enumerations mm_bearer_type_get_string mm_bearer_ip_method_get_string mm_bearer_ip_family_get_string mm_bearer_allowed_auth_build_string_from_mask mm_bearer_multiplex_support_get_string mm_bearer_apn_type_build_string_from_mask mm_sim_type_get_string mm_sim_esim_status_get_string mm_sim_removability_get_string mm_bearer_access_type_preference_get_string mm_bearer_roaming_allowance_build_string_from_mask mm_bearer_profile_source_get_string mm_modem_capability_build_string_from_mask mm_modem_state_get_string mm_modem_state_failed_reason_get_string mm_modem_state_change_reason_get_string mm_modem_power_state_get_string mm_modem_lock_get_string mm_modem_access_technology_build_string_from_mask mm_modem_mode_build_string_from_mask mm_modem_band_get_string mm_modem_port_type_get_string mm_modem_3gpp_registration_state_get_string mm_modem_3gpp_subscription_state_get_string mm_modem_3gpp_facility_build_string_from_mask mm_modem_3gpp_network_availability_get_string mm_modem_3gpp_ussd_session_state_get_string mm_modem_3gpp_eps_ue_mode_operation_get_string mm_modem_3gpp_packet_service_state_get_string mm_modem_3gpp_mico_mode_get_string mm_modem_3gpp_drx_cycle_get_string mm_modem_cdma_registration_state_get_string mm_modem_cdma_activation_state_get_string mm_modem_cdma_rm_protocol_get_string mm_modem_location_source_build_string_from_mask mm_modem_location_assistance_data_type_build_string_from_mask mm_modem_contacts_storage_get_string mm_modem_firmware_update_method_build_string_from_mask mm_sms_pdu_type_get_string mm_sms_state_get_string mm_sms_delivery_state_get_string mm_sms_storage_get_string mm_sms_validity_type_get_string mm_sms_cdma_teleservice_id_get_string mm_sms_cdma_service_category_get_string mm_firmware_image_type_get_string mm_oma_feature_build_string_from_mask mm_oma_session_type_get_string mm_oma_session_state_get_string mm_oma_session_state_failed_reason_get_string mm_call_direction_get_string mm_call_state_get_string mm_call_state_reason_get_string mm_cell_type_get_string MM_TYPE_BEARER_TYPE MM_TYPE_BEARER_IP_FAMILY MM_TYPE_BEARER_IP_METHOD MM_TYPE_BEARER_ALLOWED_AUTH MM_TYPE_BEARER_MULTIPLEX_SUPPORT MM_TYPE_BEARER_APN_TYPE MM_TYPE_SIM_TYPE MM_TYPE_SIM_ESIM_STATUS MM_TYPE_SIM_REMOVABILITY MM_TYPE_BEARER_ACCESS_TYPE_PREFERENCE MM_TYPE_BEARER_ROAMING_ALLOWANCE MM_TYPE_BEARER_PROFILE_SOURCE MM_TYPE_FIRMWARE_IMAGE_TYPE MM_TYPE_MODEM_3GPP_FACILITY MM_TYPE_MODEM_3GPP_NETWORK_AVAILABILITY MM_TYPE_MODEM_3GPP_REGISTRATION_STATE MM_TYPE_MODEM_3GPP_SUBSCRIPTION_STATE MM_TYPE_MODEM_3GPP_USSD_SESSION_STATE MM_TYPE_MODEM_3GPP_EPS_UE_MODE_OPERATION MM_TYPE_MODEM_3GPP_PACKET_SERVICE_STATE MM_TYPE_MODEM_3GPP_MICO_MODE MM_TYPE_MODEM_3GPP_DRX_CYCLE MM_TYPE_MODEM_ACCESS_TECHNOLOGY MM_TYPE_MODEM_BAND MM_TYPE_MODEM_CAPABILITY MM_TYPE_MODEM_CDMA_ACTIVATION_STATE MM_TYPE_MODEM_CDMA_REGISTRATION_STATE MM_TYPE_MODEM_CDMA_RM_PROTOCOL MM_TYPE_MODEM_CONTACTS_STORAGE MM_TYPE_MODEM_LOCATION_SOURCE MM_TYPE_MODEM_LOCATION_ASSISTANCE_DATA_TYPE MM_TYPE_MODEM_LOCK MM_TYPE_MODEM_MODE MM_TYPE_MODEM_STATE MM_TYPE_MODEM_STATE_FAILED_REASON MM_TYPE_MODEM_STATE_CHANGE_REASON MM_TYPE_MODEM_POWER_STATE MM_TYPE_MODEM_PORT_TYPE MM_TYPE_SMS_DELIVERY_STATE MM_TYPE_SMS_PDU_TYPE MM_TYPE_SMS_STATE MM_TYPE_SMS_STORAGE MM_TYPE_SMS_VALIDITY_TYPE MM_TYPE_SMS_CDMA_TELESERVICE_ID MM_TYPE_SMS_CDMA_SERVICE_CATEGORY MM_TYPE_OMA_FEATURE MM_TYPE_OMA_SESSION_STATE MM_TYPE_OMA_SESSION_STATE_FAILED_REASON MM_TYPE_OMA_SESSION_TYPE MM_TYPE_CALL_DIRECTION MM_TYPE_CALL_STATE MM_TYPE_CALL_STATE_REASON MM_TYPE_MODEM_FIRMWARE_UPDATE_METHOD MM_TYPE_CELL_TYPE mm_bearer_type_get_type mm_bearer_ip_family_get_type mm_bearer_ip_method_get_type mm_bearer_allowed_auth_get_type mm_bearer_multiplex_support_get_type mm_bearer_apn_type_get_type mm_sim_type_get_type mm_sim_esim_status_get_type mm_sim_removability_get_type mm_bearer_access_type_preference_get_type mm_bearer_roaming_allowance_get_type mm_bearer_profile_source_get_type mm_firmware_image_type_get_type mm_modem_3gpp_facility_get_type mm_modem_3gpp_network_availability_get_type mm_modem_3gpp_registration_state_get_type mm_modem_3gpp_subscription_state_get_type mm_modem_3gpp_ussd_session_state_get_type mm_modem_3gpp_eps_ue_mode_operation_get_type mm_modem_3gpp_packet_service_state_get_type mm_modem_3gpp_mico_mode_get_type mm_modem_3gpp_drx_cycle_get_type mm_modem_access_technology_get_type mm_modem_band_get_type mm_modem_capability_get_type mm_modem_cdma_activation_state_get_type mm_modem_cdma_registration_state_get_type mm_modem_cdma_rm_protocol_get_type mm_modem_contacts_storage_get_type mm_modem_location_source_get_type mm_modem_location_assistance_data_type_get_type mm_modem_lock_get_type mm_modem_mode_get_type mm_modem_state_change_reason_get_type mm_modem_state_get_type mm_modem_state_failed_reason_get_type mm_modem_power_state_get_type mm_modem_port_type_get_type mm_sms_delivery_state_get_type mm_sms_pdu_type_get_type mm_sms_state_get_type mm_sms_storage_get_type mm_sms_validity_type_get_type mm_sms_cdma_teleservice_id_get_type mm_sms_cdma_service_category_get_type mm_oma_feature_get_type mm_oma_session_state_failed_reason_get_type mm_oma_session_state_get_type mm_oma_session_type_get_type mm_call_direction_get_type mm_call_state_get_type mm_call_state_reason_get_type mm_modem_firmware_update_method_get_type mm_cell_type_get_type
mm-errors-types mm_cdma_activation_error_quark mm_connection_error_quark mm_core_error_quark mm_message_error_quark mm_mobile_equipment_error_quark mm_serial_error_quark MM_CDMA_ACTIVATION_ERROR MM_CONNECTION_ERROR MM_CORE_ERROR MM_MESSAGE_ERROR MM_MOBILE_EQUIPMENT_ERROR MM_SERIAL_ERROR MM_TYPE_CDMA_ACTIVATION_ERROR MM_TYPE_CONNECTION_ERROR MM_TYPE_CORE_ERROR MM_TYPE_MESSAGE_ERROR MM_TYPE_MOBILE_EQUIPMENT_ERROR MM_TYPE_SERIAL_ERROR mm_cdma_activation_error_get_type mm_connection_error_get_type mm_core_error_get_type mm_message_error_get_type mm_mobile_equipment_error_get_type mm_serial_error_get_type
mm-compat Deprecated Interface mm_simple_connect_properties_get_number mm_simple_connect_properties_set_number mm_bearer_properties_get_number mm_bearer_properties_set_number mm_call_properties_get_direction mm_call_properties_set_direction mm_call_properties_get_state mm_call_properties_set_state mm_call_properties_get_state_reason mm_call_properties_set_state_reason mm_location_gps_nmea_build_full mm_pco_list_free mm_simple_status_get_3gpp_subscription_state mm_modem_3gpp_get_subscription_state mm_modem_peek_pending_network_initiated_sessions mm_modem_get_pending_network_initiated_sessions
MmGdbusBearer MmGdbusBearer MmGdbusBearer MmGdbusBearerIface mm_gdbus_bearer_get_interface mm_gdbus_bearer_dup_interface mm_gdbus_bearer_get_ip4_config mm_gdbus_bearer_dup_ip4_config mm_gdbus_bearer_get_ip6_config mm_gdbus_bearer_dup_ip6_config mm_gdbus_bearer_get_ip_timeout mm_gdbus_bearer_get_properties mm_gdbus_bearer_dup_properties mm_gdbus_bearer_get_connected mm_gdbus_bearer_get_connection_error mm_gdbus_bearer_dup_connection_error mm_gdbus_bearer_get_suspended mm_gdbus_bearer_get_multiplexed mm_gdbus_bearer_get_bearer_type mm_gdbus_bearer_get_profile_id mm_gdbus_bearer_get_stats mm_gdbus_bearer_dup_stats mm_gdbus_bearer_get_reload_stats_supported mm_gdbus_bearer_call_connect mm_gdbus_bearer_call_connect_finish mm_gdbus_bearer_call_connect_sync mm_gdbus_bearer_call_disconnect mm_gdbus_bearer_call_disconnect_finish mm_gdbus_bearer_call_disconnect_sync mm_gdbus_bearer_interface_info mm_gdbus_bearer_set_connected mm_gdbus_bearer_set_connection_error mm_gdbus_bearer_set_interface mm_gdbus_bearer_set_ip4_config mm_gdbus_bearer_set_ip6_config mm_gdbus_bearer_set_ip_timeout mm_gdbus_bearer_set_properties mm_gdbus_bearer_set_suspended mm_gdbus_bearer_set_bearer_type mm_gdbus_bearer_set_profile_id mm_gdbus_bearer_set_stats mm_gdbus_bearer_set_multiplexed mm_gdbus_bearer_set_reload_stats_supported mm_gdbus_bearer_override_properties mm_gdbus_bearer_complete_connect mm_gdbus_bearer_complete_disconnect MM_GDBUS_BEARER MM_GDBUS_BEARER_GET_IFACE MM_GDBUS_IS_BEARER MM_GDBUS_TYPE_BEARER mm_gdbus_bearer_get_type
MmGdbusBearerProxy MmGdbusBearerProxy MmGdbusBearerProxy mm_gdbus_bearer_proxy_new mm_gdbus_bearer_proxy_new_finish mm_gdbus_bearer_proxy_new_for_bus mm_gdbus_bearer_proxy_new_for_bus_finish mm_gdbus_bearer_proxy_new_for_bus_sync mm_gdbus_bearer_proxy_new_sync MmGdbusBearerProxyClass MM_GDBUS_BEARER_PROXY MM_GDBUS_BEARER_PROXY_CLASS MM_GDBUS_BEARER_PROXY_GET_CLASS MM_GDBUS_IS_BEARER_PROXY MM_GDBUS_IS_BEARER_PROXY_CLASS MM_GDBUS_TYPE_BEARER_PROXY MmGdbusBearerProxyPrivate mm_gdbus_bearer_proxy_get_type
MmGdbusBearerSkeleton MmGdbusBearerSkeleton MmGdbusBearerSkeleton mm_gdbus_bearer_skeleton_new MmGdbusBearerSkeletonClass MM_GDBUS_BEARER_SKELETON MM_GDBUS_BEARER_SKELETON_CLASS MM_GDBUS_BEARER_SKELETON_GET_CLASS MM_GDBUS_IS_BEARER_SKELETON MM_GDBUS_IS_BEARER_SKELETON_CLASS MM_GDBUS_TYPE_BEARER_SKELETON MmGdbusBearerSkeletonPrivate mm_gdbus_bearer_skeleton_get_type
MmGdbusOrgFreedesktopModemManager1 MmGdbusOrgFreedesktopModemManager1 MmGdbusOrgFreedesktopModemManager1 MmGdbusOrgFreedesktopModemManager1Iface mm_gdbus_org_freedesktop_modem_manager1_dup_version mm_gdbus_org_freedesktop_modem_manager1_get_version mm_gdbus_org_freedesktop_modem_manager1_call_scan_devices mm_gdbus_org_freedesktop_modem_manager1_call_scan_devices_finish mm_gdbus_org_freedesktop_modem_manager1_call_scan_devices_sync mm_gdbus_org_freedesktop_modem_manager1_call_inhibit_device mm_gdbus_org_freedesktop_modem_manager1_call_inhibit_device_finish mm_gdbus_org_freedesktop_modem_manager1_call_inhibit_device_sync mm_gdbus_org_freedesktop_modem_manager1_call_set_logging mm_gdbus_org_freedesktop_modem_manager1_call_set_logging_finish mm_gdbus_org_freedesktop_modem_manager1_call_set_logging_sync mm_gdbus_org_freedesktop_modem_manager1_call_report_kernel_event mm_gdbus_org_freedesktop_modem_manager1_call_report_kernel_event_finish mm_gdbus_org_freedesktop_modem_manager1_call_report_kernel_event_sync mm_gdbus_org_freedesktop_modem_manager1_set_version mm_gdbus_org_freedesktop_modem_manager1_override_properties mm_gdbus_org_freedesktop_modem_manager1_complete_inhibit_device mm_gdbus_org_freedesktop_modem_manager1_complete_scan_devices mm_gdbus_org_freedesktop_modem_manager1_complete_set_logging mm_gdbus_org_freedesktop_modem_manager1_complete_report_kernel_event mm_gdbus_org_freedesktop_modem_manager1_interface_info MM_GDBUS_IS_ORG_FREEDESKTOP_MODEM_MANAGER1 MM_GDBUS_ORG_FREEDESKTOP_MODEM_MANAGER1 MM_GDBUS_ORG_FREEDESKTOP_MODEM_MANAGER1_GET_IFACE MM_GDBUS_TYPE_ORG_FREEDESKTOP_MODEM_MANAGER1 mm_gdbus_org_freedesktop_modem_manager1_get_type
MmGdbusOrgFreedesktopModemManager1Proxy MmGdbusOrgFreedesktopModemManager1Proxy MmGdbusOrgFreedesktopModemManager1Proxy mm_gdbus_org_freedesktop_modem_manager1_proxy_new mm_gdbus_org_freedesktop_modem_manager1_proxy_new_finish mm_gdbus_org_freedesktop_modem_manager1_proxy_new_for_bus mm_gdbus_org_freedesktop_modem_manager1_proxy_new_for_bus_finish mm_gdbus_org_freedesktop_modem_manager1_proxy_new_for_bus_sync mm_gdbus_org_freedesktop_modem_manager1_proxy_new_sync MmGdbusOrgFreedesktopModemManager1ProxyClass MM_GDBUS_IS_ORG_FREEDESKTOP_MODEM_MANAGER1_PROXY MM_GDBUS_IS_ORG_FREEDESKTOP_MODEM_MANAGER1_PROXY_CLASS MM_GDBUS_ORG_FREEDESKTOP_MODEM_MANAGER1_PROXY MM_GDBUS_ORG_FREEDESKTOP_MODEM_MANAGER1_PROXY_CLASS MM_GDBUS_ORG_FREEDESKTOP_MODEM_MANAGER1_PROXY_GET_CLASS MM_GDBUS_TYPE_ORG_FREEDESKTOP_MODEM_MANAGER1_PROXY MmGdbusOrgFreedesktopModemManager1ProxyPrivate mm_gdbus_org_freedesktop_modem_manager1_proxy_get_type
MmGdbusOrgFreedesktopModemManager1Skeleton MmGdbusOrgFreedesktopModemManager1Skeleton MmGdbusOrgFreedesktopModemManager1Skeleton mm_gdbus_org_freedesktop_modem_manager1_skeleton_new MmGdbusOrgFreedesktopModemManager1SkeletonClass MM_GDBUS_IS_ORG_FREEDESKTOP_MODEM_MANAGER1_SKELETON MM_GDBUS_IS_ORG_FREEDESKTOP_MODEM_MANAGER1_SKELETON_CLASS MM_GDBUS_ORG_FREEDESKTOP_MODEM_MANAGER1_SKELETON MM_GDBUS_ORG_FREEDESKTOP_MODEM_MANAGER1_SKELETON_CLASS MM_GDBUS_ORG_FREEDESKTOP_MODEM_MANAGER1_SKELETON_GET_CLASS MM_GDBUS_TYPE_ORG_FREEDESKTOP_MODEM_MANAGER1_SKELETON MmGdbusOrgFreedesktopModemManager1SkeletonPrivate mm_gdbus_org_freedesktop_modem_manager1_skeleton_get_type
MmGdbusModem3gpp MmGdbusModem3gpp MmGdbusModem3gpp MmGdbusModem3gppIface mm_gdbus_modem3gpp_get_registration_state mm_gdbus_modem3gpp_get_imei mm_gdbus_modem3gpp_dup_imei mm_gdbus_modem3gpp_get_operator_code mm_gdbus_modem3gpp_dup_operator_code mm_gdbus_modem3gpp_get_operator_name mm_gdbus_modem3gpp_dup_operator_name mm_gdbus_modem3gpp_get_enabled_facility_locks mm_gdbus_modem3gpp_get_subscription_state mm_gdbus_modem3gpp_get_eps_ue_mode_operation mm_gdbus_modem3gpp_get_pco mm_gdbus_modem3gpp_dup_pco mm_gdbus_modem3gpp_get_initial_eps_bearer mm_gdbus_modem3gpp_dup_initial_eps_bearer mm_gdbus_modem3gpp_get_initial_eps_bearer_settings mm_gdbus_modem3gpp_dup_initial_eps_bearer_settings mm_gdbus_modem3gpp_get_packet_service_state mm_gdbus_modem3gpp_dup_nr5g_registration_settings mm_gdbus_modem3gpp_get_nr5g_registration_settings mm_gdbus_modem3gpp_call_register mm_gdbus_modem3gpp_call_register_finish mm_gdbus_modem3gpp_call_register_sync mm_gdbus_modem3gpp_call_scan mm_gdbus_modem3gpp_call_scan_finish mm_gdbus_modem3gpp_call_scan_sync mm_gdbus_modem3gpp_call_set_carrier_lock mm_gdbus_modem3gpp_call_set_carrier_lock_finish mm_gdbus_modem3gpp_call_set_carrier_lock_sync mm_gdbus_modem3gpp_call_set_eps_ue_mode_operation mm_gdbus_modem3gpp_call_set_eps_ue_mode_operation_finish mm_gdbus_modem3gpp_call_set_eps_ue_mode_operation_sync mm_gdbus_modem3gpp_call_set_initial_eps_bearer_settings mm_gdbus_modem3gpp_call_set_initial_eps_bearer_settings_finish mm_gdbus_modem3gpp_call_set_initial_eps_bearer_settings_sync mm_gdbus_modem3gpp_call_disable_facility_lock mm_gdbus_modem3gpp_call_disable_facility_lock_finish mm_gdbus_modem3gpp_call_disable_facility_lock_sync mm_gdbus_modem3gpp_call_set_packet_service_state mm_gdbus_modem3gpp_call_set_packet_service_state_finish mm_gdbus_modem3gpp_call_set_packet_service_state_sync mm_gdbus_modem3gpp_call_set_nr5g_registration_settings mm_gdbus_modem3gpp_call_set_nr5g_registration_settings_finish mm_gdbus_modem3gpp_call_set_nr5g_registration_settings_sync mm_gdbus_modem3gpp_complete_register mm_gdbus_modem3gpp_complete_scan mm_gdbus_modem3gpp_complete_set_eps_ue_mode_operation mm_gdbus_modem3gpp_complete_set_initial_eps_bearer_settings mm_gdbus_modem3gpp_complete_disable_facility_lock mm_gdbus_modem3gpp_complete_set_packet_service_state mm_gdbus_modem3gpp_complete_set_nr5g_registration_settings mm_gdbus_modem3gpp_interface_info mm_gdbus_modem3gpp_override_properties mm_gdbus_modem3gpp_set_enabled_facility_locks mm_gdbus_modem3gpp_set_imei mm_gdbus_modem3gpp_set_operator_code mm_gdbus_modem3gpp_set_operator_name mm_gdbus_modem3gpp_set_registration_state mm_gdbus_modem3gpp_set_subscription_state mm_gdbus_modem3gpp_set_eps_ue_mode_operation mm_gdbus_modem3gpp_set_pco mm_gdbus_modem3gpp_set_initial_eps_bearer mm_gdbus_modem3gpp_set_initial_eps_bearer_settings mm_gdbus_modem3gpp_set_packet_service_state mm_gdbus_modem3gpp_set_nr5g_registration_settings MM_GDBUS_IS_MODEM3GPP MM_GDBUS_MODEM3GPP MM_GDBUS_MODEM3GPP_GET_IFACE MM_GDBUS_TYPE_MODEM3GPP mm_gdbus_modem3gpp_get_type
MmGdbusModem3gppProxy MmGdbusModem3gppProxy MmGdbusModem3gppProxy mm_gdbus_modem3gpp_proxy_new mm_gdbus_modem3gpp_proxy_new_finish mm_gdbus_modem3gpp_proxy_new_for_bus mm_gdbus_modem3gpp_proxy_new_for_bus_finish mm_gdbus_modem3gpp_proxy_new_for_bus_sync mm_gdbus_modem3gpp_proxy_new_sync MmGdbusModem3gppProxyClass MM_GDBUS_IS_MODEM3GPP_PROXY MM_GDBUS_IS_MODEM3GPP_PROXY_CLASS MM_GDBUS_MODEM3GPP_PROXY MM_GDBUS_MODEM3GPP_PROXY_CLASS MM_GDBUS_MODEM3GPP_PROXY_GET_CLASS MM_GDBUS_TYPE_MODEM3GPP_PROXY MmGdbusModem3gppProxyPrivate mm_gdbus_modem3gpp_proxy_get_type
MmGdbusModem3gppSkeleton MmGdbusModem3gppSkeleton MmGdbusModem3gppSkeleton mm_gdbus_modem3gpp_skeleton_new MmGdbusModem3gppSkeletonClass MM_GDBUS_IS_MODEM3GPP_SKELETON MM_GDBUS_IS_MODEM3GPP_SKELETON_CLASS MM_GDBUS_MODEM3GPP_SKELETON MM_GDBUS_MODEM3GPP_SKELETON_CLASS MM_GDBUS_MODEM3GPP_SKELETON_GET_CLASS MM_GDBUS_TYPE_MODEM3GPP_SKELETON MmGdbusModem3gppSkeletonPrivate mm_gdbus_modem3gpp_skeleton_get_type
MmGdbusModem3gppUssd MmGdbusModem3gppUssd MmGdbusModem3gppUssd MmGdbusModem3gppUssdIface mm_gdbus_modem3gpp_ussd_get_state mm_gdbus_modem3gpp_ussd_get_network_request mm_gdbus_modem3gpp_ussd_dup_network_request mm_gdbus_modem3gpp_ussd_get_network_notification mm_gdbus_modem3gpp_ussd_dup_network_notification mm_gdbus_modem3gpp_ussd_call_initiate mm_gdbus_modem3gpp_ussd_call_initiate_finish mm_gdbus_modem3gpp_ussd_call_initiate_sync mm_gdbus_modem3gpp_ussd_call_respond mm_gdbus_modem3gpp_ussd_call_respond_finish mm_gdbus_modem3gpp_ussd_call_respond_sync mm_gdbus_modem3gpp_ussd_call_cancel mm_gdbus_modem3gpp_ussd_call_cancel_finish mm_gdbus_modem3gpp_ussd_call_cancel_sync mm_gdbus_modem3gpp_ussd_complete_cancel mm_gdbus_modem3gpp_ussd_complete_initiate mm_gdbus_modem3gpp_ussd_complete_respond mm_gdbus_modem3gpp_ussd_interface_info mm_gdbus_modem3gpp_ussd_override_properties mm_gdbus_modem3gpp_ussd_set_network_notification mm_gdbus_modem3gpp_ussd_set_network_request mm_gdbus_modem3gpp_ussd_set_state MM_GDBUS_IS_MODEM3GPP_USSD MM_GDBUS_MODEM3GPP_USSD MM_GDBUS_MODEM3GPP_USSD_GET_IFACE MM_GDBUS_TYPE_MODEM3GPP_USSD mm_gdbus_modem3gpp_ussd_get_type
MmGdbusModem3gppUssdProxy MmGdbusModem3gppUssdProxy MmGdbusModem3gppUssdProxy mm_gdbus_modem3gpp_ussd_proxy_new mm_gdbus_modem3gpp_ussd_proxy_new_finish mm_gdbus_modem3gpp_ussd_proxy_new_for_bus mm_gdbus_modem3gpp_ussd_proxy_new_for_bus_finish mm_gdbus_modem3gpp_ussd_proxy_new_for_bus_sync mm_gdbus_modem3gpp_ussd_proxy_new_sync MmGdbusModem3gppUssdProxyClass MM_GDBUS_IS_MODEM3GPP_USSD_PROXY MM_GDBUS_IS_MODEM3GPP_USSD_PROXY_CLASS MM_GDBUS_MODEM3GPP_USSD_PROXY MM_GDBUS_MODEM3GPP_USSD_PROXY_CLASS MM_GDBUS_MODEM3GPP_USSD_PROXY_GET_CLASS MM_GDBUS_TYPE_MODEM3GPP_USSD_PROXY MmGdbusModem3gppUssdProxyPrivate mm_gdbus_modem3gpp_ussd_proxy_get_type
MmGdbusModem3gppUssdSkeleton MmGdbusModem3gppUssdSkeleton MmGdbusModem3gppUssdSkeleton mm_gdbus_modem3gpp_ussd_skeleton_new MmGdbusModem3gppUssdSkeletonClass MM_GDBUS_IS_MODEM3GPP_USSD_SKELETON MM_GDBUS_IS_MODEM3GPP_USSD_SKELETON_CLASS MM_GDBUS_MODEM3GPP_USSD_SKELETON MM_GDBUS_MODEM3GPP_USSD_SKELETON_CLASS MM_GDBUS_MODEM3GPP_USSD_SKELETON_GET_CLASS MM_GDBUS_TYPE_MODEM3GPP_USSD_SKELETON MmGdbusModem3gppUssdSkeletonPrivate mm_gdbus_modem3gpp_ussd_skeleton_get_type
MmGdbusModem3gppProfileManager MmGdbusModem3gppProfileManager MmGdbusModem3gppProfileManager MmGdbusModem3gppProfileManagerIface mm_gdbus_modem3gpp_profile_manager_dup_index_field mm_gdbus_modem3gpp_profile_manager_get_index_field mm_gdbus_modem3gpp_profile_manager_call_delete mm_gdbus_modem3gpp_profile_manager_call_delete_finish mm_gdbus_modem3gpp_profile_manager_call_delete_sync mm_gdbus_modem3gpp_profile_manager_call_list mm_gdbus_modem3gpp_profile_manager_call_list_finish mm_gdbus_modem3gpp_profile_manager_call_list_sync mm_gdbus_modem3gpp_profile_manager_call_set mm_gdbus_modem3gpp_profile_manager_call_set_finish mm_gdbus_modem3gpp_profile_manager_call_set_sync mm_gdbus_modem3gpp_profile_manager_emit_updated mm_gdbus_modem3gpp_profile_manager_complete_delete mm_gdbus_modem3gpp_profile_manager_complete_list mm_gdbus_modem3gpp_profile_manager_complete_set mm_gdbus_modem3gpp_profile_manager_interface_info mm_gdbus_modem3gpp_profile_manager_override_properties mm_gdbus_modem3gpp_profile_manager_set_index_field MM_GDBUS_IS_MODEM3GPP_PROFILE_MANAGER MM_GDBUS_MODEM3GPP_PROFILE_MANAGER MM_GDBUS_MODEM3GPP_PROFILE_MANAGER_GET_IFACE MM_GDBUS_TYPE_MODEM3GPP_PROFILE_MANAGER mm_gdbus_modem3gpp_profile_manager_get_type
MmGdbusModem3gppProfileManagerProxy MmGdbusModem3gppProfileManagerProxy MmGdbusModem3gppProfileManagerProxy mm_gdbus_modem3gpp_profile_manager_proxy_new mm_gdbus_modem3gpp_profile_manager_proxy_new_finish mm_gdbus_modem3gpp_profile_manager_proxy_new_for_bus mm_gdbus_modem3gpp_profile_manager_proxy_new_for_bus_finish mm_gdbus_modem3gpp_profile_manager_proxy_new_for_bus_sync mm_gdbus_modem3gpp_profile_manager_proxy_new_sync MmGdbusModem3gppProfileManagerProxyClass MM_GDBUS_IS_MODEM3GPP_PROFILE_MANAGER_PROXY MM_GDBUS_IS_MODEM3GPP_PROFILE_MANAGER_PROXY_CLASS MM_GDBUS_MODEM3GPP_PROFILE_MANAGER_PROXY MM_GDBUS_MODEM3GPP_PROFILE_MANAGER_PROXY_CLASS MM_GDBUS_MODEM3GPP_PROFILE_MANAGER_PROXY_GET_CLASS MM_GDBUS_TYPE_MODEM3GPP_PROFILE_MANAGER_PROXY MmGdbusModem3gppProfileManagerProxyPrivate mm_gdbus_modem3gpp_profile_manager_proxy_get_type
MmGdbusModem3gppProfileManagerSkeleton MmGdbusModem3gppProfileManagerSkeleton MmGdbusModem3gppProfileManagerSkeleton mm_gdbus_modem3gpp_profile_manager_skeleton_new MmGdbusModem3gppProfileManagerSkeletonClass MM_GDBUS_IS_MODEM3GPP_PROFILE_MANAGER_SKELETON MM_GDBUS_IS_MODEM3GPP_PROFILE_MANAGER_SKELETON_CLASS MM_GDBUS_MODEM3GPP_PROFILE_MANAGER_SKELETON MM_GDBUS_MODEM3GPP_PROFILE_MANAGER_SKELETON_CLASS MM_GDBUS_MODEM3GPP_PROFILE_MANAGER_SKELETON_GET_CLASS MM_GDBUS_TYPE_MODEM3GPP_PROFILE_MANAGER_SKELETON MmGdbusModem3gppProfileManagerSkeletonPrivate mm_gdbus_modem3gpp_profile_manager_skeleton_get_type
MmGdbusModem MmGdbusModem MmGdbusModem MmGdbusModemIface mm_gdbus_modem_get_access_technologies mm_gdbus_modem_get_bearers mm_gdbus_modem_dup_bearers mm_gdbus_modem_get_current_modes mm_gdbus_modem_dup_current_modes mm_gdbus_modem_get_current_bands mm_gdbus_modem_dup_current_bands mm_gdbus_modem_get_current_capabilities mm_gdbus_modem_get_device mm_gdbus_modem_dup_device mm_gdbus_modem_get_physdev mm_gdbus_modem_dup_physdev mm_gdbus_modem_get_device_identifier mm_gdbus_modem_dup_device_identifier mm_gdbus_modem_get_drivers mm_gdbus_modem_dup_drivers mm_gdbus_modem_get_equipment_identifier mm_gdbus_modem_dup_equipment_identifier mm_gdbus_modem_get_manufacturer mm_gdbus_modem_dup_manufacturer mm_gdbus_modem_get_max_active_bearers mm_gdbus_modem_get_max_active_multiplexed_bearers mm_gdbus_modem_get_max_bearers mm_gdbus_modem_get_model mm_gdbus_modem_dup_model mm_gdbus_modem_get_own_numbers mm_gdbus_modem_dup_own_numbers mm_gdbus_modem_get_plugin mm_gdbus_modem_dup_plugin mm_gdbus_modem_get_power_state mm_gdbus_modem_get_primary_port mm_gdbus_modem_dup_primary_port mm_gdbus_modem_get_ports mm_gdbus_modem_dup_ports mm_gdbus_modem_get_revision mm_gdbus_modem_dup_revision mm_gdbus_modem_get_carrier_configuration mm_gdbus_modem_dup_carrier_configuration mm_gdbus_modem_get_carrier_configuration_revision mm_gdbus_modem_dup_carrier_configuration_revision mm_gdbus_modem_get_hardware_revision mm_gdbus_modem_dup_hardware_revision mm_gdbus_modem_get_signal_quality mm_gdbus_modem_dup_signal_quality mm_gdbus_modem_get_sim mm_gdbus_modem_dup_sim mm_gdbus_modem_dup_sim_slots mm_gdbus_modem_get_sim_slots mm_gdbus_modem_get_primary_sim_slot mm_gdbus_modem_get_supported_capabilities mm_gdbus_modem_dup_supported_capabilities mm_gdbus_modem_get_state mm_gdbus_modem_get_state_failed_reason mm_gdbus_modem_get_supported_bands mm_gdbus_modem_dup_supported_bands mm_gdbus_modem_get_supported_ip_families mm_gdbus_modem_get_supported_modes mm_gdbus_modem_dup_supported_modes mm_gdbus_modem_get_unlock_required mm_gdbus_modem_get_unlock_retries mm_gdbus_modem_dup_unlock_retries mm_gdbus_modem_call_enable mm_gdbus_modem_call_enable_finish mm_gdbus_modem_call_enable_sync mm_gdbus_modem_call_set_power_state mm_gdbus_modem_call_set_power_state_finish mm_gdbus_modem_call_set_power_state_sync mm_gdbus_modem_call_create_bearer mm_gdbus_modem_call_create_bearer_finish mm_gdbus_modem_call_create_bearer_sync mm_gdbus_modem_call_delete_bearer mm_gdbus_modem_call_delete_bearer_finish mm_gdbus_modem_call_delete_bearer_sync mm_gdbus_modem_call_list_bearers mm_gdbus_modem_call_list_bearers_finish mm_gdbus_modem_call_list_bearers_sync mm_gdbus_modem_call_reset mm_gdbus_modem_call_reset_finish mm_gdbus_modem_call_reset_sync mm_gdbus_modem_call_factory_reset mm_gdbus_modem_call_factory_reset_finish mm_gdbus_modem_call_factory_reset_sync mm_gdbus_modem_call_set_current_modes mm_gdbus_modem_call_set_current_modes_finish mm_gdbus_modem_call_set_current_modes_sync mm_gdbus_modem_call_set_current_bands mm_gdbus_modem_call_set_current_bands_finish mm_gdbus_modem_call_set_current_bands_sync mm_gdbus_modem_call_set_current_capabilities mm_gdbus_modem_call_set_current_capabilities_finish mm_gdbus_modem_call_set_current_capabilities_sync mm_gdbus_modem_call_set_primary_sim_slot mm_gdbus_modem_call_set_primary_sim_slot_finish mm_gdbus_modem_call_set_primary_sim_slot_sync mm_gdbus_modem_call_command mm_gdbus_modem_call_command_finish mm_gdbus_modem_call_command_sync mm_gdbus_modem_call_get_cell_info mm_gdbus_modem_call_get_cell_info_finish mm_gdbus_modem_call_get_cell_info_sync mm_gdbus_modem_set_access_technologies mm_gdbus_modem_set_bearers mm_gdbus_modem_set_current_modes mm_gdbus_modem_set_current_bands mm_gdbus_modem_set_current_capabilities mm_gdbus_modem_set_device mm_gdbus_modem_set_device_identifier mm_gdbus_modem_set_drivers mm_gdbus_modem_set_equipment_identifier mm_gdbus_modem_set_manufacturer mm_gdbus_modem_set_max_active_bearers mm_gdbus_modem_set_max_active_multiplexed_bearers mm_gdbus_modem_set_max_bearers mm_gdbus_modem_set_model mm_gdbus_modem_set_own_numbers mm_gdbus_modem_set_plugin mm_gdbus_modem_set_primary_port mm_gdbus_modem_set_ports mm_gdbus_modem_set_revision mm_gdbus_modem_set_carrier_configuration mm_gdbus_modem_set_carrier_configuration_revision mm_gdbus_modem_set_hardware_revision mm_gdbus_modem_set_signal_quality mm_gdbus_modem_set_sim mm_gdbus_modem_set_sim_slots mm_gdbus_modem_set_primary_sim_slot mm_gdbus_modem_set_supported_capabilities mm_gdbus_modem_set_state mm_gdbus_modem_set_state_failed_reason mm_gdbus_modem_set_power_state mm_gdbus_modem_set_supported_bands mm_gdbus_modem_set_supported_ip_families mm_gdbus_modem_set_supported_modes mm_gdbus_modem_set_unlock_required mm_gdbus_modem_set_unlock_retries mm_gdbus_modem_emit_state_changed mm_gdbus_modem_complete_command mm_gdbus_modem_complete_create_bearer mm_gdbus_modem_complete_delete_bearer mm_gdbus_modem_complete_enable mm_gdbus_modem_complete_set_power_state mm_gdbus_modem_complete_factory_reset mm_gdbus_modem_complete_list_bearers mm_gdbus_modem_complete_reset mm_gdbus_modem_complete_set_current_modes mm_gdbus_modem_complete_set_current_bands mm_gdbus_modem_complete_set_current_capabilities mm_gdbus_modem_complete_set_primary_sim_slot mm_gdbus_modem_complete_get_cell_info mm_gdbus_modem_interface_info mm_gdbus_modem_override_properties MM_GDBUS_IS_MODEM MM_GDBUS_MODEM MM_GDBUS_MODEM_GET_IFACE MM_GDBUS_TYPE_MODEM mm_gdbus_modem_get_type
MmGdbusModemProxy MmGdbusModemProxy MmGdbusModemProxy mm_gdbus_modem_proxy_new mm_gdbus_modem_proxy_new_finish mm_gdbus_modem_proxy_new_for_bus mm_gdbus_modem_proxy_new_for_bus_finish mm_gdbus_modem_proxy_new_for_bus_sync mm_gdbus_modem_proxy_new_sync MmGdbusModemProxyClass MM_GDBUS_IS_MODEM_PROXY MM_GDBUS_IS_MODEM_PROXY_CLASS MM_GDBUS_MODEM_PROXY MM_GDBUS_MODEM_PROXY_CLASS MM_GDBUS_MODEM_PROXY_GET_CLASS MM_GDBUS_TYPE_MODEM_PROXY MmGdbusModemProxyPrivate mm_gdbus_modem_proxy_get_type
MmGdbusModemSkeleton MmGdbusModemSkeleton MmGdbusModemSkeleton mm_gdbus_modem_skeleton_new MmGdbusModemSkeletonClass MM_GDBUS_IS_MODEM_SKELETON MM_GDBUS_IS_MODEM_SKELETON_CLASS MM_GDBUS_MODEM_SKELETON MM_GDBUS_MODEM_SKELETON_CLASS MM_GDBUS_MODEM_SKELETON_GET_CLASS MM_GDBUS_TYPE_MODEM_SKELETON MmGdbusModemSkeletonPrivate mm_gdbus_modem_skeleton_get_type
MmGdbusModemCdma MmGdbusModemCdma MmGdbusModemCdma MmGdbusModemCdmaIface mm_gdbus_modem_cdma_get_cdma1x_registration_state mm_gdbus_modem_cdma_get_evdo_registration_state mm_gdbus_modem_cdma_get_activation_state mm_gdbus_modem_cdma_get_esn mm_gdbus_modem_cdma_dup_esn mm_gdbus_modem_cdma_get_meid mm_gdbus_modem_cdma_dup_meid mm_gdbus_modem_cdma_get_nid mm_gdbus_modem_cdma_get_sid mm_gdbus_modem_cdma_call_activate mm_gdbus_modem_cdma_call_activate_finish mm_gdbus_modem_cdma_call_activate_sync mm_gdbus_modem_cdma_call_activate_manual mm_gdbus_modem_cdma_call_activate_manual_finish mm_gdbus_modem_cdma_call_activate_manual_sync mm_gdbus_modem_cdma_set_activation_state mm_gdbus_modem_cdma_set_cdma1x_registration_state mm_gdbus_modem_cdma_set_esn mm_gdbus_modem_cdma_set_evdo_registration_state mm_gdbus_modem_cdma_set_meid mm_gdbus_modem_cdma_set_nid mm_gdbus_modem_cdma_set_sid mm_gdbus_modem_cdma_emit_activation_state_changed mm_gdbus_modem_cdma_complete_activate mm_gdbus_modem_cdma_complete_activate_manual mm_gdbus_modem_cdma_interface_info mm_gdbus_modem_cdma_override_properties MM_GDBUS_IS_MODEM_CDMA MM_GDBUS_MODEM_CDMA MM_GDBUS_MODEM_CDMA_GET_IFACE MM_GDBUS_TYPE_MODEM_CDMA mm_gdbus_modem_cdma_get_type
MmGdbusModemCdmaProxy MmGdbusModemCdmaProxy MmGdbusModemCdmaProxy mm_gdbus_modem_cdma_proxy_new mm_gdbus_modem_cdma_proxy_new_finish mm_gdbus_modem_cdma_proxy_new_for_bus mm_gdbus_modem_cdma_proxy_new_for_bus_finish mm_gdbus_modem_cdma_proxy_new_for_bus_sync mm_gdbus_modem_cdma_proxy_new_sync MmGdbusModemCdmaProxyClass MM_GDBUS_IS_MODEM_CDMA_PROXY MM_GDBUS_IS_MODEM_CDMA_PROXY_CLASS MM_GDBUS_MODEM_CDMA_PROXY MM_GDBUS_MODEM_CDMA_PROXY_CLASS MM_GDBUS_MODEM_CDMA_PROXY_GET_CLASS MM_GDBUS_TYPE_MODEM_CDMA_PROXY MmGdbusModemCdmaProxyPrivate mm_gdbus_modem_cdma_proxy_get_type
MmGdbusModemCdmaSkeleton MmGdbusModemCdmaSkeleton MmGdbusModemCdmaSkeleton mm_gdbus_modem_cdma_skeleton_new MmGdbusModemCdmaSkeletonClass MM_GDBUS_IS_MODEM_CDMA_SKELETON MM_GDBUS_IS_MODEM_CDMA_SKELETON_CLASS MM_GDBUS_MODEM_CDMA_SKELETON MM_GDBUS_MODEM_CDMA_SKELETON_CLASS MM_GDBUS_MODEM_CDMA_SKELETON_GET_CLASS MM_GDBUS_TYPE_MODEM_CDMA_SKELETON MmGdbusModemCdmaSkeletonPrivate mm_gdbus_modem_cdma_skeleton_get_type
MmGdbusModemLocation MmGdbusModemLocation MmGdbusModemLocation MmGdbusModemLocationIface mm_gdbus_modem_location_get_enabled mm_gdbus_modem_location_get_capabilities mm_gdbus_modem_location_get_signals_location mm_gdbus_modem_location_get_location mm_gdbus_modem_location_dup_location mm_gdbus_modem_location_dup_supl_server mm_gdbus_modem_location_get_supl_server mm_gdbus_modem_location_get_gps_refresh_rate mm_gdbus_modem_location_get_supported_assistance_data mm_gdbus_modem_location_dup_assistance_data_servers mm_gdbus_modem_location_get_assistance_data_servers mm_gdbus_modem_location_call_get_location mm_gdbus_modem_location_call_get_location_finish mm_gdbus_modem_location_call_get_location_sync mm_gdbus_modem_location_call_setup mm_gdbus_modem_location_call_setup_finish mm_gdbus_modem_location_call_setup_sync mm_gdbus_modem_location_call_set_supl_server mm_gdbus_modem_location_call_set_supl_server_finish mm_gdbus_modem_location_call_set_supl_server_sync mm_gdbus_modem_location_call_inject_assistance_data mm_gdbus_modem_location_call_inject_assistance_data_finish mm_gdbus_modem_location_call_inject_assistance_data_sync mm_gdbus_modem_location_call_set_gps_refresh_rate mm_gdbus_modem_location_call_set_gps_refresh_rate_finish mm_gdbus_modem_location_call_set_gps_refresh_rate_sync mm_gdbus_modem_location_set_capabilities mm_gdbus_modem_location_set_enabled mm_gdbus_modem_location_set_location mm_gdbus_modem_location_set_signals_location mm_gdbus_modem_location_set_supl_server mm_gdbus_modem_location_set_supported_assistance_data mm_gdbus_modem_location_set_gps_refresh_rate mm_gdbus_modem_location_set_assistance_data_servers mm_gdbus_modem_location_complete_get_location mm_gdbus_modem_location_complete_setup mm_gdbus_modem_location_complete_set_supl_server mm_gdbus_modem_location_complete_inject_assistance_data mm_gdbus_modem_location_complete_set_gps_refresh_rate mm_gdbus_modem_location_interface_info mm_gdbus_modem_location_override_properties MM_GDBUS_IS_MODEM_LOCATION MM_GDBUS_MODEM_LOCATION MM_GDBUS_MODEM_LOCATION_GET_IFACE MM_GDBUS_TYPE_MODEM_LOCATION mm_gdbus_modem_location_get_type
MmGdbusModemLocationProxy MmGdbusModemLocationProxy MmGdbusModemLocationProxy mm_gdbus_modem_location_proxy_new mm_gdbus_modem_location_proxy_new_finish mm_gdbus_modem_location_proxy_new_for_bus mm_gdbus_modem_location_proxy_new_for_bus_finish mm_gdbus_modem_location_proxy_new_for_bus_sync mm_gdbus_modem_location_proxy_new_sync MmGdbusModemLocationProxyClass MM_GDBUS_IS_MODEM_LOCATION_PROXY MM_GDBUS_IS_MODEM_LOCATION_PROXY_CLASS MM_GDBUS_MODEM_LOCATION_PROXY MM_GDBUS_MODEM_LOCATION_PROXY_CLASS MM_GDBUS_MODEM_LOCATION_PROXY_GET_CLASS MM_GDBUS_TYPE_MODEM_LOCATION_PROXY MmGdbusModemLocationProxyPrivate mm_gdbus_modem_location_proxy_get_type
MmGdbusModemLocationSkeleton MmGdbusModemLocationSkeleton MmGdbusModemLocationSkeleton mm_gdbus_modem_location_skeleton_new MmGdbusModemLocationSkeletonClass MM_GDBUS_IS_MODEM_LOCATION_SKELETON MM_GDBUS_IS_MODEM_LOCATION_SKELETON_CLASS MM_GDBUS_MODEM_LOCATION_SKELETON MM_GDBUS_MODEM_LOCATION_SKELETON_CLASS MM_GDBUS_MODEM_LOCATION_SKELETON_GET_CLASS MM_GDBUS_TYPE_MODEM_LOCATION_SKELETON MmGdbusModemLocationSkeletonPrivate mm_gdbus_modem_location_skeleton_get_type
MmGdbusModemMessaging MmGdbusModemMessaging MmGdbusModemMessaging MmGdbusModemMessagingIface mm_gdbus_modem_messaging_get_messages mm_gdbus_modem_messaging_dup_messages mm_gdbus_modem_messaging_get_supported_storages mm_gdbus_modem_messaging_dup_supported_storages mm_gdbus_modem_messaging_get_default_storage mm_gdbus_modem_messaging_call_create mm_gdbus_modem_messaging_call_create_finish mm_gdbus_modem_messaging_call_create_sync mm_gdbus_modem_messaging_call_delete mm_gdbus_modem_messaging_call_delete_finish mm_gdbus_modem_messaging_call_delete_sync mm_gdbus_modem_messaging_call_list mm_gdbus_modem_messaging_call_list_finish mm_gdbus_modem_messaging_call_list_sync mm_gdbus_modem_messaging_set_messages mm_gdbus_modem_messaging_set_default_storage mm_gdbus_modem_messaging_set_supported_storages mm_gdbus_modem_messaging_emit_added mm_gdbus_modem_messaging_emit_deleted mm_gdbus_modem_messaging_complete_create mm_gdbus_modem_messaging_complete_delete mm_gdbus_modem_messaging_complete_list mm_gdbus_modem_messaging_interface_info mm_gdbus_modem_messaging_override_properties MM_GDBUS_IS_MODEM_MESSAGING MM_GDBUS_MODEM_MESSAGING MM_GDBUS_MODEM_MESSAGING_GET_IFACE MM_GDBUS_TYPE_MODEM_MESSAGING mm_gdbus_modem_messaging_get_type
MmGdbusModemMessagingProxy MmGdbusModemMessagingProxy MmGdbusModemMessagingProxy mm_gdbus_modem_messaging_proxy_new mm_gdbus_modem_messaging_proxy_new_finish mm_gdbus_modem_messaging_proxy_new_for_bus mm_gdbus_modem_messaging_proxy_new_for_bus_finish mm_gdbus_modem_messaging_proxy_new_for_bus_sync mm_gdbus_modem_messaging_proxy_new_sync MmGdbusModemMessagingProxyClass MM_GDBUS_IS_MODEM_MESSAGING_PROXY MM_GDBUS_IS_MODEM_MESSAGING_PROXY_CLASS MM_GDBUS_MODEM_MESSAGING_PROXY MM_GDBUS_MODEM_MESSAGING_PROXY_CLASS MM_GDBUS_MODEM_MESSAGING_PROXY_GET_CLASS MM_GDBUS_TYPE_MODEM_MESSAGING_PROXY MmGdbusModemMessagingProxyPrivate mm_gdbus_modem_messaging_proxy_get_type
MmGdbusModemMessagingSkeleton MmGdbusModemMessagingSkeleton MmGdbusModemMessagingSkeleton mm_gdbus_modem_messaging_skeleton_new MmGdbusModemMessagingSkeletonClass MM_GDBUS_IS_MODEM_MESSAGING_SKELETON MM_GDBUS_IS_MODEM_MESSAGING_SKELETON_CLASS MM_GDBUS_MODEM_MESSAGING_SKELETON MM_GDBUS_MODEM_MESSAGING_SKELETON_CLASS MM_GDBUS_MODEM_MESSAGING_SKELETON_GET_CLASS MM_GDBUS_TYPE_MODEM_MESSAGING_SKELETON MmGdbusModemMessagingSkeletonPrivate mm_gdbus_modem_messaging_skeleton_get_type
MmGdbusModemTime MmGdbusModemTime MmGdbusModemTime MmGdbusModemTimeIface mm_gdbus_modem_time_get_network_timezone mm_gdbus_modem_time_dup_network_timezone mm_gdbus_modem_time_call_get_network_time mm_gdbus_modem_time_call_get_network_time_finish mm_gdbus_modem_time_call_get_network_time_sync mm_gdbus_modem_time_set_network_timezone mm_gdbus_modem_time_emit_network_time_changed mm_gdbus_modem_time_complete_get_network_time mm_gdbus_modem_time_interface_info mm_gdbus_modem_time_override_properties MM_GDBUS_IS_MODEM_TIME MM_GDBUS_MODEM_TIME MM_GDBUS_MODEM_TIME_GET_IFACE MM_GDBUS_TYPE_MODEM_TIME mm_gdbus_modem_time_get_type
MmGdbusModemTimeProxy MmGdbusModemTimeProxy MmGdbusModemTimeProxy mm_gdbus_modem_time_proxy_new mm_gdbus_modem_time_proxy_new_finish mm_gdbus_modem_time_proxy_new_for_bus mm_gdbus_modem_time_proxy_new_for_bus_finish mm_gdbus_modem_time_proxy_new_for_bus_sync mm_gdbus_modem_time_proxy_new_sync MmGdbusModemTimeProxyClass MM_GDBUS_IS_MODEM_TIME_PROXY MM_GDBUS_IS_MODEM_TIME_PROXY_CLASS MM_GDBUS_MODEM_TIME_PROXY MM_GDBUS_MODEM_TIME_PROXY_CLASS MM_GDBUS_MODEM_TIME_PROXY_GET_CLASS MM_GDBUS_TYPE_MODEM_TIME_PROXY MmGdbusModemTimeProxyPrivate mm_gdbus_modem_time_proxy_get_type
MmGdbusModemTimeSkeleton MmGdbusModemTimeSkeleton MmGdbusModemTimeSkeleton mm_gdbus_modem_time_skeleton_new MmGdbusModemTimeSkeletonClass MM_GDBUS_IS_MODEM_TIME_SKELETON MM_GDBUS_IS_MODEM_TIME_SKELETON_CLASS MM_GDBUS_MODEM_TIME_SKELETON MM_GDBUS_MODEM_TIME_SKELETON_CLASS MM_GDBUS_MODEM_TIME_SKELETON_GET_CLASS MM_GDBUS_TYPE_MODEM_TIME_SKELETON MmGdbusModemTimeSkeletonPrivate mm_gdbus_modem_time_skeleton_get_type
MmGdbusModemFirmware MmGdbusModemFirmware MmGdbusModemFirmware MmGdbusModemFirmwareIface mm_gdbus_modem_firmware_dup_update_settings mm_gdbus_modem_firmware_get_update_settings mm_gdbus_modem_firmware_call_list mm_gdbus_modem_firmware_call_list_finish mm_gdbus_modem_firmware_call_list_sync mm_gdbus_modem_firmware_call_select mm_gdbus_modem_firmware_call_select_finish mm_gdbus_modem_firmware_call_select_sync mm_gdbus_modem_firmware_set_update_settings mm_gdbus_modem_firmware_complete_list mm_gdbus_modem_firmware_complete_select mm_gdbus_modem_firmware_interface_info mm_gdbus_modem_firmware_override_properties MM_GDBUS_IS_MODEM_FIRMWARE MM_GDBUS_MODEM_FIRMWARE MM_GDBUS_MODEM_FIRMWARE_GET_IFACE MM_GDBUS_TYPE_MODEM_FIRMWARE mm_gdbus_modem_firmware_get_type
MmGdbusModemFirmwareProxy MmGdbusModemFirmwareProxy MmGdbusModemFirmwareProxy mm_gdbus_modem_firmware_proxy_new mm_gdbus_modem_firmware_proxy_new_finish mm_gdbus_modem_firmware_proxy_new_for_bus mm_gdbus_modem_firmware_proxy_new_for_bus_finish mm_gdbus_modem_firmware_proxy_new_for_bus_sync mm_gdbus_modem_firmware_proxy_new_sync MmGdbusModemFirmwareProxyClass MM_GDBUS_IS_MODEM_FIRMWARE_PROXY MM_GDBUS_IS_MODEM_FIRMWARE_PROXY_CLASS MM_GDBUS_MODEM_FIRMWARE_PROXY MM_GDBUS_MODEM_FIRMWARE_PROXY_CLASS MM_GDBUS_MODEM_FIRMWARE_PROXY_GET_CLASS MM_GDBUS_TYPE_MODEM_FIRMWARE_PROXY MmGdbusModemFirmwareProxyPrivate mm_gdbus_modem_firmware_proxy_get_type
MmGdbusModemFirmwareSkeleton MmGdbusModemFirmwareSkeleton MmGdbusModemFirmwareSkeleton mm_gdbus_modem_firmware_skeleton_new MmGdbusModemFirmwareSkeletonClass MM_GDBUS_IS_MODEM_FIRMWARE_SKELETON MM_GDBUS_IS_MODEM_FIRMWARE_SKELETON_CLASS MM_GDBUS_MODEM_FIRMWARE_SKELETON MM_GDBUS_MODEM_FIRMWARE_SKELETON_CLASS MM_GDBUS_MODEM_FIRMWARE_SKELETON_GET_CLASS MM_GDBUS_TYPE_MODEM_FIRMWARE_SKELETON MmGdbusModemFirmwareSkeletonPrivate mm_gdbus_modem_firmware_skeleton_get_type
MmGdbusModemOma MmGdbusModemOma MmGdbusModemOma MmGdbusModemOmaIface mm_gdbus_modem_oma_call_accept_network_initiated_session mm_gdbus_modem_oma_call_accept_network_initiated_session_finish mm_gdbus_modem_oma_call_accept_network_initiated_session_sync mm_gdbus_modem_oma_call_cancel_session mm_gdbus_modem_oma_call_cancel_session_finish mm_gdbus_modem_oma_call_cancel_session_sync mm_gdbus_modem_oma_call_setup mm_gdbus_modem_oma_call_setup_finish mm_gdbus_modem_oma_call_setup_sync mm_gdbus_modem_oma_call_start_client_initiated_session mm_gdbus_modem_oma_call_start_client_initiated_session_finish mm_gdbus_modem_oma_call_start_client_initiated_session_sync mm_gdbus_modem_oma_get_features mm_gdbus_modem_oma_get_session_state mm_gdbus_modem_oma_get_session_type mm_gdbus_modem_oma_get_pending_network_initiated_sessions mm_gdbus_modem_oma_dup_pending_network_initiated_sessions mm_gdbus_modem_oma_set_pending_network_initiated_sessions mm_gdbus_modem_oma_set_features mm_gdbus_modem_oma_set_session_state mm_gdbus_modem_oma_set_session_type mm_gdbus_modem_oma_emit_session_state_changed mm_gdbus_modem_oma_complete_accept_network_initiated_session mm_gdbus_modem_oma_complete_cancel_session mm_gdbus_modem_oma_complete_setup mm_gdbus_modem_oma_complete_start_client_initiated_session mm_gdbus_modem_oma_interface_info mm_gdbus_modem_oma_override_properties MM_GDBUS_IS_MODEM_OMA MM_GDBUS_MODEM_OMA MM_GDBUS_MODEM_OMA_GET_IFACE MM_GDBUS_TYPE_MODEM_OMA mm_gdbus_modem_oma_get_type
MmGdbusModemOmaProxy MmGdbusModemOmaProxy MmGdbusModemOmaProxy mm_gdbus_modem_oma_proxy_new mm_gdbus_modem_oma_proxy_new_finish mm_gdbus_modem_oma_proxy_new_for_bus mm_gdbus_modem_oma_proxy_new_for_bus_finish mm_gdbus_modem_oma_proxy_new_for_bus_sync mm_gdbus_modem_oma_proxy_new_sync MmGdbusModemOmaProxyClass MM_GDBUS_IS_MODEM_OMA_PROXY MM_GDBUS_IS_MODEM_OMA_PROXY_CLASS MM_GDBUS_MODEM_OMA_PROXY MM_GDBUS_MODEM_OMA_PROXY_CLASS MM_GDBUS_MODEM_OMA_PROXY_GET_CLASS MM_GDBUS_TYPE_MODEM_OMA_PROXY MmGdbusModemOmaProxyPrivate mm_gdbus_modem_oma_proxy_get_type
MmGdbusModemOmaSkeleton MmGdbusModemOmaSkeleton MmGdbusModemOmaSkeleton mm_gdbus_modem_oma_skeleton_new MmGdbusModemOmaSkeletonClass MM_GDBUS_IS_MODEM_OMA_SKELETON MM_GDBUS_IS_MODEM_OMA_SKELETON_CLASS MM_GDBUS_MODEM_OMA_SKELETON MM_GDBUS_MODEM_OMA_SKELETON_CLASS MM_GDBUS_MODEM_OMA_SKELETON_GET_CLASS MM_GDBUS_TYPE_MODEM_OMA_SKELETON MmGdbusModemOmaSkeletonPrivate mm_gdbus_modem_oma_skeleton_get_type
MmGdbusModemSimple MmGdbusModemSimple MmGdbusModemSimple MmGdbusModemSimpleIface mm_gdbus_modem_simple_call_connect mm_gdbus_modem_simple_call_connect_finish mm_gdbus_modem_simple_call_connect_sync mm_gdbus_modem_simple_call_disconnect mm_gdbus_modem_simple_call_disconnect_finish mm_gdbus_modem_simple_call_disconnect_sync mm_gdbus_modem_simple_call_get_status mm_gdbus_modem_simple_call_get_status_finish mm_gdbus_modem_simple_call_get_status_sync mm_gdbus_modem_simple_complete_connect mm_gdbus_modem_simple_complete_disconnect mm_gdbus_modem_simple_complete_get_status mm_gdbus_modem_simple_interface_info mm_gdbus_modem_simple_override_properties MM_GDBUS_IS_MODEM_SIMPLE MM_GDBUS_MODEM_SIMPLE MM_GDBUS_MODEM_SIMPLE_GET_IFACE MM_GDBUS_TYPE_MODEM_SIMPLE mm_gdbus_modem_simple_get_type
MmGdbusModemSimpleProxy MmGdbusModemSimpleProxy MmGdbusModemSimpleProxy mm_gdbus_modem_simple_proxy_new mm_gdbus_modem_simple_proxy_new_finish mm_gdbus_modem_simple_proxy_new_for_bus mm_gdbus_modem_simple_proxy_new_for_bus_finish mm_gdbus_modem_simple_proxy_new_for_bus_sync mm_gdbus_modem_simple_proxy_new_sync MmGdbusModemSimpleProxyClass MM_GDBUS_IS_MODEM_SIMPLE_PROXY MM_GDBUS_IS_MODEM_SIMPLE_PROXY_CLASS MM_GDBUS_MODEM_SIMPLE_PROXY MM_GDBUS_MODEM_SIMPLE_PROXY_CLASS MM_GDBUS_MODEM_SIMPLE_PROXY_GET_CLASS MM_GDBUS_TYPE_MODEM_SIMPLE_PROXY MmGdbusModemSimpleProxyPrivate mm_gdbus_modem_simple_proxy_get_type
MmGdbusModemSimpleSkeleton MmGdbusModemSimpleSkeleton MmGdbusModemSimpleSkeleton mm_gdbus_modem_simple_skeleton_new MmGdbusModemSimpleSkeletonClass MM_GDBUS_IS_MODEM_SIMPLE_SKELETON MM_GDBUS_IS_MODEM_SIMPLE_SKELETON_CLASS MM_GDBUS_MODEM_SIMPLE_SKELETON MM_GDBUS_MODEM_SIMPLE_SKELETON_CLASS MM_GDBUS_MODEM_SIMPLE_SKELETON_GET_CLASS MM_GDBUS_TYPE_MODEM_SIMPLE_SKELETON MmGdbusModemSimpleSkeletonPrivate mm_gdbus_modem_simple_skeleton_get_type
MmGdbusModemSignal MmGdbusModemSignal MmGdbusModemSignal MmGdbusModemSignalIface mm_gdbus_modem_signal_get_rate mm_gdbus_modem_signal_get_error_rate_threshold mm_gdbus_modem_signal_get_rssi_threshold mm_gdbus_modem_signal_get_cdma mm_gdbus_modem_signal_get_evdo mm_gdbus_modem_signal_get_gsm mm_gdbus_modem_signal_get_umts mm_gdbus_modem_signal_get_lte mm_gdbus_modem_signal_get_nr5g mm_gdbus_modem_signal_dup_cdma mm_gdbus_modem_signal_dup_evdo mm_gdbus_modem_signal_dup_gsm mm_gdbus_modem_signal_dup_umts mm_gdbus_modem_signal_dup_lte mm_gdbus_modem_signal_dup_nr5g mm_gdbus_modem_signal_call_setup mm_gdbus_modem_signal_call_setup_finish mm_gdbus_modem_signal_call_setup_sync mm_gdbus_modem_signal_call_setup_thresholds mm_gdbus_modem_signal_call_setup_thresholds_finish mm_gdbus_modem_signal_call_setup_thresholds_sync mm_gdbus_modem_signal_set_cdma mm_gdbus_modem_signal_set_evdo mm_gdbus_modem_signal_set_gsm mm_gdbus_modem_signal_set_lte mm_gdbus_modem_signal_set_nr5g mm_gdbus_modem_signal_set_rate mm_gdbus_modem_signal_set_umts mm_gdbus_modem_signal_set_error_rate_threshold mm_gdbus_modem_signal_set_rssi_threshold mm_gdbus_modem_signal_complete_setup mm_gdbus_modem_signal_complete_setup_thresholds mm_gdbus_modem_signal_interface_info mm_gdbus_modem_signal_override_properties MM_GDBUS_IS_MODEM_SIGNAL MM_GDBUS_MODEM_SIGNAL MM_GDBUS_MODEM_SIGNAL_GET_IFACE MM_GDBUS_TYPE_MODEM_SIGNAL mm_gdbus_modem_signal_get_type
MmGdbusModemSignalProxy MmGdbusModemSignalProxy MmGdbusModemSignalProxy mm_gdbus_modem_signal_proxy_new mm_gdbus_modem_signal_proxy_new_finish mm_gdbus_modem_signal_proxy_new_for_bus mm_gdbus_modem_signal_proxy_new_for_bus_finish mm_gdbus_modem_signal_proxy_new_for_bus_sync mm_gdbus_modem_signal_proxy_new_sync MmGdbusModemSignalProxyClass MM_GDBUS_IS_MODEM_SIGNAL_PROXY MM_GDBUS_IS_MODEM_SIGNAL_PROXY_CLASS MM_GDBUS_MODEM_SIGNAL_PROXY MM_GDBUS_MODEM_SIGNAL_PROXY_CLASS MM_GDBUS_MODEM_SIGNAL_PROXY_GET_CLASS MM_GDBUS_TYPE_MODEM_SIGNAL_PROXY MmGdbusModemSignalProxyPrivate mm_gdbus_modem_signal_proxy_get_type
MmGdbusModemSignalSkeleton MmGdbusModemSignalSkeleton MmGdbusModemSignalSkeleton mm_gdbus_modem_signal_skeleton_new MmGdbusModemSignalSkeletonClass MM_GDBUS_IS_MODEM_SIGNAL_SKELETON MM_GDBUS_IS_MODEM_SIGNAL_SKELETON_CLASS MM_GDBUS_MODEM_SIGNAL_SKELETON MM_GDBUS_MODEM_SIGNAL_SKELETON_CLASS MM_GDBUS_MODEM_SIGNAL_SKELETON_GET_CLASS MM_GDBUS_TYPE_MODEM_SIGNAL_SKELETON MmGdbusModemSignalSkeletonPrivate mm_gdbus_modem_signal_skeleton_get_type
MmGdbusModemVoice MmGdbusModemVoice MmGdbusModemVoice MmGdbusModemVoiceIface mm_gdbus_modem_voice_get_calls mm_gdbus_modem_voice_dup_calls mm_gdbus_modem_voice_get_emergency_only mm_gdbus_modem_voice_call_create_call mm_gdbus_modem_voice_call_create_call_finish mm_gdbus_modem_voice_call_create_call_sync mm_gdbus_modem_voice_call_delete_call mm_gdbus_modem_voice_call_delete_call_finish mm_gdbus_modem_voice_call_delete_call_sync mm_gdbus_modem_voice_call_list_calls mm_gdbus_modem_voice_call_list_calls_finish mm_gdbus_modem_voice_call_list_calls_sync mm_gdbus_modem_voice_call_hangup_and_accept mm_gdbus_modem_voice_call_hangup_and_accept_finish mm_gdbus_modem_voice_call_hangup_and_accept_sync mm_gdbus_modem_voice_call_hold_and_accept mm_gdbus_modem_voice_call_hold_and_accept_finish mm_gdbus_modem_voice_call_hold_and_accept_sync mm_gdbus_modem_voice_call_hangup_all mm_gdbus_modem_voice_call_hangup_all_finish mm_gdbus_modem_voice_call_hangup_all_sync mm_gdbus_modem_voice_call_transfer mm_gdbus_modem_voice_call_transfer_finish mm_gdbus_modem_voice_call_transfer_sync mm_gdbus_modem_voice_call_call_waiting_query mm_gdbus_modem_voice_call_call_waiting_query_finish mm_gdbus_modem_voice_call_call_waiting_query_sync mm_gdbus_modem_voice_call_call_waiting_setup mm_gdbus_modem_voice_call_call_waiting_setup_finish mm_gdbus_modem_voice_call_call_waiting_setup_sync mm_gdbus_modem_voice_set_calls mm_gdbus_modem_voice_set_emergency_only mm_gdbus_modem_voice_emit_call_added mm_gdbus_modem_voice_emit_call_deleted mm_gdbus_modem_voice_complete_create_call mm_gdbus_modem_voice_complete_delete_call mm_gdbus_modem_voice_complete_list_calls mm_gdbus_modem_voice_complete_hangup_and_accept mm_gdbus_modem_voice_complete_hold_and_accept mm_gdbus_modem_voice_complete_hangup_all mm_gdbus_modem_voice_complete_transfer mm_gdbus_modem_voice_complete_call_waiting_query mm_gdbus_modem_voice_complete_call_waiting_setup mm_gdbus_modem_voice_interface_info mm_gdbus_modem_voice_override_properties MM_GDBUS_IS_MODEM_VOICE MM_GDBUS_MODEM_VOICE MM_GDBUS_MODEM_VOICE_GET_IFACE MM_GDBUS_TYPE_MODEM_VOICE mm_gdbus_modem_voice_get_type
MmGdbusModemVoiceProxy MmGdbusModemVoiceProxy MmGdbusModemVoiceProxy mm_gdbus_modem_voice_proxy_new mm_gdbus_modem_voice_proxy_new_finish mm_gdbus_modem_voice_proxy_new_for_bus mm_gdbus_modem_voice_proxy_new_for_bus_finish mm_gdbus_modem_voice_proxy_new_for_bus_sync mm_gdbus_modem_voice_proxy_new_sync MmGdbusModemVoiceProxyClass MM_GDBUS_IS_MODEM_VOICE_PROXY MM_GDBUS_IS_MODEM_VOICE_PROXY_CLASS MM_GDBUS_MODEM_VOICE_PROXY MM_GDBUS_MODEM_VOICE_PROXY_CLASS MM_GDBUS_MODEM_VOICE_PROXY_GET_CLASS MM_GDBUS_TYPE_MODEM_VOICE_PROXY MmGdbusModemVoiceProxyPrivate mm_gdbus_modem_voice_proxy_get_type
MmGdbusModemVoiceSkeleton MmGdbusModemVoiceSkeleton MmGdbusModemVoiceSkeleton mm_gdbus_modem_voice_skeleton_new MmGdbusModemVoiceSkeletonClass MM_GDBUS_IS_MODEM_VOICE_SKELETON MM_GDBUS_IS_MODEM_VOICE_SKELETON_CLASS MM_GDBUS_MODEM_VOICE_SKELETON MM_GDBUS_MODEM_VOICE_SKELETON_CLASS MM_GDBUS_MODEM_VOICE_SKELETON_GET_CLASS MM_GDBUS_TYPE_MODEM_VOICE_SKELETON MmGdbusModemVoiceSkeletonPrivate mm_gdbus_modem_voice_skeleton_get_type
MmGdbusModemSar MmGdbusModemSar MmGdbusModemSar MmGdbusModemSarIface mm_gdbus_modem_sar_get_power_level mm_gdbus_modem_sar_get_state mm_gdbus_modem_sar_call_enable mm_gdbus_modem_sar_call_enable_finish mm_gdbus_modem_sar_call_enable_sync mm_gdbus_modem_sar_call_set_power_level mm_gdbus_modem_sar_call_set_power_level_finish mm_gdbus_modem_sar_call_set_power_level_sync mm_gdbus_modem_sar_set_power_level mm_gdbus_modem_sar_set_state mm_gdbus_modem_sar_interface_info mm_gdbus_modem_sar_override_properties mm_gdbus_modem_sar_complete_enable mm_gdbus_modem_sar_complete_set_power_level MM_GDBUS_IS_MODEM_SAR MM_GDBUS_MODEM_SAR MM_GDBUS_MODEM_SAR_GET_IFACE MM_GDBUS_TYPE_MODEM_SAR mm_gdbus_modem_sar_get_type
MmGdbusModemSarProxy MmGdbusModemSarProxy MmGdbusModemSarProxy mm_gdbus_modem_sar_proxy_new mm_gdbus_modem_sar_proxy_new_finish mm_gdbus_modem_sar_proxy_new_for_bus mm_gdbus_modem_sar_proxy_new_for_bus_finish mm_gdbus_modem_sar_proxy_new_for_bus_sync mm_gdbus_modem_sar_proxy_new_sync MmGdbusModemSarProxyClass MM_GDBUS_IS_MODEM_SAR_PROXY MM_GDBUS_IS_MODEM_SAR_PROXY_CLASS MM_GDBUS_MODEM_SAR_PROXY MM_GDBUS_MODEM_SAR_PROXY_CLASS MM_GDBUS_MODEM_SAR_PROXY_GET_CLASS MM_GDBUS_TYPE_MODEM_SAR_PROXY MmGdbusModemSarProxyPrivate mm_gdbus_modem_sar_proxy_get_type
MmGdbusModemSarSkeleton MmGdbusModemSarSkeleton MmGdbusModemSarSkeleton mm_gdbus_modem_sar_skeleton_new MmGdbusModemSarSkeletonClass MM_GDBUS_IS_MODEM_SAR_SKELETON MM_GDBUS_IS_MODEM_SAR_SKELETON_CLASS MM_GDBUS_MODEM_SAR_SKELETON MM_GDBUS_MODEM_SAR_SKELETON_CLASS MM_GDBUS_MODEM_SAR_SKELETON_GET_CLASS MM_GDBUS_TYPE_MODEM_SAR_SKELETON MmGdbusModemSarSkeletonPrivate mm_gdbus_modem_sar_skeleton_get_type
MmGdbusObject MmGdbusObject MmGdbusObject MmGdbusObjectIface mm_gdbus_object_peek_modem mm_gdbus_object_get_modem mm_gdbus_object_peek_modem3gpp mm_gdbus_object_get_modem3gpp mm_gdbus_object_peek_modem3gpp_ussd mm_gdbus_object_get_modem3gpp_ussd mm_gdbus_object_peek_modem3gpp_profile_manager mm_gdbus_object_get_modem3gpp_profile_manager mm_gdbus_object_peek_modem_cdma mm_gdbus_object_get_modem_cdma mm_gdbus_object_peek_modem_location mm_gdbus_object_get_modem_location mm_gdbus_object_peek_modem_messaging mm_gdbus_object_get_modem_messaging mm_gdbus_object_peek_modem_time mm_gdbus_object_get_modem_time mm_gdbus_object_peek_modem_firmware mm_gdbus_object_get_modem_firmware mm_gdbus_object_peek_modem_oma mm_gdbus_object_get_modem_oma mm_gdbus_object_peek_modem_simple mm_gdbus_object_get_modem_simple mm_gdbus_object_peek_modem_signal mm_gdbus_object_get_modem_signal mm_gdbus_object_peek_modem_voice mm_gdbus_object_get_modem_voice mm_gdbus_object_peek_modem_sar mm_gdbus_object_get_modem_sar MM_GDBUS_IS_OBJECT MM_GDBUS_OBJECT MM_GDBUS_OBJECT_GET_IFACE MM_GDBUS_TYPE_OBJECT mm_gdbus_object_get_type
MmGdbusObjectProxy MmGdbusObjectProxy MmGdbusObjectProxy mm_gdbus_object_proxy_new MmGdbusObjectProxyClass MM_GDBUS_IS_OBJECT_PROXY MM_GDBUS_IS_OBJECT_PROXY_CLASS MM_GDBUS_OBJECT_PROXY MM_GDBUS_OBJECT_PROXY_CLASS MM_GDBUS_OBJECT_PROXY_GET_CLASS MM_GDBUS_TYPE_OBJECT_PROXY MmGdbusObjectProxyPrivate mm_gdbus_object_proxy_get_type
MmGdbusObjectSkeleton MmGdbusObjectSkeleton MmGdbusObjectSkeleton mm_gdbus_object_skeleton_new mm_gdbus_object_skeleton_set_modem mm_gdbus_object_skeleton_set_modem3gpp mm_gdbus_object_skeleton_set_modem3gpp_ussd mm_gdbus_object_skeleton_set_modem3gpp_profile_manager mm_gdbus_object_skeleton_set_modem_cdma mm_gdbus_object_skeleton_set_modem_firmware mm_gdbus_object_skeleton_set_modem_oma mm_gdbus_object_skeleton_set_modem_location mm_gdbus_object_skeleton_set_modem_messaging mm_gdbus_object_skeleton_set_modem_simple mm_gdbus_object_skeleton_set_modem_time mm_gdbus_object_skeleton_set_modem_signal mm_gdbus_object_skeleton_set_modem_voice mm_gdbus_object_skeleton_set_modem_sar MmGdbusObjectSkeletonClass MM_GDBUS_IS_OBJECT_SKELETON MM_GDBUS_IS_OBJECT_SKELETON_CLASS MM_GDBUS_OBJECT_SKELETON MM_GDBUS_OBJECT_SKELETON_CLASS MM_GDBUS_OBJECT_SKELETON_GET_CLASS MM_GDBUS_TYPE_OBJECT_SKELETON MmGdbusObjectSkeletonPrivate mm_gdbus_object_skeleton_get_type
MmGdbusObjectManagerClient MmGdbusObjectManagerClient MmGdbusObjectManagerClient mm_gdbus_object_manager_client_new mm_gdbus_object_manager_client_new_finish mm_gdbus_object_manager_client_new_sync mm_gdbus_object_manager_client_new_for_bus mm_gdbus_object_manager_client_new_for_bus_finish mm_gdbus_object_manager_client_new_for_bus_sync mm_gdbus_object_manager_client_get_proxy_type MmGdbusObjectManagerClientClass MM_GDBUS_IS_OBJECT_MANAGER_CLIENT MM_GDBUS_IS_OBJECT_MANAGER_CLIENT_CLASS MM_GDBUS_OBJECT_MANAGER_CLIENT MM_GDBUS_OBJECT_MANAGER_CLIENT_CLASS MM_GDBUS_OBJECT_MANAGER_CLIENT_GET_CLASS MM_GDBUS_TYPE_OBJECT_MANAGER_CLIENT MmGdbusObjectManagerClientPrivate mm_gdbus_object_manager_client_get_type
MmGdbusSim MmGdbusSim MmGdbusSim MmGdbusSimIface mm_gdbus_sim_get_active mm_gdbus_sim_get_imsi mm_gdbus_sim_dup_imsi mm_gdbus_sim_get_eid mm_gdbus_sim_dup_eid mm_gdbus_sim_get_sim_identifier mm_gdbus_sim_dup_sim_identifier mm_gdbus_sim_get_operator_identifier mm_gdbus_sim_dup_operator_identifier mm_gdbus_sim_get_operator_name mm_gdbus_sim_dup_operator_name mm_gdbus_sim_get_emergency_numbers mm_gdbus_sim_dup_emergency_numbers mm_gdbus_sim_dup_preferred_networks mm_gdbus_sim_get_preferred_networks mm_gdbus_sim_dup_gid1 mm_gdbus_sim_get_gid1 mm_gdbus_sim_dup_gid2 mm_gdbus_sim_get_gid2 mm_gdbus_sim_get_sim_type mm_gdbus_sim_get_esim_status mm_gdbus_sim_get_removability mm_gdbus_sim_call_send_pin mm_gdbus_sim_call_send_pin_finish mm_gdbus_sim_call_send_pin_sync mm_gdbus_sim_call_send_puk mm_gdbus_sim_call_send_puk_finish mm_gdbus_sim_call_send_puk_sync mm_gdbus_sim_call_enable_pin mm_gdbus_sim_call_enable_pin_finish mm_gdbus_sim_call_enable_pin_sync mm_gdbus_sim_call_change_pin mm_gdbus_sim_call_change_pin_finish mm_gdbus_sim_call_change_pin_sync mm_gdbus_sim_call_set_preferred_networks mm_gdbus_sim_call_set_preferred_networks_finish mm_gdbus_sim_call_set_preferred_networks_sync mm_gdbus_sim_set_active mm_gdbus_sim_set_imsi mm_gdbus_sim_set_eid mm_gdbus_sim_set_operator_identifier mm_gdbus_sim_set_operator_name mm_gdbus_sim_set_sim_identifier mm_gdbus_sim_set_emergency_numbers mm_gdbus_sim_set_preferred_networks mm_gdbus_sim_complete_change_pin mm_gdbus_sim_complete_enable_pin mm_gdbus_sim_complete_send_pin mm_gdbus_sim_complete_send_puk mm_gdbus_sim_complete_set_preferred_networks mm_gdbus_sim_interface_info mm_gdbus_sim_override_properties mm_gdbus_sim_set_esim_status mm_gdbus_sim_set_removability mm_gdbus_sim_set_sim_type mm_gdbus_sim_set_gid1 mm_gdbus_sim_set_gid2 MM_GDBUS_IS_SIM MM_GDBUS_SIM MM_GDBUS_SIM_GET_IFACE MM_GDBUS_TYPE_SIM mm_gdbus_sim_get_type
MmGdbusSimProxy MmGdbusSimProxy MmGdbusSimProxy mm_gdbus_sim_proxy_new mm_gdbus_sim_proxy_new_finish mm_gdbus_sim_proxy_new_for_bus mm_gdbus_sim_proxy_new_for_bus_finish mm_gdbus_sim_proxy_new_for_bus_sync mm_gdbus_sim_proxy_new_sync MmGdbusSimProxyClass MM_GDBUS_IS_SIM_PROXY MM_GDBUS_IS_SIM_PROXY_CLASS MM_GDBUS_SIM_PROXY MM_GDBUS_SIM_PROXY_CLASS MM_GDBUS_SIM_PROXY_GET_CLASS MM_GDBUS_TYPE_SIM_PROXY MmGdbusSimProxyPrivate mm_gdbus_sim_proxy_get_type
MmGdbusSimSkeleton MmGdbusSimSkeleton MmGdbusSimSkeleton mm_gdbus_sim_skeleton_new MmGdbusSimSkeletonClass MM_GDBUS_IS_SIM_SKELETON MM_GDBUS_IS_SIM_SKELETON_CLASS MM_GDBUS_SIM_SKELETON MM_GDBUS_SIM_SKELETON_CLASS MM_GDBUS_SIM_SKELETON_GET_CLASS MM_GDBUS_TYPE_SIM_SKELETON MmGdbusSimSkeletonPrivate mm_gdbus_sim_skeleton_get_type
MmGdbusSms MmGdbusSms MmGdbusSms MmGdbusSmsIface mm_gdbus_sms_get_state mm_gdbus_sms_get_pdu_type mm_gdbus_sms_get_message_reference mm_gdbus_sms_get_storage mm_gdbus_sms_get_text mm_gdbus_sms_dup_text mm_gdbus_sms_get_data mm_gdbus_sms_dup_data mm_gdbus_sms_get_number mm_gdbus_sms_dup_number mm_gdbus_sms_get_smsc mm_gdbus_sms_dup_smsc mm_gdbus_sms_get_validity mm_gdbus_sms_dup_validity mm_gdbus_sms_get_class mm_gdbus_sms_get_teleservice_id mm_gdbus_sms_get_service_category mm_gdbus_sms_get_timestamp mm_gdbus_sms_dup_timestamp mm_gdbus_sms_get_discharge_timestamp mm_gdbus_sms_dup_discharge_timestamp mm_gdbus_sms_get_delivery_report_request mm_gdbus_sms_get_delivery_state mm_gdbus_sms_call_send mm_gdbus_sms_call_send_finish mm_gdbus_sms_call_send_sync mm_gdbus_sms_call_store mm_gdbus_sms_call_store_finish mm_gdbus_sms_call_store_sync mm_gdbus_sms_set_class mm_gdbus_sms_set_teleservice_id mm_gdbus_sms_set_service_category mm_gdbus_sms_set_data mm_gdbus_sms_set_delivery_report_request mm_gdbus_sms_set_delivery_state mm_gdbus_sms_set_discharge_timestamp mm_gdbus_sms_set_message_reference mm_gdbus_sms_set_number mm_gdbus_sms_set_pdu_type mm_gdbus_sms_set_smsc mm_gdbus_sms_set_state mm_gdbus_sms_set_storage mm_gdbus_sms_set_text mm_gdbus_sms_set_timestamp mm_gdbus_sms_set_validity mm_gdbus_sms_complete_send mm_gdbus_sms_complete_store mm_gdbus_sms_interface_info mm_gdbus_sms_override_properties MM_GDBUS_IS_SMS MM_GDBUS_SMS MM_GDBUS_SMS_GET_IFACE MM_GDBUS_TYPE_SMS mm_gdbus_sms_get_type
MmGdbusSmsProxy MmGdbusSmsProxy MmGdbusSmsProxy mm_gdbus_sms_proxy_new mm_gdbus_sms_proxy_new_finish mm_gdbus_sms_proxy_new_for_bus mm_gdbus_sms_proxy_new_for_bus_finish mm_gdbus_sms_proxy_new_for_bus_sync mm_gdbus_sms_proxy_new_sync MmGdbusSmsProxyClass MM_GDBUS_IS_SMS_PROXY MM_GDBUS_IS_SMS_PROXY_CLASS MM_GDBUS_SMS_PROXY MM_GDBUS_SMS_PROXY_CLASS MM_GDBUS_SMS_PROXY_GET_CLASS MM_GDBUS_TYPE_SMS_PROXY MmGdbusSmsProxyPrivate mm_gdbus_sms_proxy_get_type
MmGdbusSmsSkeleton MmGdbusSmsSkeleton MmGdbusSmsSkeleton mm_gdbus_sms_skeleton_new MmGdbusSmsSkeletonClass MM_GDBUS_IS_SMS_SKELETON MM_GDBUS_IS_SMS_SKELETON_CLASS MM_GDBUS_SMS_SKELETON MM_GDBUS_SMS_SKELETON_CLASS MM_GDBUS_SMS_SKELETON_GET_CLASS MM_GDBUS_TYPE_SMS_SKELETON MmGdbusSmsSkeletonPrivate mm_gdbus_sms_skeleton_get_type
MmGdbusCall MmGdbusCall MmGdbusCall MmGdbusCallIface mm_gdbus_call_get_direction mm_gdbus_call_get_number mm_gdbus_call_dup_number mm_gdbus_call_get_state mm_gdbus_call_get_state_reason mm_gdbus_call_dup_audio_format mm_gdbus_call_dup_audio_port mm_gdbus_call_get_audio_format mm_gdbus_call_get_audio_port mm_gdbus_call_get_multiparty mm_gdbus_call_call_accept mm_gdbus_call_call_accept_finish mm_gdbus_call_call_accept_sync mm_gdbus_call_call_start mm_gdbus_call_call_start_finish mm_gdbus_call_call_start_sync mm_gdbus_call_call_hangup mm_gdbus_call_call_hangup_finish mm_gdbus_call_call_hangup_sync mm_gdbus_call_call_send_dtmf mm_gdbus_call_call_send_dtmf_finish mm_gdbus_call_call_send_dtmf_sync mm_gdbus_call_call_deflect mm_gdbus_call_call_deflect_finish mm_gdbus_call_call_deflect_sync mm_gdbus_call_call_join_multiparty mm_gdbus_call_call_join_multiparty_finish mm_gdbus_call_call_join_multiparty_sync mm_gdbus_call_call_leave_multiparty mm_gdbus_call_call_leave_multiparty_finish mm_gdbus_call_call_leave_multiparty_sync mm_gdbus_call_set_direction mm_gdbus_call_set_number mm_gdbus_call_set_state mm_gdbus_call_set_state_reason mm_gdbus_call_set_audio_format mm_gdbus_call_set_audio_port mm_gdbus_call_set_multiparty mm_gdbus_call_complete_accept mm_gdbus_call_complete_hangup mm_gdbus_call_complete_send_dtmf mm_gdbus_call_complete_start mm_gdbus_call_complete_deflect mm_gdbus_call_complete_join_multiparty mm_gdbus_call_complete_leave_multiparty mm_gdbus_call_interface_info mm_gdbus_call_override_properties mm_gdbus_call_emit_dtmf_received mm_gdbus_call_emit_state_changed MM_GDBUS_IS_CALL MM_GDBUS_CALL MM_GDBUS_CALL_GET_IFACE MM_GDBUS_TYPE_CALL mm_gdbus_call_get_type
MmGdbusCallProxy MmGdbusCallProxy MmGdbusCallProxy mm_gdbus_call_proxy_new mm_gdbus_call_proxy_new_finish mm_gdbus_call_proxy_new_for_bus mm_gdbus_call_proxy_new_for_bus_finish mm_gdbus_call_proxy_new_for_bus_sync mm_gdbus_call_proxy_new_sync MmGdbusCallProxyClass MM_GDBUS_IS_CALL_PROXY MM_GDBUS_IS_CALL_PROXY_CLASS MM_GDBUS_CALL_PROXY MM_GDBUS_CALL_PROXY_CLASS MM_GDBUS_CALL_PROXY_GET_CLASS MM_GDBUS_TYPE_CALL_PROXY MmGdbusCallProxyPrivate mm_gdbus_call_proxy_get_type
MmGdbusCallSkeleton MmGdbusCallSkeleton MmGdbusCallSkeleton mm_gdbus_call_skeleton_new MmGdbusCallSkeletonClass MM_GDBUS_IS_CALL_SKELETON MM_GDBUS_IS_CALL_SKELETON_CLASS MM_GDBUS_CALL_SKELETON MM_GDBUS_CALL_SKELETON_CLASS MM_GDBUS_CALL_SKELETON_GET_CLASS MM_GDBUS_TYPE_CALL_SKELETON MmGdbusCallSkeletonPrivate mm_gdbus_call_skeleton_get_type
ModemManager-1.23.4-dev/docs/reference/libmm-glib/meson.build000066400000000000000000000020221456466623000237640ustar00rootroot00000000000000# SPDX-License-Identifier: GPL-2.0-or-later # Copyright (C) 2021 Iñigo Martinez doc_module = mm_glib_name src_dirs = [ generated_inc, libmm_glib_inc, ] private_headers = [ 'mm-common-helpers.h', 'mm-gdbus-test.h', 'mm-helpers.h', ] scan_args = [ '--deprecated-guards="MM_DISABLE_DEPRECATED"', '--rebuild-types', ] mkdb_args = [ '--ignore-files=mm-gdbus-test.h', '--ignore-files=mm-gdbus-test.c', ] fixxref_args = [ '--html-dir=' + (mm_prefix / gnome.gtkdoc_html_dir(doc_module)), '--extra-dir=' + mm_doc_path, ] version_xml = configure_file( input: 'version.xml.in', output: '@BASENAME@', configuration: version_conf, ) gnome.gtkdoc( doc_module, main_xml: doc_module + '-docs.xml', src_dir: src_dirs, ignore_headers: private_headers, include_directories: top_inc, dependencies: libmm_glib_dep, namespace: 'mm', scan_args: scan_args, mkdb_args: mkdb_args, fixxref_args: fixxref_args, html_assets: logos_pngs, content_files: version_xml, install: true, ) ModemManager-1.23.4-dev/docs/reference/libmm-glib/version.xml.in000066400000000000000000000000121456466623000244330ustar00rootroot00000000000000@VERSION@ ModemManager-1.23.4-dev/examples/000077500000000000000000000000001456466623000165435ustar00rootroot00000000000000ModemManager-1.23.4-dev/examples/modem-watcher-javascript/000077500000000000000000000000001456466623000234435ustar00rootroot00000000000000ModemManager-1.23.4-dev/examples/modem-watcher-javascript/README000066400000000000000000000015671456466623000243340ustar00rootroot00000000000000 The modem-watcher-javascript program makes use of the 'libmm-glib' library through GObject Introspection to talk to ModemManager. The program will just print in stdout whenever: * ModemManager is found in the bus * ModemManager is lost in the bus * A new modem is added to ModemManager * A modem is removed from ModemManager The output will look like this: $ ./modem-watcher-javascript [ModemWatcher] ModemManager service is available in bus [ModemWatcher] Vodafone (Huawei) (K3772) modem managed by ModemManager [861320000017897]: /org/freedesktop/ModemManager1/Modem/1 [ModemWatcher] modem unmanaged by ModemManager: /org/freedesktop/ModemManager1/Modem/1 [ModemWatcher] ModemManager service not available in bus Note that the program requires ModemManager and libmm-glib to be installed in the system and the introspection typelibs available in the standard paths. Have fun!ModemManager-1.23.4-dev/examples/modem-watcher-javascript/main.js000066400000000000000000000003201456466623000247200ustar00rootroot00000000000000 const Mainloop = imports.mainloop; const GLib = imports.gi.GLib; const modemWatcher = imports.modemWatcher; function start() { let watcher = new modemWatcher.ModemWatcher(); Mainloop.run(); } ModemManager-1.23.4-dev/examples/modem-watcher-javascript/modem-watcher-javascript000077500000000000000000000015541456466623000302760ustar00rootroot00000000000000#!/bin/sh # This program 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 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 Lesser General Public License for more # details. # # You should have received a copy of the GNU Lesser 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) 2014 Aleksander Morgado # exec gjs-console -I . -c "const Main = imports.main; Main.start();" ModemManager-1.23.4-dev/examples/modem-watcher-javascript/modemWatcher.js000066400000000000000000000065541456466623000264320ustar00rootroot00000000000000// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- // // This program 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 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 Lesser General Public License for more // details. // // You should have received a copy of the GNU Lesser 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) 2014 Aleksander Morgado // const Lang = imports.lang; const Mainloop = imports.mainloop; const Gio = imports.gi.Gio; const ModemManager = imports.gi.ModemManager; // The ModemWatcher class const ModemWatcher = new Lang.Class({ Name: 'ModemWatcher', _init: function() { // The manager this._manager = ModemManager.Manager.new_sync(Gio.DBus.system, Gio.DBusObjectManagerClientFlags.DO_NOT_AUTO_START, null) // Follow changes in the ModemManager1 interface name owner this._nameOwnerId = this._manager.connect('notify::name-owner', Lang.bind(this, this._ModemManagerNameOwnerChanged)); this._ModemManagerNameOwnerChanged() // Follow added/removed objects this._objectAddedId = this._manager.connect('object-added', Lang.bind(this, this._ModemAdded)); this._objectRemovedId = this._manager.connect('object-removed', Lang.bind(this, this._ModemRemoved)); // Add initial objects let modem_object_list = this._manager.get_objects(); for (let i = 0; i < modem_object_list.length; i++) this._ModemAdded(this._manager, modem_object_list[i]); }, _ModemManagerNameOwnerChanged: function() { if (this._manager.name_owner) print('[ModemWatcher] ModemManager ' + this._manager.get_version() + ' service is available in bus'); else print('[ModemWatcher] ModemManager service not available in bus'); }, _ModemAdded: function(manager, object) { let modem = object.get_modem(); print('[ModemWatcher] ' + modem.get_manufacturer() + ' (' + modem.get_model() + ') modem managed by ModemManager [' + modem.get_equipment_identifier() + ']: ' + object.get_object_path()); }, _ModemRemoved: function(manager, object) { print('[ModemWatcher] modem unmanaged by ModemManager: ' + object.get_object_path()); }, destroy: function() { if (this._nameOwnerId) { this._manager.disconnect(this._nameOwnerId); this._nameOwnerId = 0; } if (this._objectAddedId) { this._manager.disconnect(this._objectAddedId); this._objectAddedId = 0; } if (this._objectRemovedId) { this._manager.disconnect(this._objectRemovedId); this._objectRemovedId = 0; } } }); ModemManager-1.23.4-dev/examples/modem-watcher-python/000077500000000000000000000000001456466623000226165ustar00rootroot00000000000000ModemManager-1.23.4-dev/examples/modem-watcher-python/ModemWatcher.py000066400000000000000000000105611456466623000255520ustar00rootroot00000000000000#!/usr/bin/env python3 # -*- 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 Lesser 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 Lesser General Public License for more # details. # # You should have received a copy of the GNU Lesser 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) 2014 Aleksander Morgado # import gi gi.require_version('ModemManager', '1.0') from gi.repository import Gio, GLib, GObject, ModemManager class ModemWatcher: """ The ModemWatcher class is responsible for monitoring ModemManager. """ def __init__(self): # Flag for initial logs self.initializing = True # Setup DBus monitoring self.connection = Gio.bus_get_sync(Gio.BusType.SYSTEM, None) self.manager = ModemManager.Manager.new_sync( self.connection, Gio.DBusObjectManagerClientFlags.DO_NOT_AUTO_START, None) # IDs for added/removed signals self.object_added_id = 0 self.object_removed_id = 0 # Follow availability of the ModemManager process self.available = False self.manager.connect('notify::name-owner', self.on_name_owner) self.on_name_owner(self.manager, None) # Finish initialization self.initializing = False def set_available(self): """ ModemManager is now available. """ if not self.available or self.initializing: print('[ModemWatcher] ModemManager %s service is available in bus' % self.manager.get_version()) self.object_added_id = self.manager.connect('object-added', self.on_object_added) self.object_removed_id = self.manager.connect('object-removed', self.on_object_removed) self.available = True # Initial scan if self.initializing: for obj in self.manager.get_objects(): self.on_object_added(self.manager, obj) def set_unavailable(self): """ ModemManager is now unavailable. """ if self.available or self.initializing: print('[ModemWatcher] ModemManager service not available in bus') if self.object_added_id: self.manager.disconnect(self.object_added_id) self.object_added_id = 0 if self.object_removed_id: self.manager.disconnect(self.object_removed_id) self.object_removed_id = 0 self.available = False def on_name_owner(self, manager, prop): """ Name owner updates. """ if self.manager.get_name_owner(): self.set_available() else: self.set_unavailable() def on_modem_state_updated(self, modem, old, new, reason): """ Modem state updated """ print('[ModemWatcher] %s: modem state updated: %s -> %s (%s) ' % (modem.get_object_path(), ModemManager.ModemState.get_string (old), ModemManager.ModemState.get_string (new), ModemManager.ModemStateChangeReason.get_string (reason))) def on_object_added(self, manager, obj): """ Object added. """ modem = obj.get_modem() print('[ModemWatcher] %s: modem managed by ModemManager [%s]: %s (%s)' % (obj.get_object_path(), modem.get_equipment_identifier(), modem.get_manufacturer(), modem.get_model())) if modem.get_state() == ModemManager.ModemState.FAILED: print('[ModemWatcher] %s: ignoring failed modem' % obj.get_object_path()) else: modem.connect('state-changed', self.on_modem_state_updated) def on_object_removed(self, manager, obj): """ Object removed. """ print('[ModemWatcher] %s: modem unmanaged by ModemManager' % obj.get_object_path()) ModemManager-1.23.4-dev/examples/modem-watcher-python/README000066400000000000000000000015571456466623000235060ustar00rootroot00000000000000 The modem-watcher-python program makes use of the 'libmm-glib' library through GObject Introspection to talk to ModemManager. The program will just print in stdout whenever: * ModemManager is found in the bus * ModemManager is lost in the bus * A new modem is added to ModemManager * A modem is removed from ModemManager The output will look like this: $ ./modem-watcher-python [ModemWatcher] ModemManager service is available in bus [ModemWatcher] Vodafone (Huawei) (K3772) modem managed by ModemManager [861320000017897]: /org/freedesktop/ModemManager1/Modem/0 [ModemWatcher] modem unmanaged by ModemManager: /org/freedesktop/ModemManager1/Modem/0 [ModemWatcher] ModemManager service not available in bus Note that the program requires ModemManager and libmm-glib to be installed in the system and the introspection typelibs available in the standard paths. Have fun!ModemManager-1.23.4-dev/examples/modem-watcher-python/modem-watcher-python000077500000000000000000000027401456466623000266220ustar00rootroot00000000000000#!/usr/bin/env python3 # -*- 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 Lesser 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 Lesser General Public License for more # details. # # You should have received a copy of the GNU Lesser 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) 2014 Aleksander Morgado # import signal from gi.repository import GLib import ModemWatcher def signal_handler(loop): """SIGHUP and SIGINT handler.""" loop.quit() def main(): """Main routine.""" # Create modem watcher ModemWatcher.ModemWatcher() # Main loop main_loop = GLib.MainLoop() GLib.unix_signal_add( GLib.PRIORITY_HIGH, signal.SIGHUP, signal_handler, main_loop) GLib.unix_signal_add( GLib.PRIORITY_HIGH, signal.SIGTERM, signal_handler, main_loop) try: main_loop.run() except KeyboardInterrupt: pass if __name__ == "__main__": main() ModemManager-1.23.4-dev/examples/network-scan-python/000077500000000000000000000000001456466623000224755ustar00rootroot00000000000000ModemManager-1.23.4-dev/examples/network-scan-python/README000066400000000000000000000007631456466623000233630ustar00rootroot00000000000000 The network-scan-python program makes use of the 'libmm-glib' library through GObject Introspection to talk to ModemManager. The program will: * Detect whether ModemManager is found in the bus * Loop through each modem found in the system, running a network scan for each The output will look like this: $ ./network-scan-python Note that the program requires ModemManager and libmm-glib to be installed in the system and the introspection typelibs available in the standard paths. Have fun!ModemManager-1.23.4-dev/examples/network-scan-python/network-scan-python000077500000000000000000000053161456466623000263620ustar00rootroot00000000000000#!/usr/bin/env python3 # -*- 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 Lesser 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 Lesser General Public License for more # details. # # You should have received a copy of the GNU Lesser 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) 2019 Aleksander Morgado # import sys import time import gi gi.require_version('ModemManager', '1.0') from gi.repository import Gio, GLib, GObject, ModemManager def main(): """Main routine.""" # Process input arguments if len(sys.argv) != 1: sys.stderr.write('error: wrong number of arguments\n') sys.stdout.write('usage: network-scan-python\n') sys.exit(1) # Connection to ModemManager connection = Gio.bus_get_sync(Gio.BusType.SYSTEM, None) manager = ModemManager.Manager.new_sync( connection, Gio.DBusObjectManagerClientFlags.DO_NOT_AUTO_START, None) if not manager.get_name_owner(): sys.stderr.write('ModemManager not found in bus\n') sys.exit(2) # Iterate modems and scan network with each one by one for obj in manager.get_objects(): modem = obj.get_modem() modem.enable() time.sleep(1) modem3gpp = obj.get_modem_3gpp() if not modem3gpp: sys.stderr.write('%s: skipping unusable modem...\n' % obj.get_object_path()) continue modem3gpp.set_default_timeout(300000) print('%s: starting network scan...' % modem3gpp.get_object_path()) networks = modem3gpp.scan_sync() if networks: for network in networks: print('%s: %s - %s (%s, %s)' % ( network.get_operator_code(), network.get_operator_short(), network.get_operator_short(), ModemManager.modem_access_technology_build_string_from_mask( network.get_access_technology()), ModemManager.Modem3gppNetworkAvailability.get_string( network.get_availability()))) else: print('no networks found') if __name__ == "__main__": main() ModemManager-1.23.4-dev/examples/sms-c/000077500000000000000000000000001456466623000175655ustar00rootroot00000000000000ModemManager-1.23.4-dev/examples/sms-c/meson.build000066400000000000000000000005261456466623000217320ustar00rootroot00000000000000# SPDX-License-Identifier: GPL-2.0-or-later # Copyright (C) 2021 Aleksander Morgado example_units = [ 'sms-c-async', 'sms-c-sync', ] foreach example_unit: example_units executable( example_unit, example_unit + '.c', include_directories: top_inc, dependencies: libmm_glib_dep, ) endforeach ModemManager-1.23.4-dev/examples/sms-c/sms-c-async.c000066400000000000000000000136311456466623000220720ustar00rootroot00000000000000/* -*- 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. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Copyright (C) 2021 Aleksander Morgado */ #include "config.h" #include #include #include #include #include #include #include typedef struct { GMainLoop *loop; GDBusConnection *connection; MMManager *manager; GList *objects; MMSmsProperties *properties; MMObject *current_obj; MMModemMessaging *current_messaging; MMSms *current_sms; } Context; static void send_sms_next (Context *context); static void sms_send_ready (MMSms *sms, GAsyncResult *res, Context *context) { g_autoptr(GError) error = NULL; if (!mm_sms_send_finish (sms, res, &error)) g_printerr ("error: couldn't send sms in modem %s: %s\n", mm_object_get_path (context->current_obj), error->message); else g_print ("successfully sent sms in modem %s\n", mm_object_get_path (context->current_obj)); send_sms_next (context); } static void messaging_create_ready (MMModemMessaging *messaging, GAsyncResult *res, Context *context) { g_autoptr(GError) error = NULL; context->current_sms = mm_modem_messaging_create_finish (messaging, res, &error); if (!context->current_sms) { g_printerr ("error: couldn't create sms in modem %s: %s\n", mm_object_get_path (context->current_obj), error->message); send_sms_next (context); return; } mm_sms_send (context->current_sms, NULL, (GAsyncReadyCallback) sms_send_ready, context); } static void send_sms_next (Context *context) { g_clear_object (&context->current_sms); g_clear_object (&context->current_messaging); g_clear_object (&context->current_obj); if (!context->objects) { g_main_loop_quit (context->loop); return; } context->current_obj = context->objects->data; context->objects = g_list_delete_link (context->objects, context->objects); context->current_messaging = mm_object_get_modem_messaging (context->current_obj); if (!context->current_messaging) { g_printerr ("error: modem %s does not have messaging capabilities\n", mm_object_get_path (context->current_obj)); send_sms_next (context); return; } mm_modem_messaging_create (context->current_messaging, context->properties, NULL, (GAsyncReadyCallback)messaging_create_ready, context); } static void manager_new_ready (GObject *source, GAsyncResult *res, Context *context) { g_autofree gchar *name_owner = NULL; g_autoptr(GError) error = NULL; context->manager = mm_manager_new_finish (res, &error); if (!context->connection) { g_printerr ("error: couldn't get manager: %s\n", error->message); exit (EXIT_FAILURE); } name_owner = g_dbus_object_manager_client_get_name_owner (G_DBUS_OBJECT_MANAGER_CLIENT (context->manager)); if (!name_owner) { g_printerr ("error: ModemManager not found in the system bus\n"); exit (EXIT_FAILURE); } context->objects = g_dbus_object_manager_get_objects (G_DBUS_OBJECT_MANAGER (context->manager)); if (!context->objects) { g_printerr ("error: no modems found\n"); exit (EXIT_FAILURE); } send_sms_next (context); } static void bus_get_ready (GObject *source, GAsyncResult *res, Context *context) { g_autoptr(GError) error = NULL; context->connection = g_bus_get_finish (res, &error); if (!context->connection) { g_printerr ("error: couldn't get bus: %s\n", error->message); exit (EXIT_FAILURE); } mm_manager_new (context->connection, G_DBUS_OBJECT_MANAGER_CLIENT_FLAGS_DO_NOT_AUTO_START, NULL, (GAsyncReadyCallback) manager_new_ready, context); } int main (int argc, char **argv) { Context context = { 0 }; if (argc < 3) { g_printerr ("error: missing arguments\n"); g_printerr ("usage: %s \n", argv[0]); exit (EXIT_FAILURE); } context.properties = mm_sms_properties_new (); mm_sms_properties_set_number (context.properties, argv[1]); mm_sms_properties_set_text (context.properties, argv[2]); g_bus_get (G_BUS_TYPE_SYSTEM, NULL, (GAsyncReadyCallback) bus_get_ready, &context); context.loop = g_main_loop_new (NULL, FALSE); g_main_loop_run (context.loop); g_assert (!context.current_obj); g_assert (!context.current_messaging); g_assert (!context.current_sms); g_main_loop_unref (context.loop); g_clear_object (&context.connection); g_clear_object (&context.manager); g_clear_object (&context.properties); g_list_free_full (g_steal_pointer (&context.objects), g_object_unref); return 0; } ModemManager-1.23.4-dev/examples/sms-c/sms-c-sync.c000066400000000000000000000114061456466623000217270ustar00rootroot00000000000000/* -*- 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. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Copyright (C) 2021 Aleksander Morgado */ #include "config.h" #include #include #include #include #include #include #include static gboolean loop_ready (GMainLoop *loop) { g_main_loop_quit (loop); return G_SOURCE_REMOVE; } static void send_sms (MMObject *obj, MMSmsProperties *properties) { g_autoptr(MMModemMessaging) messaging = NULL; g_autoptr(MMSms) sms = NULL; g_autoptr(GError) error = NULL; messaging = mm_object_get_modem_messaging (obj); if (!messaging) { g_printerr ("error: modem %s does not have messaging capabilities\n", mm_object_get_path (obj)); return; } sms = mm_modem_messaging_create_sync (messaging, properties, NULL, &error); if (!sms) { g_printerr ("error: couldn't create sms in modem %s: %s\n", mm_object_get_path (obj), error->message); return; } if (!mm_sms_send_sync (sms, NULL, &error)) { g_printerr ("error: couldn't send sms in modem %s: %s\n", mm_object_get_path (obj), error->message); return; } g_print ("successfully sent sms in modem %s\n", mm_object_get_path (obj)); } int main (int argc, char **argv) { g_autoptr(GDBusConnection) connection = NULL; g_autoptr(MMManager) manager = NULL; g_autoptr(GError) error = NULL; g_autolist(MMObject) objects = NULL; g_autoptr(MMSmsProperties) properties = NULL; g_autofree gchar *name_owner = NULL; if (argc < 3) { g_printerr ("error: missing arguments\n"); g_printerr ("usage: %s \n", argv[0]); exit (EXIT_FAILURE); } connection = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, &error); if (!connection) { g_printerr ("error: couldn't get bus: %s\n", error->message); exit (EXIT_FAILURE); } manager = mm_manager_new_sync (connection, G_DBUS_OBJECT_MANAGER_CLIENT_FLAGS_DO_NOT_AUTO_START, NULL, &error); if (!manager) { g_printerr ("error: couldn't get manager: %s\n", error->message); exit (EXIT_FAILURE); } name_owner = g_dbus_object_manager_client_get_name_owner (G_DBUS_OBJECT_MANAGER_CLIENT (manager)); if (!name_owner) { g_printerr ("error: ModemManager not found in the system bus\n"); exit (EXIT_FAILURE); } objects = g_dbus_object_manager_get_objects (G_DBUS_OBJECT_MANAGER (manager)); if (!objects) { g_printerr ("error: no modems found\n"); exit (EXIT_FAILURE); } properties = mm_sms_properties_new (); mm_sms_properties_set_number (properties, argv[1]); mm_sms_properties_set_text (properties, argv[2]); g_list_foreach (objects, (GFunc)send_sms, properties); g_clear_object (&connection); g_clear_object (&manager); g_list_free_full (g_steal_pointer (&objects), g_object_unref); /* This main loop is responsible for processing all async events that may * have been scheduled in the previous sync operations, e.g. when disposing * some of the GDBus related objects created before. Without this loop, * the events scheduled in the main context would never be fully released. * This is obviously not a problem in this example as it will just exit, but * it can serve as guideline for others integrating sync-only libmm-glib * opertions in other programs. By creating an ephimeral main loop here and * appending an idle task (that will be run after all the other idle tasks * that may have been already scheduled), we make sure all are processed and * removed before exiting.*/ { g_autoptr(GMainLoop) loop = NULL; loop = g_main_loop_new (NULL, FALSE); g_idle_add ((GSourceFunc)loop_ready, loop); g_main_loop_run (loop); } return 0; } ModemManager-1.23.4-dev/examples/sms-python/000077500000000000000000000000001456466623000206645ustar00rootroot00000000000000ModemManager-1.23.4-dev/examples/sms-python/README000066400000000000000000000012231456466623000215420ustar00rootroot00000000000000 The sms-python program makes use of the 'libmm-glib' library through GObject Introspection to talk to ModemManager. The program will: * Detect whether ModemManager is found in the bus * Prepare SMS properties object with the provided Number and Text. * Loop through each modem found in the system, and for each: ** Create a SMS ** Send the SMS The output will look like this: $ ./sms-python "+1234567890" "hello there, how are you?" /org/freedesktop/ModemManager1/Modem/0: sms sent Note that the program requires ModemManager and libmm-glib to be installed in the system and the introspection typelibs available in the standard paths. Have fun!ModemManager-1.23.4-dev/examples/sms-python/sms-python000077500000000000000000000040301456466623000227300ustar00rootroot00000000000000#!/usr/bin/env python3 # -*- 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 Lesser 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 Lesser General Public License for more # details. # # You should have received a copy of the GNU Lesser 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) 2016 Aleksander Morgado # import sys import gi gi.require_version('ModemManager', '1.0') from gi.repository import Gio, GLib, GObject, ModemManager def main(): """Main routine.""" # Process input arguments if len(sys.argv) != 3: sys.stderr.write('error: wrong number of arguments\n') sys.stdout.write('usage: sms-python \n') sys.exit(1) # Prepare SMS properties sms_properties = ModemManager.SmsProperties.new() sms_properties.set_number(sys.argv[1]) sms_properties.set_text(sys.argv[2]) # Connection to ModemManager connection = Gio.bus_get_sync(Gio.BusType.SYSTEM, None) manager = ModemManager.Manager.new_sync( connection, Gio.DBusObjectManagerClientFlags.DO_NOT_AUTO_START, None) if not manager.get_name_owner(): sys.stderr.write('ModemManager not found in bus') sys.exit(2) # Iterate modems and send SMS with each for obj in manager.get_objects(): messaging = obj.get_modem_messaging() sms = messaging.create_sync(sms_properties) sms.send_sync() print('%s: sms sent' % messaging.get_object_path()) if __name__ == "__main__": main() ModemManager-1.23.4-dev/include/000077500000000000000000000000001456466623000163505ustar00rootroot00000000000000ModemManager-1.23.4-dev/include/ModemManager-compat.h000066400000000000000000000747631456466623000223570ustar00rootroot00000000000000/* -*- 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) 2017 Google, Inc. */ #ifndef _MODEMMANAGER_COMPAT_H_ #define _MODEMMANAGER_COMPAT_H_ #if !defined (__MODEM_MANAGER_H_INSIDE__) #error "Only can be included directly." #endif #include /** * SECTION:mm-compat * @title: API break replacements * @short_description: List of deprecated methods, types and symbols. * * These compatibility types and methods are flagged as deprecated and * therefore shouldn't be used in newly written code. They are provided to * avoid unnecessary API/ABI breaks. */ /* deprecated attribute support since gcc 3.1 */ #if defined __GNUC__ && (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1)) # define MM_DEPRECATED __attribute__((__deprecated__)) #else # define MM_DEPRECATED #endif #ifndef MM_DISABLE_DEPRECATED /* The following type exists just so that we can get deprecation warnings */ MM_DEPRECATED typedef int MMModemBandDeprecated; /** * MM_MODEM_BAND_U2100: * * WCDMA 2100 MHz (UTRAN band 1). * * Since: 1.0 * Deprecated: 1.8: Use #MM_MODEM_BAND_UTRAN_1 instead. */ #define MM_MODEM_BAND_U2100 ((MMModemBandDeprecated)MM_MODEM_BAND_UTRAN_1) /** * MM_MODEM_BAND_U1900: * * WCDMA 1900 MHz (UTRAN band 2). * * Since: 1.0 * Deprecated: 1.8: Use #MM_MODEM_BAND_UTRAN_2 instead. */ #define MM_MODEM_BAND_U1900 ((MMModemBandDeprecated)MM_MODEM_BAND_UTRAN_2) /** * MM_MODEM_BAND_U1800: * * WCDMA 1800 MHz (UTRAN band 3). * * Since: 1.0 * Deprecated: 1.8: Use #MM_MODEM_BAND_UTRAN_3 instead. */ #define MM_MODEM_BAND_U1800 ((MMModemBandDeprecated)MM_MODEM_BAND_UTRAN_3) /** * MM_MODEM_BAND_U17IV: * * AWS 1700/2100 MHz (UTRAN band 4). * * Since: 1.0 * Deprecated: 1.8: Use #MM_MODEM_BAND_UTRAN_4 instead. */ #define MM_MODEM_BAND_U17IV ((MMModemBandDeprecated)MM_MODEM_BAND_UTRAN_4) /** * MM_MODEM_BAND_U850: * * UMTS 850 MHz (UTRAN band 5). * * Since: 1.0 * Deprecated: 1.8: Use #MM_MODEM_BAND_UTRAN_5 instead. */ #define MM_MODEM_BAND_U850 ((MMModemBandDeprecated)MM_MODEM_BAND_UTRAN_5) /** * MM_MODEM_BAND_U800: * * UMTS 800 MHz (UTRAN band 6). * * Since: 1.0 * Deprecated: 1.8: Use #MM_MODEM_BAND_UTRAN_6 instead. */ #define MM_MODEM_BAND_U800 ((MMModemBandDeprecated)MM_MODEM_BAND_UTRAN_6) /** * MM_MODEM_BAND_U2600: * * UMTS 2600 MHz (UTRAN band 7). * * Since: 1.0 * Deprecated: 1.8: Use #MM_MODEM_BAND_UTRAN_7 instead. */ #define MM_MODEM_BAND_U2600 ((MMModemBandDeprecated)MM_MODEM_BAND_UTRAN_7) /** * MM_MODEM_BAND_U900: * * UMTS 900 MHz (UTRAN band 8). * * Since: 1.0 * Deprecated: 1.8: Use #MM_MODEM_BAND_UTRAN_8 instead. */ #define MM_MODEM_BAND_U900 ((MMModemBandDeprecated)MM_MODEM_BAND_UTRAN_8) /** * MM_MODEM_BAND_U17IX: * * UMTS 1700 MHz (UTRAN band 9). * * Since: 1.0 * Deprecated: 1.8: Use #MM_MODEM_BAND_UTRAN_9 instead. */ #define MM_MODEM_BAND_U17IX ((MMModemBandDeprecated)MM_MODEM_BAND_UTRAN_9) /** * MM_MODEM_BAND_EUTRAN_I: * * E-UTRAN band 1. * * Since: 1.0 * Deprecated: 1.8: Use #MM_MODEM_BAND_EUTRAN_1 instead. */ #define MM_MODEM_BAND_EUTRAN_I ((MMModemBandDeprecated)MM_MODEM_BAND_EUTRAN_1) /** * MM_MODEM_BAND_EUTRAN_II: * * E-UTRAN band 2. * * Since: 1.0 * Deprecated: 1.8: Use #MM_MODEM_BAND_EUTRAN_2 instead. */ #define MM_MODEM_BAND_EUTRAN_II ((MMModemBandDeprecated)MM_MODEM_BAND_EUTRAN_2) /** * MM_MODEM_BAND_EUTRAN_III: * * E-UTRAN band 3. * * Since: 1.0 * Deprecated: 1.8: Use #MM_MODEM_BAND_EUTRAN_3 instead. */ #define MM_MODEM_BAND_EUTRAN_III ((MMModemBandDeprecated)MM_MODEM_BAND_EUTRAN_3) /** * MM_MODEM_BAND_EUTRAN_IV: * * E-UTRAN band 4. * * Since: 1.0 * Deprecated: 1.8: Use #MM_MODEM_BAND_EUTRAN_4 instead. */ #define MM_MODEM_BAND_EUTRAN_IV ((MMModemBandDeprecated)MM_MODEM_BAND_EUTRAN_4) /** * MM_MODEM_BAND_EUTRAN_V: * * E-UTRAN band 5. * * Since: 1.0 * Deprecated: 1.8: Use #MM_MODEM_BAND_EUTRAN_5 instead. */ #define MM_MODEM_BAND_EUTRAN_V ((MMModemBandDeprecated)MM_MODEM_BAND_EUTRAN_5) /** * MM_MODEM_BAND_EUTRAN_VI: * * E-UTRAN band 6. * * Since: 1.0 * Deprecated: 1.8: Use #MM_MODEM_BAND_EUTRAN_6 instead. */ #define MM_MODEM_BAND_EUTRAN_VI ((MMModemBandDeprecated)MM_MODEM_BAND_EUTRAN_6) /** * MM_MODEM_BAND_EUTRAN_VII: * * E-UTRAN band 7. * * Since: 1.0 * Deprecated: 1.8: Use #MM_MODEM_BAND_EUTRAN_7 instead. */ #define MM_MODEM_BAND_EUTRAN_VII ((MMModemBandDeprecated)MM_MODEM_BAND_EUTRAN_7) /** * MM_MODEM_BAND_EUTRAN_VIII: * * E-UTRAN band 8. * * Since: 1.0 * Deprecated: 1.8: Use #MM_MODEM_BAND_EUTRAN_8 instead. */ #define MM_MODEM_BAND_EUTRAN_VIII ((MMModemBandDeprecated)MM_MODEM_BAND_EUTRAN_8) /** * MM_MODEM_BAND_EUTRAN_IX: * * E-UTRAN band 9. * * Since: 1.0 * Deprecated: 1.8: Use #MM_MODEM_BAND_EUTRAN_9 instead. */ #define MM_MODEM_BAND_EUTRAN_IX ((MMModemBandDeprecated)MM_MODEM_BAND_EUTRAN_9) /** * MM_MODEM_BAND_EUTRAN_X: * * E-UTRAN band 10. * * Since: 1.0 * Deprecated: 1.8: Use #MM_MODEM_BAND_EUTRAN_10 instead. */ #define MM_MODEM_BAND_EUTRAN_X ((MMModemBandDeprecated)MM_MODEM_BAND_EUTRAN_10) /** * MM_MODEM_BAND_EUTRAN_XI: * * E-UTRAN band 11. * * Since: 1.0 * Deprecated: 1.8: Use #MM_MODEM_BAND_EUTRAN_11 instead. */ #define MM_MODEM_BAND_EUTRAN_XI ((MMModemBandDeprecated)MM_MODEM_BAND_EUTRAN_11) /** * MM_MODEM_BAND_EUTRAN_XII: * * E-UTRAN band 12. * * Since: 1.0 * Deprecated: 1.8: Use #MM_MODEM_BAND_EUTRAN_12 instead. */ #define MM_MODEM_BAND_EUTRAN_XII ((MMModemBandDeprecated)MM_MODEM_BAND_EUTRAN_12) /** * MM_MODEM_BAND_EUTRAN_XIII: * * E-UTRAN band 13. * * Since: 1.0 * Deprecated: 1.8: Use #MM_MODEM_BAND_EUTRAN_13 instead. */ #define MM_MODEM_BAND_EUTRAN_XIII ((MMModemBandDeprecated)MM_MODEM_BAND_EUTRAN_13) /** * MM_MODEM_BAND_EUTRAN_XIV: * * E-UTRAN band 14. * * Since: 1.0 * Deprecated: 1.8: Use #MM_MODEM_BAND_EUTRAN_14 instead. */ #define MM_MODEM_BAND_EUTRAN_XIV ((MMModemBandDeprecated)MM_MODEM_BAND_EUTRAN_14) /** * MM_MODEM_BAND_EUTRAN_XVII: * * E-UTRAN band 17. * * Since: 1.0 * Deprecated: 1.8: Use #MM_MODEM_BAND_EUTRAN_17 instead. */ #define MM_MODEM_BAND_EUTRAN_XVII ((MMModemBandDeprecated)MM_MODEM_BAND_EUTRAN_17) /** * MM_MODEM_BAND_EUTRAN_XVIII: * * E-UTRAN band 18. * * Since: 1.0 * Deprecated: 1.8: Use #MM_MODEM_BAND_EUTRAN_18 instead. */ #define MM_MODEM_BAND_EUTRAN_XVIII ((MMModemBandDeprecated)MM_MODEM_BAND_EUTRAN_18) /** * MM_MODEM_BAND_EUTRAN_XIX: * * E-UTRAN band 19. * * Since: 1.0 * Deprecated: 1.8: Use #MM_MODEM_BAND_EUTRAN_19 instead. */ #define MM_MODEM_BAND_EUTRAN_XIX ((MMModemBandDeprecated)MM_MODEM_BAND_EUTRAN_19) /** * MM_MODEM_BAND_EUTRAN_XX: * * E-UTRAN band 20. * * Since: 1.0 * Deprecated: 1.8: Use #MM_MODEM_BAND_EUTRAN_20 instead. */ #define MM_MODEM_BAND_EUTRAN_XX ((MMModemBandDeprecated)MM_MODEM_BAND_EUTRAN_20) /** * MM_MODEM_BAND_EUTRAN_XXI: * * E-UTRAN band 21. * * Since: 1.0 * Deprecated: 1.8: Use #MM_MODEM_BAND_EUTRAN_21 instead. */ #define MM_MODEM_BAND_EUTRAN_XXI ((MMModemBandDeprecated)MM_MODEM_BAND_EUTRAN_21) /** * MM_MODEM_BAND_EUTRAN_XXII: * * E-UTRAN band 22. * * Since: 1.0 * Deprecated: 1.8: Use #MM_MODEM_BAND_EUTRAN_22 instead. */ #define MM_MODEM_BAND_EUTRAN_XXII ((MMModemBandDeprecated)MM_MODEM_BAND_EUTRAN_22) /** * MM_MODEM_BAND_EUTRAN_XXIII: * * E-UTRAN band 23. * * Since: 1.0 * Deprecated: 1.8: Use #MM_MODEM_BAND_EUTRAN_23 instead. */ #define MM_MODEM_BAND_EUTRAN_XXIII ((MMModemBandDeprecated)MM_MODEM_BAND_EUTRAN_23) /** * MM_MODEM_BAND_EUTRAN_XXIV: * * E-UTRAN band 24. * * Since: 1.0 * Deprecated: 1.8: Use #MM_MODEM_BAND_EUTRAN_24 instead. */ #define MM_MODEM_BAND_EUTRAN_XXIV ((MMModemBandDeprecated)MM_MODEM_BAND_EUTRAN_24) /** * MM_MODEM_BAND_EUTRAN_XXV: * * E-UTRAN band 25. * * Since: 1.0 * Deprecated: 1.8: Use #MM_MODEM_BAND_EUTRAN_25 instead. */ #define MM_MODEM_BAND_EUTRAN_XXV ((MMModemBandDeprecated)MM_MODEM_BAND_EUTRAN_25) /** * MM_MODEM_BAND_EUTRAN_XXVI: * * E-UTRAN band 26. * * Since: 1.0 * Deprecated: 1.8: Use #MM_MODEM_BAND_EUTRAN_26 instead. */ #define MM_MODEM_BAND_EUTRAN_XXVI ((MMModemBandDeprecated)MM_MODEM_BAND_EUTRAN_26) /** * MM_MODEM_BAND_EUTRAN_XXXIII: * * E-UTRAN band 33. * * Since: 1.0 * Deprecated: 1.8: Use #MM_MODEM_BAND_EUTRAN_33 instead. */ #define MM_MODEM_BAND_EUTRAN_XXXIII ((MMModemBandDeprecated)MM_MODEM_BAND_EUTRAN_33) /** * MM_MODEM_BAND_EUTRAN_XXXIV: * * E-UTRAN band 34. * * Since: 1.0 * Deprecated: 1.8: Use #MM_MODEM_BAND_EUTRAN_34 instead. */ #define MM_MODEM_BAND_EUTRAN_XXXIV ((MMModemBandDeprecated)MM_MODEM_BAND_EUTRAN_34) /** * MM_MODEM_BAND_EUTRAN_XXXV: * * E-UTRAN band 35. * * Since: 1.0 * Deprecated: 1.8: Use #MM_MODEM_BAND_EUTRAN_35 instead. */ #define MM_MODEM_BAND_EUTRAN_XXXV ((MMModemBandDeprecated)MM_MODEM_BAND_EUTRAN_35) /** * MM_MODEM_BAND_EUTRAN_XXXVI: * * E-UTRAN band 36. * * Since: 1.0 * Deprecated: 1.8: Use #MM_MODEM_BAND_EUTRAN_36 instead. */ #define MM_MODEM_BAND_EUTRAN_XXXVI ((MMModemBandDeprecated)MM_MODEM_BAND_EUTRAN_36) /** * MM_MODEM_BAND_EUTRAN_XXXVII: * * E-UTRAN band 37. * * Since: 1.0 * Deprecated: 1.8: Use #MM_MODEM_BAND_EUTRAN_37 instead. */ #define MM_MODEM_BAND_EUTRAN_XXXVII ((MMModemBandDeprecated)MM_MODEM_BAND_EUTRAN_37) /** * MM_MODEM_BAND_EUTRAN_XXXVIII: * * E-UTRAN band 38. * * Since: 1.0 * Deprecated: 1.8: Use #MM_MODEM_BAND_EUTRAN_38 instead. */ #define MM_MODEM_BAND_EUTRAN_XXXVIII ((MMModemBandDeprecated)MM_MODEM_BAND_EUTRAN_38) /** * MM_MODEM_BAND_EUTRAN_XXXIX: * * E-UTRAN band 39. * * Since: 1.0 * Deprecated: 1.8: Use #MM_MODEM_BAND_EUTRAN_39 instead. */ #define MM_MODEM_BAND_EUTRAN_XXXIX ((MMModemBandDeprecated)MM_MODEM_BAND_EUTRAN_39) /** * MM_MODEM_BAND_EUTRAN_XL: * * E-UTRAN band 40. * * Since: 1.0 * Deprecated: 1.8: Use #MM_MODEM_BAND_EUTRAN_40 instead. */ #define MM_MODEM_BAND_EUTRAN_XL ((MMModemBandDeprecated)MM_MODEM_BAND_EUTRAN_40) /** * MM_MODEM_BAND_EUTRAN_XLI: * * E-UTRAN band 41. * * Since: 1.0 * Deprecated: 1.8: Use #MM_MODEM_BAND_EUTRAN_41 instead. */ #define MM_MODEM_BAND_EUTRAN_XLI ((MMModemBandDeprecated)MM_MODEM_BAND_EUTRAN_41) /** * MM_MODEM_BAND_EUTRAN_XLII: * * E-UTRAN band 42. * * Since: 1.0 * Deprecated: 1.8: Use #MM_MODEM_BAND_EUTRAN_42 instead. */ #define MM_MODEM_BAND_EUTRAN_XLII ((MMModemBandDeprecated)MM_MODEM_BAND_EUTRAN_42) /** * MM_MODEM_BAND_EUTRAN_XLIII: * * E-UTRAN band 43. * * Since: 1.0 * Deprecated: 1.8: Use #MM_MODEM_BAND_EUTRAN_43 instead. */ #define MM_MODEM_BAND_EUTRAN_XLIII ((MMModemBandDeprecated)MM_MODEM_BAND_EUTRAN_43) /** * MM_MODEM_BAND_EUTRAN_XLIV: * * E-UTRAN band 44. * * Since: 1.4 * Deprecated: 1.8: Use #MM_MODEM_BAND_EUTRAN_44 instead. */ #define MM_MODEM_BAND_EUTRAN_XLIV ((MMModemBandDeprecated)MM_MODEM_BAND_EUTRAN_44) /** * MM_MODEM_BAND_CDMA_BC0_CELLULAR_800: * * CDMA Band Class 0 (US Cellular 850MHz) * * Since: 1.0 * Deprecated: 1.8: Use #MM_MODEM_BAND_CDMA_BC0 instead. */ #define MM_MODEM_BAND_CDMA_BC0_CELLULAR_800 ((MMModemBandDeprecated)MM_MODEM_BAND_CDMA_BC0) /** * MM_MODEM_BAND_CDMA_BC1_PCS_1900: * * CDMA Band Class 1 (US PCS 1900MHz). * * Since: 1.0 * Deprecated: 1.8: Use #MM_MODEM_BAND_CDMA_BC1 instead. */ #define MM_MODEM_BAND_CDMA_BC1_PCS_1900 ((MMModemBandDeprecated)MM_MODEM_BAND_CDMA_BC1) /** * MM_MODEM_BAND_CDMA_BC2_TACS: * * CDMA Band Class 2 (UK TACS 900MHz). * * Since: 1.0 * Deprecated: 1.8: Use #MM_MODEM_BAND_CDMA_BC2 instead. */ #define MM_MODEM_BAND_CDMA_BC2_TACS ((MMModemBandDeprecated)MM_MODEM_BAND_CDMA_BC2) /** * MM_MODEM_BAND_CDMA_BC3_JTACS: * * CDMA Band Class 3 (Japanese TACS). * * Since: 1.0 * Deprecated: 1.8: Use #MM_MODEM_BAND_CDMA_BC3 instead. */ #define MM_MODEM_BAND_CDMA_BC3_JTACS ((MMModemBandDeprecated)MM_MODEM_BAND_CDMA_BC3) /** * MM_MODEM_BAND_CDMA_BC4_KOREAN_PCS: * * CDMA Band Class 4 (Korean PCS). * * Since: 1.0 * Deprecated: 1.8: Use #MM_MODEM_BAND_CDMA_BC4 instead. */ #define MM_MODEM_BAND_CDMA_BC4_KOREAN_PCS ((MMModemBandDeprecated)MM_MODEM_BAND_CDMA_BC4) /** * MM_MODEM_BAND_CDMA_BC5_NMT450: * * CDMA Band Class 5 (NMT 450MHz). * * Since: 1.0 * Deprecated: 1.8: Use #MM_MODEM_BAND_CDMA_BC5 instead. */ #define MM_MODEM_BAND_CDMA_BC5_NMT450 ((MMModemBandDeprecated)MM_MODEM_BAND_CDMA_BC5) /** * MM_MODEM_BAND_CDMA_BC6_IMT2000: * * CDMA Band Class 6 (IMT2000 2100MHz). * * Since: 1.0 * Deprecated: 1.8: Use #MM_MODEM_BAND_CDMA_BC6 instead. */ #define MM_MODEM_BAND_CDMA_BC6_IMT2000 ((MMModemBandDeprecated)MM_MODEM_BAND_CDMA_BC6) /** * MM_MODEM_BAND_CDMA_BC7_CELLULAR_700: * * CDMA Band Class 7 (Cellular 700MHz). * * Since: 1.0 * Deprecated: 1.8: Use #MM_MODEM_BAND_CDMA_BC7 instead. */ #define MM_MODEM_BAND_CDMA_BC7_CELLULAR_700 ((MMModemBandDeprecated)MM_MODEM_BAND_CDMA_BC7) /** * MM_MODEM_BAND_CDMA_BC8_1800: * * CDMA Band Class 8 (1800MHz). * * Since: 1.0 * Deprecated: 1.8: Use #MM_MODEM_BAND_CDMA_BC8 instead. */ #define MM_MODEM_BAND_CDMA_BC8_1800 ((MMModemBandDeprecated)MM_MODEM_BAND_CDMA_BC8) /** * MM_MODEM_BAND_CDMA_BC9_900: * * CDMA Band Class 9 (900MHz). * * Since: 1.0 * Deprecated: 1.8: Use #MM_MODEM_BAND_CDMA_BC9 instead. */ #define MM_MODEM_BAND_CDMA_BC9_900 ((MMModemBandDeprecated)MM_MODEM_BAND_CDMA_BC9) /** * MM_MODEM_BAND_CDMA_BC10_SECONDARY_800: * * CDMA Band Class 10 (US Secondary 800). * * Since: 1.0 * Deprecated: 1.8: Use #MM_MODEM_BAND_CDMA_BC10 instead. */ #define MM_MODEM_BAND_CDMA_BC10_SECONDARY_800 ((MMModemBandDeprecated)MM_MODEM_BAND_CDMA_BC10) /** * MM_MODEM_BAND_CDMA_BC11_PAMR_400: * * CDMA Band Class 11 (European PAMR 400MHz). * * Since: 1.0 * Deprecated: 1.8: Use #MM_MODEM_BAND_CDMA_BC11 instead. */ #define MM_MODEM_BAND_CDMA_BC11_PAMR_400 ((MMModemBandDeprecated)MM_MODEM_BAND_CDMA_BC11) /** * MM_MODEM_BAND_CDMA_BC12_PAMR_800: * * CDMA Band Class 12 (PAMR 800MHz). * * Since: 1.0 * Deprecated: 1.8: Use #MM_MODEM_BAND_CDMA_BC12 instead. */ #define MM_MODEM_BAND_CDMA_BC12_PAMR_800 ((MMModemBandDeprecated)MM_MODEM_BAND_CDMA_BC12) /** * MM_MODEM_BAND_CDMA_BC13_IMT2000_2500: * * CDMA Band Class 13 (IMT2000 2500MHz Expansion). * * Since: 1.0 * Deprecated: 1.8: Use #MM_MODEM_BAND_CDMA_BC13 instead. */ #define MM_MODEM_BAND_CDMA_BC13_IMT2000_2500 ((MMModemBandDeprecated)MM_MODEM_BAND_CDMA_BC13) /** * MM_MODEM_BAND_CDMA_BC14_PCS2_1900: * * CDMA Band Class 14 (More US PCS 1900MHz). * * Since: 1.0 * Deprecated: 1.8: Use #MM_MODEM_BAND_CDMA_BC14 instead. */ #define MM_MODEM_BAND_CDMA_BC14_PCS2_1900 ((MMModemBandDeprecated)MM_MODEM_BAND_CDMA_BC14) /** * MM_MODEM_BAND_CDMA_BC15_AWS: * * CDMA Band Class 15 (AWS 1700MHz). * * Since: 1.0 * Deprecated: 1.8: Use #MM_MODEM_BAND_CDMA_BC15 instead. */ #define MM_MODEM_BAND_CDMA_BC15_AWS ((MMModemBandDeprecated)MM_MODEM_BAND_CDMA_BC15) /** * MM_MODEM_BAND_CDMA_BC16_US_2500: * * CDMA Band Class 16 (US 2500MHz). * * Since: 1.0 * Deprecated: 1.8: Use #MM_MODEM_BAND_CDMA_BC16 instead. */ #define MM_MODEM_BAND_CDMA_BC16_US_2500 ((MMModemBandDeprecated)MM_MODEM_BAND_CDMA_BC16) /** * MM_MODEM_BAND_CDMA_BC17_US_FLO_2500: * * CDMA Band Class 17 (US 2500MHz Forward Link Only). * * Since: 1.0 * Deprecated: 1.8: Use #MM_MODEM_BAND_CDMA_BC17 instead. */ #define MM_MODEM_BAND_CDMA_BC17_US_FLO_2500 ((MMModemBandDeprecated)MM_MODEM_BAND_CDMA_BC17) /** * MM_MODEM_BAND_CDMA_BC18_US_PS_700: * * CDMA Band Class 18 (US 700MHz Public Safety). * * Since: 1.0 * Deprecated: 1.8: Use #MM_MODEM_BAND_CDMA_BC18 instead. */ #define MM_MODEM_BAND_CDMA_BC18_US_PS_700 ((MMModemBandDeprecated)MM_MODEM_BAND_CDMA_BC18) /** * MM_MODEM_BAND_CDMA_BC19_US_LOWER_700: * * CDMA Band Class 19 (US Lower 700MHz). * * Since: 1.0 * Deprecated: 1.8: Use #MM_MODEM_BAND_CDMA_BC19 instead. */ #define MM_MODEM_BAND_CDMA_BC19_US_LOWER_700 ((MMModemBandDeprecated)MM_MODEM_BAND_CDMA_BC19) /* The following type exists just so that we can get deprecation warnings */ MM_DEPRECATED typedef int MMModemLocationSourceDeprecated; /** * MM_MODEM_LOCATION_SOURCE_AGPS: * * A-GPS location requested. * * Since: 1.6 * Deprecated: 1.12.0: Use #MM_MODEM_LOCATION_SOURCE_AGPS_MSA instead. */ #define MM_MODEM_LOCATION_SOURCE_AGPS ((MMModemLocationSourceDeprecated)MM_MODEM_LOCATION_SOURCE_AGPS_MSA) /* The following type exists just so that we can get deprecation warnings */ MM_DEPRECATED typedef int MMModemCapabilityDeprecated; /** * MM_MODEM_CAPABILITY_LTE_ADVANCED: * * Modem has LTE Advanced data capability. * * This value is deprecated because it is not used anywhere. LTE Advanced * capable devices are reported as LTE capable. * * Since: 1.0 * Deprecated: 1.14.0. */ #define MM_MODEM_CAPABILITY_LTE_ADVANCED ((MMModemCapabilityDeprecated)(1 << 4)) /* The following type exists just so that we can get deprecation warnings */ MM_DEPRECATED typedef int MMMobileEquipmentErrorDeprecated; /** * MM_MOBILE_EQUIPMENT_ERROR_GPRS_IMSI_UNKNOWN_IN_HLR: * * Since: 1.0 * Deprecated: 1.18.0: Use #MM_MOBILE_EQUIPMENT_ERROR_IMSI_UNKNOWN_IN_HSS instead. */ #define MM_MOBILE_EQUIPMENT_ERROR_GPRS_IMSI_UNKNOWN_IN_HLR (MMMobileEquipmentErrorDeprecated)MM_MOBILE_EQUIPMENT_ERROR_IMSI_UNKNOWN_IN_HSS /** * MM_MOBILE_EQUIPMENT_ERROR_GPRS_ILLEGAL_MS: * * Since: 1.0 * Deprecated: 1.18.0: Use #MM_MOBILE_EQUIPMENT_ERROR_ILLEGAL_UE instead. */ #define MM_MOBILE_EQUIPMENT_ERROR_GPRS_ILLEGAL_MS (MMMobileEquipmentErrorDeprecated)MM_MOBILE_EQUIPMENT_ERROR_ILLEGAL_UE /** * MM_MOBILE_EQUIPMENT_ERROR_GPRS_IMSI_UNKNOWN_IN_VLR: * * Since: 1.0 * Deprecated: 1.18.0: Use #MM_MOBILE_EQUIPMENT_ERROR_IMSI_UNKNOWN_IN_VLR instead. */ #define MM_MOBILE_EQUIPMENT_ERROR_GPRS_IMSI_UNKNOWN_IN_VLR (MMMobileEquipmentErrorDeprecated)MM_MOBILE_EQUIPMENT_ERROR_IMSI_UNKNOWN_IN_VLR /** * MM_MOBILE_EQUIPMENT_ERROR_GPRS_ILLEGAL_ME: * * Since: 1.0 * Deprecated: 1.18.0: Use #MM_MOBILE_EQUIPMENT_ERROR_ILLEGAL_ME instead. */ #define MM_MOBILE_EQUIPMENT_ERROR_GPRS_ILLEGAL_ME (MMMobileEquipmentErrorDeprecated)MM_MOBILE_EQUIPMENT_ERROR_ILLEGAL_ME /** * MM_MOBILE_EQUIPMENT_ERROR_GPRS_SERVICE_NOT_ALLOWED: * * Since: 1.0 * Deprecated: 1.18.0: Use #MM_MOBILE_EQUIPMENT_ERROR_PS_SERVICES_NOT_ALLOWED instead. */ #define MM_MOBILE_EQUIPMENT_ERROR_GPRS_SERVICE_NOT_ALLOWED (MMMobileEquipmentErrorDeprecated)MM_MOBILE_EQUIPMENT_ERROR_PS_SERVICES_NOT_ALLOWED /** * MM_MOBILE_EQUIPMENT_ERROR_GPRS_AND_NON_GPRS_SERVICES_NOT_ALLOWED: * * Since: 1.8 * Deprecated: 1.18.0: Use #MM_MOBILE_EQUIPMENT_ERROR_PS_AND_NON_PS_SERVICES_NOT_ALLOWED instead. */ #define MM_MOBILE_EQUIPMENT_ERROR_GPRS_AND_NON_GPRS_SERVICES_NOT_ALLOWED (MMMobileEquipmentErrorDeprecated)MM_MOBILE_EQUIPMENT_ERROR_PS_AND_NON_PS_SERVICES_NOT_ALLOWED /** * MM_MOBILE_EQUIPMENT_ERROR_GPRS_PLMN_NOT_ALLOWED: * * Since: 1.0 * Deprecated: 1.18.0: Use #MM_MOBILE_EQUIPMENT_ERROR_PLMN_NOT_ALLOWED instead. */ #define MM_MOBILE_EQUIPMENT_ERROR_GPRS_PLMN_NOT_ALLOWED (MMMobileEquipmentErrorDeprecated)MM_MOBILE_EQUIPMENT_ERROR_PLMN_NOT_ALLOWED /** * MM_MOBILE_EQUIPMENT_ERROR_GPRS_LOCATION_NOT_ALLOWED: * * Since: 1.0 * Deprecated: 1.18.0: Use #MM_MOBILE_EQUIPMENT_ERROR_AREA_NOT_ALLOWED instead. */ #define MM_MOBILE_EQUIPMENT_ERROR_GPRS_LOCATION_NOT_ALLOWED (MMMobileEquipmentErrorDeprecated)MM_MOBILE_EQUIPMENT_ERROR_AREA_NOT_ALLOWED /** * MM_MOBILE_EQUIPMENT_ERROR_GPRS_ROAMING_NOT_ALLOWED: * * Since: 1.0 * Deprecated: 1.18.0: Use #MM_MOBILE_EQUIPMENT_ERROR_ROAMING_NOT_ALLOWED_IN_AREA instead. */ #define MM_MOBILE_EQUIPMENT_ERROR_GPRS_ROAMING_NOT_ALLOWED (MMMobileEquipmentErrorDeprecated)MM_MOBILE_EQUIPMENT_ERROR_ROAMING_NOT_ALLOWED_IN_AREA /** * MM_MOBILE_EQUIPMENT_ERROR_GPRS_NO_CELLS_IN_LOCATION_AREA: * * Since: 1.0 * Deprecated: 1.18.0: Use #MM_MOBILE_EQUIPMENT_ERROR_NO_CELLS_IN_AREA instead. */ #define MM_MOBILE_EQUIPMENT_ERROR_GPRS_NO_CELLS_IN_LOCATION_AREA (MMMobileEquipmentErrorDeprecated)MM_MOBILE_EQUIPMENT_ERROR_NO_CELLS_IN_AREA /** * MM_MOBILE_EQUIPMENT_ERROR_GPRS_NETWORK_FAILURE: * * Since: 1.0 * Deprecated: 1.18.0: Use #MM_MOBILE_EQUIPMENT_ERROR_NETWORK_FAILURE_ATTACH instead. */ #define MM_MOBILE_EQUIPMENT_ERROR_GPRS_NETWORK_FAILURE (MMMobileEquipmentErrorDeprecated)MM_MOBILE_EQUIPMENT_ERROR_NETWORK_FAILURE_ATTACH /** * MM_MOBILE_EQUIPMENT_ERROR_GPRS_CONGESTION: * * Since: 1.0 * Deprecated: 1.18.0: Use #MM_MOBILE_EQUIPMENT_ERROR_CONGESTION instead. */ #define MM_MOBILE_EQUIPMENT_ERROR_GPRS_CONGESTION (MMMobileEquipmentErrorDeprecated)MM_MOBILE_EQUIPMENT_ERROR_CONGESTION /** * MM_MOBILE_EQUIPMENT_ERROR_GPRS_NOT_AUTHORIZED_FOR_CSG: * * Since: 1.8 * Deprecated: 1.18.0: Use #MM_MOBILE_EQUIPMENT_ERROR_NOT_AUTHORIZED_FOR_CSG instead. */ #define MM_MOBILE_EQUIPMENT_ERROR_GPRS_NOT_AUTHORIZED_FOR_CSG (MMMobileEquipmentErrorDeprecated)MM_MOBILE_EQUIPMENT_ERROR_NOT_AUTHORIZED_FOR_CSG /** * MM_MOBILE_EQUIPMENT_ERROR_GPRS_INSUFFICIENT_RESOURCES: * * Since: 1.4 * Deprecated: 1.18.0: Use #MM_MOBILE_EQUIPMENT_ERROR_INSUFFICIENT_RESOURCES instead. */ #define MM_MOBILE_EQUIPMENT_ERROR_GPRS_INSUFFICIENT_RESOURCES (MMMobileEquipmentErrorDeprecated)MM_MOBILE_EQUIPMENT_ERROR_INSUFFICIENT_RESOURCES /** * MM_MOBILE_EQUIPMENT_ERROR_GPRS_MISSING_OR_UNKNOWN_APN: * * Since: 1.4 * Deprecated: 1.18.0: Use #MM_MOBILE_EQUIPMENT_ERROR_MISSING_OR_UNKNOWN_APN instead. */ #define MM_MOBILE_EQUIPMENT_ERROR_GPRS_MISSING_OR_UNKNOWN_APN (MMMobileEquipmentErrorDeprecated)MM_MOBILE_EQUIPMENT_ERROR_MISSING_OR_UNKNOWN_APN /** * MM_MOBILE_EQUIPMENT_ERROR_GPRS_UNKNOWN_PDP_ADDRESS_OR_TYPE: * * Since: 1.8 * Deprecated: 1.18.0: Use #MM_MOBILE_EQUIPMENT_ERROR_UNKNOWN_PDP_ADDRESS_OR_TYPE instead. */ #define MM_MOBILE_EQUIPMENT_ERROR_GPRS_UNKNOWN_PDP_ADDRESS_OR_TYPE (MMMobileEquipmentErrorDeprecated)MM_MOBILE_EQUIPMENT_ERROR_UNKNOWN_PDP_ADDRESS_OR_TYPE /** * MM_MOBILE_EQUIPMENT_ERROR_GPRS_USER_AUTHENTICATION_FAILED: * * Since: 1.4 * Deprecated: 1.18.0: Use #MM_MOBILE_EQUIPMENT_ERROR_USER_AUTHENTICATION_FAILED instead. */ #define MM_MOBILE_EQUIPMENT_ERROR_GPRS_USER_AUTHENTICATION_FAILED (MMMobileEquipmentErrorDeprecated)MM_MOBILE_EQUIPMENT_ERROR_USER_AUTHENTICATION_FAILED /** * MM_MOBILE_EQUIPMENT_ERROR_GPRS_ACTIVATION_REJECTED_BY_GGSN_OR_GW: * * Since: 1.8 * Deprecated: 1.18.0: Use #MM_MOBILE_EQUIPMENT_ERROR_ACTIVATION_REJECTED_BY_GGSN_OR_GW instead. */ #define MM_MOBILE_EQUIPMENT_ERROR_GPRS_ACTIVATION_REJECTED_BY_GGSN_OR_GW (MMMobileEquipmentErrorDeprecated)MM_MOBILE_EQUIPMENT_ERROR_ACTIVATION_REJECTED_BY_GGSN_OR_GW /** * MM_MOBILE_EQUIPMENT_ERROR_GPRS_ACTIVATION_REJECTED_UNSPECIFIED: * * Since: 1.8 * Deprecated: 1.18.0: Use #MM_MOBILE_EQUIPMENT_ERROR_ACTIVATION_REJECTED_UNSPECIFIED instead. */ #define MM_MOBILE_EQUIPMENT_ERROR_GPRS_ACTIVATION_REJECTED_UNSPECIFIED (MMMobileEquipmentErrorDeprecated)MM_MOBILE_EQUIPMENT_ERROR_ACTIVATION_REJECTED_UNSPECIFIED /** * MM_MOBILE_EQUIPMENT_ERROR_GPRS_SERVICE_OPTION_NOT_SUPPORTED: * * Since: 1.0 * Deprecated: 1.18.0: Use #MM_MOBILE_EQUIPMENT_ERROR_SERVICE_OPTION_NOT_SUPPORTED instead. */ #define MM_MOBILE_EQUIPMENT_ERROR_GPRS_SERVICE_OPTION_NOT_SUPPORTED (MMMobileEquipmentErrorDeprecated)MM_MOBILE_EQUIPMENT_ERROR_SERVICE_OPTION_NOT_SUPPORTED /** * MM_MOBILE_EQUIPMENT_ERROR_GPRS_SERVICE_OPTION_NOT_SUBSCRIBED: * * Since: 1.0 * Deprecated: 1.18.0: Use #MM_MOBILE_EQUIPMENT_ERROR_SERVICE_OPTION_NOT_SUBSCRIBED instead. */ #define MM_MOBILE_EQUIPMENT_ERROR_GPRS_SERVICE_OPTION_NOT_SUBSCRIBED (MMMobileEquipmentErrorDeprecated)MM_MOBILE_EQUIPMENT_ERROR_SERVICE_OPTION_NOT_SUBSCRIBED /** * MM_MOBILE_EQUIPMENT_ERROR_GPRS_SERVICE_OPTION_OUT_OF_ORDER: * * Since: 1.0 * Deprecated: 1.18.0: Use #MM_MOBILE_EQUIPMENT_ERROR_SERVICE_OPTION_OUT_OF_ORDER instead. */ #define MM_MOBILE_EQUIPMENT_ERROR_GPRS_SERVICE_OPTION_OUT_OF_ORDER (MMMobileEquipmentErrorDeprecated)MM_MOBILE_EQUIPMENT_ERROR_SERVICE_OPTION_OUT_OF_ORDER /** * MM_MOBILE_EQUIPMENT_ERROR_GPRS_FEATURE_NOT_SUPPORTED: * * Since: 1.8 * Deprecated: 1.18.0: Use #MM_MOBILE_EQUIPMENT_ERROR_FEATURE_NOT_SUPPORTED instead. */ #define MM_MOBILE_EQUIPMENT_ERROR_GPRS_FEATURE_NOT_SUPPORTED (MMMobileEquipmentErrorDeprecated)MM_MOBILE_EQUIPMENT_ERROR_FEATURE_NOT_SUPPORTED /** * MM_MOBILE_EQUIPMENT_ERROR_GPRS_SEMANTIC_ERROR_IN_TFT_OPERATION: * * Since: 1.8 * Deprecated: 1.18.0: Use #MM_MOBILE_EQUIPMENT_ERROR_SEMANTIC_ERROR_IN_TFT_OPERATION instead. */ #define MM_MOBILE_EQUIPMENT_ERROR_GPRS_SEMANTIC_ERROR_IN_TFT_OPERATION (MMMobileEquipmentErrorDeprecated)MM_MOBILE_EQUIPMENT_ERROR_SEMANTIC_ERROR_IN_TFT_OPERATION /** * MM_MOBILE_EQUIPMENT_ERROR_GPRS_SYNTACTICAL_ERROR_IN_TFT_OPERATION: * * Since: 1.8 * Deprecated: 1.18.0: Use #MM_MOBILE_EQUIPMENT_ERROR_SYNTACTICAL_ERROR_IN_TFT_OPERATION instead. */ #define MM_MOBILE_EQUIPMENT_ERROR_GPRS_SYNTACTICAL_ERROR_IN_TFT_OPERATION (MMMobileEquipmentErrorDeprecated)MM_MOBILE_EQUIPMENT_ERROR_SYNTACTICAL_ERROR_IN_TFT_OPERATION /** * MM_MOBILE_EQUIPMENT_ERROR_GPRS_UNKNOWN_PDP_CONTEXT: * * Since: 1.8 * Deprecated: 1.18.0: Use #MM_MOBILE_EQUIPMENT_ERROR_UNKNOWN_PDP_CONTEXT instead. */ #define MM_MOBILE_EQUIPMENT_ERROR_GPRS_UNKNOWN_PDP_CONTEXT (MMMobileEquipmentErrorDeprecated)MM_MOBILE_EQUIPMENT_ERROR_UNKNOWN_PDP_CONTEXT /** * MM_MOBILE_EQUIPMENT_ERROR_GPRS_SEMANTIC_ERRORS_IN_PACKET_FILTER: * * Since: 1.8 * Deprecated: 1.18.0: Use #MM_MOBILE_EQUIPMENT_ERROR_SEMANTIC_ERRORS_IN_PACKET_FILTER instead. */ #define MM_MOBILE_EQUIPMENT_ERROR_GPRS_SEMANTIC_ERRORS_IN_PACKET_FILTER (MMMobileEquipmentErrorDeprecated)MM_MOBILE_EQUIPMENT_ERROR_SEMANTIC_ERRORS_IN_PACKET_FILTER /** * MM_MOBILE_EQUIPMENT_ERROR_GPRS_SYNTACTICAL_ERROR_IN_PACKET_FILTER: * * Since: 1.8 * Deprecated: 1.18.0: Use #MM_MOBILE_EQUIPMENT_ERROR_SYNTACTICAL_ERROR_IN_PACKET_FILTER instead. */ #define MM_MOBILE_EQUIPMENT_ERROR_GPRS_SYNTACTICAL_ERROR_IN_PACKET_FILTER (MMMobileEquipmentErrorDeprecated)MM_MOBILE_EQUIPMENT_ERROR_SYNTACTICAL_ERROR_IN_PACKET_FILTER /** * MM_MOBILE_EQUIPMENT_ERROR_GPRS_PDP_CONTEXT_WITHOUT_TFT_ALREADY_ACTIVATED: * * Since: 1.8 * Deprecated: 1.18.0: Use #MM_MOBILE_EQUIPMENT_ERROR_PDP_CONTEXT_WITHOUT_TFT_ALREADY_ACTIVATED instead. */ #define MM_MOBILE_EQUIPMENT_ERROR_GPRS_PDP_CONTEXT_WITHOUT_TFT_ALREADY_ACTIVATED (MMMobileEquipmentErrorDeprecated)MM_MOBILE_EQUIPMENT_ERROR_PDP_CONTEXT_WITHOUT_TFT_ALREADY_ACTIVATED /** * MM_MOBILE_EQUIPMENT_ERROR_GPRS_PDP_AUTH_FAILURE: * * Since: 1.0 * Deprecated: 1.18.0: Use #MM_MOBILE_EQUIPMENT_ERROR_PDP_AUTH_FAILURE instead. */ #define MM_MOBILE_EQUIPMENT_ERROR_GPRS_PDP_AUTH_FAILURE (MMMobileEquipmentErrorDeprecated)MM_MOBILE_EQUIPMENT_ERROR_PDP_AUTH_FAILURE /** * MM_MOBILE_EQUIPMENT_ERROR_GPRS_INVALID_MOBILE_CLASS: * * Since: 1.0 * Deprecated: 1.18.0: Use #MM_MOBILE_EQUIPMENT_ERROR_INVALID_MOBILE_CLASS instead. */ #define MM_MOBILE_EQUIPMENT_ERROR_GPRS_INVALID_MOBILE_CLASS (MMMobileEquipmentErrorDeprecated)MM_MOBILE_EQUIPMENT_ERROR_INVALID_MOBILE_CLASS /** * MM_MOBILE_EQUIPMENT_ERROR_GPRS_LAST_PDN_DISCONNECTION_NOT_ALLOWED_LEGACY: * * Since: 1.14 * Deprecated: 1.18.0: Use #MM_MOBILE_EQUIPMENT_ERROR_LAST_PDN_DISCONNECTION_NOT_ALLOWED_LEGACY instead. */ #define MM_MOBILE_EQUIPMENT_ERROR_GPRS_LAST_PDN_DISCONNECTION_NOT_ALLOWED_LEGACY (MMMobileEquipmentErrorDeprecated)MM_MOBILE_EQUIPMENT_ERROR_LAST_PDN_DISCONNECTION_NOT_ALLOWED_LEGACY /** * MM_MOBILE_EQUIPMENT_ERROR_GPRS_LAST_PDN_DISCONNECTION_NOT_ALLOWED: * * Since: 1.8 * Deprecated: 1.18.0: Use #MM_MOBILE_EQUIPMENT_ERROR_LAST_PDN_DISCONNECTION_NOT_ALLOWED instead. */ #define MM_MOBILE_EQUIPMENT_ERROR_GPRS_LAST_PDN_DISCONNECTION_NOT_ALLOWED (MMMobileEquipmentErrorDeprecated)MM_MOBILE_EQUIPMENT_ERROR_LAST_PDN_DISCONNECTION_NOT_ALLOWED /** * MM_MOBILE_EQUIPMENT_ERROR_GPRS_SEMANTICALLY_INCORRECT_MESSAGE: * * Since: 1.8 * Deprecated: 1.18.0: Use #MM_MOBILE_EQUIPMENT_ERROR_SEMANTICALLY_INCORRECT_MESSAGE instead. */ #define MM_MOBILE_EQUIPMENT_ERROR_GPRS_SEMANTICALLY_INCORRECT_MESSAGE (MMMobileEquipmentErrorDeprecated)MM_MOBILE_EQUIPMENT_ERROR_SEMANTICALLY_INCORRECT_MESSAGE /** * MM_MOBILE_EQUIPMENT_ERROR_GPRS_MANDATORY_IE_ERROR: * * Since: 1.8 * Deprecated: 1.18.0: Use #MM_MOBILE_EQUIPMENT_ERROR_INVALID_MANDATORY_INFORMATION instead. */ #define MM_MOBILE_EQUIPMENT_ERROR_GPRS_MANDATORY_IE_ERROR (MMMobileEquipmentErrorDeprecated)MM_MOBILE_EQUIPMENT_ERROR_INVALID_MANDATORY_INFORMATION /** * MM_MOBILE_EQUIPMENT_ERROR_GPRS_IE_NOT_IMPLEMENTED: * * Since: 1.8 * Deprecated: 1.18.0: Use #MM_MOBILE_EQUIPMENT_ERROR_MESSAGE_TYPE_NOT_IMPLEMENTED instead. */ #define MM_MOBILE_EQUIPMENT_ERROR_GPRS_IE_NOT_IMPLEMENTED (MMMobileEquipmentErrorDeprecated)MM_MOBILE_EQUIPMENT_ERROR_MESSAGE_TYPE_NOT_IMPLEMENTED /** * MM_MOBILE_EQUIPMENT_ERROR_GPRS_CONDITIONAL_IE_ERROR: * * Since: 1.8 * Deprecated: 1.18.0: Use #MM_MOBILE_EQUIPMENT_ERROR_CONDITIONAL_IE_ERROR instead. */ #define MM_MOBILE_EQUIPMENT_ERROR_GPRS_CONDITIONAL_IE_ERROR (MMMobileEquipmentErrorDeprecated)MM_MOBILE_EQUIPMENT_ERROR_CONDITIONAL_IE_ERROR /** * MM_MOBILE_EQUIPMENT_ERROR_GPRS_UNSPECIFIED_PROTOCOL_ERROR: * * Since: 1.8 * Deprecated: 1.18.0: Use #MM_MOBILE_EQUIPMENT_ERROR_UNSPECIFIED_PROTOCOL_ERROR instead. */ #define MM_MOBILE_EQUIPMENT_ERROR_GPRS_UNSPECIFIED_PROTOCOL_ERROR (MMMobileEquipmentErrorDeprecated)MM_MOBILE_EQUIPMENT_ERROR_UNSPECIFIED_PROTOCOL_ERROR /** * MM_MOBILE_EQUIPMENT_ERROR_GPRS_OPERATOR_DETERMINED_BARRING: * * Since: 1.8 * Deprecated: 1.18.0: Use #MM_MOBILE_EQUIPMENT_ERROR_OPERATOR_DETERMINED_BARRING instead. */ #define MM_MOBILE_EQUIPMENT_ERROR_GPRS_OPERATOR_DETERMINED_BARRING (MMMobileEquipmentErrorDeprecated)MM_MOBILE_EQUIPMENT_ERROR_OPERATOR_DETERMINED_BARRING /** * MM_MOBILE_EQUIPMENT_ERROR_GPRS_MAXIMUM_NUMBER_OF_PDP_CONTEXTS_REACHED: * * Since: 1.8 * Deprecated: 1.18.0: Use #MM_MOBILE_EQUIPMENT_ERROR_MAXIMUM_NUMBER_OF_BEARERS_REACHED instead. */ #define MM_MOBILE_EQUIPMENT_ERROR_GPRS_MAXIMUM_NUMBER_OF_PDP_CONTEXTS_REACHED (MMMobileEquipmentErrorDeprecated)MM_MOBILE_EQUIPMENT_ERROR_MAXIMUM_NUMBER_OF_BEARERS_REACHED /** * MM_MOBILE_EQUIPMENT_ERROR_GPRS_REQUESTED_APN_NOT_SUPPORTED: * * Since: 1.8 * Deprecated: 1.18.0: Use #MM_MOBILE_EQUIPMENT_ERROR_REQUESTED_APN_NOT_SUPPORTED instead. */ #define MM_MOBILE_EQUIPMENT_ERROR_GPRS_REQUESTED_APN_NOT_SUPPORTED (MMMobileEquipmentErrorDeprecated)MM_MOBILE_EQUIPMENT_ERROR_REQUESTED_APN_NOT_SUPPORTED /** * MM_MOBILE_EQUIPMENT_ERROR_GPRS_REQUEST_REJECTED_BCM_VIOLATION: * * Since: 1.8 * Deprecated: 1.18.0: Use #MM_MOBILE_EQUIPMENT_ERROR_REQUEST_REJECTED_BCM_VIOLATION instead. */ #define MM_MOBILE_EQUIPMENT_ERROR_GPRS_REQUEST_REJECTED_BCM_VIOLATION (MMMobileEquipmentErrorDeprecated)MM_MOBILE_EQUIPMENT_ERROR_REQUEST_REJECTED_BCM_VIOLATION #endif /* MM_DISABLE_DEPRECATED */ #endif /* _MODEMMANAGER_COMPAT_H_ */ ModemManager-1.23.4-dev/include/ModemManager-enums.h000066400000000000000000002735461456466623000222230ustar00rootroot00000000000000/* -*- 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) 2011 Red Hat, Inc. * Copyright (C) 2011 Google, Inc. * Copyright (c) 2021 Qualcomm Innovation Center, Inc. */ #ifndef _MODEMMANAGER_ENUMS_H_ #define _MODEMMANAGER_ENUMS_H_ #if !defined (__MODEM_MANAGER_H_INSIDE__) #error "Only can be included directly." #endif /** * SECTION:mm-enums * @short_description: Common enumerations and types in the API. * * This section defines enumerations and types that are used in the * ModemManager interface. **/ /** * MMModemCapability: * @MM_MODEM_CAPABILITY_NONE: Modem has no capabilities. * @MM_MODEM_CAPABILITY_POTS: Modem supports the analog wired telephone network (ie 56k dialup) and does not have wireless/cellular capabilities. * @MM_MODEM_CAPABILITY_CDMA_EVDO: Modem supports at least one of CDMA 1xRTT, EVDO revision 0, EVDO revision A, or EVDO revision B. * @MM_MODEM_CAPABILITY_GSM_UMTS: Modem supports at least one of GSM, GPRS, EDGE, UMTS, HSDPA, HSUPA, or HSPA+ packet switched data capability. * @MM_MODEM_CAPABILITY_LTE: Modem has LTE data capability. * @MM_MODEM_CAPABILITY_IRIDIUM: Modem has Iridium capabilities. * @MM_MODEM_CAPABILITY_5GNR: Modem has 5GNR capabilities. Since 1.14. * @MM_MODEM_CAPABILITY_TDS: Modem has TDS capabilties. Since 1.20. * @MM_MODEM_CAPABILITY_ANY: Mask specifying all capabilities. * * Flags describing one or more of the general access technology families that a * modem supports. * * Since: 1.0 */ typedef enum { /*< underscore_name=mm_modem_capability >*/ MM_MODEM_CAPABILITY_NONE = 0, MM_MODEM_CAPABILITY_POTS = 1 << 0, MM_MODEM_CAPABILITY_CDMA_EVDO = 1 << 1, MM_MODEM_CAPABILITY_GSM_UMTS = 1 << 2, MM_MODEM_CAPABILITY_LTE = 1 << 3, /* MM_MODEM_CAPABILITY_LTE_ADVANCED deprecated */ MM_MODEM_CAPABILITY_IRIDIUM = 1 << 5, MM_MODEM_CAPABILITY_5GNR = 1 << 6, MM_MODEM_CAPABILITY_TDS = 1 << 7, MM_MODEM_CAPABILITY_ANY = 0xFFFFFFFF } MMModemCapability; /** * MMModemLock: * @MM_MODEM_LOCK_UNKNOWN: Lock reason unknown. * @MM_MODEM_LOCK_NONE: Modem is unlocked. * @MM_MODEM_LOCK_SIM_PIN: SIM requires the PIN code. * @MM_MODEM_LOCK_SIM_PIN2: SIM requires the PIN2 code. * @MM_MODEM_LOCK_SIM_PUK: SIM requires the PUK code. * @MM_MODEM_LOCK_SIM_PUK2: SIM requires the PUK2 code. * @MM_MODEM_LOCK_PH_SP_PIN: Modem requires the service provider PIN code. * @MM_MODEM_LOCK_PH_SP_PUK: Modem requires the service provider PUK code. * @MM_MODEM_LOCK_PH_NET_PIN: Modem requires the network PIN code. * @MM_MODEM_LOCK_PH_NET_PUK: Modem requires the network PUK code. * @MM_MODEM_LOCK_PH_SIM_PIN: Modem requires the PIN code. * @MM_MODEM_LOCK_PH_CORP_PIN: Modem requires the corporate PIN code. * @MM_MODEM_LOCK_PH_CORP_PUK: Modem requires the corporate PUK code. * @MM_MODEM_LOCK_PH_FSIM_PIN: Modem requires the PH-FSIM PIN code. * @MM_MODEM_LOCK_PH_FSIM_PUK: Modem requires the PH-FSIM PUK code. * @MM_MODEM_LOCK_PH_NETSUB_PIN: Modem requires the network subset PIN code. * @MM_MODEM_LOCK_PH_NETSUB_PUK: Modem requires the network subset PUK code. * * Enumeration of possible lock reasons. * * Since: 1.0 */ typedef enum { /*< underscore_name=mm_modem_lock >*/ MM_MODEM_LOCK_UNKNOWN = 0, MM_MODEM_LOCK_NONE = 1, MM_MODEM_LOCK_SIM_PIN = 2, MM_MODEM_LOCK_SIM_PIN2 = 3, MM_MODEM_LOCK_SIM_PUK = 4, MM_MODEM_LOCK_SIM_PUK2 = 5, MM_MODEM_LOCK_PH_SP_PIN = 6, MM_MODEM_LOCK_PH_SP_PUK = 7, MM_MODEM_LOCK_PH_NET_PIN = 8, MM_MODEM_LOCK_PH_NET_PUK = 9, MM_MODEM_LOCK_PH_SIM_PIN = 10, MM_MODEM_LOCK_PH_CORP_PIN = 11, MM_MODEM_LOCK_PH_CORP_PUK = 12, MM_MODEM_LOCK_PH_FSIM_PIN = 13, MM_MODEM_LOCK_PH_FSIM_PUK = 14, MM_MODEM_LOCK_PH_NETSUB_PIN = 15, MM_MODEM_LOCK_PH_NETSUB_PUK = 16 } MMModemLock; /** * MMModemState: * @MM_MODEM_STATE_FAILED: The modem is unusable. * @MM_MODEM_STATE_UNKNOWN: State unknown or not reportable. * @MM_MODEM_STATE_INITIALIZING: The modem is currently being initialized. * @MM_MODEM_STATE_LOCKED: The modem needs to be unlocked. * @MM_MODEM_STATE_DISABLED: The modem is not enabled and is powered down. * @MM_MODEM_STATE_DISABLING: The modem is currently transitioning to the @MM_MODEM_STATE_DISABLED state. * @MM_MODEM_STATE_ENABLING: The modem is currently transitioning to the @MM_MODEM_STATE_ENABLED state. * @MM_MODEM_STATE_ENABLED: The modem is enabled and powered on but not registered with a network provider and not available for data connections. * @MM_MODEM_STATE_SEARCHING: The modem is searching for a network provider to register with. * @MM_MODEM_STATE_REGISTERED: The modem is registered with a network provider, and data connections and messaging may be available for use. * @MM_MODEM_STATE_DISCONNECTING: The modem is disconnecting and deactivating the last active packet data bearer. This state will not be entered if more than one packet data bearer is active and one of the active bearers is deactivated. * @MM_MODEM_STATE_CONNECTING: The modem is activating and connecting the first packet data bearer. Subsequent bearer activations when another bearer is already active do not cause this state to be entered. * @MM_MODEM_STATE_CONNECTED: One or more packet data bearers is active and connected. * * Enumeration of possible modem states. * * Since: 1.0 */ typedef enum { /*< underscore_name=mm_modem_state >*/ MM_MODEM_STATE_FAILED = -1, MM_MODEM_STATE_UNKNOWN = 0, MM_MODEM_STATE_INITIALIZING = 1, MM_MODEM_STATE_LOCKED = 2, MM_MODEM_STATE_DISABLED = 3, MM_MODEM_STATE_DISABLING = 4, MM_MODEM_STATE_ENABLING = 5, MM_MODEM_STATE_ENABLED = 6, MM_MODEM_STATE_SEARCHING = 7, MM_MODEM_STATE_REGISTERED = 8, MM_MODEM_STATE_DISCONNECTING = 9, MM_MODEM_STATE_CONNECTING = 10, MM_MODEM_STATE_CONNECTED = 11 } MMModemState; /** * MMModemStateFailedReason: * @MM_MODEM_STATE_FAILED_REASON_NONE: No error. * @MM_MODEM_STATE_FAILED_REASON_UNKNOWN: Unknown error. * @MM_MODEM_STATE_FAILED_REASON_SIM_MISSING: SIM is required but missing. * @MM_MODEM_STATE_FAILED_REASON_SIM_ERROR: SIM is available, but unusable (e.g. permanently locked). * @MM_MODEM_STATE_FAILED_REASON_UNKNOWN_CAPABILITIES: Unknown modem capabilities. Since 1.20. * @MM_MODEM_STATE_FAILED_REASON_ESIM_WITHOUT_PROFILES: eSIM is not initialized. Since 1.20. * * Enumeration of possible errors when the modem is in @MM_MODEM_STATE_FAILED. * * Since: 1.0 */ typedef enum { /*< underscore_name=mm_modem_state_failed_reason >*/ MM_MODEM_STATE_FAILED_REASON_NONE = 0, MM_MODEM_STATE_FAILED_REASON_UNKNOWN = 1, MM_MODEM_STATE_FAILED_REASON_SIM_MISSING = 2, MM_MODEM_STATE_FAILED_REASON_SIM_ERROR = 3, MM_MODEM_STATE_FAILED_REASON_UNKNOWN_CAPABILITIES = 4, MM_MODEM_STATE_FAILED_REASON_ESIM_WITHOUT_PROFILES = 5, } MMModemStateFailedReason; /** * MMModemPowerState: * @MM_MODEM_POWER_STATE_UNKNOWN: Unknown power state. * @MM_MODEM_POWER_STATE_OFF: Off. * @MM_MODEM_POWER_STATE_LOW: Low-power mode. * @MM_MODEM_POWER_STATE_ON: Full power mode. * * Power state of the modem. * * Since: 1.0 */ typedef enum { /*< underscore_name=mm_modem_power_state >*/ MM_MODEM_POWER_STATE_UNKNOWN = 0, MM_MODEM_POWER_STATE_OFF = 1, MM_MODEM_POWER_STATE_LOW = 2, MM_MODEM_POWER_STATE_ON = 3 } MMModemPowerState; /** * MMModemStateChangeReason: * @MM_MODEM_STATE_CHANGE_REASON_UNKNOWN: Reason unknown or not reportable. * @MM_MODEM_STATE_CHANGE_REASON_USER_REQUESTED: State change was requested by an interface user. * @MM_MODEM_STATE_CHANGE_REASON_SUSPEND: State change was caused by a system suspend. * @MM_MODEM_STATE_CHANGE_REASON_FAILURE: State change was caused by an unrecoverable error. * * Enumeration of possible reasons to have changed the modem state. * * Since: 1.0 */ typedef enum { /*< underscore_name=mm_modem_state_change_reason >*/ MM_MODEM_STATE_CHANGE_REASON_UNKNOWN = 0, MM_MODEM_STATE_CHANGE_REASON_USER_REQUESTED = 1, MM_MODEM_STATE_CHANGE_REASON_SUSPEND = 2, MM_MODEM_STATE_CHANGE_REASON_FAILURE = 3, } MMModemStateChangeReason; /** * MMModemAccessTechnology: * @MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN: The access technology used is unknown. * @MM_MODEM_ACCESS_TECHNOLOGY_POTS: Analog wireline telephone. * @MM_MODEM_ACCESS_TECHNOLOGY_GSM: GSM. * @MM_MODEM_ACCESS_TECHNOLOGY_GSM_COMPACT: Compact GSM. * @MM_MODEM_ACCESS_TECHNOLOGY_GPRS: GPRS. * @MM_MODEM_ACCESS_TECHNOLOGY_EDGE: EDGE (ETSI 27.007: "GSM w/EGPRS"). * @MM_MODEM_ACCESS_TECHNOLOGY_UMTS: UMTS (ETSI 27.007: "UTRAN"). * @MM_MODEM_ACCESS_TECHNOLOGY_HSDPA: HSDPA (ETSI 27.007: "UTRAN w/HSDPA"). * @MM_MODEM_ACCESS_TECHNOLOGY_HSUPA: HSUPA (ETSI 27.007: "UTRAN w/HSUPA"). * @MM_MODEM_ACCESS_TECHNOLOGY_HSPA: HSPA (ETSI 27.007: "UTRAN w/HSDPA and HSUPA"). * @MM_MODEM_ACCESS_TECHNOLOGY_HSPA_PLUS: HSPA+ (ETSI 27.007: "UTRAN w/HSPA+"). * @MM_MODEM_ACCESS_TECHNOLOGY_1XRTT: CDMA2000 1xRTT. * @MM_MODEM_ACCESS_TECHNOLOGY_EVDO0: CDMA2000 EVDO revision 0. * @MM_MODEM_ACCESS_TECHNOLOGY_EVDOA: CDMA2000 EVDO revision A. * @MM_MODEM_ACCESS_TECHNOLOGY_EVDOB: CDMA2000 EVDO revision B. * @MM_MODEM_ACCESS_TECHNOLOGY_LTE: LTE (ETSI 27.007: "E-UTRAN") * @MM_MODEM_ACCESS_TECHNOLOGY_5GNR: 5GNR (ETSI 27.007: "NG-RAN"). Since 1.14. * @MM_MODEM_ACCESS_TECHNOLOGY_LTE_CAT_M: Cat-M (ETSI 23.401: LTE Category M1/M2). Since 1.20. * @MM_MODEM_ACCESS_TECHNOLOGY_LTE_NB_IOT: NB IoT (ETSI 23.401: LTE Category NB1/NB2). Since 1.20. * @MM_MODEM_ACCESS_TECHNOLOGY_ANY: Mask specifying all access technologies. * * Describes various access technologies that a device uses when registered with * or connected to a network. * * Since: 1.0 */ typedef enum { /*< underscore_name=mm_modem_access_technology >*/ MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN = 0, MM_MODEM_ACCESS_TECHNOLOGY_POTS = 1 << 0, MM_MODEM_ACCESS_TECHNOLOGY_GSM = 1 << 1, MM_MODEM_ACCESS_TECHNOLOGY_GSM_COMPACT = 1 << 2, MM_MODEM_ACCESS_TECHNOLOGY_GPRS = 1 << 3, MM_MODEM_ACCESS_TECHNOLOGY_EDGE = 1 << 4, MM_MODEM_ACCESS_TECHNOLOGY_UMTS = 1 << 5, MM_MODEM_ACCESS_TECHNOLOGY_HSDPA = 1 << 6, MM_MODEM_ACCESS_TECHNOLOGY_HSUPA = 1 << 7, MM_MODEM_ACCESS_TECHNOLOGY_HSPA = 1 << 8, MM_MODEM_ACCESS_TECHNOLOGY_HSPA_PLUS = 1 << 9, MM_MODEM_ACCESS_TECHNOLOGY_1XRTT = 1 << 10, MM_MODEM_ACCESS_TECHNOLOGY_EVDO0 = 1 << 11, MM_MODEM_ACCESS_TECHNOLOGY_EVDOA = 1 << 12, MM_MODEM_ACCESS_TECHNOLOGY_EVDOB = 1 << 13, MM_MODEM_ACCESS_TECHNOLOGY_LTE = 1 << 14, MM_MODEM_ACCESS_TECHNOLOGY_5GNR = 1 << 15, MM_MODEM_ACCESS_TECHNOLOGY_LTE_CAT_M = 1 << 16, MM_MODEM_ACCESS_TECHNOLOGY_LTE_NB_IOT = 1 << 17, MM_MODEM_ACCESS_TECHNOLOGY_ANY = 0xFFFFFFFF, } MMModemAccessTechnology; /** * MMModemMode: * @MM_MODEM_MODE_NONE: None. * @MM_MODEM_MODE_CS: CSD, GSM, and other circuit-switched technologies. * @MM_MODEM_MODE_2G: GPRS, EDGE. * @MM_MODEM_MODE_3G: UMTS, HSxPA. * @MM_MODEM_MODE_4G: LTE. * @MM_MODEM_MODE_5G: 5GNR. Since 1.14. * @MM_MODEM_MODE_ANY: Any mode can be used (only this value allowed for POTS modems). * * Bitfield to indicate which access modes are supported, allowed or * preferred in a given device. * * Since: 1.0 */ typedef enum { /*< underscore_name=mm_modem_mode >*/ MM_MODEM_MODE_NONE = 0, MM_MODEM_MODE_CS = 1 << 0, MM_MODEM_MODE_2G = 1 << 1, MM_MODEM_MODE_3G = 1 << 2, MM_MODEM_MODE_4G = 1 << 3, MM_MODEM_MODE_5G = 1 << 4, MM_MODEM_MODE_ANY = 0xFFFFFFFF } MMModemMode; /** * MMModemBand: * @MM_MODEM_BAND_UNKNOWN: Unknown or invalid band. * @MM_MODEM_BAND_EGSM: GSM/GPRS/EDGE 900 MHz. * @MM_MODEM_BAND_DCS: GSM/GPRS/EDGE 1800 MHz. * @MM_MODEM_BAND_PCS: GSM/GPRS/EDGE 1900 MHz. * @MM_MODEM_BAND_G850: GSM/GPRS/EDGE 850 MHz. * @MM_MODEM_BAND_G450: GSM/GPRS/EDGE 450 MHz. * @MM_MODEM_BAND_G480: GSM/GPRS/EDGE 480 MHz. * @MM_MODEM_BAND_G750: GSM/GPRS/EDGE 750 MHz. * @MM_MODEM_BAND_G380: GSM/GPRS/EDGE 380 MHz. * @MM_MODEM_BAND_G410: GSM/GPRS/EDGE 410 MHz. * @MM_MODEM_BAND_G710: GSM/GPRS/EDGE 710 MHz. * @MM_MODEM_BAND_G810: GSM/GPRS/EDGE 810 MHz. * @MM_MODEM_BAND_UTRAN_1: UMTS 2100 MHz (IMT, UTRAN band 1). Since 1.8. * @MM_MODEM_BAND_UTRAN_2: UMTS 1900 MHz (PCS A-F, UTRAN band 2). Since 1.8. * @MM_MODEM_BAND_UTRAN_3: UMTS 1800 MHz (DCS, UTRAN band 3). Since 1.8. * @MM_MODEM_BAND_UTRAN_4: UMTS 1700 MHz (AWS A-F, UTRAN band 4). Since 1.8. * @MM_MODEM_BAND_UTRAN_5: UMTS 850 MHz (CLR, UTRAN band 5). Since 1.8. * @MM_MODEM_BAND_UTRAN_6: UMTS 800 MHz (UTRAN band 6). Since 1.8. * @MM_MODEM_BAND_UTRAN_7: UMTS 2600 MHz (IMT-E, UTRAN band 7). Since 1.8. * @MM_MODEM_BAND_UTRAN_8: UMTS 900 MHz (E-GSM, UTRAN band 8). Since 1.8. * @MM_MODEM_BAND_UTRAN_9: UMTS 1700 MHz (UTRAN band 9). Since 1.8. * @MM_MODEM_BAND_UTRAN_10: UMTS 1700 MHz (EAWS A-G, UTRAN band 10). Since 1.8. * @MM_MODEM_BAND_UTRAN_11: UMTS 1500 MHz (LPDC, UTRAN band 11). Since 1.8. * @MM_MODEM_BAND_UTRAN_12: UMTS 700 MHz (LSMH A/B/C, UTRAN band 12). Since 1.8. * @MM_MODEM_BAND_UTRAN_13: UMTS 700 MHz (USMH C, UTRAN band 13). Since 1.8. * @MM_MODEM_BAND_UTRAN_14: UMTS 700 MHz (USMH D, UTRAN band 14). Since 1.8. * @MM_MODEM_BAND_UTRAN_19: UMTS 800 MHz (UTRAN band 19). Since 1.8. * @MM_MODEM_BAND_UTRAN_20: UMTS 800 MHz (EUDD, UTRAN band 20). Since 1.8. * @MM_MODEM_BAND_UTRAN_21: UMTS 1500 MHz (UPDC, UTRAN band 21). Since 1.8. * @MM_MODEM_BAND_UTRAN_22: UMTS 3500 MHz (UTRAN band 22). Since 1.8. * @MM_MODEM_BAND_UTRAN_25: UMTS 1900 MHz (EPCS A-G, UTRAN band 25). Since 1.8. * @MM_MODEM_BAND_UTRAN_26: UMTS 850 MHz (ECLR, UTRAN band 26). Since 1.8. * @MM_MODEM_BAND_UTRAN_32: UMTS 1500 MHz (L-band, UTRAN band 32). Since 1.8. * @MM_MODEM_BAND_EUTRAN_1: E-UTRAN band 1. Since 1.8. * @MM_MODEM_BAND_EUTRAN_2: E-UTRAN band 2. Since 1.8. * @MM_MODEM_BAND_EUTRAN_3: E-UTRAN band 3. Since 1.8. * @MM_MODEM_BAND_EUTRAN_4: E-UTRAN band 4. Since 1.8. * @MM_MODEM_BAND_EUTRAN_5: E-UTRAN band 5. Since 1.8. * @MM_MODEM_BAND_EUTRAN_6: E-UTRAN band 6. Since 1.8. * @MM_MODEM_BAND_EUTRAN_7: E-UTRAN band 7. Since 1.8. * @MM_MODEM_BAND_EUTRAN_8: E-UTRAN band 8. Since 1.8. * @MM_MODEM_BAND_EUTRAN_9: E-UTRAN band 9. Since 1.8. * @MM_MODEM_BAND_EUTRAN_10: E-UTRAN band 10. Since 1.8. * @MM_MODEM_BAND_EUTRAN_11: E-UTRAN band 11. Since 1.8. * @MM_MODEM_BAND_EUTRAN_12: E-UTRAN band 12. Since 1.8. * @MM_MODEM_BAND_EUTRAN_13: E-UTRAN band 13. Since 1.8. * @MM_MODEM_BAND_EUTRAN_14: E-UTRAN band 14. Since 1.8. * @MM_MODEM_BAND_EUTRAN_17: E-UTRAN band 17. Since 1.8. * @MM_MODEM_BAND_EUTRAN_18: E-UTRAN band 18. Since 1.8. * @MM_MODEM_BAND_EUTRAN_19: E-UTRAN band 19. Since 1.8. * @MM_MODEM_BAND_EUTRAN_20: E-UTRAN band 20. Since 1.8. * @MM_MODEM_BAND_EUTRAN_21: E-UTRAN band 21. Since 1.8. * @MM_MODEM_BAND_EUTRAN_22: E-UTRAN band 22. Since 1.8. * @MM_MODEM_BAND_EUTRAN_23: E-UTRAN band 23. Since 1.8. * @MM_MODEM_BAND_EUTRAN_24: E-UTRAN band 24. Since 1.8. * @MM_MODEM_BAND_EUTRAN_25: E-UTRAN band 25. Since 1.8. * @MM_MODEM_BAND_EUTRAN_26: E-UTRAN band 26. Since 1.8. * @MM_MODEM_BAND_EUTRAN_27: E-UTRAN band 27. Since 1.8. * @MM_MODEM_BAND_EUTRAN_28: E-UTRAN band 28. Since 1.8. * @MM_MODEM_BAND_EUTRAN_29: E-UTRAN band 29. Since 1.8. * @MM_MODEM_BAND_EUTRAN_30: E-UTRAN band 30. Since 1.8. * @MM_MODEM_BAND_EUTRAN_31: E-UTRAN band 31. Since 1.8. * @MM_MODEM_BAND_EUTRAN_32: E-UTRAN band 32. Since 1.8. * @MM_MODEM_BAND_EUTRAN_33: E-UTRAN band 33. Since 1.8. * @MM_MODEM_BAND_EUTRAN_34: E-UTRAN band 34. Since 1.8. * @MM_MODEM_BAND_EUTRAN_35: E-UTRAN band 35. Since 1.8. * @MM_MODEM_BAND_EUTRAN_36: E-UTRAN band 36. Since 1.8. * @MM_MODEM_BAND_EUTRAN_37: E-UTRAN band 37. Since 1.8. * @MM_MODEM_BAND_EUTRAN_38: E-UTRAN band 38. Since 1.8. * @MM_MODEM_BAND_EUTRAN_39: E-UTRAN band 39. Since 1.8. * @MM_MODEM_BAND_EUTRAN_40: E-UTRAN band 40. Since 1.8. * @MM_MODEM_BAND_EUTRAN_41: E-UTRAN band 41. Since 1.8. * @MM_MODEM_BAND_EUTRAN_42: E-UTRAN band 42. Since 1.8. * @MM_MODEM_BAND_EUTRAN_43: E-UTRAN band 43. Since 1.8. * @MM_MODEM_BAND_EUTRAN_44: E-UTRAN band 44. Since 1.8. * @MM_MODEM_BAND_EUTRAN_45: E-UTRAN band 45. Since 1.8. * @MM_MODEM_BAND_EUTRAN_46: E-UTRAN band 46. Since 1.8. * @MM_MODEM_BAND_EUTRAN_47: E-UTRAN band 47. Since 1.8. * @MM_MODEM_BAND_EUTRAN_48: E-UTRAN band 48. Since 1.8. * @MM_MODEM_BAND_EUTRAN_49: E-UTRAN band 49. Since 1.10. * @MM_MODEM_BAND_EUTRAN_50: E-UTRAN band 50. Since 1.10. * @MM_MODEM_BAND_EUTRAN_51: E-UTRAN band 51. Since 1.10. * @MM_MODEM_BAND_EUTRAN_52: E-UTRAN band 52. Since 1.10. * @MM_MODEM_BAND_EUTRAN_53: E-UTRAN band 53. Since 1.10. * @MM_MODEM_BAND_EUTRAN_54: E-UTRAN band 54. Since 1.10. * @MM_MODEM_BAND_EUTRAN_55: E-UTRAN band 55. Since 1.10. * @MM_MODEM_BAND_EUTRAN_56: E-UTRAN band 56. Since 1.10. * @MM_MODEM_BAND_EUTRAN_57: E-UTRAN band 57. Since 1.10. * @MM_MODEM_BAND_EUTRAN_58: E-UTRAN band 58. Since 1.10. * @MM_MODEM_BAND_EUTRAN_59: E-UTRAN band 59. Since 1.10. * @MM_MODEM_BAND_EUTRAN_60: E-UTRAN band 60. Since 1.10. * @MM_MODEM_BAND_EUTRAN_61: E-UTRAN band 61. Since 1.10. * @MM_MODEM_BAND_EUTRAN_62: E-UTRAN band 62. Since 1.10. * @MM_MODEM_BAND_EUTRAN_63: E-UTRAN band 63. Since 1.10. * @MM_MODEM_BAND_EUTRAN_64: E-UTRAN band 64. Since 1.10. * @MM_MODEM_BAND_EUTRAN_65: E-UTRAN band 65. Since 1.8. * @MM_MODEM_BAND_EUTRAN_66: E-UTRAN band 66. Since 1.8. * @MM_MODEM_BAND_EUTRAN_67: E-UTRAN band 67. Since 1.8. * @MM_MODEM_BAND_EUTRAN_68: E-UTRAN band 68. Since 1.8. * @MM_MODEM_BAND_EUTRAN_69: E-UTRAN band 69. Since 1.8. * @MM_MODEM_BAND_EUTRAN_70: E-UTRAN band 70. Since 1.8. * @MM_MODEM_BAND_EUTRAN_71: E-UTRAN band 71. Since 1.8. * @MM_MODEM_BAND_EUTRAN_85: E-UTRAN band 85. Since 1.20. * @MM_MODEM_BAND_CDMA_BC0: CDMA Band Class 0 (US Cellular 850MHz). Since 1.8. * @MM_MODEM_BAND_CDMA_BC1: CDMA Band Class 1 (US PCS 1900MHz). Since 1.8. * @MM_MODEM_BAND_CDMA_BC2: CDMA Band Class 2 (UK TACS 900MHz). Since 1.8. * @MM_MODEM_BAND_CDMA_BC3: CDMA Band Class 3 (Japanese TACS). Since 1.8. * @MM_MODEM_BAND_CDMA_BC4: CDMA Band Class 4 (Korean PCS). Since 1.8. * @MM_MODEM_BAND_CDMA_BC5: CDMA Band Class 5 (NMT 450MHz). Since 1.8. * @MM_MODEM_BAND_CDMA_BC6: CDMA Band Class 6 (IMT2000 2100MHz). Since 1.8. * @MM_MODEM_BAND_CDMA_BC7: CDMA Band Class 7 (Cellular 700MHz). Since 1.8. * @MM_MODEM_BAND_CDMA_BC8: CDMA Band Class 8 (1800MHz). Since 1.8. * @MM_MODEM_BAND_CDMA_BC9: CDMA Band Class 9 (900MHz). Since 1.8. * @MM_MODEM_BAND_CDMA_BC10: CDMA Band Class 10 (US Secondary 800). Since 1.8. * @MM_MODEM_BAND_CDMA_BC11: CDMA Band Class 11 (European PAMR 400MHz). Since 1.8. * @MM_MODEM_BAND_CDMA_BC12: CDMA Band Class 12 (PAMR 800MHz). Since 1.8. * @MM_MODEM_BAND_CDMA_BC13: CDMA Band Class 13 (IMT2000 2500MHz Expansion). Since 1.8. * @MM_MODEM_BAND_CDMA_BC14: CDMA Band Class 14 (More US PCS 1900MHz). Since 1.8. * @MM_MODEM_BAND_CDMA_BC15: CDMA Band Class 15 (AWS 1700MHz). Since 1.8. * @MM_MODEM_BAND_CDMA_BC16: CDMA Band Class 16 (US 2500MHz). Since 1.8. * @MM_MODEM_BAND_CDMA_BC17: CDMA Band Class 17 (US 2500MHz Forward Link Only). Since 1.8. * @MM_MODEM_BAND_CDMA_BC18: CDMA Band Class 18 (US 700MHz Public Safety). Since 1.8. * @MM_MODEM_BAND_CDMA_BC19: CDMA Band Class 19 (US Lower 700MHz). Since 1.8. * @MM_MODEM_BAND_ANY: For certain operations, allow the modem to select a band automatically. * @MM_MODEM_BAND_NGRAN_1: NGRAN band 1. Since 1.20. * @MM_MODEM_BAND_NGRAN_2: NGRAN band 2. Since 1.20. * @MM_MODEM_BAND_NGRAN_3: NGRAN band 3. Since 1.20. * @MM_MODEM_BAND_NGRAN_5: NGRAN band 5. Since 1.20. * @MM_MODEM_BAND_NGRAN_7: NGRAN band 7. Since 1.20. * @MM_MODEM_BAND_NGRAN_8: NGRAN band 8. Since 1.20. * @MM_MODEM_BAND_NGRAN_12: NGRAN band 12. Since 1.20. * @MM_MODEM_BAND_NGRAN_13: NGRAN band 13. Since 1.20. * @MM_MODEM_BAND_NGRAN_14: NGRAN band 14. Since 1.20. * @MM_MODEM_BAND_NGRAN_18: NGRAN band 18. Since 1.20. * @MM_MODEM_BAND_NGRAN_20: NGRAN band 20. Since 1.20. * @MM_MODEM_BAND_NGRAN_25: NGRAN band 25. Since 1.20. * @MM_MODEM_BAND_NGRAN_26: NGRAN band 26. Since 1.20. * @MM_MODEM_BAND_NGRAN_28: NGRAN band 28. Since 1.20. * @MM_MODEM_BAND_NGRAN_29: NGRAN band 29. Since 1.20. * @MM_MODEM_BAND_NGRAN_30: NGRAN band 30. Since 1.20. * @MM_MODEM_BAND_NGRAN_34: NGRAN band 34. Since 1.20. * @MM_MODEM_BAND_NGRAN_38: NGRAN band 38. Since 1.20. * @MM_MODEM_BAND_NGRAN_39: NGRAN band 39. Since 1.20. * @MM_MODEM_BAND_NGRAN_40: NGRAN band 40. Since 1.20. * @MM_MODEM_BAND_NGRAN_41: NGRAN band 41. Since 1.20. * @MM_MODEM_BAND_NGRAN_48: NGRAN band 48. Since 1.20. * @MM_MODEM_BAND_NGRAN_50: NGRAN band 50. Since 1.20. * @MM_MODEM_BAND_NGRAN_51: NGRAN band 51. Since 1.20. * @MM_MODEM_BAND_NGRAN_53: NGRAN band 53. Since 1.20. * @MM_MODEM_BAND_NGRAN_65: NGRAN band 65. Since 1.20. * @MM_MODEM_BAND_NGRAN_66: NGRAN band 66. Since 1.20. * @MM_MODEM_BAND_NGRAN_70: NGRAN band 70. Since 1.20. * @MM_MODEM_BAND_NGRAN_71: NGRAN band 71. Since 1.20. * @MM_MODEM_BAND_NGRAN_74: NGRAN band 74. Since 1.20. * @MM_MODEM_BAND_NGRAN_75: NGRAN band 75. Since 1.20. * @MM_MODEM_BAND_NGRAN_76: NGRAN band 76. Since 1.20. * @MM_MODEM_BAND_NGRAN_77: NGRAN band 77. Since 1.20. * @MM_MODEM_BAND_NGRAN_78: NGRAN band 78. Since 1.20. * @MM_MODEM_BAND_NGRAN_79: NGRAN band 79. Since 1.20. * @MM_MODEM_BAND_NGRAN_80: NGRAN band 80. Since 1.20. * @MM_MODEM_BAND_NGRAN_81: NGRAN band 81. Since 1.20. * @MM_MODEM_BAND_NGRAN_82: NGRAN band 82. Since 1.20. * @MM_MODEM_BAND_NGRAN_83: NGRAN band 83. Since 1.20. * @MM_MODEM_BAND_NGRAN_84: NGRAN band 84. Since 1.20. * @MM_MODEM_BAND_NGRAN_86: NGRAN band 86. Since 1.20. * @MM_MODEM_BAND_NGRAN_89: NGRAN band 89. Since 1.20. * @MM_MODEM_BAND_NGRAN_90: NGRAN band 90. Since 1.20. * @MM_MODEM_BAND_NGRAN_91: NGRAN band 91. Since 1.20. * @MM_MODEM_BAND_NGRAN_92: NGRAN band 92. Since 1.20. * @MM_MODEM_BAND_NGRAN_93: NGRAN band 93. Since 1.20. * @MM_MODEM_BAND_NGRAN_94: NGRAN band 94. Since 1.20. * @MM_MODEM_BAND_NGRAN_95: NGRAN band 95. Since 1.20. * @MM_MODEM_BAND_NGRAN_257: NGRAN band 257. Since 1.20. * @MM_MODEM_BAND_NGRAN_258: NGRAN band 258. Since 1.20. * @MM_MODEM_BAND_NGRAN_260: NGRAN band 260. Since 1.20. * @MM_MODEM_BAND_NGRAN_261: NGRAN band 261. Since 1.20. * * Radio bands supported by the device when connecting to a mobile network. * * Since: 1.0 */ typedef enum { /*< underscore_name=mm_modem_band >*/ MM_MODEM_BAND_UNKNOWN = 0, /* GSM/UMTS bands */ MM_MODEM_BAND_EGSM = 1, MM_MODEM_BAND_DCS = 2, MM_MODEM_BAND_PCS = 3, MM_MODEM_BAND_G850 = 4, MM_MODEM_BAND_UTRAN_1 = 5, MM_MODEM_BAND_UTRAN_3 = 6, MM_MODEM_BAND_UTRAN_4 = 7, MM_MODEM_BAND_UTRAN_6 = 8, MM_MODEM_BAND_UTRAN_5 = 9, MM_MODEM_BAND_UTRAN_8 = 10, MM_MODEM_BAND_UTRAN_9 = 11, MM_MODEM_BAND_UTRAN_2 = 12, MM_MODEM_BAND_UTRAN_7 = 13, MM_MODEM_BAND_G450 = 14, MM_MODEM_BAND_G480 = 15, MM_MODEM_BAND_G750 = 16, MM_MODEM_BAND_G380 = 17, MM_MODEM_BAND_G410 = 18, MM_MODEM_BAND_G710 = 19, MM_MODEM_BAND_G810 = 20, /* LTE bands */ MM_MODEM_BAND_EUTRAN_1 = 31, MM_MODEM_BAND_EUTRAN_2 = 32, MM_MODEM_BAND_EUTRAN_3 = 33, MM_MODEM_BAND_EUTRAN_4 = 34, MM_MODEM_BAND_EUTRAN_5 = 35, MM_MODEM_BAND_EUTRAN_6 = 36, MM_MODEM_BAND_EUTRAN_7 = 37, MM_MODEM_BAND_EUTRAN_8 = 38, MM_MODEM_BAND_EUTRAN_9 = 39, MM_MODEM_BAND_EUTRAN_10 = 40, MM_MODEM_BAND_EUTRAN_11 = 41, MM_MODEM_BAND_EUTRAN_12 = 42, MM_MODEM_BAND_EUTRAN_13 = 43, MM_MODEM_BAND_EUTRAN_14 = 44, MM_MODEM_BAND_EUTRAN_17 = 47, MM_MODEM_BAND_EUTRAN_18 = 48, MM_MODEM_BAND_EUTRAN_19 = 49, MM_MODEM_BAND_EUTRAN_20 = 50, MM_MODEM_BAND_EUTRAN_21 = 51, MM_MODEM_BAND_EUTRAN_22 = 52, MM_MODEM_BAND_EUTRAN_23 = 53, MM_MODEM_BAND_EUTRAN_24 = 54, MM_MODEM_BAND_EUTRAN_25 = 55, MM_MODEM_BAND_EUTRAN_26 = 56, MM_MODEM_BAND_EUTRAN_27 = 57, MM_MODEM_BAND_EUTRAN_28 = 58, MM_MODEM_BAND_EUTRAN_29 = 59, MM_MODEM_BAND_EUTRAN_30 = 60, MM_MODEM_BAND_EUTRAN_31 = 61, MM_MODEM_BAND_EUTRAN_32 = 62, MM_MODEM_BAND_EUTRAN_33 = 63, MM_MODEM_BAND_EUTRAN_34 = 64, MM_MODEM_BAND_EUTRAN_35 = 65, MM_MODEM_BAND_EUTRAN_36 = 66, MM_MODEM_BAND_EUTRAN_37 = 67, MM_MODEM_BAND_EUTRAN_38 = 68, MM_MODEM_BAND_EUTRAN_39 = 69, MM_MODEM_BAND_EUTRAN_40 = 70, MM_MODEM_BAND_EUTRAN_41 = 71, MM_MODEM_BAND_EUTRAN_42 = 72, MM_MODEM_BAND_EUTRAN_43 = 73, MM_MODEM_BAND_EUTRAN_44 = 74, MM_MODEM_BAND_EUTRAN_45 = 75, MM_MODEM_BAND_EUTRAN_46 = 76, MM_MODEM_BAND_EUTRAN_47 = 77, MM_MODEM_BAND_EUTRAN_48 = 78, MM_MODEM_BAND_EUTRAN_49 = 79, MM_MODEM_BAND_EUTRAN_50 = 80, MM_MODEM_BAND_EUTRAN_51 = 81, MM_MODEM_BAND_EUTRAN_52 = 82, MM_MODEM_BAND_EUTRAN_53 = 83, MM_MODEM_BAND_EUTRAN_54 = 84, MM_MODEM_BAND_EUTRAN_55 = 85, MM_MODEM_BAND_EUTRAN_56 = 86, MM_MODEM_BAND_EUTRAN_57 = 87, MM_MODEM_BAND_EUTRAN_58 = 88, MM_MODEM_BAND_EUTRAN_59 = 89, MM_MODEM_BAND_EUTRAN_60 = 90, MM_MODEM_BAND_EUTRAN_61 = 91, MM_MODEM_BAND_EUTRAN_62 = 92, MM_MODEM_BAND_EUTRAN_63 = 93, MM_MODEM_BAND_EUTRAN_64 = 94, MM_MODEM_BAND_EUTRAN_65 = 95, MM_MODEM_BAND_EUTRAN_66 = 96, MM_MODEM_BAND_EUTRAN_67 = 97, MM_MODEM_BAND_EUTRAN_68 = 98, MM_MODEM_BAND_EUTRAN_69 = 99, MM_MODEM_BAND_EUTRAN_70 = 100, MM_MODEM_BAND_EUTRAN_71 = 101, MM_MODEM_BAND_EUTRAN_85 = 115, /* CDMA Band Classes (see 3GPP2 C.S0057-C) */ MM_MODEM_BAND_CDMA_BC0 = 128, MM_MODEM_BAND_CDMA_BC1 = 129, MM_MODEM_BAND_CDMA_BC2 = 130, MM_MODEM_BAND_CDMA_BC3 = 131, MM_MODEM_BAND_CDMA_BC4 = 132, MM_MODEM_BAND_CDMA_BC5 = 134, MM_MODEM_BAND_CDMA_BC6 = 135, MM_MODEM_BAND_CDMA_BC7 = 136, MM_MODEM_BAND_CDMA_BC8 = 137, MM_MODEM_BAND_CDMA_BC9 = 138, MM_MODEM_BAND_CDMA_BC10 = 139, MM_MODEM_BAND_CDMA_BC11 = 140, MM_MODEM_BAND_CDMA_BC12 = 141, MM_MODEM_BAND_CDMA_BC13 = 142, MM_MODEM_BAND_CDMA_BC14 = 143, MM_MODEM_BAND_CDMA_BC15 = 144, MM_MODEM_BAND_CDMA_BC16 = 145, MM_MODEM_BAND_CDMA_BC17 = 146, MM_MODEM_BAND_CDMA_BC18 = 147, MM_MODEM_BAND_CDMA_BC19 = 148, /* Additional UMTS bands: * 15-18 reserved * 23-24 reserved * 27-31 reserved */ MM_MODEM_BAND_UTRAN_10 = 210, MM_MODEM_BAND_UTRAN_11 = 211, MM_MODEM_BAND_UTRAN_12 = 212, MM_MODEM_BAND_UTRAN_13 = 213, MM_MODEM_BAND_UTRAN_14 = 214, MM_MODEM_BAND_UTRAN_19 = 219, MM_MODEM_BAND_UTRAN_20 = 220, MM_MODEM_BAND_UTRAN_21 = 221, MM_MODEM_BAND_UTRAN_22 = 222, MM_MODEM_BAND_UTRAN_25 = 225, MM_MODEM_BAND_UTRAN_26 = 226, MM_MODEM_BAND_UTRAN_32 = 232, /* All/Any */ MM_MODEM_BAND_ANY = 256, /* NR5G bands */ MM_MODEM_BAND_NGRAN_1 = 301, MM_MODEM_BAND_NGRAN_2 = 302, MM_MODEM_BAND_NGRAN_3 = 303, MM_MODEM_BAND_NGRAN_5 = 305, MM_MODEM_BAND_NGRAN_7 = 307, MM_MODEM_BAND_NGRAN_8 = 308, MM_MODEM_BAND_NGRAN_12 = 312, MM_MODEM_BAND_NGRAN_13 = 313, MM_MODEM_BAND_NGRAN_14 = 314, MM_MODEM_BAND_NGRAN_18 = 318, MM_MODEM_BAND_NGRAN_20 = 320, MM_MODEM_BAND_NGRAN_25 = 325, MM_MODEM_BAND_NGRAN_26 = 326, MM_MODEM_BAND_NGRAN_28 = 328, MM_MODEM_BAND_NGRAN_29 = 329, MM_MODEM_BAND_NGRAN_30 = 330, MM_MODEM_BAND_NGRAN_34 = 334, MM_MODEM_BAND_NGRAN_38 = 338, MM_MODEM_BAND_NGRAN_39 = 339, MM_MODEM_BAND_NGRAN_40 = 340, MM_MODEM_BAND_NGRAN_41 = 341, MM_MODEM_BAND_NGRAN_48 = 348, MM_MODEM_BAND_NGRAN_50 = 350, MM_MODEM_BAND_NGRAN_51 = 351, MM_MODEM_BAND_NGRAN_53 = 353, MM_MODEM_BAND_NGRAN_65 = 365, MM_MODEM_BAND_NGRAN_66 = 366, MM_MODEM_BAND_NGRAN_70 = 370, MM_MODEM_BAND_NGRAN_71 = 371, MM_MODEM_BAND_NGRAN_74 = 374, MM_MODEM_BAND_NGRAN_75 = 375, MM_MODEM_BAND_NGRAN_76 = 376, MM_MODEM_BAND_NGRAN_77 = 377, MM_MODEM_BAND_NGRAN_78 = 378, MM_MODEM_BAND_NGRAN_79 = 379, MM_MODEM_BAND_NGRAN_80 = 380, MM_MODEM_BAND_NGRAN_81 = 381, MM_MODEM_BAND_NGRAN_82 = 382, MM_MODEM_BAND_NGRAN_83 = 383, MM_MODEM_BAND_NGRAN_84 = 384, MM_MODEM_BAND_NGRAN_86 = 386, MM_MODEM_BAND_NGRAN_89 = 389, MM_MODEM_BAND_NGRAN_90 = 390, MM_MODEM_BAND_NGRAN_91 = 391, MM_MODEM_BAND_NGRAN_92 = 392, MM_MODEM_BAND_NGRAN_93 = 393, MM_MODEM_BAND_NGRAN_94 = 394, MM_MODEM_BAND_NGRAN_95 = 395, MM_MODEM_BAND_NGRAN_257 = 557, MM_MODEM_BAND_NGRAN_258 = 558, MM_MODEM_BAND_NGRAN_260 = 560, MM_MODEM_BAND_NGRAN_261 = 561 } MMModemBand; /** * MMModemPortType: * @MM_MODEM_PORT_TYPE_UNKNOWN: Unknown. * @MM_MODEM_PORT_TYPE_NET: Net port. * @MM_MODEM_PORT_TYPE_AT: AT port. * @MM_MODEM_PORT_TYPE_QCDM: QCDM port. * @MM_MODEM_PORT_TYPE_GPS: GPS port. * @MM_MODEM_PORT_TYPE_QMI: QMI port. * @MM_MODEM_PORT_TYPE_MBIM: MBIM port. * @MM_MODEM_PORT_TYPE_AUDIO: Audio port. Since 1.12. * @MM_MODEM_PORT_TYPE_IGNORED: Ignored port. Since 1.16. * * Type of modem port. * * Since: 1.0 */ typedef enum { /*< underscore_name=mm_modem_port_type >*/ MM_MODEM_PORT_TYPE_UNKNOWN = 1, MM_MODEM_PORT_TYPE_NET = 2, MM_MODEM_PORT_TYPE_AT = 3, MM_MODEM_PORT_TYPE_QCDM = 4, MM_MODEM_PORT_TYPE_GPS = 5, MM_MODEM_PORT_TYPE_QMI = 6, MM_MODEM_PORT_TYPE_MBIM = 7, MM_MODEM_PORT_TYPE_AUDIO = 8, MM_MODEM_PORT_TYPE_IGNORED = 9, } MMModemPortType; /** * MMCellType: * @MM_CELL_TYPE_UNKNOWN: Unknown. * @MM_CELL_TYPE_CDMA: CDMA cell. * @MM_CELL_TYPE_GSM: GSM cell. * @MM_CELL_TYPE_UMTS: UMTS cell. * @MM_CELL_TYPE_TDSCDMA: TD-SCDMA cell. * @MM_CELL_TYPE_LTE: LTE cell. * @MM_CELL_TYPE_5GNR: 5GNR cell. * * Type of cell information reported. * * Since: 1.20 */ typedef enum { /*< underscore_name=mm_cell_type >*/ MM_CELL_TYPE_UNKNOWN = 0, MM_CELL_TYPE_CDMA = 1, MM_CELL_TYPE_GSM = 2, MM_CELL_TYPE_UMTS = 3, MM_CELL_TYPE_TDSCDMA = 4, MM_CELL_TYPE_LTE = 5, MM_CELL_TYPE_5GNR = 6, } MMCellType; /** * MMSmsPduType: * @MM_SMS_PDU_TYPE_UNKNOWN: Unknown type. * @MM_SMS_PDU_TYPE_DELIVER: 3GPP Mobile-Terminated (MT) message. * @MM_SMS_PDU_TYPE_SUBMIT: 3GPP Mobile-Originated (MO) message. * @MM_SMS_PDU_TYPE_STATUS_REPORT: 3GPP status report (MT). * @MM_SMS_PDU_TYPE_CDMA_DELIVER: 3GPP2 Mobile-Terminated (MT) message. Since 1.2. * @MM_SMS_PDU_TYPE_CDMA_SUBMIT: 3GPP2 Mobile-Originated (MO) message. Since 1.2. * @MM_SMS_PDU_TYPE_CDMA_CANCELLATION: 3GPP2 Cancellation (MO) message. Since 1.2. * @MM_SMS_PDU_TYPE_CDMA_DELIVERY_ACKNOWLEDGEMENT: 3GPP2 Delivery Acknowledgement (MT) message. Since 1.2. * @MM_SMS_PDU_TYPE_CDMA_USER_ACKNOWLEDGEMENT: 3GPP2 User Acknowledgement (MT or MO) message. Since 1.2. * @MM_SMS_PDU_TYPE_CDMA_READ_ACKNOWLEDGEMENT: 3GPP2 Read Acknowledgement (MT or MO) message. Since 1.2. * * Type of PDUs used in the SMS. * * Since: 1.0 */ typedef enum { /*< underscore_name=mm_sms_pdu_type >*/ MM_SMS_PDU_TYPE_UNKNOWN = 0, MM_SMS_PDU_TYPE_DELIVER = 1, MM_SMS_PDU_TYPE_SUBMIT = 2, MM_SMS_PDU_TYPE_STATUS_REPORT = 3, MM_SMS_PDU_TYPE_CDMA_DELIVER = 32, MM_SMS_PDU_TYPE_CDMA_SUBMIT = 33, MM_SMS_PDU_TYPE_CDMA_CANCELLATION = 34, MM_SMS_PDU_TYPE_CDMA_DELIVERY_ACKNOWLEDGEMENT = 35, MM_SMS_PDU_TYPE_CDMA_USER_ACKNOWLEDGEMENT = 36, MM_SMS_PDU_TYPE_CDMA_READ_ACKNOWLEDGEMENT = 37, } MMSmsPduType; /** * MMSmsState: * @MM_SMS_STATE_UNKNOWN: State unknown or not reportable. * @MM_SMS_STATE_STORED: The message has been neither received nor yet sent. * @MM_SMS_STATE_RECEIVING: The message is being received but is not yet complete. * @MM_SMS_STATE_RECEIVED: The message has been completely received. * @MM_SMS_STATE_SENDING: The message is queued for delivery. * @MM_SMS_STATE_SENT: The message was successfully sent. * * State of a given SMS. * * Since: 1.0 */ typedef enum { /*< underscore_name=mm_sms_state >*/ MM_SMS_STATE_UNKNOWN = 0, MM_SMS_STATE_STORED = 1, MM_SMS_STATE_RECEIVING = 2, MM_SMS_STATE_RECEIVED = 3, MM_SMS_STATE_SENDING = 4, MM_SMS_STATE_SENT = 5, } MMSmsState; /** * MMSmsDeliveryState: * @MM_SMS_DELIVERY_STATE_COMPLETED_RECEIVED: Delivery completed, message received by the SME. * @MM_SMS_DELIVERY_STATE_COMPLETED_FORWARDED_UNCONFIRMED: Forwarded by the SC to the SME but the SC is unable to confirm delivery. * @MM_SMS_DELIVERY_STATE_COMPLETED_REPLACED_BY_SC: Message replaced by the SC. * @MM_SMS_DELIVERY_STATE_TEMPORARY_ERROR_CONGESTION: Temporary error, congestion. * @MM_SMS_DELIVERY_STATE_TEMPORARY_ERROR_SME_BUSY: Temporary error, SME busy. * @MM_SMS_DELIVERY_STATE_TEMPORARY_ERROR_NO_RESPONSE_FROM_SME: Temporary error, no response from the SME. * @MM_SMS_DELIVERY_STATE_TEMPORARY_ERROR_SERVICE_REJECTED: Temporary error, service rejected. * @MM_SMS_DELIVERY_STATE_TEMPORARY_ERROR_QOS_NOT_AVAILABLE: Temporary error, QoS not available. * @MM_SMS_DELIVERY_STATE_TEMPORARY_ERROR_IN_SME: Temporary error in the SME. * @MM_SMS_DELIVERY_STATE_ERROR_REMOTE_PROCEDURE: Permanent remote procedure error. * @MM_SMS_DELIVERY_STATE_ERROR_INCOMPATIBLE_DESTINATION: Permanent error, incompatible destination. * @MM_SMS_DELIVERY_STATE_ERROR_CONNECTION_REJECTED: Permanent error, connection rejected by the SME. * @MM_SMS_DELIVERY_STATE_ERROR_NOT_OBTAINABLE: Permanent error, not obtainable. * @MM_SMS_DELIVERY_STATE_ERROR_QOS_NOT_AVAILABLE: Permanent error, QoS not available. * @MM_SMS_DELIVERY_STATE_ERROR_NO_INTERWORKING_AVAILABLE: Permanent error, no interworking available. * @MM_SMS_DELIVERY_STATE_ERROR_VALIDITY_PERIOD_EXPIRED: Permanent error, message validity period expired. * @MM_SMS_DELIVERY_STATE_ERROR_DELETED_BY_ORIGINATING_SME: Permanent error, deleted by originating SME. * @MM_SMS_DELIVERY_STATE_ERROR_DELETED_BY_SC_ADMINISTRATION: Permanent error, deleted by SC administration. * @MM_SMS_DELIVERY_STATE_ERROR_MESSAGE_DOES_NOT_EXIST: Permanent error, message does no longer exist. * @MM_SMS_DELIVERY_STATE_TEMPORARY_FATAL_ERROR_CONGESTION: Permanent error, congestion. * @MM_SMS_DELIVERY_STATE_TEMPORARY_FATAL_ERROR_SME_BUSY: Permanent error, SME busy. * @MM_SMS_DELIVERY_STATE_TEMPORARY_FATAL_ERROR_NO_RESPONSE_FROM_SME: Permanent error, no response from the SME. * @MM_SMS_DELIVERY_STATE_TEMPORARY_FATAL_ERROR_SERVICE_REJECTED: Permanent error, service rejected. * @MM_SMS_DELIVERY_STATE_TEMPORARY_FATAL_ERROR_QOS_NOT_AVAILABLE: Permanent error, QoS not available. * @MM_SMS_DELIVERY_STATE_TEMPORARY_FATAL_ERROR_IN_SME: Permanent error in SME. * @MM_SMS_DELIVERY_STATE_UNKNOWN: Unknown state. * @MM_SMS_DELIVERY_STATE_NETWORK_PROBLEM_ADDRESS_VACANT: Permanent error in network, address vacant. Since 1.2. * @MM_SMS_DELIVERY_STATE_NETWORK_PROBLEM_ADDRESS_TRANSLATION_FAILURE: Permanent error in network, address translation failure. Since 1.2. * @MM_SMS_DELIVERY_STATE_NETWORK_PROBLEM_NETWORK_RESOURCE_OUTAGE: Permanent error in network, network resource outage. Since 1.2. * @MM_SMS_DELIVERY_STATE_NETWORK_PROBLEM_NETWORK_FAILURE: Permanent error in network, network failure. Since 1.2. * @MM_SMS_DELIVERY_STATE_NETWORK_PROBLEM_INVALID_TELESERVICE_ID: Permanent error in network, invalid teleservice id. Since 1.2. * @MM_SMS_DELIVERY_STATE_NETWORK_PROBLEM_OTHER: Permanent error, other network problem. Since 1.2. * @MM_SMS_DELIVERY_STATE_TERMINAL_PROBLEM_NO_PAGE_RESPONSE: Permanent error in terminal, no page response. Since 1.2. * @MM_SMS_DELIVERY_STATE_TERMINAL_PROBLEM_DESTINATION_BUSY: Permanent error in terminal, destination busy. Since 1.2. * @MM_SMS_DELIVERY_STATE_TERMINAL_PROBLEM_NO_ACKNOWLEDGMENT: Permanent error in terminal, no acknowledgement. Since 1.2. * @MM_SMS_DELIVERY_STATE_TERMINAL_PROBLEM_DESTINATION_RESOURCE_SHORTAGE: Permanent error in terminal, destination resource shortage. Since 1.2. * @MM_SMS_DELIVERY_STATE_TERMINAL_PROBLEM_SMS_DELIVERY_POSTPONED: Permanent error in terminal, SMS delivery postponed. Since 1.2. * @MM_SMS_DELIVERY_STATE_TERMINAL_PROBLEM_DESTINATION_OUT_OF_SERVICE: Permanent error in terminal, destination out of service. Since 1.2. * @MM_SMS_DELIVERY_STATE_TERMINAL_PROBLEM_DESTINATION_NO_LONGER_AT_THIS_ADDRESS: Permanent error in terminal, destination no longer at this address. Since 1.2. * @MM_SMS_DELIVERY_STATE_TERMINAL_PROBLEM_OTHER: Permanent error, other terminal problem. Since 1.2. * @MM_SMS_DELIVERY_STATE_RADIO_INTERFACE_PROBLEM_RESOURCE_SHORTAGE: Permanent error in radio interface, resource shortage. Since 1.2. * @MM_SMS_DELIVERY_STATE_RADIO_INTERFACE_PROBLEM_INCOMPATIBILITY: Permanent error in radio interface, problem incompatibility. Since 1.2. * @MM_SMS_DELIVERY_STATE_RADIO_INTERFACE_PROBLEM_OTHER: Permanent error, other radio interface problem. Since 1.2. * @MM_SMS_DELIVERY_STATE_GENERAL_PROBLEM_ENCODING: Permanent error, encoding. Since 1.2. * @MM_SMS_DELIVERY_STATE_GENERAL_PROBLEM_SMS_ORIGINATION_DENIED: Permanent error, SMS origination denied. Since 1.2. * @MM_SMS_DELIVERY_STATE_GENERAL_PROBLEM_SMS_TERMINATION_DENIED: Permanent error, SMS termination denied. Since 1.2. * @MM_SMS_DELIVERY_STATE_GENERAL_PROBLEM_SUPPLEMENTARY_SERVICE_NOT_SUPPORTED: Permanent error, supplementary service not supported. Since 1.2. * @MM_SMS_DELIVERY_STATE_GENERAL_PROBLEM_SMS_NOT_SUPPORTED: Permanent error, SMS not supported. Since 1.22. * @MM_SMS_DELIVERY_STATE_GENERAL_PROBLEM_MISSING_EXPECTED_PARAMETER: Permanent error, missing expected parameter. Since 1.2. * @MM_SMS_DELIVERY_STATE_GENERAL_PROBLEM_MISSING_MANDATORY_PARAMETER: Permanent error, missing mandatory parameter. Since 1.2. * @MM_SMS_DELIVERY_STATE_GENERAL_PROBLEM_UNRECOGNIZED_PARAMETER_VALUE: Permanent error, unrecognized parameter value. Since 1.2. * @MM_SMS_DELIVERY_STATE_GENERAL_PROBLEM_UNEXPECTED_PARAMETER_VALUE: Permanent error, unexpected parameter value. Since 1.2. * @MM_SMS_DELIVERY_STATE_GENERAL_PROBLEM_USER_DATA_SIZE_ERROR: Permanent error, user data size error. Since 1.2. * @MM_SMS_DELIVERY_STATE_GENERAL_PROBLEM_OTHER: Permanent error, other general problem. Since 1.2. * @MM_SMS_DELIVERY_STATE_TEMPORARY_NETWORK_PROBLEM_ADDRESS_VACANT: Temporary error in network, address vacant. Since 1.2. * @MM_SMS_DELIVERY_STATE_TEMPORARY_NETWORK_PROBLEM_ADDRESS_TRANSLATION_FAILURE: Temporary error in network, address translation failure. Since 1.2. * @MM_SMS_DELIVERY_STATE_TEMPORARY_NETWORK_PROBLEM_NETWORK_RESOURCE_OUTAGE: Temporary error in network, network resource outage. Since 1.2. * @MM_SMS_DELIVERY_STATE_TEMPORARY_NETWORK_PROBLEM_NETWORK_FAILURE: Temporary error in network, network failure. Since 1.2. * @MM_SMS_DELIVERY_STATE_TEMPORARY_NETWORK_PROBLEM_INVALID_TELESERVICE_ID: Temporary error in network, invalid teleservice id. Since 1.2. * @MM_SMS_DELIVERY_STATE_TEMPORARY_NETWORK_PROBLEM_OTHER: Temporary error, other network problem. Since 1.2. * @MM_SMS_DELIVERY_STATE_TEMPORARY_TERMINAL_PROBLEM_NO_PAGE_RESPONSE: Temporary error in terminal, no page response. Since 1.2. * @MM_SMS_DELIVERY_STATE_TEMPORARY_TERMINAL_PROBLEM_DESTINATION_BUSY: Temporary error in terminal, destination busy. Since 1.2. * @MM_SMS_DELIVERY_STATE_TEMPORARY_TERMINAL_PROBLEM_NO_ACKNOWLEDGMENT: Temporary error in terminal, no acknowledgement. Since 1.2. * @MM_SMS_DELIVERY_STATE_TEMPORARY_TERMINAL_PROBLEM_DESTINATION_RESOURCE_SHORTAGE: Temporary error in terminal, destination resource shortage. Since 1.2. * @MM_SMS_DELIVERY_STATE_TEMPORARY_TERMINAL_PROBLEM_SMS_DELIVERY_POSTPONED: Temporary error in terminal, SMS delivery postponed. Since 1.2. * @MM_SMS_DELIVERY_STATE_TEMPORARY_TERMINAL_PROBLEM_DESTINATION_OUT_OF_SERVICE: Temporary error in terminal, destination out of service. Since 1.2. * @MM_SMS_DELIVERY_STATE_TEMPORARY_TERMINAL_PROBLEM_DESTINATION_NO_LONGER_AT_THIS_ADDRESS: Temporary error in terminal, destination no longer at this address. Since 1.2. * @MM_SMS_DELIVERY_STATE_TEMPORARY_TERMINAL_PROBLEM_OTHER: Temporary error, other terminal problem. Since 1.2. * @MM_SMS_DELIVERY_STATE_TEMPORARY_RADIO_INTERFACE_PROBLEM_RESOURCE_SHORTAGE: Temporary error in radio interface, resource shortage. Since 1.2. * @MM_SMS_DELIVERY_STATE_TEMPORARY_RADIO_INTERFACE_PROBLEM_INCOMPATIBILITY: Temporary error in radio interface, problem incompatibility. Since 1.2. * @MM_SMS_DELIVERY_STATE_TEMPORARY_RADIO_INTERFACE_PROBLEM_OTHER: Temporary error, other radio interface problem. Since 1.2. * @MM_SMS_DELIVERY_STATE_TEMPORARY_GENERAL_PROBLEM_ENCODING: Temporary error, encoding. Since 1.2. * @MM_SMS_DELIVERY_STATE_TEMPORARY_GENERAL_PROBLEM_SMS_ORIGINATION_DENIED: Temporary error, SMS origination denied. Since 1.2. * @MM_SMS_DELIVERY_STATE_TEMPORARY_GENERAL_PROBLEM_SMS_TERMINATION_DENIED: Temporary error, SMS termination denied. Since 1.2. * @MM_SMS_DELIVERY_STATE_TEMPORARY_GENERAL_PROBLEM_SUPPLEMENTARY_SERVICE_NOT_SUPPORTED: Temporary error, supplementary service not supported. Since 1.2. * @MM_SMS_DELIVERY_STATE_TEMPORARY_GENERAL_PROBLEM_SMS_NOT_SUPPORTED: Temporary error, SMS not supported. Since 1.2. * @MM_SMS_DELIVERY_STATE_TEMPORARY_GENERAL_PROBLEM_MISSING_EXPECTED_PARAMETER: Temporary error, missing expected parameter. Since 1.2. * @MM_SMS_DELIVERY_STATE_TEMPORARY_GENERAL_PROBLEM_MISSING_MANDATORY_PARAMETER: Temporary error, missing mandatory parameter. Since 1.2. * @MM_SMS_DELIVERY_STATE_TEMPORARY_GENERAL_PROBLEM_UNRECOGNIZED_PARAMETER_VALUE: Temporary error, unrecognized parameter value. Since 1.2. * @MM_SMS_DELIVERY_STATE_TEMPORARY_GENERAL_PROBLEM_UNEXPECTED_PARAMETER_VALUE: Temporary error, unexpected parameter value. Since 1.2. * @MM_SMS_DELIVERY_STATE_TEMPORARY_GENERAL_PROBLEM_USER_DATA_SIZE_ERROR: Temporary error, user data size error. Since 1.2. * @MM_SMS_DELIVERY_STATE_TEMPORARY_GENERAL_PROBLEM_OTHER: Temporary error, other general problem. Since 1.2. * * Enumeration of known SMS delivery states as defined in 3GPP TS 03.40 and * 3GPP2 N.S0005-O, section 6.5.2.125. * * States out of the known ranges may also be valid (either reserved or SC-specific). * * Since: 1.0 */ typedef enum { /*< underscore_name=mm_sms_delivery_state >*/ /* --------------- 3GPP specific errors ---------------------- */ /* Completed deliveries */ MM_SMS_DELIVERY_STATE_COMPLETED_RECEIVED = 0x00, MM_SMS_DELIVERY_STATE_COMPLETED_FORWARDED_UNCONFIRMED = 0x01, MM_SMS_DELIVERY_STATE_COMPLETED_REPLACED_BY_SC = 0x02, /* Temporary failures */ MM_SMS_DELIVERY_STATE_TEMPORARY_ERROR_CONGESTION = 0x20, MM_SMS_DELIVERY_STATE_TEMPORARY_ERROR_SME_BUSY = 0x21, MM_SMS_DELIVERY_STATE_TEMPORARY_ERROR_NO_RESPONSE_FROM_SME = 0x22, MM_SMS_DELIVERY_STATE_TEMPORARY_ERROR_SERVICE_REJECTED = 0x23, MM_SMS_DELIVERY_STATE_TEMPORARY_ERROR_QOS_NOT_AVAILABLE = 0x24, MM_SMS_DELIVERY_STATE_TEMPORARY_ERROR_IN_SME = 0x25, /* Permanent failures */ MM_SMS_DELIVERY_STATE_ERROR_REMOTE_PROCEDURE = 0x40, MM_SMS_DELIVERY_STATE_ERROR_INCOMPATIBLE_DESTINATION = 0x41, MM_SMS_DELIVERY_STATE_ERROR_CONNECTION_REJECTED = 0x42, MM_SMS_DELIVERY_STATE_ERROR_NOT_OBTAINABLE = 0x43, MM_SMS_DELIVERY_STATE_ERROR_QOS_NOT_AVAILABLE = 0x44, MM_SMS_DELIVERY_STATE_ERROR_NO_INTERWORKING_AVAILABLE = 0x45, MM_SMS_DELIVERY_STATE_ERROR_VALIDITY_PERIOD_EXPIRED = 0x46, MM_SMS_DELIVERY_STATE_ERROR_DELETED_BY_ORIGINATING_SME = 0x47, MM_SMS_DELIVERY_STATE_ERROR_DELETED_BY_SC_ADMINISTRATION = 0x48, MM_SMS_DELIVERY_STATE_ERROR_MESSAGE_DOES_NOT_EXIST = 0x49, /* Temporary failures that became permanent */ MM_SMS_DELIVERY_STATE_TEMPORARY_FATAL_ERROR_CONGESTION = 0x60, MM_SMS_DELIVERY_STATE_TEMPORARY_FATAL_ERROR_SME_BUSY = 0x61, MM_SMS_DELIVERY_STATE_TEMPORARY_FATAL_ERROR_NO_RESPONSE_FROM_SME = 0x62, MM_SMS_DELIVERY_STATE_TEMPORARY_FATAL_ERROR_SERVICE_REJECTED = 0x63, MM_SMS_DELIVERY_STATE_TEMPORARY_FATAL_ERROR_QOS_NOT_AVAILABLE = 0x64, MM_SMS_DELIVERY_STATE_TEMPORARY_FATAL_ERROR_IN_SME = 0x65, /* Unknown, out of any possible valid value [0x00-0xFF] */ MM_SMS_DELIVERY_STATE_UNKNOWN = 0x100, /* --------------- 3GPP2 specific errors ---------------------- */ /* Network problems */ MM_SMS_DELIVERY_STATE_NETWORK_PROBLEM_ADDRESS_VACANT = 0x200, MM_SMS_DELIVERY_STATE_NETWORK_PROBLEM_ADDRESS_TRANSLATION_FAILURE = 0x201, MM_SMS_DELIVERY_STATE_NETWORK_PROBLEM_NETWORK_RESOURCE_OUTAGE = 0x202, MM_SMS_DELIVERY_STATE_NETWORK_PROBLEM_NETWORK_FAILURE = 0x203, MM_SMS_DELIVERY_STATE_NETWORK_PROBLEM_INVALID_TELESERVICE_ID = 0x204, MM_SMS_DELIVERY_STATE_NETWORK_PROBLEM_OTHER = 0x205, /* Terminal problems */ MM_SMS_DELIVERY_STATE_TERMINAL_PROBLEM_NO_PAGE_RESPONSE = 0x220, MM_SMS_DELIVERY_STATE_TERMINAL_PROBLEM_DESTINATION_BUSY = 0x221, MM_SMS_DELIVERY_STATE_TERMINAL_PROBLEM_NO_ACKNOWLEDGMENT = 0x222, MM_SMS_DELIVERY_STATE_TERMINAL_PROBLEM_DESTINATION_RESOURCE_SHORTAGE = 0x223, MM_SMS_DELIVERY_STATE_TERMINAL_PROBLEM_SMS_DELIVERY_POSTPONED = 0x224, MM_SMS_DELIVERY_STATE_TERMINAL_PROBLEM_DESTINATION_OUT_OF_SERVICE = 0x225, MM_SMS_DELIVERY_STATE_TERMINAL_PROBLEM_DESTINATION_NO_LONGER_AT_THIS_ADDRESS = 0x226, MM_SMS_DELIVERY_STATE_TERMINAL_PROBLEM_OTHER = 0x227, /* Radio problems */ MM_SMS_DELIVERY_STATE_RADIO_INTERFACE_PROBLEM_RESOURCE_SHORTAGE = 0x240, MM_SMS_DELIVERY_STATE_RADIO_INTERFACE_PROBLEM_INCOMPATIBILITY = 0x241, MM_SMS_DELIVERY_STATE_RADIO_INTERFACE_PROBLEM_OTHER = 0x242, /* General problems */ MM_SMS_DELIVERY_STATE_GENERAL_PROBLEM_ENCODING = 0x260, MM_SMS_DELIVERY_STATE_GENERAL_PROBLEM_SMS_ORIGINATION_DENIED = 0x261, MM_SMS_DELIVERY_STATE_GENERAL_PROBLEM_SMS_TERMINATION_DENIED = 0x262, MM_SMS_DELIVERY_STATE_GENERAL_PROBLEM_SUPPLEMENTARY_SERVICE_NOT_SUPPORTED = 0x263, MM_SMS_DELIVERY_STATE_GENERAL_PROBLEM_SMS_NOT_SUPPORTED = 0x264, MM_SMS_DELIVERY_STATE_GENERAL_PROBLEM_MISSING_EXPECTED_PARAMETER = 0x266, MM_SMS_DELIVERY_STATE_GENERAL_PROBLEM_MISSING_MANDATORY_PARAMETER = 0x267, MM_SMS_DELIVERY_STATE_GENERAL_PROBLEM_UNRECOGNIZED_PARAMETER_VALUE = 0x268, MM_SMS_DELIVERY_STATE_GENERAL_PROBLEM_UNEXPECTED_PARAMETER_VALUE = 0x269, MM_SMS_DELIVERY_STATE_GENERAL_PROBLEM_USER_DATA_SIZE_ERROR = 0x26A, MM_SMS_DELIVERY_STATE_GENERAL_PROBLEM_OTHER = 0x26B, /* Temporary network problems */ MM_SMS_DELIVERY_STATE_TEMPORARY_NETWORK_PROBLEM_ADDRESS_VACANT = 0x300, MM_SMS_DELIVERY_STATE_TEMPORARY_NETWORK_PROBLEM_ADDRESS_TRANSLATION_FAILURE = 0x301, MM_SMS_DELIVERY_STATE_TEMPORARY_NETWORK_PROBLEM_NETWORK_RESOURCE_OUTAGE = 0x302, MM_SMS_DELIVERY_STATE_TEMPORARY_NETWORK_PROBLEM_NETWORK_FAILURE = 0x303, MM_SMS_DELIVERY_STATE_TEMPORARY_NETWORK_PROBLEM_INVALID_TELESERVICE_ID = 0x304, MM_SMS_DELIVERY_STATE_TEMPORARY_NETWORK_PROBLEM_OTHER = 0x305, /* Temporary terminal problems */ MM_SMS_DELIVERY_STATE_TEMPORARY_TERMINAL_PROBLEM_NO_PAGE_RESPONSE = 0x320, MM_SMS_DELIVERY_STATE_TEMPORARY_TERMINAL_PROBLEM_DESTINATION_BUSY = 0x321, MM_SMS_DELIVERY_STATE_TEMPORARY_TERMINAL_PROBLEM_NO_ACKNOWLEDGMENT = 0x322, MM_SMS_DELIVERY_STATE_TEMPORARY_TERMINAL_PROBLEM_DESTINATION_RESOURCE_SHORTAGE = 0x323, MM_SMS_DELIVERY_STATE_TEMPORARY_TERMINAL_PROBLEM_SMS_DELIVERY_POSTPONED = 0x324, MM_SMS_DELIVERY_STATE_TEMPORARY_TERMINAL_PROBLEM_DESTINATION_OUT_OF_SERVICE = 0x325, MM_SMS_DELIVERY_STATE_TEMPORARY_TERMINAL_PROBLEM_DESTINATION_NO_LONGER_AT_THIS_ADDRESS = 0x326, MM_SMS_DELIVERY_STATE_TEMPORARY_TERMINAL_PROBLEM_OTHER = 0x327, /* Temporary radio problems */ MM_SMS_DELIVERY_STATE_TEMPORARY_RADIO_INTERFACE_PROBLEM_RESOURCE_SHORTAGE = 0x340, MM_SMS_DELIVERY_STATE_TEMPORARY_RADIO_INTERFACE_PROBLEM_INCOMPATIBILITY = 0x341, MM_SMS_DELIVERY_STATE_TEMPORARY_RADIO_INTERFACE_PROBLEM_OTHER = 0x342, /* Temporary general problems */ MM_SMS_DELIVERY_STATE_TEMPORARY_GENERAL_PROBLEM_ENCODING = 0x360, MM_SMS_DELIVERY_STATE_TEMPORARY_GENERAL_PROBLEM_SMS_ORIGINATION_DENIED = 0x361, MM_SMS_DELIVERY_STATE_TEMPORARY_GENERAL_PROBLEM_SMS_TERMINATION_DENIED = 0x362, MM_SMS_DELIVERY_STATE_TEMPORARY_GENERAL_PROBLEM_SUPPLEMENTARY_SERVICE_NOT_SUPPORTED = 0x363, MM_SMS_DELIVERY_STATE_TEMPORARY_GENERAL_PROBLEM_SMS_NOT_SUPPORTED = 0x364, MM_SMS_DELIVERY_STATE_TEMPORARY_GENERAL_PROBLEM_MISSING_EXPECTED_PARAMETER = 0x366, MM_SMS_DELIVERY_STATE_TEMPORARY_GENERAL_PROBLEM_MISSING_MANDATORY_PARAMETER = 0x367, MM_SMS_DELIVERY_STATE_TEMPORARY_GENERAL_PROBLEM_UNRECOGNIZED_PARAMETER_VALUE = 0x368, MM_SMS_DELIVERY_STATE_TEMPORARY_GENERAL_PROBLEM_UNEXPECTED_PARAMETER_VALUE = 0x369, MM_SMS_DELIVERY_STATE_TEMPORARY_GENERAL_PROBLEM_USER_DATA_SIZE_ERROR = 0x36A, MM_SMS_DELIVERY_STATE_TEMPORARY_GENERAL_PROBLEM_OTHER = 0x36B, } MMSmsDeliveryState; /** * MMSmsStorage: * @MM_SMS_STORAGE_UNKNOWN: Storage unknown. * @MM_SMS_STORAGE_SM: SIM card storage area. * @MM_SMS_STORAGE_ME: Mobile equipment storage area. * @MM_SMS_STORAGE_MT: Sum of SIM and Mobile equipment storages * @MM_SMS_STORAGE_SR: Status report message storage area. * @MM_SMS_STORAGE_BM: Broadcast message storage area. * @MM_SMS_STORAGE_TA: Terminal adaptor message storage area. * * Storage for SMS messages. * * Since: 1.0 */ typedef enum { /*< underscore_name=mm_sms_storage >*/ MM_SMS_STORAGE_UNKNOWN = 0, MM_SMS_STORAGE_SM = 1, MM_SMS_STORAGE_ME = 2, MM_SMS_STORAGE_MT = 3, MM_SMS_STORAGE_SR = 4, MM_SMS_STORAGE_BM = 5, MM_SMS_STORAGE_TA = 6, } MMSmsStorage; /** * MMSmsValidityType: * @MM_SMS_VALIDITY_TYPE_UNKNOWN: Validity type unknown. * @MM_SMS_VALIDITY_TYPE_RELATIVE: Relative validity. * @MM_SMS_VALIDITY_TYPE_ABSOLUTE: Absolute validity. * @MM_SMS_VALIDITY_TYPE_ENHANCED: Enhanced validity. * * Type of SMS validity value. * * Since: 1.0 */ typedef enum { /*< underscore_name=mm_sms_validity_type >*/ MM_SMS_VALIDITY_TYPE_UNKNOWN = 0, MM_SMS_VALIDITY_TYPE_RELATIVE = 1, MM_SMS_VALIDITY_TYPE_ABSOLUTE = 2, MM_SMS_VALIDITY_TYPE_ENHANCED = 3, } MMSmsValidityType; /** * MMSmsCdmaTeleserviceId: * @MM_SMS_CDMA_TELESERVICE_ID_UNKNOWN: Unknown. * @MM_SMS_CDMA_TELESERVICE_ID_CMT91: IS-91 Extended Protocol Enhanced Services. * @MM_SMS_CDMA_TELESERVICE_ID_WPT: Wireless Paging Teleservice. * @MM_SMS_CDMA_TELESERVICE_ID_WMT: Wireless Messaging Teleservice. * @MM_SMS_CDMA_TELESERVICE_ID_VMN: Voice Mail Notification. * @MM_SMS_CDMA_TELESERVICE_ID_WAP: Wireless Application Protocol. * @MM_SMS_CDMA_TELESERVICE_ID_WEMT: Wireless Enhanced Messaging Teleservice. * @MM_SMS_CDMA_TELESERVICE_ID_SCPT: Service Category Programming Teleservice. * @MM_SMS_CDMA_TELESERVICE_ID_CATPT: Card Application Toolkit Protocol Teleservice. * * Teleservice IDs supported for CDMA SMS, as defined in 3GPP2 X.S0004-550-E * (section 2.256) and 3GPP2 C.S0015-B (section 3.4.3.1). * * Since: 1.2 */ typedef enum { /*< underscore_name=mm_sms_cdma_teleservice_id >*/ MM_SMS_CDMA_TELESERVICE_ID_UNKNOWN = 0x0000, MM_SMS_CDMA_TELESERVICE_ID_CMT91 = 0x1000, MM_SMS_CDMA_TELESERVICE_ID_WPT = 0x1001, MM_SMS_CDMA_TELESERVICE_ID_WMT = 0x1002, MM_SMS_CDMA_TELESERVICE_ID_VMN = 0x1003, MM_SMS_CDMA_TELESERVICE_ID_WAP = 0x1004, MM_SMS_CDMA_TELESERVICE_ID_WEMT = 0x1005, MM_SMS_CDMA_TELESERVICE_ID_SCPT = 0x1006, MM_SMS_CDMA_TELESERVICE_ID_CATPT = 0x1007, } MMSmsCdmaTeleserviceId; /** * MMSmsCdmaServiceCategory: * @MM_SMS_CDMA_SERVICE_CATEGORY_UNKNOWN: Unknown. * @MM_SMS_CDMA_SERVICE_CATEGORY_EMERGENCY_BROADCAST: Emergency broadcast. * @MM_SMS_CDMA_SERVICE_CATEGORY_ADMINISTRATIVE: Administrative. * @MM_SMS_CDMA_SERVICE_CATEGORY_MAINTENANCE: Maintenance. * @MM_SMS_CDMA_SERVICE_CATEGORY_GENERAL_NEWS_LOCAL: General news (local). * @MM_SMS_CDMA_SERVICE_CATEGORY_GENERAL_NEWS_REGIONAL: General news (regional). * @MM_SMS_CDMA_SERVICE_CATEGORY_GENERAL_NEWS_NATIONAL: General news (national). * @MM_SMS_CDMA_SERVICE_CATEGORY_GENERAL_NEWS_INTERNATIONAL: General news (international). * @MM_SMS_CDMA_SERVICE_CATEGORY_BUSINESS_NEWS_LOCAL: Business/Financial news (local). * @MM_SMS_CDMA_SERVICE_CATEGORY_BUSINESS_NEWS_REGIONAL: Business/Financial news (regional). * @MM_SMS_CDMA_SERVICE_CATEGORY_BUSINESS_NEWS_NATIONAL: Business/Financial news (national). * @MM_SMS_CDMA_SERVICE_CATEGORY_BUSINESS_NEWS_INTERNATIONAL: Business/Financial news (international). * @MM_SMS_CDMA_SERVICE_CATEGORY_SPORTS_NEWS_LOCAL: Sports news (local). * @MM_SMS_CDMA_SERVICE_CATEGORY_SPORTS_NEWS_REGIONAL: Sports news (regional). * @MM_SMS_CDMA_SERVICE_CATEGORY_SPORTS_NEWS_NATIONAL: Sports news (national). * @MM_SMS_CDMA_SERVICE_CATEGORY_SPORTS_NEWS_INTERNATIONAL: Sports news (international). * @MM_SMS_CDMA_SERVICE_CATEGORY_ENTERTAINMENT_NEWS_LOCAL: Entertainment news (local). * @MM_SMS_CDMA_SERVICE_CATEGORY_ENTERTAINMENT_NEWS_REGIONAL: Entertainment news (regional). * @MM_SMS_CDMA_SERVICE_CATEGORY_ENTERTAINMENT_NEWS_NATIONAL: Entertainment news (national). * @MM_SMS_CDMA_SERVICE_CATEGORY_ENTERTAINMENT_NEWS_INTERNATIONAL: Entertainment news (international). * @MM_SMS_CDMA_SERVICE_CATEGORY_LOCAL_WEATHER: Local weather. * @MM_SMS_CDMA_SERVICE_CATEGORY_TRAFFIC_REPORT: Area traffic report. * @MM_SMS_CDMA_SERVICE_CATEGORY_FLIGHT_SCHEDULES: Local airport flight schedules. * @MM_SMS_CDMA_SERVICE_CATEGORY_RESTAURANTS: Restaurants. * @MM_SMS_CDMA_SERVICE_CATEGORY_LODGINGS: Lodgings. * @MM_SMS_CDMA_SERVICE_CATEGORY_RETAIL_DIRECTORY: Retail directory. * @MM_SMS_CDMA_SERVICE_CATEGORY_ADVERTISEMENTS: Advertisements. * @MM_SMS_CDMA_SERVICE_CATEGORY_STOCK_QUOTES: Stock quotes. * @MM_SMS_CDMA_SERVICE_CATEGORY_EMPLOYMENT: Employment. * @MM_SMS_CDMA_SERVICE_CATEGORY_HOSPITALS: Medical / Health / Hospitals. * @MM_SMS_CDMA_SERVICE_CATEGORY_TECHNOLOGY_NEWS: Technology news. * @MM_SMS_CDMA_SERVICE_CATEGORY_MULTICATEGORY: Multi-category. * @MM_SMS_CDMA_SERVICE_CATEGORY_CMAS_PRESIDENTIAL_ALERT: Presidential alert. * @MM_SMS_CDMA_SERVICE_CATEGORY_CMAS_EXTREME_THREAT: Extreme threat. * @MM_SMS_CDMA_SERVICE_CATEGORY_CMAS_SEVERE_THREAT: Severe threat. * @MM_SMS_CDMA_SERVICE_CATEGORY_CMAS_CHILD_ABDUCTION_EMERGENCY: Child abduction emergency. * @MM_SMS_CDMA_SERVICE_CATEGORY_CMAS_TEST: CMAS test. * * Service category for CDMA SMS, as defined in 3GPP2 C.R1001-D (section 9.3). * * Since: 1.2 */ typedef enum { /*< underscore_name=mm_sms_cdma_service_category >*/ MM_SMS_CDMA_SERVICE_CATEGORY_UNKNOWN = 0x0000, MM_SMS_CDMA_SERVICE_CATEGORY_EMERGENCY_BROADCAST = 0x0001, MM_SMS_CDMA_SERVICE_CATEGORY_ADMINISTRATIVE = 0x0002, MM_SMS_CDMA_SERVICE_CATEGORY_MAINTENANCE = 0x0003, MM_SMS_CDMA_SERVICE_CATEGORY_GENERAL_NEWS_LOCAL = 0x0004, MM_SMS_CDMA_SERVICE_CATEGORY_GENERAL_NEWS_REGIONAL = 0x0005, MM_SMS_CDMA_SERVICE_CATEGORY_GENERAL_NEWS_NATIONAL = 0x0006, MM_SMS_CDMA_SERVICE_CATEGORY_GENERAL_NEWS_INTERNATIONAL = 0x0007, MM_SMS_CDMA_SERVICE_CATEGORY_BUSINESS_NEWS_LOCAL = 0x0008, MM_SMS_CDMA_SERVICE_CATEGORY_BUSINESS_NEWS_REGIONAL = 0x0009, MM_SMS_CDMA_SERVICE_CATEGORY_BUSINESS_NEWS_NATIONAL = 0x000A, MM_SMS_CDMA_SERVICE_CATEGORY_BUSINESS_NEWS_INTERNATIONAL = 0x000B, MM_SMS_CDMA_SERVICE_CATEGORY_SPORTS_NEWS_LOCAL = 0x000C, MM_SMS_CDMA_SERVICE_CATEGORY_SPORTS_NEWS_REGIONAL = 0x000D, MM_SMS_CDMA_SERVICE_CATEGORY_SPORTS_NEWS_NATIONAL = 0x000E, MM_SMS_CDMA_SERVICE_CATEGORY_SPORTS_NEWS_INTERNATIONAL = 0x000F, MM_SMS_CDMA_SERVICE_CATEGORY_ENTERTAINMENT_NEWS_LOCAL = 0x0010, MM_SMS_CDMA_SERVICE_CATEGORY_ENTERTAINMENT_NEWS_REGIONAL = 0x0011, MM_SMS_CDMA_SERVICE_CATEGORY_ENTERTAINMENT_NEWS_NATIONAL = 0x0012, MM_SMS_CDMA_SERVICE_CATEGORY_ENTERTAINMENT_NEWS_INTERNATIONAL = 0x0013, MM_SMS_CDMA_SERVICE_CATEGORY_LOCAL_WEATHER = 0x0014, MM_SMS_CDMA_SERVICE_CATEGORY_TRAFFIC_REPORT = 0x0015, MM_SMS_CDMA_SERVICE_CATEGORY_FLIGHT_SCHEDULES = 0x0016, MM_SMS_CDMA_SERVICE_CATEGORY_RESTAURANTS = 0x0017, MM_SMS_CDMA_SERVICE_CATEGORY_LODGINGS = 0x0018, MM_SMS_CDMA_SERVICE_CATEGORY_RETAIL_DIRECTORY = 0x0019, MM_SMS_CDMA_SERVICE_CATEGORY_ADVERTISEMENTS = 0x001A, MM_SMS_CDMA_SERVICE_CATEGORY_STOCK_QUOTES = 0x001B, MM_SMS_CDMA_SERVICE_CATEGORY_EMPLOYMENT = 0x001C, MM_SMS_CDMA_SERVICE_CATEGORY_HOSPITALS = 0x001D, MM_SMS_CDMA_SERVICE_CATEGORY_TECHNOLOGY_NEWS = 0x001E, MM_SMS_CDMA_SERVICE_CATEGORY_MULTICATEGORY = 0x001F, MM_SMS_CDMA_SERVICE_CATEGORY_CMAS_PRESIDENTIAL_ALERT = 0x1000, MM_SMS_CDMA_SERVICE_CATEGORY_CMAS_EXTREME_THREAT = 0x1001, MM_SMS_CDMA_SERVICE_CATEGORY_CMAS_SEVERE_THREAT = 0x1002, MM_SMS_CDMA_SERVICE_CATEGORY_CMAS_CHILD_ABDUCTION_EMERGENCY = 0x1003, MM_SMS_CDMA_SERVICE_CATEGORY_CMAS_TEST = 0x1004, } MMSmsCdmaServiceCategory; /** * MMModemLocationSource: * @MM_MODEM_LOCATION_SOURCE_NONE: None. * @MM_MODEM_LOCATION_SOURCE_3GPP_LAC_CI: Location Area Code and Cell ID. * @MM_MODEM_LOCATION_SOURCE_GPS_RAW: GPS location given by predefined keys. * @MM_MODEM_LOCATION_SOURCE_GPS_NMEA: GPS location given as NMEA traces. * @MM_MODEM_LOCATION_SOURCE_CDMA_BS: CDMA base station position. * @MM_MODEM_LOCATION_SOURCE_GPS_UNMANAGED: No location given, just GPS module setup. Since 1.4. * @MM_MODEM_LOCATION_SOURCE_AGPS_MSA: Mobile Station Assisted A-GPS location requested. In * MSA A-GPS, the position fix is computed by a server online. The modem must have a valid SIM * card inserted and be enabled for this mode to be allowed. Since 1.12. * @MM_MODEM_LOCATION_SOURCE_AGPS_MSB: Mobile Station Based A-GPS location requested. In MSB * A-GPS, the position fix is computed by the modem, but it first gathers information from an * online server to facilitate the process (e.g. ephemeris). The modem must have a valid SIM * card inserted and be enabled for this mode to be allowed. Since 1.12. * * Sources of location information supported by the modem. * * Since: 1.0 */ typedef enum { /*< underscore_name=mm_modem_location_source >*/ MM_MODEM_LOCATION_SOURCE_NONE = 0, MM_MODEM_LOCATION_SOURCE_3GPP_LAC_CI = 1 << 0, MM_MODEM_LOCATION_SOURCE_GPS_RAW = 1 << 1, MM_MODEM_LOCATION_SOURCE_GPS_NMEA = 1 << 2, MM_MODEM_LOCATION_SOURCE_CDMA_BS = 1 << 3, MM_MODEM_LOCATION_SOURCE_GPS_UNMANAGED = 1 << 4, MM_MODEM_LOCATION_SOURCE_AGPS_MSA = 1 << 5, MM_MODEM_LOCATION_SOURCE_AGPS_MSB = 1 << 6, #if defined (MM_COMPILATION) /*< private >*/ MM_MODEM_LOCATION_SOURCE_FIRST = MM_MODEM_LOCATION_SOURCE_3GPP_LAC_CI, /*< skip >*/ MM_MODEM_LOCATION_SOURCE_LAST = MM_MODEM_LOCATION_SOURCE_AGPS_MSB, /*< skip >*/ #endif } MMModemLocationSource; /** * MMModemLocationAssistanceDataType: * @MM_MODEM_LOCATION_ASSISTANCE_DATA_TYPE_NONE: None. * @MM_MODEM_LOCATION_ASSISTANCE_DATA_TYPE_XTRA: Qualcomm gpsOneXTRA. * * Type of assistance data that may be injected to the GNSS module. * * Since: 1.10 */ typedef enum { /*< underscore_name=mm_modem_location_assistance_data_type >*/ MM_MODEM_LOCATION_ASSISTANCE_DATA_TYPE_NONE = 0, MM_MODEM_LOCATION_ASSISTANCE_DATA_TYPE_XTRA = 1 << 0, } MMModemLocationAssistanceDataType; /** * MMModemContactsStorage: * @MM_MODEM_CONTACTS_STORAGE_UNKNOWN: Unknown location. * @MM_MODEM_CONTACTS_STORAGE_ME: Device's local memory. * @MM_MODEM_CONTACTS_STORAGE_SM: Card inserted in the device (like a SIM/RUIM). * @MM_MODEM_CONTACTS_STORAGE_MT: Combined device/ME and SIM/SM phonebook. * * Specifies different storage locations for contact information. * * Since: 1.0 */ typedef enum { /*< underscore_name=mm_modem_contacts_storage >*/ MM_MODEM_CONTACTS_STORAGE_UNKNOWN = 0, MM_MODEM_CONTACTS_STORAGE_ME = 1, MM_MODEM_CONTACTS_STORAGE_SM = 2, MM_MODEM_CONTACTS_STORAGE_MT = 3, } MMModemContactsStorage; /** * MMBearerType: * @MM_BEARER_TYPE_UNKNOWN: Unknown bearer. * @MM_BEARER_TYPE_DEFAULT: Primary context (2G/3G) or default bearer (4G), * defined by the user of the API. * @MM_BEARER_TYPE_DEFAULT_ATTACH: The initial default bearer established * during LTE attach procedure, automatically connected as long as the device is * regitered in the LTE network. * @MM_BEARER_TYPE_DEDICATED: Secondary context (2G/3G) or dedicated bearer * (4G), defined by the user of the API. These bearers use the same IP address * used by a primary context or default bearer and provide a dedicated flow for * specific traffic with different QoS settings. * * Type of context (2G/3G) or bearer (4G). * * Since: 1.10 */ typedef enum { /*< underscore_name=mm_bearer_type >*/ MM_BEARER_TYPE_UNKNOWN = 0, MM_BEARER_TYPE_DEFAULT = 1, MM_BEARER_TYPE_DEFAULT_ATTACH = 2, MM_BEARER_TYPE_DEDICATED = 3, } MMBearerType; /** * MMBearerIpMethod: * @MM_BEARER_IP_METHOD_UNKNOWN: Unknown method. * @MM_BEARER_IP_METHOD_PPP: Use PPP to get IP addresses and DNS information. * For IPv6, use PPP to retrieve the 64-bit Interface Identifier, use the IID to * construct an IPv6 link-local address by following RFC 5072, and then run * DHCP over the PPP link to retrieve DNS settings. * @MM_BEARER_IP_METHOD_STATIC: Use the provided static IP configuration given * by the modem to configure the IP data interface. Note that DNS servers may * not be provided by the network or modem firmware. * @MM_BEARER_IP_METHOD_DHCP: Begin DHCP or IPv6 SLAAC on the data interface to * obtain any necessary IP configuration details that are not already provided * by the IP configuration. For IPv4 bearers DHCP should be used. For IPv6 * bearers SLAAC should be used, and the IP configuration may already contain * a link-local address that should be assigned to the interface before SLAAC * is started to obtain the rest of the configuration. * * Type of IP method configuration to be used in a given Bearer. * * Since: 1.0 */ typedef enum { /*< underscore_name=mm_bearer_ip_method >*/ MM_BEARER_IP_METHOD_UNKNOWN = 0, MM_BEARER_IP_METHOD_PPP = 1, MM_BEARER_IP_METHOD_STATIC = 2, MM_BEARER_IP_METHOD_DHCP = 3, } MMBearerIpMethod; /** * MMBearerIpFamily: * @MM_BEARER_IP_FAMILY_NONE: None or unknown. * @MM_BEARER_IP_FAMILY_IPV4: IPv4. * @MM_BEARER_IP_FAMILY_IPV6: IPv6. * @MM_BEARER_IP_FAMILY_IPV4V6: IPv4 and IPv6. * @MM_BEARER_IP_FAMILY_NON_IP: Non-IP Bearer. Since 1.20. * @MM_BEARER_IP_FAMILY_ANY: Mask specifying all IP based families. * * Type of IP family to be used in a given Bearer. * * Since: 1.0 */ typedef enum { /*< underscore_name=mm_bearer_ip_family >*/ MM_BEARER_IP_FAMILY_NONE = 0, MM_BEARER_IP_FAMILY_IPV4 = 1 << 0, MM_BEARER_IP_FAMILY_IPV6 = 1 << 1, MM_BEARER_IP_FAMILY_IPV4V6 = 1 << 2, MM_BEARER_IP_FAMILY_NON_IP = 1 << 3, MM_BEARER_IP_FAMILY_ANY = 0xFFFFFFF7 } MMBearerIpFamily; /** * MMBearerAllowedAuth: * @MM_BEARER_ALLOWED_AUTH_UNKNOWN: Unknown. * @MM_BEARER_ALLOWED_AUTH_NONE: None. * @MM_BEARER_ALLOWED_AUTH_PAP: PAP. * @MM_BEARER_ALLOWED_AUTH_CHAP: CHAP. * @MM_BEARER_ALLOWED_AUTH_MSCHAP: MS-CHAP. * @MM_BEARER_ALLOWED_AUTH_MSCHAPV2: MS-CHAP v2. * @MM_BEARER_ALLOWED_AUTH_EAP: EAP. * * Allowed authentication methods when authenticating with the network. * * Since: 1.0 */ typedef enum { /*< underscore_name=mm_bearer_allowed_auth >*/ MM_BEARER_ALLOWED_AUTH_UNKNOWN = 0, /* bits 0..4 order match Ericsson device bitmap */ MM_BEARER_ALLOWED_AUTH_NONE = 1 << 0, MM_BEARER_ALLOWED_AUTH_PAP = 1 << 1, MM_BEARER_ALLOWED_AUTH_CHAP = 1 << 2, MM_BEARER_ALLOWED_AUTH_MSCHAP = 1 << 3, MM_BEARER_ALLOWED_AUTH_MSCHAPV2 = 1 << 4, MM_BEARER_ALLOWED_AUTH_EAP = 1 << 5, } MMBearerAllowedAuth; /** * MMModemCdmaRegistrationState: * @MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN: Registration status is unknown or the device is not registered. * @MM_MODEM_CDMA_REGISTRATION_STATE_REGISTERED: Registered, but roaming status is unknown or cannot be provided by the device. The device may or may not be roaming. * @MM_MODEM_CDMA_REGISTRATION_STATE_HOME: Currently registered on the home network. * @MM_MODEM_CDMA_REGISTRATION_STATE_ROAMING: Currently registered on a roaming network. * * Registration state of a CDMA modem. * * Since: 1.0 */ typedef enum { /*< underscore_name=mm_modem_cdma_registration_state >*/ 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, } MMModemCdmaRegistrationState; /** * MMModemCdmaActivationState: * @MM_MODEM_CDMA_ACTIVATION_STATE_UNKNOWN: Unknown activation state. * @MM_MODEM_CDMA_ACTIVATION_STATE_NOT_ACTIVATED: Device is not activated * @MM_MODEM_CDMA_ACTIVATION_STATE_ACTIVATING: Device is activating * @MM_MODEM_CDMA_ACTIVATION_STATE_PARTIALLY_ACTIVATED: Device is partially activated; carrier-specific steps required to continue. * @MM_MODEM_CDMA_ACTIVATION_STATE_ACTIVATED: Device is ready for use. * * Activation state of a CDMA modem. * * Since: 1.0 */ typedef enum { /*< underscore_name=mm_modem_cdma_activation_state >*/ MM_MODEM_CDMA_ACTIVATION_STATE_UNKNOWN = 0, MM_MODEM_CDMA_ACTIVATION_STATE_NOT_ACTIVATED = 1, MM_MODEM_CDMA_ACTIVATION_STATE_ACTIVATING = 2, MM_MODEM_CDMA_ACTIVATION_STATE_PARTIALLY_ACTIVATED = 3, MM_MODEM_CDMA_ACTIVATION_STATE_ACTIVATED = 4, } MMModemCdmaActivationState; /** * MMModemCdmaRmProtocol: * @MM_MODEM_CDMA_RM_PROTOCOL_UNKNOWN: Unknown protocol. * @MM_MODEM_CDMA_RM_PROTOCOL_ASYNC: Asynchronous data or fax. * @MM_MODEM_CDMA_RM_PROTOCOL_PACKET_RELAY: Packet data service, Relay Layer Rm interface. * @MM_MODEM_CDMA_RM_PROTOCOL_PACKET_NETWORK_PPP: Packet data service, Network Layer Rm interface, PPP. * @MM_MODEM_CDMA_RM_PROTOCOL_PACKET_NETWORK_SLIP: Packet data service, Network Layer Rm interface, SLIP. * @MM_MODEM_CDMA_RM_PROTOCOL_STU_III: STU-III service. * * Protocol of the Rm interface in modems with CDMA capabilities. * * Since: 1.0 */ typedef enum { /*< underscore_name=mm_modem_cdma_rm_protocol >*/ MM_MODEM_CDMA_RM_PROTOCOL_UNKNOWN = 0, MM_MODEM_CDMA_RM_PROTOCOL_ASYNC = 1, MM_MODEM_CDMA_RM_PROTOCOL_PACKET_RELAY = 2, MM_MODEM_CDMA_RM_PROTOCOL_PACKET_NETWORK_PPP = 3, MM_MODEM_CDMA_RM_PROTOCOL_PACKET_NETWORK_SLIP = 4, MM_MODEM_CDMA_RM_PROTOCOL_STU_III = 5, } MMModemCdmaRmProtocol; /** * MMModem3gppRegistrationState: * @MM_MODEM_3GPP_REGISTRATION_STATE_IDLE: Not registered, not searching for new operator to register. * @MM_MODEM_3GPP_REGISTRATION_STATE_HOME: Registered on home network. * @MM_MODEM_3GPP_REGISTRATION_STATE_SEARCHING: Not registered, searching for new operator to register with. * @MM_MODEM_3GPP_REGISTRATION_STATE_DENIED: Registration denied. * @MM_MODEM_3GPP_REGISTRATION_STATE_UNKNOWN: Unknown registration status. * @MM_MODEM_3GPP_REGISTRATION_STATE_ROAMING: Registered on a roaming network. * @MM_MODEM_3GPP_REGISTRATION_STATE_HOME_SMS_ONLY: Registered for "SMS only", home network (applicable only when on LTE). Since 1.8. * @MM_MODEM_3GPP_REGISTRATION_STATE_ROAMING_SMS_ONLY: Registered for "SMS only", roaming network (applicable only when on LTE). Since 1.8. * @MM_MODEM_3GPP_REGISTRATION_STATE_EMERGENCY_ONLY: Emergency services only. Since 1.8. * @MM_MODEM_3GPP_REGISTRATION_STATE_HOME_CSFB_NOT_PREFERRED: Registered for "CSFB not preferred", home network (applicable only when on LTE). Since 1.8. * @MM_MODEM_3GPP_REGISTRATION_STATE_ROAMING_CSFB_NOT_PREFERRED: Registered for "CSFB not preferred", roaming network (applicable only when on LTE). Since 1.8. * @MM_MODEM_3GPP_REGISTRATION_STATE_ATTACHED_RLOS: Attached for access to Restricted Local Operator Services (applicable only when on LTE). Since 1.14. * * GSM registration code as defined in 3GPP TS 27.007. * * Since: 1.0 */ typedef enum { /*< underscore_name=mm_modem_3gpp_registration_state >*/ MM_MODEM_3GPP_REGISTRATION_STATE_IDLE = 0, MM_MODEM_3GPP_REGISTRATION_STATE_HOME = 1, MM_MODEM_3GPP_REGISTRATION_STATE_SEARCHING = 2, MM_MODEM_3GPP_REGISTRATION_STATE_DENIED = 3, MM_MODEM_3GPP_REGISTRATION_STATE_UNKNOWN = 4, MM_MODEM_3GPP_REGISTRATION_STATE_ROAMING = 5, MM_MODEM_3GPP_REGISTRATION_STATE_HOME_SMS_ONLY = 6, MM_MODEM_3GPP_REGISTRATION_STATE_ROAMING_SMS_ONLY = 7, MM_MODEM_3GPP_REGISTRATION_STATE_EMERGENCY_ONLY = 8, MM_MODEM_3GPP_REGISTRATION_STATE_HOME_CSFB_NOT_PREFERRED = 9, MM_MODEM_3GPP_REGISTRATION_STATE_ROAMING_CSFB_NOT_PREFERRED = 10, MM_MODEM_3GPP_REGISTRATION_STATE_ATTACHED_RLOS = 11, } MMModem3gppRegistrationState; /** * MMModem3gppFacility: * @MM_MODEM_3GPP_FACILITY_NONE: No facility. * @MM_MODEM_3GPP_FACILITY_SIM: SIM lock. * @MM_MODEM_3GPP_FACILITY_FIXED_DIALING: Fixed dialing (PIN2) SIM lock. * @MM_MODEM_3GPP_FACILITY_PH_SIM: Device is locked to a specific SIM. * @MM_MODEM_3GPP_FACILITY_PH_FSIM: Device is locked to first SIM inserted. * @MM_MODEM_3GPP_FACILITY_NET_PERS: Network personalization. * @MM_MODEM_3GPP_FACILITY_NET_SUB_PERS: Network subset personalization. * @MM_MODEM_3GPP_FACILITY_PROVIDER_PERS: Service provider personalization. * @MM_MODEM_3GPP_FACILITY_CORP_PERS: Corporate personalization. * * A bitfield describing which facilities have a lock enabled, i.e., * requires a pin or unlock code. The facilities include the * personalizations (device locks) described in 3GPP spec TS 22.022, * and the PIN and PIN2 locks, which are SIM locks. * * Since: 1.0 */ typedef enum { /*< underscore_name=mm_modem_3gpp_facility >*/ MM_MODEM_3GPP_FACILITY_NONE = 0, MM_MODEM_3GPP_FACILITY_SIM = 1 << 0, MM_MODEM_3GPP_FACILITY_FIXED_DIALING = 1 << 1, MM_MODEM_3GPP_FACILITY_PH_SIM = 1 << 2, MM_MODEM_3GPP_FACILITY_PH_FSIM = 1 << 3, MM_MODEM_3GPP_FACILITY_NET_PERS = 1 << 4, MM_MODEM_3GPP_FACILITY_NET_SUB_PERS = 1 << 5, MM_MODEM_3GPP_FACILITY_PROVIDER_PERS = 1 << 6, MM_MODEM_3GPP_FACILITY_CORP_PERS = 1 << 7, } MMModem3gppFacility; /** * MMModem3gppNetworkAvailability: * @MM_MODEM_3GPP_NETWORK_AVAILABILITY_UNKNOWN: Unknown availability. * @MM_MODEM_3GPP_NETWORK_AVAILABILITY_AVAILABLE: Network is available. * @MM_MODEM_3GPP_NETWORK_AVAILABILITY_CURRENT: Network is the current one. * @MM_MODEM_3GPP_NETWORK_AVAILABILITY_FORBIDDEN: Network is forbidden. * * Network availability status as defined in 3GPP TS 27.007 section 7.3. * * Since: 1.0 */ typedef enum { /*< underscore_name=mm_modem_3gpp_network_availability >*/ MM_MODEM_3GPP_NETWORK_AVAILABILITY_UNKNOWN = 0, MM_MODEM_3GPP_NETWORK_AVAILABILITY_AVAILABLE = 1, MM_MODEM_3GPP_NETWORK_AVAILABILITY_CURRENT = 2, MM_MODEM_3GPP_NETWORK_AVAILABILITY_FORBIDDEN = 3, } MMModem3gppNetworkAvailability; /** * MMModem3gppSubscriptionState: * @MM_MODEM_3GPP_SUBSCRIPTION_STATE_UNKNOWN: The subscription state is unknown. * @MM_MODEM_3GPP_SUBSCRIPTION_STATE_UNPROVISIONED: The account is unprovisioned. * @MM_MODEM_3GPP_SUBSCRIPTION_STATE_PROVISIONED: The account is provisioned and has data available. * @MM_MODEM_3GPP_SUBSCRIPTION_STATE_OUT_OF_DATA: The account is provisioned but there is no data left. * * Describes the current subscription status of the SIM. This value is only available after the * modem attempts to register with the network. * * Since: 1.2 */ typedef enum { /*< underscore_name=mm_modem_3gpp_subscription_state >*/ MM_MODEM_3GPP_SUBSCRIPTION_STATE_UNKNOWN = 0, MM_MODEM_3GPP_SUBSCRIPTION_STATE_UNPROVISIONED = 1, MM_MODEM_3GPP_SUBSCRIPTION_STATE_PROVISIONED = 2, MM_MODEM_3GPP_SUBSCRIPTION_STATE_OUT_OF_DATA = 3, } MMModem3gppSubscriptionState; /** * MMModem3gppUssdSessionState: * @MM_MODEM_3GPP_USSD_SESSION_STATE_UNKNOWN: Unknown state. * @MM_MODEM_3GPP_USSD_SESSION_STATE_IDLE: No active session. * @MM_MODEM_3GPP_USSD_SESSION_STATE_ACTIVE: A session is active and the mobile is waiting for a response. * @MM_MODEM_3GPP_USSD_SESSION_STATE_USER_RESPONSE: The network is waiting for the client's response. * * State of a USSD session. * * Since: 1.0 */ typedef enum { /*< underscore_name=mm_modem_3gpp_ussd_session_state >*/ MM_MODEM_3GPP_USSD_SESSION_STATE_UNKNOWN = 0, MM_MODEM_3GPP_USSD_SESSION_STATE_IDLE = 1, MM_MODEM_3GPP_USSD_SESSION_STATE_ACTIVE = 2, MM_MODEM_3GPP_USSD_SESSION_STATE_USER_RESPONSE = 3, } MMModem3gppUssdSessionState; /** * MMModem3gppEpsUeModeOperation: * @MM_MODEM_3GPP_EPS_UE_MODE_OPERATION_UNKNOWN: Unknown or not applicable. * @MM_MODEM_3GPP_EPS_UE_MODE_OPERATION_PS_1: PS mode 1 of operation: EPS only, voice-centric. * @MM_MODEM_3GPP_EPS_UE_MODE_OPERATION_PS_2: PS mode 2 of operation: EPS only, data-centric. * @MM_MODEM_3GPP_EPS_UE_MODE_OPERATION_CSPS_1: CS/PS mode 1 of operation: EPS and non-EPS, voice-centric. * @MM_MODEM_3GPP_EPS_UE_MODE_OPERATION_CSPS_2: CS/PS mode 2 of operation: EPS and non-EPS, data-centric. * * UE mode of operation for EPS, as per 3GPP TS 24.301. * * Since: 1.8 */ typedef enum { /*< underscore_name=mm_modem_3gpp_eps_ue_mode_operation >*/ MM_MODEM_3GPP_EPS_UE_MODE_OPERATION_UNKNOWN = 0, MM_MODEM_3GPP_EPS_UE_MODE_OPERATION_PS_1 = 1, MM_MODEM_3GPP_EPS_UE_MODE_OPERATION_PS_2 = 2, MM_MODEM_3GPP_EPS_UE_MODE_OPERATION_CSPS_1 = 3, MM_MODEM_3GPP_EPS_UE_MODE_OPERATION_CSPS_2 = 4, } MMModem3gppEpsUeModeOperation; /** * MMFirmwareImageType: * @MM_FIRMWARE_IMAGE_TYPE_UNKNOWN: Unknown firmware type. * @MM_FIRMWARE_IMAGE_TYPE_GENERIC: Generic firmware image. * @MM_FIRMWARE_IMAGE_TYPE_GOBI: Firmware image of Gobi devices. * * Type of firmware image. * * Since: 1.0 */ typedef enum { /*< underscore_name=mm_firmware_image_type >*/ MM_FIRMWARE_IMAGE_TYPE_UNKNOWN = 0, MM_FIRMWARE_IMAGE_TYPE_GENERIC = 1, MM_FIRMWARE_IMAGE_TYPE_GOBI = 2, } MMFirmwareImageType; /** * MMOmaFeature: * @MM_OMA_FEATURE_NONE: None. * @MM_OMA_FEATURE_DEVICE_PROVISIONING: Device provisioning service. * @MM_OMA_FEATURE_PRL_UPDATE: PRL update service. * @MM_OMA_FEATURE_HANDS_FREE_ACTIVATION: Hands free activation service. * * Features that can be enabled or disabled in the OMA device management support. * * Since: 1.2 */ typedef enum { /*< underscore_name=mm_oma_feature >*/ MM_OMA_FEATURE_NONE = 0, MM_OMA_FEATURE_DEVICE_PROVISIONING = 1 << 0, MM_OMA_FEATURE_PRL_UPDATE = 1 << 1, MM_OMA_FEATURE_HANDS_FREE_ACTIVATION = 1 << 2, } MMOmaFeature; /** * MMOmaSessionType: * @MM_OMA_SESSION_TYPE_UNKNOWN: Unknown session type. * @MM_OMA_SESSION_TYPE_CLIENT_INITIATED_DEVICE_CONFIGURE: Client-initiated device configure. * @MM_OMA_SESSION_TYPE_CLIENT_INITIATED_PRL_UPDATE: Client-initiated PRL update. * @MM_OMA_SESSION_TYPE_CLIENT_INITIATED_HANDS_FREE_ACTIVATION: Client-initiated hands free activation. * @MM_OMA_SESSION_TYPE_NETWORK_INITIATED_DEVICE_CONFIGURE: Network-initiated device configure. * @MM_OMA_SESSION_TYPE_NETWORK_INITIATED_PRL_UPDATE: Network-initiated PRL update. * @MM_OMA_SESSION_TYPE_DEVICE_INITIATED_PRL_UPDATE: Device-initiated PRL update. * @MM_OMA_SESSION_TYPE_DEVICE_INITIATED_HANDS_FREE_ACTIVATION: Device-initiated hands free activation. * * Type of OMA device management session. * * Since: 1.2 */ typedef enum { /*< underscore_name=mm_oma_session_type >*/ MM_OMA_SESSION_TYPE_UNKNOWN = 0, MM_OMA_SESSION_TYPE_CLIENT_INITIATED_DEVICE_CONFIGURE = 10, MM_OMA_SESSION_TYPE_CLIENT_INITIATED_PRL_UPDATE = 11, MM_OMA_SESSION_TYPE_CLIENT_INITIATED_HANDS_FREE_ACTIVATION = 12, MM_OMA_SESSION_TYPE_NETWORK_INITIATED_DEVICE_CONFIGURE = 20, MM_OMA_SESSION_TYPE_NETWORK_INITIATED_PRL_UPDATE = 21, MM_OMA_SESSION_TYPE_DEVICE_INITIATED_PRL_UPDATE = 30, MM_OMA_SESSION_TYPE_DEVICE_INITIATED_HANDS_FREE_ACTIVATION = 31, } MMOmaSessionType; /** * MMOmaSessionState: * @MM_OMA_SESSION_STATE_FAILED: Failed. * @MM_OMA_SESSION_STATE_UNKNOWN: Unknown. * @MM_OMA_SESSION_STATE_STARTED: Started. * @MM_OMA_SESSION_STATE_RETRYING: Retrying. * @MM_OMA_SESSION_STATE_CONNECTING: Connecting. * @MM_OMA_SESSION_STATE_CONNECTED: Connected. * @MM_OMA_SESSION_STATE_AUTHENTICATED: Authenticated. * @MM_OMA_SESSION_STATE_MDN_DOWNLOADED: MDN downloaded. * @MM_OMA_SESSION_STATE_MSID_DOWNLOADED: MSID downloaded. * @MM_OMA_SESSION_STATE_PRL_DOWNLOADED: PRL downloaded. * @MM_OMA_SESSION_STATE_MIP_PROFILE_DOWNLOADED: MIP profile downloaded. * @MM_OMA_SESSION_STATE_COMPLETED: Session completed. * * State of the OMA device management session. * * Since: 1.2 */ typedef enum { /*< underscore_name=mm_oma_session_state >*/ MM_OMA_SESSION_STATE_FAILED = -1, MM_OMA_SESSION_STATE_UNKNOWN = 0, MM_OMA_SESSION_STATE_STARTED = 1, MM_OMA_SESSION_STATE_RETRYING = 2, MM_OMA_SESSION_STATE_CONNECTING = 3, MM_OMA_SESSION_STATE_CONNECTED = 4, MM_OMA_SESSION_STATE_AUTHENTICATED = 5, MM_OMA_SESSION_STATE_MDN_DOWNLOADED = 10, MM_OMA_SESSION_STATE_MSID_DOWNLOADED = 11, MM_OMA_SESSION_STATE_PRL_DOWNLOADED = 12, MM_OMA_SESSION_STATE_MIP_PROFILE_DOWNLOADED = 13, MM_OMA_SESSION_STATE_COMPLETED = 20, } MMOmaSessionState; /** * MMOmaSessionStateFailedReason: * @MM_OMA_SESSION_STATE_FAILED_REASON_UNKNOWN: No reason or unknown. * @MM_OMA_SESSION_STATE_FAILED_REASON_NETWORK_UNAVAILABLE: Network unavailable. * @MM_OMA_SESSION_STATE_FAILED_REASON_SERVER_UNAVAILABLE: Server unavailable. * @MM_OMA_SESSION_STATE_FAILED_REASON_AUTHENTICATION_FAILED: Authentication failed. * @MM_OMA_SESSION_STATE_FAILED_REASON_MAX_RETRY_EXCEEDED: Maximum retries exceeded. * @MM_OMA_SESSION_STATE_FAILED_REASON_SESSION_CANCELLED: Session cancelled. * * Reason of failure in the OMA device management session. * * Since: 1.2 */ typedef enum { /*< underscore_name=mm_oma_session_state_failed_reason >*/ MM_OMA_SESSION_STATE_FAILED_REASON_UNKNOWN = 0, MM_OMA_SESSION_STATE_FAILED_REASON_NETWORK_UNAVAILABLE = 1, MM_OMA_SESSION_STATE_FAILED_REASON_SERVER_UNAVAILABLE = 2, MM_OMA_SESSION_STATE_FAILED_REASON_AUTHENTICATION_FAILED = 3, MM_OMA_SESSION_STATE_FAILED_REASON_MAX_RETRY_EXCEEDED = 4, MM_OMA_SESSION_STATE_FAILED_REASON_SESSION_CANCELLED = 5 } MMOmaSessionStateFailedReason; /** * MMCallState: * @MM_CALL_STATE_UNKNOWN: default state for a new outgoing call. * @MM_CALL_STATE_DIALING: outgoing call started. Wait for free channel. * @MM_CALL_STATE_RINGING_IN: incoming call is waiting for an answer. * @MM_CALL_STATE_RINGING_OUT: outgoing call attached to GSM network, waiting for an answer. * @MM_CALL_STATE_ACTIVE: call is active between two peers. * @MM_CALL_STATE_HELD: held call (by +CHLD AT command). * @MM_CALL_STATE_WAITING: waiting call (by +CCWA AT command). * @MM_CALL_STATE_TERMINATED: call is terminated. * * State of Call. * * Since: 1.6 */ typedef enum { /*< underscore_name=mm_call_state >*/ MM_CALL_STATE_UNKNOWN = 0, MM_CALL_STATE_DIALING = 1, MM_CALL_STATE_RINGING_OUT = 2, MM_CALL_STATE_RINGING_IN = 3, MM_CALL_STATE_ACTIVE = 4, MM_CALL_STATE_HELD = 5, MM_CALL_STATE_WAITING = 6, MM_CALL_STATE_TERMINATED = 7 } MMCallState; /** * MMCallStateReason: * @MM_CALL_STATE_REASON_UNKNOWN: Default value for a new outgoing call. * @MM_CALL_STATE_REASON_OUTGOING_STARTED: Outgoing call is started. * @MM_CALL_STATE_REASON_INCOMING_NEW: Received a new incoming call. * @MM_CALL_STATE_REASON_ACCEPTED: Dialing or Ringing call is accepted. * @MM_CALL_STATE_REASON_TERMINATED: Call is correctly terminated. * @MM_CALL_STATE_REASON_REFUSED_OR_BUSY: Remote peer is busy or refused call. * @MM_CALL_STATE_REASON_ERROR: Wrong number or generic network error. * @MM_CALL_STATE_REASON_AUDIO_SETUP_FAILED: Error setting up audio channel. Since 1.10. * @MM_CALL_STATE_REASON_TRANSFERRED: Call has been transferred. Since 1.12. * @MM_CALL_STATE_REASON_DEFLECTED: Call has been deflected to a new number. Since 1.12. * * Reason for the state change in the call. * * Since: 1.6 */ typedef enum { /*< underscore_name=mm_call_state_reason >*/ MM_CALL_STATE_REASON_UNKNOWN = 0, MM_CALL_STATE_REASON_OUTGOING_STARTED = 1, MM_CALL_STATE_REASON_INCOMING_NEW = 2, MM_CALL_STATE_REASON_ACCEPTED = 3, MM_CALL_STATE_REASON_TERMINATED = 4, MM_CALL_STATE_REASON_REFUSED_OR_BUSY = 5, MM_CALL_STATE_REASON_ERROR = 6, MM_CALL_STATE_REASON_AUDIO_SETUP_FAILED = 7, MM_CALL_STATE_REASON_TRANSFERRED = 8, MM_CALL_STATE_REASON_DEFLECTED = 9, } MMCallStateReason; /** * MMCallDirection: * @MM_CALL_DIRECTION_UNKNOWN: unknown. * @MM_CALL_DIRECTION_INCOMING: call from network. * @MM_CALL_DIRECTION_OUTGOING: call to network. * * Direction of the call. * * Since: 1.6 */ typedef enum { /*< underscore_name=mm_call_direction >*/ MM_CALL_DIRECTION_UNKNOWN = 0, MM_CALL_DIRECTION_INCOMING = 1, MM_CALL_DIRECTION_OUTGOING = 2 } MMCallDirection; /** * MMModemFirmwareUpdateMethod: * @MM_MODEM_FIRMWARE_UPDATE_METHOD_NONE: No method specified. * @MM_MODEM_FIRMWARE_UPDATE_METHOD_FASTBOOT: Device supports fastboot-based update. * @MM_MODEM_FIRMWARE_UPDATE_METHOD_QMI_PDC: Device supports QMI PDC based update. * @MM_MODEM_FIRMWARE_UPDATE_METHOD_MBIM_QDU: Device supports MBIM QDU based update. Since 1.18. * @MM_MODEM_FIRMWARE_UPDATE_METHOD_FIREHOSE: Device supports Firehose based update. Since 1.18. * @MM_MODEM_FIRMWARE_UPDATE_METHOD_SAHARA: Device supports Sahara protocol. Usually used in combination with Firehose. Since 1.20. * * Type of firmware update method supported by the module. * * Since: 1.10 */ typedef enum { /*< underscore_name=mm_modem_firmware_update_method >*/ MM_MODEM_FIRMWARE_UPDATE_METHOD_NONE = 0, MM_MODEM_FIRMWARE_UPDATE_METHOD_FASTBOOT = 1 << 0, MM_MODEM_FIRMWARE_UPDATE_METHOD_QMI_PDC = 1 << 1, MM_MODEM_FIRMWARE_UPDATE_METHOD_MBIM_QDU = 1 << 2, MM_MODEM_FIRMWARE_UPDATE_METHOD_FIREHOSE = 1 << 3, MM_MODEM_FIRMWARE_UPDATE_METHOD_SAHARA = 1 << 4, } MMModemFirmwareUpdateMethod; /** * MMBearerMultiplexSupport: * @MM_BEARER_MULTIPLEX_SUPPORT_UNKNOWN: Unknown. * @MM_BEARER_MULTIPLEX_SUPPORT_NONE: No multiplex support should be used. * @MM_BEARER_MULTIPLEX_SUPPORT_REQUESTED: If available, multiplex support should be used. * @MM_BEARER_MULTIPLEX_SUPPORT_REQUIRED: Multiplex support must be used or otherwise the connection attempt will fail. * * Multiplex support requested by the user. * * Since: 1.18 */ typedef enum { /*< underscore_name=mm_bearer_multiplex_support >*/ MM_BEARER_MULTIPLEX_SUPPORT_UNKNOWN = 0, MM_BEARER_MULTIPLEX_SUPPORT_NONE = 1, MM_BEARER_MULTIPLEX_SUPPORT_REQUESTED = 2, MM_BEARER_MULTIPLEX_SUPPORT_REQUIRED = 3, } MMBearerMultiplexSupport; /** * MMBearerApnType: * @MM_BEARER_APN_TYPE_NONE: Unknown or unsupported. * @MM_BEARER_APN_TYPE_INITIAL: APN used for the initial attach procedure. * @MM_BEARER_APN_TYPE_DEFAULT: Default connection APN providing access to the Internet. * @MM_BEARER_APN_TYPE_IMS: APN providing access to IMS services. * @MM_BEARER_APN_TYPE_MMS: APN providing access to MMS services. * @MM_BEARER_APN_TYPE_MANAGEMENT: APN providing access to over-the-air device management procedures. * @MM_BEARER_APN_TYPE_VOICE: APN providing access to voice-over-IP services. * @MM_BEARER_APN_TYPE_EMERGENCY: APN providing access to emergency services. * @MM_BEARER_APN_TYPE_PRIVATE: APN providing access to private networks. * @MM_BEARER_APN_TYPE_PURCHASE: APN providing access to over-the-air activation sites. Since 1.20. * @MM_BEARER_APN_TYPE_VIDEO_SHARE: APN providing access to video sharing service. Since 1.20. * @MM_BEARER_APN_TYPE_LOCAL: APN providing access to a local connection with the device. Since 1.20. * @MM_BEARER_APN_TYPE_APP: APN providing access to certain applications allowed by mobile operators. Since 1.20. * @MM_BEARER_APN_TYPE_XCAP: APN providing access to XCAP provisioning on IMS services. Since 1.20. * @MM_BEARER_APN_TYPE_TETHERING: APN providing access to mobile hotspot tethering. Since 1.20. * * Purpose of the APN used in a given Bearer. * * This information may be stored in the device configuration (e.g. if carrier * specific configurations have been enabled for the SIM in use), or provided * explicitly by the user. * * If the mask of types includes %MM_BEARER_APN_TYPE_DEFAULT, it is expected * that the connection manager will include a default route through the specific * bearer connection to the public Internet. * * For any other mask type, it is expected that the connection manager will * not setup a default route and will therefore require additional custom * routing rules to provide access to the different services. E.g. a bearer * connected with %MM_BEARER_APN_TYPE_MMS will probably require an explicit * additional route in the host to access the MMSC server at the address * specified by the operator. If this address relies on a domain name instead * of a fixed IP address, the name resolution should be performed using the * DNS servers specified in the corresponding bearer connection settings. * * If not explicitly specified during a connection attempt, the connection * manager should be free to treat it with its own logic. E.g. a good default * could be to treat the first connection as %MM_BEARER_APN_TYPE_DEFAULT (with * a default route) and any other additional connection as * %MM_BEARER_APN_TYPE_PRIVATE (without a default route). * * Since: 1.18 */ typedef enum { /*< underscore_name=mm_bearer_apn_type >*/ MM_BEARER_APN_TYPE_NONE = 0, MM_BEARER_APN_TYPE_INITIAL = 1 << 0, MM_BEARER_APN_TYPE_DEFAULT = 1 << 1, MM_BEARER_APN_TYPE_IMS = 1 << 2, MM_BEARER_APN_TYPE_MMS = 1 << 3, MM_BEARER_APN_TYPE_MANAGEMENT = 1 << 4, MM_BEARER_APN_TYPE_VOICE = 1 << 5, MM_BEARER_APN_TYPE_EMERGENCY = 1 << 6, MM_BEARER_APN_TYPE_PRIVATE = 1 << 7, MM_BEARER_APN_TYPE_PURCHASE = 1 << 8, MM_BEARER_APN_TYPE_VIDEO_SHARE = 1 << 9, MM_BEARER_APN_TYPE_LOCAL = 1 << 10, MM_BEARER_APN_TYPE_APP = 1 << 11, MM_BEARER_APN_TYPE_XCAP = 1 << 12, MM_BEARER_APN_TYPE_TETHERING = 1 << 13, } MMBearerApnType; /** * MMModem3gppPacketServiceState: * @MM_MODEM_3GPP_PACKET_SERVICE_STATE_UNKNOWN: Unknown. * @MM_MODEM_3GPP_PACKET_SERVICE_STATE_DETACHED: Detached. * @MM_MODEM_3GPP_PACKET_SERVICE_STATE_ATTACHED: Attached. * * The packet domain service state. * * Since: 1.20 */ typedef enum { /*< underscore_name=mm_modem_3gpp_packet_service_state >*/ MM_MODEM_3GPP_PACKET_SERVICE_STATE_UNKNOWN = 0, MM_MODEM_3GPP_PACKET_SERVICE_STATE_DETACHED = 1, MM_MODEM_3GPP_PACKET_SERVICE_STATE_ATTACHED = 2, } MMModem3gppPacketServiceState; /** * MMSimType: * @MM_SIM_TYPE_UNKNOWN: SIM type is not known. * @MM_SIM_TYPE_PHYSICAL: SIM is a pysical SIM. * @MM_SIM_TYPE_ESIM: SIM is a ESIM. * * SIM type indicating whether ESIM or not * * Since: 1.20 */ typedef enum { /*< underscore_name=mm_sim_type >*/ MM_SIM_TYPE_UNKNOWN = 0, MM_SIM_TYPE_PHYSICAL = 1, MM_SIM_TYPE_ESIM = 2, } MMSimType; /** * MMSimEsimStatus: * @MM_SIM_ESIM_STATUS_UNKNOWN: ESIM status unknown. * @MM_SIM_ESIM_STATUS_NO_PROFILES: ESIM with no profiles. * @MM_SIM_ESIM_STATUS_WITH_PROFILES: ESIM with profiles. * * Status of the profiles for ESIM * * Since: 1.20 */ typedef enum { /*< underscore_name=mm_sim_esim_status >*/ MM_SIM_ESIM_STATUS_UNKNOWN = 0, MM_SIM_ESIM_STATUS_NO_PROFILES = 1, MM_SIM_ESIM_STATUS_WITH_PROFILES = 2, } MMSimEsimStatus; /** * MMSimRemovability: * @MM_SIM_REMOVABILITY_UNKNOWN: SIM removability not known. * @MM_SIM_REMOVABILITY_REMOVABLE: SIM is a removable SIM. * @MM_SIM_REMOVABILITY_NOT_REMOVABLE: SIM is not a removable SIM. * * Respresents SIM removability of the current SIM. * * Since: 1.20 */ typedef enum { /*< underscore_name=mm_sim_removability >*/ MM_SIM_REMOVABILITY_UNKNOWN = 0, MM_SIM_REMOVABILITY_REMOVABLE = 1, MM_SIM_REMOVABILITY_NOT_REMOVABLE = 2, } MMSimRemovability; /** * MMModem3gppMicoMode: * @MM_MODEM_3GPP_MICO_MODE_UNKNOWN: Unknown or not specified. * @MM_MODEM_3GPP_MICO_MODE_UNSUPPORTED: Unsupported. * @MM_MODEM_3GPP_MICO_MODE_DISABLED: Disabled. * @MM_MODEM_3GPP_MICO_MODE_ENABLED: Enabled. * * Mobile Initiated Connection Only (MICO) mode. * * This is a 5G-specific registration setting. * * Since: 1.20 */ typedef enum { /*< underscore_name=mm_modem_3gpp_mico_mode >*/ MM_MODEM_3GPP_MICO_MODE_UNKNOWN = 0, MM_MODEM_3GPP_MICO_MODE_UNSUPPORTED = 1, MM_MODEM_3GPP_MICO_MODE_DISABLED = 2, MM_MODEM_3GPP_MICO_MODE_ENABLED = 3, } MMModem3gppMicoMode; /** * MMModem3gppDrxCycle: * @MM_MODEM_3GPP_DRX_CYCLE_UNKNOWN: Unknown or not specified. * @MM_MODEM_3GPP_DRX_CYCLE_UNSUPPORTED: Unsupported. * @MM_MODEM_3GPP_DRX_CYCLE_32: DRX cycle T=32. * @MM_MODEM_3GPP_DRX_CYCLE_64: DRX cycle T=64. * @MM_MODEM_3GPP_DRX_CYCLE_128: DRX cycle T=128. * @MM_MODEM_3GPP_DRX_CYCLE_256: DRX cycle T=256. * * DRX cycle. * * This is a 5G-specific registration setting. * * Since: 1.20 */ typedef enum { /*< underscore_name=mm_modem_3gpp_drx_cycle >*/ MM_MODEM_3GPP_DRX_CYCLE_UNKNOWN = 0, MM_MODEM_3GPP_DRX_CYCLE_UNSUPPORTED = 1, MM_MODEM_3GPP_DRX_CYCLE_32 = 2, MM_MODEM_3GPP_DRX_CYCLE_64 = 3, MM_MODEM_3GPP_DRX_CYCLE_128 = 4, MM_MODEM_3GPP_DRX_CYCLE_256 = 5, } MMModem3gppDrxCycle; /** * MMBearerAccessTypePreference: * @MM_BEARER_ACCESS_TYPE_PREFERENCE_NONE: No access type preference, or unknown. * @MM_BEARER_ACCESS_TYPE_PREFERENCE_3GPP_ONLY: 3GPP access type only. * @MM_BEARER_ACCESS_TYPE_PREFERENCE_3GPP_PREFERRED: All access types allowed but 3GPP preferred. * @MM_BEARER_ACCESS_TYPE_PREFERENCE_NON_3GPP_ONLY: Non-3GPP access type only. * * 5G network access type preference, such as 3GPP (NR, E-UTRA) or * non-3GPP (untrusted WiFi, trusted WiFi, wireline). * * Since: 1.20 */ typedef enum { /*< underscore_name=mm_bearer_access_type_preference >*/ MM_BEARER_ACCESS_TYPE_PREFERENCE_NONE = 0, MM_BEARER_ACCESS_TYPE_PREFERENCE_3GPP_ONLY = 1, MM_BEARER_ACCESS_TYPE_PREFERENCE_3GPP_PREFERRED = 2, MM_BEARER_ACCESS_TYPE_PREFERENCE_NON_3GPP_ONLY = 3, } MMBearerAccessTypePreference; /** * MMBearerRoamingAllowance: * @MM_BEARER_ROAMING_ALLOWANCE_NONE: No explicit roaming allowance rules. * @MM_BEARER_ROAMING_ALLOWANCE_HOME: Home network allowed. * @MM_BEARER_ROAMING_ALLOWANCE_PARTNER: Partner network allowed. * @MM_BEARER_ROAMING_ALLOWANCE_NON_PARTNER: Non-parter network allowed. * * Bitmask specifying roaming allowance rules for different network types. * * Since: 1.20 */ typedef enum { /*< underscore_name=mm_bearer_roaming_allowance >*/ MM_BEARER_ROAMING_ALLOWANCE_NONE = 0, MM_BEARER_ROAMING_ALLOWANCE_HOME = 1 << 0, MM_BEARER_ROAMING_ALLOWANCE_PARTNER = 1 << 1, MM_BEARER_ROAMING_ALLOWANCE_NON_PARTNER = 1 << 2, } MMBearerRoamingAllowance; /** * MMBearerProfileSource: * @MM_BEARER_PROFILE_SOURCE_UNKNOWN: Unknown. * @MM_BEARER_PROFILE_SOURCE_ADMIN: Profile created by an enterprise IT admin from the OS. * @MM_BEARER_PROFILE_SOURCE_USER: Profile created by the user. * @MM_BEARER_PROFILE_SOURCE_OPERATOR: Profile created by the operator through OMA-DM or similar. * @MM_BEARER_PROFILE_SOURCE_MODEM: Profile created by the OEM that was included with the modem firmware. * @MM_BEARER_PROFILE_SOURCE_DEVICE: Profile created by the OS APN database. * * Value specifying how a given context was created, mostly for informative purposes. * * Since: 1.20 */ typedef enum { /*< underscore_name=mm_bearer_profile_source >*/ MM_BEARER_PROFILE_SOURCE_UNKNOWN = 0, MM_BEARER_PROFILE_SOURCE_ADMIN = 1, MM_BEARER_PROFILE_SOURCE_USER = 2, MM_BEARER_PROFILE_SOURCE_OPERATOR = 3, MM_BEARER_PROFILE_SOURCE_MODEM = 4, MM_BEARER_PROFILE_SOURCE_DEVICE = 5, } MMBearerProfileSource; /*****************************************************************************/ /* 'RF' enums */ /** * MMServingCellType: * @MM_SERVING_CELL_TYPE_UNKNOWN: Unknown. * @MM_SERVING_CELL_TYPE_PCELL: In LTE CA, this refers to the primary cell. In ENDC case, this is the primary cell of MCS. In 5G SA, this refers to primary cell of NR. * @MM_SERVING_CELL_TYPE_SCELL: In LTE CA, this refers to the secondary cell. In ENDC case, this is the secondary cell of MCS. In 5G SA, this refers to secondary cell of NR. * @MM_SERVING_CELL_TYPE_PSCELL: Refers to primary cell of secondary cell group (SCG). * @MM_SERVING_CELL_TYPE_SSCELL: Refers to secondary cell of secondary cell group (SCG). * @MM_SERVING_CELL_TYPE_INVALID: Indicates that the radio is off, all other values are invalid. * * Indicates the frequency information provided belongs to which serving cell. * * Since: 1.22 */ typedef enum { /*< underscore_name=mm_serving_cell_type >*/ MM_SERVING_CELL_TYPE_UNKNOWN = 0, MM_SERVING_CELL_TYPE_PCELL = 1, MM_SERVING_CELL_TYPE_SCELL = 2, MM_SERVING_CELL_TYPE_PSCELL = 3, MM_SERVING_CELL_TYPE_SSCELL = 4, MM_SERVING_CELL_TYPE_INVALID = 0xFFFFFFFF } MMServingCellType; #endif /* _MODEMMANAGER_ENUMS_H_ */ ModemManager-1.23.4-dev/include/ModemManager-errors.h000066400000000000000000001502041456466623000223710ustar00rootroot00000000000000/* -*- 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) 2011 Red Hat, Inc. * Copyright (C) 2011 Google, Inc. */ #ifndef _MODEMMANAGER_ERRORS_H_ #define _MODEMMANAGER_ERRORS_H_ #if !defined (__MODEM_MANAGER_H_INSIDE__) #error "Only can be included directly." #endif #include /** * SECTION:mm-errors * @short_description: Common errors in the API. * * This section defines errors that may be reported when using methods from the * ModemManager interface. **/ /******************************************************************************/ /** * MM_CORE_ERROR_DBUS_PREFIX: * * DBus prefix for #MMCoreError errors. * * Since: 1.0 */ #define MM_CORE_ERROR_DBUS_PREFIX MM_DBUS_ERROR_PREFIX ".Core" /** * MM_MOBILE_EQUIPMENT_ERROR_DBUS_PREFIX: * * DBus prefix for #MMMobileEquipmentError errors. * * Since: 1.0 */ #define MM_MOBILE_EQUIPMENT_ERROR_DBUS_PREFIX MM_DBUS_ERROR_PREFIX ".MobileEquipment" /** * MM_CONNECTION_ERROR_DBUS_PREFIX: * * DBus prefix for #MMConnectionError errors. * * Since: 1.0 */ #define MM_CONNECTION_ERROR_DBUS_PREFIX MM_DBUS_ERROR_PREFIX ".Connection" /** * MM_SERIAL_ERROR_DBUS_PREFIX: * * DBus prefix for #MMSerialError errors. * * Since: 1.0 */ #define MM_SERIAL_ERROR_DBUS_PREFIX MM_DBUS_ERROR_PREFIX ".Serial" /** * MM_MESSAGE_ERROR_DBUS_PREFIX: * * DBus prefix for #MMMessageError errors. * * Since: 1.0 */ #define MM_MESSAGE_ERROR_DBUS_PREFIX MM_DBUS_ERROR_PREFIX ".Message" /** * MM_CDMA_ACTIVATION_ERROR_DBUS_PREFIX: * * DBus prefix for #MMCdmaActivationError errors. * * Since: 1.0 */ #define MM_CDMA_ACTIVATION_ERROR_DBUS_PREFIX MM_DBUS_ERROR_PREFIX ".CdmaActivation" /** * MM_CARRIER_LOCK_ERROR_DBUS_PREFIX: * * DBus prefix for #MMCarrierLockError errors. * * Since: 1.24 */ #define MM_CARRIER_LOCK_ERROR_DBUS_PREFIX MM_DBUS_ERROR_PREFIX ".CarrierLock" /******************************************************************************/ /** * MMCoreError: * @MM_CORE_ERROR_FAILED: Operation failed. * @MM_CORE_ERROR_CANCELLED: Operation was cancelled. * @MM_CORE_ERROR_ABORTED: Operation was aborted. * @MM_CORE_ERROR_UNSUPPORTED: Operation is not supported. * @MM_CORE_ERROR_NO_PLUGINS: Cannot operate without valid plugins. * @MM_CORE_ERROR_UNAUTHORIZED: Authorization is required to perform the operation. * @MM_CORE_ERROR_INVALID_ARGS: Invalid arguments given. * @MM_CORE_ERROR_IN_PROGRESS: Operation is already in progress. * @MM_CORE_ERROR_WRONG_STATE: Operation cannot be executed in the current state. * @MM_CORE_ERROR_CONNECTED: Operation cannot be executed while being connected. * @MM_CORE_ERROR_TOO_MANY: Too many items. * @MM_CORE_ERROR_NOT_FOUND: Item not found. * @MM_CORE_ERROR_RETRY: Operation cannot yet be performed, retry later. * @MM_CORE_ERROR_EXISTS: Item already exists. * @MM_CORE_ERROR_WRONG_SIM_STATE: Operation cannot be executed in the current SIM state. Since 1.20. * @MM_CORE_ERROR_RESET_AND_RETRY: Operation cannot yet be performed, reset the retry count and retry later. Since 1.22. * @MM_CORE_ERROR_TIMEOUT: Operation timed out. Since 1.24. * @MM_CORE_ERROR_PROTOCOL: Operation failed due to an underlying control protocol error. Since 1.24. * @MM_CORE_ERROR_THROTTLED: Operation was attempted too many times. Since 1.24. * * Common errors that may be reported by ModemManager. * * Since: 1.0 */ typedef enum { /*< underscore_name=mm_core_error >*/ MM_CORE_ERROR_FAILED = 0, /*< nick=Failed >*/ MM_CORE_ERROR_CANCELLED = 1, /*< nick=Cancelled >*/ MM_CORE_ERROR_ABORTED = 2, /*< nick=Aborted >*/ MM_CORE_ERROR_UNSUPPORTED = 3, /*< nick=Unsupported >*/ MM_CORE_ERROR_NO_PLUGINS = 4, /*< nick=NoPlugins >*/ MM_CORE_ERROR_UNAUTHORIZED = 5, /*< nick=Unauthorized >*/ MM_CORE_ERROR_INVALID_ARGS = 6, /*< nick=InvalidArgs >*/ MM_CORE_ERROR_IN_PROGRESS = 7, /*< nick=InProgress >*/ MM_CORE_ERROR_WRONG_STATE = 8, /*< nick=WrongState >*/ MM_CORE_ERROR_CONNECTED = 9, /*< nick=Connected >*/ MM_CORE_ERROR_TOO_MANY = 10, /*< nick=TooMany >*/ MM_CORE_ERROR_NOT_FOUND = 11, /*< nick=NotFound >*/ MM_CORE_ERROR_RETRY = 12, /*< nick=Retry >*/ MM_CORE_ERROR_EXISTS = 13, /*< nick=Exists >*/ MM_CORE_ERROR_WRONG_SIM_STATE = 14, /*< nick=WrongSimState >*/ MM_CORE_ERROR_RESET_AND_RETRY = 15, /*< nick=ResetRetry >*/ MM_CORE_ERROR_TIMEOUT = 16, /*< nick=Timeout >*/ MM_CORE_ERROR_PROTOCOL = 17, /*< nick=Protocol >*/ MM_CORE_ERROR_THROTTLED = 18, /*< nick=Throttled >*/ } MMCoreError; /** * MMMobileEquipmentError: * @MM_MOBILE_EQUIPMENT_ERROR_PHONE_FAILURE: Phone failure. * @MM_MOBILE_EQUIPMENT_ERROR_NO_CONNECTION: No connection to phone. * @MM_MOBILE_EQUIPMENT_ERROR_LINK_RESERVED: Phone-adaptor link reserved. * @MM_MOBILE_EQUIPMENT_ERROR_NOT_ALLOWED: Operation not allowed. * @MM_MOBILE_EQUIPMENT_ERROR_NOT_SUPPORTED: Operation not supported. * @MM_MOBILE_EQUIPMENT_ERROR_PH_SIM_PIN: PH-SIM PIN required. * @MM_MOBILE_EQUIPMENT_ERROR_PH_FSIM_PIN: PH-FSIM PIN required. * @MM_MOBILE_EQUIPMENT_ERROR_PH_FSIM_PUK: PH-FSIM PUK required. * @MM_MOBILE_EQUIPMENT_ERROR_SIM_NOT_INSERTED: SIM not inserted. * @MM_MOBILE_EQUIPMENT_ERROR_SIM_PIN: SIM PIN required. * @MM_MOBILE_EQUIPMENT_ERROR_SIM_PUK: SIM PUK required. * @MM_MOBILE_EQUIPMENT_ERROR_SIM_FAILURE: SIM failure. * @MM_MOBILE_EQUIPMENT_ERROR_SIM_BUSY: SIM busy. * @MM_MOBILE_EQUIPMENT_ERROR_SIM_WRONG: SIM wrong. * @MM_MOBILE_EQUIPMENT_ERROR_INCORRECT_PASSWORD: Incorrect password. * @MM_MOBILE_EQUIPMENT_ERROR_SIM_PIN2: SIM PIN2 required. * @MM_MOBILE_EQUIPMENT_ERROR_SIM_PUK2: SIM PUK2 required. * @MM_MOBILE_EQUIPMENT_ERROR_MEMORY_FULL: Memory full. * @MM_MOBILE_EQUIPMENT_ERROR_INVALID_INDEX: Invalid index. * @MM_MOBILE_EQUIPMENT_ERROR_NOT_FOUND: Not found. * @MM_MOBILE_EQUIPMENT_ERROR_MEMORY_FAILURE: Memory failure. * @MM_MOBILE_EQUIPMENT_ERROR_TEXT_TOO_LONG: Text string too long. * @MM_MOBILE_EQUIPMENT_ERROR_INVALID_CHARS: Invalid characters in text string. * @MM_MOBILE_EQUIPMENT_ERROR_DIAL_STRING_TOO_LONG: Dial string too long. * @MM_MOBILE_EQUIPMENT_ERROR_DIAL_STRING_INVALID: Invalid characters in dial string. * @MM_MOBILE_EQUIPMENT_ERROR_NO_NETWORK: No network service. * @MM_MOBILE_EQUIPMENT_ERROR_NETWORK_TIMEOUT: Network timeout. * @MM_MOBILE_EQUIPMENT_ERROR_NETWORK_NOT_ALLOWED: Network not allowed - Emergency calls only. * @MM_MOBILE_EQUIPMENT_ERROR_NETWORK_PIN: Network personalisation PIN required. * @MM_MOBILE_EQUIPMENT_ERROR_NETWORK_PUK: Network personalisation PUK required. * @MM_MOBILE_EQUIPMENT_ERROR_NETWORK_SUBSET_PIN: Network subset personalisation PIN required. * @MM_MOBILE_EQUIPMENT_ERROR_NETWORK_SUBSET_PUK: Network subset personalisation PUK required. * @MM_MOBILE_EQUIPMENT_ERROR_SERVICE_PIN: Service provider personalisation PIN required. * @MM_MOBILE_EQUIPMENT_ERROR_SERVICE_PUK: Service provider personalisation PUK required. * @MM_MOBILE_EQUIPMENT_ERROR_CORP_PIN: Corporate personalisation PIN required. * @MM_MOBILE_EQUIPMENT_ERROR_CORP_PUK: Corporate personalisation PUK required. * @MM_MOBILE_EQUIPMENT_ERROR_HIDDEN_KEY_REQUIRED: Hidden key required. Since: 1.8. * @MM_MOBILE_EQUIPMENT_ERROR_EAP_METHOD_NOT_SUPPORTED: EAP method not supported. Since: 1.8. * @MM_MOBILE_EQUIPMENT_ERROR_INCORRECT_PARAMETERS: Incorrect parameters. Since: 1.8. * @MM_MOBILE_EQUIPMENT_ERROR_COMMAND_DISABLED: Command implemented but currently disabled. Since 1.18. * @MM_MOBILE_EQUIPMENT_ERROR_COMMAND_ABORTED: Command aborted by user. Since 1.18. * @MM_MOBILE_EQUIPMENT_ERROR_NOT_ATTACHED_RESTRICTED: Not attached to network due to MT functionality restrictions. Since 1.18. * @MM_MOBILE_EQUIPMENT_ERROR_NOT_ALLOWED_EMERGENCY_ONLY: Modem not allowed, MT restricted to emergency calls only. Since 1.18. * @MM_MOBILE_EQUIPMENT_ERROR_NOT_ALLOWED_RESTRICTED: Operation not allowed because of MT functionality restrictions. Since 1.18. * @MM_MOBILE_EQUIPMENT_ERROR_FIXED_DIAL_NUMBER_ONLY: Fixed dial number only allowed; called number is not a fixed dial number. Since 1.18. * @MM_MOBILE_EQUIPMENT_ERROR_TEMPORARILY_OUT_OF_SERVICE: Temporarily out of service due to other MT usage. Since 1.18. * @MM_MOBILE_EQUIPMENT_ERROR_LANGUAGE_OR_ALPHABET_NOT_SUPPORTED: Language or alphabet not supported. Since 1.18. * @MM_MOBILE_EQUIPMENT_ERROR_UNEXPECTED_DATA_VALUE: Unexpected data value. Since 1.18. * @MM_MOBILE_EQUIPMENT_ERROR_SYSTEM_FAILURE: System failure. Since 1.18. * @MM_MOBILE_EQUIPMENT_ERROR_DATA_MISSING: Data missing. Since 1.18. * @MM_MOBILE_EQUIPMENT_ERROR_CALL_BARRED: Call barred. Since 1.18. * @MM_MOBILE_EQUIPMENT_ERROR_MESSAGE_WAITING_INDICATION_SUBSCRIPTION_FAILURE: Message waiting indication subscription failure. Since 1.18. * @MM_MOBILE_EQUIPMENT_ERROR_UNKNOWN: Unknown. * @MM_MOBILE_EQUIPMENT_ERROR_IMSI_UNKNOWN_IN_HSS: IMSI unknown in HLR (CS, GPRS, UMTS); IMSI unknown in HSS (EPS). Since 1.18. * @MM_MOBILE_EQUIPMENT_ERROR_ILLEGAL_UE: Illegal MS (CS, GPRS, UMTS); Illegal UE (EPS, 5GS). Since 1.18. * @MM_MOBILE_EQUIPMENT_ERROR_IMSI_UNKNOWN_IN_VLR: IMSI unknown in VLR. Since 1.18. * @MM_MOBILE_EQUIPMENT_ERROR_IMEI_NOT_ACCEPTED: IMEI not accepted (CS, GPRS, UMTS, EPS); PEI not accepted (5GS). Since 1.18. * @MM_MOBILE_EQUIPMENT_ERROR_ILLEGAL_ME: Illegal ME (CS, GPRS, UMTS, EPS, 5GS). Since 1.18. * @MM_MOBILE_EQUIPMENT_ERROR_PS_SERVICES_NOT_ALLOWED: GPRS services not allowed (CS, GPRS, UMTS); EPS services not allowed (EPS); 5GS services not allowed (5GS). Since 1.18. * @MM_MOBILE_EQUIPMENT_ERROR_PS_AND_NON_PS_SERVICES_NOT_ALLOWED: GPRS and non-GPRS services not allowed (CS, GPRS, UMTS); EPS and non-EPS services not allowed (EPS). Since 1.18. * @MM_MOBILE_EQUIPMENT_ERROR_UE_IDENTITY_NOT_DERIVED_FROM_NETWORK: MS identity cannot be derived from network (CS, GPRS, UMTS; UE identity cannot be derived from network (EPS, 5GS). Since 1.18. * @MM_MOBILE_EQUIPMENT_ERROR_IMPLICITLY_DETACHED: Implicitly detached (CS, GPRS, UMTS, EPS); implicitly degistered (5GS). Since 1.18. * @MM_MOBILE_EQUIPMENT_ERROR_PLMN_NOT_ALLOWED: PLMN not allowed (CS, GPRS, UMTS, EPS, 5GS). Since 1.18. * @MM_MOBILE_EQUIPMENT_ERROR_AREA_NOT_ALLOWED: Location area not allowed (CS, GPRS, UMTS); Tracking area not allowed (EPS, 5GS). Since 1.18. * @MM_MOBILE_EQUIPMENT_ERROR_ROAMING_NOT_ALLOWED_IN_AREA: Roaming not allowed in this location area (CS, GPRS, UMTS); Roaming not allowed in this tracking area (EPS, 5GS). Since 1.18. * @MM_MOBILE_EQUIPMENT_ERROR_PS_SERVICES_NOT_ALLOWED_IN_PLMN: GPRS services not allowed in this PLMN (CS, GPRS, UMTS); EPS services not allowed in this PLMN (EPS). Since 1.18. * @MM_MOBILE_EQUIPMENT_ERROR_NO_CELLS_IN_AREA: No suitable cells in this location area (CS, GPRS, UMTS); no suitable cells in this tracking area (EPS, 5GS). Since 1.18. * @MM_MOBILE_EQUIPMENT_ERROR_MSC_TEMPORARILY_NOT_REACHABLE: MSC temporarily not reachable (CS, GPRS, UMTS, EPS). Since 1.18. * @MM_MOBILE_EQUIPMENT_ERROR_NETWORK_FAILURE_ATTACH: Network failure during attach (CS, GPRS, UMTS, EPS). Since 1.18. * @MM_MOBILE_EQUIPMENT_ERROR_CS_DOMAIN_UNAVAILABLE: CS domain not available (EPS). Since 1.18. * @MM_MOBILE_EQUIPMENT_ERROR_ESM_FAILURE: ESM failure (EPS). Since 1.18. * @MM_MOBILE_EQUIPMENT_ERROR_CONGESTION: Congestion (CS, GPRS, UMTS, EPS, 5GS). Since 1.18. * @MM_MOBILE_EQUIPMENT_ERROR_MBMS_BEARER_CAPABILITIES_INSUFFICIENT_FOR_SERVICE: MBMS bearer capabilities insufficient for service (GPRS, UMTS). Since 1.18. * @MM_MOBILE_EQUIPMENT_ERROR_NOT_AUTHORIZED_FOR_CSG: Not authorized for this CSG (CS, GPRS, UMTS, EPS). Since: 1.18. * @MM_MOBILE_EQUIPMENT_ERROR_INSUFFICIENT_RESOURCES: Insufficient resources (GPRS, UMTS, EPS, 5GS). Since 1.18. * @MM_MOBILE_EQUIPMENT_ERROR_MISSING_OR_UNKNOWN_APN: Missing or unknown APN (GPRS, UMTS, EPS, 5GS). Since 1.18. * @MM_MOBILE_EQUIPMENT_ERROR_UNKNOWN_PDP_ADDRESS_OR_TYPE: Unknown PDP address or PDP type (GPRS, UMTS, EPS, 5GS). Since: 1.18. * @MM_MOBILE_EQUIPMENT_ERROR_USER_AUTHENTICATION_FAILED: User authentication or authorization failed (GPRS, UMTS, EPS, 5GS). Since 1.18. * @MM_MOBILE_EQUIPMENT_ERROR_ACTIVATION_REJECTED_BY_GGSN_OR_GW: Activation rejected by GGSN, Serving GW or PDN GW (GPRS, UMTS); activation rejected by Serving GW or PDN GW (EPS). Since: 1.18. * @MM_MOBILE_EQUIPMENT_ERROR_ACTIVATION_REJECTED_UNSPECIFIED: Activation rejected, unspecified (GPRS, UMTS, EPS, 5GS). Since: 1.18. * @MM_MOBILE_EQUIPMENT_ERROR_SERVICE_OPTION_NOT_SUPPORTED: Service option not supported (CS, GPRS, UMTS, EPS, 5GS). Since 1.18. * @MM_MOBILE_EQUIPMENT_ERROR_SERVICE_OPTION_NOT_SUBSCRIBED: Requested service option not subscribed (CS, GPRS, UMTS, EPS, 5GS). Since 1.18. * @MM_MOBILE_EQUIPMENT_ERROR_SERVICE_OPTION_OUT_OF_ORDER: Service option temporarily out of order (CS, GPRS, UMTS, EPS). Since 1.18. * @MM_MOBILE_EQUIPMENT_ERROR_NSAPI_OR_PTI_ALREADY_IN_USE: NSAPI out of order (GPRS, UMTS); PTI out of order (EPS, 5GS). Since 1.18. * @MM_MOBILE_EQUIPMENT_ERROR_REGULAR_DEACTIVATION: Regular deactivation (GPRS, UMTS, EPS, 5GS). Since 1.18. * @MM_MOBILE_EQUIPMENT_ERROR_QOS_NOT_ACCEPTED: EPS Qos not accepted (EPS); 5GS QoS not accepted (5GS). Since 1.18. * @MM_MOBILE_EQUIPMENT_ERROR_CALL_CANNOT_BE_IDENTIFIED: Call cannot be identified (CS, GPRS, UMTS). Since 1.18. * @MM_MOBILE_EQUIPMENT_ERROR_CS_SERVICE_TEMPORARILY_UNAVAILABLE: CS service temporarily unavailable (EPS). Since 1.18. * @MM_MOBILE_EQUIPMENT_ERROR_FEATURE_NOT_SUPPORTED: Feature not supported (GPRS, UMTS). Since: 1.18. * @MM_MOBILE_EQUIPMENT_ERROR_SEMANTIC_ERROR_IN_TFT_OPERATION: Semantic error in TFT operation (GPRS, UMTS, EPS, 5GS). Since: 1.18. * @MM_MOBILE_EQUIPMENT_ERROR_SYNTACTICAL_ERROR_IN_TFT_OPERATION: Syntactical error in TFT operation (GPRS, UMTS, EPS, 5GS). Since: 1.18. * @MM_MOBILE_EQUIPMENT_ERROR_UNKNOWN_PDP_CONTEXT: Unknown PDP context (GPRS, UMTS, EPS, 5GS). Since: 1.18. * @MM_MOBILE_EQUIPMENT_ERROR_SEMANTIC_ERRORS_IN_PACKET_FILTER: Semantic errors in packet filter (GPRS, UMTS, EPS, 5GS). Since: 1.18. * @MM_MOBILE_EQUIPMENT_ERROR_SYNTACTICAL_ERROR_IN_PACKET_FILTER: Syntactical error in packet filter (GPRS, UMTS, EPS, 5GS). Since: 1.18. * @MM_MOBILE_EQUIPMENT_ERROR_PDP_CONTEXT_WITHOUT_TFT_ALREADY_ACTIVATED: PDP context without TFT already activated (GPRS, UMTS). Since: 1.18. * @MM_MOBILE_EQUIPMENT_ERROR_MULTICAST_GROUP_MEMBERSHIP_TIMEOUT: Multicast group membership timeout (GPRS, UMTS). Since 1.18. * @MM_MOBILE_EQUIPMENT_ERROR_GPRS_UNKNOWN: Unspecified GPRS error (CS, GPRS, UMTS). * @MM_MOBILE_EQUIPMENT_ERROR_PDP_AUTH_FAILURE: PDP authentication failure (GPRS, UMTS). Since 1.18. * @MM_MOBILE_EQUIPMENT_ERROR_INVALID_MOBILE_CLASS: Invalid mobile class (CS, GPRS, UMTS). Since 1.18. * @MM_MOBILE_EQUIPMENT_ERROR_LAST_PDN_DISCONNECTION_NOT_ALLOWED_LEGACY: Last PDN disconnection not allowed, legacy value defined before 3GPP Rel-11 (EPS). Since: 1.18. * @MM_MOBILE_EQUIPMENT_ERROR_LAST_PDN_DISCONNECTION_NOT_ALLOWED: Last PDN disconnection not allowed (EPS). Since: 1.8. * @MM_MOBILE_EQUIPMENT_ERROR_SEMANTICALLY_INCORRECT_MESSAGE: Semantically incorrect message (CS, GPRS, UMTS, EPS, 5GS). Since: 1.18. * @MM_MOBILE_EQUIPMENT_ERROR_INVALID_MANDATORY_INFORMATION: Invalid mandatory information (CS, GPRS, UMTS, EPS, 5GS). Since: 1.18. * @MM_MOBILE_EQUIPMENT_ERROR_MESSAGE_TYPE_NOT_IMPLEMENTED: Message type non-existent or not implemented (CS, GPRS, UMTS, EPS, 5GS). Since: 1.18. * @MM_MOBILE_EQUIPMENT_ERROR_CONDITIONAL_IE_ERROR: Conditional IE error (CS, GPRS, UMTS, EPS, 5GS). Since: 1.18. * @MM_MOBILE_EQUIPMENT_ERROR_UNSPECIFIED_PROTOCOL_ERROR: Unspecified protocol error (CS, GPRS, UMTS, EPS, 5GS). Since: 1.18. * @MM_MOBILE_EQUIPMENT_ERROR_OPERATOR_DETERMINED_BARRING: Operator determined barring (GPRS, UMTS, EPS, 5GS). Since: 1.18. * @MM_MOBILE_EQUIPMENT_ERROR_MAXIMUM_NUMBER_OF_BEARERS_REACHED: Maximum number of PDP contexts reached (GPRS, UMTS); maximum number of EPS bearers reached (EPS); maximum number of PDU sessions reached (5GS). Since: 1.18. * @MM_MOBILE_EQUIPMENT_ERROR_REQUESTED_APN_NOT_SUPPORTED: Requested APN not supported in current RAT and PLMN combination (GPRS, UMTS, EPS). Since: 1.18. * @MM_MOBILE_EQUIPMENT_ERROR_REQUEST_REJECTED_BCM_VIOLATION: Request rejected, bearer control mode violation (GPRS, UMTS). Since: 1.18. * @MM_MOBILE_EQUIPMENT_ERROR_UNSUPPORTED_QCI_OR_5QI_VALUE: Unsupported QCI value (EPS); unsupported 5QI value (5GS). Since 1.18. * @MM_MOBILE_EQUIPMENT_ERROR_USER_DATA_VIA_CONTROL_PLANE_CONGESTED: User data transmission via control plane is congested (GPRS, UMTS). Since 1.18. * @MM_MOBILE_EQUIPMENT_ERROR_SMS_PROVIDED_VIA_GPRS_IN_ROUTING_AREA: SMS provided via GPRS in routing area (CS, GPRS, UMTS). Since 1.18. * @MM_MOBILE_EQUIPMENT_ERROR_INVALID_PTI_VALUE: Invalid PTI value (EPS, 5GS). Since 1.18. * @MM_MOBILE_EQUIPMENT_ERROR_NO_BEARER_ACTIVATED: No PDP context activated (CS, GPRS, UMTS); no bearer context activated (EPS). Since 1.18. * @MM_MOBILE_EQUIPMENT_ERROR_MESSAGE_NOT_COMPATIBLE_WITH_PROTOCOL_STATE: Message not compatible with protocol state (CS, GPRS, UMTS, EPS, 5GS). Since 1.18. * @MM_MOBILE_EQUIPMENT_ERROR_RECOVERY_ON_TIMER_EXPIRY: Recovery on timer expiry (CS, GPRS, UMTS). Since 1.18. * @MM_MOBILE_EQUIPMENT_ERROR_INVALID_TRANSACTION_ID_VALUE: Invalid transaction identifier value (GPRS, UMTS). Since 1.18. * @MM_MOBILE_EQUIPMENT_ERROR_SERVICE_OPTION_NOT_AUTHORIZED_IN_PLMN: Requested service option is not authorized in this PLMN (EPS). Since 1.18. * @MM_MOBILE_EQUIPMENT_ERROR_NETWORK_FAILURE_ACTIVATION: Network failure during context activation (GPRS, UMTS, EPS, 5GS). Since 1.18. * @MM_MOBILE_EQUIPMENT_ERROR_REACTIVATION_REQUESTED: Reactivation requested (GPRS, UMTS, EPS, 5GS). Since 1.18. * @MM_MOBILE_EQUIPMENT_ERROR_IPV4_ONLY_ALLOWED: PDP type IPv4 only allowed (GPRS, UMTS); PDN type IPv4 only allowed (EPS); PDU session type IPv4 only allowed (5GS). Since 1.18. * @MM_MOBILE_EQUIPMENT_ERROR_IPV6_ONLY_ALLOWED: PDP type IPv6 only allowed (GPRS, UMTS); PDN type IPv6 only allowed (EPS); PDU session type IPv6 only allowed (5GS). Since 1.18. * @MM_MOBILE_EQUIPMENT_ERROR_SINGLE_ADDRESS_BEARERS_ONLY_ALLOWED: Single address bearers only allowed (GPRS, UMTS, EPS). Since 1.18. * @MM_MOBILE_EQUIPMENT_ERROR_COLLISION_WITH_NETWORK_INITIATED_REQUEST: Collision with network initiated request (GPRS, UMTS, EPS). Since 1.18. * @MM_MOBILE_EQUIPMENT_ERROR_IPV4V6_ONLY_ALLOWED: PDP type IPv4v6 only allowed (GPRS, UMTS); PDN type IPv4v6 only allowed (EPS); PDU session type IPv4v6 only allowed (5GS). Since 1.18. * @MM_MOBILE_EQUIPMENT_ERROR_NON_IP_ONLY_ALLOWED: PDP type non-IP only allowed (GPRS, UMTS); PDN type non-IP only allowed (EPS); PDU session type unstructured only allowed (5GS). Since 1.18. * @MM_MOBILE_EQUIPMENT_ERROR_BEARER_HANDLING_UNSUPPORTED: Bearer handling not supported (GPRS, UMTS, EPS). Since 1.18. * @MM_MOBILE_EQUIPMENT_ERROR_APN_RESTRICTION_INCOMPATIBLE: APN restriction value incompatible with active PDP context (GPRS, UMTS); APN restriction value incompatible with active EPS bearer context (EPS). Since 1.18. * @MM_MOBILE_EQUIPMENT_ERROR_MULTIPLE_ACCESS_TO_PDN_CONNECTION_NOT_ALLOWED: Multiple accesses to PDN connection not allowed (GPRS, UMTS, EPS). Since 1.18. * @MM_MOBILE_EQUIPMENT_ERROR_ESM_INFORMATION_NOT_RECEIVED: ESM information not received (EPS).Since 1.18. * @MM_MOBILE_EQUIPMENT_ERROR_PDN_CONNECTION_NONEXISTENT: PDN connection does not exist (EPS); PDU session does not exist (5GS). Since 1.18. * @MM_MOBILE_EQUIPMENT_ERROR_MULTIPLE_PDN_CONNECTION_SAME_APN_NOT_ALLOWED: Multiple PDN connections for a given APN not allowed (EPS). Since 1.18. * @MM_MOBILE_EQUIPMENT_ERROR_SEVERE_NETWORK_FAILURE: Severe network failure (EPS). Since 1.18. * @MM_MOBILE_EQUIPMENT_ERROR_INSUFFICIENT_RESOURCES_FOR_SLICE_AND_DNN: Insufficient resources for specific slice and DNN (5GS). Since 1.18. * @MM_MOBILE_EQUIPMENT_ERROR_UNSUPPORTED_SSC_MODE: Not supported SSC mode (5GS). Since 1.18. * @MM_MOBILE_EQUIPMENT_ERROR_INSUFFICIENT_RESOURCES_FOR_SLICE: Insufficient resources for specific slice (5GS). Since 1.18. * @MM_MOBILE_EQUIPMENT_ERROR_MESSAGE_TYPE_NOT_COMPATIBLE_WITH_PROTOCOL_STATE: Message type not compatible with protocol state (CS, GPRS, UMTS, EPS, 5GS). Since 1.18. * @MM_MOBILE_EQUIPMENT_ERROR_IE_NOT_IMPLEMENTED: Information element non-existent or not implemented (CS, GPRS, UMTS, EPS, 5GS). Since: 1.18. * @MM_MOBILE_EQUIPMENT_ERROR_N1_MODE_NOT_ALLOWED: N1 mode not allowed. (5GS). Since 1.18. * @MM_MOBILE_EQUIPMENT_ERROR_RESTRICTED_SERVICE_AREA: Restricted service area (5GS). Since 1.18. * @MM_MOBILE_EQUIPMENT_ERROR_LADN_UNAVAILABLE: LADN not available (5GS). Since 1.18. * @MM_MOBILE_EQUIPMENT_ERROR_MISSING_OR_UNKNOWN_DNN_IN_SLICE: Missing or unknown DNN in a slice (5GS). Since 1.18. * @MM_MOBILE_EQUIPMENT_ERROR_NGKSI_ALREADY_IN_USE: ngKSI already in use (5GS). Since 1.18. * @MM_MOBILE_EQUIPMENT_ERROR_PAYLOAD_NOT_FORWARDED: Payload was not forwarded (5GS). Since 1.18. * @MM_MOBILE_EQUIPMENT_ERROR_NON_3GPP_ACCESS_TO_5GCN_NOT_ALLOWED: Non-3GPP access to 5GCN not allowed (5GS). Since 1.18. * @MM_MOBILE_EQUIPMENT_ERROR_SERVING_NETWORK_NOT_AUTHORIZED: Serving network not authorized (5GS). Since 1.18. * @MM_MOBILE_EQUIPMENT_ERROR_DNN_NOT_SUPPORTED_IN_SLICE: DNN not supported or not subscribed in the slice (5GS). Since 1.18. * @MM_MOBILE_EQUIPMENT_ERROR_INSUFFICIENT_USER_PLANE_RESOURCES_FOR_PDU_SESSION: Insufficient user plane resources for PDU session (5GS). Since 1.18. * @MM_MOBILE_EQUIPMENT_ERROR_OUT_OF_LADN_SERVICE_AREA: Out of LADN service area (5GS). Since 1.18. * @MM_MOBILE_EQUIPMENT_ERROR_PTI_MISMATCH: PTI mismatch (5GS). Since 1.18. * @MM_MOBILE_EQUIPMENT_ERROR_MAX_DATA_RATE_FOR_USER_PLANE_INTEGRITY_TOO_LOW: Maximum data rate per UE for user-plane integrity protection is too low (5GS). Since 1.18. * @MM_MOBILE_EQUIPMENT_ERROR_SEMANTIC_ERROR_IN_QOS_OPERATION: Semantic error in QoS operation (5GS). Since 1.18. * @MM_MOBILE_EQUIPMENT_ERROR_SYNTACTICAL_ERROR_IN_QOS_OPERATION: Semantic error in QoS operation (5GS). Since 1.18. * @MM_MOBILE_EQUIPMENT_ERROR_INVALID_MAPPED_EPS_BEARER_IDENTITY: Invalid mapped EPS bearer identity (5GS). Since 1.18. * @MM_MOBILE_EQUIPMENT_ERROR_REDIRECTION_TO_5GCN_REQUIRED: Redirection to 5GCN required (EPS). Since 1.18. * @MM_MOBILE_EQUIPMENT_ERROR_REDIRECTION_TO_EPC_REQUIRED: Redirection to EPC required (5GS). Since 1.18. * @MM_MOBILE_EQUIPMENT_ERROR_TEMPORARILY_UNAUTHORIZED_FOR_SNPN: Temporarily not authorized for this SNPN (5GS). Since 1.18. * @MM_MOBILE_EQUIPMENT_ERROR_PERMANENTLY_UNAUTHORIZED_FOR_SNPN: Permanently not authorized for this SNPN (5GS). Since 1.18. * @MM_MOBILE_EQUIPMENT_ERROR_ETHERNET_ONLY_ALLOWED: PDN type Ethernet only allowed (EPS, 5GS). Since 1.18. * @MM_MOBILE_EQUIPMENT_ERROR_UNAUTHORIZED_FOR_CAG: Not authorized for this CAG or authorized for CAG cells only (5GS). Since 1.18. * @MM_MOBILE_EQUIPMENT_ERROR_NO_NETWORK_SLICES_AVAILABLE: No network slices available (5GS). Since 1.18. * @MM_MOBILE_EQUIPMENT_ERROR_WIRELINE_ACCESS_AREA_NOT_ALLOWED: Wireline access area not allowed (5GS). Since 1.18. * * Enumeration of Mobile Equipment errors, as defined in 3GPP TS 27.007 v17.1.0, * section 9.2 (Mobile termination error result code +CME ERROR). * * Since: 1.0 */ typedef enum { /*< underscore_name=mm_mobile_equipment_error >*/ MM_MOBILE_EQUIPMENT_ERROR_PHONE_FAILURE = 0, /*< nick=PhoneFailure >*/ MM_MOBILE_EQUIPMENT_ERROR_NO_CONNECTION = 1, /*< nick=NoConnection >*/ MM_MOBILE_EQUIPMENT_ERROR_LINK_RESERVED = 2, /*< nick=LinkReserved >*/ MM_MOBILE_EQUIPMENT_ERROR_NOT_ALLOWED = 3, /*< nick=NotAllowed >*/ MM_MOBILE_EQUIPMENT_ERROR_NOT_SUPPORTED = 4, /*< nick=NotSupported >*/ MM_MOBILE_EQUIPMENT_ERROR_PH_SIM_PIN = 5, /*< nick=PhSimPin >*/ MM_MOBILE_EQUIPMENT_ERROR_PH_FSIM_PIN = 6, /*< nick=PhFsimPin >*/ MM_MOBILE_EQUIPMENT_ERROR_PH_FSIM_PUK = 7, /*< nick=PhFsimPuk >*/ MM_MOBILE_EQUIPMENT_ERROR_SIM_NOT_INSERTED = 10, /*< nick=SimNotInserted >*/ MM_MOBILE_EQUIPMENT_ERROR_SIM_PIN = 11, /*< nick=SimPin >*/ MM_MOBILE_EQUIPMENT_ERROR_SIM_PUK = 12, /*< nick=SimPuk >*/ MM_MOBILE_EQUIPMENT_ERROR_SIM_FAILURE = 13, /*< nick=SimFailure >*/ MM_MOBILE_EQUIPMENT_ERROR_SIM_BUSY = 14, /*< nick=SimBusy >*/ MM_MOBILE_EQUIPMENT_ERROR_SIM_WRONG = 15, /*< nick=SimWrong >*/ MM_MOBILE_EQUIPMENT_ERROR_INCORRECT_PASSWORD = 16, /*< nick=IncorrectPassword >*/ MM_MOBILE_EQUIPMENT_ERROR_SIM_PIN2 = 17, /*< nick=SimPin2 >*/ MM_MOBILE_EQUIPMENT_ERROR_SIM_PUK2 = 18, /*< nick=SimPuk2 >*/ MM_MOBILE_EQUIPMENT_ERROR_MEMORY_FULL = 20, /*< nick=MemoryFull >*/ MM_MOBILE_EQUIPMENT_ERROR_INVALID_INDEX = 21, /*< nick=InvalidIndex >*/ MM_MOBILE_EQUIPMENT_ERROR_NOT_FOUND = 22, /*< nick=NotFound >*/ MM_MOBILE_EQUIPMENT_ERROR_MEMORY_FAILURE = 23, /*< nick=MemoryFailure >*/ MM_MOBILE_EQUIPMENT_ERROR_TEXT_TOO_LONG = 24, /*< nick=TextTooLong >*/ MM_MOBILE_EQUIPMENT_ERROR_INVALID_CHARS = 25, /*< nick=InvalidChars >*/ MM_MOBILE_EQUIPMENT_ERROR_DIAL_STRING_TOO_LONG = 26, /*< nick=DialStringTooLong >*/ MM_MOBILE_EQUIPMENT_ERROR_DIAL_STRING_INVALID = 27, /*< nick=DialStringInvalid >*/ MM_MOBILE_EQUIPMENT_ERROR_NO_NETWORK = 30, /*< nick=NoNetwork >*/ MM_MOBILE_EQUIPMENT_ERROR_NETWORK_TIMEOUT = 31, /*< nick=NetworkTimeout >*/ MM_MOBILE_EQUIPMENT_ERROR_NETWORK_NOT_ALLOWED = 32, /*< nick=NetworkNotAllowed >*/ MM_MOBILE_EQUIPMENT_ERROR_NETWORK_PIN = 40, /*< nick=NetworkPin >*/ MM_MOBILE_EQUIPMENT_ERROR_NETWORK_PUK = 41, /*< nick=NetworkPuk >*/ MM_MOBILE_EQUIPMENT_ERROR_NETWORK_SUBSET_PIN = 42, /*< nick=NetworkSubsetPin >*/ MM_MOBILE_EQUIPMENT_ERROR_NETWORK_SUBSET_PUK = 43, /*< nick=NetworkSubsetPuk >*/ MM_MOBILE_EQUIPMENT_ERROR_SERVICE_PIN = 44, /*< nick=ServicePin >*/ MM_MOBILE_EQUIPMENT_ERROR_SERVICE_PUK = 45, /*< nick=ServicePuk >*/ MM_MOBILE_EQUIPMENT_ERROR_CORP_PIN = 46, /*< nick=CorpPin >*/ MM_MOBILE_EQUIPMENT_ERROR_CORP_PUK = 47, /*< nick=CorpPuk >*/ MM_MOBILE_EQUIPMENT_ERROR_HIDDEN_KEY_REQUIRED = 48, /*< nick=HiddenKeyRequired >*/ MM_MOBILE_EQUIPMENT_ERROR_EAP_METHOD_NOT_SUPPORTED = 49, /*< nick=EapMethodNotSupported >*/ MM_MOBILE_EQUIPMENT_ERROR_INCORRECT_PARAMETERS = 50, /*< nick=IncorrectParameters >*/ MM_MOBILE_EQUIPMENT_ERROR_COMMAND_DISABLED = 51, /*< nick=CommandDisabled >*/ MM_MOBILE_EQUIPMENT_ERROR_COMMAND_ABORTED = 52, /*< nick=CommandAborted >*/ MM_MOBILE_EQUIPMENT_ERROR_NOT_ATTACHED_RESTRICTED = 53, /*< nick=NotAttachedRestricted >*/ MM_MOBILE_EQUIPMENT_ERROR_NOT_ALLOWED_EMERGENCY_ONLY = 54, /*< nick=NotAllowedEmergencyOnly >*/ MM_MOBILE_EQUIPMENT_ERROR_NOT_ALLOWED_RESTRICTED = 55, /*< nick=NotAllowedRestricted >*/ MM_MOBILE_EQUIPMENT_ERROR_FIXED_DIAL_NUMBER_ONLY = 56, /*< nick=FixedDialNumberOnly >*/ MM_MOBILE_EQUIPMENT_ERROR_TEMPORARILY_OUT_OF_SERVICE = 57, /*< nick=TemporarilyOutOfService >*/ MM_MOBILE_EQUIPMENT_ERROR_LANGUAGE_OR_ALPHABET_NOT_SUPPORTED = 58, /*< nick=LanguageOrAlphabetNotSupported >*/ MM_MOBILE_EQUIPMENT_ERROR_UNEXPECTED_DATA_VALUE = 59, /*< nick=UnexpectedDataValue >*/ MM_MOBILE_EQUIPMENT_ERROR_SYSTEM_FAILURE = 60, /*< nick=SystemFailure >*/ MM_MOBILE_EQUIPMENT_ERROR_DATA_MISSING = 61, /*< nick=DataMissing >*/ MM_MOBILE_EQUIPMENT_ERROR_CALL_BARRED = 62, /*< nick=CallBarred >*/ MM_MOBILE_EQUIPMENT_ERROR_MESSAGE_WAITING_INDICATION_SUBSCRIPTION_FAILURE = 63, /*< nick=MessageWaitingIndicationSubscriptionFailure >*/ MM_MOBILE_EQUIPMENT_ERROR_UNKNOWN = 100, /*< nick=Unknown >*/ MM_MOBILE_EQUIPMENT_ERROR_IMSI_UNKNOWN_IN_HSS = 102, /*< nick=ImsiUnknownInHss >*/ MM_MOBILE_EQUIPMENT_ERROR_ILLEGAL_UE = 103, /*< nick=IllegalUe >*/ MM_MOBILE_EQUIPMENT_ERROR_IMSI_UNKNOWN_IN_VLR = 104, /*< nick=ImsiUnknownInVlr >*/ MM_MOBILE_EQUIPMENT_ERROR_IMEI_NOT_ACCEPTED = 105, /*< nick=ImeiNotAccepted >*/ MM_MOBILE_EQUIPMENT_ERROR_ILLEGAL_ME = 106, /*< nick=IllegalMe >*/ MM_MOBILE_EQUIPMENT_ERROR_PS_SERVICES_NOT_ALLOWED = 107, /*< nick=PsServicesNotAllowed >*/ MM_MOBILE_EQUIPMENT_ERROR_PS_AND_NON_PS_SERVICES_NOT_ALLOWED = 108, /*< nick=PsAndNonPsServicesNotAllowed >*/ MM_MOBILE_EQUIPMENT_ERROR_UE_IDENTITY_NOT_DERIVED_FROM_NETWORK = 109, /*< nick=UeIdentityNotDerivedFromNetwork >*/ MM_MOBILE_EQUIPMENT_ERROR_IMPLICITLY_DETACHED = 110, /*< nick=ImplicitlyDetached >*/ MM_MOBILE_EQUIPMENT_ERROR_PLMN_NOT_ALLOWED = 111, /*< nick=PlmnNotAllowed >*/ MM_MOBILE_EQUIPMENT_ERROR_AREA_NOT_ALLOWED = 112, /*< nick=AreaNotAllowed >*/ MM_MOBILE_EQUIPMENT_ERROR_ROAMING_NOT_ALLOWED_IN_AREA = 113, /*< nick=RoamingNotAllowedInArea >*/ MM_MOBILE_EQUIPMENT_ERROR_PS_SERVICES_NOT_ALLOWED_IN_PLMN = 114, /*< nick=PsServicesNotAllowedInPlmn >*/ MM_MOBILE_EQUIPMENT_ERROR_NO_CELLS_IN_AREA = 115, /*< nick=NoCellsInArea >*/ MM_MOBILE_EQUIPMENT_ERROR_MSC_TEMPORARILY_NOT_REACHABLE = 116, /*< nick=MscTemporarilyNotReachable >*/ MM_MOBILE_EQUIPMENT_ERROR_NETWORK_FAILURE_ATTACH = 117, /*< nick=NetworkFailureAttach >*/ MM_MOBILE_EQUIPMENT_ERROR_CS_DOMAIN_UNAVAILABLE = 118, /*< nick=CsDomainUnavailable >*/ MM_MOBILE_EQUIPMENT_ERROR_ESM_FAILURE = 119, /*< nick=EsmFailure >*/ MM_MOBILE_EQUIPMENT_ERROR_CONGESTION = 122, /*< nick=Congestion >*/ MM_MOBILE_EQUIPMENT_ERROR_MBMS_BEARER_CAPABILITIES_INSUFFICIENT_FOR_SERVICE = 124, /*< nick=MbmsBearerCapabilitiesInsufficientForService >*/ MM_MOBILE_EQUIPMENT_ERROR_NOT_AUTHORIZED_FOR_CSG = 125, /*< nick=NotAuthorizedForCsg >*/ MM_MOBILE_EQUIPMENT_ERROR_INSUFFICIENT_RESOURCES = 126, /*< nick=InsufficientResources >*/ MM_MOBILE_EQUIPMENT_ERROR_MISSING_OR_UNKNOWN_APN = 127, /*< nick=MissingOrUnknownApn >*/ MM_MOBILE_EQUIPMENT_ERROR_UNKNOWN_PDP_ADDRESS_OR_TYPE = 128, /*< nick=UnknownPdpAddressOrType >*/ MM_MOBILE_EQUIPMENT_ERROR_USER_AUTHENTICATION_FAILED = 129, /*< nick=UserAuthenticationFailed >*/ MM_MOBILE_EQUIPMENT_ERROR_ACTIVATION_REJECTED_BY_GGSN_OR_GW = 130, /*< nick=ActivationRejectedByGgsnOrGw >*/ MM_MOBILE_EQUIPMENT_ERROR_ACTIVATION_REJECTED_UNSPECIFIED = 131, /*< nick=ActivationRejectedUnspecified >*/ MM_MOBILE_EQUIPMENT_ERROR_SERVICE_OPTION_NOT_SUPPORTED = 132, /*< nick=ServiceOptionNotSupported >*/ MM_MOBILE_EQUIPMENT_ERROR_SERVICE_OPTION_NOT_SUBSCRIBED = 133, /*< nick=ServiceOptionNotSubscribed >*/ MM_MOBILE_EQUIPMENT_ERROR_SERVICE_OPTION_OUT_OF_ORDER = 134, /*< nick=ServiceOptionOutOfOrder >*/ MM_MOBILE_EQUIPMENT_ERROR_NSAPI_OR_PTI_ALREADY_IN_USE = 135, /*< nick=NsapiOrPtiAlreadyInUse >*/ MM_MOBILE_EQUIPMENT_ERROR_REGULAR_DEACTIVATION = 136, /*< nick=RegularDeactivation >*/ MM_MOBILE_EQUIPMENT_ERROR_QOS_NOT_ACCEPTED = 137, /*< nick=QosNotAccepted >*/ MM_MOBILE_EQUIPMENT_ERROR_CALL_CANNOT_BE_IDENTIFIED = 138, /*< nick=CallCannotBeIdentified >*/ MM_MOBILE_EQUIPMENT_ERROR_CS_SERVICE_TEMPORARILY_UNAVAILABLE = 139, /*< nick=CsServiceTemporarilyUnavailable >*/ MM_MOBILE_EQUIPMENT_ERROR_FEATURE_NOT_SUPPORTED = 140, /*< nick=FeatureNotSupported >*/ MM_MOBILE_EQUIPMENT_ERROR_SEMANTIC_ERROR_IN_TFT_OPERATION = 141, /*< nick=SemanticErrorInTftOperation >*/ MM_MOBILE_EQUIPMENT_ERROR_SYNTACTICAL_ERROR_IN_TFT_OPERATION = 142, /*< nick=SyntacticalErrorInTftOperation >*/ MM_MOBILE_EQUIPMENT_ERROR_UNKNOWN_PDP_CONTEXT = 143, /*< nick=UnknownPdpContext >*/ MM_MOBILE_EQUIPMENT_ERROR_SEMANTIC_ERRORS_IN_PACKET_FILTER = 144, /*< nick=SemanticErrorsInPacketFilter >*/ MM_MOBILE_EQUIPMENT_ERROR_SYNTACTICAL_ERROR_IN_PACKET_FILTER = 145, /*< nick=SyntacticalErrorsInPacketFilter >*/ MM_MOBILE_EQUIPMENT_ERROR_PDP_CONTEXT_WITHOUT_TFT_ALREADY_ACTIVATED = 146, /*< nick=PdpContextWithoutTftAlreadyActivated >*/ MM_MOBILE_EQUIPMENT_ERROR_MULTICAST_GROUP_MEMBERSHIP_TIMEOUT = 147, /*< nick=MulticastGroupMembershipTimeout >*/ MM_MOBILE_EQUIPMENT_ERROR_GPRS_UNKNOWN = 148, /*< nick=GprsUnknown >*/ MM_MOBILE_EQUIPMENT_ERROR_PDP_AUTH_FAILURE = 149, /*< nick=PdpAuthFailure >*/ MM_MOBILE_EQUIPMENT_ERROR_INVALID_MOBILE_CLASS = 150, /*< nick=InvalidMobileClass >*/ MM_MOBILE_EQUIPMENT_ERROR_LAST_PDN_DISCONNECTION_NOT_ALLOWED_LEGACY = 151, /*< nick=LastPdnDisconnectionNotAllowedLegacy >*/ MM_MOBILE_EQUIPMENT_ERROR_LAST_PDN_DISCONNECTION_NOT_ALLOWED = 171, /*< nick=LastPdnDisconnectionNotAllowed >*/ MM_MOBILE_EQUIPMENT_ERROR_SEMANTICALLY_INCORRECT_MESSAGE = 172, /*< nick=SemanticallyIncorrectMessage >*/ MM_MOBILE_EQUIPMENT_ERROR_INVALID_MANDATORY_INFORMATION = 173, /*< nick=InvalidMandatoryInformation >*/ MM_MOBILE_EQUIPMENT_ERROR_MESSAGE_TYPE_NOT_IMPLEMENTED = 174, /*< nick=MessageTypeNotImplemented >*/ MM_MOBILE_EQUIPMENT_ERROR_CONDITIONAL_IE_ERROR = 175, /*< nick=ConditionalIeError >*/ MM_MOBILE_EQUIPMENT_ERROR_UNSPECIFIED_PROTOCOL_ERROR = 176, /*< nick=UnspecifiedProtocolError >*/ MM_MOBILE_EQUIPMENT_ERROR_OPERATOR_DETERMINED_BARRING = 177, /*< nick=OperatorDeterminedBarring >*/ MM_MOBILE_EQUIPMENT_ERROR_MAXIMUM_NUMBER_OF_BEARERS_REACHED = 178, /*< nick=MaximumNumberOfBearersReached >*/ MM_MOBILE_EQUIPMENT_ERROR_REQUESTED_APN_NOT_SUPPORTED = 179, /*< nick=RequestedApnNotSupported >*/ MM_MOBILE_EQUIPMENT_ERROR_REQUEST_REJECTED_BCM_VIOLATION = 180, /*< nick=RequestRejectedBcmViolation >*/ MM_MOBILE_EQUIPMENT_ERROR_UNSUPPORTED_QCI_OR_5QI_VALUE = 181, /*< nick=UnsupportedQciOr5qiValue >*/ MM_MOBILE_EQUIPMENT_ERROR_USER_DATA_VIA_CONTROL_PLANE_CONGESTED = 182, /*< nick=UserDataViaControlPlaneCongested >*/ MM_MOBILE_EQUIPMENT_ERROR_SMS_PROVIDED_VIA_GPRS_IN_ROUTING_AREA = 183, /*< nick=SmsProvidedViaGprsInRoutingArea >*/ MM_MOBILE_EQUIPMENT_ERROR_INVALID_PTI_VALUE = 184, /*< nick=InvalidPtiValue >*/ MM_MOBILE_EQUIPMENT_ERROR_NO_BEARER_ACTIVATED = 185, /*< nick=NoBearerActivated >*/ MM_MOBILE_EQUIPMENT_ERROR_MESSAGE_NOT_COMPATIBLE_WITH_PROTOCOL_STATE = 186, /*< nick=MessageNotCompatibleWithProtocolState >*/ MM_MOBILE_EQUIPMENT_ERROR_RECOVERY_ON_TIMER_EXPIRY = 187, /*< nick=RecoveryOnTimerExpiry >*/ MM_MOBILE_EQUIPMENT_ERROR_INVALID_TRANSACTION_ID_VALUE = 188, /*< nick=InvalidTransactionIdValue >*/ MM_MOBILE_EQUIPMENT_ERROR_SERVICE_OPTION_NOT_AUTHORIZED_IN_PLMN = 189, /*< nick=ServiceOptionNotAuthorizedInPlmn >*/ MM_MOBILE_EQUIPMENT_ERROR_NETWORK_FAILURE_ACTIVATION = 190, /*< nick=NetworkFailureActivation >*/ MM_MOBILE_EQUIPMENT_ERROR_REACTIVATION_REQUESTED = 191, /*< nick=ReactivationRequested >*/ MM_MOBILE_EQUIPMENT_ERROR_IPV4_ONLY_ALLOWED = 192, /*< nick=Ipv4OnlyAllowed >*/ MM_MOBILE_EQUIPMENT_ERROR_IPV6_ONLY_ALLOWED = 193, /*< nick=Ipv6OnlyAllowed >*/ MM_MOBILE_EQUIPMENT_ERROR_SINGLE_ADDRESS_BEARERS_ONLY_ALLOWED = 194, /*< nick=SingleAddressBearersOnlyAllowed >*/ MM_MOBILE_EQUIPMENT_ERROR_COLLISION_WITH_NETWORK_INITIATED_REQUEST = 195, /*< nick=CollisionWithNetworkInitiatedRequest >*/ MM_MOBILE_EQUIPMENT_ERROR_IPV4V6_ONLY_ALLOWED = 196, /*< nick=Ipv4v6OnlyAllowed >*/ MM_MOBILE_EQUIPMENT_ERROR_NON_IP_ONLY_ALLOWED = 197, /*< nick=NonIpOnlyAllowed >*/ MM_MOBILE_EQUIPMENT_ERROR_BEARER_HANDLING_UNSUPPORTED = 198, /*< nick=BearerHandlingUnsupported >*/ MM_MOBILE_EQUIPMENT_ERROR_APN_RESTRICTION_INCOMPATIBLE = 199, /*< nick=ApnRestrictionIncompatible >*/ MM_MOBILE_EQUIPMENT_ERROR_MULTIPLE_ACCESS_TO_PDN_CONNECTION_NOT_ALLOWED = 200, /*< nick=MultipleAccessToPdnConnectionNotAllowed >*/ MM_MOBILE_EQUIPMENT_ERROR_ESM_INFORMATION_NOT_RECEIVED = 201, /*< nick=EsmInformationNotReceived >*/ MM_MOBILE_EQUIPMENT_ERROR_PDN_CONNECTION_NONEXISTENT = 202, /*< nick=PdnConnectionNonexistent >*/ MM_MOBILE_EQUIPMENT_ERROR_MULTIPLE_PDN_CONNECTION_SAME_APN_NOT_ALLOWED = 203, /*< nick=MultiplePdnConnectionSameApnNotAllowed >*/ MM_MOBILE_EQUIPMENT_ERROR_SEVERE_NETWORK_FAILURE = 204, /*< nick=SevereNetworkFailure >*/ MM_MOBILE_EQUIPMENT_ERROR_INSUFFICIENT_RESOURCES_FOR_SLICE_AND_DNN = 205, /*< nick=InsufficientResourcesForSliceAndDnn >*/ MM_MOBILE_EQUIPMENT_ERROR_UNSUPPORTED_SSC_MODE = 206, /*< nick=UnsupportedSscMode >*/ MM_MOBILE_EQUIPMENT_ERROR_INSUFFICIENT_RESOURCES_FOR_SLICE = 207, /*< nick=InsufficientResourcesForSlice >*/ MM_MOBILE_EQUIPMENT_ERROR_MESSAGE_TYPE_NOT_COMPATIBLE_WITH_PROTOCOL_STATE = 208, /*< nick=MessageTypeNotCompatibleWithProtocolState >*/ MM_MOBILE_EQUIPMENT_ERROR_IE_NOT_IMPLEMENTED = 209, /*< nick=IeNotImplemented >*/ MM_MOBILE_EQUIPMENT_ERROR_N1_MODE_NOT_ALLOWED = 210, /*< nick=N1ModeNotAllowed >*/ MM_MOBILE_EQUIPMENT_ERROR_RESTRICTED_SERVICE_AREA = 211, /*< nick=RestrictedServiceArea >*/ MM_MOBILE_EQUIPMENT_ERROR_LADN_UNAVAILABLE = 212, /*< nick=LadnUnavailable >*/ MM_MOBILE_EQUIPMENT_ERROR_MISSING_OR_UNKNOWN_DNN_IN_SLICE = 213, /*< nick=MissingOrUnknownDnnInSlice >*/ MM_MOBILE_EQUIPMENT_ERROR_NGKSI_ALREADY_IN_USE = 214, /*< nick=NkgsiAlreadyInUse >*/ MM_MOBILE_EQUIPMENT_ERROR_PAYLOAD_NOT_FORWARDED = 215, /*< nick=PayloadNotForwarded >*/ MM_MOBILE_EQUIPMENT_ERROR_NON_3GPP_ACCESS_TO_5GCN_NOT_ALLOWED = 216, /*< nick=Non3gppAccessTo5gcnNotAllowed >*/ MM_MOBILE_EQUIPMENT_ERROR_SERVING_NETWORK_NOT_AUTHORIZED = 217, /*< nick=ServingNetworkNotAuthorized >*/ MM_MOBILE_EQUIPMENT_ERROR_DNN_NOT_SUPPORTED_IN_SLICE = 218, /*< nick=DnnNotSupportedInSlice >*/ MM_MOBILE_EQUIPMENT_ERROR_INSUFFICIENT_USER_PLANE_RESOURCES_FOR_PDU_SESSION = 219, /*< nick=InsufficientUserPlaneResourcesForPduSession >*/ MM_MOBILE_EQUIPMENT_ERROR_OUT_OF_LADN_SERVICE_AREA = 220, /*< nick=OutOfLadnServiceArea >*/ MM_MOBILE_EQUIPMENT_ERROR_PTI_MISMATCH = 221, /*< nick=PtiMismatch >*/ MM_MOBILE_EQUIPMENT_ERROR_MAX_DATA_RATE_FOR_USER_PLANE_INTEGRITY_TOO_LOW = 222, /*< nick=MaxDataRateForUserPlaneIntegrityTooLow >*/ MM_MOBILE_EQUIPMENT_ERROR_SEMANTIC_ERROR_IN_QOS_OPERATION = 223, /*< nick=SemanticErrorInQosOperation >*/ MM_MOBILE_EQUIPMENT_ERROR_SYNTACTICAL_ERROR_IN_QOS_OPERATION = 224, /*< nick=SyntacticalErrorInQosOperation >*/ MM_MOBILE_EQUIPMENT_ERROR_INVALID_MAPPED_EPS_BEARER_IDENTITY = 225, /*< nick=InvalidMappedEpsBearerIdentity >*/ MM_MOBILE_EQUIPMENT_ERROR_REDIRECTION_TO_5GCN_REQUIRED = 226, /*< nick=RedirectionTo5gcnRequired >*/ MM_MOBILE_EQUIPMENT_ERROR_REDIRECTION_TO_EPC_REQUIRED = 227, /*< nick=RedirectionToEpcRequired >*/ MM_MOBILE_EQUIPMENT_ERROR_TEMPORARILY_UNAUTHORIZED_FOR_SNPN = 228, /*< nick=TemporarilyUnauthorizedForSnpn >*/ MM_MOBILE_EQUIPMENT_ERROR_PERMANENTLY_UNAUTHORIZED_FOR_SNPN = 229, /*< nick=PermanentlyUnauthorizedForSnpn >*/ MM_MOBILE_EQUIPMENT_ERROR_ETHERNET_ONLY_ALLOWED = 230, /*< nick=EthernetOnlyAllowed >*/ MM_MOBILE_EQUIPMENT_ERROR_UNAUTHORIZED_FOR_CAG = 231, /*< nick=UnauthorizedForCag >*/ MM_MOBILE_EQUIPMENT_ERROR_NO_NETWORK_SLICES_AVAILABLE = 232, /*< nick=NoNetworkSlicesAvailable >*/ MM_MOBILE_EQUIPMENT_ERROR_WIRELINE_ACCESS_AREA_NOT_ALLOWED = 233, /*< nick=WirelineAccessAreaNotAllowed >*/ } MMMobileEquipmentError; /** * MMConnectionError: * @MM_CONNECTION_ERROR_UNKNOWN: Unknown connection error. * @MM_CONNECTION_ERROR_NO_CARRIER: No carrier. * @MM_CONNECTION_ERROR_NO_DIALTONE: No dialtone. * @MM_CONNECTION_ERROR_BUSY: Busy. * @MM_CONNECTION_ERROR_NO_ANSWER: No answer. * * Connection errors that may be reported by ModemManager. * * Since: 1.0 */ typedef enum { /*< underscore_name=mm_connection_error >*/ MM_CONNECTION_ERROR_UNKNOWN = 0, /*< nick=Unknown >*/ MM_CONNECTION_ERROR_NO_CARRIER = 1, /*< nick=NoCarrier >*/ MM_CONNECTION_ERROR_NO_DIALTONE = 2, /*< nick=NoDialtone >*/ MM_CONNECTION_ERROR_BUSY = 3, /*< nick=Busy >*/ MM_CONNECTION_ERROR_NO_ANSWER = 4, /*< nick=NoAnswer >*/ } MMConnectionError; /** * MMSerialError: * @MM_SERIAL_ERROR_UNKNOWN: Unknown serial error. * @MM_SERIAL_ERROR_OPEN_FAILED: Could not open the serial device. * @MM_SERIAL_ERROR_SEND_FAILED: Could not write to the serial device. * @MM_SERIAL_ERROR_RESPONSE_TIMEOUT: A response was not received on time. * @MM_SERIAL_ERROR_OPEN_FAILED_NO_DEVICE: Could not open the serial port, no device. * @MM_SERIAL_ERROR_FLASH_FAILED: Could not flash the device. * @MM_SERIAL_ERROR_NOT_OPEN: The serial port is not open. * @MM_SERIAL_ERROR_PARSE_FAILED: The serial port specific parsing failed. * @MM_SERIAL_ERROR_FRAME_NOT_FOUND: The serial port reported that the frame marker wasn't found (e.g. for QCDM). Since 1.6. * * Serial errors that may be reported by ModemManager. * * Since: 1.0 */ typedef enum { /*< underscore_name=mm_serial_error >*/ MM_SERIAL_ERROR_UNKNOWN = 0, /*< nick=Unknown >*/ MM_SERIAL_ERROR_OPEN_FAILED = 1, /*< nick=OpenFailed >*/ MM_SERIAL_ERROR_SEND_FAILED = 2, /*< nick=SendFailed >*/ MM_SERIAL_ERROR_RESPONSE_TIMEOUT = 3, /*< nick=ResponseTimeout >*/ MM_SERIAL_ERROR_OPEN_FAILED_NO_DEVICE = 4, /*< nick=OpenFailedNoDevice >*/ MM_SERIAL_ERROR_FLASH_FAILED = 5, /*< nick=FlashFailed >*/ MM_SERIAL_ERROR_NOT_OPEN = 6, /*< nick=NotOpen >*/ MM_SERIAL_ERROR_PARSE_FAILED = 7, /*< nick=ParseFailed >*/ MM_SERIAL_ERROR_FRAME_NOT_FOUND = 8, /*< nick=FrameNotFound >*/ } MMSerialError; /** * MMMessageError: * @MM_MESSAGE_ERROR_ME_FAILURE: ME failure. * @MM_MESSAGE_ERROR_SMS_SERVICE_RESERVED: SMS service reserved. * @MM_MESSAGE_ERROR_NOT_ALLOWED: Operation not allowed. * @MM_MESSAGE_ERROR_NOT_SUPPORTED: Operation not supported. * @MM_MESSAGE_ERROR_INVALID_PDU_PARAMETER: Invalid PDU mode parameter. * @MM_MESSAGE_ERROR_INVALID_TEXT_PARAMETER: Invalid text mode parameter. * @MM_MESSAGE_ERROR_SIM_NOT_INSERTED: SIM not inserted. * @MM_MESSAGE_ERROR_SIM_PIN: SIM PIN required. * @MM_MESSAGE_ERROR_PH_SIM_PIN: PH-SIM PIN required. * @MM_MESSAGE_ERROR_SIM_FAILURE: SIM failure. * @MM_MESSAGE_ERROR_SIM_BUSY: SIM busy. * @MM_MESSAGE_ERROR_SIM_WRONG: SIM wrong. * @MM_MESSAGE_ERROR_SIM_PUK: SIM PUK required. * @MM_MESSAGE_ERROR_SIM_PIN2: SIM PIN2 required. * @MM_MESSAGE_ERROR_SIM_PUK2: SIM PUK2 required. * @MM_MESSAGE_ERROR_MEMORY_FAILURE: Memory failure. * @MM_MESSAGE_ERROR_INVALID_INDEX: Invalid index. * @MM_MESSAGE_ERROR_MEMORY_FULL: Memory full. * @MM_MESSAGE_ERROR_SMSC_ADDRESS_UNKNOWN: SMSC address unknown. * @MM_MESSAGE_ERROR_NO_NETWORK: No network. * @MM_MESSAGE_ERROR_NETWORK_TIMEOUT: Network timeout. * @MM_MESSAGE_ERROR_NO_CNMA_ACK_EXPECTED: No CNMA Acknowledgement expected. * @MM_MESSAGE_ERROR_UNKNOWN: Unknown error. * * Enumeration of message errors, as defined in 3GPP TS 27.005 version 10 section 3.2.5. * * Since: 1.0 */ typedef enum { /*< underscore_name=mm_message_error >*/ /* 0 -> 127 per 3GPP TS 24.011 [6] clause E.2 */ /* 128 -> 255 per 3GPP TS 23.040 [3] clause 9.2.3.22 */ MM_MESSAGE_ERROR_ME_FAILURE = 300, /*< nick=MeFailure >*/ MM_MESSAGE_ERROR_SMS_SERVICE_RESERVED = 301, /*< nick=SmsServiceReserved >*/ MM_MESSAGE_ERROR_NOT_ALLOWED = 302, /*< nick=NotAllowed >*/ MM_MESSAGE_ERROR_NOT_SUPPORTED = 303, /*< nick=NotSupported >*/ MM_MESSAGE_ERROR_INVALID_PDU_PARAMETER = 304, /*< nick=InvalidPduParameter >*/ MM_MESSAGE_ERROR_INVALID_TEXT_PARAMETER = 305, /*< nick=InvalidTextParameter >*/ MM_MESSAGE_ERROR_SIM_NOT_INSERTED = 310, /*< nick=SimNotInserted >*/ MM_MESSAGE_ERROR_SIM_PIN = 311, /*< nick=SimPin >*/ MM_MESSAGE_ERROR_PH_SIM_PIN = 312, /*< nick=PhSimPin >*/ MM_MESSAGE_ERROR_SIM_FAILURE = 313, /*< nick=SimFailure >*/ MM_MESSAGE_ERROR_SIM_BUSY = 314, /*< nick=SimBusy >*/ MM_MESSAGE_ERROR_SIM_WRONG = 315, /*< nick=SimWrong >*/ MM_MESSAGE_ERROR_SIM_PUK = 316, /*< nick=SimPuk >*/ MM_MESSAGE_ERROR_SIM_PIN2 = 317, /*< nick=SimPin2 >*/ MM_MESSAGE_ERROR_SIM_PUK2 = 318, /*< nick=SimPuk2 >*/ MM_MESSAGE_ERROR_MEMORY_FAILURE = 320, /*< nick=MemoryFailure >*/ MM_MESSAGE_ERROR_INVALID_INDEX = 321, /*< nick=InvalidIndex >*/ MM_MESSAGE_ERROR_MEMORY_FULL = 322, /*< nick=MemoryFull >*/ MM_MESSAGE_ERROR_SMSC_ADDRESS_UNKNOWN = 330, /*< nick=SmscAddressUnknown >*/ MM_MESSAGE_ERROR_NO_NETWORK = 331, /*< nick=NoNetwork >*/ MM_MESSAGE_ERROR_NETWORK_TIMEOUT = 332, /*< nick=NetworkTimeout >*/ MM_MESSAGE_ERROR_NO_CNMA_ACK_EXPECTED = 340, /*< nick=NoCnmaAckExpected >*/ MM_MESSAGE_ERROR_UNKNOWN = 500 /*< nick=Unknown >*/ } MMMessageError; /** * MMCdmaActivationError: * @MM_CDMA_ACTIVATION_ERROR_NONE: No error. * @MM_CDMA_ACTIVATION_ERROR_UNKNOWN: An error occurred. * @MM_CDMA_ACTIVATION_ERROR_ROAMING: Device cannot activate while roaming. * @MM_CDMA_ACTIVATION_ERROR_WRONG_RADIO_INTERFACE: Device cannot activate on this network type (eg EVDO vs 1xRTT). * @MM_CDMA_ACTIVATION_ERROR_COULD_NOT_CONNECT: Device could not connect to the network for activation. * @MM_CDMA_ACTIVATION_ERROR_SECURITY_AUTHENTICATION_FAILED: Device could not authenticate to the network for activation. * @MM_CDMA_ACTIVATION_ERROR_PROVISIONING_FAILED: Later stages of device provisioning failed. * @MM_CDMA_ACTIVATION_ERROR_NO_SIGNAL: No signal available. * @MM_CDMA_ACTIVATION_ERROR_TIMED_OUT: Activation timed out. * @MM_CDMA_ACTIVATION_ERROR_START_FAILED: API call for initial activation failed. * * CDMA Activation errors. * * Since: 1.0 */ typedef enum { /*< underscore_name=mm_cdma_activation_error >*/ MM_CDMA_ACTIVATION_ERROR_NONE = 0, /*< nick=None >*/ MM_CDMA_ACTIVATION_ERROR_UNKNOWN = 1, /*< nick=Unknown >*/ MM_CDMA_ACTIVATION_ERROR_ROAMING = 2, /*< nick=Roaming >*/ MM_CDMA_ACTIVATION_ERROR_WRONG_RADIO_INTERFACE = 3, /*< nick=WrongRadioInterface >*/ MM_CDMA_ACTIVATION_ERROR_COULD_NOT_CONNECT = 4, /*< nick=CouldNotConnect >*/ MM_CDMA_ACTIVATION_ERROR_SECURITY_AUTHENTICATION_FAILED = 5, /*< nick=SecurityAuthenticationFailed >*/ MM_CDMA_ACTIVATION_ERROR_PROVISIONING_FAILED = 6, /*< nick=ProvisioningFailed >*/ MM_CDMA_ACTIVATION_ERROR_NO_SIGNAL = 7, /*< nick=NoSignal >*/ MM_CDMA_ACTIVATION_ERROR_TIMED_OUT = 8, /*< nick=TimedOut >*/ MM_CDMA_ACTIVATION_ERROR_START_FAILED = 9 /*< nick=StartFailed >*/ } MMCdmaActivationError; /** * MMCarrierLockError: * @MM_CARRIER_LOCK_ERROR_UNKNOWN: Unknown error. * @MM_CARRIER_LOCK_ERROR_INVALID_SIGNATURE: Invalid signature. * @MM_CARRIER_LOCK_ERROR_INVALID_IMEI: Invalid IMEI. * @MM_CARRIER_LOCK_ERROR_INVALID_TIMESTAMP: Invalid timestamp. * @MM_CARRIER_LOCK_ERROR_NETWORK_LIST_TOO_LARGE: Network list too large. * @MM_CARRIER_LOCK_ERROR_SIGNATURE_ALGORITHM_NOT_SUPPORTED: Algorithm not supported. * @MM_CARRIER_LOCK_ERROR_FEATURE_NOT_SUPPORTED: Feature not supported. * @MM_CARRIER_LOCK_ERROR_DECODE_OR_PARSING_ERROR: Decode or parsing error. * * Carrier lock operation errors. * * Since: 1.24 */ typedef enum { /*< underscore_name=mm_carrier_lock_error >*/ MM_CARRIER_LOCK_ERROR_UNKNOWN = 0, /*< nick=Unknown >*/ MM_CARRIER_LOCK_ERROR_INVALID_SIGNATURE = 1, /*< nick=InvalidSignature >*/ MM_CARRIER_LOCK_ERROR_INVALID_IMEI = 2, /*< nick=InvalidImei >*/ MM_CARRIER_LOCK_ERROR_INVALID_TIMESTAMP = 3, /*< nick=InvalidTimestamp >*/ MM_CARRIER_LOCK_ERROR_NETWORK_LIST_TOO_LARGE = 6, /*< nick=NetworkListTooLarge >*/ MM_CARRIER_LOCK_ERROR_SIGNATURE_ALGORITHM_NOT_SUPPORTED = 7, /*< nick=SignatureAlgorithmNotSupported >*/ MM_CARRIER_LOCK_ERROR_FEATURE_NOT_SUPPORTED = 8, /*< nick=FeatureNotSupported >*/ MM_CARRIER_LOCK_ERROR_DECODE_OR_PARSING_ERROR = 9, /*< nick=DecodeOrParsingError >*/ } MMCarrierLockError; #endif /* _MODEMMANAGER_ERRORS_H_ */ ModemManager-1.23.4-dev/include/ModemManager-tags.h000066400000000000000000000227541456466623000220230ustar00rootroot00000000000000/* -*- 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) 2018 Aleksander Morgado */ /* * NOTE! this file is NOT part of the installed ModemManager API. * * We keep this file under include/ because we want to build and * expose the associated documentation. */ #ifndef MM_TAGS_H #define MM_TAGS_H /** * SECTION:mm-tags * @short_description: generic udev tags supported * * This section defines generic udev tags that are used by ModemManager, * associated to full devices or to specific ports in a given device. */ /** * ID_MM_CANDIDATE: * * This is a port-specific tag added automatically when all other * ModemManager related tags have already been set. * * Since: 1.10 */ #define ID_MM_CANDIDATE "ID_MM_CANDIDATE" /** * ID_MM_PHYSDEV_UID: * * This is a device-specific tag that allows users to 'name' modem * devices with a predefined 'unique ID' string. * * When this tag is given per-port, the daemon will consider that all * ports with the same UID value are associated to the same device. * This is useful for e.g. modems that expose multiple RS232 ports * connected to the system via different platform ports (or USB to * RS232 adapters). * * This UID is exposed in * the 'Device' * property and can then be used in mmcli calls to refer unequivocally * to a specific device, regardless of its modem index, e.g.: * $ mmcli --modem=UID ... * * Since: 1.10 */ #define ID_MM_PHYSDEV_UID "ID_MM_PHYSDEV_UID" /** * ID_MM_DEVICE_PROCESS: * * This is a device-specific tag that allows explicitly requesting the * processing of all ports exposed by the device. This tag is usually * used by users when the daemon runs with ALLOWLIST-ONLY filter policy * type, and is associated to the MM_FILTER_RULE_EXPLICIT_ALLOWLIST rule. * * This tag may also be specified in specific ports, e.g. when the modem * exposes a single platform port without any parent device. * * Since: 1.10 */ #define ID_MM_DEVICE_PROCESS "ID_MM_DEVICE_PROCESS" /** * ID_MM_DEVICE_IGNORE: * * This is a device-specific tag that allows explicitly requesting to * ignore all ports exposed by the device. * * This tag was originally applicable to TTY ports and only when running * in certain filter policy types. Since 1.12, this tag applies to all * filter types and to all port types (not only TTYs), and is associated * to the MM_FILTER_RULE_EXPLICIT_BLOCKLIST rule. * * Since: 1.10 */ #define ID_MM_DEVICE_IGNORE "ID_MM_DEVICE_IGNORE" /** * ID_MM_PORT_IGNORE: * * This is a port-specific tag that allows explicitly ignoring a given port * in a device. * * This tag applies to all types of ports. * * Since: 1.10 */ #define ID_MM_PORT_IGNORE "ID_MM_PORT_IGNORE" /** * ID_MM_PORT_TYPE_AT_PRIMARY: * * This is a port-specific tag applied to TTYs that we know in advance * are AT ports to be used as primary control ports. * * This tag will also prevent QCDM probing on the port. * * Since: 1.10 */ #define ID_MM_PORT_TYPE_AT_PRIMARY "ID_MM_PORT_TYPE_AT_PRIMARY" /** * ID_MM_PORT_TYPE_AT_SECONDARY: * * This is a port-specific tag applied to TTYs that we know in advance * are AT ports to be used as secondary control ports. * * This tag will also prevent QCDM probing on the port. * * Since: 1.10 */ #define ID_MM_PORT_TYPE_AT_SECONDARY "ID_MM_PORT_TYPE_AT_SECONDARY" /** * ID_MM_PORT_TYPE_AT_GPS_CONTROL: * * This is a port-specific tag applied to TTYs that we know in advance * are AT ports to be used for GPS control. Depending on the device, * this may or may not mean the same port is also used fo GPS data. * * This tag will also prevent QCDM probing on the port. * * Since: 1.20 */ #define ID_MM_PORT_TYPE_AT_GPS_CONTROL "ID_MM_PORT_TYPE_AT_GPS_CONTROL" /** * ID_MM_PORT_TYPE_AT_PPP: * * This is a port-specific tag applied to TTYs that we know in advance * are AT ports to be used as data ports exclusively. * * This tag will also prevent QCDM probing on the port. * * Since: 1.10 */ #define ID_MM_PORT_TYPE_AT_PPP "ID_MM_PORT_TYPE_AT_PPP" /** * ID_MM_PORT_TYPE_QCDM: * * This is a port-specific tag applied to TTYs that we know in advance * are QCDM ports. * * The only purpose of this tag is to prevent AT probing in the port. * * Since: 1.10 */ #define ID_MM_PORT_TYPE_QCDM "ID_MM_PORT_TYPE_QCDM" /** * ID_MM_PORT_TYPE_GPS: * * This is a port-specific tag applied to TTYs that we know in advance * are GPS data ports where we expect to receive NMEA traces. * * This tag also prevents AT and QCDM probing in the port. * * Since: 1.10 */ #define ID_MM_PORT_TYPE_GPS "ID_MM_PORT_TYPE_GPS" /** * ID_MM_PORT_TYPE_AUDIO: * * This is a port-specific tag applied to TTYs that we know in advance * are audio ports. * * This tag also prevents AT and QCDM probing in the port. * * Since: 1.12 */ #define ID_MM_PORT_TYPE_AUDIO "ID_MM_PORT_TYPE_AUDIO" /** * ID_MM_PORT_TYPE_QMI: * * This is a port-specific tag applied to generic ports that we know in advance * are QMI ports. * * This tag will also prevent other types of probing (e.g. AT, MBIM) on the * port. * * This tag is not required for QMI ports exposed by the qmi_wwan driver. * * Since: 1.16 */ #define ID_MM_PORT_TYPE_QMI "ID_MM_PORT_TYPE_QMI" /** * ID_MM_PORT_TYPE_MBIM: * * This is a port-specific tag applied to generic ports that we know in advance * are MBIM ports. * * This tag will also prevent other types of probing (e.g. AT, QMI) on the * port. * * This tag is not required for MBIM ports exposed by the cdc_mbim driver. * * Since: 1.16 */ #define ID_MM_PORT_TYPE_MBIM "ID_MM_PORT_TYPE_MBIM" /** * ID_MM_TTY_BAUDRATE: * * This is a port-specific tag applied to TTYs that require a specific * baudrate to work. USB modems will usually allow auto-bauding * configuration, so this tag is really only meaningful to true RS232 * devices. * * The value of the tag should be the number of bauds per second to * use when talking to the port, e.g. "115200". If not given, the * default of 57600bps is assumed. * * Since: 1.10 */ #define ID_MM_TTY_BAUDRATE "ID_MM_TTY_BAUDRATE" /** * ID_MM_TTY_FLOW_CONTROL: * * This is a port-specific tag applied to TTYs that require a specific * flow control mechanism to work not only in data mode but also in * control mode. * * The value of the tag should be either 'none', 'xon-xoff' or * 'rts-cts', and must be a flow control value supported by the device * where it's configured. If not given, it is assumed that the TTYs * don't require any specific flow control setting in command mode. * * Since: 1.10 */ #define ID_MM_TTY_FLOW_CONTROL "ID_MM_TTY_FLOW_CONTROL" /** * ID_MM_REQUIRED: * * This is a port-specific tag that allows users to specify that the modem * must be able to successfully probe and use the given control port. * * If this tag is set and the port probing procedure fails, the modem object * will not be created, which is the same as if the port didn't exist in the * first place. * * E.g. this tag may be set on a QMI control port if we want to make sure the * modem object exposed by ModemManager is QMI-capable and never an AT-based * modem created due to falling back on a failed QMI port probing procedure. * * Since: 1.22 */ #define ID_MM_REQUIRED "ID_MM_REQUIRED" /** * ID_MM_MAX_MULTIPLEXED_LINKS: * * This is a device-specific tag that allows users to specify the maximum amount * of multiplexed links the modem supports. * * An integer value greater or equal than 0 must be given. The value 0 in this * tag completely disables the multiplexing support in the device. * * This setting does nothing if the modem doesn't support multiplexing, or if the * value configured is greater than the one specified by the modem itself (e.g. * the control protocol in use also limits this value). * * Since: 1.22 */ #define ID_MM_MAX_MULTIPLEXED_LINKS "ID_MM_MAX_MULTIPLEXED_LINKS" /* * The following symbols are deprecated. We don't add them to -compat * because this -tags file is not really part of the installed API. */ #ifndef MM_DISABLE_DEPRECATED /** * ID_MM_TTY_BLACKLIST: * * This was a device-specific tag that allowed explicitly blacklisting * devices that exposed TTY devices so that they were never probed. * * This tag was applicable only when running in certain filter policy types, * and is no longer used since 1.18. * * Since: 1.12 * Deprecated: 1.18.0 */ #define ID_MM_TTY_BLACKLIST "ID_MM_TTY_BLACKLIST" /** * ID_MM_TTY_MANUAL_SCAN_ONLY: * * This was a device-specific tag that allowed explicitly allowlisting * devices that exposed TTY devices so that they were never probed * automatically. Instead, an explicit manual scan request could * be sent to the daemon so that the TTY ports exposed by the device * were probed. * * This tag was applicable only when running in certain filter policy types, * and is no longer used since 1.18. * * Since: 1.12 * Deprecated: 1.18.0 */ #define ID_MM_TTY_MANUAL_SCAN_ONLY "ID_MM_TTY_MANUAL_SCAN_ONLY" #endif #endif /* MM_TAGS_H */ ModemManager-1.23.4-dev/include/ModemManager-version.h.in000066400000000000000000000045201456466623000231460ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* * 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 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 Street, Fifth Floor, * Boston, MA 02110-1301 USA. * * Copyright (C) 2013 Lanedo GmbH */ #ifndef _MM_VERSION_H_ #define _MM_VERSION_H_ /** * SECTION:mm-version * @short_description: Version information in the API. * * This section defines types that are used to identify the ModemManager version. **/ /** * MM_MAJOR_VERSION: * * Evaluates to the major version number of ModemManager which this source * is compiled against. * * Since: 1.0 */ #define MM_MAJOR_VERSION (@MM_MAJOR_VERSION@) /** * MM_MINOR_VERSION: * * Evaluates to the minor version number of ModemManager which this source * is compiled against. * * Since: 1.0 */ #define MM_MINOR_VERSION (@MM_MINOR_VERSION@) /** * MM_MICRO_VERSION: * * Evaluates to the micro version number of ModemManager which this source * compiled against. * * Since: 1.0 */ #define MM_MICRO_VERSION (@MM_MICRO_VERSION@) /** * MM_CHECK_VERSION: * @major: major version (e.g. 1 for version 1.2.5) * @minor: minor version (e.g. 2 for version 1.2.5) * @micro: micro version (e.g. 5 for version 1.2.5) * * Checks the version of ModemManager at compile time. * * Returns: %TRUE if the version of the ModemManager header files * is the same as or newer than the passed-in version. * * Since: 1.0 */ #define MM_CHECK_VERSION(major,minor,micro) \ (MM_MAJOR_VERSION > (major) || \ (MM_MAJOR_VERSION == (major) && MM_MINOR_VERSION > (minor)) || \ (MM_MAJOR_VERSION == (major) && MM_MINOR_VERSION == (minor) && MM_MICRO_VERSION >= (micro))) #endif /* _MM_VERSION_H_ */ ModemManager-1.23.4-dev/include/ModemManager.h000066400000000000000000000027561456466623000210670ustar00rootroot00000000000000/* * ModemManager Interface Specification * version 1.x * * 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 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 Street, Fifth Floor, * Boston, MA 02110-1301 USA. * * Copyright (C) 2008 - 2009 Novell, Inc. * Copyright (C) 2009 - 2013 Red Hat, Inc. * Copyright (C) 2011 - 2013 Google, Inc. * Copyright (C) 2011 - 2013 Lanedo Gmbh */ #ifndef _MODEM_MANAGER_H_ #define _MODEM_MANAGER_H_ #define __MODEM_MANAGER_H_INSIDE__ /* Public header with DBus Interface, Method, Signal and Property names */ #include /* Public header with enumerations and flags */ #include /* Public header with errors */ #include /* Public header with compatibility types and methods */ #include /* Public header with version info */ #include #endif /* _MODEM_MANAGER_H_ */ ModemManager-1.23.4-dev/include/meson.build000066400000000000000000000017751456466623000205240ustar00rootroot00000000000000# SPDX-License-Identifier: GPL-2.0-or-later # Copyright (C) 2021 Iñigo Martinez include_inc = include_directories('.') mm_enums_header = files('ModemManager-enums.h') mm_errors_header = files('ModemManager-errors.h') headers = files( 'ModemManager-compat.h', 'ModemManager.h', ) install_headers( headers + mm_enums_header + mm_errors_header, install_dir: mm_pkgincludedir, ) header = 'ModemManager-names.h' mm_names_header = configure_file( input: [build_aux_dir / 'header-generator.xsl', mm_ifaces_all], output: header, command: [find_program('xsltproc'), '--xinclude', '--nonet', '--output', '@OUTPUT@', '@INPUT@'], install: true, install_dir: mm_pkgincludedir, ) mm_version_header = configure_file( input: 'ModemManager-version.h.in', output: '@BASENAME@', configuration: version_conf, install: true, install_dir: mm_pkgincludedir, ) include_dep = declare_dependency( sources: [mm_names_header, mm_version_header], include_directories: include_inc, ) ModemManager-1.23.4-dev/introspection/000077500000000000000000000000001456466623000176255ustar00rootroot00000000000000ModemManager-1.23.4-dev/introspection/all.xml000066400000000000000000000027761456466623000211330ustar00rootroot00000000000000 ModemManager-1.23.4-dev/introspection/meson.build000066400000000000000000000027511456466623000217740ustar00rootroot00000000000000# SPDX-License-Identifier: GPL-2.0-or-later # Copyright (C) 2021 Iñigo Martinez # DBus Introspection files mm_ifaces_all = files('all.xml') if enable_tests mm_ifaces_test = files('tests/org.freedesktop.ModemManager1.Test.xml') endif mm_ifaces = files('org.freedesktop.ModemManager1.xml') mm_ifaces_bearer = files('org.freedesktop.ModemManager1.Bearer.xml') mm_ifaces_call = files('org.freedesktop.ModemManager1.Call.xml') mm_ifaces_modem = files( 'org.freedesktop.ModemManager1.Modem.Firmware.xml', 'org.freedesktop.ModemManager1.Modem.Location.xml', 'org.freedesktop.ModemManager1.Modem.Messaging.xml', 'org.freedesktop.ModemManager1.Modem.Modem3gpp.ProfileManager.xml', 'org.freedesktop.ModemManager1.Modem.Modem3gpp.Ussd.xml', 'org.freedesktop.ModemManager1.Modem.Modem3gpp.xml', 'org.freedesktop.ModemManager1.Modem.ModemCdma.xml', 'org.freedesktop.ModemManager1.Modem.Oma.xml', 'org.freedesktop.ModemManager1.Modem.Sar.xml', 'org.freedesktop.ModemManager1.Modem.Signal.xml', 'org.freedesktop.ModemManager1.Modem.Simple.xml', 'org.freedesktop.ModemManager1.Modem.Time.xml', 'org.freedesktop.ModemManager1.Modem.Voice.xml', 'org.freedesktop.ModemManager1.Modem.xml', ) mm_ifaces_sim = files('org.freedesktop.ModemManager1.Sim.xml') mm_ifaces_sms = files('org.freedesktop.ModemManager1.Sms.xml',) install_data( mm_ifaces + mm_ifaces_bearer + mm_ifaces_call + mm_ifaces_modem + mm_ifaces_sim + mm_ifaces_sms, install_dir: dbus_interfaces_dir, ) ModemManager-1.23.4-dev/introspection/org.freedesktop.ModemManager1.Bearer.xml000066400000000000000000000610111456466623000272620ustar00rootroot00000000000000 ModemManager-1.23.4-dev/introspection/org.freedesktop.ModemManager1.Call.xml000066400000000000000000000157601456466623000267470ustar00rootroot00000000000000 ModemManager-1.23.4-dev/introspection/org.freedesktop.ModemManager1.Modem.Firmware.xml000066400000000000000000000207421456466623000307040ustar00rootroot00000000000000 ModemManager-1.23.4-dev/introspection/org.freedesktop.ModemManager1.Modem.Location.xml000066400000000000000000000465371456466623000307120ustar00rootroot00000000000000 ModemManager-1.23.4-dev/introspection/org.freedesktop.ModemManager1.Modem.Messaging.xml000066400000000000000000000101761456466623000310450ustar00rootroot00000000000000 org.freedesktop.ModemManager1.Modem.Modem3gpp.ProfileManager.xml000066400000000000000000000173151456466623000336000ustar00rootroot00000000000000ModemManager-1.23.4-dev/introspection ModemManager-1.23.4-dev/introspection/org.freedesktop.ModemManager1.Modem.Modem3gpp.Ussd.xml000066400000000000000000000070751456466623000317040ustar00rootroot00000000000000 ModemManager-1.23.4-dev/introspection/org.freedesktop.ModemManager1.Modem.Modem3gpp.xml000066400000000000000000000325651456466623000307710ustar00rootroot00000000000000 ModemManager-1.23.4-dev/introspection/org.freedesktop.ModemManager1.Modem.ModemCdma.xml000066400000000000000000000151571456466623000307620ustar00rootroot00000000000000 ModemManager-1.23.4-dev/introspection/org.freedesktop.ModemManager1.Modem.Oma.xml000066400000000000000000000113601456466623000276400ustar00rootroot00000000000000 ModemManager-1.23.4-dev/introspection/org.freedesktop.ModemManager1.Modem.Sar.xml000066400000000000000000000067771456466623000276710ustar00rootroot00000000000000 ModemManager-1.23.4-dev/introspection/org.freedesktop.ModemManager1.Modem.Signal.xml000066400000000000000000000346111456466623000303450ustar00rootroot00000000000000 ModemManager-1.23.4-dev/introspection/org.freedesktop.ModemManager1.Modem.Simple.xml000066400000000000000000000175271456466623000303700ustar00rootroot00000000000000 ModemManager-1.23.4-dev/introspection/org.freedesktop.ModemManager1.Modem.Time.xml000066400000000000000000000056141456466623000300270ustar00rootroot00000000000000 ModemManager-1.23.4-dev/introspection/org.freedesktop.ModemManager1.Modem.Voice.xml000066400000000000000000000133621456466623000301750ustar00rootroot00000000000000 ModemManager-1.23.4-dev/introspection/org.freedesktop.ModemManager1.Modem.xml000066400000000000000000001340201456466623000271240ustar00rootroot00000000000000 ModemManager-1.23.4-dev/introspection/org.freedesktop.ModemManager1.Sim.xml000066400000000000000000000146161456466623000266230ustar00rootroot00000000000000 ModemManager-1.23.4-dev/introspection/org.freedesktop.ModemManager1.Sms.xml000066400000000000000000000153641456466623000266360ustar00rootroot00000000000000 ModemManager-1.23.4-dev/introspection/org.freedesktop.ModemManager1.xml000066400000000000000000000111751456466623000260710ustar00rootroot00000000000000 ModemManager-1.23.4-dev/introspection/tests/000077500000000000000000000000001456466623000207675ustar00rootroot00000000000000ModemManager-1.23.4-dev/introspection/tests/org.freedesktop.ModemManager1.Test.xml000066400000000000000000000014611456466623000301460ustar00rootroot00000000000000 ModemManager-1.23.4-dev/introspection/wip-org.freedesktop.ModemManager1.Modem.Contacts.xml000066400000000000000000000130061456466623000314760ustar00rootroot00000000000000 ModemManager-1.23.4-dev/libmm-glib/000077500000000000000000000000001456466623000167405ustar00rootroot00000000000000ModemManager-1.23.4-dev/libmm-glib/generated/000077500000000000000000000000001456466623000206765ustar00rootroot00000000000000ModemManager-1.23.4-dev/libmm-glib/generated/meson.build000066400000000000000000000123551456466623000230460ustar00rootroot00000000000000# SPDX-License-Identifier: GPL-2.0-or-later # Copyright (C) 2021 Iñigo Martinez generated_inc = include_directories('.') generated_build_dir = meson.current_build_dir() common_c_args = cc.get_supported_arguments([ '-Wno-float-equal', '-Wno-shadow', '-Wno-unused-function', ]) gen_sources = [] gen_headers = [] gen_docs = [] # Enum types enums_types = 'mm-enums-types' gen_sources += custom_target( enums_types + '.c', input: mm_enums_header, output: enums_types + '.c', command: [ python, mm_mkenums, '--enums-only', '--fhead', '#include "mm-enums-types.h"\n', '--template', files(templates_dir / enums_types + '.c.template'), '@INPUT@'], capture: true, ) gen_headers += custom_target( enums_types + '.h', input: mm_enums_header, output: enums_types + '.h', command: [ python, mm_mkenums, '--enums-only', '--fhead', '#include \n#ifndef __MM_ENUMS_TYPES_H__\n#define __MM_ENUMS_TYPES_H__\n', '--template', files(templates_dir / enums_types + '.h.template'), '--ftail', '#endif /* __MM_ENUMS_TYPES_H__ */\n', '@INPUT@'], capture: true, install: true, install_dir: mm_glib_pkgincludedir, ) # Flag types enums_types = 'mm-flags-types' gen_sources += custom_target( enums_types + '.c', input: mm_enums_header, output: enums_types + '.c', command: [ python, mm_mkenums, '--flags-only', '--fhead', '#include "mm-flags-types.h"\n', '--template', files(templates_dir / enums_types + '.c.template'), '@INPUT@'], capture: true, ) gen_headers += custom_target( enums_types + '.h', input: mm_enums_header, output: enums_types + '.h', command: [ python, mm_mkenums, '--flags-only', '--fhead', '#include \n#ifndef __MM_FLAGS_TYPES_H__\n#define __MM_FLAGS_TYPES_H__\n', '--template', files(templates_dir / enums_types + '.h.template'), '--ftail', '#endif /* __MM_FLAGS_TYPES_H__ */\n', '@INPUT@'], capture: true, install: true, install_dir: mm_glib_pkgincludedir, ) # Error types & quarks errors_types = 'mm-errors-types' gen_sources += custom_target( errors_types + '.c', input: mm_errors_header, output: errors_types + '.c', command: [ python, mm_mkenums, '--fhead', '#include \n#include "mm-errors-types.h"\n', '--template', files(templates_dir / errors_types + '.c.template'), '@INPUT@'], capture: true, ) gen_headers += custom_target( errors_types + '.h', input: mm_errors_header, output: errors_types + '.h', command: [ python, mm_mkenums, '--fhead', '#ifndef __MM_ERRORS_TYPES_H__\n#define __MM_ERRORS_TYPES_H__\n', '--template', files(templates_dir / errors_types + '.h.template'), '--ftail', '#endif /* __MM_ERRORS_TYPES_H__ */\n', '@INPUT@'], capture: true, install: true, install_dir: mm_glib_pkgincludedir, ) errors_quarks = 'mm-errors-quarks' gen_sources += custom_target( errors_quarks + '.c', input: mm_errors_header, output: errors_quarks + '.c', command: [ python, mm_mkenums, '--fhead', '#include \n#include "mm-errors-types.h"\n', '--template', files(templates_dir / errors_quarks + '.c.template'), '@INPUT@'], capture: true, ) gdbus_ifaces = { 'bearer': {'sources': mm_ifaces_bearer, 'object_manager': false}, 'call': {'sources': mm_ifaces_call, 'object_manager': false}, 'manager': {'sources': mm_ifaces, 'object_manager': false}, 'sim': {'sources': mm_ifaces_sim, 'object_manager': false}, } annotations = [ ['org.freedesktop.ModemManager1.Modem.ModemCdma', 'org.gtk.GDBus.C.Name', 'ModemCdma'], ['org.freedesktop.ModemManager1.Modem.Modem3gpp', 'org.gtk.GDBus.C.Name', 'Modem3gpp'], ['org.freedesktop.ModemManager1.Modem.Modem3gpp.Ussd', 'org.gtk.GDBus.C.Name', 'Modem3gppUssd'], ['org.freedesktop.ModemManager1.Modem.Modem3gpp.ProfileManager', 'org.gtk.GDBus.C.Name', 'Modem3gppProfileManager'], ] gdbus_ifaces += {'modem': {'sources': mm_ifaces_modem, 'annotations': annotations, 'object_manager': true}} annotations = [['org.freedesktop.ModemManager1.Sms:Data', 'org.gtk.GDBus.C.ForceGVariant', 'True']] gdbus_ifaces += {'sms': {'sources': mm_ifaces_sms, 'annotations': annotations, 'object_manager': false}} foreach name, kwargs: gdbus_ifaces gdbus_sources = gnome.gdbus_codegen( 'mm-gdbus-' + name, interface_prefix: 'org.freedesktop.ModemManager1.', namespace: 'MmGdbus', docbook: 'mm-gdbus-doc', autocleanup: 'objects', kwargs: kwargs, # FIXME: due to the lack of possibility to add `docbook targets` to the `expand_content_files`. build_by_default: true, install_header: true, install_dir: mm_glib_pkgincludedir, ) gen_sources += gdbus_sources[0] gen_headers += gdbus_sources[1] # FIXME: the `expand_content_files` must be strings gen_docs += gdbus_sources[2] endforeach deps = [ include_dep, gio_unix_dep, glib_deps, ] libmm_generated = static_library( 'mm-generated', sources: gen_sources + gen_headers, include_directories: top_inc, dependencies: deps, c_args: common_c_args, ) libmm_generated_dep = declare_dependency( sources: gen_headers, include_directories: generated_inc, dependencies: glib_deps, link_whole: libmm_generated, ) if enable_tests subdir('tests') endif ModemManager-1.23.4-dev/libmm-glib/generated/tests/000077500000000000000000000000001456466623000220405ustar00rootroot00000000000000ModemManager-1.23.4-dev/libmm-glib/generated/tests/meson.build000066400000000000000000000012151456466623000242010ustar00rootroot00000000000000# SPDX-License-Identifier: GPL-2.0-or-later # Copyright (C) 2021 Iñigo Martinez # Test interface gdbus_sources = gnome.gdbus_codegen( 'mm-gdbus-test', sources: mm_ifaces_test, interface_prefix: 'org.freedesktop.ModemManager1.', namespace: 'MmGdbus', autocleanup: 'objects', ) libmm_test_generated = static_library( 'mm-test-generated', sources: gdbus_sources, include_directories: top_inc, dependencies: deps, c_args: common_c_args, ) libmm_test_generated_dep = declare_dependency( sources: gdbus_sources[1], include_directories: '.', dependencies: glib_deps, link_with: libmm_test_generated, ) ModemManager-1.23.4-dev/libmm-glib/libmm-glib.h000066400000000000000000000064251456466623000211330ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * libmm-glib -- Access modem status & information from glib applications * * 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 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 Street, Fifth Floor, * Boston, MA 02110-1301 USA. * * Copyright (C) 2011 - 2012 Google, Inc. */ #ifndef _LIBMM_GLIB_H_ #define _LIBMM_GLIB_H_ #define __LIBMM_GLIB_H_INSIDE__ /* ModemManager generic headers */ #include /* libmm-glib headers */ #if !defined (_LIBMM_INSIDE_MM) /* This headers are not exported within ModemManager */ # include # include # include # include # include # include # include # include # include # include # include # include # include # include # include # include # include # include # include # include #endif #if defined (_LIBMM_INSIDE_MM) || \ defined (_LIBMM_INSIDE_MMCLI) || \ defined (LIBMM_GLIB_COMPILATION) /* This one is not even installed */ # include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* generated */ #include #include #include #include #include #include #include #include #include #endif /* _LIBMM_GLIB_H_ */ ModemManager-1.23.4-dev/libmm-glib/meson.build000066400000000000000000000112251456466623000211030ustar00rootroot00000000000000# SPDX-License-Identifier: GPL-2.0-or-later # Copyright (C) 2021 Iñigo Martinez libmm_glib_inc = include_directories('.') subdir('generated') headers = files( 'libmm-glib.h', 'mm-3gpp-profile.h', 'mm-bearer.h', 'mm-bearer-ip-config.h', 'mm-bearer-properties.h', 'mm-bearer-stats.h', 'mm-call-audio-format.h', 'mm-call.h', 'mm-call-properties.h', 'mm-cdma-manual-activation-properties.h', 'mm-cell-info.h', 'mm-cell-info-cdma.h', 'mm-cell-info-gsm.h', 'mm-cell-info-lte.h', 'mm-cell-info-nr5g.h', 'mm-cell-info-tdscdma.h', 'mm-cell-info-umts.h', 'mm-compat.h', 'mm-firmware-properties.h', 'mm-firmware-update-settings.h', 'mm-helper-types.h', 'mm-kernel-event-properties.h', 'mm-location-3gpp.h', 'mm-location-cdma-bs.h', 'mm-location-common.h', 'mm-location-gps-nmea.h', 'mm-location-gps-raw.h', 'mm-manager.h', 'mm-modem-3gpp.h', 'mm-modem-3gpp-profile-manager.h', 'mm-modem-3gpp-ussd.h', 'mm-modem-cdma.h', 'mm-modem-firmware.h', 'mm-modem.h', 'mm-modem-location.h', 'mm-modem-messaging.h', 'mm-modem-oma.h', 'mm-modem-sar.h', 'mm-modem-signal.h', 'mm-modem-simple.h', 'mm-modem-time.h', 'mm-modem-voice.h', 'mm-network-timezone.h', 'mm-nr5g-registration-settings.h', 'mm-object.h', 'mm-pco.h', 'mm-signal.h', 'mm-signal-threshold-properties.h', 'mm-sim.h', 'mm-simple-connect-properties.h', 'mm-simple-status.h', 'mm-sim-preferred-network.h', 'mm-sms.h', 'mm-sms-properties.h', 'mm-unlock-retries.h', ) install_headers( headers, install_dir: mm_glib_pkgincludedir, ) sources = files( 'mm-3gpp-profile.c', 'mm-bearer.c', 'mm-bearer-ip-config.c', 'mm-bearer-properties.c', 'mm-bearer-stats.c', 'mm-call-audio-format.c', 'mm-call.c', 'mm-call-properties.c', 'mm-cdma-manual-activation-properties.c', 'mm-cell-info.c', 'mm-cell-info-cdma.c', 'mm-cell-info-gsm.c', 'mm-cell-info-lte.c', 'mm-cell-info-nr5g.c', 'mm-cell-info-tdscdma.c', 'mm-cell-info-umts.c', 'mm-common-helpers.c', 'mm-compat.c', 'mm-firmware-properties.c', 'mm-firmware-update-settings.c', 'mm-helper-types.c', 'mm-kernel-event-properties.c', 'mm-location-3gpp.c', 'mm-location-cdma-bs.c', 'mm-location-gps-nmea.c', 'mm-location-gps-raw.c', 'mm-manager.c', 'mm-modem-3gpp.c', 'mm-modem-3gpp-profile-manager.c', 'mm-modem-3gpp-ussd.c', 'mm-modem.c', 'mm-modem-cdma.c', 'mm-modem-firmware.c', 'mm-modem-location.c', 'mm-modem-messaging.c', 'mm-modem-oma.c', 'mm-modem-sar.c', 'mm-modem-signal.c', 'mm-modem-simple.c', 'mm-modem-time.c', 'mm-modem-voice.c', 'mm-network-timezone.c', 'mm-nr5g-registration-settings.c', 'mm-object.c', 'mm-pco.c', 'mm-signal.c', 'mm-signal-threshold-properties.c', 'mm-sim.c', 'mm-simple-connect-properties.c', 'mm-simple-status.c', 'mm-sim-preferred-network.c', 'mm-sms.c', 'mm-sms-properties.c', 'mm-unlock-retries.c', ) deps = [include_dep] libname = 'mm-glib' libmm_glib = library( libname, version: mm_glib_version, sources: sources, include_directories: top_inc, dependencies: deps + [libmm_generated_dep], c_args: '-DLIBMM_GLIB_COMPILATION', install: true, ) libmm_glib_dep = declare_dependency( include_directories: libmm_glib_inc, # FIXME: glib_deps is included because `dependencies` parameter is not part of partial_dependency dependencies: deps + [glib_deps, libmm_generated_dep.partial_dependency(sources: true, includes: true)], link_with: libmm_glib, ) pkg.generate( libraries: libmm_glib, version: mm_version, name: libname, description: 'Library to control and monitor the ModemManager', subdirs: mm_glib_name, # FIXME: produced by the inhability of meson to use internal dependencies requires: ['gio-2.0', 'glib-2.0', 'gobject-2.0', 'ModemManager'], variables: 'exec_prefix=${prefix}', ) if enable_gir incs = [ 'Gio-2.0', 'GLib-2.0', 'GObject-2.0', ] gir_ns = 'ModemManager' gir_prefix = 'Mm' args = [ '-DLIBMM_GLIB_COMPILATION', '--identifier-prefix=' + gir_prefix.to_upper(), ] libmm_glib_gir = gnome.generate_gir( libmm_glib, sources: sources + headers + gen_sources + gen_headers + [mm_names_header, mm_version_header], includes: incs, namespace: gir_ns, nsversion: mm_gir_version, identifier_prefix: gir_prefix, symbol_prefix: gir_prefix.to_lower(), extra_args: args, header: 'libmm-glib.h', export_packages: libname, install: true, ) if enable_vapi libmm_glib_vapi = gnome.generate_vapi( 'libmm-glib', sources: libmm_glib_gir[0], packages: 'gio-2.0', install: true, ) endif endif if enable_tests subdir('tests') endif ModemManager-1.23.4-dev/libmm-glib/mm-3gpp-profile.c000066400000000000000000001014641456466623000220300ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * libmm-glib -- Access modem status & information from glib applications * * 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 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 Street, Fifth Floor, * Boston, MA 02110-1301 USA. * * Copyright (C) 2021-2022 Aleksander Morgado * Copyright (C) 2022 Google, Inc. */ #include #include "mm-enums-types.h" #include "mm-flags-types.h" #include "mm-errors-types.h" #include "mm-common-helpers.h" #include "mm-3gpp-profile.h" /** * SECTION: mm-3gpp-profile * @title: MM3gppProfile * @short_description: Helper object to handle 3GPP profile settings. * * The #MM3gppProfile is an object handling the settings associated * to a connection profile stored in the device. */ G_DEFINE_TYPE (MM3gppProfile, mm_3gpp_profile, G_TYPE_OBJECT) #define PROPERTY_ID "profile-id" #define PROPERTY_NAME "profile-name" #define PROPERTY_APN "apn" #define PROPERTY_ALLOWED_AUTH "allowed-auth" #define PROPERTY_USER "user" #define PROPERTY_PASSWORD "password" #define PROPERTY_IP_TYPE "ip-type" #define PROPERTY_APN_TYPE "apn-type" #define PROPERTY_ACCESS_TYPE_PREFERENCE "access-type-preference" #define PROPERTY_ENABLED "profile-enabled" #define PROPERTY_ROAMING_ALLOWANCE "roaming-allowance" #define PROPERTY_SOURCE "profile-source" struct _MM3gppProfilePrivate { gint profile_id; gchar *profile_name; gchar *apn; MMBearerIpFamily ip_type; MMBearerApnType apn_type; MMBearerAccessTypePreference access_type_preference; gboolean enabled; gboolean enabled_set; MMBearerRoamingAllowance roaming_allowance; MMBearerProfileSource profile_source; /* Optional authentication settings */ MMBearerAllowedAuth allowed_auth; gchar *user; gchar *password; }; /*****************************************************************************/ static gboolean cmp_str (const gchar *a, const gchar *b) { /* Strict match */ if ((!a && !b) || (a && b && g_strcmp0 (a, b) == 0)) return TRUE; /* Additional match, consider NULL and EMPTY string equal */ if ((!a && !b[0]) || (!b && !a[0])) return TRUE; return FALSE; } /** * mm_3gpp_profile_cmp: (skip) */ gboolean mm_3gpp_profile_cmp (MM3gppProfile *a, MM3gppProfile *b, GEqualFunc cmp_apn, MM3gppProfileCmpFlags flags) { /* When an input cmp_apn() methods is provided to compare the APNs, we must * run it twice, with the input arguments switched, as e.g. the mm_3gpp_cmp_apn_name() * method that may be given here treats both input arguments differently. */ if (cmp_apn && !cmp_apn (a->priv->apn, b->priv->apn) && !cmp_apn (b->priv->apn, a->priv->apn)) return FALSE; if (!cmp_apn && !cmp_str (a->priv->apn, b->priv->apn)) return FALSE; if (!(flags & MM_3GPP_PROFILE_CMP_FLAGS_NO_IP_TYPE) && (a->priv->ip_type != b->priv->ip_type)) return FALSE; if (!(flags & MM_3GPP_PROFILE_CMP_FLAGS_NO_PROFILE_ID) && (a->priv->profile_id != b->priv->profile_id)) return FALSE; if (!(flags & MM_3GPP_PROFILE_CMP_FLAGS_NO_AUTH) && ((a->priv->allowed_auth != b->priv->allowed_auth) || (!cmp_str (a->priv->user, b->priv->user)) || (!cmp_str (a->priv->password, b->priv->password)))) return FALSE; if (!(flags & MM_3GPP_PROFILE_CMP_FLAGS_NO_APN_TYPE) && (a->priv->apn_type != b->priv->apn_type)) return FALSE; if (!(flags & MM_3GPP_PROFILE_CMP_FLAGS_NO_PROFILE_NAME) && (a->priv->profile_name != b->priv->profile_name)) return FALSE; if (!(flags & MM_3GPP_PROFILE_CMP_FLAGS_NO_ACCESS_TYPE_PREFERENCE) && (a->priv->access_type_preference != b->priv->access_type_preference)) return FALSE; if (!(flags & MM_3GPP_PROFILE_CMP_FLAGS_NO_ENABLED) && ((a->priv->enabled != b->priv->enabled) || (a->priv->enabled_set != b->priv->enabled_set))) return FALSE; if (!(flags & MM_3GPP_PROFILE_CMP_FLAGS_NO_ROAMING_ALLOWANCE) && (a->priv->roaming_allowance != b->priv->roaming_allowance)) return FALSE; if (!(flags & MM_3GPP_PROFILE_CMP_FLAGS_NO_PROFILE_SOURCE) && (a->priv->profile_source != b->priv->profile_source)) return FALSE; return TRUE; } /*****************************************************************************/ /** * mm_3gpp_profile_set_profile_id: * @self: a #MM3gppProfile. * @profile_id: Numeric profile id to use, or #MM_3GPP_PROFILE_ID_UNKNOWN. * * Sets the profile id to use. * * If none specified explicitly, #MM_3GPP_PROFILE_ID_UNKNOWN is assumed. * * Since: 1.18 */ void mm_3gpp_profile_set_profile_id (MM3gppProfile *self, gint profile_id) { g_return_if_fail (MM_IS_3GPP_PROFILE (self)); self->priv->profile_id = profile_id; } /** * mm_3gpp_profile_get_profile_id: * @self: a #MM3gppProfile. * * Gets the profile id. * * Returns: the profile id.. * * Since: 1.18 */ gint mm_3gpp_profile_get_profile_id (MM3gppProfile *self) { g_return_val_if_fail (MM_IS_3GPP_PROFILE (self), MM_3GPP_PROFILE_ID_UNKNOWN); return self->priv->profile_id; } /*****************************************************************************/ /** * mm_3gpp_profile_set_apn: * @self: a #MM3gppProfile. * @apn: Name of the access point. * * Sets the name of the access point to use. * * Since: 1.18 */ void mm_3gpp_profile_set_apn (MM3gppProfile *self, const gchar *apn) { g_return_if_fail (MM_IS_3GPP_PROFILE (self)); g_free (self->priv->apn); self->priv->apn = g_strdup (apn); } /** * mm_3gpp_profile_get_apn: * @self: a #MM3gppProfile. * * Gets the name of the access point. * * Returns: (transfer none): the access point, or #NULL if not set. Do not free * the returned value, it is owned by @self. * * Since: 1.18 */ const gchar * mm_3gpp_profile_get_apn (MM3gppProfile *self) { g_return_val_if_fail (MM_IS_3GPP_PROFILE (self), NULL); return self->priv->apn; } /*****************************************************************************/ /** * mm_3gpp_profile_set_profile_name: * @self: a #MM3gppProfile. * @profile_name: Name of the profile. * * Sets the name of the profile. * * Since: 1.20 */ void mm_3gpp_profile_set_profile_name (MM3gppProfile *self, const gchar *profile_name) { g_return_if_fail (MM_IS_3GPP_PROFILE (self)); g_free (self->priv->profile_name); self->priv->profile_name = g_strdup (profile_name); } /** * mm_3gpp_profile_get_profile_name: * @self: a #MM3gppProfile. * * Gets the name of the profile. * * Returns: (transfer none): the profile name, or #NULL if not set. Do not free * the returned value, it is owned by @self. * * Since: 1.20 */ const gchar * mm_3gpp_profile_get_profile_name (MM3gppProfile *self) { g_return_val_if_fail (MM_IS_3GPP_PROFILE (self), NULL); return self->priv->profile_name; } /*****************************************************************************/ /** * mm_3gpp_profile_set_allowed_auth: * @self: a #MM3gppProfile. * @allowed_auth: a bitmask of #MMBearerAllowedAuth values. * %MM_BEARER_ALLOWED_AUTH_UNKNOWN may be given to request the modem-default * method. * * Sets the method to use when authenticating with the access point. * * Since: 1.18 */ void mm_3gpp_profile_set_allowed_auth (MM3gppProfile *self, MMBearerAllowedAuth allowed_auth) { g_return_if_fail (MM_IS_3GPP_PROFILE (self)); self->priv->allowed_auth = allowed_auth; } /** * mm_3gpp_profile_get_allowed_auth: * @self: a #MM3gppProfile. * * Gets the methods allowed to use when authenticating with the access point. * * Returns: a bitmask of #MMBearerAllowedAuth values, or * %MM_BEARER_ALLOWED_AUTH_UNKNOWN to request the modem-default method. * * Since: 1.18 */ MMBearerAllowedAuth mm_3gpp_profile_get_allowed_auth (MM3gppProfile *self) { g_return_val_if_fail (MM_IS_3GPP_PROFILE (self), MM_BEARER_ALLOWED_AUTH_UNKNOWN); return self->priv->allowed_auth; } /*****************************************************************************/ /** * mm_3gpp_profile_set_user: * @self: a #MM3gppProfile. * @user: the username * * Sets the username used to authenticate with the access point. * * Since: 1.18 */ void mm_3gpp_profile_set_user (MM3gppProfile *self, const gchar *user) { g_return_if_fail (MM_IS_3GPP_PROFILE (self)); g_free (self->priv->user); self->priv->user = g_strdup (user); } /** * mm_3gpp_profile_get_user: * @self: a #MM3gppProfile. * * Gets the username used to authenticate with the access point. * * Returns: (transfer none): the username, or #NULL if not set. Do not free the * returned value, it is owned by @self. * * Since: 1.18 */ const gchar * mm_3gpp_profile_get_user (MM3gppProfile *self) { g_return_val_if_fail (MM_IS_3GPP_PROFILE (self), NULL); return self->priv->user; } /*****************************************************************************/ /** * mm_3gpp_profile_set_password: * @self: a #MM3gppProfile. * @password: the password * * Sets the password used to authenticate with the access point. * * Since: 1.18 */ void mm_3gpp_profile_set_password (MM3gppProfile *self, const gchar *password) { g_return_if_fail (MM_IS_3GPP_PROFILE (self)); g_free (self->priv->password); self->priv->password = g_strdup (password); } /** * mm_3gpp_profile_get_password: * @self: a #MM3gppProfile. * * Gets the password used to authenticate with the access point. * * Returns: (transfer none): the password, or #NULL if not set. Do not free * the returned value, it is owned by @self. * * Since: 1.18 */ const gchar * mm_3gpp_profile_get_password (MM3gppProfile *self) { g_return_val_if_fail (MM_IS_3GPP_PROFILE (self), NULL); return self->priv->password; } /*****************************************************************************/ /** * mm_3gpp_profile_set_ip_type: * @self: a #MM3gppProfile. * @ip_type: a #MMBearerIpFamily. * * Sets the IP type to use. * * Since: 1.18 */ void mm_3gpp_profile_set_ip_type (MM3gppProfile *self, MMBearerIpFamily ip_type) { g_return_if_fail (MM_IS_3GPP_PROFILE (self)); self->priv->ip_type = ip_type; } /** * mm_3gpp_profile_get_ip_type: * @self: a #MM3gppProfile. * * Gets the IP type to use. * * Returns: a #MMBearerIpFamily. * * Since: 1.18 */ MMBearerIpFamily mm_3gpp_profile_get_ip_type (MM3gppProfile *self) { g_return_val_if_fail (MM_IS_3GPP_PROFILE (self), MM_BEARER_IP_FAMILY_NONE); return self->priv->ip_type; } /*****************************************************************************/ /** * mm_3gpp_profile_set_apn_type: * @self: a #MM3gppProfile. * @apn_type: a mask of #MMBearerApnType values. * * Sets the APN types to use. * * Since: 1.18 */ void mm_3gpp_profile_set_apn_type (MM3gppProfile *self, MMBearerApnType apn_type) { g_return_if_fail (MM_IS_3GPP_PROFILE (self)); self->priv->apn_type = apn_type; } /** * mm_3gpp_profile_get_apn_type: * @self: a #MM3gppProfile. * * Gets the APN types to use. * * Returns: a mask of #MMBearerApnType values. * * Since: 1.18 */ MMBearerApnType mm_3gpp_profile_get_apn_type (MM3gppProfile *self) { g_return_val_if_fail (MM_IS_3GPP_PROFILE (self), MM_BEARER_APN_TYPE_NONE); return self->priv->apn_type; } /*****************************************************************************/ /** * mm_3gpp_profile_set_access_type_preference: * @self: a #MM3gppProfile. * @access_type_preference: a #MMBearerAccessTypePreference. * * Sets the 5G network access type preference. * * Since: 1.20 */ void mm_3gpp_profile_set_access_type_preference (MM3gppProfile *self, MMBearerAccessTypePreference access_type_preference) { g_return_if_fail (MM_IS_3GPP_PROFILE (self)); self->priv->access_type_preference = access_type_preference; } /** * mm_3gpp_profile_get_access_type_preference: * @self: a #MM3gppProfile. * * Gets 5G network access type preference. * * Returns: a #MMBearerAccessTypePreference. * * Since: 1.20 */ MMBearerAccessTypePreference mm_3gpp_profile_get_access_type_preference (MM3gppProfile *self) { g_return_val_if_fail (MM_IS_3GPP_PROFILE (self), MM_BEARER_ACCESS_TYPE_PREFERENCE_NONE); return self->priv->access_type_preference; } /*****************************************************************************/ /** * mm_3gpp_profile_set_enabled: * @self: a #MM3gppProfile. * @enabled: boolean value. * * Sets the flag to indicate whether the profile is enabled or disabled. * * Since: 1.20 */ void mm_3gpp_profile_set_enabled (MM3gppProfile *self, gboolean enabled) { g_return_if_fail (MM_IS_3GPP_PROFILE (self)); self->priv->enabled_set = TRUE; self->priv->enabled = enabled; } /** * mm_3gpp_profile_get_enabled: * @self: a #MM3gppProfile. * * Checks whether the profile is enabled or disabled. * * Returns: %TRUE if the profile is enabled, %FALSE otherwise. * * Since: 1.20 */ gboolean mm_3gpp_profile_get_enabled (MM3gppProfile *self) { g_return_val_if_fail (MM_IS_3GPP_PROFILE (self), FALSE); return self->priv->enabled; } /*****************************************************************************/ /** * mm_3gpp_profile_set_roaming_allowance: * @self: a #MM3gppProfile. * @roaming_allowance: a mask of #MMBearerRoamingAllowance values. * * Sets the roaming allowance rules. * * Since: 1.20 */ void mm_3gpp_profile_set_roaming_allowance (MM3gppProfile *self, MMBearerRoamingAllowance roaming_allowance) { g_return_if_fail (MM_IS_3GPP_PROFILE (self)); self->priv->roaming_allowance = roaming_allowance; } /** * mm_3gpp_profile_get_roaming_allowance: * @self: a #MM3gppProfile. * * Gets the roaming allowance rules. * * Returns: a mask of #MMBearerRoamingAllowance values. * * Since: 1.20 */ MMBearerRoamingAllowance mm_3gpp_profile_get_roaming_allowance (MM3gppProfile *self) { g_return_val_if_fail (MM_IS_3GPP_PROFILE (self), MM_BEARER_ROAMING_ALLOWANCE_NONE); return self->priv->roaming_allowance; } /*****************************************************************************/ /** * mm_3gpp_profile_set_profile_source: * @self: a #MM3gppProfile. * @profile_source: a #MMBearerProfileSource. * * Sets profile source. * * Since: 1.20 */ void mm_3gpp_profile_set_profile_source (MM3gppProfile *self, MMBearerProfileSource profile_source) { g_return_if_fail (MM_IS_3GPP_PROFILE (self)); self->priv->profile_source = profile_source; } /** * mm_3gpp_profile_get_profile_source: * @self: a #MM3gppProfile. * * Gets the profile source. * * Returns: a #MMBearerProfileSource. * * Since: 1.20 */ MMBearerProfileSource mm_3gpp_profile_get_profile_source (MM3gppProfile *self) { g_return_val_if_fail (MM_IS_3GPP_PROFILE (self), MM_BEARER_PROFILE_SOURCE_UNKNOWN); return self->priv->profile_source; } /*****************************************************************************/ /** * mm_3gpp_profile_get_dictionary: (skip) */ GVariant * mm_3gpp_profile_get_dictionary (MM3gppProfile *self) { GVariantBuilder builder; /* We do allow NULL */ if (!self) return NULL; g_return_val_if_fail (MM_IS_3GPP_PROFILE (self), NULL); g_variant_builder_init (&builder, G_VARIANT_TYPE ("a{sv}")); g_variant_builder_add (&builder, "{sv}", PROPERTY_ID, g_variant_new_int32 (self->priv->profile_id)); if (self->priv->profile_name) g_variant_builder_add (&builder, "{sv}", PROPERTY_NAME, g_variant_new_string (self->priv->profile_name)); if (self->priv->apn) g_variant_builder_add (&builder, "{sv}", PROPERTY_APN, g_variant_new_string (self->priv->apn)); if (self->priv->allowed_auth != MM_BEARER_ALLOWED_AUTH_UNKNOWN) g_variant_builder_add (&builder, "{sv}", PROPERTY_ALLOWED_AUTH, g_variant_new_uint32 (self->priv->allowed_auth)); if (self->priv->user) g_variant_builder_add (&builder, "{sv}", PROPERTY_USER, g_variant_new_string (self->priv->user)); if (self->priv->password) g_variant_builder_add (&builder, "{sv}", PROPERTY_PASSWORD, g_variant_new_string (self->priv->password)); if (self->priv->ip_type != MM_BEARER_IP_FAMILY_NONE) g_variant_builder_add (&builder, "{sv}", PROPERTY_IP_TYPE, g_variant_new_uint32 (self->priv->ip_type)); if (self->priv->apn_type != MM_BEARER_APN_TYPE_NONE) g_variant_builder_add (&builder, "{sv}", PROPERTY_APN_TYPE, g_variant_new_uint32 (self->priv->apn_type)); if (self->priv->access_type_preference != MM_BEARER_ACCESS_TYPE_PREFERENCE_NONE) g_variant_builder_add (&builder, "{sv}", PROPERTY_ACCESS_TYPE_PREFERENCE, g_variant_new_uint32 (self->priv->access_type_preference)); if (self->priv->profile_source != MM_BEARER_PROFILE_SOURCE_UNKNOWN) g_variant_builder_add (&builder, "{sv}", PROPERTY_SOURCE, g_variant_new_uint32 (self->priv->profile_source)); if (self->priv->roaming_allowance != MM_BEARER_ROAMING_ALLOWANCE_NONE) g_variant_builder_add (&builder, "{sv}", PROPERTY_ROAMING_ALLOWANCE, g_variant_new_uint32 (self->priv->roaming_allowance)); if (self->priv->enabled_set) g_variant_builder_add (&builder, "{sv}", PROPERTY_ENABLED, g_variant_new_boolean (self->priv->enabled)); return g_variant_ref_sink (g_variant_builder_end (&builder)); } /*****************************************************************************/ gboolean mm_3gpp_profile_consume_string (MM3gppProfile *self, const gchar *key, const gchar *value, GError **error) { g_return_val_if_fail (MM_IS_3GPP_PROFILE (self), FALSE); if (g_str_equal (key, PROPERTY_ID)) { gint profile_id; if (!mm_get_int_from_str (value, &profile_id)) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS, "invalid profile id value given: %s", value); return FALSE; } mm_3gpp_profile_set_profile_id (self, profile_id); } else if (g_str_equal (key, PROPERTY_NAME)) mm_3gpp_profile_set_profile_name (self, value); else if (g_str_equal (key, PROPERTY_APN)) mm_3gpp_profile_set_apn (self, value); else if (g_str_equal (key, PROPERTY_ALLOWED_AUTH)) { GError *inner_error = NULL; MMBearerAllowedAuth allowed_auth; allowed_auth = mm_common_get_allowed_auth_from_string (value, &inner_error); if (inner_error) { g_propagate_error (error, inner_error); return FALSE; } mm_3gpp_profile_set_allowed_auth (self, allowed_auth); } else if (g_str_equal (key, PROPERTY_USER)) mm_3gpp_profile_set_user (self, value); else if (g_str_equal (key, PROPERTY_PASSWORD)) mm_3gpp_profile_set_password (self, value); else if (g_str_equal (key, PROPERTY_IP_TYPE)) { GError *inner_error = NULL; MMBearerIpFamily ip_type; ip_type = mm_common_get_ip_type_from_string (value, &inner_error); if (inner_error) { g_propagate_error (error, inner_error); return FALSE; } mm_3gpp_profile_set_ip_type (self, ip_type); } else if (g_str_equal (key, PROPERTY_APN_TYPE)) { GError *inner_error = NULL; MMBearerApnType apn_type; apn_type = mm_common_get_apn_type_from_string (value, &inner_error); if (inner_error) { g_propagate_error (error, inner_error); return FALSE; } mm_3gpp_profile_set_apn_type (self, apn_type); } else if (g_str_equal (key, PROPERTY_ACCESS_TYPE_PREFERENCE)) { GError *inner_error = NULL; MMBearerAccessTypePreference access_type_preference; access_type_preference = mm_common_get_access_type_preference_from_string (value, &inner_error); if (inner_error) { g_propagate_error (error, inner_error); return FALSE; } mm_3gpp_profile_set_access_type_preference (self, access_type_preference); } else if (g_str_equal (key, PROPERTY_ENABLED)) { GError *inner_error = NULL; gboolean profile_enabled; profile_enabled = mm_common_get_boolean_from_string (value, &inner_error); if (inner_error) { g_propagate_error (error, inner_error); return FALSE; } mm_3gpp_profile_set_enabled (self, profile_enabled); } else if (g_str_equal (key, PROPERTY_SOURCE)) { GError *inner_error = NULL; MMBearerProfileSource profile_source; profile_source = mm_common_get_profile_source_from_string (value, &inner_error); if (inner_error) { g_propagate_error (error, inner_error); return FALSE; } mm_3gpp_profile_set_profile_source (self, profile_source); } else { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "Invalid properties string, unsupported key '%s'", key); return FALSE; } return TRUE; } typedef struct { MM3gppProfile *properties; GError *error; } ParseKeyValueContext; static gboolean key_value_foreach (const gchar *key, const gchar *value, ParseKeyValueContext *ctx) { return mm_3gpp_profile_consume_string (ctx->properties, key, value, &ctx->error); } /** * mm_3gpp_profile_new_from_string: (skip) */ MM3gppProfile * mm_3gpp_profile_new_from_string (const gchar *str, GError **error) { ParseKeyValueContext ctx; ctx.error = NULL; ctx.properties = mm_3gpp_profile_new (); mm_common_parse_key_value_string (str, &ctx.error, (MMParseKeyValueForeachFn)key_value_foreach, &ctx); /* If error, destroy the object */ if (ctx.error) { g_propagate_error (error, ctx.error); g_object_unref (ctx.properties); ctx.properties = NULL; } return ctx.properties; } /*****************************************************************************/ gboolean mm_3gpp_profile_consume_variant (MM3gppProfile *self, const gchar *key, GVariant *value, GError **error) { g_return_val_if_fail (MM_IS_3GPP_PROFILE (self), FALSE); if (g_str_equal (key, PROPERTY_ID)) mm_3gpp_profile_set_profile_id ( self, g_variant_get_int32 (value)); else if (g_str_equal (key, PROPERTY_NAME)) mm_3gpp_profile_set_profile_name ( self, g_variant_get_string (value, NULL)); else if (g_str_equal (key, PROPERTY_APN)) mm_3gpp_profile_set_apn ( self, g_variant_get_string (value, NULL)); else if (g_str_equal (key, PROPERTY_ALLOWED_AUTH)) mm_3gpp_profile_set_allowed_auth ( self, g_variant_get_uint32 (value)); else if (g_str_equal (key, PROPERTY_USER)) mm_3gpp_profile_set_user ( self, g_variant_get_string (value, NULL)); else if (g_str_equal (key, PROPERTY_PASSWORD)) mm_3gpp_profile_set_password ( self, g_variant_get_string (value, NULL)); else if (g_str_equal (key, PROPERTY_IP_TYPE)) mm_3gpp_profile_set_ip_type ( self, g_variant_get_uint32 (value)); else if (g_str_equal (key, PROPERTY_APN_TYPE)) mm_3gpp_profile_set_apn_type ( self, g_variant_get_uint32 (value)); else if (g_str_equal (key, PROPERTY_ACCESS_TYPE_PREFERENCE)) mm_3gpp_profile_set_access_type_preference ( self, g_variant_get_uint32 (value)); else if (g_str_equal (key, PROPERTY_ENABLED)) mm_3gpp_profile_set_enabled ( self, g_variant_get_boolean (value)); else if (g_str_equal (key, PROPERTY_SOURCE)) mm_3gpp_profile_set_profile_source ( self, g_variant_get_uint32 (value)); else if (g_str_equal (key, PROPERTY_ROAMING_ALLOWANCE)) mm_3gpp_profile_set_roaming_allowance ( self, g_variant_get_uint32 (value)); else { /* Set error */ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS, "Invalid self dictionary, unexpected key '%s'", key); return FALSE; } return TRUE; } /** * mm_3gpp_profile_new_from_dictionary: (skip) */ MM3gppProfile * mm_3gpp_profile_new_from_dictionary (GVariant *dictionary, GError **error) { GError *inner_error = NULL; GVariantIter iter; gchar *key; GVariant *value; MM3gppProfile *properties; properties = mm_3gpp_profile_new (); if (!dictionary) return properties; if (!g_variant_is_of_type (dictionary, G_VARIANT_TYPE ("a{sv}"))) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS, "Cannot create Bearer properties from dictionary: " "invalid variant type received"); g_object_unref (properties); return NULL; } g_variant_iter_init (&iter, dictionary); while (!inner_error && g_variant_iter_next (&iter, "{sv}", &key, &value)) { mm_3gpp_profile_consume_variant (properties, key, value, &inner_error); g_free (key); g_variant_unref (value); } /* If error, destroy the object */ if (inner_error) { g_propagate_error (error, inner_error); g_object_unref (properties); properties = NULL; } return properties; } /*****************************************************************************/ /** * mm_3gpp_profile_print: (skip) */ GPtrArray * mm_3gpp_profile_print (MM3gppProfile *self, gboolean show_personal_info) { GPtrArray *array; g_autofree gchar *ip_type_str = NULL; g_autofree gchar *apn_type_str = NULL; g_autofree gchar *roaming_allowance_str = NULL; g_autofree gchar *allowed_auth_str = NULL; const gchar *aux; array = g_ptr_array_new_with_free_func ((GDestroyNotify)g_free); if (self->priv->profile_id != MM_3GPP_PROFILE_ID_UNKNOWN) g_ptr_array_add (array, g_strdup_printf (PROPERTY_ID ": %d", self->priv->profile_id)); if (self->priv->profile_name) g_ptr_array_add (array, g_strdup_printf (PROPERTY_NAME ": %s", self->priv->profile_name)); if (self->priv->enabled_set) { aux = mm_common_str_boolean (self->priv->enabled); g_ptr_array_add (array, g_strdup_printf (PROPERTY_ENABLED ": %s", aux)); } if (self->priv->apn) g_ptr_array_add (array, g_strdup_printf (PROPERTY_APN ": %s", self->priv->apn)); if (self->priv->ip_type != MM_BEARER_IP_FAMILY_NONE) { ip_type_str = mm_bearer_ip_family_build_string_from_mask (self->priv->ip_type); g_ptr_array_add (array, g_strdup_printf (PROPERTY_IP_TYPE ": %s", ip_type_str)); } if (self->priv->apn_type != MM_BEARER_APN_TYPE_NONE) { apn_type_str = mm_bearer_apn_type_build_string_from_mask (self->priv->apn_type); g_ptr_array_add (array, g_strdup_printf (PROPERTY_APN_TYPE ": %s", apn_type_str)); } if (self->priv->access_type_preference != MM_BEARER_ACCESS_TYPE_PREFERENCE_NONE) { aux = mm_bearer_access_type_preference_get_string (self->priv->access_type_preference); g_ptr_array_add (array, g_strdup_printf (PROPERTY_ACCESS_TYPE_PREFERENCE ": %s", aux)); } if (self->priv->roaming_allowance != MM_BEARER_ROAMING_ALLOWANCE_NONE) { roaming_allowance_str = mm_bearer_roaming_allowance_build_string_from_mask (self->priv->roaming_allowance); g_ptr_array_add (array, g_strdup_printf (PROPERTY_ROAMING_ALLOWANCE ": %s", roaming_allowance_str)); } if (self->priv->profile_source != MM_BEARER_PROFILE_SOURCE_UNKNOWN) { aux = mm_bearer_profile_source_get_string (self->priv->profile_source); g_ptr_array_add (array, g_strdup_printf (PROPERTY_SOURCE ": %s", aux)); } if (self->priv->allowed_auth != MM_BEARER_ALLOWED_AUTH_NONE) { allowed_auth_str = mm_bearer_allowed_auth_build_string_from_mask (self->priv->allowed_auth); g_ptr_array_add (array, g_strdup_printf (PROPERTY_ALLOWED_AUTH ": %s", allowed_auth_str)); } if (self->priv->user) g_ptr_array_add (array, g_strdup_printf (PROPERTY_USER ": %s", mm_common_str_personal_info (self->priv->user, show_personal_info))); if (self->priv->password) g_ptr_array_add (array, g_strdup_printf (PROPERTY_PASSWORD ": %s", mm_common_str_personal_info (self->priv->password, show_personal_info))); return array; } /*****************************************************************************/ /** * mm_3gpp_profile_new: * * Creates a new empty #MM3gppProfile. * * Returns: (transfer full): a #MM3gppProfile. The returned value should be freed with g_object_unref(). * * Since: 1.18 */ MM3gppProfile * mm_3gpp_profile_new (void) { return MM_3GPP_PROFILE (g_object_new (MM_TYPE_3GPP_PROFILE, NULL)); } static void mm_3gpp_profile_init (MM3gppProfile *self) { self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, MM_TYPE_3GPP_PROFILE, MM3gppProfilePrivate); /* Some defaults */ self->priv->profile_id = MM_3GPP_PROFILE_ID_UNKNOWN; self->priv->allowed_auth = MM_BEARER_ALLOWED_AUTH_UNKNOWN; self->priv->ip_type = MM_BEARER_IP_FAMILY_NONE; self->priv->apn_type = MM_BEARER_APN_TYPE_NONE; self->priv->access_type_preference = MM_BEARER_ACCESS_TYPE_PREFERENCE_NONE; self->priv->enabled = TRUE; self->priv->roaming_allowance = MM_BEARER_ROAMING_ALLOWANCE_NONE; self->priv->profile_source = MM_BEARER_PROFILE_SOURCE_UNKNOWN; } static void finalize (GObject *object) { MM3gppProfile *self = MM_3GPP_PROFILE (object); g_free (self->priv->profile_name); g_free (self->priv->apn); g_free (self->priv->user); g_free (self->priv->password); G_OBJECT_CLASS (mm_3gpp_profile_parent_class)->finalize (object); } static void mm_3gpp_profile_class_init (MM3gppProfileClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); g_type_class_add_private (object_class, sizeof (MM3gppProfilePrivate)); object_class->finalize = finalize; } ModemManager-1.23.4-dev/libmm-glib/mm-3gpp-profile.h000066400000000000000000000203161456466623000220310ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * libmm-glib -- Access modem status & information from glib applications * * 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 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 Street, Fifth Floor, * Boston, MA 02110-1301 USA. * * Copyright (C) 2021-2022 Aleksander Morgado * Copyright (C) 2022 Google, Inc. */ #ifndef MM_3GPP_PROFILE_H #define MM_3GPP_PROFILE_H #include #include #if !defined (__LIBMM_GLIB_H_INSIDE__) && !defined (LIBMM_GLIB_COMPILATION) # error "Only can be included directly." #endif G_BEGIN_DECLS /** * MM_3GPP_PROFILE_ID_UNKNOWN: * * This value may be specified in the 'profile-id' property When the user * creates a new #MM3gppProfile, to indicate that the real profile id should * be assigned by the device. */ #define MM_3GPP_PROFILE_ID_UNKNOWN -1 #define MM_TYPE_3GPP_PROFILE (mm_3gpp_profile_get_type ()) #define MM_3GPP_PROFILE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_3GPP_PROFILE, MM3gppProfile)) #define MM_3GPP_PROFILE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_3GPP_PROFILE, MM3gppProfileClass)) #define MM_IS_3GPP_PROFILE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_3GPP_PROFILE)) #define MM_IS_3GPP_PROFILE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_3GPP_PROFILE)) #define MM_3GPP_PROFILE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_3GPP_PROFILE, MM3gppProfileClass)) typedef struct _MM3gppProfile MM3gppProfile; typedef struct _MM3gppProfileClass MM3gppProfileClass; typedef struct _MM3gppProfilePrivate MM3gppProfilePrivate; /** * MM3gppProfile: * * The #MM3gppProfile structure contains private data and should * only be accessed using the provided API. */ struct _MM3gppProfile { /*< private >*/ GObject parent; MM3gppProfilePrivate *priv; }; struct _MM3gppProfileClass { /*< private >*/ GObjectClass parent; }; GType mm_3gpp_profile_get_type (void); G_DEFINE_AUTOPTR_CLEANUP_FUNC (MM3gppProfile, g_object_unref) MM3gppProfile *mm_3gpp_profile_new (void); void mm_3gpp_profile_set_profile_id (MM3gppProfile *self, gint profile_id); void mm_3gpp_profile_set_profile_name (MM3gppProfile *self, const gchar *profile_name); void mm_3gpp_profile_set_apn (MM3gppProfile *self, const gchar *apn); void mm_3gpp_profile_set_allowed_auth (MM3gppProfile *self, MMBearerAllowedAuth allowed_auth); void mm_3gpp_profile_set_user (MM3gppProfile *self, const gchar *user); void mm_3gpp_profile_set_password (MM3gppProfile *self, const gchar *password); void mm_3gpp_profile_set_ip_type (MM3gppProfile *self, MMBearerIpFamily ip_type); void mm_3gpp_profile_set_apn_type (MM3gppProfile *self, MMBearerApnType apn_type); void mm_3gpp_profile_set_access_type_preference (MM3gppProfile *self, MMBearerAccessTypePreference access_type_preference); void mm_3gpp_profile_set_enabled (MM3gppProfile *self, gboolean enabled); void mm_3gpp_profile_set_roaming_allowance (MM3gppProfile *self, MMBearerRoamingAllowance roaming_allowance); void mm_3gpp_profile_set_profile_source (MM3gppProfile *self, MMBearerProfileSource profile_source); gint mm_3gpp_profile_get_profile_id (MM3gppProfile *self); const gchar *mm_3gpp_profile_get_profile_name (MM3gppProfile *self); const gchar *mm_3gpp_profile_get_apn (MM3gppProfile *self); MMBearerAllowedAuth mm_3gpp_profile_get_allowed_auth (MM3gppProfile *self); const gchar *mm_3gpp_profile_get_user (MM3gppProfile *self); const gchar *mm_3gpp_profile_get_password (MM3gppProfile *self); MMBearerIpFamily mm_3gpp_profile_get_ip_type (MM3gppProfile *self); MMBearerApnType mm_3gpp_profile_get_apn_type (MM3gppProfile *self); MMBearerAccessTypePreference mm_3gpp_profile_get_access_type_preference (MM3gppProfile *self); gboolean mm_3gpp_profile_get_enabled (MM3gppProfile *self); MMBearerRoamingAllowance mm_3gpp_profile_get_roaming_allowance (MM3gppProfile *self); MMBearerProfileSource mm_3gpp_profile_get_profile_source (MM3gppProfile *self); /*****************************************************************************/ /* ModemManager/libmm-glib/mmcli specific methods */ #if defined (_LIBMM_INSIDE_MM) || \ defined (_LIBMM_INSIDE_MMCLI) || \ defined (LIBMM_GLIB_COMPILATION) MM3gppProfile *mm_3gpp_profile_new_from_string (const gchar *str, GError **error); MM3gppProfile *mm_3gpp_profile_new_from_dictionary (GVariant *dictionary, GError **error); GVariant *mm_3gpp_profile_get_dictionary (MM3gppProfile *self); gboolean mm_3gpp_profile_consume_string (MM3gppProfile *self, const gchar *key, const gchar *value, GError **error); gboolean mm_3gpp_profile_consume_variant (MM3gppProfile *self, const gchar *key, GVariant *value, GError **error); typedef enum { MM_3GPP_PROFILE_CMP_FLAGS_NONE = 0, MM_3GPP_PROFILE_CMP_FLAGS_NO_PROFILE_ID = 1 << 1, MM_3GPP_PROFILE_CMP_FLAGS_NO_PROFILE_NAME = 1 << 2, MM_3GPP_PROFILE_CMP_FLAGS_NO_AUTH = 1 << 3, MM_3GPP_PROFILE_CMP_FLAGS_NO_APN_TYPE = 1 << 4, MM_3GPP_PROFILE_CMP_FLAGS_NO_IP_TYPE = 1 << 5, MM_3GPP_PROFILE_CMP_FLAGS_NO_ACCESS_TYPE_PREFERENCE = 1 << 6, MM_3GPP_PROFILE_CMP_FLAGS_NO_ENABLED = 1 << 7, MM_3GPP_PROFILE_CMP_FLAGS_NO_ROAMING_ALLOWANCE = 1 << 8, MM_3GPP_PROFILE_CMP_FLAGS_NO_PROFILE_SOURCE = 1 << 9, } MM3gppProfileCmpFlags; gboolean mm_3gpp_profile_cmp (MM3gppProfile *a, MM3gppProfile *b, GEqualFunc cmp_apn, MM3gppProfileCmpFlags flags); GPtrArray *mm_3gpp_profile_print (MM3gppProfile *self, gboolean show_personal_info); #endif G_END_DECLS #endif /* MM_3GPP_PROFILE_H */ ModemManager-1.23.4-dev/libmm-glib/mm-bearer-ip-config.c000066400000000000000000000320331456466623000226250ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * libmm-glib -- Access modem status & information from glib applications * * 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 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 Street, Fifth Floor, * Boston, MA 02110-1301 USA. * * Copyright (C) 2012 Google, Inc. */ #include #include "mm-errors-types.h" #include "mm-bearer-ip-config.h" /** * SECTION: mm-bearer-ip-config * @title: MMBearerIpConfig * @short_description: Helper object to handle IP configuration. * * The #MMBearerIpConfig is an object handling the IP configuration required by * the bearer to finish the connection. * * This object is retrieved with either mm_bearer_get_ipv4_config(), * mm_bearer_peek_ipv4_config(), mm_bearer_get_ipv6_config() or * mm_bearer_peek_ipv6_config(). */ G_DEFINE_TYPE (MMBearerIpConfig, mm_bearer_ip_config, G_TYPE_OBJECT); #define PROPERTY_METHOD "method" #define PROPERTY_ADDRESS "address" #define PROPERTY_PREFIX "prefix" #define PROPERTY_DNS1 "dns1" #define PROPERTY_DNS2 "dns2" #define PROPERTY_DNS3 "dns3" #define PROPERTY_GATEWAY "gateway" #define PROPERTY_MTU "mtu" struct _MMBearerIpConfigPrivate { MMBearerIpMethod method; gchar *address; guint prefix; gchar **dns; gchar *gateway; guint mtu; }; /*****************************************************************************/ /** * mm_bearer_ip_config_get_method: * @self: a #MMBearerIpConfig. * * Gets the IP method to be used with this bearer. * * Returns: a #MMBearerIpMethod. * * Since: 1.0 */ MMBearerIpMethod mm_bearer_ip_config_get_method (MMBearerIpConfig *self) { g_return_val_if_fail (MM_IS_BEARER_IP_CONFIG (self), MM_BEARER_IP_METHOD_UNKNOWN); return self->priv->method; } /** * mm_bearer_ip_config_set_method: (skip) */ void mm_bearer_ip_config_set_method (MMBearerIpConfig *self, MMBearerIpMethod method) { g_return_if_fail (MM_IS_BEARER_IP_CONFIG (self)); self->priv->method = method; } /*****************************************************************************/ /** * mm_bearer_ip_config_get_address: * @self: a #MMBearerIpConfig. * * Gets the IP address to be used with this bearer. * * Returns: a string with the IP address, or #NULL if unknown. Do not free the * returned value, it is owned by @self. * * Since: 1.0 */ const gchar * mm_bearer_ip_config_get_address (MMBearerIpConfig *self) { g_return_val_if_fail (MM_IS_BEARER_IP_CONFIG (self), NULL); return self->priv->address; } /** * mm_bearer_ip_config_set_address: (skip) */ void mm_bearer_ip_config_set_address (MMBearerIpConfig *self, const gchar *address) { g_return_if_fail (MM_IS_BEARER_IP_CONFIG (self)); g_free (self->priv->address); self->priv->address = g_strdup (address); } /*****************************************************************************/ /** * mm_bearer_ip_config_get_prefix: * @self: a #MMBearerIpConfig. * * Gets the network prefix to be used with this bearer. * * Returns: the network prefix. * * Since: 1.0 */ guint mm_bearer_ip_config_get_prefix (MMBearerIpConfig *self) { g_return_val_if_fail (MM_IS_BEARER_IP_CONFIG (self), 0); return self->priv->prefix; } /** * mm_bearer_ip_config_set_prefix: (skip) */ void mm_bearer_ip_config_set_prefix (MMBearerIpConfig *self, guint prefix) { g_return_if_fail (MM_IS_BEARER_IP_CONFIG (self)); self->priv->prefix = prefix; } /*****************************************************************************/ /** * mm_bearer_ip_config_get_dns: * @self: a #MMBearerIpConfig. * * Gets the list of IP addresses of DNS servers to be used with this bearer. * * Returns: (transfer none) (array zero-terminated=1): a %NULL-terminated array * of strings. Do not free the returned value, it is owned by @self. * * Since: 1.0 */ const gchar ** mm_bearer_ip_config_get_dns (MMBearerIpConfig *self) { g_return_val_if_fail (MM_IS_BEARER_IP_CONFIG (self), NULL); return (const gchar **)self->priv->dns; } /** * mm_bearer_ip_config_set_dns: (skip) */ void mm_bearer_ip_config_set_dns (MMBearerIpConfig *self, const gchar **dns) { g_return_if_fail (MM_IS_BEARER_IP_CONFIG (self)); g_strfreev (self->priv->dns); self->priv->dns = g_strdupv ((gchar **)dns); } /*****************************************************************************/ /** * mm_bearer_ip_config_get_gateway: * @self: a #MMBearerIpConfig. * * Gets the IP address of the gateway to be used with this bearer. * * Returns: a string with the IP address, or #NULL if unknown. Do not free the * returned value, it is owned by @self. * * Since: 1.0 */ const gchar * mm_bearer_ip_config_get_gateway (MMBearerIpConfig *self) { g_return_val_if_fail (MM_IS_BEARER_IP_CONFIG (self), NULL); return self->priv->gateway; } /** * mm_bearer_ip_config_set_gateway: (skip) */ void mm_bearer_ip_config_set_gateway (MMBearerIpConfig *self, const gchar *gateway) { g_return_if_fail (MM_IS_BEARER_IP_CONFIG (self)); g_free (self->priv->gateway); self->priv->gateway = g_strdup (gateway); } /*****************************************************************************/ /** * mm_bearer_ip_config_get_mtu: * @self: a #MMBearerIpConfig. * * Gets the MTU to be used with this bearer. * * Returns: the MTU. * * Since: 1.4 */ guint mm_bearer_ip_config_get_mtu (MMBearerIpConfig *self) { g_return_val_if_fail (MM_IS_BEARER_IP_CONFIG (self), 0); return self->priv->mtu; } /** * mm_bearer_ip_config_set_mtu: (skip) */ void mm_bearer_ip_config_set_mtu (MMBearerIpConfig *self, guint mtu) { g_return_if_fail (MM_IS_BEARER_IP_CONFIG (self)); self->priv->mtu = mtu; } /*****************************************************************************/ /** * mm_bearer_ip_config_get_dictionary: (skip) */ GVariant * mm_bearer_ip_config_get_dictionary (MMBearerIpConfig *self) { GVariantBuilder builder; /* We do allow self==NULL. We'll just report method=unknown in this case */ if (self) g_return_val_if_fail (MM_IS_BEARER_IP_CONFIG (self), NULL); g_variant_builder_init (&builder, G_VARIANT_TYPE ("a{sv}")); g_variant_builder_add (&builder, "{sv}", PROPERTY_METHOD, g_variant_new_uint32 (self ? self->priv->method : MM_BEARER_IP_METHOD_UNKNOWN)); if (self) { if (self->priv->address) g_variant_builder_add (&builder, "{sv}", PROPERTY_ADDRESS, g_variant_new_string (self->priv->address)); if (self->priv->prefix) g_variant_builder_add (&builder, "{sv}", PROPERTY_PREFIX, g_variant_new_uint32 (self->priv->prefix)); if (self->priv->dns && self->priv->dns[0]) { g_variant_builder_add (&builder, "{sv}", PROPERTY_DNS1, g_variant_new_string (self->priv->dns[0])); if (self->priv->dns[1]) { g_variant_builder_add (&builder, "{sv}", PROPERTY_DNS2, g_variant_new_string (self->priv->dns[1])); if (self->priv->dns[2]) { g_variant_builder_add (&builder, "{sv}", PROPERTY_DNS3, g_variant_new_string (self->priv->dns[2])); } } } if (self->priv->gateway) g_variant_builder_add (&builder, "{sv}", PROPERTY_GATEWAY, g_variant_new_string (self->priv->gateway)); if (self->priv->mtu) g_variant_builder_add (&builder, "{sv}", PROPERTY_MTU, g_variant_new_uint32 (self->priv->mtu)); } return g_variant_builder_end (&builder); } /*****************************************************************************/ /** * mm_bearer_ip_config_new_from_dictionary: (skip) */ MMBearerIpConfig * mm_bearer_ip_config_new_from_dictionary (GVariant *dictionary, GError **error) { GVariantIter iter; gchar *key; GVariant *value; MMBearerIpConfig *self; gchar *dns_array[4] = { 0 }; gboolean method_received = FALSE; self = mm_bearer_ip_config_new (); if (!dictionary) return self; if (!g_variant_is_of_type (dictionary, G_VARIANT_TYPE ("a{sv}"))) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS, "Cannot create IP config from dictionary: " "invalid variant type received"); g_object_unref (self); return NULL; } g_variant_iter_init (&iter, dictionary); while (g_variant_iter_next (&iter, "{sv}", &key, &value)) { if (g_str_equal (key, PROPERTY_METHOD)) { method_received = TRUE; mm_bearer_ip_config_set_method ( self, (MMBearerIpMethod) g_variant_get_uint32 (value)); } else if (g_str_equal (key, PROPERTY_ADDRESS)) mm_bearer_ip_config_set_address ( self, g_variant_get_string (value, NULL)); else if (g_str_equal (key, PROPERTY_PREFIX)) mm_bearer_ip_config_set_prefix ( self, g_variant_get_uint32 (value)); else if (g_str_equal (key, PROPERTY_DNS1)) { g_free (dns_array[0]); dns_array[0] = g_variant_dup_string (value, NULL); } else if (g_str_equal (key, PROPERTY_DNS2)) { g_free (dns_array[1]); dns_array[1] = g_variant_dup_string (value, NULL); } else if (g_str_equal (key, PROPERTY_DNS3)) { g_free (dns_array[2]); dns_array[2] = g_variant_dup_string (value, NULL); } else if (g_str_equal (key, PROPERTY_GATEWAY)) mm_bearer_ip_config_set_gateway ( self, g_variant_get_string (value, NULL)); else if (g_str_equal (key, PROPERTY_MTU)) mm_bearer_ip_config_set_mtu ( self, g_variant_get_uint32 (value)); g_free (key); g_variant_unref (value); } if (dns_array[0]) mm_bearer_ip_config_set_dns (self, (const gchar **)dns_array); if (!method_received) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS, "Couldn't create IP config from dictionary: 'method not given'"); g_clear_object (&self); } g_free (dns_array[0]); g_free (dns_array[1]); g_free (dns_array[2]); return self; } /*****************************************************************************/ /** * mm_bearer_ip_config_new: (skip) */ MMBearerIpConfig * mm_bearer_ip_config_new (void) { return (MM_BEARER_IP_CONFIG ( g_object_new (MM_TYPE_BEARER_IP_CONFIG, NULL))); } static void mm_bearer_ip_config_init (MMBearerIpConfig *self) { self->priv = G_TYPE_INSTANCE_GET_PRIVATE ((self), MM_TYPE_BEARER_IP_CONFIG, MMBearerIpConfigPrivate); /* Some defaults */ self->priv->method = MM_BEARER_IP_METHOD_UNKNOWN; } static void finalize (GObject *object) { MMBearerIpConfig *self = MM_BEARER_IP_CONFIG (object); g_free (self->priv->address); g_free (self->priv->gateway); g_strfreev (self->priv->dns); G_OBJECT_CLASS (mm_bearer_ip_config_parent_class)->finalize (object); } static void mm_bearer_ip_config_class_init (MMBearerIpConfigClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); g_type_class_add_private (object_class, sizeof (MMBearerIpConfigPrivate)); object_class->finalize = finalize; } ModemManager-1.23.4-dev/libmm-glib/mm-bearer-ip-config.h000066400000000000000000000102651456466623000226350ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * libmm-glib -- Access modem status & information from glib applications * * 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 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 Street, Fifth Floor, * Boston, MA 02110-1301 USA. * * Copyright (C) 2012 Google, Inc. */ #ifndef MM_BEARER_IP_CONFIG_H #define MM_BEARER_IP_CONFIG_H #if !defined (__LIBMM_GLIB_H_INSIDE__) && !defined (LIBMM_GLIB_COMPILATION) #error "Only can be included directly." #endif #include #include G_BEGIN_DECLS #define MM_TYPE_BEARER_IP_CONFIG (mm_bearer_ip_config_get_type ()) #define MM_BEARER_IP_CONFIG(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_BEARER_IP_CONFIG, MMBearerIpConfig)) #define MM_BEARER_IP_CONFIG_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_BEARER_IP_CONFIG, MMBearerIpConfigClass)) #define MM_IS_BEARER_IP_CONFIG(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_BEARER_IP_CONFIG)) #define MM_IS_BEARER_IP_CONFIG_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_BEARER_IP_CONFIG)) #define MM_BEARER_IP_CONFIG_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_BEARER_IP_CONFIG, MMBearerIpConfigClass)) typedef struct _MMBearerIpConfig MMBearerIpConfig; typedef struct _MMBearerIpConfigClass MMBearerIpConfigClass; typedef struct _MMBearerIpConfigPrivate MMBearerIpConfigPrivate; /** * MMBearerIpConfig: * * The #MMBearerIpConfig structure contains private data and should * only be accessed using the provided API. */ struct _MMBearerIpConfig { /*< private >*/ GObject parent; MMBearerIpConfigPrivate *priv; }; struct _MMBearerIpConfigClass { /*< private >*/ GObjectClass parent; }; GType mm_bearer_ip_config_get_type (void); G_DEFINE_AUTOPTR_CLEANUP_FUNC (MMBearerIpConfig, g_object_unref) MMBearerIpMethod mm_bearer_ip_config_get_method (MMBearerIpConfig *self); const gchar *mm_bearer_ip_config_get_address (MMBearerIpConfig *self); guint mm_bearer_ip_config_get_prefix (MMBearerIpConfig *self); const gchar **mm_bearer_ip_config_get_dns (MMBearerIpConfig *self); const gchar *mm_bearer_ip_config_get_gateway (MMBearerIpConfig *self); guint mm_bearer_ip_config_get_mtu (MMBearerIpConfig *self); /*****************************************************************************/ /* ModemManager/libmm-glib/mmcli specific methods */ #if defined (_LIBMM_INSIDE_MM) || \ defined (_LIBMM_INSIDE_MMCLI) || \ defined (LIBMM_GLIB_COMPILATION) MMBearerIpConfig *mm_bearer_ip_config_new (void); MMBearerIpConfig *mm_bearer_ip_config_new_from_dictionary (GVariant *dictionary, GError **error); void mm_bearer_ip_config_set_method (MMBearerIpConfig *self, MMBearerIpMethod ip_method); void mm_bearer_ip_config_set_address (MMBearerIpConfig *self, const gchar *address); void mm_bearer_ip_config_set_prefix (MMBearerIpConfig *self, guint prefix); void mm_bearer_ip_config_set_dns (MMBearerIpConfig *self, const gchar **dns); void mm_bearer_ip_config_set_gateway (MMBearerIpConfig *self, const gchar *gateway); void mm_bearer_ip_config_set_mtu (MMBearerIpConfig *self, guint mtu); GVariant *mm_bearer_ip_config_get_dictionary (MMBearerIpConfig *self); #endif G_END_DECLS #endif /* MM_BEARER_IP_CONFIG_H */ ModemManager-1.23.4-dev/libmm-glib/mm-bearer-properties.c000066400000000000000000001027041456466623000231510ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * libmm-glib -- Access modem status & information from glib applications * * 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 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 Street, Fifth Floor, * Boston, MA 02110-1301 USA. * * Copyright (C) 2011-2022 Aleksander Morgado * Copyright (C) 2022 Google, Inc. */ #include #include "mm-errors-types.h" #include "mm-enums-types.h" #include "mm-flags-types.h" #include "mm-common-helpers.h" #include "mm-bearer-properties.h" /** * SECTION: mm-bearer-properties * @title: MMBearerProperties * @short_description: Helper object to handle bearer properties. * * The #MMBearerProperties is an object handling the properties requested * to ModemManager when creating a new bearer. * * This object is created by the user and passed to ModemManager with either * mm_modem_create_bearer() or mm_modem_create_bearer_sync(). */ G_DEFINE_TYPE (MMBearerProperties, mm_bearer_properties, G_TYPE_OBJECT) #define PROPERTY_ALLOW_ROAMING "allow-roaming" #define PROPERTY_RM_PROTOCOL "rm-protocol" #define PROPERTY_MULTIPLEX "multiplex" /* special property to force send initial EPS bearer settings to the modem, * even if new bearer properties match the existing ones. * It is only applicable while setting the initial bearer settings and does * not apply to any other bearer setting operations like simple connect etc. * This property is not sent to the modem*/ #define PROPERTY_FORCE "force" /* no longer used properties */ #define DEPRECATED_PROPERTY_NUMBER "number" struct _MMBearerPropertiesPrivate { /* The 3GPP profile is a subset of the bearer properties */ MM3gppProfile *profile; /* Roaming allowance */ gboolean allow_roaming_set; gboolean allow_roaming; /* Force send to modem */ gboolean force_set; gboolean force; /* Protocol of the Rm interface */ MMModemCdmaRmProtocol rm_protocol; /* Multiplex support */ MMBearerMultiplexSupport multiplex; }; /*****************************************************************************/ /** * mm_bearer_properties_set_profile_name: * @self: a #MMBearerProperties. * @profile_name: Name of the profile. * * Sets the name of the profile to use when connecting. * * Since: 1.20 */ void mm_bearer_properties_set_profile_name (MMBearerProperties *self, const gchar *profile_name) { g_return_if_fail (MM_IS_BEARER_PROPERTIES (self)); mm_3gpp_profile_set_profile_name (self->priv->profile, profile_name); } /** * mm_bearer_properties_get_profile_name: * @self: a #MMBearerProperties. * * Gets the name of the profile to use when connecting. * * Returns: (transfer none): the profile name, or #NULL if not set. Do not free * the returned value, it is owned by @self. * * Since: 1.20 */ const gchar * mm_bearer_properties_get_profile_name (MMBearerProperties *self) { g_return_val_if_fail (MM_IS_BEARER_PROPERTIES (self), NULL); return mm_3gpp_profile_get_profile_name (self->priv->profile); } /*****************************************************************************/ /** * mm_bearer_properties_set_apn: * @self: a #MMBearerProperties. * @apn: Name of the access point. * * Sets the name of the access point to use when connecting. * * Since: 1.0 */ void mm_bearer_properties_set_apn (MMBearerProperties *self, const gchar *apn) { g_return_if_fail (MM_IS_BEARER_PROPERTIES (self)); mm_3gpp_profile_set_apn (self->priv->profile, apn); } /** * mm_bearer_properties_get_apn: * @self: a #MMBearerProperties. * * Gets the name of the access point to use when connecting. * * Returns: (transfer none): the access point, or #NULL if not set. Do not free * the returned value, it is owned by @self. * * Since: 1.0 */ const gchar * mm_bearer_properties_get_apn (MMBearerProperties *self) { g_return_val_if_fail (MM_IS_BEARER_PROPERTIES (self), NULL); return mm_3gpp_profile_get_apn (self->priv->profile); } /*****************************************************************************/ /** * mm_bearer_properties_set_allowed_auth: * @self: a #MMBearerProperties. * @allowed_auth: a bitmask of #MMBearerAllowedAuth values. * %MM_BEARER_ALLOWED_AUTH_UNKNOWN may be given to request the modem-default * method. * * Sets the authentication method to use. * * Since: 1.0 */ void mm_bearer_properties_set_allowed_auth (MMBearerProperties *self, MMBearerAllowedAuth allowed_auth) { g_return_if_fail (MM_IS_BEARER_PROPERTIES (self)); mm_3gpp_profile_set_allowed_auth (self->priv->profile, allowed_auth); } /** * mm_bearer_properties_get_allowed_auth: * @self: a #MMBearerProperties. * * Gets the authentication methods allowed in the connection. * * Returns: a bitmask of #MMBearerAllowedAuth values, or * %MM_BEARER_ALLOWED_AUTH_UNKNOWN to request the modem-default method. * * Since: 1.0 */ MMBearerAllowedAuth mm_bearer_properties_get_allowed_auth (MMBearerProperties *self) { g_return_val_if_fail (MM_IS_BEARER_PROPERTIES (self), MM_BEARER_ALLOWED_AUTH_UNKNOWN); return mm_3gpp_profile_get_allowed_auth (self->priv->profile); } /*****************************************************************************/ /** * mm_bearer_properties_set_user: * @self: a #MMBearerProperties. * @user: the username * * Sets the username used to authenticate with the access point. * * Since: 1.0 */ void mm_bearer_properties_set_user (MMBearerProperties *self, const gchar *user) { g_return_if_fail (MM_IS_BEARER_PROPERTIES (self)); mm_3gpp_profile_set_user (self->priv->profile, user); } /** * mm_bearer_properties_get_user: * @self: a #MMBearerProperties. * * Gets the username used to authenticate with the access point. * * Returns: (transfer none): the username, or #NULL if not set. Do not free the * returned value, it is owned by @self. * * Since: 1.0 */ const gchar * mm_bearer_properties_get_user (MMBearerProperties *self) { g_return_val_if_fail (MM_IS_BEARER_PROPERTIES (self), NULL); return mm_3gpp_profile_get_user (self->priv->profile); } /*****************************************************************************/ /** * mm_bearer_properties_set_password: * @self: a #MMBearerProperties. * @password: the password * * Sets the password used to authenticate with the access point. * * Since: 1.0 */ void mm_bearer_properties_set_password (MMBearerProperties *self, const gchar *password) { g_return_if_fail (MM_IS_BEARER_PROPERTIES (self)); mm_3gpp_profile_set_password (self->priv->profile, password); } /** * mm_bearer_properties_get_password: * @self: a #MMBearerProperties. * * Gets the password used to authenticate with the access point. * * Returns: (transfer none): the password, or #NULL if not set. Do not free * the returned value, it is owned by @self. * * Since: 1.0 */ const gchar * mm_bearer_properties_get_password (MMBearerProperties *self) { g_return_val_if_fail (MM_IS_BEARER_PROPERTIES (self), NULL); return mm_3gpp_profile_get_password (self->priv->profile); } /*****************************************************************************/ /** * mm_bearer_properties_set_ip_type: * @self: a #MMBearerProperties. * @ip_type: a #MMBearerIpFamily. * * Sets the IP type to use. * * Since: 1.0 */ void mm_bearer_properties_set_ip_type (MMBearerProperties *self, MMBearerIpFamily ip_type) { g_return_if_fail (MM_IS_BEARER_PROPERTIES (self)); mm_3gpp_profile_set_ip_type (self->priv->profile, ip_type); } /** * mm_bearer_properties_get_ip_type: * @self: a #MMBearerProperties. * * Sets the IP type to use. * * Returns: a #MMBearerIpFamily. * * Since: 1.0 */ MMBearerIpFamily mm_bearer_properties_get_ip_type (MMBearerProperties *self) { g_return_val_if_fail (MM_IS_BEARER_PROPERTIES (self), MM_BEARER_IP_FAMILY_NONE); return mm_3gpp_profile_get_ip_type (self->priv->profile); } /*****************************************************************************/ /** * mm_bearer_properties_set_apn_type: * @self: a #MMBearerProperties. * @apn_type: a mask of #MMBearerApnType values. * * Sets the APN types to use. * * Since: 1.18 */ void mm_bearer_properties_set_apn_type (MMBearerProperties *self, MMBearerApnType apn_type) { g_return_if_fail (MM_IS_BEARER_PROPERTIES (self)); mm_3gpp_profile_set_apn_type (self->priv->profile, apn_type); } /** * mm_bearer_properties_get_apn_type: * @self: a #MMBearerProperties. * * Gets the APN types to use. * * Returns: a mask of #MMBearerApnType values. * * Since: 1.18 */ MMBearerApnType mm_bearer_properties_get_apn_type (MMBearerProperties *self) { g_return_val_if_fail (MM_IS_BEARER_PROPERTIES (self), MM_BEARER_APN_TYPE_NONE); return mm_3gpp_profile_get_apn_type (self->priv->profile); } /*****************************************************************************/ /** * mm_bearer_properties_set_profile_id: * @self: a #MMBearerProperties. * @profile_id: a profile id. * * Sets the profile ID to use. * * Since: 1.18 */ void mm_bearer_properties_set_profile_id (MMBearerProperties *self, gint profile_id) { g_return_if_fail (MM_IS_BEARER_PROPERTIES (self)); mm_3gpp_profile_set_profile_id (self->priv->profile, profile_id); } /** * mm_bearer_properties_get_profile_id: * @self: a #MMBearerProperties. * * Gets the profile ID to use. * * Returns: the profile id. * * Since: 1.18 */ gint mm_bearer_properties_get_profile_id (MMBearerProperties *self) { g_return_val_if_fail (MM_IS_BEARER_PROPERTIES (self), MM_3GPP_PROFILE_ID_UNKNOWN); return mm_3gpp_profile_get_profile_id (self->priv->profile); } /*****************************************************************************/ /** * mm_bearer_properties_set_access_type_preference: * @self: a #MMBearerProperties. * @access_type_preference: a #MMBearerAccessTypePreference value. * * Sets the 5G network access type preference. * * Since: 1.20 */ void mm_bearer_properties_set_access_type_preference (MMBearerProperties *self, MMBearerAccessTypePreference access_type_preference) { g_return_if_fail (MM_IS_BEARER_PROPERTIES (self)); mm_3gpp_profile_set_access_type_preference (self->priv->profile, access_type_preference); } /** * mm_bearer_properties_get_access_type_preference: * @self: a #MMBearerProperties. * * Gets the 5G network access type preference. * * Returns: a #MMBearerAccessTypePreference value. * * Since: 1.20 */ MMBearerAccessTypePreference mm_bearer_properties_get_access_type_preference (MMBearerProperties *self) { g_return_val_if_fail (MM_IS_BEARER_PROPERTIES (self), MM_BEARER_ACCESS_TYPE_PREFERENCE_NONE); return mm_3gpp_profile_get_access_type_preference (self->priv->profile); } /*****************************************************************************/ /** * mm_bearer_properties_set_roaming_allowance: * @self: a #MMBearerProperties. * @roaming_allowance: a mask of #MMBearerRoamingAllowance values * * Sets the roaming allowance rules. * * Since: 1.20 */ void mm_bearer_properties_set_roaming_allowance (MMBearerProperties *self, MMBearerRoamingAllowance roaming_allowance) { g_return_if_fail (MM_IS_BEARER_PROPERTIES (self)); mm_3gpp_profile_set_roaming_allowance (self->priv->profile, roaming_allowance); } /** * mm_bearer_properties_get_roaming_allowance: * @self: a #MMBearerProperties. * * Gets the roaming allowance rules. * * Returns: a mask of #MMBearerRoamingAllowance values. * * Since: 1.20 */ MMBearerRoamingAllowance mm_bearer_properties_get_roaming_allowance (MMBearerProperties *self) { g_return_val_if_fail (MM_IS_BEARER_PROPERTIES (self), MM_BEARER_ROAMING_ALLOWANCE_NONE); return mm_3gpp_profile_get_roaming_allowance (self->priv->profile); } /*****************************************************************************/ /** * mm_bearer_properties_set_allow_roaming: * @self: a #MMBearerProperties. * @allow_roaming: boolean value. * * Sets the flag to indicate whether roaming is allowed or not in the * connection. * * Since: 1.0 */ void mm_bearer_properties_set_allow_roaming (MMBearerProperties *self, gboolean allow_roaming) { g_return_if_fail (MM_IS_BEARER_PROPERTIES (self)); self->priv->allow_roaming = allow_roaming; self->priv->allow_roaming_set = TRUE; } /** * mm_bearer_properties_get_allow_roaming: * @self: a #MMBearerProperties. * * Checks whether roaming is allowed in the connection. * * Returns: %TRUE if roaming is allowed, %FALSE otherwise. * * Since: 1.0 */ gboolean mm_bearer_properties_get_allow_roaming (MMBearerProperties *self) { g_return_val_if_fail (MM_IS_BEARER_PROPERTIES (self), FALSE); return self->priv->allow_roaming; } /*****************************************************************************/ /** * mm_bearer_properties_set_force: * @self: a #MMBearerProperties. * @force: boolean value. * * Sets the flag to indicate whether initial EPS bearer settings should be * forced sent to the modem even if they match existing properties. * This method does not apply when the bearer properties are used for other * operations. * * Since: 1.24 */ void mm_bearer_properties_set_force (MMBearerProperties *self, gboolean force) { g_return_if_fail (MM_IS_BEARER_PROPERTIES (self)); self->priv->force = force; self->priv->force_set = TRUE; } /** * mm_bearer_properties_get_force: * @self: a #MMBearerProperties. * * Checks whether initial EPS bearer settings should be forced sent * to the modem.This method does not apply when the bearer properties are * used for other operations. * * Returns: %TRUE if force send is required, %FALSE otherwise. * * Since: 1.24 */ gboolean mm_bearer_properties_get_force (MMBearerProperties *self) { g_return_val_if_fail (MM_IS_BEARER_PROPERTIES (self), FALSE); return self->priv->force; } /*****************************************************************************/ /** * mm_bearer_properties_set_rm_protocol: * @self: a #MMBearerProperties. * @protocol: a #MMModemCdmaRmProtocol. * * Sets the RM protocol to use in the CDMA connection. * * Since: 1.0 */ void mm_bearer_properties_set_rm_protocol (MMBearerProperties *self, MMModemCdmaRmProtocol protocol) { g_return_if_fail (MM_IS_BEARER_PROPERTIES (self)); self->priv->rm_protocol = protocol; } /** * mm_bearer_properties_get_rm_protocol: * @self: a #MMBearerProperties. * * Gets the RM protocol requested to use in the CDMA connection. * * Returns: a #MMModemCdmaRmProtocol. * * Since: 1.0 */ MMModemCdmaRmProtocol mm_bearer_properties_get_rm_protocol (MMBearerProperties *self) { g_return_val_if_fail (MM_IS_BEARER_PROPERTIES (self), MM_MODEM_CDMA_RM_PROTOCOL_UNKNOWN); return self->priv->rm_protocol; } /*****************************************************************************/ /** * mm_bearer_properties_set_multiplex: * @self: a #MMBearerProperties. * @multiplex: a #MMBearerMultiplexSupport. * * Gets the type of multiplex support requested by the user. * * Since: 1.18 */ void mm_bearer_properties_set_multiplex (MMBearerProperties *self, MMBearerMultiplexSupport multiplex) { g_return_if_fail (MM_IS_BEARER_PROPERTIES (self)); self->priv->multiplex = multiplex; } /** * mm_bearer_properties_get_multiplex: * @self: a #MMBearerProperties. * * Gets the type of multiplex support requested by the user. * * Returns: a #MMBearerMultiplexSupport. * * Since: 1.18 */ MMBearerMultiplexSupport mm_bearer_properties_get_multiplex (MMBearerProperties *self) { g_return_val_if_fail (MM_IS_BEARER_PROPERTIES (self), MM_BEARER_MULTIPLEX_SUPPORT_UNKNOWN); return self->priv->multiplex; } /*****************************************************************************/ /** * mm_bearer_properties_peek_3gpp_profile: (skip) */ MM3gppProfile * mm_bearer_properties_peek_3gpp_profile (MMBearerProperties *self) { return self->priv->profile; } /*****************************************************************************/ /** * mm_bearer_properties_get_dictionary: (skip) */ GVariant * mm_bearer_properties_get_dictionary (MMBearerProperties *self) { GVariantBuilder builder; GVariantIter iter; gchar *key; GVariant *value; GVariant *profile_dictionary; /* We do allow NULL */ if (!self) return NULL; g_return_val_if_fail (MM_IS_BEARER_PROPERTIES (self), NULL); g_variant_builder_init (&builder, G_VARIANT_TYPE ("a{sv}")); if (self->priv->allow_roaming_set) g_variant_builder_add (&builder, "{sv}", PROPERTY_ALLOW_ROAMING, g_variant_new_boolean (self->priv->allow_roaming)); if (self->priv->rm_protocol) g_variant_builder_add (&builder, "{sv}", PROPERTY_RM_PROTOCOL, g_variant_new_uint32 (self->priv->rm_protocol)); if (self->priv->multiplex) g_variant_builder_add (&builder, "{sv}", PROPERTY_MULTIPLEX, g_variant_new_uint32 (self->priv->multiplex)); if (self->priv->force_set) g_variant_builder_add (&builder, "{sv}", PROPERTY_FORCE, g_variant_new_boolean (self->priv->force)); /* Merge dictionaries */ profile_dictionary = mm_3gpp_profile_get_dictionary (self->priv->profile); g_variant_iter_init (&iter, profile_dictionary); while (g_variant_iter_next (&iter, "{sv}", &key, &value)) { g_variant_builder_add (&builder, "{sv}", key, value); g_variant_unref (value); g_free (key); } g_variant_unref (profile_dictionary); return g_variant_ref_sink (g_variant_builder_end (&builder)); } /*****************************************************************************/ /** * mm_bearer_properties_consume_string: (skip) */ gboolean mm_bearer_properties_consume_string (MMBearerProperties *self, const gchar *key, const gchar *value, GError **error) { GError *inner_error = NULL; g_return_val_if_fail (MM_IS_BEARER_PROPERTIES (self), FALSE); /* First, check if we can consume this as bearer properties */ if (mm_3gpp_profile_consume_string (self->priv->profile, key, value, &inner_error)) return TRUE; /* Unknown keys are reported as unsupported. Any other error is right away * fatal (e.g. an invalid value given to a known profile property) */ if (!g_error_matches (inner_error, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED)) { g_propagate_error (error, inner_error); return FALSE; } /* On unsupported errors, try with the bearer specific properties */ g_clear_error (&inner_error); if (g_str_equal (key, PROPERTY_ALLOW_ROAMING)) { gboolean allow_roaming; allow_roaming = mm_common_get_boolean_from_string (value, &inner_error); if (!inner_error) mm_bearer_properties_set_allow_roaming (self, allow_roaming); } else if (g_str_equal (key, PROPERTY_RM_PROTOCOL)) { MMModemCdmaRmProtocol protocol; protocol = mm_common_get_rm_protocol_from_string (value, &inner_error); if (!inner_error) mm_bearer_properties_set_rm_protocol (self, protocol); } else if (g_str_equal (key, PROPERTY_MULTIPLEX)) { MMBearerMultiplexSupport multiplex; multiplex = mm_common_get_multiplex_support_from_string (value, &inner_error); if (!inner_error) mm_bearer_properties_set_multiplex (self, multiplex); } else if (g_str_equal (key, DEPRECATED_PROPERTY_NUMBER)) { /* NO-OP */ } else if (g_str_equal (key, PROPERTY_FORCE)) { gboolean force; force = mm_common_get_boolean_from_string (value, &inner_error); if (!inner_error) mm_bearer_properties_set_force (self, force); } else { inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "Invalid properties string, unsupported key '%s'", key); } if (inner_error) { g_propagate_error (error, inner_error); return FALSE; } return TRUE; } typedef struct { MMBearerProperties *properties; GError *error; } ParseKeyValueContext; static gboolean key_value_foreach (const gchar *key, const gchar *value, ParseKeyValueContext *ctx) { return mm_bearer_properties_consume_string (ctx->properties, key, value, &ctx->error); } /** * mm_bearer_properties_new_from_string: (skip) */ MMBearerProperties * mm_bearer_properties_new_from_string (const gchar *str, GError **error) { ParseKeyValueContext ctx; ctx.error = NULL; ctx.properties = mm_bearer_properties_new (); mm_common_parse_key_value_string (str, &ctx.error, (MMParseKeyValueForeachFn)key_value_foreach, &ctx); /* If error, destroy the object */ if (ctx.error) { g_propagate_error (error, ctx.error); g_object_unref (ctx.properties); ctx.properties = NULL; } return ctx.properties; } /*****************************************************************************/ /** * mm_bearer_properties_consume_variant: (skip) */ gboolean mm_bearer_properties_consume_variant (MMBearerProperties *self, const gchar *key, GVariant *value, GError **error) { g_return_val_if_fail (MM_IS_BEARER_PROPERTIES (self), FALSE); /* First, check if we can consume this as profile properties */ if (mm_3gpp_profile_consume_variant (self->priv->profile, key, value, NULL)) return TRUE; if (g_str_equal (key, PROPERTY_ALLOW_ROAMING)) mm_bearer_properties_set_allow_roaming (self, g_variant_get_boolean (value)); else if (g_str_equal (key, PROPERTY_RM_PROTOCOL)) mm_bearer_properties_set_rm_protocol (self, g_variant_get_uint32 (value)); else if (g_str_equal (key, PROPERTY_MULTIPLEX)) mm_bearer_properties_set_multiplex (self, g_variant_get_uint32 (value)); else if (g_str_equal (key, DEPRECATED_PROPERTY_NUMBER)) { /* NO-OP */ } else if (g_str_equal (key, PROPERTY_FORCE)) mm_bearer_properties_set_force (self, g_variant_get_boolean (value)); else { /* Set error */ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS, "Invalid properties dictionary, unexpected key '%s'", key); return FALSE; } return TRUE; } /** * mm_bearer_properties_new_from_dictionary: (skip) */ MMBearerProperties * mm_bearer_properties_new_from_dictionary (GVariant *dictionary, GError **error) { GError *inner_error = NULL; GVariantIter iter; gchar *key; GVariant *value; MMBearerProperties *properties; properties = mm_bearer_properties_new (); if (!dictionary) return properties; if (!g_variant_is_of_type (dictionary, G_VARIANT_TYPE ("a{sv}"))) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS, "Cannot create Bearer properties from dictionary: " "invalid variant type received"); g_object_unref (properties); return NULL; } g_variant_iter_init (&iter, dictionary); while (!inner_error && g_variant_iter_next (&iter, "{sv}", &key, &value)) { mm_bearer_properties_consume_variant (properties, key, value, &inner_error); g_free (key); g_variant_unref (value); } /* If error, destroy the object */ if (inner_error) { g_propagate_error (error, inner_error); g_object_unref (properties); properties = NULL; } return properties; } /*****************************************************************************/ static gboolean cmp_str (const gchar *a, const gchar *b, MMBearerPropertiesCmpFlags flags) { /* Strict match */ if ((!a && !b) || (a && b && g_strcmp0 (a, b) == 0)) return TRUE; /* Additional loose match, consider NULL and EMPTY string equal */ if (flags & MM_BEARER_PROPERTIES_CMP_FLAGS_LOOSE) { if ((!a && !b[0]) || (!b && !a[0])) return TRUE; } return FALSE; } static gboolean cmp_ip_type (MMBearerIpFamily a, MMBearerIpFamily b, MMBearerPropertiesCmpFlags flags) { /* Strict match */ if (a == b) return TRUE; /* Additional loose match NONE == IPV4 */ if (flags & MM_BEARER_PROPERTIES_CMP_FLAGS_LOOSE) { if ((a == MM_BEARER_IP_FAMILY_NONE && b == MM_BEARER_IP_FAMILY_IPV4) || (b == MM_BEARER_IP_FAMILY_NONE && a == MM_BEARER_IP_FAMILY_IPV4)) return TRUE; } return FALSE; } static gboolean cmp_apn_type (MMBearerApnType a, MMBearerApnType b, MMBearerPropertiesCmpFlags flags) { /* Strict match */ if (a == b) return TRUE; /* Additional loose match NONE == DEFAULT */ if (flags & MM_BEARER_PROPERTIES_CMP_FLAGS_LOOSE) { if ((a == MM_BEARER_APN_TYPE_NONE && b == MM_BEARER_APN_TYPE_DEFAULT) || (b == MM_BEARER_APN_TYPE_NONE && a == MM_BEARER_APN_TYPE_DEFAULT)) return TRUE; } return FALSE; } static gboolean cmp_allowed_auth (MMBearerAllowedAuth a, MMBearerAllowedAuth b, MMBearerPropertiesCmpFlags flags) { /* Strict match */ if (a == b) return TRUE; /* Additional loose match UNKNOWN == NONE */ /* MBIM and QMI fallback to CHAP when a username or password is present, but no authentication type was provided */ if (flags & MM_BEARER_PROPERTIES_CMP_FLAGS_LOOSE) { if ((a == MM_BEARER_ALLOWED_AUTH_UNKNOWN && b == MM_BEARER_ALLOWED_AUTH_NONE) || (b == MM_BEARER_ALLOWED_AUTH_UNKNOWN && a == MM_BEARER_ALLOWED_AUTH_NONE) || (a == MM_BEARER_ALLOWED_AUTH_UNKNOWN && b == MM_BEARER_ALLOWED_AUTH_CHAP) || (b == MM_BEARER_ALLOWED_AUTH_UNKNOWN && a == MM_BEARER_ALLOWED_AUTH_CHAP) ) return TRUE; } return FALSE; } /** * mm_bearer_properties_cmp: (skip) */ gboolean mm_bearer_properties_cmp (MMBearerProperties *a, MMBearerProperties *b, MMBearerPropertiesCmpFlags flags) { /* we don't have any other need to compare profiles, so just compare the properties here */ if (!cmp_str (mm_3gpp_profile_get_apn (a->priv->profile), mm_3gpp_profile_get_apn (b->priv->profile), flags)) return FALSE; if (!cmp_ip_type (mm_3gpp_profile_get_ip_type (a->priv->profile), mm_3gpp_profile_get_ip_type (b->priv->profile), flags)) return FALSE; if (!cmp_allowed_auth (mm_3gpp_profile_get_allowed_auth (a->priv->profile), mm_3gpp_profile_get_allowed_auth (b->priv->profile), flags)) return FALSE; if (!cmp_str (mm_3gpp_profile_get_user (a->priv->profile), mm_3gpp_profile_get_user (b->priv->profile), flags)) return FALSE; if (!(flags & MM_BEARER_PROPERTIES_CMP_FLAGS_NO_PASSWORD) && !cmp_str (mm_3gpp_profile_get_password (a->priv->profile), mm_3gpp_profile_get_password (b->priv->profile), flags)) return FALSE; if (!(flags & MM_BEARER_PROPERTIES_CMP_FLAGS_NO_APN_TYPE) && !cmp_apn_type (mm_3gpp_profile_get_apn_type (a->priv->profile), mm_3gpp_profile_get_apn_type (b->priv->profile), flags)) return FALSE; if (!(flags & MM_BEARER_PROPERTIES_CMP_FLAGS_NO_PROFILE_ID) && (mm_3gpp_profile_get_profile_id (a->priv->profile) != mm_3gpp_profile_get_profile_id (b->priv->profile))) return FALSE; if (!(flags & MM_BEARER_PROPERTIES_CMP_FLAGS_NO_PROFILE_NAME) && !cmp_str (mm_3gpp_profile_get_profile_name (a->priv->profile), mm_3gpp_profile_get_profile_name (b->priv->profile), flags)) return FALSE; if (!(flags & MM_BEARER_PROPERTIES_CMP_FLAGS_NO_ACCESS_TYPE_PREFERENCE) && (mm_3gpp_profile_get_access_type_preference (a->priv->profile) != mm_3gpp_profile_get_access_type_preference (b->priv->profile))) return FALSE; if (!(flags & MM_BEARER_PROPERTIES_CMP_FLAGS_NO_ROAMING_ALLOWANCE) && (mm_3gpp_profile_get_roaming_allowance (a->priv->profile) != mm_3gpp_profile_get_roaming_allowance (b->priv->profile))) return FALSE; if (!(flags & MM_BEARER_PROPERTIES_CMP_FLAGS_NO_ALLOW_ROAMING)) { if (a->priv->allow_roaming != b->priv->allow_roaming) return FALSE; if (a->priv->allow_roaming_set != b->priv->allow_roaming_set) return FALSE; } if (!(flags & MM_BEARER_PROPERTIES_CMP_FLAGS_NO_RM_PROTOCOL)) { if (a->priv->rm_protocol != b->priv->rm_protocol) return FALSE; } if (a->priv->multiplex != b->priv->multiplex) return FALSE; return TRUE; } /*****************************************************************************/ /** * mm_bearer_properties_print: (skip) */ GPtrArray * mm_bearer_properties_print (MMBearerProperties *self, gboolean show_personal_info) { GPtrArray *array; const gchar *aux; array = mm_3gpp_profile_print (self->priv->profile, show_personal_info); if (self->priv->allow_roaming_set) { aux = mm_common_str_boolean (self->priv->allow_roaming); g_ptr_array_add (array, g_strdup_printf (PROPERTY_ALLOW_ROAMING ": %s", aux)); } if (self->priv->multiplex != MM_BEARER_MULTIPLEX_SUPPORT_UNKNOWN) { aux = mm_bearer_multiplex_support_get_string (self->priv->multiplex); g_ptr_array_add (array, g_strdup_printf (PROPERTY_MULTIPLEX ": %s", aux)); } if (self->priv->rm_protocol != MM_MODEM_CDMA_RM_PROTOCOL_UNKNOWN) { aux = mm_modem_cdma_rm_protocol_get_string (self->priv->rm_protocol); g_ptr_array_add (array, g_strdup_printf (PROPERTY_RM_PROTOCOL ": %s", aux)); } if (self->priv->force_set) { aux = mm_common_str_boolean (self->priv->force); g_ptr_array_add (array, g_strdup_printf (PROPERTY_FORCE ": %s", aux)); } return array; } /*****************************************************************************/ /** * mm_bearer_properties_new_from_profile: (skip) */ MMBearerProperties * mm_bearer_properties_new_from_profile (MM3gppProfile *profile, GError **error) { MMBearerProperties *self; self = mm_bearer_properties_new (); g_clear_object (&self->priv->profile); self->priv->profile = g_object_ref (profile); return self; } /*****************************************************************************/ /** * mm_bearer_properties_new: * * Creates a new empty #MMBearerProperties. * * Returns: (transfer full): a #MMBearerProperties. The returned value should be freed with g_object_unref(). * * Since: 1.0 */ MMBearerProperties * mm_bearer_properties_new (void) { return (MM_BEARER_PROPERTIES ( g_object_new (MM_TYPE_BEARER_PROPERTIES, NULL))); } static void mm_bearer_properties_init (MMBearerProperties *self) { self->priv = G_TYPE_INSTANCE_GET_PRIVATE ((self), MM_TYPE_BEARER_PROPERTIES, MMBearerPropertiesPrivate); /* Some defaults */ self->priv->profile = mm_3gpp_profile_new (); self->priv->allow_roaming = TRUE; self->priv->rm_protocol = MM_MODEM_CDMA_RM_PROTOCOL_UNKNOWN; self->priv->multiplex = MM_BEARER_MULTIPLEX_SUPPORT_UNKNOWN; self->priv->force = FALSE; } static void finalize (GObject *object) { MMBearerProperties *self = MM_BEARER_PROPERTIES (object); g_object_unref (self->priv->profile); G_OBJECT_CLASS (mm_bearer_properties_parent_class)->finalize (object); } static void mm_bearer_properties_class_init (MMBearerPropertiesClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); g_type_class_add_private (object_class, sizeof (MMBearerPropertiesPrivate)); object_class->finalize = finalize; } ModemManager-1.23.4-dev/libmm-glib/mm-bearer-properties.h000066400000000000000000000222561456466623000231610ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * libmm-glib -- Access modem status & information from glib applications * * 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 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 Street, Fifth Floor, * Boston, MA 02110-1301 USA.o * * Copyright (C) 2011-2022 Aleksander Morgado * Copyright (C) 2022 Google, Inc. */ #ifndef MM_BEARER_PROPERTIES_H #define MM_BEARER_PROPERTIES_H #include #include #include #if !defined (__LIBMM_GLIB_H_INSIDE__) && !defined (LIBMM_GLIB_COMPILATION) #error "Only can be included directly." #endif G_BEGIN_DECLS #define MM_TYPE_BEARER_PROPERTIES (mm_bearer_properties_get_type ()) #define MM_BEARER_PROPERTIES(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_BEARER_PROPERTIES, MMBearerProperties)) #define MM_BEARER_PROPERTIES_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_BEARER_PROPERTIES, MMBearerPropertiesClass)) #define MM_IS_BEARER_PROPERTIES(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_BEARER_PROPERTIES)) #define MM_IS_BEARER_PROPERTIES_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_BEARER_PROPERTIES)) #define MM_BEARER_PROPERTIES_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_BEARER_PROPERTIES, MMBearerPropertiesClass)) typedef struct _MMBearerProperties MMBearerProperties; typedef struct _MMBearerPropertiesClass MMBearerPropertiesClass; typedef struct _MMBearerPropertiesPrivate MMBearerPropertiesPrivate; /** * MMBearerProperties: * * The #MMBearerProperties structure contains private data and should * only be accessed using the provided API. */ struct _MMBearerProperties { /*< private >*/ GObject parent; MMBearerPropertiesPrivate *priv; }; struct _MMBearerPropertiesClass { /*< private >*/ GObjectClass parent; }; GType mm_bearer_properties_get_type (void); G_DEFINE_AUTOPTR_CLEANUP_FUNC (MMBearerProperties, g_object_unref) MMBearerProperties *mm_bearer_properties_new (void); void mm_bearer_properties_set_apn (MMBearerProperties *self, const gchar *apn); void mm_bearer_properties_set_allowed_auth (MMBearerProperties *self, MMBearerAllowedAuth allowed_auth); void mm_bearer_properties_set_user (MMBearerProperties *self, const gchar *user); void mm_bearer_properties_set_password (MMBearerProperties *self, const gchar *password); void mm_bearer_properties_set_ip_type (MMBearerProperties *self, MMBearerIpFamily ip_type); void mm_bearer_properties_set_apn_type (MMBearerProperties *self, MMBearerApnType apn_type); void mm_bearer_properties_set_profile_id (MMBearerProperties *self, gint profile_id); void mm_bearer_properties_set_profile_name (MMBearerProperties *self, const gchar *profile_name); void mm_bearer_properties_set_allow_roaming (MMBearerProperties *self, gboolean allow_roaming); void mm_bearer_properties_set_rm_protocol (MMBearerProperties *self, MMModemCdmaRmProtocol protocol); void mm_bearer_properties_set_multiplex (MMBearerProperties *self, MMBearerMultiplexSupport multiplex); void mm_bearer_properties_set_access_type_preference (MMBearerProperties *self, MMBearerAccessTypePreference access_type_preference); void mm_bearer_properties_set_roaming_allowance (MMBearerProperties *self, MMBearerRoamingAllowance roaming_allowance); void mm_bearer_properties_set_force (MMBearerProperties *self, gboolean force); const gchar *mm_bearer_properties_get_apn (MMBearerProperties *self); MMBearerAllowedAuth mm_bearer_properties_get_allowed_auth (MMBearerProperties *self); const gchar *mm_bearer_properties_get_user (MMBearerProperties *self); const gchar *mm_bearer_properties_get_password (MMBearerProperties *self); MMBearerIpFamily mm_bearer_properties_get_ip_type (MMBearerProperties *self); MMBearerApnType mm_bearer_properties_get_apn_type (MMBearerProperties *self); gint mm_bearer_properties_get_profile_id (MMBearerProperties *self); const gchar *mm_bearer_properties_get_profile_name (MMBearerProperties *self); gboolean mm_bearer_properties_get_allow_roaming (MMBearerProperties *self); MMModemCdmaRmProtocol mm_bearer_properties_get_rm_protocol (MMBearerProperties *self); MMBearerMultiplexSupport mm_bearer_properties_get_multiplex (MMBearerProperties *self); MMBearerAccessTypePreference mm_bearer_properties_get_access_type_preference (MMBearerProperties *self); MMBearerRoamingAllowance mm_bearer_properties_get_roaming_allowance (MMBearerProperties *self); gboolean mm_bearer_properties_get_force (MMBearerProperties *self); /*****************************************************************************/ /* ModemManager/libmm-glib/mmcli specific methods */ #if defined (_LIBMM_INSIDE_MM) || \ defined (_LIBMM_INSIDE_MMCLI) || \ defined (LIBMM_GLIB_COMPILATION) MMBearerProperties *mm_bearer_properties_new_from_string (const gchar *str, GError **error); MMBearerProperties *mm_bearer_properties_new_from_dictionary (GVariant *dictionary, GError **error); MMBearerProperties *mm_bearer_properties_new_from_profile (MM3gppProfile *profile, GError **error); gboolean mm_bearer_properties_consume_string (MMBearerProperties *self, const gchar *key, const gchar *value, GError **error); gboolean mm_bearer_properties_consume_variant (MMBearerProperties *properties, const gchar *key, GVariant *value, GError **error); GVariant *mm_bearer_properties_get_dictionary (MMBearerProperties *self); MM3gppProfile *mm_bearer_properties_peek_3gpp_profile (MMBearerProperties *self); typedef enum { MM_BEARER_PROPERTIES_CMP_FLAGS_NONE = 0, MM_BEARER_PROPERTIES_CMP_FLAGS_LOOSE = 1 << 0, MM_BEARER_PROPERTIES_CMP_FLAGS_NO_PASSWORD = 1 << 1, MM_BEARER_PROPERTIES_CMP_FLAGS_NO_ALLOW_ROAMING = 1 << 2, MM_BEARER_PROPERTIES_CMP_FLAGS_NO_RM_PROTOCOL = 1 << 3, MM_BEARER_PROPERTIES_CMP_FLAGS_NO_APN_TYPE = 1 << 4, MM_BEARER_PROPERTIES_CMP_FLAGS_NO_PROFILE_ID = 1 << 5, MM_BEARER_PROPERTIES_CMP_FLAGS_NO_PROFILE_NAME = 1 << 6, MM_BEARER_PROPERTIES_CMP_FLAGS_NO_ACCESS_TYPE_PREFERENCE = 1 << 7, MM_BEARER_PROPERTIES_CMP_FLAGS_NO_ROAMING_ALLOWANCE = 1 << 8, } MMBearerPropertiesCmpFlags; gboolean mm_bearer_properties_cmp (MMBearerProperties *a, MMBearerProperties *b, MMBearerPropertiesCmpFlags flags); GPtrArray *mm_bearer_properties_print (MMBearerProperties *self, gboolean show_personal_info); #endif G_END_DECLS #endif /* MM_BEARER_PROPERTIES_H */ ModemManager-1.23.4-dev/libmm-glib/mm-bearer-stats.c000066400000000000000000000376351456466623000221250ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * libmm-glib -- Access modem status & information from glib applications * * 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 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 Street, Fifth Floor, * Boston, MA 02110-1301 USA. * * Copyright (C) 2015-2021 Azimut Electronics * Copyright (C) 2015-2021 Aleksander Morgado * Copyright (C) 2021 Intel Corporation */ #include #include "mm-errors-types.h" #include "mm-bearer-stats.h" /** * SECTION: mm-bearer-stats * @title: MMBearerStats * @short_description: Helper object to handle bearer stats. * * The #MMBearerStats is an object handling the statistics reported by the * bearer object during a connection. * * This object is retrieved with either mm_bearer_get_stats() or * mm_bearer_peek_stats(). */ G_DEFINE_TYPE (MMBearerStats, mm_bearer_stats, G_TYPE_OBJECT) #define PROPERTY_DURATION "duration" #define PROPERTY_RX_BYTES "rx-bytes" #define PROPERTY_TX_BYTES "tx-bytes" #define PROPERTY_START_DATE "start-date" #define PROPERTY_ATTEMPTS "attempts" #define PROPERTY_FAILED_ATTEMPTS "failed-attempts" #define PROPERTY_TOTAL_DURATION "total-duration" #define PROPERTY_TOTAL_RX_BYTES "total-rx-bytes" #define PROPERTY_TOTAL_TX_BYTES "total-tx-bytes" #define PROPERTY_UPLINK_SPEED "uplink-speed" #define PROPERTY_DOWNLINK_SPEED "downlink-speed" struct _MMBearerStatsPrivate { guint duration; guint64 rx_bytes; guint64 tx_bytes; guint64 start_date; guint attempts; guint failed_attempts; guint total_duration; guint64 total_rx_bytes; guint64 total_tx_bytes; guint64 uplink_speed; guint64 downlink_speed; }; /*****************************************************************************/ /** * mm_bearer_stats_get_duration: * @self: a #MMBearerStats. * * Gets the duration of the current connection, in seconds. * * Returns: a #guint. * * Since: 1.6 */ guint mm_bearer_stats_get_duration (MMBearerStats *self) { g_return_val_if_fail (MM_IS_BEARER_STATS (self), 0); return self->priv->duration; } /** * mm_bearer_stats_set_duration: (skip) */ void mm_bearer_stats_set_duration (MMBearerStats *self, guint duration) { g_return_if_fail (MM_IS_BEARER_STATS (self)); self->priv->duration = duration; } /*****************************************************************************/ /** * mm_bearer_stats_get_rx_bytes: * @self: a #MMBearerStats. * * Gets the number of bytes received without error in the connection. * * Returns: a #guint64. * * Since: 1.6 */ guint64 mm_bearer_stats_get_rx_bytes (MMBearerStats *self) { g_return_val_if_fail (MM_IS_BEARER_STATS (self), 0); return self->priv->rx_bytes; } /** * mm_bearer_stats_set_rx_bytes: (skip) */ void mm_bearer_stats_set_rx_bytes (MMBearerStats *self, guint64 bytes) { g_return_if_fail (MM_IS_BEARER_STATS (self)); self->priv->rx_bytes = bytes; } /*****************************************************************************/ /** * mm_bearer_stats_get_tx_bytes: * @self: a #MMBearerStats. * * Gets the number of bytes transmitted without error in the connection. * * Returns: a #guint64. * * Since: 1.6 */ guint64 mm_bearer_stats_get_tx_bytes (MMBearerStats *self) { g_return_val_if_fail (MM_IS_BEARER_STATS (self), 0); return self->priv->tx_bytes; } /** * mm_bearer_stats_set_tx_bytes: (skip) */ void mm_bearer_stats_set_tx_bytes (MMBearerStats *self, guint64 bytes) { g_return_if_fail (MM_IS_BEARER_STATS (self)); self->priv->tx_bytes = bytes; } /*****************************************************************************/ /** * mm_bearer_stats_get_start_date: * @self: a #MMBearerStats. * * Gets the start date of the current connection as a timestamp in seconds * since the epoch. * * Returns: a #guint64. * * Since: 1.20 */ guint64 mm_bearer_stats_get_start_date (MMBearerStats *self) { g_return_val_if_fail (MM_IS_BEARER_STATS (self), 0); return self->priv->start_date; } /** * mm_bearer_stats_set_start_date: (skip) */ void mm_bearer_stats_set_start_date (MMBearerStats *self, guint64 start_date) { g_return_if_fail (MM_IS_BEARER_STATS (self)); self->priv->start_date = start_date; } /*****************************************************************************/ /** * mm_bearer_stats_get_attempts: * @self: a #MMBearerStats. * * Gets the number of connection attempts done with this bearer. * * Returns: a #guint. * * Since: 1.14 */ guint mm_bearer_stats_get_attempts (MMBearerStats *self) { g_return_val_if_fail (MM_IS_BEARER_STATS (self), 0); return self->priv->attempts; } /** * mm_bearer_stats_set_attempts: (skip) */ void mm_bearer_stats_set_attempts (MMBearerStats *self, guint attempts) { g_return_if_fail (MM_IS_BEARER_STATS (self)); self->priv->attempts = attempts; } /*****************************************************************************/ /** * mm_bearer_stats_get_failed_attempts: * @self: a #MMBearerStats. * * Gets the number of failed connection attempts done with this bearer. * * Returns: a #guint. * * Since: 1.14 */ guint mm_bearer_stats_get_failed_attempts (MMBearerStats *self) { g_return_val_if_fail (MM_IS_BEARER_STATS (self), 0); return self->priv->failed_attempts; } /** * mm_bearer_stats_set_failed_attempts: (skip) */ void mm_bearer_stats_set_failed_attempts (MMBearerStats *self, guint failed_attempts) { g_return_if_fail (MM_IS_BEARER_STATS (self)); self->priv->failed_attempts = failed_attempts; } /*****************************************************************************/ /** * mm_bearer_stats_get_total_duration: * @self: a #MMBearerStats. * * Gets the total duration of all the connections of this bearer. * * Returns: a #guint. * * Since: 1.14 */ guint mm_bearer_stats_get_total_duration (MMBearerStats *self) { g_return_val_if_fail (MM_IS_BEARER_STATS (self), 0); return self->priv->total_duration; } /** * mm_bearer_stats_set_total_duration: (skip) */ void mm_bearer_stats_set_total_duration (MMBearerStats *self, guint total_duration) { g_return_if_fail (MM_IS_BEARER_STATS (self)); self->priv->total_duration = total_duration; } /*****************************************************************************/ /** * mm_bearer_stats_get_total_rx_bytes: * @self: a #MMBearerStats. * * Gets the total number of bytes received without error during all the * connections of this bearer. * * Returns: a #guint64. * * Since: 1.14 */ guint64 mm_bearer_stats_get_total_rx_bytes (MMBearerStats *self) { g_return_val_if_fail (MM_IS_BEARER_STATS (self), 0); return self->priv->total_rx_bytes; } /** * mm_bearer_stats_set_total_rx_bytes: (skip) */ void mm_bearer_stats_set_total_rx_bytes (MMBearerStats *self, guint64 total_bytes) { g_return_if_fail (MM_IS_BEARER_STATS (self)); self->priv->total_rx_bytes = total_bytes; } /*****************************************************************************/ /** * mm_bearer_stats_get_total_tx_bytes: * @self: a #MMBearerStats. * * Gets the total number of bytes transmitted without error during all the * connections of this bearer. * * Returns: a #guint64. * * Since: 1.14 */ guint64 mm_bearer_stats_get_total_tx_bytes (MMBearerStats *self) { g_return_val_if_fail (MM_IS_BEARER_STATS (self), 0); return self->priv->total_tx_bytes; } /** * mm_bearer_stats_set_total_tx_bytes: (skip) */ void mm_bearer_stats_set_total_tx_bytes (MMBearerStats *self, guint64 total_bytes) { g_return_if_fail (MM_IS_BEARER_STATS (self)); self->priv->total_tx_bytes = total_bytes; } /*****************************************************************************/ /** * mm_bearer_stats_get_uplink_speed: * @self: a #MMBearerStats. * * Gets the speed of the uplink, in bits per second. * * Returns: a #guint64. * * Since: 1.20 */ guint64 mm_bearer_stats_get_uplink_speed (MMBearerStats *self) { g_return_val_if_fail (MM_IS_BEARER_STATS (self), 0); return self->priv->uplink_speed; } /** * mm_bearer_stats_set_uplink_speed: (skip) */ void mm_bearer_stats_set_uplink_speed (MMBearerStats *self, guint64 speed) { g_return_if_fail (MM_IS_BEARER_STATS (self)); self->priv->uplink_speed = speed; } /*****************************************************************************/ /** * mm_bearer_stats_get_downlink_speed: * @self: a #MMBearerStats. * * Gets the speed of the downlink, in bits per second. * * Returns: a #guint64. * * Since: 1.20 */ guint64 mm_bearer_stats_get_downlink_speed (MMBearerStats *self) { g_return_val_if_fail (MM_IS_BEARER_STATS (self), 0); return self->priv->downlink_speed; } /** * mm_bearer_stats_set_downlink_speed: (skip) */ void mm_bearer_stats_set_downlink_speed (MMBearerStats *self, guint64 speed) { g_return_if_fail (MM_IS_BEARER_STATS (self)); self->priv->downlink_speed = speed; } /*****************************************************************************/ /** * mm_bearer_stats_get_dictionary: (skip) */ GVariant * mm_bearer_stats_get_dictionary (MMBearerStats *self) { GVariantBuilder builder; /* We do allow self==NULL. We'll just report NULL. */ if (!self) return NULL; g_variant_builder_init (&builder, G_VARIANT_TYPE ("a{sv}")); g_variant_builder_add (&builder, "{sv}", PROPERTY_DURATION, g_variant_new_uint32 (self->priv->duration)); g_variant_builder_add (&builder, "{sv}", PROPERTY_RX_BYTES, g_variant_new_uint64 (self->priv->rx_bytes)); g_variant_builder_add (&builder, "{sv}", PROPERTY_TX_BYTES, g_variant_new_uint64 (self->priv->tx_bytes)); g_variant_builder_add (&builder, "{sv}", PROPERTY_START_DATE, g_variant_new_uint64 (self->priv->start_date)); g_variant_builder_add (&builder, "{sv}", PROPERTY_ATTEMPTS, g_variant_new_uint32 (self->priv->attempts)); g_variant_builder_add (&builder, "{sv}", PROPERTY_FAILED_ATTEMPTS, g_variant_new_uint32 (self->priv->failed_attempts)); g_variant_builder_add (&builder, "{sv}", PROPERTY_TOTAL_DURATION, g_variant_new_uint32 (self->priv->total_duration)); g_variant_builder_add (&builder, "{sv}", PROPERTY_TOTAL_RX_BYTES, g_variant_new_uint64 (self->priv->total_rx_bytes)); g_variant_builder_add (&builder, "{sv}", PROPERTY_TOTAL_TX_BYTES, g_variant_new_uint64 (self->priv->total_tx_bytes)); g_variant_builder_add (&builder, "{sv}", PROPERTY_UPLINK_SPEED, g_variant_new_uint64 (self->priv->uplink_speed)); g_variant_builder_add (&builder, "{sv}", PROPERTY_DOWNLINK_SPEED, g_variant_new_uint64 (self->priv->downlink_speed)); return g_variant_builder_end (&builder); } /*****************************************************************************/ /** * mm_bearer_stats_new_from_dictionary: (skip) */ MMBearerStats * mm_bearer_stats_new_from_dictionary (GVariant *dictionary, GError **error) { GVariantIter iter; gchar *key; GVariant *value; MMBearerStats *self; self = mm_bearer_stats_new (); if (!dictionary) return self; if (!g_variant_is_of_type (dictionary, G_VARIANT_TYPE ("a{sv}"))) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS, "Cannot create Stats from dictionary: " "invalid variant type received"); g_object_unref (self); return NULL; } g_variant_iter_init (&iter, dictionary); while (g_variant_iter_next (&iter, "{sv}", &key, &value)) { if (g_str_equal (key, PROPERTY_DURATION)) { mm_bearer_stats_set_duration ( self, g_variant_get_uint32 (value)); } else if (g_str_equal (key, PROPERTY_RX_BYTES)) { mm_bearer_stats_set_rx_bytes ( self, g_variant_get_uint64 (value)); } else if (g_str_equal (key, PROPERTY_TX_BYTES)) { mm_bearer_stats_set_tx_bytes ( self, g_variant_get_uint64 (value)); } else if (g_str_equal (key, PROPERTY_START_DATE)) { mm_bearer_stats_set_start_date ( self, g_variant_get_uint64 (value)); } else if (g_str_equal (key, PROPERTY_ATTEMPTS)) { mm_bearer_stats_set_attempts ( self, g_variant_get_uint32 (value)); } else if (g_str_equal (key, PROPERTY_FAILED_ATTEMPTS)) { mm_bearer_stats_set_failed_attempts ( self, g_variant_get_uint32 (value)); } else if (g_str_equal (key, PROPERTY_TOTAL_DURATION)) { mm_bearer_stats_set_total_duration ( self, g_variant_get_uint32 (value)); } else if (g_str_equal (key, PROPERTY_TOTAL_RX_BYTES)) { mm_bearer_stats_set_total_rx_bytes ( self, g_variant_get_uint64 (value)); } else if (g_str_equal (key, PROPERTY_TOTAL_TX_BYTES)) { mm_bearer_stats_set_total_tx_bytes ( self, g_variant_get_uint64 (value)); } else if (g_str_equal (key, PROPERTY_UPLINK_SPEED)) { mm_bearer_stats_set_uplink_speed ( self, g_variant_get_uint64 (value)); } else if (g_str_equal (key, PROPERTY_DOWNLINK_SPEED)) { mm_bearer_stats_set_downlink_speed ( self, g_variant_get_uint64 (value)); } g_free (key); g_variant_unref (value); } return self; } /*****************************************************************************/ /** * mm_bearer_stats_new: (skip) */ MMBearerStats * mm_bearer_stats_new (void) { return (MM_BEARER_STATS (g_object_new (MM_TYPE_BEARER_STATS, NULL))); } static void mm_bearer_stats_init (MMBearerStats *self) { self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, MM_TYPE_BEARER_STATS, MMBearerStatsPrivate); } static void mm_bearer_stats_class_init (MMBearerStatsClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); g_type_class_add_private (object_class, sizeof (MMBearerStatsPrivate)); } ModemManager-1.23.4-dev/libmm-glib/mm-bearer-stats.h000066400000000000000000000112441456466623000221160ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * libmm-glib -- Access modem status & information from glib applications * * 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 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 Street, Fifth Floor, * Boston, MA 02110-1301 USA. * * Copyright (C) 2015-2021 Azimut Electronics * Copyright (C) 2015-2021 Aleksander Morgado * Copyright (C) 2021 Intel Corporation */ #ifndef MM_BEARER_STATS_H #define MM_BEARER_STATS_H #if !defined (__LIBMM_GLIB_H_INSIDE__) && !defined (LIBMM_GLIB_COMPILATION) #error "Only can be included directly." #endif #include #include G_BEGIN_DECLS #define MM_TYPE_BEARER_STATS (mm_bearer_stats_get_type ()) #define MM_BEARER_STATS(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_BEARER_STATS, MMBearerStats)) #define MM_BEARER_STATS_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_BEARER_STATS, MMBearerStatsClass)) #define MM_IS_BEARER_STATS(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_BEARER_STATS)) #define MM_IS_BEARER_STATS_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_BEARER_STATS)) #define MM_BEARER_STATS_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_BEARER_STATS, MMBearerStatsClass)) typedef struct _MMBearerStats MMBearerStats; typedef struct _MMBearerStatsClass MMBearerStatsClass; typedef struct _MMBearerStatsPrivate MMBearerStatsPrivate; /** * MMBearerStats: * * The #MMBearerStats structure contains private data and should * only be accessed using the provided API. */ struct _MMBearerStats { /*< private >*/ GObject parent; MMBearerStatsPrivate *priv; }; struct _MMBearerStatsClass { /*< private >*/ GObjectClass parent; }; GType mm_bearer_stats_get_type (void); G_DEFINE_AUTOPTR_CLEANUP_FUNC (MMBearerStats, g_object_unref) guint mm_bearer_stats_get_duration (MMBearerStats *self); guint64 mm_bearer_stats_get_rx_bytes (MMBearerStats *self); guint64 mm_bearer_stats_get_tx_bytes (MMBearerStats *self); guint64 mm_bearer_stats_get_start_date (MMBearerStats *self); guint mm_bearer_stats_get_attempts (MMBearerStats *self); guint mm_bearer_stats_get_failed_attempts (MMBearerStats *self); guint mm_bearer_stats_get_total_duration (MMBearerStats *self); guint64 mm_bearer_stats_get_total_rx_bytes (MMBearerStats *self); guint64 mm_bearer_stats_get_total_tx_bytes (MMBearerStats *self); guint64 mm_bearer_stats_get_uplink_speed (MMBearerStats *self); guint64 mm_bearer_stats_get_downlink_speed (MMBearerStats *self); /*****************************************************************************/ /* ModemManager/libmm-glib/mmcli specific methods */ #if defined (_LIBMM_INSIDE_MM) || \ defined (_LIBMM_INSIDE_MMCLI) || \ defined (LIBMM_GLIB_COMPILATION) MMBearerStats *mm_bearer_stats_new (void); MMBearerStats *mm_bearer_stats_new_from_dictionary (GVariant *dictionary, GError **error); void mm_bearer_stats_set_duration (MMBearerStats *self, guint duration); void mm_bearer_stats_set_rx_bytes (MMBearerStats *self, guint64 rx_bytes); void mm_bearer_stats_set_tx_bytes (MMBearerStats *self, guint64 tx_bytes); void mm_bearer_stats_set_start_date (MMBearerStats *self, guint64 stats); void mm_bearer_stats_set_attempts (MMBearerStats *self, guint attempts); void mm_bearer_stats_set_failed_attempts (MMBearerStats *self, guint failed_attempts); void mm_bearer_stats_set_total_duration (MMBearerStats *self, guint duration); void mm_bearer_stats_set_total_rx_bytes (MMBearerStats *self, guint64 rx_bytes); void mm_bearer_stats_set_total_tx_bytes (MMBearerStats *self, guint64 tx_bytes); void mm_bearer_stats_set_uplink_speed (MMBearerStats *self, guint64 speed); void mm_bearer_stats_set_downlink_speed (MMBearerStats *self, guint64 speed); GVariant *mm_bearer_stats_get_dictionary (MMBearerStats *self); #endif G_END_DECLS #endif /* MM_BEARER_STATS_H */ ModemManager-1.23.4-dev/libmm-glib/mm-bearer.c000066400000000000000000000520241456466623000207560ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * libmm-glib -- Access modem status & information from glib applications * * 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 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 Street, Fifth Floor, * Boston, MA 02110-1301 USA. * * Copyright (C) 2011 - 2012 Aleksander Morgado * Copyright (C) 2012 Google, Inc. */ #include "mm-helpers.h" #include "mm-common-helpers.h" #include "mm-bearer.h" /** * SECTION: mm-bearer * @title: MMBearer * @short_description: The Bearer interface * * The #MMBearer is an object providing access to the methods, signals and * properties of the Bearer interface. * * When the bearer is exposed and available in the bus, it is ensured that at * least this interface is also available. */ G_DEFINE_TYPE (MMBearer, mm_bearer, MM_GDBUS_TYPE_BEARER_PROXY) struct _MMBearerPrivate { /* Common mutex to sync access */ GMutex mutex; PROPERTY_OBJECT_DECLARE (ipv4_config, MMBearerIpConfig) PROPERTY_OBJECT_DECLARE (ipv6_config, MMBearerIpConfig) PROPERTY_OBJECT_DECLARE (properties, MMBearerProperties) PROPERTY_OBJECT_DECLARE (stats, MMBearerStats) PROPERTY_ERROR_DECLARE (connection_error) }; /*****************************************************************************/ /** * mm_bearer_get_path: * @self: A #MMBearer. * * Gets the DBus path of the #MMBearer object. * * Returns: (transfer none): The DBus path of the #MMBearer object. * * Since: 1.0 */ const gchar * mm_bearer_get_path (MMBearer *self) { g_return_val_if_fail (MM_IS_BEARER (self), NULL); RETURN_NON_EMPTY_CONSTANT_STRING ( g_dbus_proxy_get_object_path (G_DBUS_PROXY (self))); } /** * mm_bearer_dup_path: * @self: A #MMBearer. * * Gets a copy of the DBus path of the #MMBearer object. * * Returns: (transfer full): The DBus path of the #MMBearer object. The returned * value should be freed with g_free(). * * Since: 1.0 */ gchar * mm_bearer_dup_path (MMBearer *self) { gchar *value; g_return_val_if_fail (MM_IS_BEARER (self), NULL); g_object_get (G_OBJECT (self), "g-object-path", &value, NULL); RETURN_NON_EMPTY_STRING (value); } /*****************************************************************************/ /** * mm_bearer_get_interface: * @self: A #MMBearer. * * Gets the operating system name for the network data interface that provides * packet data using this #MMBearer. This will only be available once the #MMBearer * is in connected state. * * The returned value is only valid until the property changes so * it is only safe to use this function on the thread where * @self was constructed. Use mm_bearer_dup_interface() if on another * thread. * * Returns: (transfer none): The name of the interface, or %NULL if it couldn't * be retrieved. * * Since: 1.0 */ const gchar * mm_bearer_get_interface (MMBearer *self) { g_return_val_if_fail (MM_IS_BEARER (self), NULL); RETURN_NON_EMPTY_CONSTANT_STRING ( mm_gdbus_bearer_get_interface (MM_GDBUS_BEARER (self))); } /** * mm_bearer_dup_interface: * @self: A #MMBearer. * * Gets a copy of the operating system name for the network data interface that * provides packet data using this #MMBearer. This will only be available once * the #MMBearer is in connected state. * * Returns: (transfer full): The name of the interface, or %NULL if it couldn't * be retrieved. The returned value should be freed with g_free(). * * Since: 1.0 */ gchar * mm_bearer_dup_interface (MMBearer *self) { g_return_val_if_fail (MM_IS_BEARER (self), NULL); RETURN_NON_EMPTY_STRING ( mm_gdbus_bearer_dup_interface (MM_GDBUS_BEARER (self))); } /*****************************************************************************/ /** * mm_bearer_get_connected: * @self: A #MMBearer. * * Checks whether or not the #MMBearer is connected and thus whether packet data * communication is possible. * * Returns: %TRUE if the #MMBearer is connected, #FALSE otherwise. * * Since: 1.0 */ gboolean mm_bearer_get_connected (MMBearer *self) { g_return_val_if_fail (MM_IS_BEARER (self), FALSE); return mm_gdbus_bearer_get_connected (MM_GDBUS_BEARER (self)); } /*****************************************************************************/ /** * mm_bearer_get_reload_stats_supported: * @self: A #MMBearer. * * Checks whether or not the #MMBearer supporting stats reload (to have * RX and TX bytes of the ongoing connection). * * Returns: %TRUE if the #MMBearer supports these stats, #FALSE otherwise. * * Since: 1.20 */ gboolean mm_bearer_get_reload_stats_supported (MMBearer *self) { g_return_val_if_fail (MM_IS_BEARER (self), FALSE); return mm_gdbus_bearer_get_reload_stats_supported (MM_GDBUS_BEARER (self)); } /*****************************************************************************/ /** * mm_bearer_get_suspended: * @self: A #MMBearer. * * Checks whether or not the #MMBearer is suspended (but not deactivated) while * the device is handling other communications, like a voice call. * * Returns: %TRUE if packet data service is suspended in the #MMBearer, #FALSE * otherwise. * * Since: 1.0 */ gboolean mm_bearer_get_suspended (MMBearer *self) { g_return_val_if_fail (MM_IS_BEARER (self), FALSE); return mm_gdbus_bearer_get_suspended (MM_GDBUS_BEARER (self)); } /*****************************************************************************/ /** * mm_bearer_get_multiplexed: * @self: A #MMBearer. * * Checks whether or not the #MMBearer is connected through a multiplexed * network likn. * * Returns: %TRUE if packet data service is connected via a multiplexed network * link in the #MMBearer, #FALSE otherwise. * * Since: 1.18 */ gboolean mm_bearer_get_multiplexed (MMBearer *self) { g_return_val_if_fail (MM_IS_BEARER (self), FALSE); return mm_gdbus_bearer_get_multiplexed (MM_GDBUS_BEARER (self)); } /*****************************************************************************/ /** * mm_bearer_get_ip_timeout: * @self: A #MMBearer. * * Gets the maximum time to wait for the bearer to retrieve a valid IP address. * * Returns: The IP timeout, or 0 if no specific one given. * * Since: 1.0 */ guint mm_bearer_get_ip_timeout (MMBearer *self) { g_return_val_if_fail (MM_IS_BEARER (self), 0); return mm_gdbus_bearer_get_ip_timeout (MM_GDBUS_BEARER (self)); } /*****************************************************************************/ /** * mm_bearer_get_bearer_type: * @self: A #MMBearer. * * Gets the type of bearer. * * Returns: a #MMBearerType. * * Since: 1.0 */ MMBearerType mm_bearer_get_bearer_type (MMBearer *self) { g_return_val_if_fail (MM_IS_BEARER (self), MM_BEARER_TYPE_UNKNOWN); return mm_gdbus_bearer_get_bearer_type (MM_GDBUS_BEARER (self)); } /*****************************************************************************/ /** * mm_bearer_get_profile_id: * @self: A #MMBearer. * * Gets profile ID associated to the bearer connection, if known. * * If the bearer is disconnected or the modem doesn't support profile management * features, %MM_3GPP_PROFILE_ID_UNKNOWN. * * Returns: a profile id. * * Since: 1.18 */ gint mm_bearer_get_profile_id (MMBearer *self) { g_return_val_if_fail (MM_IS_BEARER (self), MM_3GPP_PROFILE_ID_UNKNOWN); return mm_gdbus_bearer_get_profile_id (MM_GDBUS_BEARER (self)); } /*****************************************************************************/ /** * mm_bearer_get_ipv4_config: * @self: A #MMBearer. * * Gets a #MMBearerIpConfig object specifying the IPv4 configuration to use in * the bearer. * * The values reported by @self are not updated when the values in the * interface change. Instead, the client is expected to call * mm_bearer_get_ipv4_config() again to get a new #MMBearerIpConfig with the * new values. * * Returns: (transfer full): A #MMBearerIpConfig that must be freed with * g_object_unref() or %NULL if unknown. * * Since: 1.0 */ /** * mm_bearer_peek_ipv4_config: * @self: A #MMBearer. * * Gets a #MMBearerIpConfig object specifying the IPv4 configuration to use in * the bearer. * * The returned value is only valid until the property changes so * it is only safe to use this function on the thread where * @self was constructed. Use mm_bearer_get_ipv4_config() if on another * thread. * * Returns: (transfer none): A #MMBearerIpConfig. Do not free the returned * value, it belongs to @self. * * Since: 1.0 */ /* helpers to match the property substring name with the one in our API */ #define mm_gdbus_bearer_dup_ipv4_config mm_gdbus_bearer_dup_ip4_config PROPERTY_OBJECT_DEFINE_FAILABLE (ipv4_config, Bearer, bearer, BEARER, MMBearerIpConfig, mm_bearer_ip_config_new_from_dictionary) /*****************************************************************************/ /** * mm_bearer_get_ipv6_config: * @self: A #MMBearer. * * Gets a #MMBearerIpConfig object specifying the IPv6 configuration to use in * the bearer. * * The values reported by @self are not updated when the values in the * interface change. Instead, the client is expected to call * mm_bearer_get_ipv6_config() again to get a new #MMBearerIpConfig with the * new values. * * Returns: (transfer full): A #MMBearerIpConfig that must be freed with * g_object_unref() or %NULL if unknown. * * Since: 1.0 */ /** * mm_bearer_peek_ipv6_config: * @self: A #MMBearer. * * Gets a #MMBearerIpConfig object specifying the IPv6 configuration to use in * the bearer. * * The returned value is only valid until the property changes so * it is only safe to use this function on the thread where * @self was constructed. Use mm_bearer_get_ipv6_config() if on another * thread. * * Returns: (transfer none): A #MMBearerIpConfig. Do not free the returned * value, it belongs to @self. * * Since: 1.0 */ /* helpers to match the property substring name with the one in our API */ #define mm_gdbus_bearer_dup_ipv6_config mm_gdbus_bearer_dup_ip6_config PROPERTY_OBJECT_DEFINE_FAILABLE (ipv6_config, Bearer, bearer, BEARER, MMBearerIpConfig, mm_bearer_ip_config_new_from_dictionary) /*****************************************************************************/ /** * mm_bearer_get_properties: * @self: A #MMBearer. * * Gets a #MMBearerProperties object specifying the properties which were used * to create the bearer. * * The values reported by @self are not updated when the values in the * interface change. Instead, the client is expected to call * mm_bearer_get_properties() again to get a new #MMBearerProperties with the * new values. * * Returns: (transfer full): A #MMBearerProperties that must be freed with * g_object_unref() or %NULL if unknown. * * Since: 1.0 */ /** * mm_bearer_peek_properties: * @self: A #MMBearer. * * Gets a #MMBearerProperties object specifying the properties which were used * to create the bearer. * * The returned value is only valid until the property changes so * it is only safe to use this function on the thread where * @self was constructed. Use mm_bearer_get_properties() if on another * thread. * * Returns: (transfer none): A #MMBearerProperties. Do not free the returned * value, it belongs to @self. * * Since: 1.0 */ PROPERTY_OBJECT_DEFINE_FAILABLE (properties, Bearer, bearer, BEARER, MMBearerProperties, mm_bearer_properties_new_from_dictionary) /*****************************************************************************/ /** * mm_bearer_get_stats: * @self: A #MMBearer. * * Gets a #MMBearerStats object specifying the statistics of the current bearer * connection. * * The values reported by @self are not updated when the values in the * interface change. Instead, the client is expected to call * mm_bearer_get_stats() again to get a new #MMBearerStats with the * new values. * * Returns: (transfer full): A #MMBearerStats that must be freed with * g_object_unref() or %NULL if unknown. * * Since: 1.6 */ /** * mm_bearer_peek_stats: * @self: A #MMBearer. * * Gets a #MMBearerStats object specifying the statistics of the current bearer * connection. * * The returned value is only valid until the property changes so * it is only safe to use this function on the thread where * @self was constructed. Use mm_bearer_get_stats() if on another * thread. * * Returns: (transfer none): A #MMBearerStats. Do not free the returned value, * it belongs to @self. * * Since: 1.6 */ PROPERTY_OBJECT_DEFINE_FAILABLE (stats, Bearer, bearer, BEARER, MMBearerStats, mm_bearer_stats_new_from_dictionary) /*****************************************************************************/ /** * mm_bearer_get_connection_error: * @self: A #MMBearer. * * Gets a #GError specifying the connection error details, if any. * * The values reported by @self are not updated when the values in the * interface change. Instead, the client is expected to call * mm_bearer_get_connection_error() again to get a new #GError with the * new values. * * Returns: (transfer full): A #GError that must be freed with * g_error_free() or %NULL if none. * * Since: 1.18 */ /** * mm_bearer_peek_connection_error: * @self: A #MMBearer. * * Gets a #GError specifying the connection error details, if any. * * The returned value is only valid until the property changes so * it is only safe to use this function on the thread where * @self was constructed. Use mm_bearer_get_connection_error() if on another * thread. * * Returns: (transfer none): A #GError, or %NULL if none. Do not * free the returned value, it belongs to @self. * * Since: 1.18 */ PROPERTY_ERROR_DEFINE_FAILABLE (connection_error, Bearer, bearer, BEARER, mm_common_error_from_tuple) /*****************************************************************************/ /** * mm_bearer_connect_finish: * @self: A #MMBearer. * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to * mm_bearer_connect(). * @error: Return location for error or %NULL. * * Finishes an operation started with mm_bearer_connect(). * * Returns: %TRUE if the operation succeeded, %FALSE if @error is set. * * Since: 1.0 */ gboolean mm_bearer_connect_finish (MMBearer *self, GAsyncResult *res, GError **error) { g_return_val_if_fail (MM_IS_BEARER (self), FALSE); return mm_gdbus_bearer_call_connect_finish (MM_GDBUS_BEARER (self), res, error); } /** * mm_bearer_connect: * @self: A #MMBearer. * @cancellable: (allow-none): A #GCancellable or %NULL. * @callback: A #GAsyncReadyCallback to call when the request is satisfied or %NULL. * @user_data: User data to pass to @callback. * * Asynchronously requests activation of a packet data connection with the * network using this #MMBearer properties. * * When the operation is finished, @callback will be invoked in the * thread-default main loop * of the thread you are calling this method from. You can then call * mm_bearer_connect_finish() to get the result of the operation. * * See mm_bearer_connect_sync() for the synchronous, blocking version of this method. * * Since: 1.0 */ void mm_bearer_connect (MMBearer *self, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { g_return_if_fail (MM_IS_BEARER (self)); mm_gdbus_bearer_call_connect (MM_GDBUS_BEARER (self), cancellable, callback, user_data); } /** * mm_bearer_connect_sync: * @self: A #MMBearer. * @cancellable: (allow-none): A #GCancellable or %NULL. * @error: Return location for error or %NULL. * * Synchronously requests activation of a packet data connection with the * network using this #MMBearer properties. * * The calling thread is blocked until a reply is received. * See mm_bearer_connect() for the asynchronous version of this method. * * Returns: %TRUE if the operation succeeded, %FALSE if @error is set. * * Since: 1.0 */ gboolean mm_bearer_connect_sync (MMBearer *self, GCancellable *cancellable, GError **error) { g_return_val_if_fail (MM_IS_BEARER (self), FALSE); return mm_gdbus_bearer_call_connect_sync (MM_GDBUS_BEARER (self), cancellable, error); } /*****************************************************************************/ /** * mm_bearer_disconnect: * @self: A #MMBearer. * @cancellable: (allow-none): A #GCancellable or %NULL. * @callback: A #GAsyncReadyCallback to call when the request is satisfied or * %NULL. * @user_data: User data to pass to @callback. * * Synchronously requests disconnection and deactivation of the packet data * connection. * * When the operation is finished, @callback will be invoked in the * thread-default main loop * of the thread you are calling this method from. You can then call * mm_bearer_disconnect_finish() to get the result of the operation. * * See mm_bearer_disconnect_sync() for the synchronous, blocking version of this * method. * * Since: 1.0 */ void mm_bearer_disconnect (MMBearer *self, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { g_return_if_fail (MM_IS_BEARER (self)); mm_gdbus_bearer_call_disconnect (MM_GDBUS_BEARER (self), cancellable, callback, user_data); } /** * mm_bearer_disconnect_finish: * @self: A #MMBearer. * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to * mm_bearer_disconnect(). * @error: Return location for error or %NULL. * * Finishes an operation started with mm_bearer_disconnect(). * * Returns: %TRUE if the operation succeeded, %FALSE if @error is set. * * Since: 1.0 */ gboolean mm_bearer_disconnect_finish (MMBearer *self, GAsyncResult *res, GError **error) { g_return_val_if_fail (MM_IS_BEARER (self), FALSE); return mm_gdbus_bearer_call_disconnect_finish (MM_GDBUS_BEARER (self), res, error); } /** * mm_bearer_disconnect_sync: * @self: A #MMBearer. * @cancellable: (allow-none): A #GCancellable or %NULL. * @error: Return location for error or %NULL. * * Synchronously requests disconnection and deactivation of the packet data * connection. * * The calling thread is blocked until a reply is received. * See mm_bearer_disconnect() for the asynchronous version of this method. * * Returns: %TRUE if the operation succeeded, %FALSE if @error is set. * * Since: 1.0 */ gboolean mm_bearer_disconnect_sync (MMBearer *self, GCancellable *cancellable, GError **error) { g_return_val_if_fail (MM_IS_BEARER (self), FALSE); return mm_gdbus_bearer_call_disconnect_sync (MM_GDBUS_BEARER (self), cancellable, error); } /*****************************************************************************/ static void mm_bearer_init (MMBearer *self) { self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, MM_TYPE_BEARER, MMBearerPrivate); g_mutex_init (&self->priv->mutex); PROPERTY_INITIALIZE (ipv4_config, "ip4-config") PROPERTY_INITIALIZE (ipv6_config, "ip6-config") PROPERTY_INITIALIZE (properties, "properties") PROPERTY_INITIALIZE (stats, "stats") PROPERTY_INITIALIZE (connection_error, "connection-error") } static void finalize (GObject *object) { MMBearer *self = MM_BEARER (object); g_mutex_clear (&self->priv->mutex); PROPERTY_OBJECT_FINALIZE (ipv4_config) PROPERTY_OBJECT_FINALIZE (ipv6_config) PROPERTY_OBJECT_FINALIZE (properties) PROPERTY_OBJECT_FINALIZE (stats) PROPERTY_ERROR_FINALIZE (connection_error) G_OBJECT_CLASS (mm_bearer_parent_class)->finalize (object); } static void mm_bearer_class_init (MMBearerClass *bearer_class) { GObjectClass *object_class = G_OBJECT_CLASS (bearer_class); g_type_class_add_private (object_class, sizeof (MMBearerPrivate)); object_class->finalize = finalize; } ModemManager-1.23.4-dev/libmm-glib/mm-bearer.h000066400000000000000000000115051456466623000207620ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * libmm-glib -- Access modem status & information from glib applications * * 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 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 Street, Fifth Floor, * Boston, MA 02110-1301 USA. * * Copyright (C) 2011 - 2012 Aleksander Morgado * Copyright (C) 2012 Google, Inc. */ #ifndef _MM_BEARER_H_ #define _MM_BEARER_H_ #if !defined (__LIBMM_GLIB_H_INSIDE__) && !defined (LIBMM_GLIB_COMPILATION) #error "Only can be included directly." #endif #include #include "mm-gdbus-bearer.h" #include "mm-bearer-properties.h" #include "mm-bearer-ip-config.h" #include "mm-bearer-stats.h" G_BEGIN_DECLS #define MM_TYPE_BEARER (mm_bearer_get_type ()) #define MM_BEARER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_BEARER, MMBearer)) #define MM_BEARER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_BEARER, MMBearerClass)) #define MM_IS_BEARER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_BEARER)) #define MM_IS_BEARER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), MM_TYPE_BEARER)) #define MM_BEARER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_BEARER, MMBearerClass)) typedef struct _MMBearer MMBearer; typedef struct _MMBearerClass MMBearerClass; typedef struct _MMBearerPrivate MMBearerPrivate; /** * MMBearer: * * The #MMBearer structure contains private data and should only be accessed * using the provided API. */ struct _MMBearer { /*< private >*/ MmGdbusBearerProxy parent; MMBearerPrivate *priv; }; struct _MMBearerClass { /*< private >*/ MmGdbusBearerProxyClass parent; }; GType mm_bearer_get_type (void); G_DEFINE_AUTOPTR_CLEANUP_FUNC (MMBearer, g_object_unref) const gchar *mm_bearer_get_path (MMBearer *self); gchar *mm_bearer_dup_path (MMBearer *self); const gchar *mm_bearer_get_interface (MMBearer *self); gchar *mm_bearer_dup_interface (MMBearer *self); gboolean mm_bearer_get_connected (MMBearer *self); gboolean mm_bearer_get_reload_stats_supported (MMBearer *self); gboolean mm_bearer_get_suspended (MMBearer *self); gboolean mm_bearer_get_multiplexed (MMBearer *self); guint mm_bearer_get_ip_timeout (MMBearer *self); MMBearerType mm_bearer_get_bearer_type (MMBearer *self); gint mm_bearer_get_profile_id (MMBearer *self); void mm_bearer_connect (MMBearer *self, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); gboolean mm_bearer_connect_finish (MMBearer *self, GAsyncResult *res, GError **error); gboolean mm_bearer_connect_sync (MMBearer *self, GCancellable *cancellable, GError **error); void mm_bearer_disconnect (MMBearer *self, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); gboolean mm_bearer_disconnect_finish (MMBearer *self, GAsyncResult *res, GError **error); gboolean mm_bearer_disconnect_sync (MMBearer *self, GCancellable *cancellable, GError **error); MMBearerProperties *mm_bearer_get_properties (MMBearer *self); MMBearerProperties *mm_bearer_peek_properties (MMBearer *self); MMBearerIpConfig *mm_bearer_get_ipv4_config (MMBearer *self); MMBearerIpConfig *mm_bearer_peek_ipv4_config (MMBearer *self); MMBearerIpConfig *mm_bearer_get_ipv6_config (MMBearer *self); MMBearerIpConfig *mm_bearer_peek_ipv6_config (MMBearer *self); MMBearerStats *mm_bearer_get_stats (MMBearer *self); MMBearerStats *mm_bearer_peek_stats (MMBearer *self); GError *mm_bearer_get_connection_error (MMBearer *self); GError *mm_bearer_peek_connection_error (MMBearer *self); G_END_DECLS #endif /* _MM_BEARER_H_ */ ModemManager-1.23.4-dev/libmm-glib/mm-call-audio-format.c000066400000000000000000000175111456466623000230200ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * libmm-glib -- Access modem status & information from glib applications * * 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 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 Street, Fifth Floor, * Boston, MA 02110-1301 USA. * * Copyright (C) 2017 Red Hat, Inc. */ #include #include "mm-errors-types.h" #include "mm-call-audio-format.h" /** * SECTION: mm-call-audio-format * @title: MMCallAudioFormat * @short_description: Helper object to handle voice call audio formats. * * The #MMCallAudioFormat is an object handling the voice call audio format * which describes how to send/receive voice call audio from the host. * * This object is retrieved with either mm_call_get_audio_format() or * mm_call_peek_audio_format(). */ G_DEFINE_TYPE (MMCallAudioFormat, mm_call_audio_format, G_TYPE_OBJECT) #define PROPERTY_ENCODING "encoding" #define PROPERTY_RESOLUTION "resolution" #define PROPERTY_RATE "rate" struct _MMCallAudioFormatPrivate { gchar *encoding; gchar *resolution; guint rate; }; /*****************************************************************************/ /** * mm_call_audio_format_get_encoding: * @self: a #MMCallAudioFormat. * * Gets the encoding of the audio format. For example, "pcm" for PCM-encoded * audio. * * Returns: a string with the encoding, or #NULL if unknown. Do not free the * returned value, it is owned by @self. * * Since: 1.10 */ const gchar * mm_call_audio_format_get_encoding (MMCallAudioFormat *self) { g_return_val_if_fail (MM_IS_CALL_AUDIO_FORMAT (self), NULL); return self->priv->encoding; } /** * mm_call_audio_format_set_encoding: (skip) */ void mm_call_audio_format_set_encoding (MMCallAudioFormat *self, const gchar *encoding) { g_return_if_fail (MM_IS_CALL_AUDIO_FORMAT (self)); g_free (self->priv->encoding); self->priv->encoding = g_strdup (encoding); } /*****************************************************************************/ /** * mm_call_audio_format_get_resolution: * @self: a #MMCallAudioFormat. * * Gets the resolution of the audio format. For example, "s16le" for signed * 16-bit little-endian audio sampling resolution. * * Returns: a string with the resolution, or #NULL if unknown. Do not free the * returned value, it is owned by @self. * * Since: 1.10 */ const gchar * mm_call_audio_format_get_resolution (MMCallAudioFormat *self) { g_return_val_if_fail (MM_IS_CALL_AUDIO_FORMAT (self), NULL); return self->priv->resolution; } /** * mm_call_audio_format_set_resolution: (skip) */ void mm_call_audio_format_set_resolution (MMCallAudioFormat *self, const gchar *resolution) { g_return_if_fail (MM_IS_CALL_AUDIO_FORMAT (self)); g_free (self->priv->resolution); self->priv->resolution = g_strdup (resolution); } /*****************************************************************************/ /** * mm_call_audio_format_get_rate: * @self: a #MMCallAudioFormat. * * Gets the sampling rate of the audio format. For example, 8000 for an 8000hz * sampling rate. * * Returns: the sampling rate, or 0 if unknown. * * Since: 1.10 */ guint mm_call_audio_format_get_rate (MMCallAudioFormat *self) { g_return_val_if_fail (MM_IS_CALL_AUDIO_FORMAT (self), 0); return self->priv->rate; } /** * mm_call_audio_format_set_rate: (skip) */ void mm_call_audio_format_set_rate (MMCallAudioFormat *self, guint rate) { g_return_if_fail (MM_IS_CALL_AUDIO_FORMAT (self)); self->priv->rate = rate; } /*****************************************************************************/ /** * mm_call_audio_format_get_dictionary: (skip) */ GVariant * mm_call_audio_format_get_dictionary (MMCallAudioFormat *self) { GVariantBuilder builder; /* We do allow NULL */ if (!self) return NULL; g_variant_builder_init (&builder, G_VARIANT_TYPE ("a{sv}")); if (self->priv->encoding) g_variant_builder_add (&builder, "{sv}", PROPERTY_ENCODING, g_variant_new_string (self->priv->encoding)); if (self->priv->resolution) g_variant_builder_add (&builder, "{sv}", PROPERTY_RESOLUTION, g_variant_new_string (self->priv->resolution)); if (self->priv->rate) g_variant_builder_add (&builder, "{sv}", PROPERTY_RATE, g_variant_new_uint32 (self->priv->rate)); return g_variant_builder_end (&builder); } /*****************************************************************************/ /** * mm_call_audio_format_new_from_dictionary: (skip) */ MMCallAudioFormat * mm_call_audio_format_new_from_dictionary (GVariant *dictionary, GError **error) { GVariantIter iter; gchar *key; GVariant *value; MMCallAudioFormat *self; self = mm_call_audio_format_new (); if (!dictionary) return self; if (!g_variant_is_of_type (dictionary, G_VARIANT_TYPE ("a{sv}"))) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS, "Cannot create call audio format from dictionary: " "invalid variant type received"); g_object_unref (self); return NULL; } g_variant_iter_init (&iter, dictionary); while (g_variant_iter_next (&iter, "{sv}", &key, &value)) { if (g_str_equal (key, PROPERTY_ENCODING)) mm_call_audio_format_set_encoding ( self, g_variant_get_string (value, NULL)); else if (g_str_equal (key, PROPERTY_RESOLUTION)) mm_call_audio_format_set_resolution ( self, g_variant_get_string (value, NULL)); else if (g_str_equal (key, PROPERTY_RATE)) mm_call_audio_format_set_rate ( self, g_variant_get_uint32 (value)); g_free (key); g_variant_unref (value); } return self; } /*****************************************************************************/ /** * mm_call_audio_format_new: (skip) */ MMCallAudioFormat * mm_call_audio_format_new (void) { return (MM_CALL_AUDIO_FORMAT ( g_object_new (MM_TYPE_CALL_AUDIO_FORMAT, NULL))); } static void mm_call_audio_format_init (MMCallAudioFormat *self) { self->priv = G_TYPE_INSTANCE_GET_PRIVATE ((self), MM_TYPE_CALL_AUDIO_FORMAT, MMCallAudioFormatPrivate); } static void finalize (GObject *object) { MMCallAudioFormat *self = MM_CALL_AUDIO_FORMAT (object); g_free (self->priv->encoding); g_free (self->priv->resolution); G_OBJECT_CLASS (mm_call_audio_format_parent_class)->finalize (object); } static void mm_call_audio_format_class_init (MMCallAudioFormatClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); g_type_class_add_private (object_class, sizeof (MMCallAudioFormatPrivate)); object_class->finalize = finalize; } ModemManager-1.23.4-dev/libmm-glib/mm-call-audio-format.h000066400000000000000000000072471456466623000230320ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * libmm-glib -- Access modem status & information from glib applications * * 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 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 Street, Fifth Floor, * Boston, MA 02110-1301 USA. * * Copyright (C) 2017 Red Hat, Inc. */ #ifndef MM_CALL_AUDIO_FORMAT_H #define MM_CALL_AUDIO_FORMAT_H #if !defined (__LIBMM_GLIB_H_INSIDE__) && !defined (LIBMM_GLIB_COMPILATION) #error "Only can be included directly." #endif #include #include G_BEGIN_DECLS #define MM_TYPE_CALL_AUDIO_FORMAT (mm_call_audio_format_get_type ()) #define MM_CALL_AUDIO_FORMAT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_CALL_AUDIO_FORMAT, MMCallAudioFormat)) #define MM_CALL_AUDIO_FORMAT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_CALL_AUDIO_FORMAT, MMCallAudioFormatClass)) #define MM_IS_CALL_AUDIO_FORMAT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_CALL_AUDIO_FORMAT)) #define MM_IS_CALL_AUDIO_FORMAT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_CALL_AUDIO_FORMAT)) #define MM_CALL_AUDIO_FORMAT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_CALL_AUDIO_FORMAT, MMCallAudioFormatClass)) typedef struct _MMCallAudioFormat MMCallAudioFormat; typedef struct _MMCallAudioFormatClass MMCallAudioFormatClass; typedef struct _MMCallAudioFormatPrivate MMCallAudioFormatPrivate; /** * MMCallAudioFormat: * * The #MMCallAudioFormat structure contains private data and should * only be accessed using the provided API. */ struct _MMCallAudioFormat { /*< private >*/ GObject parent; MMCallAudioFormatPrivate *priv; }; struct _MMCallAudioFormatClass { /*< private >*/ GObjectClass parent; }; GType mm_call_audio_format_get_type (void); G_DEFINE_AUTOPTR_CLEANUP_FUNC (MMCallAudioFormat, g_object_unref) const gchar *mm_call_audio_format_get_encoding (MMCallAudioFormat *self); const gchar *mm_call_audio_format_get_resolution (MMCallAudioFormat *self); guint mm_call_audio_format_get_rate (MMCallAudioFormat *self); /*****************************************************************************/ /* ModemManager/libmm-glib/mmcli specific methods */ #if defined (_LIBMM_INSIDE_MM) || \ defined (_LIBMM_INSIDE_MMCLI) || \ defined (LIBMM_GLIB_COMPILATION) MMCallAudioFormat *mm_call_audio_format_new (void); MMCallAudioFormat *mm_call_audio_format_new_from_dictionary (GVariant *dictionary, GError **error); void mm_call_audio_format_set_encoding (MMCallAudioFormat *self, const gchar *encoding); void mm_call_audio_format_set_resolution (MMCallAudioFormat *self, const gchar *resolution); void mm_call_audio_format_set_rate (MMCallAudioFormat *self, guint rate); GVariant *mm_call_audio_format_get_dictionary (MMCallAudioFormat *self); #endif G_END_DECLS #endif /* MM_CALL_AUDIO_FORMAT_H */ ModemManager-1.23.4-dev/libmm-glib/mm-call-properties.c000066400000000000000000000201621456466623000226210ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * libmm-glib -- Access modem status & information from glib applications * * 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 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 Street, Fifth Floor, * Boston, MA 02110-1301 USA. * * Copyright (C) 2015 Riccardo Vangelisti */ #include #include #include #include "mm-errors-types.h" #include "mm-enums-types.h" #include "mm-flags-types.h" #include "mm-common-helpers.h" #include "mm-call-properties.h" /** * SECTION: mm-call-properties * @title: MMCallProperties * @short_description: Helper object to handle CALL properties. * * The #MMCallProperties is an object handling the properties to be set * in newly created CALL objects. * * This object is created by the user and passed to ModemManager with either * mm_modem_voice_create_call() or mm_modem_voice_create_call_sync(). */ G_DEFINE_TYPE (MMCallProperties, mm_call_properties, G_TYPE_OBJECT) #define PROPERTY_NUMBER "number" struct _MMCallPropertiesPrivate { gchar *number; }; /*****************************************************************************/ /** * mm_call_properties_set_number: * @self: A #MMCallProperties. * @text: The number to set, in UTF-8. * * Sets the call number. * * Since: 1.6 */ void mm_call_properties_set_number (MMCallProperties *self, const gchar *number) { g_return_if_fail (MM_IS_CALL_PROPERTIES (self)); g_free (self->priv->number); self->priv->number = g_strdup (number); } /** * mm_call_properties_get_number: * @self: A #MMCallProperties. * * Gets the number, in UTF-8. * * Returns: the call number, or %NULL if it doesn't contain any (anonymous * caller). Do not free the returned value, it is owned by @self. * * Since: 1.6 */ const gchar * mm_call_properties_get_number (MMCallProperties *self) { g_return_val_if_fail (MM_IS_CALL_PROPERTIES (self), NULL); return self->priv->number; } /*****************************************************************************/ /* * mm_call_properties_get_dictionary: (skip) */ GVariant * mm_call_properties_get_dictionary (MMCallProperties *self) { GVariantBuilder builder; /* We do allow NULL */ if (!self) return NULL; g_return_val_if_fail (MM_IS_CALL_PROPERTIES (self), NULL); g_variant_builder_init (&builder, G_VARIANT_TYPE ("a{sv}")); if (self->priv->number) g_variant_builder_add (&builder, "{sv}", PROPERTY_NUMBER, g_variant_new_string (self->priv->number)); return g_variant_ref_sink (g_variant_builder_end (&builder)); } /*****************************************************************************/ static gboolean consume_string (MMCallProperties *self, const gchar *key, const gchar *value, GError **error) { if (g_str_equal (key, PROPERTY_NUMBER)) { mm_call_properties_set_number (self, value); } else { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS, "Invalid properties string, unexpected key '%s'", key); return FALSE; } return TRUE; } typedef struct { MMCallProperties *properties; GError *error; } ParseKeyValueContext; static gboolean key_value_foreach (const gchar *key, const gchar *value, ParseKeyValueContext *ctx) { return consume_string (ctx->properties, key, value, &ctx->error); } /* * mm_call_properties_new_from_string: (skip) */ MMCallProperties * mm_call_properties_new_from_string (const gchar *str, GError **error) { ParseKeyValueContext ctx; ctx.properties = mm_call_properties_new (); ctx.error = NULL; mm_common_parse_key_value_string (str, &ctx.error, (MMParseKeyValueForeachFn)key_value_foreach, &ctx); /* If error, destroy the object */ if (ctx.error) { g_propagate_error (error, ctx.error); g_object_unref (ctx.properties); ctx.properties = NULL; } return ctx.properties; } /*****************************************************************************/ static gboolean consume_variant (MMCallProperties *properties, const gchar *key, GVariant *value, GError **error) { if (g_str_equal (key, PROPERTY_NUMBER)) mm_call_properties_set_number ( properties, g_variant_get_string (value, NULL)); else { /* Set error */ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS, "Invalid properties dictionary, unexpected key '%s'", key); return FALSE; } return TRUE; } /* * mm_call_properties_new_from_dictionary: (skip) */ MMCallProperties * mm_call_properties_new_from_dictionary (GVariant *dictionary, GError **error) { GError *inner_error = NULL; GVariantIter iter; gchar *key; GVariant *value; MMCallProperties *properties; properties = mm_call_properties_new (); if (!dictionary) return properties; if (!g_variant_is_of_type (dictionary, G_VARIANT_TYPE ("a{sv}"))) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS, "Cannot create call properties from dictionary: " "invalid variant type received"); g_object_unref (properties); return NULL; } g_variant_iter_init (&iter, dictionary); while (!inner_error && g_variant_iter_next (&iter, "{sv}", &key, &value)) { consume_variant (properties, key, value, &inner_error); g_free (key); g_variant_unref (value); } /* If error, destroy the object */ if (inner_error) { g_propagate_error (error, inner_error); g_object_unref (properties); properties = NULL; } return properties; } /*****************************************************************************/ /** * mm_call_properties_new: * * Creates a new empty #MMCallProperties. * * Returns: (transfer full): a #MMCallProperties. The returned value should be * freed with g_object_unref(). * * Since: 1.6 */ MMCallProperties * mm_call_properties_new (void) { return (MM_CALL_PROPERTIES (g_object_new (MM_TYPE_CALL_PROPERTIES, NULL))); } static void mm_call_properties_init (MMCallProperties *self) { self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, MM_TYPE_CALL_PROPERTIES, MMCallPropertiesPrivate); } static void finalize (GObject *object) { MMCallProperties *self = MM_CALL_PROPERTIES (object); g_free (self->priv->number); G_OBJECT_CLASS (mm_call_properties_parent_class)->finalize (object); } static void mm_call_properties_class_init (MMCallPropertiesClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); g_type_class_add_private (object_class, sizeof (MMCallPropertiesPrivate)); object_class->finalize = finalize; } ModemManager-1.23.4-dev/libmm-glib/mm-call-properties.h000066400000000000000000000066501456466623000226340ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * libmm-glib -- Access modem status & information from glib applications * * 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 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 Street, Fifth Floor, * Boston, MA 02110-1301 USA. * * Copyright (C) 2015 Riccardo Vangelisti */ #ifndef MM_CALL_PROPERTIES_H #define MM_CALL_PROPERTIES_H #if !defined (__LIBMM_GLIB_H_INSIDE__) && !defined (LIBMM_GLIB_COMPILATION) #error "Only can be included directly." #endif #include #include G_BEGIN_DECLS #define MM_TYPE_CALL_PROPERTIES (mm_call_properties_get_type ()) #define MM_CALL_PROPERTIES(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_CALL_PROPERTIES, MMCallProperties)) #define MM_CALL_PROPERTIES_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_CALL_PROPERTIES, MMCallPropertiesClass)) #define MM_IS_CALL_PROPERTIES(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_CALL_PROPERTIES)) #define MM_IS_CALL_PROPERTIES_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_CALL_PROPERTIES)) #define MM_CALL_PROPERTIES_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_CALL_PROPERTIES, MMCallPropertiesClass)) typedef struct _MMCallProperties MMCallProperties; typedef struct _MMCallPropertiesClass MMCallPropertiesClass; typedef struct _MMCallPropertiesPrivate MMCallPropertiesPrivate; /** * MMCallProperties: * * The #MMCallProperties structure contains private data and should only be * accessed using the provided API. */ struct _MMCallProperties { /*< private >*/ GObject parent; MMCallPropertiesPrivate *priv; }; struct _MMCallPropertiesClass { /*< private >*/ GObjectClass parent; }; GType mm_call_properties_get_type (void); G_DEFINE_AUTOPTR_CLEANUP_FUNC (MMCallProperties, g_object_unref) MMCallProperties *mm_call_properties_new (void); void mm_call_properties_set_number (MMCallProperties *self, const gchar *text); const gchar *mm_call_properties_get_number (MMCallProperties *self); /*****************************************************************************/ /* ModemManager/libmm-glib/mmcli specific methods */ #if defined (_LIBMM_INSIDE_MM) || \ defined (_LIBMM_INSIDE_MMCLI) || \ defined (LIBMM_GLIB_COMPILATION) MMCallProperties *mm_call_properties_new_from_string (const gchar *str, GError **error); MMCallProperties *mm_call_properties_new_from_dictionary (GVariant *dictionary, GError **error); GVariant *mm_call_properties_get_dictionary (MMCallProperties *self); #endif G_END_DECLS #endif /* MM_CALL_PROPERTIES_H */ ModemManager-1.23.4-dev/libmm-glib/mm-call.c000066400000000000000000000700301456466623000204260ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * libmm-glib -- Access modem status & information from glib applications * * 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 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 Street, Fifth Floor, * Boston, MA 02110-1301 USA. * * Copyright (C) 2015 Riccardo Vangelisti * Copyright (C) 2015 Marco Bascetta */ #include "string.h" #include "mm-helpers.h" #include "mm-call.h" #include "mm-modem.h" /** * SECTION: mm-call * @title: MMCall * @short_description: The call interface * * The #MMCall is an object providing access to the methods, signals and * properties of the call interface. * * When the call is exposed and available in the bus, it is ensured that at * least this interface is also available. */ G_DEFINE_TYPE (MMCall, mm_call, MM_GDBUS_TYPE_CALL_PROXY) struct _MMCallPrivate { /* Common mutex to sync access */ GMutex mutex; PROPERTY_OBJECT_DECLARE (audio_format, MMCallAudioFormat) }; /*****************************************************************************/ /** * mm_call_get_path: * @self: A #MMCall. * * Gets the DBus path of the #MMCall object. * * Returns: (transfer none): The DBus path of the #MMCall object. * * Since: 1.6 */ const gchar * mm_call_get_path (MMCall *self) { g_return_val_if_fail (MM_IS_CALL (self), NULL); RETURN_NON_EMPTY_CONSTANT_STRING ( g_dbus_proxy_get_object_path (G_DBUS_PROXY (self))); } /** * mm_call_dup_path: * @self: A #MMCall. * * Gets a copy of the DBus path of the #MMCall object. * * Returns: (transfer full): The DBus path of the #MMCall object. * The returned value should be freed with g_free(). * * Since: 1.6 */ gchar * mm_call_dup_path (MMCall *self) { gchar *value; g_return_val_if_fail (MM_IS_CALL (self), NULL); g_object_get (G_OBJECT (self), "g-object-path", &value, NULL); RETURN_NON_EMPTY_STRING (value); } /*****************************************************************************/ /** * mm_call_get_number: * @self: A #MMCall. * * Gets the call number. In outgoing calls contains the dialing number or * the remote number in incoming calls * * The returned value is only valid until the property changes so * it is only safe to use this function on the thread where * @self was constructed. Use mm_call_dup_number() if on another * thread. * * Returns: (transfer none): The number, or %NULL if it couldn't be retrieved. * * Since: 1.6 */ const gchar * mm_call_get_number (MMCall *self) { g_return_val_if_fail (MM_IS_CALL (self), NULL); RETURN_NON_EMPTY_CONSTANT_STRING ( mm_gdbus_call_get_number (MM_GDBUS_CALL (self))); } /** * mm_call_dup_number: * @self: A #MMCall. * * Gets the call number. In outgoing calls contains the dialing number or * the remote number in incoming calls * * Returns: (transfer full): The number, or %NULL if it couldn't be retrieved. * The returned value should be freed with g_free(). * * Since: 1.6 */ gchar * mm_call_dup_number (MMCall *self) { g_return_val_if_fail (MM_IS_CALL (self), NULL); RETURN_NON_EMPTY_STRING ( mm_gdbus_call_dup_number (MM_GDBUS_CALL (self))); } /*****************************************************************************/ /** * mm_call_get_direction: * @self: A #MMCall. * * Gets the call direction. * * Returns: a #MMCallDirection. * * Since: 1.6 */ MMCallDirection mm_call_get_direction (MMCall *self) { g_return_val_if_fail (MM_IS_CALL (self), MM_CALL_DIRECTION_INCOMING); return (MMCallDirection) mm_gdbus_call_get_direction (MM_GDBUS_CALL (self)); } /*****************************************************************************/ /** * mm_call_get_multiparty: * @self: A #MMCall. * * Gets whether the call is part of a multiparty call. * * Returns: %TRUE if the call is part of a multiparty call, %FALSE otherwise. * * Since: 1.12 */ gboolean mm_call_get_multiparty (MMCall *self) { g_return_val_if_fail (MM_IS_CALL (self), FALSE); return mm_gdbus_call_get_multiparty (MM_GDBUS_CALL (self)); } /*****************************************************************************/ /** * mm_call_get_state: * @self: A #MMCall. * * Gets the current state of call. * * Returns: a #MMCallState. * * Since: 1.6 */ MMCallState mm_call_get_state (MMCall *self) { g_return_val_if_fail (MM_IS_CALL (self), MM_CALL_STATE_UNKNOWN); return (MMCallState) mm_gdbus_call_get_state (MM_GDBUS_CALL (self)); } /*****************************************************************************/ /** * mm_call_get_state_reason: * @self: A #MMCall. * * Gets the reason of why the call changes its state. * * Returns: a #MMCallStateReason. * * Since: 1.6 */ MMCallStateReason mm_call_get_state_reason (MMCall *self) { g_return_val_if_fail (MM_IS_CALL (self), MM_CALL_STATE_REASON_UNKNOWN); return (MMCallStateReason) mm_gdbus_call_get_state_reason (MM_GDBUS_CALL (self)); } /*****************************************************************************/ /** * mm_call_get_audio_port: * @self: A #MMCall. * * Gets the kernel device used for audio (if any). * * Returns: (transfer none): The audio port, or %NULL if call audio is not * routed via the host or couldn't be retrieved. * * Since: 1.10 */ const gchar * mm_call_get_audio_port (MMCall *self) { g_return_val_if_fail (MM_IS_CALL (self), NULL); RETURN_NON_EMPTY_CONSTANT_STRING ( mm_gdbus_call_get_audio_port (MM_GDBUS_CALL (self))); } /** * mm_call_dup_audio_port: * @self: A #MMCall. * * Gets the kernel device used for audio (if any). * * Returns: (transfer full): The audio port, or %NULL if call audio is not * routed via the host or couldn't be retrieved. * * Since: 1.10 */ gchar * mm_call_dup_audio_port (MMCall *self) { g_return_val_if_fail (MM_IS_CALL (self), NULL); RETURN_NON_EMPTY_STRING ( mm_gdbus_call_dup_audio_port (MM_GDBUS_CALL (self))); } /*****************************************************************************/ /** * mm_call_get_audio_format: * @self: A #MMCall. * * Gets a #MMCallAudioFormat object specifying the audio format used by the * audio port if call audio is routed via the host. * * The values reported by @self are not updated when the values in the * interface change. Instead, the client is expected to call * mm_call_get_audio_format() again to get a new #MMCallAudioFormat with the * new values. * * Returns: (transfer full): A #MMCallAudioFormat that must be freed with * g_object_unref() or %NULL if unknown. * * Since: 1.10 */ /** * mm_call_peek_audio_format: * @self: A #MMCall. * * Gets a #MMCallAudioFormat object specifying the audio format used by the * audio port if call audio is routed via the host. * * The returned value is only valid until the property changes so * it is only safe to use this function on the thread where * @self was constructed. Use mm_call_get_audio_format() if on another * thread. * * Returns: (transfer none): A #MMCallAudioFormat. Do not free the returned * value, it belongs to @self. * * Since: 1.10 */ PROPERTY_OBJECT_DEFINE_FAILABLE (audio_format, Call, call, CALL, MMCallAudioFormat, mm_call_audio_format_new_from_dictionary) /*****************************************************************************/ /** * mm_call_start_finish: * @self: A #MMCall. * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to * mm_call_start(). * @error: Return location for error or %NULL. * * Finishes an operation started with mm_call_start(). * * Returns: %TRUE if the operation succeeded, %FALSE if @error is set. * * Since: 1.6 */ gboolean mm_call_start_finish (MMCall *self, GAsyncResult *res, GError **error) { g_return_val_if_fail (MM_IS_CALL (self), FALSE); return mm_gdbus_call_call_start_finish (MM_GDBUS_CALL (self), res, error); } /** * mm_call_start: * @self: A #MMCall. * @cancellable: (allow-none): A #GCancellable or %NULL. * @callback: A #GAsyncReadyCallback to call when the request is satisfied or * %NULL. * @user_data: User data to pass to @callback. * * Asynchronously requests to queue the call. * * Call objects can only be executed once. * * When the operation is finished, @callback will be invoked in the * thread-default main loop * of the thread you are calling this method from. You can then call * mm_call_start_finish() to get the result of the operation. * * See mm_call_start_sync() for the synchronous, blocking version of this method. * * Since: 1.6 */ void mm_call_start (MMCall *self, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { g_return_if_fail (MM_IS_CALL (self)); mm_gdbus_call_call_start (MM_GDBUS_CALL (self), cancellable, callback, user_data); } /** * mm_call_start_sync: * @self: A #MMCall. * @cancellable: (allow-none): A #GCancellable or %NULL. * @error: Return location for error or %NULL. * * Synchronously requests to queue the call for delivery. * * Call objects can only be sent once. * * The calling thread is blocked until a reply is received. * See mm_call_start() for the asynchronous version of this method. * * Returns: %TRUE if the operation succeeded, %FALSE if @error is set. * * Since: 1.6 */ gboolean mm_call_start_sync (MMCall *self, GCancellable *cancellable, GError **error) { g_return_val_if_fail (MM_IS_CALL (self), FALSE); return mm_gdbus_call_call_start_sync (MM_GDBUS_CALL (self), cancellable, error); } /*****************************************************************************/ /** * mm_call_accept_finish: * @self: A #MMCall. * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to * mm_call_accept(). * @error: Return location for error or %NULL. * * Finishes an operation started with mm_call_accept(). * * Returns: %TRUE if the operation succeeded, %FALSE if @error is set. * * Since: 1.6 */ gboolean mm_call_accept_finish (MMCall *self, GAsyncResult *res, GError **error) { g_return_val_if_fail (MM_IS_CALL (self), FALSE); return mm_gdbus_call_call_accept_finish (MM_GDBUS_CALL (self), res, error); } /** * mm_call_accept: * @self: A #MMCall. * @cancellable: (allow-none): A #GCancellable or %NULL. * @callback: A #GAsyncReadyCallback to call when the request is satisfied or * %NULL. * @user_data: User data to pass to @callback. * * Asynchronously requests to accept the incoming call. * * Call objects can only be executed once. * * When the operation is finished, @callback will be invoked in the * thread-default main loop * of the thread you are calling this method from. You can then call * mm_call_accept_finish() to get the result of the operation. * * See mm_call_accept_sync() for the synchronous, blocking version of this method. * * Since: 1.6 */ void mm_call_accept (MMCall *self, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { g_return_if_fail (MM_IS_CALL (self)); mm_gdbus_call_call_accept (MM_GDBUS_CALL (self), cancellable, callback, user_data); } /** * mm_call_accept_sync: * @self: A #MMCall. * @cancellable: (allow-none): A #GCancellable or %NULL. * @error: Return location for error or %NULL. * * Synchronously requests to accept the incoming call. * * Call objects can only be sent once. * * The calling thread is blocked until an incoming call is ready. * See mm_call_accept() for the asynchronous version of this method. * * Returns: %TRUE if the operation succeeded, %FALSE if @error is set. * * Since: 1.6 */ gboolean mm_call_accept_sync (MMCall *self, GCancellable *cancellable, GError **error) { g_return_val_if_fail (MM_IS_CALL (self), FALSE); return mm_gdbus_call_call_accept_sync (MM_GDBUS_CALL (self), cancellable, error); } /*****************************************************************************/ /** * mm_call_deflect_finish: * @self: A #MMCall. * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to * mm_call_deflect(). * @error: Return location for error or %NULL. * * Finishes an operation started with mm_call_deflect(). * * Returns: %TRUE if the operation succeeded, %FALSE if @error is set. * * Since: 1.12 */ gboolean mm_call_deflect_finish (MMCall *self, GAsyncResult *res, GError **error) { g_return_val_if_fail (MM_IS_CALL (self), FALSE); return mm_gdbus_call_call_deflect_finish (MM_GDBUS_CALL (self), res, error); } /** * mm_call_deflect: * @self: A #MMCall. * @number: new number where the call will be deflected. * @cancellable: (allow-none): A #GCancellable or %NULL. * @callback: A #GAsyncReadyCallback to call when the request is satisfied or * %NULL. * @user_data: User data to pass to @callback. * * Asynchronously requests to deflect the incoming call. * * This call will be considered terminated once the deflection is performed. * * When the operation is finished, @callback will be invoked in the * thread-default main loop * of the thread you are calling this method from. You can then call * mm_call_deflect_finish() to get the result of the operation. * * See mm_call_deflect_sync() for the synchronous, blocking version of this * method. * * Since: 1.12 */ void mm_call_deflect (MMCall *self, const gchar *number, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { g_return_if_fail (MM_IS_CALL (self)); mm_gdbus_call_call_deflect (MM_GDBUS_CALL (self), number, cancellable, callback, user_data); } /** * mm_call_deflect_sync: * @self: A #MMCall. * @number: new number where the call will be deflected. * @cancellable: (allow-none): A #GCancellable or %NULL. * @error: Return location for error or %NULL. * * Synchronously requests to deflect the incoming call. * * This call will be considered terminated once the deflection is performed. * * The calling thread is blocked until an incoming call is ready. * See mm_call_deflect() for the asynchronous version of this method. * * Returns: %TRUE if the operation succeeded, %FALSE if @error is set. * * Since: 1.12 */ gboolean mm_call_deflect_sync (MMCall *self, const gchar *number, GCancellable *cancellable, GError **error) { g_return_val_if_fail (MM_IS_CALL (self), FALSE); return mm_gdbus_call_call_deflect_sync (MM_GDBUS_CALL (self), number, cancellable, error); } /*****************************************************************************/ /** * mm_call_join_multiparty_finish: * @self: A #MMCall. * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to * mm_call_join_multiparty(). * @error: Return location for error or %NULL. * * Finishes an operation started with mm_call_join_multiparty(). * * Returns: %TRUE if the operation succeeded, %FALSE if @error is set. * * Since: 1.12 */ gboolean mm_call_join_multiparty_finish (MMCall *self, GAsyncResult *res, GError **error) { g_return_val_if_fail (MM_IS_CALL (self), FALSE); return mm_gdbus_call_call_join_multiparty_finish (MM_GDBUS_CALL (self), res, error); } /** * mm_call_join_multiparty: * @self: A #MMCall. * @cancellable: (allow-none): A #GCancellable or %NULL. * @callback: A #GAsyncReadyCallback to call when the request is satisfied or * %NULL. * @user_data: User data to pass to @callback. * * Synchronously requests to join this call into a multiparty call. * * When the operation is finished, @callback will be invoked in the * thread-default main loop * of the thread you are calling this method from. You can then call * mm_call_join_multiparty_finish() to get the result of the operation. * * See mm_call_join_multiparty_sync() for the synchronous, blocking version of * this method. * * Since: 1.12 */ void mm_call_join_multiparty (MMCall *self, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { g_return_if_fail (MM_IS_CALL (self)); mm_gdbus_call_call_join_multiparty (MM_GDBUS_CALL (self), cancellable, callback, user_data); } /** * mm_call_join_multiparty_sync: * @self: A #MMCall. * @cancellable: (allow-none): A #GCancellable or %NULL. * @error: Return location for error or %NULL. * * Synchronously requests to join this call into a multiparty call. * * The calling thread is blocked until an incoming call is ready. * See mm_call_join_multiparty() for the asynchronous version of this method. * * Returns: %TRUE if the operation succeeded, %FALSE if @error is set. * * Since: 1.12 */ gboolean mm_call_join_multiparty_sync (MMCall *self, GCancellable *cancellable, GError **error) { g_return_val_if_fail (MM_IS_CALL (self), FALSE); return mm_gdbus_call_call_join_multiparty_sync (MM_GDBUS_CALL (self), cancellable, error); } /** * mm_call_leave_multiparty_finish: * @self: A #MMCall. * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to * mm_call_leave_multiparty(). * @error: Return location for error or %NULL. * * Finishes an operation started with mm_call_leave_multiparty(). * * Returns: %TRUE if the operation succeeded, %FALSE if @error is set. * * Since: 1.12 */ gboolean mm_call_leave_multiparty_finish (MMCall *self, GAsyncResult *res, GError **error) { g_return_val_if_fail (MM_IS_CALL (self), FALSE); return mm_gdbus_call_call_leave_multiparty_finish (MM_GDBUS_CALL (self), res, error); } /** * mm_call_leave_multiparty: * @self: A #MMCall. * @cancellable: (allow-none): A #GCancellable or %NULL. * @callback: A #GAsyncReadyCallback to call when the request is satisfied or * %NULL. * @user_data: User data to pass to @callback. * * Synchronously requests to make this call private again by leaving the * multiparty call. * * When the operation is finished, @callback will be invoked in the * thread-default main loop * of the thread you are calling this method from. You can then call * mm_call_leave_multiparty_finish() to get the result of the operation. * * See mm_call_leave_multiparty_sync() for the synchronous, blocking version * of this method. * * Since: 1.12 */ void mm_call_leave_multiparty (MMCall *self, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { g_return_if_fail (MM_IS_CALL (self)); mm_gdbus_call_call_leave_multiparty (MM_GDBUS_CALL (self), cancellable, callback, user_data); } /** * mm_call_leave_multiparty_sync: * @self: A #MMCall. * @cancellable: (allow-none): A #GCancellable or %NULL. * @error: Return location for error or %NULL. * * Synchronously requests to make this call private again by leaving the * multiparty call. * * The calling thread is blocked until an incoming call is ready. * See mm_call_leave_multiparty() for the asynchronous version of this method. * * Returns: %TRUE if the operation succeeded, %FALSE if @error is set. * * Since: 1.12 */ gboolean mm_call_leave_multiparty_sync (MMCall *self, GCancellable *cancellable, GError **error) { g_return_val_if_fail (MM_IS_CALL (self), FALSE); return mm_gdbus_call_call_leave_multiparty_sync (MM_GDBUS_CALL (self), cancellable, error); } /*****************************************************************************/ /** * mm_call_hangup_finish: * @self: A #MMCall. * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to * mm_call_hangup(). * @error: Return location for error or %NULL. * * Finishes an operation started with mm_call_hangup(). * * Returns: %TRUE if the operation succeeded, %FALSE if @error is set. * * Since: 1.6 */ gboolean mm_call_hangup_finish (MMCall *self, GAsyncResult *res, GError **error) { g_return_val_if_fail (MM_IS_CALL (self), FALSE); return mm_gdbus_call_call_hangup_finish (MM_GDBUS_CALL (self), res, error); } /** * mm_call_hangup: * @self: A #MMCall. * @cancellable: (allow-none): A #GCancellable or %NULL. * @callback: A #GAsyncReadyCallback to call when the request is satisfied or * %NULL. * @user_data: User data to pass to @callback. * * Asynchronously requests to hangup the call. * * Call objects can only be executed once. * * When the operation is finished, @callback will be invoked in the * thread-default main loop * of the thread you are calling this method from. You can then call * mm_call_hangup_finish() to get the result of the operation. * * See mm_call_hangup_sync() for the synchronous, blocking version of this * method. * * Since: 1.6 */ void mm_call_hangup (MMCall *self, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { g_return_if_fail (MM_IS_CALL (self)); mm_gdbus_call_call_hangup (MM_GDBUS_CALL (self), cancellable, callback, user_data); } /** * mm_call_hangup_sync: * @self: A #MMCall. * @cancellable: (allow-none): A #GCancellable or %NULL. * @error: Return location for error or %NULL. * * Synchronously requests to hangup the call. * * Call objects can only be sent once. * * The calling thread is blocked until an incoming call is ready. * See mm_call_hangup() for the asynchronous version of this method. * * Returns: %TRUE if the operation succeeded, %FALSE if @error is set. * * Since: 1.6 */ gboolean mm_call_hangup_sync (MMCall *self, GCancellable *cancellable, GError **error) { g_return_val_if_fail (MM_IS_CALL (self), FALSE); return mm_gdbus_call_call_hangup_sync (MM_GDBUS_CALL (self), cancellable, error); } /*****************************************************************************/ /** * mm_call_send_dtmf_finish: * @self: A #MMCall. * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to * mm_call_send_dtmf(). * @error: Return location for error or %NULL. * * Finishes an operation started with mm_call_send_dtmf(). * * Returns: %TRUE if the operation succeeded, %FALSE if @error is set. * * Since: 1.6 */ gboolean mm_call_send_dtmf_finish (MMCall *self, GAsyncResult *res, GError **error) { g_return_val_if_fail (MM_IS_CALL (self), FALSE); return mm_gdbus_call_call_send_dtmf_finish (MM_GDBUS_CALL (self), res, error); } /** * mm_call_send_dtmf: * @self: A #MMCall. * @dtmf: the DMTF tone. * @cancellable: (allow-none): A #GCancellable or %NULL. * @callback: A #GAsyncReadyCallback to call when the request is satisfied or * %NULL. * @user_data: User data to pass to @callback. * * Asynchronously requests to send a DTMF tone the call. * * Call objects can only be executed once. * * When the operation is finished, @callback will be invoked in the * thread-default main loop * of the thread you are calling this method from. You can then call * mm_call_send_dtmf_finish() to get the result of the operation. * * See mm_call_send_dtmf_sync() for the synchronous, blocking version of this * method. * * Since: 1.6 */ void mm_call_send_dtmf (MMCall *self, const gchar *dtmf, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { g_return_if_fail (MM_IS_CALL (self)); mm_gdbus_call_call_send_dtmf (MM_GDBUS_CALL (self), dtmf, cancellable, callback, user_data); } /** * mm_call_send_dtmf_sync: * @self: A #MMCall. * @dtmf: the DMTF tone. * @cancellable: (allow-none): A #GCancellable or %NULL. * @error: Return location for error or %NULL. * * Synchronously requests to send a DTMF tone the call. * * Call objects can only be sent once. * * The calling thread is blocked until an incoming call is ready. * See mm_call_send_dtmf() for the asynchronous version of this method. * * Returns: %TRUE if the operation succeeded, %FALSE if @error is set. * * Since: 1.6 */ gboolean mm_call_send_dtmf_sync (MMCall *self, const gchar *dtmf, GCancellable *cancellable, GError **error) { g_return_val_if_fail (MM_IS_CALL (self), FALSE); return mm_gdbus_call_call_send_dtmf_sync (MM_GDBUS_CALL (self), dtmf, cancellable, error); } /*****************************************************************************/ static void mm_call_init (MMCall *self) { self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, MM_TYPE_CALL, MMCallPrivate); g_mutex_init (&self->priv->mutex); PROPERTY_INITIALIZE (audio_format, "audio-format") } static void finalize (GObject *object) { MMCall *self = MM_CALL (object); g_mutex_clear (&self->priv->mutex); PROPERTY_OBJECT_FINALIZE (audio_format) G_OBJECT_CLASS (mm_call_parent_class)->finalize (object); } static void mm_call_class_init (MMCallClass *call_class) { GObjectClass *object_class = G_OBJECT_CLASS (call_class); g_type_class_add_private (object_class, sizeof (MMCallPrivate)); object_class->finalize = finalize; } ModemManager-1.23.4-dev/libmm-glib/mm-call.h000066400000000000000000000177661456466623000204540ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * libmm-glib -- Access modem status & information from glib applications * * 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 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 Street, Fifth Floor, * Boston, MA 02110-1301 USA. * * Copyright (C) 2015 Riccardo Vangelisti * Copyright (C) 2015 Marco Bascetta */ #ifndef _MM_CALL_H_ #define _MM_CALL_H_ #if !defined (__LIBMM_GLIB_H_INSIDE__) && !defined (LIBMM_GLIB_COMPILATION) #error "Only can be included directly." #endif #include #include "mm-gdbus-call.h" #include "mm-call-audio-format.h" G_BEGIN_DECLS #define MM_TYPE_CALL (mm_call_get_type ()) #define MM_CALL(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_CALL, MMCall)) #define MM_CALL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_CALL, MMCallClass)) #define MM_IS_CALL(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_CALL)) #define MM_IS_CALL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), MM_TYPE_CALL)) #define MM_CALL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_CALL, MMCallClass)) typedef struct _MMCall MMCall; typedef struct _MMCallClass MMCallClass; typedef struct _MMCallPrivate MMCallPrivate; /** * MMCall: * * The #MMCall structure contains private data and should only be accessed * using the provided API. */ struct _MMCall { /*< private >*/ MmGdbusCallProxy parent; MMCallPrivate *priv; }; struct _MMCallClass { /*< private >*/ MmGdbusCallProxyClass parent; }; GType mm_call_get_type (void); G_DEFINE_AUTOPTR_CLEANUP_FUNC (MMCall, g_object_unref) const gchar *mm_call_get_path (MMCall *self); gchar *mm_call_dup_path (MMCall *self); const gchar *mm_call_get_number (MMCall *self); gchar *mm_call_dup_number (MMCall *self); MMCallState mm_call_get_state (MMCall *self); MMCallStateReason mm_call_get_state_reason (MMCall *self); MMCallDirection mm_call_get_direction (MMCall *self); gboolean mm_call_get_multiparty (MMCall *self); const gchar *mm_call_get_audio_port (MMCall *self); gchar *mm_call_dup_audio_port (MMCall *self); MMCallAudioFormat *mm_call_get_audio_format (MMCall *self); MMCallAudioFormat *mm_call_peek_audio_format(MMCall *self); void mm_call_start (MMCall *self, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); gboolean mm_call_start_finish (MMCall *self, GAsyncResult *res, GError **error); gboolean mm_call_start_sync (MMCall *self, GCancellable *cancellable, GError **error); void mm_call_accept (MMCall *self, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); gboolean mm_call_accept_finish (MMCall *self, GAsyncResult *res, GError **error); gboolean mm_call_accept_sync (MMCall *self, GCancellable *cancellable, GError **error); void mm_call_deflect (MMCall *self, const gchar *number, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); gboolean mm_call_deflect_finish (MMCall *self, GAsyncResult *res, GError **error); gboolean mm_call_deflect_sync (MMCall *self, const gchar *number, GCancellable *cancellable, GError **error); void mm_call_join_multiparty (MMCall *self, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); gboolean mm_call_join_multiparty_finish (MMCall *self, GAsyncResult *res, GError **error); gboolean mm_call_join_multiparty_sync (MMCall *self, GCancellable *cancellable, GError **error); void mm_call_leave_multiparty (MMCall *self, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); gboolean mm_call_leave_multiparty_finish (MMCall *self, GAsyncResult *res, GError **error); gboolean mm_call_leave_multiparty_sync (MMCall *self, GCancellable *cancellable, GError **error); void mm_call_hangup (MMCall *self, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); gboolean mm_call_hangup_finish (MMCall *self, GAsyncResult *res, GError **error); gboolean mm_call_hangup_sync (MMCall *self, GCancellable *cancellable, GError **error); void mm_call_send_dtmf (MMCall *self, const gchar *dtmf, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); gboolean mm_call_send_dtmf_finish (MMCall *self, GAsyncResult *res, GError **error); gboolean mm_call_send_dtmf_sync (MMCall *self, const gchar *dtmf, GCancellable *cancellable, GError **error); G_END_DECLS #endif /* _MM_CALL_H_ */ ModemManager-1.23.4-dev/libmm-glib/mm-cdma-manual-activation-properties.c000066400000000000000000000665011456466623000262330ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * libmm-glib -- Access modem status & information from glib applications * * 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 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 Street, Fifth Floor, * Boston, MA 02110-1301 USA. * * Copyright (C) 2012 Google Inc. */ #include #include "mm-errors-types.h" #include "mm-common-helpers.h" #include "mm-cdma-manual-activation-properties.h" /** * SECTION: mm-cdma-manual-activation-properties * @title: MMCdmaManualActivationProperties * @short_description: Helper object to handle manual CDMA activation properties. * * The #MMCdmaManualActivationProperties is an object handling the properties * required during a manual CDMA activation request. */ G_DEFINE_TYPE (MMCdmaManualActivationProperties, mm_cdma_manual_activation_properties, G_TYPE_OBJECT) #define PROPERTY_SPC "spc" #define PROPERTY_SID "sid" #define PROPERTY_MDN "mdn" #define PROPERTY_MIN "min" #define PROPERTY_MN_HA_KEY "mn-ha-key" #define PROPERTY_MN_AAA_KEY "mn-aaa-key" #define PROPERTY_PRL "prl" struct _MMCdmaManualActivationPropertiesPrivate { /* Mandatory parameters */ gchar *spc; guint16 sid; gboolean sid_set; gchar *mdn; gchar *min; /* Optional */ gchar *mn_ha_key; gchar *mn_aaa_key; GByteArray *prl; }; /*****************************************************************************/ /** * mm_cdma_manual_activation_properties_get_spc: * @self: A #MMCdmaManualActivationProperties. * * Gets the Service Programming Code. * * Returns: (transfer none): The SPC. Do not free the returned value, it is * owned by @self. * * Since: 1.2 */ const gchar * mm_cdma_manual_activation_properties_get_spc (MMCdmaManualActivationProperties *self) { g_return_val_if_fail (MM_IS_CDMA_MANUAL_ACTIVATION_PROPERTIES (self), NULL); return self->priv->spc; } /* SPC is a 6-digit string */ static gboolean validate_spc (const gchar *spc, GError **error) { guint i; if (strlen (spc) != 6) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS, "SPC must be exactly 6-digit long"); return FALSE; } for (i = 0; i < 6; i ++) { if (!g_ascii_isdigit (spc[i])) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS, "SPC must not contain non-digit characters"); return FALSE; } } return TRUE; } /** * mm_cdma_manual_activation_properties_set_spc: * @self: A #MMCdmaManualActivationProperties. * @spc: The SPC string, exactly 6 digits. * @error: Return location for error or %NULL. * * Sets the Service Programming Code. * * Returns: %TRUE if the SPC was successfully set, or %FALSE if @error is set. * * Since: 1.2 */ gboolean mm_cdma_manual_activation_properties_set_spc (MMCdmaManualActivationProperties *self, const gchar *spc, GError **error) { g_return_val_if_fail (MM_IS_CDMA_MANUAL_ACTIVATION_PROPERTIES (self), FALSE); if (!validate_spc (spc, error)) return FALSE; g_free (self->priv->spc); self->priv->spc = g_strdup (spc); return TRUE; } /*****************************************************************************/ /** * mm_cdma_manual_activation_properties_get_sid: * @self: A #MMCdmaManualActivationProperties. * * Gets the System Identification Number. * * Returns: The SID. * * Since: 1.2 */ guint16 mm_cdma_manual_activation_properties_get_sid (MMCdmaManualActivationProperties *self) { g_return_val_if_fail (MM_IS_CDMA_MANUAL_ACTIVATION_PROPERTIES (self), 0); return self->priv->sid; } /** * mm_cdma_manual_activation_properties_set_sid: * @self: A #MMCdmaManualActivationProperties. * @sid: The SID. * * Sets the Service Identification Number. * * Since: 1.2 */ void mm_cdma_manual_activation_properties_set_sid (MMCdmaManualActivationProperties *self, guint16 sid) { g_return_if_fail (MM_IS_CDMA_MANUAL_ACTIVATION_PROPERTIES (self)); self->priv->sid_set = TRUE; self->priv->sid = sid; } /*****************************************************************************/ /** * mm_cdma_manual_activation_properties_get_mdn: * @self: A #MMCdmaManualActivationProperties. * * Gets the Mobile Directory Number. * * Returns: (transfer none): The MDN. Do not free the returned value, it is * owned by @self. * * Since: 1.2 */ const gchar * mm_cdma_manual_activation_properties_get_mdn (MMCdmaManualActivationProperties *self) { g_return_val_if_fail (MM_IS_CDMA_MANUAL_ACTIVATION_PROPERTIES (self), NULL); return self->priv->mdn; } /* MDN is max 15 characters */ static gboolean validate_mdn (const gchar *mdn, GError **error) { if (strlen (mdn) > 15) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS, "MDN must be maximum 15 characters long"); return FALSE; } return TRUE; } /** * mm_cdma_manual_activation_properties_set_mdn: * @self: A #MMCdmaManualActivationProperties. * @mdn: The MDN string, maximum 15 characters. * @error: Return location for error or %NULL. * * Sets the Mobile Directory Number. * * Returns: %TRUE if the MDN was successfully set, or %FALSE if @error is set. * * Since: 1.2 */ gboolean mm_cdma_manual_activation_properties_set_mdn (MMCdmaManualActivationProperties *self, const gchar *mdn, GError **error) { g_return_val_if_fail (MM_IS_CDMA_MANUAL_ACTIVATION_PROPERTIES (self), FALSE); if (!validate_mdn (mdn, error)) return FALSE; g_free (self->priv->mdn); self->priv->mdn = g_strdup (mdn); return TRUE; } /*****************************************************************************/ /** * mm_cdma_manual_activation_properties_get_min: * @self: A #MMCdmaManualActivationProperties. * * Gets the Mobile Indentification Number. * * Returns: (transfer none): The MIN. Do not free the returned value, it is * owned by @self. * * Since: 1.2 */ const gchar * mm_cdma_manual_activation_properties_get_min (MMCdmaManualActivationProperties *self) { g_return_val_if_fail (MM_IS_CDMA_MANUAL_ACTIVATION_PROPERTIES (self), NULL); return self->priv->min; } /* MIN is max 15 characters */ static gboolean validate_min (const gchar *min, GError **error) { if (strlen (min) > 15) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS, "MIN must be maximum 15 characters long"); return FALSE; } return TRUE; } /** * mm_cdma_manual_activation_properties_set_min: * @self: A #MMCdmaManualActivationProperties. * @min: The MIN string, maximum 15 characters. * @error: Return location for error or %NULL. * * Sets the Mobile Identification Number. * * Returns: %TRUE if the MIN was successfully set, or %FALSE if @error is set. * * Since: 1.2 */ gboolean mm_cdma_manual_activation_properties_set_min (MMCdmaManualActivationProperties *self, const gchar *min, GError **error) { g_return_val_if_fail (MM_IS_CDMA_MANUAL_ACTIVATION_PROPERTIES (self), FALSE); if (!validate_min (min, error)) return FALSE; g_free (self->priv->min); self->priv->min = g_strdup (min); return TRUE; } /*****************************************************************************/ /** * mm_cdma_manual_activation_properties_get_mn_ha_key: * @self: A #MMCdmaManualActivationProperties. * * Gets the MN-HA key. * * Returns: (transfer none): The MN-HA key. Do not free the returned value, it * is owned by @self. * * Since: 1.2 */ const gchar * mm_cdma_manual_activation_properties_get_mn_ha_key (MMCdmaManualActivationProperties *self) { g_return_val_if_fail (MM_IS_CDMA_MANUAL_ACTIVATION_PROPERTIES (self), NULL); return self->priv->mn_ha_key; } /* MN-HA key is max 16 characters */ static gboolean validate_mn_ha_key (const gchar *mn_ha_key, GError **error) { if (strlen (mn_ha_key) > 16) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS, "MN-HA key must be maximum 16 characters long"); return FALSE; } return TRUE; } /** * mm_cdma_manual_activation_properties_set_mn_ha_key: * @self: A #MMCdmaManualActivationProperties. * @mn_ha_key: The MN-HA key string, maximum 16 characters. * @error: Return location for error or %NULL. * * Sets the Mobile Identification Number. * * Returns: %TRUE if the MN-HA key was successfully set, or %FALSE if @error * is set. * * Since: 1.2 */ gboolean mm_cdma_manual_activation_properties_set_mn_ha_key (MMCdmaManualActivationProperties *self, const gchar *mn_ha_key, GError **error) { g_return_val_if_fail (MM_IS_CDMA_MANUAL_ACTIVATION_PROPERTIES (self), FALSE); if (!validate_mn_ha_key (mn_ha_key, error)) return FALSE; g_free (self->priv->mn_ha_key); self->priv->mn_ha_key = g_strdup (mn_ha_key); return TRUE; } /*****************************************************************************/ /** * mm_cdma_manual_activation_properties_get_mn_aaa_key: * @self: A #MMCdmaManualActivationProperties. * * Gets the MN-AAA key. * * Returns: (transfer none): The MN-AAA key. Do not free the returned value, it * is owned by @self. * * Since: 1.2 */ const gchar * mm_cdma_manual_activation_properties_get_mn_aaa_key (MMCdmaManualActivationProperties *self) { g_return_val_if_fail (MM_IS_CDMA_MANUAL_ACTIVATION_PROPERTIES (self), NULL); return self->priv->mn_aaa_key; } /* MN-AAA key is max 16 characters */ static gboolean validate_mn_aaa_key (const gchar *mn_aaa_key, GError **error) { if (strlen (mn_aaa_key) > 16) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS, "MN-AAA key must be maximum 16 characters long"); return FALSE; } return TRUE; } /** * mm_cdma_manual_activation_properties_set_mn_aaa_key: * @self: A #MMCdmaManualActivationProperties. * @mn_aaa_key: The MN-AAA key string, maximum 16 characters. * @error: Return location for error or %NULL. * * Sets the Mobile Identification Number. * * Returns: %TRUE if the MN-AAA key was successfully set, or %FALSE if @error is * set. * * Since: 1.2 */ gboolean mm_cdma_manual_activation_properties_set_mn_aaa_key (MMCdmaManualActivationProperties *self, const gchar *mn_aaa_key, GError **error) { g_return_val_if_fail (MM_IS_CDMA_MANUAL_ACTIVATION_PROPERTIES (self), FALSE); if (!validate_mn_aaa_key (mn_aaa_key, error)) return FALSE; g_free (self->priv->mn_aaa_key); self->priv->mn_aaa_key = g_strdup (mn_aaa_key); return TRUE; } /*****************************************************************************/ /** * mm_cdma_manual_activation_properties_get_prl: * @self: A #MMCdmaManualActivationProperties. * @prl_len: (out): Size of the returned PRL. * * Gets the Preferred Roaming List. * * Returns: (transfer none): The PRL. Do not free the returned value, it is * owned by @self. * * Since: 1.2 */ const guint8 * mm_cdma_manual_activation_properties_get_prl (MMCdmaManualActivationProperties *self, gsize *prl_len) { g_return_val_if_fail (MM_IS_CDMA_MANUAL_ACTIVATION_PROPERTIES (self), NULL); if (prl_len) *prl_len = (self->priv->prl ? self->priv->prl->len : 0); return (self->priv->prl ? self->priv->prl->data : NULL); } /** * mm_cdma_manual_activation_properties_peek_prl_bytearray: * @self: A #MMCdmaManualActivationProperties. * * Gets the Preferred Roaming List. * * Returns: (transfer none): A #GByteArray with the PRL, or %NULL if it doesn't * contain any. Do not free the returned value, it is owned by @self. * * Since: 1.2 */ GByteArray * mm_cdma_manual_activation_properties_peek_prl_bytearray (MMCdmaManualActivationProperties *self) { g_return_val_if_fail (MM_IS_CDMA_MANUAL_ACTIVATION_PROPERTIES (self), NULL); return self->priv->prl; } /** * mm_cdma_manual_activation_properties_get_prl_bytearray: * @self: A #MMCdmaManualActivationProperties. * * Gets the Preferred Roaming List. * * Returns: (transfer full): A #GByteArray with the PRL, or %NULL if it doesn't * contain any. The returned value should be freed with g_byte_array_unref(). * * Since: 1.2 */ GByteArray * mm_cdma_manual_activation_properties_get_prl_bytearray (MMCdmaManualActivationProperties *self) { g_return_val_if_fail (MM_IS_CDMA_MANUAL_ACTIVATION_PROPERTIES (self), NULL); return (self->priv->prl ? g_byte_array_ref (self->priv->prl) : NULL); } static gboolean validate_prl (const guint8 *prl, gsize prl_size, GError **error) { if (prl_size > 16384) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS, "PRL must be maximum 16384 bytes long"); return FALSE; } return TRUE; } /** * mm_cdma_manual_activation_properties_set_prl: * @self: A #MMCdmaManualActivationProperties. * @prl: The PRL. * @prl_length: Length of @prl. * @error: Return location for error or %NULL. * * Sets the Preferred Roaming List. * * Returns: %TRUE if the PRL was successfully set, or %FALSE if @error is set. * * Since: 1.2 */ gboolean mm_cdma_manual_activation_properties_set_prl (MMCdmaManualActivationProperties *self, const guint8 *prl, gsize prl_length, GError **error) { g_return_val_if_fail (MM_IS_CDMA_MANUAL_ACTIVATION_PROPERTIES (self), FALSE); if (!validate_prl (prl, prl_length, error)) return FALSE; if (self->priv->prl) g_byte_array_unref (self->priv->prl); if (prl && prl_length) self->priv->prl = g_byte_array_append (g_byte_array_sized_new (prl_length), prl, prl_length); else self->priv->prl = NULL; return TRUE; } /** * mm_cdma_manual_activation_properties_set_prl_bytearray: * @self: A #MMCdmaManualActivationProperties. * @prl: A #GByteArray with the PRL to set. This method takes a new reference * of @prl. * @error: Return location for error or %NULL. * * Sets the Preferred Roaming List. * * Returns: %TRUE if the PRL was successfully set, or %FALSE if @error is set. * * Since: 1.2 */ gboolean mm_cdma_manual_activation_properties_set_prl_bytearray (MMCdmaManualActivationProperties *self, GByteArray *prl, GError **error) { g_return_val_if_fail (MM_IS_CDMA_MANUAL_ACTIVATION_PROPERTIES (self), FALSE); if (!validate_prl (prl->data, prl->len, error)) return FALSE; if (self->priv->prl) g_byte_array_unref (self->priv->prl); self->priv->prl = (prl ? g_byte_array_ref (prl) : NULL); return TRUE; } /*****************************************************************************/ /** * mm_cdma_manual_activation_properties_get_dictionary: (skip) */ GVariant * mm_cdma_manual_activation_properties_get_dictionary (MMCdmaManualActivationProperties *self) { GVariantBuilder builder; g_return_val_if_fail (MM_IS_CDMA_MANUAL_ACTIVATION_PROPERTIES (self), NULL); /* We do allow NULL */ if (!self) return NULL; g_return_val_if_fail (MM_IS_CDMA_MANUAL_ACTIVATION_PROPERTIES (self), NULL); g_variant_builder_init (&builder, G_VARIANT_TYPE ("a{sv}")); if (self->priv->spc) g_variant_builder_add (&builder, "{sv}", PROPERTY_SPC, g_variant_new_string (self->priv->spc)); if (self->priv->sid_set) g_variant_builder_add (&builder, "{sv}", PROPERTY_SID, g_variant_new_uint16 (self->priv->sid)); if (self->priv->mdn) g_variant_builder_add (&builder, "{sv}", PROPERTY_MDN, g_variant_new_string (self->priv->mdn)); if (self->priv->min) g_variant_builder_add (&builder, "{sv}", PROPERTY_MIN, g_variant_new_string (self->priv->min)); if (self->priv->mn_ha_key) g_variant_builder_add (&builder, "{sv}", PROPERTY_MN_HA_KEY, g_variant_new_string (self->priv->mn_ha_key)); if (self->priv->mn_aaa_key) g_variant_builder_add (&builder, "{sv}", PROPERTY_MN_AAA_KEY, g_variant_new_string (self->priv->mn_aaa_key)); if (self->priv->prl) g_variant_builder_add ( &builder, "{sv}", PROPERTY_PRL, g_variant_new_from_data (G_VARIANT_TYPE ("ay"), self->priv->prl->data, self->priv->prl->len * sizeof (guint8), TRUE, NULL, NULL)); return g_variant_ref_sink (g_variant_builder_end (&builder)); } /*****************************************************************************/ static gboolean consume_variant (MMCdmaManualActivationProperties *self, const gchar *key, GVariant *value, GError **error) { if (g_str_equal (key, PROPERTY_SPC)) return (mm_cdma_manual_activation_properties_set_spc ( self, g_variant_get_string (value, NULL), error)); if (g_str_equal (key, PROPERTY_SID)) { mm_cdma_manual_activation_properties_set_sid ( self, g_variant_get_uint16 (value)); return TRUE; } if (g_str_equal (key, PROPERTY_MDN)) return (mm_cdma_manual_activation_properties_set_mdn ( self, g_variant_get_string (value, NULL), error)); if (g_str_equal (key, PROPERTY_MIN)) return (mm_cdma_manual_activation_properties_set_min ( self, g_variant_get_string (value, NULL), error)); if (g_str_equal (key, PROPERTY_MN_HA_KEY)) return (mm_cdma_manual_activation_properties_set_mn_ha_key ( self, g_variant_get_string (value, NULL), error)); if (g_str_equal (key, PROPERTY_MN_AAA_KEY)) return (mm_cdma_manual_activation_properties_set_mn_aaa_key ( self, g_variant_get_string (value, NULL), error)); if (g_str_equal (key, PROPERTY_PRL)) { const guint8 *prl; gsize prl_len = 0; prl = g_variant_get_fixed_array (value, &prl_len, sizeof (guint8)); return (mm_cdma_manual_activation_properties_set_prl ( self, prl, prl_len, error)); } /* Set error */ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS, "Invalid properties dictionary, unexpected key '%s'", key); return FALSE; } /** * mm_cdma_manual_activation_properties_new_from_dictionary: (skip) */ MMCdmaManualActivationProperties * mm_cdma_manual_activation_properties_new_from_dictionary (GVariant *dictionary, GError **error) { GError *inner_error = NULL; GVariantIter iter; gchar *key; GVariant *value; MMCdmaManualActivationProperties *self; if (!dictionary) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS, "Cannot create CDMA manual activation properties from empty dictionary"); return NULL; } if (!g_variant_is_of_type (dictionary, G_VARIANT_TYPE ("a{sv}"))) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS, "Cannot create CDMA manual activation properties from dictionary: " "invalid variant type received"); return NULL; } self = mm_cdma_manual_activation_properties_new (); g_variant_iter_init (&iter, dictionary); while (!inner_error && g_variant_iter_next (&iter, "{sv}", &key, &value)) { consume_variant (self, key, value, &inner_error); g_free (key); g_variant_unref (value); } /* If error, destroy the object */ if (inner_error) { g_propagate_error (error, inner_error); g_object_unref (self); return NULL; } /* If mandatory properties missing, destroy the object */ if (!self->priv->spc || !self->priv->sid_set || !self->priv->mdn || !self->priv->min) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS, "Cannot create CDMA manual activation properties from dictionary: " "mandatory parameter missing"); g_object_unref (self); return NULL; } return self; } /*****************************************************************************/ static gboolean consume_string (MMCdmaManualActivationProperties *self, const gchar *key, const gchar *value, GError **error) { if (g_str_equal (key, PROPERTY_SPC)) return mm_cdma_manual_activation_properties_set_spc (self, value, error); if (g_str_equal (key, PROPERTY_SID)) { guint sid; if (!mm_get_uint_from_str (value, &sid)) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS, "Invalid SID integer value: '%s'", value); return FALSE; } mm_cdma_manual_activation_properties_set_sid (self, sid); return TRUE; } if (g_str_equal (key, PROPERTY_MDN)) return mm_cdma_manual_activation_properties_set_mdn (self, value, error); if (g_str_equal (key, PROPERTY_MIN)) return mm_cdma_manual_activation_properties_set_min (self, value, error); if (g_str_equal (key, PROPERTY_MN_HA_KEY)) return mm_cdma_manual_activation_properties_set_mn_ha_key (self, value, error); if (g_str_equal (key, PROPERTY_MN_AAA_KEY)) return mm_cdma_manual_activation_properties_set_mn_aaa_key (self, value, error); if (g_str_equal (key, PROPERTY_PRL)) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS, "Invalid properties string, key '%s' cannot be given in a string", key); return FALSE; } /* Set error */ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS, "Invalid properties dictionary, unexpected key '%s'", key); return FALSE; } typedef struct { MMCdmaManualActivationProperties *properties; GError *error; } ParseKeyValueContext; static gboolean key_value_foreach (const gchar *key, const gchar *value, ParseKeyValueContext *ctx) { return consume_string (ctx->properties, key, value, &ctx->error); } /** * mm_cdma_manual_activation_properties_new_from_string: (skip) */ MMCdmaManualActivationProperties * mm_cdma_manual_activation_properties_new_from_string (const gchar *str, GError **error) { ParseKeyValueContext ctx; ctx.properties = mm_cdma_manual_activation_properties_new (); ctx.error = NULL; mm_common_parse_key_value_string (str, &ctx.error, (MMParseKeyValueForeachFn)key_value_foreach, &ctx); /* If error, destroy the object */ if (ctx.error) { g_propagate_error (error, ctx.error); g_object_unref (ctx.properties); ctx.properties = NULL; } return ctx.properties; } /*****************************************************************************/ /** * mm_cdma_manual_activation_properties_new: * * Creates a new #MMCdmaManualActivationProperties object. * * Returns: (transfer full): A #MMCdmaManualActivationProperties. The returned * value should be freed with g_object_unref(). * * Since: 1.2 */ MMCdmaManualActivationProperties * mm_cdma_manual_activation_properties_new (void) { return (MMCdmaManualActivationProperties *) g_object_new (MM_TYPE_CDMA_MANUAL_ACTIVATION_PROPERTIES, NULL); } static void mm_cdma_manual_activation_properties_init (MMCdmaManualActivationProperties *self) { self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, MM_TYPE_CDMA_MANUAL_ACTIVATION_PROPERTIES, MMCdmaManualActivationPropertiesPrivate); } static void finalize (GObject *object) { MMCdmaManualActivationProperties *self = MM_CDMA_MANUAL_ACTIVATION_PROPERTIES (object); g_free (self->priv->spc); g_free (self->priv->mdn); g_free (self->priv->min); g_free (self->priv->mn_ha_key); g_free (self->priv->mn_aaa_key); if (self->priv->prl) g_byte_array_unref (self->priv->prl); G_OBJECT_CLASS (mm_cdma_manual_activation_properties_parent_class)->finalize (object); } static void mm_cdma_manual_activation_properties_class_init (MMCdmaManualActivationPropertiesClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); g_type_class_add_private (object_class, sizeof (MMCdmaManualActivationPropertiesPrivate)); object_class->finalize = finalize; } ModemManager-1.23.4-dev/libmm-glib/mm-cdma-manual-activation-properties.h000066400000000000000000000160541456466623000262360ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * libmm-glib -- Access modem status & information from glib applications * * 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 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 Street, Fifth Floor, * Boston, MA 02110-1301 USA. * * Copyright (C) 2013 Google Inc. */ #ifndef MM_CDMA_MANUAL_ACTIVATION_PROPERTIES_H #define MM_CDMA_MANUAL_ACTIVATION_PROPERTIES_H #if !defined (__LIBMM_GLIB_H_INSIDE__) && !defined (LIBMM_GLIB_COMPILATION) #error "Only can be included directly." #endif #include #include G_BEGIN_DECLS #define MM_TYPE_CDMA_MANUAL_ACTIVATION_PROPERTIES (mm_cdma_manual_activation_properties_get_type ()) #define MM_CDMA_MANUAL_ACTIVATION_PROPERTIES(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_CDMA_MANUAL_ACTIVATION_PROPERTIES, MMCdmaManualActivationProperties)) #define MM_CDMA_MANUAL_ACTIVATION_PROPERTIES_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_CDMA_MANUAL_ACTIVATION_PROPERTIES, MMCdmaManualActivationPropertiesClass)) #define MM_IS_CDMA_MANUAL_ACTIVATION_PROPERTIES(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_CDMA_MANUAL_ACTIVATION_PROPERTIES)) #define MM_IS_CDMA_MANUAL_ACTIVATION_PROPERTIES_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_CDMA_MANUAL_ACTIVATION_PROPERTIES)) #define MM_CDMA_MANUAL_ACTIVATION_PROPERTIES_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_CDMA_MANUAL_ACTIVATION_PROPERTIES, MMCdmaManualActivationPropertiesClass)) typedef struct _MMCdmaManualActivationProperties MMCdmaManualActivationProperties; typedef struct _MMCdmaManualActivationPropertiesClass MMCdmaManualActivationPropertiesClass; typedef struct _MMCdmaManualActivationPropertiesPrivate MMCdmaManualActivationPropertiesPrivate; /** * MMCdmaManualActivationProperties: * * The #MMCdmaManualActivationProperties structure contains private data and should only be accessed * using the provided API. */ struct _MMCdmaManualActivationProperties { /*< private >*/ GObject parent; MMCdmaManualActivationPropertiesPrivate *priv; }; struct _MMCdmaManualActivationPropertiesClass { /*< private >*/ GObjectClass parent; }; GType mm_cdma_manual_activation_properties_get_type (void); G_DEFINE_AUTOPTR_CLEANUP_FUNC (MMCdmaManualActivationProperties, g_object_unref) MMCdmaManualActivationProperties *mm_cdma_manual_activation_properties_new (void); gboolean mm_cdma_manual_activation_properties_set_spc (MMCdmaManualActivationProperties *self, const gchar *spc, GError **error); void mm_cdma_manual_activation_properties_set_sid (MMCdmaManualActivationProperties *self, guint16 sid); gboolean mm_cdma_manual_activation_properties_set_mdn (MMCdmaManualActivationProperties *self, const gchar *mdn, GError **error); gboolean mm_cdma_manual_activation_properties_set_min (MMCdmaManualActivationProperties *self, const gchar *min, GError **error); gboolean mm_cdma_manual_activation_properties_set_mn_ha_key (MMCdmaManualActivationProperties *self, const gchar *mn_ha_key, GError **error); gboolean mm_cdma_manual_activation_properties_set_mn_aaa_key (MMCdmaManualActivationProperties *self, const gchar *mn_aaa_key, GError **error); gboolean mm_cdma_manual_activation_properties_set_prl (MMCdmaManualActivationProperties *self, const guint8 *prl, gsize prl_length, GError **error); gboolean mm_cdma_manual_activation_properties_set_prl_bytearray (MMCdmaManualActivationProperties *self, GByteArray *prl, GError **error); const gchar *mm_cdma_manual_activation_properties_get_spc (MMCdmaManualActivationProperties *self); guint16 mm_cdma_manual_activation_properties_get_sid (MMCdmaManualActivationProperties *self); const gchar *mm_cdma_manual_activation_properties_get_mdn (MMCdmaManualActivationProperties *self); const gchar *mm_cdma_manual_activation_properties_get_min (MMCdmaManualActivationProperties *self); const gchar *mm_cdma_manual_activation_properties_get_mn_ha_key (MMCdmaManualActivationProperties *self); const gchar *mm_cdma_manual_activation_properties_get_mn_aaa_key (MMCdmaManualActivationProperties *self); const guint8 *mm_cdma_manual_activation_properties_get_prl (MMCdmaManualActivationProperties *self, gsize *prl_len); GByteArray *mm_cdma_manual_activation_properties_peek_prl_bytearray (MMCdmaManualActivationProperties *self); GByteArray *mm_cdma_manual_activation_properties_get_prl_bytearray (MMCdmaManualActivationProperties *self); /*****************************************************************************/ /* ModemManager/libmm-glib/mmcli specific methods */ #if defined (_LIBMM_INSIDE_MM) || \ defined (_LIBMM_INSIDE_MMCLI) || \ defined (LIBMM_GLIB_COMPILATION) MMCdmaManualActivationProperties *mm_cdma_manual_activation_properties_new_from_string (const gchar *str, GError **error); MMCdmaManualActivationProperties *mm_cdma_manual_activation_properties_new_from_dictionary (GVariant *dictionary, GError **error); GVariant *mm_cdma_manual_activation_properties_get_dictionary (MMCdmaManualActivationProperties *self); #endif G_END_DECLS #endif /* MM_CDMA_MANUAL_ACTIVATION_PROPERTIES_H */ ModemManager-1.23.4-dev/libmm-glib/mm-cell-info-cdma.c000066400000000000000000000210051456466623000222630ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * libmm-glib -- Access modem status & information from glib applications * * 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 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 Street, Fifth Floor, * Boston, MA 02110-1301 USA. * * Copyright (C) 2022 Aleksander Morgado */ #include "mm-helpers.h" #include "mm-cell-info-cdma.h" /** * SECTION: mm-cell-info-cdma * @title: MMCellInfoCdma * @short_description: Helper object to report CDMA cell info * * The #MMCellInfoCdma is an object used to report CDMA cell * information. * * The object inherits from the generic #MMCellInfo. */ G_DEFINE_TYPE (MMCellInfoCdma, mm_cell_info_cdma, MM_TYPE_CELL_INFO) #define PROPERTY_NID "nid" #define PROPERTY_SID "sid" #define PROPERTY_BASE_STATION_ID "base-station-id" #define PROPERTY_REF_PN "ref-pn" #define PROPERTY_PILOT_STRENGTH "pilot-strength" struct _MMCellInfoCdmaPrivate { gchar *nid; gchar *sid; gchar *base_station_id; gchar *ref_pn; guint pilot_strength; }; /*****************************************************************************/ /** * mm_cell_info_cdma_get_nid: * @self: a #MMCellInfoCdma. * * Get the CDMA network id. * * Encoded in upper-case hexadecimal format without leading zeros. * * Returns: (transfer none): the CDMA network id, or %NULL if not available. * * Since: 1.20 */ const gchar * mm_cell_info_cdma_get_nid (MMCellInfoCdma *self) { g_return_val_if_fail (MM_IS_CELL_INFO_CDMA (self), NULL); RETURN_NON_EMPTY_CONSTANT_STRING (self->priv->nid); } /** * mm_cell_info_cdma_set_nid: (skip) */ void mm_cell_info_cdma_set_nid (MMCellInfoCdma *self, const gchar *nid) { g_free (self->priv->nid); self->priv->nid = g_strdup (nid); } /** * mm_cell_info_cdma_get_sid: * @self: a #MMCellInfoCdma. * * Get the CDMA system id. * * Encoded in upper-case hexadecimal format without leading zeros. * * Returns: (transfer none): the CDMA system id, or %NULL if not available. * * Since: 1.20 */ const gchar * mm_cell_info_cdma_get_sid (MMCellInfoCdma *self) { g_return_val_if_fail (MM_IS_CELL_INFO_CDMA (self), NULL); RETURN_NON_EMPTY_CONSTANT_STRING (self->priv->sid); } /** * mm_cell_info_cdma_set_sid: (skip) */ void mm_cell_info_cdma_set_sid (MMCellInfoCdma *self, const gchar *sid) { g_free (self->priv->sid); self->priv->sid = g_strdup (sid); } /** * mm_cell_info_cdma_get_base_station_id: * @self: a #MMCellInfoCdma. * * Get the CDMA base station id. * * Encoded in upper-case hexadecimal format without leading zeros. * * Returns: (transfer none): the CDMA base station id, or %NULL if not available. * * Since: 1.20 */ const gchar * mm_cell_info_cdma_get_base_station_id (MMCellInfoCdma *self) { g_return_val_if_fail (MM_IS_CELL_INFO_CDMA (self), NULL); RETURN_NON_EMPTY_CONSTANT_STRING (self->priv->base_station_id); } /** * mm_cell_info_cdma_set_base_station_id: (skip) */ void mm_cell_info_cdma_set_base_station_id (MMCellInfoCdma *self, const gchar *base_station_id) { g_free (self->priv->base_station_id); self->priv->base_station_id = g_strdup (base_station_id); } /** * mm_cell_info_cdma_get_ref_pn: * @self: a #MMCellInfoCdma. * * Get the CDMA base station PN number. * * Encoded in upper-case hexadecimal format without leading zeros. * * Returns: (transfer none): the CDMA base station PN number, or %NULL if not available. * * Since: 1.20 */ const gchar * mm_cell_info_cdma_get_ref_pn (MMCellInfoCdma *self) { g_return_val_if_fail (MM_IS_CELL_INFO_CDMA (self), NULL); RETURN_NON_EMPTY_CONSTANT_STRING (self->priv->ref_pn); } /** * mm_cell_info_cdma_set_ref_pn: (skip) */ void mm_cell_info_cdma_set_ref_pn (MMCellInfoCdma *self, const gchar *ref_pn) { g_free (self->priv->ref_pn); self->priv->ref_pn = g_strdup (ref_pn); } /** * mm_cell_info_cdma_get_pilot_strength: * @self: a #MMCellInfoCdma. * * Get the signal strength of the pilot. * * Given in the same format and scale as the GSM SINR level. * * Returns: the pilot strength, or %G_MAXUINT if not available. * * Since: 1.20 */ guint mm_cell_info_cdma_get_pilot_strength (MMCellInfoCdma *self) { g_return_val_if_fail (MM_IS_CELL_INFO_CDMA (self), G_MAXUINT); return self->priv->pilot_strength; } /** * mm_cell_info_cdma_set_pilot_strength: (skip) */ void mm_cell_info_cdma_set_pilot_strength (MMCellInfoCdma *self, guint pilot_strength) { self->priv->pilot_strength = pilot_strength; } /*****************************************************************************/ static GString * build_string (MMCellInfo *_self) { MMCellInfoCdma *self = MM_CELL_INFO_CDMA (_self); GString *str; str = g_string_new (NULL); MM_CELL_INFO_BUILD_STRING_APPEND ("nid", "%s", nid, NULL); MM_CELL_INFO_BUILD_STRING_APPEND ("sid", "%s", sid, NULL); MM_CELL_INFO_BUILD_STRING_APPEND ("base station id", "%s", base_station_id, NULL); MM_CELL_INFO_BUILD_STRING_APPEND ("ref pn", "%s", ref_pn, NULL); MM_CELL_INFO_BUILD_STRING_APPEND ("pilot strength", "%u", pilot_strength, G_MAXUINT); return str; } /*****************************************************************************/ /** * mm_cell_info_cdma_get_dictionary: (skip) */ static GVariantDict * get_dictionary (MMCellInfo *_self) { MMCellInfoCdma *self = MM_CELL_INFO_CDMA (_self); GVariantDict *dict; dict = g_variant_dict_new (NULL); MM_CELL_INFO_GET_DICTIONARY_INSERT (NID, nid, string, NULL); MM_CELL_INFO_GET_DICTIONARY_INSERT (SID, sid, string, NULL); MM_CELL_INFO_GET_DICTIONARY_INSERT (BASE_STATION_ID, base_station_id, string, NULL); MM_CELL_INFO_GET_DICTIONARY_INSERT (REF_PN, ref_pn, string, NULL); MM_CELL_INFO_GET_DICTIONARY_INSERT (PILOT_STRENGTH, pilot_strength, uint32, G_MAXUINT); return dict; } /*****************************************************************************/ /** * mm_cell_info_cdma_new_from_dictionary: (skip) */ MMCellInfo * mm_cell_info_cdma_new_from_dictionary (GVariantDict *dict) { MMCellInfoCdma *self; self = MM_CELL_INFO_CDMA (g_object_new (MM_TYPE_CELL_INFO_CDMA, NULL)); if (dict) { MM_CELL_INFO_NEW_FROM_DICTIONARY_STRING_SET (cdma, NID, nid); MM_CELL_INFO_NEW_FROM_DICTIONARY_STRING_SET (cdma, SID, sid); MM_CELL_INFO_NEW_FROM_DICTIONARY_STRING_SET (cdma, BASE_STATION_ID, base_station_id); MM_CELL_INFO_NEW_FROM_DICTIONARY_STRING_SET (cdma, REF_PN, ref_pn); MM_CELL_INFO_NEW_FROM_DICTIONARY_NUM_SET (cdma, PILOT_STRENGTH, pilot_strength, UINT32, uint32); } return MM_CELL_INFO (self); } /*****************************************************************************/ static void mm_cell_info_cdma_init (MMCellInfoCdma *self) { self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, MM_TYPE_CELL_INFO_CDMA, MMCellInfoCdmaPrivate); self->priv->pilot_strength = G_MAXUINT; } static void finalize (GObject *object) { MMCellInfoCdma *self = MM_CELL_INFO_CDMA (object); g_free (self->priv->sid); g_free (self->priv->nid); g_free (self->priv->base_station_id); g_free (self->priv->ref_pn); G_OBJECT_CLASS (mm_cell_info_cdma_parent_class)->finalize (object); } static void mm_cell_info_cdma_class_init (MMCellInfoCdmaClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); MMCellInfoClass *cell_info_class = MM_CELL_INFO_CLASS (klass); g_type_class_add_private (object_class, sizeof (MMCellInfoCdmaPrivate)); object_class->finalize = finalize; cell_info_class->get_dictionary = get_dictionary; cell_info_class->build_string = build_string; } ModemManager-1.23.4-dev/libmm-glib/mm-cell-info-cdma.h000066400000000000000000000075731456466623000223060ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * libmm-glib -- Access modem status & information from glib applications * * 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 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 Street, Fifth Floor, * Boston, MA 02110-1301 USA. * * Copyright (C) 2022 Aleksander Morgado */ #ifndef MM_CELL_INFO_CDMA_H #define MM_CELL_INFO_CDMA_H #if !defined (__LIBMM_GLIB_H_INSIDE__) && !defined (LIBMM_GLIB_COMPILATION) #error "Only can be included directly." #endif #include #include #include "mm-cell-info.h" G_BEGIN_DECLS #define MM_TYPE_CELL_INFO_CDMA (mm_cell_info_cdma_get_type ()) #define MM_CELL_INFO_CDMA(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_CELL_INFO_CDMA, MMCellInfoCdma)) #define MM_CELL_INFO_CDMA_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_CELL_INFO_CDMA, MMCellInfoCdmaClass)) #define MM_IS_CELL_INFO_CDMA(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_CELL_INFO_CDMA)) #define MM_IS_CELL_INFO_CDMA_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_CELL_INFO_CDMA)) #define MM_CELL_INFO_CDMA_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_CELL_INFO_CDMA, MMCellInfoCdmaClass)) typedef struct _MMCellInfoCdma MMCellInfoCdma; typedef struct _MMCellInfoCdmaClass MMCellInfoCdmaClass; typedef struct _MMCellInfoCdmaPrivate MMCellInfoCdmaPrivate; /** * MMCellInfoCdma: * * The #MMCellInfoCdma structure contains private data and should only be * accessed using the provided API. */ struct _MMCellInfoCdma { /*< private >*/ MMCellInfo parent; MMCellInfoCdmaPrivate *priv; }; struct _MMCellInfoCdmaClass { /*< private >*/ MMCellInfoClass parent; }; GType mm_cell_info_cdma_get_type (void); G_DEFINE_AUTOPTR_CLEANUP_FUNC (MMCellInfoCdma, g_object_unref) const gchar *mm_cell_info_cdma_get_nid (MMCellInfoCdma *self); const gchar *mm_cell_info_cdma_get_sid (MMCellInfoCdma *self); const gchar *mm_cell_info_cdma_get_base_station_id (MMCellInfoCdma *self); const gchar *mm_cell_info_cdma_get_ref_pn (MMCellInfoCdma *self); guint mm_cell_info_cdma_get_pilot_strength (MMCellInfoCdma *self); /*****************************************************************************/ /* ModemManager/libmm-glib/mmcli specific methods */ #if defined (_LIBMM_INSIDE_MM) || \ defined (_LIBMM_INSIDE_MMCLI) || \ defined (LIBMM_GLIB_COMPILATION) void mm_cell_info_cdma_set_nid (MMCellInfoCdma *self, const gchar *nid); void mm_cell_info_cdma_set_sid (MMCellInfoCdma *self, const gchar *sid); void mm_cell_info_cdma_set_base_station_id (MMCellInfoCdma *self, const gchar *base_station_id); void mm_cell_info_cdma_set_ref_pn (MMCellInfoCdma *self, const gchar *ref_pn); void mm_cell_info_cdma_set_pilot_strength (MMCellInfoCdma *self, guint pilot_strength); MMCellInfo *mm_cell_info_cdma_new_from_dictionary (GVariantDict *dict); #endif G_END_DECLS #endif /* MM_CELL_INFO_CDMA_H */ ModemManager-1.23.4-dev/libmm-glib/mm-cell-info-gsm.c000066400000000000000000000246201456466623000221530ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * libmm-glib -- Access modem status & information from glib applications * * 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 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 Street, Fifth Floor, * Boston, MA 02110-1301 USA. * * Copyright (C) 2022 Aleksander Morgado */ #include "mm-helpers.h" #include "mm-cell-info-gsm.h" /** * SECTION: mm-cell-info-gsm * @title: MMCellInfoGsm * @short_description: Helper object to report GSM cell info * * The #MMCellInfoGsm is an object used to report GSM cell * information. * * The object inherits from the generic #MMCellInfo. */ G_DEFINE_TYPE (MMCellInfoGsm, mm_cell_info_gsm, MM_TYPE_CELL_INFO) #define PROPERTY_OPERATOR_ID "operator-id" #define PROPERTY_LAC "lac" #define PROPERTY_CI "ci" #define PROPERTY_TIMING_ADVANCE "timing-advance" #define PROPERTY_ARFCN "arfcn" #define PROPERTY_BASE_STATION_ID "base-station-id" #define PROPERTY_RX_LEVEL "rx-level" struct _MMCellInfoGsmPrivate { gchar *operator_id; gchar *lac; gchar *ci; guint timing_advance; guint arfcn; gchar *base_station_id; guint rx_level; }; /*****************************************************************************/ /** * mm_cell_info_gsm_get_operator_id: * @self: a #MMCellInfoGsm. * * Get the PLMN MCC/MNC. * * Returns: (transfer none): the MCCMNC, or %NULL if not available. * * Since: 1.20 */ const gchar * mm_cell_info_gsm_get_operator_id (MMCellInfoGsm *self) { g_return_val_if_fail (MM_IS_CELL_INFO_GSM (self), NULL); RETURN_NON_EMPTY_CONSTANT_STRING (self->priv->operator_id); } /** * mm_cell_info_gsm_set_operator_id: (skip) */ void mm_cell_info_gsm_set_operator_id (MMCellInfoGsm *self, const gchar *operator_id) { g_free (self->priv->operator_id); self->priv->operator_id = g_strdup (operator_id); } /** * mm_cell_info_gsm_get_lac: * @self: a #MMCellInfoGsm. * * Get the two-byte Location Area Code of the base station. * * Encoded in upper-case hexadecimal format without leading zeros, * as specified in 3GPP TS 27.007. * * Returns: (transfer none): the MCCMNC, or %NULL if not available. * * Since: 1.20 */ const gchar * mm_cell_info_gsm_get_lac (MMCellInfoGsm *self) { g_return_val_if_fail (MM_IS_CELL_INFO_GSM (self), NULL); RETURN_NON_EMPTY_CONSTANT_STRING (self->priv->lac); } /** * mm_cell_info_gsm_set_lac: (skip) */ void mm_cell_info_gsm_set_lac (MMCellInfoGsm *self, const gchar *lac) { g_free (self->priv->lac); self->priv->lac = g_strdup (lac); } /** * mm_cell_info_gsm_get_ci: * @self: a #MMCellInfoGsm. * * Get the two- or four-byte Cell Identifier. * * Encoded in upper-case hexadecimal format without leading zeros, * as specified in 3GPP TS 27.007. * * Returns: (transfer none): the MCCMNC, or %NULL if not available. * * Since: 1.20 */ const gchar * mm_cell_info_gsm_get_ci (MMCellInfoGsm *self) { g_return_val_if_fail (MM_IS_CELL_INFO_GSM (self), NULL); RETURN_NON_EMPTY_CONSTANT_STRING (self->priv->ci); } /** * mm_cell_info_gsm_set_ci: (skip) */ void mm_cell_info_gsm_set_ci (MMCellInfoGsm *self, const gchar *ci) { g_free (self->priv->ci); self->priv->ci = g_strdup (ci); } /** * mm_cell_info_gsm_get_timing_advance: * @self: a #MMCellInfoGsm. * * Get the measured delay (in bit periods) of an access burst transmission * on the RACH or PRACH to the expected signal from a mobile station at zero * distance under static channel conditions. * * Returns: the timing advance, or %G_MAXUINT if not available. * * Since: 1.20 */ guint mm_cell_info_gsm_get_timing_advance (MMCellInfoGsm *self) { g_return_val_if_fail (MM_IS_CELL_INFO_GSM (self), G_MAXUINT); return self->priv->timing_advance; } /** * mm_cell_info_gsm_set_timing_advance: (skip) */ void mm_cell_info_gsm_set_timing_advance (MMCellInfoGsm *self, guint timing_advance) { self->priv->timing_advance = timing_advance; } /** * mm_cell_info_gsm_get_arfcn: * @self: a #MMCellInfoGsm. * * Get the absolute RF channel number. * * Returns: the ARFCN, or %G_MAXUINT if not available. * * Since: 1.20 */ guint mm_cell_info_gsm_get_arfcn (MMCellInfoGsm *self) { g_return_val_if_fail (MM_IS_CELL_INFO_GSM (self), G_MAXUINT); return self->priv->arfcn; } /** * mm_cell_info_gsm_set_arfcn: (skip) */ void mm_cell_info_gsm_set_arfcn (MMCellInfoGsm *self, guint arfcn) { self->priv->arfcn = arfcn; } /** * mm_cell_info_gsm_get_base_station_id: * @self: a #MMCellInfoGsm. * * Get the GSM base station id, in upper-case hexadecimal format without leading * zeros. E.g. "3F". * * Returns: (transfer none): the GSM base station id, or %NULL if not available. * * Since: 1.20 */ const gchar * mm_cell_info_gsm_get_base_station_id (MMCellInfoGsm *self) { g_return_val_if_fail (MM_IS_CELL_INFO_GSM (self), NULL); RETURN_NON_EMPTY_CONSTANT_STRING (self->priv->base_station_id); } /** * mm_cell_info_gsm_set_base_station_id: (skip) */ void mm_cell_info_gsm_set_base_station_id (MMCellInfoGsm *self, const gchar *base_station_id) { g_free (self->priv->base_station_id); self->priv->base_station_id = g_strdup (base_station_id); } /** * mm_cell_info_gsm_get_rx_level: * @self: a #MMCellInfoGsm. * * Get the serving cell RX measurement. * * Returns: the rx level, or %G_MAXUINT if not available. * * Since: 1.20 */ guint mm_cell_info_gsm_get_rx_level (MMCellInfoGsm *self) { g_return_val_if_fail (MM_IS_CELL_INFO_GSM (self), G_MAXUINT); return self->priv->rx_level; } /** * mm_cell_info_gsm_set_rx_level: (skip) */ void mm_cell_info_gsm_set_rx_level (MMCellInfoGsm *self, guint rx_level) { self->priv->rx_level = rx_level; } /*****************************************************************************/ static GString * build_string (MMCellInfo *_self) { MMCellInfoGsm *self = MM_CELL_INFO_GSM (_self); GString *str; str = g_string_new (NULL); MM_CELL_INFO_BUILD_STRING_APPEND ("operator id", "%s", operator_id, NULL); MM_CELL_INFO_BUILD_STRING_APPEND ("lac", "%s", lac, NULL); MM_CELL_INFO_BUILD_STRING_APPEND ("ci", "%s", ci, NULL); MM_CELL_INFO_BUILD_STRING_APPEND ("timing advance", "%u", timing_advance, G_MAXUINT); MM_CELL_INFO_BUILD_STRING_APPEND ("arfcn", "%u", arfcn, G_MAXUINT); MM_CELL_INFO_BUILD_STRING_APPEND ("base station id", "%s", base_station_id, NULL); MM_CELL_INFO_BUILD_STRING_APPEND ("rx level", "%u", rx_level, G_MAXUINT); return str; } /*****************************************************************************/ /** * mm_cell_info_gsm_get_dictionary: (skip) */ static GVariantDict * get_dictionary (MMCellInfo *_self) { MMCellInfoGsm *self = MM_CELL_INFO_GSM (_self); GVariantDict *dict; dict = g_variant_dict_new (NULL); MM_CELL_INFO_GET_DICTIONARY_INSERT (OPERATOR_ID, operator_id, string, NULL); MM_CELL_INFO_GET_DICTIONARY_INSERT (LAC, lac, string, NULL); MM_CELL_INFO_GET_DICTIONARY_INSERT (CI, ci, string, NULL); MM_CELL_INFO_GET_DICTIONARY_INSERT (TIMING_ADVANCE, timing_advance, uint32, G_MAXUINT); MM_CELL_INFO_GET_DICTIONARY_INSERT (ARFCN, arfcn, uint32, G_MAXUINT); MM_CELL_INFO_GET_DICTIONARY_INSERT (BASE_STATION_ID, base_station_id, string, NULL); MM_CELL_INFO_GET_DICTIONARY_INSERT (RX_LEVEL, rx_level, uint32, G_MAXUINT); return dict; } /*****************************************************************************/ /** * mm_cell_info_gsm_new_from_dictionary: (skip) */ MMCellInfo * mm_cell_info_gsm_new_from_dictionary (GVariantDict *dict) { MMCellInfoGsm *self; self = MM_CELL_INFO_GSM (g_object_new (MM_TYPE_CELL_INFO_GSM, NULL)); if (dict) { MM_CELL_INFO_NEW_FROM_DICTIONARY_STRING_SET (gsm, OPERATOR_ID, operator_id); MM_CELL_INFO_NEW_FROM_DICTIONARY_STRING_SET (gsm, LAC, lac); MM_CELL_INFO_NEW_FROM_DICTIONARY_STRING_SET (gsm, CI, ci); MM_CELL_INFO_NEW_FROM_DICTIONARY_NUM_SET (gsm, TIMING_ADVANCE, timing_advance, UINT32, uint32); MM_CELL_INFO_NEW_FROM_DICTIONARY_NUM_SET (gsm, ARFCN, arfcn, UINT32, uint32); MM_CELL_INFO_NEW_FROM_DICTIONARY_STRING_SET (gsm, BASE_STATION_ID, base_station_id); MM_CELL_INFO_NEW_FROM_DICTIONARY_NUM_SET (gsm, RX_LEVEL, rx_level, UINT32, uint32); } return MM_CELL_INFO (self); } /*****************************************************************************/ static void mm_cell_info_gsm_init (MMCellInfoGsm *self) { self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, MM_TYPE_CELL_INFO_GSM, MMCellInfoGsmPrivate); self->priv->timing_advance = G_MAXUINT; self->priv->arfcn = G_MAXUINT; self->priv->rx_level = G_MAXUINT; } static void finalize (GObject *object) { MMCellInfoGsm *self = MM_CELL_INFO_GSM (object); g_free (self->priv->operator_id); g_free (self->priv->lac); g_free (self->priv->ci); g_free (self->priv->base_station_id); G_OBJECT_CLASS (mm_cell_info_gsm_parent_class)->finalize (object); } static void mm_cell_info_gsm_class_init (MMCellInfoGsmClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); MMCellInfoClass *cell_info_class = MM_CELL_INFO_CLASS (klass); g_type_class_add_private (object_class, sizeof (MMCellInfoGsmPrivate)); object_class->finalize = finalize; cell_info_class->get_dictionary = get_dictionary; cell_info_class->build_string = build_string; } ModemManager-1.23.4-dev/libmm-glib/mm-cell-info-gsm.h000066400000000000000000000103311456466623000221520ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * libmm-glib -- Access modem status & information from glib applications * * 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 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 Street, Fifth Floor, * Boston, MA 02110-1301 USA. * * Copyright (C) 2022 Aleksander Morgado */ #ifndef MM_CELL_INFO_GSM_H #define MM_CELL_INFO_GSM_H #if !defined (__LIBMM_GLIB_H_INSIDE__) && !defined (LIBMM_GLIB_COMPILATION) #error "Only can be included directly." #endif #include #include #include "mm-cell-info.h" G_BEGIN_DECLS #define MM_TYPE_CELL_INFO_GSM (mm_cell_info_gsm_get_type ()) #define MM_CELL_INFO_GSM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_CELL_INFO_GSM, MMCellInfoGsm)) #define MM_CELL_INFO_GSM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_CELL_INFO_GSM, MMCellInfoGsmClass)) #define MM_IS_CELL_INFO_GSM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_CELL_INFO_GSM)) #define MM_IS_CELL_INFO_GSM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_CELL_INFO_GSM)) #define MM_CELL_INFO_GSM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_CELL_INFO_GSM, MMCellInfoGsmClass)) typedef struct _MMCellInfoGsm MMCellInfoGsm; typedef struct _MMCellInfoGsmClass MMCellInfoGsmClass; typedef struct _MMCellInfoGsmPrivate MMCellInfoGsmPrivate; /** * MMCellInfoGsm: * * The #MMCellInfoGsm structure contains private data and should only be * accessed using the provided API. */ struct _MMCellInfoGsm { /*< private >*/ MMCellInfo parent; MMCellInfoGsmPrivate *priv; }; struct _MMCellInfoGsmClass { /*< private >*/ MMCellInfoClass parent; }; GType mm_cell_info_gsm_get_type (void); G_DEFINE_AUTOPTR_CLEANUP_FUNC (MMCellInfoGsm, g_object_unref) const gchar *mm_cell_info_gsm_get_operator_id (MMCellInfoGsm *self); const gchar *mm_cell_info_gsm_get_lac (MMCellInfoGsm *self); const gchar *mm_cell_info_gsm_get_ci (MMCellInfoGsm *self); guint mm_cell_info_gsm_get_timing_advance (MMCellInfoGsm *self); guint mm_cell_info_gsm_get_arfcn (MMCellInfoGsm *self); const gchar *mm_cell_info_gsm_get_base_station_id (MMCellInfoGsm *self); guint mm_cell_info_gsm_get_rx_level (MMCellInfoGsm *self); /*****************************************************************************/ /* ModemManager/libmm-glib/mmcli specific methods */ #if defined (_LIBMM_INSIDE_MM) || \ defined (_LIBMM_INSIDE_MMCLI) || \ defined (LIBMM_GLIB_COMPILATION) void mm_cell_info_gsm_set_operator_id (MMCellInfoGsm *self, const gchar *operator_id); void mm_cell_info_gsm_set_lac (MMCellInfoGsm *self, const gchar *lac); void mm_cell_info_gsm_set_ci (MMCellInfoGsm *self, const gchar *ci); void mm_cell_info_gsm_set_timing_advance (MMCellInfoGsm *self, guint timing_advance); void mm_cell_info_gsm_set_arfcn (MMCellInfoGsm *self, guint arfcn); void mm_cell_info_gsm_set_base_station_id (MMCellInfoGsm *self, const gchar *base_station_id); void mm_cell_info_gsm_set_rx_level (MMCellInfoGsm *self, guint rx_level); MMCellInfo *mm_cell_info_gsm_new_from_dictionary (GVariantDict *dict); #endif G_END_DECLS #endif /* MM_CELL_INFO_GSM_H */ ModemManager-1.23.4-dev/libmm-glib/mm-cell-info-lte.c000066400000000000000000000326521456466623000221550ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * libmm-glib -- Access modem status & information from glib applications * * 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 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 Street, Fifth Floor, * Boston, MA 02110-1301 USA. * * Copyright (C) 2022 Aleksander Morgado */ #include "mm-helpers.h" #include "mm-cell-info-lte.h" /** * SECTION: mm-cell-info-lte * @title: MMCellInfoLte * @short_description: Helper object to report LTE cell info * * The #MMCellInfoLte is an object used to report LTE cell * information. * * The object inherits from the generic #MMCellInfo. */ G_DEFINE_TYPE (MMCellInfoLte, mm_cell_info_lte, MM_TYPE_CELL_INFO) #define PROPERTY_OPERATOR_ID "operator-id" #define PROPERTY_TAC "tac" #define PROPERTY_CI "ci" #define PROPERTY_PHYSICAL_CI "physical-ci" #define PROPERTY_EARFCN "earfcn" #define PROPERTY_RSRP "rsrp" #define PROPERTY_RSRQ "rsrq" #define PROPERTY_TIMING_ADVANCE "timing-advance" #define PROPERTY_SERVING_CELL_TYPE "serving-cell-type" #define PROPERTY_BANDWIDTH "bandwidth" struct _MMCellInfoLtePrivate { gchar *operator_id; gchar *tac; gchar *ci; gchar *physical_ci; guint earfcn; gdouble rsrp; gdouble rsrq; guint timing_advance; guint serving_cell_type; guint bandwidth; }; /*****************************************************************************/ /** * mm_cell_info_lte_get_operator_id: * @self: a #MMCellInfoLte. * * Get the PLMN MCC/MNC. * * Returns: (transfer none): the MCCMNC, or %NULL if not available. * * Since: 1.20 */ const gchar * mm_cell_info_lte_get_operator_id (MMCellInfoLte *self) { g_return_val_if_fail (MM_IS_CELL_INFO_LTE (self), NULL); RETURN_NON_EMPTY_CONSTANT_STRING (self->priv->operator_id); } /** * mm_cell_info_lte_set_operator_id: (skip) */ void mm_cell_info_lte_set_operator_id (MMCellInfoLte *self, const gchar *operator_id) { g_free (self->priv->operator_id); self->priv->operator_id = g_strdup (operator_id); } /** * mm_cell_info_lte_get_tac: * @self: a #MMCellInfoLte. * * Get the two- or three- byte Tracking Area Code of the base station. * * Encoded in upper-case hexadecimal format without leading zeros, * as specified in 3GPP TS 27.007. * * Returns: (transfer none): the MCCMNC, or %NULL if not available. * * Since: 1.20 */ const gchar * mm_cell_info_lte_get_tac (MMCellInfoLte *self) { g_return_val_if_fail (MM_IS_CELL_INFO_LTE (self), NULL); RETURN_NON_EMPTY_CONSTANT_STRING (self->priv->tac); } /** * mm_cell_info_lte_set_tac: (skip) */ void mm_cell_info_lte_set_tac (MMCellInfoLte *self, const gchar *tac) { g_free (self->priv->tac); self->priv->tac = g_strdup (tac); } /** * mm_cell_info_lte_get_ci: * @self: a #MMCellInfoLte. * * Get the two- or four-byte Cell Identifier. * * Encoded in upper-case hexadecimal format without leading zeros, * as specified in 3GPP TS 27.007. * * Returns: (transfer none): the MCCMNC, or %NULL if not available. * * Since: 1.20 */ const gchar * mm_cell_info_lte_get_ci (MMCellInfoLte *self) { g_return_val_if_fail (MM_IS_CELL_INFO_LTE (self), NULL); RETURN_NON_EMPTY_CONSTANT_STRING (self->priv->ci); } /** * mm_cell_info_lte_set_ci: (skip) */ void mm_cell_info_lte_set_ci (MMCellInfoLte *self, const gchar *ci) { g_free (self->priv->ci); self->priv->ci = g_strdup (ci); } /** * mm_cell_info_lte_get_physical_ci: * @self: a #MMCellInfoLte. * * Get the physical cell identifier. * * Encoded in upper-case hexadecimal format without leading zeros, * as specified in 3GPP TS 27.007. * * Returns: (transfer none): the MCCMNC, or %NULL if not available. * * Since: 1.20 */ const gchar * mm_cell_info_lte_get_physical_ci (MMCellInfoLte *self) { g_return_val_if_fail (MM_IS_CELL_INFO_LTE (self), NULL); RETURN_NON_EMPTY_CONSTANT_STRING (self->priv->physical_ci); } /** * mm_cell_info_lte_set_physical_ci: (skip) */ void mm_cell_info_lte_set_physical_ci (MMCellInfoLte *self, const gchar *physical_ci) { g_free (self->priv->physical_ci); self->priv->physical_ci = g_strdup (physical_ci); } /** * mm_cell_info_lte_get_earfcn: * @self: a #MMCellInfoLte. * * Get the E-UTRA absolute RF channel number. * * Returns: the EARFCN, or %G_MAXUINT if not available. * * Since: 1.20 */ guint mm_cell_info_lte_get_earfcn (MMCellInfoLte *self) { g_return_val_if_fail (MM_IS_CELL_INFO_LTE (self), G_MAXUINT); return self->priv->earfcn; } /** * mm_cell_info_lte_set_earfcn: (skip) */ void mm_cell_info_lte_set_earfcn (MMCellInfoLte *self, guint earfcn) { self->priv->earfcn = earfcn; } /** * mm_cell_info_lte_get_rsrp: * @self: a #MMCellInfoLte. * * Get the average reference signal received power in dBm. * * Returns: the RSRP, or -%G_MAXDOUBLE if not available. * * Since: 1.20 */ gdouble mm_cell_info_lte_get_rsrp (MMCellInfoLte *self) { g_return_val_if_fail (MM_IS_CELL_INFO_LTE (self), -G_MAXDOUBLE); return self->priv->rsrp; } /** * mm_cell_info_lte_set_rsrp: (skip) */ void mm_cell_info_lte_set_rsrp (MMCellInfoLte *self, gdouble rsrp) { self->priv->rsrp = rsrp; } /** * mm_cell_info_lte_get_rsrq: * @self: a #MMCellInfoLte. * * Get the average reference signal received quality in dB. * * Returns: the RSRQ, or -%G_MAXDOUBLE if not available. * * Since: 1.20 */ gdouble mm_cell_info_lte_get_rsrq (MMCellInfoLte *self) { g_return_val_if_fail (MM_IS_CELL_INFO_LTE (self), -G_MAXDOUBLE); return self->priv->rsrq; } /** * mm_cell_info_lte_set_rsrq: (skip) */ void mm_cell_info_lte_set_rsrq (MMCellInfoLte *self, gdouble rsrq) { self->priv->rsrq = rsrq; } /** * mm_cell_info_lte_get_timing_advance: * @self: a #MMCellInfoLte. * * Get the timing advance. * * Returns: the timing advance, or %G_MAXUINT if not available. * * Since: 1.20 */ guint mm_cell_info_lte_get_timing_advance (MMCellInfoLte *self) { g_return_val_if_fail (MM_IS_CELL_INFO_LTE (self), G_MAXUINT); return self->priv->timing_advance; } /** * mm_cell_info_lte_set_timing_advance: (skip) */ void mm_cell_info_lte_set_timing_advance (MMCellInfoLte *self, guint timing_advance) { self->priv->timing_advance = timing_advance; } /** * mm_cell_info_lte_get_serving_cell_type: * @self: a #MMCellInfoLte. * * Get the serving cell type. * * Returns: the serving cell type, or %MM_SERVING_CELL_TYPE_INVALID if not available. * * Since: 1.22 */ MMServingCellType mm_cell_info_lte_get_serving_cell_type (MMCellInfoLte *self) { g_return_val_if_fail (MM_IS_CELL_INFO_LTE (self), MM_SERVING_CELL_TYPE_INVALID); return self->priv->serving_cell_type; } /** * mm_cell_info_lte_set_serving_cell_type: (skip) */ void mm_cell_info_lte_set_serving_cell_type (MMCellInfoLte *self, MMServingCellType cell_type) { self->priv->serving_cell_type = cell_type; } /** * mm_cell_info_lte_get_bandwidth: * @self: a #MMCellInfoLte. * * Get the bandwidth of the particular carrier in downlink. * * Returns: the bandwidth, or %G_MAXUINT if not available. * * Since: 1.22 */ guint mm_cell_info_lte_get_bandwidth (MMCellInfoLte *self) { g_return_val_if_fail (MM_IS_CELL_INFO_LTE (self), G_MAXUINT); return self->priv->bandwidth; } /** * mm_cell_info_lte_set_bandwidth: (skip) */ void mm_cell_info_lte_set_bandwidth (MMCellInfoLte *self, guint bandwidth) { self->priv->bandwidth = bandwidth; } /*****************************************************************************/ static GString * build_string (MMCellInfo *_self) { MMCellInfoLte *self = MM_CELL_INFO_LTE (_self); GString *str; str = g_string_new (NULL); MM_CELL_INFO_BUILD_STRING_APPEND ("operator id", "%s", operator_id, NULL); MM_CELL_INFO_BUILD_STRING_APPEND ("tac", "%s", tac, NULL); MM_CELL_INFO_BUILD_STRING_APPEND ("ci", "%s", ci, NULL); MM_CELL_INFO_BUILD_STRING_APPEND ("physical ci", "%s", physical_ci, NULL); MM_CELL_INFO_BUILD_STRING_APPEND ("earfcn", "%u", earfcn, G_MAXUINT); MM_CELL_INFO_BUILD_STRING_APPEND ("rsrp", "%lf", rsrp, -G_MAXDOUBLE); MM_CELL_INFO_BUILD_STRING_APPEND ("rsrq", "%lf", rsrq, -G_MAXDOUBLE); MM_CELL_INFO_BUILD_STRING_APPEND ("timing advance", "%u", timing_advance, G_MAXUINT); MM_CELL_INFO_BUILD_STRING_APPEND ("serving cell type", "%u", serving_cell_type, MM_SERVING_CELL_TYPE_INVALID); MM_CELL_INFO_BUILD_STRING_APPEND ("bandwidth", "%u", bandwidth, G_MAXUINT); return str; } /*****************************************************************************/ /** * mm_cell_info_lte_get_dictionary: (skip) */ static GVariantDict * get_dictionary (MMCellInfo *_self) { MMCellInfoLte *self = MM_CELL_INFO_LTE (_self); GVariantDict *dict; dict = g_variant_dict_new (NULL); MM_CELL_INFO_GET_DICTIONARY_INSERT (OPERATOR_ID, operator_id, string, NULL); MM_CELL_INFO_GET_DICTIONARY_INSERT (TAC, tac, string, NULL); MM_CELL_INFO_GET_DICTIONARY_INSERT (CI, ci, string, NULL); MM_CELL_INFO_GET_DICTIONARY_INSERT (PHYSICAL_CI, physical_ci, string, NULL); MM_CELL_INFO_GET_DICTIONARY_INSERT (EARFCN, earfcn, uint32, G_MAXUINT); MM_CELL_INFO_GET_DICTIONARY_INSERT (RSRP, rsrp, double, -G_MAXDOUBLE); MM_CELL_INFO_GET_DICTIONARY_INSERT (RSRQ, rsrq, double, -G_MAXDOUBLE); MM_CELL_INFO_GET_DICTIONARY_INSERT (TIMING_ADVANCE, timing_advance, uint32, G_MAXUINT); MM_CELL_INFO_GET_DICTIONARY_INSERT (SERVING_CELL_TYPE, serving_cell_type, uint32, MM_SERVING_CELL_TYPE_INVALID); MM_CELL_INFO_GET_DICTIONARY_INSERT (BANDWIDTH, bandwidth, uint32, G_MAXUINT); return dict; } /*****************************************************************************/ /** * mm_cell_info_lte_new_from_dictionary: (skip) */ MMCellInfo * mm_cell_info_lte_new_from_dictionary (GVariantDict *dict) { MMCellInfoLte *self; self = MM_CELL_INFO_LTE (g_object_new (MM_TYPE_CELL_INFO_LTE, NULL)); if (dict) { MM_CELL_INFO_NEW_FROM_DICTIONARY_STRING_SET (lte, OPERATOR_ID, operator_id); MM_CELL_INFO_NEW_FROM_DICTIONARY_STRING_SET (lte, TAC, tac); MM_CELL_INFO_NEW_FROM_DICTIONARY_STRING_SET (lte, CI, ci); MM_CELL_INFO_NEW_FROM_DICTIONARY_STRING_SET (lte, PHYSICAL_CI, physical_ci); MM_CELL_INFO_NEW_FROM_DICTIONARY_NUM_SET (lte, EARFCN, earfcn, UINT32, uint32); MM_CELL_INFO_NEW_FROM_DICTIONARY_NUM_SET (lte, RSRP, rsrp, DOUBLE, double); MM_CELL_INFO_NEW_FROM_DICTIONARY_NUM_SET (lte, RSRQ, rsrq, DOUBLE, double); MM_CELL_INFO_NEW_FROM_DICTIONARY_NUM_SET (lte, TIMING_ADVANCE, timing_advance, UINT32, uint32); MM_CELL_INFO_NEW_FROM_DICTIONARY_NUM_SET (lte, SERVING_CELL_TYPE, serving_cell_type, UINT32, uint32); MM_CELL_INFO_NEW_FROM_DICTIONARY_NUM_SET (lte, BANDWIDTH, bandwidth, UINT32, uint32); } return MM_CELL_INFO (self); } /*****************************************************************************/ static void mm_cell_info_lte_init (MMCellInfoLte *self) { self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, MM_TYPE_CELL_INFO_LTE, MMCellInfoLtePrivate); self->priv->earfcn = G_MAXUINT; self->priv->rsrp = -G_MAXDOUBLE; self->priv->rsrq = -G_MAXDOUBLE; self->priv->timing_advance = G_MAXUINT; self->priv->serving_cell_type = MM_SERVING_CELL_TYPE_INVALID; self->priv->bandwidth = G_MAXUINT; } static void finalize (GObject *object) { MMCellInfoLte *self = MM_CELL_INFO_LTE (object); g_free (self->priv->operator_id); g_free (self->priv->tac); g_free (self->priv->ci); g_free (self->priv->physical_ci); G_OBJECT_CLASS (mm_cell_info_lte_parent_class)->finalize (object); } static void mm_cell_info_lte_class_init (MMCellInfoLteClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); MMCellInfoClass *cell_info_class = MM_CELL_INFO_CLASS (klass); g_type_class_add_private (object_class, sizeof (MMCellInfoLtePrivate)); object_class->finalize = finalize; cell_info_class->get_dictionary = get_dictionary; cell_info_class->build_string = build_string; } ModemManager-1.23.4-dev/libmm-glib/mm-cell-info-lte.h000066400000000000000000000117501456466623000221560ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * libmm-glib -- Access modem status & information from glib applications * * 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 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 Street, Fifth Floor, * Boston, MA 02110-1301 USA. * * Copyright (C) 2022 Aleksander Morgado */ #ifndef MM_CELL_INFO_LTE_H #define MM_CELL_INFO_LTE_H #if !defined (__LIBMM_GLIB_H_INSIDE__) && !defined (LIBMM_GLIB_COMPILATION) #error "Only can be included directly." #endif #include #include #include "mm-cell-info.h" G_BEGIN_DECLS #define MM_TYPE_CELL_INFO_LTE (mm_cell_info_lte_get_type ()) #define MM_CELL_INFO_LTE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_CELL_INFO_LTE, MMCellInfoLte)) #define MM_CELL_INFO_LTE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_CELL_INFO_LTE, MMCellInfoLteClass)) #define MM_IS_CELL_INFO_LTE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_CELL_INFO_LTE)) #define MM_IS_CELL_INFO_LTE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_CELL_INFO_LTE)) #define MM_CELL_INFO_LTE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_CELL_INFO_LTE, MMCellInfoLteClass)) typedef struct _MMCellInfoLte MMCellInfoLte; typedef struct _MMCellInfoLteClass MMCellInfoLteClass; typedef struct _MMCellInfoLtePrivate MMCellInfoLtePrivate; /** * MMCellInfoLte: * * The #MMCellInfoLte structure contains private data and should only be * accessed using the provided API. */ struct _MMCellInfoLte { /*< private >*/ MMCellInfo parent; MMCellInfoLtePrivate *priv; }; struct _MMCellInfoLteClass { /*< private >*/ MMCellInfoClass parent; }; GType mm_cell_info_lte_get_type (void); G_DEFINE_AUTOPTR_CLEANUP_FUNC (MMCellInfoLte, g_object_unref) const gchar *mm_cell_info_lte_get_operator_id (MMCellInfoLte *self); const gchar *mm_cell_info_lte_get_tac (MMCellInfoLte *self); const gchar *mm_cell_info_lte_get_ci (MMCellInfoLte *self); const gchar *mm_cell_info_lte_get_physical_ci (MMCellInfoLte *self); guint mm_cell_info_lte_get_earfcn (MMCellInfoLte *self); gdouble mm_cell_info_lte_get_rsrp (MMCellInfoLte *self); gdouble mm_cell_info_lte_get_rsrq (MMCellInfoLte *self); guint mm_cell_info_lte_get_timing_advance (MMCellInfoLte *self); MMServingCellType mm_cell_info_lte_get_serving_cell_type (MMCellInfoLte *self); guint mm_cell_info_lte_get_bandwidth (MMCellInfoLte *self); /*****************************************************************************/ /* ModemManager/libmm-glib/mmcli specific methods */ #if defined (_LIBMM_INSIDE_MM) || \ defined (_LIBMM_INSIDE_MMCLI) || \ defined (LIBMM_GLIB_COMPILATION) void mm_cell_info_lte_set_operator_id (MMCellInfoLte *self, const gchar *operator_id); void mm_cell_info_lte_set_tac (MMCellInfoLte *self, const gchar *tac); void mm_cell_info_lte_set_ci (MMCellInfoLte *self, const gchar *ci); void mm_cell_info_lte_set_physical_ci (MMCellInfoLte *self, const gchar *ci); void mm_cell_info_lte_set_earfcn (MMCellInfoLte *self, guint earfcn); void mm_cell_info_lte_set_rsrp (MMCellInfoLte *self, gdouble rsrp); void mm_cell_info_lte_set_rsrq (MMCellInfoLte *self, gdouble rsrq); void mm_cell_info_lte_set_timing_advance (MMCellInfoLte *self, guint earfcn); void mm_cell_info_lte_set_serving_cell_type (MMCellInfoLte *self, MMServingCellType cell_type); void mm_cell_info_lte_set_bandwidth (MMCellInfoLte *self, guint bandwidth); MMCellInfo *mm_cell_info_lte_new_from_dictionary (GVariantDict *dict); #endif G_END_DECLS #endif /* MM_CELL_INFO_LTE_H */ ModemManager-1.23.4-dev/libmm-glib/mm-cell-info-nr5g.c000066400000000000000000000350221456466623000222360ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * libmm-glib -- Access modem status & information from glib applications * * 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 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 Street, Fifth Floor, * Boston, MA 02110-1301 USA. * * Copyright (C) 2022 Aleksander Morgado */ #include "mm-helpers.h" #include "mm-cell-info-nr5g.h" /** * SECTION: mm-cell-info-nr5g * @title: MMCellInfoNr5g * @short_description: Helper object to report 5GNR cell info * * The #MMCellInfoNr5g is an object used to report 5GNR cell * information. * * The object inherits from the generic #MMCellInfo. */ G_DEFINE_TYPE (MMCellInfoNr5g, mm_cell_info_nr5g, MM_TYPE_CELL_INFO) #define PROPERTY_OPERATOR_ID "operator-id" #define PROPERTY_TAC "tac" #define PROPERTY_CI "ci" #define PROPERTY_PHYSICAL_CI "physical-ci" #define PROPERTY_NRARFCN "nrarfcn" #define PROPERTY_RSRP "rsrp" #define PROPERTY_RSRQ "rsrq" #define PROPERTY_SINR "sinr" #define PROPERTY_TIMING_ADVANCE "timing-advance" #define PROPERTY_SERVING_CELL_TYPE "serving-cell-type" #define PROPERTY_BANDWIDTH "bandwidth" struct _MMCellInfoNr5gPrivate { gchar *operator_id; gchar *tac; gchar *ci; gchar *physical_ci; guint nrarfcn; gdouble rsrp; gdouble rsrq; gdouble sinr; guint timing_advance; guint serving_cell_type; guint bandwidth; }; /*****************************************************************************/ /** * mm_cell_info_nr5g_get_operator_id: * @self: a #MMCellInfoNr5g. * * Get the PLMN MCC/MNC. * * Returns: (transfer none): the MCCMNC, or %NULL if not available. * * Since: 1.20 */ const gchar * mm_cell_info_nr5g_get_operator_id (MMCellInfoNr5g *self) { g_return_val_if_fail (MM_IS_CELL_INFO_NR5G (self), NULL); RETURN_NON_EMPTY_CONSTANT_STRING (self->priv->operator_id); } /** * mm_cell_info_nr5g_set_operator_id: (skip) */ void mm_cell_info_nr5g_set_operator_id (MMCellInfoNr5g *self, const gchar *operator_id) { g_free (self->priv->operator_id); self->priv->operator_id = g_strdup (operator_id); } /** * mm_cell_info_nr5g_get_tac: * @self: a #MMCellInfoNr5g. * * Get the two- or three- byte Tracking Area Code of the base station. * * Encoded in upper-case hexadecimal format without leading zeros, * as specified in 3GPP TS 27.007. * * Returns: (transfer none): the MCCMNC, or %NULL if not available. * * Since: 1.20 */ const gchar * mm_cell_info_nr5g_get_tac (MMCellInfoNr5g *self) { g_return_val_if_fail (MM_IS_CELL_INFO_NR5G (self), NULL); RETURN_NON_EMPTY_CONSTANT_STRING (self->priv->tac); } /** * mm_cell_info_nr5g_set_tac: (skip) */ void mm_cell_info_nr5g_set_tac (MMCellInfoNr5g *self, const gchar *tac) { g_free (self->priv->tac); self->priv->tac = g_strdup (tac); } /** * mm_cell_info_nr5g_get_ci: * @self: a #MMCellInfoNr5g. * * Get the two- or four-byte Cell Identifier. * * Encoded in upper-case hexadecimal format without leading zeros, * as specified in 3GPP TS 27.007. * * Returns: (transfer none): the MCCMNC, or %NULL if not available. * * Since: 1.20 */ const gchar * mm_cell_info_nr5g_get_ci (MMCellInfoNr5g *self) { g_return_val_if_fail (MM_IS_CELL_INFO_NR5G (self), NULL); RETURN_NON_EMPTY_CONSTANT_STRING (self->priv->ci); } /** * mm_cell_info_nr5g_set_ci: (skip) */ void mm_cell_info_nr5g_set_ci (MMCellInfoNr5g *self, const gchar *ci) { g_free (self->priv->ci); self->priv->ci = g_strdup (ci); } /** * mm_cell_info_nr5g_get_physical_ci: * @self: a #MMCellInfoNr5g. * * Get the physical cell identifier. * * Encoded in upper-case hexadecimal format without leading zeros, * as specified in 3GPP TS 27.007. * * Returns: (transfer none): the MCCMNC, or %NULL if not available. * * Since: 1.20 */ const gchar * mm_cell_info_nr5g_get_physical_ci (MMCellInfoNr5g *self) { g_return_val_if_fail (MM_IS_CELL_INFO_NR5G (self), NULL); RETURN_NON_EMPTY_CONSTANT_STRING (self->priv->physical_ci); } /** * mm_cell_info_nr5g_set_physical_ci: (skip) */ void mm_cell_info_nr5g_set_physical_ci (MMCellInfoNr5g *self, const gchar *physical_ci) { g_free (self->priv->physical_ci); self->priv->physical_ci = g_strdup (physical_ci); } /** * mm_cell_info_nr5g_get_nrarfcn: * @self: a #MMCellInfoNr5g. * * Get the NR absolute RF channel number. * * Returns: the NRARFCN, or %G_MAXUINT if not available. * * Since: 1.20 */ guint mm_cell_info_nr5g_get_nrarfcn (MMCellInfoNr5g *self) { g_return_val_if_fail (MM_IS_CELL_INFO_NR5G (self), G_MAXUINT); return self->priv->nrarfcn; } /** * mm_cell_info_nr5g_set_nrarfcn: (skip) */ void mm_cell_info_nr5g_set_nrarfcn (MMCellInfoNr5g *self, guint nrarfcn) { self->priv->nrarfcn = nrarfcn; } /** * mm_cell_info_nr5g_get_rsrp: * @self: a #MMCellInfoNr5g. * * Get the average reference signal received power in dBm. * * Returns: the RSRP, or -%G_MAXDOUBLE if not available. * * Since: 1.20 */ gdouble mm_cell_info_nr5g_get_rsrp (MMCellInfoNr5g *self) { g_return_val_if_fail (MM_IS_CELL_INFO_NR5G (self), -G_MAXDOUBLE); return self->priv->rsrp; } /** * mm_cell_info_nr5g_set_rsrp: (skip) */ void mm_cell_info_nr5g_set_rsrp (MMCellInfoNr5g *self, gdouble rsrp) { self->priv->rsrp = rsrp; } /** * mm_cell_info_nr5g_get_rsrq: * @self: a #MMCellInfoNr5g. * * Get the average reference signal received quality in dB. * * Returns: the RSRQ, or -%G_MAXDOUBLE if not available. * * Since: 1.20 */ gdouble mm_cell_info_nr5g_get_rsrq (MMCellInfoNr5g *self) { g_return_val_if_fail (MM_IS_CELL_INFO_NR5G (self), -G_MAXDOUBLE); return self->priv->rsrq; } /** * mm_cell_info_nr5g_set_rsrq: (skip) */ void mm_cell_info_nr5g_set_rsrq (MMCellInfoNr5g *self, gdouble rsrq) { self->priv->rsrq = rsrq; } /** * mm_cell_info_nr5g_get_sinr: * @self: a #MMCellInfoNr5g. * * Get the signal to interference and noise ratio. * * Returns: the SINR, or -%G_MAXDOUBLE if not available. * * Since: 1.20 */ gdouble mm_cell_info_nr5g_get_sinr (MMCellInfoNr5g *self) { g_return_val_if_fail (MM_IS_CELL_INFO_NR5G (self), -G_MAXDOUBLE); return self->priv->sinr; } /** * mm_cell_info_nr5g_set_sinr: (skip) */ void mm_cell_info_nr5g_set_sinr (MMCellInfoNr5g *self, gdouble sinr) { self->priv->sinr = sinr; } /** * mm_cell_info_nr5g_get_timing_advance: * @self: a #MMCellInfoNr5g. * * Get the timing advance. * * Returns: the timing advance, or %G_MAXUINT if not available. * * Since: 1.20 */ guint mm_cell_info_nr5g_get_timing_advance (MMCellInfoNr5g *self) { g_return_val_if_fail (MM_IS_CELL_INFO_NR5G (self), G_MAXUINT); return self->priv->timing_advance; } /** * mm_cell_info_nr5g_set_timing_advance: (skip) */ void mm_cell_info_nr5g_set_timing_advance (MMCellInfoNr5g *self, guint timing_advance) { self->priv->timing_advance = timing_advance; } /** * mm_cell_info_nr5g_get_serving_cell_type: * @self: a #MMCellInfoNr5g. * * Get the serving cell type. * * Returns: the serving cell type, or %MM_SERVING_CELL_TYPE_INVALID if not available. * * Since: 1.22 */ MMServingCellType mm_cell_info_nr5g_get_serving_cell_type (MMCellInfoNr5g *self) { g_return_val_if_fail (MM_IS_CELL_INFO_NR5G (self), MM_SERVING_CELL_TYPE_INVALID); return self->priv->serving_cell_type; } /** * mm_cell_info_nr5g_set_serving_cell_type: (skip) */ void mm_cell_info_nr5g_set_serving_cell_type (MMCellInfoNr5g *self, MMServingCellType cell_type) { self->priv->serving_cell_type = cell_type; } /** * mm_cell_info_nr5g_get_bandwidth: * @self: a #MMCellInfoNr5g. * * Get the bandwidth of the particular carrier in downlink. * * Returns: the bandwidth, or %G_MAXUINT if not available. * * Since: 1.22 */ guint mm_cell_info_nr5g_get_bandwidth (MMCellInfoNr5g *self) { g_return_val_if_fail (MM_IS_CELL_INFO_NR5G (self), G_MAXUINT); return self->priv->bandwidth; } /** * mm_cell_info_nr5g_set_bandwidth: (skip) */ void mm_cell_info_nr5g_set_bandwidth (MMCellInfoNr5g *self, guint bandwidth) { self->priv->bandwidth = bandwidth; } /*****************************************************************************/ static GString * build_string (MMCellInfo *_self) { MMCellInfoNr5g *self = MM_CELL_INFO_NR5G (_self); GString *str; str = g_string_new (NULL); MM_CELL_INFO_BUILD_STRING_APPEND ("operator id", "%s", operator_id, NULL); MM_CELL_INFO_BUILD_STRING_APPEND ("tac", "%s", tac, NULL); MM_CELL_INFO_BUILD_STRING_APPEND ("ci", "%s", ci, NULL); MM_CELL_INFO_BUILD_STRING_APPEND ("physical ci", "%s", physical_ci, NULL); MM_CELL_INFO_BUILD_STRING_APPEND ("nrarfcn", "%u", nrarfcn, G_MAXUINT); MM_CELL_INFO_BUILD_STRING_APPEND ("rsrp", "%lf", rsrp, -G_MAXDOUBLE); MM_CELL_INFO_BUILD_STRING_APPEND ("rsrq", "%lf", rsrq, -G_MAXDOUBLE); MM_CELL_INFO_BUILD_STRING_APPEND ("sinr", "%lf", sinr, -G_MAXDOUBLE); MM_CELL_INFO_BUILD_STRING_APPEND ("timing advance", "%u", timing_advance, G_MAXUINT); MM_CELL_INFO_BUILD_STRING_APPEND ("serving cell type", "%u", serving_cell_type, MM_SERVING_CELL_TYPE_INVALID); MM_CELL_INFO_BUILD_STRING_APPEND ("bandwidth", "%u", bandwidth, G_MAXUINT); return str; } /*****************************************************************************/ /** * mm_cell_info_nr5g_get_dictionary: (skip) */ static GVariantDict * get_dictionary (MMCellInfo *_self) { MMCellInfoNr5g *self = MM_CELL_INFO_NR5G (_self); GVariantDict *dict; dict = g_variant_dict_new (NULL); MM_CELL_INFO_GET_DICTIONARY_INSERT (OPERATOR_ID, operator_id, string, NULL); MM_CELL_INFO_GET_DICTIONARY_INSERT (TAC, tac, string, NULL); MM_CELL_INFO_GET_DICTIONARY_INSERT (CI, ci, string, NULL); MM_CELL_INFO_GET_DICTIONARY_INSERT (PHYSICAL_CI, physical_ci, string, NULL); MM_CELL_INFO_GET_DICTIONARY_INSERT (NRARFCN, nrarfcn, uint32, G_MAXUINT); MM_CELL_INFO_GET_DICTIONARY_INSERT (RSRP, rsrp, double, -G_MAXDOUBLE); MM_CELL_INFO_GET_DICTIONARY_INSERT (RSRQ, rsrq, double, -G_MAXDOUBLE); MM_CELL_INFO_GET_DICTIONARY_INSERT (SINR, sinr, double, -G_MAXDOUBLE); MM_CELL_INFO_GET_DICTIONARY_INSERT (TIMING_ADVANCE, timing_advance, uint32, G_MAXUINT); MM_CELL_INFO_GET_DICTIONARY_INSERT (SERVING_CELL_TYPE, serving_cell_type, uint32, MM_SERVING_CELL_TYPE_INVALID); MM_CELL_INFO_GET_DICTIONARY_INSERT (BANDWIDTH, bandwidth, uint32, G_MAXUINT); return dict; } /*****************************************************************************/ /** * mm_cell_info_nr5g_new_from_dictionary: (skip) */ MMCellInfo * mm_cell_info_nr5g_new_from_dictionary (GVariantDict *dict) { MMCellInfoNr5g *self; self = MM_CELL_INFO_NR5G (g_object_new (MM_TYPE_CELL_INFO_NR5G, NULL)); if (dict) { MM_CELL_INFO_NEW_FROM_DICTIONARY_STRING_SET (nr5g, OPERATOR_ID, operator_id); MM_CELL_INFO_NEW_FROM_DICTIONARY_STRING_SET (nr5g, TAC, tac); MM_CELL_INFO_NEW_FROM_DICTIONARY_STRING_SET (nr5g, CI, ci); MM_CELL_INFO_NEW_FROM_DICTIONARY_STRING_SET (nr5g, PHYSICAL_CI, physical_ci); MM_CELL_INFO_NEW_FROM_DICTIONARY_NUM_SET (nr5g, NRARFCN, nrarfcn, UINT32, uint32); MM_CELL_INFO_NEW_FROM_DICTIONARY_NUM_SET (nr5g, RSRP, rsrp, DOUBLE, double); MM_CELL_INFO_NEW_FROM_DICTIONARY_NUM_SET (nr5g, RSRQ, rsrq, DOUBLE, double); MM_CELL_INFO_NEW_FROM_DICTIONARY_NUM_SET (nr5g, SINR, sinr, DOUBLE, double); MM_CELL_INFO_NEW_FROM_DICTIONARY_NUM_SET (nr5g, TIMING_ADVANCE, timing_advance, UINT32, uint32); MM_CELL_INFO_NEW_FROM_DICTIONARY_NUM_SET (nr5g, SERVING_CELL_TYPE, serving_cell_type, UINT32, uint32); MM_CELL_INFO_NEW_FROM_DICTIONARY_NUM_SET (nr5g, BANDWIDTH, bandwidth, UINT32, uint32); } return MM_CELL_INFO (self); } /*****************************************************************************/ static void mm_cell_info_nr5g_init (MMCellInfoNr5g *self) { self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, MM_TYPE_CELL_INFO_NR5G, MMCellInfoNr5gPrivate); self->priv->nrarfcn = G_MAXUINT; self->priv->rsrp = -G_MAXDOUBLE; self->priv->rsrq = -G_MAXDOUBLE; self->priv->sinr = -G_MAXDOUBLE; self->priv->timing_advance = G_MAXUINT; self->priv->serving_cell_type = MM_SERVING_CELL_TYPE_INVALID; self->priv->bandwidth = G_MAXUINT; } static void finalize (GObject *object) { MMCellInfoNr5g *self = MM_CELL_INFO_NR5G (object); g_free (self->priv->operator_id); g_free (self->priv->tac); g_free (self->priv->ci); g_free (self->priv->physical_ci); G_OBJECT_CLASS (mm_cell_info_nr5g_parent_class)->finalize (object); } static void mm_cell_info_nr5g_class_init (MMCellInfoNr5gClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); MMCellInfoClass *cell_info_class = MM_CELL_INFO_CLASS (klass); g_type_class_add_private (object_class, sizeof (MMCellInfoNr5gPrivate)); object_class->finalize = finalize; cell_info_class->get_dictionary = get_dictionary; cell_info_class->build_string = build_string; } ModemManager-1.23.4-dev/libmm-glib/mm-cell-info-nr5g.h000066400000000000000000000124211456466623000222410ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * libmm-glib -- Access modem status & information from glib applications * * 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 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 Street, Fifth Floor, * Boston, MA 02110-1301 USA. * * Copyright (C) 2022 Aleksander Morgado */ #ifndef MM_CELL_INFO_NR5G_H #define MM_CELL_INFO_NR5G_H #if !defined (__LIBMM_GLIB_H_INSIDE__) && !defined (LIBMM_GLIB_COMPILATION) #error "Only can be included directly." #endif #include #include #include "mm-cell-info.h" G_BEGIN_DECLS #define MM_TYPE_CELL_INFO_NR5G (mm_cell_info_nr5g_get_type ()) #define MM_CELL_INFO_NR5G(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_CELL_INFO_NR5G, MMCellInfoNr5g)) #define MM_CELL_INFO_NR5G_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_CELL_INFO_NR5G, MMCellInfoNr5gClass)) #define MM_IS_CELL_INFO_NR5G(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_CELL_INFO_NR5G)) #define MM_IS_CELL_INFO_NR5G_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_CELL_INFO_NR5G)) #define MM_CELL_INFO_NR5G_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_CELL_INFO_NR5G, MMCellInfoNr5gClass)) typedef struct _MMCellInfoNr5g MMCellInfoNr5g; typedef struct _MMCellInfoNr5gClass MMCellInfoNr5gClass; typedef struct _MMCellInfoNr5gPrivate MMCellInfoNr5gPrivate; /** * MMCellInfoNr5g: * * The #MMCellInfoNr5g structure contains private data and should only be * accessed using the provided API. */ struct _MMCellInfoNr5g { /*< private >*/ MMCellInfo parent; MMCellInfoNr5gPrivate *priv; }; struct _MMCellInfoNr5gClass { /*< private >*/ MMCellInfoClass parent; }; GType mm_cell_info_nr5g_get_type (void); G_DEFINE_AUTOPTR_CLEANUP_FUNC (MMCellInfoNr5g, g_object_unref) const gchar *mm_cell_info_nr5g_get_operator_id (MMCellInfoNr5g *self); const gchar *mm_cell_info_nr5g_get_tac (MMCellInfoNr5g *self); const gchar *mm_cell_info_nr5g_get_ci (MMCellInfoNr5g *self); const gchar *mm_cell_info_nr5g_get_physical_ci (MMCellInfoNr5g *self); guint mm_cell_info_nr5g_get_nrarfcn (MMCellInfoNr5g *self); gdouble mm_cell_info_nr5g_get_rsrp (MMCellInfoNr5g *self); gdouble mm_cell_info_nr5g_get_rsrq (MMCellInfoNr5g *self); gdouble mm_cell_info_nr5g_get_sinr (MMCellInfoNr5g *self); guint mm_cell_info_nr5g_get_timing_advance (MMCellInfoNr5g *self); MMServingCellType mm_cell_info_nr5g_get_serving_cell_type (MMCellInfoNr5g *self); guint mm_cell_info_nr5g_get_bandwidth (MMCellInfoNr5g *self); /*****************************************************************************/ /* ModemManager/libmm-glib/mmcli specific methods */ #if defined (_LIBMM_INSIDE_MM) || \ defined (_LIBMM_INSIDE_MMCLI) || \ defined (LIBMM_GLIB_COMPILATION) void mm_cell_info_nr5g_set_operator_id (MMCellInfoNr5g *self, const gchar *operator_id); void mm_cell_info_nr5g_set_tac (MMCellInfoNr5g *self, const gchar *tac); void mm_cell_info_nr5g_set_ci (MMCellInfoNr5g *self, const gchar *ci); void mm_cell_info_nr5g_set_physical_ci (MMCellInfoNr5g *self, const gchar *ci); void mm_cell_info_nr5g_set_nrarfcn (MMCellInfoNr5g *self, guint earfcn); void mm_cell_info_nr5g_set_rsrp (MMCellInfoNr5g *self, gdouble rsrp); void mm_cell_info_nr5g_set_rsrq (MMCellInfoNr5g *self, gdouble rsrq); void mm_cell_info_nr5g_set_sinr (MMCellInfoNr5g *self, gdouble sinr); void mm_cell_info_nr5g_set_timing_advance (MMCellInfoNr5g *self, guint earfcn); void mm_cell_info_nr5g_set_serving_cell_type (MMCellInfoNr5g *self, MMServingCellType cell_type); void mm_cell_info_nr5g_set_bandwidth (MMCellInfoNr5g *self, guint bandwidth); MMCellInfo *mm_cell_info_nr5g_new_from_dictionary (GVariantDict *dict); #endif G_END_DECLS #endif /* MM_CELL_INFO_NR5G_H */ ModemManager-1.23.4-dev/libmm-glib/mm-cell-info-tdscdma.c000066400000000000000000000274171456466623000230130ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * libmm-glib -- Access modem status & information from glib applications * * 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 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 Street, Fifth Floor, * Boston, MA 02110-1301 USA. * * Copyright (C) 2021 Aleksander Morgado */ #include "mm-helpers.h" #include "mm-cell-info-tdscdma.h" /** * SECTION: mm-cell-info-tdscdma * @title: MMCellInfoTdscdma * @short_description: Helper object to report TDSCDMA cell info * * The #MMCellInfoTdscdma is an object used to report TDSCDMA cell * information. * * The object inherits from the generic #MMCellInfo. */ G_DEFINE_TYPE (MMCellInfoTdscdma, mm_cell_info_tdscdma, MM_TYPE_CELL_INFO) #define PROPERTY_OPERATOR_ID "operator-id" #define PROPERTY_LAC "lac" #define PROPERTY_CI "ci" #define PROPERTY_UARFCN "uarfcn" #define PROPERTY_CELL_PARAMETER_ID "cell-parameter-id" #define PROPERTY_TIMING_ADVANCE "timing-advance" #define PROPERTY_RSCP "rscp" #define PROPERTY_PATH_LOSS "path-loss" struct _MMCellInfoTdscdmaPrivate { gchar *operator_id; gchar *lac; gchar *ci; guint uarfcn; guint cell_parameter_id; guint timing_advance; gdouble rscp; guint path_loss; }; /*****************************************************************************/ /** * mm_cell_info_tdscdma_get_operator_id: * @self: a #MMCellInfoTdscdma. * * Get the PLMN MCC/MNC. * * Returns: (transfer none): the MCCMNC, or %NULL if not available. * * Since: 1.20 */ const gchar * mm_cell_info_tdscdma_get_operator_id (MMCellInfoTdscdma *self) { g_return_val_if_fail (MM_IS_CELL_INFO_TDSCDMA (self), NULL); RETURN_NON_EMPTY_CONSTANT_STRING (self->priv->operator_id); } /** * mm_cell_info_tdscdma_set_operator_id: (skip) */ void mm_cell_info_tdscdma_set_operator_id (MMCellInfoTdscdma *self, const gchar *operator_id) { g_free (self->priv->operator_id); self->priv->operator_id = g_strdup (operator_id); } /** * mm_cell_info_tdscdma_get_lac: * @self: a #MMCellInfoTdscdma. * * Get the two-byte Location Area Code of the base station. * * Encoded in upper-case hexadecimal format without leading zeros, * as specified in 3GPP TS 27.007. * * Returns: (transfer none): the MCCMNC, or %NULL if not available. * * Since: 1.20 */ const gchar * mm_cell_info_tdscdma_get_lac (MMCellInfoTdscdma *self) { g_return_val_if_fail (MM_IS_CELL_INFO_TDSCDMA (self), NULL); RETURN_NON_EMPTY_CONSTANT_STRING (self->priv->lac); } /** * mm_cell_info_tdscdma_set_lac: (skip) */ void mm_cell_info_tdscdma_set_lac (MMCellInfoTdscdma *self, const gchar *lac) { g_free (self->priv->lac); self->priv->lac = g_strdup (lac); } /** * mm_cell_info_tdscdma_get_ci: * @self: a #MMCellInfoTdscdma. * * Get the two- or four-byte Cell Identifier. * * Encoded in upper-case hexadecimal format without leading zeros, * as specified in 3GPP TS 27.007. * * Returns: (transfer none): the MCCMNC, or %NULL if not available. * * Since: 1.20 */ const gchar * mm_cell_info_tdscdma_get_ci (MMCellInfoTdscdma *self) { g_return_val_if_fail (MM_IS_CELL_INFO_TDSCDMA (self), NULL); RETURN_NON_EMPTY_CONSTANT_STRING (self->priv->ci); } /** * mm_cell_info_tdscdma_set_ci: (skip) */ void mm_cell_info_tdscdma_set_ci (MMCellInfoTdscdma *self, const gchar *ci) { g_free (self->priv->ci); self->priv->ci = g_strdup (ci); } /** * mm_cell_info_tdscdma_get_uarfcn: * @self: a #MMCellInfoTdscdma. * * Get the UTRA absolute RF channel number. * * Returns: the UARFCN, or %G_MAXUINT if not available. * * Since: 1.20 */ guint mm_cell_info_tdscdma_get_uarfcn (MMCellInfoTdscdma *self) { g_return_val_if_fail (MM_IS_CELL_INFO_TDSCDMA (self), G_MAXUINT); return self->priv->uarfcn; } /** * mm_cell_info_tdscdma_set_uarfcn: (skip) */ void mm_cell_info_tdscdma_set_uarfcn (MMCellInfoTdscdma *self, guint uarfcn) { self->priv->uarfcn = uarfcn; } /** * mm_cell_info_tdscdma_get_cell_parameter_id: * @self: a #MMCellInfoTdscdma. * * Get the cell parameter id. * * Returns: the cell parameter id, or %G_MAXUINT if not available. * * Since: 1.20 */ guint mm_cell_info_tdscdma_get_cell_parameter_id (MMCellInfoTdscdma *self) { g_return_val_if_fail (MM_IS_CELL_INFO_TDSCDMA (self), G_MAXUINT); return self->priv->cell_parameter_id; } /** * mm_cell_info_tdscdma_set_cell_parameter_id: (skip) */ void mm_cell_info_tdscdma_set_cell_parameter_id (MMCellInfoTdscdma *self, guint cell_parameter_id) { self->priv->cell_parameter_id = cell_parameter_id; } /** * mm_cell_info_tdscdma_get_timing_advance: * @self: a #MMCellInfoTdscdma. * * Get the measured delay (in bit periods) of an access burst transmission * on the RACH or PRACH to the expected signal from a mobile station at zero * distance under static channel conditions. * * Returns: the timing advance, or %G_MAXUINT if not available. * * Since: 1.20 */ guint mm_cell_info_tdscdma_get_timing_advance (MMCellInfoTdscdma *self) { g_return_val_if_fail (MM_IS_CELL_INFO_TDSCDMA (self), G_MAXUINT); return self->priv->timing_advance; } /** * mm_cell_info_tdscdma_set_timing_advance: (skip) */ void mm_cell_info_tdscdma_set_timing_advance (MMCellInfoTdscdma *self, guint timing_advance) { self->priv->timing_advance = timing_advance; } /** * mm_cell_info_tdscdma_get_rscp: * @self: a #MMCellInfoTdscdma. * * Get the received signal code power. * * Returns: the RSCP, or -%G_MAXDOUBLE if not available. * * Since: 1.20 */ gdouble mm_cell_info_tdscdma_get_rscp (MMCellInfoTdscdma *self) { g_return_val_if_fail (MM_IS_CELL_INFO_TDSCDMA (self), -G_MAXDOUBLE); return self->priv->rscp; } /** * mm_cell_info_tdscdma_set_rscp: (skip) */ void mm_cell_info_tdscdma_set_rscp (MMCellInfoTdscdma *self, gdouble rscp) { self->priv->rscp = rscp; } /** * mm_cell_info_tdscdma_get_path_loss: * @self: a #MMCellInfoTdscdma. * * Get the path loss of the cell. * * Returns: the path loss, or %G_MAXUINT if not available. * * Since: 1.20 */ guint mm_cell_info_tdscdma_get_path_loss (MMCellInfoTdscdma *self) { g_return_val_if_fail (MM_IS_CELL_INFO_TDSCDMA (self), G_MAXUINT); return self->priv->path_loss; } /** * mm_cell_info_tdscdma_set_path_loss: (skip) */ void mm_cell_info_tdscdma_set_path_loss (MMCellInfoTdscdma *self, guint path_loss) { self->priv->path_loss = path_loss; } /*****************************************************************************/ static GString * build_string (MMCellInfo *_self) { MMCellInfoTdscdma *self = MM_CELL_INFO_TDSCDMA (_self); GString *str; str = g_string_new (NULL); MM_CELL_INFO_BUILD_STRING_APPEND ("operator id", "%s", operator_id, NULL); MM_CELL_INFO_BUILD_STRING_APPEND ("lac", "%s", lac, NULL); MM_CELL_INFO_BUILD_STRING_APPEND ("ci", "%s", ci, NULL); MM_CELL_INFO_BUILD_STRING_APPEND ("uarfcn", "%u", uarfcn, G_MAXUINT); MM_CELL_INFO_BUILD_STRING_APPEND ("cell parameter id", "%u", cell_parameter_id, G_MAXUINT); MM_CELL_INFO_BUILD_STRING_APPEND ("timing advance", "%u", timing_advance, G_MAXUINT); MM_CELL_INFO_BUILD_STRING_APPEND ("rscp", "%lf", rscp, -G_MAXDOUBLE); MM_CELL_INFO_BUILD_STRING_APPEND ("path loss", "%u", path_loss, G_MAXUINT); return str; } /*****************************************************************************/ /** * mm_cell_info_tdscdma_get_dictionary: (skip) */ static GVariantDict * get_dictionary (MMCellInfo *_self) { MMCellInfoTdscdma *self = MM_CELL_INFO_TDSCDMA (_self); GVariantDict *dict; dict = g_variant_dict_new (NULL); MM_CELL_INFO_GET_DICTIONARY_INSERT (OPERATOR_ID, operator_id, string, NULL); MM_CELL_INFO_GET_DICTIONARY_INSERT (LAC, lac, string, NULL); MM_CELL_INFO_GET_DICTIONARY_INSERT (CI, ci, string, NULL); MM_CELL_INFO_GET_DICTIONARY_INSERT (UARFCN, uarfcn, uint32, G_MAXUINT); MM_CELL_INFO_GET_DICTIONARY_INSERT (CELL_PARAMETER_ID, cell_parameter_id, uint32, G_MAXUINT); MM_CELL_INFO_GET_DICTIONARY_INSERT (TIMING_ADVANCE, timing_advance, uint32, G_MAXUINT); MM_CELL_INFO_GET_DICTIONARY_INSERT (RSCP, rscp, double, -G_MAXDOUBLE); MM_CELL_INFO_GET_DICTIONARY_INSERT (PATH_LOSS, path_loss, uint32, G_MAXUINT); return dict; } /*****************************************************************************/ /** * mm_cell_info_tdscdma_new_from_dictionary: (skip) */ MMCellInfo * mm_cell_info_tdscdma_new_from_dictionary (GVariantDict *dict) { MMCellInfoTdscdma *self; self = MM_CELL_INFO_TDSCDMA (g_object_new (MM_TYPE_CELL_INFO_TDSCDMA, NULL)); if (dict) { MM_CELL_INFO_NEW_FROM_DICTIONARY_STRING_SET (tdscdma, OPERATOR_ID, operator_id); MM_CELL_INFO_NEW_FROM_DICTIONARY_STRING_SET (tdscdma, LAC, lac); MM_CELL_INFO_NEW_FROM_DICTIONARY_STRING_SET (tdscdma, CI, ci); MM_CELL_INFO_NEW_FROM_DICTIONARY_NUM_SET (tdscdma, UARFCN, uarfcn, UINT32, uint32); MM_CELL_INFO_NEW_FROM_DICTIONARY_NUM_SET (tdscdma, CELL_PARAMETER_ID, cell_parameter_id, UINT32, uint32); MM_CELL_INFO_NEW_FROM_DICTIONARY_NUM_SET (tdscdma, TIMING_ADVANCE, timing_advance, UINT32, uint32); MM_CELL_INFO_NEW_FROM_DICTIONARY_NUM_SET (tdscdma, RSCP, rscp, DOUBLE, double); MM_CELL_INFO_NEW_FROM_DICTIONARY_NUM_SET (tdscdma, PATH_LOSS, path_loss, UINT32, uint32); } return MM_CELL_INFO (self); } /*****************************************************************************/ static void mm_cell_info_tdscdma_init (MMCellInfoTdscdma *self) { self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, MM_TYPE_CELL_INFO_TDSCDMA, MMCellInfoTdscdmaPrivate); self->priv->uarfcn = G_MAXUINT; self->priv->cell_parameter_id = G_MAXUINT; self->priv->timing_advance = G_MAXUINT; self->priv->rscp = -G_MAXDOUBLE; self->priv->path_loss = G_MAXUINT; } static void finalize (GObject *object) { MMCellInfoTdscdma *self = MM_CELL_INFO_TDSCDMA (object); g_free (self->priv->operator_id); g_free (self->priv->lac); g_free (self->priv->ci); G_OBJECT_CLASS (mm_cell_info_tdscdma_parent_class)->finalize (object); } static void mm_cell_info_tdscdma_class_init (MMCellInfoTdscdmaClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); MMCellInfoClass *cell_info_class = MM_CELL_INFO_CLASS (klass); g_type_class_add_private (object_class, sizeof (MMCellInfoTdscdmaPrivate)); object_class->finalize = finalize; cell_info_class->get_dictionary = get_dictionary; cell_info_class->build_string = build_string; } ModemManager-1.23.4-dev/libmm-glib/mm-cell-info-tdscdma.h000066400000000000000000000114331456466623000230070ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * libmm-glib -- Access modem status & information from glib applications * * 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 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 Street, Fifth Floor, * Boston, MA 02110-1301 USA. * * Copyright (C) 2022 Aleksander Morgado */ #ifndef MM_CELL_INFO_TDSCDMA_H #define MM_CELL_INFO_TDSCDMA_H #if !defined (__LIBMM_GLIB_H_INSIDE__) && !defined (LIBMM_GLIB_COMPILATION) #error "Only can be included directly." #endif #include #include #include "mm-cell-info.h" G_BEGIN_DECLS #define MM_TYPE_CELL_INFO_TDSCDMA (mm_cell_info_tdscdma_get_type ()) #define MM_CELL_INFO_TDSCDMA(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_CELL_INFO_TDSCDMA, MMCellInfoTdscdma)) #define MM_CELL_INFO_TDSCDMA_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_CELL_INFO_TDSCDMA, MMCellInfoTdscdmaClass)) #define MM_IS_CELL_INFO_TDSCDMA(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_CELL_INFO_TDSCDMA)) #define MM_IS_CELL_INFO_TDSCDMA_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_CELL_INFO_TDSCDMA)) #define MM_CELL_INFO_TDSCDMA_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_CELL_INFO_TDSCDMA, MMCellInfoTdscdmaClass)) typedef struct _MMCellInfoTdscdma MMCellInfoTdscdma; typedef struct _MMCellInfoTdscdmaClass MMCellInfoTdscdmaClass; typedef struct _MMCellInfoTdscdmaPrivate MMCellInfoTdscdmaPrivate; /** * MMCellInfoTdscdma: * * The #MMCellInfoTdscdma structure contains private data and should only be * accessed using the provided API. */ struct _MMCellInfoTdscdma { /*< private >*/ MMCellInfo parent; MMCellInfoTdscdmaPrivate *priv; }; struct _MMCellInfoTdscdmaClass { /*< private >*/ MMCellInfoClass parent; }; GType mm_cell_info_tdscdma_get_type (void); G_DEFINE_AUTOPTR_CLEANUP_FUNC (MMCellInfoTdscdma, g_object_unref) const gchar *mm_cell_info_tdscdma_get_operator_id (MMCellInfoTdscdma *self); const gchar *mm_cell_info_tdscdma_get_lac (MMCellInfoTdscdma *self); const gchar *mm_cell_info_tdscdma_get_ci (MMCellInfoTdscdma *self); guint mm_cell_info_tdscdma_get_uarfcn (MMCellInfoTdscdma *self); guint mm_cell_info_tdscdma_get_cell_parameter_id (MMCellInfoTdscdma *self); guint mm_cell_info_tdscdma_get_timing_advance (MMCellInfoTdscdma *self); gdouble mm_cell_info_tdscdma_get_rscp (MMCellInfoTdscdma *self); guint mm_cell_info_tdscdma_get_path_loss (MMCellInfoTdscdma *self); /*****************************************************************************/ /* ModemManager/libmm-glib/mmcli specific methods */ #if defined (_LIBMM_INSIDE_MM) || \ defined (_LIBMM_INSIDE_MMCLI) || \ defined (LIBMM_GLIB_COMPILATION) void mm_cell_info_tdscdma_set_operator_id (MMCellInfoTdscdma *self, const gchar *operator_id); void mm_cell_info_tdscdma_set_lac (MMCellInfoTdscdma *self, const gchar *lac); void mm_cell_info_tdscdma_set_ci (MMCellInfoTdscdma *self, const gchar *ci); void mm_cell_info_tdscdma_set_uarfcn (MMCellInfoTdscdma *self, guint uarfcn); void mm_cell_info_tdscdma_set_cell_parameter_id (MMCellInfoTdscdma *self, guint cell_parameter_id); void mm_cell_info_tdscdma_set_timing_advance (MMCellInfoTdscdma *self, guint timing_advance); void mm_cell_info_tdscdma_set_rscp (MMCellInfoTdscdma *self, gdouble rscp); void mm_cell_info_tdscdma_set_path_loss (MMCellInfoTdscdma *self, guint path_loss); MMCellInfo *mm_cell_info_tdscdma_new_from_dictionary (GVariantDict *dict); #endif G_END_DECLS #endif /* MM_CELL_INFO_TDSCDMA_H */ ModemManager-1.23.4-dev/libmm-glib/mm-cell-info-umts.c000066400000000000000000000343061456466623000223570ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * libmm-glib -- Access modem status & information from glib applications * * 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 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 Street, Fifth Floor, * Boston, MA 02110-1301 USA. * * Copyright (C) 2022 Aleksander Morgado */ #include "mm-helpers.h" #include "mm-cell-info-umts.h" /** * SECTION: mm-cell-info-umts * @title: MMCellInfoUmts * @short_description: Helper object to report UMTS cell info * * The #MMCellInfoUmts is an object used to report UMTS cell * information. * * The object inherits from the generic #MMCellInfo. */ G_DEFINE_TYPE (MMCellInfoUmts, mm_cell_info_umts, MM_TYPE_CELL_INFO) #define PROPERTY_OPERATOR_ID "operator-id" #define PROPERTY_LAC "lac" #define PROPERTY_CI "ci" #define PROPERTY_FREQUENCY_FDD_UL "frequency-fdd-ul" #define PROPERTY_FREQUENCY_FDD_DL "frequency-fdd-dl" #define PROPERTY_FREQUENCY_TDD "frequency-tdd" #define PROPERTY_UARFCN "uarfcn" #define PROPERTY_PSC "psc" #define PROPERTY_RSCP "rscp" #define PROPERTY_ECIO "ecio" #define PROPERTY_PATH_LOSS "path-loss" struct _MMCellInfoUmtsPrivate { gchar *operator_id; gchar *lac; gchar *ci; guint frequency_fdd_ul; guint frequency_fdd_dl; guint frequency_tdd; guint uarfcn; guint psc; gdouble rscp; gdouble ecio; guint path_loss; }; /*****************************************************************************/ /** * mm_cell_info_umts_get_operator_id: * @self: a #MMCellInfoUmts. * * Get the PLMN MCC/MNC. * * Returns: (transfer none): the MCCMNC, or %NULL if not available. * * Since: 1.20 */ const gchar * mm_cell_info_umts_get_operator_id (MMCellInfoUmts *self) { g_return_val_if_fail (MM_IS_CELL_INFO_UMTS (self), NULL); RETURN_NON_EMPTY_CONSTANT_STRING (self->priv->operator_id); } /** * mm_cell_info_umts_set_operator_id: (skip) */ void mm_cell_info_umts_set_operator_id (MMCellInfoUmts *self, const gchar *operator_id) { g_free (self->priv->operator_id); self->priv->operator_id = g_strdup (operator_id); } /** * mm_cell_info_umts_get_lac: * @self: a #MMCellInfoUmts. * * Get the two-byte Location Area Code of the base station. * * Encoded in upper-case hexadecimal format without leading zeros, * as specified in 3GPP TS 27.007. * * Returns: (transfer none): the MCCMNC, or %NULL if not available. * * Since: 1.20 */ const gchar * mm_cell_info_umts_get_lac (MMCellInfoUmts *self) { g_return_val_if_fail (MM_IS_CELL_INFO_UMTS (self), NULL); RETURN_NON_EMPTY_CONSTANT_STRING (self->priv->lac); } /** * mm_cell_info_umts_set_lac: (skip) */ void mm_cell_info_umts_set_lac (MMCellInfoUmts *self, const gchar *lac) { g_free (self->priv->lac); self->priv->lac = g_strdup (lac); } /** * mm_cell_info_umts_get_ci: * @self: a #MMCellInfoUmts. * * Get the two- or four-byte Cell Identifier. * * Encoded in upper-case hexadecimal format without leading zeros, * as specified in 3GPP TS 27.007. * * Returns: (transfer none): the MCCMNC, or %NULL if not available. * * Since: 1.20 */ const gchar * mm_cell_info_umts_get_ci (MMCellInfoUmts *self) { g_return_val_if_fail (MM_IS_CELL_INFO_UMTS (self), NULL); RETURN_NON_EMPTY_CONSTANT_STRING (self->priv->ci); } /** * mm_cell_info_umts_set_ci: (skip) */ void mm_cell_info_umts_set_ci (MMCellInfoUmts *self, const gchar *ci) { g_free (self->priv->ci); self->priv->ci = g_strdup (ci); } /** * mm_cell_info_umts_get_frequency_fdd_ul: * @self: a #MMCellInfoUmts. * * Get the frequency of the uplink in kHz while in FDD. * * Returns: the frequency, or %G_MAXUINT if not available. * * Since: 1.20 */ guint mm_cell_info_umts_get_frequency_fdd_ul (MMCellInfoUmts *self) { g_return_val_if_fail (MM_IS_CELL_INFO_UMTS (self), G_MAXUINT); return self->priv->frequency_fdd_ul; } /** * mm_cell_info_umts_set_frequency_fdd_ul: (skip) */ void mm_cell_info_umts_set_frequency_fdd_ul (MMCellInfoUmts *self, guint frequency_fdd_ul) { self->priv->frequency_fdd_ul = frequency_fdd_ul; } /** * mm_cell_info_umts_get_frequency_fdd_dl: * @self: a #MMCellInfoUmts. * * Get the frequency of the downlink in kHz while in FDD. * * Returns: the frequency, or %G_MAXUINT if not available. * * Since: 1.20 */ guint mm_cell_info_umts_get_frequency_fdd_dl (MMCellInfoUmts *self) { g_return_val_if_fail (MM_IS_CELL_INFO_UMTS (self), G_MAXUINT); return self->priv->frequency_fdd_dl; } /** * mm_cell_info_umts_set_frequency_fdd_dl: (skip) */ void mm_cell_info_umts_set_frequency_fdd_dl (MMCellInfoUmts *self, guint frequency_fdd_dl) { self->priv->frequency_fdd_dl = frequency_fdd_dl; } /** * mm_cell_info_umts_get_frequency_tdd: * @self: a #MMCellInfoUmts. * * Get the frequency in kHz while in TDD. * * Returns: the frequency, or %G_MAXUINT if not available. * * Since: 1.20 */ guint mm_cell_info_umts_get_frequency_tdd (MMCellInfoUmts *self) { g_return_val_if_fail (MM_IS_CELL_INFO_UMTS (self), G_MAXUINT); return self->priv->frequency_tdd; } /** * mm_cell_info_umts_set_frequency_tdd: (skip) */ void mm_cell_info_umts_set_frequency_tdd (MMCellInfoUmts *self, guint frequency_tdd) { self->priv->frequency_tdd = frequency_tdd; } /** * mm_cell_info_umts_get_uarfcn: * @self: a #MMCellInfoUmts. * * Get the UTRA absolute RF channel number. * * Returns: the UARFCN, or %G_MAXUINT if not available. * * Since: 1.20 */ guint mm_cell_info_umts_get_uarfcn (MMCellInfoUmts *self) { g_return_val_if_fail (MM_IS_CELL_INFO_UMTS (self), G_MAXUINT); return self->priv->uarfcn; } /** * mm_cell_info_umts_set_uarfcn: (skip) */ void mm_cell_info_umts_set_uarfcn (MMCellInfoUmts *self, guint uarfcn) { self->priv->uarfcn = uarfcn; } /** * mm_cell_info_umts_get_psc: * @self: a #MMCellInfoUmts. * * Get the primary scrambling code. * * Returns: the PSC, or %G_MAXUINT if not available. * * Since: 1.20 */ guint mm_cell_info_umts_get_psc (MMCellInfoUmts *self) { g_return_val_if_fail (MM_IS_CELL_INFO_UMTS (self), G_MAXUINT); return self->priv->psc; } /** * mm_cell_info_umts_set_psc: (skip) */ void mm_cell_info_umts_set_psc (MMCellInfoUmts *self, guint psc) { self->priv->psc = psc; } /** * mm_cell_info_umts_get_rscp: * @self: a #MMCellInfoUmts. * * Get the received signal code power. * * Returns: the RSCP, or -%G_MAXDOUBLE if not available. * * Since: 1.20 */ gdouble mm_cell_info_umts_get_rscp (MMCellInfoUmts *self) { g_return_val_if_fail (MM_IS_CELL_INFO_UMTS (self), -G_MAXDOUBLE); return self->priv->rscp; } /** * mm_cell_info_umts_set_rscp: (skip) */ void mm_cell_info_umts_set_rscp (MMCellInfoUmts *self, gdouble rscp) { self->priv->rscp = rscp; } /** * mm_cell_info_umts_get_ecio: * @self: a #MMCellInfoUmts. * * Get the ECIO, the received energy per chip divided by the power density * in the band measured in dBm on the primary CPICH channel of the cell. * * Returns: the ECIO, or -%G_MAXDOUBLE if not available. * * Since: 1.20 */ gdouble mm_cell_info_umts_get_ecio (MMCellInfoUmts *self) { g_return_val_if_fail (MM_IS_CELL_INFO_UMTS (self), -G_MAXDOUBLE); return self->priv->ecio; } /** * mm_cell_info_umts_set_ecio: (skip) */ void mm_cell_info_umts_set_ecio (MMCellInfoUmts *self, gdouble ecio) { self->priv->ecio = ecio; } /** * mm_cell_info_umts_get_path_loss: * @self: a #MMCellInfoUmts. * * Get the path loss of the cell. * * Returns: the path loss, or %G_MAXUINT if not available. * * Since: 1.20 */ guint mm_cell_info_umts_get_path_loss (MMCellInfoUmts *self) { g_return_val_if_fail (MM_IS_CELL_INFO_UMTS (self), G_MAXUINT); return self->priv->path_loss; } /** * mm_cell_info_umts_set_path_loss: (skip) */ void mm_cell_info_umts_set_path_loss (MMCellInfoUmts *self, guint path_loss) { self->priv->path_loss = path_loss; } /*****************************************************************************/ static GString * build_string (MMCellInfo *_self) { MMCellInfoUmts *self = MM_CELL_INFO_UMTS (_self); GString *str; str = g_string_new (NULL); MM_CELL_INFO_BUILD_STRING_APPEND ("operator id", "%s", operator_id, NULL); MM_CELL_INFO_BUILD_STRING_APPEND ("lac", "%s", lac, NULL); MM_CELL_INFO_BUILD_STRING_APPEND ("ci", "%s", ci, NULL); MM_CELL_INFO_BUILD_STRING_APPEND ("frequency fdd ul", "%u", frequency_fdd_ul, G_MAXUINT); MM_CELL_INFO_BUILD_STRING_APPEND ("frequency fdd dl", "%u", frequency_fdd_dl, G_MAXUINT); MM_CELL_INFO_BUILD_STRING_APPEND ("frequency tdd", "%u", frequency_tdd, G_MAXUINT); MM_CELL_INFO_BUILD_STRING_APPEND ("uarfcn", "%u", uarfcn, G_MAXUINT); MM_CELL_INFO_BUILD_STRING_APPEND ("psc", "%u", psc, G_MAXUINT); MM_CELL_INFO_BUILD_STRING_APPEND ("rscp", "%lf", rscp, -G_MAXDOUBLE); MM_CELL_INFO_BUILD_STRING_APPEND ("ecio", "%lf", ecio, -G_MAXDOUBLE); MM_CELL_INFO_BUILD_STRING_APPEND ("path loss", "%u", path_loss, G_MAXUINT); return str; } /*****************************************************************************/ /** * mm_cell_info_umts_get_dictionary: (skip) */ static GVariantDict * get_dictionary (MMCellInfo *_self) { MMCellInfoUmts *self = MM_CELL_INFO_UMTS (_self); GVariantDict *dict; dict = g_variant_dict_new (NULL); MM_CELL_INFO_GET_DICTIONARY_INSERT (OPERATOR_ID, operator_id, string, NULL); MM_CELL_INFO_GET_DICTIONARY_INSERT (LAC, lac, string, NULL); MM_CELL_INFO_GET_DICTIONARY_INSERT (CI, ci, string, NULL); MM_CELL_INFO_GET_DICTIONARY_INSERT (FREQUENCY_FDD_UL, frequency_fdd_ul, uint32, G_MAXUINT); MM_CELL_INFO_GET_DICTIONARY_INSERT (FREQUENCY_FDD_DL, frequency_fdd_dl, uint32, G_MAXUINT); MM_CELL_INFO_GET_DICTIONARY_INSERT (FREQUENCY_TDD, frequency_tdd, uint32, G_MAXUINT); MM_CELL_INFO_GET_DICTIONARY_INSERT (UARFCN, uarfcn, uint32, G_MAXUINT); MM_CELL_INFO_GET_DICTIONARY_INSERT (PSC, psc, uint32, G_MAXUINT); MM_CELL_INFO_GET_DICTIONARY_INSERT (RSCP, rscp, double, -G_MAXDOUBLE); MM_CELL_INFO_GET_DICTIONARY_INSERT (ECIO, ecio, double, -G_MAXDOUBLE); MM_CELL_INFO_GET_DICTIONARY_INSERT (PATH_LOSS, path_loss, uint32, G_MAXUINT); return dict; } /*****************************************************************************/ /** * mm_cell_info_umts_new_from_dictionary: (skip) */ MMCellInfo * mm_cell_info_umts_new_from_dictionary (GVariantDict *dict) { MMCellInfoUmts *self; self = MM_CELL_INFO_UMTS (g_object_new (MM_TYPE_CELL_INFO_UMTS, NULL)); if (dict) { MM_CELL_INFO_NEW_FROM_DICTIONARY_STRING_SET (umts, OPERATOR_ID, operator_id); MM_CELL_INFO_NEW_FROM_DICTIONARY_STRING_SET (umts, LAC, lac); MM_CELL_INFO_NEW_FROM_DICTIONARY_STRING_SET (umts, CI, ci); MM_CELL_INFO_NEW_FROM_DICTIONARY_NUM_SET (umts, FREQUENCY_FDD_UL, frequency_fdd_ul, UINT32, uint32); MM_CELL_INFO_NEW_FROM_DICTIONARY_NUM_SET (umts, FREQUENCY_FDD_DL, frequency_fdd_dl, UINT32, uint32); MM_CELL_INFO_NEW_FROM_DICTIONARY_NUM_SET (umts, FREQUENCY_TDD, frequency_tdd, UINT32, uint32); MM_CELL_INFO_NEW_FROM_DICTIONARY_NUM_SET (umts, UARFCN, uarfcn, UINT32, uint32); MM_CELL_INFO_NEW_FROM_DICTIONARY_NUM_SET (umts, PSC, psc, UINT32, uint32); MM_CELL_INFO_NEW_FROM_DICTIONARY_NUM_SET (umts, RSCP, rscp, DOUBLE, double); MM_CELL_INFO_NEW_FROM_DICTIONARY_NUM_SET (umts, ECIO, ecio, DOUBLE, double); MM_CELL_INFO_NEW_FROM_DICTIONARY_NUM_SET (umts, PATH_LOSS, path_loss, UINT32, uint32); } return MM_CELL_INFO (self); } /*****************************************************************************/ static void mm_cell_info_umts_init (MMCellInfoUmts *self) { self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, MM_TYPE_CELL_INFO_UMTS, MMCellInfoUmtsPrivate); self->priv->frequency_fdd_ul = G_MAXUINT; self->priv->frequency_fdd_dl = G_MAXUINT; self->priv->frequency_tdd = G_MAXUINT; self->priv->uarfcn = G_MAXUINT; self->priv->psc = G_MAXUINT; self->priv->rscp = -G_MAXDOUBLE; self->priv->ecio = -G_MAXDOUBLE; self->priv->path_loss = G_MAXUINT; } static void finalize (GObject *object) { MMCellInfoUmts *self = MM_CELL_INFO_UMTS (object); g_free (self->priv->operator_id); g_free (self->priv->lac); g_free (self->priv->ci); G_OBJECT_CLASS (mm_cell_info_umts_parent_class)->finalize (object); } static void mm_cell_info_umts_class_init (MMCellInfoUmtsClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); MMCellInfoClass *cell_info_class = MM_CELL_INFO_CLASS (klass); g_type_class_add_private (object_class, sizeof (MMCellInfoUmtsPrivate)); object_class->finalize = finalize; cell_info_class->get_dictionary = get_dictionary; cell_info_class->build_string = build_string; } ModemManager-1.23.4-dev/libmm-glib/mm-cell-info-umts.h000066400000000000000000000122221456466623000223550ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * libmm-glib -- Access modem status & information from glib applications * * 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 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 Street, Fifth Floor, * Boston, MA 02110-1301 USA. * * Copyright (C) 2022 Aleksander Morgado */ #ifndef MM_CELL_INFO_UMTS_H #define MM_CELL_INFO_UMTS_H #if !defined (__LIBMM_GLIB_H_INSIDE__) && !defined (LIBMM_GLIB_COMPILATION) #error "Only can be included directly." #endif #include #include #include "mm-cell-info.h" G_BEGIN_DECLS #define MM_TYPE_CELL_INFO_UMTS (mm_cell_info_umts_get_type ()) #define MM_CELL_INFO_UMTS(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_CELL_INFO_UMTS, MMCellInfoUmts)) #define MM_CELL_INFO_UMTS_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_CELL_INFO_UMTS, MMCellInfoUmtsClass)) #define MM_IS_CELL_INFO_UMTS(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_CELL_INFO_UMTS)) #define MM_IS_CELL_INFO_UMTS_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_CELL_INFO_UMTS)) #define MM_CELL_INFO_UMTS_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_CELL_INFO_UMTS, MMCellInfoUmtsClass)) typedef struct _MMCellInfoUmts MMCellInfoUmts; typedef struct _MMCellInfoUmtsClass MMCellInfoUmtsClass; typedef struct _MMCellInfoUmtsPrivate MMCellInfoUmtsPrivate; /** * MMCellInfoUmts: * * The #MMCellInfoUmts structure contains private data and should only be * accessed using the provided API. */ struct _MMCellInfoUmts { /*< private >*/ MMCellInfo parent; MMCellInfoUmtsPrivate *priv; }; struct _MMCellInfoUmtsClass { /*< private >*/ MMCellInfoClass parent; }; GType mm_cell_info_umts_get_type (void); G_DEFINE_AUTOPTR_CLEANUP_FUNC (MMCellInfoUmts, g_object_unref) const gchar *mm_cell_info_umts_get_operator_id (MMCellInfoUmts *self); const gchar *mm_cell_info_umts_get_lac (MMCellInfoUmts *self); const gchar *mm_cell_info_umts_get_ci (MMCellInfoUmts *self); guint mm_cell_info_umts_get_frequency_fdd_ul (MMCellInfoUmts *self); guint mm_cell_info_umts_get_frequency_fdd_dl (MMCellInfoUmts *self); guint mm_cell_info_umts_get_frequency_tdd (MMCellInfoUmts *self); guint mm_cell_info_umts_get_uarfcn (MMCellInfoUmts *self); guint mm_cell_info_umts_get_psc (MMCellInfoUmts *self); gdouble mm_cell_info_umts_get_rscp (MMCellInfoUmts *self); gdouble mm_cell_info_umts_get_ecio (MMCellInfoUmts *self); guint mm_cell_info_umts_get_path_loss (MMCellInfoUmts *self); /*****************************************************************************/ /* ModemManager/libmm-glib/mmcli specific methods */ #if defined (_LIBMM_INSIDE_MM) || \ defined (_LIBMM_INSIDE_MMCLI) || \ defined (LIBMM_GLIB_COMPILATION) void mm_cell_info_umts_set_operator_id (MMCellInfoUmts *self, const gchar *operator_id); void mm_cell_info_umts_set_lac (MMCellInfoUmts *self, const gchar *lac); void mm_cell_info_umts_set_ci (MMCellInfoUmts *self, const gchar *ci); void mm_cell_info_umts_set_frequency_fdd_ul (MMCellInfoUmts *self, guint frequency_fdd_ul); void mm_cell_info_umts_set_frequency_fdd_dl (MMCellInfoUmts *self, guint frequency_fdd_ul); void mm_cell_info_umts_set_frequency_tdd (MMCellInfoUmts *self, guint frequency_tdd); void mm_cell_info_umts_set_uarfcn (MMCellInfoUmts *self, guint uarfcn); void mm_cell_info_umts_set_psc (MMCellInfoUmts *self, guint psc); void mm_cell_info_umts_set_rscp (MMCellInfoUmts *self, gdouble rscp); void mm_cell_info_umts_set_ecio (MMCellInfoUmts *self, gdouble ecio); void mm_cell_info_umts_set_path_loss (MMCellInfoUmts *self, guint path_loss); MMCellInfo *mm_cell_info_umts_new_from_dictionary (GVariantDict *dict); #endif G_END_DECLS #endif /* MM_CELL_INFO_UMTS_H */ ModemManager-1.23.4-dev/libmm-glib/mm-cell-info.c000066400000000000000000000161361456466623000213720ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * libmm-glib -- Access modem status & information from glib applications * * 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 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 Street, Fifth Floor, * Boston, MA 02110-1301 USA. * * Copyright (C) 2022 Aleksander Morgado */ #include "mm-cell-info.h" #include "mm-cell-info-cdma.h" #include "mm-cell-info-gsm.h" #include "mm-cell-info-umts.h" #include "mm-cell-info-tdscdma.h" #include "mm-cell-info-lte.h" #include "mm-cell-info-nr5g.h" #include "mm-enums-types.h" #include "mm-flags-types.h" #include "mm-errors-types.h" /** * SECTION: mm-cell-info * @title: MMCellInfo * @short_description: Helper base object to report cell info * * The #MMCellInfo is a base object used to report cell information. * * This object is retrieved from the #MMModem object with * mm_modem_get_cell_info() or mm_modem_get_cell_info_sync(). */ G_DEFINE_TYPE (MMCellInfo, mm_cell_info, G_TYPE_OBJECT) #define PROPERTY_CELL_TYPE "cell-type" #define PROPERTY_SERVING "serving" struct _MMCellInfoPrivate { MMCellType cell_type; gboolean serving; }; /*****************************************************************************/ static void ensure_cell_type (MMCellInfo *self) { if (self->priv->cell_type != MM_CELL_TYPE_UNKNOWN) return; if (MM_IS_CELL_INFO_CDMA (self)) self->priv->cell_type = MM_CELL_TYPE_CDMA; else if (MM_IS_CELL_INFO_GSM (self)) self->priv->cell_type = MM_CELL_TYPE_GSM; else if (MM_IS_CELL_INFO_UMTS (self)) self->priv->cell_type = MM_CELL_TYPE_UMTS; else if (MM_IS_CELL_INFO_TDSCDMA (self)) self->priv->cell_type = MM_CELL_TYPE_TDSCDMA; else if (MM_IS_CELL_INFO_LTE (self)) self->priv->cell_type = MM_CELL_TYPE_LTE; else if (MM_IS_CELL_INFO_NR5G (self)) self->priv->cell_type = MM_CELL_TYPE_5GNR; } /** * mm_cell_info_get_cell_type: * @self: a #MMCellInfo. * * Get the type of cell. * * Returns: a #MMCellType. * * Since: 1.20 */ MMCellType mm_cell_info_get_cell_type (MMCellInfo *self) { g_return_val_if_fail (MM_IS_CELL_INFO (self), MM_CELL_TYPE_UNKNOWN); ensure_cell_type (self); return self->priv->cell_type; } /** * mm_cell_info_get_serving: * @self: a #MMCellInfo. * * Get whether the cell is a serving cell or a neighboring cell.a * * Returns: %TRUE if the cell is a serving cell, %FALSE otherwise. * * Since: 1.20 */ gboolean mm_cell_info_get_serving (MMCellInfo *self) { g_return_val_if_fail (MM_IS_CELL_INFO (self), FALSE); return self->priv->serving; } /** * mm_cell_info_set_serving: (skip) */ void mm_cell_info_set_serving (MMCellInfo *self, gboolean serving) { g_return_if_fail (MM_IS_CELL_INFO (self)); self->priv->serving = serving; } /*****************************************************************************/ /** * mm_cell_info_get_dictionary: (skip) */ GVariant * mm_cell_info_get_dictionary (MMCellInfo *self) { g_autoptr(GVariantDict) dict = NULL; dict = MM_CELL_INFO_GET_CLASS (self)->get_dictionary (self); g_assert (dict); g_variant_dict_insert_value (dict, PROPERTY_SERVING, g_variant_new_boolean (self->priv->serving)); g_variant_dict_insert_value (dict, PROPERTY_CELL_TYPE, g_variant_new_uint32 (mm_cell_info_get_cell_type (self))); return g_variant_ref_sink (g_variant_dict_end (dict)); } /*****************************************************************************/ /** * mm_cell_info_new_from_dictionary: (skip) */ MMCellInfo * mm_cell_info_new_from_dictionary (GVariant *dictionary, GError **error) { g_autoptr(MMCellInfo) self = NULL; g_autoptr(GVariantDict) dict = NULL; GVariant *aux; dict = g_variant_dict_new (dictionary); aux = g_variant_dict_lookup_value (dict, PROPERTY_CELL_TYPE, G_VARIANT_TYPE_UINT32); if (!aux) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS, "missing '" PROPERTY_CELL_TYPE "' key in cell info"); return NULL; } switch (g_variant_get_uint32 (aux)) { case MM_CELL_TYPE_CDMA: self = mm_cell_info_cdma_new_from_dictionary (dict); break; case MM_CELL_TYPE_GSM: self = mm_cell_info_gsm_new_from_dictionary (dict); break; case MM_CELL_TYPE_UMTS: self = mm_cell_info_umts_new_from_dictionary (dict); break; case MM_CELL_TYPE_TDSCDMA: self = mm_cell_info_tdscdma_new_from_dictionary (dict); break; case MM_CELL_TYPE_LTE: self = mm_cell_info_lte_new_from_dictionary (dict); break; case MM_CELL_TYPE_5GNR: self = mm_cell_info_nr5g_new_from_dictionary (dict); break; default: break; } g_variant_unref (aux); if (!self) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS, "unknown '" PROPERTY_CELL_TYPE "' key value in cell info"); return NULL; } aux = g_variant_dict_lookup_value (dict, PROPERTY_SERVING, G_VARIANT_TYPE_BOOLEAN); if (aux) { mm_cell_info_set_serving (self, g_variant_get_boolean (aux)); g_variant_unref (aux); } return g_steal_pointer (&self); } /*****************************************************************************/ /** * mm_cell_info_build_string: (skip) */ gchar * mm_cell_info_build_string (MMCellInfo *self) { GString *str; GString *substr; substr = MM_CELL_INFO_GET_CLASS (self)->build_string (self); g_assert (substr); ensure_cell_type (self); str = g_string_new (NULL); g_string_append_printf (str, "cell type: %s, serving: %s", mm_cell_type_get_string (self->priv->cell_type), self->priv->serving ? "yes" : "no"); g_string_append_len (str, substr->str, (gssize)substr->len); g_string_free (substr, TRUE); return g_string_free (str, FALSE); } /*****************************************************************************/ static void mm_cell_info_init (MMCellInfo *self) { self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, MM_TYPE_CELL_INFO, MMCellInfoPrivate); self->priv->cell_type = MM_CELL_TYPE_UNKNOWN; } static void mm_cell_info_class_init (MMCellInfoClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); g_type_class_add_private (object_class, sizeof (MMCellInfoPrivate)); } ModemManager-1.23.4-dev/libmm-glib/mm-cell-info.h000066400000000000000000000126461456466623000214010ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * libmm-glib -- Access modem status & information from glib applications * * 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 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 Street, Fifth Floor, * Boston, MA 02110-1301 USA. * * Copyright (C) 2022 Aleksander Morgado */ #ifndef MM_CELL_INFO_H #define MM_CELL_INFO_H #if !defined (__LIBMM_GLIB_H_INSIDE__) && !defined (LIBMM_GLIB_COMPILATION) #error "Only can be included directly." #endif #include #include G_BEGIN_DECLS #define MM_TYPE_CELL_INFO (mm_cell_info_get_type ()) #define MM_CELL_INFO(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_CELL_INFO, MMCellInfo)) #define MM_CELL_INFO_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_CELL_INFO, MMCellInfoClass)) #define MM_IS_CELL_INFO(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_CELL_INFO)) #define MM_IS_CELL_INFO_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_CELL_INFO)) #define MM_CELL_INFO_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_CELL_INFO, MMCellInfoClass)) typedef struct _MMCellInfo MMCellInfo; typedef struct _MMCellInfoClass MMCellInfoClass; typedef struct _MMCellInfoPrivate MMCellInfoPrivate; /** * MMCellInfo: * * The #MMCellInfo structure contains private data and should only be * accessed using the provided API. */ struct _MMCellInfo { /*< private >*/ GObject parent; MMCellInfoPrivate *priv; }; struct _MMCellInfoClass { /*< private >*/ GObjectClass parent; GVariantDict * (* get_dictionary) (MMCellInfo *self); GString * (* build_string) (MMCellInfo *self); /* class padding */ gpointer padding [5]; }; GType mm_cell_info_get_type (void); G_DEFINE_AUTOPTR_CLEANUP_FUNC (MMCellInfo, g_object_unref) MMCellType mm_cell_info_get_cell_type (MMCellInfo *self); gboolean mm_cell_info_get_serving (MMCellInfo *self); /*****************************************************************************/ /* ModemManager/libmm-glib/mmcli specific methods */ #if defined (_LIBMM_INSIDE_MM) || \ defined (_LIBMM_INSIDE_MMCLI) || \ defined (LIBMM_GLIB_COMPILATION) void mm_cell_info_set_serving (MMCellInfo *self, gboolean serving); GVariant *mm_cell_info_get_dictionary (MMCellInfo *self); MMCellInfo *mm_cell_info_new_from_dictionary (GVariant *dictionary, GError **error); gchar *mm_cell_info_build_string (MMCellInfo *self); /* helpers to implement methods */ #define MM_CELL_INFO_NEW_FROM_DICTIONARY_STRING_SET(celltype,NAME,name) do { \ GVariant *aux; \ \ aux = g_variant_dict_lookup_value (dict, PROPERTY_##NAME, G_VARIANT_TYPE_STRING); \ if (aux) { \ mm_cell_info_##celltype##_set_##name (self, g_variant_get_string (aux, NULL)); \ g_variant_unref (aux); \ } \ } while (0) #define MM_CELL_INFO_NEW_FROM_DICTIONARY_NUM_SET(celltype,NAME,name,NUMTYPE,numtype) do { \ GVariant *aux; \ \ aux = g_variant_dict_lookup_value (dict, PROPERTY_##NAME, G_VARIANT_TYPE_##NUMTYPE); \ if (aux) { \ mm_cell_info_##celltype##_set_##name (self, g_variant_get_##numtype (aux)); \ g_variant_unref (aux); \ } \ } while (0) #define MM_CELL_INFO_GET_DICTIONARY_INSERT(NAME,name,vartype,INVALID) do { \ if (self->priv->name != INVALID) \ g_variant_dict_insert_value (dict, PROPERTY_##NAME, g_variant_new_##vartype (self->priv->name)); \ } while (0) #define MM_CELL_INFO_BUILD_STRING_APPEND(STR,FORMAT,name,INVALID) do { \ if (self->priv->name != INVALID) \ g_string_append_printf (str, ", " STR ": " FORMAT, self->priv->name); \ } while (0) #endif G_END_DECLS #endif /* MM_CELL_INFO_H */ ModemManager-1.23.4-dev/libmm-glib/mm-common-helpers.c000066400000000000000000001671571456466623000224640ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * libmm-glib -- Access modem status & information from glib applications * * 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 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 Street, Fifth Floor, * Boston, MA 02110-1301 USA. * * Copyright (C) 2010 - 2012 Red Hat, Inc. * Copyright (C) 2011 - 2012 Google, Inc. */ #include #include #include #include #include #include "mm-enums-types.h" #include "mm-flags-types.h" #include "mm-errors-types.h" #include "mm-common-helpers.h" #if (!GLIB_CHECK_VERSION (2, 58, 0)) G_DEFINE_AUTOPTR_CLEANUP_FUNC(GEnumClass, g_type_class_unref) G_DEFINE_AUTOPTR_CLEANUP_FUNC(GFlagsClass, g_type_class_unref) #endif /******************************************************************************/ /* Enums/flags to string builders */ gchar * mm_common_build_capabilities_string (const MMModemCapability *capabilities, guint n_capabilities) { gboolean first = TRUE; GString *str; guint i; if (!capabilities || !n_capabilities) return g_strdup ("none"); str = g_string_new (""); for (i = 0; i < n_capabilities; i++) { gchar *tmp; tmp = mm_modem_capability_build_string_from_mask (capabilities[i]); g_string_append_printf (str, "%s%s", first ? "" : "\n", tmp); g_free (tmp); if (first) first = FALSE; } return g_string_free (str, FALSE); } gchar * mm_common_build_bands_string (const MMModemBand *bands, guint n_bands) { gboolean first = TRUE; GString *str; guint i; if (!bands || !n_bands) return g_strdup ("none"); str = g_string_new (""); for (i = 0; i < n_bands; i++) { g_string_append_printf (str, "%s%s", first ? "" : ", ", mm_modem_band_get_string (bands[i])); if (first) first = FALSE; } return g_string_free (str, FALSE); } gchar * mm_common_build_ports_string (const MMModemPortInfo *ports, guint n_ports) { gboolean first = TRUE; GString *str; guint i; if (!ports || !n_ports) return g_strdup ("none"); str = g_string_new (""); for (i = 0; i < n_ports; i++) { g_string_append_printf (str, "%s%s (%s)", first ? "" : ", ", ports[i].name, mm_modem_port_type_get_string (ports[i].type)); if (first) first = FALSE; } return g_string_free (str, FALSE); } gchar * mm_common_build_sms_storages_string (const MMSmsStorage *storages, guint n_storages) { gboolean first = TRUE; GString *str; guint i; if (!storages || !n_storages) return g_strdup ("none"); str = g_string_new (""); for (i = 0; i < n_storages; i++) { g_string_append_printf (str, "%s%s", first ? "" : ", ", mm_sms_storage_get_string (storages[i])); if (first) first = FALSE; } return g_string_free (str, FALSE); } gchar * mm_common_build_mode_combinations_string (const MMModemModeCombination *modes, guint n_modes) { gboolean first = TRUE; GString *str; guint i; if (!modes || !n_modes) return g_strdup ("none"); str = g_string_new (""); for (i = 0; i < n_modes; i++) { gchar *allowed; gchar *preferred; allowed = mm_modem_mode_build_string_from_mask (modes[i].allowed); preferred = mm_modem_mode_build_string_from_mask (modes[i].preferred); g_string_append_printf (str, "%sallowed: %s; preferred: %s", first ? "" : "\n", allowed, preferred); g_free (allowed); g_free (preferred); if (first) first = FALSE; } return g_string_free (str, FALSE); } /******************************************************************************/ /* String to enums/flags parsers */ static gint _enum_from_string (GType type, const gchar *str, gint error_value, GError **error) { g_autoptr(GEnumClass) enum_class = NULL; gint value; guint i; enum_class = G_ENUM_CLASS (g_type_class_ref (type)); for (i = 0; enum_class->values[i].value_nick; i++) { if (!g_ascii_strcasecmp (str, enum_class->values[i].value_nick)) { value = enum_class->values[i].value; return value; } } g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS, "Couldn't match '%s' with a valid %s value", str, g_type_name (type)); return error_value; } static guint _flags_from_string (GType type, const gchar *str, guint error_value, GError **error) { g_auto(GStrv) flags_strings = NULL; g_autoptr(GFlagsClass) flags_class = NULL; guint value = 0; guint i; flags_class = G_FLAGS_CLASS (g_type_class_ref (type)); flags_strings = g_strsplit (str, "|", -1); for (i = 0; flags_strings[i]; i++) { guint j; gboolean found = FALSE; for (j = 0; flags_class->values[j].value_nick; j++) { if (!g_ascii_strcasecmp (flags_strings[i], flags_class->values[j].value_nick)) { value |= flags_class->values[j].value; found = TRUE; } } if (!found) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS, "Couldn't match '%s' with a valid %s value", flags_strings[i], g_type_name (type)); return error_value; } } return value; } MMModemCapability mm_common_get_capabilities_from_string (const gchar *str, GError **error) { GError *inner_error = NULL; MMModemCapability capabilities; g_auto(GStrv) capability_strings = NULL; g_autoptr(GFlagsClass) flags_class = NULL; capabilities = MM_MODEM_CAPABILITY_NONE; flags_class = G_FLAGS_CLASS (g_type_class_ref (MM_TYPE_MODEM_CAPABILITY)); capability_strings = g_strsplit (str, "|", -1); if (capability_strings) { guint i; for (i = 0; capability_strings[i]; i++) { guint j; gboolean found = FALSE; for (j = 0; flags_class->values[j].value_nick; j++) { if (!g_ascii_strcasecmp (capability_strings[i], flags_class->values[j].value_nick)) { capabilities |= flags_class->values[j].value; found = TRUE; break; } } if (!found) { inner_error = g_error_new ( MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS, "Couldn't match '%s' with a valid MMModemCapability value", capability_strings[i]); break; } } } if (inner_error) { g_propagate_error (error, inner_error); capabilities = MM_MODEM_CAPABILITY_NONE; } return capabilities; } MMModemMode mm_common_get_modes_from_string (const gchar *str, GError **error) { GError *inner_error = NULL; MMModemMode modes; g_auto(GStrv) mode_strings = NULL; g_autoptr(GFlagsClass) flags_class = NULL; modes = MM_MODEM_MODE_NONE; flags_class = G_FLAGS_CLASS (g_type_class_ref (MM_TYPE_MODEM_MODE)); mode_strings = g_strsplit (str, "|", -1); if (mode_strings) { guint i; for (i = 0; mode_strings[i]; i++) { guint j; gboolean found = FALSE; for (j = 0; flags_class->values[j].value_nick; j++) { if (!g_ascii_strcasecmp (mode_strings[i], flags_class->values[j].value_nick)) { modes |= flags_class->values[j].value; found = TRUE; break; } } if (!found) { inner_error = g_error_new ( MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS, "Couldn't match '%s' with a valid MMModemMode value", mode_strings[i]); break; } } } if (inner_error) { g_propagate_error (error, inner_error); modes = MM_MODEM_MODE_NONE; } return modes; } gboolean mm_common_get_bands_from_string (const gchar *str, MMModemBand **bands, guint *n_bands, GError **error) { GError *inner_error = NULL; GArray *array; g_auto(GStrv) band_strings = NULL; g_autoptr(GEnumClass) enum_class = NULL; array = g_array_new (FALSE, FALSE, sizeof (MMModemBand)); enum_class = G_ENUM_CLASS (g_type_class_ref (MM_TYPE_MODEM_BAND)); band_strings = g_strsplit (str, "|", -1); if (band_strings) { guint i; for (i = 0; band_strings[i]; i++) { guint j; gboolean found = FALSE; for (j = 0; enum_class->values[j].value_nick; j++) { if (!g_ascii_strcasecmp (band_strings[i], enum_class->values[j].value_nick)) { g_array_append_val (array, enum_class->values[j].value); found = TRUE; break; } } if (!found) { inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS, "Couldn't match '%s' with a valid MMModemBand value", band_strings[i]); break; } } } if (inner_error) { g_propagate_error (error, inner_error); g_array_free (array, TRUE); *n_bands = 0; *bands = NULL; return FALSE; } if (!array->len) { GEnumValue *value; value = g_enum_get_value (enum_class, MM_MODEM_BAND_UNKNOWN); g_array_append_val (array, value->value); } *n_bands = array->len; *bands = (MMModemBand *)g_array_free (array, FALSE); return TRUE; } gboolean mm_common_get_boolean_from_string (const gchar *value, GError **error) { if (!g_ascii_strcasecmp (value, "true") || g_str_equal (value, "1") || !g_ascii_strcasecmp (value, "yes")) return TRUE; if (!g_ascii_strcasecmp (value, "false") || g_str_equal (value, "0") || !g_ascii_strcasecmp (value, "no")) return FALSE; g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS, "Cannot get boolean from string '%s'", value); return FALSE; } MMModemCdmaRmProtocol mm_common_get_rm_protocol_from_string (const gchar *str, GError **error) { return _enum_from_string (MM_TYPE_MODEM_CDMA_RM_PROTOCOL, str, MM_MODEM_CDMA_RM_PROTOCOL_UNKNOWN, error); } MMBearerIpFamily mm_common_get_ip_type_from_string (const gchar *str, GError **error) { return _flags_from_string (MM_TYPE_BEARER_IP_FAMILY, str, MM_BEARER_IP_FAMILY_NONE, error); } MMBearerAllowedAuth mm_common_get_allowed_auth_from_string (const gchar *str, GError **error) { GError *inner_error = NULL; MMBearerAllowedAuth allowed_auth; g_auto(GStrv) strings = NULL; g_autoptr(GFlagsClass) flags_class = NULL; allowed_auth = MM_BEARER_ALLOWED_AUTH_UNKNOWN; flags_class = G_FLAGS_CLASS (g_type_class_ref (MM_TYPE_BEARER_ALLOWED_AUTH)); strings = g_strsplit (str, "|", -1); if (strings) { guint i; for (i = 0; strings[i]; i++) { guint j; gboolean found = FALSE; for (j = 0; flags_class->values[j].value_nick; j++) { if (!g_ascii_strcasecmp (strings[i], flags_class->values[j].value_nick)) { allowed_auth |= flags_class->values[j].value; found = TRUE; break; } } if (!found) { inner_error = g_error_new ( MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS, "Couldn't match '%s' with a valid MMBearerAllowedAuth value", strings[i]); break; } } } if (inner_error) { g_propagate_error (error, inner_error); allowed_auth = MM_BEARER_ALLOWED_AUTH_UNKNOWN; } return allowed_auth; } MMSmsStorage mm_common_get_sms_storage_from_string (const gchar *str, GError **error) { return _enum_from_string (MM_TYPE_SMS_STORAGE, str, MM_SMS_STORAGE_UNKNOWN, error); } MMSmsCdmaTeleserviceId mm_common_get_sms_cdma_teleservice_id_from_string (const gchar *str, GError **error) { return _enum_from_string (MM_TYPE_SMS_CDMA_TELESERVICE_ID, str, MM_SMS_CDMA_TELESERVICE_ID_UNKNOWN, error); } MMSmsCdmaServiceCategory mm_common_get_sms_cdma_service_category_from_string (const gchar *str, GError **error) { return _enum_from_string (MM_TYPE_SMS_CDMA_SERVICE_CATEGORY, str, MM_SMS_CDMA_SERVICE_CATEGORY_UNKNOWN, error); } MMCallDirection mm_common_get_call_direction_from_string (const gchar *str, GError **error) { return _enum_from_string (MM_TYPE_CALL_DIRECTION, str, MM_CALL_DIRECTION_UNKNOWN, error); } MMCallState mm_common_get_call_state_from_string (const gchar *str, GError **error) { return _enum_from_string (MM_TYPE_CALL_STATE, str, MM_CALL_STATE_UNKNOWN, error); } MMCallStateReason mm_common_get_call_state_reason_from_string (const gchar *str, GError **error) { return _enum_from_string (MM_TYPE_CALL_STATE_REASON, str, MM_CALL_STATE_REASON_UNKNOWN, error); } MMOmaFeature mm_common_get_oma_features_from_string (const gchar *str, GError **error) { GError *inner_error = NULL; MMOmaFeature features; g_auto(GStrv) feature_strings = NULL; g_autoptr(GFlagsClass) flags_class = NULL; features = MM_OMA_FEATURE_NONE; flags_class = G_FLAGS_CLASS (g_type_class_ref (MM_TYPE_OMA_FEATURE)); feature_strings = g_strsplit (str, "|", -1); if (feature_strings) { guint i; for (i = 0; feature_strings[i]; i++) { guint j; gboolean found = FALSE; for (j = 0; flags_class->values[j].value_nick; j++) { if (!g_ascii_strcasecmp (feature_strings[i], flags_class->values[j].value_nick)) { features |= flags_class->values[j].value; found = TRUE; break; } } if (!found) { inner_error = g_error_new ( MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS, "Couldn't match '%s' with a valid MMOmaFeature value", feature_strings[i]); break; } } } if (inner_error) { g_propagate_error (error, inner_error); features = MM_OMA_FEATURE_NONE; } return features; } MMOmaSessionType mm_common_get_oma_session_type_from_string (const gchar *str, GError **error) { return _enum_from_string (MM_TYPE_OMA_SESSION_TYPE, str, MM_OMA_SESSION_TYPE_UNKNOWN, error); } MMModem3gppEpsUeModeOperation mm_common_get_eps_ue_mode_operation_from_string (const gchar *str, GError **error) { return _enum_from_string (MM_TYPE_MODEM_3GPP_EPS_UE_MODE_OPERATION, str, MM_MODEM_3GPP_EPS_UE_MODE_OPERATION_UNKNOWN, error); } MMModemAccessTechnology mm_common_get_access_technology_from_string (const gchar *str, GError **error) { GError *inner_error = NULL; MMModemAccessTechnology technologies; g_auto(GStrv) technology_strings = NULL; g_autoptr(GFlagsClass) flags_class = NULL; technologies = MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN; flags_class = G_FLAGS_CLASS (g_type_class_ref (MM_TYPE_MODEM_ACCESS_TECHNOLOGY)); technology_strings = g_strsplit (str, "|", -1); if (technology_strings) { guint i; for (i = 0; technology_strings[i]; i++) { guint j; gboolean found = FALSE; for (j = 0; flags_class->values[j].value_nick; j++) { if (!g_ascii_strcasecmp (technology_strings[i], flags_class->values[j].value_nick)) { technologies |= flags_class->values[j].value; found = TRUE; break; } } if (!found) { inner_error = g_error_new ( MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS, "Couldn't match '%s' with a valid MMModemAccessTechnology value", technology_strings[i]); break; } } } if (inner_error) { g_propagate_error (error, inner_error); technologies = MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN; } return technologies; } MMBearerMultiplexSupport mm_common_get_multiplex_support_from_string (const gchar *str, GError **error) { return _enum_from_string (MM_TYPE_BEARER_MULTIPLEX_SUPPORT, str, MM_BEARER_MULTIPLEX_SUPPORT_UNKNOWN, error); } MMBearerApnType mm_common_get_apn_type_from_string (const gchar *str, GError **error) { return _flags_from_string (MM_TYPE_BEARER_APN_TYPE, str, MM_BEARER_APN_TYPE_NONE, error); } MMModem3gppFacility mm_common_get_3gpp_facility_from_string (const gchar *str, GError **error) { return _flags_from_string (MM_TYPE_MODEM_3GPP_FACILITY, str, MM_MODEM_3GPP_FACILITY_NONE, error); } MMModem3gppPacketServiceState mm_common_get_3gpp_packet_service_state_from_string (const gchar *str, GError **error) { return _enum_from_string (MM_TYPE_MODEM_3GPP_PACKET_SERVICE_STATE, str, MM_MODEM_3GPP_PACKET_SERVICE_STATE_UNKNOWN, error); } MMModem3gppMicoMode mm_common_get_3gpp_mico_mode_from_string (const gchar *str, GError **error) { return _enum_from_string (MM_TYPE_MODEM_3GPP_MICO_MODE, str, MM_MODEM_3GPP_MICO_MODE_UNKNOWN, error); } MMModem3gppDrxCycle mm_common_get_3gpp_drx_cycle_from_string (const gchar *str, GError **error) { return _enum_from_string (MM_TYPE_MODEM_3GPP_DRX_CYCLE, str, MM_MODEM_3GPP_DRX_CYCLE_UNKNOWN, error); } MMBearerAccessTypePreference mm_common_get_access_type_preference_from_string (const gchar *str, GError **error) { return _enum_from_string (MM_TYPE_BEARER_ACCESS_TYPE_PREFERENCE, str, MM_BEARER_ACCESS_TYPE_PREFERENCE_NONE, error); } MMBearerProfileSource mm_common_get_profile_source_from_string (const gchar *str, GError **error) { return _enum_from_string (MM_TYPE_BEARER_PROFILE_SOURCE, str, MM_BEARER_PROFILE_SOURCE_UNKNOWN, error); } /******************************************************************************/ /* MMModemPortInfo array management */ static void clear_modem_port_info (MMModemPortInfo *info) { g_free (info->name); } GArray * mm_common_ports_variant_to_garray (GVariant *variant) { GArray *array = NULL; if (variant) { guint i; guint n; n = g_variant_n_children (variant); if (n > 0) { array = g_array_sized_new (FALSE, FALSE, sizeof (MMModemPortInfo), n); g_array_set_clear_func (array, (GDestroyNotify) clear_modem_port_info); for (i = 0; i < n; i++) { MMModemPortInfo info; g_variant_get_child (variant, i, "(su)", &info.name, &info.type); g_array_append_val (array, info); } } } return array; } GVariant * mm_common_ports_array_to_variant (const MMModemPortInfo *ports, guint n_ports) { GVariantBuilder builder; guint i; g_variant_builder_init (&builder, G_VARIANT_TYPE ("a(su)")); for (i = 0; i < n_ports; i++) { GVariant *tuple[2]; tuple[0] = g_variant_new_string (ports[i].name); tuple[1] = g_variant_new_uint32 ((guint32)ports[i].type); g_variant_builder_add_value (&builder, g_variant_new_tuple (tuple, 2)); } return g_variant_builder_end (&builder); } GVariant * mm_common_ports_garray_to_variant (GArray *array) { if (array) return mm_common_ports_array_to_variant ((const MMModemPortInfo *)array->data, array->len); return mm_common_ports_array_to_variant (NULL, 0); } gboolean mm_common_ports_garray_to_array (GArray *array, MMModemPortInfo **ports, guint *n_ports) { if (!array) return FALSE; *ports = NULL; *n_ports = array->len; if (array->len > 0) { guint i; *ports = g_malloc (sizeof (MMModemPortInfo) * array->len); /* Deep-copy the array */ for (i = 0; i < array->len; i++) { MMModemPortInfo *src; src = &g_array_index (array, MMModemPortInfo, i); (*ports)[i].name = g_strdup (src->name); (*ports)[i].type = src->type; } } return TRUE; } /******************************************************************************/ /* MMSmsStorage array management */ GArray * mm_common_sms_storages_variant_to_garray (GVariant *variant) { GArray *array = NULL; if (variant) { GVariantIter iter; guint n; g_variant_iter_init (&iter, variant); n = g_variant_iter_n_children (&iter); if (n > 0) { guint32 storage; array = g_array_sized_new (FALSE, FALSE, sizeof (MMSmsStorage), n); while (g_variant_iter_loop (&iter, "u", &storage)) g_array_append_val (array, storage); } } return array; } GVariant * mm_common_sms_storages_array_to_variant (const MMSmsStorage *storages, guint n_storages) { GVariantBuilder builder; guint i; g_variant_builder_init (&builder, G_VARIANT_TYPE ("au")); for (i = 0; i < n_storages; i++) g_variant_builder_add_value (&builder, g_variant_new_uint32 ((guint32)storages[i])); return g_variant_builder_end (&builder); } GVariant * mm_common_sms_storages_garray_to_variant (GArray *array) { if (array) return mm_common_sms_storages_array_to_variant ((const MMSmsStorage *)array->data, array->len); return mm_common_sms_storages_array_to_variant (NULL, 0); } /******************************************************************************/ /* MMModemCapability array management */ GArray * mm_common_capability_combinations_variant_to_garray (GVariant *variant) { GArray *array = NULL; if (variant) { GVariantIter iter; guint n; g_variant_iter_init (&iter, variant); n = g_variant_iter_n_children (&iter); if (n > 0) { guint32 capability; array = g_array_sized_new (FALSE, FALSE, sizeof (MMModemCapability), n); while (g_variant_iter_loop (&iter, "u", &capability)) g_array_append_val (array, capability); } } /* If nothing set, fallback to default */ if (!array) { guint32 capability = MM_MODEM_CAPABILITY_NONE; array = g_array_sized_new (FALSE, FALSE, sizeof (MMModemCapability), 1); g_array_append_val (array, capability); } return array; } GVariant * mm_common_capability_combinations_array_to_variant (const MMModemCapability *capabilities, guint n_capabilities) { GVariantBuilder builder; g_variant_builder_init (&builder, G_VARIANT_TYPE ("au")); if (n_capabilities > 0) { guint i; for (i = 0; i < n_capabilities; i++) g_variant_builder_add_value (&builder, g_variant_new_uint32 ((guint32)capabilities[i])); } else g_variant_builder_add_value (&builder, g_variant_new_uint32 (MM_MODEM_CAPABILITY_NONE)); return g_variant_builder_end (&builder); } GVariant * mm_common_capability_combinations_garray_to_variant (GArray *array) { if (array) return mm_common_capability_combinations_array_to_variant ((const MMModemCapability *)array->data, array->len); return mm_common_capability_combinations_array_to_variant (NULL, 0); } GVariant * mm_common_build_capability_combinations_none (void) { GVariantBuilder builder; g_variant_builder_init (&builder, G_VARIANT_TYPE ("au")); g_variant_builder_add_value (&builder, g_variant_new_uint32 (MM_MODEM_CAPABILITY_NONE)); return g_variant_builder_end (&builder); } /******************************************************************************/ /* MMModemModeCombination array management */ GArray * mm_common_mode_combinations_variant_to_garray (GVariant *variant) { GArray *array = NULL; if (variant) { GVariantIter iter; guint n; g_variant_iter_init (&iter, variant); n = g_variant_iter_n_children (&iter); if (n > 0) { MMModemModeCombination mode; array = g_array_sized_new (FALSE, FALSE, sizeof (MMModemModeCombination), n); while (g_variant_iter_loop (&iter, "(uu)", &mode.allowed, &mode.preferred)) g_array_append_val (array, mode); } } /* If nothing set, fallback to default */ if (!array) { MMModemModeCombination default_mode; default_mode.allowed = MM_MODEM_MODE_ANY; default_mode.preferred = MM_MODEM_MODE_NONE; array = g_array_sized_new (FALSE, FALSE, sizeof (MMModemModeCombination), 1); g_array_append_val (array, default_mode); } return array; } GVariant * mm_common_mode_combinations_array_to_variant (const MMModemModeCombination *modes, guint n_modes) { if (n_modes > 0) { GVariantBuilder builder; guint i; g_variant_builder_init (&builder, G_VARIANT_TYPE ("a(uu)")); for (i = 0; i < n_modes; i++) g_variant_builder_add_value (&builder, g_variant_new ("(uu)", ((guint32)modes[i].allowed), ((guint32)modes[i].preferred))); return g_variant_builder_end (&builder); } return mm_common_build_mode_combinations_default (); } GVariant * mm_common_mode_combinations_garray_to_variant (GArray *array) { if (array) return mm_common_mode_combinations_array_to_variant ((const MMModemModeCombination *)array->data, array->len); return mm_common_mode_combinations_array_to_variant (NULL, 0); } GVariant * mm_common_build_mode_combinations_default (void) { GVariantBuilder builder; g_variant_builder_init (&builder, G_VARIANT_TYPE ("a(uu)")); g_variant_builder_add_value (&builder, g_variant_new ("(uu)", MM_MODEM_MODE_ANY, MM_MODEM_MODE_NONE)); return g_variant_builder_end (&builder); } /******************************************************************************/ /* MMModemBand array management */ GArray * mm_common_bands_variant_to_garray (GVariant *variant) { GArray *array = NULL; if (variant) { GVariantIter iter; guint n; g_variant_iter_init (&iter, variant); n = g_variant_iter_n_children (&iter); if (n > 0) { guint32 band; array = g_array_sized_new (FALSE, FALSE, sizeof (MMModemBand), n); while (g_variant_iter_loop (&iter, "u", &band)) g_array_append_val (array, band); } } /* If nothing set, fallback to default */ if (!array) { guint32 band = MM_MODEM_BAND_UNKNOWN; array = g_array_sized_new (FALSE, FALSE, sizeof (MMModemBand), 1); g_array_append_val (array, band); } return array; } GVariant * mm_common_bands_array_to_variant (const MMModemBand *bands, guint n_bands) { if (n_bands > 0) { GVariantBuilder builder; guint i; g_variant_builder_init (&builder, G_VARIANT_TYPE ("au")); for (i = 0; i < n_bands; i++) g_variant_builder_add_value (&builder, g_variant_new_uint32 ((guint32)bands[i])); return g_variant_builder_end (&builder); } return mm_common_build_bands_unknown (); } GVariant * mm_common_bands_garray_to_variant (GArray *array) { if (array) return mm_common_bands_array_to_variant ((const MMModemBand *)array->data, array->len); return mm_common_bands_array_to_variant (NULL, 0); } GVariant * mm_common_build_bands_unknown (void) { GVariantBuilder builder; g_variant_builder_init (&builder, G_VARIANT_TYPE ("au")); g_variant_builder_add_value (&builder, g_variant_new_uint32 (MM_MODEM_BAND_UNKNOWN)); return g_variant_builder_end (&builder); } GVariant * mm_common_build_bands_any (void) { GVariantBuilder builder; g_variant_builder_init (&builder, G_VARIANT_TYPE ("au")); g_variant_builder_add_value (&builder, g_variant_new_uint32 (MM_MODEM_BAND_ANY)); return g_variant_builder_end (&builder); } static guint cmp_band (MMModemBand *a, MMModemBand *b) { return (*a - *b); } gboolean mm_common_bands_garray_cmp (GArray *a, GArray *b) { GArray *dup_a; GArray *dup_b; guint i; gboolean different; if (a->len != b->len) return FALSE; dup_a = g_array_sized_new (FALSE, FALSE, sizeof (MMModemBand), a->len); g_array_append_vals (dup_a, a->data, a->len); dup_b = g_array_sized_new (FALSE, FALSE, sizeof (MMModemBand), b->len); g_array_append_vals (dup_b, b->data, b->len); g_array_sort (dup_a, (GCompareFunc)cmp_band); g_array_sort (dup_b, (GCompareFunc)cmp_band); different = FALSE; for (i = 0; !different && i < a->len; i++) { if (g_array_index (dup_a, MMModemBand, i) != g_array_index (dup_b, MMModemBand, i)) different = TRUE; } g_array_unref (dup_a); g_array_unref (dup_b); return !different; } gboolean mm_common_bands_garray_lookup (GArray *array, MMModemBand value) { guint i; for (i = 0; i < array->len; i++) { if (value == g_array_index (array, MMModemBand, i)) return TRUE; } return FALSE; } void mm_common_bands_garray_sort (GArray *array) { g_array_sort (array, (GCompareFunc) cmp_band); } gboolean mm_common_band_is_gsm (MMModemBand band) { return ((band >= MM_MODEM_BAND_EGSM && band <= MM_MODEM_BAND_G850) || (band >= MM_MODEM_BAND_G450 && band <= MM_MODEM_BAND_G810)); } gboolean mm_common_band_is_utran (MMModemBand band) { return ((band >= MM_MODEM_BAND_UTRAN_1 && band <= MM_MODEM_BAND_UTRAN_7) || (band >= MM_MODEM_BAND_UTRAN_10 && band <= MM_MODEM_BAND_UTRAN_32)); } gboolean mm_common_band_is_eutran (MMModemBand band) { return (band >= MM_MODEM_BAND_EUTRAN_1 && band <= MM_MODEM_BAND_EUTRAN_71); } gboolean mm_common_band_is_cdma (MMModemBand band) { return (band >= MM_MODEM_BAND_CDMA_BC0 && band <= MM_MODEM_BAND_CDMA_BC19); } /******************************************************************************/ /* MMOmaPendingNetworkInitiatedSession array management */ GArray * mm_common_oma_pending_network_initiated_sessions_variant_to_garray (GVariant *variant) { GArray *array = NULL; if (variant) { GVariantIter iter; guint n; g_variant_iter_init (&iter, variant); n = g_variant_iter_n_children (&iter); if (n > 0) { MMOmaPendingNetworkInitiatedSession session; array = g_array_sized_new (FALSE, FALSE, sizeof (MMOmaPendingNetworkInitiatedSession), n); while (g_variant_iter_loop (&iter, "(uu)", &session.session_type, &session.session_id)) g_array_append_val (array, session); } } /* If nothing set, fallback to empty */ if (!array) array = g_array_new (FALSE, FALSE, sizeof (MMOmaPendingNetworkInitiatedSession)); return array; } GVariant * mm_common_oma_pending_network_initiated_sessions_array_to_variant (const MMOmaPendingNetworkInitiatedSession *sessions, guint n_sessions) { if (n_sessions > 0) { GVariantBuilder builder; guint i; g_variant_builder_init (&builder, G_VARIANT_TYPE ("a(uu)")); for (i = 0; i < n_sessions; i++) g_variant_builder_add_value (&builder, g_variant_new ("(uu)", ((guint32)sessions[i].session_type), ((guint32)sessions[i].session_id))); return g_variant_builder_end (&builder); } return mm_common_build_oma_pending_network_initiated_sessions_default (); } GVariant * mm_common_oma_pending_network_initiated_sessions_garray_to_variant (GArray *array) { if (array) return mm_common_oma_pending_network_initiated_sessions_array_to_variant ((const MMOmaPendingNetworkInitiatedSession *)array->data, array->len); return mm_common_oma_pending_network_initiated_sessions_array_to_variant (NULL, 0); } GVariant * mm_common_build_oma_pending_network_initiated_sessions_default (void) { GVariantBuilder builder; g_variant_builder_init (&builder, G_VARIANT_TYPE ("a(uu)")); return g_variant_builder_end (&builder); } /******************************************************************************/ /* Common parsers */ /* Expecting input as: * key1=string,key2=true,key3=false... * Strings may also be passed enclosed between double or single quotes, like: * key1="this is a string", key2='and so is this' */ gboolean mm_common_parse_key_value_string (const gchar *str, GError **error, MMParseKeyValueForeachFn callback, gpointer user_data) { GError *inner_error = NULL; gchar *dup, *p, *key, *key_end, *value, *value_end, quote; g_return_val_if_fail (callback != NULL, FALSE); g_return_val_if_fail (str != NULL, FALSE); /* Allow empty strings, we'll just return with success */ while (g_ascii_isspace (*str)) str++; if (!str[0]) return TRUE; dup = g_strdup (str); p = dup; while (TRUE) { gboolean keep_iteration = FALSE; /* Skip leading spaces */ while (g_ascii_isspace (*p)) p++; /* Key start */ key = p; if (!g_ascii_isalnum (*key)) { inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Key must start with alpha/num, starts with '%c'", *key); break; } /* Key end */ while (g_ascii_isalnum (*p) || (*p == '-') || (*p == '_')) p++; key_end = p; if (key_end == key) { inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't find a proper key"); break; } /* Skip whitespaces, if any */ while (g_ascii_isspace (*p)) p++; /* Equal sign must be here */ if (*p != '=') { inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't find equal sign separator"); break; } /* Skip the equal */ p++; /* Skip whitespaces, if any */ while (g_ascii_isspace (*p)) p++; /* Do we have a quote-enclosed string? */ if (*p == '\"' || *p == '\'') { quote = *p; /* Skip the quote */ p++; /* Value start */ value = p; /* Find the closing quote */ p = strchr (p, quote); if (!p) { inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Unmatched quotes in string value"); break; } /* Value end */ value_end = p; /* Skip the quote */ p++; } else { /* Value start */ value = p; /* Value end */ while ((*p != ',') && (*p != '\0') && !g_ascii_isspace (*p)) p++; value_end = p; } /* Note that we allow value == value_end here */ /* Skip whitespaces, if any */ while (g_ascii_isspace (*p)) p++; /* If a comma is found, we should keep the iteration */ if (*p == ',') { /* skip the comma */ p++; keep_iteration = TRUE; } /* Got key and value, prepare them and run the callback */ *value_end = '\0'; *key_end = '\0'; if (!callback (key, value, user_data)) { /* We were told to abort */ break; } if (keep_iteration) continue; /* Check if no more key/value pairs expected */ if (*p == '\0') break; inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Unexpected content (%s) after value", p); break; } g_free (dup); if (inner_error) { g_propagate_error (error, inner_error); return FALSE; } return TRUE; } /*****************************************************************************/ gboolean mm_get_int_from_str (const gchar *str, gint *out) { glong num; guint i; guint eol = 0; if (!str) return FALSE; /* ignore all leading whitespaces */ while (str[0] == ' ') str++; if (!str[0]) return FALSE; for (i = 0; str[i]; i++) { if (str[i] != '+' && str[i] != '-' && !g_ascii_isdigit (str[i])) { /* ignore \r\n at the end of the string */ if ((str[i] == '\r') || (str[i] == '\n')) { eol++; continue; } return FALSE; } /* if eol found before a valid char, the string is not parseable */ if (eol) return FALSE; } /* if all characters were eol, the string is not parseable */ if (eol == i) return FALSE; errno = 0; num = strtol (str, NULL, 10); if (!errno && num >= G_MININT && num <= G_MAXINT) { *out = (gint)num; return TRUE; } return FALSE; } gboolean mm_get_int_from_match_info (GMatchInfo *match_info, guint32 match_index, gint *out) { g_autofree gchar *s = NULL; s = mm_get_string_unquoted_from_match_info (match_info, match_index); return (s ? mm_get_int_from_str (s, out) : FALSE); } gboolean mm_get_uint_from_str (const gchar *str, guint *out) { guint64 num; if (!mm_get_u64_from_str (str, &num) || num > G_MAXUINT) return FALSE; *out = (guint)num; return TRUE; } gboolean mm_get_u64_from_str (const gchar *str, guint64 *out) { guint64 num; guint eol = 0; if (!str) return FALSE; /* ignore all leading whitespaces */ while (str[0] == ' ') str++; if (!str[0]) return FALSE; for (num = 0; str[num]; num++) { if (!g_ascii_isdigit (str[num])) { /* ignore \r\n at the end of the string */ if ((str[num] == '\r') || (str[num] == '\n')) { eol++; continue; } return FALSE; } /* if eol found before a valid char, the string is not parseable */ if (eol) return FALSE; } /* if all characters were eol, the string is not parseable */ if (eol == num) return FALSE; errno = 0; num = (guint64) strtoull (str, NULL, 10); if (!errno) { *out = num; return TRUE; } return FALSE; } gboolean mm_get_uint_from_hex_str (const gchar *str, guint *out) { guint64 num; if (!mm_get_u64_from_hex_str (str, &num) || num > G_MAXUINT) return FALSE; *out = (guint)num; return TRUE; } gboolean mm_get_u64_from_hex_str (const gchar *str, guint64 *out) { guint64 num; guint eol = 0; if (!str) return FALSE; /* ignore all leading whitespaces */ while (str[0] == ' ') str++; if (g_str_has_prefix (str, "0x")) str = &str[2]; if (!str[0]) return FALSE; for (num = 0; str[num]; num++) { if (!g_ascii_isxdigit (str[num])) { /* ignore \r\n at the end of the string */ if ((str[num] == '\r') || (str[num] == '\n')) { eol++; continue; } return FALSE; } /* if eol found before a valid char, the string is not parseable */ if (eol) return FALSE; } /* if all characters were eol, the string is not parseable */ if (eol == num) return FALSE; errno = 0; num = (guint64) strtoull (str, NULL, 16); if (!errno) { *out = num; return TRUE; } return FALSE; } gboolean mm_get_uint_from_match_info (GMatchInfo *match_info, guint32 match_index, guint *out) { guint64 num; if (!mm_get_u64_from_match_info (match_info, match_index, &num) || num > G_MAXUINT) return FALSE; *out = (guint)num; return TRUE; } gboolean mm_get_u64_from_match_info (GMatchInfo *match_info, guint32 match_index, guint64 *out) { g_autofree gchar *s = NULL; s = mm_get_string_unquoted_from_match_info (match_info, match_index); return (s ? mm_get_u64_from_str (s, out) : FALSE); } gboolean mm_get_uint_from_hex_match_info (GMatchInfo *match_info, guint32 match_index, guint *out) { guint64 num; if (!mm_get_u64_from_hex_match_info (match_info, match_index, &num) || num > G_MAXUINT) return FALSE; *out = (guint)num; return TRUE; } gboolean mm_get_u64_from_hex_match_info (GMatchInfo *match_info, guint32 match_index, guint64 *out) { g_autofree gchar *s = NULL; s = mm_get_string_unquoted_from_match_info (match_info, match_index); return (s ? mm_get_u64_from_hex_str (s, out) : FALSE); } gboolean mm_get_double_from_str (const gchar *str, gdouble *out) { gdouble num; guint i; guint eol = 0; if (!str || !str[0]) return FALSE; for (i = 0; str[i]; i++) { /* we don't really expect numbers in scientific notation, so * don't bother looking for exponents and such */ if ((str[i] != '-') && (str[i] != '.') && !g_ascii_isdigit (str[i])) { /* ignore \r\n at the end of the string */ if ((str[i] == '\r') || (str[i] == '\n')) { eol++; continue; } return FALSE; } /* if eol found before a valid char, the string is not parseable */ if (eol) return FALSE; } /* if all characters were eol, the string is not parseable */ if (eol == i) return FALSE; errno = 0; num = g_ascii_strtod (str, NULL); if (!errno) { *out = num; return TRUE; } return FALSE; } gboolean mm_get_double_from_match_info (GMatchInfo *match_info, guint32 match_index, gdouble *out) { g_autofree gchar *s = NULL; s = mm_get_string_unquoted_from_match_info (match_info, match_index); return (s ? mm_get_double_from_str (s, out) : FALSE); } gchar * mm_get_string_unquoted_from_match_info (GMatchInfo *match_info, guint32 match_index) { gchar *str; gsize len; str = g_match_info_fetch (match_info, match_index); if (!str) return NULL; len = strlen (str); /* Unquote the item if needed */ if ((len >= 2) && (str[0] == '"') && (str[len - 1] == '"')) { str[0] = ' '; str[len - 1] = ' '; str = g_strstrip (str); } if (!str[0]) { g_free (str); return NULL; } return str; } /* * The following implementation is taken from glib g_date_time_format_iso8601 code * https://gitlab.gnome.org/GNOME/glib/-/blob/main/glib/gdatetime.c#L3490 */ static gchar * date_time_format_iso8601 (GDateTime *dt) { #if GLIB_CHECK_VERSION (2, 62, 0) G_GNUC_BEGIN_IGNORE_DEPRECATIONS return g_date_time_format_iso8601 (dt); G_GNUC_END_IGNORE_DEPRECATIONS #else GString *outstr = NULL; g_autofree gchar *main_date = NULL; gint64 offset = 0; main_date = g_date_time_format (dt, "%Y-%m-%dT%H:%M:%S"); outstr = g_string_new (main_date); /* Timezone. Format it as `%:::z` unless the offset is zero, in which case * we can simply use `Z`. */ offset = g_date_time_get_utc_offset (dt); if (offset == 0) { g_string_append_c (outstr, 'Z'); } else { g_autofree gchar *time_zone = NULL; time_zone = g_date_time_format (dt, "%:::z"); g_string_append (outstr, time_zone); } return g_string_free (outstr, FALSE); #endif } gchar * mm_new_iso8601_time_from_unix_time (guint64 timestamp, GError **error) { g_autoptr(GDateTime) dt = NULL; dt = g_date_time_new_from_unix_utc ((gint64)timestamp); if (!dt) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS, "Invalid unix time: %" G_GUINT64_FORMAT, timestamp); return NULL; } return date_time_format_iso8601 (dt); } gchar * mm_new_iso8601_time (guint year, guint month, guint day, guint hour, guint minute, guint second, gboolean have_offset, gint offset_minutes, GError **error) { g_autoptr(GDateTime) dt = NULL; if (have_offset) { g_autoptr(GTimeZone) tz = NULL; #if GLIB_CHECK_VERSION (2, 58, 0) G_GNUC_BEGIN_IGNORE_DEPRECATIONS tz = g_time_zone_new_offset (offset_minutes * 60); G_GNUC_END_IGNORE_DEPRECATIONS #else g_autofree gchar *identifier = NULL; identifier = g_strdup_printf ("%c%02u:%02u:00", (offset_minutes >= 0) ? '+' : '-', ABS (offset_minutes) / 60, ABS (offset_minutes) % 60); tz = g_time_zone_new (identifier); #endif dt = g_date_time_new (tz, year, month, day, hour, minute, second); } else dt = g_date_time_new_utc (year, month, day, hour, minute, second); if (!dt) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS, "Invalid date: year: %u, month: %u, day: %u, hour: %u, minute: %u, second: %u", year, month, day, hour, minute, second); return NULL; } return date_time_format_iso8601 (dt); } /*****************************************************************************/ /* From hostap, Copyright (c) 2002-2005, Jouni Malinen */ static gint hex2num (gchar 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; } gint mm_utils_hex2byte (const gchar *hex) { gint a, b; a = hex2num (*hex++); if (a < 0) return -1; b = hex2num (*hex++); if (b < 0) return -1; return (a << 4) | b; } guint8 * mm_utils_hexstr2bin (const gchar *hex, gssize len, gsize *out_len, GError **error) { const gchar *ipos = hex; g_autofree guint8 *buf = NULL; gssize i; gint a; guint8 *opos; if (len < 0) len = strlen (hex); if (len == 0) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS, "Hex conversion failed: empty string"); return NULL; } /* Length must be a multiple of 2 */ if ((len % 2) != 0) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS, "Hex conversion failed: invalid input length"); return NULL; } opos = buf = g_malloc0 (len / 2); for (i = 0; i < len; i += 2) { a = mm_utils_hex2byte (ipos); if (a < 0) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS, "Hex byte conversion from '%c%c' failed", ipos[0], ipos[1]); return NULL; } *opos++ = (guint8)a; ipos += 2; } *out_len = len / 2; return g_steal_pointer (&buf); } /* End from hostap */ gboolean mm_utils_ishexstr (const gchar *hex) { gsize len; gsize i; /* Empty string or length not multiple of 2? */ len = strlen (hex); if (len == 0 || (len % 2) != 0) return FALSE; for (i = 0; i < len; i++) { /* Non-hex char? */ if (hex[i] >= '0' && hex[i] <= '9') continue; if (hex[i] >= 'a' && hex[i] <= 'f') continue; if (hex[i] >= 'A' && hex[i] <= 'F') continue; return FALSE; } return TRUE; } gchar * mm_utils_bin2hexstr (const guint8 *bin, gsize len) { GString *ret; gsize i; g_return_val_if_fail (bin != NULL, NULL); ret = g_string_sized_new (len * 2 + 1); for (i = 0; i < len; i++) g_string_append_printf (ret, "%.2X", bin[i]); return g_string_free (ret, FALSE); } gboolean mm_utils_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; } /*****************************************************************************/ gboolean mm_is_string_mccmnc (const gchar *str) { gsize len; guint i; if (!str) return FALSE; len = strlen (str); if (len < 5 || len > 6) return FALSE; for (i = 0; i < len; i++) if (str[i] < '0' || str[i] > '9') return FALSE; return TRUE; } /*****************************************************************************/ const gchar * mm_sms_delivery_state_get_string_extended (guint delivery_state) { if (delivery_state > 0x02 && delivery_state < 0x20) { if (delivery_state < 0x10) return "completed-reason-reserved"; else return "completed-sc-specific-reason"; } if (delivery_state > 0x25 && delivery_state < 0x40) { if (delivery_state < 0x30) return "temporary-error-reason-reserved"; else return "temporary-error-sc-specific-reason"; } if (delivery_state > 0x49 && delivery_state < 0x60) { if (delivery_state < 0x50) return "error-reason-reserved"; else return "error-sc-specific-reason"; } if (delivery_state > 0x65 && delivery_state < 0x80) { if (delivery_state < 0x70) return "temporary-fatal-error-reason-reserved"; else return "temporary-fatal-error-sc-specific-reason"; } if (delivery_state >= 0x80 && delivery_state < 0x100) return "unknown-reason-reserved"; if (delivery_state >= 0x100) return "unknown"; /* Otherwise, use the MMSmsDeliveryState enum as we can match the known * value */ return mm_sms_delivery_state_get_string ((MMSmsDeliveryState)delivery_state); } /*****************************************************************************/ const gchar * mm_common_str_boolean (gboolean value) { return value ? "yes" : "no"; } const gchar * mm_common_str_personal_info (const gchar *str, gboolean show_personal_info) { static const gchar *hidden_personal_info = "###"; return show_personal_info ? str : hidden_personal_info; } void mm_common_str_array_human_keys (GPtrArray *array) { guint i; /* Transforms from: * strings-as-keys: value... * Into: * strings as keys: value... */ for (i = 0; i < array->len; i++) { gchar *str; guint j; str = g_ptr_array_index (array, i); for (j = 0; str[j] && str[j] != ':'; j++) { if (str[j] == '-') str[j] = ' '; } } } /*****************************************************************************/ /* DBus error handling */ gboolean mm_common_register_errors (void) { static volatile guint32 aux = 0; if (G_LIKELY (aux)) return FALSE; /* Register all known own errors */ aux |= MM_CORE_ERROR; aux |= MM_MOBILE_EQUIPMENT_ERROR; aux |= MM_CONNECTION_ERROR; aux |= MM_SERIAL_ERROR; aux |= MM_MESSAGE_ERROR; aux |= MM_CDMA_ACTIVATION_ERROR; aux |= MM_CARRIER_LOCK_ERROR; return TRUE; } GError * mm_common_error_from_tuple (GVariant *tuple, GError **error) { g_autoptr(GError) dbus_error = NULL; g_autofree gchar *error_name = NULL; g_autofree gchar *error_message = NULL; mm_common_register_errors (); if (!g_variant_is_of_type (tuple, G_VARIANT_TYPE ("(ss)"))) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS, "Cannot create error from tuple: " "invalid variant type received"); return NULL; } g_variant_get (tuple, "(ss)", &error_name, &error_message); if (!error_name || !error_name[0]) return NULL; /* We convert the error name into a proper GError (domain+code), but we * don't attempt to give the error message to new_for_dbus_error() as that * would generate a string we don't want (e.g. instead of just "Unknown * Error" we would get "GDBus.Error:org.freedesktop.ModemManager1.Error.MobileEquipment.Unknown: Unknown error" */ dbus_error = g_dbus_error_new_for_dbus_error (error_name, ""); /* And now we build a new GError with same domain+code but with the received * error message */ return g_error_new (dbus_error->domain, dbus_error->code, "%s", error_message); } GVariant * mm_common_error_to_tuple (const GError *error) { g_autofree gchar *error_name = NULL; GVariant *tuple[2]; mm_common_register_errors (); error_name = g_dbus_error_encode_gerror (error); tuple[0] = g_variant_new_string (error_name); tuple[1] = g_variant_new_string (error->message); return g_variant_ref_sink (g_variant_new_tuple (tuple, 2)); } ModemManager-1.23.4-dev/libmm-glib/mm-common-helpers.h000066400000000000000000000405011456466623000224500ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * libmm-glib -- Access modem status & information from glib applications * * 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 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 Street, Fifth Floor, * Boston, MA 02110-1301 USA. * * Copyright (C) 2010 - 2012 Red Hat, Inc. * Copyright (C) 2011 - 2012 Google, Inc. * Copyright (C) 2012 Aleksander Morgado */ #include #include #include "mm-helper-types.h" #if !defined (__LIBMM_GLIB_H_INSIDE__) && !defined (LIBMM_GLIB_COMPILATION) #error "Only can be included directly." #endif #ifndef MM_COMMON_HELPERS_H #define MM_COMMON_HELPERS_H /******************************************************************************/ /* Enums/flags to string builders */ gchar *mm_common_build_capabilities_string (const MMModemCapability *capabilities, guint n_capabilities); gchar *mm_common_build_bands_string (const MMModemBand *bands, guint n_bands); gchar *mm_common_build_ports_string (const MMModemPortInfo *ports, guint n_ports); gchar *mm_common_build_sms_storages_string (const MMSmsStorage *storages, guint n_storages); gchar *mm_common_build_mode_combinations_string (const MMModemModeCombination *modes, guint n_modes); /******************************************************************************/ /* String to enums/flags parsers */ MMModemCapability mm_common_get_capabilities_from_string (const gchar *str, GError **error); MMModemMode mm_common_get_modes_from_string (const gchar *str, GError **error); gboolean mm_common_get_bands_from_string (const gchar *str, MMModemBand **bands, guint *n_bands, GError **error); gboolean mm_common_get_boolean_from_string (const gchar *value, GError **error); MMModemCdmaRmProtocol mm_common_get_rm_protocol_from_string (const gchar *str, GError **error); MMBearerIpFamily mm_common_get_ip_type_from_string (const gchar *str, GError **error); MMBearerAllowedAuth mm_common_get_allowed_auth_from_string (const gchar *str, GError **error); MMSmsStorage mm_common_get_sms_storage_from_string (const gchar *str, GError **error); MMSmsCdmaTeleserviceId mm_common_get_sms_cdma_teleservice_id_from_string (const gchar *str, GError **error); MMSmsCdmaServiceCategory mm_common_get_sms_cdma_service_category_from_string (const gchar *str, GError **error); MMCallDirection mm_common_get_call_direction_from_string (const gchar *str, GError **error); MMCallState mm_common_get_call_state_from_string (const gchar *str, GError **error); MMCallStateReason mm_common_get_call_state_reason_from_string (const gchar *str, GError **error); MMOmaFeature mm_common_get_oma_features_from_string (const gchar *str, GError **error); MMOmaSessionType mm_common_get_oma_session_type_from_string (const gchar *str, GError **error); MMModem3gppEpsUeModeOperation mm_common_get_eps_ue_mode_operation_from_string (const gchar *str, GError **error); MMModemAccessTechnology mm_common_get_access_technology_from_string (const gchar *str, GError **error); MMBearerMultiplexSupport mm_common_get_multiplex_support_from_string (const gchar *str, GError **error); MMBearerApnType mm_common_get_apn_type_from_string (const gchar *str, GError **error); MMModem3gppFacility mm_common_get_3gpp_facility_from_string (const gchar *str, GError **error); MMModem3gppPacketServiceState mm_common_get_3gpp_packet_service_state_from_string (const gchar *str, GError **error); MMModem3gppMicoMode mm_common_get_3gpp_mico_mode_from_string (const gchar *str, GError **error); MMModem3gppDrxCycle mm_common_get_3gpp_drx_cycle_from_string (const gchar *str, GError **error); MMBearerAccessTypePreference mm_common_get_access_type_preference_from_string (const gchar *str, GError **error); MMBearerProfileSource mm_common_get_profile_source_from_string (const gchar *str, GError **error); /******************************************************************************/ /* MMModemPortInfo array management */ GArray *mm_common_ports_variant_to_garray (GVariant *variant); GVariant *mm_common_ports_array_to_variant (const MMModemPortInfo *ports, guint n_ports); GVariant *mm_common_ports_garray_to_variant (GArray *array); gboolean mm_common_ports_garray_to_array (GArray *array, MMModemPortInfo **ports, guint *n_ports); /* MMSmsStorage array management */ GArray *mm_common_sms_storages_variant_to_garray (GVariant *variant); GVariant *mm_common_sms_storages_array_to_variant (const MMSmsStorage *storages, guint n_storages); GVariant *mm_common_sms_storages_garray_to_variant (GArray *array); /* MMModemCapability array management */ GArray *mm_common_capability_combinations_variant_to_garray (GVariant *variant); GVariant *mm_common_capability_combinations_array_to_variant (const MMModemCapability *capabilities, guint n_capabilities); GVariant *mm_common_capability_combinations_garray_to_variant (GArray *array); GVariant *mm_common_build_capability_combinations_none (void); /* MMModemModeCombination array management */ GArray *mm_common_mode_combinations_variant_to_garray (GVariant *variant); GVariant *mm_common_mode_combinations_array_to_variant (const MMModemModeCombination *modes, guint n_modes); GVariant *mm_common_mode_combinations_garray_to_variant (GArray *array); GVariant *mm_common_build_mode_combinations_default (void); /* MMModemBand array management */ GArray *mm_common_bands_variant_to_garray (GVariant *variant); GVariant *mm_common_bands_array_to_variant (const MMModemBand *bands, guint n_bands); GVariant *mm_common_bands_garray_to_variant (GArray *array); GVariant *mm_common_build_bands_any (void); GVariant *mm_common_build_bands_unknown (void); gboolean mm_common_bands_garray_cmp (GArray *a, GArray *b); void mm_common_bands_garray_sort (GArray *array); gboolean mm_common_bands_garray_lookup (GArray *array, MMModemBand value); gboolean mm_common_band_is_gsm (MMModemBand band); gboolean mm_common_band_is_utran (MMModemBand band); gboolean mm_common_band_is_eutran (MMModemBand band); gboolean mm_common_band_is_cdma (MMModemBand band); /* MMOmaPendingNetworkInitiatedSession array management */ GArray *mm_common_oma_pending_network_initiated_sessions_variant_to_garray (GVariant *variant); GVariant *mm_common_oma_pending_network_initiated_sessions_array_to_variant (const MMOmaPendingNetworkInitiatedSession *modes, guint n_modes); GVariant *mm_common_oma_pending_network_initiated_sessions_garray_to_variant (GArray *array); GVariant *mm_common_build_oma_pending_network_initiated_sessions_default (void); /******************************************************************************/ const gchar *mm_common_str_boolean (gboolean value); const gchar *mm_common_str_personal_info (const gchar *str, gboolean show_personal_info); void mm_common_str_array_human_keys (GPtrArray *array); /******************************************************************************/ /* Common parsers */ typedef gboolean (* MMParseKeyValueForeachFn) (const gchar *key, const gchar *value, gpointer user_data); gboolean mm_common_parse_key_value_string (const gchar *str, GError **error, MMParseKeyValueForeachFn callback, gpointer user_data); gboolean mm_get_int_from_str (const gchar *str, gint *out); gboolean mm_get_int_from_match_info (GMatchInfo *match_info, guint32 match_index, gint *out); gboolean mm_get_uint_from_str (const gchar *str, guint *out); gboolean mm_get_u64_from_str (const gchar *str, guint64 *out); gboolean mm_get_uint_from_hex_str (const gchar *str, guint *out); gboolean mm_get_u64_from_hex_str (const gchar *str, guint64 *out); gboolean mm_get_uint_from_match_info (GMatchInfo *match_info, guint32 match_index, guint *out); gboolean mm_get_u64_from_match_info (GMatchInfo *match_info, guint32 match_index, guint64 *out); gboolean mm_get_uint_from_hex_match_info (GMatchInfo *match_info, guint32 match_index, guint *out); gboolean mm_get_u64_from_hex_match_info (GMatchInfo *match_info, guint32 match_index, guint64 *out); gboolean mm_get_double_from_str (const gchar *str, gdouble *out); gboolean mm_get_double_from_match_info (GMatchInfo *match_info, guint32 match_index, gdouble *out); gchar *mm_get_string_unquoted_from_match_info (GMatchInfo *match_info, guint32 match_index); gchar *mm_new_iso8601_time_from_unix_time (guint64 timestamp, GError **error); gchar *mm_new_iso8601_time (guint year, guint month, guint day, guint hour, guint minute, guint second, gboolean have_offset, gint offset_minutes, GError **error); /******************************************************************************/ /* Type checkers and conversion utilities */ gint mm_utils_hex2byte (const gchar *hex); guint8 *mm_utils_hexstr2bin (const gchar *hex, gssize len, gsize *out_len, GError **error); gchar *mm_utils_bin2hexstr (const guint8 *bin, gsize len); gboolean mm_utils_ishexstr (const gchar *hex); gboolean mm_utils_check_for_single_value (guint32 value); gboolean mm_is_string_mccmnc (const gchar *str); const gchar *mm_sms_delivery_state_get_string_extended (guint delivery_state); /******************************************************************************/ /* DBus error handling */ gboolean mm_common_register_errors (void); GError *mm_common_error_from_tuple (GVariant *tuple, GError **error); GVariant *mm_common_error_to_tuple (const GError *error); #endif /* MM_COMMON_HELPERS_H */ ModemManager-1.23.4-dev/libmm-glib/mm-compat.c000066400000000000000000000121361456466623000210010ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * libmm-glib -- Access modem status & information from glib applications * * 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 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 Street, Fifth Floor, * Boston, MA 02110-1301 USA. * * Copyright (C) 2021 Aleksander Morgado */ #include #include "mm-compat.h" #ifndef MM_DISABLE_DEPRECATED /*****************************************************************************/ void mm_simple_connect_properties_set_number (MMSimpleConnectProperties *self, const gchar *number) { /* NO-OP */ } const gchar * mm_simple_connect_properties_get_number (MMSimpleConnectProperties *self) { /* NO-OP */ return NULL; } /*****************************************************************************/ void mm_bearer_properties_set_number (MMBearerProperties *self, const gchar *number) { /* NO-OP */ } const gchar * mm_bearer_properties_get_number (MMBearerProperties *self) { /* NO-OP */ return NULL; } /*****************************************************************************/ void mm_call_properties_set_direction (MMCallProperties *self, MMCallDirection direction) { /* NO-OP */ } MMCallDirection mm_call_properties_get_direction (MMCallProperties *self) { /* NO-OP */ return MM_CALL_DIRECTION_UNKNOWN; } void mm_call_properties_set_state (MMCallProperties *self, MMCallState state) { /* NO-OP */ } MMCallState mm_call_properties_get_state (MMCallProperties *self) { /* NO-OP */ return MM_CALL_STATE_UNKNOWN; } void mm_call_properties_set_state_reason (MMCallProperties *self, MMCallStateReason state_reason) { /* NO-OP */ } MMCallStateReason mm_call_properties_get_state_reason (MMCallProperties *self) { /* NO-OP */ return MM_CALL_STATE_REASON_UNKNOWN; } /*****************************************************************************/ gchar * mm_location_gps_nmea_build_full (MMLocationGpsNmea *self) { g_auto(GStrv) traces = NULL; traces = mm_location_gps_nmea_get_traces (self); return (traces ? g_strjoinv ("\r\n", traces) : g_strdup ("")); } /*****************************************************************************/ guint mm_location_3gpp_get_mobile_network_code (MMLocation3gpp *self) { const gchar *operator_code; g_return_val_if_fail (MM_IS_LOCATION_3GPP (self), 0); operator_code = mm_location_3gpp_get_operator_code (self); if (!operator_code) return 0; return strtol (operator_code + 3, NULL, 10); } /*****************************************************************************/ void mm_pco_list_free (GList *pco_list) { g_list_free_full (pco_list, g_object_unref); } /*****************************************************************************/ MMModem3gppSubscriptionState mm_simple_status_get_3gpp_subscription_state (MMSimpleStatus *self) { return MM_MODEM_3GPP_SUBSCRIPTION_STATE_UNKNOWN; } /*****************************************************************************/ MMModem3gppSubscriptionState mm_modem_3gpp_get_subscription_state (MMModem3gpp *self) { return MM_MODEM_3GPP_SUBSCRIPTION_STATE_UNKNOWN; } /*****************************************************************************/ gboolean mm_modem_get_pending_network_initiated_sessions (MMModemOma *self, MMOmaPendingNetworkInitiatedSession **sessions, guint *n_sessions) { return mm_modem_oma_get_pending_network_initiated_sessions (self, sessions, n_sessions); } gboolean mm_modem_peek_pending_network_initiated_sessions (MMModemOma *self, const MMOmaPendingNetworkInitiatedSession **sessions, guint *n_sessions) { return mm_modem_oma_peek_pending_network_initiated_sessions (self, sessions, n_sessions); } /*****************************************************************************/ guint mm_modem_get_max_bearers (MMModem *self) { g_return_val_if_fail (MM_IS_MODEM (self), 0); return mm_gdbus_modem_get_max_bearers (MM_GDBUS_MODEM (self)); } #endif /* MM_DISABLE_DEPRECATED */ ModemManager-1.23.4-dev/libmm-glib/mm-compat.h000066400000000000000000000322461456466623000210120ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * libmm-glib -- Access modem status & information from glib applications * * 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 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 Street, Fifth Floor, * Boston, MA 02110-1301 USA. * * Copyright (C) 2021 Aleksander Morgado */ #ifndef _MM_COMPAT_H_ #define _MM_COMPAT_H_ #ifndef MM_DISABLE_DEPRECATED #if !defined (__LIBMM_GLIB_H_INSIDE__) && !defined (LIBMM_GLIB_COMPILATION) #error "Only can be included directly." #endif #include "mm-simple-connect-properties.h" #include "mm-bearer-properties.h" #include "mm-call-properties.h" #include "mm-location-gps-nmea.h" #include "mm-location-3gpp.h" #include "mm-pco.h" #include "mm-simple-status.h" #include "mm-modem-3gpp.h" #include "mm-modem-oma.h" #include "mm-modem.h" /** * SECTION:mm-compat * @short_description: Deprecated types and methods. * * These types and methods are flagged as deprecated and therefore * shouldn't be used in newly written code. They are provided to avoid * innecessary API/ABI breaks, for compatibility purposes only. */ /*****************************************************************************/ /** * mm_simple_connect_properties_set_number: * @self: a #MMSimpleConnectProperties. * @number: the number. * * Sets the number to use when performing the connection. * * Since: 1.0 * Deprecated: 1.10.0. The number setting is not used anywhere, and therefore * it doesn't make sense to expose it in the ModemManager interface. */ G_DEPRECATED void mm_simple_connect_properties_set_number (MMSimpleConnectProperties *self, const gchar *number); /** * mm_simple_connect_properties_get_number: * @self: a #MMSimpleConnectProperties. * * Gets the number to use when performing the connection. * * Returns: (transfer none): the number, or #NULL if not set. Do not free the * returned value, it is owned by @self. * * Since: 1.0 * Deprecated: 1.10.0. The number setting is not used anywhere, and therefore * it doesn't make sense to expose it in the ModemManager interface. */ G_DEPRECATED const gchar *mm_simple_connect_properties_get_number (MMSimpleConnectProperties *self); /*****************************************************************************/ /** * mm_bearer_properties_set_number: * @self: a #MMBearerProperties. * @number: the number. * * Sets the number to use when performing the connection. * * Since: 1.0 * Deprecated: 1.10.0. The number setting is not used anywhere, and therefore * it doesn't make sense to expose it in the ModemManager interface. */ G_DEPRECATED void mm_bearer_properties_set_number (MMBearerProperties *self, const gchar *number); /** * mm_bearer_properties_get_number: * @self: a #MMBearerProperties. * * Gets the number to use when performing the connection. * * Returns: (transfer none): the number, or #NULL if not set. Do not free the * returned value, it is owned by @self. * * Since: 1.0 * Deprecated: 1.10.0. The number setting is not used anywhere, and therefore * it doesn't make sense to expose it in the ModemManager interface. */ G_DEPRECATED const gchar *mm_bearer_properties_get_number (MMBearerProperties *self); /*****************************************************************************/ /** * mm_call_properties_set_direction: * @self: A #MMCallProperties. * @direction: the call direction * * Sets the call direction. * * Since: 1.6 * Deprecated: 1.12: the user should not specify the direction of the call, as * it is implicit (outgoing always). Anyway, this parameter has always been * ignored during the new call creation processing. */ G_DEPRECATED void mm_call_properties_set_direction (MMCallProperties *self, MMCallDirection direction); /** * mm_call_properties_set_state_reason: * @self: A #MMCallProperties. * @state_reason: the call state reason. * * Sets the call state reason. * * Since: 1.6 * Deprecated: 1.12: the user should not specify the state reason of the call * before the call is created. This parameter has always been ignored during the * new call creation processing. */ G_DEPRECATED void mm_call_properties_set_state_reason (MMCallProperties *self, MMCallStateReason state_reason); /** * mm_call_properties_set_state: * @self: A #MMCallProperties. * @state: the call state * * Sets the call state * * Since: 1.6 * Deprecated: 1.12: the user should not specify the state of the call before * the call is created. This parameter has always been ignored during the new * call creation processing. */ G_DEPRECATED void mm_call_properties_set_state (MMCallProperties *self, MMCallState state); /** * mm_call_properties_get_direction: * @self: A #MMCallProperties. * * Gets the call direction. * * Returns: the call direction. * * Since: 1.6 * Deprecated: 1.12: the user should not specify the direction of the call, as * it is implicit (outgoing always). This parameter has always been ignored * during the new call creation processing. */ G_DEPRECATED MMCallDirection mm_call_properties_get_direction (MMCallProperties *self); /** * mm_call_properties_get_state_reason: * @self: A #MMCallProperties. * * Gets the call state reason. * * Returns: the call state reason. * * Since: 1.6 * Deprecated: 1.12: the user should not specify the state reason of the call * before the call is created. This parameter has always been ignored during the * new call creation processing. */ G_DEPRECATED MMCallStateReason mm_call_properties_get_state_reason (MMCallProperties *self); /** * mm_call_properties_get_state: * @self: A #MMCallProperties. * * Gets the call state. * * Returns: the call state. * * Since: 1.6 * Deprecated: 1.12: the user should not specify the state of the call before * the call is created. This parameter has always been ignored during the new * call creation processing. */ G_DEPRECATED MMCallState mm_call_properties_get_state (MMCallProperties *self); /*****************************************************************************/ /** * mm_location_3gpp_get_mobile_network_code: * @self: a #MMLocation3gpp. * * Gets the Mobile Network Code of the 3GPP network. * * Note that 0 may actually be a valid MNC. In general, the MNC should be * considered valid just if the reported MCC is valid, as MCC should never * be 0. * * Returns: the MNC, or 0 if unknown. * * Since: 1.0 * Deprecated: 1.18.0. This function can not separate between two-digit MNCs * and three-digit MNCs with a leading zero. Use mm_location_3gpp_get_operator_code() * instead. */ G_DEPRECATED guint mm_location_3gpp_get_mobile_network_code (MMLocation3gpp *self); /*****************************************************************************/ /** * mm_location_gps_nmea_build_full: * @self: a #MMLocationGpsNmea. * * Gets a compilation of all cached traces, in a single string. * Traces are separated by '\r\n'. * * Returns: (transfer full): a string containing all traces, or #NULL if none * available. The returned value should be freed with g_free(). * * Since: 1.0 * Deprecated: 1.14: user should use mm_location_gps_nmea_get_traces() instead, * which provides a much more generic interface to the full list of traces. */ G_DEPRECATED_FOR(mm_location_gps_nmea_get_traces) gchar *mm_location_gps_nmea_build_full (MMLocationGpsNmea *self); /*****************************************************************************/ /** * mm_pco_list_free: * @pco_list: (transfer full)(element-type ModemManager.Pco): a #GList of * #MMPco. * * Frees all of the memory used by a #GList of #MMPco. * * Since: 1.10 * Deprecated: 1.12.0: Use g_list_free_full() using g_object_unref() as * #GDestroyNotify function instead. */ G_DEPRECATED void mm_pco_list_free (GList *pco_list); /*****************************************************************************/ /** * mm_simple_status_get_3gpp_subscription_state: * @self: a #MMSimpleStatus. * * Gets the current subscription status of the account. * * Returns: a #MMModem3gppSubscriptionState. * * Since: 1.0 * Deprecated: 1.12.0. The value of this property can only be obtained with * operator specific logic (e.g. processing specific PCO info), and therefore * it doesn't make sense to expose it in the ModemManager interface. */ MMModem3gppSubscriptionState mm_simple_status_get_3gpp_subscription_state (MMSimpleStatus *self); /*****************************************************************************/ /** * mm_modem_3gpp_get_subscription_state: * @self: A #MMModem. * * Get the current subscription status of the account. This value is only * available after the modem attempts to register with the network. * * The value of this property can only be obtained with operator specific logic * (e.g. processing specific PCO info), and therefore it doesn't make sense to * expose it in the ModemManager interface. * * Returns: A #MMModem3gppSubscriptionState value, specifying the current * subscription state. * * Since: 1.0 * Deprecated: 1.10.0. The value of this property can only be obtained with * operator specific logic (e.g. processing specific PCO info), and therefore * it doesn't make sense to expose it in the ModemManager interface. */ G_DEPRECATED MMModem3gppSubscriptionState mm_modem_3gpp_get_subscription_state (MMModem3gpp *self); /*****************************************************************************/ /** * mm_modem_get_pending_network_initiated_sessions: * @self: A #MMModem. * @sessions: (out) (array length=n_sessions): Return location for the array of * #MMOmaPendingNetworkInitiatedSession structs. The returned array should be * freed with g_free() when no longer needed. * @n_sessions: (out): Return location for the number of values in @sessions. * * Gets the list of pending network-initiated OMA sessions. * * Returns: %TRUE if @sessions and @n_sessions are set, %FALSE otherwise. * * Since: 1.2 * Deprecated: 1.18: Use mm_modem_oma_get_pending_network_initiated_sessions() instead. */ G_DEPRECATED_FOR (mm_modem_oma_get_pending_network_initiated_sessions) gboolean mm_modem_get_pending_network_initiated_sessions (MMModemOma *self, MMOmaPendingNetworkInitiatedSession **sessions, guint *n_sessions); /** * mm_modem_peek_pending_network_initiated_sessions: * @self: A #MMModem. * @sessions: (out) (array length=n_sessions): Return location for the array of * #MMOmaPendingNetworkInitiatedSession values. Do not free the returned array, * it is owned by @self. * @n_sessions: (out): Return location for the number of values in @sessions. * * Gets the list of pending network-initiated OMA sessions. * * Returns: %TRUE if @sessions and @n_sessions are set, %FALSE otherwise. * * Since: 1.2 * Deprecated: 1.18: Use mm_modem_oma_peek_pending_network_initiated_sessions() instead. */ G_DEPRECATED_FOR (mm_modem_oma_peek_pending_network_initiated_sessions) gboolean mm_modem_peek_pending_network_initiated_sessions (MMModemOma *self, const MMOmaPendingNetworkInitiatedSession **sessions, guint *n_sessions); /*****************************************************************************/ /** * mm_modem_get_max_bearers: * @self: a #MMModem. * * Gets the maximum number of defined packet data bearers this #MMModem * supports. * * This is not the number of active/connected bearers the modem supports, * but simply the number of bearers that may be defined at any given time. * For example, POTS and CDMA2000-only devices support only one bearer, * while GSM/UMTS devices typically support three or more, and any * LTE-capable device (whether LTE-only, GSM/UMTS-capable, and/or * CDMA2000-capable) also typically support three or more. * * Returns: the maximum number of defined packet data bearers. * * Since: 1.0 * Deprecated: 1.18. There is no way to query the modem how many bearers * it supports, so the value exposed in this property in all the different * implementations is always equal to the one retrieved with * mm_modem_get_max_active_bearers(), so there is no point in using this * method. */ G_DEPRECATED guint mm_modem_get_max_bearers (MMModem *self); #endif /* MM_DISABLE_DEPRECATED */ #endif /* _MM_COMPAT_H_ */ ModemManager-1.23.4-dev/libmm-glib/mm-firmware-properties.c000066400000000000000000000406601456466623000235270ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * libmm-glib -- Access modem status & information from glib applications * * 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 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 Street, Fifth Floor, * Boston, MA 02110-1301 USA. * * Copyright (C) 2012 Google Inc. */ #include #include "mm-errors-types.h" #include "mm-common-helpers.h" #include "mm-firmware-properties.h" /** * SECTION: mm-firmware-properties * @title: MMFirmwareProperties * @short_description: Helper object to handle firmware information. * * The #MMFirmwareProperties is an object handling the properties exposed for * available firmware images. * * This object is retrieved with either mm_modem_firmware_list() * or mm_modem_firmware_list_sync(). */ G_DEFINE_TYPE (MMFirmwareProperties, mm_firmware_properties, G_TYPE_OBJECT) #define PROPERTY_UNIQUE_ID "unique-id" #define PROPERTY_IMAGE_TYPE "image-type" #define PROPERTY_GOBI_PRI_VERSION "gobi-pri-version" #define PROPERTY_GOBI_PRI_INFO "gobi-pri-info" #define PROPERTY_GOBI_BOOT_VERSION "gobi-boot-version" #define PROPERTY_GOBI_PRI_UNIQUE_ID "gobi-pri-unique-id" #define PROPERTY_GOBI_MODEM_UNIQUE_ID "gobi-modem-unique-id" struct _MMFirmwarePropertiesPrivate { /* Mandatory parameters */ MMFirmwareImageType image_type; gchar *unique_id; /* Gobi specific */ gchar *gobi_pri_version; gchar *gobi_pri_info; gchar *gobi_boot_version; gchar *gobi_pri_unique_id; gchar *gobi_modem_unique_id; }; static MMFirmwareProperties *firmware_properties_new_empty (void); /*****************************************************************************/ /** * mm_firmware_properties_get_unique_id: * @self: A #MMFirmwareProperties. * * Gets the unique ID of the firmare image. * * Returns: (transfer none): The ID of the image. Do not free the returned * value, it is owned by @self. * * Since: 1.0 */ const gchar * mm_firmware_properties_get_unique_id (MMFirmwareProperties *self) { g_return_val_if_fail (MM_IS_FIRMWARE_PROPERTIES (self), NULL); return self->priv->unique_id; } /*****************************************************************************/ /** * mm_firmware_properties_get_image_type: * @self: A #MMFirmwareProperties. * * Gets the type of the firmare image. * * Returns: A #MMFirmwareImageType specifying The type of the image. * * Since: 1.0 */ MMFirmwareImageType mm_firmware_properties_get_image_type (MMFirmwareProperties *self) { g_return_val_if_fail (MM_IS_FIRMWARE_PROPERTIES (self), MM_FIRMWARE_IMAGE_TYPE_UNKNOWN); return self->priv->image_type; } /*****************************************************************************/ /** * mm_firmware_properties_get_gobi_pri_version: * @self: a #MMFirmwareProperties. * * Gets the PRI version of a firmware image of type %MM_FIRMWARE_IMAGE_TYPE_GOBI. * * Returns: The PRI version, or %NULL if unknown. Do not free the returned value, * it is owned by @self. * * Since: 1.0 */ const gchar * mm_firmware_properties_get_gobi_pri_version (MMFirmwareProperties *self) { g_return_val_if_fail (MM_IS_FIRMWARE_PROPERTIES (self), NULL); g_return_val_if_fail (self->priv->image_type == MM_FIRMWARE_IMAGE_TYPE_GOBI, NULL); return self->priv->gobi_pri_version; } /* * mm_firmware_properties_set_gobi_pri_version: (skip) */ void mm_firmware_properties_set_gobi_pri_version (MMFirmwareProperties *self, const gchar *version) { g_return_if_fail (MM_IS_FIRMWARE_PROPERTIES (self)); g_return_if_fail (self->priv->image_type == MM_FIRMWARE_IMAGE_TYPE_GOBI); g_free (self->priv->gobi_pri_version); self->priv->gobi_pri_version = g_strdup (version); } /*****************************************************************************/ /** * mm_firmware_properties_get_gobi_pri_info: * @self: a #MMFirmwareProperties. * * Gets the PRI info of a firmware image of type %MM_FIRMWARE_IMAGE_TYPE_GOBI. * * Returns: The PRI info, or %NULL if unknown. Do not free the returned value, * it is owned by @self. * * Since: 1.0 */ const gchar * mm_firmware_properties_get_gobi_pri_info (MMFirmwareProperties *self) { g_return_val_if_fail (MM_IS_FIRMWARE_PROPERTIES (self), NULL); g_return_val_if_fail (self->priv->image_type == MM_FIRMWARE_IMAGE_TYPE_GOBI, NULL); return self->priv->gobi_pri_info; } /* * mm_firmware_properties_set_gobi_pri_info: (skip) */ void mm_firmware_properties_set_gobi_pri_info (MMFirmwareProperties *self, const gchar *info) { g_return_if_fail (MM_IS_FIRMWARE_PROPERTIES (self)); g_return_if_fail (self->priv->image_type == MM_FIRMWARE_IMAGE_TYPE_GOBI); g_free (self->priv->gobi_pri_info); self->priv->gobi_pri_info = g_strdup (info); } /** * mm_firmware_properties_get_gobi_boot_version: * @self: a #MMFirmwareProperties. * * Gets the boot version of a firmware image of type * %MM_FIRMWARE_IMAGE_TYPE_GOBI. * * Returns: The boot version, or %NULL if unknown. Do not free the returned * value, it is owned by @self. * * Since: 1.0 */ const gchar * mm_firmware_properties_get_gobi_boot_version (MMFirmwareProperties *self) { g_return_val_if_fail (MM_IS_FIRMWARE_PROPERTIES (self), NULL); g_return_val_if_fail (self->priv->image_type == MM_FIRMWARE_IMAGE_TYPE_GOBI, NULL); return self->priv->gobi_boot_version; } /* * mm_firmware_properties_set_gobi_boot_version: (skip) */ void mm_firmware_properties_set_gobi_boot_version (MMFirmwareProperties *self, const gchar *version) { g_return_if_fail (MM_IS_FIRMWARE_PROPERTIES (self)); g_return_if_fail (self->priv->image_type == MM_FIRMWARE_IMAGE_TYPE_GOBI); g_free (self->priv->gobi_boot_version); self->priv->gobi_boot_version = g_strdup (version); } /*****************************************************************************/ /** * mm_firmware_properties_get_gobi_pri_unique_id: * @self: a #MMFirmwareProperties. * * Gets the PRI unique ID of a firmware image of type * %MM_FIRMWARE_IMAGE_TYPE_GOBI. * * Returns: The PRI unique ID, or %NULL if unknown. Do not free the returned * value, it is owned by @self. * * Since: 1.0 */ const gchar * mm_firmware_properties_get_gobi_pri_unique_id (MMFirmwareProperties *self) { g_return_val_if_fail (MM_IS_FIRMWARE_PROPERTIES (self), NULL); g_return_val_if_fail (self->priv->image_type == MM_FIRMWARE_IMAGE_TYPE_GOBI, NULL); return self->priv->gobi_pri_unique_id; } /* * mm_firmware_properties_set_gobi_pri_unique_id: (skip) */ void mm_firmware_properties_set_gobi_pri_unique_id (MMFirmwareProperties *self, const gchar *unique_id) { g_return_if_fail (MM_IS_FIRMWARE_PROPERTIES (self)); g_return_if_fail (self->priv->image_type == MM_FIRMWARE_IMAGE_TYPE_GOBI); g_free (self->priv->gobi_pri_unique_id); self->priv->gobi_pri_unique_id = g_strdup (unique_id); } /*****************************************************************************/ /** * mm_firmware_properties_get_gobi_modem_unique_id: * @self: a #MMFirmwareProperties. * * Gets the MODEM unique ID of a firmware image of type * %MM_FIRMWARE_IMAGE_TYPE_GOBI. * * Returns: The PRI unique ID, or %NULL if unknown. Do not free the returned * value, it is owned by @self. * * Since: 1.0 */ const gchar * mm_firmware_properties_get_gobi_modem_unique_id (MMFirmwareProperties *self) { g_return_val_if_fail (MM_IS_FIRMWARE_PROPERTIES (self), NULL); g_return_val_if_fail (self->priv->image_type == MM_FIRMWARE_IMAGE_TYPE_GOBI, NULL); return self->priv->gobi_modem_unique_id; } /* * mm_firmware_properties_set_gobi_modem_unique_id: (skip) */ void mm_firmware_properties_set_gobi_modem_unique_id (MMFirmwareProperties *self, const gchar *unique_id) { g_return_if_fail (MM_IS_FIRMWARE_PROPERTIES (self)); g_return_if_fail (self->priv->image_type == MM_FIRMWARE_IMAGE_TYPE_GOBI); g_free (self->priv->gobi_modem_unique_id); self->priv->gobi_modem_unique_id = g_strdup (unique_id); } /*****************************************************************************/ /* * mm_firmware_properties_get_dictionary: (skip) */ GVariant * mm_firmware_properties_get_dictionary (MMFirmwareProperties *self) { GVariantBuilder builder; /* We do allow NULL */ if (!self) return NULL; g_return_val_if_fail (MM_IS_FIRMWARE_PROPERTIES (self), NULL); g_variant_builder_init (&builder, G_VARIANT_TYPE ("a{sv}")); g_variant_builder_add (&builder, "{sv}", PROPERTY_UNIQUE_ID, g_variant_new_string (self->priv->unique_id)); g_variant_builder_add (&builder, "{sv}", PROPERTY_IMAGE_TYPE, g_variant_new_uint32 (self->priv->image_type)); if (self->priv->image_type == MM_FIRMWARE_IMAGE_TYPE_GOBI) { if (self->priv->gobi_pri_version) g_variant_builder_add (&builder, "{sv}", PROPERTY_GOBI_PRI_VERSION, g_variant_new_string (self->priv->gobi_pri_version)); if (self->priv->gobi_pri_info) g_variant_builder_add (&builder, "{sv}", PROPERTY_GOBI_PRI_INFO, g_variant_new_string (self->priv->gobi_pri_info)); if (self->priv->gobi_boot_version) g_variant_builder_add (&builder, "{sv}", PROPERTY_GOBI_BOOT_VERSION, g_variant_new_string (self->priv->gobi_boot_version)); if (self->priv->gobi_pri_unique_id) g_variant_builder_add (&builder, "{sv}", PROPERTY_GOBI_PRI_UNIQUE_ID, g_variant_new_string (self->priv->gobi_pri_unique_id)); if (self->priv->gobi_modem_unique_id) g_variant_builder_add (&builder, "{sv}", PROPERTY_GOBI_MODEM_UNIQUE_ID, g_variant_new_string (self->priv->gobi_modem_unique_id)); } return g_variant_ref_sink (g_variant_builder_end (&builder)); } /*****************************************************************************/ static gboolean consume_variant (MMFirmwareProperties *self, const gchar *key, GVariant *value, GError **error) { if (g_str_equal (key, PROPERTY_UNIQUE_ID)) { g_free (self->priv->unique_id); self->priv->unique_id = g_variant_dup_string (value, NULL); } else if (g_str_equal (key, PROPERTY_IMAGE_TYPE)) { self->priv->image_type = g_variant_get_uint32 (value); } else if (g_str_equal (key, PROPERTY_GOBI_PRI_VERSION)) { g_free (self->priv->gobi_pri_version); self->priv->gobi_pri_version = g_variant_dup_string (value, NULL); } else if (g_str_equal (key, PROPERTY_GOBI_PRI_INFO)) { g_free (self->priv->gobi_pri_info); self->priv->gobi_pri_info = g_variant_dup_string (value, NULL); } else if (g_str_equal (key, PROPERTY_GOBI_BOOT_VERSION)) { g_free (self->priv->gobi_boot_version); self->priv->gobi_boot_version = g_variant_dup_string (value, NULL); } else if (g_str_equal (key, PROPERTY_GOBI_PRI_UNIQUE_ID)) { g_free (self->priv->gobi_pri_unique_id); self->priv->gobi_pri_unique_id = g_variant_dup_string (value, NULL); } else if (g_str_equal (key, PROPERTY_GOBI_MODEM_UNIQUE_ID)) { g_free (self->priv->gobi_modem_unique_id); self->priv->gobi_modem_unique_id = g_variant_dup_string (value, NULL); } else { /* Set error */ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS, "Invalid properties dictionary, unexpected key '%s'", key); return FALSE; } return TRUE; } /* * mm_firmware_properties_new_from_dictionary: (skip) */ MMFirmwareProperties * mm_firmware_properties_new_from_dictionary (GVariant *dictionary, GError **error) { GError *inner_error = NULL; GVariantIter iter; gchar *key; GVariant *value; MMFirmwareProperties *self; if (!dictionary) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS, "Cannot create Firmware properties from empty dictionary"); return NULL; } if (!g_variant_is_of_type (dictionary, G_VARIANT_TYPE ("a{sv}"))) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS, "Cannot create Firmware properties from dictionary: " "invalid variant type received"); return NULL; } self = firmware_properties_new_empty (); g_variant_iter_init (&iter, dictionary); while (!inner_error && g_variant_iter_next (&iter, "{sv}", &key, &value)) { consume_variant (self, key, value, &inner_error); g_free (key); g_variant_unref (value); } /* If error, destroy the object */ if (inner_error) { g_propagate_error (error, inner_error); g_object_unref (self); return NULL; } /* If mandatory properties missing, destroy the object */ if (!self->priv->unique_id || self->priv->image_type == MM_FIRMWARE_IMAGE_TYPE_UNKNOWN) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS, "Cannot create Firmware properties from dictionary: " "mandatory parameter missing"); g_object_unref (self); return NULL; } return self; } /*****************************************************************************/ /* * mm_firmware_properties_new: (skip) */ MMFirmwareProperties * mm_firmware_properties_new (MMFirmwareImageType image_type, const gchar *unique_id) { MMFirmwareProperties *self; g_return_val_if_fail (image_type != MM_FIRMWARE_IMAGE_TYPE_UNKNOWN, NULL); g_return_val_if_fail (unique_id != NULL, NULL); self = firmware_properties_new_empty (); self->priv->image_type = image_type; self->priv->unique_id = g_strdup (unique_id); return self; } static MMFirmwareProperties * firmware_properties_new_empty (void) { return (MM_FIRMWARE_PROPERTIES ( g_object_new (MM_TYPE_FIRMWARE_PROPERTIES, NULL))); } static void mm_firmware_properties_init (MMFirmwareProperties *self) { self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, MM_TYPE_FIRMWARE_PROPERTIES, MMFirmwarePropertiesPrivate); /* Some defaults */ self->priv->image_type = MM_FIRMWARE_IMAGE_TYPE_UNKNOWN; } static void finalize (GObject *object) { MMFirmwareProperties *self = MM_FIRMWARE_PROPERTIES (object); g_free (self->priv->unique_id); g_free (self->priv->gobi_pri_version); g_free (self->priv->gobi_pri_info); g_free (self->priv->gobi_boot_version); g_free (self->priv->gobi_pri_unique_id); g_free (self->priv->gobi_modem_unique_id); G_OBJECT_CLASS (mm_firmware_properties_parent_class)->finalize (object); } static void mm_firmware_properties_class_init (MMFirmwarePropertiesClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); g_type_class_add_private (object_class, sizeof (MMFirmwarePropertiesPrivate)); object_class->finalize = finalize; } ModemManager-1.23.4-dev/libmm-glib/mm-firmware-properties.h000066400000000000000000000112601456466623000235260ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * libmm-glib -- Access modem status & information from glib applications * * 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 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 Street, Fifth Floor, * Boston, MA 02110-1301 USA. * * Copyright (C) 2012 Google Inc. */ #ifndef MM_FIRMWARE_PROPERTIES_H #define MM_FIRMWARE_PROPERTIES_H #if !defined (__LIBMM_GLIB_H_INSIDE__) && !defined (LIBMM_GLIB_COMPILATION) #error "Only can be included directly." #endif #include #include G_BEGIN_DECLS #define MM_TYPE_FIRMWARE_PROPERTIES (mm_firmware_properties_get_type ()) #define MM_FIRMWARE_PROPERTIES(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_FIRMWARE_PROPERTIES, MMFirmwareProperties)) #define MM_FIRMWARE_PROPERTIES_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_FIRMWARE_PROPERTIES, MMFirmwarePropertiesClass)) #define MM_IS_FIRMWARE_PROPERTIES(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_FIRMWARE_PROPERTIES)) #define MM_IS_FIRMWARE_PROPERTIES_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_FIRMWARE_PROPERTIES)) #define MM_FIRMWARE_PROPERTIES_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_FIRMWARE_PROPERTIES, MMFirmwarePropertiesClass)) typedef struct _MMFirmwareProperties MMFirmwareProperties; typedef struct _MMFirmwarePropertiesClass MMFirmwarePropertiesClass; typedef struct _MMFirmwarePropertiesPrivate MMFirmwarePropertiesPrivate; /** * MMFirmwareProperties: * * The #MMFirmwareProperties structure contains private data and should only be accessed * using the provided API. */ struct _MMFirmwareProperties { /*< private >*/ GObject parent; MMFirmwarePropertiesPrivate *priv; }; struct _MMFirmwarePropertiesClass { /*< private >*/ GObjectClass parent; }; GType mm_firmware_properties_get_type (void); G_DEFINE_AUTOPTR_CLEANUP_FUNC (MMFirmwareProperties, g_object_unref) const gchar *mm_firmware_properties_get_unique_id (MMFirmwareProperties *self); MMFirmwareImageType mm_firmware_properties_get_image_type (MMFirmwareProperties *self); /* Gobi specific */ const gchar *mm_firmware_properties_get_gobi_pri_version (MMFirmwareProperties *self); const gchar *mm_firmware_properties_get_gobi_pri_info (MMFirmwareProperties *self); const gchar *mm_firmware_properties_get_gobi_boot_version (MMFirmwareProperties *self); const gchar *mm_firmware_properties_get_gobi_pri_unique_id (MMFirmwareProperties *self); const gchar *mm_firmware_properties_get_gobi_modem_unique_id (MMFirmwareProperties *self); /*****************************************************************************/ /* ModemManager/libmm-glib/mmcli specific methods */ #if defined (_LIBMM_INSIDE_MM) || \ defined (_LIBMM_INSIDE_MMCLI) || \ defined (LIBMM_GLIB_COMPILATION) MMFirmwareProperties *mm_firmware_properties_new (MMFirmwareImageType image_type, const gchar *unique_id); MMFirmwareProperties *mm_firmware_properties_new_from_dictionary (GVariant *dictionary, GError **error); /* Gobi specific */ void mm_firmware_properties_set_gobi_pri_version (MMFirmwareProperties *self, const gchar *version); void mm_firmware_properties_set_gobi_pri_info (MMFirmwareProperties *self, const gchar *info); void mm_firmware_properties_set_gobi_boot_version (MMFirmwareProperties *self, const gchar *version); void mm_firmware_properties_set_gobi_pri_unique_id (MMFirmwareProperties *self, const gchar *id); void mm_firmware_properties_set_gobi_modem_unique_id (MMFirmwareProperties *self, const gchar *id); GVariant *mm_firmware_properties_get_dictionary (MMFirmwareProperties *self); #endif G_END_DECLS #endif /* MM_FIRMWARE_PROPERTIES_H */ ModemManager-1.23.4-dev/libmm-glib/mm-firmware-update-settings.c000066400000000000000000000273701456466623000244560ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * libmm-glib -- Access modem status & information from glib applications * * 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 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 Street, Fifth Floor, * Boston, MA 02110-1301 USA. * * Copyright (C) 2018 Aleksander Morgado */ #include #include "mm-errors-types.h" #include "mm-common-helpers.h" #include "mm-firmware-update-settings.h" /** * SECTION: mm-firmware-update-settings * @title: MMFirmwareUpdateSettings * @short_description: Helper object to handle firmware update settings. * * The #MMFirmwareUpdateSettings is an object handling the settings exposed to * aid in the firmware update operation. */ G_DEFINE_TYPE (MMFirmwareUpdateSettings, mm_firmware_update_settings, G_TYPE_OBJECT) #define PROPERTY_DEVICE_IDS "device-ids" #define PROPERTY_VERSION "version" #define PROPERTY_FASTBOOT_AT "fastboot-at" struct _MMFirmwareUpdateSettingsPrivate { /* Generic */ MMModemFirmwareUpdateMethod method; gchar **device_ids; gchar *version; /* Fasboot specific */ gchar *fastboot_at; }; /*****************************************************************************/ /** * mm_firmware_update_settings_get_method: * @self: A #MMFirmwareUpdateSettings. * * Gets the methods to use during the firmware update operation. * * Returns: a bitmask of #MMModemFirmwareUpdateMethod values. * * Since: 1.10 */ MMModemFirmwareUpdateMethod mm_firmware_update_settings_get_method (MMFirmwareUpdateSettings *self) { g_return_val_if_fail (MM_IS_FIRMWARE_UPDATE_SETTINGS (self), MM_MODEM_FIRMWARE_UPDATE_METHOD_NONE); return self->priv->method; } /** * mm_firmware_update_settings_set_method: (skip) */ void mm_firmware_update_settings_set_method (MMFirmwareUpdateSettings *self, MMModemFirmwareUpdateMethod method) { g_return_if_fail (MM_IS_FIRMWARE_UPDATE_SETTINGS (self)); self->priv->method = method; } /*****************************************************************************/ /** * mm_firmware_update_settings_get_device_ids: * @self: a #MMFirmwareUpdateSettings. * * Gets the list of device ids used to identify the device during a firmware * update operation. * * Returns: (transfer none): The list of device ids, or %NULL if unknown. Do not * free the returned value, it is owned by @self. * * Since: 1.10 */ const gchar ** mm_firmware_update_settings_get_device_ids (MMFirmwareUpdateSettings *self) { g_return_val_if_fail (MM_IS_FIRMWARE_UPDATE_SETTINGS (self), NULL); return (const gchar **) self->priv->device_ids; } /** * mm_firmware_update_settings_set_device_ids: (skip) */ void mm_firmware_update_settings_set_device_ids (MMFirmwareUpdateSettings *self, const gchar **device_ids) { g_return_if_fail (MM_IS_FIRMWARE_UPDATE_SETTINGS (self)); g_strfreev (self->priv->device_ids); self->priv->device_ids = g_strdupv ((gchar **)device_ids); } /*****************************************************************************/ /** * mm_firmware_update_settings_get_version: * @self: a #MMFirmwareUpdateSettings. * * Gets firmware version string. * * * Returns: The version string, or %NULL if unknown. Do not free the returned * value, it is owned by @self. * * Since: 1.10 */ const gchar * mm_firmware_update_settings_get_version (MMFirmwareUpdateSettings *self) { g_return_val_if_fail (MM_IS_FIRMWARE_UPDATE_SETTINGS (self), NULL); return self->priv->version; } /** * mm_firmware_update_settings_set_version: (skip) */ void mm_firmware_update_settings_set_version (MMFirmwareUpdateSettings *self, const gchar *version) { g_return_if_fail (MM_IS_FIRMWARE_UPDATE_SETTINGS (self)); g_free (self->priv->version); self->priv->version = g_strdup (version); } /*****************************************************************************/ /** * mm_firmware_update_settings_get_fastboot_at: * @self: a #MMFirmwareUpdateSettings. * * Gets the AT command that should be sent to the module to trigger a reset * into fastboot mode. * * Only applicable if the update method includes * %MM_MODEM_FIRMWARE_UPDATE_METHOD_FASTBOOT. * * Returns: The AT command string, or %NULL if unknown. Do not free the returned * value, it is owned by @self. * * Since: 1.10 */ const gchar * mm_firmware_update_settings_get_fastboot_at (MMFirmwareUpdateSettings *self) { g_return_val_if_fail (MM_IS_FIRMWARE_UPDATE_SETTINGS (self), NULL); g_return_val_if_fail (self->priv->method & MM_MODEM_FIRMWARE_UPDATE_METHOD_FASTBOOT, NULL); return self->priv->fastboot_at; } /** * mm_firmware_update_settings_set_fastboot_at: (skip) */ void mm_firmware_update_settings_set_fastboot_at (MMFirmwareUpdateSettings *self, const gchar *fastboot_at) { g_return_if_fail (MM_IS_FIRMWARE_UPDATE_SETTINGS (self)); g_return_if_fail (self->priv->method & MM_MODEM_FIRMWARE_UPDATE_METHOD_FASTBOOT); g_free (self->priv->fastboot_at); self->priv->fastboot_at = g_strdup (fastboot_at); } /*****************************************************************************/ /** * mm_firmware_update_settings_get_variant: (skip) */ GVariant * mm_firmware_update_settings_get_variant (MMFirmwareUpdateSettings *self) { MMModemFirmwareUpdateMethod method; GVariantBuilder builder; method = (self ? self->priv->method : MM_MODEM_FIRMWARE_UPDATE_METHOD_NONE); g_variant_builder_init (&builder, G_VARIANT_TYPE ("(ua{sv})")); g_variant_builder_add (&builder, "u", method); g_variant_builder_open (&builder, G_VARIANT_TYPE ("a{sv}")); if (self) { g_variant_builder_add (&builder, "{sv}", PROPERTY_DEVICE_IDS, g_variant_new_strv ((const gchar * const *)self->priv->device_ids, -1)); g_variant_builder_add (&builder, "{sv}", PROPERTY_VERSION, g_variant_new_string (self->priv->version)); if (method & MM_MODEM_FIRMWARE_UPDATE_METHOD_FASTBOOT) { g_variant_builder_add (&builder, "{sv}", PROPERTY_FASTBOOT_AT, g_variant_new_string (self->priv->fastboot_at)); } } g_variant_builder_close (&builder); return g_variant_ref_sink (g_variant_builder_end (&builder)); } /*****************************************************************************/ static gboolean consume_variant (MMFirmwareUpdateSettings *self, const gchar *key, GVariant *value, GError **error) { if (g_str_equal (key, PROPERTY_FASTBOOT_AT)) { g_free (self->priv->fastboot_at); self->priv->fastboot_at = g_variant_dup_string (value, NULL); } else if (g_str_equal (key, PROPERTY_VERSION)) { g_free (self->priv->version); self->priv->version = g_variant_dup_string (value, NULL); } else if (g_str_equal (key, PROPERTY_DEVICE_IDS)) { g_strfreev (self->priv->device_ids); self->priv->device_ids = g_variant_dup_strv (value, NULL); } else { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS, "Invalid settings dictionary, unexpected key '%s'", key); return FALSE; } return TRUE; } /** * mm_firmware_update_settings_new_from_variant: (skip) */ MMFirmwareUpdateSettings * mm_firmware_update_settings_new_from_variant (GVariant *variant, GError **error) { MMFirmwareUpdateSettings *self; guint method = MM_MODEM_FIRMWARE_UPDATE_METHOD_NONE; GVariant *dictionary = NULL; GError *inner_error = NULL; if (!variant) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS, "No input given"); return NULL; } if (!g_variant_is_of_type (variant, G_VARIANT_TYPE ("(ua{sv})"))) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS, "Invalid input type"); return NULL; } g_variant_get (variant, "(u@a{sv})", &method, &dictionary); self = mm_firmware_update_settings_new (method); if ((method != MM_MODEM_FIRMWARE_UPDATE_METHOD_NONE) && dictionary) { GVariantIter iter; gchar *key; GVariant *value; g_variant_iter_init (&iter, dictionary); while (!inner_error && g_variant_iter_next (&iter, "{sv}", &key, &value)) { consume_variant (self, key, value, &inner_error); g_free (key); g_variant_unref (value); } if (!inner_error) { if (!self->priv->device_ids) inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS, "Missing required '" PROPERTY_DEVICE_IDS "' setting"); else if (!self->priv->version) inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS, "Missing required '" PROPERTY_VERSION "' setting"); } if (!inner_error) { if ((method & MM_MODEM_FIRMWARE_UPDATE_METHOD_FASTBOOT) && (!self->priv->fastboot_at)) inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS, "Fastboot method requires the '" PROPERTY_FASTBOOT_AT "' setting"); } g_variant_unref (dictionary); } if (inner_error) { g_propagate_error (error, inner_error); g_object_unref (self); return NULL; } return self; } /*****************************************************************************/ /** * mm_firmware_update_settings_new: (skip) */ MMFirmwareUpdateSettings * mm_firmware_update_settings_new (MMModemFirmwareUpdateMethod method) { MMFirmwareUpdateSettings *self; self = g_object_new (MM_TYPE_FIRMWARE_UPDATE_SETTINGS, NULL); self->priv->method = method; return self; } static void mm_firmware_update_settings_init (MMFirmwareUpdateSettings *self) { self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, MM_TYPE_FIRMWARE_UPDATE_SETTINGS, MMFirmwareUpdateSettingsPrivate); self->priv->method = MM_MODEM_FIRMWARE_UPDATE_METHOD_NONE; } static void finalize (GObject *object) { MMFirmwareUpdateSettings *self = MM_FIRMWARE_UPDATE_SETTINGS (object); g_strfreev (self->priv->device_ids); g_free (self->priv->version); g_free (self->priv->fastboot_at); G_OBJECT_CLASS (mm_firmware_update_settings_parent_class)->finalize (object); } static void mm_firmware_update_settings_class_init (MMFirmwareUpdateSettingsClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); g_type_class_add_private (object_class, sizeof (MMFirmwareUpdateSettingsPrivate)); object_class->finalize = finalize; } ModemManager-1.23.4-dev/libmm-glib/mm-firmware-update-settings.h000066400000000000000000000110011456466623000244430ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * libmm-glib -- Access modem status & information from glib applications * * 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 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 Street, Fifth Floor, * Boston, MA 02110-1301 USA. * * Copyright (C) 2018 Aleksander Morgado */ #ifndef MM_FIRMWARE_UPDATE_SETTINGS_H #define MM_FIRMWARE_UPDATE_SETTINGS_H #if !defined (__LIBMM_GLIB_H_INSIDE__) && !defined (LIBMM_GLIB_COMPILATION) #error "Only can be included directly." #endif #include #include G_BEGIN_DECLS #define MM_TYPE_FIRMWARE_UPDATE_SETTINGS (mm_firmware_update_settings_get_type ()) #define MM_FIRMWARE_UPDATE_SETTINGS(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_FIRMWARE_UPDATE_SETTINGS, MMFirmwareUpdateSettings)) #define MM_FIRMWARE_UPDATE_SETTINGS_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_FIRMWARE_UPDATE_SETTINGS, MMFirmwareUpdateSettingsClass)) #define MM_IS_FIRMWARE_UPDATE_SETTINGS(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_FIRMWARE_UPDATE_SETTINGS)) #define MM_IS_FIRMWARE_UPDATE_SETTINGS_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_FIRMWARE_UPDATE_SETTINGS)) #define MM_FIRMWARE_UPDATE_SETTINGS_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_FIRMWARE_UPDATE_SETTINGS, MMFirmwareUpdateSettingsClass)) typedef struct _MMFirmwareUpdateSettings MMFirmwareUpdateSettings; typedef struct _MMFirmwareUpdateSettingsClass MMFirmwareUpdateSettingsClass; typedef struct _MMFirmwareUpdateSettingsPrivate MMFirmwareUpdateSettingsPrivate; /** * MMFirmwareUpdateSettings: * * The #MMFirmwareUpdateSettings structure contains private data and should only be accessed * using the provided API. */ struct _MMFirmwareUpdateSettings { /*< private >*/ GObject parent; MMFirmwareUpdateSettingsPrivate *priv; }; struct _MMFirmwareUpdateSettingsClass { /*< private >*/ GObjectClass parent; }; GType mm_firmware_update_settings_get_type (void); G_DEFINE_AUTOPTR_CLEANUP_FUNC (MMFirmwareUpdateSettings, g_object_unref) MMModemFirmwareUpdateMethod mm_firmware_update_settings_get_method (MMFirmwareUpdateSettings *self); /* Generic */ const gchar **mm_firmware_update_settings_get_device_ids (MMFirmwareUpdateSettings *self); const gchar *mm_firmware_update_settings_get_version (MMFirmwareUpdateSettings *self); /* Fastboot specific */ const gchar *mm_firmware_update_settings_get_fastboot_at (MMFirmwareUpdateSettings *self); /*****************************************************************************/ /* ModemManager/libmm-glib/mmcli specific methods */ #if defined (_LIBMM_INSIDE_MM) || \ defined (_LIBMM_INSIDE_MMCLI) || \ defined (LIBMM_GLIB_COMPILATION) MMFirmwareUpdateSettings *mm_firmware_update_settings_new (MMModemFirmwareUpdateMethod method); MMFirmwareUpdateSettings *mm_firmware_update_settings_new_from_variant (GVariant *variant, GError **error); GVariant *mm_firmware_update_settings_get_variant (MMFirmwareUpdateSettings *self); /* Generic */ void mm_firmware_update_settings_set_device_ids (MMFirmwareUpdateSettings *self, const gchar **device_ids); void mm_firmware_update_settings_set_version (MMFirmwareUpdateSettings *self, const gchar *version); void mm_firmware_update_settings_set_method (MMFirmwareUpdateSettings *self, MMModemFirmwareUpdateMethod method); /* Fastboot specific */ void mm_firmware_update_settings_set_fastboot_at (MMFirmwareUpdateSettings *self, const gchar *fastboot_at); #endif G_END_DECLS #endif /* MM_FIRMWARE_UPDATE_SETTINGS_H */ ModemManager-1.23.4-dev/libmm-glib/mm-helper-types.c000066400000000000000000000025751456466623000221450ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * libmm-glib -- Access modem status & information from glib applications * * 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 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 Street, Fifth Floor, * Boston, MA 02110-1301 USA. * * Copyright (C) 2013 Aleksander Morgado */ #include "mm-helper-types.h" /** * mm_modem_port_info_array_free: * @array: an array of #MMModemPortInfo values. * @array_size: length of @array. * * Frees an array of #MMModemPortInfo values. * * Since: 1.0 */ void mm_modem_port_info_array_free (MMModemPortInfo *array, guint array_size) { guint i; for (i = 0; i < array_size; i++) g_free (array[i].name); g_free (array); } ModemManager-1.23.4-dev/libmm-glib/mm-helper-types.h000066400000000000000000000050141456466623000221410ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * libmm-glib -- Access modem status & information from glib applications * * 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 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 Street, Fifth Floor, * Boston, MA 02110-1301 USA. * * Copyright (C) 2013 Aleksander Morgado */ #include #include #if !defined (__LIBMM_GLIB_H_INSIDE__) && !defined (LIBMM_GLIB_COMPILATION) #error "Only can be included directly." #endif #ifndef _MM_HELPER_TYPES_H_ #define _MM_HELPER_TYPES_H_ /** * MMModemModeCombination: * @allowed: Mask of #MMModemMode values specifying allowed modes. * @preferred: A single #MMModemMode value specifying the preferred mode. * * #MMModemModeCombination is a simple struct holding a pair of #MMModemMode * values. * * Since: 1.0 */ typedef struct _MMModemModeCombination MMModemModeCombination; struct _MMModemModeCombination { MMModemMode allowed; MMModemMode preferred; }; /** * MMModemPortInfo: * @name: Name of the port. * @type: A #MMModemPortType value. * * Information of a given port. * * Since: 1.0 */ typedef struct _MMModemPortInfo MMModemPortInfo; struct _MMModemPortInfo { gchar *name; MMModemPortType type; }; void mm_modem_port_info_array_free (MMModemPortInfo *array, guint array_size); /** * MMOmaPendingNetworkInitiatedSession: * @session_type: A #MMOmaSessionType. * @session_id: Unique ID of the network-initiated OMA session. * * #MMOmaPendingNetworkInitiatedSession is a simple struct specifying the * information available for a pending network-initiated OMA session. * * Since: 1.2 */ typedef struct _MMOmaPendingNetworkInitiatedSession MMOmaPendingNetworkInitiatedSession; struct _MMOmaPendingNetworkInitiatedSession { MMOmaSessionType session_type; guint session_id; }; #endif /* _MM_HELPER_TYPES_H_ */ ModemManager-1.23.4-dev/libmm-glib/mm-helpers.h000066400000000000000000000533441456466623000211730ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * libmm-glib -- Access modem status & information from glib applications * * 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 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 Street, Fifth Floor, * Boston, MA 02110-1301 USA. * * Copyright (C) 2011-2021 Aleksander Morgado */ #ifndef _MM_HELPERS_H_ #define _MM_HELPERS_H_ /******************************************************************************/ #define RETURN_NON_EMPTY_CONSTANT_STRING(input) do { \ const gchar *str; \ \ str = (input); \ if (str && str[0]) \ return str; \ } while (0); \ return NULL #define RETURN_NON_EMPTY_STRING(input) do { \ gchar *str; \ \ str = (input); \ if (str && str[0]) \ return str; \ g_free (str); \ } while (0); \ return NULL /******************************************************************************/ /* These are helper macros to work with properties that are being monitored * internally by the proxy objects. This internal monitoring is used to allow * maintaining 'custom' types associated to complex DBus properties like * dictionaries. * * Basic ARRAY and OBJECT type support is given. */ #define PROPERTY_COMMON_DECLARE(property_name) \ guint property_name##_id; \ gboolean property_name##_refresh_required; #define PROPERTY_DECLARE(property_name,PropertyType) \ PropertyType *property_name; \ PROPERTY_COMMON_DECLARE (property_name) #define PROPERTY_ARRAY_DECLARE(property_name) PROPERTY_DECLARE (property_name, GArray) #define PROPERTY_OBJECT_DECLARE(property_name,ObjectType) PROPERTY_DECLARE (property_name, ObjectType) #define PROPERTY_ERROR_DECLARE(property_name) PROPERTY_DECLARE (property_name, GError) #define PROPERTY_INITIALIZE(property_name,signal_name) \ self->priv->property_name##_refresh_required = TRUE; \ self->priv->property_name##_id = \ g_signal_connect (self, \ "notify::" signal_name, \ G_CALLBACK (property_name##_updated), \ NULL); #define PROPERTY_ARRAY_FINALIZE(property_name) \ g_clear_pointer (&self->priv->property_name, g_array_unref); #define PROPERTY_OBJECT_FINALIZE(property_name) \ g_clear_object (&self->priv->property_name); #define PROPERTY_ERROR_FINALIZE(property_name) \ g_clear_error (&self->priv->property_name); /* This helper macro uses a GMutexLocker to lock the context * in which the macro is defined (so it must always be defined at the * start of the context). It also will run a given refresh method if * a specific input flag is set. */ #define PROPERTY_LOCK_AND_REFRESH(property_name) \ g_autoptr(GMutexLocker) locker = NULL; \ \ locker = g_mutex_locker_new (&self->priv->mutex); \ if (self->priv->property_name##_refresh_required) { \ property_name##_refresh (self); \ self->priv->property_name##_refresh_required = FALSE; \ } /* This helper defines the property refresh method, and can be used for simple * one-to-one property vs array transformations */ #define PROPERTY_ARRAY_DEFINE_REFRESH(property_name,Type,type,TYPE,variant_to_garray) \ static void \ property_name##_refresh (MM##Type *self) \ { \ g_autoptr(GVariant) variant = NULL; \ \ g_clear_pointer (&self->priv->property_name, g_array_unref); \ \ variant = mm_gdbus_##type##_dup_##property_name (MM_GDBUS_##TYPE (self)); \ if (!variant) \ return; \ \ self->priv->property_name = variant_to_garray (variant); \ } /* This helper defines the property refresh method, and can be used for simple * one-to-one property vs object transformations */ #define PROPERTY_OBJECT_DEFINE_REFRESH(property_name,Type,type,TYPE,variant_to_object) \ static void \ property_name##_refresh (MM##Type *self) \ { \ g_autoptr(GVariant) variant = NULL; \ \ g_clear_object (&self->priv->property_name); \ \ variant = mm_gdbus_##type##_dup_##property_name (MM_GDBUS_##TYPE (self)); \ if (!variant) \ return; \ \ self->priv->property_name = variant_to_object (variant); \ } #define PROPERTY_OBJECT_DEFINE_REFRESH_FAILABLE(property_name,Type,type,TYPE,variant_to_object) \ static void \ property_name##_refresh (MM##Type *self) \ { \ g_autoptr(GVariant) variant = NULL; \ g_autoptr(GError) inner_error = NULL; \ \ g_clear_object (&self->priv->property_name); \ \ variant = mm_gdbus_##type##_dup_##property_name (MM_GDBUS_##TYPE (self)); \ if (!variant) \ return; \ \ self->priv->property_name = variant_to_object (variant, &inner_error); \ if (inner_error) \ g_warning ("Invalid object variant reported: %s", inner_error->message); \ } /* This helper defines the property refresh method, and can be used for simple * one-to-one property vs GError transformations */ #define PROPERTY_ERROR_DEFINE_REFRESH_FAILABLE(property_name,Type,type,TYPE,variant_to_error) \ static void \ property_name##_refresh (MM##Type *self) \ { \ g_autoptr(GVariant) variant = NULL; \ g_autoptr(GError) inner_error = NULL; \ \ g_clear_error (&self->priv->property_name); \ \ variant = mm_gdbus_##type##_dup_##property_name (MM_GDBUS_##TYPE (self)); \ if (!variant) \ return; \ \ self->priv->property_name = variant_to_error (variant, &inner_error); \ if (inner_error) \ g_warning ("Invalid error variant reported: %s", inner_error->message); \ } /* This helper defines the common generic property updated callback */ #define PROPERTY_DEFINE_UPDATED(property_name,Type) \ static void \ property_name##_updated (MM##Type *self) \ { \ g_autoptr(GMutexLocker) locker = NULL; \ \ locker = g_mutex_locker_new (&self->priv->mutex); \ self->priv->property_name##_refresh_required = TRUE; \ } /* Getter implementation for arrays of complex types that need * deep copy. */ #define PROPERTY_ARRAY_DEFINE_GET_DEEP(property_name,Type,type,TYPE,ArrayItemType,garray_to_array) \ gboolean \ mm_##type##_get_##property_name (MM##Type *self, \ ArrayItemType **out, \ guint *n_out) \ { \ g_return_val_if_fail (MM_IS_##TYPE (self), FALSE); \ g_return_val_if_fail (out != NULL, FALSE); \ g_return_val_if_fail (n_out != NULL, FALSE); \ \ { \ PROPERTY_LOCK_AND_REFRESH (property_name) \ return garray_to_array (self->priv->property_name, out, n_out); \ } \ } /* Getter implementation for arrays of simple types */ #define PROPERTY_ARRAY_DEFINE_GET(property_name,Type,type,TYPE,ArrayItemType) \ gboolean \ mm_##type##_get_##property_name (MM##Type *self, \ ArrayItemType **out, \ guint *n_out) \ { \ g_return_val_if_fail (MM_IS_##TYPE (self), FALSE); \ g_return_val_if_fail (out != NULL, FALSE); \ g_return_val_if_fail (n_out != NULL, FALSE); \ \ { \ PROPERTY_LOCK_AND_REFRESH (property_name) \ if (!self->priv->property_name) \ return FALSE; \ \ *out = NULL; \ *n_out = self->priv->property_name->len; \ if (self->priv->property_name->len > 0) \ *out = g_memdup (self->priv->property_name->data, \ (guint)(sizeof (ArrayItemType) * self->priv->property_name->len)); \ return TRUE; \ } \ } /* Peeker implementation for arrays of any type */ #define PROPERTY_ARRAY_DEFINE_PEEK(property_name,Type,type,TYPE,ArrayItemType) \ gboolean \ mm_##type##_peek_##property_name (MM##Type *self, \ const ArrayItemType **out, \ guint *n_out) \ { \ g_return_val_if_fail (MM_IS_##TYPE (self), FALSE); \ g_return_val_if_fail (out != NULL, FALSE); \ g_return_val_if_fail (n_out != NULL, FALSE); \ \ { \ PROPERTY_LOCK_AND_REFRESH (property_name) \ \ if (!self->priv->property_name) \ return FALSE; \ \ *n_out = self->priv->property_name->len; \ *out = (ArrayItemType *)self->priv->property_name->data; \ return TRUE; \ } \ } /* Get implementations for object properties */ #define PROPERTY_OBJECT_DEFINE_GET(property_name,object_name,Type,type,TYPE,ObjectType) \ ObjectType * \ mm_##type##_get_##object_name (MM##Type *self) \ { \ g_return_val_if_fail (MM_IS_##TYPE (self), NULL); \ { \ PROPERTY_LOCK_AND_REFRESH (property_name) \ return (self->priv->object_name ? \ g_object_ref (self->priv->object_name) : \ NULL); \ } \ } /* Peek implementations for object properties */ #define PROPERTY_OBJECT_DEFINE_PEEK(property_name,object_name,Type,type,TYPE,ObjectType) \ ObjectType * \ mm_##type##_peek_##object_name (MM##Type *self) \ { \ g_return_val_if_fail (MM_IS_##TYPE (self), NULL); \ { \ PROPERTY_LOCK_AND_REFRESH (property_name) \ return self->priv->object_name; \ } \ } /* Get implementations for error properties */ #define PROPERTY_ERROR_DEFINE_GET(property_name,Type,type,TYPE) \ GError * \ mm_##type##_get_##property_name (MM##Type *self) \ { \ g_return_val_if_fail (MM_IS_##TYPE (self), NULL); \ { \ PROPERTY_LOCK_AND_REFRESH (property_name) \ return (self->priv->property_name ? \ g_error_copy (self->priv->property_name) : \ NULL); \ } \ } /* Peek implementations for error properties */ #define PROPERTY_ERROR_DEFINE_PEEK(property_name,Type,type,TYPE) \ GError * \ mm_##type##_peek_##property_name (MM##Type *self) \ { \ g_return_val_if_fail (MM_IS_##TYPE (self), NULL); \ { \ PROPERTY_LOCK_AND_REFRESH (property_name) \ return self->priv->property_name; \ } \ } #define PROPERTY_ARRAY_DEFINE(property_name,Type,type,TYPE,ArrayItemType,variant_to_garray) \ PROPERTY_ARRAY_DEFINE_REFRESH (property_name, Type, type, TYPE, variant_to_garray) \ PROPERTY_DEFINE_UPDATED (property_name, Type) \ PROPERTY_ARRAY_DEFINE_GET (property_name, Type, type, TYPE, ArrayItemType) \ PROPERTY_ARRAY_DEFINE_PEEK (property_name, Type, type, TYPE, ArrayItemType) #define PROPERTY_ARRAY_DEFINE_DEEP(property_name,Type,type,TYPE,ArrayItemType,variant_to_garray,garray_to_array) \ PROPERTY_ARRAY_DEFINE_REFRESH (property_name, Type, type, TYPE, variant_to_garray) \ PROPERTY_DEFINE_UPDATED (property_name, Type) \ PROPERTY_ARRAY_DEFINE_GET_DEEP (property_name, Type, type, TYPE, ArrayItemType, garray_to_array) \ PROPERTY_ARRAY_DEFINE_PEEK (property_name, Type, type, TYPE, ArrayItemType) #define PROPERTY_OBJECT_DEFINE(property_name,Type,type,TYPE,ObjectType,variant_to_object) \ PROPERTY_OBJECT_DEFINE_REFRESH (property_name, Type, type, TYPE, variant_to_object) \ PROPERTY_DEFINE_UPDATED (property_name, Type) \ PROPERTY_OBJECT_DEFINE_GET (property_name, property_name, Type, type, TYPE, ObjectType) \ PROPERTY_OBJECT_DEFINE_PEEK (property_name, property_name, Type, type, TYPE, ObjectType) #define PROPERTY_OBJECT_DEFINE_FAILABLE(property_name,Type,type,TYPE,ObjectType,variant_to_object) \ PROPERTY_OBJECT_DEFINE_REFRESH_FAILABLE (property_name, Type, type, TYPE, variant_to_object) \ PROPERTY_DEFINE_UPDATED (property_name, Type) \ PROPERTY_OBJECT_DEFINE_GET (property_name, property_name, Type, type, TYPE, ObjectType) \ PROPERTY_OBJECT_DEFINE_PEEK (property_name, property_name, Type, type, TYPE, ObjectType) #define PROPERTY_ERROR_DEFINE_FAILABLE(property_name,Type,type,TYPE,variant_to_error) \ PROPERTY_ERROR_DEFINE_REFRESH_FAILABLE (property_name, Type, type, TYPE, variant_to_error) \ PROPERTY_DEFINE_UPDATED (property_name, Type) \ PROPERTY_ERROR_DEFINE_GET (property_name, Type, type, TYPE) \ PROPERTY_ERROR_DEFINE_PEEK (property_name, Type, type, TYPE) #endif /* _MM_HELPERS_H_ */ ModemManager-1.23.4-dev/libmm-glib/mm-kernel-event-properties.c000066400000000000000000000322261456466623000243110ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * libmm-glib -- Access modem status & information from glib applications * * 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 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 Street, Fifth Floor, * Boston, MA 02110-1301 USA. * * Copyright (C) 2016 Velocloud, Inc. */ #include #include #include #include "mm-errors-types.h" #include "mm-enums-types.h" #include "mm-flags-types.h" #include "mm-common-helpers.h" #include "mm-kernel-event-properties.h" /** * SECTION: mm-kernel-event-properties * @title: MMKernelEventProperties * @short_description: Helper object to handle kernel event properties. * * The #MMKernelEventProperties is an object handling the properties to be set * in reported kernel events. * * This object is created by the user and passed to ModemManager with either * mm_manager_report_kernel_event() or mm_manager_report_kernel_event_sync(). */ G_DEFINE_TYPE (MMKernelEventProperties, mm_kernel_event_properties, G_TYPE_OBJECT) #define PROPERTY_ACTION "action" #define PROPERTY_SUBSYSTEM "subsystem" #define PROPERTY_NAME "name" #define PROPERTY_UID "uid" struct _MMKernelEventPropertiesPrivate { gchar *action; gchar *subsystem; gchar *name; gchar *uid; }; /*****************************************************************************/ /** * mm_kernel_event_properties_set_action: * @self: A #MMKernelEventProperties. * @action: The action to set. * * Sets the action. * * Since: 1.8 */ void mm_kernel_event_properties_set_action (MMKernelEventProperties *self, const gchar *action) { g_return_if_fail (MM_IS_KERNEL_EVENT_PROPERTIES (self)); g_free (self->priv->action); self->priv->action = g_strdup (action); } /** * mm_kernel_event_properties_get_action: * @self: A #MMKernelEventProperties. * * Gets the action. * * Returns: (transfer none): The action. Do not free the returned value, it is * owned by @self. * * Since: 1.8 */ const gchar * mm_kernel_event_properties_get_action (MMKernelEventProperties *self) { g_return_val_if_fail (MM_IS_KERNEL_EVENT_PROPERTIES (self), NULL); return self->priv->action; } /*****************************************************************************/ /** * mm_kernel_event_properties_set_subsystem: * @self: A #MMKernelEventProperties. * @subsystem: The subsystem to set. * * Sets the subsystem. * * Since: 1.8 */ void mm_kernel_event_properties_set_subsystem (MMKernelEventProperties *self, const gchar *subsystem) { g_return_if_fail (MM_IS_KERNEL_EVENT_PROPERTIES (self)); g_free (self->priv->subsystem); self->priv->subsystem = g_strdup (subsystem); } /** * mm_kernel_event_properties_get_subsystem: * @self: A #MMKernelEventProperties. * * Gets the subsystem. * * Returns: (transfer none): The subsystem. Do not free the returned value, it * is owned by @self. * * Since: 1.8 */ const gchar * mm_kernel_event_properties_get_subsystem (MMKernelEventProperties *self) { g_return_val_if_fail (MM_IS_KERNEL_EVENT_PROPERTIES (self), NULL); return self->priv->subsystem; } /*****************************************************************************/ /** * mm_kernel_event_properties_set_name: * @self: A #MMKernelEventProperties. * @name: The name to set. * * Sets the name. * * Since: 1.8 */ void mm_kernel_event_properties_set_name (MMKernelEventProperties *self, const gchar *name) { g_return_if_fail (MM_IS_KERNEL_EVENT_PROPERTIES (self)); g_free (self->priv->name); self->priv->name = g_strdup (name); } /** * mm_kernel_event_properties_get_name: * @self: A #MMKernelEventProperties. * * Gets the name. * * Returns: (transfer none): The name. Do not free the returned value, it is * owned by @self. * * Since: 1.8 */ const gchar * mm_kernel_event_properties_get_name (MMKernelEventProperties *self) { g_return_val_if_fail (MM_IS_KERNEL_EVENT_PROPERTIES (self), NULL); return self->priv->name; } /*****************************************************************************/ /** * mm_kernel_event_properties_set_uid: * @self: A #MMKernelEventProperties. * @uid: The uid to set. * * Sets the unique ID of the physical device. * * Since: 1.8 */ void mm_kernel_event_properties_set_uid (MMKernelEventProperties *self, const gchar *uid) { g_return_if_fail (MM_IS_KERNEL_EVENT_PROPERTIES (self)); g_free (self->priv->uid); self->priv->uid = g_strdup (uid); } /** * mm_kernel_event_properties_get_uid: * @self: A #MMKernelEventProperties. * * Gets the unique ID of the physical device. * * Returns: (transfer none): The uid. Do not free the returned value, it is * owned by @self. * * Since: 1.8 */ const gchar * mm_kernel_event_properties_get_uid (MMKernelEventProperties *self) { g_return_val_if_fail (MM_IS_KERNEL_EVENT_PROPERTIES (self), NULL); return self->priv->uid; } /*****************************************************************************/ /** * mm_kernel_event_properties_get_dictionary: (skip) */ GVariant * mm_kernel_event_properties_get_dictionary (MMKernelEventProperties *self) { GVariantBuilder builder; /* We do allow NULL */ if (!self) return NULL; g_return_val_if_fail (MM_IS_KERNEL_EVENT_PROPERTIES (self), NULL); g_variant_builder_init (&builder, G_VARIANT_TYPE ("a{sv}")); if (self->priv->action) g_variant_builder_add (&builder, "{sv}", PROPERTY_ACTION, g_variant_new_string (self->priv->action)); if (self->priv->subsystem) g_variant_builder_add (&builder, "{sv}", PROPERTY_SUBSYSTEM, g_variant_new_string (self->priv->subsystem)); if (self->priv->name) g_variant_builder_add (&builder, "{sv}", PROPERTY_NAME, g_variant_new_string (self->priv->name)); if (self->priv->uid) g_variant_builder_add (&builder, "{sv}", PROPERTY_UID, g_variant_new_string (self->priv->uid)); return g_variant_ref_sink (g_variant_builder_end (&builder)); } /*****************************************************************************/ static gboolean consume_string (MMKernelEventProperties *self, const gchar *key, const gchar *value, GError **error) { if (g_str_equal (key, PROPERTY_ACTION)) mm_kernel_event_properties_set_action (self, value); else if (g_str_equal (key, PROPERTY_SUBSYSTEM)) mm_kernel_event_properties_set_subsystem (self, value); else if (g_str_equal (key, PROPERTY_NAME)) mm_kernel_event_properties_set_name (self, value); else if (g_str_equal (key, PROPERTY_UID)) mm_kernel_event_properties_set_uid (self, value); else { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS, "Invalid properties string, unexpected key '%s'", key); return FALSE; } return TRUE; } typedef struct { MMKernelEventProperties *properties; GError *error; } ParseKeyValueContext; static gboolean key_value_foreach (const gchar *key, const gchar *value, ParseKeyValueContext *ctx) { return consume_string (ctx->properties, key, value, &ctx->error); } /** * mm_kernel_event_properties_new_from_string: (skip) */ MMKernelEventProperties * mm_kernel_event_properties_new_from_string (const gchar *str, GError **error) { ParseKeyValueContext ctx; ctx.properties = mm_kernel_event_properties_new (); ctx.error = NULL; mm_common_parse_key_value_string (str, &ctx.error, (MMParseKeyValueForeachFn) key_value_foreach, &ctx); /* If error, destroy the object */ if (ctx.error) { g_propagate_error (error, ctx.error); g_object_unref (ctx.properties); ctx.properties = NULL; } return ctx.properties; } /*****************************************************************************/ static gboolean consume_variant (MMKernelEventProperties *properties, const gchar *key, GVariant *value, GError **error) { if (g_str_equal (key, PROPERTY_ACTION)) mm_kernel_event_properties_set_action ( properties, g_variant_get_string (value, NULL)); else if (g_str_equal (key, PROPERTY_SUBSYSTEM)) mm_kernel_event_properties_set_subsystem ( properties, g_variant_get_string (value, NULL)); else if (g_str_equal (key, PROPERTY_NAME)) mm_kernel_event_properties_set_name ( properties, g_variant_get_string (value, NULL)); else if (g_str_equal (key, PROPERTY_UID)) mm_kernel_event_properties_set_uid ( properties, g_variant_get_string (value, NULL)); else { /* Set error */ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS, "Invalid properties dictionary, unexpected key '%s'", key); return FALSE; } return TRUE; } /** * mm_kernel_event_properties_new_from_dictionary: (skip) */ MMKernelEventProperties * mm_kernel_event_properties_new_from_dictionary (GVariant *dictionary, GError **error) { GError *inner_error = NULL; GVariantIter iter; gchar *key; GVariant *value; MMKernelEventProperties *properties; properties = mm_kernel_event_properties_new (); if (!dictionary) return properties; if (!g_variant_is_of_type (dictionary, G_VARIANT_TYPE ("a{sv}"))) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS, "Cannot create kernel event properties from dictionary: " "invalid variant type received"); g_object_unref (properties); return NULL; } g_variant_iter_init (&iter, dictionary); while (!inner_error && g_variant_iter_next (&iter, "{sv}", &key, &value)) { consume_variant (properties, key, value, &inner_error); g_free (key); g_variant_unref (value); } /* If error, destroy the object */ if (inner_error) { g_propagate_error (error, inner_error); g_object_unref (properties); properties = NULL; } return properties; } /*****************************************************************************/ /** * mm_kernel_event_properties_new: * * Creates a new empty #MMKernelEventProperties. * * Returns: (transfer full): a #MMKernelEventProperties. The returned value * should be freed with g_object_unref(). * * Since: 1.8 */ MMKernelEventProperties * mm_kernel_event_properties_new (void) { return (MM_KERNEL_EVENT_PROPERTIES (g_object_new (MM_TYPE_KERNEL_EVENT_PROPERTIES, NULL))); } static void mm_kernel_event_properties_init (MMKernelEventProperties *self) { self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, MM_TYPE_KERNEL_EVENT_PROPERTIES, MMKernelEventPropertiesPrivate); } static void finalize (GObject *object) { MMKernelEventProperties *self = MM_KERNEL_EVENT_PROPERTIES (object); g_free (self->priv->action); g_free (self->priv->subsystem); g_free (self->priv->name); g_free (self->priv->uid); G_OBJECT_CLASS (mm_kernel_event_properties_parent_class)->finalize (object); } static void mm_kernel_event_properties_class_init (MMKernelEventPropertiesClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); g_type_class_add_private (object_class, sizeof (MMKernelEventPropertiesPrivate)); object_class->finalize = finalize; } ModemManager-1.23.4-dev/libmm-glib/mm-kernel-event-properties.h000066400000000000000000000110411456466623000243060ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * libmm-glib -- Access modem status & information from glib applications * * 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 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 Street, Fifth Floor, * Boston, MA 02110-1301 USA. * * Copyright (C) 2016 Velocloud, Inc. */ #ifndef MM_KERNEL_EVENT_PROPERTIES_H #define MM_KERNEL_EVENT_PROPERTIES_H #if !defined (__LIBMM_GLIB_H_INSIDE__) && !defined (LIBMM_GLIB_COMPILATION) #error "Only can be included directly." #endif #include #include G_BEGIN_DECLS #define MM_TYPE_KERNEL_EVENT_PROPERTIES (mm_kernel_event_properties_get_type ()) #define MM_KERNEL_EVENT_PROPERTIES(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_KERNEL_EVENT_PROPERTIES, MMKernelEventProperties)) #define MM_KERNEL_EVENT_PROPERTIES_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_KERNEL_EVENT_PROPERTIES, MMKernelEventPropertiesClass)) #define MM_IS_KERNEL_EVENT_PROPERTIES(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_KERNEL_EVENT_PROPERTIES)) #define MM_IS_KERNEL_EVENT_PROPERTIES_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_KERNEL_EVENT_PROPERTIES)) #define MM_KERNEL_EVENT_PROPERTIES_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_KERNEL_EVENT_PROPERTIES, MMKernelEventPropertiesClass)) typedef struct _MMKernelEventProperties MMKernelEventProperties; typedef struct _MMKernelEventPropertiesClass MMKernelEventPropertiesClass; typedef struct _MMKernelEventPropertiesPrivate MMKernelEventPropertiesPrivate; /** * MMKernelEventProperties: * * The #MMKernelEventProperties structure contains private data and should only be * accessed using the provided API. */ struct _MMKernelEventProperties { /*< private >*/ GObject parent; MMKernelEventPropertiesPrivate *priv; }; struct _MMKernelEventPropertiesClass { /*< private >*/ GObjectClass parent; }; GType mm_kernel_event_properties_get_type (void); G_DEFINE_AUTOPTR_CLEANUP_FUNC (MMKernelEventProperties, g_object_unref) MMKernelEventProperties *mm_kernel_event_properties_new (void); void mm_kernel_event_properties_set_action (MMKernelEventProperties *self, const gchar *action); const gchar *mm_kernel_event_properties_get_action (MMKernelEventProperties *self); void mm_kernel_event_properties_set_subsystem (MMKernelEventProperties *self, const gchar *subsystem); const gchar *mm_kernel_event_properties_get_subsystem (MMKernelEventProperties *self); void mm_kernel_event_properties_set_name (MMKernelEventProperties *self, const gchar *name); const gchar *mm_kernel_event_properties_get_name (MMKernelEventProperties *self); void mm_kernel_event_properties_set_uid (MMKernelEventProperties *self, const gchar *uid); const gchar *mm_kernel_event_properties_get_uid (MMKernelEventProperties *self); /*****************************************************************************/ /* ModemManager/libmm-glib/mmcli specific methods */ #if defined (_LIBMM_INSIDE_MM) || \ defined (_LIBMM_INSIDE_MMCLI) || \ defined (LIBMM_GLIB_COMPILATION) MMKernelEventProperties *mm_kernel_event_properties_new_from_string (const gchar *str, GError **error); MMKernelEventProperties *mm_kernel_event_properties_new_from_dictionary (GVariant *dictionary, GError **error); GVariant *mm_kernel_event_properties_get_dictionary (MMKernelEventProperties *self); #endif G_END_DECLS #endif /* MM_KERNEL_EVENT_PROPERTIES_H */ ModemManager-1.23.4-dev/libmm-glib/mm-location-3gpp.c000066400000000000000000000330371456466623000222000ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * libmm-glib -- Access modem status & information from glib applications * * 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 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 Street, Fifth Floor, * Boston, MA 02110-1301 USA. * * Copyright (C) 2012 Google, Inc. */ #include #include #include #include "mm-errors-types.h" #include "mm-common-helpers.h" #include "mm-location-3gpp.h" /** * SECTION: mm-location-3gpp * @title: MMLocation3gpp * @short_description: Helper object to handle 3GPP location information. * * The #MMLocation3gpp is an object handling the location information of the * modem when this is reported by the 3GPP network. * * This object is retrieved with either mm_modem_location_get_3gpp(), * mm_modem_location_get_3gpp_sync(), mm_modem_location_get_full() or * mm_modem_location_get_full_sync(). */ G_DEFINE_TYPE (MMLocation3gpp, mm_location_3gpp, G_TYPE_OBJECT); struct _MMLocation3gppPrivate { gchar *operator_code; gulong location_area_code; gulong cell_id; gulong tracking_area_code; }; /*****************************************************************************/ static gboolean validate_string_length (const gchar *display, const gchar *str, guint min_length, guint max_length, GError **error) { /* Avoid empty strings */ if (!str || !str[0]) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS, "Invalid %s: none given", display); return FALSE; } /* Check min length of the field */ if (strlen (str) < min_length) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS, "Invalid %s: shorter than the maximum expected (%u): '%s'", display, min_length, str); return FALSE; } /* Check max length of the field */ if (strlen (str) > max_length) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS, "Invalid %s: longer than the maximum expected (%u): '%s'", display, max_length, str); return FALSE; } return TRUE; } static gboolean validate_numeric_string_content (const gchar *display, const gchar *str, gboolean hex, GError **error) { guint i; for (i = 0; str[i]; i++) { if ((hex && !g_ascii_isxdigit (str[i])) || (!hex && !g_ascii_isdigit (str[i]))) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS, "Invalid %s: unexpected char (%c): '%s'", display, str[i], str); return FALSE; } } return TRUE; } /*****************************************************************************/ /** * mm_location_3gpp_get_mobile_country_code: * @self: a #MMLocation3gpp. * * Gets the Mobile Country Code of the 3GPP network. * * Returns: the MCC, or 0 if unknown. * * Since: 1.0 */ guint mm_location_3gpp_get_mobile_country_code (MMLocation3gpp *self) { gchar mcc[4]; g_return_val_if_fail (MM_IS_LOCATION_3GPP (self), 0); if (!self->priv->operator_code) return 0; memcpy (mcc, self->priv->operator_code, 3); mcc[3] = '\0'; return strtol (mcc, NULL, 10); } /*****************************************************************************/ /** * mm_location_3gpp_get_location_area_code: * @self: a #MMLocation3gpp. * * Gets the location area code of the 3GPP network. * * Returns: the location area code, or 0 if unknown. * * Since: 1.0 */ gulong mm_location_3gpp_get_location_area_code (MMLocation3gpp *self) { g_return_val_if_fail (MM_IS_LOCATION_3GPP (self), 0); return self->priv->location_area_code; } /** * mm_location_3gpp_set_location_area_code: (skip) */ gboolean mm_location_3gpp_set_location_area_code (MMLocation3gpp *self, gulong location_area_code) { g_return_val_if_fail (MM_IS_LOCATION_3GPP (self), FALSE); /* If no change in the location info, don't do anything */ if (self->priv->location_area_code == location_area_code) return FALSE; self->priv->location_area_code = location_area_code; return TRUE; } /*****************************************************************************/ /** * mm_location_3gpp_get_cell_id: * @self: a #MMLocation3gpp. * * Gets the cell ID of the 3GPP network. * * Returns: the cell ID, or 0 if unknown. * * Since: 1.0 */ gulong mm_location_3gpp_get_cell_id (MMLocation3gpp *self) { g_return_val_if_fail (MM_IS_LOCATION_3GPP (self), 0); return self->priv->cell_id; } /** * mm_location_3gpp_set_cell_id: (skip) */ gboolean mm_location_3gpp_set_cell_id (MMLocation3gpp *self, gulong cell_id) { g_return_val_if_fail (MM_IS_LOCATION_3GPP (self), FALSE); /* If no change in the location info, don't do anything */ if (self->priv->cell_id == cell_id) return FALSE; self->priv->cell_id = cell_id; return TRUE; } /*****************************************************************************/ /** * mm_location_3gpp_get_tracking_area_code: * @self: a #MMLocation3gpp. * * Gets the location area code of the 3GPP network. * * Returns: the location area code, or 0 if unknown. * * Since: 1.10 */ gulong mm_location_3gpp_get_tracking_area_code (MMLocation3gpp *self) { g_return_val_if_fail (MM_IS_LOCATION_3GPP (self), 0); return self->priv->tracking_area_code; } /** * mm_location_3gpp_set_tracking_area_code: (skip) */ gboolean mm_location_3gpp_set_tracking_area_code (MMLocation3gpp *self, gulong tracking_area_code) { g_return_val_if_fail (MM_IS_LOCATION_3GPP (self), FALSE); /* If no change in the location info, don't do anything */ if (self->priv->tracking_area_code == tracking_area_code) return FALSE; self->priv->tracking_area_code = tracking_area_code; return TRUE; } /*****************************************************************************/ /** * mm_location_3gpp_get_operator_code: * @self: A #MMLocation3gpp. * * Gets the 3GPP network Mobile Country Code and Mobile Network Code. * * Returned in the format "MCCMNC", where * MCC is the three-digit ITU E.212 Mobile Country Code * and MNC is the two- or three-digit GSM Mobile Network * Code. e.g. e"31026" or "310260". * * Returns: (transfer none): The operator code, or %NULL if none available. * * Since: 1.18 */ const gchar * mm_location_3gpp_get_operator_code (MMLocation3gpp *self) { g_return_val_if_fail (MM_IS_LOCATION_3GPP (self), NULL); return self->priv->operator_code; } /** * mm_location_3gpp_set_operator_code: (skip) */ gboolean mm_location_3gpp_set_operator_code (MMLocation3gpp *self, const gchar *operator_code) { g_return_val_if_fail (MM_IS_LOCATION_3GPP (self), FALSE); /* If no change in operator code, don't do anything */ if (!g_strcmp0 (operator_code, self->priv->operator_code)) return FALSE; /* Check the validity here, all other functions expect it's valid. */ if (operator_code && (!validate_string_length ("MCCMNC", operator_code, 5, 6, NULL) || !validate_numeric_string_content ("MCCMNC", operator_code, FALSE, NULL))) return FALSE; g_free (self->priv->operator_code); self->priv->operator_code = g_strdup (operator_code); return TRUE; } /*****************************************************************************/ /** * mm_location_3gpp_reset: (skip) */ gboolean mm_location_3gpp_reset (MMLocation3gpp *self) { g_return_val_if_fail (MM_IS_LOCATION_3GPP (self), FALSE); if (self->priv->operator_code == NULL && self->priv->location_area_code == 0 && self->priv->tracking_area_code == 0 && self->priv->cell_id == 0) return FALSE; g_free (self->priv->operator_code); self->priv->operator_code = NULL; self->priv->location_area_code = 0; self->priv->tracking_area_code = 0; self->priv->cell_id = 0; return TRUE; } /*****************************************************************************/ /** * mm_location_3gpp_get_string_variant: (skip) */ GVariant * mm_location_3gpp_get_string_variant (MMLocation3gpp *self) { GVariant *variant = NULL; g_return_val_if_fail (MM_IS_LOCATION_3GPP (self), NULL); if (self->priv->operator_code && (self->priv->location_area_code || self->priv->tracking_area_code) && self->priv->cell_id) { gchar *str; str = g_strdup_printf ("%.3s,%s,%lX,%lX,%lX", self->priv->operator_code, self->priv->operator_code + 3, self->priv->location_area_code, self->priv->cell_id, self->priv->tracking_area_code); variant = g_variant_ref_sink (g_variant_new_string (str)); g_free (str); } return variant; } /*****************************************************************************/ /** * mm_location_3gpp_new_from_string_variant: (skip) */ MMLocation3gpp * mm_location_3gpp_new_from_string_variant (GVariant *string, GError **error) { MMLocation3gpp *self = NULL; gchar **split; if (!g_variant_is_of_type (string, G_VARIANT_TYPE_STRING)) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS, "Cannot create 3GPP location from string: " "invalid variant type received"); return NULL; } split = g_strsplit (g_variant_get_string (string, NULL), ",", -1); if (!split) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS, "Invalid 3GPP location string: '%s'", g_variant_get_string (string, NULL)); return NULL; } /* Validate fields */ if (validate_string_length ("MCC", split[0], 0, 3, error) && validate_numeric_string_content ("MCC", split[0], FALSE, error) && validate_string_length ("MNC", split[1], 0, 3, error) && validate_numeric_string_content ("MNC", split[1], FALSE, error) && validate_string_length ("Location area code", split[2], 0, 4, error) && validate_numeric_string_content ("Location area code", split[2], TRUE, error) && validate_string_length ("Cell ID", split[3], 0, 8, error) && validate_numeric_string_content ("Cell ID", split[3], TRUE, error) && validate_string_length ("Tracking area code", split[4], 0, 8, error) && validate_numeric_string_content ("Tracking area code", split[4], TRUE, error)) { /* Create new location object */ self = mm_location_3gpp_new (); /* Join MCC and MNC and ensure they are zero-padded to required widths */ self->priv->operator_code = g_strdup_printf ("%03lu%0*lu", strtoul (split[0], NULL, 10), strlen (split[1]) == 3 ? 3 : 2, strtoul (split[1], NULL, 10)); self->priv->location_area_code = strtoul (split[2], NULL, 16); self->priv->cell_id = strtoul (split[3], NULL, 16); self->priv->tracking_area_code = strtoul (split[4], NULL, 16); } g_strfreev (split); return self; } /*****************************************************************************/ /** * mm_location_3gpp_new: (skip) */ MMLocation3gpp * mm_location_3gpp_new (void) { return (MM_LOCATION_3GPP ( g_object_new (MM_TYPE_LOCATION_3GPP, NULL))); } static void mm_location_3gpp_init (MMLocation3gpp *self) { self->priv = G_TYPE_INSTANCE_GET_PRIVATE ((self), MM_TYPE_LOCATION_3GPP, MMLocation3gppPrivate); } static void finalize (GObject *object) { MMLocation3gpp *self = MM_LOCATION_3GPP (object); g_free (self->priv->operator_code); G_OBJECT_CLASS (mm_location_3gpp_parent_class)->finalize (object); } static void mm_location_3gpp_class_init (MMLocation3gppClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); g_type_class_add_private (object_class, sizeof (MMLocation3gppPrivate)); object_class->finalize = finalize; } ModemManager-1.23.4-dev/libmm-glib/mm-location-3gpp.h000066400000000000000000000077301456466623000222060ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * libmm-glib -- Access modem status & information from glib applications * * 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 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 Street, Fifth Floor, * Boston, MA 02110-1301 USA. * * Copyright (C) 2012 Google, Inc. */ #ifndef MM_LOCATION_3GPP_H #define MM_LOCATION_3GPP_H #if !defined (__LIBMM_GLIB_H_INSIDE__) && !defined (LIBMM_GLIB_COMPILATION) #error "Only can be included directly." #endif #include #include G_BEGIN_DECLS #define MM_TYPE_LOCATION_3GPP (mm_location_3gpp_get_type ()) #define MM_LOCATION_3GPP(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_LOCATION_3GPP, MMLocation3gpp)) #define MM_LOCATION_3GPP_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_LOCATION_3GPP, MMLocation3gppClass)) #define MM_IS_LOCATION_3GPP(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_LOCATION_3GPP)) #define MM_IS_LOCATION_3GPP_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_LOCATION_3GPP)) #define MM_LOCATION_3GPP_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_LOCATION_3GPP, MMLocation3gppClass)) typedef struct _MMLocation3gpp MMLocation3gpp; typedef struct _MMLocation3gppClass MMLocation3gppClass; typedef struct _MMLocation3gppPrivate MMLocation3gppPrivate; /** * MMLocation3gpp: * * The #MMLocation3gpp structure contains private data and should * only be accessed using the provided API. */ struct _MMLocation3gpp { /*< private >*/ GObject parent; MMLocation3gppPrivate *priv; }; struct _MMLocation3gppClass { /*< private >*/ GObjectClass parent; }; GType mm_location_3gpp_get_type (void); G_DEFINE_AUTOPTR_CLEANUP_FUNC (MMLocation3gpp, g_object_unref) guint mm_location_3gpp_get_mobile_country_code (MMLocation3gpp *self); gulong mm_location_3gpp_get_location_area_code (MMLocation3gpp *self); gulong mm_location_3gpp_get_cell_id (MMLocation3gpp *self); gulong mm_location_3gpp_get_tracking_area_code (MMLocation3gpp *self); const gchar *mm_location_3gpp_get_operator_code (MMLocation3gpp *self); /*****************************************************************************/ /* ModemManager/libmm-glib/mmcli specific methods */ #if defined (_LIBMM_INSIDE_MM) || \ defined (_LIBMM_INSIDE_MMCLI) || \ defined (LIBMM_GLIB_COMPILATION) GVariant *mm_location_3gpp_get_string_variant (MMLocation3gpp *self); MMLocation3gpp *mm_location_3gpp_new (void); MMLocation3gpp *mm_location_3gpp_new_from_string_variant (GVariant *string, GError **error); gboolean mm_location_3gpp_set_operator_code (MMLocation3gpp *self, const gchar *operator_code); gboolean mm_location_3gpp_set_location_area_code (MMLocation3gpp *self, gulong location_area_code); gboolean mm_location_3gpp_set_cell_id (MMLocation3gpp *self, gulong cell_id); gboolean mm_location_3gpp_set_tracking_area_code (MMLocation3gpp *self, gulong tracking_area_code); gboolean mm_location_3gpp_reset (MMLocation3gpp *self); #endif G_END_DECLS #endif /* MM_LOCATION_3GPP_H */ ModemManager-1.23.4-dev/libmm-glib/mm-location-cdma-bs.c000066400000000000000000000170361456466623000226360ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * libmm-glib -- Access modem status & information from glib applications * * 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 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 Street, Fifth Floor, * Boston, MA 02110-1301 USA. * * Copyright (C) 2012 Lanedo GmbH */ #include #include #include #include "mm-common-helpers.h" #include "mm-errors-types.h" #include "mm-location-cdma-bs.h" /** * SECTION: mm-location-cdma-bs * @title: MMLocationCdmaBs * @short_description: Helper object to handle CDMA Base Station location information. * * The #MMLocationCdmaBs is an object handling the location information of the * CDMA base station in which the modem is registered. * * This object is retrieved with either mm_modem_location_get_cdma_bs(), * mm_modem_location_get_cdma_bs_sync(), mm_modem_location_get_full() or * mm_modem_location_get_full_sync(). */ G_DEFINE_TYPE (MMLocationCdmaBs, mm_location_cdma_bs, G_TYPE_OBJECT); #define PROPERTY_LATITUDE "latitude" #define PROPERTY_LONGITUDE "longitude" struct _MMLocationCdmaBsPrivate { gdouble latitude; gdouble longitude; }; /*****************************************************************************/ /** * mm_location_cdma_bs_get_longitude: * @self: a #MMLocationCdmaBs. * * Gets the longitude, in the [-180,180] range. * * Returns: the longitude, or %MM_LOCATION_LONGITUDE_UNKNOWN if unknown. * * Since: 1.0 */ gdouble mm_location_cdma_bs_get_longitude (MMLocationCdmaBs *self) { g_return_val_if_fail (MM_IS_LOCATION_CDMA_BS (self), MM_LOCATION_LONGITUDE_UNKNOWN); return self->priv->longitude; } /*****************************************************************************/ /** * mm_location_cdma_bs_get_latitude: * @self: a #MMLocationCdmaBs. * * Gets the latitude, in the [-90,90] range. * * Returns: the latitude, or %MM_LOCATION_LATITUDE_UNKNOWN if unknown. * * Since: 1.0 */ gdouble mm_location_cdma_bs_get_latitude (MMLocationCdmaBs *self) { g_return_val_if_fail (MM_IS_LOCATION_CDMA_BS (self), MM_LOCATION_LATITUDE_UNKNOWN); return self->priv->latitude; } /*****************************************************************************/ /** * mm_location_cdma_bs_set: (skip) */ gboolean mm_location_cdma_bs_set (MMLocationCdmaBs *self, gdouble longitude, gdouble latitude) { g_return_val_if_fail ((longitude == MM_LOCATION_LONGITUDE_UNKNOWN || (longitude >= -180.0 && longitude <= 180.0)), FALSE); g_return_val_if_fail ((latitude == MM_LOCATION_LATITUDE_UNKNOWN || (latitude >= -90.0 && latitude <= 90.0)), FALSE); if (self->priv->longitude == longitude && self->priv->latitude == latitude) return FALSE; self->priv->longitude = longitude; self->priv->latitude = latitude; return TRUE; } /*****************************************************************************/ /** * mm_location_cdma_bs_get_dictionary: (skip) */ GVariant * mm_location_cdma_bs_get_dictionary (MMLocationCdmaBs *self) { GVariantBuilder builder; /* We do allow NULL */ if (!self) return NULL; g_return_val_if_fail (MM_IS_LOCATION_CDMA_BS (self), NULL); /* If mandatory parameters are not found, return NULL */ if (self->priv->longitude == MM_LOCATION_LONGITUDE_UNKNOWN || self->priv->latitude == MM_LOCATION_LATITUDE_UNKNOWN) return NULL; g_variant_builder_init (&builder, G_VARIANT_TYPE ("a{sv}")); g_variant_builder_add (&builder, "{sv}", PROPERTY_LONGITUDE, g_variant_new_double (self->priv->longitude)); g_variant_builder_add (&builder, "{sv}", PROPERTY_LATITUDE, g_variant_new_double (self->priv->latitude)); return g_variant_ref_sink (g_variant_builder_end (&builder)); } /*****************************************************************************/ /** * mm_location_cdma_bs_new_from_dictionary: (skip) */ MMLocationCdmaBs * mm_location_cdma_bs_new_from_dictionary (GVariant *dictionary, GError **error) { GError *inner_error = NULL; MMLocationCdmaBs *self; GVariantIter iter; gchar *key; GVariant *value; self = mm_location_cdma_bs_new (); if (!dictionary) return self; if (!g_variant_is_of_type (dictionary, G_VARIANT_TYPE ("a{sv}"))) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS, "Cannot create CDMA BS location from dictionary: " "invalid variant type received"); g_object_unref (self); return NULL; } g_variant_iter_init (&iter, dictionary); while (!inner_error && g_variant_iter_next (&iter, "{sv}", &key, &value)) { if (g_str_equal (key, PROPERTY_LONGITUDE)) self->priv->longitude = g_variant_get_double (value); else if (g_str_equal (key, PROPERTY_LATITUDE)) self->priv->latitude = g_variant_get_double (value); g_free (key); g_variant_unref (value); } /* If any of the mandatory parameters is missing, cleanup */ if (self->priv->longitude == MM_LOCATION_LONGITUDE_UNKNOWN || self->priv->latitude == MM_LOCATION_LATITUDE_UNKNOWN) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS, "Cannot create CDMA BS location from dictionary: " "mandatory parameters missing " "(longitude: %s, latitude: %s)", (self->priv->longitude != MM_LOCATION_LONGITUDE_UNKNOWN) ? "yes" : "missing", (self->priv->latitude != MM_LOCATION_LATITUDE_UNKNOWN) ? "yes" : "missing"); g_clear_object (&self); } return self; } /*****************************************************************************/ /** * mm_location_cdma_bs_new: (skip) */ MMLocationCdmaBs * mm_location_cdma_bs_new (void) { return (MM_LOCATION_CDMA_BS ( g_object_new (MM_TYPE_LOCATION_CDMA_BS, NULL))); } static void mm_location_cdma_bs_init (MMLocationCdmaBs *self) { self->priv = G_TYPE_INSTANCE_GET_PRIVATE ((self), MM_TYPE_LOCATION_CDMA_BS, MMLocationCdmaBsPrivate); self->priv->latitude = MM_LOCATION_LATITUDE_UNKNOWN; self->priv->longitude = MM_LOCATION_LONGITUDE_UNKNOWN; } static void mm_location_cdma_bs_class_init (MMLocationCdmaBsClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); g_type_class_add_private (object_class, sizeof (MMLocationCdmaBsPrivate)); } ModemManager-1.23.4-dev/libmm-glib/mm-location-cdma-bs.h000066400000000000000000000065651456466623000226500ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * libmm-glib -- Access modem status & information from glib applications * * 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 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 Street, Fifth Floor, * Boston, MA 02110-1301 USA. * * Copyright (C) 2012 Lanedo GmbH */ #ifndef MM_LOCATION_CDMA_BS_H #define MM_LOCATION_CDMA_BS_H #if !defined (__LIBMM_GLIB_H_INSIDE__) && !defined (LIBMM_GLIB_COMPILATION) #error "Only can be included directly." #endif #include #include #include "mm-location-common.h" G_BEGIN_DECLS #define MM_TYPE_LOCATION_CDMA_BS (mm_location_cdma_bs_get_type ()) #define MM_LOCATION_CDMA_BS(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_LOCATION_CDMA_BS, MMLocationCdmaBs)) #define MM_LOCATION_CDMA_BS_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_LOCATION_CDMA_BS, MMLocationCdmaBsClass)) #define MM_IS_LOCATION_CDMA_BS(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_LOCATION_CDMA_BS)) #define MM_IS_LOCATION_CDMA_BS_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_LOCATION_CDMA_BS)) #define MM_LOCATION_CDMA_BS_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_LOCATION_CDMA_BS, MMLocationCdmaBsClass)) typedef struct _MMLocationCdmaBs MMLocationCdmaBs; typedef struct _MMLocationCdmaBsClass MMLocationCdmaBsClass; typedef struct _MMLocationCdmaBsPrivate MMLocationCdmaBsPrivate; /** * MMLocationCdmaBs: * * The #MMLocationCdmaBs structure contains private data and should * only be accessed using the provided API. */ struct _MMLocationCdmaBs { /*< private >*/ GObject parent; MMLocationCdmaBsPrivate *priv; }; struct _MMLocationCdmaBsClass { /*< private >*/ GObjectClass parent; }; GType mm_location_cdma_bs_get_type (void); G_DEFINE_AUTOPTR_CLEANUP_FUNC (MMLocationCdmaBs, g_object_unref) gdouble mm_location_cdma_bs_get_longitude (MMLocationCdmaBs *self); gdouble mm_location_cdma_bs_get_latitude (MMLocationCdmaBs *self); /*****************************************************************************/ /* ModemManager/libmm-glib/mmcli specific methods */ #if defined (_LIBMM_INSIDE_MM) || \ defined (_LIBMM_INSIDE_MMCLI) || \ defined (LIBMM_GLIB_COMPILATION) MMLocationCdmaBs *mm_location_cdma_bs_new (void); MMLocationCdmaBs *mm_location_cdma_bs_new_from_dictionary (GVariant *string, GError **error); gboolean mm_location_cdma_bs_set (MMLocationCdmaBs *self, gdouble longitude, gdouble latitude); GVariant *mm_location_cdma_bs_get_dictionary (MMLocationCdmaBs *self); #endif G_END_DECLS #endif /* MM_LOCATION_CDMA_BS_H */ ModemManager-1.23.4-dev/libmm-glib/mm-location-common.h000066400000000000000000000033361456466623000226230ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * libmm-glib -- Access modem status & information from glib applications * * 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 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 Street, Fifth Floor, * Boston, MA 02110-1301 USA. * * Copyright (C) 2012 Lanedo GmbH */ #ifndef MM_LOCATION_COMMON_H #define MM_LOCATION_COMMON_H #if !defined (__MODEM_MANAGER_H_INSIDE__) #error "Only can be included directly." #endif /** * MM_LOCATION_LONGITUDE_UNKNOWN: * * Identifier for an unknown longitude value. * * Proper longitude values fall in the [-180,180] range. * * Since: 1.0 */ #define MM_LOCATION_LONGITUDE_UNKNOWN -G_MAXDOUBLE /** * MM_LOCATION_LATITUDE_UNKNOWN: * * Identifier for an unknown latitude value. * * Proper latitude values fall in the [-90,90] range. * * Since: 1.0 */ #define MM_LOCATION_LATITUDE_UNKNOWN -G_MAXDOUBLE /** * MM_LOCATION_ALTITUDE_UNKNOWN: * * Identifier for an unknown altitude value. * * Since: 1.0 */ #define MM_LOCATION_ALTITUDE_UNKNOWN -G_MAXDOUBLE #endif /* MM_LOCATION_COMMON_H */ ModemManager-1.23.4-dev/libmm-glib/mm-location-gps-nmea.c000066400000000000000000000220621456466623000230320ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * libmm-glib -- Access modem status & information from glib applications * * 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 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 Street, Fifth Floor, * Boston, MA 02110-1301 USA. * * Copyright (C) 2012 Lanedo GmbH */ #include #include #include #include "mm-common-helpers.h" #include "mm-errors-types.h" #include "mm-location-gps-nmea.h" /** * SECTION: mm-location-gps-nmea * @title: MMLocationGpsNmea * @short_description: Helper object to handle NMEA-based GPS location information. * * The #MMLocationGpsNmea is an object handling the location information of the * modem when this is reported by GPS. * * This object is retrieved with either mm_modem_location_get_gps_nmea(), * mm_modem_location_get_gps_nmea_sync(), mm_modem_location_get_full() or * mm_modem_location_get_full_sync(). */ G_DEFINE_TYPE (MMLocationGpsNmea, mm_location_gps_nmea, G_TYPE_OBJECT) struct _MMLocationGpsNmeaPrivate { GHashTable *traces; GRegex *sequence_regex; }; /*****************************************************************************/ static gboolean check_append_or_replace (MMLocationGpsNmea *self, const gchar *trace) { g_autoptr(GMatchInfo) match_info = NULL; if (G_UNLIKELY (!self->priv->sequence_regex)) self->priv->sequence_regex = g_regex_new ("\\$..(?:ALM|GSV|RTE|SFI),(\\d),(\\d).*", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); if (g_regex_match (self->priv->sequence_regex, trace, 0, &match_info)) { guint index; /* If we don't have the first element of a sequence, append */ if (mm_get_uint_from_match_info (match_info, 2, &index) && index != 1) return TRUE; } /* By default, replace */ return FALSE; } static gboolean location_gps_nmea_take_trace (MMLocationGpsNmea *self, gchar *trace) { gchar *i; gchar *trace_type; i = strchr (trace, ','); if (!i || i == trace) { g_free (trace); return FALSE; } trace_type = g_malloc (i - trace + 1); memcpy (trace_type, trace, i - trace); trace_type[i - trace] = '\0'; /* Some traces are part of a SEQUENCE; so we need to decide whether we * completely replace the previous trace, or we append the new one to * the already existing list */ if (check_append_or_replace (self, trace)) { /* Append */ const gchar *previous; previous = g_hash_table_lookup (self->priv->traces, trace_type); if (previous) { gchar *sequence; /* Skip the trace if we already have it there */ if (strstr (previous, trace)) { g_free (trace_type); g_free (trace); return TRUE; } sequence = g_strdup_printf ("%s%s%s", previous, g_str_has_suffix (previous, "\r\n") ? "" : "\r\n", trace); g_free (trace); trace = sequence; } } g_hash_table_replace (self->priv->traces, trace_type, trace); return TRUE; } /** * mm_location_gps_nmea_add_trace: (skip) */ gboolean mm_location_gps_nmea_add_trace (MMLocationGpsNmea *self, const gchar *trace) { return location_gps_nmea_take_trace (self, g_strdup (trace)); } /*****************************************************************************/ /** * mm_location_gps_nmea_get_trace: * @self: a #MMLocationGpsNmea. * @trace_type: specific NMEA trace type to gather. * * Gets the last cached value of the specific @trace_type given. * * Returns: the NMEA trace, or %NULL if not available. Do not free the returned * value, it is owned by @self. * * Since: 1.0 */ const gchar * mm_location_gps_nmea_get_trace (MMLocationGpsNmea *self, const gchar *trace_type) { return (const gchar *)g_hash_table_lookup (self->priv->traces, trace_type); } /*****************************************************************************/ static void build_all_foreach (const gchar *trace_type, const gchar *trace, GPtrArray **built) { if (*built == NULL) *built = g_ptr_array_new (); g_ptr_array_add (*built, g_strdup (trace)); } /** * mm_location_gps_nmea_get_traces: * @self: a #MMLocationGpsNmea. * * Gets all cached traces. * * Returns: (transfer full): The list of traces, or %NULL if none available. The returned value should be freed with g_strfreev(). * Since: 1.14 */ gchar ** mm_location_gps_nmea_get_traces (MMLocationGpsNmea *self) { GPtrArray *built = NULL; g_return_val_if_fail (MM_IS_LOCATION_GPS_NMEA (self), NULL); g_hash_table_foreach (self->priv->traces, (GHFunc)build_all_foreach, &built); if (!built) return NULL; g_ptr_array_add (built, NULL); return (gchar **) g_ptr_array_free (built, FALSE); } /*****************************************************************************/ /** * mm_location_gps_nmea_get_string_variant: (skip) */ GVariant * mm_location_gps_nmea_get_string_variant (MMLocationGpsNmea *self) { g_autofree gchar *built = NULL; g_auto (GStrv) traces = NULL; g_return_val_if_fail (MM_IS_LOCATION_GPS_NMEA (self), NULL); traces = mm_location_gps_nmea_get_traces (self); if (traces) built = g_strjoinv ("\r\n", traces); return g_variant_ref_sink (g_variant_new_string (built ? built : "")); } /*****************************************************************************/ /** * mm_location_gps_nmea_new_from_string_variant: (skip) */ MMLocationGpsNmea * mm_location_gps_nmea_new_from_string_variant (GVariant *string, GError **error) { MMLocationGpsNmea *self = NULL; gchar **split; guint i; if (!g_variant_is_of_type (string, G_VARIANT_TYPE_STRING)) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS, "Cannot create GPS NMEA location from string: " "invalid variant type received"); return NULL; } split = g_strsplit (g_variant_get_string (string, NULL), "\r\n", -1); if (!split) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS, "Invalid GPS NMEA location string: '%s'", g_variant_get_string (string, NULL)); return NULL; } /* Create new location object */ self = mm_location_gps_nmea_new (); for (i = 0; split[i]; i++) { location_gps_nmea_take_trace (self, split[i]); } /* Note that the strings in the array of strings were already taken * or freed */ g_free (split); return self; } /*****************************************************************************/ /** * mm_location_gps_nmea_new: (skip) */ MMLocationGpsNmea * mm_location_gps_nmea_new (void) { return (MM_LOCATION_GPS_NMEA ( g_object_new (MM_TYPE_LOCATION_GPS_NMEA, NULL))); } static void mm_location_gps_nmea_init (MMLocationGpsNmea *self) { self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, MM_TYPE_LOCATION_GPS_NMEA, MMLocationGpsNmeaPrivate); self->priv->traces = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); } static void finalize (GObject *object) { MMLocationGpsNmea *self = MM_LOCATION_GPS_NMEA (object); g_hash_table_destroy (self->priv->traces); if (self->priv->sequence_regex) g_regex_unref (self->priv->sequence_regex); G_OBJECT_CLASS (mm_location_gps_nmea_parent_class)->finalize (object); } static void mm_location_gps_nmea_class_init (MMLocationGpsNmeaClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); g_type_class_add_private (object_class, sizeof (MMLocationGpsNmeaPrivate)); object_class->finalize = finalize; } ModemManager-1.23.4-dev/libmm-glib/mm-location-gps-nmea.h000066400000000000000000000066731456466623000230510ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * libmm-glib -- Access modem status & information from glib applications * * 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 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 Street, Fifth Floor, * Boston, MA 02110-1301 USA. * * Copyright (C) 2012 Lanedo GmbH */ #ifndef MM_LOCATION_GPS_NMEA_H #define MM_LOCATION_GPS_NMEA_H #if !defined (__LIBMM_GLIB_H_INSIDE__) && !defined (LIBMM_GLIB_COMPILATION) #error "Only can be included directly." #endif #include #include G_BEGIN_DECLS #define MM_TYPE_LOCATION_GPS_NMEA (mm_location_gps_nmea_get_type ()) #define MM_LOCATION_GPS_NMEA(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_LOCATION_GPS_NMEA, MMLocationGpsNmea)) #define MM_LOCATION_GPS_NMEA_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_LOCATION_GPS_NMEA, MMLocationGpsNmeaClass)) #define MM_IS_LOCATION_GPS_NMEA(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_LOCATION_GPS_NMEA)) #define MM_IS_LOCATION_GPS_NMEA_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_LOCATION_GPS_NMEA)) #define MM_LOCATION_GPS_NMEA_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_LOCATION_GPS_NMEA, MMLocationGpsNmeaClass)) typedef struct _MMLocationGpsNmea MMLocationGpsNmea; typedef struct _MMLocationGpsNmeaClass MMLocationGpsNmeaClass; typedef struct _MMLocationGpsNmeaPrivate MMLocationGpsNmeaPrivate; /** * MMLocationGpsNmea: * * The #MMLocationGpsNmea structure contains private data and should * only be accessed using the provided API. */ struct _MMLocationGpsNmea { /*< private >*/ GObject parent; MMLocationGpsNmeaPrivate *priv; }; struct _MMLocationGpsNmeaClass { /*< private >*/ GObjectClass parent; }; GType mm_location_gps_nmea_get_type (void); G_DEFINE_AUTOPTR_CLEANUP_FUNC (MMLocationGpsNmea, g_object_unref) const gchar *mm_location_gps_nmea_get_trace (MMLocationGpsNmea *self, const gchar *trace_type); gchar **mm_location_gps_nmea_get_traces (MMLocationGpsNmea *self); /*****************************************************************************/ /* ModemManager/libmm-glib/mmcli specific methods */ #if defined (_LIBMM_INSIDE_MM) || \ defined (_LIBMM_INSIDE_MMCLI) || \ defined (LIBMM_GLIB_COMPILATION) MMLocationGpsNmea *mm_location_gps_nmea_new (void); MMLocationGpsNmea *mm_location_gps_nmea_new_from_string_variant (GVariant *string, GError **error); gboolean mm_location_gps_nmea_add_trace (MMLocationGpsNmea *self, const gchar *trace); GVariant *mm_location_gps_nmea_get_string_variant (MMLocationGpsNmea *self); #endif G_END_DECLS #endif /* MM_LOCATION_GPS_NMEA_H */ ModemManager-1.23.4-dev/libmm-glib/mm-location-gps-raw.c000066400000000000000000000321741456466623000227100ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * libmm-glib -- Access modem status & information from glib applications * * 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 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 Street, Fifth Floor, * Boston, MA 02110-1301 USA. * * Copyright (C) 2012 Lanedo GmbH */ #include #include #include #include "mm-common-helpers.h" #include "mm-errors-types.h" #include "mm-location-gps-raw.h" /** * SECTION: mm-location-gps-raw * @title: MMLocationGpsRaw * @short_description: Helper object to handle generic GPS location information. * * The #MMLocationGpsRaw is an object handling the location information of the * modem when this is reported by GPS. * * This object is retrieved with either mm_modem_location_get_gps_raw(), * mm_modem_location_get_gps_raw_sync(), mm_modem_location_get_full() or * mm_modem_location_get_full_sync(). */ G_DEFINE_TYPE (MMLocationGpsRaw, mm_location_gps_raw, G_TYPE_OBJECT) #define PROPERTY_UTC_TIME "utc-time" #define PROPERTY_LATITUDE "latitude" #define PROPERTY_LONGITUDE "longitude" #define PROPERTY_ALTITUDE "altitude" struct _MMLocationGpsRawPrivate { GRegex *gga_regex; gboolean prefer_gngga; gchar *utc_time; gdouble latitude; gdouble longitude; gdouble altitude; }; /*****************************************************************************/ /** * mm_location_gps_raw_get_utc_time: * @self: a #MMLocationGpsRaw. * * Gets the UTC time of the location being reported. * * Returns: a string with the UTC time, or #NULL if unknown. Do not free the * returned value, it is owned by @self. * * Since: 1.0 */ const gchar * mm_location_gps_raw_get_utc_time (MMLocationGpsRaw *self) { g_return_val_if_fail (MM_IS_LOCATION_GPS_RAW (self), NULL); return self->priv->utc_time; } /*****************************************************************************/ /** * mm_location_gps_raw_get_longitude: * @self: a #MMLocationGpsRaw. * * Gets the longitude, in the [-180,180] range. * * Returns: the longitude, or %MM_LOCATION_LONGITUDE_UNKNOWN if unknown. * * Since: 1.0 */ gdouble mm_location_gps_raw_get_longitude (MMLocationGpsRaw *self) { g_return_val_if_fail (MM_IS_LOCATION_GPS_RAW (self), MM_LOCATION_LONGITUDE_UNKNOWN); return self->priv->longitude; } /*****************************************************************************/ /** * mm_location_gps_raw_get_latitude: * @self: a #MMLocationGpsRaw. * * Gets the latitude, in the [-90,90] range. * * Returns: the latitude, or %MM_LOCATION_LATITUDE_UNKNOWN if unknown. * * Since: 1.0 */ gdouble mm_location_gps_raw_get_latitude (MMLocationGpsRaw *self) { g_return_val_if_fail (MM_IS_LOCATION_GPS_RAW (self), MM_LOCATION_LATITUDE_UNKNOWN); return self->priv->latitude; } /*****************************************************************************/ /** * mm_location_gps_raw_get_altitude: * @self: a #MMLocationGpsRaw. * * Gets the altitude, in the [-90,90] range. * * Returns: the altitude, or %MM_LOCATION_ALTITUDE_UNKNOWN if unknown. * * Since: 1.0 */ gdouble mm_location_gps_raw_get_altitude (MMLocationGpsRaw *self) { g_return_val_if_fail (MM_IS_LOCATION_GPS_RAW (self), MM_LOCATION_ALTITUDE_UNKNOWN); return self->priv->altitude; } /*****************************************************************************/ static gboolean get_longitude_or_latitude_from_match_info (GMatchInfo *match_info, guint32 match_index, gdouble *out) { gchar *aux; gchar *s; gboolean ret = FALSE; gdouble minutes; gdouble degrees; s = g_match_info_fetch (match_info, match_index); if (!s) goto out; /* 4533.35 is 45 degrees and 33.35 minutes */ aux = strchr (s, '.'); if (!aux || ((aux - s) < 3)) goto out; aux -= 2; if (!mm_get_double_from_str (aux, &minutes)) goto out; aux[0] = '\0'; if (!mm_get_double_from_str (s, °rees)) goto out; /* Include the minutes as part of the degrees */ *out = degrees + (minutes / 60.0); ret = TRUE; out: g_free (s); return ret; } /** * mm_location_gps_raw_add_trace: (skip) */ gboolean mm_location_gps_raw_add_trace (MMLocationGpsRaw *self, const gchar *trace) { g_autoptr(GMatchInfo) match_info = NULL; /* Current implementation works only with $GPGGA and $GNGGA traces */ do { if (g_str_has_prefix (trace, "$GPGGA")) { if (self->priv->prefer_gngga) /* Ignore GPGGA, prefer GNGGA */ return FALSE; break; } if (g_str_has_prefix (trace, "$GNGGA")) { if (!self->priv->prefer_gngga) self->priv->prefer_gngga = TRUE; break; } /* Otherwise, ignore trace */ return FALSE; } while (0); /* * $GPGGA,hhmmss.ss,llll.ll,a,yyyyy.yy,a,x,xx,x.x,x.x,M,x.x,M,x.x,xxxx*hh * 1 = UTC of Position * 2 = Latitude * 3 = N or S * 4 = Longitude * 5 = E or W * 6 = GPS quality indicator (0=invalid; 1=GPS fix; 2=Diff. GPS fix) * 7 = Number of satellites in use [not those in view] * 8 = Horizontal dilution of position * 9 = Antenna altitude above/below mean sea level (geoid) * 10 = Meters (Antenna height unit) * 11 = Geoidal separation (Diff. between WGS-84 earth ellipsoid and * mean sea level. -=geoid is below WGS-84 ellipsoid) * 12 = Meters (Units of geoidal separation) * 13 = Age in seconds since last update from diff. reference station * 14 = Diff. reference station ID# * 15 = Checksum */ if (G_UNLIKELY (!self->priv->gga_regex)) self->priv->gga_regex = g_regex_new ("\\$G(?:P|N)GGA,(.*),(.*),(.*),(.*),(.*),(.*),(.*),(.*),(.*),(.*),(.*),(.*),(.*),(.*)\\*(.*).*", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); if (g_regex_match (self->priv->gga_regex, trace, 0, &match_info)) { /* UTC time */ g_free (self->priv->utc_time); self->priv->utc_time = g_match_info_fetch (match_info, 1); /* Latitude */ self->priv->latitude = MM_LOCATION_LATITUDE_UNKNOWN; if (get_longitude_or_latitude_from_match_info (match_info, 2, &self->priv->latitude)) { gchar *str; /* N/S */ str = g_match_info_fetch (match_info, 3); if (str && str[0] == 'S') self->priv->latitude *= -1; g_free (str); } /* Longitude */ self->priv->longitude = MM_LOCATION_LONGITUDE_UNKNOWN; if (get_longitude_or_latitude_from_match_info (match_info, 4, &self->priv->longitude)) { gchar *str; /* N/S */ str = g_match_info_fetch (match_info, 5); if (str && str[0] == 'W') self->priv->longitude *= -1; g_free (str); } /* Altitude */ self->priv->altitude = MM_LOCATION_ALTITUDE_UNKNOWN; mm_get_double_from_match_info (match_info, 9, &self->priv->altitude); } return TRUE; } /*****************************************************************************/ /** * mm_location_gps_raw_get_dictionary: (skip) */ GVariant * mm_location_gps_raw_get_dictionary (MMLocationGpsRaw *self) { GVariantBuilder builder; /* We do allow NULL */ if (!self) return NULL; g_return_val_if_fail (MM_IS_LOCATION_GPS_RAW (self), NULL); /* If mandatory parameters are not found, return NULL */ if (!self->priv->utc_time || self->priv->longitude == MM_LOCATION_LONGITUDE_UNKNOWN || self->priv->latitude == MM_LOCATION_LATITUDE_UNKNOWN) return NULL; g_variant_builder_init (&builder, G_VARIANT_TYPE ("a{sv}")); g_variant_builder_add (&builder, "{sv}", PROPERTY_UTC_TIME, g_variant_new_string (self->priv->utc_time)); g_variant_builder_add (&builder, "{sv}", PROPERTY_LONGITUDE, g_variant_new_double (self->priv->longitude)); g_variant_builder_add (&builder, "{sv}", PROPERTY_LATITUDE, g_variant_new_double (self->priv->latitude)); /* Altitude is optional */ if (self->priv->altitude != MM_LOCATION_ALTITUDE_UNKNOWN) g_variant_builder_add (&builder, "{sv}", PROPERTY_ALTITUDE, g_variant_new_double (self->priv->altitude)); return g_variant_ref_sink (g_variant_builder_end (&builder)); } /*****************************************************************************/ /** * mm_location_gps_raw_new_from_dictionary: (skip) */ MMLocationGpsRaw * mm_location_gps_raw_new_from_dictionary (GVariant *dictionary, GError **error) { GError *inner_error = NULL; MMLocationGpsRaw *self; GVariantIter iter; gchar *key; GVariant *value; self = mm_location_gps_raw_new (); if (!dictionary) return self; if (!g_variant_is_of_type (dictionary, G_VARIANT_TYPE ("a{sv}"))) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS, "Cannot create GPS RAW location from dictionary: " "invalid variant type received"); g_object_unref (self); return NULL; } g_variant_iter_init (&iter, dictionary); while (!inner_error && g_variant_iter_next (&iter, "{sv}", &key, &value)) { if (g_str_equal (key, PROPERTY_UTC_TIME)) self->priv->utc_time = g_variant_dup_string (value, NULL); else if (g_str_equal (key, PROPERTY_LONGITUDE)) self->priv->longitude = g_variant_get_double (value); else if (g_str_equal (key, PROPERTY_LATITUDE)) self->priv->latitude = g_variant_get_double (value); else if (g_str_equal (key, PROPERTY_ALTITUDE)) self->priv->altitude = g_variant_get_double (value); g_free (key); g_variant_unref (value); } /* If any of the mandatory parameters is missing, cleanup */ if (!self->priv->utc_time || self->priv->longitude == MM_LOCATION_LONGITUDE_UNKNOWN || self->priv->latitude == MM_LOCATION_LATITUDE_UNKNOWN) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS, "Cannot create GPS RAW location from dictionary: " "mandatory parameters missing " "(utc-time: %s, longitude: %s, latitude: %s)", self->priv->utc_time ? "yes" : "missing", (self->priv->longitude != MM_LOCATION_LONGITUDE_UNKNOWN) ? "yes" : "missing", (self->priv->latitude != MM_LOCATION_LATITUDE_UNKNOWN) ? "yes" : "missing"); g_clear_object (&self); } return self; } /*****************************************************************************/ /** * mm_location_gps_raw_new: (skip) */ MMLocationGpsRaw * mm_location_gps_raw_new (void) { return (MM_LOCATION_GPS_RAW ( g_object_new (MM_TYPE_LOCATION_GPS_RAW, NULL))); } static void mm_location_gps_raw_init (MMLocationGpsRaw *self) { self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, MM_TYPE_LOCATION_GPS_RAW, MMLocationGpsRawPrivate); self->priv->utc_time = NULL; self->priv->latitude = MM_LOCATION_LATITUDE_UNKNOWN; self->priv->longitude = MM_LOCATION_LONGITUDE_UNKNOWN; self->priv->altitude = MM_LOCATION_ALTITUDE_UNKNOWN; } static void finalize (GObject *object) { MMLocationGpsRaw *self = MM_LOCATION_GPS_RAW (object); if (self->priv->gga_regex) g_regex_unref (self->priv->gga_regex); g_free (self->priv->utc_time); G_OBJECT_CLASS (mm_location_gps_raw_parent_class)->finalize (object); } static void mm_location_gps_raw_class_init (MMLocationGpsRawClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); g_type_class_add_private (object_class, sizeof (MMLocationGpsRawPrivate)); object_class->finalize = finalize; } ModemManager-1.23.4-dev/libmm-glib/mm-location-gps-raw.h000066400000000000000000000067521456466623000227200ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * libmm-glib -- Access modem status & information from glib applications * * 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 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 Street, Fifth Floor, * Boston, MA 02110-1301 USA. * * Copyright (C) 2012 Lanedo GmbH */ #ifndef MM_LOCATION_GPS_RAW_H #define MM_LOCATION_GPS_RAW_H #if !defined (__LIBMM_GLIB_H_INSIDE__) && !defined (LIBMM_GLIB_COMPILATION) #error "Only can be included directly." #endif #include #include #include "mm-location-common.h" G_BEGIN_DECLS #define MM_TYPE_LOCATION_GPS_RAW (mm_location_gps_raw_get_type ()) #define MM_LOCATION_GPS_RAW(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_LOCATION_GPS_RAW, MMLocationGpsRaw)) #define MM_LOCATION_GPS_RAW_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_LOCATION_GPS_RAW, MMLocationGpsRawClass)) #define MM_IS_LOCATION_GPS_RAW(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_LOCATION_GPS_RAW)) #define MM_IS_LOCATION_GPS_RAW_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_LOCATION_GPS_RAW)) #define MM_LOCATION_GPS_RAW_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_LOCATION_GPS_RAW, MMLocationGpsRawClass)) typedef struct _MMLocationGpsRaw MMLocationGpsRaw; typedef struct _MMLocationGpsRawClass MMLocationGpsRawClass; typedef struct _MMLocationGpsRawPrivate MMLocationGpsRawPrivate; /** * MMLocationGpsRaw: * * The #MMLocationGpsRaw structure contains private data and should * only be accessed using the provided API. */ struct _MMLocationGpsRaw { /*< private >*/ GObject parent; MMLocationGpsRawPrivate *priv; }; struct _MMLocationGpsRawClass { /*< private >*/ GObjectClass parent; }; GType mm_location_gps_raw_get_type (void); G_DEFINE_AUTOPTR_CLEANUP_FUNC (MMLocationGpsRaw, g_object_unref) const gchar *mm_location_gps_raw_get_utc_time (MMLocationGpsRaw *self); gdouble mm_location_gps_raw_get_longitude (MMLocationGpsRaw *self); gdouble mm_location_gps_raw_get_latitude (MMLocationGpsRaw *self); gdouble mm_location_gps_raw_get_altitude (MMLocationGpsRaw *self); /*****************************************************************************/ /* ModemManager/libmm-glib/mmcli specific methods */ #if defined (_LIBMM_INSIDE_MM) || \ defined (_LIBMM_INSIDE_MMCLI) || \ defined (LIBMM_GLIB_COMPILATION) MMLocationGpsRaw *mm_location_gps_raw_new (void); MMLocationGpsRaw *mm_location_gps_raw_new_from_dictionary (GVariant *string, GError **error); gboolean mm_location_gps_raw_add_trace (MMLocationGpsRaw *self, const gchar *trace); GVariant *mm_location_gps_raw_get_dictionary (MMLocationGpsRaw *self); #endif G_END_DECLS #endif /* MM_LOCATION_GPS_RAW_H */ ModemManager-1.23.4-dev/libmm-glib/mm-manager.c000066400000000000000000001034301456466623000211260ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * libmm-glib -- Access modem status & information from glib applications * * 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 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 Street, Fifth Floor, * Boston, MA 02110-1301 USA. * * Copyright (C) 2011 - 2012 Google, Inc. * Copyright (C) 2011 - 2018 Aleksander Morgado */ #include #include "mm-helpers.h" #include "mm-common-helpers.h" #include "mm-errors-types.h" #include "mm-gdbus-manager.h" #include "mm-manager.h" #include "mm-object.h" /** * SECTION: mm-manager * @title: MMManager * @short_description: The Manager object * * The #MMManager is the object allowing access to the Manager interface. * * This object is also a #GDBusObjectManagerClient, and therefore it allows to * use the standard ObjectManager interface to list and handle the managed * modem objects. */ G_DEFINE_TYPE (MMManager, mm_manager, MM_GDBUS_TYPE_OBJECT_MANAGER_CLIENT) struct _MMManagerPrivate { /* The proxy for the Manager interface */ MmGdbusOrgFreedesktopModemManager1 *manager_iface_proxy; }; /*****************************************************************************/ static GType get_proxy_type (GDBusObjectManagerClient *manager, const gchar *object_path, const gchar *interface_name, gpointer user_data) { static gsize once_init_value = 0; static GHashTable *lookup_hash; GType ret; if (interface_name == NULL) return MM_TYPE_OBJECT; if (g_once_init_enter (&once_init_value)) { lookup_hash = g_hash_table_new (g_str_hash, g_str_equal); g_hash_table_insert (lookup_hash, (gpointer) "org.freedesktop.ModemManager1.Modem", GSIZE_TO_POINTER (MM_TYPE_MODEM)); g_hash_table_insert (lookup_hash, (gpointer) "org.freedesktop.ModemManager1.Modem.Messaging", GSIZE_TO_POINTER (MM_TYPE_MODEM_MESSAGING)); g_hash_table_insert (lookup_hash, (gpointer) "org.freedesktop.ModemManager1.Modem.Voice", GSIZE_TO_POINTER (MM_TYPE_MODEM_VOICE)); g_hash_table_insert (lookup_hash, (gpointer) "org.freedesktop.ModemManager1.Modem.Location", GSIZE_TO_POINTER (MM_TYPE_MODEM_LOCATION)); g_hash_table_insert (lookup_hash, (gpointer) "org.freedesktop.ModemManager1.Modem.Time", GSIZE_TO_POINTER (MM_TYPE_MODEM_TIME)); g_hash_table_insert (lookup_hash, (gpointer) "org.freedesktop.ModemManager1.Modem.Sar", GSIZE_TO_POINTER (MM_TYPE_MODEM_SAR)); g_hash_table_insert (lookup_hash, (gpointer) "org.freedesktop.ModemManager1.Modem.Signal", GSIZE_TO_POINTER (MM_TYPE_MODEM_SIGNAL)); g_hash_table_insert (lookup_hash, (gpointer) "org.freedesktop.ModemManager1.Modem.Firmware", GSIZE_TO_POINTER (MM_TYPE_MODEM_FIRMWARE)); g_hash_table_insert (lookup_hash, (gpointer) "org.freedesktop.ModemManager1.Modem.Oma", GSIZE_TO_POINTER (MM_TYPE_MODEM_OMA)); g_hash_table_insert (lookup_hash, (gpointer) "org.freedesktop.ModemManager1.Modem.ModemCdma", GSIZE_TO_POINTER (MM_TYPE_MODEM_CDMA)); g_hash_table_insert (lookup_hash, (gpointer) "org.freedesktop.ModemManager1.Modem.Modem3gpp", GSIZE_TO_POINTER (MM_TYPE_MODEM_3GPP)); g_hash_table_insert (lookup_hash, (gpointer) "org.freedesktop.ModemManager1.Modem.Modem3gpp.ProfileManager", GSIZE_TO_POINTER (MM_TYPE_MODEM_3GPP_PROFILE_MANAGER)); g_hash_table_insert (lookup_hash, (gpointer) "org.freedesktop.ModemManager1.Modem.Modem3gpp.Ussd", GSIZE_TO_POINTER (MM_TYPE_MODEM_3GPP_USSD)); g_hash_table_insert (lookup_hash, (gpointer) "org.freedesktop.ModemManager1.Modem.Simple", GSIZE_TO_POINTER (MM_TYPE_MODEM_SIMPLE)); g_once_init_leave (&once_init_value, 1); } ret = (GType) GPOINTER_TO_SIZE (g_hash_table_lookup (lookup_hash, interface_name)); if (ret == (GType) 0) ret = G_TYPE_DBUS_PROXY; return ret; } /*****************************************************************************/ static void cleanup_modem_manager1_proxy (MMManager *self) { if (self->priv->manager_iface_proxy) { g_signal_handlers_disconnect_by_func (self, cleanup_modem_manager1_proxy, NULL); g_clear_object (&self->priv->manager_iface_proxy); } } static gboolean ensure_modem_manager1_proxy (MMManager *self, GError **error) { gchar *name = NULL; gchar *object_path = NULL; GDBusObjectManagerClientFlags obj_manager_flags = G_DBUS_OBJECT_MANAGER_CLIENT_FLAGS_NONE; GDBusProxyFlags proxy_flags = G_DBUS_PROXY_FLAGS_NONE; GDBusConnection *connection = NULL; if (self->priv->manager_iface_proxy) return TRUE; /* Get the Manager proxy created synchronously now */ g_object_get (self, "name", &name, "object-path", &object_path, "flags", &obj_manager_flags, "connection", &connection, NULL); if (obj_manager_flags & G_DBUS_OBJECT_MANAGER_CLIENT_FLAGS_DO_NOT_AUTO_START) proxy_flags |= G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START; self->priv->manager_iface_proxy = mm_gdbus_org_freedesktop_modem_manager1_proxy_new_sync (connection, proxy_flags, name, object_path, NULL, error); g_object_unref (connection); g_free (object_path); g_free (name); if (self->priv->manager_iface_proxy) g_signal_connect (self, "notify::name-owner", G_CALLBACK (cleanup_modem_manager1_proxy), NULL); return !!self->priv->manager_iface_proxy; } /*****************************************************************************/ /** * mm_manager_new_finish: * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to * mm_manager_new(). * @error: Return location for error or %NULL * * Finishes an operation started with mm_manager_new(). * * Returns: (transfer full) (type MMManager): The constructed object manager * client or %NULL if @error is set. * * Since: 1.0 */ MMManager * mm_manager_new_finish (GAsyncResult *res, GError **error) { GObject *ret; GObject *source_object; source_object = g_async_result_get_source_object (res); ret = g_async_initable_new_finish (G_ASYNC_INITABLE (source_object), res, error); g_object_unref (source_object); return MM_MANAGER (ret); } /** * mm_manager_new: * @connection: A #GDBusConnection. * @flags: Flags from the #GDBusObjectManagerClientFlags enumeration. * @cancellable: (allow-none): A #GCancellable or %NULL. * @callback: A #GAsyncReadyCallback to call when the request is satisfied. * @user_data: User data to pass to @callback. * * Asynchronously creates a #MMManager. * * When the operation is finished, @callback will be invoked in the * thread-default main loop * of the thread you are calling this method from. * * You can then call mm_manager_new_finish() to get the result of the operation. * * See mm_manager_new_sync() for the synchronous, blocking version of this * constructor. * * Since: 1.0 */ void mm_manager_new (GDBusConnection *connection, GDBusObjectManagerClientFlags flags, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { g_async_initable_new_async (MM_TYPE_MANAGER, G_PRIORITY_DEFAULT, cancellable, callback, user_data, "name", MM_DBUS_SERVICE, "object-path", MM_DBUS_PATH, "flags", flags, "connection", connection, "get-proxy-type-func", get_proxy_type, NULL); } /** * mm_manager_new_sync: * @connection: A #GDBusConnection. * @flags: Flags from the #GDBusObjectManagerClientFlags enumeration. * @cancellable: (allow-none): A #GCancellable or %NULL. * @error: Return location for error or %NULL * * Synchronously creates a #MMManager. * * The calling thread is blocked until a reply is received. * * See mm_manager_new() for the asynchronous version of this constructor. * * Returns: (transfer full) (type MMManager): The constructed object manager * client or %NULL if @error is set. * * Since: 1.0 */ MMManager * mm_manager_new_sync (GDBusConnection *connection, GDBusObjectManagerClientFlags flags, GCancellable *cancellable, GError **error) { return MM_MANAGER (g_initable_new (MM_TYPE_MANAGER, cancellable, error, "name", MM_DBUS_SERVICE, "object-path", MM_DBUS_PATH, "flags", flags, "connection", connection, "get-proxy-type-func", get_proxy_type, NULL)); } /*****************************************************************************/ /** * mm_manager_peek_proxy: * @manager: A #MMManager. * * Gets the #GDBusProxy interface of the @manager. * * Returns: (transfer none): The #GDBusProxy interface of @manager, or #NULL if * none. Do not free the returned object, it is owned by @manager. * * Since: 1.0 */ GDBusProxy * mm_manager_peek_proxy (MMManager *manager) { g_return_val_if_fail (MM_IS_MANAGER (manager), NULL); if (!ensure_modem_manager1_proxy (manager, NULL)) return NULL; return G_DBUS_PROXY (manager->priv->manager_iface_proxy); } /** * mm_manager_get_proxy: * @manager: A #MMManager. * * Gets the #GDBusProxy interface of the @manager. * * Returns: (transfer full): The #GDBusProxy interface of @manager, or #NULL if * none. The returned object must be freed with g_object_unref(). * * Since: 1.0 */ GDBusProxy * mm_manager_get_proxy (MMManager *manager) { g_return_val_if_fail (MM_IS_MANAGER (manager), NULL); if (!ensure_modem_manager1_proxy (manager, NULL)) return NULL; return G_DBUS_PROXY (g_object_ref (manager->priv->manager_iface_proxy)); } /*****************************************************************************/ /** * mm_manager_get_version: * @manager: A #MMManager. * * Gets the ModemManager version, as reported by the daemon. * * It is safe to assume this value never changes during runtime. * * Returns: (transfer none): The version, or %NULL if none available. Do not * free the returned value, it belongs to @self. * * Since: 1.0 */ const gchar * mm_manager_get_version (MMManager *manager) { g_return_val_if_fail (MM_IS_MANAGER (manager), NULL); if (!ensure_modem_manager1_proxy (manager, NULL)) return NULL; RETURN_NON_EMPTY_CONSTANT_STRING ( mm_gdbus_org_freedesktop_modem_manager1_get_version (manager->priv->manager_iface_proxy)); } /*****************************************************************************/ /** * mm_manager_set_logging_finish: * @manager: A #MMManager. * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to * mm_manager_set_logging(). * @error: Return location for error or %NULL. * * Finishes an operation started with mm_manager_set_logging(). * * Returns: %TRUE if the call succeeded, %FALSE if @error is set. * * Since: 1.0 */ gboolean mm_manager_set_logging_finish (MMManager *manager, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void set_logging_ready (MmGdbusOrgFreedesktopModemManager1 *manager_iface_proxy, GAsyncResult *res, GTask *task) { GError *error = NULL; if (!mm_gdbus_org_freedesktop_modem_manager1_call_set_logging_finish ( manager_iface_proxy, res, &error)) g_task_return_error (task, error); else g_task_return_boolean (task, TRUE); g_object_unref (task); } /** * mm_manager_set_logging: * @manager: A #MMManager. * @level: the login level to set. * @cancellable: (allow-none): A #GCancellable or %NULL. * @callback: A #GAsyncReadyCallback to call when the request is satisfied or * %NULL. * @user_data: User data to pass to @callback. * * Asynchronously requests to set the specified logging level in the daemon. * * When the operation is finished, @callback will be invoked in the * thread-default main loop * of the thread you are calling this method from. You can then call * mm_manager_set_logging_finish() to get the result of the operation. * * See mm_manager_set_logging_sync() for the synchronous, blocking version of * this method. * * Since: 1.0 */ void mm_manager_set_logging (MMManager *manager, const gchar *level, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; GError *inner_error = NULL; g_return_if_fail (MM_IS_MANAGER (manager)); task = g_task_new (manager, cancellable, callback, user_data); if (!ensure_modem_manager1_proxy (manager, &inner_error)) { g_task_return_error (task, inner_error); g_object_unref (task); return; } mm_gdbus_org_freedesktop_modem_manager1_call_set_logging ( manager->priv->manager_iface_proxy, level, cancellable, (GAsyncReadyCallback)set_logging_ready, task); } /** * mm_manager_set_logging_sync: * @manager: A #MMManager. * @level: the login level to set. * @cancellable: (allow-none): A #GCancellable or %NULL. * @error: Return location for error or %NULL. * * Synchronously requests to set the specified logging level in the daemon. * * The calling thread is blocked until a reply is received. * * See mm_manager_set_logging() for the asynchronous version of this method. * * Returns: %TRUE if the call succeeded, %FALSE if @error is set. * * Since: 1.0 */ gboolean mm_manager_set_logging_sync (MMManager *manager, const gchar *level, GCancellable *cancellable, GError **error) { g_return_val_if_fail (MM_IS_MANAGER (manager), FALSE); if (!ensure_modem_manager1_proxy (manager, error)) return FALSE; return (mm_gdbus_org_freedesktop_modem_manager1_call_set_logging_sync ( manager->priv->manager_iface_proxy, level, cancellable, error)); } /*****************************************************************************/ /** * mm_manager_scan_devices_finish: * @manager: A #MMManager. * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to * mm_manager_scan_devices(). * @error: Return location for error or %NULL. * * Finishes an operation started with mm_manager_scan_devices(). * * Returns: %TRUE if the call succeeded, %FALSE if @error is set. * * Since: 1.0 */ gboolean mm_manager_scan_devices_finish (MMManager *manager, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void scan_devices_ready (MmGdbusOrgFreedesktopModemManager1 *manager_iface_proxy, GAsyncResult *res, GTask *task) { GError *error = NULL; if (!mm_gdbus_org_freedesktop_modem_manager1_call_scan_devices_finish ( manager_iface_proxy, res, &error)) g_task_return_error (task, error); else g_task_return_boolean (task, TRUE); g_object_unref (task); } /** * mm_manager_scan_devices: * @manager: A #MMManager. * @cancellable: (allow-none): A #GCancellable or %NULL. * @callback: A #GAsyncReadyCallback to call when the request is satisfied or * %NULL. * @user_data: User data to pass to @callback. * * Asynchronously requests to scan looking for devices. * * When the operation is finished, @callback will be invoked in the * thread-default main loop * of the thread you are calling this method from. You can then call * mm_manager_scan_devices_finish() to get the result of the operation. * * See mm_manager_scan_devices_sync() for the synchronous, blocking version of * this method. * * Since: 1.0 */ void mm_manager_scan_devices (MMManager *manager, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; GError *inner_error = NULL; g_return_if_fail (MM_IS_MANAGER (manager)); task = g_task_new (manager, cancellable, callback, user_data); if (!ensure_modem_manager1_proxy (manager, &inner_error)) { g_task_return_error (task, inner_error); g_object_unref (task); return; } mm_gdbus_org_freedesktop_modem_manager1_call_scan_devices ( manager->priv->manager_iface_proxy, cancellable, (GAsyncReadyCallback)scan_devices_ready, task); } /** * mm_manager_scan_devices_sync: * @manager: A #MMManager. * @cancellable: (allow-none): A #GCancellable or %NULL. * @error: Return location for error or %NULL. * * Synchronously requests to scan looking for devices. * * The calling thread is blocked until a reply is received. * * See mm_manager_scan_devices() for the asynchronous version of this method. * * Returns: %TRUE if the call succeeded, %FALSE if @error is set. * * Since: 1.0 */ gboolean mm_manager_scan_devices_sync (MMManager *manager, GCancellable *cancellable, GError **error) { g_return_val_if_fail (MM_IS_MANAGER (manager), FALSE); if (!ensure_modem_manager1_proxy (manager, error)) return FALSE; return (mm_gdbus_org_freedesktop_modem_manager1_call_scan_devices_sync ( manager->priv->manager_iface_proxy, cancellable, error)); } /*****************************************************************************/ /** * mm_manager_report_kernel_event_finish: * @manager: A #MMManager. * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to * mm_manager_report_kernel_event(). * @error: Return location for error or %NULL. * * Finishes an operation started with mm_manager_report_kernel_event(). * * Returns: %TRUE if the operation succeeded, %FALSE if @error is set. * * Since: 1.8 */ gboolean mm_manager_report_kernel_event_finish (MMManager *manager, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void report_kernel_event_ready (MmGdbusOrgFreedesktopModemManager1 *manager_iface_proxy, GAsyncResult *res, GTask *task) { GError *error = NULL; if (!mm_gdbus_org_freedesktop_modem_manager1_call_report_kernel_event_finish ( manager_iface_proxy, res, &error)) g_task_return_error (task, error); else g_task_return_boolean (task, TRUE); g_object_unref (task); } /** * mm_manager_report_kernel_event: * @manager: A #MMManager. * @properties: the properties of the kernel event. * @cancellable: (allow-none): A #GCancellable or %NULL. * @callback: A #GAsyncReadyCallback to call when the request is satisfied or * %NULL. * @user_data: User data to pass to @callback. * * Asynchronously report kernel event. * * When the operation is finished, @callback will be invoked in the * thread-default main loop * of the thread you are calling this method from. You can then call * mm_manager_report_kernel_event_finish() to get the result of the operation. * * See mm_manager_report_kernel_event_sync() for the synchronous, blocking * version of this method. * * Since: 1.8 */ void mm_manager_report_kernel_event (MMManager *manager, MMKernelEventProperties *properties, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; GError *inner_error = NULL; GVariant *dictionary; g_return_if_fail (MM_IS_MANAGER (manager)); task = g_task_new (manager, cancellable, callback, user_data); if (!ensure_modem_manager1_proxy (manager, &inner_error)) { g_task_return_error (task, inner_error); g_object_unref (task); return; } dictionary = mm_kernel_event_properties_get_dictionary (properties); mm_gdbus_org_freedesktop_modem_manager1_call_report_kernel_event ( manager->priv->manager_iface_proxy, dictionary, cancellable, (GAsyncReadyCallback)report_kernel_event_ready, task); g_variant_unref (dictionary); } /** * mm_manager_report_kernel_event_sync: * @manager: A #MMManager. * @properties: the properties of the kernel event. * @cancellable: (allow-none): A #GCancellable or %NULL. * @error: Return location for error or %NULL. * * Synchronously report kernel event. * * The calling thread is blocked until a reply is received. * * See mm_manager_report_kernel_event() for the asynchronous version of this * method. * * Returns: %TRUE if the operation succeeded, %FALSE if @error is set. * * Since: 1.8 */ gboolean mm_manager_report_kernel_event_sync (MMManager *manager, MMKernelEventProperties *properties, GCancellable *cancellable, GError **error) { GVariant *dictionary; gboolean result; g_return_val_if_fail (MM_IS_MANAGER (manager), FALSE); if (!ensure_modem_manager1_proxy (manager, error)) return FALSE; dictionary = mm_kernel_event_properties_get_dictionary (properties); result = (mm_gdbus_org_freedesktop_modem_manager1_call_report_kernel_event_sync ( manager->priv->manager_iface_proxy, dictionary, cancellable, error)); g_variant_unref (dictionary); return result; } /*****************************************************************************/ static gboolean common_inhibit_device_finish (MMManager *manager, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void inhibit_ready (MmGdbusOrgFreedesktopModemManager1 *manager_iface_proxy, GAsyncResult *res, GTask *task) { GError *error = NULL; if (!mm_gdbus_org_freedesktop_modem_manager1_call_inhibit_device_finish (manager_iface_proxy, res, &error)) g_task_return_error (task, error); else g_task_return_boolean (task, TRUE); g_object_unref (task); } static void common_inhibit_device (MMManager *manager, const gchar *uid, gboolean inhibit, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; GError *inner_error = NULL; task = g_task_new (manager, cancellable, callback, user_data); if (!ensure_modem_manager1_proxy (manager, &inner_error)) { g_task_return_error (task, inner_error); g_object_unref (task); return; } mm_gdbus_org_freedesktop_modem_manager1_call_inhibit_device ( manager->priv->manager_iface_proxy, uid, inhibit, cancellable, (GAsyncReadyCallback)inhibit_ready, task); } static gboolean common_inhibit_device_sync (MMManager *manager, const gchar *uid, gboolean inhibit, GCancellable *cancellable, GError **error) { if (!ensure_modem_manager1_proxy (manager, error)) return FALSE; return (mm_gdbus_org_freedesktop_modem_manager1_call_inhibit_device_sync ( manager->priv->manager_iface_proxy, uid, inhibit, cancellable, error)); } /** * mm_manager_inhibit_device_finish: * @manager: A #MMManager. * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to * mm_manager_inhibit_device(). * @error: Return location for error or %NULL. * * Finishes an operation started with mm_manager_inhibit_device(). * * Returns: %TRUE if the call succeeded, %FALSE if @error is set. * * Since: 1.10 */ gboolean mm_manager_inhibit_device_finish (MMManager *manager, GAsyncResult *res, GError **error) { g_return_val_if_fail (MM_IS_MANAGER (manager), FALSE); return common_inhibit_device_finish (manager, res, error); } /** * mm_manager_inhibit_device: * @manager: A #MMManager. * @uid: the unique ID of the physical device. * @cancellable: (allow-none): A #GCancellable or %NULL. * @callback: A #GAsyncReadyCallback to call when the request is satisfied or * %NULL. * @user_data: User data to pass to @callback. * * Asynchronously requests to add an inhibition on the device identified by * @uid. * * The @uid must be the unique ID retrieved from an existing #MMModem using * mm_modem_get_device(). The caller should keep track of this @uid and use it * in the mm_manager_uninhibit_device() call when the inhibition is no longer * required. * * The inhibition added with this method may also be automatically removed when * the caller program disappears from the bus (e.g. if the program ends before * having called mm_manager_uninhibit_device() explicitly). * * When the operation is finished, @callback will be invoked in the * thread-default main loop * of the thread you are calling this method from. You can then call * mm_manager_inhibit_device_finish() to get the result of the operation. * * See mm_manager_inhibit_device_sync() for the synchronous, blocking version of * this method. * * Since: 1.10 */ void mm_manager_inhibit_device (MMManager *manager, const gchar *uid, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { g_return_if_fail (MM_IS_MANAGER (manager)); common_inhibit_device (manager, uid, TRUE, cancellable, callback, user_data); } /** * mm_manager_inhibit_device_sync: * @manager: A #MMManager. * @uid: the unique ID of the physical device. * @cancellable: (allow-none): A #GCancellable or %NULL. * @error: Return location for error or %NULL. * * Synchronously requests to add an inhibition on the device identified by @uid. * * The @uid must be the unique ID retrieved from an existing #MMModem using * mm_modem_get_device(). The caller should keep track of this @uid and use it * in the mm_manager_uninhibit_device_sync() call when the inhibition is no * longer required. * * The inhibition added with this method may also be automatically removed when * the caller program disappears from the bus (e.g. if the program ends before * having called mm_manager_uninhibit_device_sync() explicitly). * * See mm_manager_inhibit_device() for the asynchronous version of this method. * * Returns: %TRUE if the call succeeded, %FALSE if @error is set. * * Since: 1.10 */ gboolean mm_manager_inhibit_device_sync (MMManager *manager, const gchar *uid, GCancellable *cancellable, GError **error) { g_return_val_if_fail (MM_IS_MANAGER (manager), FALSE); return common_inhibit_device_sync (manager, uid, TRUE, cancellable, error); } /** * mm_manager_uninhibit_device_finish: * @manager: A #MMManager. * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to * mm_manager_uninhibit_device(). * @error: Return location for error or %NULL. * * Finishes an operation started with mm_manager_uninhibit_device(). * * Returns: %TRUE if the call succeeded, %FALSE if @error is set. * * Since: 1.10 */ gboolean mm_manager_uninhibit_device_finish (MMManager *manager, GAsyncResult *res, GError **error) { g_return_val_if_fail (MM_IS_MANAGER (manager), FALSE); return common_inhibit_device_finish (manager, res, error); } /** * mm_manager_uninhibit_device: * @manager: A #MMManager. * @uid: the unique ID of the physical device. * @cancellable: (allow-none): A #GCancellable or %NULL. * @callback: A #GAsyncReadyCallback to call when the request is satisfied or * %NULL. * @user_data: User data to pass to @callback. * * Asynchronously requests to remove an inhibition on the device identified by * @uid. * * The @uid must be the same unique ID that was sent in the inhibition request. * * Only the same program that placed an inhibition on a given device is able to * remove the inhibition. * * When the operation is finished, @callback will be invoked in the * thread-default main loop * of the thread you are calling this method from. You can then call * mm_manager_uninhibit_device_finish() to get the result of the operation. * * See mm_manager_uninhibit_device_sync() for the synchronous, blocking version * of this method. * * Since: 1.10 */ void mm_manager_uninhibit_device (MMManager *manager, const gchar *uid, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { g_return_if_fail (MM_IS_MANAGER (manager)); common_inhibit_device (manager, uid, FALSE, cancellable, callback, user_data); } /** * mm_manager_uninhibit_device_sync: * @manager: A #MMManager. * @uid: the unique ID of the physical device. * @cancellable: (allow-none): A #GCancellable or %NULL. * @error: Return location for error or %NULL. * * Synchronously requests to remove an inhibition on the device identified by * @uid. * * The @uid must be the same unique ID that was sent in the inhibition request. * * Only the same program that placed an inhibition on a given device is able to * remove the inhibition. * * See mm_manager_uninhibit_device() for the asynchronous version of this * method. * * Returns: %TRUE if the call succeeded, %FALSE if @error is set. * * Since: 1.10 */ gboolean mm_manager_uninhibit_device_sync (MMManager *manager, const gchar *uid, GCancellable *cancellable, GError **error) { g_return_val_if_fail (MM_IS_MANAGER (manager), FALSE); return common_inhibit_device_sync (manager, uid, FALSE, cancellable, error); } /*****************************************************************************/ static void mm_manager_init (MMManager *manager) { mm_common_register_errors (); /* Setup private data */ manager->priv = G_TYPE_INSTANCE_GET_PRIVATE (manager, MM_TYPE_MANAGER, MMManagerPrivate); } static void dispose (GObject *object) { MMManager *self = MM_MANAGER (object); g_clear_object (&self->priv->manager_iface_proxy); G_OBJECT_CLASS (mm_manager_parent_class)->dispose (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->dispose = dispose; } ModemManager-1.23.4-dev/libmm-glib/mm-manager.h000066400000000000000000000163471456466623000211450ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * libmm-glib -- Access modem status & information from glib applications * * 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 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 Street, Fifth Floor, * Boston, MA 02110-1301 USA. * * Copyright (C) 2011 - 2012 Aleksander Morgado * Copyright (C) 2011 - 2012 Google, Inc. * * Author: Aleksander Morgado */ #ifndef _MM_MANAGER_H_ #define _MM_MANAGER_H_ #if !defined (__LIBMM_GLIB_H_INSIDE__) && !defined (LIBMM_GLIB_COMPILATION) #error "Only can be included directly." #endif #include #include "mm-gdbus-modem.h" #include "mm-kernel-event-properties.h" G_BEGIN_DECLS #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)) typedef struct _MMManager MMManager; typedef struct _MMManagerClass MMManagerClass; typedef struct _MMManagerPrivate MMManagerPrivate; /** * MMManager: * * The #MMManager structure contains private data and should only be accessed * using the provided API. */ struct _MMManager { /*< private >*/ MmGdbusObjectManagerClient parent; MMManagerPrivate *priv; }; struct _MMManagerClass { /*< private >*/ MmGdbusObjectManagerClientClass parent; }; GType mm_manager_get_type (void); G_DEFINE_AUTOPTR_CLEANUP_FUNC (MMManager, g_object_unref) void mm_manager_new ( GDBusConnection *connection, GDBusObjectManagerClientFlags flags, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); MMManager *mm_manager_new_finish ( GAsyncResult *res, GError **error); MMManager *mm_manager_new_sync ( GDBusConnection *connection, GDBusObjectManagerClientFlags flags, GCancellable *cancellable, GError **error); GDBusProxy *mm_manager_peek_proxy (MMManager *manager); GDBusProxy *mm_manager_get_proxy (MMManager *manager); const gchar *mm_manager_get_version (MMManager *manager); void mm_manager_set_logging (MMManager *manager, const gchar *level, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); gboolean mm_manager_set_logging_finish (MMManager *manager, GAsyncResult *res, GError **error); gboolean mm_manager_set_logging_sync (MMManager *manager, const gchar *level, GCancellable *cancellable, GError **error); void mm_manager_scan_devices (MMManager *manager, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); gboolean mm_manager_scan_devices_finish (MMManager *manager, GAsyncResult *res, GError **error); gboolean mm_manager_scan_devices_sync (MMManager *manager, GCancellable *cancellable, GError **error); void mm_manager_report_kernel_event (MMManager *manager, MMKernelEventProperties *properties, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); gboolean mm_manager_report_kernel_event_finish (MMManager *manager, GAsyncResult *res, GError **error); gboolean mm_manager_report_kernel_event_sync (MMManager *manager, MMKernelEventProperties *properties, GCancellable *cancellable, GError **error); void mm_manager_inhibit_device (MMManager *manager, const gchar *uid, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); gboolean mm_manager_inhibit_device_finish (MMManager *manager, GAsyncResult *res, GError **error); gboolean mm_manager_inhibit_device_sync (MMManager *manager, const gchar *uid, GCancellable *cancellable, GError **error); void mm_manager_uninhibit_device (MMManager *manager, const gchar *uid, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); gboolean mm_manager_uninhibit_device_finish (MMManager *manager, GAsyncResult *res, GError **error); gboolean mm_manager_uninhibit_device_sync (MMManager *manager, const gchar *uid, GCancellable *cancellable, GError **error); G_END_DECLS #endif /* _MM_MANAGER_H_ */ ModemManager-1.23.4-dev/libmm-glib/mm-modem-3gpp-profile-manager.c000066400000000000000000000470031456466623000245350ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * libmm-glib -- Access modem status & information from glib applications * * 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 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 Street, Fifth Floor, * Boston, MA 02110-1301 USA. * * Copyright (C) 2021 Aleksander Morgado * Copyright (C) 2021 Google, Inc. */ #include #include "mm-helpers.h" #include "mm-errors-types.h" #include "mm-modem-3gpp-profile-manager.h" /** * SECTION: mm-modem-3gpp-profile-manager * @title: MMModem3gppProfileManager * @short_description: The 3GPP profile manager interface * * The #MMModem3gppProfileManager is an object providing access to the methods * and signals of the 3GPP Profile Manager interface. * * This interface is only exposed when the 3GPP modem is known to handle profile * management operations. */ G_DEFINE_TYPE (MMModem3gppProfileManager, mm_modem_3gpp_profile_manager, MM_GDBUS_TYPE_MODEM3GPP_PROFILE_MANAGER_PROXY) /*****************************************************************************/ /** * mm_modem_3gpp_profile_manager_get_path: * @self: A #MMModem3gppProfileManager. * * Gets the DBus path of the #MMObject which implements this interface. * * Returns: (transfer none): The DBus path of the #MMObject object. * * Since: 1.18 */ const gchar * mm_modem_3gpp_profile_manager_get_path (MMModem3gppProfileManager *self) { g_return_val_if_fail (MM_IS_MODEM_3GPP_PROFILE_MANAGER (self), NULL); RETURN_NON_EMPTY_CONSTANT_STRING ( g_dbus_proxy_get_object_path (G_DBUS_PROXY (self))); } /** * mm_modem_3gpp_profile_manager_dup_path: * @self: A #MMModem3gppProfileManager. * * Gets a copy of the DBus path of the #MMObject object which implements this * interface. * * Returns: (transfer full): The DBus path of the #MMObject. The returned value * should be freed with g_free(). * * Since: 1.18 */ gchar * mm_modem_3gpp_profile_manager_dup_path (MMModem3gppProfileManager *self) { gchar *value; g_return_val_if_fail (MM_IS_MODEM_3GPP_PROFILE_MANAGER (self), NULL); g_object_get (G_OBJECT (self), "g-object-path", &value, NULL); RETURN_NON_EMPTY_STRING (value); } /*****************************************************************************/ /** * mm_modem_3gpp_profile_manager_get_index_field: * @self: A #MMModem3gppProfileManager. * * Gets the name of the field used as index in profile management * operations. * * Returns: (transfer none): The index field, or %NULL if none available. * Do not free the returned value, it belongs to @self. * * Since: 1.20 */ const gchar * mm_modem_3gpp_profile_manager_get_index_field (MMModem3gppProfileManager *self) { g_return_val_if_fail (MM_IS_MODEM_3GPP_PROFILE_MANAGER (self), NULL); RETURN_NON_EMPTY_CONSTANT_STRING ( mm_gdbus_modem3gpp_profile_manager_get_index_field (MM_GDBUS_MODEM3GPP_PROFILE_MANAGER (self))); } /** * mm_modem_3gpp_profile_manager_dup_index_field: * @self: A #MMModem3gppProfileManager. * * Gets a copy of the name of the field used as index in profile management * operations. * * Returns: (transfer full): The index field, or %NULL if none available. * The returned value should be freed with g_free(). * * Since: 1.20 */ gchar * mm_modem_3gpp_profile_manager_dup_index_field (MMModem3gppProfileManager *self) { g_return_val_if_fail (MM_IS_MODEM_3GPP_PROFILE_MANAGER (self), NULL); RETURN_NON_EMPTY_STRING ( mm_gdbus_modem3gpp_profile_manager_dup_index_field (MM_GDBUS_MODEM3GPP_PROFILE_MANAGER (self))); } /*****************************************************************************/ static gboolean build_list_results (GVariant *dictionaries, GList **out_profiles, GError **error) { g_autoptr(GError) saved_error = NULL; GVariantIter iter; guint n; GList *profiles = NULL; if (out_profiles) *out_profiles = NULL; if (!dictionaries) return TRUE; /* Parse array of dictionaries */ g_variant_iter_init (&iter, dictionaries); n = g_variant_iter_n_children (&iter); if (n > 0) { GVariant* dictionary = NULL; while ((dictionary = g_variant_iter_next_value (&iter))) { MM3gppProfile *profile = NULL; g_autoptr(GError) inner_error = NULL; profile = mm_3gpp_profile_new_from_dictionary (dictionary, &inner_error); if (!profile) { g_warning ("Couldn't create 3GPP profile: %s", inner_error->message); if (!saved_error) saved_error = g_steal_pointer (&inner_error); } else profiles = g_list_append (profiles, profile); g_variant_unref (dictionary); } } if (saved_error && !profiles) { g_propagate_error (error, g_steal_pointer (&saved_error)); return FALSE; } if (out_profiles) *out_profiles = profiles; else g_list_free_full (profiles, g_object_unref); return TRUE; } /** * mm_modem_3gpp_profile_manager_list_finish: * @self: A #MMModem3gppProfileManager. * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to * mm_modem_3gpp_profile_manager_list(). * @profiles: (out) (allow-none) (transfer full) (element-type ModemManager.3gppProfile): * A list of #MM3gppProfile objects available in the device. The returned value * should be freed with g_list_free_full() using g_object_unref() as * #GDestroyNotify. * @error: Return location for error or %NULL. * * Finishes an operation started with mm_modem_3gpp_profile_manager_list(). * * Returns: %TRUE if the list was correctly retrieved, %FALSE if @error is set. * * Since: 1.18 */ gboolean mm_modem_3gpp_profile_manager_list_finish (MMModem3gppProfileManager *self, GAsyncResult *res, GList **profiles, GError **error) { g_autoptr(GVariant) dictionaries = NULL; g_return_val_if_fail (MM_IS_MODEM_3GPP_PROFILE_MANAGER (self), FALSE); if (!mm_gdbus_modem3gpp_profile_manager_call_list_finish (MM_GDBUS_MODEM3GPP_PROFILE_MANAGER (self), &dictionaries, res, error)) return FALSE; return build_list_results (dictionaries, profiles, error); } /** * mm_modem_3gpp_profile_manager_list: * @self: A #MMModem3gppProfileManager. * @cancellable: (allow-none): A #GCancellable or %NULL. * @callback: A #GAsyncReadyCallback to call when the request is satisfied or * %NULL. * @user_data: User data to pass to @callback. * * Asynchronously gets the list of available connection profiles. * * When the operation is finished, @callback will be invoked in the * thread-default main loop * of the thread you are calling this method from. You can then call * mm_modem_3gpp_profile_manager_list_finish() to get the result of the * operation. * * See mm_modem_3gpp_profile_manager_list_sync() for the synchronous, blocking * version of this method. * * Since: 1.18 */ void mm_modem_3gpp_profile_manager_list (MMModem3gppProfileManager *self, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { g_return_if_fail (MM_IS_MODEM_3GPP_PROFILE_MANAGER (self)); mm_gdbus_modem3gpp_profile_manager_call_list (MM_GDBUS_MODEM3GPP_PROFILE_MANAGER (self), cancellable, callback, user_data); } /** * mm_modem_3gpp_profile_manager_list_sync: * @self: A #MMModem3gppProfileManager. * @cancellable: (allow-none): A #GCancellable or %NULL. * @profiles: (out) (allow-none) (transfer full) (element-type ModemManager.3gppProfile): * A list of #MM3gppProfile objects available in the device. The returned value * should be freed with g_list_free_full() using g_object_unref() as * #GDestroyNotify. * @error: Return location for error or %NULL. * * Synchronously gets the list of available connection profiles. * * The calling thread is blocked until a reply is received. See * mm_modem_3gpp_profile_manager_list() for the asynchronous version of this * method. * * Returns: %TRUE if the list was correctly retrieved, %FALSE if @error is set. * * Since: 1.18 */ gboolean mm_modem_3gpp_profile_manager_list_sync (MMModem3gppProfileManager *self, GCancellable *cancellable, GList **profiles, GError **error) { g_autoptr(GVariant) dictionaries = NULL; g_return_val_if_fail (MM_IS_MODEM_3GPP_PROFILE_MANAGER (self), FALSE); if (!mm_gdbus_modem3gpp_profile_manager_call_list_sync (MM_GDBUS_MODEM3GPP_PROFILE_MANAGER (self), &dictionaries, cancellable, error)) return FALSE; return build_list_results (dictionaries, profiles, error); } /*****************************************************************************/ /** * mm_modem_3gpp_profile_manager_set_finish: * @self: A #MMModem3gppProfileManager. * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to * mm_modem_3gpp_profile_manager_set(). * @error: Return location for error or %NULL. * * Finishes an operation started with mm_modem_3gpp_profile_manager_set(). * * Returns: (transfer full): A #MM3gppProfile with the stored settings, or %NULL if @error is set. * * Since: 1.18 */ MM3gppProfile * mm_modem_3gpp_profile_manager_set_finish (MMModem3gppProfileManager *self, GAsyncResult *res, GError **error) { g_autoptr(GVariant) stored_dictionary = NULL; g_return_val_if_fail (MM_IS_MODEM_3GPP_PROFILE_MANAGER (self), NULL); if (!mm_gdbus_modem3gpp_profile_manager_call_set_finish (MM_GDBUS_MODEM3GPP_PROFILE_MANAGER (self), &stored_dictionary, res, error)) return NULL; return mm_3gpp_profile_new_from_dictionary (stored_dictionary, error); } /** * mm_modem_3gpp_profile_manager_set: * @self: A #MMModem3gppProfileManager. * @requested: A #MM3gppProfile with the requested settings. * @cancellable: (allow-none): A #GCancellable or %NULL. * @callback: A #GAsyncReadyCallback to call when the request is satisfied or * %NULL. * @user_data: User data to pass to @callback. * * Asynchronously updates a connection profile with the settings * given in @profile. * * If @profile does not have an explicit profile ID set, a new profile will * be created. * * When the operation is finished, @callback will be invoked in the * thread-default main loop * of the thread you are calling this method from. You can then call * mm_modem_3gpp_profile_manager_set_finish() to get the result of the * operation. * * See mm_modem_3gpp_profile_manager_set_sync() for the synchronous, blocking * version of this method. * * Since: 1.18 */ void mm_modem_3gpp_profile_manager_set (MMModem3gppProfileManager *self, MM3gppProfile *requested, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { g_autoptr(GVariant) requested_dictionary = NULL; g_return_if_fail (MM_IS_MODEM_3GPP_PROFILE_MANAGER (self)); requested_dictionary = mm_3gpp_profile_get_dictionary (requested); mm_gdbus_modem3gpp_profile_manager_call_set (MM_GDBUS_MODEM3GPP_PROFILE_MANAGER (self), requested_dictionary, cancellable, callback, user_data); } /** * mm_modem_3gpp_profile_manager_set_sync: * @self: A #MMModem3gppProfileManager. * @requested: A #MM3gppProfile with the requested settings. * @cancellable: (allow-none): A #GCancellable or %NULL. * @error: Return location for error or %NULL. * * Synchronously updates a connection profile with the settings * given in @profile. * * If @profile does not have an explicit profile ID set, a new profile will * be created. * * The calling thread is blocked until a reply is received. See * mm_modem_3gpp_profile_manager_set() for the asynchronous version of this * method. * * Returns: (transfer full): A #MM3gppProfile with the stored settings, or %NULL if @error is set. * * Since: 1.18 */ MM3gppProfile * mm_modem_3gpp_profile_manager_set_sync (MMModem3gppProfileManager *self, MM3gppProfile *requested, GCancellable *cancellable, GError **error) { g_autoptr(GVariant) requested_dictionary = NULL; g_autoptr(GVariant) stored_dictionary = NULL; g_return_val_if_fail (MM_IS_MODEM_3GPP_PROFILE_MANAGER (self), NULL); requested_dictionary = mm_3gpp_profile_get_dictionary (requested); if (!mm_gdbus_modem3gpp_profile_manager_call_set_sync (MM_GDBUS_MODEM3GPP_PROFILE_MANAGER (self), requested_dictionary, &stored_dictionary, cancellable, error)) return NULL; return mm_3gpp_profile_new_from_dictionary (stored_dictionary, error); } /*****************************************************************************/ /** * mm_modem_3gpp_profile_manager_delete_finish: * @self: A #MMModem3gppProfileManager. * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to * mm_modem_3gpp_profile_manager_delete(). * @error: Return location for error or %NULL. * * Finishes an operation started with mm_modem_3gpp_profile_manager_delete(). * * Returns: %TRUE if the operation was successful, %FALSE if @error is set. * * Since: 1.18 */ gboolean mm_modem_3gpp_profile_manager_delete_finish (MMModem3gppProfileManager *self, GAsyncResult *res, GError **error) { g_return_val_if_fail (MM_IS_MODEM_3GPP_PROFILE_MANAGER (self), FALSE); return mm_gdbus_modem3gpp_profile_manager_call_delete_finish (MM_GDBUS_MODEM3GPP_PROFILE_MANAGER (self), res, error); } /** * mm_modem_3gpp_profile_manager_delete: * @self: A #MMModem3gppProfileManager. * @profile: A #MM3gppProfile. * @cancellable: (allow-none): A #GCancellable or %NULL. * @callback: A #GAsyncReadyCallback to call when the request is satisfied or * %NULL. * @user_data: User data to pass to @callback. * * Asynchronously deletes the connection profile. * * The @profile should have at least the profile ID set for the delete operation * to succeed. * * When the operation is finished, @callback will be invoked in the * thread-default main loop * of the thread you are calling this method from. You can then call * mm_modem_3gpp_profile_manager_delete_finish() to get the result of the * operation. * * See mm_modem_3gpp_profile_manager_delete_sync() for the synchronous, blocking * version of this method. * * Since: 1.18 */ void mm_modem_3gpp_profile_manager_delete (MMModem3gppProfileManager *self, MM3gppProfile *profile, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { g_autoptr(GVariant) profile_dictionary = NULL; g_return_if_fail (MM_IS_MODEM_3GPP_PROFILE_MANAGER (self)); profile_dictionary = mm_3gpp_profile_get_dictionary (profile); mm_gdbus_modem3gpp_profile_manager_call_delete (MM_GDBUS_MODEM3GPP_PROFILE_MANAGER (self), profile_dictionary, cancellable, callback, user_data); } /** * mm_modem_3gpp_profile_manager_delete_sync: * @self: A #MMModem3gppProfileManager. * @profile: A #MM3gppProfile. * @cancellable: (allow-none): A #GCancellable or %NULL. * @error: Return location for error or %NULL. * * Synchronously deletes the connection profile. * * The @profile should have at least the profile ID set for the delete operation * to succeed. * * The calling thread is blocked until a reply is received. See * mm_modem_3gpp_profile_manager_delete() for the asynchronous version of this * method. * * Returns: %TRUE if the operation was successful, %FALSE if @error is set. * * Since: 1.18 */ gboolean mm_modem_3gpp_profile_manager_delete_sync (MMModem3gppProfileManager *self, MM3gppProfile *profile, GCancellable *cancellable, GError **error) { g_autoptr(GVariant) profile_dictionary = NULL; g_return_val_if_fail (MM_IS_MODEM_3GPP_PROFILE_MANAGER (self), FALSE); profile_dictionary = mm_3gpp_profile_get_dictionary (profile); return mm_gdbus_modem3gpp_profile_manager_call_delete_sync (MM_GDBUS_MODEM3GPP_PROFILE_MANAGER (self), profile_dictionary, cancellable, error); } /*****************************************************************************/ static void mm_modem_3gpp_profile_manager_init (MMModem3gppProfileManager *self) { } static void mm_modem_3gpp_profile_manager_class_init (MMModem3gppProfileManagerClass *modem_class) { } ModemManager-1.23.4-dev/libmm-glib/mm-modem-3gpp-profile-manager.h000066400000000000000000000153051456466623000245420ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * libmm-glib -- Access modem status & information from glib applications * * 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 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 Street, Fifth Floor, * Boston, MA 02110-1301 USA. * * Copyright (C) 2021 Aleksander Morgado * Copyright (C) 2021 Google, Inc. */ #ifndef _MM_MODEM_3GPP_PROFILE_MANAGER_H_ #define _MM_MODEM_3GPP_PROFILE_MANAGER_H_ #if !defined (__LIBMM_GLIB_H_INSIDE__) && !defined (LIBMM_GLIB_COMPILATION) #error "Only can be included directly." #endif #include #include "mm-3gpp-profile.h" #include "mm-gdbus-modem.h" G_BEGIN_DECLS #define MM_TYPE_MODEM_3GPP_PROFILE_MANAGER (mm_modem_3gpp_profile_manager_get_type ()) #define MM_MODEM_3GPP_PROFILE_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_MODEM_3GPP_PROFILE_MANAGER, MMModem3gppProfileManager)) #define MM_MODEM_3GPP_PROFILE_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_MODEM_3GPP_PROFILE_MANAGER, MMModem3gppProfileManagerClass)) #define MM_IS_MODEM_3GPP_PROFILE_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_MODEM_3GPP_PROFILE_MANAGER)) #define MM_IS_MODEM_3GPP_PROFILE_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), MM_TYPE_MODEM_3GPP_PROFILE_MANAGER)) #define MM_MODEM_3GPP_PROFILE_MANAGER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_MODEM_3GPP_PROFILE_MANAGER, MMModem3gppProfileManagerClass)) typedef struct _MMModem3gppProfileManager MMModem3gppProfileManager; typedef struct _MMModem3gppProfileManagerClass MMModem3gppProfileManagerClass; /** * MMModem3gppProfileManager: * * The #MMModem3gppProfileManager structure contains private data and should only be accessed * using the provided API. */ struct _MMModem3gppProfileManager { /*< private >*/ MmGdbusModem3gppProfileManagerProxy parent; gpointer unused; }; struct _MMModem3gppProfileManagerClass { /*< private >*/ MmGdbusModem3gppProfileManagerProxyClass parent; }; GType mm_modem_3gpp_profile_manager_get_type (void); G_DEFINE_AUTOPTR_CLEANUP_FUNC (MMModem3gppProfileManager, g_object_unref) const gchar *mm_modem_3gpp_profile_manager_get_path (MMModem3gppProfileManager *self); gchar *mm_modem_3gpp_profile_manager_dup_path (MMModem3gppProfileManager *self); const gchar *mm_modem_3gpp_profile_manager_get_index_field (MMModem3gppProfileManager *self); gchar *mm_modem_3gpp_profile_manager_dup_index_field (MMModem3gppProfileManager *self); void mm_modem_3gpp_profile_manager_list (MMModem3gppProfileManager *self, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); gboolean mm_modem_3gpp_profile_manager_list_finish (MMModem3gppProfileManager *self, GAsyncResult *res, GList **profiles, GError **error); gboolean mm_modem_3gpp_profile_manager_list_sync (MMModem3gppProfileManager *self, GCancellable *cancellable, GList **profiles, GError **error); void mm_modem_3gpp_profile_manager_set (MMModem3gppProfileManager *self, MM3gppProfile *requested, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); MM3gppProfile *mm_modem_3gpp_profile_manager_set_finish (MMModem3gppProfileManager *self, GAsyncResult *res, GError **error); MM3gppProfile *mm_modem_3gpp_profile_manager_set_sync (MMModem3gppProfileManager *self, MM3gppProfile *requested, GCancellable *cancellable, GError **error); void mm_modem_3gpp_profile_manager_delete (MMModem3gppProfileManager *self, MM3gppProfile *profile, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); gboolean mm_modem_3gpp_profile_manager_delete_finish (MMModem3gppProfileManager *self, GAsyncResult *res, GError **error); gboolean mm_modem_3gpp_profile_manager_delete_sync (MMModem3gppProfileManager *self, MM3gppProfile *profile, GCancellable *cancellable, GError **error); G_END_DECLS #endif /* _MM_MODEM_3GPP_PROFILE_MANAGER_H_ */ ModemManager-1.23.4-dev/libmm-glib/mm-modem-3gpp-ussd.c000066400000000000000000000403151456466623000224420ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * libmm-glib -- Access modem status & information from glib applications * * 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 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 Street, Fifth Floor, * Boston, MA 02110-1301 USA. * * Copyright (C) 2011 - 2012 Aleksander Morgado * Copyright (C) 2012 Google, Inc. */ #include #include "mm-helpers.h" #include "mm-errors-types.h" #include "mm-modem-3gpp-ussd.h" /** * SECTION: mm-modem-3gpp-ussd * @title: MMModem3gppUssd * @short_description: The 3GPP USSD interface * * The #MMModem3gppUssd is an object providing access to the methods, signals and * properties of the 3GPP USSD interface. * * This interface is only exposed when the 3GPP modem is known to handle USSD operations. */ G_DEFINE_TYPE (MMModem3gppUssd, mm_modem_3gpp_ussd, MM_GDBUS_TYPE_MODEM3GPP_USSD_PROXY) /*****************************************************************************/ /** * mm_modem_3gpp_ussd_get_path: * @self: A #MMModem3gppUssd. * * Gets the DBus path of the #MMObject which implements this interface. * * Returns: (transfer none): The DBus path of the #MMObject object. * * Since: 1.0 */ const gchar * mm_modem_3gpp_ussd_get_path (MMModem3gppUssd *self) { g_return_val_if_fail (MM_IS_MODEM_3GPP_USSD (self), NULL); RETURN_NON_EMPTY_CONSTANT_STRING ( g_dbus_proxy_get_object_path (G_DBUS_PROXY (self))); } /** * mm_modem_3gpp_ussd_dup_path: * @self: A #MMModem3gppUssd. * * Gets a copy of the DBus path of the #MMObject object which implements this * interface. * * Returns: (transfer full): The DBus path of the #MMObject. The returned value * should be freed with g_free(). * * Since: 1.0 */ gchar * mm_modem_3gpp_ussd_dup_path (MMModem3gppUssd *self) { gchar *value; g_return_val_if_fail (MM_IS_MODEM_3GPP_USSD (self), NULL); g_object_get (G_OBJECT (self), "g-object-path", &value, NULL); RETURN_NON_EMPTY_STRING (value); } /*****************************************************************************/ /** * mm_modem_3gpp_ussd_get_network_request: * @self: A #MMModem3gppUssd. * * Gets any pending network-initiated request. * * The returned value is only valid until the property changes so * it is only safe to use this function on the thread where * @self was constructed. Use mm_modem_3gpp_ussd_dup_network_request() if on * another thread. * * Returns: (transfer none): The network request, or %NULL if none available. * * Since: 1.0 */ const gchar * mm_modem_3gpp_ussd_get_network_request (MMModem3gppUssd *self) { g_return_val_if_fail (MM_IS_MODEM_3GPP_USSD (self), NULL); RETURN_NON_EMPTY_CONSTANT_STRING ( mm_gdbus_modem3gpp_ussd_get_network_request (MM_GDBUS_MODEM3GPP_USSD (self))); } /** * mm_modem_3gpp_ussd_dup_network_request: * @self: A #MMModem3gppUssd. * * Gets a copy of any pending network-initiated request. * * Returns: (transfer full): The network request, or %NULL if none available. * The returned value should be freed with g_free(). * * Since: 1.0 */ gchar * mm_modem_3gpp_ussd_dup_network_request (MMModem3gppUssd *self) { g_return_val_if_fail (MM_IS_MODEM_3GPP_USSD (self), NULL); RETURN_NON_EMPTY_STRING ( mm_gdbus_modem3gpp_ussd_dup_network_request (MM_GDBUS_MODEM3GPP_USSD (self))); } /*****************************************************************************/ /** * mm_modem_3gpp_ussd_get_network_notification: * @self: A #MMModem3gppUssd. * * Gets any pending network-initiated request to which no USSD response is * required. * * The returned value is only valid until the property changes so it is * only safe to use this function on the thread where @self was constructed. Use * mm_modem_3gpp_ussd_dup_network_notification() if on another thread. * * Returns: (transfer none): The network notification, or %NULL if none * available. * * Since: 1.0 */ const gchar * mm_modem_3gpp_ussd_get_network_notification (MMModem3gppUssd *self) { g_return_val_if_fail (MM_IS_MODEM_3GPP_USSD (self), NULL); RETURN_NON_EMPTY_CONSTANT_STRING ( mm_gdbus_modem3gpp_ussd_get_network_notification (MM_GDBUS_MODEM3GPP_USSD (self))); } /** * mm_modem_3gpp_ussd_dup_network_notification: * @self: A #MMModem3gppUssd. * * Gets a copy of any pending network-initiated request to which no USSD * response is required. * * Returns: (transfer full): The network notification, or %NULL if none * available. The returned value should be freed with g_free(). * * Since: 1.0 */ gchar * mm_modem_3gpp_ussd_dup_network_notification (MMModem3gppUssd *self) { g_return_val_if_fail (MM_IS_MODEM_3GPP_USSD (self), NULL); RETURN_NON_EMPTY_STRING ( mm_gdbus_modem3gpp_ussd_dup_network_notification (MM_GDBUS_MODEM3GPP_USSD (self))); } /*****************************************************************************/ /** * mm_modem_3gpp_ussd_get_state: * @self: A #MMModem. * * Get the state of the ongoing USSD session, if any. * * Returns: A #MMModem3gppUssdSessionState value, specifying the current state. * * Since: 1.0 */ MMModem3gppUssdSessionState mm_modem_3gpp_ussd_get_state (MMModem3gppUssd *self) { g_return_val_if_fail (MM_IS_MODEM_3GPP_USSD (self), MM_MODEM_3GPP_USSD_SESSION_STATE_UNKNOWN); return mm_gdbus_modem3gpp_ussd_get_state (MM_GDBUS_MODEM3GPP_USSD (self)); } /*****************************************************************************/ /** * mm_modem_3gpp_ussd_initiate_finish: * @self: A #MMModem3gppUssd. * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to * mm_modem_3gpp_ussd_initiate(). * @error: Return location for error or %NULL. * * Finishes an operation started with mm_modem_3gpp_ussd_initiate(). * * Returns: The response from the network, if any. The returned value should be * freed with g_free(). * * Since: 1.0 */ gchar * mm_modem_3gpp_ussd_initiate_finish (MMModem3gppUssd *self, GAsyncResult *res, GError **error) { gchar *reply = NULL; g_return_val_if_fail (MM_IS_MODEM_3GPP_USSD (self), NULL); mm_gdbus_modem3gpp_ussd_call_initiate_finish (MM_GDBUS_MODEM3GPP_USSD (self), &reply, res, error); return reply; } /** * mm_modem_3gpp_ussd_initiate: * @self: A #MMModem3gppUssd. * @command: The command to start the USSD session with. * @cancellable: (allow-none): A #GCancellable or %NULL. * @callback: A #GAsyncReadyCallback to call when the request is satisfied or * %NULL. * @user_data: User data to pass to @callback. * * Asynchronously sends a USSD command string to the network initiating a USSD * session. * * When the request is handled by the network, the method returns the * response or an appropriate error. The network may be awaiting further * response from the ME after returning from this method and no new command. * * When the operation is finished, @callback will be invoked in the * thread-default main loop * of the thread you are calling this method from. You can then call * mm_modem_3gpp_ussd_initiate_finish() to get the result of the operation. * * See mm_modem_3gpp_ussd_initiate_sync() for the synchronous, blocking version * of this method. * * Since: 1.0 */ void mm_modem_3gpp_ussd_initiate (MMModem3gppUssd *self, const gchar *command, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { g_return_if_fail (MM_IS_MODEM_3GPP_USSD (self)); mm_gdbus_modem3gpp_ussd_call_initiate (MM_GDBUS_MODEM3GPP_USSD (self), command, cancellable, callback, user_data); } /** * mm_modem_3gpp_ussd_initiate_sync: * @self: A #MMModem3gppUssd. * @command: The command to start the USSD session with. * @cancellable: (allow-none): A #GCancellable or %NULL. * @error: Return location for error or %NULL. * * Synchronously sends a USSD command string to the network initiating a USSD * session. * * When the request is handled by the network, the method returns the * response or an appropriate error. The network may be awaiting further * response from the ME after returning from this method and no new command. * * The calling thread is blocked until a reply is received. See * mm_modem_3gpp_ussd_initiate() for the asynchronous version of this method. * * Returns: The response from the network, if any. The returned value should be * freed with g_free(). * * Since: 1.0 */ gchar * mm_modem_3gpp_ussd_initiate_sync (MMModem3gppUssd *self, const gchar *command, GCancellable *cancellable, GError **error) { gchar *reply = NULL; g_return_val_if_fail (MM_IS_MODEM_3GPP_USSD (self), NULL); mm_gdbus_modem3gpp_ussd_call_initiate_sync (MM_GDBUS_MODEM3GPP_USSD (self), command, &reply, cancellable, error); return reply; } /*****************************************************************************/ /** * mm_modem_3gpp_ussd_respond_finish: * @self: A #MMModem3gppUssd. * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to * mm_modem_3gpp_ussd_respond(). * @error: Return location for error or %NULL. * * Finishes an operation started with mm_modem_3gpp_ussd_respond(). * * Returns: The network reply to this response to the network-initiated USSD * command. The reply may require further responses. The returned value should * be freed with g_free(). * * Since: 1.0 */ gchar * mm_modem_3gpp_ussd_respond_finish (MMModem3gppUssd *self, GAsyncResult *res, GError **error) { gchar *reply = NULL; g_return_val_if_fail (MM_IS_MODEM_3GPP_USSD (self), NULL); mm_gdbus_modem3gpp_ussd_call_respond_finish (MM_GDBUS_MODEM3GPP_USSD (self), &reply, res, error); return reply; } /** * mm_modem_3gpp_ussd_respond: * @self: A #MMModem3gppUssd. * @response: The response to network-initiated USSD command, or a response to a * request for further input. * @cancellable: (allow-none): A #GCancellable or %NULL. * @callback: A #GAsyncReadyCallback to call when the request is satisfied or * %NULL. * @user_data: User data to pass to @callback. * * Asynchronously responds to a USSD request that is either initiated by the * mobile network, or that is awaiting further input after a previous call to * mm_modem_3gpp_ussd_initiate(). * * When the operation is finished, @callback will be invoked in the * thread-default main loop * of the thread you are calling this method from. You can then call * mm_modem_3gpp_ussd_respond_finish() to get the result of the operation. * * See mm_modem_3gpp_ussd_respond_sync() for the synchronous, blocking version * of this method. * * Since: 1.0 */ void mm_modem_3gpp_ussd_respond (MMModem3gppUssd *self, const gchar *response, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { g_return_if_fail (MM_IS_MODEM_3GPP_USSD (self)); mm_gdbus_modem3gpp_ussd_call_respond (MM_GDBUS_MODEM3GPP_USSD (self), response, cancellable, callback, user_data); } /** * mm_modem_3gpp_ussd_respond_sync: * @self: A #MMModem3gppUssd. * @response: The response to network-initiated USSD command, or a response to a * request for further input. * @cancellable: (allow-none): A #GCancellable or %NULL. * @error: Return location for error or %NULL. * * Synchronously responds to a USSD request that is either initiated by the * mobile network, or that is awaiting further input after a previous call to * mm_modem_3gpp_ussd_initiate(). * * The calling thread is blocked until a reply is received. See * mm_modem_3gpp_ussd_respond() for the asynchronous version of this method. * * Returns: The network reply to this response to the network-initiated USSD * command. The reply may require further responses. The returned value should * be freed with g_free(). * * Since: 1.0 */ gchar * mm_modem_3gpp_ussd_respond_sync (MMModem3gppUssd *self, const gchar *response, GCancellable *cancellable, GError **error) { gchar *reply = NULL; g_return_val_if_fail (MM_IS_MODEM_3GPP_USSD (self), NULL); mm_gdbus_modem3gpp_ussd_call_respond_sync (MM_GDBUS_MODEM3GPP_USSD (self), response, &reply, cancellable, error); return reply; } /*****************************************************************************/ /** * mm_modem_3gpp_ussd_cancel_finish: * @self: A #MMModem3gppUssd. * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to * mm_modem_3gpp_ussd_cancel(). * @error: Return location for error or %NULL. * * Finishes an operation started with mm_modem_3gpp_ussd_cancel(). * * Returns: %TRUE if the session was successfully cancelled, %FALSE if @error is * set. * * Since: 1.0 */ gboolean mm_modem_3gpp_ussd_cancel_finish (MMModem3gppUssd *self, GAsyncResult *res, GError **error) { g_return_val_if_fail (MM_IS_MODEM_3GPP_USSD (self), FALSE); return mm_gdbus_modem3gpp_ussd_call_cancel_finish (MM_GDBUS_MODEM3GPP_USSD (self), res, error); } /** * mm_modem_3gpp_ussd_cancel: * @self: A #MMModem3gppUssd. * @cancellable: (allow-none): A #GCancellable or %NULL. * @callback: A #GAsyncReadyCallback to call when the request is satisfied or * %NULL. * @user_data: User data to pass to @callback. * * Asynchronously cancels an ongoing USSD session, either mobile or network * initiated. * * When the operation is finished, @callback will be invoked in the * thread-default main loop * of the thread you are calling this method from. You can then call * mm_modem_3gpp_ussd_cancel_finish() to get the result of the operation. * * See mm_modem_3gpp_ussd_cancel_sync() for the synchronous, blocking version * of this method. * * Since: 1.0 */ void mm_modem_3gpp_ussd_cancel (MMModem3gppUssd *self, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { g_return_if_fail (MM_IS_MODEM_3GPP_USSD (self)); mm_gdbus_modem3gpp_ussd_call_cancel (MM_GDBUS_MODEM3GPP_USSD (self), cancellable, callback, user_data); } /** * mm_modem_3gpp_ussd_cancel_sync: * @self: A #MMModem3gppUssd. * @cancellable: (allow-none): A #GCancellable or %NULL. * @error: Return location for error or %NULL. * * Synchronously cancels an ongoing USSD session, either mobile or network * initiated. * * The calling thread is blocked until a reply is received. See * mm_modem_3gpp_ussd_cancel() for the asynchronous version of this method. * * Returns: %TRUE if the session was successfully cancelled, %FALSE if @error is * set. * * Since: 1.0 */ gboolean mm_modem_3gpp_ussd_cancel_sync (MMModem3gppUssd *self, GCancellable *cancellable, GError **error) { g_return_val_if_fail (MM_IS_MODEM_3GPP_USSD (self), FALSE); return mm_gdbus_modem3gpp_ussd_call_cancel_sync (MM_GDBUS_MODEM3GPP_USSD (self), cancellable, error); } /*****************************************************************************/ static void mm_modem_3gpp_ussd_init (MMModem3gppUssd *self) { } static void mm_modem_3gpp_ussd_class_init (MMModem3gppUssdClass *modem_class) { } ModemManager-1.23.4-dev/libmm-glib/mm-modem-3gpp-ussd.h000066400000000000000000000123161456466623000224470ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * libmm-glib -- Access modem status & information from glib applications * * 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 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 Street, Fifth Floor, * Boston, MA 02110-1301 USA. * * Copyright (C) 2011 - 2012 Aleksander Morgado * Copyright (C) 2012 Google, Inc. */ #ifndef _MM_MODEM_3GPP_USSD_H_ #define _MM_MODEM_3GPP_USSD_H_ #if !defined (__LIBMM_GLIB_H_INSIDE__) && !defined (LIBMM_GLIB_COMPILATION) #error "Only can be included directly." #endif #include #include "mm-gdbus-modem.h" G_BEGIN_DECLS #define MM_TYPE_MODEM_3GPP_USSD (mm_modem_3gpp_ussd_get_type ()) #define MM_MODEM_3GPP_USSD(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_MODEM_3GPP_USSD, MMModem3gppUssd)) #define MM_MODEM_3GPP_USSD_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_MODEM_3GPP_USSD, MMModem3gppUssdClass)) #define MM_IS_MODEM_3GPP_USSD(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_MODEM_3GPP_USSD)) #define MM_IS_MODEM_3GPP_USSD_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), MM_TYPE_MODEM_3GPP_USSD)) #define MM_MODEM_3GPP_USSD_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_MODEM_3GPP_USSD, MMModem3gppUssdClass)) typedef struct _MMModem3gppUssd MMModem3gppUssd; typedef struct _MMModem3gppUssdClass MMModem3gppUssdClass; /** * MMModem3gppUssd: * * The #MMModem3gppUssd structure contains private data and should only be accessed * using the provided API. */ struct _MMModem3gppUssd { /*< private >*/ MmGdbusModem3gppUssdProxy parent; gpointer unused; }; struct _MMModem3gppUssdClass { /*< private >*/ MmGdbusModem3gppUssdProxyClass parent; }; GType mm_modem_3gpp_ussd_get_type (void); G_DEFINE_AUTOPTR_CLEANUP_FUNC (MMModem3gppUssd, g_object_unref) const gchar *mm_modem_3gpp_ussd_get_path (MMModem3gppUssd *self); gchar *mm_modem_3gpp_ussd_dup_path (MMModem3gppUssd *self); MMModem3gppUssdSessionState mm_modem_3gpp_ussd_get_state (MMModem3gppUssd *self); const gchar *mm_modem_3gpp_ussd_get_network_notification (MMModem3gppUssd *self); gchar *mm_modem_3gpp_ussd_dup_network_notification (MMModem3gppUssd *self); const gchar *mm_modem_3gpp_ussd_get_network_request (MMModem3gppUssd *self); gchar *mm_modem_3gpp_ussd_dup_network_request (MMModem3gppUssd *self); void mm_modem_3gpp_ussd_initiate (MMModem3gppUssd *self, const gchar *command, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); gchar *mm_modem_3gpp_ussd_initiate_finish (MMModem3gppUssd *self, GAsyncResult *res, GError **error); gchar *mm_modem_3gpp_ussd_initiate_sync (MMModem3gppUssd *self, const gchar *command, GCancellable *cancellable, GError **error); void mm_modem_3gpp_ussd_respond (MMModem3gppUssd *self, const gchar *response, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); gchar *mm_modem_3gpp_ussd_respond_finish (MMModem3gppUssd *self, GAsyncResult *res, GError **error); gchar *mm_modem_3gpp_ussd_respond_sync (MMModem3gppUssd *self, const gchar *response, GCancellable *cancellable, GError **error); void mm_modem_3gpp_ussd_cancel (MMModem3gppUssd *self, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); gboolean mm_modem_3gpp_ussd_cancel_finish (MMModem3gppUssd *self, GAsyncResult *res, GError **error); gboolean mm_modem_3gpp_ussd_cancel_sync (MMModem3gppUssd *self, GCancellable *cancellable, GError **error); G_END_DECLS #endif /* _MM_MODEM_3GPP_USSD_H_ */ ModemManager-1.23.4-dev/libmm-glib/mm-modem-3gpp.c000066400000000000000000001601401456466623000214650ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * libmm-glib -- Access modem status & information from glib applications * * 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 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 Street, Fifth Floor, * Boston, MA 02110-1301 USA. * * Copyright (C) 2011 - 2012 Aleksander Morgado * Copyright (C) 2012 Google, Inc. */ #include #include "mm-helpers.h" #include "mm-errors-types.h" #include "mm-modem-3gpp.h" #include "mm-bearer.h" #include "mm-pco.h" #define MM_GDBUS_MODEM_3GPP MM_GDBUS_MODEM3GPP /** * SECTION: mm-modem-3gpp * @title: MMModem3gpp * @short_description: The 3GPP interface * * The #MMModem3gpp is an object providing access to the methods, signals and * properties of the 3GPP interface. * * The 3GPP interface is exposed whenever a modem has any of the 3GPP * capabilities (%MM_MODEM_CAPABILITY_GSM_UMTS, %MM_MODEM_CAPABILITY_LTE * or %MM_MODEM_CAPABILITY_5GNR). */ G_DEFINE_TYPE (MMModem3gpp, mm_modem_3gpp, MM_GDBUS_TYPE_MODEM3GPP_PROXY) struct _MMModem3gppPrivate { /* Common mutex to sync access */ GMutex mutex; PROPERTY_OBJECT_DECLARE (initial_eps_bearer_settings, MMBearerProperties) PROPERTY_OBJECT_DECLARE (nr5g_registration_settings, MMNr5gRegistrationSettings) }; /*****************************************************************************/ /** * mm_modem_3gpp_get_path: * @self: A #MMModem3gpp. * * Gets the DBus path of the #MMObject which implements this interface. * * Returns: (transfer none): The DBus path of the #MMObject object. * * Since: 1.0 */ const gchar * mm_modem_3gpp_get_path (MMModem3gpp *self) { g_return_val_if_fail (MM_IS_MODEM_3GPP (self), NULL); RETURN_NON_EMPTY_CONSTANT_STRING ( g_dbus_proxy_get_object_path (G_DBUS_PROXY (self))); } /** * mm_modem_3gpp_dup_path: * @self: A #MMModem3gpp. * * Gets a copy of the DBus path of the #MMObject object which implements this * interface. * * Returns: (transfer full): The DBus path of the #MMObject. The returned value * should be freed with g_free(). * * Since: 1.0 */ gchar * mm_modem_3gpp_dup_path (MMModem3gpp *self) { gchar *value; g_return_val_if_fail (MM_IS_MODEM_3GPP (self), NULL); g_object_get (G_OBJECT (self), "g-object-path", &value, NULL); RETURN_NON_EMPTY_STRING (value); } /*****************************************************************************/ /** * mm_modem_3gpp_get_imei: * @self: A #MMModem3gpp. * * Gets the IMEI, * as reported by this #MMModem3gpp. * * The returned value is only valid until the property changes so * it is only safe to use this function on the thread where * @self was constructed. Use mm_modem_3gpp_dup_imei() if on another * thread. * * Returns: (transfer none): The IMEI, or %NULL if none available. * * Since: 1.0 */ const gchar * mm_modem_3gpp_get_imei (MMModem3gpp *self) { g_return_val_if_fail (MM_IS_MODEM_3GPP (self), NULL); RETURN_NON_EMPTY_CONSTANT_STRING ( mm_gdbus_modem3gpp_get_imei (MM_GDBUS_MODEM3GPP (self))); } /** * mm_modem_3gpp_dup_imei: * @self: A #MMModem3gpp. * * Gets a copy of the IMEI, * as reported by this #MMModem3gpp. * * Returns: (transfer full): The IMEI, or %NULL if none available. The returned * value should be freed with g_free(). * * Since: 1.0 */ gchar * mm_modem_3gpp_dup_imei (MMModem3gpp *self) { g_return_val_if_fail (MM_IS_MODEM_3GPP (self), NULL); RETURN_NON_EMPTY_STRING ( mm_gdbus_modem3gpp_dup_imei (MM_GDBUS_MODEM3GPP (self))); } /*****************************************************************************/ /** * mm_modem_3gpp_get_operator_code: * @self: A #MMModem3gpp. * * Gets the code of the operator to which the mobile is currently registered. * * Returned in the format "MCCMNC", where * MCC is the three-digit ITU E.212 Mobile Country Code * and MNC is the two- or three-digit GSM Mobile Network * Code. e.g. e"31026" or "310260". * * If the MCC and MNC are not known * or the mobile is not registered to a mobile network, this property will * be a zero-length (blank) string. * * The returned value is only valid until the property changes so * it is only safe to use this function on the thread where * @self was constructed. Use mm_modem_3gpp_dup_operator_code() if on another * thread. * * Returns: (transfer none): The operator code, or %NULL if none available. * * Since: 1.0 */ const gchar * mm_modem_3gpp_get_operator_code (MMModem3gpp *self) { g_return_val_if_fail (MM_IS_MODEM_3GPP (self), NULL); RETURN_NON_EMPTY_CONSTANT_STRING ( mm_gdbus_modem3gpp_get_operator_code (MM_GDBUS_MODEM3GPP (self))); } /** * mm_modem_3gpp_dup_operator_code: * @self: A #MMModem3gpp. * * Gets a copy of the code of the operator to which the mobile is currently * registered. * * Returned in the format "MCCMNC", where * MCC is the three-digit ITU E.212 Mobile Country Code * and MNC is the two- or three-digit GSM Mobile Network * Code. e.g. e"31026" or "310260". * * Returns: (transfer full): The operator code, or %NULL if none available. * The returned value should be freed with g_free(). * * Since: 1.0 */ gchar * mm_modem_3gpp_dup_operator_code (MMModem3gpp *self) { g_return_val_if_fail (MM_IS_MODEM_3GPP (self), NULL); RETURN_NON_EMPTY_STRING ( mm_gdbus_modem3gpp_dup_operator_code (MM_GDBUS_MODEM3GPP (self))); } /*****************************************************************************/ /** * mm_modem_3gpp_get_operator_name: * @self: A #MMModem3gpp. * * Gets the name of the operator to which the mobile is * currently registered. * * The returned value is only valid until the property changes so * it is only safe to use this function on the thread where * @self was constructed. Use mm_modem_3gpp_dup_operator_name() if on another * thread. * * Returns: (transfer none): The operator name, or %NULL if none available. * * Since: 1.0 */ const gchar * mm_modem_3gpp_get_operator_name (MMModem3gpp *self) { g_return_val_if_fail (MM_IS_MODEM_3GPP (self), NULL); RETURN_NON_EMPTY_CONSTANT_STRING ( mm_gdbus_modem3gpp_get_operator_name (MM_GDBUS_MODEM3GPP (self))); } /** * mm_modem_3gpp_dup_operator_name: * @self: A #MMModem3gpp. * * Gets a copy of the name of the operator to which the mobile is * currently registered. * * Returns: (transfer full): The operator name, or %NULL if none available. * The returned value should be freed with g_free(). * * Since: 1.0 */ gchar * mm_modem_3gpp_dup_operator_name (MMModem3gpp *self) { g_return_val_if_fail (MM_IS_MODEM_3GPP (self), NULL); RETURN_NON_EMPTY_STRING ( mm_gdbus_modem3gpp_dup_operator_name (MM_GDBUS_MODEM3GPP (self))); } /*****************************************************************************/ /** * mm_modem_3gpp_get_registration_state: * @self: A #MMModem. * * Get the the mobile registration status as defined in 3GPP TS 27.007 * section 10.1.19. * * Returns: A #MMModem3gppRegistrationState value, specifying the current * registration state. * * Since: 1.0 */ MMModem3gppRegistrationState mm_modem_3gpp_get_registration_state (MMModem3gpp *self) { g_return_val_if_fail (MM_IS_MODEM_3GPP (self), MM_MODEM_3GPP_REGISTRATION_STATE_IDLE); return mm_gdbus_modem3gpp_get_registration_state (MM_GDBUS_MODEM3GPP (self)); } /*****************************************************************************/ /** * mm_modem_3gpp_get_enabled_facility_locks: * @self: A #MMModem3gpp. * * Get the list of facilities for which PIN locking is enabled. * * Returns: A bitmask of #MMModem3gppFacility flags, specifying which facilities * have locks enabled. * * Since: 1.0 */ MMModem3gppFacility mm_modem_3gpp_get_enabled_facility_locks (MMModem3gpp *self) { g_return_val_if_fail (MM_IS_MODEM_3GPP (self), MM_MODEM_3GPP_FACILITY_NONE); return mm_gdbus_modem3gpp_get_enabled_facility_locks (MM_GDBUS_MODEM3GPP (self)); } /** * mm_modem_3gpp_get_eps_ue_mode_operation: * @self: A #MMModem3gpp. * * Get the UE mode of operation for EPS. * * Returns: A #MMModem3gppEpsUeModeOperation. * * Since: 1.8 */ MMModem3gppEpsUeModeOperation mm_modem_3gpp_get_eps_ue_mode_operation (MMModem3gpp *self) { g_return_val_if_fail (MM_IS_MODEM_3GPP (self), MM_MODEM_3GPP_EPS_UE_MODE_OPERATION_UNKNOWN); return mm_gdbus_modem3gpp_get_eps_ue_mode_operation (MM_GDBUS_MODEM3GPP (self)); } /*****************************************************************************/ /** * mm_modem_3gpp_get_pco: * @self: A #MMModem3gpp. * * Get the list of #MMPco received from the network. * * Returns: (transfer full) (element-type ModemManager.Pco): a list of #MMPco * objects, or #NULL if @error is set. The returned value should be freed with * g_list_free_full() using g_object_unref() as #GDestroyNotify function. * * Since: 1.10 */ GList * mm_modem_3gpp_get_pco (MMModem3gpp *self) { GList *pco_list = NULL; GVariant *container, *child; GVariantIter iter; g_return_val_if_fail (MM_IS_MODEM_3GPP (self), NULL); container = mm_gdbus_modem3gpp_get_pco (MM_GDBUS_MODEM3GPP (self)); g_return_val_if_fail (g_variant_is_of_type (container, G_VARIANT_TYPE ("a(ubay)")), NULL); g_variant_iter_init (&iter, container); while ((child = g_variant_iter_next_value (&iter))) { MMPco *pco; pco = mm_pco_from_variant (child, NULL); pco_list = mm_pco_list_add (pco_list, pco); g_object_unref (pco); g_variant_unref (child); } return pco_list; } /*****************************************************************************/ /** * mm_modem_3gpp_get_initial_eps_bearer_path: (skip) * @self: A #MMModem3gpp. * * Gets the DBus path of the initial EPS #MMBearer exposed in this #MMModem3gpp. * * The returned value is only valid until the property changes so * it is only safe to use this function on the thread where * @self was constructed. Use mm_modem_3gpp_dup_initial_eps_bearer_path() if on * another thread. * * Returns: (transfer none): The DBus path of the #MMBearer, or %NULL if none * available. Do not free the returned value, it belongs to @self. * * Since: 1.10 */ const gchar * mm_modem_3gpp_get_initial_eps_bearer_path (MMModem3gpp *self) { g_return_val_if_fail (MM_IS_MODEM_3GPP (self), NULL); RETURN_NON_EMPTY_CONSTANT_STRING (mm_gdbus_modem3gpp_get_initial_eps_bearer (MM_GDBUS_MODEM3GPP (self))); } /** * mm_modem_3gpp_dup_initial_eps_bearer_path: * @self: A #MMModem3gpp. * * Gets a copy of the DBus path of the initial EPS #MMBearer exposed in this * #MMModem3gpp. * * Returns: (transfer full): The DBus path of the #MMBearer, or %NULL if none * available. The returned value should be freed with g_free(). * * Since: 1.10 */ gchar * mm_modem_3gpp_dup_initial_eps_bearer_path (MMModem3gpp *self) { g_return_val_if_fail (MM_IS_MODEM_3GPP (self), NULL); RETURN_NON_EMPTY_STRING ( mm_gdbus_modem3gpp_dup_initial_eps_bearer (MM_GDBUS_MODEM3GPP (self))); } /*****************************************************************************/ /** * mm_modem_3gpp_get_packet_service_state: * @self: A #MMModem. * * Get the packet domain service state. * * Returns: A #MMModem3gppPacketServiceState value, specifying the current PS attach * state. * * Since: 1.20 */ MMModem3gppPacketServiceState mm_modem_3gpp_get_packet_service_state (MMModem3gpp *self) { g_return_val_if_fail (MM_IS_MODEM_3GPP (self), MM_MODEM_3GPP_PACKET_SERVICE_STATE_UNKNOWN); return mm_gdbus_modem3gpp_get_packet_service_state (MM_GDBUS_MODEM3GPP (self)); } /*****************************************************************************/ /** * mm_modem_3gpp_register_finish: * @self: A #MMModem3gpp. * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to * mm_modem_3gpp_register(). * @error: Return location for error or %NULL. * * Finishes an operation started with mm_modem_3gpp_register(). * * Returns: %TRUE if the modem was registered, %FALSE if @error is set. * * Since: 1.0 */ gboolean mm_modem_3gpp_register_finish (MMModem3gpp *self, GAsyncResult *res, GError **error) { g_return_val_if_fail (MM_IS_MODEM_3GPP (self), FALSE); return mm_gdbus_modem3gpp_call_register_finish (MM_GDBUS_MODEM3GPP (self), res, error); } /** * mm_modem_3gpp_register: * @self: A #MMModem3gpp. * @network_id: The operator ID to register. An empty string can be used to * register to the home network. * @cancellable: (allow-none): A #GCancellable or %NULL. * @callback: A #GAsyncReadyCallback to call when the request is satisfied or * %NULL. * @user_data: User data to pass to @callback. * * Asynchronously requests registration with a given mobile network. * * When the operation is finished, @callback will be invoked in the * thread-default main loop * of the thread you are calling this method from. You can then call * mm_modem_3gpp_register_finish() to get the result of the operation. * * See mm_modem_3gpp_register_sync() for the synchronous, blocking version of * this method. * * Since: 1.0 */ void mm_modem_3gpp_register (MMModem3gpp *self, const gchar *network_id, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { g_return_if_fail (MM_IS_MODEM_3GPP (self)); mm_gdbus_modem3gpp_call_register (MM_GDBUS_MODEM3GPP (self), network_id, cancellable, callback, user_data); } /** * mm_modem_3gpp_register_sync: * @self: A #MMModem3gpp. * @network_id: The operator ID to register. An empty string can be used to * register to the home network. * @cancellable: (allow-none): A #GCancellable or %NULL. * @error: Return location for error or %NULL. * * Synchronously requests registration with a given mobile network. * * The calling thread is blocked until a reply is received. See * mm_modem_3gpp_register() for the asynchronous version of this method. * * Returns: %TRUE if the modem was registered, %FALSE if @error is set. * * Since: 1.0 */ gboolean mm_modem_3gpp_register_sync (MMModem3gpp *self, const gchar *network_id, GCancellable *cancellable, GError **error) { g_return_val_if_fail (MM_IS_MODEM_3GPP (self), FALSE); return mm_gdbus_modem3gpp_call_register_sync (MM_GDBUS_MODEM3GPP (self), network_id, cancellable, error); } /*****************************************************************************/ struct _MMModem3gppNetwork { MMModem3gppNetworkAvailability availability; gchar *operator_long; gchar *operator_short; gchar *operator_code; MMModemAccessTechnology access_technology; }; /** * mm_modem_3gpp_network_free: * @network: A #MMModem3gppNetwork. * * Frees a #MMModem3gppNetwork. * * Since: 1.0 */ void mm_modem_3gpp_network_free (MMModem3gppNetwork *network) { if (!network) return; g_free (network->operator_long); g_free (network->operator_short); g_free (network->operator_code); g_slice_free (MMModem3gppNetwork, network); } static MMModem3gppNetwork * modem_3gpp_network_copy (MMModem3gppNetwork *network) { MMModem3gppNetwork *network_copy; network_copy = g_slice_new0 (MMModem3gppNetwork); network_copy->availability = network->availability; network_copy->operator_long = g_strdup (network->operator_long); network_copy->operator_short = g_strdup (network->operator_short); network_copy->operator_code = g_strdup (network->operator_code); network_copy->access_technology = network->access_technology; return network_copy; } G_DEFINE_BOXED_TYPE (MMModem3gppNetwork, mm_modem_3gpp_network, (GBoxedCopyFunc)modem_3gpp_network_copy, (GBoxedFreeFunc)mm_modem_3gpp_network_free) /** * mm_modem_3gpp_network_get_availability: * @network: A #MMModem3gppNetwork. * * Get availability of the 3GPP network. * * Returns: A #MMModem3gppNetworkAvailability. * * Since: 1.0 */ MMModem3gppNetworkAvailability mm_modem_3gpp_network_get_availability (const MMModem3gppNetwork *network) { g_return_val_if_fail (network != NULL, MM_MODEM_3GPP_NETWORK_AVAILABILITY_UNKNOWN); return network->availability; } /** * mm_modem_3gpp_network_get_operator_long: * @network: A #MMModem3gppNetwork. * * Get the long operator name of the 3GPP network. * * Returns: (transfer none): The long operator name, or %NULL if none available. * * Since: 1.0 */ const gchar * mm_modem_3gpp_network_get_operator_long (const MMModem3gppNetwork *network) { g_return_val_if_fail (network != NULL, NULL); return network->operator_long; } /** * mm_modem_3gpp_network_get_operator_short: * @network: A #MMModem3gppNetwork. * * Get the short operator name of the 3GPP network. * * Returns: (transfer none): The long operator name, or %NULL if none available. * * Since: 1.0 */ const gchar * mm_modem_3gpp_network_get_operator_short (const MMModem3gppNetwork *network) { g_return_val_if_fail (network != NULL, NULL); return network->operator_short; } /** * mm_modem_3gpp_network_get_operator_code: * @network: A #MMModem3gppNetwork. * * Get the operator code (MCCMNC) of the 3GPP network. * * Returns: (transfer none): The operator code, or %NULL if none available. * * Since: 1.0 */ const gchar * mm_modem_3gpp_network_get_operator_code (const MMModem3gppNetwork *network) { g_return_val_if_fail (network != NULL, NULL); return network->operator_code; } /** * mm_modem_3gpp_network_get_access_technology: * @network: A #MMModem3gppNetwork. * * Get the technology used to access the 3GPP network. * * Returns: A #MMModemAccessTechnology. * * Since: 1.0 */ MMModemAccessTechnology mm_modem_3gpp_network_get_access_technology (const MMModem3gppNetwork *network) { g_return_val_if_fail (network != NULL, MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN); return network->access_technology; } /*****************************************************************************/ /** * mm_modem_3gpp_get_initial_eps_bearer_settings: * @self: A #MMModem3gpp. * * Gets a #MMBearerProperties object specifying the settings configured in * the device to use when attaching to the LTE network. * * The values reported by @self are not updated when the values in the * interface change. Instead, the client is expected to call * mm_modem_3gpp_get_initial_eps_bearer_settings() again to get a new * #MMBearerProperties with the new values. * * Returns: (transfer full): A #MMBearerProperties that must be freed with * g_object_unref() or %NULL if unknown. * * Since: 1.10 */ /** * mm_modem_3gpp_peek_initial_eps_bearer_settings: * @self: A #MMModem3gpp. * * Gets a #MMBearerProperties object specifying the settings configured in * the device to use when attaching to the LTE network. * * The returned value is only valid until the property changes so * it is only safe to use this function on the thread where * @self was constructed. Use mm_modem_3gpp_get_initial_eps_bearer_settings() * if on another thread. * * Returns: (transfer none): A #MMBearerProperties. Do not free the returned * value, it belongs to @self. * * Since: 1.10 */ /* helpers to match the property substring name with the one in our API */ #define mm_gdbus_modem_3gpp_dup_initial_eps_bearer_settings mm_gdbus_modem3gpp_dup_initial_eps_bearer_settings PROPERTY_OBJECT_DEFINE_FAILABLE (initial_eps_bearer_settings, Modem3gpp, modem_3gpp, MODEM_3GPP, MMBearerProperties, mm_bearer_properties_new_from_dictionary) /*****************************************************************************/ static GList * create_networks_list (GVariant *variant) { GList *list = NULL; GVariantIter dict_iter; GVariant *dict; /* Input is aa{sv} */ g_variant_iter_init (&dict_iter, variant); while ((dict = g_variant_iter_next_value (&dict_iter))) { GVariantIter iter; gchar *key; GVariant *value; MMModem3gppNetwork *network; network = g_slice_new0 (MMModem3gppNetwork); g_variant_iter_init (&iter, dict); while (g_variant_iter_next (&iter, "{sv}", &key, &value)) { if (g_str_equal (key, "status")) { network->availability = (MMModem3gppNetworkAvailability)g_variant_get_uint32 (value); } else if (g_str_equal (key, "operator-long")) { g_warn_if_fail (network->operator_long == NULL); network->operator_long = g_variant_dup_string (value, NULL); } else if (g_str_equal (key, "operator-short")) { g_warn_if_fail (network->operator_short == NULL); network->operator_short = g_variant_dup_string (value, NULL); } else if (g_str_equal (key, "operator-code")) { g_warn_if_fail (network->operator_code == NULL); network->operator_code = g_variant_dup_string (value, NULL); } else if (g_str_equal (key, "access-technology")) { network->access_technology = (MMModemAccessTechnology)g_variant_get_uint32 (value); } else g_warning ("Unexpected property '%s' found in Network info", key); g_free (key); g_variant_unref (value); } list = g_list_prepend (list, network); g_variant_unref (dict); } g_variant_unref (variant); return list; } /** * mm_modem_3gpp_scan_finish: * @self: A #MMModem3gpp. * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to * mm_modem_3gpp_scan(). * @error: Return location for error or %NULL. * * Finishes an operation started with mm_modem_3gpp_scan(). * * Returns: (transfer full) (element-type ModemManager.Modem3gppNetwork): a list * of #MMModem3gppNetwork structs, or #NULL if @error is set. The returned value * should be freed with g_list_free_full() using mm_modem_3gpp_network_free() as * #GDestroyNotify function. * * Since: 1.0 */ GList * mm_modem_3gpp_scan_finish (MMModem3gpp *self, GAsyncResult *res, GError **error) { GVariant *result = NULL; g_return_val_if_fail (MM_IS_MODEM_3GPP (self), FALSE); if (!mm_gdbus_modem3gpp_call_scan_finish (MM_GDBUS_MODEM3GPP (self), &result, res, error)) return NULL; return create_networks_list (result); } /** * mm_modem_3gpp_scan: * @self: A #MMModem3gpp. * @cancellable: (allow-none): A #GCancellable or %NULL. * @callback: A #GAsyncReadyCallback to call when the request is satisfied or * %NULL. * @user_data: User data to pass to @callback. * * Asynchronously requests to scan available 3GPP networks. * * When the operation is finished, @callback will be invoked in the * thread-default main loop * of the thread you are calling this method from. You can then call * mm_modem_3gpp_scan_finish() to get the result of the operation. * * See mm_modem_3gpp_scan_sync() for the synchronous, blocking version of this * method. * * Since: 1.0 */ void mm_modem_3gpp_scan (MMModem3gpp *self, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { g_return_if_fail (MM_IS_MODEM_3GPP (self)); mm_gdbus_modem3gpp_call_scan (MM_GDBUS_MODEM3GPP (self), cancellable, callback, user_data); } /** * mm_modem_3gpp_scan_sync: * @self: A #MMModem3gpp. * @cancellable: (allow-none): A #GCancellable or %NULL. * @error: Return location for error or %NULL. * * Synchronously requests to scan available 3GPP networks. * * The calling thread is blocked until a reply is received. See * mm_modem_3gpp_scan() for the asynchronous version of this method. * * Returns: (transfer full) (element-type ModemManager.Modem3gppNetwork): a list * of #MMModem3gppNetwork structs, or #NULL if @error is set. The returned value * should be freed with g_list_free_full() using mm_modem_3gpp_network_free() as * #GDestroyNotify function. * * Since: 1.0 */ GList * mm_modem_3gpp_scan_sync (MMModem3gpp *self, GCancellable *cancellable, GError **error) { GVariant *result = NULL; g_return_val_if_fail (MM_IS_MODEM_3GPP (self), FALSE); if (!mm_gdbus_modem3gpp_call_scan_sync (MM_GDBUS_MODEM3GPP (self), &result,cancellable, error)) return NULL; return create_networks_list (result); } /*****************************************************************************/ /** * mm_modem_3gpp_set_eps_ue_mode_operation_finish: * @self: A #MMModem3gpp. * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to * mm_modem_3gpp_set_eps_ue_mode_operation(). * @error: Return location for error or %NULL. * * Finishes an operation started with mm_modem_3gpp_set_eps_ue_mode_operation(). * * Returns: %TRUE if the operation was successful, %FALSE if @error is set. * * Since: 1.8 */ gboolean mm_modem_3gpp_set_eps_ue_mode_operation_finish (MMModem3gpp *self, GAsyncResult *res, GError **error) { g_return_val_if_fail (MM_IS_MODEM_3GPP (self), FALSE); return mm_gdbus_modem3gpp_call_set_eps_ue_mode_operation_finish (MM_GDBUS_MODEM3GPP (self), res, error); } /** * mm_modem_3gpp_set_eps_ue_mode_operation: * @self: A #MMModem3gpp. * @mode: A #MMModem3gppEpsUeModeOperation. * @cancellable: (allow-none): A #GCancellable or %NULL. * @callback: A #GAsyncReadyCallback to call when the request is satisfied or * %NULL. * @user_data: User data to pass to @callback. * * Asynchronously requests to update the EPS UE mode of operation. * * When the operation is finished, @callback will be invoked in the * thread-default main loop * of the thread you are calling this method from. You can then call * mm_modem_3gpp_set_eps_ue_mode_operation_finish() to get the result of the * operation. * * See mm_modem_3gpp_set_eps_ue_mode_operation_sync() for the synchronous, * blocking version of this method. The calling thread is blocked until a reply * is received. * * Since: 1.8 */ void mm_modem_3gpp_set_eps_ue_mode_operation (MMModem3gpp *self, MMModem3gppEpsUeModeOperation mode, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { g_return_if_fail (MM_IS_MODEM_3GPP (self)); g_return_if_fail (mode != MM_MODEM_3GPP_EPS_UE_MODE_OPERATION_UNKNOWN); mm_gdbus_modem3gpp_call_set_eps_ue_mode_operation (MM_GDBUS_MODEM3GPP (self), (guint) mode, cancellable, callback, user_data); } /** * mm_modem_3gpp_set_eps_ue_mode_operation_sync: * @self: A #MMModem3gpp. * @mode: A #MMModem3gppEpsUeModeOperation. * @cancellable: (allow-none): A #GCancellable or %NULL. * @error: Return location for error or %NULL. * * Synchronously requests to update the EPS UE mode of operation. * * The calling thread is blocked until a reply is received. * See mm_modem_3gpp_set_eps_ue_mode_operation() for the asynchronous version * of this method. * * Returns: %TRUE if the operation was successful, %FALSE if @error is set. * * Since: 1.8 */ gboolean mm_modem_3gpp_set_eps_ue_mode_operation_sync (MMModem3gpp *self, MMModem3gppEpsUeModeOperation mode, GCancellable *cancellable, GError **error) { g_return_val_if_fail (MM_IS_MODEM_3GPP (self), FALSE); g_return_val_if_fail (mode != MM_MODEM_3GPP_EPS_UE_MODE_OPERATION_UNKNOWN, FALSE); return mm_gdbus_modem3gpp_call_set_eps_ue_mode_operation_sync (MM_GDBUS_MODEM3GPP (self), (guint) mode, cancellable, error); } /*****************************************************************************/ /** * mm_modem_3gpp_get_initial_eps_bearer_finish: * @self: A #MMModem3gpp. * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to * mm_modem_3gpp_get_initial_eps_bearer(). * @error: Return location for error or %NULL. * * Finishes an operation started with mm_modem_3gpp_get_initial_eps_bearer(). * * Returns: (transfer full): a #MMSim or #NULL if @error is set. The returned * value should be freed with g_object_unref(). * * Since: 1.10 */ MMBearer * mm_modem_3gpp_get_initial_eps_bearer_finish (MMModem3gpp *self, GAsyncResult *res, GError **error) { g_return_val_if_fail (MM_IS_MODEM_3GPP (self), NULL); return g_task_propagate_pointer (G_TASK (res), error); } static void modem_3gpp_get_initial_eps_bearer_ready (GDBusConnection *connection, GAsyncResult *res, GTask *task) { GError *error = NULL; GObject *sim; GObject *source_object; source_object = g_async_result_get_source_object (res); sim = g_async_initable_new_finish (G_ASYNC_INITABLE (source_object), res, &error); g_object_unref (source_object); if (error) g_task_return_error (task, error); else g_task_return_pointer (task, sim, g_object_unref); g_object_unref (task); } /** * mm_modem_3gpp_get_initial_eps_bearer: * @self: A #MMModem3gpp. * @cancellable: (allow-none): A #GCancellable or %NULL. * @callback: A #GAsyncReadyCallback to call when the request is satisfied or * %NULL. * @user_data: User data to pass to @callback. * * Asynchronously gets the initial EPS #MMBearer object exposed by this * #MMModem3gpp. * * When the operation is finished, @callback will be invoked in the * thread-default main loop * of the thread you are calling this method from. You can then call * mm_modem_3gpp_get_initial_eps_bearer_finish() to get the result of the * operation. * * See mm_modem_3gpp_get_initial_eps_bearer_sync() for the synchronous, blocking * version of this method. * * Since: 1.10 */ void mm_modem_3gpp_get_initial_eps_bearer (MMModem3gpp *self, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; const gchar *bearer_path; g_return_if_fail (MM_IS_MODEM_3GPP (self)); task = g_task_new (self, cancellable, callback, user_data); bearer_path = mm_modem_3gpp_get_initial_eps_bearer_path (self); if (!bearer_path || g_str_equal (bearer_path, "/")) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_NOT_FOUND, "No initial EPS bearer object available"); g_object_unref (task); return; } g_async_initable_new_async (MM_TYPE_BEARER, G_PRIORITY_DEFAULT, cancellable, (GAsyncReadyCallback)modem_3gpp_get_initial_eps_bearer_ready, task, "g-flags", G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START, "g-name", MM_DBUS_SERVICE, "g-connection", g_dbus_proxy_get_connection (G_DBUS_PROXY (self)), "g-object-path", bearer_path, "g-interface-name", "org.freedesktop.ModemManager1.Bearer", NULL); } /** * mm_modem_3gpp_get_initial_eps_bearer_sync: * @self: A #MMModem3gpp. * @cancellable: (allow-none): A #GCancellable or %NULL. * @error: Return location for error or %NULL. * * Synchronously gets the initial EPS #MMBearer object exposed by this * #MMModem3gpp. * * The calling thread is blocked until a reply is received. See * mm_modem_3gpp_get_initial_eps_bearer() for the asynchronous version of this * method. * * Returns: (transfer full): a #MMBearer or #NULL if @error is set. The returned * value should be freed with g_object_unref(). * * Since: 1.10 */ MMBearer * mm_modem_3gpp_get_initial_eps_bearer_sync (MMModem3gpp *self, GCancellable *cancellable, GError **error) { GObject *bearer; const gchar *bearer_path; g_return_val_if_fail (MM_IS_MODEM_3GPP (self), NULL); bearer_path = mm_modem_3gpp_get_initial_eps_bearer_path (self); if (!bearer_path || g_str_equal (bearer_path, "/")) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_NOT_FOUND, "No initial EPS bearer object available"); return NULL; } bearer = g_initable_new (MM_TYPE_BEARER, cancellable, error, "g-flags", G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START, "g-name", MM_DBUS_SERVICE, "g-connection", g_dbus_proxy_get_connection (G_DBUS_PROXY (self)), "g-object-path", bearer_path, "g-interface-name", "org.freedesktop.ModemManager1.Bearer", NULL); return (bearer ? MM_BEARER (bearer) : NULL); } /*****************************************************************************/ /** * mm_modem_3gpp_set_initial_eps_bearer_settings_finish: * @self: A #MMModem3gpp. * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to * mm_modem_3gpp_set_initial_eps_bearer_settings(). * @error: Return location for error or %NULL. * * Finishes an operation started with * mm_modem_3gpp_set_initial_eps_bearer_settings(). * * Returns: %TRUE if the operation was successful, %FALSE if @error is set. * * Since: 1.10 */ gboolean mm_modem_3gpp_set_initial_eps_bearer_settings_finish (MMModem3gpp *self, GAsyncResult *res, GError **error) { return mm_gdbus_modem3gpp_call_set_initial_eps_bearer_settings_finish (MM_GDBUS_MODEM3GPP (self), res, error); } /** * mm_modem_3gpp_set_initial_eps_bearer_settings: * @self: A #MMModem3gpp. * @config: A #MMBearerProperties object with the properties to use. * @cancellable: (allow-none): A #GCancellable or %NULL. * @callback: A #GAsyncReadyCallback to call when the request is satisfied or * %NULL. * @user_data: User data to pass to @callback. * * Asynchronously configures the settings for the initial LTE default bearer. * * When the operation is finished, @callback will be invoked in the * thread-default main loop * of the thread you are calling this method from. You can then call * mm_modem_3gpp_set_initial_eps_bearer_settings_finish() to get the result of * the operation. * * Since: 1.10 */ void mm_modem_3gpp_set_initial_eps_bearer_settings (MMModem3gpp *self, MMBearerProperties *config, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { GVariant *dictionary; dictionary = mm_bearer_properties_get_dictionary (config); mm_gdbus_modem3gpp_call_set_initial_eps_bearer_settings (MM_GDBUS_MODEM3GPP (self), dictionary, cancellable, callback, user_data); g_variant_unref (dictionary); } /** * mm_modem_3gpp_set_initial_eps_bearer_settings_sync: * @self: A #MMModem3gpp. * @config: A #MMBearerProperties object with the properties to use. * @cancellable: (allow-none): A #GCancellable or %NULL. * @error: Return location for error or %NULL. * * Synchronously configures the settings for the initial LTE default bearer. * * The calling thread is blocked until a reply is received. See * mm_modem_3gpp_set_initial_eps_bearer_settings() for the asynchronous * version of this method. * * Returns: %TRUE if the operation was successful, %FALSE if @error is set. * * Since: 1.10 */ gboolean mm_modem_3gpp_set_initial_eps_bearer_settings_sync (MMModem3gpp *self, MMBearerProperties *config, GCancellable *cancellable, GError **error) { gboolean result; GVariant *dictionary; dictionary = mm_bearer_properties_get_dictionary (config); result = mm_gdbus_modem3gpp_call_set_initial_eps_bearer_settings_sync (MM_GDBUS_MODEM3GPP (self), dictionary, cancellable, error); g_variant_unref (dictionary); return result; } /*****************************************************************************/ /** * mm_modem_3gpp_disable_facility_lock: * @self: A #MMModem3gpp. * @facility: Single bit value describing the modem personalization lock to disable. * @control_key: String with control key required to unlock the personalization. * @cancellable: (allow-none): A #GCancellable or %NULL. * @callback: A #GAsyncReadyCallback to call when the request is satisfied or * %NULL. * @user_data: User data to pass to @callback. * * Asynchronously disables the modem personalization lock. * * When the operation is finished, @callback will be invoked in the * thread-default main loop * of the thread you are calling this method from. You can then call * mm_modem_3gpp_disable_facility_lock_finish() to get the result of * the operation. * * Since: 1.20 */ void mm_modem_3gpp_disable_facility_lock (MMModem3gpp *self, MMModem3gppFacility facility, const gchar *control_key, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { GVariant *properties; properties = g_variant_ref_sink (g_variant_new ("(us)", (guint)facility, control_key)); mm_gdbus_modem3gpp_call_disable_facility_lock (MM_GDBUS_MODEM3GPP (self), properties, cancellable, callback, user_data); g_variant_unref (properties); } /** * mm_modem_3gpp_disable_facility_lock_finish: * @self: A #MMModem3gpp. * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to * mm_modem_3gpp_disable_facility_lock(). * @error: Return location for error or %NULL. * * Finishes an operation started with mm_modem_3gpp_disable_facility_lock(). * * Returns: %TRUE if the operation was successful, %FALSE if @error is set. * * Since: 1.20 */ gboolean mm_modem_3gpp_disable_facility_lock_finish (MMModem3gpp *self, GAsyncResult *res, GError **error) { return mm_gdbus_modem3gpp_call_disable_facility_lock_finish (MM_GDBUS_MODEM3GPP (self), res, error); } /** * mm_modem_3gpp_disable_facility_lock_sync: * @self: A #MMModem3gpp. * @facility: Single bit value describing the modem personalization lock to disable. * @control_key: String with control key required to unlock the personalization. * @cancellable: (allow-none): A #GCancellable or %NULL. * @error: Return location for error or %NULL. * * Synchronously disables facility lock. * * The calling thread is blocked until a reply is received. * See mm_modem_3gpp_disable_facility_lock() for the asynchronous * version of this method. * * Returns: %TRUE if the operation was successful, %FALSE if @error is set. * * Since: 1.20 */ gboolean mm_modem_3gpp_disable_facility_lock_sync (MMModem3gpp *self, MMModem3gppFacility facility, const gchar *control_key, GCancellable *cancellable, GError **error) { GVariant *properties; gboolean result; properties = g_variant_ref_sink (g_variant_new ("(us)", (guint)facility, control_key)); result = mm_gdbus_modem3gpp_call_disable_facility_lock_sync (MM_GDBUS_MODEM3GPP (self), properties, cancellable, error); g_variant_unref (properties); return result; } /*****************************************************************************/ /** * mm_modem_3gpp_set_packet_service_state_finish: * @self: A #MMModem3gpp. * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to * mm_modem_3gpp_set_packet_service_state(). * @error: Return location for error or %NULL. * * Finishes an operation started with mm_modem_3gpp_set_packet_service_state(). * * Returns: %TRUE if the operation was successful, %FALSE if @error is set. * * Since: 1.20 */ gboolean mm_modem_3gpp_set_packet_service_state_finish (MMModem3gpp *self, GAsyncResult *res, GError **error) { g_return_val_if_fail (MM_IS_MODEM_3GPP (self), FALSE); return mm_gdbus_modem3gpp_call_set_packet_service_state_finish (MM_GDBUS_MODEM3GPP (self), res, error); } /** * mm_modem_3gpp_set_packet_service_state: * @self: A #MMModem3gpp. * @state: A #MMModem3gppPacketServiceState. * @cancellable: (allow-none): A #GCancellable or %NULL. * @callback: A #GAsyncReadyCallback to call when the request is satisfied or * %NULL. * @user_data: User data to pass to @callback. * * Asynchronously tries to attach or detach from the packet domain service. * * * When the operation is finished, @callback will be invoked in the * thread-default main loop * of the thread you are calling this method from. You can then call * mm_modem_3gpp_set_packet_service_state_finish() to get the result of the operation. * * See mm_modem_3gpp_set_packet_service_state_sync() for the synchronous, * blocking version of this method. * * Since: 1.20 */ void mm_modem_3gpp_set_packet_service_state (MMModem3gpp *self, MMModem3gppPacketServiceState state, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { g_return_if_fail (MM_IS_MODEM_3GPP (self)); mm_gdbus_modem3gpp_call_set_packet_service_state (MM_GDBUS_MODEM3GPP (self), state, cancellable, callback, user_data); } /** * mm_modem_3gpp_set_packet_service_state_sync: * @self: A #MMModem3gpp. * @state: A #MMModem3gppPacketServiceState. * @cancellable: (allow-none): A #GCancellable or %NULL. * @error: Return location for error or %NULL. * * Synchronously tries to attach or detach from the packet domain service. * * The calling thread is blocked until a reply is received. See * mm_modem_3gpp_set_packet_service_state() for the asynchronous version of * this method. * * Returns: %TRUE if the operation was successful, %FALSE if @error is set. * * Since: 1.20 */ gboolean mm_modem_3gpp_set_packet_service_state_sync (MMModem3gpp *self, MMModem3gppPacketServiceState state, GCancellable *cancellable, GError **error) { g_return_val_if_fail (MM_IS_MODEM_3GPP (self), FALSE); return mm_gdbus_modem3gpp_call_set_packet_service_state_sync (MM_GDBUS_MODEM3GPP (self), state, cancellable, error); } /*****************************************************************************/ /** * mm_modem_3gpp_get_nr5g_registration_settings: * @self: A #MMModem3gpp. * * Gets a #MMNr5gRegistrationSettings object including the configured 5GNR * registration settings. * * The values reported by @self are not updated when the values in the * interface change. Instead, the client is expected to call * mm_modem_3gpp_get_nr5g_registration_settings() again to get a new * #MMNr5gRegistrationSettings with the new values. * * Returns: (transfer full): A #MMNr5gRegistrationSettings that must be freed with * g_object_unref() or %NULL if unknown. * * Since: 1.20 */ /** * mm_modem_3gpp_peek_nr5g_registration_settings: * @self: A #MMModem3gpp. * * Gets a #MMNr5gRegistrationSettings object including the configured 5GNR * registration settings. * * The returned value is only valid until the property changes so * it is only safe to use this function on the thread where * @self was constructed. Use mm_modem_3gpp_get_nr5g_registration_settings() * if on another thread. * * Returns: (transfer none): A #MMNr5gRegistrationSettings Do not free the returned * value, it belongs to @self. * * Since: 1.20 */ /* helpers to match the property substring name with the one in our API */ #define mm_gdbus_modem_3gpp_dup_nr5g_registration_settings mm_gdbus_modem3gpp_dup_nr5g_registration_settings PROPERTY_OBJECT_DEFINE_FAILABLE (nr5g_registration_settings, Modem3gpp, modem_3gpp, MODEM_3GPP, MMNr5gRegistrationSettings, mm_nr5g_registration_settings_new_from_dictionary) /*****************************************************************************/ /** * mm_modem_3gpp_set_nr5g_registration_settings_finish: * @self: A #MMModem3gpp. * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to * mm_modem_3gpp_set_nr5g_registration_settings(). * @error: Return location for error or %NULL. * * Finishes an operation started with mm_modem_3gpp_set_nr5g_registration_settings(). * * Returns: %TRUE if the operation was successful, %FALSE if @error is set. * * Since: 1.20 */ gboolean mm_modem_3gpp_set_nr5g_registration_settings_finish (MMModem3gpp *self, GAsyncResult *res, GError **error) { g_return_val_if_fail (MM_IS_MODEM_3GPP (self), FALSE); return mm_gdbus_modem3gpp_call_set_nr5g_registration_settings_finish (MM_GDBUS_MODEM3GPP (self), res, error); } /** * mm_modem_3gpp_set_nr5g_registration_settings: * @self: A #MMModem3gpp. * @settings: A #MMNr5gRegistrationSettings. * @cancellable: (allow-none): A #GCancellable or %NULL. * @callback: A #GAsyncReadyCallback to call when the request is satisfied or %NULL. * @user_data: User data to pass to @callback. * * Asynchronously configures the 5GNR registration settings. * * When the operation is finished, @callback will be invoked in the * thread-default main loop * of the thread you are calling this method from. You can then call * mm_modem_3gpp_set_nr5g_registration_settings_finish() to get the result of the operation. * * See mm_modem_3gpp_set_nr5g_registration_settings_sync() for the synchronous, * blocking version of this method. * * Since: 1.20 */ void mm_modem_3gpp_set_nr5g_registration_settings (MMModem3gpp *self, MMNr5gRegistrationSettings *settings, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { g_autoptr(GVariant) dictionary = NULL; g_return_if_fail (MM_IS_MODEM_3GPP (self)); dictionary = mm_nr5g_registration_settings_get_dictionary (settings); mm_gdbus_modem3gpp_call_set_nr5g_registration_settings (MM_GDBUS_MODEM3GPP (self), dictionary, cancellable, callback, user_data); } /** * mm_modem_3gpp_set_nr5g_registration_settings_sync: * @self: A #MMModem3gpp. * @settings: A #MMNr5gRegistrationSettings. * @cancellable: (allow-none): A #GCancellable or %NULL. * @error: Return location for error or %NULL. * * Synchronously configures the 5GNR registration settings. * * The calling thread is blocked until a reply is received. See * mm_modem_3gpp_set_nr5g_registration_settings() for the asynchronous * version of this method. * * Returns: %TRUE if the operation was successful, %FALSE if @error is set. * * Since: 1.20 */ gboolean mm_modem_3gpp_set_nr5g_registration_settings_sync (MMModem3gpp *self, MMNr5gRegistrationSettings *settings, GCancellable *cancellable, GError **error) { g_autoptr(GVariant) dictionary = NULL; g_return_val_if_fail (MM_IS_MODEM_3GPP (self), FALSE); dictionary = mm_nr5g_registration_settings_get_dictionary (settings); return mm_gdbus_modem3gpp_call_set_nr5g_registration_settings_sync (MM_GDBUS_MODEM3GPP (self), dictionary, cancellable, error); } /*****************************************************************************/ /** * mm_modem_3gpp_set_carrier_lock_finish: * @self: A #MMModem3gpp. * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to mm_gdbus_modem3gpp_call_set_carrier_lock(). * @error: Return location for error or %NULL. * * Finishes an operation started with mm_modem_3gpp_set_carrier_lock(). * * Returns: (skip): %TRUE if the call succeded, %FALSE if @error is set. * Since: 1.22 */ gboolean mm_modem_3gpp_set_carrier_lock_finish (MMModem3gpp *self, GAsyncResult *res, GError **error) { g_return_val_if_fail (MM_IS_MODEM_3GPP (self), FALSE); return mm_gdbus_modem3gpp_call_set_carrier_lock_finish (MM_GDBUS_MODEM3GPP (self), res, error); } /** * mm_modem_3gpp_set_carrier_lock: * @self: A #MMModem3gpp. * @data: (array length=data_size): Carrier lock information. * @data_size: size of @data. * @cancellable: (allow-none): A #GCancellable or %NULL. * @callback: A #GAsyncReadyCallback to call when the request is satisfied or * %NULL. * @user_data: User data to pass to @callback. * * Asynchronously sends the carrier lock information to the modem. * * When the operation is finished, @callback will be invoked in the * thread-default main loop * of the thread you are calling this method from. You can then call * mm_modem_location_inject_assistance_data_finish() to get the result of the * operation. * * See mm_modem_3gpp_set_carrier_lock_sync() for the synchronous, * blocking version of this method. * * Since: 1.22 */ void mm_modem_3gpp_set_carrier_lock (MMModem3gpp *self, const guint8 *data, gsize data_size, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { g_return_if_fail (MM_IS_MODEM_3GPP (self)); mm_gdbus_modem3gpp_call_set_carrier_lock (MM_GDBUS_MODEM3GPP (self), g_variant_new_fixed_array (G_VARIANT_TYPE_BYTE, data, data_size, sizeof (guint8)), cancellable, callback, user_data); } /** * mm_modem_3gpp_set_carrier_lock_sync: * @self: A #MMModem3gpp. * @data: (array length=data_size): Carrier lock information. * @data_size: size of @data. * @cancellable: (allow-none): A #GCancellable or %NULL. * @error: Return location for error or %NULL. * * Synchronously sends the carrier lock information to the modem.. * * The calling thread is blocked until a reply is received. See * mm_modem_3gpp_set_carrier_lock() for the asynchronous version of this method. * * Returns: %TRUE if the carrier network info is successfully send, %FALSE if @error is set. * * Since: 1.22 */ gboolean mm_modem_3gpp_set_carrier_lock_sync (MMModem3gpp *self, const guint8 *data, gsize data_size, GCancellable *cancellable, GError **error) { g_return_val_if_fail (MM_IS_MODEM_3GPP (self), FALSE); return mm_gdbus_modem3gpp_call_set_carrier_lock_sync (MM_GDBUS_MODEM3GPP (self), g_variant_new_fixed_array (G_VARIANT_TYPE_BYTE, data, data_size, sizeof (guint8)), cancellable, error); } /*****************************************************************************/ static void mm_modem_3gpp_init (MMModem3gpp *self) { self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, MM_TYPE_MODEM_3GPP, MMModem3gppPrivate); g_mutex_init (&self->priv->mutex); PROPERTY_INITIALIZE (initial_eps_bearer_settings, "initial-eps-bearer-settings") PROPERTY_INITIALIZE (nr5g_registration_settings, "nr5g-registration-settings") } static void finalize (GObject *object) { MMModem3gpp *self = MM_MODEM_3GPP (object); g_mutex_clear (&self->priv->mutex); PROPERTY_OBJECT_FINALIZE (initial_eps_bearer_settings); PROPERTY_OBJECT_FINALIZE (nr5g_registration_settings); G_OBJECT_CLASS (mm_modem_3gpp_parent_class)->finalize (object); } static void mm_modem_3gpp_class_init (MMModem3gppClass *modem_class) { GObjectClass *object_class = G_OBJECT_CLASS (modem_class); g_type_class_add_private (object_class, sizeof (MMModem3gppPrivate)); object_class->finalize = finalize; } ModemManager-1.23.4-dev/libmm-glib/mm-modem-3gpp.h000066400000000000000000000336151456466623000215000ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * libmm-glib -- Access modem status & information from glib applications * * 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 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 Street, Fifth Floor, * Boston, MA 02110-1301 USA. * * Copyright (C) 2011 - 2012 Aleksander Morgado * Copyright (C) 2012 Google, Inc. */ #ifndef _MM_MODEM_3GPP_H_ #define _MM_MODEM_3GPP_H_ #if !defined (__LIBMM_GLIB_H_INSIDE__) && !defined (LIBMM_GLIB_COMPILATION) #error "Only can be included directly." #endif #include #include "mm-bearer.h" #include "mm-nr5g-registration-settings.h" #include "mm-gdbus-modem.h" G_BEGIN_DECLS #define MM_TYPE_MODEM_3GPP (mm_modem_3gpp_get_type ()) #define MM_MODEM_3GPP(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_MODEM_3GPP, MMModem3gpp)) #define MM_MODEM_3GPP_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_MODEM_3GPP, MMModem3gppClass)) #define MM_IS_MODEM_3GPP(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_MODEM_3GPP)) #define MM_IS_MODEM_3GPP_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), MM_TYPE_MODEM_3GPP)) #define MM_MODEM_3GPP_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_MODEM_3GPP, MMModem3gppClass)) typedef struct _MMModem3gpp MMModem3gpp; typedef struct _MMModem3gppClass MMModem3gppClass; typedef struct _MMModem3gppPrivate MMModem3gppPrivate; /** * MMModem3gpp: * * The #MMModem3gpp structure contains private data and should only be accessed * using the provided API. */ struct _MMModem3gpp { /*< private >*/ MmGdbusModem3gppProxy parent; MMModem3gppPrivate *priv; }; struct _MMModem3gppClass { /*< private >*/ MmGdbusModem3gppProxyClass parent; }; GType mm_modem_3gpp_get_type (void); G_DEFINE_AUTOPTR_CLEANUP_FUNC (MMModem3gpp, g_object_unref) const gchar *mm_modem_3gpp_get_path (MMModem3gpp *self); gchar *mm_modem_3gpp_dup_path (MMModem3gpp *self); const gchar *mm_modem_3gpp_get_imei (MMModem3gpp *self); gchar *mm_modem_3gpp_dup_imei (MMModem3gpp *self); const gchar *mm_modem_3gpp_get_operator_code (MMModem3gpp *self); gchar *mm_modem_3gpp_dup_operator_code (MMModem3gpp *self); const gchar *mm_modem_3gpp_get_operator_name (MMModem3gpp *self); gchar *mm_modem_3gpp_dup_operator_name (MMModem3gpp *self); MMModem3gppRegistrationState mm_modem_3gpp_get_registration_state (MMModem3gpp *self); MMModem3gppFacility mm_modem_3gpp_get_enabled_facility_locks (MMModem3gpp *self); MMModem3gppEpsUeModeOperation mm_modem_3gpp_get_eps_ue_mode_operation (MMModem3gpp *self); GList *mm_modem_3gpp_get_pco (MMModem3gpp *self); const gchar *mm_modem_3gpp_get_initial_eps_bearer_path (MMModem3gpp *self); gchar *mm_modem_3gpp_dup_initial_eps_bearer_path (MMModem3gpp *self); MMBearerProperties *mm_modem_3gpp_get_initial_eps_bearer_settings (MMModem3gpp *self); MMBearerProperties *mm_modem_3gpp_peek_initial_eps_bearer_settings (MMModem3gpp *self); MMModem3gppPacketServiceState mm_modem_3gpp_get_packet_service_state (MMModem3gpp *self); MMNr5gRegistrationSettings *mm_modem_3gpp_get_nr5g_registration_settings (MMModem3gpp *self); MMNr5gRegistrationSettings *mm_modem_3gpp_peek_nr5g_registration_settings (MMModem3gpp *self); void mm_modem_3gpp_register (MMModem3gpp *self, const gchar *network_id, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); gboolean mm_modem_3gpp_register_finish (MMModem3gpp *self, GAsyncResult *res, GError **error); gboolean mm_modem_3gpp_register_sync (MMModem3gpp *self, const gchar *network_id, GCancellable *cancellable, GError **error); /** * MMModem3gppNetwork: * * The #MMModem3gppNetwork structure contains private data and should only be accessed * using the provided API. */ typedef struct _MMModem3gppNetwork MMModem3gppNetwork; #define MM_TYPE_MODEM_3GPP_NETWORK (mm_modem_3gpp_network_get_type ()) GType mm_modem_3gpp_network_get_type (void); MMModem3gppNetworkAvailability mm_modem_3gpp_network_get_availability (const MMModem3gppNetwork *network); const gchar *mm_modem_3gpp_network_get_operator_long (const MMModem3gppNetwork *network); const gchar *mm_modem_3gpp_network_get_operator_short (const MMModem3gppNetwork *network); const gchar *mm_modem_3gpp_network_get_operator_code (const MMModem3gppNetwork *network); MMModemAccessTechnology mm_modem_3gpp_network_get_access_technology (const MMModem3gppNetwork *network); void mm_modem_3gpp_network_free (MMModem3gppNetwork *network); G_DEFINE_AUTOPTR_CLEANUP_FUNC (MMModem3gppNetwork, mm_modem_3gpp_network_free) void mm_modem_3gpp_scan (MMModem3gpp *self, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); GList *mm_modem_3gpp_scan_finish (MMModem3gpp *self, GAsyncResult *res, GError **error); GList *mm_modem_3gpp_scan_sync (MMModem3gpp *self, GCancellable *cancellable, GError **error); void mm_modem_3gpp_set_eps_ue_mode_operation (MMModem3gpp *self, MMModem3gppEpsUeModeOperation mode, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); gboolean mm_modem_3gpp_set_eps_ue_mode_operation_finish (MMModem3gpp *self, GAsyncResult *res, GError **error); gboolean mm_modem_3gpp_set_eps_ue_mode_operation_sync (MMModem3gpp *self, MMModem3gppEpsUeModeOperation mode, GCancellable *cancellable, GError **error); void mm_modem_3gpp_get_initial_eps_bearer (MMModem3gpp *self, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); MMBearer *mm_modem_3gpp_get_initial_eps_bearer_finish (MMModem3gpp *self, GAsyncResult *res, GError **error); MMBearer *mm_modem_3gpp_get_initial_eps_bearer_sync (MMModem3gpp *self, GCancellable *cancellable, GError **error); void mm_modem_3gpp_set_initial_eps_bearer_settings (MMModem3gpp *self, MMBearerProperties *config, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); gboolean mm_modem_3gpp_set_initial_eps_bearer_settings_finish (MMModem3gpp *self, GAsyncResult *res, GError **error); gboolean mm_modem_3gpp_set_initial_eps_bearer_settings_sync (MMModem3gpp *self, MMBearerProperties *config, GCancellable *cancellable, GError **error); void mm_modem_3gpp_disable_facility_lock (MMModem3gpp *self, MMModem3gppFacility facility, const gchar *control_key, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); gboolean mm_modem_3gpp_disable_facility_lock_finish (MMModem3gpp *self, GAsyncResult *res, GError **error); gboolean mm_modem_3gpp_disable_facility_lock_sync (MMModem3gpp *self, MMModem3gppFacility facility, const gchar *control_key, GCancellable *cancellable, GError **error); void mm_modem_3gpp_set_packet_service_state (MMModem3gpp *self, MMModem3gppPacketServiceState state, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); gboolean mm_modem_3gpp_set_packet_service_state_finish (MMModem3gpp *self, GAsyncResult *res, GError **error); gboolean mm_modem_3gpp_set_packet_service_state_sync (MMModem3gpp *self, MMModem3gppPacketServiceState state, GCancellable *cancellable, GError **error); void mm_modem_3gpp_set_nr5g_registration_settings (MMModem3gpp *self, MMNr5gRegistrationSettings *settings, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); gboolean mm_modem_3gpp_set_nr5g_registration_settings_finish (MMModem3gpp *self, GAsyncResult *res, GError **error); gboolean mm_modem_3gpp_set_nr5g_registration_settings_sync (MMModem3gpp *self, MMNr5gRegistrationSettings *settings, GCancellable *cancellable, GError **error); gboolean mm_modem_3gpp_set_carrier_lock_finish (MMModem3gpp *self, GAsyncResult *res, GError **error); void mm_modem_3gpp_set_carrier_lock (MMModem3gpp *self, const guint8 *data, gsize data_size, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); gboolean mm_modem_3gpp_set_carrier_lock_sync (MMModem3gpp *self, const guint8 *data, gsize data_size, GCancellable *cancellable, GError **error); G_END_DECLS #endif /* _MM_MODEM_3GPP_H_ */ ModemManager-1.23.4-dev/libmm-glib/mm-modem-cdma.c000066400000000000000000000357221456466623000215270ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * libmm-glib -- Access modem status & information from glib applications * * 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 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 Street, Fifth Floor, * Boston, MA 02110-1301 USA. * * Copyright (C) 2011 - 2012 Aleksander Morgado * Copyright (C) 2012 Google, Inc. */ #include #include "mm-helpers.h" #include "mm-errors-types.h" #include "mm-modem-cdma.h" /** * SECTION: mm-modem-cdma * @title: MMModemCdma * @short_description: The CDMA interface * * The #MMModemCdma is an object providing access to the methods, signals and * properties of the CDMA interface. * * The CDMA interface is exposed whenever a modem has CDMA capabilities * (%MM_MODEM_CAPABILITY_CDMA_EVDO). */ G_DEFINE_TYPE (MMModemCdma, mm_modem_cdma, MM_GDBUS_TYPE_MODEM_CDMA_PROXY) /*****************************************************************************/ /** * mm_modem_cdma_get_path: * @self: A #MMModemCdma. * * Gets the DBus path of the #MMObject which implements this interface. * * Returns: (transfer none): The DBus path of the #MMObject object. * * Since: 1.0 */ const gchar * mm_modem_cdma_get_path (MMModemCdma *self) { g_return_val_if_fail (MM_IS_MODEM_CDMA (self), NULL); RETURN_NON_EMPTY_CONSTANT_STRING ( g_dbus_proxy_get_object_path (G_DBUS_PROXY (self))); } /** * mm_modem_cdma_dup_path: * @self: A #MMModemCdma. * * Gets a copy of the DBus path of the #MMObject object which implements this * interface. * * Returns: (transfer full): The DBus path of the #MMObject. The returned value * should be freed with g_free(). * * Since: 1.0 */ gchar * mm_modem_cdma_dup_path (MMModemCdma *self) { gchar *value; g_return_val_if_fail (MM_IS_MODEM_CDMA (self), NULL); g_object_get (G_OBJECT (self), "g-object-path", &value, NULL); RETURN_NON_EMPTY_STRING (value); } /*****************************************************************************/ /** * mm_modem_cdma_get_meid: * @self: A #MMModemCdma. * * Gets the * Mobile Equipment Identifier, * as reported by this #MMModemCdma. * * The returned value is only valid until the property changes so it is * only safe to use this function on the thread where @self was constructed. Use * mm_modem_cdma_dup_meid() if on another thread. * * Returns: (transfer none): The MEID, or %NULL if none available. * * Since: 1.0 */ const gchar * mm_modem_cdma_get_meid (MMModemCdma *self) { g_return_val_if_fail (MM_IS_MODEM_CDMA (self), NULL); RETURN_NON_EMPTY_CONSTANT_STRING ( mm_gdbus_modem_cdma_get_meid (MM_GDBUS_MODEM_CDMA (self))); } /** * mm_modem_cdma_dup_meid: * @self: A #MMModemCdma. * * Gets a copy of the * Mobile Equipment Identifier, * as reported by this #MMModemCdma. * * Returns: (transfer full): The MEID, or %NULL if none available. The returned * value should be freed with g_free(). * * Since: 1.0 */ gchar * mm_modem_cdma_dup_meid (MMModemCdma *self) { g_return_val_if_fail (MM_IS_MODEM_CDMA (self), NULL); RETURN_NON_EMPTY_STRING ( mm_gdbus_modem_cdma_dup_meid (MM_GDBUS_MODEM_CDMA (self))); } /*****************************************************************************/ /** * mm_modem_cdma_get_esn: * @self: A #MMModemCdma. * * Gets the * Electronic Serial Number, * as reported by this #MMModemCdma. * * The ESN is superceded by MEID, but still used in older devices. * * The returned value is only valid until the property changes so it is * only safe to use this function on the thread where @self was constructed. Use * mm_modem_cdma_dup_esn() if on another thread. * * Returns: (transfer none): The ESN, or %NULL if none available. * * Since: 1.0 */ const gchar * mm_modem_cdma_get_esn (MMModemCdma *self) { g_return_val_if_fail (MM_IS_MODEM_CDMA (self), NULL); RETURN_NON_EMPTY_CONSTANT_STRING ( mm_gdbus_modem_cdma_get_esn (MM_GDBUS_MODEM_CDMA (self))); } /** * mm_modem_cdma_dup_esn: * @self: A #MMModemCdma. * * Gets a copy of the * Electronic Serial Number, * as reported by this #MMModemCdma. * * The ESN is superceded by MEID, but still used in older devices. * * Returns: (transfer full): The ESN, or %NULL if none available. The returned * value should be freed with g_free(). * * Since: 1.0 */ gchar * mm_modem_cdma_dup_esn (MMModemCdma *self) { g_return_val_if_fail (MM_IS_MODEM_CDMA (self), NULL); RETURN_NON_EMPTY_STRING ( mm_gdbus_modem_cdma_dup_esn (MM_GDBUS_MODEM_CDMA (self))); } /*****************************************************************************/ /** * mm_modem_cdma_get_sid: * @self: A #MMModemCdma. * * Gets the * System Identifier * of the serving CDMA 1x network, if known, and if the modem is registered with * a CDMA 1x network. * * Returns: The SID, or %MM_MODEM_CDMA_SID_UNKNOWN. * * Since: 1.0 */ guint mm_modem_cdma_get_sid (MMModemCdma *self) { g_return_val_if_fail (MM_IS_MODEM_CDMA (self), MM_MODEM_CDMA_SID_UNKNOWN); return mm_gdbus_modem_cdma_get_sid (MM_GDBUS_MODEM_CDMA (self)); } /*****************************************************************************/ /** * mm_modem_cdma_get_nid: * @self: A #MMModemCdma. * * Gets the * Network Identifier * of the serving CDMA 1x network, if known, and if the modem is registered with * a CDMA 1x network. * * Returns: The NID, or %MM_MODEM_CDMA_NID_UNKNOWN. * * Since: 1.0 */ guint mm_modem_cdma_get_nid (MMModemCdma *self) { g_return_val_if_fail (MM_IS_MODEM_CDMA (self), MM_MODEM_CDMA_NID_UNKNOWN); return mm_gdbus_modem_cdma_get_nid (MM_GDBUS_MODEM_CDMA (self)); } /*****************************************************************************/ /** * mm_modem_cdma_get_cdma1x_registration_state: * @self: A #MMModemCdma. * * Gets the state of the registration in the CDMA 1x network. * * Returns: a #MMModemCdmaRegistrationState. * * Since: 1.0 */ MMModemCdmaRegistrationState mm_modem_cdma_get_cdma1x_registration_state (MMModemCdma *self) { g_return_val_if_fail (MM_IS_MODEM_CDMA (self), MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN); return mm_gdbus_modem_cdma_get_cdma1x_registration_state (MM_GDBUS_MODEM_CDMA (self)); } /** * mm_modem_cdma_get_evdo_registration_state: * @self: A #MMModemCdma. * * Gets the state of the registration in the EV-DO network. * * Returns: a #MMModemCdmaRegistrationState. * * Since: 1.0 */ MMModemCdmaRegistrationState mm_modem_cdma_get_evdo_registration_state (MMModemCdma *self) { g_return_val_if_fail (MM_IS_MODEM_CDMA (self), MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN); return mm_gdbus_modem_cdma_get_evdo_registration_state (MM_GDBUS_MODEM_CDMA (self)); } /*****************************************************************************/ /** * mm_modem_cdma_get_activation_state: * @self: A #MMModemCdma. * * Gets the state of the activation in the 3GPP2 network. * * Returns: a #MMModemCdmaActivationState. * * Since: 1.0 */ MMModemCdmaActivationState mm_modem_cdma_get_activation_state (MMModemCdma *self) { g_return_val_if_fail (MM_IS_MODEM_CDMA (self), MM_MODEM_CDMA_ACTIVATION_STATE_UNKNOWN); return mm_gdbus_modem_cdma_get_activation_state (MM_GDBUS_MODEM_CDMA (self)); } /*****************************************************************************/ /** * mm_modem_cdma_activate_finish: * @self: A #MMModemCdma. * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to * mm_modem_cdma_activate(). * @error: Return location for error or %NULL. * * Finishes an operation started with mm_modem_cdma_activate(). * * Returns: %TRUE if the activation was successful, %FALSE if @error is set. * * Since: 1.0 */ gboolean mm_modem_cdma_activate_finish (MMModemCdma *self, GAsyncResult *res, GError **error) { g_return_val_if_fail (MM_IS_MODEM_CDMA (self), FALSE); return mm_gdbus_modem_cdma_call_activate_finish (MM_GDBUS_MODEM_CDMA (self), res, error); } /** * mm_modem_cdma_activate: * @self: A #MMModemCdma. * @carrier: Name of the carrier. * @cancellable: (allow-none): A #GCancellable or %NULL. * @callback: A #GAsyncReadyCallback to call when the request is satisfied or * %NULL. * @user_data: User data to pass to @callback. * * Asynchronously requests to provision the modem for use with a given carrier * using the modem's OTA activation functionality, if any. * * When the operation is finished, @callback will be invoked in the * thread-default main loop * of the thread you are calling this method from. You can then call * mm_modem_cdma_activate_finish() to get the result of the operation. * * See mm_modem_cdma_activate_sync() for the synchronous, blocking version of * this method. * * Since: 1.0 */ void mm_modem_cdma_activate (MMModemCdma *self, const gchar *carrier, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { g_return_if_fail (MM_IS_MODEM_CDMA (self)); mm_gdbus_modem_cdma_call_activate (MM_GDBUS_MODEM_CDMA (self), carrier, cancellable, callback, user_data); } /** * mm_modem_cdma_activate_sync: * @self: A #MMModemCdma. * @carrier: Name of the carrier. * @cancellable: (allow-none): A #GCancellable or %NULL. * @error: Return location for error or %NULL. * * Synchronously requests to provision the modem for use with a given carrier * using the modem's OTA activation functionality, if any. * * The calling thread is blocked until a reply is received. See * mm_modem_cdma_activate() for the asynchronous version of this method. * * Returns: %TRUE if the activation was successful, %FALSE if @error is set. * * Since: 1.0 */ gboolean mm_modem_cdma_activate_sync (MMModemCdma *self, const gchar *carrier, GCancellable *cancellable, GError **error) { g_return_val_if_fail (MM_IS_MODEM_CDMA (self), FALSE); return mm_gdbus_modem_cdma_call_activate_sync (MM_GDBUS_MODEM_CDMA (self), carrier, cancellable, error); } /*****************************************************************************/ /** * mm_modem_cdma_activate_manual_finish: * @self: A #MMModemCdma. * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to * mm_modem_cdma_activate_manual(). * @error: Return location for error or %NULL. * * Finishes an operation started with mm_modem_cdma_activate_manual(). * * Returns: %TRUE if the activation was successful, %FALSE if @error is set. * * Since: 1.0 */ gboolean mm_modem_cdma_activate_manual_finish (MMModemCdma *self, GAsyncResult *res, GError **error) { g_return_val_if_fail (MM_IS_MODEM_CDMA (self), FALSE); return mm_gdbus_modem_cdma_call_activate_manual_finish (MM_GDBUS_MODEM_CDMA (self), res, error); } /** * mm_modem_cdma_activate_manual: * @self: A #MMModemCdma. * @properties: A #MMCdmaManualActivationProperties. * @cancellable: (allow-none): A #GCancellable or %NULL. * @callback: A #GAsyncReadyCallback to call when the request is satisfied or * %NULL. * @user_data: User data to pass to @callback. * * Asynchronously requests to provision the modem with the given properties. * * When the operation is finished, @callback will be invoked in the * thread-default main loop * of the thread you are calling this method from. * You can then call mm_modem_cdma_activate_manual_finish() to get the result of * the operation. * * See mm_modem_cdma_activate_manual_sync() for the synchronous, blocking * version of this method. * * Since: 1.0 */ void mm_modem_cdma_activate_manual (MMModemCdma *self, MMCdmaManualActivationProperties *properties, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { GVariant *dictionary; g_return_if_fail (MM_IS_MODEM_CDMA (self)); dictionary = mm_cdma_manual_activation_properties_get_dictionary (properties); mm_gdbus_modem_cdma_call_activate_manual (MM_GDBUS_MODEM_CDMA (self), dictionary, cancellable, callback, user_data); g_variant_unref (dictionary); } /** * mm_modem_cdma_activate_manual_sync: * @self: A #MMModemCdma. * @properties: A #MMCdmaManualActivationProperties. * @cancellable: (allow-none): A #GCancellable or %NULL. * @error: Return location for error or %NULL. * * Synchronously requests to provision the modem with the given properties. * * The calling thread is blocked until a reply is received. See * mm_modem_cdma_activate_manual() for the asynchronous version of this method. * * Returns: %TRUE if the activation was successful, %FALSE if @error is set. * * Since: 1.0 */ gboolean mm_modem_cdma_activate_manual_sync (MMModemCdma *self, MMCdmaManualActivationProperties *properties, GCancellable *cancellable, GError **error) { GVariant *dictionary; gboolean ret; g_return_val_if_fail (MM_IS_MODEM_CDMA (self), FALSE); dictionary = mm_cdma_manual_activation_properties_get_dictionary (properties); ret = (mm_gdbus_modem_cdma_call_activate_manual_sync ( MM_GDBUS_MODEM_CDMA (self), dictionary, cancellable, error)); g_variant_unref (dictionary); return ret; } /*****************************************************************************/ static void mm_modem_cdma_init (MMModemCdma *self) { } static void mm_modem_cdma_class_init (MMModemCdmaClass *modem_class) { } ModemManager-1.23.4-dev/libmm-glib/mm-modem-cdma.h000066400000000000000000000117001456466623000215220ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * libmm-glib -- Access modem status & information from glib applications * * 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 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 Street, Fifth Floor, * Boston, MA 02110-1301 USA. * * Copyright (C) 2011 - 2012 Aleksander Morgado * Copyright (C) 2012 Google, Inc. */ #ifndef _MM_MODEM_CDMA_H_ #define _MM_MODEM_CDMA_H_ #if !defined (__LIBMM_GLIB_H_INSIDE__) && !defined (LIBMM_GLIB_COMPILATION) #error "Only can be included directly." #endif #include #include "mm-gdbus-modem.h" #include "mm-cdma-manual-activation-properties.h" G_BEGIN_DECLS #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_MODEM_CDMA_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_MODEM_CDMA, MMModemCdmaClass)) #define MM_IS_MODEM_CDMA(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_MODEM_CDMA)) #define MM_IS_MODEM_CDMA_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), MM_TYPE_MODEM_CDMA)) #define MM_MODEM_CDMA_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_MODEM_CDMA, MMModemCdmaClass)) typedef struct _MMModemCdma MMModemCdma; typedef struct _MMModemCdmaClass MMModemCdmaClass; /** * MMModemCdma: * * The #MMModemCdma structure contains private data and should only be accessed * using the provided API. */ struct _MMModemCdma { /*< private >*/ MmGdbusModemCdmaProxy parent; gpointer unused; }; struct _MMModemCdmaClass { /*< private >*/ MmGdbusModemCdmaProxyClass parent; }; GType mm_modem_cdma_get_type (void); G_DEFINE_AUTOPTR_CLEANUP_FUNC (MMModemCdma, g_object_unref) const gchar *mm_modem_cdma_get_path (MMModemCdma *self); gchar *mm_modem_cdma_dup_path (MMModemCdma *self); const gchar *mm_modem_cdma_get_meid (MMModemCdma *self); gchar *mm_modem_cdma_dup_meid (MMModemCdma *self); const gchar *mm_modem_cdma_get_esn (MMModemCdma *self); gchar *mm_modem_cdma_dup_esn (MMModemCdma *self); /** * MM_MODEM_CDMA_SID_UNKNOWN: * * Identifier for an unknown SID. */ #define MM_MODEM_CDMA_SID_UNKNOWN 99999 guint mm_modem_cdma_get_sid (MMModemCdma *self); /** * MM_MODEM_CDMA_NID_UNKNOWN: * * Identifier for an unknown NID. */ #define MM_MODEM_CDMA_NID_UNKNOWN 99999 guint mm_modem_cdma_get_nid (MMModemCdma *self); MMModemCdmaRegistrationState mm_modem_cdma_get_cdma1x_registration_state (MMModemCdma *self); MMModemCdmaRegistrationState mm_modem_cdma_get_evdo_registration_state (MMModemCdma *self); MMModemCdmaActivationState mm_modem_cdma_get_activation_state (MMModemCdma *self); void mm_modem_cdma_activate (MMModemCdma *self, const gchar *carrier, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); gboolean mm_modem_cdma_activate_finish (MMModemCdma *self, GAsyncResult *res, GError **error); gboolean mm_modem_cdma_activate_sync (MMModemCdma *self, const gchar *carrier, GCancellable *cancellable, GError **error); void mm_modem_cdma_activate_manual (MMModemCdma *self, MMCdmaManualActivationProperties *properties, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); gboolean mm_modem_cdma_activate_manual_finish (MMModemCdma *self, GAsyncResult *res, GError **error); gboolean mm_modem_cdma_activate_manual_sync (MMModemCdma *self, MMCdmaManualActivationProperties *properties, GCancellable *cancellable, GError **error); G_END_DECLS #endif /* _MM_MODEM_CDMA_H_ */ ModemManager-1.23.4-dev/libmm-glib/mm-modem-firmware.c000066400000000000000000000403261456466623000224330ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * libmm-glib -- Access modem status & information from glib applications * * 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 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 Street, Fifth Floor, * Boston, MA 02110-1301 USA. * * Copyright (C) 2012 Google, Inc. * Copyright (C) 2012 Lanedo GmbH */ #include #include "mm-helpers.h" #include "mm-errors-types.h" #include "mm-modem-firmware.h" /** * SECTION: mm-modem-firmware * @title: MMModemFirmware * @short_description: The Firmware interface * * The #MMModemFirmware is an object providing access to the methods, signals and * properties of the Firmware interface. * * The Firmware interface is exposed whenever a modem has firmware capabilities. */ G_DEFINE_TYPE (MMModemFirmware, mm_modem_firmware, MM_GDBUS_TYPE_MODEM_FIRMWARE_PROXY) struct _MMModemFirmwarePrivate { /* Common mutex to sync access */ GMutex mutex; PROPERTY_OBJECT_DECLARE (update_settings, MMFirmwareUpdateSettings) }; /*****************************************************************************/ /** * mm_modem_firmware_get_path: * @self: A #MMModemFirmware. * * Gets the DBus path of the #MMObject which implements this interface. * * Returns: (transfer none): The DBus path of the #MMObject object. * * Since: 1.0 */ const gchar * mm_modem_firmware_get_path (MMModemFirmware *self) { g_return_val_if_fail (MM_IS_MODEM_FIRMWARE (self), NULL); RETURN_NON_EMPTY_CONSTANT_STRING ( g_dbus_proxy_get_object_path (G_DBUS_PROXY (self))); } /** * mm_modem_firmware_dup_path: * @self: A #MMModemFirmware. * * Gets a copy of the DBus path of the #MMObject object which implements this * interface. * * Returns: (transfer full): The DBus path of the #MMObject. The returned value * should be freed with g_free(). * * Since: 1.0 */ gchar * mm_modem_firmware_dup_path (MMModemFirmware *self) { gchar *value; g_return_val_if_fail (MM_IS_MODEM_FIRMWARE (self), NULL); g_object_get (G_OBJECT (self), "g-object-path", &value, NULL); RETURN_NON_EMPTY_STRING (value); } /*****************************************************************************/ /** * mm_modem_firmware_get_update_settings: * @self: A #MMModemFirmware. * * Gets a #MMFirmwareUpdateSettings object specifying the expected update * settings. * * The values reported by @self are not updated when the values in the * interface change. Instead, the client is expected to call * mm_modem_firmware_get_update_settings() again to get a new * #MMFirmwareUpdateSettings with the new values. * * Returns: (transfer full): A #MMFirmwareUpdateSettings that must be freed with * g_object_unref() or %NULL if unknown. * * Since: 1.10 */ /** * mm_modem_firmware_peek_update_settings: * @self: A #MMModemFirmware. * * Gets a #MMFirmwareUpdateSettings object specifying the expected update * settings. * * The returned value is only valid until the property changes so * it is only safe to use this function on the thread where * @self was constructed. Use mm_modem_firmware_get_update_settings() if on * another thread. * * Returns: (transfer none): A #MMFirmwareUpdateSettings. Do not free the * returned value, it belongs to @self. * * Since: 1.10 */ PROPERTY_OBJECT_DEFINE_FAILABLE (update_settings, ModemFirmware, modem_firmware, MODEM_FIRMWARE, MMFirmwareUpdateSettings, mm_firmware_update_settings_new_from_variant) /*****************************************************************************/ /** * mm_modem_firmware_select_finish: * @self: A #MMModemFirmware. * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to * mm_modem_firmware_select(). * @error: Return location for error or %NULL. * * Finishes an operation started with mm_modem_firmware_select(). * * Returns: %TRUE if the selection was successful, %FALSE if @error is set. * * Since: 1.0 */ gboolean mm_modem_firmware_select_finish (MMModemFirmware *self, GAsyncResult *res, GError **error) { g_return_val_if_fail (MM_IS_MODEM_FIRMWARE (self), FALSE); return mm_gdbus_modem_firmware_call_select_finish (MM_GDBUS_MODEM_FIRMWARE (self), res, error); } /** * mm_modem_firmware_select: * @self: A #MMModemFirmware. * @unique_id: Unique ID of the firmware image to select. * @cancellable: (allow-none): A #GCancellable or %NULL. * @callback: A #GAsyncReadyCallback to call when the request is satisfied or * %NULL. * @user_data: User data to pass to @callback. * * Asynchronously selects a firmware image to boot. * * The modem will possibly disappear once this action is run, as it * needs to reboot in order to select the new image. * * When the operation is finished, @callback will be invoked in the * thread-default main loop * of the thread you are calling this method from. You can then call * mm_modem_firmware_select_finish() to get the result of the operation. * * See mm_modem_firmware_select_sync() for the synchronous, blocking version of * this method. * * Since: 1.0 */ void mm_modem_firmware_select (MMModemFirmware *self, const gchar *unique_id, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { g_return_if_fail (MM_IS_MODEM_FIRMWARE (self)); g_return_if_fail (unique_id != NULL && unique_id[0] != '\0'); mm_gdbus_modem_firmware_call_select (MM_GDBUS_MODEM_FIRMWARE (self), unique_id, cancellable, callback, user_data); } /** * mm_modem_firmware_select_sync: * @self: A #MMModemFirmware. * @unique_id: Unique ID of the firmware image to select. * @cancellable: (allow-none): A #GCancellable or %NULL. * @error: Return location for error or %NULL. * * Synchronously selects a firmware image to boot. * * The modem will possibly disappear once this action is run, as it * needs to reboot in order to select the new image. * * The calling thread is blocked until a reply is received. See * mm_modem_firmware_select() for the asynchronous version of this method. * * Returns: %TRUE if the selection was successful, %FALSE if @error is set. * * Since: 1.0 */ gboolean mm_modem_firmware_select_sync (MMModemFirmware *self, const gchar *unique_id, GCancellable *cancellable, GError **error) { g_return_val_if_fail (MM_IS_MODEM_FIRMWARE (self), FALSE); g_return_val_if_fail (unique_id != NULL && unique_id[0] != '\0', FALSE); return mm_gdbus_modem_firmware_call_select_sync (MM_GDBUS_MODEM_FIRMWARE (self), unique_id, cancellable, error); } /*****************************************************************************/ static gboolean build_results (const gchar *str_selected, GVariant *dictionaries_installed, MMFirmwareProperties **selected, GList **installed, GError **error) { GError *saved_error = NULL; GVariantIter iter; guint n; g_assert (selected != NULL); g_assert (installed != NULL); *installed = NULL; *selected = NULL; if (!dictionaries_installed) { if (str_selected && str_selected[0]) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS, "Selected image specified but no installed images listed"); return FALSE; } /* Nothing else to do */ return TRUE; } /* Parse array of dictionaries */ g_variant_iter_init (&iter, dictionaries_installed); n = g_variant_iter_n_children (&iter); if (n > 0) { GVariant *dictionary = NULL; while ((dictionary = g_variant_iter_next_value (&iter))) { MMFirmwareProperties *firmware; GError *inner_error = NULL; firmware = mm_firmware_properties_new_from_dictionary (dictionary, &inner_error); if (!firmware) { g_warning ("Couldn't create firmware properties: %s", inner_error->message); if (!saved_error) saved_error = inner_error; else g_error_free (inner_error); } else { /* Save the firmware properties */ *installed = g_list_append (*installed, firmware); if (str_selected && str_selected[0] && g_str_equal (mm_firmware_properties_get_unique_id (firmware), str_selected)) *selected = g_object_ref (firmware); } g_variant_unref (dictionary); } } if (str_selected && str_selected[0] && *selected == NULL) g_warning ("Selected image '%s' was not found in the list of installed images", str_selected); if (saved_error) { if (*installed == NULL) { g_propagate_error (error, saved_error); return FALSE; } g_error_free (saved_error); } return TRUE; } /** * mm_modem_firmware_list_finish: * @self: A #MMModemFirmware. * @selected: (out) (allow-none) (transfer full): The selected firmware slot, or * %NULL if no slot is selected (such as if all slots are empty, or no slots * exist). The returned value should be freed with g_object_unref(). * @installed: (out) (allow-none) (transfer full) (element-type ModemManager.FirmwareProperties): * A list of #MMFirmwareProperties objects specifying the installed images. The * returned value should be freed with g_list_free_full() using * g_object_unref() as #GDestroyNotify. * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to * mm_modem_firmware_list(). * @error: Return location for error or %NULL. * * Finishes an operation started with mm_modem_firmware_list(). * * Returns: %TRUE if the list was correctly retrieved, %FALSE if @error is set. * * Since: 1.0 */ gboolean mm_modem_firmware_list_finish (MMModemFirmware *self, GAsyncResult *res, MMFirmwareProperties **selected, GList **installed, GError **error) { gboolean parsed; GVariant *dictionaries_installed = NULL; gchar *str_selected = NULL; g_return_val_if_fail (MM_IS_MODEM_FIRMWARE (self), FALSE); g_return_val_if_fail (selected != NULL, FALSE); g_return_val_if_fail (installed != NULL, FALSE); if (!mm_gdbus_modem_firmware_call_list_finish (MM_GDBUS_MODEM_FIRMWARE (self), &str_selected, &dictionaries_installed, res, error)) return FALSE; parsed = build_results (str_selected, dictionaries_installed, selected, installed, error); if (dictionaries_installed) g_variant_unref (dictionaries_installed); g_free (str_selected); return parsed; } /** * mm_modem_firmware_list: * @self: A #MMModemFirmware. * @cancellable: (allow-none): A #GCancellable or %NULL. * @callback: A #GAsyncReadyCallback to call when the request is satisfied or * %NULL. * @user_data: User data to pass to @callback. * * Asynchronously gets the list of available firmware images. * * When the operation is finished, @callback will be invoked in the * thread-default main loop * of the thread you are calling this method from. You can then call * mm_modem_firmware_list_finish() to get the result of the operation. * * See mm_modem_firmware_list_sync() for the synchronous, blocking version of * this method. * * Since: 1.0 */ void mm_modem_firmware_list (MMModemFirmware *self, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { g_return_if_fail (MM_IS_MODEM_FIRMWARE (self)); mm_gdbus_modem_firmware_call_list (MM_GDBUS_MODEM_FIRMWARE (self), cancellable, callback, user_data); } /** * mm_modem_firmware_list_sync: * @self: A #MMModemFirmware. * @selected: (out) (allow-none) (transfer full): The selected firmware slot, * or NULL if no slot is selected (such as if all slots are empty, or no slots * exist). The returned value should be freed with g_object_unref(). * @installed: (out) (allow-none) (transfer full) (element-type ModemManager.FirmwareProperties): * A list of #MMFirmwareProperties objects specifying the installed images. The * returned value should be freed with g_list_free_full() using * g_object_unref() as #GDestroyNotify. * @cancellable: (allow-none): A #GCancellable or %NULL. * @error: Return firmware for error or %NULL. * * Synchronously gets the list of available firmware images. * * The calling thread is blocked until a reply is received. See * mm_modem_firmware_list() for the asynchronous version of this method. * * Returns: %TRUE if the list was correctly retrieved, %FALSE if @error is set. * * Since: 1.0 */ gboolean mm_modem_firmware_list_sync (MMModemFirmware *self, MMFirmwareProperties **selected, GList **installed, GCancellable *cancellable, GError **error) { GVariant *dictionaries_installed = NULL; gchar *str_selected = NULL; gboolean parsed; g_return_val_if_fail (MM_IS_MODEM_FIRMWARE (self), FALSE); g_return_val_if_fail (selected != NULL, FALSE); g_return_val_if_fail (installed != NULL, FALSE); if (!mm_gdbus_modem_firmware_call_list_sync (MM_GDBUS_MODEM_FIRMWARE (self), &str_selected, &dictionaries_installed, cancellable, error)) return FALSE; parsed = build_results (str_selected, dictionaries_installed, selected, installed, error); if (dictionaries_installed) g_variant_unref (dictionaries_installed); g_free (str_selected); return parsed; } /*****************************************************************************/ static void mm_modem_firmware_init (MMModemFirmware *self) { self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, MM_TYPE_MODEM_FIRMWARE, MMModemFirmwarePrivate); g_mutex_init (&self->priv->mutex); PROPERTY_INITIALIZE (update_settings, "update-settings") } static void finalize (GObject *object) { MMModemFirmware *self = MM_MODEM_FIRMWARE (object); g_mutex_clear (&self->priv->mutex); PROPERTY_OBJECT_FINALIZE (update_settings) G_OBJECT_CLASS (mm_modem_firmware_parent_class)->finalize (object); } static void mm_modem_firmware_class_init (MMModemFirmwareClass *modem_class) { GObjectClass *object_class = G_OBJECT_CLASS (modem_class); g_type_class_add_private (object_class, sizeof (MMModemFirmwarePrivate)); object_class->finalize = finalize; } ModemManager-1.23.4-dev/libmm-glib/mm-modem-firmware.h000066400000000000000000000111041456466623000224300ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * libmm-glib -- Access modem status & information from glib applications * * 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 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 Street, Fifth Floor, * Boston, MA 02110-1301 USA. * * Copyright (C) 2012 Google, Inc. * Copyright (C) 2018 Aleksander Morgado */ #ifndef _MM_MODEM_FIRMWARE_H_ #define _MM_MODEM_FIRMWARE_H_ #if !defined (__LIBMM_GLIB_H_INSIDE__) && !defined (LIBMM_GLIB_COMPILATION) #error "Only can be included directly." #endif #include #include "mm-gdbus-modem.h" #include "mm-firmware-properties.h" #include "mm-firmware-update-settings.h" G_BEGIN_DECLS #define MM_TYPE_MODEM_FIRMWARE (mm_modem_firmware_get_type ()) #define MM_MODEM_FIRMWARE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_MODEM_FIRMWARE, MMModemFirmware)) #define MM_MODEM_FIRMWARE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_MODEM_FIRMWARE, MMModemFirmwareClass)) #define MM_IS_MODEM_FIRMWARE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_MODEM_FIRMWARE)) #define MM_IS_MODEM_FIRMWARE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), MM_TYPE_MODEM_FIRMWARE)) #define MM_MODEM_FIRMWARE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_MODEM_FIRMWARE, MMModemFirmwareClass)) typedef struct _MMModemFirmware MMModemFirmware; typedef struct _MMModemFirmwareClass MMModemFirmwareClass; typedef struct _MMModemFirmwarePrivate MMModemFirmwarePrivate; /** * MMModemFirmware: * * The #MMModemFirmware structure contains private data and should only be accessed * using the provided API. */ struct _MMModemFirmware { /*< private >*/ MmGdbusModemFirmwareProxy parent; MMModemFirmwarePrivate *priv; }; struct _MMModemFirmwareClass { /*< private >*/ MmGdbusModemFirmwareProxyClass parent; }; GType mm_modem_firmware_get_type (void); G_DEFINE_AUTOPTR_CLEANUP_FUNC (MMModemFirmware, g_object_unref) const gchar *mm_modem_firmware_get_path (MMModemFirmware *self); gchar *mm_modem_firmware_dup_path (MMModemFirmware *self); MMFirmwareUpdateSettings *mm_modem_firmware_get_update_settings (MMModemFirmware *self); MMFirmwareUpdateSettings *mm_modem_firmware_peek_update_settings (MMModemFirmware *self); void mm_modem_firmware_list (MMModemFirmware *self, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); gboolean mm_modem_firmware_list_finish (MMModemFirmware *self, GAsyncResult *res, MMFirmwareProperties **selected, GList **installed, GError **error); gboolean mm_modem_firmware_list_sync (MMModemFirmware *self, MMFirmwareProperties **selected, GList **installed, GCancellable *cancellable, GError **error); void mm_modem_firmware_select (MMModemFirmware *self, const gchar *unique_id, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); gboolean mm_modem_firmware_select_finish (MMModemFirmware *self, GAsyncResult *res, GError **error); gboolean mm_modem_firmware_select_sync (MMModemFirmware *self, const gchar *unique_id, GCancellable *cancellable, GError **error); G_END_DECLS #endif /* _MM_MODEM_FIRMWARE_H_ */ ModemManager-1.23.4-dev/libmm-glib/mm-modem-location.c000066400000000000000000001517011456466623000224270ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * libmm-glib -- Access modem status & information from glib applications * * 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 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 Street, Fifth Floor, * Boston, MA 02110-1301 USA. * * Copyright (C) 2012 Google, Inc. * Copyright (C) 2012 Lanedo GmbH */ #include #include "mm-helpers.h" #include "mm-errors-types.h" #include "mm-modem-location.h" /** * SECTION: mm-modem-location * @title: MMModemLocation * @short_description: The Location interface * * The #MMModemLocation is an object providing access to the methods, signals and * properties of the Location interface. * * The Location interface is exposed whenever a modem has location capabilities. */ G_DEFINE_TYPE (MMModemLocation, mm_modem_location, MM_GDBUS_TYPE_MODEM_LOCATION_PROXY) struct _MMModemLocationPrivate { /* Common mutex to sync access */ GMutex mutex; MMLocation3gpp *signaled_3gpp; MMLocationGpsNmea *signaled_gps_nmea; MMLocationGpsRaw *signaled_gps_raw; MMLocationCdmaBs *signaled_cdma_bs; PROPERTY_COMMON_DECLARE (signaled_location) }; /*****************************************************************************/ /** * mm_modem_location_get_path: * @self: A #MMModemLocation. * * Gets the DBus path of the #MMObject which implements this interface. * * Returns: (transfer none): The DBus path of the #MMObject object. * * Since: 1.0 */ const gchar * mm_modem_location_get_path (MMModemLocation *self) { g_return_val_if_fail (MM_IS_MODEM_LOCATION (self), NULL); RETURN_NON_EMPTY_CONSTANT_STRING ( g_dbus_proxy_get_object_path (G_DBUS_PROXY (self))); } /** * mm_modem_location_dup_path: * @self: A #MMModemLocation. * * Gets a copy of the DBus path of the #MMObject object which implements this * interface. * * Returns: (transfer full): The DBus path of the #MMObject. The returned value * should be freed with g_free(). * * Since: 1.0 */ gchar * mm_modem_location_dup_path (MMModemLocation *self) { gchar *value; g_return_val_if_fail (MM_IS_MODEM_LOCATION (self), NULL); g_object_get (G_OBJECT (self), "g-object-path", &value, NULL); RETURN_NON_EMPTY_STRING (value); } /*****************************************************************************/ /** * mm_modem_location_get_capabilities: * @self: A #MMModemLocation. * * Gets a bitmask of the location capabilities supported by this * #MMModemLocation. * * Returns: A #MMModemLocationSource. * * Since: 1.0 */ MMModemLocationSource mm_modem_location_get_capabilities (MMModemLocation *self) { g_return_val_if_fail (MM_IS_MODEM_LOCATION (self), MM_MODEM_LOCATION_SOURCE_NONE); return (MMModemLocationSource) mm_gdbus_modem_location_get_capabilities (MM_GDBUS_MODEM_LOCATION (self)); } /*****************************************************************************/ /** * mm_modem_location_get_supported_assistance_data: * @self: A #MMModemLocation. * * Gets a bitmask of the supported assistance data types. * * Returns: A #MMModemLocationAssistanceDataType. * * Since: 1.10 */ MMModemLocationAssistanceDataType mm_modem_location_get_supported_assistance_data (MMModemLocation *self) { g_return_val_if_fail (MM_IS_MODEM_LOCATION (self), MM_MODEM_LOCATION_ASSISTANCE_DATA_TYPE_NONE); return (MMModemLocationAssistanceDataType) mm_gdbus_modem_location_get_supported_assistance_data (MM_GDBUS_MODEM_LOCATION (self)); } /*****************************************************************************/ /** * mm_modem_location_get_enabled: * @self: A #MMModemLocation. * * Gets a bitmask of the location capabilities which are enabled in this #MMModemLocation. * * Returns: A #MMModemLocationSource. * * Since: 1.0 */ MMModemLocationSource mm_modem_location_get_enabled (MMModemLocation *self) { g_return_val_if_fail (MM_IS_MODEM_LOCATION (self), FALSE); return (MMModemLocationSource) mm_gdbus_modem_location_get_enabled (MM_GDBUS_MODEM_LOCATION (self)); } /*****************************************************************************/ /** * mm_modem_location_signals_location: * @self: A #MMModemLocation. * * Gets the status of the location signaling in the #MMModemLocation. * * Returns: %TRUE if location changes are signaled, %FALSE otherwise. * * Since: 1.0 */ gboolean mm_modem_location_signals_location (MMModemLocation *self) { g_return_val_if_fail (MM_IS_MODEM_LOCATION (self), FALSE); return mm_gdbus_modem_location_get_signals_location (MM_GDBUS_MODEM_LOCATION (self)); } /*****************************************************************************/ /** * mm_modem_location_setup_finish: * @self: A #MMModemLocation. * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to mm_modem_location_setup(). * @error: Return location for error or %NULL. * * Finishes an operation started with mm_modem_location_setup(). * * Returns: %TRUE if the setup was successful, %FALSE if @error is set. * * Since: 1.0 */ gboolean mm_modem_location_setup_finish (MMModemLocation *self, GAsyncResult *res, GError **error) { g_return_val_if_fail (MM_IS_MODEM_LOCATION (self), FALSE); return mm_gdbus_modem_location_call_setup_finish (MM_GDBUS_MODEM_LOCATION (self), res, error); } /** * mm_modem_location_setup: * @self: A #MMModemLocation. * @sources: Bitmask of #MMModemLocationSource values specifying which locations * should get enabled. * @signal_location: Flag to enable or disable location signaling. * @cancellable: (allow-none): A #GCancellable or %NULL. * @callback: A #GAsyncReadyCallback to call when the request is satisfied or * %NULL. * @user_data: User data to pass to @callback. * * Asynchronously configures the location sources to use when gathering location * information. Also enable or disable location information gathering. * * When the operation is finished, @callback will be invoked in the * thread-default main loop * of the thread you are calling this method from. You can then call * mm_modem_location_setup_finish() to get the result of the operation. * * See mm_modem_location_setup_sync() for the synchronous, blocking version of * this method. * * Since: 1.0 */ void mm_modem_location_setup (MMModemLocation *self, MMModemLocationSource sources, gboolean signal_location, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { g_return_if_fail (MM_IS_MODEM_LOCATION (self)); mm_gdbus_modem_location_call_setup (MM_GDBUS_MODEM_LOCATION (self), sources, signal_location, cancellable, callback, user_data); } /** * mm_modem_location_setup_sync: * @self: A #MMModemLocation. * @sources: Bitmask of #MMModemLocationSource values specifying which locations * should get enabled. * @signal_location: Flag to enable or disable location signaling. * @cancellable: (allow-none): A #GCancellable or %NULL. * @error: Return location for error or %NULL. * * Synchronously configures the location sources to use when gathering location * information. Also enable or disable location information gathering. * * The calling thread is blocked until a reply is received. See * mm_modem_location_setup() for the asynchronous version of this method. * * Returns: %TRUE if the setup was successful, %FALSE if @error is set. * * Since: 1.0 */ gboolean mm_modem_location_setup_sync (MMModemLocation *self, MMModemLocationSource sources, gboolean signal_location, GCancellable *cancellable, GError **error) { g_return_val_if_fail (MM_IS_MODEM_LOCATION (self), FALSE); return mm_gdbus_modem_location_call_setup_sync (MM_GDBUS_MODEM_LOCATION (self), sources, signal_location, cancellable, error); } /*****************************************************************************/ /** * mm_modem_location_set_supl_server_finish: * @self: A #MMModemLocation. * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to * mm_modem_location_set_supl_server(). * @error: Return location for error or %NULL. * * Finishes an operation started with mm_modem_location_set_supl_server(). * * Returns: %TRUE if setting the SUPL server was successful, %FALSE if @error is * set. * * Since: 1.6 */ gboolean mm_modem_location_set_supl_server_finish (MMModemLocation *self, GAsyncResult *res, GError **error) { g_return_val_if_fail (MM_IS_MODEM_LOCATION (self), FALSE); return mm_gdbus_modem_location_call_set_supl_server_finish (MM_GDBUS_MODEM_LOCATION (self), res, error); } /** * mm_modem_location_set_supl_server: * @self: A #MMModemLocation. * @supl: The SUPL server address, given as IP:PORT or with a full URL. * @cancellable: (allow-none): A #GCancellable or %NULL. * @callback: A #GAsyncReadyCallback to call when the request is satisfied or * %NULL. * @user_data: User data to pass to @callback. * * Asynchronously configures the address of the SUPL server for A-GPS operation. * * When the operation is finished, @callback will be invoked in the * thread-default main loop * of the thread you are calling this method from. You can then call * mm_modem_location_set_supl_server_finish() to get the result of the operation. * * See mm_modem_location_set_supl_server_sync() for the synchronous, blocking * version of this method. * * Since: 1.6 */ void mm_modem_location_set_supl_server (MMModemLocation *self, const gchar *supl, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { g_return_if_fail (MM_IS_MODEM_LOCATION (self)); mm_gdbus_modem_location_call_set_supl_server (MM_GDBUS_MODEM_LOCATION (self), supl, cancellable, callback, user_data); } /** * mm_modem_location_set_supl_server_sync: * @self: A #MMModemLocation. * @supl: The SUPL server address, given as IP:PORT or with a full URL. * @cancellable: (allow-none): A #GCancellable or %NULL. * @error: Return location for error or %NULL. * * Synchronously configures the address of the SUPL server for A-GPS operation. * * The calling thread is blocked until a reply is received. See * mm_modem_location_set_supl_server() for the asynchronous version of this * method. * * Returns: %TRUE if setting the SUPL server was successful, %FALSE if @error is * set. * * Since: 1.6 */ gboolean mm_modem_location_set_supl_server_sync (MMModemLocation *self, const gchar *supl, GCancellable *cancellable, GError **error) { g_return_val_if_fail (MM_IS_MODEM_LOCATION (self), FALSE); return mm_gdbus_modem_location_call_set_supl_server_sync (MM_GDBUS_MODEM_LOCATION (self), supl, cancellable, error); } /*****************************************************************************/ /** * mm_modem_location_inject_assistance_data_finish: * @self: A #MMModemLocation. * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to * mm_modem_location_inject_assistance_data(). * @error: Return location for error or %NULL. * * Finishes an operation started with * mm_modem_location_inject_assistance_data(). * * Returns: %TRUE if the injection was successful, %FALSE if @error is set. * * Since: 1.10 */ gboolean mm_modem_location_inject_assistance_data_finish (MMModemLocation *self, GAsyncResult *res, GError **error) { g_return_val_if_fail (MM_IS_MODEM_LOCATION (self), FALSE); return mm_gdbus_modem_location_call_inject_assistance_data_finish (MM_GDBUS_MODEM_LOCATION (self), res, error); } /** * mm_modem_location_inject_assistance_data: * @self: A #MMModemLocation. * @data: (array length=data_size): Data to inject. * @data_size: size of @data. * @cancellable: (allow-none): A #GCancellable or %NULL. * @callback: A #GAsyncReadyCallback to call when the request is satisfied or * %NULL. * @user_data: User data to pass to @callback. * * Aynchronously injects assistance data to the GNSS module. * * When the operation is finished, @callback will be invoked in the * thread-default main loop * of the thread you are calling this method from. You can then call * mm_modem_location_inject_assistance_data_finish() to get the result of the * operation. * * See mm_modem_location_inject_assistance_data_sync() for the synchronous, * blocking version of this method. * * Since: 1.10 */ void mm_modem_location_inject_assistance_data (MMModemLocation *self, const guint8 *data, gsize data_size, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { g_return_if_fail (MM_IS_MODEM_LOCATION (self)); mm_gdbus_modem_location_call_inject_assistance_data (MM_GDBUS_MODEM_LOCATION (self), g_variant_new_fixed_array (G_VARIANT_TYPE_BYTE, data, data_size, sizeof (guint8)), cancellable, callback, user_data); } /** * mm_modem_location_inject_assistance_data_sync: * @self: A #MMModemLocation. * @data: (array length=data_size): Data to inject. * @data_size: size of @data. * @cancellable: (allow-none): A #GCancellable or %NULL. * @error: Return location for error or %NULL. * * Synchronously injects assistance data to the GNSS module. * * The calling thread is blocked until a reply is received. See * mm_modem_location_inject_assistance_data() for the asynchronous version of * this method. * * Returns: %TRUE if the injection was successful, %FALSE if @error is set. * * Since: 1.10 */ gboolean mm_modem_location_inject_assistance_data_sync (MMModemLocation *self, const guint8 *data, gsize data_size, GCancellable *cancellable, GError **error) { g_return_val_if_fail (MM_IS_MODEM_LOCATION (self), FALSE); return mm_gdbus_modem_location_call_inject_assistance_data_sync (MM_GDBUS_MODEM_LOCATION (self), g_variant_new_fixed_array (G_VARIANT_TYPE_BYTE, data, data_size, sizeof (guint8)), cancellable, error); } /*****************************************************************************/ /** * mm_modem_location_set_gps_refresh_rate_finish: * @self: A #MMModemLocation. * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to * mm_modem_location_set_gps_refresh_rate(). * @error: Return location for error or %NULL. * * Finishes an operation started with mm_modem_location_set_gps_refresh_rate(). * * Returns: %TRUE if setting the GPS refresh rate was successful, %FALSE if * @error is set. * * Since: 1.0 */ gboolean mm_modem_location_set_gps_refresh_rate_finish (MMModemLocation *self, GAsyncResult *res, GError **error) { g_return_val_if_fail (MM_IS_MODEM_LOCATION (self), FALSE); return mm_gdbus_modem_location_call_set_gps_refresh_rate_finish (MM_GDBUS_MODEM_LOCATION (self), res, error); } /** * mm_modem_location_set_gps_refresh_rate: * @self: A #MMModemLocation. * @rate: The GPS refresh rate, in seconds. * @cancellable: (allow-none): A #GCancellable or %NULL. * @callback: A #GAsyncReadyCallback to call when the request is satisfied or %NULL. * @user_data: User data to pass to @callback. * * Asynchronously configures the GPS refresh rate. * If a 0 rate is used, the GPS location updates will be immediately propagated * to the interface. * * When the operation is finished, @callback will be invoked in the * thread-default main loop * of the thread you are calling this method from. You can then call * mm_modem_location_set_gps_refresh_rate_finish() to get the result of the * operation. * * See mm_modem_location_set_gps_refresh_rate_sync() for the synchronous, * blocking version of this method. * * Since: 1.0 */ void mm_modem_location_set_gps_refresh_rate (MMModemLocation *self, guint rate, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { g_return_if_fail (MM_IS_MODEM_LOCATION (self)); mm_gdbus_modem_location_call_set_gps_refresh_rate (MM_GDBUS_MODEM_LOCATION (self), rate, cancellable, callback, user_data); } /** * mm_modem_location_set_gps_refresh_rate_sync: * @self: A #MMModemLocation. * @rate: The GPS refresh rate, in seconds. * @cancellable: (allow-none): A #GCancellable or %NULL. * @error: Return location for error or %NULL. * * Synchronously configures the GPS refresh rate. * * If a 0 rate is used, the GPS location updates will be immediately propagated * to the interface. * * The calling thread is blocked until a reply is received. See * mm_modem_location_set_gps_refresh_rate() for the asynchronous version of this * method. * * Returns: %TRUE if setting the refresh rate was successful, %FALSE if @error * is set. * * Since: 1.0 */ gboolean mm_modem_location_set_gps_refresh_rate_sync (MMModemLocation *self, guint rate, GCancellable *cancellable, GError **error) { g_return_val_if_fail (MM_IS_MODEM_LOCATION (self), FALSE); return mm_gdbus_modem_location_call_set_gps_refresh_rate_sync (MM_GDBUS_MODEM_LOCATION (self), rate, cancellable, error); } /*****************************************************************************/ static gboolean build_locations (GVariant *dictionary, MMLocation3gpp **location_3gpp, MMLocationGpsNmea **location_gps_nmea, MMLocationGpsRaw **location_gps_raw, MMLocationCdmaBs **location_cdma_bs, GError **error) { GError *inner_error = NULL; GVariant *value; guint source; GVariantIter iter; if (!dictionary) /* No location provided. Not actually an error. */ return TRUE; g_variant_iter_init (&iter, dictionary); while (!inner_error && g_variant_iter_next (&iter, "{uv}", &source, &value)) { switch (source) { case MM_MODEM_LOCATION_SOURCE_3GPP_LAC_CI: if (location_3gpp) *location_3gpp = mm_location_3gpp_new_from_string_variant (value, &inner_error); break; case MM_MODEM_LOCATION_SOURCE_GPS_NMEA: if (location_gps_nmea) *location_gps_nmea = mm_location_gps_nmea_new_from_string_variant (value, &inner_error); break; case MM_MODEM_LOCATION_SOURCE_GPS_RAW: if (location_gps_raw) *location_gps_raw = mm_location_gps_raw_new_from_dictionary (value, &inner_error); break; case MM_MODEM_LOCATION_SOURCE_CDMA_BS: if (location_cdma_bs) *location_cdma_bs = mm_location_cdma_bs_new_from_dictionary (value, &inner_error); break; default: g_warn_if_reached (); break; } g_variant_unref (value); } if (inner_error) { g_propagate_error (error, inner_error); g_prefix_error (error, "Couldn't build locations result: "); return FALSE; } return TRUE; } /** * mm_modem_location_get_full_finish: * @self: A #MMModemLocation. * @location_3gpp: (out) (allow-none) (transfer full): Return location for a * #MMLocation3gpp if 3GPP location is requested, or #NULL if not required. The * returned value should be freed with g_object_unref(). * @location_gps_nmea: (out) (allow-none) (transfer full): Return location for a * #MMLocationGpsNmea if GPS NMEA location is requested, or #NULL if not * required. The returned value should be freed with g_object_unref(). * @location_gps_raw: (out) (allow-none) (transfer full): Return location for a * #MMLocationGpsRaw if GPS raw location is requested, or #NULL if not required. * The returned value should be freed with g_object_unref(). * @location_cdma_bs: (out) (allow-none) (transfer full): Return location for a * #MMLocationCdmaBs if CDMA Base Station location is requested, or #NULL if * not required. The returned value should be freed with g_object_unref(). * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to * mm_modem_location_get_full(). * @error: Return location for error or %NULL. * * Finishes an operation started with mm_modem_location_get_full(). * * Returns: %TRUE if the retrieval was successful, %FALSE if @error is set. * * Since: 1.0 */ gboolean mm_modem_location_get_full_finish (MMModemLocation *self, GAsyncResult *res, MMLocation3gpp **location_3gpp, MMLocationGpsNmea **location_gps_nmea, MMLocationGpsRaw **location_gps_raw, MMLocationCdmaBs **location_cdma_bs, GError **error) { g_autoptr(GVariant) dictionary = NULL; g_return_val_if_fail (MM_IS_MODEM_LOCATION (self), FALSE); if (!mm_gdbus_modem_location_call_get_location_finish (MM_GDBUS_MODEM_LOCATION (self), &dictionary, res, error)) return FALSE; return build_locations (dictionary, location_3gpp, location_gps_nmea, location_gps_raw, location_cdma_bs, error); } /** * mm_modem_location_get_full: * @self: A #MMModemLocation. * @cancellable: (allow-none): A #GCancellable or %NULL. * @callback: A #GAsyncReadyCallback to call when the request is satisfied or * %NULL. * @user_data: User data to pass to @callback. * * Asynchronously gets the current location information. * * When the operation is finished, @callback will be invoked in the * thread-default main loop * of the thread you are calling this method from. You can then call * mm_modem_location_get_full_finish() to get the result of the operation. * * See mm_modem_location_get_full_sync() for the synchronous, blocking version * of this method. * * Since: 1.0 */ void mm_modem_location_get_full (MMModemLocation *self, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { g_return_if_fail (MM_IS_MODEM_LOCATION (self)); mm_gdbus_modem_location_call_get_location (MM_GDBUS_MODEM_LOCATION (self), cancellable, callback, user_data); } /** * mm_modem_location_get_full_sync: * @self: A #MMModemLocation. * @location_3gpp: (out) (allow-none) (transfer full): Return location for a * #MMLocation3gpp if 3GPP location is requested, or #NULL if not required. The * returned value should be freed with g_object_unref(). * @location_gps_nmea: (out) (allow-none) (transfer full): Return location for a * #MMLocationGpsNmea if GPS NMEA location is requested, or #NULL if not * required. The returned value should be freed with g_object_unref(). * @location_gps_raw: (out) (allow-none) (transfer full): Return location for a * #MMLocationGpsRaw if GPS raw location is requested, or #NULL if not required. * The returned value should be freed with g_object_unref(). * @location_cdma_bs: (out) (allow-none) (transfer full): Return location for a * #MMLocationCdmaBs if CDMA Base Station location is requested, or #NULL if * not required. The returned value should be freed with g_object_unref(). * @cancellable: (allow-none): A #GCancellable or %NULL. * @error: Return location for error or %NULL. * * Synchronously gets the current location information. * * The calling thread is blocked until a reply is received. See * mm_modem_location_get_full() for the asynchronous version of this method. * * Returns: %TRUE if the setup was successful, %FALSE if @error is set. * * Since: 1.0 */ gboolean mm_modem_location_get_full_sync (MMModemLocation *self, MMLocation3gpp **location_3gpp, MMLocationGpsNmea **location_gps_nmea, MMLocationGpsRaw **location_gps_raw, MMLocationCdmaBs **location_cdma_bs, GCancellable *cancellable, GError **error) { g_autoptr(GVariant) dictionary = NULL; g_return_val_if_fail (MM_IS_MODEM_LOCATION (self), FALSE); if (!mm_gdbus_modem_location_call_get_location_sync (MM_GDBUS_MODEM_LOCATION (self), &dictionary, cancellable, error)) return FALSE; return build_locations (dictionary, location_3gpp, location_gps_nmea, location_gps_raw, location_cdma_bs, error); } /*****************************************************************************/ /** * mm_modem_location_get_3gpp_finish: * @self: A #MMModemLocation. * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to * mm_modem_location_get_3gpp(). * @error: Return location for error or %NULL. * * Finishes an operation started with mm_modem_location_get_3gpp(). * * Returns: (transfer full): A #MMLocation3gpp, or #NULL if not available. The * returned value should be freed with g_object_unref(). * * Since: 1.0 */ MMLocation3gpp * mm_modem_location_get_3gpp_finish (MMModemLocation *self, GAsyncResult *res, GError **error) { MMLocation3gpp *location = NULL; mm_modem_location_get_full_finish (self, res, &location, NULL, NULL, NULL, error); return location; } /** * mm_modem_location_get_3gpp: * @self: A #MMModemLocation. * @cancellable: (allow-none): A #GCancellable or %NULL. * @callback: A #GAsyncReadyCallback to call when the request is satisfied or * %NULL. * @user_data: User data to pass to @callback. * * Asynchronously gets the current 3GPP location information. * * When the operation is finished, @callback will be invoked in the * thread-default main loop * of the thread you are calling this method from. You can then call * mm_modem_location_get_3gpp_finish() to get the result of the operation. * * See mm_modem_location_get_3gpp_sync() for the synchronous, blocking version * of this method. * * Since: 1.0 */ void mm_modem_location_get_3gpp (MMModemLocation *self, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { mm_modem_location_get_full (self, cancellable, callback, user_data); } /** * mm_modem_location_get_3gpp_sync: * @self: A #MMModemLocation. * @cancellable: (allow-none): A #GCancellable or %NULL. * @error: Return location for error or %NULL. * * Synchronously gets the current 3GPP location information. * * The calling thread is blocked until a reply is received. See * mm_modem_location_get_3gpp() for the asynchronous version of this method. * * Returns: (transfer full): A #MMLocation3gpp, or #NULL if not available. The * returned value should be freed with g_object_unref(). * * Since: 1.0 */ MMLocation3gpp * mm_modem_location_get_3gpp_sync (MMModemLocation *self, GCancellable *cancellable, GError **error) { MMLocation3gpp *location = NULL; mm_modem_location_get_full_sync (self, &location, NULL, NULL, NULL, cancellable, error); return location; } /*****************************************************************************/ /** * mm_modem_location_get_gps_nmea_finish: * @self: A #MMModemLocation. * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to * mm_modem_location_get_gps_nmea(). * @error: Return location for error or %NULL. * * Finishes an operation started with mm_modem_location_get_gps_nmea(). * * Returns: (transfer full): A #MMLocationGpsNmea, or #NULL if not available. * The returned value should be freed with g_object_unref(). * * Since: 1.0 */ MMLocationGpsNmea * mm_modem_location_get_gps_nmea_finish (MMModemLocation *self, GAsyncResult *res, GError **error) { MMLocationGpsNmea *location = NULL; mm_modem_location_get_full_finish (self, res, NULL, &location, NULL, NULL, error); return location; } /** * mm_modem_location_get_gps_nmea: * @self: A #MMModemLocation. * @cancellable: (allow-none): A #GCancellable or %NULL. * @callback: A #GAsyncReadyCallback to call when the request is satisfied or * %NULL. * @user_data: User data to pass to @callback. * * Asynchronously gets the current GPS NMEA location information. * * When the operation is finished, @callback will be invoked in the * thread-default main loop * of the thread you are calling this method from. You can then call * mm_modem_location_get_gps_nmea_finish() to get the result of the operation. * * See mm_modem_location_get_gps_nmea_sync() for the synchronous, blocking * version of this method. * * Since: 1.0 */ void mm_modem_location_get_gps_nmea (MMModemLocation *self, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { mm_modem_location_get_full (self, cancellable, callback, user_data); } /** * mm_modem_location_get_gps_nmea_sync: * @self: A #MMModemLocation. * @cancellable: (allow-none): A #GCancellable or %NULL. * @error: Return location for error or %NULL. * * Synchronously gets the current GPS NMEA location information. * * The calling thread is blocked until a reply is received. See * mm_modem_location_get_gps_nmea() for the asynchronous version of this method. * * Returns: (transfer full): A #MMLocationGpsNmea, or #NULL if not available. * The returned value should be freed with g_object_unref(). * * Since: 1.0 */ MMLocationGpsNmea * mm_modem_location_get_gps_nmea_sync (MMModemLocation *self, GCancellable *cancellable, GError **error) { MMLocationGpsNmea *location = NULL; mm_modem_location_get_full_sync (self, NULL, &location, NULL, NULL, cancellable, error); return location; } /*****************************************************************************/ /** * mm_modem_location_get_gps_raw_finish: * @self: A #MMModemLocation. * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to * mm_modem_location_get_gps_raw(). * @error: Return location for error or %NULL. * * Finishes an operation started with mm_modem_location_get_gps_raw(). * * Returns: (transfer full): A #MMLocationGpsRaw, or #NULL if not available. * The returned value should be freed with g_object_unref(). * * Since: 1.0 */ MMLocationGpsRaw * mm_modem_location_get_gps_raw_finish (MMModemLocation *self, GAsyncResult *res, GError **error) { MMLocationGpsRaw *location = NULL; mm_modem_location_get_full_finish (self, res, NULL, NULL, &location, NULL, error); return location; } /** * mm_modem_location_get_gps_raw: * @self: A #MMModemLocation. * @cancellable: (allow-none): A #GCancellable or %NULL. * @callback: A #GAsyncReadyCallback to call when the request is satisfied or * %NULL. * @user_data: User data to pass to @callback. * * Asynchronously gets the current GPS raw location information. * * When the operation is finished, @callback will be invoked in the * thread-default main loop * of the thread you are calling this method from. You can then call * mm_modem_location_get_gps_raw_finish() to get the result of the operation. * * See mm_modem_location_get_gps_raw_sync() for the synchronous, blocking * version of this method. * * Since: 1.0 */ void mm_modem_location_get_gps_raw (MMModemLocation *self, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { mm_modem_location_get_full (self, cancellable, callback, user_data); } /** * mm_modem_location_get_gps_raw_sync: * @self: A #MMModemLocation. * @cancellable: (allow-none): A #GCancellable or %NULL. * @error: Return location for error or %NULL. * * Synchronously gets the current GPS raw location information. * * The calling thread is blocked until a reply is received. See * mm_modem_location_get_gps_raw() for the asynchronous version of this method. * * Returns: (transfer full): A #MMLocationGpsRaw, or #NULL if not available. * The returned value should be freed with g_object_unref(). * * Since: 1.0 */ MMLocationGpsRaw * mm_modem_location_get_gps_raw_sync (MMModemLocation *self, GCancellable *cancellable, GError **error) { MMLocationGpsRaw *location = NULL; mm_modem_location_get_full_sync (self, NULL, NULL, &location, NULL, cancellable, error); return location; } /*****************************************************************************/ /** * mm_modem_location_get_cdma_bs_finish: * @self: A #MMModemLocation. * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to * mm_modem_location_get_cdma_bs(). * @error: Return location for error or %NULL. * * Finishes an operation started with mm_modem_location_get_cdma_bs(). * * Returns: (transfer full): A #MMLocationCdmaBs, or #NULL if not available. * The returned value should be freed with g_object_unref(). * * Since: 1.0 */ MMLocationCdmaBs * mm_modem_location_get_cdma_bs_finish (MMModemLocation *self, GAsyncResult *res, GError **error) { MMLocationCdmaBs *location = NULL; mm_modem_location_get_full_finish (self, res, NULL, NULL, NULL, &location, error); return location; } /** * mm_modem_location_get_cdma_bs: * @self: A #MMModemLocation. * @cancellable: (allow-none): A #GCancellable or %NULL. * @callback: A #GAsyncReadyCallback to call when the request is satisfied or * %NULL. * @user_data: User data to pass to @callback. * * Asynchronously gets the current CDMA base station location information. * * When the operation is finished, @callback will be invoked in the * thread-default main loop * of the thread you are calling this method from. You can then call * mm_modem_location_get_cdma_bs_finish() to get the result of the operation. * * See mm_modem_location_get_cdma_bs_sync() for the synchronous, blocking * version of this method. * * Since: 1.0 */ void mm_modem_location_get_cdma_bs (MMModemLocation *self, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { mm_modem_location_get_full (self, cancellable, callback, user_data); } /** * mm_modem_location_get_cdma_bs_sync: * @self: A #MMModemLocation. * @cancellable: (allow-none): A #GCancellable or %NULL. * @error: Return location for error or %NULL. * * Synchronously gets the current CDMA base station location information. * * The calling thread is blocked until a reply is received. See * mm_modem_location_get_cdma_bs() for the asynchronous version of this method. * * Returns: (transfer full): A #MMLocationCdmaBs, or #NULL if not available. * The returned value should be freed with g_object_unref(). * * Since: 1.0 */ MMLocationCdmaBs * mm_modem_location_get_cdma_bs_sync (MMModemLocation *self, GCancellable *cancellable, GError **error) { MMLocationCdmaBs *location = NULL; mm_modem_location_get_full_sync (self, NULL, NULL, NULL, &location, cancellable, error); return location; } /*****************************************************************************/ /** * mm_modem_location_get_supl_server: * @self: A #MMModemLocation. * * Gets the address of the SUPL server. * * The returned value is only valid until the property changes so it is * only safe to use this function on the thread where @self was constructed. Use * mm_modem_location_dup_supl_server() if on another thread. * * Returns: (transfer none): The SUPL server address, or %NULL if none * available. Do not free the returned value, it belongs to @self. * * Since: 1.6 */ const gchar * mm_modem_location_get_supl_server (MMModemLocation *self) { g_return_val_if_fail (MM_IS_MODEM_LOCATION (self), NULL); RETURN_NON_EMPTY_CONSTANT_STRING ( mm_gdbus_modem_location_get_supl_server (MM_GDBUS_MODEM_LOCATION (self))); } /** * mm_modem_location_dup_supl_server: * @self: A #MMModemLocation. * * Gets the address of the SUPL server. * * Returns: (transfer full): The SUPL server address, or %NULL if none * available. The returned value should be freed with g_free(). * * Since: 1.6 */ gchar * mm_modem_location_dup_supl_server (MMModemLocation *self) { g_return_val_if_fail (MM_IS_MODEM_LOCATION (self), NULL); RETURN_NON_EMPTY_STRING ( mm_gdbus_modem_location_dup_supl_server (MM_GDBUS_MODEM_LOCATION (self))); } /*****************************************************************************/ /** * mm_modem_location_get_assistance_data_servers: * @self: A #MMModemLocation. * * Gets the list of assistance data servers. * * The returned value is only valid until the property changes so it is * only safe to use this function on the thread where @self was constructed. Use * mm_modem_location_dup_assistance_data_servers() if on another thread. * * * Returns: (transfer none): a %NULL-terminated array of server addresses, or * %NULL if none available. Do not free the returned value, it belongs to @self. * * Since: 1.10 */ const gchar ** mm_modem_location_get_assistance_data_servers (MMModemLocation *self) { const gchar **tmp; g_return_val_if_fail (MM_IS_MODEM_LOCATION (self), NULL); tmp = (const gchar **) mm_gdbus_modem_location_get_assistance_data_servers (MM_GDBUS_MODEM_LOCATION (self)); return ((tmp && tmp[0]) ? tmp : NULL); } /** * mm_modem_location_dup_assistance_data_servers: * @self: A #MMModemLocation. * * Gets the list of assistance data servers. * * Returns: (transfer full): a %NULL-terminated array of server addresses, or * %NULL if none available. The returned value should be freed with * g_strfreev(). * * Since: 1.10 */ gchar ** mm_modem_location_dup_assistance_data_servers (MMModemLocation *self) { gchar **tmp; g_return_val_if_fail (MM_IS_MODEM_LOCATION (self), NULL); tmp = mm_gdbus_modem_location_dup_assistance_data_servers (MM_GDBUS_MODEM_LOCATION (self)); if (tmp && tmp[0]) return tmp; g_strfreev (tmp); return NULL; } /*****************************************************************************/ /** * mm_modem_location_get_gps_refresh_rate: * @self: A #MMModemLocation. * * Gets the GPS refresh rate, in seconds. * * Returns: The GPS refresh rate, or 0 if no fixed rate is used. * * Since: 1.0 */ guint mm_modem_location_get_gps_refresh_rate (MMModemLocation *self) { g_return_val_if_fail (MM_IS_MODEM_LOCATION (self), 0); return mm_gdbus_modem_location_get_gps_refresh_rate (MM_GDBUS_MODEM_LOCATION (self)); } /*****************************************************************************/ /* custom refresh method instead of PROPERTY_OBJECT_DEFINE_REFRESH() */ static void signaled_location_refresh (MMModemLocation *self) { g_autoptr(GVariant) variant = NULL; g_autoptr(GError) inner_error = NULL; g_clear_object (&self->priv->signaled_3gpp); g_clear_object (&self->priv->signaled_gps_nmea); g_clear_object (&self->priv->signaled_gps_raw); g_clear_object (&self->priv->signaled_cdma_bs); variant = mm_gdbus_modem_location_dup_location (MM_GDBUS_MODEM_LOCATION (self)); if (!variant) return; if (!build_locations (variant, &self->priv->signaled_3gpp, &self->priv->signaled_gps_nmea, &self->priv->signaled_gps_raw, &self->priv->signaled_cdma_bs, &inner_error)) g_warning ("Invalid signaled location received: %s", inner_error->message); } PROPERTY_DEFINE_UPDATED (signaled_location, ModemLocation) /** * mm_modem_location_peek_signaled_3gpp: * @self: A #MMModemLocation. * * Gets a #MMLocation3gpp object with the current 3GPP location information. * * Unlike mm_modem_location_get_3gpp() or mm_modem_location_get_3gpp_sync(), * this method does not perform an explicit query. Instead, this method will * return the location information that may have been signaled by the modem. * Therefore, this method will only succeed if location signaling is enabled * (e.g. with mm_modem_location_setup() in the #MMModemLocation). * * The returned value is only valid until the property changes so * it is only safe to use this function on the thread where * @self was constructed. Use mm_modem_location_get_signaled_3gpp() if on * another thread. * * Returns: (transfer none): A #MMLocation3gpp, or %NULL if none available. Do * not free the returned value, it belongs to @self. * * Since: 1.18 */ PROPERTY_OBJECT_DEFINE_PEEK (signaled_location, signaled_3gpp, ModemLocation, modem_location, MODEM_LOCATION, MMLocation3gpp) /** * mm_modem_location_get_signaled_3gpp: * @self: A #MMModemLocation. * * Gets a #MMLocation3gpp object with the current 3GPP location information. * * Unlike mm_modem_location_get_3gpp() or mm_modem_location_get_3gpp_sync(), * this method does not perform an explicit query. Instead, this method will * return the location information that may have been signaled by the modem. * Therefore, this method will only succeed if location signaling is enabled * (e.g. with mm_modem_location_setup() in the #MMModemLocation). * * The values reported by @self are not updated when the values in the * interface change. Instead, the client is expected to call * mm_modem_location_get_signaled_3gpp() again to get a new #MMLocation3gpp * with the new values. * * Returns: (transfer full): A #MMLocation3gpp that must be freed with * g_object_unref() or %NULL if none available. * * Since: 1.18 */ PROPERTY_OBJECT_DEFINE_GET (signaled_location, signaled_3gpp, ModemLocation, modem_location, MODEM_LOCATION, MMLocation3gpp) /** * mm_modem_location_peek_signaled_gps_nmea: * @self: A #MMModemLocation. * * Gets a #MMLocationGpsNmea object with the current GPS NMEA location * information. * * Unlike mm_modem_location_get_gps_nmea() or * mm_modem_location_get_gps_nmea_sync(), this method does not perform an * explicit query. Instead, this method will return the location information * that may have been signaled by the modem. Therefore, this method will only * succeed if location signaling is enabled (e.g. with mm_modem_location_setup() * in the #MMModemLocation). * * The returned value is only valid until the property changes so * it is only safe to use this function on the thread where * @self was constructed. Use mm_modem_location_get_signaled_gps_nmea() if on * another thread. * * Returns: (transfer none): A #MMLocationGpsNmea, or %NULL if none available. Do * not free the returned value, it belongs to @self. * * Since: 1.18 */ PROPERTY_OBJECT_DEFINE_PEEK (signaled_location, signaled_gps_nmea, ModemLocation, modem_location, MODEM_LOCATION, MMLocationGpsNmea) /** * mm_modem_location_get_signaled_gps_nmea: * @self: A #MMModemLocation. * * Gets a #MMLocationGpsNmea object with the current GPS NMEA location * information. * * Unlike mm_modem_location_get_gps_nmea() or * mm_modem_location_get_gps_nmea_sync(), this method does not perform an * explicit query. Instead, this method will return the location information * that may have been signaled by the modem. Therefore, this method will only * succeed if location signaling is enabled (e.g. with mm_modem_location_setup() * in the #MMModemLocation). * * The values reported by @self are not updated when the values in the * interface change. Instead, the client is expected to call * mm_modem_location_get_signaled_gps_nmea() again to get a new #MMLocationGpsNmea * with the new values. * * Returns: (transfer full): A #MMLocationGpsNmea that must be freed with * g_object_unref() or %NULL if none available. * * Since: 1.18 */ PROPERTY_OBJECT_DEFINE_GET (signaled_location, signaled_gps_nmea, ModemLocation, modem_location, MODEM_LOCATION, MMLocationGpsNmea) /** * mm_modem_location_peek_signaled_gps_raw: * @self: A #MMModemLocation. * * Gets a #MMLocationGpsRaw object with the current GPS raw location * information. * * Unlike mm_modem_location_get_gps_raw() or * mm_modem_location_get_gps_raw_sync(), this method does not perform an * explicit query. Instead, this method will return the location information * that may have been signaled by the modem. Therefore, this method will only * succeed if location signaling is enabled (e.g. with mm_modem_location_setup() * in the #MMModemLocation). * * The returned value is only valid until the property changes so * it is only safe to use this function on the thread where * @self was constructed. Use mm_modem_location_get_signaled_gps_raw() if on * another thread. * * Returns: (transfer none): A #MMLocationGpsRaw, or %NULL if none available. Do * not free the returned value, it belongs to @self. * * Since: 1.18 */ PROPERTY_OBJECT_DEFINE_PEEK (signaled_location, signaled_gps_raw, ModemLocation, modem_location, MODEM_LOCATION, MMLocationGpsRaw) /** * mm_modem_location_get_signaled_gps_raw: * @self: A #MMModemLocation. * * Gets a #MMLocationGpsRaw object with the current GPS raw location * information. * * Unlike mm_modem_location_get_gps_raw() or * mm_modem_location_get_gps_raw_sync(), this method does not perform an * explicit query. Instead, this method will return the location information * that may have been signaled by the modem. Therefore, this method will only * succeed if location signaling is enabled (e.g. with mm_modem_location_setup() * in the #MMModemLocation). * * The values reported by @self are not updated when the values in the * interface change. Instead, the client is expected to call * mm_modem_location_get_signaled_gps_raw() again to get a new #MMLocationGpsRaw * with the new values. * * Returns: (transfer full): A #MMLocationGpsRaw that must be freed with * g_object_unref() or %NULL if none available. * * Since: 1.18 */ PROPERTY_OBJECT_DEFINE_GET (signaled_location, signaled_gps_raw, ModemLocation, modem_location, MODEM_LOCATION, MMLocationGpsRaw) /** * mm_modem_location_peek_signaled_cdma_bs: * @self: A #MMModemLocation. * * Gets a #MMLocationCdmaBs object with the current CDMA base station location * information. * * Unlike mm_modem_location_get_cdma_bs() or * mm_modem_location_get_cdma_bs_sync(), this method does not perform an * explicit query. Instead, this method will return the location information * that may have been signaled by the modem. Therefore, this method will only * succeed if location signaling is enabled (e.g. with mm_modem_location_setup() * in the #MMModemLocation). * * The returned value is only valid until the property changes so * it is only safe to use this function on the thread where * @self was constructed. Use mm_modem_location_get_signaled_cdma_bs() if on * another thread. * * Returns: (transfer none): A #MMLocationCdmaBs, or %NULL if none available. Do * not free the returned value, it belongs to @self. * * Since: 1.18 */ PROPERTY_OBJECT_DEFINE_PEEK (signaled_location, signaled_cdma_bs, ModemLocation, modem_location, MODEM_LOCATION, MMLocationCdmaBs) /** * mm_modem_location_get_signaled_cdma_bs: * @self: A #MMModemLocation. * * Gets a #MMLocationCdmaBs object with the current CDMA base station location * information. * * Unlike mm_modem_location_get_cdma_bs() or * mm_modem_location_get_cdma_bs_sync(), this method does not perform an * explicit query. Instead, this method will return the location information * that may have been signaled by the modem. Therefore, this method will only * succeed if location signaling is enabled (e.g. with mm_modem_location_setup() * in the #MMModemLocation). * * The values reported by @self are not updated when the values in the * interface change. Instead, the client is expected to call * mm_modem_location_get_signaled_cdma_bs() again to get a new #MMLocationCdmaBs * with the new values. * * Returns: (transfer full): A #MMLocationCdmaBs that must be freed with * g_object_unref() or %NULL if none available. * * Since: 1.18 */ PROPERTY_OBJECT_DEFINE_GET (signaled_location, signaled_cdma_bs, ModemLocation, modem_location, MODEM_LOCATION, MMLocationCdmaBs) /*****************************************************************************/ static void mm_modem_location_init (MMModemLocation *self) { self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, MM_TYPE_MODEM_LOCATION, MMModemLocationPrivate); g_mutex_init (&self->priv->mutex); PROPERTY_INITIALIZE (signaled_location, "location") } static void finalize (GObject *object) { MMModemLocation *self = MM_MODEM_LOCATION (object); g_mutex_clear (&self->priv->mutex); PROPERTY_OBJECT_FINALIZE (signaled_3gpp) PROPERTY_OBJECT_FINALIZE (signaled_gps_nmea) PROPERTY_OBJECT_FINALIZE (signaled_gps_raw) PROPERTY_OBJECT_FINALIZE (signaled_cdma_bs) G_OBJECT_CLASS (mm_modem_location_parent_class)->finalize (object); } static void mm_modem_location_class_init (MMModemLocationClass *modem_class) { GObjectClass *object_class = G_OBJECT_CLASS (modem_class); g_type_class_add_private (object_class, sizeof (MMModemLocationPrivate)); object_class->finalize = finalize; } ModemManager-1.23.4-dev/libmm-glib/mm-modem-location.h000066400000000000000000000306171456466623000224360ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * libmm-glib -- Access modem status & information from glib applications * * 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 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 Street, Fifth Floor, * Boston, MA 02110-1301 USA. * * Copyright (C) 2012 Google, Inc. * Copyright (C) 2012 Lanedo GmbH */ #ifndef _MM_MODEM_LOCATION_H_ #define _MM_MODEM_LOCATION_H_ #if !defined (__LIBMM_GLIB_H_INSIDE__) && !defined (LIBMM_GLIB_COMPILATION) #error "Only can be included directly." #endif #include #include "mm-gdbus-modem.h" #include "mm-location-3gpp.h" #include "mm-location-gps-nmea.h" #include "mm-location-gps-raw.h" #include "mm-location-cdma-bs.h" G_BEGIN_DECLS #define MM_TYPE_MODEM_LOCATION (mm_modem_location_get_type ()) #define MM_MODEM_LOCATION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_MODEM_LOCATION, MMModemLocation)) #define MM_MODEM_LOCATION_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_MODEM_LOCATION, MMModemLocationClass)) #define MM_IS_MODEM_LOCATION(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_MODEM_LOCATION)) #define MM_IS_MODEM_LOCATION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), MM_TYPE_MODEM_LOCATION)) #define MM_MODEM_LOCATION_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_MODEM_LOCATION, MMModemLocationClass)) typedef struct _MMModemLocation MMModemLocation; typedef struct _MMModemLocationClass MMModemLocationClass; typedef struct _MMModemLocationPrivate MMModemLocationPrivate; /** * MMModemLocation: * * The #MMModemLocation structure contains private data and should only be accessed * using the provided API. */ struct _MMModemLocation { /*< private >*/ MmGdbusModemLocationProxy parent; MMModemLocationPrivate *priv; }; struct _MMModemLocationClass { /*< private >*/ MmGdbusModemLocationProxyClass parent; }; GType mm_modem_location_get_type (void); G_DEFINE_AUTOPTR_CLEANUP_FUNC (MMModemLocation, g_object_unref) const gchar *mm_modem_location_get_path (MMModemLocation *self); gchar *mm_modem_location_dup_path (MMModemLocation *self); MMModemLocationSource mm_modem_location_get_capabilities (MMModemLocation *self); MMModemLocationSource mm_modem_location_get_enabled (MMModemLocation *self); gboolean mm_modem_location_signals_location (MMModemLocation *self); MMModemLocationAssistanceDataType mm_modem_location_get_supported_assistance_data (MMModemLocation *self); const gchar *mm_modem_location_get_supl_server (MMModemLocation *self); gchar *mm_modem_location_dup_supl_server (MMModemLocation *self); const gchar **mm_modem_location_get_assistance_data_servers (MMModemLocation *self); gchar **mm_modem_location_dup_assistance_data_servers (MMModemLocation *self); guint mm_modem_location_get_gps_refresh_rate (MMModemLocation *self); void mm_modem_location_setup (MMModemLocation *self, MMModemLocationSource sources, gboolean signal_location, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); gboolean mm_modem_location_setup_finish (MMModemLocation *self, GAsyncResult *res, GError **error); gboolean mm_modem_location_setup_sync (MMModemLocation *self, MMModemLocationSource sources, gboolean signal_location, GCancellable *cancellable, GError **error); void mm_modem_location_set_supl_server (MMModemLocation *self, const gchar *supl, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); gboolean mm_modem_location_set_supl_server_finish (MMModemLocation *self, GAsyncResult *res, GError **error); gboolean mm_modem_location_set_supl_server_sync (MMModemLocation *self, const gchar *supl, GCancellable *cancellable, GError **error); void mm_modem_location_inject_assistance_data (MMModemLocation *self, const guint8 *data, gsize data_size, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); gboolean mm_modem_location_inject_assistance_data_finish (MMModemLocation *self, GAsyncResult *res, GError **error); gboolean mm_modem_location_inject_assistance_data_sync (MMModemLocation *self, const guint8 *data, gsize data_size, GCancellable *cancellable, GError **error); void mm_modem_location_set_gps_refresh_rate (MMModemLocation *self, guint rate, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); gboolean mm_modem_location_set_gps_refresh_rate_finish (MMModemLocation *self, GAsyncResult *res, GError **error); gboolean mm_modem_location_set_gps_refresh_rate_sync (MMModemLocation *self, guint rate, GCancellable *cancellable, GError **error); void mm_modem_location_get_3gpp (MMModemLocation *self, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); MMLocation3gpp *mm_modem_location_get_3gpp_finish (MMModemLocation *self, GAsyncResult *res, GError **error); MMLocation3gpp *mm_modem_location_get_3gpp_sync (MMModemLocation *self, GCancellable *cancellable, GError **error); void mm_modem_location_get_gps_nmea (MMModemLocation *self, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); MMLocationGpsNmea *mm_modem_location_get_gps_nmea_finish (MMModemLocation *self, GAsyncResult *res, GError **error); MMLocationGpsNmea *mm_modem_location_get_gps_nmea_sync (MMModemLocation *self, GCancellable *cancellable, GError **error); void mm_modem_location_get_gps_raw (MMModemLocation *self, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); MMLocationGpsRaw *mm_modem_location_get_gps_raw_finish (MMModemLocation *self, GAsyncResult *res, GError **error); MMLocationGpsRaw *mm_modem_location_get_gps_raw_sync (MMModemLocation *self, GCancellable *cancellable, GError **error); void mm_modem_location_get_cdma_bs (MMModemLocation *self, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); MMLocationCdmaBs *mm_modem_location_get_cdma_bs_finish (MMModemLocation *self, GAsyncResult *res, GError **error); MMLocationCdmaBs *mm_modem_location_get_cdma_bs_sync (MMModemLocation *self, GCancellable *cancellable, GError **error); void mm_modem_location_get_full (MMModemLocation *self, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); gboolean mm_modem_location_get_full_finish (MMModemLocation *self, GAsyncResult *res, MMLocation3gpp **location_3gpp, MMLocationGpsNmea **location_gps_nmea, MMLocationGpsRaw **location_gps_raw, MMLocationCdmaBs **location_cdma_bs, GError **error); gboolean mm_modem_location_get_full_sync (MMModemLocation *self, MMLocation3gpp **location_3gpp, MMLocationGpsNmea **location_gps_nmea, MMLocationGpsRaw **location_gps_raw, MMLocationCdmaBs **location_cdma_bs, GCancellable *cancellable, GError **error); MMLocation3gpp *mm_modem_location_peek_signaled_3gpp (MMModemLocation *self); MMLocation3gpp *mm_modem_location_get_signaled_3gpp (MMModemLocation *self); MMLocationGpsNmea *mm_modem_location_peek_signaled_gps_nmea (MMModemLocation *self); MMLocationGpsNmea *mm_modem_location_get_signaled_gps_nmea (MMModemLocation *self); MMLocationGpsRaw *mm_modem_location_peek_signaled_gps_raw (MMModemLocation *self); MMLocationGpsRaw *mm_modem_location_get_signaled_gps_raw (MMModemLocation *self); MMLocationCdmaBs *mm_modem_location_peek_signaled_cdma_bs (MMModemLocation *self); MMLocationCdmaBs *mm_modem_location_get_signaled_cdma_bs (MMModemLocation *self); G_END_DECLS #endif /* _MM_MODEM_LOCATION_H_ */ ModemManager-1.23.4-dev/libmm-glib/mm-modem-messaging.c000066400000000000000000000532501456466623000225740ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * libmm-glib -- Access modem status & information from glib applications * * 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 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 Street, Fifth Floor, * Boston, MA 02110-1301 USA. * * Copyright (C) 2012 Aleksander Morgado * Copyright (C) 2012 Google, Inc. */ #include #include "mm-helpers.h" #include "mm-common-helpers.h" #include "mm-errors-types.h" #include "mm-modem-messaging.h" /** * SECTION: mm-modem-messaging * @title: MMModemMessaging * @short_description: The Messaging interface * * The #MMModemMessaging is an object providing access to the methods, signals and * properties of the Messaging interface. * * The Messaging interface is exposed whenever a modem has messaging capabilities. */ G_DEFINE_TYPE (MMModemMessaging, mm_modem_messaging, MM_GDBUS_TYPE_MODEM_MESSAGING_PROXY) struct _MMModemMessagingPrivate { /* Common mutex to sync access */ GMutex mutex; PROPERTY_ARRAY_DECLARE (supported_storages) }; /*****************************************************************************/ /** * mm_modem_messaging_get_path: * @self: A #MMModemMessaging. * * Gets the DBus path of the #MMObject which implements this interface. * * Returns: (transfer none): The DBus path of the #MMObject object. * * Since: 1.0 */ const gchar * mm_modem_messaging_get_path (MMModemMessaging *self) { g_return_val_if_fail (MM_IS_MODEM_MESSAGING (self), NULL); RETURN_NON_EMPTY_CONSTANT_STRING ( g_dbus_proxy_get_object_path (G_DBUS_PROXY (self))); } /** * mm_modem_messaging_dup_path: * @self: A #MMModemMessaging. * * Gets a copy of the DBus path of the #MMObject object which implements this * interface. * * Returns: (transfer full): The DBus path of the #MMObject. The returned value * should be freed with g_free(). * * Since: 1.0 */ gchar * mm_modem_messaging_dup_path (MMModemMessaging *self) { gchar *value; g_return_val_if_fail (MM_IS_MODEM_MESSAGING (self), NULL); g_object_get (G_OBJECT (self), "g-object-path", &value, NULL); RETURN_NON_EMPTY_STRING (value); } /*****************************************************************************/ /** * mm_modem_messaging_get_supported_storages: * @self: A #MMModem. * @storages: (out) (array length=n_storages): Return location for the array of * #MMSmsStorage values. The returned array should be freed with g_free() when * no longer needed. * @n_storages: (out): Return location for the number of values in @storages. * * Gets the list of SMS storages supported by the #MMModem. * * Returns: %TRUE if @storages and @n_storages are set, %FALSE otherwise. * * Since: 1.0 */ /** * mm_modem_messaging_peek_supported_storages: * @self: A #MMModem. * @storages: (out): Return location for the array of #MMSmsStorage values. Do * not free the returned array, it is owned by @self. * @n_storages: (out): Return location for the number of values in @storages. * * Gets the list of SMS storages supported by the #MMModem. * * Returns: %TRUE if @storages and @n_storages are set, %FALSE otherwise. * * Since: 1.0 */ PROPERTY_ARRAY_DEFINE (supported_storages, ModemMessaging, modem_messaging, MODEM_MESSAGING, MMSmsStorage, mm_common_sms_storages_variant_to_garray) /*****************************************************************************/ /** * mm_modem_messaging_get_default_storage: * @self: A #MMModem. * * Gets the default SMS storage used when storing or receiving SMS messages. * * Returns: the default #MMSmsStorage. * * Since: 1.0 */ MMSmsStorage mm_modem_messaging_get_default_storage (MMModemMessaging *self) { g_return_val_if_fail (MM_IS_MODEM_MESSAGING (self), MM_SMS_STORAGE_UNKNOWN); return (MMSmsStorage)mm_gdbus_modem_messaging_get_default_storage (MM_GDBUS_MODEM_MESSAGING (self)); } /*****************************************************************************/ typedef struct { gchar **sms_paths; GList *sms_objects; guint i; } ListSmsContext; static void sms_object_list_free (GList *list) { g_list_free_full (list, g_object_unref); } static void list_sms_context_free (ListSmsContext *ctx) { g_strfreev (ctx->sms_paths); sms_object_list_free (ctx->sms_objects); g_slice_free (ListSmsContext, ctx); } /** * mm_modem_messaging_list_finish: * @self: A #MMModem. * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to * mm_modem_messaging_list(). * @error: Return location for error or %NULL. * * Finishes an operation started with mm_modem_messaging_list(). * * Returns: (element-type ModemManager.Sms) (transfer full): A list of #MMSms * objects, or #NULL if either not found or @error is set. The returned value * should be freed with g_list_free_full() using g_object_unref() as * #GDestroyNotify function. * * Since: 1.0 */ GList * mm_modem_messaging_list_finish (MMModemMessaging *self, GAsyncResult *res, GError **error) { g_return_val_if_fail (MM_IS_MODEM_MESSAGING (self), FALSE); return g_task_propagate_pointer (G_TASK (res), error); } static void create_next_sms (GTask *task); static void list_build_object_ready (GDBusConnection *connection, GAsyncResult *res, GTask *task) { GError *error = NULL; GObject *sms; GObject *source_object; ListSmsContext *ctx; source_object = g_async_result_get_source_object (res); sms = g_async_initable_new_finish (G_ASYNC_INITABLE (source_object), res, &error); g_object_unref (source_object); if (error) { g_task_return_error (task, error); g_object_unref (task); return; } ctx = g_task_get_task_data (task); /* Keep the object */ ctx->sms_objects = g_list_prepend (ctx->sms_objects, sms); /* If no more smss, just end here. */ if (!ctx->sms_paths[++ctx->i]) { GList *sms_objects; sms_objects = g_list_copy_deep (ctx->sms_objects, (GCopyFunc)g_object_ref, NULL); g_task_return_pointer (task, sms_objects, (GDestroyNotify)sms_object_list_free); g_object_unref (task); return; } /* Keep on creating next object */ create_next_sms (task); } static void create_next_sms (GTask *task) { MMModemMessaging *self; ListSmsContext *ctx; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); g_async_initable_new_async (MM_TYPE_SMS, G_PRIORITY_DEFAULT, g_task_get_cancellable (task), (GAsyncReadyCallback)list_build_object_ready, task, "g-flags", G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START, "g-name", MM_DBUS_SERVICE, "g-connection", g_dbus_proxy_get_connection (G_DBUS_PROXY (self)), "g-object-path", ctx->sms_paths[ctx->i], "g-interface-name", "org.freedesktop.ModemManager1.Sms", NULL); } /** * mm_modem_messaging_list: * @self: A #MMModemMessaging. * @cancellable: (allow-none): A #GCancellable or %NULL. * @callback: A #GAsyncReadyCallback to call when the request is satisfied or * %NULL. * @user_data: User data to pass to @callback. * * Asynchronously lists the #MMSms objects in the modem. * * When the operation is finished, @callback will be invoked in the * thread-default main loop * of the thread you are calling this method from. You can then call * mm_modem_messaging_list_finish() to get the result of the operation. * * See mm_modem_messaging_list_sync() for the synchronous, blocking version of * this method. * * Since: 1.0 */ void mm_modem_messaging_list (MMModemMessaging *self, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { ListSmsContext *ctx; GTask *task; g_return_if_fail (MM_IS_MODEM_MESSAGING (self)); ctx = g_slice_new0 (ListSmsContext); ctx->sms_paths = mm_gdbus_modem_messaging_dup_messages (MM_GDBUS_MODEM_MESSAGING (self)); task = g_task_new (self, cancellable, callback, user_data); g_task_set_task_data (task, ctx, (GDestroyNotify)list_sms_context_free); /* If no SMS, just end here. */ if (!ctx->sms_paths || !ctx->sms_paths[0]) { g_task_return_pointer (task, NULL, NULL); g_object_unref (task); return; } /* Got list of paths. If at least one found, start creating objects for each */ ctx->i = 0; create_next_sms (task); } /** * mm_modem_messaging_list_sync: * @self: A #MMModemMessaging. * @cancellable: (allow-none): A #GCancellable or %NULL. * @error: Return location for error or %NULL. * * Synchronously lists the #MMSms objects in the modem. * * The calling thread is blocked until a reply is received. See * mm_modem_messaging_list() for the asynchronous version of this method. * * Returns: (element-type ModemManager.Sms) (transfer full): A list of #MMSms * objects, or #NULL if either not found or @error is set. The returned value * should be freed with g_list_free_full() using g_object_unref() as * #GDestroyNotify function. * * Since: 1.0 */ GList * mm_modem_messaging_list_sync (MMModemMessaging *self, GCancellable *cancellable, GError **error) { GList *sms_objects = NULL; gchar **sms_paths = NULL; guint i; g_return_val_if_fail (MM_IS_MODEM_MESSAGING (self), NULL); sms_paths = mm_gdbus_modem_messaging_dup_messages (MM_GDBUS_MODEM_MESSAGING (self)); /* Only non-empty lists are returned */ if (!sms_paths) return NULL; for (i = 0; sms_paths[i]; i++) { GObject *sms; sms = g_initable_new (MM_TYPE_SMS, cancellable, error, "g-flags", G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START, "g-name", MM_DBUS_SERVICE, "g-connection", g_dbus_proxy_get_connection (G_DBUS_PROXY (self)), "g-object-path", sms_paths[i], "g-interface-name", "org.freedesktop.ModemManager1.Sms", NULL); if (!sms) { sms_object_list_free (sms_objects); g_strfreev (sms_paths); return NULL; } /* Keep the object */ sms_objects = g_list_prepend (sms_objects, sms); } g_strfreev (sms_paths); return sms_objects; } /*****************************************************************************/ /** * mm_modem_messaging_create_finish: * @self: A #MMModemMessaging. * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to * mm_modem_messaging_create(). * @error: Return location for error or %NULL. * * Finishes an operation started with mm_modem_messaging_create(). * * Returns: (transfer full): A newly created #MMSms, or %NULL if @error is set. * The returned value should be freed with g_object_unref(). * * Since: 1.0 */ MMSms * mm_modem_messaging_create_finish (MMModemMessaging *self, GAsyncResult *res, GError **error) { g_return_val_if_fail (MM_IS_MODEM_MESSAGING (self), NULL); return g_task_propagate_pointer (G_TASK (res), error); } static void new_sms_object_ready (GDBusConnection *connection, GAsyncResult *res, GTask *task) { GError *error = NULL; GObject *sms; GObject *source_object; source_object = g_async_result_get_source_object (res); sms = g_async_initable_new_finish (G_ASYNC_INITABLE (source_object), res, &error); g_object_unref (source_object); if (error) g_task_return_error (task, error); else g_task_return_pointer (task, sms, g_object_unref); g_object_unref (task); } static void create_sms_ready (MMModemMessaging *self, GAsyncResult *res, GTask *task) { GError *error = NULL; gchar *sms_path = NULL; if (!mm_gdbus_modem_messaging_call_create_finish (MM_GDBUS_MODEM_MESSAGING (self), &sms_path, res, &error)) { g_task_return_error (task, error); g_object_unref (task); g_free (sms_path); return; } g_async_initable_new_async (MM_TYPE_SMS, G_PRIORITY_DEFAULT, g_task_get_cancellable (task), (GAsyncReadyCallback)new_sms_object_ready, task, "g-flags", G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START, "g-name", MM_DBUS_SERVICE, "g-connection", g_dbus_proxy_get_connection (G_DBUS_PROXY (self)), "g-object-path", sms_path, "g-interface-name", "org.freedesktop.ModemManager1.Sms", NULL); g_free (sms_path); } /** * mm_modem_messaging_create: * @self: A #MMModemMessaging. * @properties: A ##MMSmsProperties object with the properties to use. * @cancellable: (allow-none): A #GCancellable or %NULL. * @callback: A #GAsyncReadyCallback to call when the request is satisfied or * %NULL. * @user_data: User data to pass to @callback. * * Asynchronously creates a new #MMSms in the modem. * * When the operation is finished, @callback will be invoked in the * thread-default main loop * of the thread you are calling this method from. You can then call * mm_modem_messaging_create_finish() to get the result of the operation. * * See mm_modem_messaging_create_sync() for the synchronous, blocking version of * this method. * * Since: 1.0 */ void mm_modem_messaging_create (MMModemMessaging *self, MMSmsProperties *properties, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; GVariant *dictionary; g_return_if_fail (MM_IS_MODEM_MESSAGING (self)); task = g_task_new (self, cancellable, callback, user_data); dictionary = (mm_sms_properties_get_dictionary (properties)); mm_gdbus_modem_messaging_call_create ( MM_GDBUS_MODEM_MESSAGING (self), dictionary, cancellable, (GAsyncReadyCallback)create_sms_ready, task); g_variant_unref (dictionary); } /** * mm_modem_messaging_create_sync: * @self: A #MMModemMessaging. * @properties: A ##MMSmsProperties object with the properties to use. * @cancellable: (allow-none): A #GCancellable or %NULL. * @error: Return location for error or %NULL. * * Synchronously creates a new #MMSms in the modem. * * The calling thread is blocked until a reply is received. See * mm_modem_messaging_create() for the asynchronous version of this method. * * Returns: (transfer full): A newly created #MMSms, or %NULL if @error is set. * The returned value should be freed with g_object_unref(). * * Since: 1.0 */ MMSms * mm_modem_messaging_create_sync (MMModemMessaging *self, MMSmsProperties *properties, GCancellable *cancellable, GError **error) { MMSms *sms = NULL; gchar *sms_path = NULL; GVariant *dictionary; g_return_val_if_fail (MM_IS_MODEM_MESSAGING (self), NULL); dictionary = (mm_sms_properties_get_dictionary (properties)); mm_gdbus_modem_messaging_call_create_sync (MM_GDBUS_MODEM_MESSAGING (self), dictionary, &sms_path, cancellable, error); if (sms_path) { sms = g_initable_new (MM_TYPE_SMS, cancellable, error, "g-flags", G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START, "g-name", MM_DBUS_SERVICE, "g-connection", g_dbus_proxy_get_connection (G_DBUS_PROXY (self)), "g-object-path", sms_path, "g-interface-name", "org.freedesktop.ModemManager1.Sms", NULL); g_free (sms_path); } g_variant_unref (dictionary); return (sms ? MM_SMS (sms) : NULL); } /*****************************************************************************/ /** * mm_modem_messaging_delete_finish: * @self: A #MMModemMessaging. * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to * mm_modem_messaging_delete(). * @error: Return location for error or %NULL. * * Finishes an operation started with mm_modem_messaging_delete(). * * Returns: %TRUE if the sms was deleted, %FALSE if @error is set. * * Since: 1.0 */ gboolean mm_modem_messaging_delete_finish (MMModemMessaging *self, GAsyncResult *res, GError **error) { g_return_val_if_fail (MM_IS_MODEM_MESSAGING (self), FALSE); return mm_gdbus_modem_messaging_call_delete_finish (MM_GDBUS_MODEM_MESSAGING (self), res, error); } /** * mm_modem_messaging_delete: * @self: A #MMModemMessaging. * @sms: Path of the #MMSms to delete. * @cancellable: (allow-none): A #GCancellable or %NULL. * @callback: A #GAsyncReadyCallback to call when the request is satisfied or * %NULL. * @user_data: User data to pass to @callback. * * Asynchronously deletes a given #MMSms from the modem. * * When the operation is finished, @callback will be invoked in the * thread-default main loop * of the thread you are calling this method from. You can then call * mm_modem_messaging_delete_finish() to get the result of the operation. * * See mm_modem_messaging_delete_sync() for the synchronous, blocking version * of this method. * * Since: 1.0 */ void mm_modem_messaging_delete (MMModemMessaging *self, const gchar *sms, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { g_return_if_fail (MM_IS_MODEM_MESSAGING (self)); mm_gdbus_modem_messaging_call_delete (MM_GDBUS_MODEM_MESSAGING (self), sms, cancellable, callback, user_data); } /** * mm_modem_messaging_delete_sync: * @self: A #MMModemMessaging. * @sms: Path of the #MMSms to delete. * @cancellable: (allow-none): A #GCancellable or %NULL. * @error: Return location for error or %NULL. * Synchronously deletes a given #MMSms from the modem. * * The calling thread is blocked until a reply is received. See * mm_modem_messaging_delete() for the asynchronous version of this method. * * Returns: %TRUE if the SMS was deleted, %FALSE if @error is set. * * Since: 1.0 */ gboolean mm_modem_messaging_delete_sync (MMModemMessaging *self, const gchar *sms, GCancellable *cancellable, GError **error) { g_return_val_if_fail (MM_IS_MODEM_MESSAGING (self), FALSE); return mm_gdbus_modem_messaging_call_delete_sync (MM_GDBUS_MODEM_MESSAGING (self), sms, cancellable, error); } /*****************************************************************************/ static void mm_modem_messaging_init (MMModemMessaging *self) { self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, MM_TYPE_MODEM_MESSAGING, MMModemMessagingPrivate); g_mutex_init (&self->priv->mutex); PROPERTY_INITIALIZE (supported_storages, "supported-storages") } static void finalize (GObject *object) { MMModemMessaging *self = MM_MODEM_MESSAGING (object); g_mutex_clear (&self->priv->mutex); PROPERTY_ARRAY_FINALIZE (supported_storages) G_OBJECT_CLASS (mm_modem_messaging_parent_class)->finalize (object); } static void mm_modem_messaging_class_init (MMModemMessagingClass *modem_class) { GObjectClass *object_class = G_OBJECT_CLASS (modem_class); g_type_class_add_private (object_class, sizeof (MMModemMessagingPrivate)); object_class->finalize = finalize; } ModemManager-1.23.4-dev/libmm-glib/mm-modem-messaging.h000066400000000000000000000127161456466623000226030ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * libmm-glib -- Access modem status & information from glib applications * * 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 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 Street, Fifth Floor, * Boston, MA 02110-1301 USA. * * Copyright (C) 2012 Aleksander Morgado * Copyright (C) 2012 Google, Inc. */ #ifndef _MM_MODEM_MESSAGING_H_ #define _MM_MODEM_MESSAGING_H_ #if !defined (__LIBMM_GLIB_H_INSIDE__) && !defined (LIBMM_GLIB_COMPILATION) #error "Only can be included directly." #endif #include #include "mm-gdbus-modem.h" #include "mm-sms.h" #include "mm-sms-properties.h" G_BEGIN_DECLS #define MM_TYPE_MODEM_MESSAGING (mm_modem_messaging_get_type ()) #define MM_MODEM_MESSAGING(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_MODEM_MESSAGING, MMModemMessaging)) #define MM_MODEM_MESSAGING_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_MODEM_MESSAGING, MMModemMessagingClass)) #define MM_IS_MODEM_MESSAGING(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_MODEM_MESSAGING)) #define MM_IS_MODEM_MESSAGING_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), MM_TYPE_MODEM_MESSAGING)) #define MM_MODEM_MESSAGING_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_MODEM_MESSAGING, MMModemMessagingClass)) typedef struct _MMModemMessaging MMModemMessaging; typedef struct _MMModemMessagingClass MMModemMessagingClass; typedef struct _MMModemMessagingPrivate MMModemMessagingPrivate; /** * MMModemMessaging: * * The #MMModemMessaging structure contains private data and should only be accessed * using the provided API. */ struct _MMModemMessaging { /*< private >*/ MmGdbusModemMessagingProxy parent; MMModemMessagingPrivate *priv; }; struct _MMModemMessagingClass { /*< private >*/ MmGdbusModemMessagingProxyClass parent; }; GType mm_modem_messaging_get_type (void); G_DEFINE_AUTOPTR_CLEANUP_FUNC (MMModemMessaging, g_object_unref) const gchar *mm_modem_messaging_get_path (MMModemMessaging *self); gchar *mm_modem_messaging_dup_path (MMModemMessaging *self); gboolean mm_modem_messaging_get_supported_storages (MMModemMessaging *self, MMSmsStorage **storages, guint *n_storages); gboolean mm_modem_messaging_peek_supported_storages (MMModemMessaging *self, const MMSmsStorage **storages, guint *n_storages); MMSmsStorage mm_modem_messaging_get_default_storage (MMModemMessaging *self); void mm_modem_messaging_create (MMModemMessaging *self, MMSmsProperties *properties, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); MMSms *mm_modem_messaging_create_finish (MMModemMessaging *self, GAsyncResult *res, GError **error); MMSms *mm_modem_messaging_create_sync (MMModemMessaging *self, MMSmsProperties *properties, GCancellable *cancellable, GError **error); void mm_modem_messaging_list (MMModemMessaging *self, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); GList *mm_modem_messaging_list_finish (MMModemMessaging *self, GAsyncResult *res, GError **error); GList *mm_modem_messaging_list_sync (MMModemMessaging *self, GCancellable *cancellable, GError **error); void mm_modem_messaging_delete (MMModemMessaging *self, const gchar *sms, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); gboolean mm_modem_messaging_delete_finish (MMModemMessaging *self, GAsyncResult *res, GError **error); gboolean mm_modem_messaging_delete_sync (MMModemMessaging *self, const gchar *sms, GCancellable *cancellable, GError **error); G_END_DECLS #endif /* _MM_MODEM_MESSAGING_H_ */ ModemManager-1.23.4-dev/libmm-glib/mm-modem-oma.c000066400000000000000000000452571456466623000214030ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * libmm-glib -- Access modem status & information from glib applications * * 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 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 Street, Fifth Floor, * Boston, MA 02110-1301 USA. * * Copyright (C) 2013 Google, Inc. */ #include #include #include "mm-helpers.h" #include "mm-errors-types.h" #include "mm-modem-oma.h" #include "mm-common-helpers.h" /** * SECTION: mm-modem-oma * @title: MMModemOma * @short_description: The OMA interface * * The #MMModemOma is an object providing access to the methods, signals and * properties of the OMA interface. * * The OMA interface is exposed whenever a modem has OMA device management * capabilities. */ G_DEFINE_TYPE (MMModemOma, mm_modem_oma, MM_GDBUS_TYPE_MODEM_OMA_PROXY) struct _MMModemOmaPrivate { /* Common mutex to sync access */ GMutex mutex; PROPERTY_ARRAY_DECLARE (pending_network_initiated_sessions) }; /*****************************************************************************/ /** * mm_modem_oma_get_path: * @self: A #MMModemOma. * * Gets the DBus path of the #MMObject which implements this interface. * * Returns: (transfer none): The DBus path of the #MMObject object. * * Since: 1.2 */ const gchar * mm_modem_oma_get_path (MMModemOma *self) { g_return_val_if_fail (MM_IS_MODEM_OMA (self), NULL); RETURN_NON_EMPTY_CONSTANT_STRING ( g_dbus_proxy_get_object_path (G_DBUS_PROXY (self))); } /** * mm_modem_oma_dup_path: * @self: A #MMModemOma. * * Gets a copy of the DBus path of the #MMObject object which implements this * interface. * * Returns: (transfer full): The DBus path of the #MMObject. The returned value * should be freed with g_free(). * * Since: 1.2 */ gchar * mm_modem_oma_dup_path (MMModemOma *self) { gchar *value; g_return_val_if_fail (MM_IS_MODEM_OMA (self), NULL); g_object_get (G_OBJECT (self), "g-object-path", &value, NULL); RETURN_NON_EMPTY_STRING (value); } /*****************************************************************************/ /** * mm_modem_oma_setup_finish: * @self: A #MMModemOma. * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to * mm_modem_oma_setup(). * @error: Return location for error or %NULL. * * Finishes an operation started with mm_modem_oma_setup(). * * Returns: %TRUE if the setup was successful, %FALSE if @error is set. * * Since: 1.2 */ gboolean mm_modem_oma_setup_finish (MMModemOma *self, GAsyncResult *res, GError **error) { g_return_val_if_fail (MM_IS_MODEM_OMA (self), FALSE); return mm_gdbus_modem_oma_call_setup_finish (MM_GDBUS_MODEM_OMA (self), res, error); } /** * mm_modem_oma_setup: * @self: A #MMModemOma. * @features: Mask of #MMOmaFeature values to enable. * @cancellable: (allow-none): A #GCancellable or %NULL. * @callback: A #GAsyncReadyCallback to call when the request is satisfied or * %NULL. * @user_data: User data to pass to @callback. * * Asynchronously sets up the OMA device management service. * * When the operation is finished, @callback will be invoked in the * thread-default main loop * of the thread you are calling this method from. You can then call * mm_modem_oma_setup_finish() to get the result of the operation. * * See mm_modem_oma_setup_sync() for the synchronous, blocking version of this * method. * * Since: 1.2 */ void mm_modem_oma_setup (MMModemOma *self, MMOmaFeature features, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { g_return_if_fail (MM_IS_MODEM_OMA (self)); mm_gdbus_modem_oma_call_setup (MM_GDBUS_MODEM_OMA (self), features, cancellable, callback, user_data); } /** * mm_modem_oma_setup_sync: * @self: A #MMModemOma. * @features: Mask of #MMOmaFeature values to enable. * @cancellable: (allow-none): A #GCancellable or %NULL. * @error: Return location for error or %NULL. * * Synchronously sets up the OMA device management service. * * The calling thread is blocked until a reply is received. See * mm_modem_oma_setup() for the asynchronous version of this method. * * Returns: %TRUE if the setup was successful, %FALSE if @error is set. * * Since: 1.2 */ gboolean mm_modem_oma_setup_sync (MMModemOma *self, MMOmaFeature features, GCancellable *cancellable, GError **error) { g_return_val_if_fail (MM_IS_MODEM_OMA (self), FALSE); return mm_gdbus_modem_oma_call_setup_sync (MM_GDBUS_MODEM_OMA (self), features, cancellable, error); } /*****************************************************************************/ /** * mm_modem_oma_start_client_initiated_session_finish: * @self: A #MMModemOma. * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to * mm_modem_oma_start_client_initiated_session(). * @error: Return location for error or %NULL. * * Finishes an operation started with * mm_modem_oma_start_client_initiated_session(). * * Returns: %TRUE if the session was started, %FALSE if @error is set. * * Since: 1.2 */ gboolean mm_modem_oma_start_client_initiated_session_finish (MMModemOma *self, GAsyncResult *res, GError **error) { g_return_val_if_fail (MM_IS_MODEM_OMA (self), FALSE); return mm_gdbus_modem_oma_call_start_client_initiated_session_finish (MM_GDBUS_MODEM_OMA (self), res, error); } /** * mm_modem_oma_start_client_initiated_session: * @self: A #MMModemOma. * @session_type: A #MMOmaSessionType. * @cancellable: (allow-none): A #GCancellable or %NULL. * @callback: A #GAsyncReadyCallback to call when the request is satisfied or * %NULL. * @user_data: User data to pass to @callback. * * Asynchronously starts a client-initiated OMA device management session. * * When the operation is finished, @callback will be invoked in the * thread-default main loop * of the thread you are calling this method from. You can then call * mm_modem_oma_start_client_initiated_session_finish() to get the result of the * operation. * * See mm_modem_oma_start_client_initiated_session_sync() for the synchronous, * blocking version of this method. * * Since: 1.2 */ void mm_modem_oma_start_client_initiated_session (MMModemOma *self, MMOmaSessionType session_type, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { g_return_if_fail (MM_IS_MODEM_OMA (self)); mm_gdbus_modem_oma_call_start_client_initiated_session (MM_GDBUS_MODEM_OMA (self), session_type, cancellable, callback, user_data); } /** * mm_modem_oma_start_client_initiated_session_sync: * @self: A #MMModemOma. * @session_type: A #MMOmaSessionType. * @cancellable: (allow-none): A #GCancellable or %NULL. * @error: Return location for error or %NULL. * * Synchronously starts a client-initiated OMA device management session. * * The calling thread is blocked until a reply is received. See * mm_modem_oma_start_client_initiated_session() for the asynchronous version * of this method. * * Returns: %TRUE if the session was started, %FALSE if @error is set. * * Since: 1.2 */ gboolean mm_modem_oma_start_client_initiated_session_sync (MMModemOma *self, MMOmaSessionType session_type, GCancellable *cancellable, GError **error) { g_return_val_if_fail (MM_IS_MODEM_OMA (self), FALSE); return mm_gdbus_modem_oma_call_start_client_initiated_session_sync (MM_GDBUS_MODEM_OMA (self), session_type, cancellable, error); } /*****************************************************************************/ /** * mm_modem_oma_accept_network_initiated_session_finish: * @self: A #MMModemOma. * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to * mm_modem_oma_accept_network_initiated_session(). * @error: Return location for error or %NULL. * * Finishes an operation started with * mm_modem_oma_accept_network_initiated_session(). * * Returns: %TRUE if the session was started, %FALSE if @error is set. * * Since: 1.2 */ gboolean mm_modem_oma_accept_network_initiated_session_finish (MMModemOma *self, GAsyncResult *res, GError **error) { g_return_val_if_fail (MM_IS_MODEM_OMA (self), FALSE); return mm_gdbus_modem_oma_call_accept_network_initiated_session_finish (MM_GDBUS_MODEM_OMA (self), res, error); } /** * mm_modem_oma_accept_network_initiated_session: * @self: A #MMModemOma. * @session_id: The unique ID of the network-initiated session. * @accept: %TRUE if the session is to be accepted, %FALSE otherwise. * @cancellable: (allow-none): A #GCancellable or %NULL. * @callback: A #GAsyncReadyCallback to call when the request is satisfied or * %NULL. * @user_data: User data to pass to @callback. * * Asynchronously accepts a nework-initiated OMA device management session. * * When the operation is finished, @callback will be invoked in the * thread-default main loop * of the thread you are calling this method from. You can then call * mm_modem_oma_accept_network_initiated_session_finish() to get the result of * the operation. * * See mm_modem_oma_accept_network_initiated_session_sync() for the synchronous, * blocking version of this method. * * Since: 1.2 */ void mm_modem_oma_accept_network_initiated_session (MMModemOma *self, guint session_id, gboolean accept, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { g_return_if_fail (MM_IS_MODEM_OMA (self)); mm_gdbus_modem_oma_call_accept_network_initiated_session (MM_GDBUS_MODEM_OMA (self), session_id, accept, cancellable, callback, user_data); } /** * mm_modem_oma_accept_network_initiated_session_sync: * @self: A #MMModemOma. * @session_id: The unique ID of the network-initiated session. * @accept: %TRUE if the session is to be accepted, %FALSE otherwise. * @cancellable: (allow-none): A #GCancellable or %NULL. * @error: Return location for error or %NULL. * * Synchronously accepts a nework-initiated OMA device management session. * * The calling thread is blocked until a reply is received. See * mm_modem_oma_accept_network_initiated_session() for the asynchronous version * of this method. * * Returns: %TRUE if the session was started, %FALSE if @error is set. * * Since: 1.2 */ gboolean mm_modem_oma_accept_network_initiated_session_sync (MMModemOma *self, guint session_id, gboolean accept, GCancellable *cancellable, GError **error) { g_return_val_if_fail (MM_IS_MODEM_OMA (self), FALSE); return mm_gdbus_modem_oma_call_accept_network_initiated_session_sync (MM_GDBUS_MODEM_OMA (self), session_id, accept, cancellable, error); } /*****************************************************************************/ /** * mm_modem_oma_cancel_session_finish: * @self: A #MMModemOma. * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to * mm_modem_oma_cancel_session(). * @error: Return location for error or %NULL. * * Finishes an operation started with mm_modem_oma_cancel_session(). * * Returns: %TRUE if the session was started, %FALSE if @error is set. * * Since: 1.2 */ gboolean mm_modem_oma_cancel_session_finish (MMModemOma *self, GAsyncResult *res, GError **error) { g_return_val_if_fail (MM_IS_MODEM_OMA (self), FALSE); return mm_gdbus_modem_oma_call_cancel_session_finish (MM_GDBUS_MODEM_OMA (self), res, error); } /** * mm_modem_oma_cancel_session: * @self: A #MMModemOma. * @cancellable: (allow-none): A #GCancellable or %NULL. * @callback: A #GAsyncReadyCallback to call when the request is satisfied or * %NULL. * @user_data: User data to pass to @callback. * * Asynchronously cancels the current OMA device management session. * * When the operation is finished, @callback will be invoked in the * thread-default main loop * of the thread you are calling this method from. You can then call * mm_modem_oma_cancel_session_finish() to get the result of the operation. * * See mm_modem_oma_cancel_session_sync() for the synchronous, blocking version * of this method. * * Since: 1.2 */ void mm_modem_oma_cancel_session (MMModemOma *self, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { g_return_if_fail (MM_IS_MODEM_OMA (self)); mm_gdbus_modem_oma_call_cancel_session (MM_GDBUS_MODEM_OMA (self), cancellable, callback, user_data); } /** * mm_modem_oma_cancel_session_sync: * @self: A #MMModemOma. * @cancellable: (allow-none): A #GCancellable or %NULL. * @error: Return location for error or %NULL. * * Synchronously cancels the current OMA device management session. * * The calling thread is blocked until a reply is received. See * mm_modem_oma_cancel_session() for the asynchronous version of this method. * * Returns: %TRUE if the session was started, %FALSE if @error is set. * * Since: 1.2 */ gboolean mm_modem_oma_cancel_session_sync (MMModemOma *self, GCancellable *cancellable, GError **error) { g_return_val_if_fail (MM_IS_MODEM_OMA (self), FALSE); return mm_gdbus_modem_oma_call_cancel_session_sync (MM_GDBUS_MODEM_OMA (self), cancellable, error); } /*****************************************************************************/ /** * mm_modem_oma_get_features: * @self: A #MMModemOma. * * Gets the currently enabled OMA features. * * Returns: a bitmask of #MMOmaFeature values. * * Since: 1.2 */ MMOmaFeature mm_modem_oma_get_features (MMModemOma *self) { g_return_val_if_fail (MM_IS_MODEM_OMA (self), MM_OMA_FEATURE_NONE); return (MMOmaFeature) mm_gdbus_modem_oma_get_features (MM_GDBUS_MODEM_OMA (self)); } /*****************************************************************************/ /** * mm_modem_oma_get_session_type: * @self: A #MMModemOma. * * Gets the type of the current OMA device management session. * * Returns: a #MMOmaSessionType. * * Since: 1.2 */ MMOmaSessionType mm_modem_oma_get_session_type (MMModemOma *self) { g_return_val_if_fail (MM_IS_MODEM_OMA (self), MM_OMA_SESSION_TYPE_UNKNOWN); return (MMOmaSessionType) mm_gdbus_modem_oma_get_session_type (MM_GDBUS_MODEM_OMA (self)); } /*****************************************************************************/ /** * mm_modem_oma_get_session_state: * @self: A #MMModemOma. * * Gets the state of the current OMA device management session. * * Returns: a #MMOmaSessionState. * * Since: 1.2 */ MMOmaSessionState mm_modem_oma_get_session_state (MMModemOma *self) { g_return_val_if_fail (MM_IS_MODEM_OMA (self), MM_OMA_SESSION_STATE_UNKNOWN); return (MMOmaSessionState) mm_gdbus_modem_oma_get_session_state (MM_GDBUS_MODEM_OMA (self)); } /*****************************************************************************/ /** * mm_modem_oma_get_pending_network_initiated_sessions: * @self: A #MMModem. * @sessions: (out) (array length=n_sessions): Return location for the array of * #MMOmaPendingNetworkInitiatedSession structs. The returned array should be * freed with g_free() when no longer needed. * @n_sessions: (out): Return location for the number of values in @sessions. * * Gets the list of pending network-initiated OMA sessions. * * Returns: %TRUE if @sessions and @n_sessions are set, %FALSE otherwise. * * Since: 1.18 */ /** * mm_modem_oma_peek_pending_network_initiated_sessions: * @self: A #MMModem. * @sessions: (out) (array length=n_sessions): Return location for the array of * #MMOmaPendingNetworkInitiatedSession values. Do not free the returned array, * it is owned by @self. * @n_sessions: (out): Return location for the number of values in @sessions. * * Gets the list of pending network-initiated OMA sessions. * * Returns: %TRUE if @sessions and @n_sessions are set, %FALSE otherwise. * * Since: 1.18 */ PROPERTY_ARRAY_DEFINE (pending_network_initiated_sessions, ModemOma, modem_oma, MODEM_OMA, MMOmaPendingNetworkInitiatedSession, mm_common_oma_pending_network_initiated_sessions_variant_to_garray) /*****************************************************************************/ static void mm_modem_oma_init (MMModemOma *self) { self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, MM_TYPE_MODEM_OMA, MMModemOmaPrivate); g_mutex_init (&self->priv->mutex); PROPERTY_INITIALIZE (pending_network_initiated_sessions, "pending-network-initiated-sessions") } static void finalize (GObject *object) { MMModemOma *self = MM_MODEM_OMA (object); g_mutex_clear (&self->priv->mutex); PROPERTY_ARRAY_FINALIZE (pending_network_initiated_sessions) G_OBJECT_CLASS (mm_modem_oma_parent_class)->finalize (object); } static void mm_modem_oma_class_init (MMModemOmaClass *modem_class) { GObjectClass *object_class = G_OBJECT_CLASS (modem_class); g_type_class_add_private (object_class, sizeof (MMModemOmaPrivate)); object_class->finalize = finalize; } ModemManager-1.23.4-dev/libmm-glib/mm-modem-oma.h000066400000000000000000000154551456466623000214050ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * libmm-glib -- Access modem status & information from glib applications * * 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 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 Street, Fifth Floor, * Boston, MA 02110-1301 USA. * * Copyright (C) 2013 Google, Inc. */ #ifndef _MM_MODEM_OMA_H_ #define _MM_MODEM_OMA_H_ #if !defined (__LIBMM_GLIB_H_INSIDE__) && !defined (LIBMM_GLIB_COMPILATION) #error "Only can be included directly." #endif #include #include "mm-helper-types.h" #include "mm-gdbus-modem.h" G_BEGIN_DECLS #define MM_TYPE_MODEM_OMA (mm_modem_oma_get_type ()) #define MM_MODEM_OMA(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_MODEM_OMA, MMModemOma)) #define MM_MODEM_OMA_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_MODEM_OMA, MMModemOmaClass)) #define MM_IS_MODEM_OMA(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_MODEM_OMA)) #define MM_IS_MODEM_OMA_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), MM_TYPE_MODEM_OMA)) #define MM_MODEM_OMA_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_MODEM_OMA, MMModemOmaClass)) typedef struct _MMModemOma MMModemOma; typedef struct _MMModemOmaClass MMModemOmaClass; typedef struct _MMModemOmaPrivate MMModemOmaPrivate; /** * MMModemOma: * * The #MMModemOma structure contains private data and should only be accessed * using the provided API. */ struct _MMModemOma { /*< private >*/ MmGdbusModemOmaProxy parent; MMModemOmaPrivate *priv; }; struct _MMModemOmaClass { /*< private >*/ MmGdbusModemOmaProxyClass parent; }; GType mm_modem_oma_get_type (void); G_DEFINE_AUTOPTR_CLEANUP_FUNC (MMModemOma, g_object_unref) const gchar *mm_modem_oma_get_path (MMModemOma *self); gchar *mm_modem_oma_dup_path (MMModemOma *self); void mm_modem_oma_setup (MMModemOma *self, MMOmaFeature features, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); gboolean mm_modem_oma_setup_finish (MMModemOma *self, GAsyncResult *res, GError **error); gboolean mm_modem_oma_setup_sync (MMModemOma *self, MMOmaFeature features, GCancellable *cancellable, GError **error); void mm_modem_oma_start_client_initiated_session (MMModemOma *self, MMOmaSessionType session_type, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); gboolean mm_modem_oma_start_client_initiated_session_finish (MMModemOma *self, GAsyncResult *res, GError **error); gboolean mm_modem_oma_start_client_initiated_session_sync (MMModemOma *self, MMOmaSessionType session_type, GCancellable *cancellable, GError **error); void mm_modem_oma_accept_network_initiated_session (MMModemOma *self, guint session_id, gboolean accept, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); gboolean mm_modem_oma_accept_network_initiated_session_finish (MMModemOma *self, GAsyncResult *res, GError **error); gboolean mm_modem_oma_accept_network_initiated_session_sync (MMModemOma *self, guint session_id, gboolean accept, GCancellable *cancellable, GError **error); void mm_modem_oma_cancel_session (MMModemOma *self, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); gboolean mm_modem_oma_cancel_session_finish (MMModemOma *self, GAsyncResult *res, GError **error); gboolean mm_modem_oma_cancel_session_sync (MMModemOma *self, GCancellable *cancellable, GError **error); MMOmaFeature mm_modem_oma_get_features (MMModemOma *self); MMOmaSessionType mm_modem_oma_get_session_type (MMModemOma *self); MMOmaSessionState mm_modem_oma_get_session_state (MMModemOma *self); gboolean mm_modem_oma_peek_pending_network_initiated_sessions (MMModemOma *self, const MMOmaPendingNetworkInitiatedSession **sessions, guint *n_sessions); gboolean mm_modem_oma_get_pending_network_initiated_sessions (MMModemOma *self, MMOmaPendingNetworkInitiatedSession **sessions, guint *n_sessions); G_END_DECLS #endif /* _MM_MODEM_OMA_H_ */ ModemManager-1.23.4-dev/libmm-glib/mm-modem-sar.c000066400000000000000000000224361456466623000214060ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * libmm-glib -- Access modem status & information from glib applications * * 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 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 Street, Fifth Floor, * Boston, MA 02110-1301 USA. * * Copyright (C) 2021 Fibocom Wireless Inc. */ #include #include "mm-helpers.h" #include "mm-errors-types.h" #include "mm-modem-sar.h" #include "mm-common-helpers.h" /** * SECTION: mm-modem-sar * @title: MMModemSar * @short_description: The SAR interface * * The #MMModemSar is an object providing access to the methods, signals and * properties of the SAR interface. * * The SAR interface is exposed whenever a modem has SAR capabilities. */ G_DEFINE_TYPE (MMModemSar, mm_modem_sar, MM_GDBUS_TYPE_MODEM_SAR_PROXY) /*****************************************************************************/ /** * mm_modem_sar_get_path: * @self: A #MMModemSar. * * Gets the DBus path of the #MMObject which implements this interface. * * Returns: (transfer none): The DBus path of the #MMObject object. * * Since: 1.20 */ const gchar * mm_modem_sar_get_path (MMModemSar *self) { g_return_val_if_fail (MM_IS_MODEM_SAR (self), NULL); RETURN_NON_EMPTY_CONSTANT_STRING ( g_dbus_proxy_get_object_path (G_DBUS_PROXY (self))); } /** * mm_modem_sar_dup_path: * @self: A #MMModemSar. * * Gets a copy of the DBus path of the #MMObject object which implements this * interface. * * Returns: (transfer full): The DBus path of the #MMObject. The returned value * should be freed with g_free(). * * Since: 1.20 */ gchar * mm_modem_sar_dup_path (MMModemSar *self) { gchar *value; g_return_val_if_fail (MM_IS_MODEM_SAR (self), NULL); g_object_get (G_OBJECT (self), "g-object-path", &value, NULL); RETURN_NON_EMPTY_STRING (value); } /*****************************************************************************/ /** * mm_modem_sar_enable_finish: * @self: A #MMModemSar. * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to * mm_modem_sar_enable(). * @error: Return location for error or %NULL. * * Finishes an operation started with mm_modem_sar_enable(). * * Returns: %TRUE if the enable was successful, %FALSE if @error is set. * * Since: 1.20 */ gboolean mm_modem_sar_enable_finish (MMModemSar *self, GAsyncResult *res, GError **error) { g_return_val_if_fail (MM_IS_MODEM_SAR (self), FALSE); return mm_gdbus_modem_sar_call_enable_finish (MM_GDBUS_MODEM_SAR (self), res, error); } /** * mm_modem_sar_enable: * @self: A #MMModemSar. * @enable: %TRUE to enable dynamic SAR and %FALSE to disable it. * @cancellable: (allow-none): A #GCancellable or %NULL. * @callback: A #GAsyncReadyCallback to call when the request is satisfied or * %NULL. * @user_data: User data to pass to @callback. * * Asynchronously enable or disable dynamic SAR. * * When the operation is finished, @callback will be invoked in the * thread-default main loop * of the thread you are calling this method from. You can then call * mm_modem_sar_enable_finish() to get the result of the operation. * * See mm_modem_sar_enable_sync() for the synchronous, blocking version of * this method. * * Since: 1.20 */ void mm_modem_sar_enable (MMModemSar *self, gboolean enable, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { g_return_if_fail (MM_IS_MODEM_SAR (self)); mm_gdbus_modem_sar_call_enable (MM_GDBUS_MODEM_SAR (self), enable, cancellable, callback, user_data); } /** * mm_modem_sar_enable_sync: * @self: A #MMModemSar. * @enable: %TRUE to enable dynamic SAR and %FALSE to disable it. * @cancellable: (allow-none): A #GCancellable or %NULL. * @error: Return location for error or %NULL. * * Synchronously enable or disable dynamic SAR. * * The calling thread is blocked until a reply is received. See * mm_modem_sar_enable() for the asynchronous version of this method. * * Returns: %TRUE if the enable was successful, %FALSE if @error is set. * * Since: 1.20 */ gboolean mm_modem_sar_enable_sync (MMModemSar *self, gboolean enable, GCancellable *cancellable, GError **error) { g_return_val_if_fail (MM_IS_MODEM_SAR (self), FALSE); return mm_gdbus_modem_sar_call_enable_sync (MM_GDBUS_MODEM_SAR (self), enable, cancellable, error); } /*****************************************************************************/ /** * mm_modem_sar_power_level_finish: * @self: A #MMModemSar. * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to * mm_modem_sar_enable(). * @error: Return location for error or %NULL. * * Finishes an operation started with mm_modem_sar_set_power_level(). * * Returns: %TRUE if set power level was successful, %FALSE if @error is set. * * Since: 1.20 */ gboolean mm_modem_sar_set_power_level_finish (MMModemSar *self, GAsyncResult *res, GError **error) { g_return_val_if_fail (MM_IS_MODEM_SAR (self), FALSE); return mm_gdbus_modem_sar_call_set_power_level_finish (MM_GDBUS_MODEM_SAR (self), res, error); } /** * mm_modem_sar_set_power_level: * @self: A #MMModemSar. * @level: Index of the SAR power level mapping table * @cancellable: (allow-none): A #GCancellable or %NULL. * @callback: A #GAsyncReadyCallback to call when the request is satisfied or * %NULL. * @user_data: User data to pass to @callback. * * Asynchronously set current dynamic SAR power level. * * When the operation is finished, @callback will be invoked in the * thread-default main loop * of the thread you are calling this method from. You can then call * mm_modem_sar_set_power_level_finish() to get the result of the operation. * * See mm_modem_sar_set_power_level_sync() for the synchronous, blocking version of * this method. * * Since: 1.20 */ void mm_modem_sar_set_power_level (MMModemSar *self, guint level, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { g_return_if_fail (MM_IS_MODEM_SAR (self)); mm_gdbus_modem_sar_call_set_power_level (MM_GDBUS_MODEM_SAR (self), level, cancellable, callback, user_data); } /** * mm_modem_sar_set_power_level_sync: * @self: A #MMModemSar. * @level: Index of the SAR power level mapping table * @cancellable: (allow-none): A #GCancellable or %NULL. * @error: Return location for error or %NULL. * * Synchronously set current dynamic SAR power level. * * The calling thread is blocked until a reply is received. See * mm_modem_sar_set_power_level() for the asynchronous version of this method. * * Returns: %TRUE if set power level was successful, %FALSE if @error is set. * * Since: 1.20 */ gboolean mm_modem_sar_set_power_level_sync (MMModemSar *self, guint level, GCancellable *cancellable, GError **error) { g_return_val_if_fail (MM_IS_MODEM_SAR (self), FALSE); return mm_gdbus_modem_sar_call_set_power_level_sync (MM_GDBUS_MODEM_SAR (self), level, cancellable, error); } /*****************************************************************************/ /** * mm_modem_sar_get_state: * @self: A #MMModem. * * Gets the state of dynamic SAR. * * Returns: %TRUE if dynamic SAR is enabled, %FALSE otherwise. * * Since: 1.20 */ gboolean mm_modem_sar_get_state (MMModemSar *self) { g_return_val_if_fail (MM_IS_MODEM_SAR (self), FALSE); return mm_gdbus_modem_sar_get_state (MM_GDBUS_MODEM_SAR (self)); } /*****************************************************************************/ /** * mm_modem_sar_get_power_level: * @self: A #MMModem. * * Gets the index of the SAR power level mapping table. * * Returns: the index. * * Since: 1.20 */ guint mm_modem_sar_get_power_level (MMModemSar *self) { g_return_val_if_fail (MM_IS_MODEM_SAR (self), FALSE); return mm_gdbus_modem_sar_get_power_level (MM_GDBUS_MODEM_SAR (self)); } /*****************************************************************************/ static void mm_modem_sar_init (MMModemSar *self) { } static void mm_modem_sar_class_init (MMModemSarClass *modem_class) { /* Virtual methods */ } ModemManager-1.23.4-dev/libmm-glib/mm-modem-sar.h000066400000000000000000000101341456466623000214030ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * libmm-glib -- Access modem status & information from glib applications * * 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 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 Street, Fifth Floor, * Boston, MA 02110-1301 USA. * * Copyright (C) 2021 Fibocom Wireless Inc. */ #ifndef _MM_MODEM_SAR_H_ #define _MM_MODEM_SAR_H_ #if !defined (__LIBMM_GLIB_H_INSIDE__) && !defined (LIBMM_GLIB_COMPILATION) #error "Only can be included directly." #endif #include #include "mm-gdbus-modem.h" G_BEGIN_DECLS #define MM_TYPE_MODEM_SAR (mm_modem_sar_get_type ()) #define MM_MODEM_SAR(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_MODEM_SAR, MMModemSar)) #define MM_MODEM_SAR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_MODEM_SAR, MMModemSarClass)) #define MM_IS_MODEM_SAR(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_MODEM_SAR)) #define MM_IS_MODEM_SAR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), MM_TYPE_MODEM_SAR)) #define MM_MODEM_SAR_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_MODEM_SAR, MMModemSarClass)) typedef struct _MMModemSar MMModemSar; typedef struct _MMModemSarClass MMModemSarClass; /** * MMModemSar: * * The #MMModemSar structure contains private data and should only be accessed * using the provided API. */ struct _MMModemSar { /*< private >*/ MmGdbusModemSarProxy parent; gpointer unused; }; struct _MMModemSarClass { /*< private >*/ MmGdbusModemSarProxyClass parent; }; GType mm_modem_sar_get_type (void); G_DEFINE_AUTOPTR_CLEANUP_FUNC (MMModemSar, g_object_unref) const gchar *mm_modem_sar_get_path (MMModemSar *self); gchar *mm_modem_sar_dup_path (MMModemSar *self); void mm_modem_sar_enable (MMModemSar *self, gboolean enable, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); gboolean mm_modem_sar_enable_finish (MMModemSar *self, GAsyncResult *res, GError **error); gboolean mm_modem_sar_enable_sync (MMModemSar *self, gboolean enable, GCancellable *cancellable, GError **error); void mm_modem_sar_set_power_level (MMModemSar *self, guint level, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); gboolean mm_modem_sar_set_power_level_finish (MMModemSar *self, GAsyncResult *res, GError **error); gboolean mm_modem_sar_set_power_level_sync (MMModemSar *self, guint level, GCancellable *cancellable, GError **error); gboolean mm_modem_sar_get_state (MMModemSar *self); guint mm_modem_sar_get_power_level (MMModemSar *self); G_END_DECLS #endif /* _MM_MODEM_SAR_H_ */ ModemManager-1.23.4-dev/libmm-glib/mm-modem-signal.c000066400000000000000000000473171456466623000221030ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * libmm-glib -- Access modem status & information from glib applications * * 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 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 Street, Fifth Floor, * Boston, MA 02110-1301 USA. * * Copyright (C) 2012 Google, Inc. * Copyright (C) 2012 Lanedo GmbH * Copyright (C) 2013-2021 Aleksander Morgado * Copyright (C) 2021 Intel Corporation */ #include #include "mm-helpers.h" #include "mm-errors-types.h" #include "mm-modem-signal.h" /** * SECTION: mm-modem-signal * @title: MMModemSignal * @short_description: The extended Signal interface * * The #MMModemSignal is an object providing access to the methods, signals and * properties of the Signal interface. * * The Signal interface is exposed whenever a modem has extended signal * retrieval capabilities. */ G_DEFINE_TYPE (MMModemSignal, mm_modem_signal, MM_GDBUS_TYPE_MODEM_SIGNAL_PROXY) struct _MMModemSignalPrivate { /* Common mutex to sync access */ GMutex mutex; PROPERTY_OBJECT_DECLARE (cdma, MMSignal) PROPERTY_OBJECT_DECLARE (evdo, MMSignal) PROPERTY_OBJECT_DECLARE (gsm, MMSignal) PROPERTY_OBJECT_DECLARE (umts, MMSignal) PROPERTY_OBJECT_DECLARE (lte, MMSignal) PROPERTY_OBJECT_DECLARE (nr5g, MMSignal) }; /*****************************************************************************/ /** * mm_modem_signal_get_path: * @self: A #MMModemSignal. * * Gets the DBus path of the #MMObject which implements this interface. * * Returns: (transfer none): The DBus path of the #MMObject object. * * Since: 1.2 */ const gchar * mm_modem_signal_get_path (MMModemSignal *self) { g_return_val_if_fail (MM_IS_MODEM_SIGNAL (self), NULL); RETURN_NON_EMPTY_CONSTANT_STRING ( g_dbus_proxy_get_object_path (G_DBUS_PROXY (self))); } /** * mm_modem_signal_dup_path: * @self: A #MMModemSignal. * * Gets a copy of the DBus path of the #MMObject object which implements this * interface. * * Returns: (transfer full): The DBus path of the #MMObject. The returned value * should be freed with g_free(). * * Since: 1.2 */ gchar * mm_modem_signal_dup_path (MMModemSignal *self) { gchar *value; g_return_val_if_fail (MM_IS_MODEM_SIGNAL (self), NULL); g_object_get (G_OBJECT (self), "g-object-path", &value, NULL); RETURN_NON_EMPTY_STRING (value); } /*****************************************************************************/ /** * mm_modem_signal_setup_finish: * @self: A #MMModemSignal. * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to * mm_modem_signal_setup(). * @error: Return location for error or %NULL. * * Finishes an operation started with mm_modem_signal_setup(). * * Returns: %TRUE if the setup was successful, %FALSE if @error is set. * * Since: 1.2 */ gboolean mm_modem_signal_setup_finish (MMModemSignal *self, GAsyncResult *res, GError **error) { g_return_val_if_fail (MM_IS_MODEM_SIGNAL (self), FALSE); return mm_gdbus_modem_signal_call_setup_finish (MM_GDBUS_MODEM_SIGNAL (self), res, error); } /** * mm_modem_signal_setup: * @self: A #MMModemSignal. * @rate: Refresh rate to set, in seconds. Use 0 to disable periodic polling. * @cancellable: (allow-none): A #GCancellable or %NULL. * @callback: A #GAsyncReadyCallback to call when the request is satisfied or * %NULL. * @user_data: User data to pass to @callback. * * Asynchronously enables or disables the extended signal quality information * retrieval via periodic polling. * * When the operation is finished, @callback will be invoked in the * thread-default main loop * of the thread you are calling this method from. You can then call * mm_modem_signal_setup_finish() to get the result of the operation. * * See mm_modem_signal_setup_sync() for the synchronous, blocking version of * this method. * * Since: 1.2 */ void mm_modem_signal_setup (MMModemSignal *self, guint rate, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { g_return_if_fail (MM_IS_MODEM_SIGNAL (self)); mm_gdbus_modem_signal_call_setup (MM_GDBUS_MODEM_SIGNAL (self), rate, cancellable, callback, user_data); } /** * mm_modem_signal_setup_sync: * @self: A #MMModemSignal. * @rate: Refresh rate to set, in seconds. Use 0 to disable periodic polling. * @cancellable: (allow-none): A #GCancellable or %NULL. * @error: Return location for error or %NULL. * * Synchronously enables or disables the extended signal quality information * retrieval via periodic polling. * * The calling thread is blocked until a reply is received. See * mm_modem_signal_setup() for the asynchronous version of this method. * * Returns: %TRUE if the setup was successful, %FALSE if @error is set. * * Since: 1.2 */ gboolean mm_modem_signal_setup_sync (MMModemSignal *self, guint rate, GCancellable *cancellable, GError **error) { g_return_val_if_fail (MM_IS_MODEM_SIGNAL (self), FALSE); return mm_gdbus_modem_signal_call_setup_sync (MM_GDBUS_MODEM_SIGNAL (self), rate, cancellable, error); } /*****************************************************************************/ /** * mm_modem_signal_setup_thresholds_finish: * @self: A #MMModemSignal. * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to * mm_modem_signal_setup_thresholds(). * @error: Return location for error or %NULL. * * Finishes an operation started with mm_modem_signal_setup_thresholds(). * * Returns: %TRUE if the setup was successful, %FALSE if @error is set. * * Since: 1.20 */ gboolean mm_modem_signal_setup_thresholds_finish (MMModemSignal *self, GAsyncResult *res, GError **error) { g_return_val_if_fail (MM_IS_MODEM_SIGNAL (self), FALSE); return mm_gdbus_modem_signal_call_setup_thresholds_finish (MM_GDBUS_MODEM_SIGNAL (self), res, error); } /** * mm_modem_signal_setup_thresholds: * @self: A #MMModemSignal. * @properties: Threshold values to set. * @cancellable: (allow-none): A #GCancellable or %NULL. * @callback: A #GAsyncReadyCallback to call when the request is satisfied or * %NULL. * @user_data: User data to pass to @callback. * * Asynchronously setups thresholds so that the device itself decides when to report the * extended signal quality information updates. * * When the operation is finished, @callback will be invoked in the * thread-default main loop * of the thread you are calling this method from. You can then call * mm_modem_signal_setup_thresholds_finish() to get the result of the operation. * * See mm_modem_signal_setup_thresholds_sync() for the synchronous, blocking version of * this method. * * Since: 1.20 */ void mm_modem_signal_setup_thresholds (MMModemSignal *self, MMSignalThresholdProperties *properties, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { g_autoptr(GVariant) dictionary = NULL; g_return_if_fail (MM_IS_MODEM_SIGNAL (self)); dictionary = mm_signal_threshold_properties_get_dictionary (properties); mm_gdbus_modem_signal_call_setup_thresholds (MM_GDBUS_MODEM_SIGNAL (self), dictionary, cancellable, callback, user_data); } /** * mm_modem_signal_setup_thresholds_sync: * @self: A #MMModemSignal. * @properties: Threshold values to set. * @cancellable: (allow-none): A #GCancellable or %NULL. * @error: Return location for error or %NULL. * * Synchronously setups thresholds so that the device itself decides when to report the * extended signal quality information updates. * * The calling thread is blocked until a reply is received. See * mm_modem_signal_setup_thresholds() for the asynchronous version of this method. * * Returns: %TRUE if the setup was successful, %FALSE if @error is set. * * Since: 1.20 */ gboolean mm_modem_signal_setup_thresholds_sync (MMModemSignal *self, MMSignalThresholdProperties *properties, GCancellable *cancellable, GError **error) { g_autoptr(GVariant) dictionary = NULL; g_return_val_if_fail (MM_IS_MODEM_SIGNAL (self), FALSE); dictionary = mm_signal_threshold_properties_get_dictionary (properties); return mm_gdbus_modem_signal_call_setup_thresholds_sync (MM_GDBUS_MODEM_SIGNAL (self), dictionary, cancellable, error); } /*****************************************************************************/ /** * mm_modem_signal_get_rate: * @self: A #MMModemSignal. * * Gets the currently configured refresh rate. * * Returns: the refresh rate, in seconds. * * Since: 1.2 */ guint mm_modem_signal_get_rate (MMModemSignal *self) { g_return_val_if_fail (MM_IS_MODEM_SIGNAL (self), 0); return mm_gdbus_modem_signal_get_rate (MM_GDBUS_MODEM_SIGNAL (self)); } /*****************************************************************************/ /** * mm_modem_signal_get_rssi_threshold: * @self: A #MMModemSignal. * * Gets the currently configured RSSI threshold, in dBm. * * A value of 0 indicates the threshold is disabled. * * Returns: the RSSI threshold. * * Since: 1.20 */ guint mm_modem_signal_get_rssi_threshold (MMModemSignal *self) { g_return_val_if_fail (MM_IS_MODEM_SIGNAL (self), 0); return mm_gdbus_modem_signal_get_rssi_threshold (MM_GDBUS_MODEM_SIGNAL (self)); } /*****************************************************************************/ /** * mm_modem_signal_get_error_rate_threshold: * @self: A #MMModemSignal. * * Gets whether the error rate threshold is enabled or not. * * Returns: %TRUE if the error rate threshold is enabled, %FALSE otherwise. * * Since: 1.20 */ gboolean mm_modem_signal_get_error_rate_threshold (MMModemSignal *self) { g_return_val_if_fail (MM_IS_MODEM_SIGNAL (self), FALSE); return mm_gdbus_modem_signal_get_error_rate_threshold (MM_GDBUS_MODEM_SIGNAL (self)); } /*****************************************************************************/ /** * mm_modem_signal_get_cdma: * @self: A #MMModem. * * Gets a #MMSignal object specifying the CDMA signal information. * * The values reported by @self are not updated when the values in the * interface change. Instead, the client is expected to call * mm_modem_signal_get_cdma() again to get a new #MMSignal with the new values. * * * Returns: (transfer full): A #MMSignal that must be freed with * g_object_unref() or %NULL if unknown. * * Since: 1.2 */ /** * mm_modem_signal_peek_cdma: * @self: A #MMModem. * * Gets a #MMSignal object specifying the CDMA signal information. * * The returned value is only valid until the property changes so it is * only safe to use this function on the thread where @self was constructed. Use * mm_modem_signal_get_cdma() if on another thread. * * Returns: (transfer none): A #MMSignal. Do not free the returned value, it * belongs to @self. * * Since: 1.2 */ PROPERTY_OBJECT_DEFINE_FAILABLE (cdma, ModemSignal, modem_signal, MODEM_SIGNAL, MMSignal, mm_signal_new_from_dictionary) /*****************************************************************************/ /** * mm_modem_signal_get_evdo: * @self: A #MMModem. * * Gets a #MMSignal object specifying the EV-DO signal information. * * The values reported by @self are not updated when the values in the * interface change. Instead, the client is expected to call * mm_modem_signal_get_evdo() again to get a new #MMSignal with the new values. * * * Returns: (transfer full): A #MMSignal that must be freed with * g_object_unref() or %NULL if unknown. * * Since: 1.2 */ /** * mm_modem_signal_peek_evdo: * @self: A #MMModem. * * Gets a #MMSignal object specifying the EV-DO signal information. * * The returned value is only valid until the property changes so it is * only safe to use this function on the thread where @self was constructed. Use * mm_modem_signal_get_evdo() if on another thread. * * Returns: (transfer none): A #MMSignal. Do not free the returned value, it * belongs to @self. * * Since: 1.2 */ PROPERTY_OBJECT_DEFINE_FAILABLE (evdo, ModemSignal, modem_signal, MODEM_SIGNAL, MMSignal, mm_signal_new_from_dictionary) /*****************************************************************************/ /** * mm_modem_signal_get_gsm: * @self: A #MMModem. * * Gets a #MMSignal object specifying the GSM signal information. * * The values reported by @self are not updated when the values in the * interface change. Instead, the client is expected to call * mm_modem_signal_get_gsm() again to get a new #MMSignal with the * new values. * * Returns: (transfer full): A #MMSignal that must be freed with * g_object_unref() or %NULL if unknown. * * Since: 1.2 */ /** * mm_modem_signal_peek_gsm: * @self: A #MMModem. * * Gets a #MMSignal object specifying the GSM signal information. * * The returned value is only valid until the property changes so it is * only safe to use this function on the thread where @self was constructed. Use * mm_modem_signal_get_gsm() if on another thread. * * Returns: (transfer none): A #MMSignal. Do not free the returned value, it * belongs to @self. * * Since: 1.2 */ PROPERTY_OBJECT_DEFINE_FAILABLE (gsm, ModemSignal, modem_signal, MODEM_SIGNAL, MMSignal, mm_signal_new_from_dictionary) /*****************************************************************************/ /** * mm_modem_signal_get_umts: * @self: A #MMModem. * * Gets a #MMSignal object specifying the UMTS signal information. * * The values reported by @self are not updated when the values in the * interface change. Instead, the client is expected to call * mm_modem_signal_get_umts() again to get a new #MMSignal with the new values. * * * Returns: (transfer full): A #MMSignal that must be freed with * g_object_unref() or %NULL if unknown. * * Since: 1.2 */ /** * mm_modem_signal_peek_umts: * @self: A #MMModem. * * Gets a #MMSignal object specifying the UMTS signal information. * * The returned value is only valid until the property changes so it is * only safe to use this function on the thread where @self was constructed. Use * mm_modem_signal_get_umts() if on another thread. * * Returns: (transfer none): A #MMSignal. Do not free the returned value, it * belongs to @self. * * Since: 1.2 */ PROPERTY_OBJECT_DEFINE_FAILABLE (umts, ModemSignal, modem_signal, MODEM_SIGNAL, MMSignal, mm_signal_new_from_dictionary) /*****************************************************************************/ /** * mm_modem_signal_get_lte: * @self: A #MMModem. * * Gets a #MMSignal object specifying the LTE signal information. * * The values reported by @self are not updated when the values in the * interface change. Instead, the client is expected to call * mm_modem_signal_get_lte() again to get a new #MMSignal with the new values. * * * Returns: (transfer full): A #MMSignal that must be freed with * g_object_unref() or %NULL if unknown. * * Since: 1.2 */ /** * mm_modem_signal_peek_lte: * @self: A #MMModem. * * Gets a #MMSignal object specifying the LTE signal information. * * The returned value is only valid until the property changes so it is * only safe to use this function on the thread where @self was constructed. Use * mm_modem_signal_get_lte() if on another thread. * * Returns: (transfer none): A #MMSignal. Do not free the returned value, it * belongs to @self. * * Since: 1.2 */ PROPERTY_OBJECT_DEFINE_FAILABLE (lte, ModemSignal, modem_signal, MODEM_SIGNAL, MMSignal, mm_signal_new_from_dictionary) /*****************************************************************************/ /** * mm_modem_signal_get_nr5g: * @self: A #MMModem. * * Gets a #MMSignal object specifying the 5G signal information. * * The values reported by @self are not updated when the values in the * interface change. Instead, the client is expected to call * mm_modem_signal_get_nr5g() again to get a new #MMSignal with the new values. * * * Returns: (transfer full): A #MMSignal that must be freed with * g_object_unref() or %NULL if unknown. * * Since: 1.16 */ /** * mm_modem_signal_peek_nr5g: * @self: A #MMModem. * * Gets a #MMSignal object specifying the 5G signal information. * * The returned value is only valid until the property changes so it is * only safe to use this function on the thread where @self was constructed. Use * mm_modem_signal_get_nr5g() if on another thread. * * Returns: (transfer none): A #MMSignal. Do not free the returned value, it * belongs to @self. * * Since: 1.16 */ PROPERTY_OBJECT_DEFINE_FAILABLE (nr5g, ModemSignal, modem_signal, MODEM_SIGNAL, MMSignal, mm_signal_new_from_dictionary) /*****************************************************************************/ static void mm_modem_signal_init (MMModemSignal *self) { self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, MM_TYPE_MODEM_SIGNAL, MMModemSignalPrivate); g_mutex_init (&self->priv->mutex); PROPERTY_INITIALIZE (cdma, "cdma") PROPERTY_INITIALIZE (evdo, "evdo") PROPERTY_INITIALIZE (gsm, "gsm") PROPERTY_INITIALIZE (umts, "umts") PROPERTY_INITIALIZE (lte, "lte") PROPERTY_INITIALIZE (nr5g, "nr5g") } static void finalize (GObject *object) { MMModemSignal *self = MM_MODEM_SIGNAL (object); g_mutex_clear (&self->priv->mutex); PROPERTY_OBJECT_FINALIZE (cdma) PROPERTY_OBJECT_FINALIZE (evdo) PROPERTY_OBJECT_FINALIZE (gsm) PROPERTY_OBJECT_FINALIZE (umts) PROPERTY_OBJECT_FINALIZE (lte) PROPERTY_OBJECT_FINALIZE (nr5g) G_OBJECT_CLASS (mm_modem_signal_parent_class)->finalize (object); } static void mm_modem_signal_class_init (MMModemSignalClass *modem_class) { GObjectClass *object_class = G_OBJECT_CLASS (modem_class); g_type_class_add_private (object_class, sizeof (MMModemSignalPrivate)); object_class->finalize = finalize; } ModemManager-1.23.4-dev/libmm-glib/mm-modem-signal.h000066400000000000000000000133411456466623000220760ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * libmm-glib -- Access modem status & information from glib applications * * 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 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 Street, Fifth Floor, * Boston, MA 02110-1301 USA. * * Copyright (C) 2013-2021 Aleksander Morgado * Copyright (C) 2021 Intel Corporation */ #ifndef _MM_MODEM_SIGNAL_H_ #define _MM_MODEM_SIGNAL_H_ #if !defined (__LIBMM_GLIB_H_INSIDE__) && !defined (LIBMM_GLIB_COMPILATION) #error "Only can be included directly." #endif #include #include "mm-signal.h" #include "mm-signal-threshold-properties.h" #include "mm-gdbus-modem.h" G_BEGIN_DECLS #define MM_TYPE_MODEM_SIGNAL (mm_modem_signal_get_type ()) #define MM_MODEM_SIGNAL(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_MODEM_SIGNAL, MMModemSignal)) #define MM_MODEM_SIGNAL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_MODEM_SIGNAL, MMModemSignalClass)) #define MM_IS_MODEM_SIGNAL(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_MODEM_SIGNAL)) #define MM_IS_MODEM_SIGNAL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), MM_TYPE_MODEM_SIGNAL)) #define MM_MODEM_SIGNAL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_MODEM_SIGNAL, MMModemSignalClass)) typedef struct _MMModemSignal MMModemSignal; typedef struct _MMModemSignalClass MMModemSignalClass; typedef struct _MMModemSignalPrivate MMModemSignalPrivate; /** * MMModemSignal: * * The #MMModemSignal structure contains private data and should only be accessed * using the provided API. */ struct _MMModemSignal { /*< private >*/ MmGdbusModemSignalProxy parent; MMModemSignalPrivate *priv; }; struct _MMModemSignalClass { /*< private >*/ MmGdbusModemSignalProxyClass parent; }; GType mm_modem_signal_get_type (void); G_DEFINE_AUTOPTR_CLEANUP_FUNC (MMModemSignal, g_object_unref) const gchar *mm_modem_signal_get_path (MMModemSignal *self); gchar *mm_modem_signal_dup_path (MMModemSignal *self); guint mm_modem_signal_get_rate (MMModemSignal *self); guint mm_modem_signal_get_rssi_threshold (MMModemSignal *self); gboolean mm_modem_signal_get_error_rate_threshold (MMModemSignal *self); void mm_modem_signal_setup (MMModemSignal *self, guint rate, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); gboolean mm_modem_signal_setup_finish (MMModemSignal *self, GAsyncResult *res, GError **error); gboolean mm_modem_signal_setup_sync (MMModemSignal *self, guint rate, GCancellable *cancellable, GError **error); void mm_modem_signal_setup_thresholds (MMModemSignal *self, MMSignalThresholdProperties *properties, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); gboolean mm_modem_signal_setup_thresholds_finish (MMModemSignal *self, GAsyncResult *res, GError **error); gboolean mm_modem_signal_setup_thresholds_sync (MMModemSignal *self, MMSignalThresholdProperties *properties, GCancellable *cancellable, GError **error); MMSignal *mm_modem_signal_get_cdma (MMModemSignal *self); MMSignal *mm_modem_signal_peek_cdma (MMModemSignal *self); MMSignal *mm_modem_signal_get_evdo (MMModemSignal *self); MMSignal *mm_modem_signal_peek_evdo (MMModemSignal *self); MMSignal *mm_modem_signal_get_gsm (MMModemSignal *self); MMSignal *mm_modem_signal_peek_gsm (MMModemSignal *self); MMSignal *mm_modem_signal_get_umts (MMModemSignal *self); MMSignal *mm_modem_signal_peek_umts (MMModemSignal *self); MMSignal *mm_modem_signal_get_lte (MMModemSignal *self); MMSignal *mm_modem_signal_peek_lte (MMModemSignal *self); MMSignal *mm_modem_signal_get_nr5g (MMModemSignal *self); MMSignal *mm_modem_signal_peek_nr5g (MMModemSignal *self); G_END_DECLS #endif /* _MM_MODEM_SIGNAL_H_ */ ModemManager-1.23.4-dev/libmm-glib/mm-modem-simple.c000066400000000000000000000373721456466623000221170ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * libmm-glib -- Access modem status & information from glib applications * * 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 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 Street, Fifth Floor, * Boston, MA 02110-1301 USA. * * Copyright (C) 2011 - 2012 Aleksander Morgado * Copyright (C) 2012 Google, Inc. */ #include #include "mm-helpers.h" #include "mm-errors-types.h" #include "mm-modem-simple.h" /** * SECTION: mm-modem-simple * @title: MMModemSimple * @short_description: The Simple interface * * The #MMModemSimple is an object providing access to the methods, signals and * properties of the Simple interface. * * The Simple interface is exposed on modems which are not in * %MM_MODEM_STATE_FAILED state. */ G_DEFINE_TYPE (MMModemSimple, mm_modem_simple, MM_GDBUS_TYPE_MODEM_SIMPLE_PROXY) /*****************************************************************************/ /** * mm_modem_simple_get_path: * @self: A #MMModemSimple. * * Gets the DBus path of the #MMObject which implements this interface. * * Returns: (transfer none): The DBus path of the #MMObject object. * * Since: 1.0 */ const gchar * mm_modem_simple_get_path (MMModemSimple *self) { g_return_val_if_fail (MM_IS_MODEM_SIMPLE (self), NULL); RETURN_NON_EMPTY_CONSTANT_STRING ( g_dbus_proxy_get_object_path (G_DBUS_PROXY (self))); } /** * mm_modem_simple_dup_path: * @self: A #MMModemSimple. * * Gets a copy of the DBus path of the #MMObject object which implements this * interface. * * Returns: (transfer full): The DBus path of the #MMObject. The returned value * should be freed with g_free(). * * Since: 1.0 */ gchar * mm_modem_simple_dup_path (MMModemSimple *self) { gchar *value; g_return_val_if_fail (MM_IS_MODEM_SIMPLE (self), NULL); g_object_get (G_OBJECT (self), "g-object-path", &value, NULL); RETURN_NON_EMPTY_STRING (value); } /*****************************************************************************/ /** * mm_modem_simple_connect_finish: * @self: A #MMModemSimple. * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to * mm_modem_simple_connect(). * @error: Return location for error or %NULL. * * Finishes an operation started with mm_modem_simple_connect(). * * Returns: (transfer full): A #MMBearer, or %FALSE if @error is set. The * returned value must be freed with g_object_unref(). * * Since: 1.0 */ MMBearer * mm_modem_simple_connect_finish (MMModemSimple *self, GAsyncResult *res, GError **error) { g_return_val_if_fail (MM_IS_MODEM_SIMPLE (self), NULL); return g_task_propagate_pointer (G_TASK (res), error); } static void new_bearer_ready (GDBusConnection *connection, GAsyncResult *res, GTask *task) { GError *error = NULL; GObject *bearer; GObject *source_object; source_object = g_async_result_get_source_object (res); bearer = g_async_initable_new_finish (G_ASYNC_INITABLE (source_object), res, &error); g_object_unref (source_object); if (error) g_task_return_error (task, error); else g_task_return_pointer (task, bearer, g_object_unref); g_object_unref (task); } static void simple_connect_ready (MMModemSimple *self, GAsyncResult *res, GTask *task) { GError *error = NULL; gchar *bearer_path = NULL; if (!mm_gdbus_modem_simple_call_connect_finish (MM_GDBUS_MODEM_SIMPLE (self), &bearer_path, res, &error)) { g_task_return_error (task, error); g_object_unref (task); return; } g_async_initable_new_async (MM_TYPE_BEARER, G_PRIORITY_DEFAULT, g_task_get_cancellable (task), (GAsyncReadyCallback)new_bearer_ready, task, "g-flags", G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START, "g-name", MM_DBUS_SERVICE, "g-connection", g_dbus_proxy_get_connection (G_DBUS_PROXY (self)), "g-object-path", bearer_path, "g-interface-name", "org.freedesktop.ModemManager1.Bearer", NULL); g_free (bearer_path); } /** * mm_modem_simple_connect: * @self: A #MMModemSimple. * @properties: (transfer none): A #MMSimpleConnectProperties bundle. * @cancellable: (allow-none): A #GCancellable or %NULL. * @callback: A #GAsyncReadyCallback to call when the request is satisfied or * %NULL. * @user_data: User data to pass to @callback. * * Asynchronously requests to connect the modem using the given @properties. * * When the operation is finished, @callback will be invoked in the * thread-default main loop * of the thread you are calling this method from. You can then call * mm_modem_simple_connect_finish() to get the result of the operation. * * See mm_modem_simple_connect_sync() for the synchronous, blocking version of * this method. * * Since: 1.0 */ void mm_modem_simple_connect (MMModemSimple *self, MMSimpleConnectProperties *properties, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; GVariant *variant; g_return_if_fail (MM_IS_MODEM_SIMPLE (self)); task = g_task_new (self, cancellable, callback, user_data); variant = mm_simple_connect_properties_get_dictionary (properties); mm_gdbus_modem_simple_call_connect ( MM_GDBUS_MODEM_SIMPLE (self), variant, cancellable, (GAsyncReadyCallback)simple_connect_ready, task); g_variant_unref (variant); } /** * mm_modem_simple_connect_sync: * @self: A #MMModemSimple. * @properties: (transfer none): A #MMSimpleConnectProperties bundle. * @cancellable: (allow-none): A #GCancellable or %NULL. * @error: Return location for error or %NULL. * * Synchronously requests to connect the modem using the given @properties. * * The calling thread is blocked until a reply is received. See * mm_modem_simple_connect() for the asynchronous version of this method. * * Returns: (transfer full): A #MMBearer, or %FALSE if @error is set. The * returned value must be freed with g_object_unref(). * * Since: 1.0 */ MMBearer * mm_modem_simple_connect_sync (MMModemSimple *self, MMSimpleConnectProperties *properties, GCancellable *cancellable, GError **error) { GObject *bearer = NULL; gchar *bearer_path = NULL; GVariant *variant; g_return_val_if_fail (MM_IS_MODEM_SIMPLE (self), NULL); variant = mm_simple_connect_properties_get_dictionary (properties); mm_gdbus_modem_simple_call_connect_sync (MM_GDBUS_MODEM_SIMPLE (self), variant, &bearer_path, cancellable, error); if (bearer_path) { bearer = g_initable_new (MM_TYPE_BEARER, cancellable, error, "g-flags", G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START, "g-name", MM_DBUS_SERVICE, "g-connection", g_dbus_proxy_get_connection (G_DBUS_PROXY (self)), "g-object-path", bearer_path, "g-interface-name", "org.freedesktop.ModemManager1.Bearer", NULL); g_free (bearer_path); } g_variant_unref (variant); return (bearer ? MM_BEARER (bearer) : NULL); } /*****************************************************************************/ /** * mm_modem_simple_disconnect_finish: * @self: A #MMModemSimple. * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to * mm_modem_simple_disconnect(). * @error: Return location for error or %NULL. * * Finishes an operation started with mm_modem_simple_disconnect(). * * Returns: %TRUE if the modem is successfully disconnected, %FALSE if @error is * set. * * Since: 1.0 */ gboolean mm_modem_simple_disconnect_finish (MMModemSimple *self, GAsyncResult *res, GError **error) { g_return_val_if_fail (MM_IS_MODEM_SIMPLE (self), FALSE); return mm_gdbus_modem_simple_call_disconnect_finish (MM_GDBUS_MODEM_SIMPLE (self), res, error); } /** * mm_modem_simple_disconnect: * @self: A #MMModemSimple. * @bearer: (allow-none): Path of the bearer to disconnect, or %NULL to * disconnect all connected bearers. * @cancellable: (allow-none): A #GCancellable or %NULL. * @callback: A #GAsyncReadyCallback to call when the request is satisfied or * %NULL. * @user_data: User data to pass to @callback. * * Asynchronously requests to disconnect the modem. * * When the operation is finished, @callback will be invoked in the * thread-default main loop * of the thread you are calling this method from. You can then call * mm_modem_simple_disconnect_finish() to get the result of the operation. * * See mm_modem_simple_disconnect_sync() for the synchronous, blocking version * of this method. * * Since: 1.0 */ void mm_modem_simple_disconnect (MMModemSimple *self, const gchar *bearer, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { g_return_if_fail (MM_IS_MODEM_SIMPLE (self)); mm_gdbus_modem_simple_call_disconnect (MM_GDBUS_MODEM_SIMPLE (self), bearer ? bearer : "/", cancellable, callback, user_data); } /** * mm_modem_simple_disconnect_sync: * @self: A #MMModemSimple. * @bearer: (allow-none): Path of the bearer to disconnect, or %NULL to * disconnect all connected bearers. * @cancellable: (allow-none): A #GCancellable or %NULL. * @error: Return location for error or %NULL. * * Synchronously requests to disconnect the modem. * * The calling thread is blocked until a reply is received. See * mm_modem_simple_disconnect() for the asynchronous version of this method. * * Returns: %TRUE if the modem is successfully disconnected, %FALSE if @error is * set. * * Since: 1.0 */ gboolean mm_modem_simple_disconnect_sync (MMModemSimple *self, const gchar *bearer, GCancellable *cancellable, GError **error) { g_return_val_if_fail (MM_IS_MODEM_SIMPLE (self), FALSE); return mm_gdbus_modem_simple_call_disconnect_sync (MM_GDBUS_MODEM_SIMPLE (self), bearer ? bearer : "/", cancellable, error); } /*****************************************************************************/ /** * mm_modem_simple_get_status_finish: * @self: A #MMModemSimple. * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to * mm_modem_simple_connect(). * @error: Return location for error or %NULL. * * Finishes an operation started with mm_modem_simple_get_status(). * * Returns: (transfer full): A #MMSimpleStatus, or %FALSE if @error is set. The * returned value must be freed with g_object_unref(). * * Since: 1.0 */ MMSimpleStatus * mm_modem_simple_get_status_finish (MMModemSimple *self, GAsyncResult *res, GError **error) { MMSimpleStatus *status; GVariant *dictionary = NULL; g_return_val_if_fail (MM_IS_MODEM_SIMPLE (self), NULL); if (!mm_gdbus_modem_simple_call_get_status_finish (MM_GDBUS_MODEM_SIMPLE (self), &dictionary, res, error)) return NULL; status = mm_simple_status_new_from_dictionary (dictionary, error); g_variant_unref (dictionary); return status; } /** * mm_modem_simple_get_status: * @self: A #MMModemSimple. * @cancellable: (allow-none): A #GCancellable or %NULL. * @callback: A #GAsyncReadyCallback to call when the request is satisfied or * %NULL. * @user_data: User data to pass to @callback. * * Asynchronously requests a compilation of the status of the modem. * * When the operation is finished, @callback will be invoked in the * thread-default main loop * of the thread you are calling this method from. You can then call * mm_modem_simple_get_status_finish() to get the result of the operation. * * See mm_modem_simple_get_status_sync() for the synchronous, blocking version * of this method. * * Since: 1.0 */ void mm_modem_simple_get_status (MMModemSimple *self, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { g_return_if_fail (MM_IS_MODEM_SIMPLE (self)); mm_gdbus_modem_simple_call_get_status (MM_GDBUS_MODEM_SIMPLE (self), cancellable, callback, user_data); } /** * mm_modem_simple_get_status_sync: * @self: A #MMModemSimple. * @cancellable: (allow-none): A #GCancellable or %NULL. * @error: Return location for error or %NULL. * * Synchronously requests a compilation of the status of the modem. * * The calling thread is blocked until a reply is received. See * mm_modem_simple_get_status() for the asynchronous version of this method. * * Returns: (transfer full): A #MMSimpleStatus, or %FALSE if @error is set. The * returned value must be freed with g_object_unref(). * * Since: 1.0 */ MMSimpleStatus * mm_modem_simple_get_status_sync (MMModemSimple *self, GCancellable *cancellable, GError **error) { MMSimpleStatus *status; GVariant *dictionary = NULL; g_return_val_if_fail (MM_IS_MODEM_SIMPLE (self), NULL); if (!mm_gdbus_modem_simple_call_get_status_sync (MM_GDBUS_MODEM_SIMPLE (self), &dictionary, cancellable, error)) return NULL; status = mm_simple_status_new_from_dictionary (dictionary, error); g_variant_unref (dictionary); return status; } /*****************************************************************************/ static void mm_modem_simple_init (MMModemSimple *self) { } static void mm_modem_simple_class_init (MMModemSimpleClass *modem_class) { } ModemManager-1.23.4-dev/libmm-glib/mm-modem-simple.h000066400000000000000000000116411456466623000221130ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * libmm-glib -- Access modem status & information from glib applications * * 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 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 Street, Fifth Floor, * Boston, MA 02110-1301 USA. * * Copyright (C) 2011 - 2012 Aleksander Morgado * Copyright (C) 2012 Google, Inc. */ #ifndef _MM_MODEM_SIMPLE_H_ #define _MM_MODEM_SIMPLE_H_ #if !defined (__LIBMM_GLIB_H_INSIDE__) && !defined (LIBMM_GLIB_COMPILATION) #error "Only can be included directly." #endif #include #include "mm-gdbus-modem.h" #include "mm-simple-connect-properties.h" #include "mm-simple-status.h" #include "mm-bearer.h" G_BEGIN_DECLS #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_MODEM_SIMPLE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_MODEM_SIMPLE, MMModemSimpleClass)) #define MM_IS_MODEM_SIMPLE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_MODEM_SIMPLE)) #define MM_IS_MODEM_SIMPLE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), MM_TYPE_MODEM_SIMPLE)) #define MM_MODEM_SIMPLE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_MODEM_SIMPLE, MMModemSimpleClass)) typedef struct _MMModemSimple MMModemSimple; typedef struct _MMModemSimpleClass MMModemSimpleClass; /** * MMModemSimple: * * The #MMModemSimple structure contains private data and should only be accessed * using the provided API. */ struct _MMModemSimple { /*< private >*/ MmGdbusModemSimpleProxy parent; gpointer unused; }; struct _MMModemSimpleClass { /*< private >*/ MmGdbusModemSimpleProxyClass parent; }; GType mm_modem_simple_get_type (void); G_DEFINE_AUTOPTR_CLEANUP_FUNC (MMModemSimple, g_object_unref) const gchar *mm_modem_simple_get_path (MMModemSimple *self); gchar *mm_modem_simple_dup_path (MMModemSimple *self); void mm_modem_simple_connect (MMModemSimple *self, MMSimpleConnectProperties *properties, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); MMBearer *mm_modem_simple_connect_finish (MMModemSimple *self, GAsyncResult *res, GError **error); MMBearer *mm_modem_simple_connect_sync (MMModemSimple *self, MMSimpleConnectProperties *properties, GCancellable *cancellable, GError **error); void mm_modem_simple_disconnect (MMModemSimple *self, const gchar *bearer, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); gboolean mm_modem_simple_disconnect_finish (MMModemSimple *self, GAsyncResult *res, GError **error); gboolean mm_modem_simple_disconnect_sync (MMModemSimple *self, const gchar *bearer, GCancellable *cancellable, GError **error); void mm_modem_simple_get_status (MMModemSimple *self, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); MMSimpleStatus *mm_modem_simple_get_status_finish (MMModemSimple *self, GAsyncResult *res, GError **error); MMSimpleStatus *mm_modem_simple_get_status_sync (MMModemSimple *self, GCancellable *cancellable, GError **error); G_END_DECLS #endif /* _MM_MODEM_SIMPLE_H_ */ ModemManager-1.23.4-dev/libmm-glib/mm-modem-time.c000066400000000000000000000177171456466623000215650ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * libmm-glib -- Access modem status & information from glib applications * * 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 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 Street, Fifth Floor, * Boston, MA 02110-1301 USA. * * Copyright (C) 2012 Aleksander Morgado * Copyright (C) 2012 Google, Inc. */ #include #include "mm-helpers.h" #include "mm-errors-types.h" #include "mm-modem-time.h" /** * SECTION: mm-modem-time * @title: MMModemTime * @short_description: The Time interface * * The #MMModemTime is an object providing access to the methods, signals and * properties of the Time interface. * * The Time interface is exposed on modems which support network time retrieval. */ G_DEFINE_TYPE (MMModemTime, mm_modem_time, MM_GDBUS_TYPE_MODEM_TIME_PROXY) struct _MMModemTimePrivate { /* Common mutex to sync access */ GMutex mutex; PROPERTY_OBJECT_DECLARE (network_timezone, MMNetworkTimezone) }; /*****************************************************************************/ /** * mm_modem_time_get_path: * @self: A #MMModemTime. * * Gets the DBus path of the #MMObject which implements this interface. * * Returns: (transfer none): The DBus path of the #MMObject object. * * Since: 1.0 */ const gchar * mm_modem_time_get_path (MMModemTime *self) { g_return_val_if_fail (MM_IS_MODEM_TIME (self), NULL); RETURN_NON_EMPTY_CONSTANT_STRING ( g_dbus_proxy_get_object_path (G_DBUS_PROXY (self))); } /** * mm_modem_time_dup_path: * @self: A #MMModemTime. * * Gets a copy of the DBus path of the #MMObject object which implements this * interface. * * Returns: (transfer full): The DBus path of the #MMObject. The returned value * should be freed with g_free(). * * Since: 1.0 */ gchar * mm_modem_time_dup_path (MMModemTime *self) { gchar *value; g_return_val_if_fail (MM_IS_MODEM_TIME (self), NULL); g_object_get (G_OBJECT (self), "g-object-path", &value, NULL); RETURN_NON_EMPTY_STRING (value); } /*****************************************************************************/ /** * mm_modem_time_get_network_time_finish: * @self: A #MMModemTime. * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to * mm_modem_enable(). * @error: Return location for error or %NULL. * * Finishes an operation started with mm_modem_time_get_network_time(). * * Returns: (transfer full): A string containing the network time, or %NULL if * @error is set. The returned value should be freed with g_free(). * * Since: 1.0 */ gchar * mm_modem_time_get_network_time_finish (MMModemTime *self, GAsyncResult *res, GError **error) { gchar *network_time = NULL; g_return_val_if_fail (MM_IS_MODEM_TIME (self), NULL); if (!mm_gdbus_modem_time_call_get_network_time_finish (MM_GDBUS_MODEM_TIME (self), &network_time, res, error)) return NULL; return network_time; } /** * mm_modem_time_get_network_time: * @self: A #MMModemTime. * @cancellable: (allow-none): A #GCancellable or %NULL. * @callback: A #GAsyncReadyCallback to call when the request is satisfied or * %NULL. * @user_data: User data to pass to @callback. * * Asynchronously requests the current network time. * * When the operation is finished, @callback will be invoked in the * thread-default main loop * of the thread you are calling this method from. You can then call * mm_modem_time_get_network_time_finish() to get the result of the operation. * * See mm_modem_time_get_network_time_sync() for the synchronous, blocking * version of this method. * * Since: 1.0 */ void mm_modem_time_get_network_time (MMModemTime *self, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { g_return_if_fail (MM_IS_MODEM_TIME (self)); mm_gdbus_modem_time_call_get_network_time (MM_GDBUS_MODEM_TIME (self), cancellable, callback, user_data); } /** * mm_modem_time_get_network_time_sync: * @self: A #MMModemTime. * @cancellable: (allow-none): A #GCancellable or %NULL. * @error: Return location for error or %NULL. * * Synchronously requests the current network time. * * The calling thread is blocked until a reply is received. See * mm_modem_time_get_network_time() for the asynchronous version of this method. * * Returns: (transfer full): A string containing the network time, or %NULL if * @error is set. The returned value should be freed with g_free(). * * Since: 1.0 */ gchar * mm_modem_time_get_network_time_sync (MMModemTime *self, GCancellable *cancellable, GError **error) { gchar *network_time = NULL; g_return_val_if_fail (MM_IS_MODEM_TIME (self), NULL); if (!mm_gdbus_modem_time_call_get_network_time_sync (MM_GDBUS_MODEM_TIME (self), &network_time, cancellable, error)) return NULL; return network_time; } /*****************************************************************************/ /** * mm_modem_time_get_network_timezone: * @self: A #MMModemTime. * * Gets the network timezone information. * * The values reported by @self are not updated when the values in the * interface change. Instead, the client is expected to call * mm_modem_time_get_network_timezone() again to get a new #MMNetworkTimezone * with the new values. * * Returns: (transfer full): A #MMNetworkTimezone that must be freed with * g_object_unref() or %NULL if unknown. * * Since: 1.0 */ /** * mm_modem_time_peek_network_timezone: * @self: A #MMModemTime. * * Gets the network timezone information. * * The returned value is only valid until the property changes so it is * only safe to use this function on the thread where @self was constructed. Use * mm_modem_time_get_network_timezone() if on another thread. * * Returns: (transfer none): A #MMNetworkTimezone. Do not free the returned * value, it belongs to @self. * * Since: 1.0 */ PROPERTY_OBJECT_DEFINE_FAILABLE (network_timezone, ModemTime, modem_time, MODEM_TIME, MMNetworkTimezone, mm_network_timezone_new_from_dictionary) /*****************************************************************************/ static void mm_modem_time_init (MMModemTime *self) { self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, MM_TYPE_MODEM_TIME, MMModemTimePrivate); g_mutex_init (&self->priv->mutex); PROPERTY_INITIALIZE (network_timezone, "network-timezone") } static void finalize (GObject *object) { MMModemTime *self = MM_MODEM_TIME (object); g_mutex_clear (&self->priv->mutex); PROPERTY_OBJECT_FINALIZE (network_timezone) G_OBJECT_CLASS (mm_modem_time_parent_class)->finalize (object); } static void mm_modem_time_class_init (MMModemTimeClass *modem_class) { GObjectClass *object_class = G_OBJECT_CLASS (modem_class); g_type_class_add_private (object_class, sizeof (MMModemTimePrivate)); object_class->finalize = finalize; } ModemManager-1.23.4-dev/libmm-glib/mm-modem-time.h000066400000000000000000000065761456466623000215730ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * libmm-glib -- Access modem status & information from glib applications * * 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 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 Street, Fifth Floor, * Boston, MA 02110-1301 USA. * * Copyright (C) 2012 Aleksander Morgado * Copyright (C) 2012 Google, Inc. */ #ifndef _MM_MODEM_TIME_H_ #define _MM_MODEM_TIME_H_ #if !defined (__LIBMM_GLIB_H_INSIDE__) && !defined (LIBMM_GLIB_COMPILATION) #error "Only can be included directly." #endif #include #include "mm-gdbus-modem.h" #include "mm-network-timezone.h" G_BEGIN_DECLS #define MM_TYPE_MODEM_TIME (mm_modem_time_get_type ()) #define MM_MODEM_TIME(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_MODEM_TIME, MMModemTime)) #define MM_MODEM_TIME_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_MODEM_TIME, MMModemTimeClass)) #define MM_IS_MODEM_TIME(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_MODEM_TIME)) #define MM_IS_MODEM_TIME_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), MM_TYPE_MODEM_TIME)) #define MM_MODEM_TIME_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_MODEM_TIME, MMModemTimeClass)) typedef struct _MMModemTime MMModemTime; typedef struct _MMModemTimeClass MMModemTimeClass; typedef struct _MMModemTimePrivate MMModemTimePrivate; /** * MMModemTime: * * The #MMModemTime structure contains private data and should only be accessed * using the provided API. */ struct _MMModemTime { /*< private >*/ MmGdbusModemTimeProxy parent; MMModemTimePrivate *priv; }; struct _MMModemTimeClass { /*< private >*/ MmGdbusModemTimeProxyClass parent; }; GType mm_modem_time_get_type (void); G_DEFINE_AUTOPTR_CLEANUP_FUNC (MMModemTime, g_object_unref) const gchar *mm_modem_time_get_path (MMModemTime *self); gchar *mm_modem_time_dup_path (MMModemTime *self); void mm_modem_time_get_network_time (MMModemTime *self, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); gchar *mm_modem_time_get_network_time_finish (MMModemTime *self, GAsyncResult *res, GError **error); gchar *mm_modem_time_get_network_time_sync (MMModemTime *self, GCancellable *cancellable, GError **error); MMNetworkTimezone *mm_modem_time_peek_network_timezone (MMModemTime *self); MMNetworkTimezone *mm_modem_time_get_network_timezone (MMModemTime *self); G_END_DECLS #endif /* _MM_MODEM_TIME_H_ */ ModemManager-1.23.4-dev/libmm-glib/mm-modem-voice.c000066400000000000000000001152041456466623000217220ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * libmm-glib -- Access modem status & information from glib applications * * 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 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 Street, Fifth Floor, * Boston, MA 02110-1301 USA. * * Copyright (C) 2015 Aleksander Morgado * Copyright (C) 2015 Marco Bascetta */ #include #include "mm-helpers.h" #include "mm-common-helpers.h" #include "mm-errors-types.h" #include "mm-modem-voice.h" /** * SECTION: mm-modem-voice * @title: MMModemVoice * @short_description: The Voice interface * * The #MMModemVoice is an object providing access to the methods, signals and * properties of the Voice interface. * * The Voice interface is exposed whenever a modem has voice capabilities. */ G_DEFINE_TYPE (MMModemVoice, mm_modem_voice, MM_GDBUS_TYPE_MODEM_VOICE_PROXY) /*****************************************************************************/ /** * mm_modem_voice_get_path: * @self: A #MMModemVoice. * * Gets the DBus path of the #MMObject which implements this interface. * * Returns: (transfer none): The DBus path of the #MMObject object. * * Since: 1.6 */ const gchar * mm_modem_voice_get_path (MMModemVoice *self) { g_return_val_if_fail (MM_IS_MODEM_VOICE (self), NULL); RETURN_NON_EMPTY_CONSTANT_STRING ( g_dbus_proxy_get_object_path (G_DBUS_PROXY (self))); } /** * mm_modem_voice_dup_path: * @self: A #MMModemVoice. * * Gets a copy of the DBus path of the #MMObject object which implements this * interface. * * Returns: (transfer full): The DBus path of the #MMObject. The returned value * should be freed with g_free(). * * Since: 1.6 */ gchar * mm_modem_voice_dup_path (MMModemVoice *self) { gchar *value; g_return_val_if_fail (MM_IS_MODEM_VOICE (self), NULL); g_object_get (G_OBJECT (self), "g-object-path", &value, NULL); RETURN_NON_EMPTY_STRING (value); } /** * mm_modem_voice_get_emergency_only: * @self: A #MMModemVoice. * * Checks whether emergency calls only are allowed. * * Returns: %TRUE if only emergency calls are allowed, %FALSE otherwise. * * Since: 1.12 */ gboolean mm_modem_voice_get_emergency_only (MMModemVoice *self) { return mm_gdbus_modem_voice_get_emergency_only (MM_GDBUS_MODEM_VOICE (self)); } /*****************************************************************************/ typedef struct { gchar **call_paths; GList *call_objects; guint i; } ListCallsContext; static void call_object_list_free (GList *list) { g_list_free_full (list, g_object_unref); } static void list_call_context_free (ListCallsContext *ctx) { g_strfreev (ctx->call_paths); call_object_list_free (ctx->call_objects); g_slice_free (ListCallsContext, ctx); } /** * mm_modem_voice_list_calls_finish: * @self: A #MMModem. * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to * mm_modem_voice_list_calls(). * @error: Return location for error or %NULL. * * Finishes an operation started with mm_modem_voice_list_calls(). * * Returns: (element-type ModemManager.Call) (transfer full): A list of #MMCall * objects, or #NULL if either not found or @error is set. The returned value * should be freed with g_list_free_full() using g_object_unref() as * #GDestroyNotify function. * * Since: 1.6 */ GList * mm_modem_voice_list_calls_finish (MMModemVoice *self, GAsyncResult *res, GError **error) { g_return_val_if_fail (MM_IS_MODEM_VOICE (self), FALSE); return g_task_propagate_pointer (G_TASK (res), error); } static void create_next_call (GTask *task); static void list_build_object_ready (GDBusConnection *connection, GAsyncResult *res, GTask *task) { GError *error = NULL; GObject *call; GObject *source_object; ListCallsContext *ctx; source_object = g_async_result_get_source_object (res); call = g_async_initable_new_finish (G_ASYNC_INITABLE (source_object), res, &error); g_object_unref (source_object); if (error) { g_task_return_error (task, error); g_object_unref (task); return; } ctx = g_task_get_task_data (task); /* Keep the object */ ctx->call_objects = g_list_prepend (ctx->call_objects, call); /* If no more calls, just end here. */ if (!ctx->call_paths[++ctx->i]) { GList *call_objects; call_objects = g_list_copy_deep (ctx->call_objects, (GCopyFunc)g_object_ref, NULL); g_task_return_pointer (task, call_objects, (GDestroyNotify)call_object_list_free); g_object_unref (task); return; } /* Keep on creating next object */ create_next_call (task); } static void create_next_call (GTask *task) { MMModemVoice *self; ListCallsContext *ctx; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); g_async_initable_new_async (MM_TYPE_CALL, G_PRIORITY_DEFAULT, g_task_get_cancellable (task), (GAsyncReadyCallback)list_build_object_ready, task, "g-flags", G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START, "g-name", MM_DBUS_SERVICE, "g-connection", g_dbus_proxy_get_connection (G_DBUS_PROXY (self)), "g-object-path", ctx->call_paths[ctx->i], "g-interface-name", "org.freedesktop.ModemManager1.Call", NULL); } /** * mm_modem_voice_list_calls: * @self: A #MMModemVoice. * @cancellable: (allow-none): A #GCancellable or %NULL. * @callback: A #GAsyncReadyCallback to call when the request is satisfied or * %NULL. * @user_data: User data to pass to @callback. * * Asynchronously lists the #MMCall objects in the modem. * * When the operation is finished, @callback will be invoked in the * thread-default main loop * of the thread you are calling this method from. You can then call * mm_modem_voice_list_calls_finish() to get the result of the operation. * * See mm_modem_voice_list_calls_sync() for the synchronous, blocking version of * this method. * * Since: 1.6 */ void mm_modem_voice_list_calls (MMModemVoice *self, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { ListCallsContext *ctx; GTask *task; g_return_if_fail (MM_IS_MODEM_VOICE (self)); ctx = g_slice_new0 (ListCallsContext); ctx->call_paths = mm_gdbus_modem_voice_dup_calls (MM_GDBUS_MODEM_VOICE (self)); task = g_task_new (self, cancellable, callback, user_data); g_task_set_task_data (task, ctx, (GDestroyNotify)list_call_context_free); /* If no CALL, just end here. */ if (!ctx->call_paths || !ctx->call_paths[0]) { g_task_return_pointer (task, NULL, NULL); g_object_unref (task); return; } /* Got list of paths. If at least one found, start creating objects for each */ ctx->i = 0; create_next_call (task); } /** * mm_modem_voice_list_calls_sync: * @self: A #MMModemVoice. * @cancellable: (allow-none): A #GCancellable or %NULL. * @error: Return location for error or %NULL. * * Synchronously lists the #MMCall objects in the modem. * * The calling thread is blocked until a reply is received. See * mm_modem_voice_list_calls() for the asynchronous version of this method. * * Returns: (element-type ModemManager.Call) (transfer full): A list of #MMCall * objects, or #NULL if either not found or @error is set. The returned value * should be freed with g_list_free_full() using g_object_unref() as * #GDestroyNotify function. * * Since: 1.6 */ GList * mm_modem_voice_list_calls_sync (MMModemVoice *self, GCancellable *cancellable, GError **error) { GList *call_objects = NULL; gchar **call_paths = NULL; guint i; g_return_val_if_fail (MM_IS_MODEM_VOICE (self), NULL); call_paths = mm_gdbus_modem_voice_dup_calls (MM_GDBUS_MODEM_VOICE (self)); /* Only non-empty lists are returned */ if (!call_paths) return NULL; for (i = 0; call_paths[i]; i++) { GObject *call; call = g_initable_new (MM_TYPE_CALL, cancellable, error, "g-flags", G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START, "g-name", MM_DBUS_SERVICE, "g-connection", g_dbus_proxy_get_connection (G_DBUS_PROXY (self)), "g-object-path", call_paths[i], "g-interface-name", "org.freedesktop.ModemManager1.Call", NULL); if (!call) { call_object_list_free (call_objects); g_strfreev (call_paths); return NULL; } /* Keep the object */ call_objects = g_list_prepend (call_objects, call); } g_strfreev (call_paths); return call_objects; } /*****************************************************************************/ /** * mm_modem_voice_create_call_finish: * @self: A #MMModemVoice. * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to * mm_modem_voice_create_call(). * @error: Return location for error or %NULL. * * Finishes an operation started with mm_modem_voice_create_call(). * * Returns: (transfer full): A newly created #MMCall, or %NULL if @error is set. * The returned value should be freed with g_object_unref(). * * Since: 1.6 */ MMCall * mm_modem_voice_create_call_finish (MMModemVoice *self, GAsyncResult *res, GError **error) { g_return_val_if_fail (MM_IS_MODEM_VOICE (self), NULL); return g_task_propagate_pointer (G_TASK (res), error); } static void new_call_object_ready (GDBusConnection *connection, GAsyncResult *res, GTask *task) { GError *error = NULL; GObject *call; GObject *source_object; source_object = g_async_result_get_source_object (res); call = g_async_initable_new_finish (G_ASYNC_INITABLE (source_object), res, &error); g_object_unref (source_object); if (error) g_task_return_error (task, error); else g_task_return_pointer (task, call, g_object_unref); g_object_unref (task); } static void create_call_ready (MMModemVoice *self, GAsyncResult *res, GTask *task) { GError *error = NULL; gchar *call_path = NULL; if (!mm_gdbus_modem_voice_call_create_call_finish (MM_GDBUS_MODEM_VOICE (self), &call_path, res, &error)) { g_task_return_error (task, error); g_object_unref (task); g_free (call_path); return; } g_async_initable_new_async (MM_TYPE_CALL, G_PRIORITY_DEFAULT, g_task_get_cancellable (task), (GAsyncReadyCallback)new_call_object_ready, task, "g-flags", G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START, "g-name", MM_DBUS_SERVICE, "g-connection", g_dbus_proxy_get_connection (G_DBUS_PROXY (self)), "g-object-path", call_path, "g-interface-name", "org.freedesktop.ModemManager1.Call", NULL); g_free (call_path); } /** * mm_modem_voice_create_call: * @self: A #MMModemVoice. * @properties: A ##MMCallProperties object with the properties to use. * @cancellable: (allow-none): A #GCancellable or %NULL. * @callback: A #GAsyncReadyCallback to call when the request is satisfied or * %NULL. * @user_data: User data to pass to @callback. * * Asynchronously creates a new #MMCall in the modem. * * When the operation is finished, @callback will be invoked in the * thread-default main loop * of the thread you are calling this method from. You can then call * mm_modem_voice_create_call_finish() to get the result of the operation. * * See mm_modem_voice_create_call_sync() for the synchronous, blocking version * of this method. * * Since: 1.6 */ void mm_modem_voice_create_call (MMModemVoice *self, MMCallProperties *properties, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; GVariant *dictionary; g_return_if_fail (MM_IS_MODEM_VOICE (self)); task = g_task_new (self, cancellable, callback, user_data); dictionary = mm_call_properties_get_dictionary (properties); mm_gdbus_modem_voice_call_create_call ( MM_GDBUS_MODEM_VOICE (self), dictionary, cancellable, (GAsyncReadyCallback)create_call_ready, task); g_variant_unref (dictionary); } /** * mm_modem_voice_create_call_sync: * @self: A #MMModemVoice. * @properties: A ##MMCallProperties object with the properties to use. * @cancellable: (allow-none): A #GCancellable or %NULL. * @error: Return location for error or %NULL. * * Synchronously creates a new #MMCall in the modem. * * The calling thread is blocked until a reply is received. See * mm_modem_voice_create_call() for the asynchronous version of this method. * * Returns: (transfer full): A newly created #MMCall, or %NULL if @error is set. * The returned value should be freed with g_object_unref(). * * Since: 1.6 */ MMCall * mm_modem_voice_create_call_sync (MMModemVoice *self, MMCallProperties *properties, GCancellable *cancellable, GError **error) { MMCall *call = NULL; gchar *call_path = NULL; GVariant *dictionary; g_return_val_if_fail (MM_IS_MODEM_VOICE (self), NULL); dictionary = mm_call_properties_get_dictionary (properties); mm_gdbus_modem_voice_call_create_call_sync (MM_GDBUS_MODEM_VOICE (self), dictionary, &call_path, cancellable, error); if (call_path) { call = g_initable_new (MM_TYPE_CALL, cancellable, error, "g-flags", G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START, "g-name", MM_DBUS_SERVICE, "g-connection", g_dbus_proxy_get_connection (G_DBUS_PROXY (self)), "g-object-path", call_path, "g-interface-name", "org.freedesktop.ModemManager1.Call", NULL); g_free (call_path); } g_variant_unref (dictionary); return (call ? MM_CALL (call) : NULL); } /*****************************************************************************/ /** * mm_modem_voice_delete_call_finish: * @self: A #MMModemVoice. * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to * mm_modem_voice_delete_call(). * @error: Return location for error or %NULL. * * Finishes an operation started with mm_modem_voice_delete_call(). * * Returns: %TRUE if the call was deleted, %FALSE if @error is set. * * Since: 1.6 */ gboolean mm_modem_voice_delete_call_finish (MMModemVoice *self, GAsyncResult *res, GError **error) { g_return_val_if_fail (MM_IS_MODEM_VOICE (self), FALSE); return mm_gdbus_modem_voice_call_delete_call_finish (MM_GDBUS_MODEM_VOICE (self), res, error); } /** * mm_modem_voice_delete_call: * @self: A #MMModemVoice. * @call: Path of the #MMCall to delete. * @cancellable: (allow-none): A #GCancellable or %NULL. * @callback: A #GAsyncReadyCallback to call when the request is satisfied or * %NULL. * @user_data: User data to pass to @callback. * * Asynchronously deletes a given #MMCall from the modem. * * When the operation is finished, @callback will be invoked in the * thread-default main loop * of the thread you are calling this method from. You can then call * mm_modem_voice_delete_call_finish() to get the result of the operation. * * See mm_modem_voice_delete_call_sync() for the synchronous, blocking version * of this method. * * Since: 1.6 */ void mm_modem_voice_delete_call (MMModemVoice *self, const gchar *call, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { g_return_if_fail (MM_IS_MODEM_VOICE (self)); mm_gdbus_modem_voice_call_delete_call (MM_GDBUS_MODEM_VOICE (self), call, cancellable, callback, user_data); } /** * mm_modem_voice_delete_call_sync: * @self: A #MMModemVoice. * @call: Path of the #MMCall to delete. * @cancellable: (allow-none): A #GCancellable or %NULL. * @error: Return location for error or %NULL. * Synchronously deletes a given #MMCall from the modem. * * The calling thread is blocked until a reply is received. See * mm_modem_voice_delete_call() for the asynchronous version of this method. * * Returns: %TRUE if the CALL was deleted, %FALSE if @error is set. * * Since: 1.6 */ gboolean mm_modem_voice_delete_call_sync (MMModemVoice *self, const gchar *call, GCancellable *cancellable, GError **error) { g_return_val_if_fail (MM_IS_MODEM_VOICE (self), FALSE); return mm_gdbus_modem_voice_call_delete_call_sync (MM_GDBUS_MODEM_VOICE (self), call, cancellable, error); } /*****************************************************************************/ /** * mm_modem_voice_hold_and_accept_finish: * @self: A #MMModemVoice. * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to * mm_modem_voice_hold_and_accept(). * @error: Return location for error or %NULL. * * Finishes an operation started with mm_modem_voice_hold_and_accept(). * * Returns: %TRUE if the operation was successful, %FALSE if @error is set. * * Since: 1.12 */ gboolean mm_modem_voice_hold_and_accept_finish (MMModemVoice *self, GAsyncResult *res, GError **error) { g_return_val_if_fail (MM_IS_MODEM_VOICE (self), FALSE); return mm_gdbus_modem_voice_call_hold_and_accept_finish (MM_GDBUS_MODEM_VOICE (self), res, error); } /** * mm_modem_voice_hold_and_accept: * @self: A #MMModemVoice. * @cancellable: (allow-none): A #GCancellable or %NULL. * @callback: A #GAsyncReadyCallback to call when the request is satisfied or * %NULL. * @user_data: User data to pass to @callback. * * Asynchronously puts all active calls on hold and accepts the next waiting or * held call. * * When the operation is finished, @callback will be invoked in the * thread-default main loop * of the thread you are calling this method from. You can then call * mm_modem_voice_hold_and_accept_finish() to get the result of the operation. * * See mm_modem_voice_hold_and_accept_sync() for the synchronous, blocking * version of this method. * * Since: 1.12 */ void mm_modem_voice_hold_and_accept (MMModemVoice *self, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { g_return_if_fail (MM_IS_MODEM_VOICE (self)); mm_gdbus_modem_voice_call_hold_and_accept (MM_GDBUS_MODEM_VOICE (self), cancellable, callback, user_data); } /** * mm_modem_voice_hold_and_accept_sync: * @self: A #MMModemVoice. * @cancellable: (allow-none): A #GCancellable or %NULL. * @error: Return location for error or %NULL. * * Synchronously puts all active calls on hold and accepts the next waiting or * held call. * * The calling thread is blocked until a reply is received. See * mm_modem_voice_hold_and_accept() for the asynchronous version of this method. * * Returns: %TRUE if the operation was successful, %FALSE if @error is set. * * Since: 1.12 */ gboolean mm_modem_voice_hold_and_accept_sync (MMModemVoice *self, GCancellable *cancellable, GError **error) { g_return_val_if_fail (MM_IS_MODEM_VOICE (self), FALSE); return mm_gdbus_modem_voice_call_hold_and_accept_sync (MM_GDBUS_MODEM_VOICE (self), cancellable, error); } /*****************************************************************************/ /** * mm_modem_voice_hangup_and_accept_finish: * @self: A #MMModemVoice. * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to * mm_modem_voice_hangup_and_accept(). * @error: Return location for error or %NULL. * * Finishes an operation started with mm_modem_voice_hangup_and_accept(). * * Returns: %TRUE if the operation was successful, %FALSE if @error is set. * * Since: 1.12 */ gboolean mm_modem_voice_hangup_and_accept_finish (MMModemVoice *self, GAsyncResult *res, GError **error) { g_return_val_if_fail (MM_IS_MODEM_VOICE (self), FALSE); return mm_gdbus_modem_voice_call_hangup_and_accept_finish (MM_GDBUS_MODEM_VOICE (self), res, error); } /** * mm_modem_voice_hangup_and_accept: * @self: A #MMModemVoice. * @cancellable: (allow-none): A #GCancellable or %NULL. * @callback: A #GAsyncReadyCallback to call when the request is satisfied or * %NULL. * @user_data: User data to pass to @callback. * * Asynchronously hangs up all active calls and accepts the next waiting or held * call. * * When the operation is finished, @callback will be invoked in the * thread-default main loop * of the thread you are calling this method from. You can then call * mm_modem_voice_hangup_and_accept_finish() to get the result of the operation. * * See mm_modem_voice_hangup_and_accept_sync() for the synchronous, blocking * version of this method. * * Since: 1.12 */ void mm_modem_voice_hangup_and_accept (MMModemVoice *self, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { g_return_if_fail (MM_IS_MODEM_VOICE (self)); mm_gdbus_modem_voice_call_hangup_and_accept (MM_GDBUS_MODEM_VOICE (self), cancellable, callback, user_data); } /** * mm_modem_voice_hangup_and_accept_sync: * @self: A #MMModemVoice. * @cancellable: (allow-none): A #GCancellable or %NULL. * @error: Return location for error or %NULL. * * Synchronously hangs up all active calls and accepts the next waiting or held * call. * * The calling thread is blocked until a reply is received. See * mm_modem_voice_hangup_and_accept() for the asynchronous version of this method. * * Returns: %TRUE if the operation was successful, %FALSE if @error is set. * * Since: 1.12 */ gboolean mm_modem_voice_hangup_and_accept_sync (MMModemVoice *self, GCancellable *cancellable, GError **error) { g_return_val_if_fail (MM_IS_MODEM_VOICE (self), FALSE); return mm_gdbus_modem_voice_call_hangup_and_accept_sync (MM_GDBUS_MODEM_VOICE (self), cancellable, error); } /*****************************************************************************/ /** * mm_modem_voice_hangup_all_finish: * @self: A #MMModemVoice. * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to * mm_modem_voice_hangup_all(). * @error: Return location for error or %NULL. * * Finishes an operation started with mm_modem_voice_hangup_all(). * * Returns: %TRUE if the operation was successful, %FALSE if @error is set. * * Since: 1.12 */ gboolean mm_modem_voice_hangup_all_finish (MMModemVoice *self, GAsyncResult *res, GError **error) { g_return_val_if_fail (MM_IS_MODEM_VOICE (self), FALSE); return mm_gdbus_modem_voice_call_hangup_all_finish (MM_GDBUS_MODEM_VOICE (self), res, error); } /** * mm_modem_voice_hangup_all: * @self: A #MMModemVoice. * @cancellable: (allow-none): A #GCancellable or %NULL. * @callback: A #GAsyncReadyCallback to call when the request is satisfied or * %NULL. * @user_data: User data to pass to @callback. * * Asynchronously hangs up all ongoing (active, waiting, held) calls. * * When the operation is finished, @callback will be invoked in the * thread-default main loop * of the thread you are calling this method from. You can then call * mm_modem_voice_hangup_all_finish() to get the result of the operation. * * See mm_modem_voice_hangup_all_sync() for the synchronous, blocking version of * this method. * * Since: 1.12 */ void mm_modem_voice_hangup_all (MMModemVoice *self, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { g_return_if_fail (MM_IS_MODEM_VOICE (self)); mm_gdbus_modem_voice_call_hangup_all (MM_GDBUS_MODEM_VOICE (self), cancellable, callback, user_data); } /** * mm_modem_voice_hangup_all_sync: * @self: A #MMModemVoice. * @cancellable: (allow-none): A #GCancellable or %NULL. * @error: Return location for error or %NULL. * * Synchronously hangs up all ongoing (active, waiting, held) calls. * * The calling thread is blocked until a reply is received. See * mm_modem_voice_hangup_all() for the asynchronous version of this method. * * Returns: %TRUE if the operation was successful, %FALSE if @error is set. * * Since: 1.12 */ gboolean mm_modem_voice_hangup_all_sync (MMModemVoice *self, GCancellable *cancellable, GError **error) { g_return_val_if_fail (MM_IS_MODEM_VOICE (self), FALSE); return mm_gdbus_modem_voice_call_hangup_all_sync (MM_GDBUS_MODEM_VOICE (self), cancellable, error); } /*****************************************************************************/ /** * mm_modem_voice_transfer_finish: * @self: A #MMModemVoice. * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to * mm_modem_voice_transfer(). * @error: Return location for error or %NULL. * * Finishes an operation started with mm_modem_voice_transfer(). * * Returns: %TRUE if the operation was successful, %FALSE if @error is set. * * Since: 1.12 */ gboolean mm_modem_voice_transfer_finish (MMModemVoice *self, GAsyncResult *res, GError **error) { g_return_val_if_fail (MM_IS_MODEM_VOICE (self), FALSE); return mm_gdbus_modem_voice_call_transfer_finish (MM_GDBUS_MODEM_VOICE (self), res, error); } /** * mm_modem_voice_transfer: * @self: A #MMModemVoice. * @cancellable: (allow-none): A #GCancellable or %NULL. * @callback: A #GAsyncReadyCallback to call when the request is satisfied or * %NULL. * @user_data: User data to pass to @callback. * * Asynchronously joins all active and held calls, and disconnects from them. * * When the operation is finished, @callback will be invoked in the * thread-default main loop * of the thread you are calling this method from. You can then call * mm_modem_voice_transfer_finish() to get the result of the operation. * * See mm_modem_voice_transfer_sync() for the synchronous, blocking version of * this method. * * Since: 1.12 */ void mm_modem_voice_transfer (MMModemVoice *self, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { g_return_if_fail (MM_IS_MODEM_VOICE (self)); mm_gdbus_modem_voice_call_transfer (MM_GDBUS_MODEM_VOICE (self), cancellable, callback, user_data); } /** * mm_modem_voice_transfer_sync: * @self: A #MMModemVoice. * @cancellable: (allow-none): A #GCancellable or %NULL. * @error: Return location for error or %NULL. * * Synchronously joins all active and held calls, and disconnects from them. * * The calling thread is blocked until a reply is received. See * mm_modem_voice_transfer() for the asynchronous version of this method. * * Returns: %TRUE if the operation was successful, %FALSE if @error is set. * * Since: 1.12 */ gboolean mm_modem_voice_transfer_sync (MMModemVoice *self, GCancellable *cancellable, GError **error) { g_return_val_if_fail (MM_IS_MODEM_VOICE (self), FALSE); return mm_gdbus_modem_voice_call_transfer_sync (MM_GDBUS_MODEM_VOICE (self), cancellable, error); } /*****************************************************************************/ /** * mm_modem_voice_call_waiting_setup_finish: * @self: A #MMModemVoice. * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to * mm_modem_voice_call_waiting_setup(). * @error: Return location for error or %NULL. * * Finishes an operation started with mm_modem_voice_call_waiting_setup(). * * Returns: %TRUE if @status is set, %FALSE if @error is set. * * Since: 1.12 */ gboolean mm_modem_voice_call_waiting_setup_finish (MMModemVoice *self, GAsyncResult *res, GError **error) { g_return_val_if_fail (MM_IS_MODEM_VOICE (self), FALSE); return mm_gdbus_modem_voice_call_call_waiting_setup_finish (MM_GDBUS_MODEM_VOICE (self), res, error); } /** * mm_modem_voice_call_waiting_setup: * @self: A #MMModemVoice. * @enable: Whether the call waiting service should be enabled. * @cancellable: (allow-none): A #GCancellable or %NULL. * @callback: A #GAsyncReadyCallback to call when the request is satisfied or * %NULL. * @user_data: User data to pass to @callback. * * Asynchronously enables or disables the call waiting network service. * * When the operation is finished, @callback will be invoked in the * thread-default main loop * of the thread you are calling this method from. You can then call * mm_modem_voice_call_waiting_setup_finish() to get the result of the * operation. * * See mm_modem_voice_call_waiting_setup_sync() for the synchronous, blocking * version of this method. * * Since: 1.12 */ void mm_modem_voice_call_waiting_setup (MMModemVoice *self, gboolean enable, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { g_return_if_fail (MM_IS_MODEM_VOICE (self)); mm_gdbus_modem_voice_call_call_waiting_setup (MM_GDBUS_MODEM_VOICE (self), enable, cancellable, callback, user_data); } /** * mm_modem_voice_call_waiting_setup_sync: * @self: A #MMModemVoice. * @enable: Whether the call waiting service should be enabled. * @cancellable: (allow-none): A #GCancellable or %NULL. * @error: Return location for error or %NULL. * * Synchronously enables or disables the call waiting network service. * * The calling thread is blocked until a reply is received. See * mm_modem_voice_call_waiting_setup() for the asynchronous version of this * method. * * Returns: %TRUE if the operation is successful, %FALSE if @error is set. * * Since: 1.12 */ gboolean mm_modem_voice_call_waiting_setup_sync (MMModemVoice *self, gboolean enable, GCancellable *cancellable, GError **error) { g_return_val_if_fail (MM_IS_MODEM_VOICE (self), FALSE); return mm_gdbus_modem_voice_call_call_waiting_setup_sync (MM_GDBUS_MODEM_VOICE (self), enable, cancellable, error); } /*****************************************************************************/ /** * mm_modem_voice_call_waiting_query_finish: * @self: A #MMModemVoice. * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to * mm_modem_voice_call_waiting_query(). * @status: Output location where to store the status. * @error: Return location for error or %NULL. * * Finishes an operation started with mm_modem_voice_call_waiting_query(). * * Returns: %TRUE if @status is set, %FALSE if @error is set. * * Since: 1.12 */ gboolean mm_modem_voice_call_waiting_query_finish (MMModemVoice *self, GAsyncResult *res, gboolean *status, GError **error) { g_return_val_if_fail (MM_IS_MODEM_VOICE (self), FALSE); return mm_gdbus_modem_voice_call_call_waiting_query_finish (MM_GDBUS_MODEM_VOICE (self), status, res, error); } /** * mm_modem_voice_call_waiting_query: * @self: A #MMModemVoice. * @cancellable: (allow-none): A #GCancellable or %NULL. * @callback: A #GAsyncReadyCallback to call when the request is satisfied or * %NULL. * @user_data: User data to pass to @callback. * * Asynchronously queries the status of the call waiting network service. * * When the operation is finished, @callback will be invoked in the * thread-default main loop * of the thread you are calling this method from. You can then call * mm_modem_voice_call_waiting_query_finish() to get the result of the * operation. * * See mm_modem_voice_call_waiting_query_sync() for the synchronous, blocking * version of this method. * * Since: 1.12 */ void mm_modem_voice_call_waiting_query (MMModemVoice *self, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { g_return_if_fail (MM_IS_MODEM_VOICE (self)); mm_gdbus_modem_voice_call_call_waiting_query (MM_GDBUS_MODEM_VOICE (self), cancellable, callback, user_data); } /** * mm_modem_voice_call_waiting_query_sync: * @self: A #MMModemVoice. * @cancellable: (allow-none): A #GCancellable or %NULL. * @status: Output location where to store the status. * @error: Return location for error or %NULL. * * Synchronously queries the status of the call waiting network service. * * The calling thread is blocked until a reply is received. See * mm_modem_voice_call_waiting_query() for the asynchronous version of this * method. * * Returns: %TRUE if @status is set, %FALSE if @error is set. * * Since: 1.12 */ gboolean mm_modem_voice_call_waiting_query_sync (MMModemVoice *self, GCancellable *cancellable, gboolean *status, GError **error) { g_return_val_if_fail (MM_IS_MODEM_VOICE (self), FALSE); return mm_gdbus_modem_voice_call_call_waiting_query_sync (MM_GDBUS_MODEM_VOICE (self), status, cancellable, error); } /*****************************************************************************/ static void mm_modem_voice_init (MMModemVoice *self) { } static void mm_modem_voice_class_init (MMModemVoiceClass *modem_class) { } ModemManager-1.23.4-dev/libmm-glib/mm-modem-voice.h000066400000000000000000000235601456466623000217320ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * libmm-glib -- Access modem status & information from glib applications * * 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 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 Street, Fifth Floor, * Boston, MA 02110-1301 USA. * * Copyright (C) 2012 Aleksander Morgado * Copyright (C) 2012 Marco Bascetta */ #ifndef _MM_MODEM_VOICE_H_ #define _MM_MODEM_VOICE_H_ #if !defined (__LIBMM_GLIB_H_INSIDE__) && !defined (LIBMM_GLIB_COMPILATION) #error "Only can be included directly." #endif #include #include "mm-gdbus-modem.h" #include "mm-call.h" #include "mm-call-properties.h" G_BEGIN_DECLS #define MM_TYPE_MODEM_VOICE (mm_modem_voice_get_type ()) #define MM_MODEM_VOICE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_MODEM_VOICE, MMModemVoice)) #define MM_MODEM_VOICE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_MODEM_VOICE, MMModemVoiceClass)) #define MM_IS_MODEM_VOICE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_MODEM_VOICE)) #define MM_IS_MODEM_VOICE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), MM_TYPE_MODEM_VOICE)) #define MM_MODEM_VOICE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_MODEM_VOICE, MMModemVoiceClass)) typedef struct _MMModemVoice MMModemVoice; typedef struct _MMModemVoiceClass MMModemVoiceClass; typedef struct _MMModemVoicePrivate MMModemVoicePrivate; /** * MMModemVoice: * * The #MMModemVoice structure contains private data and should only be accessed * using the provided API. */ struct _MMModemVoice { /*< private >*/ MmGdbusModemVoiceProxy parent; MMModemVoicePrivate *priv; }; struct _MMModemVoiceClass { /*< private >*/ MmGdbusModemVoiceProxyClass parent; }; GType mm_modem_voice_get_type (void); G_DEFINE_AUTOPTR_CLEANUP_FUNC (MMModemVoice, g_object_unref) const gchar *mm_modem_voice_get_path (MMModemVoice *self); gchar *mm_modem_voice_dup_path (MMModemVoice *self); gboolean mm_modem_voice_get_emergency_only (MMModemVoice *self); void mm_modem_voice_create_call (MMModemVoice *self, MMCallProperties *properties, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); MMCall *mm_modem_voice_create_call_finish (MMModemVoice *self, GAsyncResult *res, GError **error); MMCall *mm_modem_voice_create_call_sync (MMModemVoice *self, MMCallProperties *properties, GCancellable *cancellable, GError **error); void mm_modem_voice_list_calls (MMModemVoice *self, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); GList *mm_modem_voice_list_calls_finish (MMModemVoice *self, GAsyncResult *res, GError **error); GList *mm_modem_voice_list_calls_sync (MMModemVoice *self, GCancellable *cancellable, GError **error); void mm_modem_voice_delete_call (MMModemVoice *self, const gchar *call, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); gboolean mm_modem_voice_delete_call_finish (MMModemVoice *self, GAsyncResult *res, GError **error); gboolean mm_modem_voice_delete_call_sync (MMModemVoice *self, const gchar *call, GCancellable *cancellable, GError **error); void mm_modem_voice_hold_and_accept (MMModemVoice *self, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); gboolean mm_modem_voice_hold_and_accept_finish (MMModemVoice *self, GAsyncResult *res, GError **error); gboolean mm_modem_voice_hold_and_accept_sync (MMModemVoice *self, GCancellable *cancellable, GError **error); void mm_modem_voice_hangup_and_accept (MMModemVoice *self, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); gboolean mm_modem_voice_hangup_and_accept_finish (MMModemVoice *self, GAsyncResult *res, GError **error); gboolean mm_modem_voice_hangup_and_accept_sync (MMModemVoice *self, GCancellable *cancellable, GError **error); void mm_modem_voice_hangup_all (MMModemVoice *self, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); gboolean mm_modem_voice_hangup_all_finish (MMModemVoice *self, GAsyncResult *res, GError **error); gboolean mm_modem_voice_hangup_all_sync (MMModemVoice *self, GCancellable *cancellable, GError **error); void mm_modem_voice_transfer (MMModemVoice *self, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); gboolean mm_modem_voice_transfer_finish (MMModemVoice *self, GAsyncResult *res, GError **error); gboolean mm_modem_voice_transfer_sync (MMModemVoice *self, GCancellable *cancellable, GError **error); void mm_modem_voice_call_waiting_setup (MMModemVoice *self, gboolean enable, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); gboolean mm_modem_voice_call_waiting_setup_finish (MMModemVoice *self, GAsyncResult *res, GError **error); gboolean mm_modem_voice_call_waiting_setup_sync (MMModemVoice *self, gboolean enable, GCancellable *cancellable, GError **error); void mm_modem_voice_call_waiting_query (MMModemVoice *self, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); gboolean mm_modem_voice_call_waiting_query_finish (MMModemVoice *self, GAsyncResult *res, gboolean *status, GError **error); gboolean mm_modem_voice_call_waiting_query_sync (MMModemVoice *self, GCancellable *cancellable, gboolean *status, GError **error); G_END_DECLS #endif /* _MM_MODEM_VOICE_H_ */ ModemManager-1.23.4-dev/libmm-glib/mm-modem.c000066400000000000000000003270271456466623000206270ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * libmm-glib -- Access modem status & information from glib applications * * 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 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 Street, Fifth Floor, * Boston, MA 02110-1301 USA. * * Copyright (C) 2011 - 2012 Aleksander Morgado * Copyright (C) 2012 Google, Inc. */ #include #include #include "mm-common-helpers.h" #include "mm-errors-types.h" #include "mm-helpers.h" #include "mm-modem.h" /** * SECTION: mm-modem * @title: MMModem * @short_description: The Modem interface * * The #MMModem is an object providing access to the methods, signals and * properties of the Modem interface. * * When the modem is exposed and available in the bus, it is ensured that at * least this interface is also available. */ G_DEFINE_TYPE (MMModem, mm_modem, MM_GDBUS_TYPE_MODEM_PROXY) struct _MMModemPrivate { /* Common mutex to sync access */ GMutex mutex; PROPERTY_ARRAY_DECLARE (ports) PROPERTY_ARRAY_DECLARE (supported_modes) PROPERTY_ARRAY_DECLARE (supported_capabilities) PROPERTY_ARRAY_DECLARE (supported_bands) PROPERTY_ARRAY_DECLARE (current_bands) PROPERTY_OBJECT_DECLARE (unlock_retries, MMUnlockRetries) }; /*****************************************************************************/ /** * mm_modem_get_path: (skip) * @self: A #MMModem. * * Gets the DBus path of the #MMObject which implements this interface. * * Returns: (transfer none): The DBus path of the #MMObject object. Do not free * the returned value, it belongs to @self. * * Since: 1.0 */ const gchar * mm_modem_get_path (MMModem *self) { g_return_val_if_fail (MM_IS_MODEM (self), NULL); RETURN_NON_EMPTY_CONSTANT_STRING ( g_dbus_proxy_get_object_path (G_DBUS_PROXY (self))); } /** * mm_modem_dup_path: * @self: A #MMModem. * * Gets a copy of the DBus path of the #MMObject object which implements this * interface. * * Returns: (transfer full): The DBus path of the #MMObject. The returned value * should be freed with g_free(). * * Since: 1.0 */ gchar * mm_modem_dup_path (MMModem *self) { gchar *value; g_return_val_if_fail (MM_IS_MODEM (self), NULL); g_object_get (G_OBJECT (self), "g-object-path", &value, NULL); RETURN_NON_EMPTY_STRING (value); } /*****************************************************************************/ /** * mm_modem_get_sim_path: (skip) * @self: A #MMModem. * * Gets the DBus path of the #MMSim handled in this #MMModem. * * The returned value is only valid until the property changes so it is * only safe to use this function on the thread where @self was constructed. Use * mm_modem_dup_sim_path() if on another thread. * * Returns: (transfer none): The DBus path of the #MMSim handled in this * #MMModem, or %NULL if none available. Do not free the returned value, it * belongs to @self. * * Since: 1.0 */ const gchar * mm_modem_get_sim_path (MMModem *self) { g_return_val_if_fail (MM_IS_MODEM (self), NULL); RETURN_NON_EMPTY_CONSTANT_STRING (mm_gdbus_modem_get_sim (MM_GDBUS_MODEM (self))); } /** * mm_modem_dup_sim_path: * @self: A #MMModem. * * Gets a copy of the DBus path of the #MMSim handled in this #MMModem. * * Returns: (transfer full): The DBus path of the #MMSim handled in this * #MMModem, or %NULL if none available. The returned value should be freed * with g_free(). * * Since: 1.0 */ gchar * mm_modem_dup_sim_path (MMModem *self) { g_return_val_if_fail (MM_IS_MODEM (self), NULL); RETURN_NON_EMPTY_STRING ( mm_gdbus_modem_dup_sim (MM_GDBUS_MODEM (self))); } /*****************************************************************************/ /** * mm_modem_get_sim_slot_paths: * @self: A #MMModem. * * Gets the DBus paths of the #MMSim objects available in the different SIM * slots handled in this #MMModem. If a given SIM slot at a given index doesn't * have a SIM card available, an empty object path will be given. This list * includes the currently active SIM object path. * * The returned value is only valid until the property changes so it is * only safe to use this function on the thread where @self was constructed. Use * mm_modem_dup_sim_slot_paths() if on another thread. * * Returns: (transfer none): The DBus paths of the #MMSim objects handled in * this #MMModem, or %NULL if none available. Do not free the returned value, it * belongs to @self. * * Since: 1.16 */ const gchar * const * mm_modem_get_sim_slot_paths (MMModem *self) { g_return_val_if_fail (MM_IS_MODEM (self), NULL); return mm_gdbus_modem_get_sim_slots (MM_GDBUS_MODEM (self)); } /** * mm_modem_dup_sim_slot_paths: * @self: A #MMModem. * * Gets a copy of the DBus paths of the #MMSim objects available in the * different SIM slots handled in this #MMModem. If a given SIM slot at a given * index doesn't have a SIM card available, an empty object path will be given. * This list includes the currently active SIM object path. * * Returns: (transfer full): The DBus paths of the #MMSim objects handled in * this #MMModem, or %NULL if none available. The returned value should be * freed with g_strfreev(). * * Since: 1.16 */ gchar ** mm_modem_dup_sim_slot_paths (MMModem *self) { g_return_val_if_fail (MM_IS_MODEM (self), NULL); return mm_gdbus_modem_dup_sim_slots (MM_GDBUS_MODEM (self)); } /*****************************************************************************/ /** * mm_modem_get_primary_sim_slot: * @self: A #MMModem. * * Gets the SIM slot number of the primary active SIM. * * Returns: slot number, in the [1,N] range. * * Since: 1.16 */ guint mm_modem_get_primary_sim_slot (MMModem *self) { g_return_val_if_fail (MM_IS_MODEM (self), 0); return mm_gdbus_modem_get_primary_sim_slot (MM_GDBUS_MODEM (self)); } /*****************************************************************************/ /** * mm_modem_get_supported_capabilities: * @self: A #MMModem. * @capabilities: (out) (array length=n_capabilities): Return location for the * array of #MMModemCapability values. The returned array should be freed with * g_free() when no longer needed. * @n_capabilities: (out): Return location for the number of values in * @capabilities. * * Gets the list of combinations of generic families of access technologies * supported by this #MMModem. * * Returns: %TRUE if @capabilities and @n_capabilities are set, %FALSE * otherwise. * * Since: 1.0 */ /** * mm_modem_peek_supported_capabilities: * @self: A #MMModem. * @capabilities: (out) (array length=n_capabilities): Return location for the * array of #MMModemCapability values. Do not free the returned array, it is * owned by @self. * @n_capabilities: (out): Return location for the number of values in * @capabilities. * * Gets the list of combinations of generic families of access technologies * supported by this #MMModem. * * Returns: %TRUE if @capabilities and @n_capabilities are set, %FALSE * otherwise. * * Since: 1.0 */ PROPERTY_ARRAY_DEFINE (supported_capabilities, Modem, modem, MODEM, MMModemCapability, mm_common_capability_combinations_variant_to_garray) /*****************************************************************************/ /** * mm_modem_get_current_capabilities: * @self: A #MMModem. * * Gets the list of generic families of access technologies supported by this * #MMModem without a firmware reload or reinitialization. * * Returns: A bitmask of #MMModemCapability flags. * * Since: 1.0 */ MMModemCapability mm_modem_get_current_capabilities (MMModem *self) { g_return_val_if_fail (MM_IS_MODEM (self), MM_MODEM_CAPABILITY_NONE); return mm_gdbus_modem_get_current_capabilities (MM_GDBUS_MODEM (self)); } /*****************************************************************************/ /** * mm_modem_get_max_active_bearers: * @self: a #MMModem. * * Gets the maximum number of active packet data bearers this #MMModem supports * without enabling multiplexing support. * * POTS and CDMA2000-only devices support one active bearer, while GSM/UMTS * and LTE/5GNR capable devices (including 3GPP+3GPP3 multimode devices) may support * one or more active bearers, depending on the amount of physical ports exposed * by the device. * * Returns: the maximum number of active packet data bearers. * * Since: 1.0 */ guint mm_modem_get_max_active_bearers (MMModem *self) { g_return_val_if_fail (MM_IS_MODEM (self), 0); return mm_gdbus_modem_get_max_active_bearers (MM_GDBUS_MODEM (self)); } /*****************************************************************************/ /** * mm_modem_get_max_active_multiplexed_bearers: * @self: a #MMModem. * * Gets the maximum number of active packet data bearers this #MMModem supports * after enabling multiplexing support on one single network interface. * * Returns: the maximum number of active packet data bearers, or 0 if * multiplexing is not supported. * * Since: 1.18 */ guint mm_modem_get_max_active_multiplexed_bearers (MMModem *self) { g_return_val_if_fail (MM_IS_MODEM (self), 0); return mm_gdbus_modem_get_max_active_multiplexed_bearers (MM_GDBUS_MODEM (self)); } /*****************************************************************************/ /** * mm_modem_get_bearer_paths: * @self: A #MMModem. * * Gets the DBus paths of the #MMBearer handled in this #MMModem. * * The returned value is only valid until the property changes so it is * only safe to use this function on the thread where @self was constructed. Use * mm_modem_dup_bearer_paths() if on another thread. * * Returns: (transfer none): The DBus paths of the #MMBearer handled in this * #MMModem, or %NULL if none available. Do not free the returned value, it * belongs to @self. * * Since: 1.0 */ const gchar * const * mm_modem_get_bearer_paths (MMModem *self) { g_return_val_if_fail (MM_IS_MODEM (self), NULL); return mm_gdbus_modem_get_bearers (MM_GDBUS_MODEM (self)); } /** * mm_modem_dup_bearer_paths: * @self: A #MMModem. * * Gets a copy of the DBus paths of the #MMBearer handled in this #MMModem. * * Returns: (transfer full): The DBus paths of the #MMBearer handled in this * #MMModem, or %NULL if none available. The returned value should be freed * with g_strfreev(). * * Since: 1.0 */ gchar ** mm_modem_dup_bearer_paths (MMModem *self) { g_return_val_if_fail (MM_IS_MODEM (self), NULL); return mm_gdbus_modem_dup_bearers (MM_GDBUS_MODEM (self)); } /*****************************************************************************/ /** * mm_modem_get_manufacturer: * @self: A #MMModem. * * Gets the equipment manufacturer, as reported by this #MMModem. * * The returned value is only valid until the property changes so it is * only safe to use this function on the thread where @self was constructed. Use * mm_modem_dup_manufacturer() if on another thread. * * Returns: (transfer none): The equipment manufacturer, or %NULL if none * available. Do not free the returned value, it belongs to @self. * * Since: 1.0 */ const gchar * mm_modem_get_manufacturer (MMModem *self) { g_return_val_if_fail (MM_IS_MODEM (self), NULL); RETURN_NON_EMPTY_CONSTANT_STRING ( mm_gdbus_modem_get_manufacturer (MM_GDBUS_MODEM (self))); } /** * mm_modem_dup_manufacturer: * @self: A #MMModem. * * Gets a copy of the equipment manufacturer, as reported by this #MMModem. * * Returns: (transfer full): The equipment manufacturer, or %NULL if none * available. The returned value should be freed with g_free(). * * Since: 1.0 */ gchar * mm_modem_dup_manufacturer (MMModem *self) { g_return_val_if_fail (MM_IS_MODEM (self), NULL); RETURN_NON_EMPTY_STRING ( mm_gdbus_modem_dup_manufacturer (MM_GDBUS_MODEM (self))); } /*****************************************************************************/ /** * mm_modem_get_model: * @self: A #MMModem. * * Gets the equipment model, as reported by this #MMModem. * * The returned value is only valid until the property changes so it is * only safe to use this function on the thread where @self was constructed. Use * mm_modem_dup_model() if on another thread. * * Returns: (transfer none): The equipment model, or %NULL if none available. * Do not free the returned value, it belongs to @self. * * Since: 1.0 */ const gchar * mm_modem_get_model (MMModem *self) { g_return_val_if_fail (MM_IS_MODEM (self), NULL); RETURN_NON_EMPTY_CONSTANT_STRING ( mm_gdbus_modem_get_model (MM_GDBUS_MODEM (self))); } /** * mm_modem_dup_model: * @self: A #MMModem. * * Gets a copy of the equipment model, as reported by this #MMModem. * * Returns: (transfer full): The equipment model, or %NULL if none available. * The returned value should be freed with g_free(). * * Since: 1.0 */ gchar * mm_modem_dup_model (MMModem *self) { g_return_val_if_fail (MM_IS_MODEM (self), NULL); RETURN_NON_EMPTY_STRING ( mm_gdbus_modem_dup_model (MM_GDBUS_MODEM (self))); } /*****************************************************************************/ /** * mm_modem_get_revision: * @self: A #MMModem. * * Gets the equipment revision, as reported by this #MMModem. * * The returned value is only valid until the property changes so it is * only safe to use this function on the thread where @self was constructed. Use * mm_modem_dup_revision() if on another thread. * * Returns: (transfer none): The equipment revision, or %NULL if none available. * Do not free the returned value, it belongs to @self. * * Since: 1.0 */ const gchar * mm_modem_get_revision (MMModem *self) { g_return_val_if_fail (MM_IS_MODEM (self), NULL); RETURN_NON_EMPTY_CONSTANT_STRING ( mm_gdbus_modem_get_revision (MM_GDBUS_MODEM (self))); } /** * mm_modem_dup_revision: * @self: A #MMModem. * * Gets a copy of the equipment revision, as reported by this #MMModem. * * Returns: (transfer full): The equipment revision, or %NULL if none available. * The returned value should be freed with g_free(). * * Since: 1.0 */ gchar * mm_modem_dup_revision (MMModem *self) { g_return_val_if_fail (MM_IS_MODEM (self), NULL); RETURN_NON_EMPTY_STRING ( mm_gdbus_modem_dup_revision (MM_GDBUS_MODEM (self))); } /*****************************************************************************/ /** * mm_modem_get_carrier_configuration: * @self: A #MMModem. * * Gets the carrier-specific configuration (MCFG) in use, as reported by this * #MMModem. * * The returned value is only valid until the property changes so it is * only safe to use this function on the thread where @self was constructed. Use * mm_modem_dup_carrier_configuration() if on another thread. * * Returns: (transfer none): The carrier configuration, or %NULL if none * available. Do not free the returned value, it belongs to @self. * * Since: 1.12 */ const gchar * mm_modem_get_carrier_configuration (MMModem *self) { g_return_val_if_fail (MM_IS_MODEM (self), NULL); RETURN_NON_EMPTY_CONSTANT_STRING ( mm_gdbus_modem_get_carrier_configuration (MM_GDBUS_MODEM (self))); } /** * mm_modem_dup_carrier_configuration: * @self: A #MMModem. * * Gets a copy of the carrier-specific configuration (MCFG) in use, as reported * by this #MMModem. * * Returns: (transfer full): The carrier configuration, or %NULL if none * available. The returned value should be freed with g_free(). * * Since: 1.12 */ gchar * mm_modem_dup_carrier_configuration (MMModem *self) { g_return_val_if_fail (MM_IS_MODEM (self), NULL); RETURN_NON_EMPTY_STRING ( mm_gdbus_modem_dup_carrier_configuration (MM_GDBUS_MODEM (self))); } /*****************************************************************************/ /** * mm_modem_get_carrier_configuration_revision: * @self: A #MMModem. * * Gets the carrier-specific configuration revision in use, as reported by this * #MMModem. * * The returned value is only valid until the property changes so it is * only safe to use this function on the thread where @self was constructed. Use * mm_modem_dup_carrier_configuration() if on another thread. * * Returns: (transfer none): The carrier configuration revision, or %NULL if * none available. Do not free the returned value, it belongs to @self. * * Since: 1.12 */ const gchar * mm_modem_get_carrier_configuration_revision (MMModem *self) { g_return_val_if_fail (MM_IS_MODEM (self), NULL); RETURN_NON_EMPTY_CONSTANT_STRING ( mm_gdbus_modem_get_carrier_configuration_revision (MM_GDBUS_MODEM (self))); } /** * mm_modem_dup_carrier_configuration_revision: * @self: A #MMModem. * * Gets a copy of the carrier-specific configuration revision in use, as * reported by this #MMModem. * * Returns: (transfer full): The carrier configuration revision, or %NULL if * none available. The returned value should be freed with g_free(). * * Since: 1.12 */ gchar * mm_modem_dup_carrier_configuration_revision (MMModem *self) { g_return_val_if_fail (MM_IS_MODEM (self), NULL); RETURN_NON_EMPTY_STRING ( mm_gdbus_modem_dup_carrier_configuration_revision (MM_GDBUS_MODEM (self))); } /*****************************************************************************/ /** * mm_modem_get_hardware_revision: * @self: A #MMModem. * * Gets the equipment hardware revision, as reported by this #MMModem. * * The returned value is only valid until the property changes so it is * only safe to use this function on the thread where @self was constructed. Use * mm_modem_dup_hardware_revision() if on another thread. * * Returns: (transfer none): The equipment hardware revision, or %NULL if none * available. Do not free the returned value, it belongs to @self. * * Since: 1.8 */ const gchar * mm_modem_get_hardware_revision (MMModem *self) { g_return_val_if_fail (MM_IS_MODEM (self), NULL); RETURN_NON_EMPTY_CONSTANT_STRING ( mm_gdbus_modem_get_hardware_revision (MM_GDBUS_MODEM (self))); } /** * mm_modem_dup_hardware_revision: * @self: A #MMModem. * * Gets a copy of the equipment hardware revision, as reported by this #MMModem. * * Returns: (transfer full): The equipment hardware revision, or %NULL if none * available. The returned value should be freed with g_free(). * * Since: 1.8 */ gchar * mm_modem_dup_hardware_revision (MMModem *self) { g_return_val_if_fail (MM_IS_MODEM (self), NULL); RETURN_NON_EMPTY_STRING ( mm_gdbus_modem_dup_hardware_revision (MM_GDBUS_MODEM (self))); } /*****************************************************************************/ /** * mm_modem_get_device_identifier: * @self: A #MMModem. * * Gets a best-effort device identifier based on various device information like * model name, firmware revision, USB/PCI/PCMCIA IDs, and other properties. * * This ID is not guaranteed to be unique and may be shared between * identical devices with the same firmware, but is intended to be "unique * enough" for use as a casual device identifier for various user * experience operations. * * This is not the device's IMEI or ESN since those may not be available * before unlocking the device via a PIN. * * The returned value is only valid until the property changes so it is * only safe to use this function on the thread where @self was constructed. Use * mm_modem_dup_device_identifier() if on another thread. * * Returns: (transfer none): The device identifier, or %NULL if none available. * Do not free the returned value, it belongs to @self. * * Since: 1.0 */ const gchar * mm_modem_get_device_identifier (MMModem *self) { g_return_val_if_fail (MM_IS_MODEM (self), NULL); RETURN_NON_EMPTY_CONSTANT_STRING ( mm_gdbus_modem_get_device_identifier (MM_GDBUS_MODEM (self))); } /** * mm_modem_dup_device_identifier: * @self: A #MMModem. * * Gets a copy of a best-effort device identifier based on various device * information like model name, firmware revision, USB/PCI/PCMCIA IDs, and other * properties. * * This ID is not guaranteed to be unique and may be shared between * identical devices with the same firmware, but is intended to be "unique * enough" for use as a casual device identifier for various user * experience operations. * * This is not the device's IMEI or ESN since those may not be available * before unlocking the device via a PIN. * * Returns: (transfer full): The device identifier, or %NULL if none available. * The returned value should be freed with g_free(). * * Since: 1.0 */ gchar * mm_modem_dup_device_identifier (MMModem *self) { g_return_val_if_fail (MM_IS_MODEM (self), NULL); RETURN_NON_EMPTY_STRING ( mm_gdbus_modem_dup_device_identifier (MM_GDBUS_MODEM (self))); } /*****************************************************************************/ /** * mm_modem_get_device: * @self: A #MMModem. * * Gets the physical modem device reference (ie, USB, PCI, PCMCIA device), which * may be dependent upon the operating system. * * The returned value is only valid until the property changes so it is * only safe to use this function on the thread where @self was constructed. Use * mm_modem_dup_device() if on another thread. * * Returns: (transfer none): The device, or %NULL if none available. Do not free * the returned value, it belongs to @self. * * Since: 1.0 */ const gchar * mm_modem_get_device (MMModem *self) { g_return_val_if_fail (MM_IS_MODEM (self), NULL); RETURN_NON_EMPTY_CONSTANT_STRING ( mm_gdbus_modem_get_device (MM_GDBUS_MODEM (self))); } /** * mm_modem_dup_device: * @self: A #MMModem. * * Gets a copy of the physical modem device reference (ie, USB, PCI, PCMCIA * device), which may be dependent upon the operating system. * * Returns: (transfer full): The device, or %NULL if none available. The * returned value should be freed with g_free(). * * Since: 1.0 */ gchar * mm_modem_dup_device (MMModem *self) { g_return_val_if_fail (MM_IS_MODEM (self), NULL); RETURN_NON_EMPTY_STRING ( mm_gdbus_modem_dup_device (MM_GDBUS_MODEM (self))); } /*****************************************************************************/ /** * mm_modem_get_physdev: * @self: A #MMModem. * * Gets the physical modem device path (ie, USB, PCI, PCMCIA device), which * may be dependent upon the operating system. * * The returned value is only valid until the property changes so it is * only safe to use this function on the thread where @self was constructed. Use * mm_modem_dup_physdev() if on another thread. * * Returns: (transfer none): The physdev path, or %NULL if none available. Do not * free the returned value, it belongs to @self. * * Since: 1.22 */ const gchar * mm_modem_get_physdev (MMModem *self) { g_return_val_if_fail (MM_IS_MODEM (self), NULL); RETURN_NON_EMPTY_CONSTANT_STRING ( mm_gdbus_modem_get_physdev (MM_GDBUS_MODEM (self))); } /** * mm_modem_dup_physdev: * @self: A #MMModem. * * Gets a copy of the physical modem device path (ie, USB, PCI, PCMCIA * device), which may be dependent upon the operating system. * * Returns: (transfer full): The physdev path, or %NULL if none available. The * returned value should be freed with g_free(). * * Since: 1.22 */ gchar * mm_modem_dup_physdev (MMModem *self) { g_return_val_if_fail (MM_IS_MODEM (self), NULL); RETURN_NON_EMPTY_STRING ( mm_gdbus_modem_dup_physdev (MM_GDBUS_MODEM (self))); } /*****************************************************************************/ /** * mm_modem_get_drivers: * @self: A #MMModem. * * Gets the Operating System device drivers handling communication with the * modem hardware. * * The returned value is only valid until the property changes so it is * only safe to use this function on the thread where @self was constructed. Use * mm_modem_dup_drivers() if on another thread. * * Returns: (transfer none): The drivers, or %NULL if none available. Do not * free the returned value, it belongs to @self. * * Since: 1.0 */ const gchar * const * mm_modem_get_drivers (MMModem *self) { g_return_val_if_fail (MM_IS_MODEM (self), NULL); return mm_gdbus_modem_get_drivers (MM_GDBUS_MODEM (self)); } /** * mm_modem_dup_drivers: * @self: A #MMModem. * * Gets a copy of the Operating System device driver handling communication with * the modem hardware. * * Returns: (transfer full): The drivers, or %NULL if none available. The * returned value should be freed with g_strfreev(). * * Since: 1.0 */ gchar ** mm_modem_dup_drivers (MMModem *self) { g_return_val_if_fail (MM_IS_MODEM (self), NULL); return mm_gdbus_modem_dup_drivers (MM_GDBUS_MODEM (self)); } /*****************************************************************************/ /** * mm_modem_get_plugin: * @self: A #MMModem. * * Gets the name of the plugin handling this #MMModem. * * The returned value is only valid until the property changes so it is * only safe to use this function on the thread where @self was constructed. Use * mm_modem_dup_plugin() if on another thread. * * Returns: (transfer none): The name of the plugin, or %NULL if none *available. Do not free the returned value, it belongs to @self. * * Since: 1.0 */ const gchar * mm_modem_get_plugin (MMModem *self) { g_return_val_if_fail (MM_IS_MODEM (self), NULL); RETURN_NON_EMPTY_CONSTANT_STRING ( mm_gdbus_modem_get_plugin (MM_GDBUS_MODEM (self))); } /** * mm_modem_dup_plugin: * @self: A #MMModem. * * Gets a copy of the name of the plugin handling this #MMModem. * * Returns: (transfer full): The name of the plugin, or %NULL if none available. * The returned value should be freed with g_free(). * * Since: 1.0 */ gchar * mm_modem_dup_plugin (MMModem *self) { g_return_val_if_fail (MM_IS_MODEM (self), NULL); RETURN_NON_EMPTY_STRING ( mm_gdbus_modem_dup_plugin (MM_GDBUS_MODEM (self))); } /*****************************************************************************/ /** * mm_modem_get_primary_port: * @self: A #MMModem. * * Gets the name of the primary port controlling this #MMModem. * * The returned value is only valid until the property changes so it is * only safe to use this function on the thread where @self was constructed. Use * mm_modem_dup_primary_port() if on another thread. * * Returns: (transfer none): The name of the primary port. Do not free the * returned value, it belongs to @self. * * Since: 1.0 */ const gchar * mm_modem_get_primary_port (MMModem *self) { g_return_val_if_fail (MM_IS_MODEM (self), NULL); RETURN_NON_EMPTY_CONSTANT_STRING ( mm_gdbus_modem_get_primary_port (MM_GDBUS_MODEM (self))); } /** * mm_modem_dup_primary_port: * @self: A #MMModem. * * Gets a copy of the name of the primary port controlling this #MMModem. * * Returns: (transfer full): The name of the primary port. The returned value * should be freed with g_free(). * * Since: 1.0 */ gchar * mm_modem_dup_primary_port (MMModem *self) { g_return_val_if_fail (MM_IS_MODEM (self), NULL); RETURN_NON_EMPTY_STRING ( mm_gdbus_modem_dup_primary_port (MM_GDBUS_MODEM (self))); } /*****************************************************************************/ /** * mm_modem_peek_ports: * @self: A #MMModem. * @ports: (out) (array length=n_ports) (transfer none): Return location for the * array of #MMModemPortInfo values. Do not free the returned value, it is * owned by @self. * @n_ports: (out): Return location for the number of values in @ports. * * Gets the list of ports in the modem. * * Returns: %TRUE if @ports and @n_ports are set, %FALSE otherwise. * * Since: 1.0 */ /** * mm_modem_get_ports: * @self: A #MMModem. * @ports: (out) (array length=n_ports): Return location for the array of * #MMModemPortInfo values. The returned array should be freed with * mm_modem_port_info_array_free() when no longer needed. * @n_ports: (out): Return location for the number of values in @ports. * * Gets the list of ports in the modem. * * Returns: %TRUE if @ports and @n_ports are set, %FALSE otherwise. * * Since: 1.0 */ PROPERTY_ARRAY_DEFINE_DEEP (ports, Modem, modem, MODEM, MMModemPortInfo, mm_common_ports_variant_to_garray, mm_common_ports_garray_to_array) /*****************************************************************************/ /** * mm_modem_get_equipment_identifier: * @self: A #MMModem. * * Gets the identity of the #MMModem. * * This will be the IMEI number for GSM devices and the hex-format ESN/MEID * for CDMA devices. * * The returned value is only valid until the property changes so it is * only safe to use this function on the thread where @self was constructed. Use * mm_modem_dup_equipment_identifier() if on another thread. * * Returns: (transfer none): The equipment identifier, or %NULL if none * available. Do not free the returned value, it belongs to @self. * * Since: 1.0 */ const gchar * mm_modem_get_equipment_identifier (MMModem *self) { g_return_val_if_fail (MM_IS_MODEM (self), NULL); RETURN_NON_EMPTY_CONSTANT_STRING ( mm_gdbus_modem_get_equipment_identifier (MM_GDBUS_MODEM (self))); } /** * mm_modem_dup_equipment_identifier: * @self: A #MMModem. * * Gets a copy of the identity of the #MMModem. * * This will be the IMEI number for GSM devices and the hex-format ESN/MEID * for CDMA devices. * * Returns: (transfer full): The equipment identifier, or %NULL if none * available. The returned value should be freed with g_free(). * * Since: 1.0 */ gchar * mm_modem_dup_equipment_identifier (MMModem *self) { g_return_val_if_fail (MM_IS_MODEM (self), NULL); RETURN_NON_EMPTY_STRING ( mm_gdbus_modem_dup_equipment_identifier (MM_GDBUS_MODEM (self))); } /*****************************************************************************/ /** * mm_modem_get_own_numbers: (skip) * @self: A #MMModem. * * Gets the list of numbers (e.g. MSISDN in 3GPP) being currently handled by * this modem. * * The returned value is only valid until the property changes so it is * only safe to use this function on the thread where @self was constructed. Use * mm_modem_dup_own_numbers() if on another thread. * * Returns: (transfer none): The list of own numbers or %NULL if none available. * Do not free the returned value, it belongs to @self. * * Since: 1.0 */ const gchar *const * mm_modem_get_own_numbers (MMModem *self) { const gchar *const *own; g_return_val_if_fail (MM_IS_MODEM (self), NULL); own = mm_gdbus_modem_get_own_numbers (MM_GDBUS_MODEM (self)); return (own && own[0] ? own : NULL); } /** * mm_modem_dup_own_numbers: * @self: A #MMModem. * * Gets a copy of the list of numbers (e.g. MSISDN in 3GPP) being currently * handled by this modem. * * Returns: (transfer full): The list of own numbers or %NULL if none is * available. The returned value should be freed with g_strfreev(). * * Since: 1.0 */ gchar ** mm_modem_dup_own_numbers (MMModem *self) { gchar **own; g_return_val_if_fail (MM_IS_MODEM (self), NULL); own = mm_gdbus_modem_dup_own_numbers (MM_GDBUS_MODEM (self)); if (own && own[0]) return own; g_strfreev (own); return NULL; } /*****************************************************************************/ /** * mm_modem_get_unlock_required: * @self: A #MMModem. * * Gets current lock state of the #MMModem. * * Returns: A #MMModemLock value, specifying the current lock state. * * Since: 1.0 */ MMModemLock mm_modem_get_unlock_required (MMModem *self) { g_return_val_if_fail (MM_IS_MODEM (self), MM_MODEM_LOCK_UNKNOWN); return (MMModemLock) mm_gdbus_modem_get_unlock_required (MM_GDBUS_MODEM (self)); } /*****************************************************************************/ /** * mm_modem_get_unlock_retries: * @self: A #MMModem. * * Gets a #MMUnlockRetries object, which provides, for each * MMModemLock handled by the modem, the * number of PIN tries remaining before the code becomes blocked (requiring a * PUK) or permanently blocked. * * The values reported by @self are not updated when the values in the * interface change. Instead, the client is expected to call * mm_modem_get_unlock_retries() again to get a new #MMUnlockRetries with the * new values. * * Returns: (transfer full): A #MMUnlockRetries that must be freed with * g_object_unref() or %NULL if unknown. * * Since: 1.0 */ /** * mm_modem_peek_unlock_retries: * @self: A #MMModem. * * Gets a #MMUnlockRetries object, which provides, for each * MMModemLock handled by the modem, the * number of PIN tries remaining before the code becomes blocked (requiring a * PUK) or permanently blocked. * * The returned value is only valid until the property changes so it is * only safe to use this function on the thread where @self was constructed. Use * mm_modem_get_unlock_retries() if on another thread. * * Returns: (transfer none): A #MMUnlockRetries. Do not free the returned value, * it belongs to @self. * * Since: 1.0 */ PROPERTY_OBJECT_DEFINE (unlock_retries, Modem, modem, MODEM, MMUnlockRetries, mm_unlock_retries_new_from_dictionary) /*****************************************************************************/ /** * mm_modem_get_state: * @self: A #MMModem. * * Gets the overall state of the #MMModem. * * Returns: A #MMModemState value. * * Since: 1.0 */ MMModemState mm_modem_get_state (MMModem *self) { g_return_val_if_fail (MM_IS_MODEM (self), MM_MODEM_STATE_UNKNOWN); return (MMModemState) mm_gdbus_modem_get_state (MM_GDBUS_MODEM (self)); } /*****************************************************************************/ /** * mm_modem_get_state_failed_reason: * @self: A #MMModem. * * Gets the reason specifying why the modem is in #MM_MODEM_STATE_FAILED state. * * Returns: A #MMModemStateFailedReason value. * * Since: 1.0 */ MMModemStateFailedReason mm_modem_get_state_failed_reason (MMModem *self) { g_return_val_if_fail (MM_IS_MODEM (self), MM_MODEM_STATE_FAILED_REASON_UNKNOWN); return (MMModemStateFailedReason) mm_gdbus_modem_get_state_failed_reason (MM_GDBUS_MODEM (self)); } /*****************************************************************************/ /** * mm_modem_get_power_state: * @self: A #MMModem. * * Gets the power state of the #MMModem. * * Returns: A #MMModemPowerState value. * * Since: 1.0 */ MMModemPowerState mm_modem_get_power_state (MMModem *self) { g_return_val_if_fail (MM_IS_MODEM (self), MM_MODEM_POWER_STATE_UNKNOWN); return (MMModemPowerState) mm_gdbus_modem_get_power_state (MM_GDBUS_MODEM (self)); } /*****************************************************************************/ /** * mm_modem_get_access_technologies: * @self: A #MMModem. * * Gets the current network access technology used by the #MMModem to * communicate with the network. * * Returns: A ##MMModemAccessTechnology value. * * Since: 1.0 */ MMModemAccessTechnology mm_modem_get_access_technologies (MMModem *self) { g_return_val_if_fail (MM_IS_MODEM (self), MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN); return (MMModemAccessTechnology) mm_gdbus_modem_get_access_technologies (MM_GDBUS_MODEM (self)); } /*****************************************************************************/ /** * mm_modem_get_signal_quality: * @self: A #MMModem. * @recent: (out): Return location for the flag specifying if the signal quality * value was recent or not. * * Gets the signal quality value in percent (0 - 100) of the dominant access * technology the #MMModem is using to communicate with the network. * * Always 0 for POTS devices. * * Returns: The signal quality. * * Since: 1.0 */ guint mm_modem_get_signal_quality (MMModem *self, gboolean *recent) { GVariant *variant; gboolean is_recent = FALSE; guint quality = 0; g_return_val_if_fail (MM_IS_MODEM (self), 0); variant = mm_gdbus_modem_dup_signal_quality (MM_GDBUS_MODEM (self)); if (variant) { g_variant_get (variant, "(ub)", &quality, &is_recent); g_variant_unref (variant); } if (recent) *recent = is_recent; return quality; } /*****************************************************************************/ /** * mm_modem_get_supported_modes: * @self: A #MMModem. * @modes: (out) (array length=n_modes): Return location for the array of * #MMModemModeCombination structs. The returned array should be freed with * g_free() when no longer needed. * @n_modes: (out): Return location for the number of values in @modes. * * Gets the list of supported mode combinations. * * Returns: %TRUE if @modes and @n_modes are set, %FALSE otherwise. * * Since: 1.0 */ /** * mm_modem_peek_supported_modes: * @self: A #MMModem. * @modes: (out) (array length=n_modes): Return location for the array of * #MMModemModeCombination values. Do not free the returned array, it is owned * by @self. * @n_modes: (out): Return location for the number of values in @modes. * * Gets the list of supported mode combinations. * * Returns: %TRUE if @modes and @n_modes are set, %FALSE otherwise. * * Since: 1.0 */ PROPERTY_ARRAY_DEFINE (supported_modes, Modem, modem, MODEM, MMModemModeCombination, mm_common_mode_combinations_variant_to_garray) /*****************************************************************************/ /** * mm_modem_get_current_modes: * @self: A #MMModem. * @allowed: (out): Return location for a bitmask of #MMModemMode values. * @preferred: (out): Return location for a #MMModemMode value. * * Gets the list of modes specifying the access technologies (eg 2G/3G/4G) * the #MMModem is currently allowed to use when connecting to a network, as * well as the preferred one, if any. * * Returns: %TRUE if @allowed and @preferred are set, %FALSE otherwise. * * Since: 1.0 */ gboolean mm_modem_get_current_modes (MMModem *self, MMModemMode *allowed, MMModemMode *preferred) { GVariant *variant; g_return_val_if_fail (MM_IS_MODEM (self), FALSE); g_return_val_if_fail (allowed != NULL, FALSE); g_return_val_if_fail (preferred != NULL, FALSE); variant = mm_gdbus_modem_dup_current_modes (MM_GDBUS_MODEM (self)); if (variant) { g_variant_get (variant, "(uu)", allowed, preferred); g_variant_unref (variant); return TRUE; } return FALSE; } /*****************************************************************************/ /** * mm_modem_get_supported_bands: * @self: A #MMModem. * @bands: (out) (array length=n_bands): Return location for the array of * #MMModemBand values. The returned array should be freed with g_free() when * no longer needed. * @n_bands: (out): Return location for the number of values in @bands. * * Gets the list of radio frequency and technology bands supported by the * #MMModem. * * For POTS devices, only #MM_MODEM_BAND_ANY will be returned in @bands. * * Returns: %TRUE if @bands and @n_bands are set, %FALSE otherwise. * * Since: 1.0 */ /** * mm_modem_peek_supported_bands: * @self: A #MMModem. * @bands: (out) (array length=n_bands): Return location for the array of * #MMModemBand values. Do not free the returned array, it is owned by @self. * @n_bands: (out): Return location for the number of values in @bands. * * Gets the list of radio frequency and technology bands supported by the * #MMModem. * * For POTS devices, only #MM_MODEM_BAND_ANY will be returned in @bands. * * Returns: %TRUE if @bands and @n_bands are set, %FALSE otherwise. * * Since: 1.0 */ PROPERTY_ARRAY_DEFINE (supported_bands, Modem, modem, MODEM, MMModemBand, mm_common_bands_variant_to_garray) /*****************************************************************************/ /** * mm_modem_get_current_bands: * @self: A #MMModem. * @bands: (out) (array length=n_bands): Return location for the array of * #MMModemBand values. The returned array should be freed with g_free() when * no longer needed. * @n_bands: (out): Return location for the number of values in @bands. * * Gets the list of radio frequency and technology bands the #MMModem is * currently using when connecting to a network. * * For POTS devices, only the #MM_MODEM_BAND_ANY band is supported. * * Returns: %TRUE if @bands and @n_bands are set, %FALSE otherwise. * * Since: 1.0 */ /** * mm_modem_peek_current_bands: * @self: A #MMModem. * @bands: (out) (array length=n_bands): Return location for the array of * #MMModemBand values. Do not free the returned value, it is owned by @self. * @n_bands: (out): Return location for the number of values in @bands. * * Gets the list of radio frequency and technology bands the #MMModem is * currently using when connecting to a network. * * For POTS devices, only the #MM_MODEM_BAND_ANY band is supported. * * Returns: %TRUE if @bands and @n_bands are set, %FALSE otherwise. * * Since: 1.0 */ PROPERTY_ARRAY_DEFINE (current_bands, Modem, modem, MODEM, MMModemBand, mm_common_bands_variant_to_garray) /*****************************************************************************/ /** * mm_modem_get_supported_ip_families: * @self: A #MMModem. * * Gets the list of supported IP families. * * Returns: A bitmask of #MMBearerIpFamily values. * * Since: 1.0 */ MMBearerIpFamily mm_modem_get_supported_ip_families (MMModem *self) { g_return_val_if_fail (MM_IS_MODEM (self), MM_BEARER_IP_FAMILY_NONE); return (MMBearerIpFamily) mm_gdbus_modem_get_supported_ip_families (MM_GDBUS_MODEM (self)); } /*****************************************************************************/ /** * mm_modem_enable_finish: * @self: A #MMModem. * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to * mm_modem_enable(). * @error: Return location for error or %NULL. * * Finishes an operation started with mm_modem_enable(). * * Returns: %TRUE if the modem was properly enabled, %FALSE if @error is set. * * Since: 1.0 */ gboolean mm_modem_enable_finish (MMModem *self, GAsyncResult *res, GError **error) { g_return_val_if_fail (MM_IS_MODEM (self), FALSE); return mm_gdbus_modem_call_enable_finish (MM_GDBUS_MODEM (self), res, error); } /** * mm_modem_enable: * @self: A #MMModem. * @cancellable: (allow-none): A #GCancellable or %NULL. * @callback: A #GAsyncReadyCallback to call when the request is satisfied or * %NULL. * @user_data: User data to pass to @callback. * * Asynchronously tries to enable the #MMModem. When enabled, the modem's radio * is powered on and data sessions, voice calls, location services, and Short * Message Service may be available. * * When the operation is finished, @callback will be invoked in the * thread-default main loop * of the thread you are calling this method from. You can then call * mm_modem_enable_finish() to get the result of the operation. * * See mm_modem_enable_sync() for the synchronous, blocking version of this * method. * * Since: 1.0 */ void mm_modem_enable (MMModem *self, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { g_return_if_fail (MM_IS_MODEM (self)); mm_gdbus_modem_call_enable (MM_GDBUS_MODEM (self), TRUE, cancellable, callback, user_data); } /** * mm_modem_enable_sync: * @self: A #MMModem. * @cancellable: (allow-none): A #GCancellable or %NULL. * @error: Return location for error or %NULL. * * Synchronously tries to enable the #MMModem. When enabled, the modem's radio * is powered on and data sessions, voice calls, location services, and Short * Message Service may be available. * * The calling thread is blocked until a reply is received. See * mm_modem_enable() for the asynchronous version of this method. * * Returns: %TRUE if the modem was properly enabled, %FALSE if @error is set. * * Since: 1.0 */ gboolean mm_modem_enable_sync (MMModem *self, GCancellable *cancellable, GError **error) { g_return_val_if_fail (MM_IS_MODEM (self), FALSE); return mm_gdbus_modem_call_enable_sync (MM_GDBUS_MODEM (self), TRUE, cancellable, error); } /*****************************************************************************/ /** * mm_modem_disable_finish: * @self: A #MMModem. * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to * mm_modem_disable(). * @error: Return location for error or %NULL. * * Finishes an operation started with mm_modem_disable(). * * Returns: %TRUE if the modem was properly disabled, %FALSE if @error is set. * * Since: 1.0 */ gboolean mm_modem_disable_finish (MMModem *self, GAsyncResult *res, GError **error) { g_return_val_if_fail (MM_IS_MODEM (self), FALSE); return mm_gdbus_modem_call_enable_finish (MM_GDBUS_MODEM (self), res, error); } /** * mm_modem_disable: * @self: A #MMModem. * @cancellable: (allow-none): A #GCancellable or %NULL. * @callback: A #GAsyncReadyCallback to call when the request is satisfied or * %NULL. * @user_data: User data to pass to @callback. * * Asynchronously tries to disable the #MMModem. When disabled, the modem enters * low-power state and no network-related operations are available. * * When the operation is finished, @callback will be invoked in the * thread-default main loop * of the thread you are calling this method from. You can then call * mm_modem_disable_finish() to get the result of the operation. * * See mm_modem_disable_sync() for the synchronous, blocking version of this * method. * * Since: 1.0 */ void mm_modem_disable (MMModem *self, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { g_return_if_fail (MM_IS_MODEM (self)); mm_gdbus_modem_call_enable (MM_GDBUS_MODEM (self), FALSE, cancellable, callback, user_data); } /** * mm_modem_disable_sync: * @self: A #MMModem. * @cancellable: (allow-none): A #GCancellable or %NULL. * @error: Return location for error or %NULL. * * Synchronously tries to disable the #MMModem. When disabled, the modem enters * low-power state and no network-related operations are available. * * The calling thread is blocked until a reply is received. See * mm_modem_disable() for the asynchronous version of this method. * * Returns: %TRUE if the modem was properly disabled, %FALSE if @error is set. * * Since: 1.0 */ gboolean mm_modem_disable_sync (MMModem *self, GCancellable *cancellable, GError **error) { g_return_val_if_fail (MM_IS_MODEM (self), FALSE); return mm_gdbus_modem_call_enable_sync (MM_GDBUS_MODEM (self), FALSE, cancellable, error); } /*****************************************************************************/ typedef struct { gchar **bearer_paths; GList *bearer_objects; guint i; } ListBearersContext; static void bearer_object_list_free (GList *list) { g_list_free_full (list, g_object_unref); } static void list_bearers_context_free (ListBearersContext *ctx) { g_strfreev (ctx->bearer_paths); bearer_object_list_free (ctx->bearer_objects); g_slice_free (ListBearersContext, ctx); } /** * mm_modem_list_bearers_finish: * @self: A #MMModem. * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to * mm_modem_list_bearers(). * @error: Return location for error or %NULL. * * Finishes an operation started with mm_modem_list_bearers(). * * Returns: (transfer full) (element-type ModemManager.Bearer): The list of * #MMBearer objects, or %NULL if either none found or if @error is set. * * Since: 1.0 */ GList * mm_modem_list_bearers_finish (MMModem *self, GAsyncResult *res, GError **error) { g_return_val_if_fail (MM_IS_MODEM (self), NULL); return g_task_propagate_pointer (G_TASK (res), error); } static void create_next_bearer (GTask *task); static void modem_list_bearers_build_object_ready (GDBusConnection *connection, GAsyncResult *res, GTask *task) { GObject *bearer; GError *error = NULL; GObject *source_object; ListBearersContext *ctx; source_object = g_async_result_get_source_object (res); bearer = g_async_initable_new_finish (G_ASYNC_INITABLE (source_object), res, &error); g_object_unref (source_object); if (error) { g_task_return_error (task, error); g_object_unref (task); return; } ctx = g_task_get_task_data (task); /* Keep the object */ ctx->bearer_objects = g_list_prepend (ctx->bearer_objects, bearer); /* If no more bearers, just end here. */ if (!ctx->bearer_paths[++ctx->i]) { GList *bearer_objects; bearer_objects = g_list_copy_deep (ctx->bearer_objects, (GCopyFunc)g_object_ref, NULL); g_task_return_pointer (task, bearer_objects, (GDestroyNotify)bearer_object_list_free); g_object_unref (task); return; } /* Keep on creating next object */ create_next_bearer (task); } static void create_next_bearer (GTask *task) { MMModem *self; ListBearersContext *ctx; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); g_async_initable_new_async (MM_TYPE_BEARER, G_PRIORITY_DEFAULT, g_task_get_cancellable (task), (GAsyncReadyCallback)modem_list_bearers_build_object_ready, task, "g-flags", G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START, "g-name", MM_DBUS_SERVICE, "g-connection", g_dbus_proxy_get_connection (G_DBUS_PROXY (self)), "g-object-path", ctx->bearer_paths[ctx->i], "g-interface-name", "org.freedesktop.ModemManager1.Bearer", NULL); } /** * mm_modem_list_bearers: * @self: A #MMModem. * @cancellable: (allow-none): A #GCancellable or %NULL. * @callback: A #GAsyncReadyCallback to call when the request is satisfied or * %NULL. * @user_data: User data to pass to @callback. * * Asynchronously lists the packet data bearers in the #MMModem. * * When the operation is finished, @callback will be invoked in the * thread-default main loop * of the thread you are calling this method from. You can then call * mm_modem_list_bearers_finish() to get the result of the operation. * * See mm_modem_list_bearers_sync() for the synchronous, blocking version of * this method. * * Since: 1.0 */ void mm_modem_list_bearers (MMModem *self, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { ListBearersContext *ctx; GTask *task; g_return_if_fail (MM_IS_MODEM (self)); ctx = g_slice_new0 (ListBearersContext); /* Read from the property, skip List() */ ctx->bearer_paths = mm_gdbus_modem_dup_bearers (MM_GDBUS_MODEM (self)); task = g_task_new (self, cancellable, callback, user_data); g_task_set_task_data (task, ctx, (GDestroyNotify)list_bearers_context_free); /* If no bearers, just end here. */ if (!ctx->bearer_paths || !ctx->bearer_paths[0]) { g_task_return_pointer (task, NULL, NULL); g_object_unref (task); return; } /* Got list of paths. If at least one found, start creating objects for each */ ctx->i = 0; create_next_bearer (task); } /** * mm_modem_list_bearers_sync: * @self: A #MMModem. * @cancellable: (allow-none): A #GCancellable or %NULL. * @error: Return location for error or %NULL. * * Synchronously lists the packet data bearers in the #MMModem. * * The calling thread is blocked until a reply is received. See * mm_modem_list_bearers() for the asynchronous version of this method. * * Returns: (transfer full) (element-type ModemManager.Bearer): The list of * #MMBearer objects, or %NULL if either none found or if @error is set. * * Since: 1.0 */ GList * mm_modem_list_bearers_sync (MMModem *self, GCancellable *cancellable, GError **error) { GList *bearer_objects = NULL; gchar **bearer_paths = NULL; guint i; g_return_val_if_fail (MM_IS_MODEM (self), NULL); /* Read from the property, skip List() */ bearer_paths = mm_gdbus_modem_dup_bearers (MM_GDBUS_MODEM (self)); /* Only non-empty lists are returned */ if (!bearer_paths) return NULL; for (i = 0; bearer_paths[i]; i++) { GObject *bearer; bearer = g_initable_new (MM_TYPE_BEARER, cancellable, error, "g-flags", G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START, "g-name", MM_DBUS_SERVICE, "g-connection", g_dbus_proxy_get_connection (G_DBUS_PROXY (self)), "g-object-path", bearer_paths[i], "g-interface-name", "org.freedesktop.ModemManager1.Bearer", NULL); if (!bearer) { bearer_object_list_free (bearer_objects); g_strfreev (bearer_paths); return NULL; } /* Keep the object */ bearer_objects = g_list_prepend (bearer_objects, bearer); } g_strfreev (bearer_paths); return bearer_objects; } /*****************************************************************************/ /** * mm_modem_create_bearer_finish: * @self: A #MMModem. * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to * mm_modem_create_bearer(). * @error: Return location for error or %NULL. * * Finishes an operation started with mm_modem_create_bearer(). * * Returns: (transfer full): A newly created #MMBearer, or %NULL if @error is * set. * * Since: 1.0 */ MMBearer * mm_modem_create_bearer_finish (MMModem *self, GAsyncResult *res, GError **error) { g_return_val_if_fail (MM_IS_MODEM (self), NULL); return g_task_propagate_pointer (G_TASK (res), error); } static void modem_new_bearer_ready (GDBusConnection *connection, GAsyncResult *res, GTask *task) { GError *error = NULL; GObject *bearer; GObject *source_object; source_object = g_async_result_get_source_object (res); bearer = g_async_initable_new_finish (G_ASYNC_INITABLE (source_object), res, &error); g_object_unref (source_object); if (error) g_task_return_error (task, error); else g_task_return_pointer (task, bearer, g_object_unref); g_object_unref (task); } static void modem_create_bearer_ready (MMModem *self, GAsyncResult *res, GTask *task) { GError *error = NULL; gchar *bearer_path = NULL; if (!mm_gdbus_modem_call_create_bearer_finish (MM_GDBUS_MODEM (self), &bearer_path, res, &error)) { g_task_return_error (task, error); g_object_unref (task); g_free (bearer_path); return; } g_async_initable_new_async (MM_TYPE_BEARER, G_PRIORITY_DEFAULT, g_task_get_cancellable (task), (GAsyncReadyCallback)modem_new_bearer_ready, task, "g-flags", G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START, "g-name", MM_DBUS_SERVICE, "g-connection", g_dbus_proxy_get_connection (G_DBUS_PROXY (self)), "g-object-path", bearer_path, "g-interface-name", "org.freedesktop.ModemManager1.Bearer", NULL); g_free (bearer_path); } /** * mm_modem_create_bearer: * @self: A #MMModem. * @properties: A #MMBearerProperties object with the properties to use. * @cancellable: (allow-none): A #GCancellable or %NULL. * @callback: A #GAsyncReadyCallback to call when the request is satisfied or * %NULL. * @user_data: User data to pass to @callback. * * Asynchronously creates a new packet data bearer in the #MMModem. * * This request may fail if the modem does not support additional bearers, * if too many bearers are already defined, or if @properties are invalid. * * See CreateBearer * to check which properties may be passed. * * When the operation is finished, @callback will be invoked in the * thread-default main loop * of the thread you are calling this method from. You can then call * mm_modem_create_bearer_finish() to get the result of the operation. * * See mm_modem_create_bearer_sync() for the synchronous, blocking version of * this method. * * Since: 1.0 */ void mm_modem_create_bearer (MMModem *self, MMBearerProperties *properties, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; GVariant *dictionary; g_return_if_fail (MM_IS_MODEM (self)); task = g_task_new (self, cancellable, callback, user_data); dictionary = mm_bearer_properties_get_dictionary (properties); mm_gdbus_modem_call_create_bearer ( MM_GDBUS_MODEM (self), dictionary, cancellable, (GAsyncReadyCallback)modem_create_bearer_ready, task); g_variant_unref (dictionary); } /** * mm_modem_create_bearer_sync: * @self: A #MMModem. * @properties: A #MMBearerProperties object with the properties to use. * @cancellable: (allow-none): A #GCancellable or %NULL. * @error: Return location for error or %NULL. * * Synchronously creates a new packet data bearer in the #MMModem. * * This request may fail if the modem does not support additional bearers, * if too many bearers are already defined, or if @properties are invalid. * * See CreateBearer * to check which properties may be passed. * * The calling thread is blocked until a reply is received. See * mm_modem_create_bearer() for the asynchronous version of this method. * * Returns: (transfer full): A newly created #MMBearer, or %NULL if @error is * set. * * Since: 1.0 */ MMBearer * mm_modem_create_bearer_sync (MMModem *self, MMBearerProperties *properties, GCancellable *cancellable, GError **error) { GObject *bearer = NULL; gchar *bearer_path = NULL; GVariant *dictionary; g_return_val_if_fail (MM_IS_MODEM (self), NULL); dictionary = mm_bearer_properties_get_dictionary (properties); mm_gdbus_modem_call_create_bearer_sync (MM_GDBUS_MODEM (self), dictionary, &bearer_path, cancellable, error); if (bearer_path) { bearer = g_initable_new (MM_TYPE_BEARER, cancellable, error, "g-flags", G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START, "g-name", MM_DBUS_SERVICE, "g-connection", g_dbus_proxy_get_connection (G_DBUS_PROXY (self)), "g-object-path", bearer_path, "g-interface-name", "org.freedesktop.ModemManager1.Bearer", NULL); g_free (bearer_path); } g_variant_unref (dictionary); return (bearer ? MM_BEARER (bearer) : NULL); } /*****************************************************************************/ /** * mm_modem_delete_bearer_finish: * @self: A #MMModem. * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to * mm_modem_delete_bearer(). * @error: Return location for error or %NULL. * * Finishes an operation started with mm_modem_delete_bearer(). * * Returns: %TRUE if the bearer was deleted, %FALSE if @error is set. * * Since: 1.0 */ gboolean mm_modem_delete_bearer_finish (MMModem *self, GAsyncResult *res, GError **error) { g_return_val_if_fail (MM_IS_MODEM (self), FALSE); return mm_gdbus_modem_call_delete_bearer_finish (MM_GDBUS_MODEM (self), res, error); } /** * mm_modem_delete_bearer: * @self: A #MMModem. * @bearer: Path of the bearer to delete. * @cancellable: (allow-none): A #GCancellable or %NULL. * @callback: A #GAsyncReadyCallback to call when the request is satisfied or * %NULL. * @user_data: User data to pass to @callback. * * Asynchronously deletes a given bearer from the #MMModem. * * When the operation is finished, @callback will be invoked in the * thread-default main loop * of the thread you are calling this method from. You can then call * mm_modem_delete_bearer_finish() to get the result of the operation. * * See mm_modem_delete_bearer_sync() for the synchronous, blocking version of * this method. * * Since: 1.0 */ void mm_modem_delete_bearer (MMModem *self, const gchar *bearer, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { g_return_if_fail (MM_IS_MODEM (self)); mm_gdbus_modem_call_delete_bearer (MM_GDBUS_MODEM (self), bearer, cancellable, callback, user_data); } /** * mm_modem_delete_bearer_sync: * @self: A #MMModem. * @bearer: Path of the bearer to delete. * @cancellable: (allow-none): A #GCancellable or %NULL. * @error: Return location for error or %NULL. * Synchronously deletes a given bearer from the #MMModem. * * The calling thread is blocked until a reply is received. See * mm_modem_delete_bearer() for the asynchronous version of this method. * * Returns: %TRUE if the bearer was deleted, %FALSE if @error is set. * * Since: 1.0 */ gboolean mm_modem_delete_bearer_sync (MMModem *self, const gchar *bearer, GCancellable *cancellable, GError **error) { g_return_val_if_fail (MM_IS_MODEM (self), FALSE); return mm_gdbus_modem_call_delete_bearer_sync (MM_GDBUS_MODEM (self), bearer, cancellable, error); } /*****************************************************************************/ /** * mm_modem_reset_finish: * @self: A #MMModem. * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to * mm_modem_reset(). * @error: Return location for error or %NULL. * * Finishes an operation started with mm_modem_reset(). * * Returns: %TRUE if the reset was successful, %FALSE if @error is set. * * Since: 1.0 */ gboolean mm_modem_reset_finish (MMModem *self, GAsyncResult *res, GError **error) { g_return_val_if_fail (MM_IS_MODEM (self), FALSE); return mm_gdbus_modem_call_reset_finish (MM_GDBUS_MODEM (self), res, error); } /** * mm_modem_reset: * @self: A #MMModem. * @cancellable: (allow-none): A #GCancellable or %NULL. * @callback: A #GAsyncReadyCallback to call when the request is satisfied or * %NULL. * @user_data: User data to pass to @callback. * * Asynchronously clears non-persistent configuration and state, and returns the * device to a newly-powered-on state. * * When the operation is finished, @callback will be invoked in the * thread-default main loop * of the thread you are calling this method from. You can then call * mm_modem_reset_finish() to get the result of the operation. * * See mm_modem_reset_sync() for the synchronous, blocking version of this * method. * * Since: 1.0 */ void mm_modem_reset (MMModem *self, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { g_return_if_fail (MM_IS_MODEM (self)); mm_gdbus_modem_call_reset (MM_GDBUS_MODEM (self), cancellable, callback, user_data); } /** * mm_modem_reset_sync: * @self: A #MMModem. * @cancellable: (allow-none): A #GCancellable or %NULL. * @error: Return location for error or %NULL. * * Synchronously clears non-persistent configuration and state, and returns the * device to a newly-powered-on state. * * The calling thread is blocked until a reply is received. See mm_modem_reset() * for the asynchronous version of this method. * * Returns: %TRUE if the reset was successful, %FALSE if @error is set. * * Since: 1.0 */ gboolean mm_modem_reset_sync (MMModem *self, GCancellable *cancellable, GError **error) { g_return_val_if_fail (MM_IS_MODEM (self), FALSE); return mm_gdbus_modem_call_reset_sync (MM_GDBUS_MODEM (self), cancellable, error); } /*****************************************************************************/ /** * mm_modem_factory_reset_finish: * @self: A #MMModem. * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to * mm_modem_factory_reset(). * @error: Return location for error or %NULL. * * Finishes an operation started with mm_modem_factory_reset(). * * Returns: %TRUE if the factory_reset was successful, %FALSE if @error is set. * * Since: 1.0 */ gboolean mm_modem_factory_reset_finish (MMModem *self, GAsyncResult *res, GError **error) { g_return_val_if_fail (MM_IS_MODEM (self), FALSE); return mm_gdbus_modem_call_factory_reset_finish (MM_GDBUS_MODEM (self), res, error); } /** * mm_modem_factory_reset: * @self: A #MMModem. * @code: Carrier-supplied code required to reset the modem. * @cancellable: (allow-none): A #GCancellable or %NULL. * @callback: A #GAsyncReadyCallback to call when the request is satisfied or * %NULL. * @user_data: User data to pass to @callback. * * Asynchronously clears the modem's configuration (including persistent * configuration and state), and returns the device to a factory-default state. * * When the operation is finished, @callback will be invoked in the * thread-default main loop * of the thread you are calling this method from. You can then call * mm_modem_factory_reset_finish() to get the result of the operation. * * See mm_modem_factory_reset_sync() for the synchronous, blocking version of * this method. * * Since: 1.0 */ void mm_modem_factory_reset (MMModem *self, const gchar *code, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { g_return_if_fail (MM_IS_MODEM (self)); mm_gdbus_modem_call_factory_reset (MM_GDBUS_MODEM (self), code, cancellable, callback, user_data); } /** * mm_modem_factory_reset_sync: * @self: A #MMModem. * @code: Carrier-supplied code required to reset the modem. * @cancellable: (allow-none): A #GCancellable or %NULL. * @error: Return location for error or %NULL. * * Synchronously clears the modem's configuration (including persistent * configuration and state), and returns the device to a factory-default state. * * The calling thread is blocked until a reply is received. See * mm_modem_factory_reset() for the asynchronous version of this method. * * Returns: %TRUE if the factory reset was successful, %FALSE if @error is set. * * Since: 1.0 */ gboolean mm_modem_factory_reset_sync (MMModem *self, const gchar *code, GCancellable *cancellable, GError **error) { g_return_val_if_fail (MM_IS_MODEM (self), FALSE); return mm_gdbus_modem_call_factory_reset_sync (MM_GDBUS_MODEM (self), code, cancellable, error); } /*****************************************************************************/ /** * mm_modem_command_finish: * @self: A #MMModem. * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to * mm_modem_command(). * @error: Return location for error or %NULL. * * Finishes an operation started with mm_modem_command(). * * Returns: (transfer full): A newly allocated string with the reply to the * command, or #NULL if @error is set. The returned value should be freed with * g_free(). * * Since: 1.0 */ gchar * mm_modem_command_finish (MMModem *self, GAsyncResult *res, GError **error) { gchar *result; g_return_val_if_fail (MM_IS_MODEM (self), FALSE); if (!mm_gdbus_modem_call_command_finish (MM_GDBUS_MODEM (self), &result, res, error)) return NULL; return result; } /** * mm_modem_command: * @self: A #MMModem. * @cmd: AT command to run. * @timeout: Maximum time to wait for the response, in seconds. * @cancellable: (allow-none): A #GCancellable or %NULL. * @callback: A #GAsyncReadyCallback to call when the request is satisfied or * %NULL. * @user_data: User data to pass to @callback. * * Asynchronously runs an AT command in the modem. * * When the operation is finished, @callback will be invoked in the * thread-default main loop * of the thread you are calling this method from. You can then call * mm_modem_command_finish() to get the result of the operation. * * See mm_modem_command_sync() for the synchronous, blocking version of this * method. * * Since: 1.0 */ void mm_modem_command (MMModem *self, const gchar *cmd, guint timeout, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { g_return_if_fail (MM_IS_MODEM (self)); mm_gdbus_modem_call_command (MM_GDBUS_MODEM (self), cmd, timeout, cancellable, callback, user_data); } /** * mm_modem_command_sync: * @self: A #MMModem. * @cmd: AT command to run. * @timeout: Maximum time to wait for the response, in seconds. * @cancellable: (allow-none): A #GCancellable or %NULL. * @error: Return location for error or %NULL. * * Synchronously runs an AT command in the modem. * * The calling thread is blocked until a reply is received. See * mm_modem_command() for the asynchronous version of this method. * * Returns: (transfer full): A newly allocated string with the reply to the * command, or #NULL if @error is set. The returned value should be freed * with g_free(). * * Since: 1.0 */ gchar * mm_modem_command_sync (MMModem *self, const gchar *cmd, guint timeout, GCancellable *cancellable, GError **error) { gchar *result; g_return_val_if_fail (MM_IS_MODEM (self), NULL); if (!mm_gdbus_modem_call_command_sync (MM_GDBUS_MODEM (self), cmd, timeout, &result, cancellable, error)) return NULL; return result; } /*****************************************************************************/ /** * mm_modem_set_power_state_finish: * @self: A #MMModem. * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to * mm_modem_set_power_state(). * @error: Return location for error or %NULL. * * Finishes an operation started with mm_modem_set_power_state(). * * Returns: %TRUE if the power state was successfully set, %FALSE if @error is * set. * * Since: 1.0 */ gboolean mm_modem_set_power_state_finish (MMModem *self, GAsyncResult *res, GError **error) { g_return_val_if_fail (MM_IS_MODEM (self), FALSE); return mm_gdbus_modem_call_set_power_state_finish (MM_GDBUS_MODEM (self), res, error); } /** * mm_modem_set_power_state: * @self: A #MMModem. * @state: Either %MM_MODEM_POWER_STATE_LOW or %MM_MODEM_POWER_STATE_ON. Every * other #MMModemPowerState value is not allowed. * @cancellable: (allow-none): A #GCancellable or %NULL. * @callback: A #GAsyncReadyCallback to call when the request is satisfied or * %NULL. * @user_data: User data to pass to @callback. * * Asynchronously sets the power state of the device. This method can only be * used while the modem is in %MM_MODEM_STATE_DISABLED state. * * When the operation is finished, @callback will be invoked in the * thread-default main loop * of the thread you are calling this method from. You can then call * mm_modem_set_power_state_finish() to get the result of the operation. * * See mm_modem_set_power_state_sync() for the synchronous, blocking version of * this method. * * Since: 1.0 */ void mm_modem_set_power_state (MMModem *self, MMModemPowerState state, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { g_return_if_fail (MM_IS_MODEM (self)); mm_gdbus_modem_call_set_power_state (MM_GDBUS_MODEM (self), state, cancellable, callback, user_data); } /** * mm_modem_set_power_state_sync: * @self: A #MMModem. * @state: Either %MM_MODEM_POWER_STATE_LOW or %MM_MODEM_POWER_STATE_ON. Every * other #MMModemPowerState value is not allowed. * @cancellable: (allow-none): A #GCancellable or %NULL. * @error: Return location for error or %NULL. * * Synchronously sets the power state of the device. This method can only be * used while the modem is in %MM_MODEM_STATE_DISABLED state. * * The calling thread is blocked until a reply is received. See * mm_modem_set_power_state() for the asynchronous version of this method. * * Returns: %TRUE if the power state was successfully set, %FALSE if @error is * set. * * Since: 1.0 */ gboolean mm_modem_set_power_state_sync (MMModem *self, MMModemPowerState state, GCancellable *cancellable, GError **error) { g_return_val_if_fail (MM_IS_MODEM (self), FALSE); return mm_gdbus_modem_call_set_power_state_sync (MM_GDBUS_MODEM (self), state, cancellable, error); } /*****************************************************************************/ /** * mm_modem_set_current_capabilities_finish: * @self: A #MMModem. * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to * mm_modem_set_current_capabilities(). * @error: Return location for error or %NULL. * * Finishes an operation started with mm_modem_set_current_capabilities(). * * Returns: %TRUE if the capabilities were successfully set, %FALSE if @error is * set. * * Since: 1.0 */ gboolean mm_modem_set_current_capabilities_finish (MMModem *self, GAsyncResult *res, GError **error) { g_return_val_if_fail (MM_IS_MODEM (self), FALSE); return mm_gdbus_modem_call_set_current_capabilities_finish (MM_GDBUS_MODEM (self), res, error); } /** * mm_modem_set_current_capabilities: * @self: A #MMModem. * @capabilities: A #MMModemCapability mask. * @cancellable: (allow-none): A #GCancellable or %NULL. * @callback: A #GAsyncReadyCallback to call when the request is satisfied or * %NULL. * @user_data: User data to pass to @callback. * * Asynchronously sets the capabilities of the device. A restart of the modem * may be required. * * When the operation is finished, @callback will be invoked in the * thread-default main loop * of the thread you are calling this method from. You can then call * mm_modem_set_current_capabilities_finish() to get the result of the * operation. * * See mm_modem_set_current_capabilities_sync() for the synchronous, blocking * version of this method. * * Since: 1.0 */ void mm_modem_set_current_capabilities (MMModem *self, MMModemCapability capabilities, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { g_return_if_fail (MM_IS_MODEM (self)); mm_gdbus_modem_call_set_current_capabilities (MM_GDBUS_MODEM (self), capabilities, cancellable, callback, user_data); } /** * mm_modem_set_current_capabilities_sync: * @self: A #MMModem. * @capabilities: A #MMModemCapability mask. * @cancellable: (allow-none): A #GCancellable or %NULL. * @error: Return location for error or %NULL. * * Synchronously sets the capabilities of the device. A restart of the modem may * be required. * * The calling thread is blocked until a reply is received. See * mm_modem_set_current_capabilities() for the asynchronous version of this * method. * * Returns: %TRUE if the capabilities were successfully set, %FALSE if @error is * set. * * Since: 1.0 */ gboolean mm_modem_set_current_capabilities_sync (MMModem *self, MMModemCapability capabilities, GCancellable *cancellable, GError **error) { g_return_val_if_fail (MM_IS_MODEM (self), FALSE); return (mm_gdbus_modem_call_set_current_capabilities_sync ( MM_GDBUS_MODEM (self), capabilities, cancellable, error)); } /*****************************************************************************/ /** * mm_modem_set_current_modes_finish: * @self: A #MMModem. * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to * mm_modem_set_current_modes(). * @error: Return location for error or %NULL. * * Finishes an operation started with mm_modem_set_current_modes(). * * Returns: %TRUE if the allowed modes were successfully set, %FALSE if @error * is set. * * Since: 1.0 */ gboolean mm_modem_set_current_modes_finish (MMModem *self, GAsyncResult *res, GError **error) { g_return_val_if_fail (MM_IS_MODEM (self), FALSE); return mm_gdbus_modem_call_set_current_modes_finish (MM_GDBUS_MODEM (self), res, error); } /** * mm_modem_set_current_modes: * @self: A #MMModem. * @modes: Mask of #MMModemMode values specifying which modes are allowed. * @preferred: A #MMModemMode value specifying which of the modes given in * @modes is the preferred one, or #MM_MODEM_MODE_NONE if none. * @cancellable: (allow-none): A #GCancellable or %NULL. * @callback: A #GAsyncReadyCallback to call when the request is satisfied or * %NULL. * @user_data: User data to pass to @callback. * * Asynchronously sets the access technologies (e.g. 2G/3G/4G preference) the * device is currently allowed to use when connecting to a network. * * When the operation is finished, @callback will be invoked in the * thread-default main loop * of the thread you are calling this method from. You can then call * mm_modem_set_current_modes_finish() to get the result of the operation. * * See mm_modem_set_current_modes_sync() for the synchronous, blocking version * of this method. * * Since: 1.0 */ void mm_modem_set_current_modes (MMModem *self, MMModemMode modes, MMModemMode preferred, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { g_return_if_fail (MM_IS_MODEM (self)); mm_gdbus_modem_call_set_current_modes (MM_GDBUS_MODEM (self), g_variant_new ("(uu)", modes, preferred), cancellable, callback, user_data); } /** * mm_modem_set_current_modes_sync: * @self: A #MMModem. * @modes: Mask of #MMModemMode values specifying which modes are allowed. * @preferred: A #MMModemMode value specifying which of the modes given in * @modes is the preferred one, or #MM_MODEM_MODE_NONE if none. * @cancellable: (allow-none): A #GCancellable or %NULL. * @error: Return location for error or %NULL. * * Synchronously sets the access technologies (e.g. 2G/3G/4G preference) the * device is currently allowed to use when connecting to a network. * * The calling thread is blocked until a reply is received. See * mm_modem_set_current_modes() for the asynchronous version of this method. * * Returns: %TRUE if the allowed modes were successfully set, %FALSE if @error * is set. * * Since: 1.0 */ gboolean mm_modem_set_current_modes_sync (MMModem *self, MMModemMode modes, MMModemMode preferred, GCancellable *cancellable, GError **error) { g_return_val_if_fail (MM_IS_MODEM (self), FALSE); return mm_gdbus_modem_call_set_current_modes_sync (MM_GDBUS_MODEM (self), g_variant_new ("(uu)", modes, preferred), cancellable, error); } /*****************************************************************************/ /** * mm_modem_set_current_bands_finish: * @self: A #MMModem. * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to * mm_modem_set_current_bands(). * @error: Return location for error or %NULL. * * Finishes an operation started with mm_modem_set_current_bands(). * * Returns: %TRUE if the bands were successfully set, %FALSE if @error is set. * * Since: 1.0 */ gboolean mm_modem_set_current_bands_finish (MMModem *self, GAsyncResult *res, GError **error) { g_return_val_if_fail (MM_IS_MODEM (self), FALSE); return mm_gdbus_modem_call_set_current_bands_finish (MM_GDBUS_MODEM (self), res, error); } /** * mm_modem_set_current_bands: * @self: A #MMModem. * @bands: An array of #MMModemBand values specifying which bands are allowed. * @n_bands: Number of elements in @bands. * @cancellable: (allow-none): A #GCancellable or %NULL. * @callback: A #GAsyncReadyCallback to call when the request is satisfied or * %NULL. * @user_data: User data to pass to @callback. * * Asynchronously sets the radio frequency and technology bands the device is * currently allowed to use when connecting to a network. * * When the operation is finished, @callback will be invoked in the * thread-default main loop * of the thread you are calling this method from. You can then call * mm_modem_set_current_bands_finish() to get the result of the operation. * * See mm_modem_set_current_bands_sync() for the synchronous, blocking version * of this method. * * Since: 1.0 */ void mm_modem_set_current_bands (MMModem *self, const MMModemBand *bands, guint n_bands, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { g_return_if_fail (MM_IS_MODEM (self)); mm_gdbus_modem_call_set_current_bands (MM_GDBUS_MODEM (self), mm_common_bands_array_to_variant (bands, n_bands), cancellable, callback, user_data); } /** * mm_modem_set_current_bands_sync: * @self: A #MMModem. * @bands: An array of #MMModemBand values specifying which bands are allowed. * @n_bands: Number of elements in @bands. * @cancellable: (allow-none): A #GCancellable or %NULL. * @error: Return location for error or %NULL. * * Synchronously sets the radio frequency and technology bands the device is * currently allowed to use when connecting to a network. * * The calling thread is blocked until a reply is received. See * mm_modem_set_current_bands() for the asynchronous version of this method. * * Returns: %TRUE if the bands were successfully set, %FALSE if @error is set. * * Since: 1.0 */ gboolean mm_modem_set_current_bands_sync (MMModem *self, const MMModemBand *bands, guint n_bands, GCancellable *cancellable, GError **error) { g_return_val_if_fail (MM_IS_MODEM (self), FALSE); return (mm_gdbus_modem_call_set_current_bands_sync ( MM_GDBUS_MODEM (self), mm_common_bands_array_to_variant (bands, n_bands), cancellable, error)); } /*****************************************************************************/ /** * mm_modem_get_sim_finish: * @self: A #MMModem. * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to * mm_modem_get_sim(). * @error: Return location for error or %NULL. * * Finishes an operation started with mm_modem_get_sim(). * * Returns: (transfer full): a #MMSim or #NULL if @error is set. The returned * value should be freed with g_object_unref(). * * Since: 1.0 */ MMSim * mm_modem_get_sim_finish (MMModem *self, GAsyncResult *res, GError **error) { g_return_val_if_fail (MM_IS_MODEM (self), NULL); return g_task_propagate_pointer (G_TASK (res), error); } static void modem_get_sim_ready (GDBusConnection *connection, GAsyncResult *res, GTask *task) { GError *error = NULL; GObject *sim; GObject *source_object; source_object = g_async_result_get_source_object (res); sim = g_async_initable_new_finish (G_ASYNC_INITABLE (source_object), res, &error); g_object_unref (source_object); if (error) g_task_return_error (task, error); else g_task_return_pointer (task, sim, g_object_unref); g_object_unref (task); } /** * mm_modem_get_sim: * @self: A #MMModem. * @cancellable: (allow-none): A #GCancellable or %NULL. * @callback: A #GAsyncReadyCallback to call when the request is satisfied or * %NULL. * @user_data: User data to pass to @callback. * * Asynchronously gets the #MMSim object managed by this #MMModem. * * When the operation is finished, @callback will be invoked in the * thread-default main loop * of the thread you are calling this method from. You can then call * mm_modem_get_sim_finish() to get the result of the operation. * * See mm_modem_get_sim_sync() for the synchronous, blocking version of this * method. * * Since: 1.0 */ void mm_modem_get_sim (MMModem *self, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; const gchar *sim_path; g_return_if_fail (MM_IS_MODEM (self)); task = g_task_new (self, cancellable, callback, user_data); sim_path = mm_modem_get_sim_path (self); if (!sim_path || g_str_equal (sim_path, "/")) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_NOT_FOUND, "No SIM object available"); g_object_unref (task); return; } g_async_initable_new_async (MM_TYPE_SIM, G_PRIORITY_DEFAULT, cancellable, (GAsyncReadyCallback)modem_get_sim_ready, task, "g-flags", G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START, "g-name", MM_DBUS_SERVICE, "g-connection", g_dbus_proxy_get_connection (G_DBUS_PROXY (self)), "g-object-path", sim_path, "g-interface-name", "org.freedesktop.ModemManager1.Sim", NULL); } /** * mm_modem_get_sim_sync: * @self: A #MMModem. * @cancellable: (allow-none): A #GCancellable or %NULL. * @error: Return location for error or %NULL. * * Synchronously gets the #MMSim object managed by this #MMModem. * * The calling thread is blocked until a reply is received. See * mm_modem_get_sim() for the asynchronous version of this method. * * Returns: (transfer full): a #MMSim or #NULL if @error is set. The returned * value should be freed with g_object_unref(). * * Since: 1.0 */ MMSim * mm_modem_get_sim_sync (MMModem *self, GCancellable *cancellable, GError **error) { GObject *sim; const gchar *sim_path; g_return_val_if_fail (MM_IS_MODEM (self), NULL); sim_path = mm_modem_get_sim_path (self); if (!sim_path || g_str_equal (sim_path, "/")) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_NOT_FOUND, "No SIM object available"); return NULL; } sim = g_initable_new (MM_TYPE_SIM, cancellable, error, "g-flags", G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START, "g-name", MM_DBUS_SERVICE, "g-connection", g_dbus_proxy_get_connection (G_DBUS_PROXY (self)), "g-object-path", sim_path, "g-interface-name", "org.freedesktop.ModemManager1.Sim", NULL); return (sim ? MM_SIM (sim) : NULL); } /*****************************************************************************/ typedef struct { gchar **sim_paths; GPtrArray *sim_slots; guint n_sim_paths; guint i; } ListSimSlotsContext; static void list_sim_slots_context_free (ListSimSlotsContext *ctx) { g_strfreev (ctx->sim_paths); if (ctx->sim_slots) g_ptr_array_unref (ctx->sim_slots); g_slice_free (ListSimSlotsContext, ctx); } /** * mm_modem_list_sim_slots_finish: * @self: A #MMModem. * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to * mm_modem_list_sim_slots(). * @error: Return location for error or %NULL. * * Finishes an operation started with mm_modem_list_sim_slots(). * * Returns: (transfer full) (element-type ModemManager.Sim): The array of * #MMSim objects, or %NULL if @error is set. * * Since: 1.16 */ GPtrArray * mm_modem_list_sim_slots_finish (MMModem *self, GAsyncResult *res, GError **error) { g_return_val_if_fail (MM_IS_MODEM (self), NULL); return g_task_propagate_pointer (G_TASK (res), error); } static void sim_slot_free (MMSim *sim) { if (sim) g_object_unref (sim); } static void create_next_sim (GTask *task); static void modem_list_sim_slots_build_object_ready (GDBusConnection *connection, GAsyncResult *res, GTask *task) { GObject *sim; GError *error = NULL; GObject *source_object; ListSimSlotsContext *ctx; ctx = g_task_get_task_data (task); source_object = g_async_result_get_source_object (res); sim = g_async_initable_new_finish (G_ASYNC_INITABLE (source_object), res, &error); g_object_unref (source_object); if (error) { g_task_return_error (task, error); g_object_unref (task); return; } g_ptr_array_add (ctx->sim_slots, sim); /* Keep on creating next object */ ctx->i++; create_next_sim (task); } static void create_next_sim (GTask *task) { MMModem *self; ListSimSlotsContext *ctx; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); /* If no more additional sims, just end here. */ if (ctx->i == ctx->n_sim_paths) { g_assert_cmpuint (ctx->n_sim_paths, ==, ctx->sim_slots->len); g_task_return_pointer (task, g_steal_pointer (&ctx->sim_slots), (GDestroyNotify)g_ptr_array_unref); g_object_unref (task); return; } /* Empty slot? */ if (g_str_equal (ctx->sim_paths[ctx->i], "/")) { g_ptr_array_add (ctx->sim_slots, NULL); ctx->i++; create_next_sim (task); return; } g_async_initable_new_async (MM_TYPE_SIM, G_PRIORITY_DEFAULT, g_task_get_cancellable (task), (GAsyncReadyCallback)modem_list_sim_slots_build_object_ready, task, "g-flags", G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START, "g-name", MM_DBUS_SERVICE, "g-connection", g_dbus_proxy_get_connection (G_DBUS_PROXY (self)), "g-object-path", ctx->sim_paths[ctx->i], "g-interface-name", "org.freedesktop.ModemManager1.Sim", NULL); } /** * mm_modem_list_sim_slots: * @self: A #MMModem. * @cancellable: (allow-none): A #GCancellable or %NULL. * @callback: A #GAsyncReadyCallback to call when the request is satisfied or * %NULL. * @user_data: User data to pass to @callback. * * Asynchronously lists the SIM slots available in the #MMModem. * * The returned array contains one element per slot available in the system; * a #MMSim in each of the slots that contains a valid SIM card or %NULL if * no SIM card is found. * * When the operation is finished, @callback will be invoked in the * thread-default main loop * of the thread you are calling this method from. You can then call * mm_modem_list_sim_slots_finish() to get the result of the operation. * * See mm_modem_list_sim_slots_sync() for the synchronous, blocking version of * this method. * * Since: 1.16 */ void mm_modem_list_sim_slots (MMModem *self, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { ListSimSlotsContext *ctx; GTask *task; g_return_if_fail (MM_IS_MODEM (self)); ctx = g_slice_new0 (ListSimSlotsContext); ctx->sim_paths = mm_gdbus_modem_dup_sim_slots (MM_GDBUS_MODEM (self)); task = g_task_new (self, cancellable, callback, user_data); g_task_set_task_data (task, ctx, (GDestroyNotify)list_sim_slots_context_free); /* If no sim slots, just end here. */ if (!ctx->sim_paths) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_NOT_FOUND, "No SIM slots available"); g_object_unref (task); return; } /* Got list of paths, start creating objects for each */ ctx->n_sim_paths = g_strv_length (ctx->sim_paths); ctx->sim_slots = g_ptr_array_new_full (ctx->n_sim_paths, (GDestroyNotify)sim_slot_free); ctx->i = 0; create_next_sim (task); } /** * mm_modem_list_sim_slots_sync: * @self: A #MMModem. * @cancellable: (allow-none): A #GCancellable or %NULL. * @error: Return location for error or %NULL. * * Synchronously lists the SIM slots available in the #MMModem. * * The returned array contains one element per slot available in the system; * a #MMSim in each of the slots that contains a valid SIM card or %NULL if * no SIM card is found. * * The calling thread is blocked until a reply is received. See * mm_modem_list_sim_slots() for the asynchronous version of this method. * * Returns: (transfer full) (element-type ModemManager.Sim): The array of * #MMSim objects, or %NULL if @error is set. * * Since: 1.16 */ GPtrArray * mm_modem_list_sim_slots_sync (MMModem *self, GCancellable *cancellable, GError **error) { g_autoptr(GPtrArray) sim_slots = NULL; g_auto(GStrv) sim_paths = NULL; guint n_sim_paths; guint i; g_return_val_if_fail (MM_IS_MODEM (self), NULL); sim_paths = mm_gdbus_modem_dup_sim_slots (MM_GDBUS_MODEM (self)); /* Only non-empty lists are returned */ if (!sim_paths) return NULL; n_sim_paths = g_strv_length (sim_paths); sim_slots = g_ptr_array_new_full (n_sim_paths, (GDestroyNotify)sim_slot_free); for (i = 0; i < n_sim_paths; i++) { GObject *sim; if (g_str_equal (sim_paths[i], "/")) { g_ptr_array_add (sim_slots, NULL); continue; } sim = g_initable_new (MM_TYPE_SIM, cancellable, error, "g-flags", G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START, "g-name", MM_DBUS_SERVICE, "g-connection", g_dbus_proxy_get_connection (G_DBUS_PROXY (self)), "g-object-path", sim_paths[i], "g-interface-name", "org.freedesktop.ModemManager1.Sim", NULL); if (!sim) return NULL; /* Keep the object */ g_ptr_array_add (sim_slots, sim); } g_assert_cmpuint (sim_slots->len, ==, n_sim_paths); return g_steal_pointer (&sim_slots); } /*****************************************************************************/ /** * mm_modem_set_primary_sim_slot_finish: * @self: A #MMModem. * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to * mm_modem_set_primary_sim_slot(). * @error: Return location for error or %NULL. * * Finishes an operation started with mm_modem_set_primary_sim_slot(). * * Returns: %TRUE if the SIM slot switch has been successfully requested, %FALSE if * @error is set. * * Since: 1.16 */ gboolean mm_modem_set_primary_sim_slot_finish (MMModem *self, GAsyncResult *res, GError **error) { g_return_val_if_fail (MM_IS_MODEM (self), FALSE); return mm_gdbus_modem_call_set_primary_sim_slot_finish (MM_GDBUS_MODEM (self), res, error); } /** * mm_modem_set_primary_sim_slot: * @self: A #MMModem. * @sim_slot: SIM slot number. * @cancellable: (allow-none): A #GCancellable or %NULL. * @callback: A #GAsyncReadyCallback to call when the request is satisfied or * %NULL. * @user_data: User data to pass to @callback. * * Asynchronously requests to select which SIM slot to be considered as primary. * * When the operation is finished, @callback will be invoked in the * thread-default main loop * of the thread you are calling this method from. You can then call * mm_modem_set_primary_sim_slot_finish() to get the result of the operation. * * See mm_modem_set_primary_sim_slot_sync() for the synchronous, blocking version of * this method. * * Since: 1.16 */ void mm_modem_set_primary_sim_slot (MMModem *self, guint sim_slot, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { g_return_if_fail (MM_IS_MODEM (self)); mm_gdbus_modem_call_set_primary_sim_slot (MM_GDBUS_MODEM (self), sim_slot, cancellable, callback, user_data); } /** * mm_modem_set_primary_sim_slot_sync: * @self: A #MMModem. * @sim_slot: SIM slot number. * @cancellable: (allow-none): A #GCancellable or %NULL. * @error: Return location for error or %NULL. * * Synchronously requests to select which SIM slot to be considered as primary. * * The calling thread is blocked until a reply is received. See * mm_modem_set_primary_sim_slot() for the asynchronous version of this method. * * Returns: %TRUE if the SIM slot switch has been successfully requested, %FALSE if * @error is set. * * Since: 1.16 */ gboolean mm_modem_set_primary_sim_slot_sync (MMModem *self, guint sim_slot, GCancellable *cancellable, GError **error) { g_return_val_if_fail (MM_IS_MODEM (self), FALSE); return mm_gdbus_modem_call_set_primary_sim_slot_sync (MM_GDBUS_MODEM (self), sim_slot, cancellable, error); } /*****************************************************************************/ static GList * create_cell_info_list (GVariant *variant, GError **error) { GError *inner_error = NULL; GList *list = NULL; GVariantIter dict_iter; GVariant *dict; /* Input is aa{sv} */ g_variant_iter_init (&dict_iter, variant); while ((dict = g_variant_iter_next_value (&dict_iter))) { MMCellInfo *cell_info; cell_info = mm_cell_info_new_from_dictionary (dict, &inner_error); if (inner_error) break; list = g_list_prepend (list, cell_info); g_variant_unref (dict); } if (inner_error) { g_list_free_full (g_steal_pointer (&list), g_object_unref); g_propagate_error (error, inner_error); } g_variant_unref (variant); return list; } /** * mm_modem_get_cell_info_finish: * @self: A #MMModem. * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to * mm_modem_get_cell_info(). * @error: Return location for error or %NULL. * * Finishes an operation started with mm_modem_get_cell_info(). * * Returns: (transfer full) (element-type ModemManager.CellInfo): a list * of #MMCellInfo objects, or #NULL if @error is set. The returned value * should be freed with g_list_free_full() using g_object_unref() as * #GDestroyNotify function. * * Since: 1.20 */ GList * mm_modem_get_cell_info_finish (MMModem *self, GAsyncResult *res, GError **error) { GVariant *result = NULL; g_return_val_if_fail (MM_IS_MODEM (self), FALSE); if (!mm_gdbus_modem_call_get_cell_info_finish (MM_GDBUS_MODEM (self), &result, res, error)) return NULL; return create_cell_info_list (result, error); } /** * mm_modem_get_cell_info: * @self: A #MMModem. * @cancellable: (allow-none): A #GCancellable or %NULL. * @callback: A #GAsyncReadyCallback to call when the request is satisfied or * %NULL. * @user_data: User data to pass to @callback. * * Asynchronously requests to get info about serving and neighboring cells. * * When the operation is finished, @callback will be invoked in the * thread-default main loop * of the thread you are calling this method from. You can then call * mm_modem_get_cell_info_finish() to get the result of the operation. * * See mm_modem_get_cell_info_sync() for the synchronous, blocking version of this * method. * * Since: 1.20 */ void mm_modem_get_cell_info (MMModem *self, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { g_return_if_fail (MM_IS_MODEM (self)); mm_gdbus_modem_call_get_cell_info (MM_GDBUS_MODEM (self), cancellable, callback, user_data); } /** * mm_modem_get_cell_info_sync: * @self: A #MMModem. * @cancellable: (allow-none): A #GCancellable or %NULL. * @error: Return location for error or %NULL. * * Synchronously requests to get info about serving and neighboring cells. * * The calling thread is blocked until a reply is received. See * mm_modem_get_cell_info() for the asynchronous version of this method. * * Returns: (transfer full) (element-type ModemManager.CellInfo): a list * of #MMCellInfo objects, or #NULL if @error is set. The returned value * should be freed with g_list_free_full() using g_object_unref() as * #GDestroyNotify function. * * Since: 1.20 */ GList * mm_modem_get_cell_info_sync (MMModem *self, GCancellable *cancellable, GError **error) { GVariant *result = NULL; g_return_val_if_fail (MM_IS_MODEM (self), FALSE); if (!mm_gdbus_modem_call_get_cell_info_sync (MM_GDBUS_MODEM (self), &result, cancellable, error)) return NULL; return create_cell_info_list (result, error); } /*****************************************************************************/ static void mm_modem_init (MMModem *self) { self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, MM_TYPE_MODEM, MMModemPrivate); g_mutex_init (&self->priv->mutex); PROPERTY_INITIALIZE (ports, "ports") PROPERTY_INITIALIZE (supported_modes, "supported-modes") PROPERTY_INITIALIZE (supported_capabilities, "supported-capabilities") PROPERTY_INITIALIZE (supported_bands, "supported-bands") PROPERTY_INITIALIZE (current_bands, "current-bands") PROPERTY_INITIALIZE (unlock_retries, "unlock-retries") } static void finalize (GObject *object) { MMModem *self = MM_MODEM (object); g_mutex_clear (&self->priv->mutex); PROPERTY_ARRAY_FINALIZE (ports) PROPERTY_ARRAY_FINALIZE (supported_modes) PROPERTY_ARRAY_FINALIZE (supported_capabilities) PROPERTY_ARRAY_FINALIZE (supported_bands) PROPERTY_ARRAY_FINALIZE (current_bands) PROPERTY_OBJECT_FINALIZE (unlock_retries) G_OBJECT_CLASS (mm_modem_parent_class)->finalize (object); } static void mm_modem_class_init (MMModemClass *modem_class) { GObjectClass *object_class = G_OBJECT_CLASS (modem_class); g_type_class_add_private (object_class, sizeof (MMModemPrivate)); /* Virtual methods */ object_class->finalize = finalize; } ModemManager-1.23.4-dev/libmm-glib/mm-modem.h000066400000000000000000000470361456466623000206330ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * libmm-glib -- Access modem status & information from glib applications * * 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 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 Street, Fifth Floor, * Boston, MA 02110-1301 USA. * * Copyright (C) 2011 - 2012 Aleksander Morgado * Copyright (C) 2012 Google, Inc. */ #ifndef _MM_MODEM_H_ #define _MM_MODEM_H_ #if !defined (__LIBMM_GLIB_H_INSIDE__) && !defined (LIBMM_GLIB_COMPILATION) #error "Only can be included directly." #endif #include #include "mm-gdbus-modem.h" #include "mm-unlock-retries.h" #include "mm-sim.h" #include "mm-bearer.h" #include "mm-cell-info.h" #include "mm-helper-types.h" G_BEGIN_DECLS #define MM_TYPE_MODEM (mm_modem_get_type ()) #define MM_MODEM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_MODEM, MMModem)) #define MM_MODEM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_MODEM, MMModemClass)) #define MM_IS_MODEM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_MODEM)) #define MM_IS_MODEM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), MM_TYPE_MODEM)) #define MM_MODEM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_MODEM, MMModemClass)) typedef struct _MMModem MMModem; typedef struct _MMModemClass MMModemClass; typedef struct _MMModemPrivate MMModemPrivate; /** * MMModem: * * The #MMModem structure contains private data and should only be accessed * using the provided API. */ struct _MMModem { /*< private >*/ MmGdbusModemProxy parent; MMModemPrivate *priv; }; struct _MMModemClass { /*< private >*/ MmGdbusModemProxyClass parent; }; GType mm_modem_get_type (void); G_DEFINE_AUTOPTR_CLEANUP_FUNC (MMModem, g_object_unref) const gchar *mm_modem_get_path (MMModem *self); gchar *mm_modem_dup_path (MMModem *self); const gchar *mm_modem_get_sim_path (MMModem *self); gchar *mm_modem_dup_sim_path (MMModem *self); const gchar * const *mm_modem_get_sim_slot_paths (MMModem *self); gchar **mm_modem_dup_sim_slot_paths (MMModem *self); guint mm_modem_get_primary_sim_slot (MMModem *self); gboolean mm_modem_peek_supported_capabilities (MMModem *self, const MMModemCapability **capabilities, guint *n_capabilities); gboolean mm_modem_get_supported_capabilities (MMModem *self, MMModemCapability **capabilities, guint *n_capabilities); MMModemCapability mm_modem_get_current_capabilities (MMModem *self); guint mm_modem_get_max_active_bearers (MMModem *self); guint mm_modem_get_max_active_multiplexed_bearers (MMModem *self); const gchar * const *mm_modem_get_bearer_paths (MMModem *self); gchar **mm_modem_dup_bearer_paths (MMModem *self); const gchar *mm_modem_get_manufacturer (MMModem *self); gchar *mm_modem_dup_manufacturer (MMModem *self); const gchar *mm_modem_get_model (MMModem *self); gchar *mm_modem_dup_model (MMModem *self); const gchar *mm_modem_get_revision (MMModem *self); gchar *mm_modem_dup_revision (MMModem *self); const gchar *mm_modem_get_carrier_configuration (MMModem *self); gchar *mm_modem_dup_carrier_configuration (MMModem *self); const gchar *mm_modem_get_carrier_configuration_revision (MMModem *self); gchar *mm_modem_dup_carrier_configuration_revision (MMModem *self); const gchar *mm_modem_get_hardware_revision (MMModem *self); gchar *mm_modem_dup_hardware_revision (MMModem *self); const gchar *mm_modem_get_device_identifier (MMModem *self); gchar *mm_modem_dup_device_identifier (MMModem *self); const gchar *mm_modem_get_device (MMModem *self); gchar *mm_modem_dup_device (MMModem *self); const gchar *mm_modem_get_physdev (MMModem *self); gchar *mm_modem_dup_physdev (MMModem *self); const gchar * const *mm_modem_get_drivers (MMModem *self); gchar **mm_modem_dup_drivers (MMModem *self); const gchar *mm_modem_get_plugin (MMModem *self); gchar *mm_modem_dup_plugin (MMModem *self); const gchar *mm_modem_get_primary_port (MMModem *self); gchar *mm_modem_dup_primary_port (MMModem *self); gboolean mm_modem_peek_ports (MMModem *self, const MMModemPortInfo **ports, guint *n_ports); gboolean mm_modem_get_ports (MMModem *self, MMModemPortInfo **ports, guint *n_ports); const gchar *mm_modem_get_equipment_identifier (MMModem *self); gchar *mm_modem_dup_equipment_identifier (MMModem *self); const gchar *const *mm_modem_get_own_numbers (MMModem *self); gchar **mm_modem_dup_own_numbers (MMModem *self); MMModemLock mm_modem_get_unlock_required (MMModem *self); MMUnlockRetries *mm_modem_get_unlock_retries (MMModem *self); MMUnlockRetries *mm_modem_peek_unlock_retries (MMModem *self); MMModemState mm_modem_get_state (MMModem *self); MMModemStateFailedReason mm_modem_get_state_failed_reason (MMModem *self); MMModemPowerState mm_modem_get_power_state (MMModem *self); MMModemAccessTechnology mm_modem_get_access_technologies (MMModem *self); guint mm_modem_get_signal_quality (MMModem *self, gboolean *recent); gboolean mm_modem_peek_supported_modes (MMModem *self, const MMModemModeCombination **modes, guint *n_modes); gboolean mm_modem_get_supported_modes (MMModem *self, MMModemModeCombination **modes, guint *n_modes); gboolean mm_modem_get_current_modes (MMModem *self, MMModemMode *allowed, MMModemMode *preferred); gboolean mm_modem_peek_supported_bands (MMModem *self, const MMModemBand **bands, guint *n_bands); gboolean mm_modem_get_supported_bands (MMModem *self, MMModemBand **bands, guint *n_bands); gboolean mm_modem_peek_current_bands (MMModem *self, const MMModemBand **bands, guint *n_bands); gboolean mm_modem_get_current_bands (MMModem *self, MMModemBand **bands, guint *n_bands); MMBearerIpFamily mm_modem_get_supported_ip_families (MMModem *self); void mm_modem_enable (MMModem *self, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); gboolean mm_modem_enable_finish (MMModem *self, GAsyncResult *res, GError **error); gboolean mm_modem_enable_sync (MMModem *self, GCancellable *cancellable, GError **error); void mm_modem_disable (MMModem *self, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); gboolean mm_modem_disable_finish (MMModem *self, GAsyncResult *res, GError **error); gboolean mm_modem_disable_sync (MMModem *self, GCancellable *cancellable, GError **error); void mm_modem_list_bearers (MMModem *self, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); GList *mm_modem_list_bearers_finish (MMModem *self, GAsyncResult *res, GError **error); GList *mm_modem_list_bearers_sync (MMModem *self, GCancellable *cancellable, GError **error); void mm_modem_create_bearer (MMModem *self, MMBearerProperties *properties, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); MMBearer *mm_modem_create_bearer_finish (MMModem *self, GAsyncResult *res, GError **error); MMBearer *mm_modem_create_bearer_sync (MMModem *self, MMBearerProperties *properties, GCancellable *cancellable, GError **error); void mm_modem_delete_bearer (MMModem *self, const gchar *bearer, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); gboolean mm_modem_delete_bearer_finish (MMModem *self, GAsyncResult *res, GError **error); gboolean mm_modem_delete_bearer_sync (MMModem *self, const gchar *bearer, GCancellable *cancellable, GError **error); void mm_modem_reset (MMModem *self, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); gboolean mm_modem_reset_finish (MMModem *self, GAsyncResult *res, GError **error); gboolean mm_modem_reset_sync (MMModem *self, GCancellable *cancellable, GError **error); void mm_modem_factory_reset (MMModem *self, const gchar *code, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); gboolean mm_modem_factory_reset_finish (MMModem *self, GAsyncResult *res, GError **error); gboolean mm_modem_factory_reset_sync (MMModem *self, const gchar *code, GCancellable *cancellable, GError **error); void mm_modem_command (MMModem *self, const gchar *cmd, guint timeout, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); gchar *mm_modem_command_finish (MMModem *self, GAsyncResult *res, GError **error); gchar *mm_modem_command_sync (MMModem *self, const gchar *cmd, guint timeout, GCancellable *cancellable, GError **error); void mm_modem_set_power_state (MMModem *self, MMModemPowerState state, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); gboolean mm_modem_set_power_state_finish (MMModem *self, GAsyncResult *res, GError **error); gboolean mm_modem_set_power_state_sync (MMModem *self, MMModemPowerState state, GCancellable *cancellable, GError **error); void mm_modem_set_current_capabilities (MMModem *self, MMModemCapability capabilities, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); gboolean mm_modem_set_current_capabilities_finish (MMModem *self, GAsyncResult *res, GError **error); gboolean mm_modem_set_current_capabilities_sync (MMModem *self, MMModemCapability capabilities, GCancellable *cancellable, GError **error); void mm_modem_set_current_modes (MMModem *self, MMModemMode modes, MMModemMode preferred, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); gboolean mm_modem_set_current_modes_finish (MMModem *self, GAsyncResult *res, GError **error); gboolean mm_modem_set_current_modes_sync (MMModem *self, MMModemMode modes, MMModemMode preferred, GCancellable *cancellable, GError **error); void mm_modem_set_current_bands (MMModem *self, const MMModemBand *bands, guint n_bands, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); gboolean mm_modem_set_current_bands_finish (MMModem *self, GAsyncResult *res, GError **error); gboolean mm_modem_set_current_bands_sync (MMModem *self, const MMModemBand *bands, guint n_bands, GCancellable *cancellable, GError **error); void mm_modem_get_sim (MMModem *self, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); MMSim *mm_modem_get_sim_finish (MMModem *self, GAsyncResult *res, GError **error); MMSim *mm_modem_get_sim_sync (MMModem *self, GCancellable *cancellable, GError **error); void mm_modem_list_sim_slots (MMModem *self, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); GPtrArray *mm_modem_list_sim_slots_finish (MMModem *self, GAsyncResult *res, GError **error); GPtrArray *mm_modem_list_sim_slots_sync (MMModem *self, GCancellable *cancellable, GError **error); void mm_modem_set_primary_sim_slot (MMModem *self, guint sim_slot, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); gboolean mm_modem_set_primary_sim_slot_finish (MMModem *self, GAsyncResult *res, GError **error); gboolean mm_modem_set_primary_sim_slot_sync (MMModem *self, guint sim_slot, GCancellable *cancellable, GError **error); void mm_modem_get_cell_info (MMModem *self, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); GList *mm_modem_get_cell_info_finish (MMModem *self, GAsyncResult *res, GError **error); GList *mm_modem_get_cell_info_sync (MMModem *self, GCancellable *cancellable, GError **error); G_END_DECLS #endif /* _MM_MODEM_H_ */ ModemManager-1.23.4-dev/libmm-glib/mm-network-timezone.c000066400000000000000000000212431456466623000230360ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * libmm-glib -- Access modem status & information from glib applications * * 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 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 Street, Fifth Floor, * Boston, MA 02110-1301 USA. * * Copyright (C) 2012 Google, Inc. */ #include #include "mm-errors-types.h" #include "mm-network-timezone.h" /** * SECTION: mm-network-timezone * @title: MMNetworkTimezone * @short_description: Helper object to handle network timezone information. * * The #MMNetworkTimezone is an object handling the timezone information * reported by the network. * * This object is retrieved with either mm_modem_time_peek_network_timezone() * or mm_modem_time_get_network_timezone(). */ G_DEFINE_TYPE (MMNetworkTimezone, mm_network_timezone, G_TYPE_OBJECT); struct _MMNetworkTimezonePrivate { gint32 offset; gint32 dst_offset; gint32 leap_seconds; }; /*****************************************************************************/ /** * mm_network_timezone_get_offset: * @self: a #MMNetworkTimezone. * * Gets the timezone offset (in minutes) reported by the network. * * Returns: the offset, or %MM_NETWORK_TIMEZONE_OFFSET_UNKNOWN if unknown. * * Since: 1.0 */ gint mm_network_timezone_get_offset (MMNetworkTimezone *self) { g_return_val_if_fail (MM_IS_NETWORK_TIMEZONE (self), MM_NETWORK_TIMEZONE_OFFSET_UNKNOWN); return self->priv->offset; } /** * mm_network_timezone_set_offset: (skip) */ void mm_network_timezone_set_offset (MMNetworkTimezone *self, gint offset) { g_return_if_fail (MM_IS_NETWORK_TIMEZONE (self)); self->priv->offset = offset; } /*****************************************************************************/ /** * mm_network_timezone_get_dst_offset: * @self: a #MMNetworkTimezone. * * Gets the timezone offset due to daylight saving time (in minutes) reported by * the network. * * Returns: the offset, or %MM_NETWORK_TIMEZONE_OFFSET_UNKNOWN if unknown. * * Since: 1.0 */ gint mm_network_timezone_get_dst_offset (MMNetworkTimezone *self) { g_return_val_if_fail (MM_IS_NETWORK_TIMEZONE (self), MM_NETWORK_TIMEZONE_OFFSET_UNKNOWN); return self->priv->dst_offset; } /** * mm_network_timezone_set_dst_offset: (skip) */ void mm_network_timezone_set_dst_offset (MMNetworkTimezone *self, gint dst_offset) { g_return_if_fail (MM_IS_NETWORK_TIMEZONE (self)); self->priv->dst_offset = dst_offset; } /*****************************************************************************/ /** * mm_network_timezone_get_leap_seconds: * @self: a #MMNetworkTimezone. * * Gets the number of leap seconds (TAI-UTC), as reported by the network. * * Returns: the number of leap seconds, or * %MM_NETWORK_TIMEZONE_LEAP_SECONDS_UNKNOWN if unknown. * * Since: 1.0 */ gint mm_network_timezone_get_leap_seconds (MMNetworkTimezone *self) { g_return_val_if_fail (MM_IS_NETWORK_TIMEZONE (self), MM_NETWORK_TIMEZONE_LEAP_SECONDS_UNKNOWN); return self->priv->leap_seconds; } /** * mm_network_timezone_set_leap_seconds: (skip) */ void mm_network_timezone_set_leap_seconds (MMNetworkTimezone *self, gint leap_seconds) { g_return_if_fail (MM_IS_NETWORK_TIMEZONE (self)); self->priv->leap_seconds = leap_seconds; } /*****************************************************************************/ /** * mm_network_timezone_get_dictionary: (skip) */ GVariant * mm_network_timezone_get_dictionary (MMNetworkTimezone *self) { GVariantBuilder builder; /* Allow NULL */ if (!self) return NULL; g_return_val_if_fail (MM_IS_NETWORK_TIMEZONE (self), NULL); g_variant_builder_init (&builder, G_VARIANT_TYPE ("a{sv}")); if (self->priv->offset != MM_NETWORK_TIMEZONE_OFFSET_UNKNOWN) g_variant_builder_add (&builder, "{sv}", "offset", g_variant_new_int32 (self->priv->offset)); if (self->priv->dst_offset != MM_NETWORK_TIMEZONE_OFFSET_UNKNOWN) g_variant_builder_add (&builder, "{sv}", "dst-offset", g_variant_new_int32 (self->priv->dst_offset)); if (self->priv->leap_seconds != MM_NETWORK_TIMEZONE_LEAP_SECONDS_UNKNOWN) g_variant_builder_add (&builder, "{sv}", "leap-seconds", g_variant_new_int32 (self->priv->leap_seconds)); return g_variant_ref_sink (g_variant_builder_end (&builder)); } /*****************************************************************************/ /** * mm_network_timezone_new_from_dictionary: (skip) */ MMNetworkTimezone * mm_network_timezone_new_from_dictionary (GVariant *dictionary, GError **error) { GError *inner_error = NULL; GVariantIter iter; gchar *key; GVariant *value; MMNetworkTimezone *self; self = mm_network_timezone_new (); if (!dictionary) return self; if (!g_variant_is_of_type (dictionary, G_VARIANT_TYPE ("a{sv}"))) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS, "Cannot create Network Timezone from dictionary: " "invalid variant type received"); g_object_unref (self); return NULL; } g_variant_iter_init (&iter, dictionary); while (!inner_error && g_variant_iter_next (&iter, "{sv}", &key, &value)) { /* All currently supported properties are signed integers, * so we just check the value type here */ if (!g_variant_is_of_type (value, G_VARIANT_TYPE_INT32)) { /* Set inner error, will stop the loop */ inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS, "Invalid status dictionary, unexpected value type '%s'", g_variant_get_type_string (value)); } else if (g_str_equal (key, "offset")) self->priv->offset = g_variant_get_int32 (value); else if (g_str_equal (key, "dst-offset")) self->priv->dst_offset = g_variant_get_int32 (value); else if (g_str_equal (key, "leap-seconds")) self->priv->leap_seconds = g_variant_get_int32 (value); else { /* Set inner error, will stop the loop */ inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS, "Invalid status dictionary, unexpected key '%s'", key); } g_free (key); g_variant_unref (value); } /* If error, destroy the object */ if (inner_error) { g_propagate_error (error, inner_error); g_object_unref (self); return NULL; } return self; } /*****************************************************************************/ /** * mm_network_timezone_new: (skip) */ MMNetworkTimezone * mm_network_timezone_new (void) { return (MM_NETWORK_TIMEZONE ( g_object_new (MM_TYPE_NETWORK_TIMEZONE, NULL))); } static void mm_network_timezone_init (MMNetworkTimezone *self) { self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, MM_TYPE_NETWORK_TIMEZONE, MMNetworkTimezonePrivate); /* Some defaults */ self->priv->offset = MM_NETWORK_TIMEZONE_OFFSET_UNKNOWN; self->priv->dst_offset = MM_NETWORK_TIMEZONE_OFFSET_UNKNOWN; self->priv->leap_seconds = MM_NETWORK_TIMEZONE_LEAP_SECONDS_UNKNOWN; } static void mm_network_timezone_class_init (MMNetworkTimezoneClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); g_type_class_add_private (object_class, sizeof (MMNetworkTimezonePrivate)); } ModemManager-1.23.4-dev/libmm-glib/mm-network-timezone.h000066400000000000000000000077351456466623000230550ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * libmm-glib -- Access modem status & information from glib applications * * 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 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 Street, Fifth Floor, * Boston, MA 02110-1301 USA. * * Copyright (C) 2012 Google, Inc. */ #ifndef MM_NETWORK_TIMEZONE_H #define MM_NETWORK_TIMEZONE_H #if !defined (__LIBMM_GLIB_H_INSIDE__) && !defined (LIBMM_GLIB_COMPILATION) #error "Only can be included directly." #endif #include #include G_BEGIN_DECLS #define MM_TYPE_NETWORK_TIMEZONE (mm_network_timezone_get_type ()) #define MM_NETWORK_TIMEZONE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_NETWORK_TIMEZONE, MMNetworkTimezone)) #define MM_NETWORK_TIMEZONE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_NETWORK_TIMEZONE, MMNetworkTimezoneClass)) #define MM_IS_NETWORK_TIMEZONE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_NETWORK_TIMEZONE)) #define MM_IS_NETWORK_TIMEZONE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_NETWORK_TIMEZONE)) #define MM_NETWORK_TIMEZONE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_NETWORK_TIMEZONE, MMNetworkTimezoneClass)) /** * MM_NETWORK_TIMEZONE_OFFSET_UNKNOWN: * * Identifier for an unknown timezone offset. * * Since: 1.0 */ #define MM_NETWORK_TIMEZONE_OFFSET_UNKNOWN G_MAXINT32 /** * MM_NETWORK_TIMEZONE_LEAP_SECONDS_UNKNOWN: * * Identifier for an unknown leap seconds value. * * Since: 1.0 */ #define MM_NETWORK_TIMEZONE_LEAP_SECONDS_UNKNOWN G_MAXINT32 typedef struct _MMNetworkTimezone MMNetworkTimezone; typedef struct _MMNetworkTimezoneClass MMNetworkTimezoneClass; typedef struct _MMNetworkTimezonePrivate MMNetworkTimezonePrivate; /** * MMNetworkTimezone: * * The #MMNetworkTimezone structure contains private data and should * only be accessed using the provided API. */ struct _MMNetworkTimezone { /*< private >*/ GObject parent; MMNetworkTimezonePrivate *priv; }; struct _MMNetworkTimezoneClass { /*< private >*/ GObjectClass parent; }; GType mm_network_timezone_get_type (void); G_DEFINE_AUTOPTR_CLEANUP_FUNC (MMNetworkTimezone, g_object_unref) gint32 mm_network_timezone_get_offset (MMNetworkTimezone *self); gint32 mm_network_timezone_get_dst_offset (MMNetworkTimezone *self); gint32 mm_network_timezone_get_leap_seconds (MMNetworkTimezone *self); /*****************************************************************************/ /* ModemManager/libmm-glib/mmcli specific methods */ #if defined (_LIBMM_INSIDE_MM) || \ defined (_LIBMM_INSIDE_MMCLI) || \ defined (LIBMM_GLIB_COMPILATION) MMNetworkTimezone *mm_network_timezone_new (void); MMNetworkTimezone *mm_network_timezone_new_from_dictionary (GVariant *dictionary, GError **error); void mm_network_timezone_set_offset (MMNetworkTimezone *self, gint offset); void mm_network_timezone_set_dst_offset (MMNetworkTimezone *self, gint dst_offset); void mm_network_timezone_set_leap_seconds (MMNetworkTimezone *self, gint leap_seconds); GVariant *mm_network_timezone_get_dictionary (MMNetworkTimezone *self); #endif G_END_DECLS #endif /* MM_NETWORK_TIMEZONE_H */ ModemManager-1.23.4-dev/libmm-glib/mm-nr5g-registration-settings.c000066400000000000000000000242511456466623000247400ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * libmm-glib -- Access modem status & information from glib applications * * 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 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 Street, Fifth Floor, * Boston, MA 02110-1301 USA. * * Copyright (C) 2021 Aleksander Morgado */ #include #include "mm-errors-types.h" #include "mm-common-helpers.h" #include "mm-nr5g-registration-settings.h" /** * SECTION: mm-nr5g-registration-settings * @title: MMNr5gRegistrationSettings * @short_description: Helper object to handle 5GNR registration settings. * * The #MMNr5gRegistrationSettings is an object handling the settings used * to configure the 5G registration process. */ G_DEFINE_TYPE (MMNr5gRegistrationSettings, mm_nr5g_registration_settings, G_TYPE_OBJECT) #define PROPERTY_MICO_MODE "mico-mode" #define PROPERTY_DRX_CYCLE "drx-cycle" struct _MMNr5gRegistrationSettingsPrivate { MMModem3gppMicoMode mico_mode; MMModem3gppDrxCycle drx_cycle; }; /*****************************************************************************/ /** * mm_nr5g_registration_settings_set_mico_mode: * @self: a #MMNr5gRegistrationSettings. * @mico_mode: a #MMModem3gppMicoMode. * * Sets the MICO mode configuration. * * Since: 1.20 */ void mm_nr5g_registration_settings_set_mico_mode (MMNr5gRegistrationSettings *self, MMModem3gppMicoMode mico_mode) { g_return_if_fail (MM_IS_NR5G_REGISTRATION_SETTINGS (self)); self->priv->mico_mode = mico_mode; } /** * mm_nr5g_registration_settings_get_mico_mode: * @self: a #MMNr5gRegistrationSettings. * * Gets the MICO mode configuration. * * Returns: a #MMModem3gppMicoMode. * * Since: 1.20 */ MMModem3gppMicoMode mm_nr5g_registration_settings_get_mico_mode (MMNr5gRegistrationSettings *self) { g_return_val_if_fail (MM_IS_NR5G_REGISTRATION_SETTINGS (self), MM_MODEM_3GPP_MICO_MODE_UNKNOWN); return self->priv->mico_mode; } /*****************************************************************************/ /** * mm_nr5g_registration_settings_set_drx_cycle: * @self: a #MMNr5gRegistrationSettings. * @drx_cycle: a #MMModem3gppDrxCycle. * * Sets the MICO mode configuration. * * Since: 1.20 */ void mm_nr5g_registration_settings_set_drx_cycle (MMNr5gRegistrationSettings *self, MMModem3gppDrxCycle drx_cycle) { g_return_if_fail (MM_IS_NR5G_REGISTRATION_SETTINGS (self)); self->priv->drx_cycle = drx_cycle; } /** * mm_nr5g_registration_settings_get_drx_cycle: * @self: a #MMNr5gRegistrationSettings. * * Gets the MICO mode configuration. * * Returns: a #MMModem3gppDrxCycle. * * Since: 1.20 */ MMModem3gppDrxCycle mm_nr5g_registration_settings_get_drx_cycle (MMNr5gRegistrationSettings *self) { g_return_val_if_fail (MM_IS_NR5G_REGISTRATION_SETTINGS (self), MM_MODEM_3GPP_DRX_CYCLE_UNKNOWN); return self->priv->drx_cycle; } /*****************************************************************************/ static gboolean consume_string (MMNr5gRegistrationSettings *self, const gchar *key, const gchar *value, GError **error) { GError *inner_error = NULL; if (g_str_equal (key, PROPERTY_MICO_MODE)) { MMModem3gppMicoMode mico_mode; mico_mode = mm_common_get_3gpp_mico_mode_from_string (value, &inner_error); if (!inner_error) mm_nr5g_registration_settings_set_mico_mode (self, mico_mode); } else if (g_str_equal (key, PROPERTY_DRX_CYCLE)) { MMModem3gppDrxCycle drx_cycle; drx_cycle = mm_common_get_3gpp_drx_cycle_from_string (value, &inner_error); if (!inner_error) mm_nr5g_registration_settings_set_drx_cycle (self, drx_cycle); } else { inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "Invalid properties string, unsupported key '%s'", key); } if (inner_error) { g_propagate_error (error, inner_error); return FALSE; } return TRUE; } typedef struct { MMNr5gRegistrationSettings *settings; GError *error; } ParseKeyValueContext; static gboolean key_value_foreach (const gchar *key, const gchar *value, ParseKeyValueContext *ctx) { return consume_string (ctx->settings, key, value, &ctx->error); } /** * mm_nr5g_registration_settings_new_from_string: (skip) */ MMNr5gRegistrationSettings * mm_nr5g_registration_settings_new_from_string (const gchar *str, GError **error) { ParseKeyValueContext ctx; ctx.error = NULL; ctx.settings = mm_nr5g_registration_settings_new (); mm_common_parse_key_value_string (str, &ctx.error, (MMParseKeyValueForeachFn)key_value_foreach, &ctx); /* If error, destroy the object */ if (ctx.error) { g_propagate_error (error, ctx.error); g_clear_object (&ctx.settings); } return ctx.settings; } /*****************************************************************************/ /** * mm_nr5g_registration_settings_get_dictionary: (skip) */ GVariant * mm_nr5g_registration_settings_get_dictionary (MMNr5gRegistrationSettings *self) { GVariantBuilder builder; /* We do allow NULL */ if (!self) return NULL; g_return_val_if_fail (MM_IS_NR5G_REGISTRATION_SETTINGS (self), NULL); g_variant_builder_init (&builder, G_VARIANT_TYPE ("a{sv}")); if (self->priv->mico_mode != MM_MODEM_3GPP_MICO_MODE_UNKNOWN) g_variant_builder_add (&builder, "{sv}", PROPERTY_MICO_MODE, g_variant_new_uint32 (self->priv->mico_mode)); if (self->priv->drx_cycle != MM_MODEM_3GPP_DRX_CYCLE_UNKNOWN) g_variant_builder_add (&builder, "{sv}", PROPERTY_DRX_CYCLE, g_variant_new_uint32 (self->priv->drx_cycle)); return g_variant_ref_sink (g_variant_builder_end (&builder)); } /*****************************************************************************/ static gboolean consume_variant (MMNr5gRegistrationSettings *self, const gchar *key, GVariant *value, GError **error) { if (g_str_equal (key, PROPERTY_MICO_MODE)) self->priv->mico_mode = g_variant_get_uint32 (value); else if (g_str_equal (key, PROPERTY_DRX_CYCLE)) self->priv->drx_cycle = g_variant_get_uint32 (value); else { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS, "Invalid settings dictionary, unexpected key '%s'", key); return FALSE; } return TRUE; } /** * mm_nr5g_registration_settings_new_from_dictionary: (skip) */ MMNr5gRegistrationSettings * mm_nr5g_registration_settings_new_from_dictionary (GVariant *dictionary, GError **error) { g_autoptr(MMNr5gRegistrationSettings) self = NULL; GVariantIter iter; gchar *key; GVariant *value; GError *inner_error = NULL; self = mm_nr5g_registration_settings_new (); if (!dictionary) return g_steal_pointer (&self); if (!g_variant_is_of_type (dictionary, G_VARIANT_TYPE ("a{sv}"))) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS, "Invalid input type"); return NULL; } g_variant_iter_init (&iter, dictionary); while (!inner_error && g_variant_iter_next (&iter, "{sv}", &key, &value)) { consume_variant (self, key, value, &inner_error); g_free (key); g_variant_unref (value); } if (inner_error) { g_propagate_error (error, inner_error); return NULL; } return g_steal_pointer (&self); } /*****************************************************************************/ /** * mm_nr5g_registration_settings_cmp: (skip) */ gboolean mm_nr5g_registration_settings_cmp (MMNr5gRegistrationSettings *a, MMNr5gRegistrationSettings *b) { if (a->priv->mico_mode != b->priv->mico_mode) return FALSE; if (a->priv->drx_cycle != b->priv->drx_cycle) return FALSE; return TRUE; } /*****************************************************************************/ /** * mm_nr5g_registration_settings_new: * * Creates a new empty #MMNr5gRegistrationSettings. * * Returns: (transfer full): a #MMNr5gRegistrationSettings. The returned value should be freed with g_object_unref(). * * Since: 1.20 */ MMNr5gRegistrationSettings * mm_nr5g_registration_settings_new (void) { return MM_NR5G_REGISTRATION_SETTINGS (g_object_new (MM_TYPE_NR5G_REGISTRATION_SETTINGS, NULL)); } static void mm_nr5g_registration_settings_init (MMNr5gRegistrationSettings *self) { self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, MM_TYPE_NR5G_REGISTRATION_SETTINGS, MMNr5gRegistrationSettingsPrivate); self->priv->mico_mode = MM_MODEM_3GPP_MICO_MODE_UNKNOWN; self->priv->drx_cycle = MM_MODEM_3GPP_DRX_CYCLE_UNKNOWN; } static void mm_nr5g_registration_settings_class_init (MMNr5gRegistrationSettingsClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); g_type_class_add_private (object_class, sizeof (MMNr5gRegistrationSettingsPrivate)); } ModemManager-1.23.4-dev/libmm-glib/mm-nr5g-registration-settings.h000066400000000000000000000111271456466623000247430ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * libmm-glib -- Access modem status & information from glib applications * * 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 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 Street, Fifth Floor, * Boston, MA 02110-1301 USA. * * Copyright (C) 2021 Aleksander Morgado */ #ifndef MM_NR5G_REGISTRATION_SETTINGS_H #define MM_NR5G_REGISTRATION_SETTINGS_H #if !defined (__LIBMM_GLIB_H_INSIDE__) && !defined (LIBMM_GLIB_COMPILATION) #error "Only can be included directly." #endif #include #include G_BEGIN_DECLS #define MM_TYPE_NR5G_REGISTRATION_SETTINGS (mm_nr5g_registration_settings_get_type ()) #define MM_NR5G_REGISTRATION_SETTINGS(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_NR5G_REGISTRATION_SETTINGS, MMNr5gRegistrationSettings)) #define MM_NR5G_REGISTRATION_SETTINGS_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_NR5G_REGISTRATION_SETTINGS, MMNr5gRegistrationSettingsClass)) #define MM_IS_NR5G_REGISTRATION_SETTINGS(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_NR5G_REGISTRATION_SETTINGS)) #define MM_IS_NR5G_REGISTRATION_SETTINGS_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_NR5G_REGISTRATION_SETTINGS)) #define MM_NR5G_REGISTRATION_SETTINGS_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_NR5G_REGISTRATION_SETTINGS, MMNr5gRegistrationSettingsClass)) typedef struct _MMNr5gRegistrationSettings MMNr5gRegistrationSettings; typedef struct _MMNr5gRegistrationSettingsClass MMNr5gRegistrationSettingsClass; typedef struct _MMNr5gRegistrationSettingsPrivate MMNr5gRegistrationSettingsPrivate; /** * MMNr5gRegistrationSettings: * * The #MMNr5gRegistrationSettings structure contains private data and should only be accessed * using the provided API. */ struct _MMNr5gRegistrationSettings { /*< private >*/ GObject parent; MMNr5gRegistrationSettingsPrivate *priv; }; struct _MMNr5gRegistrationSettingsClass { /*< private >*/ GObjectClass parent; }; GType mm_nr5g_registration_settings_get_type (void); G_DEFINE_AUTOPTR_CLEANUP_FUNC (MMNr5gRegistrationSettings, g_object_unref) MMNr5gRegistrationSettings *mm_nr5g_registration_settings_new (void); void mm_nr5g_registration_settings_set_mico_mode (MMNr5gRegistrationSettings *self, MMModem3gppMicoMode mico_mode); MMModem3gppMicoMode mm_nr5g_registration_settings_get_mico_mode (MMNr5gRegistrationSettings *self); void mm_nr5g_registration_settings_set_drx_cycle (MMNr5gRegistrationSettings *self, MMModem3gppDrxCycle drx_cycle); MMModem3gppDrxCycle mm_nr5g_registration_settings_get_drx_cycle (MMNr5gRegistrationSettings *self); /*****************************************************************************/ /* ModemManager/libmm-glib/mmcli specific methods */ #if defined (_LIBMM_INSIDE_MM) || \ defined (_LIBMM_INSIDE_MMCLI) || \ defined (LIBMM_GLIB_COMPILATION) MMNr5gRegistrationSettings *mm_nr5g_registration_settings_new_from_string (const gchar *str, GError **error); MMNr5gRegistrationSettings *mm_nr5g_registration_settings_new_from_dictionary (GVariant *dictionary, GError **error); GVariant *mm_nr5g_registration_settings_get_dictionary (MMNr5gRegistrationSettings *self); gboolean mm_nr5g_registration_settings_cmp (MMNr5gRegistrationSettings *a, MMNr5gRegistrationSettings *b); #endif G_END_DECLS #endif /* MM_NR5G_REGISTRATION_SETTINGS_H */ ModemManager-1.23.4-dev/libmm-glib/mm-object.c000066400000000000000000000534351456466623000207730ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * libmm-glib -- Access modem status & information from glib applications * * 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 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 Street, Fifth Floor, * Boston, MA 02110-1301 USA. * * Copyright (C) 2011 - 2012 Aleksander Morgado * Copyright (C) 2012 Google, Inc. */ #include "mm-errors-types.h" #include "mm-object.h" /** * SECTION: mm-object * @title: MMObject * @short_description: Generic object representing a modem in ModemManager * * The #MMObject is a generic object which represents any kind of modem exposed * in ModemManager, and allows accessing the exported interfaces one by one. * * When this object is available, it is ensured that at least the Modem * interface is also available. */ G_DEFINE_TYPE (MMObject, mm_object, MM_GDBUS_TYPE_OBJECT_PROXY) /*****************************************************************************/ /** * mm_object_get_path: (skip) * @self: A #MMObject. * * Gets the DBus path of the #MMObject object. * * Returns: (transfer none): The DBus path of the #MMObject object. * * Since: 1.0 */ const gchar * mm_object_get_path (MMObject *self) { g_return_val_if_fail (MM_IS_OBJECT (self), NULL); return g_dbus_object_get_object_path (G_DBUS_OBJECT (self)); } /** * mm_object_dup_path: * @self: A #MMObject. * * Gets a copy of the DBus path of the #MMObject object. * * Returns: (transfer full): The DBus path of the #MMObject. The returned value * should be freed with g_free(). * * Since: 1.0 */ gchar * mm_object_dup_path (MMObject *self) { gchar *value; g_return_val_if_fail (MM_IS_OBJECT (self), NULL); g_object_get (G_OBJECT (self), "g-object-path", &value, NULL); return value; } /*****************************************************************************/ /** * mm_object_get_modem: * @self: A #MMModem * * Gets the #MMModem instance for the D-Bus interface * org.freedesktop.ModemManager1.Modem on @self, if any. * * Returns: (transfer full): A #MMModem that must be freed with g_object_unref() * or %NULL if @self does not implement the interface. * * Since: 1.0 */ MMModem * mm_object_get_modem (MMObject *self) { MMModem *modem; g_return_val_if_fail (MM_IS_OBJECT (MM_GDBUS_OBJECT (self)), NULL); modem = (MMModem *)mm_gdbus_object_get_modem (MM_GDBUS_OBJECT (self)); g_warn_if_fail (MM_IS_MODEM (modem)); return modem; } /** * mm_object_peek_modem: (skip) * @self: A #MMObject. * * Like mm_object_get_modem() but doesn't increase the reference count on the * returned object. * * It is not safe to use the returned object if you are on another * thread than the one where the #MMManager is running. * * Returns: (transfer none): A #MMModem or %NULL if @self does not implement * the interface. Do not free the returned object, it is owned by @self. * * Since: 1.0 */ MMModem * mm_object_peek_modem (MMObject *self) { MMModem *modem; g_return_val_if_fail (MM_IS_OBJECT (MM_GDBUS_OBJECT (self)), NULL); modem = (MMModem *) mm_gdbus_object_peek_modem (MM_GDBUS_OBJECT (self)); g_warn_if_fail (MM_IS_MODEM (modem)); return modem; } /*****************************************************************************/ /** * mm_object_get_modem_3gpp: * @self: A #MMObject. * * Gets the #MMModem3gpp instance for the D-Bus interface * org.freedesktop.ModemManager1.Modem.Modem3gpp on @self, if any. * * Returns: (transfer full): A #MMModem3gpp that must be freed with * g_object_unref() or %NULL if @self does not implement the interface. * * Since: 1.0 */ MMModem3gpp * mm_object_get_modem_3gpp (MMObject *self) { g_return_val_if_fail (MM_IS_OBJECT (MM_GDBUS_OBJECT (self)), NULL); return (MMModem3gpp *)mm_gdbus_object_get_modem3gpp (MM_GDBUS_OBJECT (self)); } /** * mm_object_peek_modem_3gpp: (skip) * @self: A #MMObject. * * Like mm_object_get_modem_3gpp() but doesn't increase the reference count on * the returned object. * * It is not safe to use the returned object if you are on another * thread than the one where the #MMManager is running. * * Returns: (transfer none): A #MMModem3gpp or %NULL if @self does not implement * the interface. Do not free the returned object, it is owned by @self. * * Since: 1.0 */ MMModem3gpp * mm_object_peek_modem_3gpp (MMObject *self) { g_return_val_if_fail (MM_IS_OBJECT (MM_GDBUS_OBJECT (self)), NULL); return (MMModem3gpp *)mm_gdbus_object_peek_modem3gpp (MM_GDBUS_OBJECT (self)); } /*****************************************************************************/ /** * mm_object_get_modem_3gpp_profile_manager: * @self: A #MMObject. * * Gets the #MMModem3gppProfileManager instance for the D-Bus interface * org.freedesktop.ModemManager1.Modem.Modem3gpp-ProfileManager on @self, if any. * * Returns: (transfer full): A #MMModem3gppProfileManager that must be freed with * g_object_unref() or %NULL if @self does not implement the interface. * * Since: 1.18 */ MMModem3gppProfileManager * mm_object_get_modem_3gpp_profile_manager (MMObject *self) { g_return_val_if_fail (MM_IS_OBJECT (MM_GDBUS_OBJECT (self)), NULL); return (MMModem3gppProfileManager *)mm_gdbus_object_get_modem3gpp_profile_manager (MM_GDBUS_OBJECT (self)); } /** * mm_object_peek_modem_3gpp_profile_manager: (skip) * @self: A #MMObject. * * Like mm_object_get_modem_3gpp_profile_manager() but doesn't increase the reference count * on the returned object. * * It is not safe to use the returned object if you are on another * thread than the one where the #MMManager is running. * * Returns: (transfer none): A #MMModem3gppProfileManager or %NULL if @self does not * implement the interface. Do not free the returned object, it is owned by * @self. * * Since: 1.18 */ MMModem3gppProfileManager * mm_object_peek_modem_3gpp_profile_manager (MMObject *self) { g_return_val_if_fail (MM_IS_OBJECT (MM_GDBUS_OBJECT (self)), NULL); return (MMModem3gppProfileManager *)mm_gdbus_object_peek_modem3gpp_profile_manager (MM_GDBUS_OBJECT (self)); } /*****************************************************************************/ /** * mm_object_get_modem_3gpp_ussd: * @self: A #MMObject. * * Gets the #MMModem3gppUssd instance for the D-Bus interface * org.freedesktop.ModemManager1.Modem.Modem3gpp-Ussd on @self, if any. * * Returns: (transfer full): A #MMModem3gppUssd that must be freed with * g_object_unref() or %NULL if @self does not implement the interface. * * Since: 1.0 */ MMModem3gppUssd * mm_object_get_modem_3gpp_ussd (MMObject *self) { g_return_val_if_fail (MM_IS_OBJECT (MM_GDBUS_OBJECT (self)), NULL); return (MMModem3gppUssd *)mm_gdbus_object_get_modem3gpp_ussd (MM_GDBUS_OBJECT (self)); } /** * mm_object_peek_modem_3gpp_ussd: (skip) * @self: A #MMObject. * * Like mm_object_get_modem_3gpp_ussd() but doesn't increase the reference count * on the returned object. * * It is not safe to use the returned object if you are on another * thread than the one where the #MMManager is running. * * Returns: (transfer none): A #MMModem3gppUssd or %NULL if @self does not * implement the interface. Do not free the returned object, it is owned by * @self. * * Since: 1.0 */ MMModem3gppUssd * mm_object_peek_modem_3gpp_ussd (MMObject *self) { g_return_val_if_fail (MM_IS_OBJECT (MM_GDBUS_OBJECT (self)), NULL); return (MMModem3gppUssd *)mm_gdbus_object_peek_modem3gpp_ussd (MM_GDBUS_OBJECT (self)); } /*****************************************************************************/ /** * mm_object_get_modem_cdma: * @self: A #MMObject. * * Gets the #MMModemCdma instance for the D-Bus interface * org.freedesktop.ModemManager1.Modem.ModemCdma on @self, if any. * * Returns: (transfer full): A #MMModemCdma that must be freed with * g_object_unref() or %NULL if @self does not implement the interface. * * Since: 1.0 */ MMModemCdma * mm_object_get_modem_cdma (MMObject *self) { g_return_val_if_fail (MM_IS_OBJECT (MM_GDBUS_OBJECT (self)), NULL); return (MMModemCdma *)mm_gdbus_object_get_modem_cdma (MM_GDBUS_OBJECT (self)); } /** * mm_object_peek_modem_cdma: (skip) * @self: A #MMObject. * * Like mm_object_get_modem_cdma() but doesn't increase the reference count on * the returned object. * * It is not safe to use the returned object if you are on another * thread than the one where the #MMManager is running. * * Returns: (transfer none): A #MMModemCdma or %NULL if @self does not implement * the interface. Do not free the returned object, it is owned by @self. * * Since: 1.0 */ MMModemCdma * mm_object_peek_modem_cdma (MMObject *self) { g_return_val_if_fail (MM_IS_OBJECT (MM_GDBUS_OBJECT (self)), NULL); return (MMModemCdma *)mm_gdbus_object_peek_modem_cdma (MM_GDBUS_OBJECT (self)); } /*****************************************************************************/ /** * mm_object_get_modem_simple: * @self: A #MMObject. * * Gets the #MMModemSimple instance for the D-Bus interface * org.freedesktop.ModemManager1.Modem.Modemsimple on @self, if any. * * Returns: (transfer full): A #MMModemSimple that must be freed with * g_object_unref() or %NULL if @self does not implement the interface. * * Since: 1.0 */ MMModemSimple * mm_object_get_modem_simple (MMObject *self) { g_return_val_if_fail (MM_IS_OBJECT (MM_GDBUS_OBJECT (self)), NULL); return (MMModemSimple *)mm_gdbus_object_get_modem_simple (MM_GDBUS_OBJECT (self)); } /** * mm_object_peek_modem_simple: (skip) * @self: A #MMObject. * * Like mm_object_get_modem_simple() but doesn't increase the reference count on * the returned object. * * It is not safe to use the returned object if you are on another * thread than the one where the #MMManager is running. * * Returns: (transfer none): A #MMModemSimple or %NULL if @self does not * implement the interface. Do not free the returned object, it is owned by * @self. * * Since: 1.0 */ MMModemSimple * mm_object_peek_modem_simple (MMObject *self) { g_return_val_if_fail (MM_IS_OBJECT (MM_GDBUS_OBJECT (self)), NULL); return (MMModemSimple *)mm_gdbus_object_peek_modem_simple (MM_GDBUS_OBJECT (self)); } /*****************************************************************************/ /** * mm_object_get_modem_location: * @self: A #MMObject. * * Gets the #MMModemLocation instance for the D-Bus interface * org.freedesktop.ModemManager1.Modem.Modemlocation on @self, if any. * * Returns: (transfer full): A #MMModemLocation that must be freed with * g_object_unref() or %NULL if @self does not implement the interface. * * Since: 1.0 */ MMModemLocation * mm_object_get_modem_location (MMObject *self) { g_return_val_if_fail (MM_IS_OBJECT (MM_GDBUS_OBJECT (self)), NULL); return (MMModemLocation *)mm_gdbus_object_get_modem_location (MM_GDBUS_OBJECT (self)); } /** * mm_object_peek_modem_location: (skip) * @self: A #MMObject. * * Like mm_object_get_modem_location() but doesn't increase the reference count * on the returned object. * * It is not safe to use the returned object if you are on another * thread than the one where the #MMManager is running. * * Returns: (transfer none): A #MMModemLocation or %NULL if @self does not * implement the interface. Do not free the returned object, it is owned by * @self. * * Since: 1.0 */ MMModemLocation * mm_object_peek_modem_location (MMObject *self) { g_return_val_if_fail (MM_IS_OBJECT (MM_GDBUS_OBJECT (self)), NULL); return (MMModemLocation *)mm_gdbus_object_peek_modem_location (MM_GDBUS_OBJECT (self)); } /*****************************************************************************/ /** * mm_object_get_modem_messaging: * @self: A #MMObject. * * Gets the #MMModemMessaging instance for the D-Bus interface * org.freedesktop.ModemManager1.Modem.Modemmessaging on @self, if any. * * Returns: (transfer full): A #MMModemMessaging that must be freed with * g_object_unref() or %NULL if @self does not implement the interface. * * Since: 1.0 */ MMModemMessaging * mm_object_get_modem_messaging (MMObject *self) { g_return_val_if_fail (MM_IS_OBJECT (MM_GDBUS_OBJECT (self)), NULL); return (MMModemMessaging *)mm_gdbus_object_get_modem_messaging (MM_GDBUS_OBJECT (self)); } /** * mm_object_peek_modem_messaging: (skip) * @self: A #MMObject. * * Like mm_object_get_modem_messaging() but doesn't increase the reference count * on the returned object. * * It is not safe to use the returned object if you are on another * thread than the one where the #MMManager is running. * * Returns: (transfer none): A #MMModemMessaging or %NULL if @self does not * implement the interface. Do not free the returned object, it is owned by * @self. * * Since: 1.0 */ MMModemMessaging * mm_object_peek_modem_messaging (MMObject *self) { g_return_val_if_fail (MM_IS_OBJECT (MM_GDBUS_OBJECT (self)), NULL); return (MMModemMessaging *)mm_gdbus_object_peek_modem_messaging (MM_GDBUS_OBJECT (self)); } /*****************************************************************************/ /** * mm_object_get_modem_voice: * @self: A #MMObject. * * Gets the #MMModemVoice instance for the D-Bus interface * org.freedesktop.ModemManager1.Modem.Modemvoice on @self, if any. * * Returns: (transfer full): A #MMModemVoice that must be freed with * g_object_unref() or %NULL if @self does not implement the interface. * * Since: 1.6 */ MMModemVoice * mm_object_get_modem_voice (MMObject *self) { g_return_val_if_fail (MM_IS_OBJECT (MM_GDBUS_OBJECT (self)), NULL); return (MMModemVoice *)mm_gdbus_object_get_modem_voice (MM_GDBUS_OBJECT (self)); } /** * mm_object_peek_modem_voice: (skip) * @self: A #MMObject. * * Like mm_object_get_modem_voice() but doesn't increase the reference count on * the returned object. * * It is not safe to use the returned object if you are on another * thread than the one where the #MMManager is running. * * Returns: (transfer none): A #MMModemVoice or %NULL if @self does not * implement the interface. Do not free the returned object, it is owned by * @self. * * Since: 1.6 */ MMModemVoice * mm_object_peek_modem_voice (MMObject *self) { g_return_val_if_fail (MM_IS_OBJECT (MM_GDBUS_OBJECT (self)), NULL); return (MMModemVoice *)mm_gdbus_object_peek_modem_voice (MM_GDBUS_OBJECT (self)); } /*****************************************************************************/ /** * mm_object_get_modem_time: * @self: A #MMObject. * * Gets the #MMModemTime instance for the D-Bus interface * org.freedesktop.ModemManager1.Modem.Time on @self, if any. * * Returns: (transfer full): A #MMModemTime that must be freed with * g_object_unref() or %NULL if @self does not implement the interface. * * Since: 1.0 */ MMModemTime * mm_object_get_modem_time (MMObject *self) { g_return_val_if_fail (MM_IS_OBJECT (MM_GDBUS_OBJECT (self)), NULL); return (MMModemTime *)mm_gdbus_object_get_modem_time (MM_GDBUS_OBJECT (self)); } /** * mm_object_peek_modem_time: (skip) * @self: A #MMObject. * * Like mm_object_get_modem_time() but doesn't increase the reference count on * the returned object. * * It is not safe to use the returned object if you are on another * thread than the one where the #MMManager is running. * * Returns: (transfer none): A #MMModemTime or %NULL if @self does not implement * the interface. Do not free the returned object, it is owned by @self. * * Since: 1.0 */ MMModemTime * mm_object_peek_modem_time (MMObject *self) { g_return_val_if_fail (MM_IS_OBJECT (MM_GDBUS_OBJECT (self)), NULL); return (MMModemTime *)mm_gdbus_object_peek_modem_time (MM_GDBUS_OBJECT (self)); } /*****************************************************************************/ /** * mm_object_get_modem_firmware: * @self: A #MMObject. * * Gets the #MMModemFirmware instance for the D-Bus interface * org.freedesktop.ModemManager1.Modem.Firmware on @self, if any. * * Returns: (transfer full): A #MMModemFirmware that must be freed with * g_object_unref() or %NULL if @self does not implement the interface. * * Since: 1.0 */ MMModemFirmware * mm_object_get_modem_firmware (MMObject *self) { g_return_val_if_fail (MM_IS_OBJECT (MM_GDBUS_OBJECT (self)), NULL); return (MMModemFirmware *)mm_gdbus_object_get_modem_firmware (MM_GDBUS_OBJECT (self)); } /** * mm_object_peek_modem_firmware: (skip) * @self: A #MMObject. * * Like mm_object_get_modem_firmware() but doesn't increase the reference count * on the returned object. * * It is not safe to use the returned object if you are on another * thread than the one where the #MMManager is running. * * Returns: (transfer none): A #MMModemFirmware or %NULL if @self does not * implement the interface. Do not free the returned object, it is owned by * @self. * * Since: 1.0 */ MMModemFirmware * mm_object_peek_modem_firmware (MMObject *self) { g_return_val_if_fail (MM_IS_OBJECT (MM_GDBUS_OBJECT (self)), NULL); return (MMModemFirmware *)mm_gdbus_object_peek_modem_firmware (MM_GDBUS_OBJECT (self)); } /*****************************************************************************/ /** * mm_object_get_modem_sar: * @self: A #MMObject. * * Gets the #MMModemSar instance for the D-Bus interface * org.freedesktop.ModemManager1.Modem.Sar on @self, if any. * * Returns: (transfer full): A #MMModemSar that must be freed with * g_object_unref() or %NULL if @self does not implement the interface. * * Since: 1.20 */ MMModemSar * mm_object_get_modem_sar (MMObject *self) { g_return_val_if_fail (MM_IS_OBJECT (MM_GDBUS_OBJECT (self)), NULL); return (MMModemSar *)mm_gdbus_object_get_modem_sar (MM_GDBUS_OBJECT (self)); } /** * mm_object_peek_modem_sar: (skip) * @self: A #MMObject. * * Like mm_object_get_modem_sar() but doesn't increase the reference count * on the returned object. * * It is not safe to use the returned object if you are on another * thread than the one where the #MMManager is running. * * Returns: (transfer none): A #MMModemSar or %NULL if @self does not * implement the interface. Do not free the returned object, it is owned by * @self. * * Since: 1.20 */ MMModemSar * mm_object_peek_modem_sar (MMObject *self) { g_return_val_if_fail (MM_IS_OBJECT (MM_GDBUS_OBJECT (self)), NULL); return (MMModemSar *)mm_gdbus_object_peek_modem_sar (MM_GDBUS_OBJECT (self)); } /*****************************************************************************/ /** * mm_object_get_modem_signal: * @self: A #MMObject. * * Gets the #MMModemSignal instance for the D-Bus interface * org.freedesktop.ModemManager1.Modem.Signal on @self, if any. * * Returns: (transfer full): A #MMModemSignal that must be freed with * g_object_unref() or %NULL if @self does not implement the interface. * * Since: 1.2 */ MMModemSignal * mm_object_get_modem_signal (MMObject *self) { g_return_val_if_fail (MM_IS_OBJECT (MM_GDBUS_OBJECT (self)), NULL); return (MMModemSignal *)mm_gdbus_object_get_modem_signal (MM_GDBUS_OBJECT (self)); } /** * mm_object_peek_modem_signal: (skip) * @self: A #MMObject. * * Like mm_object_get_modem_signal() but doesn't increase the reference count on * the returned object. * * It is not safe to use the returned object if you are on another * thread than the one where the #MMManager is running. * * Returns: (transfer none): A #MMModemSignal or %NULL if @self does not * implement the interface. Do not free the returned object, it is owned by * @self. * * Since: 1.2 */ MMModemSignal * mm_object_peek_modem_signal (MMObject *self) { g_return_val_if_fail (MM_IS_OBJECT (MM_GDBUS_OBJECT (self)), NULL); return (MMModemSignal *)mm_gdbus_object_peek_modem_signal (MM_GDBUS_OBJECT (self)); } /*****************************************************************************/ /** * mm_object_get_modem_oma: * @self: A #MMObject. * * Gets the #MMModemOma instance for the D-Bus interface * org.freedesktop.ModemManager1.Modem.Oma on @self, if any. * * Returns: (transfer full): A #MMModemOma that must be freed with * g_object_unref() or %NULL if @self does not implement the interface. * * Since: 1.2 */ MMModemOma * mm_object_get_modem_oma (MMObject *self) { g_return_val_if_fail (MM_IS_OBJECT (MM_GDBUS_OBJECT (self)), NULL); return (MMModemOma *)mm_gdbus_object_get_modem_oma (MM_GDBUS_OBJECT (self)); } /** * mm_object_peek_modem_oma: (skip) * @self: A #MMObject. * * Like mm_object_get_modem_oma() but doesn't increase the reference count on * the returned object. * * It is not safe to use the returned object if you are on another * thread than the one where the #MMManager is running. * * Returns: (transfer none): A #MMModemOma or %NULL if @self does not implement * the interface. Do not free the returned object, it is owned by @self. * * Since: 1.2 */ MMModemOma * mm_object_peek_modem_oma (MMObject *self) { g_return_val_if_fail (MM_IS_OBJECT (MM_GDBUS_OBJECT (self)), NULL); return (MMModemOma *)mm_gdbus_object_peek_modem_oma (MM_GDBUS_OBJECT (self)); } /*****************************************************************************/ static void mm_object_init (MMObject *self) { } static void mm_object_class_init (MMObjectClass *object_class) { } ModemManager-1.23.4-dev/libmm-glib/mm-object.h000066400000000000000000000122531456466623000207710ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * libmm-glib -- Access modem status & information from glib applications * * 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 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 Street, Fifth Floor, * Boston, MA 02110-1301 USA. * * Copyright (C) 2011 - 2012 Aleksander Morgado * Copyright (C) 2012 Google, Inc. */ #ifndef _MM_OBJECT_H_ #define _MM_OBJECT_H_ #if !defined (__LIBMM_GLIB_H_INSIDE__) && !defined (LIBMM_GLIB_COMPILATION) #error "Only can be included directly." #endif #include #include "mm-gdbus-modem.h" #include "mm-modem.h" #include "mm-modem-3gpp.h" #include "mm-modem-3gpp-profile-manager.h" #include "mm-modem-3gpp-ussd.h" #include "mm-modem-cdma.h" #include "mm-modem-simple.h" #include "mm-modem-location.h" #include "mm-modem-messaging.h" #include "mm-modem-voice.h" #include "mm-modem-time.h" #include "mm-modem-firmware.h" #include "mm-modem-sar.h" #include "mm-modem-signal.h" #include "mm-modem-oma.h" G_BEGIN_DECLS #define MM_TYPE_OBJECT (mm_object_get_type ()) #define MM_OBJECT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_OBJECT, MMObject)) #define MM_OBJECT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_OBJECT, MMObjectClass)) #define MM_IS_OBJECT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_OBJECT)) #define MM_IS_OBJECT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), MM_TYPE_OBJECT)) #define MM_OBJECT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_OBJECT, MMObjectClass)) typedef struct _MMObject MMObject; typedef struct _MMObjectClass MMObjectClass; /** * MMObject: * * The #MMObject structure contains private data and should only be accessed * using the provided API. */ struct _MMObject { /*< private >*/ MmGdbusObjectProxy parent; gpointer unused; }; struct _MMObjectClass { /*< private >*/ MmGdbusObjectProxyClass parent; }; GType mm_object_get_type (void); G_DEFINE_AUTOPTR_CLEANUP_FUNC (MMObject, g_object_unref) const gchar *mm_object_get_path (MMObject *self); gchar *mm_object_dup_path (MMObject *self); MMModem *mm_object_get_modem (MMObject *self); MMModem3gpp *mm_object_get_modem_3gpp (MMObject *self); MMModem3gppProfileManager *mm_object_get_modem_3gpp_profile_manager (MMObject *self); MMModem3gppUssd *mm_object_get_modem_3gpp_ussd (MMObject *self); MMModemCdma *mm_object_get_modem_cdma (MMObject *self); MMModemSimple *mm_object_get_modem_simple (MMObject *self); MMModemLocation *mm_object_get_modem_location (MMObject *self); MMModemMessaging *mm_object_get_modem_messaging (MMObject *self); MMModemVoice *mm_object_get_modem_voice (MMObject *self); MMModemTime *mm_object_get_modem_time (MMObject *self); MMModemFirmware *mm_object_get_modem_firmware (MMObject *self); MMModemSar *mm_object_get_modem_sar (MMObject *self); MMModemSignal *mm_object_get_modem_signal (MMObject *self); MMModemOma *mm_object_get_modem_oma (MMObject *self); MMModem *mm_object_peek_modem (MMObject *self); MMModem3gpp *mm_object_peek_modem_3gpp (MMObject *self); MMModem3gppProfileManager *mm_object_peek_modem_3gpp_profile_manager (MMObject *self); MMModem3gppUssd *mm_object_peek_modem_3gpp_ussd (MMObject *self); MMModemCdma *mm_object_peek_modem_cdma (MMObject *self); MMModemSimple *mm_object_peek_modem_simple (MMObject *self); MMModemLocation *mm_object_peek_modem_location (MMObject *self); MMModemMessaging *mm_object_peek_modem_messaging (MMObject *self); MMModemVoice *mm_object_peek_modem_voice (MMObject *self); MMModemTime *mm_object_peek_modem_time (MMObject *self); MMModemFirmware *mm_object_peek_modem_firmware (MMObject *self); MMModemSar *mm_object_peek_modem_sar (MMObject *self); MMModemSignal *mm_object_peek_modem_signal (MMObject *self); MMModemOma *mm_object_peek_modem_oma (MMObject *self); G_END_DECLS #endif /* _MM_OBJECT_H_ */ ModemManager-1.23.4-dev/libmm-glib/mm-pco.c000066400000000000000000000176431456466623000203070ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * libmm-glib -- Access modem status & information from glib applications * * 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 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 Street, Fifth Floor, * Boston, MA 02110-1301 USA. * * Copyright 2018 Google LLC. */ #include #include #include "mm-enums-types.h" #include "mm-flags-types.h" #include "mm-errors-types.h" #include "mm-common-helpers.h" #include "mm-pco.h" /** * SECTION: mm-pco * @title: MMPco * @short_description: Helper object to handle 3GPP PCO. * * The #MMPco is an object handling the raw 3GPP Protocol Configuration Options * (PCO) that the modem has received from the network. * * This object is retrieved with mm_modem_3gpp_get_pco(). */ G_DEFINE_TYPE (MMPco, mm_pco, G_TYPE_OBJECT) struct _MMPcoPrivate { /* Session ID, signature 'u' */ guint32 session_id; /* Flag indicating if the PCO data is complete or partial, signature 'b' */ gboolean is_complete; /* Raw PCO data, signature 'ay' */ GBytes *data; }; /*****************************************************************************/ static GBytes * _g_variant_get_bytes (GVariant *variant) { GByteArray *byte_array; guint num_bytes; GVariantIter iter; guint8 byte; g_assert (g_variant_is_of_type (variant, G_VARIANT_TYPE ("ay"))); num_bytes = g_variant_n_children (variant); if (num_bytes == 0) return NULL; byte_array = g_byte_array_sized_new (num_bytes); g_variant_iter_init (&iter, variant); while (g_variant_iter_loop (&iter, "y", &byte)) g_byte_array_append (byte_array, &byte, sizeof (byte)); return g_byte_array_free_to_bytes (byte_array); } /*****************************************************************************/ /** * mm_pco_get_session_id: * @self: a #MMPco. * * Gets the session ID associated with the PCO. * * Returns: the session ID. * * Since: 1.10 */ guint32 mm_pco_get_session_id (MMPco *self) { g_return_val_if_fail (MM_IS_PCO (self), G_MAXUINT32); return self->priv->session_id; } /** * mm_pco_set_session_id: (skip) */ void mm_pco_set_session_id (MMPco *self, guint32 session_id) { g_return_if_fail (MM_IS_PCO (self)); self->priv->session_id = session_id; } /*****************************************************************************/ /** * mm_pco_is_complete: * @self: a #MMPco. * * Gets the complete flag that indicates whether the PCO data contains the * complete PCO structure received from the network. * * Returns: %TRUE if the PCO data contains the complete PCO structure, %FALSE * otherwise. * * Since: 1.10 */ gboolean mm_pco_is_complete (MMPco *self) { g_return_val_if_fail (MM_IS_PCO (self), FALSE); return self->priv->is_complete; } /** * mm_pco_set_complete: (skip) */ void mm_pco_set_complete (MMPco *self, gboolean is_complete) { g_return_if_fail (MM_IS_PCO (self)); self->priv->is_complete = is_complete; } /*****************************************************************************/ /** * mm_pco_get_data: * @self: a #MMPco. * @data_size: (out): Size of the PCO data, if any given. * * Gets the PCO data in raw bytes. * * Returns: (transfer none): the PCO data, or %NULL if it doesn't contain any. * * Since: 1.10 */ const guint8 * mm_pco_get_data (MMPco *self, gsize *data_size) { g_return_val_if_fail (MM_IS_PCO (self), NULL); return g_bytes_get_data (self->priv->data, data_size); } /** * mm_pco_set_data: (skip) */ void mm_pco_set_data (MMPco *self, const guint8 *data, gsize data_size) { g_return_if_fail (MM_IS_PCO (self)); g_bytes_unref (self->priv->data); self->priv->data = (data && data_size) ? g_bytes_new (data, data_size) : NULL; } /*****************************************************************************/ /** * mm_pco_from_variant: (skip) */ MMPco * mm_pco_from_variant (GVariant *variant, GError **error) { MMPco *pco; GVariant *pco_data = NULL; pco = mm_pco_new (); if (!variant) return pco; if (!g_variant_is_of_type (variant, G_VARIANT_TYPE ("(ubay)"))) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS, "Cannot create PCO from variant: " "invalid variant type received"); g_object_unref (pco); return NULL; } g_variant_get (variant, "(ub@ay)", &pco->priv->session_id, &pco->priv->is_complete, &pco_data); g_bytes_unref (pco->priv->data); pco->priv->data = _g_variant_get_bytes (pco_data); g_variant_unref (pco_data); return pco; } /*****************************************************************************/ /** * mm_pco_to_variant: (skip) */ GVariant * mm_pco_to_variant (MMPco *self) { GVariantBuilder builder; gsize i, pco_data_size; const guint8 *pco_data; /* Allow NULL */ if (!self) return NULL; g_return_val_if_fail (MM_IS_PCO (self), NULL); g_variant_builder_init (&builder, G_VARIANT_TYPE ("(ubay)")); g_variant_builder_add (&builder, "u", self->priv->session_id); g_variant_builder_add (&builder, "b", self->priv->is_complete); g_variant_builder_open (&builder, G_VARIANT_TYPE ("ay")); if (self->priv->data) { pco_data = g_bytes_get_data (self->priv->data, &pco_data_size); for (i = 0; i < pco_data_size; ++i) g_variant_builder_add (&builder, "y", pco_data[i]); } g_variant_builder_close (&builder); return g_variant_ref_sink (g_variant_builder_end (&builder)); } /*****************************************************************************/ /** * mm_pco_list_add: (skip) */ GList * mm_pco_list_add (GList *pco_list, MMPco *pco) { GList *iter; guint32 session_id; g_return_val_if_fail (pco != NULL, pco_list); session_id = mm_pco_get_session_id (pco); for (iter = g_list_first (pco_list); iter; iter = g_list_next (iter)) { MMPco *iter_pco = iter->data; guint32 iter_session_id = mm_pco_get_session_id (iter_pco); if (iter_session_id < session_id) continue; else if (iter_session_id == session_id) { iter->data = g_object_ref (pco); g_object_unref (iter_pco); return pco_list; } else break; } return g_list_insert_before (pco_list, iter, g_object_ref (pco)); } /*****************************************************************************/ /** * mm_pco_new: (skip) */ MMPco * mm_pco_new (void) { return (MM_PCO (g_object_new (MM_TYPE_PCO, NULL))); } static void mm_pco_init (MMPco *self) { self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, MM_TYPE_PCO, MMPcoPrivate); self->priv->session_id = G_MAXUINT32; } static void finalize (GObject *object) { MMPco *self = MM_PCO (object); g_bytes_unref (self->priv->data); G_OBJECT_CLASS (mm_pco_parent_class)->finalize (object); } static void mm_pco_class_init (MMPcoClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); g_type_class_add_private (object_class, sizeof (MMPcoPrivate)); object_class->finalize = finalize; } ModemManager-1.23.4-dev/libmm-glib/mm-pco.h000066400000000000000000000063201456466623000203020ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * libmm-glib -- Access modem status & information from glib applications * * 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 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 Street, Fifth Floor, * Boston, MA 02110-1301 USA. * * Copyright 2018 Google LLC. */ #ifndef MM_PCO_H #define MM_PCO_H #if !defined (__LIBMM_GLIB_H_INSIDE__) && !defined (LIBMM_GLIB_COMPILATION) #error "Only can be included directly." #endif #include #include G_BEGIN_DECLS #define MM_TYPE_PCO (mm_pco_get_type ()) #define MM_PCO(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_PCO, MMPco)) #define MM_PCO_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_PCO, MMPcoClass)) #define MM_IS_PCO(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_PCO)) #define MM_IS_PCO_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_PCO)) #define MM_PCO_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_PCO, MMPcoClass)) typedef struct _MMPco MMPco; typedef struct _MMPcoClass MMPcoClass; typedef struct _MMPcoPrivate MMPcoPrivate; /** * MMPco: * * The #MMPco structure contains private data and should only be accessed * using the provided API. */ struct _MMPco { /*< private >*/ GObject parent; MMPcoPrivate *priv; }; struct _MMPcoClass { /*< private >*/ GObjectClass parent; }; GType mm_pco_get_type (void); G_DEFINE_AUTOPTR_CLEANUP_FUNC (MMPco, g_object_unref) guint32 mm_pco_get_session_id (MMPco *self); gboolean mm_pco_is_complete (MMPco *self); const guint8 *mm_pco_get_data (MMPco *self, gsize *data_size); /*****************************************************************************/ /* ModemManager/libmm-glib/mmcli specific methods */ #if defined (_LIBMM_INSIDE_MM) || \ defined (_LIBMM_INSIDE_MMCLI) || \ defined (LIBMM_GLIB_COMPILATION) MMPco *mm_pco_new (void); MMPco *mm_pco_from_variant (GVariant *variant, GError **error); GVariant *mm_pco_to_variant (MMPco *self); void mm_pco_set_session_id (MMPco *self, guint32 session_id); void mm_pco_set_complete (MMPco *self, gboolean is_complete); void mm_pco_set_data (MMPco *self, const guint8 *data, gsize data_size); GList *mm_pco_list_add (GList *pco_list, MMPco *pco); #endif G_END_DECLS #endif /* MM_PCO_H */ ModemManager-1.23.4-dev/libmm-glib/mm-signal-threshold-properties.c000066400000000000000000000247651456466623000251720ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * libmm-glib -- Access modem status & information from glib applications * * 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 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 Street, Fifth Floor, * Boston, MA 02110-1301 USA. * * Copyright (C) 2021 Aleksander Morgado * Copyright (C) 2021 Intel Corporation */ #include #include "mm-errors-types.h" #include "mm-common-helpers.h" #include "mm-signal-threshold-properties.h" /** * SECTION: mm-signal-threshold-properties * @title: MMSignalThresholdProperties * @short_description: Helper object to handle signal threshold properties. * * The #MMSignalThresholdProperties is an object handling the properties requested * when setting up threshold based signal quality information reporting. * * This object is created by the user and passed to ModemManager with either * mm_modem_signal_setup_thresholds() or mm_modem_signal_setup_thresholds_sync(). */ G_DEFINE_TYPE (MMSignalThresholdProperties, mm_signal_threshold_properties, G_TYPE_OBJECT) #define PROPERTY_RSSI_THRESHOLD "rssi-threshold" #define PROPERTY_ERROR_RATE_THRESHOLD "error-rate-threshold" struct _MMSignalThresholdPropertiesPrivate { guint rssi_threshold; gboolean rssi_threshold_set; gboolean error_rate_threshold; gboolean error_rate_threshold_set; }; /*****************************************************************************/ /** * mm_signal_threshold_properties_set_rssi: * @self: a #MMSignalThresholdProperties. * @rssi_threshold: the RSSI threshold, or 0 to disable. * * Sets the RSSI threshold, in dBm. * * Since: 1.20 */ void mm_signal_threshold_properties_set_rssi (MMSignalThresholdProperties *self, guint rssi_threshold) { g_return_if_fail (MM_IS_SIGNAL_THRESHOLD_PROPERTIES (self)); self->priv->rssi_threshold = rssi_threshold; self->priv->rssi_threshold_set = TRUE; } /** * mm_signal_threshold_properties_get_rssi: * @self: a #MMSignalThresholdProperties. * * Gets the RSSI threshold, in dBm. * * Returns: the RSSI threshold, or 0 if disabled. * * Since: 1.20 */ guint mm_signal_threshold_properties_get_rssi (MMSignalThresholdProperties *self) { g_return_val_if_fail (MM_IS_SIGNAL_THRESHOLD_PROPERTIES (self), 0); return self->priv->rssi_threshold; } /*****************************************************************************/ /** * mm_signal_threshold_properties_set_error_rate: * @self: a #MMSignalThresholdProperties. * @error_rate_threshold: %TRUE to enable, %FALSE to disable. * * Enables or disables the error rate threshold. * * Since: 1.20 */ void mm_signal_threshold_properties_set_error_rate (MMSignalThresholdProperties *self, gboolean error_rate_threshold) { g_return_if_fail (MM_IS_SIGNAL_THRESHOLD_PROPERTIES (self)); self->priv->error_rate_threshold = error_rate_threshold; self->priv->error_rate_threshold_set = TRUE; } /** * mm_signal_threshold_properties_get_error_rate: * @self: a #MMSignalThresholdProperties. * * Gets whether the error rate threshold is enabled or disabled. * * Returns: %TRUE if the error rate threshold is enabled, %FALSE otherwise. * * Since: 1.20 */ gboolean mm_signal_threshold_properties_get_error_rate (MMSignalThresholdProperties *self) { g_return_val_if_fail (MM_IS_SIGNAL_THRESHOLD_PROPERTIES (self), FALSE); return self->priv->error_rate_threshold; } /*****************************************************************************/ /** * mm_signal_threshold_properties_get_dictionary: (skip) */ GVariant * mm_signal_threshold_properties_get_dictionary (MMSignalThresholdProperties *self) { GVariantBuilder builder; /* We do allow NULL */ if (!self) return NULL; g_return_val_if_fail (MM_IS_SIGNAL_THRESHOLD_PROPERTIES (self), NULL); g_variant_builder_init (&builder, G_VARIANT_TYPE ("a{sv}")); if (self->priv->rssi_threshold_set) g_variant_builder_add (&builder, "{sv}", PROPERTY_RSSI_THRESHOLD, g_variant_new_uint32 (self->priv->rssi_threshold)); if (self->priv->error_rate_threshold_set) g_variant_builder_add (&builder, "{sv}", PROPERTY_ERROR_RATE_THRESHOLD, g_variant_new_boolean (self->priv->error_rate_threshold)); return g_variant_ref_sink (g_variant_builder_end (&builder)); } /*****************************************************************************/ typedef struct { MMSignalThresholdProperties *properties; GError *error; } ParseKeyValueContext; static gboolean key_value_foreach (const gchar *key, const gchar *value, ParseKeyValueContext *ctx) { if (g_str_equal (key, PROPERTY_RSSI_THRESHOLD)) { guint rssi_threshold; if (!mm_get_uint_from_str (value, &rssi_threshold)) { g_set_error (&ctx->error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS, "invalid RSSI threshold value given: %s", value); return FALSE; } mm_signal_threshold_properties_set_rssi (ctx->properties, rssi_threshold); return TRUE; } if (g_str_equal (key, PROPERTY_ERROR_RATE_THRESHOLD)) { gboolean error_rate_threshold; error_rate_threshold = mm_common_get_boolean_from_string (value, &ctx->error); if (ctx->error) { g_prefix_error (&ctx->error, "invalid error rate threshold value given: "); return FALSE; } mm_signal_threshold_properties_set_error_rate (ctx->properties, error_rate_threshold); return TRUE; } g_set_error (&ctx->error, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "Invalid properties string, unsupported key '%s'", key); return FALSE; } /** * mm_signal_threshold_properties_new_from_string: (skip) */ MMSignalThresholdProperties * mm_signal_threshold_properties_new_from_string (const gchar *str, GError **error) { ParseKeyValueContext ctx; ctx.error = NULL; ctx.properties = mm_signal_threshold_properties_new (); mm_common_parse_key_value_string (str, &ctx.error, (MMParseKeyValueForeachFn)key_value_foreach, &ctx); /* If error, destroy the object */ if (ctx.error) { g_propagate_error (error, ctx.error); g_clear_object (&ctx.properties); } return ctx.properties; } /*****************************************************************************/ static gboolean consume_variant (MMSignalThresholdProperties *self, const gchar *key, GVariant *value, GError **error) { if (g_str_equal (key, PROPERTY_RSSI_THRESHOLD)) mm_signal_threshold_properties_set_rssi (self, g_variant_get_uint32 (value)); else if (g_str_equal (key, PROPERTY_ERROR_RATE_THRESHOLD)) mm_signal_threshold_properties_set_error_rate (self, g_variant_get_boolean (value)); else { /* Set error */ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS, "Invalid properties dictionary, unexpected key '%s'", key); return FALSE; } return TRUE; } /** * mm_signal_threshold_properties_new_from_dictionary: (skip) */ MMSignalThresholdProperties * mm_signal_threshold_properties_new_from_dictionary (GVariant *dictionary, GError **error) { GError *inner_error = NULL; GVariantIter iter; gchar *key; GVariant *value; MMSignalThresholdProperties *properties; properties = mm_signal_threshold_properties_new (); if (!dictionary) return properties; if (!g_variant_is_of_type (dictionary, G_VARIANT_TYPE ("a{sv}"))) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS, "Cannot create signal threshold properties from dictionary: " "invalid variant type received"); g_object_unref (properties); return NULL; } g_variant_iter_init (&iter, dictionary); while (!inner_error && g_variant_iter_next (&iter, "{sv}", &key, &value)) { consume_variant (properties, key, value, &inner_error); g_free (key); g_variant_unref (value); } /* If error, destroy the object */ if (inner_error) { g_propagate_error (error, inner_error); g_object_unref (properties); properties = NULL; } return properties; } /*****************************************************************************/ /** * mm_signal_threshold_properties_new: * * Creates a new empty #MMSignalThresholdProperties. * * Returns: (transfer full): a #MMSignalThresholdProperties. The returned value should be freed with g_object_unref(). * * Since: 1.20 */ MMSignalThresholdProperties * mm_signal_threshold_properties_new (void) { return (MM_SIGNAL_THRESHOLD_PROPERTIES ( g_object_new (MM_TYPE_SIGNAL_THRESHOLD_PROPERTIES, NULL))); } static void mm_signal_threshold_properties_init (MMSignalThresholdProperties *self) { self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, MM_TYPE_SIGNAL_THRESHOLD_PROPERTIES, MMSignalThresholdPropertiesPrivate); } static void mm_signal_threshold_properties_class_init (MMSignalThresholdPropertiesClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); g_type_class_add_private (object_class, sizeof (MMSignalThresholdPropertiesPrivate)); } ModemManager-1.23.4-dev/libmm-glib/mm-signal-threshold-properties.h000066400000000000000000000105671456466623000251720ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * libmm-glib -- Access modem status & information from glib applications * * 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 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 Street, Fifth Floor, * Boston, MA 02110-1301 USA. * * Copyright (C) 2021 Aleksander Morgado * Copyright (C) 2021 Intel Corporation */ #ifndef MM_SIGNAL_THRESHOLD_PROPERTIES_H #define MM_SIGNAL_THRESHOLD_PROPERTIES_H #include #include #if !defined (__LIBMM_GLIB_H_INSIDE__) && !defined (LIBMM_GLIB_COMPILATION) #error "Only can be included directly." #endif G_BEGIN_DECLS #define MM_TYPE_SIGNAL_THRESHOLD_PROPERTIES (mm_signal_threshold_properties_get_type ()) #define MM_SIGNAL_THRESHOLD_PROPERTIES(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_SIGNAL_THRESHOLD_PROPERTIES, MMSignalThresholdProperties)) #define MM_SIGNAL_THRESHOLD_PROPERTIES_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_SIGNAL_THRESHOLD_PROPERTIES, MMSignalThresholdPropertiesClass)) #define MM_IS_SIGNAL_THRESHOLD_PROPERTIES(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_SIGNAL_THRESHOLD_PROPERTIES)) #define MM_IS_SIGNAL_THRESHOLD_PROPERTIES_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_SIGNAL_THRESHOLD_PROPERTIES)) #define MM_SIGNAL_THRESHOLD_PROPERTIES_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_SIGNAL_THRESHOLD_PROPERTIES, MMSignalThresholdPropertiesClass)) typedef struct _MMSignalThresholdProperties MMSignalThresholdProperties; typedef struct _MMSignalThresholdPropertiesClass MMSignalThresholdPropertiesClass; typedef struct _MMSignalThresholdPropertiesPrivate MMSignalThresholdPropertiesPrivate; /** * MMSignalThresholdProperties: * * The #MMSignalThresholdProperties structure contains private data and should * only be accessed using the provided API. */ struct _MMSignalThresholdProperties { /*< private >*/ GObject parent; MMSignalThresholdPropertiesPrivate *priv; }; struct _MMSignalThresholdPropertiesClass { /*< private >*/ GObjectClass parent; }; GType mm_signal_threshold_properties_get_type (void); G_DEFINE_AUTOPTR_CLEANUP_FUNC (MMSignalThresholdProperties, g_object_unref) MMSignalThresholdProperties *mm_signal_threshold_properties_new (void); void mm_signal_threshold_properties_set_rssi (MMSignalThresholdProperties *self, guint rssi_threshold); void mm_signal_threshold_properties_set_error_rate (MMSignalThresholdProperties *self, gboolean error_rate_threshold); guint mm_signal_threshold_properties_get_rssi (MMSignalThresholdProperties *self); gboolean mm_signal_threshold_properties_get_error_rate (MMSignalThresholdProperties *self); /*****************************************************************************/ /* ModemManager/libmm-glib/mmcli specific methods */ #if defined (_LIBMM_INSIDE_MM) || \ defined (_LIBMM_INSIDE_MMCLI) || \ defined (LIBMM_GLIB_COMPILATION) MMSignalThresholdProperties *mm_signal_threshold_properties_new_from_string (const gchar *str, GError **error); MMSignalThresholdProperties *mm_signal_threshold_properties_new_from_dictionary (GVariant *dictionary, GError **error); GVariant *mm_signal_threshold_properties_get_dictionary (MMSignalThresholdProperties *self); #endif G_END_DECLS #endif /* MM_SIGNAL_THRESHOLD_PROPERTIES_H */ ModemManager-1.23.4-dev/libmm-glib/mm-signal.c000066400000000000000000000362531456466623000210010ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * libmm-glib -- Access modem status & information from glib applications * * 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 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 Street, Fifth Floor, * Boston, MA 02110-1301 USA. * * Copyright (C) 2013-2021 Aleksander Morgado * Copyright (C) 2021 Intel Corporation */ #include #include #include #include "mm-signal.h" #include "mm-errors-types.h" /** * SECTION: mm-signal * @title: MMSignal * @short_description: Helper object to handle extended Signal information. * * The #MMSignal is an object handling the signal information of the * modem. */ G_DEFINE_TYPE (MMSignal, mm_signal, G_TYPE_OBJECT) #define PROPERTY_RSSI "rssi" #define PROPERTY_RSCP "rscp" #define PROPERTY_ECIO "ecio" #define PROPERTY_SINR "sinr" #define PROPERTY_IO "io" #define PROPERTY_RSRQ "rsrq" #define PROPERTY_RSRP "rsrp" #define PROPERTY_SNR "snr" #define PROPERTY_ERROR_RATE "error-rate" struct _MMSignalPrivate { gdouble rssi; gdouble rscp; gdouble ecio; gdouble sinr; gdouble io; gdouble rsrq; gdouble rsrp; gdouble snr; gdouble error_rate; }; /*****************************************************************************/ /** * mm_signal_get_rssi: * @self: a #MMSignal. * * Gets the RSSI (Received Signal Strength Indication), in dBm. * * Returns: the RSSI, or %MM_SIGNAL_UNKNOWN if unknown. * * Since: 1.2 */ gdouble mm_signal_get_rssi (MMSignal *self) { g_return_val_if_fail (MM_IS_SIGNAL (self), MM_SIGNAL_UNKNOWN); return self->priv->rssi; } /** * mm_signal_set_rssi: (skip) */ void mm_signal_set_rssi (MMSignal *self, gdouble value) { g_return_if_fail (MM_IS_SIGNAL (self)); self->priv->rssi = value; } /*****************************************************************************/ /** * mm_signal_get_rscp: * @self: a #MMSignal. * * Gets the RSCP (Received Signal Code Power), in dBm. * * Returns: the RSCP, or %MM_SIGNAL_UNKNOWN if unknown. * * Since: 1.2 */ gdouble mm_signal_get_rscp (MMSignal *self) { g_return_val_if_fail (MM_IS_SIGNAL (self), MM_SIGNAL_UNKNOWN); return self->priv->rscp; } /** * mm_signal_set_rscp: (skip) */ void mm_signal_set_rscp (MMSignal *self, gdouble value) { g_return_if_fail (MM_IS_SIGNAL (self)); self->priv->rscp = value; } /*****************************************************************************/ /** * mm_signal_get_ecio: * @self: a #MMSignal. * * Gets the Ec/Io, in dB. * * Only applicable to CDMA1x, CDMA EV-DO and UMTS (WCDMA). * * Returns: the ECIO, or %MM_SIGNAL_UNKNOWN if unknown. * * Since: 1.2 */ gdouble mm_signal_get_ecio (MMSignal *self) { g_return_val_if_fail (MM_IS_SIGNAL (self), MM_SIGNAL_UNKNOWN); return self->priv->ecio; } /** * mm_signal_set_ecio: (skip) */ void mm_signal_set_ecio (MMSignal *self, gdouble value) { g_return_if_fail (MM_IS_SIGNAL (self)); self->priv->ecio = value; } /*****************************************************************************/ /** * mm_signal_get_sinr: * @self: a #MMSignal. * * Gets the SINR level, in dB. * * Only applicable to CDMA EV-DO. * * Returns: the SINR, or %MM_SIGNAL_UNKNOWN if unknown. * * Since: 1.2 */ gdouble mm_signal_get_sinr (MMSignal *self) { g_return_val_if_fail (MM_IS_SIGNAL (self), MM_SIGNAL_UNKNOWN); return self->priv->sinr; } /** * mm_signal_set_sinr: (skip) */ void mm_signal_set_sinr (MMSignal *self, gdouble value) { g_return_if_fail (MM_IS_SIGNAL (self)); self->priv->sinr = value; } /*****************************************************************************/ /** * mm_signal_get_io: * @self: a #MMSignal. * * Gets the Io, in dBm. * * Only applicable to CDMA EV-DO. * * Returns: the Io, or %MM_SIGNAL_UNKNOWN if unknown. * * Since: 1.2 */ gdouble mm_signal_get_io (MMSignal *self) { g_return_val_if_fail (MM_IS_SIGNAL (self), MM_SIGNAL_UNKNOWN); return self->priv->io; } /** * mm_signal_set_io: (skip) */ void mm_signal_set_io (MMSignal *self, gdouble value) { g_return_if_fail (MM_IS_SIGNAL (self)); self->priv->io = value; } /*****************************************************************************/ /** * mm_signal_get_rsrp: * @self: a #MMSignal. * * Gets the RSRP (Reference Signal Received Power), in dBm. * * Only applicable to LTE. * * Returns: the RSRP, or %MM_SIGNAL_UNKNOWN if unknown. * * Since: 1.2 */ gdouble mm_signal_get_rsrp (MMSignal *self) { g_return_val_if_fail (MM_IS_SIGNAL (self), MM_SIGNAL_UNKNOWN); return self->priv->rsrp; } /** * mm_signal_set_rsrp: (skip) */ void mm_signal_set_rsrp (MMSignal *self, gdouble value) { g_return_if_fail (MM_IS_SIGNAL (self)); self->priv->rsrp = value; } /*****************************************************************************/ /** * mm_signal_get_rsrq: * @self: a #MMSignal. * * Gets the RSRQ (Reference Signal Received Quality), in dB. * * Only applicable to LTE. * * Returns: the RSRQ, or %MM_SIGNAL_UNKNOWN if unknown. * * Since: 1.2 */ gdouble mm_signal_get_rsrq (MMSignal *self) { g_return_val_if_fail (MM_IS_SIGNAL (self), MM_SIGNAL_UNKNOWN); return self->priv->rsrq; } /** * mm_signal_set_rsrq: (skip) */ void mm_signal_set_rsrq (MMSignal *self, gdouble value) { g_return_if_fail (MM_IS_SIGNAL (self)); self->priv->rsrq = value; } /*****************************************************************************/ /** * mm_signal_get_snr: * @self: a #MMSignal. * * Gets the S/R ration, in dB. * * Only applicable to LTE. * * Returns: the S/R ratio, or %MM_SIGNAL_UNKNOWN if unknown. * * Since: 1.2 */ gdouble mm_signal_get_snr (MMSignal *self) { g_return_val_if_fail (MM_IS_SIGNAL (self), MM_SIGNAL_UNKNOWN); return self->priv->snr; } /** * mm_signal_set_snr: (skip) */ void mm_signal_set_snr (MMSignal *self, gdouble value) { g_return_if_fail (MM_IS_SIGNAL (self)); self->priv->snr = value; } /*****************************************************************************/ /** * mm_signal_get_error_rate: * @self: a #MMSignal. * * Gets the channel error rate (BER, BLER,... depends on the RAT), in * percentage. * * Applicable to all RAT. * * Returns: the error rate, or %MM_SIGNAL_UNKNOWN if unknown. * * Since: 1.20 */ gdouble mm_signal_get_error_rate (MMSignal *self) { g_return_val_if_fail (MM_IS_SIGNAL (self), MM_SIGNAL_UNKNOWN); return self->priv->error_rate; } /** * mm_signal_set_error_rate: (skip) */ void mm_signal_set_error_rate (MMSignal *self, gdouble value) { g_return_if_fail (MM_IS_SIGNAL (self)); self->priv->error_rate = value; } /** * mm_signal_get_string: (skip) */ gchar * mm_signal_get_string (MMSignal *self) { GString *printable; printable = g_string_new (""); if (self->priv->ecio != MM_SIGNAL_UNKNOWN) g_string_append_printf (printable, "Ec/Io: %3.0e dB", self->priv->ecio); if (self->priv->error_rate != MM_SIGNAL_UNKNOWN) g_string_append_printf (printable, "%serror rate: %f %%", printable->len ? ", " : "", self->priv->error_rate); if (self->priv->rscp != MM_SIGNAL_UNKNOWN) g_string_append_printf (printable, "%sRSCP: %f dBm", printable->len ? ", " : "", self->priv->rscp); if (self->priv->rscp != MM_SIGNAL_UNKNOWN) g_string_append_printf (printable, "%sRSRP: %f dBm", printable->len ? ", " : "", self->priv->rsrp); if (self->priv->rsrq != MM_SIGNAL_UNKNOWN) g_string_append_printf (printable, "%sRSRQ: %f dB", printable->len ? ", " : "", self->priv->rsrq); if (self->priv->rssi != MM_SIGNAL_UNKNOWN) g_string_append_printf (printable, "%sRSSI: %f dBm", printable->len ? ", " : "", self->priv->rssi); if (self->priv->sinr != MM_SIGNAL_UNKNOWN) g_string_append_printf (printable, "%sSINR: %f dB", printable->len ? ", " : "", self->priv->sinr); if (self->priv->snr != MM_SIGNAL_UNKNOWN) g_string_append_printf (printable, "%sSNR: %f dB", printable->len ? ", " : "", self->priv->snr); return g_string_free (printable, FALSE); } /*****************************************************************************/ /** * mm_signal_get_dictionary: (skip) */ GVariant * mm_signal_get_dictionary (MMSignal *self) { GVariantBuilder builder; /* We do allow NULL */ if (!self) return NULL; g_return_val_if_fail (MM_IS_SIGNAL (self), NULL); g_variant_builder_init (&builder, G_VARIANT_TYPE ("a{sv}")); if (self->priv->rssi != MM_SIGNAL_UNKNOWN) g_variant_builder_add (&builder, "{sv}", PROPERTY_RSSI, g_variant_new_double (self->priv->rssi)); if (self->priv->rscp != MM_SIGNAL_UNKNOWN) g_variant_builder_add (&builder, "{sv}", PROPERTY_RSCP, g_variant_new_double (self->priv->rscp)); if (self->priv->ecio != MM_SIGNAL_UNKNOWN) g_variant_builder_add (&builder, "{sv}", PROPERTY_ECIO, g_variant_new_double (self->priv->ecio)); if (self->priv->sinr != MM_SIGNAL_UNKNOWN) g_variant_builder_add (&builder, "{sv}", PROPERTY_SINR, g_variant_new_double (self->priv->sinr)); if (self->priv->io != MM_SIGNAL_UNKNOWN) g_variant_builder_add (&builder, "{sv}", PROPERTY_IO, g_variant_new_double (self->priv->io)); if (self->priv->rsrp != MM_SIGNAL_UNKNOWN) g_variant_builder_add (&builder, "{sv}", PROPERTY_RSRP, g_variant_new_double (self->priv->rsrp)); if (self->priv->rsrq != MM_SIGNAL_UNKNOWN) g_variant_builder_add (&builder, "{sv}", PROPERTY_RSRQ, g_variant_new_double (self->priv->rsrq)); if (self->priv->snr != MM_SIGNAL_UNKNOWN) g_variant_builder_add (&builder, "{sv}", PROPERTY_SNR, g_variant_new_double (self->priv->snr)); if (self->priv->error_rate != MM_SIGNAL_UNKNOWN) g_variant_builder_add (&builder, "{sv}", PROPERTY_ERROR_RATE, g_variant_new_double (self->priv->error_rate)); return g_variant_ref_sink (g_variant_builder_end (&builder)); } /*****************************************************************************/ static gboolean consume_variant (MMSignal *self, const gchar *key, GVariant *value, GError **error) { if (g_str_equal (key, PROPERTY_RSSI)) self->priv->rssi = g_variant_get_double (value); else if (g_str_equal (key, PROPERTY_RSCP)) self->priv->rscp = g_variant_get_double (value); else if (g_str_equal (key, PROPERTY_ECIO)) self->priv->ecio = g_variant_get_double (value); else if (g_str_equal (key, PROPERTY_SINR)) self->priv->sinr = g_variant_get_double (value); else if (g_str_equal (key, PROPERTY_IO)) self->priv->io = g_variant_get_double (value); else if (g_str_equal (key, PROPERTY_RSRP)) self->priv->rsrp = g_variant_get_double (value); else if (g_str_equal (key, PROPERTY_RSRQ)) self->priv->rsrq = g_variant_get_double (value); else if (g_str_equal (key, PROPERTY_SNR)) self->priv->snr = g_variant_get_double (value); else if (g_str_equal (key, PROPERTY_ERROR_RATE)) self->priv->error_rate = g_variant_get_double (value); else { /* Set error */ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS, "Invalid signal dictionary, unexpected key '%s'", key); return FALSE; } return TRUE; } /** * mm_signal_new_from_dictionary: (skip) */ MMSignal * mm_signal_new_from_dictionary (GVariant *dictionary, GError **error) { GError *inner_error = NULL; GVariantIter iter; gchar *key; GVariant *value; MMSignal *self = NULL; if (!dictionary) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS, "Cannot create Signal info from empty dictionary"); return NULL; } if (!g_variant_is_of_type (dictionary, G_VARIANT_TYPE ("a{sv}"))) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS, "Cannot create Signal info from dictionary: " "invalid variant type received"); return NULL; } g_variant_iter_init (&iter, dictionary); while (!inner_error && g_variant_iter_next (&iter, "{sv}", &key, &value)) { if (!self) self = mm_signal_new (); consume_variant (self, key, value, &inner_error); g_free (key); g_variant_unref (value); } /* If error, destroy the object */ if (inner_error) { g_propagate_error (error, inner_error); g_clear_object (&self); } return self; } /*****************************************************************************/ /** * mm_signal_new: (skip) */ MMSignal * mm_signal_new (void) { return MM_SIGNAL (g_object_new (MM_TYPE_SIGNAL, NULL)); } static void mm_signal_init (MMSignal *self) { self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, MM_TYPE_SIGNAL, MMSignalPrivate); self->priv->rssi = MM_SIGNAL_UNKNOWN; self->priv->rscp = MM_SIGNAL_UNKNOWN; self->priv->ecio = MM_SIGNAL_UNKNOWN; self->priv->sinr = MM_SIGNAL_UNKNOWN; self->priv->io = MM_SIGNAL_UNKNOWN; self->priv->rsrq = MM_SIGNAL_UNKNOWN; self->priv->rsrp = MM_SIGNAL_UNKNOWN; self->priv->snr = MM_SIGNAL_UNKNOWN; self->priv->error_rate = MM_SIGNAL_UNKNOWN; } static void mm_signal_class_init (MMSignalClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); g_type_class_add_private (object_class, sizeof (MMSignalPrivate)); } ModemManager-1.23.4-dev/libmm-glib/mm-signal.h000066400000000000000000000077201456466623000210030ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * libmm-glib -- Access modem status & information from glib applications * * 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 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 Street, Fifth Floor, * Boston, MA 02110-1301 USA. * * Copyright (C) 2013-2021 Aleksander Morgado * Copyright (C) 2021 Intel Corporation */ #ifndef MM_SIGNAL_H #define MM_SIGNAL_H #if !defined (__LIBMM_GLIB_H_INSIDE__) && !defined (LIBMM_GLIB_COMPILATION) #error "Only can be included directly." #endif #include #include G_BEGIN_DECLS /** * MM_SIGNAL_UNKNOWN: * * Identifier for an unknown signal or error rate value. * * Since: 1.2 */ #define MM_SIGNAL_UNKNOWN -G_MAXDOUBLE #define MM_TYPE_SIGNAL (mm_signal_get_type ()) #define MM_SIGNAL(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_SIGNAL, MMSignal)) #define MM_SIGNAL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_SIGNAL, MMSignalClass)) #define MM_IS_SIGNAL(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_SIGNAL)) #define MM_IS_SIGNAL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_SIGNAL)) #define MM_SIGNAL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_SIGNAL, MMSignalClass)) typedef struct _MMSignal MMSignal; typedef struct _MMSignalClass MMSignalClass; typedef struct _MMSignalPrivate MMSignalPrivate; /** * MMSignal: * * The #MMSignal structure contains private data and should * only be accessed using the provided API. */ struct _MMSignal { /*< private >*/ GObject parent; MMSignalPrivate *priv; }; struct _MMSignalClass { /*< private >*/ GObjectClass parent; }; GType mm_signal_get_type (void); G_DEFINE_AUTOPTR_CLEANUP_FUNC (MMSignal, g_object_unref) gdouble mm_signal_get_rssi (MMSignal *self); gdouble mm_signal_get_rscp (MMSignal *self); gdouble mm_signal_get_ecio (MMSignal *self); gdouble mm_signal_get_sinr (MMSignal *self); gdouble mm_signal_get_io (MMSignal *self); gdouble mm_signal_get_rsrq (MMSignal *self); gdouble mm_signal_get_rsrp (MMSignal *self); gdouble mm_signal_get_snr (MMSignal *self); gdouble mm_signal_get_error_rate (MMSignal *self); /*****************************************************************************/ /* ModemManager/libmm-glib/mmcli specific methods */ #if defined (_LIBMM_INSIDE_MM) || \ defined (_LIBMM_INSIDE_MMCLI) || \ defined (LIBMM_GLIB_COMPILATION) GVariant *mm_signal_get_dictionary (MMSignal *self); MMSignal *mm_signal_new (void); MMSignal *mm_signal_new_from_dictionary (GVariant *dictionary, GError **error); void mm_signal_set_rssi (MMSignal *self, gdouble value); void mm_signal_set_rscp (MMSignal *self, gdouble value); void mm_signal_set_ecio (MMSignal *self, gdouble value); void mm_signal_set_sinr (MMSignal *self, gdouble value); void mm_signal_set_io (MMSignal *self, gdouble value); void mm_signal_set_rsrq (MMSignal *self, gdouble value); void mm_signal_set_rsrp (MMSignal *self, gdouble value); void mm_signal_set_snr (MMSignal *self, gdouble value); void mm_signal_set_error_rate (MMSignal *self, gdouble value); gchar *mm_signal_get_string (MMSignal *self); #endif G_END_DECLS #endif /* MM_SIGNAL_H */ ModemManager-1.23.4-dev/libmm-glib/mm-sim-preferred-network.c000066400000000000000000000151261456466623000237530ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * libmm-glib -- Access modem status & information from glib applications * * 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 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 Street, Fifth Floor, * Boston, MA 02110-1301 USA. * * Copyright (C) 2021 UROS Ltd */ #include "mm-sim-preferred-network.h" struct _MMSimPreferredNetwork { gchar *operator_code; MMModemAccessTechnology access_technology; }; static MMSimPreferredNetwork * mm_sim_preferred_network_copy (MMSimPreferredNetwork *preferred_network) { MMSimPreferredNetwork *preferred_network_copy; preferred_network_copy = g_slice_new0 (MMSimPreferredNetwork); preferred_network_copy->operator_code = g_strdup (preferred_network->operator_code); preferred_network_copy->access_technology = preferred_network->access_technology; return preferred_network_copy; } G_DEFINE_BOXED_TYPE (MMSimPreferredNetwork, mm_sim_preferred_network, (GBoxedCopyFunc) mm_sim_preferred_network_copy, (GBoxedFreeFunc) mm_sim_preferred_network_free) /** * mm_sim_preferred_network_free: * @self: A #MMSimPreferredNetwork. * * Frees a #MMSimPreferredNetwork. * * Since: 1.18 */ void mm_sim_preferred_network_free (MMSimPreferredNetwork *self) { if (!self) return; g_free (self->operator_code); g_slice_free (MMSimPreferredNetwork, self); } /** * mm_sim_preferred_network_get_operator_code: * @self: A #MMSimPreferredNetwork. * * Get the operator code (MCCMNC) of the preferred network. * * Returns: (transfer none): The operator code, or %NULL if none available. * * Since: 1.18 */ const gchar * mm_sim_preferred_network_get_operator_code (const MMSimPreferredNetwork *self) { g_return_val_if_fail (self != NULL, NULL); return self->operator_code; } /** * mm_sim_preferred_network_get_access_technology: * @self: A #MMSimPreferredNetwork. * * Get the access technology mask of the preferred network. * * Returns: A #MMModemAccessTechnology. * * Since: 1.18 */ MMModemAccessTechnology mm_sim_preferred_network_get_access_technology (const MMSimPreferredNetwork *self) { g_return_val_if_fail (self != NULL, MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN); return self->access_technology; } /** * mm_sim_preferred_network_set_operator_code: * @self: A #MMSimPreferredNetwork. * @operator_code: Operator code * * Set the operator code (MCCMNC) of this preferred network. * * Since: 1.18 */ void mm_sim_preferred_network_set_operator_code (MMSimPreferredNetwork *self, const gchar *operator_code) { g_return_if_fail (self != NULL); g_free (self->operator_code); self->operator_code = g_strdup (operator_code); } /** * mm_sim_preferred_network_set_access_technology: * @self: A #MMSimPreferredNetwork. * @access_technology: A #MMModemAccessTechnology mask. * * Set the desired access technologies of this preferred network entry. * * Since: 1.18 */ void mm_sim_preferred_network_set_access_technology (MMSimPreferredNetwork *self, MMModemAccessTechnology access_technology) { g_return_if_fail (self != NULL); self->access_technology = access_technology; } /** * mm_sim_preferred_network_new: * * Creates a new empty #MMSimPreferredNetwork. * * Returns: (transfer full): a #MMSimPreferredNetwork. The returned value should be freed * with mm_sim_preferred_network_free(). * * Since: 1.18 */ MMSimPreferredNetwork * mm_sim_preferred_network_new (void) { return g_slice_new0 (MMSimPreferredNetwork); } /** * mm_sim_preferred_network_new_from_variant: (skip) */ MMSimPreferredNetwork * mm_sim_preferred_network_new_from_variant (GVariant *variant) { MMSimPreferredNetwork *preferred_net; g_return_val_if_fail (g_variant_is_of_type (variant, G_VARIANT_TYPE ("(su)")), NULL); preferred_net = mm_sim_preferred_network_new (); g_variant_get (variant, "(su)", &preferred_net->operator_code, &preferred_net->access_technology); return preferred_net; } /** * mm_sim_preferred_network_get_tuple: (skip) */ GVariant * mm_sim_preferred_network_get_tuple (const MMSimPreferredNetwork *self) { return g_variant_new ("(su)", self->operator_code, self->access_technology); } /** * mm_sim_preferred_network_list_get_variant: (skip) */ GVariant * mm_sim_preferred_network_list_get_variant (const GList *preferred_network_list) { GVariantBuilder builder; const GList *iter; g_variant_builder_init (&builder, G_VARIANT_TYPE ("a(su)")); for (iter = preferred_network_list; iter; iter = g_list_next (iter)) { g_variant_builder_add_value (&builder, mm_sim_preferred_network_get_tuple ((const MMSimPreferredNetwork *) iter->data)); } return g_variant_builder_end (&builder); } /** * mm_sim_preferred_network_list_new_from_variant: (skip) */ GList * mm_sim_preferred_network_list_new_from_variant (GVariant *variant) { GList *network_list = NULL; GVariant *child; GVariantIter iter; g_return_val_if_fail (g_variant_is_of_type (variant, G_VARIANT_TYPE ("a(su)")), NULL); g_variant_iter_init (&iter, variant); while ((child = g_variant_iter_next_value (&iter))) { MMSimPreferredNetwork *preferred_net; preferred_net = mm_sim_preferred_network_new_from_variant (child); if (preferred_net) network_list = g_list_append (network_list, preferred_net); g_variant_unref (child); } return network_list; } /** * mm_sim_preferred_network_list_copy: (skip) */ GList * mm_sim_preferred_network_list_copy (GList *preferred_network_list) { return g_list_copy_deep (preferred_network_list, (GCopyFunc) mm_sim_preferred_network_copy, NULL); } /** * mm_sim_preferred_network_list_free: (skip) */ void mm_sim_preferred_network_list_free (GList *preferred_network_list) { g_list_free_full (preferred_network_list, (GDestroyNotify) mm_sim_preferred_network_free); } ModemManager-1.23.4-dev/libmm-glib/mm-sim-preferred-network.h000066400000000000000000000064731456466623000237650ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * libmm-glib -- Access modem status & information from glib applications * * 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 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 Street, Fifth Floor, * Boston, MA 02110-1301 USA. * * Copyright (C) 2021 UROS Ltd */ #ifndef MM_SIM_PREFERRED_NETWORK_H #define MM_SIM_PREFERRED_NETWORK_H #if !defined (__LIBMM_GLIB_H_INSIDE__) && !defined (LIBMM_GLIB_COMPILATION) #error "Only can be included directly." #endif #include #include G_BEGIN_DECLS /** * MMSimPreferredNetwork: * * The #MMSimPreferredNetwork structure contains private data and should only be accessed * using the provided API. */ typedef struct _MMSimPreferredNetwork MMSimPreferredNetwork; #define MM_TYPE_SIM_PREFERRED_NETWORK (mm_sim_preferred_network_get_type ()) GType mm_sim_preferred_network_get_type (void); MMSimPreferredNetwork * mm_sim_preferred_network_new (void); const gchar *mm_sim_preferred_network_get_operator_code (const MMSimPreferredNetwork *self); MMModemAccessTechnology mm_sim_preferred_network_get_access_technology (const MMSimPreferredNetwork *self); void mm_sim_preferred_network_set_operator_code (MMSimPreferredNetwork *self, const gchar *operator_code); void mm_sim_preferred_network_set_access_technology (MMSimPreferredNetwork *self, MMModemAccessTechnology access_technology); void mm_sim_preferred_network_free (MMSimPreferredNetwork *self); G_DEFINE_AUTOPTR_CLEANUP_FUNC (MMSimPreferredNetwork, mm_sim_preferred_network_free) /*****************************************************************************/ /* ModemManager/libmm-glib/mmcli specific methods */ #if defined (_LIBMM_INSIDE_MM) || \ defined (_LIBMM_INSIDE_MMCLI) || \ defined (LIBMM_GLIB_COMPILATION) MMSimPreferredNetwork * mm_sim_preferred_network_new_from_variant (GVariant *variant); GVariant *mm_sim_preferred_network_get_tuple (const MMSimPreferredNetwork *self); GVariant *mm_sim_preferred_network_list_get_variant (const GList *preferred_network_list); GList *mm_sim_preferred_network_list_new_from_variant (GVariant *variant); GList *mm_sim_preferred_network_list_copy (GList *preferred_network_list); void mm_sim_preferred_network_list_free (GList *preferred_network_list); #endif G_END_DECLS #endif /* MM_SIM_PREFERRED_NETWORK_H */ ModemManager-1.23.4-dev/libmm-glib/mm-sim.c000066400000000000000000001033741456466623000203130ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * libmm-glib -- Access modem status & information from glib applications * * 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 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 Street, Fifth Floor, * Boston, MA 02110-1301 USA. * * Copyright (C) 2011 - 2012 Aleksander Morgado * Copyright (C) 2012 Google, Inc. */ #include "mm-helpers.h" #include "mm-sim.h" #include "mm-sim-preferred-network.h" /** * SECTION: mm-sim * @title: MMSim * @short_description: The SIM interface * * The #MMSim is an object providing access to the methods, signals and * properties of the SIM interface. * * When the SIM is exposed and available in the bus, it is ensured that at * least this interface is also available. */ G_DEFINE_TYPE (MMSim, mm_sim, MM_GDBUS_TYPE_SIM_PROXY) /*****************************************************************************/ /** * mm_sim_get_path: * @self: A #MMSim. * * Gets the DBus path of the #MMSim object. * * Returns: (transfer none): The DBus path of the #MMSim object. * * Since: 1.0 */ const gchar * mm_sim_get_path (MMSim *self) { g_return_val_if_fail (MM_IS_SIM (self), NULL); RETURN_NON_EMPTY_CONSTANT_STRING ( g_dbus_proxy_get_object_path (G_DBUS_PROXY (self))); } /** * mm_sim_dup_path: * @self: A #MMSim. * * Gets a copy of the DBus path of the #MMSim object. * * Returns: (transfer full): The DBus path of the #MMSim object. The returned * value should be freed with g_free(). * * Since: 1.0 */ gchar * mm_sim_dup_path (MMSim *self) { gchar *value; g_return_val_if_fail (MM_IS_SIM (self), NULL); g_object_get (G_OBJECT (self), "g-object-path", &value, NULL); RETURN_NON_EMPTY_STRING (value); } /*****************************************************************************/ /** * mm_sim_get_active: * @self: A #MMSim. * * Checks whether the #MMSim is currently active. * * Returns: %TRUE if the SIM is active, %FALSE otherwise. * * Since: 1.16 */ gboolean mm_sim_get_active (MMSim *self) { g_return_val_if_fail (MM_IS_SIM (self), FALSE); return mm_gdbus_sim_get_active (MM_GDBUS_SIM (self)); } /*****************************************************************************/ /** * mm_sim_get_identifier: * @self: A #MMSim. * * Gets the unique SIM identifier of the #MMSim object. * * The returned value is only valid until the property changes so it is * only safe to use this function on the thread where @self was constructed. Use * mm_sim_dup_identifier() if on another thread. * * Returns: (transfer none): The unique identifier of the #MMSim object, or * %NULL if it couldn't be retrieved. * * Since: 1.0 */ const gchar * mm_sim_get_identifier (MMSim *self) { g_return_val_if_fail (MM_IS_SIM (self), NULL); RETURN_NON_EMPTY_CONSTANT_STRING ( mm_gdbus_sim_get_sim_identifier (MM_GDBUS_SIM (self))); } /** * mm_sim_dup_identifier: * @self: A #MMSim. * * Gets a copy of the unique SIM identifier of the #MMSim object. * * Returns: (transfer full): The unique identifier of the #MMSim object, or * %NULL if it couldn't be retrieved. The returned value should be freed with * g_free(). * * Since: 1.0 */ gchar * mm_sim_dup_identifier (MMSim *self) { g_return_val_if_fail (MM_IS_SIM (self), NULL); RETURN_NON_EMPTY_STRING ( mm_gdbus_sim_dup_sim_identifier (MM_GDBUS_SIM (self))); } /*****************************************************************************/ /** * mm_sim_get_imsi: * @self: A #MMSim. * * Gets the International Mobile Subscriber Identity (IMSI) of the #MMSim * object. * * The returned value is only valid until the property changes so it is * only safe to use this function on the thread where @self was constructed. Use * mm_sim_dup_imsi() if on another thread. * * Returns: (transfer none): The IMSI of the #MMSim object, or %NULL if it * couldn't be retrieved. * * Since: 1.0 */ const gchar * mm_sim_get_imsi (MMSim *self) { g_return_val_if_fail (MM_IS_SIM (self), NULL); RETURN_NON_EMPTY_CONSTANT_STRING ( mm_gdbus_sim_get_imsi (MM_GDBUS_SIM (self))); } /** * mm_sim_dup_imsi: * @self: A #MMSim. * * Gets a copy of the International Mobile Subscriber Identity (IMSI) of the * #MMSim object. * * Returns: (transfer full): The IMSI of the #MMSim object, or %NULL if it * couldn't be retrieved. The returned value should be freed with g_free(). * * Since: 1.0 */ gchar * mm_sim_dup_imsi (MMSim *self) { g_return_val_if_fail (MM_IS_SIM (self), NULL); RETURN_NON_EMPTY_STRING ( mm_gdbus_sim_dup_imsi (MM_GDBUS_SIM (self))); } /*****************************************************************************/ /** * mm_sim_get_eid: * @self: A #MMSim. * * Gets the Embedded UICC ID (or EID) of the #MMSim object. * * The returned value is only valid until the property changes so it is * only safe to use this function on the thread where @self was constructed. Use * mm_sim_dup_eid() if on another thread. * * Returns: (transfer none): The EID of the #MMSim object, or %NULL if it * couldn't be retrieved. * * Since: 1.16 */ const gchar * mm_sim_get_eid (MMSim *self) { g_return_val_if_fail (MM_IS_SIM (self), NULL); RETURN_NON_EMPTY_CONSTANT_STRING ( mm_gdbus_sim_get_eid (MM_GDBUS_SIM (self))); } /** * mm_sim_dup_eid: * @self: A #MMSim. * * Gets a copy of the Embedded UICC ID (EID) of the #MMSim object. * * Returns: (transfer full): The EID of the #MMSim object, or %NULL if it * couldn't be retrieved. The returned value should be freed with g_free(). * * Since: 1.16 */ gchar * mm_sim_dup_eid (MMSim *self) { g_return_val_if_fail (MM_IS_SIM (self), NULL); RETURN_NON_EMPTY_STRING ( mm_gdbus_sim_dup_eid (MM_GDBUS_SIM (self))); } /*****************************************************************************/ /** * mm_sim_get_operator_identifier: * @self: A #MMSim. * * Gets the Operator Identifier of the #MMSim object. * * The returned value is only valid until the property changes so it is * only safe to use this function on the thread where @self was constructed. Use * mm_sim_dup_operator_identifier() if on another thread. * * Returns: (transfer none): The Operator Identifier of the #MMSim object, or * %NULL if it couldn't be retrieved. * * Since: 1.0 */ const gchar * mm_sim_get_operator_identifier (MMSim *self) { g_return_val_if_fail (MM_IS_SIM (self), NULL); RETURN_NON_EMPTY_CONSTANT_STRING ( mm_gdbus_sim_get_operator_identifier (MM_GDBUS_SIM (self))); } /** * mm_sim_dup_operator_identifier: * @self: A #MMSim. * * Gets a copy of the Operator Identifier of the #MMSim object. * * Returns: (transfer full): The Operator Identifier of the #MMSim object, or * %NULL if it couldn't be retrieved. The returned value should be freed with * g_free(). * * Since: 1.0 */ gchar * mm_sim_dup_operator_identifier (MMSim *self) { g_return_val_if_fail (MM_IS_SIM (self), NULL); RETURN_NON_EMPTY_STRING ( mm_gdbus_sim_dup_operator_identifier (MM_GDBUS_SIM (self))); } /*****************************************************************************/ /** * mm_sim_get_operator_name: * @self: A #MMSim. * * Gets the Operator Name of the #MMSim object. * * The returned value is only valid until the property changes so it is * only safe to use this function on the thread where @self was constructed. Use * mm_sim_dup_operator_name() if on another thread. * * Returns: (transfer none): The Operator Name of the #MMSim object, or %NULL if * it couldn't be retrieved. * * Since: 1.0 */ const gchar * mm_sim_get_operator_name (MMSim *self) { g_return_val_if_fail (MM_IS_SIM (self), NULL); RETURN_NON_EMPTY_CONSTANT_STRING ( mm_gdbus_sim_get_operator_name (MM_GDBUS_SIM (self))); } /** * mm_sim_dup_operator_name: * @self: A #MMSim. * * Gets a copy of the Operator Name of the #MMSim object. * * Returns: (transfer full): The Operator Name of the #MMSim object, or %NULL if * it couldn't be retrieved. The returned value should be freed with g_free(). * * Since: 1.0 */ gchar * mm_sim_dup_operator_name (MMSim *self) { g_return_val_if_fail (MM_IS_SIM (self), NULL); RETURN_NON_EMPTY_STRING ( mm_gdbus_sim_dup_operator_name (MM_GDBUS_SIM (self))); } /*****************************************************************************/ /** * mm_sim_get_emergency_numbers: * @self: A #MMSim. * * Gets the list of emergency call numbers programmed in the SIM card. * * The returned value is only valid until the property changes so it is * only safe to use this function on the thread where @self was constructed. Use * mm_sim_dup_emergency_numbers() if on another thread. * * Returns: (transfer none): The emergency numbers, or %NULL if none available. * Do not free the returned value, it belongs to @self. * * Since: 1.12 */ const gchar * const * mm_sim_get_emergency_numbers (MMSim *self) { g_return_val_if_fail (MM_IS_SIM (self), NULL); return mm_gdbus_sim_get_emergency_numbers (MM_GDBUS_SIM (self)); } /** * mm_sim_dup_emergency_numbers: * @self: A #MMSim. * * Gets a copy of the list of emergency call numbers programmed in the SIM card. * * Returns: (transfer full): The emergency numbers, or %NULL if none available. * The returned value should be freed with g_strfreev(). * * Since: 1.12 */ gchar ** mm_sim_dup_emergency_numbers (MMSim *self) { g_return_val_if_fail (MM_IS_SIM (self), NULL); return mm_gdbus_sim_dup_emergency_numbers (MM_GDBUS_SIM (self)); } /*****************************************************************************/ /** * mm_sim_get_preferred_networks: * @self: A #MMSim. * * Gets the list of #MMSimPreferredNetwork objects exposed by this * #MMSim. * * Returns: (transfer full) (element-type ModemManager.SimPreferredNetwork): a list of * #MMSimPreferredNetwork objects, or #NULL. The returned value should * be freed with g_list_free_full() using mm_sim_preferred_network_free() as #GDestroyNotify * function. * * Since: 1.18 */ GList * mm_sim_get_preferred_networks (MMSim *self) { GList *network_list = NULL; GVariant *container; g_return_val_if_fail (MM_IS_SIM (self), NULL); container = mm_gdbus_sim_get_preferred_networks (MM_GDBUS_SIM (self)); network_list = mm_sim_preferred_network_list_new_from_variant (container); return network_list; } /*****************************************************************************/ /** * mm_sim_get_gid1: * @self: A #MMSim. * @data_len: (out): Size of the output data, if any given. * * Gets the Group Identifier Level 1 of the #MMSim object. * * Returns: (transfer none) (array length=data_len) (element-type guint8): The * GID1 data, or %NULL if unknown. * * Since: 1.20 */ const guint8 * mm_sim_get_gid1 (MMSim *self, gsize *data_len) { GVariant *value; g_return_val_if_fail (MM_IS_SIM (self), NULL); g_return_val_if_fail (data_len != NULL, NULL); value = mm_gdbus_sim_get_gid1 (MM_GDBUS_SIM (self)); return (value ? g_variant_get_fixed_array (value, data_len, sizeof (guint8)) : NULL); } /** * mm_sim_dup_gid1: * @self: A #MMSim. * @data_len: (out): Size of the output data, if any given. * * Gets the Group Identifier Level 1 of the #MMSim object. * * Returns: (transfer full) (array length=data_len) (element-type guint8): The * GID1 data, or %NULL if unknown. * * Since: 1.20 */ guint8 * mm_sim_dup_gid1 (MMSim *self, gsize *data_len) { g_autoptr(GVariant) value = NULL; g_return_val_if_fail (MM_IS_SIM (self), NULL); g_return_val_if_fail (data_len != NULL, NULL); value = mm_gdbus_sim_dup_gid1 (MM_GDBUS_SIM (self)); return (value ? g_memdup (g_variant_get_fixed_array (value, data_len, sizeof (guint8)), *data_len) : NULL); } /*****************************************************************************/ /** * mm_sim_get_gid2: * @self: A #MMSim. * @data_len: (out): Size of the output data, if any given. * * Gets the Group Identifier Level 2 of the #MMSim object. * * Returns: (transfer none) (array length=data_len) (element-type guint8): The * GID2 data, or %NULL if unknown. * * Since: 1.20 */ const guint8 * mm_sim_get_gid2 (MMSim *self, gsize *data_len) { GVariant *value; g_return_val_if_fail (MM_IS_SIM (self), NULL); g_return_val_if_fail (data_len != NULL, NULL); value = mm_gdbus_sim_get_gid2 (MM_GDBUS_SIM (self)); return (value ? g_variant_get_fixed_array (value, data_len, sizeof (guint8)) : NULL); } /** * mm_sim_dup_gid2: * @self: A #MMSim. * @data_len: (out): Size of the output data, if any given. * * Gets the Group Identifier Level 2 of the #MMSim object. * * Returns: (transfer full) (array length=data_len) (element-type guint8): The * GID2 data, or %NULL if unknown. * * Since: 1.20 */ guint8 * mm_sim_dup_gid2 (MMSim *self, gsize *data_len) { g_autoptr(GVariant) value = NULL; g_return_val_if_fail (MM_IS_SIM (self), NULL); g_return_val_if_fail (data_len != NULL, NULL); value = mm_gdbus_sim_dup_gid2 (MM_GDBUS_SIM (self)); return (value ? g_memdup (g_variant_get_fixed_array (value, data_len, sizeof (guint8)), *data_len) : NULL); } /*****************************************************************************/ /** * mm_sim_get_sim_type: * @self: A #MMSim. * * Gets the SIM type. * * Returns: a #MMSimType. * * Since: 1.20 */ MMSimType mm_sim_get_sim_type (MMSim *self) { g_return_val_if_fail (MM_IS_SIM (self), MM_SIM_TYPE_UNKNOWN); return mm_gdbus_sim_get_sim_type (MM_GDBUS_SIM (self)); } /*****************************************************************************/ /** * mm_sim_get_esim_status: * @self: A #MMSim. * * Gets the eSIM status. * * Only applicable if the SIM type is %MM_SIM_TYPE_ESIM. * * Returns: a #MMSimEsimStatus. * * Since: 1.20 */ MMSimEsimStatus mm_sim_get_esim_status (MMSim *self) { g_return_val_if_fail (MM_IS_SIM (self), MM_SIM_ESIM_STATUS_UNKNOWN); return mm_gdbus_sim_get_esim_status (MM_GDBUS_SIM (self)); } /*****************************************************************************/ /** * mm_sim_get_removability: * @self: A #MMSim. * * Gets whether the SIM is removable or not. * * Returns: a #MMSimRemovability. * * Since: 1.20 */ MMSimRemovability mm_sim_get_removability (MMSim *self) { g_return_val_if_fail (MM_IS_SIM (self), MM_SIM_REMOVABILITY_UNKNOWN); return mm_gdbus_sim_get_removability (MM_GDBUS_SIM (self)); } /*****************************************************************************/ /** * mm_sim_send_pin_finish: * @self: A #MMSim. * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to * mm_sim_send_pin(). * @error: Return location for error or %NULL. * * Finishes an operation started with mm_sim_send_pin(). * * Returns: %TRUE if the operation succeeded, %FALSE if @error is set. * * Since: 1.0 */ gboolean mm_sim_send_pin_finish (MMSim *self, GAsyncResult *res, GError **error) { g_return_val_if_fail (MM_IS_SIM (self), FALSE); return mm_gdbus_sim_call_send_pin_finish (MM_GDBUS_SIM (self), res, error); } /** * mm_sim_send_pin: * @self: A #MMSim. * @pin: The PIN code. * @cancellable: (allow-none): A #GCancellable or %NULL. * @callback: A #GAsyncReadyCallback to call when the request is satisfied or * %NULL. * @user_data: User data to pass to @callback. * * Asynchronously sends the PIN code to the SIM card. * * When the operation is finished, @callback will be invoked in the * thread-default main loop * of the thread you are calling this method from. You can then call * mm_sim_send_pin_finish() to get the result of the operation. * * See mm_sim_send_pin_sync() for the synchronous, blocking version of this * method. * * Since: 1.0 */ void mm_sim_send_pin (MMSim *self, const gchar *pin, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { g_return_if_fail (MM_IS_SIM (self)); mm_gdbus_sim_call_send_pin (MM_GDBUS_SIM (self), pin, cancellable, callback, user_data); } /** * mm_sim_send_pin_sync: * @self: A #MMSim. * @pin: The PIN code. * @cancellable: (allow-none): A #GCancellable or %NULL. * @error: Return location for error or %NULL. * * Synchronously sends the PIN to the SIM card. * * The calling thread is blocked until a reply is received. See * mm_sim_send_pin() for the asynchronous version of this method. * * Returns: %TRUE if the operation succeeded, %FALSE if @error is set. * * Since: 1.0 */ gboolean mm_sim_send_pin_sync (MMSim *self, const gchar *pin, GCancellable *cancellable, GError **error) { g_return_val_if_fail (MM_IS_SIM (self), FALSE); return (mm_gdbus_sim_call_send_pin_sync (MM_GDBUS_SIM (self), pin, cancellable, error)); } /*****************************************************************************/ /** * mm_sim_send_puk_finish: * @self: A #MMSim. * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to * mm_sim_send_puk(). * @error: Return location for error or %NULL. * * Finishes an operation started with mm_sim_send_puk(). * * Returns: %TRUE if the operation succeeded, %FALSE if @error is set. * * Since: 1.0 */ gboolean mm_sim_send_puk_finish (MMSim *self, GAsyncResult *res, GError **error) { g_return_val_if_fail (MM_IS_SIM (self), FALSE); return mm_gdbus_sim_call_send_puk_finish (MM_GDBUS_SIM (self), res, error); } /** * mm_sim_send_puk: * @self: A #MMSim. * @puk: The PUK code. * @pin: The PIN code. * @cancellable: (allow-none): A #GCancellable or %NULL. * @callback: A #GAsyncReadyCallback to call when the request is satisfied or * %NULL. * @user_data: User data to pass to @callback. * * Asynchronously sends the PUK code to the SIM card. * * When the operation is finished, @callback will be invoked in the * thread-default main loop * of the thread you are calling this method from. You can then call * mm_sim_send_puk_finish() to get the result of the operation. * * See mm_sim_send_puk_sync() for the synchronous, blocking version of this * method. * * Since: 1.0 */ void mm_sim_send_puk (MMSim *self, const gchar *puk, const gchar *pin, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { g_return_if_fail (MM_IS_SIM (self)); mm_gdbus_sim_call_send_puk (MM_GDBUS_SIM (self), puk, pin, cancellable, callback, user_data); } /** * mm_sim_send_puk_sync: * @self: A #MMSim. * @puk: The PUK code. * @pin: The PIN code. * @cancellable: (allow-none): A #GCancellable or %NULL. * @error: Return location for error or %NULL. * * Synchronously sends the PUK to the SIM card. * * The calling thread is blocked until a reply is received. * See mm_sim_send_puk() for the asynchronous version of this method. * * Returns: %TRUE if the operation succeeded, %FALSE if @error is set. * * Since: 1.0 */ gboolean mm_sim_send_puk_sync (MMSim *self, const gchar *puk, const gchar *pin, GCancellable *cancellable, GError **error) { g_return_val_if_fail (MM_IS_SIM (self), FALSE); return (mm_gdbus_sim_call_send_puk_sync (MM_GDBUS_SIM (self), puk, pin, cancellable, error)); } /*****************************************************************************/ /** * mm_sim_enable_pin_finish: * @self: A #MMSim. * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to * mm_sim_enable_pin(). * @error: Return location for error or %NULL. * * Finishes an operation started with mm_sim_enable_pin(). * * Returns: %TRUE if the operation succeeded, %FALSE if @error is set. * * Since: 1.0 */ gboolean mm_sim_enable_pin_finish (MMSim *self, GAsyncResult *res, GError **error) { g_return_val_if_fail (MM_IS_SIM (self), FALSE); return mm_gdbus_sim_call_enable_pin_finish (MM_GDBUS_SIM (self), res, error); } /** * mm_sim_enable_pin: * @self: A #MMSim. * @pin: The PIN code. * @cancellable: (allow-none): A #GCancellable or %NULL. * @callback: A #GAsyncReadyCallback to call when the request is satisfied or * %NULL. * @user_data: User data to pass to @callback. * * Asynchronously enables requesting the PIN code in the SIM card. * * When the operation is finished, @callback will be invoked in the * thread-default main loop * of the thread you are calling this method from. You can then call * mm_sim_enable_pin_finish() to get the result of the operation. * * See mm_sim_enable_pin_sync() for the synchronous, blocking version of this * method. * * Since: 1.0 */ void mm_sim_enable_pin (MMSim *self, const gchar *pin, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { g_return_if_fail (MM_IS_SIM (self)); mm_gdbus_sim_call_enable_pin (MM_GDBUS_SIM (self), pin, TRUE, cancellable, callback, user_data); } /** * mm_sim_enable_pin_sync: * @self: A #MMSim. * @pin: The PIN code. * @cancellable: (allow-none): A #GCancellable or %NULL. * @error: Return location for error or %NULL. * * Synchronously enables requesting the PIN code in the SIM card. * * The calling thread is blocked until a reply is received. * See mm_sim_enable_pin() for the asynchronous version of this method. * * Returns: %TRUE if the operation succeeded, %FALSE if @error is set. * * Since: 1.0 */ gboolean mm_sim_enable_pin_sync (MMSim *self, const gchar *pin, GCancellable *cancellable, GError **error) { g_return_val_if_fail (MM_IS_SIM (self), FALSE); return (mm_gdbus_sim_call_enable_pin_sync (MM_GDBUS_SIM (self), pin, TRUE, cancellable, error)); } /*****************************************************************************/ /** * mm_sim_disable_pin_finish: * @self: A #MMSim. * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to * mm_sim_disable_pin(). * @error: Return location for error or %NULL. * * Finishes an operation started with mm_sim_disable_pin(). * * Returns: %TRUE if the operation succeeded, %FALSE if @error is set. * * Since: 1.0 */ gboolean mm_sim_disable_pin_finish (MMSim *self, GAsyncResult *res, GError **error) { g_return_val_if_fail (MM_IS_SIM (self), FALSE); return mm_gdbus_sim_call_enable_pin_finish (MM_GDBUS_SIM (self), res, error); } /** * mm_sim_disable_pin: * @self: A #MMSim. * @pin: The PIN code. * @cancellable: (allow-none): A #GCancellable or %NULL. * @callback: A #GAsyncReadyCallback to call when the request is satisfied or * %NULL. * @user_data: User data to pass to @callback. * * Asynchronously disables requesting the PIN code in the SIM card. * * When the operation is finished, @callback will be invoked in the * thread-default main loop * of the thread you are calling this method from. You can then call * mm_sim_disable_pin_finish() to get the result of the operation. * * See mm_sim_disable_pin_sync() for the synchronous, blocking version of this * method. * * Since: 1.0 */ void mm_sim_disable_pin (MMSim *self, const gchar *pin, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { g_return_if_fail (MM_IS_SIM (self)); mm_gdbus_sim_call_enable_pin (MM_GDBUS_SIM (self), pin, FALSE, cancellable, callback, user_data); } /** * mm_sim_disable_pin_sync: * @self: A #MMSim. * @pin: The PIN code. * @cancellable: (allow-none): A #GCancellable or %NULL. * @error: Return location for error or %NULL. * * Synchronously disables requesting the PIN code in the SIM card. * * The calling thread is blocked until a reply is received. * See mm_sim_disable_pin() for the asynchronous version of this method. * * Returns: %TRUE if the operation succeeded, %FALSE if @error is set. * * Since: 1.0 */ gboolean mm_sim_disable_pin_sync (MMSim *self, const gchar *pin, GCancellable *cancellable, GError **error) { g_return_val_if_fail (MM_IS_SIM (self), FALSE); return (mm_gdbus_sim_call_enable_pin_sync (MM_GDBUS_SIM (self), pin, FALSE, cancellable, error)); } /*****************************************************************************/ /** * mm_sim_change_pin_finish: * @self: A #MMSim. * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to * mm_sim_change_pin(). * @error: Return location for error or %NULL. * * Finishes an operation started with mm_sim_change_pin(). * * Returns: %TRUE if the operation succeeded, %FALSE if @error is set. * * Since: 1.0 */ gboolean mm_sim_change_pin_finish (MMSim *self, GAsyncResult *res, GError **error) { g_return_val_if_fail (MM_IS_SIM (self), FALSE); return mm_gdbus_sim_call_change_pin_finish (MM_GDBUS_SIM (self), res, error); } /** * mm_sim_change_pin: * @self: A #MMSim. * @old_pin: The current PIN code. * @new_pin: The new PIN code to be set. * @cancellable: (allow-none): A #GCancellable or %NULL. * @callback: A #GAsyncReadyCallback to call when the request is satisfied or * %NULL. * @user_data: User data to pass to @callback. * * Asynchronously changes the PIN code in the SIM card. * * When the operation is finished, @callback will be invoked in the * thread-default main loop * of the thread you are calling this method from. You can then call * mm_sim_change_pin_finish() to get the result of the operation. * * See mm_sim_change_pin_sync() for the synchronous, blocking version of this * method. * * Since: 1.0 */ void mm_sim_change_pin (MMSim *self, const gchar *old_pin, const gchar *new_pin, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { g_return_if_fail (MM_IS_SIM (self)); mm_gdbus_sim_call_change_pin (MM_GDBUS_SIM (self), old_pin, new_pin, cancellable, callback, user_data); } /** * mm_sim_change_pin_sync: * @self: A #MMSim. * @old_pin: The current PIN code. * @new_pin: The new PIN code to be set. * @cancellable: (allow-none): A #GCancellable or %NULL. * @error: Return location for error or %NULL. * * Synchronously changes the PIN code in the SIM card. * * The calling thread is blocked until a reply is received. * See mm_sim_change_pin() for the asynchronous version of this method. * * Returns: %TRUE if the operation succeeded, %FALSE if @error is set. * * Since: 1.0 */ gboolean mm_sim_change_pin_sync (MMSim *self, const gchar *old_pin, const gchar *new_pin, GCancellable *cancellable, GError **error) { g_return_val_if_fail (MM_IS_SIM (self), FALSE); return (mm_gdbus_sim_call_change_pin_sync (MM_GDBUS_SIM (self), old_pin, new_pin, cancellable, error)); } /*****************************************************************************/ /** * mm_sim_set_preferred_networks_finish: * @self: A #MMSim. * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to * mm_sim_set_preferred_networks(). * @error: Return location for error or %NULL. * * Finishes an operation started with mm_sim_set_preferred_networks(). * * Returns: %TRUE if the operation was successful, %FALSE if @error is set. * * Since: 1.18 */ gboolean mm_sim_set_preferred_networks_finish (MMSim *self, GAsyncResult *res, GError **error) { g_return_val_if_fail (MM_IS_SIM (self), FALSE); return mm_gdbus_sim_call_set_preferred_networks_finish (MM_GDBUS_SIM (self), res, error); } /** * mm_sim_set_preferred_networks: * @self: A #MMSim. * @preferred_networks: (element-type ModemManager.SimPreferredNetwork): * A list of #MMSimPreferredNetwork objects * @cancellable: (allow-none): A #GCancellable or %NULL. * @callback: A #GAsyncReadyCallback to call when the request is satisfied or * %NULL. * @user_data: User data to pass to @callback. * * Asynchronously sets the preferred network list of this #MMSim. * * When the operation is finished, @callback will be invoked in the * thread-default main loop * of the thread you are calling this method from. You can then call * mm_sim_set_preferred_networks_finish() to get the result of * the operation. * * Since: 1.18 */ void mm_sim_set_preferred_networks (MMSim *self, const GList *preferred_networks, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { GVariant *networks_list; g_return_if_fail (MM_IS_SIM (self)); networks_list = mm_sim_preferred_network_list_get_variant (preferred_networks); mm_gdbus_sim_call_set_preferred_networks (MM_GDBUS_SIM (self), networks_list, cancellable, callback, user_data); } /** * mm_sim_set_preferred_networks_sync: * @self: A #MMSim. * @preferred_networks: (element-type ModemManager.SimPreferredNetwork): * A list of #MMSimPreferredNetwork objects * @cancellable: (allow-none): A #GCancellable or %NULL. * @error: Return location for error or %NULL. * * Synchronously sets the preferred network list of this #MMSim. * * The calling thread is blocked until a reply is received. See * mm_sim_set_preferred_networks() for the asynchronous * version of this method. * * When the operation is finished, @callback will be invoked in the * thread-default main loop * of the thread you are calling this method from. You can then call * mm_sim_set_preferred_networks_finish() to get the result of * the operation. * * Since: 1.18 */ gboolean mm_sim_set_preferred_networks_sync (MMSim *self, const GList *preferred_networks, GCancellable *cancellable, GError **error) { gboolean result; GVariant *networks_list; g_return_val_if_fail (MM_IS_SIM (self), FALSE); networks_list = mm_sim_preferred_network_list_get_variant (preferred_networks); result = mm_gdbus_sim_call_set_preferred_networks_sync (MM_GDBUS_SIM (self), networks_list, cancellable, error); return result; } /*****************************************************************************/ static void mm_sim_init (MMSim *self) { } static void mm_sim_class_init (MMSimClass *sim_class) { } ModemManager-1.23.4-dev/libmm-glib/mm-sim.h000066400000000000000000000175251456466623000203220ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * libmm-glib -- Access modem status & information from glib applications * * 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 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 Street, Fifth Floor, * Boston, MA 02110-1301 USA. * * Copyright (C) 2011 - 2012 Aleksander Morgado * Copyright (C) 2012 Google, Inc. */ #ifndef _MM_SIM_H_ #define _MM_SIM_H_ #if !defined (__LIBMM_GLIB_H_INSIDE__) && !defined (LIBMM_GLIB_COMPILATION) #error "Only can be included directly." #endif #include #include "mm-gdbus-sim.h" G_BEGIN_DECLS #define MM_TYPE_SIM (mm_sim_get_type ()) #define MM_SIM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_SIM, MMSim)) #define MM_SIM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_SIM, MMSimClass)) #define MM_IS_SIM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_SIM)) #define MM_IS_SIM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), MM_TYPE_SIM)) #define MM_SIM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_SIM, MMSimClass)) typedef struct _MMSim MMSim; typedef struct _MMSimClass MMSimClass; /** * MMSim: * * The #MMSim structure contains private data and should only be accessed * using the provided API. */ struct _MMSim { /*< private >*/ MmGdbusSimProxy parent; gpointer unused; }; struct _MMSimClass { /*< private >*/ MmGdbusSimProxyClass parent; }; GType mm_sim_get_type (void); G_DEFINE_AUTOPTR_CLEANUP_FUNC (MMSim, g_object_unref) const gchar *mm_sim_get_path (MMSim *self); gchar *mm_sim_dup_path (MMSim *self); gboolean mm_sim_get_active (MMSim *self); const gchar *mm_sim_get_identifier (MMSim *self); gchar *mm_sim_dup_identifier (MMSim *self); const gchar *mm_sim_get_imsi (MMSim *self); gchar *mm_sim_dup_imsi (MMSim *self); const gchar *mm_sim_get_eid (MMSim *self); gchar *mm_sim_dup_eid (MMSim *self); const gchar *mm_sim_get_operator_identifier (MMSim *self); gchar *mm_sim_dup_operator_identifier (MMSim *self); const gchar *mm_sim_get_operator_name (MMSim *self); gchar *mm_sim_dup_operator_name (MMSim *self); const gchar * const *mm_sim_get_emergency_numbers (MMSim *self); gchar **mm_sim_dup_emergency_numbers (MMSim *self); GList* mm_sim_get_preferred_networks (MMSim *self); const guint8 *mm_sim_get_gid1 (MMSim *self, gsize *data_len); guint8 *mm_sim_dup_gid1 (MMSim *self, gsize *data_len); const guint8 *mm_sim_get_gid2 (MMSim *self, gsize *data_len); guint8 *mm_sim_dup_gid2 (MMSim *self, gsize *data_len); MMSimType mm_sim_get_sim_type (MMSim *self); MMSimEsimStatus mm_sim_get_esim_status (MMSim *self); MMSimRemovability mm_sim_get_removability (MMSim *self); void mm_sim_send_pin (MMSim *self, const gchar *pin, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); gboolean mm_sim_send_pin_finish (MMSim *self, GAsyncResult *res, GError **error); gboolean mm_sim_send_pin_sync (MMSim *self, const gchar *pin, GCancellable *cancellable, GError **error); void mm_sim_send_puk (MMSim *self, const gchar *puk, const gchar *pin, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); gboolean mm_sim_send_puk_finish (MMSim *self, GAsyncResult *res, GError **error); gboolean mm_sim_send_puk_sync (MMSim *self, const gchar *puk, const gchar *pin, GCancellable *cancellable, GError **error); void mm_sim_enable_pin (MMSim *self, const gchar *pin, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); gboolean mm_sim_enable_pin_finish (MMSim *self, GAsyncResult *res, GError **error); gboolean mm_sim_enable_pin_sync (MMSim *self, const gchar *pin, GCancellable *cancellable, GError **error); void mm_sim_disable_pin (MMSim *self, const gchar *pin, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); gboolean mm_sim_disable_pin_finish (MMSim *self, GAsyncResult *res, GError **error); gboolean mm_sim_disable_pin_sync (MMSim *self, const gchar *pin, GCancellable *cancellable, GError **error); void mm_sim_change_pin (MMSim *self, const gchar *old_pin, const gchar *new_pin, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); gboolean mm_sim_change_pin_finish (MMSim *self, GAsyncResult *res, GError **error); gboolean mm_sim_change_pin_sync (MMSim *self, const gchar *old_pin, const gchar *new_pin, GCancellable *cancellable, GError **error); void mm_sim_set_preferred_networks (MMSim *self, const GList *preferred_networks, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); gboolean mm_sim_set_preferred_networks_finish (MMSim *self, GAsyncResult *res, GError **error); gboolean mm_sim_set_preferred_networks_sync (MMSim *self, const GList *preferred_networks, GCancellable *cancellable, GError **error); G_END_DECLS #endif /* _MM_SIM_H_ */ ModemManager-1.23.4-dev/libmm-glib/mm-simple-connect-properties.c000066400000000000000000000574751456466623000246470ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * libmm-glib -- Access modem status & information from glib applications * * 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 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 Street, Fifth Floor, * Boston, MA 02110-1301 USA. * * Copyright (C) 2011-2022 Aleksander Morgado * Copyright (C) 2022 Google, Inc. */ #include #include "mm-errors-types.h" #include "mm-common-helpers.h" #include "mm-3gpp-profile.h" #include "mm-simple-connect-properties.h" /** * SECTION: mm-simple-connect-properties * @title: MMSimpleConnectProperties * @short_description: Helper object to handle connection properties. * * The #MMSimpleConnectProperties is an object handling the properties requested * to ModemManager when launching a connection with the Simple interface. * * This object is created by the user and passed to ModemManager with either * mm_modem_simple_connect() or mm_modem_simple_connect_sync(). */ G_DEFINE_TYPE (MMSimpleConnectProperties, mm_simple_connect_properties, G_TYPE_OBJECT) #define PROPERTY_PIN "pin" #define PROPERTY_OPERATOR_ID "operator-id" struct _MMSimpleConnectPropertiesPrivate { /* PIN */ gchar *pin; /* Operator ID */ gchar *operator_id; /* Bearer properties */ MMBearerProperties *bearer_properties; }; /*****************************************************************************/ /** * mm_simple_connect_properties_set_pin: * @self: a #MMSimpleConnectProperties. * @pin: PIN code. * * Sets the PIN code to use when unlocking the modem. * * Since: 1.0 */ void mm_simple_connect_properties_set_pin (MMSimpleConnectProperties *self, const gchar *pin) { g_return_if_fail (MM_IS_SIMPLE_CONNECT_PROPERTIES (self)); g_free (self->priv->pin); self->priv->pin = g_strdup (pin); } /** * mm_simple_connect_properties_get_pin: * @self: a #MMSimpleConnectProperties. * * Gets the PIN code to use when unlocking the modem. * * Returns: (transfer none): the PIN, or #NULL if not set. Do not free the * returned value, it is owned by @self. * * Since: 1.0 */ const gchar * mm_simple_connect_properties_get_pin (MMSimpleConnectProperties *self) { g_return_val_if_fail (MM_IS_SIMPLE_CONNECT_PROPERTIES (self), NULL); return self->priv->pin; } /*****************************************************************************/ /** * mm_simple_connect_properties_set_operator_id: * @self: a #MMSimpleConnectProperties. * @operator_id: operator ID, given as MCC/MNC. * * Sets the ID of the network to which register before connecting. * * Since: 1.0 */ void mm_simple_connect_properties_set_operator_id (MMSimpleConnectProperties *self, const gchar *operator_id) { g_return_if_fail (MM_IS_SIMPLE_CONNECT_PROPERTIES (self)); g_free (self->priv->operator_id); self->priv->operator_id = g_strdup (operator_id); } /** * mm_simple_connect_properties_get_operator_id: * @self: a #MMSimpleConnectProperties. * * Gets the ID of the network to which register before connecting. * * Returns: (transfer none): the operator ID, or #NULL if not set. Do not free * the returned value, it is owned by @self. * * Since: 1.0 */ const gchar * mm_simple_connect_properties_get_operator_id (MMSimpleConnectProperties *self) { g_return_val_if_fail (MM_IS_SIMPLE_CONNECT_PROPERTIES (self), NULL); return self->priv->operator_id; } /*****************************************************************************/ /** * mm_simple_connect_properties_set_apn: * @self: a #MMSimpleConnectProperties. * @apn: Name of the access point. * * Sets the name of the access point to use when connecting. * * Since: 1.0 */ void mm_simple_connect_properties_set_apn (MMSimpleConnectProperties *self, const gchar *apn) { g_return_if_fail (MM_IS_SIMPLE_CONNECT_PROPERTIES (self)); mm_bearer_properties_set_apn (self->priv->bearer_properties, apn); } /** * mm_simple_connect_properties_get_apn: * @self: a #MMSimpleConnectProperties. * * Gets the name of the access point to use when connecting. * * Returns: (transfer none): the access point, or #NULL if not set. Do not free * the returned value, it is owned by @self. * * Since: 1.0 */ const gchar * mm_simple_connect_properties_get_apn (MMSimpleConnectProperties *self) { g_return_val_if_fail (MM_IS_SIMPLE_CONNECT_PROPERTIES (self), NULL); return mm_bearer_properties_get_apn (self->priv->bearer_properties); } /*****************************************************************************/ /** * mm_simple_connect_properties_set_allowed_auth: * @self: a #MMSimpleConnectProperties. * @allowed_auth: a bitmask of #MMBearerAllowedAuth values. * %MM_BEARER_ALLOWED_AUTH_UNKNOWN may be given to request the modem-default method. * * Sets the authentication method to use. * * Since: 1.0 */ void mm_simple_connect_properties_set_allowed_auth (MMSimpleConnectProperties *self, MMBearerAllowedAuth allowed_auth) { g_return_if_fail (MM_IS_SIMPLE_CONNECT_PROPERTIES (self)); mm_bearer_properties_set_allowed_auth (self->priv->bearer_properties, allowed_auth); } /** * mm_simple_connect_properties_get_allowed_auth: * @self: a #MMSimpleConnectProperties. * * Gets the authentication methods allowed in the connection. * * Returns: a bitmask of #MMBearerAllowedAuth values, or * %MM_BEARER_ALLOWED_AUTH_UNKNOWN to request the modem-default method. * * Since: 1.0 */ MMBearerAllowedAuth mm_simple_connect_properties_get_allowed_auth (MMSimpleConnectProperties *self) { g_return_val_if_fail (MM_IS_SIMPLE_CONNECT_PROPERTIES (self), MM_BEARER_ALLOWED_AUTH_UNKNOWN); return mm_bearer_properties_get_allowed_auth (self->priv->bearer_properties); } /*****************************************************************************/ /** * mm_simple_connect_properties_set_user: * @self: a #MMSimpleConnectProperties. * @user: the username * * Sets the username used to authenticate with the access point. * * Since: 1.0 */ void mm_simple_connect_properties_set_user (MMSimpleConnectProperties *self, const gchar *user) { g_return_if_fail (MM_IS_SIMPLE_CONNECT_PROPERTIES (self)); mm_bearer_properties_set_user (self->priv->bearer_properties, user); } /** * mm_simple_connect_properties_get_user: * @self: a #MMSimpleConnectProperties. * * Gets the username used to authenticate with the access point. * * Returns: (transfer none): the username, or #NULL if not set. Do not free the * returned value, it is owned by @self. * * Since: 1.0 */ const gchar * mm_simple_connect_properties_get_user (MMSimpleConnectProperties *self) { g_return_val_if_fail (MM_IS_SIMPLE_CONNECT_PROPERTIES (self), NULL); return mm_bearer_properties_get_user (self->priv->bearer_properties); } /*****************************************************************************/ /** * mm_simple_connect_properties_set_password: * @self: a #MMSimpleConnectProperties. * @password: the password * * Sets the password used to authenticate with the access point. * * Since: 1.0 */ void mm_simple_connect_properties_set_password (MMSimpleConnectProperties *self, const gchar *password) { g_return_if_fail (MM_IS_SIMPLE_CONNECT_PROPERTIES (self)); mm_bearer_properties_set_password (self->priv->bearer_properties, password); } /** * mm_simple_connect_properties_get_password: * @self: a #MMSimpleConnectProperties. * * Gets the password used to authenticate with the access point. * * Returns: (transfer none): the password, or #NULL if not set. Do not free the * returned value, it is owned by @self. * * Since: 1.0 */ const gchar * mm_simple_connect_properties_get_password (MMSimpleConnectProperties *self) { g_return_val_if_fail (MM_IS_SIMPLE_CONNECT_PROPERTIES (self), NULL); return mm_bearer_properties_get_password (self->priv->bearer_properties); } /*****************************************************************************/ /** * mm_simple_connect_properties_set_ip_type: * @self: a #MMSimpleConnectProperties. * @ip_type: a #MMBearerIpFamily. * * Sets the IP type to use. * * Since: 1.0 */ void mm_simple_connect_properties_set_ip_type (MMSimpleConnectProperties *self, MMBearerIpFamily ip_type) { g_return_if_fail (MM_IS_SIMPLE_CONNECT_PROPERTIES (self)); mm_bearer_properties_set_ip_type (self->priv->bearer_properties, ip_type); } /** * mm_simple_connect_properties_get_ip_type: * @self: a #MMSimpleConnectProperties. * * Sets the IP type to use. * * Returns: a #MMBearerIpFamily. * * Since: 1.0 */ MMBearerIpFamily mm_simple_connect_properties_get_ip_type (MMSimpleConnectProperties *self) { g_return_val_if_fail (MM_IS_SIMPLE_CONNECT_PROPERTIES (self), MM_BEARER_IP_FAMILY_NONE); return mm_bearer_properties_get_ip_type (self->priv->bearer_properties); } /*****************************************************************************/ /** * mm_simple_connect_properties_set_apn_type: * @self: a #MMSimpleConnectProperties. * @apn_type: a mask of #MMBearerApnType values. * * Sets the APN types to use. * * Since: 1.18 */ void mm_simple_connect_properties_set_apn_type (MMSimpleConnectProperties *self, MMBearerApnType apn_type) { g_return_if_fail (MM_IS_SIMPLE_CONNECT_PROPERTIES (self)); mm_bearer_properties_set_apn_type (self->priv->bearer_properties, apn_type); } /** * mm_simple_connect_properties_get_apn_type: * @self: a #MMSimpleConnectProperties. * * Gets the APN types to use. * * Returns: a mask of #MMBearerApnType values. * * Since: 1.18 */ MMBearerApnType mm_simple_connect_properties_get_apn_type (MMSimpleConnectProperties *self) { g_return_val_if_fail (MM_IS_SIMPLE_CONNECT_PROPERTIES (self), MM_BEARER_APN_TYPE_NONE); return mm_bearer_properties_get_apn_type (self->priv->bearer_properties); } /*****************************************************************************/ /** * mm_simple_connect_properties_set_profile_id: * @self: a #MMSimpleConnectProperties. * @profile_id: a profile id. * * Sets the profile ID to use. * * Since: 1.18 */ void mm_simple_connect_properties_set_profile_id (MMSimpleConnectProperties *self, gint profile_id) { g_return_if_fail (MM_IS_SIMPLE_CONNECT_PROPERTIES (self)); mm_bearer_properties_set_profile_id (self->priv->bearer_properties, profile_id); } /** * mm_simple_connect_properties_get_profile_id: * @self: a #MMSimpleConnectProperties. * * Gets the profile ID to use. * * Returns: the profile id. * * Since: 1.18 */ gint mm_simple_connect_properties_get_profile_id (MMSimpleConnectProperties *self) { g_return_val_if_fail (MM_IS_SIMPLE_CONNECT_PROPERTIES (self), MM_3GPP_PROFILE_ID_UNKNOWN); return mm_bearer_properties_get_profile_id (self->priv->bearer_properties); } /*****************************************************************************/ /** * mm_simple_connect_properties_set_allow_roaming: * @self: a #MMSimpleConnectProperties. * @allow_roaming: boolean value. * * Sets the flag to indicate whether roaming is allowed or not in the * connection. * * Since: 1.0 */ void mm_simple_connect_properties_set_allow_roaming (MMSimpleConnectProperties *self, gboolean allow_roaming) { g_return_if_fail (MM_IS_SIMPLE_CONNECT_PROPERTIES (self)); mm_bearer_properties_set_allow_roaming (self->priv->bearer_properties, allow_roaming); } /** * mm_simple_connect_properties_get_allow_roaming: * @self: a #MMSimpleConnectProperties. * * Checks whether roaming is allowed in the connection. * * Returns: %TRUE if roaming is allowed, %FALSE otherwise. * * Since: 1.0 */ gboolean mm_simple_connect_properties_get_allow_roaming (MMSimpleConnectProperties *self) { g_return_val_if_fail (MM_IS_SIMPLE_CONNECT_PROPERTIES (self), FALSE); return mm_bearer_properties_get_allow_roaming (self->priv->bearer_properties); } /*****************************************************************************/ /** * mm_simple_connect_properties_set_rm_protocol: * @self: a #MMSimpleConnectProperties. * @protocol: a #MMModemCdmaRmProtocol. * * Sets the RM protocol requested by the user. * * Since: 1.16 */ void mm_simple_connect_properties_set_rm_protocol (MMSimpleConnectProperties *self, MMModemCdmaRmProtocol protocol) { g_return_if_fail (MM_IS_SIMPLE_CONNECT_PROPERTIES (self)); mm_bearer_properties_set_rm_protocol (self->priv->bearer_properties, protocol); } /** * mm_simple_connect_properties_get_rm_protocol: * @self: a #MMSimpleConnectProperties. * * Get the RM protocol requested by the user. * * Returns: a #MMModemCdmaRmProtocol. * * Since: 1.16 */ MMModemCdmaRmProtocol mm_simple_connect_properties_get_rm_protocol (MMSimpleConnectProperties *self) { g_return_val_if_fail (MM_IS_SIMPLE_CONNECT_PROPERTIES (self), MM_MODEM_CDMA_RM_PROTOCOL_UNKNOWN); return mm_bearer_properties_get_rm_protocol (self->priv->bearer_properties); } /*****************************************************************************/ /** * mm_simple_connect_properties_set_multiplex: * @self: a #MMSimpleConnectProperties. * @multiplex: a #MMBearerMultiplexSupport. * * Sets the multiplex support requested by the user. * * Since: 1.18 */ void mm_simple_connect_properties_set_multiplex (MMSimpleConnectProperties *self, MMBearerMultiplexSupport multiplex) { g_return_if_fail (MM_IS_SIMPLE_CONNECT_PROPERTIES (self)); mm_bearer_properties_set_multiplex (self->priv->bearer_properties, multiplex); } /** * mm_simple_connect_properties_get_multiplex: * @self: a #MMSimpleConnectProperties. * * Get the multiplex support requested by the user. * * Returns: a #MMBearerMultiplexSupport. * * Since: 1.18 */ MMBearerMultiplexSupport mm_simple_connect_properties_get_multiplex (MMSimpleConnectProperties *self) { g_return_val_if_fail (MM_IS_SIMPLE_CONNECT_PROPERTIES (self), MM_BEARER_MULTIPLEX_SUPPORT_UNKNOWN); return mm_bearer_properties_get_multiplex (self->priv->bearer_properties); } /*****************************************************************************/ /** * mm_simple_connect_properties_get_bearer_properties: (skip) */ MMBearerProperties * mm_simple_connect_properties_get_bearer_properties (MMSimpleConnectProperties *self) { g_return_val_if_fail (MM_IS_SIMPLE_CONNECT_PROPERTIES (self), NULL); return g_object_ref (self->priv->bearer_properties); } /*****************************************************************************/ /** * mm_simple_connect_properties_get_dictionary: (skip) */ GVariant * mm_simple_connect_properties_get_dictionary (MMSimpleConnectProperties *self) { GVariantBuilder builder; GVariantIter iter; gchar *key; GVariant *value; GVariant *bearer_properties_dictionary; /* We do allow NULL */ if (!self) return NULL; g_return_val_if_fail (MM_IS_SIMPLE_CONNECT_PROPERTIES (self), NULL); g_variant_builder_init (&builder, G_VARIANT_TYPE ("a{sv}")); if (self->priv->pin) g_variant_builder_add (&builder, "{sv}", PROPERTY_PIN, g_variant_new_string (self->priv->pin)); if (self->priv->operator_id) g_variant_builder_add (&builder, "{sv}", PROPERTY_OPERATOR_ID, g_variant_new_string (self->priv->operator_id)); /* Merge dictionaries */ bearer_properties_dictionary = mm_bearer_properties_get_dictionary (self->priv->bearer_properties); g_variant_iter_init (&iter, bearer_properties_dictionary); while (g_variant_iter_next (&iter, "{sv}", &key, &value)) { g_variant_builder_add (&builder, "{sv}", key, value); g_variant_unref (value); g_free (key); } g_variant_unref (bearer_properties_dictionary); return g_variant_ref_sink (g_variant_builder_end (&builder)); } /*****************************************************************************/ typedef struct { MMSimpleConnectProperties *self; GError *error; gchar *allowed_modes_str; gchar *preferred_mode_str; } ParseKeyValueContext; static gboolean key_value_foreach (const gchar *key, const gchar *value, ParseKeyValueContext *ctx) { GError *inner_error = NULL; /* First, check if we can consume this as bearer properties */ if (mm_bearer_properties_consume_string (ctx->self->priv->bearer_properties, key, value, &inner_error)) return TRUE; /* Unknown keys are reported as unsupported. Any other error is right away * fatal (e.g. an invalid value given to a known bearer property) */ if (!g_error_matches (inner_error, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED)) { ctx->error = inner_error; return FALSE; } /* On unsupported errors, try with the Simple.Connect specific properties */ g_clear_error (&inner_error); if (g_str_equal (key, PROPERTY_PIN)) mm_simple_connect_properties_set_pin (ctx->self, value); else if (g_str_equal (key, PROPERTY_OPERATOR_ID)) mm_simple_connect_properties_set_operator_id (ctx->self, value); else { ctx->error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "Invalid properties string, unsupported key '%s'", key); } return !ctx->error; } /** * mm_simple_connect_properties_new_from_string: (skip) */ MMSimpleConnectProperties * mm_simple_connect_properties_new_from_string (const gchar *str, GError **error) { ParseKeyValueContext ctx; ctx.error = NULL; ctx.self = mm_simple_connect_properties_new (); mm_common_parse_key_value_string (str, &ctx.error, (MMParseKeyValueForeachFn)key_value_foreach, &ctx); /* If error, destroy the object */ if (ctx.error) { g_propagate_error (error, ctx.error); g_object_unref (ctx.self); ctx.self = NULL; } return ctx.self; } /*****************************************************************************/ /** * mm_simple_connect_properties_new_from_dictionary: (skip) */ MMSimpleConnectProperties * mm_simple_connect_properties_new_from_dictionary (GVariant *dictionary, GError **error) { GError *inner_error = NULL; GVariantIter iter; gchar *key; GVariant *value; MMSimpleConnectProperties *self; self = mm_simple_connect_properties_new (); if (!dictionary) return self; if (!g_variant_is_of_type (dictionary, G_VARIANT_TYPE ("a{sv}"))) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS, "Cannot create Simple Connect properties from dictionary: " "invalid variant type received"); g_object_unref (self); return NULL; } g_variant_iter_init (&iter, dictionary); while (!inner_error && g_variant_iter_next (&iter, "{sv}", &key, &value)) { /* First, check if we can consume this as bearer properties */ if (!mm_bearer_properties_consume_variant (self->priv->bearer_properties, key, value, NULL)) { if (g_str_equal (key, PROPERTY_PIN)) mm_simple_connect_properties_set_pin ( self, g_variant_get_string (value, NULL)); else if (g_str_equal (key, PROPERTY_OPERATOR_ID)) mm_simple_connect_properties_set_operator_id ( self, g_variant_get_string (value, NULL)); else { /* Set inner error, will stop the loop */ inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS, "Invalid properties dictionary, unexpected key '%s'", key); } } g_free (key); g_variant_unref (value); } /* If error, destroy the object */ if (inner_error) { g_propagate_error (error, inner_error); g_object_unref (self); self = NULL; } return self; } /*****************************************************************************/ /** * mm_simple_connect_properties_print: (skip) */ GPtrArray * mm_simple_connect_properties_print (MMSimpleConnectProperties *self, gboolean show_personal_info) { GPtrArray *array; array = mm_bearer_properties_print (self->priv->bearer_properties, show_personal_info); if (self->priv->pin) g_ptr_array_add (array, g_strdup_printf (PROPERTY_PIN ": %s", mm_common_str_personal_info (self->priv->pin, show_personal_info))); if (self->priv->operator_id) g_ptr_array_add (array, g_strdup_printf (PROPERTY_OPERATOR_ID ": %s", self->priv->operator_id)); return array; } /*****************************************************************************/ /** * mm_simple_connect_properties_new: * * Creates a new empty #MMSimpleConnectProperties. * * Returns: (transfer full): a #MMSimpleConnectProperties. The returned value should be freed with g_object_unref(). * * Since: 1.0 */ MMSimpleConnectProperties * mm_simple_connect_properties_new (void) { return (MM_SIMPLE_CONNECT_PROPERTIES ( g_object_new (MM_TYPE_SIMPLE_CONNECT_PROPERTIES, NULL))); } static void mm_simple_connect_properties_init (MMSimpleConnectProperties *self) { self->priv = G_TYPE_INSTANCE_GET_PRIVATE ((self), MM_TYPE_SIMPLE_CONNECT_PROPERTIES, MMSimpleConnectPropertiesPrivate); /* Some defaults */ self->priv->bearer_properties = mm_bearer_properties_new (); } static void finalize (GObject *object) { MMSimpleConnectProperties *self = MM_SIMPLE_CONNECT_PROPERTIES (object); g_free (self->priv->pin); g_free (self->priv->operator_id); g_object_unref (self->priv->bearer_properties); G_OBJECT_CLASS (mm_simple_connect_properties_parent_class)->finalize (object); } static void mm_simple_connect_properties_class_init (MMSimpleConnectPropertiesClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); g_type_class_add_private (object_class, sizeof (MMSimpleConnectPropertiesPrivate)); object_class->finalize = finalize; } ModemManager-1.23.4-dev/libmm-glib/mm-simple-connect-properties.h000066400000000000000000000161751456466623000246440ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * libmm-glib -- Access modem status & information from glib applications * * 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 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 Street, Fifth Floor, * Boston, MA 02110-1301 USA. * * Copyright (C) 2011-2022 Aleksander Morgado * Copyright (C) 2022 Google, Inc. */ #ifndef MM_SIMPLE_CONNECT_PROPERTIES_H #define MM_SIMPLE_CONNECT_PROPERTIES_H #if !defined (__LIBMM_GLIB_H_INSIDE__) && !defined (LIBMM_GLIB_COMPILATION) #error "Only can be included directly." #endif #include #include #include "mm-bearer-properties.h" G_BEGIN_DECLS #define MM_TYPE_SIMPLE_CONNECT_PROPERTIES (mm_simple_connect_properties_get_type ()) #define MM_SIMPLE_CONNECT_PROPERTIES(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_SIMPLE_CONNECT_PROPERTIES, MMSimpleConnectProperties)) #define MM_SIMPLE_CONNECT_PROPERTIES_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_SIMPLE_CONNECT_PROPERTIES, MMSimpleConnectPropertiesClass)) #define MM_IS_SIMPLE_CONNECT_PROPERTIES(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_SIMPLE_CONNECT_PROPERTIES)) #define MM_IS_SIMPLE_CONNECT_PROPERTIES_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_SIMPLE_CONNECT_PROPERTIES)) #define MM_SIMPLE_CONNECT_PROPERTIES_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_SIMPLE_CONNECT_PROPERTIES, MMSimpleConnectPropertiesClass)) typedef struct _MMSimpleConnectProperties MMSimpleConnectProperties; typedef struct _MMSimpleConnectPropertiesClass MMSimpleConnectPropertiesClass; typedef struct _MMSimpleConnectPropertiesPrivate MMSimpleConnectPropertiesPrivate; /** * MMSimpleConnectProperties: * * The #MMSimpleConnectProperties structure contains private data and should * only be accessed using the provided API. */ struct _MMSimpleConnectProperties { /*< private >*/ GObject parent; MMSimpleConnectPropertiesPrivate *priv; }; struct _MMSimpleConnectPropertiesClass { /*< private >*/ GObjectClass parent; }; GType mm_simple_connect_properties_get_type (void); G_DEFINE_AUTOPTR_CLEANUP_FUNC (MMSimpleConnectProperties, g_object_unref) MMSimpleConnectProperties *mm_simple_connect_properties_new (void); void mm_simple_connect_properties_set_pin (MMSimpleConnectProperties *self, const gchar *pin); void mm_simple_connect_properties_set_operator_id (MMSimpleConnectProperties *self, const gchar *operator_id); void mm_simple_connect_properties_set_apn (MMSimpleConnectProperties *self, const gchar *apn); void mm_simple_connect_properties_set_allowed_auth (MMSimpleConnectProperties *self, MMBearerAllowedAuth allowed_auth); void mm_simple_connect_properties_set_user (MMSimpleConnectProperties *self, const gchar *user); void mm_simple_connect_properties_set_password (MMSimpleConnectProperties *self, const gchar *password); void mm_simple_connect_properties_set_ip_type (MMSimpleConnectProperties *self, MMBearerIpFamily ip_type); void mm_simple_connect_properties_set_apn_type (MMSimpleConnectProperties *self, MMBearerApnType apn_type); void mm_simple_connect_properties_set_profile_id (MMSimpleConnectProperties *self, gint profile_id); void mm_simple_connect_properties_set_allow_roaming (MMSimpleConnectProperties *self, gboolean allow_roaming); void mm_simple_connect_properties_set_rm_protocol (MMSimpleConnectProperties *self, MMModemCdmaRmProtocol protocol); void mm_simple_connect_properties_set_multiplex (MMSimpleConnectProperties *self, MMBearerMultiplexSupport multiplex); const gchar *mm_simple_connect_properties_get_pin (MMSimpleConnectProperties *self); const gchar *mm_simple_connect_properties_get_operator_id (MMSimpleConnectProperties *self); const gchar *mm_simple_connect_properties_get_apn (MMSimpleConnectProperties *self); MMBearerAllowedAuth mm_simple_connect_properties_get_allowed_auth (MMSimpleConnectProperties *self); const gchar *mm_simple_connect_properties_get_user (MMSimpleConnectProperties *self); const gchar *mm_simple_connect_properties_get_password (MMSimpleConnectProperties *self); MMBearerIpFamily mm_simple_connect_properties_get_ip_type (MMSimpleConnectProperties *self); MMBearerApnType mm_simple_connect_properties_get_apn_type (MMSimpleConnectProperties *self); gint mm_simple_connect_properties_get_profile_id (MMSimpleConnectProperties *self); gboolean mm_simple_connect_properties_get_allow_roaming (MMSimpleConnectProperties *self); MMModemCdmaRmProtocol mm_simple_connect_properties_get_rm_protocol (MMSimpleConnectProperties *self); MMBearerMultiplexSupport mm_simple_connect_properties_get_multiplex (MMSimpleConnectProperties *self); /*****************************************************************************/ /* ModemManager/libmm-glib/mmcli specific methods */ #if defined (_LIBMM_INSIDE_MM) || \ defined (_LIBMM_INSIDE_MMCLI) || \ defined (LIBMM_GLIB_COMPILATION) MMSimpleConnectProperties *mm_simple_connect_properties_new_from_string (const gchar *str, GError **error); MMSimpleConnectProperties *mm_simple_connect_properties_new_from_dictionary (GVariant *dictionary, GError **error); MMBearerProperties *mm_simple_connect_properties_get_bearer_properties (MMSimpleConnectProperties *self); GVariant *mm_simple_connect_properties_get_dictionary (MMSimpleConnectProperties *self); GPtrArray *mm_simple_connect_properties_print (MMSimpleConnectProperties *self, gboolean show_personal_info); #endif G_END_DECLS #endif /* MM_SIMPLE_CONNECT_PROPERTIES_H */ ModemManager-1.23.4-dev/libmm-glib/mm-simple-status.c000066400000000000000000000701021456466623000223250ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * libmm-glib -- Access modem status & information from glib applications * * 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 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 Street, Fifth Floor, * Boston, MA 02110-1301 USA. * * Copyright (C) 2011 Google, Inc. */ #include #include "mm-enums-types.h" #include "mm-flags-types.h" #include "mm-errors-types.h" #include "mm-common-helpers.h" #include "mm-simple-status.h" #include "mm-modem-cdma.h" /** * SECTION: mm-simple-status * @title: MMSimpleStatus * @short_description: Helper object to handle overall modem status. * * The #MMSimpleStatus is an object handling the general modem status properties, * available in the Simple interface. * * This object is retrieved with either mm_modem_simple_get_status() or * mm_modem_simple_get_status_sync(). */ G_DEFINE_TYPE (MMSimpleStatus, mm_simple_status, G_TYPE_OBJECT) enum { PROP_0, PROP_STATE, PROP_SIGNAL_QUALITY, PROP_CURRENT_BANDS, PROP_ACCESS_TECHNOLOGIES, PROP_3GPP_REGISTRATION_STATE, PROP_3GPP_OPERATOR_CODE, PROP_3GPP_OPERATOR_NAME, PROP_3GPP_SUBSCRIPTION_STATE, PROP_CDMA_CDMA1X_REGISTRATION_STATE, PROP_CDMA_EVDO_REGISTRATION_STATE, PROP_CDMA_SID, PROP_CDMA_NID, PROP_LAST }; static GParamSpec *properties[PROP_LAST]; struct _MMSimpleStatusPrivate { /* <--- From the Modem interface ---> */ /* Overall modem state, signature 'u' */ MMModemState state; /* Signal quality, given only when registered, signature '(ub)' */ GVariant *signal_quality; /* List of bands, given only when registered, signature: au */ GVariant *current_bands; GArray *current_bands_array; /* Access technologies, given only when registered, signature: u */ MMModemAccessTechnology access_technologies; /* <--- From the Modem 3GPP interface ---> */ /* 3GPP registration state, signature 'u' */ MMModem3gppRegistrationState modem_3gpp_registration_state; /* 3GPP operator code, given only when registered, signature 's' */ gchar *modem_3gpp_operator_code; /* 3GPP operator name, given only when registered, signature 's' */ gchar *modem_3gpp_operator_name; /* <--- From the Modem CDMA interface ---> */ /* CDMA/CDMA1x registration state, signature 'u' */ MMModemCdmaRegistrationState modem_cdma_cdma1x_registration_state; /* CDMA/EV-DO registration state, signature 'u' */ MMModemCdmaRegistrationState modem_cdma_evdo_registration_state; /* SID, signature 'u' */ guint modem_cdma_sid; /* NID, signature 'u' */ guint modem_cdma_nid; }; /*****************************************************************************/ /** * mm_simple_status_get_state: * @self: a #MMSimpleStatus. * * Gets the state of the modem. * * Returns: a #MMModemState. * * Since: 1.0 */ MMModemState mm_simple_status_get_state (MMSimpleStatus *self) { g_return_val_if_fail (MM_IS_SIMPLE_STATUS (self), MM_MODEM_STATE_UNKNOWN); return self->priv->state; } /*****************************************************************************/ /** * mm_simple_status_get_signal_quality: * @self: a #MMSimpleStatus. * @recent: (out) (allow-none): indication of whether the given signal quality * is considered recent. * * Gets the signal quality. * * Returns: the signal quality. * * Since: 1.0 */ guint32 mm_simple_status_get_signal_quality (MMSimpleStatus *self, gboolean *recent) { guint32 signal_quality = 0; gboolean signal_quality_recent = FALSE; g_return_val_if_fail (MM_IS_SIMPLE_STATUS (self), 0); if (self->priv->signal_quality) { g_variant_get (self->priv->signal_quality, "(ub)", &signal_quality, &signal_quality_recent); } if (recent) *recent = signal_quality_recent; return signal_quality; } /*****************************************************************************/ /** * mm_simple_status_get_current_bands: * @self: a #MMSimpleStatus. * @bands: (out): location for an array of #MMModemBand values. Do not free the * returned value, it is owned by @self. * @n_bands: (out): number of elements in @bands. * * Gets the currently used frequency bands. * * Since: 1.0 */ void mm_simple_status_get_current_bands (MMSimpleStatus *self, const MMModemBand **bands, guint *n_bands) { g_return_if_fail (MM_IS_SIMPLE_STATUS (self)); if (!self->priv->current_bands_array) self->priv->current_bands_array = mm_common_bands_variant_to_garray (self->priv->current_bands); *n_bands = self->priv->current_bands_array->len; *bands = (const MMModemBand *)self->priv->current_bands_array->data; } /*****************************************************************************/ /** * mm_simple_status_get_access_technologies: * @self: a #MMSimpleStatus. * * Gets the currently used access technologies. * * Returns: a bitmask of #MMModemAccessTechnology values. * * Since: 1.0 */ MMModemAccessTechnology mm_simple_status_get_access_technologies (MMSimpleStatus *self) { g_return_val_if_fail (MM_IS_SIMPLE_STATUS (self), MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN); return self->priv->access_technologies; } /*****************************************************************************/ /** * mm_simple_status_get_3gpp_registration_state: * @self: a #MMSimpleStatus. * * Gets the current state of the registration in the 3GPP network. * * Returns: a #MMModem3gppRegistrationState. * * Since: 1.0 */ MMModem3gppRegistrationState mm_simple_status_get_3gpp_registration_state (MMSimpleStatus *self) { g_return_val_if_fail (MM_IS_SIMPLE_STATUS (self), MM_MODEM_3GPP_REGISTRATION_STATE_UNKNOWN); return self->priv->modem_3gpp_registration_state; } /*****************************************************************************/ /** * mm_simple_status_get_3gpp_operator_code: * @self: a #MMSimpleStatus. * * Gets the MCC/MNC of the operator of the 3GPP network where the modem is * registered. * * Returns: the operator code, or %NULL if unknown. Do not free the returned * value, it is owned by @self. * * Since: 1.0 */ const gchar * mm_simple_status_get_3gpp_operator_code (MMSimpleStatus *self) { g_return_val_if_fail (MM_IS_SIMPLE_STATUS (self), NULL); return self->priv->modem_3gpp_operator_code; } /*****************************************************************************/ /** * mm_simple_status_get_3gpp_operator_name: * @self: a #MMSimpleStatus. * * Gets the name of the operator of the 3GPP network where the modem is * registered. * * Returns: the operator name, or %NULL if unknown. Do not free the returned * value, it is owned by @self. * * Since: 1.0 */ const gchar * mm_simple_status_get_3gpp_operator_name (MMSimpleStatus *self) { g_return_val_if_fail (MM_IS_SIMPLE_STATUS (self), NULL); return self->priv->modem_3gpp_operator_name; } /*****************************************************************************/ /** * mm_simple_status_get_cdma_cdma1x_registration_state: * @self: a #MMSimpleStatus. * * Gets the current state of the registration in the CDMA-1x network. * * Returns: a #MMModemCdmaRegistrationState. * * Since: 1.0 */ MMModemCdmaRegistrationState mm_simple_status_get_cdma_cdma1x_registration_state (MMSimpleStatus *self) { g_return_val_if_fail (MM_IS_SIMPLE_STATUS (self), MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN); return self->priv->modem_cdma_cdma1x_registration_state; } /*****************************************************************************/ /** * mm_simple_status_get_cdma_evdo_registration_state: * @self: a #MMSimpleStatus. * * Gets the current state of the registration in the EV-DO network. * * Returns: a #MMModemCdmaRegistrationState. * * Since: 1.0 */ MMModemCdmaRegistrationState mm_simple_status_get_cdma_evdo_registration_state (MMSimpleStatus *self) { g_return_val_if_fail (MM_IS_SIMPLE_STATUS (self), MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN); return self->priv->modem_cdma_evdo_registration_state; } /*****************************************************************************/ /** * mm_simple_status_get_cdma_sid: * @self: a #MMSimpleStatus. * * Gets the System Identification number of the CDMA network. * * Returns: the SID, or %MM_MODEM_CDMA_SID_UNKNOWN if unknown. * * Since: 1.0 */ guint mm_simple_status_get_cdma_sid (MMSimpleStatus *self) { g_return_val_if_fail (MM_IS_SIMPLE_STATUS (self), MM_MODEM_CDMA_SID_UNKNOWN); return self->priv->modem_cdma_sid; } /*****************************************************************************/ /** * mm_simple_status_get_cdma_nid: * @self: a #MMSimpleStatus. * * Gets the Network Identification number of the CDMA network. * * Returns: the NID, or %MM_MODEM_CDMA_NID_UNKNOWN if unknown. * * Since: 1.0 */ guint mm_simple_status_get_cdma_nid (MMSimpleStatus *self) { g_return_val_if_fail (MM_IS_SIMPLE_STATUS (self), MM_MODEM_CDMA_NID_UNKNOWN); return self->priv->modem_cdma_nid; } /*****************************************************************************/ /** * mm_simple_status_get_dictionary: (skip) */ GVariant * mm_simple_status_get_dictionary (MMSimpleStatus *self) { GVariantBuilder builder; /* Allow NULL */ if (!self) return NULL; g_return_val_if_fail (MM_IS_SIMPLE_STATUS (self), NULL); g_variant_builder_init (&builder, G_VARIANT_TYPE ("a{sv}")); g_variant_builder_add (&builder, "{sv}", MM_SIMPLE_PROPERTY_STATE, g_variant_new_uint32 (self->priv->state)); /* Next ones, only when registered */ if (self->priv->state >= MM_MODEM_STATE_REGISTERED) { g_variant_builder_add (&builder, "{sv}", MM_SIMPLE_PROPERTY_SIGNAL_QUALITY, self->priv->signal_quality); g_variant_builder_add (&builder, "{sv}", MM_SIMPLE_PROPERTY_CURRENT_BANDS, self->priv->current_bands); g_variant_builder_add (&builder, "{sv}", MM_SIMPLE_PROPERTY_ACCESS_TECHNOLOGIES, g_variant_new_uint32 (self->priv->access_technologies)); g_variant_builder_add (&builder, "{sv}", MM_SIMPLE_PROPERTY_3GPP_REGISTRATION_STATE, g_variant_new_uint32 (self->priv->modem_3gpp_registration_state)); if (self->priv->modem_3gpp_operator_code) g_variant_builder_add (&builder, "{sv}", MM_SIMPLE_PROPERTY_3GPP_OPERATOR_CODE, g_variant_new_string (self->priv->modem_3gpp_operator_code)); if (self->priv->modem_3gpp_operator_name) g_variant_builder_add (&builder, "{sv}", MM_SIMPLE_PROPERTY_3GPP_OPERATOR_NAME, g_variant_new_string (self->priv->modem_3gpp_operator_name)); if (self->priv->modem_cdma_cdma1x_registration_state != MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN) { g_variant_builder_add (&builder, "{sv}", MM_SIMPLE_PROPERTY_CDMA_CDMA1X_REGISTRATION_STATE, g_variant_new_uint32 (self->priv->modem_cdma_cdma1x_registration_state)); if (self->priv->modem_cdma_sid != MM_MODEM_CDMA_SID_UNKNOWN) g_variant_builder_add (&builder, "{sv}", MM_SIMPLE_PROPERTY_CDMA_SID, g_variant_new_uint32 (self->priv->modem_cdma_sid)); if (self->priv->modem_cdma_nid != MM_MODEM_CDMA_NID_UNKNOWN) g_variant_builder_add (&builder, "{sv}", MM_SIMPLE_PROPERTY_CDMA_NID, g_variant_new_uint32 (self->priv->modem_cdma_nid)); } if (self->priv->modem_cdma_evdo_registration_state != MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN) g_variant_builder_add (&builder, "{sv}", MM_SIMPLE_PROPERTY_CDMA_EVDO_REGISTRATION_STATE, g_variant_new_uint32 (self->priv->modem_cdma_evdo_registration_state)); } return g_variant_ref_sink (g_variant_builder_end (&builder)); } /*****************************************************************************/ /** * mm_simple_status_new_from_dictionary: (skip) */ MMSimpleStatus * mm_simple_status_new_from_dictionary (GVariant *dictionary, GError **error) { GError *inner_error = NULL; GVariantIter iter; gchar *key; GVariant *value; g_autoptr(MMSimpleStatus) props = NULL; props = mm_simple_status_new (); if (!dictionary) return g_steal_pointer (&props); if (!g_variant_is_of_type (dictionary, G_VARIANT_TYPE ("a{sv}"))) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS, "Cannot create Simple status from dictionary: " "invalid variant type received"); return NULL; } g_variant_iter_init (&iter, dictionary); while (!inner_error && g_variant_iter_next (&iter, "{sv}", &key, &value)) { /* Note: we could do a more efficient matching by checking the variant type * and just g_object_set()-ing they specific 'key' and value, but we do want * to check which input keys we receive, in order to propagate the error. */ if (g_str_equal (key, MM_SIMPLE_PROPERTY_STATE) || g_str_equal (key, MM_SIMPLE_PROPERTY_ACCESS_TECHNOLOGIES) || g_str_equal (key, MM_SIMPLE_PROPERTY_3GPP_REGISTRATION_STATE) || g_str_equal (key, MM_SIMPLE_PROPERTY_CDMA_CDMA1X_REGISTRATION_STATE) || g_str_equal (key, MM_SIMPLE_PROPERTY_CDMA_EVDO_REGISTRATION_STATE) || g_str_equal (key, MM_SIMPLE_PROPERTY_CDMA_SID) || g_str_equal (key, MM_SIMPLE_PROPERTY_CDMA_NID)) { /* uint properties */ g_object_set (props, key, g_variant_get_uint32 (value), NULL); } else if (g_str_equal (key, MM_SIMPLE_PROPERTY_3GPP_OPERATOR_CODE) || g_str_equal (key, MM_SIMPLE_PROPERTY_3GPP_OPERATOR_NAME)) { /* string properties */ g_object_set (props, key, g_variant_get_string (value, NULL), NULL); } else if (g_str_equal (key, MM_SIMPLE_PROPERTY_CURRENT_BANDS) || g_str_equal (key, MM_SIMPLE_PROPERTY_SIGNAL_QUALITY)) { /* remaining complex types, as variant */ g_object_set (props, key, value, NULL); } else { /* Set inner error, will stop the loop */ inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS, "Invalid status dictionary, unexpected key '%s'", key); } g_free (key); g_variant_unref (value); } /* If error, destroy the object */ if (inner_error) { g_propagate_error (error, inner_error); return NULL; } return g_steal_pointer (&props); } /*****************************************************************************/ /** * mm_simple_status_new: (skip) */ MMSimpleStatus * mm_simple_status_new (void) { return (MM_SIMPLE_STATUS ( g_object_new (MM_TYPE_SIMPLE_STATUS, NULL))); } static void set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { MMSimpleStatus *self = MM_SIMPLE_STATUS (object); switch (prop_id) { case PROP_STATE: self->priv->state = g_value_get_enum (value); break; case PROP_SIGNAL_QUALITY: if (self->priv->signal_quality) g_variant_unref (self->priv->signal_quality); self->priv->signal_quality = g_value_dup_variant (value); break; case PROP_CURRENT_BANDS: if (self->priv->current_bands) g_variant_unref (self->priv->current_bands); if (self->priv->current_bands_array) { g_array_unref (self->priv->current_bands_array); self->priv->current_bands_array = NULL; } self->priv->current_bands = g_value_dup_variant (value); break; case PROP_ACCESS_TECHNOLOGIES: self->priv->access_technologies = g_value_get_flags (value); break; case PROP_3GPP_REGISTRATION_STATE: self->priv->modem_3gpp_registration_state = g_value_get_enum (value); break; case PROP_3GPP_OPERATOR_CODE: g_free (self->priv->modem_3gpp_operator_code); self->priv->modem_3gpp_operator_code = g_value_dup_string (value); break; case PROP_3GPP_OPERATOR_NAME: g_free (self->priv->modem_3gpp_operator_name); self->priv->modem_3gpp_operator_name = g_value_dup_string (value); break; case PROP_3GPP_SUBSCRIPTION_STATE: /* no-op */ break; case PROP_CDMA_CDMA1X_REGISTRATION_STATE: self->priv->modem_cdma_cdma1x_registration_state = g_value_get_enum (value); break; case PROP_CDMA_EVDO_REGISTRATION_STATE: self->priv->modem_cdma_evdo_registration_state = g_value_get_enum (value); break; case PROP_CDMA_SID: self->priv->modem_cdma_sid = g_value_get_uint (value); break; case PROP_CDMA_NID: self->priv->modem_cdma_nid = g_value_get_uint (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) { MMSimpleStatus *self = MM_SIMPLE_STATUS (object); switch (prop_id) { case PROP_STATE: g_value_set_enum (value, self->priv->state); break; case PROP_SIGNAL_QUALITY: g_value_set_variant (value, self->priv->signal_quality); break; case PROP_CURRENT_BANDS: g_value_set_variant (value, self->priv->current_bands); break; case PROP_ACCESS_TECHNOLOGIES: g_value_set_flags (value, self->priv->access_technologies); break; case PROP_3GPP_REGISTRATION_STATE: g_value_set_enum (value, self->priv->modem_3gpp_registration_state); break; case PROP_3GPP_OPERATOR_CODE: g_value_set_string (value, self->priv->modem_3gpp_operator_code); break; case PROP_3GPP_OPERATOR_NAME: g_value_set_string (value, self->priv->modem_3gpp_operator_name); break; case PROP_3GPP_SUBSCRIPTION_STATE: g_value_set_enum (value, MM_MODEM_3GPP_SUBSCRIPTION_STATE_UNKNOWN); break; case PROP_CDMA_CDMA1X_REGISTRATION_STATE: g_value_set_enum (value, self->priv->modem_cdma_cdma1x_registration_state); break; case PROP_CDMA_EVDO_REGISTRATION_STATE: g_value_set_enum (value, self->priv->modem_cdma_evdo_registration_state); break; case PROP_CDMA_SID: g_value_set_uint (value, self->priv->modem_cdma_sid); break; case PROP_CDMA_NID: g_value_set_uint (value, self->priv->modem_cdma_nid); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void mm_simple_status_init (MMSimpleStatus *self) { self->priv = G_TYPE_INSTANCE_GET_PRIVATE ((self), MM_TYPE_SIMPLE_STATUS, MMSimpleStatusPrivate); /* Some defaults */ self->priv->state = MM_MODEM_STATE_UNKNOWN; self->priv->access_technologies = MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN; self->priv->modem_3gpp_registration_state = MM_MODEM_3GPP_REGISTRATION_STATE_UNKNOWN; self->priv->current_bands = g_variant_ref_sink (mm_common_build_bands_unknown ()); self->priv->signal_quality = g_variant_ref_sink (g_variant_new ("(ub)", 0, 0)); self->priv->modem_cdma_cdma1x_registration_state = MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN; self->priv->modem_cdma_evdo_registration_state = MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN; self->priv->modem_cdma_sid = MM_MODEM_CDMA_SID_UNKNOWN; self->priv->modem_cdma_nid = MM_MODEM_CDMA_NID_UNKNOWN; } static void finalize (GObject *object) { MMSimpleStatus *self = MM_SIMPLE_STATUS (object); g_variant_unref (self->priv->signal_quality); g_variant_unref (self->priv->current_bands); if (self->priv->current_bands_array) g_array_unref (self->priv->current_bands_array); g_free (self->priv->modem_3gpp_operator_code); g_free (self->priv->modem_3gpp_operator_name); G_OBJECT_CLASS (mm_simple_status_parent_class)->finalize (object); } static void mm_simple_status_class_init (MMSimpleStatusClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); g_type_class_add_private (object_class, sizeof (MMSimpleStatusPrivate)); /* Virtual methods */ object_class->set_property = set_property; object_class->get_property = get_property; object_class->finalize = finalize; properties[PROP_STATE] = g_param_spec_enum (MM_SIMPLE_PROPERTY_STATE, "State", "State of the modem", MM_TYPE_MODEM_STATE, MM_MODEM_STATE_UNKNOWN, G_PARAM_READWRITE); g_object_class_install_property (object_class, PROP_STATE, properties[PROP_STATE]); properties[PROP_SIGNAL_QUALITY] = g_param_spec_variant (MM_SIMPLE_PROPERTY_SIGNAL_QUALITY, "Signal quality", "Signal quality reported by the modem", G_VARIANT_TYPE ("(ub)"), NULL, G_PARAM_READWRITE); g_object_class_install_property (object_class, PROP_SIGNAL_QUALITY, properties[PROP_SIGNAL_QUALITY]); properties[PROP_CURRENT_BANDS] = g_param_spec_variant (MM_SIMPLE_PROPERTY_CURRENT_BANDS, "Current Bands", "Frequency bands used by the modem", G_VARIANT_TYPE ("au"), NULL, G_PARAM_READWRITE); g_object_class_install_property (object_class, PROP_CURRENT_BANDS, properties[PROP_CURRENT_BANDS]); properties[PROP_ACCESS_TECHNOLOGIES] = g_param_spec_flags (MM_SIMPLE_PROPERTY_ACCESS_TECHNOLOGIES, "Access Technologies", "Access technologies used by the modem", MM_TYPE_MODEM_ACCESS_TECHNOLOGY, MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN, G_PARAM_READWRITE); g_object_class_install_property (object_class, PROP_ACCESS_TECHNOLOGIES, properties[PROP_ACCESS_TECHNOLOGIES]); properties[PROP_3GPP_REGISTRATION_STATE] = g_param_spec_enum (MM_SIMPLE_PROPERTY_3GPP_REGISTRATION_STATE, "3GPP registration state", "Registration state in the 3GPP network", MM_TYPE_MODEM_3GPP_REGISTRATION_STATE, MM_MODEM_3GPP_REGISTRATION_STATE_UNKNOWN, G_PARAM_READWRITE); g_object_class_install_property (object_class, PROP_3GPP_REGISTRATION_STATE, properties[PROP_3GPP_REGISTRATION_STATE]); properties[PROP_3GPP_OPERATOR_CODE] = g_param_spec_string (MM_SIMPLE_PROPERTY_3GPP_OPERATOR_CODE, "3GPP operator code", "Code of the current operator in the 3GPP network", NULL, G_PARAM_READWRITE); g_object_class_install_property (object_class, PROP_3GPP_OPERATOR_CODE, properties[PROP_3GPP_OPERATOR_CODE]); properties[PROP_3GPP_OPERATOR_NAME] = g_param_spec_string (MM_SIMPLE_PROPERTY_3GPP_OPERATOR_NAME, "3GPP operator name", "Name of the current operator in the 3GPP network", NULL, G_PARAM_READWRITE); g_object_class_install_property (object_class, PROP_3GPP_OPERATOR_NAME, properties[PROP_3GPP_OPERATOR_NAME]); properties[PROP_3GPP_SUBSCRIPTION_STATE] = g_param_spec_enum (MM_SIMPLE_PROPERTY_3GPP_SUBSCRIPTION_STATE, "3GPP subscription state", "Subscription state of the account (deprecated)", MM_TYPE_MODEM_3GPP_SUBSCRIPTION_STATE, MM_MODEM_3GPP_SUBSCRIPTION_STATE_UNKNOWN, G_PARAM_READWRITE); g_object_class_install_property (object_class, PROP_3GPP_SUBSCRIPTION_STATE, properties[PROP_3GPP_SUBSCRIPTION_STATE]); properties[PROP_CDMA_CDMA1X_REGISTRATION_STATE] = g_param_spec_enum (MM_SIMPLE_PROPERTY_CDMA_CDMA1X_REGISTRATION_STATE, "CDMA1x registration state", "Registration state in the CDMA1x network", MM_TYPE_MODEM_CDMA_REGISTRATION_STATE, MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN, G_PARAM_READWRITE); g_object_class_install_property (object_class, PROP_CDMA_CDMA1X_REGISTRATION_STATE, properties[PROP_CDMA_CDMA1X_REGISTRATION_STATE]); properties[PROP_CDMA_EVDO_REGISTRATION_STATE] = g_param_spec_enum (MM_SIMPLE_PROPERTY_CDMA_EVDO_REGISTRATION_STATE, "EV-DO registration state", "Registration state in the EV-DO network", MM_TYPE_MODEM_CDMA_REGISTRATION_STATE, MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN, G_PARAM_READWRITE); g_object_class_install_property (object_class, PROP_CDMA_EVDO_REGISTRATION_STATE, properties[PROP_CDMA_EVDO_REGISTRATION_STATE]); properties[PROP_CDMA_SID] = g_param_spec_uint (MM_SIMPLE_PROPERTY_CDMA_SID, "CDMA1x SID", "System Identifier of the serving CDMA1x network", 0, MM_MODEM_CDMA_SID_UNKNOWN, MM_MODEM_CDMA_SID_UNKNOWN, G_PARAM_READWRITE); g_object_class_install_property (object_class, PROP_CDMA_SID, properties[PROP_CDMA_SID]); properties[PROP_CDMA_NID] = g_param_spec_uint (MM_SIMPLE_PROPERTY_CDMA_NID, "CDMA1x NID", "Network Identifier of the serving CDMA1x network", 0, MM_MODEM_CDMA_NID_UNKNOWN, MM_MODEM_CDMA_NID_UNKNOWN, G_PARAM_READWRITE); g_object_class_install_property (object_class, PROP_CDMA_NID, properties[PROP_CDMA_NID]); } ModemManager-1.23.4-dev/libmm-glib/mm-simple-status.h000066400000000000000000000122241456466623000223330ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * libmm-glib -- Access modem status & information from glib applications * * 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 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 Street, Fifth Floor, * Boston, MA 02110-1301 USA. * * Copyright (C) 2011 Google, Inc. */ #ifndef MM_SIMPLE_STATUS_H #define MM_SIMPLE_STATUS_H #if !defined (__LIBMM_GLIB_H_INSIDE__) && !defined (LIBMM_GLIB_COMPILATION) #error "Only can be included directly." #endif #include #include G_BEGIN_DECLS #define MM_TYPE_SIMPLE_STATUS (mm_simple_status_get_type ()) #define MM_SIMPLE_STATUS(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_SIMPLE_STATUS, MMSimpleStatus)) #define MM_SIMPLE_STATUS_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_SIMPLE_STATUS, MMSimpleStatusClass)) #define MM_IS_SIMPLE_STATUS(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_SIMPLE_STATUS)) #define MM_IS_SIMPLE_STATUS_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_SIMPLE_STATUS)) #define MM_SIMPLE_STATUS_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_SIMPLE_STATUS, MMSimpleStatusClass)) typedef struct _MMSimpleStatus MMSimpleStatus; typedef struct _MMSimpleStatusClass MMSimpleStatusClass; typedef struct _MMSimpleStatusPrivate MMSimpleStatusPrivate; /** * MMSimpleStatus: * * The #MMSimpleStatus structure contains private data and should * only be accessed using the provided API. */ struct _MMSimpleStatus { /*< private >*/ GObject parent; MMSimpleStatusPrivate *priv; }; struct _MMSimpleStatusClass { /*< private >*/ GObjectClass parent; }; GType mm_simple_status_get_type (void); G_DEFINE_AUTOPTR_CLEANUP_FUNC (MMSimpleStatus, g_object_unref) MMModemState mm_simple_status_get_state (MMSimpleStatus *self); guint32 mm_simple_status_get_signal_quality (MMSimpleStatus *self, gboolean *recent); void mm_simple_status_get_current_bands (MMSimpleStatus *self, const MMModemBand **bands, guint *n_bands); MMModemAccessTechnology mm_simple_status_get_access_technologies (MMSimpleStatus *self); MMModem3gppRegistrationState mm_simple_status_get_3gpp_registration_state (MMSimpleStatus *self); const gchar *mm_simple_status_get_3gpp_operator_code (MMSimpleStatus *self); const gchar *mm_simple_status_get_3gpp_operator_name (MMSimpleStatus *self); MMModemCdmaRegistrationState mm_simple_status_get_cdma_cdma1x_registration_state (MMSimpleStatus *self); MMModemCdmaRegistrationState mm_simple_status_get_cdma_evdo_registration_state (MMSimpleStatus *self); guint mm_simple_status_get_cdma_sid (MMSimpleStatus *self); guint mm_simple_status_get_cdma_nid (MMSimpleStatus *self); /*****************************************************************************/ /* ModemManager/libmm-glib/mmcli specific methods */ #if defined (_LIBMM_INSIDE_MM) || \ defined (_LIBMM_INSIDE_MMCLI) || \ defined (LIBMM_GLIB_COMPILATION) #define MM_SIMPLE_PROPERTY_STATE "state" #define MM_SIMPLE_PROPERTY_SIGNAL_QUALITY "signal-quality" #define MM_SIMPLE_PROPERTY_CURRENT_BANDS "current-bands" #define MM_SIMPLE_PROPERTY_ACCESS_TECHNOLOGIES "access-technologies" #define MM_SIMPLE_PROPERTY_3GPP_REGISTRATION_STATE "m3gpp-registration-state" #define MM_SIMPLE_PROPERTY_3GPP_OPERATOR_CODE "m3gpp-operator-code" #define MM_SIMPLE_PROPERTY_3GPP_OPERATOR_NAME "m3gpp-operator-name" #define MM_SIMPLE_PROPERTY_3GPP_SUBSCRIPTION_STATE "m3gpp-subscription-state" #define MM_SIMPLE_PROPERTY_CDMA_CDMA1X_REGISTRATION_STATE "cdma-cdma1x-registration-state" #define MM_SIMPLE_PROPERTY_CDMA_EVDO_REGISTRATION_STATE "cdma-evdo-registration-state" #define MM_SIMPLE_PROPERTY_CDMA_SID "cdma-sid" #define MM_SIMPLE_PROPERTY_CDMA_NID "cdma-nid" MMSimpleStatus *mm_simple_status_new (void); MMSimpleStatus *mm_simple_status_new_from_dictionary (GVariant *dictionary, GError **error); GVariant *mm_simple_status_get_dictionary (MMSimpleStatus *self); #endif G_END_DECLS #endif /* MM_SIMPLE_STATUS_H */ ModemManager-1.23.4-dev/libmm-glib/mm-sms-properties.c000066400000000000000000000653131456466623000225170ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * libmm-glib -- Access modem status & information from glib applications * * 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 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 Street, Fifth Floor, * Boston, MA 02110-1301 USA. * * Copyright (C) 2011 Aleksander Morgado */ #include #include #include #include "mm-errors-types.h" #include "mm-enums-types.h" #include "mm-flags-types.h" #include "mm-common-helpers.h" #include "mm-sms-properties.h" /** * SECTION: mm-sms-properties * @title: MMSmsProperties * @short_description: Helper object to handle SMS properties. * * The #MMSmsProperties is an object handling the properties to be set * in newly created SMS objects. * * This object is created by the user and passed to ModemManager with either * mm_modem_messaging_create() or mm_modem_messaging_create_sync(). */ G_DEFINE_TYPE (MMSmsProperties, mm_sms_properties, G_TYPE_OBJECT) #define PROPERTY_TEXT "text" #define PROPERTY_DATA "data" #define PROPERTY_NUMBER "number" #define PROPERTY_SMSC "smsc" #define PROPERTY_VALIDITY "validity" #define PROPERTY_CLASS "class" #define PROPERTY_DELIVERY_REPORT_REQUEST "delivery-report-request" #define PROPERTY_TELESERVICE_ID "teleservice-id" #define PROPERTY_SERVICE_CATEGORY "service-category" struct _MMSmsPropertiesPrivate { gchar *text; GByteArray *data; gchar *number; gchar *smsc; MMSmsValidityType validity_type; guint validity_relative; gint class; gboolean delivery_report_request_set; gboolean delivery_report_request; MMSmsCdmaTeleserviceId teleservice_id; MMSmsCdmaServiceCategory service_category; }; /*****************************************************************************/ /** * mm_sms_properties_set_text: * @self: A #MMSmsProperties. * @text: The text to set, in UTF-8. * * Sets the message text. * * Since: 1.0 */ void mm_sms_properties_set_text (MMSmsProperties *self, const gchar *text) { g_return_if_fail (MM_IS_SMS_PROPERTIES (self)); g_free (self->priv->text); self->priv->text = g_strdup (text); } /** * mm_sms_properties_get_text: * @self: A #MMSmsProperties. * * Gets the message text, in UTF-8. * * Returns: (transfer none): The message text, or %NULL if it doesn't contain * any (e.g. contains data instead). Do not free the returned value, it is * owned by @self. * * Since: 1.0 */ const gchar * mm_sms_properties_get_text (MMSmsProperties *self) { g_return_val_if_fail (MM_IS_SMS_PROPERTIES (self), NULL); return self->priv->text; } /*****************************************************************************/ /** * mm_sms_properties_set_data: * @self: A #MMSmsProperties. * @data: The data to set. * @data_length: Length of @data. * * Sets the message data. * * Since: 1.0 */ void mm_sms_properties_set_data (MMSmsProperties *self, const guint8 *data, gsize data_length) { g_return_if_fail (MM_IS_SMS_PROPERTIES (self)); if (self->priv->data) g_byte_array_unref (self->priv->data); if (data && data_length) self->priv->data = g_byte_array_append (g_byte_array_sized_new (data_length), data, data_length); else self->priv->data = NULL; } /** * mm_sms_properties_set_data_bytearray: * @self: A #MMSmsProperties. * @data: A #GByteArray with the data to set. This method takes a new reference * of @data. * * Sets the message data. * * Since: 1.0 */ void mm_sms_properties_set_data_bytearray (MMSmsProperties *self, GByteArray *data) { g_return_if_fail (MM_IS_SMS_PROPERTIES (self)); if (self->priv->data) g_byte_array_unref (self->priv->data); self->priv->data = (data ? g_byte_array_ref (data) : NULL); } /** * mm_sms_properties_get_data: * @self: A #MMSmsProperties. * @data_len: (out): Size of the output data, if any given. * * Gets the message data. * * Returns: (transfer none): The message data, or %NULL if it doesn't contain * any (e.g. contains text instead). * * Since: 1.0 */ const guint8 * mm_sms_properties_get_data (MMSmsProperties *self, gsize *data_len) { g_return_val_if_fail (MM_IS_SMS_PROPERTIES (self), NULL); if (data_len) *data_len = (self->priv->data ? self->priv->data->len : 0); return (self->priv->data ? self->priv->data->data : NULL); } /** * mm_sms_properties_peek_data_bytearray: * @self: A #MMSmsProperties. * * Gets the message data. * * Returns: (transfer none): A #GByteArray with the message data, or %NULL if it * doesn't contain any (e.g. contains text instead). Do not free the returned * value, it is owned by @self. * * Since: 1.0 */ GByteArray * mm_sms_properties_peek_data_bytearray (MMSmsProperties *self) { g_return_val_if_fail (MM_IS_SMS_PROPERTIES (self), NULL); return self->priv->data; } /** * mm_sms_properties_get_data_bytearray: * @self: A #MMSmsProperties. * * Gets the message data. * * Returns: (transfer full): A #GByteArray with the message data, or %NULL if it * doesn't contain any (e.g. contains text instead). The returned value should * be freed with g_byte_array_unref(). * * Since: 1.0 */ GByteArray * mm_sms_properties_get_data_bytearray (MMSmsProperties *self) { g_return_val_if_fail (MM_IS_SMS_PROPERTIES (self), NULL); return (self->priv->data ? g_byte_array_ref (self->priv->data) : NULL); } /*****************************************************************************/ /** * mm_sms_properties_set_number: * @self: A #MMSmsProperties. * @number: The number. * * Sets the number to which the message is addressed. * * Since: 1.0 */ void mm_sms_properties_set_number (MMSmsProperties *self, const gchar *number) { g_return_if_fail (MM_IS_SMS_PROPERTIES (self)); g_free (self->priv->number); self->priv->number = g_strdup (number); } /** * mm_sms_properties_get_number: * @self: A #MMSmsProperties. * * Gets the number to which the message is addressed. * * Returns: (transfer none): The number, or %NULL if it couldn't be retrieved. * Do not free the returned value, it is owned by @self. * * Since: 1.0 */ const gchar * mm_sms_properties_get_number (MMSmsProperties *self) { g_return_val_if_fail (MM_IS_SMS_PROPERTIES (self), NULL); return self->priv->number; } /*****************************************************************************/ /** * mm_sms_properties_set_smsc: * @self: A #MMSmsProperties. * @smsc: The SMSC number. * * Sets the SMS service center number. * * Since: 1.0 */ void mm_sms_properties_set_smsc (MMSmsProperties *self, const gchar *smsc) { g_return_if_fail (MM_IS_SMS_PROPERTIES (self)); g_free (self->priv->smsc); self->priv->smsc = g_strdup (smsc); } /** * mm_sms_properties_get_smsc: * @self: A #MMSmsProperties. * * Gets the SMS service center number. * * Returns: (transfer none): The number of the SMSC, or %NULL if it couldn't be * retrieved. Do not free the returned value, it is owned by @self. * * Since: 1.0 */ const gchar * mm_sms_properties_get_smsc (MMSmsProperties *self) { g_return_val_if_fail (MM_IS_SMS_PROPERTIES (self), NULL); return self->priv->smsc; } /*****************************************************************************/ /** * mm_sms_properties_set_validity_relative: * @self: A #MMSmsProperties. * @validity: The validity of %MM_SMS_VALIDITY_TYPE_RELATIVE type. * * Sets the relative validity time of the SMS. Validity time is in minutes. * If relative validity time is not set, the default is 24 hours. * * Since: 1.0 */ void mm_sms_properties_set_validity_relative (MMSmsProperties *self, guint validity) { g_return_if_fail (MM_IS_SMS_PROPERTIES (self)); self->priv->validity_type = MM_SMS_VALIDITY_TYPE_RELATIVE; self->priv->validity_relative = validity; } /** * mm_sms_properties_get_validity_type: * @self: A #MMSmsProperties. * * Gets the relative validity type the SMS. * * Returns: a #MMSmsValidityType. * * Since: 1.0 */ MMSmsValidityType mm_sms_properties_get_validity_type (MMSmsProperties *self) { g_return_val_if_fail (MM_IS_SMS_PROPERTIES (self), 0); return self->priv->validity_type; } /** * mm_sms_properties_get_validity_relative: * @self: A #MMSmsProperties. * * Gets the relative validity time of the SMS. * * Returns: the validity time or 0 if unknown. * * Since: 1.0 */ guint mm_sms_properties_get_validity_relative (MMSmsProperties *self) { g_return_val_if_fail (MM_IS_SMS_PROPERTIES (self), 0); g_return_val_if_fail (self->priv->validity_type == MM_SMS_VALIDITY_TYPE_RELATIVE, 0); return self->priv->validity_relative; } /*****************************************************************************/ /** * mm_sms_properties_set_class: * @self: A #MMSmsProperties. * @message_class: The message class (0..3), or -1 for invalid/unset class. * * Sets the 3GPP message class of the SMS. * * Since: 1.0 */ void mm_sms_properties_set_class (MMSmsProperties *self, gint message_class) { g_return_if_fail (MM_IS_SMS_PROPERTIES (self)); g_return_if_fail (message_class >= -1 && message_class <= 3); self->priv->class = message_class; } /** * mm_sms_properties_get_class: * @self: A #MMSmsProperties. * * Gets the 3GPP message class of the SMS. * * Returns: the message class, or -1 for invalid/unset class. * * Since: 1.0 */ gint mm_sms_properties_get_class (MMSmsProperties *self) { g_return_val_if_fail (MM_IS_SMS_PROPERTIES (self), -1); return self->priv->class; } /*****************************************************************************/ /** * mm_sms_properties_set_delivery_report_request: * @self: A #MMSmsProperties. * @request: %TRUE if delivery report is requested, %FALSE otherwise. * * Sets whether delivery report is requested for the SMS. * * Since: 1.0 */ void mm_sms_properties_set_delivery_report_request (MMSmsProperties *self, gboolean request) { g_return_if_fail (MM_IS_SMS_PROPERTIES (self)); self->priv->delivery_report_request_set = TRUE; self->priv->delivery_report_request = request; } /** * mm_sms_properties_get_delivery_report_request: * @self: A #MMSmsProperties. * * Checks whether delivery report is requested for the SMS. * * Returns: %TRUE if delivery report is requested, %FALSE otherwise. * * Since: 1.0 */ gboolean mm_sms_properties_get_delivery_report_request (MMSmsProperties *self) { g_return_val_if_fail (MM_IS_SMS_PROPERTIES (self), FALSE); return self->priv->delivery_report_request; } /*****************************************************************************/ /** * mm_sms_properties_set_teleservice_id: * @self: A #MMSmsProperties. * @teleservice_id: The CDMA teleservice ID. * * Sets the CDMA teleservice ID of the SMS. * * Since: 1.2 */ void mm_sms_properties_set_teleservice_id (MMSmsProperties *self, MMSmsCdmaTeleserviceId teleservice_id) { g_return_if_fail (MM_IS_SMS_PROPERTIES (self)); self->priv->teleservice_id = teleservice_id; } /** * mm_sms_properties_get_teleservice_id: * @self: A #MMSmsProperties. * * Gets the CDMA teleservice ID of the SMS. * * Returns: the CDMA teleservice ID. * * Since: 1.2 */ MMSmsCdmaTeleserviceId mm_sms_properties_get_teleservice_id (MMSmsProperties *self) { g_return_val_if_fail (MM_IS_SMS_PROPERTIES (self), MM_SMS_CDMA_TELESERVICE_ID_UNKNOWN); return self->priv->teleservice_id; } /*****************************************************************************/ /** * mm_sms_properties_set_service_category: * @self: A #MMSmsProperties. * @service_category: The CDMA service category. * * Sets the CDMA service category of the SMS. * * Since: 1.2 */ void mm_sms_properties_set_service_category (MMSmsProperties *self, MMSmsCdmaServiceCategory service_category) { g_return_if_fail (MM_IS_SMS_PROPERTIES (self)); self->priv->service_category = service_category; } /** * mm_sms_properties_get_service_category: * @self: A #MMSmsProperties. * * Gets the CDMA message service category of the SMS. * * Returns: the CDMA service category. * * Since: 1.2 */ MMSmsCdmaServiceCategory mm_sms_properties_get_service_category (MMSmsProperties *self) { g_return_val_if_fail (MM_IS_SMS_PROPERTIES (self), MM_SMS_CDMA_SERVICE_CATEGORY_UNKNOWN); return self->priv->service_category; } /*****************************************************************************/ /** * mm_sms_properties_get_dictionary: (skip) */ GVariant * mm_sms_properties_get_dictionary (MMSmsProperties *self) { GVariantBuilder builder; /* We do allow NULL */ if (!self) return NULL; g_return_val_if_fail (MM_IS_SMS_PROPERTIES (self), NULL); g_variant_builder_init (&builder, G_VARIANT_TYPE ("a{sv}")); if (self->priv->text) g_variant_builder_add (&builder, "{sv}", PROPERTY_TEXT, g_variant_new_string (self->priv->text)); if (self->priv->data) g_variant_builder_add ( &builder, "{sv}", PROPERTY_DATA, g_variant_new_from_data (G_VARIANT_TYPE ("ay"), self->priv->data->data, self->priv->data->len * sizeof (guint8), TRUE, NULL, NULL)); if (self->priv->number) g_variant_builder_add (&builder, "{sv}", PROPERTY_NUMBER, g_variant_new_string (self->priv->number)); if (self->priv->smsc) g_variant_builder_add (&builder, "{sv}", PROPERTY_SMSC, g_variant_new_string (self->priv->smsc)); if (self->priv->validity_type == MM_SMS_VALIDITY_TYPE_RELATIVE) g_variant_builder_add (&builder, "{sv}", PROPERTY_VALIDITY, g_variant_new ("(uv)", MM_SMS_VALIDITY_TYPE_RELATIVE, g_variant_new_uint32 (self->priv->validity_relative))); if (self->priv->class >= 0) g_variant_builder_add (&builder, "{sv}", PROPERTY_CLASS, g_variant_new_int32 (self->priv->class)); if (self->priv->delivery_report_request_set) g_variant_builder_add (&builder, "{sv}", PROPERTY_DELIVERY_REPORT_REQUEST, g_variant_new_boolean (self->priv->delivery_report_request)); if (self->priv->teleservice_id != MM_SMS_CDMA_TELESERVICE_ID_UNKNOWN) g_variant_builder_add (&builder, "{sv}", PROPERTY_TELESERVICE_ID, g_variant_new_uint32 (self->priv->teleservice_id)); if (self->priv->service_category != MM_SMS_CDMA_SERVICE_CATEGORY_UNKNOWN) g_variant_builder_add (&builder, "{sv}", PROPERTY_SERVICE_CATEGORY, g_variant_new_uint32 (self->priv->service_category)); return g_variant_ref_sink (g_variant_builder_end (&builder)); } /*****************************************************************************/ static guint parse_uint (const gchar *str, GError **error) { guint num; errno = 0; num = strtoul (str, NULL, 10); if ((num < G_MAXUINT32) && (errno == 0)) return num; g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS, "Invalid properties string, cannot parse '%s' as uint", str); return 0; } static gboolean parse_boolean (const gchar *str, GError **error) { if (g_ascii_strcasecmp (str, "yes") == 0 || g_ascii_strcasecmp (str, "true") == 0 || g_str_equal (str, "1")) return TRUE; if (g_ascii_strcasecmp (str, "no") == 0 || g_ascii_strcasecmp (str, "false") == 0 || g_str_equal (str, "0")) return FALSE; g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS, "Invalid properties string, cannot parse '%s' as boolean", str); return FALSE; } static gboolean consume_string (MMSmsProperties *self, const gchar *key, const gchar *value, GError **error) { if (g_str_equal (key, PROPERTY_TEXT)) mm_sms_properties_set_text (self, value); else if (g_str_equal (key, PROPERTY_NUMBER)) mm_sms_properties_set_number (self, value); else if (g_str_equal (key, PROPERTY_SMSC)) mm_sms_properties_set_smsc (self, value); else if (g_str_equal (key, PROPERTY_VALIDITY)) { GError *inner_error = NULL; guint n; n = parse_uint (value, &inner_error); if (inner_error) { g_propagate_error (error, inner_error); return FALSE; } mm_sms_properties_set_validity_relative (self, n); } else if (g_str_equal (key, PROPERTY_CLASS)) { gint n = 0; if (!mm_get_int_from_str (value, &n)) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS, "Invalid properties string, cannot parse '%s' as int", value); return FALSE; } mm_sms_properties_set_class (self, n); } else if (g_str_equal (key, PROPERTY_DELIVERY_REPORT_REQUEST)) { GError *inner_error = NULL; gboolean request; request = parse_boolean (value, &inner_error); if (inner_error) { g_propagate_error (error, inner_error); return FALSE; } mm_sms_properties_set_delivery_report_request (self, request); } else if (g_str_equal (key, PROPERTY_TELESERVICE_ID)) { MMSmsCdmaTeleserviceId teleservice_id; GError *inner_error = NULL; teleservice_id = mm_common_get_sms_cdma_teleservice_id_from_string (value, &inner_error); if (inner_error) { g_propagate_error (error, inner_error); return FALSE; } mm_sms_properties_set_teleservice_id (self, teleservice_id); } else if (g_str_equal (key, PROPERTY_SERVICE_CATEGORY)) { MMSmsCdmaServiceCategory service_category; GError *inner_error = NULL; service_category = mm_common_get_sms_cdma_service_category_from_string (value, &inner_error); if (inner_error) { g_propagate_error (error, inner_error); return FALSE; } mm_sms_properties_set_service_category (self, service_category); } else if (g_str_equal (key, PROPERTY_DATA)) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS, "Invalid properties string, key '%s' cannot be given in a string", key); return FALSE; } else { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS, "Invalid properties string, unexpected key '%s'", key); return FALSE; } return TRUE; } typedef struct { MMSmsProperties *properties; GError *error; } ParseKeyValueContext; static gboolean key_value_foreach (const gchar *key, const gchar *value, ParseKeyValueContext *ctx) { return consume_string (ctx->properties, key, value, &ctx->error); } /** * mm_sms_properties_new_from_string: (skip) */ MMSmsProperties * mm_sms_properties_new_from_string (const gchar *str, GError **error) { ParseKeyValueContext ctx; ctx.properties = mm_sms_properties_new (); ctx.error = NULL; mm_common_parse_key_value_string (str, &ctx.error, (MMParseKeyValueForeachFn)key_value_foreach, &ctx); /* If error, destroy the object */ if (ctx.error) { g_propagate_error (error, ctx.error); g_object_unref (ctx.properties); ctx.properties = NULL; } return ctx.properties; } /*****************************************************************************/ static gboolean consume_variant (MMSmsProperties *properties, const gchar *key, GVariant *value, GError **error) { if (g_str_equal (key, PROPERTY_TEXT)) mm_sms_properties_set_text ( properties, g_variant_get_string (value, NULL)); else if (g_str_equal (key, PROPERTY_DATA)) { const guint8 *data; gsize data_len = 0; data = g_variant_get_fixed_array (value, &data_len, sizeof (guint8)); mm_sms_properties_set_data ( properties, data, data_len); } else if (g_str_equal (key, PROPERTY_NUMBER)) mm_sms_properties_set_number ( properties, g_variant_get_string (value, NULL)); else if (g_str_equal (key, PROPERTY_SMSC)) mm_sms_properties_set_smsc ( properties, g_variant_get_string (value, NULL)); else if (g_str_equal (key, PROPERTY_VALIDITY)) { guint type; GVariant *val; g_variant_get (value, "(uv)", &type, &val); if (type == MM_SMS_VALIDITY_TYPE_RELATIVE) { mm_sms_properties_set_validity_relative ( properties, g_variant_get_uint32 (val)); } else if (type != MM_SMS_VALIDITY_TYPE_UNKNOWN) g_warning ("SMS validity type '%s' not supported yet", mm_sms_validity_type_get_string (type)); g_variant_unref (val); } else if (g_str_equal (key, PROPERTY_CLASS)) mm_sms_properties_set_class ( properties, g_variant_get_int32 (value)); else if (g_str_equal (key, PROPERTY_DELIVERY_REPORT_REQUEST)) mm_sms_properties_set_delivery_report_request ( properties, g_variant_get_boolean (value)); else if (g_str_equal (key, PROPERTY_TELESERVICE_ID)) mm_sms_properties_set_teleservice_id ( properties, g_variant_get_uint32 (value)); else if (g_str_equal (key, PROPERTY_SERVICE_CATEGORY)) mm_sms_properties_set_service_category ( properties, g_variant_get_uint32 (value)); else { /* Set error */ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS, "Invalid properties dictionary, unexpected key '%s'", key); return FALSE; } return TRUE; } /** * mm_sms_properties_new_from_dictionary: (skip) */ MMSmsProperties * mm_sms_properties_new_from_dictionary (GVariant *dictionary, GError **error) { GError *inner_error = NULL; GVariantIter iter; gchar *key; GVariant *value; MMSmsProperties *properties; properties = mm_sms_properties_new (); if (!dictionary) return properties; if (!g_variant_is_of_type (dictionary, G_VARIANT_TYPE ("a{sv}"))) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS, "Cannot create SMS properties from dictionary: " "invalid variant type received"); g_object_unref (properties); return NULL; } g_variant_iter_init (&iter, dictionary); while (!inner_error && g_variant_iter_next (&iter, "{sv}", &key, &value)) { consume_variant (properties, key, value, &inner_error); g_free (key); g_variant_unref (value); } /* If error, destroy the object */ if (inner_error) { g_propagate_error (error, inner_error); g_object_unref (properties); properties = NULL; } return properties; } /*****************************************************************************/ /** * mm_sms_properties_new: * * Creates a new empty #MMSmsProperties. * * Returns: (transfer full): a #MMSmsProperties. The returned value should be freed with g_object_unref(). * * Since: 1.0 */ MMSmsProperties * mm_sms_properties_new (void) { return (MM_SMS_PROPERTIES (g_object_new (MM_TYPE_SMS_PROPERTIES, NULL))); } static void mm_sms_properties_init (MMSmsProperties *self) { self->priv = G_TYPE_INSTANCE_GET_PRIVATE ((self), MM_TYPE_SMS_PROPERTIES, MMSmsPropertiesPrivate); self->priv->validity_type = MM_SMS_VALIDITY_TYPE_UNKNOWN; self->priv->class = -1; self->priv->teleservice_id = MM_SMS_CDMA_TELESERVICE_ID_UNKNOWN; self->priv->service_category = MM_SMS_CDMA_SERVICE_CATEGORY_UNKNOWN; } static void finalize (GObject *object) { MMSmsProperties *self = MM_SMS_PROPERTIES (object); g_free (self->priv->text); g_free (self->priv->number); g_free (self->priv->smsc); if (self->priv->data) g_byte_array_unref (self->priv->data); G_OBJECT_CLASS (mm_sms_properties_parent_class)->finalize (object); } static void mm_sms_properties_class_init (MMSmsPropertiesClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); g_type_class_add_private (object_class, sizeof (MMSmsPropertiesPrivate)); object_class->finalize = finalize; } ModemManager-1.23.4-dev/libmm-glib/mm-sms-properties.h000066400000000000000000000133501456466623000225160ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * libmm-glib -- Access modem status & information from glib applications * * 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 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 Street, Fifth Floor, * Boston, MA 02110-1301 USA. * * Copyright (C) 2012 Google, Inc. */ #ifndef MM_SMS_PROPERTIES_H #define MM_SMS_PROPERTIES_H #if !defined (__LIBMM_GLIB_H_INSIDE__) && !defined (LIBMM_GLIB_COMPILATION) #error "Only can be included directly." #endif #include #include G_BEGIN_DECLS #define MM_TYPE_SMS_PROPERTIES (mm_sms_properties_get_type ()) #define MM_SMS_PROPERTIES(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_SMS_PROPERTIES, MMSmsProperties)) #define MM_SMS_PROPERTIES_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_SMS_PROPERTIES, MMSmsPropertiesClass)) #define MM_IS_SMS_PROPERTIES(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_SMS_PROPERTIES)) #define MM_IS_SMS_PROPERTIES_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_SMS_PROPERTIES)) #define MM_SMS_PROPERTIES_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_SMS_PROPERTIES, MMSmsPropertiesClass)) typedef struct _MMSmsProperties MMSmsProperties; typedef struct _MMSmsPropertiesClass MMSmsPropertiesClass; typedef struct _MMSmsPropertiesPrivate MMSmsPropertiesPrivate; /** * MMSmsProperties: * * The #MMSmsProperties structure contains private data and should only be * accessed using the provided API. */ struct _MMSmsProperties { /*< private >*/ GObject parent; MMSmsPropertiesPrivate *priv; }; struct _MMSmsPropertiesClass { /*< private >*/ GObjectClass parent; }; GType mm_sms_properties_get_type (void); G_DEFINE_AUTOPTR_CLEANUP_FUNC (MMSmsProperties, g_object_unref) MMSmsProperties *mm_sms_properties_new (void); void mm_sms_properties_set_text (MMSmsProperties *self, const gchar *text); void mm_sms_properties_set_data (MMSmsProperties *self, const guint8 *data, gsize data_length); void mm_sms_properties_set_data_bytearray (MMSmsProperties *self, GByteArray *data); void mm_sms_properties_set_number (MMSmsProperties *self, const gchar *number); void mm_sms_properties_set_smsc (MMSmsProperties *self, const gchar *smsc); void mm_sms_properties_set_validity_relative (MMSmsProperties *self, guint validity); void mm_sms_properties_set_class (MMSmsProperties *self, gint message_class); void mm_sms_properties_set_delivery_report_request (MMSmsProperties *self, gboolean request); void mm_sms_properties_set_teleservice_id (MMSmsProperties *self, MMSmsCdmaTeleserviceId teleservice_id); void mm_sms_properties_set_service_category (MMSmsProperties *self, MMSmsCdmaServiceCategory service_category); const gchar *mm_sms_properties_get_text (MMSmsProperties *self); const guint8 *mm_sms_properties_get_data (MMSmsProperties *self, gsize *data_len); GByteArray *mm_sms_properties_peek_data_bytearray (MMSmsProperties *self); GByteArray *mm_sms_properties_get_data_bytearray (MMSmsProperties *self); const gchar *mm_sms_properties_get_number (MMSmsProperties *self); const gchar *mm_sms_properties_get_smsc (MMSmsProperties *self); MMSmsValidityType mm_sms_properties_get_validity_type (MMSmsProperties *self); guint mm_sms_properties_get_validity_relative (MMSmsProperties *self); gint mm_sms_properties_get_class (MMSmsProperties *self); gboolean mm_sms_properties_get_delivery_report_request (MMSmsProperties *self); MMSmsCdmaTeleserviceId mm_sms_properties_get_teleservice_id (MMSmsProperties *self); MMSmsCdmaServiceCategory mm_sms_properties_get_service_category (MMSmsProperties *self); /*****************************************************************************/ /* ModemManager/libmm-glib/mmcli specific methods */ #if defined (_LIBMM_INSIDE_MM) || \ defined (_LIBMM_INSIDE_MMCLI) || \ defined (LIBMM_GLIB_COMPILATION) MMSmsProperties *mm_sms_properties_new_from_string (const gchar *str, GError **error); MMSmsProperties *mm_sms_properties_new_from_dictionary (GVariant *dictionary, GError **error); GVariant *mm_sms_properties_get_dictionary (MMSmsProperties *self); #endif G_END_DECLS #endif /* MM_SMS_PROPERTIES_H */ ModemManager-1.23.4-dev/libmm-glib/mm-sms.c000066400000000000000000000536361456466623000203320ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * libmm-glib -- Access modem status & information from glib applications * * 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 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 Street, Fifth Floor, * Boston, MA 02110-1301 USA. * * Copyright (C) 2012 Google, Inc. */ #include "string.h" #include "mm-helpers.h" #include "mm-sms.h" #include "mm-modem.h" /** * SECTION: mm-sms * @title: MMSms * @short_description: The SMS interface * * The #MMSms is an object providing access to the methods, signals and * properties of the SMS interface. * * When the SMS is exposed and available in the bus, it is ensured that at * least this interface is also available. */ G_DEFINE_TYPE (MMSms, mm_sms, MM_GDBUS_TYPE_SMS_PROXY) /*****************************************************************************/ /** * mm_sms_get_path: * @self: A #MMSms. * * Gets the DBus path of the #MMSms object. * * Returns: (transfer none): The DBus path of the #MMSms object. * * Since: 1.0 */ const gchar * mm_sms_get_path (MMSms *self) { g_return_val_if_fail (MM_IS_SMS (self), NULL); RETURN_NON_EMPTY_CONSTANT_STRING ( g_dbus_proxy_get_object_path (G_DBUS_PROXY (self))); } /** * mm_sms_dup_path: * @self: A #MMSms. * * Gets a copy of the DBus path of the #MMSms object. * * Returns: (transfer full): The DBus path of the #MMSms object. The returned * value should be freed with g_free(). * * Since: 1.0 */ gchar * mm_sms_dup_path (MMSms *self) { gchar *value; g_return_val_if_fail (MM_IS_SMS (self), NULL); g_object_get (G_OBJECT (self), "g-object-path", &value, NULL); RETURN_NON_EMPTY_STRING (value); } /*****************************************************************************/ /** * mm_sms_get_text: * @self: A #MMSms. * * Gets the message text, in UTF-8. * * The returned value is only valid until the property changes so it is * only safe to use this function on the thread where @self was constructed. Use * mm_sms_dup_text() if on another thread. * * Returns: (transfer none): The message text, or %NULL if it doesn't contain * any (e.g. contains data instead). * * Since: 1.0 */ const gchar * mm_sms_get_text (MMSms *self) { g_return_val_if_fail (MM_IS_SMS (self), NULL); RETURN_NON_EMPTY_CONSTANT_STRING ( mm_gdbus_sms_get_text (MM_GDBUS_SMS (self))); } /** * mm_sms_dup_text: * @self: A #MMSms. * * Gets the message text, in UTF-8. * * Returns: (transfer full): The message text, or %NULL if it doesn't contain * any (e.g. contains data instead). The returned value should be freed with * g_free(). * * Since: 1.0 */ gchar * mm_sms_dup_text (MMSms *self) { g_return_val_if_fail (MM_IS_SMS (self), NULL); RETURN_NON_EMPTY_STRING ( mm_gdbus_sms_dup_text (MM_GDBUS_SMS (self))); } /*****************************************************************************/ /** * mm_sms_get_data: * @self: A #MMSms. * @data_len: (out): Size of the output data, if any given. * * Gets the message data. * * Returns: (transfer none) (array length=data_len) (element-type guint8): The * message data, or %NULL if it doesn't contain any (e.g. contains text * instead). * * Since: 1.0 */ const guint8 * mm_sms_get_data (MMSms *self, gsize *data_len) { GVariant *data; g_return_val_if_fail (MM_IS_SMS (self), NULL); g_return_val_if_fail (data_len != NULL, NULL); data = mm_gdbus_sms_get_data (MM_GDBUS_SMS (self)); return (data ? g_variant_get_fixed_array (data, data_len, sizeof (guint8)) : NULL); } /** * mm_sms_dup_data: * @self: A #MMSms. * @data_len: (out): Size of the output data, if any given. * * Gets the message data. * * Returns: (transfer full) (array length=data_len) (element-type guint8): The * message data, or %NULL if it doesn't contain any (e.g. contains text * instead). The returned value should be freed with g_free(). * * Since: 1.0 */ guint8 * mm_sms_dup_data (MMSms *self, gsize *data_len) { g_autoptr(GVariant) data = NULL; g_return_val_if_fail (MM_IS_SMS (self), NULL); g_return_val_if_fail (data_len != NULL, NULL); /* Get a ref to ensure the variant is valid as long as we use it */ data = mm_gdbus_sms_dup_data (MM_GDBUS_SMS (self)); return (data ? g_memdup (g_variant_get_fixed_array (data, data_len, sizeof (guint8)), *data_len) : NULL); } /*****************************************************************************/ /** * mm_sms_get_number: * @self: A #MMSms. * * Gets the number to which the message is addressed. * * The returned value is only valid until the property changes so it is * only safe to use this function on the thread where @self was constructed. Use * mm_sms_dup_number() if on another thread. * * Returns: (transfer none): The number, or %NULL if it couldn't be retrieved. * * Since: 1.0 */ const gchar * mm_sms_get_number (MMSms *self) { g_return_val_if_fail (MM_IS_SMS (self), NULL); RETURN_NON_EMPTY_CONSTANT_STRING ( mm_gdbus_sms_get_number (MM_GDBUS_SMS (self))); } /** * mm_sms_dup_number: * @self: A #MMSms. * * Gets the number to which the message is addressed. * * Returns: (transfer full): The number, or %NULL if it couldn't be retrieved. * The returned value should be freed with g_free(). * * Since: 1.0 */ gchar * mm_sms_dup_number (MMSms *self) { g_return_val_if_fail (MM_IS_SMS (self), NULL); RETURN_NON_EMPTY_STRING ( mm_gdbus_sms_dup_number (MM_GDBUS_SMS (self))); } /*****************************************************************************/ /** * mm_sms_get_smsc: * @self: A #MMSms. * * Gets the SMS service center number. * * The returned value is only valid until the property changes so it is * only safe to use this function on the thread where @self was constructed. Use * mm_sms_dup_smsc() if on another thread. * * Returns: (transfer none): The number of the SMSC, or %NULL if it couldn't be * retrieved. * * Since: 1.0 */ const gchar * mm_sms_get_smsc (MMSms *self) { g_return_val_if_fail (MM_IS_SMS (self), NULL); RETURN_NON_EMPTY_CONSTANT_STRING ( mm_gdbus_sms_get_smsc (MM_GDBUS_SMS (self))); } /** * mm_sms_dup_smsc: * @self: A #MMSms. * * Gets the SMS service center number. * * Returns: (transfer full): The number of the SMSC, or %NULL if it couldn't be * retrieved. The returned value should be freed with g_free(). * * Since: 1.0 */ gchar * mm_sms_dup_smsc (MMSms *self) { g_return_val_if_fail (MM_IS_SMS (self), NULL); RETURN_NON_EMPTY_STRING ( mm_gdbus_sms_dup_smsc (MM_GDBUS_SMS (self))); } /*****************************************************************************/ /** * mm_sms_get_timestamp: * @self: A #MMSms. * * Gets the time when the first PDU of the SMS message arrived the SMSC, in * ISO8601 * format. * * This field is only applicable if the PDU type is %MM_SMS_PDU_TYPE_DELIVER or * %MM_SMS_PDU_TYPE_STATUS_REPORT. * * The returned value is only valid until the property changes so it is * only safe to use this function on the thread where @self was constructed. Use * mm_sms_dup_timestamp() if on another thread. * * Returns: (transfer none): The timestamp, or %NULL if it couldn't be * retrieved. * * Since: 1.0 */ const gchar * mm_sms_get_timestamp (MMSms *self) { g_return_val_if_fail (MM_IS_SMS (self), NULL); RETURN_NON_EMPTY_CONSTANT_STRING ( mm_gdbus_sms_get_timestamp (MM_GDBUS_SMS (self))); } /** * mm_sms_dup_timestamp: * @self: A #MMSms. * * Gets the time when the first PDU of the SMS message arrived the SMSC, in * ISO8601 * format. * * This field is only applicable if the PDU type is %MM_SMS_PDU_TYPE_DELIVER or * %MM_SMS_PDU_TYPE_STATUS_REPORT. * * Returns: (transfer full): The timestamp, or %NULL if it couldn't be * retrieved. The returned value should be freed with g_free(). * * Since: 1.0 */ gchar * mm_sms_dup_timestamp (MMSms *self) { g_return_val_if_fail (MM_IS_SMS (self), NULL); RETURN_NON_EMPTY_STRING ( mm_gdbus_sms_dup_timestamp (MM_GDBUS_SMS (self))); } /*****************************************************************************/ /** * mm_sms_get_discharge_timestamp: * @self: A #MMSms. * * Gets the time when the first PDU of the SMS message left the SMSC, in * ISO8601 * format. * * This field is only applicable if the PDU type is * %MM_SMS_PDU_TYPE_STATUS_REPORT. * * The returned value is only valid until the property changes so it is * only safe to use this function on the thread where @self was constructed. Use * mm_sms_dup_discharge_timestamp() if on another thread. * * Returns: (transfer none): The timestamp, or %NULL if it couldn't be * retrieved. * * Since: 1.0 */ const gchar * mm_sms_get_discharge_timestamp (MMSms *self) { g_return_val_if_fail (MM_IS_SMS (self), NULL); RETURN_NON_EMPTY_CONSTANT_STRING ( mm_gdbus_sms_get_discharge_timestamp (MM_GDBUS_SMS (self))); } /** * mm_sms_dup_discharge_timestamp: * @self: A #MMSms. * * Gets the time when the first PDU of the SMS message left the SMSC, in * ISO8601 * format. * * This field is only applicable if the PDU type is * %MM_SMS_PDU_TYPE_STATUS_REPORT. * * Returns: (transfer full): The timestamp, or %NULL if it couldn't be * retrieved. The returned value should be freed with g_free(). * * Since: 1.0 */ gchar * mm_sms_dup_discharge_timestamp (MMSms *self) { g_return_val_if_fail (MM_IS_SMS (self), NULL); RETURN_NON_EMPTY_STRING ( mm_gdbus_sms_dup_discharge_timestamp (MM_GDBUS_SMS (self))); } /*****************************************************************************/ /** * mm_sms_get_validity_type: * @self: A #MMSms. * * Gets the type of validity information in the SMS. * * Returns: the validity type or #MM_SMS_VALIDITY_TYPE_UNKNOWN. * * Since: 1.0 */ MMSmsValidityType mm_sms_get_validity_type (MMSms *self) { GVariant *variant; guint type; GVariant *value; g_return_val_if_fail (MM_IS_SMS (self), MM_SMS_VALIDITY_TYPE_UNKNOWN); variant = mm_gdbus_sms_dup_validity (MM_GDBUS_SMS (self)); if (!variant) return MM_SMS_VALIDITY_TYPE_UNKNOWN; g_variant_get (variant, "(uv)", &type, &value); g_variant_unref (variant); g_variant_unref (value); return (MMSmsValidityType)type; } /** * mm_sms_get_validity_relative: * @self: A #MMSms. * * Gets the length of the validity period, in minutes. * * Only applicable if the type of validity is #MM_SMS_VALIDITY_TYPE_RELATIVE. * * Returns: the length of the validity period, or 0 if unknown. * * Since: 1.0 */ guint mm_sms_get_validity_relative (MMSms *self) { GVariant *variant; guint type; GVariant *value; guint value_integer = 0; g_return_val_if_fail (MM_IS_SMS (self), MM_SMS_VALIDITY_TYPE_UNKNOWN); variant = mm_gdbus_sms_dup_validity (MM_GDBUS_SMS (self)); if (!variant) return 0; g_variant_get (variant, "(uv)", &type, &value); if (type == MM_SMS_VALIDITY_TYPE_RELATIVE) value_integer = g_variant_get_uint32 (value); g_variant_unref (variant); g_variant_unref (value); return value_integer; } /*****************************************************************************/ /** * mm_sms_get_class: * @self: A #MMSms. * * Gets the 3GPP message class of the SMS. * * Returns: the message class, or -1 for invalid/unset class. * * Since: 1.0 */ gint mm_sms_get_class (MMSms *self) { g_return_val_if_fail (MM_IS_SMS (self), -1); return mm_gdbus_sms_get_class (MM_GDBUS_SMS (self)); } /*****************************************************************************/ /** * mm_sms_get_message_reference: * @self: A #MMSms. * * Gets the message reference of the last PDU sent/received within this SMS. * * If the PDU type is %MM_SMS_PDU_TYPE_STATUS_REPORT, this field identifies the * message reference of the PDU associated to the status report. * * Returns: The message reference. * * Since: 1.0 */ guint mm_sms_get_message_reference (MMSms *self) { g_return_val_if_fail (MM_IS_SMS (self), 0); return mm_gdbus_sms_get_message_reference (MM_GDBUS_SMS (self)); } /*****************************************************************************/ /** * mm_sms_get_delivery_report_request: * @self: A #MMSms. * * Checks whether delivery report is requested for this SMS. * * Returns: %TRUE if delivery report is requested, %FALSE otherwise. * * Since: 1.0 */ gboolean mm_sms_get_delivery_report_request (MMSms *self) { g_return_val_if_fail (MM_IS_SMS (self), FALSE); return mm_gdbus_sms_get_delivery_report_request (MM_GDBUS_SMS (self)); } /*****************************************************************************/ /** * mm_sms_get_delivery_state: * @self: A #MMSms. * * Gets the delivery state of this SMS. * * This field is only applicable if the PDU type is * %MM_SMS_PDU_TYPE_STATUS_REPORT. * * Returns: A #MMSmsDeliveryState specifying the delivery state. * * Since: 1.0 */ guint mm_sms_get_delivery_state (MMSms *self) { g_return_val_if_fail (MM_IS_SMS (self), MM_SMS_DELIVERY_STATE_UNKNOWN); return mm_gdbus_sms_get_delivery_state (MM_GDBUS_SMS (self)); } /*****************************************************************************/ /** * mm_sms_get_state: * @self: A #MMSms. * * Gets the state of this SMS. * * Returns: A #MMSmsState specifying the state. * * Since: 1.0 */ MMSmsState mm_sms_get_state (MMSms *self) { g_return_val_if_fail (MM_IS_SMS (self), MM_SMS_STATE_UNKNOWN); return (MMSmsState)mm_gdbus_sms_get_state (MM_GDBUS_SMS (self)); } /*****************************************************************************/ /** * mm_sms_get_storage: * @self: A #MMSms. * * Gets the storage in which this SMS is kept. * * Returns: A #MMSmsStorage specifying the storage. * * Since: 1.0 */ MMSmsStorage mm_sms_get_storage (MMSms *self) { g_return_val_if_fail (MM_IS_SMS (self), MM_SMS_STORAGE_UNKNOWN); return (MMSmsStorage)mm_gdbus_sms_get_storage (MM_GDBUS_SMS (self)); } /*****************************************************************************/ /** * mm_sms_get_pdu_type: * @self: A #MMSms. * * Gets the PDU type on which this SMS is based. * * Returns: A #MMSmsPduType specifying the PDU type. * * Since: 1.0 */ MMSmsPduType mm_sms_get_pdu_type (MMSms *self) { g_return_val_if_fail (MM_IS_SMS (self), MM_SMS_PDU_TYPE_UNKNOWN); return (MMSmsPduType)mm_gdbus_sms_get_pdu_type (MM_GDBUS_SMS (self)); } /*****************************************************************************/ /** * mm_sms_get_teleservice_id: * @self: A #MMSms. * * Gets the 3GPP2 Teleservice ID. * * Returns: a #MMSmsCdmaTeleserviceId. * * Since: 1.2 */ MMSmsCdmaTeleserviceId mm_sms_get_teleservice_id (MMSms *self) { g_return_val_if_fail (MM_IS_SMS (self), MM_SMS_CDMA_TELESERVICE_ID_UNKNOWN); return (MMSmsCdmaTeleserviceId) mm_gdbus_sms_get_teleservice_id (MM_GDBUS_SMS (self)); } /*****************************************************************************/ /** * mm_sms_get_service_category: * @self: A #MMSms. * * Gets the 3GPP2 Service Category. * * Returns: a #MMSmsCdmaServiceCategory. * * Since: 1.2 */ MMSmsCdmaServiceCategory mm_sms_get_service_category (MMSms *self) { g_return_val_if_fail (MM_IS_SMS (self), MM_SMS_CDMA_SERVICE_CATEGORY_UNKNOWN); return (MMSmsCdmaServiceCategory) mm_gdbus_sms_get_service_category (MM_GDBUS_SMS (self)); } /*****************************************************************************/ /** * mm_sms_send_finish: * @self: A #MMSms. * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to * mm_sms_send(). * @error: Return location for error or %NULL. * * Finishes an operation started with mm_sms_send(). * * Returns: %TRUE if the operation succeeded, %FALSE if @error is set. * * Since: 1.0 */ gboolean mm_sms_send_finish (MMSms *self, GAsyncResult *res, GError **error) { g_return_val_if_fail (MM_IS_SMS (self), FALSE); return mm_gdbus_sms_call_send_finish (MM_GDBUS_SMS (self), res, error); } /** * mm_sms_send: * @self: A #MMSms. * @cancellable: (allow-none): A #GCancellable or %NULL. * @callback: A #GAsyncReadyCallback to call when the request is satisfied or * %NULL. * @user_data: User data to pass to @callback. * * Asynchronously requests to queue the message for delivery. * * SMS objects can only be sent once. * * When the operation is finished, @callback will be invoked in the * thread-default main loop * of the thread you are calling this method from. You can then call * mm_sms_send_finish() to get the result of the operation. * * See mm_sms_send_sync() for the synchronous, blocking version of this method. * * Since: 1.0 */ void mm_sms_send (MMSms *self, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { g_return_if_fail (MM_IS_SMS (self)); mm_gdbus_sms_call_send (MM_GDBUS_SMS (self), cancellable, callback, user_data); } /** * mm_sms_send_sync: * @self: A #MMSms. * @cancellable: (allow-none): A #GCancellable or %NULL. * @error: Return location for error or %NULL. * * Synchronously requests to queue the message for delivery. * * SMS objects can only be sent once. * * The calling thread is blocked until a reply is received. See mm_sms_send() * for the asynchronous version of this method. * * Returns: %TRUE if the operation succeeded, %FALSE if @error is set. * * Since: 1.0 */ gboolean mm_sms_send_sync (MMSms *self, GCancellable *cancellable, GError **error) { g_return_val_if_fail (MM_IS_SMS (self), FALSE); return mm_gdbus_sms_call_send_sync (MM_GDBUS_SMS (self), cancellable, error); } /*****************************************************************************/ /** * mm_sms_store_finish: * @self: A #MMSms. * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to * mm_sms_store(). * @error: Return location for error or %NULL. * * Finishes an operation started with mm_sms_store(). * * Returns: %TRUE if the operation succeeded, %FALSE if @error is set. * * Since: 1.0 */ gboolean mm_sms_store_finish (MMSms *self, GAsyncResult *res, GError **error) { g_return_val_if_fail (MM_IS_SMS (self), FALSE); return mm_gdbus_sms_call_store_finish (MM_GDBUS_SMS (self), res, error); } /** * mm_sms_store: * @self: A #MMSms. * @storage: A #MMSmsStorage specifying where to store the SMS, or * %MM_SMS_STORAGE_UNKNOWN to use the default. * @cancellable: (allow-none): A #GCancellable or %NULL. * @callback: A #GAsyncReadyCallback to call when the request is satisfied or * %NULL. * @user_data: User data to pass to @callback. * * Asynchronoulsy requests to store the message in the device if not already done. * * SMS objects can only be stored once. * * When the operation is finished, @callback will be invoked in the * thread-default main loop * of the thread you are calling this method from. You can then call * mm_sms_store_finish() to get the result of the operation. * * See mm_sms_store_sync() for the synchronous, blocking version of this method. * * Since: 1.0 */ void mm_sms_store (MMSms *self, MMSmsStorage storage, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { g_return_if_fail (MM_IS_SMS (self)); mm_gdbus_sms_call_store (MM_GDBUS_SMS (self), storage, cancellable, callback, user_data); } /** * mm_sms_store_sync: * @self: A #MMSms. * @storage: A #MMSmsStorage specifying where to store the SMS, or * %MM_SMS_STORAGE_UNKNOWN to use the default. * @cancellable: (allow-none): A #GCancellable or %NULL. * @error: Return location for error or %NULL. * * Synchronoulsy requests to store the message in the device if not already * done. * * SMS objects can only be stored once. * * The calling thread is blocked until a reply is received. See mm_sms_store() * for the asynchronous version of this method. * * Returns: %TRUE if the operation succeeded, %FALSE if @error is set. * * Since: 1.0 */ gboolean mm_sms_store_sync (MMSms *self, MMSmsStorage storage, GCancellable *cancellable, GError **error) { g_return_val_if_fail (MM_IS_SMS (self), FALSE); return mm_gdbus_sms_call_store_sync (MM_GDBUS_SMS (self), storage, cancellable, error); } /*****************************************************************************/ static void mm_sms_init (MMSms *self) { } static void mm_sms_class_init (MMSmsClass *sms_class) { } ModemManager-1.23.4-dev/libmm-glib/mm-sms.h000066400000000000000000000116451456466623000203310ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * libmm-glib -- Access modem status & information from glib applications * * 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 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 Street, Fifth Floor, * Boston, MA 02110-1301 USA. * * Copyright (C) 2012 Google, Inc. */ #ifndef _MM_SMS_H_ #define _MM_SMS_H_ #if !defined (__LIBMM_GLIB_H_INSIDE__) && !defined (LIBMM_GLIB_COMPILATION) #error "Only can be included directly." #endif #include #include "mm-gdbus-sms.h" #include "mm-sms-properties.h" G_BEGIN_DECLS #define MM_TYPE_SMS (mm_sms_get_type ()) #define MM_SMS(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_SMS, MMSms)) #define MM_SMS_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_SMS, MMSmsClass)) #define MM_IS_SMS(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_SMS)) #define MM_IS_SMS_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), MM_TYPE_SMS)) #define MM_SMS_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_SMS, MMSmsClass)) typedef struct _MMSms MMSms; typedef struct _MMSmsClass MMSmsClass; /** * MMSms: * * The #MMSms structure contains private data and should only be accessed * using the provided API. */ struct _MMSms { /*< private >*/ MmGdbusSmsProxy parent; gpointer unused; }; struct _MMSmsClass { /*< private >*/ MmGdbusSmsProxyClass parent; }; GType mm_sms_get_type (void); G_DEFINE_AUTOPTR_CLEANUP_FUNC (MMSms, g_object_unref) const gchar *mm_sms_get_path (MMSms *self); gchar *mm_sms_dup_path (MMSms *self); const gchar *mm_sms_get_text (MMSms *self); gchar *mm_sms_dup_text (MMSms *self); const guint8 *mm_sms_get_data (MMSms *self, gsize *data_len); guint8 *mm_sms_dup_data (MMSms *self, gsize *data_len); const gchar *mm_sms_get_number (MMSms *self); gchar *mm_sms_dup_number (MMSms *self); const gchar *mm_sms_get_smsc (MMSms *self); gchar *mm_sms_dup_smsc (MMSms *self); const gchar *mm_sms_get_timestamp (MMSms *self); gchar *mm_sms_dup_timestamp (MMSms *self); const gchar *mm_sms_get_discharge_timestamp (MMSms *self); gchar *mm_sms_dup_discharge_timestamp (MMSms *self); MMSmsValidityType mm_sms_get_validity_type (MMSms *self); guint mm_sms_get_validity_relative (MMSms *self); gint mm_sms_get_class (MMSms *self); guint mm_sms_get_message_reference (MMSms *self); gboolean mm_sms_get_delivery_report_request (MMSms *self); guint mm_sms_get_delivery_state (MMSms *self); MMSmsState mm_sms_get_state (MMSms *self); MMSmsStorage mm_sms_get_storage (MMSms *self); MMSmsPduType mm_sms_get_pdu_type (MMSms *self); MMSmsCdmaTeleserviceId mm_sms_get_teleservice_id (MMSms *self); MMSmsCdmaServiceCategory mm_sms_get_service_category (MMSms *self); void mm_sms_send (MMSms *self, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); gboolean mm_sms_send_finish (MMSms *self, GAsyncResult *res, GError **error); gboolean mm_sms_send_sync (MMSms *self, GCancellable *cancellable, GError **error); void mm_sms_store (MMSms *self, MMSmsStorage storage, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); gboolean mm_sms_store_finish (MMSms *self, GAsyncResult *res, GError **error); gboolean mm_sms_store_sync (MMSms *self, MMSmsStorage storage, GCancellable *cancellable, GError **error); G_END_DECLS #endif /* _MM_SMS_H_ */ ModemManager-1.23.4-dev/libmm-glib/mm-unlock-retries.c000066400000000000000000000173521456466623000224710ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * libmm-glib -- Access modem status & information from glib applications * * 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 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 Street, Fifth Floor, * Boston, MA 02110-1301 USA. * * Copyright (C) 2012 Google, Inc. */ #include #include #include #include "mm-enums-types.h" #include "mm-flags-types.h" #include "mm-unlock-retries.h" /** * SECTION: mm-unlock-retries * @title: MMUnlockRetries * @short_description: Helper object to report unlock retries. * * The #MMUnlockRetries is an object exposing the unlock retry counts for * different #MMModemLock values. * * This object is retrieved from the #MMModem object with either * mm_modem_get_unlock_retries() or mm_modem_peek_unlock_retries(). */ G_DEFINE_TYPE (MMUnlockRetries, mm_unlock_retries, G_TYPE_OBJECT) struct _MMUnlockRetriesPrivate { GHashTable *ht; }; /*****************************************************************************/ /** * mm_unlock_retries_set: (skip) */ void mm_unlock_retries_set (MMUnlockRetries *self, MMModemLock lock, guint retries) { g_hash_table_replace (self->priv->ht, GUINT_TO_POINTER (lock), GUINT_TO_POINTER (retries)); } /** * mm_unlock_retries_unset: (skip) */ void mm_unlock_retries_unset (MMUnlockRetries *self, MMModemLock lock) { g_hash_table_remove (self->priv->ht, GUINT_TO_POINTER (lock)); } /*****************************************************************************/ /** * mm_unlock_retries_get: * @self: a #MMUnlockRetries. * @lock: a #MMModemLock. * * Gets the unlock retries for the given @lock. * * Returns: the unlock retries or %MM_UNLOCK_RETRIES_UNKNOWN if unknown. * * Since: 1.0 */ guint mm_unlock_retries_get (MMUnlockRetries *self, MMModemLock lock) { gpointer value = NULL; return (g_hash_table_lookup_extended (self->priv->ht, GUINT_TO_POINTER (lock), NULL, /* original key not needed */ &value) ? GPOINTER_TO_UINT (value) : MM_UNLOCK_RETRIES_UNKNOWN); } /*****************************************************************************/ /** * mm_unlock_retries_cmp: (skip) */ gboolean mm_unlock_retries_cmp (MMUnlockRetries *a, MMUnlockRetries *b) { GHashTableIter iter; gpointer key, value; if (g_hash_table_size (a->priv->ht) != g_hash_table_size (b->priv->ht)) return FALSE; g_hash_table_iter_init (&iter, a->priv->ht); while (g_hash_table_iter_next (&iter, &key, &value)) { g_assert (GPOINTER_TO_UINT (value) != MM_UNLOCK_RETRIES_UNKNOWN); if (GPOINTER_TO_UINT (value) != mm_unlock_retries_get (b, GPOINTER_TO_UINT (key))) return FALSE; } /* All equal! */ return TRUE; } /*****************************************************************************/ /** * mm_unlock_retries_foreach: * @self: a @MMUnlockRetries. * @callback: (scope call): callback to call for each available lock. * @user_data: (closure): data to pass to @callback. * * Executes @callback for each lock information found in @self. * * Since: 1.0 */ void mm_unlock_retries_foreach (MMUnlockRetries *self, MMUnlockRetriesForeachCb callback, gpointer user_data) { GHashTableIter iter; gpointer key, value; g_hash_table_iter_init (&iter, self->priv->ht); while (g_hash_table_iter_next (&iter, &key, &value)) { callback (GPOINTER_TO_UINT (key), GPOINTER_TO_UINT (value), user_data); } } /*****************************************************************************/ /** * mm_unlock_retries_get_dictionary: (skip) */ GVariant * mm_unlock_retries_get_dictionary (MMUnlockRetries *self) { GVariantBuilder builder; GHashTableIter iter; gpointer key, value; /* We do allow NULL */ if (!self) return NULL; g_variant_builder_init (&builder, G_VARIANT_TYPE ("a{uu}")); g_hash_table_iter_init (&iter, self->priv->ht); while (g_hash_table_iter_next (&iter, &key, &value)) { g_variant_builder_add (&builder, "{uu}", GPOINTER_TO_UINT (key), GPOINTER_TO_UINT (value)); } return g_variant_ref_sink (g_variant_builder_end (&builder)); } /*****************************************************************************/ /** * mm_unlock_retries_new_from_dictionary: (skip) */ MMUnlockRetries * mm_unlock_retries_new_from_dictionary (GVariant *dictionary) { GVariantIter iter; guint key, value; MMUnlockRetries *self; self = mm_unlock_retries_new (); if (!dictionary) return self; g_variant_iter_init (&iter, dictionary); while (g_variant_iter_next (&iter, "{uu}", &key, &value)) { mm_unlock_retries_set (self, (MMModemLock)key, value); } return self; } /*****************************************************************************/ /** * mm_unlock_retries_build_string: (skip) */ gchar * mm_unlock_retries_build_string (MMUnlockRetries *self) { GString *str = NULL; GHashTableIter iter; gpointer key, value; g_hash_table_iter_init (&iter, self->priv->ht); while (g_hash_table_iter_next (&iter, &key, &value)) { const gchar *lock_name; guint retries; lock_name = mm_modem_lock_get_string ((MMModemLock)GPOINTER_TO_UINT (key)); retries = GPOINTER_TO_UINT (value); if (!str) { str = g_string_new (""); g_string_append_printf (str, "%s (%u)", lock_name, retries); } else g_string_append_printf (str, ", %s (%u)", lock_name, retries); } return (str ? g_string_free (str, FALSE) : NULL); } /*****************************************************************************/ /** * mm_unlock_retries_new: (skip) */ MMUnlockRetries * mm_unlock_retries_new (void) { return (MM_UNLOCK_RETRIES ( g_object_new (MM_TYPE_UNLOCK_RETRIES, NULL))); } static void mm_unlock_retries_init (MMUnlockRetries *self) { self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, MM_TYPE_UNLOCK_RETRIES, MMUnlockRetriesPrivate); self->priv->ht = g_hash_table_new (g_direct_hash, g_direct_equal); } static void finalize (GObject *object) { MMUnlockRetries *self = MM_UNLOCK_RETRIES (object); g_hash_table_destroy (self->priv->ht); G_OBJECT_CLASS (mm_unlock_retries_parent_class)->finalize (object); } static void mm_unlock_retries_class_init (MMUnlockRetriesClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); g_type_class_add_private (object_class, sizeof (MMUnlockRetriesPrivate)); object_class->finalize = finalize; } ModemManager-1.23.4-dev/libmm-glib/mm-unlock-retries.h000066400000000000000000000102151456466623000224650ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * libmm-glib -- Access modem status & information from glib applications * * 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 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 Street, Fifth Floor, * Boston, MA 02110-1301 USA. * * Copyright (C) 2012 Google, Inc. */ #ifndef MM_UNLOCK_RETRIES_H #define MM_UNLOCK_RETRIES_H #if !defined (__LIBMM_GLIB_H_INSIDE__) && !defined (LIBMM_GLIB_COMPILATION) #error "Only can be included directly." #endif #include #include G_BEGIN_DECLS #define MM_TYPE_UNLOCK_RETRIES (mm_unlock_retries_get_type ()) #define MM_UNLOCK_RETRIES(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_UNLOCK_RETRIES, MMUnlockRetries)) #define MM_UNLOCK_RETRIES_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_UNLOCK_RETRIES, MMUnlockRetriesClass)) #define MM_IS_UNLOCK_RETRIES(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_UNLOCK_RETRIES)) #define MM_IS_UNLOCK_RETRIES_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_UNLOCK_RETRIES)) #define MM_UNLOCK_RETRIES_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_UNLOCK_RETRIES, MMUnlockRetriesClass)) /** * MM_UNLOCK_RETRIES_UNKNOWN: * * Identifier for reporting unknown unlock retries. * * Since: 1.0 */ #define MM_UNLOCK_RETRIES_UNKNOWN 999 typedef struct _MMUnlockRetries MMUnlockRetries; typedef struct _MMUnlockRetriesClass MMUnlockRetriesClass; typedef struct _MMUnlockRetriesPrivate MMUnlockRetriesPrivate; /** * MMUnlockRetries: * * The #MMUnlockRetries structure contains private data and should only be accessed * using the provided API. */ struct _MMUnlockRetries { /*< private >*/ GObject parent; MMUnlockRetriesPrivate *priv; }; struct _MMUnlockRetriesClass { /*< private >*/ GObjectClass parent; }; GType mm_unlock_retries_get_type (void); G_DEFINE_AUTOPTR_CLEANUP_FUNC (MMUnlockRetries, g_object_unref) guint mm_unlock_retries_get (MMUnlockRetries *self, MMModemLock lock); /** * MMUnlockRetriesForeachCb: * @lock: a #MMModemLock. * @count: the number of retries left for @lock. * @user_data: data passed to the function. * * Specifies the type of function passed to mm_unlock_retries_foreach(). * * Since: 1.0 */ typedef void (* MMUnlockRetriesForeachCb) (MMModemLock lock, guint count, gpointer user_data); void mm_unlock_retries_foreach (MMUnlockRetries *self, MMUnlockRetriesForeachCb callback, gpointer user_data); /*****************************************************************************/ /* ModemManager/libmm-glib/mmcli specific methods */ #if defined (_LIBMM_INSIDE_MM) || \ defined (_LIBMM_INSIDE_MMCLI) || \ defined (LIBMM_GLIB_COMPILATION) MMUnlockRetries *mm_unlock_retries_new (void); MMUnlockRetries *mm_unlock_retries_new_from_dictionary (GVariant *dictionary); void mm_unlock_retries_set (MMUnlockRetries *self, MMModemLock lock, guint retries); void mm_unlock_retries_unset (MMUnlockRetries *self, MMModemLock lock); gboolean mm_unlock_retries_cmp (MMUnlockRetries *a, MMUnlockRetries *b); GVariant *mm_unlock_retries_get_dictionary (MMUnlockRetries *self); gchar *mm_unlock_retries_build_string (MMUnlockRetries *self); #endif G_END_DECLS #endif /* MM_UNLOCK_RETRIES_H */ ModemManager-1.23.4-dev/libmm-glib/tests/000077500000000000000000000000001456466623000201025ustar00rootroot00000000000000ModemManager-1.23.4-dev/libmm-glib/tests/meson.build000066400000000000000000000006501456466623000222450ustar00rootroot00000000000000# SPDX-License-Identifier: GPL-2.0-or-later # Copyright (C) 2021 Iñigo Martinez test_units = [ 'common-helpers', 'pco', ] foreach test_unit: test_units test_name = 'test-' + test_unit exe = executable( test_name, test_name + '.c', include_directories: top_inc, dependencies: libmm_glib_dep, c_args: '-DLIBMM_GLIB_COMPILATION', ) test(test_name, exe) endforeach ModemManager-1.23.4-dev/libmm-glib/tests/test-common-helpers.c000066400000000000000000001340761456466623000241660ustar00rootroot00000000000000/* -*- 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) 2012 Google, Inc. */ #include #include #include /********************* KEY VALUE PARSER TESTS *********************/ typedef struct { const gchar *key; const gchar *value; } KeyValueEntry; /* ---- Expected cases ---- */ typedef struct { const KeyValueEntry *entries; guint n_entries; guint i; } CommonKeyValueTestContext; static gboolean common_key_value_test_foreach (const gchar *key, const gchar *value, CommonKeyValueTestContext *ctx) { g_assert_cmpuint (ctx->i, <, ctx->n_entries); g_assert_cmpstr (key, ==, ctx->entries[ctx->i].key); g_assert_cmpstr (value, ==, ctx->entries[ctx->i].value); ctx->i++; return TRUE; } static void common_key_value_test (const gchar *str, const KeyValueEntry *entries, guint n_entries) { GError *error = NULL; CommonKeyValueTestContext ctx; ctx.entries = entries; ctx.n_entries = n_entries; ctx.i = 0; mm_common_parse_key_value_string (str, &error, (MMParseKeyValueForeachFn)common_key_value_test_foreach, &ctx); g_assert_no_error (error); g_assert_cmpuint (ctx.i, ==, ctx.n_entries); } static void key_value_test_standard (void) { const gchar *str = "key1=value1," "key2=value2," "key3=value3"; const KeyValueEntry entries[] = { { "key1", "value1" }, { "key2", "value2" }, { "key3", "value3" } }; common_key_value_test (str, entries, G_N_ELEMENTS (entries)); } static void key_value_test_spaces (void) { const gchar *str = " key1 = value1 , " "\t\tkey2\t=\tvalue2\t,\t" "\n\nkey3\n=\nvalue3\n"; const KeyValueEntry entries[] = { { "key1", "value1" }, { "key2", "value2" }, { "key3", "value3" } }; common_key_value_test (str, entries, G_N_ELEMENTS (entries)); } static void key_value_test_double_quotes (void) { const gchar *str = "key1=\"this is a string\"," "key2=\"and so is this\""; const KeyValueEntry entries[] = { { "key1", "this is a string" }, { "key2", "and so is this" } }; common_key_value_test (str, entries, G_N_ELEMENTS (entries)); } static void key_value_test_single_quotes (void) { const gchar *str = "key1='this is a string'," "key2='and so is this'"; const KeyValueEntry entries[] = { { "key1", "this is a string" }, { "key2", "and so is this" } }; common_key_value_test (str, entries, G_N_ELEMENTS (entries)); } static void key_value_test_empty_value (void) { const gchar *str = "key1=," "key2=\"\""; const KeyValueEntry entries[] = { { "key1", "" }, { "key2", "" } }; common_key_value_test (str, entries, G_N_ELEMENTS (entries)); } static void key_value_test_empty_string (void) { const gchar *str = ""; const KeyValueEntry entries[] = { }; common_key_value_test (str, entries, G_N_ELEMENTS (entries)); } /* ---- Unexpected cases ---- */ static gboolean common_key_value_error_test_foreach (const gchar *key, const gchar *value, gpointer none) { /* no op */ return TRUE; } static void common_key_value_error_test (const gchar *str) { GError *error = NULL; mm_common_parse_key_value_string (str, &error, (MMParseKeyValueForeachFn)common_key_value_error_test_foreach, NULL); /* We don't really care about the specific error type */ g_assert (error != NULL); g_error_free (error); } static void key_value_error_test_no_first_key (void) { common_key_value_error_test ("=value1"); } static void key_value_error_test_no_key (void) { common_key_value_error_test ("key1=value1," "=value2"); } static void key_value_error_test_missing_double_quotes_0 (void) { common_key_value_error_test ("key1=\"value1"); } static void key_value_error_test_missing_double_quotes_1 (void) { common_key_value_error_test ("key1=\"value1," "key2=\"value2\""); } static void key_value_error_test_missing_double_quotes_2 (void) { common_key_value_error_test ("key1=\"value1\"," "key2=\"value2"); } static void key_value_error_test_missing_single_quotes_0 (void) { common_key_value_error_test ("key1='value1"); } static void key_value_error_test_missing_single_quotes_1 (void) { common_key_value_error_test ("key1='value1," "key2='value2'"); } static void key_value_error_test_missing_single_quotes_2 (void) { common_key_value_error_test ("key1='value1'," "key2='value2"); } static void key_value_error_test_missing_comma_0 (void) { common_key_value_error_test ("key1=value1 " "key2=value2"); } static void key_value_error_test_missing_comma_1 (void) { common_key_value_error_test ("key1=\"value1\" " "key2=\"value2\""); } static void key_value_error_test_missing_comma_2 (void) { common_key_value_error_test ("key1='value1' " "key2='value2'"); } /********************* BAND ARRAY TESTS *********************/ static void common_band_array_cmp_test (gboolean equal, const MMModemBand *bands_a, guint n_bands_a, const MMModemBand *bands_b, guint n_bands_b) { GArray *a; GArray *b; a = g_array_sized_new (FALSE, FALSE, sizeof (MMModemBand), n_bands_a); g_array_append_vals (a, bands_a, n_bands_a); b = g_array_sized_new (FALSE, FALSE, sizeof (MMModemBand), n_bands_b); g_array_append_vals (b, bands_b, n_bands_b); g_assert_cmpuint (equal, ==, mm_common_bands_garray_cmp (a, b)); g_assert_cmpuint (equal, ==, mm_common_bands_garray_cmp (b, a)); g_array_unref (a); g_array_unref (b); } static void band_array_cmp_test_equal_empty (void) { const MMModemBand a[] = { }; const MMModemBand b[] = { }; common_band_array_cmp_test (TRUE, a, G_N_ELEMENTS (a), b, G_N_ELEMENTS (b)); } static void band_array_cmp_test_equal_one (void) { const MMModemBand a[] = { MM_MODEM_BAND_EGSM }; const MMModemBand b[] = { MM_MODEM_BAND_EGSM }; common_band_array_cmp_test (TRUE, a, G_N_ELEMENTS (a), b, G_N_ELEMENTS (b)); } static void band_array_cmp_test_equal_multiple_same_order (void) { const MMModemBand a[] = { MM_MODEM_BAND_EGSM, MM_MODEM_BAND_DCS, MM_MODEM_BAND_PCS }; const MMModemBand b[] = { MM_MODEM_BAND_EGSM, MM_MODEM_BAND_DCS, MM_MODEM_BAND_PCS }; common_band_array_cmp_test (TRUE, a, G_N_ELEMENTS (a), b, G_N_ELEMENTS (b)); } static void band_array_cmp_test_equal_multiple_different_order (void) { const MMModemBand a[] = { MM_MODEM_BAND_EGSM, MM_MODEM_BAND_DCS, MM_MODEM_BAND_PCS }; const MMModemBand b[] = { MM_MODEM_BAND_DCS, MM_MODEM_BAND_PCS, MM_MODEM_BAND_EGSM }; common_band_array_cmp_test (TRUE, a, G_N_ELEMENTS (a), b, G_N_ELEMENTS (b)); } static void band_array_cmp_test_different_one (void) { const MMModemBand a[] = { MM_MODEM_BAND_EGSM }; const MMModemBand b[] = { MM_MODEM_BAND_DCS }; common_band_array_cmp_test (FALSE, a, G_N_ELEMENTS (a), b, G_N_ELEMENTS (b)); } static void band_array_cmp_test_different_none (void) { const MMModemBand a[] = { }; const MMModemBand b[] = { MM_MODEM_BAND_EGSM }; common_band_array_cmp_test (FALSE, a, G_N_ELEMENTS (a), b, G_N_ELEMENTS (b)); } static void band_array_cmp_test_different_multiple_1 (void) { const MMModemBand a[] = { MM_MODEM_BAND_EGSM }; const MMModemBand b[] = { MM_MODEM_BAND_EGSM, MM_MODEM_BAND_DCS }; common_band_array_cmp_test (FALSE, a, G_N_ELEMENTS (a), b, G_N_ELEMENTS (b)); } static void band_array_cmp_test_different_multiple_2 (void) { const MMModemBand a[] = { MM_MODEM_BAND_EGSM }; const MMModemBand b[] = { MM_MODEM_BAND_DCS, MM_MODEM_BAND_EGSM }; common_band_array_cmp_test (FALSE, a, G_N_ELEMENTS (a), b, G_N_ELEMENTS (b)); } /********************* FIELD PARSERS TESTS *********************/ static void field_parser_int (void) { gint num; gchar *str; /* Failures */ g_assert (mm_get_int_from_str (NULL, &num) == FALSE); g_assert (mm_get_int_from_str ("", &num) == FALSE); g_assert (mm_get_int_from_str ("a", &num) == FALSE); g_assert (mm_get_int_from_str ("a100", &num) == FALSE); g_assert (mm_get_int_from_str ("100a", &num) == FALSE); g_assert (mm_get_int_from_str ("\r\n", &num) == FALSE); str = g_strdup_printf ("%" G_GINT64_FORMAT, (gint64)G_MAXINT + 1); g_assert (mm_get_int_from_str (str, &num) == FALSE); g_free (str); str = g_strdup_printf ("%" G_GINT64_FORMAT, (gint64)(G_MININT) - 1); g_assert (mm_get_int_from_str (str, &num) == FALSE); g_free (str); /* Successes */ g_assert (mm_get_int_from_str ("0", &num) == TRUE); g_assert_cmpint (num, ==, 0); g_assert (mm_get_int_from_str ("-100", &num) == TRUE); g_assert_cmpint (num, ==, -100); g_assert (mm_get_int_from_str ("100", &num) == TRUE); g_assert_cmpint (num, ==, 100); g_assert (mm_get_int_from_str ("-256\r\n", &num) == TRUE); g_assert_cmpint (num, ==, -256); str = g_strdup_printf ("%" G_GINT64_FORMAT, (gint64)G_MAXINT); g_assert (mm_get_int_from_str (str, &num) == TRUE); g_assert_cmpint (num, ==, G_MAXINT); g_free (str); str = g_strdup_printf ("%" G_GINT64_FORMAT, (gint64)G_MININT); g_assert (mm_get_int_from_str (str, &num) == TRUE); g_assert_cmpint (num, ==, G_MININT); g_free (str); } static void field_parser_uint (void) { gchar *str; guint num; /* Failures */ g_assert (mm_get_uint_from_str (NULL, &num) == FALSE); g_assert (mm_get_uint_from_str ("", &num) == FALSE); g_assert (mm_get_uint_from_str ("a", &num) == FALSE); g_assert (mm_get_uint_from_str ("a100", &num) == FALSE); g_assert (mm_get_uint_from_str ("100a", &num) == FALSE); g_assert (mm_get_uint_from_str ("-100", &num) == FALSE); g_assert (mm_get_uint_from_str ("\r\n", &num) == FALSE); str = g_strdup_printf ("%" G_GUINT64_FORMAT, (guint64)(G_MAXUINT) + 1); g_assert (mm_get_uint_from_str (str, &num) == FALSE); g_free (str); /* Successes */ g_assert (mm_get_uint_from_str ("0", &num) == TRUE); g_assert_cmpuint (num, ==, 0); g_assert (mm_get_uint_from_str ("100", &num) == TRUE); g_assert_cmpuint (num, ==, 100); g_assert (mm_get_uint_from_str ("256\r\n", &num) == TRUE); g_assert_cmpuint (num, ==, 256); str = g_strdup_printf ("%" G_GUINT64_FORMAT, (guint64)G_MAXUINT); g_assert (mm_get_uint_from_str (str, &num) == TRUE); g_assert_cmpuint (num, ==, G_MAXUINT); g_free (str); } static void field_parser_double (void) { gchar *str; gdouble num; /* Failures */ g_assert (mm_get_double_from_str (NULL, &num) == FALSE); g_assert (mm_get_double_from_str ("", &num) == FALSE); g_assert (mm_get_double_from_str ("a", &num) == FALSE); g_assert (mm_get_double_from_str ("a100", &num) == FALSE); g_assert (mm_get_double_from_str ("100a", &num) == FALSE); g_assert (mm_get_double_from_str ("\r\n", &num) == FALSE); /* Successes */ g_assert (mm_get_double_from_str ("-100", &num) == TRUE); g_assert (num - (-100.0) < 0000000.1); g_assert (mm_get_double_from_str ("-100.7567", &num) == TRUE); g_assert (num - (-100.7567) < 0000000.1); g_assert (mm_get_double_from_str ("0", &num) == TRUE); g_assert (num < 0000000.1); g_assert (mm_get_double_from_str ("-0.0", &num) == TRUE); g_assert (num < 0000000.1); g_assert (mm_get_double_from_str ("0.0", &num) == TRUE); g_assert (num < 0000000.1); g_assert (mm_get_double_from_str ("100", &num) == TRUE); g_assert (num - (100.0) < 0000000.1); g_assert (mm_get_double_from_str ("100.7567", &num) == TRUE); g_assert (num - (100.7567) < 0000000.1); g_assert (mm_get_double_from_str ("100.7567\r\n", &num) == TRUE); g_assert (num - (100.7567) < 0000000.1); str = g_strdup_printf ("%lf", (gdouble)G_MINDOUBLE); g_assert (mm_get_double_from_str (str, &num) == TRUE); g_assert (num - G_MINDOUBLE < 0000000.1); g_free (str); str = g_strdup_printf ("%lf", (gdouble)G_MAXDOUBLE); g_assert (mm_get_double_from_str (str, &num) == TRUE); g_assert (num - G_MAXDOUBLE < 0000000.1); g_free (str); } /**************************************************************/ /* hexstr2bin & bin2hexstr */ static void common_hexstr2bin_test_failure (const gchar *input_hex) { g_autoptr(GError) error = NULL; g_autofree guint8 *bin = NULL; gsize bin_len = 0; g_assert (mm_utils_ishexstr (input_hex) == FALSE); bin = mm_utils_hexstr2bin (input_hex, -1, &bin_len, &error); g_assert_null (bin); g_assert_nonnull (error); } static void common_hexstr2bin_test_success_len (const gchar *input_hex, gssize input_hex_len) { g_autoptr(GError) error = NULL; g_autofree guint8 *bin = NULL; gsize bin_len = 0; g_autofree gchar *hex = NULL; bin = mm_utils_hexstr2bin (input_hex, input_hex_len, &bin_len, &error); g_assert_nonnull (bin); g_assert_no_error (error); hex = mm_utils_bin2hexstr (bin, bin_len); g_assert_nonnull (hex); if (input_hex_len == -1) g_assert (g_ascii_strcasecmp (input_hex, hex) == 0); else g_assert (g_ascii_strncasecmp (input_hex, hex, (gsize)input_hex_len) == 0); } static void common_hexstr2bin_test_success (const gchar *input_hex) { gsize input_hex_len; gssize i; g_assert (mm_utils_ishexstr (input_hex) == TRUE); common_hexstr2bin_test_success_len (input_hex, -1); input_hex_len = strlen (input_hex); for (i = input_hex_len; i >= 2; i-=2) common_hexstr2bin_test_success_len (input_hex, i); } static void hexstr_lower_case (void) { common_hexstr2bin_test_success ("000123456789abcdefff"); } static void hexstr_upper_case (void) { common_hexstr2bin_test_success ("000123456789ABCDEFFF"); } static void hexstr_mixed_case (void) { common_hexstr2bin_test_success ("000123456789AbcDefFf"); } static void hexstr_empty (void) { common_hexstr2bin_test_failure (""); } static void hexstr_missing_digits (void) { common_hexstr2bin_test_failure ("012"); } static void hexstr_wrong_digits_all (void) { common_hexstr2bin_test_failure ("helloworld"); } static void hexstr_wrong_digits_some (void) { common_hexstr2bin_test_failure ("012345k7"); } static void date_time_iso8601 (void) { gchar *date = NULL; GError *error = NULL; date = mm_new_iso8601_time_from_unix_time (1634307342, &error); g_assert_no_error (error); g_assert_cmpstr (date, ==, "2021-10-15T14:15:42Z"); g_free (date); date = mm_new_iso8601_time (2021, 10, 15, 16, 15, 42, FALSE, 0, &error); g_assert_no_error (error); g_assert_cmpstr (date, ==, "2021-10-15T16:15:42Z"); g_free (date); date = mm_new_iso8601_time (2021, 10, 15, 16, 15, 42, TRUE, 120, &error); g_assert_no_error (error); g_assert_cmpstr (date, ==, "2021-10-15T16:15:42+02"); g_free (date); /* Valid args: * - Year:[1-9999] * - Month:[1-12] * - Day:[1-28|29|30|31] according to year and month * - Hour: [0-23] * - Minute: [0-59] * - Seconds: [0.0-60.0) * */ date = mm_new_iso8601_time (2021, 13, 15, 16, 15, 42, TRUE, 120, &error); g_assert_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS); g_assert_null (date); g_clear_error (&error); /* No February 29 in 2021 */ date = mm_new_iso8601_time (2021, 2, 29, 16, 15, 42, TRUE, 120, &error); g_assert_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS); g_assert_null (date); g_clear_error (&error); /* Too far into the future */ date = mm_new_iso8601_time_from_unix_time (G_MAXINT64, &error); g_assert_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS); g_assert_null (date); g_clear_error (&error); } /**************************************************************/ /* string helpers */ static void bands_to_string (void) { gchar *bands_str = NULL; MMModemBand bands[2] = {MM_MODEM_BAND_G480, MM_MODEM_BAND_CDMA_BC9}; bands_str = mm_common_build_bands_string (NULL, 0); g_assert_cmpstr (bands_str, ==, "none"); g_clear_pointer (&bands_str, g_free); bands_str = mm_common_build_bands_string (bands, 1); g_assert_cmpstr (bands_str, ==, "g480"); g_clear_pointer (&bands_str, g_free); bands_str = mm_common_build_bands_string (bands, 2); g_assert_cmpstr (bands_str, ==, "g480, cdma-bc9"); g_clear_pointer (&bands_str, g_free); } static void capabilities_to_string (void) { gchar *capabilities_str = NULL; MMModemCapability capabilities[2] = {MM_MODEM_CAPABILITY_CDMA_EVDO, MM_MODEM_CAPABILITY_TDS}; capabilities_str = mm_common_build_capabilities_string (NULL, 0); g_assert_cmpstr (capabilities_str, ==, "none"); g_clear_pointer (&capabilities_str, g_free); capabilities_str = mm_common_build_capabilities_string (capabilities, 1); g_assert_cmpstr (capabilities_str, ==, "cdma-evdo"); g_clear_pointer (&capabilities_str, g_free); capabilities_str = mm_common_build_capabilities_string (capabilities, 2); g_assert_cmpstr (capabilities_str, ==, "cdma-evdo\ntds"); g_clear_pointer (&capabilities_str, g_free); } static void mode_combinations_to_string (void) { gchar *mode_combinations_str = NULL; MMModemModeCombination mode_combinations[2] = { { .allowed = MM_MODEM_MODE_2G | MM_MODEM_MODE_3G, .preferred = MM_MODEM_MODE_3G }, { .allowed = MM_MODEM_MODE_4G | MM_MODEM_MODE_5G, .preferred = MM_MODEM_MODE_5G } }; mode_combinations_str = mm_common_build_mode_combinations_string (NULL, 0); g_assert_cmpstr (mode_combinations_str, ==, "none"); g_clear_pointer (&mode_combinations_str, g_free); mode_combinations_str = mm_common_build_mode_combinations_string (mode_combinations, 1); g_assert_cmpstr (mode_combinations_str, ==, "allowed: 2g, 3g; preferred: 3g"); g_clear_pointer (&mode_combinations_str, g_free); mode_combinations_str = mm_common_build_mode_combinations_string (mode_combinations, 2); g_assert_cmpstr (mode_combinations_str, ==, "allowed: 2g, 3g; preferred: 3g\nallowed: 4g, 5g; preferred: 5g"); g_clear_pointer (&mode_combinations_str, g_free); } static void ports_to_string (void) { gchar *ports_str = NULL; MMModemPortInfo ports[2] = { { .name = (gchar*)"port1", .type = MM_MODEM_PORT_TYPE_AT }, { .name = (gchar*)"port2", .type = MM_MODEM_PORT_TYPE_QMI } }; ports_str = mm_common_build_ports_string (NULL, 0); g_assert_cmpstr (ports_str, ==, "none"); g_clear_pointer (&ports_str, g_free); ports_str = mm_common_build_ports_string (ports, 1); g_assert_cmpstr (ports_str, ==, "port1 (at)"); g_clear_pointer (&ports_str, g_free); ports_str = mm_common_build_ports_string (ports, 2); g_assert_cmpstr (ports_str, ==, "port1 (at), port2 (qmi)"); g_clear_pointer (&ports_str, g_free); } static void sms_storages_to_string (void) { gchar *sms_storages_str = NULL; MMSmsStorage sms_storages[2] = {MM_SMS_STORAGE_MT, MM_SMS_STORAGE_BM}; sms_storages_str = mm_common_build_sms_storages_string (NULL, 0); g_assert_cmpstr (sms_storages_str, ==, "none"); g_clear_pointer (&sms_storages_str, g_free); sms_storages_str = mm_common_build_sms_storages_string (sms_storages, 1); g_assert_cmpstr (sms_storages_str, ==, "mt"); g_clear_pointer (&sms_storages_str, g_free); sms_storages_str = mm_common_build_sms_storages_string (sms_storages, 2); g_assert_cmpstr (sms_storages_str, ==, "mt, bm"); g_clear_pointer (&sms_storages_str, g_free); } static void capabilities_from_string (void) { MMModemCapability capability = MM_MODEM_CAPABILITY_ANY; GError *error = NULL; capability = mm_common_get_capabilities_from_string ("not found", &error); g_assert_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS); g_assert (capability == MM_MODEM_CAPABILITY_NONE); g_clear_error (&error); capability = mm_common_get_capabilities_from_string ("gsm-umts", &error); g_assert_no_error (error); g_assert (capability == MM_MODEM_CAPABILITY_GSM_UMTS); capability = mm_common_get_capabilities_from_string ("gsm-umts|capa-unknown", &error); g_assert_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS); g_assert (capability == MM_MODEM_CAPABILITY_NONE); g_clear_error (&error); capability = mm_common_get_capabilities_from_string ("gsm-umts|lte", &error); g_assert_no_error (error); g_assert (capability == (MM_MODEM_CAPABILITY_GSM_UMTS | MM_MODEM_CAPABILITY_LTE)); } static void modes_from_string (void) { MMModemMode mode = MM_MODEM_MODE_ANY; GError *error = NULL; mode = mm_common_get_modes_from_string ("not found", &error); g_assert_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS); g_assert (mode == MM_MODEM_MODE_NONE); g_clear_error (&error); mode = mm_common_get_modes_from_string ("3g", &error); g_assert_no_error (error); g_assert (mode == MM_MODEM_MODE_3G); mode = mm_common_get_modes_from_string ("3g|mode-unknown", &error); g_assert_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS); g_assert (mode == MM_MODEM_MODE_NONE); g_clear_error (&error); mode = mm_common_get_modes_from_string ("3g|4g", &error); g_assert_no_error (error); g_assert (mode == (MM_MODEM_MODE_3G | MM_MODEM_MODE_4G)); } static void bands_from_string (void) { MMModemBand *bands = NULL; guint n_bands = 0; gboolean ret = FALSE; GError *error = NULL; ret = mm_common_get_bands_from_string ("not found", &bands, &n_bands, &error); g_assert_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS); g_assert_false (ret); g_assert_cmpuint (n_bands, ==, 0); g_assert_null (bands); g_clear_error (&error); ret = mm_common_get_bands_from_string ("eutran-9", &bands, &n_bands, &error); g_assert_no_error (error); g_assert_true (ret); g_assert_cmpuint (n_bands, ==, 1); g_assert (bands[0] == MM_MODEM_BAND_EUTRAN_9); g_clear_pointer(&bands, g_free); ret = mm_common_get_bands_from_string ("eutran-9|band-unknown", &bands, &n_bands, &error); g_assert_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS); g_assert_false (ret); g_assert_cmpuint (n_bands, ==, 0); g_assert_null (bands); g_clear_error (&error); ret = mm_common_get_bands_from_string ("eutran-9|cdma-bc7", &bands, &n_bands, &error); g_assert_no_error (error); g_assert_true (ret); g_assert_cmpuint (n_bands, ==, 2); g_assert (bands[0] == MM_MODEM_BAND_EUTRAN_9); g_assert (bands[1] == MM_MODEM_BAND_CDMA_BC7); g_clear_pointer(&bands, g_free); } static void boolean_from_string (void) { gboolean ret = FALSE; GError *error = NULL; ret = mm_common_get_boolean_from_string ("not found", &error); g_assert_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS); g_assert_false (ret); g_clear_error (&error); ret = mm_common_get_boolean_from_string ("true", &error); g_assert_no_error (error); g_assert_true (ret); ret = mm_common_get_boolean_from_string ("1", &error); g_assert_no_error (error); g_assert_true (ret); ret = mm_common_get_boolean_from_string ("yes", &error); g_assert_no_error (error); g_assert_true (ret); ret = mm_common_get_boolean_from_string ("false", &error); g_assert_no_error (error); g_assert_false (ret); ret = mm_common_get_boolean_from_string ("0", &error); g_assert_no_error (error); g_assert_false (ret); ret = mm_common_get_boolean_from_string ("no", &error); g_assert_no_error (error); g_assert_false (ret); } static void rm_protocol_from_string (void) { MMModemCdmaRmProtocol rm_protocol = MM_MODEM_CDMA_RM_PROTOCOL_STU_III; GError *error = NULL; rm_protocol = mm_common_get_rm_protocol_from_string ("not found", &error); g_assert_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS); g_assert (rm_protocol == MM_MODEM_CDMA_RM_PROTOCOL_UNKNOWN); g_clear_error (&error); rm_protocol = mm_common_get_rm_protocol_from_string ("packet-network-ppp", &error); g_assert_no_error (error); g_assert (rm_protocol == MM_MODEM_CDMA_RM_PROTOCOL_PACKET_NETWORK_PPP); } static void ip_type_from_string (void) { MMBearerIpFamily ip_type = MM_BEARER_IP_FAMILY_ANY; GError *error = NULL; ip_type = mm_common_get_ip_type_from_string("not found", &error); g_assert_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS); g_assert (ip_type == MM_BEARER_IP_FAMILY_NONE); g_clear_error (&error); ip_type = mm_common_get_ip_type_from_string ("ipv4v6", &error); g_assert_no_error (error); g_assert (ip_type == MM_BEARER_IP_FAMILY_IPV4V6); ip_type = mm_common_get_ip_type_from_string ("ipv4v6|type-unknown", &error); g_assert_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS); g_assert (ip_type == MM_BEARER_IP_FAMILY_NONE); g_clear_error (&error); ip_type = mm_common_get_ip_type_from_string ("ipv4|ipv6", &error); g_assert_no_error (error); g_assert (ip_type == (MM_BEARER_IP_FAMILY_IPV4 | MM_BEARER_IP_FAMILY_IPV6)); } static void allowed_auth_from_string (void) { MMBearerAllowedAuth allowed_auth = MM_BEARER_ALLOWED_AUTH_EAP; GError *error = NULL; allowed_auth = mm_common_get_allowed_auth_from_string ("not found", &error); g_assert_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS); g_assert (allowed_auth == MM_BEARER_ALLOWED_AUTH_UNKNOWN); g_clear_error (&error); allowed_auth = mm_common_get_allowed_auth_from_string ("pap", &error); g_assert_no_error (error); g_assert (allowed_auth == MM_BEARER_ALLOWED_AUTH_PAP); allowed_auth = mm_common_get_allowed_auth_from_string ("pap|auth-unknown", &error); g_assert_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS); g_assert (allowed_auth == MM_BEARER_ALLOWED_AUTH_UNKNOWN); g_clear_error (&error); allowed_auth = mm_common_get_allowed_auth_from_string ("pap|chap", &error); g_assert_no_error (error); g_assert (allowed_auth == (MM_BEARER_ALLOWED_AUTH_PAP | MM_BEARER_ALLOWED_AUTH_CHAP)); } static void sms_storage_from_string (void) { MMSmsStorage sms_storage = MM_SMS_STORAGE_TA; GError *error = NULL; sms_storage = mm_common_get_sms_storage_from_string("not found", &error); g_assert_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS); g_assert (sms_storage == MM_SMS_STORAGE_UNKNOWN); g_clear_error (&error); sms_storage = mm_common_get_sms_storage_from_string ("bm", &error); g_assert_no_error (error); g_assert (sms_storage == MM_SMS_STORAGE_BM); } static void sms_cdma_teleservice_id_from_string (void) { MMSmsCdmaTeleserviceId sms_cdma_teleservice_id = MM_SMS_CDMA_TELESERVICE_ID_CATPT; GError *error = NULL; sms_cdma_teleservice_id = mm_common_get_sms_cdma_teleservice_id_from_string ("not found", &error); g_assert_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS); g_assert (sms_cdma_teleservice_id == MM_SMS_CDMA_TELESERVICE_ID_UNKNOWN); g_clear_error (&error); sms_cdma_teleservice_id = mm_common_get_sms_cdma_teleservice_id_from_string ("wemt", &error); g_assert_no_error (error); g_assert (sms_cdma_teleservice_id == MM_SMS_CDMA_TELESERVICE_ID_WEMT); } static void sms_cdma_service_category_from_string (void) { MMSmsCdmaServiceCategory sms_cdma_service_category = MM_SMS_CDMA_SERVICE_CATEGORY_LODGINGS; GError *error = NULL; sms_cdma_service_category = mm_common_get_sms_cdma_service_category_from_string ("not found", &error); g_assert_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS); g_assert (sms_cdma_service_category == MM_SMS_CDMA_SERVICE_CATEGORY_UNKNOWN); g_clear_error (&error); sms_cdma_service_category = mm_common_get_sms_cdma_service_category_from_string ("lodgings", &error); g_assert_no_error (error); g_assert (sms_cdma_service_category == MM_SMS_CDMA_SERVICE_CATEGORY_LODGINGS); } static void call_direction_from_string (void) { MMCallDirection call_direction = MM_CALL_DIRECTION_OUTGOING; GError *error = NULL; call_direction = mm_common_get_call_direction_from_string ("not found", &error); g_assert_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS); g_assert (call_direction == MM_CALL_DIRECTION_UNKNOWN); g_clear_error (&error); call_direction = mm_common_get_call_direction_from_string ("incoming", &error); g_assert_no_error (error); g_assert (call_direction == MM_CALL_DIRECTION_INCOMING); } static void call_state_from_string (void) { MMCallState call_state = MM_CALL_STATE_RINGING_IN; GError *error = NULL; call_state = mm_common_get_call_state_from_string ("not found", &error); g_assert_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS); g_assert (call_state == MM_CALL_STATE_UNKNOWN); g_clear_error (&error); call_state = mm_common_get_call_state_from_string ("waiting", &error); g_assert_no_error (error); g_assert (call_state == MM_CALL_STATE_WAITING); } static void call_state_reason_from_string (void) { MMCallStateReason call_state_reason = MM_CALL_STATE_REASON_TERMINATED; GError *error = NULL; call_state_reason = mm_common_get_call_state_reason_from_string ("not found", &error); g_assert_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS); g_assert (call_state_reason == MM_CALL_STATE_REASON_UNKNOWN); g_clear_error (&error); call_state_reason = mm_common_get_call_state_reason_from_string ("refused-or-busy", &error); g_assert_no_error (error); g_assert (call_state_reason == MM_CALL_STATE_REASON_REFUSED_OR_BUSY); } static void oma_features_from_string (void) { MMOmaFeature oma_features = MM_OMA_FEATURE_HANDS_FREE_ACTIVATION; GError *error = NULL; oma_features = mm_common_get_oma_features_from_string ("not found", &error); g_assert_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS); g_assert (oma_features == MM_OMA_FEATURE_NONE); g_clear_error (&error); oma_features = mm_common_get_oma_features_from_string ("device-provisioning", &error); g_assert_no_error (error); g_assert (oma_features == MM_OMA_FEATURE_DEVICE_PROVISIONING); oma_features = mm_common_get_oma_features_from_string ("device-provisioning|oma-unknown", &error); g_assert_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS); g_assert (oma_features == MM_OMA_FEATURE_NONE); g_clear_error (&error); oma_features = mm_common_get_oma_features_from_string ("device-provisioning|prl-update", &error); g_assert_no_error (error); g_assert (oma_features == (MM_OMA_FEATURE_DEVICE_PROVISIONING | MM_OMA_FEATURE_PRL_UPDATE)); } static void oma_session_type_from_string (void) { MMOmaSessionType oma_session_type = MM_OMA_SESSION_TYPE_NETWORK_INITIATED_DEVICE_CONFIGURE; GError *error = NULL; oma_session_type = mm_common_get_oma_session_type_from_string ("not found", &error); g_assert_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS); g_assert (oma_session_type == MM_OMA_SESSION_TYPE_UNKNOWN); g_clear_error (&error); oma_session_type = mm_common_get_oma_session_type_from_string ("device-initiated-prl-update", &error); g_assert_no_error (error); g_assert (oma_session_type == MM_OMA_SESSION_TYPE_DEVICE_INITIATED_PRL_UPDATE); } static void eps_ue_mode_operation_from_string (void) { MMModem3gppEpsUeModeOperation eps_ue_mode_opearation = MM_MODEM_3GPP_EPS_UE_MODE_OPERATION_CSPS_1; GError *error = NULL; eps_ue_mode_opearation = mm_common_get_eps_ue_mode_operation_from_string ("not found", &error); g_assert_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS); g_assert (eps_ue_mode_opearation == MM_MODEM_3GPP_EPS_UE_MODE_OPERATION_UNKNOWN); g_clear_error (&error); eps_ue_mode_opearation = mm_common_get_eps_ue_mode_operation_from_string ("ps-2", &error); g_assert_no_error (error); g_assert (eps_ue_mode_opearation == MM_MODEM_3GPP_EPS_UE_MODE_OPERATION_PS_2); } static void access_technology_from_string (void) { MMModemAccessTechnology access_technology = MM_MODEM_ACCESS_TECHNOLOGY_ANY; GError *error = NULL; access_technology = mm_common_get_access_technology_from_string ("not found", &error); g_assert_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS); g_assert (access_technology == MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN); g_clear_error (&error); access_technology = mm_common_get_access_technology_from_string ("hsdpa", &error); g_assert_no_error (error); g_assert (access_technology == MM_MODEM_ACCESS_TECHNOLOGY_HSDPA); access_technology = mm_common_get_access_technology_from_string ("hsdpa|access-unknown", &error); g_assert_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS); g_assert (access_technology == MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN); g_clear_error (&error); access_technology = mm_common_get_access_technology_from_string ("hsdpa|hspa-plus", &error); g_assert_no_error (error); g_assert (access_technology == (MM_MODEM_ACCESS_TECHNOLOGY_HSDPA | MM_MODEM_ACCESS_TECHNOLOGY_HSPA_PLUS)); } static void multiplex_support_from_string (void) { MMBearerMultiplexSupport multiplex_support = MM_BEARER_MULTIPLEX_SUPPORT_REQUIRED; GError *error = NULL; multiplex_support = mm_common_get_multiplex_support_from_string ("not found", &error); g_assert_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS); g_assert (multiplex_support == MM_BEARER_MULTIPLEX_SUPPORT_UNKNOWN); g_clear_error (&error); multiplex_support = mm_common_get_multiplex_support_from_string ("requested", &error); g_assert_no_error (error); g_assert (multiplex_support == MM_BEARER_MULTIPLEX_SUPPORT_REQUESTED); } static void apn_type_from_string (void) { MMBearerApnType apn_type = MM_BEARER_APN_TYPE_DEFAULT; GError *error = NULL; apn_type = mm_common_get_apn_type_from_string ("not found", &error); g_assert_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS); g_assert (apn_type == MM_BEARER_APN_TYPE_NONE); g_clear_error (&error); apn_type = mm_common_get_apn_type_from_string ("emergency", &error); g_assert_no_error (error); g_assert (apn_type == MM_BEARER_APN_TYPE_EMERGENCY); apn_type = mm_common_get_apn_type_from_string ("emergency|type-unknown", &error); g_assert_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS); g_assert (apn_type == MM_BEARER_APN_TYPE_NONE); g_clear_error (&error); apn_type = mm_common_get_apn_type_from_string ("emergency|local", &error); g_assert_no_error (error); g_assert (apn_type == (MM_BEARER_APN_TYPE_EMERGENCY | MM_BEARER_APN_TYPE_LOCAL)); } static void _3gpp_facility_from_string (void) { MMModem3gppFacility facility = MM_MODEM_3GPP_FACILITY_CORP_PERS; GError *error = NULL; facility = mm_common_get_3gpp_facility_from_string ("not found", &error); g_assert_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS); g_assert (facility == MM_MODEM_3GPP_FACILITY_NONE); g_clear_error (&error); facility = mm_common_get_3gpp_facility_from_string ("ph-sim", &error); g_assert_no_error (error); g_assert (facility == MM_MODEM_3GPP_FACILITY_PH_SIM); facility = mm_common_get_3gpp_facility_from_string ("ph-sim|type-unknown", &error); g_assert_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS); g_assert (facility == MM_MODEM_3GPP_FACILITY_NONE); g_clear_error (&error); facility = mm_common_get_3gpp_facility_from_string ("ph-fsim|provider-pers", &error); g_assert_no_error (error); g_assert (facility == (MM_MODEM_3GPP_FACILITY_PH_FSIM | MM_MODEM_3GPP_FACILITY_PROVIDER_PERS)); } static void _3gpp_packet_service_state_from_string (void) { MMModem3gppPacketServiceState packet_service_state = MM_MODEM_3GPP_PACKET_SERVICE_STATE_DETACHED; GError *error = NULL; packet_service_state = mm_common_get_3gpp_packet_service_state_from_string ("not found", &error); g_assert_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS); g_assert (packet_service_state == MM_MODEM_3GPP_PACKET_SERVICE_STATE_UNKNOWN); g_clear_error (&error); packet_service_state = mm_common_get_3gpp_packet_service_state_from_string ("attached", &error); g_assert_no_error (error); g_assert (packet_service_state == MM_MODEM_3GPP_PACKET_SERVICE_STATE_ATTACHED); } static void _3gpp_mico_mode_from_string (void) { MMModem3gppMicoMode mico_mode = MM_MODEM_3GPP_MICO_MODE_UNSUPPORTED; GError *error = NULL; mico_mode = mm_common_get_3gpp_mico_mode_from_string ("not found", &error); g_assert_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS); g_assert (mico_mode == MM_MODEM_3GPP_MICO_MODE_UNKNOWN); g_clear_error (&error); mico_mode = mm_common_get_3gpp_mico_mode_from_string ("enabled", &error); g_assert_no_error (error); g_assert (mico_mode == MM_MODEM_3GPP_MICO_MODE_ENABLED); } static void _3gpp_drx_cycle_from_string (void) { MMModem3gppDrxCycle drx_cycle = MM_MODEM_3GPP_DRX_CYCLE_UNSUPPORTED; GError *error = NULL; drx_cycle = mm_common_get_3gpp_drx_cycle_from_string ("not found", &error); g_assert_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS); g_assert (drx_cycle == MM_MODEM_3GPP_DRX_CYCLE_UNKNOWN); g_clear_error (&error); drx_cycle = mm_common_get_3gpp_drx_cycle_from_string ("128", &error); g_assert_no_error (error); g_assert (drx_cycle == MM_MODEM_3GPP_DRX_CYCLE_128); } static void access_type_preference_from_string (void) { MMBearerAccessTypePreference access_type_preference = MM_BEARER_ACCESS_TYPE_PREFERENCE_NON_3GPP_ONLY; GError *error = NULL; access_type_preference = mm_common_get_access_type_preference_from_string ("not found", &error); g_assert_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS); g_assert (access_type_preference == MM_BEARER_ACCESS_TYPE_PREFERENCE_NONE); g_clear_error (&error); access_type_preference = mm_common_get_access_type_preference_from_string ("3gpp-preferred", &error); g_assert_no_error (error); g_assert (access_type_preference == MM_BEARER_ACCESS_TYPE_PREFERENCE_3GPP_PREFERRED); } static void profile_source_from_string (void) { MMBearerProfileSource profile_source = MM_BEARER_PROFILE_SOURCE_OPERATOR; GError *error = NULL; profile_source = mm_common_get_profile_source_from_string ("not found", &error); g_assert_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS); g_assert (profile_source == MM_BEARER_PROFILE_SOURCE_UNKNOWN); g_clear_error (&error); profile_source = mm_common_get_profile_source_from_string ("modem", &error); g_assert_no_error (error); g_assert (profile_source == MM_BEARER_PROFILE_SOURCE_MODEM); } /**************************************************************/ int main (int argc, char **argv) { g_test_init (&argc, &argv, NULL); g_test_add_func ("/MM/Common/KeyValue/standard", key_value_test_standard); g_test_add_func ("/MM/Common/KeyValue/spaces", key_value_test_spaces); g_test_add_func ("/MM/Common/KeyValue/double-quotes", key_value_test_double_quotes); g_test_add_func ("/MM/Common/KeyValue/single-quotes", key_value_test_single_quotes); g_test_add_func ("/MM/Common/KeyValue/empty-value", key_value_test_empty_value); g_test_add_func ("/MM/Common/KeyValue/empty-string", key_value_test_empty_string); g_test_add_func ("/MM/Common/KeyValue/Error/no-first-key", key_value_error_test_no_first_key); g_test_add_func ("/MM/Common/KeyValue/Error/no-key", key_value_error_test_no_key); g_test_add_func ("/MM/Common/KeyValue/Error/missing-double-quotes-0", key_value_error_test_missing_double_quotes_0); g_test_add_func ("/MM/Common/KeyValue/Error/missing-double-quotes-1", key_value_error_test_missing_double_quotes_1); g_test_add_func ("/MM/Common/KeyValue/Error/missing-double-quotes-2", key_value_error_test_missing_double_quotes_2); g_test_add_func ("/MM/Common/KeyValue/Error/missing-single-quotes-0", key_value_error_test_missing_single_quotes_0); g_test_add_func ("/MM/Common/KeyValue/Error/missing-single-quotes-1", key_value_error_test_missing_single_quotes_1); g_test_add_func ("/MM/Common/KeyValue/Error/missing-single-quotes-2", key_value_error_test_missing_single_quotes_2); g_test_add_func ("/MM/Common/KeyValue/Error/missing-comma-0", key_value_error_test_missing_comma_0); g_test_add_func ("/MM/Common/KeyValue/Error/missing-comma-1", key_value_error_test_missing_comma_1); g_test_add_func ("/MM/Common/KeyValue/Error/missing-comma-2", key_value_error_test_missing_comma_2); g_test_add_func ("/MM/Common/BandArray/Cmp/equal-empty", band_array_cmp_test_equal_empty); g_test_add_func ("/MM/Common/BandArray/Cmp/equal-one", band_array_cmp_test_equal_one); g_test_add_func ("/MM/Common/BandArray/Cmp/equal-multiple-same-order", band_array_cmp_test_equal_multiple_same_order); g_test_add_func ("/MM/Common/BandArray/Cmp/equal-multiple-different-order", band_array_cmp_test_equal_multiple_different_order); g_test_add_func ("/MM/Common/BandArray/Cmp/different-one", band_array_cmp_test_different_one); g_test_add_func ("/MM/Common/BandArray/Cmp/different-none", band_array_cmp_test_different_none); g_test_add_func ("/MM/Common/BandArray/Cmp/different-multiple-1", band_array_cmp_test_different_multiple_1); g_test_add_func ("/MM/Common/BandArray/Cmp/different-multiple-2", band_array_cmp_test_different_multiple_2); g_test_add_func ("/MM/Common/FieldParsers/Int", field_parser_int); g_test_add_func ("/MM/Common/FieldParsers/Uint", field_parser_uint); g_test_add_func ("/MM/Common/FieldParsers/Double", field_parser_double); g_test_add_func ("/MM/Common/HexStr/lower-case", hexstr_lower_case); g_test_add_func ("/MM/Common/HexStr/upper-case", hexstr_upper_case); g_test_add_func ("/MM/Common/HexStr/mixed-case", hexstr_mixed_case); g_test_add_func ("/MM/Common/HexStr/missing-empty", hexstr_empty); g_test_add_func ("/MM/Common/HexStr/missing-digits", hexstr_missing_digits); g_test_add_func ("/MM/Common/HexStr/wrong-digits-all", hexstr_wrong_digits_all); g_test_add_func ("/MM/Common/HexStr/wrong-digits-some", hexstr_wrong_digits_some); g_test_add_func ("/MM/Common/DateTime/iso8601", date_time_iso8601); g_test_add_func ("/MM/Common/StrConvTo/bands", bands_to_string); g_test_add_func ("/MM/Common/StrConvTo/capabilities", capabilities_to_string); g_test_add_func ("/MM/Common/StrConvTo/mode-combinations", mode_combinations_to_string); g_test_add_func ("/MM/Common/StrConvTo/ports", ports_to_string); g_test_add_func ("/MM/Common/StrConvTo/sms-storages", sms_storages_to_string); g_test_add_func ("/MM/Common/StrConvFrom/capabilities", capabilities_from_string); g_test_add_func ("/MM/Common/StrConvFrom/modes", modes_from_string); g_test_add_func ("/MM/Common/StrConvFrom/bands", bands_from_string); g_test_add_func ("/MM/Common/StrConvFrom/boolean", boolean_from_string); g_test_add_func ("/MM/Common/StrConvFrom/rm_protocol", rm_protocol_from_string); g_test_add_func ("/MM/Common/StrConvFrom/ip_type", ip_type_from_string); g_test_add_func ("/MM/Common/StrConvFrom/allowed_auth", allowed_auth_from_string); g_test_add_func ("/MM/Common/StrConvFrom/sms_storage", sms_storage_from_string); g_test_add_func ("/MM/Common/StrConvFrom/sms_cdma_teleservice_id", sms_cdma_teleservice_id_from_string); g_test_add_func ("/MM/Common/StrConvFrom/sms_cdma_service_category", sms_cdma_service_category_from_string); g_test_add_func ("/MM/Common/StrConvFrom/call_direction", call_direction_from_string); g_test_add_func ("/MM/Common/StrConvFrom/call_state", call_state_from_string); g_test_add_func ("/MM/Common/StrConvFrom/call_state_reason", call_state_reason_from_string); g_test_add_func ("/MM/Common/StrConvFrom/oma_features", oma_features_from_string); g_test_add_func ("/MM/Common/StrConvFrom/oma_session_type", oma_session_type_from_string); g_test_add_func ("/MM/Common/StrConvFrom/eps_ue_mode_operation", eps_ue_mode_operation_from_string); g_test_add_func ("/MM/Common/StrConvFrom/access_technology", access_technology_from_string); g_test_add_func ("/MM/Common/StrConvFrom/multiplex_support", multiplex_support_from_string); g_test_add_func ("/MM/Common/StrConvFrom/apn_type", apn_type_from_string); g_test_add_func ("/MM/Common/StrConvFrom/3gpp_facility", _3gpp_facility_from_string); g_test_add_func ("/MM/Common/StrConvFrom/3gpp_packet_service_state", _3gpp_packet_service_state_from_string); g_test_add_func ("/MM/Common/StrConvFrom/3gpp_mico_mode", _3gpp_mico_mode_from_string); g_test_add_func ("/MM/Common/StrConvFrom/3gpp_drx_cycle", _3gpp_drx_cycle_from_string); g_test_add_func ("/MM/Common/StrConvFrom/access_type", access_type_preference_from_string); g_test_add_func ("/MM/Common/StrConvFrom/profile_source", profile_source_from_string); return g_test_run (); } ModemManager-1.23.4-dev/libmm-glib/tests/test-pco.c000066400000000000000000000063221456466623000220070ustar00rootroot00000000000000/* -*- 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 2018 Google LLC. */ #include #include #include typedef struct { guint32 session_id; gboolean is_complete; gsize pco_data_size; guint8 pco_data[50]; } TestPco; static const TestPco test_pco_list[] = { { 3, TRUE, 8, { 0x27, 0x06, 0x80, 0x00, 0x10, 0x02, 0x05, 0x94 } }, { 1, FALSE, 3, { 0x27, 0x01, 0x80 } }, { 5, FALSE, 10, { 0x27, 0x08, 0x80, 0xFF, 0x00, 0x04, 0x13, 0x01, 0x84, 0x05 } }, { 4, TRUE, 14, { 0x27, 0x0C, 0x80, 0x10, 0x02, 0x05, 0x94, 0xFF, 0x00, 0x04, 0x13, 0x01, 0x84, 0x05 } }, { 3, FALSE, 10, { 0x27, 0x08, 0x80, 0x00, 0x0D, 0x04, 0xC6, 0xE0, 0xAD, 0x87 } }, }; static const TestPco expected_pco_list[] = { { 1, FALSE, 3, { 0x27, 0x01, 0x80 } }, { 3, FALSE, 10, { 0x27, 0x08, 0x80, 0x00, 0x0D, 0x04, 0xC6, 0xE0, 0xAD, 0x87 } }, { 4, TRUE, 14, { 0x27, 0x0C, 0x80, 0x10, 0x02, 0x05, 0x94, 0xFF, 0x00, 0x04, 0x13, 0x01, 0x84, 0x05 } }, { 5, FALSE, 10, { 0x27, 0x08, 0x80, 0xFF, 0x00, 0x04, 0x13, 0x01, 0x84, 0x05 } }, }; static void test_pco_list_add (void) { GList *list = NULL; guint i; for (i = 0; i < G_N_ELEMENTS (test_pco_list); ++i) { const TestPco *test_pco = &test_pco_list[i]; MMPco *pco; pco = mm_pco_new (); mm_pco_set_session_id (pco, test_pco->session_id); mm_pco_set_complete (pco, test_pco->is_complete); mm_pco_set_data (pco, test_pco->pco_data, test_pco->pco_data_size); list = mm_pco_list_add (list, pco); } g_assert (list != NULL); g_assert_cmpuint (g_list_length (list), ==, G_N_ELEMENTS (expected_pco_list)); for (i = 0; i < G_N_ELEMENTS (expected_pco_list); ++i) { GList *current; MMPco *pco; const TestPco *expected_pco; gsize pco_data_size; const guint8 *pco_data; current = g_list_nth (list, i); pco = current->data; expected_pco = &expected_pco_list[i]; g_assert (pco != NULL); g_assert_cmpuint (mm_pco_get_session_id (pco), ==, expected_pco->session_id); g_assert (mm_pco_is_complete (pco) == expected_pco->is_complete); pco_data = mm_pco_get_data (pco, &pco_data_size); g_assert (pco_data != NULL); g_assert_cmpuint (pco_data_size, ==, expected_pco->pco_data_size); g_assert_cmpint (memcmp (pco_data, expected_pco->pco_data, pco_data_size), ==, 0); } g_list_free_full (list, g_object_unref); } /**************************************************************/ int main (int argc, char **argv) { g_test_init (&argc, &argv, NULL); g_test_add_func ("/MM/Pco/pco-list-add", test_pco_list_add); return g_test_run (); } ModemManager-1.23.4-dev/libqcdm/000077500000000000000000000000001456466623000163405ustar00rootroot00000000000000ModemManager-1.23.4-dev/libqcdm/src/000077500000000000000000000000001456466623000171275ustar00rootroot00000000000000ModemManager-1.23.4-dev/libqcdm/src/com.c000066400000000000000000000034321456466623000200530ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * Copyright (C) 2010 Red Hat, Inc. * * This program is free software: you can redistribute it and/or * modify it under the terms of version 2 of the GNU General Public * License as published by the Free Software Foundation * * 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, see . */ #include #include #include #include #include "com.h" #include "errors.h" int qcdm_port_setup (int fd) { struct termios stbuf; errno = 0; memset (&stbuf, 0, sizeof (stbuf)); if (tcgetattr (fd, &stbuf) != 0) { qcdm_err (0, "tcgetattr() error: %d", errno); return -QCDM_ERROR_SERIAL_CONFIG_FAILED; } stbuf.c_cflag &= ~(CBAUD | CSIZE | CSTOPB | CLOCAL | PARENB); stbuf.c_iflag &= ~(HUPCL | IUTF8 | IUCLC | ISTRIP | IXON | IXOFF | IXANY | ICRNL); stbuf.c_oflag &= ~(OPOST | OCRNL | ONLCR | OLCUC | ONLRET); stbuf.c_lflag &= ~(ICANON | ISIG | IEXTEN | ECHO | ECHOE | ECHOK | ECHONL); stbuf.c_lflag &= ~(NOFLSH | TOSTOP | ECHOPRT | ECHOCTL | ECHOKE); stbuf.c_cc[VMIN] = 1; stbuf.c_cc[VTIME] = 0; stbuf.c_cc[VEOF] = 1; stbuf.c_cflag |= (B115200 | CS8 | CREAD | 0 | 0); /* No parity, 1 stop bit */ errno = 0; if (tcsetattr (fd, TCSANOW, &stbuf) < 0) { qcdm_err (0, "tcgetattr() error: %d", errno); return -QCDM_ERROR_SERIAL_CONFIG_FAILED; } return QCDM_SUCCESS; } ModemManager-1.23.4-dev/libqcdm/src/com.h000066400000000000000000000015001456466623000200520ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * Copyright (C) 2010 Red Hat, Inc. * * This program is free software: you can redistribute it and/or * modify it under the terms of version 2 of the GNU General Public * License as published by the Free Software Foundation * * 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, see . */ #ifndef LIBQCDM_COM_H #define LIBQCDM_COM_H #include "utils.h" int qcdm_port_setup (int fd); #endif /* LIBQCDM_COM_H */ ModemManager-1.23.4-dev/libqcdm/src/commands.c000066400000000000000000002050721456466623000211020ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * Copyright (C) 2010 Red Hat, Inc. * * This program is free software: you can redistribute it and/or * modify it under the terms of version 2 of the GNU General Public * License as published by the Free Software Foundation * * 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, see . */ #include #include #include #include "commands.h" #include "errors.h" #include "dm-commands.h" #include "nv-items.h" #include "result-private.h" #include "utils.h" /**********************************************************************/ static uint8_t cdma_prev_to_qcdm (uint8_t cdma) { switch (cdma) { case CDMA_PREV_IS_95: return QCDM_CDMA_PREV_IS_95; case CDMA_PREV_IS_95A: return QCDM_CDMA_PREV_IS_95A; case CDMA_PREV_IS_95A_TSB74: return QCDM_CDMA_PREV_IS_95A_TSB74; case CDMA_PREV_IS_95B_PHASE1: return QCDM_CDMA_PREV_IS_95B_PHASE1; case CDMA_PREV_IS_95B_PHASE2: return QCDM_CDMA_PREV_IS_95B_PHASE2; case CDMA_PREV_IS2000_REL0: return QCDM_CDMA_PREV_IS2000_REL0; case CDMA_PREV_IS2000_RELA: return QCDM_CDMA_PREV_IS2000_RELA; default: break; } return QCDM_CDMA_PREV_UNKNOWN; } static uint8_t cdma_band_class_to_qcdm (uint8_t cdma) { switch (cdma) { case CDMA_BAND_CLASS_0_CELLULAR_800: return QCDM_CDMA_BAND_CLASS_0_CELLULAR_800; case CDMA_BAND_CLASS_1_PCS: return QCDM_CDMA_BAND_CLASS_1_PCS; case CDMA_BAND_CLASS_2_TACS: return QCDM_CDMA_BAND_CLASS_2_TACS; case CDMA_BAND_CLASS_3_JTACS: return QCDM_CDMA_BAND_CLASS_3_JTACS; case CDMA_BAND_CLASS_4_KOREAN_PCS: return QCDM_CDMA_BAND_CLASS_4_KOREAN_PCS; case CDMA_BAND_CLASS_5_NMT450: return QCDM_CDMA_BAND_CLASS_5_NMT450; case CDMA_BAND_CLASS_6_IMT2000: return QCDM_CDMA_BAND_CLASS_6_IMT2000; case CDMA_BAND_CLASS_7_CELLULAR_700: return QCDM_CDMA_BAND_CLASS_7_CELLULAR_700; case CDMA_BAND_CLASS_8_1800: return QCDM_CDMA_BAND_CLASS_8_1800; case CDMA_BAND_CLASS_9_900: return QCDM_CDMA_BAND_CLASS_9_900; case CDMA_BAND_CLASS_10_SECONDARY_800: return QCDM_CDMA_BAND_CLASS_10_SECONDARY_800; case CDMA_BAND_CLASS_11_PAMR_400: return QCDM_CDMA_BAND_CLASS_11_PAMR_400; case CDMA_BAND_CLASS_12_PAMR_800: return QCDM_CDMA_BAND_CLASS_12_PAMR_800; case CDMA_BAND_CLASS_13_IMT2000_2500: return QCDM_CDMA_BAND_CLASS_13_IMT2000_2500; case CDMA_BAND_CLASS_14_US_PCS_1900: return QCDM_CDMA_BAND_CLASS_14_US_PCS_1900; case CDMA_BAND_CLASS_15_AWS: return QCDM_CDMA_BAND_CLASS_15_AWS; case CDMA_BAND_CLASS_16_US_2500: return QCDM_CDMA_BAND_CLASS_16_US_2500; case CDMA_BAND_CLASS_17_US_FLO_2500: return QCDM_CDMA_BAND_CLASS_17_US_FLO_2500; case CDMA_BAND_CLASS_18_US_PS_700: return QCDM_CDMA_BAND_CLASS_18_US_PS_700; case CDMA_BAND_CLASS_19_US_LOWER_700: return QCDM_CDMA_BAND_CLASS_19_US_LOWER_700; default: break; } return QCDM_CDMA_BAND_CLASS_UNKNOWN; } static uint8_t nv_mode_pref_from_qcdm (uint8_t qcdm) { switch (qcdm) { case QCDM_CMD_NV_MODE_PREF_ITEM_MODE_PREF_DIGITAL: return DIAG_NV_MODE_PREF_DIGITAL; case QCDM_CMD_NV_MODE_PREF_ITEM_MODE_PREF_DIGITAL_ONLY: return DIAG_NV_MODE_PREF_DIGITAL_ONLY; case QCDM_CMD_NV_MODE_PREF_ITEM_MODE_PREF_ANALOG: return DIAG_NV_MODE_PREF_ANALOG; case QCDM_CMD_NV_MODE_PREF_ITEM_MODE_PREF_ANALOG_ONLY: return DIAG_NV_MODE_PREF_ANALOG_ONLY; case QCDM_CMD_NV_MODE_PREF_ITEM_MODE_PREF_AUTO: return DIAG_NV_MODE_PREF_AUTO; case QCDM_CMD_NV_MODE_PREF_ITEM_MODE_PREF_1X_ONLY: return DIAG_NV_MODE_PREF_1X_ONLY; case QCDM_CMD_NV_MODE_PREF_ITEM_MODE_PREF_HDR_ONLY: return DIAG_NV_MODE_PREF_HDR_ONLY; case QCDM_CMD_NV_MODE_PREF_ITEM_MODE_PREF_GPRS_ONLY: return DIAG_NV_MODE_PREF_GPRS_ONLY; case QCDM_CMD_NV_MODE_PREF_ITEM_MODE_PREF_UMTS_ONLY: return DIAG_NV_MODE_PREF_UMTS_ONLY; case QCDM_CMD_NV_MODE_PREF_ITEM_MODE_PREF_GSM_UMTS_ONLY: return DIAG_NV_MODE_PREF_GSM_UMTS_ONLY; case QCDM_CMD_NV_MODE_PREF_ITEM_MODE_PREF_1X_HDR_ONLY: return DIAG_NV_MODE_PREF_1X_HDR_ONLY; case QCDM_CMD_NV_MODE_PREF_ITEM_MODE_PREF_LTE_ONLY: return DIAG_NV_MODE_PREF_LTE_ONLY; case QCDM_CMD_NV_MODE_PREF_ITEM_MODE_PREF_GSM_UMTS_LTE_ONLY: return DIAG_NV_MODE_PREF_GSM_UMTS_LTE_ONLY; case QCDM_CMD_NV_MODE_PREF_ITEM_MODE_PREF_1X_HDR_LTE_ONLY: return DIAG_NV_MODE_PREF_1X_HDR_LTE_ONLY; default: break; } return DIAG_NV_MODE_PREF_AUTO; }; /**********************************************************************/ /* * utils_bin2hexstr * * Convert a byte-array into a hexadecimal string. * * Code originally by Alex Larsson and * copyright Red Hat, Inc. under terms of the LGPL. * */ static char * bin2hexstr (const uint8_t *bytes, int len) { const char hex_digits[] = "0123456789abcdef"; char *result; int i; size_t buflen = (len * 2) + 1; qcdm_return_val_if_fail (bytes != NULL, NULL); qcdm_return_val_if_fail (len > 0, NULL); qcdm_return_val_if_fail (len < 4096, NULL); /* Arbitrary limit */ result = calloc (1, buflen); if (result == NULL) return NULL; for (i = 0; i < len; i++) { result[2*i] = hex_digits[(bytes[i] >> 4) & 0xf]; result[2*i+1] = hex_digits[bytes[i] & 0xf]; } result[buflen - 1] = '\0'; return result; } /**********************************************************************/ static qcdmbool check_command (const char *buf, size_t len, uint8_t cmd, size_t min_len, int *out_error) { if (len < 1) { qcdm_err (0, "DM command response malformed (must be at least 1 byte in length)"); if (out_error) *out_error = -QCDM_ERROR_RESPONSE_MALFORMED; return FALSE; } switch (buf[0]) { case DIAG_CMD_BAD_CMD: qcdm_err (0, "DM command %d unknown or unimplemented by the device", cmd); if (out_error) *out_error = -QCDM_ERROR_RESPONSE_BAD_COMMAND; return FALSE; case DIAG_CMD_BAD_PARM: qcdm_err (0, "DM command %d contained invalid parameter", cmd); if (out_error) *out_error = -QCDM_ERROR_RESPONSE_BAD_PARAMETER; return FALSE; case DIAG_CMD_BAD_LEN: qcdm_err (0, "DM command %d was the wrong size", cmd); if (out_error) *out_error = -QCDM_ERROR_RESPONSE_BAD_LENGTH; return FALSE; case DIAG_CMD_BAD_DEV: qcdm_err (0, "DM command %d was not accepted by the device", cmd); if (out_error) *out_error = -QCDM_ERROR_RESPONSE_NOT_ACCEPTED; return FALSE; case DIAG_CMD_BAD_MODE: qcdm_err (0, "DM command %d not allowed in the current device mode", cmd); if (out_error) *out_error = -QCDM_ERROR_RESPONSE_BAD_MODE; return FALSE; case DIAG_CMD_BAD_SPC_MODE: qcdm_err (0, "DM command %d not allowed because the Service Programming Code is locked", cmd); if (out_error) *out_error = -QCDM_ERROR_SPC_LOCKED; return FALSE; default: break; } if (buf[0] != cmd) { qcdm_err (0, "Unexpected DM command response (expected %d, got %d)", cmd, buf[0]); if (out_error) *out_error = -QCDM_ERROR_RESPONSE_UNEXPECTED; return FALSE; } if (len < min_len) { qcdm_err (0, "DM command %d response not long enough (got %zu, expected " "at least %zu).", cmd, len, min_len); if (out_error) *out_error = -QCDM_ERROR_RESPONSE_BAD_LENGTH; return FALSE; } return TRUE; } static int nv_status_to_qcdm_error (uint16_t status) { switch (status) { case DIAG_NV_STATUS_OK: return QCDM_SUCCESS; case DIAG_NV_STATUS_BUSY: return -QCDM_ERROR_NV_ERROR_BUSY; case DIAG_NV_STATUS_BAD_COMMAND: return -QCDM_ERROR_NV_ERROR_BAD_COMMAND; case DIAG_NV_STATUS_MEMORY_FULL: return -QCDM_ERROR_NV_ERROR_MEMORY_FULL; case DIAG_NV_STATUS_FAILED: return -QCDM_ERROR_NV_ERROR_FAILED; case DIAG_NV_STATUS_INACTIVE: return -QCDM_ERROR_NV_ERROR_INACTIVE; case DIAG_NV_STATUS_BAD_PARAMETER: return -QCDM_ERROR_NV_ERROR_BAD_PARAMETER; case DIAG_NV_STATUS_READ_ONLY: return -QCDM_ERROR_NV_ERROR_READ_ONLY; default: return -QCDM_ERROR_NVCMD_FAILED; } } static qcdmbool check_nv_cmd (DMCmdNVReadWrite *cmd, uint16_t nv_item, int *out_error) { uint16_t cmd_item; qcdm_return_val_if_fail (cmd != NULL, FALSE); qcdm_return_val_if_fail ((cmd->code == DIAG_CMD_NV_READ) || (cmd->code == DIAG_CMD_NV_WRITE), FALSE); /* NV read/write have a status byte at the end */ if (cmd->status != 0) { qcdm_err (0, "The NV operation failed (status 0x%X).", le16toh (cmd->status)); if (out_error) *out_error = nv_status_to_qcdm_error (le16toh (cmd->status)); return FALSE; } cmd_item = le16toh (cmd->nv_item); if (cmd_item != nv_item) { qcdm_err (0, "Unexpected DM NV command response (expected item %d, got " "item %d)", nv_item, cmd_item); if (out_error) *out_error = -QCDM_ERROR_RESPONSE_UNEXPECTED; return FALSE; } return TRUE; } /**********************************************************************/ size_t qcdm_cmd_version_info_new (char *buf, size_t len) { char cmdbuf[3]; DMCmdHeader *cmd = (DMCmdHeader *) &cmdbuf[0]; qcdm_return_val_if_fail (buf != NULL, 0); qcdm_return_val_if_fail (len >= sizeof (*cmd) + DIAG_TRAILER_LEN, 0); memset (cmd, 0, sizeof (*cmd)); cmd->code = DIAG_CMD_VERSION_INFO; return dm_encapsulate_buffer (cmdbuf, sizeof (*cmd), sizeof (cmdbuf), buf, len); } QcdmResult * qcdm_cmd_version_info_result (const char *buf, size_t len, int *out_error) { QcdmResult *result = NULL; DMCmdVersionInfoRsp *rsp = (DMCmdVersionInfoRsp *) buf; char tmp[12]; qcdm_return_val_if_fail (buf != NULL, NULL); if (!check_command (buf, len, DIAG_CMD_VERSION_INFO, sizeof (DMCmdVersionInfoRsp), out_error)) return NULL; result = qcdm_result_new (); memset (tmp, 0, sizeof (tmp)); qcdm_assert (sizeof (rsp->comp_date) <= sizeof (tmp)); memcpy (tmp, rsp->comp_date, sizeof (rsp->comp_date)); qcdm_result_add_string (result, QCDM_CMD_VERSION_INFO_ITEM_COMP_DATE, tmp); memset (tmp, 0, sizeof (tmp)); qcdm_assert (sizeof (rsp->comp_time) <= sizeof (tmp)); memcpy (tmp, rsp->comp_time, sizeof (rsp->comp_time)); qcdm_result_add_string (result, QCDM_CMD_VERSION_INFO_ITEM_COMP_TIME, tmp); memset (tmp, 0, sizeof (tmp)); qcdm_assert (sizeof (rsp->rel_date) <= sizeof (tmp)); memcpy (tmp, rsp->rel_date, sizeof (rsp->rel_date)); qcdm_result_add_string (result, QCDM_CMD_VERSION_INFO_ITEM_RELEASE_DATE, tmp); memset (tmp, 0, sizeof (tmp)); qcdm_assert (sizeof (rsp->rel_time) <= sizeof (tmp)); memcpy (tmp, rsp->rel_time, sizeof (rsp->rel_time)); qcdm_result_add_string (result, QCDM_CMD_VERSION_INFO_ITEM_RELEASE_TIME, tmp); memset (tmp, 0, sizeof (tmp)); qcdm_assert (sizeof (rsp->model) <= sizeof (tmp)); memcpy (tmp, rsp->model, sizeof (rsp->model)); qcdm_result_add_string (result, QCDM_CMD_VERSION_INFO_ITEM_MODEL, tmp); return result; } /**********************************************************************/ size_t qcdm_cmd_esn_new (char *buf, size_t len) { char cmdbuf[3]; DMCmdHeader *cmd = (DMCmdHeader *) &cmdbuf[0]; qcdm_return_val_if_fail (buf != NULL, 0); qcdm_return_val_if_fail (len >= sizeof (*cmd) + DIAG_TRAILER_LEN, 0); memset (cmd, 0, sizeof (*cmd)); cmd->code = DIAG_CMD_ESN; return dm_encapsulate_buffer (cmdbuf, sizeof (*cmd), sizeof (cmdbuf), buf, len); } QcdmResult * qcdm_cmd_esn_result (const char *buf, size_t len, int *out_error) { QcdmResult *result = NULL; DMCmdEsnRsp *rsp = (DMCmdEsnRsp *) buf; char *tmp; uint8_t swapped[4]; qcdm_return_val_if_fail (buf != NULL, NULL); if (!check_command (buf, len, DIAG_CMD_ESN, sizeof (DMCmdEsnRsp), out_error)) return NULL; /* Convert the ESN from binary to a hex string; it's LE so we have to * swap it to get the correct ordering. */ swapped[0] = rsp->esn[3]; swapped[1] = rsp->esn[2]; swapped[2] = rsp->esn[1]; swapped[3] = rsp->esn[0]; tmp = bin2hexstr (&swapped[0], sizeof (swapped)); if (tmp != NULL) { result = qcdm_result_new (); qcdm_result_add_string (result, QCDM_CMD_ESN_ITEM_ESN, tmp); free (tmp); } return result; } /**********************************************************************/ size_t qcdm_cmd_control_new (char *buf, size_t len, uint8_t mode) { char cmdbuf[5]; DMCmdControl *cmd = (DMCmdControl *) &cmdbuf[0]; qcdm_return_val_if_fail (buf != NULL, 0); qcdm_return_val_if_fail (len >= sizeof (*cmd) + DIAG_TRAILER_LEN, 0); memset (cmd, 0, sizeof (*cmd)); cmd->code = DIAG_CMD_CONTROL; cmd->mode = htole16 ((uint16_t) mode); return dm_encapsulate_buffer (cmdbuf, sizeof (*cmd), sizeof (cmdbuf), buf, len); } QcdmResult * qcdm_cmd_control_result (const char *buf, size_t len, int *out_error) { qcdm_return_val_if_fail (buf != NULL, NULL); if (!check_command (buf, len, DIAG_CMD_CONTROL, sizeof (DMCmdControl), out_error)) return NULL; return qcdm_result_new (); } /**********************************************************************/ size_t qcdm_cmd_cdma_status_new (char *buf, size_t len) { char cmdbuf[3]; DMCmdHeader *cmd = (DMCmdHeader *) &cmdbuf[0]; qcdm_return_val_if_fail (buf != NULL, 0); qcdm_return_val_if_fail (len >= sizeof (*cmd) + DIAG_TRAILER_LEN, 0); memset (cmd, 0, sizeof (*cmd)); cmd->code = DIAG_CMD_STATUS; return dm_encapsulate_buffer (cmdbuf, sizeof (*cmd), sizeof (cmdbuf), buf, len); } QcdmResult * qcdm_cmd_cdma_status_result (const char *buf, size_t len, int *out_error) { QcdmResult *result = NULL; DMCmdStatusRsp *rsp = (DMCmdStatusRsp *) buf; char *tmp; uint8_t swapped[4]; uint32_t tmp_num; qcdm_return_val_if_fail (buf != NULL, NULL); if (!check_command (buf, len, DIAG_CMD_STATUS, sizeof (DMCmdStatusRsp), out_error)) return NULL; result = qcdm_result_new (); /* Convert the ESN from binary to a hex string; it's LE so we have to * swap it to get the correct ordering. */ swapped[0] = rsp->esn[3]; swapped[1] = rsp->esn[2]; swapped[2] = rsp->esn[1]; swapped[3] = rsp->esn[0]; tmp = bin2hexstr (&swapped[0], sizeof (swapped)); qcdm_result_add_string (result, QCDM_CMD_CDMA_STATUS_ITEM_ESN, tmp); free (tmp); tmp_num = (uint32_t) le16toh (rsp->rf_mode); qcdm_result_add_u32 (result, QCDM_CMD_CDMA_STATUS_ITEM_RF_MODE, tmp_num); tmp_num = (uint32_t) le16toh (rsp->cdma_rx_state); qcdm_result_add_u32 (result, QCDM_CMD_CDMA_STATUS_ITEM_RX_STATE, tmp_num); tmp_num = (uint32_t) le16toh (rsp->entry_reason); qcdm_result_add_u32 (result, QCDM_CMD_CDMA_STATUS_ITEM_ENTRY_REASON, tmp_num); tmp_num = (uint32_t) le16toh (rsp->curr_chan); qcdm_result_add_u32 (result, QCDM_CMD_CDMA_STATUS_ITEM_CURRENT_CHANNEL, tmp_num); qcdm_result_add_u8 (result, QCDM_CMD_CDMA_STATUS_ITEM_CODE_CHANNEL, rsp->cdma_code_chan); tmp_num = (uint32_t) le16toh (rsp->pilot_base); qcdm_result_add_u32 (result, QCDM_CMD_CDMA_STATUS_ITEM_PILOT_BASE, tmp_num); tmp_num = (uint32_t) le16toh (rsp->sid); qcdm_result_add_u32 (result, QCDM_CMD_CDMA_STATUS_ITEM_SID, tmp_num); tmp_num = (uint32_t) le16toh (rsp->nid); qcdm_result_add_u32 (result, QCDM_CMD_CDMA_STATUS_ITEM_NID, tmp_num); return result; } /**********************************************************************/ size_t qcdm_cmd_sw_version_new (char *buf, size_t len) { char cmdbuf[3]; DMCmdHeader *cmd = (DMCmdHeader *) &cmdbuf[0]; qcdm_return_val_if_fail (buf != NULL, 0); qcdm_return_val_if_fail (len >= sizeof (*cmd) + DIAG_TRAILER_LEN, 0); memset (cmd, 0, sizeof (*cmd)); cmd->code = DIAG_CMD_SW_VERSION; return dm_encapsulate_buffer (cmdbuf, sizeof (*cmd), sizeof (cmdbuf), buf, len); } QcdmResult * qcdm_cmd_sw_version_result (const char *buf, size_t len, int *out_error) { QcdmResult *result = NULL; DMCmdSwVersionRsp *rsp = (DMCmdSwVersionRsp *) buf; char tmp[32]; qcdm_return_val_if_fail (buf != NULL, NULL); if (!check_command (buf, len, DIAG_CMD_SW_VERSION, sizeof (*rsp), out_error)) return NULL; result = qcdm_result_new (); memset (tmp, 0, sizeof (tmp)); qcdm_assert (sizeof (rsp->version) <= sizeof (tmp)); memcpy (tmp, rsp->version, sizeof (rsp->version)); qcdm_result_add_string (result, QCDM_CMD_SW_VERSION_ITEM_VERSION, tmp); memset (tmp, 0, sizeof (tmp)); qcdm_assert (sizeof (rsp->comp_date) <= sizeof (tmp)); memcpy (tmp, rsp->comp_date, sizeof (rsp->comp_date)); qcdm_result_add_string (result, QCDM_CMD_SW_VERSION_ITEM_COMP_DATE, tmp); memset (tmp, 0, sizeof (tmp)); qcdm_assert (sizeof (rsp->comp_time) <= sizeof (tmp)); memcpy (tmp, rsp->comp_time, sizeof (rsp->comp_time)); qcdm_result_add_string (result, QCDM_CMD_SW_VERSION_ITEM_COMP_TIME, tmp); return result; } /**********************************************************************/ size_t qcdm_cmd_status_snapshot_new (char *buf, size_t len) { char cmdbuf[3]; DMCmdHeader *cmd = (DMCmdHeader *) &cmdbuf[0]; qcdm_return_val_if_fail (buf != NULL, 0); qcdm_return_val_if_fail (len >= sizeof (*cmd) + DIAG_TRAILER_LEN, 0); memset (cmd, 0, sizeof (*cmd)); cmd->code = DIAG_CMD_STATUS_SNAPSHOT; return dm_encapsulate_buffer (cmdbuf, sizeof (*cmd), sizeof (cmdbuf), buf, len); } static uint8_t snapshot_state_to_qcdm (uint8_t cdma_state) { /* CDMA_STATUS_SNAPSHOT_STATE_* -> QCDM_STATUS_SNAPSHOT_STATE_* */ return cdma_state + 1; } static inline uint8_t digit_fixup (uint8_t d) { /* CDMA MCC/IMSI conversion adds 1 to each digit, and digits equal to * 10 are really zero. */ if (d + 1 < 10) return d + 1; return 0; } QcdmResult * qcdm_cmd_status_snapshot_result (const char *buf, size_t len, int *out_error) { QcdmResult *result = NULL; DMCmdStatusSnapshotRsp *rsp = (DMCmdStatusSnapshotRsp *) buf; char *tmp; uint8_t swapped[4]; uint8_t tmcc[3]; uint16_t mcc, hmcc; qcdm_return_val_if_fail (buf != NULL, NULL); if (!check_command (buf, len, DIAG_CMD_STATUS_SNAPSHOT, sizeof (*rsp), out_error)) return NULL; result = qcdm_result_new (); /* Convert the ESN from binary to a hex string; it's LE so we have to * swap it to get the correct ordering. */ swapped[0] = rsp->esn[3]; swapped[1] = rsp->esn[2]; swapped[2] = rsp->esn[1]; swapped[3] = rsp->esn[0]; tmp = bin2hexstr (&swapped[0], sizeof (swapped)); qcdm_result_add_string (result, QCDM_CMD_STATUS_SNAPSHOT_ITEM_ESN, tmp); free (tmp); /* Cheap binary -> decimal conversion */ hmcc = le16toh (rsp->mcc); tmcc[2] = hmcc / 100; tmcc[1] = (hmcc - (tmcc[2] * 100)) / 10; tmcc[0] = (hmcc - (tmcc[2] * 100) - (tmcc[1] * 10)); mcc = (100 * digit_fixup (tmcc[2])) + (10 * digit_fixup (tmcc[1])) + digit_fixup (tmcc[0]); qcdm_result_add_u32 (result, QCDM_CMD_STATUS_SNAPSHOT_ITEM_HOME_MCC, mcc); qcdm_result_add_u8 (result, QCDM_CMD_STATUS_SNAPSHOT_ITEM_BAND_CLASS, cdma_band_class_to_qcdm (rsp->band_class)); qcdm_result_add_u8 (result, QCDM_CMD_STATUS_SNAPSHOT_ITEM_BASE_STATION_PREV, cdma_prev_to_qcdm (rsp->prev)); qcdm_result_add_u8 (result, QCDM_CMD_STATUS_SNAPSHOT_ITEM_MOBILE_PREV, cdma_prev_to_qcdm (rsp->mob_prev)); qcdm_result_add_u8 (result, QCDM_CMD_STATUS_SNAPSHOT_ITEM_PREV_IN_USE, cdma_prev_to_qcdm (rsp->prev_in_use)); qcdm_result_add_u8 (result, QCDM_CMD_STATUS_SNAPSHOT_ITEM_STATE, snapshot_state_to_qcdm (rsp->state & 0xF)); return result; } /**********************************************************************/ size_t qcdm_cmd_pilot_sets_new (char *buf, size_t len) { char cmdbuf[3]; DMCmdHeader *cmd = (DMCmdHeader *) &cmdbuf[0]; qcdm_return_val_if_fail (buf != NULL, 0); qcdm_return_val_if_fail (len >= sizeof (*cmd) + DIAG_TRAILER_LEN, 0); memset (cmd, 0, sizeof (*cmd)); cmd->code = DIAG_CMD_PILOT_SETS; return dm_encapsulate_buffer (cmdbuf, sizeof (*cmd), sizeof (cmdbuf), buf, len); } #define PILOT_SETS_CMD_ACTIVE_SET "active-set" #define PILOT_SETS_CMD_CANDIDATE_SET "candidate-set" #define PILOT_SETS_CMD_NEIGHBOR_SET "neighbor-set" static const char * set_num_to_str (uint32_t num) { if (num == QCDM_CMD_PILOT_SETS_TYPE_ACTIVE) return PILOT_SETS_CMD_ACTIVE_SET; if (num == QCDM_CMD_PILOT_SETS_TYPE_CANDIDATE) return PILOT_SETS_CMD_CANDIDATE_SET; if (num == QCDM_CMD_PILOT_SETS_TYPE_NEIGHBOR) return PILOT_SETS_CMD_NEIGHBOR_SET; return NULL; } QcdmResult * qcdm_cmd_pilot_sets_result (const char *buf, size_t len, int *out_error) { QcdmResult *result = NULL; DMCmdPilotSetsRsp *rsp = (DMCmdPilotSetsRsp *) buf; size_t sets_len; qcdm_return_val_if_fail (buf != NULL, NULL); if (!check_command (buf, len, DIAG_CMD_PILOT_SETS, sizeof (DMCmdPilotSetsRsp), out_error)) return NULL; result = qcdm_result_new (); sets_len = rsp->active_count * sizeof (DMCmdPilotSetsSet); if (sets_len > 0) { qcdm_result_add_u8_array (result, PILOT_SETS_CMD_ACTIVE_SET, (const uint8_t *) &rsp->sets[0], sets_len); } sets_len = rsp->candidate_count * sizeof (DMCmdPilotSetsSet); if (sets_len > 0) { qcdm_result_add_u8_array (result, PILOT_SETS_CMD_CANDIDATE_SET, (const uint8_t *) &rsp->sets[rsp->active_count], sets_len); } sets_len = rsp->neighbor_count * sizeof (DMCmdPilotSetsSet); if (sets_len > 0) { qcdm_result_add_u8_array (result, PILOT_SETS_CMD_NEIGHBOR_SET, (const uint8_t *) &rsp->sets[rsp->active_count + rsp->candidate_count], sets_len); } return result; } qcdmbool qcdm_cmd_pilot_sets_result_get_num (QcdmResult *result, uint32_t set_type, uint32_t *out_num) { const char *set_name; const uint8_t *array = NULL; size_t array_len = 0; qcdm_return_val_if_fail (result != NULL, FALSE); set_name = set_num_to_str (set_type); qcdm_return_val_if_fail (set_name != NULL, FALSE); if (qcdm_result_get_u8_array (result, set_name, &array, &array_len)) return FALSE; *out_num = array_len / sizeof (DMCmdPilotSetsSet); return TRUE; } qcdmbool qcdm_cmd_pilot_sets_result_get_pilot (QcdmResult *result, uint32_t set_type, uint32_t num, uint32_t *out_pn_offset, uint32_t *out_ecio, float *out_db) { const char *set_name; DMCmdPilotSetsSet *set; const uint8_t *array = NULL; size_t array_len = 0; qcdm_return_val_if_fail (result != NULL, FALSE); set_name = set_num_to_str (set_type); qcdm_return_val_if_fail (set_name != NULL, FALSE); if (qcdm_result_get_u8_array (result, set_name, &array, &array_len)) return FALSE; qcdm_return_val_if_fail (num < array_len / sizeof (DMCmdPilotSetsSet), FALSE); set = (DMCmdPilotSetsSet *) &array[num * sizeof (DMCmdPilotSetsSet)]; *out_pn_offset = set->pn_offset; *out_ecio = set->ecio; /* EC/IO is in units of -0.5 dB per the specs */ *out_db = (float) (set->ecio * -0.5); return TRUE; } /**********************************************************************/ size_t qcdm_cmd_nv_get_mdn_new (char *buf, size_t len, uint8_t profile) { char cmdbuf[sizeof (DMCmdNVReadWrite) + 2]; DMCmdNVReadWrite *cmd = (DMCmdNVReadWrite *) &cmdbuf[0]; DMNVItemMdn *req; qcdm_return_val_if_fail (buf != NULL, 0); qcdm_return_val_if_fail (len >= sizeof (*cmd) + DIAG_TRAILER_LEN, 0); memset (cmd, 0, sizeof (*cmd)); cmd->code = DIAG_CMD_NV_READ; cmd->nv_item = htole16 (DIAG_NV_DIR_NUMBER); req = (DMNVItemMdn *) &cmd->data[0]; req->profile = profile; return dm_encapsulate_buffer (cmdbuf, sizeof (*cmd), sizeof (cmdbuf), buf, len); } QcdmResult * qcdm_cmd_nv_get_mdn_result (const char *buf, size_t len, int *out_error) { QcdmResult *result = NULL; DMCmdNVReadWrite *rsp = (DMCmdNVReadWrite *) buf; DMNVItemMdn *mdn; char tmp[11]; qcdm_return_val_if_fail (buf != NULL, NULL); if (!check_command (buf, len, DIAG_CMD_NV_READ, sizeof (DMCmdNVReadWrite), out_error)) return NULL; if (!check_nv_cmd (rsp, DIAG_NV_DIR_NUMBER, out_error)) return NULL; mdn = (DMNVItemMdn *) &rsp->data[0]; result = qcdm_result_new (); qcdm_result_add_u8 (result, QCDM_CMD_NV_GET_MDN_ITEM_PROFILE, mdn->profile); memset (tmp, 0, sizeof (tmp)); qcdm_assert (sizeof (mdn->mdn) <= sizeof (tmp)); memcpy (tmp, mdn->mdn, sizeof (mdn->mdn)); qcdm_result_add_string (result, QCDM_CMD_NV_GET_MDN_ITEM_MDN, tmp); return result; } /**********************************************************************/ static qcdmbool roam_pref_validate (uint8_t dm) { if ( dm == DIAG_NV_ROAM_PREF_HOME_ONLY || dm == DIAG_NV_ROAM_PREF_ROAM_ONLY || dm == DIAG_NV_ROAM_PREF_AUTO) return TRUE; return FALSE; } size_t qcdm_cmd_nv_get_roam_pref_new (char *buf, size_t len, uint8_t profile) { char cmdbuf[sizeof (DMCmdNVReadWrite) + 2]; DMCmdNVReadWrite *cmd = (DMCmdNVReadWrite *) &cmdbuf[0]; DMNVItemRoamPref *req; qcdm_return_val_if_fail (buf != NULL, 0); qcdm_return_val_if_fail (len >= sizeof (*cmd) + DIAG_TRAILER_LEN, 0); memset (cmd, 0, sizeof (*cmd)); cmd->code = DIAG_CMD_NV_READ; cmd->nv_item = htole16 (DIAG_NV_ROAM_PREF); req = (DMNVItemRoamPref *) &cmd->data[0]; req->profile = profile; return dm_encapsulate_buffer (cmdbuf, sizeof (*cmd), sizeof (cmdbuf), buf, len); } QcdmResult * qcdm_cmd_nv_get_roam_pref_result (const char *buf, size_t len, int *out_error) { QcdmResult *result = NULL; DMCmdNVReadWrite *rsp = (DMCmdNVReadWrite *) buf; DMNVItemRoamPref *roam; qcdm_return_val_if_fail (buf != NULL, NULL); if (!check_command (buf, len, DIAG_CMD_NV_READ, sizeof (DMCmdNVReadWrite), out_error)) return NULL; if (!check_nv_cmd (rsp, DIAG_NV_ROAM_PREF, out_error)) return NULL; roam = (DMNVItemRoamPref *) &rsp->data[0]; if (!roam_pref_validate (roam->roam_pref)) { qcdm_err (0, "Unknown roam preference 0x%X", roam->roam_pref); return NULL; } result = qcdm_result_new (); qcdm_result_add_u8 (result, QCDM_CMD_NV_GET_ROAM_PREF_ITEM_PROFILE, roam->profile); qcdm_result_add_u8 (result, QCDM_CMD_NV_GET_ROAM_PREF_ITEM_ROAM_PREF, roam->roam_pref); return result; } size_t qcdm_cmd_nv_set_roam_pref_new (char *buf, size_t len, uint8_t profile, uint8_t roam_pref) { char cmdbuf[sizeof (DMCmdNVReadWrite) + 2]; DMCmdNVReadWrite *cmd = (DMCmdNVReadWrite *) &cmdbuf[0]; DMNVItemRoamPref *req; qcdm_return_val_if_fail (buf != NULL, 0); qcdm_return_val_if_fail (len >= sizeof (*cmd) + DIAG_TRAILER_LEN, 0); if (!roam_pref_validate (roam_pref)) { qcdm_err (0, "Invalid roam preference %d", roam_pref); return 0; } memset (cmd, 0, sizeof (*cmd)); cmd->code = DIAG_CMD_NV_WRITE; cmd->nv_item = htole16 (DIAG_NV_ROAM_PREF); req = (DMNVItemRoamPref *) &cmd->data[0]; req->profile = profile; req->roam_pref = roam_pref; return dm_encapsulate_buffer (cmdbuf, sizeof (*cmd), sizeof (cmdbuf), buf, len); } QcdmResult * qcdm_cmd_nv_set_roam_pref_result (const char *buf, size_t len, int *out_error) { qcdm_return_val_if_fail (buf != NULL, NULL); if (!check_command (buf, len, DIAG_CMD_NV_WRITE, sizeof (DMCmdNVReadWrite), out_error)) return NULL; if (!check_nv_cmd ((DMCmdNVReadWrite *) buf, DIAG_NV_ROAM_PREF, out_error)) return NULL; return qcdm_result_new (); } /**********************************************************************/ size_t qcdm_cmd_nv_get_mode_pref_new (char *buf, size_t len, uint8_t profile) { char cmdbuf[sizeof (DMCmdNVReadWrite) + 2]; DMCmdNVReadWrite *cmd = (DMCmdNVReadWrite *) &cmdbuf[0]; DMNVItemModePref *req; qcdm_return_val_if_fail (buf != NULL, 0); qcdm_return_val_if_fail (len >= sizeof (*cmd) + DIAG_TRAILER_LEN, 0); memset (cmd, 0, sizeof (*cmd)); cmd->code = DIAG_CMD_NV_READ; cmd->nv_item = htole16 (DIAG_NV_MODE_PREF); req = (DMNVItemModePref *) &cmd->data[0]; req->profile = profile; return dm_encapsulate_buffer (cmdbuf, sizeof (*cmd), sizeof (cmdbuf), buf, len); } QcdmResult * qcdm_cmd_nv_get_mode_pref_result (const char *buf, size_t len, int *out_error) { QcdmResult *result = NULL; DMCmdNVReadWrite *rsp = (DMCmdNVReadWrite *) buf; DMNVItemModePref *mode; qcdm_return_val_if_fail (buf != NULL, NULL); if (!check_command (buf, len, DIAG_CMD_NV_READ, sizeof (DMCmdNVReadWrite), out_error)) return NULL; if (!check_nv_cmd (rsp, DIAG_NV_MODE_PREF, out_error)) return NULL; mode = (DMNVItemModePref *) &rsp->data[0]; result = qcdm_result_new (); qcdm_result_add_u8 (result, QCDM_CMD_NV_GET_MODE_PREF_ITEM_PROFILE, mode->profile); qcdm_result_add_u8 (result, QCDM_CMD_NV_GET_MODE_PREF_ITEM_MODE_PREF, mode->mode_pref); return result; } size_t qcdm_cmd_nv_set_mode_pref_new (char *buf, size_t len, uint8_t profile, uint8_t mode_pref) { char cmdbuf[sizeof (DMCmdNVReadWrite) + 2]; DMCmdNVReadWrite *cmd = (DMCmdNVReadWrite *) &cmdbuf[0]; DMNVItemModePref *req; qcdm_return_val_if_fail (buf != NULL, 0); qcdm_return_val_if_fail (len >= sizeof (*cmd) + DIAG_TRAILER_LEN, 0); memset (cmd, 0, sizeof (*cmd)); cmd->code = DIAG_CMD_NV_WRITE; cmd->nv_item = htole16 (DIAG_NV_MODE_PREF); req = (DMNVItemModePref *) &cmd->data[0]; req->profile = profile; req->mode_pref = nv_mode_pref_from_qcdm (mode_pref); return dm_encapsulate_buffer (cmdbuf, sizeof (*cmd), sizeof (cmdbuf), buf, len); } QcdmResult * qcdm_cmd_nv_set_mode_pref_result (const char *buf, size_t len, int *out_error) { qcdm_return_val_if_fail (buf != NULL, NULL); if (!check_command (buf, len, DIAG_CMD_NV_WRITE, sizeof (DMCmdNVReadWrite), out_error)) return NULL; if (!check_nv_cmd ((DMCmdNVReadWrite *) buf, DIAG_NV_MODE_PREF, out_error)) return NULL; return qcdm_result_new (); } /**********************************************************************/ size_t qcdm_cmd_nv_get_hybrid_pref_new (char *buf, size_t len) { char cmdbuf[sizeof (DMCmdNVReadWrite) + 2]; DMCmdNVReadWrite *cmd = (DMCmdNVReadWrite *) &cmdbuf[0]; qcdm_return_val_if_fail (buf != NULL, 0); qcdm_return_val_if_fail (len >= sizeof (*cmd) + DIAG_TRAILER_LEN, 0); memset (cmd, 0, sizeof (*cmd)); cmd->code = DIAG_CMD_NV_READ; cmd->nv_item = htole16 (DIAG_NV_HYBRID_PREF); return dm_encapsulate_buffer (cmdbuf, sizeof (*cmd), sizeof (cmdbuf), buf, len); } QcdmResult * qcdm_cmd_nv_get_hybrid_pref_result (const char *buf, size_t len, int *out_error) { QcdmResult *result = NULL; DMCmdNVReadWrite *rsp = (DMCmdNVReadWrite *) buf; DMNVItemHybridPref *hybrid; qcdm_return_val_if_fail (buf != NULL, NULL); if (!check_command (buf, len, DIAG_CMD_NV_READ, sizeof (DMCmdNVReadWrite), out_error)) return NULL; if (!check_nv_cmd (rsp, DIAG_NV_HYBRID_PREF, out_error)) return NULL; hybrid = (DMNVItemHybridPref *) &rsp->data[0]; if (hybrid->hybrid_pref > 1) qcdm_warn (0, "Unknown hybrid preference 0x%X", hybrid->hybrid_pref); result = qcdm_result_new (); qcdm_result_add_u8 (result, QCDM_CMD_NV_GET_HYBRID_PREF_ITEM_HYBRID_PREF, hybrid->hybrid_pref); return result; } size_t qcdm_cmd_nv_set_hybrid_pref_new (char *buf, size_t len, uint8_t hybrid_pref) { char cmdbuf[sizeof (DMCmdNVReadWrite) + 2]; DMCmdNVReadWrite *cmd = (DMCmdNVReadWrite *) &cmdbuf[0]; DMNVItemHybridPref *req; qcdm_return_val_if_fail (buf != NULL, 0); qcdm_return_val_if_fail (len >= sizeof (*cmd) + DIAG_TRAILER_LEN, 0); if (hybrid_pref > QCDM_CMD_NV_HYBRID_PREF_ITEM_REV_HYBRID_ON) { qcdm_err (0, "Invalid hybrid preference %d", hybrid_pref); return 0; } memset (cmd, 0, sizeof (*cmd)); cmd->code = DIAG_CMD_NV_WRITE; cmd->nv_item = htole16 (DIAG_NV_HYBRID_PREF); req = (DMNVItemHybridPref *) &cmd->data[0]; if (hybrid_pref == QCDM_CMD_NV_HYBRID_PREF_ITEM_REV_HYBRID_OFF) req->hybrid_pref = DIAG_NV_HYBRID_PREF_OFF; else if (hybrid_pref == QCDM_CMD_NV_HYBRID_PREF_ITEM_REV_HYBRID_ON) req->hybrid_pref = DIAG_NV_HYBRID_PREF_ON; return dm_encapsulate_buffer (cmdbuf, sizeof (*cmd), sizeof (cmdbuf), buf, len); } QcdmResult * qcdm_cmd_nv_set_hybrid_pref_result (const char *buf, size_t len, int *out_error) { qcdm_return_val_if_fail (buf != NULL, NULL); if (!check_command (buf, len, DIAG_CMD_NV_WRITE, sizeof (DMCmdNVReadWrite), out_error)) return NULL; if (!check_nv_cmd ((DMCmdNVReadWrite *) buf, DIAG_NV_HYBRID_PREF, out_error)) return NULL; return qcdm_result_new (); } /**********************************************************************/ size_t qcdm_cmd_nv_get_ipv6_enabled_new (char *buf, size_t len) { char cmdbuf[sizeof (DMCmdNVReadWrite) + 2]; DMCmdNVReadWrite *cmd = (DMCmdNVReadWrite *) &cmdbuf[0]; qcdm_return_val_if_fail (buf != NULL, 0); qcdm_return_val_if_fail (len >= sizeof (*cmd) + DIAG_TRAILER_LEN, 0); memset (cmd, 0, sizeof (*cmd)); cmd->code = DIAG_CMD_NV_READ; cmd->nv_item = htole16 (DIAG_NV_IPV6_ENABLED); return dm_encapsulate_buffer (cmdbuf, sizeof (*cmd), sizeof (cmdbuf), buf, len); } QcdmResult * qcdm_cmd_nv_get_ipv6_enabled_result (const char *buf, size_t len, int *out_error) { QcdmResult *result = NULL; DMCmdNVReadWrite *rsp = (DMCmdNVReadWrite *) buf; DMNVItemIPv6Enabled *ipv6; qcdm_return_val_if_fail (buf != NULL, NULL); if (!check_command (buf, len, DIAG_CMD_NV_READ, sizeof (DMCmdNVReadWrite), out_error)) return NULL; if (!check_nv_cmd (rsp, DIAG_NV_IPV6_ENABLED, out_error)) return NULL; ipv6 = (DMNVItemIPv6Enabled *) &rsp->data[0]; if (ipv6->enabled > 1) qcdm_warn (0, "Unknown ipv6 preference 0x%X", ipv6->enabled); result = qcdm_result_new (); qcdm_result_add_u8 (result, QCDM_CMD_NV_GET_IPV6_ENABLED_ITEM_ENABLED, ipv6->enabled); return result; } size_t qcdm_cmd_nv_set_ipv6_enabled_new (char *buf, size_t len, uint8_t enabled) { char cmdbuf[sizeof (DMCmdNVReadWrite) + 2]; DMCmdNVReadWrite *cmd = (DMCmdNVReadWrite *) &cmdbuf[0]; DMNVItemIPv6Enabled *req; qcdm_return_val_if_fail (buf != NULL, 0); qcdm_return_val_if_fail (len >= sizeof (*cmd) + DIAG_TRAILER_LEN, 0); if (enabled > QCDM_CMD_NV_IPV6_ENABLED_ON) { qcdm_err (0, "Invalid ipv6 preference %d", enabled); return 0; } memset (cmd, 0, sizeof (*cmd)); cmd->code = DIAG_CMD_NV_WRITE; cmd->nv_item = htole16 (DIAG_NV_IPV6_ENABLED); req = (DMNVItemIPv6Enabled *) &cmd->data[0]; if (enabled == QCDM_CMD_NV_IPV6_ENABLED_OFF) req->enabled = DIAG_NV_IPV6_ENABLED_OFF; else if (enabled == QCDM_CMD_NV_IPV6_ENABLED_ON) req->enabled = DIAG_NV_IPV6_ENABLED_ON; return dm_encapsulate_buffer (cmdbuf, sizeof (*cmd), sizeof (cmdbuf), buf, len); } QcdmResult * qcdm_cmd_nv_set_ipv6_enabled_result (const char *buf, size_t len, int *out_error) { qcdm_return_val_if_fail (buf != NULL, NULL); if (!check_command (buf, len, DIAG_CMD_NV_WRITE, sizeof (DMCmdNVReadWrite), out_error)) return NULL; if (!check_nv_cmd ((DMCmdNVReadWrite *) buf, DIAG_NV_IPV6_ENABLED, out_error)) return NULL; return qcdm_result_new (); } /**********************************************************************/ static qcdmbool hdr_rev_pref_validate (uint8_t dm) { if ( dm == DIAG_NV_HDR_REV_PREF_0 || dm == DIAG_NV_HDR_REV_PREF_A || dm == DIAG_NV_HDR_REV_PREF_EHRPD) return TRUE; return FALSE; } size_t qcdm_cmd_nv_get_hdr_rev_pref_new (char *buf, size_t len) { char cmdbuf[sizeof (DMCmdNVReadWrite) + 2]; DMCmdNVReadWrite *cmd = (DMCmdNVReadWrite *) &cmdbuf[0]; qcdm_return_val_if_fail (buf != NULL, 0); qcdm_return_val_if_fail (len >= sizeof (*cmd) + DIAG_TRAILER_LEN, 0); memset (cmd, 0, sizeof (*cmd)); cmd->code = DIAG_CMD_NV_READ; cmd->nv_item = htole16 (DIAG_NV_HDR_REV_PREF); return dm_encapsulate_buffer (cmdbuf, sizeof (*cmd), sizeof (cmdbuf), buf, len); } QcdmResult * qcdm_cmd_nv_get_hdr_rev_pref_result (const char *buf, size_t len, int *out_error) { QcdmResult *result = NULL; DMCmdNVReadWrite *rsp = (DMCmdNVReadWrite *) buf; DMNVItemHdrRevPref *rev; qcdm_return_val_if_fail (buf != NULL, NULL); if (!check_command (buf, len, DIAG_CMD_NV_READ, sizeof (DMCmdNVReadWrite), out_error)) return NULL; if (!check_nv_cmd (rsp, DIAG_NV_HDR_REV_PREF, out_error)) return NULL; rev = (DMNVItemHdrRevPref *) &rsp->data[0]; if (!hdr_rev_pref_validate (rev->rev_pref)) { qcdm_err (0, "Unknown HDR revision preference 0x%X", rev->rev_pref); return NULL; } result = qcdm_result_new (); qcdm_result_add_u8 (result, QCDM_CMD_NV_GET_HDR_REV_PREF_ITEM_REV_PREF, rev->rev_pref); return result; } size_t qcdm_cmd_nv_set_hdr_rev_pref_new (char *buf, size_t len, uint8_t rev_pref) { char cmdbuf[sizeof (DMCmdNVReadWrite) + 2]; DMCmdNVReadWrite *cmd = (DMCmdNVReadWrite *) &cmdbuf[0]; DMNVItemHdrRevPref *req; qcdm_return_val_if_fail (buf != NULL, 0); qcdm_return_val_if_fail (len >= sizeof (*cmd) + DIAG_TRAILER_LEN, 0); if (!hdr_rev_pref_validate (rev_pref)) { qcdm_err (0, "Invalid HDR revision preference %d", rev_pref); return 0; } memset (cmd, 0, sizeof (*cmd)); cmd->code = DIAG_CMD_NV_WRITE; cmd->nv_item = htole16 (DIAG_NV_HDR_REV_PREF); req = (DMNVItemHdrRevPref *) &cmd->data[0]; req->rev_pref = rev_pref; return dm_encapsulate_buffer (cmdbuf, sizeof (*cmd), sizeof (cmdbuf), buf, len); } QcdmResult * qcdm_cmd_nv_set_hdr_rev_pref_result (const char *buf, size_t len, int *out_error) { qcdm_return_val_if_fail (buf != NULL, NULL); if (!check_command (buf, len, DIAG_CMD_NV_WRITE, sizeof (DMCmdNVReadWrite), out_error)) return NULL; if (!check_nv_cmd ((DMCmdNVReadWrite *) buf, DIAG_NV_HDR_REV_PREF, out_error)) return NULL; return qcdm_result_new (); } /**********************************************************************/ size_t qcdm_cmd_cm_subsys_state_info_new (char *buf, size_t len) { char cmdbuf[sizeof (DMCmdSubsysHeader) + 2]; DMCmdSubsysHeader *cmd = (DMCmdSubsysHeader *) &cmdbuf[0]; qcdm_return_val_if_fail (buf != NULL, 0); qcdm_return_val_if_fail (len >= sizeof (*cmd) + DIAG_TRAILER_LEN, 0); memset (cmd, 0, sizeof (*cmd)); cmd->code = DIAG_CMD_SUBSYS; cmd->subsys_id = DIAG_SUBSYS_CM; cmd->subsys_cmd = htole16 (DIAG_SUBSYS_CM_STATE_INFO); return dm_encapsulate_buffer (cmdbuf, sizeof (*cmd), sizeof (cmdbuf), buf, len); } QcdmResult * qcdm_cmd_cm_subsys_state_info_result (const char *buf, size_t len, int *out_error) { QcdmResult *result = NULL; DMCmdSubsysCMStateInfoRsp *rsp = (DMCmdSubsysCMStateInfoRsp *) buf; uint32_t tmp_num; uint32_t roam_pref; qcdm_return_val_if_fail (buf != NULL, NULL); if (!check_command (buf, len, DIAG_CMD_SUBSYS, sizeof (DMCmdSubsysCMStateInfoRsp), out_error)) return NULL; roam_pref = (uint32_t) le32toh (rsp->roam_pref); if (!roam_pref_validate (roam_pref)) { qcdm_err (0, "Unknown roam preference 0x%X", roam_pref); return NULL; } result = qcdm_result_new (); tmp_num = (uint32_t) le32toh (rsp->call_state); qcdm_result_add_u32 (result, QCDM_CMD_CM_SUBSYS_STATE_INFO_ITEM_CALL_STATE, tmp_num); tmp_num = (uint32_t) le32toh (rsp->oper_mode); qcdm_result_add_u32 (result, QCDM_CMD_CM_SUBSYS_STATE_INFO_ITEM_OPERATING_MODE, tmp_num); tmp_num = (uint32_t) le32toh (rsp->system_mode); qcdm_result_add_u32 (result, QCDM_CMD_CM_SUBSYS_STATE_INFO_ITEM_SYSTEM_MODE, tmp_num); tmp_num = (uint32_t) le32toh (rsp->mode_pref); qcdm_result_add_u32 (result, QCDM_CMD_CM_SUBSYS_STATE_INFO_ITEM_MODE_PREF, tmp_num); tmp_num = (uint32_t) le32toh (rsp->band_pref); qcdm_result_add_u32 (result, QCDM_CMD_CM_SUBSYS_STATE_INFO_ITEM_BAND_PREF, tmp_num); qcdm_result_add_u32 (result, QCDM_CMD_CM_SUBSYS_STATE_INFO_ITEM_ROAM_PREF, roam_pref); tmp_num = (uint32_t) le32toh (rsp->srv_domain_pref); qcdm_result_add_u32 (result, QCDM_CMD_CM_SUBSYS_STATE_INFO_ITEM_SERVICE_DOMAIN_PREF, tmp_num); tmp_num = (uint32_t) le32toh (rsp->acq_order_pref); qcdm_result_add_u32 (result, QCDM_CMD_CM_SUBSYS_STATE_INFO_ITEM_ACQ_ORDER_PREF, tmp_num); tmp_num = (uint32_t) le32toh (rsp->hybrid_pref); qcdm_result_add_u32 (result, QCDM_CMD_CM_SUBSYS_STATE_INFO_ITEM_HYBRID_PREF, tmp_num); tmp_num = (uint32_t) le32toh (rsp->network_sel_mode_pref); qcdm_result_add_u32 (result, QCDM_CMD_CM_SUBSYS_STATE_INFO_ITEM_NETWORK_SELECTION_PREF, tmp_num); return result; } /**********************************************************************/ size_t qcdm_cmd_hdr_subsys_state_info_new (char *buf, size_t len) { char cmdbuf[sizeof (DMCmdSubsysHeader) + 2]; DMCmdSubsysHeader *cmd = (DMCmdSubsysHeader *) &cmdbuf[0]; qcdm_return_val_if_fail (buf != NULL, 0); qcdm_return_val_if_fail (len >= sizeof (*cmd) + DIAG_TRAILER_LEN, 0); memset (cmd, 0, sizeof (*cmd)); cmd->code = DIAG_CMD_SUBSYS; cmd->subsys_id = DIAG_SUBSYS_HDR; cmd->subsys_cmd = htole16 (DIAG_SUBSYS_HDR_STATE_INFO); return dm_encapsulate_buffer (cmdbuf, sizeof (*cmd), sizeof (cmdbuf), buf, len); } QcdmResult * qcdm_cmd_hdr_subsys_state_info_result (const char *buf, size_t len, int *out_error) { QcdmResult *result = NULL; DMCmdSubsysHDRStateInfoRsp *rsp = (DMCmdSubsysHDRStateInfoRsp *) buf; qcdm_return_val_if_fail (buf != NULL, NULL); if (!check_command (buf, len, DIAG_CMD_SUBSYS, sizeof (DMCmdSubsysHDRStateInfoRsp), out_error)) return NULL; result = qcdm_result_new (); qcdm_result_add_u8 (result, QCDM_CMD_HDR_SUBSYS_STATE_INFO_ITEM_AT_STATE, rsp->at_state); qcdm_result_add_u8 (result, QCDM_CMD_HDR_SUBSYS_STATE_INFO_ITEM_SESSION_STATE, rsp->session_state); qcdm_result_add_u8 (result, QCDM_CMD_HDR_SUBSYS_STATE_INFO_ITEM_ALMP_STATE, rsp->almp_state); qcdm_result_add_u8 (result, QCDM_CMD_HDR_SUBSYS_STATE_INFO_ITEM_INIT_STATE, rsp->init_state); qcdm_result_add_u8 (result, QCDM_CMD_HDR_SUBSYS_STATE_INFO_ITEM_IDLE_STATE, rsp->idle_state); qcdm_result_add_u8 (result, QCDM_CMD_HDR_SUBSYS_STATE_INFO_ITEM_CONNECTED_STATE, rsp->connected_state); qcdm_result_add_u8 (result, QCDM_CMD_HDR_SUBSYS_STATE_INFO_ITEM_ROUTE_UPDATE_STATE, rsp->route_update_state); qcdm_result_add_u8 (result, QCDM_CMD_HDR_SUBSYS_STATE_INFO_ITEM_OVERHEAD_MSG_STATE, rsp->overhead_msg_state); qcdm_result_add_u8 (result, QCDM_CMD_HDR_SUBSYS_STATE_INFO_ITEM_HDR_HYBRID_MODE, rsp->hdr_hybrid_mode); return result; } /**********************************************************************/ size_t qcdm_cmd_ext_logmask_new (char *buf, size_t len, uint32_t items[], uint16_t maxlog) { char cmdbuf[sizeof (DMCmdExtLogMask) + 2]; DMCmdExtLogMask *cmd = (DMCmdExtLogMask *) &cmdbuf[0]; uint16_t highest = 0; size_t total = 3; uint32_t i; qcdm_return_val_if_fail (buf != NULL, 0); qcdm_return_val_if_fail (len >= sizeof (*cmd) + DIAG_TRAILER_LEN, 0); memset (cmd, 0, sizeof (*cmd)); cmd->code = DIAG_CMD_EXT_LOGMASK; if (items) { for (i = 0; items[i] > 0; i++) { qcdm_warn_if_fail (items[i] > 0); qcdm_warn_if_fail (items[i] < 4095); cmd->mask[items[i] / 8] |= 1 << items[i] % 8; if (items[i] > highest) highest = items[i]; } } qcdm_return_val_if_fail (highest <= maxlog, 0); cmd->len = htole16 (maxlog); total += maxlog / 8; if (maxlog && maxlog % 8) total++; return dm_encapsulate_buffer (cmdbuf, total, sizeof (cmdbuf), buf, len); } QcdmResult * qcdm_cmd_ext_logmask_result (const char *buf, size_t len, int *out_error) { QcdmResult *result = NULL; DMCmdExtLogMask *rsp = (DMCmdExtLogMask *) buf; uint32_t masklen = 0, maxlog = 0; size_t minlen = 0; qcdm_return_val_if_fail (buf != NULL, NULL); /* Ensure size is at least enough for the command header */ if (len < 1) { qcdm_err (0, "DM command %d response not long enough (got %zu, expected " "at least %d).", DIAG_CMD_EXT_LOGMASK, len, 3); return NULL; } /* Result of a 'set' operation will be only 1 byte in size; result of * a 'get' operation (ie setting len to 0x0000 in the request) will be * the size of the header (3) plus the max log length. */ if (len == 1) minlen = 1; else { /* Ensure size is equal to max # of log items + 3 */ maxlog = le16toh (rsp->len); masklen = maxlog / 8; if (maxlog % 8) masklen++; if (len < (masklen + 3)) { qcdm_err (0, "DM command %d response not long enough (got %zu, expected " "at least %d).", DIAG_CMD_EXT_LOGMASK, len, masklen + 3); return NULL; } minlen = masklen + 3; } if (!check_command (buf, len, DIAG_CMD_EXT_LOGMASK, minlen, out_error)) return NULL; result = qcdm_result_new (); if (minlen != 4) qcdm_result_add_u32 (result, QCDM_CMD_EXT_LOGMASK_ITEM_MAX_ITEMS, maxlog); return result; } qcdmbool qcmd_cmd_ext_logmask_result_get_item (QcdmResult *result, uint16_t item) { return FALSE; } /**********************************************************************/ size_t qcdm_cmd_event_report_new (char *buf, size_t len, qcdmbool start) { char cmdbuf[4]; DMCmdEventReport *cmd = (DMCmdEventReport *) &cmdbuf[0]; qcdm_return_val_if_fail (buf != NULL, 0); qcdm_return_val_if_fail (len >= sizeof (*cmd) + DIAG_TRAILER_LEN, 0); memset (cmd, 0, sizeof (*cmd)); cmd->code = DIAG_CMD_EVENT_REPORT; cmd->on = start ? 1 : 0; return dm_encapsulate_buffer (cmdbuf, sizeof (*cmd), sizeof (cmdbuf), buf, len); } QcdmResult * qcdm_cmd_event_report_result (const char *buf, size_t len, int *out_error) { qcdm_return_val_if_fail (buf != NULL, NULL); if (!check_command (buf, len, DIAG_CMD_EVENT_REPORT, sizeof (DMCmdEventReport), out_error)) return NULL; return qcdm_result_new (); } /**********************************************************************/ size_t qcdm_cmd_zte_subsys_status_new (char *buf, size_t len) { char cmdbuf[sizeof (DMCmdSubsysHeader) + 2]; DMCmdSubsysHeader *cmd = (DMCmdSubsysHeader *) &cmdbuf[0]; qcdm_return_val_if_fail (buf != NULL, 0); qcdm_return_val_if_fail (len >= sizeof (*cmd) + DIAG_TRAILER_LEN, 0); memset (cmd, 0, sizeof (*cmd)); cmd->code = DIAG_CMD_SUBSYS; cmd->subsys_id = DIAG_SUBSYS_ZTE; cmd->subsys_cmd = htole16 (DIAG_SUBSYS_ZTE_STATUS); return dm_encapsulate_buffer (cmdbuf, sizeof (*cmd), sizeof (cmdbuf), buf, len); } QcdmResult * qcdm_cmd_zte_subsys_status_result (const char *buf, size_t len, int *out_error) { QcdmResult *result = NULL; DMCmdSubsysZteStatusRsp *rsp = (DMCmdSubsysZteStatusRsp *) buf; qcdm_return_val_if_fail (buf != NULL, NULL); if (!check_command (buf, len, DIAG_CMD_SUBSYS, sizeof (DMCmdSubsysZteStatusRsp), out_error)) return NULL; result = qcdm_result_new (); qcdm_result_add_u8 (result, QCDM_CMD_ZTE_SUBSYS_STATUS_ITEM_SIGNAL_INDICATOR, rsp->signal_ind); return result; } /**********************************************************************/ size_t qcdm_cmd_nw_subsys_modem_snapshot_cdma_new (char *buf, size_t len, uint8_t chipset) { char cmdbuf[sizeof (DMCmdSubsysNwSnapshotReq) + 2]; DMCmdSubsysNwSnapshotReq *cmd = (DMCmdSubsysNwSnapshotReq *) &cmdbuf[0]; qcdm_return_val_if_fail (buf != NULL, 0); qcdm_return_val_if_fail (len >= sizeof (*cmd) + DIAG_TRAILER_LEN, 0); /* Validate chipset */ if (chipset != QCDM_NW_CHIPSET_6500 && chipset != QCDM_NW_CHIPSET_6800) { qcdm_err (0, "Unknown Novatel chipset 0x%X", chipset); return 0; } memset (cmd, 0, sizeof (*cmd)); cmd->hdr.code = DIAG_CMD_SUBSYS; switch (chipset) { case QCDM_NW_CHIPSET_6500: cmd->hdr.subsys_id = DIAG_SUBSYS_NOVATEL_6500; break; case QCDM_NW_CHIPSET_6800: cmd->hdr.subsys_id = DIAG_SUBSYS_NOVATEL_6800; break; default: qcdm_assert_not_reached (); } cmd->hdr.subsys_cmd = htole16 (DIAG_SUBSYS_NOVATEL_MODEM_SNAPSHOT); cmd->technology = DIAG_SUBSYS_NOVATEL_MODEM_SNAPSHOT_TECH_CDMA_EVDO; cmd->snapshot_mask = htole32 (0xFFFF); return dm_encapsulate_buffer (cmdbuf, sizeof (*cmd), sizeof (cmdbuf), buf, len); } QcdmResult * qcdm_cmd_nw_subsys_modem_snapshot_cdma_result (const char *buf, size_t len, int *out_error) { QcdmResult *result = NULL; DMCmdSubsysNwSnapshotRsp *rsp = (DMCmdSubsysNwSnapshotRsp *) buf; DMCmdSubsysNwSnapshotCdma *cdma = (DMCmdSubsysNwSnapshotCdma *) &rsp->data; uint32_t num; uint8_t num8; qcdm_return_val_if_fail (buf != NULL, NULL); if (!check_command (buf, len, DIAG_CMD_SUBSYS, sizeof (DMCmdSubsysNwSnapshotRsp), out_error)) return NULL; /* FIXME: check response_code when we know what it means */ result = qcdm_result_new (); num = le32toh (cdma->rssi); qcdm_result_add_u32 (result, QCDM_CMD_NW_SUBSYS_MODEM_SNAPSHOT_CDMA_ITEM_RSSI, num); num8 = cdma_prev_to_qcdm (cdma->prev); qcdm_result_add_u8 (result, QCDM_CMD_NW_SUBSYS_MODEM_SNAPSHOT_CDMA_ITEM_PREV, num8); num8 = cdma_band_class_to_qcdm (cdma->band_class); qcdm_result_add_u8 (result, QCDM_CMD_NW_SUBSYS_MODEM_SNAPSHOT_CDMA_ITEM_BAND_CLASS, num8); qcdm_result_add_u8 (result, QCDM_CMD_NW_SUBSYS_MODEM_SNAPSHOT_CDMA_ITEM_ERI, cdma->eri); num8 = QCDM_HDR_REV_UNKNOWN; switch (cdma->hdr_rev) { case 0: num8 = QCDM_HDR_REV_0; break; case 1: num8 = QCDM_HDR_REV_A; break; default: break; } qcdm_result_add_u8 (result, QCDM_CMD_NW_SUBSYS_MODEM_SNAPSHOT_CDMA_ITEM_HDR_REV, num8); return result; } /**********************************************************************/ size_t qcdm_cmd_nw_subsys_eri_new (char *buf, size_t len, uint8_t chipset) { char cmdbuf[sizeof (DMCmdSubsysHeader) + 2]; DMCmdSubsysHeader *cmd = (DMCmdSubsysHeader *) &cmdbuf[0]; qcdm_return_val_if_fail (buf != NULL, 0); qcdm_return_val_if_fail (len >= sizeof (*cmd) + DIAG_TRAILER_LEN, 0); /* Validate chipset */ if (chipset != QCDM_NW_CHIPSET_6500 && chipset != QCDM_NW_CHIPSET_6800) { qcdm_err (0, "Unknown Novatel chipset 0x%X", chipset); return 0; } memset (cmd, 0, sizeof (*cmd)); cmd->code = DIAG_CMD_SUBSYS; switch (chipset) { case QCDM_NW_CHIPSET_6500: cmd->subsys_id = DIAG_SUBSYS_NOVATEL_6500; break; case QCDM_NW_CHIPSET_6800: cmd->subsys_id = DIAG_SUBSYS_NOVATEL_6800; break; default: qcdm_assert_not_reached (); } cmd->subsys_cmd = htole16 (DIAG_SUBSYS_NOVATEL_ERI); return dm_encapsulate_buffer (cmdbuf, sizeof (*cmd), sizeof (cmdbuf), buf, len); } QcdmResult * qcdm_cmd_nw_subsys_eri_result (const char *buf, size_t len, int *out_error) { QcdmResult *result = NULL; DMCmdSubsysNwEriRsp *rsp = (DMCmdSubsysNwEriRsp *) buf; char str[50]; qcdm_return_val_if_fail (buf != NULL, NULL); if (!check_command (buf, len, DIAG_CMD_SUBSYS, sizeof (DMCmdSubsysNwEriRsp), out_error)) return NULL; /* FIXME: check 'status' when we know what it means */ result = qcdm_result_new (); qcdm_result_add_u8 (result, QCDM_CMD_NW_SUBSYS_ERI_ITEM_ROAM, rsp->roam); qcdm_result_add_u8 (result, QCDM_CMD_NW_SUBSYS_ERI_ITEM_INDICATOR_ID, rsp->indicator_id); qcdm_result_add_u8 (result, QCDM_CMD_NW_SUBSYS_ERI_ITEM_ICON_ID, rsp->icon_id); qcdm_result_add_u8 (result, QCDM_CMD_NW_SUBSYS_ERI_ITEM_ICON_MODE, rsp->icon_mode); qcdm_result_add_u8 (result, QCDM_CMD_NW_SUBSYS_ERI_ITEM_CALL_PROMPT_ID, rsp->call_prompt_id); qcdm_result_add_u8 (result, QCDM_CMD_NW_SUBSYS_ERI_ITEM_ALERT_ID, rsp->alert_id); qcdm_warn_if_fail (rsp->text_len < sizeof (str)); if (rsp->text_len < sizeof (str)) { qcdm_assert (sizeof (str) > sizeof (rsp->text)); memcpy (str, rsp->text, sizeof (rsp->text)); str[rsp->text_len] = '\0'; qcdm_result_add_string (result, QCDM_CMD_NW_SUBSYS_ERI_ITEM_TEXT, str); } return result; } /**********************************************************************/ static size_t qcdm_cmd_log_config_new (char *buf, size_t len, uint32_t op, uint32_t equip_id, uint16_t items[]) { DMCmdLogConfig *cmd; uint16_t highest = 0; uint32_t items_len = 0; size_t cmdsize = 0, cmdbufsize; uint32_t i; uint16_t log_code; size_t ret; qcdm_return_val_if_fail (buf != NULL, 0); qcdm_return_val_if_fail ((equip_id & 0xFFF0) == 0, 0); /* Find number of log items */ if (items) { while (items_len < 4095 && items[items_len]) { /* Find highest log item so we can size the items mask */ log_code = items[items_len] & 0x0FFF; if (log_code > highest) highest = log_code; items_len++; } } cmdsize = sizeof (DMCmdLogConfig) + ((highest + 7) / 8); cmdbufsize = cmdsize + DIAG_TRAILER_LEN; qcdm_return_val_if_fail (len >= cmdsize, 0); cmd = calloc (1, cmdbufsize); cmd->code = DIAG_CMD_LOG_CONFIG; cmd->op = htole32 (op); cmd->equipid = htole32 (equip_id); if (items) { /* Set up the bitmask of log items */ for (i = 0; i < items_len; i++) { log_code = items[i] & 0x0FFF; /* Strip off equip ID */ cmd->mask[log_code / 8] |= 1 << log_code % 8; } cmd->num_items = htole32 (highest); } ret = dm_encapsulate_buffer ((char *) cmd, cmdsize, cmdbufsize, buf, len); free (cmd); return ret; } size_t qcdm_cmd_log_config_get_mask_new (char *buf, size_t len, uint32_t equip_id) { return qcdm_cmd_log_config_new (buf, len, DIAG_CMD_LOG_CONFIG_OP_GET_MASK, equip_id, NULL); } static int check_log_config_respose (const char *buf, size_t len, uint32_t op) { DMCmdLogConfigRsp *rsp = (DMCmdLogConfigRsp *) buf; size_t minlen = 16; /* minimum valid resposne */ int err; /* Ensure size is at least enough for the command header */ if (len < 1) { qcdm_err (0, "DIAG_CMD_LOG_CONFIG response not long enough (got %zu, " "expected at least %d).", len, 3); return -QCDM_ERROR_RESPONSE_BAD_LENGTH; } if (rsp->code == DIAG_CMD_LOG_CONFIG) { uint32_t rspop; if (len < 16) { /* At least enough for code + op + result + equipid */ qcdm_err (0, "DIAG_CMD_LOG_CONFIG response not long enough (got %zu, " "expected at least %d).", len, 16); return -QCDM_ERROR_RESPONSE_BAD_LENGTH; } rspop = le32toh (rsp->op); if (rspop != op) { qcdm_err (0, "DIAG_CMD_LOG_CONFIG response operation mismatch (got " "op %u, expected %u)", rspop, op); return -QCDM_ERROR_RESPONSE_BAD_COMMAND; } /* check for success */ if (le32toh (rsp->result) != 0) { qcdm_err (0, "DIAG_CMD_LOG_CONFIG response failed with result %u.", le32toh (rsp->result)); return -QCDM_ERROR_RESPONSE_FAILED; } switch (rspop) { case DIAG_CMD_LOG_CONFIG_OP_GET_RANGE: minlen += 16; /* get_range_items */ break; case DIAG_CMD_LOG_CONFIG_OP_SET_MASK: case DIAG_CMD_LOG_CONFIG_OP_GET_MASK: if (len < 16) { qcdm_err (0, "DIAG_CMD_LOG_CONFIG response not long enough " "(got %zu, expected at least %d).", len, 16); return -QCDM_ERROR_RESPONSE_BAD_LENGTH; } minlen += 4; /* num_items */ minlen += (le32toh (rsp->u.get_set_items.num_items) + 7) / 8; break; default: qcdm_err (0, "Unknown DIAG_CMD_LOG_CONFIG response operation %d", rspop); return -QCDM_ERROR_RESPONSE_UNEXPECTED; } } if (!check_command (buf, len, DIAG_CMD_LOG_CONFIG, minlen, &err)) return err; return 0; } #define LOG_CODE_SET(mask, code) (mask[code / 8] & (1 << (code % 8))) static QcdmResult * log_config_get_set_result (const char *buf, size_t len, uint32_t op, int *out_error) { QcdmResult *result = NULL; DMCmdLogConfigRsp *rsp = (DMCmdLogConfigRsp *) buf; int err; uint32_t num_items; uint32_t equipid; qcdm_return_val_if_fail (buf != NULL, NULL); err = check_log_config_respose (buf, len, op); if (err) { if (out_error) *out_error = err; return NULL; } result = qcdm_result_new (); equipid = le32toh (rsp->equipid); qcdm_result_add_u32 (result, QCDM_CMD_LOG_CONFIG_MASK_ITEM_EQUIP_ID, equipid); num_items = le32toh (rsp->u.get_set_items.num_items); qcdm_result_add_u32 (result, QCDM_CMD_LOG_CONFIG_MASK_ITEM_NUM_ITEMS, num_items); if (num_items > 0) { uint32_t i, num_result_items = 0, count = 0; uint16_t *items; /* First pass to find out how many are actually enabled */ for (i = 0; i < num_items; i++) { /* Check if the bit corresponding to this log item is set */ if (LOG_CODE_SET (rsp->u.get_set_items.mask, i)) num_result_items++; } if (num_result_items) { items = malloc (sizeof (*items) * num_result_items); for (i = 0; i < num_items; i++) { if (LOG_CODE_SET (rsp->u.get_set_items.mask, i)) items[count++] = (equipid << 12) | (i & 0x0FFF); } qcdm_result_add_u16_array (result, QCDM_CMD_LOG_CONFIG_MASK_ITEM_ITEMS, items, count); free (items); } } return result; } QcdmResult * qcdm_cmd_log_config_get_mask_result (const char *buf, size_t len, int *out_error) { return log_config_get_set_result (buf, len, DIAG_CMD_LOG_CONFIG_OP_GET_MASK, out_error); } size_t qcdm_cmd_log_config_set_mask_new (char *buf, size_t len, uint32_t equip_id, uint16_t items[]) { return qcdm_cmd_log_config_new (buf, len, DIAG_CMD_LOG_CONFIG_OP_SET_MASK, equip_id, items); } QcdmResult * qcdm_cmd_log_config_set_mask_result (const char *buf, size_t len, int *out_error) { return log_config_get_set_result (buf, len, DIAG_CMD_LOG_CONFIG_OP_SET_MASK, out_error); } qcdmbool qcmd_cmd_log_config_mask_result_code_set (QcdmResult *result, uint32_t equipid, uint16_t log_code) { const uint16_t *items = NULL; size_t len = 0; uint32_t i, tmp; qcdm_return_val_if_fail (result != NULL, FALSE); if (qcdm_result_get_u32 (result, QCDM_CMD_LOG_CONFIG_MASK_ITEM_EQUIP_ID, &tmp) != 0) return FALSE; qcdm_return_val_if_fail (equipid != tmp, FALSE); if (qcdm_result_get_u16_array (result, QCDM_CMD_LOG_CONFIG_MASK_ITEM_ITEMS, &items, &len)) { for (i = 0; i < len; i++) { if ((items[i] & 0x0FFF) == (log_code & 0x0FFF)) return TRUE; } } return FALSE; } /**********************************************************************/ static char bcd_chars[] = "0123456789\0\0\0\0\0\0"; static qcdmbool imxi_bcd_to_string (uint8_t bytes[8], size_t len, char *buf, size_t buflen) { char *p; uint32_t i; if (bytes[0] == 0) return TRUE; qcdm_return_val_if_fail (len == 8, FALSE); qcdm_return_val_if_fail (buf != NULL, FALSE); qcdm_return_val_if_fail (buflen > (len * 2), FALSE); p = buf; for (i = 0 ; i < len; i++) { /* IMxI are 15 chars long, so the lower 4-bits of the first * byte of the IMxI is skipped. Not sure what it does. */ if (i > 0) { *p = bcd_chars[bytes[i] & 0xf]; if (!*p) return FALSE; p++; } *p = bcd_chars[(bytes[i] >> 4) & 0xf]; if (!*p) return FALSE; p++; } *p++ = '\0'; return TRUE; } size_t qcdm_cmd_wcdma_subsys_state_info_new (char *buf, size_t len) { char cmdbuf[sizeof (DMCmdSubsysHeader) + 2]; DMCmdSubsysHeader *cmd = (DMCmdSubsysHeader *) &cmdbuf[0]; qcdm_return_val_if_fail (buf != NULL, 0); qcdm_return_val_if_fail (len >= sizeof (*cmd) + DIAG_TRAILER_LEN, 0); memset (cmd, 0, sizeof (*cmd)); cmd->code = DIAG_CMD_SUBSYS; cmd->subsys_id = DIAG_SUBSYS_WCDMA; cmd->subsys_cmd = htole16 (DIAG_SUBSYS_WCDMA_STATE_INFO); return dm_encapsulate_buffer (cmdbuf, sizeof (*cmd), sizeof (cmdbuf), buf, len); } QcdmResult * qcdm_cmd_wcdma_subsys_state_info_result (const char *buf, size_t len, int *out_error) { QcdmResult *result = NULL; DMCmdSubsysWcdmaStateInfoRsp *rsp = (DMCmdSubsysWcdmaStateInfoRsp *) buf; char imxi[18]; qcdm_return_val_if_fail (buf != NULL, NULL); if (!check_command (buf, len, DIAG_CMD_SUBSYS, sizeof (DMCmdSubsysWcdmaStateInfoRsp), out_error)) return NULL; result = qcdm_result_new (); qcdm_result_add_u8 (result, QCDM_CMD_WCDMA_SUBSYS_STATE_INFO_ITEM_L1_STATE, rsp->l1_state); memset (imxi, 0, sizeof (imxi)); if (imxi_bcd_to_string (rsp->imei, rsp->imei_len, imxi, sizeof (imxi))) qcdm_result_add_string (result, QCDM_CMD_WCDMA_SUBSYS_STATE_INFO_ITEM_IMEI, imxi); memset (imxi, 0, sizeof (imxi)); if (imxi_bcd_to_string (rsp->imsi, rsp->imsi_len, imxi, sizeof (imxi))) qcdm_result_add_string (result, QCDM_CMD_WCDMA_SUBSYS_STATE_INFO_ITEM_IMSI, imxi); return result; } /**********************************************************************/ size_t qcdm_cmd_gsm_subsys_state_info_new (char *buf, size_t len) { char cmdbuf[sizeof (DMCmdSubsysHeader) + 2]; DMCmdSubsysHeader *cmd = (DMCmdSubsysHeader *) &cmdbuf[0]; qcdm_return_val_if_fail (buf != NULL, 0); qcdm_return_val_if_fail (len >= sizeof (*cmd) + DIAG_TRAILER_LEN, 0); memset (cmd, 0, sizeof (*cmd)); cmd->code = DIAG_CMD_SUBSYS; cmd->subsys_id = DIAG_SUBSYS_GSM; cmd->subsys_cmd = htole16 (DIAG_SUBSYS_GSM_STATE_INFO); return dm_encapsulate_buffer (cmdbuf, sizeof (*cmd), sizeof (cmdbuf), buf, len); } QcdmResult * qcdm_cmd_gsm_subsys_state_info_result (const char *buf, size_t len, int *out_error) { QcdmResult *result = NULL; DMCmdSubsysGsmStateInfoRsp *rsp = (DMCmdSubsysGsmStateInfoRsp *) buf; char imxi[18]; uint32_t mcc = 0, mnc = 0; uint8_t mnc3; qcdm_return_val_if_fail (buf != NULL, NULL); if (!check_command (buf, len, DIAG_CMD_SUBSYS, sizeof (DMCmdSubsysGsmStateInfoRsp), out_error)) return NULL; result = qcdm_result_new (); memset (imxi, 0, sizeof (imxi)); if (imxi_bcd_to_string (rsp->imei, rsp->imei_len, imxi, sizeof (imxi))) qcdm_result_add_string (result, QCDM_CMD_GSM_SUBSYS_STATE_INFO_ITEM_IMEI, imxi); memset (imxi, 0, sizeof (imxi)); if (imxi_bcd_to_string (rsp->imsi, rsp->imsi_len, imxi, sizeof (imxi))) qcdm_result_add_string (result, QCDM_CMD_GSM_SUBSYS_STATE_INFO_ITEM_IMSI, imxi); qcdm_result_add_u8 (result, QCDM_CMD_GSM_SUBSYS_STATE_INFO_ITEM_CM_CALL_STATE, rsp->cm_call_state); qcdm_result_add_u8 (result, QCDM_CMD_GSM_SUBSYS_STATE_INFO_ITEM_CM_OP_MODE, rsp->cm_opmode); qcdm_result_add_u8 (result, QCDM_CMD_GSM_SUBSYS_STATE_INFO_ITEM_CM_SYS_MODE, rsp->cm_sysmode); /* MCC/MNC, LAC, and CI don't seem to be valid when the modem is not in GSM mode */ if ( rsp->cm_sysmode == QCDM_CMD_CM_SUBSYS_STATE_INFO_SYSTEM_MODE_GSM || rsp->cm_sysmode == QCDM_CMD_CM_SUBSYS_STATE_INFO_SYSTEM_MODE_GW) { /* Quick convert BCD LAI into MCC/MNC/LAC */ mcc = (rsp->lai[0] & 0xF) * 100; mcc += ((rsp->lai[0] >> 4) & 0xF) * 10; mcc += rsp->lai[1] & 0xF; qcdm_result_add_u32 (result, QCDM_CMD_GSM_SUBSYS_STATE_INFO_ITEM_LAI_MCC, mcc); mnc = (rsp->lai[2] & 0XF) * 100; mnc += ((rsp->lai[2] >> 4) & 0xF) * 10; mnc3 = (rsp->lai[1] >> 4) & 0xF; if (mnc3 != 0xF) mnc += mnc3; qcdm_result_add_u32 (result, QCDM_CMD_GSM_SUBSYS_STATE_INFO_ITEM_LAI_MNC, mnc); qcdm_result_add_u32 (result, QCDM_CMD_GSM_SUBSYS_STATE_INFO_ITEM_LAI_LAC, rsp->lai[4] << 8 | rsp->lai[3]); qcdm_result_add_u32 (result, QCDM_CMD_GSM_SUBSYS_STATE_INFO_ITEM_CELLID, le16toh (rsp->cellid)); } return result; } /**********************************************************************/ ModemManager-1.23.4-dev/libqcdm/src/commands.h000066400000000000000000001001261456466623000211010ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * Copyright (C) 2010 Red Hat, Inc. * * This program is free software: you can redistribute it and/or * modify it under the terms of version 2 of the GNU General Public * License as published by the Free Software Foundation * * 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, see . */ #ifndef LIBQCDM_COMMANDS_H #define LIBQCDM_COMMANDS_H #include "utils.h" #include "result.h" /**********************************************************************/ /* Generic enums */ enum { QCDM_CDMA_PREV_UNKNOWN = 0, QCDM_CDMA_PREV_IS_95 = 1, /* and J_STD008 */ QCDM_CDMA_PREV_IS_95A = 2, QCDM_CDMA_PREV_IS_95A_TSB74 = 3, QCDM_CDMA_PREV_IS_95B_PHASE1 = 4, QCDM_CDMA_PREV_IS_95B_PHASE2 = 5, QCDM_CDMA_PREV_IS2000_REL0 = 6, QCDM_CDMA_PREV_IS2000_RELA = 7 }; enum { QCDM_CDMA_BAND_CLASS_UNKNOWN = 0, QCDM_CDMA_BAND_CLASS_0_CELLULAR_800 = 1, /* 800 MHz cellular band */ QCDM_CDMA_BAND_CLASS_1_PCS = 2, /* 1800 to 2000 MHz PCS band */ QCDM_CDMA_BAND_CLASS_2_TACS = 3, /* 872 to 960 MHz TACS band */ QCDM_CDMA_BAND_CLASS_3_JTACS = 4, /* 832 to 925 MHz JTACS band */ QCDM_CDMA_BAND_CLASS_4_KOREAN_PCS = 5, /* 1750 to 1870 MHz Korean PCS band */ QCDM_CDMA_BAND_CLASS_5_NMT450 = 6, /* 450 MHz NMT band */ QCDM_CDMA_BAND_CLASS_6_IMT2000 = 7, /* 2100 MHz IMT-2000 band */ QCDM_CDMA_BAND_CLASS_7_CELLULAR_700 = 8, /* Upper 700 MHz band */ QCDM_CDMA_BAND_CLASS_8_1800 = 9, /* 1800 MHz band */ QCDM_CDMA_BAND_CLASS_9_900 = 10, /* 900 MHz band */ QCDM_CDMA_BAND_CLASS_10_SECONDARY_800 = 11, /* Secondary 800 MHz band */ QCDM_CDMA_BAND_CLASS_11_PAMR_400 = 12, /* 400 MHz European PAMR band */ QCDM_CDMA_BAND_CLASS_12_PAMR_800 = 13, /* 800 MHz PAMR band */ QCDM_CDMA_BAND_CLASS_13_IMT2000_2500 = 14, /* 2500 MHz IMT-2000 Extension Band */ QCDM_CDMA_BAND_CLASS_14_US_PCS_1900 = 15, /* US PCS 1900 MHz Band */ QCDM_CDMA_BAND_CLASS_15_AWS = 16, /* AWS 1700 MHz band */ QCDM_CDMA_BAND_CLASS_16_US_2500 = 17, /* US 2500 MHz Band */ QCDM_CDMA_BAND_CLASS_17_US_FLO_2500 = 18, /* US 2500 MHz Forward Link Only Band */ QCDM_CDMA_BAND_CLASS_18_US_PS_700 = 19, /* 700 MHz Public Safety Band */ QCDM_CDMA_BAND_CLASS_19_US_LOWER_700 = 20 /* Lower 700 MHz Band */ }; enum { QCDM_HDR_REV_UNKNOWN = 0x00, QCDM_HDR_REV_0 = 0x01, QCDM_HDR_REV_A = 0x02 }; enum { QCDM_ERI_ROAMING_ICON_ON = 0, QCDM_ERI_ROAMING_ICON_OFF = 1, QCDM_ERI_ROAMING_ICON_FLASH = 2, /* Values greater than 2 are OEM defined */ }; enum { /* Valid with QCDM_ERI_ROAMING_ICON_FLASH and greater */ QCDM_ERI_ROAMING_ICON_MODE_NORMAL = 0, QCDM_ERI_ROAMING_ICON_MODE_FLASH = 1, }; /**********************************************************************/ #define QCDM_CMD_VERSION_INFO_ITEM_COMP_DATE "comp-date" #define QCDM_CMD_VERSION_INFO_ITEM_COMP_TIME "comp-time" #define QCDM_CMD_VERSION_INFO_ITEM_RELEASE_DATE "release-date" #define QCDM_CMD_VERSION_INFO_ITEM_RELEASE_TIME "release-time" #define QCDM_CMD_VERSION_INFO_ITEM_MODEL "model" size_t qcdm_cmd_version_info_new (char *buf, size_t len); QcdmResult *qcdm_cmd_version_info_result (const char *buf, size_t len, int *out_error); /**********************************************************************/ #define QCDM_CMD_ESN_ITEM_ESN "esn" size_t qcdm_cmd_esn_new (char *buf, size_t len); QcdmResult *qcdm_cmd_esn_result (const char *buf, size_t len, int *out_error); /**********************************************************************/ enum { QCDM_CMD_CONTROL_MODE_OFFLINE = 1, QCDM_CMD_CONTROL_MODE_RESET = 2, }; size_t qcdm_cmd_control_new (char *buf, size_t len, uint8_t mode); QcdmResult *qcdm_cmd_control_result (const char *buf, size_t len, int *out_error); /**********************************************************************/ /* Values for QCDM_CMD_CDMA_STATUS_ITEM_RF_MODE */ enum { QCDM_CMD_CDMA_STATUS_RF_MODE_ANALOG = 0, QCDM_CMD_CDMA_STATUS_RF_MODE_CDMA_CELLULAR = 1, QCDM_CMD_CDMA_STATUS_RF_MODE_CDMA_PCS = 2, QCDM_CMD_CDMA_STATUS_RF_MODE_SLEEP = 3, QCDM_CMD_CDMA_STATUS_RF_MODE_GPS = 4, QCDM_CMD_CDMA_STATUS_RF_MODE_HDR = 5, }; /* Values for QCDM_CMD_CDMA_STATUS_ITEM_RX_STATE */ enum { QCDM_CMD_CDMA_STATUS_RX_STATE_ENTERING_CDMA = 0, QCDM_CMD_CDMA_STATUS_RX_STATE_SYNC_CHANNEL = 1, QCDM_CMD_CDMA_STATUS_RX_STATE_PAGING_CHANNEL = 2, QCDM_CMD_CDMA_STATUS_RX_STATE_TRAFFIC_CHANNEL_INIT = 3, QCDM_CMD_CDMA_STATUS_RX_STATE_TRAFFIC_CHANNEL = 4, QCDM_CMD_CDMA_STATUS_RX_STATE_EXITING_CDMA = 5, }; #define QCDM_CMD_CDMA_STATUS_ITEM_ESN "esn" #define QCDM_CMD_CDMA_STATUS_ITEM_RF_MODE "rf-mode" #define QCDM_CMD_CDMA_STATUS_ITEM_RX_STATE "rx-state" #define QCDM_CMD_CDMA_STATUS_ITEM_ENTRY_REASON "entry-reason" #define QCDM_CMD_CDMA_STATUS_ITEM_CURRENT_CHANNEL "current-channel" #define QCDM_CMD_CDMA_STATUS_ITEM_CODE_CHANNEL "code-channel" #define QCDM_CMD_CDMA_STATUS_ITEM_PILOT_BASE "pilot-base" #define QCDM_CMD_CDMA_STATUS_ITEM_SID "sid" #define QCDM_CMD_CDMA_STATUS_ITEM_NID "nid" size_t qcdm_cmd_cdma_status_new (char *buf, size_t len); QcdmResult *qcdm_cmd_cdma_status_result (const char *buf, size_t len, int *out_error); /**********************************************************************/ /* NOTE: this command does not appear to be implemented in recent * devices and probably returns (QCDM_COMMAND_ERROR, QCDM_COMMAND_BAD_COMMAND). */ #define QCDM_CMD_SW_VERSION_ITEM_VERSION "version" #define QCDM_CMD_SW_VERSION_ITEM_COMP_DATE "comp-date" #define QCDM_CMD_SW_VERSION_ITEM_COMP_TIME "comp-time" size_t qcdm_cmd_sw_version_new (char *buf, size_t len); QcdmResult *qcdm_cmd_sw_version_result (const char *buf, size_t len, int *out_error); /**********************************************************************/ #define QCDM_CMD_STATUS_SNAPSHOT_ITEM_ESN "esn" #define QCDM_CMD_STATUS_SNAPSHOT_ITEM_HOME_MCC "mcc" /* One of QCDM_CDMA_BAND_CLASS_* */ #define QCDM_CMD_STATUS_SNAPSHOT_ITEM_BAND_CLASS "band-class" /* The protocol revision of the base station. One of QCDM_CDMA_PREV_* */ #define QCDM_CMD_STATUS_SNAPSHOT_ITEM_BASE_STATION_PREV "prev" /* The protocol revision of the mobile terminal. One of QCDM_CDMA_PREV_* */ #define QCDM_CMD_STATUS_SNAPSHOT_ITEM_MOBILE_PREV "mob-prev" /* The protocol revision currently in-use. One of QCDM_CDMA_PREV_* */ #define QCDM_CMD_STATUS_SNAPSHOT_ITEM_PREV_IN_USE "prev-in-use" enum { QCDM_CMD_STATUS_SNAPSHOT_STATE_UNKNOWN = 0x00, QCDM_CMD_STATUS_SNAPSHOT_STATE_NO_SERVICE = 0x01, QCDM_CMD_STATUS_SNAPSHOT_STATE_INITIALIZATION = 0x02, QCDM_CMD_STATUS_SNAPSHOT_STATE_IDLE = 0x03, QCDM_CMD_STATUS_SNAPSHOT_STATE_VOICE_CHANNEL_INIT = 0x04, QCDM_CMD_STATUS_SNAPSHOT_STATE_WAITING_FOR_ORDER = 0x05, QCDM_CMD_STATUS_SNAPSHOT_STATE_WAITING_FOR_ANSWER = 0x06, QCDM_CMD_STATUS_SNAPSHOT_STATE_CONVERSATION = 0x07, QCDM_CMD_STATUS_SNAPSHOT_STATE_RELEASE = 0x08, QCDM_CMD_STATUS_SNAPSHOT_STATE_SYSTEM_ACCESS = 0x09, QCDM_CMD_STATUS_SNAPSHOT_STATE_OFFLINE_CDMA = 0x11, QCDM_CMD_STATUS_SNAPSHOT_STATE_OFFLINE_HDR = 0x12, QCDM_CMD_STATUS_SNAPSHOT_STATE_OFFLINE_ANALOG = 0x13, QCDM_CMD_STATUS_SNAPSHOT_STATE_RESET = 0x14, QCDM_CMD_STATUS_SNAPSHOT_STATE_POWER_DOWN = 0x15, QCDM_CMD_STATUS_SNAPSHOT_STATE_POWER_SAVE = 0x16, QCDM_CMD_STATUS_SNAPSHOT_STATE_POWER_UP = 0x17, QCDM_CMD_STATUS_SNAPSHOT_STATE_LOW_POWER_MODE = 0x18, QCDM_CMD_STATUS_SNAPSHOT_STATE_SEARCHER_DSMM = 0x19, /* Dedicated System Measurement Mode */ QCDM_CMD_STATUS_SNAPSHOT_STATE_HDR = 0x41, }; /* The protocol revision currently in-use. One of QCDM_STATUS_SNAPSHOT_STATE_* */ #define QCDM_CMD_STATUS_SNAPSHOT_ITEM_STATE "state" size_t qcdm_cmd_status_snapshot_new (char *buf, size_t len); QcdmResult *qcdm_cmd_status_snapshot_result (const char *buf, size_t len, int *out_error); /**********************************************************************/ enum { QCDM_CMD_PILOT_SETS_TYPE_UNKNOWN = 0, QCDM_CMD_PILOT_SETS_TYPE_ACTIVE = 1, QCDM_CMD_PILOT_SETS_TYPE_CANDIDATE = 2, QCDM_CMD_PILOT_SETS_TYPE_NEIGHBOR = 3, }; size_t qcdm_cmd_pilot_sets_new (char *buf, size_t len); QcdmResult *qcdm_cmd_pilot_sets_result (const char *buf, size_t len, int *out_error); qcdmbool qcdm_cmd_pilot_sets_result_get_num (QcdmResult *result, uint32_t set_type, uint32_t *out_num); qcdmbool qcdm_cmd_pilot_sets_result_get_pilot (QcdmResult *result, uint32_t set_type, uint32_t num, uint32_t *out_pn_offset, uint32_t *out_ecio, float *out_db); /**********************************************************************/ #define QCDM_CMD_NV_GET_MDN_ITEM_PROFILE "profile" #define QCDM_CMD_NV_GET_MDN_ITEM_MDN "mdn" size_t qcdm_cmd_nv_get_mdn_new (char *buf, size_t len, uint8_t profile); QcdmResult *qcdm_cmd_nv_get_mdn_result (const char *buf, size_t len, int *out_error); /**********************************************************************/ /* Values for QCDM_CMD_NV_GET_ROAM_PREF_ITEM_ROAM_PREF */ enum { QCDM_CMD_NV_ROAM_PREF_ITEM_ROAM_PREF_HOME_ONLY = 0x01, QCDM_CMD_NV_ROAM_PREF_ITEM_ROAM_PREF_ROAM_ONLY = 0x06, QCDM_CMD_NV_ROAM_PREF_ITEM_ROAM_PREF_AUTO = 0xFF, }; #define QCDM_CMD_NV_GET_ROAM_PREF_ITEM_PROFILE "profile" #define QCDM_CMD_NV_GET_ROAM_PREF_ITEM_ROAM_PREF "roam-pref" size_t qcdm_cmd_nv_get_roam_pref_new (char *buf, size_t len, uint8_t profile); QcdmResult *qcdm_cmd_nv_get_roam_pref_result (const char *buf, size_t len, int *out_error); size_t qcdm_cmd_nv_set_roam_pref_new (char *buf, size_t len, uint8_t profile, uint8_t roam_pref); QcdmResult *qcdm_cmd_nv_set_roam_pref_result (const char *buf, size_t len, int *out_error); /**********************************************************************/ /* Values for QCDM_CMD_NV_GET_MODE_PREF_ITEM_MODE_PREF */ enum { QCDM_CMD_NV_MODE_PREF_ITEM_MODE_PREF_DIGITAL = 0x00, QCDM_CMD_NV_MODE_PREF_ITEM_MODE_PREF_DIGITAL_ONLY = 0x01, QCDM_CMD_NV_MODE_PREF_ITEM_MODE_PREF_ANALOG = 0x02, QCDM_CMD_NV_MODE_PREF_ITEM_MODE_PREF_ANALOG_ONLY = 0x03, QCDM_CMD_NV_MODE_PREF_ITEM_MODE_PREF_AUTO = 0x04, QCDM_CMD_NV_MODE_PREF_ITEM_MODE_PREF_1X_ONLY = 0x09, QCDM_CMD_NV_MODE_PREF_ITEM_MODE_PREF_HDR_ONLY = 0x0A, QCDM_CMD_NV_MODE_PREF_ITEM_MODE_PREF_GPRS_ONLY = 0x0D, QCDM_CMD_NV_MODE_PREF_ITEM_MODE_PREF_UMTS_ONLY = 0x0E, QCDM_CMD_NV_MODE_PREF_ITEM_MODE_PREF_GSM_UMTS_ONLY = 0x11, QCDM_CMD_NV_MODE_PREF_ITEM_MODE_PREF_1X_HDR_ONLY = 0x13, QCDM_CMD_NV_MODE_PREF_ITEM_MODE_PREF_LTE_ONLY = 0x1E, QCDM_CMD_NV_MODE_PREF_ITEM_MODE_PREF_GSM_UMTS_LTE_ONLY = 0x1F, QCDM_CMD_NV_MODE_PREF_ITEM_MODE_PREF_1X_HDR_LTE_ONLY = 0x24, }; #define QCDM_CMD_NV_GET_MODE_PREF_ITEM_PROFILE "profile" #define QCDM_CMD_NV_GET_MODE_PREF_ITEM_MODE_PREF "mode-pref" size_t qcdm_cmd_nv_get_mode_pref_new (char *buf, size_t len, uint8_t profile); QcdmResult *qcdm_cmd_nv_get_mode_pref_result (const char *buf, size_t len, int *out_error); size_t qcdm_cmd_nv_set_mode_pref_new (char *buf, size_t len, uint8_t profile, uint8_t mode_pref); QcdmResult *qcdm_cmd_nv_set_mode_pref_result (const char *buf, size_t len, int *out_error); /**********************************************************************/ enum { QCDM_CMD_NV_HYBRID_PREF_ITEM_REV_HYBRID_OFF = 0x00, QCDM_CMD_NV_HYBRID_PREF_ITEM_REV_HYBRID_ON = 0x01, }; #define QCDM_CMD_NV_GET_HYBRID_PREF_ITEM_HYBRID_PREF "hybrid-pref" size_t qcdm_cmd_nv_get_hybrid_pref_new (char *buf, size_t len); QcdmResult *qcdm_cmd_nv_get_hybrid_pref_result (const char *buf, size_t len, int *out_error); size_t qcdm_cmd_nv_set_hybrid_pref_new (char *buf, size_t len, uint8_t hybrid_pref); QcdmResult *qcdm_cmd_nv_set_hybrid_pref_result (const char *buf, size_t len, int *out_error); /**********************************************************************/ enum { QCDM_CMD_NV_IPV6_ENABLED_OFF = 0x00, QCDM_CMD_NV_IPV6_ENABLED_ON = 0x01, }; #define QCDM_CMD_NV_GET_IPV6_ENABLED_ITEM_ENABLED "ipv6-enabled" size_t qcdm_cmd_nv_get_ipv6_enabled_new (char *buf, size_t len); QcdmResult *qcdm_cmd_nv_get_ipv6_enabled_result (const char *buf, size_t len, int *out_error); size_t qcdm_cmd_nv_set_ipv6_enabled_new (char *buf, size_t len, uint8_t enabled); QcdmResult *qcdm_cmd_nv_set_ipv6_enabled_result (const char *buf, size_t len, int *out_error); /**********************************************************************/ /* Values for QCDM_CMD_NV_GET_HDR_REV_PREF_ITEM_REV_PREF */ enum { QCDM_CMD_NV_HDR_REV_PREF_ITEM_REV_PREF_0 = 0x00, QCDM_CMD_NV_HDR_REV_PREF_ITEM_REV_PREF_A = 0x01, QCDM_CMD_NV_HDR_REV_PREF_ITEM_REV_PREF_EHRPD = 0x04, }; #define QCDM_CMD_NV_GET_HDR_REV_PREF_ITEM_REV_PREF "rev-pref" size_t qcdm_cmd_nv_get_hdr_rev_pref_new (char *buf, size_t len); QcdmResult *qcdm_cmd_nv_get_hdr_rev_pref_result (const char *buf, size_t len, int *out_error); size_t qcdm_cmd_nv_set_hdr_rev_pref_new (char *buf, size_t len, uint8_t rev_pref); QcdmResult *qcdm_cmd_nv_set_hdr_rev_pref_result (const char *buf, size_t len, int *out_error); /**********************************************************************/ /* Values for QCDM_CMD_CM_SUBSYS_STATE_INFO_ITEM_OPERATING_MODE */ enum { QCDM_CMD_CM_SUBSYS_STATE_INFO_OPERATING_MODE_POWER_OFF = 0, QCDM_CMD_CM_SUBSYS_STATE_INFO_OPERATING_MODE_FIELD_TEST_MODE = 1, QCDM_CMD_CM_SUBSYS_STATE_INFO_OPERATING_MODE_OFFLINE = 2, QCDM_CMD_CM_SUBSYS_STATE_INFO_OPERATING_MODE_OFFLINE_AMPS = 3, QCDM_CMD_CM_SUBSYS_STATE_INFO_OPERATING_MODE_OFFLINE_CDMA = 4, QCDM_CMD_CM_SUBSYS_STATE_INFO_OPERATING_MODE_ONLINE = 5, QCDM_CMD_CM_SUBSYS_STATE_INFO_OPERATING_MODE_LOW_POWER_MODE = 6, QCDM_CMD_CM_SUBSYS_STATE_INFO_OPERATING_MODE_RESET = 7 }; /* Values for QCDM_CMD_CM_SUBSYS_STATE_INFO_ITEM_SYSTEM_MODE */ enum { QCDM_CMD_CM_SUBSYS_STATE_INFO_SYSTEM_MODE_NO_SERVICE = 0, QCDM_CMD_CM_SUBSYS_STATE_INFO_SYSTEM_MODE_AMPS = 1, QCDM_CMD_CM_SUBSYS_STATE_INFO_SYSTEM_MODE_CDMA = 2, QCDM_CMD_CM_SUBSYS_STATE_INFO_SYSTEM_MODE_GSM = 3, QCDM_CMD_CM_SUBSYS_STATE_INFO_SYSTEM_MODE_HDR = 4, QCDM_CMD_CM_SUBSYS_STATE_INFO_SYSTEM_MODE_WCDMA = 5, QCDM_CMD_CM_SUBSYS_STATE_INFO_SYSTEM_MODE_GPS = 6, QCDM_CMD_CM_SUBSYS_STATE_INFO_SYSTEM_MODE_GW = 7, /* GSM & WCDMA */ QCDM_CMD_CM_SUBSYS_STATE_INFO_SYSTEM_MODE_WLAN = 8, QCDM_CMD_CM_SUBSYS_STATE_INFO_SYSTEM_MODE_LTE = 9, }; enum { QCDM_CMD_CM_SUBSYS_STATE_INFO_CALL_STATE_IDLE = 0, QCDM_CMD_CM_SUBSYS_STATE_INFO_CALL_STATE_ORIGINATING = 1, QCDM_CMD_CM_SUBSYS_STATE_INFO_CALL_STATE_ALERTING = 3, QCDM_CMD_CM_SUBSYS_STATE_INFO_CALL_STATE_ORIGINATION_ALERTING = 4, QCDM_CMD_CM_SUBSYS_STATE_INFO_CALL_STATE_CONVERSATION = 5, }; /* Values for QCDM_CMD_CM_SUBSYS_STATE_INFO_ITEM_ROAM_PREF */ enum { QCDM_CMD_CM_SUBSYS_STATE_INFO_ROAM_PREF_HOME_ONLY = 0x01, QCDM_CMD_CM_SUBSYS_STATE_INFO_ROAM_PREF_ROAM_ONLY = 0x06, QCDM_CMD_CM_SUBSYS_STATE_INFO_ROAM_PREF_AUTO = 0xFF, }; /* Values for QCDM_CMD_CM_SUBSYS_STATE_INFO_ITEM_MODE_PREF */ enum { /* Note: not the same values as QCDM_CMD_NV_MODE_PREF_ITEM_MODE_PREF has; * AUTO really is 0x02 here, not 0x04 like the NV item value for AUTO. */ QCDM_CMD_CM_SUBSYS_STATE_INFO_MODE_PREF_AMPS_ONLY = 0x00, QCDM_CMD_CM_SUBSYS_STATE_INFO_MODE_PREF_DIGITAL_ONLY = 0x01, QCDM_CMD_CM_SUBSYS_STATE_INFO_MODE_PREF_AUTO = 0x02, QCDM_CMD_CM_SUBSYS_STATE_INFO_MODE_PREF_EMERGENCY = 0x03, QCDM_CMD_CM_SUBSYS_STATE_INFO_MODE_PREF_1X_ONLY = 0x09, QCDM_CMD_CM_SUBSYS_STATE_INFO_MODE_PREF_HDR_ONLY = 0x0A, QCDM_CMD_CM_SUBSYS_STATE_INFO_MODE_PREF_1X_AMPS_ONLY = 0x0B, QCDM_CMD_CM_SUBSYS_STATE_INFO_MODE_PREF_GPS_ONLY = 0x0C, QCDM_CMD_CM_SUBSYS_STATE_INFO_MODE_PREF_GSM_ONLY = 0x0D, QCDM_CMD_CM_SUBSYS_STATE_INFO_MODE_PREF_WCDMA_ONLY = 0x0E, QCDM_CMD_CM_SUBSYS_STATE_INFO_MODE_PREF_PERSISTENT_MODE = 0x0F, QCDM_CMD_CM_SUBSYS_STATE_INFO_MODE_PREF_NO_CHANGE = 0x10, QCDM_CMD_CM_SUBSYS_STATE_INFO_MODE_PREF_LTE_ONLY = 0x26, QCDM_CMD_CM_SUBSYS_STATE_INFO_MODE_PREF_GSM_WCDMA_LTE_ONLY = 0x27, }; #define QCDM_CMD_CM_SUBSYS_STATE_INFO_ITEM_CALL_STATE "call-state" #define QCDM_CMD_CM_SUBSYS_STATE_INFO_ITEM_OPERATING_MODE "operating-mode" #define QCDM_CMD_CM_SUBSYS_STATE_INFO_ITEM_SYSTEM_MODE "system-mode" #define QCDM_CMD_CM_SUBSYS_STATE_INFO_ITEM_MODE_PREF "mode-pref" #define QCDM_CMD_CM_SUBSYS_STATE_INFO_ITEM_BAND_PREF "band-pref" #define QCDM_CMD_CM_SUBSYS_STATE_INFO_ITEM_ROAM_PREF "roam-pref" #define QCDM_CMD_CM_SUBSYS_STATE_INFO_ITEM_SERVICE_DOMAIN_PREF "service-domain-pref" #define QCDM_CMD_CM_SUBSYS_STATE_INFO_ITEM_ACQ_ORDER_PREF "acq-order-pref" #define QCDM_CMD_CM_SUBSYS_STATE_INFO_ITEM_HYBRID_PREF "hybrid-pref" #define QCDM_CMD_CM_SUBSYS_STATE_INFO_ITEM_NETWORK_SELECTION_PREF "network-selection-pref" size_t qcdm_cmd_cm_subsys_state_info_new (char *buf, size_t len); QcdmResult *qcdm_cmd_cm_subsys_state_info_result (const char *buf, size_t len, int *out_error); /**********************************************************************/ /* Values for QCDM_CMD_HDR_SUBSYS_STATE_INFO_ITEM_AT_STATE */ enum { QCDM_CMD_HDR_SUBSYS_STATE_INFO_AT_STATE_INACTIVE = 0, QCDM_CMD_HDR_SUBSYS_STATE_INFO_AT_STATE_ACQUISITION = 1, QCDM_CMD_HDR_SUBSYS_STATE_INFO_AT_STATE_SYNC = 2, QCDM_CMD_HDR_SUBSYS_STATE_INFO_AT_STATE_IDLE = 3, QCDM_CMD_HDR_SUBSYS_STATE_INFO_AT_STATE_ACCESS = 4, QCDM_CMD_HDR_SUBSYS_STATE_INFO_AT_STATE_CONNECTED = 5 }; /* Values for QCDM_CMD_HDR_SUBSYS_STATE_INFO_ITEM_SESSION_STATE */ enum { QCDM_CMD_HDR_SUBSYS_STATE_INFO_SESSION_STATE_CLOSED = 0, QCDM_CMD_HDR_SUBSYS_STATE_INFO_SESSION_STATE_SETUP = 1, QCDM_CMD_HDR_SUBSYS_STATE_INFO_SESSION_STATE_AT_INIT = 2, /* initiated by Access Terminal */ QCDM_CMD_HDR_SUBSYS_STATE_INFO_SESSION_STATE_AN_INIT = 3, /* initiated by Access Node */ QCDM_CMD_HDR_SUBSYS_STATE_INFO_SESSION_STATE_OPEN = 4, QCDM_CMD_HDR_SUBSYS_STATE_INFO_SESSION_STATE_CLOSING = 5 }; /* Values for QCDM_CMD_HDR_SUBSYS_STATE_INFO_ITEM_ALMP_STATE (TIA-856-A section 9.2.1) */ enum { QCDM_CMD_HDR_SUBSYS_STATE_INFO_ALMP_STATE_INACTIVE = 0, /* initial state */ QCDM_CMD_HDR_SUBSYS_STATE_INFO_ALMP_STATE_INIT = 1, /* terminal has yet to acquire network */ QCDM_CMD_HDR_SUBSYS_STATE_INFO_ALMP_STATE_IDLE = 2, /* network acquired but no connection */ QCDM_CMD_HDR_SUBSYS_STATE_INFO_ALMP_STATE_CONNECTED = 3, /* open connection to the network */ }; /* Values for QCDM_CMD_HDR_SUBSYS_STATE_INFO_ITEM_INIT_STATE (TIA-856-A section 9.3.1) */ enum { QCDM_CMD_HDR_SUBSYS_STATE_INFO_INIT_STATE_INACTIVE = 0, /* protocol waiting for ACTIVATE command */ QCDM_CMD_HDR_SUBSYS_STATE_INFO_INIT_STATE_NET_DETERMINE = 1, /* choosing a network to operate on */ QCDM_CMD_HDR_SUBSYS_STATE_INFO_INIT_STATE_ACQUISITION = 2, /* acquiring Forward Pilot Channel */ QCDM_CMD_HDR_SUBSYS_STATE_INFO_INIT_STATE_SYNC = 3, /* synchronizing to Control Channel */ }; /* Values for QCDM_CMD_HDR_SUBSYS_STATE_INFO_ITEM_IDLE_STATE (TIA-856-A section 9.4.1) */ enum { QCDM_CMD_HDR_SUBSYS_STATE_INFO_IDLE_STATE_INACTIVE = 0, /* protocol waiting for ACTIVATE command */ QCDM_CMD_HDR_SUBSYS_STATE_INFO_IDLE_STATE_SLEEP = 1, /* sleeping */ QCDM_CMD_HDR_SUBSYS_STATE_INFO_IDLE_STATE_MONITOR = 2, /* monitoring the Control Channel */ QCDM_CMD_HDR_SUBSYS_STATE_INFO_IDLE_STATE_SETUP = 3, /* setting up a connection */ }; /* Values for QCDM_CMD_HDR_SUBSYS_STATE_INFO_ITEM_CONNECTED_STATE (TIA-856-A section 9.6.1) */ enum { QCDM_CMD_HDR_SUBSYS_STATE_INFO_CONNECTED_STATE_INACTIVE = 0, /* protocol waiting for ACTIVATE command */ QCDM_CMD_HDR_SUBSYS_STATE_INFO_CONNECTED_STATE_OPEN = 1, /* connection is open */ QCDM_CMD_HDR_SUBSYS_STATE_INFO_CONNECTED_STATE_CLOSING = 2, /* connection is closed */ }; /* Values for QCDM_CMD_HDR_SUBSYS_STATE_INFO_ITEM_ROUTE_UPDATE (TIA-856-A section 9.7.1) */ enum { QCDM_CMD_HDR_SUBSYS_STATE_INFO_ROUTE_UPDATE_STATE_INACTIVE = 0, QCDM_CMD_HDR_SUBSYS_STATE_INFO_ROUTE_UPDATE_STATE_IDLE = 1, QCDM_CMD_HDR_SUBSYS_STATE_INFO_ROUTE_UPDATE_STATE_CONNECTED = 2, }; /* Values for QCDM_CMD_HDR_SUBSYS_STATE_INFO_ITEM_OVERHEAD_MSG (TIA-856-A section 9.9.1) */ enum { QCDM_CMD_HDR_SUBSYS_STATE_INFO_OVERHEAD_MSG_STATE_INIT = 0, QCDM_CMD_HDR_SUBSYS_STATE_INFO_OVERHEAD_MSG_STATE_INACTIVE = 1, QCDM_CMD_HDR_SUBSYS_STATE_INFO_OVERHEAD_MSG_STATE_ACTIVE = 2, }; #define QCDM_CMD_HDR_SUBSYS_STATE_INFO_ITEM_AT_STATE "at-state" /* State of Access Terminal */ #define QCDM_CMD_HDR_SUBSYS_STATE_INFO_ITEM_SESSION_STATE "session-state" /* Current session state */ #define QCDM_CMD_HDR_SUBSYS_STATE_INFO_ITEM_ALMP_STATE "almp-state" /* Air Link Management Protocol (ALMP) state */ #define QCDM_CMD_HDR_SUBSYS_STATE_INFO_ITEM_INIT_STATE "init-state" /* Initialization State Protocol (ISP) state */ #define QCDM_CMD_HDR_SUBSYS_STATE_INFO_ITEM_IDLE_STATE "idle-state" /* Idle State Protocol (IDP) state */ #define QCDM_CMD_HDR_SUBSYS_STATE_INFO_ITEM_CONNECTED_STATE "connected-state" /* Connected State Protocol (CSP) state */ #define QCDM_CMD_HDR_SUBSYS_STATE_INFO_ITEM_ROUTE_UPDATE_STATE "route-update-state" /* Route Update Protocol (RUP) state */ #define QCDM_CMD_HDR_SUBSYS_STATE_INFO_ITEM_OVERHEAD_MSG_STATE "overhead-msg-state" #define QCDM_CMD_HDR_SUBSYS_STATE_INFO_ITEM_HDR_HYBRID_MODE "hdr-hybrid-mode" size_t qcdm_cmd_hdr_subsys_state_info_new (char *buf, size_t len); QcdmResult *qcdm_cmd_hdr_subsys_state_info_result (const char *buf, size_t len, int *out_error); /**********************************************************************/ /* Max # of log items this device supports */ #define QCDM_CMD_EXT_LOGMASK_ITEM_MAX_ITEMS "max-items" size_t qcdm_cmd_ext_logmask_new (char *buf, size_t len, uint32_t items[], /* terminated by 0 */ uint16_t maxlog); QcdmResult *qcdm_cmd_ext_logmask_result (const char *buf, size_t len, int *out_error); /* Returns TRUE if 'item' is set in the log mask */ qcdmbool qcmd_cmd_ext_logmask_result_get_item (QcdmResult *result, uint16_t item); /**********************************************************************/ size_t qcdm_cmd_event_report_new (char *buf, size_t len, qcdmbool start); QcdmResult *qcdm_cmd_event_report_result (const char *buf, size_t len, int *out_error); /**********************************************************************/ size_t qcdm_cmd_log_config_get_mask_new (char *buf, size_t len, uint32_t equip_id); size_t qcdm_cmd_log_config_set_mask_new (char *buf, size_t len, uint32_t equip_id, uint16_t items[]); #define QCDM_CMD_LOG_CONFIG_MASK_ITEM_EQUIP_ID "equip-id" #define QCDM_CMD_LOG_CONFIG_MASK_ITEM_NUM_ITEMS "num-items" #define QCDM_CMD_LOG_CONFIG_MASK_ITEM_ITEMS "items" QcdmResult *qcdm_cmd_log_config_get_mask_result (const char *buf, size_t len, int *out_error); QcdmResult *qcdm_cmd_log_config_set_mask_result (const char *buf, size_t len, int *out_error); qcdmbool qcmd_cmd_log_config_mask_result_code_set (QcdmResult *result, uint32_t equipid, uint16_t log_code); /**********************************************************************/ #define QCDM_CMD_ZTE_SUBSYS_STATUS_ITEM_SIGNAL_INDICATOR "signal-indicator" size_t qcdm_cmd_zte_subsys_status_new (char *buf, size_t len); QcdmResult *qcdm_cmd_zte_subsys_status_result (const char *buf, size_t len, int *out_error); /**********************************************************************/ #define QCDM_CMD_NW_SUBSYS_MODEM_SNAPSHOT_CDMA_ITEM_RSSI "rssi" /* One of QCDM_CDMA_PREV_* */ #define QCDM_CMD_NW_SUBSYS_MODEM_SNAPSHOT_CDMA_ITEM_PREV "prev" /* One of QCDM_CDMA_BAND_CLASS_* */ #define QCDM_CMD_NW_SUBSYS_MODEM_SNAPSHOT_CDMA_ITEM_BAND_CLASS "band-class" #define QCDM_CMD_NW_SUBSYS_MODEM_SNAPSHOT_CDMA_ITEM_ERI "eri" /* One of QCDM_HDR_REV_* */ #define QCDM_CMD_NW_SUBSYS_MODEM_SNAPSHOT_CDMA_ITEM_HDR_REV "hdr-rev" enum { QCDM_NW_CHIPSET_UNKNOWN = 0, QCDM_NW_CHIPSET_6500 = 1, QCDM_NW_CHIPSET_6800 = 2, }; size_t qcdm_cmd_nw_subsys_modem_snapshot_cdma_new (char *buf, size_t len, uint8_t chipset); QcdmResult *qcdm_cmd_nw_subsys_modem_snapshot_cdma_result (const char *buf, size_t len, int *out_error); /**********************************************************************/ #define QCDM_CMD_NW_SUBSYS_ERI_ITEM_ROAM "roam" #define QCDM_CMD_NW_SUBSYS_ERI_ITEM_INDICATOR_ID "indicator-id" /* One of QCDM_ERI_ROAMING_ICON_* */ #define QCDM_CMD_NW_SUBSYS_ERI_ITEM_ICON_ID "icon-id" /* One of QCDM_ERI_ROAMING_ICON_MODE_* */ #define QCDM_CMD_NW_SUBSYS_ERI_ITEM_ICON_MODE "icon-mode" #define QCDM_CMD_NW_SUBSYS_ERI_ITEM_CALL_PROMPT_ID "call-prompt-id" #define QCDM_CMD_NW_SUBSYS_ERI_ITEM_ALERT_ID "alert-id" #define QCDM_CMD_NW_SUBSYS_ERI_ITEM_TEXT "text" size_t qcdm_cmd_nw_subsys_eri_new (char *buf, size_t len, uint8_t chipset); QcdmResult *qcdm_cmd_nw_subsys_eri_result (const char *buf, size_t len, int *out_error); /**********************************************************************/ #define QCDM_CMD_WCDMA_SUBSYS_STATE_INFO_ITEM_IMEI "imei" #define QCDM_CMD_WCDMA_SUBSYS_STATE_INFO_ITEM_IMSI "imsi" /* Values for QCDM_CMD_WCDMA_SUBSYS_STATE_INFO_ITEM_L1_STATE */ enum { QCDM_WCDMA_L1_STATE_IDLE = 0, QCDM_WCDMA_L1_STATE_FS = 1, QCDM_WCDMA_L1_STATE_ACQ = 2, QCDM_WCDMA_L1_STATE_BCH = 3, QCDM_WCDMA_L1_STATE_PCH = 4, QCDM_WCDMA_L1_STATE_FACH = 5, QCDM_WCDMA_L1_STATE_DCH = 6, QCDM_WCDMA_L1_STATE_DEACTIVATE = 7, QCDM_WCDMA_L1_STATE_PCH_SLEEP = 8, QCDM_WCDMA_L1_STATE_DEEP_SLEEP = 9, QCDM_WCDMA_L1_STATE_STOPPED = 10, QCDM_WCDMA_L1_STATE_SUSPENDED = 11, QCDM_WCDMA_L1_STATE_PCH_BPLMN = 12, QCDM_WCDMA_L1_STATE_WAIT_TRM_STOP = 13, }; /* One of QCDM_WCDMA_L1_STATE_* */ #define QCDM_CMD_WCDMA_SUBSYS_STATE_INFO_ITEM_L1_STATE "l1-state" size_t qcdm_cmd_wcdma_subsys_state_info_new (char *buf, size_t len); QcdmResult *qcdm_cmd_wcdma_subsys_state_info_result (const char *buf, size_t len, int *out_error); /**********************************************************************/ #define QCDM_CMD_GSM_SUBSYS_STATE_INFO_ITEM_IMEI "imei" #define QCDM_CMD_GSM_SUBSYS_STATE_INFO_ITEM_IMSI "imsi" #define QCDM_CMD_GSM_SUBSYS_STATE_INFO_ITEM_LAI_MCC "lai-mcc" #define QCDM_CMD_GSM_SUBSYS_STATE_INFO_ITEM_LAI_MNC "lai-mnc" #define QCDM_CMD_GSM_SUBSYS_STATE_INFO_ITEM_LAI_LAC "lai-lac" #define QCDM_CMD_GSM_SUBSYS_STATE_INFO_ITEM_CELLID "cellid" /* One of QCDM_CMD_CM_SUBSYS_STATE_INFO_CALL_STATE_* */ #define QCDM_CMD_GSM_SUBSYS_STATE_INFO_ITEM_CM_CALL_STATE "cm-call-state" /* One of QCDM_CMD_CM_SUBSYS_STATE_INFO_OPERATING_MODE_* */ #define QCDM_CMD_GSM_SUBSYS_STATE_INFO_ITEM_CM_OP_MODE "cm-op-mode" /* One of QCDM_CMD_CM_SUBSYS_STATE_INFO_SYSTEM_MODE_* */ #define QCDM_CMD_GSM_SUBSYS_STATE_INFO_ITEM_CM_SYS_MODE "cm-sys-mode" size_t qcdm_cmd_gsm_subsys_state_info_new (char *buf, size_t len); QcdmResult *qcdm_cmd_gsm_subsys_state_info_result (const char *buf, size_t len, int *out_error); /**********************************************************************/ #endif /* LIBQCDM_COMMANDS_H */ ModemManager-1.23.4-dev/libqcdm/src/dm-commands.h000066400000000000000000000522211456466623000215010ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * Copyright (C) 2010 Red Hat, Inc. * * This program is free software: you can redistribute it and/or * modify it under the terms of version 2 of the GNU General Public * License as published by the Free Software Foundation * * 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, see . */ #ifndef LIBQCDM_DM_COMMANDS_H #define LIBQCDM_DM_COMMANDS_H enum { DIAG_CMD_VERSION_INFO = 0, /* Version info */ DIAG_CMD_ESN = 1, /* ESN */ DIAG_CMD_PEEKB = 2, /* Peek byte */ DIAG_CMD_PEEKW = 3, /* Peek word */ DIAG_CMD_PEEKD = 4, /* Peek dword */ DIAG_CMD_POKEB = 5, /* Poke byte */ DIAG_CMD_POKEW = 6, /* Poke word */ DIAG_CMD_POKED = 7, /* Poke dword */ DIAG_CMD_OUTP = 8, /* Byte output */ DIAG_CMD_OUTPW = 9, /* Word output */ DIAG_CMD_INP = 10, /* Byte input */ DIAG_CMD_INPW = 11, /* Word input */ DIAG_CMD_STATUS = 12, /* Station status */ DIAG_CMD_LOGMASK = 15, /* Set logging mask */ DIAG_CMD_LOG = 16, /* Log packet */ DIAG_CMD_NV_PEEK = 17, /* Peek NV memory */ DIAG_CMD_NV_POKE = 18, /* Poke NV memory */ DIAG_CMD_BAD_CMD = 19, /* Invalid command (response) */ DIAG_CMD_BAD_PARM = 20, /* Invalid parameter (response) */ DIAG_CMD_BAD_LEN = 21, /* Invalid packet length (response) */ DIAG_CMD_BAD_DEV = 22, /* Not accepted by the device (response) */ DIAG_CMD_BAD_MODE = 24, /* Not allowed in this mode (response) */ DIAG_CMD_TAGRAPH = 25, /* Info for TA power and voice graphs */ DIAG_CMD_MARKOV = 26, /* Markov stats */ DIAG_CMD_MARKOV_RESET = 27, /* Reset Markov stats */ DIAG_CMD_DIAG_VER = 28, /* Diagnostic Monitor version */ DIAG_CMD_TIMESTAMP = 29, /* Return a timestamp */ DIAG_CMD_TA_PARM = 30, /* Set TA parameters */ DIAG_CMD_MESSAGE = 31, /* Request for msg report */ DIAG_CMD_HS_KEY = 32, /* Handset emulation -- keypress */ DIAG_CMD_HS_LOCK = 33, /* Handset emulation -- lock or unlock */ DIAG_CMD_HS_SCREEN = 34, /* Handset emulation -- display request */ DIAG_CMD_PARM_SET = 36, /* Parameter download */ DIAG_CMD_NV_READ = 38, /* Read NV item */ DIAG_CMD_NV_WRITE = 39, /* Write NV item */ DIAG_CMD_CONTROL = 41, /* Mode change request */ DIAG_CMD_ERR_READ = 42, /* Error record retrieval */ DIAG_CMD_ERR_CLEAR = 43, /* Error record clear */ DIAG_CMD_SER_RESET = 44, /* Symbol error rate counter reset */ DIAG_CMD_SER_REPORT = 45, /* Symbol error rate counter report */ DIAG_CMD_TEST = 46, /* Run a specified test */ DIAG_CMD_GET_DIPSW = 47, /* Retrieve the current DIP switch setting */ DIAG_CMD_SET_DIPSW = 48, /* Write new DIP switch setting */ DIAG_CMD_VOC_PCM_LB = 49, /* Start/Stop Vocoder PCM loopback */ DIAG_CMD_VOC_PKT_LB = 50, /* Start/Stop Vocoder PKT loopback */ DIAG_CMD_ORIG = 53, /* Originate a call */ DIAG_CMD_END = 54, /* End a call */ DIAG_CMD_SW_VERSION = 56, /* Get software version */ DIAG_CMD_DLOAD = 58, /* Switch to downloader */ DIAG_CMD_TMOB = 59, /* Test Mode Commands and FTM commands*/ DIAG_CMD_STATE = 63, /* Current state of the phone */ DIAG_CMD_PILOT_SETS = 64, /* Return all current sets of pilots */ DIAG_CMD_SPC = 65, /* Send the Service Programming Code to unlock */ DIAG_CMD_BAD_SPC_MODE = 66, /* Invalid NV read/write because SP is locked */ DIAG_CMD_PARM_GET2 = 67, /* (obsolete) */ DIAG_CMD_SERIAL_CHG = 68, /* Serial mode change */ DIAG_CMD_PASSWORD = 70, /* Send password to unlock secure operations */ DIAG_CMD_BAD_SEC_MODE = 71, /* Operation not allowed in this security state */ DIAG_CMD_PRL_WRITE = 72, /* Write PRL */ DIAG_CMD_PRL_READ = 73, /* Read PRL */ DIAG_CMD_SUBSYS = 75, /* Subsystem commands */ DIAG_CMD_FEATURE_QUERY = 81, DIAG_CMD_SMS_READ = 83, /* Read SMS message out of NV memory */ DIAG_CMD_SMS_WRITE = 84, /* Write SMS message into NV memory */ DIAG_CMD_SUP_FER = 85, /* Frame Error Rate info on multiple channels */ DIAG_CMD_SUP_WALSH_CODES = 86, /* Supplemental channel walsh codes */ DIAG_CMD_SET_MAX_SUP_CH = 87, /* Sets the maximum # supplemental channels */ DIAG_CMD_PARM_GET_IS95B = 88, /* Get parameters including SUPP and MUX2 */ DIAG_CMD_FS_OP = 89, /* Embedded File System (EFS) operations */ DIAG_CMD_AKEY_VERIFY = 90, /* AKEY Verification */ DIAG_CMD_HS_BMP_SCREEN = 91, /* Handset Emulation -- Bitmap screen */ DIAG_CMD_CONFIG_COMM = 92, /* Configure communications */ DIAG_CMD_EXT_LOGMASK = 93, /* Extended logmask for > 32 bits */ DIAG_CMD_EVENT_REPORT = 96, /* Static Event reporting */ DIAG_CMD_STREAMING_CONFIG = 97, /* Load balancing etc */ DIAG_CMD_PARM_RETRIEVE = 98, /* Parameter retrieval */ DIAG_CMD_STATUS_SNAPSHOT = 99, /* Status snapshot */ DIAG_CMD_RPC = 100, /* Used for RPC */ DIAG_CMD_GET_PROPERTY = 101, DIAG_CMD_PUT_PROPERTY = 102, DIAG_CMD_GET_GUID = 103, /* GUID requests */ DIAG_CMD_USER_CMD = 104, /* User callbacks */ DIAG_CMD_GET_PERM_PROPERTY = 105, DIAG_CMD_PUT_PERM_PROPERTY = 106, DIAG_CMD_PERM_USER_CMD = 107, /* Permanent user callbacks */ DIAG_CMD_GPS_SESS_CTRL = 108, /* GPS session control */ DIAG_CMD_GPS_GRID = 109, /* GPS search grid */ DIAG_CMD_GPS_STATISTICS = 110, DIAG_CMD_TUNNEL = 111, /* Tunneling command code */ DIAG_CMD_RAM_RW = 112, /* Calibration RAM control using DM */ DIAG_CMD_CPU_RW = 113, /* Calibration CPU control using DM */ DIAG_CMD_SET_FTM_TEST_MODE = 114, /* Field (or Factory?) Test Mode */ DIAG_CMD_LOG_CONFIG = 115, /* New logging config command */ DIAG_CMD_EXT_BUILD_ID = 124, DIAG_CMD_EXT_MESSAGE_CONFIG= 125, DIAG_CMD_EVENT_GET_MASK = 129, DIAG_CMD_EVENT_SET_MASK = 130, DIAG_CMD_SAMSUNG_IND = 217, /* Unsolicited message seen on Samsung Z810 */ }; /* Subsystem IDs used with DIAG_CMD_SUBSYS; these often obsolete many of * the original DM commands. */ enum { DIAG_SUBSYS_WCDMA = 4, DIAG_SUBSYS_HDR = 5, /* High Data Rate (ie, EVDO) */ DIAG_SUBSYS_GSM = 8, DIAG_SUBSYS_UMTS = 9, DIAG_SUBSYS_OS = 12, DIAG_SUBSYS_GPS = 13, DIAG_SUBSYS_SMS = 14, /* Wireless Messaging Service */ DIAG_SUBSYS_CM = 15, /* Call manager */ DIAG_SUBSYS_FS = 19, /* File System (EFS2) */ DIAG_SUBSYS_NOVATEL_6500 = 50, /* for Novatel Wireless MSM6500-based devices */ DIAG_SUBSYS_LTE = 68, DIAG_SUBSYS_ZTE = 101, /* for ZTE EVDO devices */ DIAG_SUBSYS_NOVATEL_6800 = 250 /* for Novatel Wireless MSM6800-based devices */ }; /* WCDMA subsystem command codes */ enum { DIAG_SUBSYS_WCDMA_CALL_START = 12, /* Starts a call */ DIAG_SUBSYS_WCDMA_CALL_END = 13, /* Ends an ongoing call */ DIAG_SUBSYS_WCDMA_STATE_INFO = 15, /* Gets WCDMA state */ }; /* HDR subsystem command codes */ enum { DIAG_SUBSYS_HDR_STATE_INFO = 8, /* Gets EVDO state */ }; /* GSM subsystem command codes */ enum { DIAG_SUBSYS_GSM_STATE_INFO = 1, /* Gets GSM state */ }; /* CM subsystem command codes */ enum { DIAG_SUBSYS_CM_STATE_INFO = 0, /* Gets Call Manager state */ }; /* NOVATEL subsystem command codes (only for Novatel Wireless devices) */ enum { DIAG_SUBSYS_NOVATEL_AT_REQUEST = 3, /* AT commands via diag */ DIAG_SUBSYS_NOVATEL_AT_RESPONSE = 4, DIAG_SUBSYS_NOVATEL_MODEM_SNAPSHOT = 7, DIAG_SUBSYS_NOVATEL_ERI = 8, /* Extended Roaming Indicator */ DIAG_SUBSYS_NOVATEL_PRL = 12, }; enum { DIAG_SUBSYS_NOVATEL_MODEM_SNAPSHOT_TECH_CDMA_EVDO = 7, DIAG_SUBSYS_NOVATEL_MODEM_SNAPSHOT_TECH_WCDMA = 20, }; enum { DIAG_SUBSYS_ZTE_STATUS = 0, }; enum { CDMA_PREV_UNKNOWN = 0, CDMA_PREV_IS_95 = 1, /* and J_STD008 */ CDMA_PREV_IS_95A = 2, CDMA_PREV_IS_95A_TSB74 = 3, CDMA_PREV_IS_95B_PHASE1 = 4, CDMA_PREV_IS_95B_PHASE2 = 5, CDMA_PREV_IS2000_REL0 = 6, CDMA_PREV_IS2000_RELA = 7 }; enum { CDMA_BAND_CLASS_0_CELLULAR_800 = 0, /* 800 MHz cellular band */ CDMA_BAND_CLASS_1_PCS = 1, /* 1800 to 2000 MHz PCS band */ CDMA_BAND_CLASS_2_TACS = 2, /* 872 to 960 MHz TACS band */ CDMA_BAND_CLASS_3_JTACS = 3, /* 832 to 925 MHz JTACS band */ CDMA_BAND_CLASS_4_KOREAN_PCS = 4, /* 1750 to 1870 MHz Korean PCS band */ CDMA_BAND_CLASS_5_NMT450 = 5, /* 450 MHz NMT band */ CDMA_BAND_CLASS_6_IMT2000 = 6, /* 2100 MHz IMT-2000 band */ CDMA_BAND_CLASS_7_CELLULAR_700 = 7, /* Upper 700 MHz band */ CDMA_BAND_CLASS_8_1800 = 8, /* 1800 MHz band */ CDMA_BAND_CLASS_9_900 = 9, /* 900 MHz band */ CDMA_BAND_CLASS_10_SECONDARY_800 = 10, /* Secondary 800 MHz band */ CDMA_BAND_CLASS_11_PAMR_400 = 11, /* 400 MHz European PAMR band */ CDMA_BAND_CLASS_12_PAMR_800 = 12, /* 800 MHz PAMR band */ CDMA_BAND_CLASS_13_IMT2000_2500 = 13, /* 2500 MHz IMT-2000 Extension Band */ CDMA_BAND_CLASS_14_US_PCS_1900 = 14, /* US PCS 1900 MHz Band */ CDMA_BAND_CLASS_15_AWS = 15, /* AWS 1700 MHz band */ CDMA_BAND_CLASS_16_US_2500 = 16, /* US 2500 MHz Band */ CDMA_BAND_CLASS_17_US_FLO_2500 = 17, /* US 2500 MHz Forward Link Only Band */ CDMA_BAND_CLASS_18_US_PS_700 = 18, /* 700 MHz Public Safety Band */ CDMA_BAND_CLASS_19_US_LOWER_700 = 19 /* Lower 700 MHz Band */ }; enum { CDMA_STATUS_SNAPSHOT_STATE_NO_SERVICE = 0x00, CDMA_STATUS_SNAPSHOT_STATE_INITIALIZATION = 0x01, CDMA_STATUS_SNAPSHOT_STATE_IDLE = 0x02, CDMA_STATUS_SNAPSHOT_STATE_VOICE_CHANNEL_INIT = 0x03, CDMA_STATUS_SNAPSHOT_STATE_WAITING_FOR_ORDER = 0x04, CDMA_STATUS_SNAPSHOT_STATE_WAITING_FOR_ANSWER = 0x05, CDMA_STATUS_SNAPSHOT_STATE_CONVERSATION = 0x06, CDMA_STATUS_SNAPSHOT_STATE_RELEASE = 0x07, CDMA_STATUS_SNAPSHOT_STATE_SYSTEM_ACCESS = 0x08, CDMA_STATUS_SNAPSHOT_STATE_OFFLINE_CDMA = 0x10, CDMA_STATUS_SNAPSHOT_STATE_OFFLINE_HDR = 0x11, CDMA_STATUS_SNAPSHOT_STATE_OFFLINE_ANALOG = 0x12, CDMA_STATUS_SNAPSHOT_STATE_RESET = 0x13, CDMA_STATUS_SNAPSHOT_STATE_POWER_DOWN = 0x14, CDMA_STATUS_SNAPSHOT_STATE_POWER_SAVE = 0x15, CDMA_STATUS_SNAPSHOT_STATE_POWER_UP = 0x16, CDMA_STATUS_SNAPSHOT_STATE_LOW_POWER_MODE = 0x17, CDMA_STATUS_SNAPSHOT_STATE_SEARCHER_DSMM = 0x18, /* Dedicated System Measurement Mode */ CDMA_STATUS_SNAPSHOT_STATE_HDR = 0x40, }; /* Generic DM command header */ struct DMCmdHeader { uint8_t code; } __attribute__ ((packed)); typedef struct DMCmdHeader DMCmdHeader; /* DIAG_CMD_SUBSYS */ struct DMCmdSubsysHeader { uint8_t code; uint8_t subsys_id; uint16_t subsys_cmd; } __attribute__ ((packed)); typedef struct DMCmdSubsysHeader DMCmdSubsysHeader; typedef enum { DM_CONTROL_MODE_OFFLINE = 1, DM_CONTROL_MODE_RESET = 2, } DMControlMode; /* DIAG_CMD_CONTROL */ struct DMCmdControl { uint8_t code; /* DMControlMode */ uint16_t mode; } __attribute__ ((packed)); typedef struct DMCmdControl DMCmdControl; /* DIAG_CMD_NV_READ / DIAG_CMD_NV_WRITE */ struct DMCmdNVReadWrite { uint8_t code; uint16_t nv_item; uint8_t data[128]; uint16_t status; } __attribute__ ((packed)); typedef struct DMCmdNVReadWrite DMCmdNVReadWrite; /* DIAG_CMD_VERSION_INFO */ struct DMCmdVersionInfoRsp { uint8_t code; char comp_date[11]; char comp_time[8]; char rel_date[11]; char rel_time[8]; char model[8]; uint8_t scm; uint8_t mob_cai_rev; uint8_t mob_model; uint16_t mob_firmware_rev; uint8_t slot_cycle_index; uint8_t msm_ver; uint8_t _unknown; } __attribute__ ((packed)); typedef struct DMCmdVersionInfoRsp DMCmdVersionInfoRsp; /* DIAG_CMD_ESN */ struct DMCmdEsnRsp { uint8_t code; uint8_t esn[4]; } __attribute__ ((packed)); typedef struct DMCmdEsnRsp DMCmdEsnRsp; /* DIAG_CMD_STATUS */ struct DMCmdStatusRsp { uint8_t code; uint8_t _unknown[3]; uint8_t esn[4]; uint16_t rf_mode; uint8_t min1_analog[4]; uint8_t min1_cdma[4]; uint8_t min2_analog[2]; uint8_t min2_cdma[2]; uint8_t _unknown1; uint16_t cdma_rx_state; uint8_t good_frames; uint16_t analog_corrected_frames; uint16_t analog_bad_frames; uint16_t analog_word_syncs; uint16_t entry_reason; uint16_t curr_chan; uint8_t cdma_code_chan; uint16_t pilot_base; uint16_t sid; uint16_t nid; uint16_t analog_locaid; uint16_t analog_rssi; uint8_t analog_power; } __attribute__ ((packed)); typedef struct DMCmdStatusRsp DMCmdStatusRsp; /* DIAG_CMD_SW_VERSION */ struct DMCmdSwVersionRsp { uint8_t code; char version[31]; char comp_date[11]; uint8_t _unknown1[2]; char comp_time[8]; uint8_t _unknown2[2]; } __attribute__ ((packed)); typedef struct DMCmdSwVersionRsp DMCmdSwVersionRsp; typedef enum { DM_OPER_MODE_POWER_OFF = 0, DM_OPER_MODE_FIELD_TEST_MODE = 1, DM_OPER_MODE_OFFLINE = 2, DM_OPER_MODE_OFFLINE_AMPS = 3, DM_OPER_MODE_OFFLINE_CDMA = 4, DM_OPER_MODE_ONLINE = 5, DM_OPER_MODE_LOW_POWER_MODE = 6, DM_OPER_MODE_RESETTING = 7, } DMOperMode; /* DIAG_CMD_STATUS_SNAPSHOT */ struct DMCmdStatusSnapshotRsp { uint8_t code; uint8_t esn[4]; uint8_t imsi_s1[4]; uint8_t imsi_s2[2]; uint8_t imsi_s[8]; uint8_t imsi_11_12; uint16_t mcc; uint8_t imsi_addr_num; uint16_t sid; uint16_t nid; uint8_t prev; uint8_t prev_in_use; uint8_t mob_prev; uint8_t band_class; uint16_t frequency; uint8_t oper_mode; uint8_t state; uint8_t sub_state; } __attribute__ ((packed)); typedef struct DMCmdStatusSnapshotRsp DMCmdStatusSnapshotRsp; /* DIAG_SUBSYS_CM_STATE_INFO subsys command */ struct DMCmdSubsysCMStateInfoRsp { DMCmdSubsysHeader header; uint32_t call_state; uint32_t oper_mode; uint32_t system_mode; uint32_t mode_pref; uint32_t band_pref; uint32_t roam_pref; uint32_t srv_domain_pref; uint32_t acq_order_pref; uint32_t hybrid_pref; uint32_t network_sel_mode_pref; } __attribute__ ((packed)); typedef struct DMCmdSubsysCMStateInfoRsp DMCmdSubsysCMStateInfoRsp; /* DIAG_SUBSYS_HDR_STATE_INFO subsys command */ struct DMCmdSubsysHDRStateInfoRsp { DMCmdSubsysHeader header; uint8_t at_state; uint8_t session_state; uint8_t almp_state; uint8_t init_state; uint8_t idle_state; uint8_t connected_state; uint8_t route_update_state; uint8_t overhead_msg_state; uint8_t hdr_hybrid_mode; } __attribute__ ((packed)); typedef struct DMCmdSubsysHDRStateInfoRsp DMCmdSubsysHDRStateInfoRsp; /* DIAG_SUBSYS_ZTE_STATUS subsys command */ struct DMCmdSubsysZteStatusRsp { DMCmdSubsysHeader header; uint8_t _unknown1[8]; uint8_t signal_ind; uint8_t _unknown2; } __attribute__ ((packed)); typedef struct DMCmdSubsysZteStatusRsp DMCmdSubsysZteStatusRsp; /* DIAG_CMD_PILOT_SETS command */ struct DMCmdPilotSetsSet { uint16_t pn_offset; uint16_t ecio; } __attribute__ ((packed)); typedef struct DMCmdPilotSetsSet DMCmdPilotSetsSet; struct DMCmdPilotSetsRsp { uint8_t code; uint16_t pilot_inc; uint8_t active_count; uint8_t candidate_count; uint8_t neighbor_count; DMCmdPilotSetsSet sets[52]; } __attribute__ ((packed)); typedef struct DMCmdPilotSetsRsp DMCmdPilotSetsRsp; struct DMCmdLog { uint8_t code; uint8_t more; uint16_t len; /* size of packet after this member */ uint16_t _unknown2; /* contains same value as len */ uint16_t log_code; uint64_t timestamp; uint8_t data[0]; } __attribute__ ((packed)); typedef struct DMCmdLog DMCmdLog; struct DMCmdExtLogMask { uint8_t code; /* Bit number of highest '1' in 'mask'; set to 0 to get current mask. */ uint16_t len; /* Bitfield of log messages to receive */ uint8_t mask[512]; } __attribute__ ((packed)); typedef struct DMCmdExtLogMask DMCmdExtLogMask; struct DMCmdEventReport { uint8_t code; uint8_t on; } __attribute__ ((packed)); typedef struct DMCmdEventReport DMCmdEventReport; struct DMCmdEventReportRsp { uint8_t code; uint16_t len; uint16_t event_id; uint8_t data[0]; } __attribute__ ((packed)); typedef struct DMCmdEventReportRsp DMCmdEventReportRsp; /* DIAG_SUBSYS_NOVATEL_* subsys command */ struct DMCmdSubsysNwSnapshotReq { DMCmdSubsysHeader hdr; uint8_t technology; /* DIAG_SUBSYS_NOVATEL_MODEM_SNAPSHOT_TECH_* */ uint32_t snapshot_mask; } __attribute__ ((packed)); typedef struct DMCmdSubsysNwSnapshotReq DMCmdSubsysNwSnapshotReq; /* DIAG_SUBSYS_NOVATEL_MODEM_SNAPSHOT response */ struct DMCmdSubsysNwSnapshotRsp { DMCmdSubsysHeader hdr; uint8_t response_code; uint32_t bitfield1; uint32_t bitfield2; uint8_t data[100]; /* DMCmdSubsysNwSnapshotCdma */ } __attribute__ ((packed)); typedef struct DMCmdSubsysNwSnapshotRsp DMCmdSubsysNwSnapshotRsp; struct DMCmdSubsysNwSnapshotCdma { uint32_t rssi; uint32_t battery_level; uint8_t call_info; uint8_t new_sms_ind; uint8_t missed_calls; uint32_t voicemail_ind; uint8_t pkt_call_ctrl_state; uint8_t mip_rrp_err_code; uint8_t cur_packet_zone_id; uint8_t prev; uint8_t band_class; uint8_t eri; uint8_t eri_alert_id; uint32_t cur_call_total_time; uint32_t cur_call_active_time; uint32_t cur_call_tx_ip_bytes; uint32_t cur_call_rx_ip_bytes; uint8_t connection_status; uint16_t dominant_pn; uint8_t wdisable_mask; uint8_t hdr_rev; } __attribute__ ((packed)); typedef struct DMCmdSubsysNwSnapshotCdma DMCmdSubsysNwSnapshotCdma; /* DIAG_SUBSYS_NOVATEL_MODEM_SNAPSHOT response */ struct DMCmdSubsysNwEriRsp { DMCmdSubsysHeader hdr; uint8_t status; uint16_t error; uint8_t roam; uint8_t eri_header[6]; uint8_t eri_call_prompt[38]; /* Roaming Indicator */ uint8_t indicator_id; uint8_t icon_id; uint8_t icon_mode; uint8_t call_prompt_id; /* Call Guard? */ uint8_t alert_id; /* Ringer? */ uint8_t encoding_type; uint8_t text_len; uint8_t text[32]; } __attribute__ ((packed)); typedef struct DMCmdSubsysNwEriRsp DMCmdSubsysNwEriRsp; enum { DIAG_CMD_LOG_CONFIG_OP_GET_RANGE = 0x01, DIAG_CMD_LOG_CONFIG_OP_SET_MASK = 0x03, DIAG_CMD_LOG_CONFIG_OP_GET_MASK = 0x04, }; struct DMCmdLogConfig { uint8_t code; uint8_t pad[3]; uint32_t op; uint32_t equipid; uint32_t num_items; uint8_t mask[0]; } __attribute__ ((packed)); typedef struct DMCmdLogConfig DMCmdLogConfig; struct DMCmdLogConfigRsp { uint8_t code; uint8_t pad[3]; uint32_t op; uint32_t result; /* 0 = success */ uint32_t equipid; union { uint32_t get_range_items[16]; struct { uint32_t num_items; uint8_t mask[0]; } get_set_items; } u; } __attribute__ ((packed)); typedef struct DMCmdLogConfigRsp DMCmdLogConfigRsp; /* DIAG_SUBSYS_WCDMA_CALL_START command */ struct DMCmdSubsysWcdmaCallStart { DMCmdSubsysHeader hdr; uint8_t number_len; uint8_t number_digits[32]; uint8_t amr_rate; /* default to 7 */ } __attribute__ ((packed)); typedef struct DMCmdSubsysWcdmaCallStart DMCmdSubsysWcdmaCallStart; /* DIAG_SUBSYS_WCDMA_STATE_INFO response */ struct DMCmdSubsysWcdmaStateInfoRsp { DMCmdSubsysHeader hdr; uint8_t imei_len; uint8_t imei[8]; uint8_t imsi_len; uint8_t imsi[8]; uint8_t l1_state; } __attribute__ ((packed)); typedef struct DMCmdSubsysWcdmaStateInfoRsp DMCmdSubsysWcdmaStateInfoRsp; /* DIAG_SUBSYS_GSM_STATE_INFO response */ struct DMCmdSubsysGsmStateInfoRsp { DMCmdSubsysHeader hdr; uint8_t imei_len; uint8_t imei[8]; uint8_t imsi_len; uint8_t imsi[8]; uint8_t lai[5]; uint16_t cellid; uint8_t cm_call_state; uint8_t cm_opmode; uint8_t cm_sysmode; } __attribute__ ((packed)); typedef struct DMCmdSubsysGsmStateInfoRsp DMCmdSubsysGsmStateInfoRsp; /* DIAG_CMD_SAMSUNG_IND response */ struct DMCmdSamsungIndRsp { DMCmdHeader hdr; uint8_t _unknown1; /* always zero */ uint8_t _unknown2; /* 0x0c */ uint8_t _unknown3[4]; /* always zero */ uint8_t _unknown4; /* 0x05 */ uint8_t _unknown5; /* always zero */ uint8_t _unknown6; /* 0x01 */ uint8_t _unknown7; /* always zero */ uint8_t signal; /* 0 - 5 */ } __attribute__ ((packed)); typedef struct DMCmdSamsungIndRsp DMCmdSamsungIndRsp; #endif /* LIBQCDM_DM_COMMANDS_H */ ModemManager-1.23.4-dev/libqcdm/src/errors.c000066400000000000000000000031421456466623000206070ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * Copyright (C) 2010 Red Hat, Inc. * * This program is free software: you can redistribute it and/or * modify it under the terms of version 2 of the GNU General Public * License as published by the Free Software Foundation * * 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, see . */ #include "errors.h" #include #include void _qcdm_log (const char *file, int line, const char *func, int domain, int level, const char *format, ...) { va_list args; char *message = NULL; int n; const char *prefix = "info"; qcdm_return_if_fail (format != NULL); qcdm_return_if_fail (format[0] != '\0'); /* level & domain ignored for now */ if (getenv ("QCDM_DEBUG") == NULL) return; va_start (args, format); n = vasprintf (&message, format, args); va_end (args); if (level & QCDM_LOGL_ERR) prefix = "err"; else if (level & QCDM_LOGL_WARN) prefix = "warn"; else if (level & QCDM_LOGL_DEBUG) prefix = "dbg"; if (n >= 0) { fprintf (stderr, "<%s> [%s:%d] %s(): %s\n", prefix, file, line, func, message); free (message); } } ModemManager-1.23.4-dev/libqcdm/src/errors.h000066400000000000000000000056601456466623000206230ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * Copyright (C) 2010 Red Hat, Inc. * * This program is free software: you can redistribute it and/or * modify it under the terms of version 2 of the GNU General Public * License as published by the Free Software Foundation * * 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, see . */ #ifndef LIBQCDM_ERRORS_H #define LIBQCDM_ERRORS_H #include #include #include #include #include enum { QCDM_LOGL_ERR = 0x00000001, QCDM_LOGL_WARN = 0x00000002, QCDM_LOGL_INFO = 0x00000004, QCDM_LOGL_DEBUG = 0x00000008 }; enum { QCDM_SUCCESS = 0, QCDM_ERROR_INVALID_ARGUMENTS = 1, QCDM_ERROR_SERIAL_CONFIG_FAILED = 2, QCDM_ERROR_VALUE_NOT_FOUND = 3, QCDM_ERROR_RESPONSE_UNEXPECTED = 4, QCDM_ERROR_RESPONSE_BAD_LENGTH = 5, QCDM_ERROR_RESPONSE_MALFORMED = 6, QCDM_ERROR_RESPONSE_BAD_COMMAND = 7, QCDM_ERROR_RESPONSE_BAD_PARAMETER = 8, QCDM_ERROR_RESPONSE_NOT_ACCEPTED = 9, QCDM_ERROR_RESPONSE_BAD_MODE = 10, QCDM_ERROR_NVCMD_FAILED = 11, QCDM_ERROR_SPC_LOCKED = 12, QCDM_ERROR_NV_ERROR_BUSY = 13, QCDM_ERROR_NV_ERROR_BAD_COMMAND = 14, QCDM_ERROR_NV_ERROR_MEMORY_FULL = 15, QCDM_ERROR_NV_ERROR_FAILED = 16, QCDM_ERROR_NV_ERROR_INACTIVE = 17, /* NV location is not active */ QCDM_ERROR_NV_ERROR_BAD_PARAMETER = 18, QCDM_ERROR_NV_ERROR_READ_ONLY = 19, /* NV location is read-only */ QCDM_ERROR_RESPONSE_FAILED = 20, /* command-specific failure */ }; #define qcdm_assert assert #define qcdm_assert_not_reached() assert(0) #define qcdm_return_if_fail(e) \ { \ if (!(e)) { \ qcdm_warn (0, "failed: " #e "\n"); \ return; \ } \ } #define qcdm_return_val_if_fail(e, v) \ { \ if (!(e)) { \ qcdm_warn (0, "failed: " #e "\n"); \ return v; \ } \ } #define qcdm_warn_if_fail(e) \ { \ if (!(e)) { \ qcdm_warn (0, "failed: " #e "\n"); \ } \ } void _qcdm_log (const char *file, int line, const char *func, int domain, int level, const char *format, ...) __attribute__((__format__ (__printf__, 6, 7))); #define qcdm_dbg(domain, ...) \ _qcdm_log (__FILE__, __LINE__, __func__, domain, QCDM_LOGL_DEBUG, ## __VA_ARGS__ ) #define qcdm_warn(domain, ...) \ _qcdm_log (__FILE__, __LINE__, __func__, domain, QCDM_LOGL_WARN, ## __VA_ARGS__ ) #define qcdm_err(domain, ...) \ _qcdm_log (__FILE__, __LINE__, __func__, domain, QCDM_LOGL_ERR, ## __VA_ARGS__ ) #endif /* LIBQCDM_ERRORS_H */ ModemManager-1.23.4-dev/libqcdm/src/log-items.h000066400000000000000000000177101456466623000212060ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * Copyright (C) 2011 Red Hat, Inc. * * This program is free software: you can redistribute it and/or * modify it under the terms of version 2 of the GNU General Public * License as published by the Free Software Foundation * * 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, see . */ #ifndef LIBQCDM_LOG_ITEMS_H #define LIBQCDM_LOG_ITEMS_H #include enum { /* CDMA and EVDO items */ DM_LOG_ITEM_CDMA_ACCESS_CHANNEL_MSG = 0x1004, DM_LOG_ITEM_CDMA_REV_CHANNEL_TRAFFIC_MSG = 0x1005, DM_LOG_ITEM_CDMA_SYNC_CHANNEL_MSG = 0x1006, DM_LOG_ITEM_CDMA_PAGING_CHANNEL_MSG = 0x1007, DM_LOG_ITEM_CDMA_FWD_CHANNEL_TRAFFIC_MSG = 0x1008, DM_LOG_ITEM_CDMA_FWD_LINK_VOCODER_PACKET = 0x1009, DM_LOG_ITEM_CDMA_REV_LINK_VOCODER_PACKET = 0x100A, DM_LOG_ITEM_CDMA_MARKOV_STATS = 0x100E, DM_LOG_ITEM_CDMA_REVERSE_POWER_CONTROL = 0x102C, DM_LOG_ITEM_CDMA_SERVICE_CONFIG = 0x102E, DM_LOG_ITEM_EVDO_HANDOFF_STATE = 0x105E, DM_LOG_ITEM_EVDO_ACTIVE_PILOT_SET = 0x105F, DM_LOG_ITEM_EVDO_REV_LINK_PACKET_SUMMARY = 0x1060, DM_LOG_ITEM_EVDO_REV_TRAFFIC_RATE_COUNT = 0x1062, DM_LOG_ITEM_EVDO_REV_POWER_CONTROL = 0x1063, DM_LOG_ITEM_EVDO_ARQ_EFFECTIVE_RECEIVE_RATE = 0x1066, DM_LOG_ITEM_EVDO_AIR_LINK_SUMMARY = 0x1068, DM_LOG_ITEM_EVDO_POWER = 0x1069, DM_LOG_ITEM_EVDO_FWD_LINK_PACKET_SNAPSHOT = 0x106A, DM_LOG_ITEM_EVDO_ACCESS_ATTEMPT = 0x106C, DM_LOG_ITEM_EVDO_REV_ACTIVITY_BITS_BUFFER = 0x106D, DM_LOG_ITEM_EVDO_PILOT_SETS = 0x107A, DM_LOG_ITEM_EVDO_STATE_INFO = 0x107E, DM_LOG_ITEM_EVDO_SECTOR_INFO = 0x1080, DM_LOG_ITEM_EVDO_PILOT_SETS_V2 = 0x108B, /* WCDMA items */ DM_LOG_ITEM_WCDMA_TA_FINGER_INFO = 0x4003, DM_LOG_ITEM_WCDMA_AGC_INFO = 0x4105, DM_LOG_ITEM_WCDMA_RRC_STATE = 0x4125, DM_LOG_ITEM_WCDMA_CELL_ID = 0x4127, /* GSM items */ DM_LOG_ITEM_GSM_BURST_METRICS = 0x506c, DM_LOG_ITEM_GSM_BCCH_MESSAGE = 0x5134, }; /* DM_LOG_ITEM_CDMA_PAGING_CHANNEL_MSG */ struct DMLogItemPagingChannelMsg { uint8_t msg_len; /* size of entire struct including this field */ uint8_t msg_type; /* MSG_TYPE as in 3GPP2 C.S0004 Table 3.1.2.3.1.1.2 */ uint8_t data[0]; /* Packed message as in 3GPP2 C.S0005 3.7.2.3.2.x */ } __attribute ((packed)); typedef struct DMLogItemPagingChannelMsg DMLogItemPagingChannelMsg; /* DM_LOG_ITEM_CDMA_REVERSE_POWER_CONTROL */ struct DMLogItemRPCItem { uint8_t channel_set_mask; uint16_t frame_count; uint8_t len_per_frame; uint16_t dec_history; uint8_t rx_agc_vals; uint8_t tx_power_vals; uint8_t tx_gain_adjust; } __attribute__ ((packed)); typedef struct DMLogItemRPCItem DMLogItemRPCItem; struct DMLogItemCdmaReversePowerControl { uint8_t frame_offset; uint8_t band_class; uint16_t rev_chan_rc; uint8_t pilot_gating_rate; uint8_t step_size; uint8_t num_records; DMLogItemRPCItem records[]; } __attribute__ ((packed)); typedef struct DMLogItemCdmaReversePowerControl DMLogItemCdmaReversePowerControl; /* DM_LOG_ITEM_EVDO_PILOT_SETS_V2 */ struct DMLogItemEvdoPilotSetsV2Pilot { uint16_t pilot_pn; /* HDR pilot energy doesn't appear to be in the same units as 1x pilot * energy (eg, -0.5 dBm increments). Instead it appears roughly correlated * to RSSI dBm by using this formula empirically derived from simultaneous * AT!RSSI and HDR Pilot Sets V2 results from a Sierra modem: * * RSSI dBm = -110 + (MAX(pilot_energy - 50, 0) / 14) */ uint16_t pilot_energy; union { struct { uint16_t mac_index; uint8_t unknown1; uint8_t unknown2; uint16_t window_center; } Active; struct { uint16_t channel_number; uint8_t unknown1; uint8_t unknown2; uint16_t window_center; } Candidate; struct { uint16_t channel_number; uint16_t window_center; uint8_t unknown1; // Offset? uint8_t unknown2; // Age? } Remaining; }; } __attribute__ ((packed)); typedef struct DMLogItemEvdoPilotSetsV2Pilot DMLogItemEvdoPilotSetsV2Pilot; /* DM_LOG_ITEM_EVDO_PILOT_SETS_V2 */ struct DMLogItemEvdoPilotSetsV2 { uint8_t pn_offset; uint8_t active_count; uint8_t active_window; uint16_t active_channel; uint8_t unknown1; uint8_t candidate_count; uint8_t candidate_window; uint8_t remaining_count; uint8_t remaining_window; uint8_t unknown2; DMLogItemEvdoPilotSetsV2Pilot sets[]; } __attribute__ ((packed)); typedef struct DMLogItemEvdoPilotSetsV2 DMLogItemEvdoPilotSetsV2; /* DM_LOG_ITEM_WCDMA_TA_FINGER_INFO */ struct DMLogItemWcdmaTaFingerInfo { int32_t tx_pos; int16_t coherent_interval_len; uint8_t non_coherent_interval_len; uint8_t num_paths; uint32_t path_enr; int32_t pn_pos_path; int16_t pri_cpich_psc; uint8_t unknown1; uint8_t sec_cpich_ssc; uint8_t finger_channel_code_index; uint8_t finger_index; } __attribute__ ((packed)); typedef struct DMLogItemWcdmaTaFingerInfo DMLogItemWcdmaTaFingerInfo; /* DM_LOG_ITEM_WCDMA_AGC_INFO */ struct DMLogItemWcdmaAgcInfo { uint8_t num_samples; uint16_t rx_agc; uint16_t tx_agc; uint16_t rx_agc_adj_pdm; uint16_t tx_agc_adj_pdm; uint16_t max_tx; /* Bit 4 means tx_agc is valid */ uint8_t agc_info; } __attribute__ ((packed)); typedef struct DMLogItemWcdmaAgcInfo DMLogItemWcdmaAgcInfo; /* DM_LOG_ITEM_WCDMA_RRC_STATE */ enum { DM_LOG_ITEM_WCDMA_RRC_STATE_DISCONNECTED = 0, DM_LOG_ITEM_WCDMA_RRC_STATE_CONNECTING = 1, DM_LOG_ITEM_WCDMA_RRC_STATE_CELL_FACH = 2, DM_LOG_ITEM_WCDMA_RRC_STATE_CELL_DCH = 3, DM_LOG_ITEM_WCDMA_RRC_STATE_CELL_PCH = 4, DM_LOG_ITEM_WCDMA_RRC_STATE_URA_PCH = 5, }; struct DMLogItemWcdmaRrcState { uint8_t rrc_state; } __attribute__ ((packed)); typedef struct DMLogItemWcdmaRrcState DMLogItemWcdmaRrcState; /* DM_LOG_ITEM_WCDMA_CELL_ID */ struct DMLogItemWcdmaCellId { uint8_t unknown1[8]; uint32_t cellid; uint8_t unknown2[4]; } __attribute__ ((packed)); typedef struct DMLogItemWcdmaCellId DMLogItemWcdmaCellId; /* DM_LOG_ITEM_GSM_BURST_METRICS */ struct DMLogItemGsmBurstMetric { uint32_t fn; uint16_t arfcn; uint32_t rssi; uint16_t power; uint16_t dc_offset_i; uint16_t dc_offset_q; uint16_t freq_offset; uint16_t timing_offset; uint16_t snr; uint8_t gain_state; } __attribute__ ((packed)); typedef struct DMLogItemGsmBurstMetric DMLogItemGsmBurstMetric; struct DMLogItemGsmBurstMetrics { uint8_t channel; DMLogItemGsmBurstMetric metrics[4]; } __attribute__ ((packed)); typedef struct DMLogItemGsmBurstMetrics DMLogItemGsmBurstMetrics; /* DM_LOG_ITEM_GSM_BCCH_MESSAGE */ enum { DM_LOG_ITEM_GSM_BCCH_BAND_UNKNOWN = 0, DM_LOG_ITEM_GSM_BCCH_BAND_GSM_900 = 8, DM_LOG_ITEM_GSM_BCCH_BAND_DCS_1800 = 9, DM_LOG_ITEM_GSM_BCCH_BAND_PCS_1900 = 10, DM_LOG_ITEM_GSM_BCCH_BAND_GSM_850 = 11, DM_LOG_ITEM_GSM_BCCH_BAND_GSM_450 = 12, }; struct DMLogItemGsmBcchMessage { /* Band is top 4 bits; lower 12 is ARFCN */ uint16_t bcch_arfcn; uint16_t bsic; uint16_t cell_id; uint8_t lai[5]; uint8_t cell_selection_prio; uint8_t ncc_permitted; } __attribute__ ((packed)); typedef struct DMLogItemGsmBcchMessage DMLogItemGsmBcchMessage; #endif /* LIBQCDM_LOG_ITEMS_H */ ModemManager-1.23.4-dev/libqcdm/src/logs.c000066400000000000000000000140701456466623000202410ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * Copyright (C) 2010 Red Hat, Inc. * * This program is free software: you can redistribute it and/or * modify it under the terms of version 2 of the GNU General Public * License as published by the Free Software Foundation * * 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, see . */ #include #include #include #include "log-items.h" #include "logs.h" #include "errors.h" #include "dm-commands.h" #include "result-private.h" #include "utils.h" /**********************************************************************/ static qcdmbool check_log_item (const char *buf, size_t len, uint16_t log_code, size_t min_len, int *out_error) { DMCmdLog *log_cmd = (DMCmdLog *) buf; if (len < sizeof (DMCmdLog)) { qcdm_err (0, "DM log item malformed (must be at least %zu bytes in length)", sizeof (DMCmdLog)); if (out_error) *out_error = -QCDM_ERROR_RESPONSE_MALFORMED; return FALSE; } if (buf[0] != DIAG_CMD_LOG) { if (out_error) *out_error = -QCDM_ERROR_RESPONSE_UNEXPECTED; return FALSE; } if (le16toh (log_cmd->log_code) != log_code) { if (out_error) *out_error = -QCDM_ERROR_RESPONSE_UNEXPECTED; return FALSE; } if (len < sizeof (DMCmdLog) + min_len) { qcdm_err (0, "DM log item response not long enough (got %zu, expected " "at least %zu).", len, sizeof (DMCmdLog) + min_len); if (out_error) *out_error = -QCDM_ERROR_RESPONSE_BAD_LENGTH; return FALSE; } return TRUE; } /**********************************************************************/ #define PILOT_SETS_LOG_ACTIVE_SET "active-set" #define PILOT_SETS_LOG_CANDIDATE_SET "candidate-set" #define PILOT_SETS_LOG_REMAINING_SET "remaining-set" static const char * set_num_to_str (uint32_t num) { if (num == QCDM_LOG_ITEM_EVDO_PILOT_SETS_V2_TYPE_ACTIVE) return PILOT_SETS_LOG_ACTIVE_SET; if (num == QCDM_LOG_ITEM_EVDO_PILOT_SETS_V2_TYPE_CANDIDATE) return PILOT_SETS_LOG_CANDIDATE_SET; if (num == QCDM_LOG_ITEM_EVDO_PILOT_SETS_V2_TYPE_REMAINING) return PILOT_SETS_LOG_REMAINING_SET; return NULL; } QcdmResult * qcdm_log_item_evdo_pilot_sets_v2_new (const char *buf, size_t len, int *out_error) { QcdmResult *result = NULL; DMLogItemEvdoPilotSetsV2 *pilot_sets; DMCmdLog *log_cmd = (DMCmdLog *) buf; size_t sets_len; qcdm_return_val_if_fail (buf != NULL, NULL); if (!check_log_item (buf, len, DM_LOG_ITEM_EVDO_PILOT_SETS_V2, sizeof (DMLogItemEvdoPilotSetsV2), out_error)) return NULL; pilot_sets = (DMLogItemEvdoPilotSetsV2 *) log_cmd->data; result = qcdm_result_new (); sets_len = pilot_sets->active_count * sizeof (DMLogItemEvdoPilotSetsV2Pilot); if (sets_len > 0) { qcdm_result_add_u8_array (result, PILOT_SETS_LOG_ACTIVE_SET, (const uint8_t *) &pilot_sets->sets[0], sets_len); } sets_len = pilot_sets->candidate_count * sizeof (DMLogItemEvdoPilotSetsV2Pilot); if (sets_len > 0) { qcdm_result_add_u8_array (result, PILOT_SETS_LOG_CANDIDATE_SET, (const uint8_t *) &pilot_sets->sets[pilot_sets->active_count], sets_len); } sets_len = pilot_sets->remaining_count * sizeof (DMLogItemEvdoPilotSetsV2Pilot); if (sets_len > 0) { qcdm_result_add_u8_array (result, PILOT_SETS_LOG_REMAINING_SET, (const uint8_t *) &pilot_sets->sets[pilot_sets->active_count + pilot_sets->candidate_count], sets_len); } return result; } qcdmbool qcdm_log_item_evdo_pilot_sets_v2_get_num (QcdmResult *result, uint32_t set_type, uint32_t *out_num) { const char *set_name; const uint8_t *array = NULL; size_t array_len = 0; qcdm_return_val_if_fail (result != NULL, FALSE); set_name = set_num_to_str (set_type); qcdm_return_val_if_fail (set_name != NULL, FALSE); if (qcdm_result_get_u8_array (result, set_name, &array, &array_len)) return FALSE; *out_num = array_len / sizeof (DMLogItemEvdoPilotSetsV2Pilot); return TRUE; } #define MAX(a, b) (((a) > (b)) ? (a) : (b)) qcdmbool qcdm_log_item_evdo_pilot_sets_v2_get_pilot (QcdmResult *result, uint32_t set_type, uint32_t num, uint32_t *out_pilot_pn, uint32_t *out_pilot_energy, int32_t *out_rssi_dbm) { const char *set_name; DMLogItemEvdoPilotSetsV2Pilot *pilot; const uint8_t *array = NULL; size_t array_len = 0; qcdm_return_val_if_fail (result != NULL, FALSE); set_name = set_num_to_str (set_type); qcdm_return_val_if_fail (set_name != NULL, FALSE); if (qcdm_result_get_u8_array (result, set_name, &array, &array_len)) return FALSE; qcdm_return_val_if_fail (num < array_len / sizeof (DMLogItemEvdoPilotSetsV2Pilot), FALSE); pilot = (DMLogItemEvdoPilotSetsV2Pilot *) &array[num * sizeof (DMLogItemEvdoPilotSetsV2Pilot)]; *out_pilot_pn = le16toh (pilot->pilot_pn); *out_pilot_energy = le16toh (pilot->pilot_energy); *out_rssi_dbm = (int32_t) (-110.0 + (MAX (le16toh (pilot->pilot_energy) - 50, 0) / 14.0)); return TRUE; } /**********************************************************************/ ModemManager-1.23.4-dev/libqcdm/src/logs.h000066400000000000000000000041031456466623000202420ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * Copyright (C) 2010 Red Hat, Inc. * * This program is free software: you can redistribute it and/or * modify it under the terms of version 2 of the GNU General Public * License as published by the Free Software Foundation * * 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, see . */ #ifndef LIBQCDM_LOGS_H #define LIBQCDM_LOGS_H #include "utils.h" #include "result.h" /**********************************************************************/ enum { QCDM_LOG_ITEM_EVDO_PILOT_SETS_V2_TYPE_UNKNOWN = 0, QCDM_LOG_ITEM_EVDO_PILOT_SETS_V2_TYPE_ACTIVE = 1, QCDM_LOG_ITEM_EVDO_PILOT_SETS_V2_TYPE_CANDIDATE = 2, QCDM_LOG_ITEM_EVDO_PILOT_SETS_V2_TYPE_REMAINING = 3, }; QcdmResult *qcdm_log_item_evdo_pilot_sets_v2_new (const char *buf, size_t len, int *out_error); qcdmbool qcdm_log_item_evdo_pilot_sets_v2_get_num (QcdmResult *result, uint32_t set_type, uint32_t *out_num); qcdmbool qcdm_log_item_evdo_pilot_sets_v2_get_pilot (QcdmResult *result, uint32_t set_type, uint32_t num, uint32_t *out_pilot_pn, uint32_t *out_pilot_energy, int32_t *out_rssi_dbm); /**********************************************************************/ #endif /* LIBQCDM_LOGS_H */ ModemManager-1.23.4-dev/libqcdm/src/meson.build000066400000000000000000000010411456466623000212650ustar00rootroot00000000000000# SPDX-License-Identifier: GPL-2.0-or-later # Copyright (C) 2021 Iñigo Martinez sources = files( 'com.c', 'commands.c', 'errors.c', 'logs.c', 'result.c', 'utils.c', ) libqcdm = static_library( 'qcdm', sources: sources, include_directories: top_inc, ) libqcdm_dep = declare_dependency( include_directories: '.', link_with: libqcdm, ) # FIXME: Created following autotools but actually unused libqcdm_test = static_library( 'qcdm-test', sources: 'utils.c', include_directories: top_inc, ) ModemManager-1.23.4-dev/libqcdm/src/nv-items.h000066400000000000000000000075351456466623000210540ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * Copyright (C) 2010 Red Hat, Inc. * * This program is free software: you can redistribute it and/or * modify it under the terms of version 2 of the GNU General Public * License as published by the Free Software Foundation * * 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, see . */ #ifndef LIBQCDM_NV_ITEMS_H #define LIBQCDM_NV_ITEMS_H #include /* NV read/write status codes */ typedef enum { DIAG_NV_STATUS_OK = 0, DIAG_NV_STATUS_BUSY = 1, DIAG_NV_STATUS_BAD_COMMAND = 2, DIAG_NV_STATUS_MEMORY_FULL = 3, DIAG_NV_STATUS_FAILED = 4, DIAG_NV_STATUS_INACTIVE = 5, /* NV location not active */ DIAG_NV_STATUS_BAD_PARAMETER = 6, DIAG_NV_STATUS_READ_ONLY = 7, /* NV location is read-only */ } DMNVStatus; enum { DIAG_NV_MODE_PREF = 10, /* Mode preference: 1x, HDR, auto */ DIAG_NV_DIR_NUMBER = 178, /* Mobile Directory Number (MDN) */ DIAG_NV_ROAM_PREF = 442, /* Roaming preference */ DIAG_NV_HYBRID_PREF = 562, /* Hybrid 1x + HDR preference */ DIAG_NV_IPV6_ENABLED = 1896, /* Enable IPv6 */ DIAG_NV_HDR_REV_PREF = 4964, /* HDR mode preference(?): rev0, revA, eHRPD */ }; /* Mode preference values */ enum { DIAG_NV_MODE_PREF_DIGITAL = 0x00, DIAG_NV_MODE_PREF_DIGITAL_ONLY = 0x01, DIAG_NV_MODE_PREF_ANALOG = 0x02, DIAG_NV_MODE_PREF_ANALOG_ONLY = 0x03, DIAG_NV_MODE_PREF_AUTO = 0x04, DIAG_NV_MODE_PREF_E911 = 0x05, DIAG_NV_MODE_PREF_HOME_ONLY = 0x06, DIAG_NV_MODE_PREF_1X_ONLY = 0x09, DIAG_NV_MODE_PREF_HDR_ONLY = 0x0A, DIAG_NV_MODE_PREF_GPRS_ONLY = 0x0D, DIAG_NV_MODE_PREF_UMTS_ONLY = 0x0E, DIAG_NV_MODE_PREF_GSM_UMTS_ONLY = 0x11, DIAG_NV_MODE_PREF_1X_HDR_ONLY = 0x13, DIAG_NV_MODE_PREF_LTE_ONLY = 0x1E, DIAG_NV_MODE_PREF_GSM_UMTS_LTE_ONLY = 0x1F, DIAG_NV_MODE_PREF_1X_HDR_LTE_ONLY = 0x24, }; /* DIAG_NV_MODE_PREF */ struct DMNVItemModePref { uint8_t profile; uint8_t mode_pref; } __attribute__ ((packed)); typedef struct DMNVItemModePref DMNVItemModePref; /* DIAG_NV_DIR_NUMBER */ struct DMNVItemMdn { uint8_t profile; uint8_t mdn[10]; } __attribute__ ((packed)); typedef struct DMNVItemMdn DMNVItemMdn; /* Roam preference values */ enum { DIAG_NV_ROAM_PREF_HOME_ONLY = 0x01, DIAG_NV_ROAM_PREF_ROAM_ONLY = 0x06, DIAG_NV_ROAM_PREF_AUTO = 0xFF, }; /* DIAG_NV_ROAM_PREF */ struct DMNVItemRoamPref { uint8_t profile; uint8_t roam_pref; } __attribute__ ((packed)); typedef struct DMNVItemRoamPref DMNVItemRoamPref; /* HDR Revision preference values (?) */ enum { DIAG_NV_HDR_REV_PREF_0 = 0x00, DIAG_NV_HDR_REV_PREF_A = 0x01, DIAG_NV_HDR_REV_PREF_EHRPD = 0x04, }; /* DIAG_NV_HDR_REV_PREF */ struct DMNVItemHdrRevPref { uint8_t rev_pref; } __attribute__ ((packed)); typedef struct DMNVItemHdrRevPref DMNVItemHdrRevPref; /* Hybrid pref */ enum { DIAG_NV_HYBRID_PREF_OFF = 0x00, DIAG_NV_HYBRID_PREF_ON = 0x01, }; /* DIAG_NV_HYBRID_PREF */ struct DMNVItemHybridPref { uint8_t hybrid_pref; } __attribute__ ((packed)); typedef struct DMNVItemHybridPref DMNVItemHybridPref; /* IPv6 enable */ enum { DIAG_NV_IPV6_ENABLED_OFF = 0x00, DIAG_NV_IPV6_ENABLED_ON = 0x01, }; /* DIAG_NV_IPV6_ENABLED */ struct DMNVItemIPv6Enabled { uint8_t enabled; } __attribute__ ((packed)); typedef struct DMNVItemIPv6Enabled DMNVItemIPv6Enabled; #endif /* LIBQCDM_NV_ITEMS_H */ ModemManager-1.23.4-dev/libqcdm/src/result-private.h000066400000000000000000000035661456466623000223000ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * Copyright (C) 2011 Red Hat, Inc. * * This program is free software: you can redistribute it and/or * modify it under the terms of version 2 of the GNU General Public * License as published by the Free Software Foundation * * 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, see . */ #ifndef LIBQCDM_RESULT_PRIVATE_H #define LIBQCDM_RESULT_PRIVATE_H #include "result.h" QcdmResult *qcdm_result_new (void); void qcdm_result_add_string (QcdmResult *result, const char *key, const char *str); void qcdm_result_add_u8 (QcdmResult *result, const char *key, uint8_t num); void qcdm_result_add_u8_array (QcdmResult *result, const char *key, const uint8_t *array, size_t array_len); int qcdm_result_get_u8_array (QcdmResult *result, const char *key, const uint8_t **out_val, size_t *out_len); void qcdm_result_add_u16_array (QcdmResult *result, const char *key, const uint16_t *array, size_t array_len); void qcdm_result_add_u32 (QcdmResult *result, const char *key, uint32_t num); #endif /* LIBQCDM_RESULT_PRIVATE_H */ ModemManager-1.23.4-dev/libqcdm/src/result.c000066400000000000000000000250441456466623000206160ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * Copyright (C) 2010 Red Hat, Inc. * * This program is free software: you can redistribute it and/or * modify it under the terms of version 2 of the GNU General Public * License as published by the Free Software Foundation * * 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, see . */ #include #include #include "result.h" #include "result-private.h" #include "errors.h" /*********************************************************/ typedef struct Val Val; typedef enum { VAL_TYPE_NONE = 0, VAL_TYPE_STRING = 1, VAL_TYPE_U8 = 2, VAL_TYPE_U32 = 3, VAL_TYPE_U8_ARRAY = 4, VAL_TYPE_U16_ARRAY = 5, } ValType; struct Val { char *key; uint8_t type; union { char *s; uint8_t u8; uint32_t u32; uint8_t *u8_array; uint16_t *u16_array; } u; uint32_t array_len; Val *next; }; static void val_free (Val *v) { if (v->type == VAL_TYPE_STRING) { free (v->u.s); } else if (v->type == VAL_TYPE_U8_ARRAY) { free (v->u.u8_array); } else if (v->type == VAL_TYPE_U16_ARRAY) { free (v->u.u16_array); } free (v->key); memset (v, 0, sizeof (*v)); free (v); } static Val * val_new_string (const char *key, const char *value) { Val *v; qcdm_return_val_if_fail (key != NULL, NULL); qcdm_return_val_if_fail (key[0] != '\0', NULL); qcdm_return_val_if_fail (value != NULL, NULL); v = calloc (sizeof (Val), 1); if (v == NULL) return NULL; v->key = strdup (key); v->type = VAL_TYPE_STRING; v->u.s = strdup (value); return v; } static Val * val_new_u8 (const char *key, uint8_t u) { Val *v; qcdm_return_val_if_fail (key != NULL, NULL); qcdm_return_val_if_fail (key[0] != '\0', NULL); v = calloc (sizeof (Val), 1); if (v == NULL) return NULL; v->key = strdup (key); v->type = VAL_TYPE_U8; v->u.u8 = u; return v; } static Val * val_new_u8_array (const char *key, const uint8_t *array, size_t array_len) { Val *v; qcdm_return_val_if_fail (key != NULL, NULL); qcdm_return_val_if_fail (key[0] != '\0', NULL); qcdm_return_val_if_fail (array != NULL, NULL); qcdm_return_val_if_fail (array_len > 0, NULL); v = calloc (sizeof (Val), 1); if (v == NULL) return NULL; v->key = strdup (key); v->type = VAL_TYPE_U8_ARRAY; v->u.u8_array = malloc (array_len); if (v->u.u8_array == NULL) { val_free (v); return NULL; } memcpy (v->u.u8_array, array, array_len); v->array_len = array_len; return v; } static Val * val_new_u32 (const char *key, uint32_t u) { Val *v; qcdm_return_val_if_fail (key != NULL, NULL); qcdm_return_val_if_fail (key[0] != '\0', NULL); v = calloc (sizeof (Val), 1); if (v == NULL) return NULL; v->key = strdup (key); v->type = VAL_TYPE_U32; v->u.u32 = u; return v; } static Val * val_new_u16_array (const char *key, const uint16_t *array, size_t array_len) { Val *v; size_t sz; qcdm_return_val_if_fail (key != NULL, NULL); qcdm_return_val_if_fail (key[0] != '\0', NULL); qcdm_return_val_if_fail (array != NULL, NULL); qcdm_return_val_if_fail (array_len > 0, NULL); v = calloc (sizeof (Val), 1); if (v == NULL) return NULL; v->key = strdup (key); v->type = VAL_TYPE_U16_ARRAY; sz = sizeof (uint16_t) * array_len; v->u.u16_array = malloc (sz); if (v->u.u16_array == NULL) { val_free (v); return NULL; } memcpy (v->u.u16_array, array, sz); v->array_len = array_len; return v; } /*********************************************************/ struct QcdmResult { uint32_t refcount; Val *first; }; QcdmResult * qcdm_result_new (void) { QcdmResult *r; r = calloc (sizeof (QcdmResult), 1); if (r) r->refcount = 1; return r; } QcdmResult * qcdm_result_ref (QcdmResult *r) { qcdm_return_val_if_fail (r != NULL, NULL); qcdm_return_val_if_fail (r->refcount > 0, NULL); r->refcount++; return r; } static void qcdm_result_free (QcdmResult *r) { Val *v, *n; v = r->first; while (v) { n = v->next; val_free (v); v = n; } memset (r, 0, sizeof (*r)); free (r); } void qcdm_result_unref (QcdmResult *r) { qcdm_return_if_fail (r != NULL); qcdm_return_if_fail (r->refcount > 0); r->refcount--; if (r->refcount == 0) qcdm_result_free (r); } static Val * find_val (QcdmResult *r, const char *key, ValType expected_type) { Val *v, *n; v = r->first; while (v) { n = v->next; if (strcmp (v->key, key) == 0) { /* Check type */ qcdm_return_val_if_fail (v->type == expected_type, NULL); return v; } v = n; } return NULL; } void qcdm_result_add_string (QcdmResult *r, const char *key, const char *str) { Val *v; qcdm_return_if_fail (r != NULL); qcdm_return_if_fail (r->refcount > 0); qcdm_return_if_fail (key != NULL); qcdm_return_if_fail (str != NULL); v = val_new_string (key, str); qcdm_return_if_fail (v != NULL); v->next = r->first; r->first = v; } int qcdm_result_get_string (QcdmResult *r, const char *key, const char **out_val) { Val *v; qcdm_return_val_if_fail (r != NULL, -QCDM_ERROR_INVALID_ARGUMENTS); qcdm_return_val_if_fail (r->refcount > 0, -QCDM_ERROR_INVALID_ARGUMENTS); qcdm_return_val_if_fail (key != NULL, -QCDM_ERROR_INVALID_ARGUMENTS); qcdm_return_val_if_fail (out_val != NULL, -QCDM_ERROR_INVALID_ARGUMENTS); qcdm_return_val_if_fail (*out_val == NULL, -QCDM_ERROR_INVALID_ARGUMENTS); v = find_val (r, key, VAL_TYPE_STRING); if (v == NULL) return -QCDM_ERROR_VALUE_NOT_FOUND; *out_val = v->u.s; return 0; } void qcdm_result_add_u8 (QcdmResult *r, const char *key, uint8_t num) { Val *v; qcdm_return_if_fail (r != NULL); qcdm_return_if_fail (r->refcount > 0); qcdm_return_if_fail (key != NULL); v = val_new_u8 (key, num); qcdm_return_if_fail (v != NULL); v->next = r->first; r->first = v; } int qcdm_result_get_u8 (QcdmResult *r, const char *key, uint8_t *out_val) { Val *v; qcdm_return_val_if_fail (r != NULL, -QCDM_ERROR_INVALID_ARGUMENTS); qcdm_return_val_if_fail (r->refcount > 0, -QCDM_ERROR_INVALID_ARGUMENTS); qcdm_return_val_if_fail (key != NULL, -QCDM_ERROR_INVALID_ARGUMENTS); qcdm_return_val_if_fail (out_val != NULL, -QCDM_ERROR_INVALID_ARGUMENTS); v = find_val (r, key, VAL_TYPE_U8); if (v == NULL) return -QCDM_ERROR_VALUE_NOT_FOUND; *out_val = v->u.u8; return 0; } void qcdm_result_add_u8_array (QcdmResult *r, const char *key, const uint8_t *array, size_t array_len) { Val *v; qcdm_return_if_fail (r != NULL); qcdm_return_if_fail (r->refcount > 0); qcdm_return_if_fail (key != NULL); qcdm_return_if_fail (array != NULL); v = val_new_u8_array (key, array, array_len); qcdm_return_if_fail (v != NULL); v->next = r->first; r->first = v; } int qcdm_result_get_u8_array (QcdmResult *r, const char *key, const uint8_t **out_val, size_t *out_len) { Val *v; qcdm_return_val_if_fail (r != NULL, -QCDM_ERROR_INVALID_ARGUMENTS); qcdm_return_val_if_fail (r->refcount > 0, -QCDM_ERROR_INVALID_ARGUMENTS); qcdm_return_val_if_fail (key != NULL, -QCDM_ERROR_INVALID_ARGUMENTS); qcdm_return_val_if_fail (out_val != NULL, -QCDM_ERROR_INVALID_ARGUMENTS); qcdm_return_val_if_fail (out_len != NULL, -QCDM_ERROR_INVALID_ARGUMENTS); v = find_val (r, key, VAL_TYPE_U8_ARRAY); if (v == NULL) return -QCDM_ERROR_VALUE_NOT_FOUND; *out_val = v->u.u8_array; *out_len = v->array_len; return 0; } void qcdm_result_add_u32 (QcdmResult *r, const char *key, uint32_t num) { Val *v; qcdm_return_if_fail (r != NULL); qcdm_return_if_fail (r->refcount > 0); qcdm_return_if_fail (key != NULL); v = val_new_u32 (key, num); qcdm_return_if_fail (v != NULL); v->next = r->first; r->first = v; } int qcdm_result_get_u32 (QcdmResult *r, const char *key, uint32_t *out_val) { Val *v; qcdm_return_val_if_fail (r != NULL, -QCDM_ERROR_INVALID_ARGUMENTS); qcdm_return_val_if_fail (r->refcount > 0, -QCDM_ERROR_INVALID_ARGUMENTS); qcdm_return_val_if_fail (key != NULL, -QCDM_ERROR_INVALID_ARGUMENTS); qcdm_return_val_if_fail (out_val != NULL, -QCDM_ERROR_INVALID_ARGUMENTS); v = find_val (r, key, VAL_TYPE_U32); if (v == NULL) return -QCDM_ERROR_VALUE_NOT_FOUND; *out_val = v->u.u32; return 0; } void qcdm_result_add_u16_array (QcdmResult *r, const char *key, const uint16_t *array, size_t array_len) { Val *v; qcdm_return_if_fail (r != NULL); qcdm_return_if_fail (r->refcount > 0); qcdm_return_if_fail (key != NULL); qcdm_return_if_fail (array != NULL); v = val_new_u16_array (key, array, array_len); qcdm_return_if_fail (v != NULL); v->next = r->first; r->first = v; } int qcdm_result_get_u16_array (QcdmResult *r, const char *key, const uint16_t **out_val, size_t *out_len) { Val *v; qcdm_return_val_if_fail (r != NULL, -QCDM_ERROR_INVALID_ARGUMENTS); qcdm_return_val_if_fail (r->refcount > 0, -QCDM_ERROR_INVALID_ARGUMENTS); qcdm_return_val_if_fail (key != NULL, -QCDM_ERROR_INVALID_ARGUMENTS); qcdm_return_val_if_fail (out_val != NULL, -QCDM_ERROR_INVALID_ARGUMENTS); qcdm_return_val_if_fail (out_len != NULL, -QCDM_ERROR_INVALID_ARGUMENTS); v = find_val (r, key, VAL_TYPE_U16_ARRAY); if (v == NULL) return -QCDM_ERROR_VALUE_NOT_FOUND; *out_val = v->u.u16_array; *out_len = v->array_len; return 0; } ModemManager-1.23.4-dev/libqcdm/src/result.h000066400000000000000000000030431456466623000206160ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * Copyright (C) 2010 Red Hat, Inc. * * This program is free software: you can redistribute it and/or * modify it under the terms of version 2 of the GNU General Public * License as published by the Free Software Foundation * * 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, see . */ #ifndef LIBQCDM_RESULT_H #define LIBQCDM_RESULT_H #include typedef struct QcdmResult QcdmResult; int qcdm_result_get_string (QcdmResult *r, const char *key, const char **out_val); int qcdm_result_get_u8 (QcdmResult *r, const char *key, uint8_t *out_val); int qcdm_result_get_u32 (QcdmResult *r, const char *key, uint32_t *out_val); int qcdm_result_get_u16_array (QcdmResult *result, const char *key, const uint16_t **out_val, size_t *out_len); QcdmResult *qcdm_result_ref (QcdmResult *r); void qcdm_result_unref (QcdmResult *r); #endif /* LIBQCDM_RESULT_H */ ModemManager-1.23.4-dev/libqcdm/src/utils.c000066400000000000000000000261171456466623000204420ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * Copyright (C) 2010 Red Hat, Inc. * * This program is free software: you can redistribute it and/or * modify it under the terms of version 2 of the GNU General Public * License as published by the Free Software Foundation * * 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, see . */ #include #include #include #include #include #include #include "utils.h" #include "errors.h" /* QCDM protocol frames are pseudo Async HDLC frames which end with a 3-byte * trailer. This trailer consists of the 16-bit CRC of the frame plus an ending * "async control character" whose value is 0x7E. The frame *and* the CRC are * escaped before adding the trailing control character so that the control * character (0x7E) and the escape marker (0x7D) are never seen in the frame. */ /* Table of CRCs for each possible byte, with a generator polynomial of 0x8408 */ static const uint16_t crc_table[256] = { 0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf, 0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7, 0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e, 0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876, 0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd, 0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5, 0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c, 0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974, 0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb, 0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3, 0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a, 0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72, 0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9, 0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1, 0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738, 0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70, 0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7, 0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff, 0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036, 0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e, 0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5, 0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd, 0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134, 0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c, 0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3, 0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb, 0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232, 0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a, 0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1, 0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9, 0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330, 0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78 }; /* Calculate the CRC for a buffer using a seed of 0xffff */ uint16_t dm_crc16 (const char *buffer, size_t len) { uint16_t crc = 0xffff; while (len--) crc = crc_table[(crc ^ *buffer++) & 0xff] ^ (crc >> 8); return ~crc; } #define DIAG_ESC_CHAR 0x7D /* Escape sequence 1st character value */ #define DIAG_ESC_MASK 0x20 /* Escape sequence complement value */ /* Performs DM escaping on inbuf putting the result into outbuf, and returns * the final length of the buffer. */ size_t dm_escape (const char *inbuf, size_t inbuf_len, char *outbuf, size_t outbuf_len) { const char *src = inbuf; char *dst = outbuf; size_t i = inbuf_len; qcdm_return_val_if_fail (inbuf != NULL, 0); qcdm_return_val_if_fail (inbuf_len > 0, 0); qcdm_return_val_if_fail (outbuf != NULL, 0); qcdm_return_val_if_fail (outbuf_len > inbuf_len, 0); /* Since escaping potentially doubles the # of bytes, short-circuit the * length check if destination buffer is clearly large enough. Note the * */ if (outbuf_len <= inbuf_len << 1) { size_t outbuf_required = inbuf_len + 1; /* +1 for the trailing control char */ /* Each escaped character takes up two bytes in the output buffer */ while (i--) { if (*src == DIAG_CONTROL_CHAR || *src == DIAG_ESC_CHAR) outbuf_required++; src++; } if (outbuf_len < outbuf_required) return 0; } /* Do the actual escaping. Replace both the control character and * the escape character in the source buffer with the following sequence: * * */ src = inbuf; i = inbuf_len; while (i--) { if (*src == DIAG_CONTROL_CHAR || *src == DIAG_ESC_CHAR) { *dst++ = DIAG_ESC_CHAR; *dst++ = *src ^ DIAG_ESC_MASK; } else *dst++ = *src; src++; } return (dst - outbuf); } size_t dm_unescape (const char *inbuf, size_t inbuf_len, char *outbuf, size_t outbuf_len, qcdmbool *escaping) { size_t i, outsize; qcdm_return_val_if_fail (inbuf_len > 0, 0); qcdm_return_val_if_fail (outbuf_len >= inbuf_len, 0); qcdm_return_val_if_fail (escaping != NULL, 0); for (i = 0, outsize = 0; i < inbuf_len; i++) { if (*escaping) { outbuf[outsize++] = inbuf[i] ^ DIAG_ESC_MASK; *escaping = FALSE; } else if (inbuf[i] == DIAG_ESC_CHAR) *escaping = TRUE; else outbuf[outsize++] = inbuf[i]; /* About to overrun output buffer size */ if (outsize >= outbuf_len) return 0; } return outsize; } /** * dm_encapsulate_buffer: * @inbuf: buffer in which a valid QCDM packet exists * @cmd_len: size of the QCDM packet contained in @inbuf * @inbuf_len: total size of @inbuf itself (not just the packet) * @outbuf: buffer in which to put the encapsulated QCDM packet * @outbuf_len: total size of @outbuf * * Escapes and CRCs a QCDM packet, and finally adds the trailing control * character that denotes the end of the QCDM packet. * * Returns: size of the encapsulated QCDM command writted to @outbuf. **/ size_t dm_encapsulate_buffer (char *inbuf, size_t cmd_len, size_t inbuf_len, char *outbuf, size_t outbuf_len) { uint16_t crc; size_t escaped_len; qcdm_return_val_if_fail (inbuf != NULL, 0); qcdm_return_val_if_fail (cmd_len >= 1, 0); qcdm_return_val_if_fail (inbuf_len >= cmd_len + 2, 0); /* space for CRC */ qcdm_return_val_if_fail (outbuf != NULL, 0); /* Add the CRC */ crc = dm_crc16 (inbuf, cmd_len); inbuf[cmd_len++] = crc & 0xFF; inbuf[cmd_len++] = (crc >> 8) & 0xFF; escaped_len = dm_escape (inbuf, cmd_len, outbuf, outbuf_len); qcdm_return_val_if_fail (outbuf_len > escaped_len, 0); outbuf[escaped_len++] = DIAG_CONTROL_CHAR; return escaped_len; } /** * dm_decapsulate_buffer: * @inbuf: buffer in which to look for a QCDM packet * @inbuf_len: length of valid data in @inbuf * @outbuf: buffer in which to put decapsulated QCDM packet * @outbuf_len: max size of @outbuf * @out_decap_len: on success, size of the decapsulated QCDM packet * @out_used: on either success or failure, amount of data used; caller should * discard this much data from @inbuf before the next call to this function * @out_need_more: when TRUE, indicates that more data is required before * and determination about a valid QCDM packet can be made; caller should add * more data to @inbuf before calling this function again. * * Attempts to retrieve, unescape, and CRC-check a QCDM packet from the given * buffer. * * Returns: FALSE on error (packet was invalid or malformed, or the CRC check * failed, etc) and places number of bytes to discard from @inbuf in @out_used. * When TRUE, either more data is required (in which case @out_need_more will * be TRUE), or a QCDM packet was successfully retrieved from @inbuf and the * decapsulated packet of length @out_decap_len was placed into @outbuf. In * all cases the caller should advance the buffer by the number of bytes * returned in @out_used before calling this function again. **/ qcdmbool dm_decapsulate_buffer (const char *inbuf, size_t inbuf_len, char *outbuf, size_t outbuf_len, size_t *out_decap_len, size_t *out_used, qcdmbool *out_need_more) { qcdmbool escaping = FALSE; size_t i, pkt_len = 0, unesc_len; uint16_t crc, pkt_crc; qcdm_return_val_if_fail (inbuf != NULL, FALSE); qcdm_return_val_if_fail (outbuf != NULL, FALSE); qcdm_return_val_if_fail (outbuf_len > 0, FALSE); qcdm_return_val_if_fail (out_decap_len != NULL, FALSE); qcdm_return_val_if_fail (out_used != NULL, FALSE); qcdm_return_val_if_fail (out_need_more != NULL, FALSE); *out_decap_len = 0; *out_used = 0; *out_need_more = FALSE; if (inbuf_len < 4) { *out_need_more = TRUE; return TRUE; } /* Find the async control character */ for (i = 0; i < inbuf_len; i++) { if (inbuf[i] == DIAG_CONTROL_CHAR) { /* If the control character shows up in a position before a valid * QCDM packet length (4), the packet is malformed. */ if (i < 3) { /* Tell the caller to advance the buffer past the control char */ *out_used = i + 1; return FALSE; } pkt_len = i; break; } } /* No control char yet, need more data */ if (!pkt_len) { *out_need_more = TRUE; return TRUE; } /* Unescape first; note that pkt_len */ unesc_len = dm_unescape (inbuf, pkt_len, outbuf, outbuf_len, &escaping); if (!unesc_len) { /* Tell the caller to advance the buffer past the control char */ *out_used = pkt_len + 1; return FALSE; } if (escaping) { *out_need_more = TRUE; return TRUE; } /* Check the CRC of the packet's data */ crc = dm_crc16 (outbuf, unesc_len - 2); pkt_crc = outbuf[unesc_len - 2] & 0xFF; pkt_crc |= (outbuf[unesc_len - 1] & 0xFF) << 8; if (crc != pkt_crc) { *out_used = pkt_len + 1; /* packet + CRC + 0x7E */ return FALSE; } *out_used = pkt_len + 1; /* packet + CRC + 0x7E */ *out_decap_len = unesc_len - 2; /* decap_len should not include the CRC */ return TRUE; } ModemManager-1.23.4-dev/libqcdm/src/utils.h000066400000000000000000000037121456466623000204430ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * Copyright (C) 2010 Red Hat, Inc. * * This program is free software: you can redistribute it and/or * modify it under the terms of version 2 of the GNU General Public * License as published by the Free Software Foundation * * 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, see . */ #ifndef LIBQCDM_UTILS_H #define LIBQCDM_UTILS_H #include #include typedef uint8_t qcdmbool; #ifndef TRUE #define TRUE ((uint8_t) 1) #endif #ifndef FALSE #define FALSE ((uint8_t) 0) #endif #define DIAG_CONTROL_CHAR 0x7E #define DIAG_TRAILER_LEN 3 uint16_t dm_crc16 (const char *buffer, size_t len); size_t dm_escape (const char *inbuf, size_t inbuf_len, char *outbuf, size_t outbuf_len); size_t dm_unescape (const char *inbuf, size_t inbuf_len, char *outbuf, size_t outbuf_len, qcdmbool *escaping); size_t dm_encapsulate_buffer (char *inbuf, size_t cmd_len, size_t inbuf_len, char *outbuf, size_t outbuf_len); qcdmbool dm_decapsulate_buffer (const char *inbuf, size_t inbuf_len, char *outbuf, size_t outbuf_len, size_t *out_decap_len, size_t *out_used, qcdmbool *out_need_more); #endif /* LIBQCDM_UTILS_H */ ModemManager-1.23.4-dev/libqcdm/tests/000077500000000000000000000000001456466623000175025ustar00rootroot00000000000000ModemManager-1.23.4-dev/libqcdm/tests/ipv6pref.c000066400000000000000000000160331456466623000214120ustar00rootroot00000000000000/* -*- 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) 2012 Red Hat, Inc. */ #include #include #include #include #include #include #include #include #include #include "utils.h" #include "errors.h" #include "commands.h" #include "com.h" static int debug = 0; static void print_buf (const char *detail, const char *buf, size_t len) { unsigned int i, z; qcdmbool newline = FALSE; char tmp[500]; uint32_t flen; flen = snprintf (tmp, sizeof (tmp) - 1, "%s (%zu) ", detail, len); fprintf (stdout, "%s", tmp); for (i = 0; i < len; i++) { fprintf (stdout, "%02x ", buf[i] & 0xFF); if (((i + 1) % 16) == 0) { fprintf (stdout, "\n"); z = flen; while (z--) fprintf (stdout, " "); newline = TRUE; } else newline = FALSE; } if (!newline) fprintf (stdout, "\n"); } static int com_setup (const char *port) { int ret, fd; errno = 0; fd = open (port, O_RDWR | O_EXCL | O_NONBLOCK | O_NOCTTY); if (fd < 0) { fprintf (stderr, "E: failed to open port %s\n", port); return -1; } ret = ioctl (fd, TIOCEXCL); if (ret) { fprintf (stderr, "E: failed to lock port %s\n", port); close (fd); return -1; } return fd; } /******************************************************************/ static qcdmbool qcdm_send (int fd, char *buf, size_t len) { int status; int eagain_count = 1000; size_t i = 0; if (debug) print_buf ("DM:ENC>>>", buf, len); while (i < len) { errno = 0; status = write (fd, &buf[i], 1); if (status < 0) { if (errno == EAGAIN) { eagain_count--; if (eagain_count <= 0) return FALSE; } else assert (errno == 0); } else i++; usleep (1000); } return TRUE; } static size_t qcdm_wait_reply (int fd, char *buf, size_t len) { fd_set in; int result; struct timeval timeout = { 1, 0 }; char readbuf[1024]; ssize_t bytes_read; unsigned int total = 0, retries = 0; size_t decap_len = 0; FD_ZERO (&in); FD_SET (fd, &in); result = select (fd + 1, &in, NULL, NULL, &timeout); if (result != 1 || !FD_ISSET (fd, &in)) return 0; do { errno = 0; bytes_read = read (fd, &readbuf[total], 1); if ((bytes_read == 0) || (errno == EAGAIN)) { /* Haven't gotten the async control char yet */ if (retries > 20) return 0; /* 2 seconds, give up */ /* Otherwise wait a bit and try again */ usleep (100000); retries++; continue; } else if (bytes_read == 1) { qcdmbool more = FALSE; qcdmbool success; size_t used = 0; total++; decap_len = 0; success = dm_decapsulate_buffer (readbuf, total, buf, len, &decap_len, &used, &more); /* Discard used data */ if (used > 0) { total -= used; memmove (readbuf, &readbuf[used], total); } if (success && !more) { /* Success; we have a packet */ break; } } else { /* Some error occurred */ return 0; } } while (total < sizeof (readbuf)); if (debug) print_buf ("QCDM:DEC<<", buf, decap_len); return decap_len; } static int qcdm_set_ipv6_enabled (int fd, uint8_t ipv6pref) { int err; char buf[512]; size_t len; QcdmResult *result; size_t reply_len; len = qcdm_cmd_nv_set_ipv6_enabled_new (buf, sizeof (buf), ipv6pref); assert (len); /* Send the command */ if (!qcdm_send (fd, buf, len)) { fprintf (stderr, "E: failed to send QCDM IPv6 enabled command\n"); return -1; } reply_len = qcdm_wait_reply (fd, buf, sizeof (buf)); if (!reply_len) { fprintf (stderr, "E: failed to receive QCDM IPv6 enabled command reply\n"); return -1; } /* Parse the response into a result structure */ err = QCDM_SUCCESS; result = qcdm_cmd_nv_set_ipv6_enabled_result (buf, reply_len, &err); if (!result) { fprintf (stderr, "E: failed to parse QCDM IPv6 enabled command reply: %d\n", err); return -1; } qcdm_result_unref (result); return 0; } static int qcdm_get_ipv6_enabled (int fd) { int err; char buf[512]; size_t len; QcdmResult *result; size_t reply_len; uint8_t mode; len = qcdm_cmd_nv_get_ipv6_enabled_new (buf, sizeof (buf)); assert (len); /* Send the command */ if (!qcdm_send (fd, buf, len)) { fprintf (stderr, "E: failed to send QCDM IPv6 enabled command\n"); return -1; } reply_len = qcdm_wait_reply (fd, buf, sizeof (buf)); if (!reply_len) { fprintf (stderr, "E: failed to receive QCDM IPv6 pref command reply\n"); return -1; } /* Parse the response into a result structure */ err = QCDM_SUCCESS; result = qcdm_cmd_nv_get_ipv6_enabled_result (buf, reply_len, &err); if (!result) { /* An inactive NVRAM entry has the same effect as setting "disabled" */ if (err == -QCDM_ERROR_NV_ERROR_INACTIVE) return QCDM_CMD_NV_IPV6_ENABLED_OFF; fprintf (stderr, "E: failed to parse QCDM IPv6 enabled command reply: %d\n", err); return -1; } err = qcdm_result_get_u8 (result, QCDM_CMD_NV_GET_IPV6_ENABLED_ITEM_ENABLED, &mode); qcdm_result_unref (result); return mode; } /******************************************************************/ static void usage (const char *prog) { fprintf (stderr, "Usage: %s [--enable|--disable] [--debug]\n", prog); fprintf (stderr, " Current mode will always be printed.\n\n"); } int main (int argc, char *argv[]) { const char *dmport = argv[1]; int fd, err, old, new = -1; if (argc < 2 || argc > 4) { usage (argv[0]); return 1; } if (argc >= 3) { if (strcasecmp (argv[2], "--debug") == 0) debug = 1; else if (strcasecmp (argv[2], "--enable") == 0) new = QCDM_CMD_NV_IPV6_ENABLED_ON; else if (strcasecmp (argv[2], "--disable") == 0) new = QCDM_CMD_NV_IPV6_ENABLED_OFF; else usage (argv[0]); if (argc >= 4 && strcasecmp (argv[3], "--debug") == 0) debug = 1; } if (debug) putenv ((char *)"QCDM_DEBUG=1"); fd = com_setup (dmport); if (fd < 0) return 1; err = qcdm_port_setup (fd); if (err != QCDM_SUCCESS) { fprintf (stderr, "E: failed to set up DM port %s: %d\n", dmport, err); return 1; } old = qcdm_get_ipv6_enabled (fd); if (old < 0) { fprintf (stderr, "E: failed to get IPv6 state\n"); return 1; } fprintf (stdout, "IPv6: %s\n", old ? "enabled" : "disabled"); if (new >=0 && old != new) { if (qcdm_set_ipv6_enabled (fd, new)) fprintf (stdout, "Failed to %s IPv6\n", new ? "enable" : "disable"); else fprintf (stdout, "IPv6 successfully %s. Replug your device.\n", new ? "enabled" : "disabled"); } return 0; } ModemManager-1.23.4-dev/libqcdm/tests/meson.build000066400000000000000000000013001456466623000216360ustar00rootroot00000000000000# SPDX-License-Identifier: GPL-2.0-or-later # Copyright (C) 2021 Iñigo Martinez test_units = [ ['ipv6pref', files('ipv6pref.c'), false], ['modepref', files('modepref.c'), false], ['reset', files('reset.c'), false], ] sources = files( 'test-qcdm.c', 'test-qcdm-com.c', 'test-qcdm-crc.c', 'test-qcdm-escaping.c', 'test-qcdm-result.c', 'test-qcdm-utils.c', ) test_units += [['test-qcdm', sources, true]] deps = [ glib_deps, libqcdm_dep, ] foreach test_unit: test_units exe = executable( test_unit[0], test_unit[1], include_directories: top_inc, dependencies: deps, ) if test_unit[2] test(test_unit[0], exe) endif endforeach ModemManager-1.23.4-dev/libqcdm/tests/modepref.c000066400000000000000000000324271456466623000214570ustar00rootroot00000000000000/* -*- 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) 2012 Red Hat, Inc. */ #include #include #include #include #include #include #include #include #include #include "utils.h" #include "errors.h" #include "commands.h" #include "com.h" static int debug = 0; static void print_buf (const char *detail, const char *buf, size_t len) { unsigned int i, z; qcdmbool newline = FALSE; char tmp[500]; uint32_t flen; flen = snprintf (tmp, sizeof (tmp) - 1, "%s (%zu) ", detail, len); fprintf (stdout, "%s", tmp); for (i = 0; i < len; i++) { fprintf (stdout, "%02x ", buf[i] & 0xFF); if (((i + 1) % 16) == 0) { fprintf (stdout, "\n"); z = flen; while (z--) fprintf (stdout, " "); newline = TRUE; } else newline = FALSE; } if (!newline) fprintf (stdout, "\n"); } static int com_setup (const char *port) { int ret, fd; errno = 0; fd = open (port, O_RDWR | O_EXCL | O_NONBLOCK | O_NOCTTY); if (fd < 0) { fprintf (stderr, "E: failed to open port %s\n", port); return -1; } ret = ioctl (fd, TIOCEXCL); if (ret) { fprintf (stderr, "E: failed to lock port %s\n", port); close (fd); return -1; } return fd; } /******************************************************************/ static qcdmbool qcdm_send (int fd, char *buf, size_t len) { int status; int eagain_count = 1000; size_t i = 0; if (debug) print_buf ("DM:ENC>>>", buf, len); while (i < len) { errno = 0; status = write (fd, &buf[i], 1); if (status < 0) { if (errno == EAGAIN) { eagain_count--; if (eagain_count <= 0) return FALSE; } else assert (errno == 0); } else i++; usleep (1000); } return TRUE; } static size_t qcdm_wait_reply (int fd, char *buf, size_t len) { fd_set in; int result; struct timeval timeout = { 1, 0 }; char readbuf[1024]; ssize_t bytes_read; unsigned int total = 0, retries = 0; size_t decap_len = 0; FD_ZERO (&in); FD_SET (fd, &in); result = select (fd + 1, &in, NULL, NULL, &timeout); if (result != 1 || !FD_ISSET (fd, &in)) return 0; do { errno = 0; bytes_read = read (fd, &readbuf[total], 1); if ((bytes_read == 0) || (errno == EAGAIN)) { /* Haven't gotten the async control char yet */ if (retries > 20) return 0; /* 2 seconds, give up */ /* Otherwise wait a bit and try again */ usleep (100000); retries++; continue; } else if (bytes_read == 1) { qcdmbool more = FALSE; qcdmbool success; size_t used = 0; total++; decap_len = 0; success = dm_decapsulate_buffer (readbuf, total, buf, len, &decap_len, &used, &more); /* Discard used data */ if (used > 0) { total -= used; memmove (readbuf, &readbuf[used], total); } if (success && !more) { /* Success; we have a packet */ break; } } else { /* Some error occurred */ return 0; } } while (total < sizeof (readbuf)); if (debug) print_buf ("QCDM:DEC<<", buf, decap_len); return decap_len; } static int qcdm_set_mode_pref (int fd, uint8_t modepref) { int err; char buf[512]; size_t len; QcdmResult *result; size_t reply_len; len = qcdm_cmd_nv_set_mode_pref_new (buf, sizeof (buf), 0, modepref); assert (len); /* Send the command */ if (!qcdm_send (fd, buf, len)) { fprintf (stderr, "E: failed to send QCDM mode pref command\n"); return -1; } reply_len = qcdm_wait_reply (fd, buf, sizeof (buf)); if (!reply_len) { fprintf (stderr, "E: failed to receive QCDM mode pref command reply\n"); return -1; } /* Parse the response into a result structure */ err = QCDM_SUCCESS; result = qcdm_cmd_nv_set_mode_pref_result (buf, reply_len, &err); if (!result) { fprintf (stderr, "E: failed to parse QCDM mode pref command reply: %d\n", err); return -1; } qcdm_result_unref (result); return 0; } static const char * qcdm_get_mode_pref (int fd) { int err; char buf[512]; size_t len; QcdmResult *result; size_t reply_len; const char *smode = NULL; uint8_t mode = 0; len = qcdm_cmd_nv_get_mode_pref_new (buf, sizeof (buf), 0); assert (len); /* Send the command */ if (!qcdm_send (fd, buf, len)) { fprintf (stderr, "E: failed to send QCDM mode pref command\n"); return NULL; } reply_len = qcdm_wait_reply (fd, buf, sizeof (buf)); if (!reply_len) { fprintf (stderr, "E: failed to receive QCDM mode pref command reply\n"); return NULL; } /* Parse the response into a result structure */ err = QCDM_SUCCESS; result = qcdm_cmd_nv_get_mode_pref_result (buf, reply_len, &err); if (!result) { fprintf (stderr, "E: failed to parse QCDM mode pref command reply: %d\n", err); return NULL; } err = qcdm_result_get_u8 (result, QCDM_CMD_NV_GET_MODE_PREF_ITEM_MODE_PREF, &mode); if (err == QCDM_SUCCESS) { switch (mode) { case QCDM_CMD_NV_MODE_PREF_ITEM_MODE_PREF_DIGITAL: smode = "digital"; break; case QCDM_CMD_NV_MODE_PREF_ITEM_MODE_PREF_DIGITAL_ONLY: smode = "digital only"; break; case QCDM_CMD_NV_MODE_PREF_ITEM_MODE_PREF_AUTO: smode = "automatic"; break; case QCDM_CMD_NV_MODE_PREF_ITEM_MODE_PREF_1X_ONLY: smode = "CDMA 1x only"; break; case QCDM_CMD_NV_MODE_PREF_ITEM_MODE_PREF_HDR_ONLY: smode = "HDR only"; break; case QCDM_CMD_NV_MODE_PREF_ITEM_MODE_PREF_GPRS_ONLY: smode = "GPRS only"; break; case QCDM_CMD_NV_MODE_PREF_ITEM_MODE_PREF_UMTS_ONLY: smode = "UMTS only"; break; case QCDM_CMD_NV_MODE_PREF_ITEM_MODE_PREF_GSM_UMTS_ONLY: smode = "GSM and UMTS only"; break; case QCDM_CMD_NV_MODE_PREF_ITEM_MODE_PREF_1X_HDR_ONLY: smode = "CDMA 1x and HDR only"; break; case QCDM_CMD_NV_MODE_PREF_ITEM_MODE_PREF_LTE_ONLY: smode = "LTE only"; break; case QCDM_CMD_NV_MODE_PREF_ITEM_MODE_PREF_GSM_UMTS_LTE_ONLY: smode = "GSM/UMTS/LTE only"; break; case QCDM_CMD_NV_MODE_PREF_ITEM_MODE_PREF_1X_HDR_LTE_ONLY: smode = "CDMA 1x, HDR, and LTE only"; break; default: break; } } qcdm_result_unref (result); return smode; } static int qcdm_set_hdr_pref (int fd, uint8_t hdrpref) { int err; char buf[512]; size_t len; QcdmResult *result; size_t reply_len; len = qcdm_cmd_nv_set_hdr_rev_pref_new (buf, sizeof (buf), hdrpref); assert (len); /* Send the command */ if (!qcdm_send (fd, buf, len)) { fprintf (stderr, "E: failed to send QCDM HDR pref command\n"); return -1; } reply_len = qcdm_wait_reply (fd, buf, sizeof (buf)); if (!reply_len) { fprintf (stderr, "E: failed to receive HDR pref command reply\n"); return -1; } /* Parse the response into a result structure */ err = QCDM_SUCCESS; result = qcdm_cmd_nv_set_hdr_rev_pref_result (buf, reply_len, &err); if (!result) { fprintf (stderr, "E: failed to parse HDR pref command reply: %d\n", err); return -1; } qcdm_result_unref (result); return 0; } static const char * qcdm_get_hdr_pref (int fd) { int err; char buf[512]; size_t len; QcdmResult *result = NULL; size_t reply_len; uint8_t pref; const char *spref = NULL; len = qcdm_cmd_nv_get_hdr_rev_pref_new (buf, sizeof (buf)); assert (len > 0); /* Send the command */ if (!qcdm_send (fd, buf, len)) { fprintf (stderr, "E: failed to send QCDM HDR pref command\n"); goto error; } reply_len = qcdm_wait_reply (fd, buf, sizeof (buf)); if (!reply_len) { fprintf (stderr, "E: failed to receive HDR pref command reply\n"); goto error; } /* Parse the response into a result structure */ err = QCDM_SUCCESS; result = qcdm_cmd_nv_get_hdr_rev_pref_result (buf, reply_len, &err); if (!result) { fprintf (stderr, "E: failed to parse HDR pref command reply: %d\n", err); goto error; } err = qcdm_result_get_u8 (result, QCDM_CMD_NV_GET_HDR_REV_PREF_ITEM_REV_PREF, &pref); if (err != QCDM_SUCCESS) goto error; switch (pref) { case QCDM_CMD_NV_HDR_REV_PREF_ITEM_REV_PREF_0: spref = "rev0"; break; case QCDM_CMD_NV_HDR_REV_PREF_ITEM_REV_PREF_A: spref = "revA"; break; case QCDM_CMD_NV_HDR_REV_PREF_ITEM_REV_PREF_EHRPD: spref = "eHRPD"; break; default: break; } error: if (result) qcdm_result_unref (result); return spref; } static int qcdm_set_mode (int fd, uint8_t mode) { int err; char buf[512]; size_t len; QcdmResult *result; size_t reply_len; len = qcdm_cmd_control_new (buf, sizeof (buf), mode); assert (len); /* Send the command */ if (!qcdm_send (fd, buf, len)) { fprintf (stderr, "E: failed to send QCDM Control command\n"); goto error; } reply_len = qcdm_wait_reply (fd, buf, sizeof (buf)); if (!reply_len) { fprintf (stderr, "E: failed to receive Control command reply\n"); goto error; } /* Parse the response into a result structure */ err = QCDM_SUCCESS; result = qcdm_cmd_control_result (buf, reply_len, &err); if (!result) { fprintf (stderr, "E: failed to parse Control command reply: %d\n", err); goto error; } qcdm_result_unref (result); return 0; error: return -1; } /******************************************************************/ static void usage (const char *prog) { fprintf (stderr, "Usage: %s [] [--debug]\n", prog); fprintf (stderr, " = auto, lte, auto-cdma-lte, auto-cdma, cdma, evdo, auto-gsm-lte, auto-gsm, gsm, umts\n"); fprintf (stderr, " If is missing, current mode will be printed.\n\n"); } static qcdmbool parse_mode (const char *s, uint8_t *out_mode, uint8_t *out_hdrpref, qcdmbool *out_set_evdo) { if (strcasecmp (s, "lte") == 0) { *out_mode = QCDM_CMD_NV_MODE_PREF_ITEM_MODE_PREF_LTE_ONLY; *out_hdrpref = QCDM_CMD_NV_HDR_REV_PREF_ITEM_REV_PREF_EHRPD; return TRUE; } if (strcasecmp (s, "auto-cdma-lte") == 0) { *out_mode = QCDM_CMD_NV_MODE_PREF_ITEM_MODE_PREF_1X_HDR_LTE_ONLY; *out_hdrpref = QCDM_CMD_NV_HDR_REV_PREF_ITEM_REV_PREF_EHRPD; *out_set_evdo = TRUE; return TRUE; } if (strcasecmp (s, "auto-cdma") == 0) { *out_mode = QCDM_CMD_NV_MODE_PREF_ITEM_MODE_PREF_1X_HDR_ONLY; *out_hdrpref = QCDM_CMD_NV_HDR_REV_PREF_ITEM_REV_PREF_A; *out_set_evdo = TRUE; return TRUE; } if (strcasecmp (s, "auto") == 0) { *out_mode = QCDM_CMD_NV_MODE_PREF_ITEM_MODE_PREF_AUTO; *out_hdrpref = QCDM_CMD_NV_HDR_REV_PREF_ITEM_REV_PREF_EHRPD; *out_set_evdo = TRUE; return TRUE; } if (strcasecmp (s, "cdma") == 0) { *out_mode = QCDM_CMD_NV_MODE_PREF_ITEM_MODE_PREF_1X_ONLY; *out_hdrpref = QCDM_CMD_NV_HDR_REV_PREF_ITEM_REV_PREF_A; *out_set_evdo = TRUE; return TRUE; } if (strcasecmp (s, "evdo") == 0) { *out_mode = QCDM_CMD_NV_MODE_PREF_ITEM_MODE_PREF_HDR_ONLY; *out_hdrpref = QCDM_CMD_NV_HDR_REV_PREF_ITEM_REV_PREF_A; *out_set_evdo = TRUE; return TRUE; } if (strcasecmp (s, "auto-gsm-lte") == 0) { *out_mode = QCDM_CMD_NV_MODE_PREF_ITEM_MODE_PREF_GSM_UMTS_LTE_ONLY; return TRUE; } if (strcasecmp (s, "auto-gsm") == 0) { *out_mode = QCDM_CMD_NV_MODE_PREF_ITEM_MODE_PREF_GSM_UMTS_ONLY; return TRUE; } if (strcasecmp (s, "gsm") == 0) { *out_mode = QCDM_CMD_NV_MODE_PREF_ITEM_MODE_PREF_GPRS_ONLY; return TRUE; } if (strcasecmp (s, "umts") == 0) { *out_mode = QCDM_CMD_NV_MODE_PREF_ITEM_MODE_PREF_UMTS_ONLY; return TRUE; } return FALSE; } int main (int argc, char *argv[]) { uint8_t mode = QCDM_CMD_NV_MODE_PREF_ITEM_MODE_PREF_AUTO; uint8_t hdrpref = QCDM_CMD_NV_HDR_REV_PREF_ITEM_REV_PREF_EHRPD; const char *dmport = argv[1]; const char *smode = argv[2]; const char *msg; qcdmbool set_evdo = FALSE; qcdmbool set_mode = FALSE; int fd, err; if (argc < 2 || argc > 4) { usage (argv[0]); return 1; } if (argc >= 3) { if (strcasecmp (argv[2], "--debug") == 0) debug = 1; else { set_mode = parse_mode (argv[2], &mode, &hdrpref, &set_evdo); if (!set_mode) { usage (argv[0]); return 1; } } if (argc >= 4 && strcasecmp (argv[3], "--debug") == 0) debug = 1; } if (debug) putenv ((char *)"QCDM_DEBUG=1"); fd = com_setup (dmport); if (fd < 0) return 1; err = qcdm_port_setup (fd); if (err != QCDM_SUCCESS) { fprintf (stderr, "E: failed to set up DM port %s: %d\n", dmport, err); return 1; } if (set_mode) { if (qcdm_set_mode_pref (fd, mode)) return 1; if (set_evdo && qcdm_set_hdr_pref (fd, hdrpref)) return 1; /* Send DM reset command */ qcdm_set_mode (fd, QCDM_CMD_CONTROL_MODE_OFFLINE); sleep (2); qcdm_set_mode (fd, QCDM_CMD_CONTROL_MODE_RESET); sleep (2); fprintf (stdout, "Success setting mode to '%s': replug your device.\n", smode); } else { msg = qcdm_get_mode_pref (fd); fprintf (stdout, "Mode preference: %s\n", msg ? msg : "(unknown)"); msg = qcdm_get_hdr_pref (fd); fprintf (stdout, "HDR revision: %s\n", msg ? msg : "(unknown)"); } return 0; } ModemManager-1.23.4-dev/libqcdm/tests/reset.c000066400000000000000000000143511456466623000207740ustar00rootroot00000000000000/* -*- 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) 2012 Red Hat, Inc. * Copyright (C) 2014 Aleksander Morgado */ #include #include #include #include #include #include #include #include #include #include "utils.h" #include "errors.h" #include "commands.h" #include "com.h" static int debug = 0; static void print_buf (const char *detail, const char *buf, size_t len) { unsigned int i, z; qcdmbool newline = FALSE; char tmp[500]; uint32_t flen; flen = snprintf (tmp, sizeof (tmp) - 1, "%s (%zu) ", detail, len); fprintf (stdout, "%s", tmp); for (i = 0; i < len; i++) { fprintf (stdout, "%02x ", buf[i] & 0xFF); if (((i + 1) % 16) == 0) { fprintf (stdout, "\n"); z = flen; while (z--) fprintf (stdout, " "); newline = TRUE; } else newline = FALSE; } if (!newline) fprintf (stdout, "\n"); } static int com_setup (const char *port) { int ret, fd; errno = 0; fd = open (port, O_RDWR | O_EXCL | O_NONBLOCK | O_NOCTTY); if (fd < 0) { fprintf (stderr, "E: failed to open port %s\n", port); return -1; } ret = ioctl (fd, TIOCEXCL); if (ret) { fprintf (stderr, "E: failed to lock port %s\n", port); close (fd); return -1; } return fd; } /******************************************************************/ static qcdmbool qcdm_send (int fd, char *buf, size_t len) { int status; int eagain_count = 1000; size_t i = 0; if (debug) print_buf ("DM:ENC>>>", buf, len); while (i < len) { errno = 0; status = write (fd, &buf[i], 1); if (status < 0) { if (errno == EAGAIN) { eagain_count--; if (eagain_count <= 0) return FALSE; } else assert (errno == 0); } else i++; usleep (1000); } return TRUE; } static size_t qcdm_wait_reply (int fd, char *buf, size_t len) { fd_set in; int result; struct timeval timeout = { 1, 0 }; char readbuf[1024]; ssize_t bytes_read; unsigned int total = 0, retries = 0; size_t decap_len = 0; FD_ZERO (&in); FD_SET (fd, &in); result = select (fd + 1, &in, NULL, NULL, &timeout); if (result != 1 || !FD_ISSET (fd, &in)) return 0; do { errno = 0; bytes_read = read (fd, &readbuf[total], 1); if ((bytes_read == 0) || (errno == EAGAIN)) { /* Haven't gotten the async control char yet */ if (retries > 20) return 0; /* 2 seconds, give up */ /* Otherwise wait a bit and try again */ usleep (100000); retries++; continue; } else if (bytes_read == 1) { qcdmbool more = FALSE; qcdmbool success; size_t used = 0; total++; decap_len = 0; success = dm_decapsulate_buffer (readbuf, total, buf, len, &decap_len, &used, &more); /* Discard used data */ if (used > 0) { total -= used; memmove (readbuf, &readbuf[used], total); } if (success && !more) { /* Success; we have a packet */ break; } } else { /* Some error occurred */ return 0; } } while (total < sizeof (readbuf)); if (debug) print_buf ("QCDM:DEC<<", buf, decap_len); return decap_len; } static int qcdm_set_mode (int fd, uint8_t mode) { int err; char buf[512]; size_t len; QcdmResult *result; size_t reply_len; len = qcdm_cmd_control_new (buf, sizeof (buf), mode); assert (len); /* Send the command */ if (!qcdm_send (fd, buf, len)) { fprintf (stderr, "E: failed to send QCDM Control command\n"); goto error; } reply_len = qcdm_wait_reply (fd, buf, sizeof (buf)); if (!reply_len) { fprintf (stderr, "E: failed to receive Control command reply\n"); goto error; } /* Parse the response into a result structure */ err = QCDM_SUCCESS; result = qcdm_cmd_control_result (buf, reply_len, &err); if (!result) { fprintf (stderr, "E: failed to parse Control command reply: %d\n", err); goto error; } qcdm_result_unref (result); return 0; error: return -1; } /******************************************************************/ static void usage (const char *prog) { fprintf (stderr, "Usage: %s [--debug]\n", prog); } int main (int argc, char *argv[]) { const char *dmport = argv[1]; int fd, err; if (argc < 1 || argc > 3) { usage (argv[0]); return 1; } if (argc == 3 && strcasecmp (argv[2], "--debug") == 0) debug = 1; if (debug) putenv ((char *)"QCDM_DEBUG=1"); fd = com_setup (dmport); if (fd < 0) return 1; err = qcdm_port_setup (fd); if (err != QCDM_SUCCESS) { fprintf (stderr, "E: failed to set up DM port %s: %d\n", dmport, err); return 1; } /* Send DM reset command */ printf ("setting offline...\n"); qcdm_set_mode (fd, QCDM_CMD_CONTROL_MODE_OFFLINE); sleep (2); printf ("reset...\n"); qcdm_set_mode (fd, QCDM_CMD_CONTROL_MODE_RESET); sleep (2); printf ("done\n"); return 0; } ModemManager-1.23.4-dev/libqcdm/tests/test-qcdm-com.c000066400000000000000000001604001456466623000223240ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * Copyright (C) 2010 Red Hat, Inc. * * This program is free software: you can redistribute it and/or * modify it under the terms of version 2 of the GNU General Public * License as published by the Free Software Foundation * * 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, see . */ #include #include #include #include #include #include #include #include #include "test-qcdm-com.h" #include "com.h" #include "utils.h" #include "result.h" #include "commands.h" #include "errors.h" /************************************************************/ static const char * prev_to_string (guint8 prev) { switch (prev) { case QCDM_CDMA_PREV_IS_95: return "1 (IS-95)"; case QCDM_CDMA_PREV_IS_95A: return "2 (IS-95A)"; case QCDM_CDMA_PREV_IS_95A_TSB74: return "3 (IS-95A TSB-74)"; case QCDM_CDMA_PREV_IS_95B_PHASE1: return "4 (IS-95B Phase I)"; case QCDM_CDMA_PREV_IS_95B_PHASE2: return "5 (IS-95B Phase II)"; case QCDM_CDMA_PREV_IS2000_REL0: return "6 (IS-2000 Release 0)"; case QCDM_CDMA_PREV_IS2000_RELA: return "7 (IS-2000 Release A)"; default: break; } return "unknown"; } static const char * band_class_to_string (guint8 band_class) { switch (band_class) { case QCDM_CDMA_BAND_CLASS_0_CELLULAR_800: return "0 (Cellular 800)"; case QCDM_CDMA_BAND_CLASS_1_PCS: return "1 (PCS 1900)"; case QCDM_CDMA_BAND_CLASS_2_TACS: return "2 (TACS)"; case QCDM_CDMA_BAND_CLASS_3_JTACS: return "3 (JTACS)"; case QCDM_CDMA_BAND_CLASS_4_KOREAN_PCS: return "4 (Korean PCS)"; case QCDM_CDMA_BAND_CLASS_5_NMT450: return "5 (NMT-450)"; case QCDM_CDMA_BAND_CLASS_6_IMT2000: return "6 (IMT-2000)"; case QCDM_CDMA_BAND_CLASS_7_CELLULAR_700: return "7 (Cellular 700)"; case QCDM_CDMA_BAND_CLASS_8_1800: return "8 (1800 MHz)"; case QCDM_CDMA_BAND_CLASS_9_900: return "9 (1900 MHz)"; case QCDM_CDMA_BAND_CLASS_10_SECONDARY_800: return "10 (Secondary 800 MHz)"; case QCDM_CDMA_BAND_CLASS_11_PAMR_400: return "11 (PAMR 400)"; case QCDM_CDMA_BAND_CLASS_12_PAMR_800: return "11 (PAMR 800)"; default: break; } return "unknown"; } static const char * hdr_rev_to_string (guint8 hdr_rev) { switch (hdr_rev) { case QCDM_HDR_REV_0: return "0"; case QCDM_HDR_REV_A: return "A"; default: break; } return "unknown"; } static const char * status_snapshot_state_to_string (guint8 state) { switch (state) { case QCDM_CMD_STATUS_SNAPSHOT_STATE_NO_SERVICE: return "no service"; case QCDM_CMD_STATUS_SNAPSHOT_STATE_INITIALIZATION: return "initialization"; case QCDM_CMD_STATUS_SNAPSHOT_STATE_IDLE: return "idle"; case QCDM_CMD_STATUS_SNAPSHOT_STATE_VOICE_CHANNEL_INIT: return "voice channel init"; case QCDM_CMD_STATUS_SNAPSHOT_STATE_WAITING_FOR_ORDER: return "waiting for order"; case QCDM_CMD_STATUS_SNAPSHOT_STATE_WAITING_FOR_ANSWER: return "waiting for answer"; case QCDM_CMD_STATUS_SNAPSHOT_STATE_CONVERSATION: return "conversation"; case QCDM_CMD_STATUS_SNAPSHOT_STATE_RELEASE: return "release"; case QCDM_CMD_STATUS_SNAPSHOT_STATE_SYSTEM_ACCESS: return "system access"; case QCDM_CMD_STATUS_SNAPSHOT_STATE_OFFLINE_CDMA: return "offline CDMA"; case QCDM_CMD_STATUS_SNAPSHOT_STATE_OFFLINE_HDR: return "offline HDR"; case QCDM_CMD_STATUS_SNAPSHOT_STATE_OFFLINE_ANALOG: return "offline analog"; case QCDM_CMD_STATUS_SNAPSHOT_STATE_RESET: return "reset"; case QCDM_CMD_STATUS_SNAPSHOT_STATE_POWER_DOWN: return "power down"; case QCDM_CMD_STATUS_SNAPSHOT_STATE_POWER_SAVE: return "power save"; case QCDM_CMD_STATUS_SNAPSHOT_STATE_POWER_UP: return "power up"; case QCDM_CMD_STATUS_SNAPSHOT_STATE_LOW_POWER_MODE: return "low power mode"; case QCDM_CMD_STATUS_SNAPSHOT_STATE_SEARCHER_DSMM: return "searcher DSMM"; case QCDM_CMD_STATUS_SNAPSHOT_STATE_HDR: return "HDR"; default: break; } return "unknown"; } static const char * cm_call_state_to_string (uint32_t state) { switch (state) { case QCDM_CMD_CM_SUBSYS_STATE_INFO_CALL_STATE_IDLE: return "idle"; case QCDM_CMD_CM_SUBSYS_STATE_INFO_CALL_STATE_ORIGINATING: return "originating"; case QCDM_CMD_CM_SUBSYS_STATE_INFO_CALL_STATE_ALERTING: return "alerting"; case QCDM_CMD_CM_SUBSYS_STATE_INFO_CALL_STATE_ORIGINATION_ALERTING: return "originating alerting"; case QCDM_CMD_CM_SUBSYS_STATE_INFO_CALL_STATE_CONVERSATION: return "conversation"; default: break; } return "unknown"; } static const char * cm_system_mode_to_string (uint32_t mode) { switch (mode) { case QCDM_CMD_CM_SUBSYS_STATE_INFO_SYSTEM_MODE_NO_SERVICE: return "no service"; case QCDM_CMD_CM_SUBSYS_STATE_INFO_SYSTEM_MODE_AMPS: return "AMPS"; case QCDM_CMD_CM_SUBSYS_STATE_INFO_SYSTEM_MODE_CDMA: return "CDMA"; case QCDM_CMD_CM_SUBSYS_STATE_INFO_SYSTEM_MODE_GSM: return "GSM"; case QCDM_CMD_CM_SUBSYS_STATE_INFO_SYSTEM_MODE_HDR: return "HDR/EVDO"; case QCDM_CMD_CM_SUBSYS_STATE_INFO_SYSTEM_MODE_WCDMA: return "WCDMA"; case QCDM_CMD_CM_SUBSYS_STATE_INFO_SYSTEM_MODE_GW: return "GSM/WCDMA"; case QCDM_CMD_CM_SUBSYS_STATE_INFO_SYSTEM_MODE_WLAN: return "WLAN"; case QCDM_CMD_CM_SUBSYS_STATE_INFO_SYSTEM_MODE_LTE: return "LTE"; default: break; } return "unknown"; } /************************************************************/ typedef struct { char *port; int fd; struct termios old_t; gboolean debug; } TestComData; gpointer test_com_setup (const char *port) { TestComData *d; int ret; d = g_malloc0 (sizeof (TestComData)); g_assert (d); if (getenv ("SERIAL_DEBUG")) d->debug = TRUE; errno = 0; d->fd = open (port, O_RDWR | O_EXCL | O_NONBLOCK | O_NOCTTY); if (d->fd < 0) g_warning ("%s: open failed: (%d) %s", port, errno, strerror (errno)); g_assert (d->fd >= 0); ret = ioctl (d->fd, TIOCEXCL); if (ret) { g_warning ("%s: lock failed: (%d) %s", port, errno, strerror (errno)); close (d->fd); d->fd = -1; } g_assert (ret == 0); ret = ioctl (d->fd, TCGETA, &d->old_t); if (ret) { g_warning ("%s: old termios failed: (%d) %s", port, errno, strerror (errno)); close (d->fd); d->fd = -1; } g_assert (ret == 0); d->port = g_strdup (port); return d; } void test_com_teardown (gpointer user_data) { TestComData *d = user_data; g_assert (d); g_free (d->port); close (d->fd); g_free (d); } static void print_buf (const char *detail, const char *buf, gsize len) { unsigned int i = 0; gboolean newline = FALSE; g_print ("%s (%zu) ", detail, len); for (i = 0; i < len; i++) { g_print ("0x%02x ", buf[i] & 0xFF); if (((i + 1) % 12) == 0) { g_print ("\n"); newline = TRUE; } else newline = FALSE; } if (!newline) g_print ("\n"); } static gboolean send_command (TestComData *d, char *buf, gsize len) { int status; int eagain_count = 1000; gsize i = 0; if (d->debug) print_buf (">>>", buf, len); while (i < len) { errno = 0; status = write (d->fd, &buf[i], 1); if (status < 0) { if (errno == EAGAIN) { eagain_count--; if (eagain_count <= 0) return FALSE; } else g_assert (errno == 0); } else i++; usleep (1000); } return TRUE; } static gsize wait_reply (TestComData *d, char *buf, gsize len) { fd_set in; int result; struct timeval timeout = { 1, 0 }; char readbuf[1024]; ssize_t bytes_read; unsigned int total = 0, retries = 0; gsize decap_len = 0; FD_ZERO (&in); FD_SET (d->fd, &in); result = select (d->fd + 1, &in, NULL, NULL, &timeout); if (result != 1 || !FD_ISSET (d->fd, &in)) return 0; do { errno = 0; bytes_read = read (d->fd, &readbuf[total], 1); if ((bytes_read == 0) || (errno == EAGAIN)) { /* Haven't gotten the async control char yet */ if (retries > 20) return 0; /* 2 seconds, give up */ /* Otherwise wait a bit and try again */ usleep (100000); retries++; continue; } else if (bytes_read == 1) { qcdmbool more = FALSE; gboolean success; gsize used = 0; total++; decap_len = 0; success = dm_decapsulate_buffer (readbuf, total, buf, len, &decap_len, &used, &more); /* Discard used data */ if (used > 0) { total -= used; memmove (readbuf, &readbuf[used], total); } if (success && !more) { /* Success; we have a packet */ break; } } else { /* Some error occurred */ return 0; } } while (total < sizeof (readbuf)); if (d->debug) { print_buf ("<<<", readbuf, total); print_buf ("D<<", buf, decap_len); } return decap_len; } void test_com_port_init (void *f, void *data) { TestComData *d = data; int err; err = qcdm_port_setup (d->fd); if (err != QCDM_SUCCESS) g_warning ("%s: error setting up port: %d", d->port, err); g_assert (err == QCDM_SUCCESS); } void test_com_version_info (void *f, void *data) { TestComData *d = data; gboolean success; char buf[512]; const char *str; gint len; QcdmResult *result; gsize reply_len; len = qcdm_cmd_version_info_new (buf, sizeof (buf)); g_assert (len == 4); /* Send the command */ success = send_command (d, buf, len); g_assert (success); /* Get a response */ reply_len = wait_reply (d, buf, sizeof (buf)); /* Parse the response into a result structure */ result = qcdm_cmd_version_info_result (buf, reply_len, NULL); g_assert (result); g_print ("\n"); str = NULL; qcdm_result_get_string (result, QCDM_CMD_VERSION_INFO_ITEM_COMP_DATE, &str); g_message ("%s: Compiled Date: %s", __func__, str); str = NULL; qcdm_result_get_string (result, QCDM_CMD_VERSION_INFO_ITEM_COMP_TIME, &str); g_message ("%s: Compiled Time: %s", __func__, str); str = NULL; qcdm_result_get_string (result, QCDM_CMD_VERSION_INFO_ITEM_RELEASE_DATE, &str); g_message ("%s: Release Date: %s", __func__, str); str = NULL; qcdm_result_get_string (result, QCDM_CMD_VERSION_INFO_ITEM_RELEASE_TIME, &str); g_message ("%s: Release Time: %s", __func__, str); str = NULL; qcdm_result_get_string (result, QCDM_CMD_VERSION_INFO_ITEM_MODEL, &str); g_message ("%s: Model: %s", __func__, str); qcdm_result_unref (result); } void test_com_esn (void *f, void *data) { TestComData *d = data; gboolean success; char buf[512]; const char *str; gint len; QcdmResult *result; gsize reply_len; len = qcdm_cmd_esn_new (buf, sizeof (buf)); g_assert (len == 4); /* Send the command */ success = send_command (d, buf, len); g_assert (success); /* Get a response */ reply_len = wait_reply (d, buf, sizeof (buf)); /* Parse the response into a result structure */ result = qcdm_cmd_esn_result (buf, reply_len, NULL); g_assert (result); g_print ("\n"); str = NULL; qcdm_result_get_string (result, QCDM_CMD_ESN_ITEM_ESN, &str); g_message ("%s: ESN: %s", __func__, str); qcdm_result_unref (result); } void test_com_mdn (void *f, void *data) { TestComData *d = data; gboolean success; char buf[512]; const char *str; gint len; QcdmResult *result; gsize reply_len; int err = QCDM_SUCCESS; len = qcdm_cmd_nv_get_mdn_new (buf, sizeof (buf), 0); g_assert (len > 0); /* Send the command */ success = send_command (d, buf, len); g_assert (success); /* Get a response */ reply_len = wait_reply (d, buf, sizeof (buf)); /* Parse the response into a result structure */ result = qcdm_cmd_nv_get_mdn_result (buf, reply_len, &err); if (!result) { if ( err == -QCDM_ERROR_NVCMD_FAILED || err == -QCDM_ERROR_RESPONSE_BAD_PARAMETER || err == -QCDM_ERROR_NV_ERROR_INACTIVE || err == -QCDM_ERROR_NV_ERROR_BAD_PARAMETER) return; g_assert_cmpint (err, ==, QCDM_SUCCESS); } g_print ("\n"); str = NULL; qcdm_result_get_string (result, QCDM_CMD_NV_GET_MDN_ITEM_MDN, &str); g_message ("%s: MDN: %s", __func__, str); qcdm_result_unref (result); } void test_com_read_roam_pref (void *f, void *data) { TestComData *d = data; gboolean success; int err = QCDM_SUCCESS; char buf[512]; guint8 pref; const char *msg; gint len; QcdmResult *result; gsize reply_len; len = qcdm_cmd_nv_get_roam_pref_new (buf, sizeof (buf), 0); g_assert (len > 0); /* Send the command */ success = send_command (d, buf, len); g_assert (success); /* Get a response */ reply_len = wait_reply (d, buf, sizeof (buf)); /* Parse the response into a result structure */ result = qcdm_cmd_nv_get_roam_pref_result (buf, reply_len, &err); if (!result) { if ( err == -QCDM_ERROR_NVCMD_FAILED || err == -QCDM_ERROR_RESPONSE_BAD_PARAMETER || err == -QCDM_ERROR_NV_ERROR_INACTIVE || err == -QCDM_ERROR_NV_ERROR_BAD_PARAMETER) return; g_assert_cmpint (err, ==, QCDM_SUCCESS); } g_assert (result); g_print ("\n"); err = qcdm_result_get_u8 (result, QCDM_CMD_NV_GET_ROAM_PREF_ITEM_ROAM_PREF, &pref); g_assert_cmpint (err, ==, QCDM_SUCCESS); switch (pref) { case QCDM_CMD_NV_ROAM_PREF_ITEM_ROAM_PREF_HOME_ONLY: msg = "home only"; break; case QCDM_CMD_NV_ROAM_PREF_ITEM_ROAM_PREF_ROAM_ONLY: msg = "roaming only"; break; case QCDM_CMD_NV_ROAM_PREF_ITEM_ROAM_PREF_AUTO: msg = "automatic"; break; default: g_assert_not_reached (); } g_message ("%s: Roam preference: 0x%02X (%s)", __func__, pref, msg); qcdm_result_unref (result); } void test_com_read_mode_pref (void *f, void *data) { TestComData *d = data; gboolean success; int err = QCDM_SUCCESS; char buf[512]; guint8 pref; const char *msg; gint len; QcdmResult *result; gsize reply_len; len = qcdm_cmd_nv_get_mode_pref_new (buf, sizeof (buf), 0); g_assert (len > 0); /* Send the command */ success = send_command (d, buf, len); g_assert (success); /* Get a response */ reply_len = wait_reply (d, buf, sizeof (buf)); /* Parse the response into a result structure */ result = qcdm_cmd_nv_get_mode_pref_result (buf, reply_len, &err); if (!result) { if ( err == -QCDM_ERROR_NVCMD_FAILED || err == -QCDM_ERROR_RESPONSE_BAD_PARAMETER || err == -QCDM_ERROR_NV_ERROR_INACTIVE || err == -QCDM_ERROR_NV_ERROR_BAD_PARAMETER) return; g_assert_cmpint (err, ==, QCDM_SUCCESS); } g_print ("\n"); err = qcdm_result_get_u8 (result, QCDM_CMD_NV_GET_MODE_PREF_ITEM_MODE_PREF, &pref); g_assert_cmpint (err, ==, QCDM_SUCCESS); switch (pref) { case QCDM_CMD_NV_MODE_PREF_ITEM_MODE_PREF_DIGITAL: msg = "digital"; break; case QCDM_CMD_NV_MODE_PREF_ITEM_MODE_PREF_DIGITAL_ONLY: msg = "digital only"; break; case QCDM_CMD_NV_MODE_PREF_ITEM_MODE_PREF_AUTO: msg = "automatic"; break; case QCDM_CMD_NV_MODE_PREF_ITEM_MODE_PREF_1X_ONLY: msg = "CDMA 1x only"; break; case QCDM_CMD_NV_MODE_PREF_ITEM_MODE_PREF_HDR_ONLY: msg = "HDR only"; break; case QCDM_CMD_NV_MODE_PREF_ITEM_MODE_PREF_GPRS_ONLY: msg = "GPRS only"; break; case QCDM_CMD_NV_MODE_PREF_ITEM_MODE_PREF_UMTS_ONLY: msg = "UMTS only"; break; case QCDM_CMD_NV_MODE_PREF_ITEM_MODE_PREF_GSM_UMTS_ONLY: msg = "GSM and UMTS only"; break; case QCDM_CMD_NV_MODE_PREF_ITEM_MODE_PREF_1X_HDR_ONLY: msg = "CDMA 1x and HDR only"; break; case QCDM_CMD_NV_MODE_PREF_ITEM_MODE_PREF_LTE_ONLY: msg = "LTE only"; break; case QCDM_CMD_NV_MODE_PREF_ITEM_MODE_PREF_GSM_UMTS_LTE_ONLY: msg = "GSM/UMTS/LTE only"; break; case QCDM_CMD_NV_MODE_PREF_ITEM_MODE_PREF_1X_HDR_LTE_ONLY: msg = "CDMA 1x, HDR, and LTE only"; break; default: msg = "unknown"; break; } g_message ("%s: Mode preference: 0x%02X (%s)", __func__, pref, msg); qcdm_result_unref (result); } void test_com_read_hybrid_pref (void *f, void *data) { TestComData *d = data; gboolean success; int err = QCDM_SUCCESS; char buf[512]; guint8 pref; gint len; QcdmResult *result; gsize reply_len; len = qcdm_cmd_nv_get_hybrid_pref_new (buf, sizeof (buf)); g_assert (len > 0); /* Send the command */ success = send_command (d, buf, len); g_assert (success); /* Get a response */ reply_len = wait_reply (d, buf, sizeof (buf)); /* Parse the response into a result structure */ result = qcdm_cmd_nv_get_hybrid_pref_result (buf, reply_len, &err); if (!result) { if ( err == -QCDM_ERROR_NVCMD_FAILED || err == -QCDM_ERROR_RESPONSE_BAD_PARAMETER || err == -QCDM_ERROR_NV_ERROR_INACTIVE || err == -QCDM_ERROR_NV_ERROR_BAD_PARAMETER) return; g_assert_cmpint (err, ==, QCDM_SUCCESS); } g_print ("\n"); err = qcdm_result_get_u8 (result, QCDM_CMD_NV_GET_HYBRID_PREF_ITEM_HYBRID_PREF, &pref); g_assert_cmpint (err, ==, QCDM_SUCCESS); g_message ("%s: Hybrid preference: 0x%02X", __func__, pref); qcdm_result_unref (result); } void test_com_read_ipv6_enabled (void *f, void *data) { TestComData *d = data; gboolean success; int err = QCDM_SUCCESS; char buf[512]; guint8 pref; const char *msg; gint len; QcdmResult *result; gsize reply_len; len = qcdm_cmd_nv_get_ipv6_enabled_new (buf, sizeof (buf)); g_assert (len > 0); /* Send the command */ success = send_command (d, buf, len); g_assert (success); /* Get a response */ reply_len = wait_reply (d, buf, sizeof (buf)); /* Parse the response into a result structure */ result = qcdm_cmd_nv_get_ipv6_enabled_result (buf, reply_len, &err); if (!result) { if ( err == -QCDM_ERROR_NVCMD_FAILED || err == -QCDM_ERROR_RESPONSE_BAD_PARAMETER || err == -QCDM_ERROR_NV_ERROR_INACTIVE || err == -QCDM_ERROR_NV_ERROR_BAD_PARAMETER) return; g_assert_cmpint (err, ==, QCDM_SUCCESS); } g_print ("\n"); err = qcdm_result_get_u8 (result, QCDM_CMD_NV_GET_IPV6_ENABLED_ITEM_ENABLED, &pref); g_assert_cmpint (err, ==, QCDM_SUCCESS); switch (pref) { case QCDM_CMD_NV_IPV6_ENABLED_OFF: msg = "disabled"; break; case QCDM_CMD_NV_IPV6_ENABLED_ON: msg = "enabled"; break; default: msg = "unknown"; break; } g_message ("%s: IPv6 preference: 0x%02X (%s)", __func__, pref, msg); qcdm_result_unref (result); } void test_com_read_hdr_rev_pref (void *f, void *data) { TestComData *d = data; gboolean success; int err = QCDM_SUCCESS; char buf[512]; guint8 pref; const char *msg; gint len; QcdmResult *result; gsize reply_len; len = qcdm_cmd_nv_get_hdr_rev_pref_new (buf, sizeof (buf)); g_assert (len > 0); /* Send the command */ success = send_command (d, buf, len); g_assert (success); /* Get a response */ reply_len = wait_reply (d, buf, sizeof (buf)); /* Parse the response into a result structure */ result = qcdm_cmd_nv_get_hdr_rev_pref_result (buf, reply_len, &err); if (!result) { if ( err == -QCDM_ERROR_NVCMD_FAILED || err == -QCDM_ERROR_RESPONSE_BAD_PARAMETER || err == -QCDM_ERROR_NV_ERROR_INACTIVE || err == -QCDM_ERROR_NV_ERROR_BAD_PARAMETER) return; g_assert_cmpint (err, ==, QCDM_SUCCESS); } g_print ("\n"); err = qcdm_result_get_u8 (result, QCDM_CMD_NV_GET_HDR_REV_PREF_ITEM_REV_PREF, &pref); g_assert_cmpint (err, ==, QCDM_SUCCESS); switch (pref) { case QCDM_CMD_NV_HDR_REV_PREF_ITEM_REV_PREF_0: msg = "rev0"; break; case QCDM_CMD_NV_HDR_REV_PREF_ITEM_REV_PREF_A: msg = "revA"; break; case QCDM_CMD_NV_HDR_REV_PREF_ITEM_REV_PREF_EHRPD: msg = "eHRPD"; break; default: msg = "unknown"; break; } g_message ("%s: HDR rev preference: 0x%02X (%s)", __func__, pref, msg); qcdm_result_unref (result); } void test_com_status (void *f, void *data) { TestComData *d = data; gboolean success; int err = QCDM_SUCCESS; char buf[100]; const char *str, *detail; gint len; QcdmResult *result; gsize reply_len; guint32 n32; guint8 n8; len = qcdm_cmd_cdma_status_new (buf, sizeof (buf)); g_assert (len == 4); /* Send the command */ success = send_command (d, buf, len); g_assert (success); /* Get a response */ reply_len = wait_reply (d, buf, sizeof (buf)); /* Parse the response into a result structure */ result = qcdm_cmd_cdma_status_result (buf, reply_len, &err); if (!result) { /* WCDMA/GSM devices don't implement this command */ g_assert_cmpint (err, ==, -QCDM_ERROR_RESPONSE_BAD_COMMAND); return; } g_assert (result); g_print ("\n"); str = NULL; qcdm_result_get_string (result, QCDM_CMD_CDMA_STATUS_ITEM_ESN, &str); g_message ("%s: ESN: %s", __func__, str); n32 = 0; detail = NULL; qcdm_result_get_u32 (result, QCDM_CMD_CDMA_STATUS_ITEM_RF_MODE, &n32); switch (n32) { case QCDM_CMD_CDMA_STATUS_RF_MODE_ANALOG: detail = "analog"; break; case QCDM_CMD_CDMA_STATUS_RF_MODE_CDMA_CELLULAR: detail = "CDMA cellular"; break; case QCDM_CMD_CDMA_STATUS_RF_MODE_CDMA_PCS: detail = "CDMA PCS"; break; case QCDM_CMD_CDMA_STATUS_RF_MODE_SLEEP: detail = "sleep"; break; case QCDM_CMD_CDMA_STATUS_RF_MODE_GPS: detail = "GPS"; break; case QCDM_CMD_CDMA_STATUS_RF_MODE_HDR: detail = "HDR"; break; default: detail = "unknown"; break; } g_message ("%s: CDMA RF Mode: %u (%s)", __func__, n32, detail); n32 = 0; detail = NULL; qcdm_result_get_u32 (result, QCDM_CMD_CDMA_STATUS_ITEM_RX_STATE, &n32); switch (n32) { case QCDM_CMD_CDMA_STATUS_RX_STATE_ENTERING_CDMA: detail = "entering CDMA"; break; case QCDM_CMD_CDMA_STATUS_RX_STATE_SYNC_CHANNEL: detail = "sync channel"; break; case QCDM_CMD_CDMA_STATUS_RX_STATE_PAGING_CHANNEL: detail = "paging channel"; break; case QCDM_CMD_CDMA_STATUS_RX_STATE_TRAFFIC_CHANNEL_INIT: detail = "traffic channel init"; break; case QCDM_CMD_CDMA_STATUS_RX_STATE_TRAFFIC_CHANNEL: detail = "traffic channel"; break; case QCDM_CMD_CDMA_STATUS_RX_STATE_EXITING_CDMA: detail = "exiting CDMA"; break; default: detail = "unknown"; break; } g_message ("%s: CDMA RX State: %u (%s)", __func__, n32, detail); n32 = 0; qcdm_result_get_u32 (result, QCDM_CMD_CDMA_STATUS_ITEM_ENTRY_REASON, &n32); g_message ("%s: Entry Reason: %u", __func__, n32); n32 = 0; qcdm_result_get_u32 (result, QCDM_CMD_CDMA_STATUS_ITEM_CURRENT_CHANNEL, &n32); g_message ("%s: Current Channel: %u", __func__, n32); n8 = 0; qcdm_result_get_u8 (result, QCDM_CMD_CDMA_STATUS_ITEM_CODE_CHANNEL, &n8); g_message ("%s: Code Channel: %u", __func__, n8); n32 = 0; qcdm_result_get_u32 (result, QCDM_CMD_CDMA_STATUS_ITEM_PILOT_BASE, &n32); g_message ("%s: Pilot Base: %u", __func__, n32); n32 = 0; qcdm_result_get_u32 (result, QCDM_CMD_CDMA_STATUS_ITEM_SID, &n32); g_message ("%s: CDMA System ID: %u", __func__, n32); n32 = 0; qcdm_result_get_u32 (result, QCDM_CMD_CDMA_STATUS_ITEM_NID, &n32); g_message ("%s: CDMA Network ID: %u", __func__, n32); qcdm_result_unref (result); } void test_com_sw_version (void *f, void *data) { TestComData *d = data; gboolean success; int err = QCDM_SUCCESS; char buf[100]; gint len; QcdmResult *result; gsize reply_len; const char *str; len = qcdm_cmd_sw_version_new (buf, sizeof (buf)); g_assert (len == 4); /* Send the command */ success = send_command (d, buf, len); g_assert (success); /* Get a response */ reply_len = wait_reply (d, buf, sizeof (buf)); /* Parse the response into a result structure */ result = qcdm_cmd_sw_version_result (buf, reply_len, &err); if (!result) { g_assert_cmpint (err, ==, -QCDM_ERROR_RESPONSE_BAD_COMMAND); return; } str = NULL; qcdm_result_get_string (result, QCDM_CMD_SW_VERSION_ITEM_VERSION, &str); g_message ("%s: SW Version: %s", __func__, str); str = NULL; qcdm_result_get_string (result, QCDM_CMD_SW_VERSION_ITEM_COMP_DATE, &str); g_message ("%s: Compiled Date: %s", __func__, str); str = NULL; qcdm_result_get_string (result, QCDM_CMD_SW_VERSION_ITEM_COMP_TIME, &str); g_message ("%s: Compiled Time: %s", __func__, str); qcdm_result_unref (result); } void test_com_status_snapshot (void *f, void *data) { TestComData *d = data; gboolean success; int err = QCDM_SUCCESS; char buf[100]; gint len; QcdmResult *result; gsize reply_len; guint8 n8; guint32 n32; len = qcdm_cmd_status_snapshot_new (buf, sizeof (buf)); g_assert (len == 4); /* Send the command */ success = send_command (d, buf, len); g_assert (success); /* Get a response */ reply_len = wait_reply (d, buf, sizeof (buf)); /* Parse the response into a result structure */ result = qcdm_cmd_status_snapshot_result (buf, reply_len, &err); if (!result) { /* WCDMA/GSM devices don't implement this command */ g_assert_cmpint (err, ==, -QCDM_ERROR_RESPONSE_BAD_COMMAND); return; } g_assert (result); g_print ("\n"); n32 = 0; qcdm_result_get_u32 (result, QCDM_CMD_STATUS_SNAPSHOT_ITEM_HOME_MCC, &n32); g_message ("%s: Home MCC: %d", __func__, n32); n8 = 0; qcdm_result_get_u8 (result, QCDM_CMD_STATUS_SNAPSHOT_ITEM_BAND_CLASS, &n8); g_message ("%s: Band Class: %s", __func__, band_class_to_string (n8)); n8 = 0; qcdm_result_get_u8 (result, QCDM_CMD_STATUS_SNAPSHOT_ITEM_BASE_STATION_PREV, &n8); g_message ("%s: Base station P_REV: %s", __func__, prev_to_string (n8)); n8 = 0; qcdm_result_get_u8 (result, QCDM_CMD_STATUS_SNAPSHOT_ITEM_MOBILE_PREV, &n8); g_message ("%s: Mobile P_REV: %s", __func__, prev_to_string (n8)); n8 = 0; qcdm_result_get_u8 (result, QCDM_CMD_STATUS_SNAPSHOT_ITEM_PREV_IN_USE, &n8); g_message ("%s: P_REV in-use: %s", __func__, prev_to_string (n8)); n8 = 0; qcdm_result_get_u8 (result, QCDM_CMD_STATUS_SNAPSHOT_ITEM_STATE, &n8); g_message ("%s: State: %d (%s)", __func__, n8, status_snapshot_state_to_string (n8)); qcdm_result_unref (result); } void test_com_pilot_sets (void *f, void *data) { TestComData *d = data; gboolean success; int err = QCDM_SUCCESS; char buf[256]; gint len; QcdmResult *result; gsize reply_len; guint32 num, i; len = qcdm_cmd_pilot_sets_new (buf, sizeof (buf)); g_assert (len == 4); /* Send the command */ success = send_command (d, buf, len); g_assert (success); /* Get a response */ reply_len = wait_reply (d, buf, sizeof (buf)); /* Parse the response into a result structure */ result = qcdm_cmd_pilot_sets_result (buf, reply_len, &err); if (!result) { /* WCDMA/GSM devices don't implement this command */ g_assert_cmpint (err, ==, -QCDM_ERROR_RESPONSE_BAD_COMMAND); return; } g_assert (result); num = 0; qcdm_cmd_pilot_sets_result_get_num (result, QCDM_CMD_PILOT_SETS_TYPE_ACTIVE, &num); g_message ("%s: Active Pilots: %d", __func__, num); for (i = 0; i < num; i++) { guint32 pn_offset = 0, ecio = 0; float db = 0; qcdm_cmd_pilot_sets_result_get_pilot (result, QCDM_CMD_PILOT_SETS_TYPE_ACTIVE, i, &pn_offset, &ecio, &db); g_message (" %d: PN offset %d", i, pn_offset); g_message (" EC/IO %d (%.1lf dB)", ecio, (double)db); } num = 0; qcdm_cmd_pilot_sets_result_get_num (result, QCDM_CMD_PILOT_SETS_TYPE_CANDIDATE, &num); g_message ("%s: Candidate Pilots: %d", __func__, num); num = 0; qcdm_cmd_pilot_sets_result_get_num (result, QCDM_CMD_PILOT_SETS_TYPE_NEIGHBOR, &num); g_message ("%s: Neighbor Pilots: %d", __func__, num); qcdm_result_unref (result); } static const char * operating_mode_to_string (guint32 mode) { switch (mode) { case QCDM_CMD_CM_SUBSYS_STATE_INFO_OPERATING_MODE_POWER_OFF: return "powering off"; case QCDM_CMD_CM_SUBSYS_STATE_INFO_OPERATING_MODE_FIELD_TEST_MODE: return "field test mode"; case QCDM_CMD_CM_SUBSYS_STATE_INFO_OPERATING_MODE_OFFLINE: return "offline"; case QCDM_CMD_CM_SUBSYS_STATE_INFO_OPERATING_MODE_OFFLINE_AMPS: return "online (AMPS)"; case QCDM_CMD_CM_SUBSYS_STATE_INFO_OPERATING_MODE_OFFLINE_CDMA: return "online (CDMA)"; case QCDM_CMD_CM_SUBSYS_STATE_INFO_OPERATING_MODE_ONLINE: return "online"; case QCDM_CMD_CM_SUBSYS_STATE_INFO_OPERATING_MODE_LOW_POWER_MODE: return "low power mode"; case QCDM_CMD_CM_SUBSYS_STATE_INFO_OPERATING_MODE_RESET: return "reset"; default: return "unknown"; } } void test_com_cm_subsys_state_info (void *f, void *data) { TestComData *d = data; gboolean success; int err = QCDM_SUCCESS; char buf[100]; gint len; QcdmResult *result; gsize reply_len; guint32 n32; const char *detail; len = qcdm_cmd_cm_subsys_state_info_new (buf, sizeof (buf)); g_assert (len == 7); /* Send the command */ success = send_command (d, buf, len); g_assert (success); /* Get a response */ reply_len = wait_reply (d, buf, sizeof (buf)); /* Parse the response into a result structure */ result = qcdm_cmd_cm_subsys_state_info_result (buf, reply_len, &err); g_assert (result); g_print ("\n"); n32 = 0; qcdm_result_get_u32 (result, QCDM_CMD_CM_SUBSYS_STATE_INFO_ITEM_CALL_STATE, &n32); g_message ("%s: Call State: %u (%s)", __func__, n32, cm_call_state_to_string (n32)); n32 = 0; detail = NULL; qcdm_result_get_u32 (result, QCDM_CMD_CM_SUBSYS_STATE_INFO_ITEM_OPERATING_MODE, &n32); g_message ("%s: Operating Mode: %u (%s)", __func__, n32, operating_mode_to_string (n32)); n32 = 0; detail = NULL; qcdm_result_get_u32 (result, QCDM_CMD_CM_SUBSYS_STATE_INFO_ITEM_SYSTEM_MODE, &n32); g_message ("%s: System Mode: %u (%s)", __func__, n32, cm_system_mode_to_string (n32)); n32 = 0; qcdm_result_get_u32 (result, QCDM_CMD_CM_SUBSYS_STATE_INFO_ITEM_MODE_PREF, &n32); switch (n32) { case QCDM_CMD_CM_SUBSYS_STATE_INFO_MODE_PREF_AMPS_ONLY: detail = "AMPS only"; break; case QCDM_CMD_CM_SUBSYS_STATE_INFO_MODE_PREF_DIGITAL_ONLY: detail = "digital only"; break; case QCDM_CMD_CM_SUBSYS_STATE_INFO_MODE_PREF_AUTO: detail = "automatic"; break; case QCDM_CMD_CM_SUBSYS_STATE_INFO_MODE_PREF_EMERGENCY: detail = "emergency"; break; case QCDM_CMD_CM_SUBSYS_STATE_INFO_MODE_PREF_1X_ONLY: detail = "1X only"; break; case QCDM_CMD_CM_SUBSYS_STATE_INFO_MODE_PREF_HDR_ONLY: detail = "HDR only"; break; case QCDM_CMD_CM_SUBSYS_STATE_INFO_MODE_PREF_1X_AMPS_ONLY: detail = "1x/AMPS only"; break; case QCDM_CMD_CM_SUBSYS_STATE_INFO_MODE_PREF_GPS_ONLY: detail = "GPS only"; break; case QCDM_CMD_CM_SUBSYS_STATE_INFO_MODE_PREF_GSM_ONLY: detail = "GSM only"; break; case QCDM_CMD_CM_SUBSYS_STATE_INFO_MODE_PREF_WCDMA_ONLY: detail = "WCDMA only"; break; case QCDM_CMD_CM_SUBSYS_STATE_INFO_MODE_PREF_LTE_ONLY: detail = "LTE only"; break; case QCDM_CMD_CM_SUBSYS_STATE_INFO_MODE_PREF_GSM_WCDMA_LTE_ONLY: detail = "GSM/WCDMA/LTE only"; break; default: detail = "unknown"; break; } g_message ("%s: Mode Preference: 0x%02X (%s)", __func__, n32 & 0xFF, detail); n32 = 0; qcdm_result_get_u32 (result, QCDM_CMD_CM_SUBSYS_STATE_INFO_ITEM_BAND_PREF, &n32); g_message ("%s: Band Preference: %u", __func__, n32); n32 = 0; qcdm_result_get_u32 (result, QCDM_CMD_CM_SUBSYS_STATE_INFO_ITEM_ROAM_PREF, &n32); switch (n32) { case QCDM_CMD_CM_SUBSYS_STATE_INFO_ROAM_PREF_HOME_ONLY: detail = "home only"; break; case QCDM_CMD_CM_SUBSYS_STATE_INFO_ROAM_PREF_ROAM_ONLY: detail = "roaming only"; break; case QCDM_CMD_CM_SUBSYS_STATE_INFO_ROAM_PREF_AUTO: detail = "automatic"; break; default: g_assert_not_reached (); } g_message ("%s: Roam Preference: 0x%02X (%s)", __func__, n32 & 0xFF, detail); n32 = 0; qcdm_result_get_u32 (result, QCDM_CMD_CM_SUBSYS_STATE_INFO_ITEM_SERVICE_DOMAIN_PREF, &n32); g_message ("%s: Service Domain Preference: %u", __func__, n32); n32 = 0; qcdm_result_get_u32 (result, QCDM_CMD_CM_SUBSYS_STATE_INFO_ITEM_ACQ_ORDER_PREF, &n32); g_message ("%s: Acquisition Order Preference: %u", __func__, n32); n32 = 0; qcdm_result_get_u32 (result, QCDM_CMD_CM_SUBSYS_STATE_INFO_ITEM_HYBRID_PREF, &n32); g_message ("%s: Hybrid Preference: %u", __func__, n32); n32 = 0; qcdm_result_get_u32 (result, QCDM_CMD_CM_SUBSYS_STATE_INFO_ITEM_NETWORK_SELECTION_PREF, &n32); g_message ("%s: Network Selection Preference: %u", __func__, n32); qcdm_result_unref (result); } void test_com_hdr_subsys_state_info (void *f, void *data) { TestComData *d = data; gboolean success; int err = QCDM_SUCCESS; char buf[100]; gint len; QcdmResult *result; gsize reply_len; guint8 num; const char *detail; len = qcdm_cmd_hdr_subsys_state_info_new (buf, sizeof (buf)); g_assert (len == 7); /* Send the command */ success = send_command (d, buf, len); g_assert (success); /* Get a response */ reply_len = wait_reply (d, buf, sizeof (buf)); g_print ("\n"); /* Parse the response into a result structure */ result = qcdm_cmd_hdr_subsys_state_info_result (buf, reply_len, &err); if (!result) { /* 1x-only devices won't implement the HDR subsystem of course */ g_assert_cmpint (err, ==, -QCDM_ERROR_RESPONSE_BAD_COMMAND); g_message ("%s: device does not implement the HDR subsystem", __func__); return; } g_assert (result); num = 0; detail = NULL; qcdm_result_get_u8 (result, QCDM_CMD_HDR_SUBSYS_STATE_INFO_ITEM_AT_STATE, &num); switch (num) { case QCDM_CMD_HDR_SUBSYS_STATE_INFO_AT_STATE_INACTIVE: detail = "inactive"; break; case QCDM_CMD_HDR_SUBSYS_STATE_INFO_AT_STATE_ACQUISITION: detail = "acquisition"; break; case QCDM_CMD_HDR_SUBSYS_STATE_INFO_AT_STATE_SYNC: detail = "sync"; break; case QCDM_CMD_HDR_SUBSYS_STATE_INFO_AT_STATE_IDLE: detail = "idle"; break; case QCDM_CMD_HDR_SUBSYS_STATE_INFO_AT_STATE_ACCESS: detail = "access"; break; case QCDM_CMD_HDR_SUBSYS_STATE_INFO_AT_STATE_CONNECTED: detail = "connected"; break; default: detail = "unknown"; break; } g_message ("%s: AT State: %u (%s)", __func__, num, detail); num = 0; detail = NULL; qcdm_result_get_u8 (result, QCDM_CMD_HDR_SUBSYS_STATE_INFO_ITEM_SESSION_STATE, &num); switch (num) { case QCDM_CMD_HDR_SUBSYS_STATE_INFO_SESSION_STATE_CLOSED: detail = "closed"; break; case QCDM_CMD_HDR_SUBSYS_STATE_INFO_SESSION_STATE_SETUP: detail = "setup"; break; case QCDM_CMD_HDR_SUBSYS_STATE_INFO_SESSION_STATE_AT_INIT: detail = "AT init"; break; case QCDM_CMD_HDR_SUBSYS_STATE_INFO_SESSION_STATE_AN_INIT: detail = "AN init"; break; case QCDM_CMD_HDR_SUBSYS_STATE_INFO_SESSION_STATE_OPEN: detail = "open"; break; case QCDM_CMD_HDR_SUBSYS_STATE_INFO_SESSION_STATE_CLOSING: detail = "closing"; break; default: detail = "unknown"; break; } g_message ("%s: Session State: %u (%s)", __func__, num, detail); num = 0; detail = NULL; qcdm_result_get_u8 (result, QCDM_CMD_HDR_SUBSYS_STATE_INFO_ITEM_ALMP_STATE, &num); switch (num) { case QCDM_CMD_HDR_SUBSYS_STATE_INFO_ALMP_STATE_INACTIVE: detail = "inactive"; break; case QCDM_CMD_HDR_SUBSYS_STATE_INFO_ALMP_STATE_INIT: detail = "init"; break; case QCDM_CMD_HDR_SUBSYS_STATE_INFO_ALMP_STATE_IDLE: detail = "idle"; break; case QCDM_CMD_HDR_SUBSYS_STATE_INFO_ALMP_STATE_CONNECTED: detail = "connected"; break; default: detail = "unknown"; break; } g_message ("%s: ALMP State: %u (%s)", __func__, num, detail); num = 0; detail = NULL; qcdm_result_get_u8 (result, QCDM_CMD_HDR_SUBSYS_STATE_INFO_ITEM_INIT_STATE, &num); switch (num) { case QCDM_CMD_HDR_SUBSYS_STATE_INFO_INIT_STATE_INACTIVE: detail = "inactive"; break; case QCDM_CMD_HDR_SUBSYS_STATE_INFO_INIT_STATE_NET_DETERMINE: detail = "searching"; break; case QCDM_CMD_HDR_SUBSYS_STATE_INFO_INIT_STATE_ACQUISITION: detail = "acquisition"; break; case QCDM_CMD_HDR_SUBSYS_STATE_INFO_INIT_STATE_SYNC: detail = "sync"; break; default: detail = "unknown"; break; } g_message ("%s: Init State: %u (%s)", __func__, num, detail); num = 0; detail = NULL; qcdm_result_get_u8 (result, QCDM_CMD_HDR_SUBSYS_STATE_INFO_ITEM_IDLE_STATE, &num); switch (num) { case QCDM_CMD_HDR_SUBSYS_STATE_INFO_IDLE_STATE_INACTIVE: detail = "inactive"; break; case QCDM_CMD_HDR_SUBSYS_STATE_INFO_IDLE_STATE_SLEEP: detail = "sleep"; break; case QCDM_CMD_HDR_SUBSYS_STATE_INFO_IDLE_STATE_MONITOR: detail = "monitor"; break; case QCDM_CMD_HDR_SUBSYS_STATE_INFO_IDLE_STATE_SETUP: detail = "setup"; break; default: detail = "unknown"; break; } g_message ("%s: Idle State: %u (%s)", __func__, num, detail); num = 0; detail = NULL; qcdm_result_get_u8 (result, QCDM_CMD_HDR_SUBSYS_STATE_INFO_ITEM_CONNECTED_STATE, &num); switch (num) { case QCDM_CMD_HDR_SUBSYS_STATE_INFO_CONNECTED_STATE_INACTIVE: detail = "inactive"; break; case QCDM_CMD_HDR_SUBSYS_STATE_INFO_CONNECTED_STATE_OPEN: detail = "open"; break; case QCDM_CMD_HDR_SUBSYS_STATE_INFO_CONNECTED_STATE_CLOSING: detail = "closing"; break; default: detail = "unknown"; break; } g_message ("%s: Connected State: %u (%s)", __func__, num, detail); num = 0; detail = NULL; qcdm_result_get_u8 (result, QCDM_CMD_HDR_SUBSYS_STATE_INFO_ITEM_ROUTE_UPDATE_STATE, &num); switch (num) { case QCDM_CMD_HDR_SUBSYS_STATE_INFO_ROUTE_UPDATE_STATE_INACTIVE: detail = "inactive"; break; case QCDM_CMD_HDR_SUBSYS_STATE_INFO_ROUTE_UPDATE_STATE_IDLE: detail = "idle"; break; case QCDM_CMD_HDR_SUBSYS_STATE_INFO_ROUTE_UPDATE_STATE_CONNECTED: detail = "connected"; break; default: detail = "unknown"; break; } g_message ("%s: Route Update State: %u (%s)", __func__, num, detail); num = 0; detail = NULL; qcdm_result_get_u8 (result, QCDM_CMD_HDR_SUBSYS_STATE_INFO_ITEM_OVERHEAD_MSG_STATE, &num); switch (num) { case QCDM_CMD_HDR_SUBSYS_STATE_INFO_OVERHEAD_MSG_STATE_INIT: detail = "initial"; break; case QCDM_CMD_HDR_SUBSYS_STATE_INFO_OVERHEAD_MSG_STATE_INACTIVE: detail = "inactive"; break; case QCDM_CMD_HDR_SUBSYS_STATE_INFO_OVERHEAD_MSG_STATE_ACTIVE: detail = "active"; break; default: detail = "unknown"; break; } g_message ("%s: Overhead Msg State: %u (%s)", __func__, num, detail); num = 0; qcdm_result_get_u8 (result, QCDM_CMD_HDR_SUBSYS_STATE_INFO_ITEM_HDR_HYBRID_MODE, &num); g_message ("%s: HDR Hybrid Mode: %u", __func__, num); qcdm_result_unref (result); } void test_com_ext_logmask (void *f, void *data) { TestComData *d = data; gboolean success; int err = QCDM_SUCCESS; char buf[520]; gint len; QcdmResult *result; gsize reply_len; uint32_t items[] = { 0x002C, 0x002E, 0 }; guint32 maxlog = 0; /* First get # of items the device supports */ len = qcdm_cmd_ext_logmask_new (buf, sizeof (buf), NULL, 0); /* Send the command */ success = send_command (d, buf, len); g_assert (success); /* Get a response */ reply_len = wait_reply (d, buf, sizeof (buf)); g_print ("\n"); /* Parse the response into a result structure */ result = qcdm_cmd_ext_logmask_result (buf, reply_len, &err); g_assert (result); qcdm_result_get_u32 (result, QCDM_CMD_EXT_LOGMASK_ITEM_MAX_ITEMS, &maxlog); g_message ("%s: Max # Log Items: %u (0x%X)", __func__, maxlog, maxlog); qcdm_result_unref (result); /* Now enable some log items */ len = qcdm_cmd_ext_logmask_new (buf, sizeof (buf), items, (uint16_t) maxlog); /* Send the command */ success = send_command (d, buf, len); g_assert (success); /* Get a response */ reply_len = wait_reply (d, buf, sizeof (buf)); g_print ("\n"); /* Parse the response into a result structure */ result = qcdm_cmd_ext_logmask_result (buf, reply_len, &err); g_assert (result); qcdm_result_unref (result); /* Wait for a log packet */ reply_len = wait_reply (d, buf, sizeof (buf)); } void test_com_event_report (void *f, void *data) { TestComData *d = data; gboolean success; int err = QCDM_SUCCESS; char buf[520]; gint len; QcdmResult *result; gsize reply_len; guint32 i; /* Turn event reporting on */ len = qcdm_cmd_event_report_new (buf, sizeof (buf), TRUE); /* Send the command */ success = send_command (d, buf, len); g_assert (success); /* Get a response */ reply_len = wait_reply (d, buf, sizeof (buf)); g_print ("\n"); /* Parse the response into a result structure */ result = qcdm_cmd_event_report_result (buf, reply_len, &err); g_assert (result); qcdm_result_unref (result); /* Wait for a few events */ for (i = 0; i < 4; i++) reply_len = wait_reply (d, buf, sizeof (buf)); /* Turn event reporting off */ len = qcdm_cmd_event_report_new (buf, sizeof (buf), FALSE); /* Send the command */ success = send_command (d, buf, len); g_assert (success); /* Get a response */ reply_len = wait_reply (d, buf, sizeof (buf)); } void test_com_log_config (void *f, void *data) { TestComData *d = data; gboolean success; int err = QCDM_SUCCESS; char buf[520]; gint len; QcdmResult *result; gsize reply_len; uint32_t num_items = 0; const uint16_t *items = NULL, *reread_items; size_t items_len = 0, reread_len; uint32_t i; uint16_t test_items[] = { 0x1004, 0x1005, 0x1006, 0x1007, 0x1008, 0x102C, 0x102E, 0 }; /* Get existing mask for CDMA/EVDO equip ID */ len = qcdm_cmd_log_config_get_mask_new (buf, sizeof (buf), 0x01); g_assert (len); /* Send the command */ success = send_command (d, buf, len); g_assert (success); /* Get a response */ reply_len = wait_reply (d, buf, sizeof (buf)); g_print ("\n"); /* Parse the response into a result structure */ result = qcdm_cmd_log_config_get_mask_result (buf, reply_len, &err); g_assert (result); qcdm_result_get_u32 (result, QCDM_CMD_LOG_CONFIG_MASK_ITEM_NUM_ITEMS, &num_items); g_message ("%s: Num Log Items: %u (0x%X)", __func__, num_items, num_items); qcdm_result_get_u16_array (result, QCDM_CMD_LOG_CONFIG_MASK_ITEM_ITEMS, &items, &items_len); for (i = 0; i < items_len; i++) g_message ("%s: Enabled: 0x%04x", __func__, items[i]); qcdm_result_unref (result); /* Turn on some log messages */ len = qcdm_cmd_log_config_set_mask_new (buf, sizeof (buf), 0x01, test_items); g_assert (len); /* Send the command */ success = send_command (d, buf, len); g_assert (success); /* Get a response */ reply_len = wait_reply (d, buf, sizeof (buf)); g_print ("\n"); /* Parse the response into a result structure */ result = qcdm_cmd_log_config_set_mask_result (buf, reply_len, &err); g_assert (result); qcdm_result_unref (result); /* Get the mask again so we can compare it to what we just set */ len = qcdm_cmd_log_config_get_mask_new (buf, sizeof (buf), 0x01); g_assert (len); /* Send the command */ success = send_command (d, buf, len); g_assert (success); /* Get a response */ reply_len = wait_reply (d, buf, sizeof (buf)); g_print ("\n"); /* Parse the response into a result structure */ result = qcdm_cmd_log_config_get_mask_result (buf, reply_len, &err); g_assert (result); qcdm_result_get_u16_array (result, QCDM_CMD_LOG_CONFIG_MASK_ITEM_ITEMS, &reread_items, &reread_len); g_assert_cmpint (reread_len, ==, (sizeof (test_items) - 1) / sizeof (test_items[0])); g_assert (memcmp (reread_items, test_items, reread_len * sizeof (test_items[0])) == 0); qcdm_result_unref (result); /* Wait for a few log packets */ for (i = 0; i < 5; i++) reply_len = wait_reply (d, buf, sizeof (buf)); } void test_com_zte_subsys_status (void *f, void *data) { TestComData *d = data; gboolean success; int err = QCDM_SUCCESS; char buf[100]; gint len; QcdmResult *result; gsize reply_len; guint8 ind = 0; len = qcdm_cmd_zte_subsys_status_new (buf, sizeof (buf)); g_assert (len == 7); /* Send the command */ success = send_command (d, buf, len); g_assert (success); /* Get a response */ reply_len = wait_reply (d, buf, sizeof (buf)); g_print ("\n"); /* Parse the response into a result structure */ result = qcdm_cmd_zte_subsys_status_result (buf, reply_len, &err); if (!result) { /* Obviously not all devices implement this command */ g_assert_cmpint (err, ==, -QCDM_ERROR_RESPONSE_BAD_COMMAND); g_message ("%s: device does not implement the ZTE subsystem", __func__); return; } g_assert (result); qcdm_result_get_u8 (result, QCDM_CMD_ZTE_SUBSYS_STATUS_ITEM_SIGNAL_INDICATOR, &ind); g_message ("%s: Signal Indicator: %d", __func__, ind); qcdm_result_unref (result); } void test_com_nw_subsys_modem_snapshot_cdma (void *f, void *data) { TestComData *d = data; gboolean success; int err = QCDM_SUCCESS; char buf[200]; gint len; QcdmResult *result; gsize reply_len; guint8 num8 = 0; guint32 num32 = 0; len = qcdm_cmd_nw_subsys_modem_snapshot_cdma_new (buf, sizeof (buf), QCDM_NW_CHIPSET_6800); g_assert (len == 12); /* Send the command */ success = send_command (d, buf, len); g_assert (success); /* Get a response */ reply_len = wait_reply (d, buf, sizeof (buf)); g_print ("\n"); /* Parse the response into a result structure */ result = qcdm_cmd_nw_subsys_modem_snapshot_cdma_result (buf, reply_len, &err); if (!result) { /* Obviously not all devices implement this command */ if ( err == -QCDM_ERROR_RESPONSE_BAD_COMMAND || err == -QCDM_ERROR_RESPONSE_BAD_LENGTH) return; g_assert_cmpint (err, ==, QCDM_SUCCESS); } g_assert (result); qcdm_result_get_u32 (result, QCDM_CMD_NW_SUBSYS_MODEM_SNAPSHOT_CDMA_ITEM_RSSI, &num32); g_message ("%s: RSSI: %d", __func__, num32); qcdm_result_get_u8 (result, QCDM_CMD_NW_SUBSYS_MODEM_SNAPSHOT_CDMA_ITEM_PREV, &num8); g_message ("%s: P_REV: %s", __func__, prev_to_string (num8)); qcdm_result_get_u8 (result, QCDM_CMD_NW_SUBSYS_MODEM_SNAPSHOT_CDMA_ITEM_BAND_CLASS, &num8); g_message ("%s: Band Class: %s", __func__, band_class_to_string (num8)); qcdm_result_get_u8 (result, QCDM_CMD_NW_SUBSYS_MODEM_SNAPSHOT_CDMA_ITEM_ERI, &num8); g_message ("%s: ERI: %d", __func__, num8); qcdm_result_get_u8 (result, QCDM_CMD_NW_SUBSYS_MODEM_SNAPSHOT_CDMA_ITEM_HDR_REV, &num8); g_message ("%s: HDR Revision: %s", __func__, hdr_rev_to_string (num8)); qcdm_result_unref (result); } void test_com_nw_subsys_eri (void *f, void *data) { TestComData *d = data; gboolean success; int err = QCDM_SUCCESS; char buf[200]; gint len; QcdmResult *result; gsize reply_len; guint8 num8 = 0; const char *str = NULL; len = qcdm_cmd_nw_subsys_eri_new (buf, sizeof (buf), QCDM_NW_CHIPSET_6800); g_assert_cmpint (len, ==, 7); /* Send the command */ success = send_command (d, buf, len); g_assert (success); /* Get a response */ reply_len = wait_reply (d, buf, sizeof (buf)); g_print ("\n"); /* Parse the response into a result structure */ result = qcdm_cmd_nw_subsys_eri_result (buf, reply_len, &err); if (!result) { /* Obviously not all devices implement this command */ if ( err == -QCDM_ERROR_RESPONSE_BAD_COMMAND || err == -QCDM_ERROR_RESPONSE_BAD_LENGTH) return; g_assert_cmpint (err, ==, QCDM_SUCCESS); } g_assert (result); qcdm_result_get_u8 (result, QCDM_CMD_NW_SUBSYS_ERI_ITEM_ROAM, &num8); g_message ("%s: Roam: %d", __func__, num8); qcdm_result_get_u8 (result, QCDM_CMD_NW_SUBSYS_ERI_ITEM_INDICATOR_ID, &num8); g_message ("%s: Indicator ID: %d", __func__, num8); qcdm_result_get_u8 (result, QCDM_CMD_NW_SUBSYS_ERI_ITEM_ICON_ID, &num8); g_message ("%s: Icon ID: %d", __func__, num8); qcdm_result_get_u8 (result, QCDM_CMD_NW_SUBSYS_ERI_ITEM_ICON_MODE, &num8); g_message ("%s: Icon Mode: %d", __func__, num8); qcdm_result_get_u8 (result, QCDM_CMD_NW_SUBSYS_ERI_ITEM_CALL_PROMPT_ID, &num8); g_message ("%s: Call Prompt ID: %d", __func__, num8); qcdm_result_get_u8 (result, QCDM_CMD_NW_SUBSYS_ERI_ITEM_ALERT_ID, &num8); g_message ("%s: Alert ID: %d", __func__, num8); qcdm_result_get_string (result, QCDM_CMD_NW_SUBSYS_ERI_ITEM_TEXT, &str); g_message ("%s: Banner: '%s'", __func__, str); qcdm_result_unref (result); } void test_com_wcdma_subsys_state_info (void *f, void *data) { TestComData *d = data; gboolean success; int err = QCDM_SUCCESS; char buf[200]; gint len; QcdmResult *result; gsize reply_len; guint8 num8 = 0; const char *str; len = qcdm_cmd_wcdma_subsys_state_info_new (buf, sizeof (buf)); g_assert (len == 7); /* Send the command */ success = send_command (d, buf, len); g_assert (success); /* Get a response */ reply_len = wait_reply (d, buf, sizeof (buf)); g_print ("\n"); /* Parse the response into a result structure */ result = qcdm_cmd_wcdma_subsys_state_info_result (buf, reply_len, &err); if (!result) { /* Obviously not all devices implement this command */ g_assert_cmpint (err, ==, -QCDM_ERROR_RESPONSE_BAD_COMMAND); return; } g_assert (result); str = NULL; qcdm_result_get_string (result, QCDM_CMD_WCDMA_SUBSYS_STATE_INFO_ITEM_IMEI, &str); g_message ("%s: IMEI: %s", __func__, str); str = NULL; qcdm_result_get_string (result, QCDM_CMD_WCDMA_SUBSYS_STATE_INFO_ITEM_IMSI, &str); g_message ("%s: IMSI: %s", __func__, str); str = "unknown"; qcdm_result_get_u8 (result, QCDM_CMD_WCDMA_SUBSYS_STATE_INFO_ITEM_L1_STATE, &num8); switch (num8) { case QCDM_WCDMA_L1_STATE_IDLE: str = "Idle"; break; case QCDM_WCDMA_L1_STATE_FS: str = "FS"; break; case QCDM_WCDMA_L1_STATE_ACQ: str = "ACQ"; break; case QCDM_WCDMA_L1_STATE_BCH: str = "BCH"; break; case QCDM_WCDMA_L1_STATE_PCH: str = "PCH"; break; case QCDM_WCDMA_L1_STATE_FACH: str = "FACH"; break; case QCDM_WCDMA_L1_STATE_DCH: str = "DCH"; break; case QCDM_WCDMA_L1_STATE_DEACTIVATE: str = "Deactivated"; break; case QCDM_WCDMA_L1_STATE_PCH_SLEEP: str = "PCH Sleep"; break; case QCDM_WCDMA_L1_STATE_DEEP_SLEEP: str = "Deep Sleep"; break; case QCDM_WCDMA_L1_STATE_STOPPED: str = "Stopped"; break; case QCDM_WCDMA_L1_STATE_SUSPENDED: str = "Suspended"; break; case QCDM_WCDMA_L1_STATE_PCH_BPLMN: str = "PCH BPLMN"; break; case QCDM_WCDMA_L1_STATE_WAIT_TRM_STOP: str = "Wait TRM Stop"; break; default: break; } g_message ("%s: L1 state: %d (%s)", __func__, num8, str); qcdm_result_unref (result); } void test_com_gsm_subsys_state_info (void *f, void *data) { TestComData *d = data; gboolean success; int err = QCDM_SUCCESS; char buf[200]; gint len; QcdmResult *result; gsize reply_len; const char *str; uint32_t num; uint8_t u8; len = qcdm_cmd_gsm_subsys_state_info_new (buf, sizeof (buf)); g_assert (len == 7); /* Send the command */ success = send_command (d, buf, len); g_assert (success); /* Get a response */ reply_len = wait_reply (d, buf, sizeof (buf)); g_print ("\n"); /* Parse the response into a result structure */ result = qcdm_cmd_gsm_subsys_state_info_result (buf, reply_len, &err); if (!result) { /* Obviously not all devices implement this command */ g_assert_cmpint (err, ==, -QCDM_ERROR_RESPONSE_BAD_COMMAND); return; } g_assert (result); str = NULL; qcdm_result_get_string (result, QCDM_CMD_GSM_SUBSYS_STATE_INFO_ITEM_IMEI, &str); g_message ("%s: IMEI: %s", __func__, str); str = NULL; qcdm_result_get_string (result, QCDM_CMD_GSM_SUBSYS_STATE_INFO_ITEM_IMSI, &str); g_message ("%s: IMSI: %s", __func__, str); num = 0; qcdm_result_get_u32 (result, QCDM_CMD_GSM_SUBSYS_STATE_INFO_ITEM_LAI_MCC, &num); g_message ("%s: MCC: %d", __func__, num); num = 0; qcdm_result_get_u32 (result, QCDM_CMD_GSM_SUBSYS_STATE_INFO_ITEM_LAI_MNC, &num); g_message ("%s: MNC: %d", __func__, num); num = 0; qcdm_result_get_u32 (result, QCDM_CMD_GSM_SUBSYS_STATE_INFO_ITEM_LAI_LAC, &num); g_message ("%s: LAC: 0x%04X", __func__, num); num = 0; qcdm_result_get_u32 (result, QCDM_CMD_GSM_SUBSYS_STATE_INFO_ITEM_CELLID, &num); g_message ("%s: Cell ID: 0x%04X", __func__, num); u8 = 0; qcdm_result_get_u8 (result, QCDM_CMD_GSM_SUBSYS_STATE_INFO_ITEM_CM_CALL_STATE, &u8); g_message ("%s: CM Call State: %d (%s)", __func__, u8, cm_call_state_to_string (u8)); u8 = 0; qcdm_result_get_u8 (result, QCDM_CMD_GSM_SUBSYS_STATE_INFO_ITEM_CM_OP_MODE, &u8); g_message ("%s: CM Opmode: %d (%s)", __func__, u8, operating_mode_to_string (u8)); u8 = 0; qcdm_result_get_u8 (result, QCDM_CMD_GSM_SUBSYS_STATE_INFO_ITEM_CM_SYS_MODE, &u8); g_message ("%s: CM Sysmode: %d (%s)", __func__, u8, cm_system_mode_to_string (u8)); qcdm_result_unref (result); } ModemManager-1.23.4-dev/libqcdm/tests/test-qcdm-com.h000066400000000000000000000040521456466623000223310ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * Copyright (C) 2010 Red Hat, Inc. * * This program is free software: you can redistribute it and/or * modify it under the terms of version 2 of the GNU General Public * License as published by the Free Software Foundation * * 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, see . */ #ifndef TEST_QCDM_COM_H #define TEST_QCDM_COM_H gpointer test_com_setup (const char *port); void test_com_teardown (gpointer d); void test_com_port_init (void *f, void *data); void test_com_version_info (void *f, void *data); void test_com_esn (void *f, void *data); void test_com_mdn (void *f, void *data); void test_com_read_roam_pref (void *f, void *data); void test_com_read_mode_pref (void *f, void *data); void test_com_read_hybrid_pref (void *f, void *data); void test_com_read_ipv6_enabled (void *f, void *data); void test_com_read_hdr_rev_pref (void *f, void *data); void test_com_status (void *f, void *data); void test_com_sw_version (void *f, void *data); void test_com_status_snapshot (void *f, void *data); void test_com_pilot_sets (void *f, void *data); void test_com_cm_subsys_state_info (void *f, void *data); void test_com_hdr_subsys_state_info (void *f, void *data); void test_com_ext_logmask (void *f, void *data); void test_com_event_report (void *f, void *data); void test_com_log_config (void *f, void *data); void test_com_zte_subsys_status (void *f, void *data); void test_com_nw_subsys_modem_snapshot_cdma (void *f, void *data); void test_com_nw_subsys_eri (void *f, void *data); void test_com_wcdma_subsys_state_info (void *f, void *data); void test_com_gsm_subsys_state_info (void *f, void *data); #endif /* TEST_QCDM_COM_H */ ModemManager-1.23.4-dev/libqcdm/tests/test-qcdm-crc.c000066400000000000000000000046301456466623000223170ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * Copyright (C) 2010 Red Hat, Inc. * * This program is free software: you can redistribute it and/or * modify it under the terms of version 2 of the GNU General Public * License as published by the Free Software Foundation * * 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, see . */ #include #include #include "test-qcdm-crc.h" #include "utils.h" void test_crc16_2 (void *f, void *data) { static const char buf[] = { 0x26, 0xb2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; guint16 crc; guint16 expected = 0x6D69; /* CRC check */ crc = dm_crc16 (buf, sizeof (buf)); g_assert (crc == expected); } void test_crc16_1 (void *f, void *data) { static const char buf[] = { 0x4b, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x3f, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff }; guint16 crc; guint16 expected = 0x097A; /* CRC check */ crc = dm_crc16 (buf, sizeof (buf)); g_assert (crc == expected); } ModemManager-1.23.4-dev/libqcdm/tests/test-qcdm-crc.h000066400000000000000000000015471456466623000223300ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * Copyright (C) 2010 Red Hat, Inc. * * This program is free software: you can redistribute it and/or * modify it under the terms of version 2 of the GNU General Public * License as published by the Free Software Foundation * * 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, see . */ #ifndef TEST_QCDM_CRC_H #define TEST_QCDM_CRC_H void test_crc16_2 (void *f, void *data); void test_crc16_1 (void *f, void *data); #endif /* TEST_QCDM_CRC_H */ ModemManager-1.23.4-dev/libqcdm/tests/test-qcdm-escaping.c000066400000000000000000000131741456466623000233440ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * Copyright (C) 2010 Red Hat, Inc. * * This program is free software: you can redistribute it and/or * modify it under the terms of version 2 of the GNU General Public * License as published by the Free Software Foundation * * 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, see . */ #include #include #include "test-qcdm-escaping.h" #include "utils.h" static const char data1[] = { 0x70, 0x68, 0x6f, 0x6e, 0x65, 0x66, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x0a, 0x6e, 0x6f, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x74, 0x6f, 0x70, 0x68, 0x6f, 0x6e, 0x65, 0x0a, 0x70, 0x68, 0x6f, 0x6e, 0x7e, 0x7e, 0x7e, 0x7d, 0x7d, 0x7e, 0x7d, 0x7e, 0x7d, 0x7e, 0x6e, 0x6b, 0x72, 0x65, 0x73, 0x65, 0x72, 0x76, 0x65, 0x64, 0x0a, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x0a, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x6e, 0x6f, 0x74, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x0a, 0x70, 0x68, 0x73, 0x69, 0x6d, 0x70, 0x69, 0x6e, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x0a, 0x70, 0x68, 0x66, 0x73, 0x69, 0x6d, 0x70, 0x69, 0x6e, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x0a, 0x70, 0x68, 0x66, 0x73, 0x69, 0x6d, 0x70, 0x75, 0x6b, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x0a, 0x73, 0x69, 0x6d, 0x6e, 0x6f, 0x74, 0x69, 0x6e, 0x73, 0x65, 0x72, 0x74, 0x65, 0x64, 0x0a, 0x73, 0x69, 0x6d, 0x70, 0x69, 0x6e, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x0a, 0x73, 0x69, 0x6d, 0x70, 0x75, 0x6b, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x0a, 0x73, 0x69, 0x6d, 0x66, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x0a, 0x73, 0x69, 0x6d, 0x62, 0x75, 0x73, 0x79, 0x0a, 0x73, 0x69, 0x6d, 0x77, 0x72, 0x6f, 0x6e, 0x67, 0x0a, 0x69, 0x6e, 0x63, 0x6f, 0x72, 0x72, 0x65, 0x63, 0x74, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x0a, 0x73, 0x69, 0x6d, 0x70, 0x69, 0x6e, 0x32, 0x72, 0x65, 0x71, 0x75, 0x69 }; static const char expected1[] = { 0x70, 0x68, 0x6f, 0x6e, 0x65, 0x66, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x0a, 0x6e, 0x6f, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x74, 0x6f, 0x70, 0x68, 0x6f, 0x6e, 0x65, 0x0a, 0x70, 0x68, 0x6f, 0x6e, 0x7d, 0x5e, 0x7d, 0x5e, 0x7d, 0x5e, 0x7d, 0x5d, 0x7d, 0x5d, 0x7d, 0x5e, 0x7d, 0x5d, 0x7d, 0x5e, 0x7d, 0x5d, 0x7d, 0x5e, 0x6e, 0x6b, 0x72, 0x65, 0x73, 0x65, 0x72, 0x76, 0x65, 0x64, 0x0a, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x0a, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x6e, 0x6f, 0x74, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x0a, 0x70, 0x68, 0x73, 0x69, 0x6d, 0x70, 0x69, 0x6e, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x0a, 0x70, 0x68, 0x66, 0x73, 0x69, 0x6d, 0x70, 0x69, 0x6e, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x0a, 0x70, 0x68, 0x66, 0x73, 0x69, 0x6d, 0x70, 0x75, 0x6b, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x0a, 0x73, 0x69, 0x6d, 0x6e, 0x6f, 0x74, 0x69, 0x6e, 0x73, 0x65, 0x72, 0x74, 0x65, 0x64, 0x0a, 0x73, 0x69, 0x6d, 0x70, 0x69, 0x6e, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x0a, 0x73, 0x69, 0x6d, 0x70, 0x75, 0x6b, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x0a, 0x73, 0x69, 0x6d, 0x66, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x0a, 0x73, 0x69, 0x6d, 0x62, 0x75, 0x73, 0x79, 0x0a, 0x73, 0x69, 0x6d, 0x77, 0x72, 0x6f, 0x6e, 0x67, 0x0a, 0x69, 0x6e, 0x63, 0x6f, 0x72, 0x72, 0x65, 0x63, 0x74, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x0a, 0x73, 0x69, 0x6d, 0x70, 0x69, 0x6e, 0x32, 0x72, 0x65, 0x71, 0x75, 0x69 }; void test_escape1 (void *f, void *data) { char escaped[1024]; gsize len; /* Ensure that escaping in general works */ len = dm_escape (data1, sizeof (data1), escaped, sizeof (escaped)); g_assert (len == 266); g_assert (len == sizeof (expected1)); g_assert (memcmp (escaped, expected1, len) == 0); } static const char data2[] = { 0x4b, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x3f, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff }; void test_escape2 (void *f, void *data) { char escaped[1024]; gsize len; /* Ensure that escaping data that doesn't need escaping works */ len = dm_escape (data2, sizeof (data2), escaped, sizeof (escaped)); g_assert (len == sizeof (data2)); g_assert (memcmp (escaped, data2, len) == 0); } void test_escape_unescape (void *f, void *data) { char escaped[512]; char unescaped[512]; gsize len, unlen; qcdmbool escaping = FALSE; /* Ensure that escaping data that needs escaping, and then unescaping it, * produces the exact same data as was originally escaped. */ len = dm_escape (data1, sizeof (data1), escaped, sizeof (escaped)); unlen = dm_unescape (escaped, len, unescaped, sizeof (unescaped), &escaping); g_assert (unlen == sizeof (data1)); g_assert (memcmp (unescaped, data1, unlen) == 0); } ModemManager-1.23.4-dev/libqcdm/tests/test-qcdm-escaping.h000066400000000000000000000016471456466623000233530ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * Copyright (C) 2010 Red Hat, Inc. * * This program is free software: you can redistribute it and/or * modify it under the terms of version 2 of the GNU General Public * License as published by the Free Software Foundation * * 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, see . */ #ifndef TEST_QCDM_ESCAPING_H #define TEST_QCDM_ESCAPING_H void test_escape1 (void *f, void *data); void test_escape2 (void *f, void *data); void test_escape_unescape (void *f, void *data); #endif /* TEST_QCDM_ESCAPING_H */ ModemManager-1.23.4-dev/libqcdm/tests/test-qcdm-result.c000066400000000000000000000045001456466623000230620ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * Copyright (C) 2010 Red Hat, Inc. * * This program is free software: you can redistribute it and/or * modify it under the terms of version 2 of the GNU General Public * License as published by the Free Software Foundation * * 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, see . */ #include #include #include "test-qcdm-result.h" #include "result.h" #include "result-private.h" #define TEST_TAG "test" void test_result_string (void *f, void *data) { const char *str = "foobarblahblahblah"; const char *tmp = NULL; QcdmResult *result; result = qcdm_result_new (); qcdm_result_add_string (result, TEST_TAG, str); qcdm_result_get_string (result, TEST_TAG, &tmp); g_assert (tmp); g_assert (strcmp (tmp, str) == 0); qcdm_result_unref (result); } void test_result_uint32 (void *f, void *data) { guint32 num = 0xDEADBEEF; guint32 tmp = 0; QcdmResult *result; result = qcdm_result_new (); qcdm_result_add_u32 (result, TEST_TAG, num); qcdm_result_get_u32 (result, TEST_TAG, &tmp); g_assert_cmpint (tmp, ==, num); qcdm_result_unref (result); } void test_result_uint8 (void *f, void *data) { guint8 num = 0x1E; guint8 tmp = 0; QcdmResult *result; result = qcdm_result_new (); qcdm_result_add_u8 (result, TEST_TAG, num); qcdm_result_get_u8 (result, TEST_TAG, &tmp); g_assert (tmp == num); qcdm_result_unref (result); } void test_result_uint8_array (void *f, void *data) { uint8_t array[] = { 0, 1, 255, 32, 128, 127 }; const uint8_t *tmp = NULL; size_t tmp_len = 0; QcdmResult *result; result = qcdm_result_new (); qcdm_result_add_u8_array (result, TEST_TAG, array, sizeof (array)); qcdm_result_get_u8_array (result, TEST_TAG, &tmp, &tmp_len); g_assert_cmpint (tmp_len, ==, sizeof (array)); g_assert_cmpint (memcmp (tmp, array, tmp_len), ==, 0); qcdm_result_unref (result); } ModemManager-1.23.4-dev/libqcdm/tests/test-qcdm-result.h000066400000000000000000000017361456466623000230770ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * Copyright (C) 2010 Red Hat, Inc. * * This program is free software: you can redistribute it and/or * modify it under the terms of version 2 of the GNU General Public * License as published by the Free Software Foundation * * 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, see . */ #ifndef TEST_QCDM_RESULT_H #define TEST_QCDM_RESULT_H void test_result_string (void *f, void *data); void test_result_uint32 (void *f, void *data); void test_result_uint8 (void *f, void *data); void test_result_uint8_array (void *f, void *data); #endif /* TEST_QCDM_RESULT_H */ ModemManager-1.23.4-dev/libqcdm/tests/test-qcdm-utils.c000066400000000000000000000076251456466623000227170ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * Copyright (C) 2010 Red Hat, Inc. * * This program is free software: you can redistribute it and/or * modify it under the terms of version 2 of the GNU General Public * License as published by the Free Software Foundation * * 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, see . */ #include #include #include "test-qcdm-utils.h" #include "utils.h" static const char decap_inbuf[] = { 0x40, 0x03, 0x00, 0x01, 0x00, 0x19, 0xf0, 0x00, 0x16, 0x00, 0x21, 0x00, 0x1c, 0x00, 0xd8, 0x00, 0x3f, 0x00, 0x56, 0x01, 0x3f, 0x00, 0x15, 0x00, 0x1a, 0x00, 0x11, 0x01, 0x3f, 0x00, 0x92, 0x01, 0x3f, 0x00, 0x39, 0x00, 0x3f, 0x00, 0x95, 0x01, 0x3f, 0x00, 0x12, 0x00, 0x3f, 0x00, 0x23, 0x01, 0x3f, 0x00, 0x66, 0x00, 0x3f, 0x00, 0x0b, 0x01, 0x3f, 0x00, 0xae, 0x00, 0x3f, 0x00, 0x02, 0x01, 0x3f, 0x00, 0xa8, 0x00, 0x3f, 0x00, 0x50, 0x01, 0x3f, 0x00, 0xf8, 0x01, 0x3f, 0x00, 0x57, 0x00, 0x3f, 0x00, 0x7d, 0x5e, 0x00, 0x3f, 0x00, 0x93, 0x00, 0x3f, 0x00, 0xbd, 0x00, 0x3f, 0x00, 0x77, 0x01, 0x3f, 0x00, 0xb7, 0x00, 0x3f, 0x00, 0xab, 0x00, 0x3f, 0x00, 0x33, 0x00, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xad, 0xde, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x13, 0x13, 0x50, 0x1f, 0x00, 0x00, 0xff, 0xff, 0x00, 0xaa, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb1, 0xc4, 0x7d, 0x5e, 0x7d, 0x5e, 0x7d, 0x5d, 0x5d, 0x04, 0x58, 0x1b, 0x5b, 0x1b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x65, 0x69, 0x7e }; void test_utils_decapsulate_buffer (void *f, void *data) { gboolean success; char outbuf[512]; gsize decap_len = 0; gsize used = 0; qcdmbool more = FALSE; success = dm_decapsulate_buffer (decap_inbuf, sizeof (decap_inbuf), outbuf, sizeof (outbuf), &decap_len, &used, &more); g_assert (success); g_assert (decap_len == 214); g_assert (used == 221); g_assert (more == FALSE); } static const char encap_outbuf[] = { 0x4b, 0x05, 0x08, 0x00, 0x01, 0xdd, 0x7e }; void test_utils_encapsulate_buffer (void *f, void *data) { char cmdbuf[10]; char outbuf[512]; gsize encap_len = 0; cmdbuf[0] = 0x4B; /* DIAG_CMD_SUBSYS */ cmdbuf[1] = 0x05; /* DIAG_SUBSYS_HDR */ cmdbuf[2] = 0x08; /* first byte of DIAG_SUBSYS_HDR_STATE_INFO in LE */ cmdbuf[3] = 0x00; /* second byte of DIAG_SUBSYS_HDR_STATE_INFO in LE */ encap_len = dm_encapsulate_buffer (cmdbuf, 4, sizeof (cmdbuf), &outbuf[0], sizeof (outbuf)); g_assert (encap_len == sizeof (encap_outbuf)); g_assert (memcmp (outbuf, encap_outbuf, encap_len) == 0); } static const char cns_inbuf[] = { 0x00, 0x0a, 0x6b, 0x74, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e }; void test_utils_decapsulate_sierra_cns (void *f, void *data) { gboolean success; char outbuf[512]; gsize decap_len = 0; gsize used = 0; qcdmbool more = FALSE; success = dm_decapsulate_buffer (cns_inbuf, sizeof (cns_inbuf), outbuf, sizeof (outbuf), &decap_len, &used, &more); g_assert (success == FALSE); } ModemManager-1.23.4-dev/libqcdm/tests/test-qcdm-utils.h000066400000000000000000000017171456466623000227200ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * Copyright (C) 2010 Red Hat, Inc. * * This program is free software: you can redistribute it and/or * modify it under the terms of version 2 of the GNU General Public * License as published by the Free Software Foundation * * 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, see . */ #ifndef TEST_QCDM_UTILS_H #define TEST_QCDM_UTILS_H void test_utils_decapsulate_buffer (void *f, void *data); void test_utils_encapsulate_buffer (void *f, void *data); void test_utils_decapsulate_sierra_cns (void *f, void *data); #endif /* TEST_QCDM_UTILS_H */ ModemManager-1.23.4-dev/libqcdm/tests/test-qcdm.c000066400000000000000000000113561456466623000215550ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * Copyright (C) 2010 Red Hat, Inc. * * This program is free software: you can redistribute it and/or * modify it under the terms of version 2 of the GNU General Public * License as published by the Free Software Foundation * * 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, see . */ #include #include #include "test-qcdm-crc.h" #include "test-qcdm-escaping.h" #include "test-qcdm-com.h" #include "test-qcdm-result.h" #include "test-qcdm-utils.h" typedef struct { gpointer com_data; } TestData; typedef GTestFixtureFunc TCFunc; #define TESTCASE(t, d) g_test_create_case (#t, 0, d, NULL, (TCFunc) t, NULL) static TestData * test_data_new (const char *port) { TestData *d; d = g_malloc0 (sizeof (TestData)); g_assert (d); if (port) d->com_data = test_com_setup (port); return d; } static void test_data_free (TestData *d) { if (d->com_data) test_com_teardown (d->com_data); g_free (d); } int main (int argc, char **argv) { GTestSuite *suite; TestData *data; int i; const char *port = NULL; gint result; g_test_init (&argc, &argv, NULL); /* See if we got passed a serial port for live testing */ for (i = 0; i < argc; i++) { if (!strcmp (argv[i], "--port")) { /* Make sure there's actually a port in the next arg */ g_assert (argc > i + 1); port = argv[++i]; } } data = test_data_new (port); suite = g_test_get_root (); g_test_suite_add (suite, TESTCASE (test_crc16_1, NULL)); g_test_suite_add (suite, TESTCASE (test_crc16_2, NULL)); g_test_suite_add (suite, TESTCASE (test_escape1, NULL)); g_test_suite_add (suite, TESTCASE (test_escape2, NULL)); g_test_suite_add (suite, TESTCASE (test_escape_unescape, NULL)); g_test_suite_add (suite, TESTCASE (test_utils_decapsulate_buffer, NULL)); g_test_suite_add (suite, TESTCASE (test_utils_encapsulate_buffer, NULL)); g_test_suite_add (suite, TESTCASE (test_utils_decapsulate_sierra_cns, NULL)); g_test_suite_add (suite, TESTCASE (test_result_string, NULL)); g_test_suite_add (suite, TESTCASE (test_result_uint32, NULL)); g_test_suite_add (suite, TESTCASE (test_result_uint8, NULL)); g_test_suite_add (suite, TESTCASE (test_result_uint8_array, NULL)); /* Live tests */ if (port) { g_test_suite_add (suite, TESTCASE (test_com_port_init, data->com_data)); g_test_suite_add (suite, TESTCASE (test_com_version_info, data->com_data)); g_test_suite_add (suite, TESTCASE (test_com_esn, data->com_data)); g_test_suite_add (suite, TESTCASE (test_com_mdn, data->com_data)); g_test_suite_add (suite, TESTCASE (test_com_read_roam_pref, data->com_data)); g_test_suite_add (suite, TESTCASE (test_com_read_mode_pref, data->com_data)); g_test_suite_add (suite, TESTCASE (test_com_read_hybrid_pref, data->com_data)); g_test_suite_add (suite, TESTCASE (test_com_read_ipv6_enabled, data->com_data)); g_test_suite_add (suite, TESTCASE (test_com_read_hdr_rev_pref, data->com_data)); g_test_suite_add (suite, TESTCASE (test_com_status, data->com_data)); g_test_suite_add (suite, TESTCASE (test_com_sw_version, data->com_data)); g_test_suite_add (suite, TESTCASE (test_com_status_snapshot, data->com_data)); g_test_suite_add (suite, TESTCASE (test_com_pilot_sets, data->com_data)); g_test_suite_add (suite, TESTCASE (test_com_cm_subsys_state_info, data->com_data)); g_test_suite_add (suite, TESTCASE (test_com_hdr_subsys_state_info, data->com_data)); g_test_suite_add (suite, TESTCASE (test_com_ext_logmask, data->com_data)); g_test_suite_add (suite, TESTCASE (test_com_event_report, data->com_data)); g_test_suite_add (suite, TESTCASE (test_com_log_config, data->com_data)); g_test_suite_add (suite, TESTCASE (test_com_zte_subsys_status, data->com_data)); g_test_suite_add (suite, TESTCASE (test_com_nw_subsys_modem_snapshot_cdma, data->com_data)); g_test_suite_add (suite, TESTCASE (test_com_nw_subsys_eri, data->com_data)); g_test_suite_add (suite, TESTCASE (test_com_wcdma_subsys_state_info, data->com_data)); g_test_suite_add (suite, TESTCASE (test_com_gsm_subsys_state_info, data->com_data)); } result = g_test_run (); test_data_free (data); return result; } ModemManager-1.23.4-dev/meson.build000066400000000000000000000364541456466623000171030ustar00rootroot00000000000000# SPDX-License-Identifier: GPL-2.0-or-later # Copyright (C) 2021 Iñigo Martinez project( 'ModemManager', 'c', version: '1.23.4', license: 'GPL2', default_options: [ 'buildtype=debugoptimized', 'c_std=gnu89', 'warning_level=2', ], meson_version: '>= 0.53.0', ) meson_version = meson.version() if meson_version.version_compare('>=1.0.0') fs = import('fs') endif mm_name = meson.project_name() mm_version = meson.project_version() version_array = mm_version.split('.') mm_major_version = version_array[0].to_int() mm_minor_version = version_array[1].to_int() mm_micro_version = version_array[2].to_int() mm_prefix = get_option('prefix') mm_datadir = get_option('datadir') mm_includedir = get_option('includedir') mm_libdir = get_option('libdir') mm_sbindir = get_option('sbindir') mm_sysconfdir = get_option('sysconfdir') mm_pkgdatadir = mm_datadir / mm_name mm_pkgincludedir = mm_includedir / mm_name mm_pkglibdir = mm_libdir / mm_name mm_pkgsysconfdir = mm_sysconfdir / mm_name mm_glib_name = 'libmm-glib' mm_glib_pkgincludedir = mm_includedir / mm_glib_name # libtool versioning for libmm-glib (-version-info c:r:a) # - If the interface is unchanged, but the implementation has changed or been fixed, then increment r # - Otherwise, increment c and zero r. # - If the interface has grown (that is, the new library is compatible with old code), increment a. # - If the interface has changed in an incompatible way (that is, functions have changed or been removed), then zero a. current = 10 revision = 0 age = 10 mm_glib_version = '@0@.@1@.@2@'.format(current - age, age, revision) mm_gir_version = '1.0' gnome = import('gnome') i18n = import('i18n') pkg = import('pkgconfig') python = import('python').find_installation('python3') source_root = meson.current_source_dir() build_root = meson.current_build_dir() build_aux_dir = source_root / 'build-aux' templates_dir = source_root / 'build-aux/templates' po_dir = source_root / 'po' src_dir = source_root / 'src' plugins_dir = source_root / 'src/plugins' mm_mkenums = find_program(source_root / 'build-aux/mm-mkenums') top_inc = include_directories('.') cc = meson.get_compiler('c') config_h = configuration_data() config_h.set_quoted('PACKAGE_VERSION', mm_version) config_h.set_quoted('VERSION', mm_version) # Globally define_GNU_SOURCE and therefore enable the GNU extensions config_h.set('_GNU_SOURCE', true) # compiler flags common_args = ['-DHAVE_CONFIG_H'] # compiler flags that are always enabled, even in release builds cc_args = cc.get_supported_arguments([ # warning on unused parameters is overkill, never do that '-Wno-unused-parameter', # function type cast disabled: used throughout the code especially to # cast GAsyncReadyCallbacks with the real object type instead of GObject '-Wno-cast-function-type', # all message protocol structs are packed, never complain about it '-Wno-packed', # we use some floating point ids as unknown, so we want to compare with them '-Wno-float-equal', # avoid warning if we're ignoring fields on purpose '-Wno-missing-field-initializers', ]) # tests are enabled by default enable_tests = get_option('tests') config_h.set('WITH_TESTS', enable_tests) # strict flags to use in debug builds if get_option('buildtype').contains('debug') cc_args += cc.get_supported_arguments([ '-fno-strict-aliasing', '-Waggregate-return', '-Wcast-align', '-Wdeclaration-after-statement', '-Wdouble-promotion', '-Wduplicated-branches', '-Wduplicated-cond', '-Wformat=2', '-Wformat-nonliteral', '-Wformat-security', '-Winit-self', '-Winline', '-Wjump-misses-init', '-Wlogical-op', '-Wnested-externs', '-Wmaybe-uninitialized', '-Wmissing-declarations', '-Wmissing-format-attribute', '-Wmissing-include-dirs', '-Wmissing-noreturn', '-Wmissing-prototypes', '-Wnull-dereference', '-Wpointer-arith', '-Wredundant-decls', '-Wrestrict', '-Wreturn-type', '-Wshadow', '-Wstrict-prototypes', '-Wsuggest-attribute=format', '-Wswitch-default', '-Wswitch-enum', '-Wundef', '-Wunused-but-set-variable', '-Wwrite-strings', ]) endif add_project_arguments(common_args + cc_args, language: 'c') glib_version = '2.56' gio_unix_dep = dependency('gio-unix-2.0') glib_dep = dependency('glib-2.0', version: '>= ' + glib_version) gmodule_dep = dependency('gmodule-2.0') deps = [ glib_dep, dependency('gio-2.0'), dependency('gobject-2.0'), ] c_args = [ '-DGLIB_VERSION_MIN_REQUIRED=GLIB_VERSION_' + glib_version.underscorify(), '-DGLIB_VERSION_MAX_ALLOWED=GLIB_VERSION_' + glib_version.underscorify(), ] glib_deps = declare_dependency( dependencies: deps, compile_args: c_args, ) # DBus system directory dbus_dep = dependency('dbus-1') dbus_interfaces_dir = dbus_dep.get_pkgconfig_variable('interfaces_dir', define_variable: ['datadir', mm_datadir]) dbus_system_bus_services_dir = dbus_dep.get_pkgconfig_variable('system_bus_services_dir', define_variable: ['datadir', mm_datadir]) dbus_policy_dir = get_option('dbus_policy_dir') if dbus_policy_dir == '' dbus_policy_dir = dbus_dep.get_pkgconfig_variable('sysconfdir', define_variable: ['sysconfdir', mm_sysconfdir]) / 'dbus-1/system.d' endif enable_bash_completion = get_option('bash_completion') if enable_bash_completion bash_completion_dep = dependency('bash-completion') bash_completion_completionsdir = bash_completion_dep.get_pkgconfig_variable( 'completionsdir', # bash-completion 2.10 changed the substitutions define_variable: bash_completion_dep.version().version_compare('>= 2.10') ? ['datadir', mm_datadir] : ['prefix', mm_prefix], ) endif # udev support (enabled by default) enable_udev = get_option('udev') if enable_udev gudev_dep = dependency('gudev-1.0', version: '>= 232') endif config_h.set('WITH_UDEV', enable_udev) # udev base directory (required to install rules even when udev support is disabled) udev_udevdir = get_option('udevdir') if udev_udevdir == '' assert(enable_udev, 'udevdir must be explicitly given if udev support is disabled') udev_udevdir = dependency('udev').get_pkgconfig_variable('udevdir') endif udev_rulesdir = udev_udevdir / 'rules.d' # systemd unit / service files systemd_systemdsystemunitdir = get_option('systemdsystemunitdir') install_systemdunitdir = (systemd_systemdsystemunitdir != 'no') if install_systemdunitdir and systemd_systemdsystemunitdir == '' systemd_dep = dependency('systemd', not_found_message: 'systemd required but not found, please provide a valid systemd user unit dir or disable it') systemd_systemdsystemunitdir = systemd_dep.get_pkgconfig_variable('systemdsystemunitdir', define_variable: ['root_prefix', mm_prefix]) endif # Suspend/resume support enable_systemd_suspend_resume = get_option('systemd_suspend_resume') enable_powerd_suspend_resume = get_option('powerd_suspend_resume') assert(not (enable_systemd_suspend_resume and enable_powerd_suspend_resume), 'systemd_suspend_resume and powerd_suspend_resume are not supported at the same time') # systemd journal support enable_systemd_journal = get_option('systemd_journal') if enable_systemd_suspend_resume or enable_systemd_journal libsystemd_dep = dependency('libsystemd', version: '>= 209', required: false) if not libsystemd_dep.found() libsystemd_dep = dependency('libsystemd-login', version: '>= 183', required: false) if not libsystemd_dep.found() libsystemd_dep = dependency( 'libelogind', version: '>= 209', not_found_message: 'libsystemd, libsystemd-login or elogind must be available at runtime for suspend/resume or systemd journal support', ) endif endif endif config_h.set('WITH_SUSPEND_RESUME', enable_systemd_suspend_resume or enable_powerd_suspend_resume) config_h.set('WITH_SYSTEMD_JOURNAL', enable_systemd_journal) # PolicyKit polkit_opt = get_option('polkit') enable_polkit = (polkit_opt != 'no') if enable_polkit polkit_gobject_dep = dependency('polkit-gobject-1', version: '>= 0.97', not_found_message: 'PolicyKit development headers are required') polkit_gobject_policydir = polkit_gobject_dep.get_pkgconfig_variable('policydir', define_variable: ['prefix', mm_prefix]) policy_conf = {'MM_DEFAULT_USER_POLICY': (polkit_opt == 'permissive' ? 'yes' : 'auth_self_keep')} endif config_h.set('WITH_POLKIT', enable_polkit) # AT command via DBus support (disabled by default unless running in --debug) # It is suggested that this option is only enabled in custom built systems and # only if truly required. enable_at_command_via_dbus = get_option('at_command_via_dbus') config_h.set('WITH_AT_COMMAND_VIA_DBUS', enable_at_command_via_dbus) # Builtin plugin support (disabled by default) enable_builtin_plugins = get_option('builtin_plugins') config_h.set('WITH_BUILTIN_PLUGINS', enable_builtin_plugins) # MBIM support (enabled by default) enable_mbim = get_option('mbim') if enable_mbim mbim_glib_dep = dependency('mbim-glib', version: '>= 1.30.0') endif config_h.set('WITH_MBIM', enable_mbim) # QMI support (enabled by default) enable_qmi = get_option('qmi') if enable_qmi qmi_glib_dep = dependency('qmi-glib', version: '>= 1.34.0') endif config_h.set('WITH_QMI', enable_qmi) # QRTR support (both as qrtr-glib and qmi-glib apis) enable_qrtr = get_option('qrtr') if enable_qrtr assert(enable_qmi, 'QRTR support requires QMI enabled') assert(qmi_glib_dep.get_pkgconfig_variable('qmi_qrtr_supported').to_int().is_odd(), 'Couldn\'t find QRTR support in qmi-glib.') qrtr_glib_dep = dependency('qrtr-glib', version: '>= 1.0.0') endif config_h.set('WITH_QRTR', enable_qrtr) # Distribution version string dist_version = get_option('dist_version') if dist_version != '' config_h.set('MM_DIST_VERSION', dist_version) endif if enable_tests util_dep = cc.find_library('util') endif # introspection support enable_gir = get_option('introspection') if enable_gir dependency('gobject-introspection-1.0', version: '>= 0.9.6') endif # vala support enable_vapi = get_option('vapi') # gtkdoc support enable_gtk_doc = get_option('gtk_doc') enable_plugins = not get_option('auto_features').disabled() plugins_shared_reqs = { 'fibocom': enable_mbim, 'foxconn': enable_mbim, 'icera': true, 'novatel': true, 'option': true, 'sierra': true, 'telit': true, 'xmm': true, } fibocom_shared_reqs = ['xmm'] dell_shared_reqs = ['novatel', 'sierra', 'telit', 'xmm'] mtk_shared_reqs = [] if enable_mbim fibocom_shared_reqs += ['fibocom'] dell_shared_reqs += ['foxconn'] mtk_shared_reqs += ['fibocom'] endif plugins_options_reqs = { 'altair-lte': {'available': true, 'shared': []}, 'anydata': {'available': true, 'shared': []}, 'broadmobi': {'available': true, 'shared': []}, 'cinterion': {'available': true, 'shared': []}, 'dell': {'available': true, 'shared': dell_shared_reqs}, 'dlink': {'available': true, 'shared': []}, 'fibocom': {'available': true, 'shared': fibocom_shared_reqs}, 'foxconn': {'available': enable_mbim, 'shared': ['foxconn']}, 'generic': {'available': true, 'shared': []}, 'gosuncn': {'available': true, 'shared': []}, 'haier': {'available': true, 'shared': []}, 'huawei': {'available': true, 'shared': []}, 'intel': {'available': true, 'shared': ['xmm']}, 'iridium': {'available': true, 'shared': []}, 'linktop': {'available': true, 'shared': []}, 'longcheer': {'available': true, 'shared': []}, 'mbm': {'available': true, 'shared': []}, 'motorola': {'available': true, 'shared': []}, 'mtk-legacy': {'available': true, 'shared': []}, 'mtk': {'available': true, 'shared': mtk_shared_reqs}, 'nokia': {'available': true, 'shared': []}, 'nokia-icera': {'available': true, 'shared': ['icera']}, 'novatel': {'available': true, 'shared': ['novatel']}, 'novatel-lte': {'available': true, 'shared': []}, 'option': {'available': true, 'shared': ['option']}, 'option-hso': {'available': true, 'shared': ['option']}, 'pantech': {'available': true, 'shared': []}, 'qcom-soc': {'available': enable_qmi, 'shared': []}, 'quectel': {'available': true, 'shared': []}, 'samsung': {'available': true, 'shared': ['icera']}, 'sierra-legacy': {'available': true, 'shared': ['icera', 'sierra']}, 'sierra': {'available': true, 'shared': ['xmm']}, 'simtech': {'available': true, 'shared': []}, 'telit': {'available': true, 'shared': ['telit']}, 'thuraya': {'available': true, 'shared': []}, 'tplink': {'available': true, 'shared': []}, 'ublox': {'available': true, 'shared': []}, 'via': {'available': true, 'shared': []}, 'wavecom': {'available': true, 'shared': []}, 'x22x': {'available': true, 'shared': []}, 'zte': {'available': true, 'shared': ['icera']}, } plugins_shared = {} foreach plugin_name, _: plugins_shared_reqs plugins_shared += {plugin_name: false} endforeach plugins_options = {} foreach plugin_name, plugin_reqs: plugins_options_reqs plugin_opt = get_option('plugin_' + plugin_name.underscorify()) assert(plugin_reqs['available'] or not plugin_opt.enabled(), '@0@ is not available'.format(plugin_name)) plugin_enabled = not plugin_opt.disabled() and plugin_reqs['available'] if plugin_enabled foreach plugin_req: plugin_reqs['shared'] if plugins_shared_reqs[plugin_req] plugins_shared += {plugin_req: true} else assert(plugin_opt.enabled(), '@0@ required @1@ but is not available'.format(plugin_name, plugin_req)) plugin_enabled = false break endif endforeach config_h.set('ENABLE_PLUGIN_' + plugin_name.underscorify().to_upper(), true) endif plugins_options += {plugin_name: plugin_enabled} endforeach version_conf = { 'MM_MAJOR_VERSION': mm_major_version, 'MM_MINOR_VERSION': mm_minor_version, 'MM_MICRO_VERSION': mm_micro_version, 'VERSION': mm_version, } subdir('po') subdir('data') if get_option('examples') subdir('data/dispatcher-connection') subdir('data/dispatcher-fcc-unlock') endif subdir('introspection') subdir('include') subdir('libqcdm/src') if enable_tests subdir('libqcdm/tests') endif subdir('libmm-glib') subdir('src') subdir('cli') if enable_tests subdir('test') subdir('tools/tests') endif subdir('examples/sms-c') enable_man = get_option('man') if enable_man subdir('docs/man') endif if enable_gtk_doc subdir('docs/reference/api') subdir('docs/reference/libmm-glib') endif enable_fuzzer = get_option('fuzzer') configure_file( output: 'config.h', configuration: config_h, ) summary({ 'compiler': cc.get_id(), 'cflags': cc_args, }, section: 'Build') summary({ 'prefix': mm_prefix, 'configuration directory': mm_pkgsysconfdir, 'D-Bus policy directory': dbus_policy_dir, 'udev base directory': udev_udevdir, 'systemd user unit directory': systemd_systemdsystemunitdir, }, section: 'System paths') summary({ 'udev': enable_udev, 'policykit': polkit_opt, 'mbim': enable_mbim, 'qmi': enable_qmi, 'qrtr': enable_qrtr, 'systemd suspend/resume': enable_systemd_suspend_resume, 'powerd suspend/resume': enable_powerd_suspend_resume, 'systemd journal': enable_systemd_journal, 'at command via dbus': enable_at_command_via_dbus, 'builtin plugins': enable_builtin_plugins, }, section: 'Features') summary(plugins_shared, section: 'Shared utils') summary(plugins_options, section: 'Plugins') summary({ 'gobject introspection': enable_gir, 'Man': enable_man, 'Documentation': enable_gtk_doc, 'bash completion': enable_bash_completion, 'vala bindings': enable_vapi, 'code coverage': get_option('b_coverage'), 'fuzzer': enable_fuzzer, }, section: 'Miscellaneous') ModemManager-1.23.4-dev/meson_options.txt000066400000000000000000000151571456466623000203730ustar00rootroot00000000000000# SPDX-License-Identifier: GPL-2.0-or-later # Copyright (C) 2021 Iñigo Martinez option('udev', type: 'boolean', value: true, description: 'enable udev support') option('udevdir', type: 'string', value: '', description: 'udev base directory') option('examples', type: 'boolean', value: true, description: 'install examples') option('tests', type: 'boolean', value: true, description: 'enable tests') option('dbus_policy_dir', type: 'string', value: '', description: 'd-bus system policy directory') option('systemdsystemunitdir', type: 'string', value: '', description: 'systemd system units directory') option('systemd_suspend_resume', type: 'boolean', value: true, description: 'enable suspend/resume support with systemd') option('powerd_suspend_resume', type: 'boolean', value: false, description: 'enable suspend/resume support with powerd') option('systemd_journal', type: 'boolean', value: true, description: 'enable systemd journal support') option('polkit', type: 'combo', choices: ['strict', 'permissive', 'no'], value: 'strict', description: 'User auth-polkit configuration option.') option('at_command_via_dbus', type: 'boolean', value: false, description: 'enable at commands via d-bus') option('builtin_plugins', type: 'boolean', value: false, description: 'integrate all built plugins within the daemon binary') option('mbim', type: 'boolean', value: true, description: 'enable MBIM support') option('qmi', type: 'boolean', value: true, description: 'enable QMI support') option('qrtr', type: 'boolean', value: true, description: 'enable QRTR support') option('dist_version', type: 'string', value: '', description: 'define the custom version (like distribution package name and revision') option('plugin_generic', type: 'feature', value: 'auto', description: 'enable generic plugin support') option('plugin_altair_lte', type: 'feature', value: 'auto', description: 'enable altair lte plugin support') option('plugin_anydata', type: 'feature', value: 'auto', description: 'enable anydata plugin support') option('plugin_broadmobi', type: 'feature', value: 'auto', description: 'enable broadmobi plugin support') option('plugin_cinterion', type: 'feature', value: 'auto', description: 'enable cinterion plugin support') # shared_sierra, shared_novatel, shared_xmm, shared_telit, shared_foxonn option('plugin_dell', type: 'feature', value: 'auto', description: 'enable dell plugin support') option('plugin_dlink', type: 'feature', value: 'auto', description: 'enable dlink plugin support') # shared_xmm option('plugin_fibocom', type: 'feature', value: 'auto', description: 'enable fibocom plugin support') # shared_foxconn option('plugin_foxconn', type: 'feature', value: 'auto', description: 'enable foxconn plugin support') option('plugin_gosuncn', type: 'feature', value: 'auto', description: 'enable gosuncn plugin support') option('plugin_haier', type: 'feature', value: 'auto', description: 'enable haier plugin support') option('plugin_huawei', type: 'feature', value: 'auto', description: 'enable huawei plugin support') option('plugin_intel', type: 'feature', value: 'auto', description: 'enable intel plugin support') option('plugin_iridium', type: 'feature', value: 'auto', description: 'enable iridium plugin support') option('plugin_linktop', type: 'feature', value: 'auto', description: 'enable linktop plugin support') option('plugin_longcheer', type: 'feature', value: 'auto', description: 'enable longcheer plugin support') option('plugin_mbm', type: 'feature', value: 'auto', description: 'enable mbm plugin support') option('plugin_motorola', type: 'feature', value: 'auto', description: 'enable motorola plugin support') option('plugin_mtk_legacy', type: 'feature', value: 'auto', description: 'enable mtk legacy plugin support') option('plugin_mtk', type: 'feature', value: 'auto', description: 'enable mtk plugin support') option('plugin_nokia', type: 'feature', value: 'auto', description: 'enable nokia plugin support') # shared_icera option('plugin_nokia_icera', type: 'feature', value: 'auto', description: 'enable nokia icera plugin support') # shared_novatel option('plugin_novatel', type: 'feature', value: 'auto', description: 'enable novatel plugin support') option('plugin_novatel_lte', type: 'feature', value: 'auto', description: 'enable novatel lte plugin support') # shared_option option('plugin_option', type: 'feature', value: 'auto', description: 'enable option plugin support') # shared_option option('plugin_option_hso', type: 'feature', value: 'auto', description: 'enable option hso plugin support') option('plugin_pantech', type: 'feature', value: 'auto', description: 'enable pantech plugin support') option('plugin_qcom_soc', type: 'feature', value: 'auto', description: 'enable qcom soc plugin support') option('plugin_quectel', type: 'feature', value: 'auto', description: 'enable quectel plugin support') # shared_icera option('plugin_samsung', type: 'feature', value: 'auto', description: 'enable samsung plugin support') # shared_icera, shared_sierra option('plugin_sierra_legacy', type: 'feature', value: 'auto', description: 'enable sierra legacy plugin support') # shared_xmm option('plugin_sierra', type: 'feature', value: 'auto', description: 'enable sierra plugin support') option('plugin_simtech', type: 'feature', value: 'auto', description: 'enable simtech plugin support') # shared_telit option('plugin_telit', type: 'feature', value: 'auto', description: 'enable telit plugin support') option('plugin_thuraya', type: 'feature', value: 'auto', description: 'enable thuraya plugin support') option('plugin_tplink', type: 'feature', value: 'auto', description: 'enable tplink plugin support') option('plugin_ublox', type: 'feature', value: 'auto', description: 'enable ublox plugin support') option('plugin_via', type: 'feature', value: 'auto', description: 'enable via plugin support') option('plugin_wavecom', type: 'feature', value: 'auto', description: 'enable wavecom plugin support') option('plugin_x22x', type: 'feature', value: 'auto', description: 'enable x22x plugin support') # shared_icera option('plugin_zte', type: 'feature', value: 'auto', description: 'enable zte plugin support') option('introspection', type: 'boolean', value: true, description: 'build introspection support') option('vapi', type: 'boolean', value: false, description: 'build vala bindings') option('man', type: 'boolean', value: true, description: 'build manual pages') option('gtk_doc', type: 'boolean', value: false, description: 'use gtk-doc to build documentation') option('bash_completion', type: 'boolean', value: true, description: 'install bash completion files') option('fuzzer', type: 'boolean', value: false, description: 'build fuzzer tests') ModemManager-1.23.4-dev/po/000077500000000000000000000000001456466623000153435ustar00rootroot00000000000000ModemManager-1.23.4-dev/po/LINGUAS000066400000000000000000000001141456466623000163640ustar00rootroot00000000000000cs ca da de fi fr fur gl he hu id it ka lt nl pl pt_BR ru sk sv tr uk zh_CN ModemManager-1.23.4-dev/po/POTFILES.in000066400000000000000000000003241456466623000171170ustar00rootroot00000000000000# List of source files containing translatable strings. # Please keep this file sorted alphabetically. data/org.freedesktop.ModemManager1.policy.in.in src/mm-sleep-monitor-powerd.c src/mm-sleep-monitor-systemd.c ModemManager-1.23.4-dev/po/POTFILES.skip000066400000000000000000000001411456466623000174540ustar00rootroot00000000000000data/org.freedesktop.ModemManager1.policy.in data/tests/org.freedesktop.ModemManager1.service.in ModemManager-1.23.4-dev/po/ca.po000066400000000000000000000107231456466623000162710ustar00rootroot00000000000000# Catalan translation for ModemManager. # Copyright (C) 2023 ModemManager's COPYRIGHT HOLDER # This file is distributed under the same license as the ModemManager package. # pocsenderi , 2023. # msgid "" msgstr "" "Project-Id-Version: ModemManager main\n" "Report-Msgid-Bugs-To: https://gitlab.freedesktop.org/mobile-broadband/" "ModemManager/issues\n" "POT-Creation-Date: 2023-05-30 15:25+0000\n" "PO-Revision-Date: 2023-05-30 21:43+0200\n" "Last-Translator: pocsenderi \n" "Language-Team: Catalan \n" "Language: ca\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Generator: Poedit 2.4.2\n" #: data/org.freedesktop.ModemManager1.policy.in.in:13 msgid "Control the Modem Manager daemon" msgstr "Controla el dimoni del Modem Manager" #: data/org.freedesktop.ModemManager1.policy.in.in:14 msgid "System policy prevents controlling the Modem Manager." msgstr "La política del sistema impedeix controlar el Modem Manager." #: data/org.freedesktop.ModemManager1.policy.in.in:22 msgid "Unlock and control a mobile broadband device" msgstr "Desbloqueja i controla un dispositiu de banda ampla mòbil" #: data/org.freedesktop.ModemManager1.policy.in.in:23 msgid "" "System policy prevents unlocking or controlling the mobile broadband device." msgstr "" "La política del sistema impedeix desbloquejar o controlar el dispositiu de " "banda ampla mòbil." #: data/org.freedesktop.ModemManager1.policy.in.in:31 msgid "Add, modify, and delete mobile broadband contacts" msgstr "Afegeix, modifica o esborra contactes de mòbil" #: data/org.freedesktop.ModemManager1.policy.in.in:32 msgid "" "System policy prevents adding, modifying, or deleting this device's contacts." msgstr "" "La política del sistema impedeix afegir, modificar o suprimir els contactes " "d'aquest dispositiu." #: data/org.freedesktop.ModemManager1.policy.in.in:40 msgid "Send, save, modify, and delete text messages" msgstr "Envia, desa, modifica i esborra missatges de text" #: data/org.freedesktop.ModemManager1.policy.in.in:41 msgid "" "System policy prevents sending or manipulating this device's text messages." msgstr "" "La política del sistema impedeix enviar o manipular els missatges de text " "d'aquest dispositiu." #: data/org.freedesktop.ModemManager1.policy.in.in:49 msgid "Accept incoming voice calls or start outgoing voice calls." msgstr "Accepta trucades de veu entrants o n'inicia de sortints." #: data/org.freedesktop.ModemManager1.policy.in.in:50 msgid "System policy prevents voice calls." msgstr "La política del sistema impedeix les trucades de veu." #: data/org.freedesktop.ModemManager1.policy.in.in:58 msgid "Query network time and timezone information" msgstr "Consulta informació del fus horari i l'hora de la xarxa" #: data/org.freedesktop.ModemManager1.policy.in.in:59 msgid "System policy prevents querying network time information." msgstr "" "La política del sistema impedeix consultar la informació de l'hora de la " "xarxa." #: data/org.freedesktop.ModemManager1.policy.in.in:67 msgid "Enable and view geographic location and positioning information" msgstr "Activa i mostra informació de posicionament i ubicació geogràfica" #: data/org.freedesktop.ModemManager1.policy.in.in:68 msgid "" "System policy prevents enabling or viewing geographic location information." msgstr "" "La política del sistema impedeix habilitar o visualitzar informació " "d'ubicació geogràfica." #: data/org.freedesktop.ModemManager1.policy.in.in:76 msgid "Query and utilize network information and services" msgstr "Consulta i fa ús d'informació i serveis de xarxa" #: data/org.freedesktop.ModemManager1.policy.in.in:77 msgid "" "System policy prevents querying or utilizing network information and " "services." msgstr "" "La política del sistema impedeix consultar o utilitzar informació i serveis " "de xarxa." #: data/org.freedesktop.ModemManager1.policy.in.in:85 msgid "Query and manage firmware on a mobile broadband device" msgstr "" "Consulta i gestiona el microprogramari en un dispositiu de banda ampla mòbil" #: data/org.freedesktop.ModemManager1.policy.in.in:86 msgid "System policy prevents querying or managing this device's firmware." msgstr "" "La política del sistema impedeix consultar o gestionar el microprogramari " "d'aquest dispositiu." #: src/mm-sleep-monitor-systemd.c:125 msgid "ModemManager needs to reset devices" msgstr "ModemManager necessita reinicialitzar els dispositius" ModemManager-1.23.4-dev/po/cs.po000066400000000000000000000112151456466623000163100ustar00rootroot00000000000000# Czech translation for ModemManager. # Copyright (C) 2017 ModemManager's COPYRIGHT HOLDER # This file is distributed under the same license as the ModemManager package. # FIRST AUTHOR , YEAR. # Marek Černocký , 2017. # msgid "" msgstr "" "Project-Id-Version: ModemManager main\n" "Report-Msgid-Bugs-To: https://gitlab.freedesktop.org/mobile-broadband/" "ModemManager/issues\n" "POT-Creation-Date: 2022-07-16 14:32+0200\n" "PO-Revision-Date: 2017-10-21 15:32+0200\n" "Last-Translator: Marek Černocký \n" "Language-Team: čeština \n" "Language: cs\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2;\n" "X-Generator: Gtranslator 2.91.7\n" #: data/org.freedesktop.ModemManager1.policy.in.in:13 msgid "Control the Modem Manager daemon" msgstr "Ovládat démona pro správu modemů" #: data/org.freedesktop.ModemManager1.policy.in.in:14 msgid "System policy prevents controlling the Modem Manager." msgstr "Systémová zásada brání v ovládání Správy modemů." #: data/org.freedesktop.ModemManager1.policy.in.in:22 msgid "Unlock and control a mobile broadband device" msgstr "Odemykat a ovládat mobilní širokopásmové zařízení" #: data/org.freedesktop.ModemManager1.policy.in.in:23 msgid "" "System policy prevents unlocking or controlling the mobile broadband device." msgstr "" "Systémová zásada brání v odemknutí nebo v ovládání mobilního širokopásmového " "zařízení." #: data/org.freedesktop.ModemManager1.policy.in.in:31 msgid "Add, modify, and delete mobile broadband contacts" msgstr "Přidávat, měnit a mazat kontakty v mobilním zařízení" #: data/org.freedesktop.ModemManager1.policy.in.in:32 msgid "" "System policy prevents adding, modifying, or deleting this device's contacts." msgstr "" "Systémová zásada brání v přidání, změně nebo smazání kontaktů v tomto " "zařízení." #: data/org.freedesktop.ModemManager1.policy.in.in:40 msgid "Send, save, modify, and delete text messages" msgstr "Odesílat, ukládat, měnit a mazat textové zprávy" #: data/org.freedesktop.ModemManager1.policy.in.in:41 msgid "" "System policy prevents sending or manipulating this device's text messages." msgstr "" "Systémová zásada brání v odesílání nebo v manipulaci s textovými zprávami na " "tomto zařízení." #: data/org.freedesktop.ModemManager1.policy.in.in:49 msgid "Accept incoming voice calls or start outgoing voice calls." msgstr "Přijímat příchozí hovory nebo začínat odchozí hovory" #: data/org.freedesktop.ModemManager1.policy.in.in:50 msgid "System policy prevents voice calls." msgstr "Systémová zásada brání v hlasových hovorech." #: data/org.freedesktop.ModemManager1.policy.in.in:58 msgid "Query network time and timezone information" msgstr "" #: data/org.freedesktop.ModemManager1.policy.in.in:59 #, fuzzy msgid "System policy prevents querying network time information." msgstr "" "Systémová zásada brání v dotazování na informace o síti a na služby, nebo " "brání v jejich využívání." #: data/org.freedesktop.ModemManager1.policy.in.in:67 msgid "Enable and view geographic location and positioning information" msgstr "" "Povolovat sdělování a zobrazování geografické polohy a informací o pozici" #: data/org.freedesktop.ModemManager1.policy.in.in:68 msgid "" "System policy prevents enabling or viewing geographic location information." msgstr "" "Systémová zásada brání v povolení sdělování a v zobrazení informací o " "geografické poloze." #: data/org.freedesktop.ModemManager1.policy.in.in:76 msgid "Query and utilize network information and services" msgstr "Dotazovat se na informace o síti a na služby a využívat je" #: data/org.freedesktop.ModemManager1.policy.in.in:77 msgid "" "System policy prevents querying or utilizing network information and " "services." msgstr "" "Systémová zásada brání v dotazování na informace o síti a na služby, nebo " "brání v jejich využívání." #: data/org.freedesktop.ModemManager1.policy.in.in:85 msgid "Query and manage firmware on a mobile broadband device" msgstr "" "Dotazovat se na firmware a spravovat jej na mobilním širokopásmovém zařízení" #: data/org.freedesktop.ModemManager1.policy.in.in:86 msgid "System policy prevents querying or managing this device's firmware." msgstr "" "Systémová zásada brání v dotázání na firmware nebo brání v jeho správě na " "tomto zařízení." #: src/mm-sleep-monitor-systemd.c:125 msgid "ModemManager needs to reset devices" msgstr "Správa modemů potřebuje resetovat zařízení" ModemManager-1.23.4-dev/po/da.po000066400000000000000000000103761456466623000162760ustar00rootroot00000000000000# Danish translation for ModemManager. # Copyright (C) 2019 ModemManager's COPYRIGHT HOLDER # This file is distributed under the same license as the ModemManager package. # scootergrisen, 2019. msgid "" msgstr "" "Project-Id-Version: ModemManager main\n" "Report-Msgid-Bugs-To: https://gitlab.freedesktop.org/mobile-broadband/" "ModemManager/issues\n" "POT-Creation-Date: 2022-07-16 14:32+0200\n" "PO-Revision-Date: 2019-02-10 16:46+0200\n" "Last-Translator: scootergrisen\n" "Language-Team: Danish\n" "Language: da\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" #: data/org.freedesktop.ModemManager1.policy.in.in:13 msgid "Control the Modem Manager daemon" msgstr "Styr Modem Manager-dæmonen" #: data/org.freedesktop.ModemManager1.policy.in.in:14 msgid "System policy prevents controlling the Modem Manager." msgstr "Systempolitikken forhindrer styring af Modem Manager." #: data/org.freedesktop.ModemManager1.policy.in.in:22 msgid "Unlock and control a mobile broadband device" msgstr "Lås op for og styr en mobilt bredbånd-enhed" #: data/org.freedesktop.ModemManager1.policy.in.in:23 msgid "" "System policy prevents unlocking or controlling the mobile broadband device." msgstr "" "Systempolitikken forhindrer oplåsning og styring af mobilt bredbånd-enheden." #: data/org.freedesktop.ModemManager1.policy.in.in:31 msgid "Add, modify, and delete mobile broadband contacts" msgstr "Tilføj, rediger og slet mobilt bredbånd-kontakter" #: data/org.freedesktop.ModemManager1.policy.in.in:32 msgid "" "System policy prevents adding, modifying, or deleting this device's contacts." msgstr "" "Systempolitikken forhindrer tilføjelse, redigering eller sletning af " "enhedens kontakter." #: data/org.freedesktop.ModemManager1.policy.in.in:40 msgid "Send, save, modify, and delete text messages" msgstr "Send, gem, rediger og slet tekstbeskeder" #: data/org.freedesktop.ModemManager1.policy.in.in:41 msgid "" "System policy prevents sending or manipulating this device's text messages." msgstr "" "Systempolitikken forhindrer afsendelse eller manipulering af enhedens " "tekstbeskeder." #: data/org.freedesktop.ModemManager1.policy.in.in:49 msgid "Accept incoming voice calls or start outgoing voice calls." msgstr "Accepter indkommende stemmeopkald eller start udgående stemmeopkald." #: data/org.freedesktop.ModemManager1.policy.in.in:50 msgid "System policy prevents voice calls." msgstr "Systempolitikken forhindrer stemmeopkald." #: data/org.freedesktop.ModemManager1.policy.in.in:58 msgid "Query network time and timezone information" msgstr "" #: data/org.freedesktop.ModemManager1.policy.in.in:59 #, fuzzy msgid "System policy prevents querying network time information." msgstr "" "Systempolitikken forhindrer forespørgsel eller anvendelse af " "netværksinformation og -tjenester." #: data/org.freedesktop.ModemManager1.policy.in.in:67 msgid "Enable and view geographic location and positioning information" msgstr "Aktivér og vis information om geografisk placering og positition" #: data/org.freedesktop.ModemManager1.policy.in.in:68 msgid "" "System policy prevents enabling or viewing geographic location information." msgstr "" "Systempolitikken forhindrer aktivering og visning af information geografisk " "placering." #: data/org.freedesktop.ModemManager1.policy.in.in:76 msgid "Query and utilize network information and services" msgstr "Forespørg og anvend netværksinformation og -tjenester" #: data/org.freedesktop.ModemManager1.policy.in.in:77 msgid "" "System policy prevents querying or utilizing network information and " "services." msgstr "" "Systempolitikken forhindrer forespørgsel eller anvendelse af " "netværksinformation og -tjenester." #: data/org.freedesktop.ModemManager1.policy.in.in:85 msgid "Query and manage firmware on a mobile broadband device" msgstr "Forespørg og håndter firmware på en mobilt bredbånd-enhed" #: data/org.freedesktop.ModemManager1.policy.in.in:86 msgid "System policy prevents querying or managing this device's firmware." msgstr "" "Systempolitikken forhindrer forespørgsel og håndtering af enhedens firmware." #: src/mm-sleep-monitor-systemd.c:125 msgid "ModemManager needs to reset devices" msgstr "ModemManager har brug for at nulstille enhederne" ModemManager-1.23.4-dev/po/de.po000066400000000000000000000110151456466623000162710ustar00rootroot00000000000000# German translation of Modem Manager # Copyright (C) 2013 Free Software Foundation, Inc. # This file is distributed under the same license as the Modem Manager package. # # Mario Blättermann , 2013. # Jürgen Benvenuti , 2022. # msgid "" msgstr "" "Project-Id-Version: ModemManager\n" "Report-Msgid-Bugs-To: https://gitlab.freedesktop.org/mobile-broadband/" "ModemManager/issues\n" "POT-Creation-Date: 2022-09-09 15:25+0000\n" "PO-Revision-Date: 2022-09-10 21:20+0200\n" "Last-Translator: Jürgen Benvenuti \n" "Language-Team: German \n" "Language: de\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Generator: Poedit 3.1.1\n" #: data/org.freedesktop.ModemManager1.policy.in.in:13 msgid "Control the Modem Manager daemon" msgstr "Den Modem-Manager-Daemon steuern" #: data/org.freedesktop.ModemManager1.policy.in.in:14 msgid "System policy prevents controlling the Modem Manager." msgstr "Die Systemrichtlinien verhindern die Steuerung von ModemManager." #: data/org.freedesktop.ModemManager1.policy.in.in:22 msgid "Unlock and control a mobile broadband device" msgstr "Ein mobiles Breitbandgerät entsperren und steuern" #: data/org.freedesktop.ModemManager1.policy.in.in:23 msgid "" "System policy prevents unlocking or controlling the mobile broadband device." msgstr "" "Die Systemrichtlinien verhindern das Entsperren oder Steuern des mobilen " "Breitbandgerätes." #: data/org.freedesktop.ModemManager1.policy.in.in:31 msgid "Add, modify, and delete mobile broadband contacts" msgstr "Kontakte für mobiles Breitband hinzufügen, ändern und löschen" #: data/org.freedesktop.ModemManager1.policy.in.in:32 msgid "" "System policy prevents adding, modifying, or deleting this device's contacts." msgstr "" "Die Systemrichtlinien verhindern das Hinzufügen, Ändern oder Löschen der " "Kontakte dieses Gerätes." #: data/org.freedesktop.ModemManager1.policy.in.in:40 msgid "Send, save, modify, and delete text messages" msgstr "Textnachrichten senden, speichern, bearbeiten und löschen" #: data/org.freedesktop.ModemManager1.policy.in.in:41 msgid "" "System policy prevents sending or manipulating this device's text messages." msgstr "" "Die Systemrichtlinien verhindern das Senden oder Bearbeiten der " "Textnachrichten dieses Gerätes." #: data/org.freedesktop.ModemManager1.policy.in.in:49 msgid "Accept incoming voice calls or start outgoing voice calls." msgstr "" "Eingehende Sprachanrufe annehmen oder ausgehende Sprachanrufe beginnen." #: data/org.freedesktop.ModemManager1.policy.in.in:50 msgid "System policy prevents voice calls." msgstr "Die Systemrichtlinien verhindern Sprachanrufe." #: data/org.freedesktop.ModemManager1.policy.in.in:58 msgid "Query network time and timezone information" msgstr "Netzwerkzeit und Zeitzonen-Information abfragen" #: data/org.freedesktop.ModemManager1.policy.in.in:59 msgid "System policy prevents querying network time information." msgstr "" "Die Systemrichtlinien verhindern die Abfrage der Netzwerkzeit-Information." #: data/org.freedesktop.ModemManager1.policy.in.in:67 msgid "Enable and view geographic location and positioning information" msgstr "" "Informationen zum geografischen Standort und zur Positionierung aktivieren " "und anzeigen" #: data/org.freedesktop.ModemManager1.policy.in.in:68 msgid "" "System policy prevents enabling or viewing geographic location information." msgstr "" "Die Systemrichtlinien verhindern das Aktivieren oder Anzeigen der " "Informationen zum geografischen Standort." #: data/org.freedesktop.ModemManager1.policy.in.in:76 msgid "Query and utilize network information and services" msgstr "Netzwerkinformationen und -dienste abfragen und nutzen" #: data/org.freedesktop.ModemManager1.policy.in.in:77 msgid "" "System policy prevents querying or utilizing network information and " "services." msgstr "" "Die Systemrichtlinien verhindern die Abfrage der Netzwerkinformationen und -" "dienste." #: data/org.freedesktop.ModemManager1.policy.in.in:85 msgid "Query and manage firmware on a mobile broadband device" msgstr "Firmware auf mobilen Breitbandgeräten abfragen und verwalten" #: data/org.freedesktop.ModemManager1.policy.in.in:86 msgid "System policy prevents querying or managing this device's firmware." msgstr "" "Die Systemrichtlinien verhindern die Abfrage oder Verwaltung der Firmware " "dieses Gerätes." #: src/mm-sleep-monitor-systemd.c:125 msgid "ModemManager needs to reset devices" msgstr "ModemManager muss Geräte zurücksetzen" ModemManager-1.23.4-dev/po/fi.po000066400000000000000000000107271456466623000163100ustar00rootroot00000000000000# Finnish translation for ModemManager. # Copyright (C) 2020 ModemManager's COPYRIGHT HOLDER # This file is distributed under the same license as the ModemManager package. # JRfi , 2020. # msgid "" msgstr "" "Project-Id-Version: ModemManager main\n" "Report-Msgid-Bugs-To: https://gitlab.freedesktop.org/mobile-broadband/" "ModemManager/issues\n" "POT-Creation-Date: 2022-07-16 14:32+0200\n" "PO-Revision-Date: 2020-08-08 18:58+0300\n" "Last-Translator: JR-Fi \n" "Language-Team: Finnish \n" "Language: fi\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" "X-Generator: Poedit 2.0.6\n" #: data/org.freedesktop.ModemManager1.policy.in.in:13 msgid "Control the Modem Manager daemon" msgstr "Hallitse Modem Manager taustaprosessia" #: data/org.freedesktop.ModemManager1.policy.in.in:14 msgid "System policy prevents controlling the Modem Manager." msgstr "" "Järjestelmän sääntö estää Modem Managerin hallinta-asetusten muuttamisen." #: data/org.freedesktop.ModemManager1.policy.in.in:22 msgid "Unlock and control a mobile broadband device" msgstr "Avaa ja hallitse mobiilia laajakaistalaitetta" #: data/org.freedesktop.ModemManager1.policy.in.in:23 msgid "" "System policy prevents unlocking or controlling the mobile broadband device." msgstr "" "Järjestelmän sääntö estää avaamasta tai hallitsemasta mobiilia " "laajakaistalaitetta." #: data/org.freedesktop.ModemManager1.policy.in.in:31 msgid "Add, modify, and delete mobile broadband contacts" msgstr "Lisää, muuta ja poista mobiilin laajakaistan yhteystietoja" #: data/org.freedesktop.ModemManager1.policy.in.in:32 msgid "" "System policy prevents adding, modifying, or deleting this device's contacts." msgstr "" "Järjestelmän sääntö estää lisäämästä, muuttamasta tai poistamasta tämän " "laitteen yhteystietoja." #: data/org.freedesktop.ModemManager1.policy.in.in:40 msgid "Send, save, modify, and delete text messages" msgstr "Lähetä, talleta, muuta ja poista tekstiviestejä" #: data/org.freedesktop.ModemManager1.policy.in.in:41 msgid "" "System policy prevents sending or manipulating this device's text messages." msgstr "" "Järjestelmän sääntö estää lähettämästä tai muokkaamasta tämän laitteen " "tekstiviestejä." #: data/org.freedesktop.ModemManager1.policy.in.in:49 msgid "Accept incoming voice calls or start outgoing voice calls." msgstr "Hyväksy tulevat puhelut tai aloita (ääni)puheluita." #: data/org.freedesktop.ModemManager1.policy.in.in:50 msgid "System policy prevents voice calls." msgstr "Järjestelmän sääntö estää äänipuhelut." #: data/org.freedesktop.ModemManager1.policy.in.in:58 msgid "Query network time and timezone information" msgstr "Tarkista verkon aika ja aikavyöhyketiedot" #: data/org.freedesktop.ModemManager1.policy.in.in:59 msgid "System policy prevents querying network time information." msgstr "" "Järjestelmän sääntö estää tarkastamasta verkon aikaa ja aikavyöhyketietoja." #: data/org.freedesktop.ModemManager1.policy.in.in:67 msgid "Enable and view geographic location and positioning information" msgstr "Salli ja katso sijainti- ja paikkatiedot" #: data/org.freedesktop.ModemManager1.policy.in.in:68 msgid "" "System policy prevents enabling or viewing geographic location information." msgstr "" "Järjestelmän sääntö estää sallimasta tai katsomasta sijainti- ja " "paikkatietoja" #: data/org.freedesktop.ModemManager1.policy.in.in:76 msgid "Query and utilize network information and services" msgstr "Tarkista ja käytä verkon tietoja ja palveluita" #: data/org.freedesktop.ModemManager1.policy.in.in:77 msgid "" "System policy prevents querying or utilizing network information and " "services." msgstr "" "Järjestelmän sääntö estää tarkistamasta tai käyttämästä verkon tietoja ja " "palveluita." #: data/org.freedesktop.ModemManager1.policy.in.in:85 msgid "Query and manage firmware on a mobile broadband device" msgstr "Tarkista ja hallitse mobiilin laajakaistalaitteen laiteohjelmistoa" #: data/org.freedesktop.ModemManager1.policy.in.in:86 msgid "System policy prevents querying or managing this device's firmware." msgstr "" "Järjestelmän sääntö estää tarkistamasta ja hallitsemasta tämän laitteen " "laiteohjelmistoa." #: src/mm-sleep-monitor-systemd.c:125 msgid "ModemManager needs to reset devices" msgstr "Modem Managerin pitää uudelleen käynnistää laitteita" ModemManager-1.23.4-dev/po/fr.po000066400000000000000000000107751456466623000163240ustar00rootroot00000000000000# ModemManager translation to French. # Copyright (C) 2018 Listed translators # This file is distributed under the same license as the ModemManager package. # Claude Paroz , 2018 # #, fuzzy msgid "" msgstr "" "Project-Id-Version: ModemManager main\n" "Report-Msgid-Bugs-To: https://gitlab.freedesktop.org/mobile-broadband/" "ModemManager/issues\n" "POT-Creation-Date: 2022-07-16 14:32+0200\n" "PO-Revision-Date: 2018-08-18 16:17+0200\n" "Last-Translator: Claude Paroz \n" "Language-Team: French \n" "Language: fr\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" #: data/org.freedesktop.ModemManager1.policy.in.in:13 msgid "Control the Modem Manager daemon" msgstr "Contrôle le service Modem Manager" #: data/org.freedesktop.ModemManager1.policy.in.in:14 msgid "System policy prevents controlling the Modem Manager." msgstr "La politique système empêche le contrôle de Modem Manager." #: data/org.freedesktop.ModemManager1.policy.in.in:22 msgid "Unlock and control a mobile broadband device" msgstr "Déverrouiller et contrôler un périphérique mobile à large bande" #: data/org.freedesktop.ModemManager1.policy.in.in:23 msgid "" "System policy prevents unlocking or controlling the mobile broadband device." msgstr "" "La politique système empêche le verrouillage et le contrôle d’un " "périphérique mobile à large bande." #: data/org.freedesktop.ModemManager1.policy.in.in:31 msgid "Add, modify, and delete mobile broadband contacts" msgstr "Ajouter, modifier et supprimer des contacts de connexions mobiles" #: data/org.freedesktop.ModemManager1.policy.in.in:32 msgid "" "System policy prevents adding, modifying, or deleting this device's contacts." msgstr "" "La politique système empêche l’ajout, la modification ou la suppression des " "contacts de cet appareil." #: data/org.freedesktop.ModemManager1.policy.in.in:40 msgid "Send, save, modify, and delete text messages" msgstr "Envoyer, enregistrer, modifier et supprimer des messages textuels" #: data/org.freedesktop.ModemManager1.policy.in.in:41 msgid "" "System policy prevents sending or manipulating this device's text messages." msgstr "" "La politique système empêche l’envoi ou la manipulation des messages " "textuels ce cet appareil." #: data/org.freedesktop.ModemManager1.policy.in.in:49 msgid "Accept incoming voice calls or start outgoing voice calls." msgstr "" "Accepter des appels vocaux entrants ou initier des appels vocaux sortants." #: data/org.freedesktop.ModemManager1.policy.in.in:50 msgid "System policy prevents voice calls." msgstr "La politique système empêche les appels vocaux." #: data/org.freedesktop.ModemManager1.policy.in.in:58 msgid "Query network time and timezone information" msgstr "" #: data/org.freedesktop.ModemManager1.policy.in.in:59 #, fuzzy msgid "System policy prevents querying network time information." msgstr "" "La politique système empêche l’interrogation et l’utilisation des " "informations et des services du réseau." #: data/org.freedesktop.ModemManager1.policy.in.in:67 msgid "Enable and view geographic location and positioning information" msgstr "Activer et voir les informations de position géographique" #: data/org.freedesktop.ModemManager1.policy.in.in:68 msgid "" "System policy prevents enabling or viewing geographic location information." msgstr "" "La politique système empêche d’activer ou de voir les informations de " "position géographique." #: data/org.freedesktop.ModemManager1.policy.in.in:76 msgid "Query and utilize network information and services" msgstr "Interroger et utiliser les informations et services du réseau" #: data/org.freedesktop.ModemManager1.policy.in.in:77 msgid "" "System policy prevents querying or utilizing network information and " "services." msgstr "" "La politique système empêche l’interrogation et l’utilisation des " "informations et des services du réseau." #: data/org.freedesktop.ModemManager1.policy.in.in:85 msgid "Query and manage firmware on a mobile broadband device" msgstr "Interroger et gérer le matériel d’un périphérique mobile à large bande" #: data/org.freedesktop.ModemManager1.policy.in.in:86 msgid "System policy prevents querying or managing this device's firmware." msgstr "" "La politique système empêche l’interrogation et la gestion du matériel de ce " "périphérique." #: src/mm-sleep-monitor-systemd.c:125 msgid "ModemManager needs to reset devices" msgstr "ModemManager a besoin de réinitialiser les périphériques" ModemManager-1.23.4-dev/po/fur.po000066400000000000000000000110621456466623000164770ustar00rootroot00000000000000# Friulian translation for ModemManager. # Copyright (C) 2018 ModemManager's COPYRIGHT HOLDER # This file is distributed under the same license as the ModemManager package. # Fabio Tomat , 2018. # msgid "" msgstr "" "Project-Id-Version: ModemManager main\n" "Report-Msgid-Bugs-To: https://gitlab.freedesktop.org/mobile-broadband/" "ModemManager/issues\n" "POT-Creation-Date: 2022-07-16 14:32+0200\n" "PO-Revision-Date: 2018-03-25 17:20+0200\n" "Last-Translator: Fabio Tomat \n" "Language-Team: Friulian \n" "Language: fur\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Generator: Poedit 2.0.6\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" #: data/org.freedesktop.ModemManager1.policy.in.in:13 msgid "Control the Modem Manager daemon" msgstr "Controle il demoni di Modem Manager" #: data/org.freedesktop.ModemManager1.policy.in.in:14 msgid "System policy prevents controlling the Modem Manager." msgstr "La politiche dal sisteme e impedìs il control di Modem Manager." #: data/org.freedesktop.ModemManager1.policy.in.in:22 msgid "Unlock and control a mobile broadband device" msgstr "Sbloche e controle un dispositîf a bande largje mobile" #: data/org.freedesktop.ModemManager1.policy.in.in:23 msgid "" "System policy prevents unlocking or controlling the mobile broadband device." msgstr "" "La politiche dal sisteme e impedìs di sblocâ o controlâ il dispositîf a " "bande largje mobile." #: data/org.freedesktop.ModemManager1.policy.in.in:31 msgid "Add, modify, and delete mobile broadband contacts" msgstr "Zonte, modifiche e elimine i contats de bande largje mobile" #: data/org.freedesktop.ModemManager1.policy.in.in:32 msgid "" "System policy prevents adding, modifying, or deleting this device's contacts." msgstr "" "La politiche dal sisteme e impedìs di zontâ, modificâ o eliminâ i contats di " "chest dispositîf." #: data/org.freedesktop.ModemManager1.policy.in.in:40 msgid "Send, save, modify, and delete text messages" msgstr "Invie, salve, modifiche e elimine i messaçs di test" #: data/org.freedesktop.ModemManager1.policy.in.in:41 msgid "" "System policy prevents sending or manipulating this device's text messages." msgstr "" "La politiche dal sisteme e impedìs di inviâ o manipolâ i messaçs di test di " "chest dispositîf." #: data/org.freedesktop.ModemManager1.policy.in.in:49 msgid "Accept incoming voice calls or start outgoing voice calls." msgstr "" "Acete lis clamadis vocâls in jentrade o tacâ clamadis vocâls in jessude." #: data/org.freedesktop.ModemManager1.policy.in.in:50 msgid "System policy prevents voice calls." msgstr "La politiche dal sisteme e impedìs lis clamadis vocâls." #: data/org.freedesktop.ModemManager1.policy.in.in:58 msgid "Query network time and timezone information" msgstr "" #: data/org.freedesktop.ModemManager1.policy.in.in:59 #, fuzzy msgid "System policy prevents querying network time information." msgstr "" "La politiche dal sisteme e impedìs la interogazion e la utilizazion di " "informazions di rêt e servizis." #: data/org.freedesktop.ModemManager1.policy.in.in:67 msgid "Enable and view geographic location and positioning information" msgstr "" "Abilite e viôt la posizion gjeografiche e lis informazions su la posizion" #: data/org.freedesktop.ModemManager1.policy.in.in:68 msgid "" "System policy prevents enabling or viewing geographic location information." msgstr "" "La politiche dal sisteme e impedìs di abilitâ o viodi lis informazions su la " "posizion gjeografiche." #: data/org.freedesktop.ModemManager1.policy.in.in:76 msgid "Query and utilize network information and services" msgstr "Interoghe e dopre lis informazions di rêt e i servizis" #: data/org.freedesktop.ModemManager1.policy.in.in:77 msgid "" "System policy prevents querying or utilizing network information and " "services." msgstr "" "La politiche dal sisteme e impedìs la interogazion e la utilizazion di " "informazions di rêt e servizis." #: data/org.freedesktop.ModemManager1.policy.in.in:85 msgid "Query and manage firmware on a mobile broadband device" msgstr "" "Interoghe e gjestìs il firmware suntun dispositîf a bande largje mobile" #: data/org.freedesktop.ModemManager1.policy.in.in:86 msgid "System policy prevents querying or managing this device's firmware." msgstr "" "La politiche dal sisteme e impedìs di interogâ o gjestî il firmware di chest " "dispositîf." #: src/mm-sleep-monitor-systemd.c:125 msgid "ModemManager needs to reset devices" msgstr "ModemManager al à bisugne di ristabilî/azerâ i dispositîfs" ModemManager-1.23.4-dev/po/gl.po000066400000000000000000000107411456466623000163100ustar00rootroot00000000000000# Galician translation for ModemManager. # Copyright (C) 2021 ModemManager's COPYRIGHT HOLDER # This file is distributed under the same license as the ModemManager package. # Fran Dieguez , 2021. # msgid "" msgstr "" "Project-Id-Version: ModemManager master\n" "Report-Msgid-Bugs-To: https://gitlab.freedesktop.org/mobile-broadband/" "ModemManager/issues\n" "POT-Creation-Date: 2022-07-16 14:32+0200\n" "PO-Revision-Date: 2021-11-19 22:43+0100\n" "Last-Translator: Fran Dieguez \n" "Language-Team: Galician \n" "Language: gl\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1)\n" "X-Generator: Gtranslator 40.0\n" #: data/org.freedesktop.ModemManager1.policy.in.in:13 msgid "Control the Modem Manager daemon" msgstr "Controla o «daemon» do Modem Manager." #: data/org.freedesktop.ModemManager1.policy.in.in:14 msgid "System policy prevents controlling the Modem Manager." msgstr "A normativa do sistema prevén o control de Modem Manager." #: data/org.freedesktop.ModemManager1.policy.in.in:22 msgid "Unlock and control a mobile broadband device" msgstr "Desbloquear e controlar un dispositivo de banda larga móvil" #: data/org.freedesktop.ModemManager1.policy.in.in:23 msgid "" "System policy prevents unlocking or controlling the mobile broadband device." msgstr "" "A normativa do sistema prevén desbloquear ou controlar un dispositivo de " "banda larga móvil." #: data/org.freedesktop.ModemManager1.policy.in.in:31 msgid "Add, modify, and delete mobile broadband contacts" msgstr "Engadir, modificar e eliminar os contactos de banda larga móvil." #: data/org.freedesktop.ModemManager1.policy.in.in:32 msgid "" "System policy prevents adding, modifying, or deleting this device's contacts." msgstr "" "A normativa do sistema prevén engadir, modificar ou eliminar os contactos " "deste dispositivo." #: data/org.freedesktop.ModemManager1.policy.in.in:40 msgid "Send, save, modify, and delete text messages" msgstr "Enviar, gardar, modificar e eliminar mensaxes de texto" #: data/org.freedesktop.ModemManager1.policy.in.in:41 msgid "" "System policy prevents sending or manipulating this device's text messages." msgstr "" "A normativa do sistema prevén o envío e manipulación dos mensaxes de texto " "do dispositivo." #: data/org.freedesktop.ModemManager1.policy.in.in:49 msgid "Accept incoming voice calls or start outgoing voice calls." msgstr "Aceptar chamadas de voz entrantes ou comezar chamadas de voz saíntes." #: data/org.freedesktop.ModemManager1.policy.in.in:50 msgid "System policy prevents voice calls." msgstr "A normativa do sistema prevén as chamadas de voz." #: data/org.freedesktop.ModemManager1.policy.in.in:58 msgid "Query network time and timezone information" msgstr "Consultar informacións de hora e fuso horario" #: data/org.freedesktop.ModemManager1.policy.in.in:59 msgid "System policy prevents querying network time information." msgstr "" "A normativa do sistema prevén a consulta da información da hora da rede." #: data/org.freedesktop.ModemManager1.policy.in.in:67 msgid "Enable and view geographic location and positioning information" msgstr "" "Activar e ver a información de localización xeográfica e posicionamento" #: data/org.freedesktop.ModemManager1.policy.in.in:68 msgid "" "System policy prevents enabling or viewing geographic location information." msgstr "" "A normativa do sistema prevén activar ou ver a información de localización " "xeográfica." #: data/org.freedesktop.ModemManager1.policy.in.in:76 msgid "Query and utilize network information and services" msgstr "Consultar e usar a información e servizos da ree" #: data/org.freedesktop.ModemManager1.policy.in.in:77 msgid "" "System policy prevents querying or utilizing network information and " "services." msgstr "" "A normativa do sistema prevén consultar ou usar a información e servizos da " "rede." #: data/org.freedesktop.ModemManager1.policy.in.in:85 msgid "Query and manage firmware on a mobile broadband device" msgstr "Consultar e xeraionar o firmware nun dispositivo de banda larga móvil" #: data/org.freedesktop.ModemManager1.policy.in.in:86 msgid "System policy prevents querying or managing this device's firmware." msgstr "" "A normativa do sistema prevén a consulta e xesión do firmware do dispositivo." #: src/mm-sleep-monitor-systemd.c:125 msgid "ModemManager needs to reset devices" msgstr "ModemManager precisa reiniciar os dispositivos" ModemManager-1.23.4-dev/po/he.po000066400000000000000000000112611456466623000163000ustar00rootroot00000000000000# Hebrew translation of Modem Manager # Copyright (C) 2020 Free Software Foundation, Inc. # This file is distributed under the same license as the Modem Manager package. # # Yaron Shahrabani , 2020. msgid "" msgstr "" "Project-Id-Version: Modem Manager\n" "Report-Msgid-Bugs-To: https://gitlab.freedesktop.org/mobile-broadband/" "ModemManager/issues\n" "POT-Creation-Date: 2022-07-16 14:32+0200\n" "PO-Revision-Date: 2020-02-08 18:14+0200\n" "Last-Translator: Yaron Shahrabani \n" "Language-Team: Hebrew \n" "Language: he\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" "X-Generator: GitLab Editor\n" #: data/org.freedesktop.ModemManager1.policy.in.in:13 msgid "Control the Modem Manager daemon" msgstr "שליטה בסוכן מנהל המודמים" #: data/org.freedesktop.ModemManager1.policy.in.in:14 msgid "System policy prevents controlling the Modem Manager." msgstr "מדיניות מערכת מונעת שליטה במנהל המודמים." #: data/org.freedesktop.ModemManager1.policy.in.in:22 msgid "Unlock and control a mobile broadband device" msgstr "שחרור ושליטה במכשיר לרשת תקשורת סלולרית" #: data/org.freedesktop.ModemManager1.policy.in.in:23 msgid "" "System policy prevents unlocking or controlling the mobile broadband device." msgstr "מדיניות המערכת מונעת שחרור או שליטה בהתקן רשת התקשורת האלחוטית." #: data/org.freedesktop.ModemManager1.policy.in.in:31 msgid "Add, modify, and delete mobile broadband contacts" msgstr "ניתן להוסיף, לערוך ולמחוק אנשי קשר בהתקן רשת תקשורת אלחוטית" #: data/org.freedesktop.ModemManager1.policy.in.in:32 msgid "" "System policy prevents adding, modifying, or deleting this device's contacts." msgstr "מדיניות המערכת מונעת הוספה, עריכה או מחיקה של אנשי הקשר במכשיר הזה." #: data/org.freedesktop.ModemManager1.policy.in.in:40 msgid "Send, save, modify, and delete text messages" msgstr "שליחה, שמירה, עריכה ומחיקה של הודעות טקסט" #: data/org.freedesktop.ModemManager1.policy.in.in:41 msgid "" "System policy prevents sending or manipulating this device's text messages." msgstr "מדיניות המערכת מונעת שליחה או עריכה של המסרונים בהתקן הזה." #: data/org.freedesktop.ModemManager1.policy.in.in:49 msgid "Accept incoming voice calls or start outgoing voice calls." msgstr "לקבל שיחות נכנסות או להוציא שיחות." #: data/org.freedesktop.ModemManager1.policy.in.in:50 msgid "System policy prevents voice calls." msgstr "מדיניות המערכת מונעת שיחות קוליות." #: data/org.freedesktop.ModemManager1.policy.in.in:58 msgid "Query network time and timezone information" msgstr "לתשאל את פרטי השעה ואזור הזמן מהרשת" #: data/org.freedesktop.ModemManager1.policy.in.in:59 msgid "System policy prevents querying network time information." msgstr "מדיניות מערכת מונעת תשאול פרטי זמן מהרשת." #: data/org.freedesktop.ModemManager1.policy.in.in:67 msgid "Enable and view geographic location and positioning information" msgstr "להפעיל ולהציג פרטי מיקום גאוגרפי" #: data/org.freedesktop.ModemManager1.policy.in.in:68 msgid "" "System policy prevents enabling or viewing geographic location information." msgstr "מדיניות מערכת מונעת הפעלה או הצגה של פרטי מיקום גאוגרפי." #: data/org.freedesktop.ModemManager1.policy.in.in:76 msgid "Query and utilize network information and services" msgstr "תשאול ושימוש בשירותי ומידע על הרשת" #: data/org.freedesktop.ModemManager1.policy.in.in:77 msgid "" "System policy prevents querying or utilizing network information and " "services." msgstr "מדיניות המערכת מונעת תשאול או שימוש במידע על הרשת או שירותים שלה." #: data/org.freedesktop.ModemManager1.policy.in.in:85 msgid "Query and manage firmware on a mobile broadband device" msgstr "תשאול וניהול קושחה על התקן רשת תקשורת סלולרית" #: data/org.freedesktop.ModemManager1.policy.in.in:86 msgid "System policy prevents querying or managing this device's firmware." msgstr "מדיניות המערכת מונעת תשאול או ניהול של קושחת ההתקן הזה." #: src/mm-sleep-monitor-systemd.c:125 msgid "ModemManager needs to reset devices" msgstr "על ModemManager לאפס התקנים" ModemManager-1.23.4-dev/po/hu.po000066400000000000000000000114711456466623000163230ustar00rootroot00000000000000# Hungarian translation for modemmanager. # Copyright (C) 2017, 2021. Free Software Foundation, Inc. # This file is distributed under the same license as the modemmanager package. # # Gabor Kelemen , 2017. # Balázs Úr , 2021. msgid "" msgstr "" "Project-Id-Version: modemmanager main\n" "Report-Msgid-Bugs-To: https://gitlab.freedesktop.org/mobile-broadband/" "ModemManager/issues\n" "POT-Creation-Date: 2022-07-16 14:32+0200\n" "PO-Revision-Date: 2021-03-17 00:52+0100\n" "Last-Translator: Balázs Úr \n" "Language-Team: Hungarian \n" "Language: hu\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" "X-Generator: Lokalize 19.12.3\n" #: data/org.freedesktop.ModemManager1.policy.in.in:13 msgid "Control the Modem Manager daemon" msgstr "Modemkezelő démon vezérlése" #: data/org.freedesktop.ModemManager1.policy.in.in:14 msgid "System policy prevents controlling the Modem Manager." msgstr "A rendszer házirendje nem teszi lehetővé a Modemkezelő vezérlését." #: data/org.freedesktop.ModemManager1.policy.in.in:22 msgid "Unlock and control a mobile broadband device" msgstr "Mobil széles sávú készülék feloldása és vezérlése" #: data/org.freedesktop.ModemManager1.policy.in.in:23 msgid "" "System policy prevents unlocking or controlling the mobile broadband device." msgstr "" "A rendszer házirendje nem teszi lehetővé a mobil széles sávú készülék " "feloldását vagy vezérlését." #: data/org.freedesktop.ModemManager1.policy.in.in:31 msgid "Add, modify, and delete mobile broadband contacts" msgstr "Névjegyek hozzáadása, módosítása és eltávolítása" #: data/org.freedesktop.ModemManager1.policy.in.in:32 msgid "" "System policy prevents adding, modifying, or deleting this device's contacts." msgstr "" "A rendszer házirendje nem teszi lehetővé ezen az eszközön a névjegyek " "hozzáadását, módosítását és eltávolítását." #: data/org.freedesktop.ModemManager1.policy.in.in:40 msgid "Send, save, modify, and delete text messages" msgstr "Szöveges üzenetek küldése, mentése, módosítása és törlése" #: data/org.freedesktop.ModemManager1.policy.in.in:41 msgid "" "System policy prevents sending or manipulating this device's text messages." msgstr "" "A rendszer házirendje nem teszi lehetővé a szöveges üzenetek küldését vagy " "kezelését." #: data/org.freedesktop.ModemManager1.policy.in.in:49 msgid "Accept incoming voice calls or start outgoing voice calls." msgstr "Bejövő hívások fogadása vagy kimenő hívások indítása." #: data/org.freedesktop.ModemManager1.policy.in.in:50 msgid "System policy prevents voice calls." msgstr "A rendszer házirendje nem teszi lehetővé a hívásokat." #: data/org.freedesktop.ModemManager1.policy.in.in:58 msgid "Query network time and timezone information" msgstr "Hálózati idő és időzóna-információk lekérdezése" #: data/org.freedesktop.ModemManager1.policy.in.in:59 msgid "System policy prevents querying network time information." msgstr "" "A rendszer házirendje nem teszi lehetővé a hálózati idő információinak " "lekérdezését." #: data/org.freedesktop.ModemManager1.policy.in.in:67 msgid "Enable and view geographic location and positioning information" msgstr "" "Földrajzi helyzetmeghatározás bekapcsolása és az információk megtekintése" #: data/org.freedesktop.ModemManager1.policy.in.in:68 msgid "" "System policy prevents enabling or viewing geographic location information." msgstr "" "A rendszer házirendje nem teszi lehetővé a földrajzi helyzetmeghatározás " "bekapcsolását vagy az információk megtekintését." #: data/org.freedesktop.ModemManager1.policy.in.in:76 msgid "Query and utilize network information and services" msgstr "Hálózati információk és szolgáltatások lekérdezése és használata" #: data/org.freedesktop.ModemManager1.policy.in.in:77 msgid "" "System policy prevents querying or utilizing network information and " "services." msgstr "" "A rendszer házirendje nem teszi lehetővé a hálózati információk és " "szolgáltatások lekérdezését és használatát." #: data/org.freedesktop.ModemManager1.policy.in.in:85 msgid "Query and manage firmware on a mobile broadband device" msgstr "Firmware lekérdezése és kezelése a mobil széles sávú eszközön" #: data/org.freedesktop.ModemManager1.policy.in.in:86 msgid "System policy prevents querying or managing this device's firmware." msgstr "" "A rendszer lekérdezése és használata lehetővé a firmware lekérdezését és " "kezelését az eszközön." #: src/mm-sleep-monitor-systemd.c:125 msgid "ModemManager needs to reset devices" msgstr "A Modemkezelőnek alapállapotba kell állítania eszközöket" ModemManager-1.23.4-dev/po/id.po000066400000000000000000000104051456466623000162770ustar00rootroot00000000000000# Indonesian translation for ModemManager. # Copyright (C) 2018 ModemManager's COPYRIGHT HOLDER # This file is distributed under the same license as the ModemManager package. # Andika Triwidada , 2018. # msgid "" msgstr "" "Project-Id-Version: ModemManager main\n" "Report-Msgid-Bugs-To: https://gitlab.freedesktop.org/mobile-broadband/" "ModemManager/issues\n" "POT-Creation-Date: 2022-07-16 14:32+0200\n" "PO-Revision-Date: 2020-03-31 18:14+0700\n" "Last-Translator: Andika Triwidada \n" "Language-Team: Indonesian \n" "Language: id\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Generator: Poedit 2.3\n" #: data/org.freedesktop.ModemManager1.policy.in.in:13 msgid "Control the Modem Manager daemon" msgstr "Mengendalikan daemon Manajer Modem" #: data/org.freedesktop.ModemManager1.policy.in.in:14 msgid "System policy prevents controlling the Modem Manager." msgstr "Kebijakan sistem mencegah pengendalian Manajer Modem." #: data/org.freedesktop.ModemManager1.policy.in.in:22 msgid "Unlock and control a mobile broadband device" msgstr "Buka kunci dan kendalikan suatu peranti data seluler" #: data/org.freedesktop.ModemManager1.policy.in.in:23 msgid "" "System policy prevents unlocking or controlling the mobile broadband device." msgstr "" "Kebijakan sistem mencegah membuka kunci atau mengendalikan peranti data " "seluler." #: data/org.freedesktop.ModemManager1.policy.in.in:31 msgid "Add, modify, and delete mobile broadband contacts" msgstr "Tambah, ubah, dan hapus kontak data seluler" #: data/org.freedesktop.ModemManager1.policy.in.in:32 msgid "" "System policy prevents adding, modifying, or deleting this device's contacts." msgstr "" "Kebijakan sistem mencegah menambah, mengubah, atau menghapus kontak-kontak " "peranti ini." #: data/org.freedesktop.ModemManager1.policy.in.in:40 msgid "Send, save, modify, and delete text messages" msgstr "Kirim, simpan, ubah, dan hapus pesan-pesan teks" #: data/org.freedesktop.ModemManager1.policy.in.in:41 msgid "" "System policy prevents sending or manipulating this device's text messages." msgstr "" "Kebijakan sistem mencegah pengiriman atau manipulasi pesan-pesan teks " "peranti ini." #: data/org.freedesktop.ModemManager1.policy.in.in:49 msgid "Accept incoming voice calls or start outgoing voice calls." msgstr "Terima panggilan suara masuk atau mulai pemanggilan suara keluar." #: data/org.freedesktop.ModemManager1.policy.in.in:50 msgid "System policy prevents voice calls." msgstr "Kebijakan sistem mencegah panggilan suara." #: data/org.freedesktop.ModemManager1.policy.in.in:58 msgid "Query network time and timezone information" msgstr "Tanyakan waktu jaringan dan informasi zona waktu" #: data/org.freedesktop.ModemManager1.policy.in.in:59 msgid "System policy prevents querying network time information." msgstr "Kebijakan sistem mencegah kuiri informasi waktu jaringan." #: data/org.freedesktop.ModemManager1.policy.in.in:67 msgid "Enable and view geographic location and positioning information" msgstr "Fungsikan dan tilik lokasi geografis dan informasi posisi" #: data/org.freedesktop.ModemManager1.policy.in.in:68 msgid "" "System policy prevents enabling or viewing geographic location information." msgstr "" "Kebijakan sistem mencegah memfungsikan atau menilik informasi lokasi " "geografis." #: data/org.freedesktop.ModemManager1.policy.in.in:76 msgid "Query and utilize network information and services" msgstr "Kuiri dan manfaatkan layanan dan informasi jaringan" #: data/org.freedesktop.ModemManager1.policy.in.in:77 msgid "" "System policy prevents querying or utilizing network information and " "services." msgstr "" "Kebijakan sistem mencegah kuiri atau pemanfaatan layanan dan informasi " "jaringan." #: data/org.freedesktop.ModemManager1.policy.in.in:85 msgid "Query and manage firmware on a mobile broadband device" msgstr "Kuiri dan kelola firmware pada suatu peranti data seluler" #: data/org.freedesktop.ModemManager1.policy.in.in:86 msgid "System policy prevents querying or managing this device's firmware." msgstr "Kebijakan sistem mencegah kuiri atau pengelolaan firmware peranti ini." #: src/mm-sleep-monitor-systemd.c:125 msgid "ModemManager needs to reset devices" msgstr "ModemManager perlu mereset peranti" ModemManager-1.23.4-dev/po/it.po000066400000000000000000000110631456466623000163200ustar00rootroot00000000000000# Italian translation for ModemManager. # Copyright (C) 2018, 2020 ModemManager's COPYRIGHT HOLDER # This file is distributed under the same license as the ModemManager package. # Milo Casagrande , 2018, 2020. # msgid "" msgstr "" "Project-Id-Version: ModemManager main\n" "Report-Msgid-Bugs-To: https://gitlab.freedesktop.org/mobile-broadband/" "ModemManager/issues\n" "POT-Creation-Date: 2022-07-16 14:32+0200\n" "PO-Revision-Date: 2020-03-10 20:15+0100\n" "Last-Translator: Milo Casagrande \n" "Language-Team: Italian \n" "Language: it\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" "X-Generator: Poedit 2.2.4\n" #: data/org.freedesktop.ModemManager1.policy.in.in:13 msgid "Control the Modem Manager daemon" msgstr "Controlla il demone di «Modem Manager»" #: data/org.freedesktop.ModemManager1.policy.in.in:14 msgid "System policy prevents controlling the Modem Manager." msgstr "La politica di sistema impedisce il controllo di «Modem Manager»" #: data/org.freedesktop.ModemManager1.policy.in.in:22 msgid "Unlock and control a mobile broadband device" msgstr "Sblocca e controlla un dispositivo mobile a banda larga" #: data/org.freedesktop.ModemManager1.policy.in.in:23 msgid "" "System policy prevents unlocking or controlling the mobile broadband device." msgstr "" "La politica di sistema impedisce di sbloccare o controllare il dispositivo " "mobile a banda larga." #: data/org.freedesktop.ModemManager1.policy.in.in:31 msgid "Add, modify, and delete mobile broadband contacts" msgstr "Aggiunge, modifica ed elimina contatti mobili a banda larga" #: data/org.freedesktop.ModemManager1.policy.in.in:32 msgid "" "System policy prevents adding, modifying, or deleting this device's contacts." msgstr "" "La politica di sistema impedisce di aggiungere, modificare o eliminare i " "contatti di questo dispositivo." #: data/org.freedesktop.ModemManager1.policy.in.in:40 msgid "Send, save, modify, and delete text messages" msgstr "Invia, salva, modifica ed elimina messaggi di testo" #: data/org.freedesktop.ModemManager1.policy.in.in:41 msgid "" "System policy prevents sending or manipulating this device's text messages." msgstr "" "La politica di sistema impedisce di inviare o manipolare i messaggi di testo " "di questo dispositivo." #: data/org.freedesktop.ModemManager1.policy.in.in:49 msgid "Accept incoming voice calls or start outgoing voice calls." msgstr "Accetta chiamate vocali in arrivo o avvia chiamate vocali." #: data/org.freedesktop.ModemManager1.policy.in.in:50 msgid "System policy prevents voice calls." msgstr "La politica di sistema impedisce di effettuare chiamate vocali." #: data/org.freedesktop.ModemManager1.policy.in.in:58 msgid "Query network time and timezone information" msgstr "Interroga le informazioni sull'ora di rete e sul fuso orario" #: data/org.freedesktop.ModemManager1.policy.in.in:59 msgid "System policy prevents querying network time information." msgstr "" "La politica di sistema impedisce di interrogare le informazioni sull'ora di " "rete" #: data/org.freedesktop.ModemManager1.policy.in.in:67 msgid "Enable and view geographic location and positioning information" msgstr "" "Abilita e visualizza informazioni di geolocalizzazione e posizionamento" #: data/org.freedesktop.ModemManager1.policy.in.in:68 msgid "" "System policy prevents enabling or viewing geographic location information." msgstr "" "La politica di sistema impedisce di abilitare o visualizzare informazioni di " "geolocalizzazione." #: data/org.freedesktop.ModemManager1.policy.in.in:76 msgid "Query and utilize network information and services" msgstr "Interroga e utilizza informazioni e servizi della rete" #: data/org.freedesktop.ModemManager1.policy.in.in:77 msgid "" "System policy prevents querying or utilizing network information and " "services." msgstr "" "La politica di sistema impedisce di interrogare o di utilizzare le " "informazioni e i servizi della rete." #: data/org.freedesktop.ModemManager1.policy.in.in:85 msgid "Query and manage firmware on a mobile broadband device" msgstr "" "Interroga e gestisce il firmware su un dispositivo mobile a banda larga" #: data/org.freedesktop.ModemManager1.policy.in.in:86 msgid "System policy prevents querying or managing this device's firmware." msgstr "" "La politica di sistema impedisce di interrogare o gestire il firmware di " "questo dispositivo." #: src/mm-sleep-monitor-systemd.c:125 msgid "ModemManager needs to reset devices" msgstr "ModemManager deve reimpostare i dispositivi" ModemManager-1.23.4-dev/po/ka.po000066400000000000000000000143451456466623000163050ustar00rootroot00000000000000# Georgian translation for ModemManager. # Copyright (C) 2022 ModemManager's COPYRIGHT HOLDER # This file is distributed under the same license as the ModemManager package. # Temuri Doghonadze , 2022 # msgid "" msgstr "" "Project-Id-Version: \n" "Report-Msgid-Bugs-To: https://gitlab.freedesktop.org/mobile-broadband/" "ModemManager/issues\n" "POT-Creation-Date: 2022-07-25 15:26+0000\n" "PO-Revision-Date: 2022-07-25 21:43+0200\n" "Last-Translator: Temuri Doghonadze \n" "Language-Team: \n" "Language: ka\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" "X-Generator: Poedit 3.1.1\n" #: data/org.freedesktop.ModemManager1.policy.in.in:13 msgid "Control the Modem Manager daemon" msgstr "ModemManager-ის დემონის კონტროლი" #: data/org.freedesktop.ModemManager1.policy.in.in:14 msgid "System policy prevents controlling the Modem Manager." msgstr "სისტემის პოლიტიკა გიკრძალავთ ModemManager-ის კონტროლს." #: data/org.freedesktop.ModemManager1.policy.in.in:22 msgid "Unlock and control a mobile broadband device" msgstr "განბლოკეთ და აკონტროლეთ მობილური ფართოზოლოვანი მოწყობილობა" #: data/org.freedesktop.ModemManager1.policy.in.in:23 msgid "" "System policy prevents unlocking or controlling the mobile broadband device." msgstr "" "სისტემის პოლიტიკა გიკრძალავთ მობილური ფართოზოლოვანი მოწყობილობის განბლოკვას " "და კონტროლს." #: data/org.freedesktop.ModemManager1.policy.in.in:31 msgid "Add, modify, and delete mobile broadband contacts" msgstr "დაამატეთ, ჩაასწორეთ და წაშალეთ მობილური კონტაქტები" #: data/org.freedesktop.ModemManager1.policy.in.in:32 msgid "" "System policy prevents adding, modifying, or deleting this device's contacts." msgstr "" "სისტემის პოლიტიკა გიკრძალავთ ამ მოწყობილობის კონტაქტებიდაამატოთ, ჩაასწოროთ " "ან წაშალოთ." #: data/org.freedesktop.ModemManager1.policy.in.in:40 msgid "Send, save, modify, and delete text messages" msgstr "გააგზავნეთ, შეინახეთ, ჩაასწორეთ და წაშალეთ ტექსტური შეტყობინებები" #: data/org.freedesktop.ModemManager1.policy.in.in:41 msgid "" "System policy prevents sending or manipulating this device's text messages." msgstr "" "სისტემის პოლიტიკა გიკრძალავთ მოწყობილობის ტექსტური შეტყობინებების გაგზავნას " "და მანიპულაციას." #: data/org.freedesktop.ModemManager1.policy.in.in:49 msgid "Accept incoming voice calls or start outgoing voice calls." msgstr "შემომავალი ხმოვანი ზარების მიღება ან გამავალი ზარის დაწყება." #: data/org.freedesktop.ModemManager1.policy.in.in:50 msgid "System policy prevents voice calls." msgstr "სისტემური პოლიტიკა გიკრძალავთ ხმოვან ზარებს." #: data/org.freedesktop.ModemManager1.policy.in.in:58 msgid "Query network time and timezone information" msgstr "ქსელური დროისა და დროის სარტყლის გამოთხოვა" #: data/org.freedesktop.ModemManager1.policy.in.in:59 msgid "System policy prevents querying network time information." msgstr "სისტემური პოლიტიკა გიკრძალავთ ქსელური დროის ინფორმაციის გამოთხოვას." #: data/org.freedesktop.ModemManager1.policy.in.in:67 msgid "Enable and view geographic location and positioning information" msgstr "ჩართეთ და ნახეთ გეოგრაფიული მდებარეობა" #: data/org.freedesktop.ModemManager1.policy.in.in:68 msgid "" "System policy prevents enabling or viewing geographic location information." msgstr "" "სისტემური პოლიტიკა გიკრძალავთ გეოგრაფიული მდებარეობის ჩართვასა და " "გამოყენებას." #: data/org.freedesktop.ModemManager1.policy.in.in:76 msgid "Query and utilize network information and services" msgstr "ქსელის ინფორმაციის და სერვისების მოთხოვა და გამოყენება" #: data/org.freedesktop.ModemManager1.policy.in.in:77 msgid "" "System policy prevents querying or utilizing network information and " "services." msgstr "სისტემური პოლიტიკა გიკრძალავთ სერვისების პოვნასა და გამოყენებას." #: data/org.freedesktop.ModemManager1.policy.in.in:85 msgid "Query and manage firmware on a mobile broadband device" msgstr "მობილურ მოწყობილობაზე მიკროკოდის გამოთხოვა და მართვა" #: data/org.freedesktop.ModemManager1.policy.in.in:86 msgid "System policy prevents querying or managing this device's firmware." msgstr "" "სისტემური პოლიტიკა გიკრძალავთ მოწყობილობის მიკროკოდის გამოთხოვას და მართვას." #: src/mm-sleep-monitor-systemd.c:125 msgid "ModemManager needs to reset devices" msgstr "ModemManager -ს მოწყობილობის გადატვირთვა სჭირდება" ModemManager-1.23.4-dev/po/lt.po000066400000000000000000000107641456466623000163320ustar00rootroot00000000000000# Lithuanian translation for ModemManager. # Copyright (C) 2019 ModemManager's COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # Moo, 2019. # msgid "" msgstr "" "Project-Id-Version: \n" "Report-Msgid-Bugs-To: https://gitlab.freedesktop.org/mobile-broadband/" "ModemManager/issues\n" "POT-Creation-Date: 2022-07-16 14:32+0200\n" "PO-Revision-Date: 2019-04-13 22:08+0300\n" "Last-Translator: \n" "Language-Team: \n" "Language: lt\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Generator: Poedit 2.2.1\n" "Plural-Forms: nplurals=3; plural=(n%10==1 && (n%100<11 || n%100>19) ? 0 : " "n%10>=2 && n%10<=9 && (n%100<11 || n%100>19) ? 1 : 2);\n" #: data/org.freedesktop.ModemManager1.policy.in.in:13 msgid "Control the Modem Manager daemon" msgstr "Valdyti modemo tvarkytuvės tarnybą" #: data/org.freedesktop.ModemManager1.policy.in.in:14 msgid "System policy prevents controlling the Modem Manager." msgstr "Sistemos politika neleidžia valdyti modemo tvarkytuvę." #: data/org.freedesktop.ModemManager1.policy.in.in:22 msgid "Unlock and control a mobile broadband device" msgstr "Atrakinti ir valdyti mobiliojo plačiajuosčio ryšio įrenginį" #: data/org.freedesktop.ModemManager1.policy.in.in:23 msgid "" "System policy prevents unlocking or controlling the mobile broadband device." msgstr "" "Sistemos politika neleidžia atrakinti ir valdyti mobiliojo plačiajuosčio " "ryšio įrenginį." #: data/org.freedesktop.ModemManager1.policy.in.in:31 msgid "Add, modify, and delete mobile broadband contacts" msgstr "" "Pridėti, modifikuoti bei ištrinti mobiliojo plačiajuosčio ryšio kontaktus" #: data/org.freedesktop.ModemManager1.policy.in.in:32 msgid "" "System policy prevents adding, modifying, or deleting this device's contacts." msgstr "" "Sistemos politika neleidžia pridėti, modifikuoti ar ištrinti šio įrenginio " "kontaktus." #: data/org.freedesktop.ModemManager1.policy.in.in:40 msgid "Send, save, modify, and delete text messages" msgstr "Siųsti, įrašyti, modifikuoti bei ištrinti tekstines žinutes" #: data/org.freedesktop.ModemManager1.policy.in.in:41 msgid "" "System policy prevents sending or manipulating this device's text messages." msgstr "" "Sistemos politika neleidžia siųsti ar valdyti šio įrenginio tekstines " "žinutes." #: data/org.freedesktop.ModemManager1.policy.in.in:49 msgid "Accept incoming voice calls or start outgoing voice calls." msgstr "" "Atsiliepti į gaunamus balso skambučius ar inicijuoti išsiunčiamuosius balso " "skambučius." #: data/org.freedesktop.ModemManager1.policy.in.in:50 msgid "System policy prevents voice calls." msgstr "Sistemos politika neleidžia balso skambučių." #: data/org.freedesktop.ModemManager1.policy.in.in:58 msgid "Query network time and timezone information" msgstr "" #: data/org.freedesktop.ModemManager1.policy.in.in:59 #, fuzzy msgid "System policy prevents querying network time information." msgstr "" "Sistemos politika neleidžia užklausti ar panaudoti tinklo informacija ir " "paslaugas." #: data/org.freedesktop.ModemManager1.policy.in.in:67 msgid "Enable and view geographic location and positioning information" msgstr "Įjungti ir rodyti geografinės vietos bei pozicionavimo informaciją" #: data/org.freedesktop.ModemManager1.policy.in.in:68 msgid "" "System policy prevents enabling or viewing geographic location information." msgstr "" "Sistemos politika neleidžia įjungti ar rodyti geografinės vietos informaciją." #: data/org.freedesktop.ModemManager1.policy.in.in:76 msgid "Query and utilize network information and services" msgstr "Užklausti ir panaudoti tinklo informacija bei paslaugas" #: data/org.freedesktop.ModemManager1.policy.in.in:77 msgid "" "System policy prevents querying or utilizing network information and " "services." msgstr "" "Sistemos politika neleidžia užklausti ar panaudoti tinklo informacija ir " "paslaugas." #: data/org.freedesktop.ModemManager1.policy.in.in:85 msgid "Query and manage firmware on a mobile broadband device" msgstr "" "Užklausti ir tvarkyti programinę aparatinę įrangą mobiliojo plačiajuosčio " "ryšio įrenginyje" #: data/org.freedesktop.ModemManager1.policy.in.in:86 msgid "System policy prevents querying or managing this device's firmware." msgstr "" "Sistemos politika neleidžia užklausti ar tvarkyti šio įrenginio programinę " "aparatinę įrangą." #: src/mm-sleep-monitor-systemd.c:125 msgid "ModemManager needs to reset devices" msgstr "ModemManager turi atstatyti įrenginius" ModemManager-1.23.4-dev/po/meson.build000066400000000000000000000002221456466623000175010ustar00rootroot00000000000000# SPDX-License-Identifier: GPL-2.0-or-later # Copyright (C) 2021 Iñigo Martinez i18n.gettext(mm_name, preset: 'glib') ModemManager-1.23.4-dev/po/nl.po000066400000000000000000000105761456466623000163250ustar00rootroot00000000000000# Dutch translation for ModemManager. # Copyright (C) 2022 ModemManager's COPYRIGHT HOLDER # This file is distributed under the same license as the ModemManager package. # Nathan Follens , 2022. # msgid "" msgstr "" "Project-Id-Version: ModemManager main\n" "Report-Msgid-Bugs-To: https://gitlab.freedesktop.org/mobile-broadband/" "ModemManager/issues\n" "POT-Creation-Date: 2022-07-16 14:32+0200\n" "PO-Revision-Date: 2022-03-27 20:42+0200\n" "Last-Translator: Nathan Follens \n" "Language-Team: Dutch \n" "Language: nl\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" "X-Generator: Poedit 3.0.1\n" #: data/org.freedesktop.ModemManager1.policy.in.in:13 msgid "Control the Modem Manager daemon" msgstr "De Modembeheer-daemon besturen" #: data/org.freedesktop.ModemManager1.policy.in.in:14 msgid "System policy prevents controlling the Modem Manager." msgstr "Systeembeleid verbiedt Modembeheer te besturen." #: data/org.freedesktop.ModemManager1.policy.in.in:22 msgid "Unlock and control a mobile broadband device" msgstr "Een mobiele modem ontgrendelen en besturen" #: data/org.freedesktop.ModemManager1.policy.in.in:23 msgid "" "System policy prevents unlocking or controlling the mobile broadband device." msgstr "" "Systeembeleid verbiedt het ontgrendelen of besturen van de mobiele modem." #: data/org.freedesktop.ModemManager1.policy.in.in:31 msgid "Add, modify, and delete mobile broadband contacts" msgstr "Mobiele contacten toevoegen, bewerken en verwijderen" #: data/org.freedesktop.ModemManager1.policy.in.in:32 msgid "" "System policy prevents adding, modifying, or deleting this device's contacts." msgstr "" "Systeembeleid verbiedt het toevoegen, bewerken of verwijderen van de " "contacten op dit apparaat." #: data/org.freedesktop.ModemManager1.policy.in.in:40 msgid "Send, save, modify, and delete text messages" msgstr "Berichten verzenden, opslaan, bewerken en verwijderen" #: data/org.freedesktop.ModemManager1.policy.in.in:41 msgid "" "System policy prevents sending or manipulating this device's text messages." msgstr "" "Systeembeleid verbiedt het verzenden of manipuleren van de berichten op dit " "apparaat." #: data/org.freedesktop.ModemManager1.policy.in.in:49 msgid "Accept incoming voice calls or start outgoing voice calls." msgstr "" "Inkomende audiogesprekken aanvaarden of uitgaande audiogesprekken starten." #: data/org.freedesktop.ModemManager1.policy.in.in:50 msgid "System policy prevents voice calls." msgstr "Systeembeleid verbiedt audiogesprekken." #: data/org.freedesktop.ModemManager1.policy.in.in:58 msgid "Query network time and timezone information" msgstr "Netwerktijd en tijdszone-informatie ophalen" #: data/org.freedesktop.ModemManager1.policy.in.in:59 msgid "System policy prevents querying network time information." msgstr "" "Systeeminstellingen verbieden het ophalen van informatie over de netwerktijd." #: data/org.freedesktop.ModemManager1.policy.in.in:67 msgid "Enable and view geographic location and positioning information" msgstr "" "Informatie over de geografische locatie en positiebepaling inschakelen en " "bekijken" #: data/org.freedesktop.ModemManager1.policy.in.in:68 msgid "" "System policy prevents enabling or viewing geographic location information." msgstr "" "Systeembeleid verbiedt het inschakelen of bekijken van informatie over de " "geografische locatie." #: data/org.freedesktop.ModemManager1.policy.in.in:76 msgid "Query and utilize network information and services" msgstr "Netwerkinformatie en -diensten ophalen en gebruiken" #: data/org.freedesktop.ModemManager1.policy.in.in:77 msgid "" "System policy prevents querying or utilizing network information and " "services." msgstr "" "Systeembeleid verbiedt het ophalen of gebruiken van netwerkinformatie en -" "diensten." #: data/org.freedesktop.ModemManager1.policy.in.in:85 msgid "Query and manage firmware on a mobile broadband device" msgstr "Firmware op een mobiele modem ophalen en beheren" #: data/org.freedesktop.ModemManager1.policy.in.in:86 msgid "System policy prevents querying or managing this device's firmware." msgstr "" "Systeembeleid verbiedt het ophalen of beheren van de firmware van dit " "apparaat." #: src/mm-sleep-monitor-systemd.c:125 msgid "ModemManager needs to reset devices" msgstr "Modembeheer moet apparaten opnieuw instellen" ModemManager-1.23.4-dev/po/pl.po000066400000000000000000000112661456466623000163240ustar00rootroot00000000000000# Polish translation for ModemManager. # Copyright © 2017-2019 the ModemManager authors. # This file is distributed under the same license as the ModemManager package. # Piotr Drąg , 2017-2019. # Aviary.pl , 2017-2019. # msgid "" msgstr "" "Project-Id-Version: ModemManager\n" "Report-Msgid-Bugs-To: https://gitlab.freedesktop.org/mobile-broadband/" "ModemManager/issues\n" "POT-Creation-Date: 2022-07-16 14:32+0200\n" "PO-Revision-Date: 2019-09-28 15:02+0200\n" "Last-Translator: Piotr Drąg \n" "Language-Team: Polish \n" "Language: pl\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 " "|| n%100>=20) ? 1 : 2);\n" #: data/org.freedesktop.ModemManager1.policy.in.in:13 msgid "Control the Modem Manager daemon" msgstr "Sterowanie usługą ModemManager" #: data/org.freedesktop.ModemManager1.policy.in.in:14 msgid "System policy prevents controlling the Modem Manager." msgstr "Ustawienia systemu uniemożliwiają sterowanie usługą ModemManager." #: data/org.freedesktop.ModemManager1.policy.in.in:22 msgid "Unlock and control a mobile broadband device" msgstr "Odblokowanie i sterowanie urządzeniem komórkowym" #: data/org.freedesktop.ModemManager1.policy.in.in:23 msgid "" "System policy prevents unlocking or controlling the mobile broadband device." msgstr "" "Ustawienia systemu uniemożliwiają odblokowanie lub sterowanie urządzeniem " "komórkowym." #: data/org.freedesktop.ModemManager1.policy.in.in:31 msgid "Add, modify, and delete mobile broadband contacts" msgstr "Dodawanie, modyfikowanie i usuwanie kontaktów urządzenia komórkowego" #: data/org.freedesktop.ModemManager1.policy.in.in:32 msgid "" "System policy prevents adding, modifying, or deleting this device's contacts." msgstr "" "Ustawienia systemu uniemożliwiają dodawanie, modyfikowanie lub usuwanie " "kontaktów tego urządzenia." #: data/org.freedesktop.ModemManager1.policy.in.in:40 msgid "Send, save, modify, and delete text messages" msgstr "Wysyłanie, zapisywanie, modyfikowanie i usuwanie wiadomości SMS" #: data/org.freedesktop.ModemManager1.policy.in.in:41 msgid "" "System policy prevents sending or manipulating this device's text messages." msgstr "" "Ustawienia systemu uniemożliwiają wysyłanie lub manipulowanie wiadomościami " "SMS tego urządzenia." #: data/org.freedesktop.ModemManager1.policy.in.in:49 msgid "Accept incoming voice calls or start outgoing voice calls." msgstr "Przyjmowanie połączeń przychodzących lub dzwonienie" #: data/org.freedesktop.ModemManager1.policy.in.in:50 msgid "System policy prevents voice calls." msgstr "Ustawienia systemu uniemożliwiają dzwonienie." #: data/org.freedesktop.ModemManager1.policy.in.in:58 msgid "Query network time and timezone information" msgstr "Odpytywanie informacji o czasie sieciowym i strefie czasowej" #: data/org.freedesktop.ModemManager1.policy.in.in:59 msgid "System policy prevents querying network time information." msgstr "" "Ustawienia systemu uniemożliwiają odpytywanie informacji o czasie sieciowym." #: data/org.freedesktop.ModemManager1.policy.in.in:67 msgid "Enable and view geographic location and positioning information" msgstr "" "Włączanie i wyświetlanie informacji o położeniu geograficznym " "i pozycjonowaniu" #: data/org.freedesktop.ModemManager1.policy.in.in:68 msgid "" "System policy prevents enabling or viewing geographic location information." msgstr "" "Ustawienia systemu uniemożliwiają włączanie lub wyświetlanie informacji " "o położeniu geograficznym." #: data/org.freedesktop.ModemManager1.policy.in.in:76 msgid "Query and utilize network information and services" msgstr "Odpytywanie i używanie informacji i usług sieciowych" #: data/org.freedesktop.ModemManager1.policy.in.in:77 msgid "" "System policy prevents querying or utilizing network information and " "services." msgstr "" "Ustawienia systemu uniemożliwiają odpytywanie lub używanie informacji " "i usług sieciowych." #: data/org.freedesktop.ModemManager1.policy.in.in:85 msgid "Query and manage firmware on a mobile broadband device" msgstr "" "Odpytywanie i zarządzanie oprogramowaniem sprzętowym urządzenia komórkowego" #: data/org.freedesktop.ModemManager1.policy.in.in:86 msgid "System policy prevents querying or managing this device's firmware." msgstr "" "Ustawienia systemu uniemożliwiają odpytywanie lub zarządzanie " "oprogramowaniem sprzętowym tego urządzenia." #: src/mm-sleep-monitor-systemd.c:125 msgid "ModemManager needs to reset devices" msgstr "Usługa ModemManager musi ponownie uruchomić urządzenia" ModemManager-1.23.4-dev/po/pt_BR.po000066400000000000000000000110421456466623000167070ustar00rootroot00000000000000# Brazilian Portuguese translation for ModemManager. # Copyright (C) 2019 ModemManager's COPYRIGHT HOLDER # This file is distributed under the same license as the ModemManager package. # Rafael Fontenelle , 2017-2019. # msgid "" msgstr "" "Project-Id-Version: ModemManager main\n" "Report-Msgid-Bugs-To: https://gitlab.freedesktop.org/mobile-broadband/" "ModemManager/issues\n" "POT-Creation-Date: 2022-07-16 14:32+0200\n" "PO-Revision-Date: 2019-11-25 00:17-0300\n" "Last-Translator: Rafael Fontenelle \n" "Language-Team: Brazilian Portuguese \n" "Language: pt_BR\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n > 1)\n" "X-Generator: Gtranslator 3.32.0\n" #: data/org.freedesktop.ModemManager1.policy.in.in:13 msgid "Control the Modem Manager daemon" msgstr "Controlar o daemon do ModemManager" #: data/org.freedesktop.ModemManager1.policy.in.in:14 msgid "System policy prevents controlling the Modem Manager." msgstr "A política de sistema impede de controlar o ModemManager." #: data/org.freedesktop.ModemManager1.policy.in.in:22 msgid "Unlock and control a mobile broadband device" msgstr "Desbloquear e controlar um dispositivo de banda larga móvel" #: data/org.freedesktop.ModemManager1.policy.in.in:23 msgid "" "System policy prevents unlocking or controlling the mobile broadband device." msgstr "" "A política de sistema impede de desbloquear ou controlar o dispositivo de " "banda larga móvel." #: data/org.freedesktop.ModemManager1.policy.in.in:31 msgid "Add, modify, and delete mobile broadband contacts" msgstr "Adicionar, modificar e excluir contatos de banda larga móvel" #: data/org.freedesktop.ModemManager1.policy.in.in:32 msgid "" "System policy prevents adding, modifying, or deleting this device's contacts." msgstr "" "A política de sistema impede de adicionar, modificar ou excluir os contatos " "deste dispositivo." #: data/org.freedesktop.ModemManager1.policy.in.in:40 msgid "Send, save, modify, and delete text messages" msgstr "Enviar, salvar, modificar e excluir mensagens de texto" #: data/org.freedesktop.ModemManager1.policy.in.in:41 msgid "" "System policy prevents sending or manipulating this device's text messages." msgstr "" "A política de sistema impede de enviar ou manipular as mensagens de texto " "deste dispositivo." #: data/org.freedesktop.ModemManager1.policy.in.in:49 msgid "Accept incoming voice calls or start outgoing voice calls." msgstr "Aceitar chamadas de voz recebidas ou iniciar chamadas de voz de saída." #: data/org.freedesktop.ModemManager1.policy.in.in:50 msgid "System policy prevents voice calls." msgstr "A política de sistema impede chamadas de voz." #: data/org.freedesktop.ModemManager1.policy.in.in:58 msgid "Query network time and timezone information" msgstr "Consultar informações de hora e fuso horário da rede" #: data/org.freedesktop.ModemManager1.policy.in.in:59 msgid "System policy prevents querying network time information." msgstr "" "A política de sistema impede de consultar informações de horário da rede." #: data/org.freedesktop.ModemManager1.policy.in.in:67 msgid "Enable and view geographic location and positioning information" msgstr "Habilitar e ver informações de posicionamento e localização geográfica" #: data/org.freedesktop.ModemManager1.policy.in.in:68 msgid "" "System policy prevents enabling or viewing geographic location information." msgstr "" "A política de sistema impede de habilitar ou ver informações de localização " "geográfica." #: data/org.freedesktop.ModemManager1.policy.in.in:76 msgid "Query and utilize network information and services" msgstr "Consultar ou utilizar serviços e informações de rede." #: data/org.freedesktop.ModemManager1.policy.in.in:77 msgid "" "System policy prevents querying or utilizing network information and " "services." msgstr "" "A política de sistema impede de consultar ou utilizar serviços e informações " "de rede." #: data/org.freedesktop.ModemManager1.policy.in.in:85 msgid "Query and manage firmware on a mobile broadband device" msgstr "Consultar e gerenciar firmware em um dispositivo de banda larga móvel" #: data/org.freedesktop.ModemManager1.policy.in.in:86 msgid "System policy prevents querying or managing this device's firmware." msgstr "" "A política de sistema impede de consultar ou gerenciar o firmware do " "dispositivo." #: src/mm-sleep-monitor-systemd.c:125 msgid "ModemManager needs to reset devices" msgstr "O ModemManager precisa reiniciar os dispositivos" ModemManager-1.23.4-dev/po/ru.po000066400000000000000000000133251456466623000163350ustar00rootroot00000000000000# Russian translation for ModemManager. # Copyright (C) 2020 ModemManager's COPYRIGHT HOLDER # This file is distributed under the same license as the ModemManager package. # Артемий Судаков , 2020. # msgid "" msgstr "" "Project-Id-Version: ModemManager main\n" "Report-Msgid-Bugs-To: https://gitlab.freedesktop.org/mobile-broadband/" "ModemManager/issues\n" "POT-Creation-Date: 2022-07-16 14:32+0200\n" "PO-Revision-Date: 2020-04-04 21:26+0300\n" "Last-Translator: Артемий Судаков \n" "Language-Team: Russian \n" "Language: ru\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && " "n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n" "X-Generator: Poedit 2.3\n" #: data/org.freedesktop.ModemManager1.policy.in.in:13 msgid "Control the Modem Manager daemon" msgstr "Настроить сервис Modem Manager" #: data/org.freedesktop.ModemManager1.policy.in.in:14 msgid "System policy prevents controlling the Modem Manager." msgstr "Системная политика не позволяет управлять Modem Manager'ом." #: data/org.freedesktop.ModemManager1.policy.in.in:22 msgid "Unlock and control a mobile broadband device" msgstr "Разблокировка и управление мобильным широкополосным устройством" #: data/org.freedesktop.ModemManager1.policy.in.in:23 msgid "" "System policy prevents unlocking or controlling the mobile broadband device." msgstr "" "Системная политика предотвращает разблокировку или управление мобильным " "широкополосным устройством." #: data/org.freedesktop.ModemManager1.policy.in.in:31 msgid "Add, modify, and delete mobile broadband contacts" msgstr "Добавить, изменить и удалить мобильные широкополосные контакты" #: data/org.freedesktop.ModemManager1.policy.in.in:32 msgid "" "System policy prevents adding, modifying, or deleting this device's contacts." msgstr "" "Системная политика запрещает добавление, изменение или удаление контактов " "этого устройства." #: data/org.freedesktop.ModemManager1.policy.in.in:40 msgid "Send, save, modify, and delete text messages" msgstr "Отправить, сохранить, изменить и удалить текстовые сообщения" #: data/org.freedesktop.ModemManager1.policy.in.in:41 msgid "" "System policy prevents sending or manipulating this device's text messages." msgstr "" "Системная политика запрещает отправку или манипулирование текстовыми " "сообщениями этого устройства." #: data/org.freedesktop.ModemManager1.policy.in.in:49 msgid "Accept incoming voice calls or start outgoing voice calls." msgstr "" "Принимать входящие голосовые звонки или начать исходящие голосовые звонки." #: data/org.freedesktop.ModemManager1.policy.in.in:50 msgid "System policy prevents voice calls." msgstr "Системная политика запрещает голосовые звонки." #: data/org.freedesktop.ModemManager1.policy.in.in:58 msgid "Query network time and timezone information" msgstr "Запрос информации о времени и часовых поясах в сети" #: data/org.freedesktop.ModemManager1.policy.in.in:59 msgid "System policy prevents querying network time information." msgstr "Системная политика запрещает запрашивать информацию о времени в сети." #: data/org.freedesktop.ModemManager1.policy.in.in:67 msgid "Enable and view geographic location and positioning information" msgstr "" "Включить и просмотреть географическое местоположение и информацию о " "местоположении" #: data/org.freedesktop.ModemManager1.policy.in.in:68 msgid "" "System policy prevents enabling or viewing geographic location information." msgstr "" "Системная политика запрещает включение или просмотр информации о " "географическом местоположении." #: data/org.freedesktop.ModemManager1.policy.in.in:76 msgid "Query and utilize network information and services" msgstr "Запрос, использование сетевой информации и услуг" #: data/org.freedesktop.ModemManager1.policy.in.in:77 msgid "" "System policy prevents querying or utilizing network information and " "services." msgstr "" "Системная политика не позволяет запрашивать или использовать сетевую " "информацию и сервисы." #: data/org.freedesktop.ModemManager1.policy.in.in:85 msgid "Query and manage firmware on a mobile broadband device" msgstr "Запрос и управление прошивкой на мобильном широкополосном устройстве" #: data/org.freedesktop.ModemManager1.policy.in.in:86 msgid "System policy prevents querying or managing this device's firmware." msgstr "" "Системная политика не позволяет запрашивать или управлять прошивкой этого " "устройства." #: src/mm-sleep-monitor-systemd.c:125 msgid "ModemManager needs to reset devices" msgstr "ModemManager'у необходимо перезагрузить устройства" ModemManager-1.23.4-dev/po/sk.po000066400000000000000000000111371456466623000163230ustar00rootroot00000000000000# Slovak translation for ModemManager. # Copyright (C) 2017 ModemManager's COPYRIGHT HOLDER # This file is distributed under the same license as the ModemManager package. # Dušan Kazik , 2020. # msgid "" msgstr "" "Project-Id-Version: ModemManager main\n" "Report-Msgid-Bugs-To: https://gitlab.freedesktop.org/mobile-broadband/" "ModemManager/issues\n" "POT-Creation-Date: 2022-07-16 14:32+0200\n" "PO-Revision-Date: 2020-09-17 15:00+0200\n" "Last-Translator: Dušan Kazik \n" "Language-Team: Slovak \n" "Language: sk\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=3; plural=(n==1) ? 1 : (n>=2 && n<=4) ? 2 : 0;\n" "X-Generator: Poedit 2.4.1\n" #: data/org.freedesktop.ModemManager1.policy.in.in:13 msgid "Control the Modem Manager daemon" msgstr "Ovládanie služby správcu modemov" #: data/org.freedesktop.ModemManager1.policy.in.in:14 msgid "System policy prevents controlling the Modem Manager." msgstr "Politika systému zabraňuje ovládaniu správcu modemov." #: data/org.freedesktop.ModemManager1.policy.in.in:22 msgid "Unlock and control a mobile broadband device" msgstr "Odomknutie a ovládanie mobilného širokopásmového zariadenia" #: data/org.freedesktop.ModemManager1.policy.in.in:23 msgid "" "System policy prevents unlocking or controlling the mobile broadband device." msgstr "" "Politika systému zabraňuje odomknutiu alebo ovládaniu mobilného " "širokopásmového zariadenia." #: data/org.freedesktop.ModemManager1.policy.in.in:31 msgid "Add, modify, and delete mobile broadband contacts" msgstr "" "Pridanie, úprava a odstránenie kontaktov mobilného širokopásmového zariadenia" #: data/org.freedesktop.ModemManager1.policy.in.in:32 msgid "" "System policy prevents adding, modifying, or deleting this device's contacts." msgstr "" "Politika systému zabraňuje pridaniu, úprave, alebo odstráneniu kontaktov v " "tomto zariadení." #: data/org.freedesktop.ModemManager1.policy.in.in:40 msgid "Send, save, modify, and delete text messages" msgstr "Odoslanie, uloženie, úprava a odstránenie textových správ" #: data/org.freedesktop.ModemManager1.policy.in.in:41 msgid "" "System policy prevents sending or manipulating this device's text messages." msgstr "" "Politika systému zabraňuje odoslaniu, alebo manipulácii textových správ v " "tomto zariadení." #: data/org.freedesktop.ModemManager1.policy.in.in:49 msgid "Accept incoming voice calls or start outgoing voice calls." msgstr "" "Prijatie prichádzajúcich hovorov, alebo zahájenie odchádzajúcich hlasových " "hovorov." #: data/org.freedesktop.ModemManager1.policy.in.in:50 msgid "System policy prevents voice calls." msgstr "Politika systému zabraňuje hlasovým hovorom." #: data/org.freedesktop.ModemManager1.policy.in.in:58 msgid "Query network time and timezone information" msgstr "Požadovanie informácií o sieťovom čase a časovej zóne" #: data/org.freedesktop.ModemManager1.policy.in.in:59 msgid "System policy prevents querying network time information." msgstr "Politika systému zabraňuje požadovaniu informácií o sieťovom čase." #: data/org.freedesktop.ModemManager1.policy.in.in:67 msgid "Enable and view geographic location and positioning information" msgstr "Povolenie a zobrazenie geografickej polohy a informácií o pozícii" #: data/org.freedesktop.ModemManager1.policy.in.in:68 msgid "" "System policy prevents enabling or viewing geographic location information." msgstr "" "Politika systému zabraňuje povoleniu, alebo zobrazeniu informácií o " "geografickej polohe." #: data/org.freedesktop.ModemManager1.policy.in.in:76 msgid "Query and utilize network information and services" msgstr "Požadovanie a spracovanie sieťových informácií a služieb" #: data/org.freedesktop.ModemManager1.policy.in.in:77 msgid "" "System policy prevents querying or utilizing network information and " "services." msgstr "" "Politika systému zabraňuje požadovaniu, alebo spracovaniu sieťových " "informácií a službám." #: data/org.freedesktop.ModemManager1.policy.in.in:85 msgid "Query and manage firmware on a mobile broadband device" msgstr "Požadovanie a správa firmvéru mobilného širokopásmového zariadenia" #: data/org.freedesktop.ModemManager1.policy.in.in:86 msgid "System policy prevents querying or managing this device's firmware." msgstr "" "Politika systému zabraňuje požadovaniu, alebo správe firmvéru tohto " "zariadenia." #: src/mm-sleep-monitor-systemd.c:125 msgid "ModemManager needs to reset devices" msgstr "Služba ModemManager vyžaduje obnovenie zariadení" ModemManager-1.23.4-dev/po/sv.po000066400000000000000000000107271456466623000163420ustar00rootroot00000000000000# Swedish translation for ModemManager. # Copyright (C) 2017, 2020 ModemManager's COPYRIGHT HOLDER # This file is distributed under the same license as the ModemManager package. # Josef Andersson , 2017. # Anders Jonsson , 2020. # msgid "" msgstr "" "Project-Id-Version: ModemManager main\n" "Report-Msgid-Bugs-To: https://gitlab.freedesktop.org/mobile-broadband/" "ModemManager/issues\n" "POT-Creation-Date: 2022-07-16 14:32+0200\n" "PO-Revision-Date: 2020-09-18 23:56+0200\n" "Last-Translator: Anders Jonsson \n" "Language-Team: Swedish \n" "Language: sv\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" "X-Generator: Poedit 2.4.1\n" #: data/org.freedesktop.ModemManager1.policy.in.in:13 msgid "Control the Modem Manager daemon" msgstr "Kontrollera demonen för Modem Manager" #: data/org.freedesktop.ModemManager1.policy.in.in:14 msgid "System policy prevents controlling the Modem Manager." msgstr "En systempolicy förhindrar kontroll av Modem Manager." #: data/org.freedesktop.ModemManager1.policy.in.in:22 msgid "Unlock and control a mobile broadband device" msgstr "Lås upp och kontrollera en mobil bredbandsenhet" #: data/org.freedesktop.ModemManager1.policy.in.in:23 msgid "" "System policy prevents unlocking or controlling the mobile broadband device." msgstr "" "En systempolicy hindrar upplåsning eller kontroll över den mobila " "bredbandsenheten." #: data/org.freedesktop.ModemManager1.policy.in.in:31 msgid "Add, modify, and delete mobile broadband contacts" msgstr "Lägg till, ändra och ta bort mobila bredbandskontakter" #: data/org.freedesktop.ModemManager1.policy.in.in:32 msgid "" "System policy prevents adding, modifying, or deleting this device's contacts." msgstr "" "En systempolicy förhindrar att lägga till, ändra och ta bort denna enhets " "kontakter." #: data/org.freedesktop.ModemManager1.policy.in.in:40 msgid "Send, save, modify, and delete text messages" msgstr "Skicka, spara, ändra och ta bort meddelanden" #: data/org.freedesktop.ModemManager1.policy.in.in:41 msgid "" "System policy prevents sending or manipulating this device's text messages." msgstr "" "En systempolicy förhindrar att skicka eller ändra denna enhets " "textmeddelanden." #: data/org.freedesktop.ModemManager1.policy.in.in:49 msgid "Accept incoming voice calls or start outgoing voice calls." msgstr "Acceptera inkommande röstsamtal eller påbörja utgående röstsamtal." #: data/org.freedesktop.ModemManager1.policy.in.in:50 msgid "System policy prevents voice calls." msgstr "En systempolicy förhindrar röstsamtal." #: data/org.freedesktop.ModemManager1.policy.in.in:58 msgid "Query network time and timezone information" msgstr "Fråga efter nätverkstid och tidzonsinformation" #: data/org.freedesktop.ModemManager1.policy.in.in:59 msgid "System policy prevents querying network time information." msgstr "En systempolicy förhindrar frågande av nätverkstidsinformation." #: data/org.freedesktop.ModemManager1.policy.in.in:67 msgid "Enable and view geographic location and positioning information" msgstr "Aktivera och visa geografisk plats samt positioneringsinformation" #: data/org.freedesktop.ModemManager1.policy.in.in:68 msgid "" "System policy prevents enabling or viewing geographic location information." msgstr "" "En systempolicy förhindrar aktivering eller visning av geografisk " "platsinformation." #: data/org.freedesktop.ModemManager1.policy.in.in:76 msgid "Query and utilize network information and services" msgstr "Fråga efter och nyttja nätverksinformation och tjänster" #: data/org.freedesktop.ModemManager1.policy.in.in:77 msgid "" "System policy prevents querying or utilizing network information and " "services." msgstr "" "En systempolicy förhindrar frågande och nyttjande av nätverksinformation och " "tjänster." #: data/org.freedesktop.ModemManager1.policy.in.in:85 msgid "Query and manage firmware on a mobile broadband device" msgstr "Fråga efter och hantera fast programvara för en mobil bredbandsenhet" #: data/org.freedesktop.ModemManager1.policy.in.in:86 msgid "System policy prevents querying or managing this device's firmware." msgstr "" "En systempolicy förhindrar att fråga och hantera denna enhets fasta " "programvara." #: src/mm-sleep-monitor-systemd.c:125 msgid "ModemManager needs to reset devices" msgstr "ModemManager behöver starta om enheter" ModemManager-1.23.4-dev/po/tr.po000066400000000000000000000106161456466623000163340ustar00rootroot00000000000000# Turkish translation for ModemManager. # Copyright (C) 2017-2023 the ModemManager authors. # This file is distributed under the same license as the ModemManager package. # Emin Tufan Çetin , 2018, 2019. # msgid "" msgstr "" "Project-Id-Version: ModemManager main\n" "Report-Msgid-Bugs-To: https://gitlab.freedesktop.org/mobile-broadband/" "ModemManager/issues\n" "POT-Creation-Date: 2022-07-16 14:32+0200\n" "PO-Revision-Date: 2019-11-10 09:05+0300\n" "Last-Translator: Emin Tufan Çetin \n" "Language-Team: Türkçe \n" "Language: tr\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=1; plural=0;\n" "X-Generator: Gtranslator 2.91.7\n" #: data/org.freedesktop.ModemManager1.policy.in.in:13 msgid "Control the Modem Manager daemon" msgstr "Modem Manager art alan hizmetini denetle" #: data/org.freedesktop.ModemManager1.policy.in.in:14 msgid "System policy prevents controlling the Modem Manager." msgstr "Sistem ilkesi Modem Manager'ı denetlemeyi engelliyor." #: data/org.freedesktop.ModemManager1.policy.in.in:22 msgid "Unlock and control a mobile broadband device" msgstr "Mobil geniş bant aygıtının kilidini aç ve denetle" #: data/org.freedesktop.ModemManager1.policy.in.in:23 msgid "" "System policy prevents unlocking or controlling the mobile broadband device." msgstr "" "Sistem ilkesi mobil geniş bant aygıtını denetlemeyi veya kilidini açmayı " "engelliyor." #: data/org.freedesktop.ModemManager1.policy.in.in:31 msgid "Add, modify, and delete mobile broadband contacts" msgstr "Mobil geniş bant kişileri ekle, düzenle ve sil" #: data/org.freedesktop.ModemManager1.policy.in.in:32 msgid "" "System policy prevents adding, modifying, or deleting this device's contacts." msgstr "" "Sistem ilkesi bu aygıtın kişi eklemesini, düzenlemesini veya silmesini " "engelliyor." #: data/org.freedesktop.ModemManager1.policy.in.in:40 msgid "Send, save, modify, and delete text messages" msgstr "Metin ileti gönder, kaydet, düzenle ve sil" #: data/org.freedesktop.ModemManager1.policy.in.in:41 msgid "" "System policy prevents sending or manipulating this device's text messages." msgstr "" "Sistem ilkesi bu aygıtın metin iletiler göndermesini veya işlemesini " "engelliyor." #: data/org.freedesktop.ModemManager1.policy.in.in:49 msgid "Accept incoming voice calls or start outgoing voice calls." msgstr "Gelen sesli çağrıları kabul et veya giden sesli çağrılar başlat." #: data/org.freedesktop.ModemManager1.policy.in.in:50 msgid "System policy prevents voice calls." msgstr "Sistem ilkesi sesli çağrıları engelliyor." #: data/org.freedesktop.ModemManager1.policy.in.in:58 msgid "Query network time and timezone information" msgstr "Ağ zaman ve saat dilimi bilgisini sorgula" #: data/org.freedesktop.ModemManager1.policy.in.in:59 msgid "System policy prevents querying network time information." msgstr "Sistem ilkesi ağ zaman bilgisini sorgulamayı engelliyor." #: data/org.freedesktop.ModemManager1.policy.in.in:67 msgid "Enable and view geographic location and positioning information" msgstr "Coğrafi konum ve konumlandırma bilgisini etkinleştir ve gör" #: data/org.freedesktop.ModemManager1.policy.in.in:68 msgid "" "System policy prevents enabling or viewing geographic location information." msgstr "" "Sistem ilkesi coğrafi konum bilgisini etkinleştirmeyi ve göstermeyi " "engelliyor." #: data/org.freedesktop.ModemManager1.policy.in.in:76 msgid "Query and utilize network information and services" msgstr "Ağ bilgisi ve hizmetleri sorgula ve yararlan" #: data/org.freedesktop.ModemManager1.policy.in.in:77 msgid "" "System policy prevents querying or utilizing network information and " "services." msgstr "" "Sistem ilkesi ağ bilgisini ve hizmetleri sorgulamayı veya yararlanmayı " "engelliyor." #: data/org.freedesktop.ModemManager1.policy.in.in:85 msgid "Query and manage firmware on a mobile broadband device" msgstr "Mobil geniş bant aygıtındaki donanım yazılımını sorgula ve yönet" #: data/org.freedesktop.ModemManager1.policy.in.in:86 msgid "System policy prevents querying or managing this device's firmware." msgstr "" "Sistem ilkesi bu aygıtın donanım yazılımını sorgulamayı veya yönetmeyi " "engelliyor." #: src/mm-sleep-monitor-systemd.c:125 msgid "ModemManager needs to reset devices" msgstr "ModemManager'in aygıtları sıfırlaması gerekiyor" ModemManager-1.23.4-dev/po/uk.po000066400000000000000000000134251456466623000163270ustar00rootroot00000000000000# Ukrainian translation of Modem Manager # Copyright (C) 2013 Free Software Foundation, Inc. # This file is distributed under the same license as the Modem Manager package. # # Yuri Chornoivan , 2013, 2017, 2019. msgid "" msgstr "" "Project-Id-Version: Modem Manager\n" "Report-Msgid-Bugs-To: https://gitlab.freedesktop.org/mobile-broadband/" "ModemManager/issues\n" "POT-Creation-Date: 2022-07-16 14:32+0200\n" "PO-Revision-Date: 2019-11-10 14:35+0200\n" "Last-Translator: Yuri Chornoivan \n" "Language-Team: Ukrainian \n" "Language: uk\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && " "n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n" "X-Generator: Lokalize 19.11.70\n" #: data/org.freedesktop.ModemManager1.policy.in.in:13 msgid "Control the Modem Manager daemon" msgstr "Керування фоновою службою Modem Manager" #: data/org.freedesktop.ModemManager1.policy.in.in:14 msgid "System policy prevents controlling the Modem Manager." msgstr "Правила системи перешкоджають керування Modem Manager." #: data/org.freedesktop.ModemManager1.policy.in.in:22 msgid "Unlock and control a mobile broadband device" msgstr "Розблокувати пристрій мобільної широкосмугової мережі і керувати ним" #: data/org.freedesktop.ModemManager1.policy.in.in:23 msgid "" "System policy prevents unlocking or controlling the mobile broadband device." msgstr "" "Правила системи забороняють розблокування і керування пристроями " "широкосмугових мобільних мереж." #: data/org.freedesktop.ModemManager1.policy.in.in:31 msgid "Add, modify, and delete mobile broadband contacts" msgstr "" "Додати, внести зміни і вилучити контакти пристрою мобільних широкосмугових " "мереж" #: data/org.freedesktop.ModemManager1.policy.in.in:32 msgid "" "System policy prevents adding, modifying, or deleting this device's contacts." msgstr "" "Правила системи перешкоджають додаванню, внесенню змін та вилученню записів " "контактів на цьому пристрої." #: data/org.freedesktop.ModemManager1.policy.in.in:40 msgid "Send, save, modify, and delete text messages" msgstr "Надіслати, зберегти, внести зміни або вилучити текстові повідомлення" #: data/org.freedesktop.ModemManager1.policy.in.in:41 msgid "" "System policy prevents sending or manipulating this device's text messages." msgstr "" "Правила системи забороняють надсилання або керування текстовими " "повідомленнями цього пристрою." #: data/org.freedesktop.ModemManager1.policy.in.in:49 msgid "Accept incoming voice calls or start outgoing voice calls." msgstr "" "Приймати вхідні голосові виклики і розпочинати вихідні голосові виклики." #: data/org.freedesktop.ModemManager1.policy.in.in:50 msgid "System policy prevents voice calls." msgstr "Правила системи перешкоджають голосовим викликам." #: data/org.freedesktop.ModemManager1.policy.in.in:58 msgid "Query network time and timezone information" msgstr "Надсилання запиту щодо часу і часового поясу мережі" #: data/org.freedesktop.ModemManager1.policy.in.in:59 msgid "System policy prevents querying network time information." msgstr "Правила системи забороняють надсилання запитів щодо часу мережі." #: data/org.freedesktop.ModemManager1.policy.in.in:67 msgid "Enable and view geographic location and positioning information" msgstr "" "Увімкнути або переглянути дані щодо географічного розташування і позиціювання" #: data/org.freedesktop.ModemManager1.policy.in.in:68 msgid "" "System policy prevents enabling or viewing geographic location information." msgstr "" "Правила системи забороняють вмикання або перегляд даних щодо розташування." #: data/org.freedesktop.ModemManager1.policy.in.in:76 msgid "Query and utilize network information and services" msgstr "Надіслати запит і використати дані щодо мережі і служби" #: data/org.freedesktop.ModemManager1.policy.in.in:77 msgid "" "System policy prevents querying or utilizing network information and " "services." msgstr "" "Правила системи забороняють надсилання запитів і використання даних щодо " "мережі і служб." #: data/org.freedesktop.ModemManager1.policy.in.in:85 msgid "Query and manage firmware on a mobile broadband device" msgstr "" "Опитування та керування мікропрограмою на пристрої мобільної широкосмугової " "мережі" #: data/org.freedesktop.ModemManager1.policy.in.in:86 msgid "System policy prevents querying or managing this device's firmware." msgstr "" "Правила системи перешкоджають опитуванню або керування мікропрограмою цього " "пристрою." #: src/mm-sleep-monitor-systemd.c:125 msgid "ModemManager needs to reset devices" msgstr "ModemManager потребує відновлення початкового стану пристроїв" ModemManager-1.23.4-dev/po/zh_CN.po000066400000000000000000000101061456466623000167020ustar00rootroot00000000000000# Chinese (China) translation for ModemManager. # Copyright (C) 2019 ModemManager's COPYRIGHT HOLDER # This file is distributed under the same license as the ModemManager package. # Estel Zhang , 2018. # 王滋涵 Zephyr Waitzman , 2019. # msgid "" msgstr "" "Project-Id-Version: ModemManager main\n" "Report-Msgid-Bugs-To: https://gitlab.freedesktop.org/mobile-broadband/" "ModemManager/issues\n" "POT-Creation-Date: 2022-07-16 14:32+0200\n" "PO-Revision-Date: 2019-05-03 00:10+0800\n" "Last-Translator: 王滋涵 \n" "Language-Team: Chinese (China) \n" "Language: zh_CN\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Generator: Poedit 2.2.1\n" #: data/org.freedesktop.ModemManager1.policy.in.in:13 msgid "Control the Modem Manager daemon" msgstr "控制调制解调器管理器守护进程" #: data/org.freedesktop.ModemManager1.policy.in.in:14 msgid "System policy prevents controlling the Modem Manager." msgstr "系统策略禁止控制调制解调器管理器。" #: data/org.freedesktop.ModemManager1.policy.in.in:22 msgid "Unlock and control a mobile broadband device" msgstr "解锁并控制移动宽带设备" #: data/org.freedesktop.ModemManager1.policy.in.in:23 msgid "" "System policy prevents unlocking or controlling the mobile broadband device." msgstr "系统策略禁止解锁或控制移动宽带设备。" #: data/org.freedesktop.ModemManager1.policy.in.in:31 msgid "Add, modify, and delete mobile broadband contacts" msgstr "添加、修改或删除移动宽带联系人" #: data/org.freedesktop.ModemManager1.policy.in.in:32 msgid "" "System policy prevents adding, modifying, or deleting this device's contacts." msgstr "系统策略禁止添加、修改或删除移动宽带联系人。" #: data/org.freedesktop.ModemManager1.policy.in.in:40 msgid "Send, save, modify, and delete text messages" msgstr "发送、保存修改或删除文本消息" #: data/org.freedesktop.ModemManager1.policy.in.in:41 msgid "" "System policy prevents sending or manipulating this device's text messages." msgstr "系统策略禁止发送或操作此设备的文本消息。" #: data/org.freedesktop.ModemManager1.policy.in.in:49 msgid "Accept incoming voice calls or start outgoing voice calls." msgstr "接听传入的语音呼叫或者开始传出语音呼叫。" #: data/org.freedesktop.ModemManager1.policy.in.in:50 msgid "System policy prevents voice calls." msgstr "系统策略禁止语音呼叫。" #: data/org.freedesktop.ModemManager1.policy.in.in:58 msgid "Query network time and timezone information" msgstr "" #: data/org.freedesktop.ModemManager1.policy.in.in:59 #, fuzzy msgid "System policy prevents querying network time information." msgstr "系统策略禁止查询和利用网络信息和服务。" #: data/org.freedesktop.ModemManager1.policy.in.in:67 msgid "Enable and view geographic location and positioning information" msgstr "启用和查看地理位置和定位信息" #: data/org.freedesktop.ModemManager1.policy.in.in:68 msgid "" "System policy prevents enabling or viewing geographic location information." msgstr "系统策略禁止启用和查看地理位置和定位信息。" #: data/org.freedesktop.ModemManager1.policy.in.in:76 msgid "Query and utilize network information and services" msgstr "查询和利用网络信息和服务" #: data/org.freedesktop.ModemManager1.policy.in.in:77 msgid "" "System policy prevents querying or utilizing network information and " "services." msgstr "系统策略禁止查询和利用网络信息和服务。" #: data/org.freedesktop.ModemManager1.policy.in.in:85 msgid "Query and manage firmware on a mobile broadband device" msgstr "在移动宽带设备上查询和管理固件" #: data/org.freedesktop.ModemManager1.policy.in.in:86 msgid "System policy prevents querying or managing this device's firmware." msgstr "系统策略禁止在移动宽带设备上查询和管理固件。" #: src/mm-sleep-monitor-systemd.c:125 msgid "ModemManager needs to reset devices" msgstr "调制解调器管理器需要重置设备" ModemManager-1.23.4-dev/src/000077500000000000000000000000001456466623000155145ustar00rootroot00000000000000ModemManager-1.23.4-dev/src/80-mm-candidate.rules000066400000000000000000000031161456466623000213410ustar00rootroot00000000000000# do not edit this file, it will be overwritten on update # Tag any devices that MM might be interested in; if ModemManager is started # up right after udev, when MM explicitly requests devices on startup it may # get devices that haven't had all rules run yet. Thus, we tag devices we're # interested in and when handling devices during MM startup we ignore any # that don't have this tag. MM will still get the udev 'add' event for the # device a short while later and then process it as normal. ACTION!="add|change|move|bind", GOTO="mm_candidate_end" # Opening bound but disconnected Bluetooth RFCOMM ttys would initiate the # connection. Don't do that. KERNEL=="rfcomm*", DEVPATH=="*/virtual/*", GOTO="mm_candidate_end" # All Qualcomm-based USB devices may switch to a recovery layout with a single # serial port in QDL mode if they fail to boot correctly too many times. SUBSYSTEMS=="usb", ATTRS{idVendor}=="05c6", ATTRS{idProduct}=="9008", ENV{ID_MM_DEVICE_IGNORE}="1" # Generic candidate rules for tty/usbmisc/net subsystems SUBSYSTEM=="tty", ENV{ID_MM_CANDIDATE}="1" SUBSYSTEM=="net", ENV{ID_MM_CANDIDATE}="1" KERNEL=="cdc-wdm*", SUBSYSTEM=="usbmisc", ENV{ID_MM_CANDIDATE}="1" # WWAN subsystem candidate rules # - All USB devices ignored for now, only PCI devices expected # - Only "wwan_port" device types processed (single ports); we fully ignore # the "wwan_dev" device type (full device, not just one port) SUBSYSTEMS=="usb", GOTO="mm_candidate_end" SUBSYSTEM=="wwan", ENV{DEVTYPE}=="wwan_dev", GOTO="mm_candidate_end" SUBSYSTEM=="wwan", ENV{ID_MM_CANDIDATE}="1" LABEL="mm_candidate_end" ModemManager-1.23.4-dev/src/kerneldevice/000077500000000000000000000000001456466623000201545ustar00rootroot00000000000000ModemManager-1.23.4-dev/src/kerneldevice/mm-kernel-device-generic-rules.c000066400000000000000000000321761456466623000262170ustar00rootroot00000000000000/* -*- 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. * * 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) 2016 Aleksander Morgado */ #include "config.h" #include #include #define _LIBMM_INSIDE_MM #include #include "mm-log.h" #include "mm-kernel-device-generic-rules.h" static void udev_rule_match_clear (MMUdevRuleMatch *rule_match) { g_free (rule_match->parameter); g_free (rule_match->value); } static void udev_rule_clear (MMUdevRule *rule) { switch (rule->result.type) { case MM_UDEV_RULE_RESULT_TYPE_PROPERTY: g_free (rule->result.content.property.name); g_free (rule->result.content.property.value); break; case MM_UDEV_RULE_RESULT_TYPE_GOTO_TAG: case MM_UDEV_RULE_RESULT_TYPE_LABEL: g_free (rule->result.content.tag); break; case MM_UDEV_RULE_RESULT_TYPE_GOTO_INDEX: case MM_UDEV_RULE_RESULT_TYPE_UNKNOWN: default: break; } if (rule->conditions) g_array_unref (rule->conditions); } static gboolean split_item (const gchar *item, gchar **out_left, gchar **out_operator, gchar **out_right, GError **error) { const gchar *aux; gchar *left = NULL; gchar *operator = NULL; gchar *right = NULL; GError *inner_error = NULL; g_assert (item && out_left && out_operator && out_right); /* Get left/operator/right */ if (((aux = strstr (item, "==")) != NULL) || ((aux = strstr (item, "!=")) != NULL)) { operator = g_strndup (aux, 2); right = g_strdup (aux + 2); } else if ((aux = strstr (item, "=")) != NULL) { operator = g_strndup (aux, 1); right = g_strdup (aux + 1); } else { inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Invalid rule item, missing operator: '%s'", item); goto out; } left = g_strndup (item, (aux - item)); g_strstrip (left); if (!left[0]) { inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Invalid rule item, missing left field: '%s'", item); goto out; } g_strdelimit (right, "\"", ' '); g_strstrip (right); if (!right[0]) { inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Invalid rule item, missing right field: '%s'", item); goto out; } out: if (inner_error) { g_free (left); g_free (operator); g_free (right); g_propagate_error (error, inner_error); return FALSE; } *out_left = left; *out_operator = operator; *out_right = right; return TRUE; } static gboolean load_rule_result (MMUdevRuleResult *rule_result, const gchar *item, GError **error) { gchar *left; gchar *operator; gchar *right; GError *inner_error = NULL; gsize left_len; if (!split_item (item, &left, &operator, &right, error)) return FALSE; if (!g_str_equal (operator, "=")) { inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Invalid rule result operator: '%s'", item); goto out; } if (g_str_equal (left, "LABEL")) { rule_result->type = MM_UDEV_RULE_RESULT_TYPE_LABEL; rule_result->content.tag = right; right = NULL; goto out; } if (g_str_equal (left, "GOTO")) { rule_result->type = MM_UDEV_RULE_RESULT_TYPE_GOTO_TAG; rule_result->content.tag = right; right = NULL; goto out; } left_len = strlen (left); if (g_str_has_prefix (left, "ENV{") && left[left_len - 1] == '}') { rule_result->type = MM_UDEV_RULE_RESULT_TYPE_PROPERTY; rule_result->content.property.name = g_strndup (left + 4, left_len - 5); rule_result->content.property.value = right; right = NULL; goto out; } g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Invalid rule result parameter: '%s'", item); out: g_free (left); g_free (operator); g_free (right); if (inner_error) { g_propagate_error (error, inner_error); return FALSE; } return TRUE; } static gboolean load_rule_match (MMUdevRuleMatch *rule_match, const gchar *item, GError **error) { gchar *left; gchar *operator; gchar *right; if (!split_item (item, &left, &operator, &right, error)) return FALSE; if (g_str_equal (operator, "==")) rule_match->type = MM_UDEV_RULE_MATCH_TYPE_EQUAL; else if (g_str_equal (operator, "!=")) rule_match->type = MM_UDEV_RULE_MATCH_TYPE_NOT_EQUAL; else { g_free (left); g_free (operator); g_free (right); g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Invalid rule match, wrong match type: '%s'", item); return FALSE; } g_free (operator); rule_match->parameter = left; rule_match->value = right; return TRUE; } static gboolean load_rule_from_line (MMUdevRule *rule, const gchar *line, GError **error) { gchar **split; guint n_items; GError *inner_error = NULL; split = g_strsplit (line, ",", -1); n_items = g_strv_length (split); /* Conditions */ if (n_items > 1) { guint i; rule->conditions = g_array_sized_new (FALSE, FALSE, sizeof (MMUdevRuleMatch), n_items - 1); g_array_set_clear_func (rule->conditions, (GDestroyNotify) udev_rule_match_clear); /* All items except for the last one are conditions */ for (i = 0; !inner_error && i < (n_items - 1); i++) { MMUdevRuleMatch rule_match = { 0 }; /* If condition correctly preloaded, add it to the rule */ if (!load_rule_match (&rule_match, split[i], &inner_error)) goto out; g_assert (rule_match.type != MM_UDEV_RULE_MATCH_TYPE_UNKNOWN); g_assert (rule_match.parameter); g_assert (rule_match.value); g_array_append_val (rule->conditions, rule_match); } } /* Last item, the result */ if (!load_rule_result (&rule->result, split[n_items - 1], &inner_error)) goto out; g_assert ((rule->result.type == MM_UDEV_RULE_RESULT_TYPE_GOTO_TAG && rule->result.content.tag) || (rule->result.type == MM_UDEV_RULE_RESULT_TYPE_LABEL && rule->result.content.tag) || (rule->result.type == MM_UDEV_RULE_RESULT_TYPE_PROPERTY && rule->result.content.property.name && rule->result.content.property.value)); out: g_strfreev (split); if (inner_error) { g_propagate_error (error, inner_error); return FALSE; } return TRUE; } static gboolean process_goto_tags (GArray *rules, guint first_rule_index, GError **error) { guint i; for (i = first_rule_index; i < rules->len; i++) { MMUdevRule *rule; rule = &g_array_index (rules, MMUdevRule, i); if (rule->result.type == MM_UDEV_RULE_RESULT_TYPE_GOTO_TAG) { guint j; guint label_index = 0; for (j = i + 1; j < rules->len; j++) { MMUdevRule *walker; walker = &g_array_index (rules, MMUdevRule, j); if (walker->result.type == MM_UDEV_RULE_RESULT_TYPE_LABEL && g_str_equal (rule->result.content.tag, walker->result.content.tag)) { if (label_index) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "More than one label '%s' found", rule->result.content.tag); return FALSE; } label_index = j; } } if (!label_index) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't find label '%s'", rule->result.content.tag); return FALSE; } rule->result.type = MM_UDEV_RULE_RESULT_TYPE_GOTO_INDEX; g_free (rule->result.content.tag); rule->result.content.index = label_index; } } return TRUE; } static gboolean load_rules_from_file (GArray *rules, const gchar *path, GError **error) { GFile *file; GFileInputStream *fistream; GDataInputStream *distream = NULL; GError *inner_error = NULL; gchar *line; guint first_rule_index; first_rule_index = rules->len; file = g_file_new_for_path (path); fistream = g_file_read (file, NULL, &inner_error); if (!fistream) goto out; distream = g_data_input_stream_new (G_INPUT_STREAM (fistream)); while (((line = g_data_input_stream_read_line_utf8 (distream, NULL, NULL, &inner_error)) != NULL) && !inner_error) { const gchar *aux; aux = line; while (*aux == ' ') aux++; if (*aux != '#' && *aux != '\0') { MMUdevRule rule = { 0 }; if (load_rule_from_line (&rule, aux, &inner_error)) g_array_append_val (rules, rule); else udev_rule_clear (&rule); } g_free (line); } out: if (distream) g_object_unref (distream); if (fistream) g_object_unref (fistream); g_object_unref (file); if (inner_error) { g_propagate_error (error, inner_error); return FALSE; } if (first_rule_index < rules->len && !process_goto_tags (rules, first_rule_index, error)) return FALSE; return TRUE; } static GList * list_rule_files (const gchar *rules_dir_path) { static const gchar *expected_rules_prefix[] = { "77-mm-", "78-mm-", "79-mm-", "80-mm-" }; GFile *udevrulesdir; GFileEnumerator *enumerator; GList *children = NULL; udevrulesdir = g_file_new_for_path (rules_dir_path); enumerator = g_file_enumerate_children (udevrulesdir, G_FILE_ATTRIBUTE_STANDARD_NAME, G_FILE_QUERY_INFO_NONE, NULL, NULL); if (enumerator) { GFileInfo *info; /* If we get any kind of error, assume we need to stop enumerating */ while ((info = g_file_enumerator_next_file (enumerator, NULL, NULL)) != NULL) { guint i; for (i = 0; i < G_N_ELEMENTS (expected_rules_prefix); i++) { if (g_str_has_prefix (g_file_info_get_name (info), expected_rules_prefix[i])) { children = g_list_prepend (children, g_build_path (G_DIR_SEPARATOR_S, rules_dir_path, g_file_info_get_name (info), NULL)); break; } } g_object_unref (info); } g_object_unref (enumerator); } g_object_unref (udevrulesdir); return g_list_sort (children, (GCompareFunc) g_strcmp0); } GArray * mm_kernel_device_generic_rules_load (const gchar *rules_dir, GError **error) { GList *rule_files, *l; GArray *rules; GError *inner_error = NULL; rules = g_array_new (FALSE, FALSE, sizeof (MMUdevRule)); g_array_set_clear_func (rules, (GDestroyNotify) udev_rule_clear); /* List rule files in rules dir */ rule_files = list_rule_files (rules_dir); if (!rule_files) { inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "No rule files found in '%s'", rules_dir); goto out; } /* Iterate over rule files */ for (l = rule_files; l; l = g_list_next (l)) { if (!load_rules_from_file (rules, (const gchar *)(l->data), &inner_error)) goto out; } /* Fail if no rules were loaded */ if (rules->len == 0) { inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "No rules loaded"); goto out; } out: if (rule_files) g_list_free_full (rule_files, g_free); if (inner_error) { g_propagate_error (error, inner_error); g_array_unref (rules); return NULL; } return rules; } ModemManager-1.23.4-dev/src/kerneldevice/mm-kernel-device-generic-rules.h000066400000000000000000000033661456466623000262230ustar00rootroot00000000000000/* -*- 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) 2016 Aleksander Morgado */ #include G_BEGIN_DECLS typedef enum { MM_UDEV_RULE_MATCH_TYPE_UNKNOWN, MM_UDEV_RULE_MATCH_TYPE_EQUAL, MM_UDEV_RULE_MATCH_TYPE_NOT_EQUAL, } MMUdevRuleMatchType; typedef struct { MMUdevRuleMatchType type; gchar *parameter; gchar *value; } MMUdevRuleMatch; typedef enum { MM_UDEV_RULE_RESULT_TYPE_UNKNOWN, MM_UDEV_RULE_RESULT_TYPE_PROPERTY, MM_UDEV_RULE_RESULT_TYPE_LABEL, MM_UDEV_RULE_RESULT_TYPE_GOTO_INDEX, MM_UDEV_RULE_RESULT_TYPE_GOTO_TAG, /* internal use only */ } MMUdevRuleResultType; typedef struct { gchar *name; gchar *value; } MMUdevRuleResultProperty; typedef struct { MMUdevRuleResultType type; union { MMUdevRuleResultProperty property; gchar *tag; guint index; } content; } MMUdevRuleResult; typedef struct { GArray *conditions; MMUdevRuleResult result; } MMUdevRule; GArray *mm_kernel_device_generic_rules_load (const gchar *rules_dir, GError **error); G_END_DECLS ModemManager-1.23.4-dev/src/kerneldevice/mm-kernel-device-generic.c000066400000000000000000001367651456466623000251000ustar00rootroot00000000000000/* -*- 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) 2016 Velocloud, Inc. * Copyright (C) 2020 Aleksander Morgado */ #define _GNU_SOURCE #include #include #include #define _LIBMM_INSIDE_MM #include #include #include "mm-kernel-device-generic.h" #include "mm-kernel-device-generic-rules.h" #include "mm-kernel-device-helpers.h" #include "mm-log-object.h" #include "mm-utils.h" #if !defined UDEVRULESDIR # error UDEVRULESDIR is not defined #endif static void initable_iface_init (GInitableIface *iface); G_DEFINE_TYPE_EXTENDED (MMKernelDeviceGeneric, mm_kernel_device_generic, MM_TYPE_KERNEL_DEVICE, 0, G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, initable_iface_init)) enum { PROP_0, PROP_PROPERTIES, PROP_RULES, PROP_LAST }; static GParamSpec *properties[PROP_LAST]; struct _MMKernelDeviceGenericPrivate { /* Input properties */ MMKernelEventProperties *properties; /* Rules to apply */ GArray *rules; /* Contents from sysfs */ gchar **drivers; gchar **subsystems; gchar *sysfs_path; gchar *wwandev_sysfs_path; gchar *interface_sysfs_path; guint8 interface_class; guint8 interface_subclass; guint8 interface_protocol; guint8 interface_number; gchar *interface_description; gchar *physdev_sysfs_path; guint16 physdev_vid; guint16 physdev_pid; guint16 physdev_subsystem_vid; guint16 physdev_revision; gchar *physdev_manufacturer; gchar *physdev_product; }; static gboolean has_sysfs_attribute (const gchar *path, const gchar *attribute) { g_autofree gchar *aux_filepath = NULL; aux_filepath = g_strdup_printf ("%s/%s", path, attribute); return g_file_test (aux_filepath, G_FILE_TEST_EXISTS); } static gchar * read_sysfs_attribute_as_string (const gchar *path, const gchar *attribute) { g_autofree gchar *aux = NULL; gchar *contents = NULL; aux = g_strdup_printf ("%s/%s", path, attribute); if (g_file_get_contents (aux, &contents, NULL, NULL)) { g_strdelimit (contents, "\r\n", ' '); g_strstrip (contents); } return contents; } static guint read_sysfs_attribute_as_hex (const gchar *path, const gchar *attribute) { g_autofree gchar *contents = NULL; guint val = 0; contents = read_sysfs_attribute_as_string (path, attribute); if (contents) mm_get_uint_from_hex_str (contents, &val); return val; } static gchar * read_sysfs_attribute_link_basename (const gchar *path, const gchar *attribute) { g_autofree gchar *aux_filepath = NULL; g_autofree gchar *canonicalized_path = NULL; aux_filepath = g_strdup_printf ("%s/%s", path, attribute); if (!g_file_test (aux_filepath, G_FILE_TEST_EXISTS)) return NULL; canonicalized_path = realpath (aux_filepath, NULL); return g_path_get_basename (canonicalized_path); } static gchar * lookup_sysfs_attribute_as_string (MMKernelDeviceGeneric *self, const gchar *attribute, gboolean iterate) { g_autofree gchar *iter = NULL; /* if there is no parent sysfs path set, we look for the attribute * only in the port sysfs path */ if (!self->priv->physdev_sysfs_path) return read_sysfs_attribute_as_string (self->priv->sysfs_path, attribute); iter = g_strdup (self->priv->sysfs_path); while (iter) { g_autofree gchar *parent = NULL; gchar *value; /* return first one found */ if ((value = read_sysfs_attribute_as_string (iter, attribute)) != NULL) return value; else if (!iterate) break; if (g_strcmp0 (iter, self->priv->physdev_sysfs_path) == 0) break; parent = g_path_get_dirname (iter); g_clear_pointer (&iter, g_free); iter = g_steal_pointer (&parent); } return NULL; } /*****************************************************************************/ /* Load contents */ static void preload_sysfs_path (MMKernelDeviceGeneric *self) { g_autofree gchar *tmp = NULL; if (self->priv->sysfs_path) return; /* sysfs can be built directly using subsystem and name; e.g. for subsystem * usbmisc and name cdc-wdm0: * $ realpath /sys/class/usbmisc/cdc-wdm0 * /sys/devices/pci0000:00/0000:00:1d.0/usb4/4-1/4-1.3/4-1.3:1.8/usbmisc/cdc-wdm0 */ tmp = g_strdup_printf ("/sys/class/%s/%s", mm_kernel_event_properties_get_subsystem (self->priv->properties), mm_kernel_event_properties_get_name (self->priv->properties)); self->priv->sysfs_path = realpath (tmp, NULL); if (!self->priv->sysfs_path || !g_file_test (self->priv->sysfs_path, G_FILE_TEST_EXISTS)) { mm_obj_warn (self, "invalid sysfs path read for %s/%s", mm_kernel_event_properties_get_subsystem (self->priv->properties), mm_kernel_event_properties_get_name (self->priv->properties)); g_clear_pointer (&self->priv->sysfs_path, g_free); } if (self->priv->sysfs_path) { const gchar *devpath; mm_obj_dbg (self, "sysfs path: %s", self->priv->sysfs_path); devpath = (g_str_has_prefix (self->priv->sysfs_path, "/sys") ? &self->priv->sysfs_path[4] : self->priv->sysfs_path); g_object_set_data_full (G_OBJECT (self), "DEVPATH", g_strdup (devpath), g_free); } } /*****************************************************************************/ static void preload_common_properties (MMKernelDeviceGeneric *self) { if (self->priv->interface_sysfs_path) { mm_obj_dbg (self, " ID_USB_INTERFACE_NUM: 0x%02x", self->priv->interface_number); g_object_set_data_full (G_OBJECT (self), "ID_USB_INTERFACE_NUM", g_strdup_printf ("%02x", self->priv->interface_number), g_free); } if (self->priv->physdev_product) { mm_obj_dbg (self, " ID_MODEL: %s", self->priv->physdev_product); g_object_set_data_full (G_OBJECT (self), "ID_MODEL", g_strdup (self->priv->physdev_product), g_free); } if (self->priv->physdev_manufacturer) { mm_obj_dbg (self, " ID_VENDOR: %s", self->priv->physdev_manufacturer); g_object_set_data_full (G_OBJECT (self), "ID_VENDOR", g_strdup (self->priv->physdev_manufacturer), g_free); } if (self->priv->physdev_sysfs_path) { mm_obj_dbg (self, " ID_VENDOR_ID: 0x%04x", self->priv->physdev_vid); g_object_set_data_full (G_OBJECT (self), "ID_VENDOR_ID", g_strdup_printf ("%04x", self->priv->physdev_vid), g_free); mm_obj_dbg (self, " ID_MODEL_ID: 0x%04x", self->priv->physdev_pid); g_object_set_data_full (G_OBJECT (self), "ID_MODEL_ID", g_strdup_printf ("%04x", self->priv->physdev_pid), g_free); mm_obj_dbg (self, " ID_REVISION: 0x%04x", self->priv->physdev_revision); g_object_set_data_full (G_OBJECT (self), "ID_REVISION", g_strdup_printf ("%04x", self->priv->physdev_revision), g_free); } } static void ptr_array_add_sysfs_attribute_link_basename (GPtrArray *array, const gchar *sysfs_path, const gchar *attribute, gchar **out_value) { g_autofree gchar *value = NULL; g_assert (array && sysfs_path && attribute); value = read_sysfs_attribute_link_basename (sysfs_path, attribute); if (out_value) *out_value = g_strdup (value); if (value && !g_ptr_array_find_with_equal_func (array, value, g_str_equal, NULL)) g_ptr_array_add (array, g_steal_pointer (&value)); } static void preload_contents_other (MMKernelDeviceGeneric *self) { g_autofree gchar *lower_device_name = NULL; GPtrArray *drivers; GPtrArray *subsystems; /* For any other kind of bus (or the absence of one, as in virtual devices), * assume this is a single port device and don't try to match multiple ports * together. Also, obviously, no vendor, product, revision or interface. */ drivers = g_ptr_array_sized_new (2); ptr_array_add_sysfs_attribute_link_basename (drivers, self->priv->sysfs_path, "driver", NULL); g_ptr_array_add (drivers, NULL); self->priv->drivers = (gchar **) g_ptr_array_free (drivers, FALSE); subsystems = g_ptr_array_sized_new (2); ptr_array_add_sysfs_attribute_link_basename (subsystems, self->priv->sysfs_path, "subsystem", NULL); g_ptr_array_add (subsystems, NULL); self->priv->subsystems = (gchar **) g_ptr_array_free (subsystems, FALSE); /* But look for a lower real physical device, as we may have one */ lower_device_name = mm_kernel_device_get_lower_device_name (self->priv->sysfs_path); if (lower_device_name) { g_autoptr(MMKernelDevice) lower_kernel_device = NULL; g_autoptr(MMKernelEventProperties) props = NULL; g_autoptr(GError) error = NULL; const gchar *subsystem; subsystem = mm_kernel_device_get_subsystem (MM_KERNEL_DEVICE (self)); props = mm_kernel_event_properties_new (); mm_kernel_event_properties_set_subsystem (props, subsystem); mm_kernel_event_properties_set_name (props, lower_device_name); lower_kernel_device = mm_kernel_device_generic_new (props, &error); if (!lower_kernel_device) { mm_obj_dbg (self, "couldn't find lower device: %s/%s", subsystem, lower_device_name); } else { mm_obj_dbg (self, "setting up lower device: %s/%s", subsystem, lower_device_name); g_object_set (self, "lower-device", lower_kernel_device, NULL); } } } static void preload_contents_platform (MMKernelDeviceGeneric *self, const gchar *platform) { g_autofree gchar *iter = NULL; GPtrArray *drivers; GPtrArray *subsystems; drivers = g_ptr_array_sized_new (3); subsystems = g_ptr_array_sized_new (3); iter = g_strdup (self->priv->sysfs_path); while (iter && (g_strcmp0 (iter, "/") != 0)) { gchar *parent; g_autofree gchar *current_subsystem = NULL; ptr_array_add_sysfs_attribute_link_basename (drivers, iter, "driver", NULL); ptr_array_add_sysfs_attribute_link_basename (subsystems, iter, "subsystem", ¤t_subsystem); /* Take first parent with the given platform subsystem as physical device */ current_subsystem = read_sysfs_attribute_link_basename (iter, "subsystem"); if (!self->priv->physdev_sysfs_path && (g_strcmp0 (current_subsystem, platform) == 0)) { self->priv->physdev_sysfs_path = g_strdup (iter); /* stop traversing as soon as the physical device is found */ break; } parent = g_path_get_dirname (iter); g_clear_pointer (&iter, g_free); iter = parent; } g_ptr_array_add (drivers, NULL); self->priv->drivers = (gchar **) g_ptr_array_free (drivers, FALSE); g_ptr_array_add (subsystems, NULL); self->priv->subsystems = (gchar **) g_ptr_array_free (subsystems, FALSE); } static void preload_contents_pcmcia (MMKernelDeviceGeneric *self) { g_autofree gchar *iter = NULL; GPtrArray *drivers; GPtrArray *subsystems; gboolean pcmcia_subsystem_found = FALSE; drivers = g_ptr_array_sized_new (3); subsystems = g_ptr_array_sized_new (3); iter = g_strdup (self->priv->sysfs_path); while (iter && (g_strcmp0 (iter, "/") != 0)) { g_autofree gchar *parent = NULL; g_autofree gchar *parent_subsystem = NULL; g_autofree gchar *current_subsystem = NULL; ptr_array_add_sysfs_attribute_link_basename (drivers, iter, "driver", NULL); ptr_array_add_sysfs_attribute_link_basename (subsystems, iter, "subsystem", ¤t_subsystem); if (g_strcmp0 (current_subsystem, "pcmcia") == 0) pcmcia_subsystem_found = TRUE; parent = g_path_get_dirname (iter); if (parent) parent_subsystem = read_sysfs_attribute_link_basename (parent, "subsystem"); if (pcmcia_subsystem_found && parent_subsystem && (g_strcmp0 (parent_subsystem, "pcmcia") != 0)) { self->priv->physdev_sysfs_path = g_strdup (iter); self->priv->physdev_vid = read_sysfs_attribute_as_hex (self->priv->physdev_sysfs_path, "manf_id"); self->priv->physdev_pid = read_sysfs_attribute_as_hex (self->priv->physdev_sysfs_path, "card_id"); /* stop traversing as soon as the physical device is found */ break; } g_clear_pointer (&iter, g_free); iter = g_steal_pointer (&parent); } g_ptr_array_add (drivers, NULL); self->priv->drivers = (gchar **) g_ptr_array_free (drivers, FALSE); g_ptr_array_add (subsystems, NULL); self->priv->subsystems = (gchar **) g_ptr_array_free (subsystems, FALSE); } static void preload_contents_pci (MMKernelDeviceGeneric *self) { g_autofree gchar *iter = NULL; GPtrArray *drivers; GPtrArray *subsystems; drivers = g_ptr_array_sized_new (4); subsystems = g_ptr_array_sized_new (4); iter = g_strdup (self->priv->sysfs_path); while (iter && (g_strcmp0 (iter, "/") != 0)) { g_autofree gchar *current_subsystem = NULL; gchar *parent; ptr_array_add_sysfs_attribute_link_basename (drivers, iter, "driver", NULL); ptr_array_add_sysfs_attribute_link_basename (subsystems, iter, "subsystem", ¤t_subsystem); /* the PCI channel specific devices have their own drivers and * subsystems, we can rely on the physical device being the first * one that reports the 'pci' subsystem */ if (!self->priv->physdev_sysfs_path && (g_strcmp0 (current_subsystem, "pci") == 0)) { self->priv->physdev_sysfs_path = g_strdup (iter); self->priv->physdev_vid = read_sysfs_attribute_as_hex (self->priv->physdev_sysfs_path, "vendor"); self->priv->physdev_pid = read_sysfs_attribute_as_hex (self->priv->physdev_sysfs_path, "device"); self->priv->physdev_subsystem_vid = read_sysfs_attribute_as_hex (self->priv->physdev_sysfs_path, "subsystem_vendor"); self->priv->physdev_revision = read_sysfs_attribute_as_hex (self->priv->physdev_sysfs_path, "revision"); /* stop traversing as soon as the physical device is found */ break; } parent = g_path_get_dirname (iter); g_clear_pointer (&iter, g_free); iter = parent; } g_ptr_array_add (drivers, NULL); self->priv->drivers = (gchar **) g_ptr_array_free (drivers, FALSE); g_ptr_array_add (subsystems, NULL); self->priv->subsystems = (gchar **) g_ptr_array_free (subsystems, FALSE); } static void preload_contents_usb (MMKernelDeviceGeneric *self) { g_autofree gchar *iter = NULL; GPtrArray *drivers; GPtrArray *subsystems; drivers = g_ptr_array_sized_new (4); subsystems = g_ptr_array_sized_new (4); iter = g_strdup (self->priv->sysfs_path); while (iter && (g_strcmp0 (iter, "/") != 0)) { gchar *parent; ptr_array_add_sysfs_attribute_link_basename (drivers, iter, "driver", NULL); ptr_array_add_sysfs_attribute_link_basename (subsystems, iter, "subsystem", NULL); /* is this the USB interface? */ if (!self->priv->interface_sysfs_path && has_sysfs_attribute (iter, "bInterfaceClass")) { self->priv->interface_sysfs_path = g_strdup (iter); self->priv->interface_class = read_sysfs_attribute_as_hex (self->priv->interface_sysfs_path, "bInterfaceClass"); self->priv->interface_subclass = read_sysfs_attribute_as_hex (self->priv->interface_sysfs_path, "bInterfaceSubClass"); self->priv->interface_protocol = read_sysfs_attribute_as_hex (self->priv->interface_sysfs_path, "bInterfaceProtocol"); self->priv->interface_number = read_sysfs_attribute_as_hex (self->priv->interface_sysfs_path, "bInterfaceNumber"); self->priv->interface_description = read_sysfs_attribute_as_string (self->priv->interface_sysfs_path, "interface"); } /* is this the USB physdev? */ else if (!self->priv->physdev_sysfs_path && has_sysfs_attribute (iter, "idVendor")) { self->priv->physdev_sysfs_path = g_strdup (iter); self->priv->physdev_vid = read_sysfs_attribute_as_hex (self->priv->physdev_sysfs_path, "idVendor"); self->priv->physdev_pid = read_sysfs_attribute_as_hex (self->priv->physdev_sysfs_path, "idProduct"); self->priv->physdev_revision = read_sysfs_attribute_as_hex (self->priv->physdev_sysfs_path, "bcdDevice"); self->priv->physdev_manufacturer = read_sysfs_attribute_as_string (self->priv->physdev_sysfs_path, "manufacturer"); self->priv->physdev_product = read_sysfs_attribute_as_string (self->priv->physdev_sysfs_path, "product"); /* stop traversing as soon as the physical device is found */ break; } parent = g_path_get_dirname (iter); g_clear_pointer (&iter, g_free); iter = parent; } g_ptr_array_add (drivers, NULL); self->priv->drivers = (gchar **) g_ptr_array_free (drivers, FALSE); g_ptr_array_add (subsystems, NULL); self->priv->subsystems = (gchar **) g_ptr_array_free (subsystems, FALSE); } static void preload_contents_wwan (MMKernelDeviceGeneric *self) { g_autofree gchar *iter = NULL; /* Find the first parent device subsystem */ iter = g_path_get_dirname(self->priv->sysfs_path); while (iter && (g_strcmp0 (iter, "/") != 0)) { g_autofree gchar *current_subsystem = NULL; gchar *parent; current_subsystem = read_sysfs_attribute_link_basename (iter, "subsystem"); if (current_subsystem) { if (g_strcmp0 (current_subsystem, "wwan") == 0) self->priv->wwandev_sysfs_path = g_strdup (iter); break; } parent = g_path_get_dirname (iter); g_clear_pointer (&iter, g_free); iter = parent; } } static gchar * find_device_bus_subsystem (MMKernelDeviceGeneric *self) { g_autofree gchar *iter = NULL; iter = g_strdup (self->priv->sysfs_path); while (iter && (g_strcmp0 (iter, "/") != 0)) { g_autofree gchar *subsys = NULL; gchar *parent; subsys = read_sysfs_attribute_link_basename (iter, "subsystem"); /* stop search as soon as we find a parent object * of one of the supported bus subsystems */ if (subsys && ((g_strcmp0 (subsys, "usb") == 0) || (g_strcmp0 (subsys, "pcmcia") == 0) || (g_strcmp0 (subsys, "pci") == 0) || (g_strcmp0 (subsys, "platform") == 0) || (g_strcmp0 (subsys, "pnp") == 0) || (g_strcmp0 (subsys, "sdio") == 0))) return g_steal_pointer (&subsys); parent = g_path_get_dirname (iter); g_clear_pointer (&iter, g_free); iter = parent; } /* no more parents to check */ return NULL; } static void preload_contents (MMKernelDeviceGeneric *self) { g_autofree gchar *bus_subsys = NULL; if (self->priv->sysfs_path) return; preload_sysfs_path (self); if (!self->priv->sysfs_path) return; bus_subsys = find_device_bus_subsystem (self); if (g_strcmp0 (bus_subsys, "usb") == 0) preload_contents_usb (self); else if (g_strcmp0 (bus_subsys, "pcmcia") == 0) preload_contents_pcmcia (self); else if (g_strcmp0 (bus_subsys, "pci") == 0) preload_contents_pci (self); else if ((g_strcmp0 (bus_subsys, "platform") == 0) || (g_strcmp0 (bus_subsys, "pnp") == 0) || (g_strcmp0 (bus_subsys, "sdio") == 0)) preload_contents_platform (self, bus_subsys); else preload_contents_other (self); preload_contents_wwan (self); /* wwan is bus agnostic class */ if (!bus_subsys) return; mm_obj_dbg (self, "port contents loaded:"); mm_obj_dbg (self, " bus: %s", bus_subsys ? bus_subsys : "n/a"); if (self->priv->interface_sysfs_path) { mm_obj_dbg (self, " interface: %s", self->priv->interface_sysfs_path); mm_obj_dbg (self, " interface class: %02x", self->priv->interface_class); mm_obj_dbg (self, " interface subclass: %02x", self->priv->interface_subclass); mm_obj_dbg (self, " interface protocol: %02x", self->priv->interface_protocol); mm_obj_dbg (self, " interface number: %02x", self->priv->interface_number); } if (self->priv->interface_description) mm_obj_dbg (self, " interface description: %s", self->priv->interface_description); if (self->priv->physdev_sysfs_path) mm_obj_dbg (self, " device: %s", self->priv->physdev_sysfs_path); if (self->priv->subsystems) { g_autofree gchar *subsystems_str = NULL; subsystems_str = g_strjoinv (", ", self->priv->subsystems); mm_obj_dbg (self, " subsystems: %s", subsystems_str); } if (self->priv->drivers) { g_autofree gchar *drivers_str = NULL; drivers_str = g_strjoinv (", ", self->priv->drivers); mm_obj_dbg (self, " drivers: %s", drivers_str); } if (self->priv->physdev_vid) mm_obj_dbg (self, " vendor: %04x", self->priv->physdev_vid); if (self->priv->physdev_pid) mm_obj_dbg (self, " product: %04x", self->priv->physdev_pid); if (self->priv->physdev_subsystem_vid) mm_obj_dbg (self, " subsystem vendor: %04x", self->priv->physdev_subsystem_vid); if (self->priv->physdev_revision) mm_obj_dbg (self, " revision: %04x", self->priv->physdev_revision); if (self->priv->physdev_manufacturer) mm_obj_dbg (self, " manufacturer: %s", self->priv->physdev_manufacturer); if (self->priv->physdev_product) mm_obj_dbg (self, " product: %s", self->priv->physdev_product); preload_common_properties (self); } /*****************************************************************************/ static const gchar * kernel_device_get_subsystem (MMKernelDevice *self) { return mm_kernel_event_properties_get_subsystem (MM_KERNEL_DEVICE_GENERIC (self)->priv->properties); } static const gchar * kernel_device_get_name (MMKernelDevice *self) { return mm_kernel_event_properties_get_name (MM_KERNEL_DEVICE_GENERIC (self)->priv->properties); } static const gchar * kernel_device_get_sysfs_path (MMKernelDevice *self) { return MM_KERNEL_DEVICE_GENERIC (self)->priv->sysfs_path; } static const gchar * kernel_device_get_wwandev_sysfs_path (MMKernelDevice *self) { return MM_KERNEL_DEVICE_GENERIC (self)->priv->wwandev_sysfs_path; } static gint kernel_device_get_interface_number (MMKernelDevice *self) { return (gint) MM_KERNEL_DEVICE_GENERIC (self)->priv->interface_number; } static gint kernel_device_get_interface_class (MMKernelDevice *self) { return (gint) MM_KERNEL_DEVICE_GENERIC (self)->priv->interface_class; } static gint kernel_device_get_interface_subclass (MMKernelDevice *self) { return (gint) MM_KERNEL_DEVICE_GENERIC (self)->priv->interface_subclass; } static gint kernel_device_get_interface_protocol (MMKernelDevice *self) { return (gint) MM_KERNEL_DEVICE_GENERIC (self)->priv->interface_protocol; } static const gchar * kernel_device_get_interface_sysfs_path (MMKernelDevice *self) { return MM_KERNEL_DEVICE_GENERIC (self)->priv->interface_sysfs_path; } static const gchar * kernel_device_get_interface_description (MMKernelDevice *self) { return MM_KERNEL_DEVICE_GENERIC (self)->priv->interface_description; } static const gchar * kernel_device_get_physdev_uid (MMKernelDevice *self) { const gchar *uid; /* Prefer the one coming in the properties, if any */ if ((uid = mm_kernel_event_properties_get_uid (MM_KERNEL_DEVICE_GENERIC (self)->priv->properties)) != NULL) return uid; /* Try to load from properties set */ if ((uid = mm_kernel_device_get_property (self, ID_MM_PHYSDEV_UID)) != NULL) return uid; /* Use physical device path, if any */ if (MM_KERNEL_DEVICE_GENERIC (self)->priv->physdev_sysfs_path) return MM_KERNEL_DEVICE_GENERIC (self)->priv->physdev_sysfs_path; /* If there is no physdev sysfs path, e.g. for platform ports, use the device sysfs itself */ return MM_KERNEL_DEVICE_GENERIC (self)->priv->sysfs_path; } static const gchar * kernel_device_get_driver (MMKernelDevice *_self) { MMKernelDeviceGeneric *self = MM_KERNEL_DEVICE_GENERIC (_self); return (self->priv->drivers ? self->priv->drivers[0] : NULL); } static guint16 kernel_device_get_physdev_vid (MMKernelDevice *self) { return MM_KERNEL_DEVICE_GENERIC (self)->priv->physdev_vid; } static guint16 kernel_device_get_physdev_pid (MMKernelDevice *self) { return MM_KERNEL_DEVICE_GENERIC (self)->priv->physdev_pid; } static guint16 kernel_device_get_physdev_subsystem_vid (MMKernelDevice *self) { return MM_KERNEL_DEVICE_GENERIC (self)->priv->physdev_subsystem_vid; } static guint16 kernel_device_get_physdev_revision (MMKernelDevice *self) { return MM_KERNEL_DEVICE_GENERIC (self)->priv->physdev_revision; } static const gchar * kernel_device_get_physdev_sysfs_path (MMKernelDevice *self) { return MM_KERNEL_DEVICE_GENERIC (self)->priv->physdev_sysfs_path; } static const gchar * kernel_device_get_physdev_subsystem (MMKernelDevice *_self) { MMKernelDeviceGeneric *self = MM_KERNEL_DEVICE_GENERIC (_self); guint len; len = (self->priv->subsystems ? g_strv_length (self->priv->subsystems) : 0); return (len > 0 ? self->priv->subsystems[len - 1] : NULL); } static const gchar * kernel_device_get_physdev_manufacturer (MMKernelDevice *self) { return MM_KERNEL_DEVICE_GENERIC (self)->priv->physdev_manufacturer; } static const gchar * kernel_device_get_physdev_product (MMKernelDevice *self) { return MM_KERNEL_DEVICE_GENERIC (self)->priv->physdev_product; } static gboolean kernel_device_cmp (MMKernelDevice *a, MMKernelDevice *b) { return (!g_strcmp0 (mm_kernel_device_get_subsystem (a), mm_kernel_device_get_subsystem (b)) && !g_strcmp0 (mm_kernel_device_get_name (a), mm_kernel_device_get_name (b))); } /*****************************************************************************/ static gboolean check_condition (MMKernelDeviceGeneric *self, MMUdevRuleMatch *match) { gboolean condition_equal; condition_equal = (match->type == MM_UDEV_RULE_MATCH_TYPE_EQUAL); /* We only apply 'add' rules */ if (g_str_equal (match->parameter, "ACTION")) return ((!!strstr (match->value, "add")) == condition_equal); /* Exact SUBSYSTEM match */ if (g_str_equal (match->parameter, "SUBSYSTEM")) return ((self->priv->subsystems && !g_strcmp0 (self->priv->subsystems[0], match->value)) == condition_equal); /* Loose SUBSYSTEMS match */ if (g_str_equal (match->parameter, "SUBSYSTEMS")) return ((self->priv->subsystems && g_strv_contains ((const gchar * const *) self->priv->subsystems, match->value)) == condition_equal); /* Exact DRIVER match */ if (g_str_equal (match->parameter, "DRIVER")) return ((self->priv->drivers && !g_strcmp0 (self->priv->drivers[0], match->value)) == condition_equal); /* Loose DRIVERS match */ if (g_str_equal (match->parameter, "DRIVERS")) return ((self->priv->drivers && g_strv_contains ((const gchar * const *) self->priv->drivers, match->value)) == condition_equal); /* Device name checks */ if (g_str_equal (match->parameter, "KERNEL")) return (mm_kernel_device_generic_string_match (mm_kernel_device_get_name (MM_KERNEL_DEVICE (self)), match->value, self) == condition_equal); /* Device sysfs path checks; we allow both a direct match and a prefix patch */ if (g_str_equal (match->parameter, "DEVPATH")) { g_autofree gchar *prefix_match = NULL; /* If sysfs path invalid (e.g. path doesn't exist), no match */ if (!self->priv->sysfs_path) return FALSE; /* If not already doing a prefix match, do an implicit one. This is so that * we can add properties to the usb_device owning all ports, and then apply * the property to all ports individually processed here. */ if (match->value[0] && match->value[strlen (match->value) - 1] != '*') prefix_match = g_strdup_printf ("%s/*", match->value); if ((mm_kernel_device_generic_string_match (self->priv->sysfs_path, match->value, self) == condition_equal) || (prefix_match && mm_kernel_device_generic_string_match (self->priv->sysfs_path, prefix_match, self) == condition_equal)) return TRUE; if (g_str_has_prefix (self->priv->sysfs_path, "/sys")) { if ((mm_kernel_device_generic_string_match (&self->priv->sysfs_path[4], match->value, self) == condition_equal) || (prefix_match && mm_kernel_device_generic_string_match (&self->priv->sysfs_path[4], prefix_match, self) == condition_equal)) return TRUE; } return FALSE; } /* Attributes checks */ if (g_str_has_prefix (match->parameter, "ATTR")) { gchar *attribute; gchar *contents = NULL; gboolean result = FALSE; guint val; attribute = g_strdup (&match->parameter[5]); g_strdelimit (attribute, "{}", ' '); g_strstrip (attribute); /* VID/PID/SUBSYSTEM VID directly from our API */ if (g_str_equal (attribute, "idVendor") || g_str_equal (attribute, "vendor")) result = ((mm_get_uint_from_hex_str (match->value, &val)) && ((mm_kernel_device_get_physdev_vid (MM_KERNEL_DEVICE (self)) == val) == condition_equal)); else if (g_str_equal (attribute, "idProduct") || g_str_equal (attribute, "device")) result = ((mm_get_uint_from_hex_str (match->value, &val)) && ((mm_kernel_device_get_physdev_pid (MM_KERNEL_DEVICE (self)) == val) == condition_equal)); else if (g_str_equal (attribute, "subsystem_vendor")) result = ((mm_get_uint_from_hex_str (match->value, &val)) && ((mm_kernel_device_get_physdev_subsystem_vid (MM_KERNEL_DEVICE (self)) == val) == condition_equal)); /* manufacturer in the physdev */ else if (g_str_equal (attribute, "manufacturer")) result = ((self->priv->physdev_manufacturer && g_str_equal (self->priv->physdev_manufacturer, match->value)) == condition_equal); /* product in the physdev */ else if (g_str_equal (attribute, "product")) result = ((self->priv->physdev_product && g_str_equal (self->priv->physdev_product, match->value)) == condition_equal); /* interface class/subclass/protocol/number in the interface */ else if (g_str_equal (attribute, "bInterfaceClass")) result = (g_str_equal (match->value, "?*") || ((mm_get_uint_from_hex_str (match->value, &val)) && ((self->priv->interface_class == val) == condition_equal))); else if (g_str_equal (attribute, "bInterfaceSubClass")) result = (g_str_equal (match->value, "?*") || ((mm_get_uint_from_hex_str (match->value, &val)) && ((self->priv->interface_subclass == val) == condition_equal))); else if (g_str_equal (attribute, "bInterfaceProtocol")) result = (g_str_equal (match->value, "?*") || ((mm_get_uint_from_hex_str (match->value, &val)) && ((self->priv->interface_protocol == val) == condition_equal))); else if (g_str_equal (attribute, "bInterfaceNumber")) result = (g_str_equal (match->value, "?*") || ((mm_get_uint_from_hex_str (match->value, &val)) && ((self->priv->interface_number == val) == condition_equal))); else { g_autofree gchar *found_value = NULL; found_value = lookup_sysfs_attribute_as_string (self, attribute, g_str_has_prefix (match->parameter, "ATTRS")); result = ((found_value && g_str_equal (found_value, match->value)) == condition_equal); } g_free (contents); g_free (attribute); return result; } /* Previously set property checks */ if (g_str_has_prefix (match->parameter, "ENV")) { gchar *property; gboolean result = FALSE; property = g_strdup (&match->parameter[3]); g_strdelimit (property, "{}", ' '); g_strstrip (property); result = ((!g_strcmp0 ((const gchar *) g_object_get_data (G_OBJECT (self), property), match->value)) == condition_equal); g_free (property); return result; } mm_obj_warn (self, "unknown match condition parameter: %s", match->parameter); return FALSE; } static guint check_rule (MMKernelDeviceGeneric *self, guint rule_i) { MMUdevRule *rule; gboolean apply = TRUE; g_assert (rule_i < self->priv->rules->len); rule = &g_array_index (self->priv->rules, MMUdevRule, rule_i); if (rule->conditions) { guint condition_i; for (condition_i = 0; condition_i < rule->conditions->len; condition_i++) { MMUdevRuleMatch *match; match = &g_array_index (rule->conditions, MMUdevRuleMatch, condition_i); if (!check_condition (self, match)) { apply = FALSE; break; } } } if (apply) { switch (rule->result.type) { case MM_UDEV_RULE_RESULT_TYPE_PROPERTY: { gchar *property_value_read = NULL; if (g_str_equal (rule->result.content.property.value, "$attr{bInterfaceClass}")) property_value_read = g_strdup_printf ("%02x", self->priv->interface_class); else if (g_str_equal (rule->result.content.property.value, "$attr{bInterfaceSubClass}")) property_value_read = g_strdup_printf ("%02x", self->priv->interface_subclass); else if (g_str_equal (rule->result.content.property.value, "$attr{bInterfaceProtocol}")) property_value_read = g_strdup_printf ("%02x", self->priv->interface_protocol); else if (g_str_equal (rule->result.content.property.value, "$attr{bInterfaceNumber}")) property_value_read = g_strdup_printf ("%02x", self->priv->interface_number); /* add new property */ mm_obj_dbg (self, "property added: %s=%s", rule->result.content.property.name, property_value_read ? property_value_read : rule->result.content.property.value); if (!property_value_read) /* NOTE: we keep a reference to the list of rules ourselves, so it isn't * an issue if we re-use the same string (i.e. without g_strdup-ing it) * as a property value. */ g_object_set_data (G_OBJECT (self), rule->result.content.property.name, rule->result.content.property.value); else g_object_set_data_full (G_OBJECT (self), rule->result.content.property.name, property_value_read, g_free); break; } case MM_UDEV_RULE_RESULT_TYPE_LABEL: /* noop */ break; case MM_UDEV_RULE_RESULT_TYPE_GOTO_INDEX: /* Jump to a new index */ return rule->result.content.index; case MM_UDEV_RULE_RESULT_TYPE_GOTO_TAG: case MM_UDEV_RULE_RESULT_TYPE_UNKNOWN: default: g_assert_not_reached (); } } /* Go to the next rule */ return rule_i + 1; } static void preload_rule_properties (MMKernelDeviceGeneric *self) { guint i; g_assert (self->priv->rules); g_assert (self->priv->rules->len > 0); /* Start to process rules */ i = 0; while (i < self->priv->rules->len) { guint next_rule; next_rule = check_rule (self, i); i = next_rule; } } static void check_preload (MMKernelDeviceGeneric *self) { /* Only preload when properties and rules are set */ if (!self->priv->properties || !self->priv->rules) return; /* Don't preload on "remove" actions, where we don't have the device any more */ if (g_strcmp0 (mm_kernel_event_properties_get_action (self->priv->properties), "remove") == 0) return; /* Don't preload for devices in the 'virtual' subsystem */ if (g_strcmp0 (mm_kernel_event_properties_get_subsystem (self->priv->properties), "virtual") == 0) return; mm_obj_dbg (self, "preloading contents and properties..."); preload_contents (self); preload_rule_properties (self); } static gboolean kernel_device_has_property (MMKernelDevice *self, const gchar *property) { return !!g_object_get_data (G_OBJECT (self), property); } static const gchar * kernel_device_get_property (MMKernelDevice *self, const gchar *property) { return g_object_get_data (G_OBJECT (self), property); } /*****************************************************************************/ static gchar * build_attribute_data_key (const gchar *attribute) { return g_strdup_printf ("ATTR:%s", attribute); } static gboolean kernel_device_has_attribute (MMKernelDevice *self, const gchar *attribute) { return has_sysfs_attribute (MM_KERNEL_DEVICE_GENERIC (self)->priv->sysfs_path, attribute); } static const gchar * kernel_device_get_attribute (MMKernelDevice *_self, const gchar *attribute) { MMKernelDeviceGeneric *self; g_autofree gchar *key = NULL; gchar *value = NULL; self = MM_KERNEL_DEVICE_GENERIC (_self); key = build_attribute_data_key (attribute); value = g_object_get_data (G_OBJECT (self), key); if (!value) { value = read_sysfs_attribute_as_string (self->priv->sysfs_path, attribute); if (value) g_object_set_data_full (G_OBJECT (self), key, value, g_free); } return (const gchar *) value; } /*****************************************************************************/ MMKernelDevice * mm_kernel_device_generic_new_with_rules (MMKernelEventProperties *props, GArray *rules, GError **error) { /* Note: we allow NULL rules, e.g. for virtual devices */ return MM_KERNEL_DEVICE (g_initable_new (MM_TYPE_KERNEL_DEVICE_GENERIC, NULL, error, "properties", props, "rules", rules, NULL)); } MMKernelDevice * mm_kernel_device_generic_new (MMKernelEventProperties *props, GError **error) { static GArray *rules = NULL; /* We only try to load the default list of rules once */ if (G_UNLIKELY (!rules)) { rules = mm_kernel_device_generic_rules_load (UDEVRULESDIR, error); if (!rules) return NULL; } return mm_kernel_device_generic_new_with_rules (props, rules, error); } /*****************************************************************************/ static void mm_kernel_device_generic_init (MMKernelDeviceGeneric *self) { /* Initialize private data */ self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, MM_TYPE_KERNEL_DEVICE_GENERIC, MMKernelDeviceGenericPrivate); } static void set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { MMKernelDeviceGeneric *self = MM_KERNEL_DEVICE_GENERIC (object); switch (prop_id) { case PROP_PROPERTIES: g_assert (!self->priv->properties); self->priv->properties = g_value_dup_object (value); break; case PROP_RULES: g_assert (!self->priv->rules); self->priv->rules = g_value_dup_boxed (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) { MMKernelDeviceGeneric *self = MM_KERNEL_DEVICE_GENERIC (object); switch (prop_id) { case PROP_PROPERTIES: g_value_set_object (value, self->priv->properties); break; case PROP_RULES: g_value_set_boxed (value, self->priv->rules); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static gboolean initable_init (GInitable *initable, GCancellable *cancellable, GError **error) { MMKernelDeviceGeneric *self = MM_KERNEL_DEVICE_GENERIC (initable); const gchar *subsystem; check_preload (self); subsystem = mm_kernel_device_get_subsystem (MM_KERNEL_DEVICE (self)); if (!subsystem) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS, "subsystem is mandatory in kernel device"); return FALSE; } if (!mm_kernel_device_get_name (MM_KERNEL_DEVICE (self))) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS, "name is mandatory in kernel device"); return FALSE; } /* sysfs path is mandatory as output, and will only be given if the * specified device exists; but only if this wasn't a 'remove' event * and not a virtual device. */ if (self->priv->properties && g_strcmp0 (mm_kernel_event_properties_get_action (self->priv->properties), "remove") && g_strcmp0 (mm_kernel_event_properties_get_subsystem (self->priv->properties), "virtual") && !self->priv->sysfs_path) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS, "device %s/%s not found", mm_kernel_event_properties_get_subsystem (self->priv->properties), mm_kernel_event_properties_get_name (self->priv->properties)); return FALSE; } return TRUE; } static void dispose (GObject *object) { MMKernelDeviceGeneric *self = MM_KERNEL_DEVICE_GENERIC (object); g_clear_pointer (&self->priv->physdev_product, g_free); g_clear_pointer (&self->priv->physdev_manufacturer, g_free); g_clear_pointer (&self->priv->physdev_sysfs_path, g_free); g_clear_pointer (&self->priv->interface_description, g_free); g_clear_pointer (&self->priv->interface_sysfs_path, g_free); g_clear_pointer (&self->priv->sysfs_path, g_free); g_clear_pointer (&self->priv->drivers, g_strfreev); g_clear_pointer (&self->priv->subsystems, g_strfreev); g_clear_pointer (&self->priv->rules, g_array_unref); g_clear_object (&self->priv->properties); G_OBJECT_CLASS (mm_kernel_device_generic_parent_class)->dispose (object); } static void initable_iface_init (GInitableIface *iface) { iface->init = initable_init; } static void mm_kernel_device_generic_class_init (MMKernelDeviceGenericClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); MMKernelDeviceClass *kernel_device_class = MM_KERNEL_DEVICE_CLASS (klass); g_type_class_add_private (object_class, sizeof (MMKernelDeviceGenericPrivate)); object_class->dispose = dispose; object_class->get_property = get_property; object_class->set_property = set_property; kernel_device_class->get_subsystem = kernel_device_get_subsystem; kernel_device_class->get_name = kernel_device_get_name; kernel_device_class->get_driver = kernel_device_get_driver; kernel_device_class->get_sysfs_path = kernel_device_get_sysfs_path; kernel_device_class->get_wwandev_sysfs_path = kernel_device_get_wwandev_sysfs_path; kernel_device_class->get_physdev_uid = kernel_device_get_physdev_uid; kernel_device_class->get_physdev_vid = kernel_device_get_physdev_vid; kernel_device_class->get_physdev_pid = kernel_device_get_physdev_pid; kernel_device_class->get_physdev_subsystem_vid = kernel_device_get_physdev_subsystem_vid; kernel_device_class->get_physdev_revision = kernel_device_get_physdev_revision; kernel_device_class->get_physdev_sysfs_path = kernel_device_get_physdev_sysfs_path; kernel_device_class->get_physdev_subsystem = kernel_device_get_physdev_subsystem; kernel_device_class->get_physdev_manufacturer = kernel_device_get_physdev_manufacturer; kernel_device_class->get_physdev_product = kernel_device_get_physdev_product; kernel_device_class->get_interface_number = kernel_device_get_interface_number; kernel_device_class->get_interface_class = kernel_device_get_interface_class; kernel_device_class->get_interface_subclass = kernel_device_get_interface_subclass; kernel_device_class->get_interface_protocol = kernel_device_get_interface_protocol; kernel_device_class->get_interface_sysfs_path = kernel_device_get_interface_sysfs_path; kernel_device_class->get_interface_description = kernel_device_get_interface_description; kernel_device_class->cmp = kernel_device_cmp; kernel_device_class->has_property = kernel_device_has_property; kernel_device_class->get_property = kernel_device_get_property; kernel_device_class->has_attribute = kernel_device_has_attribute; kernel_device_class->get_attribute = kernel_device_get_attribute; /* Device-wide properties are stored per-port in the generic backend */ kernel_device_class->has_global_property = kernel_device_has_property; kernel_device_class->get_global_property = kernel_device_get_property; properties[PROP_PROPERTIES] = g_param_spec_object ("properties", "Properties", "Generic kernel event properties", MM_TYPE_KERNEL_EVENT_PROPERTIES, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY); g_object_class_install_property (object_class, PROP_PROPERTIES, properties[PROP_PROPERTIES]); properties[PROP_RULES] = g_param_spec_boxed ("rules", "Rules", "List of rules to apply", G_TYPE_ARRAY, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY); g_object_class_install_property (object_class, PROP_RULES, properties[PROP_RULES]); } ModemManager-1.23.4-dev/src/kerneldevice/mm-kernel-device-generic.h000066400000000000000000000050401456466623000250620ustar00rootroot00000000000000/* -*- 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) 2016 Velocloud, Inc. */ #ifndef MM_KERNEL_DEVICE_GENERIC_H #define MM_KERNEL_DEVICE_GENERIC_H #include #include #define _LIBMM_INSIDE_MM #include #include "mm-kernel-device.h" #define MM_TYPE_KERNEL_DEVICE_GENERIC (mm_kernel_device_generic_get_type ()) #define MM_KERNEL_DEVICE_GENERIC(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_KERNEL_DEVICE_GENERIC, MMKernelDeviceGeneric)) #define MM_KERNEL_DEVICE_GENERIC_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_KERNEL_DEVICE_GENERIC, MMKernelDeviceGenericClass)) #define MM_IS_KERNEL_DEVICE_GENERIC(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_KERNEL_DEVICE_GENERIC)) #define MM_IS_KERNEL_DEVICE_GENERIC_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_KERNEL_DEVICE_GENERIC)) #define MM_KERNEL_DEVICE_GENERIC_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_KERNEL_DEVICE_GENERIC, MMKernelDeviceGenericClass)) typedef struct _MMKernelDeviceGeneric MMKernelDeviceGeneric; typedef struct _MMKernelDeviceGenericClass MMKernelDeviceGenericClass; typedef struct _MMKernelDeviceGenericPrivate MMKernelDeviceGenericPrivate; struct _MMKernelDeviceGeneric { MMKernelDevice parent; MMKernelDeviceGenericPrivate *priv; }; struct _MMKernelDeviceGenericClass { MMKernelDeviceClass parent; }; GType mm_kernel_device_generic_get_type (void); G_DEFINE_AUTOPTR_CLEANUP_FUNC (MMKernelDeviceGeneric, g_object_unref) MMKernelDevice *mm_kernel_device_generic_new (MMKernelEventProperties *properties, GError **error); MMKernelDevice *mm_kernel_device_generic_new_with_rules (MMKernelEventProperties *properties, GArray *rules, GError **error); #endif /* MM_KERNEL_DEVICE_GENERIC_H */ ModemManager-1.23.4-dev/src/kerneldevice/mm-kernel-device-helpers.c000066400000000000000000000110361456466623000251050ustar00rootroot00000000000000/* -*- 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) 2021 Aleksander Morgado */ #include #include #include #include #include "mm-log-object.h" #include "mm-kernel-device-helpers.h" /******************************************************************************/ gchar * mm_kernel_device_get_lower_device_name (const gchar *sysfs_path) { g_autoptr(GFile) dirfile = NULL; g_autoptr(GFileEnumerator) direnum = NULL; dirfile = g_file_new_for_path (sysfs_path); direnum = g_file_enumerate_children (dirfile, G_FILE_ATTRIBUTE_STANDARD_NAME, G_FILE_QUERY_INFO_NONE, NULL, NULL); if (!direnum) return NULL; while (TRUE) { GFileInfo *info; g_autofree gchar *filename = NULL; g_autofree gchar *link_path = NULL; g_autofree gchar *real_path = NULL; if (!g_file_enumerator_iterate (direnum, &info, NULL, NULL, NULL) || !info) break; filename = g_file_info_get_attribute_as_string (info, G_FILE_ATTRIBUTE_STANDARD_NAME); if (!filename || !g_str_has_prefix (filename, "lower_")) continue; link_path = g_strdup_printf ("%s/%s", sysfs_path, filename); real_path = realpath (link_path, NULL); if (!real_path) continue; return g_path_get_basename (real_path); } return NULL; } /******************************************************************************/ static gchar * build_string_match_pattern (const gchar *str) { GString *regex_pattern; const gchar *str_start; gsize len; g_autofree gchar *aux = NULL; gboolean prefix_match = FALSE; gboolean suffix_match = FALSE; /* We allow prefix and suffix matches given as input, by means of the * single '*' character given either at the beginning or the end of the * string. If given in another place, it will assumed to be explicitly * the '*' character, not a catch-all indication. */ regex_pattern = g_string_new (NULL); /* suffix match? */ if (str[0] == '*') { str_start = &str[1]; suffix_match = TRUE; } else str_start = str; /* prefix match? */ len = strlen (str_start); if (len > 0 && str_start[len - 1] == '*') { len--; prefix_match = TRUE; } /* match start of string */ g_string_append (regex_pattern, "^"); if (suffix_match) g_string_append (regex_pattern, ".*"); aux = g_regex_escape_string (str_start, len); g_string_append (regex_pattern, aux); if (prefix_match) g_string_append (regex_pattern, ".*"); /* match end of string */ g_string_append (regex_pattern, "$"); return g_string_free (regex_pattern, FALSE); } gboolean mm_kernel_device_generic_string_match (const gchar *str, const gchar *pattern, gpointer log_object) { g_autoptr(GError) inner_error = NULL; g_autoptr(GRegex) regex = NULL; g_autoptr(GMatchInfo) match_info = NULL; g_autofree gchar *regex_pattern = NULL; regex_pattern = build_string_match_pattern (pattern); regex = g_regex_new (regex_pattern, G_REGEX_UNGREEDY, 0, &inner_error); if (!regex) { mm_obj_warn (log_object, "invalid pattern in rule '%s': %s", regex_pattern, inner_error->message); return FALSE; } g_regex_match_full (regex, str, -1, 0, 0, &match_info, &inner_error); if (inner_error) { mm_obj_warn (log_object, "couldn't apply pattern match in rule '%s': %s", regex_pattern, inner_error->message); return FALSE; } if (!g_match_info_matches (match_info)) return FALSE; mm_obj_dbg (log_object, "pattern '%s' matched: '%s'", regex_pattern, str); return TRUE; } ModemManager-1.23.4-dev/src/kerneldevice/mm-kernel-device-helpers.h000066400000000000000000000024261456466623000251150ustar00rootroot00000000000000/* -*- 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) 2021 Aleksander Morgado */ #ifndef MM_KERNEL_DEVICE_HELPERS_H #define MM_KERNEL_DEVICE_HELPERS_H #include /* For virtual devices that keep a "lower" link to the parent physical device * they're based on, get the name of that parent physical device. * (e.g. lower_device_name(qmimux0) == wwan0) */ gchar *mm_kernel_device_get_lower_device_name (const gchar *sysfs_path); /* Generic string matching logic */ gboolean mm_kernel_device_generic_string_match (const gchar *str, const gchar *pattern, gpointer log_object); #endif /* MM_KERNEL_DEVICE_HELPERS_H */ ModemManager-1.23.4-dev/src/kerneldevice/mm-kernel-device-qrtr.c000066400000000000000000000155121456466623000244360ustar00rootroot00000000000000/* -*- 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 2020 Google LLC */ #include #define _LIBMM_INSIDE_MM #include #include #include "mm-kernel-device-qrtr.h" G_DEFINE_TYPE (MMKernelDeviceQrtr, mm_kernel_device_qrtr, MM_TYPE_KERNEL_DEVICE) enum { PROP_0, PROP_QRTR_NODE, PROP_LAST }; static GParamSpec *properties[PROP_LAST]; struct _MMKernelDeviceQrtrPrivate { QrtrNode *node; gchar *name; gchar *physdev_uid; }; /*****************************************************************************/ gchar * mm_kernel_device_qrtr_helper_build_name (guint32 node_id) { return g_strdup_printf ("qrtr%u", node_id); } /*****************************************************************************/ QrtrNode * mm_kernel_device_qrtr_get_node (MMKernelDeviceQrtr *self) { return g_object_ref (self->priv->node); } /*****************************************************************************/ static gboolean kernel_device_cmp (MMKernelDevice *_a, MMKernelDevice *_b) { MMKernelDeviceQrtr *a; MMKernelDeviceQrtr *b; a = MM_KERNEL_DEVICE_QRTR (_a); b = MM_KERNEL_DEVICE_QRTR (_b); return qrtr_node_get_id (a->priv->node) == qrtr_node_get_id (b->priv->node); } static gboolean kernel_device_has_property (MMKernelDevice *_self, const gchar *property) { MMKernelDeviceQrtr *self; self = MM_KERNEL_DEVICE_QRTR (_self); return !!g_object_get_data (G_OBJECT (self), property); } static const gchar * kernel_device_get_property (MMKernelDevice *_self, const gchar *property) { MMKernelDeviceQrtr *self; self = MM_KERNEL_DEVICE_QRTR (_self); return g_object_get_data (G_OBJECT (self), property); } static const gchar * kernel_device_get_driver (MMKernelDevice *_self) { return MM_KERNEL_DEVICE_QRTR_DRIVER; } static const gchar * kernel_device_get_name (MMKernelDevice *_self) { MMKernelDeviceQrtr *self; self = MM_KERNEL_DEVICE_QRTR (_self); if (!self->priv->name) self->priv->name = mm_kernel_device_qrtr_helper_build_name (qrtr_node_get_id (self->priv->node)); return self->priv->name; } static const gchar * kernel_device_get_physdev_uid (MMKernelDevice *_self) { return MM_KERNEL_DEVICE_QRTR_PHYSDEV_UID; } static const gchar * kernel_device_get_subsystem (MMKernelDevice *_self) { return MM_KERNEL_DEVICE_QRTR_SUBSYSTEM; } /*****************************************************************************/ MMKernelDevice * mm_kernel_device_qrtr_new (QrtrNode *qrtr_node) { MMKernelDevice *self; self = MM_KERNEL_DEVICE (g_object_new (MM_TYPE_KERNEL_DEVICE_QRTR, "qrtr-node", qrtr_node, NULL)); return self; } /*****************************************************************************/ static void mm_kernel_device_qrtr_init (MMKernelDeviceQrtr *self) { /* Initialize private data */ self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, MM_TYPE_KERNEL_DEVICE_QRTR, MMKernelDeviceQrtrPrivate); /* Set properties*/ g_object_set_data_full (G_OBJECT (self), ID_MM_PORT_TYPE_QMI, g_strdup ("true"), g_free); g_object_set_data_full (G_OBJECT (self), ID_MM_CANDIDATE, g_strdup ("1"), g_free); /* For now we're assuming that QRTR ports are available exclusively on Qualcomm SoCs */ g_object_set_data_full (G_OBJECT (self), "ID_MM_QCOM_SOC", g_strdup ("1"), g_free); } static void set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { MMKernelDeviceQrtr *self = MM_KERNEL_DEVICE_QRTR (object); switch (prop_id) { case PROP_QRTR_NODE: g_assert (!self->priv->node); self->priv->node = g_value_dup_object (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) { MMKernelDeviceQrtr *self = MM_KERNEL_DEVICE_QRTR (object); switch (prop_id) { case PROP_QRTR_NODE: g_value_set_object (value, self->priv->node); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void dispose (GObject *object) { MMKernelDeviceQrtr *self = MM_KERNEL_DEVICE_QRTR (object); g_clear_pointer (&self->priv->name, g_free); g_clear_pointer (&self->priv->physdev_uid, g_free); g_object_unref (self->priv->node); G_OBJECT_CLASS (mm_kernel_device_qrtr_parent_class)->dispose (object); } static void mm_kernel_device_qrtr_class_init (MMKernelDeviceQrtrClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); MMKernelDeviceClass *kernel_device_class = MM_KERNEL_DEVICE_CLASS (klass); g_type_class_add_private (object_class, sizeof (MMKernelDeviceQrtrPrivate)); object_class->dispose = dispose; object_class->get_property = get_property; object_class->set_property = set_property; kernel_device_class->get_driver = kernel_device_get_driver; kernel_device_class->get_name = kernel_device_get_name; kernel_device_class->get_physdev_uid = kernel_device_get_physdev_uid; kernel_device_class->get_subsystem = kernel_device_get_subsystem; kernel_device_class->cmp = kernel_device_cmp; kernel_device_class->has_property = kernel_device_has_property; kernel_device_class->get_property = kernel_device_get_property; /* Device-wide properties are stored per-port in the qrtr backend */ kernel_device_class->has_global_property = kernel_device_has_property; kernel_device_class->get_global_property = kernel_device_get_property; properties[PROP_QRTR_NODE] = g_param_spec_object ("qrtr-node", "qrtr node", "Node object as reported by QrtrNode", QRTR_TYPE_NODE, G_PARAM_READWRITE); g_object_class_install_properties (object_class, PROP_LAST, properties); } ModemManager-1.23.4-dev/src/kerneldevice/mm-kernel-device-qrtr.h000066400000000000000000000055061456466623000244450ustar00rootroot00000000000000/* -*- 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 2020 Google LLC */ #ifndef MM_KERNEL_DEVICE_QRTR_H #define MM_KERNEL_DEVICE_QRTR_H #include #include #include #define _LIBMM_INSIDE_MM #include #include "mm-kernel-device.h" /* Driver string reported for all QRTR nodes; not really a kernel driver */ #define MM_KERNEL_DEVICE_QRTR_DRIVER "qrtr" /* Subsytem string reported for all QRTR nodes; not really a kernel subsystem */ #define MM_KERNEL_DEVICE_QRTR_SUBSYSTEM "qrtr" /* Physical device UID string reported for all QRTR nodes; equal to the UID * used in the 'qcom-soc' plugin, which is the only one supporting QRTR nodes * for now. This UID must be equal for all ports on the same modem, and so for * Qualcomm SoCs we use the same plugin name as common string. */ #define MM_KERNEL_DEVICE_QRTR_PHYSDEV_UID "qcom-soc" /* Helper to create a unique device name from the QRTR node id */ gchar *mm_kernel_device_qrtr_helper_build_name (guint32 node_id); #define MM_TYPE_KERNEL_DEVICE_QRTR (mm_kernel_device_qrtr_get_type ()) #define MM_KERNEL_DEVICE_QRTR(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_KERNEL_DEVICE_QRTR, MMKernelDeviceQrtr)) #define MM_KERNEL_DEVICE_QRTR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_KERNEL_DEVICE_QRTR, MMKernelDeviceQrtrClass)) #define MM_IS_KERNEL_DEVICE_QRTR(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_KERNEL_DEVICE_QRTR)) #define MM_IS_KERNEL_DEVICE_QRTR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_KERNEL_DEVICE_QRTR)) #define MM_KERNEL_DEVICE_QRTR_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_KERNEL_DEVICE_QRTR, MMKernelDeviceQrtrClass)) typedef struct _MMKernelDeviceQrtr MMKernelDeviceQrtr; typedef struct _MMKernelDeviceQrtrClass MMKernelDeviceQrtrClass; typedef struct _MMKernelDeviceQrtrPrivate MMKernelDeviceQrtrPrivate; struct _MMKernelDeviceQrtr { MMKernelDevice parent; MMKernelDeviceQrtrPrivate *priv; }; struct _MMKernelDeviceQrtrClass { MMKernelDeviceClass parent; }; QrtrNode *mm_kernel_device_qrtr_get_node (MMKernelDeviceQrtr *self); GType mm_kernel_device_qrtr_get_type (void); MMKernelDevice *mm_kernel_device_qrtr_new (QrtrNode *qrtr_node); #endif /* MM_KERNEL_DEVICE_QRTR_H */ ModemManager-1.23.4-dev/src/kerneldevice/mm-kernel-device-udev.c000066400000000000000000000722021456466623000244100ustar00rootroot00000000000000/* -*- 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) 2016 Velocloud, Inc. * Copyright (C) 2020 Aleksander Morgado */ #include #define _LIBMM_INSIDE_MM #include #include #include "mm-kernel-device-udev.h" #include "mm-kernel-device-helpers.h" #include "mm-log-object.h" static void initable_iface_init (GInitableIface *iface); G_DEFINE_TYPE_EXTENDED (MMKernelDeviceUdev, mm_kernel_device_udev, MM_TYPE_KERNEL_DEVICE, 0, G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, initable_iface_init)) enum { PROP_0, PROP_UDEV_CLIENT, PROP_UDEV_DEVICE, PROP_PROPERTIES, PROP_LAST }; static GParamSpec *properties[PROP_LAST]; struct _MMKernelDeviceUdevPrivate { GUdevClient *client; GUdevDevice *device; GUdevDevice *interface; GUdevDevice *physdev; guint16 vendor; guint16 product; guint16 subsystem_vendor; guint16 revision; gchar *driver; MMKernelEventProperties *properties; }; /*****************************************************************************/ static guint udev_device_get_sysfs_attr_as_hex (GUdevDevice *device, const gchar *attribute) { const gchar *attr; guint val = 0; attr = g_udev_device_get_sysfs_attr (device, attribute); if (attr) mm_get_uint_from_hex_str (attr, &val); return val; } /*****************************************************************************/ static void preload_contents_other (MMKernelDeviceUdev *self) { g_autofree gchar *lower_device_name = NULL; /* For any other kind of bus (or the absence of one, as in virtual devices), * assume this is a single port device and don't try to match multiple ports * together. Also, obviously, no vendor, product, revision or interface. */ self->priv->driver = g_strdup (g_udev_device_get_driver (self->priv->device)); /* But look for a lower real physical device, as we may have one */ lower_device_name = mm_kernel_device_get_lower_device_name (g_udev_device_get_sysfs_path (self->priv->device)); if (lower_device_name) { g_autoptr(GUdevDevice) lower_device = NULL; const gchar *subsystem; subsystem = g_udev_device_get_subsystem (self->priv->device); lower_device = g_udev_client_query_by_subsystem_and_name (self->priv->client, subsystem, lower_device_name); if (!lower_device) { mm_obj_dbg (self, "couldn't find lower device: %s/%s", subsystem, lower_device_name); } else { g_autoptr(MMKernelDevice) lower_kernel_device = NULL; mm_obj_dbg (self, "setting up lower device: %s/%s", subsystem, lower_device_name); lower_kernel_device = mm_kernel_device_udev_new (self->priv->client, lower_device); g_object_set (self, "lower-device", lower_kernel_device, NULL); } } } static void preload_contents_platform (MMKernelDeviceUdev *self, const gchar *platform) { g_autoptr(GUdevDevice) iter = NULL; iter = g_object_ref (self->priv->device); while (iter) { GUdevDevice *parent; /* Store the first driver found */ if (!self->priv->driver) self->priv->driver = g_strdup (g_udev_device_get_driver (iter)); /* Take first parent with the given platform subsystem as physical device */ if (!self->priv->physdev && (g_strcmp0 (g_udev_device_get_subsystem (iter), platform) == 0)) { self->priv->physdev = g_object_ref (iter); /* stop traversing as soon as the physical device is found */ break; } parent = g_udev_device_get_parent (iter); g_clear_object (&iter); iter = parent; } } static void preload_contents_pcmcia (MMKernelDeviceUdev *self) { g_autoptr(GUdevDevice) iter = NULL; gboolean pcmcia_subsystem_found = FALSE; iter = g_object_ref (self->priv->device); while (iter) { g_autoptr(GUdevDevice) parent = NULL; /* Store the first driver found */ if (!self->priv->driver) self->priv->driver = g_strdup (g_udev_device_get_driver (iter)); if (g_strcmp0 (g_udev_device_get_subsystem (iter), "pcmcia") == 0) pcmcia_subsystem_found = TRUE; /* If the parent of this PCMCIA device is no longer part of * the PCMCIA subsystem, we want to stop since we're looking * for the base PCMCIA device, not the PCMCIA controller which * is usually PCI or some other bus type. */ parent = g_udev_device_get_parent (iter); if (pcmcia_subsystem_found && parent && (g_strcmp0 (g_udev_device_get_subsystem (parent), "pcmcia") != 0)) { self->priv->vendor = udev_device_get_sysfs_attr_as_hex (iter, "manf_id"); self->priv->product = udev_device_get_sysfs_attr_as_hex (iter, "card_id"); self->priv->physdev = g_object_ref (iter); /* stop traversing as soon as the physical device is found */ break; } g_clear_object (&iter); iter = g_steal_pointer (&parent); } } static void preload_contents_pci (MMKernelDeviceUdev *self) { g_autoptr(GUdevDevice) iter = NULL; iter = g_object_ref (self->priv->device); while (iter) { GUdevDevice *parent; /* Store the first driver found */ if (!self->priv->driver) self->priv->driver = g_strdup (g_udev_device_get_driver (iter)); /* the PCI channel specific devices have their own drivers and * subsystems, we can rely on the physical device being the first * one that reports the 'pci' subsystem */ if (!self->priv->physdev && (g_strcmp0 (g_udev_device_get_subsystem (iter), "pci") == 0)) { self->priv->vendor = udev_device_get_sysfs_attr_as_hex (iter, "vendor"); self->priv->product = udev_device_get_sysfs_attr_as_hex (iter, "device"); self->priv->subsystem_vendor = udev_device_get_sysfs_attr_as_hex (iter, "subsystem_vendor"); self->priv->revision = udev_device_get_sysfs_attr_as_hex (iter, "revision"); self->priv->physdev = g_object_ref (iter); /* stop traversing as soon as the physical device is found */ break; } parent = g_udev_device_get_parent (iter); g_clear_object (&iter); iter = parent; } } static void preload_contents_usb (MMKernelDeviceUdev *self) { g_autoptr(GUdevDevice) iter = NULL; iter = g_object_ref (self->priv->device); while (iter) { GUdevDevice *parent; const gchar *devtype; devtype = g_udev_device_get_devtype (iter); /* is this the USB interface? */ if (!self->priv->interface && (g_strcmp0 (devtype, "usb_interface") == 0)) { self->priv->interface = g_object_ref (iter); self->priv->driver = g_strdup (g_udev_device_get_driver (iter)); } /* is this the USB physdev? */ if (!self->priv->physdev && (g_strcmp0 (devtype, "usb_device") == 0)) { self->priv->vendor = udev_device_get_sysfs_attr_as_hex (iter, "idVendor"); self->priv->product = udev_device_get_sysfs_attr_as_hex (iter, "idProduct"); self->priv->revision = udev_device_get_sysfs_attr_as_hex (iter, "bcdDevice"); self->priv->physdev = g_object_ref (iter); /* stop traversing as soon as the physical device is found */ break; } parent = g_udev_device_get_parent (iter); g_clear_object (&iter); iter = parent; } } static gchar * find_device_bus_subsystem (MMKernelDeviceUdev *self) { g_autoptr(GUdevDevice) iter = NULL; iter = g_object_ref (self->priv->device); while (iter) { const gchar *subsys; GUdevDevice *parent; /* stop search as soon as we find a parent object * of one of the supported bus subsystems */ subsys = g_udev_device_get_subsystem (iter); if ((g_strcmp0 (subsys, "usb") == 0) || (g_strcmp0 (subsys, "pcmcia") == 0) || (g_strcmp0 (subsys, "pci") == 0) || (g_strcmp0 (subsys, "platform") == 0) || (g_strcmp0 (subsys, "pnp") == 0) || (g_strcmp0 (subsys, "sdio") == 0)) return g_strdup (subsys); parent = g_udev_device_get_parent (iter); g_clear_object (&iter); iter = parent; } /* no more parents to check */ return NULL; } static void preload_contents (MMKernelDeviceUdev *self) { g_autofree gchar *bus_subsys = NULL; bus_subsys = find_device_bus_subsystem (self); if (g_strcmp0 (bus_subsys, "usb") == 0) preload_contents_usb (self); else if (g_strcmp0 (bus_subsys, "pcmcia") == 0) preload_contents_pcmcia (self); else if (g_strcmp0 (bus_subsys, "pci") == 0) preload_contents_pci (self); else if ((g_strcmp0 (bus_subsys, "platform") == 0) || (g_strcmp0 (bus_subsys, "pnp") == 0) || (g_strcmp0 (bus_subsys, "sdio") == 0)) preload_contents_platform (self, bus_subsys); else preload_contents_other (self); if (!bus_subsys) return; mm_obj_dbg (self, "port contents loaded:"); mm_obj_dbg (self, " bus: %s", bus_subsys ? bus_subsys : "n/a"); if (self->priv->interface) mm_obj_dbg (self, " interface: %s", g_udev_device_get_sysfs_path (self->priv->interface)); if (self->priv->physdev) mm_obj_dbg (self, " device: %s", g_udev_device_get_sysfs_path (self->priv->physdev)); if (self->priv->driver) mm_obj_dbg (self, " driver: %s", self->priv->driver); if (self->priv->vendor) mm_obj_dbg (self, " vendor: %04x", self->priv->vendor); if (self->priv->product) mm_obj_dbg (self, " product: %04x", self->priv->product); if (self->priv->subsystem_vendor) mm_obj_dbg (self, " subsystem vendor: %04x", self->priv->subsystem_vendor); if (self->priv->revision) mm_obj_dbg (self, " revision: %04x", self->priv->revision); } /*****************************************************************************/ static const gchar * kernel_device_get_subsystem (MMKernelDevice *_self) { MMKernelDeviceUdev *self; self = MM_KERNEL_DEVICE_UDEV (_self); if (self->priv->device) return g_udev_device_get_subsystem (self->priv->device); g_assert (self->priv->properties); return mm_kernel_event_properties_get_subsystem (self->priv->properties); } static const gchar * kernel_device_get_name (MMKernelDevice *_self) { MMKernelDeviceUdev *self; self = MM_KERNEL_DEVICE_UDEV (_self); if (self->priv->device) return g_udev_device_get_name (self->priv->device); g_assert (self->priv->properties); return mm_kernel_event_properties_get_name (self->priv->properties); } static const gchar * kernel_device_get_driver (MMKernelDevice *self) { return MM_KERNEL_DEVICE_UDEV (self)->priv->driver; } static const gchar * kernel_device_get_sysfs_path (MMKernelDevice *_self) { MMKernelDeviceUdev *self; self = MM_KERNEL_DEVICE_UDEV (_self); return (self->priv->device ? g_udev_device_get_sysfs_path (self->priv->device) : NULL); } static const gchar * kernel_device_get_wwandev_sysfs_path (MMKernelDevice *_self) { g_autoptr(GUdevDevice) parent = NULL; MMKernelDeviceUdev *self; const gchar *subsys; self = MM_KERNEL_DEVICE_UDEV (_self); parent = g_udev_device_get_parent (self->priv->device); if (!parent) return NULL; subsys = g_udev_device_get_subsystem (parent); if (!subsys || g_strcmp0 (subsys, "wwan")) return NULL; return g_udev_device_get_sysfs_path (parent); } static const gchar * kernel_device_get_physdev_uid (MMKernelDevice *_self) { MMKernelDeviceUdev *self; const gchar *uid = NULL; self = MM_KERNEL_DEVICE_UDEV (_self); /* Prefer the one coming in the properties, if any */ if (self->priv->properties) { if ((uid = mm_kernel_event_properties_get_uid (self->priv->properties)) != NULL) return uid; } /* Try to load from properties set on the physical device */ if ((uid = mm_kernel_device_get_global_property (_self, ID_MM_PHYSDEV_UID)) != NULL) return uid; /* Use physical device sysfs path, if any */ if (self->priv->physdev && (uid = g_udev_device_get_sysfs_path (self->priv->physdev)) != NULL) return uid; /* If there is no physical device sysfs path, use the device sysfs itself */ g_assert (self->priv->device); return g_udev_device_get_sysfs_path (self->priv->device); } static guint16 kernel_device_get_physdev_vid (MMKernelDevice *self) { return MM_KERNEL_DEVICE_UDEV (self)->priv->vendor; } static guint16 kernel_device_get_physdev_pid (MMKernelDevice *self) { return MM_KERNEL_DEVICE_UDEV (self)->priv->product; } static guint16 kernel_device_get_physdev_subsystem_vid (MMKernelDevice *self) { return MM_KERNEL_DEVICE_UDEV (self)->priv->subsystem_vendor; } static guint16 kernel_device_get_physdev_revision (MMKernelDevice *self) { return MM_KERNEL_DEVICE_UDEV (self)->priv->revision; } static const gchar * kernel_device_get_physdev_sysfs_path (MMKernelDevice *_self) { MMKernelDeviceUdev *self; self = MM_KERNEL_DEVICE_UDEV (_self); return (self->priv->physdev ? g_udev_device_get_sysfs_path (self->priv->physdev) : NULL); } static const gchar * kernel_device_get_physdev_subsystem (MMKernelDevice *_self) { MMKernelDeviceUdev *self; self = MM_KERNEL_DEVICE_UDEV (_self); return (self->priv->physdev ? g_udev_device_get_subsystem (self->priv->physdev) : NULL); } static const gchar * kernel_device_get_physdev_manufacturer (MMKernelDevice *_self) { MMKernelDeviceUdev *self; self = MM_KERNEL_DEVICE_UDEV (_self); return (self->priv->physdev ? g_udev_device_get_sysfs_attr (self->priv->physdev, "manufacturer") : NULL); } static const gchar * kernel_device_get_physdev_product (MMKernelDevice *_self) { MMKernelDeviceUdev *self; self = MM_KERNEL_DEVICE_UDEV (_self); return (self->priv->physdev ? g_udev_device_get_sysfs_attr (self->priv->physdev, "product") : NULL); } static gint kernel_device_get_interface_number (MMKernelDevice *_self) { MMKernelDeviceUdev *self; self = MM_KERNEL_DEVICE_UDEV (_self); return (self->priv->interface ? (gint) udev_device_get_sysfs_attr_as_hex (self->priv->interface, "bInterfaceNumber") : -1); } static gint kernel_device_get_interface_class (MMKernelDevice *_self) { MMKernelDeviceUdev *self; self = MM_KERNEL_DEVICE_UDEV (_self); return (self->priv->interface ? (gint) udev_device_get_sysfs_attr_as_hex (self->priv->interface, "bInterfaceClass") : -1); } static gint kernel_device_get_interface_subclass (MMKernelDevice *_self) { MMKernelDeviceUdev *self; self = MM_KERNEL_DEVICE_UDEV (_self); return (self->priv->interface ? (gint) udev_device_get_sysfs_attr_as_hex (self->priv->interface, "bInterfaceSubClass") : -1); } static gint kernel_device_get_interface_protocol (MMKernelDevice *_self) { MMKernelDeviceUdev *self; self = MM_KERNEL_DEVICE_UDEV (_self); return (self->priv->interface ? (gint) udev_device_get_sysfs_attr_as_hex (self->priv->interface, "bInterfaceProtocol") : -1); } static const gchar * kernel_device_get_interface_sysfs_path (MMKernelDevice *_self) { MMKernelDeviceUdev *self; self = MM_KERNEL_DEVICE_UDEV (_self); return (self->priv->interface ? g_udev_device_get_sysfs_path (self->priv->interface) : NULL); } static const gchar * kernel_device_get_interface_description (MMKernelDevice *_self) { MMKernelDeviceUdev *self; self = MM_KERNEL_DEVICE_UDEV (_self); return (self->priv->interface ? g_udev_device_get_sysfs_attr (self->priv->interface, "interface") : NULL); } static gboolean kernel_device_cmp (MMKernelDevice *_a, MMKernelDevice *_b) { MMKernelDeviceUdev *a; MMKernelDeviceUdev *b; a = MM_KERNEL_DEVICE_UDEV (_a); b = MM_KERNEL_DEVICE_UDEV (_b); if (a->priv->device && b->priv->device) { if (g_udev_device_has_property (a->priv->device, "DEVPATH_OLD") && g_str_has_suffix (g_udev_device_get_sysfs_path (b->priv->device), g_udev_device_get_property (a->priv->device, "DEVPATH_OLD"))) return TRUE; if (g_udev_device_has_property (b->priv->device, "DEVPATH_OLD") && g_str_has_suffix (g_udev_device_get_sysfs_path (a->priv->device), g_udev_device_get_property (b->priv->device, "DEVPATH_OLD"))) return TRUE; return !g_strcmp0 (g_udev_device_get_sysfs_path (a->priv->device), g_udev_device_get_sysfs_path (b->priv->device)); } return (!g_strcmp0 (mm_kernel_device_get_subsystem (_a), mm_kernel_device_get_subsystem (_b)) && !g_strcmp0 (mm_kernel_device_get_name (_a), mm_kernel_device_get_name (_b))); } static gboolean kernel_device_has_property (MMKernelDevice *_self, const gchar *property) { MMKernelDeviceUdev *self; self = MM_KERNEL_DEVICE_UDEV (_self); return (self->priv->device ? g_udev_device_has_property (self->priv->device, property) : FALSE); } static const gchar * kernel_device_get_property (MMKernelDevice *_self, const gchar *property) { MMKernelDeviceUdev *self; self = MM_KERNEL_DEVICE_UDEV (_self); return (self->priv->device ? g_udev_device_get_property (self->priv->device, property) : NULL); } static gboolean kernel_device_has_global_property (MMKernelDevice *_self, const gchar *property) { MMKernelDeviceUdev *self; self = MM_KERNEL_DEVICE_UDEV (_self); if (self->priv->physdev && g_udev_device_has_property (self->priv->physdev, property)) return TRUE; return kernel_device_has_property (_self, property); } static const gchar * kernel_device_get_global_property (MMKernelDevice *_self, const gchar *property) { MMKernelDeviceUdev *self; const gchar *str; self = MM_KERNEL_DEVICE_UDEV (_self); if (self->priv->physdev && g_udev_device_has_property (self->priv->physdev, property) && (str = g_udev_device_get_property (self->priv->physdev, property)) != NULL) return str; return kernel_device_get_property (_self, property); } /*****************************************************************************/ static gboolean kernel_device_has_attribute (MMKernelDevice *_self, const gchar *attribute) { MMKernelDeviceUdev *self; self = MM_KERNEL_DEVICE_UDEV (_self); if (!self->priv->device) return FALSE; return g_udev_device_has_sysfs_attr (self->priv->device, attribute); } static const gchar * kernel_device_get_attribute (MMKernelDevice *_self, const gchar *attribute) { MMKernelDeviceUdev *self; self = MM_KERNEL_DEVICE_UDEV (_self); if (!self->priv->device) return NULL; return g_udev_device_get_sysfs_attr (self->priv->device, attribute); } /*****************************************************************************/ MMKernelDevice * mm_kernel_device_udev_new (GUdevClient *udev_client, GUdevDevice *udev_device) { GError *error = NULL; MMKernelDevice *self; self = MM_KERNEL_DEVICE (g_initable_new (MM_TYPE_KERNEL_DEVICE_UDEV, NULL, &error, "udev-client", udev_client, "udev-device", udev_device, NULL)); g_assert_no_error (error); return self; } /*****************************************************************************/ MMKernelDevice * mm_kernel_device_udev_new_from_properties (GUdevClient *udev_client, MMKernelEventProperties *props, GError **error) { return MM_KERNEL_DEVICE (g_initable_new (MM_TYPE_KERNEL_DEVICE_UDEV, NULL, error, "udev-client", udev_client, "properties", props, NULL)); } /*****************************************************************************/ static void mm_kernel_device_udev_init (MMKernelDeviceUdev *self) { /* Initialize private data */ self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, MM_TYPE_KERNEL_DEVICE_UDEV, MMKernelDeviceUdevPrivate); } static void set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { MMKernelDeviceUdev *self = MM_KERNEL_DEVICE_UDEV (object); switch (prop_id) { case PROP_UDEV_CLIENT: g_assert (!self->priv->client); self->priv->client = g_value_dup_object (value); break; case PROP_UDEV_DEVICE: g_assert (!self->priv->device); self->priv->device = g_value_dup_object (value); break; case PROP_PROPERTIES: g_assert (!self->priv->properties); self->priv->properties = g_value_dup_object (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) { MMKernelDeviceUdev *self = MM_KERNEL_DEVICE_UDEV (object); switch (prop_id) { case PROP_UDEV_CLIENT: g_value_set_object (value, self->priv->client); break; case PROP_UDEV_DEVICE: g_value_set_object (value, self->priv->device); break; case PROP_PROPERTIES: g_value_set_object (value, self->priv->properties); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static gboolean initable_init (GInitable *initable, GCancellable *cancellable, GError **error) { MMKernelDeviceUdev *self = MM_KERNEL_DEVICE_UDEV (initable); const gchar *subsystem; const gchar *name; if (!self->priv->client) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS, "missing client in kernel device"); return FALSE; } /* When created from a GUdevDevice, we're done */ if (self->priv->device) { preload_contents (self); return TRUE; } /* Otherwise, we do need properties with subsystem and name */ if (!self->priv->properties) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS, "missing properties in kernel device"); return FALSE; } subsystem = mm_kernel_event_properties_get_subsystem (self->priv->properties); if (!subsystem) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS, "subsystem is mandatory in kernel device"); return FALSE; } name = mm_kernel_event_properties_get_name (self->priv->properties); if (!mm_kernel_device_get_name (MM_KERNEL_DEVICE (self))) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS, "name is mandatory in kernel device"); return FALSE; } /* On remove events, we don't look for the GUdevDevice */ if (g_strcmp0 (mm_kernel_event_properties_get_action (self->priv->properties), "remove")) { g_assert (!self->priv->device); self->priv->device = g_udev_client_query_by_subsystem_and_name (self->priv->client, subsystem, name); if (!self->priv->device) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS, "device %s/%s not found", subsystem, name); return FALSE; } } if (self->priv->device) preload_contents (self); return TRUE; } static void dispose (GObject *object) { MMKernelDeviceUdev *self = MM_KERNEL_DEVICE_UDEV (object); g_clear_pointer (&self->priv->driver, g_free); g_clear_object (&self->priv->physdev); g_clear_object (&self->priv->interface); g_clear_object (&self->priv->device); g_clear_object (&self->priv->client); g_clear_object (&self->priv->properties); G_OBJECT_CLASS (mm_kernel_device_udev_parent_class)->dispose (object); } static void initable_iface_init (GInitableIface *iface) { iface->init = initable_init; } static void mm_kernel_device_udev_class_init (MMKernelDeviceUdevClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); MMKernelDeviceClass *kernel_device_class = MM_KERNEL_DEVICE_CLASS (klass); g_type_class_add_private (object_class, sizeof (MMKernelDeviceUdevPrivate)); object_class->dispose = dispose; object_class->get_property = get_property; object_class->set_property = set_property; kernel_device_class->get_subsystem = kernel_device_get_subsystem; kernel_device_class->get_name = kernel_device_get_name; kernel_device_class->get_driver = kernel_device_get_driver; kernel_device_class->get_sysfs_path = kernel_device_get_sysfs_path; kernel_device_class->get_wwandev_sysfs_path = kernel_device_get_wwandev_sysfs_path; kernel_device_class->get_physdev_uid = kernel_device_get_physdev_uid; kernel_device_class->get_physdev_vid = kernel_device_get_physdev_vid; kernel_device_class->get_physdev_pid = kernel_device_get_physdev_pid; kernel_device_class->get_physdev_subsystem_vid = kernel_device_get_physdev_subsystem_vid; kernel_device_class->get_physdev_revision = kernel_device_get_physdev_revision; kernel_device_class->get_physdev_sysfs_path = kernel_device_get_physdev_sysfs_path; kernel_device_class->get_physdev_subsystem = kernel_device_get_physdev_subsystem; kernel_device_class->get_physdev_manufacturer = kernel_device_get_physdev_manufacturer; kernel_device_class->get_physdev_product = kernel_device_get_physdev_product; kernel_device_class->get_interface_number = kernel_device_get_interface_number; kernel_device_class->get_interface_class = kernel_device_get_interface_class; kernel_device_class->get_interface_subclass = kernel_device_get_interface_subclass; kernel_device_class->get_interface_protocol = kernel_device_get_interface_protocol; kernel_device_class->get_interface_sysfs_path = kernel_device_get_interface_sysfs_path; kernel_device_class->get_interface_description = kernel_device_get_interface_description; kernel_device_class->cmp = kernel_device_cmp; kernel_device_class->has_property = kernel_device_has_property; kernel_device_class->get_property = kernel_device_get_property; kernel_device_class->has_global_property = kernel_device_has_global_property; kernel_device_class->get_global_property = kernel_device_get_global_property; kernel_device_class->has_attribute = kernel_device_has_attribute; kernel_device_class->get_attribute = kernel_device_get_attribute; properties[PROP_UDEV_DEVICE] = g_param_spec_object ("udev-device", "udev device", "Device object as reported by GUdev", G_UDEV_TYPE_DEVICE, G_PARAM_READWRITE); g_object_class_install_property (object_class, PROP_UDEV_DEVICE, properties[PROP_UDEV_DEVICE]); properties[PROP_UDEV_CLIENT] = g_param_spec_object ("udev-client", "udev client", "GUdev client", G_UDEV_TYPE_CLIENT, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY); g_object_class_install_property (object_class, PROP_UDEV_CLIENT, properties[PROP_UDEV_CLIENT]); properties[PROP_PROPERTIES] = g_param_spec_object ("properties", "Properties", "Generic kernel event properties", MM_TYPE_KERNEL_EVENT_PROPERTIES, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY); g_object_class_install_property (object_class, PROP_PROPERTIES, properties[PROP_PROPERTIES]); } ModemManager-1.23.4-dev/src/kerneldevice/mm-kernel-device-udev.h000066400000000000000000000047711456466623000244230ustar00rootroot00000000000000/* -*- 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) 2016 Velocloud, Inc. */ #ifndef MM_KERNEL_DEVICE_UDEV_H #define MM_KERNEL_DEVICE_UDEV_H #include #include #include #define _LIBMM_INSIDE_MM #include #include "mm-kernel-device.h" #define MM_TYPE_KERNEL_DEVICE_UDEV (mm_kernel_device_udev_get_type ()) #define MM_KERNEL_DEVICE_UDEV(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_KERNEL_DEVICE_UDEV, MMKernelDeviceUdev)) #define MM_KERNEL_DEVICE_UDEV_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_KERNEL_DEVICE_UDEV, MMKernelDeviceUdevClass)) #define MM_IS_KERNEL_DEVICE_UDEV(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_KERNEL_DEVICE_UDEV)) #define MM_IS_KERNEL_DEVICE_UDEV_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_KERNEL_DEVICE_UDEV)) #define MM_KERNEL_DEVICE_UDEV_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_KERNEL_DEVICE_UDEV, MMKernelDeviceUdevClass)) typedef struct _MMKernelDeviceUdev MMKernelDeviceUdev; typedef struct _MMKernelDeviceUdevClass MMKernelDeviceUdevClass; typedef struct _MMKernelDeviceUdevPrivate MMKernelDeviceUdevPrivate; struct _MMKernelDeviceUdev { MMKernelDevice parent; MMKernelDeviceUdevPrivate *priv; }; struct _MMKernelDeviceUdevClass { MMKernelDeviceClass parent; }; GType mm_kernel_device_udev_get_type (void); G_DEFINE_AUTOPTR_CLEANUP_FUNC (MMKernelDeviceUdev, g_object_unref) MMKernelDevice *mm_kernel_device_udev_new (GUdevClient *udev_client, GUdevDevice *udev_device); MMKernelDevice *mm_kernel_device_udev_new_from_properties (GUdevClient *udev_client, MMKernelEventProperties *properties, GError **error); #endif /* MM_KERNEL_DEVICE_UDEV_H */ ModemManager-1.23.4-dev/src/kerneldevice/mm-kernel-device.c000066400000000000000000000362051456466623000234520ustar00rootroot00000000000000/* -*- 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) 2016 Velocloud, Inc. */ #include #include #define _LIBMM_INSIDE_MM #include #include "mm-kernel-device.h" #include "mm-log-object.h" static void log_object_iface_init (MMLogObjectInterface *iface); G_DEFINE_ABSTRACT_TYPE_WITH_CODE (MMKernelDevice, mm_kernel_device, G_TYPE_OBJECT, G_IMPLEMENT_INTERFACE (MM_TYPE_LOG_OBJECT, log_object_iface_init)) enum { PROP_0, PROP_LOWER_DEVICE, PROP_LAST }; static GParamSpec *properties[PROP_LAST]; struct _MMKernelDevicePrivate { MMKernelDevice *lower_device; }; /*****************************************************************************/ const gchar * mm_kernel_device_get_subsystem (MMKernelDevice *self) { return (MM_KERNEL_DEVICE_GET_CLASS (self)->get_subsystem ? MM_KERNEL_DEVICE_GET_CLASS (self)->get_subsystem (self) : NULL); } const gchar * mm_kernel_device_get_name (MMKernelDevice *self) { return (MM_KERNEL_DEVICE_GET_CLASS (self)->get_name ? MM_KERNEL_DEVICE_GET_CLASS (self)->get_name (self) : NULL); } const gchar * mm_kernel_device_get_driver (MMKernelDevice *self) { return (MM_KERNEL_DEVICE_GET_CLASS (self)->get_driver ? MM_KERNEL_DEVICE_GET_CLASS (self)->get_driver (self) : NULL); } const gchar * mm_kernel_device_get_sysfs_path (MMKernelDevice *self) { return (MM_KERNEL_DEVICE_GET_CLASS (self)->get_sysfs_path ? MM_KERNEL_DEVICE_GET_CLASS (self)->get_sysfs_path (self) : NULL); } const gchar * mm_kernel_device_get_wwandev_sysfs_path (MMKernelDevice *self) { return (MM_KERNEL_DEVICE_GET_CLASS (self)->get_wwandev_sysfs_path ? MM_KERNEL_DEVICE_GET_CLASS (self)->get_wwandev_sysfs_path (self) : NULL); } const gchar * mm_kernel_device_get_physdev_uid (MMKernelDevice *self) { /* when a lower device is available, physdev info taken from it */ if (self->priv->lower_device) return mm_kernel_device_get_physdev_uid (self->priv->lower_device); return (MM_KERNEL_DEVICE_GET_CLASS (self)->get_physdev_uid ? MM_KERNEL_DEVICE_GET_CLASS (self)->get_physdev_uid (self) : NULL); } guint16 mm_kernel_device_get_physdev_vid (MMKernelDevice *self) { /* when a lower device is available, physdev info taken from it */ if (self->priv->lower_device) return mm_kernel_device_get_physdev_vid (self->priv->lower_device); return (MM_KERNEL_DEVICE_GET_CLASS (self)->get_physdev_vid ? MM_KERNEL_DEVICE_GET_CLASS (self)->get_physdev_vid (self) : 0); } guint16 mm_kernel_device_get_physdev_subsystem_vid (MMKernelDevice *self) { /* when a lower device is available, physdev info taken from it */ if (self->priv->lower_device) return mm_kernel_device_get_physdev_subsystem_vid (self->priv->lower_device); return (MM_KERNEL_DEVICE_GET_CLASS (self)->get_physdev_subsystem_vid ? MM_KERNEL_DEVICE_GET_CLASS (self)->get_physdev_subsystem_vid (self) : 0); } guint16 mm_kernel_device_get_physdev_pid (MMKernelDevice *self) { /* when a lower device is available, physdev info taken from it */ if (self->priv->lower_device) return mm_kernel_device_get_physdev_pid (self->priv->lower_device); return (MM_KERNEL_DEVICE_GET_CLASS (self)->get_physdev_pid ? MM_KERNEL_DEVICE_GET_CLASS (self)->get_physdev_pid (self) : 0); } guint16 mm_kernel_device_get_physdev_revision (MMKernelDevice *self) { /* when a lower device is available, physdev info taken from it */ if (self->priv->lower_device) return mm_kernel_device_get_physdev_revision (self->priv->lower_device); return (MM_KERNEL_DEVICE_GET_CLASS (self)->get_physdev_revision ? MM_KERNEL_DEVICE_GET_CLASS (self)->get_physdev_revision (self) : 0); } const gchar * mm_kernel_device_get_physdev_subsystem (MMKernelDevice *self) { /* when a lower device is available, physdev info taken from it */ if (self->priv->lower_device) return mm_kernel_device_get_physdev_subsystem (self->priv->lower_device); return (MM_KERNEL_DEVICE_GET_CLASS (self)->get_physdev_subsystem ? MM_KERNEL_DEVICE_GET_CLASS (self)->get_physdev_subsystem (self) : NULL); } const gchar * mm_kernel_device_get_physdev_sysfs_path (MMKernelDevice *self) { /* when a lower device is available, physdev info taken from it */ if (self->priv->lower_device) return mm_kernel_device_get_physdev_sysfs_path (self->priv->lower_device); return (MM_KERNEL_DEVICE_GET_CLASS (self)->get_physdev_sysfs_path ? MM_KERNEL_DEVICE_GET_CLASS (self)->get_physdev_sysfs_path (self) : NULL); } const gchar * mm_kernel_device_get_physdev_manufacturer (MMKernelDevice *self) { /* when a lower device is available, physdev info taken from it */ if (self->priv->lower_device) return mm_kernel_device_get_physdev_manufacturer (self->priv->lower_device); return (MM_KERNEL_DEVICE_GET_CLASS (self)->get_physdev_manufacturer ? MM_KERNEL_DEVICE_GET_CLASS (self)->get_physdev_manufacturer (self) : NULL); } const gchar * mm_kernel_device_get_physdev_product (MMKernelDevice *self) { /* when a lower device is available, physdev info taken from it */ if (self->priv->lower_device) return mm_kernel_device_get_physdev_product (self->priv->lower_device); return (MM_KERNEL_DEVICE_GET_CLASS (self)->get_physdev_product ? MM_KERNEL_DEVICE_GET_CLASS (self)->get_physdev_product (self) : NULL); } MMKernelDevice * mm_kernel_device_peek_lower_device (MMKernelDevice *self) { return self->priv->lower_device; } gint mm_kernel_device_get_interface_number (MMKernelDevice *self) { return (MM_KERNEL_DEVICE_GET_CLASS (self)->get_interface_number ? MM_KERNEL_DEVICE_GET_CLASS (self)->get_interface_number (self) : -1); } gint mm_kernel_device_get_interface_class (MMKernelDevice *self) { return (MM_KERNEL_DEVICE_GET_CLASS (self)->get_interface_class ? MM_KERNEL_DEVICE_GET_CLASS (self)->get_interface_class (self) : -1); } gint mm_kernel_device_get_interface_subclass (MMKernelDevice *self) { return (MM_KERNEL_DEVICE_GET_CLASS (self)->get_interface_subclass ? MM_KERNEL_DEVICE_GET_CLASS (self)->get_interface_subclass (self) : -1); } gint mm_kernel_device_get_interface_protocol (MMKernelDevice *self) { return (MM_KERNEL_DEVICE_GET_CLASS (self)->get_interface_protocol ? MM_KERNEL_DEVICE_GET_CLASS (self)->get_interface_protocol (self) : -1); } const gchar * mm_kernel_device_get_interface_sysfs_path (MMKernelDevice *self) { return (MM_KERNEL_DEVICE_GET_CLASS (self)->get_interface_sysfs_path ? MM_KERNEL_DEVICE_GET_CLASS (self)->get_interface_sysfs_path (self) : NULL); } const gchar * mm_kernel_device_get_interface_description (MMKernelDevice *self) { return (MM_KERNEL_DEVICE_GET_CLASS (self)->get_interface_description ? MM_KERNEL_DEVICE_GET_CLASS (self)->get_interface_description (self) : NULL); } gboolean mm_kernel_device_cmp (MMKernelDevice *a, MMKernelDevice *b) { if (G_OBJECT_TYPE (a) != G_OBJECT_TYPE (b)) return FALSE; return (MM_KERNEL_DEVICE_GET_CLASS (a)->cmp ? MM_KERNEL_DEVICE_GET_CLASS (a)->cmp (a, b) : FALSE); } gboolean mm_kernel_device_has_property (MMKernelDevice *self, const gchar *property) { return (MM_KERNEL_DEVICE_GET_CLASS (self)->has_property ? MM_KERNEL_DEVICE_GET_CLASS (self)->has_property (self, property) : FALSE); } const gchar * mm_kernel_device_get_property (MMKernelDevice *self, const gchar *property) { return (MM_KERNEL_DEVICE_GET_CLASS (self)->get_property ? MM_KERNEL_DEVICE_GET_CLASS (self)->get_property (self, property) : NULL); } gboolean mm_kernel_device_get_property_as_boolean (MMKernelDevice *self, const gchar *property) { const gchar *value; value = mm_kernel_device_get_property (self, property); return (value && mm_common_get_boolean_from_string (value, NULL)); } gint mm_kernel_device_get_property_as_int (MMKernelDevice *self, const gchar *property) { const gchar *value; gint aux; value = mm_kernel_device_get_property (self, property); return ((value && mm_get_int_from_str (value, &aux)) ? aux : 0); } guint mm_kernel_device_get_property_as_int_hex (MMKernelDevice *self, const gchar *property) { const gchar *value; guint aux; value = mm_kernel_device_get_property (self, property); return ((value && mm_get_uint_from_hex_str (value, &aux)) ? aux : 0); } gboolean mm_kernel_device_has_global_property (MMKernelDevice *self, const gchar *property) { return (MM_KERNEL_DEVICE_GET_CLASS (self)->has_global_property ? MM_KERNEL_DEVICE_GET_CLASS (self)->has_global_property (self, property) : FALSE); } const gchar * mm_kernel_device_get_global_property (MMKernelDevice *self, const gchar *property) { return (MM_KERNEL_DEVICE_GET_CLASS (self)->get_global_property ? MM_KERNEL_DEVICE_GET_CLASS (self)->get_global_property (self, property) : NULL); } gboolean mm_kernel_device_get_global_property_as_boolean (MMKernelDevice *self, const gchar *property) { const gchar *value; value = mm_kernel_device_get_global_property (self, property); return (value && mm_common_get_boolean_from_string (value, NULL)); } gint mm_kernel_device_get_global_property_as_int (MMKernelDevice *self, const gchar *property) { const gchar *value; gint aux; value = mm_kernel_device_get_global_property (self, property); return ((value && mm_get_int_from_str (value, &aux)) ? aux : 0); } guint mm_kernel_device_get_global_property_as_int_hex (MMKernelDevice *self, const gchar *property) { const gchar *value; guint aux; value = mm_kernel_device_get_global_property (self, property); return ((value && mm_get_uint_from_hex_str (value, &aux)) ? aux : 0); } gboolean mm_kernel_device_has_attribute (MMKernelDevice *self, const gchar *attribute) { g_return_val_if_fail (MM_IS_KERNEL_DEVICE (self), FALSE); return (MM_KERNEL_DEVICE_GET_CLASS (self)->has_attribute ? MM_KERNEL_DEVICE_GET_CLASS (self)->has_attribute (self, attribute) : FALSE); } const gchar * mm_kernel_device_get_attribute (MMKernelDevice *self, const gchar *attribute) { g_return_val_if_fail (MM_IS_KERNEL_DEVICE (self), NULL); return (MM_KERNEL_DEVICE_GET_CLASS (self)->get_attribute ? MM_KERNEL_DEVICE_GET_CLASS (self)->get_attribute (self, attribute) : NULL); } gboolean mm_kernel_device_get_attribute_as_boolean (MMKernelDevice *self, const gchar *attribute) { const gchar *value; value = mm_kernel_device_get_attribute (self, attribute); return (value && mm_common_get_boolean_from_string (value, NULL)); } gint mm_kernel_device_get_attribute_as_int (MMKernelDevice *self, const gchar *attribute) { const gchar *value; gint aux; value = mm_kernel_device_get_attribute (self, attribute); return ((value && mm_get_int_from_str (value, &aux)) ? aux : 0); } guint mm_kernel_device_get_attribute_as_int_hex (MMKernelDevice *self, const gchar *attribute) { const gchar *value; guint aux; value = mm_kernel_device_get_attribute (self, attribute); return ((value && mm_get_uint_from_hex_str (value, &aux)) ? aux : 0); } /*****************************************************************************/ static gchar * log_object_build_id (MMLogObject *_self) { MMKernelDevice *self; self = MM_KERNEL_DEVICE (_self); return g_strdup (mm_kernel_device_get_name (self)); } /*****************************************************************************/ static void mm_kernel_device_init (MMKernelDevice *self) { /* Initialize private data */ self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, MM_TYPE_KERNEL_DEVICE, MMKernelDevicePrivate); } static void set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { MMKernelDevice *self = MM_KERNEL_DEVICE (object); switch (prop_id) { case PROP_LOWER_DEVICE: g_clear_object (&self->priv->lower_device); self->priv->lower_device = g_value_dup_object (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) { MMKernelDevice *self = MM_KERNEL_DEVICE (object); switch (prop_id) { case PROP_LOWER_DEVICE: g_value_set_object (value, self->priv->lower_device); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void log_object_iface_init (MMLogObjectInterface *iface) { iface->build_id = log_object_build_id; } static void dispose (GObject *object) { MMKernelDevice *self = MM_KERNEL_DEVICE (object); g_clear_object (&self->priv->lower_device); G_OBJECT_CLASS (mm_kernel_device_parent_class)->dispose (object); } static void mm_kernel_device_class_init (MMKernelDeviceClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); g_type_class_add_private (object_class, sizeof (MMKernelDevicePrivate)); object_class->dispose = dispose; object_class->get_property = get_property; object_class->set_property = set_property; properties[PROP_LOWER_DEVICE] = g_param_spec_object ("lower-device", "lower device", "Lower real device, when this is a virtual one", MM_TYPE_KERNEL_DEVICE, G_PARAM_READWRITE); g_object_class_install_property (object_class, PROP_LOWER_DEVICE, properties[PROP_LOWER_DEVICE]); } ModemManager-1.23.4-dev/src/kerneldevice/mm-kernel-device.h000066400000000000000000000161111456466623000234510ustar00rootroot00000000000000/* -*- 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) 2016 Velocloud, Inc. */ #ifndef MM_KERNEL_DEVICE_H #define MM_KERNEL_DEVICE_H #include #include #define MM_TYPE_KERNEL_DEVICE (mm_kernel_device_get_type ()) #define MM_KERNEL_DEVICE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_KERNEL_DEVICE, MMKernelDevice)) #define MM_KERNEL_DEVICE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_KERNEL_DEVICE, MMKernelDeviceClass)) #define MM_IS_KERNEL_DEVICE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_KERNEL_DEVICE)) #define MM_IS_KERNEL_DEVICE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_KERNEL_DEVICE)) #define MM_KERNEL_DEVICE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_KERNEL_DEVICE, MMKernelDeviceClass)) typedef struct _MMKernelDevice MMKernelDevice; typedef struct _MMKernelDeviceClass MMKernelDeviceClass; typedef struct _MMKernelDevicePrivate MMKernelDevicePrivate; struct _MMKernelDevice { GObject parent; MMKernelDevicePrivate *priv; }; struct _MMKernelDeviceClass { GObjectClass parent; const gchar * (* get_subsystem) (MMKernelDevice *self); const gchar * (* get_name) (MMKernelDevice *self); const gchar * (* get_driver) (MMKernelDevice *self); const gchar * (* get_sysfs_path) (MMKernelDevice *self); const gchar * (* get_wwandev_sysfs_path) (MMKernelDevice *self); gint (* get_interface_number) (MMKernelDevice *self); gint (* get_interface_class) (MMKernelDevice *self); gint (* get_interface_subclass) (MMKernelDevice *self); gint (* get_interface_protocol) (MMKernelDevice *self); const gchar * (* get_interface_sysfs_path) (MMKernelDevice *self); const gchar * (* get_interface_description) (MMKernelDevice *self); const gchar * (* get_physdev_uid) (MMKernelDevice *self); guint16 (* get_physdev_vid) (MMKernelDevice *self); guint16 (* get_physdev_pid) (MMKernelDevice *self); guint16 (* get_physdev_subsystem_vid) (MMKernelDevice *self); guint16 (* get_physdev_revision) (MMKernelDevice *self); const gchar * (* get_physdev_sysfs_path) (MMKernelDevice *self); const gchar * (* get_physdev_subsystem) (MMKernelDevice *self); const gchar * (* get_physdev_manufacturer) (MMKernelDevice *self); const gchar * (* get_physdev_product) (MMKernelDevice *self); gboolean (* cmp) (MMKernelDevice *a, MMKernelDevice *b); gboolean (* has_property) (MMKernelDevice *self, const gchar *property); const gchar * (* get_property) (MMKernelDevice *self, const gchar *property); gboolean (* has_global_property) (MMKernelDevice *self, const gchar *property); const gchar * (* get_global_property) (MMKernelDevice *self, const gchar *property); gboolean (* has_attribute) (MMKernelDevice *self, const gchar *attribute); const gchar * (* get_attribute) (MMKernelDevice *self, const gchar *attribute); }; GType mm_kernel_device_get_type (void); G_DEFINE_AUTOPTR_CLEANUP_FUNC (MMKernelDevice, g_object_unref) const gchar *mm_kernel_device_get_subsystem (MMKernelDevice *self); const gchar *mm_kernel_device_get_name (MMKernelDevice *self); const gchar *mm_kernel_device_get_driver (MMKernelDevice *self); const gchar *mm_kernel_device_get_sysfs_path (MMKernelDevice *self); const gchar *mm_kernel_device_get_wwandev_sysfs_path (MMKernelDevice *self); gint mm_kernel_device_get_interface_number (MMKernelDevice *self); gint mm_kernel_device_get_interface_class (MMKernelDevice *self); gint mm_kernel_device_get_interface_subclass (MMKernelDevice *self); gint mm_kernel_device_get_interface_protocol (MMKernelDevice *self); const gchar *mm_kernel_device_get_interface_sysfs_path (MMKernelDevice *self); const gchar *mm_kernel_device_get_interface_description (MMKernelDevice *self); const gchar *mm_kernel_device_get_physdev_uid (MMKernelDevice *self); guint16 mm_kernel_device_get_physdev_vid (MMKernelDevice *self); guint16 mm_kernel_device_get_physdev_pid (MMKernelDevice *self); guint16 mm_kernel_device_get_physdev_subsystem_vid (MMKernelDevice *self); guint16 mm_kernel_device_get_physdev_revision (MMKernelDevice *self); const gchar *mm_kernel_device_get_physdev_sysfs_path (MMKernelDevice *self); const gchar *mm_kernel_device_get_physdev_subsystem (MMKernelDevice *self); const gchar *mm_kernel_device_get_physdev_manufacturer (MMKernelDevice *self); const gchar *mm_kernel_device_get_physdev_product (MMKernelDevice *self); MMKernelDevice *mm_kernel_device_peek_lower_device (MMKernelDevice *self); gboolean mm_kernel_device_cmp (MMKernelDevice *a, MMKernelDevice *b); /* Standard properties are usually associated to single ports */ gboolean mm_kernel_device_has_property (MMKernelDevice *self, const gchar *property); const gchar *mm_kernel_device_get_property (MMKernelDevice *self, const gchar *property); gboolean mm_kernel_device_get_property_as_boolean (MMKernelDevice *self, const gchar *property); gint mm_kernel_device_get_property_as_int (MMKernelDevice *self, const gchar *property); guint mm_kernel_device_get_property_as_int_hex (MMKernelDevice *self, const gchar *property); /* Global properties are usually associated to full devices */ gboolean mm_kernel_device_has_global_property (MMKernelDevice *self, const gchar *property); const gchar *mm_kernel_device_get_global_property (MMKernelDevice *self, const gchar *property); gboolean mm_kernel_device_get_global_property_as_boolean (MMKernelDevice *self, const gchar *property); gint mm_kernel_device_get_global_property_as_int (MMKernelDevice *self, const gchar *property); guint mm_kernel_device_get_global_property_as_int_hex (MMKernelDevice *self, const gchar *property); /* Attributes in sysfs */ gboolean mm_kernel_device_has_attribute (MMKernelDevice *self, const gchar *attribute); const gchar *mm_kernel_device_get_attribute (MMKernelDevice *self, const gchar *attribute); gboolean mm_kernel_device_get_attribute_as_boolean (MMKernelDevice *self, const gchar *attribute); gint mm_kernel_device_get_attribute_as_int (MMKernelDevice *self, const gchar *attribute); guint mm_kernel_device_get_attribute_as_int_hex (MMKernelDevice *self, const gchar *attribute); #endif /* MM_KERNEL_DEVICE_H */ ModemManager-1.23.4-dev/src/main.c000066400000000000000000000206451456466623000166130ustar00rootroot00000000000000/* -*- 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 - 2011 Red Hat, Inc. */ #include #include #include #include #include #include #include #include #include "ModemManager.h" #define MM_LOG_NO_OBJECT #include "mm-log.h" #include "mm-base-manager.h" #include "mm-context.h" #if defined WITH_SUSPEND_RESUME # include "mm-sleep-monitor.h" #endif /* Maximum time to wait for all modems to get disabled and removed */ #define MAX_SHUTDOWN_TIME_SECS 20 static GMainLoop *loop; static MMBaseManager *manager; static gboolean quit_cb (gpointer user_data) { mm_msg ("caught signal, shutting down..."); if (manager) g_object_set (manager, MM_BASE_MANAGER_CONNECTION, NULL, NULL); if (loop) g_idle_add ((GSourceFunc) g_main_loop_quit, loop); else exit (0); return FALSE; } #if defined WITH_SUSPEND_RESUME static void sleeping_cb (MMSleepMonitor *sleep_monitor) { if (mm_context_get_test_low_power_suspend_resume ()) { mm_dbg ("removing devices and setting them in low power mode... (sleeping)"); mm_base_manager_shutdown (manager, TRUE, TRUE, TRUE); } else { mm_dbg ("removing devices... (sleeping)"); mm_base_manager_shutdown (manager, FALSE, FALSE, TRUE); } } static void resuming_cb (MMSleepMonitor *sleep_monitor) { mm_dbg ("re-scanning (resuming)"); mm_base_manager_start (manager, FALSE); } static void sleeping_quick_cb (MMSleepMonitor *sleep_monitor) { if (mm_context_get_test_low_power_suspend_resume ()) { mm_dbg ("setting modem in low power mode... (sleeping)"); mm_base_manager_shutdown (manager, TRUE, TRUE, FALSE); } } static void resuming_quick_cb (MMSleepMonitor *sleep_monitor) { mm_dbg ("syncing modem state (quick resuming)"); mm_base_manager_sync (manager); } #endif static void bus_acquired_cb (GDBusConnection *connection, const gchar *name, gpointer user_data) { GError *error = NULL; mm_dbg ("bus acquired, creating manager..."); /* Create Manager object */ g_assert (!manager); manager = mm_base_manager_new (connection, #if !defined WITH_BUILTIN_PLUGINS mm_context_get_test_plugin_dir (), #endif !mm_context_get_no_auto_scan (), mm_context_get_filter_policy (), mm_context_get_initial_kernel_events (), #if defined WITH_TESTS mm_context_get_test_enable (), #endif &error); if (!manager) { mm_warn ("could not create manager: %s", error->message); g_error_free (error); g_main_loop_quit (loop); return; } } static void name_acquired_cb (GDBusConnection *connection, const gchar *name, gpointer user_data) { mm_dbg ("service name '%s' was acquired", name); /* Launch automatic scan for devices */ g_assert (manager); mm_base_manager_start (manager, FALSE); } static void name_lost_cb (GDBusConnection *connection, const gchar *name, gpointer user_data) { /* Note that we're not allowing replacement, so once the name acquired, the * process won't lose it. */ if (!name) mm_warn ("could not get the system bus; make sure the message bus daemon is running!"); else mm_warn ("could not acquire the '%s' service name", name); if (manager) g_object_set (manager, MM_BASE_MANAGER_CONNECTION, NULL, NULL); g_main_loop_quit (loop); } static void register_dbus_errors (void) { /* This method will always return success once during runtime */ if (!mm_common_register_errors ()) return; /* We no longer use MM_CORE_ERROR_CANCELLED in the daemon, we rely on * G_IO_ERROR_CANCELLED internally */ g_dbus_error_unregister_error (MM_CORE_ERROR, MM_CORE_ERROR_CANCELLED, MM_CORE_ERROR_DBUS_PREFIX ".Cancelled"); g_dbus_error_register_error (G_IO_ERROR, G_IO_ERROR_CANCELLED, MM_CORE_ERROR_DBUS_PREFIX ".Cancelled"); } int main (int argc, char *argv[]) { GMainLoop *inner; GError *error = NULL; guint name_id; /* Setup application context */ mm_context_init (argc, argv); if (!mm_log_setup (mm_context_get_log_level (), mm_context_get_log_file (), mm_context_get_log_journal (), mm_context_get_log_timestamps (), mm_context_get_log_relative_timestamps (), mm_context_get_log_personal_info (), &error)) { g_printerr ("error: failed to set up logging: %s\n", error->message); g_error_free (error); exit (1); } g_unix_signal_add (SIGTERM, quit_cb, NULL); g_unix_signal_add (SIGINT, quit_cb, NULL); /* Early register all known errors */ register_dbus_errors (); mm_msg ("ModemManager (version " MM_DIST_VERSION ") starting in %s bus...", mm_context_get_test_session () ? "session" : "system"); /* Detect runtime charset conversion support */ mm_modem_charsets_init (); /* Acquire name, don't allow replacement */ name_id = g_bus_own_name (mm_context_get_test_session () ? G_BUS_TYPE_SESSION : G_BUS_TYPE_SYSTEM, MM_DBUS_SERVICE, G_BUS_NAME_OWNER_FLAGS_NONE, bus_acquired_cb, name_acquired_cb, name_lost_cb, NULL, NULL); #if defined WITH_SUSPEND_RESUME { MMSleepMonitor *sleep_monitor; if (mm_context_get_test_no_suspend_resume ()) mm_dbg ("Suspend/resume support disabled at runtime"); else if (mm_context_get_test_quick_suspend_resume ()) { mm_dbg ("Quick suspend/resume hooks enabled"); sleep_monitor = mm_sleep_monitor_get (); g_signal_connect (sleep_monitor, MM_SLEEP_MONITOR_SLEEPING, G_CALLBACK (sleeping_quick_cb), NULL); g_signal_connect (sleep_monitor, MM_SLEEP_MONITOR_RESUMING, G_CALLBACK (resuming_quick_cb), NULL); } else { mm_dbg ("Full suspend/resume hooks enabled"); sleep_monitor = mm_sleep_monitor_get (); g_signal_connect (sleep_monitor, MM_SLEEP_MONITOR_SLEEPING, G_CALLBACK (sleeping_cb), NULL); g_signal_connect (sleep_monitor, MM_SLEEP_MONITOR_RESUMING, G_CALLBACK (resuming_cb), NULL); } } #endif /* Go into the main loop */ loop = g_main_loop_new (NULL, FALSE); g_main_loop_run (loop); /* Clear the global variable, so that subsequent requests to * exit succeed. */ inner = loop; loop = NULL; if (manager) { GTimer *timer; mm_base_manager_shutdown (manager, TRUE, FALSE, TRUE); /* Wait for all modems to be disabled and removed, but don't wait * forever: if disabling the modems takes longer than 20s, just * shutdown anyway. */ timer = g_timer_new (); while (mm_base_manager_num_modems (manager) && g_timer_elapsed (timer, NULL) < (gdouble)MAX_SHUTDOWN_TIME_SECS) { GMainContext *ctx = g_main_loop_get_context (inner); g_main_context_iteration (ctx, FALSE); g_usleep (50); } if (mm_base_manager_num_modems (manager)) mm_warn ("disabling modems took too long, shutting down with %u modems around", mm_base_manager_num_modems (manager)); g_object_unref (manager); g_timer_destroy (timer); } g_main_loop_unref (inner); g_bus_unown_name (name_id); mm_msg ("ModemManager is shut down"); mm_log_shutdown (); return 0; } ModemManager-1.23.4-dev/src/meson.build000066400000000000000000000206041456466623000176600ustar00rootroot00000000000000# SPDX-License-Identifier: GPL-2.0-or-later # Copyright (C) 2021 Iñigo Martinez # helpers library src_inc = include_directories('.') kerneldevice_inc = include_directories('kerneldevice') plugins_inc = include_directories('plugins') headers = files( 'mm-modem-helpers.h', 'mm-sms-part.h', ) enums_types = 'mm-helper-enums-types' enums_sources = [] enums_sources += custom_target( enums_types + '.c', input: headers, output: enums_types + '.c', command: [ python, mm_mkenums, '--fhead', '#include "mm-helper-enums-types.h"\n', '--template', files(templates_dir / enums_types + '.c.template'), '@INPUT@'], capture: true, ) enums_sources += custom_target( enums_types + '.h', input: headers, output: enums_types + '.h', command: [ python, mm_mkenums, '--fhead', '#include "mm-sms-part.h"\n#include "mm-modem-helpers.h"\n#ifndef __MM_HELPER_ENUMS_TYPES_H__\n#define __MM_HELPER_ENUMS_TYPES_H__\n', '--template', files(templates_dir / enums_types + '.h.template'), '--ftail', '#endif /* __MM_HELPER_ENUMS_TYPES_H__ */\n', '@INPUT@'], capture: true, ) sources = files( 'mm-charsets.c', 'mm-error-helpers.c', 'mm-log.c', 'mm-log-object.c', 'mm-modem-helpers.c', 'mm-sms-part-3gpp.c', 'mm-sms-part.c', 'mm-sms-part-cdma.c', ) incs = [ top_inc, # FIXME: only necessary if qmi is enabled? kerneldevice_inc, ] deps = [libmm_glib_dep] private_deps = [] if enable_qmi sources += files('mm-modem-helpers-qmi.c') deps += qmi_glib_dep endif if enable_mbim sources += files('mm-modem-helpers-mbim.c') deps += mbim_glib_dep endif if enable_systemd_journal private_deps += libsystemd_dep endif libhelpers = static_library( 'helpers', sources: sources + enums_sources, include_directories: incs, dependencies: deps + private_deps, ) libhelpers_dep = declare_dependency( sources: enums_sources[1], include_directories: ['.', kerneldevice_inc], dependencies: deps, link_with: libhelpers, ) # kerneldevice library sources = files( 'kerneldevice/mm-kernel-device.c', 'kerneldevice/mm-kernel-device-generic.c', 'kerneldevice/mm-kernel-device-generic-rules.c', 'kerneldevice/mm-kernel-device-helpers.c', ) deps = [libhelpers_dep] if enable_qrtr sources += files('kerneldevice/mm-kernel-device-qrtr.c') deps += qrtr_glib_dep endif if enable_udev sources += files('kerneldevice/mm-kernel-device-udev.c') deps += gudev_dep endif libkerneldevice = static_library( 'kerneldevice', sources: sources, include_directories: top_inc, dependencies: deps, c_args: '-DUDEVRULESDIR="@0@"'.format(udev_rulesdir), ) libkerneldevice_dep = declare_dependency( dependencies: deps, link_with: libkerneldevice, ) # ports library headers = files( 'mm-port.h', 'mm-port-serial-at.h', ) sources = files( 'mm-netlink.c', 'mm-port.c', 'mm-port-net.c', 'mm-port-serial-at.c', 'mm-port-serial.c', 'mm-port-serial-gps.c', 'mm-port-serial-qcdm.c', 'mm-serial-parsers.c', ) deps = [libkerneldevice_dep] private_deps = [gio_unix_dep] if enable_qmi headers += files('mm-port-qmi.h') sources += files('mm-port-qmi.c') endif if enable_mbim sources += files('mm-port-mbim.c') endif enums_types = 'mm-port-enums-types' port_enums_sources = [] port_enums_sources += custom_target( enums_types + '.c', input: headers, output: enums_types + '.c', command: [ python, mm_mkenums, '--fhead', '#include "mm-port-enums-types.h"\n', '--template', files(templates_dir / enums_types + '.c.template'), '@INPUT@'], capture: true, ) port_enums_sources += custom_target( enums_types + '.h', input: headers, output: enums_types + '.h', command: [ python, mm_mkenums, '--fhead', '#include "config.h"\n#include "mm-port.h"\n#include "mm-port-serial-at.h"\n#if defined WITH_QMI\n#include "mm-port-qmi.h"\n#endif\n#ifndef __MM_PORT_ENUMS_TYPES_H__\n#define __MM_PORT_ENUMS_TYPES_H__\n', '--template', files(templates_dir / enums_types + '.h.template'), '--ftail', '#endif /* __MM_PORT_ENUMS_TYPES_H__ */\n', '@INPUT@'], capture: true, ) libport = static_library( 'port', sources: sources + port_enums_sources, include_directories: top_inc, dependencies: deps + private_deps, ) libport_dep = declare_dependency( sources: port_enums_sources[1], include_directories: '.', dependencies: deps, link_with: libport, ) # Daemon enums, required by plugins headers = files( 'mm-base-bearer.h', 'mm-filter.h', 'mm-port-probe.h', ) enums_types = 'mm-daemon-enums-types' daemon_enums_sources = [] daemon_enums_sources += custom_target( enums_types + '.c', input: headers, output: enums_types + '.c', command: [ python, mm_mkenums, '--fhead', '#include "mm-daemon-enums-types.h"\n', '--template', files(templates_dir / enums_types + '.c.template'), '@INPUT@'], capture: true, ) daemon_enums_sources += custom_target( enums_types + '.h', input: headers, output: enums_types + '.h', command: [ python, mm_mkenums, '--fhead', '#include "mm-filter.h"\n#include "mm-base-bearer.h"\n#include "mm-port-probe.h"\n#ifndef __MM_DAEMON_ENUMS_TYPES_H__\n#define __MM_DAEMON_ENUMS_TYPES_H__\n', '--template', files(templates_dir / enums_types + '.h.template'), '--ftail', '#endif /* __MM_DAEMON_ENUMS_TYPES_H__ */\n', '@INPUT@'], capture: true, ) daemon_enums_types_dep = declare_dependency( sources: daemon_enums_sources[1], include_directories: '.', ) # Additional vendor plugins subdir('plugins') # ModemManager daemon sources = files( 'main.c', 'mm-auth-provider.c', 'mm-base-bearer.c', 'mm-base-call.c', 'mm-base-manager.c', 'mm-base-modem-at.c', 'mm-base-modem.c', 'mm-base-sim.c', 'mm-base-sms.c', 'mm-bearer-list.c', 'mm-broadband-bearer.c', 'mm-broadband-modem.c', 'mm-call-list.c', 'mm-context.c', 'mm-device.c', 'mm-dispatcher.c', 'mm-dispatcher-connection.c', 'mm-dispatcher-fcc-unlock.c', 'mm-filter.c', 'mm-iface-modem-3gpp.c', 'mm-iface-modem-3gpp-profile-manager.c', 'mm-iface-modem-3gpp-ussd.c', 'mm-iface-modem.c', 'mm-iface-modem-cdma.c', 'mm-iface-modem-firmware.c', 'mm-iface-modem-location.c', 'mm-iface-modem-messaging.c', 'mm-iface-modem-oma.c', 'mm-iface-modem-sar.c', 'mm-iface-modem-signal.c', 'mm-iface-modem-simple.c', 'mm-iface-modem-time.c', 'mm-iface-modem-voice.c', 'mm-log-helpers.c', 'mm-plugin.c', 'mm-plugin-manager.c', 'mm-port-probe.c', 'mm-port-probe-at.c', 'mm-private-boxed-types.c', 'mm-sms-list.c', ) sources += daemon_enums_sources deps = [ gmodule_dep, libport_dep, libqcdm_dep, ] if enable_tests deps += [libmm_test_generated_dep] endif c_args = [ '-DMM_COMPILATION', '-DPLUGINDIR="@0@"'.format(mm_prefix / mm_pkglibdir), '-DFCCUNLOCKDIRPACKAGE="@0@"'.format(mm_prefix / mm_pkglibdir / 'fcc-unlock.d'), '-DFCCUNLOCKDIRUSER="@0@"'.format(mm_prefix / mm_pkgsysconfdir / 'fcc-unlock.d'), '-DCONNECTIONDIRPACKAGE="@0@"'.format(mm_prefix / mm_pkglibdir / 'connection.d'), '-DCONNECTIONDIRUSER="@0@"'.format(mm_prefix / mm_pkgsysconfdir / 'connection.d'), ] if enable_qrtr sources += files('mm-qrtr-bus-watcher.c') endif # Additional suspend/resume support via systemd if enable_systemd_suspend_resume sources += files('mm-sleep-monitor-systemd.c') deps += [ gio_unix_dep, libsystemd_dep, ] endif #suspend/resume support via powerd if enable_powerd_suspend_resume sources += files('mm-sleep-monitor-powerd.c') endif if enable_polkit deps += polkit_gobject_dep endif # Additional QMI support in ModemManager if enable_qmi sources += files( 'mm-bearer-qmi.c', 'mm-broadband-modem-qmi.c', 'mm-call-qmi.c', 'mm-shared-qmi.c', 'mm-sim-qmi.c', 'mm-sms-qmi.c', ) endif # Additional MBIM support in ModemManager if enable_mbim sources += files( 'mm-bearer-mbim.c', 'mm-broadband-modem-mbim.c', 'mm-sim-mbim.c', 'mm-sms-mbim.c', ) endif executable( 'ModemManager', sources: [sources, builtin_sources], include_directories: [ top_inc, plugins_inc ], dependencies: deps, c_args: c_args, link_whole: builtin_plugins, install: true, install_dir: mm_sbindir, ) pkg.generate( version: mm_version, name: mm_name, description: 'Common headers provided by ModemManager', subdirs: mm_name, variables: 'exec_prefix=${prefix}', ) # generic udev rules install_data( '80-mm-candidate.rules', install_dir: udev_rulesdir, ) if enable_tests subdir('tests') endifModemManager-1.23.4-dev/src/mm-auth-provider.c000066400000000000000000000161701456466623000210650ustar00rootroot00000000000000/* -*- 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 - 2012 Red Hat, Inc. * Copyright (C) 2012 Google, Inc. * Copyright (C) 2020 Aleksander Morgado */ #include #include #include "mm-errors-types.h" #include "mm-log-object.h" #include "mm-utils.h" #include "mm-auth-provider.h" #if defined WITH_POLKIT # include #endif struct _MMAuthProvider { GObject parent; #if defined WITH_POLKIT PolkitAuthority *authority; #endif }; struct _MMAuthProviderClass { GObjectClass parent; }; static void log_object_iface_init (MMLogObjectInterface *iface); G_DEFINE_TYPE_EXTENDED (MMAuthProvider, mm_auth_provider, G_TYPE_OBJECT, 0, G_IMPLEMENT_INTERFACE (MM_TYPE_LOG_OBJECT, log_object_iface_init)) /*****************************************************************************/ gboolean mm_auth_provider_authorize_finish (MMAuthProvider *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } #if defined WITH_POLKIT typedef struct { PolkitSubject *subject; gchar *authorization; GDBusMethodInvocation *invocation; } AuthorizeContext; static void authorize_context_free (AuthorizeContext *ctx) { g_object_unref (ctx->invocation); g_object_unref (ctx->subject); g_free (ctx->authorization); g_free (ctx); } static void check_authorization_ready (PolkitAuthority *authority, GAsyncResult *res, GTask *task) { PolkitAuthorizationResult *pk_result; GError *error = NULL; AuthorizeContext *ctx; if (g_task_return_error_if_cancelled (task)) { g_object_unref (task); return; } ctx = g_task_get_task_data (task); pk_result = polkit_authority_check_authorization_finish (authority, res, &error); if (!pk_result) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "PolicyKit authorization failed: '%s'", error->message); g_error_free (error); } else { if (polkit_authorization_result_get_is_authorized (pk_result)) /* Good! */ g_task_return_boolean (task, TRUE); else if (polkit_authorization_result_get_is_challenge (pk_result)) g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_UNAUTHORIZED, "PolicyKit authorization failed: challenge needed for '%s'", ctx->authorization); else g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_UNAUTHORIZED, "PolicyKit authorization failed: not authorized for '%s'", ctx->authorization); g_object_unref (pk_result); } g_object_unref (task); } #endif void mm_auth_provider_authorize (MMAuthProvider *self, GDBusMethodInvocation *invocation, const gchar *authorization, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; task = g_task_new (self, cancellable, callback, user_data); #if defined WITH_POLKIT { AuthorizeContext *ctx; /* When creating the object, we actually allowed errors when looking for the * authority. If that is the case, we'll just forbid any incoming * authentication request */ if (!self->authority) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "PolicyKit authorization error: 'authority not found'"); g_object_unref (task); return; } ctx = g_new (AuthorizeContext, 1); ctx->invocation = g_object_ref (invocation); ctx->authorization = g_strdup (authorization); ctx->subject = polkit_system_bus_name_new (g_dbus_method_invocation_get_sender (ctx->invocation)); g_task_set_task_data (task, ctx, (GDestroyNotify)authorize_context_free); polkit_authority_check_authorization (self->authority, ctx->subject, authorization, NULL, /* details */ POLKIT_CHECK_AUTHORIZATION_FLAGS_ALLOW_USER_INTERACTION, cancellable, (GAsyncReadyCallback)check_authorization_ready, task); } #else /* Just create the result and complete it */ g_task_return_boolean (task, TRUE); g_object_unref (task); #endif } /*****************************************************************************/ static gchar * log_object_build_id (MMLogObject *_self) { return g_strdup ("auth-provider"); } /*****************************************************************************/ static void mm_auth_provider_init (MMAuthProvider *self) { #if defined WITH_POLKIT { GError *error = NULL; self->authority = polkit_authority_get_sync (NULL, &error); if (!self->authority) { /* NOTE: we failed to create the polkit authority, but we still create * our AuthProvider. Every request will fail, though. */ mm_obj_warn (self, "failed to create PolicyKit authority: '%s'", error ? error->message : "unknown"); g_clear_error (&error); } } #endif } static void dispose (GObject *object) { #if defined WITH_POLKIT g_clear_object (&(MM_AUTH_PROVIDER (object)->authority)); #endif G_OBJECT_CLASS (mm_auth_provider_parent_class)->dispose (object); } static void log_object_iface_init (MMLogObjectInterface *iface) { iface->build_id = log_object_build_id; } static void mm_auth_provider_class_init (MMAuthProviderClass *class) { GObjectClass *object_class = G_OBJECT_CLASS (class); object_class->dispose = dispose; } MM_DEFINE_SINGLETON_GETTER (MMAuthProvider, mm_auth_provider_get, MM_TYPE_AUTH_PROVIDER) ModemManager-1.23.4-dev/src/mm-auth-provider.h000066400000000000000000000061341456466623000210710ustar00rootroot00000000000000/* -*- 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 - 2012 Red Hat, Inc. * Copyright (C) 2012 Google, Inc. */ #ifndef MM_AUTH_PROVIDER_H #define MM_AUTH_PROVIDER_H #include #include #define MM_TYPE_AUTH_PROVIDER (mm_auth_provider_get_type ()) #define MM_AUTH_PROVIDER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_AUTH_PROVIDER, MMAuthProvider)) #define MM_AUTH_PROVIDER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_AUTH_PROVIDER, MMAuthProviderClass)) #define MM_IS_AUTH_PROVIDER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_AUTH_PROVIDER)) #define MM_IS_AUTH_PROVIDER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_AUTH_PROVIDER)) #define MM_AUTH_PROVIDER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_AUTH_PROVIDER, MMAuthProviderClass)) /* Authorizations */ #define MM_AUTHORIZATION_MANAGER_CONTROL "org.freedesktop.ModemManager1.Control" #define MM_AUTHORIZATION_DEVICE_CONTROL "org.freedesktop.ModemManager1.Device.Control" #define MM_AUTHORIZATION_CONTACTS "org.freedesktop.ModemManager1.Contacts" #define MM_AUTHORIZATION_MESSAGING "org.freedesktop.ModemManager1.Messaging" #define MM_AUTHORIZATION_VOICE "org.freedesktop.ModemManager1.Voice" #define MM_AUTHORIZATION_USSD "org.freedesktop.ModemManager1.USSD" #define MM_AUTHORIZATION_LOCATION "org.freedesktop.ModemManager1.Location" #define MM_AUTHORIZATION_TIME "org.freedesktop.ModemManager1.Time" #define MM_AUTHORIZATION_FIRMWARE "org.freedesktop.ModemManager1.Firmware" typedef struct _MMAuthProvider MMAuthProvider; typedef struct _MMAuthProviderClass MMAuthProviderClass; typedef struct _MMAuthProviderPrivate MMAuthProviderPrivate; GType mm_auth_provider_get_type (void); MMAuthProvider *mm_auth_provider_get (void); void mm_auth_provider_authorize (MMAuthProvider *self, GDBusMethodInvocation *invocation, const gchar *authorization, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); gboolean mm_auth_provider_authorize_finish (MMAuthProvider *self, GAsyncResult *res, GError **error); #endif /* MM_AUTH_PROVIDER_H */ ModemManager-1.23.4-dev/src/mm-base-bearer.c000066400000000000000000002212771456466623000204520ustar00rootroot00000000000000/* -*- 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 - 2011 Red Hat, Inc. * Copyright (C) 2011 Google, Inc. * Copyright (C) 2015 Azimut Electronics * Copyright (C) 2011 - 2016 Aleksander Morgado */ #include #include #include #include #include #include #include #define _LIBMM_INSIDE_MM #include #include "mm-daemon-enums-types.h" #include "mm-iface-modem.h" #include "mm-iface-modem-3gpp.h" #include "mm-iface-modem-3gpp-profile-manager.h" #include "mm-iface-modem-cdma.h" #include "mm-base-bearer.h" #include "mm-base-modem-at.h" #include "mm-base-modem.h" #include "mm-log-object.h" #include "mm-modem-helpers.h" #include "mm-error-helpers.h" #include "mm-bearer-stats.h" #include "mm-dispatcher-connection.h" /* We require up to 20s to get a proper IP when using PPP */ #define BEARER_IP_TIMEOUT_DEFAULT 20 #define BEARER_DEFERRED_UNREGISTRATION_TIMEOUT 15 #define BEARER_STATS_UPDATE_TIMEOUT 30 /* Initial connectivity check after 30s, then each 5s */ #define BEARER_CONNECTION_MONITOR_INITIAL_TIMEOUT 30 #define BEARER_CONNECTION_MONITOR_TIMEOUT 5 static void log_object_iface_init (MMLogObjectInterface *iface); G_DEFINE_TYPE_EXTENDED (MMBaseBearer, mm_base_bearer, MM_GDBUS_TYPE_BEARER_SKELETON, 0, G_IMPLEMENT_INTERFACE (MM_TYPE_LOG_OBJECT, log_object_iface_init)) typedef enum { CONNECTION_FORBIDDEN_REASON_NONE, CONNECTION_FORBIDDEN_REASON_UNREGISTERED, CONNECTION_FORBIDDEN_REASON_ROAMING, CONNECTION_FORBIDDEN_REASON_EMERGENCY_ONLY, CONNECTION_FORBIDDEN_REASON_LAST } ConnectionForbiddenReason; enum { PROP_0, PROP_PATH, PROP_CONNECTION, PROP_MODEM, PROP_STATUS, PROP_CONFIG, PROP_LAST }; static GParamSpec *properties[PROP_LAST]; struct _MMBaseBearerPrivate { /* The connection to the system bus */ GDBusConnection *connection; guint dbus_id; /* The modem which owns this BEARER */ MMBaseModem *modem; /* The path where the BEARER object is exported */ gchar *path; /* Status of this bearer */ MMBearerStatus status; /* Whether we must ignore all disconnection updates if they're * detected by ModemManager itself. */ gboolean ignore_disconnection_reports; /* Configuration of the bearer */ MMBearerProperties *config; /* Cancellable for connect() */ GCancellable *connect_cancellable; /* handler id for the disconnect + cancel connect request */ gulong disconnect_signal_handler; /* Connection status monitoring */ guint connection_monitor_id; /* Flag to specify whether connection monitoring is supported or not */ gboolean load_connection_status_unsupported; /*-- 3GPP specific --*/ guint deferred_3gpp_unregistration_id; /* Reason if 3GPP connection is forbidden */ ConnectionForbiddenReason reason_3gpp; /* Handler ID for the registration state change signals */ guint id_3gpp_registration_change; /*-- CDMA specific --*/ guint deferred_cdma_unregistration_id; /* Reason if CDMA connection is forbidden */ ConnectionForbiddenReason reason_cdma; /* Handler IDs for the registration state change signals */ guint id_cdma1x_registration_change; guint id_evdo_registration_change; /* The stats object to expose */ MMBearerStats *stats; /* Handler id for the stats update timeout */ guint stats_update_id; /* Timer to measure the duration of the connection */ GTimer *duration_timer; /* Flag to specify whether reloading stats is supported or not */ gboolean reload_stats_supported; }; /*****************************************************************************/ static const gchar *connection_forbidden_reason_str [CONNECTION_FORBIDDEN_REASON_LAST] = { "none", "Not registered in the network", "Registered in roaming network, and roaming not allowed", "Emergency services only", }; /*****************************************************************************/ void mm_base_bearer_export (MMBaseBearer *self) { gchar *path; path = g_strdup_printf (MM_DBUS_BEARER_PREFIX "/%d", self->priv->dbus_id); g_object_set (self, MM_BASE_BEARER_PATH, path, NULL); g_free (path); } /*****************************************************************************/ static void connection_monitor_stop (MMBaseBearer *self) { if (self->priv->connection_monitor_id) { g_source_remove (self->priv->connection_monitor_id); self->priv->connection_monitor_id = 0; } } static void load_connection_status_ready (MMBaseBearer *self, GAsyncResult *res) { GError *error = NULL; MMBearerConnectionStatus status; status = MM_BASE_BEARER_GET_CLASS (self)->load_connection_status_finish (self, res, &error); if (status == MM_BEARER_CONNECTION_STATUS_UNKNOWN) { /* Only warn if not reporting an "unsupported" error */ if (!g_error_matches (error, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED)) { mm_obj_warn (self, "checking if connected failed: %s", error->message); g_error_free (error); return; } /* If we're being told that connection monitoring is unsupported, just * ignore the error and remove the timeout. */ mm_obj_dbg (self, "connection monitoring is unsupported by the device"); self->priv->load_connection_status_unsupported = TRUE; connection_monitor_stop (self); g_error_free (error); return; } /* Report connection or disconnection */ g_assert (status == MM_BEARER_CONNECTION_STATUS_CONNECTED || status == MM_BEARER_CONNECTION_STATUS_DISCONNECTED); mm_obj_dbg (self, "connection status loaded: %s", mm_bearer_connection_status_get_string (status)); mm_base_bearer_report_connection_status (self, status); } static gboolean connection_monitor_cb (MMBaseBearer *self) { /* If the implementation knows how to load connection status, run it */ if (self->priv->status == MM_BEARER_STATUS_CONNECTED) MM_BASE_BEARER_GET_CLASS (self)->load_connection_status ( self, (GAsyncReadyCallback)load_connection_status_ready, NULL); return G_SOURCE_CONTINUE; } static gboolean initial_connection_monitor_cb (MMBaseBearer *self) { if (self->priv->status == MM_BEARER_STATUS_CONNECTED) MM_BASE_BEARER_GET_CLASS (self)->load_connection_status ( self, (GAsyncReadyCallback)load_connection_status_ready, NULL); /* Add new monitor timeout at a higher rate */ self->priv->connection_monitor_id = g_timeout_add_seconds (BEARER_CONNECTION_MONITOR_TIMEOUT, (GSourceFunc) connection_monitor_cb, self); /* Remove the initial connection monitor timeout as we added a new one */ return G_SOURCE_REMOVE; } static void connection_monitor_start (MMBaseBearer *self) { /* If not implemented, don't schedule anything */ if (!MM_BASE_BEARER_GET_CLASS (self)->load_connection_status || !MM_BASE_BEARER_GET_CLASS (self)->load_connection_status_finish) return; if (self->priv->load_connection_status_unsupported) return; /* Schedule initial check */ g_assert (!self->priv->connection_monitor_id); self->priv->connection_monitor_id = g_timeout_add_seconds (BEARER_CONNECTION_MONITOR_INITIAL_TIMEOUT, (GSourceFunc) initial_connection_monitor_cb, self); } /*****************************************************************************/ static void bearer_update_connection_error (MMBaseBearer *self, const GError *connection_error) { g_autoptr(GVariant) tuple = NULL; if (connection_error) { g_autoptr(GError) normalized_error = NULL; /* Never overwrite a connection error if it's already set */ tuple = mm_gdbus_bearer_dup_connection_error (MM_GDBUS_BEARER (self)); if (tuple) return; /* * Limit the type of errors we can expose in the interface; * e.g. we don't want QMI or MBIM specific errors reported. */ normalized_error = mm_normalize_error (connection_error); tuple = mm_common_error_to_tuple (normalized_error); } mm_gdbus_bearer_set_connection_error (MM_GDBUS_BEARER (self), tuple); } /*****************************************************************************/ static void bearer_update_interface_stats (MMBaseBearer *self) { mm_gdbus_bearer_set_stats ( MM_GDBUS_BEARER (self), mm_bearer_stats_get_dictionary (self->priv->stats)); } static void bearer_reset_ongoing_interface_stats (MMBaseBearer *self) { mm_bearer_stats_set_duration (self->priv->stats, 0); mm_bearer_stats_set_tx_bytes (self->priv->stats, 0); mm_bearer_stats_set_rx_bytes (self->priv->stats, 0); mm_bearer_stats_set_start_date (self->priv->stats, 0); mm_bearer_stats_set_uplink_speed (self->priv->stats, 0); mm_bearer_stats_set_downlink_speed (self->priv->stats, 0); bearer_update_interface_stats (self); } static void bearer_set_ongoing_interface_stats (MMBaseBearer *self, guint duration, guint64 rx_bytes, guint64 tx_bytes) { guint n_updates = 0; /* Make sure we don't reset to 0 these values if we had ever set them * before. Just ignore the update if we're reported 0 */ if (duration) { gint delta_duration; delta_duration = duration - mm_bearer_stats_get_duration (self->priv->stats); if (delta_duration > 0) { mm_bearer_stats_set_duration (self->priv->stats, duration); mm_bearer_stats_set_total_duration (self->priv->stats, mm_bearer_stats_get_total_duration (self->priv->stats) + delta_duration); n_updates++; } } if (rx_bytes) { gint64 delta_rx_bytes; delta_rx_bytes = rx_bytes - mm_bearer_stats_get_rx_bytes (self->priv->stats); if (delta_rx_bytes > 0) { mm_bearer_stats_set_rx_bytes (self->priv->stats, rx_bytes); mm_bearer_stats_set_total_rx_bytes (self->priv->stats, mm_bearer_stats_get_total_rx_bytes (self->priv->stats) + delta_rx_bytes); n_updates++; } } if (tx_bytes) { gint64 delta_tx_bytes; delta_tx_bytes = tx_bytes - mm_bearer_stats_get_tx_bytes (self->priv->stats); if (delta_tx_bytes > 0) { mm_bearer_stats_set_tx_bytes (self->priv->stats, tx_bytes); mm_bearer_stats_set_total_tx_bytes (self->priv->stats, mm_bearer_stats_get_total_tx_bytes (self->priv->stats) + delta_tx_bytes); n_updates++; } } if (n_updates) bearer_update_interface_stats (self); } static void bearer_stats_stop (MMBaseBearer *self) { if (self->priv->duration_timer) { bearer_set_ongoing_interface_stats (self, (guint64) g_timer_elapsed (self->priv->duration_timer, NULL), 0, 0); g_timer_destroy (self->priv->duration_timer); self->priv->duration_timer = NULL; } if (self->priv->stats_update_id) { g_source_remove (self->priv->stats_update_id); self->priv->stats_update_id = 0; } } static void reload_stats_ready (MMBaseBearer *self, GAsyncResult *res) { GError *error = NULL; guint64 rx_bytes = 0; guint64 tx_bytes = 0; if (!MM_BASE_BEARER_GET_CLASS (self)->reload_stats_finish (self, &rx_bytes, &tx_bytes, res, &error)) { mm_obj_warn (self, "reloading stats failed: %s", error->message); g_error_free (error); return; } /* We only update stats if they were retrieved properly */ bearer_set_ongoing_interface_stats (self, (guint32) g_timer_elapsed (self->priv->duration_timer, NULL), rx_bytes, tx_bytes); } static gboolean stats_update_cb (MMBaseBearer *self) { /* Ignore stats update if we're not connected */ if (self->priv->status != MM_BEARER_STATUS_CONNECTED) return G_SOURCE_CONTINUE; /* If the implementation knows how to update stat values, run it */ if (self->priv->reload_stats_supported) { MM_BASE_BEARER_GET_CLASS (self)->reload_stats ( self, (GAsyncReadyCallback)reload_stats_ready, NULL); return G_SOURCE_CONTINUE; } /* Otherwise, just update duration and we're done */ bearer_set_ongoing_interface_stats (self, (guint32) g_timer_elapsed (self->priv->duration_timer, NULL), 0, 0); return G_SOURCE_CONTINUE; } static void bearer_stats_start (MMBaseBearer *self, guint64 uplink_speed, guint64 downlink_speed) { /* Start duration timer */ g_assert (!self->priv->duration_timer); self->priv->duration_timer = g_timer_new (); /* Schedule */ g_assert (!self->priv->stats_update_id); self->priv->stats_update_id = g_timeout_add_seconds (BEARER_STATS_UPDATE_TIMEOUT, (GSourceFunc) stats_update_cb, self); mm_bearer_stats_set_start_date (self->priv->stats, (guint64)(g_get_real_time() / G_USEC_PER_SEC)); mm_bearer_stats_set_uplink_speed (self->priv->stats, uplink_speed); mm_bearer_stats_set_downlink_speed (self->priv->stats, downlink_speed); bearer_update_interface_stats (self); /* Load initial values */ stats_update_cb (self); } /*****************************************************************************/ void mm_base_bearer_report_speeds (MMBaseBearer *self, guint64 uplink_speed, guint64 downlink_speed) { /* Ignore speeds update if we're not connected */ if (self->priv->status != MM_BEARER_STATUS_CONNECTED) return; mm_bearer_stats_set_uplink_speed (self->priv->stats, uplink_speed); mm_bearer_stats_set_downlink_speed (self->priv->stats, downlink_speed); bearer_update_interface_stats (self); } /*****************************************************************************/ static void dispatcher_connection_run_ready (MMDispatcherConnection *dispatcher, GAsyncResult *res, MMBaseBearer *self) { g_autoptr(GError) error = NULL; if (!mm_dispatcher_connection_run_finish (dispatcher, res, &error)) mm_obj_warn (self, "errors detected in dispatcher: %s", error->message); g_object_unref (self); } static void bearer_run_dispatcher_scripts (MMBaseBearer *self, gboolean connected) { MMDispatcherConnection *dispatcher; const gchar *interface; interface = mm_gdbus_bearer_get_interface (MM_GDBUS_BEARER (self)); if (!self->priv->modem || !self->priv->path || !interface) return; dispatcher = mm_dispatcher_connection_get (); mm_dispatcher_connection_run (dispatcher, g_dbus_object_get_object_path (G_DBUS_OBJECT (self->priv->modem)), self->priv->path, interface, connected, NULL, /* cancellable */ (GAsyncReadyCallback)dispatcher_connection_run_ready, g_object_ref (self)); } /*****************************************************************************/ static void bearer_reset_interface_status (MMBaseBearer *self) { mm_gdbus_bearer_set_profile_id (MM_GDBUS_BEARER (self), MM_3GPP_PROFILE_ID_UNKNOWN); mm_gdbus_bearer_set_multiplexed (MM_GDBUS_BEARER (self), FALSE); mm_gdbus_bearer_set_connected (MM_GDBUS_BEARER (self), FALSE); mm_gdbus_bearer_set_suspended (MM_GDBUS_BEARER (self), FALSE); mm_gdbus_bearer_set_interface (MM_GDBUS_BEARER (self), NULL); mm_gdbus_bearer_set_ip4_config ( MM_GDBUS_BEARER (self), mm_bearer_ip_config_get_dictionary (NULL)); mm_gdbus_bearer_set_ip6_config ( MM_GDBUS_BEARER (self), mm_bearer_ip_config_get_dictionary (NULL)); } static void bearer_update_status (MMBaseBearer *self, MMBearerStatus status) { /* NOTE: we do allow status 'CONNECTED' here; it may happen if we go into * DISCONNECTING and we cannot disconnect */ /* Do nothing if the status is the same */ if (self->priv->status == status) return; /* Update the property value */ self->priv->status = status; g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_STATUS]); /* Ensure that we don't expose any connection related data in the * interface when going into disconnected state. */ if (self->priv->status == MM_BEARER_STATUS_DISCONNECTED) { g_autoptr(GString) report = NULL; /* Report disconnection via dispatcher scripts, before reseting the interface */ bearer_run_dispatcher_scripts (self, FALSE); bearer_reset_interface_status (self); /* Cleanup flag to ignore disconnection reports */ self->priv->ignore_disconnection_reports = FALSE; /* Stop statistics */ bearer_stats_stop (self); /* Stop connection monitoring */ connection_monitor_stop (self); /* Build and log report */ report = g_string_new (NULL); g_string_append_printf (report, "connection #%u finished: duration %us", mm_bearer_stats_get_attempts (self->priv->stats), mm_bearer_stats_get_duration (self->priv->stats)); if (self->priv->reload_stats_supported) g_string_append_printf (report, ", tx: %" G_GUINT64_FORMAT " bytes, rx: %" G_GUINT64_FORMAT " bytes", mm_bearer_stats_get_tx_bytes (self->priv->stats), mm_bearer_stats_get_rx_bytes (self->priv->stats)); mm_obj_msg (self, "%s", report->str); } } static void bearer_update_status_connected (MMBaseBearer *self, const gchar *interface, gboolean multiplexed, gint profile_id, MMBearerIpConfig *ipv4_config, MMBearerIpConfig *ipv6_config, guint64 uplink_speed, guint64 downlink_speed) { mm_gdbus_bearer_set_profile_id (MM_GDBUS_BEARER (self), profile_id); mm_gdbus_bearer_set_multiplexed (MM_GDBUS_BEARER (self), multiplexed); mm_gdbus_bearer_set_connected (MM_GDBUS_BEARER (self), TRUE); mm_gdbus_bearer_set_suspended (MM_GDBUS_BEARER (self), FALSE); mm_gdbus_bearer_set_interface (MM_GDBUS_BEARER (self), interface); mm_gdbus_bearer_set_ip4_config ( MM_GDBUS_BEARER (self), mm_bearer_ip_config_get_dictionary (ipv4_config)); mm_gdbus_bearer_set_ip6_config ( MM_GDBUS_BEARER (self), mm_bearer_ip_config_get_dictionary (ipv6_config)); /* If PPP is involved in the requested IP config, we must ignore * all disconnection reports found via CGACT? polling or CGEV URCs. * In this case, upper layers should always explicitly disconnect * the bearer when ownership of the TTY is given back to MM. */ if ((ipv4_config && mm_bearer_ip_config_get_method (ipv4_config) == MM_BEARER_IP_METHOD_PPP) || (ipv6_config && mm_bearer_ip_config_get_method (ipv6_config) == MM_BEARER_IP_METHOD_PPP)) { mm_obj_dbg (self, "PPP is required for connection, will ignore disconnection reports"); self->priv->ignore_disconnection_reports = TRUE; } /* Update the property value */ self->priv->status = MM_BEARER_STATUS_CONNECTED; g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_STATUS]); /* Start statistics */ bearer_stats_start (self, uplink_speed, downlink_speed); /* Start connection monitor, if supported */ connection_monitor_start (self); /* Run dispatcher scripts */ bearer_run_dispatcher_scripts (self, TRUE); } /*****************************************************************************/ static void reset_deferred_unregistration (MMBaseBearer *self) { if (self->priv->deferred_cdma_unregistration_id) { g_source_remove (self->priv->deferred_cdma_unregistration_id); self->priv->deferred_cdma_unregistration_id = 0; } if (self->priv->deferred_3gpp_unregistration_id) { g_source_remove (self->priv->deferred_3gpp_unregistration_id); self->priv->deferred_3gpp_unregistration_id = 0; } } static gboolean deferred_3gpp_unregistration_cb (MMBaseBearer *self) { g_warn_if_fail (self->priv->reason_3gpp == CONNECTION_FORBIDDEN_REASON_UNREGISTERED); self->priv->deferred_3gpp_unregistration_id = 0; mm_obj_dbg (self, "forcing bearer disconnection, not registered in 3GPP network"); mm_base_bearer_disconnect_force (self); return G_SOURCE_REMOVE; } static void modem_3gpp_registration_state_changed (MMIfaceModem3gpp *modem, GParamSpec *pspec, MMBaseBearer *self) { MMModem3gppRegistrationState state = MM_MODEM_3GPP_REGISTRATION_STATE_UNKNOWN; g_object_get (modem, MM_IFACE_MODEM_3GPP_REGISTRATION_STATE, &state, NULL); switch (state) { case MM_MODEM_3GPP_REGISTRATION_STATE_IDLE: case MM_MODEM_3GPP_REGISTRATION_STATE_DENIED: case MM_MODEM_3GPP_REGISTRATION_STATE_UNKNOWN: self->priv->reason_3gpp = CONNECTION_FORBIDDEN_REASON_UNREGISTERED; break; case MM_MODEM_3GPP_REGISTRATION_STATE_HOME: case MM_MODEM_3GPP_REGISTRATION_STATE_HOME_SMS_ONLY: case MM_MODEM_3GPP_REGISTRATION_STATE_HOME_CSFB_NOT_PREFERRED: case MM_MODEM_3GPP_REGISTRATION_STATE_SEARCHING: case MM_MODEM_3GPP_REGISTRATION_STATE_ATTACHED_RLOS: self->priv->reason_3gpp = CONNECTION_FORBIDDEN_REASON_NONE; break; case MM_MODEM_3GPP_REGISTRATION_STATE_ROAMING: case MM_MODEM_3GPP_REGISTRATION_STATE_ROAMING_SMS_ONLY: case MM_MODEM_3GPP_REGISTRATION_STATE_ROAMING_CSFB_NOT_PREFERRED: if (mm_bearer_properties_get_allow_roaming (mm_base_bearer_peek_config (self))) self->priv->reason_3gpp = CONNECTION_FORBIDDEN_REASON_NONE; else self->priv->reason_3gpp = CONNECTION_FORBIDDEN_REASON_ROAMING; break; case MM_MODEM_3GPP_REGISTRATION_STATE_EMERGENCY_ONLY: self->priv->reason_3gpp = CONNECTION_FORBIDDEN_REASON_EMERGENCY_ONLY; break; default: g_assert_not_reached (); } /* If no reason to disconnect, or if it's a mixed CDMA+LTE modem without a CDMA reason, * just don't do anything. */ if (self->priv->reason_3gpp == CONNECTION_FORBIDDEN_REASON_NONE || (mm_iface_modem_is_cdma (MM_IFACE_MODEM (modem)) && self->priv->reason_cdma == CONNECTION_FORBIDDEN_REASON_NONE)) { reset_deferred_unregistration (self); return; } /* Modem is roaming and roaming not allowed, report right away */ if (self->priv->reason_3gpp == CONNECTION_FORBIDDEN_REASON_ROAMING) { mm_obj_dbg (self, "bearer not allowed to connect, registered in roaming 3GPP network"); reset_deferred_unregistration (self); mm_base_bearer_disconnect_force (self); return; } /* Modem is registered under emergency services only? */ if (self->priv->reason_3gpp == CONNECTION_FORBIDDEN_REASON_EMERGENCY_ONLY) { mm_obj_dbg (self, "bearer not allowed to connect, emergency services only"); reset_deferred_unregistration (self); mm_base_bearer_disconnect_force (self); return; } /* Modem reports being unregistered */ if (self->priv->reason_3gpp == CONNECTION_FORBIDDEN_REASON_UNREGISTERED) { /* If there is already a notification pending, just return */ if (self->priv->deferred_3gpp_unregistration_id) return; /* If the bearer is not connected, report right away */ if (self->priv->status != MM_BEARER_STATUS_CONNECTED) { mm_obj_dbg (self, "bearer not allowed to connect, not registered in 3GPP network"); mm_base_bearer_disconnect_force (self); return; } /* Otherwise, setup the new timeout */ mm_obj_dbg (self, "connected bearer not registered in 3GPP network"); self->priv->deferred_3gpp_unregistration_id = g_timeout_add_seconds (BEARER_DEFERRED_UNREGISTRATION_TIMEOUT, (GSourceFunc) deferred_3gpp_unregistration_cb, self); return; } g_assert_not_reached (); } static gboolean deferred_cdma_unregistration_cb (MMBaseBearer *self) { g_warn_if_fail (self->priv->reason_cdma == CONNECTION_FORBIDDEN_REASON_UNREGISTERED); self->priv->deferred_cdma_unregistration_id = 0; mm_obj_dbg (self, "forcing bearer disconnection, not registered in CDMA network"); mm_base_bearer_disconnect_force (self); return G_SOURCE_REMOVE; } static void modem_cdma_registration_state_changed (MMIfaceModemCdma *modem, GParamSpec *pspec, MMBaseBearer *self) { MMModemCdmaRegistrationState cdma1x_state = MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN; MMModemCdmaRegistrationState evdo_state = MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN; g_object_get (modem, MM_IFACE_MODEM_CDMA_CDMA1X_REGISTRATION_STATE, &cdma1x_state, MM_IFACE_MODEM_CDMA_EVDO_REGISTRATION_STATE, &evdo_state, NULL); if (cdma1x_state == MM_MODEM_CDMA_REGISTRATION_STATE_ROAMING || evdo_state == MM_MODEM_CDMA_REGISTRATION_STATE_ROAMING) { if (mm_bearer_properties_get_allow_roaming (mm_base_bearer_peek_config (self))) self->priv->reason_cdma = CONNECTION_FORBIDDEN_REASON_NONE; else self->priv->reason_cdma = CONNECTION_FORBIDDEN_REASON_ROAMING; } else if (cdma1x_state != MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN || evdo_state != MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN) { self->priv->reason_cdma = CONNECTION_FORBIDDEN_REASON_NONE; } else { self->priv->reason_cdma = CONNECTION_FORBIDDEN_REASON_UNREGISTERED; } /* If no reason to disconnect, or if it's a mixed CDMA+LTE modem without a 3GPP reason, * just don't do anything. */ if (self->priv->reason_cdma == CONNECTION_FORBIDDEN_REASON_NONE || (mm_iface_modem_is_3gpp (MM_IFACE_MODEM (modem)) && self->priv->reason_3gpp == CONNECTION_FORBIDDEN_REASON_NONE)) { reset_deferred_unregistration (self); return; } /* Modem is roaming and roaming not allowed, report right away */ if (self->priv->reason_cdma == CONNECTION_FORBIDDEN_REASON_ROAMING) { mm_obj_dbg (self, "bearer not allowed to connect, registered in roaming CDMA network"); reset_deferred_unregistration (self); mm_base_bearer_disconnect_force (self); return; } /* Modem reports being unregistered */ if (self->priv->reason_cdma == CONNECTION_FORBIDDEN_REASON_UNREGISTERED) { /* If there is already a notification pending, just return */ if (self->priv->deferred_cdma_unregistration_id) return; /* If the bearer is not connected, report right away */ if (self->priv->status != MM_BEARER_STATUS_CONNECTED) { mm_obj_dbg (self, "bearer not allowed to connect, not registered in CDMA network"); mm_base_bearer_disconnect_force (self); return; } /* Otherwise, setup the new timeout */ mm_obj_dbg (self, "connected bearer not registered in CDMA network"); self->priv->deferred_cdma_unregistration_id = g_timeout_add_seconds (BEARER_DEFERRED_UNREGISTRATION_TIMEOUT, (GSourceFunc) deferred_cdma_unregistration_cb, self); return; } g_assert_not_reached (); } static void set_signal_handlers (MMBaseBearer *self) { g_assert (self->priv->modem != NULL); g_assert (self->priv->config != NULL); /* Don't set the 3GPP registration change signal handlers if they * are already set. */ if (mm_iface_modem_is_3gpp (MM_IFACE_MODEM (self->priv->modem)) && !self->priv->id_3gpp_registration_change) { self->priv->id_3gpp_registration_change = g_signal_connect (self->priv->modem, "notify::" MM_IFACE_MODEM_3GPP_REGISTRATION_STATE, G_CALLBACK (modem_3gpp_registration_state_changed), self); modem_3gpp_registration_state_changed (MM_IFACE_MODEM_3GPP (self->priv->modem), NULL, self); } /* Don't set the CDMA1x/EV-DO registration change signal handlers if they * are already set. */ if (mm_iface_modem_is_cdma (MM_IFACE_MODEM (self->priv->modem)) && !self->priv->id_cdma1x_registration_change && !self->priv->id_evdo_registration_change) { self->priv->id_cdma1x_registration_change = g_signal_connect (self->priv->modem, "notify::" MM_IFACE_MODEM_CDMA_CDMA1X_REGISTRATION_STATE, G_CALLBACK (modem_cdma_registration_state_changed), self); self->priv->id_evdo_registration_change = g_signal_connect (self->priv->modem, "notify::" MM_IFACE_MODEM_CDMA_EVDO_REGISTRATION_STATE, G_CALLBACK (modem_cdma_registration_state_changed), self); modem_cdma_registration_state_changed (MM_IFACE_MODEM_CDMA (self->priv->modem), NULL, self); } } static void reset_signal_handlers (MMBaseBearer *self) { if (!self->priv->modem) return; if (self->priv->id_3gpp_registration_change) { if (g_signal_handler_is_connected (self->priv->modem, self->priv->id_3gpp_registration_change)) g_signal_handler_disconnect (self->priv->modem, self->priv->id_3gpp_registration_change); self->priv->id_3gpp_registration_change = 0; } if (self->priv->id_cdma1x_registration_change) { if (g_signal_handler_is_connected (self->priv->modem, self->priv->id_cdma1x_registration_change)) g_signal_handler_disconnect (self->priv->modem, self->priv->id_cdma1x_registration_change); self->priv->id_cdma1x_registration_change = 0; } if (self->priv->id_evdo_registration_change) { if (g_signal_handler_is_connected (self->priv->modem, self->priv->id_evdo_registration_change)) g_signal_handler_disconnect (self->priv->modem, self->priv->id_evdo_registration_change); self->priv->id_evdo_registration_change = 0; } } /*****************************************************************************/ /* CONNECT */ gboolean mm_base_bearer_connect_finish (MMBaseBearer *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void connect_succeeded (MMBaseBearer *self, GTask *task) { MMBearerConnectResult *result; result = g_task_get_task_data (task); /* Update bearer and interface status */ bearer_update_status_connected ( self, mm_port_get_device (mm_bearer_connect_result_peek_data (result)), mm_bearer_connect_result_get_multiplexed (result), mm_bearer_connect_result_get_profile_id (result), mm_bearer_connect_result_peek_ipv4_config (result), mm_bearer_connect_result_peek_ipv6_config (result), mm_bearer_connect_result_get_uplink_speed (result), mm_bearer_connect_result_get_downlink_speed (result)); g_clear_object (&self->priv->connect_cancellable); g_task_return_boolean (task, TRUE); g_object_unref (task); } static void disconnect_after_cancel_ready (MMBaseBearer *self, GAsyncResult *res) { g_autoptr(GError) error = NULL; if (!MM_BASE_BEARER_GET_CLASS (self)->disconnect_finish (self, res, &error)) mm_obj_warn (self, "error disconnecting: %s; will assume disconnected anyway", error->message); else mm_obj_dbg (self, "disconnected bearer '%s'", self->priv->path); /* Report disconnection to the bearer object using class method * mm_bearer_report_connection_status. This gives subclass implementations a * chance to correctly update their own connection state, in case this base * class ignores a failed disconnection attempt. */ mm_base_bearer_report_connection_status (self, MM_BEARER_CONNECTION_STATUS_DISCONNECTED); } static void connect_failed (MMBaseBearer *self, GTask *task, GError *error) { /* Update failed attempts */ mm_bearer_stats_set_failed_attempts (self->priv->stats, mm_bearer_stats_get_failed_attempts (self->priv->stats) + 1); bearer_update_interface_stats (self); /* Update reported connection error before the status update */ bearer_update_connection_error (self, error); if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) { bearer_update_status (self, MM_BEARER_STATUS_DISCONNECTING); MM_BASE_BEARER_GET_CLASS (self)->disconnect (self, (GAsyncReadyCallback)disconnect_after_cancel_ready, NULL); } else bearer_update_status (self, MM_BEARER_STATUS_DISCONNECTED); g_clear_object (&self->priv->connect_cancellable); g_task_return_error (task, error); g_object_unref (task); } static gboolean connect_check_cancel (MMBaseBearer *self, GTask *task) { GError *error = NULL; if (!g_cancellable_is_cancelled (self->priv->connect_cancellable)) return FALSE; mm_obj_dbg (self, "connected, but need to disconnect"); error = g_error_new (G_IO_ERROR, G_IO_ERROR_CANCELLED, "Bearer got connected, but had to disconnect after cancellation request"); connect_failed (self, task, error); return TRUE; } static void reload_stats_supported_ready (MMBaseBearer *self, GAsyncResult *res, GTask *task) { if (MM_BASE_BEARER_GET_CLASS (self)->reload_stats_finish (self, NULL, NULL, res, NULL)) { mm_obj_dbg (self, "reloading stats is supported by the device"); self->priv->reload_stats_supported = TRUE; mm_gdbus_bearer_set_reload_stats_supported (MM_GDBUS_BEARER (self), self->priv->reload_stats_supported); } else mm_obj_dbg (self, "reloading stats is not supported by the device"); if (connect_check_cancel (self, task)) return; connect_succeeded (self, task); } static void connect_ready (MMBaseBearer *self, GAsyncResult *res, GTask *task) { GError *error = NULL; g_autoptr(MMBearerConnectResult) result = NULL; /* NOTE: connect() implementations *MUST* handle cancellations themselves */ result = MM_BASE_BEARER_GET_CLASS (self)->connect_finish (self, res, &error); if (!result) { mm_obj_warn (self, "connection attempt #%u failed: %s", mm_bearer_stats_get_attempts (self->priv->stats), error->message); /* process profile manager updates right away on error */ if (mm_iface_modem_is_3gpp (MM_IFACE_MODEM (self->priv->modem))) mm_iface_modem_3gpp_profile_manager_update_ignore_stop (MM_IFACE_MODEM_3GPP_PROFILE_MANAGER (self->priv->modem)); connect_failed (self, task, error); return; } /* delay processing profile manager updates on success */ if (mm_iface_modem_is_3gpp (MM_IFACE_MODEM (self->priv->modem))) mm_iface_modem_3gpp_profile_manager_update_ignore_stop_delayed (MM_IFACE_MODEM_3GPP_PROFILE_MANAGER (self->priv->modem)); /* Handle cancellations detected after successful connection */ if (connect_check_cancel (self, task)) return; mm_obj_dbg (self, "connected"); g_task_set_task_data (task, g_steal_pointer (&result), (GDestroyNotify)mm_bearer_connect_result_unref); /* Check that reload statistics is supported by the device; we can only do this while * connected. */ if (MM_BASE_BEARER_GET_CLASS (self)->reload_stats && MM_BASE_BEARER_GET_CLASS (self)->reload_stats_finish) { MM_BASE_BEARER_GET_CLASS (self)->reload_stats ( self, (GAsyncReadyCallback)reload_stats_supported_ready, task); return; } connect_succeeded (self, task); } void mm_base_bearer_connect (MMBaseBearer *self, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; if (!MM_BASE_BEARER_GET_CLASS (self)->connect) { g_assert (!MM_BASE_BEARER_GET_CLASS (self)->connect_finish); g_task_report_new_error ( self, callback, user_data, mm_base_bearer_connect, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Bearer doesn't allow explicit connection requests"); return; } /* If already connecting, return error, don't allow a second request. */ if (self->priv->status == MM_BEARER_STATUS_CONNECTING) { g_task_report_new_error ( self, callback, user_data, mm_base_bearer_connect, MM_CORE_ERROR, MM_CORE_ERROR_IN_PROGRESS, "Bearer already being connected"); return; } /* If currently disconnecting, return error, previous operation should * finish before allowing to connect again. */ if (self->priv->status == MM_BEARER_STATUS_DISCONNECTING) { g_task_report_new_error ( self, callback, user_data, mm_base_bearer_connect, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Bearer currently being disconnected"); return; } /* Check 3GPP roaming allowance, *only* roaming related here */ if (mm_iface_modem_is_3gpp (MM_IFACE_MODEM (self->priv->modem)) && self->priv->reason_3gpp == CONNECTION_FORBIDDEN_REASON_ROAMING) { g_task_report_new_error ( self, callback, user_data, mm_base_bearer_connect, MM_CORE_ERROR, MM_CORE_ERROR_UNAUTHORIZED, "Not allowed to connect bearer in 3GPP network: '%s'", connection_forbidden_reason_str[self->priv->reason_3gpp]); return; } /* Check CDMA roaming allowance, *only* roaming related here */ if (mm_iface_modem_is_cdma (MM_IFACE_MODEM (self->priv->modem)) && self->priv->reason_cdma == CONNECTION_FORBIDDEN_REASON_ROAMING) { g_task_report_new_error ( self, callback, user_data, mm_base_bearer_connect, MM_CORE_ERROR, MM_CORE_ERROR_UNAUTHORIZED, "Not allowed to connect bearer in CDMA network: '%s'", connection_forbidden_reason_str[self->priv->reason_cdma]); return; } task = g_task_new (self, NULL, callback, user_data); /* If already connected, done */ if (self->priv->status == MM_BEARER_STATUS_CONNECTED) { g_task_return_boolean (task, TRUE); g_object_unref (task); return; } /* Update total attempts */ mm_bearer_stats_set_attempts (self->priv->stats, mm_bearer_stats_get_attempts (self->priv->stats) + 1); bearer_reset_ongoing_interface_stats (self); /* Clear previous connection error, if any */ bearer_update_connection_error (self, NULL); /* The connect request may imply a profile update internally, so ignore it */ if (mm_iface_modem_is_3gpp (MM_IFACE_MODEM (self->priv->modem))) mm_iface_modem_3gpp_profile_manager_update_ignore_start (MM_IFACE_MODEM_3GPP_PROFILE_MANAGER (self->priv->modem)); /* Connecting! */ mm_obj_dbg (self, "connecting..."); self->priv->connect_cancellable = g_cancellable_new (); bearer_update_status (self, MM_BEARER_STATUS_CONNECTING); MM_BASE_BEARER_GET_CLASS (self)->connect ( self, self->priv->connect_cancellable, (GAsyncReadyCallback)connect_ready, task); } typedef struct { MMBaseBearer *self; MMBaseModem *modem; GDBusMethodInvocation *invocation; } HandleConnectContext; static void handle_connect_context_free (HandleConnectContext *ctx) { g_object_unref (ctx->invocation); g_object_unref (ctx->modem); g_object_unref (ctx->self); g_free (ctx); } static void handle_connect_ready (MMBaseBearer *self, GAsyncResult *res, HandleConnectContext *ctx) { GError *error = NULL; if (!mm_base_bearer_connect_finish (self, res, &error)) mm_dbus_method_invocation_take_error (ctx->invocation, error); else mm_gdbus_bearer_complete_connect (MM_GDBUS_BEARER (self), ctx->invocation); handle_connect_context_free (ctx); } static void handle_connect_auth_ready (MMBaseModem *modem, GAsyncResult *res, HandleConnectContext *ctx) { GError *error = NULL; if (!mm_base_modem_authorize_finish (modem, res, &error)) { mm_dbus_method_invocation_take_error (ctx->invocation, error); handle_connect_context_free (ctx); return; } mm_base_bearer_connect (ctx->self, (GAsyncReadyCallback)handle_connect_ready, ctx); } static gboolean handle_connect (MMBaseBearer *self, GDBusMethodInvocation *invocation) { HandleConnectContext *ctx; ctx = g_new0 (HandleConnectContext, 1); ctx->self = g_object_ref (self); ctx->invocation = g_object_ref (invocation); g_object_get (self, MM_BASE_BEARER_MODEM, &ctx->modem, NULL); mm_obj_dbg (self, "user request to connect"); mm_base_modem_authorize (ctx->modem, invocation, MM_AUTHORIZATION_DEVICE_CONTROL, (GAsyncReadyCallback)handle_connect_auth_ready, ctx); return TRUE; } /*****************************************************************************/ /* DISCONNECT */ gboolean mm_base_bearer_disconnect_finish (MMBaseBearer *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void disconnect_ready (MMBaseBearer *self, GAsyncResult *res, GTask *task) { GError *error = NULL; if (!MM_BASE_BEARER_GET_CLASS (self)->disconnect_finish (self, res, &error)) { mm_obj_dbg (self, "couldn't disconnect: %s", error->message); bearer_update_status (self, MM_BEARER_STATUS_CONNECTED); g_task_return_error (task, error); } else { mm_obj_dbg (self, "disconnected"); bearer_update_status (self, MM_BEARER_STATUS_DISCONNECTED); g_task_return_boolean (task, TRUE); } g_object_unref (task); } static void status_changed_complete_disconnect (MMBaseBearer *self, GParamSpec *pspec, GTask *task) { /* We may get other states here before DISCONNECTED, like DISCONNECTING or * even CONNECTED. */ if (self->priv->status != MM_BEARER_STATUS_DISCONNECTED) return; mm_obj_dbg (self, "disconnected after cancelling previous connect request"); g_signal_handler_disconnect (self, self->priv->disconnect_signal_handler); self->priv->disconnect_signal_handler = 0; /* Note: interface state is updated when the DISCONNECTED state is set */ g_task_return_boolean (task, TRUE); g_object_unref (task); } void mm_base_bearer_disconnect (MMBaseBearer *self, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; task = g_task_new (self, NULL, callback, user_data); if (!MM_BASE_BEARER_GET_CLASS (self)->disconnect) { g_assert (!MM_BASE_BEARER_GET_CLASS (self)->disconnect_finish); g_task_return_new_error ( task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Bearer doesn't allow explicit disconnection requests"); g_object_unref (task); return; } /* If already disconnected, done */ if (self->priv->status == MM_BEARER_STATUS_DISCONNECTED) { g_task_return_boolean (task, TRUE); g_object_unref (task); return; } /* If already disconnecting, return error, don't allow a second request. */ if (self->priv->status == MM_BEARER_STATUS_DISCONNECTING) { g_task_return_new_error ( task, MM_CORE_ERROR, MM_CORE_ERROR_IN_PROGRESS, "Bearer already being disconnected"); g_object_unref (task); return; } mm_obj_dbg (self, "disconnecting..."); /* If currently connecting, try to cancel that operation, and wait to get * disconnected. */ if (self->priv->status == MM_BEARER_STATUS_CONNECTING) { /* Set ourselves as disconnecting */ bearer_update_status (self, MM_BEARER_STATUS_DISCONNECTING); /* We MUST ensure that we get to DISCONNECTED */ g_cancellable_cancel (self->priv->connect_cancellable); /* Note that we only allow to remove disconnected bearers, so should * be safe to assume that we'll get the signal handler called properly */ self->priv->disconnect_signal_handler = g_signal_connect (self, "notify::" MM_BASE_BEARER_STATUS, (GCallback)status_changed_complete_disconnect, task); /* takes ownership */ return; } /* Disconnecting! */ bearer_update_status (self, MM_BEARER_STATUS_DISCONNECTING); MM_BASE_BEARER_GET_CLASS (self)->disconnect ( self, (GAsyncReadyCallback)disconnect_ready, task); /* takes ownership */ } typedef struct { MMBaseBearer *self; MMBaseModem *modem; GDBusMethodInvocation *invocation; } HandleDisconnectContext; static void handle_disconnect_context_free (HandleDisconnectContext *ctx) { g_object_unref (ctx->invocation); g_object_unref (ctx->modem); g_object_unref (ctx->self); g_free (ctx); } static void handle_disconnect_ready (MMBaseBearer *self, GAsyncResult *res, HandleDisconnectContext *ctx) { GError *error = NULL; if (!mm_base_bearer_disconnect_finish (self, res, &error)) mm_dbus_method_invocation_take_error (ctx->invocation, error); else mm_gdbus_bearer_complete_disconnect (MM_GDBUS_BEARER (self), ctx->invocation); handle_disconnect_context_free (ctx); } static void handle_disconnect_auth_ready (MMBaseModem *modem, GAsyncResult *res, HandleDisconnectContext *ctx) { GError *error = NULL; if (!mm_base_modem_authorize_finish (modem, res, &error)) { mm_dbus_method_invocation_take_error (ctx->invocation, error); handle_disconnect_context_free (ctx); return; } mm_base_bearer_disconnect (ctx->self, (GAsyncReadyCallback)handle_disconnect_ready, ctx); } static gboolean handle_disconnect (MMBaseBearer *self, GDBusMethodInvocation *invocation) { HandleDisconnectContext *ctx; ctx = g_new0 (HandleDisconnectContext, 1); ctx->self = g_object_ref (self); ctx->invocation = g_object_ref (invocation); g_object_get (self, MM_BASE_BEARER_MODEM, &ctx->modem, NULL); mm_obj_dbg (self, "user request to disconnect"); mm_base_modem_authorize (ctx->modem, invocation, MM_AUTHORIZATION_DEVICE_CONTROL, (GAsyncReadyCallback)handle_disconnect_auth_ready, ctx); return TRUE; } /*****************************************************************************/ static void base_bearer_dbus_export (MMBaseBearer *self) { GError *error = NULL; /* Handle method invocations */ g_signal_connect (self, "handle-connect", G_CALLBACK (handle_connect), NULL); g_signal_connect (self, "handle-disconnect", G_CALLBACK (handle_disconnect), NULL); if (!g_dbus_interface_skeleton_export (G_DBUS_INTERFACE_SKELETON (self), self->priv->connection, self->priv->path, &error)) { mm_obj_warn (self, "couldn't export to bus: %s", error->message); g_error_free (error); } } static void base_bearer_dbus_unexport (MMBaseBearer *self) { const gchar *path; path = g_dbus_interface_skeleton_get_object_path (G_DBUS_INTERFACE_SKELETON (self)); /* Only unexport if currently exported */ if (path) { mm_obj_dbg (self, "removing from bus"); g_dbus_interface_skeleton_unexport (G_DBUS_INTERFACE_SKELETON (self)); } } /*****************************************************************************/ MMBearerStatus mm_base_bearer_get_status (MMBaseBearer *self) { return self->priv->status; } const gchar * mm_base_bearer_get_path (MMBaseBearer *self) { return self->priv->path; } MMBearerProperties * mm_base_bearer_peek_config (MMBaseBearer *self) { return self->priv->config; } MMBearerProperties * mm_base_bearer_get_config (MMBaseBearer *self) { return (self->priv->config ? g_object_ref (self->priv->config) : NULL); } gint mm_base_bearer_get_profile_id (MMBaseBearer *self) { return mm_gdbus_bearer_get_profile_id (MM_GDBUS_BEARER (self)); } MMBearerApnType mm_base_bearer_get_apn_type (MMBaseBearer *self) { /* when none explicitly requested, apn type always defaults to internet */ return (self->priv->config ? mm_bearer_properties_get_apn_type (self->priv->config) : MM_BEARER_APN_TYPE_DEFAULT); } /*****************************************************************************/ static void disconnect_force_ready (MMBaseBearer *self, GAsyncResult *res) { GError *error = NULL; if (!MM_BASE_BEARER_GET_CLASS (self)->disconnect_finish (self, res, &error)) { mm_obj_warn (self, "error disconnecting: %s; will assume disconnected anyway", error->message); g_error_free (error); } else mm_obj_dbg (self, "disconnected"); /* Report disconnection to the bearer object using class method * mm_bearer_report_connection_status. This gives subclass implementations a * chance to correctly update their own connection state, in case this base * class ignores a failed disconnection attempt. */ mm_base_bearer_report_connection_status (self, MM_BEARER_CONNECTION_STATUS_DISCONNECTED); } void mm_base_bearer_disconnect_force (MMBaseBearer *self) { if (self->priv->status == MM_BEARER_STATUS_DISCONNECTING || self->priv->status == MM_BEARER_STATUS_DISCONNECTED) return; if (self->priv->ignore_disconnection_reports) { mm_obj_dbg (self, "disconnection should be forced but it's explicitly ignored"); return; } mm_obj_dbg (self, "forcing disconnection"); /* If currently connecting, try to cancel that operation. */ if (self->priv->status == MM_BEARER_STATUS_CONNECTING) { g_cancellable_cancel (self->priv->connect_cancellable); return; } /* Disconnecting! */ bearer_update_status (self, MM_BEARER_STATUS_DISCONNECTING); MM_BASE_BEARER_GET_CLASS (self)->disconnect ( self, (GAsyncReadyCallback)disconnect_force_ready, NULL); } /*****************************************************************************/ static void report_connection_status (MMBaseBearer *self, MMBearerConnectionStatus status, const GError *connection_error) { /* The only status expected at this point is DISCONNECTED or CONNECTED, * although here we just process the DISCONNECTED one. */ g_assert (status == MM_BEARER_CONNECTION_STATUS_CONNECTED || status == MM_BEARER_CONNECTION_STATUS_DISCONNECTED); /* In the generic bearer implementation we just need to reset the * interface status */ if (status == MM_BEARER_CONNECTION_STATUS_DISCONNECTED) { bearer_update_connection_error (self, connection_error); bearer_update_status (self, MM_BEARER_STATUS_DISCONNECTED); } } /* * This method is used exclusively in two different scenarios: * a) to report disconnections detected by ModemManager itself (e.g. based on * CGACT polling or CGEV URCs), applicable to bearers using both NET and * PPP data ports. * b) to report failed or successful connection attempts by plugins using NET * data ports that rely on vendor-specific URCs (e.g. Icera, MBM, Option * HSO). * * The method is also subclass-able because plugins may require specific * cleanup operations to be done when a bearer is reported as disconnected. * (e.g. the QMI or MBIM implementations require removing signal handlers). * * For all the scenarios involving a) the plugins are required to call the * parent report_connection_status() implementation to report the * DISCONNECTED state. For scenarios involving b) the parent reporting is not * expected at all. In other words, the parent report_connection_status() * is exclusively used in processing disconnections detected by ModemManager * itself. * * If the bearer has been connected and it has required PPP method, we will * ignore all disconnection reports because we cannot disconnect a PPP-based * bearer before the upper layers have stopped using the TTY. In this case, * we must wait for upper layers to detect the disconnection themselves (e.g. * pppd should detect it) and disconnect the bearer through DBus. */ void mm_base_bearer_report_connection_status_detailed (MMBaseBearer *self, MMBearerConnectionStatus status, const GError *connection_error) { /* Reporting disconnection? */ if (status == MM_BEARER_CONNECTION_STATUS_DISCONNECTED || status == MM_BEARER_CONNECTION_STATUS_CONNECTION_FAILED) { if (self->priv->ignore_disconnection_reports) { mm_obj_dbg (self, "ignoring disconnection report"); return; } /* Setup a generic default error if none explicitly given when reporting * bearer disconnections. */ if (!connection_error) { g_autoptr(GError) default_connection_error = NULL; default_connection_error = mm_mobile_equipment_error_for_code (MM_MOBILE_EQUIPMENT_ERROR_UNKNOWN, self); return MM_BASE_BEARER_GET_CLASS (self)->report_connection_status (self, status, default_connection_error); } } return MM_BASE_BEARER_GET_CLASS (self)->report_connection_status (self, status, connection_error); } /*****************************************************************************/ #if defined WITH_SUSPEND_RESUME typedef struct _SyncingContext SyncingContext; static void interface_syncing_step (GTask *task); typedef enum { SYNCING_STEP_FIRST, SYNCING_STEP_REFRESH_CONNECTION, SYNCING_STEP_LAST } SyncingStep; struct _SyncingContext { SyncingStep step; MMBearerStatus status; }; gboolean mm_base_bearer_sync_finish (MMBaseBearer *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void reload_connection_status_ready (MMBaseBearer *self, GAsyncResult *res, GTask *task) { SyncingContext *ctx; MMBearerConnectionStatus reloaded_status; g_autoptr(GError) error = NULL; ctx = g_task_get_task_data (task); /* The only update we're really interested in is the connected->disconnected * one, because any other would be extremely strange and it's probably not * worth trying to support those; e.g. a disconnected->connected change here * would be impossible to be handled correctly. We'll also ignore intermediate * states (connecting/disconnecting), as we can rely on the reports of the final * state at some point soon. * * So, just handle DISCONNECTED at this point. */ reloaded_status = MM_BASE_BEARER_GET_CLASS (self)->reload_connection_status_finish (self, res, &error); if (reloaded_status == MM_BEARER_CONNECTION_STATUS_UNKNOWN) mm_obj_warn (self, "reloading connection status failed: %s", error->message); else if ((ctx->status == MM_BEARER_STATUS_CONNECTED) && (reloaded_status == MM_BEARER_CONNECTION_STATUS_DISCONNECTED)) { mm_obj_dbg (self, "disconnection detected during status synchronization"); mm_base_bearer_report_connection_status (self, reloaded_status); } /* Go on to the next step */ ctx->step++; interface_syncing_step (task); } static void interface_syncing_step (GTask *task) { MMBaseBearer *self; SyncingContext *ctx; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); switch (ctx->step) { case SYNCING_STEP_FIRST: ctx->step++; /* fall through */ case SYNCING_STEP_REFRESH_CONNECTION: /* * AT+PPP based connections should not be synced. * When a AT+PPP connection bearer is connected, the 'ignore_disconnection_reports' flag is set. */ if (!self->priv->ignore_disconnection_reports) { if (!MM_BASE_BEARER_GET_CLASS (self)->reload_connection_status) mm_obj_warn (self, "unable to reload connection status, method not implemented"); else { mm_obj_dbg (self, "refreshing connection status"); MM_BASE_BEARER_GET_CLASS (self)->reload_connection_status (self, (GAsyncReadyCallback) reload_connection_status_ready, task); return; } } ctx->step++; /* fall through */ case SYNCING_STEP_LAST: /* We are done without errors! */ g_task_return_boolean (task, TRUE); g_object_unref (task); return; default: break; } g_assert_not_reached (); } void mm_base_bearer_sync (MMBaseBearer *self, GAsyncReadyCallback callback, gpointer user_data) { SyncingContext *ctx; GTask *task; /* Create SyncingContext and store the original bearer status */ ctx = g_new0 (SyncingContext, 1); ctx->step = SYNCING_STEP_FIRST; ctx->status = self->priv->status; /* Create sync steps task and execute it */ task = g_task_new (self, NULL, callback, user_data); g_task_set_task_data (task, ctx, (GDestroyNotify)g_free); interface_syncing_step (task); } #endif /*****************************************************************************/ static gchar * log_object_build_id (MMLogObject *_self) { MMBaseBearer *self; self = MM_BASE_BEARER (_self); return g_strdup_printf ("bearer%u", self->priv->dbus_id); } /*****************************************************************************/ static void set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { MMBaseBearer *self = MM_BASE_BEARER (object); switch (prop_id) { case PROP_PATH: g_free (self->priv->path); self->priv->path = g_value_dup_string (value); /* Export when we get a DBus connection AND we have a path */ if (self->priv->path && self->priv->connection) base_bearer_dbus_export (self); break; case PROP_CONNECTION: g_clear_object (&self->priv->connection); self->priv->connection = g_value_dup_object (value); /* Export when we get a DBus connection AND we have a path */ if (!self->priv->connection) base_bearer_dbus_unexport (self); else if (self->priv->path) base_bearer_dbus_export (self); break; case PROP_MODEM: g_clear_object (&self->priv->modem); self->priv->modem = g_value_dup_object (value); if (self->priv->modem) { /* Set owner ID */ mm_log_object_set_owner_id (MM_LOG_OBJECT (self), mm_log_object_get_id (MM_LOG_OBJECT (self->priv->modem))); /* Bind the modem's connection (which is set when it is exported, * and unset when unexported) to the BEARER's connection */ g_object_bind_property (self->priv->modem, MM_BASE_MODEM_CONNECTION, self, MM_BASE_BEARER_CONNECTION, G_BINDING_DEFAULT | G_BINDING_SYNC_CREATE); if (self->priv->config) { /* Listen to 3GPP/CDMA registration state changes. We need both * 'config' and 'modem' set. */ set_signal_handlers (self); } } break; case PROP_STATUS: /* We don't allow g_object_set()-ing the status property */ g_assert_not_reached (); break; case PROP_CONFIG: { GVariant *dictionary; g_clear_object (&self->priv->config); self->priv->config = g_value_dup_object (value); if (self->priv->modem) { /* Listen to 3GPP/CDMA registration state changes. We need both * 'config' and 'modem' set. */ set_signal_handlers (self); } /* Also expose the properties */ dictionary = mm_bearer_properties_get_dictionary (self->priv->config); mm_gdbus_bearer_set_properties (MM_GDBUS_BEARER (self), dictionary); if (dictionary) g_variant_unref (dictionary); 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) { MMBaseBearer *self = MM_BASE_BEARER (object); switch (prop_id) { case PROP_PATH: g_value_set_string (value, self->priv->path); break; case PROP_CONNECTION: g_value_set_object (value, self->priv->connection); break; case PROP_MODEM: g_value_set_object (value, self->priv->modem); break; case PROP_STATUS: g_value_set_enum (value, self->priv->status); break; case PROP_CONFIG: g_value_set_object (value, self->priv->config); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void mm_base_bearer_init (MMBaseBearer *self) { static guint id = 0; /* Initialize private data */ self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, MM_TYPE_BASE_BEARER, MMBaseBearerPrivate); /* Each bearer is given a unique id to build its own DBus path */ self->priv->dbus_id = id++; self->priv->status = MM_BEARER_STATUS_DISCONNECTED; self->priv->reason_3gpp = CONNECTION_FORBIDDEN_REASON_NONE; self->priv->reason_cdma = CONNECTION_FORBIDDEN_REASON_NONE; self->priv->reload_stats_supported = FALSE; self->priv->stats = mm_bearer_stats_new (); /* Set defaults */ mm_gdbus_bearer_set_interface (MM_GDBUS_BEARER (self), NULL); mm_gdbus_bearer_set_multiplexed (MM_GDBUS_BEARER (self), FALSE); mm_gdbus_bearer_set_profile_id (MM_GDBUS_BEARER (self), MM_3GPP_PROFILE_ID_UNKNOWN); mm_gdbus_bearer_set_connected (MM_GDBUS_BEARER (self), FALSE); mm_gdbus_bearer_set_suspended (MM_GDBUS_BEARER (self), FALSE); mm_gdbus_bearer_set_properties (MM_GDBUS_BEARER (self), NULL); mm_gdbus_bearer_set_ip_timeout (MM_GDBUS_BEARER (self), BEARER_IP_TIMEOUT_DEFAULT); mm_gdbus_bearer_set_bearer_type (MM_GDBUS_BEARER (self), MM_BEARER_TYPE_DEFAULT); mm_gdbus_bearer_set_ip4_config (MM_GDBUS_BEARER (self), mm_bearer_ip_config_get_dictionary (NULL)); mm_gdbus_bearer_set_ip6_config (MM_GDBUS_BEARER (self), mm_bearer_ip_config_get_dictionary (NULL)); bearer_update_interface_stats (self); } static void finalize (GObject *object) { MMBaseBearer *self = MM_BASE_BEARER (object); g_free (self->priv->path); G_OBJECT_CLASS (mm_base_bearer_parent_class)->finalize (object); } static void dispose (GObject *object) { MMBaseBearer *self = MM_BASE_BEARER (object); connection_monitor_stop (self); bearer_stats_stop (self); g_clear_object (&self->priv->stats); if (self->priv->connection) { base_bearer_dbus_unexport (self); g_clear_object (&self->priv->connection); } reset_signal_handlers (self); reset_deferred_unregistration (self); g_clear_object (&self->priv->modem); g_clear_object (&self->priv->config); G_OBJECT_CLASS (mm_base_bearer_parent_class)->dispose (object); } static void log_object_iface_init (MMLogObjectInterface *iface) { iface->build_id = log_object_build_id; } static void mm_base_bearer_class_init (MMBaseBearerClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); g_type_class_add_private (object_class, sizeof (MMBaseBearerPrivate)); /* Virtual methods */ object_class->get_property = get_property; object_class->set_property = set_property; object_class->finalize = finalize; object_class->dispose = dispose; klass->report_connection_status = report_connection_status; properties[PROP_CONNECTION] = g_param_spec_object (MM_BASE_BEARER_CONNECTION, "Connection", "GDBus connection to the system bus.", G_TYPE_DBUS_CONNECTION, G_PARAM_READWRITE); g_object_class_install_property (object_class, PROP_CONNECTION, properties[PROP_CONNECTION]); properties[PROP_PATH] = g_param_spec_string (MM_BASE_BEARER_PATH, "Path", "DBus path of the Bearer", NULL, G_PARAM_READWRITE); g_object_class_install_property (object_class, PROP_PATH, properties[PROP_PATH]); properties[PROP_MODEM] = g_param_spec_object (MM_BASE_BEARER_MODEM, "Modem", "The Modem which owns this Bearer", MM_TYPE_BASE_MODEM, G_PARAM_READWRITE); g_object_class_install_property (object_class, PROP_MODEM, properties[PROP_MODEM]); properties[PROP_STATUS] = g_param_spec_enum (MM_BASE_BEARER_STATUS, "Bearer status", "Status of the bearer", MM_TYPE_BEARER_STATUS, MM_BEARER_STATUS_DISCONNECTED, G_PARAM_READABLE); g_object_class_install_property (object_class, PROP_STATUS, properties[PROP_STATUS]); properties[PROP_CONFIG] = g_param_spec_object (MM_BASE_BEARER_CONFIG, "Bearer configuration", "List of user provided properties", MM_TYPE_BEARER_PROPERTIES, G_PARAM_READWRITE); g_object_class_install_property (object_class, PROP_CONFIG, properties[PROP_CONFIG]); } /*****************************************************************************/ /* Helpers to implement connect() */ struct _MMBearerConnectResult { volatile gint ref_count; MMPort *data; MMBearerIpConfig *ipv4_config; MMBearerIpConfig *ipv6_config; gboolean multiplexed; gint profile_id; guint64 uplink_speed; guint64 downlink_speed; }; MMBearerConnectResult * mm_bearer_connect_result_ref (MMBearerConnectResult *result) { g_atomic_int_inc (&result->ref_count); return result; } void mm_bearer_connect_result_unref (MMBearerConnectResult *result) { if (g_atomic_int_dec_and_test (&result->ref_count)) { if (result->ipv4_config) g_object_unref (result->ipv4_config); if (result->ipv6_config) g_object_unref (result->ipv6_config); if (result->data) g_object_unref (result->data); g_slice_free (MMBearerConnectResult, result); } } MMPort * mm_bearer_connect_result_peek_data (MMBearerConnectResult *result) { return result->data; } MMBearerIpConfig * mm_bearer_connect_result_peek_ipv4_config (MMBearerConnectResult *result) { return result->ipv4_config; } MMBearerIpConfig * mm_bearer_connect_result_peek_ipv6_config (MMBearerConnectResult *result) { return result->ipv6_config; } void mm_bearer_connect_result_set_multiplexed (MMBearerConnectResult *result, gboolean multiplexed) { result->multiplexed = multiplexed; } gboolean mm_bearer_connect_result_get_multiplexed (MMBearerConnectResult *result) { return result->multiplexed; } void mm_bearer_connect_result_set_profile_id (MMBearerConnectResult *result, gint profile_id) { result->profile_id = profile_id; } gint mm_bearer_connect_result_get_profile_id (MMBearerConnectResult *result) { return result->profile_id; } void mm_bearer_connect_result_set_uplink_speed (MMBearerConnectResult *result, guint64 speed) { result->uplink_speed = speed; } guint64 mm_bearer_connect_result_get_uplink_speed (MMBearerConnectResult *result) { return result->uplink_speed; } void mm_bearer_connect_result_set_downlink_speed (MMBearerConnectResult *result, guint64 speed) { result->downlink_speed = speed; } guint64 mm_bearer_connect_result_get_downlink_speed (MMBearerConnectResult *result) { return result->downlink_speed; } MMBearerConnectResult * mm_bearer_connect_result_new (MMPort *data, MMBearerIpConfig *ipv4_config, MMBearerIpConfig *ipv6_config) { MMBearerConnectResult *result; /* 'data' must always be given */ g_assert (MM_IS_PORT (data)); result = g_slice_new0 (MMBearerConnectResult); result->ref_count = 1; result->data = g_object_ref (data); if (ipv4_config) result->ipv4_config = g_object_ref (ipv4_config); if (ipv6_config) result->ipv6_config = g_object_ref (ipv6_config); result->multiplexed = FALSE; /* default */ result->profile_id = MM_3GPP_PROFILE_ID_UNKNOWN; return result; } ModemManager-1.23.4-dev/src/mm-base-bearer.h000066400000000000000000000264161456466623000204550ustar00rootroot00000000000000/* -*- 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) 2011 Google, Inc. * Copyright (C) 2015 Azimut Electronics * Copyright (C) 2011 - 2015 Aleksander Morgado */ #ifndef MM_BASE_BEARER_H #define MM_BASE_BEARER_H #include #include #include #define _LIBMM_INSIDE_MM #include #include "mm-base-modem.h" /*****************************************************************************/ /* Helpers to implement connect() */ typedef struct _MMBearerConnectResult MMBearerConnectResult; MMBearerConnectResult *mm_bearer_connect_result_new (MMPort *data, MMBearerIpConfig *ipv4_config, MMBearerIpConfig *ipv6_config); void mm_bearer_connect_result_unref (MMBearerConnectResult *result); MMBearerConnectResult *mm_bearer_connect_result_ref (MMBearerConnectResult *result); G_DEFINE_AUTOPTR_CLEANUP_FUNC (MMBearerConnectResult, mm_bearer_connect_result_unref) MMPort *mm_bearer_connect_result_peek_data (MMBearerConnectResult *result); MMBearerIpConfig *mm_bearer_connect_result_peek_ipv4_config (MMBearerConnectResult *result); MMBearerIpConfig *mm_bearer_connect_result_peek_ipv6_config (MMBearerConnectResult *result); /* by default, if none specified, multiplexed=FALSE */ void mm_bearer_connect_result_set_multiplexed (MMBearerConnectResult *result, gboolean multiplexed); gboolean mm_bearer_connect_result_get_multiplexed (MMBearerConnectResult *result); /* profile id, if known */ void mm_bearer_connect_result_set_profile_id (MMBearerConnectResult *result, gint profile_id); gint mm_bearer_connect_result_get_profile_id (MMBearerConnectResult *result); /* speed, for stats */ void mm_bearer_connect_result_set_uplink_speed (MMBearerConnectResult *result, guint64 speed); guint64 mm_bearer_connect_result_get_uplink_speed (MMBearerConnectResult *result); void mm_bearer_connect_result_set_downlink_speed (MMBearerConnectResult *result, guint64 speed); guint64 mm_bearer_connect_result_get_downlink_speed (MMBearerConnectResult *result); /*****************************************************************************/ /* Default timeout values to be used in the steps of a connection or * disconnection attempt that may take long to complete. Note that the actual * connection attempt from the user may have a different timeout, but we don't * really fully care about that, it's a problem to consider in the user side. * In the daemon itself, what we want and require is to be in sync with the * state of the modem. */ #define MM_BASE_BEARER_DEFAULT_CONNECTION_TIMEOUT 180 #define MM_BASE_BEARER_DEFAULT_DISCONNECTION_TIMEOUT 120 /*****************************************************************************/ #define MM_TYPE_BASE_BEARER (mm_base_bearer_get_type ()) #define MM_BASE_BEARER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_BASE_BEARER, MMBaseBearer)) #define MM_BASE_BEARER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_BASE_BEARER, MMBaseBearerClass)) #define MM_IS_BASE_BEARER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_BASE_BEARER)) #define MM_IS_BASE_BEARER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_BASE_BEARER)) #define MM_BASE_BEARER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_BASE_BEARER, MMBaseBearerClass)) typedef struct _MMBaseBearer MMBaseBearer; typedef struct _MMBaseBearerClass MMBaseBearerClass; typedef struct _MMBaseBearerPrivate MMBaseBearerPrivate; #define MM_BASE_BEARER_PATH "bearer-path" #define MM_BASE_BEARER_CONNECTION "bearer-connection" #define MM_BASE_BEARER_MODEM "bearer-modem" #define MM_BASE_BEARER_STATUS "bearer-status" #define MM_BASE_BEARER_CONFIG "bearer-config" typedef enum { /*< underscore_name=mm_bearer_status >*/ MM_BEARER_STATUS_DISCONNECTED, MM_BEARER_STATUS_DISCONNECTING, MM_BEARER_STATUS_CONNECTING, MM_BEARER_STATUS_CONNECTED, } MMBearerStatus; typedef enum { /*< underscore_name=mm_bearer_connection_status >*/ MM_BEARER_CONNECTION_STATUS_UNKNOWN, MM_BEARER_CONNECTION_STATUS_DISCONNECTED, MM_BEARER_CONNECTION_STATUS_DISCONNECTING, MM_BEARER_CONNECTION_STATUS_CONNECTED, MM_BEARER_CONNECTION_STATUS_CONNECTION_FAILED, } MMBearerConnectionStatus; struct _MMBaseBearer { MmGdbusBearerSkeleton parent; MMBaseBearerPrivate *priv; }; struct _MMBaseBearerClass { MmGdbusBearerSkeletonClass parent; /* Connect this bearer */ void (* connect) (MMBaseBearer *bearer, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); MMBearerConnectResult * (* connect_finish) (MMBaseBearer *bearer, GAsyncResult *res, GError **error); /* Disconnect this bearer */ void (* disconnect) (MMBaseBearer *bearer, GAsyncReadyCallback callback, gpointer user_data); gboolean (* disconnect_finish) (MMBaseBearer *bearer, GAsyncResult *res, GError **error); /* Monitor connection status: * * Only CONNECTED or DISCONNECTED should be reported here; this method * is used to poll for connection status once the connection has been * established. * * This method will return MM_CORE_ERROR_UNSUPPORTED if the polling * is not required (i.e. if we can safely rely on async indications * sent by the modem). */ void (* load_connection_status) (MMBaseBearer *bearer, GAsyncReadyCallback callback, gpointer user_data); MMBearerConnectionStatus (* load_connection_status_finish) (MMBaseBearer *bearer, GAsyncResult *res, GError **error); #if defined WITH_SUSPEND_RESUME /* Reload connection status: * * This method should return the exact connection status of the bearer, and * the check must always be performed (if supported). This method should not * return MM_CORE_ERROR_UNSUPPORTED as a way to skip the operation, as in * this case the connection monitoring is required during the quick * suspend/resume synchronization. * * It is up to each protocol/plugin whether providing the same method here * and in load_connection_status() makes sense. */ void (* reload_connection_status) (MMBaseBearer *bearer, GAsyncReadyCallback callback, gpointer user_data); MMBearerConnectionStatus (* reload_connection_status_finish) (MMBaseBearer *bearer, GAsyncResult *res, GError **error); #endif /* Reload statistics */ void (* reload_stats) (MMBaseBearer *bearer, GAsyncReadyCallback callback, gpointer user_data); gboolean (* reload_stats_finish) (MMBaseBearer *bearer, guint64 *bytes_rx, guint64 *bytes_tx, GAsyncResult *res, GError **error); /* Report connection status of this bearer */ void (* report_connection_status) (MMBaseBearer *bearer, MMBearerConnectionStatus status, const GError *connection_error); }; GType mm_base_bearer_get_type (void); G_DEFINE_AUTOPTR_CLEANUP_FUNC (MMBaseBearer, g_object_unref) void mm_base_bearer_export (MMBaseBearer *self); const gchar *mm_base_bearer_get_path (MMBaseBearer *self); MMBearerStatus mm_base_bearer_get_status (MMBaseBearer *self); MMBearerProperties *mm_base_bearer_peek_config (MMBaseBearer *self); MMBearerProperties *mm_base_bearer_get_config (MMBaseBearer *self); gint mm_base_bearer_get_profile_id (MMBaseBearer *self); MMBearerApnType mm_base_bearer_get_apn_type (MMBaseBearer *self); void mm_base_bearer_connect (MMBaseBearer *self, GAsyncReadyCallback callback, gpointer user_data); gboolean mm_base_bearer_connect_finish (MMBaseBearer *self, GAsyncResult *res, GError **error); void mm_base_bearer_disconnect (MMBaseBearer *self, GAsyncReadyCallback callback, gpointer user_data); gboolean mm_base_bearer_disconnect_finish (MMBaseBearer *self, GAsyncResult *res, GError **error); void mm_base_bearer_disconnect_force (MMBaseBearer *self); void mm_base_bearer_report_connection_status_detailed (MMBaseBearer *self, MMBearerConnectionStatus status, const GError *connection_error); /* When unknown, just pass NULL */ #define mm_base_bearer_report_connection_status(self, status) mm_base_bearer_report_connection_status_detailed (self, status, NULL) void mm_base_bearer_report_speeds (MMBaseBearer *self, guint64 uplink_speed, guint64 downlink_speed); #if defined WITH_SUSPEND_RESUME /* Sync Broadband Bearer (async) */ void mm_base_bearer_sync (MMBaseBearer *self, GAsyncReadyCallback callback, gpointer user_data); gboolean mm_base_bearer_sync_finish (MMBaseBearer *self, GAsyncResult *res, GError **error); #endif #endif /* MM_BASE_BEARER_H */ ModemManager-1.23.4-dev/src/mm-base-call.c000066400000000000000000001474241456466623000201260ustar00rootroot00000000000000/* -*- 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) 2015 Riccardo Vangelisti * Copyright (C) 2019 Aleksander Morgado * Copyright (C) 2019 Purism SPC */ #include #include #include #include #include #include #include #define _LIBMM_INSIDE_MM #include #include "mm-base-call.h" #include "mm-broadband-modem.h" #include "mm-iface-modem.h" #include "mm-iface-modem-voice.h" #include "mm-base-modem-at.h" #include "mm-base-modem.h" #include "mm-log-object.h" #include "mm-modem-helpers.h" #include "mm-error-helpers.h" static void log_object_iface_init (MMLogObjectInterface *iface); G_DEFINE_TYPE_EXTENDED (MMBaseCall, mm_base_call, MM_GDBUS_TYPE_CALL_SKELETON, 0, G_IMPLEMENT_INTERFACE (MM_TYPE_LOG_OBJECT, log_object_iface_init)) enum { PROP_0, PROP_PATH, PROP_CONNECTION, PROP_MODEM, PROP_SKIP_INCOMING_TIMEOUT, PROP_SUPPORTS_DIALING_TO_RINGING, PROP_SUPPORTS_RINGING_TO_ACTIVE, PROP_LAST }; static GParamSpec *properties[PROP_LAST]; struct _MMBaseCallPrivate { /* The connection to the system bus */ GDBusConnection *connection; guint dbus_id; /* The modem which owns this call */ MMBaseModem *modem; /* The path where the call object is exported */ gchar *path; /* Features */ gboolean skip_incoming_timeout; gboolean supports_dialing_to_ringing; gboolean supports_ringing_to_active; guint incoming_timeout; /* The port used for audio while call is ongoing, if known */ MMPort *audio_port; /* Ongoing call index */ guint index; /* Start cancellable, used when the call state transition to * 'terminated' is coming asynchronously (e.g. via in-call state * update notifications) */ GCancellable *start_cancellable; }; /*****************************************************************************/ /* Incoming calls are reported via RING URCs. If the caller stops the call * attempt before it has been answered, the only thing we would see is that the * URCs are no longer received. So, we will start a timeout whenever a new RING * URC is received, and we refresh the timeout any time a new URC arrives. If * the timeout is expired (meaning no URCs were received in the last N seconds) * then we assume the call attempt is finished and we transition to TERMINATED. */ #define INCOMING_TIMEOUT_SECS 10 static gboolean incoming_timeout_cb (MMBaseCall *self) { self->priv->incoming_timeout = 0; mm_obj_msg (self, "incoming call timed out: no response"); mm_base_call_change_state (self, MM_CALL_STATE_TERMINATED, MM_CALL_STATE_REASON_TERMINATED); return G_SOURCE_REMOVE; } void mm_base_call_incoming_refresh (MMBaseCall *self) { if (self->priv->skip_incoming_timeout) return; if (self->priv->incoming_timeout) g_source_remove (self->priv->incoming_timeout); self->priv->incoming_timeout = g_timeout_add_seconds (INCOMING_TIMEOUT_SECS, (GSourceFunc)incoming_timeout_cb, self); } /*****************************************************************************/ /* Update audio settings */ void mm_base_call_change_audio_settings (MMBaseCall *self, MMPort *audio_port, MMCallAudioFormat *audio_format) { if (!audio_port && self->priv->audio_port && mm_port_get_connected (self->priv->audio_port)) mm_port_set_connected (self->priv->audio_port, FALSE); g_clear_object (&self->priv->audio_port); if (audio_port) { self->priv->audio_port = g_object_ref (audio_port); mm_port_set_connected (self->priv->audio_port, TRUE); } mm_gdbus_call_set_audio_port (MM_GDBUS_CALL (self), audio_port ? mm_port_get_device (audio_port) : NULL); mm_gdbus_call_set_audio_format (MM_GDBUS_CALL (self), mm_call_audio_format_get_dictionary (audio_format)); } /*****************************************************************************/ /* Start call (DBus call handling) */ typedef struct { MMBaseCall *self; MMBaseModem *modem; GDBusMethodInvocation *invocation; } HandleStartContext; static void handle_start_context_free (HandleStartContext *ctx) { g_object_unref (ctx->invocation); g_object_unref (ctx->modem); g_object_unref (ctx->self); g_free (ctx); } static void handle_start_ready (MMBaseCall *self, GAsyncResult *res, HandleStartContext *ctx) { GError *error = NULL; g_clear_object (&ctx->self->priv->start_cancellable); if (!MM_BASE_CALL_GET_CLASS (self)->start_finish (self, res, &error)) { mm_obj_warn (self, "couldn't start call: %s", error->message); /* When cancelled via the start cancellable, it's because we got an early in-call error * before the call attempt was reported as started. */ if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED) || g_error_matches (error, MM_CORE_ERROR, MM_CORE_ERROR_CANCELLED)) { g_clear_error (&error); error = mm_connection_error_for_code (MM_CONNECTION_ERROR_NO_DIALTONE, self); } /* Convert errors into call state updates */ if (g_error_matches (error, MM_CONNECTION_ERROR, MM_CONNECTION_ERROR_NO_DIALTONE)) mm_base_call_change_state (self, MM_CALL_STATE_TERMINATED, MM_CALL_STATE_REASON_ERROR); else if (g_error_matches (error, MM_CONNECTION_ERROR, MM_CONNECTION_ERROR_BUSY) || g_error_matches (error, MM_CONNECTION_ERROR, MM_CONNECTION_ERROR_NO_ANSWER) || g_error_matches (error, MM_CONNECTION_ERROR, MM_CONNECTION_ERROR_NO_CARRIER)) mm_base_call_change_state (self, MM_CALL_STATE_TERMINATED, MM_CALL_STATE_REASON_REFUSED_OR_BUSY); else mm_base_call_change_state (self, MM_CALL_STATE_TERMINATED, MM_CALL_STATE_REASON_UNKNOWN); mm_dbus_method_invocation_take_error (ctx->invocation, error); handle_start_context_free (ctx); return; } mm_obj_msg (self, "call is started"); /* If dialing to ringing supported, leave it dialing */ if (!ctx->self->priv->supports_dialing_to_ringing) { /* If ringing to active supported, set it ringing */ if (ctx->self->priv->supports_ringing_to_active) mm_base_call_change_state (ctx->self, MM_CALL_STATE_RINGING_OUT, MM_CALL_STATE_REASON_OUTGOING_STARTED); else /* Otherwise, active right away */ mm_base_call_change_state (ctx->self, MM_CALL_STATE_ACTIVE, MM_CALL_STATE_REASON_OUTGOING_STARTED); } mm_gdbus_call_complete_start (MM_GDBUS_CALL (ctx->self), ctx->invocation); handle_start_context_free (ctx); } static void handle_start_auth_ready (MMBaseModem *modem, GAsyncResult *res, HandleStartContext *ctx) { MMCallState state; GError *error = NULL; if (!mm_base_modem_authorize_finish (modem, res, &error)) { mm_base_call_change_state (ctx->self, MM_CALL_STATE_TERMINATED, MM_CALL_STATE_REASON_UNKNOWN); mm_dbus_method_invocation_take_error (ctx->invocation, error); handle_start_context_free (ctx); return; } /* We can only start call created by the user */ state = mm_gdbus_call_get_state (MM_GDBUS_CALL (ctx->self)); if (state != MM_CALL_STATE_UNKNOWN) { mm_dbus_method_invocation_return_error_literal (ctx->invocation, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "This call was not in unknown state, cannot start it"); handle_start_context_free (ctx); return; } mm_obj_msg (ctx->self, "user request to start call"); /* Disallow non-emergency calls when in emergency-only state */ if (!mm_iface_modem_voice_authorize_outgoing_call (MM_IFACE_MODEM_VOICE (modem), ctx->self, &error)) { mm_base_call_change_state (ctx->self, MM_CALL_STATE_TERMINATED, MM_CALL_STATE_REASON_UNKNOWN); mm_dbus_method_invocation_take_error (ctx->invocation, error); handle_start_context_free (ctx); return; } /* Check if we do support doing it */ if (!MM_BASE_CALL_GET_CLASS (ctx->self)->start || !MM_BASE_CALL_GET_CLASS (ctx->self)->start_finish) { mm_base_call_change_state (ctx->self, MM_CALL_STATE_TERMINATED, MM_CALL_STATE_REASON_UNKNOWN); mm_dbus_method_invocation_return_error_literal (ctx->invocation, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "Starting call is not supported by this modem"); handle_start_context_free (ctx); return; } mm_base_call_change_state (ctx->self, MM_CALL_STATE_DIALING, MM_CALL_STATE_REASON_OUTGOING_STARTED); /* Setup start cancellable to get notified of termination asynchronously */ g_assert (!ctx->self->priv->start_cancellable); ctx->self->priv->start_cancellable = g_cancellable_new (); MM_BASE_CALL_GET_CLASS (ctx->self)->start (ctx->self, ctx->self->priv->start_cancellable, (GAsyncReadyCallback)handle_start_ready, ctx); } static gboolean handle_start (MMBaseCall *self, GDBusMethodInvocation *invocation) { HandleStartContext *ctx; ctx = g_new0 (HandleStartContext, 1); ctx->self = g_object_ref (self); ctx->invocation = g_object_ref (invocation); g_object_get (self, MM_BASE_CALL_MODEM, &ctx->modem, NULL); mm_base_modem_authorize (ctx->modem, invocation, MM_AUTHORIZATION_VOICE, (GAsyncReadyCallback)handle_start_auth_ready, ctx); return TRUE; } /*****************************************************************************/ /* Accept call (DBus call handling) */ typedef struct { MMBaseCall *self; MMBaseModem *modem; GDBusMethodInvocation *invocation; } HandleAcceptContext; static void handle_accept_context_free (HandleAcceptContext *ctx) { g_object_unref (ctx->invocation); g_object_unref (ctx->modem); g_object_unref (ctx->self); g_free (ctx); } static void handle_accept_ready (MMBaseCall *self, GAsyncResult *res, HandleAcceptContext *ctx) { GError *error = NULL; if (!MM_BASE_CALL_GET_CLASS (self)->accept_finish (self, res, &error)) { mm_base_call_change_state (self, MM_CALL_STATE_TERMINATED, MM_CALL_STATE_REASON_ERROR); mm_dbus_method_invocation_take_error (ctx->invocation, error); handle_accept_context_free (ctx); return; } mm_obj_msg (self, "call is accepted"); if (ctx->self->priv->incoming_timeout) { g_source_remove (ctx->self->priv->incoming_timeout); ctx->self->priv->incoming_timeout = 0; } mm_base_call_change_state (ctx->self, MM_CALL_STATE_ACTIVE, MM_CALL_STATE_REASON_ACCEPTED); mm_gdbus_call_complete_accept (MM_GDBUS_CALL (ctx->self), ctx->invocation); handle_accept_context_free (ctx); } static void handle_accept_auth_ready (MMBaseModem *modem, GAsyncResult *res, HandleAcceptContext *ctx) { MMCallState state; GError *error = NULL; if (!mm_base_modem_authorize_finish (modem, res, &error)) { mm_dbus_method_invocation_take_error (ctx->invocation, error); handle_accept_context_free (ctx); return; } state = mm_gdbus_call_get_state (MM_GDBUS_CALL (ctx->self)); /* We can only accept incoming call in ringing state */ if (state != MM_CALL_STATE_RINGING_IN) { mm_dbus_method_invocation_return_error_literal (ctx->invocation, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "This call was not ringing, cannot accept"); handle_accept_context_free (ctx); return; } mm_obj_msg (ctx->self, "user request to accept call"); /* Check if we do support doing it */ if (!MM_BASE_CALL_GET_CLASS (ctx->self)->accept || !MM_BASE_CALL_GET_CLASS (ctx->self)->accept_finish) { mm_dbus_method_invocation_return_error_literal (ctx->invocation, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "Accepting call is not supported by this modem"); handle_accept_context_free (ctx); return; } MM_BASE_CALL_GET_CLASS (ctx->self)->accept (ctx->self, (GAsyncReadyCallback)handle_accept_ready, ctx); } static gboolean handle_accept (MMBaseCall *self, GDBusMethodInvocation *invocation) { HandleAcceptContext *ctx; ctx = g_new0 (HandleAcceptContext, 1); ctx->self = g_object_ref (self); ctx->invocation = g_object_ref (invocation); g_object_get (self, MM_BASE_CALL_MODEM, &ctx->modem, NULL); mm_base_modem_authorize (ctx->modem, invocation, MM_AUTHORIZATION_VOICE, (GAsyncReadyCallback)handle_accept_auth_ready, ctx); return TRUE; } /*****************************************************************************/ /* Deflect call (DBus call handling) */ typedef struct { MMBaseCall *self; MMBaseModem *modem; GDBusMethodInvocation *invocation; gchar *number; } HandleDeflectContext; static void handle_deflect_context_free (HandleDeflectContext *ctx) { g_free (ctx->number); g_object_unref (ctx->invocation); g_object_unref (ctx->modem); g_object_unref (ctx->self); g_slice_free (HandleDeflectContext, ctx); } static void handle_deflect_ready (MMBaseCall *self, GAsyncResult *res, HandleDeflectContext *ctx) { GError *error = NULL; if (!MM_BASE_CALL_GET_CLASS (self)->deflect_finish (self, res, &error)) { mm_base_call_change_state (self, MM_CALL_STATE_TERMINATED, MM_CALL_STATE_REASON_ERROR); mm_dbus_method_invocation_take_error (ctx->invocation, error); handle_deflect_context_free (ctx); return; } mm_obj_msg (self, "call is deflected to '%s'", ctx->number); mm_base_call_change_state (ctx->self, MM_CALL_STATE_TERMINATED, MM_CALL_STATE_REASON_DEFLECTED); mm_gdbus_call_complete_deflect (MM_GDBUS_CALL (ctx->self), ctx->invocation); handle_deflect_context_free (ctx); } static void handle_deflect_auth_ready (MMBaseModem *modem, GAsyncResult *res, HandleDeflectContext *ctx) { MMCallState state; GError *error = NULL; if (!mm_base_modem_authorize_finish (modem, res, &error)) { mm_dbus_method_invocation_take_error (ctx->invocation, error); handle_deflect_context_free (ctx); return; } state = mm_gdbus_call_get_state (MM_GDBUS_CALL (ctx->self)); /* We can only deflect incoming call in ringing or waiting state */ if (state != MM_CALL_STATE_RINGING_IN && state != MM_CALL_STATE_WAITING) { mm_dbus_method_invocation_return_error_literal (ctx->invocation, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "This call was not ringing/waiting, cannot deflect"); handle_deflect_context_free (ctx); return; } mm_obj_msg (ctx->self, "user request to deflect call"); /* Check if we do support doing it */ if (!MM_BASE_CALL_GET_CLASS (ctx->self)->deflect || !MM_BASE_CALL_GET_CLASS (ctx->self)->deflect_finish) { mm_dbus_method_invocation_return_error_literal (ctx->invocation, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "Deflecting call is not supported by this modem"); handle_deflect_context_free (ctx); return; } MM_BASE_CALL_GET_CLASS (ctx->self)->deflect (ctx->self, ctx->number, (GAsyncReadyCallback)handle_deflect_ready, ctx); } static gboolean handle_deflect (MMBaseCall *self, GDBusMethodInvocation *invocation, const gchar *number) { HandleDeflectContext *ctx; ctx = g_slice_new0 (HandleDeflectContext); ctx->self = g_object_ref (self); ctx->invocation = g_object_ref (invocation); ctx->number = g_strdup (number); g_object_get (self, MM_BASE_CALL_MODEM, &ctx->modem, NULL); mm_base_modem_authorize (ctx->modem, invocation, MM_AUTHORIZATION_VOICE, (GAsyncReadyCallback)handle_deflect_auth_ready, ctx); return TRUE; } /*****************************************************************************/ /* Join multiparty call (DBus call handling) */ typedef struct { MMBaseCall *self; MMBaseModem *modem; GDBusMethodInvocation *invocation; } HandleJoinMultipartyContext; static void handle_join_multiparty_context_free (HandleJoinMultipartyContext *ctx) { g_object_unref (ctx->invocation); g_object_unref (ctx->modem); g_object_unref (ctx->self); g_free (ctx); } static void modem_voice_join_multiparty_ready (MMIfaceModemVoice *modem, GAsyncResult *res, HandleJoinMultipartyContext *ctx) { GError *error = NULL; if (!mm_iface_modem_voice_join_multiparty_finish (modem, res, &error)) mm_dbus_method_invocation_take_error (ctx->invocation, error); else mm_gdbus_call_complete_join_multiparty (MM_GDBUS_CALL (ctx->self), ctx->invocation); handle_join_multiparty_context_free (ctx); } static void handle_join_multiparty_auth_ready (MMBaseModem *modem, GAsyncResult *res, HandleJoinMultipartyContext *ctx) { GError *error = NULL; if (!mm_base_modem_authorize_finish (modem, res, &error)) { mm_dbus_method_invocation_take_error (ctx->invocation, error); handle_join_multiparty_context_free (ctx); return; } /* This action is provided in the Call API, but implemented in the Modem.Voice interface * logic, because the action affects not only one call object, but all call objects that * are part of the multiparty call. */ mm_iface_modem_voice_join_multiparty (MM_IFACE_MODEM_VOICE (ctx->modem), ctx->self, (GAsyncReadyCallback)modem_voice_join_multiparty_ready, ctx); } static gboolean handle_join_multiparty (MMBaseCall *self, GDBusMethodInvocation *invocation) { HandleJoinMultipartyContext *ctx; ctx = g_new0 (HandleJoinMultipartyContext, 1); ctx->self = g_object_ref (self); ctx->invocation = g_object_ref (invocation); g_object_get (self, MM_BASE_CALL_MODEM, &ctx->modem, NULL); mm_base_modem_authorize (ctx->modem, invocation, MM_AUTHORIZATION_VOICE, (GAsyncReadyCallback)handle_join_multiparty_auth_ready, ctx); return TRUE; } /*****************************************************************************/ /* Leave multiparty call (DBus call handling) */ typedef struct { MMBaseCall *self; MMBaseModem *modem; GDBusMethodInvocation *invocation; } HandleLeaveMultipartyContext; static void handle_leave_multiparty_context_free (HandleLeaveMultipartyContext *ctx) { g_object_unref (ctx->invocation); g_object_unref (ctx->modem); g_object_unref (ctx->self); g_free (ctx); } static void modem_voice_leave_multiparty_ready (MMIfaceModemVoice *modem, GAsyncResult *res, HandleLeaveMultipartyContext *ctx) { GError *error = NULL; if (!mm_iface_modem_voice_leave_multiparty_finish (modem, res, &error)) mm_dbus_method_invocation_take_error (ctx->invocation, error); else mm_gdbus_call_complete_leave_multiparty (MM_GDBUS_CALL (ctx->self), ctx->invocation); handle_leave_multiparty_context_free (ctx); } static void handle_leave_multiparty_auth_ready (MMBaseModem *modem, GAsyncResult *res, HandleLeaveMultipartyContext *ctx) { GError *error = NULL; if (!mm_base_modem_authorize_finish (modem, res, &error)) { mm_dbus_method_invocation_take_error (ctx->invocation, error); handle_leave_multiparty_context_free (ctx); return; } /* This action is provided in the Call API, but implemented in the Modem.Voice interface * logic, because the action affects not only one call object, but all call objects that * are part of the multiparty call. */ mm_iface_modem_voice_leave_multiparty (MM_IFACE_MODEM_VOICE (ctx->modem), ctx->self, (GAsyncReadyCallback)modem_voice_leave_multiparty_ready, ctx); } static gboolean handle_leave_multiparty (MMBaseCall *self, GDBusMethodInvocation *invocation) { HandleLeaveMultipartyContext *ctx; ctx = g_new0 (HandleLeaveMultipartyContext, 1); ctx->self = g_object_ref (self); ctx->invocation = g_object_ref (invocation); g_object_get (self, MM_BASE_CALL_MODEM, &ctx->modem, NULL); mm_base_modem_authorize (ctx->modem, invocation, MM_AUTHORIZATION_VOICE, (GAsyncReadyCallback)handle_leave_multiparty_auth_ready, ctx); return TRUE; } /*****************************************************************************/ /* Hangup call (DBus call handling) */ typedef struct { MMBaseCall *self; MMBaseModem *modem; GDBusMethodInvocation *invocation; } HandleHangupContext; static void handle_hangup_context_free (HandleHangupContext *ctx) { g_object_unref (ctx->invocation); g_object_unref (ctx->modem); g_object_unref (ctx->self); g_free (ctx); } static void handle_hangup_ready (MMBaseCall *self, GAsyncResult *res, HandleHangupContext *ctx) { GError *error = NULL; /* we set it as terminated even if we got an error reported */ mm_base_call_change_state (self, MM_CALL_STATE_TERMINATED, MM_CALL_STATE_REASON_TERMINATED); if (!MM_BASE_CALL_GET_CLASS (self)->hangup_finish (self, res, &error)) mm_dbus_method_invocation_take_error (ctx->invocation, error); else { /* note: timeouts are already removed when setting state as TERMINATED */ mm_gdbus_call_complete_hangup (MM_GDBUS_CALL (ctx->self), ctx->invocation); } handle_hangup_context_free (ctx); } static void handle_hangup_auth_ready (MMBaseModem *modem, GAsyncResult *res, HandleHangupContext *ctx) { MMCallState state; GError *error = NULL; if (!mm_base_modem_authorize_finish (modem, res, &error)) { mm_dbus_method_invocation_take_error (ctx->invocation, error); handle_hangup_context_free (ctx); return; } state = mm_gdbus_call_get_state (MM_GDBUS_CALL (ctx->self)); /* We can only hangup call in a valid state */ if (state == MM_CALL_STATE_TERMINATED || state == MM_CALL_STATE_UNKNOWN) { mm_dbus_method_invocation_return_error_literal (ctx->invocation, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "This call was not active, cannot hangup"); handle_hangup_context_free (ctx); return; } mm_obj_msg (ctx->self, "user request to hangup call"); /* Check if we do support doing it */ if (!MM_BASE_CALL_GET_CLASS (ctx->self)->hangup || !MM_BASE_CALL_GET_CLASS (ctx->self)->hangup_finish) { mm_dbus_method_invocation_return_error_literal (ctx->invocation, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "Hanging up call is not supported by this modem"); handle_hangup_context_free (ctx); return; } MM_BASE_CALL_GET_CLASS (ctx->self)->hangup (ctx->self, (GAsyncReadyCallback)handle_hangup_ready, ctx); } static gboolean handle_hangup (MMBaseCall *self, GDBusMethodInvocation *invocation) { HandleHangupContext *ctx; ctx = g_new0 (HandleHangupContext, 1); ctx->self = g_object_ref (self); ctx->invocation = g_object_ref (invocation); g_object_get (self, MM_BASE_CALL_MODEM, &ctx->modem, NULL); mm_base_modem_authorize (ctx->modem, invocation, MM_AUTHORIZATION_VOICE, (GAsyncReadyCallback)handle_hangup_auth_ready, ctx); return TRUE; } /*****************************************************************************/ /* Send dtmf (DBus call handling) */ typedef struct { MMBaseCall *self; MMBaseModem *modem; GDBusMethodInvocation *invocation; gchar *dtmf; } HandleSendDtmfContext; static void handle_send_dtmf_context_free (HandleSendDtmfContext *ctx) { g_object_unref (ctx->invocation); g_object_unref (ctx->modem); g_object_unref (ctx->self); g_free (ctx->dtmf); g_free (ctx); } static void handle_send_dtmf_ready (MMBaseCall *self, GAsyncResult *res, HandleSendDtmfContext *ctx) { GError *error = NULL; if (!MM_BASE_CALL_GET_CLASS (self)->send_dtmf_finish (self, res, &error)) { mm_dbus_method_invocation_take_error (ctx->invocation, error); } else { mm_gdbus_call_complete_send_dtmf (MM_GDBUS_CALL (ctx->self), ctx->invocation); } handle_send_dtmf_context_free (ctx); } static void handle_send_dtmf_auth_ready (MMBaseModem *modem, GAsyncResult *res, HandleSendDtmfContext *ctx) { MMCallState state; GError *error = NULL; if (!mm_base_modem_authorize_finish (modem, res, &error)) { mm_dbus_method_invocation_take_error (ctx->invocation, error); handle_send_dtmf_context_free (ctx); return; } state = mm_gdbus_call_get_state (MM_GDBUS_CALL (ctx->self)); /* Check if we do support doing it */ if (!MM_BASE_CALL_GET_CLASS (ctx->self)->send_dtmf || !MM_BASE_CALL_GET_CLASS (ctx->self)->send_dtmf_finish) { mm_dbus_method_invocation_return_error_literal (ctx->invocation, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "Sending dtmf is not supported by this modem"); handle_send_dtmf_context_free (ctx); return; } /* We can only send_dtmf when call is in ACTIVE state */ if (state != MM_CALL_STATE_ACTIVE ){ mm_dbus_method_invocation_return_error_literal (ctx->invocation, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "This call was not active, cannot send dtmf"); handle_send_dtmf_context_free (ctx); return; } MM_BASE_CALL_GET_CLASS (ctx->self)->send_dtmf (ctx->self, ctx->dtmf, (GAsyncReadyCallback)handle_send_dtmf_ready, ctx); } static gboolean handle_send_dtmf (MMBaseCall *self, GDBusMethodInvocation *invocation, const gchar *dtmf) { HandleSendDtmfContext *ctx; ctx = g_new0 (HandleSendDtmfContext, 1); ctx->self = g_object_ref (self); ctx->invocation = g_object_ref (invocation); ctx->dtmf = g_strdup (dtmf); g_object_get (self, MM_BASE_CALL_MODEM, &ctx->modem, NULL); mm_base_modem_authorize (ctx->modem, invocation, MM_AUTHORIZATION_VOICE, (GAsyncReadyCallback)handle_send_dtmf_auth_ready, ctx); return TRUE; } /*****************************************************************************/ void mm_base_call_export (MMBaseCall *self) { gchar *path; path = g_strdup_printf (MM_DBUS_CALL_PREFIX "/%d", self->priv->dbus_id); g_object_set (self, MM_BASE_CALL_PATH, path, NULL); g_free (path); } void mm_base_call_unexport (MMBaseCall *self) { g_object_set (self, MM_BASE_CALL_PATH, NULL, NULL); } /*****************************************************************************/ static void call_dbus_export (MMBaseCall *self) { GError *error = NULL; /* Handle method invocations */ g_object_connect (self, "signal::handle-start", G_CALLBACK (handle_start), NULL, "signal::handle-accept", G_CALLBACK (handle_accept), NULL, "signal::handle-deflect", G_CALLBACK (handle_deflect), NULL, "signal::handle-join-multiparty", G_CALLBACK (handle_join_multiparty), NULL, "signal::handle-leave-multiparty", G_CALLBACK (handle_leave_multiparty), NULL, "signal::handle-hangup", G_CALLBACK (handle_hangup), NULL, "signal::handle-send-dtmf", G_CALLBACK (handle_send_dtmf), NULL, NULL); if (!g_dbus_interface_skeleton_export (G_DBUS_INTERFACE_SKELETON (self), self->priv->connection, self->priv->path, &error)) { mm_obj_warn (self, "couldn't export call: %s", error->message); g_error_free (error); } } static void call_dbus_unexport (MMBaseCall *self) { /* Only unexport if currently exported */ if (g_dbus_interface_skeleton_get_object_path (G_DBUS_INTERFACE_SKELETON (self))) g_dbus_interface_skeleton_unexport (G_DBUS_INTERFACE_SKELETON (self)); } /*****************************************************************************/ const gchar * mm_base_call_get_path (MMBaseCall *self) { return self->priv->path; } const gchar * mm_base_call_get_number (MMBaseCall *self) { return mm_gdbus_call_get_number (MM_GDBUS_CALL (self)); } void mm_base_call_set_number (MMBaseCall *self, const gchar *number) { return mm_gdbus_call_set_number (MM_GDBUS_CALL (self), number); } MMCallDirection mm_base_call_get_direction (MMBaseCall *self) { return (MMCallDirection) mm_gdbus_call_get_direction (MM_GDBUS_CALL (self)); } MMCallState mm_base_call_get_state (MMBaseCall *self) { return (MMCallState) mm_gdbus_call_get_state (MM_GDBUS_CALL (self)); } gboolean mm_base_call_get_multiparty (MMBaseCall *self) { return mm_gdbus_call_get_multiparty (MM_GDBUS_CALL (self)); } void mm_base_call_set_multiparty (MMBaseCall *self, gboolean multiparty) { return mm_gdbus_call_set_multiparty (MM_GDBUS_CALL (self), multiparty); } /*****************************************************************************/ /* Current call index, only applicable while the call is ongoing * See 3GPP TS 22.030 [27], subclause 6.5.5.1. */ guint mm_base_call_get_index (MMBaseCall *self) { return self->priv->index; } void mm_base_call_set_index (MMBaseCall *self, guint index) { self->priv->index = index; } /*****************************************************************************/ void mm_base_call_change_state (MMBaseCall *self, MMCallState new_state, MMCallStateReason reason) { MMCallState old_state; old_state = mm_gdbus_call_get_state (MM_GDBUS_CALL (self)); if (old_state == new_state) return; mm_obj_msg (self, "call state changed: %s -> %s (%s)", mm_call_state_get_string (old_state), mm_call_state_get_string (new_state), mm_call_state_reason_get_string (reason)); /* Setup/cleanup unsolicited events based on state transitions to/from ACTIVE */ if (new_state == MM_CALL_STATE_TERMINATED) { /* reset index */ self->priv->index = 0; /* cleanup incoming timeout, if any */ if (self->priv->incoming_timeout) { g_source_remove (self->priv->incoming_timeout); self->priv->incoming_timeout = 0; } /* cancel start if ongoing */ g_cancellable_cancel (self->priv->start_cancellable); } mm_gdbus_call_set_state (MM_GDBUS_CALL (self), new_state); mm_gdbus_call_set_state_reason (MM_GDBUS_CALL (self), reason); mm_gdbus_call_emit_state_changed (MM_GDBUS_CALL (self), old_state, new_state, reason); } /*****************************************************************************/ void mm_base_call_received_dtmf (MMBaseCall *self, const gchar *dtmf) { mm_gdbus_call_emit_dtmf_received (MM_GDBUS_CALL (self), dtmf); } /*****************************************************************************/ /* Start the CALL */ static gboolean call_start_finish (MMBaseCall *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void call_start_ready (MMBaseModem *modem, GAsyncResult *res, GTask *task) { GError *error = NULL; const gchar *response = NULL; response = mm_base_modem_at_command_finish (modem, res, &error); /* check response for error */ if (response && response[0]) error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't start the call: Unhandled response '%s'", response); if (error) g_task_return_error (task, error); else g_task_return_boolean (task, TRUE); g_object_unref (task); } static void call_start (MMBaseCall *self, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { GError *error = NULL; GTask *task; gchar *cmd; MMPortSerialAt *port; task = g_task_new (self, NULL, callback, user_data); port = mm_base_modem_peek_best_at_port (MM_BASE_MODEM (self->priv->modem), &error); if (!port) { g_task_return_error (task, error); g_object_unref (task); return; } cmd = g_strdup_printf ("ATD%s;", mm_gdbus_call_get_number (MM_GDBUS_CALL (self))); mm_base_modem_at_command_full (self->priv->modem, port, cmd, 90, FALSE, /* no cached */ FALSE, /* no raw */ cancellable, (GAsyncReadyCallback)call_start_ready, task); g_free (cmd); } /*****************************************************************************/ /* Accept the call */ static gboolean call_accept_finish (MMBaseCall *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void call_accept_ready (MMBaseModem *modem, GAsyncResult *res, GTask *task) { GError *error = NULL; const gchar *response; response = mm_base_modem_at_command_finish (modem, res, &error); /* check response for error */ if (response && response[0]) g_set_error (&error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't accept the call: Unhandled response '%s'", response); if (error) g_task_return_error (task, error); else g_task_return_boolean (task, TRUE); g_object_unref (task); } static void call_accept (MMBaseCall *self, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; task = g_task_new (self, NULL, callback, user_data); mm_base_modem_at_command (self->priv->modem, "ATA", 2, FALSE, (GAsyncReadyCallback)call_accept_ready, task); } /*****************************************************************************/ /* Deflect the call */ static gboolean call_deflect_finish (MMBaseCall *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void call_deflect_ready (MMBaseModem *modem, GAsyncResult *res, GTask *task) { GError *error = NULL; mm_base_modem_at_command_finish (modem, res, &error); if (error) g_task_return_error (task, error); else g_task_return_boolean (task, TRUE); g_object_unref (task); } static void call_deflect (MMBaseCall *self, const gchar *number, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; gchar *cmd; task = g_task_new (self, NULL, callback, user_data); cmd = g_strdup_printf ("+CTFR=%s", number); mm_base_modem_at_command (self->priv->modem, cmd, 20, FALSE, (GAsyncReadyCallback)call_deflect_ready, task); g_free (cmd); } /*****************************************************************************/ /* Hangup the call */ static gboolean call_hangup_finish (MMBaseCall *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void chup_ready (MMBaseModem *modem, GAsyncResult *res, GTask *task) { GError *error = NULL; mm_base_modem_at_command_finish (modem, res, &error); if (error) g_task_return_error (task, error); else g_task_return_boolean (task, TRUE); g_object_unref (task); } static void chup_fallback (GTask *task) { MMBaseCall *self; self = g_task_get_source_object (task); mm_base_modem_at_command (self->priv->modem, "+CHUP", 2, FALSE, (GAsyncReadyCallback)chup_ready, task); } static void chld_hangup_ready (MMBaseModem *modem, GAsyncResult *res, GTask *task) { MMBaseCall *self; GError *error = NULL; self = g_task_get_source_object (task); mm_base_modem_at_command_finish (modem, res, &error); if (error) { mm_obj_warn (self, "couldn't hangup single call with call id '%u': %s", self->priv->index, error->message); g_error_free (error); chup_fallback (task); return; } g_task_return_boolean (task, TRUE); g_object_unref (task); } static void call_hangup (MMBaseCall *self, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; task = g_task_new (self, NULL, callback, user_data); /* Try to hangup the single call id */ if (self->priv->index) { gchar *cmd; cmd = g_strdup_printf ("+CHLD=1%u", self->priv->index); mm_base_modem_at_command (self->priv->modem, cmd, 2, FALSE, (GAsyncReadyCallback)chld_hangup_ready, task); g_free (cmd); return; } /* otherwise terminate all */ chup_fallback (task); } /*****************************************************************************/ /* Send DTMF tone to call */ static gboolean call_send_dtmf_finish (MMBaseCall *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void call_send_dtmf_ready (MMBaseModem *modem, GAsyncResult *res, GTask *task) { MMBaseCall *self; GError *error = NULL; self = g_task_get_source_object (task); mm_base_modem_at_command_finish (modem, res, &error); if (error) { mm_obj_dbg (self, "couldn't send dtmf: %s", error->message); g_task_return_error (task, error); g_object_unref (task); return; } g_task_return_boolean (task, TRUE); g_object_unref (task); } static void call_send_dtmf (MMBaseCall *self, const gchar *dtmf, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; gchar *cmd; task = g_task_new (self, NULL, callback, user_data); cmd = g_strdup_printf ("AT+VTS=%c", dtmf[0]); mm_base_modem_at_command (self->priv->modem, cmd, 3, FALSE, (GAsyncReadyCallback)call_send_dtmf_ready, task); g_free (cmd); } /*****************************************************************************/ static gchar * log_object_build_id (MMLogObject *_self) { MMBaseCall *self; self = MM_BASE_CALL (_self); return g_strdup_printf ("call%u", self->priv->dbus_id); } /*****************************************************************************/ MMBaseCall * mm_base_call_new (MMBaseModem *modem, MMCallDirection direction, const gchar *number, gboolean skip_incoming_timeout, gboolean supports_dialing_to_ringing, gboolean supports_ringing_to_active) { return MM_BASE_CALL (g_object_new (MM_TYPE_BASE_CALL, MM_BASE_CALL_MODEM, modem, "direction", direction, "number", number, MM_BASE_CALL_SKIP_INCOMING_TIMEOUT, skip_incoming_timeout, MM_BASE_CALL_SUPPORTS_DIALING_TO_RINGING, supports_dialing_to_ringing, MM_BASE_CALL_SUPPORTS_RINGING_TO_ACTIVE, supports_ringing_to_active, NULL)); } /*****************************************************************************/ static void set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { MMBaseCall *self = MM_BASE_CALL (object); switch (prop_id) { case PROP_PATH: g_free (self->priv->path); self->priv->path = g_value_dup_string (value); /* Export when we get a DBus connection AND we have a path */ if (!self->priv->path) call_dbus_unexport (self); else if (self->priv->connection) call_dbus_export (self); break; case PROP_CONNECTION: g_clear_object (&self->priv->connection); self->priv->connection = g_value_dup_object (value); /* Export when we get a DBus connection AND we have a path */ if (!self->priv->connection) call_dbus_unexport (self); else if (self->priv->path) call_dbus_export (self); break; case PROP_MODEM: g_clear_object (&self->priv->modem); self->priv->modem = g_value_dup_object (value); if (self->priv->modem) { /* Set owner ID */ mm_log_object_set_owner_id (MM_LOG_OBJECT (self), mm_log_object_get_id (MM_LOG_OBJECT (self->priv->modem))); /* Bind the modem's connection (which is set when it is exported, * and unset when unexported) to the call's connection */ g_object_bind_property (self->priv->modem, MM_BASE_MODEM_CONNECTION, self, MM_BASE_CALL_CONNECTION, G_BINDING_DEFAULT | G_BINDING_SYNC_CREATE); } break; case PROP_SKIP_INCOMING_TIMEOUT: self->priv->skip_incoming_timeout = g_value_get_boolean (value); break; case PROP_SUPPORTS_DIALING_TO_RINGING: self->priv->supports_dialing_to_ringing = g_value_get_boolean (value); break; case PROP_SUPPORTS_RINGING_TO_ACTIVE: self->priv->supports_ringing_to_active = 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) { MMBaseCall *self = MM_BASE_CALL (object); switch (prop_id) { case PROP_PATH: g_value_set_string (value, self->priv->path); break; case PROP_CONNECTION: g_value_set_object (value, self->priv->connection); break; case PROP_MODEM: g_value_set_object (value, self->priv->modem); break; case PROP_SKIP_INCOMING_TIMEOUT: g_value_set_boolean (value, self->priv->skip_incoming_timeout); break; case PROP_SUPPORTS_DIALING_TO_RINGING: g_value_set_boolean (value, self->priv->supports_dialing_to_ringing); break; case PROP_SUPPORTS_RINGING_TO_ACTIVE: g_value_set_boolean (value, self->priv->supports_ringing_to_active); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void mm_base_call_init (MMBaseCall *self) { static guint id = 0; /* Initialize private data */ self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, MM_TYPE_BASE_CALL, MMBaseCallPrivate); /* Each call is given a unique id to build its own DBus path */ self->priv->dbus_id = id++; } static void finalize (GObject *object) { MMBaseCall *self = MM_BASE_CALL (object); g_assert (!self->priv->start_cancellable); g_free (self->priv->path); G_OBJECT_CLASS (mm_base_call_parent_class)->finalize (object); } static void dispose (GObject *object) { MMBaseCall *self = MM_BASE_CALL (object); g_clear_object (&self->priv->audio_port); if (self->priv->incoming_timeout) { g_source_remove (self->priv->incoming_timeout); self->priv->incoming_timeout = 0; } if (self->priv->connection) { /* If we arrived here with a valid connection, make sure we unexport * the object */ call_dbus_unexport (self); g_clear_object (&self->priv->connection); } g_clear_object (&self->priv->modem); G_OBJECT_CLASS (mm_base_call_parent_class)->dispose (object); } static void log_object_iface_init (MMLogObjectInterface *iface) { iface->build_id = log_object_build_id; } static void mm_base_call_class_init (MMBaseCallClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); g_type_class_add_private (object_class, sizeof (MMBaseCallPrivate)); /* Virtual methods */ object_class->get_property = get_property; object_class->set_property = set_property; object_class->finalize = finalize; object_class->dispose = dispose; klass->start = call_start; klass->start_finish = call_start_finish; klass->accept = call_accept; klass->accept_finish = call_accept_finish; klass->deflect = call_deflect; klass->deflect_finish = call_deflect_finish; klass->hangup = call_hangup; klass->hangup_finish = call_hangup_finish; klass->send_dtmf = call_send_dtmf; klass->send_dtmf_finish = call_send_dtmf_finish; properties[PROP_CONNECTION] = g_param_spec_object (MM_BASE_CALL_CONNECTION, "Connection", "GDBus connection to the system bus.", G_TYPE_DBUS_CONNECTION, G_PARAM_READWRITE); g_object_class_install_property (object_class, PROP_CONNECTION, properties[PROP_CONNECTION]); properties[PROP_PATH] = g_param_spec_string (MM_BASE_CALL_PATH, "Path", "DBus path of the call", NULL, G_PARAM_READWRITE); g_object_class_install_property (object_class, PROP_PATH, properties[PROP_PATH]); properties[PROP_MODEM] = g_param_spec_object (MM_BASE_CALL_MODEM, "Modem", "The Modem which owns this call", MM_TYPE_BASE_MODEM, G_PARAM_READWRITE); g_object_class_install_property (object_class, PROP_MODEM, properties[PROP_MODEM]); properties[PROP_SKIP_INCOMING_TIMEOUT] = g_param_spec_boolean (MM_BASE_CALL_SKIP_INCOMING_TIMEOUT, "Skip incoming timeout", "There is no need to setup a timeout for incoming calls", FALSE, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY); g_object_class_install_property (object_class, PROP_SKIP_INCOMING_TIMEOUT, properties[PROP_SKIP_INCOMING_TIMEOUT]); properties[PROP_SUPPORTS_DIALING_TO_RINGING] = g_param_spec_boolean (MM_BASE_CALL_SUPPORTS_DIALING_TO_RINGING, "Dialing to ringing", "Whether the call implementation reports dialing to ringing state updates", FALSE, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY); g_object_class_install_property (object_class, PROP_SUPPORTS_DIALING_TO_RINGING, properties[PROP_SUPPORTS_DIALING_TO_RINGING]); properties[PROP_SUPPORTS_RINGING_TO_ACTIVE] = g_param_spec_boolean (MM_BASE_CALL_SUPPORTS_RINGING_TO_ACTIVE, "Ringing to active", "Whether the call implementation reports ringing to active state updates", FALSE, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY); g_object_class_install_property (object_class, PROP_SUPPORTS_RINGING_TO_ACTIVE, properties[PROP_SUPPORTS_RINGING_TO_ACTIVE]); } ModemManager-1.23.4-dev/src/mm-base-call.h000066400000000000000000000141641456466623000201250ustar00rootroot00000000000000/* -*- 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) 2015 Riccardo Vangelisti * Copyright (C) 2019 Purism SPC */ #ifndef MM_BASE_CALL_H #define MM_BASE_CALL_H #include #include #define _LIBMM_INSIDE_MM #include #include "mm-base-modem.h" #include "mm-call-audio-format.h" #define MM_TYPE_BASE_CALL (mm_base_call_get_type ()) #define MM_BASE_CALL(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_BASE_CALL, MMBaseCall)) #define MM_BASE_CALL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_BASE_CALL, MMBaseCallClass)) #define MM_IS_BASE_CALL(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_BASE_CALL)) #define MM_IS_BASE_CALL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_BASE_CALL)) #define MM_BASE_CALL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_BASE_CALL, MMBaseCallClass)) typedef struct _MMBaseCall MMBaseCall; typedef struct _MMBaseCallClass MMBaseCallClass; typedef struct _MMBaseCallPrivate MMBaseCallPrivate; #define MM_BASE_CALL_PATH "call-path" #define MM_BASE_CALL_CONNECTION "call-connection" #define MM_BASE_CALL_MODEM "call-modem" #define MM_BASE_CALL_SKIP_INCOMING_TIMEOUT "call-skip-incoming-timeout" #define MM_BASE_CALL_SUPPORTS_DIALING_TO_RINGING "call-supports-dialing-to-ringing" #define MM_BASE_CALL_SUPPORTS_RINGING_TO_ACTIVE "call-supports-ringing-to-active" struct _MMBaseCall { MmGdbusCallSkeleton parent; MMBaseCallPrivate *priv; }; struct _MMBaseCallClass { MmGdbusCallSkeletonClass parent; /* Start the call */ void (* start) (MMBaseCall *self, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); gboolean (* start_finish) (MMBaseCall *self, GAsyncResult *res, GError **error); /* Accept the call */ void (* accept) (MMBaseCall *self, GAsyncReadyCallback callback, gpointer user_data); gboolean (* accept_finish) (MMBaseCall *self, GAsyncResult *res, GError **error); /* Deflect the call */ void (* deflect) (MMBaseCall *self, const gchar *number, GAsyncReadyCallback callback, gpointer user_data); gboolean (* deflect_finish) (MMBaseCall *self, GAsyncResult *res, GError **error); /* Hangup the call */ void (* hangup) (MMBaseCall *self, GAsyncReadyCallback callback, gpointer user_data); gboolean (* hangup_finish) (MMBaseCall *self, GAsyncResult *res, GError **error); /* Send a DTMF tone */ void (* send_dtmf) (MMBaseCall *self, const gchar *dtmf, GAsyncReadyCallback callback, gpointer user_data); gboolean (* send_dtmf_finish) (MMBaseCall *self, GAsyncResult *res, GError **error); }; GType mm_base_call_get_type (void); G_DEFINE_AUTOPTR_CLEANUP_FUNC (MMBaseCall, g_object_unref) /* This one can be overriden by plugins */ MMBaseCall *mm_base_call_new (MMBaseModem *modem, MMCallDirection direction, const gchar *number, gboolean skip_incoming_timeout, gboolean supports_dialing_to_ringing, gboolean supports_ringing_to_active); void mm_base_call_export (MMBaseCall *self); void mm_base_call_unexport (MMBaseCall *self); const gchar *mm_base_call_get_path (MMBaseCall *self); const gchar *mm_base_call_get_number (MMBaseCall *self); MMCallDirection mm_base_call_get_direction (MMBaseCall *self); MMCallState mm_base_call_get_state (MMBaseCall *self); guint mm_base_call_get_index (MMBaseCall *self); gboolean mm_base_call_get_multiparty (MMBaseCall *self); void mm_base_call_set_number (MMBaseCall *self, const gchar *number); void mm_base_call_set_index (MMBaseCall *self, guint index); void mm_base_call_set_multiparty (MMBaseCall *self, gboolean multiparty); void mm_base_call_change_state (MMBaseCall *self, MMCallState new_state, MMCallStateReason reason); void mm_base_call_change_audio_settings (MMBaseCall *self, MMPort *audio_port, MMCallAudioFormat *audio_format); void mm_base_call_received_dtmf (MMBaseCall *self, const gchar *dtmf); void mm_base_call_incoming_refresh (MMBaseCall *self); #endif /* MM_BASE_CALL_H */ ModemManager-1.23.4-dev/src/mm-base-manager.c000066400000000000000000001746621456466623000206310ustar00rootroot00000000000000/* -*- 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 - 2012 Red Hat, Inc. * Copyright (C) 2011 - 2012 Google, Inc * Copyright (C) 2016 Velocloud, Inc. * Copyright (C) 2011 - 2016 Aleksander Morgado */ #include #include #include #include #if defined WITH_QMI # include #endif #if defined WITH_QRTR # include "mm-kernel-device-qrtr.h" # include "mm-qrtr-bus-watcher.h" #endif #if defined WITH_UDEV # include "mm-kernel-device-udev.h" #endif #include "mm-kernel-device-generic.h" #include #include #include #include "mm-error-helpers.h" #include #if defined WITH_TESTS # include #endif #include "mm-context.h" #include "mm-base-manager.h" #include "mm-daemon-enums-types.h" #include "mm-device.h" #include "mm-plugin-manager.h" #include "mm-auth-provider.h" #include "mm-plugin.h" #include "mm-filter.h" #include "mm-log-object.h" #include "mm-base-modem.h" #include "mm-iface-modem.h" static void initable_iface_init (GInitableIface *iface); static void log_object_iface_init (MMLogObjectInterface *iface); G_DEFINE_TYPE_EXTENDED (MMBaseManager, mm_base_manager, MM_GDBUS_TYPE_ORG_FREEDESKTOP_MODEM_MANAGER1_SKELETON, 0, G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, initable_iface_init) G_IMPLEMENT_INTERFACE (MM_TYPE_LOG_OBJECT, log_object_iface_init)) enum { PROP_0, PROP_CONNECTION, PROP_AUTO_SCAN, PROP_FILTER_POLICY, #if !defined WITH_BUILTIN_PLUGINS PROP_PLUGIN_DIR, #endif PROP_INITIAL_KERNEL_EVENTS, #if defined WITH_TESTS PROP_ENABLE_TEST, #endif LAST_PROP }; struct _MMBaseManagerPrivate { /* The connection to the system bus */ GDBusConnection *connection; /* Whether auto-scanning is enabled */ gboolean auto_scan; /* Filter policy (mask of enabled rules) */ MMFilterRule filter_policy; #if !defined WITH_BUILTIN_PLUGINS /* Path to look for plugins */ gchar *plugin_dir; #endif /* Path to the list of initial kernel events */ gchar *initial_kernel_events; /* The authorization provider */ MMAuthProvider *authp; GCancellable *authp_cancellable; /* The Plugin Manager object */ MMPluginManager *plugin_manager; /* The port/device filter */ MMFilter *filter; /* The container of devices being prepared */ GHashTable *devices; /* The Object Manager server */ GDBusObjectManagerServer *object_manager; /* The map of inhibited devices */ GHashTable *inhibited_devices; #if defined WITH_TESTS /* Whether the test interface is enabled */ gboolean enable_test; /* The Test interface support */ MmGdbusTest *test_skeleton; #endif #if defined WITH_UDEV /* The UDev client */ GUdevClient *udev; #endif #if defined WITH_QRTR /* The Qrtr Bus Watcher */ MMQrtrBusWatcher *qrtr_bus_watcher; #endif }; /*****************************************************************************/ static MMDevice * find_device_by_modem (MMBaseManager *manager, MMBaseModem *modem) { GHashTableIter iter; gpointer key, value; g_hash_table_iter_init (&iter, manager->priv->devices); while (g_hash_table_iter_next (&iter, &key, &value)) { MMDevice *candidate = MM_DEVICE (value); if (modem == mm_device_peek_modem (candidate)) return candidate; } return NULL; } static MMDevice * find_device_by_port (MMBaseManager *manager, MMKernelDevice *port) { GHashTableIter iter; gpointer key, value; g_hash_table_iter_init (&iter, manager->priv->devices); while (g_hash_table_iter_next (&iter, &key, &value)) { MMDevice *candidate = MM_DEVICE (value); if (mm_device_owns_port (candidate, port)) return candidate; } return NULL; } static MMDevice * find_device_by_port_name (MMBaseManager *manager, const gchar *subsystem, const gchar *name) { GHashTableIter iter; gpointer key, value; g_hash_table_iter_init (&iter, manager->priv->devices); while (g_hash_table_iter_next (&iter, &key, &value)) { MMDevice *candidate = MM_DEVICE (value); if (mm_device_owns_port_name (candidate, subsystem, name)) return candidate; } return NULL; } static MMDevice * find_device_by_physdev_uid (MMBaseManager *self, const gchar *physdev_uid) { return g_hash_table_lookup (self->priv->devices, physdev_uid); } /*****************************************************************************/ typedef struct { MMBaseManager *self; MMDevice *device; } FindDeviceSupportContext; static void find_device_support_context_free (FindDeviceSupportContext *ctx) { g_object_unref (ctx->self); g_object_unref (ctx->device); g_slice_free (FindDeviceSupportContext, ctx); } static void device_support_check_ready (MMPluginManager *plugin_manager, GAsyncResult *res, FindDeviceSupportContext *ctx) { GError *error = NULL; MMPlugin *plugin; /* If the device support check fails, either with an error, or afterwards * when trying to create a modem object, we must remove the MMDevice from * the tracking table of devices, so that a manual scan request afterwards * re-scans all ports. */ /* Receive plugin result from the plugin manager */ plugin = mm_plugin_manager_device_support_check_finish (plugin_manager, res, &error); if (!plugin) { mm_obj_msg (ctx->self, "couldn't check support for device '%s': %s", mm_device_get_uid (ctx->device), error->message); g_error_free (error); g_hash_table_remove (ctx->self->priv->devices, mm_device_get_uid (ctx->device)); find_device_support_context_free (ctx); return; } /* Set the plugin as the one expected in the device */ mm_device_set_plugin (ctx->device, G_OBJECT (plugin)); g_object_unref (plugin); if (!mm_device_create_modem (ctx->device, &error)) { mm_obj_warn (ctx->self, "couldn't create modem for device '%s': %s", mm_device_get_uid (ctx->device), error->message); g_error_free (error); g_hash_table_remove (ctx->self->priv->devices, mm_device_get_uid (ctx->device)); find_device_support_context_free (ctx); return; } /* Modem now created */ mm_obj_msg (ctx->self, "modem for device '%s' successfully created", mm_device_get_uid (ctx->device)); find_device_support_context_free (ctx); } static gboolean is_device_inhibited (MMBaseManager *self, const gchar *physdev_uid); static void device_inhibited_track_port (MMBaseManager *self, const gchar *physdev_uid, MMKernelDevice *port, gboolean manual_scan); static void device_inhibited_untrack_port (MMBaseManager *self, const gchar *subsystem, const gchar *name); static void device_removed (MMBaseManager *self, const gchar *subsystem, const gchar *name) { g_autoptr(MMDevice) device = NULL; g_assert (subsystem); g_assert (name); device = find_device_by_port_name (self, subsystem, name); if (!device) { /* If the device was inhibited and the port is gone, untrack it. * This is only needed for ports that were tracked out of device objects. * In this case we don't rely on the physdev uid, as API-reported * remove kernel events may not include uid. */ device_inhibited_untrack_port (self, subsystem, name); return; } /* The callbacks triggered when the port is released or device support is * cancelled may end up unreffing the device or removing it from the HT, and * so in order to make sure the reference is still valid when we call * support_check_cancel() and g_hash_table_remove(), we hold a full reference * ourselves. */ g_object_ref (device); mm_obj_msg (self, "port %s released by device '%s'", name, mm_device_get_uid (device)); mm_device_release_port_name (device, subsystem, name); /* If port probe list gets empty, remove the device object iself */ if (!mm_device_peek_port_probe_list (device)) { mm_obj_dbg (self, "removing empty device '%s'", mm_device_get_uid (device)); if (mm_plugin_manager_device_support_check_cancel (self->priv->plugin_manager, device)) mm_obj_dbg (self, "device support check has been cancelled"); /* The device may have already been removed from the tracking HT, we * just try to remove it and if it fails, we ignore it */ mm_device_remove_modem (device); g_hash_table_remove (self->priv->devices, mm_device_get_uid (device)); } } static void device_added (MMBaseManager *self, MMKernelDevice *port, gboolean hotplugged, gboolean manual_scan) { MMDevice *device; const gchar *physdev_uid; const gchar *name; g_return_if_fail (port != NULL); name = mm_kernel_device_get_name (port); mm_obj_dbg (self, "adding port %s at sysfs path: %s", name, mm_kernel_device_get_sysfs_path (port)); /* Ignore devices that aren't completely configured by udev yet. If * ModemManager is started in parallel with udev, explicitly requesting * devices may return devices for which not all udev rules have yet been * applied (a bug in udev/gudev). Since we often need those rules to match * the device to a specific ModemManager driver, we need to ensure that all * rules have been processed before handling a device. * * This udev tag applies to each port in a device. In other words, the flag * may be set in some ports, but not in others */ if (!mm_kernel_device_get_property_as_boolean (port, ID_MM_CANDIDATE)) { /* This could mean that device changed, losing its candidate * flags (such as Bluetooth RFCOMM devices upon disconnect. * Try to forget it. */ device_removed (self, mm_kernel_device_get_subsystem (port), name); mm_obj_dbg (self, "port %s not candidate", name); return; } /* Get the port's physical device's uid. All ports of the same physical * device will share the same uid. */ physdev_uid = mm_kernel_device_get_physdev_uid (port); g_assert (physdev_uid); /* If the device is inhibited, do nothing else */ if (is_device_inhibited (self, physdev_uid)) { /* Note: we will not report as hotplugged an inhibited device port * because we don't know what was done with the port out of our * context. */ device_inhibited_track_port (self, physdev_uid, port, manual_scan); return; } /* Run port filter */ if (!mm_filter_port (self->priv->filter, port, manual_scan)) return; /* If already added, ignore new event */ if (find_device_by_port (self, port)) { mm_obj_dbg (self, "port %s already added", name); return; } /* See if we already created an object to handle ports in this device */ device = find_device_by_physdev_uid (self, physdev_uid); if (!device) { const gchar *physdev; FindDeviceSupportContext *ctx; mm_obj_dbg (self, "port %s is first in device %s", name, physdev_uid); physdev = mm_kernel_device_get_physdev_sysfs_path (port); /* Keep the device listed in the Manager */ device = mm_device_new (physdev_uid, physdev, hotplugged, FALSE, self->priv->object_manager); g_hash_table_insert (self->priv->devices, g_strdup (physdev_uid), device); /* Launch device support check */ ctx = g_slice_new (FindDeviceSupportContext); ctx->self = g_object_ref (self); ctx->device = g_object_ref (device); mm_plugin_manager_device_support_check ( self->priv->plugin_manager, device, (GAsyncReadyCallback) device_support_check_ready, ctx); } else mm_obj_dbg (self, "additional port %s in device %s", name, physdev_uid); /* Grab the port in the existing device. */ mm_device_grab_port (device, port); } #if defined WITH_QRTR static void handle_qrtr_device_added (MMBaseManager *self, guint node_id, MMQrtrBusWatcher *bus_watcher) { g_autoptr(MMKernelDevice) kernel_device = NULL; QrtrNode *node; node = mm_qrtr_bus_watcher_peek_node (bus_watcher, node_id); kernel_device = mm_kernel_device_qrtr_new (node); device_added (self, kernel_device, TRUE, FALSE); } static void handle_qrtr_device_removed (MMBaseManager *self, guint node_id) { g_autofree gchar *qrtr_device_name = NULL; qrtr_device_name = mm_kernel_device_qrtr_helper_build_name (node_id); device_removed (self, MM_KERNEL_DEVICE_QRTR_SUBSYSTEM, qrtr_device_name); } #endif static gboolean handle_kernel_event (MMBaseManager *self, MMKernelEventProperties *properties, GError **error) { const gchar *action; const gchar *subsystem; const gchar *name; const gchar *uid; action = mm_kernel_event_properties_get_action (properties); if (!action) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS, "Missing mandatory parameter 'action'"); return FALSE; } if (g_strcmp0 (action, "add") != 0 && g_strcmp0 (action, "remove") != 0) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS, "Invalid 'action' parameter given: '%s' (expected 'add' or 'remove')", action); return FALSE; } subsystem = mm_kernel_event_properties_get_subsystem (properties); if (!subsystem) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS, "Missing mandatory parameter 'subsystem'"); return FALSE; } if (!g_strv_contains (mm_plugin_manager_get_subsystems (self->priv->plugin_manager), subsystem)) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS, "Invalid 'subsystem' parameter given: '%s'", subsystem); return FALSE; } name = mm_kernel_event_properties_get_name (properties); if (!name) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS, "Missing mandatory parameter 'name'"); return FALSE; } uid = mm_kernel_event_properties_get_uid (properties); mm_obj_dbg (self, "kernel event reported:"); mm_obj_dbg (self, " action: %s", action); mm_obj_dbg (self, " subsystem: %s", subsystem); mm_obj_dbg (self, " name: %s", name); mm_obj_dbg (self, " uid: %s", uid ? uid : "n/a"); if (g_strcmp0 (action, "add") == 0) { g_autoptr(MMKernelDevice) kernel_device = NULL; #if defined WITH_UDEV if (!mm_context_get_test_no_udev ()) kernel_device = mm_kernel_device_udev_new_from_properties (self->priv->udev, properties, error); else #endif kernel_device = mm_kernel_device_generic_new (properties, error); if (!kernel_device) return FALSE; device_added (self, kernel_device, TRUE, TRUE); return TRUE; } if (g_strcmp0 (action, "remove") == 0) { device_removed (self, subsystem, name); return TRUE; } g_assert_not_reached (); } #if defined WITH_UDEV static void handle_uevent (MMBaseManager *self, const gchar *action, GUdevDevice *device) { const gchar *subsystem; const gchar *name; subsystem = g_udev_device_get_subsystem (device); name = g_udev_device_get_name (device); /* Valid udev devices must have subsystem and name set; if they don't have * both things, we silently ignore them. */ if (!subsystem || !name) return; if (g_str_equal (action, "add") || g_str_equal (action, "move") || g_str_equal (action, "change")) { g_autoptr(MMKernelDevice) kernel_device = NULL; kernel_device = mm_kernel_device_udev_new (self->priv->udev, device); device_added (self, kernel_device, TRUE, FALSE); return; } if (g_str_equal (action, "remove")) { device_removed (self, subsystem, name); return; } } typedef struct { MMBaseManager *self; GUdevDevice *device; gboolean manual_scan; } StartDeviceAdded; static gboolean start_device_added_idle (StartDeviceAdded *ctx) { const gchar *subsystem; const gchar *name; subsystem = g_udev_device_get_subsystem (ctx->device); name = g_udev_device_get_name (ctx->device); /* Valid udev devices must have subsystem and name set; if they don't have * both things, we silently ignore them. */ if (subsystem && name) { g_autoptr(MMKernelDevice) kernel_device = NULL; kernel_device = mm_kernel_device_udev_new (ctx->self->priv->udev, ctx->device); device_added (ctx->self, kernel_device, FALSE, ctx->manual_scan); } g_object_unref (ctx->self); g_object_unref (ctx->device); g_slice_free (StartDeviceAdded, ctx); return G_SOURCE_REMOVE; } static void start_device_added (MMBaseManager *self, GUdevDevice *device, gboolean manual_scan) { StartDeviceAdded *ctx; ctx = g_slice_new (StartDeviceAdded); ctx->self = g_object_ref (self); ctx->device = g_object_ref (device); ctx->manual_scan = manual_scan; g_idle_add ((GSourceFunc)start_device_added_idle, ctx); } static void process_scan (MMBaseManager *self, gboolean manual_scan) { const gchar **subsystems; guint i; subsystems = mm_plugin_manager_get_subsystems (self->priv->plugin_manager); for (i = 0; subsystems[i]; i++) { GList *devices; GList *iter; devices = g_udev_client_query_by_subsystem (self->priv->udev, subsystems[i]); for (iter = devices; iter; iter = g_list_next (iter)) start_device_added (self, G_UDEV_DEVICE (iter->data), manual_scan); g_list_free_full (devices, g_object_unref); } } #endif static void process_initial_kernel_events (MMBaseManager *self) { gchar *contents = NULL; gchar *line; GError *error = NULL; if (!self->priv->initial_kernel_events) return; if (!g_file_get_contents (self->priv->initial_kernel_events, &contents, NULL, &error)) { mm_obj_warn (self, "couldn't load initial kernel events: %s", error->message); g_error_free (error); return; } line = contents; while (line) { gchar *next; next = strchr (line, '\n'); if (next) { *next = '\0'; next++; } /* ignore empty lines */ if (line[0] != '\0') { MMKernelEventProperties *properties; properties = mm_kernel_event_properties_new_from_string (line, &error); if (!properties) { mm_obj_warn (self, "couldn't parse line '%s' as initial kernel event %s", line, error->message); g_clear_error (&error); } else if (!handle_kernel_event (self, properties, &error)) { mm_obj_warn (self, "couldn't process line '%s' as initial kernel event %s", line, error->message); g_clear_error (&error); } else mm_obj_dbg (self, "processed initial kernel event:' %s'", line); g_clear_object (&properties); } line = next; } g_free (contents); } void mm_base_manager_start (MMBaseManager *self, gboolean manual_scan) { g_return_if_fail (self != NULL); g_return_if_fail (MM_IS_BASE_MANAGER (self)); if (!self->priv->auto_scan && !manual_scan) { /* If we have a list of initial kernel events, process it now */ process_initial_kernel_events (self); return; } #if defined WITH_UDEV if (!mm_context_get_test_no_udev ()) { mm_obj_dbg (self, "starting %s device scan...", manual_scan ? "manual" : "automatic"); process_scan (self, manual_scan); mm_obj_dbg (self, "finished device scan..."); } else #endif mm_obj_dbg (self, "unsupported %s device scan...", manual_scan ? "manual" : "automatic"); } /*****************************************************************************/ typedef struct { MMBaseManager *self; gboolean low_power; gboolean remove; } DisableContext; static void disable_context_free (DisableContext *ctx) { g_object_unref (ctx->self); g_slice_free (DisableContext, ctx); } static void remove_device_after_disable (MMBaseModem *modem, DisableContext *ctx) { MMDevice *device; device = find_device_by_modem (ctx->self, modem); if (device) { g_cancellable_cancel (mm_base_modem_peek_cancellable (modem)); mm_device_remove_modem (device); g_hash_table_remove (ctx->self->priv->devices, mm_device_get_uid (device)); } disable_context_free (ctx); } static void shutdown_low_power_ready (MMIfaceModem *modem, GAsyncResult *res, DisableContext *ctx) { g_autoptr(GError) error = NULL; if (!mm_iface_modem_set_power_state_finish (modem, res, &error)) mm_obj_info (ctx->self, "changing to low power state failed: %s", error->message); if (ctx->remove) remove_device_after_disable (MM_BASE_MODEM (modem), ctx); else disable_context_free (ctx); } static void shutdown_disable_ready (MMBaseModem *modem, GAsyncResult *res, DisableContext *ctx) { g_autoptr(GError) error = NULL; /* We don't care about errors disabling at this point */ if (!mm_base_modem_disable_finish (modem, res, &error)) { mm_obj_info (ctx->self, "disabling modem failed: %s", error->message); } /* Bring the modem to low power mode if requested */ else if (ctx->low_power) { mm_iface_modem_set_power_state (MM_IFACE_MODEM (modem), MM_MODEM_POWER_STATE_LOW, (GAsyncReadyCallback)shutdown_low_power_ready, ctx); return; } if (ctx->remove) remove_device_after_disable (modem, ctx); else disable_context_free (ctx); } static void foreach_disable (gpointer key, MMDevice *device, DisableContext *foreach_ctx) { MMBaseModem *modem; DisableContext *ctx; modem = mm_device_peek_modem (device); if (!modem) return; ctx = g_slice_new0 (DisableContext); ctx->self = g_object_ref (foreach_ctx->self); ctx->low_power = foreach_ctx->low_power; ctx->remove = foreach_ctx->remove; mm_base_modem_disable (modem, (GAsyncReadyCallback)shutdown_disable_ready, ctx); } static gboolean foreach_remove (gpointer key, MMDevice *device, MMBaseManager *self) { MMBaseModem *modem; modem = mm_device_peek_modem (device); if (modem) g_cancellable_cancel (mm_base_modem_peek_cancellable (modem)); mm_device_remove_modem (device); return TRUE; } void mm_base_manager_shutdown (MMBaseManager *self, gboolean disable, gboolean low_power, gboolean remove) { g_return_if_fail (self != NULL); g_return_if_fail (MM_IS_BASE_MANAGER (self)); /* Cancel all ongoing auth requests */ g_cancellable_cancel (self->priv->authp_cancellable); if (disable) { DisableContext foreach_ctx = { .self = self, .low_power = low_power, .remove = remove, }; g_hash_table_foreach (self->priv->devices, (GHFunc)foreach_disable, &foreach_ctx); /* Disabling may take a few iterations of the mainloop, so the caller * has to iterate the mainloop until all devices have been disabled and * removed. */ return; } if (remove) { /* Otherwise, just remove directly */ g_hash_table_foreach_remove (self->priv->devices, (GHRFunc)foreach_remove, self); } } guint32 mm_base_manager_num_modems (MMBaseManager *self) { GHashTableIter iter; gpointer key, value; guint32 n; g_return_val_if_fail (self != NULL, 0); g_return_val_if_fail (MM_IS_BASE_MANAGER (self), 0); n = 0; g_hash_table_iter_init (&iter, self->priv->devices); while (g_hash_table_iter_next (&iter, &key, &value)) { n += !!mm_device_peek_modem (MM_DEVICE (value)); } return n; } /*****************************************************************************/ /* Quick resume synchronization */ #if defined WITH_SUSPEND_RESUME static void base_modem_sync_ready (MMBaseModem *self, GAsyncResult *res, gpointer user_data) { g_autoptr(GError) error = NULL; mm_base_modem_sync_finish (self, res, &error); if (error) { mm_obj_warn (self, "synchronization failed: %s", error->message); return; } mm_obj_msg (self, "synchronization finished"); } void mm_base_manager_sync (MMBaseManager *self) { GHashTableIter iter; gpointer key, value; g_return_if_fail (self != NULL); g_return_if_fail (MM_IS_BASE_MANAGER (self)); /* Refresh each device */ g_hash_table_iter_init (&iter, self->priv->devices); while (g_hash_table_iter_next (&iter, &key, &value)) { MMBaseModem *modem; modem = mm_device_peek_modem (MM_DEVICE (value)); /* We just want to start the synchronization, we don't need the result */ if (modem) mm_base_modem_sync (modem, (GAsyncReadyCallback)base_modem_sync_ready, NULL); } } #endif /*****************************************************************************/ /* Set logging */ typedef struct { MMBaseManager *self; GDBusMethodInvocation *invocation; gchar *level; } SetLoggingContext; static void set_logging_context_free (SetLoggingContext *ctx) { g_object_unref (ctx->invocation); g_object_unref (ctx->self); g_free (ctx->level); g_free (ctx); } static void set_logging_auth_ready (MMAuthProvider *authp, GAsyncResult *res, SetLoggingContext *ctx) { GError *error = NULL; if (!mm_auth_provider_authorize_finish (authp, res, &error)) mm_dbus_method_invocation_take_error (ctx->invocation, error); else if (!mm_log_set_level (ctx->level, &error)) mm_dbus_method_invocation_take_error (ctx->invocation, error); else { mm_obj_msg (ctx->self, "logging: level '%s'", ctx->level); mm_gdbus_org_freedesktop_modem_manager1_complete_set_logging ( MM_GDBUS_ORG_FREEDESKTOP_MODEM_MANAGER1 (ctx->self), ctx->invocation); } set_logging_context_free (ctx); } static gboolean handle_set_logging (MmGdbusOrgFreedesktopModemManager1 *manager, GDBusMethodInvocation *invocation, const gchar *level) { SetLoggingContext *ctx; ctx = g_new0 (SetLoggingContext, 1); ctx->self = MM_BASE_MANAGER (g_object_ref (manager)); ctx->invocation = g_object_ref (invocation); ctx->level = g_strdup (level); mm_auth_provider_authorize (ctx->self->priv->authp, invocation, MM_AUTHORIZATION_MANAGER_CONTROL, ctx->self->priv->authp_cancellable, (GAsyncReadyCallback)set_logging_auth_ready, ctx); return TRUE; } /*****************************************************************************/ /* Manual scan */ typedef struct { MMBaseManager *self; GDBusMethodInvocation *invocation; } ScanDevicesContext; static void scan_devices_context_free (ScanDevicesContext *ctx) { g_object_unref (ctx->invocation); g_object_unref (ctx->self); g_free (ctx); } static void scan_devices_auth_ready (MMAuthProvider *authp, GAsyncResult *res, ScanDevicesContext *ctx) { GError *error = NULL; if (!mm_auth_provider_authorize_finish (authp, res, &error)) mm_dbus_method_invocation_take_error (ctx->invocation, error); else { #if defined WITH_UDEV if (!mm_context_get_test_no_udev ()) { mm_obj_info (ctx->self, "processing user request to launch device scan"); mm_base_manager_start (MM_BASE_MANAGER (ctx->self), TRUE); mm_gdbus_org_freedesktop_modem_manager1_complete_scan_devices ( MM_GDBUS_ORG_FREEDESKTOP_MODEM_MANAGER1 (ctx->self), ctx->invocation); } else #endif mm_dbus_method_invocation_return_error_literal (ctx->invocation, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "Cannot request manual scan of devices: unsupported"); } scan_devices_context_free (ctx); } static gboolean handle_scan_devices (MmGdbusOrgFreedesktopModemManager1 *manager, GDBusMethodInvocation *invocation) { ScanDevicesContext *ctx; ctx = g_new (ScanDevicesContext, 1); ctx->self = MM_BASE_MANAGER (g_object_ref (manager)); ctx->invocation = g_object_ref (invocation); mm_auth_provider_authorize (ctx->self->priv->authp, invocation, MM_AUTHORIZATION_MANAGER_CONTROL, ctx->self->priv->authp_cancellable, (GAsyncReadyCallback)scan_devices_auth_ready, ctx); return TRUE; } /*****************************************************************************/ typedef struct { MMBaseManager *self; GDBusMethodInvocation *invocation; GVariant *dictionary; } ReportKernelEventContext; static void report_kernel_event_context_free (ReportKernelEventContext *ctx) { g_object_unref (ctx->invocation); g_object_unref (ctx->self); g_variant_unref (ctx->dictionary); g_slice_free (ReportKernelEventContext, ctx); } static void report_kernel_event_auth_ready (MMAuthProvider *authp, GAsyncResult *res, ReportKernelEventContext *ctx) { GError *error = NULL; MMKernelEventProperties *properties = NULL; if (!mm_auth_provider_authorize_finish (authp, res, &error)) goto out; #if defined WITH_UDEV if (!mm_context_get_test_no_udev () && ctx->self->priv->auto_scan) { error = g_error_new_literal (MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "Cannot report kernel event: " "udev monitoring already in place"); goto out; } #endif properties = mm_kernel_event_properties_new_from_dictionary (ctx->dictionary, &error); if (!properties) goto out; handle_kernel_event (ctx->self, properties, &error); out: if (error) { mm_obj_warn (ctx->self, "couldn't handle kernel event: %s", error->message); mm_dbus_method_invocation_take_error (ctx->invocation, error); } else mm_gdbus_org_freedesktop_modem_manager1_complete_report_kernel_event ( MM_GDBUS_ORG_FREEDESKTOP_MODEM_MANAGER1 (ctx->self), ctx->invocation); if (properties) g_object_unref (properties); report_kernel_event_context_free (ctx); } static gboolean handle_report_kernel_event (MmGdbusOrgFreedesktopModemManager1 *manager, GDBusMethodInvocation *invocation, GVariant *dictionary) { ReportKernelEventContext *ctx; ctx = g_slice_new0 (ReportKernelEventContext); ctx->self = MM_BASE_MANAGER (g_object_ref (manager)); ctx->invocation = g_object_ref (invocation); ctx->dictionary = g_variant_ref (dictionary); mm_auth_provider_authorize (ctx->self->priv->authp, invocation, MM_AUTHORIZATION_MANAGER_CONTROL, ctx->self->priv->authp_cancellable, (GAsyncReadyCallback)report_kernel_event_auth_ready, ctx); return TRUE; } /*****************************************************************************/ /* Inhibit or uninhibit device */ typedef struct { MMKernelDevice *kernel_port; gboolean manual_scan; } InhibitedDevicePortInfo; static void inhibited_device_port_info_free (InhibitedDevicePortInfo *port_info) { g_object_unref (port_info->kernel_port); g_slice_free (InhibitedDevicePortInfo, port_info); } typedef struct { gchar *sender; guint name_lost_id; GList *port_infos; } InhibitedDeviceInfo; static void inhibited_device_info_free (InhibitedDeviceInfo *info) { g_list_free_full (info->port_infos, (GDestroyNotify)inhibited_device_port_info_free); g_bus_unwatch_name (info->name_lost_id); g_free (info->sender); g_slice_free (InhibitedDeviceInfo, info); } static InhibitedDeviceInfo * find_inhibited_device_info_by_physdev_uid (MMBaseManager *self, const gchar *physdev_uid) { return (physdev_uid ? g_hash_table_lookup (self->priv->inhibited_devices, physdev_uid) : NULL); } static gboolean is_device_inhibited (MMBaseManager *self, const gchar *physdev_uid) { return !!find_inhibited_device_info_by_physdev_uid (self, physdev_uid); } static void device_inhibited_untrack_port (MMBaseManager *self, const gchar *subsystem, const gchar *name) { GHashTableIter iter; gchar *uid; InhibitedDeviceInfo *info; g_hash_table_iter_init (&iter, self->priv->inhibited_devices); while (g_hash_table_iter_next (&iter, (gpointer)&uid, (gpointer)&info)) { GList *l; for (l = info->port_infos; l; l = g_list_next (l)) { InhibitedDevicePortInfo *port_info; port_info = (InhibitedDevicePortInfo *)(l->data); if ((g_strcmp0 (subsystem, mm_kernel_device_get_subsystem (port_info->kernel_port)) == 0) && (g_strcmp0 (name, mm_kernel_device_get_name (port_info->kernel_port)) == 0)) { mm_obj_dbg (self, "released port %s while inhibited", name); inhibited_device_port_info_free (port_info); info->port_infos = g_list_delete_link (info->port_infos, l); return; } } } } static void device_inhibited_track_port (MMBaseManager *self, const gchar *physdev_uid, MMKernelDevice *kernel_port, gboolean manual_scan) { InhibitedDevicePortInfo *port_info; InhibitedDeviceInfo *info; GList *l; info = find_inhibited_device_info_by_physdev_uid (self, physdev_uid); g_assert (info); for (l = info->port_infos; l; l = g_list_next (l)) { /* If device is already tracked, just overwrite the manual scan info */ port_info = (InhibitedDevicePortInfo *)(l->data); if (mm_kernel_device_cmp (port_info->kernel_port, kernel_port)) { port_info->manual_scan = manual_scan; return; } } mm_obj_dbg (self, "added port %s while inhibited", mm_kernel_device_get_name (kernel_port)); port_info = g_slice_new0 (InhibitedDevicePortInfo); port_info->kernel_port = g_object_ref (kernel_port); port_info->manual_scan = manual_scan; info->port_infos = g_list_append (info->port_infos, port_info); } typedef struct { MMBaseManager *self; gchar *uid; } InhibitSenderLostContext; static void inhibit_sender_lost_context_free (InhibitSenderLostContext *lost_ctx) { g_free (lost_ctx->uid); g_slice_free (InhibitSenderLostContext, lost_ctx); } static void remove_device_inhibition (MMBaseManager *self, const gchar *uid) { InhibitedDeviceInfo *info; MMDevice *device; GList *port_infos; info = find_inhibited_device_info_by_physdev_uid (self, uid); g_assert (info); device = find_device_by_physdev_uid (self, uid); port_infos = info->port_infos; info->port_infos = NULL; g_hash_table_remove (self->priv->inhibited_devices, uid); /* If any port info exists, we require explicit port probing that will be * triggered via the artificial port notifications emitted with the * device_added() calls */ if (port_infos) { GList *l; /* A device may exist at this point if e.g. not all ports were * removed during the inhibition (i.e. the MMDevice was never fully * removed) and new ports were then added while inhibited. In this * case, we must fake all ports going away so that the MMDevice gets * completely removed, otherwise the plugin manager won't start a new * device probing task and therefore no port probing tasks. */ if (device) { GList *readded_port_infos = NULL; GList *leftover_ports; /* Create a new list of inhibited device port infos from the existing * port probes */ leftover_ports = mm_device_peek_port_probe_list (device); for (l = leftover_ports; l; l = g_list_next (l)) { InhibitedDevicePortInfo *port_info; MMPortProbe *port_probe; port_probe = MM_PORT_PROBE (l->data); port_info = g_slice_new0 (InhibitedDevicePortInfo); port_info->kernel_port = mm_port_probe_get_port (port_probe); port_info->manual_scan = TRUE; readded_port_infos = g_list_append (readded_port_infos, port_info); } /* Now, explicitly request to remove all ports, the device should go * away as well while doing so. */ for (l = readded_port_infos; l; l = g_list_next (l)) { InhibitedDevicePortInfo *port_info; port_info = (InhibitedDevicePortInfo *)(l->data); mm_obj_msg (self, "fake releasing port %s/%s during uninhibition...", mm_kernel_device_get_subsystem (port_info->kernel_port), mm_kernel_device_get_name (port_info->kernel_port)); device_removed (self, mm_kernel_device_get_subsystem (port_info->kernel_port), mm_kernel_device_get_name (port_info->kernel_port)); } /* At this point, the device should have gone completely */ g_assert (!find_device_by_physdev_uid (self, uid)); /* Added the ports to re-add in the pending list */ port_infos = g_list_concat (port_infos, readded_port_infos); } /* Report as added all port infos that we had tracked while the * device was inhibited. We can only report the added port after * having removed the entry from the inhibited devices tracking * table. */ for (l = port_infos; l; l = g_list_next (l)) { InhibitedDevicePortInfo *port_info; port_info = (InhibitedDevicePortInfo *)(l->data); device_added (self, port_info->kernel_port, FALSE, port_info->manual_scan); } g_list_free_full (port_infos, (GDestroyNotify)inhibited_device_port_info_free); return; } /* The device may be totally gone from the system while we were * keeping the inhibition, so do not error out if not found. */ if (device) { GError *error = NULL; /* Uninhibit device, which will create and expose the modem object */ if (!mm_device_uninhibit (device, &error)) { mm_obj_warn (self, "couldn't uninhibit device: %s", error->message); g_error_free (error); } } } static void inhibit_sender_lost (GDBusConnection *connection, const gchar *sender_name, InhibitSenderLostContext *lost_ctx) { mm_obj_msg (lost_ctx->self, "device inhibition teardown for uid '%s' (owner disappeared from bus)", lost_ctx->uid); remove_device_inhibition (lost_ctx->self, lost_ctx->uid); } typedef struct { MMBaseManager *self; GDBusMethodInvocation *invocation; gchar *uid; gboolean inhibit; } InhibitDeviceContext; static void inhibit_device_context_free (InhibitDeviceContext *ctx) { g_object_unref (ctx->invocation); g_object_unref (ctx->self); g_free (ctx->uid); g_slice_free (InhibitDeviceContext, ctx); } static void device_inhibit_ready (MMDevice *device, GAsyncResult *res, InhibitDeviceContext *ctx) { InhibitSenderLostContext *lost_ctx; InhibitedDeviceInfo *info; GError *error = NULL; if (!mm_device_inhibit_finish (device, res, &error)) { mm_dbus_method_invocation_take_error (ctx->invocation, error); inhibit_device_context_free (ctx); return; } info = g_slice_new0 (InhibitedDeviceInfo); info->sender = g_strdup (g_dbus_method_invocation_get_sender (ctx->invocation)); /* This context will exist as long as the sender name watcher exists, * i.e. as long as the associated InhibitDeviceInfo exists. We don't need * an extra reference of self here because these contexts are stored within * self, and therefore bound to its lifetime. */ lost_ctx = g_slice_new0 (InhibitSenderLostContext); lost_ctx->self = ctx->self; lost_ctx->uid = g_strdup (ctx->uid); info->name_lost_id = g_bus_watch_name_on_connection (g_dbus_method_invocation_get_connection (ctx->invocation), info->sender, G_BUS_NAME_WATCHER_FLAGS_NONE, NULL, (GBusNameVanishedCallback)inhibit_sender_lost, lost_ctx, (GDestroyNotify)inhibit_sender_lost_context_free); g_hash_table_insert (ctx->self->priv->inhibited_devices, g_strdup (ctx->uid), info); mm_obj_msg (ctx->self, "device inhibition setup for uid '%s'", ctx->uid); mm_gdbus_org_freedesktop_modem_manager1_complete_inhibit_device ( MM_GDBUS_ORG_FREEDESKTOP_MODEM_MANAGER1 (ctx->self), ctx->invocation); inhibit_device_context_free (ctx); } static void base_manager_inhibit_device (InhibitDeviceContext *ctx) { MMDevice *device; device = find_device_by_physdev_uid (ctx->self, ctx->uid); if (!device) { mm_dbus_method_invocation_return_error (ctx->invocation, MM_CORE_ERROR, MM_CORE_ERROR_NOT_FOUND, "No device found with uid '%s'", ctx->uid); inhibit_device_context_free (ctx); return; } if (mm_device_get_inhibited (device)) { mm_dbus_method_invocation_return_error (ctx->invocation, MM_CORE_ERROR, MM_CORE_ERROR_IN_PROGRESS, "Device '%s' is already inhibited", ctx->uid); inhibit_device_context_free (ctx); return; } mm_obj_info (ctx->self, "processing user request to inhibit uid '%s'", ctx->uid); mm_device_inhibit (device, (GAsyncReadyCallback) device_inhibit_ready, ctx); } static void base_manager_uninhibit_device (InhibitDeviceContext *ctx) { InhibitedDeviceInfo *info; const gchar *sender; /* Validate uninhibit request */ sender = g_dbus_method_invocation_get_sender (ctx->invocation); info = find_inhibited_device_info_by_physdev_uid (ctx->self, ctx->uid); if (!info || (g_strcmp0 (info->sender, sender) != 0)) { mm_dbus_method_invocation_return_error (ctx->invocation, MM_CORE_ERROR, MM_CORE_ERROR_NOT_FOUND, "No inhibition found for uid '%s'", ctx->uid); inhibit_device_context_free (ctx); return; } mm_obj_info (ctx->self, "processing user request to uninhibit uid '%s'", ctx->uid); remove_device_inhibition (ctx->self, ctx->uid); mm_obj_msg (ctx->self, "device inhibition teardown for uid '%s'", ctx->uid); mm_gdbus_org_freedesktop_modem_manager1_complete_inhibit_device ( MM_GDBUS_ORG_FREEDESKTOP_MODEM_MANAGER1 (ctx->self), ctx->invocation); inhibit_device_context_free (ctx); } static void inhibit_device_auth_ready (MMAuthProvider *authp, GAsyncResult *res, InhibitDeviceContext *ctx) { GError *error = NULL; if (!mm_auth_provider_authorize_finish (authp, res, &error)) { mm_dbus_method_invocation_take_error (ctx->invocation, error); inhibit_device_context_free (ctx); return; } if (ctx->inhibit) base_manager_inhibit_device (ctx); else base_manager_uninhibit_device (ctx); } static gboolean handle_inhibit_device (MmGdbusOrgFreedesktopModemManager1 *manager, GDBusMethodInvocation *invocation, const gchar *uid, gboolean inhibit) { InhibitDeviceContext *ctx; ctx = g_slice_new0 (InhibitDeviceContext); ctx->self = MM_BASE_MANAGER (g_object_ref (manager)); ctx->invocation = g_object_ref (invocation); ctx->uid = g_strdup (uid); ctx->inhibit = inhibit; mm_auth_provider_authorize (ctx->self->priv->authp, invocation, MM_AUTHORIZATION_MANAGER_CONTROL, ctx->self->priv->authp_cancellable, (GAsyncReadyCallback)inhibit_device_auth_ready, ctx); return TRUE; } /*****************************************************************************/ /* Test profile setup */ #if defined WITH_TESTS static gboolean handle_set_profile (MmGdbusTest *skeleton, GDBusMethodInvocation *invocation, const gchar *id, const gchar *plugin_name, const gchar *const *ports, MMBaseManager *self) { MMPlugin *plugin; MMDevice *device; gchar *physdev_uid; gchar *physdev = NULL; GError *error = NULL; mm_obj_msg (self, "test profile set to: '%s'", id); /* Create device and keep it listed in the Manager */ physdev_uid = g_strdup_printf ("/virtual/%s", id); device = mm_device_new (physdev_uid, physdev, TRUE, TRUE, self->priv->object_manager); g_hash_table_insert (self->priv->devices, physdev_uid, device); /* Grab virtual ports */ mm_device_virtual_grab_ports (device, (const gchar **)ports); /* Set plugin to use */ plugin = mm_plugin_manager_peek_plugin (self->priv->plugin_manager, plugin_name); if (!plugin) { error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_NOT_FOUND, "Requested plugin '%s' not found", plugin_name); mm_obj_warn (self, "couldn't set plugin for virtual device '%s': %s", mm_device_get_uid (device), error->message); goto out; } mm_device_set_plugin (device, G_OBJECT (plugin)); /* Create modem */ if (!mm_device_create_modem (device, &error)) { mm_obj_warn (self, "couldn't create modem for virtual device '%s': %s", mm_device_get_uid (device), error->message); goto out; } mm_obj_msg (self, "modem for virtual device '%s' successfully created", mm_device_get_uid (device)); out: if (error) { mm_device_remove_modem (device); g_hash_table_remove (self->priv->devices, mm_device_get_uid (device)); mm_dbus_method_invocation_return_gerror (invocation, error); g_error_free (error); } else mm_gdbus_test_complete_set_profile (skeleton, invocation); return TRUE; } #endif /*****************************************************************************/ static gchar * log_object_build_id (MMLogObject *_self) { return g_strdup ("base-manager"); } /*****************************************************************************/ MMBaseManager * mm_base_manager_new (GDBusConnection *connection, #if !defined WITH_BUILTIN_PLUGINS const gchar *plugin_dir, #endif gboolean auto_scan, MMFilterRule filter_policy, const gchar *initial_kernel_events, #if defined WITH_TESTS gboolean enable_test, #endif GError **error) { g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), NULL); return g_initable_new (MM_TYPE_BASE_MANAGER, NULL, /* cancellable */ error, MM_BASE_MANAGER_CONNECTION, connection, #if !defined WITH_BUILTIN_PLUGINS MM_BASE_MANAGER_PLUGIN_DIR, plugin_dir, #endif MM_BASE_MANAGER_AUTO_SCAN, auto_scan, MM_BASE_MANAGER_FILTER_POLICY, filter_policy, MM_BASE_MANAGER_INITIAL_KERNEL_EVENTS, initial_kernel_events, "version", MM_DIST_VERSION, #if defined WITH_TESTS MM_BASE_MANAGER_ENABLE_TEST, enable_test, #endif NULL); } static void set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { MMBaseManager *self = MM_BASE_MANAGER (object); switch (prop_id) { case PROP_CONNECTION: { gboolean had_connection = FALSE; if (self->priv->connection) { had_connection = TRUE; g_object_unref (self->priv->connection); } self->priv->connection = g_value_dup_object (value); /* Propagate connection loss to subobjects */ if (had_connection && !self->priv->connection) { if (self->priv->object_manager) { mm_obj_dbg (self, "stopping connection in object manager server"); g_dbus_object_manager_server_set_connection (self->priv->object_manager, NULL); } #if defined WITH_TESTS if (self->priv->test_skeleton && g_dbus_interface_skeleton_get_connection (G_DBUS_INTERFACE_SKELETON (self->priv->test_skeleton))) { mm_obj_dbg (self, "stopping connection in test skeleton"); g_dbus_interface_skeleton_unexport (G_DBUS_INTERFACE_SKELETON (self->priv->test_skeleton)); } #endif } break; } case PROP_AUTO_SCAN: self->priv->auto_scan = g_value_get_boolean (value); break; case PROP_FILTER_POLICY: self->priv->filter_policy = g_value_get_flags (value); break; #if !defined WITH_BUILTIN_PLUGINS case PROP_PLUGIN_DIR: g_free (self->priv->plugin_dir); self->priv->plugin_dir = g_value_dup_string (value); break; #endif case PROP_INITIAL_KERNEL_EVENTS: g_free (self->priv->initial_kernel_events); self->priv->initial_kernel_events = g_value_dup_string (value); break; #if defined WITH_TESTS case PROP_ENABLE_TEST: self->priv->enable_test = g_value_get_boolean (value); break; #endif 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) { MMBaseManager *self = MM_BASE_MANAGER (object); switch (prop_id) { case PROP_CONNECTION: g_value_set_object (value, self->priv->connection); break; case PROP_AUTO_SCAN: g_value_set_boolean (value, self->priv->auto_scan); break; case PROP_FILTER_POLICY: g_value_set_flags (value, self->priv->filter_policy); break; #if !defined WITH_BUILTIN_PLUGINS case PROP_PLUGIN_DIR: g_value_set_string (value, self->priv->plugin_dir); break; #endif case PROP_INITIAL_KERNEL_EVENTS: g_value_set_string (value, self->priv->initial_kernel_events); break; #if defined WITH_TESTS case PROP_ENABLE_TEST: g_value_set_boolean (value, self->priv->enable_test); break; #endif default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void mm_base_manager_init (MMBaseManager *self) { /* Setup private data */ self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, MM_TYPE_BASE_MANAGER, MMBaseManagerPrivate); /* Setup authorization provider */ self->priv->authp = mm_auth_provider_get (); self->priv->authp_cancellable = g_cancellable_new (); /* Setup internal lists of device objects */ self->priv->devices = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref); /* Setup internal list of inhibited devices */ self->priv->inhibited_devices = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify)inhibited_device_info_free); /* By default, enable autoscan */ self->priv->auto_scan = TRUE; #if defined WITH_TESTS /* By default, no test interface */ self->priv->enable_test = FALSE; #endif /* Setup Object Manager Server */ self->priv->object_manager = g_dbus_object_manager_server_new (MM_DBUS_PATH); /* Enable processing of input DBus messages */ g_object_connect (self, "signal::handle-set-logging", G_CALLBACK (handle_set_logging), NULL, "signal::handle-scan-devices", G_CALLBACK (handle_scan_devices), NULL, "signal::handle-report-kernel-event", G_CALLBACK (handle_report_kernel_event), NULL, "signal::handle-inhibit-device", G_CALLBACK (handle_inhibit_device), NULL, NULL); } static gboolean initable_init (GInitable *initable, GCancellable *cancellable, GError **error) { MMBaseManager *self = MM_BASE_MANAGER (initable); /* Create filter */ self->priv->filter = mm_filter_new (self->priv->filter_policy, error); if (!self->priv->filter) return FALSE; /* Create plugin manager */ self->priv->plugin_manager = mm_plugin_manager_new (self->priv->filter, #if !defined WITH_BUILTIN_PLUGINS self->priv->plugin_dir, #endif error); if (!self->priv->plugin_manager) return FALSE; #if defined WITH_UDEV /* Create udev client based on the subsystems requested by the plugins */ self->priv->udev = g_udev_client_new (mm_plugin_manager_get_subsystems (self->priv->plugin_manager)); /* If autoscan enabled, list for udev events */ if (!mm_context_get_test_no_udev () && self->priv->auto_scan) g_signal_connect_swapped (self->priv->udev, "uevent", G_CALLBACK (handle_uevent), initable); #endif #if defined WITH_QRTR if (!mm_context_get_test_no_qrtr ()) { /* Create and setup the QrtrBusWatcher */ self->priv->qrtr_bus_watcher = mm_qrtr_bus_watcher_new (); mm_qrtr_bus_watcher_start (self->priv->qrtr_bus_watcher, NULL, NULL); /* If autoscan enabled, list for QrtrBusWatcher events */ if (self->priv->auto_scan) { g_object_connect (self->priv->qrtr_bus_watcher, "swapped-signal::" MM_QRTR_BUS_WATCHER_DEVICE_ADDED, G_CALLBACK (handle_qrtr_device_added), self, "swapped-signal::" MM_QRTR_BUS_WATCHER_DEVICE_REMOVED, G_CALLBACK (handle_qrtr_device_removed), self, NULL); } } #endif /* Export the manager interface */ if (!g_dbus_interface_skeleton_export (G_DBUS_INTERFACE_SKELETON (initable), self->priv->connection, MM_DBUS_PATH, error)) return FALSE; /* Export the Object Manager interface */ g_dbus_object_manager_server_set_connection (self->priv->object_manager, self->priv->connection); #if defined WITH_TESTS /* Setup the Test skeleton and export the interface */ if (self->priv->enable_test) { self->priv->test_skeleton = mm_gdbus_test_skeleton_new (); g_signal_connect (self->priv->test_skeleton, "handle-set-profile", G_CALLBACK (handle_set_profile), initable); if (!g_dbus_interface_skeleton_export (G_DBUS_INTERFACE_SKELETON (self->priv->test_skeleton), self->priv->connection, MM_DBUS_PATH, error)) return FALSE; } #endif /* All good */ return TRUE; } static void finalize (GObject *object) { MMBaseManager *self = MM_BASE_MANAGER (object); g_free (self->priv->initial_kernel_events); #if !defined WITH_BUILTIN_PLUGINS g_free (self->priv->plugin_dir); #endif g_hash_table_destroy (self->priv->inhibited_devices); g_hash_table_destroy (self->priv->devices); #if defined WITH_UDEV if (self->priv->udev) g_object_unref (self->priv->udev); #endif #if defined WITH_QRTR if (self->priv->qrtr_bus_watcher) g_object_unref (self->priv->qrtr_bus_watcher); #endif if (self->priv->filter) g_object_unref (self->priv->filter); if (self->priv->plugin_manager) g_object_unref (self->priv->plugin_manager); if (self->priv->object_manager) g_object_unref (self->priv->object_manager); #if defined WITH_TESTS if (self->priv->test_skeleton) g_object_unref (self->priv->test_skeleton); #endif if (self->priv->connection) g_object_unref (self->priv->connection); /* note: authp is a singleton, we don't keep a full reference */ if (self->priv->authp_cancellable) g_object_unref (self->priv->authp_cancellable); G_OBJECT_CLASS (mm_base_manager_parent_class)->finalize (object); } static void initable_iface_init (GInitableIface *iface) { iface->init = initable_init; } static void log_object_iface_init (MMLogObjectInterface *iface) { iface->build_id = log_object_build_id; } static void mm_base_manager_class_init (MMBaseManagerClass *manager_class) { GObjectClass *object_class = G_OBJECT_CLASS (manager_class); g_type_class_add_private (object_class, sizeof (MMBaseManagerPrivate)); /* Virtual methods */ object_class->set_property = set_property; object_class->get_property = get_property; object_class->finalize = finalize; /* Properties */ g_object_class_install_property (object_class, PROP_CONNECTION, g_param_spec_object (MM_BASE_MANAGER_CONNECTION, "Connection", "GDBus connection to the system bus.", G_TYPE_DBUS_CONNECTION, G_PARAM_READWRITE | G_PARAM_CONSTRUCT)); g_object_class_install_property (object_class, PROP_AUTO_SCAN, g_param_spec_boolean (MM_BASE_MANAGER_AUTO_SCAN, "Auto scan", "Automatically look for new devices", TRUE, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); g_object_class_install_property ( object_class, PROP_FILTER_POLICY, g_param_spec_flags (MM_BASE_MANAGER_FILTER_POLICY, "Filter policy", "Mask of rules enabled in the filter", MM_TYPE_FILTER_RULE, MM_FILTER_RULE_NONE, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); #if !defined WITH_BUILTIN_PLUGINS g_object_class_install_property (object_class, PROP_PLUGIN_DIR, g_param_spec_string (MM_BASE_MANAGER_PLUGIN_DIR, "Plugin directory", "Where to look for plugins", NULL, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); #endif g_object_class_install_property (object_class, PROP_INITIAL_KERNEL_EVENTS, g_param_spec_string (MM_BASE_MANAGER_INITIAL_KERNEL_EVENTS, "Initial kernel events", "Path to a file with the list of initial kernel events", NULL, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); #if defined WITH_TESTS g_object_class_install_property (object_class, PROP_ENABLE_TEST, g_param_spec_boolean (MM_BASE_MANAGER_ENABLE_TEST, "Enable tests", "Enable the Test interface", FALSE, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); #endif } ModemManager-1.23.4-dev/src/mm-base-manager.h000066400000000000000000000071721456466623000206250ustar00rootroot00000000000000/* -*- 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 - 2011 Red Hat, Inc. * Copyright (C) 2011 Google, Inc. */ #ifndef MM_BASE_MANAGER_H #define MM_BASE_MANAGER_H #include #include #include #include "mm-filter.h" #include "mm-gdbus-manager.h" #define MM_TYPE_BASE_MANAGER (mm_base_manager_get_type ()) #define MM_BASE_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_BASE_MANAGER, MMBaseManager)) #define MM_BASE_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_BASE_MANAGER, MMBaseManagerClass)) #define MM_IS_BASE_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_BASE_MANAGER)) #define MM_IS_BASE_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), MM_TYPE_BASE_MANAGER)) #define MM_BASE_MANAGER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_BASE_MANAGER, MMBaseManagerClass)) #define MM_BASE_MANAGER_CONNECTION "connection" /* Construct-only */ #define MM_BASE_MANAGER_AUTO_SCAN "auto-scan" /* Construct-only */ #define MM_BASE_MANAGER_FILTER_POLICY "filter-policy" /* Construct-only */ #define MM_BASE_MANAGER_PLUGIN_DIR "plugin-dir" /* Construct-only */ #define MM_BASE_MANAGER_INITIAL_KERNEL_EVENTS "initial-kernel-events" /* Construct-only */ #if defined WITH_TESTS #define MM_BASE_MANAGER_ENABLE_TEST "enable-test" /* Construct-only */ #endif typedef struct _MMBaseManagerPrivate MMBaseManagerPrivate; typedef struct { MmGdbusOrgFreedesktopModemManager1Skeleton parent; MMBaseManagerPrivate *priv; } MMBaseManager; typedef struct { MmGdbusOrgFreedesktopModemManager1SkeletonClass parent; } MMBaseManagerClass; GType mm_base_manager_get_type (void); G_DEFINE_AUTOPTR_CLEANUP_FUNC (MMBaseManager, g_object_unref) MMBaseManager *mm_base_manager_new (GDBusConnection *bus, #if !defined WITH_BUILTIN_PLUGINS const gchar *plugin_dir, #endif gboolean auto_scan, MMFilterRule filter_policy, const gchar *initial_kernel_events, #if defined WITH_TESTS gboolean enable_test, #endif GError **error); void mm_base_manager_start (MMBaseManager *manager, gboolean manual_scan); void mm_base_manager_shutdown (MMBaseManager *manager, gboolean disable, gboolean power_low, gboolean remove); #if defined WITH_SUSPEND_RESUME void mm_base_manager_sync (MMBaseManager *manager); #endif guint32 mm_base_manager_num_modems (MMBaseManager *manager); #endif /* MM_BASE_MANAGER_H */ ModemManager-1.23.4-dev/src/mm-base-modem-at.c000066400000000000000000000601531456466623000207070ustar00rootroot00000000000000/* -*- 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) 2011 Aleksander Morgado */ #include #include #include #include "mm-base-modem-at.h" #include "mm-errors-types.h" static gboolean abort_task_if_port_unusable (MMBaseModem *self, MMPortSerialAt *port, GTask *task) { GError *error = NULL; gboolean init_sequence_enabled = FALSE; /* If no port given, probably the port disappeared */ if (!port) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_NOT_FOUND, "Cannot run sequence: port not given"); g_object_unref (task); return FALSE; } /* Ensure we don't try to use a connected port */ if (mm_port_get_connected (MM_PORT (port))) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_CONNECTED, "Cannot run sequence: port is connected"); g_object_unref (task); return FALSE; } /* Temporarily disable init sequence if we're just sending a * command to a just opened port */ g_object_get (port, MM_PORT_SERIAL_AT_INIT_SEQUENCE_ENABLED, &init_sequence_enabled, NULL); g_object_set (port, MM_PORT_SERIAL_AT_INIT_SEQUENCE_ENABLED, FALSE, NULL); /* Ensure we have a port open during the sequence */ if (!mm_port_serial_open (MM_PORT_SERIAL (port), &error)) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_CONNECTED, "Cannot run sequence: '%s'", error->message); g_error_free (error); g_object_unref (task); return FALSE; } /* Reset previous init sequence state */ g_object_set (port, MM_PORT_SERIAL_AT_INIT_SEQUENCE_ENABLED, init_sequence_enabled, NULL); return TRUE; } static void parent_cancellable_cancelled (GCancellable *parent_cancellable, GCancellable *cancellable) { g_cancellable_cancel (cancellable); } /*****************************************************************************/ /* AT sequence handling */ typedef struct { MMPortSerialAt *port; gulong cancelled_id; GCancellable *parent_cancellable; const MMBaseModemAtCommand *current; const MMBaseModemAtCommand *sequence; gpointer response_processor_context; GDestroyNotify response_processor_context_free; GVariant *result; guint next_command_wait_id; } AtSequenceContext; static void at_sequence_parse_response (MMPortSerialAt *port, GAsyncResult *res, GTask *task); static void at_sequence_context_free (AtSequenceContext *ctx) { mm_port_serial_close (MM_PORT_SERIAL (ctx->port)); g_object_unref (ctx->port); if (ctx->response_processor_context && ctx->response_processor_context_free) ctx->response_processor_context_free (ctx->response_processor_context); if (ctx->parent_cancellable) { g_cancellable_disconnect (ctx->parent_cancellable, ctx->cancelled_id); g_object_unref (ctx->parent_cancellable); } if (ctx->next_command_wait_id > 0) { g_source_remove (ctx->next_command_wait_id); ctx->next_command_wait_id = 0; } if (ctx->result) g_variant_unref (ctx->result); g_free (ctx); } GVariant * mm_base_modem_at_sequence_full_finish (MMBaseModem *self, GAsyncResult *res, gpointer *response_processor_context, GError **error) { GTask *task; GVariant *result; task = G_TASK (res); result = g_task_propagate_pointer (task, error); if (response_processor_context && !g_task_had_error (task)) { AtSequenceContext *ctx; ctx = g_task_get_task_data (task); /* transfer none, no need to free the context ourselves, if * we gave a response_processor_context_free callback */ *response_processor_context = ctx->response_processor_context; } /* transfer-none! (so that we can ignore it) */ return result; } static gboolean at_sequence_next_command (GTask *task) { AtSequenceContext *ctx; ctx = g_task_get_task_data (task); ctx->next_command_wait_id = 0; /* Schedule the next command in the probing group */ mm_port_serial_at_command ( ctx->port, ctx->current->command, ctx->current->timeout, FALSE, ctx->current->allow_cached, g_task_get_cancellable (task), (GAsyncReadyCallback)at_sequence_parse_response, task); return G_SOURCE_REMOVE; } static void at_sequence_parse_response (MMPortSerialAt *port, GAsyncResult *res, GTask *task) { MMBaseModemAtResponseProcessorResult processor_result; GVariant *result = NULL; GError *result_error = NULL; AtSequenceContext *ctx; g_autofree gchar *response = NULL; GError *error = NULL; response = mm_port_serial_at_command_finish (port, res, &error); /* Cancelled? */ if (g_task_return_error_if_cancelled (task)) { g_object_unref (task); return; } ctx = g_task_get_task_data (task); if (!ctx->current->response_processor) processor_result = MM_BASE_MODEM_AT_RESPONSE_PROCESSOR_RESULT_CONTINUE; else { const MMBaseModemAtCommand *next = ctx->current + 1; /* Response processor will tell us if we need to keep on the sequence */ processor_result = ctx->current->response_processor (g_task_get_source_object (task), ctx->response_processor_context, ctx->current->command, response, next->command ? FALSE : TRUE, /* Last command in sequence? */ error, &result, &result_error); switch (processor_result) { case MM_BASE_MODEM_AT_RESPONSE_PROCESSOR_RESULT_CONTINUE: g_assert (!result && !result_error); break; case MM_BASE_MODEM_AT_RESPONSE_PROCESSOR_RESULT_SUCCESS: g_assert (!result_error); /* result is optional */ break; case MM_BASE_MODEM_AT_RESPONSE_PROCESSOR_RESULT_FAILURE: /* On failure, complete with error right away */ g_assert (!result && result_error); /* result is optional */ g_task_return_error (task, result_error); g_object_unref (task); if (error) g_error_free (error); return; default: g_assert_not_reached (); } } if (error) g_error_free (error); if (processor_result == MM_BASE_MODEM_AT_RESPONSE_PROCESSOR_RESULT_CONTINUE) { ctx->current++; if (ctx->current->command) { g_assert (!ctx->next_command_wait_id); ctx->next_command_wait_id = g_timeout_add_seconds (ctx->current->wait_seconds, (GSourceFunc) at_sequence_next_command, task); return; } /* On last command, end. */ } /* If we got a response, set it as result */ if (result) /* transfer-full */ ctx->result = result; /* transfer-none, the result remains owned by the GTask context */ g_task_return_pointer (task, ctx->result, NULL); g_object_unref (task); } static void at_sequence_common (MMBaseModem *self, MMPortSerialAt *port, const MMBaseModemAtCommand *sequence, gpointer response_processor_context, GDestroyNotify response_processor_context_free, GTask *task, GCancellable *parent_cancellable) { AtSequenceContext *ctx; /* Ensure that we have an open port */ if (!abort_task_if_port_unusable (self, port, task)) return; /* Setup context */ ctx = g_new0 (AtSequenceContext, 1); ctx->port = g_object_ref (port); ctx->current = ctx->sequence = sequence; ctx->response_processor_context = response_processor_context; ctx->response_processor_context_free = response_processor_context_free; /* Ensure the cancellable that's already associated with the modem * will also get cancelled if the modem wide-one gets cancelled */ if (parent_cancellable) { GCancellable *cancellable; cancellable = g_task_get_cancellable (task); ctx->parent_cancellable = g_object_ref (parent_cancellable); ctx->cancelled_id = g_cancellable_connect (ctx->parent_cancellable, G_CALLBACK (parent_cancellable_cancelled), cancellable, NULL); } g_task_set_task_data (task, ctx, (GDestroyNotify)at_sequence_context_free); /* Go on with the first one in the sequence */ mm_port_serial_at_command ( ctx->port, ctx->current->command, ctx->current->timeout, FALSE, ctx->current->allow_cached, g_task_get_cancellable (task), (GAsyncReadyCallback)at_sequence_parse_response, task); } void mm_base_modem_at_sequence_full (MMBaseModem *self, MMPortSerialAt *port, const MMBaseModemAtCommand *sequence, gpointer response_processor_context, GDestroyNotify response_processor_context_free, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { GCancellable *modem_cancellable; GTask *task; modem_cancellable = mm_base_modem_peek_cancellable (self); task = g_task_new (self, cancellable ? cancellable : modem_cancellable, callback, user_data); at_sequence_common (self, port, sequence, response_processor_context, response_processor_context_free, task, cancellable ? modem_cancellable : NULL); } GVariant * mm_base_modem_at_sequence_finish (MMBaseModem *self, GAsyncResult *res, gpointer *response_processor_context, GError **error) { return (mm_base_modem_at_sequence_full_finish ( self, res, response_processor_context, error)); } void mm_base_modem_at_sequence (MMBaseModem *self, const MMBaseModemAtCommand *sequence, gpointer response_processor_context, GDestroyNotify response_processor_context_free, GAsyncReadyCallback callback, gpointer user_data) { MMPortSerialAt *port; GError *error = NULL; GTask *task; task = g_task_new (self, mm_base_modem_peek_cancellable (self), callback, user_data); /* No port given, so we'll try to guess which is best */ port = mm_base_modem_peek_best_at_port (self, &error); if (!port) { g_assert (error != NULL); g_task_return_error (task, error); g_object_unref (task); return; } at_sequence_common (self, port, sequence, response_processor_context, response_processor_context_free, task, NULL); } /*****************************************************************************/ /* Response processor helpers */ MMBaseModemAtResponseProcessorResult mm_base_modem_response_processor_string (MMBaseModem *self, gpointer none, const gchar *command, const gchar *response, gboolean last_command, const GError *error, GVariant **result, GError **result_error) { if (error) { *result = NULL; *result_error = g_error_copy (error); return MM_BASE_MODEM_AT_RESPONSE_PROCESSOR_RESULT_FAILURE; } *result = g_variant_new_string (response); *result_error = NULL; return MM_BASE_MODEM_AT_RESPONSE_PROCESSOR_RESULT_SUCCESS; } MMBaseModemAtResponseProcessorResult mm_base_modem_response_processor_no_result (MMBaseModem *self, gpointer none, const gchar *command, const gchar *response, gboolean last_command, const GError *error, GVariant **result, GError **result_error) { if (error) { *result = NULL; *result_error = g_error_copy (error); return MM_BASE_MODEM_AT_RESPONSE_PROCESSOR_RESULT_FAILURE; } *result = NULL; *result_error = NULL; return MM_BASE_MODEM_AT_RESPONSE_PROCESSOR_RESULT_SUCCESS; } MMBaseModemAtResponseProcessorResult mm_base_modem_response_processor_no_result_continue (MMBaseModem *self, gpointer none, const gchar *command, const gchar *response, gboolean last_command, const GError *error, GVariant **result, GError **result_error) { *result = NULL; if (error) { *result_error = g_error_copy (error); return MM_BASE_MODEM_AT_RESPONSE_PROCESSOR_RESULT_FAILURE; } *result_error = NULL; return MM_BASE_MODEM_AT_RESPONSE_PROCESSOR_RESULT_CONTINUE; } MMBaseModemAtResponseProcessorResult mm_base_modem_response_processor_continue_on_error (MMBaseModem *self, gpointer none, const gchar *command, const gchar *response, gboolean last_command, const GError *error, GVariant **result, GError **result_error) { *result = NULL; *result_error = NULL; return (error ? MM_BASE_MODEM_AT_RESPONSE_PROCESSOR_RESULT_CONTINUE : MM_BASE_MODEM_AT_RESPONSE_PROCESSOR_RESULT_SUCCESS); } MMBaseModemAtResponseProcessorResult mm_base_modem_response_processor_string_ignore_at_errors (MMBaseModem *self, gpointer none, const gchar *command, const gchar *response, gboolean last_command, const GError *error, GVariant **result, GError **result_error) { if (error) { *result = NULL; /* Ignore AT errors (ie, ERROR or CMx ERROR) */ if (error->domain != MM_MOBILE_EQUIPMENT_ERROR || last_command) { *result_error = g_error_copy (error); return MM_BASE_MODEM_AT_RESPONSE_PROCESSOR_RESULT_FAILURE; } *result_error = NULL; return MM_BASE_MODEM_AT_RESPONSE_PROCESSOR_RESULT_CONTINUE; } *result = g_variant_new_string (response); return MM_BASE_MODEM_AT_RESPONSE_PROCESSOR_RESULT_SUCCESS; } /*****************************************************************************/ /* Single AT command handling */ typedef struct { MMPortSerialAt *port; gulong cancelled_id; GCancellable *parent_cancellable; gchar *response; } AtCommandContext; static void at_command_context_free (AtCommandContext *ctx) { mm_port_serial_close (MM_PORT_SERIAL (ctx->port)); if (ctx->parent_cancellable) { g_cancellable_disconnect (ctx->parent_cancellable, ctx->cancelled_id); g_object_unref (ctx->parent_cancellable); } g_object_unref (ctx->port); g_free (ctx->response); g_free (ctx); } const gchar * mm_base_modem_at_command_full_finish (MMBaseModem *self, GAsyncResult *res, GError **error) { return g_task_propagate_pointer (G_TASK (res), error); } static void at_command_ready (MMPortSerialAt *port, GAsyncResult *res, GTask *task) { AtCommandContext *ctx; GError *error = NULL; ctx = g_task_get_task_data (task); g_assert (!ctx->response); ctx->response = mm_port_serial_at_command_finish (port, res, &error); /* Cancelled? */ if (g_task_return_error_if_cancelled (task)) { if (error) g_error_free (error); } /* Error coming from the serial port? */ else if (error) g_task_return_error (task, error); /* Valid string response */ else if (ctx->response) /* transfer-none, the response remains owned by the GTask context */ g_task_return_pointer (task, ctx->response, NULL); else g_assert_not_reached (); g_object_unref (task); } static void at_command_common (MMBaseModem *self, MMPortSerialAt *port, const gchar *command, guint timeout, gboolean allow_cached, gboolean is_raw, GTask *task, GCancellable *parent_cancellable) { AtCommandContext *ctx; /* Ensure that we have an open port */ if (!abort_task_if_port_unusable (self, port, task)) return; ctx = g_new0 (AtCommandContext, 1); ctx->port = g_object_ref (port); /* Ensure the cancellable that's already associated with the modem * will also get cancelled if the modem wide-one gets cancelled */ if (parent_cancellable) { GCancellable *cancellable; cancellable = g_task_get_cancellable (task); ctx->parent_cancellable = g_object_ref (parent_cancellable); ctx->cancelled_id = g_cancellable_connect (ctx->parent_cancellable, G_CALLBACK (parent_cancellable_cancelled), cancellable, NULL); } g_task_set_task_data (task, ctx, (GDestroyNotify)at_command_context_free); /* Go on with the command */ mm_port_serial_at_command ( port, command, timeout, is_raw, allow_cached, g_task_get_cancellable (task), (GAsyncReadyCallback)at_command_ready, task); } void mm_base_modem_at_command_full (MMBaseModem *self, MMPortSerialAt *port, const gchar *command, guint timeout, gboolean allow_cached, gboolean is_raw, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { GCancellable *modem_cancellable; GTask *task; modem_cancellable = mm_base_modem_peek_cancellable (self); task = g_task_new (self, cancellable ? cancellable : modem_cancellable, callback, user_data); at_command_common (self, port, command, timeout, allow_cached, is_raw, task, cancellable ? modem_cancellable : NULL); } const gchar * mm_base_modem_at_command_finish (MMBaseModem *self, GAsyncResult *res, GError **error) { return mm_base_modem_at_command_full_finish (self, res, error); } static void _at_command (MMBaseModem *self, const gchar *command, guint timeout, gboolean allow_cached, gboolean is_raw, GAsyncReadyCallback callback, gpointer user_data) { MMPortSerialAt *port; GError *error = NULL; GTask *task; task = g_task_new (self, mm_base_modem_peek_cancellable (self), callback, user_data); /* No port given, so we'll try to guess which is best */ port = mm_base_modem_peek_best_at_port (self, &error); if (!port) { g_assert (error != NULL); g_task_return_error (task, error); g_object_unref (task); return; } at_command_common (self, port, command, timeout, allow_cached, is_raw, task, NULL); } void mm_base_modem_at_command (MMBaseModem *self, const gchar *command, guint timeout, gboolean allow_cached, GAsyncReadyCallback callback, gpointer user_data) { _at_command (self, command, timeout, allow_cached, FALSE, callback, user_data); } void mm_base_modem_at_command_raw (MMBaseModem *self, const gchar *command, guint timeout, gboolean allow_cached, GAsyncReadyCallback callback, gpointer user_data) { _at_command (self, command, timeout, allow_cached, TRUE, callback, user_data); } void mm_base_modem_at_command_alloc_clear (MMBaseModemAtCommandAlloc *command) { g_free (command->command); } ModemManager-1.23.4-dev/src/mm-base-modem-at.h000066400000000000000000000337561456466623000207250ustar00rootroot00000000000000/* -*- 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) 2011 Aleksander Morgado */ #ifndef MM_BASE_MODEM_AT_H #define MM_BASE_MODEM_AT_H #include #include "mm-base-modem.h" #include "mm-port-serial-at.h" typedef enum { MM_BASE_MODEM_AT_RESPONSE_PROCESSOR_RESULT_CONTINUE, MM_BASE_MODEM_AT_RESPONSE_PROCESSOR_RESULT_SUCCESS, MM_BASE_MODEM_AT_RESPONSE_PROCESSOR_RESULT_FAILURE, } MMBaseModemAtResponseProcessorResult; /* * The expected result depends on the specific operation, so the GVariant * created by the response processor needs to match the one expected in * finish(). * * SUCCESS must be returned when the operation is to be considered successful, * and a result may be given. * * FAILURE must be returned when a GError is propagated into result_error, * which will be treated as a critical error and therefore the operation will be aborted. * * CONTINUE must be returned when neither result nor result_error are given and * the operation should go on with the next scheduled command. * * This setup, therefore allows: * - Running a single command and processing its result. * - Running a set of N commands, providing a global result after all have * been executed. * - Running a set of N commands out of M (N #include #include #include #include #include #include #include #include #include "mm-context.h" #include "mm-base-modem.h" #if defined WITH_QRTR #include "mm-kernel-device-qrtr.h" #endif #include "mm-log-object.h" #include "mm-port-enums-types.h" #include "mm-serial-parsers.h" #include "mm-modem-helpers.h" static void log_object_iface_init (MMLogObjectInterface *iface); G_DEFINE_ABSTRACT_TYPE_WITH_CODE (MMBaseModem, mm_base_modem, MM_GDBUS_TYPE_OBJECT_SKELETON, G_IMPLEMENT_INTERFACE (MM_TYPE_LOG_OBJECT, log_object_iface_init)) /* If we get 10 consecutive timeouts in a serial port, we consider the modem * invalid and we request re-probing. */ #define DEFAULT_MAX_TIMEOUTS 10 enum { PROP_0, PROP_VALID, PROP_MAX_TIMEOUTS, PROP_DEVICE, PROP_PHYSDEV, PROP_DRIVERS, PROP_PLUGIN, PROP_VENDOR_ID, PROP_PRODUCT_ID, PROP_SUBSYSTEM_VENDOR_ID, PROP_CONNECTION, PROP_REPROBE, PROP_DATA_NET_SUPPORTED, PROP_DATA_TTY_SUPPORTED, PROP_LAST }; enum { SIGNAL_LINK_PORT_GRABBED, SIGNAL_LINK_PORT_RELEASED, SIGNAL_LAST }; static GParamSpec *properties[PROP_LAST]; static guint signals[SIGNAL_LAST]; struct _MMBaseModemPrivate { /* The connection to the system bus */ GDBusConnection *connection; guint dbus_id; /* Modem-wide cancellable. If it ever gets cancelled, no further operations * should be done by the modem. */ GCancellable *cancellable; gulong invalid_if_cancelled; gchar *device; gchar *physdev; gchar **drivers; gchar *plugin; guint vendor_id; guint product_id; guint subsystem_vendor_id; gboolean hotplugged; gboolean valid; gboolean reprobe; guint max_timeouts; /* The authorization provider */ MMAuthProvider *authp; GCancellable *authp_cancellable; GHashTable *ports; MMPortSerialAt *primary; MMPortSerialAt *secondary; MMPortSerialQcdm *qcdm; GList *data; gboolean data_net_supported; gboolean data_tty_supported; /* GPS-enabled modems will have an AT port for control, and a raw serial * port to receive all GPS traces */ MMPortSerialAt *gps_control; MMPortSerialGps *gps; /* Some audio-capable devices will have a port for audio specifically */ MMPortSerial *audio; /* Support for parallel enable/disable operations */ GList *enable_tasks; GList *disable_tasks; #if defined WITH_QMI /* QMI ports */ GList *qmi; #endif #if defined WITH_MBIM /* MBIM ports */ GList *mbim; #endif /* Additional port links grabbed after having * organized ports */ GHashTable *link_ports; }; guint mm_base_modem_get_dbus_id (MMBaseModem *self) { return self->priv->dbus_id; } /******************************************************************************/ static void port_removed_cb (MMPort *port, MMBaseModem *self) { /* We have to do a full re-probe here because simply reopening the device * and restarting proxy would leave us without proper notifications. */ mm_obj_msg (self, "port '%s' no longer controllable, reprobing", mm_port_get_device (MM_PORT (port))); self->priv->reprobe = TRUE; g_cancellable_cancel (self->priv->cancellable); } static void port_timed_out_cb (MMPort *port, guint n_consecutive_timeouts, MMBaseModem *self) { /* If reached the maximum number of timeouts, invalidate modem */ if (n_consecutive_timeouts >= self->priv->max_timeouts) { mm_obj_err (self, "port %s timed out %u consecutive times, marking modem as invalid", mm_port_get_device (MM_PORT (port)), n_consecutive_timeouts); g_cancellable_cancel (self->priv->cancellable); return; } if (n_consecutive_timeouts > 1) mm_obj_warn (self, "port %s timed out %u consecutive times", mm_port_get_device (MM_PORT (port)), n_consecutive_timeouts); } static MMPort * base_modem_create_ignored_port (MMBaseModem *self, const gchar *name) { return MM_PORT (g_object_new (MM_TYPE_PORT, MM_PORT_DEVICE, name, MM_PORT_TYPE, MM_PORT_TYPE_IGNORED, NULL)); } static MMPort * base_modem_create_net_port (MMBaseModem *self, const gchar *name) { return MM_PORT (mm_port_net_new (name)); } static MMPort * base_modem_create_tty_port (MMBaseModem *self, const gchar *name, MMKernelDevice *kernel_device, MMPortType ptype) { MMPort *port = NULL; const gchar *flow_control_tag; if (ptype == MM_PORT_TYPE_QCDM) port = MM_PORT (mm_port_serial_qcdm_new (name, MM_PORT_SUBSYS_TTY)); else if (ptype == MM_PORT_TYPE_GPS) port = MM_PORT (mm_port_serial_gps_new (name)); else if (ptype == MM_PORT_TYPE_AUDIO) port = MM_PORT (mm_port_serial_new (name, ptype)); else if (ptype == MM_PORT_TYPE_AT) port = MM_PORT (mm_port_serial_at_new (name, MM_PORT_SUBSYS_TTY)); if (!port) return NULL; /* Optional user-provided baudrate */ if (mm_kernel_device_has_property (kernel_device, ID_MM_TTY_BAUDRATE)) g_object_set (port, MM_PORT_SERIAL_BAUD, mm_kernel_device_get_property_as_int (kernel_device, ID_MM_TTY_BAUDRATE), NULL); /* Optional user-provided flow control */ flow_control_tag = mm_kernel_device_get_property (kernel_device, ID_MM_TTY_FLOW_CONTROL); if (flow_control_tag) { MMFlowControl flow_control; g_autoptr(GError) inner_error = NULL; flow_control = mm_flow_control_from_string (flow_control_tag, &inner_error); if (flow_control != MM_FLOW_CONTROL_UNKNOWN) g_object_set (port, MM_PORT_SERIAL_FLOW_CONTROL, flow_control, NULL); else mm_obj_warn (self, "unsupported flow control settings in port %s: %s", name, inner_error->message); } return port; } static MMPort * base_modem_create_usbmisc_port (MMBaseModem *self, const gchar *name, MMPortType ptype) { #if defined WITH_QMI if (ptype == MM_PORT_TYPE_QMI) return MM_PORT (mm_port_qmi_new (name, MM_PORT_SUBSYS_USBMISC)); #endif #if defined WITH_MBIM if (ptype == MM_PORT_TYPE_MBIM) return MM_PORT (mm_port_mbim_new (name, MM_PORT_SUBSYS_USBMISC)); #endif if (ptype == MM_PORT_TYPE_AT) return MM_PORT (mm_port_serial_at_new (name, MM_PORT_SUBSYS_USBMISC)); return NULL; } static MMPort * base_modem_create_rpmsg_port (MMBaseModem *self, const gchar *name, MMPortType ptype) { #if defined WITH_QMI if (ptype == MM_PORT_TYPE_QMI) return MM_PORT (mm_port_qmi_new (name, MM_PORT_SUBSYS_RPMSG)); #endif if (ptype == MM_PORT_TYPE_AT) return MM_PORT (mm_port_serial_at_new (name, MM_PORT_SUBSYS_RPMSG)); return NULL; } #if defined WITH_QRTR static MMPort * base_modem_create_qrtr_port (MMBaseModem *self, const gchar *name, MMKernelDevice *kernel_device, MMPortType ptype) { if (ptype == MM_PORT_TYPE_QMI) { g_autoptr(QrtrNode) node = NULL; g_assert (MM_IS_KERNEL_DEVICE_QRTR (kernel_device)); node = mm_kernel_device_qrtr_get_node (MM_KERNEL_DEVICE_QRTR (kernel_device)); return MM_PORT (mm_port_qmi_new_from_node (name, node)); } return NULL; } #endif static MMPort * base_modem_create_wwan_port (MMBaseModem *self, const gchar *name, MMPortType ptype) { #if defined WITH_QMI if (ptype == MM_PORT_TYPE_QMI) return MM_PORT (mm_port_qmi_new (name, MM_PORT_SUBSYS_WWAN)); #endif #if defined WITH_MBIM if (ptype == MM_PORT_TYPE_MBIM) return MM_PORT (mm_port_mbim_new (name, MM_PORT_SUBSYS_WWAN)); #endif if (ptype == MM_PORT_TYPE_QCDM) return MM_PORT (mm_port_serial_qcdm_new (name, MM_PORT_SUBSYS_WWAN)); if (ptype == MM_PORT_TYPE_AT) return MM_PORT (mm_port_serial_at_new (name, MM_PORT_SUBSYS_WWAN)); return NULL; } static MMPort * base_modem_create_virtual_port (MMBaseModem *self, const gchar *name) { return MM_PORT (mm_port_serial_at_new (name, MM_PORT_SUBSYS_UNIX)); } static MMPort * base_modem_internal_grab_port (MMBaseModem *self, MMKernelDevice *kernel_device, gboolean link_port, MMPortType ptype, MMPortSerialAtFlag at_pflags, GError **error) { MMPort *port; const gchar *subsys; const gchar *name; g_autofree gchar *key = NULL; gboolean port_monitoring = FALSE; subsys = mm_kernel_device_get_subsystem (kernel_device); name = mm_kernel_device_get_name (kernel_device); if (!self->priv->ports || (link_port && !self->priv->link_ports)) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Cannot add port '%s/%s', no ports table", subsys, name); return NULL; } /* Check whether we already have it stored */ key = g_strdup_printf ("%s%s", subsys, name); port = g_hash_table_lookup (self->priv->ports, key); if (port) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "Cannot add port '%s/%s', already exists", subsys, name); return NULL; } /* Explicitly ignored ports, grab them but explicitly flag them as ignored * right away, all the same way (i.e. regardless of subsystem). */ if (ptype == MM_PORT_TYPE_IGNORED) port = base_modem_create_ignored_port (self, name); else if (g_str_equal (subsys, "net")) port = base_modem_create_net_port (self, name); else if (g_str_equal (subsys, "tty")) port = base_modem_create_tty_port (self, name, kernel_device, ptype); else if (g_str_equal (subsys, "usbmisc")) port = base_modem_create_usbmisc_port (self, name, ptype); else if (g_str_equal (subsys, "rpmsg")) port = base_modem_create_rpmsg_port (self, name, ptype); #if defined WITH_QRTR else if (g_str_equal (subsys, "qrtr")) port = base_modem_create_qrtr_port (self, name, kernel_device, ptype); #endif else if (g_str_equal (subsys, "virtual")) port = base_modem_create_virtual_port (self, name); else if (g_str_equal (subsys, "wwan")) port = base_modem_create_wwan_port (self, name, ptype); if (!port) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "Cannot add port '%s/%s', unhandled port type", subsys, name); return NULL; } /* Setup consecutive ports and removal watchers in all control ports */ if (MM_IS_PORT_SERIAL_AT (port)) { mm_obj_dbg (port, "port monitoring enabled in AT port"); port_monitoring = TRUE; } else if (MM_IS_PORT_SERIAL_QCDM (port)) { mm_obj_dbg (port, "port monitoring enabled in QCDM port"); port_monitoring = TRUE; } #if defined WITH_QMI else if (MM_IS_PORT_QMI (port)) { mm_obj_dbg (port, "port monitoring enabled in QMI port"); port_monitoring = TRUE; } #endif #if defined WITH_MBIM else if (MM_IS_PORT_MBIM (port)) { mm_obj_dbg (port, "port monitoring enabled in MBIM port"); port_monitoring = TRUE; } #endif if (port_monitoring) { if (self->priv->max_timeouts > 0) g_signal_connect (port, MM_PORT_SIGNAL_TIMED_OUT, G_CALLBACK (port_timed_out_cb), self); g_signal_connect (port, MM_PORT_SIGNAL_REMOVED, G_CALLBACK (port_removed_cb), self); } /* Store kernel device */ g_object_set (port, MM_PORT_KERNEL_DEVICE, kernel_device, NULL); /* Set owner ID */ mm_log_object_set_owner_id (MM_LOG_OBJECT (port), mm_log_object_get_id (MM_LOG_OBJECT (self))); /* Common setup for all AT ports from all subsystems */ if (MM_IS_PORT_SERIAL_AT (port)) { mm_port_serial_at_set_response_parser (MM_PORT_SERIAL_AT (port), mm_serial_parser_v1_parse, mm_serial_parser_v1_new (), mm_serial_parser_v1_destroy); /* Prefer plugin-provided flags to the generic ones */ if (at_pflags == MM_PORT_SERIAL_AT_FLAG_NONE) { if (mm_kernel_device_get_property_as_boolean (kernel_device, ID_MM_PORT_TYPE_AT_PRIMARY)) { mm_obj_dbg (port, "AT port flagged as primary"); at_pflags = MM_PORT_SERIAL_AT_FLAG_PRIMARY; } else if (mm_kernel_device_get_property_as_boolean (kernel_device, ID_MM_PORT_TYPE_AT_SECONDARY)) { mm_obj_dbg (port, "AT port flagged as secondary"); at_pflags = MM_PORT_SERIAL_AT_FLAG_SECONDARY; } else if (mm_kernel_device_get_property_as_boolean (kernel_device, ID_MM_PORT_TYPE_AT_PPP)) { mm_obj_dbg (port, "AT port flagged as PPP"); at_pflags = MM_PORT_SERIAL_AT_FLAG_PPP; } /* Additionally, the ports may also be flagged as GPS control explicitly, if there is * one specific port to be used for that purpose */ if (mm_kernel_device_get_property_as_boolean (kernel_device, ID_MM_PORT_TYPE_AT_GPS_CONTROL)) { mm_obj_dbg (port, "AT port flagged as GPS control"); at_pflags = MM_PORT_SERIAL_AT_FLAG_GPS_CONTROL; } } /* The plugin may specify NONE_NO_GENERIC to avoid the generic * port type hints from being applied. */ if (at_pflags == MM_PORT_SERIAL_AT_FLAG_NONE_NO_GENERIC) at_pflags = MM_PORT_SERIAL_AT_FLAG_NONE; mm_port_serial_at_set_flags (MM_PORT_SERIAL_AT (port), at_pflags); } /* Add it to the tracking HT. * Note: 'key' and 'port' now owned by the HT. */ if (link_port) g_hash_table_insert (self->priv->link_ports, g_steal_pointer (&key), port); else g_hash_table_insert (self->priv->ports, g_steal_pointer (&key), port); return port; } gboolean mm_base_modem_grab_port (MMBaseModem *self, MMKernelDevice *kernel_device, MMPortType ptype, MMPortSerialAtFlag at_pflags, GError **error) { g_autoptr(GError) inner_error = NULL; if (!base_modem_internal_grab_port (self, kernel_device, FALSE, ptype, at_pflags, &inner_error)) { /* If the port was REQUIRED via udev tags and we failed to grab it, we will report * a fatal error. */ if (mm_kernel_device_get_property_as_boolean (kernel_device, ID_MM_REQUIRED)) { mm_obj_err (self, "required port '%s/%s' failed to be grabbed", mm_kernel_device_get_subsystem (kernel_device), mm_kernel_device_get_name (kernel_device)); g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_ABORTED, "Required port failed to be grabbed"); } else g_propagate_error (error, g_steal_pointer (&inner_error)); return FALSE; } mm_obj_dbg (self, "port '%s/%s' grabbed", mm_kernel_device_get_subsystem (kernel_device), mm_kernel_device_get_name (kernel_device)); return TRUE; } /******************************************************************************/ gboolean mm_base_modem_grab_link_port (MMBaseModem *self, MMKernelDevice *kernel_device, GError **error) { const gchar *subsystem; const gchar *name; MMPort *port; /* To simplify things, we only support NET link ports at this point */ subsystem = mm_kernel_device_get_subsystem (kernel_device); name = mm_kernel_device_get_name (kernel_device); if (!g_str_equal (subsystem, "net")) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "Cannot add port '%s/%s', unexpected link port subsystem", subsystem, name); return FALSE; } /* all the newly added link ports will NOT be 'organized'; i.e. they won't * be available as 'data ports' in the modem, but they can be looked up * by name */ port = base_modem_internal_grab_port (self, kernel_device, TRUE, MM_PORT_TYPE_NET, MM_PORT_SERIAL_AT_FLAG_NONE, error); if (!port) return FALSE; mm_obj_dbg (self, "link port '%s/%s' grabbed", subsystem, name); g_signal_emit (self, signals[SIGNAL_LINK_PORT_GRABBED], 0, port); return TRUE; } gboolean mm_base_modem_release_link_port (MMBaseModem *self, const gchar *subsystem, const gchar *name, GError **error) { g_autofree gchar *key = NULL; MMPort *port; if (!self->priv->link_ports) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Cannot release link port '%s/%s', no link ports table", subsystem, name); return FALSE; } key = g_strdup_printf ("%s%s", subsystem, name); port = g_hash_table_lookup (self->priv->link_ports, key); if (!port) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "Cannot release link port '%s/%s', not grabbed", subsystem, name); return FALSE; } /* make sure the port object is valid during the port release signal */ g_object_ref (port); g_hash_table_remove (self->priv->link_ports, key); mm_obj_dbg (self, "link port '%s/%s' released", subsystem, name); g_signal_emit (self, signals[SIGNAL_LINK_PORT_RELEASED], 0, port); g_object_unref (port); return TRUE; } /******************************************************************************/ typedef struct { gchar *name; gulong link_port_grabbed_id; guint timeout_id; } WaitLinkPortContext; static void wait_link_port_context_free (WaitLinkPortContext *ctx) { g_assert (!ctx->link_port_grabbed_id); g_assert (!ctx->timeout_id); g_free (ctx->name); g_slice_free (WaitLinkPortContext, ctx); } MMPort * mm_base_modem_wait_link_port_finish (MMBaseModem *self, GAsyncResult *res, GError **error) { return g_task_propagate_pointer (G_TASK (res), error); } static gboolean wait_link_port_timeout_cb (GTask *task) { WaitLinkPortContext *ctx; MMBaseModem *self; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); ctx->timeout_id = 0; g_signal_handler_disconnect (self, ctx->link_port_grabbed_id); ctx->link_port_grabbed_id = 0; g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_NOT_FOUND, "Timed out waiting for link port 'net/%s'", ctx->name); g_object_unref (task); return G_SOURCE_REMOVE; } static void wait_link_port_grabbed_cb (MMBaseModem *self, MMPort *link_port, GTask *task) { WaitLinkPortContext *ctx; MMPortSubsys link_port_subsystem; const gchar *link_port_name; ctx = g_task_get_task_data (task); link_port_subsystem = mm_port_get_subsys (link_port); link_port_name = mm_port_get_device (link_port); if (link_port_subsystem != MM_PORT_SUBSYS_NET) { mm_obj_warn (self, "unexpected link port subsystem grabbed: %s/%s", mm_port_subsys_get_string (link_port_subsystem), link_port_name); return; } if (g_strcmp0 (link_port_name, ctx->name) != 0) return; /* we got it! */ g_source_remove (ctx->timeout_id); ctx->timeout_id = 0; g_signal_handler_disconnect (self, ctx->link_port_grabbed_id); ctx->link_port_grabbed_id = 0; g_task_return_pointer (task, g_object_ref (link_port), g_object_unref); g_object_unref (task); } void mm_base_modem_wait_link_port (MMBaseModem *self, const gchar *subsystem, const gchar *name, guint timeout_ms, GAsyncReadyCallback callback, gpointer user_data) { WaitLinkPortContext *ctx; GTask *task; g_autofree gchar *key = NULL; MMPort *port; task = g_task_new (self, NULL, callback, user_data); if (!g_str_equal (subsystem, "net")) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "Cannot wait for port '%s/%s', unexpected link port subsystem", subsystem, name); g_object_unref (task); return; } if (!self->priv->link_ports) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Cannot wait for port '%s/%s', no link ports table", subsystem, name); g_object_unref (task); return; } key = g_strdup_printf ("%s%s", subsystem, name); port = g_hash_table_lookup (self->priv->link_ports, key); if (port) { mm_obj_dbg (self, "no need to wait for port '%s/%s': already grabbed", subsystem, name); g_task_return_pointer (task, g_object_ref (port), g_object_unref); g_object_unref (task); return; } ctx = g_slice_new0 (WaitLinkPortContext); ctx->name = g_strdup (name); g_task_set_task_data (task, ctx, (GDestroyNotify)wait_link_port_context_free); /* task ownership shared between timeout and signal handler */ ctx->timeout_id = g_timeout_add (timeout_ms, (GSourceFunc) wait_link_port_timeout_cb, task); ctx->link_port_grabbed_id = g_signal_connect (self, MM_BASE_MODEM_SIGNAL_LINK_PORT_GRABBED, G_CALLBACK (wait_link_port_grabbed_cb), task); mm_obj_dbg (self, "waiting for port '%s/%s'...", subsystem, name); } /******************************************************************************/ #if defined WITH_SUSPEND_RESUME gboolean mm_base_modem_sync_finish (MMBaseModem *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void sync_ready (MMBaseModem *self, GAsyncResult *res, GTask *task) { GError *error = NULL; if (!MM_BASE_MODEM_GET_CLASS (self)->sync_finish (self, res, &error)) g_task_return_error (task, error); else g_task_return_boolean (task, TRUE); g_object_unref (task); } void mm_base_modem_sync (MMBaseModem *self, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; task = g_task_new (self, NULL, callback, user_data); if (!MM_BASE_MODEM_GET_CLASS (self)->sync || !MM_BASE_MODEM_GET_CLASS (self)->sync_finish) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "Suspend/resume quick synchronization unsupported"); g_object_unref (task); return; } MM_BASE_MODEM_GET_CLASS (self)->sync (self, (GAsyncReadyCallback) sync_ready, task); } #endif /* WITH_SUSPEND_RESUME */ /******************************************************************************/ gboolean mm_base_modem_disable_finish (MMBaseModem *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void disable_ready (MMBaseModem *self, GAsyncResult *res) { GError *error = NULL; GList *l; GList *disable_tasks; g_assert (self->priv->disable_tasks); disable_tasks = self->priv->disable_tasks; self->priv->disable_tasks = NULL; MM_BASE_MODEM_GET_CLASS (self)->disable_finish (self, res, &error); for (l = disable_tasks; l; l = g_list_next (l)) { if (error) g_task_return_error (G_TASK (l->data), g_error_copy (error)); else g_task_return_boolean (G_TASK (l->data), TRUE); } g_clear_error (&error); g_list_free_full (disable_tasks, g_object_unref); } void mm_base_modem_disable (MMBaseModem *self, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; gboolean run_disable; g_assert (MM_BASE_MODEM_GET_CLASS (self)->disable != NULL); g_assert (MM_BASE_MODEM_GET_CLASS (self)->disable_finish != NULL); /* If the list of disable tasks is empty, we need to run */ run_disable = !self->priv->disable_tasks; /* Store task */ task = g_task_new (self, self->priv->cancellable, callback, user_data); self->priv->disable_tasks = g_list_append (self->priv->disable_tasks, task); if (!run_disable) return; MM_BASE_MODEM_GET_CLASS (self)->disable ( self, self->priv->cancellable, (GAsyncReadyCallback) disable_ready, NULL); } gboolean mm_base_modem_enable_finish (MMBaseModem *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void enable_ready (MMBaseModem *self, GAsyncResult *res) { GError *error = NULL; GList *l; GList *enable_tasks; g_assert (self->priv->enable_tasks); enable_tasks = self->priv->enable_tasks; self->priv->enable_tasks = NULL; MM_BASE_MODEM_GET_CLASS (self)->enable_finish (self, res, &error); for (l = enable_tasks; l; l = g_list_next (l)) { if (error) g_task_return_error (G_TASK (l->data), g_error_copy (error)); else g_task_return_boolean (G_TASK (l->data), TRUE); } g_clear_error (&error); g_list_free_full (enable_tasks, g_object_unref); } void mm_base_modem_enable (MMBaseModem *self, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; gboolean run_enable; g_assert (MM_BASE_MODEM_GET_CLASS (self)->enable != NULL); g_assert (MM_BASE_MODEM_GET_CLASS (self)->enable_finish != NULL); /* If the list of enable tasks is empty, we need to run */ run_enable = !self->priv->enable_tasks; /* Store task */ task = g_task_new (self, self->priv->cancellable, callback, user_data); self->priv->enable_tasks = g_list_append (self->priv->enable_tasks, task); if (!run_enable) return; MM_BASE_MODEM_GET_CLASS (self)->enable ( self, self->priv->cancellable, (GAsyncReadyCallback) enable_ready, NULL); } gboolean mm_base_modem_initialize_finish (MMBaseModem *self, GAsyncResult *res, GError **error) { return MM_BASE_MODEM_GET_CLASS (self)->initialize_finish (self, res, error); } void mm_base_modem_initialize (MMBaseModem *self, GAsyncReadyCallback callback, gpointer user_data) { g_assert (MM_BASE_MODEM_GET_CLASS (self)->initialize != NULL); g_assert (MM_BASE_MODEM_GET_CLASS (self)->initialize_finish != NULL); MM_BASE_MODEM_GET_CLASS (self)->initialize ( self, self->priv->cancellable, callback, user_data); } void mm_base_modem_set_hotplugged (MMBaseModem *self, gboolean hotplugged) { g_return_if_fail (MM_IS_BASE_MODEM (self)); self->priv->hotplugged = hotplugged; } gboolean mm_base_modem_get_hotplugged (MMBaseModem *self) { g_return_val_if_fail (MM_IS_BASE_MODEM (self), FALSE); return self->priv->hotplugged; } void mm_base_modem_set_valid (MMBaseModem *self, gboolean new_valid) { g_return_if_fail (MM_IS_BASE_MODEM (self)); /* If validity changed OR if both old and new were invalid, notify. This * last case is to cover failures during initialization. */ if (self->priv->valid != new_valid || !new_valid) { self->priv->valid = new_valid; g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_VALID]); } } void mm_base_modem_set_reprobe (MMBaseModem *self, gboolean reprobe) { g_return_if_fail (MM_IS_BASE_MODEM (self)); self->priv->reprobe = reprobe; } gboolean mm_base_modem_get_reprobe (MMBaseModem *self) { g_return_val_if_fail (MM_IS_BASE_MODEM (self), FALSE); return self->priv->reprobe; } gboolean mm_base_modem_get_valid (MMBaseModem *self) { g_return_val_if_fail (MM_IS_BASE_MODEM (self), FALSE); return self->priv->valid; } GCancellable * mm_base_modem_peek_cancellable (MMBaseModem *self) { g_return_val_if_fail (MM_IS_BASE_MODEM (self), NULL); return self->priv->cancellable; } MMPortSerialAt * mm_base_modem_get_port_primary (MMBaseModem *self) { g_return_val_if_fail (MM_IS_BASE_MODEM (self), NULL); return (self->priv->primary ? g_object_ref (self->priv->primary) : NULL); } MMPortSerialAt * mm_base_modem_peek_port_primary (MMBaseModem *self) { g_return_val_if_fail (MM_IS_BASE_MODEM (self), NULL); return self->priv->primary; } MMPortSerialAt * mm_base_modem_get_port_secondary (MMBaseModem *self) { g_return_val_if_fail (MM_IS_BASE_MODEM (self), NULL); return (self->priv->secondary ? g_object_ref (self->priv->secondary) : NULL); } MMPortSerialAt * mm_base_modem_peek_port_secondary (MMBaseModem *self) { g_return_val_if_fail (MM_IS_BASE_MODEM (self), NULL); return self->priv->secondary; } MMPortSerialQcdm * mm_base_modem_get_port_qcdm (MMBaseModem *self) { g_return_val_if_fail (MM_IS_BASE_MODEM (self), NULL); return (self->priv->qcdm ? g_object_ref (self->priv->qcdm) : NULL); } MMPortSerialQcdm * mm_base_modem_peek_port_qcdm (MMBaseModem *self) { g_return_val_if_fail (MM_IS_BASE_MODEM (self), NULL); return self->priv->qcdm; } MMPortSerialAt * mm_base_modem_get_port_gps_control (MMBaseModem *self) { g_return_val_if_fail (MM_IS_BASE_MODEM (self), NULL); return (self->priv->gps_control ? g_object_ref (self->priv->gps_control) : NULL); } MMPortSerialAt * mm_base_modem_peek_port_gps_control (MMBaseModem *self) { g_return_val_if_fail (MM_IS_BASE_MODEM (self), NULL); return self->priv->gps_control; } MMPortSerialGps * mm_base_modem_get_port_gps (MMBaseModem *self) { g_return_val_if_fail (MM_IS_BASE_MODEM (self), NULL); return (self->priv->gps ? g_object_ref (self->priv->gps) : NULL); } MMPortSerialGps * mm_base_modem_peek_port_gps (MMBaseModem *self) { g_return_val_if_fail (MM_IS_BASE_MODEM (self), NULL); return self->priv->gps; } MMPortSerial * mm_base_modem_get_port_audio (MMBaseModem *self) { g_return_val_if_fail (MM_IS_BASE_MODEM (self), NULL); return (self->priv->audio ? g_object_ref (self->priv->audio) : NULL); } MMPortSerial * mm_base_modem_peek_port_audio (MMBaseModem *self) { g_return_val_if_fail (MM_IS_BASE_MODEM (self), NULL); return self->priv->audio; } MMPort * mm_base_modem_get_best_data_port (MMBaseModem *self, MMPortType type) { MMPort *port; port = mm_base_modem_peek_best_data_port (self, type); return (port ? g_object_ref (port) : NULL); } MMPort * mm_base_modem_peek_best_data_port (MMBaseModem *self, MMPortType type) { GList *l; g_return_val_if_fail (MM_IS_BASE_MODEM (self), NULL); /* Return first not-connected data port */ for (l = self->priv->data; l; l = g_list_next (l)) { if (!mm_port_get_connected ((MMPort *)l->data) && (mm_port_get_port_type ((MMPort *)l->data) == type || type == MM_PORT_TYPE_UNKNOWN)) { return (MMPort *)l->data; } } return NULL; } GList * mm_base_modem_get_data_ports (MMBaseModem *self) { g_return_val_if_fail (MM_IS_BASE_MODEM (self), NULL); return g_list_copy_deep (self->priv->data, (GCopyFunc)g_object_ref, NULL); } GList * mm_base_modem_peek_data_ports (MMBaseModem *self) { g_return_val_if_fail (MM_IS_BASE_MODEM (self), NULL); return self->priv->data; } MMPortSerialAt * mm_base_modem_get_best_at_port (MMBaseModem *self, GError **error) { MMPortSerialAt *best; best = mm_base_modem_peek_best_at_port (self, error); return (best ? g_object_ref (best) : NULL); } MMPortSerialAt * mm_base_modem_peek_best_at_port (MMBaseModem *self, GError **error) { /* Decide which port to use */ if (self->priv->primary && !mm_port_get_connected (MM_PORT (self->priv->primary))) return self->priv->primary; /* If primary port is connected, check if we can get the secondary * port */ if (self->priv->secondary && !mm_port_get_connected (MM_PORT (self->priv->secondary))) return self->priv->secondary; /* Otherwise, we cannot get any port */ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_CONNECTED, "No AT port available to run command"); return NULL; } gboolean mm_base_modem_has_at_port (MMBaseModem *self) { GHashTableIter iter; gpointer value; gpointer key; if (!self->priv->ports) return FALSE; /* We'll iterate the ht of ports, looking for any port which is AT */ g_hash_table_iter_init (&iter, self->priv->ports); while (g_hash_table_iter_next (&iter, &key, &value)) { if (MM_IS_PORT_SERIAL_AT (value)) return TRUE; } return FALSE; } static gint port_info_cmp (const MMModemPortInfo *a, const MMModemPortInfo *b) { /* default to alphabetical sorting on the port name */ return g_strcmp0 (a->name, b->name); } MMModemPortInfo * mm_base_modem_get_port_infos (MMBaseModem *self, guint *n_port_infos) { GHashTableIter iter; GArray *port_infos; MMPort *port; if (!self->priv->ports) { *n_port_infos = 0; return NULL; } *n_port_infos = g_hash_table_size (self->priv->ports); port_infos = g_array_sized_new (FALSE, FALSE, sizeof (MMModemPortInfo), *n_port_infos); g_hash_table_iter_init (&iter, self->priv->ports); while (g_hash_table_iter_next (&iter, NULL, (gpointer)&port)) { MMModemPortInfo port_info; port_info.name = g_strdup (mm_port_get_device (port)); switch (mm_port_get_port_type (port)) { case MM_PORT_TYPE_NET: port_info.type = MM_MODEM_PORT_TYPE_NET; break; case MM_PORT_TYPE_AT: port_info.type = MM_MODEM_PORT_TYPE_AT; break; case MM_PORT_TYPE_QCDM: port_info.type = MM_MODEM_PORT_TYPE_QCDM; break; case MM_PORT_TYPE_GPS: port_info.type = MM_MODEM_PORT_TYPE_GPS; break; case MM_PORT_TYPE_AUDIO: port_info.type = MM_MODEM_PORT_TYPE_AUDIO; break; case MM_PORT_TYPE_QMI: port_info.type = MM_MODEM_PORT_TYPE_QMI; break; case MM_PORT_TYPE_MBIM: port_info.type = MM_MODEM_PORT_TYPE_MBIM; break; case MM_PORT_TYPE_IGNORED: port_info.type = MM_MODEM_PORT_TYPE_IGNORED; break; case MM_PORT_TYPE_UNKNOWN: default: port_info.type = MM_MODEM_PORT_TYPE_UNKNOWN; break; } g_array_append_val (port_infos, port_info); } g_assert (*n_port_infos == port_infos->len); g_array_sort (port_infos, (GCompareFunc) port_info_cmp); return (MMModemPortInfo *) g_array_free (port_infos, FALSE); } static gint port_cmp (MMPort *a, MMPort *b) { /* default to alphabetical sorting on the port name */ return g_strcmp0 (mm_port_get_device (a), mm_port_get_device (b)); } GList * mm_base_modem_find_ports (MMBaseModem *self, MMPortSubsys subsys, MMPortType type) { GList *out = NULL; GHashTableIter iter; gpointer value; gpointer key; if (!self->priv->ports) return NULL; g_hash_table_iter_init (&iter, self->priv->ports); while (g_hash_table_iter_next (&iter, &key, &value)) { MMPort *port = MM_PORT (value); if (subsys != MM_PORT_SUBSYS_UNKNOWN && mm_port_get_subsys (port) != subsys) continue; if (type != MM_PORT_TYPE_UNKNOWN && mm_port_get_port_type (port) != type) continue; out = g_list_append (out, g_object_ref (port)); } return g_list_sort (out, (GCompareFunc) port_cmp); } static MMPort * peek_port_in_ht (GHashTable *ht, const gchar *name) { GHashTableIter iter; gpointer value; gpointer key; if (!ht) return NULL; g_hash_table_iter_init (&iter, ht); while (g_hash_table_iter_next (&iter, &key, &value)) { MMPort *port = MM_PORT (value); if (g_str_equal (mm_port_get_device (port), name)) return port; } return NULL; } MMPort * mm_base_modem_peek_port (MMBaseModem *self, const gchar *name) { MMPort *found; found = peek_port_in_ht (self->priv->ports, name); if (!found) found = peek_port_in_ht (self->priv->link_ports, name); return found; } MMPort * mm_base_modem_get_port (MMBaseModem *self, const gchar *name) { MMPort *port; port = mm_base_modem_peek_port (self, name); return (port ? g_object_ref (port) : NULL); } static void initialize_ready (MMBaseModem *self, GAsyncResult *res) { g_autoptr(GError) error = NULL; if (!mm_base_modem_initialize_finish (self, res, &error)) { if (g_error_matches (error, MM_CORE_ERROR, MM_CORE_ERROR_ABORTED)) { /* FATAL error, won't even be exported in DBus */ mm_obj_err (self, "fatal error initializing: %s", error->message); } else { /* non-fatal error */ mm_obj_warn (self, "error initializing: %s", error->message); mm_base_modem_set_valid (self, TRUE); } } else { mm_obj_dbg (self, "modem initialized"); mm_base_modem_set_valid (self, TRUE); } } static inline void log_port (MMBaseModem *self, MMPort *port, const gchar *desc) { if (!port) return; mm_obj_info (self, "%s/%s: %s", mm_port_subsys_get_string (mm_port_get_subsys (port)), mm_port_get_device (port), desc); } gboolean mm_base_modem_organize_ports (MMBaseModem *self, GError **error) { GHashTableIter iter; MMPort *candidate; MMPortSerialAtFlag flags; MMPortSerialAt *backup_primary = NULL; MMPortSerialAt *primary = NULL; MMPortSerialAt *secondary = NULL; MMPortSerialAt *backup_secondary = NULL; MMPortSerialQcdm *qcdm = NULL; MMPortSerialAt *gps_control = NULL; MMPortSerialGps *gps = NULL; MMPortSerial *audio = NULL; MMPortSerialAt *data_at_primary = NULL; GList *l; /* These lists don't keep full references, so they should be * g_list_free()-ed on error exits */ g_autoptr(GList) data_at = NULL; g_autoptr(GList) data_net = NULL; #if defined WITH_QMI g_autoptr(GList) qmi = NULL; #endif #if defined WITH_MBIM g_autoptr(GList) mbim = NULL; #endif g_return_val_if_fail (MM_IS_BASE_MODEM (self), FALSE); /* If ports have already been organized, just return success */ if (self->priv->primary) return TRUE; /* Ports table is created on init and removed on dispose(), not on * finalize(), so there is a chance this may happen */ if (!self->priv->ports) { g_set_error_literal (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "No ports table"); return FALSE; } g_hash_table_iter_init (&iter, self->priv->ports); while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &candidate)) { switch (mm_port_get_port_type (candidate)) { case MM_PORT_TYPE_AT: g_assert (MM_IS_PORT_SERIAL_AT (candidate)); flags = mm_port_serial_at_get_flags (MM_PORT_SERIAL_AT (candidate)); if (flags & MM_PORT_SERIAL_AT_FLAG_PRIMARY) { if (!primary) primary = MM_PORT_SERIAL_AT (candidate); else if (!backup_primary) { /* Just in case the plugin gave us more than one primary * and no secondaries, treat additional primary ports as * secondary. */ backup_primary = MM_PORT_SERIAL_AT (candidate); } } if (flags & MM_PORT_SERIAL_AT_FLAG_PPP) { if (!data_at_primary) data_at_primary = MM_PORT_SERIAL_AT (candidate); else data_at = g_list_append (data_at, candidate); } /* Explicitly flagged secondary ports trump NONE ports for secondary */ if (flags & MM_PORT_SERIAL_AT_FLAG_SECONDARY) { if (!secondary || !(mm_port_serial_at_get_flags (secondary) & MM_PORT_SERIAL_AT_FLAG_SECONDARY)) secondary = MM_PORT_SERIAL_AT (candidate); } if (flags & MM_PORT_SERIAL_AT_FLAG_GPS_CONTROL) { if (!gps_control) gps_control = MM_PORT_SERIAL_AT (candidate); } /* Fallback secondary */ if (flags == MM_PORT_SERIAL_AT_FLAG_NONE) { if (!secondary) secondary = MM_PORT_SERIAL_AT (candidate); else if (!backup_secondary) backup_secondary = MM_PORT_SERIAL_AT (candidate); } break; case MM_PORT_TYPE_QCDM: g_assert (MM_IS_PORT_SERIAL_QCDM (candidate)); if (!qcdm) qcdm = MM_PORT_SERIAL_QCDM (candidate); break; case MM_PORT_TYPE_NET: data_net = g_list_append (data_net, candidate); break; case MM_PORT_TYPE_GPS: g_assert (MM_IS_PORT_SERIAL_GPS (candidate)); if (!gps) gps = MM_PORT_SERIAL_GPS (candidate); break; case MM_PORT_TYPE_AUDIO: g_assert (MM_IS_PORT_SERIAL (candidate)); if (!audio) audio = MM_PORT_SERIAL (candidate); break; #if defined WITH_QMI case MM_PORT_TYPE_QMI: qmi = g_list_append (qmi, candidate); break; #endif #if defined WITH_MBIM case MM_PORT_TYPE_MBIM: mbim = g_list_append (mbim, candidate); break; #endif case MM_PORT_TYPE_UNKNOWN: case MM_PORT_TYPE_IGNORED: #if !defined WITH_MBIM case MM_PORT_TYPE_MBIM: #endif #if !defined WITH_QMI case MM_PORT_TYPE_QMI: #endif default: /* Ignore port */ break; } } if (!primary) { /* Fall back to a secondary port if we didn't find a primary port */ if (secondary) { primary = secondary; secondary = NULL; } /* Fallback to a data port if no primary or secondary */ else if (data_at_primary) { primary = data_at_primary; data_at_primary = NULL; } else { gboolean allow_modem_without_at_port = FALSE; #if defined WITH_QMI if (qmi) allow_modem_without_at_port = TRUE; #endif #if defined WITH_MBIM if (mbim) allow_modem_without_at_port = TRUE; #endif if (!allow_modem_without_at_port) { g_set_error_literal (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Failed to find primary AT port"); return FALSE; } } } /* If the plugin didn't give us any secondary ports, use any additional * primary ports or backup secondary ports as secondary. */ if (!secondary) secondary = backup_primary ? backup_primary : backup_secondary; #if defined WITH_QMI /* On QMI-based modems, we need to have at least a net port */ if (qmi && !data_net) { g_set_error_literal (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Failed to find a net port in the QMI modem"); return FALSE; } #endif #if defined WITH_MBIM /* On MBIM-based modems, we need to have at least a net port */ if (mbim && !data_net) { g_set_error_literal (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Failed to find a net port in the MBIM modem"); return FALSE; } #endif /* Data port defaults to primary AT port */ if (primary && !data_at_primary) data_at_primary = primary; /* Reset flags on all ports; clear data port first since it might also * be the primary or secondary port. */ if (data_at_primary) mm_port_serial_at_set_flags (data_at_primary, MM_PORT_SERIAL_AT_FLAG_NONE); if (primary) mm_port_serial_at_set_flags (primary, MM_PORT_SERIAL_AT_FLAG_PRIMARY); if (secondary) mm_port_serial_at_set_flags (secondary, MM_PORT_SERIAL_AT_FLAG_SECONDARY); if (data_at_primary) { flags = mm_port_serial_at_get_flags (data_at_primary); mm_port_serial_at_set_flags (data_at_primary, flags | MM_PORT_SERIAL_AT_FLAG_PPP); } /* sort ports by name */ #if defined WITH_QMI qmi = g_list_sort (qmi, (GCompareFunc) port_cmp); #endif #if defined WITH_MBIM mbim = g_list_sort (mbim, (GCompareFunc) port_cmp); #endif data_net = g_list_sort (data_net, (GCompareFunc) port_cmp); data_at = g_list_sort (data_at, (GCompareFunc) port_cmp); log_port (self, MM_PORT (primary), "at (primary)"); log_port (self, MM_PORT (secondary), "at (secondary)"); log_port (self, MM_PORT (data_at_primary), "at (data primary)"); for (l = data_at; l; l = g_list_next (l)) log_port (self, MM_PORT (l->data), "at (data secondary)"); for (l = data_net; l; l = g_list_next (l)) log_port (self, MM_PORT (l->data), "net (data)"); log_port (self, MM_PORT (qcdm), "qcdm"); log_port (self, MM_PORT (gps_control), "gps (control)"); log_port (self, MM_PORT (gps), "gps (nmea)"); log_port (self, MM_PORT (audio), "audio"); #if defined WITH_QMI for (l = qmi; l; l = g_list_next (l)) log_port (self, MM_PORT (l->data), "qmi"); #endif #if defined WITH_MBIM for (l = mbim; l; l = g_list_next (l)) log_port (self, MM_PORT (l->data), "mbim"); #endif /* We keep new refs to the objects here */ self->priv->primary = (primary ? g_object_ref (primary) : NULL); self->priv->secondary = (secondary ? g_object_ref (secondary) : NULL); self->priv->qcdm = (qcdm ? g_object_ref (qcdm) : NULL); self->priv->gps_control = (gps_control ? g_object_ref (gps_control) : NULL); self->priv->gps = (gps ? g_object_ref (gps) : NULL); /* Append net ports to the final list of data ports, but only if the modem * supports them */ if (data_net) { if (self->priv->data_net_supported) { g_list_foreach (data_net, (GFunc)g_object_ref, NULL); self->priv->data = g_list_concat (self->priv->data, g_steal_pointer (&data_net)); } else mm_obj_dbg (self, "net ports available but ignored"); } /* Append tty ports to the final list of data ports, but only if the modem * supports them */ if (data_at_primary || data_at) { if (self->priv->data_tty_supported) { if (data_at_primary) self->priv->data = g_list_append (self->priv->data, g_object_ref (data_at_primary)); if (data_at) { g_list_foreach (data_at, (GFunc)g_object_ref, NULL); self->priv->data = g_list_concat (self->priv->data, g_steal_pointer (&data_at)); } } else mm_obj_dbg (self, "at data ports available but ignored"); } /* Fail if we haven't added any single data port; this is probably a plugin * misconfiguration */ if (!self->priv->data) { g_set_error_literal (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Failed to find a data port in the modem"); return FALSE; } #if defined WITH_QMI if (qmi) { /* The first item in the data list must be a net port, because * QMI modems only expect net ports */ g_assert (MM_IS_PORT_NET (self->priv->data->data)); /* let the MMPortQmi know which net driver is being used, taken * from the first item in the net port list */ g_list_foreach (qmi, (GFunc)mm_port_qmi_set_net_details, (gpointer) MM_PORT (self->priv->data->data)); g_list_foreach (qmi, (GFunc)g_object_ref, NULL); self->priv->qmi = g_steal_pointer (&qmi); } #endif #if defined WITH_MBIM if (mbim) { g_list_foreach (mbim, (GFunc)g_object_ref, NULL); self->priv->mbim = g_steal_pointer (&mbim); } #endif /* As soon as we get the ports organized, we initialize the modem */ mm_base_modem_initialize (self, (GAsyncReadyCallback)initialize_ready, NULL); return TRUE; } /*****************************************************************************/ /* Authorization */ gboolean mm_base_modem_authorize_finish (MMBaseModem *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void authorize_ready (MMAuthProvider *authp, GAsyncResult *res, GTask *task) { GError *error = NULL; if (!mm_auth_provider_authorize_finish (authp, res, &error)) g_task_return_error (task, error); else g_task_return_boolean (task, TRUE); g_object_unref (task); } void mm_base_modem_authorize (MMBaseModem *self, GDBusMethodInvocation *invocation, const gchar *authorization, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; task = g_task_new (self, self->priv->authp_cancellable, callback, user_data); /* When running in the session bus for tests, default to always allow */ if (mm_context_get_test_session ()) { g_task_return_boolean (task, TRUE); g_object_unref (task); return; } mm_auth_provider_authorize (self->priv->authp, invocation, authorization, self->priv->authp_cancellable, (GAsyncReadyCallback)authorize_ready, task); } /*****************************************************************************/ const gchar * mm_base_modem_get_device (MMBaseModem *self) { g_return_val_if_fail (MM_IS_BASE_MODEM (self), NULL); return self->priv->device; } const gchar * mm_base_modem_get_physdev (MMBaseModem *self) { g_return_val_if_fail (MM_IS_BASE_MODEM (self), NULL); return self->priv->physdev; } const gchar ** mm_base_modem_get_drivers (MMBaseModem *self) { g_return_val_if_fail (MM_IS_BASE_MODEM (self), NULL); return (const gchar **)self->priv->drivers; } const gchar * mm_base_modem_get_plugin (MMBaseModem *self) { g_return_val_if_fail (MM_IS_BASE_MODEM (self), NULL); return self->priv->plugin; } guint mm_base_modem_get_vendor_id (MMBaseModem *self) { g_return_val_if_fail (MM_IS_BASE_MODEM (self), 0); return self->priv->vendor_id; } guint mm_base_modem_get_product_id (MMBaseModem *self) { g_return_val_if_fail (MM_IS_BASE_MODEM (self), 0); return self->priv->product_id; } guint mm_base_modem_get_subsystem_vendor_id (MMBaseModem *self) { g_return_val_if_fail (MM_IS_BASE_MODEM (self), 0); return self->priv->subsystem_vendor_id; } /*****************************************************************************/ static gboolean base_modem_invalid_idle (MMBaseModem *self) { /* Ensure the modem is set invalid if we get the modem-wide cancellable * cancelled */ mm_base_modem_set_valid (self, FALSE); g_object_unref (self); return G_SOURCE_REMOVE; } static void base_modem_cancelled (GCancellable *cancellable, MMBaseModem *self) { /* NOTE: Don't call set_valid() directly here, do it in an idle, and ensure * that we pass a valid reference of the modem object as context. */ g_idle_add ((GSourceFunc)base_modem_invalid_idle, g_object_ref (self)); } /*****************************************************************************/ static void setup_ports_table (GHashTable **ht) { g_assert (ht && !*ht); *ht = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref); } static void cleanup_modem_port (MMBaseModem *self, MMPort *port) { mm_obj_dbg (self, "cleaning up port '%s/%s'...", mm_port_subsys_get_string (mm_port_get_subsys (MM_PORT (port))), mm_port_get_device (MM_PORT (port))); /* Cleanup on all control ports */ g_signal_handlers_disconnect_by_func (port, port_timed_out_cb, self); g_signal_handlers_disconnect_by_func (port, port_removed_cb, self); #if defined WITH_MBIM /* We need to close the MBIM port cleanly when disposing the modem object */ if (MM_IS_PORT_MBIM (port)) { mm_port_mbim_close (MM_PORT_MBIM (port), NULL, NULL); return; } #endif #if defined WITH_QMI /* We need to close the QMI port cleanly when disposing the modem object, * otherwise the allocated CIDs will be kept allocated, and if we end up * allocating too many newer allocations will fail with client-ids-exhausted * errors. */ if (MM_IS_PORT_QMI (port)) { mm_port_qmi_close (MM_PORT_QMI (port), NULL, NULL); return; } #endif } static void teardown_ports_table (MMBaseModem *self, GHashTable **ht) { GHashTableIter iter; gpointer value; gpointer key; if (!*ht) return; g_hash_table_iter_init (&iter, *ht); while (g_hash_table_iter_next (&iter, &key, &value)) cleanup_modem_port (self, MM_PORT (value)); g_hash_table_destroy (g_steal_pointer (ht)); } /*****************************************************************************/ static gchar * log_object_build_id (MMLogObject *_self) { MMBaseModem *self; self = MM_BASE_MODEM (_self); return g_strdup_printf ("modem%u", self->priv->dbus_id); } /*****************************************************************************/ static void mm_base_modem_init (MMBaseModem *self) { static guint id = 0; /* Initialize private data */ self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, MM_TYPE_BASE_MODEM, MMBaseModemPrivate); /* Each modem is given a unique id to build its own DBus path */ self->priv->dbus_id = id++; /* Setup authorization provider */ self->priv->authp = mm_auth_provider_get (); self->priv->authp_cancellable = g_cancellable_new (); /* Setup modem-wide cancellable */ self->priv->cancellable = g_cancellable_new (); self->priv->invalid_if_cancelled = g_cancellable_connect (self->priv->cancellable, G_CALLBACK (base_modem_cancelled), self, NULL); self->priv->max_timeouts = DEFAULT_MAX_TIMEOUTS; setup_ports_table (&self->priv->ports); setup_ports_table (&self->priv->link_ports); } static void set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { MMBaseModem *self = MM_BASE_MODEM (object); switch (prop_id) { case PROP_VALID: mm_base_modem_set_valid (self, g_value_get_boolean (value)); break; case PROP_REPROBE: mm_base_modem_set_reprobe (self, g_value_get_boolean (value)); break; case PROP_MAX_TIMEOUTS: self->priv->max_timeouts = g_value_get_uint (value); break; case PROP_DEVICE: g_free (self->priv->device); self->priv->device = g_value_dup_string (value); break; case PROP_PHYSDEV: g_free (self->priv->physdev); self->priv->physdev = g_value_dup_string (value); break; case PROP_DRIVERS: g_strfreev (self->priv->drivers); self->priv->drivers = g_value_dup_boxed (value); break; case PROP_PLUGIN: g_free (self->priv->plugin); self->priv->plugin = g_value_dup_string (value); break; case PROP_VENDOR_ID: self->priv->vendor_id = g_value_get_uint (value); break; case PROP_PRODUCT_ID: self->priv->product_id = g_value_get_uint (value); break; case PROP_SUBSYSTEM_VENDOR_ID: self->priv->subsystem_vendor_id = g_value_get_uint (value); break; case PROP_CONNECTION: g_clear_object (&self->priv->connection); self->priv->connection = g_value_dup_object (value); break; case PROP_DATA_NET_SUPPORTED: self->priv->data_net_supported = g_value_get_boolean (value); break; case PROP_DATA_TTY_SUPPORTED: self->priv->data_tty_supported = 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) { MMBaseModem *self = MM_BASE_MODEM (object); switch (prop_id) { case PROP_VALID: g_value_set_boolean (value, self->priv->valid); break; case PROP_REPROBE: g_value_set_boolean (value, self->priv->reprobe); break; case PROP_MAX_TIMEOUTS: g_value_set_uint (value, self->priv->max_timeouts); break; case PROP_DEVICE: g_value_set_string (value, self->priv->device); break; case PROP_PHYSDEV: g_value_set_string (value, self->priv->physdev); break; case PROP_DRIVERS: g_value_set_boxed (value, self->priv->drivers); break; case PROP_PLUGIN: g_value_set_string (value, self->priv->plugin); break; case PROP_VENDOR_ID: g_value_set_uint (value, self->priv->vendor_id); break; case PROP_PRODUCT_ID: g_value_set_uint (value, self->priv->product_id); break; case PROP_SUBSYSTEM_VENDOR_ID: g_value_set_uint (value, self->priv->subsystem_vendor_id); break; case PROP_CONNECTION: g_value_set_object (value, self->priv->connection); break; case PROP_DATA_NET_SUPPORTED: g_value_set_boolean (value, self->priv->data_net_supported); break; case PROP_DATA_TTY_SUPPORTED: g_value_set_boolean (value, self->priv->data_tty_supported); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void finalize (GObject *object) { MMBaseModem *self = MM_BASE_MODEM (object); /* TODO * mm_auth_provider_cancel_for_owner (self->priv->authp, object); */ g_assert (!self->priv->enable_tasks); g_assert (!self->priv->disable_tasks); mm_obj_dbg (self, "completely disposed"); g_free (self->priv->device); g_free (self->priv->physdev); g_strfreev (self->priv->drivers); g_free (self->priv->plugin); G_OBJECT_CLASS (mm_base_modem_parent_class)->finalize (object); } static void dispose (GObject *object) { MMBaseModem *self = MM_BASE_MODEM (object); /* Cancel all ongoing auth requests */ g_cancellable_cancel (self->priv->authp_cancellable); g_clear_object (&self->priv->authp_cancellable); /* note: authp is a singleton, we don't keep a full reference */ /* Ensure we cancel any ongoing operation, but before * disconnect our own signal handler, or we'll end up with * another reference of the modem object around. */ g_cancellable_disconnect (self->priv->cancellable, self->priv->invalid_if_cancelled); g_cancellable_cancel (self->priv->cancellable); g_clear_object (&self->priv->cancellable); g_clear_object (&self->priv->primary); g_clear_object (&self->priv->secondary); g_list_free_full (g_steal_pointer (&self->priv->data), g_object_unref); g_clear_object (&self->priv->qcdm); g_clear_object (&self->priv->gps_control); g_clear_object (&self->priv->gps); g_clear_object (&self->priv->audio); #if defined WITH_QMI g_list_free_full (g_steal_pointer (&self->priv->qmi), g_object_unref); #endif #if defined WITH_MBIM g_list_free_full (g_steal_pointer (&self->priv->mbim), g_object_unref); #endif teardown_ports_table (self, &self->priv->link_ports); teardown_ports_table (self, &self->priv->ports); g_clear_object (&self->priv->connection); G_OBJECT_CLASS (mm_base_modem_parent_class)->dispose (object); } static void log_object_iface_init (MMLogObjectInterface *iface) { iface->build_id = log_object_build_id; } static void mm_base_modem_class_init (MMBaseModemClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); g_type_class_add_private (object_class, sizeof (MMBaseModemPrivate)); /* Virtual methods */ object_class->get_property = get_property; object_class->set_property = set_property; object_class->finalize = finalize; object_class->dispose = dispose; properties[PROP_MAX_TIMEOUTS] = g_param_spec_uint (MM_BASE_MODEM_MAX_TIMEOUTS, "Max timeouts", "Maximum number of consecutive timed out commands sent to " "the modem before disabling it. If 0, this feature is disabled.", 0, G_MAXUINT, DEFAULT_MAX_TIMEOUTS, G_PARAM_READWRITE); g_object_class_install_property (object_class, PROP_MAX_TIMEOUTS, properties[PROP_MAX_TIMEOUTS]); properties[PROP_VALID] = g_param_spec_boolean (MM_BASE_MODEM_VALID, "Valid", "Whether the modem is to be considered valid or not.", FALSE, G_PARAM_READWRITE); g_object_class_install_property (object_class, PROP_VALID, properties[PROP_VALID]); properties[PROP_DEVICE] = g_param_spec_string (MM_BASE_MODEM_DEVICE, "Device", "Main modem parent device of all the modem's ports", NULL, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY); g_object_class_install_property (object_class, PROP_DEVICE, properties[PROP_DEVICE]); properties[PROP_PHYSDEV] = g_param_spec_string (MM_BASE_MODEM_PHYSDEV, "Physdev path", "Main modem parent physical device path", NULL, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY); g_object_class_install_property (object_class, PROP_PHYSDEV, properties[PROP_PHYSDEV]); properties[PROP_DRIVERS] = g_param_spec_boxed (MM_BASE_MODEM_DRIVERS, "Drivers", "Kernel drivers", G_TYPE_STRV, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY); g_object_class_install_property (object_class, PROP_DRIVERS, properties[PROP_DRIVERS]); properties[PROP_PLUGIN] = g_param_spec_string (MM_BASE_MODEM_PLUGIN, "Plugin", "Name of the plugin managing this modem", NULL, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY); g_object_class_install_property (object_class, PROP_PLUGIN, properties[PROP_PLUGIN]); properties[PROP_VENDOR_ID] = g_param_spec_uint (MM_BASE_MODEM_VENDOR_ID, "Hardware vendor ID", "Hardware vendor ID. May be unknown for serial devices.", 0, G_MAXUINT, 0, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY); g_object_class_install_property (object_class, PROP_VENDOR_ID, properties[PROP_VENDOR_ID]); properties[PROP_PRODUCT_ID] = g_param_spec_uint (MM_BASE_MODEM_PRODUCT_ID, "Hardware product ID", "Hardware product ID. May be unknown for serial devices.", 0, G_MAXUINT, 0, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY); g_object_class_install_property (object_class, PROP_PRODUCT_ID, properties[PROP_PRODUCT_ID]); properties[PROP_SUBSYSTEM_VENDOR_ID] = g_param_spec_uint (MM_BASE_MODEM_SUBSYSTEM_VENDOR_ID, "Hardware subsystem vendor ID", "Hardware subsystem vendor ID. Available for pci devices.", 0, G_MAXUINT, 0, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY); g_object_class_install_property (object_class, PROP_SUBSYSTEM_VENDOR_ID, properties[PROP_SUBSYSTEM_VENDOR_ID]); properties[PROP_CONNECTION] = g_param_spec_object (MM_BASE_MODEM_CONNECTION, "Connection", "GDBus connection to the system bus.", G_TYPE_DBUS_CONNECTION, G_PARAM_READWRITE); g_object_class_install_property (object_class, PROP_CONNECTION, properties[PROP_CONNECTION]); properties[PROP_REPROBE] = g_param_spec_boolean (MM_BASE_MODEM_REPROBE, "Reprobe", "Whether the modem needs to be reprobed or not.", FALSE, G_PARAM_READWRITE); g_object_class_install_property (object_class, PROP_REPROBE, properties[PROP_REPROBE]); properties[PROP_DATA_NET_SUPPORTED] = g_param_spec_boolean (MM_BASE_MODEM_DATA_NET_SUPPORTED, "Data NET supported", "Whether the modem supports connection via a NET port.", FALSE, G_PARAM_READWRITE); g_object_class_install_property (object_class, PROP_DATA_NET_SUPPORTED, properties[PROP_DATA_NET_SUPPORTED]); properties[PROP_DATA_TTY_SUPPORTED] = g_param_spec_boolean (MM_BASE_MODEM_DATA_TTY_SUPPORTED, "Data TTY supported", "Whether the modem supports connection via a TTY port.", FALSE, G_PARAM_READWRITE); g_object_class_install_property (object_class, PROP_DATA_TTY_SUPPORTED, properties[PROP_DATA_TTY_SUPPORTED]); signals[SIGNAL_LINK_PORT_GRABBED] = g_signal_new (MM_BASE_MODEM_SIGNAL_LINK_PORT_GRABBED, G_OBJECT_CLASS_TYPE (object_class), G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (MMBaseModemClass, link_port_grabbed), NULL, NULL, g_cclosure_marshal_generic, G_TYPE_NONE, 1, MM_TYPE_PORT); signals[SIGNAL_LINK_PORT_RELEASED] = g_signal_new (MM_BASE_MODEM_SIGNAL_LINK_PORT_RELEASED, G_OBJECT_CLASS_TYPE (object_class), G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (MMBaseModemClass, link_port_released), NULL, NULL, g_cclosure_marshal_generic, G_TYPE_NONE, 1, MM_TYPE_PORT); } ModemManager-1.23.4-dev/src/mm-base-modem.h000066400000000000000000000276471456466623000203250ustar00rootroot00000000000000/* -*- 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 - 2011 Red Hat, Inc. * Copyright (C) 2011 Google, Inc. */ #ifndef MM_BASE_MODEM_H #define MM_BASE_MODEM_H #include "config.h" #include #include #define _LIBMM_INSIDE_MM #include #include #include "mm-auth-provider.h" #include "mm-kernel-device.h" #include "mm-port.h" #include "mm-port-net.h" #include "mm-port-serial-at.h" #include "mm-port-serial-qcdm.h" #include "mm-port-serial-gps.h" #if defined WITH_QMI #include "mm-port-qmi.h" #endif #if defined WITH_MBIM #include "mm-port-mbim.h" #endif #define MM_TYPE_BASE_MODEM (mm_base_modem_get_type ()) #define MM_BASE_MODEM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_BASE_MODEM, MMBaseModem)) #define MM_BASE_MODEM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_BASE_MODEM, MMBaseModemClass)) #define MM_IS_BASE_MODEM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_BASE_MODEM)) #define MM_IS_BASE_MODEM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_BASE_MODEM)) #define MM_BASE_MODEM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_BASE_MODEM, MMBaseModemClass)) typedef struct _MMBaseModem MMBaseModem; typedef struct _MMBaseModemClass MMBaseModemClass; typedef struct _MMBaseModemPrivate MMBaseModemPrivate; #define MM_BASE_MODEM_CONNECTION "base-modem-connection" #define MM_BASE_MODEM_MAX_TIMEOUTS "base-modem-max-timeouts" #define MM_BASE_MODEM_VALID "base-modem-valid" #define MM_BASE_MODEM_DEVICE "base-modem-device" #define MM_BASE_MODEM_PHYSDEV "base-modem-physdev-path" #define MM_BASE_MODEM_DRIVERS "base-modem-drivers" #define MM_BASE_MODEM_PLUGIN "base-modem-plugin" #define MM_BASE_MODEM_VENDOR_ID "base-modem-vendor-id" #define MM_BASE_MODEM_PRODUCT_ID "base-modem-product-id" #define MM_BASE_MODEM_SUBSYSTEM_VENDOR_ID "base-modem-subsystem-vendor-id" #define MM_BASE_MODEM_REPROBE "base-modem-reprobe" #define MM_BASE_MODEM_DATA_NET_SUPPORTED "base-modem-data-net-supported" #define MM_BASE_MODEM_DATA_TTY_SUPPORTED "base-modem-data-tty-supported" #define MM_BASE_MODEM_SIGNAL_LINK_PORT_GRABBED "base-modem-link-port-grabbed" #define MM_BASE_MODEM_SIGNAL_LINK_PORT_RELEASED "base-modem-link-port-released" struct _MMBaseModem { MmGdbusObjectSkeleton parent; MMBaseModemPrivate *priv; }; struct _MMBaseModemClass { MmGdbusObjectSkeletonClass parent; /* Modem initialization. * As soon as the ports are organized, this method gets called */ void (* initialize) (MMBaseModem *self, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); gboolean (*initialize_finish) (MMBaseModem *self, GAsyncResult *res, GError **error); /* Modem enabling. * User action requested from DBus, usually */ void (* enable) (MMBaseModem *self, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); gboolean (*enable_finish) (MMBaseModem *self, GAsyncResult *res, GError **error); /* Modem disabling. * User action requested from DBus, usually */ void (* disable) (MMBaseModem *self, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); gboolean (*disable_finish) (MMBaseModem *self, GAsyncResult *res, GError **error); #if defined WITH_SUSPEND_RESUME /* Modem synchronization. * When resuming in quick suspend/resume mode, * this method triggers a synchronization of all modem interfaces */ void (* sync) (MMBaseModem *self, GAsyncReadyCallback callback, gpointer user_data); gboolean (* sync_finish) (MMBaseModem *self, GAsyncResult *res, GError **error); #endif /* signals */ void (* link_port_grabbed) (MMBaseModem *self, MMPort *link_port); void (* link_port_released) (MMBaseModem *self, MMPort *link_port); }; GType mm_base_modem_get_type (void); G_DEFINE_AUTOPTR_CLEANUP_FUNC (MMBaseModem, g_object_unref) guint mm_base_modem_get_dbus_id (MMBaseModem *self); gboolean mm_base_modem_grab_port (MMBaseModem *self, MMKernelDevice *kernel_device, MMPortType ptype, MMPortSerialAtFlag at_pflags, GError **error); gboolean mm_base_modem_grab_link_port (MMBaseModem *self, MMKernelDevice *kernel_device, GError **error); gboolean mm_base_modem_release_link_port (MMBaseModem *self, const gchar *subsystem, const gchar *name, GError **error); void mm_base_modem_wait_link_port (MMBaseModem *self, const gchar *subsystem, const gchar *name, guint timeout_ms, GAsyncReadyCallback callback, gpointer user_data); MMPort *mm_base_modem_wait_link_port_finish (MMBaseModem *self, GAsyncResult *res, GError **error); gboolean mm_base_modem_has_at_port (MMBaseModem *self); gboolean mm_base_modem_organize_ports (MMBaseModem *self, GError **error); MMPortSerialAt *mm_base_modem_peek_port_primary (MMBaseModem *self); MMPortSerialAt *mm_base_modem_peek_port_secondary (MMBaseModem *self); MMPortSerialQcdm *mm_base_modem_peek_port_qcdm (MMBaseModem *self); MMPortSerialAt *mm_base_modem_peek_port_gps_control (MMBaseModem *self); MMPortSerialGps *mm_base_modem_peek_port_gps (MMBaseModem *self); MMPortSerial *mm_base_modem_peek_port_audio (MMBaseModem *self); MMPortSerialAt *mm_base_modem_peek_best_at_port (MMBaseModem *self, GError **error); MMPort *mm_base_modem_peek_best_data_port (MMBaseModem *self, MMPortType type); GList *mm_base_modem_peek_data_ports (MMBaseModem *self); MMPortSerialAt *mm_base_modem_get_port_primary (MMBaseModem *self); MMPortSerialAt *mm_base_modem_get_port_secondary (MMBaseModem *self); MMPortSerialQcdm *mm_base_modem_get_port_qcdm (MMBaseModem *self); MMPortSerialAt *mm_base_modem_get_port_gps_control (MMBaseModem *self); MMPortSerialGps *mm_base_modem_get_port_gps (MMBaseModem *self); MMPortSerial *mm_base_modem_get_port_audio (MMBaseModem *self); MMPortSerialAt *mm_base_modem_get_best_at_port (MMBaseModem *self, GError **error); MMPort *mm_base_modem_get_best_data_port (MMBaseModem *self, MMPortType type); GList *mm_base_modem_get_data_ports (MMBaseModem *self); MMModemPortInfo *mm_base_modem_get_port_infos (MMBaseModem *self, guint *n_port_infos); GList *mm_base_modem_find_ports (MMBaseModem *self, MMPortSubsys subsys, MMPortType type); MMPort *mm_base_modem_peek_port (MMBaseModem *self, const gchar *name); MMPort *mm_base_modem_get_port (MMBaseModem *self, const gchar *name); void mm_base_modem_set_hotplugged (MMBaseModem *self, gboolean hotplugged); gboolean mm_base_modem_get_hotplugged (MMBaseModem *self); void mm_base_modem_set_valid (MMBaseModem *self, gboolean valid); gboolean mm_base_modem_get_valid (MMBaseModem *self); void mm_base_modem_set_reprobe (MMBaseModem *self, gboolean reprobe); gboolean mm_base_modem_get_reprobe (MMBaseModem *self); const gchar *mm_base_modem_get_device (MMBaseModem *self); const gchar *mm_base_modem_get_physdev (MMBaseModem *self); const gchar **mm_base_modem_get_drivers (MMBaseModem *self); const gchar *mm_base_modem_get_plugin (MMBaseModem *self); guint mm_base_modem_get_vendor_id (MMBaseModem *self); guint mm_base_modem_get_product_id (MMBaseModem *self); guint mm_base_modem_get_subsystem_vendor_id (MMBaseModem *self); GCancellable *mm_base_modem_peek_cancellable (MMBaseModem *self); void mm_base_modem_authorize (MMBaseModem *self, GDBusMethodInvocation *invocation, const gchar *authorization, GAsyncReadyCallback callback, gpointer user_data); gboolean mm_base_modem_authorize_finish (MMBaseModem *self, GAsyncResult *res, GError **error); void mm_base_modem_initialize (MMBaseModem *self, GAsyncReadyCallback callback, gpointer user_data); gboolean mm_base_modem_initialize_finish (MMBaseModem *self, GAsyncResult *res, GError **error); void mm_base_modem_enable (MMBaseModem *self, GAsyncReadyCallback callback, gpointer user_data); gboolean mm_base_modem_enable_finish (MMBaseModem *self, GAsyncResult *res, GError **error); void mm_base_modem_disable (MMBaseModem *self, GAsyncReadyCallback callback, gpointer user_data); gboolean mm_base_modem_disable_finish (MMBaseModem *self, GAsyncResult *res, GError **error); #if defined WITH_SUSPEND_RESUME void mm_base_modem_sync (MMBaseModem *self, GAsyncReadyCallback callback, gpointer user_data); gboolean mm_base_modem_sync_finish (MMBaseModem *self, GAsyncResult *res, GError **error); #endif #endif /* MM_BASE_MODEM_H */ ModemManager-1.23.4-dev/src/mm-base-sim.c000066400000000000000000003367451456466623000200110ustar00rootroot00000000000000/* -*- 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 - 2011 Red Hat, Inc. * Copyright (C) 2011 - 2022 Aleksander Morgado * Copyright (C) 2011 - 2022 Google, Inc. */ #include #include #include #include #include #include #include #define _LIBMM_INSIDE_MM #include #include "mm-iface-modem.h" #include "mm-base-sim.h" #include "mm-base-modem-at.h" #include "mm-base-modem.h" #include "mm-log-object.h" #include "mm-modem-helpers.h" #include "mm-error-helpers.h" static void async_initable_iface_init (GAsyncInitableIface *iface); static void log_object_iface_init (MMLogObjectInterface *iface); G_DEFINE_TYPE_EXTENDED (MMBaseSim, mm_base_sim, MM_GDBUS_TYPE_SIM_SKELETON, 0, G_IMPLEMENT_INTERFACE (G_TYPE_ASYNC_INITABLE, async_initable_iface_init) G_IMPLEMENT_INTERFACE (MM_TYPE_LOG_OBJECT, log_object_iface_init)) enum { PROP_0, PROP_PATH, PROP_CONNECTION, PROP_MODEM, PROP_SLOT_NUMBER, PROP_LAST }; enum { SIGNAL_PIN_LOCK_ENABLED, SIGNAL_LAST }; static GParamSpec *properties[PROP_LAST]; struct _MMBaseSimPrivate { /* The connection to the system bus */ GDBusConnection *connection; guint dbus_id; /* The modem which owns this SIM */ MMBaseModem *modem; /* The path where the SIM object is exported */ gchar *path; /* The SIM slot number, which will be 0 always if the system * doesn't support multiple SIMS. */ guint slot_number; }; static guint signals[SIGNAL_LAST] = { 0 }; /*****************************************************************************/ /* SIM type helpers */ #define IS_PSIM(self) \ (mm_gdbus_sim_get_sim_type (MM_GDBUS_SIM (self)) == MM_SIM_TYPE_PHYSICAL) #define IS_ESIM(self) \ (mm_gdbus_sim_get_sim_type (MM_GDBUS_SIM (self)) == MM_SIM_TYPE_ESIM) #define IS_ESIM_WITHOUT_PROFILES(self) \ (IS_ESIM (self) && (mm_gdbus_sim_get_esim_status (MM_GDBUS_SIM (self)) == MM_SIM_ESIM_STATUS_NO_PROFILES)) gboolean mm_base_sim_is_esim_without_profiles (MMBaseSim *self) { return IS_ESIM_WITHOUT_PROFILES (self); } /*****************************************************************************/ void mm_base_sim_export (MMBaseSim *self) { gchar *path; path = g_strdup_printf (MM_DBUS_SIM_PREFIX "/%d", self->priv->dbus_id); g_object_set (self, MM_BASE_SIM_PATH, path, NULL); g_free (path); } /*****************************************************************************/ /* Reprobe when a puk lock is discovered after pin1_retries are exhausted */ static void reprobe_if_puk_discovered (MMBaseSim *self, GError *error) { if (g_error_matches (error, MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_SIM_PUK)) { mm_obj_dbg (self, "Discovered PUK lock, discarding old modem..."); mm_iface_modem_process_sim_event (MM_IFACE_MODEM (self->priv->modem)); } } /*****************************************************************************/ /* CHANGE PIN (Generic implementation) */ static gboolean change_pin_finish (MMBaseSim *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void change_pin_ready (MMBaseModem *modem, GAsyncResult *res, GTask *task) { GError *error = NULL; mm_base_modem_at_command_finish (modem, res, &error); if (error) g_task_return_error (task, error); else g_task_return_boolean (task, TRUE); g_object_unref (task); } static void change_pin (MMBaseSim *self, const gchar *old_pin, const gchar *new_pin, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; gchar *command; task = g_task_new (self, NULL, callback, user_data); command = g_strdup_printf ("+CPWD=\"SC\",\"%s\",\"%s\"", old_pin, new_pin); mm_base_modem_at_command (self->priv->modem, command, 3, FALSE, (GAsyncReadyCallback)change_pin_ready, task); g_free (command); } /*****************************************************************************/ /* CHANGE PIN (DBus call handling) */ typedef struct { MMBaseSim *self; GDBusMethodInvocation *invocation; gchar *old_pin; gchar *new_pin; GError *save_error; } HandleChangePinContext; static void handle_change_pin_context_free (HandleChangePinContext *ctx) { g_assert (ctx->save_error == NULL); g_object_unref (ctx->invocation); g_object_unref (ctx->self); g_free (ctx->old_pin); g_free (ctx->new_pin); g_free (ctx); } static void after_change_update_lock_info_ready (MMIfaceModem *modem, GAsyncResult *res, HandleChangePinContext *ctx) { /* We just want to ensure that we tried to update the unlock * retries, no big issue if it failed */ mm_iface_modem_update_lock_info_finish (modem, res, NULL); if (ctx->save_error) { mm_dbus_method_invocation_return_gerror (ctx->invocation, ctx->save_error); reprobe_if_puk_discovered (ctx->self, ctx->save_error); g_clear_error (&ctx->save_error); } else { mm_gdbus_sim_complete_change_pin (MM_GDBUS_SIM (ctx->self), ctx->invocation); } handle_change_pin_context_free (ctx); } static void handle_change_pin_ready (MMBaseSim *self, GAsyncResult *res, HandleChangePinContext *ctx) { MMModemLock known_lock = MM_MODEM_LOCK_UNKNOWN; if (!MM_BASE_SIM_GET_CLASS (self)->change_pin_finish (self, res, &ctx->save_error)) { if (g_error_matches (ctx->save_error, MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_SIM_PUK)) known_lock = MM_MODEM_LOCK_SIM_PUK; } mm_iface_modem_update_lock_info ( MM_IFACE_MODEM (self->priv->modem), known_lock, (GAsyncReadyCallback)after_change_update_lock_info_ready, ctx); } static void handle_change_pin_auth_ready (MMBaseModem *modem, GAsyncResult *res, HandleChangePinContext *ctx) { GError *error = NULL; if (!mm_base_modem_authorize_finish (modem, res, &error)) { mm_dbus_method_invocation_take_error (ctx->invocation, error); handle_change_pin_context_free (ctx); return; } /* If changing PIN is not implemented, report an error */ if (!MM_BASE_SIM_GET_CLASS (ctx->self)->change_pin || !MM_BASE_SIM_GET_CLASS (ctx->self)->change_pin_finish) { mm_dbus_method_invocation_return_error_literal (ctx->invocation, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "Cannot change PIN: operation not supported"); handle_change_pin_context_free (ctx); return; } if (!mm_gdbus_sim_get_active (MM_GDBUS_SIM (ctx->self))) { mm_dbus_method_invocation_return_error_literal (ctx->invocation, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "Cannot change PIN: SIM not currently active"); handle_change_pin_context_free (ctx); return; } if (IS_ESIM_WITHOUT_PROFILES (ctx->self)) { mm_dbus_method_invocation_return_error_literal (ctx->invocation, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "Cannot change PIN: eSIM without profiles"); handle_change_pin_context_free (ctx); return; } MM_BASE_SIM_GET_CLASS (ctx->self)->change_pin (ctx->self, ctx->old_pin, ctx->new_pin, (GAsyncReadyCallback)handle_change_pin_ready, ctx); } static gboolean handle_change_pin (MMBaseSim *self, GDBusMethodInvocation *invocation, const gchar *old_pin, const gchar *new_pin, gboolean changed) { HandleChangePinContext *ctx; ctx = g_new0 (HandleChangePinContext, 1); ctx->self = g_object_ref (self); ctx->invocation = g_object_ref (invocation); ctx->old_pin = g_strdup (old_pin); ctx->new_pin = g_strdup (new_pin); mm_base_modem_authorize (self->priv->modem, invocation, MM_AUTHORIZATION_DEVICE_CONTROL, (GAsyncReadyCallback)handle_change_pin_auth_ready, ctx); return TRUE; } /*****************************************************************************/ /* ENABLE PIN (Generic implementation) */ static gboolean enable_pin_finish (MMBaseSim *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void enable_pin_ready (MMBaseModem *modem, GAsyncResult *res, GTask *task) { GError *error = NULL; mm_base_modem_at_command_finish (modem, res, &error); if (error) g_task_return_error (task, error); else g_task_return_boolean (task, TRUE); g_object_unref (task); } static void enable_pin (MMBaseSim *self, const gchar *pin, gboolean enabled, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; gchar *command; task = g_task_new (self, NULL, callback, user_data); command = g_strdup_printf ("+CLCK=\"SC\",%d,\"%s\"", enabled ? 1 : 0, pin); mm_base_modem_at_command (self->priv->modem, command, 3, FALSE, (GAsyncReadyCallback)enable_pin_ready, task); g_free (command); } /*****************************************************************************/ /* ENABLE PIN (DBus call handling) */ typedef struct { MMBaseSim *self; GDBusMethodInvocation *invocation; gchar *pin; gboolean enabled; GError *save_error; } HandleEnablePinContext; static void handle_enable_pin_context_free (HandleEnablePinContext *ctx) { g_assert (ctx->save_error == NULL); g_object_unref (ctx->invocation); g_object_unref (ctx->self); g_free (ctx->pin); g_free (ctx); } static void after_enable_update_lock_info_ready (MMIfaceModem *modem, GAsyncResult *res, HandleEnablePinContext *ctx) { /* We just want to ensure that we tried to update the unlock * retries, no big issue if it failed */ mm_iface_modem_update_lock_info_finish (modem, res, NULL); if (ctx->save_error) { mm_dbus_method_invocation_return_gerror (ctx->invocation, ctx->save_error); reprobe_if_puk_discovered (ctx->self, ctx->save_error); g_clear_error (&ctx->save_error); } else { /* Signal about the new lock state */ g_signal_emit (ctx->self, signals[SIGNAL_PIN_LOCK_ENABLED], 0, ctx->enabled); mm_gdbus_sim_complete_enable_pin (MM_GDBUS_SIM (ctx->self), ctx->invocation); } handle_enable_pin_context_free (ctx); } static void handle_enable_pin_ready (MMBaseSim *self, GAsyncResult *res, HandleEnablePinContext *ctx) { MMModemLock known_lock = MM_MODEM_LOCK_UNKNOWN; if (!MM_BASE_SIM_GET_CLASS (self)->enable_pin_finish (self, res, &ctx->save_error)) { if (g_error_matches (ctx->save_error, MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_SIM_PUK)) known_lock = MM_MODEM_LOCK_SIM_PUK; } mm_iface_modem_update_lock_info ( MM_IFACE_MODEM (self->priv->modem), known_lock, (GAsyncReadyCallback)after_enable_update_lock_info_ready, ctx); } static void handle_enable_pin_auth_ready (MMBaseModem *modem, GAsyncResult *res, HandleEnablePinContext *ctx) { GError *error = NULL; if (!mm_base_modem_authorize_finish (modem, res, &error)) { mm_dbus_method_invocation_take_error (ctx->invocation, error); handle_enable_pin_context_free (ctx); return; } /* If changing PIN is not implemented, report an error */ if (!MM_BASE_SIM_GET_CLASS (ctx->self)->enable_pin || !MM_BASE_SIM_GET_CLASS (ctx->self)->enable_pin_finish) { mm_dbus_method_invocation_return_error_literal (ctx->invocation, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "Cannot enable/disable PIN: operation not supported"); handle_enable_pin_context_free (ctx); return; } if (!mm_gdbus_sim_get_active (MM_GDBUS_SIM (ctx->self))) { mm_dbus_method_invocation_return_error_literal (ctx->invocation, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "Cannot enable/disable PIN: SIM not currently active"); handle_enable_pin_context_free (ctx); return; } if (IS_ESIM_WITHOUT_PROFILES (ctx->self)) { mm_dbus_method_invocation_return_error (ctx->invocation, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "Cannot enable/disable PIN: eSIM without profiles"); handle_enable_pin_context_free (ctx); return; } MM_BASE_SIM_GET_CLASS (ctx->self)->enable_pin (ctx->self, ctx->pin, ctx->enabled, (GAsyncReadyCallback)handle_enable_pin_ready, ctx); } static gboolean handle_enable_pin (MMBaseSim *self, GDBusMethodInvocation *invocation, const gchar *pin, gboolean enabled) { HandleEnablePinContext *ctx; ctx = g_new0 (HandleEnablePinContext, 1); ctx->self = g_object_ref (self); ctx->invocation = g_object_ref (invocation); ctx->pin = g_strdup (pin); ctx->enabled = enabled; mm_base_modem_authorize (self->priv->modem, invocation, MM_AUTHORIZATION_DEVICE_CONTROL, (GAsyncReadyCallback)handle_enable_pin_auth_ready, ctx); return TRUE; } /*****************************************************************************/ /* SEND PIN/PUK (Generic implementation) */ static gboolean common_send_pin_puk_finish (MMBaseSim *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void send_pin_puk_ready (MMBaseModem *modem, GAsyncResult *res, GTask *task) { GError *error = NULL; mm_base_modem_at_command_finish (modem, res, &error); if (error) g_task_return_error (task, error); else g_task_return_boolean (task, TRUE); g_object_unref (task); } static void common_send_pin_puk (MMBaseSim *self, const gchar *pin, const gchar *puk, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; gchar *command; task = g_task_new (self, NULL, callback, user_data); command = (puk ? g_strdup_printf ("+CPIN=\"%s\",\"%s\"", puk, pin) : g_strdup_printf ("+CPIN=\"%s\"", pin)); mm_base_modem_at_command (self->priv->modem, command, 3, FALSE, (GAsyncReadyCallback)send_pin_puk_ready, task); g_free (command); } static void send_puk (MMBaseSim *self, const gchar *puk, const gchar *new_pin, GAsyncReadyCallback callback, gpointer user_data) { common_send_pin_puk (self, new_pin, puk, callback, user_data); } static void send_pin (MMBaseSim *self, const gchar *pin, GAsyncReadyCallback callback, gpointer user_data) { common_send_pin_puk (self, pin, NULL, callback, user_data); } /*****************************************************************************/ /* SEND PIN/PUK (common logic) */ static GError * error_for_unlock_check (MMModemLock lock) { static const MMMobileEquipmentError errors_for_locks [] = { MM_MOBILE_EQUIPMENT_ERROR_UNKNOWN, /* MM_MODEM_LOCK_UNKNOWN */ MM_MOBILE_EQUIPMENT_ERROR_UNKNOWN, /* MM_MODEM_LOCK_NONE */ MM_MOBILE_EQUIPMENT_ERROR_SIM_PIN, /* MM_MODEM_LOCK_SIM_PIN */ MM_MOBILE_EQUIPMENT_ERROR_SIM_PIN2, /* MM_MODEM_LOCK_SIM_PIN2 */ MM_MOBILE_EQUIPMENT_ERROR_SIM_PUK, /* MM_MODEM_LOCK_SIM_PUK */ MM_MOBILE_EQUIPMENT_ERROR_SIM_PUK2, /* MM_MODEM_LOCK_SIM_PUK2 */ MM_MOBILE_EQUIPMENT_ERROR_SERVICE_PIN, /* MM_MODEM_LOCK_PH_SP_PIN */ MM_MOBILE_EQUIPMENT_ERROR_SERVICE_PUK, /* MM_MODEM_LOCK_PH_SP_PUK */ MM_MOBILE_EQUIPMENT_ERROR_NETWORK_PIN, /* MM_MODEM_LOCK_PH_NET_PIN */ MM_MOBILE_EQUIPMENT_ERROR_NETWORK_PUK, /* MM_MODEM_LOCK_PH_NET_PUK */ MM_MOBILE_EQUIPMENT_ERROR_PH_SIM_PIN, /* MM_MODEM_LOCK_PH_SIM_PIN */ MM_MOBILE_EQUIPMENT_ERROR_CORP_PIN, /* MM_MODEM_LOCK_PH_CORP_PIN */ MM_MOBILE_EQUIPMENT_ERROR_CORP_PUK, /* MM_MODEM_LOCK_PH_CORP_PUK */ MM_MOBILE_EQUIPMENT_ERROR_PH_FSIM_PIN, /* MM_MODEM_LOCK_PH_FSIM_PIN */ MM_MOBILE_EQUIPMENT_ERROR_PH_FSIM_PUK, /* MM_MODEM_LOCK_PH_FSIM_PUK */ MM_MOBILE_EQUIPMENT_ERROR_NETWORK_SUBSET_PIN, /* MM_MODEM_LOCK_PH_NETSUB_PIN */ MM_MOBILE_EQUIPMENT_ERROR_NETWORK_SUBSET_PUK, /* MM_MODEM_LOCK_PH_NETSUB_PUK */ }; g_assert (lock >= MM_MODEM_LOCK_UNKNOWN); g_assert (lock <= MM_MODEM_LOCK_PH_NETSUB_PUK); return g_error_new (MM_MOBILE_EQUIPMENT_ERROR, errors_for_locks[lock], "Device is locked: '%s'", mm_modem_lock_get_string (lock)); } gboolean mm_base_sim_send_pin_finish (MMBaseSim *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } gboolean mm_base_sim_send_puk_finish (MMBaseSim *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void update_lock_info_ready (MMIfaceModem *modem, GAsyncResult *res, GTask *task) { GError *error = NULL; MMModemLock lock; lock = mm_iface_modem_update_lock_info_finish (modem, res, &error); /* Consider it only an error if SIM-PIN/PUK is locked or lock is unknown */ if (lock == MM_MODEM_LOCK_UNKNOWN || lock == MM_MODEM_LOCK_SIM_PIN || lock == MM_MODEM_LOCK_SIM_PUK) { const GError *saved_error; /* Device is locked. Now: * - If we got an error during update_lock_info, report it. The sim might have been blocked. * - If we got an error in the original send-pin action, report it. * - Otherwise, build our own error from the lock code. */ if (!error) { saved_error = g_task_get_task_data (task); if (saved_error) error = g_error_copy (saved_error); else error = error_for_unlock_check (lock); } g_task_return_error (task, error); } else g_task_return_boolean (task, TRUE); g_object_unref (task); } static void send_pin_ready (MMBaseSim *self, GAsyncResult *res, GTask *task) { GError *error = NULL; MMModemLock known_lock = MM_MODEM_LOCK_UNKNOWN; if (!MM_BASE_SIM_GET_CLASS (self)->send_pin_finish (self, res, &error)) { g_task_set_task_data (task, error, (GDestroyNotify)g_error_free); if (g_error_matches (error, MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_SIM_PUK)) known_lock = MM_MODEM_LOCK_SIM_PUK; } /* Once pin/puk has been sent, recheck lock */ mm_iface_modem_update_lock_info ( MM_IFACE_MODEM (self->priv->modem), known_lock, (GAsyncReadyCallback)update_lock_info_ready, task); } static void send_puk_ready (MMBaseSim *self, GAsyncResult *res, GTask *task) { GError *error = NULL; if (!MM_BASE_SIM_GET_CLASS (self)->send_puk_finish (self, res, &error)) g_task_set_task_data (task, error, (GDestroyNotify)g_error_free); /* Once pin/puk has been sent, recheck lock */ mm_iface_modem_update_lock_info (MM_IFACE_MODEM (self->priv->modem), MM_MODEM_LOCK_UNKNOWN, /* ask */ (GAsyncReadyCallback)update_lock_info_ready, task); } void mm_base_sim_send_pin (MMBaseSim *self, const gchar *pin, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; task = g_task_new (self, NULL, callback, user_data); /* If sending PIN is not implemented, report an error */ if (!MM_BASE_SIM_GET_CLASS (self)->send_pin || !MM_BASE_SIM_GET_CLASS (self)->send_pin_finish) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "Cannot send PIN: operation not supported"); g_object_unref (task); return; } /* Only allow sending SIM-PIN if really SIM-PIN locked */ if (mm_iface_modem_get_unlock_required (MM_IFACE_MODEM (self->priv->modem)) != MM_MODEM_LOCK_SIM_PIN) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_WRONG_STATE, "Cannot send PIN: device is not SIM-PIN locked"); g_object_unref (task); return; } MM_BASE_SIM_GET_CLASS (self)->send_pin (self, pin, (GAsyncReadyCallback)send_pin_ready, task); } void mm_base_sim_send_puk (MMBaseSim *self, const gchar *puk, const gchar *new_pin, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; task = g_task_new (self, NULL, callback, user_data); /* If sending PIN is not implemented, report an error */ if (!MM_BASE_SIM_GET_CLASS (self)->send_puk || !MM_BASE_SIM_GET_CLASS (self)->send_puk_finish) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "Cannot send PUK: operation not supported"); g_object_unref (task); return; } /* Only allow sending SIM-PUK if really SIM-PUK locked */ if (mm_iface_modem_get_unlock_required (MM_IFACE_MODEM (self->priv->modem)) != MM_MODEM_LOCK_SIM_PUK) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_WRONG_STATE, "Cannot send PUK: device is not SIM-PUK locked"); g_object_unref (task); return; } MM_BASE_SIM_GET_CLASS (self)->send_puk (self, puk, new_pin, (GAsyncReadyCallback)send_puk_ready, task); } /*****************************************************************************/ /* LOAD SIM IDENTIFIER */ gchar * mm_base_sim_load_sim_identifier_finish (MMBaseSim *self, GAsyncResult *res, GError **error) { return g_task_propagate_pointer (G_TASK (res), error); } static void load_sim_identifier_ready (MMBaseSim *self, GAsyncResult *res, GTask *task) { gchar *simid; GError *error = NULL; simid = MM_BASE_SIM_GET_CLASS (self)->load_sim_identifier_finish (self, res, &error); if (!simid) g_task_return_error (task, error); else g_task_return_pointer (task, simid, g_free); g_object_unref (task); } void mm_base_sim_load_sim_identifier (MMBaseSim *self, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; task = g_task_new (self, NULL, callback, user_data); if (!MM_BASE_SIM_GET_CLASS (self)->load_sim_identifier || !MM_BASE_SIM_GET_CLASS (self)->load_sim_identifier_finish) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "not implemented"); g_object_unref (task); return; } if (IS_ESIM_WITHOUT_PROFILES (self)) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "no SIM identifier in eSIM without profiles"); g_object_unref (task); return; } MM_BASE_SIM_GET_CLASS (self)->load_sim_identifier ( self, (GAsyncReadyCallback)load_sim_identifier_ready, task); } /*****************************************************************************/ /* SEND PIN (DBus call handling) */ typedef struct { MMBaseSim *self; GDBusMethodInvocation *invocation; gchar *pin; } HandleSendPinContext; static void handle_send_pin_context_free (HandleSendPinContext *ctx) { g_object_unref (ctx->invocation); g_object_unref (ctx->self); g_free (ctx->pin); g_free (ctx); } static void handle_send_pin_ready (MMBaseSim *self, GAsyncResult *res, HandleSendPinContext *ctx) { GError *error = NULL; if (!mm_base_sim_send_pin_finish (self, res, &error)) { mm_dbus_method_invocation_return_gerror (ctx->invocation, error); reprobe_if_puk_discovered (self, error); g_clear_error (&error); } else mm_gdbus_sim_complete_send_pin (MM_GDBUS_SIM (self), ctx->invocation); handle_send_pin_context_free (ctx); } static void handle_send_pin_auth_ready (MMBaseModem *modem, GAsyncResult *res, HandleSendPinContext *ctx) { GError *error = NULL; if (!mm_base_modem_authorize_finish (modem, res, &error)) { mm_dbus_method_invocation_take_error (ctx->invocation, error); handle_send_pin_context_free (ctx); return; } if (!mm_gdbus_sim_get_active (MM_GDBUS_SIM (ctx->self))) { mm_dbus_method_invocation_return_error_literal (ctx->invocation, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "Cannot send PIN: SIM not currently active"); handle_send_pin_context_free (ctx); return; } if (IS_ESIM_WITHOUT_PROFILES (ctx->self)) { mm_dbus_method_invocation_return_error_literal (ctx->invocation, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "Cannot send PIN: eSIM without profiles"); handle_send_pin_context_free (ctx); return; } mm_base_sim_send_pin (ctx->self, ctx->pin, (GAsyncReadyCallback)handle_send_pin_ready, ctx); } static gboolean handle_send_pin (MMBaseSim *self, GDBusMethodInvocation *invocation, const gchar *pin) { HandleSendPinContext *ctx; ctx = g_new0 (HandleSendPinContext, 1); ctx->self = g_object_ref (self); ctx->invocation = g_object_ref (invocation); ctx->pin = g_strdup (pin); mm_base_modem_authorize (self->priv->modem, invocation, MM_AUTHORIZATION_DEVICE_CONTROL, (GAsyncReadyCallback)handle_send_pin_auth_ready, ctx); return TRUE; } /*****************************************************************************/ /* SEND PUK (DBus call handling) */ typedef struct { MMBaseSim *self; GDBusMethodInvocation *invocation; gchar *puk; gchar *new_pin; } HandleSendPukContext; static void handle_send_puk_context_free (HandleSendPukContext *ctx) { g_object_unref (ctx->invocation); g_object_unref (ctx->self); g_free (ctx->puk); g_free (ctx->new_pin); g_free (ctx); } static void handle_send_puk_ready (MMBaseSim *self, GAsyncResult *res, HandleSendPukContext *ctx) { GError *error = NULL; gboolean sim_error = FALSE; if (!mm_base_sim_send_puk_finish (self, res, &error)) { sim_error = g_error_matches (error, MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_SIM_NOT_INSERTED) || g_error_matches (error, MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_SIM_FAILURE) || g_error_matches (error, MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_SIM_WRONG); mm_dbus_method_invocation_take_error (ctx->invocation, error); } else mm_gdbus_sim_complete_send_puk (MM_GDBUS_SIM (self), ctx->invocation); if (sim_error) { mm_obj_msg (self, "received critical sim error: SIM might be permanently blocked, reprobing..."); mm_iface_modem_process_sim_event (MM_IFACE_MODEM (self->priv->modem)); } handle_send_puk_context_free (ctx); } static void handle_send_puk_auth_ready (MMBaseModem *modem, GAsyncResult *res, HandleSendPukContext *ctx) { GError *error = NULL; if (!mm_base_modem_authorize_finish (modem, res, &error)) { mm_dbus_method_invocation_take_error (ctx->invocation, error); handle_send_puk_context_free (ctx); return; } if (!mm_gdbus_sim_get_active (MM_GDBUS_SIM (ctx->self))) { mm_dbus_method_invocation_return_error_literal (ctx->invocation, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "Cannot send PUK: SIM not currently active"); handle_send_puk_context_free (ctx); return; } if (IS_ESIM_WITHOUT_PROFILES (ctx->self)) { mm_dbus_method_invocation_return_error_literal (ctx->invocation, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "Cannot send PUK: eSIM without profiles"); handle_send_puk_context_free (ctx); return; } mm_base_sim_send_puk (ctx->self, ctx->puk, ctx->new_pin, (GAsyncReadyCallback)handle_send_puk_ready, ctx); } static gboolean handle_send_puk (MMBaseSim *self, GDBusMethodInvocation *invocation, const gchar *puk, const gchar *new_pin) { HandleSendPukContext *ctx; ctx = g_new0 (HandleSendPukContext, 1); ctx->self = g_object_ref (self); ctx->invocation = g_object_ref (invocation); ctx->puk = g_strdup (puk); ctx->new_pin = g_strdup (new_pin); mm_base_modem_authorize (self->priv->modem, invocation, MM_AUTHORIZATION_DEVICE_CONTROL, (GAsyncReadyCallback)handle_send_puk_auth_ready, ctx); return TRUE; } /*****************************************************************************/ /* Check if preferred networks is supported. * * Modems like the Intel-based EM7345 fail very badly when CPOL? is run, even * completely blocking the AT port after that. We need to avoid running any * CPOL related command in these modules. */ static gboolean check_preferred_networks_disabled (MMBaseSim *self) { MMPort *primary; primary = MM_PORT (mm_base_modem_peek_port_primary (self->priv->modem)); return (primary ? mm_kernel_device_get_global_property_as_boolean (mm_port_peek_kernel_device (primary), "ID_MM_PREFERRED_NETWORKS_CPOL_DISABLED") : FALSE); } /*****************************************************************************/ /* SET PREFERRED NETWORKS (Generic implementation) */ /* Setting preferred network list with AT+CPOL is a complicated procedure with * the following steps: * 1. Using AT+CPOL=? to get SIM capacity; the capacity is checked to ensure * that the list is not too large for the SIM card. * 2. Reading existing preferred networks from SIM with AT+CPOL?. * 3. Clearing existing networks with a series of AT+CPOL= commands. * 4. Setting the new list by invoking AT+CPOL for each network. * * There are some complications with AT+CPOL handling which makes the work more * difficult for us. It seems that modems can only handle a certain exact number * of access technology identifiers - and this cannot be certainly known in * advance. * * If AT+CPOL? in step 2 returns anything, we can deduce the number of supported * identifiers there. But if there were no networks configured earlier, we must * start with a default based on modem capacity and work our way down from there * if the AT+CPOL command fails. */ static void set_preferred_networks_set_next (MMBaseSim *self, GTask *task); static void set_preferred_networks_clear_next (MMBaseSim *self, GTask *task); typedef struct { GList *set_list; /* AT+CPOL indices that must be cleared before setting the networks. */ GArray *clear_index; /* Number of access technology identifiers we will set. */ guint act_count; /* If TRUE, we know that act_count is something the modem can handle */ gboolean act_count_verified; /* Index of preferred network currently being set (0 = first) */ guint current_write_index; /* Operation error code */ GError *error; } SetPreferredNetworksContext; static void set_preferred_network_context_free (SetPreferredNetworksContext *ctx) { g_list_free_full (ctx->set_list, (GDestroyNotify) mm_sim_preferred_network_free); g_clear_error (&ctx->error); g_array_free (ctx->clear_index, TRUE); g_slice_free (SetPreferredNetworksContext, ctx); } static gboolean set_preferred_networks_finish (MMBaseSim *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void parse_old_preferred_networks (const gchar *response, SetPreferredNetworksContext *ctx) { gchar **entries; gchar **iter; entries = g_strsplit_set (response, "\r\n", -1); for (iter = entries; iter && *iter; iter++) { guint index; guint act_count = 0; g_strstrip (*iter); if (strlen (*iter) == 0) continue; if (mm_sim_parse_cpol_query_response (*iter, &index, NULL, NULL, NULL, NULL, NULL, NULL, &act_count, NULL) && index > 0) { /* Remember how many access technologies the modem/SIM can take */ if (!ctx->act_count_verified || act_count > ctx->act_count) { ctx->act_count = act_count; ctx->act_count_verified = TRUE; } /* Store the index to be cleared */ g_array_append_val (ctx->clear_index, index); } } g_strfreev (entries); } /* This function is called only in error case, after reloading the network list from SIM. */ static void set_preferred_networks_reload_ready (MMBaseSim *self, GAsyncResult *res, GTask *task) { g_autoptr(GError) error = NULL; GList *preferred_nets_list; SetPreferredNetworksContext *ctx; ctx = g_task_get_task_data (task); preferred_nets_list = MM_BASE_SIM_GET_CLASS (self)->load_preferred_networks_finish (self, res, &error); if (error) mm_obj_dbg (self, "couldn't load list of preferred networks: %s", error->message); mm_gdbus_sim_set_preferred_networks (MM_GDBUS_SIM (self), mm_sim_preferred_network_list_get_variant (preferred_nets_list)); g_list_free_full (preferred_nets_list, (GDestroyNotify) mm_sim_preferred_network_free); /* Return the original error stored in our context */ g_task_return_error (task, g_steal_pointer (&ctx->error)); g_object_unref (task); } static gboolean set_preferred_networks_retry_command (MMBaseSim *self, SetPreferredNetworksContext *ctx) { /* If we haven't yet determined the number of access technology parameters supported by * the modem, try reducing the count if possible and retry with the reduced count. */ if (!ctx->act_count_verified && ctx->act_count > 0) { ctx->act_count--; mm_obj_dbg (self, "retrying operation with %u access technologies", ctx->act_count); return TRUE; } return FALSE; } static void set_preferred_network_reload_and_return_error (MMBaseSim *self, GTask *task, GError *error) { SetPreferredNetworksContext *ctx; ctx = g_task_get_task_data (task); /* Reload the complete list from SIM card to ensure that the PreferredNetworks * property matches with whatever is actually on the SIM. */ if (MM_BASE_SIM_GET_CLASS (self)->load_preferred_networks && MM_BASE_SIM_GET_CLASS (self)->load_preferred_networks_finish) { ctx->error = error; MM_BASE_SIM_GET_CLASS (self)->load_preferred_networks ( self, (GAsyncReadyCallback)set_preferred_networks_reload_ready, task); } else { g_task_return_error (task, error); g_object_unref (task); } } static void set_preferred_networks_set_ready (MMBaseModem *modem, GAsyncResult *res, GTask *task) { MMBaseSim *self; SetPreferredNetworksContext *ctx; GError *error = NULL; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); mm_base_modem_at_command_finish (modem, res, &error); /* The command may fail; check if we can retry */ if (error) { if (!set_preferred_networks_retry_command (self, ctx)) { /* Retrying not possible, failing... */ mm_obj_warn (self, "failed to set preferred networks: '%s'", error->message); set_preferred_network_reload_and_return_error (self, task, error); return; } /* Retrying possible */ g_clear_error (&error); } else { /* Last set operation was successful, so we know for sure how many access technologies * the modem can take. */ ctx->act_count_verified = TRUE; ctx->current_write_index++; } set_preferred_networks_set_next (self, task); } static gboolean set_preferred_networks_check_support (MMBaseSim *self, SetPreferredNetworksContext *ctx, MMSimPreferredNetwork *network, GError **error) { MMModemAccessTechnology requested_act; MMModemAccessTechnology supported_act = MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN; requested_act = mm_sim_preferred_network_get_access_technology (network); if (ctx->act_count >= 1) supported_act |= MM_MODEM_ACCESS_TECHNOLOGY_GSM; if (ctx->act_count >= 2) supported_act |= MM_MODEM_ACCESS_TECHNOLOGY_GSM_COMPACT; if (ctx->act_count >= 3) supported_act |= MM_MODEM_ACCESS_TECHNOLOGY_UMTS; if (ctx->act_count >= 4) supported_act |= MM_MODEM_ACCESS_TECHNOLOGY_LTE; if (ctx->act_count >= 5) supported_act |= MM_MODEM_ACCESS_TECHNOLOGY_5GNR; if (requested_act & ~supported_act) { g_autofree gchar *act_string = NULL; act_string = mm_modem_access_technology_build_string_from_mask (requested_act & ~supported_act); mm_obj_warn (self, "cannot set preferred net '%s'; access technology '%s' not supported by modem/SIM", mm_sim_preferred_network_get_operator_code (network), act_string); g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "Access technology unsupported by modem or SIM"); return FALSE; } return TRUE; } /* Set next preferred network in queue */ static void set_preferred_networks_set_next (MMBaseSim *self, GTask *task) { SetPreferredNetworksContext *ctx; g_autofree gchar *command = NULL; MMSimPreferredNetwork *current_network; const gchar *operator_code; MMModemAccessTechnology act; GError *error = NULL; ctx = g_task_get_task_data (task); current_network = (MMSimPreferredNetwork *) g_list_nth_data (ctx->set_list, ctx->current_write_index); if (current_network == NULL) { /* No more networks to set; we are done. */ mm_obj_dbg (self, "setting preferred networks completed."); g_task_return_boolean (task, TRUE); g_object_unref (task); return; } if (!set_preferred_networks_check_support (self, ctx, current_network, &error)) { set_preferred_network_reload_and_return_error (self, task, error); return; } operator_code = mm_sim_preferred_network_get_operator_code (current_network); act = mm_sim_preferred_network_get_access_technology (current_network); /* Assemble the command to set the network */ command = g_strdup_printf ("+CPOL=%u,2,\"%s\"%s%s%s%s%s", ctx->current_write_index + 1, operator_code, ctx->act_count == 0 ? "" : ((act & MM_MODEM_ACCESS_TECHNOLOGY_GSM) ? ",1" : ",0"), ctx->act_count <= 1 ? "" : ((act & MM_MODEM_ACCESS_TECHNOLOGY_GSM_COMPACT) ? ",1" : ",0"), ctx->act_count <= 2 ? "" : ((act & MM_MODEM_ACCESS_TECHNOLOGY_UMTS) ? ",1" : ",0"), ctx->act_count <= 3 ? "" : ((act & MM_MODEM_ACCESS_TECHNOLOGY_LTE) ? ",1" : ",0"), ctx->act_count <= 4 ? "" : ((act & MM_MODEM_ACCESS_TECHNOLOGY_5GNR) ? ",1" : ",0")); mm_base_modem_at_command ( self->priv->modem, command, 20, FALSE, (GAsyncReadyCallback)set_preferred_networks_set_ready, task); } static void set_preferred_networks_clear_ready (MMBaseModem *modem, GAsyncResult *res, GTask *task) { MMBaseSim *self; GError *error = NULL; self = g_task_get_source_object (task); mm_base_modem_at_command_finish (modem, res, &error); if (error) { mm_obj_warn (self, "couldn't clear preferred network entry: '%s'", error->message); set_preferred_network_reload_and_return_error (self, task, error); return; } set_preferred_networks_clear_next (self, task); } /* Clear one of the networks in the clear list */ static void set_preferred_networks_clear_next (MMBaseSim *self, GTask *task) { SetPreferredNetworksContext *ctx; g_autofree gchar *command = NULL; guint current_clear_index; ctx = g_task_get_task_data (task); /* Clear from last index to first, since some modems (e.g. u-blox) may shift up items * following the cleared ones. */ if (ctx->clear_index->len > 0) { current_clear_index = g_array_index (ctx->clear_index, guint, ctx->clear_index->len - 1); g_array_remove_index (ctx->clear_index, ctx->clear_index->len - 1); command = g_strdup_printf ("+CPOL=%u", current_clear_index); mm_base_modem_at_command ( self->priv->modem, command, 20, FALSE, (GAsyncReadyCallback)set_preferred_networks_clear_ready, task); return; } /* All clear operations done; start setting new networks */ set_preferred_networks_set_next (self, task); } static void set_preferred_networks_load_existing_ready (MMBaseModem *modem, GAsyncResult *res, GTask *task) { MMBaseSim *self; GError *error = NULL; SetPreferredNetworksContext *ctx; const gchar *response; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); response = mm_base_modem_at_command_finish (modem, res, &error); if (error) { mm_obj_warn (self, "couldn't load existing preferred network list: '%s'", error->message); g_task_return_error (task, error); g_object_unref (task); return; } parse_old_preferred_networks (response, ctx); set_preferred_networks_clear_next (self, task); } static void set_preferred_networks_query_sim_capacity_ready (MMBaseModem *modem, GAsyncResult *res, GTask *task) { MMBaseSim *self; GError *error = NULL; SetPreferredNetworksContext *ctx; const gchar *response; guint max_index; guint networks_count; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); response = mm_base_modem_at_command_finish (modem, res, &error); if (error) { mm_obj_warn (self, "couldn't query preferred network list capacity: '%s'", error->message); g_task_return_error (task, error); g_object_unref (task); return; } if (!mm_sim_parse_cpol_test_response (response, NULL, &max_index, &error)) { g_task_return_error (task, error); g_object_unref (task); return; } /* Compare the number of networks to maximum index returned by AT+CPOL=? */ networks_count = g_list_length (ctx->set_list); if (networks_count > max_index) { mm_obj_warn (self, "can't set %u preferred networks; SIM capacity: %u", networks_count, max_index); g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_TOO_MANY, "Too many networks; SIM capacity %u", max_index); g_object_unref (task); return; } mm_obj_dbg (self, "setting %u preferred networks, SIM capacity: %u", networks_count, max_index); mm_base_modem_at_command ( self->priv->modem, "+CPOL?", 20, FALSE, (GAsyncReadyCallback)set_preferred_networks_load_existing_ready, task); } static void set_preferred_networks (MMBaseSim *self, GList *preferred_network_list, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; SetPreferredNetworksContext *ctx; task = g_task_new (self, NULL, callback, user_data); if (check_preferred_networks_disabled (self)) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "setting preferred networks is unsupported"); g_object_unref (task); return; } mm_obj_dbg (self, "set preferred networks: loading existing networks..."); ctx = g_slice_new0 (SetPreferredNetworksContext); ctx->set_list = mm_sim_preferred_network_list_copy (preferred_network_list); ctx->clear_index = g_array_new (FALSE, TRUE, sizeof (guint)); if (mm_iface_modem_is_5g (MM_IFACE_MODEM (self->priv->modem))) ctx->act_count = 5; else if (mm_iface_modem_is_4g (MM_IFACE_MODEM (self->priv->modem))) ctx->act_count = 4; else if (mm_iface_modem_is_3g (MM_IFACE_MODEM (self->priv->modem))) ctx->act_count = 3; else if (mm_iface_modem_is_2g (MM_IFACE_MODEM (self->priv->modem))) ctx->act_count = 2; g_task_set_task_data (task, ctx, (GDestroyNotify) set_preferred_network_context_free); /* Query SIM capacity first to find out how many preferred networks it can take */ mm_base_modem_at_command ( self->priv->modem, "+CPOL=?", 20, FALSE, /* Do not cache, the response depends on SIM card properties */ (GAsyncReadyCallback)set_preferred_networks_query_sim_capacity_ready, task); } /*****************************************************************************/ /* SET PREFERRED NETWORKS (DBus call handling) */ typedef struct { MMBaseSim *self; GDBusMethodInvocation *invocation; GVariant *networks; } HandleSetPreferredNetworksContext; static void handle_set_preferred_networks_context_free (HandleSetPreferredNetworksContext *ctx) { g_object_unref (ctx->invocation); g_object_unref (ctx->self); g_variant_unref (ctx->networks); g_free (ctx); } static void handle_set_preferred_networks_ready (MMBaseSim *self, GAsyncResult *res, HandleSetPreferredNetworksContext *ctx) { GError *error = NULL; MM_BASE_SIM_GET_CLASS (self)->set_preferred_networks_finish (self, res, &error); if (error) { mm_obj_warn (self, "couldn't set preferred networks: %s", error->message); mm_dbus_method_invocation_take_error (ctx->invocation, g_steal_pointer (&error)); } else { mm_gdbus_sim_set_preferred_networks (MM_GDBUS_SIM (self), ctx->networks); mm_gdbus_sim_complete_set_preferred_networks (MM_GDBUS_SIM (self), ctx->invocation); } handle_set_preferred_networks_context_free (ctx); } static void handle_set_preferred_networks_auth_ready (MMBaseModem *modem, GAsyncResult *res, HandleSetPreferredNetworksContext *ctx) { GError *error = NULL; if (!mm_base_modem_authorize_finish (modem, res, &error)) { mm_dbus_method_invocation_take_error (ctx->invocation, error); handle_set_preferred_networks_context_free (ctx); return; } if (!mm_gdbus_sim_get_active (MM_GDBUS_SIM (ctx->self))) { mm_dbus_method_invocation_return_error_literal (ctx->invocation, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "Cannot set preferred networks: SIM not currently active"); handle_set_preferred_networks_context_free (ctx); return; } if (IS_ESIM_WITHOUT_PROFILES (ctx->self)) { mm_dbus_method_invocation_return_error_literal (ctx->invocation, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "Cannot set preferred networks: eSIM without profiles"); handle_set_preferred_networks_context_free (ctx); return; } if (!MM_BASE_SIM_GET_CLASS (ctx->self)->set_preferred_networks || !MM_BASE_SIM_GET_CLASS (ctx->self)->set_preferred_networks_finish) { mm_dbus_method_invocation_return_error_literal (ctx->invocation, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "Cannot set preferred networks: not implemented"); handle_set_preferred_networks_context_free (ctx); return; } MM_BASE_SIM_GET_CLASS (ctx->self)->set_preferred_networks ( ctx->self, mm_sim_preferred_network_list_new_from_variant (ctx->networks), (GAsyncReadyCallback)handle_set_preferred_networks_ready, ctx); } static gboolean handle_set_preferred_networks (MMBaseSim *self, GDBusMethodInvocation *invocation, GVariant *networks_variant) { HandleSetPreferredNetworksContext *ctx; ctx = g_new0 (HandleSetPreferredNetworksContext, 1); ctx->self = g_object_ref (self); ctx->invocation = g_object_ref (invocation); ctx->networks = g_variant_ref (networks_variant); mm_base_modem_authorize (self->priv->modem, invocation, MM_AUTHORIZATION_DEVICE_CONTROL, (GAsyncReadyCallback)handle_set_preferred_networks_auth_ready, ctx); return TRUE; } /*****************************************************************************/ static void sim_dbus_export (MMBaseSim *self) { GError *error = NULL; /* Handle method invocations */ g_signal_connect (self, "handle-change-pin", G_CALLBACK (handle_change_pin), NULL); g_signal_connect (self, "handle-enable-pin", G_CALLBACK (handle_enable_pin), NULL); g_signal_connect (self, "handle-send-pin", G_CALLBACK (handle_send_pin), NULL); g_signal_connect (self, "handle-send-puk", G_CALLBACK (handle_send_puk), NULL); g_signal_connect (self, "handle-set-preferred-networks", G_CALLBACK (handle_set_preferred_networks), NULL); if (!g_dbus_interface_skeleton_export (G_DBUS_INTERFACE_SKELETON (self), self->priv->connection, self->priv->path, &error)) { mm_obj_warn (self, "couldn't export SIM: %s", error->message); g_error_free (error); } } static void sim_dbus_unexport (MMBaseSim *self) { /* Only unexport if currently exported */ if (g_dbus_interface_skeleton_get_object_path (G_DBUS_INTERFACE_SKELETON (self))) g_dbus_interface_skeleton_unexport (G_DBUS_INTERFACE_SKELETON (self)); } /*****************************************************************************/ const gchar * mm_base_sim_get_path (MMBaseSim *self) { return self->priv->path; } guint mm_base_sim_get_slot_number (MMBaseSim *self) { return self->priv->slot_number; } /*****************************************************************************/ gboolean mm_base_sim_is_emergency_number (MMBaseSim *self, const gchar *number) { const gchar *const *emergency_numbers; guint i; emergency_numbers = mm_gdbus_sim_get_emergency_numbers (MM_GDBUS_SIM (self)); if (!emergency_numbers) return FALSE; for (i = 0; emergency_numbers[i]; i++) { if (g_strcmp0 (number, emergency_numbers[i]) == 0) return TRUE; } return FALSE; } /*****************************************************************************/ #undef STR_REPLY_READY_FN #define STR_REPLY_READY_FN(NAME) \ static void \ NAME##_command_ready (MMBaseModem *modem, \ GAsyncResult *res, \ GTask *task) \ { \ GError *error = NULL; \ const gchar *response; \ \ response = mm_base_modem_at_command_finish (modem, res, &error); \ if (error) \ g_task_return_error (task, error); \ else \ g_task_return_pointer (task, g_strdup (response), g_free); \ \ g_object_unref (task); \ } /*****************************************************************************/ /* Emergency numbers */ static GStrv parse_emergency_numbers (const gchar *response, GError **error) { guint sw1 = 0; guint sw2 = 0; gchar *hex = 0; GStrv ret; if (!mm_3gpp_parse_crsm_response (response, &sw1, &sw2, &hex, error)) return NULL; if ((sw1 == 0x90 && sw2 == 0x00) || (sw1 == 0x91) || (sw1 == 0x92) || (sw1 == 0x9f)) { ret = mm_3gpp_parse_emergency_numbers (hex, error); g_free (hex); return ret; } g_free (hex); g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "SIM failed to handle CRSM request (sw1 %d sw2 %d)", sw1, sw2); return NULL; } static GStrv load_emergency_numbers_finish (MMBaseSim *self, GAsyncResult *res, GError **error) { gchar *result; GStrv emergency_numbers; guint i; result = g_task_propagate_pointer (G_TASK (res), error); if (!result) return NULL; emergency_numbers = parse_emergency_numbers (result, error); g_free (result); if (!emergency_numbers) return NULL; for (i = 0; emergency_numbers[i]; i++) mm_obj_dbg (self, "loaded emergency number: %s", emergency_numbers[i]); return emergency_numbers; } STR_REPLY_READY_FN (load_emergency_numbers) static void load_emergency_numbers (MMBaseSim *self, GAsyncReadyCallback callback, gpointer user_data) { mm_obj_dbg (self, "loading emergency numbers..."); /* READ BINARY of EF_ECC (Emergency Call Codes) ETSI TS 51.011 section 10.3.27 */ mm_base_modem_at_command ( self->priv->modem, "+CRSM=176,28599,0,0,15", 20, FALSE, (GAsyncReadyCallback)load_emergency_numbers_command_ready, g_task_new (self, NULL, callback, user_data)); } /*****************************************************************************/ /* Preferred networks */ static GList * parse_preferred_networks (const gchar *response, GError **error) { gchar **entries; gchar **iter; GList *result = NULL; entries = g_strsplit_set (response, "\r\n", -1); for (iter = entries; iter && *iter; iter++) { gchar *operator_code = NULL; gboolean gsm_act; gboolean gsm_compact_act; gboolean utran_act; gboolean eutran_act; gboolean ngran_act; MMSimPreferredNetwork *preferred_network = NULL; MMModemAccessTechnology act = MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN; g_strstrip (*iter); if (strlen (*iter) == 0) continue; if (mm_sim_parse_cpol_query_response (*iter, NULL, &operator_code, &gsm_act, &gsm_compact_act, &utran_act, &eutran_act, &ngran_act, NULL, error)) { preferred_network = mm_sim_preferred_network_new (); mm_sim_preferred_network_set_operator_code (preferred_network, operator_code); if (gsm_act) act |= MM_MODEM_ACCESS_TECHNOLOGY_GSM; if (gsm_compact_act) act |= MM_MODEM_ACCESS_TECHNOLOGY_GSM_COMPACT; if (utran_act) act |= MM_MODEM_ACCESS_TECHNOLOGY_UMTS; if (eutran_act) act |= MM_MODEM_ACCESS_TECHNOLOGY_LTE; if (ngran_act) act |= MM_MODEM_ACCESS_TECHNOLOGY_5GNR; mm_sim_preferred_network_set_access_technology (preferred_network, act); result = g_list_append (result, preferred_network); } else break; g_free (operator_code); } g_strfreev (entries); return result; } static GList * load_preferred_networks_finish (MMBaseSim *self, GAsyncResult *res, GError **error) { gchar *result; GList *preferred_network_list; result = g_task_propagate_pointer (G_TASK (res), error); if (!result) return NULL; preferred_network_list = parse_preferred_networks (result, error); mm_obj_dbg (self, "loaded %u preferred networks", g_list_length (preferred_network_list)); g_free (result); return preferred_network_list; } STR_REPLY_READY_FN (load_preferred_networks) static void load_preferred_networks_set_format_ready (MMBaseModem *modem, GAsyncResult *res, GTask *task) { MMBaseSim *self; GError *error = NULL; self = g_task_get_source_object (task); /* Ignore error */ mm_base_modem_at_command_finish (modem, res, &error); if (error) { mm_obj_dbg (self, "setting preferred network list format failed: '%s'", error->message); g_error_free (error); } mm_obj_dbg (self, "loading preferred networks..."); mm_base_modem_at_command ( modem, "+CPOL?", 20, FALSE, (GAsyncReadyCallback)load_preferred_networks_command_ready, task); } static void load_preferred_networks_cpls_command_ready (MMBaseModem *modem, GAsyncResult *res, GTask *task) { MMBaseSim *self; GError *error = NULL; self = g_task_get_source_object (task); /* AT+CPLS may not be supported so we ignore any error and proceed even if it fails */ mm_base_modem_at_command_finish (modem, res, &error); if (error) { mm_obj_dbg (self, "selecting user-defined preferred network list failed: '%s'", error->message); g_error_free (error); } mm_obj_dbg (self, "setting preferred networks format..."); /* Request numeric MCCMNC format */ mm_base_modem_at_command ( modem, "+CPOL=,2", 20, FALSE, (GAsyncReadyCallback)load_preferred_networks_set_format_ready, task); } static void load_preferred_networks (MMBaseSim *self, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; task = g_task_new (self, NULL, callback, user_data); if (check_preferred_networks_disabled (self)) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "setting preferred networks is unsupported"); g_object_unref (task); return; } /* Invoke AT+CPLS=0 first to make sure the correct (user-defined) preferred network list is selected */ mm_obj_dbg (self, "selecting user-defined preferred network list..."); mm_base_modem_at_command ( self->priv->modem, "+CPLS=0", 20, FALSE, (GAsyncReadyCallback)load_preferred_networks_cpls_command_ready, task); } /*****************************************************************************/ /* ICCID */ static gchar * parse_iccid (const gchar *response, GError **error) { guint sw1 = 0; guint sw2 = 0; gchar *hex = 0; gchar *ret; if (!mm_3gpp_parse_crsm_response (response, &sw1, &sw2, &hex, error)) return NULL; if ((sw1 == 0x90 && sw2 == 0x00) || (sw1 == 0x91) || (sw1 == 0x92) || (sw1 == 0x9f)) { ret = mm_3gpp_parse_iccid (hex, error); g_free (hex); return ret; } else { g_free (hex); g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "SIM failed to handle CRSM request (sw1 %d sw2 %d)", sw1, sw2); return NULL; } } static gchar * load_sim_identifier_finish (MMBaseSim *self, GAsyncResult *res, GError **error) { gchar *result; gchar *sim_identifier; result = g_task_propagate_pointer (G_TASK (res), error); if (!result) return NULL; sim_identifier = parse_iccid (result, error); g_free (result); if (!sim_identifier) return NULL; mm_obj_dbg (self, "loaded SIM identifier: %s", sim_identifier); return sim_identifier; } STR_REPLY_READY_FN (load_sim_identifier) static void load_sim_identifier (MMBaseSim *self, GAsyncReadyCallback callback, gpointer user_data) { mm_obj_dbg (self, "loading SIM identifier..."); /* READ BINARY of EFiccid (ICC Identification) ETSI TS 102.221 section 13.2 */ mm_base_modem_at_command ( self->priv->modem, "+CRSM=176,12258,0,0,10", 20, FALSE, (GAsyncReadyCallback)load_sim_identifier_command_ready, g_task_new (self, NULL, callback, user_data)); } /*****************************************************************************/ /* IMSI */ static gchar * parse_imsi (const gchar *response, GError **error) { const gchar *s; gint len; g_assert (response != NULL); for (s = mm_strip_tag (response, "+CIMI"), len = 0; *s; ++s, ++len) { /* IMSI is a number with 15 or less decimal digits. */ if (!isdigit (*s) || len > 15) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Invalid +CIMI response '%s'", response ? response : ""); return NULL; } } return g_strdup (response); } static gchar * load_imsi_finish (MMBaseSim *self, GAsyncResult *res, GError **error) { gchar *result; gchar *imsi; result = g_task_propagate_pointer (G_TASK (res), error); if (!result) return NULL; imsi = parse_imsi (result, error); g_free (result); if (!imsi) return NULL; mm_obj_dbg (self, "loaded IMSI: %s", imsi); return imsi; } STR_REPLY_READY_FN (load_imsi) static void load_imsi (MMBaseSim *self, GAsyncReadyCallback callback, gpointer user_data) { mm_obj_dbg (self, "loading IMSI..."); mm_base_modem_at_command ( self->priv->modem, "+CIMI", 3, FALSE, (GAsyncReadyCallback)load_imsi_command_ready, g_task_new (self, NULL, callback, user_data)); } /*****************************************************************************/ /* Operator ID */ static guint parse_mnc_length (const gchar *response, GError **error) { guint sw1 = 0; guint sw2 = 0; g_autofree gchar *hex = NULL; if (!mm_3gpp_parse_crsm_response (response, &sw1, &sw2, &hex, error)) return 0; if ((sw1 == 0x90 && sw2 == 0x00) || (sw1 == 0x91) || (sw1 == 0x92) || (sw1 == 0x9f)) { gsize buflen = 0; guint32 mnc_len; g_autofree guint8 *bin = NULL; /* Convert hex string to binary */ bin = mm_utils_hexstr2bin (hex, -1, &buflen, error); if (!bin) { g_prefix_error (error, "SIM returned malformed response '%s': ", hex); return 0; } if (buflen < 4) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "SIM returned malformed response '%s': too short", hex); return 0; } /* MNC length is byte 4 of this SIM file */ mnc_len = bin[3]; if (mnc_len == 2 || mnc_len == 3) return mnc_len; g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "SIM returned invalid MNC length %d (should be either 2 or 3)", mnc_len); return 0; } g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "SIM failed to handle CRSM request (sw1 %d sw2 %d)", sw1, sw2); return 0; } static gchar * load_operator_identifier_finish (MMBaseSim *self, GAsyncResult *res, GError **error) { GError *inner_error = NULL; const gchar *imsi; gchar *result; guint mnc_length; result = g_task_propagate_pointer (G_TASK (res), error); if (!result) return NULL; mnc_length = parse_mnc_length (result, &inner_error); g_free (result); if (inner_error) { g_propagate_error (error, inner_error); return NULL; } imsi = mm_gdbus_sim_get_imsi (MM_GDBUS_SIM (self)); if (!imsi) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Cannot load Operator ID without IMSI"); return NULL; } /* Build Operator ID */ return g_strndup (imsi, 3 + mnc_length); } STR_REPLY_READY_FN (load_operator_identifier) static void load_operator_identifier (MMBaseSim *self, GAsyncReadyCallback callback, gpointer user_data) { mm_obj_dbg (self, "loading operator ID..."); /* READ BINARY of EFad (Administrative Data) ETSI 51.011 section 10.3.18 * SIMCOM A760xE-H modems can answer in 10s or more, so use rather big timeout */ mm_base_modem_at_command ( self->priv->modem, "+CRSM=176,28589,0,0,4", 20, FALSE, (GAsyncReadyCallback)load_operator_identifier_command_ready, g_task_new (self, NULL, callback, user_data)); } /*****************************************************************************/ /* Operator Name (Service Provider Name) */ static gchar * parse_spn (const gchar *response, GError **error) { guint sw1 = 0; guint sw2 = 0; g_autofree gchar *hex = NULL; if (!mm_3gpp_parse_crsm_response (response, &sw1, &sw2, &hex, error)) return NULL; if ((sw1 == 0x90 && sw2 == 0x00) || (sw1 == 0x91) || (sw1 == 0x92) || (sw1 == 0x9f)) { g_autoptr(GByteArray) bin_array = NULL; g_autofree guint8 *bin = NULL; gsize binlen = 0; /* Convert hex string to binary */ bin = mm_utils_hexstr2bin (hex, -1, &binlen, error); if (!bin) { g_prefix_error (error, "SIM returned malformed response '%s': ", hex); return NULL; } /* Remove the FF filler at the end */ while (binlen > 1 && bin[binlen - 1] == 0xff) binlen--; if (binlen <= 1) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "SIM returned empty response '%s'", hex); return NULL; } /* Setup as bytearray. * First byte is metadata; remainder is GSM-7 unpacked into octets; convert to UTF8 */ bin_array = g_byte_array_sized_new (binlen - 1); g_byte_array_append (bin_array, bin + 1, binlen - 1); return mm_modem_charset_bytearray_to_utf8 (bin_array, MM_MODEM_CHARSET_GSM, FALSE, error); } g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "SIM failed to handle CRSM request (sw1 %d sw2 %d)", sw1, sw2); return NULL; } static gchar * load_operator_name_finish (MMBaseSim *self, GAsyncResult *res, GError **error) { gchar *result; gchar *spn; result = g_task_propagate_pointer (G_TASK (res), error); if (!result) return NULL; spn = parse_spn (result, error); g_free (result); return spn; } STR_REPLY_READY_FN (load_operator_name) static void load_operator_name (MMBaseSim *self, GAsyncReadyCallback callback, gpointer user_data) { mm_obj_dbg (self, "loading operator name..."); /* READ BINARY of EFspn (Service Provider Name) ETSI 51.011 section 10.3.11 */ mm_base_modem_at_command ( self->priv->modem, "+CRSM=176,28486,0,0,17", 10, FALSE, (GAsyncReadyCallback)load_operator_name_command_ready, g_task_new (self, NULL, callback, user_data)); } /*****************************************************************************/ /* GID1 and GID2 */ static GByteArray * parse_gid (const gchar *response, GError **error) { guint sw1 = 0; guint sw2 = 0; g_autofree gchar *hex = NULL; if (!mm_3gpp_parse_crsm_response (response, &sw1, &sw2, &hex, error)) return NULL; if ((sw1 == 0x90 && sw2 == 0x00) || (sw1 == 0x91) || (sw1 == 0x92) || (sw1 == 0x9f)) { guint8 *bin = NULL; gsize binlen = 0; /* Convert hex string to binary */ bin = mm_utils_hexstr2bin (hex, -1, &binlen, error); if (!bin) { g_prefix_error (error, "SIM returned malformed response '%s': ", hex); return NULL; } /* return as bytearray */ return g_byte_array_new_take (bin, binlen); } g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "SIM failed to handle CRSM request (sw1 %d sw2 %d)", sw1, sw2); return NULL; } static GByteArray * common_load_gid_finish (MMBaseSim *self, GAsyncResult *res, GError **error) { g_autofree gchar *result = NULL; result = g_task_propagate_pointer (G_TASK (res), error); if (!result) return NULL; return parse_gid (result, error); } STR_REPLY_READY_FN (load_gid1) STR_REPLY_READY_FN (load_gid2) static void load_gid1 (MMBaseSim *self, GAsyncReadyCallback callback, gpointer user_data) { /* READ BINARY of EFgid1 */ mm_base_modem_at_command ( self->priv->modem, "+CRSM=176,28478,0,0,0", 10, FALSE, (GAsyncReadyCallback)load_gid1_command_ready, g_task_new (self, NULL, callback, user_data)); } static void load_gid2 (MMBaseSim *self, GAsyncReadyCallback callback, gpointer user_data) { /* READ BINARY of EFgid2 */ mm_base_modem_at_command ( self->priv->modem, "+CRSM=176,28479,0,0,0", 10, FALSE, (GAsyncReadyCallback)load_gid2_command_ready, g_task_new (self, NULL, callback, user_data)); } /*****************************************************************************/ MMBaseSim * mm_base_sim_new_initialized (MMBaseModem *modem, guint slot_number, gboolean active, const gchar *sim_identifier, const gchar *imsi, const gchar *eid, const gchar *operator_identifier, const gchar *operator_name, const GStrv emergency_numbers) { MMBaseSim *sim; sim = MM_BASE_SIM (g_object_new (MM_TYPE_BASE_SIM, MM_BASE_SIM_MODEM, modem, MM_BASE_SIM_SLOT_NUMBER, slot_number, "active", active, "sim-identifier", sim_identifier, "imsi", imsi, "eid", eid, "operator-identifier", operator_identifier, "operator-name", operator_name, "emergency-numbers", emergency_numbers, NULL)); mm_base_sim_export (sim); return sim; } /*****************************************************************************/ typedef struct _InitAsyncContext InitAsyncContext; static void interface_initialization_step (GTask *task); typedef enum { INITIALIZATION_STEP_FIRST, INITIALIZATION_STEP_WAIT_READY, INITIALIZATION_STEP_SIM_TYPE, INITIALIZATION_STEP_ESIM_STATUS, INITIALIZATION_STEP_SIM_IDENTIFIER, INITIALIZATION_STEP_IMSI, INITIALIZATION_STEP_OPERATOR_ID, INITIALIZATION_STEP_OPERATOR_NAME, INITIALIZATION_STEP_EMERGENCY_NUMBERS, INITIALIZATION_STEP_PREFERRED_NETWORKS, INITIALIZATION_STEP_GID1, INITIALIZATION_STEP_GID2, INITIALIZATION_STEP_EID, INITIALIZATION_STEP_REMOVABILITY, INITIALIZATION_STEP_LAST } InitializationStep; struct _InitAsyncContext { InitializationStep step; guint sim_identifier_tries; }; MMBaseSim * mm_base_sim_new_finish (GAsyncResult *res, GError **error) { GObject *source; GObject *sim; source = g_async_result_get_source_object (res); sim = g_async_initable_new_finish (G_ASYNC_INITABLE (source), res, error); g_object_unref (source); if (!sim) return NULL; /* Only export valid SIMs */ mm_base_sim_export (MM_BASE_SIM (sim)); return MM_BASE_SIM (sim); } static gboolean initable_init_finish (GAsyncInitable *initable, GAsyncResult *result, GError **error) { return g_task_propagate_boolean (G_TASK (result), error); } #undef COMMON_STR_REPLY_READY_FN #define COMMON_STR_REPLY_READY_FN(NAME,DISPLAY,VALUE_FORMAT) \ static void \ init_load_##NAME##_ready (MMBaseSim *self, \ GAsyncResult *res, \ GTask *task) \ { \ InitAsyncContext *ctx; \ g_autoptr(GError) error = NULL; \ g_autofree gchar *val = NULL; \ \ val = MM_BASE_SIM_GET_CLASS (self)->load_##NAME##_finish (self, res, &error); \ mm_gdbus_sim_set_##NAME (MM_GDBUS_SIM (self), val); \ \ if (error) \ mm_obj_dbg (self, "couldn't load %s: %s", DISPLAY, error->message); \ else \ mm_obj_info (self, "loaded %s: %s", DISPLAY, VALUE_FORMAT (val)); \ \ /* Go on to next step */ \ ctx = g_task_get_task_data (task); \ ctx->step++; \ interface_initialization_step (task); \ } #undef STR_REPLY_READY_FN #define STR_REPLY_READY_FN(NAME,DISPLAY) COMMON_STR_REPLY_READY_FN (NAME, DISPLAY, (const gchar *)) #undef PERSONAL_STR_REPLY_READY_FN #define PERSONAL_STR_REPLY_READY_FN(NAME,DISPLAY) COMMON_STR_REPLY_READY_FN (NAME, DISPLAY, mm_log_str_personal_info) #undef ENUM_REPLY_READY_FN #define ENUM_REPLY_READY_FN(NAME,DISPLAY,ENUM_TYPE,ENUM_GET_STRING) \ static void \ init_load_##NAME##_ready (MMBaseSim *self, \ GAsyncResult *res, \ GTask *task) \ { \ InitAsyncContext *ctx; \ g_autoptr(GError) error = NULL; \ ENUM_TYPE val; \ \ val = MM_BASE_SIM_GET_CLASS (self)->load_##NAME##_finish (self, res, &error); \ mm_gdbus_sim_set_##NAME (MM_GDBUS_SIM (self), (guint) val); \ \ if (error) \ mm_obj_dbg (self, "couldn't load %s: %s", DISPLAY, error->message); \ else \ mm_obj_info (self, "loaded %s: %s", DISPLAY, ENUM_GET_STRING (val)); \ \ /* Go on to next step */ \ ctx = g_task_get_task_data (task); \ ctx->step++; \ interface_initialization_step (task); \ } #undef BYTEARRAY_REPLY_READY_FN #define BYTEARRAY_REPLY_READY_FN(NAME,DISPLAY) \ static void \ init_load_##NAME##_ready (MMBaseSim *self, \ GAsyncResult *res, \ GTask *task) \ { \ InitAsyncContext *ctx; \ g_autoptr(GError) error = NULL; \ g_autoptr(GByteArray) bytearray = NULL; \ \ bytearray = MM_BASE_SIM_GET_CLASS (self)->load_##NAME##_finish (self, res, &error); \ mm_gdbus_sim_set_##NAME (MM_GDBUS_SIM (self), \ (bytearray ? \ g_variant_new_fixed_array (G_VARIANT_TYPE_BYTE, \ bytearray->data, \ bytearray->len, \ sizeof (guint8)) : \ NULL)); \ \ if (error) \ mm_obj_dbg (self, "couldn't load %s: %s", DISPLAY, error->message); \ else { \ g_autofree gchar *bytearray_str = NULL; \ \ bytearray_str = mm_utils_bin2hexstr (bytearray->data, bytearray->len); \ mm_obj_info (self, "loaded %s: %s", DISPLAY, bytearray_str); \ } \ \ /* Go on to next step */ \ ctx = g_task_get_task_data (task); \ ctx->step++; \ interface_initialization_step (task); \ } ENUM_REPLY_READY_FN (removability, "removability", MMSimRemovability, mm_sim_removability_get_string) PERSONAL_STR_REPLY_READY_FN (eid, "EID") BYTEARRAY_REPLY_READY_FN (gid2, "GID2") BYTEARRAY_REPLY_READY_FN (gid1, "GID1") static void init_load_preferred_networks_ready (MMBaseSim *self, GAsyncResult *res, GTask *task) { InitAsyncContext *ctx; g_autoptr(GError) error = NULL; GList *preferred_nets_list; preferred_nets_list = MM_BASE_SIM_GET_CLASS (self)->load_preferred_networks_finish (self, res, &error); if (error) mm_obj_dbg (self, "couldn't load list of preferred networks: %s", error->message); else { g_autoptr(GString) str = NULL; GList *l; str = g_string_new (""); for (l = preferred_nets_list; l; l = g_list_next (l)) { MMSimPreferredNetwork *item; g_autofree gchar *access_tech_str = NULL; item = (MMSimPreferredNetwork *)(l->data); access_tech_str = mm_modem_access_technology_build_string_from_mask (mm_sim_preferred_network_get_access_technology (item)); g_string_append_printf (str, "%s%s (%s)", str->len ? ", " : "", mm_sim_preferred_network_get_operator_code (item), access_tech_str); } mm_obj_info (self, "loaded list of preferred networks: %s", str->str); } mm_gdbus_sim_set_preferred_networks (MM_GDBUS_SIM (self), mm_sim_preferred_network_list_get_variant (preferred_nets_list)); g_list_free_full (preferred_nets_list, (GDestroyNotify) mm_sim_preferred_network_free); /* Go on to next step */ ctx = g_task_get_task_data (task); ctx->step++; interface_initialization_step (task); } static void init_load_emergency_numbers_ready (MMBaseSim *self, GAsyncResult *res, GTask *task) { InitAsyncContext *ctx; g_autoptr(GError) error = NULL; g_auto(GStrv) str_list = NULL; str_list = MM_BASE_SIM_GET_CLASS (self)->load_emergency_numbers_finish (self, res, &error); if (error) mm_obj_dbg (self, "couldn't load list of emergency numbers: %s", error->message); else { g_autoptr(GString) str = NULL; guint i; str = g_string_new (""); for (i = 0; str_list && str_list[i]; i++) g_string_append_printf (str, "%s%s", str->len ? ", " : "", str_list[i]); mm_obj_info (self, "loaded list of emergency numbers: %s", str->str); } mm_gdbus_sim_set_emergency_numbers (MM_GDBUS_SIM (self), (const gchar *const *) str_list); /* Go on to next step */ ctx = g_task_get_task_data (task); ctx->step++; interface_initialization_step (task); } STR_REPLY_READY_FN (operator_name, "operator name") STR_REPLY_READY_FN (operator_identifier, "operator identifier") PERSONAL_STR_REPLY_READY_FN (imsi, "IMSI") static void init_load_sim_identifier_ready (MMBaseSim *self, GAsyncResult *res, GTask *task) { InitAsyncContext *ctx; GError *error = NULL; gchar *simid; ctx = g_task_get_task_data (task); simid = MM_BASE_SIM_GET_CLASS (self)->load_sim_identifier_finish (self, res, &error); if (!simid) { /* TODO: make the retries gobi-specific? */ /* Try one more time... Gobi 1K cards may reply to the first * request with '+CRSM: 106,134,""' which is bogus because * subsequent requests work fine. */ if (++ctx->sim_identifier_tries < 2) { g_clear_error (&error); interface_initialization_step (task); return; } mm_obj_warn (self, "couldn't load SIM identifier: %s", error ? error->message : "unknown error"); g_clear_error (&error); } else mm_obj_info (self, "loaded SIM identifier: %s", mm_log_str_personal_info (simid)); mm_gdbus_sim_set_sim_identifier (MM_GDBUS_SIM (self), simid); g_free (simid); /* Go on to next step */ ctx->step++; interface_initialization_step (task); } ENUM_REPLY_READY_FN (esim_status, "esim status", MMSimEsimStatus, mm_sim_esim_status_get_string) ENUM_REPLY_READY_FN (sim_type, "sim type", MMSimType, mm_sim_type_get_string) static void init_wait_sim_ready (MMBaseSim *self, GAsyncResult *res, GTask *task) { InitAsyncContext *ctx; g_autoptr(GError) error = NULL; if (!MM_BASE_SIM_GET_CLASS (self)->wait_sim_ready_finish (self, res, &error)) mm_obj_dbg (self, "couldn't wait for SIM to be ready: %s", error->message); /* Go on to next step */ ctx = g_task_get_task_data (task); ctx->step++; interface_initialization_step (task); } static void interface_initialization_step (GTask *task) { MMBaseSim *self; InitAsyncContext *ctx; if (g_task_return_error_if_cancelled (task)) { g_object_unref (task); return; } self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); switch (ctx->step) { case INITIALIZATION_STEP_FIRST: ctx->step++; /* Fall through */ case INITIALIZATION_STEP_WAIT_READY: if (MM_BASE_SIM_GET_CLASS (self)->wait_sim_ready && MM_BASE_SIM_GET_CLASS (self)->wait_sim_ready_finish) { MM_BASE_SIM_GET_CLASS (self)->wait_sim_ready ( self, (GAsyncReadyCallback)init_wait_sim_ready, task); return; } ctx->step++; /* Fall through */ case INITIALIZATION_STEP_SIM_TYPE: if (mm_gdbus_sim_get_sim_type (MM_GDBUS_SIM (self)) == MM_SIM_TYPE_UNKNOWN && MM_BASE_SIM_GET_CLASS (self)->load_sim_type && MM_BASE_SIM_GET_CLASS (self)->load_sim_type_finish) { MM_BASE_SIM_GET_CLASS (self)->load_sim_type ( self, (GAsyncReadyCallback)init_load_sim_type_ready, task); return; } ctx->step++; /* Fall through */ case INITIALIZATION_STEP_ESIM_STATUS: /* Don't load eSIM status if the SIM is known to be a physical SIM */ if (IS_PSIM (self)) mm_obj_dbg (self, "not loading eSIM status in physical SIM"); else if (mm_gdbus_sim_get_esim_status (MM_GDBUS_SIM (self)) == MM_SIM_ESIM_STATUS_UNKNOWN && MM_BASE_SIM_GET_CLASS (self)->load_esim_status && MM_BASE_SIM_GET_CLASS (self)->load_esim_status_finish) { MM_BASE_SIM_GET_CLASS (self)->load_esim_status ( self, (GAsyncReadyCallback)init_load_esim_status_ready, task); return; } ctx->step++; /* Fall through */ case INITIALIZATION_STEP_SIM_IDENTIFIER: /* Don't load SIM ICCID if the SIM is known to be an eSIM without * profiles; otherwise (if physical SIM, or if eSIM with profile, or if * SIM type unknown) try to load it. */ if (IS_ESIM_WITHOUT_PROFILES (self)) mm_obj_dbg (self, "not loading SIM identifier in eSIM without profiles"); else if (mm_gdbus_sim_get_sim_identifier (MM_GDBUS_SIM (self)) == NULL && MM_BASE_SIM_GET_CLASS (self)->load_sim_identifier && MM_BASE_SIM_GET_CLASS (self)->load_sim_identifier_finish) { MM_BASE_SIM_GET_CLASS (self)->load_sim_identifier ( self, (GAsyncReadyCallback)init_load_sim_identifier_ready, task); return; } ctx->step++; /* Fall through */ case INITIALIZATION_STEP_IMSI: /* Don't load SIM IMSI if the SIM is known to be an eSIM without * profiles; otherwise (if physical SIM, or if eSIM with profile, or if * SIM type unknown) try to load it. */ if (IS_ESIM_WITHOUT_PROFILES (self)) mm_obj_dbg (self, "not loading IMSI in eSIM without profiles"); else if (mm_gdbus_sim_get_imsi (MM_GDBUS_SIM (self)) == NULL && MM_BASE_SIM_GET_CLASS (self)->load_imsi && MM_BASE_SIM_GET_CLASS (self)->load_imsi_finish) { MM_BASE_SIM_GET_CLASS (self)->load_imsi ( self, (GAsyncReadyCallback)init_load_imsi_ready, task); return; } ctx->step++; /* Fall through */ case INITIALIZATION_STEP_OPERATOR_ID: /* Don't load operator ID if the SIM is known to be an eSIM without * profiles; otherwise (if physical SIM, or if eSIM with profile, or if * SIM type unknown) try to load it. */ if (IS_ESIM_WITHOUT_PROFILES (self)) mm_obj_dbg (self, "not loading operator ID in eSIM without profiles"); else if (mm_gdbus_sim_get_operator_identifier (MM_GDBUS_SIM (self)) == NULL && MM_BASE_SIM_GET_CLASS (self)->load_operator_identifier && MM_BASE_SIM_GET_CLASS (self)->load_operator_identifier_finish) { MM_BASE_SIM_GET_CLASS (self)->load_operator_identifier ( self, (GAsyncReadyCallback)init_load_operator_identifier_ready, task); return; } ctx->step++; /* Fall through */ case INITIALIZATION_STEP_OPERATOR_NAME: /* Don't load operator name if the SIM is known to be an eSIM without * profiles; otherwise (if physical SIM, or if eSIM with profile, or if * SIM type unknown) try to load it. */ if (IS_ESIM_WITHOUT_PROFILES (self)) mm_obj_dbg (self, "not loading operator name in eSIM without profiles"); else if (mm_gdbus_sim_get_operator_name (MM_GDBUS_SIM (self)) == NULL && MM_BASE_SIM_GET_CLASS (self)->load_operator_name && MM_BASE_SIM_GET_CLASS (self)->load_operator_name_finish) { MM_BASE_SIM_GET_CLASS (self)->load_operator_name ( self, (GAsyncReadyCallback)init_load_operator_name_ready, task); return; } ctx->step++; /* Fall through */ case INITIALIZATION_STEP_EMERGENCY_NUMBERS: /* Don't load emergency numbers if the SIM is known to be an eSIM without * profiles; otherwise (if physical SIM, or if eSIM with profile, or if * SIM type unknown) try to load it. */ if (IS_ESIM_WITHOUT_PROFILES (self)) mm_obj_dbg (self, "not loading emergency numbers in eSIM without profiles"); else if (mm_gdbus_sim_get_emergency_numbers (MM_GDBUS_SIM (self)) == NULL && MM_BASE_SIM_GET_CLASS (self)->load_emergency_numbers && MM_BASE_SIM_GET_CLASS (self)->load_emergency_numbers_finish) { MM_BASE_SIM_GET_CLASS (self)->load_emergency_numbers ( self, (GAsyncReadyCallback)init_load_emergency_numbers_ready, task); return; } ctx->step++; /* Fall through */ case INITIALIZATION_STEP_PREFERRED_NETWORKS: /* Don't load preferred networks if the SIM is known to be an eSIM without * profiles; otherwise (if physical SIM, or if eSIM with profile, or if * SIM type unknown) try to load it. */ if (IS_ESIM_WITHOUT_PROFILES (self)) mm_obj_dbg (self, "not loading preferred networks in eSIM without profiles"); else if (mm_gdbus_sim_get_preferred_networks (MM_GDBUS_SIM (self)) == NULL && MM_BASE_SIM_GET_CLASS (self)->load_preferred_networks && MM_BASE_SIM_GET_CLASS (self)->load_preferred_networks_finish) { MM_BASE_SIM_GET_CLASS (self)->load_preferred_networks ( self, (GAsyncReadyCallback)init_load_preferred_networks_ready, task); return; } ctx->step++; /* Fall through */ case INITIALIZATION_STEP_GID1: /* Don't load GID1 if the SIM is known to be an eSIM without profiles; * otherwise (if physical SIM, or if eSIM with profile, or if * SIM type unknown) try to load it. */ if (IS_ESIM_WITHOUT_PROFILES (self)) mm_obj_dbg (self, "not loading GID1 in eSIM without profiles"); else if (mm_gdbus_sim_get_gid1 (MM_GDBUS_SIM (self)) == NULL && MM_BASE_SIM_GET_CLASS (self)->load_gid1 && MM_BASE_SIM_GET_CLASS (self)->load_gid1_finish) { MM_BASE_SIM_GET_CLASS (self)->load_gid1 ( self, (GAsyncReadyCallback)init_load_gid1_ready, task); return; } ctx->step++; /* Fall through */ case INITIALIZATION_STEP_GID2: /* Don't load GID2 if the SIM is known to be an eSIM without profiles; * otherwise (if physical SIM, or if eSIM with profile, or if * SIM type unknown) try to load it. */ if (IS_ESIM_WITHOUT_PROFILES (self)) mm_obj_dbg (self, "not loading GID2 in eSIM without profiles"); else if (mm_gdbus_sim_get_gid2 (MM_GDBUS_SIM (self)) == NULL && MM_BASE_SIM_GET_CLASS (self)->load_gid2 && MM_BASE_SIM_GET_CLASS (self)->load_gid2_finish) { MM_BASE_SIM_GET_CLASS (self)->load_gid2 ( self, (GAsyncReadyCallback)init_load_gid2_ready, task); return; } ctx->step++; /* Fall through */ case INITIALIZATION_STEP_EID: /* Don't load EID if the SIM is known to be a physical SIM; otherwise * (if eSIM with or without profiles) try to load it. */ if (IS_PSIM (self)) mm_obj_dbg (self, "not loading EID in physical SIM"); else if (mm_gdbus_sim_get_eid (MM_GDBUS_SIM (self)) == NULL && MM_BASE_SIM_GET_CLASS (self)->load_eid && MM_BASE_SIM_GET_CLASS (self)->load_eid_finish) { MM_BASE_SIM_GET_CLASS (self)->load_eid ( self, (GAsyncReadyCallback)init_load_eid_ready, task); return; } ctx->step++; /* Fall through */ case INITIALIZATION_STEP_REMOVABILITY: /* Although not very common, there are removable eSIMs, so always try to * load it, regardless of SIM type. */ if (mm_gdbus_sim_get_removability (MM_GDBUS_SIM (self)) == MM_SIM_REMOVABILITY_UNKNOWN && MM_BASE_SIM_GET_CLASS (self)->load_removability && MM_BASE_SIM_GET_CLASS (self)->load_removability_finish) { MM_BASE_SIM_GET_CLASS (self)->load_removability ( self, (GAsyncReadyCallback)init_load_removability_ready, task); return; } ctx->step++; /* Fall through */ case INITIALIZATION_STEP_LAST: /* We are done without errors! */ g_task_return_boolean (task, TRUE); g_object_unref (task); return; default: break; } g_assert_not_reached (); } static void common_init_async (GAsyncInitable *initable, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { MMBaseSim *self; InitAsyncContext *ctx; GTask *task; self = MM_BASE_SIM (initable); ctx = g_new (InitAsyncContext, 1); ctx->step = INITIALIZATION_STEP_FIRST; ctx->sim_identifier_tries = 0; task = g_task_new (self, cancellable, callback, user_data); g_task_set_task_data (task, ctx, g_free); interface_initialization_step (task); } static void initable_init_async (GAsyncInitable *initable, int io_priority, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { mm_gdbus_sim_set_sim_identifier (MM_GDBUS_SIM (initable), NULL); mm_gdbus_sim_set_imsi (MM_GDBUS_SIM (initable), NULL); mm_gdbus_sim_set_eid (MM_GDBUS_SIM (initable), NULL); mm_gdbus_sim_set_operator_identifier (MM_GDBUS_SIM (initable), NULL); mm_gdbus_sim_set_operator_name (MM_GDBUS_SIM (initable), NULL); mm_gdbus_sim_set_gid1 (MM_GDBUS_SIM (initable), NULL); mm_gdbus_sim_set_gid2 (MM_GDBUS_SIM (initable), NULL); common_init_async (initable, cancellable, callback, user_data); } void mm_base_sim_new (MMBaseModem *modem, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { g_async_initable_new_async (MM_TYPE_BASE_SIM, G_PRIORITY_DEFAULT, cancellable, callback, user_data, MM_BASE_SIM_MODEM, modem, "active", TRUE, /* by default always active */ NULL); } gboolean mm_base_sim_initialize_finish (MMBaseSim *self, GAsyncResult *result, GError **error) { return initable_init_finish (G_ASYNC_INITABLE (self), result, error); } void mm_base_sim_initialize (MMBaseSim *self, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { common_init_async (G_ASYNC_INITABLE (self), cancellable, callback, user_data); } /*****************************************************************************/ static gchar * log_object_build_id (MMLogObject *_self) { MMBaseSim *self; self = MM_BASE_SIM (_self); return g_strdup_printf ("sim%u", self->priv->dbus_id); } /*****************************************************************************/ static void set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { MMBaseSim *self = MM_BASE_SIM (object); switch (prop_id) { case PROP_PATH: g_free (self->priv->path); self->priv->path = g_value_dup_string (value); /* Export when we get a DBus connection AND we have a path */ if (self->priv->path && self->priv->connection) sim_dbus_export (self); break; case PROP_CONNECTION: g_clear_object (&self->priv->connection); self->priv->connection = g_value_dup_object (value); /* Export when we get a DBus connection AND we have a path */ if (!self->priv->connection) sim_dbus_unexport (self); else if (self->priv->path) sim_dbus_export (self); break; case PROP_MODEM: g_clear_object (&self->priv->modem); self->priv->modem = g_value_dup_object (value); if (self->priv->modem) { /* Set owner ID */ mm_log_object_set_owner_id (MM_LOG_OBJECT (self), mm_log_object_get_id (MM_LOG_OBJECT (self->priv->modem))); /* Bind the modem's connection (which is set when it is exported, * and unset when unexported) to the SIM's connection */ g_object_bind_property (self->priv->modem, MM_BASE_MODEM_CONNECTION, self, MM_BASE_SIM_CONNECTION, G_BINDING_DEFAULT | G_BINDING_SYNC_CREATE); } break; case PROP_SLOT_NUMBER: self->priv->slot_number = g_value_get_uint (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) { MMBaseSim *self = MM_BASE_SIM (object); switch (prop_id) { case PROP_PATH: g_value_set_string (value, self->priv->path); break; case PROP_CONNECTION: g_value_set_object (value, self->priv->connection); break; case PROP_MODEM: g_value_set_object (value, self->priv->modem); break; case PROP_SLOT_NUMBER: g_value_set_uint (value, self->priv->slot_number); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void mm_base_sim_init (MMBaseSim *self) { static guint id = 0; /* Initialize private data */ self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, MM_TYPE_BASE_SIM, MMBaseSimPrivate); /* Each SIM is given a unique id to build its own DBus path */ self->priv->dbus_id = id++; } static void finalize (GObject *object) { MMBaseSim *self = MM_BASE_SIM (object); g_free (self->priv->path); G_OBJECT_CLASS (mm_base_sim_parent_class)->finalize (object); } static void dispose (GObject *object) { MMBaseSim *self = MM_BASE_SIM (object); if (self->priv->connection) { /* If we arrived here with a valid connection, make sure we unexport * the object */ sim_dbus_unexport (self); g_clear_object (&self->priv->connection); } g_clear_object (&self->priv->modem); G_OBJECT_CLASS (mm_base_sim_parent_class)->dispose (object); } static void async_initable_iface_init (GAsyncInitableIface *iface) { iface->init_async = initable_init_async; iface->init_finish = initable_init_finish; } static void log_object_iface_init (MMLogObjectInterface *iface) { iface->build_id = log_object_build_id; } static void mm_base_sim_class_init (MMBaseSimClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); g_type_class_add_private (object_class, sizeof (MMBaseSimPrivate)); /* Virtual methods */ object_class->get_property = get_property; object_class->set_property = set_property; object_class->finalize = finalize; object_class->dispose = dispose; klass->load_sim_identifier = load_sim_identifier; klass->load_sim_identifier_finish = load_sim_identifier_finish; klass->load_imsi = load_imsi; klass->load_imsi_finish = load_imsi_finish; klass->load_operator_identifier = load_operator_identifier; klass->load_operator_identifier_finish = load_operator_identifier_finish; klass->load_operator_name = load_operator_name; klass->load_operator_name_finish = load_operator_name_finish; klass->load_emergency_numbers = load_emergency_numbers; klass->load_emergency_numbers_finish = load_emergency_numbers_finish; klass->load_preferred_networks = load_preferred_networks; klass->load_preferred_networks_finish = load_preferred_networks_finish; klass->load_gid1 = load_gid1; klass->load_gid1_finish = common_load_gid_finish; klass->load_gid2 = load_gid2; klass->load_gid2_finish = common_load_gid_finish; klass->set_preferred_networks = set_preferred_networks; klass->set_preferred_networks_finish = set_preferred_networks_finish; klass->send_pin = send_pin; klass->send_pin_finish = common_send_pin_puk_finish; klass->send_puk = send_puk; klass->send_puk_finish = common_send_pin_puk_finish; klass->enable_pin = enable_pin; klass->enable_pin_finish = enable_pin_finish; klass->change_pin = change_pin; klass->change_pin_finish = change_pin_finish; properties[PROP_CONNECTION] = g_param_spec_object (MM_BASE_SIM_CONNECTION, "Connection", "GDBus connection to the system bus.", G_TYPE_DBUS_CONNECTION, G_PARAM_READWRITE); g_object_class_install_property (object_class, PROP_CONNECTION, properties[PROP_CONNECTION]); properties[PROP_PATH] = g_param_spec_string (MM_BASE_SIM_PATH, "Path", "DBus path of the SIM", NULL, G_PARAM_READWRITE); g_object_class_install_property (object_class, PROP_PATH, properties[PROP_PATH]); properties[PROP_MODEM] = g_param_spec_object (MM_BASE_SIM_MODEM, "Modem", "The Modem which owns this SIM", MM_TYPE_BASE_MODEM, G_PARAM_READWRITE); g_object_class_install_property (object_class, PROP_MODEM, properties[PROP_MODEM]); properties[PROP_SLOT_NUMBER] = g_param_spec_uint (MM_BASE_SIM_SLOT_NUMBER, "Slot number", "The slot number where the SIM is inserted", 0, G_MAXUINT, 0, G_PARAM_READWRITE); g_object_class_install_property (object_class, PROP_SLOT_NUMBER, properties[PROP_SLOT_NUMBER]); /* Signals */ signals[SIGNAL_PIN_LOCK_ENABLED] = g_signal_new (MM_BASE_SIM_PIN_LOCK_ENABLED, G_OBJECT_CLASS_TYPE (object_class), G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (MMBaseSimClass, pin_lock_enabled), NULL, NULL, g_cclosure_marshal_generic, G_TYPE_NONE, 1, G_TYPE_BOOLEAN); } ModemManager-1.23.4-dev/src/mm-base-sim.h000066400000000000000000000331441456466623000200010ustar00rootroot00000000000000/* -*- 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: * * Author: Aleksander Morgado * * Copyright (C) 2011 Google, Inc. */ #ifndef MM_BASE_SIM_H #define MM_BASE_SIM_H #include #include #include #include "mm-base-modem.h" #define MM_TYPE_BASE_SIM (mm_base_sim_get_type ()) #define MM_BASE_SIM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_BASE_SIM, MMBaseSim)) #define MM_BASE_SIM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_BASE_SIM, MMBaseSimClass)) #define MM_IS_BASE_SIM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_BASE_SIM)) #define MM_IS_BASE_SIM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_BASE_SIM)) #define MM_BASE_SIM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_BASE_SIM, MMBaseSimClass)) typedef struct _MMBaseSim MMBaseSim; typedef struct _MMBaseSimClass MMBaseSimClass; typedef struct _MMBaseSimPrivate MMBaseSimPrivate; /* Properties */ #define MM_BASE_SIM_PATH "sim-path" #define MM_BASE_SIM_CONNECTION "sim-connection" #define MM_BASE_SIM_MODEM "sim-modem" #define MM_BASE_SIM_SLOT_NUMBER "sim-slot-number" /* Signals */ #define MM_BASE_SIM_PIN_LOCK_ENABLED "sim-pin-lock-enabled" struct _MMBaseSim { MmGdbusSimSkeleton parent; MMBaseSimPrivate *priv; }; struct _MMBaseSimClass { MmGdbusSimSkeletonClass parent; /* Wait SIM ready (async) */ void (* wait_sim_ready) (MMBaseSim *self, GAsyncReadyCallback callback, gpointer user_data); gboolean (* wait_sim_ready_finish) (MMBaseSim *self, GAsyncResult *res, GError **error); /* Load SIM identifier (async) */ void (* load_sim_identifier) (MMBaseSim *self, GAsyncReadyCallback callback, gpointer user_data); gchar * (* load_sim_identifier_finish) (MMBaseSim *self, GAsyncResult *res, GError **error); /* Load IMSI (async) */ void (* load_imsi) (MMBaseSim *self, GAsyncReadyCallback callback, gpointer user_data); gchar * (* load_imsi_finish) (MMBaseSim *self, GAsyncResult *res, GError **error); /* Load EID (async) */ void (* load_eid) (MMBaseSim *self, GAsyncReadyCallback callback, gpointer user_data); gchar * (* load_eid_finish) (MMBaseSim *self, GAsyncResult *res, GError **error); /* Load operator identifier (async) */ void (* load_operator_identifier) (MMBaseSim *self, GAsyncReadyCallback callback, gpointer user_data); gchar * (* load_operator_identifier_finish) (MMBaseSim *self, GAsyncResult *res, GError **error); /* Load operator name (async) */ void (* load_operator_name) (MMBaseSim *self, GAsyncReadyCallback callback, gpointer user_data); gchar * (* load_operator_name_finish) (MMBaseSim *self, GAsyncResult *res, GError **error); /* Load emergency numbers (async) */ void (* load_emergency_numbers) (MMBaseSim *self, GAsyncReadyCallback callback, gpointer user_data); GStrv (* load_emergency_numbers_finish) (MMBaseSim *self, GAsyncResult *res, GError **error); /* Load preferred networks (async) */ void (* load_preferred_networks) (MMBaseSim *self, GAsyncReadyCallback callback, gpointer user_data); GList * (* load_preferred_networks_finish) (MMBaseSim *self, GAsyncResult *res, GError **error); /* Load GID1 (async) */ void (* load_gid1) (MMBaseSim *self, GAsyncReadyCallback callback, gpointer user_data); GByteArray * (* load_gid1_finish) (MMBaseSim *self, GAsyncResult *res, GError **error); /* Load GID2 (async) */ void (* load_gid2) (MMBaseSim *self, GAsyncReadyCallback callback, gpointer user_data); GByteArray * (* load_gid2_finish) (MMBaseSim *self, GAsyncResult *res, GError **error); /* Load sim type (async) */ void (* load_sim_type) (MMBaseSim *self, GAsyncReadyCallback callback, gpointer user_data); MMSimType (* load_sim_type_finish) (MMBaseSim *self, GAsyncResult *res, GError **error); /* Load esim status (async) */ void (* load_esim_status) (MMBaseSim *self, GAsyncReadyCallback callback, gpointer user_data); MMSimEsimStatus (* load_esim_status_finish) (MMBaseSim *self, GAsyncResult *res, GError **error); /* Load removabilitu (async) */ void (* load_removability) (MMBaseSim *self, GAsyncReadyCallback callback, gpointer user_data); MMSimRemovability (* load_removability_finish) (MMBaseSim *self, GAsyncResult *res, GError **error); /* Change PIN (async) */ void (* change_pin) (MMBaseSim *self, const gchar *old_pin, const gchar *new_pin, GAsyncReadyCallback callback, gpointer user_data); gboolean (* change_pin_finish) (MMBaseSim *self, GAsyncResult *res, GError **error); /* Enable PIN (async) */ void (* enable_pin) (MMBaseSim *self, const gchar *pin, gboolean enabled, GAsyncReadyCallback callback, gpointer user_data); gboolean (* enable_pin_finish) (MMBaseSim *self, GAsyncResult *res, GError **error); /* Send PIN (async) */ void (* send_pin) (MMBaseSim *self, const gchar *pin, GAsyncReadyCallback callback, gpointer user_data); gboolean (* send_pin_finish) (MMBaseSim *self, GAsyncResult *res, GError **error); /* Send PUK (async) */ void (* send_puk) (MMBaseSim *self, const gchar *puk, const gchar *new_pin, GAsyncReadyCallback callback, gpointer user_data); gboolean (* send_puk_finish) (MMBaseSim *self, GAsyncResult *res, GError **error); /* Signals */ void (* pin_lock_enabled) (MMBaseSim *self, gboolean enabled); /* Set preferred networks (async) */ void (* set_preferred_networks) (MMBaseSim *self, GList *preferred_network_list, GAsyncReadyCallback callback, gpointer user_data); gboolean (* set_preferred_networks_finish) (MMBaseSim *self, GAsyncResult *res, GError **error); }; GType mm_base_sim_get_type (void); G_DEFINE_AUTOPTR_CLEANUP_FUNC (MMBaseSim, g_object_unref) void mm_base_sim_new (MMBaseModem *modem, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); MMBaseSim *mm_base_sim_new_finish (GAsyncResult *res, GError **error); void mm_base_sim_initialize (MMBaseSim *self, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); gboolean mm_base_sim_initialize_finish (MMBaseSim *self, GAsyncResult *result, GError **error); MMBaseSim *mm_base_sim_new_initialized (MMBaseModem *modem, guint slot_number, gboolean active, const gchar *sim_identifier, const gchar *imsi, const gchar *eid, const gchar *operator_identifier, const gchar *operator_name, const GStrv emergency_numbers); void mm_base_sim_send_pin (MMBaseSim *self, const gchar *pin, GAsyncReadyCallback callback, gpointer user_data); gboolean mm_base_sim_send_pin_finish (MMBaseSim *self, GAsyncResult *res, GError **error); void mm_base_sim_send_puk (MMBaseSim *self, const gchar *puk, const gchar *new_pin, GAsyncReadyCallback callback, gpointer user_data); gboolean mm_base_sim_send_puk_finish (MMBaseSim *self, GAsyncResult *res, GError **error); void mm_base_sim_export (MMBaseSim *self); const gchar *mm_base_sim_get_path (MMBaseSim *sim); guint mm_base_sim_get_slot_number (MMBaseSim *self); void mm_base_sim_load_sim_identifier (MMBaseSim *self, GAsyncReadyCallback callback, gpointer user_data); gchar *mm_base_sim_load_sim_identifier_finish (MMBaseSim *self, GAsyncResult *res, GError **error); gboolean mm_base_sim_is_emergency_number (MMBaseSim *self, const gchar *number); gboolean mm_base_sim_is_esim_without_profiles (MMBaseSim *self); #endif /* MM_BASE_SIM_H */ ModemManager-1.23.4-dev/src/mm-base-sms.c000066400000000000000000002147551456466623000200170ustar00rootroot00000000000000/* -*- 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 - 2012 Red Hat, Inc. * Copyright (C) 2012 Google, Inc. */ #include #include #include #include #include #include #include #define _LIBMM_INSIDE_MM #include #include "mm-base-sms.h" #include "mm-broadband-modem.h" #include "mm-iface-modem.h" #include "mm-iface-modem-messaging.h" #include "mm-sms-part-3gpp.h" #include "mm-base-modem-at.h" #include "mm-base-modem.h" #include "mm-log-object.h" #include "mm-modem-helpers.h" #include "mm-error-helpers.h" static void log_object_iface_init (MMLogObjectInterface *iface); G_DEFINE_TYPE_EXTENDED (MMBaseSms, mm_base_sms, MM_GDBUS_TYPE_SMS_SKELETON, 0, G_IMPLEMENT_INTERFACE (MM_TYPE_LOG_OBJECT, log_object_iface_init)) enum { PROP_0, PROP_PATH, PROP_CONNECTION, PROP_MODEM, PROP_IS_MULTIPART, PROP_MAX_PARTS, PROP_MULTIPART_REFERENCE, PROP_LAST }; static GParamSpec *properties[PROP_LAST]; struct _MMBaseSmsPrivate { /* The connection to the system bus */ GDBusConnection *connection; guint dbus_id; /* The modem which owns this SMS */ MMBaseModem *modem; /* The path where the SMS object is exported */ gchar *path; /* Multipart SMS specific stuff */ gboolean is_multipart; guint multipart_reference; /* List of SMS parts */ guint max_parts; GList *parts; /* Set to true when all needed parts were received, * parsed and assembled */ gboolean is_assembled; }; /*****************************************************************************/ static guint get_validity_relative (GVariant *tuple) { guint type; GVariant *value; guint value_integer = 0; if (!tuple) return 0; g_variant_get (tuple, "(uv)", &type, &value); if (type == MM_SMS_VALIDITY_TYPE_RELATIVE) value_integer = g_variant_get_uint32 (value); g_variant_unref (value); return value_integer; } static gboolean generate_3gpp_submit_pdus (MMBaseSms *self, GError **error) { guint i; guint n_parts; const gchar *text; GVariant *data_variant; const guint8 *data; gsize data_len = 0; MMSmsEncoding encoding; MMModemCharset charset; gchar **split_text = NULL; GByteArray **split_data = NULL; g_assert (self->priv->parts == NULL); text = mm_gdbus_sms_get_text (MM_GDBUS_SMS (self)); data_variant = mm_gdbus_sms_get_data (MM_GDBUS_SMS (self)); data = (data_variant ? g_variant_get_fixed_array (data_variant, &data_len, sizeof (guchar)) : NULL); g_assert (text != NULL || data != NULL); g_assert (!(text != NULL && data != NULL)); if (text) { split_text = mm_charset_util_split_text (text, &charset, self); if (!split_text) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS, "Cannot generate PDUs: Error processing input text"); return FALSE; } encoding = (charset == MM_MODEM_CHARSET_GSM) ? MM_SMS_ENCODING_GSM7 : MM_SMS_ENCODING_UCS2; n_parts = g_strv_length (split_text); } else if (data) { encoding = MM_SMS_ENCODING_8BIT; split_data = mm_sms_part_3gpp_util_split_data (data, data_len); g_assert (split_data != NULL); /* noop within the for */ for (n_parts = 0; split_data[n_parts]; n_parts++); } else g_assert_not_reached (); g_assert (split_text != NULL || split_data != NULL); g_assert (!(split_text != NULL && split_data != NULL)); if (n_parts > 255) { if (split_text) g_strfreev (split_text); else if (split_data) { i = 0; while (split_data[i]) g_byte_array_unref (split_data[i++]); g_free (split_data); } g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_TOO_MANY, "Cannot generate PDUs: Text or Data too long"); return FALSE; } /* Loop text/data chunks */ i = 0; while (1) { MMSmsPart *part; gchar *part_text = NULL; GByteArray *part_data = NULL; if (split_text) { if (!split_text[i]) break; part_text = split_text[i]; mm_obj_dbg (self, " processing chunk '%u' of text with '%u' bytes", i, (guint) strlen (part_text)); } else if (split_data) { if (!split_data[i]) break; part_data = split_data[i]; mm_obj_dbg (self, " processing chunk '%u' of data with '%u' bytes", i, part_data->len); } else g_assert_not_reached (); /* Create new part */ part = mm_sms_part_new (SMS_PART_INVALID_INDEX, MM_SMS_PDU_TYPE_SUBMIT); mm_sms_part_take_text (part, part_text); mm_sms_part_take_data (part, part_data); mm_sms_part_set_encoding (part, encoding); mm_sms_part_set_number (part, mm_gdbus_sms_get_number (MM_GDBUS_SMS (self))); mm_sms_part_set_smsc (part, mm_gdbus_sms_get_smsc (MM_GDBUS_SMS (self))); mm_sms_part_set_validity_relative (part, get_validity_relative (mm_gdbus_sms_get_validity (MM_GDBUS_SMS (self)))); mm_sms_part_set_class (part, mm_gdbus_sms_get_class (MM_GDBUS_SMS (self))); mm_sms_part_set_delivery_report_request (part, mm_gdbus_sms_get_delivery_report_request (MM_GDBUS_SMS (self))); if (n_parts > 1) { mm_sms_part_set_concat_reference (part, 0); /* We don't set a concat reference here */ mm_sms_part_set_concat_sequence (part, i + 1); mm_sms_part_set_concat_max (part, n_parts); mm_obj_dbg (self, "created SMS part '%u' for multipart SMS ('%u' parts expected)", i + 1, n_parts); } else { mm_obj_dbg (self, "created SMS part for singlepart SMS"); } /* Add to the list of parts */ self->priv->parts = g_list_append (self->priv->parts, part); i++; } /* Free array (not contents, which were taken for the part) */ g_free (split_text); g_free (split_data); /* Set additional multipart specific properties */ if (n_parts > 1) { self->priv->is_multipart = TRUE; self->priv->max_parts = n_parts; } /* No more parts are expected */ self->priv->is_assembled = TRUE; return TRUE; } static gboolean generate_cdma_submit_pdus (MMBaseSms *self, GError **error) { const gchar *text; GVariant *data_variant; const guint8 *data; gsize data_len = 0; MMSmsPart *part; g_assert (self->priv->parts == NULL); text = mm_gdbus_sms_get_text (MM_GDBUS_SMS (self)); data_variant = mm_gdbus_sms_get_data (MM_GDBUS_SMS (self)); data = (data_variant ? g_variant_get_fixed_array (data_variant, &data_len, sizeof (guchar)) : NULL); g_assert (text != NULL || data != NULL); g_assert (!(text != NULL && data != NULL)); /* Create new part */ part = mm_sms_part_new (SMS_PART_INVALID_INDEX, MM_SMS_PDU_TYPE_CDMA_SUBMIT); if (text) mm_sms_part_set_text (part, text); else if (data) { GByteArray *part_data; part_data = g_byte_array_sized_new (data_len); g_byte_array_append (part_data, data, data_len); mm_sms_part_take_data (part, part_data); } else g_assert_not_reached (); mm_sms_part_set_encoding (part, data ? MM_SMS_ENCODING_8BIT : MM_SMS_ENCODING_UNKNOWN); mm_sms_part_set_number (part, mm_gdbus_sms_get_number (MM_GDBUS_SMS (self))); /* If creating a CDMA SMS part but we don't have a Teleservice ID, we default to WMT */ if (mm_gdbus_sms_get_teleservice_id (MM_GDBUS_SMS (self)) == MM_SMS_CDMA_TELESERVICE_ID_UNKNOWN) { mm_obj_dbg (self, "defaulting to WMT teleservice ID when creating SMS part"); mm_sms_part_set_cdma_teleservice_id (part, MM_SMS_CDMA_TELESERVICE_ID_WMT); } else mm_sms_part_set_cdma_teleservice_id (part, mm_gdbus_sms_get_teleservice_id (MM_GDBUS_SMS (self))); mm_sms_part_set_cdma_service_category (part, mm_gdbus_sms_get_service_category (MM_GDBUS_SMS (self))); mm_obj_dbg (self, "created SMS part for CDMA SMS"); /* Add to the list of parts */ self->priv->parts = g_list_append (self->priv->parts, part); /* No more parts are expected */ self->priv->is_assembled = TRUE; return TRUE; } static gboolean generate_submit_pdus (MMBaseSms *self, GError **error) { MMBaseModem *modem; gboolean is_3gpp; /* First; decide which kind of PDU we'll generate, based on the current modem caps */ g_object_get (self, MM_BASE_SMS_MODEM, &modem, NULL); g_assert (modem != NULL); is_3gpp = mm_iface_modem_is_3gpp (MM_IFACE_MODEM (modem)); g_object_unref (modem); /* On a 3GPP-capable modem, create always a 3GPP SMS (even if the modem is 3GPP+3GPP2) */ if (is_3gpp) return generate_3gpp_submit_pdus (self, error); /* Otherwise, create a 3GPP2 SMS */ return generate_cdma_submit_pdus (self, error); } /*****************************************************************************/ /* Store SMS (DBus call handling) */ typedef struct { MMBaseSms *self; MMBaseModem *modem; GDBusMethodInvocation *invocation; MMSmsStorage storage; } HandleStoreContext; static void handle_store_context_free (HandleStoreContext *ctx) { g_object_unref (ctx->invocation); g_object_unref (ctx->modem); g_object_unref (ctx->self); g_slice_free (HandleStoreContext, ctx); } static void handle_store_ready (MMBaseSms *self, GAsyncResult *res, HandleStoreContext *ctx) { GError *error = NULL; if (!MM_BASE_SMS_GET_CLASS (self)->store_finish (self, res, &error)) { mm_obj_warn (self, "failed storing SMS message: %s", error->message); mm_dbus_method_invocation_take_error (ctx->invocation, error); handle_store_context_free (ctx); return; } mm_gdbus_sms_set_storage (MM_GDBUS_SMS (ctx->self), ctx->storage); /* Transition from Unknown->Stored for SMS which were created by the user */ if (mm_gdbus_sms_get_state (MM_GDBUS_SMS (ctx->self)) == MM_SMS_STATE_UNKNOWN) mm_gdbus_sms_set_state (MM_GDBUS_SMS (ctx->self), MM_SMS_STATE_STORED); mm_obj_info (self, "stored SMS message"); mm_gdbus_sms_complete_store (MM_GDBUS_SMS (ctx->self), ctx->invocation); handle_store_context_free (ctx); } static gboolean prepare_sms_to_be_stored (MMBaseSms *self, GError **error) { /* Create parts if we did not create them already before (e.g. when * sending) */ if (!self->priv->parts && !generate_submit_pdus (self, error)) { g_prefix_error (error, "Cannot create submit PDUs: "); return FALSE; } /* If the message is a multipart message, we need to set a proper * multipart reference. When sending a message which wasn't stored * yet, we chose a random multipart reference, but that doesn't work * when storing locally, as we could collide with the references used * in other existing messages. */ if (self->priv->is_multipart) { GList *l; guint8 reference; /* Look for a valid multipart reference to use. When storing, we need to * check whether we have already stored multipart SMS with the same * reference and destination number */ reference = mm_iface_modem_messaging_get_local_multipart_reference ( MM_IFACE_MODEM_MESSAGING (self->priv->modem), mm_gdbus_sms_get_number (MM_GDBUS_SMS (self)), error); if (!reference) { g_prefix_error (error, "Cannot get local multipart reference: "); return FALSE; } self->priv->multipart_reference = reference; for (l = self->priv->parts; l; l = g_list_next (l)) { mm_sms_part_set_concat_reference ((MMSmsPart *)l->data, self->priv->multipart_reference); } } return TRUE; } static void handle_store_auth_ready (MMBaseModem *modem, GAsyncResult *res, HandleStoreContext *ctx) { GError *error = NULL; if (!mm_base_modem_authorize_finish (modem, res, &error)) { mm_dbus_method_invocation_take_error (ctx->invocation, error); handle_store_context_free (ctx); return; } mm_obj_info (ctx->self, "processing user request to store SMS message in storage '%s'...", mm_sms_storage_get_string (ctx->storage)); /* First of all, check if we already have the SMS stored. */ if (mm_base_sms_get_storage (ctx->self) != MM_SMS_STORAGE_UNKNOWN) { /* Check if SMS stored in some other storage */ if (mm_base_sms_get_storage (ctx->self) == ctx->storage) { /* Good, same storage */ mm_obj_info (ctx->self, "SMS message already stored"); mm_gdbus_sms_complete_store (MM_GDBUS_SMS (ctx->self), ctx->invocation); } else { error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "SMS is already stored in storage '%s', cannot store it in storage '%s'", mm_sms_storage_get_string (mm_base_sms_get_storage (ctx->self)), mm_sms_storage_get_string (ctx->storage)); mm_obj_warn (ctx->self, "failed storing SMS message: %s", error->message); mm_dbus_method_invocation_take_error (ctx->invocation, error); } handle_store_context_free (ctx); return; } /* Check if the requested storage is allowed for storing */ if (!mm_iface_modem_messaging_is_storage_supported_for_storing (MM_IFACE_MODEM_MESSAGING (ctx->modem), ctx->storage, &error)) { mm_obj_warn (ctx->self, "failed storing SMS message: %s", error->message); mm_dbus_method_invocation_take_error (ctx->invocation, error); handle_store_context_free (ctx); return; } /* Prepare the SMS to be stored, creating the PDU list if required */ if (!prepare_sms_to_be_stored (ctx->self, &error)) { mm_obj_warn (ctx->self, "failed preparing SMS message to be stored: %s", error->message); mm_dbus_method_invocation_take_error (ctx->invocation, error); handle_store_context_free (ctx); return; } /* If not stored, check if we do support doing it */ if (!MM_BASE_SMS_GET_CLASS (ctx->self)->store || !MM_BASE_SMS_GET_CLASS (ctx->self)->store_finish) { mm_obj_warn (ctx->self, "failed storing SMS message: unsupported"); mm_dbus_method_invocation_return_error_literal (ctx->invocation, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "Storing SMS is not supported by this modem"); handle_store_context_free (ctx); return; } MM_BASE_SMS_GET_CLASS (ctx->self)->store (ctx->self, ctx->storage, (GAsyncReadyCallback)handle_store_ready, ctx); } static gboolean handle_store (MMBaseSms *self, GDBusMethodInvocation *invocation, guint32 storage) { HandleStoreContext *ctx; ctx = g_slice_new0 (HandleStoreContext); ctx->self = g_object_ref (self); ctx->invocation = g_object_ref (invocation); g_object_get (self, MM_BASE_SMS_MODEM, &ctx->modem, NULL); ctx->storage = (MMSmsStorage)storage; if (ctx->storage == MM_SMS_STORAGE_UNKNOWN) { /* We'll set now the proper storage, taken from the default mem2 one */ g_object_get (self->priv->modem, MM_IFACE_MODEM_MESSAGING_SMS_DEFAULT_STORAGE, &ctx->storage, NULL); g_assert (ctx->storage != MM_SMS_STORAGE_UNKNOWN); } mm_base_modem_authorize (ctx->modem, invocation, MM_AUTHORIZATION_MESSAGING, (GAsyncReadyCallback)handle_store_auth_ready, ctx); return TRUE; } /*****************************************************************************/ /* Send SMS (DBus call handling) */ typedef struct { MMBaseSms *self; MMBaseModem *modem; GDBusMethodInvocation *invocation; } HandleSendContext; static void handle_send_context_free (HandleSendContext *ctx) { g_object_unref (ctx->invocation); g_object_unref (ctx->modem); g_object_unref (ctx->self); g_slice_free (HandleSendContext, ctx); } static void handle_send_ready (MMBaseSms *self, GAsyncResult *res, HandleSendContext *ctx) { GError *error = NULL; if (!MM_BASE_SMS_GET_CLASS (self)->send_finish (self, res, &error)) { mm_obj_warn (self, "failed sending SMS message: %s", error->message); mm_dbus_method_invocation_take_error (ctx->invocation, error); handle_send_context_free (ctx); return; } /* Transition from Unknown->Sent or Stored->Sent */ if (mm_gdbus_sms_get_state (MM_GDBUS_SMS (ctx->self)) == MM_SMS_STATE_UNKNOWN || mm_gdbus_sms_get_state (MM_GDBUS_SMS (ctx->self)) == MM_SMS_STATE_STORED) { GList *l; /* Update state */ mm_gdbus_sms_set_state (MM_GDBUS_SMS (ctx->self), MM_SMS_STATE_SENT); /* Grab last message reference */ l = g_list_last (mm_base_sms_get_parts (ctx->self)); mm_gdbus_sms_set_message_reference (MM_GDBUS_SMS (ctx->self), mm_sms_part_get_message_reference ((MMSmsPart *)l->data)); } mm_obj_info (self, "sent SMS message"); mm_gdbus_sms_complete_send (MM_GDBUS_SMS (ctx->self), ctx->invocation); handle_send_context_free (ctx); } static gboolean prepare_sms_to_be_sent (MMBaseSms *self, GError **error) { GList *l; /* If we created the parts when storing, we're fine already */ if (self->priv->parts) return TRUE; if (!generate_submit_pdus (self, error)) { g_prefix_error (error, "Cannot create submit PDUs: "); return FALSE; } /* If the message is a multipart message, we need to set a proper * multipart reference. When sending a message which wasn't stored * yet, we can just get a random multipart reference. */ if (self->priv->is_multipart) { self->priv->multipart_reference = g_random_int_range (1,255); for (l = self->priv->parts; l; l = g_list_next (l)) { mm_sms_part_set_concat_reference ((MMSmsPart *)l->data, self->priv->multipart_reference); } } return TRUE; } static void handle_send_auth_ready (MMBaseModem *modem, GAsyncResult *res, HandleSendContext *ctx) { MMSmsState state; GError *error = NULL; if (!mm_base_modem_authorize_finish (modem, res, &error)) { mm_dbus_method_invocation_take_error (ctx->invocation, error); handle_send_context_free (ctx); return; } /* We can only send SMS created by the user */ state = mm_gdbus_sms_get_state (MM_GDBUS_SMS (ctx->self)); if (state == MM_SMS_STATE_RECEIVED || state == MM_SMS_STATE_RECEIVING) { mm_dbus_method_invocation_return_error_literal (ctx->invocation, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "This SMS was received, cannot send it"); handle_send_context_free (ctx); return; } /* Don't allow sending the same SMS multiple times, we would lose the message reference */ if (state == MM_SMS_STATE_SENT) { mm_dbus_method_invocation_return_error_literal (ctx->invocation, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "This SMS was already sent, cannot send it again"); handle_send_context_free (ctx); return; } mm_obj_info (ctx->self, "processing user request to send SMS message..."); /* Prepare the SMS to be sent, creating the PDU list if required */ if (!prepare_sms_to_be_sent (ctx->self, &error)) { mm_obj_warn (ctx->self, "failed preparing SMS message to be sent: %s", error->message); mm_dbus_method_invocation_take_error (ctx->invocation, error); handle_send_context_free (ctx); return; } /* Check if we do support doing it */ if (!MM_BASE_SMS_GET_CLASS (ctx->self)->send || !MM_BASE_SMS_GET_CLASS (ctx->self)->send_finish) { mm_obj_warn (ctx->self, "failed sending SMS message: unsupported"); mm_dbus_method_invocation_return_error_literal (ctx->invocation, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "Sending SMS is not supported by this modem"); handle_send_context_free (ctx); return; } MM_BASE_SMS_GET_CLASS (ctx->self)->send (ctx->self, (GAsyncReadyCallback)handle_send_ready, ctx); } static gboolean handle_send (MMBaseSms *self, GDBusMethodInvocation *invocation) { HandleSendContext *ctx; ctx = g_slice_new0 (HandleSendContext); ctx->self = g_object_ref (self); ctx->invocation = g_object_ref (invocation); g_object_get (self, MM_BASE_SMS_MODEM, &ctx->modem, NULL); mm_base_modem_authorize (ctx->modem, invocation, MM_AUTHORIZATION_MESSAGING, (GAsyncReadyCallback)handle_send_auth_ready, ctx); return TRUE; } /*****************************************************************************/ void mm_base_sms_export (MMBaseSms *self) { gchar *path; path = g_strdup_printf (MM_DBUS_SMS_PREFIX "/%d", self->priv->dbus_id); g_object_set (self, MM_BASE_SMS_PATH, path, NULL); g_free (path); } void mm_base_sms_unexport (MMBaseSms *self) { g_object_set (self, MM_BASE_SMS_PATH, NULL, NULL); } /*****************************************************************************/ static void sms_dbus_export (MMBaseSms *self) { GError *error = NULL; /* Handle method invocations */ g_signal_connect (self, "handle-store", G_CALLBACK (handle_store), NULL); g_signal_connect (self, "handle-send", G_CALLBACK (handle_send), NULL); if (!g_dbus_interface_skeleton_export (G_DBUS_INTERFACE_SKELETON (self), self->priv->connection, self->priv->path, &error)) { mm_obj_warn (self, "couldn't export SMS: %s", error->message); g_error_free (error); } } static void sms_dbus_unexport (MMBaseSms *self) { /* Only unexport if currently exported */ if (g_dbus_interface_skeleton_get_object_path (G_DBUS_INTERFACE_SKELETON (self))) g_dbus_interface_skeleton_unexport (G_DBUS_INTERFACE_SKELETON (self)); } /*****************************************************************************/ const gchar * mm_base_sms_get_path (MMBaseSms *self) { return self->priv->path; } MMSmsStorage mm_base_sms_get_storage (MMBaseSms *self) { return mm_gdbus_sms_get_storage (MM_GDBUS_SMS (self)); } gboolean mm_base_sms_is_multipart (MMBaseSms *self) { return self->priv->is_multipart; } guint mm_base_sms_get_multipart_reference (MMBaseSms *self) { g_return_val_if_fail (self->priv->is_multipart, 0); return self->priv->multipart_reference; } gboolean mm_base_sms_multipart_is_complete (MMBaseSms *self) { return (g_list_length (self->priv->parts) == self->priv->max_parts); } gboolean mm_base_sms_multipart_is_assembled (MMBaseSms *self) { return self->priv->is_assembled; } /*****************************************************************************/ static guint cmp_sms_part_index (MMSmsPart *part, gpointer user_data) { return (GPOINTER_TO_UINT (user_data) - mm_sms_part_get_index (part)); } gboolean mm_base_sms_has_part_index (MMBaseSms *self, guint index) { return !!g_list_find_custom (self->priv->parts, GUINT_TO_POINTER (index), (GCompareFunc)cmp_sms_part_index); } GList * mm_base_sms_get_parts (MMBaseSms *self) { return self->priv->parts; } /*****************************************************************************/ static gboolean sms_get_store_or_send_command (MMBaseSms *self, MMSmsPart *part, gboolean text_or_pdu, /* TRUE for PDU */ gboolean store_or_send, /* TRUE for send */ gchar **out_cmd, gchar **out_msg_data, GError **error) { g_assert (out_cmd != NULL); g_assert (out_msg_data != NULL); if (!text_or_pdu) { /* Text mode */ *out_cmd = g_strdup_printf ("+CMG%c=\"%s\"", store_or_send ? 'S' : 'W', mm_sms_part_get_number (part)); *out_msg_data = g_strdup_printf ("%s\x1a", mm_sms_part_get_text (part)); } else { guint8 *pdu; guint pdulen = 0; guint msgstart = 0; gchar *hex; /* AT+CMGW=[, ] PDU can be entered. / */ pdu = mm_sms_part_3gpp_get_submit_pdu (part, &pdulen, &msgstart, self, error); if (!pdu) /* 'error' should already be set */ return FALSE; /* Convert PDU to hex */ hex = mm_utils_bin2hexstr (pdu, pdulen); g_free (pdu); if (!hex) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Not enough memory to send SMS PDU"); return FALSE; } /* CMGW/S length is the size of the PDU without SMSC information */ *out_cmd = g_strdup_printf ("+CMG%c=%d", store_or_send ? 'S' : 'W', pdulen - msgstart); *out_msg_data = g_strdup_printf ("%s\x1a", hex); g_free (hex); } return TRUE; } /*****************************************************************************/ /* Store the SMS */ typedef struct { MMBaseModem *modem; MMSmsStorage storage; gboolean need_unlock; gboolean use_pdu_mode; GList *current; gchar *msg_data; } SmsStoreContext; static void sms_store_context_free (SmsStoreContext *ctx) { /* Unlock mem2 storage if we had the lock */ if (ctx->need_unlock) mm_broadband_modem_unlock_sms_storages (MM_BROADBAND_MODEM (ctx->modem), FALSE, TRUE); g_object_unref (ctx->modem); g_free (ctx->msg_data); g_free (ctx); } static gboolean sms_store_finish (MMBaseSms *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void sms_store_next_part (GTask *task); static void store_msg_data_ready (MMBaseModem *modem, GAsyncResult *res, GTask *task) { SmsStoreContext *ctx; const gchar *response; GError *error = NULL; gint rv; gint idx; response = mm_base_modem_at_command_finish (modem, res, &error); if (error) { g_task_return_error (task, error); g_object_unref (task); return; } /* Read the new part index from the reply */ rv = sscanf (response, "+CMGW: %d", &idx); if (rv != 1 || idx < 0) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't read index of already stored part: " "%d fields parsed", rv); g_object_unref (task); return; } ctx = g_task_get_task_data (task); /* Set the index in the part we hold */ mm_sms_part_set_index ((MMSmsPart *)ctx->current->data, (guint)idx); ctx->current = g_list_next (ctx->current); sms_store_next_part (task); } static void store_ready (MMBaseModem *modem, GAsyncResult *res, GTask *task) { SmsStoreContext *ctx; GError *error = NULL; mm_base_modem_at_command_finish (modem, res, &error); if (error) { g_task_return_error (task, error); g_object_unref (task); return; } ctx = g_task_get_task_data (task); /* Send the actual message data. * We send the data as 'raw' data because we do NOT want it to * be treated as an AT command (i.e. we don't want it prefixed * with AT+ and suffixed with ), plus, we want it to be * sent right away (not queued after other AT commands). */ mm_base_modem_at_command_raw (ctx->modem, ctx->msg_data, 10, FALSE, (GAsyncReadyCallback)store_msg_data_ready, task); } static void sms_store_next_part (GTask *task) { MMBaseSms *self; SmsStoreContext *ctx; gchar *cmd; GError *error = NULL; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); if (!ctx->current) { /* Done we are */ g_task_return_boolean (task, TRUE); g_object_unref (task); return; } g_clear_pointer (&ctx->msg_data, g_free); if (!sms_get_store_or_send_command (self, (MMSmsPart *)ctx->current->data, ctx->use_pdu_mode, FALSE, &cmd, &ctx->msg_data, &error)) { g_task_return_error (task, error); g_object_unref (task); return; } g_assert (cmd != NULL); g_assert (ctx->msg_data != NULL); mm_base_modem_at_command (ctx->modem, cmd, 10, FALSE, (GAsyncReadyCallback)store_ready, task); g_free (cmd); } static void store_lock_sms_storages_ready (MMBroadbandModem *modem, GAsyncResult *res, GTask *task) { MMBaseSms *self; SmsStoreContext *ctx; GError *error = NULL; if (!mm_broadband_modem_lock_sms_storages_finish (modem, res, &error)) { g_task_return_error (task, error); g_object_unref (task); return; } self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); /* We are now locked. Whatever result we have here, we need to make sure * we unlock the storages before finishing. */ ctx->need_unlock = TRUE; /* Go on to store the parts */ ctx->current = self->priv->parts; sms_store_next_part (task); } static void sms_store (MMBaseSms *self, MMSmsStorage storage, GAsyncReadyCallback callback, gpointer user_data) { SmsStoreContext *ctx; GTask *task; /* Setup the context */ ctx = g_new0 (SmsStoreContext, 1); ctx->modem = g_object_ref (self->priv->modem); ctx->storage = storage; /* Different ways to do it if on PDU or text mode */ g_object_get (self->priv->modem, MM_IFACE_MODEM_MESSAGING_SMS_PDU_MODE, &ctx->use_pdu_mode, NULL); task = g_task_new (self, NULL, callback, user_data); g_task_set_task_data (task, ctx, (GDestroyNotify)sms_store_context_free); /* First, lock storage to use */ g_assert (MM_IS_BROADBAND_MODEM (self->priv->modem)); mm_broadband_modem_lock_sms_storages ( MM_BROADBAND_MODEM (self->priv->modem), MM_SMS_STORAGE_UNKNOWN, /* none required for mem1 */ ctx->storage, (GAsyncReadyCallback)store_lock_sms_storages_ready, task); } /*****************************************************************************/ /* Send the SMS */ typedef struct { MMBaseModem *modem; gboolean need_unlock; gboolean from_storage; gboolean use_pdu_mode; GList *current; gchar *msg_data; } SmsSendContext; static void sms_send_context_free (SmsSendContext *ctx) { /* Unlock mem2 storage if we had the lock */ if (ctx->need_unlock) mm_broadband_modem_unlock_sms_storages (MM_BROADBAND_MODEM (ctx->modem), FALSE, TRUE); g_object_unref (ctx->modem); g_free (ctx->msg_data); g_free (ctx); } static gboolean sms_send_finish (MMBaseSms *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void sms_send_next_part (GTask *task); static gint read_message_reference_from_reply (const gchar *response, GError **error) { gint rv = 0; gint idx = -1; if (strstr (response, "+CMGS")) rv = sscanf (strstr (response, "+CMGS"), "+CMGS: %d", &idx); else if (strstr (response, "+CMSS")) rv = sscanf (strstr (response, "+CMSS"), "+CMSS: %d", &idx); if (rv != 1 || idx < 0) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't read message reference: " "%d fields parsed from response '%s'", rv, response); return -1; } return idx; } static void send_generic_msg_data_ready (MMBaseModem *modem, GAsyncResult *res, GTask *task) { SmsSendContext *ctx; GError *error = NULL; const gchar *response; gint message_reference; response = mm_base_modem_at_command_finish (modem, res, &error); if (error) { g_task_return_error (task, error); g_object_unref (task); return; } message_reference = read_message_reference_from_reply (response, &error); if (error) { g_task_return_error (task, error); g_object_unref (task); return; } ctx = g_task_get_task_data (task); mm_sms_part_set_message_reference ((MMSmsPart *)ctx->current->data, (guint)message_reference); ctx->current = g_list_next (ctx->current); sms_send_next_part (task); } static void send_generic_ready (MMBaseModem *modem, GAsyncResult *res, GTask *task) { SmsSendContext *ctx; GError *error = NULL; mm_base_modem_at_command_finish (modem, res, &error); if (error) { g_task_return_error (task, error); g_object_unref (task); return; } ctx = g_task_get_task_data (task); /* Send the actual message data. * We send the data as 'raw' data because we do NOT want it to * be treated as an AT command (i.e. we don't want it prefixed * with AT+ and suffixed with ), plus, we want it to be * sent right away (not queued after other AT commands). */ mm_base_modem_at_command_raw (ctx->modem, ctx->msg_data, MM_BASE_SMS_DEFAULT_SEND_TIMEOUT, FALSE, (GAsyncReadyCallback)send_generic_msg_data_ready, task); } static void send_from_storage_ready (MMBaseModem *modem, GAsyncResult *res, GTask *task) { MMBaseSms *self; SmsSendContext *ctx; GError *error = NULL; const gchar *response; gint message_reference; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); response = mm_base_modem_at_command_finish (modem, res, &error); if (error) { if (g_error_matches (error, MM_SERIAL_ERROR, MM_SERIAL_ERROR_RESPONSE_TIMEOUT)) { g_task_return_error (task, error); g_object_unref (task); return; } mm_obj_dbg (self, "couldn't send SMS from storage: %s; trying generic send...", error->message); g_error_free (error); ctx->from_storage = FALSE; sms_send_next_part (task); return; } message_reference = read_message_reference_from_reply (response, &error); if (error) { g_task_return_error (task, error); g_object_unref (task); return; } mm_sms_part_set_message_reference ((MMSmsPart *)ctx->current->data, (guint)message_reference); ctx->current = g_list_next (ctx->current); sms_send_next_part (task); } static void sms_send_next_part (GTask *task) { MMBaseSms *self; SmsSendContext *ctx; GError *error = NULL; gchar *cmd; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); if (!ctx->current) { /* Done we are */ g_task_return_boolean (task, TRUE); g_object_unref (task); return; } /* Send from storage */ if (ctx->from_storage) { cmd = g_strdup_printf ("+CMSS=%d", mm_sms_part_get_index ((MMSmsPart *)ctx->current->data)); mm_base_modem_at_command (ctx->modem, cmd, MM_BASE_SMS_DEFAULT_SEND_TIMEOUT, FALSE, (GAsyncReadyCallback)send_from_storage_ready, task); g_free (cmd); return; } /* Generic send */ g_clear_pointer (&ctx->msg_data, g_free); if (!sms_get_store_or_send_command (self, (MMSmsPart *)ctx->current->data, ctx->use_pdu_mode, TRUE, &cmd, &ctx->msg_data, &error)) { g_task_return_error (task, error); g_object_unref (task); return; } g_assert (cmd != NULL); g_assert (ctx->msg_data != NULL); /* no network involved in this initial AT command, so lower timeout */ mm_base_modem_at_command (ctx->modem, cmd, 10, FALSE, (GAsyncReadyCallback)send_generic_ready, task); g_free (cmd); } static void send_lock_sms_storages_ready (MMBroadbandModem *modem, GAsyncResult *res, GTask *task) { MMBaseSms *self; SmsSendContext *ctx; GError *error = NULL; if (!mm_broadband_modem_lock_sms_storages_finish (modem, res, &error)) { g_task_return_error (task, error); g_object_unref (task); return; } self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); /* We are now locked. Whatever result we have here, we need to make sure * we unlock the storages before finishing. */ ctx->need_unlock = TRUE; /* Go on to send the parts */ ctx->current = self->priv->parts; sms_send_next_part (task); } static void sms_send (MMBaseSms *self, GAsyncReadyCallback callback, gpointer user_data) { SmsSendContext *ctx; GTask *task; /* Setup the context */ ctx = g_new0 (SmsSendContext, 1); ctx->modem = g_object_ref (self->priv->modem); task = g_task_new (self, NULL, callback, user_data); g_task_set_task_data (task, ctx, (GDestroyNotify)sms_send_context_free); /* If the SMS is STORED, try to send from storage */ ctx->from_storage = (mm_base_sms_get_storage (self) != MM_SMS_STORAGE_UNKNOWN); if (ctx->from_storage) { /* When sending from storage, first lock storage to use */ g_assert (MM_IS_BROADBAND_MODEM (self->priv->modem)); mm_broadband_modem_lock_sms_storages ( MM_BROADBAND_MODEM (self->priv->modem), MM_SMS_STORAGE_UNKNOWN, /* none required for mem1 */ mm_base_sms_get_storage (self), (GAsyncReadyCallback)send_lock_sms_storages_ready, task); return; } /* Different ways to do it if on PDU or text mode */ g_object_get (self->priv->modem, MM_IFACE_MODEM_MESSAGING_SMS_PDU_MODE, &ctx->use_pdu_mode, NULL); ctx->current = self->priv->parts; sms_send_next_part (task); } /*****************************************************************************/ typedef struct { MMBaseModem *modem; gboolean need_unlock; GList *current; guint n_failed; } SmsDeletePartsContext; static void sms_delete_parts_context_free (SmsDeletePartsContext *ctx) { /* Unlock mem1 storage if we had the lock */ if (ctx->need_unlock) mm_broadband_modem_unlock_sms_storages (MM_BROADBAND_MODEM (ctx->modem), TRUE, FALSE); g_object_unref (ctx->modem); g_free (ctx); } static gboolean sms_delete_finish (MMBaseSms *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void delete_next_part (GTask *task); static void delete_part_ready (MMBaseModem *modem, GAsyncResult *res, GTask *task) { MMBaseSms *self; SmsDeletePartsContext *ctx; GError *error = NULL; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); mm_base_modem_at_command_finish (modem, res, &error); if (error) { ctx->n_failed++; mm_obj_dbg (self, "couldn't delete SMS part with index %u: %s", mm_sms_part_get_index ((MMSmsPart *)ctx->current->data), error->message); g_error_free (error); } /* We reset the index, as there is no longer that part */ mm_sms_part_set_index ((MMSmsPart *)ctx->current->data, SMS_PART_INVALID_INDEX); ctx->current = g_list_next (ctx->current); delete_next_part (task); } static void delete_next_part (GTask *task) { SmsDeletePartsContext *ctx; gchar *cmd; ctx = g_task_get_task_data (task); /* Skip non-stored parts */ while (ctx->current && mm_sms_part_get_index ((MMSmsPart *)ctx->current->data) == SMS_PART_INVALID_INDEX) ctx->current = g_list_next (ctx->current); /* If all removed, we're done */ if (!ctx->current) { if (ctx->n_failed > 0) g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't delete %u parts from this SMS", ctx->n_failed); else g_task_return_boolean (task, TRUE); g_object_unref (task); return; } cmd = g_strdup_printf ("+CMGD=%d", mm_sms_part_get_index ((MMSmsPart *)ctx->current->data)); mm_base_modem_at_command (ctx->modem, cmd, 10, FALSE, (GAsyncReadyCallback)delete_part_ready, task); g_free (cmd); } static void delete_lock_sms_storages_ready (MMBroadbandModem *modem, GAsyncResult *res, GTask *task) { MMBaseSms *self; SmsDeletePartsContext *ctx; GError *error = NULL; if (!mm_broadband_modem_lock_sms_storages_finish (modem, res, &error)) { g_task_return_error (task, error); g_object_unref (task); return; } self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); /* We are now locked. Whatever result we have here, we need to make sure * we unlock the storages before finishing. */ ctx->need_unlock = TRUE; /* Go on deleting parts */ ctx->current = self->priv->parts; delete_next_part (task); } static void sms_delete (MMBaseSms *self, GAsyncReadyCallback callback, gpointer user_data) { SmsDeletePartsContext *ctx; GTask *task; ctx = g_new0 (SmsDeletePartsContext, 1); ctx->modem = g_object_ref (self->priv->modem); task = g_task_new (self, NULL, callback, user_data); g_task_set_task_data (task, ctx, (GDestroyNotify)sms_delete_parts_context_free); if (mm_base_sms_get_storage (self) == MM_SMS_STORAGE_UNKNOWN) { mm_obj_dbg (self, "not removing parts from non-stored SMS"); g_task_return_boolean (task, TRUE); g_object_unref (task); return; } /* Select specific storage to delete from */ mm_broadband_modem_lock_sms_storages ( MM_BROADBAND_MODEM (self->priv->modem), mm_base_sms_get_storage (self), MM_SMS_STORAGE_UNKNOWN, /* none required for mem2 */ (GAsyncReadyCallback)delete_lock_sms_storages_ready, task); } /*****************************************************************************/ gboolean mm_base_sms_delete_finish (MMBaseSms *self, GAsyncResult *res, GError **error) { if (MM_BASE_SMS_GET_CLASS (self)->delete_finish) { gboolean deleted; deleted = MM_BASE_SMS_GET_CLASS (self)->delete_finish (self, res, error); if (deleted) /* We do change the state of this SMS back to UNKNOWN, as it is no * longer stored in the device */ mm_gdbus_sms_set_state (MM_GDBUS_SMS (self), MM_SMS_STATE_UNKNOWN); return deleted; } return g_task_propagate_boolean (G_TASK (res), error); } void mm_base_sms_delete (MMBaseSms *self, GAsyncReadyCallback callback, gpointer user_data) { if (MM_BASE_SMS_GET_CLASS (self)->delete && MM_BASE_SMS_GET_CLASS (self)->delete_finish) { MM_BASE_SMS_GET_CLASS (self)->delete (self, callback, user_data); return; } g_task_report_new_error (self, callback, user_data, mm_base_sms_delete, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "Deleting SMS is not supported by this modem"); } /*****************************************************************************/ static void initialize_sms (MMBaseSms *self) { MMSmsPart *part; guint validity_relative; /* Some of the fields of the SMS object may be initialized as soon as we have * one part already available, even if it's not exactly the first one */ g_assert (self->priv->parts); part = (MMSmsPart *)(self->priv->parts->data); /* Prepare for validity tuple */ validity_relative = mm_sms_part_get_validity_relative (part); g_object_set (self, "pdu-type", mm_sms_part_get_pdu_type (part), "smsc", mm_sms_part_get_smsc (part), "class", mm_sms_part_get_class (part), "teleservice-id", mm_sms_part_get_cdma_teleservice_id (part), "service-category", mm_sms_part_get_cdma_service_category (part), "number", mm_sms_part_get_number (part), "validity", (validity_relative ? g_variant_new ("(uv)", MM_SMS_VALIDITY_TYPE_RELATIVE, g_variant_new_uint32 (validity_relative)) : g_variant_new ("(uv)", MM_SMS_VALIDITY_TYPE_UNKNOWN, g_variant_new_boolean (FALSE))), "timestamp", mm_sms_part_get_timestamp (part), "discharge-timestamp", mm_sms_part_get_discharge_timestamp (part), "delivery-state", mm_sms_part_get_delivery_state (part), NULL); } static gboolean assemble_sms (MMBaseSms *self, GError **error) { GList *l; guint idx; MMSmsPart **sorted_parts; GString *fulltext; GByteArray *fulldata; sorted_parts = g_new0 (MMSmsPart *, self->priv->max_parts); /* Note that sequence in multipart messages start with '1', while singlepart * messages have '0' as sequence. */ if (self->priv->max_parts == 1) { if (g_list_length (self->priv->parts) != 1) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Single part message with multiple parts (%u) found", g_list_length (self->priv->parts)); g_free (sorted_parts); return FALSE; } sorted_parts[0] = (MMSmsPart *)self->priv->parts->data; } else { /* Check if we have duplicate parts */ for (l = self->priv->parts; l; l = g_list_next (l)) { idx = mm_sms_part_get_concat_sequence ((MMSmsPart *)l->data); if (idx < 1 || idx > self->priv->max_parts) { mm_obj_warn (self, "invalid part index (%u) found, ignoring", idx); continue; } if (sorted_parts[idx - 1]) { mm_obj_warn (self, "duplicate part index (%u) found, ignoring", idx); continue; } /* Add the part to the proper index */ sorted_parts[idx - 1] = (MMSmsPart *)l->data; } } fulltext = g_string_new (""); fulldata = g_byte_array_sized_new (160 * self->priv->max_parts); /* Assemble text and data from all parts. Now 'idx' is the index of the * array, so for multipart messages the real index of the part is 'idx + 1' */ for (idx = 0; idx < self->priv->max_parts; idx++) { const gchar *parttext; const GByteArray *partdata; if (!sorted_parts[idx]) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Cannot assemble SMS, missing part at index (%u)", self->priv->max_parts == 1 ? idx : idx + 1); g_string_free (fulltext, TRUE); g_byte_array_free (fulldata, TRUE); g_free (sorted_parts); return FALSE; } /* When the user creates the SMS, it will have either 'text' or 'data', * not both. Also status report PDUs may not have neither text nor data. */ parttext = mm_sms_part_get_text (sorted_parts[idx]); partdata = mm_sms_part_get_data (sorted_parts[idx]); if (!parttext && !partdata && mm_sms_part_get_pdu_type (sorted_parts[idx]) != MM_SMS_PDU_TYPE_STATUS_REPORT) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Cannot assemble SMS, part at index (%u) has neither text nor data", self->priv->max_parts == 1 ? idx : idx + 1); g_string_free (fulltext, TRUE); g_byte_array_free (fulldata, TRUE); g_free (sorted_parts); return FALSE; } if (parttext) g_string_append (fulltext, parttext); if (partdata) g_byte_array_append (fulldata, partdata->data, partdata->len); } /* If we got all parts, we also have the first one always */ g_assert (sorted_parts[0] != NULL); /* If we got everything, assemble the text! */ g_object_set (self, "text", fulltext->str, "data", g_variant_new_from_data (G_VARIANT_TYPE ("ay"), fulldata->data, fulldata->len * sizeof (guint8), TRUE, (GDestroyNotify) g_byte_array_unref, g_byte_array_ref (fulldata)), /* delivery report request and message reference taken always from the last part */ "message-reference", mm_sms_part_get_message_reference (sorted_parts[self->priv->max_parts - 1]), "delivery-report-request", mm_sms_part_get_delivery_report_request (sorted_parts[self->priv->max_parts - 1]), NULL); g_string_free (fulltext, TRUE); g_byte_array_unref (fulldata); g_free (sorted_parts); self->priv->is_assembled = TRUE; return TRUE; } /*****************************************************************************/ static guint cmp_sms_part_sequence (MMSmsPart *a, MMSmsPart *b) { return (mm_sms_part_get_concat_sequence (a) - mm_sms_part_get_concat_sequence (b)); } gboolean mm_base_sms_multipart_take_part (MMBaseSms *self, MMSmsPart *part, GError **error) { if (!self->priv->is_multipart) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "This SMS is not a multipart message"); return FALSE; } if (g_list_length (self->priv->parts) >= self->priv->max_parts) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Already took %u parts, cannot take more", g_list_length (self->priv->parts)); return FALSE; } if (g_list_find_custom (self->priv->parts, part, (GCompareFunc)cmp_sms_part_sequence)) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Cannot take part, sequence %u already taken", mm_sms_part_get_concat_sequence (part)); return FALSE; } if (mm_sms_part_get_concat_sequence (part) > self->priv->max_parts) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Cannot take part with sequence %u, maximum is %u", mm_sms_part_get_concat_sequence (part), self->priv->max_parts); return FALSE; } /* Insert sorted by concat sequence */ self->priv->parts = g_list_insert_sorted (self->priv->parts, part, (GCompareFunc)cmp_sms_part_sequence); /* If this is the first part we take, initialize common SMS fields */ if (g_list_length (self->priv->parts) == 1) initialize_sms (self); /* We only populate contents when the multipart SMS is complete */ if (mm_base_sms_multipart_is_complete (self)) { GError *inner_error = NULL; if (!assemble_sms (self, &inner_error)) { /* We DO NOT propagate the error. The part was properly taken * so ownership passed to the MMBaseSms object. */ mm_obj_warn (self, "couldn't assemble SMS: %s", inner_error->message); g_error_free (inner_error); } else { /* Completed AND assembled * Change state RECEIVING->RECEIVED, and signal completeness */ if (mm_gdbus_sms_get_state (MM_GDBUS_SMS (self)) == MM_SMS_STATE_RECEIVING) mm_gdbus_sms_set_state (MM_GDBUS_SMS (self), MM_SMS_STATE_RECEIVED); } } return TRUE; } MMBaseSms * mm_base_sms_new (MMBaseModem *modem) { return MM_BASE_SMS (g_object_new (MM_TYPE_BASE_SMS, MM_BASE_SMS_MODEM, modem, NULL)); } MMBaseSms * mm_base_sms_singlepart_new (MMBaseModem *modem, MMSmsState state, MMSmsStorage storage, MMSmsPart *part, GError **error) { MMBaseSms *self; g_assert (MM_IS_IFACE_MODEM_MESSAGING (modem)); /* Create an SMS object as defined by the interface */ self = mm_iface_modem_messaging_create_sms (MM_IFACE_MODEM_MESSAGING (modem)); g_object_set (self, "state", state, "storage", storage, NULL); /* Keep the single part in the list */ self->priv->parts = g_list_prepend (self->priv->parts, part); /* Initialize common SMS fields */ initialize_sms (self); if (!assemble_sms (self, error)) { /* Note: we need to remove the part from the list, as we really didn't * take it, and therefore the caller is responsible for freeing it. */ self->priv->parts = g_list_remove (self->priv->parts, part); g_clear_object (&self); } else /* Only export once properly created */ mm_base_sms_export (self); return self; } MMBaseSms * mm_base_sms_multipart_new (MMBaseModem *modem, MMSmsState state, MMSmsStorage storage, guint reference, guint max_parts, MMSmsPart *first_part, GError **error) { MMBaseSms *self; g_assert (MM_IS_IFACE_MODEM_MESSAGING (modem)); /* If this is the first part of a RECEIVED SMS, we overwrite the state * as RECEIVING, to indicate that it is not completed yet. */ if (state == MM_SMS_STATE_RECEIVED) state = MM_SMS_STATE_RECEIVING; /* Create an SMS object as defined by the interface */ self = mm_iface_modem_messaging_create_sms (MM_IFACE_MODEM_MESSAGING (modem)); g_object_set (self, MM_BASE_SMS_IS_MULTIPART, TRUE, MM_BASE_SMS_MAX_PARTS, max_parts, MM_BASE_SMS_MULTIPART_REFERENCE, reference, "state", state, "storage", storage, "validity", g_variant_new ("(uv)", MM_SMS_VALIDITY_TYPE_UNKNOWN, g_variant_new_boolean (FALSE)), NULL); if (!mm_base_sms_multipart_take_part (self, first_part, error)) g_clear_object (&self); /* We do export uncomplete multipart messages, in order to be able to * request removal of all parts of those multipart SMS that will never * get completed. * Only the STATE of the SMS object will be valid in the exported DBus * interface.*/ if (self) mm_base_sms_export (self); return self; } MMBaseSms * mm_base_sms_new_from_properties (MMBaseModem *modem, MMSmsProperties *props, GError **error) { MMBaseSms *self; const gchar *text; GByteArray *data; g_assert (MM_IS_IFACE_MODEM_MESSAGING (modem)); text = mm_sms_properties_get_text (props); data = mm_sms_properties_peek_data_bytearray (props); /* Don't create SMS from properties if either (text|data) or number is missing */ if (!mm_sms_properties_get_number (props) || (!text && !data)) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS, "Cannot create SMS: mandatory parameter '%s' is missing", (!mm_sms_properties_get_number (props)? "number" : "text' or 'data")); return NULL; } /* Don't create SMS from properties if both text and data are given */ if (text && data) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS, "Cannot create SMS: both 'text' and 'data' given"); return NULL; } /* Create an SMS object as defined by the interface */ self = mm_iface_modem_messaging_create_sms (MM_IFACE_MODEM_MESSAGING (modem)); g_object_set (self, "state", MM_SMS_STATE_UNKNOWN, "storage", MM_SMS_STORAGE_UNKNOWN, "number", mm_sms_properties_get_number (props), "pdu-type", (mm_sms_properties_get_teleservice_id (props) == MM_SMS_CDMA_TELESERVICE_ID_UNKNOWN ? MM_SMS_PDU_TYPE_SUBMIT : MM_SMS_PDU_TYPE_CDMA_SUBMIT), "text", text, "data", (data ? g_variant_new_from_data (G_VARIANT_TYPE ("ay"), data->data, data->len * sizeof (guint8), TRUE, (GDestroyNotify) g_byte_array_unref, g_byte_array_ref (data)) : NULL), "smsc", mm_sms_properties_get_smsc (props), "class", mm_sms_properties_get_class (props), "teleservice-id", mm_sms_properties_get_teleservice_id (props), "service-category", mm_sms_properties_get_service_category (props), "delivery-report-request", mm_sms_properties_get_delivery_report_request (props), "delivery-state", MM_SMS_DELIVERY_STATE_UNKNOWN, "validity", (mm_sms_properties_get_validity_type (props) == MM_SMS_VALIDITY_TYPE_RELATIVE ? g_variant_new ("(uv)", MM_SMS_VALIDITY_TYPE_RELATIVE, g_variant_new_uint32 (mm_sms_properties_get_validity_relative (props))) : g_variant_new ("(uv)", MM_SMS_VALIDITY_TYPE_UNKNOWN, g_variant_new_boolean (FALSE))), NULL); /* Only export once properly created */ mm_base_sms_export (self); return self; } /*****************************************************************************/ static gchar * log_object_build_id (MMLogObject *_self) { MMBaseSms *self; self = MM_BASE_SMS (_self); return g_strdup_printf ("sms%u", self->priv->dbus_id); } /*****************************************************************************/ static void set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { MMBaseSms *self = MM_BASE_SMS (object); switch (prop_id) { case PROP_PATH: g_free (self->priv->path); self->priv->path = g_value_dup_string (value); /* Export when we get a DBus connection AND we have a path */ if (!self->priv->path) sms_dbus_unexport (self); else if (self->priv->connection) sms_dbus_export (self); break; case PROP_CONNECTION: g_clear_object (&self->priv->connection); self->priv->connection = g_value_dup_object (value); /* Export when we get a DBus connection AND we have a path */ if (!self->priv->connection) sms_dbus_unexport (self); else if (self->priv->path) sms_dbus_export (self); break; case PROP_MODEM: g_clear_object (&self->priv->modem); self->priv->modem = g_value_dup_object (value); if (self->priv->modem) { /* Set owner ID */ mm_log_object_set_owner_id (MM_LOG_OBJECT (self), mm_log_object_get_id (MM_LOG_OBJECT (self->priv->modem))); /* Bind the modem's connection (which is set when it is exported, * and unset when unexported) to the SMS's connection */ g_object_bind_property (self->priv->modem, MM_BASE_MODEM_CONNECTION, self, MM_BASE_SMS_CONNECTION, G_BINDING_DEFAULT | G_BINDING_SYNC_CREATE); } break; case PROP_IS_MULTIPART: self->priv->is_multipart = g_value_get_boolean (value); break; case PROP_MAX_PARTS: self->priv->max_parts = g_value_get_uint (value); break; case PROP_MULTIPART_REFERENCE: self->priv->multipart_reference = g_value_get_uint (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) { MMBaseSms *self = MM_BASE_SMS (object); switch (prop_id) { case PROP_PATH: g_value_set_string (value, self->priv->path); break; case PROP_CONNECTION: g_value_set_object (value, self->priv->connection); break; case PROP_MODEM: g_value_set_object (value, self->priv->modem); break; case PROP_IS_MULTIPART: g_value_set_boolean (value, self->priv->is_multipart); break; case PROP_MAX_PARTS: g_value_set_uint (value, self->priv->max_parts); break; case PROP_MULTIPART_REFERENCE: g_value_set_uint (value, self->priv->multipart_reference); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void mm_base_sms_init (MMBaseSms *self) { static guint id = 0; /* Initialize private data */ self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, MM_TYPE_BASE_SMS, MMBaseSmsPrivate); /* Defaults */ self->priv->max_parts = 1; /* Each SMS is given a unique id to build its own DBus path */ self->priv->dbus_id = id++; } static void finalize (GObject *object) { MMBaseSms *self = MM_BASE_SMS (object); g_list_free_full (self->priv->parts, (GDestroyNotify)mm_sms_part_free); g_free (self->priv->path); G_OBJECT_CLASS (mm_base_sms_parent_class)->finalize (object); } static void dispose (GObject *object) { MMBaseSms *self = MM_BASE_SMS (object); if (self->priv->connection) { /* If we arrived here with a valid connection, make sure we unexport * the object */ sms_dbus_unexport (self); g_clear_object (&self->priv->connection); } g_clear_object (&self->priv->modem); G_OBJECT_CLASS (mm_base_sms_parent_class)->dispose (object); } static void log_object_iface_init (MMLogObjectInterface *iface) { iface->build_id = log_object_build_id; } static void mm_base_sms_class_init (MMBaseSmsClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); g_type_class_add_private (object_class, sizeof (MMBaseSmsPrivate)); /* Virtual methods */ object_class->get_property = get_property; object_class->set_property = set_property; object_class->finalize = finalize; object_class->dispose = dispose; klass->store = sms_store; klass->store_finish = sms_store_finish; klass->send = sms_send; klass->send_finish = sms_send_finish; klass->delete = sms_delete; klass->delete_finish = sms_delete_finish; properties[PROP_CONNECTION] = g_param_spec_object (MM_BASE_SMS_CONNECTION, "Connection", "GDBus connection to the system bus.", G_TYPE_DBUS_CONNECTION, G_PARAM_READWRITE); g_object_class_install_property (object_class, PROP_CONNECTION, properties[PROP_CONNECTION]); properties[PROP_PATH] = g_param_spec_string (MM_BASE_SMS_PATH, "Path", "DBus path of the SMS", NULL, G_PARAM_READWRITE); g_object_class_install_property (object_class, PROP_PATH, properties[PROP_PATH]); properties[PROP_MODEM] = g_param_spec_object (MM_BASE_SMS_MODEM, "Modem", "The Modem which owns this SMS", MM_TYPE_BASE_MODEM, G_PARAM_READWRITE); g_object_class_install_property (object_class, PROP_MODEM, properties[PROP_MODEM]); properties[PROP_IS_MULTIPART] = g_param_spec_boolean (MM_BASE_SMS_IS_MULTIPART, "Is multipart", "Flag specifying if the SMS is multipart", FALSE, G_PARAM_READWRITE); g_object_class_install_property (object_class, PROP_IS_MULTIPART, properties[PROP_IS_MULTIPART]); properties[PROP_MAX_PARTS] = g_param_spec_uint (MM_BASE_SMS_MAX_PARTS, "Max parts", "Maximum number of parts composing this SMS", 1,255, 1, G_PARAM_READWRITE); g_object_class_install_property (object_class, PROP_MAX_PARTS, properties[PROP_MAX_PARTS]); properties[PROP_MULTIPART_REFERENCE] = g_param_spec_uint (MM_BASE_SMS_MULTIPART_REFERENCE, "Multipart reference", "Common reference for all parts in the multipart SMS", 0, G_MAXUINT, 0, G_PARAM_READWRITE); g_object_class_install_property (object_class, PROP_MULTIPART_REFERENCE, properties[PROP_MULTIPART_REFERENCE]); } ModemManager-1.23.4-dev/src/mm-base-sms.h000066400000000000000000000131511456466623000200070ustar00rootroot00000000000000/* -*- 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: * * Author: Aleksander Morgado * * Copyright (C) 2012 Google, Inc. */ #ifndef MM_BASE_SMS_H #define MM_BASE_SMS_H #include #include #define _LIBMM_INSIDE_MM #include #include "mm-sms-part.h" #include "mm-base-modem.h" /*****************************************************************************/ /* Default timeout value to be used when sending a SMS, long enough so that the * operation succeeds or fails under low signal conditions. */ #define MM_BASE_SMS_DEFAULT_SEND_TIMEOUT (5 * 60) /*****************************************************************************/ #define MM_TYPE_BASE_SMS (mm_base_sms_get_type ()) #define MM_BASE_SMS(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_BASE_SMS, MMBaseSms)) #define MM_BASE_SMS_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_BASE_SMS, MMBaseSmsClass)) #define MM_IS_BASE_SMS(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_BASE_SMS)) #define MM_IS_BASE_SMS_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_BASE_SMS)) #define MM_BASE_SMS_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_BASE_SMS, MMBaseSmsClass)) typedef struct _MMBaseSms MMBaseSms; typedef struct _MMBaseSmsClass MMBaseSmsClass; typedef struct _MMBaseSmsPrivate MMBaseSmsPrivate; #define MM_BASE_SMS_PATH "sms-path" #define MM_BASE_SMS_CONNECTION "sms-connection" #define MM_BASE_SMS_MODEM "sms-modem" #define MM_BASE_SMS_IS_MULTIPART "sms-is-multipart" #define MM_BASE_SMS_MAX_PARTS "sms-max-parts" #define MM_BASE_SMS_MULTIPART_REFERENCE "sms-multipart-reference" struct _MMBaseSms { MmGdbusSmsSkeleton parent; MMBaseSmsPrivate *priv; }; struct _MMBaseSmsClass { MmGdbusSmsSkeletonClass parent; /* Store the SMS */ void (* store) (MMBaseSms *self, MMSmsStorage storage, GAsyncReadyCallback callback, gpointer user_data); gboolean (* store_finish) (MMBaseSms *self, GAsyncResult *res, GError **error); /* Send the SMS */ void (* send) (MMBaseSms *self, GAsyncReadyCallback callback, gpointer user_data); gboolean (* send_finish) (MMBaseSms *self, GAsyncResult *res, GError **error); /* Delete the SMS */ void (* delete) (MMBaseSms *self, GAsyncReadyCallback callback, gpointer user_data); gboolean (* delete_finish) (MMBaseSms *self, GAsyncResult *res, GError **error); }; GType mm_base_sms_get_type (void); G_DEFINE_AUTOPTR_CLEANUP_FUNC (MMBaseSms, g_object_unref) /* This one can be overridden by plugins */ MMBaseSms *mm_base_sms_new (MMBaseModem *modem); MMBaseSms *mm_base_sms_new_from_properties (MMBaseModem *modem, MMSmsProperties *properties, GError **error); MMBaseSms *mm_base_sms_singlepart_new (MMBaseModem *modem, MMSmsState state, MMSmsStorage storage, MMSmsPart *part, GError **error); MMBaseSms *mm_base_sms_multipart_new (MMBaseModem *modem, MMSmsState state, MMSmsStorage storage, guint reference, guint max_parts, MMSmsPart *first_part, GError **error); gboolean mm_base_sms_multipart_take_part (MMBaseSms *self, MMSmsPart *part, GError **error); void mm_base_sms_export (MMBaseSms *self); void mm_base_sms_unexport (MMBaseSms *self); const gchar *mm_base_sms_get_path (MMBaseSms *self); MMSmsStorage mm_base_sms_get_storage (MMBaseSms *self); gboolean mm_base_sms_has_part_index (MMBaseSms *self, guint index); GList *mm_base_sms_get_parts (MMBaseSms *self); gboolean mm_base_sms_is_multipart (MMBaseSms *self); guint mm_base_sms_get_multipart_reference (MMBaseSms *self); gboolean mm_base_sms_multipart_is_complete (MMBaseSms *self); gboolean mm_base_sms_multipart_is_assembled (MMBaseSms *self); void mm_base_sms_delete (MMBaseSms *self, GAsyncReadyCallback callback, gpointer user_data); gboolean mm_base_sms_delete_finish (MMBaseSms *self, GAsyncResult *res, GError **error); #endif /* MM_BASE_SMS_H */ ModemManager-1.23.4-dev/src/mm-bearer-list.c000066400000000000000000000361011456466623000205010ustar00rootroot00000000000000/* -*- 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 - 2011 Red Hat, Inc. * Copyright (C) 2011 Google, Inc. */ #include #include #include #include #include #include #include #define _LIBMM_INSIDE_MM #include #include "mm-bearer-list.h" #include "mm-log.h" G_DEFINE_TYPE (MMBearerList, mm_bearer_list, G_TYPE_OBJECT) enum { PROP_0, PROP_NUM_BEARERS, PROP_MAX_ACTIVE_BEARERS, PROP_MAX_ACTIVE_MULTIPLEXED_BEARERS, PROP_LAST }; static GParamSpec *properties[PROP_LAST]; struct _MMBearerListPrivate { /* List of bearers */ GList *bearers; /* Max number of active bearers */ guint max_active_bearers; guint max_active_multiplexed_bearers; }; /*****************************************************************************/ guint mm_bearer_list_get_max_active (MMBearerList *self) { return self->priv->max_active_bearers; } guint mm_bearer_list_get_max_active_multiplexed (MMBearerList *self) { return self->priv->max_active_multiplexed_bearers; } gboolean mm_bearer_list_add_bearer (MMBearerList *self, MMBaseBearer *bearer, GError **error) { /* Keep our own reference */ self->priv->bearers = g_list_prepend (self->priv->bearers, g_object_ref (bearer)); g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_NUM_BEARERS]); return TRUE; } gboolean mm_bearer_list_delete_bearer (MMBearerList *self, const gchar *path, GError **error) { GList *l; for (l = self->priv->bearers; l; l = g_list_next (l)) { if (g_str_equal (path, mm_base_bearer_get_path (MM_BASE_BEARER (l->data)))) { g_object_unref (l->data); self->priv->bearers = g_list_delete_link (self->priv->bearers, l); g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_NUM_BEARERS]); return TRUE; } } g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_NOT_FOUND, "Cannot delete bearer: path '%s' not found", path); return FALSE; } GStrv mm_bearer_list_get_paths (MMBearerList *self) { GStrv path_list = NULL; GList *l; guint i; path_list = g_new0 (gchar *, 1 + g_list_length (self->priv->bearers)); for (i = 0, l = self->priv->bearers; l; l = g_list_next (l)) path_list[i++] = g_strdup (mm_base_bearer_get_path (MM_BASE_BEARER (l->data))); return path_list; } void mm_bearer_list_foreach (MMBearerList *self, MMBearerListForeachFunc func, gpointer user_data) { g_list_foreach (self->priv->bearers, (GFunc)func, user_data); } MMBaseBearer * mm_bearer_list_find_by_properties (MMBearerList *self, MMBearerProperties *props) { GList *l; for (l = self->priv->bearers; l; l = g_list_next (l)) { /* always strict matching when comparing these bearer properties, as they're all * built in the same place */ if (mm_bearer_properties_cmp (mm_base_bearer_peek_config (MM_BASE_BEARER (l->data)), props, MM_BEARER_PROPERTIES_CMP_FLAGS_NONE)) return g_object_ref (l->data); } return NULL; } MMBaseBearer * mm_bearer_list_find_by_path (MMBearerList *self, const gchar *path) { GList *l; for (l = self->priv->bearers; l; l = g_list_next (l)) { if (g_str_equal (path, mm_base_bearer_get_path (MM_BASE_BEARER (l->data)))) return g_object_ref (l->data); } return NULL; } MMBaseBearer * mm_bearer_list_find_by_profile_id (MMBearerList *self, gint profile_id) { GList *l; g_assert (profile_id != MM_3GPP_PROFILE_ID_UNKNOWN); for (l = self->priv->bearers; l; l = g_list_next (l)) { if (mm_base_bearer_get_profile_id (MM_BASE_BEARER (l->data)) == profile_id) return g_object_ref (l->data); } return NULL; } MMBaseBearer * mm_bearer_list_find_by_apn_type (MMBearerList *self, MMBearerApnType apn_type) { GList *l; g_assert (apn_type != MM_BEARER_APN_TYPE_NONE); for (l = self->priv->bearers; l; l = g_list_next (l)) { if (mm_base_bearer_get_apn_type (MM_BASE_BEARER (l->data)) == apn_type) return g_object_ref (l->data); } return NULL; } /*****************************************************************************/ typedef struct { gchar *bearer_path; GList *pending_to_disconnect; MMBaseBearer *current_to_disconnect; } DisconnectBearersContext; static void disconnect_bearers_context_free (DisconnectBearersContext *ctx) { g_free (ctx->bearer_path); if (ctx->current_to_disconnect) g_object_unref (ctx->current_to_disconnect); g_list_free_full (ctx->pending_to_disconnect, g_object_unref); g_slice_free (DisconnectBearersContext, ctx); } gboolean mm_bearer_list_disconnect_bearers_finish (MMBearerList *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void disconnect_bearers_next (GTask *task); static void bearer_disconnect_ready (MMBaseBearer *bearer, GAsyncResult *res, GTask *task) { GError *error = NULL; if (!mm_base_bearer_disconnect_finish (bearer, res, &error)) { g_task_return_error (task, error); g_object_unref (task); return; } disconnect_bearers_next (task); } static void disconnect_bearers_next (GTask *task) { DisconnectBearersContext *ctx; ctx = g_task_get_task_data (task); g_clear_object (&ctx->current_to_disconnect); /* No more bearers? all done! */ if (!ctx->pending_to_disconnect) { g_task_return_boolean (task, TRUE); g_object_unref (task); return; } ctx->current_to_disconnect = MM_BASE_BEARER (ctx->pending_to_disconnect->data); ctx->pending_to_disconnect = g_list_delete_link (ctx->pending_to_disconnect, ctx->pending_to_disconnect); mm_base_bearer_disconnect (ctx->current_to_disconnect, (GAsyncReadyCallback)bearer_disconnect_ready, task); } static void build_connected_bearer_list (MMBaseBearer *bearer, DisconnectBearersContext *ctx) { if (!ctx->bearer_path || g_str_equal (ctx->bearer_path, mm_base_bearer_get_path (bearer))) ctx->pending_to_disconnect = g_list_prepend (ctx->pending_to_disconnect, g_object_ref (bearer)); } void mm_bearer_list_disconnect_bearers (MMBearerList *self, const gchar *bearer_path, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; DisconnectBearersContext *ctx; task = g_task_new (self, NULL, callback, user_data); ctx = g_slice_new0 (DisconnectBearersContext); ctx->bearer_path = g_strdup (bearer_path); /* may be NULL if disconnecting all */ g_task_set_task_data (task, ctx, (GDestroyNotify)disconnect_bearers_context_free); /* If a given specific bearer is being disconnected, only add that one. Otherwise, * disconnect all. */ mm_bearer_list_foreach (self, (MMBearerListForeachFunc)build_connected_bearer_list, ctx); if (ctx->bearer_path && !ctx->pending_to_disconnect) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS, "Couldn't disconnect bearer '%s': not found", ctx->bearer_path); g_object_unref (task); return; } disconnect_bearers_next (task); } /*****************************************************************************/ #if defined WITH_SUSPEND_RESUME typedef struct { GList *pending_to_sync; MMBaseBearer *current_to_sync; } SyncAllContext; static void sync_all_context_free (SyncAllContext *ctx) { if (ctx->current_to_sync) g_object_unref (ctx->current_to_sync); g_list_free_full (ctx->pending_to_sync, g_object_unref); g_free (ctx); } gboolean mm_bearer_list_sync_all_bearers_finish (MMBearerList *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void sync_next_bearer (GTask *task); static void sync_ready (MMBaseBearer *bearer, GAsyncResult *res, GTask *task) { g_autoptr(GError) error = NULL; if (!mm_base_bearer_sync_finish (bearer, res, &error)) mm_obj_warn (bearer, "failed synchronizing state: %s", error->message); sync_next_bearer (task); } static void sync_next_bearer (GTask *task) { SyncAllContext *ctx; ctx = g_task_get_task_data (task); if (ctx->current_to_sync) g_clear_object (&ctx->current_to_sync); /* No more bearers? all done! */ if (!ctx->pending_to_sync) { g_task_return_boolean (task, TRUE); g_object_unref (task); return; } ctx->current_to_sync = MM_BASE_BEARER (ctx->pending_to_sync->data); ctx->pending_to_sync = g_list_delete_link (ctx->pending_to_sync, ctx->pending_to_sync); mm_base_bearer_sync (ctx->current_to_sync, (GAsyncReadyCallback)sync_ready, task); } void mm_bearer_list_sync_all_bearers (MMBearerList *self, GAsyncReadyCallback callback, gpointer user_data) { SyncAllContext *ctx; GTask *task; ctx = g_new0 (SyncAllContext, 1); /* Get a copy of the list */ ctx->pending_to_sync = g_list_copy_deep (self->priv->bearers, (GCopyFunc)g_object_ref, NULL); task = g_task_new (self, NULL, callback, user_data); g_task_set_task_data (task, ctx, (GDestroyNotify)sync_all_context_free); sync_next_bearer (task); } #endif /*****************************************************************************/ MMBearerList * mm_bearer_list_new (guint max_active_bearers, guint max_active_multiplexed_bearers) { /* Create the object */ return g_object_new (MM_TYPE_BEARER_LIST, MM_BEARER_LIST_MAX_ACTIVE_BEARERS, max_active_bearers, MM_BEARER_LIST_MAX_ACTIVE_MULTIPLEXED_BEARERS, max_active_multiplexed_bearers, NULL); } static void set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { MMBearerList *self = MM_BEARER_LIST (object); switch (prop_id) { case PROP_NUM_BEARERS: g_assert_not_reached (); break; case PROP_MAX_ACTIVE_BEARERS: self->priv->max_active_bearers = g_value_get_uint (value); break; case PROP_MAX_ACTIVE_MULTIPLEXED_BEARERS: self->priv->max_active_multiplexed_bearers = g_value_get_uint (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) { MMBearerList *self = MM_BEARER_LIST (object); switch (prop_id) { case PROP_NUM_BEARERS: g_value_set_uint (value, g_list_length (self->priv->bearers)); break; case PROP_MAX_ACTIVE_BEARERS: g_value_set_uint (value, self->priv->max_active_bearers); break; case PROP_MAX_ACTIVE_MULTIPLEXED_BEARERS: g_value_set_uint (value, self->priv->max_active_multiplexed_bearers); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void mm_bearer_list_init (MMBearerList *self) { /* Initialize private data */ self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, MM_TYPE_BEARER_LIST, MMBearerListPrivate); } static void dispose (GObject *object) { MMBearerList *self = MM_BEARER_LIST (object); if (self->priv->bearers) { g_list_free_full (self->priv->bearers, g_object_unref); self->priv->bearers = NULL; } G_OBJECT_CLASS (mm_bearer_list_parent_class)->dispose (object); } static void mm_bearer_list_class_init (MMBearerListClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); g_type_class_add_private (object_class, sizeof (MMBearerListPrivate)); /* Virtual methods */ object_class->get_property = get_property; object_class->set_property = set_property; object_class->dispose = dispose; properties[PROP_NUM_BEARERS] = g_param_spec_uint (MM_BEARER_LIST_NUM_BEARERS, "Number of bearers", "Current number of bearers in the list", 0, G_MAXUINT, 0, G_PARAM_READABLE); g_object_class_install_property (object_class, PROP_NUM_BEARERS, properties[PROP_NUM_BEARERS]); properties[PROP_MAX_ACTIVE_BEARERS] = g_param_spec_uint (MM_BEARER_LIST_MAX_ACTIVE_BEARERS, "Max active bearers", "Maximum number of active bearers the list can handle", 0, G_MAXUINT, 0, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY); g_object_class_install_property (object_class, PROP_MAX_ACTIVE_BEARERS, properties[PROP_MAX_ACTIVE_BEARERS]); properties[PROP_MAX_ACTIVE_MULTIPLEXED_BEARERS] = g_param_spec_uint (MM_BEARER_LIST_MAX_ACTIVE_MULTIPLEXED_BEARERS, "Max active multiplexed bearers", "Maximum number of active multiplexed bearers the list can handle", 0, G_MAXUINT, 0, G_PARAM_READWRITE); g_object_class_install_property (object_class, PROP_MAX_ACTIVE_MULTIPLEXED_BEARERS, properties[PROP_MAX_ACTIVE_MULTIPLEXED_BEARERS]); } ModemManager-1.23.4-dev/src/mm-bearer-list.h000066400000000000000000000111111456466623000205000ustar00rootroot00000000000000/* -*- 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: * * Author: Aleksander Morgado * * Copyright (C) 2011 Google, Inc. */ #ifndef MM_BEARER_LIST_H #define MM_BEARER_LIST_H #include #include #include "mm-base-bearer.h" #define MM_TYPE_BEARER_LIST (mm_bearer_list_get_type ()) #define MM_BEARER_LIST(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_BEARER_LIST, MMBearerList)) #define MM_BEARER_LIST_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_BEARER_LIST, MMBearerListClass)) #define MM_IS_BEARER_LIST(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_BEARER_LIST)) #define MM_IS_BEARER_LIST_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_BEARER_LIST)) #define MM_BEARER_LIST_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_BEARER_LIST, MMBearerListClass)) #define MM_BEARER_LIST_NUM_BEARERS "num-bearers" #define MM_BEARER_LIST_MAX_ACTIVE_BEARERS "max-active-bearers" #define MM_BEARER_LIST_MAX_ACTIVE_MULTIPLEXED_BEARERS "max-active-multiplexed-bearers" typedef struct _MMBearerList MMBearerList; typedef struct _MMBearerListClass MMBearerListClass; typedef struct _MMBearerListPrivate MMBearerListPrivate; struct _MMBearerList { GObject parent; MMBearerListPrivate *priv; }; struct _MMBearerListClass { GObjectClass parent; }; GType mm_bearer_list_get_type (void); G_DEFINE_AUTOPTR_CLEANUP_FUNC (MMBearerList, g_object_unref) MMBearerList *mm_bearer_list_new (guint max_active_bearers, guint max_active_multiplexed_bearers); GStrv mm_bearer_list_get_paths (MMBearerList *self); guint mm_bearer_list_get_max_active (MMBearerList *self); guint mm_bearer_list_get_max_active_multiplexed (MMBearerList *self); gboolean mm_bearer_list_add_bearer (MMBearerList *self, MMBaseBearer *bearer, GError **error); gboolean mm_bearer_list_delete_bearer (MMBearerList *self, const gchar *path, GError **error); typedef void (*MMBearerListForeachFunc) (MMBaseBearer *bearer, gpointer user_data); void mm_bearer_list_foreach (MMBearerList *self, MMBearerListForeachFunc func, gpointer user_data); MMBaseBearer *mm_bearer_list_find_by_properties (MMBearerList *self, MMBearerProperties *properties); MMBaseBearer *mm_bearer_list_find_by_path (MMBearerList *self, const gchar *path); MMBaseBearer *mm_bearer_list_find_by_profile_id (MMBearerList *self, gint profile_id); MMBaseBearer *mm_bearer_list_find_by_apn_type (MMBearerList *self, MMBearerApnType apn_type); void mm_bearer_list_disconnect_bearers (MMBearerList *self, const gchar *bearer_path, GAsyncReadyCallback callback, gpointer user_data); gboolean mm_bearer_list_disconnect_bearers_finish (MMBearerList *self, GAsyncResult *res, GError **error); #if defined WITH_SUSPEND_RESUME void mm_bearer_list_sync_all_bearers (MMBearerList *self, GAsyncReadyCallback callback, gpointer user_data); gboolean mm_bearer_list_sync_all_bearers_finish (MMBearerList *self, GAsyncResult *res, GError **error); #endif #endif /* MM_BEARER_LIST_H */ ModemManager-1.23.4-dev/src/mm-bearer-mbim.c000066400000000000000000002374401456466623000204630ustar00rootroot00000000000000/* -*- 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) 2013 Aleksander Morgado */ #include #include #include #include #include #include #include #include #include #define _LIBMM_INSIDE_MM #include #include "mm-iface-modem.h" #include "mm-iface-modem-3gpp-profile-manager.h" #include "mm-modem-helpers-mbim.h" #include "mm-port-enums-types.h" #include "mm-bearer-mbim.h" #include "mm-log-object.h" #include "mm-context.h" G_DEFINE_TYPE (MMBearerMbim, mm_bearer_mbim, MM_TYPE_BASE_BEARER) enum { PROP_0, PROP_ASYNC_SLAAC, PROP_LAST }; struct _MMBearerMbimPrivate { MMPortMbim *mbim; MMPort *data; MMPort *link; guint32 session_id; /* Whether IP configuration indications should be expected before finalizing * a connection attempt. */ gboolean async_slaac; /* Ongoing connection attempt waiting for async SLAAC result */ GTask *attempt_ongoing; }; static GParamSpec *properties[PROP_LAST]; /*****************************************************************************/ static gboolean peek_ports (gpointer self, MMPortMbim **o_mbim, MMPort **o_data, GAsyncReadyCallback callback, gpointer user_data) { g_autoptr(MMBaseModem) modem = NULL; g_object_get (G_OBJECT (self), MM_BASE_BEARER_MODEM, &modem, NULL); g_assert (MM_IS_BASE_MODEM (modem)); if (o_mbim) { MMPortMbim *port; port = mm_broadband_modem_mbim_peek_port_mbim (MM_BROADBAND_MODEM_MBIM (modem)); if (!port) { g_task_report_new_error (self, callback, user_data, peek_ports, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't peek MBIM port"); return FALSE; } *o_mbim = port; } if (o_data) { MMPort *port; /* Grab a data port */ port = mm_base_modem_peek_best_data_port (modem, MM_PORT_TYPE_NET); if (!port) { g_task_report_new_error (self, callback, user_data, peek_ports, MM_CORE_ERROR, MM_CORE_ERROR_NOT_FOUND, "No valid data port found to launch connection"); return FALSE; } *o_data = port; } return TRUE; } /*****************************************************************************/ /* Stats */ typedef struct { guint64 rx_bytes; guint64 tx_bytes; } ReloadStatsResult; static gboolean reload_stats_finish (MMBaseBearer *bearer, guint64 *rx_bytes, guint64 *tx_bytes, GAsyncResult *res, GError **error) { ReloadStatsResult *stats; stats = g_task_propagate_pointer (G_TASK (res), error); if (!stats) return FALSE; if (rx_bytes) *rx_bytes = stats->rx_bytes; if (tx_bytes) *tx_bytes = stats->tx_bytes; g_free (stats); return TRUE; } static void packet_statistics_query_ready (MbimDevice *device, GAsyncResult *res, GTask *task) { GError *error = NULL; g_autoptr(MbimMessage) response = NULL; guint64 in_octets = 0; guint64 out_octets = 0; response = mbim_device_command_finish (device, res, &error); if (response && mbim_message_response_get_result (response, MBIM_MESSAGE_TYPE_COMMAND_DONE, &error) && mbim_message_packet_statistics_response_parse ( response, NULL, /* in_discards */ NULL, /* in_errors */ &in_octets, /* in_octets */ NULL, /* in_packets */ &out_octets, /* out_octets */ NULL, /* out_packets */ NULL, /* out_errors */ NULL, /* out_discards */ &error)) { /* Store results */ ReloadStatsResult *stats; stats = g_new (ReloadStatsResult, 1); stats->rx_bytes = in_octets; stats->tx_bytes = out_octets; g_task_return_pointer (task, stats, g_free); } else if (g_error_matches (error, MBIM_STATUS_ERROR, MBIM_STATUS_ERROR_OPERATION_NOT_ALLOWED)) { g_clear_error (&error); g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "operation not allowed"); } else g_task_return_error (task, error); g_object_unref (task); } static void reload_stats (MMBaseBearer *self, GAsyncReadyCallback callback, gpointer user_data) { MMPortMbim *mbim; GTask *task; g_autoptr(MbimMessage) message = NULL; if (!peek_ports (self, &mbim, NULL, callback, user_data)) return; task = g_task_new (self, NULL, callback, user_data); message = (mbim_message_packet_statistics_query_new (NULL)); mbim_device_command (mm_port_mbim_peek_device (mbim), message, 5, NULL, (GAsyncReadyCallback)packet_statistics_query_ready, task); } /*****************************************************************************/ /* Disconnection message builder. */ static MbimMessage * build_disconnect_message (MMBearerMbim *self, MMPortMbim *port, guint32 session_id) { if (mbim_device_check_ms_mbimex_version (mm_port_mbim_peek_device (port), 3, 0)) return mbim_message_ms_basic_connect_v3_connect_set_new (session_id, MBIM_ACTIVATION_COMMAND_DEACTIVATE, MBIM_COMPRESSION_NONE, MBIM_AUTH_PROTOCOL_NONE, MBIM_CONTEXT_IP_TYPE_DEFAULT, mbim_uuid_from_context_type (MBIM_CONTEXT_TYPE_INTERNET), MBIM_ACCESS_MEDIA_TYPE_UNKNOWN, "", /* access string */ "", /* user name */ "", /* password */ NULL, /* unnamed ies */ NULL); return mbim_message_connect_set_new (session_id, MBIM_ACTIVATION_COMMAND_DEACTIVATE, "", /* access string */ "", /* user name */ "", /* password */ MBIM_COMPRESSION_NONE, MBIM_AUTH_PROTOCOL_NONE, MBIM_CONTEXT_IP_TYPE_DEFAULT, mbim_uuid_from_context_type (MBIM_CONTEXT_TYPE_INTERNET), NULL); } /*****************************************************************************/ /* Connect */ #define WAIT_LINK_PORT_TIMEOUT_MS 2500 #define WAIT_IP_CONFIGURATION_ASYNC_TIMEOUT_S 20 typedef enum { CONNECT_STEP_FIRST, CONNECT_STEP_LOAD_PROFILE_SETTINGS, CONNECT_STEP_SETUP_LINK, CONNECT_STEP_SETUP_LINK_MAIN_UP, CONNECT_STEP_CHECK_DISCONNECTED, CONNECT_STEP_ENSURE_DISCONNECTED, CONNECT_STEP_CONNECT, CONNECT_STEP_IP_CONFIGURATION, CONNECT_STEP_IP_CONFIGURATION_ASYNC, CONNECT_STEP_LAST } ConnectStep; typedef struct { MMPortMbim *mbim; MMBroadbandModemMbim *modem; ConnectStep step; MMPort *data; MMBearerConnectResult *connect_result; MbimMessage *abort_on_failure; /* settings to use */ gint profile_id; gchar *apn; MbimContextType context_type; gchar *user; gchar *password; MbimAuthProtocol auth; MbimContextIpType requested_ip_type; MbimContextIpType activated_ip_type; /* multiplex support */ guint session_id; gchar *link_prefix_hint; gchar *link_name; MMPort *link; /* async slaac support */ guint async_slaac_timeout_id; gulong async_slaac_notification_id; gulong async_slaac_cancellation_id; } ConnectContext; static void connect_context_free (ConnectContext *ctx) { g_assert (!ctx->async_slaac_cancellation_id); g_assert (!ctx->async_slaac_timeout_id); g_assert (!ctx->async_slaac_notification_id); if (ctx->abort_on_failure) { mbim_device_command (mm_port_mbim_peek_device (ctx->mbim), ctx->abort_on_failure, MM_BASE_BEARER_DEFAULT_DISCONNECTION_TIMEOUT, NULL, NULL, NULL); mbim_message_unref (ctx->abort_on_failure); } if (ctx->link_name) { mm_port_mbim_cleanup_link (ctx->mbim, ctx->link_name, NULL, NULL); g_free (ctx->link_name); } g_clear_object (&ctx->link); g_free (ctx->link_prefix_hint); g_free (ctx->apn); g_free (ctx->user); g_free (ctx->password); g_clear_pointer (&ctx->connect_result, (GDestroyNotify)mm_bearer_connect_result_unref); g_clear_object (&ctx->data); g_object_unref (ctx->mbim); g_object_unref (ctx->modem); g_slice_free (ConnectContext, ctx); } static MMBearerConnectResult * connect_finish (MMBaseBearer *self, GAsyncResult *res, GError **error) { return g_task_propagate_pointer (G_TASK (res), error); } static void connect_context_step (GTask *task); static void process_ip_configuration (MMBearerMbim *self, ConnectContext *ctx, MbimIPConfigurationAvailableFlag ipv4configurationavailable, MbimIPConfigurationAvailableFlag ipv6configurationavailable, guint32 ipv4addresscount, const MbimIPv4ElementArray *ipv4address, guint32 ipv6addresscount, const MbimIPv6ElementArray *ipv6address, const MbimIPv4 *ipv4gateway, const MbimIPv6 *ipv6gateway, guint32 ipv4dnsservercount, const MbimIPv4 *ipv4dnsserver, guint32 ipv6dnsservercount, const MbimIPv6 *ipv6dnsserver, guint32 ipv4mtu, guint32 ipv6mtu) { g_autofree gchar *ipv4configurationavailable_str = NULL; g_autofree gchar *ipv6configurationavailable_str = NULL; g_autoptr(MMBearerIpConfig) ipv4_config = NULL; g_autoptr(MMBearerIpConfig) ipv6_config = NULL; guint64 uplink_speed = 0; guint64 downlink_speed = 0; /* Always recreate the connect result completely */ g_clear_pointer (&ctx->connect_result, (GDestroyNotify)mm_bearer_connect_result_unref); /* IPv4 info */ ipv4configurationavailable_str = mbim_ip_configuration_available_flag_build_string_from_mask (ipv4configurationavailable); mm_obj_dbg (self, "IPv4 configuration available: '%s'", ipv4configurationavailable_str); if ((ipv4configurationavailable & MBIM_IP_CONFIGURATION_AVAILABLE_FLAG_ADDRESS) && ipv4addresscount) { guint i; mm_obj_dbg (self, " IP addresses (%u)", ipv4addresscount); for (i = 0; i < ipv4addresscount; i++) { g_autoptr(GInetAddress) addr = NULL; g_autofree gchar *str = NULL; addr = g_inet_address_new_from_bytes ((guint8 *)&ipv4address[i]->ipv4_address, G_SOCKET_FAMILY_IPV4); str = g_inet_address_to_string (addr); mm_obj_dbg (self, " IP [%u]: '%s/%u'", i, str, ipv4address[i]->on_link_prefix_length); } } if ((ipv4configurationavailable & MBIM_IP_CONFIGURATION_AVAILABLE_FLAG_GATEWAY) && ipv4gateway) { g_autoptr(GInetAddress) addr = NULL; g_autofree gchar *str = NULL; addr = g_inet_address_new_from_bytes ((guint8 *)ipv4gateway, G_SOCKET_FAMILY_IPV4); str = g_inet_address_to_string (addr); mm_obj_dbg (self, " gateway: '%s'", str); } if ((ipv4configurationavailable & MBIM_IP_CONFIGURATION_AVAILABLE_FLAG_DNS) && ipv4dnsservercount) { guint i; mm_obj_dbg (self, " DNS addresses (%u)", ipv4dnsservercount); for (i = 0; i < ipv4dnsservercount; i++) { g_autoptr(GInetAddress) addr = NULL; addr = g_inet_address_new_from_bytes ((guint8 *)&ipv4dnsserver[i], G_SOCKET_FAMILY_IPV4); if (!g_inet_address_get_is_any (addr)) { g_autofree gchar *str = NULL; str = g_inet_address_to_string (addr); mm_obj_dbg (self, " DNS [%u]: '%s'", i, str); } } } if ((ipv4configurationavailable & MBIM_IP_CONFIGURATION_AVAILABLE_FLAG_MTU) && ipv4mtu) mm_obj_dbg (self, " MTU: '%u'", ipv4mtu); /* IPv6 info */ ipv6configurationavailable_str = mbim_ip_configuration_available_flag_build_string_from_mask (ipv6configurationavailable); mm_obj_dbg (self, "IPv6 configuration available: '%s'", ipv6configurationavailable_str); if ((ipv6configurationavailable & MBIM_IP_CONFIGURATION_AVAILABLE_FLAG_ADDRESS) && ipv6addresscount) { guint i; mm_obj_dbg (self, " IP addresses (%u)", ipv6addresscount); for (i = 0; i < ipv6addresscount; i++) { g_autoptr(GInetAddress) addr = NULL; g_autofree gchar *str = NULL; addr = g_inet_address_new_from_bytes ((guint8 *)&ipv6address[i]->ipv6_address, G_SOCKET_FAMILY_IPV6); str = g_inet_address_to_string (addr); mm_obj_dbg (self, " IP [%u]: '%s/%u'", i, str, ipv6address[i]->on_link_prefix_length); } } if ((ipv6configurationavailable & MBIM_IP_CONFIGURATION_AVAILABLE_FLAG_GATEWAY) && ipv6gateway) { g_autoptr(GInetAddress) addr = NULL; g_autofree gchar *str = NULL; addr = g_inet_address_new_from_bytes ((guint8 *)ipv6gateway, G_SOCKET_FAMILY_IPV6); str = g_inet_address_to_string (addr); mm_obj_dbg (self, " gateway: '%s'", str); } if ((ipv6configurationavailable & MBIM_IP_CONFIGURATION_AVAILABLE_FLAG_DNS) && ipv6dnsservercount) { guint i; mm_obj_dbg (self, " DNS addresses (%u)", ipv6dnsservercount); for (i = 0; i < ipv6dnsservercount; i++) { g_autoptr(GInetAddress) addr = NULL; addr = g_inet_address_new_from_bytes ((guint8 *)&ipv6dnsserver[i], G_SOCKET_FAMILY_IPV6); if (!g_inet_address_get_is_any (addr)) { g_autofree gchar *str = NULL; str = g_inet_address_to_string (addr); mm_obj_dbg (self, " DNS [%u]: '%s'", i, str); } } } if ((ipv6configurationavailable & MBIM_IP_CONFIGURATION_AVAILABLE_FLAG_MTU) && ipv6mtu) mm_obj_dbg (self, " MTU: '%u'", ipv6mtu); /* Build connection results */ /* Build IPv4 config */ if (ctx->requested_ip_type == MBIM_CONTEXT_IP_TYPE_IPV4 || ctx->requested_ip_type == MBIM_CONTEXT_IP_TYPE_IPV4V6 || ctx->requested_ip_type == MBIM_CONTEXT_IP_TYPE_IPV4_AND_IPV6) { gboolean address_set = FALSE; ipv4_config = mm_bearer_ip_config_new (); /* We assume that if we have an IP we can use static configuration. * Not all modems or providers will return DNS servers or even a * gateway, and not all modems support DHCP either. The IP management * daemon/script just has to deal with this... */ if ((ipv4configurationavailable & MBIM_IP_CONFIGURATION_AVAILABLE_FLAG_ADDRESS) && (ipv4addresscount > 0)) { g_autoptr(GInetAddress) addr = NULL; g_autofree gchar *str = NULL; mm_bearer_ip_config_set_method (ipv4_config, MM_BEARER_IP_METHOD_STATIC); /* IP address, pick the first one */ addr = g_inet_address_new_from_bytes ((guint8 *)&ipv4address[0]->ipv4_address, G_SOCKET_FAMILY_IPV4); str = g_inet_address_to_string (addr); mm_bearer_ip_config_set_address (ipv4_config, str); address_set = TRUE; /* Netmask */ mm_bearer_ip_config_set_prefix (ipv4_config, ipv4address[0]->on_link_prefix_length); /* Gateway */ if (ipv4configurationavailable & MBIM_IP_CONFIGURATION_AVAILABLE_FLAG_GATEWAY) { g_autoptr(GInetAddress) gw_addr = NULL; g_autofree gchar *gw_str = NULL; gw_addr = g_inet_address_new_from_bytes ((guint8 *)ipv4gateway, G_SOCKET_FAMILY_IPV4); gw_str = g_inet_address_to_string (gw_addr); mm_bearer_ip_config_set_gateway (ipv4_config, gw_str); } } else mm_bearer_ip_config_set_method (ipv4_config, MM_BEARER_IP_METHOD_DHCP); /* DNS */ if ((ipv4configurationavailable & MBIM_IP_CONFIGURATION_AVAILABLE_FLAG_DNS) && (ipv4dnsservercount > 0)) { g_auto(GStrv) strarr = NULL; guint i; guint n; strarr = g_new0 (gchar *, ipv4dnsservercount + 1); for (i = 0, n = 0; i < ipv4dnsservercount; i++) { g_autoptr(GInetAddress) addr = NULL; addr = g_inet_address_new_from_bytes ((guint8 *)&ipv4dnsserver[i], G_SOCKET_FAMILY_IPV4); if (!g_inet_address_get_is_any (addr)) strarr[n++] = g_inet_address_to_string (addr); } mm_bearer_ip_config_set_dns (ipv4_config, (const gchar **)strarr); } /* MTU */ if (ipv4configurationavailable & MBIM_IP_CONFIGURATION_AVAILABLE_FLAG_MTU) mm_bearer_ip_config_set_mtu (ipv4_config, ipv4mtu); /* We requested IPv4, but it wasn't reported as activated. If there is no IP address * provided by the modem, we assume the IPv4 bearer wasn't truly activated */ if (!address_set && ctx->activated_ip_type != MBIM_CONTEXT_IP_TYPE_IPV4 && ctx->activated_ip_type != MBIM_CONTEXT_IP_TYPE_IPV4V6 && ctx->activated_ip_type != MBIM_CONTEXT_IP_TYPE_IPV4_AND_IPV6) { mm_obj_dbg (self, "IPv4 requested but no IPv4 activated and no IPv4 address set: ignoring"); g_clear_object (&ipv4_config); } } /* Build IPv6 config */ if (ctx->requested_ip_type == MBIM_CONTEXT_IP_TYPE_IPV6 || ctx->requested_ip_type == MBIM_CONTEXT_IP_TYPE_IPV4V6 || ctx->requested_ip_type == MBIM_CONTEXT_IP_TYPE_IPV4_AND_IPV6) { gboolean address_set = FALSE; ipv6_config = mm_bearer_ip_config_new (); if ((ipv6configurationavailable & MBIM_IP_CONFIGURATION_AVAILABLE_FLAG_ADDRESS) && (ipv6addresscount > 0)) { g_autoptr(GInetAddress) addr = NULL; g_autofree gchar *str = NULL; /* IP address, pick the first one */ addr = g_inet_address_new_from_bytes ((guint8 *)&ipv6address[0]->ipv6_address, G_SOCKET_FAMILY_IPV6); str = g_inet_address_to_string (addr); mm_bearer_ip_config_set_address (ipv6_config, str); address_set = TRUE; /* If the address is a link-local one, then SLAAC or DHCP must be used * to get the real prefix and address. * If the address is a global one, then the modem did SLAAC already and * there is no need to run host SLAAC. */ if (g_inet_address_get_is_link_local (addr)) mm_bearer_ip_config_set_method (ipv6_config, MM_BEARER_IP_METHOD_DHCP); else mm_bearer_ip_config_set_method (ipv6_config, MM_BEARER_IP_METHOD_STATIC); /* Netmask */ mm_bearer_ip_config_set_prefix (ipv6_config, ipv6address[0]->on_link_prefix_length); /* If the modem has done SLAAC itself, it is never expected to return a /128 prefix, * warn if it happens and workaround it. Use /64 as default. */ if ((mm_bearer_ip_config_get_method (ipv6_config) == MM_BEARER_IP_METHOD_STATIC) && (mm_bearer_ip_config_get_prefix (ipv6_config) == 128)) { mm_obj_warn (self, "unexpected link prefix returned with global IPv6 address (128): ignoring"); mm_bearer_ip_config_set_prefix (ipv6_config, 64); } /* Gateway */ if (ipv6configurationavailable & MBIM_IP_CONFIGURATION_AVAILABLE_FLAG_GATEWAY) { g_autoptr(GInetAddress) gw_addr = NULL; g_autofree gchar *gw_str = NULL; gw_addr = g_inet_address_new_from_bytes ((guint8 *)ipv6gateway, G_SOCKET_FAMILY_IPV6); gw_str = g_inet_address_to_string (gw_addr); mm_bearer_ip_config_set_gateway (ipv6_config, gw_str); } } else { /* If no address is given, this is likely a bug in the modem firmware, because even in the * case of needing to run host SLAAC, a link-local IPv6 address must be given. Either way, * go on requesting the need of host SLAAC, and let the network decide whether our SLAAC * Router Solicitation messages with an unexpected link-local address are accepted or not. */ mm_bearer_ip_config_set_method (ipv6_config, MM_BEARER_IP_METHOD_DHCP); } if ((ipv6configurationavailable & MBIM_IP_CONFIGURATION_AVAILABLE_FLAG_DNS) && (ipv6dnsservercount > 0)) { g_auto(GStrv) strarr = NULL; guint i; guint n; /* DNS */ strarr = g_new0 (gchar *, ipv6dnsservercount + 1); for (i = 0, n = 0; i < ipv6dnsservercount; i++) { g_autoptr(GInetAddress) addr = NULL; addr = g_inet_address_new_from_bytes ((guint8 *)&ipv6dnsserver[i], G_SOCKET_FAMILY_IPV6); if (!g_inet_address_get_is_any (addr)) strarr[n++] = g_inet_address_to_string (addr); } mm_bearer_ip_config_set_dns (ipv6_config, (const gchar **)strarr); } /* MTU */ if (ipv6configurationavailable & MBIM_IP_CONFIGURATION_AVAILABLE_FLAG_MTU) mm_bearer_ip_config_set_mtu (ipv6_config, ipv6mtu); /* We requested IPv6, but it wasn't reported as activated. If there is no IPv6 address * provided by the modem, we assume the IPv6 bearer wasn't truly activated */ if (!address_set && ctx->activated_ip_type != MBIM_CONTEXT_IP_TYPE_IPV6 && ctx->activated_ip_type != MBIM_CONTEXT_IP_TYPE_IPV4V6 && ctx->activated_ip_type != MBIM_CONTEXT_IP_TYPE_IPV4_AND_IPV6) { mm_obj_dbg (self, "IPv6 requested but no IPv6 activated and no IPv6 address set: ignoring"); g_clear_object (&ipv6_config); } } /* Store result */ ctx->connect_result = mm_bearer_connect_result_new (ctx->link ? ctx->link : ctx->data, ipv4_config, ipv6_config); mm_bearer_connect_result_set_multiplexed (ctx->connect_result, !!ctx->link); if (ctx->profile_id != MM_3GPP_PROFILE_ID_UNKNOWN) mm_bearer_connect_result_set_profile_id (ctx->connect_result, ctx->profile_id); /* Propagate speeds from modem object */ mm_broadband_modem_mbim_get_speeds (ctx->modem, &uplink_speed, &downlink_speed); mm_bearer_connect_result_set_uplink_speed (ctx->connect_result, uplink_speed); mm_bearer_connect_result_set_downlink_speed (ctx->connect_result, downlink_speed); } static void ip_configuration_async_cleanup (GTask *task) { ConnectContext *ctx; ctx = g_task_get_task_data (task); g_assert (ctx->async_slaac_cancellation_id); g_cancellable_disconnect (g_task_get_cancellable (task), ctx->async_slaac_cancellation_id); ctx->async_slaac_cancellation_id = 0; g_assert (ctx->async_slaac_notification_id); if (g_signal_handler_is_connected (ctx->mbim, ctx->async_slaac_notification_id)) g_signal_handler_disconnect (ctx->mbim, ctx->async_slaac_notification_id); ctx->async_slaac_notification_id = 0; g_assert (ctx->async_slaac_timeout_id); g_source_remove (ctx->async_slaac_timeout_id); ctx->async_slaac_timeout_id = 0; } static gboolean ip_configuration_async_timeout (GTask *task) { ip_configuration_async_cleanup (task); g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_TIMEOUT, "Timed out waiting for SLAAC notification"); g_object_unref (task); return G_SOURCE_REMOVE; } static void ip_configuration_async_cancelled (GTask *task) { ip_configuration_async_cleanup (task); g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_CANCELLED, "Cancelled waiting for SLAAC notification"); g_object_unref (task); } static void ip_configuration_async_notification (GTask *task, MbimMessage *notification, MMPortMbim *port) { MMBearerMbim *self; ConnectContext *ctx; MMBearerIpConfig *ipv6_config; g_autoptr(GError) error = NULL; guint32 session_id; MbimIPConfigurationAvailableFlag ipv4configurationavailable; MbimIPConfigurationAvailableFlag ipv6configurationavailable; guint32 ipv4addresscount; g_autoptr(MbimIPv4ElementArray) ipv4address = NULL; guint32 ipv6addresscount; g_autoptr(MbimIPv6ElementArray) ipv6address = NULL; const MbimIPv4 *ipv4gateway; const MbimIPv6 *ipv6gateway; guint32 ipv4dnsservercount; g_autofree MbimIPv4 *ipv4dnsserver = NULL; guint32 ipv6dnsservercount; g_autofree MbimIPv6 *ipv6dnsserver = NULL; guint32 ipv4mtu; guint32 ipv6mtu; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); /* Only process notifications if the device still exists */ if (!mm_port_mbim_peek_device (port)) return; /* We only want the IP configuration updates */ if ((mbim_message_indicate_status_get_service (notification) != MBIM_SERVICE_BASIC_CONNECT) || (mbim_message_indicate_status_get_cid (notification) != MBIM_CID_BASIC_CONNECT_IP_CONFIGURATION)) return; if (!mbim_message_ip_configuration_notification_parse ( notification, &session_id, &ipv4configurationavailable, &ipv6configurationavailable, &ipv4addresscount, &ipv4address, &ipv6addresscount, &ipv6address, &ipv4gateway, &ipv6gateway, &ipv4dnsservercount, &ipv4dnsserver, &ipv6dnsservercount, &ipv6dnsserver, &ipv4mtu, &ipv6mtu, &error)) { mm_obj_warn (self, "couldn't parse IP configuration indication: %s", error->message); return; } /* We only want the updates for the current session ongoing */ if (ctx->session_id != session_id) return; /* Process content in a common way */ process_ip_configuration (self, ctx, ipv4configurationavailable, ipv6configurationavailable, ipv4addresscount, ipv4address, ipv6addresscount, ipv6address, ipv4gateway, ipv6gateway, ipv4dnsservercount, ipv4dnsserver, ipv6dnsservercount, ipv6dnsserver, ipv4mtu, ipv6mtu); /* We will be done once method is STATIC (i.e. when a global IPv6 is detected) */ ipv6_config = mm_bearer_connect_result_peek_ipv6_config (ctx->connect_result); if (!ipv6_config || (mm_bearer_ip_config_get_method (ipv6_config) != MM_BEARER_IP_METHOD_STATIC)) { mm_obj_dbg (self, "SLAAC process didn't finish yet"); return; } mm_obj_dbg (self, "SLAAC process finished"); ip_configuration_async_cleanup (task); /* Keep on */ ctx->step++; connect_context_step (task); } static gboolean ip_configuration_async_setup (GTask *task) { MMBearerMbim *self; ConnectContext *ctx; MMBearerIpConfig *ipv6_config; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); /* On async SLAAC results if not supported by modem */ if (!self->priv->async_slaac) return FALSE; /* Only expect SLAAC results if the attempt required IPv6 and if we didn't already * get a global IPv6 address reported. */ ipv6_config = mm_bearer_connect_result_peek_ipv6_config (ctx->connect_result); if (!ipv6_config || (mm_bearer_ip_config_get_method (ipv6_config) != MM_BEARER_IP_METHOD_DHCP)) return FALSE; mm_obj_info (self, "connection attempt requires async updated with SLAAC results"); /* The task implementing the connection attempt is now shared between the timeout, the notification * signal handler and the cancellation signal handler. Whichever decides to complete the task must * cancel the others right away. */ ctx->async_slaac_cancellation_id = g_cancellable_connect (g_task_get_cancellable (task), G_CALLBACK (ip_configuration_async_cancelled), task, NULL); ctx->async_slaac_timeout_id = g_timeout_add_seconds (WAIT_IP_CONFIGURATION_ASYNC_TIMEOUT_S, (GSourceFunc) ip_configuration_async_timeout, task); ctx->async_slaac_notification_id = g_signal_connect_swapped (ctx->mbim, MM_PORT_MBIM_SIGNAL_NOTIFICATION, G_CALLBACK (ip_configuration_async_notification), task); return TRUE; } static void ip_configuration_query_ready (MbimDevice *device, GAsyncResult *res, GTask *task) { MMBearerMbim *self; ConnectContext *ctx; GError *error = NULL; g_autoptr(MbimMessage) response = NULL; MbimIPConfigurationAvailableFlag ipv4configurationavailable; MbimIPConfigurationAvailableFlag ipv6configurationavailable; guint32 ipv4addresscount; g_autoptr(MbimIPv4ElementArray) ipv4address = NULL; guint32 ipv6addresscount; g_autoptr(MbimIPv6ElementArray) ipv6address = NULL; const MbimIPv4 *ipv4gateway; const MbimIPv6 *ipv6gateway; guint32 ipv4dnsservercount; g_autofree MbimIPv4 *ipv4dnsserver = NULL; guint32 ipv6dnsservercount; g_autofree MbimIPv6 *ipv6dnsserver = NULL; guint32 ipv4mtu; guint32 ipv6mtu; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); response = mbim_device_command_finish (device, res, &error); if (response && mbim_message_response_get_result (response, MBIM_MESSAGE_TYPE_COMMAND_DONE, &error) && mbim_message_ip_configuration_response_parse ( response, NULL, /* sessionid */ &ipv4configurationavailable, &ipv6configurationavailable, &ipv4addresscount, &ipv4address, &ipv6addresscount, &ipv6address, &ipv4gateway, &ipv6gateway, &ipv4dnsservercount, &ipv4dnsserver, &ipv6dnsservercount, &ipv6dnsserver, &ipv4mtu, &ipv6mtu, &error)) { /* Process content in a common way */ process_ip_configuration (self, ctx, ipv4configurationavailable, ipv6configurationavailable, ipv4addresscount, ipv4address, ipv6addresscount, ipv6address, ipv4gateway, ipv6gateway, ipv4dnsservercount, ipv4dnsserver, ipv6dnsservercount, ipv6dnsserver, ipv4mtu, ipv6mtu); } if (error) { g_task_return_error (task, error); g_object_unref (task); return; } /* Keep on */ ctx->step++; connect_context_step (task); } static void connect_set_ready (MbimDevice *device, GAsyncResult *res, GTask *task) { MMBearerMbim *self; ConnectContext *ctx; GError *error = NULL; g_autoptr(GError) inner_error = NULL; g_autoptr(MbimMessage) response = NULL; guint32 session_id; MbimActivationState activation_state; guint32 nw_error; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); response = mbim_device_command_finish (device, res, &error); if (!response) { g_task_return_error (task, error); g_object_unref (task); return; } /* always parse, because on failure we also check the NwError */ mbim_message_response_get_result (response, MBIM_MESSAGE_TYPE_COMMAND_DONE, &error); if (mbim_device_check_ms_mbimex_version (device, 3, 0)) { if (!mbim_message_ms_basic_connect_v3_connect_response_parse ( response, &session_id, &activation_state, NULL, /* voice_call_state */ &ctx->activated_ip_type, NULL, /* context_type */ &nw_error, NULL, /* media_preference */ NULL, /* access_string */ NULL, /* unnamed_ies */ &inner_error)) g_prefix_error (&inner_error, "Failed processing MBIMEx v3.0 connect response: "); else mm_obj_dbg (self, "processed MBIMEx v3.0 connect response"); } else { if (!mbim_message_connect_response_parse ( response, &session_id, &activation_state, NULL, /* voice_call_state */ &ctx->activated_ip_type, NULL, /* context_type */ &nw_error, &inner_error)) g_prefix_error (&inner_error, "Failed processing connect response: "); else mm_obj_dbg (self, "processed connect response"); } /* Prefer the error from the result to the parsing error */ if (inner_error) { if (!error) error = g_steal_pointer (&inner_error); } else { /* Report the IP type we asked for and the one returned by the modem */ nw_error = mm_broadband_modem_mbim_normalize_nw_error (ctx->modem, nw_error); mm_obj_dbg (self, "session ID '%u': %s (requested IP type: %s, activated IP type: %s, nw error: %s)", session_id, mbim_activation_state_get_string (activation_state), mbim_context_ip_type_get_string (ctx->requested_ip_type), mbim_context_ip_type_get_string (ctx->activated_ip_type), mbim_nw_error_get_string (nw_error)); /* If the response reports an ACTIVATED state, we're good even if * there is a nw_error set (e.g. asking for IPv4v6 may return a * 'pdp-type-ipv4-only-allowed' nw_error). * If the nw_error is not set (MBIM_NW_ERROR_NONE), we prefer to * return any operation error (e.g. 'OperationNotAllowed') instead * of MM_MOBILE_EQUIPMENT_ERROR_UNKNOWN ('Unknown error'). */ if (nw_error != MBIM_NW_ERROR_NONE && activation_state != MBIM_ACTIVATION_STATE_ACTIVATED && activation_state != MBIM_ACTIVATION_STATE_ACTIVATING) { g_clear_error (&error); error = mm_error_from_mbim_nw_error (nw_error, self); } } if (error) { /* A timeout when attempting to activate the request will require us to * explicitly abort the operation */ if (g_error_matches (error, MBIM_CORE_ERROR, MBIM_CORE_ERROR_TIMEOUT)) ctx->abort_on_failure = build_disconnect_message (self, ctx->mbim, ctx->session_id); g_task_return_error (task, error); g_object_unref (task); return; } /* Keep on. From now on, any additional command failure will require an * explicit disconnection */ ctx->abort_on_failure = build_disconnect_message (self, ctx->mbim, ctx->session_id); ctx->step++; connect_context_step (task); } static void ensure_disconnected_ready (MbimDevice *device, GAsyncResult *res, GTask *task) { ConnectContext *ctx; g_autoptr(MbimMessage) response = NULL; ctx = g_task_get_task_data (task); /* Ignore all errors, just go on */ response = mbim_device_command_finish (device, res, NULL); /* Keep on */ ctx->step++; connect_context_step (task); } static void check_disconnected_ready (MbimDevice *device, GAsyncResult *res, GTask *task) { MMBearerMbim *self; ConnectContext *ctx; g_autoptr(MbimMessage) response = NULL; guint32 session_id; MbimActivationState activation_state = MBIM_ACTIVATION_STATE_UNKNOWN; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); response = mbim_device_command_finish (device, res, NULL); if (response && mbim_message_response_get_result (response, MBIM_MESSAGE_TYPE_COMMAND_DONE, NULL)) { if (mbim_device_check_ms_mbimex_version (device, 3, 0)) mbim_message_ms_basic_connect_v3_connect_response_parse ( response, &session_id, &activation_state, NULL, /* voice_call_state */ NULL, /* ip_type */ NULL, /* context_type */ NULL, /* nw_error */ NULL, /* media_preference */ NULL, /* access_string */ NULL, /* unnamed_ies */ NULL); else mbim_message_connect_response_parse ( response, &session_id, &activation_state, NULL, /* voice_call_state */ NULL, /* ip_type */ NULL, /* context_type */ NULL, /* nw_error */ NULL); } if (activation_state != MBIM_ACTIVATION_STATE_UNKNOWN) mm_obj_dbg (self, "session ID '%u': %s", session_id, mbim_activation_state_get_string (activation_state)); /* Some modem (e.g. Huawei ME936) reports MBIM_ACTIVATION_STATE_UNKNOWN * when being queried for the activation state before an IP session has * been activated once. Here we expect a modem would at least tell the * truth when the session has been activated, so we proceed to deactivate * the session only the modem indicates the session has been activated or * is being activated. */ if (activation_state == MBIM_ACTIVATION_STATE_ACTIVATED || activation_state == MBIM_ACTIVATION_STATE_ACTIVATING) ctx->step = CONNECT_STEP_ENSURE_DISCONNECTED; else ctx->step = CONNECT_STEP_CONNECT; connect_context_step (task); } static void main_interface_up_ready (MMPortNet *link, GAsyncResult *res, GTask *task) { ConnectContext *ctx; GError *error = NULL; ctx = g_task_get_task_data (task); if (!mm_port_net_link_setup_finish (link, res, &error)) { g_prefix_error (&error, "Couldn't bring main interface up: "); g_task_return_error (task, error); g_object_unref (task); return; } /* Keep on */ ctx->step++; connect_context_step (task); } static void wait_link_port_ready (MMBaseModem *modem, GAsyncResult *res, GTask *task) { ConnectContext *ctx; GError *error = NULL; ctx = g_task_get_task_data (task); ctx->link = mm_base_modem_wait_link_port_finish (modem, res, &error); if (!ctx->link) { g_task_return_error (task, error); g_object_unref (task); return; } /* Keep on */ ctx->step++; connect_context_step (task); } static void setup_link_ready (MMPortMbim *mbim, GAsyncResult *res, GTask *task) { MMBearerMbim *self; ConnectContext *ctx; GError *error = NULL; g_autoptr(MMBaseModem) modem = NULL; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); ctx->link_name = mm_port_mbim_setup_link_finish (mbim, res, &ctx->session_id, &error); if (!ctx->link_name) { g_prefix_error (&error, "failed to create net link for device: "); g_task_return_error (task, error); g_object_unref (task); return; } /* From now on link_name will be set, and we'll use that to know * whether we should cleanup the link upon a connection failure */ mm_obj_msg (self, "net link %s created (session id %u)", ctx->link_name, ctx->session_id); /* Wait for the data port with the given interface name, which will be * added asynchronously */ g_object_get (self, MM_BASE_BEARER_MODEM, &modem, NULL); g_assert (modem); mm_base_modem_wait_link_port (modem, "net", ctx->link_name, WAIT_LINK_PORT_TIMEOUT_MS, (GAsyncReadyCallback) wait_link_port_ready, task); } static gboolean load_settings_from_profile (MMBearerMbim *self, ConnectContext *ctx, MM3gppProfile *profile, MMBearerApnType default_apn_type, GError **error) { MMBearerAllowedAuth bearer_auth; MMBearerApnType apn_type; GError *inner_error = NULL; g_autoptr(MMBaseModem) modem = NULL; g_object_get (self, MM_BASE_BEARER_MODEM, &modem, NULL); g_assert (modem); /* APN settings */ ctx->apn = g_strdup (mm_3gpp_profile_get_apn (profile)); apn_type = mm_3gpp_profile_get_apn_type (profile); if (apn_type == MM_BEARER_APN_TYPE_NONE) { if (default_apn_type == MM_BEARER_APN_TYPE_NONE) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "APN type in profile is not initialized"); return FALSE; } apn_type = default_apn_type; } ctx->context_type = mm_bearer_apn_type_to_mbim_context_type ( apn_type, mm_broadband_modem_mbim_is_context_type_ext_supported (MM_BROADBAND_MODEM_MBIM (modem)), self, &inner_error); if (inner_error) { g_propagate_error (error, inner_error); return FALSE; } /* Auth settings */ ctx->user = g_strdup (mm_3gpp_profile_get_user (profile)); ctx->password = g_strdup (mm_3gpp_profile_get_password (profile)); if (!ctx->user && !ctx->password) { ctx->auth = MBIM_AUTH_PROTOCOL_NONE; } else { bearer_auth = mm_3gpp_profile_get_allowed_auth (profile); ctx->auth = mm_bearer_allowed_auth_to_mbim_auth_protocol (bearer_auth, self, &inner_error); if (inner_error) { g_propagate_error (error, inner_error); return FALSE; } } /* This IP type reading is applicable only when the profile comes * from the input bearer properties, as there is no IP type stored * in the device profiles. Therefore, only read it if it hasn't been * read yet */ if (ctx->requested_ip_type == MBIM_CONTEXT_IP_TYPE_DEFAULT) { MMBearerIpFamily ip_type; ip_type = mm_3gpp_profile_get_ip_type (profile); mm_3gpp_normalize_ip_family (&ip_type, TRUE); ctx->requested_ip_type = mm_bearer_ip_family_to_mbim_context_ip_type (ip_type, &inner_error); if (inner_error) { g_propagate_error (error, inner_error); return FALSE; } } return TRUE; } static void get_profile_ready (MMIfaceModem3gppProfileManager *modem, GAsyncResult *res, GTask *task) { MMBearerMbim *self; ConnectContext *ctx; GError *error = NULL; g_autoptr(MM3gppProfile) profile = NULL; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); profile = mm_iface_modem_3gpp_profile_manager_get_profile_finish (modem, res, &error); if (!profile) { g_task_return_error (task, error); g_object_unref (task); return; } if (!load_settings_from_profile (self, ctx, profile, MM_BEARER_APN_TYPE_NONE, &error)) { g_prefix_error (&error, "Couldn't load settings from profile: "); g_task_return_error (task, error); g_object_unref (task); return; } /* Keep on */ ctx->step++; connect_context_step (task); } static void connect_context_step (GTask *task) { MMBearerMbim *self; ConnectContext *ctx; g_autoptr(MbimMessage) message = NULL; /* If cancelled, complete */ if (g_task_return_error_if_cancelled (task)) { g_object_unref (task); return; } self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); switch (ctx->step) { case CONNECT_STEP_FIRST: ctx->step++; /* Fall through */ case CONNECT_STEP_LOAD_PROFILE_SETTINGS: if (ctx->profile_id != MM_3GPP_PROFILE_ID_UNKNOWN) { mm_obj_dbg (self, "loading connection settings from profile '%d'...", ctx->profile_id); mm_iface_modem_3gpp_profile_manager_get_profile ( MM_IFACE_MODEM_3GPP_PROFILE_MANAGER (ctx->modem), ctx->profile_id, (GAsyncReadyCallback)get_profile_ready, task); return; } ctx->step++; /* Fall through */ case CONNECT_STEP_SETUP_LINK: /* if a link prefix hint is available, it's because we should be doing * multiplexing */ if (ctx->link_prefix_hint) { mm_obj_dbg (self, "setting up new multiplexed link..."); mm_port_mbim_setup_link (ctx->mbim, ctx->data, ctx->link_prefix_hint, (GAsyncReadyCallback) setup_link_ready, task); return; } ctx->step++; /* fall through */ case CONNECT_STEP_SETUP_LINK_MAIN_UP: /* if the connection is done through a new link, we need to ifup the main interface */ if (ctx->link) { mm_obj_dbg (self, "bringing main interface %s up...", mm_port_get_device (ctx->data)); mm_port_net_link_setup (MM_PORT_NET (ctx->data), TRUE, 0, /* ignore */ g_task_get_cancellable (task), (GAsyncReadyCallback) main_interface_up_ready, task); return; } ctx->step++; /* fall through */ case CONNECT_STEP_CHECK_DISCONNECTED: { MbimDevice *device; mm_obj_dbg (self, "checking if session %u is disconnected...", ctx->session_id); device = mm_port_mbim_peek_device (ctx->mbim); if (mbim_device_check_ms_mbimex_version (device, 3, 0)) message = mbim_message_ms_basic_connect_v3_connect_query_new (ctx->session_id, NULL); else message = mbim_message_connect_query_new ( ctx->session_id, MBIM_ACTIVATION_STATE_UNKNOWN, MBIM_VOICE_CALL_STATE_NONE, MBIM_CONTEXT_IP_TYPE_DEFAULT, mbim_uuid_from_context_type (MBIM_CONTEXT_TYPE_INTERNET), 0, NULL); mbim_device_command (device, message, 10, g_task_get_cancellable (task), (GAsyncReadyCallback)check_disconnected_ready, task); return; } case CONNECT_STEP_ENSURE_DISCONNECTED: mm_obj_dbg (self, "ensuring session %u is disconnected...", ctx->session_id); message = build_disconnect_message (self, ctx->mbim, ctx->session_id); mbim_device_command (mm_port_mbim_peek_device (ctx->mbim), message, MM_BASE_BEARER_DEFAULT_DISCONNECTION_TIMEOUT, g_task_get_cancellable (task), (GAsyncReadyCallback)ensure_disconnected_ready, task); return; case CONNECT_STEP_CONNECT: { MbimDevice *device; mm_obj_dbg (self, "launching %s connection in session %u...", mbim_context_ip_type_get_string (ctx->requested_ip_type), ctx->session_id); device = mm_port_mbim_peek_device (ctx->mbim); if (mbim_device_check_ms_mbimex_version (device, 3, 0)) message = mbim_message_ms_basic_connect_v3_connect_set_new ( ctx->session_id, MBIM_ACTIVATION_COMMAND_ACTIVATE, MBIM_COMPRESSION_NONE, ctx->auth, ctx->requested_ip_type, mbim_uuid_from_context_type (ctx->context_type), MBIM_ACCESS_MEDIA_TYPE_UNKNOWN, ctx->apn ? ctx->apn : "", ctx->user ? ctx->user : "", ctx->password ? ctx->password : "", NULL, /* unnamed ies */ NULL); else message = mbim_message_connect_set_new ( ctx->session_id, MBIM_ACTIVATION_COMMAND_ACTIVATE, ctx->apn ? ctx->apn : "", ctx->user ? ctx->user : "", ctx->password ? ctx->password : "", MBIM_COMPRESSION_NONE, ctx->auth, ctx->requested_ip_type, mbim_uuid_from_context_type (ctx->context_type), NULL); mbim_device_command (mm_port_mbim_peek_device (ctx->mbim), message, MM_BASE_BEARER_DEFAULT_CONNECTION_TIMEOUT, g_task_get_cancellable (task), (GAsyncReadyCallback)connect_set_ready, task); return; } case CONNECT_STEP_IP_CONFIGURATION: mm_obj_dbg (self, "querying IP configuration..."); message = mbim_message_ip_configuration_query_new ( ctx->session_id, MBIM_IP_CONFIGURATION_AVAILABLE_FLAG_NONE, /* ipv4configurationavailable */ MBIM_IP_CONFIGURATION_AVAILABLE_FLAG_NONE, /* ipv6configurationavailable */ 0, /* ipv4addresscount */ NULL, /* ipv4address */ 0, /* ipv6addresscount */ NULL, /* ipv6address */ NULL, /* ipv4gateway */ NULL, /* ipv6gateway */ 0, /* ipv4dnsservercount */ NULL, /* ipv4dnsserver */ 0, /* ipv6dnsservercount */ NULL, /* ipv6dnsserver */ 0, /* ipv4mtu */ 0, /* ipv6mtu */ NULL); mbim_device_command (mm_port_mbim_peek_device (ctx->mbim), message, 60, g_task_get_cancellable (task), (GAsyncReadyCallback)ip_configuration_query_ready, task); return; case CONNECT_STEP_IP_CONFIGURATION_ASYNC: /* At this point, we must have IP settings ready. Now, if we know the modem * supports modem-SLAAC and we're returning IPv6 settings, we must make sure * we're returning already a global IPv6 address and method STATIC, or otherwise * we need to wait for indications to happen. */ if (ip_configuration_async_setup (task)) return; ctx->step++; /* fall through */ case CONNECT_STEP_LAST: /* Cleanup the abort message so that we don't * run it */ g_clear_pointer (&ctx->abort_on_failure, mbim_message_unref); /* Port is connected; update the state */ mm_port_set_connected (ctx->link ? ctx->link : ctx->data, TRUE); /* Keep connection related data */ g_assert (self->priv->mbim == NULL); self->priv->mbim = g_object_ref (ctx->mbim); g_assert (self->priv->data == NULL); self->priv->data = ctx->data ? g_object_ref (ctx->data) : NULL; g_assert (self->priv->link == NULL); self->priv->link = ctx->link ? g_object_ref (ctx->link) : NULL; g_assert (!self->priv->session_id); self->priv->session_id = ctx->session_id; /* reset the link name to avoid cleaning up the link on context free */ g_clear_pointer (&ctx->link_name, g_free); /* Set operation result */ g_task_return_pointer ( task, mm_bearer_connect_result_ref (ctx->connect_result), (GDestroyNotify)mm_bearer_connect_result_unref); g_object_unref (task); return; default: break; } g_assert_not_reached (); } static gboolean load_settings_from_bearer (MMBearerMbim *self, ConnectContext *ctx, MMBearerProperties *props, GError **error) { MMBearerMultiplexSupport multiplex; gboolean multiplex_supported = TRUE; guint current_multiplexed_bearers; guint max_multiplexed_bearers; const gchar *data_port_driver; if (!mm_broadband_modem_get_active_multiplexed_bearers (MM_BROADBAND_MODEM (ctx->modem), ¤t_multiplexed_bearers, &max_multiplexed_bearers, error)) return FALSE; /* Check multiplex support in the kernel and the device */ data_port_driver = mm_kernel_device_get_driver (mm_port_peek_kernel_device (ctx->data)); if (!g_strcmp0 (data_port_driver, "mhi_net") || !max_multiplexed_bearers) multiplex_supported = FALSE; /* If no multiplex setting given by the user, assume none */ multiplex = mm_bearer_properties_get_multiplex (props); if (multiplex == MM_BEARER_MULTIPLEX_SUPPORT_UNKNOWN) { if (mm_context_get_test_multiplex_requested ()) multiplex = MM_BEARER_MULTIPLEX_SUPPORT_REQUESTED; else multiplex = MM_BEARER_MULTIPLEX_SUPPORT_NONE; } /* If multiplex unsupported, either abort or default to none */ if (!multiplex_supported) { if (multiplex == MM_BEARER_MULTIPLEX_SUPPORT_REQUIRED) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "Multiplexing required but not supported"); return FALSE; } if (multiplex == MM_BEARER_MULTIPLEX_SUPPORT_REQUESTED) { mm_obj_dbg (self, "Multiplexing unsupported"); multiplex = MM_BEARER_MULTIPLEX_SUPPORT_NONE; } } /* Go on with multiplexing enabled */ if (multiplex == MM_BEARER_MULTIPLEX_SUPPORT_REQUESTED || multiplex == MM_BEARER_MULTIPLEX_SUPPORT_REQUIRED) { g_assert (multiplex_supported); if (current_multiplexed_bearers == max_multiplexed_bearers) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "Maximum number of multiplexed bearers reached"); return FALSE; } /* the link prefix hint given must be modem-specific */ ctx->link_prefix_hint = g_strdup_printf ("mbimmux%u.", mm_base_modem_get_dbus_id (MM_BASE_MODEM (ctx->modem))); } /* If profile id is given, we'll load all settings from the stored profile, * so ignore any other setting received in the bearer properties */ ctx->profile_id = mm_bearer_properties_get_profile_id (props); if (ctx->profile_id != MM_3GPP_PROFILE_ID_UNKNOWN) { MMBearerIpFamily ip_type; GError *inner_error = NULL; /* If we're loading settings from a profile, still read the ip-type * from the user input, as that is not stored in the profile */ ip_type = mm_bearer_properties_get_ip_type (props); mm_3gpp_normalize_ip_family (&ip_type, FALSE); ctx->requested_ip_type = mm_bearer_ip_family_to_mbim_context_ip_type (ip_type, &inner_error); if (inner_error) { g_propagate_error (error, inner_error); return FALSE; } return TRUE; } /* Use the implicit profile settings in the bearer properties. * If not loading from a stored profile, initialize the APN type to 'internet' * (TYPE_DEFAULT) by default, which is what we've done until now. */ if (!load_settings_from_profile (self, ctx, mm_bearer_properties_peek_3gpp_profile (props), MM_BEARER_APN_TYPE_DEFAULT, error)) return FALSE; /* Is this a 3GPP only modem and no APN or profile id was given? If so, error */ if (mm_iface_modem_is_3gpp_only (MM_IFACE_MODEM (ctx->modem)) && !ctx->apn) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS, "3GPP connection logic requires APN or profile id setting"); return FALSE; } return TRUE; } static void _connect (MMBaseBearer *self, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { ConnectContext *ctx; MMPort *data; MMPortMbim *mbim; GTask *task; GError *error = NULL; g_autoptr(MMBaseModem) modem = NULL; g_autoptr(MMBearerProperties) props = NULL; if (!peek_ports (self, &mbim, &data, callback, user_data)) return; task = g_task_new (self, cancellable, callback, user_data); g_object_get (self, MM_BASE_BEARER_MODEM, &modem, MM_BASE_BEARER_CONFIG, &props, NULL); g_assert (modem); ctx = g_slice_new0 (ConnectContext); ctx->modem = MM_BROADBAND_MODEM_MBIM (g_object_ref (modem)); ctx->mbim = g_object_ref (mbim); ctx->data = g_object_ref (data); ctx->step = CONNECT_STEP_FIRST; ctx->requested_ip_type = MBIM_CONTEXT_IP_TYPE_DEFAULT; ctx->activated_ip_type = MBIM_CONTEXT_IP_TYPE_DEFAULT; g_task_set_task_data (task, ctx, (GDestroyNotify)connect_context_free); if (!load_settings_from_bearer (MM_BEARER_MBIM (self), ctx, props, &error)) { g_prefix_error (&error, "Invalid bearer properties: "); g_task_return_error (task, error); g_object_unref (task); return; } mm_obj_dbg (self, "launching %sconnection with data port (%s/%s)", ctx->link_prefix_hint ? "multiplexed " : "", mm_port_subsys_get_string (mm_port_get_subsys (data)), mm_port_get_device (data)); /* Run! */ connect_context_step (task); } /*****************************************************************************/ /* Disconnect */ typedef enum { DISCONNECT_STEP_FIRST, DISCONNECT_STEP_DISCONNECT, DISCONNECT_STEP_LAST } DisconnectStep; typedef struct { MMPortMbim *mbim; MMBroadbandModemMbim *modem; guint session_id; DisconnectStep step; } DisconnectContext; static void disconnect_context_free (DisconnectContext *ctx) { g_object_unref (ctx->mbim); g_object_unref (ctx->modem); g_slice_free (DisconnectContext, ctx); } static gboolean disconnect_finish (MMBaseBearer *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void reset_bearer_connection (MMBearerMbim *self) { if (self->priv->data) { mm_port_set_connected (self->priv->data, FALSE); g_clear_object (&self->priv->data); } if (self->priv->link) { g_assert (self->priv->mbim); /* Link is disconnected; update the state */ mm_port_set_connected (self->priv->link, FALSE); mm_port_mbim_cleanup_link (self->priv->mbim, mm_port_get_device (self->priv->link), NULL, NULL); g_clear_object (&self->priv->link); } self->priv->session_id = 0; g_clear_object (&self->priv->mbim); } static void disconnect_context_step (GTask *task); static void disconnect_set_ready (MbimDevice *device, GAsyncResult *res, GTask *task) { MMBearerMbim *self; DisconnectContext *ctx; GError *error = NULL; g_autoptr(MbimMessage) response = NULL; guint32 session_id; MbimActivationState activation_state; guint32 nw_error; g_autoptr(GError) inner_error = NULL; gboolean result = FALSE; gboolean parsed_result = FALSE; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); response = mbim_device_command_finish (device, res, &error); if (!response) goto out; result = mbim_message_response_get_result (response, MBIM_MESSAGE_TYPE_COMMAND_DONE, &error); /* Parse the response only for the cases we need to */ if (result || g_error_matches (error, MBIM_STATUS_ERROR, MBIM_STATUS_ERROR_FAILURE) || g_error_matches (error, MBIM_STATUS_ERROR, MBIM_STATUS_ERROR_CONTEXT_NOT_ACTIVATED)) { if (mbim_device_check_ms_mbimex_version (device, 3, 0)) parsed_result = mbim_message_ms_basic_connect_v3_connect_response_parse ( response, &session_id, &activation_state, NULL, /* voice_call_state */ NULL, /* ip_type */ NULL, /* context_type */ &nw_error, NULL, /* media_preference */ NULL, /* access_string */ NULL, /* unnamed_ies */ &inner_error); else parsed_result = mbim_message_connect_response_parse ( response, &session_id, &activation_state, NULL, /* voice_call_state */ NULL, /* ip_type */ NULL, /* context_type */ &nw_error, &inner_error); } /* Now handle different response / error cases */ if (result && parsed_result) { g_assert (!error); g_assert (!inner_error); mm_obj_dbg (self, "session ID '%u': %s", session_id, mbim_activation_state_get_string (activation_state)); /* success */ goto out; } if (g_error_matches (error, MBIM_STATUS_ERROR, MBIM_STATUS_ERROR_CONTEXT_NOT_ACTIVATED)) { if (parsed_result) mm_obj_dbg (self, "context not activated: session ID '%u' already disconnected", session_id); else mm_obj_dbg (self, "context not activated: already disconnected"); g_clear_error (&error); g_clear_error (&inner_error); /* success */ goto out; } if (g_error_matches (error, MBIM_STATUS_ERROR, MBIM_STATUS_ERROR_SIM_NOT_INSERTED)) { g_clear_error (&error); g_clear_error (&inner_error); mm_obj_dbg (self, "SIM card not inserted: already disconnected"); /* success */ goto out; } nw_error = mm_broadband_modem_mbim_normalize_nw_error (ctx->modem, nw_error); if (g_error_matches (error, MBIM_STATUS_ERROR, MBIM_STATUS_ERROR_FAILURE) && parsed_result && nw_error != 0) { g_assert (!inner_error); g_error_free (error); error = mm_error_from_mbim_nw_error (nw_error, self); /* error out with nw_error error */ goto out; } /* Give precedence to original error over parsing error */ if (!error && inner_error) error = g_steal_pointer (&inner_error); out: if (error) { g_task_return_error (task, error); g_object_unref (task); return; } /* Keep on */ ctx->step++; disconnect_context_step (task); } static void disconnect_context_step (GTask *task) { MMBearerMbim *self; DisconnectContext *ctx; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); switch (ctx->step) { case DISCONNECT_STEP_FIRST: ctx->step++; /* Fall through */ case DISCONNECT_STEP_DISCONNECT: { g_autoptr(MbimMessage) message = NULL; message = build_disconnect_message (self, ctx->mbim, ctx->session_id); mbim_device_command (mm_port_mbim_peek_device (ctx->mbim), message, MM_BASE_BEARER_DEFAULT_DISCONNECTION_TIMEOUT, NULL, (GAsyncReadyCallback)disconnect_set_ready, task); return; } case DISCONNECT_STEP_LAST: /* Port is disconnected; update the state */ reset_bearer_connection (self); g_task_return_boolean (task, TRUE); g_object_unref (task); return; default: g_assert_not_reached (); } } static void disconnect (MMBaseBearer *_self, GAsyncReadyCallback callback, gpointer user_data) { MMBearerMbim *self = MM_BEARER_MBIM (_self); DisconnectContext *ctx; GTask *task; g_autoptr(MMBaseModem) modem = NULL; task = g_task_new (self, NULL, callback, user_data); if ((!self->priv->data && !self->priv->link) || !self->priv->mbim) { mm_obj_dbg (self, "no need to disconnect: MBIM bearer is already disconnected"); g_task_return_boolean (task, TRUE); g_object_unref (task); return; } g_object_get (self, MM_BASE_BEARER_MODEM, &modem, NULL); g_assert (modem); mm_obj_dbg (self, "launching disconnection on data port (%s/%s)", mm_port_subsys_get_string (mm_port_get_subsys (self->priv->data)), mm_port_get_device (self->priv->data)); ctx = g_slice_new0 (DisconnectContext); ctx->modem = MM_BROADBAND_MODEM_MBIM (g_object_ref (modem)); ctx->mbim = g_object_ref (self->priv->mbim); ctx->session_id = self->priv->session_id; ctx->step = DISCONNECT_STEP_FIRST; g_task_set_task_data (task, ctx, (GDestroyNotify)disconnect_context_free); /* Run! */ disconnect_context_step (task); } /*****************************************************************************/ guint32 mm_bearer_mbim_get_session_id (MMBearerMbim *self) { g_return_val_if_fail (MM_IS_BEARER_MBIM (self), 0); return self->priv->session_id; } /*****************************************************************************/ static void report_connection_status (MMBaseBearer *self, MMBearerConnectionStatus status, const GError *connection_error) { if (status == MM_BEARER_CONNECTION_STATUS_DISCONNECTED) /* Cleanup all connection related data */ reset_bearer_connection (MM_BEARER_MBIM (self)); /* Chain up parent's report_connection_status() */ MM_BASE_BEARER_CLASS (mm_bearer_mbim_parent_class)->report_connection_status (self, status, connection_error); } /*****************************************************************************/ #if defined WITH_SUSPEND_RESUME static MMBearerConnectionStatus reload_connection_status_finish (MMBaseBearer *self, GAsyncResult *res, GError **error) { gint val; val = g_task_propagate_int (G_TASK (res), error); if (val < 0) return MM_BEARER_CONNECTION_STATUS_UNKNOWN; return (MMBearerConnectionStatus) val; } static void reload_connection_status_ready (MbimDevice *device, GAsyncResult *res, GTask *task) { MMBearerMbim *self; guint32 session_id; MbimActivationState activation_state; MMBearerConnectionStatus bearer_connection_status = MM_BEARER_CONNECTION_STATUS_UNKNOWN; GError *error = NULL; g_autoptr(MbimMessage) response = NULL; self = g_task_get_source_object (task); response = mbim_device_command_finish (device, res, &error); if (!response || !mbim_message_response_get_result (response, MBIM_MESSAGE_TYPE_COMMAND_DONE, &error)) { g_prefix_error (&error, "Cannot load session ID '%u' status: ", mm_bearer_mbim_get_session_id (MM_BEARER_MBIM (self))); g_task_return_error (task, error); g_object_unref (task); return; } if (mbim_device_check_ms_mbimex_version (device, 3, 0)) { if (!mbim_message_ms_basic_connect_v3_connect_response_parse ( response, &session_id, &activation_state, NULL, /* voice_call_state */ NULL, /* ip_type */ NULL, /* context_type */ NULL, /* nw_error */ NULL, /* media_preference */ NULL, /* access_string */ NULL, /* unnamed_ies */ &error)) g_prefix_error (&error, "Failed processing MBIMEx v3.0 connect response: "); else mm_obj_dbg (self, "processed MBIMEx v3.0 connect response"); } else { if (!mbim_message_connect_response_parse ( response, &session_id, &activation_state, NULL, /* voice_call_state */ NULL, /* ip_type */ NULL, /* context_type */ NULL, /* nw_error */ &error)) g_prefix_error (&error, "Failed processing connect response: "); else mm_obj_dbg (self, "processed connect response"); } if (error) { g_prefix_error (&error, "Cannot load session ID '%u' status: ", mm_bearer_mbim_get_session_id (MM_BEARER_MBIM (self))); g_task_return_error (task, error); g_object_unref (task); return; } mm_obj_dbg (self, "session ID '%u': %s", session_id, mbim_activation_state_get_string (activation_state)); switch (activation_state) { case MBIM_ACTIVATION_STATE_ACTIVATED: case MBIM_ACTIVATION_STATE_ACTIVATING: /* for the purposes of the sync operation, it's fine to map ACTIVATING * to CONNECTED, as we're really going to ignore that state in the actual * processing of the logic. */ bearer_connection_status = MM_BEARER_CONNECTION_STATUS_CONNECTED; break; case MBIM_ACTIVATION_STATE_DEACTIVATING: bearer_connection_status = MM_BEARER_CONNECTION_STATUS_DISCONNECTING; break; case MBIM_ACTIVATION_STATE_DEACTIVATED: bearer_connection_status = MM_BEARER_CONNECTION_STATUS_DISCONNECTED; break; case MBIM_ACTIVATION_STATE_UNKNOWN: default: break; } if (bearer_connection_status == MM_BEARER_CONNECTION_STATUS_UNKNOWN) g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Cannot load session ID '%u' status", mm_bearer_mbim_get_session_id (MM_BEARER_MBIM (self))); else g_task_return_int (task, bearer_connection_status); g_object_unref (task); } static void reload_connection_status (MMBaseBearer *self, GAsyncReadyCallback callback, gpointer user_data) { MMPortMbim *mbim; GTask *task = NULL; g_autoptr(MbimMessage) message = NULL; if (!peek_ports (self, &mbim, NULL, callback, user_data)) return; task = g_task_new (self, NULL, callback, user_data); message = mbim_message_connect_query_new (mm_bearer_mbim_get_session_id (MM_BEARER_MBIM (self)), MBIM_ACTIVATION_STATE_UNKNOWN, MBIM_VOICE_CALL_STATE_NONE, MBIM_CONTEXT_IP_TYPE_DEFAULT, mbim_uuid_from_context_type (MBIM_CONTEXT_TYPE_INTERNET), 0, NULL); mbim_device_command (mm_port_mbim_peek_device (mbim), message, 10, NULL, (GAsyncReadyCallback)reload_connection_status_ready, task); } #endif /* WITH_SUSPEND_RESUME */ /*****************************************************************************/ MMBaseBearer * mm_bearer_mbim_new (MMBroadbandModemMbim *modem, MMBearerProperties *config) { MMBaseBearer *bearer; /* The Mbim bearer inherits from MMBaseBearer (so it's not a MMBroadbandBearer) * and that means that the object is not async-initable, so we just use * g_object_new() here */ bearer = g_object_new (MM_TYPE_BEARER_MBIM, MM_BASE_BEARER_MODEM, modem, MM_BASE_BEARER_CONFIG, config, NULL); /* Only export valid bearers */ mm_base_bearer_export (bearer); return bearer; } static void mm_bearer_mbim_init (MMBearerMbim *self) { /* Initialize private data */ self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, MM_TYPE_BEARER_MBIM, MMBearerMbimPrivate); } static void set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { MMBearerMbim *self = MM_BEARER_MBIM (object); switch (prop_id) { case PROP_ASYNC_SLAAC: self->priv->async_slaac = 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) { MMBearerMbim *self = MM_BEARER_MBIM (object); switch (prop_id) { case PROP_ASYNC_SLAAC: g_value_set_boolean (value, self->priv->async_slaac); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void dispose (GObject *object) { MMBearerMbim *self = MM_BEARER_MBIM (object); reset_bearer_connection (self); G_OBJECT_CLASS (mm_bearer_mbim_parent_class)->dispose (object); } static void mm_bearer_mbim_class_init (MMBearerMbimClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); MMBaseBearerClass *base_bearer_class = MM_BASE_BEARER_CLASS (klass); g_type_class_add_private (object_class, sizeof (MMBearerMbimPrivate)); /* Virtual methods */ object_class->get_property = get_property; object_class->set_property = set_property; object_class->dispose = dispose; base_bearer_class->connect = _connect; base_bearer_class->connect_finish = connect_finish; base_bearer_class->disconnect = disconnect; base_bearer_class->disconnect_finish = disconnect_finish; base_bearer_class->report_connection_status = report_connection_status; base_bearer_class->reload_stats = reload_stats; base_bearer_class->reload_stats_finish = reload_stats_finish; base_bearer_class->load_connection_status = NULL; base_bearer_class->load_connection_status_finish = NULL; #if defined WITH_SUSPEND_RESUME base_bearer_class->reload_connection_status = reload_connection_status; base_bearer_class->reload_connection_status_finish = reload_connection_status_finish; #endif properties[PROP_ASYNC_SLAAC] = g_param_spec_boolean (MM_BEARER_MBIM_ASYNC_SLAAC, "Async SLAAC", "Whether async SLAAC updates are expected.", FALSE, G_PARAM_READWRITE); g_object_class_install_property (object_class, PROP_ASYNC_SLAAC, properties[PROP_ASYNC_SLAAC]); } ModemManager-1.23.4-dev/src/mm-bearer-mbim.h000066400000000000000000000043171456466623000204630ustar00rootroot00000000000000/* -*- 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) 2013 Aleksander Morgado */ #ifndef MM_BEARER_MBIM_H #define MM_BEARER_MBIM_H #include #include #define _LIBMM_INSIDE_MM #include #include "mm-base-bearer.h" #include "mm-broadband-modem-mbim.h" #define MM_TYPE_BEARER_MBIM (mm_bearer_mbim_get_type ()) #define MM_BEARER_MBIM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_BEARER_MBIM, MMBearerMbim)) #define MM_BEARER_MBIM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_BEARER_MBIM, MMBearerMbimClass)) #define MM_IS_BEARER_MBIM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_BEARER_MBIM)) #define MM_IS_BEARER_MBIM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_BEARER_MBIM)) #define MM_BEARER_MBIM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_BEARER_MBIM, MMBearerMbimClass)) #define MM_BEARER_MBIM_ASYNC_SLAAC "bearer-mbim-async-slaac" typedef struct _MMBearerMbim MMBearerMbim; typedef struct _MMBearerMbimClass MMBearerMbimClass; typedef struct _MMBearerMbimPrivate MMBearerMbimPrivate; struct _MMBearerMbim { MMBaseBearer parent; MMBearerMbimPrivate *priv; }; struct _MMBearerMbimClass { MMBaseBearerClass parent; }; GType mm_bearer_mbim_get_type (void); G_DEFINE_AUTOPTR_CLEANUP_FUNC (MMBearerMbim, g_object_unref) /* MBIM bearer creation implementation. * NOTE it is *not* a broadband bearer, so not async-initable */ MMBaseBearer *mm_bearer_mbim_new (MMBroadbandModemMbim *modem, MMBearerProperties *config); guint32 mm_bearer_mbim_get_session_id (MMBearerMbim *self); #endif /* MM_BEARER_MBIM_H */ ModemManager-1.23.4-dev/src/mm-bearer-qmi.c000066400000000000000000003371561456466623000203320ustar00rootroot00000000000000/* -*- 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) 2012 Google, Inc. * Copyright (C) 2015 Azimut Electronics * Copyright (c) 2022 Qualcomm Innovation Center, Inc. */ #include #include #include #include #include #include #include #include #include #define _LIBMM_INSIDE_MM #include #include "mm-iface-modem-3gpp.h" #include "mm-iface-modem.h" #include "mm-iface-modem-3gpp-profile-manager.h" #include "mm-bearer-qmi.h" #include "mm-modem-helpers-qmi.h" #include "mm-port-enums-types.h" #include "mm-log-object.h" #include "mm-modem-helpers.h" #include "mm-context.h" G_DEFINE_TYPE (MMBearerQmi, mm_bearer_qmi, MM_TYPE_BASE_BEARER) #define GLOBAL_PACKET_DATA_HANDLE 0xFFFFFFFF struct _MMBearerQmiPrivate { /* Cancellables available during a connection attempt */ GCancellable *ongoing_connect_user_cancellable; GCancellable *ongoing_connect_network_cancellable; /* State kept while connected */ MMPortQmi *qmi; gboolean explicit_qmi_open; QmiClientWds *client_ipv4; guint packet_service_status_ipv4_indication_id; guint event_report_ipv4_indication_id; guint extended_ipv4_config_change_id; QmiClientWds *client_ipv6; guint packet_service_status_ipv6_indication_id; guint event_report_ipv6_indication_id; guint extended_ipv6_config_change_id; MMPort *data; MMPort *link; guint mux_id; guint32 packet_data_handle_ipv4; guint32 packet_data_handle_ipv6; GList *pco_list; }; /*****************************************************************************/ /* Stats */ typedef enum { RELOAD_STATS_CONTEXT_STEP_FIRST, RELOAD_STATS_CONTEXT_STEP_IPV4, RELOAD_STATS_CONTEXT_STEP_IPV6, RELOAD_STATS_CONTEXT_STEP_LAST, } ReloadStatsContextStep; typedef struct { guint64 rx_bytes; guint64 tx_bytes; } ReloadStatsResult; typedef struct { QmiMessageWdsGetPacketStatisticsInput *input; ReloadStatsContextStep step; ReloadStatsResult stats; } ReloadStatsContext; static gboolean reload_stats_finish (MMBaseBearer *bearer, guint64 *rx_bytes, guint64 *tx_bytes, GAsyncResult *res, GError **error) { ReloadStatsResult *stats; stats = g_task_propagate_pointer (G_TASK (res), error); if (!stats) return FALSE; if (rx_bytes) *rx_bytes = stats->rx_bytes; if (tx_bytes) *tx_bytes = stats->tx_bytes; g_free (stats); return TRUE; } static void reload_stats_context_free (ReloadStatsContext *ctx) { qmi_message_wds_get_packet_statistics_input_unref (ctx->input); g_slice_free (ReloadStatsContext, ctx); } static void reload_stats_context_step (GTask *task); static void get_packet_statistics_ready (QmiClientWds *client, GAsyncResult *res, GTask *task) { ReloadStatsContext *ctx; GError *error = NULL; QmiMessageWdsGetPacketStatisticsOutput *output; guint64 tx_bytes_ok = 0; guint64 rx_bytes_ok = 0; ctx = g_task_get_task_data (task); output = qmi_client_wds_get_packet_statistics_finish (client, res, &error); if (!output) { g_prefix_error (&error, "QMI operation failed: "); g_task_return_error (task, error); g_object_unref (task); return; } if (!qmi_message_wds_get_packet_statistics_output_get_result (output, &error)) { g_prefix_error (&error, "Couldn't get packet statistics: "); g_task_return_error (task, error); g_object_unref (task); qmi_message_wds_get_packet_statistics_output_unref (output); return; } qmi_message_wds_get_packet_statistics_output_get_tx_bytes_ok (output, &tx_bytes_ok, NULL); qmi_message_wds_get_packet_statistics_output_get_rx_bytes_ok (output, &rx_bytes_ok, NULL); ctx->stats.rx_bytes += rx_bytes_ok; ctx->stats.tx_bytes += tx_bytes_ok; qmi_message_wds_get_packet_statistics_output_unref (output); /* Go on */ ctx->step++; reload_stats_context_step (task); } static void reload_stats_context_step (GTask *task) { MMBearerQmi *self; ReloadStatsContext *ctx; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); switch (ctx->step) { case RELOAD_STATS_CONTEXT_STEP_FIRST: ctx->step++; /* fall through */ case RELOAD_STATS_CONTEXT_STEP_IPV4: if (self->priv->client_ipv4) { qmi_client_wds_get_packet_statistics (QMI_CLIENT_WDS (self->priv->client_ipv4), ctx->input, 10, NULL, (GAsyncReadyCallback)get_packet_statistics_ready, task); return; } ctx->step++; /* fall through */ case RELOAD_STATS_CONTEXT_STEP_IPV6: if (self->priv->client_ipv6) { qmi_client_wds_get_packet_statistics (QMI_CLIENT_WDS (self->priv->client_ipv6), ctx->input, 10, NULL, (GAsyncReadyCallback)get_packet_statistics_ready, task); return; } ctx->step++; /* fall through */ case RELOAD_STATS_CONTEXT_STEP_LAST: g_task_return_pointer (task, g_memdup (&ctx->stats, sizeof (ctx->stats)), g_free); g_object_unref (task); return; default: g_assert_not_reached (); } } static void reload_stats (MMBaseBearer *self, GAsyncReadyCallback callback, gpointer user_data) { ReloadStatsContext *ctx; GTask *task; ctx = g_slice_new0 (ReloadStatsContext); ctx->input = qmi_message_wds_get_packet_statistics_input_new (); qmi_message_wds_get_packet_statistics_input_set_mask ( ctx->input, (QMI_WDS_PACKET_STATISTICS_MASK_FLAG_TX_BYTES_OK | QMI_WDS_PACKET_STATISTICS_MASK_FLAG_RX_BYTES_OK), NULL); ctx->step = RELOAD_STATS_CONTEXT_STEP_FIRST; task = g_task_new (self, NULL, callback, user_data); g_task_set_task_data (task, ctx, (GDestroyNotify)reload_stats_context_free); reload_stats_context_step (task); } /*****************************************************************************/ /* Connection status check */ typedef enum { CONNECTION_STATUS_CONTEXT_STEP_FIRST, CONNECTION_STATUS_CONTEXT_STEP_IPV4, CONNECTION_STATUS_CONTEXT_STEP_IPV6, CONNECTION_STATUS_CONTEXT_STEP_LAST, } ConnectionStatusContextStep; typedef struct { ConnectionStatusContextStep step; } ConnectionStatusContext; static MMBearerConnectionStatus reload_connection_status_finish (MMBaseBearer *self, GAsyncResult *res, GError **error) { gint val; val = g_task_propagate_int (G_TASK (res), error); if (val < 0) return MM_BEARER_CONNECTION_STATUS_UNKNOWN; return (MMBearerConnectionStatus) val; } static void connection_status_context_step (GTask *task); static void get_packet_service_status_ready (QmiClientWds *client, GAsyncResult *res, GTask *task) { GError *error = NULL; QmiMessageWdsGetPacketServiceStatusOutput *output; QmiWdsConnectionStatus status = QMI_WDS_CONNECTION_STATUS_UNKNOWN; ConnectionStatusContext *ctx; output = qmi_client_wds_get_packet_service_status_finish (client, res, &error); if (!output) goto out; if (!qmi_message_wds_get_packet_service_status_output_get_result (output, &error)) goto out; qmi_message_wds_get_packet_service_status_output_get_connection_status ( output, &status, NULL); out: if (output) qmi_message_wds_get_packet_service_status_output_unref (output); /* An error checking status is reported right away */ if (error) { g_task_return_error (task, error); g_object_unref (task); return; } /* Report disconnection right away */ if (status != QMI_WDS_CONNECTION_STATUS_CONNECTED) { g_task_return_int (task, MM_BEARER_CONNECTION_STATUS_DISCONNECTED); g_object_unref (task); return; } /* we're reported as connected, go on to next check if any */ ctx = g_task_get_task_data (task); ctx->step++; connection_status_context_step (task); } static void connection_status_context_step (GTask *task) { MMBearerQmi *self; ConnectionStatusContext *ctx; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); switch (ctx->step) { case CONNECTION_STATUS_CONTEXT_STEP_FIRST: /* If no clients ready on start, assume disconnected */ if (!self->priv->client_ipv4 && !self->priv->client_ipv6) { g_task_return_int (task, MM_BEARER_CONNECTION_STATUS_DISCONNECTED); g_object_unref (task); return; } ctx->step++; /* fall through */ case CONNECTION_STATUS_CONTEXT_STEP_IPV4: if (self->priv->client_ipv4) { qmi_client_wds_get_packet_service_status (self->priv->client_ipv4, NULL, 10, NULL, (GAsyncReadyCallback)get_packet_service_status_ready, task); return; } ctx->step++; /* fall through */ case CONNECTION_STATUS_CONTEXT_STEP_IPV6: if (self->priv->client_ipv6) { qmi_client_wds_get_packet_service_status (self->priv->client_ipv6, NULL, 10, NULL, (GAsyncReadyCallback)get_packet_service_status_ready, task); return; } ctx->step++; /* fall through */ case CONNECTION_STATUS_CONTEXT_STEP_LAST: /* All available clients are connected */ g_task_return_int (task, MM_BEARER_CONNECTION_STATUS_CONNECTED); g_object_unref (task); return; default: g_assert_not_reached (); } } static void reload_connection_status (MMBaseBearer *self, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; ConnectionStatusContext *ctx; ctx = g_new (ConnectionStatusContext, 1); ctx->step = CONNECTION_STATUS_CONTEXT_STEP_FIRST; task = g_task_new (self, NULL, callback, user_data); g_task_set_task_data (task, ctx, g_free); connection_status_context_step (task); } /*****************************************************************************/ /* Connection status polling */ static MMBearerConnectionStatus load_connection_status_finish (MMBaseBearer *self, GAsyncResult *res, GError **error) { gint val; val = g_task_propagate_int (G_TASK (res), error); if (val < 0) return MM_BEARER_CONNECTION_STATUS_UNKNOWN; return (MMBearerConnectionStatus) val; } static void reload_connection_status_ready (MMBaseBearer *self, GAsyncResult *res, GTask *task) { MMBearerConnectionStatus status; GError *error = NULL; status = reload_connection_status_finish (self, res, &error); if (status == MM_BEARER_CONNECTION_STATUS_UNKNOWN) g_task_return_error (task, error); else g_task_return_int (task, MM_BEARER_CONNECTION_STATUS_CONNECTED); g_object_unref (task); } static void load_connection_status (MMBaseBearer *_self, GAsyncReadyCallback callback, gpointer user_data) { MMBearerQmi *self = MM_BEARER_QMI (_self); GTask *task; task = g_task_new (self, NULL, callback, user_data); /* Connection status polling is an optional feature that must be * enabled explicitly via udev tags. If not set, out as unsupported. * Note that when connected via a muxed link, the udev tag should be * checked on the main interface (lower device) */ if ((self->priv->data && !mm_kernel_device_get_global_property_as_boolean (mm_port_peek_kernel_device (self->priv->data), "ID_MM_QMI_CONNECTION_STATUS_POLLING_ENABLE")) || (self->priv->link && !mm_kernel_device_get_global_property_as_boolean (mm_kernel_device_peek_lower_device (mm_port_peek_kernel_device (self->priv->link)), "ID_MM_QMI_CONNECTION_STATUS_POLLING_ENABLE"))) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "Connection status polling not required"); g_object_unref (task); return; } reload_connection_status (_self, (GAsyncReadyCallback)reload_connection_status_ready, task); } /*****************************************************************************/ /* Connect */ #define WAIT_LINK_PORT_TIMEOUT_MS 2500 static void common_setup_cleanup_packet_service_status_unsolicited_events (MMBearerQmi *self, QmiClientWds *client, gboolean enable, guint *indication_id); static void setup_event_report_unsolicited_events (MMBearerQmi *self, QmiClientWds *client, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); static void cleanup_event_report_unsolicited_events (MMBearerQmi *self, QmiClientWds *client, guint *indication_id); typedef enum { CONNECT_STEP_FIRST, CONNECT_STEP_LOAD_PROFILE_SETTINGS, CONNECT_STEP_OPEN_QMI_PORT, CONNECT_STEP_SETUP_DATA_FORMAT, CONNECT_STEP_SETUP_LINK, CONNECT_STEP_SETUP_LINK_MAIN_UP, CONNECT_STEP_IP_METHOD, CONNECT_STEP_IPV4, CONNECT_STEP_WDS_CLIENT_IPV4, CONNECT_STEP_BIND_DATA_PORT_IPV4, CONNECT_STEP_IP_FAMILY_IPV4, CONNECT_STEP_ENABLE_INDICATIONS_IPV4, CONNECT_STEP_START_NETWORK_IPV4, CONNECT_STEP_ENABLE_WDS_INDICATIONS_IPV4, CONNECT_STEP_GET_CURRENT_SETTINGS_IPV4, CONNECT_STEP_IPV6, CONNECT_STEP_WDS_CLIENT_IPV6, CONNECT_STEP_BIND_DATA_PORT_IPV6, CONNECT_STEP_IP_FAMILY_IPV6, CONNECT_STEP_ENABLE_INDICATIONS_IPV6, CONNECT_STEP_START_NETWORK_IPV6, CONNECT_STEP_ENABLE_WDS_INDICATIONS_IPV6, CONNECT_STEP_GET_CURRENT_SETTINGS_IPV6, CONNECT_STEP_LAST } ConnectStep; typedef struct { MMBearerQmi *self; MMBaseModem *modem; ConnectStep step; MMPort *data; MMPortQmi *qmi; MMQmiDataEndpoint endpoint; gboolean sio_port_failed; gint profile_id; MMBearerIpMethod ip_method; gboolean explicit_qmi_open; gchar *user; gchar *password; gchar *apn; QmiWdsAuthentication auth; gboolean no_ip_family_preference; MMBearerMultiplexSupport multiplex; QmiWdaDataAggregationProtocol dap; guint mux_id; gchar *link_prefix_hint; gchar *link_name; MMPort *link; gboolean ipv4; gboolean running_ipv4; QmiClientWds *client_ipv4; guint packet_service_status_ipv4_indication_id; guint event_report_ipv4_indication_id; guint32 packet_data_handle_ipv4; MMBearerIpConfig *ipv4_config; GError *error_ipv4; gboolean ipv6; gboolean running_ipv6; QmiClientWds *client_ipv6; guint packet_service_status_ipv6_indication_id; guint event_report_ipv6_indication_id; guint32 packet_data_handle_ipv6; MMBearerIpConfig *ipv6_config; GError *error_ipv6; guint extended_ipv4_config_change_id; guint extended_ipv6_config_change_id; } ConnectContext; /* When using the WDS service, we may not only want to have explicit different * clients for IPv4 or IPv6, but also for different mux ids/endpoints as well, * so that different bearer objects never attempt to use the same WDS clients. */ #define MM_BEARER_QMI_PORT_FLAG(flag, ctx) \ (((ctx->endpoint.interface_number & 0xFF) << 24) | \ ((ctx->endpoint.type & 0xFF) << 16) | \ ((ctx->mux_id & 0xFF) << 8) | (flag & 0xFF)) /*****************************************************************************/ static void process_operator_reserved_pco (MMBearerQmi *self, QmiMessageWdsGetCurrentSettingsOutput *output) { MMBaseModem *modem = NULL; MMPco *pco; g_autofree gchar *app_specific_info_str = NULL; GArray *array = NULL; g_autoptr(GByteArray) pco_raw = NULL; guint16 container_id; guint16 tmp_mcc; guint16 tmp_mnc; gboolean mnc_includes_pcs_digit; guint8 pco_prefix[9]; gsize pco_raw_len; if (!qmi_message_wds_get_current_settings_output_get_operator_reserved_pco ( output, &tmp_mcc, &tmp_mnc, &mnc_includes_pcs_digit, &array, &container_id, NULL)) return; /* Ignore PCOs with undefined contents */ if (!tmp_mcc && !tmp_mnc && !container_id && !array->len) return; app_specific_info_str = ((array->len > 0) ? mm_utils_bin2hexstr ((guint8*) (array->data), array->len) : NULL); mm_obj_dbg (self, "container ID: %d", container_id); mm_obj_dbg (self, "app specific info: %s", app_specific_info_str ? app_specific_info_str : "n/a"); pco_raw_len = sizeof (pco_prefix) + array->len; pco_prefix[0] = 0x27; pco_prefix[1] = pco_raw_len - 2; pco_prefix[2] = 0x80; pco_prefix[3] = (container_id >> 8) & 0xFF; pco_prefix[4] = container_id & 0xFF; pco_prefix[5] = 3 * sizeof (guint8) + array->len; /* if MNC consist of 3 digits * pco_prefix[7] = 0x * if MNC consist of 2 digits * pco_prefix[7] = 0xF * pco_prefix[6] = 0x * pco_prefix[8] = 0x * * e.g. from MCCMNC 311480 (MCC=311, MNC=480 with PCS digit), logic would do * pco_prefix[7] = 0x01 | (0x00 << 4) = 0x01 * pco_prefix[6] = 0x03 | (0x01 << 4) = 0x13 * pco_prefix[6] = 0x04 | (0x08 << 4) = 0x84 * And so the `pco_prefix` includes bytes `13:01:84` when the operator is 311480 (Verizon) * * See 3GPP TS 24.008, subclause 10.5.6.3.1 (Protocol Configuration Options) and * 10.5.1.3 for more details on the coding of MCC and MNC. */ if (mnc_includes_pcs_digit) { pco_prefix[7] = (guint8)(tmp_mcc%10) | ((guint8)(tmp_mnc%10) << 4); tmp_mnc /= 10; } else pco_prefix[7] = (guint8)(tmp_mcc%10) | 0xF0; tmp_mcc /= 10; pco_prefix[6] = (guint8)(tmp_mcc/10) | ((guint8)(tmp_mcc%10) << 4); pco_prefix[8] = (guint8)(tmp_mnc/10) | ((guint8)(tmp_mnc%10) << 4); pco_raw = g_byte_array_sized_new (pco_raw_len); g_byte_array_append (pco_raw, pco_prefix, sizeof (pco_prefix)); if (array->len > 0) g_byte_array_append (pco_raw, (const guint8 *)array->data, array->len); pco = mm_pco_new (); /* set session ID to 0 (default) */ mm_pco_set_session_id (pco, 0); mm_pco_set_complete (pco, TRUE); mm_pco_set_data (pco, pco_raw->data, pco_raw->len); /* mm_pco_list_add API takes care of duplicate entry */ self->priv->pco_list = mm_pco_list_add (self->priv->pco_list, pco); g_object_get (self, MM_BASE_BEARER_MODEM, &modem, NULL); mm_iface_modem_3gpp_update_pco_list (MM_IFACE_MODEM_3GPP (modem), self->priv->pco_list); mm_obj_dbg (self, "pco info sent successfully"); g_object_unref (modem); } static void get_pco_settings_ready (QmiClientWds *client, GAsyncResult *res, MMBearerQmi *self) { g_autoptr(QmiMessageWdsGetCurrentSettingsOutput) output = NULL; GError *error = NULL; output = qmi_client_wds_get_current_settings_finish (client, res, &error); if (!output) { mm_obj_warn (self, "error: operation failed: %s", error->message); g_error_free (error); g_object_unref (self); return; } if (!qmi_message_wds_get_current_settings_output_get_result (output, &error)) { mm_obj_warn (self, "error: couldn't get current settings: %s", error->message); g_error_free (error); g_object_unref (self); return; } process_operator_reserved_pco (self, output); g_object_unref (self); } static void fetch_pco_data_from_modem (QmiClientWds *client, MMBearerQmi *self) { QmiMessageWdsGetCurrentSettingsInput *input; input = qmi_message_wds_get_current_settings_input_new (); qmi_message_wds_get_current_settings_input_set_requested_settings ( input, QMI_WDS_REQUESTED_SETTINGS_OPERATOR_RESERVED_PCO, NULL); mm_obj_dbg (self, "Getting PCO Information from Modem"); qmi_client_wds_get_current_settings (client, input, 10, NULL, (GAsyncReadyCallback) get_pco_settings_ready, g_object_ref (self)); qmi_message_wds_get_current_settings_input_unref (input); } static void extended_ip_config_indication_received (QmiClientWds *client, QmiIndicationWdsExtendedIpConfigOutput *output, MMBearerQmi *self) { QmiWdsRequestedSettings mask; g_autofree gchar *mask_str = NULL; if (!qmi_indication_wds_extended_ip_config_output_get_changed_ip_configuration (output, &mask, NULL)) return; mask_str = qmi_wds_requested_settings_build_string_from_mask (mask); mm_obj_dbg (self, "received extended ip type mask %s", mask_str); if (mask & QMI_WDS_REQUESTED_SETTINGS_OPERATOR_RESERVED_PCO) fetch_pco_data_from_modem (client, self); } /*****************************************************************************/ static void connect_context_free (ConnectContext *ctx) { g_free (ctx->apn); g_free (ctx->user); g_free (ctx->password); if (ctx->client_ipv4) { if (ctx->packet_service_status_ipv4_indication_id) { common_setup_cleanup_packet_service_status_unsolicited_events (ctx->self, ctx->client_ipv4, FALSE, &ctx->packet_service_status_ipv4_indication_id); } if (ctx->event_report_ipv4_indication_id) { cleanup_event_report_unsolicited_events (ctx->self, ctx->client_ipv4, &ctx->event_report_ipv4_indication_id); } if (ctx->extended_ipv4_config_change_id) { g_signal_handler_disconnect (ctx->client_ipv4, ctx->extended_ipv4_config_change_id); ctx->extended_ipv4_config_change_id = 0; } if (ctx->packet_data_handle_ipv4) { g_autoptr(QmiMessageWdsStopNetworkInput) input = NULL; input = qmi_message_wds_stop_network_input_new (); qmi_message_wds_stop_network_input_set_packet_data_handle (input, ctx->packet_data_handle_ipv4, NULL); qmi_client_wds_stop_network (ctx->client_ipv4, input, MM_BASE_BEARER_DEFAULT_DISCONNECTION_TIMEOUT, NULL, NULL, NULL); } g_clear_object (&ctx->client_ipv4); } if (ctx->client_ipv6) { if (ctx->packet_service_status_ipv6_indication_id) { common_setup_cleanup_packet_service_status_unsolicited_events (ctx->self, ctx->client_ipv6, FALSE, &ctx->packet_service_status_ipv6_indication_id); } if (ctx->event_report_ipv6_indication_id) { cleanup_event_report_unsolicited_events (ctx->self, ctx->client_ipv6, &ctx->event_report_ipv6_indication_id); } if (ctx->extended_ipv6_config_change_id) { g_signal_handler_disconnect (ctx->client_ipv6, ctx->extended_ipv6_config_change_id); ctx->extended_ipv6_config_change_id = 0; } if (ctx->packet_data_handle_ipv6) { g_autoptr(QmiMessageWdsStopNetworkInput) input = NULL; input = qmi_message_wds_stop_network_input_new (); qmi_message_wds_stop_network_input_set_packet_data_handle (input, ctx->packet_data_handle_ipv6, NULL); qmi_client_wds_stop_network (ctx->client_ipv6, input, MM_BASE_BEARER_DEFAULT_DISCONNECTION_TIMEOUT, NULL, NULL, NULL); } g_clear_object (&ctx->client_ipv6); } if (ctx->link_name) { mm_port_qmi_cleanup_link (ctx->qmi, ctx->link_name, ctx->mux_id, NULL, NULL); g_free (ctx->link_name); } g_clear_object (&ctx->link); g_free (ctx->link_prefix_hint); if (ctx->explicit_qmi_open) mm_port_qmi_close (ctx->qmi, NULL, NULL); g_clear_error (&ctx->error_ipv4); g_clear_error (&ctx->error_ipv6); g_clear_object (&ctx->ipv4_config); g_clear_object (&ctx->ipv6_config); g_clear_object (&ctx->data); g_clear_object (&ctx->qmi); g_clear_object (&ctx->modem); g_clear_object (&ctx->self); g_slice_free (ConnectContext, ctx); } static MMBearerConnectResult * connect_finish (MMBaseBearer *self, GAsyncResult *res, GError **error) { return g_task_propagate_pointer (G_TASK (res), error); } static void complete_connect (GTask *task, MMBearerConnectResult *result, GError *error) { ConnectContext *ctx; g_assert (result || error); g_assert (!(result && error)); ctx = g_task_get_task_data (task); g_clear_object (&ctx->self->priv->ongoing_connect_user_cancellable); g_clear_object (&ctx->self->priv->ongoing_connect_network_cancellable); if (error) g_task_return_error (task, error); else g_task_return_pointer (task, result, (GDestroyNotify)mm_bearer_connect_result_unref); g_object_unref (task); } static void connect_context_step (GTask *task); static void qmi_inet4_ntop (guint32 address, char *buf, const gsize buflen) { struct in_addr a = { .s_addr = GUINT32_TO_BE (address) }; g_assert (buflen >= INET_ADDRSTRLEN); /* We can ignore inet_ntop() return value if 'buf' is * at least INET_ADDRSTRLEN in size. */ memset (buf, 0, buflen); g_assert (inet_ntop (AF_INET, &a, buf, buflen)); } static MMBearerIpConfig * get_ipv4_config (MMBearerQmi *self, MMBearerIpMethod ip_method, QmiMessageWdsGetCurrentSettingsOutput *output, guint32 mtu) { MMBearerIpConfig *config; char buf[INET_ADDRSTRLEN]; char buf2[INET_ADDRSTRLEN]; const gchar *dns[3] = { 0 }; guint dns_idx = 0; guint32 addr = 0; GError *error = NULL; guint32 prefix = 0; /* IPv4 subnet mask */ if (!qmi_message_wds_get_current_settings_output_get_ipv4_gateway_subnet_mask (output, &addr, &error)) { mm_obj_warn (self, "failed to read IPv4 netmask: %s", error->message); g_clear_error (&error); return NULL; } qmi_inet4_ntop (addr, buf, sizeof (buf)); prefix = mm_netmask_to_cidr (buf); /* IPv4 address */ if (!qmi_message_wds_get_current_settings_output_get_ipv4_address (output, &addr, &error)) { mm_obj_warn (self, "IPv4 family but no IPv4 address: %s", error->message); g_clear_error (&error); return NULL; } mm_obj_msg (self, "QMI IPv4 Settings:"); config = mm_bearer_ip_config_new (); mm_bearer_ip_config_set_method (config, ip_method); /* IPv4 address */ qmi_inet4_ntop (addr, buf, sizeof (buf)); mm_bearer_ip_config_set_address (config, buf); mm_bearer_ip_config_set_prefix (config, prefix); mm_obj_msg (self, " address: %s/%d", buf, prefix); /* IPv4 gateway address */ if (qmi_message_wds_get_current_settings_output_get_ipv4_gateway_address (output, &addr, &error)) { qmi_inet4_ntop (addr, buf, sizeof (buf)); mm_bearer_ip_config_set_gateway (config, buf); mm_obj_msg (self, " gateway: %s", buf); } else { mm_obj_msg (self, " gateway: failed (%s)", error->message); g_clear_error (&error); } /* IPv4 DNS #1 */ if (qmi_message_wds_get_current_settings_output_get_primary_ipv4_dns_address (output, &addr, &error)) { qmi_inet4_ntop (addr, buf, sizeof (buf)); dns[dns_idx++] = buf; mm_obj_msg (self, " DNS #1: %s", buf); } else { mm_obj_msg (self, " DNS #1: failed (%s)", error->message); g_clear_error (&error); } /* IPv4 DNS #2 */ if (qmi_message_wds_get_current_settings_output_get_secondary_ipv4_dns_address (output, &addr, &error)) { qmi_inet4_ntop (addr, buf2, sizeof (buf2)); dns[dns_idx++] = buf2; mm_obj_msg (self, " DNS #2: %s", buf2); } else { mm_obj_msg (self, " DNS #2: failed (%s)", error->message); g_clear_error (&error); } if (dns_idx > 0) mm_bearer_ip_config_set_dns (config, (const gchar **) &dns); if (mtu) { mm_bearer_ip_config_set_mtu (config, mtu); mm_obj_msg (self, " MTU: %d", mtu); } return config; } static void qmi_inet6_ntop (GArray *array, char *buf, const gsize buflen) { struct in6_addr a; guint32 i; g_assert (array); g_assert (array->len == 8); g_assert (buflen >= INET6_ADDRSTRLEN); for (i = 0; i < array->len; i++) a.s6_addr16[i] = GUINT16_TO_BE (g_array_index (array, guint16, i)); /* We can ignore inet_ntop() return value if 'buf' is * at least INET6_ADDRSTRLEN in size. */ memset (buf, 0, buflen); g_assert (inet_ntop (AF_INET6, &a, buf, buflen)); } static MMBearerIpConfig * get_ipv6_config (MMBearerQmi *self, MMBearerIpMethod ip_method, QmiMessageWdsGetCurrentSettingsOutput *output, guint32 mtu) { MMBearerIpConfig *config; char buf[INET6_ADDRSTRLEN]; char buf2[INET6_ADDRSTRLEN]; const gchar *dns[3] = { 0 }; guint dns_idx = 0; GArray *array; GError *error = NULL; guint8 prefix = 0; /* If the message has an IPv6 address, create an IPv6 bearer config */ if (!qmi_message_wds_get_current_settings_output_get_ipv6_address (output, &array, &prefix, &error)) { mm_obj_warn (self, "IPv6 family but no IPv6 address: %s", error->message); g_clear_error (&error); return NULL; } mm_obj_msg (self, "QMI IPv6 Settings:"); config = mm_bearer_ip_config_new (); mm_bearer_ip_config_set_method (config, ip_method); /* IPv6 address */ qmi_inet6_ntop (array, buf, sizeof (buf)); mm_bearer_ip_config_set_address (config, buf); mm_bearer_ip_config_set_prefix (config, prefix); mm_obj_msg (self, " address: %s/%d", buf, prefix); /* IPv6 gateway address */ if (qmi_message_wds_get_current_settings_output_get_ipv6_gateway_address (output, &array, &prefix, &error)) { qmi_inet6_ntop (array, buf, sizeof (buf)); mm_bearer_ip_config_set_gateway (config, buf); mm_obj_msg (self, " gateway: %s/%d", buf, prefix); } else { mm_obj_msg (self, " gateway: failed (%s)", error->message); g_clear_error (&error); } /* IPv6 DNS #1 */ if (qmi_message_wds_get_current_settings_output_get_ipv6_primary_dns_address (output, &array, &error)) { qmi_inet6_ntop (array, buf, sizeof (buf)); dns[dns_idx++] = buf; mm_obj_msg (self, " DNS #1: %s", buf); } else { mm_obj_msg (self, " DNS #1: failed (%s)", error->message); g_clear_error (&error); } /* IPv6 DNS #2 */ if (qmi_message_wds_get_current_settings_output_get_ipv6_secondary_dns_address (output, &array, &error)) { qmi_inet6_ntop (array, buf2, sizeof (buf2)); dns[dns_idx++] = buf2; mm_obj_msg (self, " DNS #2: %s", buf2); } else { mm_obj_msg (self, " DNS #2: failed (%s)", error->message); g_clear_error (&error); } if (dns_idx > 0) mm_bearer_ip_config_set_dns (config, (const gchar **) &dns); if (mtu) { mm_bearer_ip_config_set_mtu (config, mtu); mm_obj_msg (self, " MTU: %d", mtu); } return config; } static void get_current_settings_ready (QmiClientWds *client, GAsyncResult *res, GTask *task) { MMBearerQmi *self; ConnectContext *ctx; GError *error = NULL; QmiMessageWdsGetCurrentSettingsOutput *output; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); g_assert (ctx->running_ipv4 || ctx->running_ipv6); output = qmi_client_wds_get_current_settings_finish (client, res, &error); if (!output || !qmi_message_wds_get_current_settings_output_get_result (output, &error)) { MMBearerIpConfig *config; /* When we're using static IP address, the current settings are mandatory */ if (ctx->ip_method == MM_BEARER_IP_METHOD_STATIC) { mm_obj_warn (self, "failed to retrieve mandatory IP settings: %s", error->message); if (output) qmi_message_wds_get_current_settings_output_unref (output); complete_connect (task, NULL, error); return; } /* Otherwise, just go on as we're asking for DHCP */ mm_obj_dbg (self, "couldn't get current settings: %s", error->message); g_error_free (error); config = mm_bearer_ip_config_new (); mm_bearer_ip_config_set_method (config, ctx->ip_method); if (ctx->running_ipv4) ctx->ipv4_config = config; else if (ctx->running_ipv6) ctx->ipv6_config = config; else g_assert_not_reached (); } else { QmiWdsIpFamily ip_family = QMI_WDS_IP_FAMILY_UNSPECIFIED; guint32 mtu = 0; GArray *array; if (!qmi_message_wds_get_current_settings_output_get_ip_family (output, &ip_family, &error)) { mm_obj_dbg (self, " IP Family: failed (%s); assuming IPv4", error->message); g_clear_error (&error); ip_family = QMI_WDS_IP_FAMILY_IPV4; } mm_obj_dbg (self, " IP Family: %s", (ip_family == QMI_WDS_IP_FAMILY_IPV4) ? "IPv4" : (ip_family == QMI_WDS_IP_FAMILY_IPV6) ? "IPv6" : "unknown"); if (!qmi_message_wds_get_current_settings_output_get_mtu (output, &mtu, &error)) { mm_obj_dbg (self, " MTU: failed (%s)", error->message); g_clear_error (&error); } if (ip_family == QMI_WDS_IP_FAMILY_IPV4) ctx->ipv4_config = get_ipv4_config (ctx->self, ctx->ip_method, output, mtu); else if (ip_family == QMI_WDS_IP_FAMILY_IPV6) ctx->ipv6_config = get_ipv6_config (ctx->self, ctx->ip_method, output, mtu); /* Domain names */ if (qmi_message_wds_get_current_settings_output_get_domain_name_list (output, &array, &error)) { GString *s = g_string_sized_new (array ? (array->len * 20) : 1); guint i; for (i = 0; array && (i < array->len); i++) { if (s->len) g_string_append (s, ", "); g_string_append (s, g_array_index (array, const char *, i)); } mm_obj_dbg (self, " domains: %s", s->str); g_string_free (s, TRUE); } else { mm_obj_dbg (self, " domains: failed (%s)", error ? error->message : "unknown"); g_clear_error (&error); } process_operator_reserved_pco (self, output); } if (output) qmi_message_wds_get_current_settings_output_unref (output); /* Keep on */ ctx->step++; connect_context_step (task); } static void get_current_settings (GTask *task, QmiClientWds *client) { ConnectContext *ctx; QmiMessageWdsGetCurrentSettingsInput *input; QmiWdsRequestedSettings requested; ctx = g_task_get_task_data (task); g_assert (ctx->running_ipv4 || ctx->running_ipv6); requested = QMI_WDS_REQUESTED_SETTINGS_DNS_ADDRESS | QMI_WDS_REQUESTED_SETTINGS_GRANTED_QOS | QMI_WDS_REQUESTED_SETTINGS_IP_ADDRESS | QMI_WDS_REQUESTED_SETTINGS_GATEWAY_INFO | QMI_WDS_REQUESTED_SETTINGS_MTU | QMI_WDS_REQUESTED_SETTINGS_DOMAIN_NAME_LIST | QMI_WDS_REQUESTED_SETTINGS_IP_FAMILY | QMI_WDS_REQUESTED_SETTINGS_OPERATOR_RESERVED_PCO; input = qmi_message_wds_get_current_settings_input_new (); qmi_message_wds_get_current_settings_input_set_requested_settings (input, requested, NULL); qmi_client_wds_get_current_settings (client, input, 10, g_task_get_cancellable (task), (GAsyncReadyCallback)get_current_settings_ready, task); qmi_message_wds_get_current_settings_input_unref (input); } static void wds_indication_register_response_ready (QmiClientWds *client, GAsyncResult *res, GTask *task) { MMBearerQmi *self; ConnectContext *ctx; QmiMessageWdsIndicationRegisterOutput *output; GError *error = NULL; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); output = qmi_client_wds_indication_register_finish (client, res, &error); if (!output) { mm_obj_warn (self, "error: operation failed: %s", error->message); g_error_free (error); ctx->step++; connect_context_step (task); return; } if (!qmi_message_wds_indication_register_output_get_result (output, &error)) { mm_obj_warn (self, "error: could not register for indication: %s", error->message); qmi_message_wds_indication_register_output_unref (output); g_error_free (error); ctx->step++; connect_context_step (task); return; } qmi_message_wds_indication_register_output_unref (output); if (ctx->running_ipv4) { mm_obj_dbg (self, "v4 extended ip config indication registered successfully"); g_assert (ctx->extended_ipv4_config_change_id == 0); ctx->extended_ipv4_config_change_id = g_signal_connect (client, "extended-ip-config", G_CALLBACK (extended_ip_config_indication_received), self); } else { mm_obj_dbg (self, "v6 extended ip Config indication registered successfully"); g_assert (ctx->extended_ipv6_config_change_id == 0); ctx->extended_ipv6_config_change_id = g_signal_connect (client, "extended-ip-config", G_CALLBACK (extended_ip_config_indication_received), self); } ctx->step++; connect_context_step (task); } static void register_for_wds_indication (ConnectContext *ctx, GTask *task) { QmiMessageWdsIndicationRegisterInput *input; QmiClientWds *client; MMBearerQmi *self; input = qmi_message_wds_indication_register_input_new (); self = g_task_get_source_object (task); if (ctx->running_ipv4) { client = ctx->client_ipv4; mm_obj_dbg (self, "registering for wds extended ip V4 info indication"); } else { client = ctx->client_ipv6; mm_obj_dbg (self, "registering for wds extended ip V6 info indication"); } qmi_message_wds_indication_register_input_set_report_extended_ip_configuration_change (input, TRUE, NULL); qmi_client_wds_indication_register ( client, input, 10, g_task_get_cancellable (task), (GAsyncReadyCallback) wds_indication_register_response_ready, task); qmi_message_wds_indication_register_input_unref (input); } static GError * error_from_start_network_output (MMBearerQmi *self, gboolean running_ipv4, QmiMessageWdsStartNetworkOutput *output) { QmiWdsCallEndReason cer; QmiWdsVerboseCallEndReasonType verbose_cer_type; gint16 verbose_cer_reason; if (qmi_message_wds_start_network_output_get_verbose_call_end_reason ( output, &verbose_cer_type, &verbose_cer_reason, NULL)) { return mm_error_from_wds_verbose_call_end_reason (verbose_cer_type, verbose_cer_reason, running_ipv4 ? MM_BEARER_IP_FAMILY_IPV4 : MM_BEARER_IP_FAMILY_IPV6, self); } if (qmi_message_wds_start_network_output_get_call_end_reason ( output, &cer, NULL)) { const gchar *cer_str; cer_str = qmi_wds_call_end_reason_get_string (cer); mm_obj_msg (self, " call end reason (%u): %s", cer, cer_str); return g_error_new (MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_UNKNOWN, "Call failed: %s", cer_str); } return g_error_new_literal (MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_UNKNOWN, "Call failed"); } static void start_network_ready (QmiClientWds *client, GAsyncResult *res, GTask *task) { MMBearerQmi *self; ConnectContext *ctx; GError *error = NULL; QmiMessageWdsStartNetworkOutput *output; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); g_assert (ctx->running_ipv4 || ctx->running_ipv6); g_assert (!(ctx->running_ipv4 && ctx->running_ipv6)); output = qmi_client_wds_start_network_finish (client, res, &error); if (output && !qmi_message_wds_start_network_output_get_result (output, &error)) { /* No-effect errors should be ignored. The modem will keep the * connection active as long as there is a WDS client which requested * to start the network. If ModemManager crashed while a connection was * active, we would be leaving an unreleased WDS client around and the * modem would just keep connected. */ if (g_error_matches (error, QMI_PROTOCOL_ERROR, QMI_PROTOCOL_ERROR_NO_EFFECT)) { g_clear_error (&error); if (ctx->running_ipv4) ctx->packet_data_handle_ipv4 = GLOBAL_PACKET_DATA_HANDLE; else ctx->packet_data_handle_ipv6 = GLOBAL_PACKET_DATA_HANDLE; /* Fall down to a successful connection */ } else { mm_obj_msg (self, "couldn't start %s network: %s", ctx->running_ipv4 ? "IPv4" : "IPv6", error->message); if (g_error_matches (error, QMI_PROTOCOL_ERROR, QMI_PROTOCOL_ERROR_CALL_FAILED)) { g_clear_error (&error); error = error_from_start_network_output (self, ctx->running_ipv4, output); } } } if (error) { if (ctx->running_ipv4) ctx->error_ipv4 = error; else ctx->error_ipv6 = error; } else { if (ctx->running_ipv4) qmi_message_wds_start_network_output_get_packet_data_handle (output, &ctx->packet_data_handle_ipv4, NULL); else qmi_message_wds_start_network_output_get_packet_data_handle (output, &ctx->packet_data_handle_ipv6, NULL); } if (output) qmi_message_wds_start_network_output_unref (output); /* Keep on */ ctx->step++; connect_context_step (task); } static QmiMessageWdsStartNetworkInput * build_start_network_input (ConnectContext *ctx) { QmiMessageWdsStartNetworkInput *input; g_assert (ctx->running_ipv4 || ctx->running_ipv6); g_assert (!(ctx->running_ipv4 && ctx->running_ipv6)); input = qmi_message_wds_start_network_input_new (); /* When requesting to connect through a profile, add the profile-id setting */ if (ctx->profile_id != MM_3GPP_PROFILE_ID_UNKNOWN) { g_assert (ctx->profile_id <= (gint)G_MAXUINT8); qmi_message_wds_start_network_input_set_profile_index_3gpp (input, (guint8)ctx->profile_id, NULL); } else { /* If user gives empty string as APN, we also skip setting it in the * request. */ if (ctx->apn && ctx->apn[0]) qmi_message_wds_start_network_input_set_apn (input, ctx->apn, NULL); /* Auth info */ qmi_message_wds_start_network_input_set_authentication_preference (input, ctx->auth, NULL); if (ctx->auth != QMI_WDS_AUTHENTICATION_NONE) { if (ctx->user) qmi_message_wds_start_network_input_set_username (input, ctx->user, NULL); if (ctx->user) qmi_message_wds_start_network_input_set_password (input, ctx->password, NULL); } } /* Only add the IP family preference TLV if explicitly requested a given * family. This TLV may be newer than the Start Network command itself, so * we'll just allow the case where none is specified. */ if (!ctx->no_ip_family_preference) { qmi_message_wds_start_network_input_set_ip_family_preference ( input, (ctx->running_ipv6 ? QMI_WDS_IP_FAMILY_IPV6 : QMI_WDS_IP_FAMILY_IPV4), NULL); } return input; } static void packet_service_status_indication_cb (QmiClientWds *client, QmiIndicationWdsPacketServiceStatusOutput *output, MMBearerQmi *self) { QmiWdsConnectionStatus connection_status; MMBearerStatus bearer_status; if (!qmi_indication_wds_packet_service_status_output_get_connection_status ( output, &connection_status, NULL, NULL)) return; bearer_status = mm_base_bearer_get_status (MM_BASE_BEARER (self)); if (connection_status == QMI_WDS_CONNECTION_STATUS_DISCONNECTED && bearer_status != MM_BEARER_STATUS_DISCONNECTED && bearer_status != MM_BEARER_STATUS_DISCONNECTING) { QmiWdsCallEndReason cer; QmiWdsVerboseCallEndReasonType verbose_cer_type; gint16 verbose_cer_reason; g_autoptr(GError) connection_error = NULL; if (qmi_indication_wds_packet_service_status_output_get_verbose_call_end_reason ( output, &verbose_cer_type, &verbose_cer_reason, NULL)) { /* Create MM error based on the verbose call end reason details. There is no real * need for now to provide the correct IP type associated to the QMI WDS client * that received the indication, because the type of errors that need this info * would happen upon a Start Network operation, not after the modem has been * connected. */ connection_error = mm_error_from_wds_verbose_call_end_reason (verbose_cer_type, verbose_cer_reason, MM_BEARER_IP_FAMILY_NONE, self); } else if (qmi_indication_wds_packet_service_status_output_get_call_end_reason ( output, &cer, NULL)) { const gchar *cer_str; cer_str = qmi_wds_call_end_reason_get_string (cer); mm_obj_msg (self, "call end reason (%u): %s", cer, cer_str); connection_error = g_error_new (MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_UNKNOWN, "Call failed: %s", cer_str); } else connection_error = g_error_new_literal (MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_UNKNOWN, "Call failed"); mm_base_bearer_report_connection_status_detailed (MM_BASE_BEARER (self), MM_BEARER_CONNECTION_STATUS_DISCONNECTED, connection_error); } } static void common_setup_cleanup_packet_service_status_unsolicited_events (MMBearerQmi *self, QmiClientWds *client, gboolean enable, guint *indication_id) { if (!client) return; /* Connect/Disconnect "Packet Service Status" indications */ if (enable) { g_assert (*indication_id == 0); *indication_id = g_signal_connect (client, "packet-service-status", G_CALLBACK (packet_service_status_indication_cb), self); } else if (*indication_id != 0) { g_signal_handler_disconnect (client, *indication_id); *indication_id = 0; } } static void event_report_indication_cb (QmiClientWds *client, QmiIndicationWdsEventReportOutput *output, MMBearerQmi *self) { mm_obj_dbg (self, "got QMI WDS event report"); } static guint connect_enable_indications_ready (QmiClientWds *client, GAsyncResult *res, MMBearerQmi *self, GError **error) { QmiMessageWdsSetEventReportOutput *output; /* Don't care about the result */ output = qmi_client_wds_set_event_report_finish (client, res, error); if (!output || !qmi_message_wds_set_event_report_output_get_result (output, error)) { if (output) qmi_message_wds_set_event_report_output_unref (output); return 0; } qmi_message_wds_set_event_report_output_unref (output); return g_signal_connect (client, "event-report", G_CALLBACK (event_report_indication_cb), self); } static void connect_enable_indications_ipv4_ready (QmiClientWds *client, GAsyncResult *res, GTask *task) { ConnectContext *ctx; ctx = g_task_get_task_data (task); g_assert (ctx->event_report_ipv4_indication_id == 0); ctx->event_report_ipv4_indication_id = connect_enable_indications_ready (client, res, ctx->self, &ctx->error_ipv4); if (!ctx->event_report_ipv4_indication_id) ctx->step = CONNECT_STEP_LAST; else ctx->step++; connect_context_step (task); } static void connect_enable_indications_ipv6_ready (QmiClientWds *client, GAsyncResult *res, GTask *task) { ConnectContext *ctx; ctx = g_task_get_task_data (task); g_assert (ctx->event_report_ipv6_indication_id == 0); ctx->event_report_ipv6_indication_id = connect_enable_indications_ready (client, res, ctx->self, &ctx->error_ipv6); if (!ctx->event_report_ipv6_indication_id) ctx->step = CONNECT_STEP_LAST; else ctx->step++; connect_context_step (task); } static QmiMessageWdsSetEventReportInput * event_report_input_new (gboolean enable) { QmiMessageWdsSetEventReportInput *input; input = qmi_message_wds_set_event_report_input_new (); qmi_message_wds_set_event_report_input_set_extended_data_bearer_technology (input, enable, NULL); qmi_message_wds_set_event_report_input_set_limited_data_system_status (input, enable, NULL); qmi_message_wds_set_event_report_input_set_uplink_flow_control (input, enable, NULL); qmi_message_wds_set_event_report_input_set_data_systems (input, enable, NULL); qmi_message_wds_set_event_report_input_set_evdo_pm_change (input, enable, NULL); qmi_message_wds_set_event_report_input_set_preferred_data_system (input, enable, NULL); qmi_message_wds_set_event_report_input_set_data_call_status (input, enable, NULL); qmi_message_wds_set_event_report_input_set_current_data_bearer_technology (input, enable, NULL); qmi_message_wds_set_event_report_input_set_mip_status (input, enable, NULL); qmi_message_wds_set_event_report_input_set_dormancy_status (input, enable, NULL); qmi_message_wds_set_event_report_input_set_data_bearer_technology (input, enable, NULL); qmi_message_wds_set_event_report_input_set_channel_rate (input, enable, NULL); return input; } static void setup_event_report_unsolicited_events (MMBearerQmi *self, QmiClientWds *client, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { QmiMessageWdsSetEventReportInput *input = event_report_input_new (TRUE); qmi_client_wds_set_event_report (client, input, 5, cancellable, callback, user_data); qmi_message_wds_set_event_report_input_unref (input); } static void cleanup_event_report_unsolicited_events (MMBearerQmi *self, QmiClientWds *client, guint *indication_id) { QmiMessageWdsSetEventReportInput *input; g_assert (*indication_id != 0); g_signal_handler_disconnect (client, *indication_id); *indication_id = 0; input = event_report_input_new (FALSE); qmi_client_wds_set_event_report (client, input, 5, NULL, NULL, NULL); qmi_message_wds_set_event_report_input_unref (input); } static void set_ip_family_ready (QmiClientWds *client, GAsyncResult *res, GTask *task) { MMBearerQmi *self; ConnectContext *ctx; GError *error = NULL; QmiMessageWdsSetIpFamilyOutput *output; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); g_assert (ctx->running_ipv4 || ctx->running_ipv6); g_assert (!(ctx->running_ipv4 && ctx->running_ipv6)); output = qmi_client_wds_set_ip_family_finish (client, res, &error); if (output) { qmi_message_wds_set_ip_family_output_get_result (output, &error); qmi_message_wds_set_ip_family_output_unref (output); } if (error) { mm_obj_dbg (self, "couldn't set IP family preference: %s", error->message); g_error_free (error); } /* Keep on */ ctx->step++; connect_context_step (task); } static void bind_data_port_ready (QmiClientWds *client, GAsyncResult *res, GTask *task) { ConnectContext *ctx; GError *error = NULL; g_autoptr(QmiMessageWdsBindDataPortOutput) output = NULL; ctx = g_task_get_task_data (task); g_assert (ctx->running_ipv4 || ctx->running_ipv6); g_assert (!(ctx->running_ipv4 && ctx->running_ipv6)); output = qmi_client_wds_bind_data_port_finish (client, res, &error); if (!output || !qmi_message_wds_bind_data_port_output_get_result (output, &error)) { if (g_error_matches (error, QMI_PROTOCOL_ERROR, QMI_PROTOCOL_ERROR_DEVICE_UNSUPPORTED)) { /* Some firmwares only support this through "Bind Mux Data Port", * even if multiplexing is disabled. Try again with that. */ g_error_free (error); ctx->sio_port_failed = TRUE; connect_context_step (task); return; } g_prefix_error (&error, "Couldn't bind data port: "); complete_connect (task, NULL, error); return; } /* Keep on */ ctx->step++; connect_context_step (task); } static void bind_mux_data_port_ready (QmiClientWds *client, GAsyncResult *res, GTask *task) { ConnectContext *ctx; GError *error = NULL; g_autoptr(QmiMessageWdsBindMuxDataPortOutput) output = NULL; ctx = g_task_get_task_data (task); g_assert (ctx->running_ipv4 || ctx->running_ipv6); g_assert (!(ctx->running_ipv4 && ctx->running_ipv6)); output = qmi_client_wds_bind_mux_data_port_finish (client, res, &error); if (!output || !qmi_message_wds_bind_mux_data_port_output_get_result (output, &error)) { g_prefix_error (&error, "Couldn't bind mux data port: "); complete_connect (task, NULL, error); return; } /* Keep on */ ctx->step++; connect_context_step (task); } static void qmi_port_allocate_client_ready (MMPortQmi *qmi, GAsyncResult *res, GTask *task) { ConnectContext *ctx; GError *error = NULL; ctx = g_task_get_task_data (task); g_assert (ctx->running_ipv4 || ctx->running_ipv6); g_assert (!(ctx->running_ipv4 && ctx->running_ipv6)); if (!mm_port_qmi_allocate_client_finish (qmi, res, &error)) { g_prefix_error (&error, "Couldn't allocate %s client in QMI port %s: ", ctx->running_ipv4 ? "IPv4" : "IPv6", mm_port_get_device (MM_PORT (qmi))); complete_connect (task, NULL, error); return; } if (ctx->running_ipv4) ctx->client_ipv4 = QMI_CLIENT_WDS (mm_port_qmi_get_client ( qmi, QMI_SERVICE_WDS, MM_BEARER_QMI_PORT_FLAG (MM_PORT_QMI_FLAG_WDS_IPV4, ctx))); else ctx->client_ipv6 = QMI_CLIENT_WDS (mm_port_qmi_get_client ( qmi, QMI_SERVICE_WDS, MM_BEARER_QMI_PORT_FLAG (MM_PORT_QMI_FLAG_WDS_IPV6, ctx))); /* Keep on */ ctx->step++; connect_context_step (task); } static void main_interface_up_ready (MMPortNet *link, GAsyncResult *res, GTask *task) { ConnectContext *ctx; GError *error = NULL; ctx = g_task_get_task_data (task); if (!mm_port_net_link_setup_finish (link, res, &error)) { g_prefix_error (&error, "Couldn't bring main interface up: "); complete_connect (task, NULL, error); return; } /* Keep on */ ctx->step++; connect_context_step (task); } static void wait_link_port_ready (MMBaseModem *modem, GAsyncResult *res, GTask *task) { ConnectContext *ctx; GError *error = NULL; ctx = g_task_get_task_data (task); ctx->link = mm_base_modem_wait_link_port_finish (modem, res, &error); if (!ctx->link) { complete_connect (task, NULL, error); return; } /* Keep on */ ctx->step++; connect_context_step (task); } static void setup_link_ready (MMPortQmi *qmi, GAsyncResult *res, GTask *task) { ConnectContext *ctx; GError *error = NULL; g_autoptr(MMBaseModem) modem = NULL; ctx = g_task_get_task_data (task); ctx->link_name = mm_port_qmi_setup_link_finish (qmi, res, &ctx->mux_id, &error); if (!ctx->link_name) { g_prefix_error (&error, "failed to create net link for device: "); complete_connect (task, NULL, error); return; } /* From now on link_name will be set, and we'll use that to know * whether we should cleanup the link upon a connection failure */ mm_obj_msg (ctx->self, "net link %s created (mux id %u)", ctx->link_name, ctx->mux_id); /* Wait for the data port with the given interface name, which will be * added asynchronously */ g_object_get (ctx->self, MM_BASE_BEARER_MODEM, &modem, NULL); g_assert (modem); mm_base_modem_wait_link_port (modem, "net", ctx->link_name, WAIT_LINK_PORT_TIMEOUT_MS, (GAsyncReadyCallback) wait_link_port_ready, task); } static void setup_data_format_ready (MMPortQmi *qmi, GAsyncResult *res, GTask *task) { ConnectContext *ctx; g_autoptr(GError) error = NULL; ctx = g_task_get_task_data (task); if (!mm_port_qmi_setup_data_format_finish (qmi, res, &error)) { /* a failure here could indicate no support for WDA Set Data Format, * if so, just go on with the plain CTL based support (and data aggregation * protocol disabled) */ if (!g_error_matches (error, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED)) { complete_connect (task, NULL, g_steal_pointer (&error)); return; } ctx->dap = QMI_WDA_DATA_AGGREGATION_PROTOCOL_DISABLED; } else ctx->dap = mm_port_qmi_get_data_aggregation_protocol (ctx->qmi); if ((ctx->multiplex == MM_BEARER_MULTIPLEX_SUPPORT_REQUIRED) && (ctx->dap == QMI_WDA_DATA_AGGREGATION_PROTOCOL_DISABLED)) { complete_connect (task, NULL, g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "Cannot enable multiplex support")); return; } if ((ctx->multiplex == MM_BEARER_MULTIPLEX_SUPPORT_NONE) && (ctx->dap != QMI_WDA_DATA_AGGREGATION_PROTOCOL_DISABLED)) { complete_connect (task, NULL, g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "Cannot disable multiplex support")); return; } /* Keep on */ ctx->step++; connect_context_step (task); } static void qmi_port_open_ready (MMPortQmi *qmi, GAsyncResult *res, GTask *task) { ConnectContext *ctx; GError *error = NULL; if (!mm_port_qmi_open_finish (qmi, res, &error)) { g_prefix_error (&error, "Couldn't open QMI port %s: ", mm_port_get_device (MM_PORT (qmi))); complete_connect (task, NULL, error); return; } ctx = g_task_get_task_data (task); ctx->explicit_qmi_open = TRUE; /* Keep on */ ctx->step++; connect_context_step (task); } static gboolean load_ip_type_settings_from_profile (ConnectContext *ctx, MM3gppProfile *profile, GError **error) { MMBearerIpFamily ip_family; ip_family = mm_3gpp_profile_get_ip_type (profile); if (mm_3gpp_normalize_ip_family (&ip_family, TRUE)) ctx->no_ip_family_preference = TRUE; if (ip_family & MM_BEARER_IP_FAMILY_IPV4) ctx->ipv4 = TRUE; if (ip_family & MM_BEARER_IP_FAMILY_IPV6) ctx->ipv6 = TRUE; if (ip_family & MM_BEARER_IP_FAMILY_IPV4V6) { ctx->ipv4 = TRUE; ctx->ipv6 = TRUE; } if (!ctx->ipv4 && !ctx->ipv6) { g_autofree gchar *str = NULL; str = mm_bearer_ip_family_build_string_from_mask (ip_family); g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "Unsupported IP type requested: '%s'", str); return FALSE; } return TRUE; } static void get_profile_ready (MMIfaceModem3gppProfileManager *modem, GAsyncResult *res, GTask *task) { ConnectContext *ctx; GError *error = NULL; g_autoptr(MM3gppProfile) profile = NULL; ctx = g_task_get_task_data (task); profile = mm_iface_modem_3gpp_profile_manager_get_profile_finish (modem, res, &error); if (!profile) { g_task_return_error (task, error); g_object_unref (task); return; } if (!load_ip_type_settings_from_profile (ctx, profile, &error)) { g_prefix_error (&error, "Couldn't load ip type settings from profile: "); g_task_return_error (task, error); g_object_unref (task); return; } /* Keep on */ ctx->step++; connect_context_step (task); } static void connect_context_step (GTask *task) { MMBearerQmi *self; ConnectContext *ctx; self = g_task_get_source_object (task); g_assert (self->priv->ongoing_connect_user_cancellable); if (g_cancellable_is_cancelled (self->priv->ongoing_connect_user_cancellable)) { complete_connect (task, NULL, g_error_new (G_IO_ERROR, G_IO_ERROR_CANCELLED, "operation cancelled")); return; } g_assert (self->priv->ongoing_connect_network_cancellable); if (g_cancellable_is_cancelled (self->priv->ongoing_connect_network_cancellable)) { complete_connect (task, NULL, g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_ABORTED, "aborted by the network")); return; } ctx = g_task_get_task_data (task); switch (ctx->step) { case CONNECT_STEP_FIRST: ctx->step++; /* fall through */ case CONNECT_STEP_LOAD_PROFILE_SETTINGS: if (ctx->profile_id != MM_3GPP_PROFILE_ID_UNKNOWN) { mm_obj_dbg (self, "loading connection settings from profile '%d'...", ctx->profile_id); mm_iface_modem_3gpp_profile_manager_get_profile ( MM_IFACE_MODEM_3GPP_PROFILE_MANAGER (ctx->modem), ctx->profile_id, (GAsyncReadyCallback)get_profile_ready, task); return; } ctx->step++; /* fall through */ case CONNECT_STEP_OPEN_QMI_PORT: g_assert (ctx->ipv4 || ctx->ipv6); /* If we're explicitly opening the port (e.g. using a different cdc-wdm * port because the primary one is already connected by a different * bearer), then make sure we also close it if anything goes wrong and * during disconnect */ if (!mm_port_qmi_is_open (ctx->qmi)) { mm_port_qmi_open (ctx->qmi, TRUE, g_task_get_cancellable (task), (GAsyncReadyCallback)qmi_port_open_ready, task); return; } ctx->step++; /* fall through */ case CONNECT_STEP_SETUP_DATA_FORMAT: { MMPortQmiSetupDataFormatAction action; switch (ctx->multiplex) { case MM_BEARER_MULTIPLEX_SUPPORT_NONE: action = MM_PORT_QMI_SETUP_DATA_FORMAT_ACTION_SET_DEFAULT; break; case MM_BEARER_MULTIPLEX_SUPPORT_REQUESTED: case MM_BEARER_MULTIPLEX_SUPPORT_REQUIRED: action = MM_PORT_QMI_SETUP_DATA_FORMAT_ACTION_SET_MULTIPLEX; break; case MM_BEARER_MULTIPLEX_SUPPORT_UNKNOWN: default: g_assert_not_reached (); } mm_port_qmi_setup_data_format (ctx->qmi, ctx->data, action, (GAsyncReadyCallback) setup_data_format_ready, task); return; } case CONNECT_STEP_SETUP_LINK: /* if muxing has been enabled in the port, we need to create a new link * interface. */ if (MM_PORT_QMI_DAP_IS_SUPPORTED_QMAP (ctx->dap)) { mm_port_qmi_setup_link (ctx->qmi, ctx->data, ctx->link_prefix_hint, (GAsyncReadyCallback) setup_link_ready, task); return; } ctx->step++; /* fall through */ case CONNECT_STEP_SETUP_LINK_MAIN_UP: /* if the connection is done through a new link, we need to ifup the main interface */ if (ctx->link) { mm_obj_dbg (self, "bringing main interface %s up...", mm_port_get_device (ctx->data)); mm_port_net_link_setup (MM_PORT_NET (ctx->data), TRUE, 0, /* ignore */ g_task_get_cancellable (task), (GAsyncReadyCallback) main_interface_up_ready, task); return; } ctx->step++; /* fall through */ case CONNECT_STEP_IP_METHOD: /* Once the QMI port is open, we decide the IP method we're going * to request. If the LLP is raw-ip, we force Static IP, because not * all DHCP clients support the raw-ip interfaces; otherwise default * to DHCP as always. */ if (mm_port_qmi_get_link_layer_protocol (ctx->qmi) == QMI_WDA_LINK_LAYER_PROTOCOL_RAW_IP) ctx->ip_method = MM_BEARER_IP_METHOD_STATIC; else ctx->ip_method = MM_BEARER_IP_METHOD_DHCP; mm_obj_dbg (self, "defaulting to use %s IP method", mm_bearer_ip_method_get_string (ctx->ip_method)); ctx->step++; /* fall through */ case CONNECT_STEP_IPV4: /* If no IPv4 setup needed, jump to IPv6 */ if (!ctx->ipv4) { ctx->step = CONNECT_STEP_IPV6; connect_context_step (task); return; } /* Start IPv4 setup */ mm_obj_dbg (self, "running IPv4 connection setup"); ctx->running_ipv4 = TRUE; ctx->running_ipv6 = FALSE; ctx->step++; /* fall through */ case CONNECT_STEP_WDS_CLIENT_IPV4: { QmiClient *client; client = mm_port_qmi_get_client (ctx->qmi, QMI_SERVICE_WDS, MM_BEARER_QMI_PORT_FLAG (MM_PORT_QMI_FLAG_WDS_IPV4, ctx)); if (!client) { mm_obj_dbg (self, "allocating IPv4-specific WDS client (mux id %u)", ctx->mux_id); mm_port_qmi_allocate_client (ctx->qmi, QMI_SERVICE_WDS, MM_BEARER_QMI_PORT_FLAG (MM_PORT_QMI_FLAG_WDS_IPV4, ctx), g_task_get_cancellable (task), (GAsyncReadyCallback)qmi_port_allocate_client_ready, task); return; } ctx->client_ipv4 = QMI_CLIENT_WDS (client); ctx->step++; } /* fall through */ case CONNECT_STEP_BIND_DATA_PORT_IPV4: /* If SIO port given, bind client to it */ if (!ctx->sio_port_failed && ctx->endpoint.sio_port != QMI_SIO_PORT_NONE) { g_autoptr(QmiMessageWdsBindDataPortInput) input = NULL; mm_obj_dbg (self, "binding to data port: %s", qmi_sio_port_get_string (ctx->endpoint.sio_port)); input = qmi_message_wds_bind_data_port_input_new (); qmi_message_wds_bind_data_port_input_set_data_port (input, ctx->endpoint.sio_port, NULL); qmi_client_wds_bind_data_port (ctx->client_ipv4, input, 10, g_task_get_cancellable (task), (GAsyncReadyCallback)bind_data_port_ready, task); return; } /* If mux id given, bind mux data port */ if (ctx->sio_port_failed || ctx->mux_id != QMI_DEVICE_MUX_ID_UNBOUND) { g_autoptr(QmiMessageWdsBindMuxDataPortInput) input = NULL; mm_obj_dbg (self, "binding to mux id %d", ctx->mux_id); input = qmi_message_wds_bind_mux_data_port_input_new (); qmi_message_wds_bind_mux_data_port_input_set_endpoint_info ( input, ctx->endpoint.type, ctx->endpoint.interface_number, NULL); qmi_message_wds_bind_mux_data_port_input_set_mux_id (input, ctx->mux_id, NULL); qmi_client_wds_bind_mux_data_port (ctx->client_ipv4, input, 10, g_task_get_cancellable (task), (GAsyncReadyCallback)bind_mux_data_port_ready, task); return; } ctx->step++; /* fall through */ case CONNECT_STEP_IP_FAMILY_IPV4: /* If client is new enough, select IP family */ if (!ctx->no_ip_family_preference) { QmiMessageWdsSetIpFamilyInput *input; mm_obj_dbg (self, "setting default IP family to: IPv4"); input = qmi_message_wds_set_ip_family_input_new (); qmi_message_wds_set_ip_family_input_set_preference (input, QMI_WDS_IP_FAMILY_IPV4, NULL); qmi_client_wds_set_ip_family (ctx->client_ipv4, input, 10, g_task_get_cancellable (task), (GAsyncReadyCallback)set_ip_family_ready, task); qmi_message_wds_set_ip_family_input_unref (input); return; } ctx->step++; /* fall through */ case CONNECT_STEP_ENABLE_INDICATIONS_IPV4: common_setup_cleanup_packet_service_status_unsolicited_events (ctx->self, ctx->client_ipv4, TRUE, &ctx->packet_service_status_ipv4_indication_id); setup_event_report_unsolicited_events (ctx->self, ctx->client_ipv4, g_task_get_cancellable (task), (GAsyncReadyCallback) connect_enable_indications_ipv4_ready, task); return; case CONNECT_STEP_START_NETWORK_IPV4: { QmiMessageWdsStartNetworkInput *input; mm_obj_dbg (self, "starting IPv4 connection..."); input = build_start_network_input (ctx); qmi_client_wds_start_network (ctx->client_ipv4, input, MM_BASE_BEARER_DEFAULT_CONNECTION_TIMEOUT, g_task_get_cancellable (task), (GAsyncReadyCallback)start_network_ready, task); qmi_message_wds_start_network_input_unref (input); return; } case CONNECT_STEP_ENABLE_WDS_INDICATIONS_IPV4: /* If call is connected enable wds indications */ if (ctx->packet_data_handle_ipv4) { register_for_wds_indication (ctx, task); return; } ctx->step++; /* fall through */ case CONNECT_STEP_GET_CURRENT_SETTINGS_IPV4: /* Retrieve and print IP configuration */ if (ctx->packet_data_handle_ipv4) { mm_obj_dbg (self, "getting IPv4 configuration..."); get_current_settings (task, ctx->client_ipv4); return; } ctx->step++; /* fall through */ case CONNECT_STEP_IPV6: /* If no IPv6 setup needed, jump to last */ if (!ctx->ipv6) { ctx->step = CONNECT_STEP_LAST; connect_context_step (task); return; } /* Start IPv6 setup */ mm_obj_dbg (self, "running IPv6 connection setup"); ctx->running_ipv4 = FALSE; ctx->running_ipv6 = TRUE; ctx->step++; /* fall through */ case CONNECT_STEP_WDS_CLIENT_IPV6: { QmiClient *client; client = mm_port_qmi_get_client (ctx->qmi, QMI_SERVICE_WDS, MM_BEARER_QMI_PORT_FLAG (MM_PORT_QMI_FLAG_WDS_IPV6, ctx)); if (!client) { mm_obj_dbg (self, "allocating IPv6-specific WDS client (mux id %u)", ctx->mux_id); mm_port_qmi_allocate_client (ctx->qmi, QMI_SERVICE_WDS, MM_BEARER_QMI_PORT_FLAG (MM_PORT_QMI_FLAG_WDS_IPV6, ctx), g_task_get_cancellable (task), (GAsyncReadyCallback)qmi_port_allocate_client_ready, task); return; } ctx->client_ipv6 = QMI_CLIENT_WDS (client); ctx->step++; } /* fall through */ case CONNECT_STEP_BIND_DATA_PORT_IPV6: /* If SIO port given, bind client to it */ if (!ctx->sio_port_failed && ctx->endpoint.sio_port != QMI_SIO_PORT_NONE) { g_autoptr(QmiMessageWdsBindDataPortInput) input = NULL; mm_obj_dbg (self, "binding to data port: %s", qmi_sio_port_get_string (ctx->endpoint.sio_port)); input = qmi_message_wds_bind_data_port_input_new (); qmi_message_wds_bind_data_port_input_set_data_port (input, ctx->endpoint.sio_port, NULL); qmi_client_wds_bind_data_port (ctx->client_ipv6, input, 10, g_task_get_cancellable (task), (GAsyncReadyCallback)bind_data_port_ready, task); return; } /* If mux id given, bind mux data port */ if (ctx->sio_port_failed || ctx->mux_id != QMI_DEVICE_MUX_ID_UNBOUND) { g_autoptr(QmiMessageWdsBindMuxDataPortInput) input = NULL; mm_obj_dbg (self, "binding to mux id %d", ctx->mux_id); input = qmi_message_wds_bind_mux_data_port_input_new (); qmi_message_wds_bind_mux_data_port_input_set_endpoint_info ( input, ctx->endpoint.type, ctx->endpoint.interface_number, NULL); qmi_message_wds_bind_mux_data_port_input_set_mux_id (input, ctx->mux_id, NULL); qmi_client_wds_bind_mux_data_port (ctx->client_ipv6, input, 10, g_task_get_cancellable (task), (GAsyncReadyCallback)bind_mux_data_port_ready, task); return; } ctx->step++; /* fall through */ case CONNECT_STEP_IP_FAMILY_IPV6: { QmiMessageWdsSetIpFamilyInput *input; g_assert (ctx->no_ip_family_preference == FALSE); mm_obj_dbg (self, "setting default IP family to: IPv6"); input = qmi_message_wds_set_ip_family_input_new (); qmi_message_wds_set_ip_family_input_set_preference (input, QMI_WDS_IP_FAMILY_IPV6, NULL); qmi_client_wds_set_ip_family (ctx->client_ipv6, input, 10, g_task_get_cancellable (task), (GAsyncReadyCallback)set_ip_family_ready, task); qmi_message_wds_set_ip_family_input_unref (input); return; } case CONNECT_STEP_ENABLE_INDICATIONS_IPV6: common_setup_cleanup_packet_service_status_unsolicited_events (ctx->self, ctx->client_ipv6, TRUE, &ctx->packet_service_status_ipv6_indication_id); setup_event_report_unsolicited_events (ctx->self, ctx->client_ipv6, g_task_get_cancellable (task), (GAsyncReadyCallback) connect_enable_indications_ipv6_ready, task); return; case CONNECT_STEP_START_NETWORK_IPV6: { QmiMessageWdsStartNetworkInput *input; mm_obj_dbg (self, "starting IPv6 connection..."); input = build_start_network_input (ctx); qmi_client_wds_start_network (ctx->client_ipv6, input, MM_BASE_BEARER_DEFAULT_CONNECTION_TIMEOUT, g_task_get_cancellable (task), (GAsyncReadyCallback)start_network_ready, task); qmi_message_wds_start_network_input_unref (input); return; } case CONNECT_STEP_ENABLE_WDS_INDICATIONS_IPV6: /* If call is connected enable wds indications */ if (ctx->packet_data_handle_ipv6) { register_for_wds_indication (ctx, task); return; } ctx->step++; /* fall through */ case CONNECT_STEP_GET_CURRENT_SETTINGS_IPV6: /* Retrieve and print IP configuration */ if (ctx->packet_data_handle_ipv6) { mm_obj_dbg (self, "getting IPv6 configuration..."); get_current_settings (task, ctx->client_ipv6); return; } ctx->step++; /* fall through */ case CONNECT_STEP_LAST: { MMBearerConnectResult *connect_result; /* If one of IPv4 or IPv6 succeeds, we're connected */ if (!ctx->packet_data_handle_ipv4 && !ctx->packet_data_handle_ipv6) { GError *error; /* No connection, set error. If both set, IPv4 error preferred */ if (ctx->error_ipv4) { error = ctx->error_ipv4; ctx->error_ipv4 = NULL; } else { error = ctx->error_ipv6; ctx->error_ipv6 = NULL; } complete_connect (task, NULL, error); return; } /* Port is connected; update the state */ mm_port_set_connected (ctx->link ? ctx->link : ctx->data, TRUE); /* Keep connection related data */ g_assert (ctx->self->priv->qmi == NULL); ctx->self->priv->qmi = g_object_ref (ctx->qmi); if (ctx->explicit_qmi_open) { ctx->self->priv->explicit_qmi_open = TRUE; ctx->explicit_qmi_open = FALSE; } g_assert (ctx->self->priv->data == NULL); ctx->self->priv->data = ctx->data ? g_object_ref (ctx->data) : NULL; g_assert (ctx->self->priv->link == NULL); ctx->self->priv->link = ctx->link ? g_object_ref (ctx->link) : NULL; g_assert (ctx->self->priv->mux_id == QMI_DEVICE_MUX_ID_UNBOUND); ctx->self->priv->mux_id = ctx->mux_id; /* reset the link name to avoid cleaning up the link on context free */ g_clear_pointer (&ctx->link_name, g_free); g_assert (ctx->self->priv->packet_data_handle_ipv4 == 0); g_assert (ctx->self->priv->client_ipv4 == NULL); if (ctx->packet_data_handle_ipv4) { ctx->self->priv->packet_data_handle_ipv4 = ctx->packet_data_handle_ipv4; ctx->packet_data_handle_ipv4 = 0; ctx->self->priv->packet_service_status_ipv4_indication_id = ctx->packet_service_status_ipv4_indication_id; ctx->packet_service_status_ipv4_indication_id = 0; ctx->self->priv->event_report_ipv4_indication_id = ctx->event_report_ipv4_indication_id; ctx->event_report_ipv4_indication_id = 0; ctx->self->priv->extended_ipv4_config_change_id = ctx->extended_ipv4_config_change_id; ctx->extended_ipv4_config_change_id = 0; ctx->self->priv->client_ipv4 = g_object_ref (ctx->client_ipv4); } g_assert (ctx->self->priv->packet_data_handle_ipv6 == 0); g_assert (ctx->self->priv->client_ipv6 == NULL); if (ctx->packet_data_handle_ipv6) { ctx->self->priv->packet_data_handle_ipv6 = ctx->packet_data_handle_ipv6; ctx->packet_data_handle_ipv6 = 0; ctx->self->priv->packet_service_status_ipv6_indication_id = ctx->packet_service_status_ipv6_indication_id; ctx->packet_service_status_ipv6_indication_id = 0; ctx->self->priv->event_report_ipv6_indication_id = ctx->event_report_ipv6_indication_id; ctx->event_report_ipv6_indication_id = 0; ctx->self->priv->extended_ipv6_config_change_id = ctx->extended_ipv6_config_change_id; ctx->extended_ipv6_config_change_id = 0; ctx->self->priv->client_ipv6 = g_object_ref (ctx->client_ipv6); } connect_result = mm_bearer_connect_result_new (ctx->link ? ctx->link : ctx->data, ctx->ipv4_config, ctx->ipv6_config); mm_bearer_connect_result_set_multiplexed (connect_result, !!ctx->link); if (ctx->profile_id != MM_3GPP_PROFILE_ID_UNKNOWN) mm_bearer_connect_result_set_profile_id (connect_result, ctx->profile_id); complete_connect (task, connect_result, NULL); return; } default: g_assert_not_reached (); } } static void cancel_operation_cancellable (GCancellable *cancellable, GCancellable *operation_cancellable) { g_cancellable_cancel (operation_cancellable); } static gboolean load_settings_from_bearer (MMBearerQmi *self, MMBaseModem *modem, ConnectContext *ctx, MMBearerProperties *properties, GError **error) { MMBearerAllowedAuth bearer_auth; GError *inner_error = NULL; const gchar *str; const gchar *data_port_driver; guint current_multiplexed_bearers; guint max_multiplexed_bearers; gboolean multiplex_supported = TRUE; if (!mm_broadband_modem_get_active_multiplexed_bearers (MM_BROADBAND_MODEM (ctx->modem), ¤t_multiplexed_bearers, &max_multiplexed_bearers, error)) return FALSE; /* Check multiplex support in the kernel and the device */ data_port_driver = mm_kernel_device_get_driver (mm_port_peek_kernel_device (ctx->data)); /* All drivers should support multiplexing */ if (!max_multiplexed_bearers) multiplex_supported = FALSE; /* If no multiplex setting given by the user, assume none; unless in IPA */ ctx->multiplex = mm_bearer_properties_get_multiplex (properties); if (ctx->multiplex == MM_BEARER_MULTIPLEX_SUPPORT_UNKNOWN) { if (mm_context_get_test_multiplex_requested ()) ctx->multiplex = MM_BEARER_MULTIPLEX_SUPPORT_REQUESTED; else if (!g_strcmp0 (data_port_driver, "ipa")) ctx->multiplex = MM_BEARER_MULTIPLEX_SUPPORT_REQUIRED; else ctx->multiplex = MM_BEARER_MULTIPLEX_SUPPORT_NONE; } /* If multiplex unsupported, either abort or default to none */ if (!multiplex_supported) { if (ctx->multiplex == MM_BEARER_MULTIPLEX_SUPPORT_REQUIRED) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "Multiplexing required but not supported"); return FALSE; } if (ctx->multiplex == MM_BEARER_MULTIPLEX_SUPPORT_REQUESTED) { mm_obj_dbg (self, "Multiplexing unsupported"); ctx->multiplex = MM_BEARER_MULTIPLEX_SUPPORT_NONE; } } /* Go on with multiplexing enabled */ if (ctx->multiplex == MM_BEARER_MULTIPLEX_SUPPORT_REQUESTED || ctx->multiplex == MM_BEARER_MULTIPLEX_SUPPORT_REQUIRED) { g_assert (multiplex_supported); if (current_multiplexed_bearers == max_multiplexed_bearers) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "Maximum number of multiplexed bearers reached"); return FALSE; } /* The link prefix hint given must be modem-specific */ ctx->link_prefix_hint = g_strdup_printf ("qmapmux%u.", mm_base_modem_get_dbus_id (MM_BASE_MODEM (modem))); } /* If profile id is given, we'll launch the connection specifying the profile id in use * exclusively, so we ignore any additional user provided setting */ ctx->profile_id = mm_bearer_properties_get_profile_id (properties); if (ctx->profile_id != MM_3GPP_PROFILE_ID_UNKNOWN) { /* Is this a 3GPP2 only modem and profile id was given? If so, error, as we don't support * 3GPP2 profiles in ModemManager */ if (mm_iface_modem_is_cdma_only (MM_IFACE_MODEM (modem))) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS, "3GPP2 doesn't support profile id setting"); return FALSE; } /* All done now, we'll need to load IP type settings later on once * we load the real profile to use */ return TRUE; } /* APN settings */ ctx->apn = g_strdup (mm_bearer_properties_get_apn (properties)); /* Is this a 3GPP only modem and no APN was given? If so, error */ if (mm_iface_modem_is_3gpp_only (MM_IFACE_MODEM (modem)) && !ctx->apn) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS, "3GPP connection logic requires APN setting"); return FALSE; } /* Is this a 3GPP2 only modem and APN was given? If so, error */ if (mm_iface_modem_is_cdma_only (MM_IFACE_MODEM (modem)) && ctx->apn) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS, "3GPP2 doesn't support APN setting"); return FALSE; } /* IP type settings */ if (!load_ip_type_settings_from_profile (ctx, mm_bearer_properties_peek_3gpp_profile (properties), error)) return FALSE; /* Auth settings; in we treat user/password empty strings as no strings */ str = mm_bearer_properties_get_user (properties); if (str && str[0]) ctx->user = g_strdup (str); str = mm_bearer_properties_get_password (properties); if (str && str[0]) ctx->password = g_strdup (str); if (!ctx->user && !ctx->password) ctx->auth = QMI_WDS_AUTHENTICATION_NONE; else { bearer_auth = mm_bearer_properties_get_allowed_auth (properties); ctx->auth = mm_bearer_allowed_auth_to_qmi_authentication (bearer_auth, self, &inner_error); if (inner_error) { g_propagate_error (error, inner_error); return FALSE; } } return TRUE; } static void _connect (MMBaseBearer *_self, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { MMBearerQmi *self = MM_BEARER_QMI (_self); ConnectContext *ctx; GError *error = NULL; GTask *task; g_autoptr(GCancellable) operation_cancellable = NULL; g_autoptr(MMBaseModem) modem = NULL; g_autoptr(MMBearerProperties) properties = NULL; operation_cancellable = g_cancellable_new (); task = g_task_new (self, operation_cancellable, callback, user_data); g_task_set_check_cancellable (task, FALSE); g_object_get (self, MM_BASE_BEARER_MODEM, &modem, MM_BASE_BEARER_CONFIG, &properties, NULL); g_assert (modem); ctx = g_slice_new0 (ConnectContext); ctx->self = g_object_ref (self); ctx->modem = g_object_ref (modem); ctx->mux_id = QMI_DEVICE_MUX_ID_UNBOUND; ctx->step = CONNECT_STEP_FIRST; ctx->ip_method = MM_BEARER_IP_METHOD_UNKNOWN; g_task_set_task_data (task, ctx, (GDestroyNotify)connect_context_free); /* Grab a data port */ ctx->data = mm_base_modem_get_best_data_port (modem, MM_PORT_TYPE_NET); if (!ctx->data) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_NOT_FOUND, "No valid data port found to launch connection"); g_object_unref (task); return; } /* Each data port has a single QMI port associated */ ctx->qmi = mm_broadband_modem_qmi_get_port_qmi_for_data (MM_BROADBAND_MODEM_QMI (modem), ctx->data, &ctx->endpoint, &error); if (!ctx->qmi) { g_task_return_error (task, error); g_object_unref (task); return; } ctx->dap = mm_port_qmi_get_data_aggregation_protocol (ctx->qmi); /* load all settings from bearer */ if (!load_settings_from_bearer (self, modem, ctx, properties, &error)) { g_prefix_error (&error, "Invalid bearer properties: "); g_task_return_error (task, error); g_object_unref (task); return; } /* setup network cancellable */ g_assert (!self->priv->ongoing_connect_network_cancellable); self->priv->ongoing_connect_network_cancellable = g_cancellable_new (); g_cancellable_connect (self->priv->ongoing_connect_network_cancellable, G_CALLBACK (cancel_operation_cancellable), g_object_ref (operation_cancellable), g_object_unref); /* setup user cancellable */ g_assert (!self->priv->ongoing_connect_user_cancellable); self->priv->ongoing_connect_user_cancellable = g_object_ref (cancellable); g_cancellable_connect (self->priv->ongoing_connect_user_cancellable, G_CALLBACK (cancel_operation_cancellable), g_object_ref (operation_cancellable), g_object_unref); /* Run! */ mm_obj_dbg (self, "launching connection with QMI port (%s) and data port (%s) (multiplex %s)", mm_port_get_device (MM_PORT (ctx->qmi)), mm_port_get_device (ctx->data), mm_bearer_multiplex_support_get_string (ctx->multiplex)); connect_context_step (task); } /*****************************************************************************/ /* Disconnect */ typedef enum { DISCONNECT_STEP_FIRST, DISCONNECT_STEP_STOP_NETWORK_IPV4, DISCONNECT_STEP_STOP_NETWORK_IPV6, DISCONNECT_STEP_LAST } DisconnectStep; typedef struct { DisconnectStep step; gboolean running_ipv4; QmiClientWds *client_ipv4; guint32 packet_data_handle_ipv4; GError *error_ipv4; gboolean running_ipv6; QmiClientWds *client_ipv6; guint32 packet_data_handle_ipv6; GError *error_ipv6; } DisconnectContext; static void disconnect_context_free (DisconnectContext *ctx) { if (ctx->error_ipv4) g_error_free (ctx->error_ipv4); if (ctx->error_ipv6) g_error_free (ctx->error_ipv6); if (ctx->client_ipv4) g_object_unref (ctx->client_ipv4); if (ctx->client_ipv6) g_object_unref (ctx->client_ipv6); g_slice_free (DisconnectContext, ctx); } static gboolean disconnect_finish (MMBaseBearer *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void reset_bearer_connection (MMBearerQmi *self, gboolean reset_ipv4, gboolean reset_ipv6) { if (reset_ipv4) { if (self->priv->client_ipv4) { if (self->priv->packet_service_status_ipv4_indication_id) common_setup_cleanup_packet_service_status_unsolicited_events (self, self->priv->client_ipv4, FALSE, &self->priv->packet_service_status_ipv4_indication_id); if (self->priv->event_report_ipv4_indication_id) cleanup_event_report_unsolicited_events (self, self->priv->client_ipv4, &self->priv->event_report_ipv4_indication_id); } self->priv->packet_data_handle_ipv4 = 0; g_clear_object (&self->priv->client_ipv4); } if (reset_ipv6) { if (self->priv->client_ipv6) { if (self->priv->packet_service_status_ipv6_indication_id) common_setup_cleanup_packet_service_status_unsolicited_events (self, self->priv->client_ipv6, FALSE, &self->priv->packet_service_status_ipv6_indication_id); if (self->priv->event_report_ipv6_indication_id) cleanup_event_report_unsolicited_events (self, self->priv->client_ipv6, &self->priv->event_report_ipv6_indication_id); } self->priv->packet_data_handle_ipv6 = 0; g_clear_object (&self->priv->client_ipv6); } if (!self->priv->packet_data_handle_ipv4 && !self->priv->packet_data_handle_ipv6) { if (self->priv->data) { /* Port is disconnected; update the state */ mm_port_set_connected (self->priv->data, FALSE); g_clear_object (&self->priv->data); } if (self->priv->link) { g_assert (self->priv->qmi); /* Link is disconnected; update the state */ mm_port_set_connected (self->priv->link, FALSE); mm_port_qmi_cleanup_link (self->priv->qmi, mm_port_get_device (self->priv->link), self->priv->mux_id, NULL, NULL); g_clear_object (&self->priv->link); } self->priv->mux_id = QMI_DEVICE_MUX_ID_UNBOUND; /* Close port if we had it explicitly open for this connection */ if (self->priv->qmi) { if (self->priv->explicit_qmi_open) { self->priv->explicit_qmi_open = FALSE; mm_port_qmi_close (self->priv->qmi, NULL, NULL); } g_clear_object (&self->priv->qmi); } } } static void disconnect_context_step (GTask *task); static void stop_network_ready (QmiClientWds *client, GAsyncResult *res, GTask *task) { MMBearerQmi *self; DisconnectContext *ctx; GError *error = NULL; QmiMessageWdsStopNetworkOutput *output; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); output = qmi_client_wds_stop_network_finish (client, res, &error); if (output && !qmi_message_wds_stop_network_output_get_result (output, &error)) { /* No effect error, we're already disconnected */ if (g_error_matches (error, QMI_PROTOCOL_ERROR, QMI_PROTOCOL_ERROR_NO_EFFECT)) { g_error_free (error); error = NULL; } } if (error) { if (ctx->running_ipv4) ctx->error_ipv4 = error; else ctx->error_ipv6 = error; } else { /* Clear internal status */ reset_bearer_connection (self, ctx->running_ipv4, ctx->running_ipv6); } if (output) qmi_message_wds_stop_network_output_unref (output); /* Keep on */ ctx->step++; disconnect_context_step (task); } static void disconnect_context_step (GTask *task) { MMBearerQmi *self; DisconnectContext *ctx; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); switch (ctx->step) { case DISCONNECT_STEP_FIRST: ctx->step++; /* fall through */ case DISCONNECT_STEP_STOP_NETWORK_IPV4: if (ctx->packet_data_handle_ipv4) { QmiMessageWdsStopNetworkInput *input; common_setup_cleanup_packet_service_status_unsolicited_events (self, ctx->client_ipv4, FALSE, &self->priv->packet_service_status_ipv4_indication_id); if (self->priv->event_report_ipv4_indication_id) cleanup_event_report_unsolicited_events (self, ctx->client_ipv4, &self->priv->event_report_ipv4_indication_id); if (self->priv->extended_ipv4_config_change_id) { g_signal_handler_disconnect (ctx->client_ipv4, self->priv->extended_ipv4_config_change_id); self->priv->extended_ipv4_config_change_id = 0; } input = qmi_message_wds_stop_network_input_new (); qmi_message_wds_stop_network_input_set_packet_data_handle (input, ctx->packet_data_handle_ipv4, NULL); ctx->running_ipv4 = TRUE; ctx->running_ipv6 = FALSE; qmi_client_wds_stop_network (ctx->client_ipv4, input, MM_BASE_BEARER_DEFAULT_DISCONNECTION_TIMEOUT, NULL, (GAsyncReadyCallback)stop_network_ready, task); qmi_message_wds_stop_network_input_unref (input); return; } ctx->step++; /* fall through */ case DISCONNECT_STEP_STOP_NETWORK_IPV6: if (ctx->packet_data_handle_ipv6) { QmiMessageWdsStopNetworkInput *input; common_setup_cleanup_packet_service_status_unsolicited_events (self, ctx->client_ipv6, FALSE, &self->priv->packet_service_status_ipv6_indication_id); if (self->priv->event_report_ipv6_indication_id) cleanup_event_report_unsolicited_events (self, ctx->client_ipv6, &self->priv->event_report_ipv6_indication_id); if (self->priv->extended_ipv6_config_change_id) { g_signal_handler_disconnect (ctx->client_ipv6, self->priv->extended_ipv6_config_change_id); self->priv->extended_ipv6_config_change_id = 0; } input = qmi_message_wds_stop_network_input_new (); qmi_message_wds_stop_network_input_set_packet_data_handle (input, ctx->packet_data_handle_ipv6, NULL); ctx->running_ipv4 = FALSE; ctx->running_ipv6 = TRUE; qmi_client_wds_stop_network (ctx->client_ipv6, input, MM_BASE_BEARER_DEFAULT_DISCONNECTION_TIMEOUT, NULL, (GAsyncReadyCallback)stop_network_ready, task); qmi_message_wds_stop_network_input_unref (input); return; } ctx->step++; /* fall through */ case DISCONNECT_STEP_LAST: if (!ctx->error_ipv4 && !ctx->error_ipv6) g_task_return_boolean (task, TRUE); else { GError *error; /* If both set, IPv4 error preferred */ if (ctx->error_ipv4) { error = ctx->error_ipv4; ctx->error_ipv4 = NULL; } else { error = ctx->error_ipv6; ctx->error_ipv6 = NULL; } g_task_return_error (task, error); } g_object_unref (task); return; default: g_assert_not_reached (); } } static void disconnect (MMBaseBearer *_self, GAsyncReadyCallback callback, gpointer user_data) { MMBearerQmi *self = MM_BEARER_QMI (_self); DisconnectContext *ctx; GTask *task; task = g_task_new (self, NULL, callback, user_data); if ((!self->priv->packet_data_handle_ipv4 && !self->priv->packet_data_handle_ipv6) || (!self->priv->client_ipv4 && !self->priv->client_ipv6) || (!self->priv->data && !self->priv->link) || !self->priv->qmi) { mm_obj_dbg (self, "no need to disconnect: QMI bearer is already disconnected"); g_task_return_boolean (task, TRUE); g_object_unref (task); return; } ctx = g_slice_new0 (DisconnectContext); ctx->client_ipv4 = self->priv->client_ipv4 ? g_object_ref (self->priv->client_ipv4) : NULL; ctx->packet_data_handle_ipv4 = self->priv->packet_data_handle_ipv4; ctx->client_ipv6 = self->priv->client_ipv6 ? g_object_ref (self->priv->client_ipv6) : NULL; ctx->packet_data_handle_ipv6 = self->priv->packet_data_handle_ipv6; ctx->step = DISCONNECT_STEP_FIRST; g_task_set_task_data (task, ctx, (GDestroyNotify)disconnect_context_free); /* Run! */ disconnect_context_step (task); } /*****************************************************************************/ static void report_connection_status (MMBaseBearer *_self, MMBearerConnectionStatus status, const GError *connection_error) { MMBearerQmi *self = MM_BEARER_QMI (_self); if (status == MM_BEARER_CONNECTION_STATUS_DISCONNECTED) { /* Cancel any ongoing connection attempt */ g_cancellable_cancel (self->priv->ongoing_connect_network_cancellable); /* Cleanup all connection related data */ reset_bearer_connection (self, TRUE, TRUE); } /* Chain up parent's report_connection_status() */ MM_BASE_BEARER_CLASS (mm_bearer_qmi_parent_class)->report_connection_status (_self, status, connection_error); } /*****************************************************************************/ MMBaseBearer * mm_bearer_qmi_new (MMBroadbandModemQmi *modem, MMBearerProperties *config) { MMBaseBearer *bearer; /* The Qmi bearer inherits from MMBaseBearer (so it's not a MMBroadbandBearer) * and that means that the object is not async-initable, so we just use * g_object_new() here */ bearer = g_object_new (MM_TYPE_BEARER_QMI, MM_BASE_BEARER_MODEM, modem, MM_BASE_BEARER_CONFIG, config, NULL); /* Only export valid bearers */ mm_base_bearer_export (bearer); return bearer; } static void mm_bearer_qmi_init (MMBearerQmi *self) { /* Initialize private data */ self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, MM_TYPE_BEARER_QMI, MMBearerQmiPrivate); self->priv->mux_id = QMI_DEVICE_MUX_ID_UNBOUND; } static void dispose (GObject *object) { MMBearerQmi *self = MM_BEARER_QMI (object); g_assert (!self->priv->ongoing_connect_user_cancellable); g_assert (!self->priv->ongoing_connect_network_cancellable); reset_bearer_connection (self, TRUE, TRUE); g_list_free_full (self->priv->pco_list, g_object_unref); self->priv->pco_list = NULL; G_OBJECT_CLASS (mm_bearer_qmi_parent_class)->dispose (object); } static void mm_bearer_qmi_class_init (MMBearerQmiClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); MMBaseBearerClass *base_bearer_class = MM_BASE_BEARER_CLASS (klass); g_type_class_add_private (object_class, sizeof (MMBearerQmiPrivate)); /* Virtual methods */ object_class->dispose = dispose; base_bearer_class->connect = _connect; base_bearer_class->connect_finish = connect_finish; base_bearer_class->disconnect = disconnect; base_bearer_class->disconnect_finish = disconnect_finish; base_bearer_class->report_connection_status = report_connection_status; base_bearer_class->reload_stats = reload_stats; base_bearer_class->reload_stats_finish = reload_stats_finish; base_bearer_class->load_connection_status = load_connection_status; base_bearer_class->load_connection_status_finish = load_connection_status_finish; #if defined WITH_SUSPEND_RESUME base_bearer_class->reload_connection_status = reload_connection_status; base_bearer_class->reload_connection_status_finish = reload_connection_status_finish; #endif } ModemManager-1.23.4-dev/src/mm-bearer-qmi.h000066400000000000000000000041161456466623000203220ustar00rootroot00000000000000/* -*- 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: * * Author: Aleksander Morgado * * Copyright (C) 2012 Google, Inc. */ #ifndef MM_BEARER_QMI_H #define MM_BEARER_QMI_H #include #include #define _LIBMM_INSIDE_MM #include #include "mm-base-bearer.h" #include "mm-broadband-modem-qmi.h" #define MM_TYPE_BEARER_QMI (mm_bearer_qmi_get_type ()) #define MM_BEARER_QMI(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_BEARER_QMI, MMBearerQmi)) #define MM_BEARER_QMI_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_BEARER_QMI, MMBearerQmiClass)) #define MM_IS_BEARER_QMI(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_BEARER_QMI)) #define MM_IS_BEARER_QMI_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_BEARER_QMI)) #define MM_BEARER_QMI_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_BEARER_QMI, MMBearerQmiClass)) typedef struct _MMBearerQmi MMBearerQmi; typedef struct _MMBearerQmiClass MMBearerQmiClass; typedef struct _MMBearerQmiPrivate MMBearerQmiPrivate; struct _MMBearerQmi { MMBaseBearer parent; MMBearerQmiPrivate *priv; }; struct _MMBearerQmiClass { MMBaseBearerClass parent; }; GType mm_bearer_qmi_get_type (void); G_DEFINE_AUTOPTR_CLEANUP_FUNC (MMBearerQmi, g_object_unref) /* QMI bearer creation implementation. * NOTE it is *not* a broadband bearer, so not async-initable */ MMBaseBearer *mm_bearer_qmi_new (MMBroadbandModemQmi *modem, MMBearerProperties *config); #endif /* MM_BEARER_QMI_H */ ModemManager-1.23.4-dev/src/mm-broadband-bearer.c000066400000000000000000002127221456466623000214470ustar00rootroot00000000000000/* -*- 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 - 2012 Red Hat, Inc. * Copyright (C) 2011 - 2012 Google, Inc. * Copyright (C) 2011 - 2013 Aleksander Morgado */ #include #include #include #include #include #include #include #define _LIBMM_INSIDE_MM #include #include "mm-broadband-bearer.h" #include "mm-iface-modem.h" #include "mm-iface-modem-3gpp.h" #include "mm-iface-modem-3gpp-profile-manager.h" #include "mm-iface-modem-cdma.h" #include "mm-base-modem-at.h" #include "mm-log-object.h" #include "mm-modem-helpers.h" #include "mm-port-enums-types.h" #include "mm-helper-enums-types.h" static void async_initable_iface_init (GAsyncInitableIface *iface); G_DEFINE_TYPE_EXTENDED (MMBroadbandBearer, mm_broadband_bearer, MM_TYPE_BASE_BEARER, 0, G_IMPLEMENT_INTERFACE (G_TYPE_ASYNC_INITABLE, async_initable_iface_init)); typedef enum { CONNECTION_TYPE_NONE, CONNECTION_TYPE_3GPP, CONNECTION_TYPE_CDMA, } ConnectionType; enum { PROP_0, PROP_FLOW_CONTROL, PROP_LAST }; static GParamSpec *properties[PROP_LAST]; struct _MMBroadbandBearerPrivate { /*-- Common stuff --*/ /* Data port used when modem is connected */ MMPort *port; /* Current connection type */ ConnectionType connection_type; /* PPP specific */ MMFlowControl flow_control; /*-- 3GPP specific --*/ /* CID of the PDP context */ gint profile_id; }; /*****************************************************************************/ /* Detailed connect context, used in both CDMA and 3GPP sequences */ typedef struct { MMBaseModem *modem; MMPortSerialAt *primary; MMPortSerialAt *secondary; MMPort *data; gboolean close_data_on_exit; /* 3GPP-specific */ MMBearerIpFamily ip_family; } DetailedConnectContext; static MMBearerConnectResult * detailed_connect_finish (MMBroadbandBearer *self, GAsyncResult *res, GError **error) { return g_task_propagate_pointer (G_TASK (res), error); } static void detailed_connect_context_free (DetailedConnectContext *ctx) { g_object_unref (ctx->primary); if (ctx->secondary) g_object_unref (ctx->secondary); if (ctx->data) { if (ctx->close_data_on_exit) mm_port_serial_close (MM_PORT_SERIAL (ctx->data)); g_object_unref (ctx->data); } g_object_unref (ctx->modem); g_slice_free (DetailedConnectContext, ctx); } static DetailedConnectContext * detailed_connect_context_new (MMBroadbandBearer *self, MMBroadbandModem *modem, MMPortSerialAt *primary, MMPortSerialAt *secondary) { DetailedConnectContext *ctx; ctx = g_slice_new0 (DetailedConnectContext); ctx->modem = MM_BASE_MODEM (g_object_ref (modem)); ctx->primary = g_object_ref (primary); ctx->secondary = (secondary ? g_object_ref (secondary) : NULL); ctx->ip_family = mm_bearer_properties_get_ip_type (mm_base_bearer_peek_config (MM_BASE_BEARER (self))); mm_3gpp_normalize_ip_family (&ctx->ip_family, TRUE); return ctx; } /*****************************************************************************/ /* Generic implementations (both 3GPP and CDMA) are always AT-port based */ static MMPortSerialAt * common_get_at_data_port (MMBroadbandBearer *self, MMBaseModem *modem, GError **error) { MMPort *data; /* Look for best data port, NULL if none available. */ data = mm_base_modem_peek_best_data_port (modem, MM_PORT_TYPE_AT); if (!data) { /* It may happen that the desired data port grabbed during probing was * actually a 'net' port, which the generic logic cannot handle, so if * that is the case, and we have no AT data ports specified, just fallback to the primary AT port. */ data = (MMPort *) mm_base_modem_peek_port_primary (modem); } g_assert (MM_IS_PORT_SERIAL_AT (data)); if (!mm_port_serial_open (MM_PORT_SERIAL (data), error)) { g_prefix_error (error, "Couldn't connect: cannot keep data port open."); return NULL; } mm_obj_dbg (self, "connection through a plain serial AT port: %s", mm_port_get_device (data)); return MM_PORT_SERIAL_AT (g_object_ref (data)); } /*****************************************************************************/ /* CDMA CONNECT * * CDMA connection procedure of a bearer involves several steps: * 1) Get data port from the modem. Default implementation will have only * one single possible data port, but plugins may have more. * 2) If requesting specific RM, load current. * 2.1) If current RM different to the requested one, set the new one. * 3) Initiate call. */ static void dial_cdma_ready (MMBaseModem *modem, GAsyncResult *res, GTask *task) { MMBroadbandBearer *self; DetailedConnectContext *ctx; GError *error = NULL; MMBearerIpConfig *config; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); /* DO NOT check for cancellable here. If we got here without errors, the * bearer is really connected and therefore we need to reflect that in * the state machine. */ mm_base_modem_at_command_full_finish (modem, res, &error); if (error) { mm_obj_warn (self, "couldn't connect: %s", error->message); g_task_return_error (task, error); g_object_unref (task); return; } /* Configure flow control to use while connected */ if (self->priv->flow_control != MM_FLOW_CONTROL_NONE) { gchar *flow_control_str; flow_control_str = mm_flow_control_build_string_from_mask (self->priv->flow_control); mm_obj_dbg (self, "setting flow control in %s: %s", mm_port_get_device (ctx->data), flow_control_str); g_free (flow_control_str); if (!mm_port_serial_set_flow_control (MM_PORT_SERIAL (ctx->data), self->priv->flow_control, &error)) { mm_obj_warn (self, "couldn't set flow control settings in %s: %s", mm_port_get_device (ctx->data), error->message); g_clear_error (&error); } } /* The ATD command has succeeded, and therefore the TTY is in data mode now. * Instead of waiting for setting the port as connected later in * connect_succeeded(), we do it right away so that we stop our polling. */ mm_port_set_connected (ctx->data, TRUE); /* Keep port open during connection */ ctx->close_data_on_exit = FALSE; /* Generic CDMA connections are done over PPP always */ g_assert (MM_IS_PORT_SERIAL_AT (ctx->data)); config = mm_bearer_ip_config_new (); mm_bearer_ip_config_set_method (config, MM_BEARER_IP_METHOD_PPP); /* Assume only IPv4 is given */ g_task_return_pointer ( task, mm_bearer_connect_result_new (ctx->data, config, NULL), (GDestroyNotify)mm_bearer_connect_result_unref); g_object_unref (task); g_object_unref (config); } static void cdma_connect_context_dial (GTask *task) { DetailedConnectContext *ctx; ctx = g_task_get_task_data (task); mm_base_modem_at_command_full (ctx->modem, MM_PORT_SERIAL_AT (ctx->data), "DT#777", MM_BASE_BEARER_DEFAULT_CONNECTION_TIMEOUT, FALSE, FALSE, NULL, (GAsyncReadyCallback)dial_cdma_ready, task); } static void set_rm_protocol_ready (MMBaseModem *self, GAsyncResult *res, GTask *task) { GError *error = NULL; /* If cancelled, complete */ if (g_task_return_error_if_cancelled (task)) { g_object_unref (task); return; } mm_base_modem_at_command_full_finish (self, res, &error); if (error) { mm_obj_warn (self, "couldn't set RM protocol: %s", error->message); g_task_return_error (task, error); g_object_unref (task); return; } /* Nothing else needed, go on with dialing */ cdma_connect_context_dial (task); } static void current_rm_protocol_ready (MMBaseModem *self, GAsyncResult *res, GTask *task) { const gchar *result; GError *error = NULL; guint current_index; MMModemCdmaRmProtocol current_rm; /* If cancelled, complete */ if (g_task_return_error_if_cancelled (task)) { g_object_unref (task); return; } result = mm_base_modem_at_command_full_finish (self, res, &error); if (error) { mm_obj_warn (self, "couldn't query current RM protocol: %s", error->message); g_task_return_error (task, error); g_object_unref (task); return; } result = mm_strip_tag (result, "+CRM:"); current_index = (guint) atoi (result); current_rm = mm_cdma_get_rm_protocol_from_index (current_index, &error); if (error) { mm_obj_warn (self, "couldn't parse RM protocol reply (%s): %s", result, error->message); g_task_return_error (task, error); g_object_unref (task); return; } if (current_rm != mm_bearer_properties_get_rm_protocol (mm_base_bearer_peek_config (MM_BASE_BEARER (self)))) { DetailedConnectContext *ctx; guint new_index; gchar *command; mm_obj_dbg (self, "setting requested RM protocol..."); new_index = (mm_cdma_get_index_from_rm_protocol ( mm_bearer_properties_get_rm_protocol (mm_base_bearer_peek_config (MM_BASE_BEARER (self))), &error)); if (error) { mm_obj_warn (self, "cannot set RM protocol: %s", error->message); g_task_return_error (task, error); g_object_unref (task); return; } ctx = g_task_get_task_data (task); command = g_strdup_printf ("+CRM=%u", new_index); mm_base_modem_at_command_full (ctx->modem, ctx->primary, command, 3, FALSE, FALSE, NULL, (GAsyncReadyCallback)set_rm_protocol_ready, task); g_free (command); return; } /* Nothing else needed, go on with dialing */ cdma_connect_context_dial (task); } static void connect_cdma (MMBroadbandBearer *self, MMBroadbandModem *modem, MMPortSerialAt *primary, MMPortSerialAt *secondary, /* unused by us */ GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { DetailedConnectContext *ctx; GTask *task; GError *error = NULL; g_assert (primary != NULL); ctx = detailed_connect_context_new (self, modem, primary, NULL); task = g_task_new (self, cancellable, callback, user_data); g_task_set_task_data (task, ctx, (GDestroyNotify)detailed_connect_context_free); /* Grab dial port. This gets a reference to the dial port and OPENs it. * If we fail, we'll need to close it ourselves. */ ctx->data = (MMPort *)common_get_at_data_port (self, ctx->modem, &error); if (!ctx->data) { g_task_return_error (task, error); g_object_unref (task); return; } ctx->close_data_on_exit = TRUE; if (mm_bearer_properties_get_rm_protocol ( mm_base_bearer_peek_config (MM_BASE_BEARER (self))) != MM_MODEM_CDMA_RM_PROTOCOL_UNKNOWN) { /* Need to query current RM protocol */ mm_obj_dbg (self, "querying current RM protocol set..."); mm_base_modem_at_command_full (ctx->modem, ctx->primary, "+CRM?", 3, FALSE, FALSE, /* raw */ NULL, /* cancellable */ (GAsyncReadyCallback)current_rm_protocol_ready, task); return; } /* Nothing else needed, go on with dialing */ cdma_connect_context_dial (task); } /*****************************************************************************/ /* 3GPP Dialing (sub-step of the 3GPP Connection sequence) */ typedef struct { MMBaseModem *modem; MMPortSerialAt *primary; GError *saved_error; MMPortSerialAt *dial_port; gboolean close_dial_port_on_exit; } Dial3gppContext; static void dial_3gpp_context_free (Dial3gppContext *ctx) { if (ctx->saved_error) g_error_free (ctx->saved_error); if (ctx->dial_port) g_object_unref (ctx->dial_port); g_object_unref (ctx->primary); g_object_unref (ctx->modem); g_slice_free (Dial3gppContext, ctx); } static MMPort * dial_3gpp_finish (MMBroadbandBearer *self, GAsyncResult *res, GError **error) { return g_task_propagate_pointer (G_TASK (res), error); } static void extended_error_ready (MMBaseModem *modem, GAsyncResult *res, GTask *task) { Dial3gppContext *ctx; const gchar *result; GError *error = NULL; ctx = g_task_get_task_data (task); /* Close the dialling port as we got an error */ mm_port_serial_close (MM_PORT_SERIAL (ctx->dial_port)); /* If cancelled, complete */ if (g_task_return_error_if_cancelled (task)) { g_object_unref (task); return; } result = mm_base_modem_at_command_full_finish (modem, res, NULL); if (result && g_str_has_prefix (result, "+CEER: ") && strlen (result) > 7) { error = g_error_new (ctx->saved_error->domain, ctx->saved_error->code, "%s", &result[7]); g_error_free (ctx->saved_error); } else g_propagate_error (&error, ctx->saved_error); ctx->saved_error = NULL; /* Done with errors */ g_task_return_error (task, error); g_object_unref (task); } static void atd_ready (MMBaseModem *modem, GAsyncResult *res, GTask *task) { MMBroadbandBearer *self; Dial3gppContext *ctx; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); /* DO NOT check for cancellable here. If we got here without errors, the * bearer is really connected and therefore we need to reflect that in * the state machine. */ mm_base_modem_at_command_full_finish (modem, res, &ctx->saved_error); if (ctx->saved_error) { /* Try to get more information why it failed */ mm_base_modem_at_command_full (ctx->modem, ctx->primary, "+CEER", 3, FALSE, FALSE, /* raw */ NULL, /* cancellable */ (GAsyncReadyCallback)extended_error_ready, task); return; } /* Configure flow control to use while connected */ if (self->priv->flow_control != MM_FLOW_CONTROL_NONE) { gchar *flow_control_str; GError *error = NULL; flow_control_str = mm_flow_control_build_string_from_mask (self->priv->flow_control); mm_obj_dbg (self, "setting flow control in %s: %s", mm_port_get_device (MM_PORT (ctx->dial_port)), flow_control_str); g_free (flow_control_str); if (!mm_port_serial_set_flow_control (MM_PORT_SERIAL (ctx->dial_port), self->priv->flow_control, &error)) { mm_obj_warn (self, "couldn't set flow control settings in %s: %s", mm_port_get_device (MM_PORT (ctx->dial_port)), error->message); g_clear_error (&error); } } /* The ATD command has succeeded, and therefore the TTY is in data mode now. * Instead of waiting for setting the port as connected later in * connect_succeeded(), we do it right away so that we stop our polling. */ mm_port_set_connected (MM_PORT (ctx->dial_port), TRUE); g_task_return_pointer (task, g_object_ref (ctx->dial_port), g_object_unref); g_object_unref (task); } static void dial_3gpp (MMBroadbandBearer *self, MMBaseModem *modem, MMPortSerialAt *primary, guint cid, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { gchar *command; Dial3gppContext *ctx; GTask *task; GError *error = NULL; g_assert (primary != NULL); ctx = g_slice_new0 (Dial3gppContext); ctx->modem = g_object_ref (modem); ctx->primary = g_object_ref (primary); task = g_task_new (self, cancellable, callback, user_data); g_task_set_task_data (task, ctx, (GDestroyNotify)dial_3gpp_context_free); /* Grab dial port. This gets a reference to the dial port and OPENs it. * If we fail, we'll need to close it ourselves. */ ctx->dial_port = common_get_at_data_port (self, ctx->modem, &error); if (!ctx->dial_port) { g_task_return_error (task, error); g_object_unref (task); return; } /* Use default *99 to connect */ command = g_strdup_printf ("ATD*99***%d#", cid); mm_base_modem_at_command_full (ctx->modem, ctx->dial_port, command, MM_BASE_BEARER_DEFAULT_CONNECTION_TIMEOUT, FALSE, FALSE, /* raw */ NULL, /* cancellable */ (GAsyncReadyCallback)atd_ready, task); g_free (command); } /*****************************************************************************/ /* 3GPP cid selection (sub-step of the 3GPP Connection sequence) */ typedef struct { MMBaseModem *modem; GCancellable *cancellable; gint profile_id; } SelectProfile3gppContext; static void select_profile_3gpp_context_free (SelectProfile3gppContext *ctx) { g_object_unref (ctx->modem); g_object_unref (ctx->cancellable); g_slice_free (SelectProfile3gppContext, ctx); } static gint select_profile_3gpp_finish (MMBroadbandBearer *self, GAsyncResult *res, GError **error) { gint profile_id; /* returns -1 on failure */ profile_id = (gint) g_task_propagate_int (G_TASK (res), error); return (profile_id < 0) ? MM_3GPP_PROFILE_ID_UNKNOWN : profile_id; } static void select_profile_3gpp_set_profile_ready (MMIfaceModem3gppProfileManager *modem, GAsyncResult *res, GTask *task) { GError *error = NULL; g_autoptr(MM3gppProfile) profile = NULL; profile = mm_iface_modem_3gpp_profile_manager_set_profile_finish (modem, res, &error); if (!profile) g_task_return_error (task, error); else g_task_return_int (task, mm_3gpp_profile_get_profile_id (profile)); g_object_unref (task); } static void select_profile_3gpp_get_profile_ready (MMIfaceModem3gppProfileManager *modem, GAsyncResult *res, GTask *task) { SelectProfile3gppContext *ctx; GError *error = NULL; g_autoptr(MM3gppProfile) profile = NULL; ctx = g_task_get_task_data (task); profile = mm_iface_modem_3gpp_profile_manager_get_profile_finish (modem, res, &error); if (!profile) g_task_return_error (task, error); else g_task_return_int (task, ctx->profile_id); g_object_unref (task); } static void select_profile_3gpp (MMBroadbandBearer *self, MMBaseModem *modem, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; SelectProfile3gppContext *ctx; MMBearerProperties *bearer_properties; task = g_task_new (self, cancellable, callback, user_data); ctx = g_slice_new0 (SelectProfile3gppContext); ctx->modem = g_object_ref (modem); ctx->cancellable = g_object_ref (cancellable); bearer_properties = mm_base_bearer_peek_config (MM_BASE_BEARER (self)); ctx->profile_id = mm_bearer_properties_get_profile_id (bearer_properties); g_task_set_task_data (task, ctx, (GDestroyNotify)select_profile_3gpp_context_free); if (ctx->profile_id == MM_3GPP_PROFILE_ID_UNKNOWN) { mm_iface_modem_3gpp_profile_manager_set_profile ( MM_IFACE_MODEM_3GPP_PROFILE_MANAGER (modem), mm_bearer_properties_peek_3gpp_profile (bearer_properties), "profile-id", FALSE, /* not strict! */ (GAsyncReadyCallback)select_profile_3gpp_set_profile_ready, task); return; } mm_iface_modem_3gpp_profile_manager_get_profile ( MM_IFACE_MODEM_3GPP_PROFILE_MANAGER (modem), ctx->profile_id, (GAsyncReadyCallback)select_profile_3gpp_get_profile_ready, task); } /*****************************************************************************/ /* 3GPP CONNECT * * 3GPP connection procedure of a bearer involves several steps: * 1) Get data port from the modem. Default implementation will have only * one single possible data port, but plugins may have more. * 2) Decide which PDP context to use * 2.1) Look for an already existing PDP context with the same APN. * 2.2) If none found with the same APN, try to find a PDP context without any * predefined APN. * 2.3) If none found, look for the highest available CID, and use that one. * 3) Activate PDP context. * 4) Initiate call. */ static void get_ip_config_3gpp_ready (MMBroadbandBearer *self, GAsyncResult *res, GTask *task) { DetailedConnectContext *ctx; GError *error = NULL; g_autoptr(MMBearerConnectResult) result = NULL; g_autoptr(MMBearerIpConfig) ipv4_config = NULL; g_autoptr(MMBearerIpConfig) ipv6_config = NULL; ctx = g_task_get_task_data (task); if (!MM_BROADBAND_BEARER_GET_CLASS (self)->get_ip_config_3gpp_finish (self, res, &ipv4_config, &ipv6_config, &error)) { g_task_return_error (task, error); g_object_unref (task); return; } /* Keep port open during connection */ if (MM_IS_PORT_SERIAL_AT (ctx->data)) ctx->close_data_on_exit = FALSE; result = mm_bearer_connect_result_new (ctx->data, ipv4_config, ipv6_config); mm_bearer_connect_result_set_profile_id (result, self->priv->profile_id); g_task_return_pointer (task, g_steal_pointer (&result), (GDestroyNotify)mm_bearer_connect_result_unref); g_object_unref (task); } static void dial_3gpp_ready (MMBroadbandBearer *self, GAsyncResult *res, GTask *task) { DetailedConnectContext *ctx; MMBearerIpMethod ip_method = MM_BEARER_IP_METHOD_UNKNOWN; GError *error = NULL; g_autoptr(MMBearerConnectResult) result = NULL; g_autoptr(MMBearerIpConfig) ipv4_config = NULL; g_autoptr(MMBearerIpConfig) ipv6_config = NULL; ctx = g_task_get_task_data (task); ctx->data = MM_BROADBAND_BEARER_GET_CLASS (self)->dial_3gpp_finish (self, res, &error); if (!ctx->data) { /* Clear CID when it failed to connect. */ self->priv->profile_id = MM_3GPP_PROFILE_ID_UNKNOWN; g_task_return_error (task, error); g_object_unref (task); return; } /* If the dialling operation used an AT port, it is assumed to have an extra * open() count. */ if (MM_IS_PORT_SERIAL_AT (ctx->data)) ctx->close_data_on_exit = TRUE; if (MM_BROADBAND_BEARER_GET_CLASS (self)->get_ip_config_3gpp && MM_BROADBAND_BEARER_GET_CLASS (self)->get_ip_config_3gpp_finish) { /* Launch specific IP config retrieval */ MM_BROADBAND_BEARER_GET_CLASS (self)->get_ip_config_3gpp ( self, MM_BROADBAND_MODEM (ctx->modem), ctx->primary, ctx->secondary, ctx->data, (guint)self->priv->profile_id, ctx->ip_family, (GAsyncReadyCallback)get_ip_config_3gpp_ready, task); return; } /* Yuhu! */ /* Keep port open during connection */ if (MM_IS_PORT_SERIAL_AT (ctx->data)) ctx->close_data_on_exit = FALSE; /* If no specific IP retrieval requested, set the default implementation * (PPP if data port is AT, DHCP otherwise) */ ip_method = MM_IS_PORT_SERIAL_AT (ctx->data) ? MM_BEARER_IP_METHOD_PPP : MM_BEARER_IP_METHOD_DHCP; if (ctx->ip_family & MM_BEARER_IP_FAMILY_IPV4 || ctx->ip_family & MM_BEARER_IP_FAMILY_IPV4V6) { ipv4_config = mm_bearer_ip_config_new (); mm_bearer_ip_config_set_method (ipv4_config, ip_method); } if (ctx->ip_family & MM_BEARER_IP_FAMILY_IPV6 || ctx->ip_family & MM_BEARER_IP_FAMILY_IPV4V6) { ipv6_config = mm_bearer_ip_config_new (); mm_bearer_ip_config_set_method (ipv6_config, ip_method); } g_assert (ipv4_config || ipv6_config); result = mm_bearer_connect_result_new (ctx->data, ipv4_config, ipv6_config); mm_bearer_connect_result_set_profile_id (result, self->priv->profile_id); g_task_return_pointer (task, g_steal_pointer (&result), (GDestroyNotify)mm_bearer_connect_result_unref); g_object_unref (task); } static void select_profile_3gpp_ready (MMBroadbandBearer *self, GAsyncResult *res, GTask *task) { DetailedConnectContext *ctx; GError *error = NULL; ctx = g_task_get_task_data (task); /* Keep CID around after initializing the PDP context in order to * handle corresponding unsolicited PDP activation responses. */ self->priv->profile_id = select_profile_3gpp_finish (self, res, &error); if (self->priv->profile_id == MM_3GPP_PROFILE_ID_UNKNOWN) { g_task_return_error (task, error); g_object_unref (task); return; } MM_BROADBAND_BEARER_GET_CLASS (self)->dial_3gpp (self, ctx->modem, ctx->primary, (guint) self->priv->profile_id, g_task_get_cancellable (task), (GAsyncReadyCallback) dial_3gpp_ready, task); } static void connect_3gpp (MMBroadbandBearer *self, MMBroadbandModem *modem, MMPortSerialAt *primary, MMPortSerialAt *secondary, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { DetailedConnectContext *ctx; GTask *task; g_assert (primary != NULL); /* Clear CID on every connection attempt */ self->priv->profile_id = MM_3GPP_PROFILE_ID_UNKNOWN; ctx = detailed_connect_context_new (self, modem, primary, secondary); task = g_task_new (self, cancellable, callback, user_data); g_task_set_task_data (task, ctx, (GDestroyNotify)detailed_connect_context_free); select_profile_3gpp (self, ctx->modem, cancellable, (GAsyncReadyCallback)select_profile_3gpp_ready, task); } /*****************************************************************************/ /* CONNECT */ static MMBearerConnectResult * connect_finish (MMBaseBearer *self, GAsyncResult *res, GError **error) { return g_task_propagate_pointer (G_TASK (res), error); } static void connect_succeeded (GTask *task, ConnectionType connection_type, MMBearerConnectResult *result) { MMBroadbandBearer *self; self = g_task_get_source_object (task); /* Keep connected port and type of connection */ self->priv->port = g_object_ref (mm_bearer_connect_result_peek_data (result)); self->priv->connection_type = connection_type; /* Save profile ID for 3GPP connections. This is required for cases where * the standard profile selection of this class is not executed during * connection, e.g. if a derived class implements their own connect_3gpp. */ self->priv->profile_id = mm_bearer_connect_result_get_profile_id (result); /* Port is connected; update the state. For ATD based connections, the port * may already be set as connected, but no big deal. */ mm_port_set_connected (self->priv->port, TRUE); /* Set operation result */ g_task_return_pointer (task, result, (GDestroyNotify)mm_bearer_connect_result_unref); g_object_unref (task); } static void connect_cdma_ready (MMBroadbandBearer *self, GAsyncResult *res, GTask *task) { MMBearerConnectResult *result; GError *error = NULL; result = MM_BROADBAND_BEARER_GET_CLASS (self)->connect_cdma_finish (self, res, &error); if (!result) { g_task_return_error (task, error); g_object_unref (task); return; } /* take result */ connect_succeeded (task, CONNECTION_TYPE_CDMA, result); } static void connect_3gpp_ready (MMBroadbandBearer *self, GAsyncResult *res, GTask *task) { MMBearerConnectResult *result; GError *error = NULL; result = MM_BROADBAND_BEARER_GET_CLASS (self)->connect_3gpp_finish (self, res, &error); if (!result) { g_task_return_error (task, error); g_object_unref (task); return; } /* take result */ connect_succeeded (task, CONNECTION_TYPE_3GPP, result); } static void connect (MMBaseBearer *self, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { MMPortSerialAt *primary; const gchar *apn; gint profile_id; MMBearerMultiplexSupport multiplex; GTask *task; g_autoptr(MMBaseModem) modem = NULL; task = g_task_new (self, cancellable, callback, user_data); /* Don't try to connect if already connected */ if (MM_BROADBAND_BEARER (self)->priv->port) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_CONNECTED, "Couldn't connect: this bearer is already connected"); g_object_unref (task); return; } /* Get the owner modem object */ g_object_get (self, MM_BASE_BEARER_MODEM, &modem, NULL); g_assert (modem != NULL); /* We will launch the ATD call in the primary port... */ primary = mm_base_modem_peek_port_primary (modem); if (!primary) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_CONNECTED, "Couldn't connect: couldn't get primary port"); g_object_unref (task); return; } /* ...only if not already connected */ if (mm_port_get_connected (MM_PORT (primary))) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_CONNECTED, "Couldn't connect: primary AT port is already connected"); g_object_unref (task); return; } /* Default bearer connection logic * * 1) 3GPP-only modem: * 1a) If no profile id or APN given, error. * 1b) If APN or profile id given, run 3GPP connection logic. * 1c) If APN given, but empty (""), run 3GPP connection logic and try * to use default subscription APN. * * 2) 3GPP2-only modem: * 2a) If no APN or profile id given, run CDMA connection logic. * 2b) If APN or profile id given, error. * * 3) 3GPP and 3GPP2 modem: * 3a) If no profile id or APN given, run CDMA connection logic. * 3b) If APN given, run 3GPP connection logic. * 3c) If APN given, but empty (""), run 3GPP connection logic and try * to use default subscription APN. */ /* Check whether we have an APN */ apn = mm_bearer_properties_get_apn (mm_base_bearer_peek_config (MM_BASE_BEARER (self))); profile_id = mm_bearer_properties_get_profile_id (mm_base_bearer_peek_config (MM_BASE_BEARER (self))); /* Is this a 3GPP only modem and no APN or profile id was given? If so, error */ if (mm_iface_modem_is_3gpp_only (MM_IFACE_MODEM (modem)) && !apn && (profile_id == MM_3GPP_PROFILE_ID_UNKNOWN)) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS, "3GPP connection logic requires APN or profile id setting"); g_object_unref (task); return; } /* Is this a 3GPP2 only modem and APN or profile id was given? If so, error */ if (mm_iface_modem_is_cdma_only (MM_IFACE_MODEM (modem)) && (apn || (profile_id != MM_3GPP_PROFILE_ID_UNKNOWN))) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS, "3GPP2 doesn't support APN or profile id setting"); g_object_unref (task); return; } /* If no multiplex setting given by the user, assume none (which is the only * supported mode anyway) */ multiplex = mm_bearer_properties_get_multiplex (mm_base_bearer_peek_config (MM_BASE_BEARER (self))); if (multiplex == MM_BEARER_MULTIPLEX_SUPPORT_UNKNOWN) multiplex = MM_BEARER_MULTIPLEX_SUPPORT_NONE; /* The generic broadband bearer doesn't support multiplexing */ if (multiplex == MM_BEARER_MULTIPLEX_SUPPORT_REQUIRED) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "Multiplexing required but not supported"); g_object_unref (task); return; } /* If the modem has 3GPP capabilities and an APN, launch 3GPP-based connection */ if (mm_iface_modem_is_3gpp (MM_IFACE_MODEM (modem)) && (apn || (profile_id != MM_3GPP_PROFILE_ID_UNKNOWN))) { mm_obj_dbg (self, "launching 3GPP connection attempt"); MM_BROADBAND_BEARER_GET_CLASS (self)->connect_3gpp ( MM_BROADBAND_BEARER (self), MM_BROADBAND_MODEM (modem), primary, mm_base_modem_peek_port_secondary (modem), cancellable, (GAsyncReadyCallback) connect_3gpp_ready, task); return; } /* Otherwise, launch CDMA-specific connection. */ if (mm_iface_modem_is_cdma (MM_IFACE_MODEM (modem)) && !apn && (profile_id == MM_3GPP_PROFILE_ID_UNKNOWN)) { mm_obj_dbg (self, "launching 3GPP2 connection attempt"); MM_BROADBAND_BEARER_GET_CLASS (self)->connect_cdma ( MM_BROADBAND_BEARER (self), MM_BROADBAND_MODEM (modem), primary, mm_base_modem_peek_port_secondary (modem), cancellable, (GAsyncReadyCallback) connect_cdma_ready, task); return; } g_assert_not_reached (); } /*****************************************************************************/ /* Detailed disconnect context, used in both CDMA and 3GPP sequences */ typedef struct { MMBaseModem *modem; MMPortSerialAt *primary; MMPortSerialAt *secondary; MMPort *data; /* 3GPP-specific */ gchar *cgact_command; gboolean cgact_sent; } DetailedDisconnectContext; static gboolean detailed_disconnect_finish (MMBroadbandBearer *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void detailed_disconnect_context_free (DetailedDisconnectContext *ctx) { g_free (ctx->cgact_command); g_object_unref (ctx->data); g_object_unref (ctx->primary); if (ctx->secondary) g_object_unref (ctx->secondary); g_object_unref (ctx->modem); g_free (ctx); } static DetailedDisconnectContext * detailed_disconnect_context_new (MMBroadbandModem *modem, MMPortSerialAt *primary, MMPortSerialAt *secondary, MMPort *data) { DetailedDisconnectContext *ctx; ctx = g_new0 (DetailedDisconnectContext, 1); ctx->modem = MM_BASE_MODEM (g_object_ref (modem)); ctx->primary = g_object_ref (primary); ctx->secondary = (secondary ? g_object_ref (secondary) : NULL); ctx->data = g_object_ref (data); return ctx; } /*****************************************************************************/ /* CDMA disconnect */ static void data_flash_cdma_ready (MMPortSerial *data, GAsyncResult *res, GTask *task) { MMBroadbandBearer *self; GError *error = NULL; self = g_task_get_source_object (task); mm_port_serial_flash_finish (data, res, &error); /* We kept the serial port open during connection, now we close that open * count */ mm_port_serial_close (data); /* Port is disconnected; update the state */ mm_port_set_connected (MM_PORT (data), FALSE); if (error) { /* Ignore "NO CARRIER" response when modem disconnects and any flash * failures we might encounter. Other errors are hard errors. */ if (!g_error_matches (error, MM_CONNECTION_ERROR, MM_CONNECTION_ERROR_NO_CARRIER) && !g_error_matches (error, MM_SERIAL_ERROR, MM_SERIAL_ERROR_FLASH_FAILED)) { /* Fatal */ g_task_return_error (task, error); g_object_unref (task); return; } mm_obj_dbg (self, "port flashing failed (not fatal): %s", error->message); g_error_free (error); } /* Run init port sequence in the data port */ mm_port_serial_at_run_init_sequence (MM_PORT_SERIAL_AT (data)); g_task_return_boolean (task, TRUE); g_object_unref (task); } static void data_reopen_cdma_ready (MMPortSerial *data, GAsyncResult *res, GTask *task) { MMBroadbandBearer *self; DetailedDisconnectContext *ctx; GError *error = NULL; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); g_object_set (data, MM_PORT_SERIAL_AT_INIT_SEQUENCE_ENABLED, TRUE, NULL); if (!mm_port_serial_reopen_finish (data, res, &error)) { /* Fatal */ g_task_return_error (task, error); g_object_unref (task); return; } /* Just flash the data port */ mm_obj_dbg (self, "flashing data port %s...", mm_port_get_device (MM_PORT (ctx->data))); mm_port_serial_flash (MM_PORT_SERIAL (ctx->data), 1000, TRUE, (GAsyncReadyCallback)data_flash_cdma_ready, task); } static void disconnect_cdma (MMBroadbandBearer *self, MMBroadbandModem *modem, MMPortSerialAt *primary, MMPortSerialAt *secondary, MMPort *data, GAsyncReadyCallback callback, gpointer user_data) { DetailedDisconnectContext *ctx; GTask *task; g_assert (primary != NULL); /* Generic CDMA plays only with SERIAL data ports */ g_assert (MM_IS_PORT_SERIAL (data)); ctx = detailed_disconnect_context_new (modem, primary, secondary, data); task = g_task_new (self, NULL, callback, user_data); g_task_set_task_data (task, ctx, (GDestroyNotify)detailed_disconnect_context_free); /* We don't want to run init sequence right away during the reopen, as we're * going to flash afterwards. */ g_object_set (data, MM_PORT_SERIAL_AT_INIT_SEQUENCE_ENABLED, FALSE, NULL); /* Fully reopen the port before flashing */ mm_obj_dbg (self, "reopening data port %s...", mm_port_get_device (MM_PORT (ctx->data))); mm_port_serial_reopen (MM_PORT_SERIAL (ctx->data), 1000, (GAsyncReadyCallback)data_reopen_cdma_ready, task); } /*****************************************************************************/ /* 3GPP disconnect */ static void cgact_data_ready (MMBaseModem *modem, GAsyncResult *res, GTask *task) { MMBroadbandBearer *self; GError *error = NULL; self = g_task_get_source_object (task); /* Ignore errors for now */ mm_base_modem_at_command_full_finish (modem, res, &error); if (error) { mm_obj_dbg (self, "PDP context deactivation failed (not fatal): %s", error->message); g_error_free (error); } g_task_return_boolean (task, TRUE); g_object_unref (task); } static void data_flash_3gpp_ready (MMPortSerial *data, GAsyncResult *res, GTask *task) { MMBroadbandBearer *self; DetailedDisconnectContext *ctx; GError *error = NULL; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); mm_port_serial_flash_finish (data, res, &error); /* We kept the serial port open during connection, now we close that open * count */ mm_port_serial_close (data); /* Port is disconnected; update the state */ mm_port_set_connected (MM_PORT (data), FALSE); if (error) { /* Ignore "NO CARRIER" response when modem disconnects and any flash * failures we might encounter. Other errors are hard errors. */ if (!g_error_matches (error, MM_CONNECTION_ERROR, MM_CONNECTION_ERROR_NO_CARRIER) && !g_error_matches (error, MM_SERIAL_ERROR, MM_SERIAL_ERROR_FLASH_FAILED)) { /* Fatal */ g_task_return_error (task, error); g_object_unref (task); return; } mm_obj_dbg (self, "port flashing failed (not fatal): %s", error->message); g_error_free (error); } /* Run init port sequence in the data port */ mm_port_serial_at_run_init_sequence (MM_PORT_SERIAL_AT (data)); /* Don't bother doing the CGACT again if it was already done on the * primary or secondary port */ if (ctx->cgact_sent) { mm_obj_dbg (self, "PDP disconnection already sent"); g_task_return_boolean (task, TRUE); g_object_unref (task); return; } /* Send another CGACT on the primary port (also the data port when the modem * only has one serial port) if the previous one failed. Some modems, like * the Huawei E173 (fw 11.126.15.00.445) stop responding on their primary * port when the CGACT is sent on the separte data port. */ if (MM_PORT_SERIAL (ctx->primary) == data) mm_obj_dbg (self, "sending PDP context deactivation in primary/data port..."); else mm_obj_dbg (self, "sending PDP context deactivation in primary port again..."); mm_base_modem_at_command_full (ctx->modem, ctx->primary, ctx->cgact_command, 10, FALSE, FALSE, /* raw */ NULL, /* cancellable */ (GAsyncReadyCallback)cgact_data_ready, task); } static void data_reopen_3gpp_ready (MMPortSerial *data, GAsyncResult *res, GTask *task) { MMBroadbandBearer *self; DetailedDisconnectContext *ctx; GError *error = NULL; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); g_object_set (data, MM_PORT_SERIAL_AT_INIT_SEQUENCE_ENABLED, TRUE, NULL); if (!mm_port_serial_reopen_finish (data, res, &error)) { /* Fatal */ g_task_return_error (task, error); g_object_unref (task); return; } /* Just flash the data port */ mm_obj_dbg (self, "flashing data port %s...", mm_port_get_device (MM_PORT (ctx->data))); mm_port_serial_flash (MM_PORT_SERIAL (ctx->data), 1000, TRUE, (GAsyncReadyCallback)data_flash_3gpp_ready, task); } static void data_reopen_3gpp (GTask *task) { MMBroadbandBearer *self; DetailedDisconnectContext *ctx; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); /* We don't want to run init sequence right away during the reopen, as we're * going to flash afterwards. */ g_object_set (ctx->data, MM_PORT_SERIAL_AT_INIT_SEQUENCE_ENABLED, FALSE, NULL); /* Fully reopen the port before flashing */ mm_obj_dbg (self, "reopening data port %s...", mm_port_get_device (MM_PORT (ctx->data))); mm_port_serial_reopen (MM_PORT_SERIAL (ctx->data), 1000, (GAsyncReadyCallback)data_reopen_3gpp_ready, task); } static void cgact_ready (MMBaseModem *modem, GAsyncResult *res, GTask *task) { MMBroadbandBearer *self; DetailedDisconnectContext *ctx; GError *error = NULL; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); mm_base_modem_at_command_full_finish (modem, res, &error); if (!error) ctx->cgact_sent = TRUE; else { mm_obj_dbg (self, "PDP context deactivation failed (not fatal): %s", error->message); g_error_free (error); } data_reopen_3gpp (task); } static void disconnect_3gpp (MMBroadbandBearer *self, MMBroadbandModem *modem, MMPortSerialAt *primary, MMPortSerialAt *secondary, MMPort *data, guint cid, GAsyncReadyCallback callback, gpointer user_data) { DetailedDisconnectContext *ctx; GTask *task; g_assert (primary != NULL); /* Generic 3GPP plays only with SERIAL data ports */ g_assert (MM_IS_PORT_SERIAL (data)); ctx = detailed_disconnect_context_new (modem, primary, secondary, data); /* If no specific CID was used, disable all PDP contexts */ ctx->cgact_command = (cid > 0 ? g_strdup_printf ("+CGACT=0,%d", cid) : g_strdup_printf ("+CGACT=0")); task = g_task_new (self, NULL, callback, user_data); g_task_set_task_data (task, ctx, (GDestroyNotify)detailed_disconnect_context_free); /* If the primary port is NOT connected (doesn't have to be the data port), * we'll send CGACT there */ if (!mm_port_get_connected (MM_PORT (ctx->primary))) { mm_obj_dbg (self, "sending PDP context deactivation in primary port..."); mm_base_modem_at_command_full (ctx->modem, ctx->primary, ctx->cgact_command, 45, FALSE, FALSE, /* raw */ NULL, /* cancellable */ (GAsyncReadyCallback)cgact_ready, task); return; } /* If the primary port is connected, then try sending the PDP * context deactivation on the secondary port because not all modems will * respond to flashing (since either the modem or the kernel's serial * driver doesn't support it). */ if (ctx->secondary) { mm_obj_dbg (self, "sending PDP context deactivation in secondary port..."); mm_base_modem_at_command_full (ctx->modem, ctx->secondary, ctx->cgact_command, 45, FALSE, FALSE, /* raw */ NULL, /* cancellable */ (GAsyncReadyCallback)cgact_ready, task); return; } /* If no secondary port, go on to reopen & flash the data/primary port */ data_reopen_3gpp (task); } /*****************************************************************************/ /* DISCONNECT */ static gboolean disconnect_finish (MMBaseBearer *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void reset_bearer_connection (MMBroadbandBearer *self) { if (self->priv->port) { /* Port is disconnected; update the state. Note: implementations may * already have set the port as disconnected (e.g the 3GPP one) */ mm_port_set_connected (self->priv->port, FALSE); /* Clear data port */ g_clear_object (&self->priv->port); } /* Reset current connection type */ self->priv->connection_type = CONNECTION_TYPE_NONE; } static void disconnect_cdma_ready (MMBroadbandBearer *self, GAsyncResult *res, GTask *task) { GError *error = NULL; if (!MM_BROADBAND_BEARER_GET_CLASS (self)->disconnect_cdma_finish (self, res, &error)) g_task_return_error (task, error); else { /* Cleanup all connection related data */ reset_bearer_connection (self); g_task_return_boolean (task, TRUE); } g_object_unref (task); } static void disconnect_3gpp_ready (MMBroadbandBearer *self, GAsyncResult *res, GTask *task) { GError *error = NULL; if (!MM_BROADBAND_BEARER_GET_CLASS (self)->disconnect_3gpp_finish (self, res, &error)) g_task_return_error (task, error); else { /* Clear CID if we got any set */ self->priv->profile_id = MM_3GPP_PROFILE_ID_UNKNOWN; /* Cleanup all connection related data */ reset_bearer_connection (self); g_task_return_boolean (task, TRUE); } g_object_unref (task); } static void disconnect (MMBaseBearer *self, GAsyncReadyCallback callback, gpointer user_data) { MMPortSerialAt *primary; MMBaseModem *modem = NULL; GTask *task; task = g_task_new (self, NULL, callback, user_data); if (!MM_BROADBAND_BEARER (self)->priv->port) { mm_obj_dbg (self, "no need to disconnect: bearer is already disconnected"); g_task_return_boolean (task, TRUE); g_object_unref (task); return; } g_object_get (self, MM_BASE_BEARER_MODEM, &modem, NULL); g_assert (modem != NULL); /* We need the primary port to disconnect... */ primary = mm_base_modem_peek_port_primary (modem); if (!primary) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't disconnect: couldn't get primary port"); g_object_unref (task); g_object_unref (modem); return; } switch (MM_BROADBAND_BEARER (self)->priv->connection_type) { case CONNECTION_TYPE_3GPP: MM_BROADBAND_BEARER_GET_CLASS (self)->disconnect_3gpp ( MM_BROADBAND_BEARER (self), MM_BROADBAND_MODEM (modem), primary, mm_base_modem_peek_port_secondary (modem), MM_BROADBAND_BEARER (self)->priv->port, (guint)MM_BROADBAND_BEARER (self)->priv->profile_id, (GAsyncReadyCallback) disconnect_3gpp_ready, task); break; case CONNECTION_TYPE_CDMA: MM_BROADBAND_BEARER_GET_CLASS (self)->disconnect_cdma ( MM_BROADBAND_BEARER (self), MM_BROADBAND_MODEM (modem), primary, mm_base_modem_peek_port_secondary (modem), MM_BROADBAND_BEARER (self)->priv->port, (GAsyncReadyCallback) disconnect_cdma_ready, task); break; case CONNECTION_TYPE_NONE: default: g_assert_not_reached (); } g_object_unref (modem); } /*****************************************************************************/ /* Connection status monitoring */ static MMBearerConnectionStatus load_connection_status_finish (MMBaseBearer *bearer, GAsyncResult *res, GError **error) { GError *inner_error = NULL; gssize value; value = g_task_propagate_int (G_TASK (res), &inner_error); if (inner_error) { g_propagate_error (error, inner_error); return MM_BEARER_CONNECTION_STATUS_UNKNOWN; } return (MMBearerConnectionStatus)value; } static void cgact_periodic_query_ready (MMBaseModem *modem, GAsyncResult *res, GTask *task) { MMBroadbandBearer *self; const gchar *response; GError *error = NULL; GList *pdp_active_list = NULL; GList *l; MMBearerConnectionStatus status = MM_BEARER_CONNECTION_STATUS_UNKNOWN; self = MM_BROADBAND_BEARER (g_task_get_source_object (task)); response = mm_base_modem_at_command_finish (modem, res, &error); if (response) pdp_active_list = mm_3gpp_parse_cgact_read_response (response, &error); if (error) { g_assert (!pdp_active_list); g_prefix_error (&error, "Couldn't check current list of active PDP contexts: "); g_task_return_error (task, error); g_object_unref (task); return; } for (l = pdp_active_list; l; l = g_list_next (l)) { MM3gppPdpContextActive *pdp_active; /* Just assume the first active PDP context found is the one we're looking for. */ pdp_active = (MM3gppPdpContextActive *)(l->data); if (pdp_active->cid == (guint)self->priv->profile_id) { status = (pdp_active->active ? MM_BEARER_CONNECTION_STATUS_CONNECTED : MM_BEARER_CONNECTION_STATUS_DISCONNECTED); break; } } mm_3gpp_pdp_context_active_list_free (pdp_active_list); /* PDP context not found? This shouldn't happen, error out */ if (status == MM_BEARER_CONNECTION_STATUS_UNKNOWN) g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "PDP context not found in the known contexts list"); else g_task_return_int (task, (gssize) status); g_object_unref (task); } static void load_connection_status (MMBaseBearer *self, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; MMBaseModem *modem = NULL; MMPortSerialAt *port; task = g_task_new (self, NULL, callback, user_data); g_object_get (MM_BASE_BEARER (self), MM_BASE_BEARER_MODEM, &modem, NULL); /* No connection status checks on CDMA-only */ if (MM_BROADBAND_BEARER (self)->priv->connection_type == CONNECTION_TYPE_CDMA) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "Couldn't load connection status: unsupported in CDMA"); g_object_unref (task); goto out; } /* If CID not defined, error out */ if (MM_BROADBAND_BEARER (self)->priv->profile_id == MM_3GPP_PROFILE_ID_UNKNOWN) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't load connection status: cid not defined"); g_object_unref (task); goto out; } /* If no control port available, error out */ port = mm_base_modem_peek_best_at_port (modem, NULL); if (!port) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "Couldn't load connection status: no control port available"); g_object_unref (task); goto out; } mm_base_modem_at_command_full (MM_BASE_MODEM (modem), port, "+CGACT?", 3, FALSE, /* allow cached */ FALSE, /* raw */ NULL, /* cancellable */ (GAsyncReadyCallback) cgact_periodic_query_ready, task); out: g_clear_object (&modem); } /*****************************************************************************/ static void report_connection_status (MMBaseBearer *self, MMBearerConnectionStatus status, const GError *connection_error) { if (status == MM_BEARER_CONNECTION_STATUS_DISCONNECTED) /* Cleanup all connection related data */ reset_bearer_connection (MM_BROADBAND_BEARER (self)); /* Chain up parent's report_connection_status() */ MM_BASE_BEARER_CLASS (mm_broadband_bearer_parent_class)->report_connection_status ( self, status, connection_error); } /*****************************************************************************/ typedef struct _InitAsyncContext InitAsyncContext; static void interface_initialization_step (GTask *task); typedef enum { INITIALIZATION_STEP_FIRST, INITIALIZATION_STEP_CDMA_RM_PROTOCOL, INITIALIZATION_STEP_LAST } InitializationStep; struct _InitAsyncContext { MMBaseModem *modem; InitializationStep step; MMPortSerialAt *port; }; static void init_async_context_free (InitAsyncContext *ctx) { if (ctx->port) { mm_port_serial_close (MM_PORT_SERIAL (ctx->port)); g_object_unref (ctx->port); } g_object_unref (ctx->modem); g_free (ctx); } MMBaseBearer * mm_broadband_bearer_new_finish (GAsyncResult *res, GError **error) { GObject *bearer; GObject *source; source = g_async_result_get_source_object (res); bearer = g_async_initable_new_finish (G_ASYNC_INITABLE (source), res, error); g_object_unref (source); if (!bearer) return NULL; /* Only export valid bearers */ mm_base_bearer_export (MM_BASE_BEARER (bearer)); return MM_BASE_BEARER (bearer); } static gboolean initable_init_finish (GAsyncInitable *initable, GAsyncResult *result, GError **error) { return g_task_propagate_boolean (G_TASK (result), error); } static void crm_range_ready (MMBaseModem *modem, GAsyncResult *res, GTask *task) { MMBroadbandModem *self; InitAsyncContext *ctx; GError *error = NULL; const gchar *response; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); response = mm_base_modem_at_command_full_finish (modem, res, &error); if (error) { /* We should possibly take this error as fatal. If we were told to use a * specific Rm protocol, we must be able to check if it is supported. */ } else { MMModemCdmaRmProtocol min = MM_MODEM_CDMA_RM_PROTOCOL_UNKNOWN; MMModemCdmaRmProtocol max = MM_MODEM_CDMA_RM_PROTOCOL_UNKNOWN; if (mm_cdma_parse_crm_test_response (response, &min, &max, &error)) { MMModemCdmaRmProtocol current; current = mm_bearer_properties_get_rm_protocol (mm_base_bearer_peek_config (MM_BASE_BEARER (self))); /* Check if value within the range */ if (current >= min && current <= max) { /* Fine, go on with next step */ ctx->step++; interface_initialization_step (task); return; } g_assert (error == NULL); error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Requested RM protocol '%s' is not supported", mm_modem_cdma_rm_protocol_get_string (current)); } /* Failed, set as fatal as well */ } g_task_return_error (task, error); g_object_unref (task); } static void interface_initialization_step (GTask *task) { MMBroadbandModem *self; InitAsyncContext *ctx; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); switch (ctx->step) { case INITIALIZATION_STEP_FIRST: ctx->step++; /* Fall through */ case INITIALIZATION_STEP_CDMA_RM_PROTOCOL: /* If a specific RM protocol is given, we need to check whether it is * supported. */ if (mm_iface_modem_is_cdma (MM_IFACE_MODEM (ctx->modem)) && mm_bearer_properties_get_rm_protocol ( mm_base_bearer_peek_config (MM_BASE_BEARER (self))) != MM_MODEM_CDMA_RM_PROTOCOL_UNKNOWN) { mm_base_modem_at_command_full (ctx->modem, ctx->port, "+CRM=?", 3, TRUE, /* getting range, so reply can be cached */ FALSE, /* raw */ NULL, /* cancellable */ (GAsyncReadyCallback)crm_range_ready, task); return; } ctx->step++; /* Fall through */ case INITIALIZATION_STEP_LAST: /* We are done without errors! */ g_task_return_boolean (task, TRUE); g_object_unref (task); return; default: g_assert_not_reached (); } g_assert_not_reached (); } static void initable_init_async (GAsyncInitable *initable, int io_priority, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { InitAsyncContext *ctx; GTask *task; GError *error = NULL; ctx = g_new0 (InitAsyncContext, 1); g_object_get (initable, MM_BASE_BEARER_MODEM, &ctx->modem, NULL); task = g_task_new (MM_BROADBAND_BEARER (initable), cancellable, callback, user_data); g_task_set_task_data (task, ctx, (GDestroyNotify)init_async_context_free); ctx->port = mm_base_modem_get_port_primary (ctx->modem); if (!ctx->port) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't get primary port"); g_object_unref (task); return; } if (!mm_port_serial_open (MM_PORT_SERIAL (ctx->port), &error)) { g_clear_object (&ctx->port); g_task_return_error (task, error); g_object_unref (task); return; } interface_initialization_step (task); } void mm_broadband_bearer_new (MMBroadbandModem *modem, MMBearerProperties *bearer_properties, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { MMFlowControl flow_control; /* Inherit flow control from modem object directly */ g_object_get (modem, MM_BROADBAND_MODEM_FLOW_CONTROL, &flow_control, NULL); g_async_initable_new_async ( MM_TYPE_BROADBAND_BEARER, G_PRIORITY_DEFAULT, cancellable, callback, user_data, MM_BASE_BEARER_MODEM, modem, MM_BASE_BEARER_CONFIG, bearer_properties, MM_BROADBAND_BEARER_FLOW_CONTROL, flow_control, NULL); } static void set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { MMBroadbandBearer *self = MM_BROADBAND_BEARER (object); switch (prop_id) { case PROP_FLOW_CONTROL: self->priv->flow_control = g_value_get_flags (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) { MMBroadbandBearer *self = MM_BROADBAND_BEARER (object); switch (prop_id) { case PROP_FLOW_CONTROL: g_value_set_flags (value, self->priv->flow_control); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void mm_broadband_bearer_init (MMBroadbandBearer *self) { /* Initialize private data */ self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, MM_TYPE_BROADBAND_BEARER, MMBroadbandBearerPrivate); /* Set defaults */ self->priv->connection_type = CONNECTION_TYPE_NONE; self->priv->flow_control = MM_FLOW_CONTROL_NONE; } static void dispose (GObject *object) { MMBroadbandBearer *self = MM_BROADBAND_BEARER (object); reset_bearer_connection (self); G_OBJECT_CLASS (mm_broadband_bearer_parent_class)->dispose (object); } static void async_initable_iface_init (GAsyncInitableIface *iface) { iface->init_async = initable_init_async; iface->init_finish = initable_init_finish; } static void mm_broadband_bearer_class_init (MMBroadbandBearerClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); MMBaseBearerClass *base_bearer_class = MM_BASE_BEARER_CLASS (klass); g_type_class_add_private (object_class, sizeof (MMBroadbandBearerPrivate)); object_class->get_property = get_property; object_class->set_property = set_property; object_class->dispose = dispose; base_bearer_class->connect = connect; base_bearer_class->connect_finish = connect_finish; base_bearer_class->disconnect = disconnect; base_bearer_class->disconnect_finish = disconnect_finish; base_bearer_class->report_connection_status = report_connection_status; base_bearer_class->load_connection_status = load_connection_status; base_bearer_class->load_connection_status_finish = load_connection_status_finish; #if defined WITH_SUSPEND_RESUME base_bearer_class->reload_connection_status = load_connection_status; base_bearer_class->reload_connection_status_finish = load_connection_status_finish; #endif klass->connect_3gpp = connect_3gpp; klass->connect_3gpp_finish = detailed_connect_finish; klass->dial_3gpp = dial_3gpp; klass->dial_3gpp_finish = dial_3gpp_finish; klass->connect_cdma = connect_cdma; klass->connect_cdma_finish = detailed_connect_finish; klass->disconnect_3gpp = disconnect_3gpp; klass->disconnect_3gpp_finish = detailed_disconnect_finish; klass->disconnect_cdma = disconnect_cdma; klass->disconnect_cdma_finish = detailed_disconnect_finish; properties[PROP_FLOW_CONTROL] = g_param_spec_flags (MM_BROADBAND_BEARER_FLOW_CONTROL, "Flow control", "Flow control settings to use during connection", MM_TYPE_FLOW_CONTROL, MM_FLOW_CONTROL_NONE, G_PARAM_READWRITE); g_object_class_install_property (object_class, PROP_FLOW_CONTROL, properties[PROP_FLOW_CONTROL]); } ModemManager-1.23.4-dev/src/mm-broadband-bearer.h000066400000000000000000000161711456466623000214540ustar00rootroot00000000000000/* -*- 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: * * Author: Aleksander Morgado * * Copyright (C) 2011 - 2012 Google, Inc. * Copyright (C) 2011 - 2013 Aleksander Morgado */ #ifndef MM_BROADBAND_BEARER_H #define MM_BROADBAND_BEARER_H #include #include #define _LIBMM_INSIDE_MM #include #include "mm-modem-helpers.h" #include "mm-base-bearer.h" #include "mm-broadband-modem.h" #define MM_TYPE_BROADBAND_BEARER (mm_broadband_bearer_get_type ()) #define MM_BROADBAND_BEARER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_BROADBAND_BEARER, MMBroadbandBearer)) #define MM_BROADBAND_BEARER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_BROADBAND_BEARER, MMBroadbandBearerClass)) #define MM_IS_BROADBAND_BEARER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_BROADBAND_BEARER)) #define MM_IS_BROADBAND_BEARER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_BROADBAND_BEARER)) #define MM_BROADBAND_BEARER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_BROADBAND_BEARER, MMBroadbandBearerClass)) typedef struct _MMBroadbandBearer MMBroadbandBearer; typedef struct _MMBroadbandBearerClass MMBroadbandBearerClass; typedef struct _MMBroadbandBearerPrivate MMBroadbandBearerPrivate; #define MM_BROADBAND_BEARER_FLOW_CONTROL "broadband-bearer-flow-control" struct _MMBroadbandBearer { MMBaseBearer parent; MMBroadbandBearerPrivate *priv; }; struct _MMBroadbandBearerClass { MMBaseBearerClass parent; /* Full 3GPP connection sequence (cid selection, dial, get ip config) */ void (* connect_3gpp) (MMBroadbandBearer *self, MMBroadbandModem *modem, MMPortSerialAt *primary, MMPortSerialAt *secondary, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); MMBearerConnectResult * (* connect_3gpp_finish) (MMBroadbandBearer *self, GAsyncResult *res, GError **error); /* Dialing sub-part of 3GPP connection */ void (* dial_3gpp) (MMBroadbandBearer *self, MMBaseModem *modem, MMPortSerialAt *primary, guint cid, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); MMPort * (* dial_3gpp_finish) (MMBroadbandBearer *self, GAsyncResult *res, GError **error); /* IP config retrieval sub-part of 3GPP connection. * Only really required when using net port + static IP address. */ void (* get_ip_config_3gpp) (MMBroadbandBearer *self, MMBroadbandModem *modem, MMPortSerialAt *primary, MMPortSerialAt *secondary, MMPort *data, guint cid, MMBearerIpFamily ip_family, GAsyncReadyCallback callback, gpointer user_data); gboolean (* get_ip_config_3gpp_finish) (MMBroadbandBearer *self, GAsyncResult *res, MMBearerIpConfig **ipv4_config, MMBearerIpConfig **ipv6_config, GError **error); /* Full 3GPP disconnection sequence */ void (* disconnect_3gpp) (MMBroadbandBearer *self, MMBroadbandModem *modem, MMPortSerialAt *primary, MMPortSerialAt *secondary, MMPort *data, guint cid, GAsyncReadyCallback callback, gpointer user_data); gboolean (* disconnect_3gpp_finish) (MMBroadbandBearer *self, GAsyncResult *res, GError **error); /* Full CDMA connection sequence */ void (* connect_cdma) (MMBroadbandBearer *self, MMBroadbandModem *modem, MMPortSerialAt *primary, MMPortSerialAt *secondary, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); MMBearerConnectResult * (* connect_cdma_finish) (MMBroadbandBearer *self, GAsyncResult *res, GError **error); /* Full CDMA disconnection sequence */ void (* disconnect_cdma) (MMBroadbandBearer *self, MMBroadbandModem *modem, MMPortSerialAt *primary, MMPortSerialAt *secondary, MMPort *data, GAsyncReadyCallback callback, gpointer user_data); gboolean (* disconnect_cdma_finish) (MMBroadbandBearer *self, GAsyncResult *res, GError **error); }; GType mm_broadband_bearer_get_type (void); G_DEFINE_AUTOPTR_CLEANUP_FUNC (MMBroadbandBearer, g_object_unref) /* Default 3GPP bearer creation implementation */ void mm_broadband_bearer_new (MMBroadbandModem *modem, MMBearerProperties *properties, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); MMBaseBearer *mm_broadband_bearer_new_finish (GAsyncResult *res, GError **error); #endif /* MM_BROADBAND_BEARER_H */ ModemManager-1.23.4-dev/src/mm-broadband-modem-mbim.c000066400000000000000000014543041456466623000222370ustar00rootroot00000000000000/* -*- 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) 2013-2021 Aleksander Morgado */ #include #include #include #include #include #include #include #include "mm-modem-helpers-mbim.h" #include "mm-broadband-modem-mbim.h" #include "mm-bearer-mbim.h" #include "mm-sim-mbim.h" #include "mm-sms-mbim.h" #include "ModemManager.h" #include #include "mm-context.h" #include "mm-log-object.h" #include "mm-errors-types.h" #include "mm-error-helpers.h" #include "mm-modem-helpers.h" #include "mm-bearer-list.h" #include "mm-iface-modem.h" #include "mm-iface-modem-3gpp.h" #include "mm-iface-modem-3gpp-profile-manager.h" #include "mm-iface-modem-3gpp-ussd.h" #include "mm-iface-modem-location.h" #include "mm-iface-modem-messaging.h" #include "mm-iface-modem-signal.h" #include "mm-iface-modem-sar.h" #include "mm-sms-part-3gpp.h" #if defined WITH_QMI && QMI_MBIM_QMUX_SUPPORTED # include # include "mm-shared-qmi.h" #endif static void iface_modem_init (MMIfaceModem *iface); static void iface_modem_3gpp_init (MMIfaceModem3gpp *iface); static void iface_modem_3gpp_profile_manager_init (MMIfaceModem3gppProfileManager *iface); static void iface_modem_3gpp_ussd_init (MMIfaceModem3gppUssd *iface); static void iface_modem_location_init (MMIfaceModemLocation *iface); static void iface_modem_messaging_init (MMIfaceModemMessaging *iface); static void iface_modem_signal_init (MMIfaceModemSignal *iface); static void iface_modem_sar_init (MMIfaceModemSar *iface); #if defined WITH_QMI && QMI_MBIM_QMUX_SUPPORTED static void shared_qmi_init (MMSharedQmi *iface); #endif #if defined WITH_QMI && QMI_MBIM_QMUX_SUPPORTED static MMIfaceModemLocation *iface_modem_location_parent; #endif static MMIfaceModemSignal *iface_modem_signal_parent; static MMIfaceModem *iface_modem_parent; G_DEFINE_TYPE_EXTENDED (MMBroadbandModemMbim, mm_broadband_modem_mbim, MM_TYPE_BROADBAND_MODEM, 0, G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM, iface_modem_init) G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_3GPP, iface_modem_3gpp_init) G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_3GPP_PROFILE_MANAGER, iface_modem_3gpp_profile_manager_init) G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_3GPP_USSD, iface_modem_3gpp_ussd_init) G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_LOCATION, iface_modem_location_init) G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_MESSAGING, iface_modem_messaging_init) G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_SIGNAL, iface_modem_signal_init) G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_SAR, iface_modem_sar_init) #if defined WITH_QMI && QMI_MBIM_QMUX_SUPPORTED G_IMPLEMENT_INTERFACE (MM_TYPE_SHARED_QMI, shared_qmi_init) #endif ) typedef enum { PROCESS_NOTIFICATION_FLAG_NONE = 0, PROCESS_NOTIFICATION_FLAG_SIGNAL_QUALITY = 1 << 0, PROCESS_NOTIFICATION_FLAG_REGISTRATION_UPDATES = 1 << 1, PROCESS_NOTIFICATION_FLAG_SMS_READ = 1 << 2, PROCESS_NOTIFICATION_FLAG_CONNECT = 1 << 3, PROCESS_NOTIFICATION_FLAG_SUBSCRIBER_INFO = 1 << 4, PROCESS_NOTIFICATION_FLAG_PACKET_SERVICE = 1 << 5, PROCESS_NOTIFICATION_FLAG_PCO = 1 << 6, PROCESS_NOTIFICATION_FLAG_USSD = 1 << 7, PROCESS_NOTIFICATION_FLAG_LTE_ATTACH_INFO = 1 << 8, PROCESS_NOTIFICATION_FLAG_PROVISIONED_CONTEXTS = 1 << 9, PROCESS_NOTIFICATION_FLAG_SLOT_INFO_STATUS = 1 << 10, PROCESS_NOTIFICATION_FLAG_IP_CONFIGURATION = 1 << 11, } ProcessNotificationFlag; enum { PROP_0, #if defined WITH_QMI && QMI_MBIM_QMUX_SUPPORTED PROP_QMI_UNSUPPORTED, #endif PROP_INTEL_FIRMWARE_UPDATE_UNSUPPORTED, PROP_LAST }; /* Runtime cache while enabled */ typedef struct { gchar *current_operator_id; gchar *current_operator_name; GList *pco_list; MbimDataClass available_data_classes; MbimDataClass highest_available_data_class; MbimRegisterState reg_state; MbimPacketServiceState packet_service_state; guint64 packet_service_uplink_speed; guint64 packet_service_downlink_speed; MbimSubscriberReadyState last_ready_state; MbimPinType last_pin_type; } EnabledCache; struct _MMBroadbandModemMbimPrivate { /* Queried and cached capabilities */ MbimCellularClass caps_cellular_class; MbimDataClass caps_data_class; gchar *caps_custom_data_class; MbimSmsCaps caps_sms; guint caps_max_sessions; gchar *caps_device_id; gchar *caps_firmware_info; gchar *caps_hardware_info; /* Supported features */ gboolean is_profile_management_supported; gboolean is_profile_management_ext_supported; gboolean is_context_type_ext_supported; gboolean is_pco_supported; gboolean is_lte_attach_info_supported; gboolean is_nr5g_registration_settings_supported; gboolean is_base_stations_info_supported; gboolean is_ussd_supported; gboolean is_atds_location_supported; gboolean is_atds_signal_supported; gboolean is_intel_reset_supported; gboolean is_slot_info_status_supported; gboolean is_ms_sar_supported; gboolean is_google_carrier_lock_supported; /* Process unsolicited notifications */ gulong notification_id; ProcessNotificationFlag setup_flags; ProcessNotificationFlag enable_flags; /* State while enabled */ EnabledCache enabled_cache; /* 3GPP registration helpers */ gulong enabling_signal_id; gchar *requested_operator_id; MbimDataClass requested_data_class; /* 0 for defaults/auto */ /* Allowed modes helpers */ GTask *pending_allowed_modes_action; /* USSD helpers */ GTask *pending_ussd_action; /* SIM hot swap setup */ gboolean sim_hot_swap_configured; /* For notifying when the mbim-proxy connection is dead */ gulong mbim_device_removed_id; #if defined WITH_QMI && QMI_MBIM_QMUX_SUPPORTED gboolean qmi_unsupported; /* Flag when QMI-based capability/mode switching is in use */ gboolean qmi_capability_and_mode_switching; #endif gboolean intel_firmware_update_unsupported; /* Multi-SIM support */ guint32 executor_index; guint active_slot_index; gboolean pending_sim_slot_switch_action; MMUnlockRetries *unlock_retries; }; /*****************************************************************************/ static gboolean peek_device (gpointer self, MbimDevice **o_device, GAsyncReadyCallback callback, gpointer user_data) { MMPortMbim *port; port = mm_broadband_modem_mbim_peek_port_mbim (MM_BROADBAND_MODEM_MBIM (self)); if (!port) { g_task_report_new_error (self, callback, user_data, peek_device, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't peek MBIM port"); return FALSE; } *o_device = mm_port_mbim_peek_device (port); return TRUE; } static gboolean peek_device_in_task (gpointer self, MbimDevice **o_device, GTask *task) { MMPortMbim *port; port = mm_broadband_modem_mbim_peek_port_mbim (MM_BROADBAND_MODEM_MBIM (self)); if (!port) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't peek MBIM port"); g_object_unref (task); return FALSE; } *o_device = mm_port_mbim_peek_device (port); return TRUE; } #if defined WITH_QMI && QMI_MBIM_QMUX_SUPPORTED static QmiClient * shared_qmi_peek_client (MMSharedQmi *self, QmiService service, MMPortQmiFlag flag, GError **error) { MMPortMbim *port; QmiClient *client; g_assert (flag == MM_PORT_QMI_FLAG_DEFAULT); port = mm_broadband_modem_mbim_peek_port_mbim (MM_BROADBAND_MODEM_MBIM (self)); if (!port) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't peek MBIM port"); return NULL; } if (!mm_port_mbim_supports_qmi (port)) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "Unsupported"); return NULL; } client = mm_port_mbim_peek_qmi_client (port, service); if (!client) g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't peek client for service '%s'", qmi_service_get_string (service)); return client; } #endif /*****************************************************************************/ MMPortMbim * mm_broadband_modem_mbim_get_port_mbim (MMBroadbandModemMbim *self) { MMPortMbim *primary_mbim_port; g_assert (MM_IS_BROADBAND_MODEM_MBIM (self)); primary_mbim_port = mm_broadband_modem_mbim_peek_port_mbim (self); return (primary_mbim_port ? MM_PORT_MBIM (g_object_ref (primary_mbim_port)) : NULL); } MMPortMbim * mm_broadband_modem_mbim_peek_port_mbim (MMBroadbandModemMbim *self) { MMPortMbim *primary_mbim_port = NULL; GList *mbim_ports; g_assert (MM_IS_BROADBAND_MODEM_MBIM (self)); mbim_ports = mm_base_modem_find_ports (MM_BASE_MODEM (self), MM_PORT_SUBSYS_UNKNOWN, MM_PORT_TYPE_MBIM); /* First MBIM port in the list is the primary one always */ if (mbim_ports) primary_mbim_port = MM_PORT_MBIM (mbim_ports->data); g_list_free_full (mbim_ports, g_object_unref); return primary_mbim_port; } MMPortMbim * mm_broadband_modem_mbim_get_port_mbim_for_data (MMBroadbandModemMbim *self, MMPort *data, GError **error) { MMPortMbim *mbim_port; g_assert (MM_IS_BROADBAND_MODEM_MBIM (self)); mbim_port = mm_broadband_modem_mbim_peek_port_mbim_for_data (self, data, error); return (mbim_port ? MM_PORT_MBIM (g_object_ref (mbim_port)) : NULL); } static MMPortMbim * peek_port_mbim_for_data (MMBroadbandModemMbim *self, MMPort *data, GError **error) { GList *cdc_wdm_mbim_ports; GList *l; const gchar *net_port_parent_path; MMPortMbim *found = NULL; const gchar *net_port_driver; g_assert (MM_IS_BROADBAND_MODEM_MBIM (self)); g_assert (mm_port_get_subsys (data) == MM_PORT_SUBSYS_NET); net_port_driver = mm_kernel_device_get_driver (mm_port_peek_kernel_device (data)); if (g_strcmp0 (net_port_driver, "cdc_mbim") != 0 && g_strcmp0 (net_port_driver, "mhi_net")) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Unsupported MBIM kernel driver for 'net/%s': %s", mm_port_get_device (data), net_port_driver); return NULL; } if (!g_strcmp0 (net_port_driver, "mhi_net")) return mm_broadband_modem_mbim_peek_port_mbim (self); net_port_parent_path = mm_kernel_device_get_interface_sysfs_path (mm_port_peek_kernel_device (data)); if (!net_port_parent_path) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "No parent path for 'net/%s'", mm_port_get_device (data)); return NULL; } /* Find the CDC-WDM port on the same USB interface as the given net port */ cdc_wdm_mbim_ports = mm_base_modem_find_ports (MM_BASE_MODEM (self), MM_PORT_SUBSYS_USBMISC, MM_PORT_TYPE_MBIM); for (l = cdc_wdm_mbim_ports; l && !found; l = g_list_next (l)) { const gchar *wdm_port_parent_path; g_assert (MM_IS_PORT_MBIM (l->data)); wdm_port_parent_path = mm_kernel_device_get_interface_sysfs_path (mm_port_peek_kernel_device (MM_PORT (l->data))); if (wdm_port_parent_path && g_str_equal (wdm_port_parent_path, net_port_parent_path)) found = MM_PORT_MBIM (l->data); } g_list_free_full (cdc_wdm_mbim_ports, g_object_unref); if (!found) g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_NOT_FOUND, "Couldn't find associated MBIM port for 'net/%s'", mm_port_get_device (data)); return found; } MMPortMbim * mm_broadband_modem_mbim_peek_port_mbim_for_data (MMBroadbandModemMbim *self, MMPort *data, GError **error) { g_assert (MM_BROADBAND_MODEM_MBIM_GET_CLASS (self)->peek_port_mbim_for_data); return MM_BROADBAND_MODEM_MBIM_GET_CLASS (self)->peek_port_mbim_for_data (self, data, error); } /*****************************************************************************/ guint32 mm_broadband_modem_mbim_normalize_nw_error (MMBroadbandModemMbim *self, guint32 nw_error) { if (MM_BROADBAND_MODEM_MBIM_GET_CLASS (self)->normalize_nw_error) return MM_BROADBAND_MODEM_MBIM_GET_CLASS (self)->normalize_nw_error (self, nw_error); return nw_error; } /*****************************************************************************/ gboolean mm_broadband_modem_mbim_is_context_type_ext_supported (MMBroadbandModemMbim *self) { return self->priv->is_context_type_ext_supported; } /*****************************************************************************/ /* Current capabilities (Modem interface) */ typedef struct { #if defined WITH_QMI && QMI_MBIM_QMUX_SUPPORTED MMModemCapability current_qmi; #endif MbimDevice *device; MMModemCapability current_mbim; } LoadCurrentCapabilitiesContext; static void load_current_capabilities_context_free (LoadCurrentCapabilitiesContext *ctx) { g_object_unref (ctx->device); g_free (ctx); } static MMModemCapability modem_load_current_capabilities_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { GError *inner_error = NULL; gssize value; value = g_task_propagate_int (G_TASK (res), &inner_error); if (inner_error) { g_propagate_error (error, inner_error); return MM_MODEM_CAPABILITY_NONE; } return (MMModemCapability)value; } static void complete_current_capabilities (GTask *task) { LoadCurrentCapabilitiesContext *ctx; MMModemCapability result = 0; ctx = g_task_get_task_data (task); #if defined WITH_QMI && QMI_MBIM_QMUX_SUPPORTED { MMBroadbandModemMbim *self; self = g_task_get_source_object (task); /* Warn if the MBIM loaded capabilities isn't a subset of the QMI loaded ones */ if (ctx->current_qmi && ctx->current_mbim) { gchar *mbim_caps_str; gchar *qmi_caps_str; mbim_caps_str = mm_common_build_capabilities_string ((const MMModemCapability *)&(ctx->current_mbim), 1); qmi_caps_str = mm_common_build_capabilities_string ((const MMModemCapability *)&(ctx->current_qmi), 1); if ((ctx->current_mbim & ctx->current_qmi) != ctx->current_mbim) mm_obj_warn (self, "MBIM reported current capabilities (%s) not found in QMI-over-MBIM reported ones (%s)", mbim_caps_str, qmi_caps_str); else mm_obj_dbg (self, "MBIM reported current capabilities (%s) is a subset of the QMI-over-MBIM reported ones (%s)", mbim_caps_str, qmi_caps_str); g_free (mbim_caps_str); g_free (qmi_caps_str); result = ctx->current_qmi; self->priv->qmi_capability_and_mode_switching = TRUE; } else if (ctx->current_qmi) { result = ctx->current_qmi; self->priv->qmi_capability_and_mode_switching = TRUE; } else result = ctx->current_mbim; /* If current capabilities loading is done via QMI, we can safely assume that all the other * capability and mode related operations are going to be done via QMI as well, so that we * don't mix both logics */ if (self->priv->qmi_capability_and_mode_switching) mm_obj_dbg (self, "QMI-based capability and mode switching support enabled"); } #else result = ctx->current_mbim; #endif g_task_return_int (task, (gint) result); g_object_unref (task); } static void device_caps_query_ready (MbimDevice *device, GAsyncResult *res, GTask *task) { g_autoptr(MbimMessage) response = NULL; MMBroadbandModemMbim *self; GError *error = NULL; LoadCurrentCapabilitiesContext *ctx; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); response = mbim_device_command_finish (device, res, &error); if (!response || !mbim_message_response_get_result (response, MBIM_MESSAGE_TYPE_COMMAND_DONE, &error)) { g_task_return_error (task, error); g_object_unref (task); return; } if (mbim_device_check_ms_mbimex_version (device, 3, 0)) { MbimDataClassV3 data_class_v3; MbimDataSubclass data_subclass; if (!mbim_message_ms_basic_connect_extensions_v3_device_caps_response_parse ( response, NULL, /* device_type */ &self->priv->caps_cellular_class, NULL, /* voice_class */ NULL, /* sim_class */ &data_class_v3, &self->priv->caps_sms, NULL, /* ctrl_caps */ &data_subclass, &self->priv->caps_max_sessions, NULL, /* executor_index */ NULL, /* wcdma_band_class */ NULL, /* lte_band_class_array_size */ NULL, /* lte_band_class_array */ NULL, /* nr_band_class_array_size */ NULL, /* nr_band_class_array */ &self->priv->caps_custom_data_class, &self->priv->caps_device_id, &self->priv->caps_firmware_info, &self->priv->caps_hardware_info, &error)) { g_task_return_error (task, error); g_object_unref (task); return; } /* Translate data class v3 to standard data class to simplify further usage of the field */ self->priv->caps_data_class = mm_mbim_data_class_from_mbim_data_class_v3_and_subclass (data_class_v3, data_subclass); } else if (mbim_device_check_ms_mbimex_version (device, 2, 0)) { if (!mbim_message_ms_basic_connect_extensions_device_caps_response_parse ( response, NULL, /* device_type */ &self->priv->caps_cellular_class, NULL, /* voice_class */ NULL, /* sim_class */ &self->priv->caps_data_class, &self->priv->caps_sms, NULL, /* ctrl_caps */ &self->priv->caps_max_sessions, &self->priv->caps_custom_data_class, &self->priv->caps_device_id, &self->priv->caps_firmware_info, &self->priv->caps_hardware_info, NULL, /* executor_index */ &error)) { g_task_return_error (task, error); g_object_unref (task); return; } } else { if (!mbim_message_device_caps_response_parse ( response, NULL, /* device_type */ &self->priv->caps_cellular_class, NULL, /* voice_class */ NULL, /* sim_class */ &self->priv->caps_data_class, &self->priv->caps_sms, NULL, /* ctrl_caps */ &self->priv->caps_max_sessions, &self->priv->caps_custom_data_class, &self->priv->caps_device_id, &self->priv->caps_firmware_info, &self->priv->caps_hardware_info, &error)) { g_task_return_error (task, error); g_object_unref (task); return; } } ctx->current_mbim = mm_modem_capability_from_mbim_device_caps (self->priv->caps_cellular_class, self->priv->caps_data_class, self->priv->caps_custom_data_class); complete_current_capabilities (task); } static void load_current_capabilities_mbim (GTask *task) { g_autoptr(MbimMessage) message = NULL; MMBroadbandModemMbim *self; LoadCurrentCapabilitiesContext *ctx; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); mm_obj_dbg (self, "loading current capabilities..."); if (mbim_device_check_ms_mbimex_version (ctx->device, 2, 0)) message = mbim_message_ms_basic_connect_extensions_device_caps_query_new (NULL); else message = mbim_message_device_caps_query_new (NULL); mbim_device_command (ctx->device, message, 10, NULL, (GAsyncReadyCallback)device_caps_query_ready, task); } #if defined WITH_QMI && QMI_MBIM_QMUX_SUPPORTED static void qmi_load_current_capabilities_ready (MMIfaceModem *self, GAsyncResult *res, GTask *task) { LoadCurrentCapabilitiesContext *ctx; GError *error = NULL; ctx = g_task_get_task_data (task); ctx->current_qmi = mm_shared_qmi_load_current_capabilities_finish (self, res, &error); if (error) { mm_obj_dbg (self, "couldn't load currrent capabilities using QMI over MBIM: %s", error->message); g_clear_error (&error); } load_current_capabilities_mbim (task); } #endif static void modem_load_current_capabilities (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data) { MbimDevice *device; GTask *task; LoadCurrentCapabilitiesContext *ctx; if (!peek_device (self, &device, callback, user_data)) return; task = g_task_new (self, NULL, callback, user_data); ctx = g_new0 (LoadCurrentCapabilitiesContext, 1); ctx->device = g_object_ref (device); g_task_set_task_data (task, ctx, (GDestroyNotify) load_current_capabilities_context_free); #if defined WITH_QMI && QMI_MBIM_QMUX_SUPPORTED mm_shared_qmi_load_current_capabilities (self, (GAsyncReadyCallback)qmi_load_current_capabilities_ready, task); #else load_current_capabilities_mbim (task); #endif } /*****************************************************************************/ /* Supported Capabilities loading (Modem interface) */ static GArray * modem_load_supported_capabilities_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { #if defined WITH_QMI && QMI_MBIM_QMUX_SUPPORTED if (MM_BROADBAND_MODEM_MBIM (self)->priv->qmi_capability_and_mode_switching) return mm_shared_qmi_load_supported_capabilities_finish (self, res, error); #endif return g_task_propagate_pointer (G_TASK (res), error); } static void load_supported_capabilities_mbim (GTask *task) { MMBroadbandModemMbim *self; MMModemCapability current; GArray *supported = NULL; self = g_task_get_source_object (task); /* Current capabilities should have been cached already, just assume them */ current = mm_modem_capability_from_mbim_device_caps (self->priv->caps_cellular_class, self->priv->caps_data_class, self->priv->caps_custom_data_class); if (current != 0) { supported = g_array_sized_new (FALSE, FALSE, sizeof (MMModemCapability), 1); g_array_append_val (supported, current); } if (!supported) g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't load supported capabilities: no previously catched current capabilities"); else g_task_return_pointer (task, supported, (GDestroyNotify) g_array_unref); g_object_unref (task); } static void modem_load_supported_capabilities (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; #if defined WITH_QMI && QMI_MBIM_QMUX_SUPPORTED if (MM_BROADBAND_MODEM_MBIM (self)->priv->qmi_capability_and_mode_switching) { mm_shared_qmi_load_supported_capabilities (self, callback, user_data); return; } #endif task = g_task_new (self, NULL, callback, user_data); load_supported_capabilities_mbim (task); } /*****************************************************************************/ /* Capabilities switching (Modem interface) */ static gboolean modem_set_current_capabilities_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { #if defined WITH_QMI && QMI_MBIM_QMUX_SUPPORTED if (MM_BROADBAND_MODEM_MBIM (self)->priv->qmi_capability_and_mode_switching) return mm_shared_qmi_set_current_capabilities_finish (self, res, error); #endif g_assert (error); return g_task_propagate_boolean (G_TASK (res), error); } static void modem_set_current_capabilities (MMIfaceModem *self, MMModemCapability capabilities, GAsyncReadyCallback callback, gpointer user_data) { #if defined WITH_QMI && QMI_MBIM_QMUX_SUPPORTED if (MM_BROADBAND_MODEM_MBIM (self)->priv->qmi_capability_and_mode_switching) { mm_shared_qmi_set_current_capabilities (self, capabilities, callback, user_data); return; } #endif g_task_report_new_error (self, callback, user_data, modem_set_current_capabilities, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "Capability switching is not supported"); } /*****************************************************************************/ /* Manufacturer loading (Modem interface) */ static gchar * modem_load_manufacturer_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { return g_task_propagate_pointer (G_TASK (res), error); } static void modem_load_manufacturer (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; gchar *manufacturer = NULL; MMPortMbim *port; port = mm_broadband_modem_mbim_peek_port_mbim (MM_BROADBAND_MODEM_MBIM (self)); if (port) { manufacturer = g_strdup (mm_kernel_device_get_physdev_manufacturer ( mm_port_peek_kernel_device (MM_PORT (port)))); } if (!manufacturer) manufacturer = g_strdup (mm_base_modem_get_plugin (MM_BASE_MODEM (self))); task = g_task_new (self, NULL, callback, user_data); g_task_return_pointer (task, manufacturer, g_free); g_object_unref (task); } /*****************************************************************************/ /* Model loading (Modem interface) */ static gchar * modem_load_model_default (MMIfaceModem *self) { return g_strdup_printf ("MBIM [%04X:%04X]", (mm_base_modem_get_vendor_id (MM_BASE_MODEM (self)) & 0xFFFF), (mm_base_modem_get_product_id (MM_BASE_MODEM (self)) & 0xFFFF)); } static gchar * modem_load_model_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { return g_task_propagate_pointer (G_TASK (res), error); } #if defined WITH_QMI && QMI_MBIM_QMUX_SUPPORTED static void qmi_load_model_ready (MMIfaceModem *self, GAsyncResult *res, GTask *task) { gchar *model = NULL; GError *error = NULL; model = mm_shared_qmi_load_model_finish (self, res, &error); if (!model) { mm_obj_dbg (self, "couldn't load model using QMI over MBIM: %s", error->message); model = modem_load_model_default (self); g_clear_error (&error); } g_task_return_pointer (task, model, g_free); g_object_unref (task); } #endif static void at_load_model_ready (MMIfaceModem *self, GAsyncResult *res, GTask *task) { gchar *model = NULL; GError *error = NULL; model = iface_modem_parent->load_model_finish (self, res, &error); if (!model) { mm_obj_dbg (self, "couldn't load model using AT: %s", error->message); model = modem_load_model_default (self); g_clear_error (&error); } g_task_return_pointer (task, model, g_free); g_object_unref (task); } static void modem_load_model (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data) { gchar *model = NULL; GTask *task; MMPortMbim *port; task = g_task_new (self, NULL, callback, user_data); port = mm_broadband_modem_mbim_peek_port_mbim (MM_BROADBAND_MODEM_MBIM (self)); if (port) { model = g_strdup (mm_kernel_device_get_physdev_product ( mm_port_peek_kernel_device (MM_PORT (port)))); if (model) { g_task_return_pointer (task, model, g_free); g_object_unref (task); return; } } #if defined WITH_QMI && QMI_MBIM_QMUX_SUPPORTED if (!model) { mm_shared_qmi_load_model (self, (GAsyncReadyCallback)qmi_load_model_ready, task); return; } #endif iface_modem_parent->load_model (self, (GAsyncReadyCallback)at_load_model_ready, task); } /*****************************************************************************/ /* Revision loading (Modem interface) */ static gchar * modem_load_revision_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { return g_task_propagate_pointer (G_TASK (res), error); } static void modem_load_revision (MMIfaceModem *_self, GAsyncReadyCallback callback, gpointer user_data) { MMBroadbandModemMbim *self = MM_BROADBAND_MODEM_MBIM (_self); GTask *task; task = g_task_new (self, NULL, callback, user_data); if (self->priv->caps_firmware_info) g_task_return_pointer (task, g_strdup (self->priv->caps_firmware_info), g_free); else g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Firmware revision information not given in device capabilities"); g_object_unref (task); } /*****************************************************************************/ /* Hardware Revision loading (Modem interface) */ static gchar * modem_load_hardware_revision_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { return g_task_propagate_pointer (G_TASK (res), error); } static void modem_load_hardware_revision (MMIfaceModem *_self, GAsyncReadyCallback callback, gpointer user_data) { MMBroadbandModemMbim *self = MM_BROADBAND_MODEM_MBIM (_self); GTask *task; task = g_task_new (self, NULL, callback, user_data); if (self->priv->caps_hardware_info) g_task_return_pointer (task, g_strdup (self->priv->caps_hardware_info), g_free); else g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Hardware revision information not given in device capabilities"); g_object_unref (task); } /*****************************************************************************/ /* Equipment Identifier loading (Modem interface) */ static gchar * modem_load_equipment_identifier_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { return g_task_propagate_pointer (G_TASK (res), error); } static void modem_load_equipment_identifier (MMIfaceModem *_self, GAsyncReadyCallback callback, gpointer user_data) { MMBroadbandModemMbim *self = MM_BROADBAND_MODEM_MBIM (_self); GTask *task; task = g_task_new (self, NULL, callback, user_data); if (self->priv->caps_device_id) g_task_return_pointer (task, g_strdup (self->priv->caps_device_id), g_free); else g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Device ID not given in device capabilities"); g_object_unref (task); } /*****************************************************************************/ /* Device identifier loading (Modem interface) */ static gchar * modem_load_device_identifier_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { return g_task_propagate_pointer (G_TASK (res), error); } static void modem_load_device_identifier (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data) { gchar *device_identifier; GTask *task; GError *error = NULL; task = g_task_new (self, NULL, callback, user_data); /* Just use placeholder ATI/ATI1 replies, all the other internal info should be * enough for uniqueness */ device_identifier = mm_broadband_modem_create_device_identifier (MM_BROADBAND_MODEM (self), "", "", &error); if (!device_identifier) g_task_return_error (task, error); else g_task_return_pointer (task, device_identifier, g_free); g_object_unref (task); } /*****************************************************************************/ /* Supported modes loading (Modem interface) */ static GArray * modem_load_supported_modes_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { return g_task_propagate_pointer (G_TASK (res), error); } static void load_supported_modes_mbim (GTask *task, MbimDevice *device) { MMBroadbandModemMbim *self; GArray *all; GArray *filtered; MMModemMode mask_all; MMModemModeCombination mode = { .allowed = MM_MODEM_MODE_NONE, .preferred = MM_MODEM_MODE_NONE, }; self = g_task_get_source_object (task); if (self->priv->caps_data_class == 0) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Data class not given in device capabilities"); g_object_unref (task); return; } /* Build all */ mask_all = mm_modem_mode_from_mbim_data_class (self->priv->caps_data_class, self->priv->caps_custom_data_class); mode.allowed = mask_all; mode.preferred = MM_MODEM_MODE_NONE; all = g_array_sized_new (FALSE, FALSE, sizeof (MMModemModeCombination), 1); g_array_append_val (all, mode); /* When using MBIMEx we can enable the mode switching operation because * we'll be able to know if the modes requested are the ones configured * as preferred after the operation. */ if (mbim_device_check_ms_mbimex_version (device, 2, 0)) { GArray *combinations; combinations = g_array_new (FALSE, FALSE, sizeof (MMModemModeCombination)); #define ADD_MODE_PREFERENCE(MODE_MASK) do { \ mode.allowed = MODE_MASK; \ mode.preferred = MM_MODEM_MODE_NONE; \ g_array_append_val (combinations, mode); \ } while (0) /* 2G, 3G */ ADD_MODE_PREFERENCE (MM_MODEM_MODE_2G); ADD_MODE_PREFERENCE (MM_MODEM_MODE_3G); ADD_MODE_PREFERENCE (MM_MODEM_MODE_2G | MM_MODEM_MODE_3G); /* +4G */ ADD_MODE_PREFERENCE (MM_MODEM_MODE_4G); ADD_MODE_PREFERENCE (MM_MODEM_MODE_2G | MM_MODEM_MODE_4G); ADD_MODE_PREFERENCE (MM_MODEM_MODE_3G | MM_MODEM_MODE_4G); ADD_MODE_PREFERENCE (MM_MODEM_MODE_2G | MM_MODEM_MODE_3G | MM_MODEM_MODE_4G); /* +5G */ ADD_MODE_PREFERENCE (MM_MODEM_MODE_5G); ADD_MODE_PREFERENCE (MM_MODEM_MODE_2G | MM_MODEM_MODE_5G); ADD_MODE_PREFERENCE (MM_MODEM_MODE_3G | MM_MODEM_MODE_5G); ADD_MODE_PREFERENCE (MM_MODEM_MODE_4G | MM_MODEM_MODE_5G); ADD_MODE_PREFERENCE (MM_MODEM_MODE_2G | MM_MODEM_MODE_3G | MM_MODEM_MODE_5G); ADD_MODE_PREFERENCE (MM_MODEM_MODE_2G | MM_MODEM_MODE_4G | MM_MODEM_MODE_5G); ADD_MODE_PREFERENCE (MM_MODEM_MODE_3G | MM_MODEM_MODE_4G | MM_MODEM_MODE_5G); ADD_MODE_PREFERENCE (MM_MODEM_MODE_2G | MM_MODEM_MODE_3G | MM_MODEM_MODE_4G | MM_MODEM_MODE_5G); filtered = mm_filter_supported_modes (all, combinations, self); g_array_unref (combinations); g_array_unref (all); #undef ADD_MODE_PREFERENCE } else filtered = all; g_task_return_pointer (task, filtered, (GDestroyNotify)g_array_unref); g_object_unref (task); } #if defined WITH_QMI && QMI_MBIM_QMUX_SUPPORTED static void shared_qmi_load_supported_modes_ready (MMIfaceModem *self, GAsyncResult *res, GTask *task) { GArray *combinations; GError *error = NULL; combinations = mm_shared_qmi_load_supported_modes_finish (self, res, &error); if (!combinations) g_task_return_error (task, error); else g_task_return_pointer (task, combinations, (GDestroyNotify)g_array_unref); g_object_unref (task); } #endif static void modem_load_supported_modes (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; MbimDevice *device; if (!peek_device (self, &device, callback, user_data)) return; task = g_task_new (self, NULL, callback, user_data); #if defined WITH_QMI && QMI_MBIM_QMUX_SUPPORTED if (MM_BROADBAND_MODEM_MBIM (self)->priv->qmi_capability_and_mode_switching) { mm_shared_qmi_load_supported_modes (self, (GAsyncReadyCallback)shared_qmi_load_supported_modes_ready, task); return; } #endif load_supported_modes_mbim (task, device); } /*****************************************************************************/ /* Current modes loading (Modem interface) */ static gboolean modem_load_current_modes_finish (MMIfaceModem *self, GAsyncResult *res, MMModemMode *allowed, MMModemMode *preferred, GError **error) { g_autofree MMModemModeCombination *mode = NULL; mode = g_task_propagate_pointer (G_TASK (res), error); if (!mode) return FALSE; *allowed = mode->allowed; *preferred = mode->preferred; return TRUE; } static void register_state_current_modes_query_ready (MbimDevice *device, GAsyncResult *res, GTask *task) { g_autoptr(MbimMessage) response = NULL; MMModemModeCombination *mode = NULL; GError *error = NULL; MbimDataClass preferred_data_classes; response = mbim_device_command_finish (device, res, &error); if (!response || !mbim_message_response_get_result (response, MBIM_MESSAGE_TYPE_COMMAND_DONE, &error) || !mbim_message_ms_basic_connect_v2_register_state_response_parse ( response, NULL, /* nw_error */ NULL, /* register_state */ NULL, /* register_mode */ NULL, /* available_data_classes */ NULL, /* current_cellular_class */ NULL, /* provider_id */ NULL, /* provider_name */ NULL, /* roaming_text */ NULL, /* registration_flag */ &preferred_data_classes, &error)) { g_task_return_error (task, error); g_object_unref (task); return; } mode = g_new0 (MMModemModeCombination, 1); mode->allowed = mm_modem_mode_from_mbim_data_class (preferred_data_classes, NULL); mode->preferred = MM_MODEM_MODE_NONE; g_task_return_pointer (task, mode, (GDestroyNotify)g_free); g_object_unref (task); } #if defined WITH_QMI && QMI_MBIM_QMUX_SUPPORTED static void shared_qmi_load_current_modes_ready (MMIfaceModem *self, GAsyncResult *res, GTask *task) { g_autofree MMModemModeCombination *mode = NULL; GError *error = NULL; mode = g_new0 (MMModemModeCombination, 1); if (!mm_shared_qmi_load_current_modes_finish (self, res, &mode->allowed, &mode->preferred, &error)) g_task_return_error (task, error); else g_task_return_pointer (task, g_steal_pointer (&mode), (GDestroyNotify)g_free); g_object_unref (task); } #endif static void modem_load_current_modes (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; MbimDevice *device; if (!peek_device (self, &device, callback, user_data)) return; task = g_task_new (self, NULL, callback, user_data); #if defined WITH_QMI && QMI_MBIM_QMUX_SUPPORTED if (MM_BROADBAND_MODEM_MBIM (self)->priv->qmi_capability_and_mode_switching) { mm_shared_qmi_load_current_modes (self, (GAsyncReadyCallback)shared_qmi_load_current_modes_ready, task); return; } #endif if (mbim_device_check_ms_mbimex_version (device, 2, 0)) { g_autoptr(MbimMessage) message = NULL; message = mbim_message_register_state_query_new (NULL); mbim_device_command (device, message, 60, NULL, (GAsyncReadyCallback)register_state_current_modes_query_ready, task); return; } g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "Current mode loading is not supported"); g_object_unref (task); } /*****************************************************************************/ /* Current modes switching (Modem interface) */ static gboolean modem_set_current_modes_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void complete_pending_allowed_modes_action (MMBroadbandModemMbim *self, MbimDataClass preferred_data_classes) { MbimDataClass requested_data_classes; MMModemMode preferred_modes; MMModemMode requested_modes; if (!self->priv->pending_allowed_modes_action) return; requested_data_classes = (MbimDataClass) GPOINTER_TO_UINT (g_task_get_task_data (self->priv->pending_allowed_modes_action)); requested_modes = mm_modem_mode_from_mbim_data_class (requested_data_classes, NULL); preferred_modes = mm_modem_mode_from_mbim_data_class (preferred_data_classes, NULL); /* only early complete on success, as we don't know if they're going to be * intermediate indications emitted before the preference change is valid */ if (requested_modes == preferred_modes) { GTask *task; task = g_steal_pointer (&self->priv->pending_allowed_modes_action); g_task_return_boolean (task, TRUE); g_object_unref (task); } } static void register_state_current_modes_set_ready (MbimDevice *device, GAsyncResult *res, GTask *task) { g_autoptr(MbimMessage) response = NULL; MMBroadbandModemMbim *self; GError *error = NULL; MbimDataClass preferred_data_classes; MMModemMode preferred_modes; MbimDataClass requested_data_classes; MMModemMode requested_modes; self = g_task_get_source_object (task); requested_data_classes = (MbimDataClass) GPOINTER_TO_UINT (g_task_get_task_data (task)); /* If the task is still in the private info, it means it wasn't either * cancelled or completed, so we just unref that reference and go on * with out response processing. But if the task is no longer in the * private info (or if there's a different task), then it means we're * either cancelled (by some new incoming user request) or otherwise * successfully completed (if completed via a Register State indication). * In both those cases, just unref the incoming task and go on. */ if (self->priv->pending_allowed_modes_action != task) { g_assert (g_task_get_completed (task)); g_object_unref (task); return; } g_clear_object (&self->priv->pending_allowed_modes_action); response = mbim_device_command_finish (device, res, &error); if (!response || !mbim_message_response_get_result (response, MBIM_MESSAGE_TYPE_COMMAND_DONE, &error) || !mbim_message_ms_basic_connect_v2_register_state_response_parse ( response, NULL, /* nw_error */ NULL, /* register_state */ NULL, /* register_mode */ NULL, /* available_data_classes */ NULL, /* current_cellular_class */ NULL, /* provider_id */ NULL, /* provider_name */ NULL, /* roaming_text */ NULL, /* registration_flag */ &preferred_data_classes, &error)) { g_task_return_error (task, error); g_object_unref (task); return; } requested_modes = mm_modem_mode_from_mbim_data_class (requested_data_classes, NULL); preferred_modes = mm_modem_mode_from_mbim_data_class (preferred_data_classes, NULL); if (requested_modes != preferred_modes) { g_autofree gchar *requested_modes_str = NULL; g_autofree gchar *preferred_modes_str = NULL; g_autofree gchar *requested_data_classes_str = NULL; g_autofree gchar *preferred_data_classes_str = NULL; requested_modes_str = mm_modem_mode_build_string_from_mask (requested_modes); preferred_modes_str = mm_modem_mode_build_string_from_mask (preferred_modes); requested_data_classes_str = mbim_data_class_build_string_from_mask (requested_data_classes); preferred_data_classes_str = mbim_data_class_build_string_from_mask (preferred_data_classes); g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Current mode update failed: requested %s (%s) but reported preferred is %s (%s)", requested_modes_str, requested_data_classes_str, preferred_modes_str, preferred_data_classes_str); g_object_unref (task); return; } g_task_return_boolean (task, TRUE); g_object_unref (task); } #if defined WITH_QMI && QMI_MBIM_QMUX_SUPPORTED static void shared_qmi_set_current_modes_ready (MMIfaceModem *self, GAsyncResult *res, GTask *task) { GError *error = NULL; if (!mm_shared_qmi_set_current_modes_finish (self, res, &error)) g_task_return_error (task, error); else g_task_return_boolean (task, TRUE); g_object_unref (task); } #endif static void modem_set_current_modes (MMIfaceModem *_self, MMModemMode allowed, MMModemMode preferred, GAsyncReadyCallback callback, gpointer user_data) { MMBroadbandModemMbim *self = MM_BROADBAND_MODEM_MBIM (_self); GTask *task; MbimDevice *device; g_autoptr(GCancellable) cancellable = NULL; if (!peek_device (self, &device, callback, user_data)) return; cancellable = g_cancellable_new (); task = g_task_new (self, cancellable, callback, user_data); #if defined WITH_QMI && QMI_MBIM_QMUX_SUPPORTED if (self->priv->qmi_capability_and_mode_switching) { mm_shared_qmi_set_current_modes (MM_IFACE_MODEM (self), allowed, preferred, (GAsyncReadyCallback)shared_qmi_set_current_modes_ready, task); return; } #endif if (mbim_device_check_ms_mbimex_version (device, 2, 0)) { g_autoptr(MbimMessage) message = NULL; /* Limit ANY to the currently supported modes */ if (allowed == MM_MODEM_MODE_ANY) allowed = mm_modem_mode_from_mbim_data_class (self->priv->caps_data_class, self->priv->caps_custom_data_class); self->priv->requested_data_class = mm_mbim_data_class_from_modem_mode (allowed, mm_iface_modem_is_3gpp (_self), mm_iface_modem_is_cdma (_self)); /* Store the ongoing allowed modes action, so that we can finish the * operation early via indications, instead of waiting for the modem * to be registered on the requested access technologies */ g_task_set_task_data (task, GUINT_TO_POINTER (self->priv->requested_data_class), NULL); if (self->priv->pending_allowed_modes_action) { /* cancel the task and clear this reference; the _set_ready() * will take care of completing the task */ g_cancellable_cancel (g_task_get_cancellable (self->priv->pending_allowed_modes_action)); g_task_return_error_if_cancelled (self->priv->pending_allowed_modes_action); g_clear_object (&self->priv->pending_allowed_modes_action); } self->priv->pending_allowed_modes_action = g_object_ref (task); /* use the last requested operator id to determine whether the * registration should be manual or automatic */ message = mbim_message_register_state_set_new ( self->priv->requested_operator_id ? self->priv->requested_operator_id : "", self->priv->requested_operator_id ? MBIM_REGISTER_ACTION_MANUAL : MBIM_REGISTER_ACTION_AUTOMATIC, self->priv->requested_data_class, NULL); mbim_device_command (device, message, 60, NULL, (GAsyncReadyCallback)register_state_current_modes_set_ready, task); return; } g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "Current mode switching is not supported"); g_object_unref (task); } /*****************************************************************************/ /* Load supported IP families (Modem interface) */ static MMBearerIpFamily modem_load_supported_ip_families_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { GError *inner_error = NULL; gssize value; value = g_task_propagate_int (G_TASK (res), &inner_error); if (inner_error) { g_propagate_error (error, inner_error); return MM_BEARER_IP_FAMILY_NONE; } return (MMBearerIpFamily)value; } static void modem_load_supported_ip_families (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; task = g_task_new (self, NULL, callback, user_data); /* Assume IPv4 + IPv6 + IPv4v6 supported */ g_task_return_int (task, MM_BEARER_IP_FAMILY_IPV4 | MM_BEARER_IP_FAMILY_IPV6 | MM_BEARER_IP_FAMILY_IPV4V6); g_object_unref (task); } /*****************************************************************************/ /* Unlock required loading (Modem interface) */ typedef struct { MbimDevice *device; gboolean last_attempt; } LoadUnlockRequiredContext; static void load_unlock_required_context_free (LoadUnlockRequiredContext *ctx) { g_object_unref (ctx->device); g_slice_free (LoadUnlockRequiredContext, ctx); } static MMModemLock modem_load_unlock_required_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { GError *inner_error = NULL; gssize value; value = g_task_propagate_int (G_TASK (res), &inner_error); if (inner_error) { g_propagate_error (error, inner_error); return MM_MODEM_LOCK_UNKNOWN; } return (MMModemLock)value; } static void pin_query_ready (MbimDevice *device, GAsyncResult *res, GTask *task) { MbimMessage *response; GError *error = NULL; MbimPinType pin_type; MbimPinState pin_state; MMBroadbandModemMbim *self; self = g_task_get_source_object (task); response = mbim_device_command_finish (device, res, &error); if (response && mbim_message_response_get_result (response, MBIM_MESSAGE_TYPE_COMMAND_DONE, &error) && mbim_message_pin_response_parse ( response, &pin_type, &pin_state, NULL, &error)) { MMModemLock unlock_required; if (pin_state == MBIM_PIN_STATE_UNLOCKED) unlock_required = MM_MODEM_LOCK_NONE; else unlock_required = mm_modem_lock_from_mbim_pin_type (pin_type); g_task_return_int (task, unlock_required); self->priv->enabled_cache.last_pin_type = pin_type; } /* VZ20M reports an error when SIM-PIN is required... */ else if (g_error_matches (error, MBIM_STATUS_ERROR, MBIM_STATUS_ERROR_PIN_REQUIRED)) { g_error_free (error); g_task_return_int (task, MBIM_PIN_TYPE_PIN1); } else g_task_return_error (task, error); g_object_unref (task); if (response) mbim_message_unref (response); } static void unlock_required_subscriber_ready_state_ready (MbimDevice *device, GAsyncResult *res, GTask *task) { LoadUnlockRequiredContext *ctx; MMBroadbandModemMbim *self; g_autoptr(MbimMessage) response = NULL; GError *error = NULL; MbimSubscriberReadyState ready_state = MBIM_SUBSCRIBER_READY_STATE_NOT_INITIALIZED; MbimSubscriberReadyState prev_ready_state = MBIM_SUBSCRIBER_READY_STATE_NOT_INITIALIZED; ctx = g_task_get_task_data (task); self = g_task_get_source_object (task); /* hold on to the previous ready_state value to determine if the retry logic needs to be reset. */ prev_ready_state = self->priv->enabled_cache.last_ready_state; /* reset to the default if any error happens */ self->priv->enabled_cache.last_ready_state = MBIM_SUBSCRIBER_READY_STATE_NOT_INITIALIZED; response = mbim_device_command_finish (device, res, &error); if (!response || !mbim_message_response_get_result (response, MBIM_MESSAGE_TYPE_COMMAND_DONE, &error)) { g_task_return_error (task, error); g_object_unref (task); return; } if (mbim_device_check_ms_mbimex_version (device, 3, 0)) { if (!mbim_message_ms_basic_connect_v3_subscriber_ready_status_response_parse ( response, &ready_state, NULL, /* flags */ NULL, /* subscriber id */ NULL, /* sim_iccid */ NULL, /* ready_info */ NULL, /* telephone_numbers_count */ NULL, /* telephone_numbers */ &error)) g_prefix_error (&error, "Failed processing MBIMEx v3.0 subscriber ready status response: "); else mm_obj_dbg (self, "processed MBIMEx v3.0 subscriber ready status response"); } else { if (!mbim_message_subscriber_ready_status_response_parse ( response, &ready_state, NULL, /* subscriber id */ NULL, /* sim_iccid */ NULL, /* ready_info */ NULL, /* telephone_numbers_count */ NULL, /* telephone_numbers */ &error)) g_prefix_error (&error, "Failed processing subscriber ready status response: "); else mm_obj_dbg (self, "processed subscriber ready status response"); } if (!error) { /* Store last valid ready state until it is NOT_INITIALIZED which is temporary */ if (ready_state != MBIM_SUBSCRIBER_READY_STATE_NOT_INITIALIZED) { self->priv->enabled_cache.last_ready_state = ready_state; } else { self->priv->enabled_cache.last_ready_state = prev_ready_state; } switch (ready_state) { case MBIM_SUBSCRIBER_READY_STATE_NOT_INITIALIZED: case MBIM_SUBSCRIBER_READY_STATE_INITIALIZED: case MBIM_SUBSCRIBER_READY_STATE_DEVICE_LOCKED: case MBIM_SUBSCRIBER_READY_STATE_NO_ESIM_PROFILE: /* Don't set error */ break; case MBIM_SUBSCRIBER_READY_STATE_SIM_NOT_INSERTED: /* This is an error, but we still want to retry. * The MC7710 may use this while the SIM is not ready yet. */ break; case MBIM_SUBSCRIBER_READY_STATE_BAD_SIM: error = mm_mobile_equipment_error_for_code (MM_MOBILE_EQUIPMENT_ERROR_SIM_WRONG, self); break; case MBIM_SUBSCRIBER_READY_STATE_FAILURE: case MBIM_SUBSCRIBER_READY_STATE_NOT_ACTIVATED: default: error = mm_mobile_equipment_error_for_code (MM_MOBILE_EQUIPMENT_ERROR_SIM_FAILURE, self); break; } } if (g_error_matches (error, MBIM_STATUS_ERROR, MBIM_STATUS_ERROR_NOT_INITIALIZED)) { g_clear_error (&error); ready_state = MBIM_SUBSCRIBER_READY_STATE_NOT_INITIALIZED; } /* Fatal errors are reported right away */ if (error) { g_task_return_error (task, error); g_object_unref (task); return; } /* Need to retry? */ if (ready_state == MBIM_SUBSCRIBER_READY_STATE_NOT_INITIALIZED || ready_state == MBIM_SUBSCRIBER_READY_STATE_SIM_NOT_INSERTED) { /* All retries consumed? issue error */ if (ctx->last_attempt) { if (ready_state == MBIM_SUBSCRIBER_READY_STATE_SIM_NOT_INSERTED) g_task_return_error (task, mm_mobile_equipment_error_for_code (MM_MOBILE_EQUIPMENT_ERROR_SIM_NOT_INSERTED, self)); else g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Error waiting for SIM to get initialized"); } else { /* Start the retry process from the top if the SIM state changes from * MBIM_SUBSCRIBER_READY_STATE_SIM_NOT_INSERTED to * MBIM_SUBSCRIBER_READY_STATE_NOT_INITIALIZED * This will address the race condition that occurs during rapid hotswap. */ gboolean retry_reset_needed = FALSE; if (prev_ready_state == MBIM_SUBSCRIBER_READY_STATE_SIM_NOT_INSERTED && ready_state == MBIM_SUBSCRIBER_READY_STATE_NOT_INITIALIZED) retry_reset_needed = TRUE; g_task_return_new_error (task, MM_CORE_ERROR, retry_reset_needed ? MM_CORE_ERROR_RESET_AND_RETRY : MM_CORE_ERROR_RETRY, "SIM not ready yet (retry)"); } g_object_unref (task); return; } /* Initialized */ if (ready_state == MBIM_SUBSCRIBER_READY_STATE_DEVICE_LOCKED || ready_state == MBIM_SUBSCRIBER_READY_STATE_INITIALIZED) { MbimMessage *message; /* Query which lock is to unlock */ message = mbim_message_pin_query_new (NULL); mbim_device_command (device, message, 10, g_task_get_cancellable (task), (GAsyncReadyCallback)pin_query_ready, task); mbim_message_unref (message); return; } /* When initialized but there are not profile set, assume no lock is * applied. */ mm_obj_dbg (self, "eSIM without profiles: assuming no lock is required"); g_assert (ready_state == MBIM_SUBSCRIBER_READY_STATE_NO_ESIM_PROFILE); g_task_return_int (task, MM_MODEM_LOCK_NONE); g_object_unref (task); } static gboolean wait_for_sim_ready (GTask *task) { LoadUnlockRequiredContext *ctx; MbimMessage *message; ctx = g_task_get_task_data (task); message = mbim_message_subscriber_ready_status_query_new (NULL); mbim_device_command (ctx->device, message, 10, g_task_get_cancellable (task), (GAsyncReadyCallback)unlock_required_subscriber_ready_state_ready, task); mbim_message_unref (message); return G_SOURCE_REMOVE; } static void modem_load_unlock_required (MMIfaceModem *self, gboolean last_attempt, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { LoadUnlockRequiredContext *ctx; MbimDevice *device; GTask *task; if (!peek_device (self, &device, callback, user_data)) return; ctx = g_slice_new (LoadUnlockRequiredContext); ctx->device = g_object_ref (device); ctx->last_attempt = last_attempt; task = g_task_new (self, cancellable, callback, user_data); g_task_set_task_data (task, ctx, (GDestroyNotify)load_unlock_required_context_free); wait_for_sim_ready (task); } /*****************************************************************************/ /* Unlock retries loading (Modem interface) */ static MMUnlockRetries * modem_load_unlock_retries_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { return g_task_propagate_pointer (G_TASK (res), error); } static void pin_query_unlock_retries_ready (MbimDevice *device, GAsyncResult *res, GTask *task) { MbimMessage *response; GError *error = NULL; MbimPinType pin_type; guint32 remaining_attempts; response = mbim_device_command_finish (device, res, &error); if (response && mbim_message_response_get_result (response, MBIM_MESSAGE_TYPE_COMMAND_DONE, &error) && mbim_message_pin_response_parse ( response, &pin_type, NULL, &remaining_attempts, &error)) { MMBroadbandModemMbim *self; MMModemLock lock; self = MM_BROADBAND_MODEM_MBIM (g_task_get_source_object (task)); lock = mm_modem_lock_from_mbim_pin_type (pin_type); mm_broadband_modem_mbim_set_unlock_retries (self, lock, remaining_attempts); g_task_return_pointer (task, g_object_ref (self->priv->unlock_retries), g_object_unref); self->priv->enabled_cache.last_pin_type = pin_type; } else g_task_return_error (task, error); g_object_unref (task); if (response) mbim_message_unref (response); } static void modem_load_unlock_retries (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data) { MbimDevice *device; MbimMessage *message; GTask *task; if (!peek_device (self, &device, callback, user_data)) return; task = g_task_new (self, NULL, callback, user_data); message = mbim_message_pin_query_new (NULL); mbim_device_command (device, message, 10, NULL, (GAsyncReadyCallback)pin_query_unlock_retries_ready, task); mbim_message_unref (message); } void mm_broadband_modem_mbim_set_unlock_retries (MMBroadbandModemMbim *self, MMModemLock lock_type, guint32 remaining_attempts) { g_assert (MM_IS_BROADBAND_MODEM_MBIM (self)); if (!self->priv->unlock_retries) self->priv->unlock_retries = mm_unlock_retries_new (); /* According to the MBIM specification, RemainingAttempts is set to * 0xffffffff if the device does not support this information. */ if (remaining_attempts != G_MAXUINT32) mm_unlock_retries_set (self->priv->unlock_retries, lock_type, remaining_attempts); } /*****************************************************************************/ /* Own numbers loading */ static GStrv modem_load_own_numbers_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { return g_task_propagate_pointer (G_TASK (res), error); } static void own_numbers_subscriber_ready_state_ready (MbimDevice *device, GAsyncResult *res, GTask *task) { MMBroadbandModemMbim *self; g_autoptr(MbimMessage) response = NULL; GError *error = NULL; gchar **telephone_numbers; self = g_task_get_source_object (task); response = mbim_device_command_finish (device, res, &error); if (!response || !mbim_message_response_get_result (response, MBIM_MESSAGE_TYPE_COMMAND_DONE, &error)) { g_task_return_error (task, error); g_object_unref (task); return; } if (mbim_device_check_ms_mbimex_version (device, 3, 0)) { if (!mbim_message_ms_basic_connect_v3_subscriber_ready_status_response_parse ( response, NULL, /* ready_state */ NULL, /* flags */ NULL, /* subscriber id */ NULL, /* sim_iccid */ NULL, /* ready_info */ NULL, /* telephone_numbers_count */ &telephone_numbers, &error)) g_prefix_error (&error, "Failed processing MBIMEx v3.0 subscriber ready status response: "); else mm_obj_dbg (self, "processed MBIMEx v3.0 subscriber ready status response"); } else { if (!mbim_message_subscriber_ready_status_response_parse ( response, NULL, /* ready_state */ NULL, /* subscriber id */ NULL, /* sim_iccid */ NULL, /* ready_info */ NULL, /* telephone_numbers_count */ &telephone_numbers, &error)) g_prefix_error (&error, "Failed processing subscriber ready status response: "); else mm_obj_dbg (self, "processed subscriber ready status response"); } if (error) g_task_return_error (task, error); else g_task_return_pointer (task, telephone_numbers, (GDestroyNotify)g_strfreev); g_object_unref (task); } static void modem_load_own_numbers (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data) { MbimDevice *device; GTask *task; g_autoptr(MbimMessage) message = NULL; if (!peek_device (self, &device, callback, user_data)) return; task = g_task_new (self, NULL, callback, user_data); message = mbim_message_subscriber_ready_status_query_new (NULL); mbim_device_command (device, message, 10, NULL, (GAsyncReadyCallback)own_numbers_subscriber_ready_state_ready, task); } /*****************************************************************************/ /* Initial power state loading */ static MMModemPowerState modem_load_power_state_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { GError *inner_error = NULL; gssize value; value = g_task_propagate_int (G_TASK (res), &inner_error); if (inner_error) { g_propagate_error (error, inner_error); return MM_MODEM_POWER_STATE_UNKNOWN; } return (MMModemPowerState)value; } static void radio_state_query_ready (MbimDevice *device, GAsyncResult *res, GTask *task) { MbimMessage *response; GError *error = NULL; MbimRadioSwitchState hardware_radio_state; MbimRadioSwitchState software_radio_state; response = mbim_device_command_finish (device, res, &error); if (response && mbim_message_response_get_result (response, MBIM_MESSAGE_TYPE_COMMAND_DONE, &error) && mbim_message_radio_state_response_parse ( response, &hardware_radio_state, &software_radio_state, &error)) { MMModemPowerState state; if (hardware_radio_state == MBIM_RADIO_SWITCH_STATE_OFF || software_radio_state == MBIM_RADIO_SWITCH_STATE_OFF) state = MM_MODEM_POWER_STATE_LOW; else state = MM_MODEM_POWER_STATE_ON; g_task_return_int (task, state); } else g_task_return_error (task, error); g_object_unref (task); if (response) mbim_message_unref (response); } static void modem_load_power_state (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data) { MbimDevice *device; MbimMessage *message; GTask *task; if (!peek_device (self, &device, callback, user_data)) return; task = g_task_new (self, NULL, callback, user_data); message = mbim_message_radio_state_query_new (NULL); mbim_device_command (device, message, 10, NULL, (GAsyncReadyCallback)radio_state_query_ready, task); mbim_message_unref (message); } /*****************************************************************************/ /* Power up (Modem interface) */ static gboolean power_up_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void radio_state_set_up_ready (MbimDevice *device, GAsyncResult *res, GTask *task) { MMBroadbandModemMbim *self; g_autoptr(MbimMessage) response = NULL; g_autoptr(GError) error = NULL; MbimRadioSwitchState hardware_radio_state; MbimRadioSwitchState software_radio_state; self = g_task_get_source_object (task); response = mbim_device_command_finish (device, res, &error); if (response && mbim_message_response_get_result (response, MBIM_MESSAGE_TYPE_COMMAND_DONE, &error) && mbim_message_radio_state_response_parse ( response, &hardware_radio_state, &software_radio_state, &error)) { if (hardware_radio_state == MBIM_RADIO_SWITCH_STATE_OFF) error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Cannot power-up: hardware radio switch is OFF"); else if (software_radio_state == MBIM_RADIO_SWITCH_STATE_OFF) error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Cannot power-up: sotware radio switch is OFF"); } /* Nice! we're done, quick exit */ if (!error) { g_task_return_boolean (task, TRUE); g_object_unref (task); return; } /* The SDX55 returns "Operation not allowed", but not really sure about other * older devices. The original logic in the MBIM implemetation triggered a retry * for any kind of error, so let's do the same for now. */ mm_obj_warn (self, "%s", error->message); g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_RETRY, "Invalid transition"); g_object_unref (task); } static void modem_power_up (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data) { MbimDevice *device; GTask *task; g_autoptr(MbimMessage) message = NULL; if (!peek_device (self, &device, callback, user_data)) return; task = g_task_new (self, NULL, callback, user_data); message = mbim_message_radio_state_set_new (MBIM_RADIO_SWITCH_STATE_ON, NULL); mbim_device_command (device, message, 20, NULL, (GAsyncReadyCallback)radio_state_set_up_ready, task); } /*****************************************************************************/ /* Power down (Modem interface) */ static gboolean power_down_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void radio_state_set_down_ready (MbimDevice *device, GAsyncResult *res, GTask *task) { MbimMessage *response; GError *error = NULL; response = mbim_device_command_finish (device, res, &error); if (response) { mbim_message_response_get_result (response, MBIM_MESSAGE_TYPE_COMMAND_DONE, &error); mbim_message_unref (response); } if (error) g_task_return_error (task, error); else g_task_return_boolean (task, TRUE); g_object_unref (task); } static void modem_power_down (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data) { MbimDevice *device; MbimMessage *message; GTask *task; if (!peek_device (self, &device, callback, user_data)) return; task = g_task_new (self, NULL, callback, user_data); message = mbim_message_radio_state_set_new (MBIM_RADIO_SWITCH_STATE_OFF, NULL); mbim_device_command (device, message, 20, NULL, (GAsyncReadyCallback)radio_state_set_down_ready, task); mbim_message_unref (message); } /*****************************************************************************/ /* Signal quality loading (Modem interface) */ static guint modem_load_signal_quality_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { gssize value; value = g_task_propagate_int (G_TASK (res), error); return value < 0 ? 0 : value; } static void signal_state_query_ready (MbimDevice *device, GAsyncResult *res, GTask *task) { MMBroadbandModemMbim *self; GError *error = NULL; g_autoptr(MbimMessage) response = NULL; g_autoptr(MbimRsrpSnrInfoArray) rsrp_snr = NULL; guint32 rsrp_snr_count = 0; guint32 rssi; guint32 error_rate = 99; guint quality; MbimDataClass data_class; g_autoptr(MMSignal) cdma = NULL; g_autoptr(MMSignal) evdo = NULL; g_autoptr(MMSignal) gsm = NULL; g_autoptr(MMSignal) umts = NULL; g_autoptr(MMSignal) lte = NULL; g_autoptr(MMSignal) nr5g = NULL; self = g_task_get_source_object (task); response = mbim_device_command_finish (device, res, &error); if (!response || !mbim_message_response_get_result (response, MBIM_MESSAGE_TYPE_COMMAND_DONE, &error)) { g_task_return_error (task, error); g_object_unref (task); return; } if (mbim_device_check_ms_mbimex_version (device, 2, 0)) { if (!mbim_message_ms_basic_connect_v2_signal_state_response_parse ( response, &rssi, &error_rate, NULL, /* signal_strength_interval */ NULL, /* rssi_threshold */ NULL, /* error_rate_threshold */ &rsrp_snr_count, &rsrp_snr, &error)) g_prefix_error (&error, "Failed processing MBIMEx v2.0 signal state response: "); else mm_obj_dbg (self, "processed MBIMEx v2.0 signal state response"); } else { if (!mbim_message_signal_state_response_parse ( response, &rssi, &error_rate, NULL, /* signal_strength_interval */ NULL, /* rssi_threshold */ NULL, /* error_rate_threshold */ &error)) g_prefix_error (&error, "Failed processing signal state response: "); else mm_obj_dbg (self, "processed signal state response"); } if (error) g_task_return_error (task, error); else { /* Best guess of current data class */ data_class = self->priv->enabled_cache.highest_available_data_class; if (data_class == 0) data_class = self->priv->enabled_cache.available_data_classes; if (mm_signal_from_mbim_signal_state (data_class, rssi, error_rate, rsrp_snr, rsrp_snr_count, self, &cdma, &evdo, &gsm, &umts, <e, &nr5g)) mm_iface_modem_signal_update (MM_IFACE_MODEM_SIGNAL (self), cdma, evdo, gsm, umts, lte, nr5g); quality = mm_signal_quality_from_mbim_signal_state (rssi, rsrp_snr, rsrp_snr_count, self); g_task_return_int (task, quality); } g_object_unref (task); } static void modem_load_signal_quality (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data) { MbimDevice *device; MbimMessage *message; GTask *task; if (!peek_device (self, &device, callback, user_data)) return; task = g_task_new (self, NULL, callback, user_data); message = mbim_message_signal_state_query_new (NULL); mbim_device_command (device, message, 10, NULL, (GAsyncReadyCallback)signal_state_query_ready, task); mbim_message_unref (message); } /*****************************************************************************/ /* Reset */ static gboolean modem_reset_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } #if defined WITH_QMI && QMI_MBIM_QMUX_SUPPORTED static void shared_qmi_reset_ready (MMIfaceModem *self, GAsyncResult *res, GTask *task) { GError *error = NULL; if (!mm_shared_qmi_reset_finish (self, res, &error)) g_task_return_error (task, error); else g_task_return_boolean (task, TRUE); g_object_unref (task); } static void modem_reset_shared_qmi (GTask *task) { mm_shared_qmi_reset (MM_IFACE_MODEM (g_task_get_source_object (task)), (GAsyncReadyCallback)shared_qmi_reset_ready, task); } #endif static void intel_firmware_update_modem_reboot_set_ready (MbimDevice *device, GAsyncResult *res, GTask *task) { MbimMessage *response; GError *error = NULL; response = mbim_device_command_finish (device, res, &error); if (!response || !mbim_message_response_get_result (response, MBIM_MESSAGE_TYPE_COMMAND_DONE, &error)) { #if defined WITH_QMI && QMI_MBIM_QMUX_SUPPORTED /* We don't really expect the Intel firmware update service to be * available in QMI modems, but doesn't harm to fallback to the QMI * implementation here */ mm_obj_dbg (g_task_get_source_object (task), "couldn't run intel reset: %s", error->message); g_error_free (error); modem_reset_shared_qmi (task); #else g_task_return_error (task, error); g_object_unref (task); #endif } else { g_task_return_boolean (task, TRUE); g_object_unref (task); } if (response) mbim_message_unref (response); } static void modem_reset (MMIfaceModem *_self, GAsyncReadyCallback callback, gpointer user_data) { MMBroadbandModemMbim *self = MM_BROADBAND_MODEM_MBIM (_self); GTask *task; MbimDevice *device; MbimMessage *message; if (!peek_device (self, &device, callback, user_data)) return; task = g_task_new (self, NULL, callback, user_data); if (!self->priv->is_intel_reset_supported) { #if defined WITH_QMI && QMI_MBIM_QMUX_SUPPORTED modem_reset_shared_qmi (task); #else g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "modem reset operation is not supported"); g_object_unref (task); #endif return; } /* This message is defined in the Intel Firmware Update service, but it * really is just a standard modem reboot. */ message = mbim_message_intel_firmware_update_modem_reboot_set_new (NULL); mbim_device_command (device, message, 10, NULL, (GAsyncReadyCallback)intel_firmware_update_modem_reboot_set_ready, task); mbim_message_unref (message); } /*****************************************************************************/ /* Cell info retrieval */ typedef enum { GET_CELL_INFO_STEP_FIRST, GET_CELL_INFO_STEP_RFIM, GET_CELL_INFO_STEP_CELL_INFO, GET_CELL_INFO_STEP_LAST } GetCellInfoStep; typedef struct { GetCellInfoStep step; GList *rfim_info_list; GList *cell_info_list; GError *saved_error; } GetCellInfoContext; static void get_cell_info_context_free (GetCellInfoContext *ctx) { mm_rfim_info_list_free (ctx->rfim_info_list); g_assert (!ctx->saved_error); g_free (ctx); } static void get_cell_info_step (MbimDevice *device, GTask *task); static GList * modem_get_cell_info_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { return g_task_propagate_pointer (G_TASK (res), error); } static void cell_info_list_free (GList *list) { g_list_free_full (list, (GDestroyNotify)g_object_unref); } static void base_stations_info_query_ready (MbimDevice *device, GAsyncResult *res, GTask *task) { MMBroadbandModemMbim *self; g_autoptr(MbimMessage) response = NULL; GError *error = NULL; GList *list = NULL; MMCellInfo *info = NULL; g_autoptr(MbimCellInfoServingGsm) gsm_serving_cell = NULL; g_autoptr(MbimCellInfoServingUmts) umts_serving_cell = NULL; g_autoptr(MbimCellInfoServingTdscdma) tdscdma_serving_cell = NULL; g_autoptr(MbimCellInfoServingLte) lte_serving_cell = NULL; guint32 gsm_neighboring_cells_count = 0; g_autoptr(MbimCellInfoNeighboringGsmArray) gsm_neighboring_cells = NULL; guint32 umts_neighboring_cells_count = 0; g_autoptr(MbimCellInfoNeighboringUmtsArray) umts_neighboring_cells = NULL; guint32 tdscdma_neighboring_cells_count = 0; g_autoptr(MbimCellInfoNeighboringTdscdmaArray) tdscdma_neighboring_cells = NULL; guint32 lte_neighboring_cells_count = 0; g_autoptr(MbimCellInfoNeighboringLteArray) lte_neighboring_cells = NULL; guint32 cdma_cells_count = 0; g_autoptr(MbimCellInfoCdmaArray) cdma_cells = NULL; guint32 nr_serving_cells_count = 0; g_autoptr(MbimCellInfoServingNrArray) nr_serving_cells = NULL; guint32 nr_neighboring_cells_count = 0; g_autoptr(MbimCellInfoNeighboringNrArray) nr_neighboring_cells = NULL; GetCellInfoContext *ctx; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); response = mbim_device_command_finish (device, res, &error); if (!response || !mbim_message_response_get_result (response, MBIM_MESSAGE_TYPE_COMMAND_DONE, &error)) { ctx->saved_error = error; ctx->step = GET_CELL_INFO_STEP_LAST; get_cell_info_step (device, task); return; } /* MBIMEx 3.0 support */ if (mbim_device_check_ms_mbimex_version (device, 3, 0)) { if (!mbim_message_ms_basic_connect_extensions_v3_base_stations_info_response_parse ( response, NULL, /* system_type_v3 */ NULL, /* system_subtype */ &gsm_serving_cell, &umts_serving_cell, &tdscdma_serving_cell, <e_serving_cell, &gsm_neighboring_cells_count, &gsm_neighboring_cells, &umts_neighboring_cells_count, &umts_neighboring_cells, &tdscdma_neighboring_cells_count, &tdscdma_neighboring_cells, <e_neighboring_cells_count, <e_neighboring_cells, &cdma_cells_count, &cdma_cells, &nr_serving_cells_count, &nr_serving_cells, &nr_neighboring_cells_count, &nr_neighboring_cells, &error)) g_prefix_error (&error, "Failed processing MBIMEx v3.0 base stations info response: "); else mm_obj_dbg (self, "processed MBIMEx v3.0 base stations info response"); } /* MBIMEx 1.0 support */ else { if (!mbim_message_ms_basic_connect_extensions_base_stations_info_response_parse ( response, NULL, /* system_type */ &gsm_serving_cell, &umts_serving_cell, &tdscdma_serving_cell, <e_serving_cell, &gsm_neighboring_cells_count, &gsm_neighboring_cells, &umts_neighboring_cells_count, &umts_neighboring_cells, &tdscdma_neighboring_cells_count, &tdscdma_neighboring_cells, <e_neighboring_cells_count, <e_neighboring_cells, &cdma_cells_count, &cdma_cells, &error)) g_prefix_error (&error, "Failed processing MBIMEx v1.0 base stations info response: "); else mm_obj_dbg (self, "processed MBIMEx v1.0 base stations info response"); } if (error) { ctx->saved_error = error; ctx->step = GET_CELL_INFO_STEP_LAST; get_cell_info_step (device, task); return; } #define CELL_INFO_SET_STR(VALUE, SETTER, CELL_TYPE) do { \ if (VALUE) { \ mm_cell_info_##SETTER (CELL_TYPE (info), VALUE); \ } \ } while (0) #define CELL_INFO_SET_HEXSTR(VALUE, UNKNOWN, MODIFIER, SETTER, CELL_TYPE) do { \ if (VALUE != UNKNOWN) { \ g_autofree gchar *str = NULL; \ \ str = g_strdup_printf ("%" MODIFIER "X", VALUE); \ mm_cell_info_##SETTER (CELL_TYPE (info), str); \ } \ } while (0) #define CELL_INFO_SET_UINT(VALUE, UNKNOWN, SETTER, CELL_TYPE) do { \ if (VALUE != UNKNOWN) { \ mm_cell_info_##SETTER (CELL_TYPE (info), VALUE); \ } \ } while (0) #define CELL_INFO_SET_INT_DOUBLE(VALUE, UNKNOWN, SETTER, CELL_TYPE) do { \ if (VALUE != (gint)UNKNOWN) { \ mm_cell_info_##SETTER (CELL_TYPE (info), (gdouble)VALUE); \ } \ } while (0) #define CELL_INFO_SET_UINT_DOUBLE_SCALED(VALUE, UNKNOWN, SCALE, SETTER, CELL_TYPE) do { \ if (VALUE != UNKNOWN) { \ mm_cell_info_##SETTER (CELL_TYPE (info), (gdouble)(VALUE + SCALE)); \ } \ } while (0) if (gsm_serving_cell) { info = mm_cell_info_gsm_new_from_dictionary (NULL); mm_cell_info_set_serving (info, TRUE); CELL_INFO_SET_STR (gsm_serving_cell->provider_id, gsm_set_operator_id, MM_CELL_INFO_GSM); CELL_INFO_SET_HEXSTR (gsm_serving_cell->location_area_code, 0xFFFFFFFF, "", gsm_set_lac, MM_CELL_INFO_GSM); CELL_INFO_SET_HEXSTR (gsm_serving_cell->cell_id, 0xFFFFFFFF, "", gsm_set_ci, MM_CELL_INFO_GSM); CELL_INFO_SET_UINT (gsm_serving_cell->timing_advance, 0xFFFFFFFF, gsm_set_timing_advance, MM_CELL_INFO_GSM); CELL_INFO_SET_UINT (gsm_serving_cell->arfcn, 0xFFFFFFFF, gsm_set_arfcn, MM_CELL_INFO_GSM); CELL_INFO_SET_HEXSTR (gsm_serving_cell->base_station_id, 0xFFFFFFFF, "", gsm_set_base_station_id, MM_CELL_INFO_GSM); CELL_INFO_SET_UINT (gsm_serving_cell->rx_level, 0xFFFFFFFF, gsm_set_rx_level, MM_CELL_INFO_GSM); list = g_list_append (list, g_steal_pointer (&info)); } if (gsm_neighboring_cells_count && gsm_neighboring_cells) { guint i; for (i = 0; i < gsm_neighboring_cells_count; i++) { info = mm_cell_info_gsm_new_from_dictionary (NULL); mm_cell_info_set_serving (info, FALSE); CELL_INFO_SET_STR (gsm_neighboring_cells[i]->provider_id, gsm_set_operator_id, MM_CELL_INFO_GSM); CELL_INFO_SET_HEXSTR (gsm_neighboring_cells[i]->location_area_code, 0xFFFFFFFF, "", gsm_set_lac, MM_CELL_INFO_GSM); CELL_INFO_SET_HEXSTR (gsm_neighboring_cells[i]->cell_id, 0xFFFFFFFF, "", gsm_set_ci, MM_CELL_INFO_GSM); CELL_INFO_SET_UINT (gsm_neighboring_cells[i]->arfcn, 0xFFFFFFFF, gsm_set_arfcn, MM_CELL_INFO_GSM); CELL_INFO_SET_HEXSTR (gsm_neighboring_cells[i]->base_station_id, 0xFFFFFFFF, "", gsm_set_base_station_id, MM_CELL_INFO_GSM); CELL_INFO_SET_UINT (gsm_neighboring_cells[i]->rx_level, 0xFFFFFFFF, gsm_set_rx_level, MM_CELL_INFO_GSM); list = g_list_append (list, g_steal_pointer (&info)); } } if (umts_serving_cell) { info = mm_cell_info_umts_new_from_dictionary (NULL); mm_cell_info_set_serving (info, TRUE); CELL_INFO_SET_STR (umts_serving_cell->provider_id, umts_set_operator_id, MM_CELL_INFO_UMTS); CELL_INFO_SET_HEXSTR (umts_serving_cell->location_area_code, 0xFFFFFFFF, "", umts_set_lac, MM_CELL_INFO_UMTS); CELL_INFO_SET_HEXSTR (umts_serving_cell->cell_id, 0xFFFFFFFF, "", umts_set_ci, MM_CELL_INFO_UMTS); CELL_INFO_SET_UINT (umts_serving_cell->frequency_info_ul, 0xFFFFFFFF, umts_set_frequency_fdd_ul, MM_CELL_INFO_UMTS); CELL_INFO_SET_UINT (umts_serving_cell->frequency_info_dl, 0xFFFFFFFF, umts_set_frequency_fdd_dl, MM_CELL_INFO_UMTS); CELL_INFO_SET_UINT (umts_serving_cell->frequency_info_nt, 0xFFFFFFFF, umts_set_frequency_tdd, MM_CELL_INFO_UMTS); CELL_INFO_SET_UINT (umts_serving_cell->uarfcn, 0xFFFFFFFF, umts_set_uarfcn, MM_CELL_INFO_UMTS); CELL_INFO_SET_UINT (umts_serving_cell->primary_scrambling_code, 0xFFFFFFFF, umts_set_psc, MM_CELL_INFO_UMTS); CELL_INFO_SET_INT_DOUBLE (umts_serving_cell->rscp, 0, umts_set_rscp, MM_CELL_INFO_UMTS); CELL_INFO_SET_INT_DOUBLE (umts_serving_cell->ecno, 1, umts_set_ecio, MM_CELL_INFO_UMTS); CELL_INFO_SET_UINT (umts_serving_cell->path_loss, 0xFFFFFFFF, umts_set_path_loss, MM_CELL_INFO_UMTS); list = g_list_append (list, g_steal_pointer (&info)); } if (umts_neighboring_cells_count && umts_neighboring_cells) { guint i; for (i = 0; i < umts_neighboring_cells_count; i++) { info = mm_cell_info_umts_new_from_dictionary (NULL); mm_cell_info_set_serving (info, FALSE); CELL_INFO_SET_STR (umts_neighboring_cells[i]->provider_id, umts_set_operator_id, MM_CELL_INFO_UMTS); CELL_INFO_SET_HEXSTR (umts_neighboring_cells[i]->location_area_code, 0xFFFFFFFF, "", umts_set_lac, MM_CELL_INFO_UMTS); CELL_INFO_SET_HEXSTR (umts_neighboring_cells[i]->cell_id, 0xFFFFFFFF, "", umts_set_ci, MM_CELL_INFO_UMTS); CELL_INFO_SET_UINT (umts_neighboring_cells[i]->uarfcn, 0xFFFFFFFF, umts_set_uarfcn, MM_CELL_INFO_UMTS); CELL_INFO_SET_UINT (umts_neighboring_cells[i]->primary_scrambling_code, 0xFFFFFFFF, umts_set_psc, MM_CELL_INFO_UMTS); CELL_INFO_SET_INT_DOUBLE (umts_neighboring_cells[i]->rscp, 0, umts_set_rscp, MM_CELL_INFO_UMTS); CELL_INFO_SET_INT_DOUBLE (umts_neighboring_cells[i]->ecno, 1, umts_set_ecio, MM_CELL_INFO_UMTS); CELL_INFO_SET_UINT (umts_neighboring_cells[i]->path_loss, 0xFFFFFFFF, umts_set_path_loss, MM_CELL_INFO_UMTS); list = g_list_append (list, g_steal_pointer (&info)); } } if (tdscdma_serving_cell) { info = mm_cell_info_tdscdma_new_from_dictionary (NULL); mm_cell_info_set_serving (info, TRUE); CELL_INFO_SET_STR (tdscdma_serving_cell->provider_id, tdscdma_set_operator_id, MM_CELL_INFO_TDSCDMA); CELL_INFO_SET_HEXSTR (tdscdma_serving_cell->location_area_code, 0xFFFFFFFF, "", tdscdma_set_lac, MM_CELL_INFO_TDSCDMA); CELL_INFO_SET_HEXSTR (tdscdma_serving_cell->cell_id, 0xFFFFFFFF, "", tdscdma_set_ci, MM_CELL_INFO_TDSCDMA); CELL_INFO_SET_UINT (tdscdma_serving_cell->uarfcn, 0xFFFFFFFF, tdscdma_set_uarfcn, MM_CELL_INFO_TDSCDMA); CELL_INFO_SET_UINT (tdscdma_serving_cell->cell_parameter_id, 0xFFFFFFFF, tdscdma_set_cell_parameter_id, MM_CELL_INFO_TDSCDMA); CELL_INFO_SET_UINT (tdscdma_serving_cell->timing_advance, 0xFFFFFFFF, tdscdma_set_timing_advance, MM_CELL_INFO_TDSCDMA); CELL_INFO_SET_INT_DOUBLE (tdscdma_serving_cell->rscp, 0xFFFFFFFF, tdscdma_set_rscp, MM_CELL_INFO_TDSCDMA); CELL_INFO_SET_UINT (tdscdma_serving_cell->path_loss, 0xFFFFFFFF, tdscdma_set_path_loss, MM_CELL_INFO_TDSCDMA); list = g_list_append (list, g_steal_pointer (&info)); } if (tdscdma_neighboring_cells_count && tdscdma_neighboring_cells) { guint i; for (i = 0; i < tdscdma_neighboring_cells_count; i++) { info = mm_cell_info_tdscdma_new_from_dictionary (NULL); mm_cell_info_set_serving (info, FALSE); CELL_INFO_SET_STR (tdscdma_neighboring_cells[i]->provider_id, tdscdma_set_operator_id, MM_CELL_INFO_TDSCDMA); CELL_INFO_SET_HEXSTR (tdscdma_neighboring_cells[i]->location_area_code, 0xFFFFFFFF, "", tdscdma_set_lac, MM_CELL_INFO_TDSCDMA); CELL_INFO_SET_HEXSTR (tdscdma_neighboring_cells[i]->cell_id, 0xFFFFFFFF, "", tdscdma_set_ci, MM_CELL_INFO_TDSCDMA); CELL_INFO_SET_UINT (tdscdma_neighboring_cells[i]->uarfcn, 0xFFFFFFFF, tdscdma_set_uarfcn, MM_CELL_INFO_TDSCDMA); CELL_INFO_SET_UINT (tdscdma_neighboring_cells[i]->cell_parameter_id, 0xFFFFFFFF, tdscdma_set_cell_parameter_id, MM_CELL_INFO_TDSCDMA); CELL_INFO_SET_UINT (tdscdma_neighboring_cells[i]->timing_advance, 0xFFFFFFFF, tdscdma_set_timing_advance, MM_CELL_INFO_TDSCDMA); CELL_INFO_SET_INT_DOUBLE (tdscdma_neighboring_cells[i]->rscp, 0xFFFFFFFF, tdscdma_set_rscp, MM_CELL_INFO_TDSCDMA); CELL_INFO_SET_UINT (tdscdma_neighboring_cells[i]->path_loss, 0xFFFFFFFF, tdscdma_set_path_loss, MM_CELL_INFO_TDSCDMA); list = g_list_append (list, g_steal_pointer (&info)); } } if (lte_serving_cell) { GList *l; GList *next; info = mm_cell_info_lte_new_from_dictionary (NULL); mm_cell_info_set_serving (info, TRUE); CELL_INFO_SET_STR (lte_serving_cell->provider_id, lte_set_operator_id, MM_CELL_INFO_LTE); CELL_INFO_SET_HEXSTR (lte_serving_cell->tac, 0xFFFFFFFF, "", lte_set_tac, MM_CELL_INFO_LTE); CELL_INFO_SET_HEXSTR (lte_serving_cell->cell_id, 0xFFFFFFFF, "", lte_set_ci, MM_CELL_INFO_LTE); CELL_INFO_SET_HEXSTR (lte_serving_cell->physical_cell_id, 0xFFFFFFFF, "", lte_set_physical_ci, MM_CELL_INFO_LTE); CELL_INFO_SET_UINT (lte_serving_cell->earfcn, 0xFFFFFFFF, lte_set_earfcn, MM_CELL_INFO_LTE); CELL_INFO_SET_INT_DOUBLE (lte_serving_cell->rsrp, 0xFFFFFFFF, lte_set_rsrp, MM_CELL_INFO_LTE); CELL_INFO_SET_INT_DOUBLE (lte_serving_cell->rsrq, 0xFFFFFFFF, lte_set_rsrq, MM_CELL_INFO_LTE); CELL_INFO_SET_UINT (lte_serving_cell->timing_advance, 0xFFFFFFFF, lte_set_timing_advance, MM_CELL_INFO_LTE); /* Update cell info with the radio frequency information received previously */ for (l = ctx->rfim_info_list, next = NULL; l; l = next) { MMRfInfo *data; next = g_list_next (l); data = (MMRfInfo *)(l->data); if (fabs ((mm_earfcn_to_frequency (lte_serving_cell->earfcn, self)) - data->center_frequency) < FREQUENCY_TOLERANCE_HZ) { mm_obj_dbg (self, "Merging radio frequency data with lte serving cell info"); CELL_INFO_SET_UINT (data->serving_cell_type, MM_SERVING_CELL_TYPE_INVALID, lte_set_serving_cell_type, MM_CELL_INFO_LTE); CELL_INFO_SET_UINT (data->bandwidth, 0xFFFFFFFF, lte_set_bandwidth, MM_CELL_INFO_LTE); ctx->rfim_info_list = g_list_delete_link (ctx->rfim_info_list, l); mm_rf_info_free (data); } } list = g_list_append (list, g_steal_pointer (&info)); } if (lte_neighboring_cells_count && lte_neighboring_cells) { guint i; for (i = 0; i < lte_neighboring_cells_count; i++) { info = mm_cell_info_lte_new_from_dictionary (NULL); mm_cell_info_set_serving (info, FALSE); CELL_INFO_SET_STR (lte_neighboring_cells[i]->provider_id, lte_set_operator_id, MM_CELL_INFO_LTE); CELL_INFO_SET_HEXSTR (lte_neighboring_cells[i]->tac, 0xFFFFFFFF, "", lte_set_tac, MM_CELL_INFO_LTE); CELL_INFO_SET_HEXSTR (lte_neighboring_cells[i]->cell_id, 0xFFFFFFFF, "", lte_set_ci, MM_CELL_INFO_LTE); CELL_INFO_SET_HEXSTR (lte_neighboring_cells[i]->physical_cell_id, 0xFFFFFFFF, "", lte_set_physical_ci, MM_CELL_INFO_LTE); CELL_INFO_SET_UINT (lte_neighboring_cells[i]->earfcn, 0xFFFFFFFF, lte_set_earfcn, MM_CELL_INFO_LTE); CELL_INFO_SET_INT_DOUBLE (lte_neighboring_cells[i]->rsrp, 0xFFFFFFFF, lte_set_rsrp, MM_CELL_INFO_LTE); CELL_INFO_SET_INT_DOUBLE (lte_neighboring_cells[i]->rsrq, 0xFFFFFFFF, lte_set_rsrq, MM_CELL_INFO_LTE); list = g_list_append (list, g_steal_pointer (&info)); } } if (cdma_cells_count && cdma_cells) { guint i; for (i = 0; i < cdma_cells_count; i++) { info = mm_cell_info_cdma_new_from_dictionary (NULL); mm_cell_info_set_serving (info, cdma_cells[i]->serving_cell_flag); CELL_INFO_SET_HEXSTR (cdma_cells[i]->nid, 0xFFFFFFFF, "", cdma_set_nid, MM_CELL_INFO_CDMA); CELL_INFO_SET_HEXSTR (cdma_cells[i]->sid, 0xFFFFFFFF, "", cdma_set_sid, MM_CELL_INFO_CDMA); CELL_INFO_SET_HEXSTR (cdma_cells[i]->base_station_id, 0xFFFFFFFF, "", cdma_set_base_station_id, MM_CELL_INFO_CDMA); CELL_INFO_SET_HEXSTR (cdma_cells[i]->ref_pn, 0xFFFFFFFF, "", cdma_set_ref_pn, MM_CELL_INFO_CDMA); CELL_INFO_SET_UINT (cdma_cells[i]->pilot_strength, 0xFFFFFFFF, cdma_set_pilot_strength, MM_CELL_INFO_CDMA); list = g_list_append (list, g_steal_pointer (&info)); } } if (nr_serving_cells_count && nr_serving_cells) { guint i; for (i = 0; i < nr_serving_cells_count; i++) { GList *l; GList *next; info = mm_cell_info_nr5g_new_from_dictionary (NULL); mm_cell_info_set_serving (info, TRUE); CELL_INFO_SET_STR (nr_serving_cells[i]->provider_id, nr5g_set_operator_id, MM_CELL_INFO_NR5G); CELL_INFO_SET_HEXSTR (nr_serving_cells[i]->tac, 0xFFFFFFFF, "", nr5g_set_tac, MM_CELL_INFO_NR5G); CELL_INFO_SET_HEXSTR (nr_serving_cells[i]->nci, 0xFFFFFFFFFFFFFFFF, G_GINT64_MODIFIER, nr5g_set_ci, MM_CELL_INFO_NR5G); CELL_INFO_SET_HEXSTR (nr_serving_cells[i]->physical_cell_id, 0xFFFFFFFF, "", nr5g_set_physical_ci, MM_CELL_INFO_NR5G); CELL_INFO_SET_UINT (nr_serving_cells[i]->nrarfcn, 0xFFFFFFFF, nr5g_set_nrarfcn, MM_CELL_INFO_NR5G); CELL_INFO_SET_UINT_DOUBLE_SCALED (nr_serving_cells[i]->rsrp, 0xFFFFFFFF, -156, nr5g_set_rsrp, MM_CELL_INFO_NR5G); CELL_INFO_SET_UINT_DOUBLE_SCALED (nr_serving_cells[i]->rsrq, 0xFFFFFFFF, -43, nr5g_set_rsrq, MM_CELL_INFO_NR5G); CELL_INFO_SET_UINT_DOUBLE_SCALED (nr_serving_cells[i]->sinr, 0xFFFFFFFF, -23, nr5g_set_sinr, MM_CELL_INFO_NR5G); CELL_INFO_SET_UINT (nr_serving_cells[i]->timing_advance, 0xFFFFFFFFFFFFFFFF, nr5g_set_timing_advance, MM_CELL_INFO_NR5G); /* Update cell info with the radio frequency information received previously */ for (l = ctx->rfim_info_list, next = NULL; l; l = next) { MMRfInfo *data; next = g_list_next (l); data = (MMRfInfo *)(l->data); /* Comparing the derived frequncy value from NRARFCN with received center frequency data to map the NR CELL */ if (fabs (mm_nrarfcn_to_frequency (nr_serving_cells[i]->nrarfcn, self) - data->center_frequency) < FREQUENCY_TOLERANCE_HZ) { mm_obj_dbg (self, "Merging radio frequency data with 5gnr serving cell info"); CELL_INFO_SET_UINT (data->serving_cell_type, MM_SERVING_CELL_TYPE_INVALID, nr5g_set_serving_cell_type, MM_CELL_INFO_NR5G); CELL_INFO_SET_UINT (data->bandwidth, 0xFFFFFFFF, nr5g_set_bandwidth, MM_CELL_INFO_NR5G); ctx->rfim_info_list = g_list_delete_link (ctx->rfim_info_list, l); mm_rf_info_free (data); } } list = g_list_append (list, g_steal_pointer (&info)); } } if (nr_neighboring_cells_count && nr_neighboring_cells) { guint i; for (i = 0; i < nr_neighboring_cells_count; i++) { info = mm_cell_info_nr5g_new_from_dictionary (NULL); mm_cell_info_set_serving (info, FALSE); CELL_INFO_SET_STR (nr_neighboring_cells[i]->provider_id, nr5g_set_operator_id, MM_CELL_INFO_NR5G); CELL_INFO_SET_HEXSTR (nr_neighboring_cells[i]->tac, 0xFFFFFFFF, "", nr5g_set_tac, MM_CELL_INFO_NR5G); CELL_INFO_SET_STR (nr_neighboring_cells[i]->cell_id, nr5g_set_ci, MM_CELL_INFO_NR5G); CELL_INFO_SET_HEXSTR (nr_neighboring_cells[i]->physical_cell_id, 0xFFFFFFFF, "", nr5g_set_physical_ci, MM_CELL_INFO_NR5G); CELL_INFO_SET_UINT_DOUBLE_SCALED (nr_neighboring_cells[i]->rsrp, 0xFFFFFFFF, -156, nr5g_set_rsrp, MM_CELL_INFO_NR5G); CELL_INFO_SET_UINT_DOUBLE_SCALED (nr_neighboring_cells[i]->rsrq, 0xFFFFFFFF, -43, nr5g_set_rsrq, MM_CELL_INFO_NR5G); CELL_INFO_SET_UINT_DOUBLE_SCALED (nr_neighboring_cells[i]->sinr, 0xFFFFFFFF, -23, nr5g_set_sinr, MM_CELL_INFO_NR5G); list = g_list_append (list, g_steal_pointer (&info)); } } #undef CELL_INFO_SET_STR #undef CELL_INFO_SET_HEXSTR #undef CELL_INFO_SET_UINT #undef CELL_INFO_SET_INT_DOUBLE #undef CELL_INFO_SET_UINT_DOUBLE_SCALED ctx->cell_info_list = list; ctx->step++; get_cell_info_step (device, task); } static void check_rfim_query_ready (MbimDevice *device, GAsyncResult *res, GTask *task) { g_autoptr(MbimMessage) response = NULL; MbimIntelRfimFrequencyValueArray *freq_info; guint freq_count; GetCellInfoContext *ctx; MMBroadbandModemMbim *self; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); response = mbim_device_command_finish (device, res, NULL); if (response && mbim_message_response_get_result (response, MBIM_MESSAGE_TYPE_COMMAND_DONE, NULL) && mbim_message_intel_thermal_rf_rfim_response_parse (response, &freq_count, &freq_info, NULL)) { ctx->rfim_info_list = mm_rfim_info_list_from_mbim_intel_rfim_frequency_value_array (freq_info, freq_count, self); mbim_intel_rfim_frequency_value_array_free (freq_info); } else { mm_obj_dbg (self, "Fetching of bandwidth and serving cell type data failed"); } ctx->step++; get_cell_info_step (device, task); } static void get_cell_info_step (MbimDevice *device, GTask *task) { MMBroadbandModemMbim *self; GetCellInfoContext *ctx; g_autoptr(MbimMessage) message = NULL; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); /* Don't run new steps if we're cancelled */ if (g_task_return_error_if_cancelled (task)) { g_object_unref (task); return; } switch (ctx->step) { case GET_CELL_INFO_STEP_FIRST: if (!self->priv->is_base_stations_info_supported) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "base stations info is not supported"); g_object_unref (task); return; } ctx->step++; /* fall through */ case GET_CELL_INFO_STEP_RFIM: { mm_obj_dbg (self, "Obtaining RFIM data..."); message = mbim_message_intel_thermal_rf_rfim_query_new (NULL); mbim_device_command (device, message, 10, NULL, (GAsyncReadyCallback)check_rfim_query_ready, task); return; } case GET_CELL_INFO_STEP_CELL_INFO: { mm_obj_dbg (self, "Obtaining cell info..."); /* Default capacity is 15 */ if (mbim_device_check_ms_mbimex_version (device, 3, 0)) message = mbim_message_ms_basic_connect_extensions_v3_base_stations_info_query_new (15, 15, 15, 15, 15, 15, NULL); else message = mbim_message_ms_basic_connect_extensions_base_stations_info_query_new (15, 15, 15, 15, 15, NULL); mbim_device_command (device, message, 300, NULL, (GAsyncReadyCallback)base_stations_info_query_ready, task); return; } case GET_CELL_INFO_STEP_LAST: if (ctx->saved_error) g_task_return_error (task, g_steal_pointer (&ctx->saved_error)); else if (ctx->cell_info_list) g_task_return_pointer (task, ctx->cell_info_list, (GDestroyNotify)cell_info_list_free); else g_assert_not_reached (); g_object_unref (task); return; default: break; } g_assert_not_reached (); } static void modem_get_cell_info (MMIfaceModem *_self, GAsyncReadyCallback callback, gpointer user_data) { MMBroadbandModemMbim *self = MM_BROADBAND_MODEM_MBIM (_self); MbimDevice *device; GTask *task; GetCellInfoContext *ctx; if (!peek_device (self, &device, callback, user_data)) return; task = g_task_new (self, NULL, callback, user_data); ctx = g_new0 (GetCellInfoContext, 1); ctx->step = GET_CELL_INFO_STEP_FIRST; g_task_set_task_data (task, ctx, (GDestroyNotify)get_cell_info_context_free); get_cell_info_step (device, task); } /*****************************************************************************/ /* Create Bearer (Modem interface) */ static MMBaseBearer * modem_create_bearer_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { return g_task_propagate_pointer (G_TASK (res), error); } static void modem_create_bearer (MMIfaceModem *self, MMBearerProperties *properties, GAsyncReadyCallback callback, gpointer user_data) { MMBaseBearer *bearer; GTask *task; /* Note: the session id to be used by the bearer will always be 0 * for non-multiplexed sessions, bound to the non-VLAN-tagged traffic * managed by the main network interface */ task = g_task_new (self, NULL, callback, user_data); mm_obj_dbg (self, "creating MBIM bearer in MBIM modem"); bearer = mm_bearer_mbim_new (MM_BROADBAND_MODEM_MBIM (self), properties); g_task_return_pointer (task, bearer, g_object_unref); g_object_unref (task); } /*****************************************************************************/ /* Create Bearer List (Modem interface) */ static MMBearerList * modem_create_bearer_list (MMIfaceModem *self) { MMPortMbim *port; guint n; guint n_multiplexed; /* The maximum number of available/connected modems is guessed from * the size of the data ports list. */ n = g_list_length (mm_base_modem_peek_data_ports (MM_BASE_MODEM (self))); mm_obj_dbg (self, "allowed up to %u active bearers", n); /* The maximum number of multiplexed links is defined by the MBIM protocol */ n_multiplexed = (MBIM_DEVICE_SESSION_ID_MAX - MBIM_DEVICE_SESSION_ID_MIN + 1); mm_obj_dbg (self, "allowed up to %u active multiplexed bearers", n_multiplexed); port = mm_broadband_modem_mbim_peek_port_mbim (MM_BROADBAND_MODEM_MBIM (self)); if (port && mm_kernel_device_has_global_property (mm_port_peek_kernel_device (MM_PORT (port)), ID_MM_MAX_MULTIPLEXED_LINKS)) { guint n_multiplexed_limited; n_multiplexed_limited = mm_kernel_device_get_global_property_as_int ( mm_port_peek_kernel_device (MM_PORT (port)), ID_MM_MAX_MULTIPLEXED_LINKS); if (n_multiplexed_limited < n_multiplexed) { n_multiplexed = n_multiplexed_limited; mm_obj_dbg (self, "limited to %u active multiplexed bearers", n_multiplexed); } } /* by default, no multiplexing support */ return mm_bearer_list_new (n, n_multiplexed); } /*****************************************************************************/ /* Create SIM (Modem interface) */ static MMBaseSim * create_sim_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { return mm_sim_mbim_new_finish (res, error); } static void create_sim (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data) { /* New MBIM SIM */ mm_sim_mbim_new (MM_BASE_MODEM (self), NULL, /* cancellable */ callback, user_data); } /*****************************************************************************/ /* Reset data interfaces during initialization */ typedef struct { GList *ports; MMPort *data; MMPortMbim *mbim; } ResetPortsContext; static void reset_ports_context_free (ResetPortsContext *ctx) { g_assert (!ctx->data); g_assert (!ctx->mbim); g_list_free_full (ctx->ports, g_object_unref); g_slice_free (ResetPortsContext, ctx); } static gboolean reset_ports_finish (MMBroadbandModemMbim *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void reset_next_port (GTask *task); static void port_mbim_reset_ready (MMPortMbim *mbim, GAsyncResult *res, GTask *task) { MMBroadbandModemMbim *self; ResetPortsContext *ctx; g_autoptr(GError) error = NULL; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); if (!mm_port_mbim_reset_finish (mbim, res, &error)) mm_obj_warn (self, "couldn't reset MBIM port '%s' with data interface '%s': %s", mm_port_get_device (MM_PORT (ctx->mbim)), mm_port_get_device (ctx->data), error->message); g_clear_object (&ctx->data); g_clear_object (&ctx->mbim); reset_next_port (task); } static void reset_next_port (GTask *task) { MMBroadbandModemMbim *self; ResetPortsContext *ctx; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); if (!ctx->ports) { g_task_return_boolean (task, TRUE); g_object_unref (task); return; } /* steal full data port reference from list head */ ctx->data = ctx->ports->data; ctx->ports = g_list_delete_link (ctx->ports, ctx->ports); ctx->mbim = mm_broadband_modem_mbim_get_port_mbim_for_data (self, ctx->data, NULL); if (!ctx->mbim) { mm_obj_dbg (self, "no MBIM port associated to data port '%s': ignoring data interface reset", mm_port_get_device (ctx->data)); g_clear_object (&ctx->data); reset_next_port (task); return; } mm_obj_dbg (self, "running MBIM port '%s' reset with data interface '%s'", mm_port_get_device (MM_PORT (ctx->mbim)), mm_port_get_device (ctx->data)); mm_port_mbim_reset (ctx->mbim, ctx->data, (GAsyncReadyCallback) port_mbim_reset_ready, task); } static void reset_ports (MMBroadbandModemMbim *self, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; ResetPortsContext *ctx; task = g_task_new (self, NULL, callback, user_data); ctx = g_slice_new0 (ResetPortsContext); g_task_set_task_data (task, ctx, (GDestroyNotify)reset_ports_context_free); ctx->ports = mm_base_modem_find_ports (MM_BASE_MODEM (self), MM_PORT_SUBSYS_UNKNOWN, MM_PORT_TYPE_NET); reset_next_port (task); } /*****************************************************************************/ /* First enabling step */ static gboolean enabling_started_finish (MMBroadbandModem *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void parent_enabling_started_ready (MMBroadbandModem *self, GAsyncResult *res, GTask *task) { GError *error = NULL; if (!MM_BROADBAND_MODEM_CLASS (mm_broadband_modem_mbim_parent_class)->enabling_started_finish ( self, res, &error)) { /* Don't treat this as fatal. Parent enabling may fail if it cannot grab a primary * AT port, which isn't really an issue in MBIM-based modems */ mm_obj_dbg (self, "couldn't start parent enabling: %s", error->message); g_error_free (error); } g_task_return_boolean (task, TRUE); g_object_unref (task); } static void enabling_started (MMBroadbandModem *self, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; task = g_task_new (self, NULL, callback, user_data); MM_BROADBAND_MODEM_CLASS (mm_broadband_modem_mbim_parent_class)->enabling_started ( self, (GAsyncReadyCallback)parent_enabling_started_ready, task); } /*****************************************************************************/ /* First initialization step */ typedef struct { MMPortMbim *mbim; #if defined WITH_QMI && QMI_MBIM_QMUX_SUPPORTED guint qmi_service_index; #endif } InitializationStartedContext; static void initialization_started_context_free (InitializationStartedContext *ctx) { if (ctx->mbim) g_object_unref (ctx->mbim); g_slice_free (InitializationStartedContext, ctx); } static gpointer initialization_started_finish (MMBroadbandModem *self, GAsyncResult *res, GError **error) { return g_task_propagate_pointer (G_TASK (res), error); } static void parent_initialization_started_ready (MMBroadbandModem *self, GAsyncResult *res, GTask *task) { gpointer parent_ctx; GError *error = NULL; parent_ctx = MM_BROADBAND_MODEM_CLASS (mm_broadband_modem_mbim_parent_class)->initialization_started_finish ( self, res, &error); if (error) { /* Don't treat this as fatal. Parent initialization may fail if it cannot grab a primary * AT port, which isn't really an issue in MBIM-based modems */ mm_obj_dbg (self, "couldn't start parent initialization: %s", error->message); g_error_free (error); } /* Just parent's pointer passed here */ g_task_return_pointer (task, parent_ctx, NULL); g_object_unref (task); } static void parent_initialization_started (GTask *task) { MMBroadbandModem *self; self = g_task_get_source_object (task); MM_BROADBAND_MODEM_CLASS (mm_broadband_modem_mbim_parent_class)->initialization_started ( self, (GAsyncReadyCallback)parent_initialization_started_ready, task); } #if defined WITH_QMI && QMI_MBIM_QMUX_SUPPORTED static const QmiService qmi_services[] = { QMI_SERVICE_DMS, QMI_SERVICE_NAS, QMI_SERVICE_PDS, QMI_SERVICE_LOC, QMI_SERVICE_PDC, QMI_SERVICE_UIM, }; static void allocate_next_qmi_client (GTask *task); static void mbim_port_allocate_qmi_client_ready (MMPortMbim *mbim, GAsyncResult *res, GTask *task) { MMBroadbandModemMbim *self; InitializationStartedContext *ctx; GError *error = NULL; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); if (!mm_port_mbim_allocate_qmi_client_finish (mbim, res, &error)) { mm_obj_dbg (self, "couldn't allocate QMI client for service '%s': %s", qmi_service_get_string (qmi_services[ctx->qmi_service_index]), error->message); g_error_free (error); } ctx->qmi_service_index++; allocate_next_qmi_client (task); } static void allocate_next_qmi_client (GTask *task) { InitializationStartedContext *ctx; ctx = g_task_get_task_data (task); if (ctx->qmi_service_index == G_N_ELEMENTS (qmi_services)) { parent_initialization_started (task); return; } /* Otherwise, allocate next client */ mm_port_mbim_allocate_qmi_client (ctx->mbim, qmi_services[ctx->qmi_service_index], NULL, (GAsyncReadyCallback)mbim_port_allocate_qmi_client_ready, task); } #endif static void query_device_services_ready (MbimDevice *device, GAsyncResult *res, GTask *task) { MMBroadbandModemMbim *self; MbimMessage *response; GError *error = NULL; MbimDeviceServiceElement **device_services; guint32 device_services_count; self = g_task_get_source_object (task); response = mbim_device_command_finish (device, res, &error); if (response && mbim_message_response_get_result (response, MBIM_MESSAGE_TYPE_COMMAND_DONE, &error) && mbim_message_device_services_response_parse ( response, &device_services_count, NULL, /* max_dss_sessions */ &device_services, &error)) { guint32 i; for (i = 0; i < device_services_count; i++) { MbimService service; guint32 j; service = mbim_uuid_to_service (&device_services[i]->device_service_id); if (service == MBIM_SERVICE_BASIC_CONNECT) { for (j = 0; j < device_services[i]->cids_count; j++) { if (device_services[i]->cids[j] == MBIM_CID_BASIC_CONNECT_PROVISIONED_CONTEXTS) { mm_obj_dbg (self, "Profile management is supported"); self->priv->is_profile_management_supported = TRUE; } } continue; } if (service == MBIM_SERVICE_MS_BASIC_CONNECT_EXTENSIONS) { for (j = 0; j < device_services[i]->cids_count; j++) { if (device_services[i]->cids[j] == MBIM_CID_MS_BASIC_CONNECT_EXTENSIONS_PCO) { mm_obj_dbg (self, "PCO is supported"); self->priv->is_pco_supported = TRUE; } else if (device_services[i]->cids[j] == MBIM_CID_MS_BASIC_CONNECT_EXTENSIONS_LTE_ATTACH_INFO) { mm_obj_dbg (self, "LTE attach info is supported"); self->priv->is_lte_attach_info_supported = TRUE; } else if (device_services[i]->cids[j] == MBIM_CID_MS_BASIC_CONNECT_EXTENSIONS_SLOT_INFO_STATUS) { mm_obj_dbg (self, "Slot info status is supported"); self->priv->is_slot_info_status_supported = TRUE; } else if (device_services[i]->cids[j] == MBIM_CID_MS_BASIC_CONNECT_EXTENSIONS_REGISTRATION_PARAMETERS) { mm_obj_dbg (self, "5GNR registration settings are supported"); self->priv->is_nr5g_registration_settings_supported = TRUE; } else if (device_services[i]->cids[j] == MBIM_CID_MS_BASIC_CONNECT_EXTENSIONS_BASE_STATIONS_INFO) { mm_obj_dbg (self, "Base stations info is supported"); self->priv->is_base_stations_info_supported = TRUE; } else if (device_services[i]->cids[j] == MBIM_CID_MS_BASIC_CONNECT_EXTENSIONS_PROVISIONED_CONTEXTS) { mm_obj_dbg (self, "Context types extension is supported"); self->priv->is_context_type_ext_supported = TRUE; if (mm_context_get_test_mbimex_profile_management ()) { mm_obj_dbg (self, "Profile management extension is supported"); self->priv->is_profile_management_ext_supported = TRUE; } else mm_obj_dbg (self, "Profile management extension is supported but not allowed"); } } continue; } if (service == MBIM_SERVICE_USSD) { for (j = 0; j < device_services[i]->cids_count; j++) { if (device_services[i]->cids[j] == MBIM_CID_USSD) { mm_obj_dbg (self, "USSD is supported"); self->priv->is_ussd_supported = TRUE; break; } } continue; } if (service == MBIM_SERVICE_ATDS) { for (j = 0; j < device_services[i]->cids_count; j++) { if (device_services[i]->cids[j] == MBIM_CID_ATDS_LOCATION) { mm_obj_dbg (self, "ATDS location is supported"); self->priv->is_atds_location_supported = TRUE; } else if (device_services[i]->cids[j] == MBIM_CID_ATDS_SIGNAL) { mm_obj_dbg (self, "ATDS signal is supported"); self->priv->is_atds_signal_supported = TRUE; } } continue; } if (service == MBIM_SERVICE_INTEL_FIRMWARE_UPDATE) { if (self->priv->intel_firmware_update_unsupported) { mm_obj_dbg (self, "Intel firmware update service is explicitly ignored"); continue; } for (j = 0; j < device_services[i]->cids_count; j++) { if (device_services[i]->cids[j] == MBIM_CID_INTEL_FIRMWARE_UPDATE_MODEM_REBOOT) { mm_obj_dbg (self, "Intel reset is supported"); self->priv->is_intel_reset_supported = TRUE; } } continue; } if (service == MBIM_SERVICE_MS_SAR) { for (j = 0; j < device_services[i]->cids_count; j++) { if (device_services[i]->cids[j] == MBIM_CID_MS_SAR_CONFIG) { mm_obj_dbg (self, "SAR is supported"); self->priv->is_ms_sar_supported = TRUE; } } continue; } if (service == MBIM_SERVICE_GOOGLE) { for (j = 0; j < device_services[i]->cids_count; j++) { if (device_services[i]->cids[j] == MBIM_CID_GOOGLE_CARRIER_LOCK) { mm_obj_dbg (self, "Google carrier lock is supported"); self->priv->is_google_carrier_lock_supported = TRUE; } } continue; } /* no optional features to check in remaining services */ } mbim_device_service_element_array_free (device_services); } else { /* Ignore error */ mm_obj_warn (self, "couldn't query device services: %s", error->message); g_error_free (error); } if (response) mbim_message_unref (response); #if defined WITH_QMI && QMI_MBIM_QMUX_SUPPORTED allocate_next_qmi_client (task); #else parent_initialization_started (task); #endif } static void query_device_services (GTask *task) { MMBroadbandModem *self; InitializationStartedContext *ctx; MbimMessage *message; MbimDevice *device; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); device = mm_port_mbim_peek_device (ctx->mbim); g_assert (device); mm_obj_dbg (self, "querying device services..."); message = mbim_message_device_services_query_new (NULL); mbim_device_command (device, message, 10, NULL, (GAsyncReadyCallback)query_device_services_ready, task); mbim_message_unref (message); } static void mbim_port_open_ready (MMPortMbim *mbim, GAsyncResult *res, GTask *task) { GError *error = NULL; if (!mm_port_mbim_open_finish (mbim, res, &error)) { g_task_return_error (task, error); g_object_unref (task); return; } query_device_services (task); } static void initialization_open_port (GTask *task) { InitializationStartedContext *ctx; #if defined WITH_QMI && QMI_MBIM_QMUX_SUPPORTED MMBroadbandModemMbim *self; gboolean qmi_unsupported = FALSE; #endif ctx = g_task_get_task_data (task); #if defined WITH_QMI && QMI_MBIM_QMUX_SUPPORTED self = g_task_get_source_object (task); g_object_get (self, MM_BROADBAND_MODEM_MBIM_QMI_UNSUPPORTED, &qmi_unsupported, NULL); #endif /* Now open our MBIM port */ mm_port_mbim_open (ctx->mbim, #if defined WITH_QMI && QMI_MBIM_QMUX_SUPPORTED ! qmi_unsupported, /* With QMI over MBIM support if available */ #endif NULL, (GAsyncReadyCallback)mbim_port_open_ready, task); } static void reset_ports_ready (MMBroadbandModemMbim *self, GAsyncResult *res, GTask *task) { GError *error = NULL; if (!reset_ports_finish (self, res, &error)) { g_task_return_error (task, error); g_object_unref (task); return; } initialization_open_port (task); } static void initialization_reset_ports (GTask *task) { MMBroadbandModemMbim *self; self = g_task_get_source_object (task); /* reseting the data interfaces is really only needed if the device * hasn't been hotplugged */ if (mm_base_modem_get_hotplugged (MM_BASE_MODEM (self))) { mm_obj_dbg (self, "not running data interface reset procedure: device is hotplugged"); initialization_open_port (task); return; } reset_ports (self, (GAsyncReadyCallback)reset_ports_ready, task); } static void initialization_started (MMBroadbandModem *self, GAsyncReadyCallback callback, gpointer user_data) { InitializationStartedContext *ctx; GTask *task; ctx = g_slice_new0 (InitializationStartedContext); ctx->mbim = mm_broadband_modem_mbim_get_port_mbim (MM_BROADBAND_MODEM_MBIM (self)); task = g_task_new (self, NULL, callback, user_data); g_task_set_task_data (task, ctx, (GDestroyNotify)initialization_started_context_free); /* This may happen if we unplug the modem unexpectedly */ if (!ctx->mbim) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Cannot initialize: MBIM port went missing"); g_object_unref (task); return; } if (mm_port_mbim_is_open (ctx->mbim)) { /* Nothing to be done, just connect to a signal and launch parent's * callback */ query_device_services (task); return; } initialization_reset_ports (task); } /*****************************************************************************/ /* IMEI loading (3GPP interface) */ static gchar * modem_3gpp_load_imei_finish (MMIfaceModem3gpp *self, GAsyncResult *res, GError **error) { return g_task_propagate_pointer (G_TASK (res), error); } static void modem_3gpp_load_imei (MMIfaceModem3gpp *_self, GAsyncReadyCallback callback, gpointer user_data) { MMBroadbandModemMbim *self = MM_BROADBAND_MODEM_MBIM (_self); GTask *task; task = g_task_new (self, NULL, callback, user_data); if (self->priv->caps_device_id) g_task_return_pointer (task, g_strdup (self->priv->caps_device_id), g_free); else g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Device doesn't report a valid IMEI"); g_object_unref (task); } /*****************************************************************************/ /* Facility locks status loading (3GPP interface) */ typedef struct { MMModem3gppFacility facilities; } LoadEnabledFacilityLocksContext; static MMModem3gppFacility modem_3gpp_load_enabled_facility_locks_finish (MMIfaceModem3gpp *self, GAsyncResult *res, GError **error) { GError *inner_error = NULL; gssize value; value = g_task_propagate_int (G_TASK (res), &inner_error); if (inner_error) { g_propagate_error (error, inner_error); return MM_MODEM_3GPP_FACILITY_NONE; } return (MMModem3gppFacility)value; } static void pin_list_query_ready (MbimDevice *device, GAsyncResult *res, GTask *task) { LoadEnabledFacilityLocksContext *ctx; MbimMessage *response; GError *error = NULL; MbimPinDesc *pin_desc_pin1; MbimPinDesc *pin_desc_pin2; MbimPinDesc *pin_desc_device_sim_pin; MbimPinDesc *pin_desc_device_first_sim_pin; MbimPinDesc *pin_desc_network_pin; MbimPinDesc *pin_desc_network_subset_pin; MbimPinDesc *pin_desc_service_provider_pin; MbimPinDesc *pin_desc_corporate_pin; ctx = g_task_get_task_data (task); response = mbim_device_command_finish (device, res, &error); if (response && mbim_message_response_get_result (response, MBIM_MESSAGE_TYPE_COMMAND_DONE, &error) && mbim_message_pin_list_response_parse ( response, &pin_desc_pin1, &pin_desc_pin2, &pin_desc_device_sim_pin, &pin_desc_device_first_sim_pin, &pin_desc_network_pin, &pin_desc_network_subset_pin, &pin_desc_service_provider_pin, &pin_desc_corporate_pin, NULL, /* pin_desc_subsidy_lock */ NULL, /* pin_desc_custom */ &error)) { MMModem3gppFacility mask = ctx->facilities; if (pin_desc_pin1->pin_mode == MBIM_PIN_MODE_ENABLED) mask |= MM_MODEM_3GPP_FACILITY_SIM; mbim_pin_desc_free (pin_desc_pin1); if (pin_desc_pin2->pin_mode == MBIM_PIN_MODE_ENABLED) mask |= MM_MODEM_3GPP_FACILITY_FIXED_DIALING; mbim_pin_desc_free (pin_desc_pin2); if (pin_desc_device_sim_pin->pin_mode == MBIM_PIN_MODE_ENABLED) mask |= MM_MODEM_3GPP_FACILITY_PH_SIM; mbim_pin_desc_free (pin_desc_device_sim_pin); if (pin_desc_device_first_sim_pin->pin_mode == MBIM_PIN_MODE_ENABLED) mask |= MM_MODEM_3GPP_FACILITY_PH_FSIM; mbim_pin_desc_free (pin_desc_device_first_sim_pin); if (pin_desc_network_pin->pin_mode == MBIM_PIN_MODE_ENABLED) mask |= MM_MODEM_3GPP_FACILITY_NET_PERS; mbim_pin_desc_free (pin_desc_network_pin); if (pin_desc_network_subset_pin->pin_mode == MBIM_PIN_MODE_ENABLED) mask |= MM_MODEM_3GPP_FACILITY_NET_SUB_PERS; mbim_pin_desc_free (pin_desc_network_subset_pin); if (pin_desc_service_provider_pin->pin_mode == MBIM_PIN_MODE_ENABLED) mask |= MM_MODEM_3GPP_FACILITY_PROVIDER_PERS; mbim_pin_desc_free (pin_desc_service_provider_pin); if (pin_desc_corporate_pin->pin_mode == MBIM_PIN_MODE_ENABLED) mask |= MM_MODEM_3GPP_FACILITY_CORP_PERS; mbim_pin_desc_free (pin_desc_corporate_pin); g_task_return_int (task, mask); } else g_task_return_error (task, error); g_object_unref (task); if (response) mbim_message_unref (response); } static void load_enabled_facility_pin_query_ready (MbimDevice *device, GAsyncResult *res, GTask *task) { LoadEnabledFacilityLocksContext *ctx; MbimMessage *response; MbimMessage *message; MbimPinType pin_type; MbimPinState pin_state; MMBroadbandModemMbim *self; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); response = mbim_device_command_finish (device, res, NULL); if (response) { if (mbim_message_response_get_result (response, MBIM_MESSAGE_TYPE_COMMAND_DONE, NULL) && mbim_message_pin_response_parse (response, &pin_type, &pin_state, NULL, NULL)) { if (pin_state == MBIM_PIN_STATE_LOCKED) ctx->facilities |= mm_modem_3gpp_facility_from_mbim_pin_type (pin_type); self->priv->enabled_cache.last_pin_type = pin_type; } mbim_message_unref (response); } message = mbim_message_pin_list_query_new (NULL); mbim_device_command (device, message, 10, NULL, (GAsyncReadyCallback)pin_list_query_ready, task); mbim_message_unref (message); } static void modem_3gpp_load_enabled_facility_locks (MMIfaceModem3gpp *self, GAsyncReadyCallback callback, gpointer user_data) { LoadEnabledFacilityLocksContext *ctx; MbimDevice *device; MbimMessage *message; GTask *task; if (!peek_device (self, &device, callback, user_data)) return; task = g_task_new (self, NULL, callback, user_data); ctx = g_new0 (LoadEnabledFacilityLocksContext, 1); g_task_set_task_data (task, ctx, g_free); /* The PIN LIST command returns status of pin locks but omits PUK locked * facilities. To workaround this limitation additional PIN command query * was added to get currently active PIN or PUK lock. */ message = mbim_message_pin_query_new (NULL); mbim_device_command (device, message, 10, NULL, (GAsyncReadyCallback)load_enabled_facility_pin_query_ready, task); mbim_message_unref (message); } /*****************************************************************************/ /* Facility locks disabling (3GPP interface) */ typedef struct _DisableFacilityLockContext DisableFacilityLockContext; struct _DisableFacilityLockContext { MbimPinType pin_type; }; static gboolean modem_3gpp_disable_facility_lock_finish (MMIfaceModem3gpp *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void disable_facility_lock_ready (MbimDevice *device, GAsyncResult *res, GTask *task) { DisableFacilityLockContext *ctx; MbimMessage *response = NULL; guint32 remaining_attempts; MbimPinState pin_state; MbimPinType pin_type; GError *error = NULL; ctx = g_task_get_task_data (task); response = mbim_device_command_finish (device, res, &error); if (!response || !mbim_message_response_get_result (response, MBIM_MESSAGE_TYPE_COMMAND_DONE, &error)) { g_task_return_error (task, error); } else if (!mbim_message_pin_response_parse (response, &pin_type, &pin_state, &remaining_attempts, &error)) { g_task_return_error (task, error); } else if (pin_type == ctx->pin_type && pin_state == MBIM_PIN_STATE_LOCKED) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Disabling PIN lock %s failed, remaining attempts: %u", mbim_pin_state_get_string (pin_state), remaining_attempts); } else g_task_return_boolean (task, TRUE); if (response) mbim_message_unref (response); g_object_unref (task); } static void modem_3gpp_disable_facility_lock (MMIfaceModem3gpp *self, MMModem3gppFacility facility, guint8 slot, const gchar *key, GAsyncReadyCallback callback, gpointer user_data) { DisableFacilityLockContext *ctx; MbimMessage *message; MbimPinType pin_type; MbimDevice *device; GTask *task; if (!peek_device (self, &device, callback, user_data)) return; task = g_task_new (self, NULL, callback, user_data); /* Set type of pin lock to disable */ pin_type = mbim_pin_type_from_mm_modem_3gpp_facility (facility); if (pin_type == MBIM_PIN_TYPE_UNKNOWN) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS, "Not supported type of facility lock."); g_object_unref (task); return; } mm_obj_dbg (self, "Trying to disable %s lock using key: %s", mbim_pin_type_get_string (pin_type), key); ctx = g_new0 (DisableFacilityLockContext, 1); ctx->pin_type = pin_type; g_task_set_task_data (task, ctx, g_free); message = mbim_message_pin_set_new (pin_type, MBIM_PIN_OPERATION_DISABLE, key, NULL, NULL); mbim_device_command (device, message, 10, NULL, (GAsyncReadyCallback)disable_facility_lock_ready, task); mbim_message_unref (message); } /*****************************************************************************/ /* Initial EPS bearer info loading */ static MMBearerProperties * modem_3gpp_load_initial_eps_bearer_finish (MMIfaceModem3gpp *self, GAsyncResult *res, GError **error) { return MM_BEARER_PROPERTIES (g_task_propagate_pointer (G_TASK (res), error)); } static MMBearerProperties * common_process_lte_attach_info (MMBroadbandModemMbim *self, guint32 lte_attach_state, guint32 ip_type, const gchar *access_string, const gchar *user_name, const gchar *password, guint32 compression, guint32 auth_protocol, GError **error) { MMBearerProperties *properties; MMBearerIpFamily ip_family; MMBearerAllowedAuth auth; /* Remove LTE attach bearer info */ if (lte_attach_state == MBIM_LTE_ATTACH_STATE_DETACHED) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_WRONG_STATE, "Not attached to LTE"); return NULL; } properties = mm_bearer_properties_new (); if (access_string) mm_bearer_properties_set_apn (properties, access_string); if (user_name) mm_bearer_properties_set_user (properties, user_name); if (password) mm_bearer_properties_set_password (properties, password); ip_family = mm_bearer_ip_family_from_mbim_context_ip_type (ip_type); if (ip_family != MM_BEARER_IP_FAMILY_NONE) mm_bearer_properties_set_ip_type (properties, ip_family); auth = mm_bearer_allowed_auth_from_mbim_auth_protocol (auth_protocol); if (auth != MM_BEARER_ALLOWED_AUTH_UNKNOWN) mm_bearer_properties_set_allowed_auth (properties, auth); /* note: we don't expose compression settings */ return properties; } static void lte_attach_info_query_ready (MbimDevice *device, GAsyncResult *res, GTask *task) { MMBroadbandModemMbim *self; g_autoptr(MbimMessage) response = NULL; GError *error = NULL; MMBearerProperties *properties; guint32 lte_attach_state; guint32 ip_type; g_autofree gchar *access_string = NULL; g_autofree gchar *user_name = NULL; g_autofree gchar *password = NULL; guint32 compression; guint32 auth_protocol; MbimNwError nw_error = 0; self = g_task_get_source_object (task); response = mbim_device_command_finish (device, res, &error); if (!response || !mbim_message_response_get_result (response, MBIM_MESSAGE_TYPE_COMMAND_DONE, &error)) { g_task_return_error (task, error); g_object_unref (task); return; } if (mbim_device_check_ms_mbimex_version (device, 3, 0)) { if (!mbim_message_ms_basic_connect_extensions_v3_lte_attach_info_response_parse ( response, <e_attach_state, &nw_error, &ip_type, &access_string, &user_name, &password, &compression, &auth_protocol, &error)) g_prefix_error (&error, "Failed processing MBIMEx v3.0 LTE attach info response: "); else mm_obj_dbg (self, "processed MBIMEx v3.0 LTE attach info response"); } else { if (!mbim_message_ms_basic_connect_extensions_lte_attach_info_response_parse ( response, <e_attach_state, &ip_type, &access_string, &user_name, &password, &compression, &auth_protocol, &error)) g_prefix_error (&error, "Failed processing LTE attach info response: "); else mm_obj_dbg (self, "processed LTE attach info response"); } properties = common_process_lte_attach_info (self, lte_attach_state, ip_type, access_string, user_name, password, compression, auth_protocol, &error); g_assert (properties || error); if (!error) { /* If network error is reported, then log it */ if (nw_error) { const gchar *nw_error_str; nw_error = mm_broadband_modem_mbim_normalize_nw_error (self, nw_error); nw_error_str = mbim_nw_error_get_string (nw_error); if (nw_error_str) mm_obj_dbg (self, "LTE attach info network error reported: %s", nw_error_str); else mm_obj_dbg (self, "LTE attach info network error reported: 0x%x", nw_error); } g_task_return_pointer (task, properties, g_object_unref); } else { g_task_return_error (task, error); } g_object_unref (task); } static void modem_3gpp_load_initial_eps_bearer (MMIfaceModem3gpp *_self, GAsyncReadyCallback callback, gpointer user_data) { MMBroadbandModemMbim *self = MM_BROADBAND_MODEM_MBIM (_self); MbimDevice *device; MbimMessage *message; GTask *task; if (!peek_device (self, &device, callback, user_data)) return; task = g_task_new (self, NULL, callback, user_data); if (!self->priv->is_lte_attach_info_supported) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "LTE attach status is unsupported"); g_object_unref (task); return; } message = mbim_message_ms_basic_connect_extensions_lte_attach_info_query_new (NULL); mbim_device_command (device, message, 10, NULL, (GAsyncReadyCallback)lte_attach_info_query_ready, task); mbim_message_unref (message); } /*****************************************************************************/ /* Initial EPS bearer settings loading */ static MMBearerProperties * modem_3gpp_load_initial_eps_bearer_settings_finish (MMIfaceModem3gpp *self, GAsyncResult *res, GError **error) { return MM_BEARER_PROPERTIES (g_task_propagate_pointer (G_TASK (res), error)); } static MMBearerProperties * common_process_lte_attach_configuration (MMBroadbandModemMbim *self, MbimLteAttachConfiguration *config, GError **error) { MMBearerProperties *properties; MMBearerIpFamily ip_family = MM_BEARER_IP_FAMILY_NONE; MMBearerAllowedAuth auth; properties = mm_bearer_properties_new (); if (config->access_string) mm_bearer_properties_set_apn (properties, config->access_string); if (config->user_name) mm_bearer_properties_set_user (properties, config->user_name); if (config->password) mm_bearer_properties_set_password (properties, config->password); ip_family = mm_bearer_ip_family_from_mbim_context_ip_type (config->ip_type); if (ip_family != MM_BEARER_IP_FAMILY_NONE) mm_bearer_properties_set_ip_type (properties, ip_family); auth = mm_bearer_allowed_auth_from_mbim_auth_protocol (config->auth_protocol); if (auth != MM_BEARER_ALLOWED_AUTH_UNKNOWN) mm_bearer_properties_set_allowed_auth (properties, auth); /* note: we don't expose compression settings or the configuration source details */ return properties; } static void lte_attach_configuration_query_ready (MbimDevice *device, GAsyncResult *res, GTask *task) { MMBroadbandModemMbim *self; MbimMessage *response; GError *error = NULL; MMBearerProperties *properties = NULL; guint32 n_configurations = 0; MbimLteAttachConfiguration **configurations = NULL; guint i; self = g_task_get_source_object (task); response = mbim_device_command_finish (device, res, &error); if (!response || !mbim_message_response_get_result (response, MBIM_MESSAGE_TYPE_COMMAND_DONE, &error) || !mbim_message_ms_basic_connect_extensions_lte_attach_configuration_response_parse ( response, &n_configurations, &configurations, &error)) { g_task_return_error (task, error); g_object_unref (task); goto out; } /* We should always receive 3 configurations but the MBIM API doesn't force * that so we'll just assume we don't get always the same fixed number */ for (i = 0; i < n_configurations; i++) { /* We only support configuring the HOME settings */ if (configurations[i]->roaming != MBIM_LTE_ATTACH_CONTEXT_ROAMING_CONTROL_HOME) continue; properties = common_process_lte_attach_configuration (self, configurations[i], &error); break; } mbim_lte_attach_configuration_array_free (configurations); if (!properties && !error) error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_NOT_FOUND, "Couldn't find home network LTE attach settings"); g_assert (properties || error); if (properties) g_task_return_pointer (task, properties, g_object_unref); else g_task_return_error (task, error); g_object_unref (task); out: if (response) mbim_message_unref (response); } static void modem_3gpp_load_initial_eps_bearer_settings (MMIfaceModem3gpp *_self, GAsyncReadyCallback callback, gpointer user_data) { MMBroadbandModemMbim *self = MM_BROADBAND_MODEM_MBIM (_self); MbimDevice *device; MbimMessage *message; GTask *task; if (!peek_device (self, &device, callback, user_data)) return; task = g_task_new (self, NULL, callback, user_data); if (!self->priv->is_lte_attach_info_supported) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "LTE attach status info is unsupported"); g_object_unref (task); return; } message = mbim_message_ms_basic_connect_extensions_lte_attach_configuration_query_new (NULL); mbim_device_command (device, message, 10, NULL, (GAsyncReadyCallback)lte_attach_configuration_query_ready, task); mbim_message_unref (message); } /*****************************************************************************/ /* Set initial EPS bearer settings * * The logic to set the EPS bearer settings requires us to first load the current * settings from the module, because we are only going to change the settings * associated to the HOME slot, we will leave untouched the PARTNER and NON-PARTNER * slots. */ static gboolean modem_3gpp_set_initial_eps_bearer_settings_finish (MMIfaceModem3gpp *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void set_lte_attach_configuration_set_ready (MbimDevice *device, GAsyncResult *res, GTask *task) { MbimMessage *response; GError *error = NULL; response = mbim_device_command_finish (device, res, &error); if (!response || !mbim_message_response_get_result (response, MBIM_MESSAGE_TYPE_COMMAND_DONE, &error)) g_task_return_error (task, error); else g_task_return_boolean (task, TRUE); g_object_unref (task); if (response) mbim_message_unref (response); } static void before_set_lte_attach_configuration_query_ready (MbimDevice *device, GAsyncResult *res, GTask *task) { MMBroadbandModemMbim *self; MbimMessage *request; MbimMessage *response; GError *error = NULL; MMBearerProperties *config; guint32 n_configurations = 0; MbimLteAttachConfiguration **configurations = NULL; guint i; self = g_task_get_source_object (task); config = g_task_get_task_data (task); response = mbim_device_command_finish (device, res, &error); if (!response || !mbim_message_response_get_result (response, MBIM_MESSAGE_TYPE_COMMAND_DONE, &error) || !mbim_message_ms_basic_connect_extensions_lte_attach_configuration_response_parse ( response, &n_configurations, &configurations, &error)) { g_task_return_error (task, error); g_object_unref (task); goto out; } /* We should always receive 3 configurations but the MBIM API doesn't force * that so we'll just assume we don't get always the same fixed number */ for (i = 0; i < n_configurations; i++) { MMBearerIpFamily ip_family; MMBearerAllowedAuth auth; /* We only support configuring the HOME settings */ if (configurations[i]->roaming != MBIM_LTE_ATTACH_CONTEXT_ROAMING_CONTROL_HOME) continue; ip_family = mm_bearer_properties_get_ip_type (config); if (ip_family == MM_BEARER_IP_FAMILY_NONE || ip_family == MM_BEARER_IP_FAMILY_ANY) configurations[i]->ip_type = MBIM_CONTEXT_IP_TYPE_DEFAULT; else { configurations[i]->ip_type = mm_bearer_ip_family_to_mbim_context_ip_type (ip_family, &error); if (error) { configurations[i]->ip_type = MBIM_CONTEXT_IP_TYPE_DEFAULT; mm_obj_warn (self, "unexpected IP type settings requested: %s", error->message); g_clear_error (&error); } } g_clear_pointer (&(configurations[i]->access_string), g_free); configurations[i]->access_string = g_strdup (mm_bearer_properties_get_apn (config)); g_clear_pointer (&(configurations[i]->user_name), g_free); configurations[i]->user_name = g_strdup (mm_bearer_properties_get_user (config)); g_clear_pointer (&(configurations[i]->password), g_free); configurations[i]->password = g_strdup (mm_bearer_properties_get_password (config)); auth = mm_bearer_properties_get_allowed_auth (config); if ((auth != MM_BEARER_ALLOWED_AUTH_UNKNOWN) || configurations[i]->user_name || configurations[i]->password) { configurations[i]->auth_protocol = mm_bearer_allowed_auth_to_mbim_auth_protocol (auth, self, &error); if (error) { configurations[i]->auth_protocol = MBIM_AUTH_PROTOCOL_NONE; mm_obj_warn (self, "unexpected auth settings requested: %s", error->message); g_clear_error (&error); } } else { configurations[i]->auth_protocol = MBIM_AUTH_PROTOCOL_NONE; } configurations[i]->source = MBIM_CONTEXT_SOURCE_USER; configurations[i]->compression = MBIM_COMPRESSION_NONE; break; } request = mbim_message_ms_basic_connect_extensions_lte_attach_configuration_set_new ( MBIM_LTE_ATTACH_CONTEXT_OPERATION_DEFAULT, n_configurations, (const MbimLteAttachConfiguration *const *)configurations, &error); if (!request) { g_task_return_error (task, error); g_object_unref (task); goto out; } mbim_device_command (device, request, 10, NULL, (GAsyncReadyCallback)set_lte_attach_configuration_set_ready, task); mbim_message_unref (request); out: if (configurations) mbim_lte_attach_configuration_array_free (configurations); if (response) mbim_message_unref (response); } static void modem_3gpp_set_initial_eps_bearer_settings (MMIfaceModem3gpp *_self, MMBearerProperties *config, GAsyncReadyCallback callback, gpointer user_data) { MMBroadbandModemMbim *self = MM_BROADBAND_MODEM_MBIM (_self); GTask *task; MbimDevice *device; MbimMessage *message; if (!peek_device (self, &device, callback, user_data)) return; task = g_task_new (self, NULL, callback, user_data); if (!self->priv->is_lte_attach_info_supported) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "LTE attach configuration is unsupported"); g_object_unref (task); return; } g_task_set_task_data (task, g_object_ref (config), g_object_unref); message = mbim_message_ms_basic_connect_extensions_lte_attach_configuration_query_new (NULL); mbim_device_command (device, message, 10, NULL, (GAsyncReadyCallback)before_set_lte_attach_configuration_query_ready, task); mbim_message_unref (message); } /*****************************************************************************/ /* 5GNR registration settings loading */ static MMNr5gRegistrationSettings * modem_3gpp_load_nr5g_registration_settings_finish (MMIfaceModem3gpp *self, GAsyncResult *res, GError **error) { return g_task_propagate_pointer (G_TASK (res), error); } static void registration_parameters_query_ready (MbimDevice *device, GAsyncResult *res, GTask *task) { GError *error = NULL; MMNr5gRegistrationSettings *settings; MbimMicoMode mico_mode = MBIM_MICO_MODE_DEFAULT; MbimDrxCycle drx_cycle = MBIM_DRX_CYCLE_NOT_SPECIFIED; g_autoptr(MbimMessage) response = NULL; response = mbim_device_command_finish (device, res, &error); if (!response || !mbim_message_response_get_result (response, MBIM_MESSAGE_TYPE_COMMAND_DONE, &error) || !mbim_message_ms_basic_connect_extensions_v3_registration_parameters_response_parse ( response, &mico_mode, &drx_cycle, NULL, /* ladn info */ NULL, /* default pdu activation hint */ NULL, /* reregister if needed */ NULL, /* unnamed ies */ &error)) { g_task_return_error (task, error); g_object_unref (task); return; } settings = mm_nr5g_registration_settings_new (); mm_nr5g_registration_settings_set_mico_mode (settings, mm_modem_3gpp_mico_mode_from_mbim_mico_mode (mico_mode)); mm_nr5g_registration_settings_set_drx_cycle (settings, mm_modem_3gpp_drx_cycle_from_mbim_drx_cycle (drx_cycle)); g_task_return_pointer (task, settings, (GDestroyNotify) g_object_unref); g_object_unref (task); } static void modem_3gpp_load_nr5g_registration_settings (MMIfaceModem3gpp *_self, GAsyncReadyCallback callback, gpointer user_data) { MMBroadbandModemMbim *self = MM_BROADBAND_MODEM_MBIM (_self); GTask *task; MbimDevice *device; g_autoptr(MbimMessage) message = NULL; if (!peek_device (self, &device, callback, user_data)) return; task = g_task_new (self, NULL, callback, user_data); if (!self->priv->is_nr5g_registration_settings_supported) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "5GNR registration settings are unsupported"); g_object_unref (task); return; } message = mbim_message_ms_basic_connect_extensions_v3_registration_parameters_query_new (NULL); mbim_device_command (device, message, 10, NULL, (GAsyncReadyCallback)registration_parameters_query_ready, task); } /*****************************************************************************/ /* Set 5GNR registration settings */ static gboolean modem_3gpp_set_nr5g_registration_settings_finish (MMIfaceModem3gpp *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void set_nr5g_registration_settings_ready (MbimDevice *device, GAsyncResult *res, GTask *task) { g_autoptr(MbimMessage) response = NULL; GError *error = NULL; response = mbim_device_command_finish (device, res, &error); if (!response || !mbim_message_response_get_result (response, MBIM_MESSAGE_TYPE_COMMAND_DONE, &error)) g_task_return_error (task, error); else g_task_return_boolean (task, TRUE); g_object_unref (task); } static void modem_3gpp_set_nr5g_registration_settings (MMIfaceModem3gpp *_self, MMNr5gRegistrationSettings *settings, GAsyncReadyCallback callback, gpointer user_data) { MMBroadbandModemMbim *self = MM_BROADBAND_MODEM_MBIM (_self); GTask *task; MbimDevice *device; g_autoptr(MbimMessage) message = NULL; MbimMicoMode mico_mode; MbimDrxCycle drx_cycle; if (!peek_device (self, &device, callback, user_data)) return; task = g_task_new (self, NULL, callback, user_data); if (!self->priv->is_nr5g_registration_settings_supported) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "5GNR registration settings are unsupported"); g_object_unref (task); return; } mico_mode = mm_modem_3gpp_mico_mode_to_mbim_mico_mode (mm_nr5g_registration_settings_get_mico_mode (settings)); drx_cycle = mm_modem_3gpp_drx_cycle_to_mbim_drx_cycle (mm_nr5g_registration_settings_get_drx_cycle (settings)); message = mbim_message_ms_basic_connect_extensions_v3_registration_parameters_set_new (mico_mode, drx_cycle, MBIM_LADN_INFO_NOT_NEEDED, MBIM_DEFAULT_PDU_ACTIVATION_HINT_LIKELY, TRUE, NULL, /* unnamed ies */ NULL); mbim_device_command (device, message, 10, NULL, (GAsyncReadyCallback)set_nr5g_registration_settings_ready, task); } /*****************************************************************************/ /* Signal state updates */ static void atds_signal_query_after_indication_ready (MbimDevice *device, GAsyncResult *res, MMBroadbandModemMbim *_self) /* full reference! */ { g_autoptr(MMBroadbandModemMbim) self = _self; g_autoptr(MbimMessage) response = NULL; g_autoptr(MMSignal) gsm = NULL; g_autoptr(MMSignal) umts = NULL; g_autoptr(MMSignal) lte = NULL; g_autoptr(GError) error = NULL; guint32 rssi; guint32 error_rate; guint32 rscp; guint32 ecno; guint32 rsrq; guint32 rsrp; guint32 snr; response = mbim_device_command_finish (device, res, &error); if (!response || !mbim_message_response_get_result (response, MBIM_MESSAGE_TYPE_COMMAND_DONE, &error) || !mbim_message_atds_signal_response_parse (response, &rssi, &error_rate, &rscp, &ecno, &rsrq, &rsrp, &snr, &error)) { mm_obj_warn (self, "ATDS signal query failed: %s", error->message); return; } if (!mm_signal_from_atds_signal_response (rssi, rscp, ecno, rsrq, rsrp, snr, &gsm, &umts, <e)) { mm_obj_warn (self, "No signal details given"); return; } mm_iface_modem_signal_update (MM_IFACE_MODEM_SIGNAL (self), NULL, NULL, gsm, umts, lte, NULL); } static void basic_connect_notification_signal_state (MMBroadbandModemMbim *self, MbimDevice *device, MbimMessage *notification) { g_autoptr(GError) error = NULL; g_autoptr(MbimRsrpSnrInfoArray) rsrp_snr = NULL; guint32 rsrp_snr_count = 0; guint32 coded_rssi; guint32 coded_error_rate = 99; guint32 quality; MbimDataClass data_class; g_autoptr(MMSignal) cdma = NULL; g_autoptr(MMSignal) evdo = NULL; g_autoptr(MMSignal) gsm = NULL; g_autoptr(MMSignal) umts = NULL; g_autoptr(MMSignal) lte = NULL; g_autoptr(MMSignal) nr5g = NULL; if (mbim_device_check_ms_mbimex_version (device, 2, 0)) { if (!mbim_message_ms_basic_connect_v2_signal_state_notification_parse ( notification, &coded_rssi, &coded_error_rate, NULL, /* signal_strength_interval */ NULL, /* rssi_threshold */ NULL, /* error_rate_threshold */ &rsrp_snr_count, &rsrp_snr, &error)) { mm_obj_warn (self, "failed processing MBIMEx v2.0 signal state indication: %s", error->message); return; } mm_obj_dbg (self, "processed MBIMEx v2.0 signal state indication"); } else { if (!mbim_message_signal_state_notification_parse ( notification, &coded_rssi, &coded_error_rate, NULL, /* signal_strength_interval */ NULL, /* rssi_threshold */ NULL, /* error_rate_threshold */ &error)) { mm_obj_warn (self, "failed processing signal state indication: %s", error->message); return; } mm_obj_dbg (self, "processed signal state indication"); if (self->priv->is_atds_signal_supported) { g_autoptr(MbimMessage) message = NULL; mm_obj_dbg (self, "triggering ATDS signal query"); message = mbim_message_atds_signal_query_new (NULL); mbim_device_command (device, message, 5, NULL, (GAsyncReadyCallback)atds_signal_query_after_indication_ready, g_object_ref (self)); } } quality = mm_signal_quality_from_mbim_signal_state (coded_rssi, rsrp_snr, rsrp_snr_count, self); mm_iface_modem_update_signal_quality (MM_IFACE_MODEM (self), quality); /* Best guess of current data class */ data_class = self->priv->enabled_cache.highest_available_data_class; if (data_class == 0) data_class = self->priv->enabled_cache.available_data_classes; if (mm_signal_from_mbim_signal_state (data_class, coded_rssi, coded_error_rate, rsrp_snr, rsrp_snr_count, self, &cdma, &evdo, &gsm, &umts, <e, &nr5g)) mm_iface_modem_signal_update (MM_IFACE_MODEM_SIGNAL (self), cdma, evdo, gsm, umts, lte, nr5g); } /*****************************************************************************/ /* ATDS location update */ static void atds_location_query_ready (MbimDevice *device, GAsyncResult *res, MMBroadbandModemMbim *self) { g_autoptr(MbimMessage) response = NULL; GError *error = NULL; guint32 lac; guint32 tac; guint32 cid; response = mbim_device_command_finish (device, res, &error); if (!response || !mbim_message_response_get_result (response, MBIM_MESSAGE_TYPE_COMMAND_DONE, &error) || !mbim_message_atds_location_response_parse (response, &lac, &tac, &cid, &error)) { mm_obj_warn (self, "failed processing ATDS location query response: %s", error->message); mm_iface_modem_3gpp_update_location (MM_IFACE_MODEM_3GPP (self), 0, 0, 0); } else { mm_iface_modem_3gpp_update_location (MM_IFACE_MODEM_3GPP (self), lac, tac, cid); } g_object_unref (self); } static void update_atds_location (MMBroadbandModemMbim *self) { g_autoptr(MbimMessage) message = NULL; MMPortMbim *port; MbimDevice *device; port = mm_broadband_modem_mbim_peek_port_mbim (self); if (!port) return; device = mm_port_mbim_peek_device (port); if (!device) return; message = mbim_message_atds_location_query_new (NULL); mbim_device_command (device, message, 10, NULL, (GAsyncReadyCallback)atds_location_query_ready, g_object_ref (self)); } /*****************************************************************************/ /* Access technology updates */ static void update_access_technologies (MMBroadbandModemMbim *self) { MMModemAccessTechnology act; act = mm_modem_access_technology_from_mbim_data_class (self->priv->enabled_cache.highest_available_data_class); if (act == MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN) act = mm_modem_access_technology_from_mbim_data_class (self->priv->enabled_cache.available_data_classes); mm_iface_modem_3gpp_update_access_technologies (MM_IFACE_MODEM_3GPP (self), act); } /*****************************************************************************/ /* Packet service updates */ static void update_registration_info (MMBroadbandModemMbim *self, gboolean scheduled, MbimRegisterState state, MbimDataClass available_data_classes, gchar *operator_id_take, gchar *operator_name_take); static void update_packet_service_info (MMBroadbandModemMbim *self, MbimPacketServiceState packet_service_state) { MMModem3gppPacketServiceState state; /* Report the new value to the 3GPP interface right away, don't assume it has the same * cached value. */ state = mm_modem_3gpp_packet_service_state_from_mbim_packet_service_state (packet_service_state); mm_iface_modem_3gpp_update_packet_service_state (MM_IFACE_MODEM_3GPP (self), state); if (packet_service_state == self->priv->enabled_cache.packet_service_state) return; /* PS reg state depends on the packet service state */ self->priv->enabled_cache.packet_service_state = packet_service_state; update_registration_info (self, FALSE, self->priv->enabled_cache.reg_state, self->priv->enabled_cache.available_data_classes, g_strdup (self->priv->enabled_cache.current_operator_id), g_strdup (self->priv->enabled_cache.current_operator_name)); } /*****************************************************************************/ /* Registration info updates */ static void enabling_state_changed (MMBroadbandModemMbim *self) { MMModemState state; g_object_get (self, MM_IFACE_MODEM_STATE, &state, NULL); /* if we've reached a enabled state, we can trigger the update */ if (state > MM_MODEM_STATE_ENABLING) { mm_obj_dbg (self, "triggering 3GPP registration info update"); update_registration_info (self, TRUE, self->priv->enabled_cache.reg_state, self->priv->enabled_cache.available_data_classes, g_strdup (self->priv->enabled_cache.current_operator_id), g_strdup (self->priv->enabled_cache.current_operator_name)); } /* if something bad happened during enabling, we can ignore any pending * registration info update */ else if (state < MM_MODEM_STATE_ENABLING) mm_obj_dbg (self, "discarding pending 3GPP registration info update"); /* this signal is expected to be fired just once */ g_signal_handler_disconnect (self, self->priv->enabling_signal_id); self->priv->enabling_signal_id = 0; } static void update_registration_info (MMBroadbandModemMbim *self, gboolean scheduled, MbimRegisterState state, MbimDataClass available_data_classes, gchar *operator_id_take, gchar *operator_name_take) { MMModem3gppRegistrationState reg_state; MMModem3gppRegistrationState reg_state_cs; MMModem3gppRegistrationState reg_state_ps; MMModem3gppRegistrationState reg_state_eps; MMModem3gppRegistrationState reg_state_5gs; gboolean operator_updated = FALSE; gboolean reg_state_updated = FALSE; MMModemState modem_state; gboolean schedule_update_in_enabled = FALSE; /* If we're enabling, we will not attempt to update anything yet, we will * instead store the info and schedule the updates for when we're enabled */ g_object_get (self, MM_IFACE_MODEM_STATE, &modem_state, NULL); if (modem_state == MM_MODEM_STATE_ENABLING) schedule_update_in_enabled = TRUE; if (self->priv->enabled_cache.reg_state != state) reg_state_updated = TRUE; self->priv->enabled_cache.reg_state = state; reg_state = mm_modem_3gpp_registration_state_from_mbim_register_state (state); if (reg_state == MM_MODEM_3GPP_REGISTRATION_STATE_HOME || reg_state == MM_MODEM_3GPP_REGISTRATION_STATE_ROAMING) { if (self->priv->enabled_cache.current_operator_id && operator_id_take && g_str_equal (self->priv->enabled_cache.current_operator_id, operator_id_take)) { g_free (operator_id_take); } else { operator_updated = TRUE; g_free (self->priv->enabled_cache.current_operator_id); self->priv->enabled_cache.current_operator_id = operator_id_take; } if (self->priv->enabled_cache.current_operator_name && operator_name_take && g_str_equal (self->priv->enabled_cache.current_operator_name, operator_name_take)) { g_free (operator_name_take); } else { operator_updated = TRUE; g_free (self->priv->enabled_cache.current_operator_name); self->priv->enabled_cache.current_operator_name = operator_name_take; } } else { if (self->priv->enabled_cache.current_operator_id || self->priv->enabled_cache.current_operator_name) { operator_updated = TRUE; } g_clear_pointer (&self->priv->enabled_cache.current_operator_id, g_free); g_clear_pointer (&self->priv->enabled_cache.current_operator_name, g_free); g_free (operator_id_take); g_free (operator_name_take); /* Explicitly reset packet service state if we're not registered */ update_packet_service_info (self, MBIM_PACKET_SERVICE_STATE_UNKNOWN); } /* If we can update domain registration states right now, do it */ if (!schedule_update_in_enabled) { reg_state_cs = MM_MODEM_3GPP_REGISTRATION_STATE_IDLE; reg_state_ps = MM_MODEM_3GPP_REGISTRATION_STATE_IDLE; reg_state_eps = MM_MODEM_3GPP_REGISTRATION_STATE_IDLE; reg_state_5gs = MM_MODEM_3GPP_REGISTRATION_STATE_IDLE; if (available_data_classes & (MBIM_DATA_CLASS_GPRS | MBIM_DATA_CLASS_EDGE | MBIM_DATA_CLASS_UMTS | MBIM_DATA_CLASS_HSDPA | MBIM_DATA_CLASS_HSUPA)) { reg_state_cs = reg_state; if (self->priv->enabled_cache.packet_service_state == MBIM_PACKET_SERVICE_STATE_ATTACHED) reg_state_ps = reg_state; } if (available_data_classes & (MBIM_DATA_CLASS_LTE)) reg_state_eps = reg_state; if (available_data_classes & (MBIM_DATA_CLASS_5G_NSA | MBIM_DATA_CLASS_5G_SA)) reg_state_5gs = reg_state; mm_iface_modem_3gpp_update_cs_registration_state (MM_IFACE_MODEM_3GPP (self), reg_state_cs, TRUE); mm_iface_modem_3gpp_update_ps_registration_state (MM_IFACE_MODEM_3GPP (self), reg_state_ps, TRUE); if (mm_iface_modem_is_3gpp_lte (MM_IFACE_MODEM (self))) mm_iface_modem_3gpp_update_eps_registration_state (MM_IFACE_MODEM_3GPP (self), reg_state_eps, TRUE); if (mm_iface_modem_is_3gpp_5gnr (MM_IFACE_MODEM (self))) mm_iface_modem_3gpp_update_5gs_registration_state (MM_IFACE_MODEM_3GPP (self), reg_state_5gs, TRUE); mm_iface_modem_3gpp_apply_deferred_registration_state (MM_IFACE_MODEM_3GPP (self)); /* request to reload operator info explicitly, so that the new * operator name and code is propagated to the DBus interface */ if (operator_updated || scheduled) mm_iface_modem_3gpp_reload_current_registration_info (MM_IFACE_MODEM_3GPP (self), NULL, NULL); } self->priv->enabled_cache.available_data_classes = available_data_classes; /* If we can update access technologies right now, do it */ if (!schedule_update_in_enabled) update_access_technologies (self); /* request to reload location info */ if (!schedule_update_in_enabled && self->priv->is_atds_location_supported && (reg_state_updated || scheduled)) { if (self->priv->enabled_cache.reg_state < MBIM_REGISTER_STATE_HOME) { mm_iface_modem_3gpp_update_location (MM_IFACE_MODEM_3GPP (self), 0, 0, 0); } else update_atds_location (self); } if (schedule_update_in_enabled && !self->priv->enabling_signal_id) { mm_obj_dbg (self, "Scheduled registration info update once the modem is enabled"); self->priv->enabling_signal_id = g_signal_connect (self, "notify::" MM_IFACE_MODEM_STATE, G_CALLBACK (enabling_state_changed), NULL); } } static gboolean common_process_register_state (MMBroadbandModemMbim *self, MbimDevice *device, MbimMessage *message, MbimNwError *out_nw_error, GError **error) { MbimNwError nw_error = 0; MbimRegisterState register_state = MBIM_REGISTER_STATE_UNKNOWN; MbimDataClass available_data_classes = 0; g_autofree gchar *provider_id = NULL; g_autofree gchar *provider_name = NULL; MbimDataClass preferred_data_classes = 0; const gchar *nw_error_str; g_autofree gchar *available_data_classes_str = NULL; g_autofree gchar *preferred_data_classes_str = NULL; gboolean is_notification; is_notification = (mbim_message_get_message_type (message) == MBIM_MESSAGE_TYPE_INDICATE_STATUS); g_assert (is_notification || (mbim_message_get_message_type (message) == MBIM_MESSAGE_TYPE_COMMAND_DONE)); if (mbim_device_check_ms_mbimex_version (device, 2, 0)) { if (is_notification) { if (!mbim_message_ms_basic_connect_v2_register_state_notification_parse ( message, &nw_error, ®ister_state, NULL, /* register_mode */ &available_data_classes, NULL, /* current_cellular_class */ &provider_id, &provider_name, NULL, /* roaming_text */ NULL, /* registration_flag */ &preferred_data_classes, error)) { g_prefix_error (error, "Failed processing MBIMEx v2.0 register state indication: "); return FALSE; } mm_obj_dbg (self, "processed MBIMEx v2.0 register state indication"); } else { if (!mbim_message_ms_basic_connect_v2_register_state_response_parse ( message, &nw_error, ®ister_state, NULL, /* register_mode */ &available_data_classes, NULL, /* current_cellular_class */ &provider_id, &provider_name, NULL, /* roaming_text */ NULL, /* registration_flag */ &preferred_data_classes, error)) { g_prefix_error (error, "Failed processing MBIMEx v2.0 register state response: "); return FALSE; } mm_obj_dbg (self, "processed MBIMEx v2.0 register state indication"); } } else { if (is_notification) { if (!mbim_message_register_state_notification_parse ( message, &nw_error, ®ister_state, NULL, /* register_mode */ &available_data_classes, NULL, /* current_cellular_class */ &provider_id, &provider_name, NULL, /* roaming_text */ NULL, /* registration_flag */ error)) { g_prefix_error (error, "Failed processing register state indication: "); return FALSE; } mm_obj_dbg (self, "processed register state indication"); } else { if (!mbim_message_register_state_response_parse ( message, &nw_error, ®ister_state, NULL, /* register_mode */ &available_data_classes, NULL, /* current_cellular_class */ &provider_id, &provider_name, NULL, /* roaming_text */ NULL, /* registration_flag */ error)) { g_prefix_error (error, "Failed processing register state response: "); return FALSE; } mm_obj_dbg (self, "processed register state response"); } } nw_error = mm_broadband_modem_mbim_normalize_nw_error (self, nw_error); nw_error_str = mbim_nw_error_get_string (nw_error); available_data_classes_str = mbim_data_class_build_string_from_mask (available_data_classes); preferred_data_classes_str = mbim_data_class_build_string_from_mask (preferred_data_classes); mm_obj_dbg (self, "register state update:"); if (nw_error_str) mm_obj_dbg (self, " nw error: '%s'", nw_error_str); else mm_obj_dbg (self, " nw error: '0x%x'", nw_error); mm_obj_dbg (self, " state: '%s'", mbim_register_state_get_string (register_state)); mm_obj_dbg (self, " provider id: '%s'", provider_id ? provider_id : "n/a"); mm_obj_dbg (self, " provider name: '%s'", provider_name ? provider_name : "n/a"); mm_obj_dbg (self, "available data classes: '%s'", available_data_classes_str); mm_obj_dbg (self, "preferred data classes: '%s'", preferred_data_classes_str); update_registration_info (self, FALSE, register_state, available_data_classes, g_steal_pointer (&provider_id), g_steal_pointer (&provider_name)); if (preferred_data_classes) complete_pending_allowed_modes_action (self, preferred_data_classes); if (out_nw_error) *out_nw_error = nw_error; return TRUE; } static void basic_connect_notification_register_state (MMBroadbandModemMbim *self, MbimDevice *device, MbimMessage *notification) { g_autoptr(GError) error = NULL; if (!common_process_register_state (self, device, notification, NULL, &error)) mm_obj_warn (self, "%s", error->message); } typedef struct { MMBroadbandModemMbim *self; guint32 session_id; GError *connection_error; } ReportDisconnectedStatusContext; static void bearer_list_report_disconnected_status (MMBaseBearer *bearer, gpointer user_data) { ReportDisconnectedStatusContext *ctx = user_data; if (MM_IS_BEARER_MBIM (bearer) && mm_bearer_mbim_get_session_id (MM_BEARER_MBIM (bearer)) == ctx->session_id) { mm_obj_dbg (ctx->self, "bearer '%s' was disconnected.", mm_base_bearer_get_path (bearer)); mm_base_bearer_report_connection_status_detailed (bearer, MM_BEARER_CONNECTION_STATUS_DISCONNECTED, ctx->connection_error); } } static void basic_connect_notification_connect (MMBroadbandModemMbim *self, MbimDevice *device, MbimMessage *notification) { guint32 session_id; MbimActivationState activation_state; const MbimUuid *context_type; guint32 nw_error; g_autoptr(MMBearerList) bearer_list = NULL; g_autoptr(GError) error = NULL; if (mbim_device_check_ms_mbimex_version (device, 3, 0)) { if (!mbim_message_ms_basic_connect_v3_connect_notification_parse ( notification, &session_id, &activation_state, NULL, /* voice_call_state */ NULL, /* ip_type */ &context_type, /* context_type */ &nw_error, NULL, /* media_preference */ NULL, /* access_string */ NULL, /* unnamed_ies */ &error)) { mm_obj_warn (self, "Failed processing MBIMEx v3.0 connect notification: %s", error->message); return; } mm_obj_dbg (self, "processed MBIMEx v3.0 connect notification"); } else { if (!mbim_message_connect_notification_parse ( notification, &session_id, &activation_state, NULL, /* voice_call_state */ NULL, /* ip_type */ &context_type, &nw_error, &error)) { mm_obj_warn (self, "Failed processing connect notification: %s", error->message); return; } mm_obj_dbg (self, "processed connect notification"); } g_object_get (self, MM_IFACE_MODEM_BEARER_LIST, &bearer_list, NULL); if (!bearer_list) return; if (activation_state == MBIM_ACTIVATION_STATE_DEACTIVATED) { ReportDisconnectedStatusContext ctx; g_autoptr(GError) connection_error = NULL; nw_error = mm_broadband_modem_mbim_normalize_nw_error (self, nw_error); connection_error = mm_error_from_mbim_nw_error (nw_error, self); mm_obj_dbg (self, "session ID '%u' was deactivated: %s", session_id, connection_error->message); ctx.self = self; ctx.session_id = session_id; ctx.connection_error = connection_error; mm_bearer_list_foreach (bearer_list, (MMBearerListForeachFunc)bearer_list_report_disconnected_status, &ctx); } } static void pin_query_after_subscriber_ready_status_ready (MbimDevice *device, GAsyncResult *res, MMBroadbandModemMbim *self) /* full reference! */ { g_autoptr(MbimMessage) response = NULL; g_autoptr(GError) error = NULL; MbimPinType pin_type; MbimPinState pin_state; gboolean sim_event = FALSE; response = mbim_device_command_finish (device, res, &error); if (response && mbim_message_response_get_result (response, MBIM_MESSAGE_TYPE_COMMAND_DONE, &error) && mbim_message_pin_response_parse ( response, &pin_type, &pin_state, NULL, &error)) { if (pin_type == MBIM_PIN_TYPE_NETWORK_PIN || pin_type == MBIM_PIN_TYPE_NETWORK_PUK || self->priv->enabled_cache.last_pin_type == MBIM_PIN_TYPE_NETWORK_PIN) { sim_event = TRUE; } self->priv->enabled_cache.last_pin_type = pin_type; } if (error) mm_obj_dbg (self, "PIN query for basic connect failed: %s", error->message); if (sim_event) mm_iface_modem_process_sim_event (MM_IFACE_MODEM (self)); g_object_unref (self); } static void basic_connect_notification_subscriber_ready_status (MMBroadbandModemMbim *self, MbimDevice *device, MbimMessage *notification) { MbimSubscriberReadyState ready_state; g_auto(GStrv) telephone_numbers = NULL; g_autoptr(GError) error = NULL; gboolean active_sim_event = FALSE; if (self->priv->pending_sim_slot_switch_action) { mm_obj_dbg (self, "ignoring slot status change"); return; } if (mbim_device_check_ms_mbimex_version (device, 3, 0)) { if (!mbim_message_ms_basic_connect_v3_subscriber_ready_status_notification_parse ( notification, &ready_state, NULL, /* flags */ NULL, /* subscriber id */ NULL, /* sim_iccid */ NULL, /* ready_info */ NULL, /* telephone_numbers_count */ &telephone_numbers, &error)) { mm_obj_warn (self, "Failed processing MBIMEx v3.0 subscriber ready status notification: %s", error->message); return; } mm_obj_dbg (self, "processed MBIMEx v3.0 subscriber ready status notification"); } else { if (!mbim_message_subscriber_ready_status_notification_parse ( notification, &ready_state, NULL, /* subscriber_id */ NULL, /* sim_iccid */ NULL, /* ready_info */ NULL, /* telephone_numbers_count */ &telephone_numbers, &error)) { mm_obj_warn (self, "Failed processing subscriber ready status notification: %s", error->message); return; } mm_obj_dbg (self, "processed subscriber ready status notification"); } if (ready_state == MBIM_SUBSCRIBER_READY_STATE_INITIALIZED) mm_iface_modem_update_own_numbers (MM_IFACE_MODEM (self), telephone_numbers); if ((self->priv->enabled_cache.last_ready_state != MBIM_SUBSCRIBER_READY_STATE_NO_ESIM_PROFILE && ready_state == MBIM_SUBSCRIBER_READY_STATE_NO_ESIM_PROFILE) || (self->priv->enabled_cache.last_ready_state == MBIM_SUBSCRIBER_READY_STATE_NO_ESIM_PROFILE && ready_state != MBIM_SUBSCRIBER_READY_STATE_NO_ESIM_PROFILE)) { /* eSIM profiles have been added or removed, re-probe to ensure correct interfaces are exposed */ mm_obj_dbg (self, "eSIM profile updates detected"); active_sim_event = TRUE; } if ((self->priv->enabled_cache.last_ready_state != MBIM_SUBSCRIBER_READY_STATE_SIM_NOT_INSERTED && ready_state == MBIM_SUBSCRIBER_READY_STATE_SIM_NOT_INSERTED) || (self->priv->enabled_cache.last_ready_state == MBIM_SUBSCRIBER_READY_STATE_SIM_NOT_INSERTED && ready_state != MBIM_SUBSCRIBER_READY_STATE_SIM_NOT_INSERTED)) { /* SIM has been removed or reinserted, re-probe to ensure correct interfaces are exposed */ mm_obj_dbg (self, "SIM hot swap detected"); active_sim_event = TRUE; } if ((self->priv->enabled_cache.last_ready_state != MBIM_SUBSCRIBER_READY_STATE_DEVICE_LOCKED && ready_state == MBIM_SUBSCRIBER_READY_STATE_DEVICE_LOCKED) || (self->priv->enabled_cache.last_ready_state == MBIM_SUBSCRIBER_READY_STATE_DEVICE_LOCKED && ready_state != MBIM_SUBSCRIBER_READY_STATE_DEVICE_LOCKED)) { g_autoptr(MbimMessage) message = NULL; /* Query which lock has changed */ message = mbim_message_pin_query_new (NULL); mbim_device_command (device, message, 10, NULL, (GAsyncReadyCallback)pin_query_after_subscriber_ready_status_ready, g_object_ref (self)); } /* Ignore NOT_INITIALIZED state when setting the last_ready_state as it is * reported regardless of whether SIM was inserted or unlocked */ if (ready_state != MBIM_SUBSCRIBER_READY_STATE_NOT_INITIALIZED) { self->priv->enabled_cache.last_ready_state = ready_state; } if (active_sim_event) { mm_iface_modem_process_sim_event (MM_IFACE_MODEM (self)); } } /*****************************************************************************/ /* Speed updates */ void mm_broadband_modem_mbim_get_speeds (MMBroadbandModemMbim *self, guint64 *uplink_speed, guint64 *downlink_speed) { if (uplink_speed) *uplink_speed = self->priv->enabled_cache.packet_service_uplink_speed; if (downlink_speed) *downlink_speed = self->priv->enabled_cache.packet_service_downlink_speed; } static void bearer_list_report_speeds (MMBaseBearer *bearer, MMBroadbandModemMbim *self) { if (!MM_IS_BEARER_MBIM (bearer)) return; /* Update speeds only if connected or connecting */ if (mm_base_bearer_get_status (bearer) >= MM_BEARER_STATUS_CONNECTING) { mm_obj_dbg (self, "bearer '%s' speeds updated", mm_base_bearer_get_path (bearer)); mm_base_bearer_report_speeds (bearer, self->priv->enabled_cache.packet_service_uplink_speed, self->priv->enabled_cache.packet_service_downlink_speed); } } static void update_bearer_speeds (MMBroadbandModemMbim *self, guint64 uplink_speed, guint64 downlink_speed) { g_autoptr(MMBearerList) bearer_list = NULL; if ((self->priv->enabled_cache.packet_service_uplink_speed == uplink_speed) && (self->priv->enabled_cache.packet_service_downlink_speed == downlink_speed)) return; self->priv->enabled_cache.packet_service_uplink_speed = uplink_speed; self->priv->enabled_cache.packet_service_downlink_speed = downlink_speed; g_object_get (self, MM_IFACE_MODEM_BEARER_LIST, &bearer_list, NULL); if (!bearer_list) return; mm_bearer_list_foreach (bearer_list, (MMBearerListForeachFunc)bearer_list_report_speeds, self); } /*****************************************************************************/ /* Packet service updates */ static gboolean common_process_packet_service (MMBroadbandModemMbim *self, MbimDevice *device, MbimMessage *message, guint32 *out_nw_error, MbimPacketServiceState *out_packet_service_state, GError **error) { guint32 nw_error = 0; MbimPacketServiceState packet_service_state = MBIM_PACKET_SERVICE_STATE_UNKNOWN; MbimDataClass data_class = 0; MbimDataClassV3 data_class_v3 = 0; MbimDataSubclass data_subclass = 0; guint64 uplink_speed = 0; guint64 downlink_speed = 0; MbimFrequencyRange frequency_range = MBIM_FREQUENCY_RANGE_UNKNOWN; g_autofree gchar *data_class_str = NULL; g_autofree gchar *data_class_v3_str = NULL; g_autofree gchar *data_subclass_str = NULL; g_autofree gchar *frequency_range_str = NULL; const gchar *nw_error_str; gboolean is_notification; is_notification = (mbim_message_get_message_type (message) == MBIM_MESSAGE_TYPE_INDICATE_STATUS); g_assert (is_notification || (mbim_message_get_message_type (message) == MBIM_MESSAGE_TYPE_COMMAND_DONE)); if (mbim_device_check_ms_mbimex_version (device, 3, 0)) { if (is_notification) { if (!mbim_message_ms_basic_connect_v3_packet_service_notification_parse ( message, &nw_error, &packet_service_state, &data_class_v3, &uplink_speed, &downlink_speed, &frequency_range, &data_subclass, NULL, /* tai */ error)) { g_prefix_error (error, "Failed processing MBIMEx v3.0 packet service indication: "); return FALSE; } mm_obj_dbg (self, "processed MBIMEx v3.0 packet service indication"); } else { if (!mbim_message_ms_basic_connect_v3_packet_service_response_parse ( message, &nw_error, &packet_service_state, &data_class_v3, &uplink_speed, &downlink_speed, &frequency_range, &data_subclass, NULL, /* tai */ error)) { g_prefix_error (error, "Failed processing MBIMEx v3.0 packet service response: "); return FALSE; } mm_obj_dbg (self, "processed MBIMEx v3.0 packet service response"); } data_class_v3_str = mbim_data_class_v3_build_string_from_mask (data_class_v3); data_subclass_str = mbim_data_subclass_build_string_from_mask (data_subclass); } else if (mbim_device_check_ms_mbimex_version (device, 2, 0)) { if (is_notification) { if (!mbim_message_ms_basic_connect_v2_packet_service_notification_parse ( message, &nw_error, &packet_service_state, &data_class, /* current */ &uplink_speed, &downlink_speed, &frequency_range, error)) { g_prefix_error (error, "Failed processing MBIMEx v2.0 packet service indication: "); return FALSE; } mm_obj_dbg (self, "processed MBIMEx v2.0 packet service indication"); } else { if (!mbim_message_ms_basic_connect_v2_packet_service_response_parse ( message, &nw_error, &packet_service_state, &data_class, /* current */ &uplink_speed, &downlink_speed, &frequency_range, error)) { g_prefix_error (error, "Failed processing MBIMEx v2.0 packet service response: "); return FALSE; } mm_obj_dbg (self, "processed MBIMEx v2.0 packet service response"); } data_class_str = mbim_data_class_build_string_from_mask (data_class); } else { if (is_notification) { if (!mbim_message_packet_service_notification_parse ( message, &nw_error, &packet_service_state, &data_class, /* highest available */ &uplink_speed, &downlink_speed, error)) { g_prefix_error (error, "Failed processing packet service indication: "); return FALSE; } mm_obj_dbg (self, "processed packet service indication"); } else { if (!mbim_message_packet_service_response_parse ( message, &nw_error, &packet_service_state, &data_class, /* highest available */ &uplink_speed, &downlink_speed, error)) { g_prefix_error (error, "Failed processing packet service response: "); return FALSE; } mm_obj_dbg (self, "processed packet service response"); } data_class_str = mbim_data_class_build_string_from_mask (data_class); } frequency_range_str = mbim_frequency_range_build_string_from_mask (frequency_range); nw_error = mm_broadband_modem_mbim_normalize_nw_error (self, nw_error); nw_error_str = mbim_nw_error_get_string (nw_error); mm_obj_dbg (self, "packet service update:"); if (nw_error_str) mm_obj_dbg (self, " nw error: '%s'", nw_error_str); else mm_obj_dbg (self, " nw error: '0x%x'", nw_error); mm_obj_dbg (self, " state: '%s'", mbim_packet_service_state_get_string (packet_service_state)); if (data_class_str) mm_obj_dbg (self, " data class: '%s'", data_class_str); else if (data_class_v3_str) mm_obj_dbg (self, " data class: '%s'", data_class_v3_str); if (data_subclass_str) mm_obj_dbg (self, " data subclass: '%s'", data_subclass_str); mm_obj_dbg (self, " uplink: '%" G_GUINT64_FORMAT "' bps", uplink_speed); mm_obj_dbg (self, " downlink: '%" G_GUINT64_FORMAT "' bps", downlink_speed); mm_obj_dbg (self, " frequency range: '%s'", frequency_range_str); if (packet_service_state == MBIM_PACKET_SERVICE_STATE_ATTACHED) { if (data_class_v3) self->priv->enabled_cache.highest_available_data_class = mm_mbim_data_class_from_mbim_data_class_v3_and_subclass (data_class_v3, data_subclass); else self->priv->enabled_cache.highest_available_data_class = data_class; } else if (packet_service_state == MBIM_PACKET_SERVICE_STATE_DETACHED) { self->priv->enabled_cache.highest_available_data_class = 0; } update_access_technologies (self); update_packet_service_info (self, packet_service_state); update_bearer_speeds (self, uplink_speed, downlink_speed); if (out_nw_error) *out_nw_error = nw_error; if (out_packet_service_state) *out_packet_service_state = packet_service_state; return TRUE; } static void basic_connect_notification_packet_service (MMBroadbandModemMbim *self, MbimDevice *device, MbimMessage *notification) { g_autoptr(GError) error = NULL; if (!common_process_packet_service (self, device, notification, NULL, NULL, &error)) mm_obj_warn (self, "%s", error->message); } static void basic_connect_notification_provisioned_contexts (MMBroadbandModemMbim *self, MbimDevice *device, MbimMessage *notification) { /* We don't even attempt to parse the indication, we just need to notify that * something changed to the upper layers */ mm_iface_modem_3gpp_profile_manager_updated (MM_IFACE_MODEM_3GPP_PROFILE_MANAGER (self)); } static gboolean process_pdu_messages (MMBroadbandModemMbim *self, MbimSmsFormat format, guint32 messages_count, MbimSmsPduReadRecordArray *pdu_messages, GError **error); static void sms_notification_read_flash_sms (MMBroadbandModemMbim *self, MbimDevice *device, MbimMessage *notification) { g_autoptr(MbimSmsPduReadRecordArray) pdu_messages = NULL; g_autoptr(GError) error = NULL; MbimSmsFormat format; guint32 messages_count; if (!mbim_message_sms_read_notification_parse ( notification, &format, &messages_count, &pdu_messages, NULL, /* cdma_messages */ &error) || !process_pdu_messages (self, format, messages_count, pdu_messages, &error)) mm_obj_dbg (self, "flash SMS message reading failed: %s", error->message); } static void basic_connect_notification (MMBroadbandModemMbim *self, MbimDevice *device, MbimMessage *notification) { switch (mbim_message_indicate_status_get_cid (notification)) { case MBIM_CID_BASIC_CONNECT_SIGNAL_STATE: if (self->priv->setup_flags & PROCESS_NOTIFICATION_FLAG_SIGNAL_QUALITY) basic_connect_notification_signal_state (self, device, notification); break; case MBIM_CID_BASIC_CONNECT_REGISTER_STATE: if (self->priv->setup_flags & PROCESS_NOTIFICATION_FLAG_REGISTRATION_UPDATES) basic_connect_notification_register_state (self, device, notification); break; case MBIM_CID_BASIC_CONNECT_CONNECT: if (self->priv->setup_flags & PROCESS_NOTIFICATION_FLAG_CONNECT) basic_connect_notification_connect (self, device, notification); break; case MBIM_CID_BASIC_CONNECT_SUBSCRIBER_READY_STATUS: if (self->priv->setup_flags & PROCESS_NOTIFICATION_FLAG_SUBSCRIBER_INFO) basic_connect_notification_subscriber_ready_status (self, device, notification); break; case MBIM_CID_BASIC_CONNECT_PACKET_SERVICE: if (self->priv->setup_flags & PROCESS_NOTIFICATION_FLAG_PACKET_SERVICE) basic_connect_notification_packet_service (self, device, notification); break; case MBIM_CID_BASIC_CONNECT_PROVISIONED_CONTEXTS: if (self->priv->setup_flags & PROCESS_NOTIFICATION_FLAG_PROVISIONED_CONTEXTS) basic_connect_notification_provisioned_contexts (self, device, notification); case MBIM_CID_BASIC_CONNECT_IP_CONFIGURATION: /* Ignored at modem level, only managed by bearer if waiting for async SLAAC results */ default: /* Ignore */ break; } } static void alert_sms_read_query_ready (MbimDevice *device, GAsyncResult *res, MMBroadbandModemMbim *self) /* full ref */ { g_autoptr(MbimMessage) response = NULL; g_autoptr(GError) error = NULL; g_autoptr(MbimSmsPduReadRecordArray) pdu_messages = NULL; MbimSmsFormat format; guint32 messages_count; response = mbim_device_command_finish (device, res, &error); if (!response || !mbim_message_response_get_result (response, MBIM_MESSAGE_TYPE_COMMAND_DONE, &error) || !mbim_message_sms_read_response_parse ( response, &format, &messages_count, &pdu_messages, NULL, /* cdma_messages */ &error) || !process_pdu_messages (self, format, messages_count, pdu_messages, &error)) mm_obj_dbg (self, "SMS message reading failed: %s", error->message); g_object_unref (self); } static void sms_notification_read_stored_sms (MMBroadbandModemMbim *self, guint32 index) { g_autoptr(MbimMessage) message = NULL; MMPortMbim *port; MbimDevice *device; port = mm_broadband_modem_mbim_peek_port_mbim (self); if (!port) return; device = mm_port_mbim_peek_device (port); if (!device) return; mm_obj_dbg (self, "reading new SMS at index '%u'", index); message = mbim_message_sms_read_query_new (MBIM_SMS_FORMAT_PDU, MBIM_SMS_FLAG_INDEX, index, NULL); mbim_device_command (device, message, 10, NULL, (GAsyncReadyCallback)alert_sms_read_query_ready, g_object_ref (self)); } static void sms_notification (MMBroadbandModemMbim *self, MbimDevice *device, MbimMessage *notification) { switch (mbim_message_indicate_status_get_cid (notification)) { case MBIM_CID_SMS_READ: /* New flash/alert message? */ if (self->priv->setup_flags & PROCESS_NOTIFICATION_FLAG_SMS_READ) sms_notification_read_flash_sms (self, device, notification); break; case MBIM_CID_SMS_MESSAGE_STORE_STATUS: { MbimSmsStatusFlag flags; guint32 index; if (self->priv->setup_flags & PROCESS_NOTIFICATION_FLAG_SMS_READ && mbim_message_sms_message_store_status_notification_parse ( notification, &flags, &index, NULL)) { g_autofree gchar *flags_str = NULL; flags_str = mbim_sms_status_flag_build_string_from_mask (flags); mm_obj_dbg (self, "received SMS store status update: '%s'", flags_str); if (flags & MBIM_SMS_STATUS_FLAG_NEW_MESSAGE) sms_notification_read_stored_sms (self, index); } break; } default: /* Ignore */ break; } } static void ms_basic_connect_extensions_notification_pco (MMBroadbandModemMbim *self, MbimDevice *device, MbimMessage *notification) { MbimPcoValue *pco_value; GError *error = NULL; gchar *pco_data_hex; MMPco *pco; if (!mbim_message_ms_basic_connect_extensions_pco_notification_parse ( notification, &pco_value, &error)) { mm_obj_warn (self, "couldn't parse PCO notification: %s", error->message); g_error_free (error); return; } pco_data_hex = mm_utils_bin2hexstr (pco_value->pco_data_buffer, pco_value->pco_data_size); mm_obj_dbg (self, "received PCO: session ID=%u type=%s size=%u data=%s", pco_value->session_id, mbim_pco_type_get_string (pco_value->pco_data_type), pco_value->pco_data_size, pco_data_hex); g_free (pco_data_hex); pco = mm_pco_new (); mm_pco_set_session_id (pco, pco_value->session_id); mm_pco_set_complete (pco, pco_value->pco_data_type == MBIM_PCO_TYPE_COMPLETE); mm_pco_set_data (pco, pco_value->pco_data_buffer, pco_value->pco_data_size); self->priv->enabled_cache.pco_list = mm_pco_list_add (self->priv->enabled_cache.pco_list, pco); mm_iface_modem_3gpp_update_pco_list (MM_IFACE_MODEM_3GPP (self), self->priv->enabled_cache.pco_list); g_object_unref (pco); mbim_pco_value_free (pco_value); } static void ms_basic_connect_extensions_notification_lte_attach_info (MMBroadbandModemMbim *self, MbimDevice *device, MbimMessage *notification) { g_autoptr(GError) error = NULL; g_autoptr(MMBearerProperties) properties = NULL; guint32 lte_attach_state; guint32 ip_type; g_autofree gchar *access_string = NULL; g_autofree gchar *user_name = NULL; g_autofree gchar *password = NULL; guint32 compression; guint32 auth_protocol; MbimNwError nw_error = 0; if (mbim_device_check_ms_mbimex_version (device, 3, 0)) { if (!mbim_message_ms_basic_connect_extensions_v3_lte_attach_info_notification_parse ( notification, <e_attach_state, &nw_error, &ip_type, &access_string, &user_name, &password, &compression, &auth_protocol, &error)) { mm_obj_warn (self, "Failed processing MBIMEx v3.0 LTE attach info notification: %s", error->message); return; } mm_obj_dbg (self, "Processed MBIMEx v3.0 LTE attach info notification"); } else { if (!mbim_message_ms_basic_connect_extensions_lte_attach_info_notification_parse ( notification, <e_attach_state, &ip_type, &access_string, &user_name, &password, &compression, &auth_protocol, &error)) { mm_obj_warn (self, "Failed processing LTE attach info notification: %s", error->message); return; } mm_obj_dbg (self, "Processed LTE attach info notification"); } properties = common_process_lte_attach_info (self, lte_attach_state, ip_type, access_string, user_name, password, compression, auth_protocol, NULL); mm_iface_modem_3gpp_update_initial_eps_bearer (MM_IFACE_MODEM_3GPP (self), properties); /* If network error is reported, then log it */ if (nw_error) { const gchar *nw_error_str; nw_error = mm_broadband_modem_mbim_normalize_nw_error (self, nw_error); nw_error_str = mbim_nw_error_get_string (nw_error); if (nw_error_str) mm_obj_dbg (self, "LTE attach info network error reported: %s", nw_error_str); else mm_obj_dbg (self, "LTE attach info network error reported: 0x%x", nw_error); } } static MMBaseSim * create_sim_from_slot_state (MMBroadbandModemMbim *self, gboolean active, guint slot_index, MbimUiccSlotState slot_state) { MMSimType sim_type = MM_SIM_TYPE_UNKNOWN; MMSimEsimStatus esim_status = MM_SIM_ESIM_STATUS_UNKNOWN; switch (slot_state) { case MBIM_UICC_SLOT_STATE_ACTIVE: sim_type = MM_SIM_TYPE_PHYSICAL; break; case MBIM_UICC_SLOT_STATE_ACTIVE_ESIM: sim_type = MM_SIM_TYPE_ESIM; esim_status = MM_SIM_ESIM_STATUS_WITH_PROFILES; break; case MBIM_UICC_SLOT_STATE_ACTIVE_ESIM_NO_PROFILES: sim_type = MM_SIM_TYPE_ESIM; esim_status = MM_SIM_ESIM_STATUS_NO_PROFILES; break; case MBIM_UICC_SLOT_STATE_NOT_READY: case MBIM_UICC_SLOT_STATE_ERROR: /* Not fully ready (NOT_READY) or unusable (ERROR) SIM cards should also be * reported as being available in the non-active slot. */ break; case MBIM_UICC_SLOT_STATE_UNKNOWN: case MBIM_UICC_SLOT_SATE_OFF_EMPTY: case MBIM_UICC_SLOT_STATE_OFF: case MBIM_UICC_SLOT_STATE_EMPTY: default: return NULL; } mm_obj_dbg (self, "found %s SIM in slot %u: %s (%s)", active ? "active" : "inactive", slot_index, mm_sim_type_get_string (sim_type), (sim_type == MM_SIM_TYPE_ESIM) ? mm_sim_esim_status_get_string (esim_status) : "n/a"); return MM_BASE_SIM (mm_sim_mbim_new_initialized (MM_BASE_MODEM (self), slot_index, active, sim_type, esim_status, NULL, NULL, NULL, NULL, NULL, NULL)); } static void ms_basic_connect_extensions_notification_slot_info_status (MMBroadbandModemMbim *self, MbimDevice *device, MbimMessage *notification) { g_autoptr(GError) error = NULL; guint32 slot_index; MbimUiccSlotState slot_state; if (!mbim_message_ms_basic_connect_extensions_slot_info_status_notification_parse ( notification, &slot_index, &slot_state, &error)) { mm_obj_warn (self, "Couldn't parse slot info status notification: %s", error->message); return; } if (self->priv->pending_sim_slot_switch_action) { mm_obj_dbg (self, "ignoring slot status change in SIM slot %d: %s", slot_index + 1, mbim_uicc_slot_state_get_string (slot_state)); return; } if (self->priv->active_slot_index != (slot_index + 1)) { /* Modifies SIM object at the given slot based on the reported state, * when the slot is not the active one. */ g_autoptr(MMBaseSim) sim = NULL; mm_obj_dbg (self, "processing slot status change in non-active SIM slot %d: %s", slot_index + 1, mbim_uicc_slot_state_get_string (slot_state)); sim = create_sim_from_slot_state (self, FALSE, slot_index, slot_state); mm_iface_modem_modify_sim (MM_IFACE_MODEM (self), slot_index, sim); return; } /* Major SIM event on the active slot, will request reprobing the * modem from scratch. */ mm_obj_dbg (self, "processing slot status change in active SIM slot %d: %s", slot_index + 1, mbim_uicc_slot_state_get_string (slot_state)); mm_iface_modem_process_sim_event (MM_IFACE_MODEM (self)); } static void ms_basic_connect_extensions_notification (MMBroadbandModemMbim *self, MbimDevice *device, MbimMessage *notification) { switch (mbim_message_indicate_status_get_cid (notification)) { case MBIM_CID_MS_BASIC_CONNECT_EXTENSIONS_PCO: if (self->priv->setup_flags & PROCESS_NOTIFICATION_FLAG_PCO) ms_basic_connect_extensions_notification_pco (self, device, notification); break; case MBIM_CID_MS_BASIC_CONNECT_EXTENSIONS_LTE_ATTACH_INFO: if (self->priv->setup_flags & PROCESS_NOTIFICATION_FLAG_LTE_ATTACH_INFO) ms_basic_connect_extensions_notification_lte_attach_info (self, device, notification); break; case MBIM_CID_MS_BASIC_CONNECT_EXTENSIONS_SLOT_INFO_STATUS: if (self->priv->setup_flags & PROCESS_NOTIFICATION_FLAG_SLOT_INFO_STATUS) ms_basic_connect_extensions_notification_slot_info_status (self, device, notification); break; default: /* Ignore */ break; } } static void process_ussd_notification (MMBroadbandModemMbim *self, MbimMessage *notification); static void ussd_notification (MMBroadbandModemMbim *self, MbimDevice *device, MbimMessage *notification) { if (mbim_message_indicate_status_get_cid (notification) != MBIM_CID_USSD) { mm_obj_warn (self, "unexpected USSD notification (cid %u)", mbim_message_indicate_status_get_cid (notification)); return; } if (!(self->priv->setup_flags & PROCESS_NOTIFICATION_FLAG_USSD)) return; process_ussd_notification (self, notification); } static void port_notification_cb (MMPortMbim *port, MbimMessage *notification, MMBroadbandModemMbim *self) { MbimService service; MbimDevice *device; /* Only process notifications if the device still exists */ device = mm_port_mbim_peek_device (port); if (!device) return; service = mbim_message_indicate_status_get_service (notification); mm_obj_dbg (self, "received notification (service '%s', command '%s')", mbim_service_get_string (service), mbim_cid_get_printable (service, mbim_message_indicate_status_get_cid (notification))); if (service == MBIM_SERVICE_BASIC_CONNECT) basic_connect_notification (self, device, notification); else if (service == MBIM_SERVICE_MS_BASIC_CONNECT_EXTENSIONS) ms_basic_connect_extensions_notification (self, device, notification); else if (service == MBIM_SERVICE_SMS) sms_notification (self, device, notification); else if (service == MBIM_SERVICE_USSD) ussd_notification (self, device, notification); } static void common_setup_cleanup_unsolicited_events_sync (MMBroadbandModemMbim *self, MMPortMbim *port, gboolean setup) { if (!port) return; mm_obj_dbg (self, "supported notifications: signal (%s), registration (%s), sms (%s), " "connect (%s), subscriber (%s), packet (%s), pco (%s), ussd (%s), " "lte attach info (%s), provisioned contexts (%s), slot info status (%s), " "ip configuration (%s)", self->priv->setup_flags & PROCESS_NOTIFICATION_FLAG_SIGNAL_QUALITY ? "yes" : "no", self->priv->setup_flags & PROCESS_NOTIFICATION_FLAG_REGISTRATION_UPDATES ? "yes" : "no", self->priv->setup_flags & PROCESS_NOTIFICATION_FLAG_SMS_READ ? "yes" : "no", self->priv->setup_flags & PROCESS_NOTIFICATION_FLAG_CONNECT ? "yes" : "no", self->priv->setup_flags & PROCESS_NOTIFICATION_FLAG_SUBSCRIBER_INFO ? "yes" : "no", self->priv->setup_flags & PROCESS_NOTIFICATION_FLAG_PACKET_SERVICE ? "yes" : "no", self->priv->setup_flags & PROCESS_NOTIFICATION_FLAG_PCO ? "yes" : "no", self->priv->setup_flags & PROCESS_NOTIFICATION_FLAG_USSD ? "yes" : "no", self->priv->setup_flags & PROCESS_NOTIFICATION_FLAG_LTE_ATTACH_INFO ? "yes" : "no", self->priv->setup_flags & PROCESS_NOTIFICATION_FLAG_PROVISIONED_CONTEXTS ? "yes" : "no", self->priv->setup_flags & PROCESS_NOTIFICATION_FLAG_SLOT_INFO_STATUS ? "yes" : "no", self->priv->setup_flags & PROCESS_NOTIFICATION_FLAG_IP_CONFIGURATION ? "yes" : "no"); if (setup) { /* Don't re-enable it if already there */ if (!self->priv->notification_id) self->priv->notification_id = g_signal_connect (port, MM_PORT_MBIM_SIGNAL_NOTIFICATION, G_CALLBACK (port_notification_cb), self); } else { /* Don't remove the signal if there are still listeners interested */ if (self->priv->setup_flags == PROCESS_NOTIFICATION_FLAG_NONE && self->priv->notification_id && g_signal_handler_is_connected (port, self->priv->notification_id)) { g_signal_handler_disconnect (port, self->priv->notification_id); self->priv->notification_id = 0; } } } static gboolean common_setup_cleanup_unsolicited_events_finish (MMBroadbandModemMbim *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void common_setup_cleanup_unsolicited_events (MMBroadbandModemMbim *self, gboolean setup, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; MMPortMbim *port; task = g_task_new (self, NULL, callback, user_data); port = mm_broadband_modem_mbim_peek_port_mbim (MM_BROADBAND_MODEM_MBIM (self)); if (!port) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't peek MBIM port"); g_object_unref (task); return; } common_setup_cleanup_unsolicited_events_sync (self, port, setup); g_task_return_boolean (task, TRUE); g_object_unref (task); } /*****************************************************************************/ /* Setup/cleanup unsolicited events (3GPP interface) */ static gboolean common_setup_cleanup_unsolicited_events_3gpp_finish (MMIfaceModem3gpp *self, GAsyncResult *res, GError **error) { return common_setup_cleanup_unsolicited_events_finish (MM_BROADBAND_MODEM_MBIM (self), res, error); } static void cleanup_enabled_cache (MMBroadbandModemMbim *self) { g_clear_pointer (&self->priv->enabled_cache.current_operator_id, g_free); g_clear_pointer (&self->priv->enabled_cache.current_operator_name, g_free); g_list_free_full (self->priv->enabled_cache.pco_list, g_object_unref); self->priv->enabled_cache.pco_list = NULL; self->priv->enabled_cache.available_data_classes = MBIM_DATA_CLASS_NONE; self->priv->enabled_cache.highest_available_data_class = MBIM_DATA_CLASS_NONE; self->priv->enabled_cache.reg_state = MBIM_REGISTER_STATE_UNKNOWN; self->priv->enabled_cache.packet_service_state = MBIM_PACKET_SERVICE_STATE_UNKNOWN; self->priv->enabled_cache.packet_service_uplink_speed = 0; self->priv->enabled_cache.packet_service_downlink_speed = 0; self->priv->enabled_cache.last_pin_type = MBIM_PIN_TYPE_UNKNOWN; /* NOTE: FLAG_SUBSCRIBER_INFO is managed both via 3GPP unsolicited * events and via SIM hot swap setup. We only reset the last ready state * if SIM hot swap context is not using it. */ if (!self->priv->sim_hot_swap_configured) self->priv->enabled_cache.last_ready_state = MBIM_SUBSCRIBER_READY_STATE_NOT_INITIALIZED; } static void cleanup_unsolicited_events_3gpp (MMIfaceModem3gpp *_self, GAsyncReadyCallback callback, gpointer user_data) { MMBroadbandModemMbim *self = MM_BROADBAND_MODEM_MBIM (_self); /* NOTE: FLAG_SUBSCRIBER_INFO and FLAG_SLOT_INFO_STATUS are managed both * via 3GPP unsolicited events and via SIM hot swap setup. We only really * cleanup the indication if SIM hot swap context is not using it. */ if (!self->priv->sim_hot_swap_configured) { self->priv->setup_flags &= ~PROCESS_NOTIFICATION_FLAG_SUBSCRIBER_INFO; if (self->priv->is_slot_info_status_supported) self->priv->setup_flags &= ~PROCESS_NOTIFICATION_FLAG_SLOT_INFO_STATUS; } self->priv->setup_flags &= ~PROCESS_NOTIFICATION_FLAG_SIGNAL_QUALITY; self->priv->setup_flags &= ~PROCESS_NOTIFICATION_FLAG_CONNECT; self->priv->setup_flags &= ~PROCESS_NOTIFICATION_FLAG_PACKET_SERVICE; self->priv->setup_flags &= ~PROCESS_NOTIFICATION_FLAG_IP_CONFIGURATION; if (self->priv->is_pco_supported) self->priv->setup_flags &= ~PROCESS_NOTIFICATION_FLAG_PCO; if (self->priv->is_lte_attach_info_supported) self->priv->setup_flags &= ~PROCESS_NOTIFICATION_FLAG_LTE_ATTACH_INFO; common_setup_cleanup_unsolicited_events (self, FALSE, callback, user_data); /* Runtime cached state while enabled, to be cleaned up once disabled */ cleanup_enabled_cache (self); } static void setup_unsolicited_events_3gpp (MMIfaceModem3gpp *_self, GAsyncReadyCallback callback, gpointer user_data) { MMBroadbandModemMbim *self = MM_BROADBAND_MODEM_MBIM (_self); /* NOTE: FLAG_SUBSCRIBER_INFO is managed both via 3GPP unsolicited * events and via SIM hot swap setup. */ self->priv->setup_flags |= PROCESS_NOTIFICATION_FLAG_SUBSCRIBER_INFO; self->priv->setup_flags |= PROCESS_NOTIFICATION_FLAG_SIGNAL_QUALITY; self->priv->setup_flags |= PROCESS_NOTIFICATION_FLAG_CONNECT; self->priv->setup_flags |= PROCESS_NOTIFICATION_FLAG_PACKET_SERVICE; self->priv->setup_flags |= PROCESS_NOTIFICATION_FLAG_IP_CONFIGURATION; if (self->priv->is_pco_supported) self->priv->setup_flags |= PROCESS_NOTIFICATION_FLAG_PCO; if (self->priv->is_lte_attach_info_supported) self->priv->setup_flags |= PROCESS_NOTIFICATION_FLAG_LTE_ATTACH_INFO; if (self->priv->is_slot_info_status_supported) self->priv->setup_flags |= PROCESS_NOTIFICATION_FLAG_SLOT_INFO_STATUS; common_setup_cleanup_unsolicited_events (self, TRUE, callback, user_data); } /*****************************************************************************/ /* Cleanup/Setup unsolicited registration events */ static void cleanup_unsolicited_registration_events (MMIfaceModem3gpp *_self, GAsyncReadyCallback callback, gpointer user_data) { MMBroadbandModemMbim *self = MM_BROADBAND_MODEM_MBIM (_self); self->priv->setup_flags &= ~PROCESS_NOTIFICATION_FLAG_REGISTRATION_UPDATES; common_setup_cleanup_unsolicited_events (self, FALSE, callback, user_data); } static void setup_unsolicited_registration_events (MMIfaceModem3gpp *_self, GAsyncReadyCallback callback, gpointer user_data) { MMBroadbandModemMbim *self = MM_BROADBAND_MODEM_MBIM (_self); self->priv->setup_flags |= PROCESS_NOTIFICATION_FLAG_REGISTRATION_UPDATES; common_setup_cleanup_unsolicited_events (self, TRUE, callback, user_data); } /*****************************************************************************/ /* Enable/disable unsolicited events (common) */ static gboolean common_enable_disable_unsolicited_events_finish (MMBroadbandModemMbim *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void subscribe_list_set_ready_cb (MbimDevice *device, GAsyncResult *res, GTask *task) { MbimMessage *response; GError *error = NULL; response = mbim_device_command_finish (device, res, &error); if (response) { mbim_message_response_get_result (response, MBIM_MESSAGE_TYPE_COMMAND_DONE, &error); mbim_message_unref (response); } if (error) g_task_return_error (task, error); else g_task_return_boolean (task, TRUE); g_object_unref (task); } static void common_enable_disable_unsolicited_events (MMBroadbandModemMbim *self, GAsyncReadyCallback callback, gpointer user_data) { MbimMessage *request; MbimDevice *device; MbimEventEntry **entries; guint n_entries = 0; GTask *task; if (!peek_device (self, &device, callback, user_data)) return; mm_obj_dbg (self, "enabled notifications: signal (%s), registration (%s), sms (%s), " "connect (%s), subscriber (%s), packet (%s), pco (%s), ussd (%s), " "lte attach info (%s), provisioned contexts (%s), slot info status (%s), " "ip configuration (%s)", self->priv->enable_flags & PROCESS_NOTIFICATION_FLAG_SIGNAL_QUALITY ? "yes" : "no", self->priv->enable_flags & PROCESS_NOTIFICATION_FLAG_REGISTRATION_UPDATES ? "yes" : "no", self->priv->enable_flags & PROCESS_NOTIFICATION_FLAG_SMS_READ ? "yes" : "no", self->priv->enable_flags & PROCESS_NOTIFICATION_FLAG_CONNECT ? "yes" : "no", self->priv->enable_flags & PROCESS_NOTIFICATION_FLAG_SUBSCRIBER_INFO ? "yes" : "no", self->priv->enable_flags & PROCESS_NOTIFICATION_FLAG_PACKET_SERVICE ? "yes" : "no", self->priv->enable_flags & PROCESS_NOTIFICATION_FLAG_PCO ? "yes" : "no", self->priv->enable_flags & PROCESS_NOTIFICATION_FLAG_USSD ? "yes" : "no", self->priv->enable_flags & PROCESS_NOTIFICATION_FLAG_LTE_ATTACH_INFO ? "yes" : "no", self->priv->enable_flags & PROCESS_NOTIFICATION_FLAG_PROVISIONED_CONTEXTS ? "yes" : "no", self->priv->enable_flags & PROCESS_NOTIFICATION_FLAG_SLOT_INFO_STATUS ? "yes" : "no", self->priv->enable_flags & PROCESS_NOTIFICATION_FLAG_IP_CONFIGURATION ? "yes" : "no"); entries = g_new0 (MbimEventEntry *, 5); /* Basic connect service */ if (self->priv->enable_flags & PROCESS_NOTIFICATION_FLAG_SIGNAL_QUALITY || self->priv->enable_flags & PROCESS_NOTIFICATION_FLAG_REGISTRATION_UPDATES || self->priv->enable_flags & PROCESS_NOTIFICATION_FLAG_CONNECT || self->priv->enable_flags & PROCESS_NOTIFICATION_FLAG_SUBSCRIBER_INFO || self->priv->enable_flags & PROCESS_NOTIFICATION_FLAG_PACKET_SERVICE || self->priv->enable_flags & PROCESS_NOTIFICATION_FLAG_PROVISIONED_CONTEXTS || self->priv->enable_flags & PROCESS_NOTIFICATION_FLAG_IP_CONFIGURATION) { entries[n_entries] = g_new (MbimEventEntry, 1); memcpy (&(entries[n_entries]->device_service_id), MBIM_UUID_BASIC_CONNECT, sizeof (MbimUuid)); entries[n_entries]->cids_count = 0; entries[n_entries]->cids = g_new0 (guint32, 7); if (self->priv->enable_flags & PROCESS_NOTIFICATION_FLAG_SIGNAL_QUALITY) entries[n_entries]->cids[entries[n_entries]->cids_count++] = MBIM_CID_BASIC_CONNECT_SIGNAL_STATE; if (self->priv->enable_flags & PROCESS_NOTIFICATION_FLAG_REGISTRATION_UPDATES) entries[n_entries]->cids[entries[n_entries]->cids_count++] = MBIM_CID_BASIC_CONNECT_REGISTER_STATE; if (self->priv->enable_flags & PROCESS_NOTIFICATION_FLAG_CONNECT) entries[n_entries]->cids[entries[n_entries]->cids_count++] = MBIM_CID_BASIC_CONNECT_CONNECT; if (self->priv->enable_flags & PROCESS_NOTIFICATION_FLAG_SUBSCRIBER_INFO) entries[n_entries]->cids[entries[n_entries]->cids_count++] = MBIM_CID_BASIC_CONNECT_SUBSCRIBER_READY_STATUS; if (self->priv->enable_flags & PROCESS_NOTIFICATION_FLAG_PACKET_SERVICE) entries[n_entries]->cids[entries[n_entries]->cids_count++] = MBIM_CID_BASIC_CONNECT_PACKET_SERVICE; if (self->priv->enable_flags & PROCESS_NOTIFICATION_FLAG_PROVISIONED_CONTEXTS) entries[n_entries]->cids[entries[n_entries]->cids_count++] = MBIM_CID_BASIC_CONNECT_PROVISIONED_CONTEXTS; if (self->priv->enable_flags & PROCESS_NOTIFICATION_FLAG_IP_CONFIGURATION) entries[n_entries]->cids[entries[n_entries]->cids_count++] = MBIM_CID_BASIC_CONNECT_IP_CONFIGURATION; n_entries++; } /* Basic connect extensions service */ if (self->priv->enable_flags & PROCESS_NOTIFICATION_FLAG_PCO || self->priv->enable_flags & PROCESS_NOTIFICATION_FLAG_LTE_ATTACH_INFO || self->priv->enable_flags & PROCESS_NOTIFICATION_FLAG_SLOT_INFO_STATUS) { entries[n_entries] = g_new (MbimEventEntry, 1); memcpy (&(entries[n_entries]->device_service_id), MBIM_UUID_MS_BASIC_CONNECT_EXTENSIONS, sizeof (MbimUuid)); entries[n_entries]->cids_count = 0; entries[n_entries]->cids = g_new0 (guint32, 3); if (self->priv->enable_flags & PROCESS_NOTIFICATION_FLAG_PCO) entries[n_entries]->cids[entries[n_entries]->cids_count++] = MBIM_CID_MS_BASIC_CONNECT_EXTENSIONS_PCO; if (self->priv->enable_flags & PROCESS_NOTIFICATION_FLAG_LTE_ATTACH_INFO) entries[n_entries]->cids[entries[n_entries]->cids_count++] = MBIM_CID_MS_BASIC_CONNECT_EXTENSIONS_LTE_ATTACH_INFO; if (self->priv->enable_flags & PROCESS_NOTIFICATION_FLAG_SLOT_INFO_STATUS) entries[n_entries]->cids[entries[n_entries]->cids_count++] = MBIM_CID_MS_BASIC_CONNECT_EXTENSIONS_SLOT_INFO_STATUS; n_entries++; } /* SMS service */ if (self->priv->enable_flags & PROCESS_NOTIFICATION_FLAG_SMS_READ) { entries[n_entries] = g_new (MbimEventEntry, 1); memcpy (&(entries[n_entries]->device_service_id), MBIM_UUID_SMS, sizeof (MbimUuid)); entries[n_entries]->cids_count = 2; entries[n_entries]->cids = g_new0 (guint32, 2); entries[n_entries]->cids[0] = MBIM_CID_SMS_READ; entries[n_entries]->cids[1] = MBIM_CID_SMS_MESSAGE_STORE_STATUS; n_entries++; } /* USSD service */ if (self->priv->enable_flags & PROCESS_NOTIFICATION_FLAG_USSD) { entries[n_entries] = g_new (MbimEventEntry, 1); memcpy (&(entries[n_entries]->device_service_id), MBIM_UUID_USSD, sizeof (MbimUuid)); entries[n_entries]->cids_count = 1; entries[n_entries]->cids = g_new0 (guint32, 1); entries[n_entries]->cids[0] = MBIM_CID_USSD; n_entries++; } task = g_task_new (self, NULL, callback, user_data); request = (mbim_message_device_service_subscribe_list_set_new ( n_entries, (const MbimEventEntry *const *)entries, NULL)); mbim_device_command (device, request, 10, NULL, (GAsyncReadyCallback)subscribe_list_set_ready_cb, task); mbim_message_unref (request); mbim_event_entry_array_free (entries); } /*****************************************************************************/ /* Enable/Disable unsolicited registration events */ static gboolean modem_3gpp_common_enable_disable_unsolicited_registration_events_finish (MMIfaceModem3gpp *self, GAsyncResult *res, GError **error) { return common_enable_disable_unsolicited_events_finish (MM_BROADBAND_MODEM_MBIM (self), res, error); } static void modem_3gpp_disable_unsolicited_registration_events (MMIfaceModem3gpp *_self, gboolean cs_supported, gboolean ps_supported, gboolean eps_supported, GAsyncReadyCallback callback, gpointer user_data) { MMBroadbandModemMbim *self = MM_BROADBAND_MODEM_MBIM (_self); self->priv->enable_flags &= ~PROCESS_NOTIFICATION_FLAG_REGISTRATION_UPDATES; common_enable_disable_unsolicited_events (self, callback, user_data); } static void modem_3gpp_enable_unsolicited_registration_events (MMIfaceModem3gpp *_self, gboolean cs_supported, gboolean ps_supported, gboolean eps_supported, GAsyncReadyCallback callback, gpointer user_data) { MMBroadbandModemMbim *self = MM_BROADBAND_MODEM_MBIM (_self); self->priv->enable_flags |= PROCESS_NOTIFICATION_FLAG_REGISTRATION_UPDATES; common_enable_disable_unsolicited_events (self, callback, user_data); } /*****************************************************************************/ /* Setup SIM hot swap */ typedef struct { MMPortMbim *port; GError *subscriber_info_error; #if defined WITH_QMI && QMI_MBIM_QMUX_SUPPORTED GError *qmi_error; #endif } SetupSimHotSwapContext; static void setup_sim_hot_swap_context_free (SetupSimHotSwapContext *ctx) { #if defined WITH_QMI && QMI_MBIM_QMUX_SUPPORTED g_clear_error (&ctx->qmi_error); #endif g_clear_error (&ctx->subscriber_info_error); g_clear_object (&ctx->port); g_slice_free (SetupSimHotSwapContext, ctx); } static gboolean modem_setup_sim_hot_swap_finish (MMIfaceModem *_self, GAsyncResult *res, GError **error) { MMBroadbandModemMbim *self = MM_BROADBAND_MODEM_MBIM (_self); self->priv->sim_hot_swap_configured = g_task_propagate_boolean (G_TASK (res), error); return self->priv->sim_hot_swap_configured; } static void sim_hot_swap_complete (GTask *task) { SetupSimHotSwapContext *ctx; ctx = g_task_get_task_data (task); /* If MBIM based logic worked, success */ if (!ctx->subscriber_info_error) g_task_return_boolean (task, TRUE); #if defined WITH_QMI && QMI_MBIM_QMUX_SUPPORTED /* Otherwise, If QMI-over-MBIM based logic worked, success */ else if (!ctx->qmi_error) g_task_return_boolean (task, TRUE); #endif /* Otherwise, prefer MBIM specific error */ else g_task_return_error (task, g_steal_pointer (&ctx->subscriber_info_error)); g_object_unref (task); } #if defined WITH_QMI && QMI_MBIM_QMUX_SUPPORTED static void qmi_setup_sim_hot_swap_ready (MMIfaceModem *self, GAsyncResult *res, GTask *task) { SetupSimHotSwapContext *ctx; ctx = g_task_get_task_data (task); if (!mm_shared_qmi_setup_sim_hot_swap_finish (self, res, &ctx->qmi_error)) mm_obj_dbg (self, "couldn't setup SIM hot swap using QMI over MBIM: %s", ctx->qmi_error->message); sim_hot_swap_complete (task); } #endif static void enable_subscriber_info_unsolicited_events_ready (MMBroadbandModemMbim *self, GAsyncResult *res, GTask *task) { SetupSimHotSwapContext *ctx; ctx = g_task_get_task_data (task); if (!common_enable_disable_unsolicited_events_finish (self, res, &ctx->subscriber_info_error)) { mm_obj_dbg (self, "failed to enable subscriber info events: %s", ctx->subscriber_info_error->message); /* reset setup flags if enabling failed */ self->priv->setup_flags &= ~PROCESS_NOTIFICATION_FLAG_SUBSCRIBER_INFO; if (self->priv->is_slot_info_status_supported) self->priv->setup_flags &= ~PROCESS_NOTIFICATION_FLAG_SLOT_INFO_STATUS; common_setup_cleanup_unsolicited_events_sync (self, ctx->port, FALSE); /* and also reset enable flags */ self->priv->enable_flags &= ~PROCESS_NOTIFICATION_FLAG_SUBSCRIBER_INFO; if (self->priv->is_slot_info_status_supported) self->priv->enable_flags &= ~PROCESS_NOTIFICATION_FLAG_SLOT_INFO_STATUS; } #if defined WITH_QMI && QMI_MBIM_QMUX_SUPPORTED mm_shared_qmi_setup_sim_hot_swap (MM_IFACE_MODEM (self), (GAsyncReadyCallback)qmi_setup_sim_hot_swap_ready, task); #else sim_hot_swap_complete (task); #endif } static void modem_setup_sim_hot_swap (MMIfaceModem *_self, GAsyncReadyCallback callback, gpointer user_data) { MMBroadbandModemMbim *self = MM_BROADBAND_MODEM_MBIM (_self); MMPortMbim *port; GTask *task; SetupSimHotSwapContext *ctx; task = g_task_new (self, NULL, callback, user_data); port = mm_broadband_modem_mbim_peek_port_mbim (MM_BROADBAND_MODEM_MBIM (self)); if (!port) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't peek MBIM port"); g_object_unref (task); return; } ctx = g_slice_new0 (SetupSimHotSwapContext); ctx->port = g_object_ref (port); g_task_set_task_data (task, ctx, (GDestroyNotify)setup_sim_hot_swap_context_free); /* Setup flags synchronously, which never fails */ self->priv->setup_flags |= PROCESS_NOTIFICATION_FLAG_SUBSCRIBER_INFO; if (self->priv->is_slot_info_status_supported) self->priv->setup_flags |= PROCESS_NOTIFICATION_FLAG_SLOT_INFO_STATUS; common_setup_cleanup_unsolicited_events_sync (self, ctx->port, TRUE); /* Enable flags asynchronously, which may fail */ self->priv->enable_flags |= PROCESS_NOTIFICATION_FLAG_SUBSCRIBER_INFO; if (self->priv->is_slot_info_status_supported) self->priv->enable_flags |= PROCESS_NOTIFICATION_FLAG_SLOT_INFO_STATUS; common_enable_disable_unsolicited_events (self, (GAsyncReadyCallback)enable_subscriber_info_unsolicited_events_ready, task); } /*****************************************************************************/ /* Check basic SIM details */ typedef struct { gboolean sim_inserted; gchar *iccid; gchar *imsi; } SimDetails; static void sim_details_free (SimDetails *sim_info) { g_free (sim_info->iccid); g_free (sim_info->imsi); g_slice_free (SimDetails, sim_info); } static gboolean modem_check_basic_sim_details_finish (MMIfaceModem *self, GAsyncResult *res, gboolean *sim_inserted, gchar **iccid, gchar **imsi, GError **error) { SimDetails *sim_info; sim_info = g_task_propagate_pointer (G_TASK (res), error); if (!sim_info) return FALSE; *sim_inserted = sim_info->sim_inserted; if (iccid) *iccid = g_steal_pointer (&sim_info->iccid); if (imsi) *imsi = g_steal_pointer (&sim_info->imsi); sim_details_free (sim_info); return TRUE; } static void basic_sim_details_subscriber_ready_state_ready (MbimDevice *device, GAsyncResult *res, GTask *task) { MMBroadbandModemMbim *self; g_autoptr(MbimMessage) response = NULL; GError *error = NULL; gchar *imsi = NULL; g_autofree gchar *raw_iccid = NULL; MbimSubscriberReadyState ready_state = MBIM_SUBSCRIBER_READY_STATE_NOT_INITIALIZED; self = g_task_get_source_object (task); response = mbim_device_command_finish (device, res, &error); if (!response || !mbim_message_response_get_result (response, MBIM_MESSAGE_TYPE_COMMAND_DONE, &error)) { g_task_return_error (task, error); g_object_unref (task); return; } if (mbim_device_check_ms_mbimex_version (device, 3, 0)) { if (!mbim_message_ms_basic_connect_v3_subscriber_ready_status_response_parse ( response, &ready_state, /* ready_state */ NULL, /* flags */ &imsi, /* subscriber id */ &raw_iccid, /* sim_iccid */ NULL, /* ready_info */ NULL, /* telephone_numbers_count */ NULL, &error)) g_prefix_error (&error, "Failed processing MBIMEx v3.0 subscriber ready status response: "); else mm_obj_dbg (self, "processed MBIMEx v3.0 subscriber ready status response"); } else { if (!mbim_message_subscriber_ready_status_response_parse ( response, &ready_state, /* ready_state */ &imsi, /* subscriber id */ &raw_iccid, /* sim_iccid */ NULL, /* ready_info */ NULL, /* telephone_numbers_count */ NULL, &error)) g_prefix_error (&error, "Failed processing subscriber ready status response: "); else mm_obj_dbg (self, "processed subscriber ready status response"); } if (error) g_task_return_error (task, error); else { SimDetails *sim_details; g_autoptr(GError) inner_error = NULL; sim_details = g_slice_new0 (SimDetails); if (ready_state != MBIM_SUBSCRIBER_READY_STATE_SIM_NOT_INSERTED) { sim_details->sim_inserted = TRUE; if (raw_iccid) { sim_details->iccid = mm_3gpp_parse_iccid (raw_iccid, &inner_error); if (!sim_details->iccid) { mm_obj_warn (self, "can not get ICCID info: couldn't parse SIM ICCID: %s", inner_error->message); } } sim_details->imsi = imsi; } g_task_return_pointer (task, sim_details, (GDestroyNotify)sim_details_free); } g_object_unref (task); } static void modem_check_basic_sim_details (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data) { MbimDevice *device; GTask *task; g_autoptr(MbimMessage) message = NULL; if (!peek_device (self, &device, callback, user_data)) return; task = g_task_new (self, NULL, callback, user_data); message = mbim_message_subscriber_ready_status_query_new (NULL); mbim_device_command (device, message, 10, NULL, (GAsyncReadyCallback)basic_sim_details_subscriber_ready_state_ready, task); } /*****************************************************************************/ /* Enable/Disable unsolicited events (3GPP interface) */ static gboolean modem_3gpp_common_enable_disable_unsolicited_events_finish (MMIfaceModem3gpp *self, GAsyncResult *res, GError **error) { return common_enable_disable_unsolicited_events_finish (MM_BROADBAND_MODEM_MBIM (self), res, error); } static void modem_3gpp_disable_unsolicited_events (MMIfaceModem3gpp *_self, GAsyncReadyCallback callback, gpointer user_data) { MMBroadbandModemMbim *self = MM_BROADBAND_MODEM_MBIM (_self); /* NOTE: FLAG_SUBSCRIBER_INFO and FLAG_SLOT_INFO_STATUS are managed both * via 3GPP unsolicited events and via SIM hot swap setup. We only really * cleanup the indication if SIM hot swap context is not using it. */ if (!self->priv->sim_hot_swap_configured) { self->priv->enable_flags &= ~PROCESS_NOTIFICATION_FLAG_SUBSCRIBER_INFO; if (self->priv->is_slot_info_status_supported) self->priv->enable_flags &= ~PROCESS_NOTIFICATION_FLAG_SLOT_INFO_STATUS; } self->priv->enable_flags &= ~PROCESS_NOTIFICATION_FLAG_SIGNAL_QUALITY; self->priv->enable_flags &= ~PROCESS_NOTIFICATION_FLAG_CONNECT; self->priv->enable_flags &= ~PROCESS_NOTIFICATION_FLAG_PACKET_SERVICE; self->priv->enable_flags &= ~PROCESS_NOTIFICATION_FLAG_IP_CONFIGURATION; if (self->priv->is_pco_supported) self->priv->enable_flags &= ~PROCESS_NOTIFICATION_FLAG_PCO; if (self->priv->is_lte_attach_info_supported) self->priv->enable_flags &= ~PROCESS_NOTIFICATION_FLAG_LTE_ATTACH_INFO; common_enable_disable_unsolicited_events (self, callback, user_data); } static void modem_3gpp_enable_unsolicited_events (MMIfaceModem3gpp *_self, GAsyncReadyCallback callback, gpointer user_data) { MMBroadbandModemMbim *self = MM_BROADBAND_MODEM_MBIM (_self); /* NOTE: FLAG_SUBSCRIBER_INFO is managed both via 3GPP unsolicited * events and via SIM hot swap setup. */ self->priv->enable_flags |= PROCESS_NOTIFICATION_FLAG_SUBSCRIBER_INFO; self->priv->enable_flags |= PROCESS_NOTIFICATION_FLAG_SIGNAL_QUALITY; self->priv->enable_flags |= PROCESS_NOTIFICATION_FLAG_CONNECT; self->priv->enable_flags |= PROCESS_NOTIFICATION_FLAG_PACKET_SERVICE; self->priv->enable_flags |= PROCESS_NOTIFICATION_FLAG_IP_CONFIGURATION; if (self->priv->is_pco_supported) self->priv->enable_flags |= PROCESS_NOTIFICATION_FLAG_PCO; if (self->priv->is_lte_attach_info_supported) self->priv->enable_flags |= PROCESS_NOTIFICATION_FLAG_LTE_ATTACH_INFO; if (self->priv->is_slot_info_status_supported) self->priv->enable_flags |= PROCESS_NOTIFICATION_FLAG_SLOT_INFO_STATUS; common_enable_disable_unsolicited_events (self, callback, user_data); } /*****************************************************************************/ /* Load operator name (3GPP interface) */ static gchar * modem_3gpp_load_operator_name_finish (MMIfaceModem3gpp *self, GAsyncResult *res, GError **error) { return g_task_propagate_pointer (G_TASK (res), error); } static void modem_3gpp_load_operator_name (MMIfaceModem3gpp *_self, GAsyncReadyCallback callback, gpointer user_data) { MMBroadbandModemMbim *self = MM_BROADBAND_MODEM_MBIM (_self); GTask *task; task = g_task_new (self, NULL, callback, user_data); if (self->priv->enabled_cache.current_operator_name) g_task_return_pointer (task, g_strdup (self->priv->enabled_cache.current_operator_name), g_free); else g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Current operator name is still unknown"); g_object_unref (task); } /*****************************************************************************/ /* Load operator code (3GPP interface) */ static gchar * modem_3gpp_load_operator_code_finish (MMIfaceModem3gpp *self, GAsyncResult *res, GError **error) { return g_task_propagate_pointer (G_TASK (res), error); } static void modem_3gpp_load_operator_code (MMIfaceModem3gpp *_self, GAsyncReadyCallback callback, gpointer user_data) { MMBroadbandModemMbim *self = MM_BROADBAND_MODEM_MBIM (_self); GTask *task; task = g_task_new (self, NULL, callback, user_data); if (self->priv->enabled_cache.current_operator_id) g_task_return_pointer (task, g_strdup (self->priv->enabled_cache.current_operator_id), g_free); else g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Current operator MCC/MNC is still unknown"); g_object_unref (task); } /*****************************************************************************/ /* Registration checks (3GPP interface) */ static gboolean modem_3gpp_run_registration_checks_finish (MMIfaceModem3gpp *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void packet_service_query_ready (MbimDevice *device, GAsyncResult *res, GTask *task) { MMBroadbandModemMbim *self; g_autoptr(MbimMessage) response = NULL; g_autoptr(GError) error = NULL; self = g_task_get_source_object (task); response = mbim_device_command_finish (device, res, &error); if (response && (mbim_message_response_get_result (response, MBIM_MESSAGE_TYPE_COMMAND_DONE, &error) || g_error_matches (error, MBIM_STATUS_ERROR, MBIM_STATUS_ERROR_FAILURE))) { g_autoptr(GError) inner_error = NULL; if (!common_process_packet_service (self, device, response, NULL, NULL, &inner_error)) mm_obj_warn (self, "%s", inner_error->message); } g_task_return_boolean (task, TRUE); g_object_unref (task); } static void register_state_query_ready (MbimDevice *device, GAsyncResult *res, GTask *task) { g_autoptr(MbimMessage) response = NULL; g_autoptr(MbimMessage) message = NULL; MMBroadbandModemMbim *self; GError *error = NULL; self = g_task_get_source_object (task); response = mbim_device_command_finish (device, res, &error); if (!response || !mbim_message_response_get_result (response, MBIM_MESSAGE_TYPE_COMMAND_DONE, &error)) { g_task_return_error (task, error); g_object_unref (task); return; } if (!common_process_register_state (self, device, response, NULL, &error)) { g_task_return_error (task, error); g_object_unref (task); return; } /* Now queue packet service state update */ message = mbim_message_packet_service_query_new (NULL); mbim_device_command (device, message, 10, NULL, (GAsyncReadyCallback)packet_service_query_ready, task); } static void modem_3gpp_run_registration_checks (MMIfaceModem3gpp *self, gboolean is_cs_supported, gboolean is_ps_supported, gboolean is_eps_supported, gboolean is_5gs_supported, GAsyncReadyCallback callback, gpointer user_data) { g_autoptr(MbimMessage) message = NULL; MbimDevice *device; GTask *task; if (!peek_device (self, &device, callback, user_data)) return; task = g_task_new (self, NULL, callback, user_data); message = mbim_message_register_state_query_new (NULL); mbim_device_command (device, message, 10, NULL, (GAsyncReadyCallback)register_state_query_ready, task); } /*****************************************************************************/ static gboolean modem_3gpp_register_in_network_finish (MMIfaceModem3gpp *self, GAsyncResult *res, GError **error) { #if defined WITH_QMI && QMI_MBIM_QMUX_SUPPORTED if (MM_BROADBAND_MODEM_MBIM (self)->priv->qmi_capability_and_mode_switching) return mm_shared_qmi_3gpp_register_in_network_finish (self, res, error); #endif return g_task_propagate_boolean (G_TASK (res), error); } static void register_state_set_ready (MbimDevice *device, GAsyncResult *res, GTask *task) { MMBroadbandModemMbim *self; g_autoptr(MbimMessage) response = NULL; GError *error = NULL; self = g_task_get_source_object (task); /* The NwError field is valid if MBIM_SET_REGISTER_STATE response status code * equals MBIM_STATUS_FAILURE, so we parse the message both on success and on that * specific failure */ response = mbim_device_command_finish (device, res, &error); if (response && (mbim_message_response_get_result (response, MBIM_MESSAGE_TYPE_COMMAND_DONE, &error) || g_error_matches (error, MBIM_STATUS_ERROR, MBIM_STATUS_ERROR_FAILURE))) { g_autoptr(GError) inner_error = NULL; MbimNwError normalized_nw_error = 0; if (!common_process_register_state (self, device, response, &normalized_nw_error, &inner_error)) { mm_obj_warn (self, "%s", inner_error->message); /* Prefer the error from the result to the parsing error */ if (!error) error = g_steal_pointer (&inner_error); } else { /* Prefer the NW error if available. * * NwError "0" is defined in 3GPP TS 24.008 as "Unknown error", * not "No error", making it unsuitable as condition for registration check. * Still, there are certain modems (e.g. Fibocom NL668) that will * report Failure+NwError=0 even after the modem has already reported a * succesful registration via indications after the set operation. If * that is the case, log about it and ignore the error; we are anyway * reloading the registration info after the set, so it should not be * a big issue. */ if (normalized_nw_error) { g_clear_error (&error); error = mm_error_from_mbim_nw_error (normalized_nw_error, self); } } } if (error) g_task_return_error (task, error); else g_task_return_boolean (task, TRUE); g_object_unref (task); } static void modem_3gpp_register_in_network (MMIfaceModem3gpp *_self, const gchar *operator_id, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { MMBroadbandModemMbim *self = MM_BROADBAND_MODEM_MBIM (_self); MbimDevice *device; GTask *task; g_autoptr(MbimMessage) message = NULL; #if defined WITH_QMI && QMI_MBIM_QMUX_SUPPORTED /* data_class set to 0 in the MBIM register state set message ends up * selecting some "auto" mode that would overwrite whatever capabilities * and modes we had set. So, if we're using QMI-based capability and * mode switching, also use QMI-based network registration. */ if (self->priv->qmi_capability_and_mode_switching) { mm_shared_qmi_3gpp_register_in_network (_self, operator_id, cancellable, callback, user_data); return; } #endif if (!peek_device (self, &device, callback, user_data)) return; task = g_task_new (self, NULL, callback, user_data); /* keep track of which operator id is selected */ g_clear_pointer (&self->priv->requested_operator_id, g_free); if (operator_id && operator_id[0]) self->priv->requested_operator_id = g_strdup (operator_id); message = (mbim_message_register_state_set_new ( self->priv->requested_operator_id ? self->priv->requested_operator_id : "", self->priv->requested_operator_id ? MBIM_REGISTER_ACTION_MANUAL : MBIM_REGISTER_ACTION_AUTOMATIC, self->priv->requested_data_class, NULL)); mbim_device_command (device, message, 60, NULL, (GAsyncReadyCallback)register_state_set_ready, task); } /*****************************************************************************/ /* Scan networks (3GPP interface) */ static GList * modem_3gpp_scan_networks_finish (MMIfaceModem3gpp *self, GAsyncResult *res, GError **error) { return g_task_propagate_pointer (G_TASK (res), error); } static void visible_providers_query_ready (MbimDevice *device, GAsyncResult *res, GTask *task) { MbimMessage *response; MbimProvider **providers; guint n_providers; GError *error = NULL; response = mbim_device_command_finish (device, res, &error); if (response && mbim_message_response_get_result (response, MBIM_MESSAGE_TYPE_COMMAND_DONE, &error) && mbim_message_visible_providers_response_parse (response, &n_providers, &providers, &error)) { GList *info_list; info_list = mm_3gpp_network_info_list_from_mbim_providers ((const MbimProvider *const *)providers, n_providers); mbim_provider_array_free (providers); g_task_return_pointer (task, info_list, (GDestroyNotify)mm_3gpp_network_info_list_free); } else g_task_return_error (task, error); g_object_unref (task); if (response) mbim_message_unref (response); } static void modem_3gpp_scan_networks (MMIfaceModem3gpp *self, GAsyncReadyCallback callback, gpointer user_data) { MbimDevice *device; MbimMessage *message; GTask *task; if (!peek_device (self, &device, callback, user_data)) return; task = g_task_new (self, NULL, callback, user_data); mm_obj_dbg (self, "scanning networks..."); message = mbim_message_visible_providers_query_new (MBIM_VISIBLE_PROVIDERS_ACTION_FULL_SCAN, NULL); mbim_device_command (device, message, 300, NULL, (GAsyncReadyCallback)visible_providers_query_ready, task); mbim_message_unref (message); } /*****************************************************************************/ /* Check support (Signal interface) */ static gboolean modem_signal_check_support_finish (MMIfaceModemSignal *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void parent_signal_check_support_ready (MMIfaceModemSignal *self, GAsyncResult *res, GTask *task) { gboolean parent_supported; parent_supported = iface_modem_signal_parent->check_support_finish (self, res, NULL); g_task_return_boolean (task, parent_supported); g_object_unref (task); } static void modem_signal_check_support (MMIfaceModemSignal *self, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; task = g_task_new (self, NULL, callback, user_data); /* If ATDS signal is supported, we support the Signal interface */ if (MM_BROADBAND_MODEM_MBIM (self)->priv->is_atds_signal_supported) { g_task_return_boolean (task, TRUE); g_object_unref (task); return; } /* Otherwise, check if the parent CESQ-based implementation works */ g_assert (iface_modem_signal_parent->check_support && iface_modem_signal_parent->check_support_finish); iface_modem_signal_parent->check_support (self, (GAsyncReadyCallback)parent_signal_check_support_ready, task); } /*****************************************************************************/ /* Load extended signal information (Signal interface) */ typedef struct { MMSignal *gsm; MMSignal *umts; MMSignal *lte; MMSignal *nr5g; } SignalLoadValuesResult; static void signal_load_values_result_free (SignalLoadValuesResult *result) { g_clear_object (&result->gsm); g_clear_object (&result->umts); g_clear_object (&result->lte); g_clear_object (&result->nr5g); g_slice_free (SignalLoadValuesResult, result); } static gboolean modem_signal_load_values_finish (MMIfaceModemSignal *self, GAsyncResult *res, MMSignal **cdma, MMSignal **evdo, MMSignal **gsm, MMSignal **umts, MMSignal **lte, MMSignal **nr5g, GError **error) { SignalLoadValuesResult *result; result = g_task_propagate_pointer (G_TASK (res), error); if (!result) return FALSE; if (gsm) *gsm = g_steal_pointer (&result->gsm); if (umts) *umts = g_steal_pointer (&result->umts); if (lte) *lte = g_steal_pointer (&result->lte); if (nr5g) *nr5g = g_steal_pointer (&result->nr5g); signal_load_values_result_free (result); /* No 3GPP2 support */ if (cdma) *cdma = NULL; if (evdo) *evdo = NULL; return TRUE; } static void atds_signal_query_ready (MbimDevice *device, GAsyncResult *res, GTask *task) { MbimMessage *response; SignalLoadValuesResult *result; GError *error = NULL; guint32 rssi; guint32 error_rate; guint32 rscp; guint32 ecno; guint32 rsrq; guint32 rsrp; guint32 snr; response = mbim_device_command_finish (device, res, &error); if (!response || !mbim_message_response_get_result (response, MBIM_MESSAGE_TYPE_COMMAND_DONE, &error) || !mbim_message_atds_signal_response_parse (response, &rssi, &error_rate, &rscp, &ecno, &rsrq, &rsrp, &snr, &error)) { g_task_return_error (task, error); goto out; } result = g_slice_new0 (SignalLoadValuesResult); if (!mm_signal_from_atds_signal_response (rssi, rscp, ecno, rsrq, rsrp, snr, &result->gsm, &result->umts, &result->lte)) { signal_load_values_result_free (result); g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "No signal details given"); goto out; } g_task_return_pointer (task, result, (GDestroyNotify) signal_load_values_result_free); out: if (response) mbim_message_unref (response); g_object_unref (task); } static void parent_signal_load_values_ready (MMIfaceModemSignal *self, GAsyncResult *res, GTask *task) { SignalLoadValuesResult *result; GError *error = NULL; result = g_slice_new0 (SignalLoadValuesResult); if (!iface_modem_signal_parent->load_values_finish (self, res, NULL, NULL, &result->gsm, &result->umts, &result->lte, &result->nr5g, &error)) { signal_load_values_result_free (result); g_task_return_error (task, error); } else if (!result->gsm && !result->umts && !result->lte) { signal_load_values_result_free (result); g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "No signal details given"); } else g_task_return_pointer (task, result, (GDestroyNotify) signal_load_values_result_free); g_object_unref (task); } static void mbimexv2_signal_state_query_ready (MbimDevice *device, GAsyncResult *res, GTask *task) { MMBroadbandModemMbim *self; GError *error = NULL; SignalLoadValuesResult *result; g_autoptr(MbimMessage) response = NULL; g_autoptr(MbimRsrpSnrInfoArray) rsrp_snr = NULL; guint32 rsrp_snr_count = 0; guint32 rssi; guint32 error_rate = 99; MbimDataClass data_class; self = g_task_get_source_object (task); response = mbim_device_command_finish (device, res, &error); if (!response || !mbim_message_response_get_result (response, MBIM_MESSAGE_TYPE_COMMAND_DONE, &error)) { g_task_return_error (task, error); g_object_unref (task); return; } if (!mbim_device_check_ms_mbimex_version (device, 2, 0)) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Failed MBIMEx v2.0 signal state response support check"); g_object_unref (task); return; } if (!mbim_message_ms_basic_connect_v2_signal_state_response_parse ( response, &rssi, &error_rate, NULL, /* signal_strength_interval */ NULL, /* rssi_threshold */ NULL, /* error_rate_threshold */ &rsrp_snr_count, &rsrp_snr, &error)) { g_prefix_error (&error, "Failed processing MBIMEx v2.0 signal state response: "); g_task_return_error (task, error); g_object_unref (task); return; } result = g_slice_new0 (SignalLoadValuesResult); /* Best guess of current data class */ data_class = self->priv->enabled_cache.highest_available_data_class; if (data_class == 0) data_class = self->priv->enabled_cache.available_data_classes; if (!mm_signal_from_mbim_signal_state ( data_class, rssi, error_rate, rsrp_snr, rsrp_snr_count, self, NULL, NULL, &result->gsm, &result->umts, &result->lte, &result->nr5g)) { signal_load_values_result_free (result); g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "No signal details given"); g_object_unref (task); return; } g_task_return_pointer (task, result, (GDestroyNotify) signal_load_values_result_free); g_object_unref (task); } static void modem_signal_load_values (MMIfaceModemSignal *self, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { MbimDevice *device; MbimMessage *message; GTask *task; if (!peek_device (self, &device, callback, user_data)) return; task = g_task_new (self, NULL, callback, user_data); if (mbim_device_check_ms_mbimex_version (device, 2, 0)) { message = mbim_message_signal_state_query_new (NULL); mbim_device_command (device, message, 5, NULL, (GAsyncReadyCallback)mbimexv2_signal_state_query_ready, task); mbim_message_unref (message); return; } if (MM_BROADBAND_MODEM_MBIM (self)->priv->is_atds_signal_supported) { message = mbim_message_atds_signal_query_new (NULL); mbim_device_command (device, message, 5, NULL, (GAsyncReadyCallback)atds_signal_query_ready, task); mbim_message_unref (message); return; } /* Fallback to parent CESQ based implementation */ g_assert (iface_modem_signal_parent->load_values && iface_modem_signal_parent->load_values_finish); iface_modem_signal_parent->load_values (self, NULL, (GAsyncReadyCallback)parent_signal_load_values_ready, task); } /*****************************************************************************/ /* Setup threshold values (Signal interface) */ static gboolean modem_signal_setup_thresholds_finish (MMIfaceModemSignal *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void signal_state_set_thresholds_ready (MbimDevice *device, GAsyncResult *res, GTask *task) { g_autoptr(MbimMessage) response = NULL; GError *error = NULL; response = mbim_device_command_finish (device, res, &error); if (response && mbim_message_response_get_result (response, MBIM_MESSAGE_TYPE_COMMAND_DONE, &error) && mbim_message_signal_state_response_parse ( response, NULL, /* rssi */ NULL, /* error_rate */ NULL, /* signal_strength_interval */ NULL, /* rssi_threshold */ NULL, /* error_rate_threshold */ &error)) g_task_return_boolean (task, TRUE); else g_task_return_error (task, error); g_object_unref (task); } static void modem_signal_setup_thresholds (MMIfaceModemSignal *self, guint rssi_threshold, gboolean error_rate_threshold, GAsyncReadyCallback callback, gpointer user_data) { g_autoptr(MbimMessage) message = NULL; MbimDevice *device; GTask *task; guint coded_rssi_threshold = 0; guint coded_error_rate_threshold = 0; if (!peek_device (self, &device, callback, user_data)) return; task = g_task_new (self, NULL, callback, user_data); /* the input RSSI threshold difference is given in dBm, and in the MBIM * protocol we use a linear scale of coded values that correspond to 2dBm * per code point. */ if (rssi_threshold) { coded_rssi_threshold = rssi_threshold / 2; if (!coded_rssi_threshold) coded_rssi_threshold = 1; /* minimum value when enabled */ } /* the input error rate threshold is given as a boolean to enable or * disable, and in the MBIM protocol we have a non-linear scale of * coded values. We just select the minimum coded value, so that we * get all reports, i.e. every time it changes the coded value */ if (error_rate_threshold) coded_error_rate_threshold = 1; /* minimum value when enabled */ /* setting signal strength interval to 0 disables threshold-based * notifications on certain modems (FM350). * hence, it is being set to 5 as per FBC's recommendation. * typically, setting this parameter to 0 should make the modem * set the value to its internal default as per spec. */ message = (mbim_message_signal_state_set_new ( 5, /* non-zero default signal strength interval */ coded_rssi_threshold, coded_error_rate_threshold, NULL)); mbim_device_command (device, message, 10, NULL, (GAsyncReadyCallback)signal_state_set_thresholds_ready, task); } /*****************************************************************************/ /* Check support (3GPP profile management interface) */ static gboolean modem_3gpp_profile_manager_check_support_finish (MMIfaceModem3gppProfileManager *_self, GAsyncResult *res, gchar **index_field, GError **error) { MMBroadbandModemMbim *self = MM_BROADBAND_MODEM_MBIM (_self); g_assert (g_task_propagate_boolean (G_TASK (res), NULL)); if (mm_iface_modem_is_3gpp (MM_IFACE_MODEM (self))) { if (self->priv->is_profile_management_ext_supported) { *index_field = g_strdup ("apn-type"); return TRUE; } if (self->priv->is_profile_management_supported) { *index_field = g_strdup ("profile-id"); return TRUE; } } return FALSE; } static void modem_3gpp_profile_manager_check_support (MMIfaceModem3gppProfileManager *self, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; task = g_task_new (self, NULL, callback, user_data); g_task_return_boolean (task, TRUE); g_object_unref (task); } /*****************************************************************************/ /* Setup/Cleanup unsolicited event handlers (3gppProfileManager interface) */ static gboolean common_setup_cleanup_unsolicited_events_3gpp_profile_manager_finish (MMIfaceModem3gppProfileManager *self, GAsyncResult *res, GError **error) { return common_setup_cleanup_unsolicited_events_finish (MM_BROADBAND_MODEM_MBIM (self), res, error); } static void cleanup_unsolicited_events_3gpp_profile_manager (MMIfaceModem3gppProfileManager *_self, GAsyncReadyCallback callback, gpointer user_data) { MMBroadbandModemMbim *self = MM_BROADBAND_MODEM_MBIM (_self); self->priv->setup_flags &= ~PROCESS_NOTIFICATION_FLAG_PROVISIONED_CONTEXTS; common_setup_cleanup_unsolicited_events (self, FALSE, callback, user_data); } static void setup_unsolicited_events_3gpp_profile_manager (MMIfaceModem3gppProfileManager *_self, GAsyncReadyCallback callback, gpointer user_data) { MMBroadbandModemMbim *self = MM_BROADBAND_MODEM_MBIM (_self); self->priv->setup_flags |= PROCESS_NOTIFICATION_FLAG_PROVISIONED_CONTEXTS; common_setup_cleanup_unsolicited_events (self, TRUE, callback, user_data); } /*****************************************************************************/ /* Enable/Disable unsolicited event handlers (3gppProfileManager interface) */ static gboolean common_enable_disable_unsolicited_events_3gpp_profile_manager_finish (MMIfaceModem3gppProfileManager *self, GAsyncResult *res, GError **error) { return common_enable_disable_unsolicited_events_finish (MM_BROADBAND_MODEM_MBIM (self), res, error); } static void disable_unsolicited_events_3gpp_profile_manager (MMIfaceModem3gppProfileManager *_self, GAsyncReadyCallback callback, gpointer user_data) { MMBroadbandModemMbim *self = MM_BROADBAND_MODEM_MBIM (_self); self->priv->enable_flags &= ~PROCESS_NOTIFICATION_FLAG_PROVISIONED_CONTEXTS; common_enable_disable_unsolicited_events (self, callback, user_data); } static void enable_unsolicited_events_3gpp_profile_manager (MMIfaceModem3gppProfileManager *_self, GAsyncReadyCallback callback, gpointer user_data) { MMBroadbandModemMbim *self = MM_BROADBAND_MODEM_MBIM (_self); self->priv->enable_flags |= PROCESS_NOTIFICATION_FLAG_PROVISIONED_CONTEXTS; common_enable_disable_unsolicited_events (self, callback, user_data); } /*****************************************************************************/ /* Check format (3gppProfileManager interface) */ static gboolean modem_3gpp_profile_manager_check_format_finish (MMIfaceModem3gppProfileManager *_self, GAsyncResult *res, gboolean *new_id, gint *min_profile_id, gint *max_profile_id, GEqualFunc *apn_cmp, MM3gppProfileCmpFlags *profile_cmp_flags, GError **error) { MMBroadbandModemMbim *self = MM_BROADBAND_MODEM_MBIM (_self); if (!g_task_propagate_boolean (G_TASK (res), error)) { g_assert_not_reached (); return FALSE; } if (new_id) *new_id = TRUE; if (min_profile_id) *min_profile_id = 1; if (max_profile_id) *max_profile_id = G_MAXINT - 1; /* use default string comparison method */ if (apn_cmp) *apn_cmp = NULL; if (profile_cmp_flags) { if (!self->priv->is_profile_management_ext_supported) *profile_cmp_flags = (MM_3GPP_PROFILE_CMP_FLAGS_NO_IP_TYPE | MM_3GPP_PROFILE_CMP_FLAGS_NO_ACCESS_TYPE_PREFERENCE | MM_3GPP_PROFILE_CMP_FLAGS_NO_ROAMING_ALLOWANCE | MM_3GPP_PROFILE_CMP_FLAGS_NO_PROFILE_SOURCE); else /* when using the MS extensions, we support all IP type, access type * preference, roaming allowance and profile source */ *profile_cmp_flags = 0; } return TRUE; } static void modem_3gpp_profile_manager_check_format (MMIfaceModem3gppProfileManager *self, MMBearerIpFamily ip_type, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; task = g_task_new (self, NULL, callback, user_data); g_task_return_boolean (task, TRUE); g_object_unref (task); } /*****************************************************************************/ /* List profiles (3GPP profile management interface) */ typedef struct { GList *profiles; } ListProfilesContext; static void list_profiles_context_free (ListProfilesContext *ctx) { mm_3gpp_profile_list_free (ctx->profiles); g_slice_free (ListProfilesContext, ctx); } static gboolean modem_3gpp_profile_manager_list_profiles_finish (MMIfaceModem3gppProfileManager *self, GAsyncResult *res, GList **out_profiles, GError **error) { ListProfilesContext *ctx; if (!g_task_propagate_boolean (G_TASK (res), error)) return FALSE; ctx = g_task_get_task_data (G_TASK (res)); if (out_profiles) *out_profiles = g_steal_pointer (&ctx->profiles); return TRUE; } static MM3gppProfile * provisioned_context_element_to_3gpp_profile (MbimProvisionedContextElement *element) { MM3gppProfile *profile; profile = mm_3gpp_profile_new (); mm_3gpp_profile_set_profile_id (profile, element->context_id); mm_3gpp_profile_set_apn (profile, element->access_string); mm_3gpp_profile_set_apn_type (profile, mm_bearer_apn_type_from_mbim_context_type (mbim_uuid_to_context_type (&element->context_type))); mm_3gpp_profile_set_user (profile, element->user_name); mm_3gpp_profile_set_password (profile, element->password); mm_3gpp_profile_set_allowed_auth (profile, (mm_bearer_allowed_auth_from_mbim_auth_protocol (element->auth_protocol))); /* compression unused, and ip-type not provided */ return profile; } static MM3gppProfile * provisioned_context_element_v2_to_3gpp_profile (MMBroadbandModemMbim *self, MbimProvisionedContextElementV2 *element) { MM3gppProfile *profile; GError *error = NULL; gboolean enabled; MMBearerRoamingAllowance roaming_allowance; MMBearerAccessTypePreference access_type_preference; MMBearerProfileSource profile_source; profile = mm_3gpp_profile_new (); mm_3gpp_profile_set_profile_id (profile, element->context_id); mm_3gpp_profile_set_apn (profile, element->access_string); mm_3gpp_profile_set_apn_type (profile, mm_bearer_apn_type_from_mbim_context_type (mbim_uuid_to_context_type (&element->context_type))); mm_3gpp_profile_set_user (profile, element->user_name); mm_3gpp_profile_set_password (profile, element->password); mm_3gpp_profile_set_allowed_auth (profile, (mm_bearer_allowed_auth_from_mbim_auth_protocol (element->auth_protocol))); mm_3gpp_profile_set_ip_type (profile, mm_bearer_ip_family_from_mbim_context_ip_type (element->ip_type)); if (!mm_boolean_from_mbim_context_state (element->state, &enabled, &error)) { mm_obj_dbg (self, "ignoring enable setting: %s", error->message); g_clear_error (&error); } else mm_3gpp_profile_set_enabled (profile, enabled); roaming_allowance = mm_bearer_roaming_allowance_from_mbim_context_roaming_control (element->roaming, &error); if (roaming_allowance == MM_BEARER_ROAMING_ALLOWANCE_NONE) { mm_obj_dbg (self, "ignoring roaming allowance: %s", error->message); g_clear_error (&error); } else mm_3gpp_profile_set_roaming_allowance (profile, roaming_allowance); if (!mm_bearer_access_type_preference_from_mbim_context_media_type (element->media_type, &access_type_preference, &error)) { mm_obj_dbg (self, "ignoring access type preference: %s", error->message); g_clear_error (&error); } else mm_3gpp_profile_set_access_type_preference (profile, access_type_preference); profile_source = mm_bearer_profile_source_from_mbim_context_source (element->source, &error); if (profile_source == MM_BEARER_PROFILE_SOURCE_UNKNOWN) { mm_obj_dbg (self, "ignoring profile source: %s", error->message); g_clear_error (&error); } else mm_3gpp_profile_set_profile_source (profile, profile_source); /* compression unused */ return profile; } static void profile_manager_provisioned_contexts_query_ready (MbimDevice *device, GAsyncResult *res, GTask *task) { ListProfilesContext *ctx; GError *error = NULL; guint i; guint32 provisioned_contexts_count = 0; g_autoptr(MbimMessage) response = NULL; g_autoptr(MbimProvisionedContextElementArray) provisioned_contexts = NULL; ctx = g_task_get_task_data (task); response = mbim_device_command_finish (device, res, &error); if (!response || !mbim_message_response_get_result (response, MBIM_MESSAGE_TYPE_COMMAND_DONE, &error) || !mbim_message_provisioned_contexts_response_parse (response, &provisioned_contexts_count, &provisioned_contexts, &error)) { g_task_return_error (task, error); g_object_unref (task); return; } for (i = 0; i < provisioned_contexts_count; i++) { MM3gppProfile *profile; profile = provisioned_context_element_to_3gpp_profile (provisioned_contexts[i]); ctx->profiles = g_list_append (ctx->profiles, profile); } g_task_return_boolean (task, TRUE); g_object_unref (task); } static void profile_manager_provisioned_contexts_v2_query_ready (MbimDevice *device, GAsyncResult *res, GTask *task) { MMBroadbandModemMbim *self; ListProfilesContext *ctx; GError *error = NULL; guint i; guint32 provisioned_contexts_count = 0; g_autoptr(MbimMessage) response = NULL; g_autoptr(MbimProvisionedContextElementV2Array) provisioned_contexts_v2 = NULL; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); response = mbim_device_command_finish (device, res, &error); if (!response || !mbim_message_response_get_result (response, MBIM_MESSAGE_TYPE_COMMAND_DONE, &error) || !mbim_message_ms_basic_connect_extensions_provisioned_contexts_response_parse (response, &provisioned_contexts_count, &provisioned_contexts_v2, &error)) { g_task_return_error (task, error); g_object_unref (task); return; } for (i = 0; i < provisioned_contexts_count; i++) { MM3gppProfile *profile; profile = provisioned_context_element_v2_to_3gpp_profile (self, provisioned_contexts_v2[i]); ctx->profiles = g_list_append (ctx->profiles, profile); } g_task_return_boolean (task, TRUE); g_object_unref (task); } static void modem_3gpp_profile_manager_list_profiles (MMIfaceModem3gppProfileManager *_self, GAsyncReadyCallback callback, gpointer user_data) { MMBroadbandModemMbim *self = MM_BROADBAND_MODEM_MBIM (_self); ListProfilesContext *ctx; MbimDevice *device; GTask *task; g_autoptr(MbimMessage) message = NULL; if (!peek_device (self, &device, callback, user_data)) return; task = g_task_new (self, NULL, callback, user_data); ctx = g_slice_new0 (ListProfilesContext); g_task_set_task_data (task, ctx, (GDestroyNotify) list_profiles_context_free); mm_obj_dbg (self, "querying provisioned contexts..."); /* We will use the MBIMEx operation not only when testing with MBIMEx profile management * support, but also any time the command is supported by the modem, because it gives much * more information than the default one. */ if (self->priv->is_context_type_ext_supported) { message = mbim_message_ms_basic_connect_extensions_provisioned_contexts_query_new (NULL); mbim_device_command (device, message, 10, NULL, (GAsyncReadyCallback)profile_manager_provisioned_contexts_v2_query_ready, task); return; } message = mbim_message_provisioned_contexts_query_new (NULL); mbim_device_command (device, message, 10, NULL, (GAsyncReadyCallback)profile_manager_provisioned_contexts_query_ready, task); } /*****************************************************************************/ /* Store profile (3GPP profile management interface) */ typedef struct { gint profile_id; MMBearerApnType apn_type; } StoreProfileContext; static gboolean modem_3gpp_profile_manager_store_profile_finish (MMIfaceModem3gppProfileManager *self, GAsyncResult *res, gint *out_profile_id, MMBearerApnType *out_apn_type, GError **error) { StoreProfileContext *ctx; if (!g_task_propagate_boolean (G_TASK (res), error)) return FALSE; ctx = g_task_get_task_data (G_TASK (res)); if (out_profile_id) *out_profile_id = ctx->profile_id; if (out_apn_type) *out_apn_type = ctx->apn_type; return TRUE; } static void profile_manager_provisioned_contexts_set_ready (MbimDevice *device, GAsyncResult *res, GTask *task) { GError *error = NULL; g_autoptr(MbimMessage) response = NULL; response = mbim_device_command_finish (device, res, &error); if (response && mbim_message_response_get_result (response, MBIM_MESSAGE_TYPE_COMMAND_DONE, &error)) g_task_return_boolean (task, TRUE); else g_task_return_error (task, error); g_object_unref (task); } static void modem_3gpp_profile_manager_store_profile (MMIfaceModem3gppProfileManager *_self, MM3gppProfile *profile, const gchar *index_field, GAsyncReadyCallback callback, gpointer user_data) { MMBroadbandModemMbim *self = MM_BROADBAND_MODEM_MBIM (_self); StoreProfileContext *ctx = NULL; MbimDevice *device; GTask *task; GError *error = NULL; MMBearerAllowedAuth allowed_auth; MbimAuthProtocol auth_protocol = MBIM_AUTH_PROTOCOL_NONE; MbimContextType context_type; const MbimUuid *context_type_uuid; const gchar *apn; const gchar *user; const gchar *password; g_autofree gchar *apn_type_str = NULL; g_autoptr(MbimMessage) message = NULL; if (!peek_device (self, &device, callback, user_data)) return; task = g_task_new (self, NULL, callback, user_data); ctx = g_new0 (StoreProfileContext, 1); ctx->profile_id = MM_3GPP_PROFILE_ID_UNKNOWN; ctx->apn_type = MM_BEARER_APN_TYPE_NONE; g_task_set_task_data (task, ctx, (GDestroyNotify) g_free); ctx->profile_id = mm_3gpp_profile_get_profile_id (profile); ctx->apn_type = mm_3gpp_profile_get_apn_type (profile); apn_type_str = mm_bearer_apn_type_build_string_from_mask (ctx->apn_type); context_type = mm_bearer_apn_type_to_mbim_context_type (ctx->apn_type, self->priv->is_context_type_ext_supported, self, &error); if (error) { g_prefix_error (&error, "Failed to convert mbim context type from apn type: "); g_task_return_error (task, error); g_object_unref (task); return; } context_type_uuid = mbim_uuid_from_context_type (context_type); apn = mm_3gpp_profile_get_apn (profile); user = mm_3gpp_profile_get_user (profile); password = mm_3gpp_profile_get_password (profile); allowed_auth = mm_3gpp_profile_get_allowed_auth (profile); if ((allowed_auth != MM_BEARER_ALLOWED_AUTH_UNKNOWN) || user || password) { auth_protocol = mm_bearer_allowed_auth_to_mbim_auth_protocol (allowed_auth, self, &error); if (error) { g_prefix_error (&error, "Failed to convert mbim auth protocol from allowed auth: "); g_task_return_error (task, error); g_object_unref (task); return; } } if (g_strcmp0 (index_field, "profile-id") == 0) { mm_obj_dbg (self, "storing profile '%d': apn '%s', apn type '%s'", ctx->profile_id, apn, apn_type_str); message = mbim_message_provisioned_contexts_set_new (ctx->profile_id, context_type_uuid, apn ? apn : "", user ? user : "", password ? password : "", MBIM_COMPRESSION_NONE, auth_protocol, "", /* provider id */ &error); } else if (g_strcmp0 (index_field, "apn-type") == 0) { MbimContextIpType ip_type; MbimContextState state; MbimContextRoamingControl roaming; MbimContextMediaType media_type; MbimContextSource source; g_assert (self->priv->is_profile_management_ext_supported); state = mm_boolean_to_mbim_context_state (mm_3gpp_profile_get_enabled (profile)); ip_type = mm_bearer_ip_family_to_mbim_context_ip_type (mm_3gpp_profile_get_ip_type (profile), &error); if (error) { g_prefix_error (&error, "Failed to convert mbim context ip type from ip type: "); g_task_return_error (task, error); g_object_unref (task); return; } if (!mm_bearer_roaming_allowance_to_mbim_context_roaming_control (mm_3gpp_profile_get_roaming_allowance (profile), self, &roaming, &error)) { g_prefix_error (&error, "Failed to convert mbim context roaming control from roaming allowance: "); g_task_return_error (task, error); g_object_unref (task); return; } if (!mm_bearer_access_type_preference_to_mbim_context_media_type (mm_3gpp_profile_get_access_type_preference (profile), self, &media_type, &error)) { g_prefix_error (&error, "Failed to convert mbim context media type from access type preference: "); g_task_return_error (task, error); g_object_unref (task); return; } if (!mm_bearer_profile_source_to_mbim_context_source (mm_3gpp_profile_get_profile_source (profile), self, &source, &error)) { g_prefix_error (&error, "Failed to convert mbim context source from profile source: "); g_task_return_error (task, error); g_object_unref (task); return; } if (ctx->profile_id != MM_3GPP_PROFILE_ID_UNKNOWN) mm_obj_warn (self, "ignoring profile id '%d' when storing profile: unsupported", ctx->profile_id); mm_obj_dbg (self, "storing profile '%s': apn '%s'", apn_type_str, apn); message = mbim_message_ms_basic_connect_extensions_provisioned_contexts_set_new (MBIM_CONTEXT_OPERATION_DEFAULT, context_type_uuid, ip_type, state, roaming, media_type, source, apn ? apn : "", user ? user : "", password ? password : "", MBIM_COMPRESSION_NONE, auth_protocol, &error); } else g_assert_not_reached (); if (error) { g_task_return_error (task, error); g_object_unref (task); return; } mbim_device_command (device, message, 10, NULL, (GAsyncReadyCallback)profile_manager_provisioned_contexts_set_ready, task); } /*****************************************************************************/ /* Delete profile (3GPP profile management interface) */ static gboolean modem_3gpp_profile_manager_delete_profile_finish (MMIfaceModem3gppProfileManager *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void profile_manager_provisioned_contexts_reset_ready (MbimDevice *device, GAsyncResult *res, GTask *task) { GError *error = NULL; g_autoptr(MbimMessage) response = NULL; response = mbim_device_command_finish (device, res, &error); if (response && mbim_message_response_get_result (response, MBIM_MESSAGE_TYPE_COMMAND_DONE, &error)) g_task_return_boolean (task, TRUE); else g_task_return_error (task, error); g_object_unref (task); } static void modem_3gpp_profile_manager_delete_profile (MMIfaceModem3gppProfileManager *_self, MM3gppProfile *profile, const gchar *index_field, GAsyncReadyCallback callback, gpointer user_data) { MMBroadbandModemMbim *self = MM_BROADBAND_MODEM_MBIM (_self); MbimDevice *device; GTask *task; GError *error = NULL; g_autoptr(MbimMessage) message = NULL; if (!peek_device (self, &device, callback, user_data)) return; task = g_task_new (self, NULL, callback, user_data); if (g_strcmp0 (index_field, "profile-id") == 0) { gint profile_id; profile_id = mm_3gpp_profile_get_profile_id (profile); g_assert (profile_id != MM_3GPP_PROFILE_ID_UNKNOWN); mm_obj_dbg (self, "deleting profile '%d'", profile_id); message = mbim_message_provisioned_contexts_set_new (profile_id, mbim_uuid_from_context_type (MBIM_CONTEXT_TYPE_NONE), "", /* access string */ "", /* user */ "", /* pass */ MBIM_COMPRESSION_NONE, MBIM_AUTH_PROTOCOL_NONE, "", /* provider id */ &error); } else if (g_strcmp0 (index_field, "apn-type") == 0) { MMBearerApnType apn_type; MbimContextType context_type; g_assert (self->priv->is_profile_management_ext_supported); g_assert (self->priv->is_context_type_ext_supported); apn_type = mm_3gpp_profile_get_apn_type (profile); g_assert (apn_type != MM_BEARER_APN_TYPE_NONE); context_type = mm_bearer_apn_type_to_mbim_context_type (apn_type, TRUE, self, &error); if (error) g_prefix_error (&error, "Failed to convert mbim context type from apn type: "); else { const MbimUuid *context_type_uuid; context_type_uuid = mbim_uuid_from_context_type (context_type); message = mbim_message_ms_basic_connect_extensions_provisioned_contexts_set_new (MBIM_CONTEXT_OPERATION_DELETE, context_type_uuid, MBIM_CONTEXT_IP_TYPE_DEFAULT, MBIM_CONTEXT_STATE_DISABLED, MBIM_CONTEXT_ROAMING_CONTROL_ALLOW_ALL, MBIM_CONTEXT_MEDIA_TYPE_ALL, MBIM_CONTEXT_SOURCE_ADMIN, "", /* access string */ "", /* user */ "", /* password */ MBIM_COMPRESSION_NONE, MBIM_AUTH_PROTOCOL_NONE, &error); } } else g_assert_not_reached (); if (error) { g_task_return_error (task, error); g_object_unref (task); return; } mbim_device_command (device, message, 10, NULL, (GAsyncReadyCallback)profile_manager_provisioned_contexts_reset_ready, task); } /*****************************************************************************/ /* Check if USSD supported (3GPP/USSD interface) */ static gboolean modem_3gpp_ussd_check_support_finish (MMIfaceModem3gppUssd *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void modem_3gpp_ussd_check_support (MMIfaceModem3gppUssd *self, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; task = g_task_new (self, NULL, callback, user_data); g_task_return_boolean (task, MM_BROADBAND_MODEM_MBIM (self)->priv->is_ussd_supported); g_object_unref (task); } /*****************************************************************************/ /* USSD encoding/deconding helpers * * Note: we don't care about subclassing the ussd_encode/decode methods in the * interface, as we're going to use this methods just here. */ static GByteArray * ussd_encode (const gchar *command, guint32 *scheme, GError **error) { g_autoptr(GByteArray) array = NULL; if (mm_charset_can_convert_to (command, MM_MODEM_CHARSET_GSM)) { g_autoptr(GByteArray) gsm = NULL; guint8 *packed; guint32 packed_len = 0; *scheme = MM_MODEM_GSM_USSD_SCHEME_7BIT; gsm = mm_modem_charset_bytearray_from_utf8 (command, MM_MODEM_CHARSET_GSM, FALSE, error); if (!gsm) { g_prefix_error (error, "Failed to encode USSD command in GSM7 charset: "); return NULL; } packed = mm_charset_gsm_pack (gsm->data, gsm->len, 0, &packed_len); array = g_byte_array_new_take (packed, packed_len); } else { *scheme = MM_MODEM_GSM_USSD_SCHEME_UCS2; array = mm_modem_charset_bytearray_from_utf8 (command, MM_MODEM_CHARSET_UCS2, FALSE, error); if (!array) { g_prefix_error (error, "Failed to encode USSD command in UCS2 charset: "); return NULL; } } if (array->len > 160) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS, "Failed to encode USSD command: encoded data too long (%u > 160)", array->len); return NULL; } return g_steal_pointer (&array); } static gchar * ussd_decode (guint32 scheme, GByteArray *data, GError **error) { gchar *decoded = NULL; if (scheme == MM_MODEM_GSM_USSD_SCHEME_7BIT) { g_autoptr(GByteArray) unpacked_array = NULL; guint8 *unpacked = NULL; guint32 unpacked_len; unpacked = mm_charset_gsm_unpack ((const guint8 *)data->data, (data->len * 8) / 7, 0, &unpacked_len); unpacked_array = g_byte_array_new_take (unpacked, unpacked_len); decoded = mm_modem_charset_bytearray_to_utf8 (unpacked_array, MM_MODEM_CHARSET_GSM, FALSE, error); if (!decoded) g_prefix_error (error, "Error decoding USSD command in 0x%04x scheme (GSM7 charset): ", scheme); } else if (scheme == MM_MODEM_GSM_USSD_SCHEME_UCS2) { decoded = mm_modem_charset_bytearray_to_utf8 (data, MM_MODEM_CHARSET_UCS2, FALSE, error); if (!decoded) g_prefix_error (error, "Error decoding USSD command in 0x%04x scheme (UCS2 charset): ", scheme); } else g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "Failed to decode USSD command in unsupported 0x%04x scheme", scheme); return decoded; } /*****************************************************************************/ /* USSD notifications */ static void process_ussd_message (MMBroadbandModemMbim *self, MbimUssdResponse ussd_response, MbimUssdSessionState ussd_session_state, guint32 scheme, guint32 data_size, const guint8 *data) { GTask *task = NULL; MMModem3gppUssdSessionState ussd_state = MM_MODEM_3GPP_USSD_SESSION_STATE_IDLE; GByteArray *bytearray; gchar *converted = NULL; GError *error = NULL; /* Steal task and balance out received reference */ if (self->priv->pending_ussd_action) { task = self->priv->pending_ussd_action; self->priv->pending_ussd_action = NULL; } bytearray = g_byte_array_new (); if (data && data_size) bytearray = g_byte_array_append (bytearray, data, data_size); switch (ussd_response) { case MBIM_USSD_RESPONSE_NO_ACTION_REQUIRED: /* no further action required */ converted = ussd_decode (scheme, bytearray, &error); if (!converted) break; /* Response to the user's request? */ if (task) break; /* Network-initiated USSD-Notify */ mm_iface_modem_3gpp_ussd_update_network_notification (MM_IFACE_MODEM_3GPP_USSD (self), converted); g_clear_pointer (&converted, g_free); break; case MBIM_USSD_RESPONSE_ACTION_REQUIRED: /* further action required */ ussd_state = MM_MODEM_3GPP_USSD_SESSION_STATE_USER_RESPONSE; converted = ussd_decode (scheme, bytearray, &error); if (!converted) break; /* Response to the user's request? */ if (task) break; /* Network-initiated USSD-Request */ mm_iface_modem_3gpp_ussd_update_network_request (MM_IFACE_MODEM_3GPP_USSD (self), converted); g_clear_pointer (&converted, g_free); break; case MBIM_USSD_RESPONSE_TERMINATED_BY_NETWORK: error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_ABORTED, "USSD terminated by network"); break; case MBIM_USSD_RESPONSE_OTHER_LOCAL_CLIENT: error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_RETRY, "Another ongoing USSD operation is in progress"); break; case MBIM_USSD_RESPONSE_OPERATION_NOT_SUPPORTED: error = g_error_new (MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_NOT_SUPPORTED, "Operation not supported"); break; case MBIM_USSD_RESPONSE_NETWORK_TIMEOUT: error = g_error_new (MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_NETWORK_TIMEOUT, "Network timeout"); break; default: error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Unknown USSD response (%u)", ussd_response); break; } mm_iface_modem_3gpp_ussd_update_state (MM_IFACE_MODEM_3GPP_USSD (self), ussd_state); g_byte_array_unref (bytearray); /* Complete the pending action */ if (task) { if (error) g_task_return_error (task, error); else if (converted) g_task_return_pointer (task, converted, g_free); else g_assert_not_reached (); g_object_unref (task); return; } /* If no pending task, just report the error */ if (error) { mm_obj_dbg (self, "network reported USSD message: %s", error->message); g_error_free (error); } g_assert (!converted); } static void process_ussd_notification (MMBroadbandModemMbim *self, MbimMessage *notification) { MbimUssdResponse ussd_response; MbimUssdSessionState ussd_session_state; guint32 scheme; guint32 data_size; const guint8 *data; if (mbim_message_ussd_notification_parse (notification, &ussd_response, &ussd_session_state, &scheme, &data_size, &data, NULL)) { mm_obj_dbg (self, "received USSD indication: %s, session state: %s, scheme: 0x%x, data size: %u bytes", mbim_ussd_response_get_string (ussd_response), mbim_ussd_session_state_get_string (ussd_session_state), scheme, data_size); process_ussd_message (self, ussd_response, ussd_session_state, scheme, data_size, data); } } /*****************************************************************************/ /* Setup/Cleanup unsolicited result codes (3GPP/USSD interface) */ static gboolean modem_3gpp_ussd_setup_cleanup_unsolicited_events_finish (MMIfaceModem3gppUssd *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void common_setup_flag_ussd_ready (MMBroadbandModemMbim *self, GAsyncResult *res, GTask *task) { GError *error = NULL; if (!common_setup_cleanup_unsolicited_events_finish (self, res, &error)) { g_task_return_error (task, error); g_object_unref (task); return; } g_task_return_boolean (task, TRUE); g_object_unref (task); } static void common_setup_cleanup_unsolicited_ussd_events (MMBroadbandModemMbim *self, gboolean setup, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; task = g_task_new (self, NULL, callback, user_data); g_task_set_task_data (task, GINT_TO_POINTER (setup), NULL); if (setup) self->priv->setup_flags |= PROCESS_NOTIFICATION_FLAG_USSD; else self->priv->setup_flags &= ~PROCESS_NOTIFICATION_FLAG_USSD; common_setup_cleanup_unsolicited_events (self, setup, (GAsyncReadyCallback)common_setup_flag_ussd_ready, task); } static void modem_3gpp_ussd_cleanup_unsolicited_events (MMIfaceModem3gppUssd *self, GAsyncReadyCallback callback, gpointer user_data) { common_setup_cleanup_unsolicited_ussd_events (MM_BROADBAND_MODEM_MBIM (self), FALSE, callback, user_data); } static void modem_3gpp_ussd_setup_unsolicited_events (MMIfaceModem3gppUssd *self, GAsyncReadyCallback callback, gpointer user_data) { common_setup_cleanup_unsolicited_ussd_events (MM_BROADBAND_MODEM_MBIM (self), TRUE, callback, user_data); } /*****************************************************************************/ /* Enable/Disable URCs (3GPP/USSD interface) */ static gboolean modem_3gpp_ussd_enable_disable_unsolicited_events_finish (MMIfaceModem3gppUssd *self, GAsyncResult *res, GError **error) { return common_enable_disable_unsolicited_events_finish (MM_BROADBAND_MODEM_MBIM (self), res, error); } static void modem_3gpp_ussd_disable_unsolicited_events (MMIfaceModem3gppUssd *_self, GAsyncReadyCallback callback, gpointer user_data) { MMBroadbandModemMbim *self = MM_BROADBAND_MODEM_MBIM (_self); self->priv->enable_flags &= ~PROCESS_NOTIFICATION_FLAG_USSD; common_enable_disable_unsolicited_events (self, callback, user_data); } static void modem_3gpp_ussd_enable_unsolicited_events (MMIfaceModem3gppUssd *_self, GAsyncReadyCallback callback, gpointer user_data) { MMBroadbandModemMbim *self = MM_BROADBAND_MODEM_MBIM (_self); self->priv->enable_flags |= PROCESS_NOTIFICATION_FLAG_USSD; common_enable_disable_unsolicited_events (self, callback, user_data); } /*****************************************************************************/ /* Send command (3GPP/USSD interface) */ static gchar * modem_3gpp_ussd_send_finish (MMIfaceModem3gppUssd *self, GAsyncResult *res, GError **error) { return g_task_propagate_pointer (G_TASK (res), error); } static void ussd_send_ready (MbimDevice *device, GAsyncResult *res, MMBroadbandModemMbim *self) { MbimMessage *response; GError *error = NULL; MbimUssdResponse ussd_response; MbimUssdSessionState ussd_session_state; guint32 scheme; guint32 data_size; const guint8 *data; /* Note: if there is a cached task, it is ALWAYS completed here */ response = mbim_device_command_finish (device, res, &error); if (response && mbim_message_response_get_result (response, MBIM_MESSAGE_TYPE_COMMAND_DONE, &error) && mbim_message_ussd_response_parse (response, &ussd_response, &ussd_session_state, &scheme, &data_size, &data, &error)) { mm_obj_dbg (self, "received USSD response: %s, session state: %s, scheme: 0x%x, data size: %u bytes", mbim_ussd_response_get_string (ussd_response), mbim_ussd_session_state_get_string (ussd_session_state), scheme, data_size); process_ussd_message (self, ussd_response, ussd_session_state, scheme, data_size, data); } else { /* Report error in the cached task, if any */ if (self->priv->pending_ussd_action) { GTask *task; task = self->priv->pending_ussd_action; self->priv->pending_ussd_action = NULL; g_task_return_error (task, error); g_object_unref (task); } else { mm_obj_dbg (self, "failed to parse USSD response: %s", error->message); g_clear_error (&error); } } if (response) mbim_message_unref (response); /* Balance out received reference */ g_object_unref (self); } static void modem_3gpp_ussd_send (MMIfaceModem3gppUssd *_self, const gchar *command, GAsyncReadyCallback callback, gpointer user_data) { MMBroadbandModemMbim *self; MbimDevice *device; GTask *task; MbimUssdAction action; MbimMessage *message; GByteArray *encoded; guint32 scheme = 0; GError *error = NULL; self = MM_BROADBAND_MODEM_MBIM (_self); if (!peek_device (self, &device, callback, user_data)) return; task = g_task_new (self, NULL, callback, user_data); /* Fail if there is an ongoing operation already */ if (self->priv->pending_ussd_action) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_IN_PROGRESS, "there is already an ongoing USSD operation"); g_object_unref (task); return; } switch (mm_iface_modem_3gpp_ussd_get_state (MM_IFACE_MODEM_3GPP_USSD (self))) { case MM_MODEM_3GPP_USSD_SESSION_STATE_IDLE: action = MBIM_USSD_ACTION_INITIATE; break; case MM_MODEM_3GPP_USSD_SESSION_STATE_USER_RESPONSE: action = MBIM_USSD_ACTION_CONTINUE; break; case MM_MODEM_3GPP_USSD_SESSION_STATE_UNKNOWN: case MM_MODEM_3GPP_USSD_SESSION_STATE_ACTIVE: default: g_assert_not_reached (); return; } encoded = ussd_encode (command, &scheme, &error); if (!encoded) { g_task_return_error (task, error); g_object_unref (task); return; } message = mbim_message_ussd_set_new (action, scheme, encoded->len, encoded->data, &error); if (!message) { g_task_return_error (task, error); g_object_unref (task); return; } /* Cache the action, as it may be completed via URCs */ self->priv->pending_ussd_action = task; mm_iface_modem_3gpp_ussd_update_state (_self, MM_MODEM_3GPP_USSD_SESSION_STATE_ACTIVE); mbim_device_command (device, message, 100, NULL, (GAsyncReadyCallback)ussd_send_ready, g_object_ref (self)); /* Full reference! */ mbim_message_unref (message); } /*****************************************************************************/ /* Cancel USSD (3GPP/USSD interface) */ static gboolean modem_3gpp_ussd_cancel_finish (MMIfaceModem3gppUssd *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void ussd_cancel_ready (MbimDevice *device, GAsyncResult *res, GTask *task) { MMBroadbandModemMbim *self; MbimMessage *response; GError *error = NULL; self = g_task_get_source_object (task); response = mbim_device_command_finish (device, res, &error); if (response) mbim_message_response_get_result (response, MBIM_MESSAGE_TYPE_COMMAND_DONE, &error); /* Complete the pending action, regardless of the operation result */ if (self->priv->pending_ussd_action) { GTask *pending_task; pending_task = self->priv->pending_ussd_action; self->priv->pending_ussd_action = NULL; g_task_return_new_error (pending_task, MM_CORE_ERROR, MM_CORE_ERROR_ABORTED, "USSD session was cancelled"); g_object_unref (pending_task); } mm_iface_modem_3gpp_ussd_update_state (MM_IFACE_MODEM_3GPP_USSD (self), MM_MODEM_3GPP_USSD_SESSION_STATE_IDLE); if (error) g_task_return_error (task, error); else g_task_return_boolean (task, TRUE); g_object_unref (task); if (response) mbim_message_unref (response); } static void modem_3gpp_ussd_cancel (MMIfaceModem3gppUssd *_self, GAsyncReadyCallback callback, gpointer user_data) { MMBroadbandModemMbim *self; MbimDevice *device; GTask *task; MbimMessage *message; GError *error = NULL; self = MM_BROADBAND_MODEM_MBIM (_self); if (!peek_device (self, &device, callback, user_data)) return; task = g_task_new (self, NULL, callback, user_data); message = mbim_message_ussd_set_new (MBIM_USSD_ACTION_CANCEL, 0, 0, NULL, &error); if (!message) { g_task_return_error (task, error); g_object_unref (task); return; } mbim_device_command (device, message, 10, NULL, (GAsyncReadyCallback)ussd_cancel_ready, task); mbim_message_unref (message); } /*****************************************************************************/ /* Check support (Messaging interface) */ static gboolean messaging_check_support_finish (MMIfaceModemMessaging *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void messaging_check_support (MMIfaceModemMessaging *_self, GAsyncReadyCallback callback, gpointer user_data) { MMBroadbandModemMbim *self = MM_BROADBAND_MODEM_MBIM (_self); GTask *task; task = g_task_new (self, NULL, callback, user_data); /* We only handle 3GPP messaging (PDU based) currently */ if (self->priv->caps_sms & MBIM_SMS_CAPS_PDU_RECEIVE && self->priv->caps_sms & MBIM_SMS_CAPS_PDU_SEND) { mm_obj_dbg (self, "messaging capabilities supported"); g_task_return_boolean (task, TRUE); } else { mm_obj_dbg (self, "messaging capabilities not supported by this modem"); g_task_return_boolean (task, FALSE); } g_object_unref (task); } /*****************************************************************************/ /* Load supported storages (Messaging interface) */ static gboolean messaging_load_supported_storages_finish (MMIfaceModemMessaging *self, GAsyncResult *res, GArray **mem1, GArray **mem2, GArray **mem3, GError **error) { MMSmsStorage supported; *mem1 = g_array_sized_new (FALSE, FALSE, sizeof (MMSmsStorage), 2); supported = MM_SMS_STORAGE_MT; g_array_append_val (*mem1, supported); *mem2 = g_array_ref (*mem1); *mem3 = g_array_ref (*mem1); return TRUE; } static void messaging_load_supported_storages (MMIfaceModemMessaging *self, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; task = g_task_new (self, NULL, callback, user_data); g_task_return_boolean (task, TRUE); g_object_unref (task); } /*****************************************************************************/ /* Load initial SMS parts */ static gboolean load_initial_sms_parts_finish (MMIfaceModemMessaging *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void add_sms_part (MMBroadbandModemMbim *self, const MbimSmsPduReadRecord *pdu) { MMSmsPart *part; g_autoptr(GError) error = NULL; part = mm_sms_part_3gpp_new_from_binary_pdu (pdu->message_index, pdu->pdu_data, pdu->pdu_data_size, self, FALSE, &error); if (part) { mm_obj_dbg (self, "correctly parsed PDU (%d)", pdu->message_index); mm_iface_modem_messaging_take_part (MM_IFACE_MODEM_MESSAGING (self), part, mm_sms_state_from_mbim_message_status (pdu->message_status), MM_SMS_STORAGE_MT); } else { /* Don't treat the error as critical */ mm_obj_dbg (self, "error parsing PDU (%d): %s", pdu->message_index, error->message); } } static gboolean process_pdu_messages (MMBroadbandModemMbim *self, MbimSmsFormat format, guint32 messages_count, MbimSmsPduReadRecordArray *pdu_messages, GError **error) { guint i; if (format != MBIM_SMS_FORMAT_PDU) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Ignoring SMS, unsupported format: '%s'", mbim_sms_format_get_string (format)); return FALSE; } if (!pdu_messages || !messages_count) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "No SMS PDUs read"); return FALSE; } mm_obj_dbg (self, "%u SMS PDUs reported", messages_count); for (i = 0; i < messages_count; i++) { if (pdu_messages[i]) { mm_obj_dbg (self, "processing SMS PDU %u/%u...", i+1, messages_count); add_sms_part (self, pdu_messages[i]); } else mm_obj_dbg (self, "ignoring invalid SMS PDU %u/%u...", i+1, messages_count); } return TRUE; } static void sms_read_query_ready (MbimDevice *device, GAsyncResult *res, GTask *task) { MMBroadbandModemMbim *self; GError *error = NULL; MbimSmsFormat format; guint32 messages_count; g_autoptr(MbimMessage) response = NULL; g_autoptr(MbimSmsPduReadRecordArray) pdu_messages = NULL; self = g_task_get_source_object (task); response = mbim_device_command_finish (device, res, &error); if (!response || !mbim_message_response_get_result (response, MBIM_MESSAGE_TYPE_COMMAND_DONE, &error) || !mbim_message_sms_read_response_parse ( response, &format, &messages_count, &pdu_messages, NULL, /* cdma_messages */ &error) || !process_pdu_messages (self, format, messages_count, pdu_messages, &error)) g_task_return_error (task, error); else g_task_return_boolean (task, TRUE); g_object_unref (task); } static void load_initial_sms_parts (MMIfaceModemMessaging *self, MMSmsStorage storage, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; MbimDevice *device; g_autoptr(MbimMessage) message = NULL; if (!peek_device (self, &device, callback, user_data)) return; g_assert (storage == MM_SMS_STORAGE_MT); task = g_task_new (self, NULL, callback, user_data); message = mbim_message_sms_read_query_new (MBIM_SMS_FORMAT_PDU, MBIM_SMS_FLAG_ALL, 0, /* message index, unused */ NULL); mbim_device_command (device, message, 10, NULL, (GAsyncReadyCallback)sms_read_query_ready, task); } /*****************************************************************************/ /* Setup/Cleanup unsolicited event handlers (Messaging interface) */ static gboolean common_setup_cleanup_unsolicited_events_messaging_finish (MMIfaceModemMessaging *self, GAsyncResult *res, GError **error) { return common_setup_cleanup_unsolicited_events_finish (MM_BROADBAND_MODEM_MBIM (self), res, error); } static void cleanup_unsolicited_events_messaging (MMIfaceModemMessaging *_self, GAsyncReadyCallback callback, gpointer user_data) { MMBroadbandModemMbim *self = MM_BROADBAND_MODEM_MBIM (_self); self->priv->setup_flags &= ~PROCESS_NOTIFICATION_FLAG_SMS_READ; common_setup_cleanup_unsolicited_events (self, FALSE, callback, user_data); } static void setup_unsolicited_events_messaging (MMIfaceModemMessaging *_self, GAsyncReadyCallback callback, gpointer user_data) { MMBroadbandModemMbim *self = MM_BROADBAND_MODEM_MBIM (_self); self->priv->setup_flags |= PROCESS_NOTIFICATION_FLAG_SMS_READ; common_setup_cleanup_unsolicited_events (self, TRUE, callback, user_data); } /*****************************************************************************/ /* Enable/Disable unsolicited event handlers (Messaging interface) */ static gboolean common_enable_disable_unsolicited_events_messaging_finish (MMIfaceModemMessaging *self, GAsyncResult *res, GError **error) { return common_enable_disable_unsolicited_events_finish (MM_BROADBAND_MODEM_MBIM (self), res, error); } static void disable_unsolicited_events_messaging (MMIfaceModemMessaging *_self, GAsyncReadyCallback callback, gpointer user_data) { MMBroadbandModemMbim *self = MM_BROADBAND_MODEM_MBIM (_self); self->priv->enable_flags &= ~PROCESS_NOTIFICATION_FLAG_SMS_READ; common_enable_disable_unsolicited_events (self, callback, user_data); } static void enable_unsolicited_events_messaging (MMIfaceModemMessaging *_self, GAsyncReadyCallback callback, gpointer user_data) { MMBroadbandModemMbim *self = MM_BROADBAND_MODEM_MBIM (_self); self->priv->enable_flags |= PROCESS_NOTIFICATION_FLAG_SMS_READ; common_enable_disable_unsolicited_events (self, callback, user_data); } /*****************************************************************************/ /* Create SMS (Messaging interface) */ static MMBaseSms * messaging_create_sms (MMIfaceModemMessaging *self) { return mm_sms_mbim_new (MM_BASE_MODEM (self)); } /*****************************************************************************/ /* Check support (SAR interface) */ static gboolean sar_check_support_finish (MMIfaceModemSar *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void sar_check_support (MMIfaceModemSar *_self, GAsyncReadyCallback callback, gpointer user_data) { MMBroadbandModemMbim *self = MM_BROADBAND_MODEM_MBIM (_self); GTask *task; task = g_task_new (self, NULL, callback, user_data); mm_obj_dbg (self, "SAR capabilities %s", self->priv->is_ms_sar_supported ? "supported" : "not supported"); g_task_return_boolean (task, self->priv->is_ms_sar_supported); g_object_unref (task); } /*****************************************************************************/ static gboolean sar_load_state_finish (MMIfaceModemSar *self, GAsyncResult *res, gboolean *out_state, GError **error) { GError *inner_error = NULL; gboolean result; result = g_task_propagate_boolean (G_TASK (res), &inner_error); if (inner_error) { g_propagate_error (error, inner_error); return FALSE; } if (out_state) *out_state = result; return TRUE; } static void sar_config_query_state_ready (MbimDevice *device, GAsyncResult *res, GTask *task) { g_autoptr(MbimMessage) response = NULL; GError *error = NULL; MbimSarBackoffState state; response = mbim_device_command_finish (device, res, &error); if (response && mbim_message_response_get_result (response, MBIM_MESSAGE_TYPE_COMMAND_DONE, &error) && mbim_message_ms_sar_config_response_parse ( response, NULL, &state, NULL, NULL, NULL, &error)) g_task_return_boolean (task, state); else g_task_return_error (task, error); g_object_unref (task); } static void sar_load_state (MMIfaceModemSar *_self, GAsyncReadyCallback callback, gpointer user_data) { MMBroadbandModemMbim *self = MM_BROADBAND_MODEM_MBIM (_self); MbimDevice *device; GTask *task; g_autoptr(MbimMessage) message = NULL; if (!peek_device (self, &device, callback, user_data)) return; task = g_task_new (self, NULL, callback, user_data); message = mbim_message_ms_sar_config_query_new (NULL); mbim_device_command (device, message, 10, NULL, (GAsyncReadyCallback)sar_config_query_state_ready, task); } /*****************************************************************************/ static gboolean sar_load_power_level_finish (MMIfaceModemSar *self, GAsyncResult *res, guint *out_power_level, GError **error) { gssize result; result = g_task_propagate_int (G_TASK (res), error); if (result < 0) return FALSE; *out_power_level = (guint) result; return TRUE; } static void sar_config_query_power_level_ready (MbimDevice *device, GAsyncResult *res, GTask *task) { MMBroadbandModemMbim *self; GError *error = NULL; guint32 states_count; g_autoptr(MbimSarConfigStateArray) config_states = NULL; g_autoptr(MbimMessage) response = NULL; self = g_task_get_source_object (task); response = mbim_device_command_finish (device, res, &error); if (response && mbim_message_response_get_result (response, MBIM_MESSAGE_TYPE_COMMAND_DONE, &error) && mbim_message_ms_sar_config_response_parse ( response, NULL, NULL, NULL, &states_count, &config_states, &error)) { if (states_count == 0) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_WRONG_STATE, "Couldn't load config states"); } else { if (states_count > 1) mm_obj_dbg (self, "Device reports SAR config states for %u antennas separately, but only considering the first one", states_count); g_task_return_int (task, config_states[0]->backoff_index); } } else g_task_return_error (task, error); g_object_unref (task); } static void sar_load_power_level (MMIfaceModemSar *_self, GAsyncReadyCallback callback, gpointer user_data) { MMBroadbandModemMbim *self = MM_BROADBAND_MODEM_MBIM (_self); MbimDevice *device; GTask *task; g_autoptr(MbimMessage) message = NULL; if (!peek_device (self, &device, callback, user_data)) return; task = g_task_new (self, NULL, callback, user_data); message = mbim_message_ms_sar_config_query_new (NULL); mbim_device_command (device, message, 10, NULL, (GAsyncReadyCallback)sar_config_query_power_level_ready, task); } /*****************************************************************************/ static gboolean sar_enable_finish (MMIfaceModemSar *self, GAsyncResult *res, guint *out_sar_power_level, GError **error) { guint level; if (!g_task_propagate_boolean (G_TASK (res), error)) return FALSE; level = GPOINTER_TO_UINT (g_task_get_task_data (G_TASK (res))); if (out_sar_power_level) *out_sar_power_level = level; return TRUE; } static void sar_config_set_enable_ready (MbimDevice *device, GAsyncResult *res, GTask *task) { g_autoptr(MbimMessage) response = NULL; GError *error = NULL; response = mbim_device_command_finish (device, res, &error); if (response && mbim_message_response_get_result (response, MBIM_MESSAGE_TYPE_COMMAND_DONE, &error)) { g_task_return_boolean (task, TRUE); } else g_task_return_error (task, error); g_object_unref (task); } static void sar_enable (MMIfaceModemSar *_self, gboolean enable, GAsyncReadyCallback callback, gpointer user_data) { MMBroadbandModemMbim *self = MM_BROADBAND_MODEM_MBIM (_self); MbimDevice *device; GTask *task; g_autoptr(MbimMessage) message = NULL; g_autofree MbimSarConfigState *config_state = NULL; if (!peek_device (self, &device, callback, user_data)) return; task = g_task_new (self, NULL, callback, user_data); /* * the value 0xFFFFFFFF means all antennas * the backoff index set to the current index of modem */ config_state = g_new (MbimSarConfigState, 1); config_state->antenna_index = 0xFFFFFFFF; config_state->backoff_index = mm_iface_modem_sar_get_power_level (_self); g_task_set_task_data (task, GUINT_TO_POINTER (config_state->backoff_index), NULL); message = mbim_message_ms_sar_config_set_new (MBIM_SAR_CONTROL_MODE_OS, enable ? MBIM_SAR_BACKOFF_STATE_ENABLED : MBIM_SAR_BACKOFF_STATE_DISABLED, 1, (const MbimSarConfigState **)&config_state, NULL); mbim_device_command (device, message, 10, NULL, (GAsyncReadyCallback)sar_config_set_enable_ready, task); } /*****************************************************************************/ static gboolean sar_set_power_level_finish (MMIfaceModemSar *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void sar_config_set_power_level_ready (MbimDevice *device, GAsyncResult *res, GTask *task) { g_autoptr(MbimMessage) response = NULL; GError *error = NULL; response = mbim_device_command_finish (device, res, &error); if (response && mbim_message_response_get_result (response, MBIM_MESSAGE_TYPE_COMMAND_DONE, &error)) { g_task_return_boolean (task, TRUE); } else g_task_return_error (task, error); g_object_unref (task); } static void sar_set_power_level (MMIfaceModemSar *_self, guint power_level, GAsyncReadyCallback callback, gpointer user_data) { MMBroadbandModemMbim *self = MM_BROADBAND_MODEM_MBIM (_self); MbimDevice *device; GTask *task; g_autoptr(MbimMessage) message = NULL; g_autofree MbimSarConfigState *config_state = NULL; if (!peek_device (self, &device, callback, user_data)) return; /* * the value 0xFFFFFFFF means all antennas * the backoff index set to the input power level */ config_state = g_new (MbimSarConfigState, 1); config_state->antenna_index = 0xFFFFFFFF; config_state->backoff_index = power_level; task = g_task_new (self, NULL, callback, user_data); message = mbim_message_ms_sar_config_set_new (MBIM_SAR_CONTROL_MODE_OS, MBIM_SAR_BACKOFF_STATE_ENABLED, 1, (const MbimSarConfigState **)&config_state, NULL); mbim_device_command (device, message, 10, NULL, (GAsyncReadyCallback)sar_config_set_power_level_ready, task); } /*****************************************************************************/ static void set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { MMBroadbandModemMbim *self = MM_BROADBAND_MODEM_MBIM (object); switch (prop_id) { #if defined WITH_QMI && QMI_MBIM_QMUX_SUPPORTED case PROP_QMI_UNSUPPORTED: self->priv->qmi_unsupported = g_value_get_boolean (value); break; #endif case PROP_INTEL_FIRMWARE_UPDATE_UNSUPPORTED: self->priv->intel_firmware_update_unsupported = 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) { MMBroadbandModemMbim *self = MM_BROADBAND_MODEM_MBIM (object); switch (prop_id) { #if defined WITH_QMI && QMI_MBIM_QMUX_SUPPORTED case PROP_QMI_UNSUPPORTED: g_value_set_boolean (value, self->priv->qmi_unsupported); break; #endif case PROP_INTEL_FIRMWARE_UPDATE_UNSUPPORTED: g_value_set_boolean (value, self->priv->intel_firmware_update_unsupported); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } /*****************************************************************************/ /* Create SIMs in all SIM slots */ typedef struct { GPtrArray *sim_slots; guint number_slots; guint query_slot_index; /* range [0,number_slots-1] */ guint active_slot_index; /* range [1,number_slots] */ } LoadSimSlotsContext; static void load_sim_slots_context_free (LoadSimSlotsContext *ctx) { g_clear_pointer (&ctx->sim_slots, g_ptr_array_unref); g_slice_free (LoadSimSlotsContext, ctx); } static void sim_slot_free (MMBaseSim *sim) { if (sim) g_object_unref (sim); } static gboolean load_sim_slots_finish (MMIfaceModem *self, GAsyncResult *res, GPtrArray **sim_slots, guint *primary_sim_slot, GError **error) { LoadSimSlotsContext *ctx; if (!g_task_propagate_boolean (G_TASK(res), error)) return FALSE; ctx = g_task_get_task_data (G_TASK (res)); if (sim_slots) *sim_slots = g_steal_pointer (&ctx->sim_slots); if (primary_sim_slot) *primary_sim_slot = ctx->active_slot_index; return TRUE; } static void query_slot_information_status (MbimDevice *device, guint slot_index, GTask *task); static void query_slot_information_status_ready (MbimDevice *device, GAsyncResult *res, GTask *task) { MMBroadbandModemMbim *self; g_autoptr(MbimMessage) response = NULL; GError *error = NULL; guint32 slot_index; MbimUiccSlotState slot_state; LoadSimSlotsContext *ctx; MMBaseSim *sim; gboolean sim_active = FALSE; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); response = mbim_device_command_finish (device, res, &error); if (!response || !mbim_message_response_get_result (response, MBIM_MESSAGE_TYPE_COMMAND_DONE, &error) || !mbim_message_ms_basic_connect_extensions_slot_info_status_response_parse ( response, &slot_index, &slot_state, &error)) { g_task_return_error (task, error); g_object_unref (task); return; } /* the slot index in MM starts at 1 */ if ((slot_index + 1) == ctx->active_slot_index) sim_active = TRUE; sim = create_sim_from_slot_state (self, sim_active, slot_index, slot_state); g_ptr_array_add (ctx->sim_slots, sim); ctx->query_slot_index++; if (ctx->query_slot_index < ctx->number_slots) { query_slot_information_status (device, ctx->query_slot_index, task); return; } g_task_return_boolean (task, TRUE); g_object_unref (task); } static void query_slot_information_status (MbimDevice *device, guint slot_index, GTask *task) { g_autoptr(MbimMessage) message = NULL; message = mbim_message_ms_basic_connect_extensions_slot_info_status_query_new (slot_index, NULL); mbim_device_command (device, message, 10, NULL, (GAsyncReadyCallback)query_slot_information_status_ready, task); } static void query_device_slot_mappings_ready (MbimDevice *device, GAsyncResult *res, GTask *task) { MMBroadbandModemMbim *self; g_autoptr(MbimMessage) response = NULL; GError *error = NULL; guint32 map_count = 0; g_autoptr(MbimSlotArray) slot_mappings = NULL; LoadSimSlotsContext *ctx; ctx = g_task_get_task_data (task); self = g_task_get_source_object (task); response = mbim_device_command_finish (device, res, &error); if (!response || !mbim_message_response_get_result (response, MBIM_MESSAGE_TYPE_COMMAND_DONE, &error) || !mbim_message_ms_basic_connect_extensions_device_slot_mappings_response_parse ( response, &map_count, &slot_mappings, &error)) { g_task_return_error (task, error); g_object_unref (task); return; } if (self->priv->executor_index >= map_count) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_NOT_FOUND, "The executor index doesn't have an entry in the map count"); g_object_unref (task); return; } /* the slot index in MM starts at 1 */ ctx->active_slot_index = slot_mappings[self->priv->executor_index]->slot + 1; self->priv->active_slot_index = ctx->active_slot_index; query_slot_information_status (device, ctx->query_slot_index, task); } static void query_device_slot_mappings (MbimDevice *device, GTask *task) { g_autoptr(MbimMessage) message = NULL; message = mbim_message_ms_basic_connect_extensions_device_slot_mappings_query_new (NULL); mbim_device_command (device, message, 10, NULL, (GAsyncReadyCallback)query_device_slot_mappings_ready, task); } static void query_device_caps_ready (MbimDevice *device, GAsyncResult *res, GTask *task) { MMBroadbandModemMbim *self; g_autoptr(MbimMessage) response = NULL; GError *error = NULL; guint32 executor_index; self = g_task_get_source_object (task); response = mbim_device_command_finish (device, res, &error); if (!response || !mbim_message_response_get_result (response, MBIM_MESSAGE_TYPE_COMMAND_DONE, &error) || !mbim_message_ms_basic_connect_extensions_device_caps_response_parse ( response, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, &executor_index, &error)) { g_task_return_error (task, error); g_object_unref (task); return; } self->priv->executor_index = executor_index; query_device_slot_mappings (device, task); } static void query_sys_caps_ready (MbimDevice *device, GAsyncResult *res, GTask *task) { MMBroadbandModemMbim *self; g_autoptr(MbimMessage) response = NULL; g_autoptr(MbimMessage) message = NULL; GError *error = NULL; guint32 number_executors; guint32 number_slots; guint32 concurrency; guint64 modem_id; LoadSimSlotsContext *ctx; ctx = g_task_get_task_data (task); self = g_task_get_source_object (task); response = mbim_device_command_finish (device, res, &error); if (!response || !mbim_message_response_get_result (response, MBIM_MESSAGE_TYPE_COMMAND_DONE, &error) || !mbim_message_ms_basic_connect_extensions_sys_caps_response_parse ( response, &number_executors, &number_slots, &concurrency, &modem_id, &error)) { g_task_return_error (task, error); g_object_unref (task); return; } if (number_slots == 1) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_NOT_FOUND, "Only one SIM slot is supported"); g_object_unref (task); return; } ctx->number_slots = number_slots; ctx->sim_slots = g_ptr_array_new_full (number_slots, (GDestroyNotify) sim_slot_free); if (number_executors == 0) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_NOT_FOUND, "There is no executor"); g_object_unref (task); return; } /* Given that there is one single executor supported,we assume the executor index to be always 0 */ if (number_executors == 1) { self->priv->executor_index = 0; query_device_slot_mappings (device, task); return; } /* Given that more than one executors supported,we first query the current device caps to know which is the current executor index */ message = mbim_message_ms_basic_connect_extensions_device_caps_query_new (NULL); mbim_device_command (device, message, 10, NULL, (GAsyncReadyCallback)query_device_caps_ready, task); } static void load_sim_slots_mbim (GTask *task) { MMBroadbandModemMbim *self; MbimDevice *device; g_autoptr(MbimMessage) message = NULL; self = g_task_get_source_object (task); if (!peek_device_in_task (self, &device, task)) return; message = mbim_message_ms_basic_connect_extensions_sys_caps_query_new (NULL); mbim_device_command (device, message, 10, NULL, (GAsyncReadyCallback)query_sys_caps_ready, task); } #if defined WITH_QMI && QMI_MBIM_QMUX_SUPPORTED static void shared_qmi_load_sim_slots_ready (MMIfaceModem *self, GAsyncResult *res, GTask *task) { g_autoptr(GPtrArray) sim_slots = NULL; guint primary_sim_slot = 0; LoadSimSlotsContext *ctx; if (!mm_shared_qmi_load_sim_slots_finish (self, res, &sim_slots, &primary_sim_slot, NULL)) { load_sim_slots_mbim (task); return; } ctx = g_task_get_task_data (task); ctx->sim_slots = g_steal_pointer (&sim_slots); ctx->active_slot_index = primary_sim_slot; g_task_return_boolean (task, TRUE); g_object_unref (task); } #endif static void load_sim_slots (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; LoadSimSlotsContext *ctx; task = g_task_new (self, NULL, callback, user_data); ctx = g_slice_new0 (LoadSimSlotsContext); g_task_set_task_data (task, ctx, (GDestroyNotify)load_sim_slots_context_free); #if defined WITH_QMI && QMI_MBIM_QMUX_SUPPORTED mm_shared_qmi_load_sim_slots (self, (GAsyncReadyCallback)shared_qmi_load_sim_slots_ready, task); #else load_sim_slots_mbim (task); #endif } /*****************************************************************************/ /* Set Primary SIM slot (modem interface) */ static gboolean set_primary_sim_slot_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void set_device_slot_mappings_ready (MbimDevice *device, GAsyncResult *res, GTask *task) { MMBroadbandModemMbim *self; g_autoptr(MbimMessage) response = NULL; GError *error = NULL; guint32 map_count = 0; g_autoptr(MbimSlotArray) slot_mappings = NULL; guint i; guint slot_number; self = g_task_get_source_object (task); g_assert (self->priv->pending_sim_slot_switch_action); /* the slot index in MM starts at 1 */ slot_number = GPOINTER_TO_UINT (g_task_get_task_data (task)) - 1; response = mbim_device_command_finish (device, res, &error); if (!response || !mbim_message_response_get_result (response, MBIM_MESSAGE_TYPE_COMMAND_DONE, &error) || !mbim_message_ms_basic_connect_extensions_device_slot_mappings_response_parse ( response, &map_count, &slot_mappings, &error)) { self->priv->pending_sim_slot_switch_action = FALSE; g_task_return_error (task, error); g_object_unref (task); return; } for (i = 0; i < map_count; i++) { if (i == self->priv->executor_index) { if (slot_number != slot_mappings[i]->slot) { self->priv->pending_sim_slot_switch_action = FALSE; g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_NOT_FOUND, "SIM slot switch to '%u' failed", slot_number); } else { /* Keep pending_sim_slot_switch_action flag TRUE to cleanly ignore SIM related indications * during slot swithing, We don't want SIM related indications received trigger the update * of SimSlots property, which may not be what we want as the modem object is being shutdown */ self->priv->active_slot_index = slot_number + 1; g_task_return_boolean (task, TRUE); } g_object_unref (task); return; } } self->priv->pending_sim_slot_switch_action = FALSE; g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_NOT_FOUND, "Can't find executor index '%u'", self->priv->executor_index); g_object_unref (task); } static void before_set_query_device_slot_mappings_ready (MbimDevice *device, GAsyncResult *res, GTask *task) { MMBroadbandModemMbim *self; g_autoptr(MbimMessage) response = NULL; GError *error = NULL; g_autoptr(MbimMessage) message = NULL; guint32 map_count = 0; g_autoptr(MbimSlotArray) slot_mappings = NULL; guint i; guint slot_number; self = g_task_get_source_object (task); /* the slot index in MM starts at 1 */ slot_number = GPOINTER_TO_UINT (g_task_get_task_data (task)) - 1; response = mbim_device_command_finish (device, res, &error); if (!response || !mbim_message_response_get_result (response, MBIM_MESSAGE_TYPE_COMMAND_DONE, &error) || !mbim_message_ms_basic_connect_extensions_device_slot_mappings_response_parse ( response, &map_count, &slot_mappings, &error)) { g_task_return_error (task, error); g_object_unref (task); return; } for (i = 0; i < map_count; i++) { if (slot_number == slot_mappings[i]->slot) { if (i != self->priv->executor_index) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_NOT_FOUND, "The sim slot '%u' is already used by executor '%u'", slot_number, i); } else { mm_obj_dbg (self, "The slot is already the requested one"); g_task_return_boolean (task, TRUE); } g_object_unref (task); return; } } /* Flag a pending SIM slot switch operation, so that we can ignore slot state updates * during the process. */ if (self->priv->pending_sim_slot_switch_action) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_IN_PROGRESS, "there is already an ongoing SIM slot switch operation"); g_object_unref (task); return; } self->priv->pending_sim_slot_switch_action = TRUE; for (i = 0; i < map_count; i++) { if (i == self->priv->executor_index) slot_mappings[i]->slot = slot_number; } message = mbim_message_ms_basic_connect_extensions_device_slot_mappings_set_new (map_count, (const MbimSlot **)slot_mappings, NULL); mbim_device_command (device, message, 10, NULL, (GAsyncReadyCallback)set_device_slot_mappings_ready, task); } static void set_primary_sim_slot_mbim (GTask *task) { MMBroadbandModemMbim *self; MbimDevice *device; g_autoptr(MbimMessage) message = NULL; self = g_task_get_source_object (task); if (!peek_device_in_task (self, &device, task)) return; message = mbim_message_ms_basic_connect_extensions_device_slot_mappings_query_new (NULL); mbim_device_command (device, message, 10, NULL, (GAsyncReadyCallback)before_set_query_device_slot_mappings_ready, task); } #if defined WITH_QMI && QMI_MBIM_QMUX_SUPPORTED static void shared_qmi_set_primary_sim_slot_ready (MMIfaceModem *self, GAsyncResult *res, GTask *task) { g_autoptr(GError) error = NULL; if (!mm_shared_qmi_set_primary_sim_slot_finish (self, res, &error)) { /* Fallback to MBIM */ if (g_error_matches (error, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED)) { set_primary_sim_slot_mbim (task); return; } g_task_return_error (task, g_steal_pointer (&error)); g_object_unref (task); return; } g_task_return_boolean (task, TRUE); g_object_unref (task); } #endif static void set_primary_sim_slot (MMIfaceModem *self, guint sim_slot, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; task = g_task_new (self, NULL, callback, user_data); g_task_set_task_data (task, GUINT_TO_POINTER (sim_slot), NULL); #if defined WITH_QMI && QMI_MBIM_QMUX_SUPPORTED mm_shared_qmi_set_primary_sim_slot (self, sim_slot, (GAsyncReadyCallback)shared_qmi_set_primary_sim_slot_ready, task); #else set_primary_sim_slot_mbim (task); #endif } /*****************************************************************************/ /* Set packet service state (3GPP interface) */ static gboolean set_packet_service_state_finish (MMIfaceModem3gpp *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void packet_service_set_ready (MbimDevice *device, GAsyncResult *res, GTask *task) { MMBroadbandModemMbim *self; g_autoptr(MbimMessage) response = NULL; g_autoptr(GError) error = NULL; MbimPacketServiceState requested_packet_service_state; MbimPacketServiceState packet_service_state = MBIM_PACKET_SERVICE_STATE_UNKNOWN; self = g_task_get_source_object (task); /* The NwError field is valid if MBIM_SET_PACKET_SERVICE response status code * equals MBIM_STATUS_FAILURE, so we parse the message both on success and on that * specific failure */ response = mbim_device_command_finish (device, res, &error); if (response && (mbim_message_response_get_result (response, MBIM_MESSAGE_TYPE_COMMAND_DONE, &error) || g_error_matches (error, MBIM_STATUS_ERROR, MBIM_STATUS_ERROR_FAILURE))) { g_autoptr(GError) inner_error = NULL; guint32 normalized_nw_error = 0; if (!common_process_packet_service (self, device, response, &normalized_nw_error, &packet_service_state, &inner_error)) { mm_obj_warn (self, "%s", inner_error->message); /* Prefer the error from the result to the parsing error */ if (!error) error = g_steal_pointer (&inner_error); } else { /* Prefer the NW error if available */ if (normalized_nw_error) { g_clear_error (&error); error = mm_error_from_mbim_nw_error (normalized_nw_error, self); } } } if (error) { if (g_error_matches (error, MBIM_STATUS_ERROR, MBIM_STATUS_ERROR_NO_DEVICE_SUPPORT)) g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "%s", error->message); else g_task_return_error (task, g_steal_pointer (&error)); g_object_unref (task); return; } requested_packet_service_state = GPOINTER_TO_UINT (g_task_get_task_data (task)); if (((requested_packet_service_state == MBIM_PACKET_SERVICE_STATE_ATTACHED) && (packet_service_state == MBIM_PACKET_SERVICE_STATE_ATTACHED || packet_service_state == MBIM_PACKET_SERVICE_STATE_ATTACHING)) || ((requested_packet_service_state == MBIM_PACKET_SERVICE_STATE_DETACHED) && (packet_service_state == MBIM_PACKET_SERVICE_STATE_DETACHED || packet_service_state == MBIM_PACKET_SERVICE_STATE_DETACHING))) g_task_return_boolean (task, TRUE); else g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Failed to request state %s, current state: %s", mbim_packet_service_state_get_string (requested_packet_service_state), mbim_packet_service_state_get_string (packet_service_state)); g_object_unref (task); } static void set_packet_service_state (MMIfaceModem3gpp *self, MMModem3gppPacketServiceState packet_service_state, GAsyncReadyCallback callback, gpointer user_data) { g_autoptr(MbimMessage) message = NULL; MbimDevice *device; GTask *task; MbimPacketServiceAction packet_service_action; MbimPacketServiceState requested_packet_service_state; g_assert ((packet_service_state == MM_MODEM_3GPP_PACKET_SERVICE_STATE_ATTACHED) || (packet_service_state == MM_MODEM_3GPP_PACKET_SERVICE_STATE_DETACHED)); if (!peek_device (self, &device, callback, user_data)) return; task = g_task_new (self, NULL, callback, user_data); switch (packet_service_state) { case MM_MODEM_3GPP_PACKET_SERVICE_STATE_ATTACHED: packet_service_action = MBIM_PACKET_SERVICE_ACTION_ATTACH; requested_packet_service_state = MBIM_PACKET_SERVICE_STATE_ATTACHED; break; case MM_MODEM_3GPP_PACKET_SERVICE_STATE_DETACHED: packet_service_action = MBIM_PACKET_SERVICE_ACTION_DETACH; requested_packet_service_state = MBIM_PACKET_SERVICE_STATE_DETACHED; break; case MM_MODEM_3GPP_PACKET_SERVICE_STATE_UNKNOWN: default: g_assert_not_reached (); } g_task_set_task_data (task, GUINT_TO_POINTER (requested_packet_service_state), NULL); message = mbim_message_packet_service_set_new (packet_service_action, NULL); mbim_device_command (device, message, 30, NULL, (GAsyncReadyCallback)packet_service_set_ready, task); } /*****************************************************************************/ /* Set carrier lock */ static gboolean modem_set_carrier_lock_finish (MMIfaceModem3gpp *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void set_carrier_lock_ready (MbimDevice *device, GAsyncResult *res, GTask *task) { g_autoptr(MbimMessage) response = NULL; GError *error = NULL; response = mbim_device_command_finish (device, res, &error); if (response && mbim_message_response_get_result (response, MBIM_MESSAGE_TYPE_COMMAND_DONE, &error)) { g_task_return_boolean (task, TRUE); } else if (g_error_matches (error, MBIM_STATUS_ERROR, MBIM_STATUS_ERROR_OPERATION_NOT_ALLOWED)) { g_clear_error (&error); g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "operation not allowed"); } else g_task_return_error (task, error); g_object_unref (task); } static void modem_set_carrier_lock (MMIfaceModem3gpp *_self, const guint8 *data, gsize data_size, GAsyncReadyCallback callback, gpointer user_data) { MMBroadbandModemMbim *self = MM_BROADBAND_MODEM_MBIM (_self); MbimDevice *device; g_autoptr(MbimMessage) message = NULL; GTask *task; if (!peek_device (self, &device, callback, user_data)) return; task = g_task_new (self, NULL, callback, user_data); if (!self->priv->is_google_carrier_lock_supported) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "Google carrier lock is not supported"); g_object_unref (task); return; } mm_obj_dbg (self, "Sending carrier lock request..."); message = mbim_message_google_carrier_lock_set_new (data_size, data, NULL); mbim_device_command (device, message, 10, NULL, (GAsyncReadyCallback)set_carrier_lock_ready, task); } /*****************************************************************************/ MMBroadbandModemMbim * mm_broadband_modem_mbim_new (const gchar *device, const gchar *physdev, const gchar **drivers, const gchar *plugin, guint16 vendor_id, guint16 product_id) { return g_object_new (MM_TYPE_BROADBAND_MODEM_MBIM, MM_BASE_MODEM_DEVICE, device, MM_BASE_MODEM_PHYSDEV, physdev, MM_BASE_MODEM_DRIVERS, drivers, MM_BASE_MODEM_PLUGIN, plugin, MM_BASE_MODEM_VENDOR_ID, vendor_id, MM_BASE_MODEM_PRODUCT_ID, product_id, /* MBIM bearer supports NET only */ MM_BASE_MODEM_DATA_NET_SUPPORTED, TRUE, MM_BASE_MODEM_DATA_TTY_SUPPORTED, FALSE, MM_IFACE_MODEM_SIM_HOT_SWAP_SUPPORTED, TRUE, MM_IFACE_MODEM_PERIODIC_SIGNAL_CHECK_DISABLED, TRUE, NULL); } static void mm_broadband_modem_mbim_init (MMBroadbandModemMbim *self) { /* Initialize private data */ self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, MM_TYPE_BROADBAND_MODEM_MBIM, MMBroadbandModemMbimPrivate); self->priv->enabled_cache.available_data_classes = MBIM_DATA_CLASS_NONE; self->priv->enabled_cache.highest_available_data_class = MBIM_DATA_CLASS_NONE; self->priv->enabled_cache.reg_state = MBIM_REGISTER_STATE_UNKNOWN; self->priv->enabled_cache.packet_service_state = MBIM_PACKET_SERVICE_STATE_UNKNOWN; self->priv->enabled_cache.last_ready_state = MBIM_SUBSCRIBER_READY_STATE_NOT_INITIALIZED; self->priv->enabled_cache.last_pin_type = MBIM_PIN_TYPE_UNKNOWN; } static void dispose (GObject *object) { MMBroadbandModemMbim *self = MM_BROADBAND_MODEM_MBIM (object); MMPortMbim *mbim; if (self->priv->enabling_signal_id && g_signal_handler_is_connected (self, self->priv->enabling_signal_id)) { g_signal_handler_disconnect (self, self->priv->enabling_signal_id); self->priv->enabling_signal_id = 0; } /* If any port cleanup is needed, it must be done during dispose(), as * the modem object will be affected by an explciit g_object_run_dispose() * that will remove all port references right away */ mbim = mm_broadband_modem_mbim_peek_port_mbim (self); if (mbim) { /* Explicitly remove notification handler */ self->priv->setup_flags = PROCESS_NOTIFICATION_FLAG_NONE; common_setup_cleanup_unsolicited_events_sync (self, mbim, FALSE); /* If we did open the MBIM port during initialization, close it now */ if (mm_port_mbim_is_open (mbim)) mm_port_mbim_close (mbim, NULL, NULL); } g_clear_object (&self->priv->unlock_retries); G_OBJECT_CLASS (mm_broadband_modem_mbim_parent_class)->dispose (object); } static void finalize (GObject *object) { MMBroadbandModemMbim *self = MM_BROADBAND_MODEM_MBIM (object); g_free (self->priv->caps_custom_data_class); g_free (self->priv->caps_device_id); g_free (self->priv->caps_firmware_info); g_free (self->priv->caps_hardware_info); g_free (self->priv->enabled_cache.current_operator_id); g_free (self->priv->enabled_cache.current_operator_name); g_free (self->priv->requested_operator_id); g_list_free_full (self->priv->enabled_cache.pco_list, g_object_unref); G_OBJECT_CLASS (mm_broadband_modem_mbim_parent_class)->finalize (object); } static void iface_modem_init (MMIfaceModem *iface) { iface_modem_parent = g_type_interface_peek_parent (iface); /* Initialization steps */ iface->load_supported_capabilities = modem_load_supported_capabilities; iface->load_supported_capabilities_finish = modem_load_supported_capabilities_finish; iface->load_current_capabilities = modem_load_current_capabilities; iface->load_current_capabilities_finish = modem_load_current_capabilities_finish; iface->set_current_capabilities = modem_set_current_capabilities; iface->set_current_capabilities_finish = modem_set_current_capabilities_finish; iface->load_manufacturer = modem_load_manufacturer; iface->load_manufacturer_finish = modem_load_manufacturer_finish; iface->load_model = modem_load_model; iface->load_model_finish = modem_load_model_finish; iface->load_revision = modem_load_revision; iface->load_revision_finish = modem_load_revision_finish; iface->load_hardware_revision = modem_load_hardware_revision; iface->load_hardware_revision_finish = modem_load_hardware_revision_finish; iface->load_equipment_identifier = modem_load_equipment_identifier; iface->load_equipment_identifier_finish = modem_load_equipment_identifier_finish; iface->load_device_identifier = modem_load_device_identifier; iface->load_device_identifier_finish = modem_load_device_identifier_finish; iface->load_supported_modes = modem_load_supported_modes; iface->load_supported_modes_finish = modem_load_supported_modes_finish; iface->load_current_modes = modem_load_current_modes; iface->load_current_modes_finish = modem_load_current_modes_finish; iface->set_current_modes = modem_set_current_modes; iface->set_current_modes_finish = modem_set_current_modes_finish; iface->load_unlock_required = modem_load_unlock_required; iface->load_unlock_required_finish = modem_load_unlock_required_finish; iface->load_unlock_retries = modem_load_unlock_retries; iface->load_unlock_retries_finish = modem_load_unlock_retries_finish; iface->load_own_numbers = modem_load_own_numbers; iface->load_own_numbers_finish = modem_load_own_numbers_finish; iface->load_power_state = modem_load_power_state; iface->load_power_state_finish = modem_load_power_state_finish; iface->modem_power_up = modem_power_up; iface->modem_power_up_finish = power_up_finish; iface->modem_power_down = modem_power_down; iface->modem_power_down_finish = power_down_finish; iface->reset = modem_reset; iface->reset_finish = modem_reset_finish; iface->load_supported_ip_families = modem_load_supported_ip_families; iface->load_supported_ip_families_finish = modem_load_supported_ip_families_finish; #if defined WITH_QMI && QMI_MBIM_QMUX_SUPPORTED iface->load_carrier_config = mm_shared_qmi_load_carrier_config; iface->load_carrier_config_finish = mm_shared_qmi_load_carrier_config_finish; iface->setup_carrier_config = mm_shared_qmi_setup_carrier_config; iface->setup_carrier_config_finish = mm_shared_qmi_setup_carrier_config_finish; iface->load_supported_bands = mm_shared_qmi_load_supported_bands; iface->load_supported_bands_finish = mm_shared_qmi_load_supported_bands_finish; iface->load_current_bands = mm_shared_qmi_load_current_bands; iface->load_current_bands_finish = mm_shared_qmi_load_current_bands_finish; iface->set_current_bands = mm_shared_qmi_set_current_bands; iface->set_current_bands_finish = mm_shared_qmi_set_current_bands_finish; #endif /* Additional actions */ iface->load_signal_quality = modem_load_signal_quality; iface->load_signal_quality_finish = modem_load_signal_quality_finish; iface->get_cell_info = modem_get_cell_info; iface->get_cell_info_finish = modem_get_cell_info_finish; /* Unneeded things */ iface->modem_after_power_up = NULL; iface->modem_after_power_up_finish = NULL; iface->load_supported_charsets = NULL; iface->load_supported_charsets_finish = NULL; iface->setup_flow_control = NULL; iface->setup_flow_control_finish = NULL; iface->setup_charset = NULL; iface->setup_charset_finish = NULL; iface->load_access_technologies = NULL; iface->load_access_technologies_finish = NULL; /* Create MBIM-specific SIM */ iface->create_sim = create_sim; iface->create_sim_finish = create_sim_finish; iface->load_sim_slots = load_sim_slots; iface->load_sim_slots_finish = load_sim_slots_finish; iface->set_primary_sim_slot = set_primary_sim_slot; iface->set_primary_sim_slot_finish = set_primary_sim_slot_finish; /* Create MBIM-specific bearer and bearer list */ iface->create_bearer = modem_create_bearer; iface->create_bearer_finish = modem_create_bearer_finish; iface->create_bearer_list = modem_create_bearer_list; /* SIM hot swapping */ iface->setup_sim_hot_swap = modem_setup_sim_hot_swap; iface->setup_sim_hot_swap_finish = modem_setup_sim_hot_swap_finish; iface->check_basic_sim_details = modem_check_basic_sim_details; iface->check_basic_sim_details_finish = modem_check_basic_sim_details_finish; /* Other actions */ #if defined WITH_QMI && QMI_MBIM_QMUX_SUPPORTED iface->factory_reset = mm_shared_qmi_factory_reset; iface->factory_reset_finish = mm_shared_qmi_factory_reset_finish; #endif } static void iface_modem_3gpp_init (MMIfaceModem3gpp *iface) { /* Initialization steps */ iface->load_imei = modem_3gpp_load_imei; iface->load_imei_finish = modem_3gpp_load_imei_finish; iface->load_enabled_facility_locks = modem_3gpp_load_enabled_facility_locks; iface->load_enabled_facility_locks_finish = modem_3gpp_load_enabled_facility_locks_finish; iface->setup_unsolicited_events = setup_unsolicited_events_3gpp; iface->setup_unsolicited_events_finish = common_setup_cleanup_unsolicited_events_3gpp_finish; iface->cleanup_unsolicited_events = cleanup_unsolicited_events_3gpp; iface->cleanup_unsolicited_events_finish = common_setup_cleanup_unsolicited_events_3gpp_finish; iface->enable_unsolicited_events = modem_3gpp_enable_unsolicited_events; iface->enable_unsolicited_events_finish = modem_3gpp_common_enable_disable_unsolicited_events_finish; iface->disable_unsolicited_events = modem_3gpp_disable_unsolicited_events; iface->disable_unsolicited_events_finish = modem_3gpp_common_enable_disable_unsolicited_events_finish; iface->setup_unsolicited_registration_events = setup_unsolicited_registration_events; iface->setup_unsolicited_registration_events_finish = common_setup_cleanup_unsolicited_events_3gpp_finish; iface->cleanup_unsolicited_registration_events = cleanup_unsolicited_registration_events; iface->cleanup_unsolicited_registration_events_finish = common_setup_cleanup_unsolicited_events_3gpp_finish; iface->enable_unsolicited_registration_events = modem_3gpp_enable_unsolicited_registration_events; iface->enable_unsolicited_registration_events_finish = modem_3gpp_common_enable_disable_unsolicited_registration_events_finish; iface->disable_unsolicited_registration_events = modem_3gpp_disable_unsolicited_registration_events; iface->disable_unsolicited_registration_events_finish = modem_3gpp_common_enable_disable_unsolicited_registration_events_finish; iface->load_operator_code = modem_3gpp_load_operator_code; iface->load_operator_code_finish = modem_3gpp_load_operator_code_finish; iface->load_operator_name = modem_3gpp_load_operator_name; iface->load_operator_name_finish = modem_3gpp_load_operator_name_finish; iface->load_initial_eps_bearer = modem_3gpp_load_initial_eps_bearer; iface->load_initial_eps_bearer_finish = modem_3gpp_load_initial_eps_bearer_finish; iface->load_initial_eps_bearer_settings = modem_3gpp_load_initial_eps_bearer_settings; iface->load_initial_eps_bearer_settings_finish = modem_3gpp_load_initial_eps_bearer_settings_finish; iface->load_nr5g_registration_settings = modem_3gpp_load_nr5g_registration_settings; iface->load_nr5g_registration_settings_finish = modem_3gpp_load_nr5g_registration_settings_finish; iface->set_initial_eps_bearer_settings = modem_3gpp_set_initial_eps_bearer_settings; iface->set_initial_eps_bearer_settings_finish = modem_3gpp_set_initial_eps_bearer_settings_finish; iface->set_nr5g_registration_settings = modem_3gpp_set_nr5g_registration_settings; iface->set_nr5g_registration_settings_finish = modem_3gpp_set_nr5g_registration_settings_finish; iface->run_registration_checks = modem_3gpp_run_registration_checks; iface->run_registration_checks_finish = modem_3gpp_run_registration_checks_finish; iface->register_in_network = modem_3gpp_register_in_network; iface->register_in_network_finish = modem_3gpp_register_in_network_finish; iface->scan_networks = modem_3gpp_scan_networks; iface->scan_networks_finish = modem_3gpp_scan_networks_finish; iface->disable_facility_lock = modem_3gpp_disable_facility_lock; iface->disable_facility_lock_finish = modem_3gpp_disable_facility_lock_finish; iface->set_packet_service_state = set_packet_service_state; iface->set_packet_service_state_finish = set_packet_service_state_finish; /* carrier lock */ iface->set_carrier_lock = modem_set_carrier_lock; iface->set_carrier_lock_finish = modem_set_carrier_lock_finish; } static void iface_modem_3gpp_profile_manager_init (MMIfaceModem3gppProfileManager *iface) { /* Initialization steps */ iface->check_support = modem_3gpp_profile_manager_check_support; iface->check_support_finish = modem_3gpp_profile_manager_check_support_finish; /* Enabling steps */ iface->setup_unsolicited_events = setup_unsolicited_events_3gpp_profile_manager; iface->setup_unsolicited_events_finish = common_setup_cleanup_unsolicited_events_3gpp_profile_manager_finish; iface->enable_unsolicited_events = enable_unsolicited_events_3gpp_profile_manager; iface->enable_unsolicited_events_finish = common_enable_disable_unsolicited_events_3gpp_profile_manager_finish; /* Disabling steps */ iface->cleanup_unsolicited_events = cleanup_unsolicited_events_3gpp_profile_manager; iface->cleanup_unsolicited_events_finish = common_setup_cleanup_unsolicited_events_3gpp_profile_manager_finish; iface->disable_unsolicited_events = disable_unsolicited_events_3gpp_profile_manager; iface->disable_unsolicited_events_finish = common_enable_disable_unsolicited_events_3gpp_profile_manager_finish; /* Additional actions */ iface->list_profiles = modem_3gpp_profile_manager_list_profiles; iface->list_profiles_finish = modem_3gpp_profile_manager_list_profiles_finish; iface->check_format = modem_3gpp_profile_manager_check_format; iface->check_format_finish = modem_3gpp_profile_manager_check_format_finish; iface->check_activated_profile = NULL; iface->check_activated_profile_finish = NULL; iface->deactivate_profile = NULL; iface->deactivate_profile_finish = NULL; iface->store_profile = modem_3gpp_profile_manager_store_profile; iface->store_profile_finish = modem_3gpp_profile_manager_store_profile_finish; iface->delete_profile = modem_3gpp_profile_manager_delete_profile; iface->delete_profile_finish = modem_3gpp_profile_manager_delete_profile_finish; } static void iface_modem_3gpp_ussd_init (MMIfaceModem3gppUssd *iface) { /* Initialization steps */ iface->check_support = modem_3gpp_ussd_check_support; iface->check_support_finish = modem_3gpp_ussd_check_support_finish; /* Enabling steps */ iface->setup_unsolicited_events = modem_3gpp_ussd_setup_unsolicited_events; iface->setup_unsolicited_events_finish = modem_3gpp_ussd_setup_cleanup_unsolicited_events_finish; iface->enable_unsolicited_events = modem_3gpp_ussd_enable_unsolicited_events; iface->enable_unsolicited_events_finish = modem_3gpp_ussd_enable_disable_unsolicited_events_finish; /* Disabling steps */ iface->cleanup_unsolicited_events_finish = modem_3gpp_ussd_setup_cleanup_unsolicited_events_finish; iface->cleanup_unsolicited_events = modem_3gpp_ussd_cleanup_unsolicited_events; iface->disable_unsolicited_events = modem_3gpp_ussd_disable_unsolicited_events; iface->disable_unsolicited_events_finish = modem_3gpp_ussd_enable_disable_unsolicited_events_finish; /* Additional actions */ iface->send = modem_3gpp_ussd_send; iface->send_finish = modem_3gpp_ussd_send_finish; iface->cancel = modem_3gpp_ussd_cancel; iface->cancel_finish = modem_3gpp_ussd_cancel_finish; } static void iface_modem_location_init (MMIfaceModemLocation *iface) { #if defined WITH_QMI && QMI_MBIM_QMUX_SUPPORTED iface_modem_location_parent = g_type_interface_peek_parent (iface); iface->load_capabilities = mm_shared_qmi_location_load_capabilities; iface->load_capabilities_finish = mm_shared_qmi_location_load_capabilities_finish; iface->enable_location_gathering = mm_shared_qmi_enable_location_gathering; iface->enable_location_gathering_finish = mm_shared_qmi_enable_location_gathering_finish; iface->disable_location_gathering = mm_shared_qmi_disable_location_gathering; iface->disable_location_gathering_finish = mm_shared_qmi_disable_location_gathering_finish; iface->load_supl_server = mm_shared_qmi_location_load_supl_server; iface->load_supl_server_finish = mm_shared_qmi_location_load_supl_server_finish; iface->set_supl_server = mm_shared_qmi_location_set_supl_server; iface->set_supl_server_finish = mm_shared_qmi_location_set_supl_server_finish; iface->load_supported_assistance_data = mm_shared_qmi_location_load_supported_assistance_data; iface->load_supported_assistance_data_finish = mm_shared_qmi_location_load_supported_assistance_data_finish; iface->inject_assistance_data = mm_shared_qmi_location_inject_assistance_data; iface->inject_assistance_data_finish = mm_shared_qmi_location_inject_assistance_data_finish; iface->load_assistance_data_servers = mm_shared_qmi_location_load_assistance_data_servers; iface->load_assistance_data_servers_finish = mm_shared_qmi_location_load_assistance_data_servers_finish; #else iface->load_capabilities = NULL; iface->load_capabilities_finish = NULL; iface->enable_location_gathering = NULL; iface->enable_location_gathering_finish = NULL; #endif } static void iface_modem_messaging_init (MMIfaceModemMessaging *iface) { iface->check_support = messaging_check_support; iface->check_support_finish = messaging_check_support_finish; iface->load_supported_storages = messaging_load_supported_storages; iface->load_supported_storages_finish = messaging_load_supported_storages_finish; iface->setup_sms_format = NULL; iface->setup_sms_format_finish = NULL; iface->set_default_storage = NULL; iface->set_default_storage_finish = NULL; iface->init_current_storages = NULL; iface->init_current_storages_finish = NULL; iface->load_initial_sms_parts = load_initial_sms_parts; iface->load_initial_sms_parts_finish = load_initial_sms_parts_finish; iface->setup_unsolicited_events = setup_unsolicited_events_messaging; iface->setup_unsolicited_events_finish = common_setup_cleanup_unsolicited_events_messaging_finish; iface->cleanup_unsolicited_events = cleanup_unsolicited_events_messaging; iface->cleanup_unsolicited_events_finish = common_setup_cleanup_unsolicited_events_messaging_finish; iface->enable_unsolicited_events = enable_unsolicited_events_messaging; iface->enable_unsolicited_events_finish = common_enable_disable_unsolicited_events_messaging_finish; iface->disable_unsolicited_events = disable_unsolicited_events_messaging; iface->disable_unsolicited_events_finish = common_enable_disable_unsolicited_events_messaging_finish; iface->create_sms = messaging_create_sms; } static void iface_modem_signal_init (MMIfaceModemSignal *iface) { iface_modem_signal_parent = g_type_interface_peek_parent (iface); iface->check_support = modem_signal_check_support; iface->check_support_finish = modem_signal_check_support_finish; iface->load_values = modem_signal_load_values; iface->load_values_finish = modem_signal_load_values_finish; iface->setup_thresholds = modem_signal_setup_thresholds; iface->setup_thresholds_finish = modem_signal_setup_thresholds_finish; } static void iface_modem_sar_init (MMIfaceModemSar *iface) { iface->check_support = sar_check_support; iface->check_support_finish = sar_check_support_finish; iface->load_state = sar_load_state; iface->load_state_finish = sar_load_state_finish; iface->load_power_level = sar_load_power_level; iface->load_power_level_finish = sar_load_power_level_finish; iface->enable = sar_enable; iface->enable_finish = sar_enable_finish; iface->set_power_level = sar_set_power_level; iface->set_power_level_finish = sar_set_power_level_finish; } #if defined WITH_QMI && QMI_MBIM_QMUX_SUPPORTED static MMIfaceModemLocation * peek_parent_location_interface (MMSharedQmi *self) { return iface_modem_location_parent; } static void shared_qmi_init (MMSharedQmi *iface) { iface->peek_client = shared_qmi_peek_client; iface->peek_parent_location_interface = peek_parent_location_interface; } #endif static void mm_broadband_modem_mbim_class_init (MMBroadbandModemMbimClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); MMBroadbandModemClass *broadband_modem_class = MM_BROADBAND_MODEM_CLASS (klass); g_type_class_add_private (object_class, sizeof (MMBroadbandModemMbimPrivate)); klass->peek_port_mbim_for_data = peek_port_mbim_for_data; object_class->set_property = set_property; object_class->get_property = get_property; object_class->dispose = dispose; object_class->finalize = finalize; broadband_modem_class->initialization_started = initialization_started; broadband_modem_class->initialization_started_finish = initialization_started_finish; broadband_modem_class->enabling_started = enabling_started; broadband_modem_class->enabling_started_finish = enabling_started_finish; /* Do not initialize the MBIM modem through AT commands */ broadband_modem_class->enabling_modem_init = NULL; broadband_modem_class->enabling_modem_init_finish = NULL; #if defined WITH_QMI && QMI_MBIM_QMUX_SUPPORTED g_object_class_install_property (object_class, PROP_QMI_UNSUPPORTED, g_param_spec_boolean (MM_BROADBAND_MODEM_MBIM_QMI_UNSUPPORTED, "QMI over MBIM unsupported", "TRUE when QMI over MBIM should not be considered.", FALSE, G_PARAM_READWRITE)); #endif g_object_class_install_property (object_class, PROP_INTEL_FIRMWARE_UPDATE_UNSUPPORTED, g_param_spec_boolean (MM_BROADBAND_MODEM_MBIM_INTEL_FIRMWARE_UPDATE_UNSUPPORTED, "Intel Firmware Update service unsupported", "TRUE when the Intel Firmware Update service should not be considered.", FALSE, G_PARAM_READWRITE)); } ModemManager-1.23.4-dev/src/mm-broadband-modem-mbim.h000066400000000000000000000107071456466623000222360ustar00rootroot00000000000000/* -*- 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) 2013 Aleksander Morgado */ #ifndef MM_BROADBAND_MODEM_MBIM_H #define MM_BROADBAND_MODEM_MBIM_H #include "mm-broadband-modem.h" #define MM_TYPE_BROADBAND_MODEM_MBIM (mm_broadband_modem_mbim_get_type ()) #define MM_BROADBAND_MODEM_MBIM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_BROADBAND_MODEM_MBIM, MMBroadbandModemMbim)) #define MM_BROADBAND_MODEM_MBIM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_BROADBAND_MODEM_MBIM, MMBroadbandModemMbimClass)) #define MM_IS_BROADBAND_MODEM_MBIM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_BROADBAND_MODEM_MBIM)) #define MM_IS_BROADBAND_MODEM_MBIM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_BROADBAND_MODEM_MBIM)) #define MM_BROADBAND_MODEM_MBIM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_BROADBAND_MODEM_MBIM, MMBroadbandModemMbimClass)) typedef struct _MMBroadbandModemMbim MMBroadbandModemMbim; typedef struct _MMBroadbandModemMbimClass MMBroadbandModemMbimClass; typedef struct _MMBroadbandModemMbimPrivate MMBroadbandModemMbimPrivate; #define MM_BROADBAND_MODEM_MBIM_QMI_UNSUPPORTED "broadband-modem-mbim-qmi-unsupported" #define MM_BROADBAND_MODEM_MBIM_INTEL_FIRMWARE_UPDATE_UNSUPPORTED "broadband-modem-mbim-intel-firmware-update-unsupported" struct _MMBroadbandModemMbim { MMBroadbandModem parent; MMBroadbandModemMbimPrivate *priv; }; struct _MMBroadbandModemMbimClass{ MMBroadbandModemClass parent; MMPortMbim * (* peek_port_mbim_for_data) (MMBroadbandModemMbim *self, MMPort *data, GError **error); guint32 (* normalize_nw_error) (MMBroadbandModemMbim *self, guint32 nw_error); }; GType mm_broadband_modem_mbim_get_type (void); G_DEFINE_AUTOPTR_CLEANUP_FUNC (MMBroadbandModemMbim, g_object_unref) MMBroadbandModemMbim *mm_broadband_modem_mbim_new (const gchar *device, const gchar *physdev, const gchar **drivers, const gchar *plugin, guint16 vendor_id, guint16 product_id); MMPortMbim *mm_broadband_modem_mbim_peek_port_mbim (MMBroadbandModemMbim *self); MMPortMbim *mm_broadband_modem_mbim_peek_port_mbim_for_data (MMBroadbandModemMbim *self, MMPort *data, GError **error); MMPortMbim *mm_broadband_modem_mbim_get_port_mbim (MMBroadbandModemMbim *self); MMPortMbim *mm_broadband_modem_mbim_get_port_mbim_for_data (MMBroadbandModemMbim *self, MMPort *data, GError **error); guint32 mm_broadband_modem_mbim_normalize_nw_error (MMBroadbandModemMbim *self, guint32 nw_error); void mm_broadband_modem_mbim_set_unlock_retries (MMBroadbandModemMbim *self, MMModemLock lock_type, guint32 remaining_attempts); void mm_broadband_modem_mbim_get_speeds (MMBroadbandModemMbim *self, guint64 *uplink_speed, guint64 *downlink_speed); gboolean mm_broadband_modem_mbim_is_context_type_ext_supported (MMBroadbandModemMbim *self); #endif /* MM_BROADBAND_MODEM_MBIM_H */ ModemManager-1.23.4-dev/src/mm-broadband-modem-qmi.c000066400000000000000000020606371456466623000221040ustar00rootroot00000000000000/* -*- 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) 2012 Google Inc. * Copyright (C) 2014 Aleksander Morgado * Copyright (c) 2021-2023 Qualcomm Innovation Center, Inc. */ #include #include #include #include #include #include #include #include "mm-broadband-modem-qmi.h" #include "ModemManager.h" #include #include "mm-log.h" #include "mm-errors-types.h" #include "mm-modem-helpers.h" #include "mm-modem-helpers-qmi.h" #include "mm-iface-modem.h" #include "mm-iface-modem-3gpp.h" #include "mm-iface-modem-3gpp-profile-manager.h" #include "mm-iface-modem-3gpp-ussd.h" #include "mm-iface-modem-voice.h" #include "mm-iface-modem-cdma.h" #include "mm-iface-modem-messaging.h" #include "mm-iface-modem-location.h" #include "mm-iface-modem-firmware.h" #include "mm-iface-modem-sar.h" #include "mm-iface-modem-signal.h" #include "mm-iface-modem-oma.h" #include "mm-shared-qmi.h" #include "mm-sim-qmi.h" #include "mm-bearer-qmi.h" #include "mm-sms-qmi.h" #include "mm-sms-part-3gpp.h" #include "mm-sms-part-cdma.h" #include "mm-call-qmi.h" #include "mm-call-list.h" static void iface_modem_init (MMIfaceModem *iface); static void iface_modem_3gpp_init (MMIfaceModem3gpp *iface); static void iface_modem_3gpp_profile_manager_init (MMIfaceModem3gppProfileManager *iface); static void iface_modem_3gpp_ussd_init (MMIfaceModem3gppUssd *iface); static void iface_modem_voice_init (MMIfaceModemVoice *iface); static void iface_modem_cdma_init (MMIfaceModemCdma *iface); static void iface_modem_messaging_init (MMIfaceModemMessaging *iface); static void iface_modem_location_init (MMIfaceModemLocation *iface); static void iface_modem_oma_init (MMIfaceModemOma *iface); static void iface_modem_firmware_init (MMIfaceModemFirmware *iface); static void iface_modem_sar_init (MMIfaceModemSar *iface); static void iface_modem_signal_init (MMIfaceModemSignal *iface); static void shared_qmi_init (MMSharedQmi *iface); static MMIfaceModemLocation *iface_modem_location_parent; static MMIfaceModemMessaging *iface_modem_messaging_parent; static MMIfaceModemVoice *iface_modem_voice_parent; G_DEFINE_TYPE_EXTENDED (MMBroadbandModemQmi, mm_broadband_modem_qmi, MM_TYPE_BROADBAND_MODEM, 0, G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM, iface_modem_init) G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_3GPP, iface_modem_3gpp_init) G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_3GPP_PROFILE_MANAGER, iface_modem_3gpp_profile_manager_init) G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_3GPP_USSD, iface_modem_3gpp_ussd_init) G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_VOICE, iface_modem_voice_init) G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_CDMA, iface_modem_cdma_init) G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_MESSAGING, iface_modem_messaging_init) G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_LOCATION, iface_modem_location_init) G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_SAR, iface_modem_sar_init) G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_SIGNAL, iface_modem_signal_init) G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_OMA, iface_modem_oma_init) G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_FIRMWARE, iface_modem_firmware_init) G_IMPLEMENT_INTERFACE (MM_TYPE_SHARED_QMI, shared_qmi_init)) struct _MMBroadbandModemQmiPrivate { /* Cached device IDs, retrieved by the modem interface when loading device * IDs, and used afterwards in the 3GPP and CDMA interfaces. */ gchar *imei; gchar *meid; gchar *esn; /* Cached supported frequency bands; in order to handle ANY */ GArray *supported_bands; /* 3GPP and CDMA share unsolicited events setup/enable/disable/cleanup */ gboolean unsolicited_events_enabled; gboolean unsolicited_events_setup; guint nas_event_report_indication_id; guint wds_event_report_indication_id; guint nas_signal_info_indication_id; /* New devices may not support the legacy DMS UIM commands */ gboolean dms_uim_deprecated; /* Whether autoconnect disabling needs to be checked up during * the device enabling */ gboolean autoconnect_checked; /* Index of the WDS profile used as initial EPS bearer */ guint16 default_attach_pdn; /* Support for the APN type mask in profiles */ gboolean apn_type_not_supported; /* 3GPP/CDMA registration helpers */ gchar *current_operator_id; gchar *current_operator_description; gboolean unsolicited_registration_events_enabled; gboolean unsolicited_registration_events_setup; guint serving_system_indication_id; guint system_info_indication_id; guint system_status_indication_id; guint network_reject_indication_id; /* CDMA activation helpers */ MMModemCdmaActivationState activation_state; guint activation_event_report_indication_id; GTask *activation_task; /* Messaging helpers */ gboolean messaging_fallback_at_only; gboolean messaging_unsolicited_events_enabled; gboolean messaging_unsolicited_events_setup; guint messaging_event_report_indication_id; /* Location helpers */ MMModemLocationSource enabled_sources; /* Oma helpers */ gboolean oma_unsolicited_events_enabled; gboolean oma_unsolicited_events_setup; guint oma_event_report_indication_id; /* 3GPP USSD helpers */ guint ussd_indication_id; guint ussd_release_indication_id; gboolean ussd_unsolicited_events_enabled; gboolean ussd_unsolicited_events_setup; GTask *pending_ussd_action; /* Voice helpers */ guint all_call_status_indication_id; gboolean all_call_status_unsolicited_events_enabled; gboolean all_call_status_unsolicited_events_setup; guint supplementary_service_indication_id; gboolean supplementary_service_unsolicited_events_setup; /* Firmware helpers */ gboolean firmware_list_preloaded; GList *firmware_list; MMFirmwareProperties *current_firmware; /* For notifying when the qmi-proxy connection is dead */ guint qmi_device_removed_id; /* Power Set Operating Mode Helper */ GTask *set_operating_mode_task; /* PDC Refresh notifications ID (3gpp Profile Manager) */ gboolean profile_manager_unsolicited_events_enabled; gboolean profile_manager_unsolicited_events_setup; guint refresh_indication_id; /* WDS Profile changed notification ID (3gpp Profile Manager) */ guint profile_changed_indication_id; /* Packet service state helpers when using NAS System Info and DSD * (not applicable when using NAS Serving System) */ gboolean dsd_supported; }; /*****************************************************************************/ static QmiClient * shared_qmi_peek_client (MMSharedQmi *self, QmiService service, MMPortQmiFlag flag, GError **error) { MMPortQmi *port; QmiClient *client; port = mm_broadband_modem_qmi_peek_port_qmi (MM_BROADBAND_MODEM_QMI (self)); if (!port) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't peek QMI port"); return NULL; } client = mm_port_qmi_peek_client (port, service, flag); if (!client) g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't peek client for service '%s'", qmi_service_get_string (service)); return client; } /*****************************************************************************/ MMPortQmi * mm_broadband_modem_qmi_get_port_qmi (MMBroadbandModemQmi *self) { MMPortQmi *primary_qmi_port; g_assert (MM_IS_BROADBAND_MODEM_QMI (self)); primary_qmi_port = mm_broadband_modem_qmi_peek_port_qmi (self); return (primary_qmi_port ? MM_PORT_QMI (g_object_ref (primary_qmi_port)) : NULL); } MMPortQmi * mm_broadband_modem_qmi_peek_port_qmi (MMBroadbandModemQmi *self) { MMPortQmi *primary_qmi_port = NULL; GList *qmi_ports; g_assert (MM_IS_BROADBAND_MODEM_QMI (self)); qmi_ports = mm_base_modem_find_ports (MM_BASE_MODEM (self), MM_PORT_SUBSYS_UNKNOWN, MM_PORT_TYPE_QMI); /* First QMI port in the list is the primary one always */ if (qmi_ports) primary_qmi_port = MM_PORT_QMI (qmi_ports->data); g_list_free_full (qmi_ports, g_object_unref); return primary_qmi_port; } MMPortQmi * mm_broadband_modem_qmi_get_port_qmi_for_data (MMBroadbandModemQmi *self, MMPort *data, MMQmiDataEndpoint *out_endpoint, GError **error) { MMPortQmi *qmi_port; g_assert (MM_IS_BROADBAND_MODEM_QMI (self)); qmi_port = mm_broadband_modem_qmi_peek_port_qmi_for_data (self, data, out_endpoint, error); return (qmi_port ? MM_PORT_QMI (g_object_ref (qmi_port)) : NULL); } static MMPortQmi * peek_port_qmi_for_data_mhi (MMBroadbandModemQmi *self, MMPort *data, MMQmiDataEndpoint *out_endpoint, GError **error) { MMPortQmi *found = NULL; found = mm_broadband_modem_qmi_peek_port_qmi (self); if (!found) g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_NOT_FOUND, "Couldn't find associated QMI port for 'net/%s'", mm_port_get_device (data)); else if (out_endpoint) mm_port_qmi_get_endpoint_info (found, out_endpoint); return found; } static MMPortQmi * peek_port_qmi_for_data_usb (MMBroadbandModemQmi *self, MMPort *data, MMQmiDataEndpoint *out_endpoint, GError **error) { GList *cdc_wdm_qmi_ports; GList *l; const gchar *net_port_parent_path; MMPortQmi *found = NULL; net_port_parent_path = mm_kernel_device_get_interface_sysfs_path (mm_port_peek_kernel_device (data)); if (!net_port_parent_path) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "No parent path for 'net/%s'", mm_port_get_device (data)); return NULL; } /* Find the CDC-WDM port on the same USB interface as the given net port */ cdc_wdm_qmi_ports = mm_base_modem_find_ports (MM_BASE_MODEM (self), MM_PORT_SUBSYS_USBMISC, MM_PORT_TYPE_QMI); for (l = cdc_wdm_qmi_ports; l && !found; l = g_list_next (l)) { const gchar *wdm_port_parent_path; g_assert (MM_IS_PORT_QMI (l->data)); wdm_port_parent_path = mm_kernel_device_get_interface_sysfs_path (mm_port_peek_kernel_device (MM_PORT (l->data))); if (wdm_port_parent_path && g_str_equal (wdm_port_parent_path, net_port_parent_path)) found = MM_PORT_QMI (l->data); } g_list_free_full (cdc_wdm_qmi_ports, g_object_unref); if (!found) g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_NOT_FOUND, "Couldn't find associated QMI port for 'net/%s'", mm_port_get_device (data)); else if (out_endpoint) mm_port_qmi_get_endpoint_info (found, out_endpoint); return found; } static MMPortQmi * peek_port_qmi_for_data (MMBroadbandModemQmi *self, MMPort *data, MMQmiDataEndpoint *out_endpoint, GError **error) { const gchar *net_port_driver; g_assert (MM_IS_BROADBAND_MODEM_QMI (self)); g_assert (mm_port_get_subsys (data) == MM_PORT_SUBSYS_NET); net_port_driver = mm_kernel_device_get_driver (mm_port_peek_kernel_device (data)); if (!g_strcmp0 (net_port_driver, "qmi_wwan")) return peek_port_qmi_for_data_usb (self, data, out_endpoint, error); if (!g_strcmp0 (net_port_driver, "mhi_net")) return peek_port_qmi_for_data_mhi (self, data, out_endpoint, error); g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Unsupported QMI kernel driver for 'net/%s': %s", mm_port_get_device (data), net_port_driver); return NULL; } MMPortQmi * mm_broadband_modem_qmi_peek_port_qmi_for_data (MMBroadbandModemQmi *self, MMPort *data, MMQmiDataEndpoint *out_endpoint, GError **error) { g_assert (MM_BROADBAND_MODEM_QMI_GET_CLASS (self)->peek_port_qmi_for_data); return MM_BROADBAND_MODEM_QMI_GET_CLASS (self)->peek_port_qmi_for_data (self, data, out_endpoint, error); } /*****************************************************************************/ /* Create Bearer (Modem interface) */ static MMBaseBearer * modem_create_bearer_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { return g_task_propagate_pointer (G_TASK (res), error); } static void modem_create_bearer (MMIfaceModem *self, MMBearerProperties *properties, GAsyncReadyCallback callback, gpointer user_data) { MMBaseBearer *bearer; GTask *task; /* We just create a MMBearerQmi */ bearer = mm_bearer_qmi_new (MM_BROADBAND_MODEM_QMI (self), properties); task = g_task_new (self, NULL, callback, user_data); g_task_return_pointer (task, bearer, g_object_unref); g_object_unref (task); } /*****************************************************************************/ /* Create Bearer List (Modem interface) */ static MMBearerList * modem_create_bearer_list (MMIfaceModem *self) { MMPortQmi *port; guint n = 0; guint n_multiplexed = 0; port = mm_broadband_modem_qmi_peek_port_qmi (MM_BROADBAND_MODEM_QMI (self)); if (!port) { /* this should not happen, just fallback to defaults */ mm_obj_warn (self, "no port to query maximum number of supported network links"); } else { MMPortQmiKernelDataMode kernel_data_modes; kernel_data_modes = mm_port_qmi_get_kernel_data_modes (port); /* There are setups, like IPA, where there is ONLY multiplexing expected * and supported. In those cases, there isn't any expected non-multiplexed * bearer */ if (kernel_data_modes & (QMI_WDA_LINK_LAYER_PROTOCOL_RAW_IP | MM_PORT_QMI_KERNEL_DATA_MODE_802_3)) { /* The maximum number of available/connected modems is guessed from * the size of the data ports list. */ n = g_list_length (mm_base_modem_peek_data_ports (MM_BASE_MODEM (self))); mm_obj_dbg (self, "allowed up to %u active bearers", n); } if (kernel_data_modes & (MM_PORT_QMI_KERNEL_DATA_MODE_MUX_RMNET | MM_PORT_QMI_KERNEL_DATA_MODE_MUX_QMIWWAN)) { /* The maximum number of multiplexed links is retrieved from the MMPortQmi */ n_multiplexed = mm_port_qmi_get_max_multiplexed_links (port); mm_obj_dbg (self, "allowed up to %u active multiplexed bearers", n_multiplexed); if (mm_kernel_device_has_global_property (mm_port_peek_kernel_device (MM_PORT (port)), ID_MM_MAX_MULTIPLEXED_LINKS)) { guint n_multiplexed_limited; n_multiplexed_limited = mm_kernel_device_get_global_property_as_int ( mm_port_peek_kernel_device (MM_PORT (port)), ID_MM_MAX_MULTIPLEXED_LINKS); if (n_multiplexed_limited < n_multiplexed) { n_multiplexed = n_multiplexed_limited; mm_obj_dbg (self, "limited to %u active multiplexed bearers", n_multiplexed); } } } } /* by default, no multiplexing support */ return mm_bearer_list_new (n, n_multiplexed); } /*****************************************************************************/ /* Manufacturer loading (Modem interface) */ static gchar * modem_load_manufacturer_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { return g_task_propagate_pointer (G_TASK (res), error); } static void dms_get_manufacturer_ready (QmiClientDms *client, GAsyncResult *res, GTask *task) { QmiMessageDmsGetManufacturerOutput *output = NULL; GError *error = NULL; output = qmi_client_dms_get_manufacturer_finish (client, res, &error); if (!output) { g_prefix_error (&error, "QMI operation failed: "); g_task_return_error (task, error); } else if (!qmi_message_dms_get_manufacturer_output_get_result (output, &error)) { g_prefix_error (&error, "Couldn't get Manufacturer: "); g_task_return_error (task, error); } else { const gchar *str; qmi_message_dms_get_manufacturer_output_get_manufacturer (output, &str, NULL); g_task_return_pointer (task, g_strdup (str), g_free); } if (output) qmi_message_dms_get_manufacturer_output_unref (output); g_object_unref (task); } static void modem_load_manufacturer (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data) { QmiClient *client = NULL; if (!mm_shared_qmi_ensure_client (MM_SHARED_QMI (self), QMI_SERVICE_DMS, &client, callback, user_data)) return; mm_obj_dbg (self, "loading manufacturer..."); qmi_client_dms_get_manufacturer (QMI_CLIENT_DMS (client), NULL, 5, NULL, (GAsyncReadyCallback)dms_get_manufacturer_ready, g_task_new (self, NULL, callback, user_data)); } /*****************************************************************************/ /* Revision loading (Modem interface) */ static gchar * modem_load_revision_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { return g_task_propagate_pointer (G_TASK (res), error); } static void dms_get_revision_ready (QmiClientDms *client, GAsyncResult *res, GTask *task) { QmiMessageDmsGetRevisionOutput *output = NULL; GError *error = NULL; output = qmi_client_dms_get_revision_finish (client, res, &error); if (!output) { g_prefix_error (&error, "QMI operation failed: "); g_task_return_error (task, error); } else if (!qmi_message_dms_get_revision_output_get_result (output, &error)) { g_prefix_error (&error, "Couldn't get Revision: "); g_task_return_error (task, error); } else { const gchar *str; qmi_message_dms_get_revision_output_get_revision (output, &str, NULL); g_task_return_pointer (task, g_strdup (str), g_free); } if (output) qmi_message_dms_get_revision_output_unref (output); g_object_unref (task); } static void modem_load_revision (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data) { QmiClient *client = NULL; if (!mm_shared_qmi_ensure_client (MM_SHARED_QMI (self), QMI_SERVICE_DMS, &client, callback, user_data)) return; mm_obj_dbg (self, "loading revision..."); qmi_client_dms_get_revision (QMI_CLIENT_DMS (client), NULL, 5, NULL, (GAsyncReadyCallback)dms_get_revision_ready, g_task_new (self, NULL, callback, user_data)); } /*****************************************************************************/ /* Hardware Revision loading (Modem interface) */ static gchar * modem_load_hardware_revision_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { return g_task_propagate_pointer (G_TASK (res), error); } static void dms_get_hardware_revision_ready (QmiClientDms *client, GAsyncResult *res, GTask *task) { QmiMessageDmsGetHardwareRevisionOutput *output = NULL; GError *error = NULL; output = qmi_client_dms_get_hardware_revision_finish (client, res, &error); if (!output) { g_prefix_error (&error, "QMI operation failed: "); g_task_return_error (task, error); } else if (!qmi_message_dms_get_hardware_revision_output_get_result (output, &error)) { g_prefix_error (&error, "Couldn't get Hardware Revision: "); g_task_return_error (task, error); } else { const gchar *str; qmi_message_dms_get_hardware_revision_output_get_revision (output, &str, NULL); g_task_return_pointer (task, g_strdup (str), g_free); } if (output) qmi_message_dms_get_hardware_revision_output_unref (output); g_object_unref (task); } static void modem_load_hardware_revision (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data) { QmiClient *client = NULL; if (!mm_shared_qmi_ensure_client (MM_SHARED_QMI (self), QMI_SERVICE_DMS, &client, callback, user_data)) return; mm_obj_dbg (self, "loading hardware revision..."); qmi_client_dms_get_hardware_revision (QMI_CLIENT_DMS (client), NULL, 5, NULL, (GAsyncReadyCallback)dms_get_hardware_revision_ready, g_task_new (self, NULL, callback, user_data)); } /*****************************************************************************/ /* Equipment Identifier loading (Modem interface) */ static gchar * modem_load_equipment_identifier_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { return g_task_propagate_pointer (G_TASK (res), error); } static void dms_get_ids_ready (QmiClientDms *client, GAsyncResult *res, GTask *task) { MMBroadbandModemQmi *self; QmiMessageDmsGetIdsOutput *output = NULL; GError *error = NULL; const gchar *str; guint len; output = qmi_client_dms_get_ids_finish (client, res, &error); if (!output) { g_prefix_error (&error, "QMI operation failed: "); g_task_return_error (task, error); g_object_unref (task); return; } if (!qmi_message_dms_get_ids_output_get_result (output, &error)) { g_prefix_error (&error, "Couldn't get IDs: "); g_task_return_error (task, error); g_object_unref (task); qmi_message_dms_get_ids_output_unref (output); return; } self = g_task_get_source_object (task); /* In order: * If we have a IMEI, use it... * Otherwise, if we have a ESN, use it... * Otherwise, if we have a MEID, use it... * Otherwise, 'unknown' */ if (qmi_message_dms_get_ids_output_get_imei (output, &str, NULL) && str[0] != '\0') { g_free (self->priv->imei); self->priv->imei = g_strdup (str); } if (qmi_message_dms_get_ids_output_get_esn (output, &str, NULL) && str[0] != '\0') { g_clear_pointer (&self->priv->esn, g_free); len = strlen (str); if (len == 7) self->priv->esn = g_strdup_printf ("0%s", str); /* zero-pad to 8 chars */ else if (len == 8) self->priv->esn = g_strdup (str); else mm_obj_dbg (self, "invalid ESN reported: '%s' (unexpected length)", str); } if (qmi_message_dms_get_ids_output_get_meid (output, &str, NULL) && str[0] != '\0') { g_clear_pointer (&self->priv->meid, g_free); len = strlen (str); if (len == 14) self->priv->meid = g_strdup (str); else mm_obj_dbg (self, "invalid MEID reported: '%s' (unexpected length)", str); } if (self->priv->imei) str = self->priv->imei; else if (self->priv->esn) str = self->priv->esn; else if (self->priv->meid) str = self->priv->meid; else str = "unknown"; g_task_return_pointer (task, g_strdup (str), g_free); g_object_unref (task); qmi_message_dms_get_ids_output_unref (output); } static void modem_load_equipment_identifier (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data) { QmiClient *client = NULL; if (!mm_shared_qmi_ensure_client (MM_SHARED_QMI (self), QMI_SERVICE_DMS, &client, callback, user_data)) return; mm_obj_dbg (self, "loading equipment identifier..."); qmi_client_dms_get_ids (QMI_CLIENT_DMS (client), NULL, 5, NULL, (GAsyncReadyCallback)dms_get_ids_ready, g_task_new (self, NULL, callback, user_data)); } /*****************************************************************************/ /* Device identifier loading (Modem interface) */ static gchar * modem_load_device_identifier_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { return g_task_propagate_pointer (G_TASK (res), error); } static void modem_load_device_identifier (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data) { gchar *device_identifier; GTask *task; GError *error = NULL; task = g_task_new (self, NULL, callback, user_data); mm_obj_dbg (self, "loading device identifier..."); /* Just use placeholder ATI/ATI1 replies, all the other internal info should be * enough for uniqueness */ device_identifier = mm_broadband_modem_create_device_identifier (MM_BROADBAND_MODEM (self), "", "", &error); if (!device_identifier) g_task_return_error (task, error); else g_task_return_pointer (task, device_identifier, g_free); g_object_unref (task); } /*****************************************************************************/ /* Own Numbers loading (Modem interface) */ static GStrv modem_load_own_numbers_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { return g_task_propagate_pointer (G_TASK (res), error); } static void dms_get_msisdn_ready (QmiClientDms *client, GAsyncResult *res, GTask *task) { QmiMessageDmsGetMsisdnOutput *output = NULL; GError *error = NULL; output = qmi_client_dms_get_msisdn_finish (client, res, &error); if (!output) { g_prefix_error (&error, "QMI operation failed: "); g_task_return_error (task, error); } else if (!qmi_message_dms_get_msisdn_output_get_result (output, &error)) { g_prefix_error (&error, "Couldn't get MSISDN: "); g_task_return_error (task, error); } else { const gchar *str = NULL; GStrv numbers; qmi_message_dms_get_msisdn_output_get_msisdn (output, &str, NULL); numbers = g_new0 (gchar *, 2); numbers[0] = g_strdup (str); g_task_return_pointer (task, numbers, (GDestroyNotify)g_strfreev); } if (output) qmi_message_dms_get_msisdn_output_unref (output); g_object_unref (task); } static void modem_load_own_numbers (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data) { QmiClient *client = NULL; if (!mm_shared_qmi_ensure_client (MM_SHARED_QMI (self), QMI_SERVICE_DMS, &client, callback, user_data)) return; mm_obj_dbg (self, "loading own numbers..."); qmi_client_dms_get_msisdn (QMI_CLIENT_DMS (client), NULL, 5, NULL, (GAsyncReadyCallback)dms_get_msisdn_ready, g_task_new (self, NULL, callback, user_data)); } /*****************************************************************************/ /* Check if unlock required (Modem interface) */ typedef enum { LOAD_UNLOCK_REQUIRED_STEP_FIRST, LOAD_UNLOCK_REQUIRED_STEP_CDMA, LOAD_UNLOCK_REQUIRED_STEP_DMS, LOAD_UNLOCK_REQUIRED_STEP_UIM, } LoadUnlockRequiredStep; typedef struct { LoadUnlockRequiredStep step; gboolean last_attempt; } LoadUnlockRequiredContext; static MMModemLock modem_load_unlock_required_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { GError *inner_error = NULL; gssize value; value = g_task_propagate_int (G_TASK (res), &inner_error); if (inner_error) { g_propagate_error (error, inner_error); return MM_MODEM_LOCK_UNKNOWN; } return (MMModemLock)value; } static void load_unlock_required_context_step (GTask *task); static void unlock_required_uim_get_card_status_ready (QmiClientUim *client, GAsyncResult *res, GTask *task) { MMBroadbandModemQmi *self; LoadUnlockRequiredContext *ctx; QmiMessageUimGetCardStatusOutput *output; GError *error = NULL; MMModemLock lock = MM_MODEM_LOCK_UNKNOWN; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); output = qmi_client_uim_get_card_status_finish (client, res, &error); if (!output || !qmi_message_uim_get_card_status_output_get_result (output, &error)) { g_prefix_error (&error, "QMI operation failed: "); g_task_return_error (task, error); g_object_unref (task); return; } if (!mm_qmi_uim_get_card_status_output_parse (self, output, &lock, NULL, NULL, NULL, NULL, NULL, NULL, NULL, &error)) { /* The device may report a SIM NOT INSERTED error if we're querying the * card status soon after power on. We'll let the Modem interface generic * logic retry loading the info a bit later if that's the case. This will * make device detection slower when there's really no SIM card, but there's * no clear better way to handle it :/ */ if (g_error_matches (error, MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_SIM_NOT_INSERTED) && !ctx->last_attempt) { g_clear_error (&error); g_set_error (&error, MM_CORE_ERROR, MM_CORE_ERROR_RETRY, "No card found (retry)"); } g_prefix_error (&error, "QMI operation failed: "); g_task_return_error (task, error); } else g_task_return_int (task, lock); g_object_unref (task); qmi_message_uim_get_card_status_output_unref (output); } static void dms_uim_get_pin_status_ready (QmiClientDms *client, GAsyncResult *res, GTask *task) { MMBroadbandModemQmi *self; LoadUnlockRequiredContext *ctx; QmiMessageDmsUimGetPinStatusOutput *output; GError *error = NULL; MMModemLock lock = MM_MODEM_LOCK_UNKNOWN; QmiDmsUimPinStatus current_status; output = qmi_client_dms_uim_get_pin_status_finish (client, res, &error); if (!output) { g_prefix_error (&error, "QMI operation failed: "); g_task_return_error (task, error); g_object_unref (task); return; } self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); if (!qmi_message_dms_uim_get_pin_status_output_get_result (output, &error)) { /* We get InvalidQmiCommand on newer devices which don't like the legacy way */ if (g_error_matches (error, QMI_PROTOCOL_ERROR, QMI_PROTOCOL_ERROR_INVALID_QMI_COMMAND) || g_error_matches (error, QMI_PROTOCOL_ERROR, QMI_PROTOCOL_ERROR_NOT_SUPPORTED)) { g_error_free (error); qmi_message_dms_uim_get_pin_status_output_unref (output); /* Flag that the command is unsupported, and try with the new way */ self->priv->dms_uim_deprecated = TRUE; ctx->step++; load_unlock_required_context_step (task); return; } /* Internal and uim-uninitialized errors are retry-able before being fatal */ if (g_error_matches (error, QMI_PROTOCOL_ERROR, QMI_PROTOCOL_ERROR_INTERNAL) || g_error_matches (error, QMI_PROTOCOL_ERROR, QMI_PROTOCOL_ERROR_UIM_UNINITIALIZED)) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_RETRY, "Couldn't get PIN status (retry): %s", error->message); g_object_unref (task); g_error_free (error); qmi_message_dms_uim_get_pin_status_output_unref (output); return; } /* Other errors, just propagate them */ g_prefix_error (&error, "Couldn't get PIN status: "); g_task_return_error (task, error); g_object_unref (task); qmi_message_dms_uim_get_pin_status_output_unref (output); return; } /* Command succeeded, process results */ if (qmi_message_dms_uim_get_pin_status_output_get_pin1_status ( output, ¤t_status, NULL, /* verify_retries_left */ NULL, /* unblock_retries_left */ NULL)) lock = mm_modem_lock_from_qmi_uim_pin_status (current_status, TRUE); if (lock == MM_MODEM_LOCK_NONE && qmi_message_dms_uim_get_pin_status_output_get_pin2_status ( output, ¤t_status, NULL, /* verify_retries_left */ NULL, /* unblock_retries_left */ NULL)) { MMModemLock lock2; /* We only use the PIN2 status if it isn't unknown */ lock2 = mm_modem_lock_from_qmi_uim_pin_status (current_status, FALSE); if (lock2 != MM_MODEM_LOCK_UNKNOWN) lock = lock2; } /* We're done! */ qmi_message_dms_uim_get_pin_status_output_unref (output); g_task_return_int (task, lock); g_object_unref (task); } static void load_unlock_required_context_step (GTask *task) { MMBroadbandModemQmi *self; LoadUnlockRequiredContext *ctx; GError *error = NULL; QmiClient *client; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); if (g_task_return_error_if_cancelled (task)) { g_object_unref (task); return; } switch (ctx->step) { case LOAD_UNLOCK_REQUIRED_STEP_FIRST: ctx->step++; /* Fall through */ case LOAD_UNLOCK_REQUIRED_STEP_CDMA: /* CDMA-only modems don't need this */ if (mm_iface_modem_is_cdma_only (MM_IFACE_MODEM (self))) { mm_obj_dbg (self, "skipping unlock check in CDMA-only modem..."); g_task_return_int (task, MM_MODEM_LOCK_NONE); g_object_unref (task); return; } ctx->step++; /* Fall through */ case LOAD_UNLOCK_REQUIRED_STEP_DMS: if (!self->priv->dms_uim_deprecated) { /* Failure to get DMS client is hard really */ client = mm_shared_qmi_peek_client (MM_SHARED_QMI (self), QMI_SERVICE_DMS, MM_PORT_QMI_FLAG_DEFAULT, &error); if (!client) { g_task_return_error (task, error); g_object_unref (task); return; } mm_obj_dbg (self, "loading unlock required (DMS)..."); qmi_client_dms_uim_get_pin_status (QMI_CLIENT_DMS (client), NULL, 5, g_task_get_cancellable (task), (GAsyncReadyCallback) dms_uim_get_pin_status_ready, task); return; } ctx->step++; /* Fall through */ case LOAD_UNLOCK_REQUIRED_STEP_UIM: /* Failure to get UIM client at this point is hard as well */ client = mm_shared_qmi_peek_client (MM_SHARED_QMI (self), QMI_SERVICE_UIM, MM_PORT_QMI_FLAG_DEFAULT, &error); if (!client) { g_task_return_error (task, error); g_object_unref (task); return; } mm_obj_dbg (self, "loading unlock required (UIM)..."); qmi_client_uim_get_card_status (QMI_CLIENT_UIM (client), NULL, 5, g_task_get_cancellable (task), (GAsyncReadyCallback) unlock_required_uim_get_card_status_ready, task); return; default: g_assert_not_reached (); } } static void modem_load_unlock_required (MMIfaceModem *self, gboolean last_attempt, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { LoadUnlockRequiredContext *ctx; GTask *task; ctx = g_new0 (LoadUnlockRequiredContext, 1); ctx->step = LOAD_UNLOCK_REQUIRED_STEP_FIRST; ctx->last_attempt = last_attempt; task = g_task_new (self, cancellable, callback, user_data); g_task_set_task_data (task, ctx, g_free); load_unlock_required_context_step (task); } /*****************************************************************************/ /* Check if unlock retries (Modem interface) */ static MMUnlockRetries * modem_load_unlock_retries_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { return MM_UNLOCK_RETRIES (g_task_propagate_pointer (G_TASK (res), error)); } static void unlock_retries_uim_get_card_status_ready (QmiClientUim *client, GAsyncResult *res, GTask *task) { MMBroadbandModemQmi *self; QmiMessageUimGetCardStatusOutput *output; GError *error = NULL; guint pin1_retries = 0; guint puk1_retries = 0; guint pin2_retries = 0; guint puk2_retries = 0; guint pers_retries = 0; MMUnlockRetries *retries; MMModemLock lock = MM_MODEM_LOCK_UNKNOWN; self = g_task_get_source_object (task); output = qmi_client_uim_get_card_status_finish (client, res, &error); if (!output || !qmi_message_uim_get_card_status_output_get_result (output, &error)) { g_prefix_error (&error, "QMI operation failed: "); g_task_return_error (task, error); g_object_unref (task); return; } if (!mm_qmi_uim_get_card_status_output_parse (self, output, &lock, NULL, &pin1_retries, &puk1_retries, NULL, &pin2_retries, &puk2_retries, &pers_retries, &error)) { g_task_return_error (task, error); g_object_unref (task); return; } retries = mm_unlock_retries_new (); mm_unlock_retries_set (retries, MM_MODEM_LOCK_SIM_PIN, pin1_retries); mm_unlock_retries_set (retries, MM_MODEM_LOCK_SIM_PUK, puk1_retries); mm_unlock_retries_set (retries, MM_MODEM_LOCK_SIM_PIN2, pin2_retries); mm_unlock_retries_set (retries, MM_MODEM_LOCK_SIM_PUK2, puk2_retries); if (lock >= MM_MODEM_LOCK_PH_SP_PIN) mm_unlock_retries_set (retries, lock, pers_retries); qmi_message_uim_get_card_status_output_unref (output); g_task_return_pointer (task, retries, g_object_unref); g_object_unref (task); } static void uim_load_unlock_retries (MMBroadbandModemQmi *self, GTask *task) { QmiClient *client; GError *error = NULL; client = mm_shared_qmi_peek_client (MM_SHARED_QMI (self), QMI_SERVICE_UIM, MM_PORT_QMI_FLAG_DEFAULT, &error); if (!client) { g_task_return_error (task, error); g_object_unref (task); return; } qmi_client_uim_get_card_status (QMI_CLIENT_UIM (client), NULL, 5, NULL, (GAsyncReadyCallback) unlock_retries_uim_get_card_status_ready, task); } static void unlock_retries_dms_uim_get_pin_status_ready (QmiClientDms *client, GAsyncResult *res, GTask *task) { QmiMessageDmsUimGetPinStatusOutput *output; GError *error = NULL; MMBroadbandModemQmi *self; MMUnlockRetries *retries; guint8 verify_retries_left; guint8 unblock_retries_left; self = g_task_get_source_object (task); output = qmi_client_dms_uim_get_pin_status_finish (client, res, &error); if (!output) { g_prefix_error (&error, "QMI operation failed: "); g_task_return_error (task, error); g_object_unref (task); return; } if (!qmi_message_dms_uim_get_pin_status_output_get_result (output, &error)) { qmi_message_dms_uim_get_pin_status_output_unref (output); /* We get InvalidQmiCommand on newer devices which don't like the legacy way */ if (g_error_matches (error, QMI_PROTOCOL_ERROR, QMI_PROTOCOL_ERROR_INVALID_QMI_COMMAND)) { g_error_free (error); /* Flag that the command is unsupported, and try with the new way */ self->priv->dms_uim_deprecated = TRUE; uim_load_unlock_retries (self, task); return; } g_prefix_error (&error, "Couldn't get unlock retries: "); g_task_return_error (task, error); g_object_unref (task); return; } retries = mm_unlock_retries_new (); if (qmi_message_dms_uim_get_pin_status_output_get_pin1_status ( output, NULL, /* current_status */ &verify_retries_left, &unblock_retries_left, NULL)) { mm_unlock_retries_set (retries, MM_MODEM_LOCK_SIM_PIN, verify_retries_left); mm_unlock_retries_set (retries, MM_MODEM_LOCK_SIM_PUK, unblock_retries_left); } if (qmi_message_dms_uim_get_pin_status_output_get_pin2_status ( output, NULL, /* current_status */ &verify_retries_left, &unblock_retries_left, NULL)) { mm_unlock_retries_set (retries, MM_MODEM_LOCK_SIM_PIN2, verify_retries_left); mm_unlock_retries_set (retries, MM_MODEM_LOCK_SIM_PUK2, unblock_retries_left); } qmi_message_dms_uim_get_pin_status_output_unref (output); g_task_return_pointer (task, retries, g_object_unref); g_object_unref (task); } static void dms_uim_load_unlock_retries (MMBroadbandModemQmi *self, GTask *task) { QmiClient *client; client = mm_shared_qmi_peek_client (MM_SHARED_QMI (self), QMI_SERVICE_DMS, MM_PORT_QMI_FLAG_DEFAULT, NULL); if (!client) { /* Very unlikely that this will ever happen, but anyway, try with * UIM service instead */ uim_load_unlock_retries (self, task); return; } qmi_client_dms_uim_get_pin_status (QMI_CLIENT_DMS (client), NULL, 5, NULL, (GAsyncReadyCallback) unlock_retries_dms_uim_get_pin_status_ready, task); } static void modem_load_unlock_retries (MMIfaceModem *_self, GAsyncReadyCallback callback, gpointer user_data) { MMBroadbandModemQmi *self; GTask *task; self = MM_BROADBAND_MODEM_QMI (_self); task = g_task_new (self, NULL, callback, user_data); mm_obj_dbg (self, "loading unlock retries..."); if (!self->priv->dms_uim_deprecated) dms_uim_load_unlock_retries (MM_BROADBAND_MODEM_QMI (self), task); else uim_load_unlock_retries (MM_BROADBAND_MODEM_QMI (self), task); } /*****************************************************************************/ /* Load supported IP families (Modem interface) */ static MMBearerIpFamily modem_load_supported_ip_families_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { GError *inner_error = NULL; gssize value; value = g_task_propagate_int (G_TASK (res), &inner_error); if (inner_error) { g_propagate_error (error, inner_error); return MM_BEARER_IP_FAMILY_NONE; } return (MMBearerIpFamily)value; } static void modem_load_supported_ip_families (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; task = g_task_new (self, NULL, callback, user_data); if (mm_iface_modem_is_cdma_only (MM_IFACE_MODEM (self))) /* CDMA-only: IPv4 */ g_task_return_int (task, MM_BEARER_IP_FAMILY_IPV4); else /* Assume IPv4 + IPv6 supported */ g_task_return_int (task, MM_BEARER_IP_FAMILY_IPV4 | MM_BEARER_IP_FAMILY_IPV6 | MM_BEARER_IP_FAMILY_IPV4V6); g_object_unref (task); } /*****************************************************************************/ /* Load signal quality (Modem interface) */ #define RSSI_MAX -30 #define RSSI_MIN -125 #define RSRP_MAX -44 #define RSRP_MIN -140 #define SNR_MAX 40 #define SNR_MIN -23 #define RSRQ_MAX 20 #define RSRQ_MIN -43 static gboolean qmi_dbm_valid (gint8 dbm, QmiNasRadioInterface radio_interface) { /* Different radio interfaces have different signal quality bounds */ switch (radio_interface) { case QMI_NAS_RADIO_INTERFACE_CDMA_1X: case QMI_NAS_RADIO_INTERFACE_CDMA_1XEVDO: return (dbm > -125 && dbm < -30); case QMI_NAS_RADIO_INTERFACE_UMTS: return (dbm > -125 && dbm < -30); case QMI_NAS_RADIO_INTERFACE_UNKNOWN: case QMI_NAS_RADIO_INTERFACE_NONE: case QMI_NAS_RADIO_INTERFACE_AMPS: case QMI_NAS_RADIO_INTERFACE_GSM: case QMI_NAS_RADIO_INTERFACE_LTE: case QMI_NAS_RADIO_INTERFACE_TD_SCDMA: case QMI_NAS_RADIO_INTERFACE_5GNR: /* no explicit validation */ default: break; } return TRUE; } static guint load_signal_quality_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { GError *inner_error = NULL; gssize value; value = g_task_propagate_int (G_TASK (res), &inner_error); if (inner_error) { g_propagate_error (error, inner_error); return 0; } return value; } static gboolean common_signal_info_get_quality (MMBroadbandModemQmi *self, gint8 cdma1x_rssi, gint8 evdo_rssi, gint8 gsm_rssi, gint8 wcdma_rssi, gint8 lte_rssi, gint16 nr5g_rsrp, gint16 nr5g_snr, gint16 nr5g_rsrq, guint8 *out_quality) { gint8 rssi_max = -125; gint8 signal_quality = -1; /* Valid nr5g signal quality will be in percentage [0,100]. * It is minimum of (rsrp, snr, rsrq) signal quality for 5G. */ guint8 nr5g_signal_quality_min = 101; g_assert (out_quality != NULL); /* We do not report per-technology signal quality, so just get the highest * one of the ones reported. TODO: When several technologies are in use, if * the indication only contains the data of the one which passed a threshold * value, we'll need to have an internal cache of per-technology values, in * order to report always the one with the maximum value. */ if (cdma1x_rssi < 0) { mm_obj_dbg (self, "RSSI (CDMA): %d dBm", cdma1x_rssi); if (qmi_dbm_valid (cdma1x_rssi, QMI_NAS_RADIO_INTERFACE_CDMA_1X)) rssi_max = MAX (cdma1x_rssi, rssi_max); } if (evdo_rssi < 0) { mm_obj_dbg (self, "RSSI (HDR): %d dBm", evdo_rssi); if (qmi_dbm_valid (evdo_rssi, QMI_NAS_RADIO_INTERFACE_CDMA_1XEVDO)) rssi_max = MAX (evdo_rssi, rssi_max); } if (gsm_rssi < 0) { mm_obj_dbg (self, "RSSI (GSM): %d dBm", gsm_rssi); if (qmi_dbm_valid (gsm_rssi, QMI_NAS_RADIO_INTERFACE_GSM)) rssi_max = MAX (gsm_rssi, rssi_max); } if (wcdma_rssi < 0) { mm_obj_dbg (self, "RSSI (WCDMA): %d dBm", wcdma_rssi); if (qmi_dbm_valid (wcdma_rssi, QMI_NAS_RADIO_INTERFACE_UMTS)) rssi_max = MAX (wcdma_rssi, rssi_max); } if (lte_rssi < 0) { mm_obj_dbg (self, "RSSI (LTE): %d dBm", lte_rssi); if (qmi_dbm_valid (lte_rssi, QMI_NAS_RADIO_INTERFACE_LTE)) rssi_max = MAX (lte_rssi, rssi_max); } if (nr5g_rsrp <= RSRP_MAX && nr5g_rsrp >= RSRP_MIN) { mm_obj_dbg (self, "RSRP (5G): %d dBm", nr5g_rsrp); nr5g_signal_quality_min = MIN (nr5g_signal_quality_min, (guint8)((nr5g_rsrp - RSRP_MIN) * 100 / (RSRP_MAX - RSRP_MIN))); } if (nr5g_snr <= SNR_MAX && nr5g_snr >= SNR_MIN) { mm_obj_dbg (self, "SNR (5G): %d dB", nr5g_snr); nr5g_signal_quality_min = MIN (nr5g_signal_quality_min, (guint8)((nr5g_snr - SNR_MIN) * 100 / (SNR_MAX - SNR_MIN))); } if (nr5g_rsrq <= RSRQ_MAX && nr5g_rsrq >= RSRQ_MIN) { mm_obj_dbg (self, "RSRQ (5G): %d dB", nr5g_rsrq); nr5g_signal_quality_min = MIN (nr5g_signal_quality_min, (guint8)((nr5g_rsrq - RSRQ_MIN) * 100 / (RSRQ_MAX - RSRQ_MIN))); } if (rssi_max < 0 && rssi_max > -125) { /* This RSSI comes as negative dBms */ signal_quality = MM_RSSI_TO_QUALITY (rssi_max); mm_obj_dbg (self, "RSSI: %d dBm --> %u%%", rssi_max, signal_quality); } if (nr5g_signal_quality_min < 101 && nr5g_signal_quality_min >= signal_quality) { signal_quality = nr5g_signal_quality_min; mm_obj_dbg (self, "5G signal quality: %d%%", signal_quality); } if (signal_quality >= 0) { *out_quality = signal_quality; return TRUE; } return FALSE; } static gboolean signal_info_get_quality (MMBroadbandModemQmi *self, QmiMessageNasGetSignalInfoOutput *output, guint8 *out_quality) { gint8 cdma1x_rssi = 0; gint8 evdo_rssi = 0; gint8 gsm_rssi = 0; gint8 wcdma_rssi = 0; gint8 lte_rssi = 0; gint16 nr5g_rsrp = RSRP_MAX + 1; /* Multiplying SNR_MAX by 10 as QMI gives SNR level * as a scaled integer in units of 0.1 dB. */ gint16 nr5g_snr = 10 * SNR_MAX + 10; gint16 nr5g_rsrq = RSRQ_MAX + 1; qmi_message_nas_get_signal_info_output_get_cdma_signal_strength (output, &cdma1x_rssi, NULL, NULL); qmi_message_nas_get_signal_info_output_get_hdr_signal_strength (output, &evdo_rssi, NULL, NULL, NULL, NULL); qmi_message_nas_get_signal_info_output_get_gsm_signal_strength (output, &gsm_rssi, NULL); qmi_message_nas_get_signal_info_output_get_wcdma_signal_strength (output, &wcdma_rssi, NULL, NULL); qmi_message_nas_get_signal_info_output_get_lte_signal_strength (output, <e_rssi, NULL, NULL, NULL, NULL); qmi_message_nas_get_signal_info_output_get_5g_signal_strength (output, &nr5g_rsrp, &nr5g_snr, NULL); qmi_message_nas_get_signal_info_output_get_5g_signal_strength_extended (output, &nr5g_rsrq, NULL); /* Scale to integer values in units of 1 dB/dBm, if any */ nr5g_snr = 0.1 * nr5g_snr; return common_signal_info_get_quality (self, cdma1x_rssi, evdo_rssi, gsm_rssi, wcdma_rssi, lte_rssi, nr5g_rsrp, nr5g_snr, nr5g_rsrq, out_quality); } static gboolean signal_strength_get_quality (MMBroadbandModemQmi *self, QmiMessageNasGetSignalStrengthOutput *output, guint8 *o_quality) { GArray *array = NULL; gint8 signal_max = 0; QmiNasRadioInterface main_interface; /* We do not report per-technology signal quality, so just get the highest * one of the ones reported. */ /* The mandatory one is always present */ qmi_message_nas_get_signal_strength_output_get_signal_strength (output, &signal_max, &main_interface, NULL); mm_obj_dbg (self, "signal strength (%s): %d dBm", qmi_nas_radio_interface_get_string (main_interface), signal_max); /* Treat results as invalid if main signal strength is invalid */ if (!qmi_dbm_valid (signal_max, main_interface)) return FALSE; /* On multimode devices we may get more */ if (qmi_message_nas_get_signal_strength_output_get_strength_list (output, &array, NULL)) { guint i; for (i = 0; i < array->len; i++) { QmiMessageNasGetSignalStrengthOutputStrengthListElement *element; element = &g_array_index (array, QmiMessageNasGetSignalStrengthOutputStrengthListElement, i); mm_obj_dbg (self, "signal strength (%s): %d dBm", qmi_nas_radio_interface_get_string (element->radio_interface), element->strength); if (qmi_dbm_valid (element->strength, element->radio_interface)) { signal_max = MAX (element->strength, signal_max); } } } if (signal_max < 0) { /* This signal strength comes as negative dBms */ *o_quality = MM_RSSI_TO_QUALITY (signal_max); mm_obj_dbg (self, "signal strength: %d dBm --> %u%%", signal_max, *o_quality); } return (signal_max < 0); } static void get_signal_strength_ready (QmiClientNas *client, GAsyncResult *res, GTask *task) { MMBroadbandModemQmi *self; QmiMessageNasGetSignalStrengthOutput *output; GError *error = NULL; guint8 quality = 0; output = qmi_client_nas_get_signal_strength_finish (client, res, &error); if (!output) { g_task_return_error (task, error); g_object_unref (task); return; } if (!qmi_message_nas_get_signal_strength_output_get_result (output, &error)) { qmi_message_nas_get_signal_strength_output_unref (output); g_task_return_error (task, error); g_object_unref (task); return; } self = g_task_get_source_object (task); if (!signal_strength_get_quality (self, output, &quality)) { qmi_message_nas_get_signal_strength_output_unref (output); g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "GetSignalStrength signal strength invalid."); g_object_unref (task); return; } g_task_return_int (task, quality); g_object_unref (task); qmi_message_nas_get_signal_strength_output_unref (output); } static void get_signal_info_ready (QmiClientNas *client, GAsyncResult *res, GTask *task) { MMBroadbandModemQmi *self; QmiMessageNasGetSignalInfoOutput *output; GError *error = NULL; guint8 quality = 0; self = g_task_get_source_object (task); output = qmi_client_nas_get_signal_info_finish (client, res, &error); if (!output) { mm_obj_dbg (self, "couldn't get signal info: '%s': falling back to get signal strength", error->message); qmi_client_nas_get_signal_strength (client, NULL, 10, NULL, (GAsyncReadyCallback)get_signal_strength_ready, task); g_clear_error (&error); return; } if (!qmi_message_nas_get_signal_info_output_get_result (output, &error)) { qmi_message_nas_get_signal_info_output_unref (output); if (g_error_matches (error, QMI_PROTOCOL_ERROR, QMI_PROTOCOL_ERROR_INVALID_QMI_COMMAND) || g_error_matches (error, QMI_PROTOCOL_ERROR, QMI_PROTOCOL_ERROR_NOT_SUPPORTED)) { mm_obj_dbg (self, "couldn't get signal info: '%s': falling back to get signal strength", error->message); qmi_client_nas_get_signal_strength (client, NULL, 10, NULL, (GAsyncReadyCallback)get_signal_strength_ready, task); g_clear_error (&error); return; } g_task_return_error (task, error); g_object_unref (task); return; } if (!signal_info_get_quality (self, output, &quality)) { qmi_message_nas_get_signal_info_output_unref (output); g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Signal info reported invalid signal strength."); g_object_unref (task); return; } g_task_return_int (task, quality); g_object_unref (task); qmi_message_nas_get_signal_info_output_unref (output); } static void load_signal_quality (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data) { QmiClient *client = NULL; GTask *task; if (!mm_shared_qmi_ensure_client (MM_SHARED_QMI (self), QMI_SERVICE_NAS, &client, callback, user_data)) return; task = g_task_new (self, NULL, callback, user_data); mm_obj_dbg (self, "loading signal quality..."); qmi_client_nas_get_signal_info (QMI_CLIENT_NAS (client), NULL, 10, NULL, (GAsyncReadyCallback)get_signal_info_ready, task); } /*****************************************************************************/ /* Cell info */ static GList * get_cell_info_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { return g_task_propagate_pointer (G_TASK (res), error); } static void cell_info_list_free (GList *cell_info_list) { g_list_free_full (cell_info_list, g_object_unref); } /* Stolen from qmicli-nas.c as mm_bcd_to_string() doesn't correctly handle * special filler byte (0xF) for 2-digit MNCs. * ref: Table 10.5.3/3GPP TS 24.008 */ static gchar * str_from_bcd_plmn (GArray *bcd) { static const gchar bcd_chars[] = "0123456789*#abc\0\0"; gchar *str; guint i; guint j; if (!bcd || !bcd->len) return NULL; str = g_malloc (1 + (bcd->len * 2)); for (i = 0, j = 0 ; i < bcd->len; i++) { str[j] = bcd_chars[g_array_index (bcd, guint8, i) & 0xF]; if (str[j]) j++; str[j] = bcd_chars[(g_array_index (bcd, guint8, i) >> 4) & 0xF]; if (str[j]) j++; } str[j] = '\0'; return str; } static void get_cell_info_ready (QmiClientNas *client, GAsyncResult *res, GTask *task) { QmiMessageNasGetCellLocationInfoOutput *output; GError *error = NULL; GList *list = NULL; /* common vars */ GArray *operator; guint32 cell_id; guint16 arfcn; GArray* cell_array; GArray* frequency_array; guint16 lte_tac; guint16 lte_scell_id; guint32 lte_timing_advance; GArray *nr5g_tac; guint64 nr5g_global_ci; guint16 nr5g_pci; gint16 nr5g_rsrq; gint16 nr5g_rsrp; gint16 nr5g_snr; guint32 nr5g_arfcn; output = qmi_client_nas_get_cell_location_info_finish (client, res, &error); if (!output) { g_task_return_error (task, error); g_object_unref (task); return; } if (!qmi_message_nas_get_cell_location_info_output_get_result (output, &error)) { g_task_return_error (task, error); g_object_unref (task); qmi_message_nas_get_cell_location_info_output_unref (output); return; } if (qmi_message_nas_get_cell_location_info_output_get_intrafrequency_lte_info_v2 ( output, NULL /* ue in idle */, &operator, <e_tac, &cell_id, &arfcn, <e_scell_id, NULL /* cell reselect prio */, NULL /* non-intra search thres */, NULL /* scell low thres */, NULL /* s intra search thres */, &cell_array, &error)) { g_autofree gchar *operator_id = NULL; g_autofree gchar *tac = NULL; g_autofree gchar *ci = NULL; guint i; operator_id = str_from_bcd_plmn (operator); /* Encoded in upper-case hexadecimal format without leading zeros, as specified in 3GPP TS 27.007. */ tac = g_strdup_printf ("%X", lte_tac); ci = g_strdup_printf ("%X", cell_id); for (i = 0; i < cell_array->len; i++) { QmiMessageNasGetCellLocationInfoOutputIntrafrequencyLteInfoV2CellElement *element; MMCellInfoLte *lte_info; g_autofree gchar *pci = NULL; element = &g_array_index (cell_array, QmiMessageNasGetCellLocationInfoOutputIntrafrequencyLteInfoV2CellElement, i); lte_info = MM_CELL_INFO_LTE (mm_cell_info_lte_new_from_dictionary (NULL)); /* valid for all cells */ mm_cell_info_lte_set_operator_id (lte_info, operator_id); mm_cell_info_lte_set_tac (lte_info, tac); mm_cell_info_lte_set_earfcn (lte_info, arfcn); /* this cell */ pci = g_strdup_printf ("%X", element->physical_cell_id); mm_cell_info_lte_set_physical_ci (lte_info, pci); mm_cell_info_lte_set_rsrp (lte_info, (0.1) * ((gdouble)element->rsrp)); mm_cell_info_lte_set_rsrq (lte_info, (0.1) * ((gdouble)element->rsrq)); /* only for serving cell, we get details about CGI and TA */ if (element->physical_cell_id == lte_scell_id) { mm_cell_info_set_serving (MM_CELL_INFO (lte_info), TRUE); mm_cell_info_lte_set_ci (lte_info, ci); if (qmi_message_nas_get_cell_location_info_output_get_lte_info_timing_advance (output, <e_timing_advance, NULL)) { mm_cell_info_lte_set_timing_advance (lte_info, lte_timing_advance); } } list = g_list_append (list, g_steal_pointer (<e_info)); } } if (qmi_message_nas_get_cell_location_info_output_get_interfrequency_lte_info (output, NULL /* UE in idle */, &frequency_array, NULL)) { guint i; for (i = 0; i < frequency_array->len; i++) { QmiMessageNasGetCellLocationInfoOutputInterfrequencyLteInfoFrequencyElement *frequency; MMCellInfoLte *lte_info; guint j; frequency = &g_array_index (frequency_array, QmiMessageNasGetCellLocationInfoOutputInterfrequencyLteInfoFrequencyElement, i); arfcn = frequency->eutra_absolute_rf_channel_number; cell_array = frequency->cell; for (j = 0; j < cell_array->len; j++) { QmiMessageNasGetCellLocationInfoOutputInterfrequencyLteInfoFrequencyElementCellElement *cell; g_autofree gchar *pci = NULL; cell = &g_array_index (cell_array, QmiMessageNasGetCellLocationInfoOutputInterfrequencyLteInfoFrequencyElementCellElement, j); lte_info = MM_CELL_INFO_LTE (mm_cell_info_lte_new_from_dictionary (NULL)); pci = g_strdup_printf ("%X", cell->physical_cell_id); mm_cell_info_lte_set_earfcn (lte_info, arfcn); mm_cell_info_lte_set_physical_ci (lte_info, pci); mm_cell_info_lte_set_rsrp (lte_info, (0.1) * ((gdouble)cell->rsrp)); mm_cell_info_lte_set_rsrq (lte_info, (0.1) * ((gdouble)cell->rsrq)); list = g_list_append (list, g_steal_pointer (<e_info)); } } } if (qmi_message_nas_get_cell_location_info_output_get_nr5g_cell_information (output, &operator, &nr5g_tac, &nr5g_global_ci, &nr5g_pci, &nr5g_rsrq, &nr5g_rsrp, &nr5g_snr, &error)) { MMCellInfoNr5g *nr5g_info; g_autofree gchar *operator_id = NULL; g_autofree gchar *tac = NULL; g_autofree gchar *global_ci = NULL; g_autofree gchar *pci = NULL; operator_id = str_from_bcd_plmn (operator); g_assert (nr5g_tac->len == 3); /* Encoded in upper-case hexadecimal format without leading zeros, as specified in 3GPP TS 27.007. */ tac = g_strdup_printf ("%X", ((((g_array_index (nr5g_tac, guint8, 0) << 8) | g_array_index (nr5g_tac, guint8, 1)) << 8) | g_array_index (nr5g_tac, guint8, 2))); global_ci = g_strdup_printf ("%" G_GINT64_MODIFIER "X", nr5g_global_ci); pci = g_strdup_printf ("%X", nr5g_pci); nr5g_info = MM_CELL_INFO_NR5G (mm_cell_info_nr5g_new_from_dictionary (NULL)); mm_cell_info_set_serving (MM_CELL_INFO (nr5g_info), TRUE); mm_cell_info_nr5g_set_operator_id (nr5g_info, operator_id); mm_cell_info_nr5g_set_tac (nr5g_info, tac); mm_cell_info_nr5g_set_ci (nr5g_info, global_ci); mm_cell_info_nr5g_set_physical_ci (nr5g_info, pci); mm_cell_info_nr5g_set_rsrq (nr5g_info, (0.1) * ((gdouble)nr5g_rsrq)); mm_cell_info_nr5g_set_rsrp (nr5g_info, (0.1) * ((gdouble)nr5g_rsrp)); mm_cell_info_nr5g_set_sinr (nr5g_info, (0.1) * ((gdouble)nr5g_snr)); if (qmi_message_nas_get_cell_location_info_output_get_nr5g_arfcn (output, &nr5g_arfcn, &error)) { mm_cell_info_nr5g_set_nrarfcn (nr5g_info, nr5g_arfcn); } list = g_list_append (list, g_steal_pointer (&nr5g_info)); } g_task_return_pointer (task, list, (GDestroyNotify)cell_info_list_free); g_object_unref (task); qmi_message_nas_get_cell_location_info_output_unref (output); } static void get_cell_info (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data) { QmiClient *client = NULL; GTask *task; if (!mm_shared_qmi_ensure_client (MM_SHARED_QMI (self), QMI_SERVICE_NAS, &client, callback, user_data)) return; task = g_task_new (self, NULL, callback, user_data); mm_obj_dbg (self, "getting cell info..."); qmi_client_nas_get_cell_location_info (QMI_CLIENT_NAS (client), NULL, 10, NULL, (GAsyncReadyCallback)get_cell_info_ready, task); } /*****************************************************************************/ /* Powering up/down/off the modem (Modem interface) */ typedef enum { SET_OPERATING_MODE_STEP_FIRST, SET_OPERATING_MODE_STEP_INDICATION_REGISTER, SET_OPERATING_MODE_STEP_SETUP_WAIT_INDICATION, SET_OPERATING_MODE_STEP_SEND_REQUEST, SET_OPERATING_MODE_STEP_WAIT_FOR_INDICATION, SET_OPERATING_MODE_STEP_DONE, SET_OPERATING_MODE_STEP_CLEANUP_WAIT_INDICATION, SET_OPERATING_MODE_STEP_INDICATION_UNREGISTER, SET_OPERATING_MODE_STEP_RELOAD, SET_OPERATING_MODE_STEP_LAST, } SetOperatingModeStep; typedef struct { SetOperatingModeStep step; QmiDmsOperatingMode mode; QmiClientDms *client; gboolean event_report_set; guint indication_id; guint timeout_id; gboolean indication_received; gboolean response_received; gboolean reload; GError *saved_error; } SetOperatingModeContext; static void set_operating_mode_context_free (SetOperatingModeContext *ctx) { g_assert (!ctx->saved_error); g_assert (ctx->indication_id == 0); g_assert (ctx->timeout_id == 0); g_clear_object (&ctx->client); g_slice_free (SetOperatingModeContext, ctx); } static gboolean modem_power_up_down_off_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void set_operating_mode_context_step (MMBroadbandModemQmi *self); static void dms_reload_current_operating_mode_ready (QmiClientDms *client, GAsyncResult *res, MMBroadbandModemQmi *_self) /* full reference */ { g_autoptr(MMBroadbandModemQmi) self = _self; g_autoptr(QmiMessageDmsGetOperatingModeOutput) output = NULL; g_autoptr(GError) error = NULL; SetOperatingModeContext *ctx; QmiDmsOperatingMode mode = QMI_DMS_OPERATING_MODE_UNKNOWN; g_assert (self->priv->set_operating_mode_task); ctx = g_task_get_task_data (self->priv->set_operating_mode_task); /* Reloading the current operating mode is only expected if the operation is * failed for some other reason */ g_assert (ctx->saved_error); output = qmi_client_dms_get_operating_mode_finish (client, res, &error); if (!output || !qmi_message_dms_get_operating_mode_output_get_result (output, &error)) { mm_obj_warn (self, "couldn't reload current operating mode: %s", error->message); ctx->step++; set_operating_mode_context_step (self); return; } qmi_message_dms_get_operating_mode_output_get_mode (output, &mode, NULL); if (mode != ctx->mode) { g_prefix_error (&ctx->saved_error, "Requested (%s) and reloaded (%s) modes did not match: ", qmi_dms_operating_mode_get_string (ctx->mode), qmi_dms_operating_mode_get_string (mode)); ctx->step++; set_operating_mode_context_step (self); return; } /* Reloaded mode is the one we requested! we can cleanup the saved error * as the operation did really not fail */ mm_obj_info (self, "power update operation successful even after error"); g_clear_error (&ctx->saved_error); ctx->step++; set_operating_mode_context_step (self); } static void set_operating_mode_reload (MMBroadbandModemQmi *self) { SetOperatingModeContext *ctx; g_assert (self->priv->set_operating_mode_task); ctx = g_task_get_task_data (self->priv->set_operating_mode_task); qmi_client_dms_get_operating_mode (ctx->client, NULL, 5, NULL, (GAsyncReadyCallback)dms_reload_current_operating_mode_ready, g_object_ref (self)); } static void dms_set_event_report_operating_mode_deactivate_ready (QmiClientDms *client, GAsyncResult *res, MMBroadbandModemQmi *_self) /* full reference */ { g_autoptr(MMBroadbandModemQmi) self = _self; g_autoptr(QmiMessageDmsSetEventReportOutput) output = NULL; g_autoptr(GError) error = NULL; SetOperatingModeContext *ctx; g_assert (self->priv->set_operating_mode_task); ctx = g_task_get_task_data (self->priv->set_operating_mode_task); output = qmi_client_dms_set_event_report_finish (client, res, &error); if (!output || !qmi_message_dms_set_event_report_output_get_result (output, &error)) mm_obj_dbg (self, "couldn't deregister for power indications: %s", error->message); /* go on to next step */ ctx->step++; set_operating_mode_context_step (self); } static void set_operating_mode_indication_unregister (MMBroadbandModemQmi *self) { g_autoptr(QmiMessageDmsSetEventReportInput) input = NULL; SetOperatingModeContext *ctx; g_assert (self->priv->set_operating_mode_task); ctx = g_task_get_task_data (self->priv->set_operating_mode_task); input = qmi_message_dms_set_event_report_input_new (); qmi_message_dms_set_event_report_input_set_operating_mode_reporting (input, FALSE, NULL); qmi_client_dms_set_event_report ( ctx->client, input, 5, NULL, (GAsyncReadyCallback)dms_set_event_report_operating_mode_deactivate_ready, g_object_ref (self)); } static gboolean set_operating_mode_timeout_cb (MMBroadbandModemQmi *self) { SetOperatingModeContext *ctx; g_assert (self->priv->set_operating_mode_task); ctx = g_task_get_task_data (self->priv->set_operating_mode_task); /* We may have received an indication but not the response to the request. In this case, * complete with the results already set in the indication. */ if (ctx->indication_received && !ctx->response_received) { mm_obj_dbg (self, "Power update operation timed out, but result was already received"); } else { /* Save a timeout error, but also request to reload, in case the modem * failed to send the indication even if it did update the power state */ ctx->saved_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_TIMEOUT, "Power update operation timed out"); ctx->reload = TRUE; } ctx->step = SET_OPERATING_MODE_STEP_DONE; set_operating_mode_context_step (self); return G_SOURCE_REMOVE; } static void power_event_report_indication_cb (QmiClientDms *client, QmiIndicationDmsEventReportOutput *output, MMBroadbandModemQmi *self) { QmiDmsOperatingMode state; SetOperatingModeContext *ctx; g_assert (self->priv->set_operating_mode_task); ctx = g_task_get_task_data (self->priv->set_operating_mode_task); ctx->indication_received = TRUE; /* Always keep last error only */ g_clear_error (&ctx->saved_error); if (!qmi_indication_dms_event_report_output_get_operating_mode (output, &state, NULL)) { ctx->saved_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Invalid power indication received"); } else if (ctx->mode != state) { ctx->saved_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Requested (%s) and notified (%s) modes did not match", qmi_dms_operating_mode_get_string (ctx->mode), qmi_dms_operating_mode_get_string (state)); } else { mm_obj_dbg (self, "power state successfully updated: '%s'", qmi_dms_operating_mode_get_string (state)); } /* The indication only completes the operation if it is received AFTER the response */ if (ctx->response_received) { ctx->step = SET_OPERATING_MODE_STEP_DONE; set_operating_mode_context_step (self); } } static void dms_set_operating_mode_ready (QmiClientDms *client, GAsyncResult *res, MMBroadbandModemQmi *_self) /* full reference */ { g_autoptr(MMBroadbandModemQmi) self = _self; g_autoptr (QmiMessageDmsSetOperatingModeOutput) output = NULL; g_autoptr(GError) error = NULL; SetOperatingModeContext *ctx; /* We may have completed the operation already as a timeout */ if (!self->priv->set_operating_mode_task) return; ctx = g_task_get_task_data (self->priv->set_operating_mode_task); ctx->response_received = TRUE; output = qmi_client_dms_set_operating_mode_finish (client, res, &error); if (!output || !qmi_message_dms_set_operating_mode_output_get_result (output, &error)) { /* * Some new devices, like the Dell DW5770, will return an internal error when * trying to bring the power mode to online. * * Other devices, like some rebranded EM7455 modules, will return an "invalid * transition" instead when trying to bring the power mode to online. * * We can avoid this by sending the magic "DMS Set FCC Auth" message before * retrying. Notify this to upper layers with the special MM_CORE_ERROR_RETRY * error. */ if ((ctx->mode == QMI_DMS_OPERATING_MODE_ONLINE) && ((g_error_matches (error, QMI_PROTOCOL_ERROR, QMI_PROTOCOL_ERROR_INTERNAL) || g_error_matches (error, QMI_PROTOCOL_ERROR, QMI_PROTOCOL_ERROR_INVALID_TRANSITION)))) { g_clear_error (&error); error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_RETRY, "Invalid transition"); } else g_prefix_error (&error, "Couldn't set operating mode: "); } /* If unsupported, just complete without errors */ if (g_error_matches (error, QMI_CORE_ERROR, QMI_CORE_ERROR_UNSUPPORTED)) { mm_obj_dbg (self, "device doesn't support operating mode setting: ignoring power update"); g_clear_error (&ctx->saved_error); ctx->step = SET_OPERATING_MODE_STEP_DONE; set_operating_mode_context_step (self); return; } /* An error reported right away, prefer it to the one received via indication, if any */ if (error) { g_clear_error (&ctx->saved_error); ctx->saved_error = g_steal_pointer (&error); ctx->step = SET_OPERATING_MODE_STEP_DONE; set_operating_mode_context_step (self); return; } /* Request successful but indication not yet received */ if (ctx->event_report_set && !ctx->indication_received) { ctx->step = SET_OPERATING_MODE_STEP_WAIT_FOR_INDICATION; set_operating_mode_context_step (self); return; } /* Request successful and no indication needed, or indication already received */ mm_obj_dbg (self, "operating mode request finished: no need to wait for indications"); ctx->step = SET_OPERATING_MODE_STEP_DONE; set_operating_mode_context_step (self); } static void set_operating_mode_send_request (MMBroadbandModemQmi *self) { g_autoptr (QmiMessageDmsSetOperatingModeInput) input = NULL; SetOperatingModeContext *ctx; g_assert (self->priv->set_operating_mode_task); ctx = g_task_get_task_data (self->priv->set_operating_mode_task); input = qmi_message_dms_set_operating_mode_input_new (); qmi_message_dms_set_operating_mode_input_set_mode (input, ctx->mode, NULL); qmi_client_dms_set_operating_mode (ctx->client, input, 20, NULL, (GAsyncReadyCallback)dms_set_operating_mode_ready, g_object_ref (self)); } static void dms_set_event_report_operating_mode_activate_ready (QmiClientDms *client, GAsyncResult *res, MMBroadbandModemQmi *_self) /* full reference */ { g_autoptr(MMBroadbandModemQmi) self = _self; g_autoptr(QmiMessageDmsSetEventReportOutput) output = NULL; g_autoptr(GError) error = NULL; SetOperatingModeContext *ctx; g_assert (self->priv->set_operating_mode_task); ctx = g_task_get_task_data (self->priv->set_operating_mode_task); output = qmi_client_dms_set_event_report_finish (client, res, &error); if (!output || !qmi_message_dms_set_event_report_output_get_result (output, &error)) { if (!g_error_matches (error, QMI_PROTOCOL_ERROR, QMI_PROTOCOL_ERROR_MISSING_ARGUMENT)) { /* fatal error */ g_prefix_error (&error, "Couldn't register for power indications: "); ctx->saved_error = g_steal_pointer (&error); ctx->step = SET_OPERATING_MODE_STEP_LAST; set_operating_mode_context_step (self); return; } mm_obj_dbg (self, "device doesn't support power indication registration: ignore it and continue"); } else { mm_obj_dbg (self, "device supports power indications"); ctx->event_report_set = TRUE; } /* go on to next step */ ctx->step++; set_operating_mode_context_step (self); } static void set_operating_mode_indication_register (MMBroadbandModemQmi *self) { g_autoptr(QmiMessageDmsSetEventReportInput) input = NULL; SetOperatingModeContext *ctx; g_assert (self->priv->set_operating_mode_task); ctx = g_task_get_task_data (self->priv->set_operating_mode_task); input = qmi_message_dms_set_event_report_input_new (); qmi_message_dms_set_event_report_input_set_operating_mode_reporting (input, TRUE, NULL); qmi_client_dms_set_event_report ( ctx->client, input, 5, NULL, (GAsyncReadyCallback)dms_set_event_report_operating_mode_activate_ready, g_object_ref (self)); } static void set_operating_mode_context_step (MMBroadbandModemQmi *self) { SetOperatingModeContext *ctx; g_assert (self->priv->set_operating_mode_task); ctx = g_task_get_task_data (self->priv->set_operating_mode_task); switch (ctx->step) { case SET_OPERATING_MODE_STEP_FIRST: ctx->step++; /* fall through */ case SET_OPERATING_MODE_STEP_INDICATION_REGISTER: mm_obj_dbg (self, "operating mode update (%d/%d): indication register", ctx->step, SET_OPERATING_MODE_STEP_LAST); set_operating_mode_indication_register (self); return; case SET_OPERATING_MODE_STEP_SETUP_WAIT_INDICATION: g_assert (ctx->indication_id == 0); g_assert (ctx->timeout_id == 0); if (ctx->event_report_set) { mm_obj_dbg (self, "operating mode update (%d/%d): setup wait indication", ctx->step, SET_OPERATING_MODE_STEP_LAST); ctx->indication_id = g_signal_connect (ctx->client, "event-report", G_CALLBACK (power_event_report_indication_cb), self); ctx->timeout_id = g_timeout_add_seconds (10, (GSourceFunc) set_operating_mode_timeout_cb, self); } else { mm_obj_dbg (self, "operating mode update (%d/%d): setup wait indication not needed", ctx->step, SET_OPERATING_MODE_STEP_LAST); } ctx->step++; /* fall through */ case SET_OPERATING_MODE_STEP_SEND_REQUEST: mm_obj_dbg (self, "operating mode update (%d/%d): send request", ctx->step, SET_OPERATING_MODE_STEP_LAST); set_operating_mode_send_request (self); return; case SET_OPERATING_MODE_STEP_WAIT_FOR_INDICATION: g_assert (ctx->event_report_set); g_assert (ctx->indication_id); g_assert (ctx->timeout_id); mm_obj_dbg (self, "operating mode update (%d/%d): wait indication", ctx->step, SET_OPERATING_MODE_STEP_LAST); return; case SET_OPERATING_MODE_STEP_DONE: if (!ctx->saved_error) { mm_obj_dbg (self, "operating mode update (%d/%d): done", ctx->step, SET_OPERATING_MODE_STEP_LAST); } else { mm_obj_dbg (self, "operating mode update (%d/%d): done with failure: %s", ctx->step, SET_OPERATING_MODE_STEP_LAST, ctx->saved_error->message); } ctx->step++; /* fall through */ case SET_OPERATING_MODE_STEP_CLEANUP_WAIT_INDICATION: if (ctx->timeout_id || ctx->indication_id) { mm_obj_dbg (self, "operating mode update (%d/%d): cleanup wait indication", ctx->step, SET_OPERATING_MODE_STEP_LAST); if (ctx->timeout_id) { g_source_remove (ctx->timeout_id); ctx->timeout_id = 0; } if (ctx->indication_id) { g_signal_handler_disconnect (ctx->client, ctx->indication_id); ctx->indication_id = 0; } } else { mm_obj_dbg (self, "operating mode update (%d/%d): cleanup wait indication not needed", ctx->step, SET_OPERATING_MODE_STEP_LAST); } ctx->step++; /* fall through */ case SET_OPERATING_MODE_STEP_INDICATION_UNREGISTER: if (ctx->event_report_set) { mm_obj_dbg (self, "operating mode update (%d/%d): indication unregister", ctx->step, SET_OPERATING_MODE_STEP_LAST); set_operating_mode_indication_unregister (self); return; } mm_obj_dbg (self, "operating mode update (%d/%d): indication unregister not needed", ctx->step, SET_OPERATING_MODE_STEP_LAST); ctx->step++; /* fall through */ case SET_OPERATING_MODE_STEP_RELOAD: if (ctx->reload) { mm_obj_dbg (self, "operating mode update (%d/%d): reload", ctx->step, SET_OPERATING_MODE_STEP_LAST); set_operating_mode_reload (self); return; } mm_obj_dbg (self, "operating mode update (%d/%d): reload not needed", ctx->step, SET_OPERATING_MODE_STEP_LAST); ctx->step++; /* fall through */ case SET_OPERATING_MODE_STEP_LAST: { GTask *task; task = g_steal_pointer (&self->priv->set_operating_mode_task); if (ctx->saved_error) { mm_obj_dbg (self, "operating mode update (%d/%d): failed: %s", ctx->step, SET_OPERATING_MODE_STEP_LAST, ctx->saved_error->message); g_task_return_error (task, g_steal_pointer (&ctx->saved_error)); } else { mm_obj_dbg (self, "operating mode update (%d/%d): all done", ctx->step, SET_OPERATING_MODE_STEP_LAST); g_task_return_boolean (task, TRUE); } g_object_unref (task); return; } default: g_assert_not_reached (); } } static void common_power_up_down_off (MMIfaceModem *_self, QmiDmsOperatingMode mode, GAsyncReadyCallback callback, gpointer user_data) { MMBroadbandModemQmi *self = MM_BROADBAND_MODEM_QMI (_self); GError *error = NULL; GTask *task; SetOperatingModeContext *ctx; QmiClient *client; task = g_task_new (self, NULL, callback, user_data); if (self->priv->set_operating_mode_task) { error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_IN_PROGRESS, "Another operation in progress"); g_task_return_error (task, error); g_object_unref (task); return; } client = mm_shared_qmi_peek_client (MM_SHARED_QMI (self), QMI_SERVICE_DMS, MM_PORT_QMI_FLAG_DEFAULT, &error); if (!client) { g_task_return_error (task, error); g_object_unref (task); return; } ctx = g_slice_new0 (SetOperatingModeContext); ctx->step = SET_OPERATING_MODE_STEP_FIRST; ctx->mode = mode; ctx->client = QMI_CLIENT_DMS (g_object_ref (client)); g_task_set_task_data (task, ctx, (GDestroyNotify)set_operating_mode_context_free); /* Store the task in the private info, and start the state machine sequence */ self->priv->set_operating_mode_task = task; set_operating_mode_context_step (self); } static void modem_power_off (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data) { common_power_up_down_off (self, QMI_DMS_OPERATING_MODE_OFFLINE, callback, user_data); } static void modem_power_down (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data) { common_power_up_down_off (self, QMI_DMS_OPERATING_MODE_LOW_POWER, callback, user_data); } static void modem_power_up (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data) { common_power_up_down_off (self, QMI_DMS_OPERATING_MODE_ONLINE, callback, user_data); } /*****************************************************************************/ /* Power state loading (Modem interface) */ static MMModemPowerState load_power_state_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { GError *inner_error = NULL; gssize value; value = g_task_propagate_int (G_TASK (res), &inner_error); if (inner_error) { g_propagate_error (error, inner_error); return MM_MODEM_POWER_STATE_UNKNOWN; } return (MMModemPowerState)value; } static void dms_get_operating_mode_ready (QmiClientDms *client, GAsyncResult *res, GTask *task) { QmiMessageDmsGetOperatingModeOutput *output = NULL; GError *error = NULL; output = qmi_client_dms_get_operating_mode_finish (client, res, &error); if (!output) { g_prefix_error (&error, "QMI operation failed: "); g_task_return_error (task, error); } else if (!qmi_message_dms_get_operating_mode_output_get_result (output, &error)) { g_prefix_error (&error, "Couldn't get operating mode: "); g_task_return_error (task, error); } else { QmiDmsOperatingMode mode = QMI_DMS_OPERATING_MODE_UNKNOWN; qmi_message_dms_get_operating_mode_output_get_mode (output, &mode, NULL); switch (mode) { case QMI_DMS_OPERATING_MODE_ONLINE: g_task_return_int (task, MM_MODEM_POWER_STATE_ON); break; case QMI_DMS_OPERATING_MODE_LOW_POWER: case QMI_DMS_OPERATING_MODE_PERSISTENT_LOW_POWER: case QMI_DMS_OPERATING_MODE_MODE_ONLY_LOW_POWER: g_task_return_int (task, MM_MODEM_POWER_STATE_LOW); break; case QMI_DMS_OPERATING_MODE_OFFLINE: g_task_return_int (task, MM_MODEM_POWER_STATE_OFF); break; case QMI_DMS_OPERATING_MODE_SHUTTING_DOWN: case QMI_DMS_OPERATING_MODE_FACTORY_TEST: case QMI_DMS_OPERATING_MODE_RESET: case QMI_DMS_OPERATING_MODE_UNKNOWN: default: g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Unhandled power state: '%s' (%u)", qmi_dms_operating_mode_get_string (mode), mode); break; } } if (output) qmi_message_dms_get_operating_mode_output_unref (output); g_object_unref (task); } static void load_power_state (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data) { QmiClient *client = NULL; if (!mm_shared_qmi_ensure_client (MM_SHARED_QMI (self), QMI_SERVICE_DMS, &client, callback, user_data)) return; mm_obj_dbg (self, "getting device operating mode..."); qmi_client_dms_get_operating_mode (QMI_CLIENT_DMS (client), NULL, 5, NULL, (GAsyncReadyCallback)dms_get_operating_mode_ready, g_task_new (self, NULL, callback, user_data)); } /*****************************************************************************/ /* Create SIM (Modem interface) */ static MMBaseSim * create_sim_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { return mm_sim_qmi_new_finish (res, error); } static void create_sim (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data) { /* New QMI SIM */ mm_sim_qmi_new (MM_BASE_MODEM (self), MM_BROADBAND_MODEM_QMI (self)->priv->dms_uim_deprecated, NULL, /* cancellable */ callback, user_data); } /*****************************************************************************/ /* IMEI loading (3GPP interface) */ static gchar * modem_3gpp_load_imei_finish (MMIfaceModem3gpp *self, GAsyncResult *res, GError **error) { return g_task_propagate_pointer (G_TASK (res), error); } static void modem_3gpp_load_imei (MMIfaceModem3gpp *_self, GAsyncReadyCallback callback, gpointer user_data) { MMBroadbandModemQmi *self = MM_BROADBAND_MODEM_QMI (_self); GTask *task; task = g_task_new (self, NULL, callback, user_data); if (self->priv->imei) g_task_return_pointer (task, g_strdup (self->priv->imei), g_free); else g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Device doesn't report a valid IMEI"); g_object_unref (task); } /*****************************************************************************/ /* Facility locks status loading (3GPP interface) */ typedef struct { QmiClient *client; guint current; MMModem3gppFacility facilities; MMModem3gppFacility locks; } LoadEnabledFacilityLocksContext; static void get_next_facility_lock_status_via_dms (GTask *task); static void load_enabled_facility_locks_context_free (LoadEnabledFacilityLocksContext *ctx) { g_object_unref (ctx->client); g_free (ctx); } static MMModem3gppFacility modem_3gpp_load_enabled_facility_locks_finish (MMIfaceModem3gpp *self, GAsyncResult *res, GError **error) { GError *inner_error = NULL; gssize value; value = g_task_propagate_int (G_TASK (res), &inner_error); if (inner_error) { g_propagate_error (error, inner_error); return MM_MODEM_3GPP_FACILITY_NONE; } return (MMModem3gppFacility)value; } static void get_sim_lock_status_via_get_card_status_ready (QmiClientUim *client, GAsyncResult *res, GTask *task) { MMBroadbandModemQmi *self; LoadEnabledFacilityLocksContext *ctx; QmiMessageUimGetCardStatusOutput *output; GError *error = NULL; MMModemLock lock = MM_MODEM_LOCK_UNKNOWN; QmiUimPinState pin1_state; QmiUimPinState pin2_state; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); output = qmi_client_uim_get_card_status_finish (client, res, &error); if (!output || !qmi_message_uim_get_card_status_output_get_result (output, &error)) { g_prefix_error (&error, "QMI operation failed: "); g_task_return_error (task, error); g_object_unref (task); if (output) qmi_message_uim_get_card_status_output_unref (output); return; } if (!mm_qmi_uim_get_card_status_output_parse (self, output, &lock, &pin1_state, NULL, NULL, &pin2_state, NULL, NULL, NULL, &error)) { g_prefix_error (&error, "QMI operation failed: "); g_task_return_error (task, error); } else { ctx->locks &= ~(MM_MODEM_3GPP_FACILITY_SIM); ctx->locks &= ~(MM_MODEM_3GPP_FACILITY_FIXED_DIALING); if (pin1_state == QMI_UIM_PIN_STATE_ENABLED_VERIFIED || pin1_state == QMI_UIM_PIN_STATE_ENABLED_NOT_VERIFIED || pin1_state == QMI_UIM_PIN_STATE_BLOCKED) { ctx->locks |= (MM_MODEM_3GPP_FACILITY_SIM); } if (pin2_state == QMI_UIM_PIN_STATE_ENABLED_VERIFIED || pin2_state == QMI_UIM_PIN_STATE_ENABLED_NOT_VERIFIED || pin2_state == QMI_UIM_PIN_STATE_BLOCKED) { ctx->locks |= (MM_MODEM_3GPP_FACILITY_FIXED_DIALING); } g_task_return_int (task, ctx->locks); } qmi_message_uim_get_card_status_output_unref (output); g_object_unref (task); } static void get_pin_lock_status_via_get_configuration_ready (QmiClientUim *client, GAsyncResult *res, GTask *task) { MMModem3gppFacility lock = MM_MODEM_3GPP_FACILITY_NONE; QmiMessageUimGetConfigurationOutput *output; LoadEnabledFacilityLocksContext *ctx; MMBroadbandModemQmi *self; GError *error = NULL; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); output = qmi_client_uim_get_configuration_finish (client, res, &error); if (!output || !qmi_message_uim_get_configuration_output_get_result (output, &error)) { g_prefix_error (&error, "QMI message Get Configuration failed: "); g_task_return_error (task, error); g_object_unref (task); if (output) qmi_message_uim_get_configuration_output_unref (output); return; } if (!mm_qmi_uim_get_configuration_output_parse (self, output, &lock, &error)) { g_prefix_error (&error, "QMI operation failed: "); g_task_return_error (task, error); g_object_unref (task); qmi_message_uim_get_configuration_output_unref (output); return; } ctx->locks = lock; qmi_message_uim_get_configuration_output_unref (output); mm_obj_dbg (self, "Getting UIM card status to read pin lock state..."); qmi_client_uim_get_card_status (QMI_CLIENT_UIM (ctx->client), NULL, 5, NULL, (GAsyncReadyCallback) get_sim_lock_status_via_get_card_status_ready, task); } static void get_facility_lock_status_via_uim (GTask *task) { QmiMessageUimGetConfigurationInput *input; LoadEnabledFacilityLocksContext *ctx; MMBroadbandModemQmi *self; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); mm_obj_dbg (self, "Getting UIM Get Configuration to read facility lock state..."); input = qmi_message_uim_get_configuration_input_new (); qmi_message_uim_get_configuration_input_set_configuration_mask ( input, QMI_UIM_CONFIGURATION_PERSONALIZATION_STATUS, NULL); qmi_client_uim_get_configuration (QMI_CLIENT_UIM (ctx->client), input, 5, NULL, (GAsyncReadyCallback)get_pin_lock_status_via_get_configuration_ready, task); qmi_message_uim_get_configuration_input_unref (input); } static void get_sim_lock_status_via_pin_status_ready (QmiClientDms *client, GAsyncResult *res, GTask *task) { MMBroadbandModemQmi *self; LoadEnabledFacilityLocksContext *ctx; QmiMessageDmsUimGetPinStatusOutput *output; QmiDmsUimPinStatus current_status; GError *error = NULL; gboolean pin1_enabled; gboolean pin2_enabled; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); output = qmi_client_dms_uim_get_pin_status_finish (client, res, &error); if (!output || !qmi_message_dms_uim_get_pin_status_output_get_result (output, &error)) { g_task_return_error (task, error); g_object_unref (task); if (output) qmi_message_dms_uim_get_pin_status_output_unref (output); return; } if (qmi_message_dms_uim_get_pin_status_output_get_pin1_status ( output, ¤t_status, NULL, /* verify_retries_left */ NULL, /* unblock_retries_left */ &error)) { pin1_enabled = mm_pin_enabled_from_qmi_uim_pin_status (current_status); mm_obj_dbg (self, "PIN1 is reported %s", (pin1_enabled ? "enabled" : "disabled")); } else { qmi_message_dms_uim_get_pin_status_output_unref (output); g_task_return_error (task, error); g_object_unref (task); return; } if (qmi_message_dms_uim_get_pin_status_output_get_pin2_status ( output, ¤t_status, NULL, /* verify_retries_left */ NULL, /* unblock_retries_left */ &error)) { pin2_enabled = mm_pin_enabled_from_qmi_uim_pin_status (current_status); mm_obj_dbg (self, "PIN2 is reported %s", (pin2_enabled ? "enabled" : "disabled")); } else { qmi_message_dms_uim_get_pin_status_output_unref (output); g_task_return_error (task, error); g_object_unref (task); return; } qmi_message_dms_uim_get_pin_status_output_unref (output); if (pin1_enabled) ctx->locks |= (MM_MODEM_3GPP_FACILITY_SIM); else ctx->locks &= ~(MM_MODEM_3GPP_FACILITY_SIM); if (pin2_enabled) ctx->locks |= (MM_MODEM_3GPP_FACILITY_FIXED_DIALING); else ctx->locks &= ~(MM_MODEM_3GPP_FACILITY_FIXED_DIALING); /* No more facilities to query, all done */ g_task_return_int (task, ctx->locks); g_object_unref (task); } /* the SIM lock cannot be queried with the qmi_get_ck_status function, * therefore using the PIN status */ static void get_sim_lock_status_via_pin_status (GTask *task) { MMBroadbandModemQmi *self; LoadEnabledFacilityLocksContext *ctx; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); mm_obj_dbg (self, "retrieving PIN status to check for enabled PIN"); /* if the SIM is locked or not can only be queried by locking at * the PIN status */ qmi_client_dms_uim_get_pin_status (QMI_CLIENT_DMS (ctx->client), NULL, 5, NULL, (GAsyncReadyCallback)get_sim_lock_status_via_pin_status_ready, task); } static void dms_uim_get_ck_status_ready (QmiClientDms *client, GAsyncResult *res, GTask *task) { MMBroadbandModemQmi *self; LoadEnabledFacilityLocksContext *ctx; gchar *facility_str; QmiMessageDmsUimGetCkStatusOutput *output; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); facility_str = mm_modem_3gpp_facility_build_string_from_mask (1 << ctx->current); output = qmi_client_dms_uim_get_ck_status_finish (client, res, NULL); if (!output || !qmi_message_dms_uim_get_ck_status_output_get_result (output, NULL)) { /* On errors, we'll just assume disabled */ mm_obj_dbg (self, "couldn't query facility '%s' status, assuming disabled", facility_str); ctx->locks &= ~(1 << ctx->current); } else { QmiDmsUimFacilityState state; guint8 verify_retries_left; guint8 unblock_retries_left; qmi_message_dms_uim_get_ck_status_output_get_ck_status ( output, &state, &verify_retries_left, &unblock_retries_left, NULL); mm_obj_dbg (self, "facility '%s' is: '%s'", facility_str, qmi_dms_uim_facility_state_get_string (state)); if (state == QMI_DMS_UIM_FACILITY_STATE_ACTIVATED || state == QMI_DMS_UIM_FACILITY_STATE_BLOCKED) { ctx->locks |= (1 << ctx->current); } } if (output) qmi_message_dms_uim_get_ck_status_output_unref (output); g_free (facility_str); /* And go on with the next one */ ctx->current++; get_next_facility_lock_status_via_dms (task); } static void get_next_facility_lock_status_via_dms (GTask *task) { LoadEnabledFacilityLocksContext *ctx; guint i; ctx = g_task_get_task_data (task); for (i = ctx->current; i < sizeof (MMModem3gppFacility) * 8; i++) { guint32 facility = 1 << i; /* Found the next one to query! */ if (ctx->facilities & facility) { QmiMessageDmsUimGetCkStatusInput *input; /* Keep the current one */ ctx->current = i; /* Query current */ input = qmi_message_dms_uim_get_ck_status_input_new (); qmi_message_dms_uim_get_ck_status_input_set_facility ( input, mm_3gpp_facility_to_qmi_uim_facility (facility), NULL); qmi_client_dms_uim_get_ck_status (QMI_CLIENT_DMS (ctx->client), input, 5, NULL, (GAsyncReadyCallback)dms_uim_get_ck_status_ready, task); qmi_message_dms_uim_get_ck_status_input_unref (input); return; } } get_sim_lock_status_via_pin_status (task); } static void modem_3gpp_load_enabled_facility_locks (MMIfaceModem3gpp *self, GAsyncReadyCallback callback, gpointer user_data) { LoadEnabledFacilityLocksContext *ctx; GTask *task; QmiClient *client = NULL; MMPort *port; if (!MM_BROADBAND_MODEM_QMI (self)->priv->dms_uim_deprecated) { if (!mm_shared_qmi_ensure_client (MM_SHARED_QMI (self), QMI_SERVICE_DMS, &client, callback, user_data)) return; } else { if (!mm_shared_qmi_ensure_client (MM_SHARED_QMI (self), QMI_SERVICE_UIM, &client, callback, user_data)) return; } ctx = g_new (LoadEnabledFacilityLocksContext, 1); ctx->client = g_object_ref (client); /* Set initial list of facilities to query */ ctx->facilities = (MM_MODEM_3GPP_FACILITY_PH_SIM | MM_MODEM_3GPP_FACILITY_NET_PERS | MM_MODEM_3GPP_FACILITY_NET_SUB_PERS | MM_MODEM_3GPP_FACILITY_PROVIDER_PERS | MM_MODEM_3GPP_FACILITY_CORP_PERS); ctx->locks = MM_MODEM_3GPP_FACILITY_NONE; ctx->current = 0; task = g_task_new (self, NULL, callback, user_data); g_task_set_task_data (task, ctx, (GDestroyNotify)load_enabled_facility_locks_context_free); /* If tagged by udev, perform a reduced facility lock query via DMS * by skipping get_ck_status and process get_pin_status only */ port = MM_PORT (mm_broadband_modem_qmi_peek_port_qmi (MM_BROADBAND_MODEM_QMI (self))); if (mm_kernel_device_get_global_property_as_boolean (mm_port_peek_kernel_device (port), "ID_MM_QMI_FACILITY_LOCK_QUERY_REDUCED")) { mm_obj_dbg (self, "performing reduced facility lock query (DMS)"); get_sim_lock_status_via_pin_status (task); return; } /* Regular facility lock query * DMS uses get_ck_status and get_pin_status to probe facilities * UIM uses get_configuration and get_card_status */ if (!MM_BROADBAND_MODEM_QMI (self)->priv->dms_uim_deprecated) get_next_facility_lock_status_via_dms (task); else get_facility_lock_status_via_uim (task); } /*****************************************************************************/ /* Facility locks disabling (3GPP interface) */ # define DISABLE_FACILITY_LOCK_CHECK_TIMEOUT_MS 100 # define DISABLE_FACILITY_LOCK_CHECK_ATTEMPTS 10 typedef struct _DisableFacilityLockContext DisableFacilityLockContext; struct _DisableFacilityLockContext { MMModem3gppFacility facility; guint remaining_attempts; guint8 slot; }; static gboolean disable_facility_lock_check (GTask *task); static gboolean modem_3gpp_disable_facility_lock_finish (MMIfaceModem3gpp *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void disable_facility_lock_check_ready (MMIfaceModem3gpp *self, GAsyncResult *res, GTask *task) { DisableFacilityLockContext *ctx; MMModem3gppFacility facilities; GError *error = NULL; ctx = g_task_get_task_data (task); facilities = modem_3gpp_load_enabled_facility_locks_finish (self, res, &error); if (error) { g_prefix_error (&error, "Failed to check the facility locks: "); g_task_return_error (task, error); g_object_unref (task); return; } /* Check if the facility lock is still enabled */ if (facilities & ctx->facility) { /* Wait again and retry */ g_timeout_add (DISABLE_FACILITY_LOCK_CHECK_TIMEOUT_MS, (GSourceFunc)disable_facility_lock_check, task); return; } g_task_return_boolean (task, TRUE); g_object_unref (task); } static gboolean disable_facility_lock_check (GTask *task) { DisableFacilityLockContext *ctx; MMIfaceModem3gpp *self; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); if (ctx->remaining_attempts) { ctx->remaining_attempts--; modem_3gpp_load_enabled_facility_locks (self, (GAsyncReadyCallback)disable_facility_lock_check_ready, task); } else { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Failed to disable the facility lock."); g_object_unref (task); } return G_SOURCE_REMOVE; } static void disable_facility_lock_ready (QmiClientUim *client, GAsyncResult *res, GTask *task) { QmiMessageUimDepersonalizationOutput *output; GError *error = NULL; output = qmi_client_uim_depersonalization_finish (client, res, &error); if (!output || !qmi_message_uim_depersonalization_output_get_result (output, &error)) { g_prefix_error (&error, "QMI message Depersonalization failed: "); g_task_return_error (task, error); g_object_unref (task); } else { /* Wait defined time for lock change to propagate to Card Status */ g_timeout_add (DISABLE_FACILITY_LOCK_CHECK_TIMEOUT_MS, (GSourceFunc)disable_facility_lock_check, task); } if (output) qmi_message_uim_depersonalization_output_unref (output); } static void modem_3gpp_disable_facility_lock (MMIfaceModem3gpp *self, MMModem3gppFacility facility, guint8 slot, const gchar *key, GAsyncReadyCallback callback, gpointer user_data) { QmiUimCardApplicationPersonalizationFeature feature; QmiMessageUimDepersonalizationInput *input; DisableFacilityLockContext *ctx; QmiClient *client = NULL; GTask *task; if (!mm_shared_qmi_ensure_client (MM_SHARED_QMI (self), QMI_SERVICE_UIM, &client, callback, user_data)) return; task = g_task_new (self, NULL, callback, user_data); /* Choose facility to disable */ if (!qmi_personalization_feature_from_mm_modem_3gpp_facility (facility, &feature)) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS, "Not supported type of facility lock."); g_object_unref (task); return; } mm_obj_dbg (self, "Trying to disable %s lock on slot %d using key: %s", qmi_uim_card_application_personalization_feature_get_string (feature), slot, key); input = qmi_message_uim_depersonalization_input_new (); qmi_message_uim_depersonalization_input_set_info (input, feature, QMI_UIM_DEPERSONALIZATION_OPERATION_DEACTIVATE, key, NULL); qmi_message_uim_depersonalization_input_set_slot (input, slot, NULL); ctx = g_new0 (DisableFacilityLockContext, 1); ctx->facility = facility; ctx->slot = slot; ctx->remaining_attempts = DISABLE_FACILITY_LOCK_CHECK_ATTEMPTS; g_task_set_task_data (task, ctx, g_free); qmi_client_uim_depersonalization (QMI_CLIENT_UIM (client), input, 30, NULL, (GAsyncReadyCallback) disable_facility_lock_ready, task); qmi_message_uim_depersonalization_input_unref (input); } /*****************************************************************************/ /* Scan networks (3GPP interface) */ static GList * modem_3gpp_scan_networks_finish (MMIfaceModem3gpp *self, GAsyncResult *res, GError **error) { return g_task_propagate_pointer (G_TASK (res), error); } static MMModem3gppNetworkAvailability network_availability_from_qmi_nas_network_status (QmiNasNetworkStatus qmi) { if (qmi & QMI_NAS_NETWORK_STATUS_CURRENT_SERVING) return MM_MODEM_3GPP_NETWORK_AVAILABILITY_CURRENT; if (qmi & QMI_NAS_NETWORK_STATUS_AVAILABLE) { if (qmi & QMI_NAS_NETWORK_STATUS_FORBIDDEN) return MM_MODEM_3GPP_NETWORK_AVAILABILITY_FORBIDDEN; return MM_MODEM_3GPP_NETWORK_AVAILABILITY_AVAILABLE; } return MM_MODEM_3GPP_NETWORK_AVAILABILITY_UNKNOWN; } static MM3gppNetworkInfo * get_3gpp_network_info (QmiMessageNasNetworkScanOutputNetworkInformationElement *element) { GString *aux; MM3gppNetworkInfo *info; info = g_new (MM3gppNetworkInfo, 1); info->status = network_availability_from_qmi_nas_network_status (element->network_status); aux = g_string_new (""); /* MCC always 3 digits */ g_string_append_printf (aux, "%.3"G_GUINT16_FORMAT, element->mcc); /* Guess about MNC, if < 100 assume it's 2 digits, no PCS info here */ if (element->mnc >= 100) g_string_append_printf (aux, "%.3"G_GUINT16_FORMAT, element->mnc); else g_string_append_printf (aux, "%.2"G_GUINT16_FORMAT, element->mnc); info->operator_code = g_string_free (aux, FALSE); info->operator_short = NULL; info->operator_long = g_strdup (element->description); info->access_tech = MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN; return info; } static MMModemAccessTechnology get_3gpp_access_technology (GArray *array, gboolean *array_used_flags, guint16 mcc, guint16 mnc) { guint i; for (i = 0; i < array->len; i++) { QmiMessageNasNetworkScanOutputRadioAccessTechnologyElement *element; if (array_used_flags[i]) continue; element = &g_array_index (array, QmiMessageNasNetworkScanOutputRadioAccessTechnologyElement, i); if (element->mcc == mcc && element->mnc == mnc) { array_used_flags[i] = TRUE; return mm_modem_access_technology_from_qmi_radio_interface (element->radio_interface); } } return MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN; } static void nas_network_scan_ready (QmiClientNas *client, GAsyncResult *res, GTask *task) { QmiMessageNasNetworkScanOutput *output = NULL; GError *error = NULL; output = qmi_client_nas_network_scan_finish (client, res, &error); if (!output) { g_prefix_error (&error, "QMI operation failed: "); g_task_return_error (task, error); } else if (!qmi_message_nas_network_scan_output_get_result (output, &error)) { g_prefix_error (&error, "Couldn't scan networks: "); g_task_return_error (task, error); } else { GList *scan_result = NULL; GArray *info_array = NULL; if (qmi_message_nas_network_scan_output_get_network_information (output, &info_array, NULL)) { GArray *rat_array = NULL; gboolean *rat_array_used_flags = NULL; guint i; /* Get optional RAT array */ qmi_message_nas_network_scan_output_get_radio_access_technology (output, &rat_array, NULL); if (rat_array) rat_array_used_flags = g_new0 (gboolean, rat_array->len); for (i = 0; i < info_array->len; i++) { QmiMessageNasNetworkScanOutputNetworkInformationElement *info_element; MM3gppNetworkInfo *info; info_element = &g_array_index (info_array, QmiMessageNasNetworkScanOutputNetworkInformationElement, i); info = get_3gpp_network_info (info_element); if (rat_array) info->access_tech = get_3gpp_access_technology (rat_array, rat_array_used_flags, info_element->mcc, info_element->mnc); scan_result = g_list_append (scan_result, info); } g_free (rat_array_used_flags); } g_task_return_pointer (task, scan_result, (GDestroyNotify)mm_3gpp_network_info_list_free); } if (output) qmi_message_nas_network_scan_output_unref (output); g_object_unref (task); } static void modem_3gpp_scan_networks (MMIfaceModem3gpp *self, GAsyncReadyCallback callback, gpointer user_data) { QmiClient *client = NULL; /* We will pass the GList in the GSimpleAsyncResult, so we must * ensure that there is a callback so that we get it properly * passed to the caller and deallocated afterwards */ g_assert (callback != NULL); if (!mm_shared_qmi_ensure_client (MM_SHARED_QMI (self), QMI_SERVICE_NAS, &client, callback, user_data)) return; mm_obj_dbg (self, "scanning networks..."); qmi_client_nas_network_scan (QMI_CLIENT_NAS (client), NULL, 300, NULL, (GAsyncReadyCallback)nas_network_scan_ready, g_task_new (self, NULL, callback, user_data)); } /*****************************************************************************/ /* Load operator name (3GPP interface) */ static gchar * modem_3gpp_load_operator_name_finish (MMIfaceModem3gpp *self, GAsyncResult *res, GError **error) { return g_task_propagate_pointer (G_TASK (res), error); } static void get_plmn_name_ready (QmiClientNas *client, GAsyncResult *res, GTask *task) { MMBroadbandModemQmi *self; GError *error = NULL; QmiNasNetworkDescriptionEncoding plmn_name_service_provider_name_encoding; QmiNasNetworkDescriptionEncoding plmn_name_short_name_encoding; QmiNasNetworkDescriptionEncoding plmn_name_long_name_encoding; GArray *plmn_name_service_provider_name; GArray *plmn_name_short_name; GArray *plmn_name_long_name; g_autoptr(QmiMessageNasGetPlmnNameOutput) output = NULL; self = g_task_get_source_object (task); output = qmi_client_nas_get_plmn_name_finish (client, res, &error); if (!output) { g_prefix_error (&error, "QMI operation failed: "); g_task_return_error (task, error); g_object_unref (task); return; } if (!qmi_message_nas_get_plmn_name_output_get_result (output, &error)) { g_task_return_error (task, error); g_object_unref (task); return; } if (qmi_message_nas_get_plmn_name_output_get_3gpp_eons_plmn_name ( output, &plmn_name_service_provider_name_encoding, &plmn_name_service_provider_name, &plmn_name_short_name_encoding, NULL, NULL, &plmn_name_short_name, &plmn_name_long_name_encoding, NULL, NULL, &plmn_name_long_name, NULL)) { g_autofree gchar *long_name = NULL; g_autofree gchar *short_name = NULL; g_autofree gchar *service_name = NULL; long_name = qmi_nas_read_string_from_network_description_encoded_array (plmn_name_long_name_encoding, plmn_name_long_name); short_name = qmi_nas_read_string_from_network_description_encoded_array (plmn_name_short_name_encoding, plmn_name_short_name); service_name = qmi_nas_read_string_from_network_description_encoded_array (plmn_name_service_provider_name_encoding, plmn_name_service_provider_name); mm_obj_dbg (self, "current operator long name: %s", long_name); mm_obj_dbg (self, "current operator short name: %s", short_name); mm_obj_dbg (self, "current operator service name: %s", service_name); if (!self->priv->current_operator_description) { if (long_name) { mm_obj_dbg (self, "setting operator description to long_name: %s", long_name); self->priv->current_operator_description = g_steal_pointer (&long_name); } else if (short_name) { mm_obj_dbg (self, "setting operator description to short name: %s", short_name); self->priv->current_operator_description = g_steal_pointer (&short_name); } else if (service_name) { mm_obj_dbg (self, "setting operator description to service name: %s", service_name); self->priv->current_operator_description = g_steal_pointer (&service_name); } else { mm_obj_dbg (self, "unable to set operator description"); } } } if (!self->priv->current_operator_description) g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Current operator description is still unknown and cannot be retrieved from MCC/MNC"); else g_task_return_pointer (task, g_strdup (self->priv->current_operator_description), g_free); g_object_unref (task); } static void modem_3gpp_load_operator_name (MMIfaceModem3gpp *_self, GAsyncReadyCallback callback, gpointer user_data) { MMBroadbandModemQmi *self = MM_BROADBAND_MODEM_QMI (_self); GTask *task; QmiClient *client; guint16 mcc = 0; guint16 mnc = 0; gboolean mnc_pcs_digit = FALSE; g_autoptr(GError) error = NULL; g_autoptr(QmiMessageNasGetPlmnNameInput) input = NULL; task = g_task_new (self, NULL, callback, user_data); if (self->priv->current_operator_description) { g_task_return_pointer (task, g_strdup (self->priv->current_operator_description), g_free); g_object_unref (task); return; } /* Check if operator id is set */ if (!self->priv->current_operator_id) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Current operator id is still unknown"); g_object_unref (task); return; } /* Parse input MCC/MNC */ if (!mm_3gpp_parse_operator_id (self->priv->current_operator_id, &mcc, &mnc, &mnc_pcs_digit, &error)) { g_task_return_error (task, g_steal_pointer (&error)); g_object_unref (task); return; } /* Try to get PLMN name from MCC/MNC */ client = mm_shared_qmi_peek_client (MM_SHARED_QMI (self), QMI_SERVICE_NAS, MM_PORT_QMI_FLAG_DEFAULT, &error); if (!client) { g_task_return_error (task, g_steal_pointer (&error)); g_object_unref (task); return; } input = qmi_message_nas_get_plmn_name_input_new (); qmi_message_nas_get_plmn_name_input_set_plmn (input, mcc, mnc, NULL); if (mnc_pcs_digit && mnc < 100) qmi_message_nas_get_plmn_name_input_set_mnc_pcs_digit_include_status (input, mnc_pcs_digit, NULL); qmi_client_nas_get_plmn_name (QMI_CLIENT_NAS (client), input, 5, NULL, (GAsyncReadyCallback)get_plmn_name_ready, task); } /*****************************************************************************/ /* Load operator code (3GPP interface) */ static gchar * modem_3gpp_load_operator_code_finish (MMIfaceModem3gpp *self, GAsyncResult *res, GError **error) { return g_task_propagate_pointer (G_TASK (res), error); } static void modem_3gpp_load_operator_code (MMIfaceModem3gpp *_self, GAsyncReadyCallback callback, gpointer user_data) { MMBroadbandModemQmi *self = MM_BROADBAND_MODEM_QMI (_self); GTask *task; task = g_task_new (self, NULL, callback, user_data); if (self->priv->current_operator_id) g_task_return_pointer (task, g_strdup (self->priv->current_operator_id), g_free); else g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Current operator MCC/MNC is still unknown"); g_object_unref (task); } /*****************************************************************************/ /* Registration checks (3GPP interface) */ static gboolean modem_3gpp_run_registration_checks_finish (MMIfaceModem3gpp *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void common_process_serving_system_3gpp (MMBroadbandModemQmi *self, QmiMessageNasGetServingSystemOutput *response_output, QmiIndicationNasServingSystemOutput *indication_output) { QmiNasRegistrationState registration_state; QmiNasAttachState cs_attach_state; QmiNasAttachState ps_attach_state; QmiNasNetworkType selected_network; GArray *radio_interfaces; GArray *data_service_capabilities; QmiNasRoamingIndicatorStatus roaming; guint16 mcc; guint16 mnc; const gchar *description; gboolean has_pcs_digit; guint16 lac; guint16 tac; guint32 cid; MMModemAccessTechnology mm_access_technologies; MMModem3gppRegistrationState mm_cs_registration_state; MMModem3gppRegistrationState mm_ps_registration_state; gboolean operator_updated = FALSE; if (response_output) qmi_message_nas_get_serving_system_output_get_serving_system ( response_output, ®istration_state, &cs_attach_state, &ps_attach_state, &selected_network, &radio_interfaces, NULL); else qmi_indication_nas_serving_system_output_get_serving_system ( indication_output, ®istration_state, &cs_attach_state, &ps_attach_state, &selected_network, &radio_interfaces, NULL); /* Build access technologies mask */ data_service_capabilities = NULL; if (response_output) qmi_message_nas_get_serving_system_output_get_data_service_capability (response_output, &data_service_capabilities, NULL); else qmi_indication_nas_serving_system_output_get_data_service_capability (indication_output, &data_service_capabilities, NULL); if (data_service_capabilities && data_service_capabilities->len > 0) mm_access_technologies = mm_modem_access_technologies_from_qmi_data_capability_array (data_service_capabilities); else mm_access_technologies = mm_modem_access_technologies_from_qmi_radio_interface_array (radio_interfaces); /* Only process 3GPP info. * Seen the case already where 'selected_network' gives UNKNOWN but we still * have valid LTE/5GNR info around. */ if (selected_network == QMI_NAS_NETWORK_TYPE_3GPP || (selected_network == QMI_NAS_NETWORK_TYPE_UNKNOWN && (mm_access_technologies & MM_IFACE_MODEM_3GPP_ALL_ACCESS_TECHNOLOGIES_MASK))) { mm_obj_dbg (self, "processing 3GPP info..."); } else { MMModem3gppRegistrationState reg_state_3gpp; mm_obj_dbg (self, "no 3GPP info given..."); if (self->priv->current_operator_id || self->priv->current_operator_description) operator_updated = TRUE; g_free (self->priv->current_operator_id); self->priv->current_operator_id = NULL; g_free (self->priv->current_operator_description); self->priv->current_operator_description = NULL; if (registration_state == QMI_NAS_REGISTRATION_STATE_NOT_REGISTERED_SEARCHING) reg_state_3gpp = MM_MODEM_3GPP_REGISTRATION_STATE_SEARCHING; else reg_state_3gpp = MM_MODEM_3GPP_REGISTRATION_STATE_UNKNOWN; mm_iface_modem_3gpp_update_cs_registration_state (MM_IFACE_MODEM_3GPP (self), reg_state_3gpp, TRUE); mm_iface_modem_3gpp_update_ps_registration_state (MM_IFACE_MODEM_3GPP (self), reg_state_3gpp, TRUE); if (mm_iface_modem_is_3gpp_lte (MM_IFACE_MODEM (self))) mm_iface_modem_3gpp_update_eps_registration_state (MM_IFACE_MODEM_3GPP (self), reg_state_3gpp, TRUE); if (mm_iface_modem_is_3gpp_5gnr (MM_IFACE_MODEM (self))) mm_iface_modem_3gpp_update_5gs_registration_state (MM_IFACE_MODEM_3GPP (self), reg_state_3gpp, TRUE); mm_iface_modem_3gpp_apply_deferred_registration_state (MM_IFACE_MODEM_3GPP (self)); mm_iface_modem_3gpp_update_access_technologies (MM_IFACE_MODEM_3GPP (self), MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN); mm_iface_modem_3gpp_update_location (MM_IFACE_MODEM_3GPP (self), 0, 0, 0); /* request to reload operator info explicitly, so that the new * operator name and code is propagated to the DBus interface */ if (operator_updated) mm_iface_modem_3gpp_reload_current_registration_info (MM_IFACE_MODEM_3GPP (self), NULL, NULL); return; } /* Get roaming status. * TODO: QMI may report per-access-technology roaming indicators, for when * the modem is connected to more than one network. How to handle those? */ roaming = QMI_NAS_ROAMING_INDICATOR_STATUS_OFF; if (response_output) qmi_message_nas_get_serving_system_output_get_roaming_indicator (response_output, &roaming, NULL); else qmi_indication_nas_serving_system_output_get_roaming_indicator (indication_output, &roaming, NULL); /* Build MM registration states */ mm_cs_registration_state = mm_modem_3gpp_registration_state_from_qmi_registration_state ( cs_attach_state, registration_state, (roaming == QMI_NAS_ROAMING_INDICATOR_STATUS_ON)); mm_ps_registration_state = mm_modem_3gpp_registration_state_from_qmi_registration_state ( ps_attach_state, registration_state, (roaming == QMI_NAS_ROAMING_INDICATOR_STATUS_ON)); /* Get and cache operator ID/name */ if ((response_output && qmi_message_nas_get_serving_system_output_get_current_plmn ( response_output, &mcc, &mnc, &description, NULL)) || (indication_output && qmi_indication_nas_serving_system_output_get_current_plmn ( indication_output, &mcc, &mnc, &description, NULL))) { gchar *new_operator_id; /* When we don't have information about leading PCS digit, guess best */ if (mnc >= 100) new_operator_id = g_strdup_printf ("%.3" G_GUINT16_FORMAT "%.3" G_GUINT16_FORMAT, mcc, mnc); else new_operator_id = g_strdup_printf ("%.3" G_GUINT16_FORMAT "%.2" G_GUINT16_FORMAT, mcc, mnc); if (!self->priv->current_operator_id || !g_str_equal (self->priv->current_operator_id, new_operator_id)) { operator_updated = TRUE; g_free (self->priv->current_operator_id); self->priv->current_operator_id = new_operator_id; } else g_free (new_operator_id); if (!self->priv->current_operator_description || !g_str_equal (self->priv->current_operator_description, description)) { operator_updated = TRUE; g_free (self->priv->current_operator_description); self->priv->current_operator_description = g_strdup (description); } } /* If MNC comes with PCS digit, we must make sure the additional * leading '0' is added */ if (((response_output && qmi_message_nas_get_serving_system_output_get_mnc_pcs_digit_include_status ( response_output, &mcc, &mnc, &has_pcs_digit, NULL)) || (indication_output && qmi_indication_nas_serving_system_output_get_mnc_pcs_digit_include_status ( indication_output, &mcc, &mnc, &has_pcs_digit, NULL))) && has_pcs_digit) { gchar *new_operator_id; new_operator_id = g_strdup_printf ("%.3" G_GUINT16_FORMAT "%.3" G_GUINT16_FORMAT, mcc, mnc); if (!self->priv->current_operator_id || !g_str_equal (self->priv->current_operator_id, new_operator_id)) { operator_updated = TRUE; g_free (self->priv->current_operator_id); self->priv->current_operator_id = new_operator_id; } else g_free (new_operator_id); } /* Report new registration states. The QMI serving system API reports "CS" * and "PS" registration states, and if the device is in LTE, we'll take the "PS" * one as "EPS". But, if the device is not in LTE, we should also set the "EPS" * state as unknown, so that the "PS" one takes precedence when building * the consolidated registration state (otherwise we may be using some old cached * "EPS" state wrongly). */ mm_iface_modem_3gpp_update_cs_registration_state (MM_IFACE_MODEM_3GPP (self), mm_cs_registration_state, TRUE); mm_iface_modem_3gpp_update_ps_registration_state (MM_IFACE_MODEM_3GPP (self), mm_ps_registration_state, TRUE); if (mm_iface_modem_is_3gpp_lte (MM_IFACE_MODEM (self))) mm_iface_modem_3gpp_update_eps_registration_state (MM_IFACE_MODEM_3GPP (self), ((mm_access_technologies & MM_MODEM_ACCESS_TECHNOLOGY_LTE) ? mm_ps_registration_state : MM_MODEM_3GPP_REGISTRATION_STATE_UNKNOWN), TRUE); /* Same thing for "5GS" state */ if (mm_iface_modem_is_3gpp_5gnr (MM_IFACE_MODEM (self))) mm_iface_modem_3gpp_update_5gs_registration_state (MM_IFACE_MODEM_3GPP (self), ((mm_access_technologies & MM_MODEM_ACCESS_TECHNOLOGY_5GNR) ? mm_ps_registration_state : MM_MODEM_3GPP_REGISTRATION_STATE_UNKNOWN), TRUE); mm_iface_modem_3gpp_apply_deferred_registration_state (MM_IFACE_MODEM_3GPP (self)); mm_iface_modem_3gpp_update_access_technologies (MM_IFACE_MODEM_3GPP (self), mm_access_technologies); /* Get 3GPP location LAC/TAC and CI */ lac = 0; tac = 0; cid = 0; if (response_output) { qmi_message_nas_get_serving_system_output_get_lac_3gpp (response_output, &lac, NULL); qmi_message_nas_get_serving_system_output_get_lte_tac (response_output, &tac, NULL); qmi_message_nas_get_serving_system_output_get_cid_3gpp (response_output, &cid, NULL); } else if (indication_output) { qmi_indication_nas_serving_system_output_get_lac_3gpp (indication_output, &lac, NULL); qmi_indication_nas_serving_system_output_get_lte_tac (indication_output, &tac, NULL); qmi_indication_nas_serving_system_output_get_cid_3gpp (indication_output, &cid, NULL); } /* Only update info in the interface if we get something */ if (cid || lac || tac) mm_iface_modem_3gpp_update_location (MM_IFACE_MODEM_3GPP (self), lac, tac, cid); /* request to reload operator info explicitly, so that the new * operator name and code is propagated to the DBus interface */ if (operator_updated) mm_iface_modem_3gpp_reload_current_registration_info (MM_IFACE_MODEM_3GPP (self), NULL, NULL); } static void get_serving_system_3gpp_ready (QmiClientNas *client, GAsyncResult *res, GTask *task) { MMBroadbandModemQmi *self; QmiMessageNasGetServingSystemOutput *output; GError *error = NULL; output = qmi_client_nas_get_serving_system_finish (client, res, &error); if (!output) { g_prefix_error (&error, "QMI operation failed: "); g_task_return_error (task, error); g_object_unref (task); return; } if (!qmi_message_nas_get_serving_system_output_get_result (output, &error)) { g_prefix_error (&error, "Couldn't get serving system: "); g_task_return_error (task, error); g_object_unref (task); qmi_message_nas_get_serving_system_output_unref (output); return; } self = g_task_get_source_object (task); common_process_serving_system_3gpp (self, output, NULL); g_task_return_boolean (task, TRUE); g_object_unref (task); qmi_message_nas_get_serving_system_output_unref (output); } static void common_process_system_info_3gpp (MMBroadbandModemQmi *self, QmiMessageNasGetSystemInfoOutput *response_output, QmiIndicationNasSystemInfoOutput *indication_output) { MMModem3gppRegistrationState registration_state_cs = MM_MODEM_3GPP_REGISTRATION_STATE_UNKNOWN; MMModem3gppRegistrationState registration_state_ps = MM_MODEM_3GPP_REGISTRATION_STATE_UNKNOWN; MMModem3gppRegistrationState registration_state_eps = MM_MODEM_3GPP_REGISTRATION_STATE_UNKNOWN; MMModem3gppRegistrationState registration_state_5gs = MM_MODEM_3GPP_REGISTRATION_STATE_UNKNOWN; MMModemAccessTechnology act = MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN; guint16 lac = 0; guint16 tac = 0; guint32 cid = 0; gchar *operator_id = NULL; mm_modem_registration_state_from_qmi_system_info (response_output, indication_output, ®istration_state_cs, ®istration_state_ps, ®istration_state_eps, ®istration_state_5gs, &lac, &tac, &cid, &operator_id, &act, self); /* Cache current operator ID */ if (operator_id) { g_free (self->priv->current_operator_id); self->priv->current_operator_id = operator_id; } /* Update registration states */ mm_iface_modem_3gpp_update_cs_registration_state (MM_IFACE_MODEM_3GPP (self), registration_state_cs, TRUE); mm_iface_modem_3gpp_update_ps_registration_state (MM_IFACE_MODEM_3GPP (self), registration_state_ps, TRUE); mm_iface_modem_3gpp_update_eps_registration_state (MM_IFACE_MODEM_3GPP (self), registration_state_eps, TRUE); mm_iface_modem_3gpp_update_5gs_registration_state (MM_IFACE_MODEM_3GPP (self), registration_state_5gs, TRUE); mm_iface_modem_3gpp_apply_deferred_registration_state (MM_IFACE_MODEM_3GPP (self)); /* Update act and location info */ mm_iface_modem_3gpp_update_access_technologies (MM_IFACE_MODEM_3GPP (self), act); mm_iface_modem_3gpp_update_location (MM_IFACE_MODEM_3GPP (self), lac, tac, cid); } static gboolean get_3gpp_rat_data_available (QmiDsdRadioAccessTechnology rat) { switch (rat) { case QMI_DSD_RADIO_ACCESS_TECHNOLOGY_3GPP_WCDMA: case QMI_DSD_RADIO_ACCESS_TECHNOLOGY_3GPP_TDSCDMA: case QMI_DSD_RADIO_ACCESS_TECHNOLOGY_3GPP_GERAN: case QMI_DSD_RADIO_ACCESS_TECHNOLOGY_3GPP_LTE: case QMI_DSD_RADIO_ACCESS_TECHNOLOGY_3GPP_5G: return TRUE; case QMI_DSD_RADIO_ACCESS_TECHNOLOGY_3GPP_WLAN: case QMI_DSD_RADIO_ACCESS_TECHNOLOGY_3GPP2_1X: case QMI_DSD_RADIO_ACCESS_TECHNOLOGY_3GPP2_HRPD: case QMI_DSD_RADIO_ACCESS_TECHNOLOGY_3GPP2_EHRPD: case QMI_DSD_RADIO_ACCESS_TECHNOLOGY_3GPP2_WLAN: case QMI_DSD_RADIO_ACCESS_TECHNOLOGY_UNKNOWN: default: return FALSE; } } static void common_process_system_status_3gpp (MMBroadbandModemQmi *self, QmiMessageDsdGetSystemStatusOutput *response_output, QmiIndicationDsdSystemStatusOutput *indication_output) { GArray *available_systems = NULL; gboolean data_rat_available = FALSE; if (response_output) { qmi_message_dsd_get_system_status_output_get_available_systems (response_output, &available_systems, NULL); if (available_systems && available_systems->len) { QmiMessageDsdGetSystemStatusOutputAvailableSystemsSystem *system; system = &g_array_index (available_systems, QmiMessageDsdGetSystemStatusOutputAvailableSystemsSystem, 0); if (system->technology == QMI_DSD_DATA_SYSTEM_NETWORK_TYPE_3GPP) data_rat_available = get_3gpp_rat_data_available (system->rat); } } else { qmi_indication_dsd_system_status_output_get_available_systems (indication_output, &available_systems, NULL); if (available_systems && available_systems->len) { QmiIndicationDsdSystemStatusOutputAvailableSystemsSystem *system; system = &g_array_index (available_systems, QmiIndicationDsdSystemStatusOutputAvailableSystemsSystem, 0); if (system->technology == QMI_DSD_DATA_SYSTEM_NETWORK_TYPE_3GPP) data_rat_available = get_3gpp_rat_data_available (system->rat); } } /* Store DSD data RAT availability and update PS/EPS/5GS states accordingly */ mm_iface_modem_3gpp_update_packet_service_state ( MM_IFACE_MODEM_3GPP (self), data_rat_available ? MM_MODEM_3GPP_PACKET_SERVICE_STATE_ATTACHED : MM_MODEM_3GPP_PACKET_SERVICE_STATE_DETACHED); } static void get_system_status_3gpp_ready (QmiClientDsd *client, GAsyncResult *res, GTask *task) { MMBroadbandModemQmi *self; QmiMessageDsdGetSystemStatusOutput *output; GError *error = NULL; self = g_task_get_source_object (task); output = qmi_client_dsd_get_system_status_finish (client, res, &error); if (!output || !qmi_message_dsd_get_system_status_output_get_result (output, &error)) { /* If this command fails, flag that dsd is not unsupported */ self->priv->dsd_supported = FALSE; g_error_free (error); } if (output) { common_process_system_status_3gpp (self, output, NULL); qmi_message_dsd_get_system_status_output_unref (output); } /* Just ignore errors for now */ g_task_return_boolean (task, TRUE); g_object_unref (task); } static void get_system_info_ready (QmiClientNas *client, GAsyncResult *res, GTask *task) { MMBroadbandModemQmi *self; QmiMessageNasGetSystemInfoOutput *output; GError *error = NULL; QmiClient *client_dsd; self = g_task_get_source_object (task); output = qmi_client_nas_get_system_info_finish (client, res, &error); if (!output) { mm_obj_dbg (self, "couldn't get system info: '%s', falling back to nas get serving system 3gpp", error->message); qmi_client_nas_get_serving_system (QMI_CLIENT_NAS (client), NULL, 10, NULL, (GAsyncReadyCallback)get_serving_system_3gpp_ready, task); g_clear_error (&error); return; } if (!qmi_message_nas_get_system_info_output_get_result (output, &error)) { qmi_message_nas_get_system_info_output_unref (output); if (g_error_matches (error, QMI_PROTOCOL_ERROR, QMI_PROTOCOL_ERROR_INVALID_QMI_COMMAND) || g_error_matches (error, QMI_PROTOCOL_ERROR, QMI_PROTOCOL_ERROR_NOT_SUPPORTED)) { mm_obj_dbg (self, "couldn't get system info: '%s', falling back to nas get serving system 3gpp", error->message); qmi_client_nas_get_serving_system (QMI_CLIENT_NAS (client), NULL, 10, NULL, (GAsyncReadyCallback)get_serving_system_3gpp_ready, task); g_clear_error (&error); return; } g_prefix_error (&error, "Couldn't get system info: "); g_task_return_error (task, error); g_object_unref (task); return; } common_process_system_info_3gpp (self, output, NULL); qmi_message_nas_get_system_info_output_unref (output); if (self->priv->dsd_supported) { client_dsd = mm_shared_qmi_peek_client (MM_SHARED_QMI (self), QMI_SERVICE_DSD, MM_PORT_QMI_FLAG_DEFAULT, &error); if (!client_dsd) { g_task_return_error (task, error); g_object_unref (task); return; } qmi_client_dsd_get_system_status (QMI_CLIENT_DSD (client_dsd), NULL, 10, NULL, (GAsyncReadyCallback)get_system_status_3gpp_ready, task); return; } g_task_return_boolean (task, TRUE); g_object_unref (task); } static void modem_3gpp_run_registration_checks (MMIfaceModem3gpp *self, gboolean is_cs_supported, gboolean is_ps_supported, gboolean is_eps_supported, gboolean is_5gs_supported, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; QmiClient *client = NULL; if (!mm_shared_qmi_ensure_client (MM_SHARED_QMI (self), QMI_SERVICE_NAS, &client, callback, user_data)) return; task = g_task_new (self, NULL, callback, user_data); qmi_client_nas_get_system_info (QMI_CLIENT_NAS (client), NULL, 10, NULL, (GAsyncReadyCallback)get_system_info_ready, task); } /*****************************************************************************/ /* Enable/Disable unsolicited registration events (3GPP interface) */ typedef struct { QmiClientNas *client; gboolean enable; /* TRUE for enabling, FALSE for disabling */ gboolean system_info_checked; } UnsolicitedRegistrationEventsContext; static void unsolicited_registration_events_context_free (UnsolicitedRegistrationEventsContext *ctx) { g_object_unref (ctx->client); g_free (ctx); } static GTask * unsolicited_registration_events_task_new (MMBroadbandModemQmi *self, QmiClient *client, gboolean enable, GAsyncReadyCallback callback, gpointer user_data) { UnsolicitedRegistrationEventsContext *ctx; GTask *task; ctx = g_new0 (UnsolicitedRegistrationEventsContext, 1); ctx->client = QMI_CLIENT_NAS (g_object_ref (client)); ctx->enable = enable; task = g_task_new (self, NULL, callback, user_data); g_task_set_task_data (task, ctx, (GDestroyNotify)unsolicited_registration_events_context_free); return task; } static gboolean modem_3gpp_enable_disable_unsolicited_registration_events_finish (MMIfaceModem3gpp *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void common_enable_disable_unsolicited_registration_events_serving_system (GTask *task); static void common_enable_disable_unsolicited_registration_events_system_status (GTask *task); static void ri_serving_system_or_system_info_ready (QmiClientNas *client, GAsyncResult *res, GTask *task) { MMBroadbandModemQmi *self; UnsolicitedRegistrationEventsContext *ctx; g_autoptr(QmiMessageNasRegisterIndicationsOutput) output = NULL; g_autoptr(GError) error = NULL; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); output = qmi_client_nas_register_indications_finish (client, res, &error); if (!output || !qmi_message_nas_register_indications_output_get_result (output, &error)) { if (!ctx->system_info_checked) { mm_obj_dbg (self, "couldn't register system info indication: '%s', falling-back to serving system", error->message); ctx->system_info_checked = TRUE; common_enable_disable_unsolicited_registration_events_serving_system (task); g_clear_error (&error); return; } if (ctx->enable) mm_obj_dbg (self, "couldn't register serving system indications: '%s', assuming always enabled", error->message); } if (!ctx->system_info_checked) { ctx->system_info_checked = TRUE; /* registered system info indications. now try to register for system status indications */ if (self->priv->dsd_supported) { common_enable_disable_unsolicited_registration_events_system_status (task); return; } } /* Just ignore errors for now */ self->priv->unsolicited_registration_events_enabled = ctx->enable; g_task_return_boolean (task, TRUE); g_object_unref (task); } static void ri_system_status_ready (QmiClientDsd *client, GAsyncResult *res, GTask *task) { MMBroadbandModemQmi *self; UnsolicitedRegistrationEventsContext *ctx; g_autoptr(QmiMessageDsdSystemStatusChangeOutput) output = NULL; g_autoptr(GError) error = NULL; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); output = qmi_client_dsd_system_status_change_finish (client, res, &error); if (!output || !qmi_message_dsd_system_status_change_output_get_result (output, &error)) { /* If this command fails, flag that dsd is not unsupported */ self->priv->dsd_supported = FALSE; if (ctx->enable) mm_obj_dbg (self, "couldn't register system status indications: '%s', assuming always enabled", error->message); g_clear_error (&error); } /* Just ignore errors for now */ self->priv->unsolicited_registration_events_enabled = ctx->enable; g_task_return_boolean (task, TRUE); g_object_unref (task); } static void common_enable_disable_unsolicited_registration_events_serving_system (GTask *task) { UnsolicitedRegistrationEventsContext *ctx; g_autoptr(QmiMessageNasRegisterIndicationsInput) input = NULL; ctx = g_task_get_task_data (task); input = qmi_message_nas_register_indications_input_new (); qmi_message_nas_register_indications_input_set_serving_system_events (input, ctx->enable, NULL); qmi_message_nas_register_indications_input_set_network_reject_information (input, ctx->enable, FALSE, NULL); qmi_client_nas_register_indications ( ctx->client, input, 5, NULL, (GAsyncReadyCallback)ri_serving_system_or_system_info_ready, task); } static void common_enable_disable_unsolicited_registration_events_system_status (GTask *task) { MMBroadbandModemQmi *self; QmiClient *client; UnsolicitedRegistrationEventsContext *ctx; g_autoptr(QmiMessageDsdSystemStatusChangeInput) input = NULL; GError *error = NULL; self = g_task_get_source_object (task); client = mm_shared_qmi_peek_client (MM_SHARED_QMI (self), QMI_SERVICE_DSD, MM_PORT_QMI_FLAG_DEFAULT, &error); if (!client) { g_task_return_error (task, error); g_object_unref (task); return; } ctx = g_task_get_task_data (task); input = qmi_message_dsd_system_status_change_input_new (); qmi_message_dsd_system_status_change_input_set_register_indication (input, ctx->enable, NULL); qmi_client_dsd_system_status_change ( QMI_CLIENT_DSD (client), input, 5, NULL, (GAsyncReadyCallback)ri_system_status_ready, task); } static void common_enable_disable_unsolicited_registration_events_system_info (GTask *task) { UnsolicitedRegistrationEventsContext *ctx; g_autoptr(QmiMessageNasRegisterIndicationsInput) input = NULL; ctx = g_task_get_task_data (task); input = qmi_message_nas_register_indications_input_new (); qmi_message_nas_register_indications_input_set_system_info (input, ctx->enable, NULL); /* When enabling, serving system events are turned-off, since some modems have them * active by default. They will be turned-on again if setting system info events fails */ if (ctx->enable) qmi_message_nas_register_indications_input_set_serving_system_events (input, FALSE, NULL); qmi_message_nas_register_indications_input_set_network_reject_information (input, ctx->enable, FALSE, NULL); qmi_client_nas_register_indications ( ctx->client, input, 5, NULL, (GAsyncReadyCallback)ri_serving_system_or_system_info_ready, task); } static void modem_3gpp_disable_unsolicited_registration_events (MMIfaceModem3gpp *self, gboolean cs_supported, gboolean ps_supported, gboolean eps_supported, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; QmiClient *client = NULL; if (!mm_shared_qmi_ensure_client (MM_SHARED_QMI (self), QMI_SERVICE_NAS, &client, callback, user_data)) return; task = unsolicited_registration_events_task_new (MM_BROADBAND_MODEM_QMI (self), client, FALSE, callback, user_data); common_enable_disable_unsolicited_registration_events_system_info (task); } static void modem_3gpp_enable_unsolicited_registration_events (MMIfaceModem3gpp *self, gboolean cs_supported, gboolean ps_supported, gboolean eps_supported, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; QmiClient *client = NULL; if (!mm_shared_qmi_ensure_client (MM_SHARED_QMI (self), QMI_SERVICE_NAS, &client, callback, user_data)) return; task = unsolicited_registration_events_task_new (MM_BROADBAND_MODEM_QMI (self), client, TRUE, callback, user_data); common_enable_disable_unsolicited_registration_events_system_info (task); } /*****************************************************************************/ /* Registration checks (CDMA interface) */ static gboolean modem_cdma_run_registration_checks_finish (MMIfaceModemCdma *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void common_process_serving_system_cdma (MMBroadbandModemQmi *self, QmiMessageNasGetServingSystemOutput *response_output, QmiIndicationNasServingSystemOutput *indication_output) { QmiNasRegistrationState registration_state; QmiNasNetworkType selected_network; GArray *radio_interfaces; GArray *data_service_capabilities; MMModemAccessTechnology mm_access_technologies; MMModemCdmaRegistrationState mm_cdma1x_registration_state; MMModemCdmaRegistrationState mm_evdo_registration_state; guint16 sid = 0; guint16 nid = 0; guint16 bs_id = 0; gint32 bs_longitude = G_MININT32; gint32 bs_latitude = G_MININT32; if (response_output) qmi_message_nas_get_serving_system_output_get_serving_system ( response_output, ®istration_state, NULL, /* cs_attach_state */ NULL, /* ps_attach_state */ &selected_network, &radio_interfaces, NULL); else qmi_indication_nas_serving_system_output_get_serving_system ( indication_output, ®istration_state, NULL, /* cs_attach_state */ NULL, /* ps_attach_state */ &selected_network, &radio_interfaces, NULL); /* Build access technologies mask */ data_service_capabilities = NULL; if (response_output) qmi_message_nas_get_serving_system_output_get_data_service_capability (response_output, &data_service_capabilities, NULL); else qmi_indication_nas_serving_system_output_get_data_service_capability (indication_output, &data_service_capabilities, NULL); if (data_service_capabilities) mm_access_technologies = mm_modem_access_technologies_from_qmi_data_capability_array (data_service_capabilities); else mm_access_technologies = mm_modem_access_technologies_from_qmi_radio_interface_array (radio_interfaces); /* Only process 3GPP2 info */ if (selected_network == QMI_NAS_NETWORK_TYPE_3GPP2 || (selected_network == QMI_NAS_NETWORK_TYPE_UNKNOWN && (mm_access_technologies & MM_IFACE_MODEM_CDMA_ALL_ACCESS_TECHNOLOGIES_MASK))) { mm_obj_dbg (self, "processing CDMA info..."); } else { mm_obj_dbg (self, "no CDMA info given..."); mm_iface_modem_cdma_update_cdma1x_registration_state (MM_IFACE_MODEM_CDMA (self), MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN, 0, 0); mm_iface_modem_cdma_update_evdo_registration_state (MM_IFACE_MODEM_CDMA (self), MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN); mm_iface_modem_cdma_update_access_technologies (MM_IFACE_MODEM_CDMA (self), MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN); mm_iface_modem_location_cdma_bs_clear (MM_IFACE_MODEM_LOCATION (self)); return; } /* Get SID/NID */ if (response_output) qmi_message_nas_get_serving_system_output_get_cdma_system_id (response_output, &sid, &nid, NULL); else qmi_indication_nas_serving_system_output_get_cdma_system_id (indication_output, &sid, &nid, NULL); /* Get BS location */ if (response_output) qmi_message_nas_get_serving_system_output_get_cdma_base_station_info (response_output, &bs_id, &bs_latitude, &bs_longitude, NULL); else qmi_indication_nas_serving_system_output_get_cdma_base_station_info (indication_output, &bs_id, &bs_latitude, &bs_longitude, NULL); /* Build generic registration states */ if (mm_access_technologies & MM_IFACE_MODEM_CDMA_ALL_CDMA1X_ACCESS_TECHNOLOGIES_MASK) mm_cdma1x_registration_state = mm_modem_cdma_registration_state_from_qmi_registration_state (registration_state); else mm_cdma1x_registration_state = MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN; if (mm_access_technologies & MM_IFACE_MODEM_CDMA_ALL_EVDO_ACCESS_TECHNOLOGIES_MASK) mm_evdo_registration_state = mm_modem_cdma_registration_state_from_qmi_registration_state (registration_state); else mm_evdo_registration_state = MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN; /* Process per-technology roaming flags */ if (response_output) { GArray *array; if (qmi_message_nas_get_serving_system_output_get_roaming_indicator_list (response_output, &array, NULL)) { guint i; for (i = 0; i < array->len; i++) { QmiMessageNasGetServingSystemOutputRoamingIndicatorListElement *element; element = &g_array_index (array, QmiMessageNasGetServingSystemOutputRoamingIndicatorListElement, i); if (element->radio_interface == QMI_NAS_RADIO_INTERFACE_CDMA_1X && mm_cdma1x_registration_state == MM_MODEM_CDMA_REGISTRATION_STATE_REGISTERED) { if (element->roaming_indicator == QMI_NAS_ROAMING_INDICATOR_STATUS_ON) mm_cdma1x_registration_state = MM_MODEM_CDMA_REGISTRATION_STATE_ROAMING; else if (element->roaming_indicator == QMI_NAS_ROAMING_INDICATOR_STATUS_OFF) mm_cdma1x_registration_state = MM_MODEM_CDMA_REGISTRATION_STATE_HOME; } else if (element->radio_interface == QMI_NAS_RADIO_INTERFACE_CDMA_1XEVDO && mm_evdo_registration_state == MM_MODEM_CDMA_REGISTRATION_STATE_REGISTERED) { if (element->roaming_indicator == QMI_NAS_ROAMING_INDICATOR_STATUS_ON) mm_evdo_registration_state = MM_MODEM_CDMA_REGISTRATION_STATE_ROAMING; else if (element->roaming_indicator == QMI_NAS_ROAMING_INDICATOR_STATUS_OFF) mm_evdo_registration_state = MM_MODEM_CDMA_REGISTRATION_STATE_HOME; } } } } else { GArray *array; if (qmi_indication_nas_serving_system_output_get_roaming_indicator_list (indication_output, &array, NULL)) { guint i; for (i = 0; i < array->len; i++) { QmiIndicationNasServingSystemOutputRoamingIndicatorListElement *element; element = &g_array_index (array, QmiIndicationNasServingSystemOutputRoamingIndicatorListElement, i); if (element->radio_interface == QMI_NAS_RADIO_INTERFACE_CDMA_1X && mm_cdma1x_registration_state == MM_MODEM_CDMA_REGISTRATION_STATE_REGISTERED) { if (element->roaming_indicator == QMI_NAS_ROAMING_INDICATOR_STATUS_ON) mm_cdma1x_registration_state = MM_MODEM_CDMA_REGISTRATION_STATE_ROAMING; else if (element->roaming_indicator == QMI_NAS_ROAMING_INDICATOR_STATUS_OFF) mm_cdma1x_registration_state = MM_MODEM_CDMA_REGISTRATION_STATE_HOME; } else if (element->radio_interface == QMI_NAS_RADIO_INTERFACE_CDMA_1XEVDO && mm_evdo_registration_state == MM_MODEM_CDMA_REGISTRATION_STATE_REGISTERED) { if (element->roaming_indicator == QMI_NAS_ROAMING_INDICATOR_STATUS_ON) mm_evdo_registration_state = MM_MODEM_CDMA_REGISTRATION_STATE_ROAMING; else if (element->roaming_indicator == QMI_NAS_ROAMING_INDICATOR_STATUS_OFF) mm_evdo_registration_state = MM_MODEM_CDMA_REGISTRATION_STATE_HOME; } } } } /* Note: don't rely on the 'Detailed Service Status', it's not always given. */ /* Report new registration states */ mm_iface_modem_cdma_update_cdma1x_registration_state (MM_IFACE_MODEM_CDMA (self), mm_cdma1x_registration_state, sid, nid); mm_iface_modem_cdma_update_evdo_registration_state (MM_IFACE_MODEM_CDMA (self), mm_evdo_registration_state); mm_iface_modem_cdma_update_access_technologies (MM_IFACE_MODEM_CDMA (self), mm_access_technologies); /* Longitude and latitude given in units of 0.25 secs * Note that multiplying by 0.25 is like dividing by 4, so 60*60*4=14400 */ #define QMI_LONGITUDE_TO_DEGREES(longitude) \ (longitude != G_MININT32 ? \ (((gdouble)longitude) / 14400.0) : \ MM_LOCATION_LONGITUDE_UNKNOWN) #define QMI_LATITUDE_TO_DEGREES(latitude) \ (latitude != G_MININT32 ? \ (((gdouble)latitude) / 14400.0) : \ MM_LOCATION_LATITUDE_UNKNOWN) mm_iface_modem_location_cdma_bs_update (MM_IFACE_MODEM_LOCATION (self), QMI_LONGITUDE_TO_DEGREES (bs_longitude), QMI_LATITUDE_TO_DEGREES (bs_latitude)); } static void get_serving_system_cdma_ready (QmiClientNas *client, GAsyncResult *res, GTask *task) { MMBroadbandModemQmi *self; QmiMessageNasGetServingSystemOutput *output; GError *error = NULL; output = qmi_client_nas_get_serving_system_finish (client, res, &error); if (!output) { g_prefix_error (&error, "QMI operation failed: "); g_task_return_error (task, error); g_object_unref (task); return; } if (!qmi_message_nas_get_serving_system_output_get_result (output, &error)) { g_prefix_error (&error, "Couldn't get serving system: "); g_task_return_error (task, error); g_object_unref (task); qmi_message_nas_get_serving_system_output_unref (output); return; } self = g_task_get_source_object (task); common_process_serving_system_cdma (self, output, NULL); qmi_message_nas_get_serving_system_output_unref (output); g_task_return_boolean (task, TRUE); g_object_unref (task); } static void modem_cdma_run_registration_checks (MMIfaceModemCdma *self, gboolean cdma1x_supported, gboolean evdo_supported, GAsyncReadyCallback callback, gpointer user_data) { QmiClient *client = NULL; if (!mm_shared_qmi_ensure_client (MM_SHARED_QMI (self), QMI_SERVICE_NAS, &client, callback, user_data)) return; /* TODO: Run Get System Info in NAS >= 1.8 */ qmi_client_nas_get_serving_system (QMI_CLIENT_NAS (client), NULL, 10, NULL, (GAsyncReadyCallback)get_serving_system_cdma_ready, g_task_new (self, NULL, callback, user_data)); } /*****************************************************************************/ /* Load initial activation state (CDMA interface) */ static MMModemCdmaActivationState modem_cdma_load_activation_state_finish (MMIfaceModemCdma *_self, GAsyncResult *res, GError **error) { MMBroadbandModemQmi *self = MM_BROADBAND_MODEM_QMI (_self); GError *inner_error = NULL; gssize value; value = g_task_propagate_int (G_TASK (res), &inner_error); if (inner_error) { g_propagate_error (error, inner_error); return MM_MODEM_CDMA_ACTIVATION_STATE_UNKNOWN; } /* Cache the value and also return it */ self->priv->activation_state = (MMModemCdmaActivationState)value; return self->priv->activation_state; } static void get_activation_state_ready (QmiClientDms *client, GAsyncResult *res, GTask *task) { QmiDmsActivationState state = QMI_DMS_ACTIVATION_STATE_NOT_ACTIVATED; QmiMessageDmsGetActivationStateOutput *output; GError *error = NULL; output = qmi_client_dms_get_activation_state_finish (client, res, &error); if (!output) { g_prefix_error (&error, "QMI operation failed: "); g_task_return_error (task, error); g_object_unref (task); return; } if (!qmi_message_dms_get_activation_state_output_get_result (output, &error)) { g_prefix_error (&error, "Couldn't get activation state: "); g_task_return_error (task, error); g_object_unref (task); qmi_message_dms_get_activation_state_output_unref (output); return; } qmi_message_dms_get_activation_state_output_get_info (output, &state, NULL); qmi_message_dms_get_activation_state_output_unref (output); g_task_return_int (task, mm_modem_cdma_activation_state_from_qmi_activation_state (state)); g_object_unref (task); } static void modem_cdma_load_activation_state (MMIfaceModemCdma *self, GAsyncReadyCallback callback, gpointer user_data) { QmiClient *client = NULL; if (!mm_shared_qmi_ensure_client (MM_SHARED_QMI (self), QMI_SERVICE_DMS, &client, callback, user_data)) return; qmi_client_dms_get_activation_state (QMI_CLIENT_DMS (client), NULL, 10, NULL, (GAsyncReadyCallback)get_activation_state_ready, g_task_new (self, NULL, callback, user_data)); } /*****************************************************************************/ /* Manual and OTA Activation (CDMA interface) */ #define MAX_MDN_CHECK_RETRIES 10 typedef enum { CDMA_ACTIVATION_STEP_FIRST, CDMA_ACTIVATION_STEP_ENABLE_INDICATIONS, CDMA_ACTIVATION_STEP_REQUEST_ACTIVATION, CDMA_ACTIVATION_STEP_WAIT_UNTIL_FINISHED, CDMA_ACTIVATION_STEP_RESET, CDMA_ACTIVATION_STEP_LAST } CdmaActivationStep; typedef struct { MMBroadbandModemQmi *self; QmiClientDms *client; CdmaActivationStep step; /* OTA activation... */ QmiMessageDmsActivateAutomaticInput *input_automatic; /* Manual activation... */ QmiMessageDmsActivateManualInput *input_manual; guint total_segments_size; guint segment_i; guint n_segments; GArray **segments; guint n_mdn_check_retries; } CdmaActivationContext; static void cdma_activation_context_free (CdmaActivationContext *ctx) { /* Cleanup the activation task from the private info */ ctx->self->priv->activation_task = NULL; for (ctx->segment_i = 0; ctx->segment_i < ctx->n_segments; ctx->segment_i++) g_array_unref (ctx->segments[ctx->segment_i]); g_free (ctx->segments); if (ctx->input_automatic) qmi_message_dms_activate_automatic_input_unref (ctx->input_automatic); if (ctx->input_manual) qmi_message_dms_activate_manual_input_unref (ctx->input_manual); g_object_unref (ctx->client); g_object_unref (ctx->self); g_slice_free (CdmaActivationContext, ctx); } static gboolean modem_cdma_activate_finish (MMIfaceModemCdma *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static gboolean modem_cdma_activate_manual_finish (MMIfaceModemCdma *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void cdma_activation_context_step (GTask *task); static void cdma_activation_disable_indications (CdmaActivationContext *ctx) { QmiMessageDmsSetEventReportInput *input; /* Remove the signal handler */ g_assert (ctx->self->priv->activation_event_report_indication_id != 0); g_signal_handler_disconnect (ctx->client, ctx->self->priv->activation_event_report_indication_id); ctx->self->priv->activation_event_report_indication_id = 0; /* Disable the activation state change indications; don't worry about the result */ input = qmi_message_dms_set_event_report_input_new (); qmi_message_dms_set_event_report_input_set_activation_state_reporting (input, FALSE, NULL); qmi_client_dms_set_event_report (ctx->client, input, 5, NULL, NULL, NULL); qmi_message_dms_set_event_report_input_unref (input); } static void activation_reset_ready (MMIfaceModem *self, GAsyncResult *res, GTask *task) { CdmaActivationContext *ctx; GError *error = NULL; if (!mm_shared_qmi_reset_finish (self, res, &error)) { g_task_return_error (task, error); g_object_unref (task); return; } /* And go on to next step */ ctx = g_task_get_task_data (task); ctx->step++; cdma_activation_context_step (task); } static gboolean retry_msisdn_check_cb (GTask *task) { cdma_activation_context_step (task); return G_SOURCE_REMOVE; } static void activate_manual_get_msisdn_ready (QmiClientDms *client, GAsyncResult *res, GTask *task) { MMBroadbandModemQmi *self; CdmaActivationContext *ctx; QmiMessageDmsGetMsisdnOutput *output = NULL; GError *error = NULL; const gchar *current_mdn = NULL; const gchar *expected_mdn = NULL; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); qmi_message_dms_activate_manual_input_get_info (ctx->input_manual, NULL, /* spc */ NULL, /* sid */ &expected_mdn, NULL, /* min */ NULL); output = qmi_client_dms_get_msisdn_finish (client, res, &error); if (output && qmi_message_dms_get_msisdn_output_get_result (output, NULL) && qmi_message_dms_get_msisdn_output_get_msisdn (output, ¤t_mdn, NULL) && g_str_equal (current_mdn, expected_mdn)) { mm_obj_dbg (self, "MDN successfully updated to '%s'", expected_mdn); qmi_message_dms_get_msisdn_output_unref (output); /* And go on to next step */ ctx->step++; cdma_activation_context_step (task); return; } if (output) qmi_message_dms_get_msisdn_output_unref (output); if (ctx->n_mdn_check_retries < MAX_MDN_CHECK_RETRIES) { /* Retry after some time */ mm_obj_dbg (self, "MDN not yet updated, retrying..."); g_timeout_add (1, (GSourceFunc) retry_msisdn_check_cb, task); return; } /* Well, all retries consumed already, return error */ g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "MDN was not correctly set during manual activation"); g_object_unref (task); } static void activation_event_report_indication_cb (QmiClientDms *client, QmiIndicationDmsEventReportOutput *output, MMBroadbandModemQmi *self) { QmiDmsActivationState state; MMModemCdmaActivationState new; GError *error; /* If the indication doesn't have any activation state info, just return */ if (!qmi_indication_dms_event_report_output_get_activation_state (output, &state, NULL)) return; mm_obj_dbg (self, "activation state update: '%s'", qmi_dms_activation_state_get_string (state)); new = mm_modem_cdma_activation_state_from_qmi_activation_state (state); if (self->priv->activation_state != new) mm_obj_msg (self, "activation state changed: '%s'-->'%s'", mm_modem_cdma_activation_state_get_string (self->priv->activation_state), mm_modem_cdma_activation_state_get_string (new)); /* Cache the new value */ self->priv->activation_state = new; /* We consider a not-activated report in the indication as a failure */ error = (new == MM_MODEM_CDMA_ACTIVATION_STATE_NOT_ACTIVATED ? g_error_new (MM_CDMA_ACTIVATION_ERROR, MM_CDMA_ACTIVATION_ERROR_UNKNOWN, "Activation process failed") : NULL); /* Update activation state in the interface */ mm_iface_modem_cdma_update_activation_state (MM_IFACE_MODEM_CDMA (self), new, error); /* Now, if we have a FINAL state, finish the ongoing activation state request */ if (new != MM_MODEM_CDMA_ACTIVATION_STATE_ACTIVATING) { GTask *task; CdmaActivationContext *ctx; g_assert (self->priv->activation_task != NULL); task = self->priv->activation_task; ctx = g_task_get_task_data (task); /* Disable further indications. */ cdma_activation_disable_indications (ctx); /* If there is any error, finish the async method */ if (error) { g_task_return_error (task, error); g_object_unref (task); return; } /* Otherwise, go on to next step */ ctx->step++; cdma_activation_context_step (task); return; } mm_obj_dbg (self, "activation process still ongoing..."); } static void activate_automatic_ready (QmiClientDms *client, GAsyncResult *res, GTask *task) { CdmaActivationContext *ctx; QmiMessageDmsActivateAutomaticOutput *output; GError *error = NULL; ctx = g_task_get_task_data (task); output = qmi_client_dms_activate_automatic_finish (client, res, &error); if (!output) { cdma_activation_disable_indications (ctx); g_prefix_error (&error, "QMI operation failed: "); g_task_return_error (task, error); g_object_unref (task); return; } if (!qmi_message_dms_activate_automatic_output_get_result (output, &error)) { qmi_message_dms_activate_automatic_output_unref (output); cdma_activation_disable_indications (ctx); g_prefix_error (&error, "Couldn't request OTA activation: "); g_task_return_error (task, error); g_object_unref (task); return; } qmi_message_dms_activate_automatic_output_unref (output); /* Keep on */ ctx->step++; cdma_activation_context_step (task); } static void activate_manual_ready (QmiClientDms *client, GAsyncResult *res, GTask *task) { CdmaActivationContext *ctx; QmiMessageDmsActivateManualOutput *output; GError *error = NULL; ctx = g_task_get_task_data (task); output = qmi_client_dms_activate_manual_finish (client, res, &error); if (!output) { g_prefix_error (&error, "QMI operation failed: "); g_task_return_error (task, error); g_object_unref (task); return; } if (!qmi_message_dms_activate_manual_output_get_result (output, &error)) { qmi_message_dms_activate_manual_output_unref (output); g_prefix_error (&error, "Couldn't request manual activation: "); g_task_return_error (task, error); g_object_unref (task); return; } qmi_message_dms_activate_manual_output_unref (output); /* If pending segments to send, re-run same step */ if (ctx->n_segments) { ctx->segment_i++; if (ctx->segment_i < ctx->n_segments) { /* There's a pending segment */ cdma_activation_context_step (task); return; } } /* No more segments to send, go on */ ctx->step++; cdma_activation_context_step (task); } static void ser_activation_state_ready (QmiClientDms *client, GAsyncResult *res, GTask *task) { CdmaActivationContext *ctx; QmiMessageDmsSetEventReportOutput *output; GError *error = NULL; ctx = g_task_get_task_data (task); /* We cannot ignore errors, we NEED the indications to finish the * activation request properly */ output = qmi_client_dms_set_event_report_finish (client, res, &error); if (!output) { g_prefix_error (&error, "QMI operation failed: "); g_task_return_error (task, error); g_object_unref (task); return; } if (!qmi_message_dms_set_event_report_output_get_result (output, &error)) { qmi_message_dms_set_event_report_output_unref (output); g_prefix_error (&error, "Couldn't set event report: "); g_task_return_error (task, error); g_object_unref (task); return; } qmi_message_dms_set_event_report_output_unref (output); /* Setup the indication handler */ g_assert (ctx->self->priv->activation_event_report_indication_id == 0); ctx->self->priv->activation_event_report_indication_id = g_signal_connect (client, "event-report", G_CALLBACK (activation_event_report_indication_cb), ctx->self); /* Keep on */ ctx->step++; cdma_activation_context_step (task); } static void cdma_activation_context_step (GTask *task) { CdmaActivationContext *ctx; ctx = g_task_get_task_data (task); switch (ctx->step) { case CDMA_ACTIVATION_STEP_FIRST: ctx->step++; /* Fall through */ case CDMA_ACTIVATION_STEP_ENABLE_INDICATIONS: /* Indications needed in automatic activation */ if (ctx->input_automatic) { QmiMessageDmsSetEventReportInput *input; mm_obj_msg (ctx->self, "activation step [1/5]: enabling indications"); input = qmi_message_dms_set_event_report_input_new (); qmi_message_dms_set_event_report_input_set_activation_state_reporting (input, TRUE, NULL); qmi_client_dms_set_event_report ( ctx->client, input, 5, NULL, (GAsyncReadyCallback)ser_activation_state_ready, task); qmi_message_dms_set_event_report_input_unref (input); return; } /* Manual activation, no indications needed */ g_assert (ctx->input_manual != NULL); mm_obj_msg (ctx->self, "activation step [1/5]: indications not needed in manual activation"); ctx->step++; /* Fall through */ case CDMA_ACTIVATION_STEP_REQUEST_ACTIVATION: /* Automatic activation */ if (ctx->input_automatic) { mm_obj_msg (ctx->self, "activation step [2/5]: requesting automatic (OTA) activation"); qmi_client_dms_activate_automatic (ctx->client, ctx->input_automatic, 10, NULL, (GAsyncReadyCallback)activate_automatic_ready, task); return; } /* Manual activation */ g_assert (ctx->input_manual != NULL); if (!ctx->segments) mm_obj_msg (ctx->self, "activation step [2/5]: requesting manual activation"); else { mm_obj_msg (ctx->self, "activation step [2/5]: requesting manual activation (PRL segment %u/%u)", (ctx->segment_i + 1), ctx->n_segments); qmi_message_dms_activate_manual_input_set_prl ( ctx->input_manual, (guint16)ctx->total_segments_size, (guint8)ctx->segment_i, ctx->segments[ctx->segment_i], NULL); } qmi_client_dms_activate_manual (ctx->client, ctx->input_manual, 10, NULL, (GAsyncReadyCallback)activate_manual_ready, task); return; case CDMA_ACTIVATION_STEP_WAIT_UNTIL_FINISHED: /* Automatic activation */ if (ctx->input_automatic) { /* State updates via unsolicited messages */ mm_obj_msg (ctx->self, "activation step [3/5]: waiting for activation state updates"); return; } /* Manual activation; needs MSISDN checks */ g_assert (ctx->input_manual != NULL); ctx->n_mdn_check_retries++; mm_obj_msg (ctx->self, "activation step [3/5]: checking MDN update (retry %u)", ctx->n_mdn_check_retries); qmi_client_dms_get_msisdn (ctx->client, NULL, 5, NULL, (GAsyncReadyCallback)activate_manual_get_msisdn_ready, task); return; case CDMA_ACTIVATION_STEP_RESET: mm_obj_msg (ctx->self, "activation step [4/5]: power-cycling..."); mm_shared_qmi_reset (MM_IFACE_MODEM (ctx->self), (GAsyncReadyCallback)activation_reset_ready, task); return; case CDMA_ACTIVATION_STEP_LAST: mm_obj_msg (ctx->self, "activation step [5/5]: finished"); g_task_return_boolean (task, TRUE); g_object_unref (task); return; default: g_assert_not_reached (); } } static void modem_cdma_activate (MMIfaceModemCdma *_self, const gchar *carrier_code, GAsyncReadyCallback callback, gpointer user_data) { MMBroadbandModemQmi *self = MM_BROADBAND_MODEM_QMI (_self); GTask *task; CdmaActivationContext *ctx; QmiClient *client = NULL; if (!mm_shared_qmi_ensure_client (MM_SHARED_QMI (self), QMI_SERVICE_DMS, &client, callback, user_data)) return; task = g_task_new (self, NULL, callback, user_data); /* Fail if we have already an activation ongoing */ if (self->priv->activation_task) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_IN_PROGRESS, "An activation operation is already in progress"); g_object_unref (task); return; } /* Setup context */ ctx = g_slice_new0 (CdmaActivationContext); ctx->self = g_object_ref (self); ctx->client = QMI_CLIENT_DMS (g_object_ref (client)); ctx->step = CDMA_ACTIVATION_STEP_FIRST; /* Build base input bundle for the Automatic activation */ ctx->input_automatic = qmi_message_dms_activate_automatic_input_new (); qmi_message_dms_activate_automatic_input_set_activation_code (ctx->input_automatic, carrier_code, NULL); g_task_set_task_data (task, ctx, (GDestroyNotify)cdma_activation_context_free); /* We keep the activation task in the private data, so that we don't * allow multiple activation requests at the same time. */ self->priv->activation_task = task; cdma_activation_context_step (task); } static void modem_cdma_activate_manual (MMIfaceModemCdma *_self, MMCdmaManualActivationProperties *properties, GAsyncReadyCallback callback, gpointer user_data) { MMBroadbandModemQmi *self = MM_BROADBAND_MODEM_QMI (_self); GTask *task; CdmaActivationContext *ctx; QmiClient *client = NULL; if (!mm_shared_qmi_ensure_client (MM_SHARED_QMI (self), QMI_SERVICE_DMS, &client, callback, user_data)) return; task = g_task_new (self, NULL, callback, user_data); /* Fail if we have already an activation ongoing */ if (self->priv->activation_task) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_IN_PROGRESS, "An activation operation is already in progress"); g_object_unref (task); return; } /* Setup context */ ctx = g_slice_new0 (CdmaActivationContext); ctx->self = g_object_ref (self); ctx->client = QMI_CLIENT_DMS (g_object_ref (client)); g_task_set_task_data (task, ctx, (GDestroyNotify)cdma_activation_context_free); /* We keep the activation task in the private data, so that we don't * allow multiple activation requests at the same time. */ self->priv->activation_task = task; /* Build base input bundle for the Manual activation */ ctx->input_manual = qmi_message_dms_activate_manual_input_new (); qmi_message_dms_activate_manual_input_set_info ( ctx->input_manual, mm_cdma_manual_activation_properties_get_spc (properties), mm_cdma_manual_activation_properties_get_sid (properties), mm_cdma_manual_activation_properties_get_mdn (properties), mm_cdma_manual_activation_properties_get_min (properties), NULL); if (mm_cdma_manual_activation_properties_get_mn_ha_key (properties)) qmi_message_dms_activate_manual_input_set_mn_ha_key ( ctx->input_manual, mm_cdma_manual_activation_properties_get_mn_ha_key (properties), NULL); if (mm_cdma_manual_activation_properties_get_mn_aaa_key (properties)) qmi_message_dms_activate_manual_input_set_mn_aaa_key ( ctx->input_manual, mm_cdma_manual_activation_properties_get_mn_aaa_key (properties), NULL); if (mm_cdma_manual_activation_properties_peek_prl_bytearray (properties)) { GByteArray *full_prl; guint i; guint adding; guint remaining; /* Just assume 512 is the max segment size... * TODO: probably need to read max segment size from the usb descriptor * WARN! Never ever use a MAX_PRL_SEGMENT_SIZE less than 64, or the sequence number * won't fit in a single byte!!! (16384/256=64) */ #define MAX_PRL_SEGMENT_SIZE 512 full_prl = mm_cdma_manual_activation_properties_peek_prl_bytearray (properties); /* NOTE: max PRL size should already be checked when reading from DBus, * so assert if longer */ ctx->total_segments_size = full_prl->len; g_assert (ctx->total_segments_size <= 16384); ctx->n_segments = (guint) (full_prl->len / MAX_PRL_SEGMENT_SIZE); if (full_prl->len % MAX_PRL_SEGMENT_SIZE != 0) ctx->n_segments++; g_assert (ctx->n_segments <= 256); ctx->segments = g_new0 (GArray *, (ctx->n_segments + 1)); adding = 0; remaining = full_prl->len; for (i = 0; i < ctx->n_segments; i++) { guint current_add; g_assert (remaining > 0); current_add = remaining > MAX_PRL_SEGMENT_SIZE ? MAX_PRL_SEGMENT_SIZE : remaining; ctx->segments[i] = g_array_sized_new (FALSE, FALSE, sizeof (guint8), current_add); g_array_append_vals (ctx->segments[i], &(full_prl->data[adding]), current_add); adding += current_add; g_assert (remaining >= current_add); remaining -= current_add; } #undef MAX_PRL_SEGMENT_SIZE } cdma_activation_context_step (task); } /*****************************************************************************/ /* Setup/Cleanup unsolicited registration event handlers * (3GPP and CDMA interface) */ static gboolean common_setup_cleanup_unsolicited_registration_events_finish (MMBroadbandModemQmi *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void system_info_indication_cb (QmiClientNas *client, QmiIndicationNasSystemInfoOutput *output, MMBroadbandModemQmi *self) { if (mm_iface_modem_is_3gpp (MM_IFACE_MODEM (self))) common_process_system_info_3gpp (self, NULL, output); } static void system_status_indication_cb (QmiClientDsd *client, QmiIndicationDsdSystemStatusOutput *output, MMBroadbandModemQmi *self) { if (mm_iface_modem_is_3gpp (MM_IFACE_MODEM (self))) common_process_system_status_3gpp (self, NULL, output); } static void serving_system_indication_cb (QmiClientNas *client, QmiIndicationNasServingSystemOutput *output, MMBroadbandModemQmi *self) { if (mm_iface_modem_is_3gpp (MM_IFACE_MODEM (self))) common_process_serving_system_3gpp (self, NULL, output); else if (mm_iface_modem_is_cdma (MM_IFACE_MODEM (self))) common_process_serving_system_cdma (self, NULL, output); } /* network reject indications enabled in both with/without newest QMI commands */ static void network_reject_indication_cb (QmiClientNas *client, QmiIndicationNasNetworkRejectOutput *output, MMBroadbandModemQmi *self) { QmiNasNetworkServiceDomain service_domain = QMI_NAS_NETWORK_SERVICE_DOMAIN_UNKNOWN; QmiNasRadioInterface radio_interface = QMI_NAS_RADIO_INTERFACE_UNKNOWN; QmiNasRejectCause reject_cause = QMI_NAS_REJECT_CAUSE_NONE; guint16 mcc = 0; guint16 mnc = 0; guint32 closed_subscriber_group = 0; mm_obj_warn (self, "network reject indication received"); if (qmi_indication_nas_network_reject_output_get_service_domain (output, &service_domain, NULL)) mm_obj_warn (self, " service domain: %s", qmi_nas_network_service_domain_get_string (service_domain)); if (qmi_indication_nas_network_reject_output_get_radio_interface (output, &radio_interface, NULL)) mm_obj_warn (self, " radio interface: %s", qmi_nas_radio_interface_get_string (radio_interface)); if (qmi_indication_nas_network_reject_output_get_reject_cause (output, &reject_cause, NULL)) mm_obj_warn (self, " reject cause: %s", qmi_nas_reject_cause_get_string (reject_cause)); if (qmi_indication_nas_network_reject_output_get_plmn (output, &mcc, &mnc, NULL, NULL)) { mm_obj_warn (self, " mcc: %" G_GUINT16_FORMAT, mcc); mm_obj_warn (self, " mnc: %" G_GUINT16_FORMAT, mnc); } if (qmi_indication_nas_network_reject_output_get_closed_subscriber_group (output, &closed_subscriber_group, NULL)) mm_obj_warn (self, " closed subscriber group: %u", closed_subscriber_group); } static void common_setup_cleanup_unsolicited_registration_events (MMBroadbandModemQmi *self, gboolean enable, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; QmiClient *client_nas = NULL; QmiClient *client_dsd = NULL; if (!mm_shared_qmi_ensure_client (MM_SHARED_QMI (self), QMI_SERVICE_NAS, &client_nas, callback, user_data)) return; client_dsd = mm_shared_qmi_peek_client (MM_SHARED_QMI (self), QMI_SERVICE_DSD, MM_PORT_QMI_FLAG_DEFAULT, NULL); task = g_task_new (self, NULL, callback, user_data); if (enable == self->priv->unsolicited_registration_events_setup) { mm_obj_dbg (self, "unsolicited registration events already %s; skipping", enable ? "setup" : "cleanup"); g_task_return_boolean (task, TRUE); g_object_unref (task); return; } /* Store new state */ self->priv->unsolicited_registration_events_setup = enable; /* Connect/Disconnect "System Info" indications */ if (enable) { g_assert (self->priv->system_info_indication_id == 0); self->priv->system_info_indication_id = g_signal_connect (client_nas, "system-info", G_CALLBACK (system_info_indication_cb), self); } else { g_assert (self->priv->system_info_indication_id != 0); g_signal_handler_disconnect (client_nas, self->priv->system_info_indication_id); self->priv->system_info_indication_id = 0; } /* Connect/Disconnect "Serving System" indications */ if (enable) { g_assert (self->priv->serving_system_indication_id == 0); self->priv->serving_system_indication_id = g_signal_connect (client_nas, "serving-system", G_CALLBACK (serving_system_indication_cb), self); } else { g_assert (self->priv->serving_system_indication_id != 0); g_signal_handler_disconnect (client_nas, self->priv->serving_system_indication_id); self->priv->serving_system_indication_id = 0; } /* Connect/Disconnect "Network Reject" indications */ if (enable) { g_assert (self->priv->network_reject_indication_id == 0); self->priv->network_reject_indication_id = g_signal_connect (client_nas, "network-reject", G_CALLBACK (network_reject_indication_cb), self); } else { g_assert (self->priv->network_reject_indication_id != 0); g_signal_handler_disconnect (client_nas, self->priv->network_reject_indication_id); self->priv->network_reject_indication_id = 0; } /* Connect/Disconnect "System Status" indications */ if (client_dsd) { self->priv->dsd_supported = TRUE; if (enable) { g_assert (self->priv->system_status_indication_id == 0); self->priv->system_status_indication_id = g_signal_connect (client_dsd, "system-status", G_CALLBACK (system_status_indication_cb), self); } else { g_assert (self->priv->system_status_indication_id != 0); g_signal_handler_disconnect (client_dsd, self->priv->system_status_indication_id); self->priv->system_status_indication_id = 0; } } g_task_return_boolean (task, TRUE); g_object_unref (task); } /*****************************************************************************/ /* Setup/Cleanup unsolicited registration events (3GPP interface) */ static gboolean modem_3gpp_setup_cleanup_unsolicited_registration_events_finish (MMIfaceModem3gpp *self, GAsyncResult *res, GError **error) { return common_setup_cleanup_unsolicited_registration_events_finish (MM_BROADBAND_MODEM_QMI (self), res, error); } static void modem_3gpp_cleanup_unsolicited_registration_events (MMIfaceModem3gpp *self, GAsyncReadyCallback callback, gpointer user_data) { common_setup_cleanup_unsolicited_registration_events (MM_BROADBAND_MODEM_QMI (self), FALSE, callback, user_data); } static void modem_3gpp_setup_unsolicited_registration_events (MMIfaceModem3gpp *self, GAsyncReadyCallback callback, gpointer user_data) { common_setup_cleanup_unsolicited_registration_events (MM_BROADBAND_MODEM_QMI (self), TRUE, callback, user_data); } /*****************************************************************************/ /* MEID loading (CDMA interface) */ static gchar * modem_cdma_load_meid_finish (MMIfaceModemCdma *self, GAsyncResult *res, GError **error) { return g_task_propagate_pointer (G_TASK (res), error); } static void modem_cdma_load_meid (MMIfaceModemCdma *_self, GAsyncReadyCallback callback, gpointer user_data) { MMBroadbandModemQmi *self = MM_BROADBAND_MODEM_QMI (_self); GTask *task; task = g_task_new (self, NULL, callback, user_data); if (self->priv->meid) g_task_return_pointer (task, g_strdup (self->priv->meid), g_free); else g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Device doesn't report a valid MEID"); g_object_unref (task); } /*****************************************************************************/ /* ESN loading (CDMA interface) */ static gchar * modem_cdma_load_esn_finish (MMIfaceModemCdma *self, GAsyncResult *res, GError **error) { return g_task_propagate_pointer (G_TASK (res), error); } static void modem_cdma_load_esn (MMIfaceModemCdma *_self, GAsyncReadyCallback callback, gpointer user_data) { MMBroadbandModemQmi *self = MM_BROADBAND_MODEM_QMI (_self); GTask *task; task = g_task_new (self, NULL, callback, user_data); if (self->priv->esn) g_task_return_pointer (task, g_strdup (self->priv->esn), g_free); else g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Device doesn't report a valid ESN"); g_object_unref (task); } /*****************************************************************************/ /* Enabling/disabling unsolicited events (3GPP and CDMA interface) */ typedef struct { QmiClientNas *client_nas; QmiClientWds *client_wds; gboolean enable; } EnableUnsolicitedEventsContext; static void enable_unsolicited_events_context_free (EnableUnsolicitedEventsContext *ctx) { g_clear_object (&ctx->client_wds); g_clear_object (&ctx->client_nas); g_free (ctx); } /* default rssi delta value */ static const guint default_rssi_delta_dbm = 5; /* RSSI values go between -105 and -60 for 3GPP technologies, * and from -105 to -90 in 3GPP2 technologies (approx). */ static const gint8 default_thresholds_data_dbm[] = { -100, -97, -95, -92, -90, -85, -80, -75, -70, -65 }; static gboolean common_enable_disable_unsolicited_events_finish (MMBroadbandModemQmi *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void ser_data_system_status_ready (QmiClientWds *client, GAsyncResult *res, GTask *task) { MMBroadbandModemQmi *self; g_autoptr(QmiMessageWdsSetEventReportOutput) output = NULL; g_autoptr(GError) error = NULL; self = g_task_get_source_object (task); output = qmi_client_wds_set_event_report_finish (client, res, &error); if (!output || !qmi_message_wds_set_event_report_output_get_result (output, &error)) mm_obj_dbg (self, "couldn't set event report: '%s'", error->message); g_task_return_boolean (task, TRUE); g_object_unref (task); } static void common_enable_disable_unsolicited_events_data_system_status (GTask *task) { EnableUnsolicitedEventsContext *ctx; g_autoptr(QmiMessageWdsSetEventReportInput) input = NULL; ctx = g_task_get_task_data (task); input = qmi_message_wds_set_event_report_input_new (); qmi_message_wds_set_event_report_input_set_data_systems (input, ctx->enable, NULL); qmi_client_wds_set_event_report (ctx->client_wds, input, 5, NULL, (GAsyncReadyCallback)ser_data_system_status_ready, task); } static void ser_signal_strength_ready (QmiClientNas *client, GAsyncResult *res, GTask *task) { MMBroadbandModemQmi *self; EnableUnsolicitedEventsContext *ctx; g_autoptr(QmiMessageNasSetEventReportOutput) output = NULL; g_autoptr(GError) error = NULL; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); output = qmi_client_nas_set_event_report_finish (client, res, &error); if (!output || !qmi_message_nas_set_event_report_output_get_result (output, &error)) mm_obj_dbg (self, "couldn't enable signal strength indications: '%s'", error->message); else { /* Disable access technology and signal quality polling if we can use the indications */ mm_obj_dbg (self, "signal strength indications enabled: polling disabled"); g_object_set (self, MM_IFACE_MODEM_PERIODIC_SIGNAL_CHECK_DISABLED, TRUE, MM_IFACE_MODEM_PERIODIC_ACCESS_TECH_CHECK_DISABLED, TRUE, NULL); } if (!ctx->client_wds) { g_task_return_boolean (task, TRUE); g_object_unref (task); return; } common_enable_disable_unsolicited_events_data_system_status (task); } static void common_enable_disable_unsolicited_events_signal_strength (GTask *task) { EnableUnsolicitedEventsContext *ctx; g_autoptr(QmiMessageNasSetEventReportInput) input = NULL; g_autoptr(GArray) thresholds = NULL; /* The device doesn't really like to have many threshold values, so don't * grow this array without checking first * The values are chosen towards their results through MM_RSSI_TO_QUALITY * -106 dBm gives 11% * -94 dBm gives 30% * -82 dBm gives 50% * -69 dBm gives 70% * -57 dBm gives 90% */ static const gint8 thresholds_data[] = { -106, -94, -82, -69, -57 }; ctx = g_task_get_task_data (task); /* Always set thresholds, both in enable and disable, or otherwise the protocol will * complain with FAILURE: NoThresholdsProvided */ thresholds = g_array_sized_new (FALSE, FALSE, sizeof (gint8), G_N_ELEMENTS (thresholds_data)); g_array_append_vals (thresholds, thresholds_data, G_N_ELEMENTS (thresholds_data)); input = qmi_message_nas_set_event_report_input_new (); qmi_message_nas_set_event_report_input_set_signal_strength_indicator ( input, ctx->enable, thresholds, NULL); qmi_client_nas_set_event_report ( ctx->client_nas, input, 5, NULL, (GAsyncReadyCallback)ser_signal_strength_ready, task); } static void ri_signal_info_ready (QmiClientNas *client, GAsyncResult *res, GTask *task) { MMBroadbandModemQmi *self; EnableUnsolicitedEventsContext *ctx; g_autoptr(QmiMessageNasRegisterIndicationsOutput) output = NULL; g_autoptr(GError) error = NULL; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); output = qmi_client_nas_register_indications_finish (client, res, &error); if (!output || !qmi_message_nas_register_indications_output_get_result (output, &error)) { mm_obj_dbg (self, "couldn't register signal info indications: '%s', falling back to signal strength", error->message); common_enable_disable_unsolicited_events_signal_strength (task); g_clear_error (&error); return; } else { /* Disable access technology and signal quality polling if we can use the indications */ mm_obj_dbg (self, "signal info indications enabled: polling disabled"); g_object_set (self, MM_IFACE_MODEM_PERIODIC_SIGNAL_CHECK_DISABLED, TRUE, MM_IFACE_MODEM_PERIODIC_ACCESS_TECH_CHECK_DISABLED, TRUE, NULL); } if (!ctx->client_wds) { g_task_return_boolean (task, TRUE); g_object_unref (task); return; } common_enable_disable_unsolicited_events_data_system_status (task); } static void common_enable_disable_unsolicited_events_signal_info (GTask *task) { EnableUnsolicitedEventsContext *ctx; g_autoptr(QmiMessageNasRegisterIndicationsInput) input = NULL; ctx = g_task_get_task_data (task); input = qmi_message_nas_register_indications_input_new (); qmi_message_nas_register_indications_input_set_signal_info (input, ctx->enable, NULL); qmi_client_nas_register_indications ( ctx->client_nas, input, 5, NULL, (GAsyncReadyCallback)ri_signal_info_ready, task); } static void config_signal_info_ready (QmiClientNas *client, GAsyncResult *res, GTask *task) { MMBroadbandModemQmi *self; g_autoptr(QmiMessageNasConfigSignalInfoOutput) output = NULL; g_autoptr(GError) error = NULL; self = g_task_get_source_object (task); output = qmi_client_nas_config_signal_info_finish (client, res, &error); if (!output || !qmi_message_nas_config_signal_info_output_get_result (output, &error)) mm_obj_dbg (self, "couldn't config signal info: '%s'", error->message); /* Keep on */ common_enable_disable_unsolicited_events_signal_info (task); } static void config_signal_info_v2_ready (QmiClientNas *client, GAsyncResult *res, GTask *task) { MMBroadbandModemQmi *self; EnableUnsolicitedEventsContext *ctx; g_autoptr(QmiMessageNasConfigSignalInfoV2Output) output = NULL; g_autoptr(QmiMessageNasConfigSignalInfoInput) input = NULL; g_autoptr(GError) error = NULL; g_autoptr(GArray) thresholds = NULL; ctx = g_task_get_task_data (task); self = g_task_get_source_object (task); output = qmi_client_nas_config_signal_info_v2_finish (client, res, &error); if (!output || !qmi_message_nas_config_signal_info_v2_output_get_result (output, &error)) { if (g_error_matches (error, QMI_PROTOCOL_ERROR, QMI_PROTOCOL_ERROR_INVALID_QMI_COMMAND) || g_error_matches (error, QMI_PROTOCOL_ERROR, QMI_PROTOCOL_ERROR_NOT_SUPPORTED)) { mm_obj_dbg (self, "couldn't config signal info using v2: '%s', falling back to config signal info using v1", error->message); thresholds = g_array_sized_new (FALSE, FALSE, sizeof (gint8), G_N_ELEMENTS (default_thresholds_data_dbm)); g_array_append_vals (thresholds, default_thresholds_data_dbm, G_N_ELEMENTS (default_thresholds_data_dbm)); input = qmi_message_nas_config_signal_info_input_new (); qmi_message_nas_config_signal_info_input_set_rssi_threshold (input, thresholds, NULL); qmi_client_nas_config_signal_info ( ctx->client_nas, input, 5, NULL, (GAsyncReadyCallback)config_signal_info_ready, task); return; } mm_obj_dbg (self, "couldn't config signal info: '%s'", error->message); } /* Keep on */ common_enable_disable_unsolicited_events_signal_info (task); } static void common_enable_disable_unsolicited_events_signal_info_config (GTask *task) { EnableUnsolicitedEventsContext *ctx; g_autoptr(QmiMessageNasConfigSignalInfoV2Input) input = NULL; guint delta; ctx = g_task_get_task_data (task); /* Signal info config only to be run when enabling */ if (!ctx->enable) { common_enable_disable_unsolicited_events_signal_info (task); return; } /* delta in units of 0.1dBm */ delta = default_rssi_delta_dbm * 10; input = qmi_message_nas_config_signal_info_v2_input_new (); qmi_message_nas_config_signal_info_v2_input_set_cdma_rssi_delta (input, delta, NULL); qmi_message_nas_config_signal_info_v2_input_set_hdr_rssi_delta (input, delta, NULL); qmi_message_nas_config_signal_info_v2_input_set_gsm_rssi_delta (input, delta, NULL); qmi_message_nas_config_signal_info_v2_input_set_wcdma_rssi_delta (input, delta, NULL); qmi_message_nas_config_signal_info_v2_input_set_lte_rssi_delta (input, delta, NULL); /* use RSRP for 5GNR*/ qmi_message_nas_config_signal_info_v2_input_set_nr5g_rsrp_delta (input, delta, NULL); qmi_client_nas_config_signal_info_v2 ( ctx->client_nas, input, 5, NULL, (GAsyncReadyCallback)config_signal_info_v2_ready, task); } static void common_enable_disable_unsolicited_events (MMBroadbandModemQmi *self, gboolean enable, GAsyncReadyCallback callback, gpointer user_data) { EnableUnsolicitedEventsContext *ctx; GTask *task; QmiClient *client_nas = NULL; QmiClient *client_wds = NULL; task = g_task_new (self, NULL, callback, user_data); if (enable == self->priv->unsolicited_events_enabled) { mm_obj_dbg (self, "unsolicited events already %s; skipping", enable ? "enabled" : "disabled"); g_task_return_boolean (task, TRUE); g_object_unref (task); return; } self->priv->unsolicited_events_enabled = enable; client_nas = mm_shared_qmi_peek_client (MM_SHARED_QMI (self), QMI_SERVICE_NAS, MM_PORT_QMI_FLAG_DEFAULT, NULL); client_wds = mm_shared_qmi_peek_client (MM_SHARED_QMI (self), QMI_SERVICE_WDS, MM_PORT_QMI_FLAG_DEFAULT, NULL); ctx = g_new0 (EnableUnsolicitedEventsContext, 1); ctx->enable = enable; ctx->client_nas = client_nas ? QMI_CLIENT_NAS (g_object_ref (client_nas)) : NULL; ctx->client_wds = client_wds ? QMI_CLIENT_WDS (g_object_ref (client_wds)) : NULL; g_task_set_task_data (task, ctx, (GDestroyNotify)enable_unsolicited_events_context_free); if (ctx->client_nas) { common_enable_disable_unsolicited_events_signal_info_config (task); return; } if (ctx->client_wds) { common_enable_disable_unsolicited_events_data_system_status (task); return; } g_task_return_boolean (task, TRUE); g_object_unref (task); } /*****************************************************************************/ /* Enable/Disable unsolicited events (3GPP interface) */ static gboolean modem_3gpp_enable_disable_unsolicited_events_finish (MMIfaceModem3gpp *self, GAsyncResult *res, GError **error) { return common_enable_disable_unsolicited_events_finish (MM_BROADBAND_MODEM_QMI (self), res, error); } static void modem_3gpp_disable_unsolicited_events (MMIfaceModem3gpp *self, GAsyncReadyCallback callback, gpointer user_data) { common_enable_disable_unsolicited_events (MM_BROADBAND_MODEM_QMI (self), FALSE, callback, user_data); } static void modem_3gpp_enable_unsolicited_events (MMIfaceModem3gpp *self, GAsyncReadyCallback callback, gpointer user_data) { common_enable_disable_unsolicited_events (MM_BROADBAND_MODEM_QMI (self), TRUE, callback, user_data); } /*****************************************************************************/ /* Enable/Disable unsolicited events (CDMA interface) */ static gboolean modem_cdma_enable_disable_unsolicited_events_finish (MMIfaceModemCdma *self, GAsyncResult *res, GError **error) { return common_enable_disable_unsolicited_events_finish (MM_BROADBAND_MODEM_QMI (self), res, error); } static void modem_cdma_disable_unsolicited_events (MMIfaceModemCdma *self, GAsyncReadyCallback callback, gpointer user_data) { common_enable_disable_unsolicited_events (MM_BROADBAND_MODEM_QMI (self), FALSE, callback, user_data); } static void modem_cdma_enable_unsolicited_events (MMIfaceModemCdma *self, GAsyncReadyCallback callback, gpointer user_data) { common_enable_disable_unsolicited_events (MM_BROADBAND_MODEM_QMI (self), TRUE, callback, user_data); } /*****************************************************************************/ /* Setup/Cleanup unsolicited event handlers (3GPP and CDMA interface) */ static gboolean common_setup_cleanup_unsolicited_events_finish (MMBroadbandModemQmi *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void wds_event_report_indication_cb (QmiClientWds *client, QmiIndicationWdsEventReportOutput *output, MMBroadbandModemQmi *self) { QmiWdsDataSystemNetworkType preferred_network; if (qmi_indication_wds_event_report_output_get_data_systems (output, &preferred_network, NULL, NULL)) { mm_obj_dbg (self, "data systems update, preferred network: %s", qmi_wds_data_system_network_type_get_string (preferred_network)); if (preferred_network == QMI_WDS_DATA_SYSTEM_NETWORK_TYPE_3GPP) mm_iface_modem_3gpp_reload_initial_eps_bearer (MM_IFACE_MODEM_3GPP (self)); else mm_iface_modem_3gpp_update_initial_eps_bearer (MM_IFACE_MODEM_3GPP (self), NULL); } } static void nas_event_report_indication_cb (QmiClientNas *client, QmiIndicationNasEventReportOutput *output, MMBroadbandModemQmi *self) { gint8 signal_strength; QmiNasRadioInterface signal_strength_radio_interface; if (qmi_indication_nas_event_report_output_get_signal_strength ( output, &signal_strength, &signal_strength_radio_interface, NULL)) { if (qmi_dbm_valid (signal_strength, signal_strength_radio_interface)) { guint8 quality; /* This signal strength comes as negative dBms */ quality = MM_RSSI_TO_QUALITY (signal_strength); mm_obj_dbg (self, "signal strength indication (%s): %d dBm --> %u%%", qmi_nas_radio_interface_get_string (signal_strength_radio_interface), signal_strength, quality); mm_iface_modem_update_signal_quality (MM_IFACE_MODEM (self), quality); } else { mm_obj_dbg (self, "ignoring invalid signal strength (%s): %d dBm", qmi_nas_radio_interface_get_string (signal_strength_radio_interface), signal_strength); } } } static gdouble get_db_from_sinr_level (MMBroadbandModemQmi *self, QmiNasEvdoSinrLevel level) { switch (level) { case QMI_NAS_EVDO_SINR_LEVEL_0: return -9.0; case QMI_NAS_EVDO_SINR_LEVEL_1: return -6; case QMI_NAS_EVDO_SINR_LEVEL_2: return -4.5; case QMI_NAS_EVDO_SINR_LEVEL_3: return -3; case QMI_NAS_EVDO_SINR_LEVEL_4: return -2; case QMI_NAS_EVDO_SINR_LEVEL_5: return 1; case QMI_NAS_EVDO_SINR_LEVEL_6: return 3; case QMI_NAS_EVDO_SINR_LEVEL_7: return 6; case QMI_NAS_EVDO_SINR_LEVEL_8: return +9; default: mm_obj_warn (self, "invalid SINR level '%u'", level); return -G_MAXDOUBLE; } } static void common_process_signal_info (MMBroadbandModemQmi *self, QmiMessageNasGetSignalInfoOutput *response_output, QmiIndicationNasSignalInfoOutput *indication_output, MMSignal **out_cdma, MMSignal **out_evdo, MMSignal **out_gsm, MMSignal **out_umts, MMSignal **out_lte, MMSignal **out_nr5g) { gint8 rssi; gint16 ecio; QmiNasEvdoSinrLevel sinr_level; gint32 io; gint8 rsrq; gint16 rsrp; gint16 snr; gint16 rscp_umts; gint16 rsrq_5g; *out_cdma = NULL; *out_evdo = NULL; *out_gsm = NULL; *out_umts = NULL; *out_lte = NULL; *out_nr5g = NULL; /* CDMA */ if ((response_output && qmi_message_nas_get_signal_info_output_get_cdma_signal_strength (response_output, &rssi, &ecio, NULL)) || (indication_output && qmi_indication_nas_signal_info_output_get_cdma_signal_strength (indication_output, &rssi, &ecio, NULL))) { *out_cdma = mm_signal_new (); mm_signal_set_rssi (*out_cdma, (gdouble)rssi); mm_signal_set_ecio (*out_cdma, ((gdouble)ecio) * (-0.5)); } /* HDR... */ if ((response_output && qmi_message_nas_get_signal_info_output_get_hdr_signal_strength (response_output, &rssi, &ecio, &sinr_level, &io, NULL)) || (indication_output && qmi_indication_nas_signal_info_output_get_hdr_signal_strength (indication_output, &rssi, &ecio, &sinr_level, &io, NULL))) { *out_evdo = mm_signal_new (); mm_signal_set_rssi (*out_evdo, (gdouble)rssi); mm_signal_set_ecio (*out_evdo, ((gdouble)ecio) * (-0.5)); mm_signal_set_sinr (*out_evdo, get_db_from_sinr_level (self, sinr_level)); mm_signal_set_io (*out_evdo, (gdouble)io); } /* GSM */ if ((response_output && qmi_message_nas_get_signal_info_output_get_gsm_signal_strength (response_output, &rssi, NULL)) || (indication_output && qmi_indication_nas_signal_info_output_get_gsm_signal_strength (indication_output, &rssi, NULL))) { *out_gsm = mm_signal_new (); mm_signal_set_rssi (*out_gsm, (gdouble)rssi); } /* WCDMA... */ if ((response_output && qmi_message_nas_get_signal_info_output_get_wcdma_signal_strength (response_output, &rssi, &ecio, NULL)) || (indication_output && qmi_indication_nas_signal_info_output_get_wcdma_signal_strength (indication_output, &rssi, &ecio, NULL))) { *out_umts = mm_signal_new (); mm_signal_set_rssi (*out_umts, (gdouble)rssi); mm_signal_set_ecio (*out_umts, ((gdouble)ecio) * (-0.5)); } if ((response_output && qmi_message_nas_get_signal_info_output_get_wcdma_rscp (response_output, &rscp_umts, NULL)) || (indication_output && qmi_indication_nas_signal_info_output_get_wcdma_rscp (indication_output, &rscp_umts, NULL))) { if (G_UNLIKELY (!*out_umts)) *out_umts = mm_signal_new (); mm_signal_set_rscp (*out_umts, (-1.0) * ((gdouble)rscp_umts)); } /* LTE... */ if ((response_output && qmi_message_nas_get_signal_info_output_get_lte_signal_strength (response_output, &rssi, &rsrq, &rsrp, &snr, NULL)) || (indication_output && qmi_indication_nas_signal_info_output_get_lte_signal_strength (indication_output, &rssi, &rsrq, &rsrp, &snr, NULL))) { *out_lte = mm_signal_new (); mm_signal_set_rssi (*out_lte, (gdouble)rssi); mm_signal_set_rsrq (*out_lte, (gdouble)rsrq); mm_signal_set_rsrp (*out_lte, (gdouble)rsrp); mm_signal_set_snr (*out_lte, (0.1) * ((gdouble)snr)); } /* 5G */ if ((response_output && qmi_message_nas_get_signal_info_output_get_5g_signal_strength (response_output, &rsrp, &snr, NULL)) || (indication_output && qmi_indication_nas_signal_info_output_get_5g_signal_strength (indication_output, &rsrp, &snr, NULL))) { *out_nr5g = mm_signal_new (); mm_signal_set_rsrp (*out_nr5g, (gdouble)rsrp); mm_signal_set_snr (*out_nr5g, (0.1) * ((gdouble)snr)); } if ((response_output && qmi_message_nas_get_signal_info_output_get_5g_signal_strength_extended (response_output, &rsrq_5g, NULL)) || (indication_output && qmi_indication_nas_signal_info_output_get_5g_signal_strength_extended (indication_output, &rsrq_5g, NULL))) { if (G_UNLIKELY (!*out_nr5g)) *out_nr5g = mm_signal_new (); mm_signal_set_rsrq (*out_nr5g, (gdouble)rsrq_5g); } } static void nas_signal_info_indication_cb (QmiClientNas *client, QmiIndicationNasSignalInfoOutput *output, MMBroadbandModemQmi *self) { gint8 cdma1x_rssi = 0; gint8 evdo_rssi = 0; gint8 gsm_rssi = 0; gint8 wcdma_rssi = 0; gint8 lte_rssi = 0; gint16 nr5g_rsrp = RSRP_MAX + 1; /* Multiplying SNR_MAX by 10 as QMI gives SNR level * as a scaled integer in units of 0.1 dB. */ gint16 nr5g_snr = 10 * SNR_MAX + 10; gint16 nr5g_rsrq = RSRQ_MAX + 1; guint8 quality; g_autoptr(MMSignal) cdma = NULL; g_autoptr(MMSignal) evdo = NULL; g_autoptr(MMSignal) gsm = NULL; g_autoptr(MMSignal) umts = NULL; g_autoptr(MMSignal) lte = NULL; g_autoptr(MMSignal) nr5g = NULL; qmi_indication_nas_signal_info_output_get_cdma_signal_strength (output, &cdma1x_rssi, NULL, NULL); qmi_indication_nas_signal_info_output_get_hdr_signal_strength (output, &evdo_rssi, NULL, NULL, NULL, NULL); qmi_indication_nas_signal_info_output_get_gsm_signal_strength (output, &gsm_rssi, NULL); qmi_indication_nas_signal_info_output_get_wcdma_signal_strength (output, &wcdma_rssi, NULL, NULL); qmi_indication_nas_signal_info_output_get_lte_signal_strength (output, <e_rssi, NULL, NULL, NULL, NULL); qmi_indication_nas_signal_info_output_get_5g_signal_strength (output, &nr5g_rsrp, &nr5g_snr, NULL); qmi_indication_nas_signal_info_output_get_5g_signal_strength_extended (output, &nr5g_rsrq, NULL); /* Scale to integer values in units of 1 dB/dBm, if any */ nr5g_snr = 0.1 * nr5g_snr; if (common_signal_info_get_quality (self, cdma1x_rssi, evdo_rssi, gsm_rssi, wcdma_rssi, lte_rssi, nr5g_rsrp, nr5g_snr, nr5g_rsrq, &quality)) { mm_iface_modem_update_signal_quality (MM_IFACE_MODEM (self), quality); } common_process_signal_info (self, NULL, output, &cdma, &evdo, &gsm, &umts, <e, &nr5g); mm_iface_modem_signal_update (MM_IFACE_MODEM_SIGNAL (self), cdma, evdo, gsm, umts, lte, nr5g); } static void common_setup_cleanup_unsolicited_events (MMBroadbandModemQmi *self, gboolean enable, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; QmiClient *client_nas = NULL; QmiClient *client_wds = NULL; task = g_task_new (self, NULL, callback, user_data); if (enable == self->priv->unsolicited_events_setup) { mm_obj_dbg (self, "unsolicited events already %s; skipping", enable ? "setup" : "cleanup"); g_task_return_boolean (task, TRUE); g_object_unref (task); return; } self->priv->unsolicited_events_setup = enable; client_nas = mm_shared_qmi_peek_client (MM_SHARED_QMI (self), QMI_SERVICE_NAS, MM_PORT_QMI_FLAG_DEFAULT, NULL); client_wds = mm_shared_qmi_peek_client (MM_SHARED_QMI (self), QMI_SERVICE_WDS, MM_PORT_QMI_FLAG_DEFAULT, NULL); /* Connect/Disconnect "Event Report" indications */ if (client_nas) { if (enable) { g_assert (self->priv->nas_event_report_indication_id == 0); self->priv->nas_event_report_indication_id = g_signal_connect (client_nas, "event-report", G_CALLBACK (nas_event_report_indication_cb), self); } else if (self->priv->nas_event_report_indication_id != 0) { g_signal_handler_disconnect (client_nas, self->priv->nas_event_report_indication_id); self->priv->nas_event_report_indication_id = 0; } if (enable) { g_assert (self->priv->nas_signal_info_indication_id == 0); self->priv->nas_signal_info_indication_id = g_signal_connect (client_nas, "signal-info", G_CALLBACK (nas_signal_info_indication_cb), self); } else if (self->priv->nas_signal_info_indication_id != 0) { g_signal_handler_disconnect (client_nas, self->priv->nas_signal_info_indication_id); self->priv->nas_signal_info_indication_id = 0; } } if (client_wds) { if (enable) { g_assert (self->priv->wds_event_report_indication_id == 0); self->priv->wds_event_report_indication_id = g_signal_connect (client_wds, "event-report", G_CALLBACK (wds_event_report_indication_cb), self); } else if (self->priv->wds_event_report_indication_id != 0) { g_signal_handler_disconnect (client_wds, self->priv->wds_event_report_indication_id); self->priv->wds_event_report_indication_id = 0; } } g_task_return_boolean (task, TRUE); g_object_unref (task); } /*****************************************************************************/ /* Enable/Disable unsolicited events (3GPP interface) */ static gboolean modem_3gpp_setup_cleanup_unsolicited_events_finish (MMIfaceModem3gpp *self, GAsyncResult *res, GError **error) { return common_setup_cleanup_unsolicited_events_finish (MM_BROADBAND_MODEM_QMI (self), res, error); } static void modem_3gpp_cleanup_unsolicited_events (MMIfaceModem3gpp *self, GAsyncReadyCallback callback, gpointer user_data) { common_setup_cleanup_unsolicited_events (MM_BROADBAND_MODEM_QMI (self), FALSE, callback, user_data); } static void modem_3gpp_setup_unsolicited_events (MMIfaceModem3gpp *self, GAsyncReadyCallback callback, gpointer user_data) { common_setup_cleanup_unsolicited_events (MM_BROADBAND_MODEM_QMI (self), TRUE, callback, user_data); } /*****************************************************************************/ /* Enable/Disable unsolicited events (CDMA interface) */ static gboolean modem_cdma_setup_cleanup_unsolicited_events_finish (MMIfaceModemCdma *self, GAsyncResult *res, GError **error) { return common_setup_cleanup_unsolicited_events_finish (MM_BROADBAND_MODEM_QMI (self), res, error); } static void modem_cdma_cleanup_unsolicited_events (MMIfaceModemCdma *self, GAsyncReadyCallback callback, gpointer user_data) { common_setup_cleanup_unsolicited_events (MM_BROADBAND_MODEM_QMI (self), FALSE, callback, user_data); } static void modem_cdma_setup_unsolicited_events (MMIfaceModemCdma *self, GAsyncReadyCallback callback, gpointer user_data) { common_setup_cleanup_unsolicited_events (MM_BROADBAND_MODEM_QMI (self), TRUE, callback, user_data); } /*****************************************************************************/ /* Check format (3gppProfileManager interface) */ static gboolean modem_3gpp_profile_manager_check_format_finish (MMIfaceModem3gppProfileManager *self, GAsyncResult *res, gboolean *new_id, gint *min_profile_id, gint *max_profile_id, GEqualFunc *apn_cmp, MM3gppProfileCmpFlags *profile_cmp_flags, GError **error) { if (!g_task_propagate_boolean (G_TASK (res), error)) { g_assert_not_reached (); return FALSE; } /* Generic WDS Create Profile method does NOT allow specifying a specific * profile id */ if (new_id) *new_id = FALSE; if (min_profile_id) *min_profile_id = 1; if (max_profile_id) *max_profile_id = G_MAXINT - 1; /* use default string comparison method */ if (apn_cmp) *apn_cmp = NULL; if (profile_cmp_flags) *profile_cmp_flags = (MM_3GPP_PROFILE_CMP_FLAGS_NO_ACCESS_TYPE_PREFERENCE | MM_3GPP_PROFILE_CMP_FLAGS_NO_ROAMING_ALLOWANCE | MM_3GPP_PROFILE_CMP_FLAGS_NO_PROFILE_SOURCE); return TRUE; } static void modem_3gpp_profile_manager_check_format (MMIfaceModem3gppProfileManager *self, MMBearerIpFamily ip_type, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; task = g_task_new (self, NULL, callback, user_data); g_task_return_boolean (task, TRUE); g_object_unref (task); } /*****************************************************************************/ /* Get profile (3GPP profile management interface) */ static MM3gppProfile * modem_3gpp_profile_manager_get_profile_finish (MMIfaceModem3gppProfileManager *self, GAsyncResult *res, GError **error) { return MM_3GPP_PROFILE (g_task_propagate_pointer (G_TASK (res), error)); } static MM3gppProfile * wds_profile_settings_to_3gpp_profile (MMBroadbandModemQmi *self, guint profile_index, QmiMessageWdsGetProfileSettingsOutput *output, GError **error) { MM3gppProfile *profile = NULL; const gchar *str; QmiWdsPdpType pdp_type; QmiWdsAuthentication auth; QmiWdsApnTypeMask apn_type; profile = mm_3gpp_profile_new (); /* On 3GPP modems, the modem seems to force profile-index = pdp-context-number, * and so, we're just going to rely on the profile-index ourselves.*/ mm_3gpp_profile_set_profile_id (profile, (gint) profile_index); if (qmi_message_wds_get_profile_settings_output_get_apn_name (output, &str, NULL)) mm_3gpp_profile_set_apn (profile, str); if (qmi_message_wds_get_profile_settings_output_get_profile_name (output, &str, NULL)) mm_3gpp_profile_set_profile_name (profile, str); if (qmi_message_wds_get_profile_settings_output_get_pdp_type (output, &pdp_type, NULL)) mm_3gpp_profile_set_ip_type (profile, mm_bearer_ip_family_from_qmi_pdp_type (pdp_type)); if (qmi_message_wds_get_profile_settings_output_get_authentication (output, &auth, NULL)) mm_3gpp_profile_set_allowed_auth (profile, mm_bearer_allowed_auth_from_qmi_authentication (auth)); /* ignore empty user/pass strings */ if (qmi_message_wds_get_profile_settings_output_get_username (output, &str, NULL) && str[0]) mm_3gpp_profile_set_user (profile, str); if (qmi_message_wds_get_profile_settings_output_get_password (output, &str, NULL) && str[0]) mm_3gpp_profile_set_password (profile, str); /* If loading APN type TLV fails, flag it as unsupported so that we don't try to use it any * more. */ if (qmi_message_wds_get_profile_settings_output_get_apn_type_mask (output, &apn_type, NULL)) mm_3gpp_profile_set_apn_type (profile, mm_bearer_apn_type_from_qmi_apn_type (apn_type)); else if (!self->priv->apn_type_not_supported) { mm_obj_dbg (self, "APN type flagged as not supported: not given in profile settings"); self->priv->apn_type_not_supported = TRUE; } return profile; } static void get_profile_settings_ready (QmiClientWds *client, GAsyncResult *res, GTask *task) { MMBroadbandModemQmi *self; GError *error = NULL; gint profile_id; gboolean profile_disabled = FALSE; MM3gppProfile *profile; g_autoptr(QmiMessageWdsGetProfileSettingsOutput) output = NULL; self = g_task_get_source_object (task); profile_id = GPOINTER_TO_INT (g_task_get_task_data (task)); output = qmi_client_wds_get_profile_settings_finish (client, res, &error); if (!output || !qmi_message_wds_get_profile_settings_output_get_result (output, &error)) { g_prefix_error (&error, "Couldn't load settings from profile index %u: ", profile_id); g_task_return_error (task, error); g_object_unref (task); return; } /* just ignore the profile if it's disabled */ qmi_message_wds_get_profile_settings_output_get_apn_disabled_flag (output, &profile_disabled, NULL); if (profile_disabled) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Profile '%d' is internally disabled", profile_id); g_object_unref (task); return; } profile = wds_profile_settings_to_3gpp_profile (self, profile_id, output, &error); if (!profile) g_task_return_error (task, error); else g_task_return_pointer (task, profile, g_object_unref); g_object_unref (task); } static void modem_3gpp_profile_manager_get_profile (MMIfaceModem3gppProfileManager *self, gint profile_id, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; QmiClient *client = NULL; g_autoptr(QmiMessageWdsGetProfileSettingsInput) input = NULL; if (!mm_shared_qmi_ensure_client (MM_SHARED_QMI (self), QMI_SERVICE_WDS, &client, callback, user_data)) return; task = g_task_new (self, NULL, callback, user_data); g_task_set_task_data (task, GINT_TO_POINTER (profile_id), NULL); input = qmi_message_wds_get_profile_settings_input_new (); qmi_message_wds_get_profile_settings_input_set_profile_id ( input, QMI_WDS_PROFILE_TYPE_3GPP, profile_id, NULL); qmi_client_wds_get_profile_settings (QMI_CLIENT_WDS (client), input, 3, NULL, (GAsyncReadyCallback)get_profile_settings_ready, task); } /*****************************************************************************/ /* List profiles (3GPP profile management interface) */ typedef struct { GList *profiles; GArray *qmi_profiles; guint i; } ListProfilesContext; static void list_profiles_context_free (ListProfilesContext *ctx) { g_clear_pointer (&ctx->qmi_profiles, (GDestroyNotify)g_array_unref); mm_3gpp_profile_list_free (ctx->profiles); g_slice_free (ListProfilesContext, ctx); } static gboolean modem_3gpp_profile_manager_list_profiles_finish (MMIfaceModem3gppProfileManager *self, GAsyncResult *res, GList **out_profiles, GError **error) { ListProfilesContext *ctx; if (!g_task_propagate_boolean (G_TASK (res), error)) return FALSE; ctx = g_task_get_task_data (G_TASK (res)); if (out_profiles) *out_profiles = g_steal_pointer (&ctx->profiles); return TRUE; } static void get_next_profile_settings (GTask *task); static void get_next_profile_settings_ready (MMIfaceModem3gppProfileManager *self, GAsyncResult *res, GTask *task) { ListProfilesContext *ctx; MM3gppProfile *profile; GError *error = NULL; ctx = g_task_get_task_data (task); profile = modem_3gpp_profile_manager_get_profile_finish (self, res, &error); if (!profile) { g_prefix_error (&error, "Couldn't load settings from profile index %u: ", ctx->i); g_task_return_error (task, error); g_object_unref (task); return; } ctx->profiles = g_list_append (ctx->profiles, profile); /* Keep on */ ctx->i++; get_next_profile_settings (task); } static void get_next_profile_settings (GTask *task) { MMBroadbandModemQmi *self; ListProfilesContext *ctx; QmiMessageWdsGetProfileListOutputProfileListProfile *current; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); if (ctx->i >= ctx->qmi_profiles->len) { /* All done */ g_task_return_boolean (task, TRUE); g_object_unref (task); return; } current = &g_array_index (ctx->qmi_profiles, QmiMessageWdsGetProfileListOutputProfileListProfile, ctx->i); modem_3gpp_profile_manager_get_profile (MM_IFACE_MODEM_3GPP_PROFILE_MANAGER (self), current->profile_index, (GAsyncReadyCallback)get_next_profile_settings_ready, task); } static void get_profile_list_ready (QmiClientWds *client, GAsyncResult *res, GTask *task) { ListProfilesContext *ctx; GError *error = NULL; GArray *qmi_profiles = NULL; g_autoptr(QmiMessageWdsGetProfileListOutput) output = NULL; ctx = g_slice_new0 (ListProfilesContext); g_task_set_task_data (task, ctx, (GDestroyNotify) list_profiles_context_free); output = qmi_client_wds_get_profile_list_finish (client, res, &error); if (!output || !qmi_message_wds_get_profile_list_output_get_result (output, &error)) { g_task_return_error (task, error); g_object_unref (task); return; } qmi_message_wds_get_profile_list_output_get_profile_list (output, &qmi_profiles, NULL); /* empty list? */ if (!qmi_profiles || !qmi_profiles->len) { g_task_return_boolean (task, TRUE); g_object_unref (task); return; } ctx->qmi_profiles = g_array_ref (qmi_profiles); get_next_profile_settings (task); } static void modem_3gpp_profile_manager_list_profiles (MMIfaceModem3gppProfileManager *self, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; QmiClient *client = NULL; g_autoptr(QmiMessageWdsGetProfileListInput) input = NULL; if (!mm_shared_qmi_ensure_client (MM_SHARED_QMI (self), QMI_SERVICE_WDS, &client, callback, user_data)) return; task = g_task_new (self, NULL, callback, user_data); input = qmi_message_wds_get_profile_list_input_new (); qmi_message_wds_get_profile_list_input_set_profile_type (input, QMI_WDS_PROFILE_TYPE_3GPP, NULL); qmi_client_wds_get_profile_list (QMI_CLIENT_WDS (client), input, 10, NULL, (GAsyncReadyCallback)get_profile_list_ready, task); } /*****************************************************************************/ /* Store profile (3GPP profile management interface) */ typedef struct { QmiClientWds *client; gint profile_id; gchar *profile_name; gchar *apn; gchar *user; gchar *password; QmiWdsApnTypeMask qmi_apn_type; QmiWdsAuthentication qmi_auth; QmiWdsPdpType qmi_pdp_type; } StoreProfileContext; static void store_profile_context_free (StoreProfileContext *ctx) { g_free (ctx->profile_name); g_free (ctx->apn); g_free (ctx->user); g_free (ctx->password); g_clear_object (&ctx->client); g_slice_free (StoreProfileContext, ctx); } static gboolean modem_3gpp_profile_manager_store_profile_finish (MMIfaceModem3gppProfileManager *self, GAsyncResult *res, gint *out_profile_id, MMBearerApnType *out_apn_type, GError **error) { StoreProfileContext *ctx; if (!g_task_propagate_boolean (G_TASK (res), error)) return FALSE; ctx = g_task_get_task_data (G_TASK (res)); if (out_profile_id) *out_profile_id = ctx->profile_id; if (out_apn_type) *out_apn_type = MM_BEARER_APN_TYPE_NONE; return TRUE; } static void store_profile_run (GTask *task); static void modify_profile_ready (QmiClientWds *client, GAsyncResult *res, GTask *task) { MMBroadbandModemQmi *self; GError *error = NULL; g_autoptr(QmiMessageWdsModifyProfileOutput) output = NULL; self = g_task_get_source_object (task); output = qmi_client_wds_modify_profile_finish (client, res, &error); if (!output) { g_task_return_error (task, error); } else if (!qmi_message_wds_modify_profile_output_get_result (output, &error)) { QmiWdsDsProfileError ds_profile_error; if (g_error_matches (error, QMI_PROTOCOL_ERROR, QMI_PROTOCOL_ERROR_INVALID_PROFILE_TYPE) && !self->priv->apn_type_not_supported) { /* we'll retry the operation without APN type, which is a setting available only * in newer devices. */ mm_obj_dbg (self, "APN type flagged as not supported: failed to modify profile"); self->priv->apn_type_not_supported = TRUE; g_clear_error (&error); store_profile_run (task); return; } if (g_error_matches (error, QMI_PROTOCOL_ERROR, QMI_PROTOCOL_ERROR_EXTENDED_INTERNAL) && qmi_message_wds_modify_profile_output_get_extended_error_code (output, &ds_profile_error, NULL)) { g_prefix_error (&error, "DS profile error: %s: ", qmi_wds_ds_profile_error_get_string (ds_profile_error)); } g_prefix_error (&error, "Couldn't modify profile: "); g_task_return_error (task, error); } else { g_task_return_boolean (task, TRUE); } g_object_unref (task); } static void create_profile_ready (QmiClientWds *client, GAsyncResult *res, GTask *task) { MMBroadbandModemQmi *self; StoreProfileContext *ctx; GError *error = NULL; guint8 profile_index; g_autoptr(QmiMessageWdsCreateProfileOutput) output = NULL; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); output = qmi_client_wds_create_profile_finish (client, res, &error); if (!output) { g_task_return_error (task, error); } else if (!qmi_message_wds_create_profile_output_get_result (output, &error)) { QmiWdsDsProfileError ds_profile_error; if (g_error_matches (error, QMI_PROTOCOL_ERROR, QMI_PROTOCOL_ERROR_INVALID_PROFILE_TYPE) && !self->priv->apn_type_not_supported) { /* we'll retry the operation without APN type, which is a setting available only * in newer devices. */ mm_obj_dbg (self, "APN type flagged as not supported: failed to create profile"); self->priv->apn_type_not_supported = TRUE; g_clear_error (&error); store_profile_run (task); return; } if (g_error_matches (error, QMI_PROTOCOL_ERROR, QMI_PROTOCOL_ERROR_EXTENDED_INTERNAL) && qmi_message_wds_create_profile_output_get_extended_error_code (output, &ds_profile_error, NULL)) { g_prefix_error (&error, "DS profile error: %s: ", qmi_wds_ds_profile_error_get_string (ds_profile_error)); } g_prefix_error (&error, "Couldn't create profile: "); g_task_return_error (task, error); } else if (!qmi_message_wds_create_profile_output_get_profile_identifier (output, NULL, &profile_index, &error)) { g_task_return_error (task, error); } else { ctx->profile_id = profile_index; g_task_return_boolean (task, TRUE); } g_object_unref (task); } static void store_profile_run (GTask *task) { MMBroadbandModemQmi *self; StoreProfileContext *ctx; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); if (ctx->profile_id == MM_3GPP_PROFILE_ID_UNKNOWN) { g_autoptr(QmiMessageWdsCreateProfileInput) input = NULL; /* when creating, we cannot select which profile id to use */ input = qmi_message_wds_create_profile_input_new (); qmi_message_wds_create_profile_input_set_profile_type (input, QMI_WDS_PROFILE_TYPE_3GPP, NULL); qmi_message_wds_create_profile_input_set_profile_name (input, ctx->profile_name, NULL); qmi_message_wds_create_profile_input_set_pdp_type (input, ctx->qmi_pdp_type, NULL); qmi_message_wds_create_profile_input_set_apn_name (input, ctx->apn, NULL); qmi_message_wds_create_profile_input_set_authentication (input, ctx->qmi_auth, NULL); qmi_message_wds_create_profile_input_set_username (input, ctx->user, NULL); qmi_message_wds_create_profile_input_set_password (input, ctx->password, NULL); if (!self->priv->apn_type_not_supported) qmi_message_wds_create_profile_input_set_apn_type_mask (input, ctx->qmi_apn_type, NULL); qmi_client_wds_create_profile (ctx->client, input, 10, NULL, (GAsyncReadyCallback)create_profile_ready, task); } else { g_autoptr(QmiMessageWdsModifyProfileInput) input = NULL; input = qmi_message_wds_modify_profile_input_new (); qmi_message_wds_modify_profile_input_set_profile_identifier (input, QMI_WDS_PROFILE_TYPE_3GPP, ctx->profile_id, NULL); qmi_message_wds_modify_profile_input_set_profile_name (input, ctx->profile_name, NULL); qmi_message_wds_modify_profile_input_set_pdp_type (input, ctx->qmi_pdp_type, NULL); qmi_message_wds_modify_profile_input_set_apn_name (input, ctx->apn, NULL); qmi_message_wds_modify_profile_input_set_authentication (input, ctx->qmi_auth, NULL); qmi_message_wds_modify_profile_input_set_username (input, ctx->user, NULL); qmi_message_wds_modify_profile_input_set_password (input, ctx->password, NULL); if (!self->priv->apn_type_not_supported) qmi_message_wds_modify_profile_input_set_apn_type_mask (input, ctx->qmi_apn_type, NULL); qmi_client_wds_modify_profile (ctx->client, input, 10, NULL, (GAsyncReadyCallback)modify_profile_ready, task); } } static void modem_3gpp_profile_manager_store_profile (MMIfaceModem3gppProfileManager *self, MM3gppProfile *profile, const gchar *index_field, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; StoreProfileContext *ctx; QmiClient *client = NULL; MMBearerIpFamily ip_type; MMBearerApnType apn_type; MMBearerAllowedAuth allowed_auth; g_assert (g_strcmp0 (index_field, "profile-id") == 0); if (!mm_shared_qmi_ensure_client (MM_SHARED_QMI (self), QMI_SERVICE_WDS, &client, callback, user_data)) return; task = g_task_new (self, NULL, callback, user_data); ctx = g_slice_new0 (StoreProfileContext); ctx->client = QMI_CLIENT_WDS (g_object_ref (client)); g_task_set_task_data (task, ctx, (GDestroyNotify)store_profile_context_free); /* Note: may be UNKNOWN */ ctx->profile_id = mm_3gpp_profile_get_profile_id (profile); ctx->profile_name = g_strdup (mm_3gpp_profile_get_profile_name (profile)); ctx->apn = g_strdup (mm_3gpp_profile_get_apn (profile)); apn_type = mm_3gpp_profile_get_apn_type (profile); ctx->qmi_apn_type = mm_bearer_apn_type_to_qmi_apn_type (apn_type, self); ctx->user = g_strdup (mm_3gpp_profile_get_user (profile)); ctx->password = g_strdup (mm_3gpp_profile_get_password (profile)); allowed_auth = mm_3gpp_profile_get_allowed_auth (profile); if ((allowed_auth != MM_BEARER_ALLOWED_AUTH_UNKNOWN) || ctx->user || ctx->password) { GError *error = NULL; ctx->qmi_auth = mm_bearer_allowed_auth_to_qmi_authentication (allowed_auth, self, &error); if (error) { g_task_return_error (task, error); g_object_unref (task); return; } } ip_type = mm_3gpp_profile_get_ip_type (profile); if (!mm_bearer_ip_family_to_qmi_pdp_type (ip_type, &ctx->qmi_pdp_type)) { g_autofree gchar *ip_type_str = NULL; ip_type_str = mm_bearer_ip_family_build_string_from_mask (ip_type); g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Invalid IP type specified: %s", ip_type_str); g_object_unref (task); return; } store_profile_run (task); } /*****************************************************************************/ /* Delete profile (3GPP profile management interface) */ static gboolean modem_3gpp_profile_manager_delete_profile_finish (MMIfaceModem3gppProfileManager *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void delete_profile_ready (QmiClientWds *client, GAsyncResult *res, GTask *task) { GError *error = NULL; g_autoptr(QmiMessageWdsDeleteProfileOutput) output = NULL; output = qmi_client_wds_delete_profile_finish (client, res, &error); if (!output || !qmi_message_wds_delete_profile_output_get_result (output, &error)) { g_prefix_error (&error, "Couldn't delete profile: "); g_task_return_error (task, error); } else g_task_return_boolean (task, TRUE); g_object_unref (task); } static void modem_3gpp_profile_manager_delete_profile (MMIfaceModem3gppProfileManager *self, MM3gppProfile *profile, const gchar *index_field, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; QmiClient *client = NULL; gint profile_id; g_autoptr(QmiMessageWdsDeleteProfileInput) input = NULL; g_assert (g_strcmp0 (index_field, "profile-id") == 0); if (!mm_shared_qmi_ensure_client (MM_SHARED_QMI (self), QMI_SERVICE_WDS, &client, callback, user_data)) return; task = g_task_new (self, NULL, callback, user_data); profile_id = mm_3gpp_profile_get_profile_id (profile); g_assert (profile_id != MM_3GPP_PROFILE_ID_UNKNOWN); mm_obj_dbg (self, "deleting profile '%d'", profile_id); input = qmi_message_wds_delete_profile_input_new (); qmi_message_wds_delete_profile_input_set_profile_identifier (input, QMI_WDS_PROFILE_TYPE_3GPP, profile_id, NULL); qmi_client_wds_delete_profile (QMI_CLIENT_WDS (client), input, 10, NULL, (GAsyncReadyCallback)delete_profile_ready, task); } /*****************************************************************************/ /* PDC Refresh events (3gppProfileManager interface) */ static void pdc_refresh_received (QmiClientPdc *client, QmiIndicationPdcRefreshOutput *output, MMBroadbandModemQmi *self) { mm_obj_dbg (self, "profile refresh indication was received"); mm_iface_modem_3gpp_profile_manager_updated (MM_IFACE_MODEM_3GPP_PROFILE_MANAGER (self)); } /*****************************************************************************/ /* Profile Changed events (3gppProfileManager interface) */ static void profile_changed_indication_received (QmiClientWds *client, QmiIndicationWdsProfileChangedOutput *output, MMBroadbandModemQmi *self) { mm_obj_dbg (self, "profile changed indication was received"); mm_iface_modem_3gpp_profile_manager_updated (MM_IFACE_MODEM_3GPP_PROFILE_MANAGER (self)); } /*****************************************************************************/ /* Enable/Disable unsolicited events (3gppProfileManager interface) */ typedef enum { REGISTER_PROFILE_REFRESH_STEP_FIRST, REGISTER_PROFILE_REFRESH_STEP_PROFILE_REFRESH, REGISTER_PROFILE_REFRESH_STEP_PROFILE_CHANGE, REGISTER_PROFILE_REFRESH_STEP_CONFIGURE_PROFILE_EVENT, REGISTER_PROFILE_REFRESH_STEP_LAST, } RegisterProfileRefreshStep; typedef struct { QmiClientPdc *client_pdc; QmiClientWds *client_wds; RegisterProfileRefreshStep step; gboolean enable; } RegisterProfileRefreshContext; static void register_profile_refresh_context_free (RegisterProfileRefreshContext *ctx) { g_clear_object (&ctx->client_pdc); g_clear_object (&ctx->client_wds); g_free (ctx); } static gboolean modem_3gpp_profile_manager_enable_disable_unsolicited_events_finish (MMIfaceModem3gppProfileManager *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void register_profile_refresh_context_step (GTask *task); static void register_pdc_refresh_ready (QmiClientPdc *client, GAsyncResult *res, GTask *task) { g_autoptr(QmiMessagePdcRegisterOutput) output = NULL; RegisterProfileRefreshContext *ctx; GError *error = NULL; ctx = g_task_get_task_data (task); output = qmi_client_pdc_register_finish (client, res, &error); if (!output || !qmi_message_pdc_register_output_get_result (output, &error)) { g_task_return_error (task, error); g_object_unref (task); return; } ctx->step++; register_profile_refresh_context_step (task); } static void register_wds_profile_change_ready (QmiClientWds *client, GAsyncResult *res, GTask *task) { g_autoptr(QmiMessageWdsIndicationRegisterOutput) output = NULL; RegisterProfileRefreshContext *ctx; GError *error = NULL; ctx = g_task_get_task_data (task); output = qmi_client_wds_indication_register_finish (client, res, &error); if (!output || !qmi_message_wds_indication_register_output_get_result (output, &error)) { g_task_return_error (task, error); g_object_unref (task); return; } ctx->step++; register_profile_refresh_context_step (task); } static void register_wds_configure_profile_event_ready (QmiClientWds *client, GAsyncResult *res, GTask *task) { g_autoptr(QmiMessageWdsConfigureProfileEventListOutput) output = NULL; RegisterProfileRefreshContext *ctx; GError *error = NULL; ctx = g_task_get_task_data (task); output = qmi_client_wds_configure_profile_event_list_finish (client, res, &error); if (!output || !qmi_message_wds_configure_profile_event_list_output_get_result (output, &error)) { g_task_return_error (task, error); g_object_unref (task); return; } ctx->step++; register_profile_refresh_context_step (task); } static void register_profile_refresh_context_step (GTask *task) { MMBroadbandModemQmi *self; RegisterProfileRefreshContext *ctx; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); switch (ctx->step) { case REGISTER_PROFILE_REFRESH_STEP_FIRST: ctx->step++; /* Fall through */ case REGISTER_PROFILE_REFRESH_STEP_PROFILE_REFRESH: if (ctx->client_pdc) { g_autoptr(QmiMessagePdcRegisterInput) input = NULL; input = qmi_message_pdc_register_input_new (); qmi_message_pdc_register_input_set_enable_reporting (input, ctx->enable, NULL); qmi_client_pdc_register (ctx->client_pdc, input, 10, NULL, (GAsyncReadyCallback) register_pdc_refresh_ready, task); return; } ctx->step++; /* Fall through */ case REGISTER_PROFILE_REFRESH_STEP_PROFILE_CHANGE: if (ctx->client_wds) { g_autoptr(QmiMessageWdsIndicationRegisterInput) input = NULL; input = qmi_message_wds_indication_register_input_new (); qmi_message_wds_indication_register_input_set_report_profile_changes (input, ctx->enable, NULL); qmi_client_wds_indication_register (ctx->client_wds, input, 10, NULL, (GAsyncReadyCallback) register_wds_profile_change_ready, task); return; } ctx->step++; /* Fall through */ case REGISTER_PROFILE_REFRESH_STEP_CONFIGURE_PROFILE_EVENT: if (ctx->client_wds) { g_autoptr(QmiMessageWdsConfigureProfileEventListInput) input = NULL; g_autoptr(GArray) array = NULL; QmiMessageWdsConfigureProfileEventListInputRegisterElement element = {0}; element.profile_type = QMI_WDS_PROFILE_TYPE_ALL; element.profile_index = 0xFF; array = g_array_new (FALSE, FALSE, sizeof (QmiMessageWdsConfigureProfileEventListInputRegisterElement)); g_array_append_val (array, element); input = qmi_message_wds_configure_profile_event_list_input_new (); qmi_message_wds_configure_profile_event_list_input_set_register (input, array, NULL); qmi_client_wds_configure_profile_event_list (ctx->client_wds, input, 10, NULL, (GAsyncReadyCallback) register_wds_configure_profile_event_ready, task); return; } ctx->step++; /* Fall through */ case REGISTER_PROFILE_REFRESH_STEP_LAST: self->priv->profile_manager_unsolicited_events_enabled = ctx->enable; mm_obj_dbg (self, "%s for refresh events", ctx->enable ? "registered" : "unregistered"); g_task_return_boolean (task, TRUE); g_object_unref (task); return; default: g_assert_not_reached (); } } static void common_enable_disable_unsolicited_events_3gpp_profile_manager (MMBroadbandModemQmi *self, gboolean enable, GAsyncReadyCallback callback, gpointer user_data) { RegisterProfileRefreshContext *ctx; GTask *task; QmiClient *client_pdc = NULL; QmiClient *client_wds = NULL; task = g_task_new (self, NULL, callback, user_data); if (enable == self->priv->profile_manager_unsolicited_events_enabled) { mm_obj_dbg (self, "profile manager unsolicited events already %s; skipping", enable ? "enabled" : "disabled"); g_task_return_boolean (task, TRUE); g_object_unref (task); return; } client_pdc = mm_shared_qmi_peek_client (MM_SHARED_QMI (self), QMI_SERVICE_PDC, MM_PORT_QMI_FLAG_DEFAULT, NULL); client_wds = mm_shared_qmi_peek_client (MM_SHARED_QMI (self), QMI_SERVICE_WDS, MM_PORT_QMI_FLAG_DEFAULT, NULL); /* Fail if none of the clients can be allocated */ if (!client_pdc && !client_wds) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "No support for profile refresh events"); g_object_unref (task); return; } ctx = g_new0 (RegisterProfileRefreshContext, 1); ctx->step = REGISTER_PROFILE_REFRESH_STEP_FIRST; ctx->enable = enable; ctx->client_pdc = client_pdc ? QMI_CLIENT_PDC (g_object_ref (client_pdc)) : NULL; ctx->client_wds = client_wds ? QMI_CLIENT_WDS (g_object_ref (client_wds)) : NULL; g_task_set_task_data (task, ctx, (GDestroyNotify)register_profile_refresh_context_free); register_profile_refresh_context_step (task); } static void modem_3gpp_profile_manager_disable_unsolicited_events (MMIfaceModem3gppProfileManager *self, GAsyncReadyCallback callback, gpointer user_data) { common_enable_disable_unsolicited_events_3gpp_profile_manager (MM_BROADBAND_MODEM_QMI (self), FALSE, callback, user_data); } static void modem_3gpp_profile_manager_enable_unsolicited_events (MMIfaceModem3gppProfileManager *self, GAsyncReadyCallback callback, gpointer user_data) { common_enable_disable_unsolicited_events_3gpp_profile_manager (MM_BROADBAND_MODEM_QMI (self), TRUE, callback, user_data); } /*****************************************************************************/ /* Setup/cleanup unsolicited events (3gppProfileManager interface) */ static gboolean modem_3gpp_profile_manager_setup_cleanup_unsolicited_events_finish (MMIfaceModem3gppProfileManager *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void common_setup_cleanup_unsolicited_events_3gpp_profile_manager (MMBroadbandModemQmi *self, gboolean enable, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; QmiClient *client_pdc = NULL; QmiClient *client_wds = NULL; if (!mm_shared_qmi_ensure_client (MM_SHARED_QMI (self), QMI_SERVICE_PDC, &client_pdc, callback, user_data)) return; if (!mm_shared_qmi_ensure_client (MM_SHARED_QMI (self), QMI_SERVICE_WDS, &client_wds, callback, user_data)) return; task = g_task_new (self, NULL, callback, user_data); if (enable == self->priv->profile_manager_unsolicited_events_setup) { mm_obj_dbg (self, "profile manager unsolicited events already %s; skipping", enable ? "set up" : "cleaned up"); g_task_return_boolean (task, TRUE); g_object_unref (task); return; } self->priv->profile_manager_unsolicited_events_setup = enable; if (enable) { g_assert (self->priv->refresh_indication_id == 0); self->priv->refresh_indication_id = g_signal_connect (client_pdc, "refresh", G_CALLBACK (pdc_refresh_received), self); g_assert (self->priv->profile_changed_indication_id == 0); self->priv->profile_changed_indication_id = g_signal_connect (client_wds, "profile-changed", G_CALLBACK (profile_changed_indication_received), self); } else { g_assert (self->priv->refresh_indication_id != 0); g_signal_handler_disconnect (client_pdc, self->priv->refresh_indication_id); self->priv->refresh_indication_id = 0; g_assert (self->priv->profile_changed_indication_id != 0); g_signal_handler_disconnect (client_wds, self->priv->profile_changed_indication_id); self->priv->profile_changed_indication_id = 0; } mm_obj_dbg (self, "%s profile events handler", enable ? "set up" : "cleaned up"); g_task_return_boolean (task, TRUE); g_object_unref (task); } static void modem_3gpp_profile_manager_cleanup_unsolicited_events (MMIfaceModem3gppProfileManager *self, GAsyncReadyCallback callback, gpointer user_data) { common_setup_cleanup_unsolicited_events_3gpp_profile_manager (MM_BROADBAND_MODEM_QMI (self), FALSE, callback, user_data); } static void modem_3gpp_profile_manager_setup_unsolicited_events (MMIfaceModem3gppProfileManager *self, GAsyncReadyCallback callback, gpointer user_data) { common_setup_cleanup_unsolicited_events_3gpp_profile_manager (MM_BROADBAND_MODEM_QMI (self), TRUE, callback, user_data); } /*****************************************************************************/ /* Check support (Messaging interface) */ static gboolean messaging_check_support_finish (MMIfaceModemMessaging *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void parent_messaging_check_support_ready (MMIfaceModemMessaging *_self, GAsyncResult *res, GTask *task) { MMBroadbandModemQmi *self = MM_BROADBAND_MODEM_QMI (_self); self->priv->messaging_fallback_at_only = iface_modem_messaging_parent->check_support_finish (_self, res, NULL); g_task_return_boolean (task, self->priv->messaging_fallback_at_only); g_object_unref (task); } static void messaging_check_support (MMIfaceModemMessaging *self, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; task = g_task_new (self, NULL, callback, user_data); /* If we have support for the WMS client, messaging is supported */ if (!mm_shared_qmi_peek_client (MM_SHARED_QMI (self), QMI_SERVICE_WMS, MM_PORT_QMI_FLAG_DEFAULT, NULL)) { /* Try to fallback to AT support */ iface_modem_messaging_parent->check_support ( self, (GAsyncReadyCallback)parent_messaging_check_support_ready, task); return; } mm_obj_dbg (self, "messaging capabilities supported"); g_task_return_boolean (task, TRUE); g_object_unref (task); } /*****************************************************************************/ /* Load supported storages (Messaging interface) */ static gboolean messaging_load_supported_storages_finish (MMIfaceModemMessaging *_self, GAsyncResult *res, GArray **mem1, GArray **mem2, GArray **mem3, GError **error) { MMBroadbandModemQmi *self = MM_BROADBAND_MODEM_QMI (_self); MMSmsStorage supported; /* Handle AT URC only fallback */ if (self->priv->messaging_fallback_at_only) { return iface_modem_messaging_parent->load_supported_storages_finish (_self, res, mem1, mem2, mem3, error); } g_assert (g_task_propagate_boolean (G_TASK (res), NULL)); *mem1 = g_array_sized_new (FALSE, FALSE, sizeof (MMSmsStorage), 2); /* Add SM storage only if not CDMA-only */ if (!mm_iface_modem_is_cdma_only (MM_IFACE_MODEM (self))) { supported = MM_SMS_STORAGE_SM; g_array_append_val (*mem1, supported); } supported = MM_SMS_STORAGE_ME; g_array_append_val (*mem1, supported); *mem2 = g_array_ref (*mem1); *mem3 = g_array_ref (*mem1); return TRUE; } static void messaging_load_supported_storages (MMIfaceModemMessaging *_self, GAsyncReadyCallback callback, gpointer user_data) { MMBroadbandModemQmi *self = MM_BROADBAND_MODEM_QMI (_self); GTask *task; /* Handle AT URC only fallback */ if (self->priv->messaging_fallback_at_only) { iface_modem_messaging_parent->load_supported_storages (_self, callback, user_data); return; } task = g_task_new (self, NULL, callback, user_data); g_task_return_boolean (task, TRUE); g_object_unref (task); } /*****************************************************************************/ /* Setup SMS format (Messaging interface) */ static gboolean modem_messaging_setup_sms_format_finish (MMIfaceModemMessaging *_self, GAsyncResult *res, GError **error) { MMBroadbandModemQmi *self = MM_BROADBAND_MODEM_QMI (_self); /* Handle AT URC only fallback */ if (self->priv->messaging_fallback_at_only) { return iface_modem_messaging_parent->setup_sms_format_finish (_self, res, error); } return g_task_propagate_boolean (G_TASK (res), error); } static void modem_messaging_setup_sms_format (MMIfaceModemMessaging *_self, GAsyncReadyCallback callback, gpointer user_data) { MMBroadbandModemQmi *self = MM_BROADBAND_MODEM_QMI (_self); GTask *task; /* Handle AT URC only fallback */ if (self->priv->messaging_fallback_at_only) { return iface_modem_messaging_parent->setup_sms_format (_self, callback, user_data); } /* noop */ task = g_task_new (self, NULL, callback, user_data); g_task_return_boolean (task, TRUE); g_object_unref (task); } /*****************************************************************************/ /* Set default storage (Messaging interface) */ static gboolean messaging_set_default_storage_finish (MMIfaceModemMessaging *_self, GAsyncResult *res, GError **error) { MMBroadbandModemQmi *self = MM_BROADBAND_MODEM_QMI (_self); /* Handle AT URC only fallback */ if (self->priv->messaging_fallback_at_only) { return iface_modem_messaging_parent->set_default_storage_finish (_self, res, error); } return g_task_propagate_boolean (G_TASK (res), error);; } static void wms_set_routes_ready (QmiClientWms *client, GAsyncResult *res, GTask *task) { QmiMessageWmsSetRoutesOutput *output = NULL; GError *error = NULL; output = qmi_client_wms_set_routes_finish (client, res, &error); if (!output) { g_prefix_error (&error, "QMI operation failed: "); g_task_return_error (task, error); } else if (!qmi_message_wms_set_routes_output_get_result (output, &error)) { g_prefix_error (&error, "Couldn't set routes: "); g_task_return_error (task, error); } else g_task_return_boolean (task, TRUE); g_object_unref (task); if (output) qmi_message_wms_set_routes_output_unref (output); } static void messaging_set_default_storage (MMIfaceModemMessaging *_self, MMSmsStorage storage, GAsyncReadyCallback callback, gpointer user_data) { MMBroadbandModemQmi *self = MM_BROADBAND_MODEM_QMI (_self); QmiClient *client = NULL; QmiMessageWmsSetRoutesInput *input; GArray *routes_array; QmiMessageWmsSetRoutesInputRouteListElement route; /* Handle AT URC only fallback */ if (self->priv->messaging_fallback_at_only) { iface_modem_messaging_parent->set_default_storage (_self, storage, callback, user_data); return; } if (!mm_shared_qmi_ensure_client (MM_SHARED_QMI (self), QMI_SERVICE_WMS, &client, callback, user_data)) return; /* Build routes array and add it as input * Just worry about Class 0 and Class 1 messages for now */ input = qmi_message_wms_set_routes_input_new (); routes_array = g_array_sized_new (FALSE, FALSE, sizeof (route), 2); route.message_type = QMI_WMS_MESSAGE_TYPE_POINT_TO_POINT; route.message_class = QMI_WMS_MESSAGE_CLASS_0; route.storage = mm_sms_storage_to_qmi_storage_type (storage); route.receipt_action = QMI_WMS_RECEIPT_ACTION_STORE_AND_NOTIFY; g_array_append_val (routes_array, route); route.message_class = QMI_WMS_MESSAGE_CLASS_1; g_array_append_val (routes_array, route); qmi_message_wms_set_routes_input_set_route_list (input, routes_array, NULL); mm_obj_dbg (self, "setting default messaging routes..."); qmi_client_wms_set_routes (QMI_CLIENT_WMS (client), input, 5, NULL, (GAsyncReadyCallback)wms_set_routes_ready, g_task_new (self, NULL, callback, user_data)); qmi_message_wms_set_routes_input_unref (input); g_array_unref (routes_array); } /*****************************************************************************/ /* Load initial SMS parts */ typedef enum { LOAD_INITIAL_SMS_PARTS_STEP_FIRST, LOAD_INITIAL_SMS_PARTS_STEP_3GPP_FIRST, LOAD_INITIAL_SMS_PARTS_STEP_3GPP_LIST_ALL, LOAD_INITIAL_SMS_PARTS_STEP_3GPP_LIST_MT_READ, LOAD_INITIAL_SMS_PARTS_STEP_3GPP_LIST_MT_NOT_READ, LOAD_INITIAL_SMS_PARTS_STEP_3GPP_LIST_MO_SENT, LOAD_INITIAL_SMS_PARTS_STEP_3GPP_LIST_MO_NOT_SENT, LOAD_INITIAL_SMS_PARTS_STEP_3GPP_LAST, LOAD_INITIAL_SMS_PARTS_STEP_CDMA_FIRST, LOAD_INITIAL_SMS_PARTS_STEP_CDMA_LIST_ALL, LOAD_INITIAL_SMS_PARTS_STEP_CDMA_LIST_MT_READ, LOAD_INITIAL_SMS_PARTS_STEP_CDMA_LIST_MT_NOT_READ, LOAD_INITIAL_SMS_PARTS_STEP_CDMA_LIST_MO_SENT, LOAD_INITIAL_SMS_PARTS_STEP_CDMA_LIST_MO_NOT_SENT, LOAD_INITIAL_SMS_PARTS_STEP_CDMA_LAST, LOAD_INITIAL_SMS_PARTS_STEP_LAST } LoadInitialSmsPartsStep; typedef struct { QmiClientWms *client; MMSmsStorage storage; LoadInitialSmsPartsStep step; /* For each step */ GArray *message_array; guint i; } LoadInitialSmsPartsContext; static void load_initial_sms_parts_context_free (LoadInitialSmsPartsContext *ctx) { if (ctx->message_array) g_array_unref (ctx->message_array); g_object_unref (ctx->client); g_slice_free (LoadInitialSmsPartsContext, ctx); } static gboolean load_initial_sms_parts_finish (MMIfaceModemMessaging *_self, GAsyncResult *res, GError **error) { MMBroadbandModemQmi *self = MM_BROADBAND_MODEM_QMI (_self); /* Handle AT URC only fallback */ if (self->priv->messaging_fallback_at_only) { return iface_modem_messaging_parent->load_initial_sms_parts_finish (_self, res, error); } return g_task_propagate_boolean (G_TASK (res), error);; } static void read_next_sms_part (GTask *task); static void add_new_read_sms_part (MMIfaceModemMessaging *self, QmiWmsStorageType storage, guint32 index, QmiWmsMessageTagType tag, QmiWmsMessageFormat format, gboolean transfer_route, GArray *data) { MMSmsPart *part = NULL; GError *error = NULL; switch (format) { case QMI_WMS_MESSAGE_FORMAT_CDMA: part = mm_sms_part_cdma_new_from_binary_pdu (index, (guint8 *)data->data, data->len, self, &error); break; case QMI_WMS_MESSAGE_FORMAT_GSM_WCDMA_POINT_TO_POINT: case QMI_WMS_MESSAGE_FORMAT_GSM_WCDMA_BROADCAST: part = mm_sms_part_3gpp_new_from_binary_pdu (index, (guint8 *)data->data, data->len, self, transfer_route, &error); break; case QMI_WMS_MESSAGE_FORMAT_MWI: mm_obj_dbg (self, "don't know how to process 'message waiting indicator' messages"); break; default: mm_obj_dbg (self, "unhandled message format '%u'", format); break; } if (part) { mm_obj_dbg (self, "correctly parsed PDU (%d)", index); mm_iface_modem_messaging_take_part (self, part, mm_sms_state_from_qmi_message_tag (tag), mm_sms_storage_from_qmi_storage_type (storage)); } else if (error) { /* Don't treat the error as critical */ mm_obj_dbg (self, "error parsing PDU (%d): %s", index, error->message); g_error_free (error); } } static void wms_raw_read_ready (QmiClientWms *client, GAsyncResult *res, GTask *task) { MMBroadbandModemQmi *self; LoadInitialSmsPartsContext *ctx; QmiMessageWmsRawReadOutput *output = NULL; GError *error = NULL; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); /* Ignore errors, just keep on with the next messages */ output = qmi_client_wms_raw_read_finish (client, res, &error); if (!output) { mm_obj_dbg (self, "QMI operation failed: %s", error->message); g_error_free (error); } else if (!qmi_message_wms_raw_read_output_get_result (output, &error)) { mm_obj_dbg (self, "couldn't read raw message: %s", error->message); g_error_free (error); } else { QmiWmsMessageTagType tag; QmiWmsMessageFormat format; GArray *data; QmiMessageWmsListMessagesOutputMessageListElement *message; message = &g_array_index (ctx->message_array, QmiMessageWmsListMessagesOutputMessageListElement, ctx->i); qmi_message_wms_raw_read_output_get_raw_message_data ( output, &tag, &format, &data, NULL); add_new_read_sms_part (MM_IFACE_MODEM_MESSAGING (self), mm_sms_storage_to_qmi_storage_type (ctx->storage), message->memory_index, tag, format, FALSE, data); } if (output) qmi_message_wms_raw_read_output_unref (output); /* Keep on reading parts */ ctx->i++; read_next_sms_part (task); } static void load_initial_sms_parts_step (GTask *task); static void read_next_sms_part (GTask *task) { LoadInitialSmsPartsContext *ctx; QmiMessageWmsListMessagesOutputMessageListElement *message; QmiMessageWmsRawReadInput *input; ctx = g_task_get_task_data (task); if (ctx->i >= ctx->message_array->len || !ctx->message_array) { /* If we just listed all SMS, we're done. Otherwise go to next tag. */ if (ctx->step == LOAD_INITIAL_SMS_PARTS_STEP_3GPP_LIST_ALL) ctx->step = LOAD_INITIAL_SMS_PARTS_STEP_3GPP_LAST; else if (ctx->step == LOAD_INITIAL_SMS_PARTS_STEP_CDMA_LIST_ALL) ctx->step = LOAD_INITIAL_SMS_PARTS_STEP_CDMA_LAST; else ctx->step++; load_initial_sms_parts_step (task); return; } message = &g_array_index (ctx->message_array, QmiMessageWmsListMessagesOutputMessageListElement, ctx->i); input = qmi_message_wms_raw_read_input_new (); qmi_message_wms_raw_read_input_set_message_memory_storage_id ( input, mm_sms_storage_to_qmi_storage_type (ctx->storage), message->memory_index, NULL); /* set message mode */ if (ctx->step < LOAD_INITIAL_SMS_PARTS_STEP_3GPP_LAST) qmi_message_wms_raw_read_input_set_message_mode ( input, QMI_WMS_MESSAGE_MODE_GSM_WCDMA, NULL); else if (ctx->step < LOAD_INITIAL_SMS_PARTS_STEP_CDMA_LAST) qmi_message_wms_raw_read_input_set_message_mode ( input, QMI_WMS_MESSAGE_MODE_CDMA, NULL); else g_assert_not_reached (); qmi_client_wms_raw_read (QMI_CLIENT_WMS (ctx->client), input, 3, NULL, (GAsyncReadyCallback)wms_raw_read_ready, task); qmi_message_wms_raw_read_input_unref (input); } static void wms_list_messages_ready (QmiClientWms *client, GAsyncResult *res, GTask *task) { MMBroadbandModemQmi *self; LoadInitialSmsPartsContext *ctx; QmiMessageWmsListMessagesOutput *output = NULL; GError *error = NULL; GArray *message_array; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); output = qmi_client_wms_list_messages_finish (client, res, &error); if (!output) { g_prefix_error (&error, "QMI operation failed: "); g_task_return_error (task, error); g_object_unref (task); return; } if (!qmi_message_wms_list_messages_output_get_result (output, &error)) { /* Ignore error, keep on */ mm_obj_dbg (self, "couldn't read SMS messages: %s", error->message); g_error_free (error); ctx->step++; load_initial_sms_parts_step (task); qmi_message_wms_list_messages_output_unref (output); return; } qmi_message_wms_list_messages_output_get_message_list ( output, &message_array, NULL); /* Keep a reference to the array ourselves */ if (ctx->message_array) g_array_unref (ctx->message_array); ctx->message_array = g_array_ref (message_array); qmi_message_wms_list_messages_output_unref (output); /* Start reading parts */ ctx->i = 0; read_next_sms_part (task); } static void load_initial_sms_parts_step (GTask *task) { MMBroadbandModemQmi *self; LoadInitialSmsPartsContext *ctx; QmiMessageWmsListMessagesInput *input; gint mode = -1; gint tag_type = -1; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); switch (ctx->step) { case LOAD_INITIAL_SMS_PARTS_STEP_FIRST: ctx->step++; /* Fall through */ case LOAD_INITIAL_SMS_PARTS_STEP_3GPP_FIRST: /* If modem doesn't have 3GPP caps, skip 3GPP SMS */ if (!mm_iface_modem_is_3gpp (MM_IFACE_MODEM (self))) { ctx->step = LOAD_INITIAL_SMS_PARTS_STEP_3GPP_LAST; load_initial_sms_parts_step (task); return; } ctx->step++; /* Fall through */ case LOAD_INITIAL_SMS_PARTS_STEP_3GPP_LIST_ALL: mm_obj_dbg (self, "loading all 3GPP messages from storage '%s'...", mm_sms_storage_get_string (ctx->storage)); mode = QMI_WMS_MESSAGE_MODE_GSM_WCDMA; break; case LOAD_INITIAL_SMS_PARTS_STEP_3GPP_LIST_MT_READ: mm_obj_dbg (self, "loading 3GPP MT-read messages from storage '%s'...", mm_sms_storage_get_string (ctx->storage)); tag_type = QMI_WMS_MESSAGE_TAG_TYPE_MT_READ; mode = QMI_WMS_MESSAGE_MODE_GSM_WCDMA; break; case LOAD_INITIAL_SMS_PARTS_STEP_3GPP_LIST_MT_NOT_READ: mm_obj_dbg (self, "loading 3GPP MT-not-read messages from storage '%s'...", mm_sms_storage_get_string (ctx->storage)); tag_type = QMI_WMS_MESSAGE_TAG_TYPE_MT_NOT_READ; mode = QMI_WMS_MESSAGE_MODE_GSM_WCDMA; break; case LOAD_INITIAL_SMS_PARTS_STEP_3GPP_LIST_MO_SENT: mm_obj_dbg (self, "loading 3GPP MO-sent messages from storage '%s'...", mm_sms_storage_get_string (ctx->storage)); tag_type = QMI_WMS_MESSAGE_TAG_TYPE_MO_SENT; mode = QMI_WMS_MESSAGE_MODE_GSM_WCDMA; break; case LOAD_INITIAL_SMS_PARTS_STEP_3GPP_LIST_MO_NOT_SENT: mm_obj_dbg (self, "loading 3GPP MO-not-sent messages from storage '%s'...", mm_sms_storage_get_string (ctx->storage)); tag_type = QMI_WMS_MESSAGE_TAG_TYPE_MO_NOT_SENT; mode = QMI_WMS_MESSAGE_MODE_GSM_WCDMA; break; case LOAD_INITIAL_SMS_PARTS_STEP_3GPP_LAST: ctx->step++; /* Fall through */ case LOAD_INITIAL_SMS_PARTS_STEP_CDMA_FIRST: /* If modem doesn't have CDMA caps, skip CDMA SMS */ if (!mm_iface_modem_is_cdma (MM_IFACE_MODEM (self))) { ctx->step = LOAD_INITIAL_SMS_PARTS_STEP_CDMA_LAST; load_initial_sms_parts_step (task); return; } ctx->step++; /* Fall through */ case LOAD_INITIAL_SMS_PARTS_STEP_CDMA_LIST_ALL: mm_obj_dbg (self, "loading all CDMA messages from storage '%s'...", mm_sms_storage_get_string (ctx->storage)); mode = QMI_WMS_MESSAGE_MODE_CDMA; break; case LOAD_INITIAL_SMS_PARTS_STEP_CDMA_LIST_MT_READ: mm_obj_dbg (self, "loading CDMA MT-read messages from storage '%s'...", mm_sms_storage_get_string (ctx->storage)); tag_type = QMI_WMS_MESSAGE_TAG_TYPE_MT_READ; mode = QMI_WMS_MESSAGE_MODE_CDMA; break; case LOAD_INITIAL_SMS_PARTS_STEP_CDMA_LIST_MT_NOT_READ: mm_obj_dbg (self, "loading CDMA MT-not-read messages from storage '%s'...", mm_sms_storage_get_string (ctx->storage)); tag_type = QMI_WMS_MESSAGE_TAG_TYPE_MT_NOT_READ; mode = QMI_WMS_MESSAGE_MODE_CDMA; break; case LOAD_INITIAL_SMS_PARTS_STEP_CDMA_LIST_MO_SENT: mm_obj_dbg (self, "loading CDMA MO-sent messages from storage '%s'...", mm_sms_storage_get_string (ctx->storage)); tag_type = QMI_WMS_MESSAGE_TAG_TYPE_MO_SENT; mode = QMI_WMS_MESSAGE_MODE_CDMA; break; case LOAD_INITIAL_SMS_PARTS_STEP_CDMA_LIST_MO_NOT_SENT: mm_obj_dbg (self, "loading CDMA MO-not-sent messages from storage '%s'...", mm_sms_storage_get_string (ctx->storage)); tag_type = QMI_WMS_MESSAGE_TAG_TYPE_MO_NOT_SENT; mode = QMI_WMS_MESSAGE_MODE_CDMA; break; case LOAD_INITIAL_SMS_PARTS_STEP_CDMA_LAST: ctx->step++; /* Fall through */ case LOAD_INITIAL_SMS_PARTS_STEP_LAST: /* All steps done */ g_task_return_boolean (task, TRUE); g_object_unref (task); return; default: g_assert_not_reached (); } g_assert (mode != -1); input = qmi_message_wms_list_messages_input_new (); qmi_message_wms_list_messages_input_set_storage_type ( input, mm_sms_storage_to_qmi_storage_type (ctx->storage), NULL); qmi_message_wms_list_messages_input_set_message_mode ( input, (QmiWmsMessageMode)mode, NULL); if (tag_type != -1) qmi_message_wms_list_messages_input_set_message_tag ( input, (QmiWmsMessageTagType)tag_type, NULL); qmi_client_wms_list_messages (QMI_CLIENT_WMS (ctx->client), input, 5, NULL, (GAsyncReadyCallback)wms_list_messages_ready, task); qmi_message_wms_list_messages_input_unref (input); } static void load_initial_sms_parts (MMIfaceModemMessaging *_self, MMSmsStorage storage, GAsyncReadyCallback callback, gpointer user_data) { MMBroadbandModemQmi *self = MM_BROADBAND_MODEM_QMI (_self); LoadInitialSmsPartsContext *ctx; GTask *task; QmiClient *client = NULL; /* Handle AT URC only fallback */ if (self->priv->messaging_fallback_at_only) { return iface_modem_messaging_parent->load_initial_sms_parts (_self, storage, callback, user_data); } if (!mm_shared_qmi_ensure_client (MM_SHARED_QMI (self), QMI_SERVICE_WMS, &client, callback, user_data)) return; ctx = g_slice_new0 (LoadInitialSmsPartsContext); ctx->client = QMI_CLIENT_WMS (g_object_ref (client)); ctx->storage = storage; ctx->step = LOAD_INITIAL_SMS_PARTS_STEP_FIRST; task = g_task_new (self, NULL, callback, user_data); g_task_set_task_data (task, ctx, (GDestroyNotify)load_initial_sms_parts_context_free); load_initial_sms_parts_step (task); } /*****************************************************************************/ /* Common setup/cleanup unsolicited event handlers (Messaging interface) */ typedef struct { MMBroadbandModemQmi *self; QmiClientWms *client; QmiWmsStorageType storage; guint32 memory_index; QmiWmsMessageMode message_mode; } IndicationRawReadContext; static void indication_raw_read_context_free (IndicationRawReadContext *ctx) { g_object_unref (ctx->client); g_object_unref (ctx->self); g_slice_free (IndicationRawReadContext, ctx); } static void wms_indication_raw_read_ready (QmiClientWms *client, GAsyncResult *res, IndicationRawReadContext *ctx) { QmiMessageWmsRawReadOutput *output = NULL; GError *error = NULL; /* Ignore errors */ output = qmi_client_wms_raw_read_finish (client, res, &error); if (!output) { mm_obj_dbg (ctx->self, "QMI operation failed: %s", error->message); g_error_free (error); } else if (!qmi_message_wms_raw_read_output_get_result (output, &error)) { mm_obj_dbg (ctx->self, "couldn't read raw message: %s", error->message); g_error_free (error); } else { QmiWmsMessageTagType tag; QmiWmsMessageFormat format; GArray *data; qmi_message_wms_raw_read_output_get_raw_message_data ( output, &tag, &format, &data, NULL); add_new_read_sms_part (MM_IFACE_MODEM_MESSAGING (ctx->self), ctx->storage, ctx->memory_index, tag, format, FALSE, data); } if (output) qmi_message_wms_raw_read_output_unref (output); indication_raw_read_context_free (ctx); } static void wms_send_ack_ready (QmiClientWms *client, GAsyncResult *res, MMBroadbandModemQmi *self) { g_autoptr(QmiMessageWmsSendAckOutput) output = NULL; g_autoptr(GError) error= NULL; output = qmi_client_wms_send_ack_finish (client, res, &error); if (!output) { mm_obj_dbg (self, "QMI operation failed: '%s'", error->message); } g_object_unref (self); } static void messaging_event_report_indication_cb (QmiClientNas *client, QmiIndicationWmsEventReportOutput *output, MMBroadbandModemQmi *self) { QmiWmsStorageType storage; guint32 memory_index; QmiWmsAckIndicator ack_ind; guint32 transaction_id; QmiWmsMessageFormat msg_format; QmiWmsMessageTagType tag; GArray *raw_data = NULL; /* Handle transfer-route MT messages */ if (qmi_indication_wms_event_report_output_get_transfer_route_mt_message ( output, &ack_ind, &transaction_id, &msg_format, &raw_data, NULL)) { mm_obj_dbg (self, "Got transfer-route MT message"); /* If this is the first of a multi-part message, send an ACK to get the * second part */ if (ack_ind == QMI_WMS_ACK_INDICATOR_SEND) { g_autoptr(QmiMessageWmsSendAckInput) ack_input = NULL; QmiWmsMessageProtocol message_protocol; /* Need to ack message */ mm_obj_dbg (self, "Need to ACK indicator"); switch (msg_format) { case QMI_WMS_MESSAGE_FORMAT_CDMA: message_protocol = QMI_WMS_MESSAGE_PROTOCOL_CDMA; break; case QMI_WMS_MESSAGE_FORMAT_MWI: case QMI_WMS_MESSAGE_FORMAT_GSM_WCDMA_POINT_TO_POINT: case QMI_WMS_MESSAGE_FORMAT_GSM_WCDMA_BROADCAST: default: message_protocol = QMI_WMS_MESSAGE_PROTOCOL_WCDMA; break; } ack_input = qmi_message_wms_send_ack_input_new(); qmi_message_wms_send_ack_input_set_information (ack_input, transaction_id, message_protocol, TRUE, NULL); qmi_client_wms_send_ack (QMI_CLIENT_WMS (client), ack_input, MM_BASE_SMS_DEFAULT_SEND_TIMEOUT, NULL, (GAsyncReadyCallback)wms_send_ack_ready, g_object_ref (self)); } /* Defaults for transfer-route messages, which are not stored anywhere */ storage = QMI_WMS_STORAGE_TYPE_NONE; memory_index = 0; tag = QMI_WMS_MESSAGE_TAG_TYPE_MT_NOT_READ; add_new_read_sms_part (MM_IFACE_MODEM_MESSAGING (self), storage, memory_index, tag, msg_format, TRUE, raw_data); return; } if (qmi_indication_wms_event_report_output_get_mt_message ( output, &storage, &memory_index, NULL)) { IndicationRawReadContext *ctx; QmiMessageWmsRawReadInput *input; ctx = g_slice_new (IndicationRawReadContext); ctx->self = g_object_ref (self); ctx->client = QMI_CLIENT_WMS (g_object_ref (client)); ctx->storage = storage; ctx->memory_index = memory_index; input = qmi_message_wms_raw_read_input_new (); qmi_message_wms_raw_read_input_set_message_memory_storage_id ( input, storage, memory_index, NULL); /* Default to 3GPP message mode if none given */ if (!qmi_indication_wms_event_report_output_get_message_mode ( output, &ctx->message_mode, NULL)) ctx->message_mode = QMI_WMS_MESSAGE_MODE_GSM_WCDMA; qmi_message_wms_raw_read_input_set_message_mode ( input, ctx->message_mode, NULL); qmi_client_wms_raw_read (QMI_CLIENT_WMS (client), input, 3, NULL, (GAsyncReadyCallback)wms_indication_raw_read_ready, ctx); qmi_message_wms_raw_read_input_unref (input); } } static gboolean common_setup_cleanup_messaging_unsolicited_events (MMBroadbandModemQmi *self, gboolean enable, GError **error) { QmiClient *client = NULL; client = mm_shared_qmi_peek_client (MM_SHARED_QMI (self), QMI_SERVICE_WMS, MM_PORT_QMI_FLAG_DEFAULT, error); if (!client) return FALSE; if (enable == self->priv->messaging_unsolicited_events_setup) { mm_obj_dbg (self, "messaging unsolicited events already %s; skipping", enable ? "setup" : "cleanup"); return TRUE; } /* Store new state */ self->priv->messaging_unsolicited_events_setup = enable; /* Connect/Disconnect "Event Report" indications */ if (enable) { g_assert (self->priv->messaging_event_report_indication_id == 0); self->priv->messaging_event_report_indication_id = g_signal_connect (client, "event-report", G_CALLBACK (messaging_event_report_indication_cb), self); } else { g_assert (self->priv->messaging_event_report_indication_id != 0); g_signal_handler_disconnect (client, self->priv->messaging_event_report_indication_id); self->priv->messaging_event_report_indication_id = 0; } return TRUE; } /*****************************************************************************/ /* Cleanup unsolicited event handlers (Messaging interface) */ static gboolean messaging_cleanup_unsolicited_events_finish (MMIfaceModemMessaging *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void parent_messaging_cleanup_unsolicited_events_ready (MMIfaceModemMessaging *_self, GAsyncResult *res, GTask *task) { MMBroadbandModemQmi *self = MM_BROADBAND_MODEM_QMI (_self); GError *error = NULL; if (!iface_modem_messaging_parent->cleanup_unsolicited_events_finish (_self, res, &error)) { if (self->priv->messaging_fallback_at_only) { g_task_return_error (task, error); g_object_unref (task); return; } mm_obj_dbg (self, "cleaning up parent messaging unsolicited events failed: %s", error->message); g_clear_error (&error); } /* handle AT URC only fallback */ if (self->priv->messaging_fallback_at_only) { g_task_return_boolean (task, TRUE); g_object_unref (task); return; } /* Disable QMI indications */ if (!common_setup_cleanup_messaging_unsolicited_events (self, FALSE, &error)) g_task_return_error (task, error); else g_task_return_boolean (task, TRUE); g_object_unref (task); } static void messaging_cleanup_unsolicited_events (MMIfaceModemMessaging *self, GAsyncReadyCallback callback, gpointer user_data) { /* Disable AT URCs parent and chain QMI indications disabling */ iface_modem_messaging_parent->cleanup_unsolicited_events ( self, (GAsyncReadyCallback)parent_messaging_cleanup_unsolicited_events_ready, g_task_new (self, NULL, callback, user_data)); } /*****************************************************************************/ /* Setup unsolicited event handlers (Messaging interface) */ static gboolean messaging_setup_unsolicited_events_finish (MMIfaceModemMessaging *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void parent_messaging_setup_unsolicited_events_ready (MMIfaceModemMessaging *_self, GAsyncResult *res, GTask *task) { MMBroadbandModemQmi *self = MM_BROADBAND_MODEM_QMI (_self); GError *error = NULL; if (!iface_modem_messaging_parent->setup_unsolicited_events_finish (_self, res, &error)) { if (self->priv->messaging_fallback_at_only) { g_task_return_error (task, error); g_object_unref (task); return; } mm_obj_dbg (self, "setting up parent messaging unsolicited events failed: %s", error->message); g_clear_error (&error); } /* handle AT URC only fallback */ if (self->priv->messaging_fallback_at_only) { g_task_return_boolean (task, TRUE); g_object_unref (task); return; } /* Enable QMI indications */ if (!common_setup_cleanup_messaging_unsolicited_events (self, TRUE, &error)) g_task_return_error (task, error); else g_task_return_boolean (task, TRUE); g_object_unref (task); } static void messaging_setup_unsolicited_events (MMIfaceModemMessaging *self, GAsyncReadyCallback callback, gpointer user_data) { /* Enable AT URCs parent and chain QMI indication enabling */ iface_modem_messaging_parent->setup_unsolicited_events ( self, (GAsyncReadyCallback)parent_messaging_setup_unsolicited_events_ready, g_task_new (self, NULL, callback, user_data)); } /*****************************************************************************/ /* Enable/Disable unsolicited events (Messaging interface) */ typedef struct { gboolean enable; } EnableMessagingUnsolicitedEventsContext; static gboolean messaging_disable_unsolicited_events_finish (MMIfaceModemMessaging *_self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static gboolean messaging_enable_unsolicited_events_finish (MMIfaceModemMessaging *_self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void ser_messaging_indicator_ready (QmiClientWms *client, GAsyncResult *res, GTask *task) { MMBroadbandModemQmi *self; EnableMessagingUnsolicitedEventsContext *ctx; QmiMessageWmsSetEventReportOutput *output = NULL; GError *error = NULL; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); output = qmi_client_wms_set_event_report_finish (client, res, &error); if (!output) { mm_obj_dbg (self, "QMI operation failed: '%s'", error->message); g_error_free (error); } else if (!qmi_message_wms_set_event_report_output_get_result (output, &error)) { mm_obj_dbg (self, "couldn't set event report: '%s'", error->message); g_error_free (error); } if (output) qmi_message_wms_set_event_report_output_unref (output); /* Just ignore errors for now */ self->priv->messaging_unsolicited_events_enabled = ctx->enable; g_task_return_boolean (task, TRUE); g_object_unref (task); } static void common_enable_disable_messaging_unsolicited_events (MMBroadbandModemQmi *self, gboolean enable, GTask *task) { EnableMessagingUnsolicitedEventsContext *ctx; QmiClient *client = NULL; QmiMessageWmsSetEventReportInput *input; GError *error = NULL; client = mm_shared_qmi_peek_client (MM_SHARED_QMI (self), QMI_SERVICE_WMS, MM_PORT_QMI_FLAG_DEFAULT, &error); if (!client) { g_task_return_error (task, error); g_object_unref (task); return; } if (enable == self->priv->messaging_unsolicited_events_enabled) { mm_obj_dbg (self, "messaging unsolicited events already %s; skipping", enable ? "enabled" : "disabled"); g_task_return_boolean (task, TRUE); g_object_unref (task); return; } ctx = g_new (EnableMessagingUnsolicitedEventsContext, 1); ctx->enable = enable; g_task_set_task_data (task, ctx, g_free); input = qmi_message_wms_set_event_report_input_new (); qmi_message_wms_set_event_report_input_set_new_mt_message_indicator ( input, ctx->enable, NULL); qmi_client_wms_set_event_report ( QMI_CLIENT_WMS (client), input, 5, NULL, (GAsyncReadyCallback)ser_messaging_indicator_ready, task); qmi_message_wms_set_event_report_input_unref (input); } static void parent_messaging_disable_unsolicited_events_ready (MMIfaceModemMessaging *_self, GAsyncResult *res, GTask *task) { MMBroadbandModemQmi *self = MM_BROADBAND_MODEM_QMI (_self); GError *error = NULL; if (!iface_modem_messaging_parent->disable_unsolicited_events_finish (_self, res, &error)) { if (self->priv->messaging_fallback_at_only) { g_task_return_error (task, error); g_object_unref (task); return; } mm_obj_dbg (self, "disabling parent messaging unsolicited events failed: %s", error->message); g_clear_error (&error); } /* handle AT URC only fallback */ if (self->priv->messaging_fallback_at_only) { g_task_return_boolean (task, TRUE); g_object_unref (task); return; } /* Disable QMI indications */ common_enable_disable_messaging_unsolicited_events (self, FALSE, task); } static void messaging_disable_unsolicited_events (MMIfaceModemMessaging *_self, GAsyncReadyCallback callback, gpointer user_data) { MMBroadbandModemQmi *self = MM_BROADBAND_MODEM_QMI (_self); GTask *task; task = g_task_new (self, NULL, callback, user_data); /* Generic implementation doesn't actually have a method to disable * unsolicited messaging events */ if (iface_modem_messaging_parent->disable_unsolicited_events) { /* Disable AT URCs parent and chain QMI indication disabling */ iface_modem_messaging_parent->disable_unsolicited_events ( _self, (GAsyncReadyCallback)parent_messaging_disable_unsolicited_events_ready, task); return; } /* handle AT URC only fallback */ if (self->priv->messaging_fallback_at_only) { g_task_return_boolean (task, TRUE); g_object_unref (task); return; } /* Disable QMI indications */ common_enable_disable_messaging_unsolicited_events (self, FALSE, task); } static void parent_messaging_enable_unsolicited_events_ready (MMIfaceModemMessaging *_self, GAsyncResult *res, GTask *task) { MMBroadbandModemQmi *self = MM_BROADBAND_MODEM_QMI (_self); GError *error = NULL; if (!iface_modem_messaging_parent->enable_unsolicited_events_finish (_self, res, &error)) { if (self->priv->messaging_fallback_at_only) { g_task_return_error (task, error); g_object_unref (task); return; } mm_obj_dbg (self, "enabling parent messaging unsolicited events failed: %s", error->message); g_clear_error (&error); } /* handle AT URC only fallback */ if (self->priv->messaging_fallback_at_only) { g_task_return_boolean (task, TRUE); g_object_unref (task); return; } /* Enable QMI indications */ common_enable_disable_messaging_unsolicited_events (self, TRUE, task); } static void messaging_enable_unsolicited_events (MMIfaceModemMessaging *_self, GAsyncReadyCallback callback, gpointer user_data) { MMBroadbandModemQmi *self = MM_BROADBAND_MODEM_QMI (_self); GTask *task; task = g_task_new (self, NULL, callback, user_data); /* Enable AT URCs parent and chain QMI indication enabling */ iface_modem_messaging_parent->enable_unsolicited_events ( _self, (GAsyncReadyCallback)parent_messaging_enable_unsolicited_events_ready, task); } /*****************************************************************************/ /* Create SMS (Messaging interface) */ static MMBaseSms * messaging_create_sms (MMIfaceModemMessaging *_self) { MMBroadbandModemQmi *self = MM_BROADBAND_MODEM_QMI (_self); /* Handle AT URC only fallback */ if (self->priv->messaging_fallback_at_only) { return iface_modem_messaging_parent->create_sms (_self); } return mm_sms_qmi_new (MM_BASE_MODEM (self)); } /*****************************************************************************/ /* Check support (SAR interface) */ /* SAR level 0 is assumed DISABLED, and any other level is assumed ENABLED */ #define QMI_SAR_ENABLE_POWER_INDEX QMI_SAR_RF_STATE_1 #define QMI_SAR_DISABLED_POWER_INDEX QMI_SAR_RF_STATE_0 static gboolean sar_check_support_finish (MMIfaceModemSar *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void sar_check_support (MMIfaceModemSar *self, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; task = g_task_new (self, NULL, callback, user_data); /* If SAR service is available, assume sar state is OS */ if (!mm_shared_qmi_peek_client (MM_SHARED_QMI (self), QMI_SERVICE_SAR, MM_PORT_QMI_FLAG_DEFAULT, NULL)) { mm_obj_dbg (self, "SAR capabilities not supported"); g_task_return_boolean (task, FALSE); } else { mm_obj_dbg (self, "SAR capabilities supported"); g_task_return_boolean (task, TRUE); } g_object_unref (task); } /*****************************************************************************/ /* Load SAR state (SAR interface) */ static gboolean sar_load_state_finish (MMIfaceModemSar *self, GAsyncResult *res, gboolean *out_state, GError **error) { GError *inner_error = NULL; gboolean result; result = g_task_propagate_boolean (G_TASK (res), &inner_error); if (inner_error) { g_propagate_error (error, inner_error); return FALSE; } if (out_state) *out_state = result; return TRUE; } static void sar_load_state_ready (QmiClientSar *client, GAsyncResult *res, GTask *task) { g_autoptr(QmiMessageSarRfGetStateOutput) output = NULL; GError *error = NULL; QmiSarRfState rf_state; output = qmi_client_sar_rf_get_state_finish (client, res, &error); if (output && qmi_message_sar_rf_get_state_output_get_result (output, &error) && qmi_message_sar_rf_get_state_output_get_state (output, &rf_state, &error)) { if (rf_state == QMI_SAR_DISABLED_POWER_INDEX) g_task_return_boolean (task, FALSE); else g_task_return_boolean (task, TRUE); } else g_task_return_error (task, error); g_object_unref (task); } static void sar_load_state (MMIfaceModemSar *self, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; QmiClient *client = NULL; if (!mm_shared_qmi_ensure_client (MM_SHARED_QMI (self), QMI_SERVICE_SAR, &client, callback, user_data)) return; task = g_task_new (self, NULL, callback, user_data); qmi_client_sar_rf_get_state ( QMI_CLIENT_SAR (client), NULL, 5, NULL, (GAsyncReadyCallback)sar_load_state_ready, task); } /*****************************************************************************/ /* Load SAR power level (SAR interface) */ static gboolean sar_load_power_level_finish (MMIfaceModemSar *self, GAsyncResult *res, guint *out_power_level, GError **error) { gssize result; result = g_task_propagate_int (G_TASK (res), error); if (result < 0) return FALSE; if (out_power_level) *out_power_level = (guint) result; return TRUE; } static void sar_load_power_level_ready (QmiClientSar *client, GAsyncResult *res, GTask *task) { g_autoptr(QmiMessageSarRfGetStateOutput) output = NULL; GError *error = NULL; QmiSarRfState rf_state; output = qmi_client_sar_rf_get_state_finish (client, res, &error); if (output && qmi_message_sar_rf_get_state_output_get_result (output, &error) && qmi_message_sar_rf_get_state_output_get_state (output, &rf_state, &error)) g_task_return_int (task, rf_state); else g_task_return_error (task, error); g_object_unref (task); } static void sar_load_power_level (MMIfaceModemSar *self, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; QmiClient *client = NULL; if (!mm_shared_qmi_ensure_client (MM_SHARED_QMI (self), QMI_SERVICE_SAR, &client, callback, user_data)) return; task = g_task_new (self, NULL, callback, user_data); qmi_client_sar_rf_get_state ( QMI_CLIENT_SAR (client), NULL, 5, NULL, (GAsyncReadyCallback)sar_load_power_level_ready, task); } /*****************************************************************************/ /* Enable/Disable SAR (SAR interface) */ static gboolean sar_enable_finish (MMIfaceModemSar *self, GAsyncResult *res, guint *out_sar_power_level, GError **error) { QmiSarRfState level; if (!g_task_propagate_boolean (G_TASK (res), error)) return FALSE; level = GPOINTER_TO_UINT (g_task_get_task_data (G_TASK (res))); if (out_sar_power_level) *out_sar_power_level = level; return TRUE; } static void sar_enable_ready (QmiClientSar *client, GAsyncResult *res, GTask *task) { g_autoptr(QmiMessageSarRfSetStateOutput) output = NULL; GError *error = NULL; output = qmi_client_sar_rf_set_state_finish (client, res, &error); if (output && qmi_message_sar_rf_set_state_output_get_result (output, &error)) g_task_return_boolean (task, TRUE); else g_task_return_error (task, error); g_object_unref (task); } static void sar_enable (MMIfaceModemSar *self, gboolean enable, GAsyncReadyCallback callback, gpointer user_data) { g_autoptr(QmiMessageSarRfSetStateInput) input = NULL; GTask *task; QmiClient *client = NULL; QmiSarRfState level; if (!mm_shared_qmi_ensure_client (MM_SHARED_QMI (self), QMI_SERVICE_SAR, &client, callback, user_data)) return; task = g_task_new (self, NULL, callback, user_data); input = qmi_message_sar_rf_set_state_input_new (); /* When enabling, try to set the last valid known power level used, instead * of defaulting to level 1 */ if (enable) { level = mm_iface_modem_sar_get_power_level (self); if (level == QMI_SAR_DISABLED_POWER_INDEX) level = QMI_SAR_ENABLE_POWER_INDEX; } else level = QMI_SAR_DISABLED_POWER_INDEX; qmi_message_sar_rf_set_state_input_set_state (input, level, NULL); g_task_set_task_data (task, GUINT_TO_POINTER (level), NULL); qmi_client_sar_rf_set_state ( QMI_CLIENT_SAR (client), input, 5, NULL, (GAsyncReadyCallback)sar_enable_ready, task); } /*****************************************************************************/ /* Set SAR power level (SAR interface) */ static gboolean sar_set_power_level_finish (MMIfaceModemSar *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void sar_set_power_level_ready (QmiClientSar *client, GAsyncResult *res, GTask *task) { g_autoptr(QmiMessageSarRfSetStateOutput) output = NULL; GError *error = NULL; output = qmi_client_sar_rf_set_state_finish (client, res, &error); if (output && qmi_message_sar_rf_set_state_output_get_result (output, &error)) g_task_return_boolean (task, TRUE); else g_task_return_error (task, error); g_object_unref (task); } static void sar_set_power_level (MMIfaceModemSar *self, guint power_level, GAsyncReadyCallback callback, gpointer user_data) { g_autoptr(QmiMessageSarRfSetStateInput) input = NULL; GTask *task; QmiClient *client = NULL; if (!mm_shared_qmi_ensure_client (MM_SHARED_QMI (self), QMI_SERVICE_SAR, &client, callback, user_data)) return; task = g_task_new (self, NULL, callback, user_data); if (power_level == QMI_SAR_DISABLED_POWER_INDEX) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS, "Unsupported power level"); g_object_unref (task); return; } input = qmi_message_sar_rf_set_state_input_new (); qmi_message_sar_rf_set_state_input_set_state (input, power_level, NULL); qmi_client_sar_rf_set_state ( QMI_CLIENT_SAR (client), input, 5, NULL, (GAsyncReadyCallback)sar_set_power_level_ready, task); } /*****************************************************************************/ /* Location capabilities loading (Location interface) */ static MMModemLocationSource location_load_capabilities_finish (MMIfaceModemLocation *self, GAsyncResult *res, GError **error) { GError *inner_error = NULL; gssize value; value = g_task_propagate_int (G_TASK (res), &inner_error); if (inner_error) { g_propagate_error (error, inner_error); return MM_MODEM_LOCATION_SOURCE_NONE; } return (MMModemLocationSource)value; } static void shared_qmi_location_load_capabilities_ready (MMIfaceModemLocation *self, GAsyncResult *res, GTask *task) { MMModemLocationSource sources; GError *error = NULL; sources = mm_shared_qmi_location_load_capabilities_finish (self, res, &error); if (error) { g_task_return_error (task, error); g_object_unref (task); return; } /* If the modem is CDMA, we have support for CDMA BS location */ if (mm_iface_modem_is_cdma (MM_IFACE_MODEM (self))) sources |= MM_MODEM_LOCATION_SOURCE_CDMA_BS; /* If the modem is 3GPP, we have support for 3GPP LAC/CI location */ if (mm_iface_modem_is_3gpp (MM_IFACE_MODEM (self))) sources |= MM_MODEM_LOCATION_SOURCE_3GPP_LAC_CI; /* So we're done, complete */ g_task_return_int (task, sources); g_object_unref (task); } static void location_load_capabilities (MMIfaceModemLocation *self, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; task = g_task_new (self, NULL, callback, user_data); /* Chain up shared QMI setup, which takes care of running the PARENT * setup as well as processing GPS-related checks. */ mm_shared_qmi_location_load_capabilities ( self, (GAsyncReadyCallback)shared_qmi_location_load_capabilities_ready, task); } /*****************************************************************************/ /* Disable location gathering (Location interface) */ static gboolean disable_location_gathering_finish (MMIfaceModemLocation *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void shared_qmi_disable_location_gathering_ready (MMIfaceModemLocation *self, GAsyncResult *res, GTask *task) { GError *error = NULL; if (!mm_shared_qmi_disable_location_gathering_finish (self, res, &error)) g_task_return_error (task, error); else g_task_return_boolean (task, TRUE); g_object_unref (task); } static void disable_location_gathering (MMIfaceModemLocation *_self, MMModemLocationSource source, GAsyncReadyCallback callback, gpointer user_data) { MMBroadbandModemQmi *self = MM_BROADBAND_MODEM_QMI (_self); GTask *task; task = g_task_new (self, NULL, callback, user_data); /* Nothing to be done to disable 3GPP or CDMA locations */ if (source == MM_MODEM_LOCATION_SOURCE_3GPP_LAC_CI || source == MM_MODEM_LOCATION_SOURCE_CDMA_BS) self->priv->enabled_sources &= ~source; mm_shared_qmi_disable_location_gathering ( _self, source, (GAsyncReadyCallback) shared_qmi_disable_location_gathering_ready, task); } /*****************************************************************************/ /* Enable location gathering (Location interface) */ static gboolean enable_location_gathering_finish (MMIfaceModemLocation *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void shared_qmi_enable_location_gathering_ready (MMIfaceModemLocation *_self, GAsyncResult *res, GTask *task) { MMBroadbandModemQmi *self = MM_BROADBAND_MODEM_QMI (_self); MMModemLocationSource source; GError *error = NULL; if (!mm_shared_qmi_enable_location_gathering_finish (_self, res, &error)) { g_task_return_error (task, error); g_object_unref (task); return; } source = (MMModemLocationSource) GPOINTER_TO_UINT (g_task_get_task_data (task)); /* Nothing else needed in the QMI side for LAC/CI */ if (source == MM_MODEM_LOCATION_SOURCE_3GPP_LAC_CI && mm_iface_modem_is_3gpp (MM_IFACE_MODEM (self))) { self->priv->enabled_sources |= source; g_task_return_boolean (task, TRUE); g_object_unref (task); return; } /* CDMA modems need to re-run registration checks when enabling the CDMA BS * location source, so that we get up to date BS location information. * Note that we don't care for when the registration checks get finished. */ if (source == MM_MODEM_LOCATION_SOURCE_CDMA_BS && mm_iface_modem_is_cdma (MM_IFACE_MODEM (self))) { /* Reload registration to get LAC/CI */ mm_iface_modem_cdma_run_registration_checks (MM_IFACE_MODEM_CDMA (self), NULL, NULL); /* Just mark it as enabled */ self->priv->enabled_sources |= source; g_task_return_boolean (task, TRUE); g_object_unref (task); return; } /* Otherwise, we're done */ g_task_return_boolean (task, TRUE); g_object_unref (task); } static void enable_location_gathering (MMIfaceModemLocation *self, MMModemLocationSource source, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; task = g_task_new (self, NULL, callback, user_data); g_task_set_task_data (task, GUINT_TO_POINTER (source), NULL); mm_shared_qmi_enable_location_gathering ( self, source, (GAsyncReadyCallback)shared_qmi_enable_location_gathering_ready, task); } /*****************************************************************************/ /* Check support (OMA interface) */ static gboolean oma_check_support_finish (MMIfaceModemOma *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void oma_check_support (MMIfaceModemOma *self, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; task = g_task_new (self, NULL, callback, user_data); /* If we have support for the OMA client, OMA is supported */ if (!mm_shared_qmi_peek_client (MM_SHARED_QMI (self), QMI_SERVICE_OMA, MM_PORT_QMI_FLAG_DEFAULT, NULL)) { mm_obj_dbg (self, "OMA capabilities not supported"); g_task_return_boolean (task, FALSE); } else { mm_obj_dbg (self, "OMA capabilities supported"); g_task_return_boolean (task, TRUE); } g_object_unref (task); } /*****************************************************************************/ /* Load features (OMA interface) */ static MMOmaFeature oma_load_features_finish (MMIfaceModemOma *self, GAsyncResult *res, GError **error) { GError *inner_error = NULL; gssize value; value = g_task_propagate_int (G_TASK (res), &inner_error); if (inner_error) { g_propagate_error (error, inner_error); return MM_OMA_FEATURE_NONE; } return (MMOmaFeature)value; } static void oma_get_feature_setting_ready (QmiClientOma *client, GAsyncResult *res, GTask *task) { QmiMessageOmaGetFeatureSettingOutput *output = NULL; GError *error = NULL; output = qmi_client_oma_get_feature_setting_finish (client, res, &error); if (!output || !qmi_message_oma_get_feature_setting_output_get_result (output, &error)) g_task_return_error (task, error); else { MMOmaFeature features = MM_OMA_FEATURE_NONE; gboolean enabled; if (qmi_message_oma_get_feature_setting_output_get_device_provisioning_service_update_config ( output, &enabled, NULL) && enabled) features |= MM_OMA_FEATURE_DEVICE_PROVISIONING; if (qmi_message_oma_get_feature_setting_output_get_prl_update_service_config ( output, &enabled, NULL) && enabled) features |= MM_OMA_FEATURE_PRL_UPDATE; if (qmi_message_oma_get_feature_setting_output_get_hfa_feature_config ( output, &enabled, NULL) && enabled) features |= MM_OMA_FEATURE_HANDS_FREE_ACTIVATION; g_task_return_int (task, features); } if (output) qmi_message_oma_get_feature_setting_output_unref (output); g_object_unref (task); } static void oma_load_features (MMIfaceModemOma *self, GAsyncReadyCallback callback, gpointer user_data) { QmiClient *client = NULL; if (!mm_shared_qmi_ensure_client (MM_SHARED_QMI (self), QMI_SERVICE_OMA, &client, callback, user_data)) return; qmi_client_oma_get_feature_setting ( QMI_CLIENT_OMA (client), NULL, 5, NULL, (GAsyncReadyCallback)oma_get_feature_setting_ready, g_task_new (self, NULL, callback, user_data)); } /*****************************************************************************/ /* Setup (OMA interface) */ static gboolean oma_setup_finish (MMIfaceModemOma *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void oma_set_feature_setting_ready (QmiClientOma *client, GAsyncResult *res, GTask *task) { QmiMessageOmaSetFeatureSettingOutput *output; GError *error = NULL; output = qmi_client_oma_set_feature_setting_finish (client, res, &error); if (!output || !qmi_message_oma_set_feature_setting_output_get_result (output, &error)) g_task_return_error (task, error); else g_task_return_boolean (task, TRUE); g_object_unref (task); if (output) qmi_message_oma_set_feature_setting_output_unref (output); } static void oma_setup (MMIfaceModemOma *self, MMOmaFeature features, GAsyncReadyCallback callback, gpointer user_data) { QmiClient *client = NULL; QmiMessageOmaSetFeatureSettingInput *input; if (!mm_shared_qmi_ensure_client (MM_SHARED_QMI (self), QMI_SERVICE_OMA, &client, callback, user_data)) return; input = qmi_message_oma_set_feature_setting_input_new (); qmi_message_oma_set_feature_setting_input_set_device_provisioning_service_update_config ( input, !!(features & MM_OMA_FEATURE_DEVICE_PROVISIONING), NULL); qmi_message_oma_set_feature_setting_input_set_prl_update_service_config ( input, !!(features & MM_OMA_FEATURE_PRL_UPDATE), NULL); qmi_message_oma_set_feature_setting_input_set_hfa_feature_config ( input, !!(features & MM_OMA_FEATURE_HANDS_FREE_ACTIVATION), NULL); qmi_client_oma_set_feature_setting ( QMI_CLIENT_OMA (client), input, 5, NULL, (GAsyncReadyCallback)oma_set_feature_setting_ready, g_task_new (self, NULL, callback, user_data)); qmi_message_oma_set_feature_setting_input_unref (input); } /*****************************************************************************/ /* Start client initiated session (OMA interface) */ static gboolean oma_start_client_initiated_session_finish (MMIfaceModemOma *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void oma_start_session_ready (QmiClientOma *client, GAsyncResult *res, GTask *task) { QmiMessageOmaStartSessionOutput *output; GError *error = NULL; output = qmi_client_oma_start_session_finish (client, res, &error); if (!output || !qmi_message_oma_start_session_output_get_result (output, &error)) g_task_return_error (task, error); else g_task_return_boolean (task, TRUE); g_object_unref (task); if (output) qmi_message_oma_start_session_output_unref (output); } static void oma_start_client_initiated_session (MMIfaceModemOma *self, MMOmaSessionType session_type, GAsyncReadyCallback callback, gpointer user_data) { QmiClient *client = NULL; QmiMessageOmaStartSessionInput *input; if (!mm_shared_qmi_ensure_client (MM_SHARED_QMI (self), QMI_SERVICE_OMA, &client, callback, user_data)) return; /* It's already checked in mm-iface-modem-oma; so just assert if this is not ok */ g_assert (session_type == MM_OMA_SESSION_TYPE_CLIENT_INITIATED_DEVICE_CONFIGURE || session_type == MM_OMA_SESSION_TYPE_CLIENT_INITIATED_PRL_UPDATE || session_type == MM_OMA_SESSION_TYPE_CLIENT_INITIATED_HANDS_FREE_ACTIVATION); input = qmi_message_oma_start_session_input_new (); qmi_message_oma_start_session_input_set_session_type ( input, mm_oma_session_type_to_qmi_oma_session_type (session_type), NULL); qmi_client_oma_start_session ( QMI_CLIENT_OMA (client), input, 5, NULL, (GAsyncReadyCallback)oma_start_session_ready, g_task_new (self, NULL, callback, user_data)); qmi_message_oma_start_session_input_unref (input); } /*****************************************************************************/ /* Accept network initiated session (OMA interface) */ static gboolean oma_accept_network_initiated_session_finish (MMIfaceModemOma *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void oma_send_selection_ready (QmiClientOma *client, GAsyncResult *res, GTask *task) { QmiMessageOmaSendSelectionOutput *output; GError *error = NULL; output = qmi_client_oma_send_selection_finish (client, res, &error); if (!output || !qmi_message_oma_send_selection_output_get_result (output, &error)) g_task_return_error (task, error); else g_task_return_boolean (task, TRUE); g_object_unref (task); if (output) qmi_message_oma_send_selection_output_unref (output); } static void oma_accept_network_initiated_session (MMIfaceModemOma *self, guint session_id, gboolean accept, GAsyncReadyCallback callback, gpointer user_data) { QmiClient *client = NULL; QmiMessageOmaSendSelectionInput *input; if (!mm_shared_qmi_ensure_client (MM_SHARED_QMI (self), QMI_SERVICE_OMA, &client, callback, user_data)) return; input = qmi_message_oma_send_selection_input_new (); qmi_message_oma_send_selection_input_set_network_initiated_alert_selection ( input, accept, (guint16)session_id, NULL); qmi_client_oma_send_selection ( QMI_CLIENT_OMA (client), input, 5, NULL, (GAsyncReadyCallback)oma_send_selection_ready, g_task_new (self, NULL, callback, user_data)); qmi_message_oma_send_selection_input_unref (input); } /*****************************************************************************/ /* Cancel session (OMA interface) */ static gboolean oma_cancel_session_finish (MMIfaceModemOma *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void oma_cancel_session_ready (QmiClientOma *client, GAsyncResult *res, GTask *task) { QmiMessageOmaCancelSessionOutput *output; GError *error = NULL; output = qmi_client_oma_cancel_session_finish (client, res, &error); if (!output || !qmi_message_oma_cancel_session_output_get_result (output, &error)) g_task_return_error (task, error); else g_task_return_boolean (task, TRUE); g_object_unref (task); if (output) qmi_message_oma_cancel_session_output_unref (output); } static void oma_cancel_session (MMIfaceModemOma *self, GAsyncReadyCallback callback, gpointer user_data) { QmiClient *client = NULL; if (!mm_shared_qmi_ensure_client (MM_SHARED_QMI (self), QMI_SERVICE_OMA, &client, callback, user_data)) return; qmi_client_oma_cancel_session ( QMI_CLIENT_OMA (client), NULL, 5, NULL, (GAsyncReadyCallback)oma_cancel_session_ready, g_task_new (self, NULL, callback, user_data)); } /*****************************************************************************/ /* Setup/Cleanup unsolicited event handlers (OMA interface) */ static void oma_event_report_indication_cb (QmiClientNas *client, QmiIndicationOmaEventReportOutput *output, MMBroadbandModemQmi *self) { QmiOmaSessionState qmi_session_state; QmiOmaSessionType network_initiated_alert_session_type; guint16 network_initiated_alert_session_id; /* Update session state? */ if (qmi_indication_oma_event_report_output_get_session_state ( output, &qmi_session_state, NULL)) { QmiOmaSessionFailedReason qmi_oma_session_failed_reason = QMI_OMA_SESSION_FAILED_REASON_UNKNOWN; if (qmi_session_state == QMI_OMA_SESSION_STATE_FAILED) qmi_indication_oma_event_report_output_get_session_fail_reason ( output, &qmi_oma_session_failed_reason, NULL); mm_iface_modem_oma_update_session_state ( MM_IFACE_MODEM_OMA (self), mm_oma_session_state_from_qmi_oma_session_state (qmi_session_state), mm_oma_session_state_failed_reason_from_qmi_oma_session_failed_reason (qmi_oma_session_failed_reason)); } /* New network initiated session? */ if (qmi_indication_oma_event_report_output_get_network_initiated_alert ( output, &network_initiated_alert_session_type, &network_initiated_alert_session_id, NULL)) { MMOmaSessionType session_type; session_type = mm_oma_session_type_from_qmi_oma_session_type (network_initiated_alert_session_type); if (session_type == MM_OMA_SESSION_TYPE_UNKNOWN) mm_obj_warn (self, "unknown QMI OMA session type '%u'", network_initiated_alert_session_type); else mm_iface_modem_oma_add_pending_network_initiated_session ( MM_IFACE_MODEM_OMA (self), session_type, (guint)network_initiated_alert_session_id); } } static gboolean common_oma_setup_cleanup_unsolicited_events_finish (MMIfaceModemOma *_self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void common_setup_cleanup_oma_unsolicited_events (MMBroadbandModemQmi *self, gboolean enable, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; QmiClient *client = NULL; if (!mm_shared_qmi_ensure_client (MM_SHARED_QMI (self), QMI_SERVICE_OMA, &client, callback, user_data)) return; task = g_task_new (self, NULL, callback, user_data); if (enable == self->priv->oma_unsolicited_events_setup) { mm_obj_dbg (self, "OMA unsolicited events already %s; skipping", enable ? "setup" : "cleanup"); g_task_return_boolean (task, TRUE); g_object_unref (task); return; } /* Store new state */ self->priv->oma_unsolicited_events_setup = enable; /* Connect/Disconnect "Event Report" indications */ if (enable) { g_assert (self->priv->oma_event_report_indication_id == 0); self->priv->oma_event_report_indication_id = g_signal_connect (client, "event-report", G_CALLBACK (oma_event_report_indication_cb), self); } else { g_assert (self->priv->oma_event_report_indication_id != 0); g_signal_handler_disconnect (client, self->priv->oma_event_report_indication_id); self->priv->oma_event_report_indication_id = 0; } g_task_return_boolean (task, TRUE); g_object_unref (task); } static void oma_cleanup_unsolicited_events (MMIfaceModemOma *self, GAsyncReadyCallback callback, gpointer user_data) { common_setup_cleanup_oma_unsolicited_events (MM_BROADBAND_MODEM_QMI (self), FALSE, callback, user_data); } static void oma_setup_unsolicited_events (MMIfaceModemOma *self, GAsyncReadyCallback callback, gpointer user_data) { common_setup_cleanup_oma_unsolicited_events (MM_BROADBAND_MODEM_QMI (self), TRUE, callback, user_data); } /*****************************************************************************/ /* Enable/Disable unsolicited events (OMA interface) */ typedef struct { gboolean enable; } EnableOmaUnsolicitedEventsContext; static gboolean common_oma_enable_disable_unsolicited_events_finish (MMIfaceModemOma *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void ser_oma_indicator_ready (QmiClientOma *client, GAsyncResult *res, GTask *task) { MMBroadbandModemQmi *self; EnableOmaUnsolicitedEventsContext *ctx; QmiMessageOmaSetEventReportOutput *output = NULL; GError *error = NULL; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); output = qmi_client_oma_set_event_report_finish (client, res, &error); if (!output) { mm_obj_dbg (self, "QMI operation failed: '%s'", error->message); g_error_free (error); } else if (!qmi_message_oma_set_event_report_output_get_result (output, &error)) { mm_obj_dbg (self, "couldn't set event report: '%s'", error->message); g_error_free (error); } if (output) qmi_message_oma_set_event_report_output_unref (output); /* Just ignore errors for now */ self->priv->oma_unsolicited_events_enabled = ctx->enable; g_task_return_boolean (task, TRUE); g_object_unref (task); } static void common_enable_disable_oma_unsolicited_events (MMBroadbandModemQmi *self, gboolean enable, GAsyncReadyCallback callback, gpointer user_data) { EnableOmaUnsolicitedEventsContext *ctx; GTask *task; QmiClient *client = NULL; QmiMessageOmaSetEventReportInput *input; if (!mm_shared_qmi_ensure_client (MM_SHARED_QMI (self), QMI_SERVICE_OMA, &client, callback, user_data)) return; task = g_task_new (self, NULL, callback, user_data); if (enable == self->priv->oma_unsolicited_events_enabled) { mm_obj_dbg (self, "OMA unsolicited events already %s; skipping", enable ? "enabled" : "disabled"); g_task_return_boolean (task, TRUE); g_object_unref (task); return; } ctx = g_new (EnableOmaUnsolicitedEventsContext, 1); ctx->enable = enable; g_task_set_task_data (task, ctx, g_free); input = qmi_message_oma_set_event_report_input_new (); qmi_message_oma_set_event_report_input_set_session_state_reporting ( input, ctx->enable, NULL); qmi_message_oma_set_event_report_input_set_network_initiated_alert_reporting ( input, ctx->enable, NULL); qmi_client_oma_set_event_report ( QMI_CLIENT_OMA (client), input, 5, NULL, (GAsyncReadyCallback)ser_oma_indicator_ready, task); qmi_message_oma_set_event_report_input_unref (input); } static void oma_disable_unsolicited_events (MMIfaceModemOma *self, GAsyncReadyCallback callback, gpointer user_data) { common_enable_disable_oma_unsolicited_events (MM_BROADBAND_MODEM_QMI (self), FALSE, callback, user_data); } static void oma_enable_unsolicited_events (MMIfaceModemOma *self, GAsyncReadyCallback callback, gpointer user_data) { common_enable_disable_oma_unsolicited_events (MM_BROADBAND_MODEM_QMI (self), TRUE, callback, user_data); } /*****************************************************************************/ /* Check support (3GPP USSD interface) */ static gboolean modem_3gpp_ussd_check_support_finish (MMIfaceModem3gppUssd *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void modem_3gpp_ussd_check_support (MMIfaceModem3gppUssd *self, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; task = g_task_new (self, NULL, callback, user_data); /* If we have support for the Voice client, USSD is supported */ if (!mm_shared_qmi_peek_client (MM_SHARED_QMI (self), QMI_SERVICE_VOICE, MM_PORT_QMI_FLAG_DEFAULT, NULL)) { mm_obj_dbg (self, "USSD capabilities not supported"); g_task_return_boolean (task, FALSE); } else { mm_obj_dbg (self, "USSD capabilities supported"); g_task_return_boolean (task, TRUE); } g_object_unref (task); } /*****************************************************************************/ /* USSD encode/decode helpers */ static GArray * ussd_encode (const gchar *command, QmiVoiceUssDataCodingScheme *scheme, GError **error) { gsize command_len; g_autoptr(GByteArray) barray = NULL; g_autoptr(GError) inner_error = NULL; command_len = strlen (command); if (g_str_is_ascii (command)) { barray = g_byte_array_sized_new (command_len); g_byte_array_append (barray, (const guint8 *)command, command_len); *scheme = QMI_VOICE_USS_DATA_CODING_SCHEME_ASCII; return (GArray *) g_steal_pointer (&barray); } barray = mm_modem_charset_bytearray_from_utf8 (command, MM_MODEM_CHARSET_UCS2, FALSE, &inner_error); if (!barray) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "Failed to encode USSD command in UCS2 charset: %s", inner_error->message); return NULL; } *scheme = QMI_VOICE_USS_DATA_CODING_SCHEME_UCS2; return (GArray *) g_steal_pointer (&barray); } static gchar * ussd_decode (QmiVoiceUssDataCodingScheme scheme, GArray *data, GError **error) { gchar *decoded = NULL; if (scheme == QMI_VOICE_USS_DATA_CODING_SCHEME_ASCII) { decoded = g_strndup ((const gchar *) data->data, data->len); if (!decoded) g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "Error decoding USSD command in 0x%04x scheme (ASCII charset)", scheme); } else if (scheme == QMI_VOICE_USS_DATA_CODING_SCHEME_UCS2) { decoded = mm_modem_charset_bytearray_to_utf8 ((GByteArray *) data, MM_MODEM_CHARSET_UCS2, FALSE, error); if (!decoded) g_prefix_error (error, "Error decoding USSD command in 0x%04x scheme (UCS2 charset): ", scheme); } else g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "Failed to decode USSD command in unsupported 0x%04x scheme", scheme); return decoded; } /*****************************************************************************/ /* USSD indications */ static void process_ussd_message (MMBroadbandModemQmi *self, QmiVoiceUserAction user_action, gchar *utf8_take, GError *error_take) { MMModem3gppUssdSessionState ussd_state = MM_MODEM_3GPP_USSD_SESSION_STATE_IDLE; g_autoptr(GTask) task = NULL; g_autofree gchar *utf8 = utf8_take; g_autoptr(GError) error = error_take; task = g_steal_pointer (&self->priv->pending_ussd_action); if (error) { g_assert (!utf8); if (task) g_task_return_error (task, g_steal_pointer (&error)); else mm_obj_dbg (self, "USSD operation failed: %s", error->message); return; } switch (user_action) { case QMI_VOICE_USER_ACTION_NOT_REQUIRED: case QMI_VOICE_USER_ACTION_UNKNOWN: /* Treat unknown user action as user action not required. */ /* no response, or a response to user's request? */ if (!utf8 || task) break; /* Network-initiated USSD-Notify */ mm_iface_modem_3gpp_ussd_update_network_notification (MM_IFACE_MODEM_3GPP_USSD (self), utf8); g_clear_pointer (&utf8, g_free); break; case QMI_VOICE_USER_ACTION_REQUIRED: /* further action required */ ussd_state = MM_MODEM_3GPP_USSD_SESSION_STATE_USER_RESPONSE; /* no response, or a response to user's request? */ if (!utf8 || task) break; /* Network-initiated USSD-Request */ mm_iface_modem_3gpp_ussd_update_network_request (MM_IFACE_MODEM_3GPP_USSD (self), utf8); g_clear_pointer (&utf8, g_free); break; default: /* Not an indication */ break; } mm_iface_modem_3gpp_ussd_update_state (MM_IFACE_MODEM_3GPP_USSD (self), ussd_state); if (!task) { if (utf8) mm_obj_dbg (self, "ignoring unprocessed USSD message: %s", utf8); return; } /* Complete the pending action, if any */ if (utf8) g_task_return_pointer (task, g_steal_pointer (&utf8), g_free); else g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "USSD action response not processed correctly"); } static void ussd_indication_cb (QmiClientVoice *client, QmiIndicationVoiceUssdOutput *output, MMBroadbandModemQmi *self) { QmiVoiceUserAction user_action = QMI_VOICE_USER_ACTION_UNKNOWN; QmiVoiceUssDataCodingScheme scheme; GArray *uss_data = NULL; gchar *utf8 = NULL; GError *error = NULL; qmi_indication_voice_ussd_output_get_user_action (output, &user_action, NULL); if (qmi_indication_voice_ussd_output_get_uss_data_utf16 (output, &uss_data, NULL) && uss_data) utf8 = g_convert ((const gchar *) uss_data->data, (2 * uss_data->len), "UTF-8", "UTF-16LE", NULL, NULL, &error); else if (qmi_indication_voice_ussd_output_get_uss_data (output, &scheme, &uss_data, NULL) && uss_data) utf8 = ussd_decode(scheme, uss_data, &error); process_ussd_message (self, user_action, utf8, error); } static void ussd_release_indication_cb (QmiClientVoice *client, MMBroadbandModemQmi *self) { GTask *pending_task; GError *error; mm_iface_modem_3gpp_ussd_update_state (MM_IFACE_MODEM_3GPP_USSD (self), MM_MODEM_3GPP_USSD_SESSION_STATE_IDLE); error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_ABORTED, "USSD terminated by network"); pending_task = g_steal_pointer (&self->priv->pending_ussd_action); if (pending_task) { g_task_return_error (pending_task, error); g_object_unref (pending_task); return; } /* If no pending task, just report the error */ mm_obj_dbg (self, "USSD release indication: %s", error->message); g_error_free (error); } /*****************************************************************************/ /* Setup/cleanup unsolicited events */ static gboolean common_3gpp_ussd_setup_cleanup_unsolicited_events_finish (MMIfaceModem3gppUssd *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void common_3gpp_ussd_setup_cleanup_unsolicited_events (MMBroadbandModemQmi *self, gboolean setup, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; QmiClient *client = NULL; if (!mm_shared_qmi_ensure_client (MM_SHARED_QMI (self), QMI_SERVICE_VOICE, &client, callback, user_data)) return; task = g_task_new (self, NULL, callback, user_data); if (setup == self->priv->ussd_unsolicited_events_setup) { mm_obj_dbg (self, "USSD unsolicited events already %s; skipping", setup ? "setup" : "cleanup"); g_task_return_boolean (task, TRUE); g_object_unref (task); return; } self->priv->ussd_unsolicited_events_setup = setup; if (setup) { g_assert (self->priv->ussd_indication_id == 0); self->priv->ussd_indication_id = g_signal_connect (client, "ussd", G_CALLBACK (ussd_indication_cb), self); g_assert (self->priv->ussd_release_indication_id == 0); self->priv->ussd_release_indication_id = g_signal_connect (client, "release-ussd", G_CALLBACK (ussd_release_indication_cb), self); } else { g_assert (self->priv->ussd_indication_id != 0); g_signal_handler_disconnect (client, self->priv->ussd_indication_id); self->priv->ussd_indication_id = 0; g_assert (self->priv->ussd_release_indication_id != 0); g_signal_handler_disconnect (client, self->priv->ussd_release_indication_id); self->priv->ussd_release_indication_id = 0; } g_task_return_boolean (task, TRUE); g_object_unref (task); } static void modem_3gpp_ussd_setup_unsolicited_events (MMIfaceModem3gppUssd *self, GAsyncReadyCallback callback, gpointer user_data) { common_3gpp_ussd_setup_cleanup_unsolicited_events (MM_BROADBAND_MODEM_QMI (self), TRUE, callback, user_data); } static void modem_3gpp_ussd_cleanup_unsolicited_events (MMIfaceModem3gppUssd *self, GAsyncReadyCallback callback, gpointer user_data) { common_3gpp_ussd_setup_cleanup_unsolicited_events (MM_BROADBAND_MODEM_QMI (self), FALSE, callback, user_data); } /*****************************************************************************/ /* Enable/disable unsolicited events */ static gboolean common_3gpp_ussd_enable_disable_unsolicited_events_finish (MMIfaceModem3gppUssd *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void ussd_indication_register_ready (QmiClientVoice *client, GAsyncResult *res, GTask *task) { g_autoptr(QmiMessageVoiceIndicationRegisterOutput) output = NULL; GError *error = NULL; output = qmi_client_voice_indication_register_finish (client, res, &error); if (!output) { g_prefix_error (&error, "QMI operation failed: "); g_task_return_error (task, error); } else if (!qmi_message_voice_indication_register_output_get_result (output, &error)) { g_prefix_error (&error, "Couldn't register voice USSD indications: "); g_task_return_error (task, error); } else g_task_return_boolean (task, TRUE); g_object_unref (task); } static void common_3gpp_ussd_enable_disable_unsolicited_events (MMBroadbandModemQmi *self, gboolean enable, GAsyncReadyCallback callback, gpointer user_data) { g_autoptr(QmiMessageVoiceIndicationRegisterInput) input = NULL; GTask *task; QmiClient *client; if (!mm_shared_qmi_ensure_client (MM_SHARED_QMI (self), QMI_SERVICE_VOICE, &client, callback, user_data)) return; task = g_task_new (self, NULL, callback, user_data); if (enable == self->priv->ussd_unsolicited_events_enabled) { mm_obj_dbg (self, "USSD unsolicited events already %s; skipping", enable ? "enabled" : "disabled"); g_task_return_boolean (task, TRUE); g_object_unref (task); return; } self->priv->ussd_unsolicited_events_enabled = enable; input = qmi_message_voice_indication_register_input_new (); qmi_message_voice_indication_register_input_set_ussd_notification_events (input, enable, NULL); qmi_client_voice_indication_register (QMI_CLIENT_VOICE (client), input, 10, NULL, (GAsyncReadyCallback) ussd_indication_register_ready, task); } static void modem_3gpp_ussd_enable_unsolicited_events (MMIfaceModem3gppUssd *self, GAsyncReadyCallback callback, gpointer user_data) { common_3gpp_ussd_enable_disable_unsolicited_events (MM_BROADBAND_MODEM_QMI (self), TRUE, callback, user_data); } static void modem_3gpp_ussd_disable_unsolicited_events (MMIfaceModem3gppUssd *self, GAsyncReadyCallback callback, gpointer user_data) { common_3gpp_ussd_enable_disable_unsolicited_events (MM_BROADBAND_MODEM_QMI (self), FALSE, callback, user_data); } /*****************************************************************************/ /* Send command (3GPP/USSD interface) */ static gchar * modem_3gpp_ussd_send_finish (MMIfaceModem3gppUssd *self, GAsyncResult *res, GError **error) { return g_task_propagate_pointer (G_TASK (res), error); } static void voice_answer_ussd_ready (QmiClientVoice *client, GAsyncResult *res, MMBroadbandModemQmi *self) { g_autoptr(QmiMessageVoiceAnswerUssdOutput) output = NULL; GError *error = NULL; output = qmi_client_voice_answer_ussd_finish (client, res, &error); if (!output) g_prefix_error (&error, "QMI operation failed: "); else if (!qmi_message_voice_answer_ussd_output_get_result (output, &error)) g_prefix_error (&error, "Couldn't answer USSD operation: "); process_ussd_message (self, QMI_VOICE_USER_ACTION_UNKNOWN, error ? NULL : g_strdup (""), error); /* balance out the full reference we received */ g_object_unref (self); } static void voice_originate_ussd_ready (QmiClientVoice *client, GAsyncResult *res, MMBroadbandModemQmi *self) { g_autoptr(QmiMessageVoiceOriginateUssdOutput) output = NULL; QmiVoiceUssDataCodingScheme scheme; GError *error = NULL; GArray *uss_data = NULL; gchar *utf8 = NULL; output = qmi_client_voice_originate_ussd_finish (client, res, &error); if (!output) g_prefix_error (&error, "QMI operation failed: "); else if (!qmi_message_voice_originate_ussd_output_get_result (output, &error)) g_prefix_error (&error, "Couldn't originate USSD operation: "); else if (qmi_message_voice_originate_ussd_output_get_uss_data_utf16 (output, &uss_data, NULL) && uss_data) utf8 = g_convert ((const gchar *) uss_data->data, (2 * uss_data->len), "UTF-8", "UTF-16LE", NULL, NULL, &error); else if (qmi_message_voice_originate_ussd_output_get_uss_data (output, &scheme, &uss_data, NULL) && uss_data) utf8 = ussd_decode (scheme, uss_data, &error); process_ussd_message (self, QMI_VOICE_USER_ACTION_UNKNOWN, utf8, error); /* balance out the full reference we received */ g_object_unref (self); } static void modem_3gpp_ussd_send (MMIfaceModem3gppUssd *_self, const gchar *command, GAsyncReadyCallback callback, gpointer user_data) { MMBroadbandModemQmi *self = MM_BROADBAND_MODEM_QMI (_self); GTask *task; QmiClient *client; QmiVoiceUssDataCodingScheme scheme = QMI_VOICE_USS_DATA_CODING_SCHEME_UNKNOWN; g_autoptr(GArray) encoded = NULL; GError *error = NULL; MMModem3gppUssdSessionState state; if (!mm_shared_qmi_ensure_client (MM_SHARED_QMI (self), QMI_SERVICE_VOICE, &client, callback, user_data)) return; task = g_task_new (self, NULL, callback, user_data); /* Fail if there is an ongoing operation already */ if (self->priv->pending_ussd_action) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_IN_PROGRESS, "there is already an ongoing USSD operation"); g_object_unref (task); return; } encoded = ussd_encode (command, &scheme, &error); if (!encoded) { g_task_return_error (task, error); g_object_unref (task); return; } state = mm_iface_modem_3gpp_ussd_get_state (MM_IFACE_MODEM_3GPP_USSD (self)); /* Cache the action, as it may be completed via URCs */ self->priv->pending_ussd_action = task; mm_iface_modem_3gpp_ussd_update_state (_self, MM_MODEM_3GPP_USSD_SESSION_STATE_ACTIVE); switch (state) { case MM_MODEM_3GPP_USSD_SESSION_STATE_IDLE: { g_autoptr(QmiMessageVoiceOriginateUssdInput) input = NULL; input = qmi_message_voice_originate_ussd_input_new (); qmi_message_voice_originate_ussd_input_set_uss_data (input, scheme, encoded, NULL); qmi_client_voice_originate_ussd (QMI_CLIENT_VOICE (client), input, 100, NULL, (GAsyncReadyCallback) voice_originate_ussd_ready, g_object_ref (self)); /* full reference! */ return; } case MM_MODEM_3GPP_USSD_SESSION_STATE_USER_RESPONSE: { g_autoptr(QmiMessageVoiceAnswerUssdInput) input = NULL; input = qmi_message_voice_answer_ussd_input_new (); qmi_message_voice_answer_ussd_input_set_uss_data (input, scheme, encoded, NULL); qmi_client_voice_answer_ussd (QMI_CLIENT_VOICE (client), input, 100, NULL, (GAsyncReadyCallback) voice_answer_ussd_ready, g_object_ref (self)); /* full reference! */ return; } case MM_MODEM_3GPP_USSD_SESSION_STATE_UNKNOWN: case MM_MODEM_3GPP_USSD_SESSION_STATE_ACTIVE: default: g_assert_not_reached (); return; } } /*****************************************************************************/ /* Cancel USSD (3GPP/USSD interface) */ static gboolean modem_3gpp_ussd_cancel_finish (MMIfaceModem3gppUssd *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void voice_cancel_ussd_ready (QmiClientVoice *client, GAsyncResult *res, GTask *task) { g_autoptr(QmiMessageVoiceCancelUssdOutput) output = NULL; MMBroadbandModemQmi *self; GTask *pending_task; GError *error = NULL; self = g_task_get_source_object (task); output = qmi_client_voice_cancel_ussd_finish (client, res, &error); if (!output) g_prefix_error (&error, "QMI operation failed: "); else if (!qmi_message_voice_cancel_ussd_output_get_result (output, &error)) g_prefix_error (&error, "Couldn't cancel USSD operation: "); /* Complete the pending action, regardless of the operation result */ pending_task = g_steal_pointer (&self->priv->pending_ussd_action); if (pending_task) { g_task_return_new_error (pending_task, MM_CORE_ERROR, MM_CORE_ERROR_ABORTED, "USSD session was cancelled"); g_object_unref (pending_task); } mm_iface_modem_3gpp_ussd_update_state (MM_IFACE_MODEM_3GPP_USSD (self), MM_MODEM_3GPP_USSD_SESSION_STATE_IDLE); if (error) g_task_return_error (task, error); else g_task_return_boolean (task, TRUE); g_object_unref (task); } static void modem_3gpp_ussd_cancel (MMIfaceModem3gppUssd *_self, GAsyncReadyCallback callback, gpointer user_data) { MMBroadbandModemQmi *self = MM_BROADBAND_MODEM_QMI (_self); GTask *task; QmiClient *client; if (!mm_shared_qmi_ensure_client (MM_SHARED_QMI (self), QMI_SERVICE_VOICE, &client, callback, user_data)) return; task = g_task_new (self, NULL, callback, user_data); qmi_client_voice_cancel_ussd (QMI_CLIENT_VOICE (client), NULL, 100, NULL, (GAsyncReadyCallback) voice_cancel_ussd_ready, task); } /*****************************************************************************/ /* Check support (Voice interface) */ static gboolean modem_voice_check_support_finish (MMIfaceModemVoice *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void modem_voice_check_support (MMIfaceModemVoice *self, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; task = g_task_new (self, NULL, callback, user_data); /* If we have support for the Voice client, Voice is supported */ if (!mm_shared_qmi_peek_client (MM_SHARED_QMI (self), QMI_SERVICE_VOICE, MM_PORT_QMI_FLAG_DEFAULT, NULL)) { mm_obj_dbg (self, "Voice capabilities not supported"); g_task_return_boolean (task, FALSE); } else { /* * In case of QMI, we don't need polling as call list * will be dynamically updated by All Call Status indication. * If an AT URC is received, reload the call list through QMI. */ g_object_set (self, MM_IFACE_MODEM_VOICE_PERIODIC_CALL_LIST_CHECK_DISABLED, TRUE, MM_IFACE_MODEM_VOICE_INDICATION_CALL_LIST_RELOAD_ENABLED, TRUE, NULL); mm_obj_dbg (self, "Voice capabilities supported"); g_task_return_boolean (task, TRUE); } g_object_unref (task); } /*****************************************************************************/ /* All Call Status indications */ static void all_call_status_indication_cb (QmiClientVoice *client, QmiIndicationVoiceAllCallStatusOutput *output, MMBroadbandModemQmi *self) { GArray *qmi_remote_party_number_list = NULL; GArray *qmi_call_information_list = NULL; GList *call_info_list = NULL; guint i; guint j; qmi_indication_voice_all_call_status_output_get_remote_party_number (output, &qmi_remote_party_number_list, NULL); qmi_indication_voice_all_call_status_output_get_call_information (output, &qmi_call_information_list, NULL); if (!qmi_remote_party_number_list || !qmi_call_information_list) { mm_obj_dbg (self, "Ignoring All Call Status indication. Remote party number or call information not available"); return; } for (i = 0; i < qmi_call_information_list->len; i++) { QmiIndicationVoiceAllCallStatusOutputCallInformationCall qmi_call_information; qmi_call_information = g_array_index (qmi_call_information_list, QmiIndicationVoiceAllCallStatusOutputCallInformationCall, i); for (j = 0; j < qmi_remote_party_number_list->len; j++) { QmiIndicationVoiceAllCallStatusOutputRemotePartyNumberCall qmi_remote_party_number; qmi_remote_party_number = g_array_index (qmi_remote_party_number_list, QmiIndicationVoiceAllCallStatusOutputRemotePartyNumberCall, j); if (qmi_call_information.id == qmi_remote_party_number.id) { MMCallInfo *call_info; call_info = g_slice_new0 (MMCallInfo); call_info->index = qmi_call_information.id; call_info->number = g_strdup (qmi_remote_party_number.type); switch (qmi_call_information.state) { case QMI_VOICE_CALL_STATE_UNKNOWN: call_info->state = MM_CALL_STATE_UNKNOWN; break; case QMI_VOICE_CALL_STATE_ORIGINATION: case QMI_VOICE_CALL_STATE_CC_IN_PROGRESS: call_info->state = MM_CALL_STATE_DIALING; break; case QMI_VOICE_CALL_STATE_ALERTING: call_info->state = MM_CALL_STATE_RINGING_OUT; break; case QMI_VOICE_CALL_STATE_SETUP: case QMI_VOICE_CALL_STATE_INCOMING: call_info->state = MM_CALL_STATE_RINGING_IN; break; case QMI_VOICE_CALL_STATE_CONVERSATION: call_info->state = MM_CALL_STATE_ACTIVE; break; case QMI_VOICE_CALL_STATE_HOLD: call_info->state = MM_CALL_STATE_HELD; break; case QMI_VOICE_CALL_STATE_WAITING: call_info->state = MM_CALL_STATE_WAITING; break; case QMI_VOICE_CALL_STATE_DISCONNECTING: case QMI_VOICE_CALL_STATE_END: call_info->state = MM_CALL_STATE_TERMINATED; break; default: call_info->state = MM_CALL_STATE_UNKNOWN; break; } switch (qmi_call_information.direction) { case QMI_VOICE_CALL_DIRECTION_UNKNOWN: call_info->direction = MM_CALL_DIRECTION_UNKNOWN; break; case QMI_VOICE_CALL_DIRECTION_MO: call_info->direction = MM_CALL_DIRECTION_OUTGOING; break; case QMI_VOICE_CALL_DIRECTION_MT: call_info->direction = MM_CALL_DIRECTION_INCOMING; break; default: call_info->direction = MM_CALL_DIRECTION_UNKNOWN; break; } call_info_list = g_list_append (call_info_list, call_info); } } } mm_iface_modem_voice_report_all_calls (MM_IFACE_MODEM_VOICE (self), call_info_list); mm_3gpp_call_info_list_free (call_info_list); } /*****************************************************************************/ /* Supplementary service indication */ static void supplementary_service_indication_cb (QmiClientVoice *client, QmiIndicationVoiceSupplementaryServiceOutput *output, MMBroadbandModemQmi *self) { QmiVoiceSupplementaryServiceNotificationType notification_type; guint8 call_id = 0; g_autoptr(MMCallList) call_list = NULL; MMBaseCall *call = NULL; if (!qmi_indication_voice_supplementary_service_output_get_info (output, &call_id, ¬ification_type, NULL)) { mm_obj_dbg (self, "Ignoring supplementary service indication: no call id or notification type given"); return; } /* Retrieve list of known calls */ g_object_get (MM_BASE_MODEM (self), MM_IFACE_MODEM_VOICE_CALL_LIST, &call_list, NULL); if (!call_list) { mm_obj_dbg (self, "Ignoring supplementary service indication: no call list exists"); return; } call = mm_call_list_get_call_by_index (call_list, call_id); if (!call) { mm_obj_dbg (self, "Ignoring supplementary service indication: no matching call exists"); return; } if (notification_type == QMI_VOICE_SUPPLEMENTARY_SERVICE_NOTIFICATION_TYPE_CALL_IS_ON_HOLD) mm_base_call_change_state (call, MM_CALL_STATE_HELD, MM_CALL_STATE_REASON_UNKNOWN); else if (notification_type == QMI_VOICE_SUPPLEMENTARY_SERVICE_NOTIFICATION_TYPE_CALL_IS_RETRIEVED) mm_base_call_change_state (call, MM_CALL_STATE_ACTIVE, MM_CALL_STATE_REASON_UNKNOWN); else if (notification_type == QMI_VOICE_SUPPLEMENTARY_SERVICE_NOTIFICATION_TYPE_OUTGOING_CALL_IS_WAITING) mm_base_call_change_state (call, MM_CALL_STATE_WAITING, MM_CALL_STATE_REASON_REFUSED_OR_BUSY); else mm_obj_dbg (self, "Ignoring supplementary service indication: unhandled notification type"); } /*****************************************************************************/ /* Setup/cleanup unsolicited events */ static void parent_voice_setup_unsolicited_events_ready (MMIfaceModemVoice *self, GAsyncResult *res, GTask *task) { GError *error = NULL; if (!iface_modem_voice_parent->setup_unsolicited_events_finish (self, res, &error)) { mm_obj_warn (self, "setting up parent voice unsolicited events failed: %s", error->message); g_clear_error (&error); } g_task_return_boolean (task, TRUE); g_object_unref (task); } static void parent_voice_cleanup_unsolicited_events_ready (MMIfaceModemVoice *self, GAsyncResult *res, GTask *task) { GError *error = NULL; if (!iface_modem_voice_parent->cleanup_unsolicited_events_finish (self, res, &error)) { mm_obj_warn (self, "cleaning up parent voice unsolicited events failed: %s", error->message); g_clear_error (&error); } g_task_return_boolean (task, TRUE); g_object_unref (task); } static gboolean common_voice_setup_cleanup_unsolicited_events_finish (MMIfaceModemVoice *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void common_voice_setup_cleanup_unsolicited_events (MMBroadbandModemQmi *self, gboolean setup, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; QmiClient *client = NULL; if (!mm_shared_qmi_ensure_client (MM_SHARED_QMI (self), QMI_SERVICE_VOICE, &client, callback, user_data)) return; task = g_task_new (self, NULL, callback, user_data); if (setup == self->priv->all_call_status_unsolicited_events_setup) { mm_obj_dbg (self, "voice unsolicited events already %s; skipping", setup ? "setup" : "cleanup"); g_task_return_boolean (task, TRUE); g_object_unref (task); return; } self->priv->all_call_status_unsolicited_events_setup = setup; if (setup) { /* Connect QMI indications signals for calls */ g_assert (self->priv->all_call_status_indication_id == 0); self->priv->all_call_status_indication_id = g_signal_connect (client, "all-call-status", G_CALLBACK (all_call_status_indication_cb), self); /* Setup AT URCs as fall back for calls */ if (iface_modem_voice_parent->setup_unsolicited_events) { iface_modem_voice_parent->setup_unsolicited_events ( MM_IFACE_MODEM_VOICE (self), (GAsyncReadyCallback) parent_voice_setup_unsolicited_events_ready, task); return; } } else { /* Disconnect QMI indications signals for calls */ g_assert (self->priv->all_call_status_indication_id != 0); g_signal_handler_disconnect (client, self->priv->all_call_status_indication_id); self->priv->all_call_status_indication_id = 0; /* Cleanup AT URCs as fall back for calls */ if (iface_modem_voice_parent->cleanup_unsolicited_events) { iface_modem_voice_parent->cleanup_unsolicited_events ( MM_IFACE_MODEM_VOICE (self), (GAsyncReadyCallback) parent_voice_cleanup_unsolicited_events_ready, task); return; } } g_task_return_boolean (task, TRUE); g_object_unref (task); } static void modem_voice_setup_unsolicited_events (MMIfaceModemVoice *self, GAsyncReadyCallback callback, gpointer user_data) { common_voice_setup_cleanup_unsolicited_events (MM_BROADBAND_MODEM_QMI (self), TRUE, callback, user_data); } static void modem_voice_cleanup_unsolicited_events (MMIfaceModemVoice *self, GAsyncReadyCallback callback, gpointer user_data) { common_voice_setup_cleanup_unsolicited_events (MM_BROADBAND_MODEM_QMI (self), FALSE, callback, user_data); } /*****************************************************************************/ /* Enable/disable unsolicited events */ static gboolean common_voice_enable_disable_unsolicited_events_finish (MMIfaceModemVoice *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void call_indication_register_ready (QmiClientVoice *client, GAsyncResult *res, GTask *task) { g_autoptr(QmiMessageVoiceIndicationRegisterOutput) output = NULL; GError *error = NULL; output = qmi_client_voice_indication_register_finish (client, res, &error); if (!output) { g_prefix_error (&error, "QMI operation failed: "); g_task_return_error (task, error); } else if (!qmi_message_voice_indication_register_output_get_result (output, &error)) { g_prefix_error (&error, "Couldn't register voice call indications: "); g_task_return_error (task, error); } else g_task_return_boolean (task, TRUE); g_object_unref (task); } static void common_voice_enable_disable_unsolicited_events (MMBroadbandModemQmi *self, gboolean enable, GAsyncReadyCallback callback, gpointer user_data) { g_autoptr(QmiMessageVoiceIndicationRegisterInput) input = NULL; GTask *task; QmiClient *client; if (!mm_shared_qmi_ensure_client (MM_SHARED_QMI (self), QMI_SERVICE_VOICE, &client, callback, user_data)) return; task = g_task_new (self, NULL, callback, user_data); if (enable == self->priv->all_call_status_unsolicited_events_enabled) { mm_obj_dbg (self, "voice unsolicited events already %s; skipping", enable ? "enabled" : "disabled"); g_task_return_boolean (task, TRUE); g_object_unref (task); return; } self->priv->all_call_status_unsolicited_events_enabled = enable; input = qmi_message_voice_indication_register_input_new (); qmi_message_voice_indication_register_input_set_call_notification_events (input, enable, NULL); qmi_message_voice_indication_register_input_set_supplementary_service_notification_events (input, enable, NULL); qmi_client_voice_indication_register (QMI_CLIENT_VOICE (client), input, 10, NULL, (GAsyncReadyCallback) call_indication_register_ready, task); } static void modem_voice_enable_unsolicited_events (MMIfaceModemVoice *self, GAsyncReadyCallback callback, gpointer user_data) { common_voice_enable_disable_unsolicited_events (MM_BROADBAND_MODEM_QMI (self), TRUE, callback, user_data); } static void modem_voice_disable_unsolicited_events (MMIfaceModemVoice *self, GAsyncReadyCallback callback, gpointer user_data) { common_voice_enable_disable_unsolicited_events (MM_BROADBAND_MODEM_QMI (self), FALSE, callback, user_data); } /*****************************************************************************/ /* Setup/cleanup in-call unsolicited events */ static gboolean common_voice_setup_cleanup_in_call_unsolicited_events_finish (MMIfaceModemVoice *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void common_voice_setup_in_call_cleanup_unsolicited_events (MMBroadbandModemQmi *self, gboolean setup, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; QmiClient *client = NULL; if (!mm_shared_qmi_ensure_client (MM_SHARED_QMI (self), QMI_SERVICE_VOICE, &client, callback, user_data)) return; task = g_task_new (self, NULL, callback, user_data); if (setup == self->priv->supplementary_service_unsolicited_events_setup) { mm_obj_dbg (self, "Supplementary service unsolicited events already %s; skipping", setup ? "setup" : "cleanup"); g_task_return_boolean (task, TRUE); g_object_unref (task); return; } self->priv->supplementary_service_unsolicited_events_setup = setup; if (setup) { g_assert (self->priv->supplementary_service_indication_id == 0); self->priv->supplementary_service_indication_id = g_signal_connect (client, "supplementary-service", G_CALLBACK (supplementary_service_indication_cb), self); } else { g_assert (self->priv->supplementary_service_indication_id != 0); g_signal_handler_disconnect (client, self->priv->supplementary_service_indication_id); self->priv->supplementary_service_indication_id = 0; } g_task_return_boolean (task, TRUE); g_object_unref (task); } static void modem_voice_setup_in_call_unsolicited_events (MMIfaceModemVoice *self, GAsyncReadyCallback callback, gpointer user_data) { common_voice_setup_in_call_cleanup_unsolicited_events (MM_BROADBAND_MODEM_QMI (self), TRUE, callback, user_data); } static void modem_voice_cleanup_in_call_unsolicited_events (MMIfaceModemVoice *self, GAsyncReadyCallback callback, gpointer user_data) { common_voice_setup_in_call_cleanup_unsolicited_events (MM_BROADBAND_MODEM_QMI (self), FALSE, callback, user_data); } /*****************************************************************************/ /* Load full list of calls (Voice interface) */ static gboolean modem_voice_load_call_list_finish (MMIfaceModemVoice *self, GAsyncResult *res, GList **out_call_info_list, GError **error) { GList *call_info_list; GError *inner_error = NULL; call_info_list = g_task_propagate_pointer (G_TASK (res), &inner_error); if (inner_error) { g_assert (!call_info_list); g_propagate_error (error, inner_error); return FALSE; } *out_call_info_list = call_info_list; return TRUE; } static gboolean process_get_all_call_info (QmiClientVoice *client, QmiMessageVoiceGetAllCallInfoOutput *output, GList **out_call_info_list, GError **error) { GArray *qmi_remote_party_number_list = NULL; GArray *qmi_call_information_list = NULL; GList *call_info_list = NULL; guint i; guint j; /* If TLVs missing, report an error */ if (!qmi_message_voice_get_all_call_info_output_get_remote_party_number (output, &qmi_remote_party_number_list, NULL) || !qmi_message_voice_get_all_call_info_output_get_call_information (output, &qmi_call_information_list, NULL)) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS, "Remote party number or call information not available"); return FALSE; } /* If there are no ongoing calls, the lists will be NULL */ if (!qmi_remote_party_number_list || !qmi_call_information_list) { *out_call_info_list = NULL; return TRUE; } for (i = 0; i < qmi_call_information_list->len; i++) { QmiMessageVoiceGetAllCallInfoOutputCallInformationCall qmi_call_information; qmi_call_information = g_array_index (qmi_call_information_list, QmiMessageVoiceGetAllCallInfoOutputCallInformationCall, i); for (j = 0; j < qmi_remote_party_number_list->len; j++) { QmiMessageVoiceGetAllCallInfoOutputRemotePartyNumberCall qmi_remote_party_number; qmi_remote_party_number = g_array_index (qmi_remote_party_number_list, QmiMessageVoiceGetAllCallInfoOutputRemotePartyNumberCall, j); if (qmi_call_information.id == qmi_remote_party_number.id) { MMCallInfo *call_info; call_info = g_slice_new0 (MMCallInfo); call_info->index = qmi_call_information.id; call_info->number = g_strdup (qmi_remote_party_number.type); switch (qmi_call_information.state) { case QMI_VOICE_CALL_STATE_UNKNOWN: call_info->state = MM_CALL_STATE_UNKNOWN; break; case QMI_VOICE_CALL_STATE_ORIGINATION: case QMI_VOICE_CALL_STATE_CC_IN_PROGRESS: call_info->state = MM_CALL_STATE_DIALING; break; case QMI_VOICE_CALL_STATE_ALERTING: call_info->state = MM_CALL_STATE_RINGING_OUT; break; case QMI_VOICE_CALL_STATE_SETUP: case QMI_VOICE_CALL_STATE_INCOMING: call_info->state = MM_CALL_STATE_RINGING_IN; break; case QMI_VOICE_CALL_STATE_CONVERSATION: call_info->state = MM_CALL_STATE_ACTIVE; break; case QMI_VOICE_CALL_STATE_HOLD: call_info->state = MM_CALL_STATE_HELD; break; case QMI_VOICE_CALL_STATE_WAITING: call_info->state = MM_CALL_STATE_WAITING; break; case QMI_VOICE_CALL_STATE_DISCONNECTING: case QMI_VOICE_CALL_STATE_END: call_info->state = MM_CALL_STATE_TERMINATED; break; default: call_info->state = MM_CALL_STATE_UNKNOWN; break; } switch (qmi_call_information.direction) { case QMI_VOICE_CALL_DIRECTION_UNKNOWN: call_info->direction = MM_CALL_DIRECTION_UNKNOWN; break; case QMI_VOICE_CALL_DIRECTION_MO: call_info->direction = MM_CALL_DIRECTION_OUTGOING; break; case QMI_VOICE_CALL_DIRECTION_MT: call_info->direction = MM_CALL_DIRECTION_INCOMING; break; default: call_info->direction = MM_CALL_DIRECTION_UNKNOWN; break; } call_info_list = g_list_append (call_info_list, call_info); } } } *out_call_info_list = call_info_list; return TRUE; } static void modem_voice_load_call_list_ready (QmiClientVoice *client, GAsyncResult *res, GTask *task) { g_autoptr(QmiMessageVoiceGetAllCallInfoOutput) output = NULL; GError *error = NULL; GList *call_info_list = NULL; /* Parse QMI message */ output = qmi_client_voice_get_all_call_info_finish (client, res, &error); if (!output) { g_prefix_error (&error, "QMI operation failed: "); g_task_return_error (task, error); } else if (!qmi_message_voice_get_all_call_info_output_get_result (output, &error)) { g_prefix_error (&error, "Couldn't run Get All Call Info action: "); g_task_return_error (task, error); } else if (!process_get_all_call_info (client, output, &call_info_list, &error)) { g_prefix_error (&error, "Couldn't process Get All Call Info action: "); g_task_return_error (task, error); } else g_task_return_pointer (task, call_info_list, (GDestroyNotify)mm_3gpp_call_info_list_free); g_object_unref (task); } static void modem_voice_load_call_list (MMIfaceModemVoice *self, GAsyncReadyCallback callback, gpointer user_data) { QmiClient *client = NULL; GTask *task; if (!mm_shared_qmi_ensure_client (MM_SHARED_QMI (self), QMI_SERVICE_VOICE, &client, callback, user_data)) return; task = g_task_new (self, NULL, callback, user_data); /* Update call list through QMI instead of AT+CLCC */ qmi_client_voice_get_all_call_info (QMI_CLIENT_VOICE (client), NULL, /* no input data */ 10, NULL, (GAsyncReadyCallback) modem_voice_load_call_list_ready, task); } /*****************************************************************************/ /* Create CALL (Voice interface) */ static MMBaseCall * modem_voice_create_call (MMIfaceModemVoice *self, MMCallDirection direction, const gchar *number) { return mm_call_qmi_new (MM_BASE_MODEM (self), direction, number); } /*****************************************************************************/ /* Common manage calls (Voice interface) */ static gboolean common_manage_calls_finish (MMIfaceModemVoice *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void common_manage_calls_ready (QmiClientVoice *client, GAsyncResult *res, GTask *task) { g_autoptr(QmiMessageVoiceManageCallsOutput) output = NULL; GError *error = NULL; output = qmi_client_voice_manage_calls_finish (client, res, &error); if (!output) { g_prefix_error (&error, "QMI operation failed: "); g_task_return_error (task, error); } else if (!qmi_message_voice_manage_calls_output_get_result (output, &error)) { g_prefix_error (&error, "Couldn't process manage calls action: "); g_task_return_error (task, error); } else g_task_return_boolean (task, TRUE); g_object_unref (task); } static void common_manage_calls (MMIfaceModemVoice *self, GAsyncReadyCallback callback, gpointer user_data, QmiMessageVoiceManageCallsInput *input) { GTask *task; QmiClient *client; if (!mm_shared_qmi_ensure_client (MM_SHARED_QMI (self), QMI_SERVICE_VOICE, &client, callback, user_data)) return; task = g_task_new (self, NULL, callback, user_data); if (!input) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS, "Cannot perform call management operation: invalid input"); g_object_unref (task); return; } qmi_client_voice_manage_calls (QMI_CLIENT_VOICE (client), input, 10, NULL, (GAsyncReadyCallback) common_manage_calls_ready, task); } /*****************************************************************************/ /* Hold and accept (Voice interface) */ static gboolean modem_voice_hold_and_accept_finish (MMIfaceModemVoice *self, GAsyncResult *res, GError **error) { return common_manage_calls_finish (self, res, error); } static void modem_voice_hold_and_accept (MMIfaceModemVoice *self, GAsyncReadyCallback callback, gpointer user_data) { g_autoptr(QmiMessageVoiceManageCallsInput) input = NULL; input = qmi_message_voice_manage_calls_input_new (); qmi_message_voice_manage_calls_input_set_service_type ( input, QMI_VOICE_SUPPLEMENTARY_SERVICE_TYPE_HOLD_ACTIVE_ACCEPT_WAITING_OR_HELD, NULL); common_manage_calls (self, callback, user_data, input); } /*****************************************************************************/ /* Hangup and accept (Voice interface) */ static gboolean modem_voice_hangup_and_accept_finish (MMIfaceModemVoice *self, GAsyncResult *res, GError **error) { return common_manage_calls_finish (self, res, error); } static void modem_voice_hangup_and_accept (MMIfaceModemVoice *self, GAsyncReadyCallback callback, gpointer user_data) { g_autoptr(QmiMessageVoiceManageCallsInput) input = NULL; input = qmi_message_voice_manage_calls_input_new (); qmi_message_voice_manage_calls_input_set_service_type ( input, QMI_VOICE_SUPPLEMENTARY_SERVICE_TYPE_RELEASE_ACTIVE_ACCEPT_HELD_OR_WAITING, NULL); common_manage_calls (self, callback, user_data, input); } /*****************************************************************************/ /* Hangup all (Voice interface) */ static gboolean modem_voice_hangup_all_finish (MMIfaceModemVoice *self, GAsyncResult *res, GError **error) { return common_manage_calls_finish (self, res, error); } static void modem_voice_hangup_all (MMIfaceModemVoice *self, GAsyncReadyCallback callback, gpointer user_data) { g_autoptr(QmiMessageVoiceManageCallsInput) input = NULL; input = qmi_message_voice_manage_calls_input_new (); qmi_message_voice_manage_calls_input_set_service_type ( input, QMI_VOICE_SUPPLEMENTARY_SERVICE_TYPE_END_ALL_CALLS, NULL); common_manage_calls (self, callback, user_data, input); } /*****************************************************************************/ /* Join multiparty (Voice interface) */ static gboolean modem_voice_join_multiparty_finish (MMIfaceModemVoice *self, GAsyncResult *res, GError **error) { return common_manage_calls_finish (self, res, error); } static void modem_voice_join_multiparty (MMIfaceModemVoice *self, GAsyncReadyCallback callback, gpointer user_data) { g_autoptr(QmiMessageVoiceManageCallsInput) input = NULL; input = qmi_message_voice_manage_calls_input_new (); qmi_message_voice_manage_calls_input_set_service_type ( input, QMI_VOICE_SUPPLEMENTARY_SERVICE_TYPE_MAKE_CONFERENCE_CALL, NULL); common_manage_calls (self, callback, user_data, input); } /*****************************************************************************/ /* Leave multiparty (Voice interface) */ static gboolean modem_voice_leave_multiparty_finish (MMIfaceModemVoice *self, GAsyncResult *res, GError **error) { return common_manage_calls_finish (self, res, error); } static void modem_voice_leave_multiparty (MMIfaceModemVoice *self, MMBaseCall *call, GAsyncReadyCallback callback, gpointer user_data) { g_autoptr(QmiMessageVoiceManageCallsInput) input = NULL; guint idx = 0; idx = mm_base_call_get_index (call); if (idx != 0) { input = qmi_message_voice_manage_calls_input_new (); qmi_message_voice_manage_calls_input_set_call_id ( input, idx, NULL ); qmi_message_voice_manage_calls_input_set_service_type ( input, QMI_VOICE_SUPPLEMENTARY_SERVICE_TYPE_HOLD_ALL_EXCEPT_SPECIFIED_CALL, NULL); } common_manage_calls (self, callback, user_data, input); } /*****************************************************************************/ /* Transfer (Voice interface) */ static gboolean modem_voice_transfer_finish (MMIfaceModemVoice *self, GAsyncResult *res, GError **error) { return common_manage_calls_finish (self, res, error); } static void modem_voice_transfer (MMIfaceModemVoice *self, GAsyncReadyCallback callback, gpointer user_data) { g_autoptr(QmiMessageVoiceManageCallsInput) input = NULL; input = qmi_message_voice_manage_calls_input_new (); qmi_message_voice_manage_calls_input_set_service_type ( input, QMI_VOICE_SUPPLEMENTARY_SERVICE_TYPE_EXPLICIT_CALL_TRANSFER, NULL); common_manage_calls (self, callback, user_data, input); } /*****************************************************************************/ /* Call waiting setup (Voice interface) */ static gboolean modem_voice_call_waiting_setup_finish (MMIfaceModemVoice *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void call_waiting_setup_ready (QmiClientVoice *client, GAsyncResult *res, GTask *task) { g_autoptr(QmiMessageVoiceSetSupplementaryServiceOutput) output = NULL; GError *error = NULL; output = qmi_client_voice_set_supplementary_service_finish (client, res, &error); if (!output) { g_prefix_error (&error, "QMI operation failed: "); g_task_return_error (task, error); } else if (!qmi_message_voice_set_supplementary_service_output_get_result (output, &error)) { g_prefix_error (&error, "Couldn't setup call waiting: "); g_task_return_error (task, error); } else g_task_return_boolean (task, TRUE); g_object_unref (task); } static void modem_voice_call_waiting_setup (MMIfaceModemVoice *self, gboolean enable, GAsyncReadyCallback callback, gpointer user_data) { g_autoptr(QmiMessageVoiceSetSupplementaryServiceInput) input = NULL; GTask *task; QmiClient *client; if (!mm_shared_qmi_ensure_client (MM_SHARED_QMI (self), QMI_SERVICE_VOICE, &client, callback, user_data)) return; task = g_task_new (self, NULL, callback, user_data); input = qmi_message_voice_set_supplementary_service_input_new (); qmi_message_voice_set_supplementary_service_input_set_supplementary_service_information ( input, enable ? QMI_VOICE_SUPPLEMENTARY_SERVICE_ACTION_ACTIVATE : QMI_VOICE_SUPPLEMENTARY_SERVICE_ACTION_DEACTIVATE, QMI_VOICE_SUPPLEMENTARY_SERVICE_REASON_CALL_WAITING, NULL); qmi_client_voice_set_supplementary_service (QMI_CLIENT_VOICE (client), input, 30, NULL, (GAsyncReadyCallback) call_waiting_setup_ready, task); } /*****************************************************************************/ /* Call waiting query (Voice interface) */ typedef enum { CLASS_NONE = 0x00, CLASS_VOICE = 0x01, CLASS_DATA = 0x02, CLASS_FAX = 0x04, CLASS_SMS = 0x08, CLASS_DATACIRCUITSYNC = 0x10, CLASS_DATACIRCUITASYNC = 0x20, CLASS_PACKETACCESS = 0x40, CLASS_PADACCESS = 0x80 } SupplementaryServiceInformationClass; typedef enum { ALL_TELESERVICES = CLASS_VOICE + CLASS_FAX + CLASS_SMS, ALL_DATA_TELESERVICES = CLASS_FAX + CLASS_SMS, ALL_TELESERVICES_EXCEPT_SMS = CLASS_VOICE + CLASS_FAX, ALL_BEARER_SERVICES = CLASS_DATACIRCUITSYNC + CLASS_DATACIRCUITASYNC, ALL_ASYNC_SERVICES = CLASS_DATACIRCUITASYNC + CLASS_PACKETACCESS, ALL_SYNC_SERVICES = CLASS_DATACIRCUITSYNC + CLASS_PACKETACCESS, ALL_SYNC_SERVICES_AND_TELEPHONY = CLASS_DATACIRCUITSYNC + CLASS_VOICE } SupplementaryServiceInformationClassCombination; static gboolean modem_voice_call_waiting_query_finish (MMIfaceModemVoice *self, GAsyncResult *res, gboolean *status, GError **error) { gboolean ret; GError *inner_error = NULL; ret = g_task_propagate_boolean (G_TASK (res), &inner_error); if (inner_error) { g_propagate_error (error, inner_error); return FALSE; } *status = ret; return TRUE; } static void call_wait_query_ready (QmiClientVoice *client, GAsyncResult *res, GTask *task) { g_autoptr(QmiMessageVoiceGetCallWaitingOutput) output = NULL; GError *error = NULL; output = qmi_client_voice_get_call_waiting_finish (client, res, &error); if (!output) { g_prefix_error (&error, "QMI operation failed: "); g_task_return_error (task, error); } else if (!qmi_message_voice_get_call_waiting_output_get_result (output, &error)) { g_prefix_error (&error, "Couldn't query call waiting: "); g_task_return_error (task, error); } else { guint8 service_class = 0; if (!qmi_message_voice_get_call_waiting_output_get_service_class (output, &service_class, &error)) { g_prefix_error (&error, "Couldn't get call waiting service class: "); g_task_return_error (task, error); } else if (service_class == CLASS_VOICE || service_class == ALL_TELESERVICES || service_class == ALL_TELESERVICES_EXCEPT_SMS || service_class == ALL_SYNC_SERVICES_AND_TELEPHONY) { g_task_return_boolean (task, TRUE); } else { g_task_return_boolean (task, FALSE); } } g_object_unref (task); } static void modem_voice_call_waiting_query (MMIfaceModemVoice *self, GAsyncReadyCallback callback, gpointer user_data) { g_autoptr(QmiMessageVoiceGetCallWaitingInput) input = NULL; GTask *task; QmiClient *client; if (!mm_shared_qmi_ensure_client (MM_SHARED_QMI (self), QMI_SERVICE_VOICE, &client, callback, user_data)) return; task = g_task_new (self, NULL, callback, user_data); input = qmi_message_voice_get_call_waiting_input_new (); qmi_client_voice_get_call_waiting (QMI_CLIENT_VOICE (client), input, 30, NULL, (GAsyncReadyCallback) call_wait_query_ready, task); } /*****************************************************************************/ /* Initial EPS bearer info loading */ static MMBearerProperties * modem_3gpp_load_initial_eps_bearer_finish (MMIfaceModem3gpp *self, GAsyncResult *res, GError **error) { return MM_BEARER_PROPERTIES (g_task_propagate_pointer (G_TASK (res), error)); } static void get_lte_attach_parameters_ready (QmiClientWds *client, GAsyncResult *res, GTask *task) { g_autoptr(QmiMessageWdsGetLteAttachParametersOutput) output = NULL; GError *error = NULL; MMBearerProperties *properties; const gchar *apn; QmiWdsIpSupportType ip_support_type; output = qmi_client_wds_get_lte_attach_parameters_finish (client, res, &error); if (!output) { g_prefix_error (&error, "QMI operation failed: "); g_task_return_error (task, error); g_object_unref (task); return; } if (!qmi_message_wds_get_lte_attach_parameters_output_get_result (output, &error)) { g_prefix_error (&error, "Couldn't get LTE attach parameters: "); g_task_return_error (task, error); g_object_unref (task); return; } properties = mm_bearer_properties_new (); if (qmi_message_wds_get_lte_attach_parameters_output_get_apn (output, &apn, NULL)) mm_bearer_properties_set_apn (properties, apn); if (qmi_message_wds_get_lte_attach_parameters_output_get_ip_support_type (output, &ip_support_type, NULL)) { MMBearerIpFamily ip_family; ip_family = mm_bearer_ip_family_from_qmi_ip_support_type (ip_support_type); if (ip_family != MM_BEARER_IP_FAMILY_NONE) mm_bearer_properties_set_ip_type (properties, ip_family); } g_task_return_pointer (task, properties, g_object_unref); g_object_unref (task); } static void modem_3gpp_load_initial_eps_bearer (MMIfaceModem3gpp *self, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; QmiClient *client; if (!mm_shared_qmi_ensure_client (MM_SHARED_QMI (self), QMI_SERVICE_WDS, &client, callback, user_data)) return; task = g_task_new (self, NULL, callback, user_data); qmi_client_wds_get_lte_attach_parameters (QMI_CLIENT_WDS (client), NULL, 10, NULL, (GAsyncReadyCallback) get_lte_attach_parameters_ready, task); } /*****************************************************************************/ /* Initial EPS bearer settings setting */ typedef enum { SET_INITIAL_EPS_BEARER_SETTINGS_STEP_FIRST, SET_INITIAL_EPS_BEARER_SETTINGS_STEP_LOAD_POWER_STATE, SET_INITIAL_EPS_BEARER_SETTINGS_STEP_POWER_DOWN, SET_INITIAL_EPS_BEARER_SETTINGS_STEP_MODIFY_PROFILE, SET_INITIAL_EPS_BEARER_SETTINGS_STEP_POWER_UP, SET_INITIAL_EPS_BEARER_SETTINGS_STEP_LAST_SETTING, } SetInitialEpsBearerSettingsStep; typedef struct { SetInitialEpsBearerSettingsStep step; MM3gppProfile *profile; MMModemPowerState power_state; } SetInitialEpsBearerSettingsContext; static void set_initial_eps_bearer_settings_context_free (SetInitialEpsBearerSettingsContext *ctx) { g_clear_object (&ctx->profile); g_slice_free (SetInitialEpsBearerSettingsContext, ctx); } static gboolean modem_3gpp_set_initial_eps_bearer_settings_finish (MMIfaceModem3gpp *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void set_initial_eps_bearer_settings_step (GTask *task); static void set_initial_eps_bearer_power_up_ready (MMIfaceModem *self, GAsyncResult *res, GTask *task) { SetInitialEpsBearerSettingsContext *ctx; GError *error = NULL; ctx = g_task_get_task_data (task); if (!modem_power_up_down_off_finish (self, res, &error)) { g_prefix_error (&error, "Couldn't power up modem: "); g_task_return_error (task, error); g_object_unref (task); return; } ctx->step++; set_initial_eps_bearer_settings_step (task); } static void set_initial_eps_bearer_modify_profile_ready (MMIfaceModem3gppProfileManager *self, GAsyncResult *res, GTask *task) { GError *error = NULL; SetInitialEpsBearerSettingsContext *ctx; g_autoptr(MM3gppProfile) stored = NULL; ctx = g_task_get_task_data (task); stored = mm_iface_modem_3gpp_profile_manager_set_profile_finish (self, res, &error); if (!stored) { g_task_return_error (task, error); g_object_unref (task); return; } ctx->step++; set_initial_eps_bearer_settings_step (task); } static void set_initial_eps_bearer_modify_profile (GTask *task) { MMBroadbandModemQmi *self; SetInitialEpsBearerSettingsContext *ctx; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); mm_iface_modem_3gpp_profile_manager_set_profile (MM_IFACE_MODEM_3GPP_PROFILE_MANAGER (self), ctx->profile, "profile-id", TRUE, (GAsyncReadyCallback)set_initial_eps_bearer_modify_profile_ready, task); } static void set_initial_eps_bearer_power_down_ready (MMIfaceModem *self, GAsyncResult *res, GTask *task) { SetInitialEpsBearerSettingsContext *ctx; GError *error = NULL; ctx = g_task_get_task_data (task); if (!modem_power_up_down_off_finish (self, res, &error)) { g_prefix_error (&error, "Couldn't power down modem: "); g_task_return_error (task, error); g_object_unref (task); return; } ctx->step++; set_initial_eps_bearer_settings_step (task); } static void set_initial_eps_bearer_load_power_state_ready (MMIfaceModem *self, GAsyncResult *res, GTask *task) { SetInitialEpsBearerSettingsContext *ctx; GError *error = NULL; ctx = g_task_get_task_data (task); ctx->power_state = load_power_state_finish (self, res, &error); if (ctx->power_state == MM_MODEM_POWER_STATE_UNKNOWN) { g_prefix_error (&error, "Couldn't load power state: "); g_task_return_error (task, error); g_object_unref (task); return; } ctx->step++; set_initial_eps_bearer_settings_step (task); } static void set_initial_eps_bearer_settings_step (GTask *task) { SetInitialEpsBearerSettingsContext *ctx; MMBroadbandModemQmi *self; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); switch (ctx->step) { case SET_INITIAL_EPS_BEARER_SETTINGS_STEP_FIRST: ctx->step++; /* fall through */ case SET_INITIAL_EPS_BEARER_SETTINGS_STEP_LOAD_POWER_STATE: mm_obj_dbg (self, "querying current power state..."); load_power_state (MM_IFACE_MODEM (self), (GAsyncReadyCallback) set_initial_eps_bearer_load_power_state_ready, task); return; case SET_INITIAL_EPS_BEARER_SETTINGS_STEP_POWER_DOWN: if (ctx->power_state == MM_MODEM_POWER_STATE_ON) { mm_obj_dbg (self, "powering down before changing initial EPS bearer settings..."); modem_power_down (MM_IFACE_MODEM (self), (GAsyncReadyCallback) set_initial_eps_bearer_power_down_ready, task); return; } ctx->step++; /* fall through */ case SET_INITIAL_EPS_BEARER_SETTINGS_STEP_MODIFY_PROFILE: mm_obj_dbg (self, "modifying initial EPS bearer settings profile..."); set_initial_eps_bearer_modify_profile (task); return; case SET_INITIAL_EPS_BEARER_SETTINGS_STEP_POWER_UP: if (ctx->power_state == MM_MODEM_POWER_STATE_ON) { mm_obj_dbg (self, "powering up after changing initial EPS bearer settings..."); modem_power_up (MM_IFACE_MODEM (self), (GAsyncReadyCallback) set_initial_eps_bearer_power_up_ready, task); return; } ctx->step++; /* fall through */ case SET_INITIAL_EPS_BEARER_SETTINGS_STEP_LAST_SETTING: g_task_return_boolean (task, TRUE); g_object_unref (task); return; default: g_assert_not_reached (); } } static void modem_3gpp_set_initial_eps_bearer_settings (MMIfaceModem3gpp *_self, MMBearerProperties *config, GAsyncReadyCallback callback, gpointer user_data) { MMBroadbandModemQmi *self = MM_BROADBAND_MODEM_QMI (_self); SetInitialEpsBearerSettingsContext *ctx; GTask *task; MM3gppProfile *profile; task = g_task_new (self, NULL, callback, user_data); if (!self->priv->default_attach_pdn) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Unknown default LTE attach APN index"); g_object_unref (task); return; } profile = mm_bearer_properties_peek_3gpp_profile (config); mm_3gpp_profile_set_profile_id (profile, self->priv->default_attach_pdn); ctx = g_slice_new0 (SetInitialEpsBearerSettingsContext); ctx->profile = g_object_ref (profile); ctx->step = SET_INITIAL_EPS_BEARER_SETTINGS_STEP_FIRST; g_task_set_task_data (task, ctx, (GDestroyNotify) set_initial_eps_bearer_settings_context_free); set_initial_eps_bearer_settings_step (task); } /*****************************************************************************/ /* Initial EPS bearer settings loading */ static MMBearerProperties * modem_3gpp_load_initial_eps_bearer_settings_finish (MMIfaceModem3gpp *self, GAsyncResult *res, GError **error) { return MM_BEARER_PROPERTIES (g_task_propagate_pointer (G_TASK (res), error)); } static void load_initial_eps_bearer_get_profile_ready (MMIfaceModem3gppProfileManager *_self, GAsyncResult *res, GTask *task) { GError *error = NULL; g_autoptr(MM3gppProfile) profile = NULL; MMBearerProperties *properties; profile = mm_iface_modem_3gpp_profile_manager_get_profile_finish (_self, res, &error); if (!profile) { g_task_return_error (task, error); g_object_unref (task); return; } properties = mm_bearer_properties_new_from_profile (profile, &error); if (!properties) g_task_return_error (task, error); else g_task_return_pointer (task, properties, g_object_unref); g_object_unref (task); } static void load_initial_eps_bearer_get_profile_settings (GTask *task) { MMBroadbandModemQmi *self; self = g_task_get_source_object (task); mm_iface_modem_3gpp_profile_manager_get_profile ( MM_IFACE_MODEM_3GPP_PROFILE_MANAGER (self), self->priv->default_attach_pdn, (GAsyncReadyCallback)load_initial_eps_bearer_get_profile_ready, task); } static void load_initial_eps_bearer_get_lte_attach_pdn_list_ready (QmiClientWds *client, GAsyncResult *res, GTask *task) { g_autoptr(QmiMessageWdsGetLteAttachPdnListOutput) output = NULL; MMBroadbandModemQmi *self; GError *error = NULL; GArray *current_list = NULL; guint i; self = g_task_get_source_object (task); output = qmi_client_wds_get_lte_attach_pdn_list_finish (client, res, &error); if (!output) { g_prefix_error (&error, "QMI operation failed: "); g_task_return_error (task, error); g_object_unref (task); return; } if (!qmi_message_wds_get_lte_attach_pdn_list_output_get_result (output, &error)) { g_prefix_error (&error, "Couldn't get LTE attach PDN list: "); g_task_return_error (task, error); g_object_unref (task); return; } qmi_message_wds_get_lte_attach_pdn_list_output_get_current_list (output, ¤t_list, NULL); if (!current_list || !current_list->len) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Undefined list of LTE attach PDN"); g_object_unref (task); return; } mm_obj_dbg (self, "Found %u LTE attach PDNs defined", current_list->len); for (i = 0; i < current_list->len; i++) { if (i == 0) { self->priv->default_attach_pdn = g_array_index (current_list, guint16, i); mm_obj_dbg (self, "Default LTE attach PDN profile: %u", self->priv->default_attach_pdn); } else mm_obj_dbg (self, "Additional LTE attach PDN profile: %u", g_array_index (current_list, guint16, i)); } load_initial_eps_bearer_get_profile_settings (task); } static void modem_3gpp_load_initial_eps_bearer_settings (MMIfaceModem3gpp *_self, GAsyncReadyCallback callback, gpointer user_data) { MMBroadbandModemQmi *self = MM_BROADBAND_MODEM_QMI (_self); GTask *task; task = g_task_new (self, NULL, callback, user_data); /* Default attach PDN is assumed to never change during runtime * (we don't change it) so just load it the first time */ if (!self->priv->default_attach_pdn) { QmiClient *client; GError *error = NULL; client = mm_shared_qmi_peek_client (MM_SHARED_QMI (self), QMI_SERVICE_WDS, MM_PORT_QMI_FLAG_DEFAULT, &error); if (!client) { g_task_return_error (task, error); g_object_unref (task); return; } mm_obj_dbg (self, "querying LTE attach PDN list..."); qmi_client_wds_get_lte_attach_pdn_list (QMI_CLIENT_WDS (client), NULL, 10, NULL, (GAsyncReadyCallback)load_initial_eps_bearer_get_lte_attach_pdn_list_ready, task); return; } load_initial_eps_bearer_get_profile_settings (task); } /*****************************************************************************/ /* Check firmware support (Firmware interface) */ typedef struct { gchar *build_id; GArray *modem_unique_id; GArray *pri_unique_id; gboolean current; } FirmwarePair; static void firmware_pair_free (FirmwarePair *pair) { g_free (pair->build_id); g_array_unref (pair->modem_unique_id); g_array_unref (pair->pri_unique_id); g_slice_free (FirmwarePair, pair); } typedef struct { QmiClientDms *client; GList *pairs; FirmwarePair *current_pair; MMFirmwareProperties *current_firmware; gboolean skip_image_info; } FirmwareListPreloadContext; static void firmware_list_preload_context_free (FirmwareListPreloadContext *ctx) { g_clear_object (&ctx->current_firmware); g_clear_pointer (&ctx->current_pair, (GDestroyNotify)firmware_pair_free); g_list_free_full (ctx->pairs, (GDestroyNotify)firmware_pair_free); g_object_unref (ctx->client); g_slice_free (FirmwareListPreloadContext, ctx); } static gboolean firmware_list_preload_finish (MMBroadbandModemQmi *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void store_preloaded_firmware_image_info (MMBroadbandModemQmi *self, MMFirmwareProperties *firmware, gboolean running) { self->priv->firmware_list = g_list_append (self->priv->firmware_list, g_object_ref (firmware)); /* If this is is also the running image, keep an extra reference to it */ if (running) { if (self->priv->current_firmware) mm_obj_warn (self, "a running firmware is already set (%s), not setting '%s'", mm_firmware_properties_get_unique_id (self->priv->current_firmware), mm_firmware_properties_get_unique_id (firmware)); else self->priv->current_firmware = g_object_ref (firmware); } } static void get_next_image_info (GTask *task); static void get_pri_image_info_ready (QmiClientDms *client, GAsyncResult *res, GTask *task) { MMBroadbandModemQmi *self; FirmwareListPreloadContext *ctx; QmiMessageDmsGetStoredImageInfoOutput *output; GError *error = NULL; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); g_assert (ctx->current_pair); g_assert (ctx->current_firmware); output = qmi_client_dms_get_stored_image_info_finish (client, res, &error); if (!output || !qmi_message_dms_get_stored_image_info_output_get_result (output, &error)) { if (g_error_matches (error, QMI_PROTOCOL_ERROR, QMI_PROTOCOL_ERROR_INVALID_QMI_COMMAND)) ctx->skip_image_info = TRUE; else mm_obj_dbg (self, "couldn't get detailed info for PRI image with build ID '%s': %s", ctx->current_pair->build_id, error->message); g_error_free (error); goto out; } /* Boot version (optional) */ { guint16 boot_major_version; guint16 boot_minor_version; if (qmi_message_dms_get_stored_image_info_output_get_boot_version ( output, &boot_major_version, &boot_minor_version, NULL)) { gchar *aux; aux = g_strdup_printf ("%u.%u", boot_major_version, boot_minor_version); mm_firmware_properties_set_gobi_boot_version (ctx->current_firmware, aux); g_free (aux); } } /* PRI version (optional) */ { guint32 pri_version; const gchar *pri_info; if (qmi_message_dms_get_stored_image_info_output_get_pri_version ( output, &pri_version, &pri_info, NULL)) { gchar *aux; aux = g_strdup_printf ("%u", pri_version); mm_firmware_properties_set_gobi_pri_version (ctx->current_firmware, aux); g_free (aux); mm_firmware_properties_set_gobi_pri_info (ctx->current_firmware, pri_info); } } out: /* We're done with this image */ store_preloaded_firmware_image_info (self, ctx->current_firmware, ctx->current_pair->current); g_clear_object (&ctx->current_firmware); g_clear_pointer (&ctx->current_pair, (GDestroyNotify)firmware_pair_free); /* Go on to the next one */ get_next_image_info (task); if (output) qmi_message_dms_get_stored_image_info_output_unref (output); } static MMFirmwareProperties * create_firmware_properties_from_pair (FirmwarePair *pair, GError **error) { gchar *pri_unique_id_str = NULL; gchar *modem_unique_id_str = NULL; gchar *firmware_unique_id_str = NULL; MMFirmwareProperties *firmware = NULL; /* If the string is ASCII, use it without converting to HEX */ pri_unique_id_str = mm_qmi_unique_id_to_firmware_unique_id (pair->pri_unique_id, error); if (!pri_unique_id_str) goto out; modem_unique_id_str = mm_qmi_unique_id_to_firmware_unique_id (pair->modem_unique_id, error); if (!modem_unique_id_str) goto out; /* We will always append the PRI unique ID to the build id to form the unique id * used by the API, because it may happen that a device holds multiple PRI images * for the same build ID. * * E.g. we could have a single modem image (e.g. 02.14.03.00) and then two or more * different PRI images with the same build ID (e.g. 02.14.03.00_VODAFONE) but * different unique IDs (e.g. 000.008_000 and 000.016_000). */ firmware_unique_id_str = g_strdup_printf ("%s_%s", pair->build_id, pri_unique_id_str); firmware = mm_firmware_properties_new (MM_FIRMWARE_IMAGE_TYPE_GOBI, firmware_unique_id_str); mm_firmware_properties_set_gobi_pri_unique_id (firmware, pri_unique_id_str); mm_firmware_properties_set_gobi_modem_unique_id (firmware, modem_unique_id_str); out: g_free (firmware_unique_id_str); g_free (pri_unique_id_str); g_free (modem_unique_id_str); return firmware; } static void get_next_image_info (GTask *task) { MMBroadbandModemQmi *self; FirmwareListPreloadContext *ctx; GError *error = NULL; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); if (!ctx->pairs) { g_task_return_boolean (task, TRUE); g_object_unref (task); return; } /* Take next pair to process from list head */ ctx->current_pair = (FirmwarePair *)ctx->pairs->data; ctx->pairs = g_list_delete_link (ctx->pairs, ctx->pairs); /* Build firmware properties */ ctx->current_firmware = create_firmware_properties_from_pair (ctx->current_pair, &error); if (!ctx->current_firmware) { g_task_return_error (task, error); g_object_unref (task); return; } /* Now, load additional optional information for the PRI image */ if (!ctx->skip_image_info) { g_autoptr(QmiMessageDmsGetStoredImageInfoInput) input = NULL; input = qmi_message_dms_get_stored_image_info_input_new (); qmi_message_dms_get_stored_image_info_input_set_image_details (input, QMI_DMS_FIRMWARE_IMAGE_TYPE_PRI, ctx->current_pair->pri_unique_id, ctx->current_pair->build_id, NULL); qmi_client_dms_get_stored_image_info (ctx->client, input, 10, NULL, (GAsyncReadyCallback)get_pri_image_info_ready, task); return; } /* If we shouldn't be loading additional image info, we're done with this image */ store_preloaded_firmware_image_info (self, ctx->current_firmware, ctx->current_pair->current); g_clear_object (&ctx->current_firmware); g_clear_pointer (&ctx->current_pair, (GDestroyNotify)firmware_pair_free); /* Go on to the next one */ get_next_image_info (task); } static gboolean match_images (const gchar *pri_id, const gchar *modem_id) { gsize modem_id_len; if (!pri_id || !modem_id) return FALSE; if (g_str_equal (pri_id, modem_id)) return TRUE; /* If the Modem image build_id ends in '?' just use a prefix match. eg, * assume that modem="02.08.02.00_?" matches pri="02.08.02.00_ATT" or * pri="02.08.02.00_GENERIC". */ modem_id_len = strlen (modem_id); if (modem_id[modem_id_len - 1] != '?') return FALSE; return strncmp (pri_id, modem_id, modem_id_len - 1) == 0; } static GList * find_image_pairs (MMBroadbandModemQmi *self, QmiMessageDmsListStoredImagesOutputListImage *image_pri, QmiMessageDmsListStoredImagesOutputListImage *image_modem, GError **error) { guint i, j; GList *pairs = NULL; /* Loop PRI images and try to find a pairing MODEM image with same build ID */ for (i = 0; i < image_pri->sublist->len; i++) { QmiMessageDmsListStoredImagesOutputListImageSublistSublistElement *subimage_pri; subimage_pri = &g_array_index (image_pri->sublist, QmiMessageDmsListStoredImagesOutputListImageSublistSublistElement, i); for (j = 0; j < image_modem->sublist->len; j++) { QmiMessageDmsListStoredImagesOutputListImageSublistSublistElement *subimage_modem; subimage_modem = &g_array_index (image_modem->sublist, QmiMessageDmsListStoredImagesOutputListImageSublistSublistElement, j); if (match_images (subimage_pri->build_id, subimage_modem->build_id)) { FirmwarePair *pair; mm_obj_dbg (self, "found pairing PRI+MODEM images with build ID '%s'", subimage_pri->build_id); pair = g_slice_new (FirmwarePair); pair->build_id = g_strdup (subimage_pri->build_id); pair->modem_unique_id = g_array_ref (subimage_modem->unique_id); pair->pri_unique_id = g_array_ref (subimage_pri->unique_id); /* We're using the PRI 'index_of_running_image' only as source to select * which is the current running firmware. This avoids issues with the wrong * 'index_of_running_image' reported for the MODEM images, see: * https://forum.sierrawireless.com/t/mc74xx-wrong-running-image-in-qmi-get-stored-images/8998 */ pair->current = (image_pri->index_of_running_image == i ? TRUE : FALSE); pairs = g_list_append (pairs, pair); break; } } if (j == image_modem->sublist->len) mm_obj_dbg (self, "pairing for PRI image with build ID '%s' not found", subimage_pri->build_id); } if (!pairs) g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_NOT_FOUND, "No valid PRI+MODEM pairs found"); return pairs; } static gboolean find_image_type_indices (MMBroadbandModemQmi *self, GArray *array, QmiMessageDmsListStoredImagesOutputListImage **image_pri, QmiMessageDmsListStoredImagesOutputListImage **image_modem, GError **error) { guint i; g_assert (array); g_assert (image_pri); g_assert (image_modem); *image_pri = NULL; *image_modem = NULL; /* The MODEM image list is usually given before the PRI image list, but * let's not assume that. Try to find both lists and report at which index * we can find each. */ for (i = 0; i < array->len; i++) { QmiMessageDmsListStoredImagesOutputListImage *image; image = &g_array_index (array, QmiMessageDmsListStoredImagesOutputListImage, i); switch (image->type) { case QMI_DMS_FIRMWARE_IMAGE_TYPE_PRI: if (*image_pri != NULL) mm_obj_dbg (self, "multiple array elements found with PRI type: ignoring additional list at index %u", i); else *image_pri = image; break; case QMI_DMS_FIRMWARE_IMAGE_TYPE_MODEM: if (*image_modem != NULL) mm_obj_dbg (self, "multiple array elements found with MODEM type: ignoring additional list at index %u", i); else *image_modem = image; break; default: break; } } if (!(*image_pri) || !(*image_modem)) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_NOT_FOUND, "Missing image list: pri list %s, modem list %s", !(*image_pri) ? "not found" : "found", !(*image_modem) ? "not found" : "found"); return FALSE; } return TRUE; } static void list_stored_images_ready (QmiClientDms *client, GAsyncResult *res, GTask *task) { MMBroadbandModemQmi *self; FirmwareListPreloadContext *ctx; GArray *array; QmiMessageDmsListStoredImagesOutputListImage *image_pri; QmiMessageDmsListStoredImagesOutputListImage *image_modem; QmiMessageDmsListStoredImagesOutput *output; GError *error = NULL; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); /* Read array from output */ output = qmi_client_dms_list_stored_images_finish (client, res, &error); if (!output || !qmi_message_dms_list_stored_images_output_get_result (output, &error) || !qmi_message_dms_list_stored_images_output_get_list (output, &array, &error)) { g_task_return_error (task, error); g_object_unref (task); goto out; } /* Find which index corresponds to each image type */ if (!find_image_type_indices (self, array, &image_pri, &image_modem, &error)) { g_task_return_error (task, error); g_object_unref (task); goto out; } /* Build firmware PRI+MODEM pair list */ ctx->pairs = find_image_pairs (self, image_pri, image_modem, &error); if (!ctx->pairs) { g_task_return_error (task, error); g_object_unref (task); goto out; } /* Now keep on loading info for each image and cache it */ get_next_image_info (task); out: if (output) qmi_message_dms_list_stored_images_output_unref (output); } static void firmware_list_preload (MMBroadbandModemQmi *self, GAsyncReadyCallback callback, gpointer user_data) { FirmwareListPreloadContext *ctx; GTask *task; QmiClient *client = NULL; if (!mm_shared_qmi_ensure_client (MM_SHARED_QMI (self), QMI_SERVICE_DMS, &client, callback, user_data)) return; ctx = g_slice_new0 (FirmwareListPreloadContext); ctx->client = QMI_CLIENT_DMS (g_object_ref (client)); task = g_task_new (self, NULL, callback, user_data); g_task_set_task_data (task, ctx, (GDestroyNotify)firmware_list_preload_context_free); mm_obj_dbg (self, "loading firmware images..."); qmi_client_dms_list_stored_images (QMI_CLIENT_DMS (client), NULL, 10, NULL, (GAsyncReadyCallback)list_stored_images_ready, task); } /*****************************************************************************/ /* Load firmware list (Firmware interface) */ static void firmware_list_free (GList *firmware_list) { g_list_free_full (firmware_list, g_object_unref); } static GList * firmware_load_list_finish (MMIfaceModemFirmware *self, GAsyncResult *res, GError **error) { return g_task_propagate_pointer (G_TASK (res), error); } static void firmware_load_list_preloaded (GTask *task) { MMBroadbandModemQmi *self; GList *dup; self = g_task_get_source_object (task); g_assert (self->priv->firmware_list_preloaded); dup = g_list_copy_deep (self->priv->firmware_list, (GCopyFunc)g_object_ref, NULL); if (dup) g_task_return_pointer (task, dup, (GDestroyNotify)firmware_list_free); else g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_NOT_FOUND, "firmware list unknown"); g_object_unref (task); } static void firmware_list_preload_ready (MMBroadbandModemQmi *self, GAsyncResult *res, GTask *task) { GError *error = NULL; if (!firmware_list_preload_finish (self, res, &error)) { mm_obj_dbg (self, "firmware list loading failed: %s", error ? error->message : "unsupported"); g_clear_error (&error); } firmware_load_list_preloaded (task); } static void firmware_load_list (MMIfaceModemFirmware *_self, GAsyncReadyCallback callback, gpointer user_data) { MMBroadbandModemQmi *self = MM_BROADBAND_MODEM_QMI (_self); GTask *task; task = g_task_new (self, NULL, callback, user_data); if (self->priv->firmware_list_preloaded) { firmware_load_list_preloaded (task); return; } self->priv->firmware_list_preloaded = TRUE; firmware_list_preload (self, (GAsyncReadyCallback)firmware_list_preload_ready, task); } /*****************************************************************************/ /* Load current firmware (Firmware interface) */ static MMFirmwareProperties * firmware_load_current_finish (MMIfaceModemFirmware *self, GAsyncResult *res, GError **error) { return g_task_propagate_pointer (G_TASK (res), error); } static void firmware_load_current (MMIfaceModemFirmware *_self, GAsyncReadyCallback callback, gpointer user_data) { MMBroadbandModemQmi *self = MM_BROADBAND_MODEM_QMI (_self); GTask *task; task = g_task_new (self, NULL, callback, user_data); if (self->priv->current_firmware) g_task_return_pointer (task, g_object_ref (self->priv->current_firmware), g_object_unref); else g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_NOT_FOUND, "current firmware unknown"); g_object_unref (task); } /*****************************************************************************/ /* Change current firmware (Firmware interface) */ typedef struct { MMFirmwareProperties *firmware; } FirmwareChangeCurrentContext; static void firmware_change_current_context_free (FirmwareChangeCurrentContext *ctx) { if (ctx->firmware) g_object_unref (ctx->firmware); g_slice_free (FirmwareChangeCurrentContext, ctx); } static gboolean firmware_change_current_finish (MMIfaceModemFirmware *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void firmware_reset_ready (MMIfaceModem *self, GAsyncResult *res, GTask *task) { GError *error = NULL; if (!mm_shared_qmi_reset_finish (self, res, &error)) g_task_return_error (task, error); else g_task_return_boolean (task, TRUE); g_object_unref (task); } static void firmware_select_stored_image_ready (QmiClientDms *client, GAsyncResult *res, GTask *task) { MMBroadbandModemQmi *self; QmiMessageDmsSetFirmwarePreferenceOutput *output; GError *error = NULL; output = qmi_client_dms_set_firmware_preference_finish (client, res, &error); if (!output) { g_task_return_error (task, error); g_object_unref (task); return; } if (!qmi_message_dms_set_firmware_preference_output_get_result (output, &error)) { g_task_return_error (task, error); g_object_unref (task); qmi_message_dms_set_firmware_preference_output_unref (output); return; } self = g_task_get_source_object (task); qmi_message_dms_set_firmware_preference_output_unref (output); /* Now, go into offline mode */ mm_shared_qmi_reset (MM_IFACE_MODEM (self), (GAsyncReadyCallback)firmware_reset_ready, task); } static MMFirmwareProperties * find_firmware_properties_by_unique_id (MMBroadbandModemQmi *self, const gchar *unique_id) { GList *l; for (l = self->priv->firmware_list; l; l = g_list_next (l)) { if (g_str_equal (mm_firmware_properties_get_unique_id (MM_FIRMWARE_PROPERTIES (l->data)), unique_id)) return g_object_ref (l->data); } return NULL; } static MMFirmwareProperties * find_firmware_properties_by_gobi_pri_info_substring (MMBroadbandModemQmi *self, const gchar *str, guint *n_found) { MMFirmwareProperties *first = NULL; GList *l; *n_found = 0; for (l = self->priv->firmware_list; l; l = g_list_next (l)) { const gchar *pri_info; pri_info = mm_firmware_properties_get_gobi_pri_info (MM_FIRMWARE_PROPERTIES (l->data)); if (pri_info && strstr (pri_info, str)) { if (!first && *n_found == 0) first = g_object_ref (l->data); else g_clear_object (&first); (*n_found)++; } } return first; } static void firmware_change_current (MMIfaceModemFirmware *_self, const gchar *unique_id, GAsyncReadyCallback callback, gpointer user_data) { MMBroadbandModemQmi *self; GTask *task; FirmwareChangeCurrentContext *ctx; GError *error = NULL; QmiClient *client = NULL; GArray *array; QmiMessageDmsSetFirmwarePreferenceInput *input = NULL; QmiMessageDmsSetFirmwarePreferenceInputListImage modem_image_id = { 0 }; QmiMessageDmsSetFirmwarePreferenceInputListImage pri_image_id = { 0 }; self = MM_BROADBAND_MODEM_QMI (_self); if (!mm_shared_qmi_ensure_client (MM_SHARED_QMI (self), QMI_SERVICE_DMS, &client, callback, user_data)) return; ctx = g_slice_new0 (FirmwareChangeCurrentContext); task = g_task_new (self, NULL, callback, user_data); g_task_set_task_data (task, ctx, (GDestroyNotify)firmware_change_current_context_free); /* Look for the firmware image with the requested unique ID */ ctx->firmware = find_firmware_properties_by_unique_id (self, unique_id); if (!ctx->firmware) { guint n = 0; /* Ok, let's look at the PRI info */ ctx->firmware = find_firmware_properties_by_gobi_pri_info_substring (self, unique_id, &n); if (n > 1) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_NOT_FOUND, "Multiple firmware images (%u) found matching '%s' as PRI info substring", n, unique_id); g_object_unref (task); return; } if (n == 0) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_NOT_FOUND, "Firmware with unique ID '%s' wasn't found", unique_id); g_object_unref (task); return; } g_assert (n == 1 && MM_IS_FIRMWARE_PROPERTIES (ctx->firmware)); } /* If we're already in the requested firmware, we're done */ if (self->priv->current_firmware && g_str_equal (mm_firmware_properties_get_unique_id (self->priv->current_firmware), mm_firmware_properties_get_unique_id (ctx->firmware))) { mm_obj_dbg (self, "modem is already running firmware image '%s'", mm_firmware_properties_get_unique_id (self->priv->current_firmware)); g_task_return_boolean (task, TRUE); g_object_unref (task); return; } /* Modem image ID */ modem_image_id.type = QMI_DMS_FIRMWARE_IMAGE_TYPE_MODEM; modem_image_id.build_id = (gchar *)mm_firmware_properties_get_unique_id (ctx->firmware); modem_image_id.unique_id = mm_firmware_unique_id_to_qmi_unique_id (mm_firmware_properties_get_gobi_modem_unique_id (ctx->firmware), &error); if (!modem_image_id.unique_id) { g_prefix_error (&error, "Couldn't build modem image unique id: "); g_task_return_error (task, error); g_object_unref (task); goto out; } /* PRI image ID */ pri_image_id.type = QMI_DMS_FIRMWARE_IMAGE_TYPE_PRI; pri_image_id.build_id = (gchar *)mm_firmware_properties_get_unique_id (ctx->firmware); pri_image_id.unique_id = mm_firmware_unique_id_to_qmi_unique_id (mm_firmware_properties_get_gobi_pri_unique_id (ctx->firmware), &error); if (!pri_image_id.unique_id) { g_prefix_error (&error, "Couldn't build PRI image unique id: "); g_task_return_error (task, error); g_object_unref (task); goto out; } mm_obj_dbg (self, "changing Gobi firmware to MODEM '%s' and PRI '%s' with Build ID '%s'...", mm_firmware_properties_get_gobi_modem_unique_id (ctx->firmware), mm_firmware_properties_get_gobi_pri_unique_id (ctx->firmware), unique_id); /* Build array of image IDs */ array = g_array_sized_new (FALSE, FALSE, sizeof (QmiMessageDmsSetFirmwarePreferenceInputListImage), 2); g_array_append_val (array, modem_image_id); g_array_append_val (array, pri_image_id); input = qmi_message_dms_set_firmware_preference_input_new (); qmi_message_dms_set_firmware_preference_input_set_list (input, array, NULL); g_array_unref (array); qmi_client_dms_set_firmware_preference ( QMI_CLIENT_DMS (client), input, 10, NULL, (GAsyncReadyCallback)firmware_select_stored_image_ready, task); out: if (modem_image_id.unique_id) g_array_unref (modem_image_id.unique_id); if (pri_image_id.unique_id) g_array_unref (pri_image_id.unique_id); if (input) qmi_message_dms_set_firmware_preference_input_unref (input); } /*****************************************************************************/ /* Check support (Signal interface) */ static gboolean signal_check_support_finish (MMIfaceModemSignal *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void signal_check_support (MMIfaceModemSignal *self, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; task = g_task_new (self, NULL, callback, user_data); /* If NAS service is available, assume either signal info or signal strength are supported */ if (!mm_shared_qmi_peek_client (MM_SHARED_QMI (self), QMI_SERVICE_NAS, MM_PORT_QMI_FLAG_DEFAULT, NULL)) { mm_obj_dbg (self, "extended signal capabilities not supported"); g_task_return_boolean (task, FALSE); } else { mm_obj_dbg (self, "extended signal capabilities supported"); g_task_return_boolean (task, TRUE); } g_object_unref (task); } /*****************************************************************************/ /* Load extended signal information */ typedef enum { SIGNAL_LOAD_VALUES_STEP_SIGNAL_FIRST, SIGNAL_LOAD_VALUES_STEP_SIGNAL_INFO, SIGNAL_LOAD_VALUES_STEP_SIGNAL_STRENGTH, SIGNAL_LOAD_VALUES_STEP_SIGNAL_LAST } SignalLoadValuesStep; typedef struct { MMSignal *cdma; MMSignal *evdo; MMSignal *gsm; MMSignal *umts; MMSignal *lte; MMSignal *nr5g; } SignalLoadValuesResult; typedef struct { QmiClientNas *client; SignalLoadValuesStep step; SignalLoadValuesResult *values_result; } SignalLoadValuesContext; static void signal_load_values_result_free (SignalLoadValuesResult *result) { g_clear_object (&result->cdma); g_clear_object (&result->evdo); g_clear_object (&result->gsm); g_clear_object (&result->umts); g_clear_object (&result->lte); g_clear_object (&result->nr5g); g_slice_free (SignalLoadValuesResult, result); } static void signal_load_values_context_free (SignalLoadValuesContext *ctx) { g_clear_pointer (&ctx->values_result, (GDestroyNotify)signal_load_values_result_free); g_slice_free (SignalLoadValuesContext, ctx); } static gboolean signal_load_values_finish (MMIfaceModemSignal *self, GAsyncResult *res, MMSignal **cdma, MMSignal **evdo, MMSignal **gsm, MMSignal **umts, MMSignal **lte, MMSignal **nr5g, GError **error) { SignalLoadValuesResult *values_result; values_result = g_task_propagate_pointer (G_TASK (res), error); if (!values_result) return FALSE; *cdma = values_result->cdma ? g_object_ref (values_result->cdma) : NULL; *evdo = values_result->evdo ? g_object_ref (values_result->evdo) : NULL; *gsm = values_result->gsm ? g_object_ref (values_result->gsm) : NULL; *umts = values_result->umts ? g_object_ref (values_result->umts) : NULL; *lte = values_result->lte ? g_object_ref (values_result->lte) : NULL; *nr5g = values_result->nr5g ? g_object_ref (values_result->nr5g) : NULL; signal_load_values_result_free (values_result); return TRUE; } static void signal_load_values_context_step (GTask *task); static void signal_load_values_get_signal_strength_ready (QmiClientNas *client, GAsyncResult *res, GTask *task) { MMBroadbandModemQmi *self; SignalLoadValuesContext *ctx; GArray *array; gint32 aux_int32; gint16 aux_int16; gint8 aux_int8; QmiNasRadioInterface radio_interface; QmiNasEvdoSinrLevel sinr; g_autoptr(QmiMessageNasGetSignalStrengthOutput) output = NULL; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); output = qmi_client_nas_get_signal_strength_finish (client, res, NULL); if (!output || !qmi_message_nas_get_signal_strength_output_get_result (output, NULL)) { /* No hard errors, go on to next step */ ctx->step++; signal_load_values_context_step (task); return; } /* Good, we have results */ ctx->values_result = g_slice_new0 (SignalLoadValuesResult); /* RSSI * * We will assume that valid access technologies reported in this output * are the ones which are listed in the RSSI output. If a given access tech * is not given in this list, it will not be considered afterwards (e.g. if * no EV-DO is given in the RSSI list, the SINR level won't be processed, * even if the TLV is available. */ if (qmi_message_nas_get_signal_strength_output_get_rssi_list (output, &array, NULL)) { guint i; for (i = 0; i < array->len; i++) { QmiMessageNasGetSignalStrengthOutputRssiListElement *element; element = &g_array_index (array, QmiMessageNasGetSignalStrengthOutputRssiListElement, i); switch (element->radio_interface) { case QMI_NAS_RADIO_INTERFACE_CDMA_1X: if (!ctx->values_result->cdma) ctx->values_result->cdma = mm_signal_new (); mm_signal_set_rssi (ctx->values_result->cdma, (gdouble)element->rssi); break; case QMI_NAS_RADIO_INTERFACE_CDMA_1XEVDO: if (!ctx->values_result->evdo) ctx->values_result->evdo = mm_signal_new (); mm_signal_set_rssi (ctx->values_result->evdo, (gdouble)element->rssi); break; case QMI_NAS_RADIO_INTERFACE_GSM: if (!ctx->values_result->gsm) ctx->values_result->gsm = mm_signal_new (); mm_signal_set_rssi (ctx->values_result->gsm, (gdouble)element->rssi); break; case QMI_NAS_RADIO_INTERFACE_UMTS: if (!ctx->values_result->umts) ctx->values_result->umts = mm_signal_new (); mm_signal_set_rssi (ctx->values_result->umts, (gdouble)element->rssi); break; case QMI_NAS_RADIO_INTERFACE_LTE: if (!ctx->values_result->lte) ctx->values_result->lte = mm_signal_new (); mm_signal_set_rssi (ctx->values_result->lte, (gdouble)element->rssi); break; case QMI_NAS_RADIO_INTERFACE_UNKNOWN: case QMI_NAS_RADIO_INTERFACE_NONE: case QMI_NAS_RADIO_INTERFACE_AMPS: case QMI_NAS_RADIO_INTERFACE_TD_SCDMA: case QMI_NAS_RADIO_INTERFACE_5GNR: default: break; } } } /* ECIO (CDMA, EV-DO and UMTS) */ if (qmi_message_nas_get_signal_strength_output_get_ecio_list (output, &array, NULL)) { guint i; for (i = 0; i < array->len; i++) { QmiMessageNasGetSignalStrengthOutputEcioListElement *element; element = &g_array_index (array, QmiMessageNasGetSignalStrengthOutputEcioListElement, i); switch (element->radio_interface) { case QMI_NAS_RADIO_INTERFACE_CDMA_1X: if (ctx->values_result->cdma) mm_signal_set_ecio (ctx->values_result->cdma, ((gdouble)element->ecio) * (-0.5)); break; case QMI_NAS_RADIO_INTERFACE_CDMA_1XEVDO: if (ctx->values_result->evdo) mm_signal_set_ecio (ctx->values_result->evdo, ((gdouble)element->ecio) * (-0.5)); break; case QMI_NAS_RADIO_INTERFACE_UMTS: if (ctx->values_result->umts) mm_signal_set_ecio (ctx->values_result->umts, ((gdouble)element->ecio) * (-0.5)); break; default: case QMI_NAS_RADIO_INTERFACE_GSM: case QMI_NAS_RADIO_INTERFACE_LTE: case QMI_NAS_RADIO_INTERFACE_UNKNOWN: case QMI_NAS_RADIO_INTERFACE_NONE: case QMI_NAS_RADIO_INTERFACE_AMPS: case QMI_NAS_RADIO_INTERFACE_TD_SCDMA: case QMI_NAS_RADIO_INTERFACE_5GNR: break; } } } /* IO (EV-DO) */ if (qmi_message_nas_get_signal_strength_output_get_io (output, &aux_int32, NULL)) { if (ctx->values_result->evdo) mm_signal_set_io (ctx->values_result->evdo, (gdouble)aux_int32); } /* RSRP (LTE) */ if (qmi_message_nas_get_signal_strength_output_get_lte_rsrp (output, &aux_int16, NULL)) { if (ctx->values_result->lte) mm_signal_set_rsrp (ctx->values_result->lte, (gdouble)aux_int16); } /* RSRQ (LTE) */ if (qmi_message_nas_get_signal_strength_output_get_rsrq (output, &aux_int8, &radio_interface, NULL) && radio_interface == QMI_NAS_RADIO_INTERFACE_LTE) { if (ctx->values_result->lte) mm_signal_set_rsrq (ctx->values_result->lte, (gdouble)aux_int8); } /* SNR (LTE) */ if (qmi_message_nas_get_signal_strength_output_get_lte_snr (output, &aux_int16, NULL)) { if (ctx->values_result->lte) mm_signal_set_snr (ctx->values_result->lte, (0.1) * ((gdouble)aux_int16)); } /* SINR (EV-DO) */ if (qmi_message_nas_get_signal_strength_output_get_sinr (output, &sinr, NULL)) { if (ctx->values_result->evdo) mm_signal_set_sinr (ctx->values_result->evdo, get_db_from_sinr_level (self, sinr)); } /* Go on */ ctx->step++; signal_load_values_context_step (task); } static void signal_load_values_get_signal_info_ready (QmiClientNas *client, GAsyncResult *res, GTask *task) { MMBroadbandModemQmi *self; SignalLoadValuesContext *ctx; g_autoptr(QmiMessageNasGetSignalInfoOutput) output = NULL; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); output = qmi_client_nas_get_signal_info_finish (client, res, NULL); if (!output || !qmi_message_nas_get_signal_info_output_get_result (output, NULL)) { /* No hard errors, go on to next step */ ctx->step++; signal_load_values_context_step (task); return; } /* Good, we have results */ ctx->values_result = g_slice_new0 (SignalLoadValuesResult); common_process_signal_info (self, output, NULL, &ctx->values_result->cdma, &ctx->values_result->evdo, &ctx->values_result->gsm, &ctx->values_result->umts, &ctx->values_result->lte, &ctx->values_result->nr5g); /* Keep on */ ctx->step++; signal_load_values_context_step (task); } static void signal_load_values_context_step (GTask *task) { SignalLoadValuesContext *ctx; #define VALUES_RESULT_LOADED(ctx) \ (ctx->values_result && \ (ctx->values_result->cdma || \ ctx->values_result->evdo || \ ctx->values_result->gsm || \ ctx->values_result->umts || \ ctx->values_result->lte || \ ctx->values_result->nr5g)) ctx = g_task_get_task_data (task); switch (ctx->step) { case SIGNAL_LOAD_VALUES_STEP_SIGNAL_FIRST: ctx->step++; /* Fall through */ case SIGNAL_LOAD_VALUES_STEP_SIGNAL_INFO: qmi_client_nas_get_signal_info (ctx->client, NULL, 5, NULL, (GAsyncReadyCallback)signal_load_values_get_signal_info_ready, task); return; case SIGNAL_LOAD_VALUES_STEP_SIGNAL_STRENGTH: /* If already loaded with signal info, don't try signal strength */ if (!VALUES_RESULT_LOADED (ctx)) { g_autoptr(QmiMessageNasGetSignalStrengthInput) input = NULL; input = qmi_message_nas_get_signal_strength_input_new (); qmi_message_nas_get_signal_strength_input_set_request_mask ( input, (QMI_NAS_SIGNAL_STRENGTH_REQUEST_RSSI | QMI_NAS_SIGNAL_STRENGTH_REQUEST_ECIO | QMI_NAS_SIGNAL_STRENGTH_REQUEST_IO | QMI_NAS_SIGNAL_STRENGTH_REQUEST_SINR | QMI_NAS_SIGNAL_STRENGTH_REQUEST_RSRQ | QMI_NAS_SIGNAL_STRENGTH_REQUEST_LTE_SNR | QMI_NAS_SIGNAL_STRENGTH_REQUEST_LTE_RSRP), NULL); qmi_client_nas_get_signal_strength (ctx->client, input, 5, NULL, (GAsyncReadyCallback)signal_load_values_get_signal_strength_ready, task); return; } ctx->step++; /* Fall through */ case SIGNAL_LOAD_VALUES_STEP_SIGNAL_LAST: /* If any result is set, succeed */ if (VALUES_RESULT_LOADED (ctx)) g_task_return_pointer (task, g_steal_pointer (&ctx->values_result), (GDestroyNotify)signal_load_values_result_free); else g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "No way to load extended signal information"); g_object_unref (task); return; default: break; } g_assert_not_reached (); #undef VALUES_RESULT_LOADED } static void signal_load_values (MMIfaceModemSignal *self, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { SignalLoadValuesContext *ctx; GTask *task; QmiClient *client = NULL; mm_obj_dbg (self, "loading extended signal information..."); if (!mm_shared_qmi_ensure_client (MM_SHARED_QMI (self), QMI_SERVICE_NAS, &client, callback, user_data)) return; ctx = g_slice_new0 (SignalLoadValuesContext); ctx->client = QMI_CLIENT_NAS (g_object_ref (client)); ctx->step = SIGNAL_LOAD_VALUES_STEP_SIGNAL_FIRST; task = g_task_new (self, cancellable, callback, user_data); g_task_set_task_data (task, ctx, (GDestroyNotify)signal_load_values_context_free); signal_load_values_context_step (task); } /*****************************************************************************/ /* Setup threshold values (Signal interface) */ typedef struct { QmiClientNas *client; guint rssi_threshold; } SignalSetupThresholdsContext; static void signal_setup_thresholds_context_free (SignalSetupThresholdsContext *ctx) { g_clear_object (&ctx->client); g_slice_free (SignalSetupThresholdsContext, ctx); } static gboolean signal_setup_thresholds_finish (MMIfaceModemSignal *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void signal_setup_thresholds_config_signal_info_ready (QmiClientNas *client, GAsyncResult *res, GTask *task) { GError *error = NULL; g_autoptr(QmiMessageNasConfigSignalInfoOutput) output = NULL; output = qmi_client_nas_config_signal_info_finish (client, res, &error); if (!output || !qmi_message_nas_config_signal_info_output_get_result (output, &error)) g_task_return_error (task, error); else g_task_return_boolean (task, TRUE); g_object_unref (task); } static void config_signal_info (GTask *task) { SignalSetupThresholdsContext *ctx; g_autoptr(QmiMessageNasConfigSignalInfoInput) input = NULL; g_autoptr(GArray) rssi_thresholds = NULL; ctx = g_task_get_task_data (task); input = qmi_message_nas_config_signal_info_input_new (); if (ctx->rssi_threshold) { gint8 threshold; rssi_thresholds = g_array_new (FALSE, FALSE, sizeof (gint8)); threshold = RSSI_MIN; while (RSSI_MIN <= threshold && threshold <= RSSI_MAX) { g_array_append_val (rssi_thresholds, threshold); threshold += ctx->rssi_threshold; } } else { rssi_thresholds = g_array_sized_new (FALSE, FALSE, sizeof (gint8), G_N_ELEMENTS (default_thresholds_data_dbm)); g_array_append_vals (rssi_thresholds, default_thresholds_data_dbm, G_N_ELEMENTS (default_thresholds_data_dbm)); } qmi_message_nas_config_signal_info_input_set_rssi_threshold (input, rssi_thresholds, NULL); qmi_client_nas_config_signal_info (ctx->client, input, 5, NULL, (GAsyncReadyCallback)signal_setup_thresholds_config_signal_info_ready, task); } static void signal_setup_thresholds_config_signal_info_v2_ready (QmiClientNas *client, GAsyncResult *res, GTask *task) { MMBroadbandModemQmi *self; GError *error = NULL; g_autoptr(QmiMessageNasConfigSignalInfoV2Output) output = NULL; self = g_task_get_source_object (task); output = qmi_client_nas_config_signal_info_v2_finish (client, res, &error); if (!output || !qmi_message_nas_config_signal_info_v2_output_get_result (output, &error)) { if (g_error_matches (error, QMI_PROTOCOL_ERROR, QMI_PROTOCOL_ERROR_INVALID_QMI_COMMAND) || g_error_matches (error, QMI_PROTOCOL_ERROR, QMI_PROTOCOL_ERROR_NOT_SUPPORTED)) { mm_obj_dbg (self, "couldn't config signal info using v2: '%s', falling back to config signal info using v1", error->message); config_signal_info (task); g_clear_error (&error); return; } g_task_return_error (task, error); g_object_unref (task); return; } g_task_return_boolean (task, TRUE); g_object_unref (task); } static void config_signal_info_v2 (GTask *task) { SignalSetupThresholdsContext *ctx; g_autoptr(QmiMessageNasConfigSignalInfoV2Input) input = NULL; guint delta; ctx = g_task_get_task_data (task); /* delta in units of 0.1dBm */ delta = ctx->rssi_threshold ? ctx->rssi_threshold * 10 : default_rssi_delta_dbm * 10; input = qmi_message_nas_config_signal_info_v2_input_new (); qmi_message_nas_config_signal_info_v2_input_set_cdma_rssi_delta (input, delta, NULL); qmi_message_nas_config_signal_info_v2_input_set_hdr_rssi_delta (input, delta, NULL); qmi_message_nas_config_signal_info_v2_input_set_gsm_rssi_delta (input, delta, NULL); qmi_message_nas_config_signal_info_v2_input_set_wcdma_rssi_delta (input, delta, NULL); qmi_message_nas_config_signal_info_v2_input_set_lte_rssi_delta (input, delta, NULL); /* use RSRP for 5GNR*/ qmi_message_nas_config_signal_info_v2_input_set_nr5g_rsrp_delta (input, delta, NULL); qmi_client_nas_config_signal_info_v2 (ctx->client, input, 5, NULL, (GAsyncReadyCallback)signal_setup_thresholds_config_signal_info_v2_ready, task); } static void signal_setup_thresholds (MMIfaceModemSignal *self, guint rssi_threshold, gboolean error_rate_threshold, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; SignalSetupThresholdsContext *ctx; QmiClient *client = NULL; task = g_task_new (self, NULL, callback, user_data); if (error_rate_threshold) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "Setting error rate threshold not supported"); g_object_unref (task); return; } if (!mm_shared_qmi_ensure_client (MM_SHARED_QMI (self), QMI_SERVICE_NAS, &client, callback, user_data)) return; ctx = g_slice_new0 (SignalSetupThresholdsContext); ctx->client = QMI_CLIENT_NAS (g_object_ref (client)); ctx->rssi_threshold = rssi_threshold; g_task_set_task_data (task, ctx, (GDestroyNotify)signal_setup_thresholds_context_free); config_signal_info_v2 (task); } /*****************************************************************************/ /* Reset data interfaces during initialization */ typedef struct { GList *ports; MMPort *data; MMPortQmi *qmi; } ResetPortsContext; static void reset_ports_context_free (ResetPortsContext *ctx) { g_assert (!ctx->data); g_assert (!ctx->qmi); g_list_free_full (ctx->ports, g_object_unref); g_slice_free (ResetPortsContext, ctx); } static gboolean reset_ports_finish (MMBroadbandModemQmi *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void reset_next_port (GTask *task); static void port_qmi_reset_ready (MMPortQmi *qmi, GAsyncResult *res, GTask *task) { MMBroadbandModemQmi *self; ResetPortsContext *ctx; g_autoptr(GError) error = NULL; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); if (!mm_port_qmi_reset_finish (qmi, res, &error)) mm_obj_warn (self, "couldn't reset QMI port '%s' with data interface '%s': %s", mm_port_get_device (MM_PORT (ctx->qmi)), mm_port_get_device (ctx->data), error->message); g_clear_object (&ctx->data); g_clear_object (&ctx->qmi); reset_next_port (task); } static void reset_next_port (GTask *task) { MMBroadbandModemQmi *self; ResetPortsContext *ctx; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); if (!ctx->ports) { g_task_return_boolean (task, TRUE); g_object_unref (task); return; } /* steal full data port reference from list head */ ctx->data = ctx->ports->data; ctx->ports = g_list_delete_link (ctx->ports, ctx->ports); ctx->qmi = mm_broadband_modem_qmi_get_port_qmi_for_data (self, ctx->data, NULL, NULL); if (!ctx->qmi) { mm_obj_dbg (self, "no QMI port associated to data port '%s': ignoring data interface reset", mm_port_get_device (ctx->data)); g_clear_object (&ctx->data); reset_next_port (task); return; } mm_obj_dbg (self, "running QMI port '%s' reset with data interface '%s'", mm_port_get_device (MM_PORT (ctx->qmi)), mm_port_get_device (ctx->data)); mm_port_qmi_reset (ctx->qmi, ctx->data, (GAsyncReadyCallback) port_qmi_reset_ready, task); } static void reset_ports (MMBroadbandModemQmi *self, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; ResetPortsContext *ctx; task = g_task_new (self, NULL, callback, user_data); ctx = g_slice_new0 (ResetPortsContext); g_task_set_task_data (task, ctx, (GDestroyNotify)reset_ports_context_free); ctx->ports = mm_base_modem_find_ports (MM_BASE_MODEM (self), MM_PORT_SUBSYS_UNKNOWN, MM_PORT_TYPE_NET); reset_next_port (task); } /*****************************************************************************/ /* First enabling step */ static gboolean enabling_started_finish (MMBroadbandModem *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void wds_set_autoconnect_settings_ready (QmiClientWds *client, GAsyncResult *res, GTask *task) { MMBroadbandModemQmi *self; g_autoptr(GError) error = NULL; g_autoptr(QmiMessageWdsSetAutoconnectSettingsOutput) output = NULL; self = g_task_get_source_object (task); output = qmi_client_wds_set_autoconnect_settings_finish (client, res, &error); if (!output || !qmi_message_wds_set_autoconnect_settings_output_get_result (output, &error)) mm_obj_warn (self, "failed disabling autoconnect: %s", error->message); else mm_obj_msg (self, "autoconnect explicitly disabled"); g_task_return_boolean (task, TRUE); g_object_unref (task); } static void wds_get_autoconnect_settings_ready (QmiClientWds *client, GAsyncResult *res, GTask *task) { MMBroadbandModemQmi *self; QmiWdsAutoconnectSetting autoconnect_setting; g_autoptr(GError) error = NULL; g_autoptr(QmiMessageWdsSetAutoconnectSettingsInput) input = NULL; g_autoptr(QmiMessageWdsGetAutoconnectSettingsOutput) output = NULL; self = g_task_get_source_object (task); output = qmi_client_wds_get_autoconnect_settings_finish (client, res, &error); if (!output || !qmi_message_wds_get_autoconnect_settings_output_get_result (output, &error) || !qmi_message_wds_get_autoconnect_settings_output_get_status (output, &autoconnect_setting, &error)) { mm_obj_warn (self, "failed checking whether autoconnect is disabled or not: %s", error->message); g_task_return_boolean (task, TRUE); g_object_unref (task); return; } if (autoconnect_setting != QMI_WDS_AUTOCONNECT_SETTING_ENABLED) { mm_obj_dbg (self, "autoconnect is already disabled"); g_task_return_boolean (task, TRUE); g_object_unref (task); return; } mm_obj_dbg (self, "need to explicitly disable autoconnect"); input = qmi_message_wds_set_autoconnect_settings_input_new (); qmi_message_wds_set_autoconnect_settings_input_set_status (input, QMI_WDS_AUTOCONNECT_SETTING_DISABLED, NULL); qmi_client_wds_set_autoconnect_settings (client, input, 10, NULL, (GAsyncReadyCallback) wds_set_autoconnect_settings_ready, task); } static void parent_enabling_started_ready (MMBroadbandModem *_self, GAsyncResult *res, GTask *task) { MMBroadbandModemQmi *self = MM_BROADBAND_MODEM_QMI (_self); QmiClient *client = NULL; g_autoptr(GError) error = NULL; if (!MM_BROADBAND_MODEM_CLASS (mm_broadband_modem_qmi_parent_class)->enabling_started_finish (_self, res, &error)) { /* Don't treat this as fatal. Parent enabling may fail if it cannot grab a primary * AT port, which isn't really an issue in QMI-based modems */ mm_obj_dbg (self, "couldn't start parent enabling: %s", error->message); } /* If the autoconnect check has already been done, we're finished */ if (self->priv->autoconnect_checked) { g_task_return_boolean (task, TRUE); g_object_unref (task); return; } /* The connection logic doesn't work properly when the device is already set * to autoconnect, so automatically disable autoconnect ourselves. */ mm_obj_dbg (self, "need to check whether autoconnect is disabled or not..."); self->priv->autoconnect_checked = TRUE; /* Use default WDS client to query autoconnect settings */ client = mm_shared_qmi_peek_client (MM_SHARED_QMI (self), QMI_SERVICE_WDS, MM_PORT_QMI_FLAG_DEFAULT, NULL); if (!client) { mm_obj_warn (self, "cannot check whether autoconnect is disabled or not: couldn't peek default WDS client"); /* not fatal, just assume autoconnect is disabled */ g_task_return_boolean (task, TRUE); g_object_unref (task); return; } qmi_client_wds_get_autoconnect_settings (QMI_CLIENT_WDS (client), NULL, 5, NULL, (GAsyncReadyCallback) wds_get_autoconnect_settings_ready, task); } static void enabling_started (MMBroadbandModem *self, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; task = g_task_new (self, NULL, callback, user_data); MM_BROADBAND_MODEM_CLASS (mm_broadband_modem_qmi_parent_class)->enabling_started ( self, (GAsyncReadyCallback)parent_enabling_started_ready, task); } /*****************************************************************************/ /* First initialization step */ static const QmiService qmi_services[] = { QMI_SERVICE_DMS, QMI_SERVICE_NAS, QMI_SERVICE_WDS, QMI_SERVICE_WMS, QMI_SERVICE_PDS, QMI_SERVICE_OMA, QMI_SERVICE_UIM, QMI_SERVICE_LOC, QMI_SERVICE_PDC, QMI_SERVICE_VOICE, QMI_SERVICE_DSD, QMI_SERVICE_SAR, }; typedef struct { MMPortQmi *qmi; guint service_index; } InitializationStartedContext; static void initialization_started_context_free (InitializationStartedContext *ctx) { if (ctx->qmi) g_object_unref (ctx->qmi); g_free (ctx); } static gpointer initialization_started_finish (MMBroadbandModem *self, GAsyncResult *res, GError **error) { return g_task_propagate_pointer (G_TASK (res), error); } static void parent_initialization_started_ready (MMBroadbandModem *self, GAsyncResult *res, GTask *task) { gpointer parent_ctx; GError *error = NULL; parent_ctx = MM_BROADBAND_MODEM_CLASS (mm_broadband_modem_qmi_parent_class)->initialization_started_finish ( self, res, &error); if (error) { /* Don't treat this as fatal. Parent initialization may fail if it cannot grab a primary * AT port, which isn't really an issue in QMI-based modems */ mm_obj_dbg (self, "couldn't start parent initialization: %s", error->message); g_error_free (error); } /* Just parent's pointer passed here */ g_task_return_pointer (task, parent_ctx, NULL); g_object_unref (task); } static void parent_initialization_started (GTask *task) { MMBroadbandModem *self; self = g_task_get_source_object (task); MM_BROADBAND_MODEM_CLASS (mm_broadband_modem_qmi_parent_class)->initialization_started ( self, (GAsyncReadyCallback)parent_initialization_started_ready, task); } static void allocate_next_client (GTask *task); static void qmi_port_allocate_client_ready (MMPortQmi *qmi, GAsyncResult *res, GTask *task) { MMBroadbandModemQmi *self; InitializationStartedContext *ctx; GError *error = NULL; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); if (!mm_port_qmi_allocate_client_finish (qmi, res, &error)) { mm_obj_dbg (self, "couldn't allocate client for service '%s': %s", qmi_service_get_string (qmi_services[ctx->service_index]), error->message); g_error_free (error); } ctx->service_index++; allocate_next_client (task); } static void allocate_next_client (GTask *task) { InitializationStartedContext *ctx; ctx = g_task_get_task_data (task); if (ctx->service_index == G_N_ELEMENTS (qmi_services)) { parent_initialization_started (task); return; } /* Otherwise, allocate next client */ mm_port_qmi_allocate_client (ctx->qmi, qmi_services[ctx->service_index], MM_PORT_QMI_FLAG_DEFAULT, NULL, (GAsyncReadyCallback)qmi_port_allocate_client_ready, task); } static void qmi_port_open_ready_no_data_format (MMPortQmi *qmi, GAsyncResult *res, GTask *task) { GError *error = NULL; if (!mm_port_qmi_open_finish (qmi, res, &error)) { g_task_return_error (task, error); g_object_unref (task); return; } allocate_next_client (task); } static void qmi_port_open_ready (MMPortQmi *qmi, GAsyncResult *res, GTask *task) { MMBroadbandModemQmi *self; InitializationStartedContext *ctx; GError *error = NULL; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); if (!mm_port_qmi_open_finish (qmi, res, &error)) { /* Really, really old devices (Gobi 1K, 2008-era firmware) may not * support SetDataFormat, so if we get an error opening the port * try without it. The qmi_wwan driver will fix up any issues that * the device might have between raw-ip and 802.3 mode anyway. */ mm_obj_dbg (self, "couldn't open QMI port with data format update: %s", error->message); g_error_free (error); mm_port_qmi_open (ctx->qmi, FALSE, NULL, (GAsyncReadyCallback)qmi_port_open_ready_no_data_format, task); return; } allocate_next_client (task); } static void initialization_open_port (GTask *task) { InitializationStartedContext *ctx; ctx = g_task_get_task_data (task); mm_port_qmi_open (ctx->qmi, TRUE, NULL, (GAsyncReadyCallback)qmi_port_open_ready, task); } static void reset_ports_ready (MMBroadbandModemQmi *self, GAsyncResult *res, GTask *task) { GError *error = NULL; if (!reset_ports_finish (self, res, &error)) { g_task_return_error (task, error); g_object_unref (task); return; } initialization_open_port (task); } static void initialization_reset_ports (GTask *task) { MMBroadbandModemQmi *self; self = g_task_get_source_object (task); /* reseting the data interfaces is really only needed if the device * hasn't been hotplugged */ if (mm_base_modem_get_hotplugged (MM_BASE_MODEM (self))) { mm_obj_dbg (self, "not running data interface reset procedure: device is hotplugged"); initialization_open_port (task); return; } reset_ports (self, (GAsyncReadyCallback)reset_ports_ready, task); } static void initialization_started (MMBroadbandModem *self, GAsyncReadyCallback callback, gpointer user_data) { InitializationStartedContext *ctx; GTask *task; ctx = g_new0 (InitializationStartedContext, 1); ctx->qmi = mm_broadband_modem_qmi_get_port_qmi (MM_BROADBAND_MODEM_QMI (self)); task = g_task_new (self, NULL, callback, user_data); g_task_set_task_data (task, ctx, (GDestroyNotify)initialization_started_context_free); /* This may happen if we unplug the modem unexpectedly */ if (!ctx->qmi) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Cannot initialize: QMI port no longer available"); g_object_unref (task); return; } if (mm_port_qmi_is_open (ctx->qmi)) { parent_initialization_started (task); return; } initialization_reset_ports (task); } /*****************************************************************************/ MMBroadbandModemQmi * mm_broadband_modem_qmi_new (const gchar *device, const gchar *physdev, const gchar **drivers, const gchar *plugin, guint16 vendor_id, guint16 product_id) { return g_object_new (MM_TYPE_BROADBAND_MODEM_QMI, MM_BASE_MODEM_DEVICE, device, MM_BASE_MODEM_PHYSDEV, physdev, MM_BASE_MODEM_DRIVERS, drivers, MM_BASE_MODEM_PLUGIN, plugin, MM_BASE_MODEM_VENDOR_ID, vendor_id, MM_BASE_MODEM_PRODUCT_ID, product_id, /* QMI bearer supports NET only */ MM_BASE_MODEM_DATA_NET_SUPPORTED, TRUE, MM_BASE_MODEM_DATA_TTY_SUPPORTED, FALSE, MM_IFACE_MODEM_SIM_HOT_SWAP_SUPPORTED, TRUE, NULL); } static void mm_broadband_modem_qmi_init (MMBroadbandModemQmi *self) { /* Initialize private data */ self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, MM_TYPE_BROADBAND_MODEM_QMI, MMBroadbandModemQmiPrivate); } static void finalize (GObject *object) { MMBroadbandModemQmi *self = MM_BROADBAND_MODEM_QMI (object); g_free (self->priv->imei); g_free (self->priv->meid); g_free (self->priv->esn); g_free (self->priv->current_operator_id); g_free (self->priv->current_operator_description); if (self->priv->supported_bands) g_array_unref (self->priv->supported_bands); G_OBJECT_CLASS (mm_broadband_modem_qmi_parent_class)->finalize (object); } static void dispose (GObject *object) { MMBroadbandModemQmi *self = MM_BROADBAND_MODEM_QMI (object); MMPortQmi *qmi; /* If any port cleanup is needed, it must be done during dispose(), as * the modem object will be affected by an explicit g_object_run_dispose() * that will remove all port references right away */ qmi = mm_broadband_modem_qmi_peek_port_qmi (self); if (qmi) { /* If we did open the QMI port during initialization, close it now */ if (mm_port_qmi_is_open (qmi)) mm_port_qmi_close (qmi, NULL, NULL); } g_list_free_full (self->priv->firmware_list, g_object_unref); self->priv->firmware_list = NULL; g_clear_object (&self->priv->current_firmware); G_OBJECT_CLASS (mm_broadband_modem_qmi_parent_class)->dispose (object); } static void iface_modem_init (MMIfaceModem *iface) { /* Initialization steps */ iface->load_current_capabilities = mm_shared_qmi_load_current_capabilities; iface->load_current_capabilities_finish = mm_shared_qmi_load_current_capabilities_finish; iface->load_supported_capabilities = mm_shared_qmi_load_supported_capabilities; iface->load_supported_capabilities_finish = mm_shared_qmi_load_supported_capabilities_finish; iface->set_current_capabilities = mm_shared_qmi_set_current_capabilities; iface->set_current_capabilities_finish = mm_shared_qmi_set_current_capabilities_finish; iface->load_manufacturer = modem_load_manufacturer; iface->load_manufacturer_finish = modem_load_manufacturer_finish; iface->load_model = mm_shared_qmi_load_model; iface->load_model_finish = mm_shared_qmi_load_model_finish; iface->load_revision = modem_load_revision; iface->load_revision_finish = modem_load_revision_finish; iface->load_hardware_revision = modem_load_hardware_revision; iface->load_hardware_revision_finish = modem_load_hardware_revision_finish; iface->load_equipment_identifier = modem_load_equipment_identifier; iface->load_equipment_identifier_finish = modem_load_equipment_identifier_finish; iface->load_device_identifier = modem_load_device_identifier; iface->load_device_identifier_finish = modem_load_device_identifier_finish; iface->load_own_numbers = modem_load_own_numbers; iface->load_own_numbers_finish = modem_load_own_numbers_finish; iface->load_unlock_required = modem_load_unlock_required; iface->load_unlock_required_finish = modem_load_unlock_required_finish; iface->load_unlock_retries = modem_load_unlock_retries; iface->load_unlock_retries_finish = modem_load_unlock_retries_finish; iface->load_supported_bands = mm_shared_qmi_load_supported_bands; iface->load_supported_bands_finish = mm_shared_qmi_load_supported_bands_finish; iface->load_supported_modes = mm_shared_qmi_load_supported_modes; iface->load_supported_modes_finish = mm_shared_qmi_load_supported_modes_finish; iface->load_power_state = load_power_state; iface->load_power_state_finish = load_power_state_finish; iface->load_supported_ip_families = modem_load_supported_ip_families; iface->load_supported_ip_families_finish = modem_load_supported_ip_families_finish; iface->load_carrier_config = mm_shared_qmi_load_carrier_config; iface->load_carrier_config_finish = mm_shared_qmi_load_carrier_config_finish; iface->setup_carrier_config = mm_shared_qmi_setup_carrier_config; iface->setup_carrier_config_finish = mm_shared_qmi_setup_carrier_config_finish; /* Enabling/disabling */ iface->modem_power_up = modem_power_up; iface->modem_power_up_finish = modem_power_up_down_off_finish; iface->modem_after_power_up = NULL; iface->modem_after_power_up_finish = NULL; iface->modem_power_down = modem_power_down; iface->modem_power_down_finish = modem_power_up_down_off_finish; iface->modem_power_off = modem_power_off; iface->modem_power_off_finish = modem_power_up_down_off_finish; iface->setup_flow_control = NULL; iface->setup_flow_control_finish = NULL; iface->load_supported_charsets = NULL; iface->load_supported_charsets_finish = NULL; iface->setup_charset = NULL; iface->setup_charset_finish = NULL; iface->load_current_modes = mm_shared_qmi_load_current_modes; iface->load_current_modes_finish = mm_shared_qmi_load_current_modes_finish; iface->set_current_modes = mm_shared_qmi_set_current_modes; iface->set_current_modes_finish = mm_shared_qmi_set_current_modes_finish; iface->load_signal_quality = load_signal_quality; iface->load_signal_quality_finish = load_signal_quality_finish; iface->get_cell_info = get_cell_info; iface->get_cell_info_finish = get_cell_info_finish; iface->load_current_bands = mm_shared_qmi_load_current_bands; iface->load_current_bands_finish = mm_shared_qmi_load_current_bands_finish; iface->set_current_bands = mm_shared_qmi_set_current_bands; iface->set_current_bands_finish = mm_shared_qmi_set_current_bands_finish; /* Don't try to load access technologies, as we would be using parent's * generic method (QCDM based). Access technologies are already reported via * QMI when we load signal quality. */ iface->load_access_technologies = NULL; iface->load_access_technologies_finish = NULL; /* Create QMI-specific SIM */ iface->create_sim = create_sim; iface->create_sim_finish = create_sim_finish; iface->load_sim_slots = mm_shared_qmi_load_sim_slots; iface->load_sim_slots_finish = mm_shared_qmi_load_sim_slots_finish; iface->set_primary_sim_slot = mm_shared_qmi_set_primary_sim_slot; iface->set_primary_sim_slot_finish = mm_shared_qmi_set_primary_sim_slot_finish; iface->setup_sim_hot_swap = mm_shared_qmi_setup_sim_hot_swap; iface->setup_sim_hot_swap_finish = mm_shared_qmi_setup_sim_hot_swap_finish; /* Create QMI-specific bearer and bearer list */ iface->create_bearer = modem_create_bearer; iface->create_bearer_finish = modem_create_bearer_finish; iface->create_bearer_list = modem_create_bearer_list; /* Other actions */ iface->reset = mm_shared_qmi_reset; iface->reset_finish = mm_shared_qmi_reset_finish; iface->factory_reset = mm_shared_qmi_factory_reset; iface->factory_reset_finish = mm_shared_qmi_factory_reset_finish; } static void iface_modem_3gpp_init (MMIfaceModem3gpp *iface) { /* Initialization steps */ iface->load_imei = modem_3gpp_load_imei; iface->load_imei_finish = modem_3gpp_load_imei_finish; iface->load_enabled_facility_locks = modem_3gpp_load_enabled_facility_locks; iface->load_enabled_facility_locks_finish = modem_3gpp_load_enabled_facility_locks_finish; /* Enabling/Disabling steps */ iface->setup_unsolicited_events = modem_3gpp_setup_unsolicited_events; iface->setup_unsolicited_events_finish = modem_3gpp_setup_cleanup_unsolicited_events_finish; iface->cleanup_unsolicited_events = modem_3gpp_cleanup_unsolicited_events; iface->cleanup_unsolicited_events_finish = modem_3gpp_setup_cleanup_unsolicited_events_finish; iface->enable_unsolicited_events = modem_3gpp_enable_unsolicited_events; iface->enable_unsolicited_events_finish = modem_3gpp_enable_disable_unsolicited_events_finish; iface->disable_unsolicited_events = modem_3gpp_disable_unsolicited_events; iface->disable_unsolicited_events_finish = modem_3gpp_enable_disable_unsolicited_events_finish; iface->setup_unsolicited_registration_events = modem_3gpp_setup_unsolicited_registration_events; iface->setup_unsolicited_registration_events_finish = modem_3gpp_setup_cleanup_unsolicited_registration_events_finish; iface->cleanup_unsolicited_registration_events = modem_3gpp_cleanup_unsolicited_registration_events; iface->cleanup_unsolicited_registration_events_finish = modem_3gpp_setup_cleanup_unsolicited_registration_events_finish; iface->enable_unsolicited_registration_events = modem_3gpp_enable_unsolicited_registration_events; iface->enable_unsolicited_registration_events_finish = modem_3gpp_enable_disable_unsolicited_registration_events_finish; iface->disable_unsolicited_registration_events = modem_3gpp_disable_unsolicited_registration_events; iface->disable_unsolicited_registration_events_finish = modem_3gpp_enable_disable_unsolicited_registration_events_finish; /* Other actions */ iface->scan_networks = modem_3gpp_scan_networks; iface->scan_networks_finish = modem_3gpp_scan_networks_finish; iface->register_in_network = mm_shared_qmi_3gpp_register_in_network; iface->register_in_network_finish = mm_shared_qmi_3gpp_register_in_network_finish; iface->run_registration_checks = modem_3gpp_run_registration_checks; iface->run_registration_checks_finish = modem_3gpp_run_registration_checks_finish; iface->load_operator_code = modem_3gpp_load_operator_code; iface->load_operator_code_finish = modem_3gpp_load_operator_code_finish; iface->load_operator_name = modem_3gpp_load_operator_name; iface->load_operator_name_finish = modem_3gpp_load_operator_name_finish; iface->load_initial_eps_bearer = modem_3gpp_load_initial_eps_bearer; iface->load_initial_eps_bearer_finish = modem_3gpp_load_initial_eps_bearer_finish; iface->load_initial_eps_bearer_settings = modem_3gpp_load_initial_eps_bearer_settings; iface->load_initial_eps_bearer_settings_finish = modem_3gpp_load_initial_eps_bearer_settings_finish; iface->set_initial_eps_bearer_settings = modem_3gpp_set_initial_eps_bearer_settings; iface->set_initial_eps_bearer_settings_finish = modem_3gpp_set_initial_eps_bearer_settings_finish; iface->disable_facility_lock = modem_3gpp_disable_facility_lock; iface->disable_facility_lock_finish = modem_3gpp_disable_facility_lock_finish; iface->set_packet_service_state = mm_shared_qmi_set_packet_service_state; iface->set_packet_service_state_finish = mm_shared_qmi_set_packet_service_state_finish; } static void iface_modem_3gpp_profile_manager_init (MMIfaceModem3gppProfileManager *iface) { /* No explicit check support for the profile management feature, just * rely on the generic way to check for support */ iface->check_support = NULL; iface->check_support_finish = NULL; iface->setup_unsolicited_events = modem_3gpp_profile_manager_setup_unsolicited_events; iface->setup_unsolicited_events_finish = modem_3gpp_profile_manager_setup_cleanup_unsolicited_events_finish; iface->cleanup_unsolicited_events = modem_3gpp_profile_manager_cleanup_unsolicited_events; iface->cleanup_unsolicited_events_finish = modem_3gpp_profile_manager_setup_cleanup_unsolicited_events_finish; iface->enable_unsolicited_events = modem_3gpp_profile_manager_enable_unsolicited_events; iface->enable_unsolicited_events_finish = modem_3gpp_profile_manager_enable_disable_unsolicited_events_finish; iface->disable_unsolicited_events = modem_3gpp_profile_manager_disable_unsolicited_events; iface->disable_unsolicited_events_finish = modem_3gpp_profile_manager_enable_disable_unsolicited_events_finish; /* Additional actions */ iface->get_profile = modem_3gpp_profile_manager_get_profile; iface->get_profile_finish = modem_3gpp_profile_manager_get_profile_finish; iface->list_profiles = modem_3gpp_profile_manager_list_profiles; iface->list_profiles_finish = modem_3gpp_profile_manager_list_profiles_finish; iface->check_format = modem_3gpp_profile_manager_check_format; iface->check_format_finish = modem_3gpp_profile_manager_check_format_finish; iface->check_activated_profile = NULL; iface->check_activated_profile_finish = NULL; iface->deactivate_profile = NULL; iface->deactivate_profile_finish = NULL; iface->store_profile = modem_3gpp_profile_manager_store_profile; iface->store_profile_finish = modem_3gpp_profile_manager_store_profile_finish; iface->delete_profile = modem_3gpp_profile_manager_delete_profile; iface->delete_profile_finish = modem_3gpp_profile_manager_delete_profile_finish; } static void iface_modem_3gpp_ussd_init (MMIfaceModem3gppUssd *iface) { iface->check_support = modem_3gpp_ussd_check_support; iface->check_support_finish = modem_3gpp_ussd_check_support_finish; iface->setup_unsolicited_events = modem_3gpp_ussd_setup_unsolicited_events; iface->setup_unsolicited_events_finish = common_3gpp_ussd_setup_cleanup_unsolicited_events_finish; iface->cleanup_unsolicited_events = modem_3gpp_ussd_cleanup_unsolicited_events; iface->cleanup_unsolicited_events_finish = common_3gpp_ussd_setup_cleanup_unsolicited_events_finish; iface->enable_unsolicited_events = modem_3gpp_ussd_enable_unsolicited_events; iface->enable_unsolicited_events_finish = common_3gpp_ussd_enable_disable_unsolicited_events_finish; iface->disable_unsolicited_events = modem_3gpp_ussd_disable_unsolicited_events; iface->disable_unsolicited_events_finish = common_3gpp_ussd_enable_disable_unsolicited_events_finish; iface->send = modem_3gpp_ussd_send; iface->send_finish = modem_3gpp_ussd_send_finish; iface->cancel = modem_3gpp_ussd_cancel; iface->cancel_finish = modem_3gpp_ussd_cancel_finish; } static void iface_modem_voice_init (MMIfaceModemVoice *iface) { iface_modem_voice_parent = g_type_interface_peek_parent (iface); iface->check_support = modem_voice_check_support; iface->check_support_finish = modem_voice_check_support_finish; iface->setup_unsolicited_events = modem_voice_setup_unsolicited_events; iface->setup_unsolicited_events_finish = common_voice_setup_cleanup_unsolicited_events_finish; iface->enable_unsolicited_events = modem_voice_enable_unsolicited_events; iface->enable_unsolicited_events_finish = common_voice_enable_disable_unsolicited_events_finish; iface->disable_unsolicited_events = modem_voice_disable_unsolicited_events; iface->disable_unsolicited_events_finish = common_voice_enable_disable_unsolicited_events_finish; iface->cleanup_unsolicited_events = modem_voice_cleanup_unsolicited_events; iface->cleanup_unsolicited_events_finish = common_voice_setup_cleanup_unsolicited_events_finish; iface->setup_in_call_unsolicited_events = modem_voice_setup_in_call_unsolicited_events; iface->setup_in_call_unsolicited_events_finish = common_voice_setup_cleanup_in_call_unsolicited_events_finish; iface->cleanup_in_call_unsolicited_events = modem_voice_cleanup_in_call_unsolicited_events; iface->cleanup_in_call_unsolicited_events_finish = common_voice_setup_cleanup_in_call_unsolicited_events_finish; iface->create_call = modem_voice_create_call; iface->load_call_list = modem_voice_load_call_list; iface->load_call_list_finish = modem_voice_load_call_list_finish; iface->hold_and_accept = modem_voice_hold_and_accept; iface->hold_and_accept_finish = modem_voice_hold_and_accept_finish; iface->hangup_and_accept = modem_voice_hangup_and_accept; iface->hangup_and_accept_finish = modem_voice_hangup_and_accept_finish; iface->hangup_all = modem_voice_hangup_all; iface->hangup_all_finish = modem_voice_hangup_all_finish; iface->join_multiparty = modem_voice_join_multiparty; iface->join_multiparty_finish = modem_voice_join_multiparty_finish; iface->leave_multiparty = modem_voice_leave_multiparty; iface->leave_multiparty_finish = modem_voice_leave_multiparty_finish; iface->transfer = modem_voice_transfer; iface->transfer_finish = modem_voice_transfer_finish; iface->call_waiting_setup = modem_voice_call_waiting_setup; iface->call_waiting_setup_finish = modem_voice_call_waiting_setup_finish; iface->call_waiting_query = modem_voice_call_waiting_query; iface->call_waiting_query_finish = modem_voice_call_waiting_query_finish; } static void iface_modem_cdma_init (MMIfaceModemCdma *iface) { iface->load_meid = modem_cdma_load_meid; iface->load_meid_finish = modem_cdma_load_meid_finish; iface->load_esn = modem_cdma_load_esn; iface->load_esn_finish = modem_cdma_load_esn_finish; /* Enabling/Disabling steps */ iface->setup_unsolicited_events = modem_cdma_setup_unsolicited_events; iface->setup_unsolicited_events_finish = modem_cdma_setup_cleanup_unsolicited_events_finish; iface->cleanup_unsolicited_events = modem_cdma_cleanup_unsolicited_events; iface->cleanup_unsolicited_events_finish = modem_cdma_setup_cleanup_unsolicited_events_finish; iface->enable_unsolicited_events = modem_cdma_enable_unsolicited_events; iface->enable_unsolicited_events_finish = modem_cdma_enable_disable_unsolicited_events_finish; iface->disable_unsolicited_events = modem_cdma_disable_unsolicited_events; iface->disable_unsolicited_events_finish = modem_cdma_enable_disable_unsolicited_events_finish; /* Other actions */ iface->run_registration_checks = modem_cdma_run_registration_checks; iface->run_registration_checks_finish = modem_cdma_run_registration_checks_finish; iface->load_activation_state = modem_cdma_load_activation_state; iface->load_activation_state_finish = modem_cdma_load_activation_state_finish; iface->activate = modem_cdma_activate; iface->activate_finish = modem_cdma_activate_finish; iface->activate_manual = modem_cdma_activate_manual; iface->activate_manual_finish = modem_cdma_activate_manual_finish; } static void iface_modem_messaging_init (MMIfaceModemMessaging *iface) { iface_modem_messaging_parent = g_type_interface_peek_parent (iface); iface->check_support = messaging_check_support; iface->check_support_finish = messaging_check_support_finish; iface->load_supported_storages = messaging_load_supported_storages; iface->load_supported_storages_finish = messaging_load_supported_storages_finish; iface->setup_sms_format = modem_messaging_setup_sms_format; iface->setup_sms_format_finish = modem_messaging_setup_sms_format_finish; iface->set_default_storage = messaging_set_default_storage; iface->set_default_storage_finish = messaging_set_default_storage_finish; iface->load_initial_sms_parts = load_initial_sms_parts; iface->load_initial_sms_parts_finish = load_initial_sms_parts_finish; iface->setup_unsolicited_events = messaging_setup_unsolicited_events; iface->setup_unsolicited_events_finish = messaging_setup_unsolicited_events_finish; iface->cleanup_unsolicited_events = messaging_cleanup_unsolicited_events; iface->cleanup_unsolicited_events_finish = messaging_cleanup_unsolicited_events_finish; iface->enable_unsolicited_events = messaging_enable_unsolicited_events; iface->enable_unsolicited_events_finish = messaging_enable_unsolicited_events_finish; iface->disable_unsolicited_events = messaging_disable_unsolicited_events; iface->disable_unsolicited_events_finish = messaging_disable_unsolicited_events_finish; iface->create_sms = messaging_create_sms; } static void iface_modem_location_init (MMIfaceModemLocation *iface) { iface_modem_location_parent = g_type_interface_peek_parent (iface); iface->load_capabilities = location_load_capabilities; iface->load_capabilities_finish = location_load_capabilities_finish; iface->enable_location_gathering = enable_location_gathering; iface->enable_location_gathering_finish = enable_location_gathering_finish; iface->disable_location_gathering = disable_location_gathering; iface->disable_location_gathering_finish = disable_location_gathering_finish; iface->load_supl_server = mm_shared_qmi_location_load_supl_server; iface->load_supl_server_finish = mm_shared_qmi_location_load_supl_server_finish; iface->set_supl_server = mm_shared_qmi_location_set_supl_server; iface->set_supl_server_finish = mm_shared_qmi_location_set_supl_server_finish; iface->load_supported_assistance_data = mm_shared_qmi_location_load_supported_assistance_data; iface->load_supported_assistance_data_finish = mm_shared_qmi_location_load_supported_assistance_data_finish; iface->inject_assistance_data = mm_shared_qmi_location_inject_assistance_data; iface->inject_assistance_data_finish = mm_shared_qmi_location_inject_assistance_data_finish; iface->load_assistance_data_servers = mm_shared_qmi_location_load_assistance_data_servers; iface->load_assistance_data_servers_finish = mm_shared_qmi_location_load_assistance_data_servers_finish; } static void iface_modem_sar_init (MMIfaceModemSar *iface) { iface->check_support = sar_check_support; iface->check_support_finish = sar_check_support_finish; iface->load_state = sar_load_state; iface->load_state_finish = sar_load_state_finish; iface->load_power_level = sar_load_power_level; iface->load_power_level_finish = sar_load_power_level_finish; iface->enable = sar_enable; iface->enable_finish = sar_enable_finish; iface->set_power_level = sar_set_power_level; iface->set_power_level_finish = sar_set_power_level_finish; } static void iface_modem_signal_init (MMIfaceModemSignal *iface) { iface->check_support = signal_check_support; iface->check_support_finish = signal_check_support_finish; iface->load_values = signal_load_values; iface->load_values_finish = signal_load_values_finish; iface->setup_thresholds = signal_setup_thresholds; iface->setup_thresholds_finish = signal_setup_thresholds_finish; } static void iface_modem_oma_init (MMIfaceModemOma *iface) { iface->check_support = oma_check_support; iface->check_support_finish = oma_check_support_finish; iface->load_features = oma_load_features; iface->load_features_finish = oma_load_features_finish; iface->setup = oma_setup; iface->setup_finish = oma_setup_finish; iface->start_client_initiated_session = oma_start_client_initiated_session; iface->start_client_initiated_session_finish = oma_start_client_initiated_session_finish; iface->accept_network_initiated_session = oma_accept_network_initiated_session; iface->accept_network_initiated_session_finish = oma_accept_network_initiated_session_finish; iface->cancel_session = oma_cancel_session; iface->cancel_session_finish = oma_cancel_session_finish; iface->setup_unsolicited_events = oma_setup_unsolicited_events; iface->setup_unsolicited_events_finish = common_oma_setup_cleanup_unsolicited_events_finish; iface->cleanup_unsolicited_events = oma_cleanup_unsolicited_events; iface->cleanup_unsolicited_events_finish = common_oma_setup_cleanup_unsolicited_events_finish; iface->enable_unsolicited_events = oma_enable_unsolicited_events; iface->enable_unsolicited_events_finish = common_oma_enable_disable_unsolicited_events_finish; iface->disable_unsolicited_events = oma_disable_unsolicited_events; iface->disable_unsolicited_events_finish = common_oma_enable_disable_unsolicited_events_finish; } static void iface_modem_firmware_init (MMIfaceModemFirmware *iface) { iface->load_list = firmware_load_list; iface->load_list_finish = firmware_load_list_finish; iface->load_current = firmware_load_current; iface->load_current_finish = firmware_load_current_finish; iface->change_current = firmware_change_current; iface->change_current_finish = firmware_change_current_finish; } static MMIfaceModemLocation * peek_parent_location_interface (MMSharedQmi *self) { return iface_modem_location_parent; } static void shared_qmi_init (MMSharedQmi *iface) { iface->peek_client = shared_qmi_peek_client; iface->peek_parent_location_interface = peek_parent_location_interface; } static void mm_broadband_modem_qmi_class_init (MMBroadbandModemQmiClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); MMBroadbandModemClass *broadband_modem_class = MM_BROADBAND_MODEM_CLASS (klass); g_type_class_add_private (object_class, sizeof (MMBroadbandModemQmiPrivate)); klass->peek_port_qmi_for_data = peek_port_qmi_for_data; object_class->finalize = finalize; object_class->dispose = dispose; broadband_modem_class->initialization_started = initialization_started; broadband_modem_class->initialization_started_finish = initialization_started_finish; broadband_modem_class->enabling_started = enabling_started; broadband_modem_class->enabling_started_finish = enabling_started_finish; /* Do not initialize the QMI modem through AT commands */ broadband_modem_class->enabling_modem_init = NULL; broadband_modem_class->enabling_modem_init_finish = NULL; } ModemManager-1.23.4-dev/src/mm-broadband-modem-qmi.h000066400000000000000000000070051456466623000220750ustar00rootroot00000000000000/* -*- 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) 2012 Google Inc. */ #ifndef MM_BROADBAND_MODEM_QMI_H #define MM_BROADBAND_MODEM_QMI_H #include "mm-broadband-modem.h" #define MM_TYPE_BROADBAND_MODEM_QMI (mm_broadband_modem_qmi_get_type ()) #define MM_BROADBAND_MODEM_QMI(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_BROADBAND_MODEM_QMI, MMBroadbandModemQmi)) #define MM_BROADBAND_MODEM_QMI_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_BROADBAND_MODEM_QMI, MMBroadbandModemQmiClass)) #define MM_IS_BROADBAND_MODEM_QMI(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_BROADBAND_MODEM_QMI)) #define MM_IS_BROADBAND_MODEM_QMI_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_BROADBAND_MODEM_QMI)) #define MM_BROADBAND_MODEM_QMI_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_BROADBAND_MODEM_QMI, MMBroadbandModemQmiClass)) typedef struct _MMBroadbandModemQmi MMBroadbandModemQmi; typedef struct _MMBroadbandModemQmiClass MMBroadbandModemQmiClass; typedef struct _MMBroadbandModemQmiPrivate MMBroadbandModemQmiPrivate; struct _MMBroadbandModemQmi { MMBroadbandModem parent; MMBroadbandModemQmiPrivate *priv; }; struct _MMBroadbandModemQmiClass { MMBroadbandModemClass parent; MMPortQmi * (* peek_port_qmi_for_data) (MMBroadbandModemQmi *self, MMPort *data, MMQmiDataEndpoint *out_endpoint, GError **error); }; GType mm_broadband_modem_qmi_get_type (void); G_DEFINE_AUTOPTR_CLEANUP_FUNC (MMBroadbandModemQmi, g_object_unref) MMBroadbandModemQmi *mm_broadband_modem_qmi_new (const gchar *device, const gchar *physdev, const gchar **drivers, const gchar *plugin, guint16 vendor_id, guint16 product_id); MMPortQmi *mm_broadband_modem_qmi_peek_port_qmi (MMBroadbandModemQmi *self); MMPortQmi *mm_broadband_modem_qmi_peek_port_qmi_for_data (MMBroadbandModemQmi *self, MMPort *data, MMQmiDataEndpoint *out_endpoint, GError **error); MMPortQmi *mm_broadband_modem_qmi_get_port_qmi (MMBroadbandModemQmi *self); MMPortQmi *mm_broadband_modem_qmi_get_port_qmi_for_data (MMBroadbandModemQmi *self, MMPort *data, MMQmiDataEndpoint *out_endpoint, GError **error); #endif /* MM_BROADBAND_MODEM_QMI_H */ ModemManager-1.23.4-dev/src/mm-broadband-modem.c000066400000000000000000017436521456466623000213240ustar00rootroot00000000000000/* -*- 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 - 2012 Red Hat, Inc. * Copyright (C) 2015 Marco Bascetta * Copyright (C) 2019 Purism SPC * Copyright (C) 2011 - 2022 Google, Inc. * Copyright (c) 2022 Qualcomm Innovation Center, Inc. */ #include #include #include #include #include #include #include #define _LIBMM_INSIDE_MM #include #include "mm-base-modem-at.h" #include "mm-broadband-modem.h" #include "mm-iface-modem.h" #include "mm-iface-modem-3gpp.h" #include "mm-iface-modem-3gpp-profile-manager.h" #include "mm-iface-modem-3gpp-ussd.h" #include "mm-iface-modem-cdma.h" #include "mm-iface-modem-simple.h" #include "mm-iface-modem-location.h" #include "mm-iface-modem-messaging.h" #include "mm-iface-modem-voice.h" #include "mm-iface-modem-time.h" #include "mm-iface-modem-firmware.h" #include "mm-iface-modem-sar.h" #include "mm-iface-modem-signal.h" #include "mm-iface-modem-oma.h" #include "mm-broadband-bearer.h" #include "mm-bearer-list.h" #include "mm-sms-list.h" #include "mm-sms-part-3gpp.h" #include "mm-call-list.h" #include "mm-base-sim.h" #include "mm-log-object.h" #include "mm-modem-helpers.h" #include "mm-error-helpers.h" #include "mm-port-serial-qcdm.h" #include "libqcdm/src/errors.h" #include "libqcdm/src/commands.h" #include "libqcdm/src/logs.h" #include "libqcdm/src/log-items.h" #include "mm-helper-enums-types.h" static void iface_modem_init (MMIfaceModem *iface); static void iface_modem_3gpp_init (MMIfaceModem3gpp *iface); static void iface_modem_3gpp_profile_manager_init (MMIfaceModem3gppProfileManager *iface); static void iface_modem_3gpp_ussd_init (MMIfaceModem3gppUssd *iface); static void iface_modem_cdma_init (MMIfaceModemCdma *iface); static void iface_modem_simple_init (MMIfaceModemSimple *iface); static void iface_modem_location_init (MMIfaceModemLocation *iface); static void iface_modem_messaging_init (MMIfaceModemMessaging *iface); static void iface_modem_voice_init (MMIfaceModemVoice *iface); static void iface_modem_time_init (MMIfaceModemTime *iface); static void iface_modem_signal_init (MMIfaceModemSignal *iface); static void iface_modem_oma_init (MMIfaceModemOma *iface); static void iface_modem_firmware_init (MMIfaceModemFirmware *iface); static void iface_modem_sar_init (MMIfaceModemSar *iface); G_DEFINE_TYPE_EXTENDED (MMBroadbandModem, mm_broadband_modem, MM_TYPE_BASE_MODEM, 0, G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM, iface_modem_init) G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_3GPP, iface_modem_3gpp_init) G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_3GPP_PROFILE_MANAGER, iface_modem_3gpp_profile_manager_init) G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_3GPP_USSD, iface_modem_3gpp_ussd_init) G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_CDMA, iface_modem_cdma_init) G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_SIMPLE, iface_modem_simple_init) G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_LOCATION, iface_modem_location_init) G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_MESSAGING, iface_modem_messaging_init) G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_VOICE, iface_modem_voice_init) G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_TIME, iface_modem_time_init) G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_SIGNAL, iface_modem_signal_init) G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_OMA, iface_modem_oma_init) G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_SAR, iface_modem_sar_init) G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_FIRMWARE, iface_modem_firmware_init)) enum { PROP_0, PROP_MODEM_DBUS_SKELETON, PROP_MODEM_3GPP_DBUS_SKELETON, PROP_MODEM_3GPP_PROFILE_MANAGER_DBUS_SKELETON, PROP_MODEM_3GPP_USSD_DBUS_SKELETON, PROP_MODEM_CDMA_DBUS_SKELETON, PROP_MODEM_SIMPLE_DBUS_SKELETON, PROP_MODEM_LOCATION_DBUS_SKELETON, PROP_MODEM_MESSAGING_DBUS_SKELETON, PROP_MODEM_VOICE_DBUS_SKELETON, PROP_MODEM_TIME_DBUS_SKELETON, PROP_MODEM_SIGNAL_DBUS_SKELETON, PROP_MODEM_OMA_DBUS_SKELETON, PROP_MODEM_FIRMWARE_DBUS_SKELETON, PROP_MODEM_SAR_DBUS_SKELETON, PROP_MODEM_SIM, PROP_MODEM_SIM_SLOTS, PROP_MODEM_BEARER_LIST, PROP_MODEM_STATE, PROP_MODEM_3GPP_REGISTRATION_STATE, PROP_MODEM_3GPP_CS_NETWORK_SUPPORTED, PROP_MODEM_3GPP_PS_NETWORK_SUPPORTED, PROP_MODEM_3GPP_EPS_NETWORK_SUPPORTED, PROP_MODEM_3GPP_5GS_NETWORK_SUPPORTED, PROP_MODEM_3GPP_IGNORED_FACILITY_LOCKS, PROP_MODEM_3GPP_INITIAL_EPS_BEARER, PROP_MODEM_3GPP_PACKET_SERVICE_STATE, PROP_MODEM_CDMA_CDMA1X_REGISTRATION_STATE, PROP_MODEM_CDMA_EVDO_REGISTRATION_STATE, PROP_MODEM_CDMA_CDMA1X_NETWORK_SUPPORTED, PROP_MODEM_CDMA_EVDO_NETWORK_SUPPORTED, PROP_MODEM_MESSAGING_SMS_LIST, PROP_MODEM_MESSAGING_SMS_PDU_MODE, PROP_MODEM_MESSAGING_SMS_DEFAULT_STORAGE, PROP_MODEM_LOCATION_ALLOW_GPS_UNMANAGED_ALWAYS, PROP_MODEM_VOICE_CALL_LIST, PROP_MODEM_SIMPLE_STATUS, PROP_MODEM_SIM_HOT_SWAP_SUPPORTED, PROP_MODEM_PERIODIC_SIGNAL_CHECK_DISABLED, PROP_MODEM_PERIODIC_ACCESS_TECH_CHECK_DISABLED, PROP_MODEM_PERIODIC_CALL_LIST_CHECK_DISABLED, PROP_MODEM_INDICATION_CALL_LIST_RELOAD_ENABLED, PROP_MODEM_CARRIER_CONFIG_MAPPING, PROP_MODEM_FIRMWARE_IGNORE_CARRIER, PROP_FLOW_CONTROL, PROP_INDICATORS_DISABLED, PROP_LAST }; static GParamSpec *properties[PROP_LAST]; #if defined WITH_SUSPEND_RESUME enum { SIGNAL_SYNC_NEEDED, SIGNAL_LAST }; static guint signals[SIGNAL_LAST]; #endif /* When CIND is supported, invalid indicators are marked with this value */ #define CIND_INDICATOR_INVALID 255 #define CIND_INDICATOR_IS_VALID(u) (u != CIND_INDICATOR_INVALID) typedef struct _PortsContext PortsContext; struct _MMBroadbandModemPrivate { /* Broadband modem specific implementation */ PortsContext *enabled_ports_ctx; PortsContext *sim_hot_swap_ports_ctx; PortsContext *in_call_ports_ctx; gboolean modem_init_run; gboolean sim_hot_swap_supported; gboolean periodic_signal_check_disabled; gboolean periodic_access_tech_check_disabled; /*<--- Modem interface --->*/ /* Properties */ GObject *modem_dbus_skeleton; MMBaseSim *modem_sim; GPtrArray *modem_sim_slots; MMBearerList *modem_bearer_list; MMModemState modem_state; gchar *carrier_config_mapping; /* Implementation helpers */ MMModemCharset modem_current_charset; gboolean modem_cind_disabled; gboolean modem_cind_support_checked; gboolean modem_cind_supported; guint modem_cind_indicator_signal_quality; guint modem_cind_min_signal_quality; guint modem_cind_max_signal_quality; guint modem_cind_indicator_roaming; guint modem_cind_indicator_service; MM3gppCmerMode modem_cmer_enable_mode; MM3gppCmerMode modem_cmer_disable_mode; MM3gppCmerInd modem_cmer_ind; gboolean modem_cgerep_support_checked; gboolean modem_cgerep_supported; MMFlowControl flow_control; /*<--- Modem 3GPP interface --->*/ /* Properties */ GObject *modem_3gpp_dbus_skeleton; MMModem3gppRegistrationState modem_3gpp_registration_state; gboolean modem_3gpp_cs_network_supported; gboolean modem_3gpp_ps_network_supported; gboolean modem_3gpp_eps_network_supported; gboolean modem_3gpp_5gs_network_supported; /* Implementation helpers */ GPtrArray *modem_3gpp_registration_regex; MMModem3gppFacility modem_3gpp_ignored_facility_locks; MMBaseBearer *modem_3gpp_initial_eps_bearer; MMModem3gppPacketServiceState modem_3gpp_packet_service_state; /*<--- Modem 3GPP Profile Manager interface --->*/ /* Properties */ GObject *modem_3gpp_profile_manager_dbus_skeleton; /*<--- Modem 3GPP USSD interface --->*/ /* Properties */ GObject *modem_3gpp_ussd_dbus_skeleton; /* Implementation helpers */ gboolean use_unencoded_ussd; GTask *pending_ussd_action; /*<--- Modem CDMA interface --->*/ /* Properties */ GObject *modem_cdma_dbus_skeleton; MMModemCdmaRegistrationState modem_cdma_cdma1x_registration_state; MMModemCdmaRegistrationState modem_cdma_evdo_registration_state; gboolean modem_cdma_cdma1x_network_supported; gboolean modem_cdma_evdo_network_supported; GCancellable *modem_cdma_pending_registration_cancellable; /* Implementation helpers */ gboolean checked_sprint_support; gboolean has_spservice; gboolean has_speri; gint evdo_pilot_rssi; /*<--- Modem Simple interface --->*/ /* Properties */ GObject *modem_simple_dbus_skeleton; MMSimpleStatus *modem_simple_status; /*<--- Modem Location interface --->*/ /* Properties */ GObject *modem_location_dbus_skeleton; gboolean modem_location_allow_gps_unmanaged_always; /*<--- Modem Messaging interface --->*/ /* Properties */ GObject *modem_messaging_dbus_skeleton; MMSmsList *modem_messaging_sms_list; gboolean modem_messaging_sms_pdu_mode; MMSmsStorage modem_messaging_sms_default_storage; /* Implementation helpers */ gboolean sms_supported_modes_checked; gboolean mem1_storage_locked; MMSmsStorage current_sms_mem1_storage; gboolean mem2_storage_locked; MMSmsStorage current_sms_mem2_storage; /*<--- Modem Voice interface --->*/ /* Properties */ GObject *modem_voice_dbus_skeleton; MMCallList *modem_voice_call_list; gboolean periodic_call_list_check_disabled; gboolean indication_call_list_reload_enabled; gboolean clcc_supported; /*<--- Modem Time interface --->*/ /* Properties */ GObject *modem_time_dbus_skeleton; /*<--- Modem Signal interface --->*/ /* Properties */ GObject *modem_signal_dbus_skeleton; /*<--- Modem OMA interface --->*/ /* Properties */ GObject *modem_oma_dbus_skeleton; /*<--- Modem Firmware interface --->*/ /* Properties */ GObject *modem_firmware_dbus_skeleton; /*<--- Modem Sar interface --->*/ /* Properties */ GObject *modem_sar_dbus_skeleton; gboolean modem_firmware_ignore_carrier; }; /*****************************************************************************/ /* Generic ports open/close context */ struct _PortsContext { volatile gint ref_count; MMPortSerialAt *primary; gboolean primary_open; MMPortSerialAt *secondary; gboolean secondary_open; MMPortSerialQcdm *qcdm; gboolean qcdm_open; }; static PortsContext * ports_context_ref (PortsContext *ctx) { g_atomic_int_inc (&ctx->ref_count); return ctx; } static void ports_context_unref (PortsContext *ctx) { if (g_atomic_int_dec_and_test (&ctx->ref_count)) { if (ctx->primary) { if (ctx->primary_open) mm_port_serial_close (MM_PORT_SERIAL (ctx->primary)); g_object_unref (ctx->primary); } if (ctx->secondary) { if (ctx->secondary_open) mm_port_serial_close (MM_PORT_SERIAL (ctx->secondary)); g_object_unref (ctx->secondary); } if (ctx->qcdm) { if (ctx->qcdm_open) mm_port_serial_close (MM_PORT_SERIAL (ctx->qcdm)); g_object_unref (ctx->qcdm); } g_free (ctx); } } static gboolean ports_context_open (MMBroadbandModem *self, PortsContext *ctx, gboolean disable_at_init_sequence, gboolean with_at_secondary, gboolean with_qcdm, GError **error) { /* Open primary */ ctx->primary = mm_base_modem_get_port_primary (MM_BASE_MODEM (self)); if (!ctx->primary) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't get primary port"); return FALSE; } /* If we'll need to run modem initialization, disable port init sequence */ if (disable_at_init_sequence) g_object_set (ctx->primary, MM_PORT_SERIAL_AT_INIT_SEQUENCE_ENABLED, FALSE, NULL); if (!mm_port_serial_open (MM_PORT_SERIAL (ctx->primary), error)) { g_prefix_error (error, "Couldn't open primary port: "); return FALSE; } ctx->primary_open = TRUE; /* Open secondary (optional) */ if (with_at_secondary) { ctx->secondary = mm_base_modem_get_port_secondary (MM_BASE_MODEM (self)); if (ctx->secondary) { /* If we'll need to run modem initialization, disable port init sequence */ if (disable_at_init_sequence) g_object_set (ctx->secondary, MM_PORT_SERIAL_AT_INIT_SEQUENCE_ENABLED, FALSE, NULL); if (!mm_port_serial_open (MM_PORT_SERIAL (ctx->secondary), error)) { g_prefix_error (error, "Couldn't open secondary port: "); return FALSE; } ctx->secondary_open = TRUE; } } /* Open qcdm (optional) */ if (with_qcdm) { ctx->qcdm = mm_base_modem_get_port_qcdm (MM_BASE_MODEM (self)); if (ctx->qcdm) { if (!mm_port_serial_open (MM_PORT_SERIAL (ctx->qcdm), error)) { g_prefix_error (error, "Couldn't open QCDM port: "); return FALSE; } ctx->qcdm_open = TRUE; } } return TRUE; } static PortsContext * ports_context_new (void) { PortsContext *ctx; ctx = g_new0 (PortsContext, 1); ctx->ref_count = 1; return ctx; } /*****************************************************************************/ /* Create Bearer (Modem interface) */ static MMBaseBearer * modem_create_bearer_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { return g_task_propagate_pointer (G_TASK (res), error); } static void broadband_bearer_new_ready (GObject *source, GAsyncResult *res, GTask *task) { MMBaseBearer *bearer = NULL; GError *error = NULL; bearer = mm_broadband_bearer_new_finish (res, &error); if (!bearer) g_task_return_error (task, error); else g_task_return_pointer (task, bearer, g_object_unref); g_object_unref (task); } static void modem_create_bearer (MMIfaceModem *self, MMBearerProperties *props, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; task = g_task_new (self, NULL, callback, user_data); /* We just create a MMBroadbandBearer */ mm_obj_dbg (self, "creating broadband bearer in broadband modem..."); mm_broadband_bearer_new (MM_BROADBAND_MODEM (self), props, NULL, /* cancellable */ (GAsyncReadyCallback)broadband_bearer_new_ready, task); } /*****************************************************************************/ /* Create Bearer List (Modem interface) */ static MMBearerList * modem_create_bearer_list (MMIfaceModem *self) { guint n; /* The maximum number of available/connected modems is guessed from * the size of the data ports list. */ n = g_list_length (mm_base_modem_peek_data_ports (MM_BASE_MODEM (self))); mm_obj_dbg (self, "allowed up to %u active bearers", n); /* by default, no multiplexing support */ return mm_bearer_list_new (n, 0); } /*****************************************************************************/ /* Create SIM (Modem interface) */ static MMBaseSim * modem_create_sim_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { return mm_base_sim_new_finish (res, error); } static void modem_create_sim (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data) { /* New generic SIM */ mm_base_sim_new (MM_BASE_MODEM (self), NULL, /* cancellable */ callback, user_data); } /*****************************************************************************/ /* Helper to manage AT-based SIM hot swap ports context */ gboolean mm_broadband_modem_sim_hot_swap_ports_context_init (MMBroadbandModem *self, GError **error) { PortsContext *ports; mm_obj_dbg (self, "creating serial ports context for SIM hot swap..."); ports = ports_context_new (); if (!ports_context_open (self, ports, FALSE, FALSE, FALSE, error)) { ports_context_unref (ports); return FALSE; } g_assert (!self->priv->sim_hot_swap_ports_ctx); self->priv->sim_hot_swap_ports_ctx = ports; return TRUE; } void mm_broadband_modem_sim_hot_swap_ports_context_reset (MMBroadbandModem *self) { if (self->priv->sim_hot_swap_ports_ctx) { mm_obj_dbg (self, "releasing serial ports context for SIM hot swap"); ports_context_unref (self->priv->sim_hot_swap_ports_ctx); self->priv->sim_hot_swap_ports_ctx = NULL; } } /*****************************************************************************/ /* Capabilities loading (Modem interface) */ typedef struct { MMModemCapability caps; MMPortSerialQcdm *qcdm_port; } LoadCapabilitiesContext; static void load_capabilities_context_free (LoadCapabilitiesContext *ctx) { if (ctx->qcdm_port) { mm_port_serial_close (MM_PORT_SERIAL (ctx->qcdm_port)); g_object_unref (ctx->qcdm_port); } g_slice_free (LoadCapabilitiesContext, ctx); } static MMModemCapability modem_load_current_capabilities_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { GError *inner_error = NULL; gssize value; value = g_task_propagate_int (G_TASK (res), &inner_error); if (inner_error) { g_propagate_error (error, inner_error); return MM_MODEM_CAPABILITY_NONE; } return (MMModemCapability)value; } static void current_capabilities_ws46_test_ready (MMBaseModem *self, GAsyncResult *res, GTask *task) { LoadCapabilitiesContext *ctx; const gchar *response; GArray *modes; guint i; gboolean is_2g = FALSE; gboolean is_3g = FALSE; gboolean is_4g = FALSE; gboolean is_5g = FALSE; ctx = g_task_get_task_data (task); /* Completely ignore errors in AT+WS46=? */ response = mm_base_modem_at_command_finish (self, res, NULL); if (!response) goto out; modes = mm_3gpp_parse_ws46_test_response (response, self, NULL); if (!modes) goto out; /* Process list of modes to gather supported ones */ for (i = 0; i < modes->len; i++) { if (g_array_index (modes, MMModemMode, i) & MM_MODEM_MODE_2G) is_2g = TRUE; if (g_array_index (modes, MMModemMode, i) & MM_MODEM_MODE_3G) is_3g = TRUE; if (g_array_index (modes, MMModemMode, i) & MM_MODEM_MODE_4G) is_4g = TRUE; if (g_array_index (modes, MMModemMode, i) & MM_MODEM_MODE_5G) is_5g = TRUE; } /* Add capabilities from modes */ if (is_2g || is_3g) ctx->caps |= MM_MODEM_CAPABILITY_GSM_UMTS; if (is_4g) ctx->caps |= MM_MODEM_CAPABILITY_LTE; if (is_5g) ctx->caps |= MM_MODEM_CAPABILITY_5GNR; /* The +CGSM capability is saying that the modem is a 3GPP modem, but that * doesn't necessarily mean it's a GSM/UMTS modem, it could be a LTE-only * or 5GNR-only device. We did add the GSM_UMTS capability when +CGSM was * found, so now we'll check if the device only reports 4G or 5G modes, and * remove the capability if so. * * Note that we don't change the default +CGSM -> GSM/UMTS logic, we just * fix it up. */ if ((is_4g || is_5g) && !is_2g && !is_3g) ctx->caps &= ~MM_MODEM_CAPABILITY_GSM_UMTS; g_array_unref (modes); out: g_task_return_int (task, ctx->caps); g_object_unref (task); } typedef struct { const gchar *name; MMModemCapability bits; } ModemCaps; static const ModemCaps modem_caps[] = { { "+CGSM", MM_MODEM_CAPABILITY_GSM_UMTS }, { "+CLTE2", MM_MODEM_CAPABILITY_LTE }, /* Novatel */ { "+CLTE1", MM_MODEM_CAPABILITY_LTE }, /* Novatel */ { "+CLTE", MM_MODEM_CAPABILITY_LTE }, { "+CIS707-A", MM_MODEM_CAPABILITY_CDMA_EVDO }, { "+CIS707A", MM_MODEM_CAPABILITY_CDMA_EVDO }, /* Cmotech */ { "+CIS707", MM_MODEM_CAPABILITY_CDMA_EVDO }, { "CIS707", MM_MODEM_CAPABILITY_CDMA_EVDO }, /* Qualcomm Gobi */ { "+CIS707P", MM_MODEM_CAPABILITY_CDMA_EVDO }, { "CIS-856", MM_MODEM_CAPABILITY_CDMA_EVDO }, { "+IS-856", MM_MODEM_CAPABILITY_CDMA_EVDO }, /* Cmotech */ { "CIS-856-A", MM_MODEM_CAPABILITY_CDMA_EVDO }, { "CIS-856A", MM_MODEM_CAPABILITY_CDMA_EVDO }, /* Kyocera KPC680 */ { "+WIRIDIUM", MM_MODEM_CAPABILITY_IRIDIUM }, /* Iridium satellite modems */ { "CDMA 1x", MM_MODEM_CAPABILITY_CDMA_EVDO }, /* Huawei Data07, ATI reply */ /* TODO: FCLASS, MS, ES, DS? */ { NULL } }; static MMBaseModemAtResponseProcessorResult parse_caps_gcap (MMBaseModem *self, gpointer none, const gchar *command, const gchar *response, gboolean last_command, const GError *error, GVariant **result, GError **result_error) { const ModemCaps *cap = modem_caps; guint32 ret = 0; *result = NULL; *result_error = NULL; if (!response) return MM_BASE_MODEM_AT_RESPONSE_PROCESSOR_RESULT_CONTINUE; /* Some modems (Huawei E160g) won't respond to +GCAP with no SIM, but * will respond to ATI. Ignore the error and continue. */ if (strstr (response, "+CME ERROR:") || (error && error->domain == MM_MOBILE_EQUIPMENT_ERROR)) return MM_BASE_MODEM_AT_RESPONSE_PROCESSOR_RESULT_CONTINUE; while (cap->name) { if (strstr (response, cap->name)) ret |= cap->bits; cap++; } /* No result built? */ if (ret == 0) return MM_BASE_MODEM_AT_RESPONSE_PROCESSOR_RESULT_CONTINUE; *result = g_variant_new_uint32 (ret); return MM_BASE_MODEM_AT_RESPONSE_PROCESSOR_RESULT_SUCCESS; } static MMBaseModemAtResponseProcessorResult parse_caps_cpin (MMBaseModem *self, gpointer none, const gchar *command, const gchar *response, gboolean last_command, const GError *error, GVariant **result, GError **result_error) { *result = NULL; *result_error = NULL; if (!response) { if (error && (g_error_matches (error, MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_SIM_NOT_INSERTED) || g_error_matches (error, MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_SIM_FAILURE) || g_error_matches (error, MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_SIM_BUSY) || g_error_matches (error, MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_SIM_WRONG))) { /* At least, it's a GSM modem */ *result = g_variant_new_uint32 (MM_MODEM_CAPABILITY_GSM_UMTS); return MM_BASE_MODEM_AT_RESPONSE_PROCESSOR_RESULT_SUCCESS; } return MM_BASE_MODEM_AT_RESPONSE_PROCESSOR_RESULT_CONTINUE; } if (strcasestr (response, "SIM PIN") || strcasestr (response, "SIM PUK") || strcasestr (response, "PH-SIM PIN") || strcasestr (response, "PH-FSIM PIN") || strcasestr (response, "PH-FSIM PUK") || strcasestr (response, "SIM PIN2") || strcasestr (response, "SIM PUK2") || strcasestr (response, "PH-NET PIN") || strcasestr (response, "PH-NET PUK") || strcasestr (response, "PH-NETSUB PIN") || strcasestr (response, "PH-NETSUB PUK") || strcasestr (response, "PH-SP PIN") || strcasestr (response, "PH-SP PUK") || strcasestr (response, "PH-CORP PIN") || strcasestr (response, "PH-CORP PUK") || strcasestr (response, "READY")) { /* At least, it's a GSM modem */ *result = g_variant_new_uint32 (MM_MODEM_CAPABILITY_GSM_UMTS); return MM_BASE_MODEM_AT_RESPONSE_PROCESSOR_RESULT_SUCCESS; } return MM_BASE_MODEM_AT_RESPONSE_PROCESSOR_RESULT_CONTINUE; } static MMBaseModemAtResponseProcessorResult parse_caps_cgmm (MMBaseModem *self, gpointer none, const gchar *command, const gchar *response, gboolean last_command, const GError *error, GVariant **result, GError **result_error) { *result = NULL; *result_error = NULL; if (!response) return MM_BASE_MODEM_AT_RESPONSE_PROCESSOR_RESULT_CONTINUE; /* This check detects some really old Motorola GPRS dongles and phones */ if (strstr (response, "GSM900") || strstr (response, "GSM1800") || strstr (response, "GSM1900") || strstr (response, "GSM850")) { /* At least, it's a GSM modem */ *result = g_variant_new_uint32 (MM_MODEM_CAPABILITY_GSM_UMTS); return MM_BASE_MODEM_AT_RESPONSE_PROCESSOR_RESULT_SUCCESS; } return MM_BASE_MODEM_AT_RESPONSE_PROCESSOR_RESULT_CONTINUE; } static const MMBaseModemAtCommand capabilities[] = { { "+GCAP", 2, TRUE, parse_caps_gcap }, { "I", 1, TRUE, parse_caps_gcap }, /* yes, really parse as +GCAP */ { "+CPIN?", 1, FALSE, parse_caps_cpin }, { "+CGMM", 1, TRUE, parse_caps_cgmm }, { NULL } }; static void capabilities_sequence_ready (MMBaseModem *self, GAsyncResult *res, GTask *task) { LoadCapabilitiesContext *ctx; GError *error = NULL; GVariant *result; ctx = g_task_get_task_data (task); result = mm_base_modem_at_sequence_finish (self, res, NULL, &error); if (!result) { if (error) g_task_return_error (task, error); else g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "%s", "Failed to determine modem capabilities."); g_object_unref (task); return; } ctx->caps = (MMModemCapability)g_variant_get_uint32 (result); /* Some modems (e.g. Sierra Wireless MC7710 or ZTE MF820D) won't report LTE * capabilities even if they have them. So just run AT+WS46=? as well to see * if the current supported modes includes any LTE-specific mode. * This is not a big deal, as the AT+WS46=? command is a test command with a * cache-able result. * * E.g.: * AT+WS46=? * +WS46: (12,22,25,28,29) * OK * */ if (ctx->caps & MM_MODEM_CAPABILITY_GSM_UMTS && !(ctx->caps & MM_MODEM_CAPABILITY_LTE)) { mm_base_modem_at_command ( self, "+WS46=?", 3, TRUE, /* allow caching, it's a test command */ (GAsyncReadyCallback)current_capabilities_ws46_test_ready, task); return; } /* Otherwise, just set the already retrieved capabilities */ g_task_return_int (task, ctx->caps); g_object_unref (task); } static void load_current_capabilities_at (GTask *task) { MMBroadbandModem *self; self = g_task_get_source_object (task); /* Launch sequence, we will expect a "u" GVariant */ mm_base_modem_at_sequence ( MM_BASE_MODEM (self), capabilities, NULL, /* response_processor_context */ NULL, /* response_processor_context_free */ (GAsyncReadyCallback)capabilities_sequence_ready, task); } static void mode_pref_qcdm_ready (MMPortSerialQcdm *port, GAsyncResult *res, GTask *task) { MMBroadbandModem *self; LoadCapabilitiesContext *ctx; QcdmResult *result; gint err = QCDM_SUCCESS; uint8_t pref = 0; GError *error = NULL; GByteArray *response; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); response = mm_port_serial_qcdm_command_finish (port, res, &error); if (error) { /* Fall back to AT checking */ mm_obj_dbg (self, "failed to load NV ModePref: %s", error->message); g_error_free (error); goto at_caps; } /* Parse the response */ result = qcdm_cmd_nv_get_mode_pref_result ((const gchar *)response->data, response->len, &err); g_byte_array_unref (response); if (!result) { mm_obj_dbg (self, "failed to parse NV ModePref result: %d", err); g_byte_array_unref (response); goto at_caps; } err = qcdm_result_get_u8 (result, QCDM_CMD_NV_GET_MODE_PREF_ITEM_MODE_PREF, &pref); qcdm_result_unref (result); if (err) { mm_obj_dbg (self, "failed to read NV ModePref: %d", err); goto at_caps; } /* Only parse explicit modes; for 'auto' just fall back to whatever * the AT current capabilities probing figures out. */ switch (pref) { case QCDM_CMD_NV_MODE_PREF_ITEM_MODE_PREF_1X_HDR_LTE_ONLY: ctx->caps |= MM_MODEM_CAPABILITY_LTE; /* Fall through */ case QCDM_CMD_NV_MODE_PREF_ITEM_MODE_PREF_1X_ONLY: case QCDM_CMD_NV_MODE_PREF_ITEM_MODE_PREF_HDR_ONLY: case QCDM_CMD_NV_MODE_PREF_ITEM_MODE_PREF_1X_HDR_ONLY: ctx->caps |= MM_MODEM_CAPABILITY_CDMA_EVDO; break; case QCDM_CMD_NV_MODE_PREF_ITEM_MODE_PREF_GSM_UMTS_LTE_ONLY: ctx->caps |= MM_MODEM_CAPABILITY_LTE; /* Fall through */ case QCDM_CMD_NV_MODE_PREF_ITEM_MODE_PREF_GPRS_ONLY: case QCDM_CMD_NV_MODE_PREF_ITEM_MODE_PREF_UMTS_ONLY: case QCDM_CMD_NV_MODE_PREF_ITEM_MODE_PREF_GSM_UMTS_ONLY: ctx->caps |= MM_MODEM_CAPABILITY_GSM_UMTS; break; case QCDM_CMD_NV_MODE_PREF_ITEM_MODE_PREF_LTE_ONLY: ctx->caps |= MM_MODEM_CAPABILITY_LTE; break; default: break; } if (ctx->caps != MM_MODEM_CAPABILITY_NONE) { g_task_return_int (task, ctx->caps); g_object_unref (task); return; } at_caps: load_current_capabilities_at (task); } static void load_current_capabilities_qcdm (GTask *task) { MMBroadbandModem *self; LoadCapabilitiesContext *ctx; GByteArray *cmd; GError *error = NULL; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); ctx->qcdm_port = mm_base_modem_peek_port_qcdm (MM_BASE_MODEM (self)); g_assert (ctx->qcdm_port); if (!mm_port_serial_open (MM_PORT_SERIAL (ctx->qcdm_port), &error)) { mm_obj_dbg (self, "failed to open QCDM port for NV ModePref request: %s", error->message); g_error_free (error); ctx->qcdm_port = NULL; load_current_capabilities_at (task); return; } g_object_ref (ctx->qcdm_port); cmd = g_byte_array_sized_new (300); cmd->len = qcdm_cmd_nv_get_mode_pref_new ((char *) cmd->data, 300, 0); g_assert (cmd->len); mm_port_serial_qcdm_command (ctx->qcdm_port, cmd, 3, NULL, (GAsyncReadyCallback)mode_pref_qcdm_ready, task); g_byte_array_unref (cmd); } static void modem_load_current_capabilities (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data) { LoadCapabilitiesContext *ctx; GTask *task; mm_obj_dbg (self, "loading current capabilities..."); task = g_task_new (self, NULL, callback, user_data); ctx = g_slice_new0 (LoadCapabilitiesContext); g_task_set_task_data (task, ctx, (GDestroyNotify)load_capabilities_context_free); if (mm_base_modem_peek_port_qcdm (MM_BASE_MODEM (self))) load_current_capabilities_qcdm (task); else load_current_capabilities_at (task); } /*****************************************************************************/ /* Manufacturer loading (Modem interface) */ static gchar * sanitize_info_reply (GVariant *v, const char *prefix) { const gchar *reply, *p; gchar *sanitized; /* Strip any leading command reply */ reply = g_variant_get_string (v, NULL); p = strstr (reply, prefix); if (p) reply = p + strlen (prefix); sanitized = g_strdup (reply); return mm_strip_quotes (g_strstrip (sanitized)); } static gchar * modem_load_manufacturer_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { GVariant *result; gchar *manufacturer = NULL; result = mm_base_modem_at_sequence_finish (MM_BASE_MODEM (self), res, NULL, error); if (result) { manufacturer = sanitize_info_reply (result, "GMI:"); mm_obj_dbg (self, "loaded manufacturer: %s", manufacturer); } return manufacturer; } static const MMBaseModemAtCommand manufacturers[] = { { "+CGMI", 3, TRUE, mm_base_modem_response_processor_string_ignore_at_errors }, { "+GMI", 3, TRUE, mm_base_modem_response_processor_string_ignore_at_errors }, { NULL } }; static void modem_load_manufacturer (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data) { mm_obj_dbg (self, "loading manufacturer..."); mm_base_modem_at_sequence ( MM_BASE_MODEM (self), manufacturers, NULL, /* response_processor_context */ NULL, /* response_processor_context_free */ callback, user_data); } /*****************************************************************************/ /* Model loading (Modem interface) */ static gchar * modem_load_model_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { GVariant *result; gchar *model = NULL; result = mm_base_modem_at_sequence_finish (MM_BASE_MODEM (self), res, NULL, error); if (result) { model = sanitize_info_reply (result, "GMM:"); mm_obj_dbg (self, "loaded model: %s", model); } return model; } static const MMBaseModemAtCommand models[] = { { "+CGMM", 3, TRUE, mm_base_modem_response_processor_string_ignore_at_errors }, { "+GMM", 3, TRUE, mm_base_modem_response_processor_string_ignore_at_errors }, { NULL } }; static void modem_load_model (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data) { mm_obj_dbg (self, "loading model..."); mm_base_modem_at_sequence ( MM_BASE_MODEM (self), models, NULL, /* response_processor_context */ NULL, /* response_processor_context_free */ callback, user_data); } /*****************************************************************************/ /* Revision loading */ static gchar * modem_load_revision_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { GVariant *result; gchar *revision = NULL; result = mm_base_modem_at_sequence_finish (MM_BASE_MODEM (self), res, NULL, error); if (result) { revision = sanitize_info_reply (result, "GMR:"); mm_obj_dbg (self, "loaded revision: %s", revision); } return revision; } static const MMBaseModemAtCommand revisions[] = { { "+CGMR", 3, TRUE, mm_base_modem_response_processor_string_ignore_at_errors }, { "+GMR", 3, TRUE, mm_base_modem_response_processor_string_ignore_at_errors }, { NULL } }; static void modem_load_revision (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data) { mm_obj_dbg (self, "loading revision..."); mm_base_modem_at_sequence ( MM_BASE_MODEM (self), revisions, NULL, /* response_processor_context */ NULL, /* response_processor_context_free */ callback, user_data); } /*****************************************************************************/ /* Equipment ID loading (Modem interface) */ static gchar * modem_load_equipment_identifier_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { GVariant *result; gchar *equip_id = NULL, *esn = NULL, *meid = NULL, *imei = NULL; result = mm_base_modem_at_sequence_finish (MM_BASE_MODEM (self), res, NULL, error); if (result) { equip_id = sanitize_info_reply (result, "GSN:"); /* Modems put all sorts of things into the GSN response; sanitize it */ if (mm_parse_gsn (equip_id, &imei, &meid, &esn)) { g_clear_pointer (&equip_id, g_free); if (imei) equip_id = g_strdup (imei); else if (meid) equip_id = g_strdup (meid); else if (esn) equip_id = g_strdup (esn); else g_assert_not_reached (); g_free (esn); g_free (meid); g_free (imei); } else { /* Leave whatever the modem returned alone */ } mm_obj_dbg (self, "loaded equipment identifier: %s", equip_id); } return equip_id; } static const MMBaseModemAtCommand equipment_identifiers[] = { { "+CGSN", 3, TRUE, mm_base_modem_response_processor_string_ignore_at_errors }, { "+GSN", 3, TRUE, mm_base_modem_response_processor_string_ignore_at_errors }, { NULL } }; static void modem_load_equipment_identifier (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data) { const MMBaseModemAtCommand *commands = equipment_identifiers; mm_obj_dbg (self, "loading equipment identifier..."); /* On CDMA-only (non-3GPP) modems, just try +GSN */ if (mm_iface_modem_is_cdma_only (self)) commands++; mm_base_modem_at_sequence ( MM_BASE_MODEM (self), commands, NULL, /* response_processor_context */ NULL, /* response_processor_context_free */ callback, user_data); } /*****************************************************************************/ /* Device identifier loading (Modem interface) */ typedef struct { gchar *ati; gchar *ati1; } DeviceIdentifierContext; static void device_identifier_context_free (DeviceIdentifierContext *ctx) { g_free (ctx->ati); g_free (ctx->ati1); g_free (ctx); } static gchar * modem_load_device_identifier_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { GError *inner_error = NULL; gpointer ctx = NULL; gchar *device_identifier; mm_base_modem_at_sequence_finish (MM_BASE_MODEM (self), res, &ctx, &inner_error); if (inner_error) { g_propagate_error (error, inner_error); return NULL; } g_assert (ctx != NULL); device_identifier = mm_broadband_modem_create_device_identifier ( MM_BROADBAND_MODEM (self), ((DeviceIdentifierContext *)ctx)->ati, ((DeviceIdentifierContext *)ctx)->ati1, &inner_error); if (!device_identifier) { g_propagate_error (error, inner_error); return NULL; } mm_obj_dbg (self, "loaded device identifier: %s", device_identifier); return device_identifier; } static gboolean parse_ati_reply (MMBaseModem *self, DeviceIdentifierContext *ctx, const gchar *command, const gchar *response, gboolean last_command, const GError *error, GVariant **result, GError **result_error) { /* Store the proper string in the proper place */ if (!error) { if (g_str_equal (command, "ATI1")) ctx->ati1 = g_strdup (response); else ctx->ati = g_strdup (response); } /* Always keep on, this is a sequence where all the steps should be taken */ return TRUE; } static const MMBaseModemAtCommand device_identifier_steps[] = { { "ATI", 3, TRUE, (MMBaseModemAtResponseProcessor)parse_ati_reply }, { "ATI1", 3, TRUE, (MMBaseModemAtResponseProcessor)parse_ati_reply }, { NULL } }; static void modem_load_device_identifier (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data) { mm_obj_dbg (self, "loading device identifier..."); mm_base_modem_at_sequence ( MM_BASE_MODEM (self), device_identifier_steps, g_new0 (DeviceIdentifierContext, 1), (GDestroyNotify)device_identifier_context_free, callback, user_data); } /*****************************************************************************/ /* Load own numbers (Modem interface) */ typedef struct { MMPortSerialQcdm *qcdm; } OwnNumbersContext; static void own_numbers_context_free (OwnNumbersContext *ctx) { if (ctx->qcdm) { mm_port_serial_close (MM_PORT_SERIAL (ctx->qcdm)); g_object_unref (ctx->qcdm); } g_free (ctx); } static GStrv modem_load_own_numbers_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { return g_task_propagate_pointer (G_TASK (res), error); } static void mdn_qcdm_ready (MMPortSerialQcdm *port, GAsyncResult *res, GTask *task) { QcdmResult *result; gint err = QCDM_SUCCESS; const char *numbers[2] = { NULL, NULL }; GByteArray *response; GError *error = NULL; response = mm_port_serial_qcdm_command_finish (port, res, &error); if (error) { g_task_return_error (task, error); g_object_unref (task); return; } /* Parse the response */ result = qcdm_cmd_nv_get_mdn_result ((const gchar *) response->data, response->len, &err); g_byte_array_unref (response); if (!result) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Failed to parse NV MDN command result: %d", err); g_object_unref (task); return; } if (qcdm_result_get_string (result, QCDM_CMD_NV_GET_MDN_ITEM_MDN, &numbers[0]) >= 0) { gboolean valid = TRUE; const char *p = numbers[0]; /* Returned NV item data is read directly out of NV memory on the card, * so minimally verify it. */ if (strlen (numbers[0]) < 6 || strlen (numbers[0]) > 15) valid = FALSE; /* MDN is always decimal digits; allow + for good measure */ while (p && *p && valid) valid = g_ascii_isdigit (*p++) || (*p == '+'); if (valid) { g_task_return_pointer (task, g_strdupv ((gchar **) numbers), (GDestroyNotify)g_strfreev); } else { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "%s", "MDN from NV memory appears invalid"); } } else { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "%s", "Failed retrieve MDN"); } g_object_unref (task); qcdm_result_unref (result); } static MMBaseModemAtResponseProcessorResult modem_load_own_numbers_continue_on_sim_busy (MMBaseModem *self, gpointer none, const gchar *command, const gchar *response, gboolean last_command, const GError *error, GVariant **result, GError **result_error) { if (error) { *result = NULL; if (!g_error_matches (error, MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_SIM_BUSY) || last_command) { *result_error = g_error_copy (error); return MM_BASE_MODEM_AT_RESPONSE_PROCESSOR_RESULT_FAILURE; } /* Retry on SIM BUSY errors */ *result_error = NULL; return MM_BASE_MODEM_AT_RESPONSE_PROCESSOR_RESULT_CONTINUE; } *result = g_variant_new_string (response); return MM_BASE_MODEM_AT_RESPONSE_PROCESSOR_RESULT_SUCCESS; } static const MMBaseModemAtCommand own_numbers_sequence[] = { { "+CNUM", 3, FALSE, modem_load_own_numbers_continue_on_sim_busy, 0 }, { "+CNUM", 3, FALSE, modem_load_own_numbers_continue_on_sim_busy, 3 }, { "+CNUM", 3, FALSE, modem_load_own_numbers_continue_on_sim_busy, 3 }, { NULL } }; static void modem_load_own_numbers_ready (MMIfaceModem *self, GAsyncResult *res, GTask *task) { OwnNumbersContext *ctx; GVariant *result; GError *error = NULL; GStrv numbers; ctx = g_task_get_task_data (task); result = mm_base_modem_at_sequence_finish (MM_BASE_MODEM (self), res, NULL, &error); if (!result) { /* try QCDM */ if (ctx->qcdm) { GByteArray *mdn; g_clear_error (&error); mdn = g_byte_array_sized_new (200); mdn->len = qcdm_cmd_nv_get_mdn_new ((char *) mdn->data, 200, 0); g_assert (mdn->len); mm_port_serial_qcdm_command (ctx->qcdm, mdn, 3, NULL, (GAsyncReadyCallback)mdn_qcdm_ready, task); g_byte_array_unref (mdn); return; } g_task_return_error (task, error); } else { numbers = mm_3gpp_parse_cnum_exec_response (g_variant_get_string (result, NULL)); g_task_return_pointer (task, numbers, (GDestroyNotify)g_strfreev); } g_object_unref (task); } static void modem_load_own_numbers (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data) { OwnNumbersContext *ctx; GError *error = NULL; GTask *task; ctx = g_new0 (OwnNumbersContext, 1); ctx->qcdm = mm_base_modem_peek_port_qcdm (MM_BASE_MODEM (self)); if (ctx->qcdm) { if (mm_port_serial_open (MM_PORT_SERIAL (ctx->qcdm), &error)) { ctx->qcdm = g_object_ref (ctx->qcdm); } else { mm_obj_dbg (self, "couldn't open QCDM port: (%d) %s", error ? error->code : -1, error ? error->message : "(unknown)"); ctx->qcdm = NULL; } } task = g_task_new (self, NULL, callback, user_data); g_task_set_task_data (task, ctx, (GDestroyNotify)own_numbers_context_free); mm_obj_dbg (self, "loading own numbers..."); mm_base_modem_at_sequence ( MM_BASE_MODEM (self), own_numbers_sequence, NULL, /* response_processor_context */ NULL, /* response_processor_context_free */ (GAsyncReadyCallback)modem_load_own_numbers_ready, task); } /*****************************************************************************/ /* Check if unlock required (Modem interface) */ typedef struct { const gchar *result; MMModemLock code; } CPinResult; static CPinResult unlock_results[] = { /* Longer entries first so we catch the correct one with strcmp() */ { "READY", MM_MODEM_LOCK_NONE }, { "SIM PIN2", MM_MODEM_LOCK_SIM_PIN2 }, { "SIM PUK2", MM_MODEM_LOCK_SIM_PUK2 }, { "SIM PIN", MM_MODEM_LOCK_SIM_PIN }, { "SIM PUK", MM_MODEM_LOCK_SIM_PUK }, { "PH-NETSUB PIN", MM_MODEM_LOCK_PH_NETSUB_PIN }, { "PH-NETSUB PUK", MM_MODEM_LOCK_PH_NETSUB_PUK }, { "PH-FSIM PIN", MM_MODEM_LOCK_PH_FSIM_PIN }, { "PH-FSIM PUK", MM_MODEM_LOCK_PH_FSIM_PUK }, { "PH-CORP PIN", MM_MODEM_LOCK_PH_CORP_PIN }, { "PH-CORP PUK", MM_MODEM_LOCK_PH_CORP_PUK }, { "PH-SIM PIN", MM_MODEM_LOCK_PH_SIM_PIN }, { "PH-NET PIN", MM_MODEM_LOCK_PH_NET_PIN }, { "PH-NET PUK", MM_MODEM_LOCK_PH_NET_PUK }, { "PH-SP PIN", MM_MODEM_LOCK_PH_SP_PIN }, { "PH-SP PUK", MM_MODEM_LOCK_PH_SP_PUK }, { NULL } }; static MMModemLock modem_load_unlock_required_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { GError *inner_error = NULL; gssize value; value = g_task_propagate_int (G_TASK (res), &inner_error); if (inner_error) { g_propagate_error (error, inner_error); return MM_MODEM_LOCK_UNKNOWN; } return (MMModemLock)value; } static void cpin_query_ready (MMIfaceModem *self, GAsyncResult *res, GTask *task) { MMModemLock lock = MM_MODEM_LOCK_UNKNOWN; const gchar *result; GError *error = NULL; result = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error); if (error) { g_task_return_error (task, error); g_object_unref (task); return; } if (result && strstr (result, "+CPIN:")) { CPinResult *iter = &unlock_results[0]; const gchar *str; str = strstr (result, "+CPIN:") + 6; /* Skip possible whitespaces after '+CPIN:' and before the response */ while (*str == ' ') str++; /* Some phones (Motorola EZX models) seem to quote the response */ if (str[0] == '"') str++; /* Translate the reply */ while (iter->result) { if (g_str_has_prefix (str, iter->result)) { lock = iter->code; break; } iter++; } } g_task_return_int (task, lock); g_object_unref (task); } static void modem_load_unlock_required (MMIfaceModem *self, gboolean last_attempt, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; task = g_task_new (self, cancellable, callback, user_data); /* CDMA-only modems don't need this */ if (mm_iface_modem_is_cdma_only (self)) { mm_obj_dbg (self, "skipping unlock check in CDMA-only modem..."); g_task_return_int (task, MM_MODEM_LOCK_NONE); g_object_unref (task); return; } mm_obj_dbg (self, "checking if unlock required..."); mm_base_modem_at_command (MM_BASE_MODEM (self), "+CPIN?", 10, FALSE, (GAsyncReadyCallback)cpin_query_ready, task); } /*****************************************************************************/ /* Supported modes loading (Modem interface) */ typedef struct { MMUnlockRetries *retries; guint i; } LoadUnlockRetriesContext; typedef struct { MMModemLock lock; const gchar *command; } UnlockRetriesMap; static const UnlockRetriesMap unlock_retries_map [] = { { MM_MODEM_LOCK_SIM_PIN, "+CSIM=10,\"0020000100\"" }, { MM_MODEM_LOCK_SIM_PUK, "+CSIM=10,\"002C000100\"" }, { MM_MODEM_LOCK_SIM_PIN2, "+CSIM=10,\"0020008100\"" }, { MM_MODEM_LOCK_SIM_PUK2, "+CSIM=10,\"002C008100\"" }, }; static void load_unlock_retries_context_free (LoadUnlockRetriesContext *ctx) { g_object_unref (ctx->retries); g_slice_free (LoadUnlockRetriesContext, ctx); } static MMUnlockRetries * load_unlock_retries_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { return g_task_propagate_pointer (G_TASK (res), error); } static void load_unlock_retries_context_step (GTask *task); static void csim_ready (MMBaseModem *self, GAsyncResult *res, GTask *task) { LoadUnlockRetriesContext *ctx; const gchar *response; GError *error = NULL; gint val; ctx = g_task_get_task_data (task); response = mm_base_modem_at_command_finish (self, res, &error); if (!response) { mm_obj_dbg (self, "couldn't load retry count for lock '%s': %s", mm_modem_lock_get_string (unlock_retries_map[ctx->i].lock), error->message); goto next; } val = mm_parse_csim_response (response, &error); if (val < 0) { mm_obj_dbg (self, "couldn't parse retry count value for lock '%s': %s", mm_modem_lock_get_string (unlock_retries_map[ctx->i].lock), error->message); goto next; } mm_unlock_retries_set (ctx->retries, unlock_retries_map[ctx->i].lock, val); next: g_clear_error (&error); /* Go to next lock value */ ctx->i++; load_unlock_retries_context_step (task); } static void load_unlock_retries_context_step (GTask *task) { MMBroadbandModem *self; LoadUnlockRetriesContext *ctx; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); if (ctx->i == G_N_ELEMENTS (unlock_retries_map)) { g_task_return_pointer (task, g_object_ref (ctx->retries), g_object_unref); g_object_unref (task); return; } mm_base_modem_at_command ( MM_BASE_MODEM (self), unlock_retries_map[ctx->i].command, 3, FALSE, (GAsyncReadyCallback)csim_ready, task); } static void load_unlock_retries (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; LoadUnlockRetriesContext *ctx; task = g_task_new (self, NULL, callback, user_data); ctx = g_slice_new0 (LoadUnlockRetriesContext); ctx->retries = mm_unlock_retries_new (); ctx->i = 0; g_task_set_task_data (task, ctx, (GDestroyNotify)load_unlock_retries_context_free); load_unlock_retries_context_step (task); } /*****************************************************************************/ /* Supported modes loading (Modem interface) */ typedef struct { MMModemMode mode; gboolean run_cnti; gboolean run_ws46; gboolean run_gcap; } LoadSupportedModesContext; static GArray * modem_load_supported_modes_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { GError *inner_error = NULL; gssize value; GArray *modes; MMModemModeCombination mode; value = g_task_propagate_int (G_TASK (res), &inner_error); if (inner_error) { g_propagate_error (error, inner_error); return NULL; } /* Build a mask with all supported modes */ modes = g_array_sized_new (FALSE, FALSE, sizeof (MMModemModeCombination), 1); mode.allowed = (MMModemMode) value; mode.preferred = MM_MODEM_MODE_NONE; g_array_append_val (modes, mode); return modes; } static void load_supported_modes_step (GTask *task); static void supported_modes_gcap_ready (MMBaseModem *_self, GAsyncResult *res, GTask *task) { MMBroadbandModem *self = MM_BROADBAND_MODEM (_self); LoadSupportedModesContext *ctx; const gchar *response; GError *error = NULL; ctx = g_task_get_task_data (task); response = mm_base_modem_at_command_finish (_self, res, &error); if (!error) { MMModemMode mode = MM_MODEM_MODE_NONE; if (strstr (response, "IS")) { /* IS-856 is the EV-DO family */ if (strstr (response, "856")) { if (!self->priv->modem_cdma_evdo_network_supported) { self->priv->modem_cdma_evdo_network_supported = TRUE; g_object_notify (G_OBJECT (self), MM_IFACE_MODEM_CDMA_EVDO_NETWORK_SUPPORTED); } mm_obj_dbg (self, "device allows (CDMA) 3G network mode"); mode |= MM_MODEM_MODE_3G; } /* IS-707 is the 1xRTT family, which we consider as 2G */ if (strstr (response, "707")) { if (!self->priv->modem_cdma_cdma1x_network_supported) { self->priv->modem_cdma_cdma1x_network_supported = TRUE; g_object_notify (G_OBJECT (self), MM_IFACE_MODEM_CDMA_CDMA1X_NETWORK_SUPPORTED); } mm_obj_dbg (self, "device allows (CDMA) 2G network mode"); mode |= MM_MODEM_MODE_2G; } } /* If no expected mode found, error */ if (mode == MM_MODEM_MODE_NONE) { /* This should really never happen in the default implementation. */ error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't find specific CDMA mode in capabilities string: '%s'", response); } else { /* Keep our results */ ctx->mode |= mode; } } if (error) { mm_obj_dbg (self, "generic query of supported CDMA networks failed: '%s'", error->message); g_error_free (error); /* Use defaults */ if (self->priv->modem_cdma_cdma1x_network_supported) { mm_obj_dbg (self, "assuming device allows (CDMA) 2G network mode"); ctx->mode |= MM_MODEM_MODE_2G; } if (self->priv->modem_cdma_evdo_network_supported) { mm_obj_dbg (self, "assuming device allows (CDMA) 3G network mode"); ctx->mode |= MM_MODEM_MODE_3G; } } /* Now keep on with the loading, we're probably finishing now */ ctx->run_gcap = FALSE; load_supported_modes_step (task); } static void supported_modes_ws46_test_ready (MMBroadbandModem *self, GAsyncResult *res, GTask *task) { LoadSupportedModesContext *ctx; const gchar *response; GArray *modes; GError *error = NULL; guint i; ctx = g_task_get_task_data (task); response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error); if (error) { mm_obj_dbg (self, "generic query of supported 3GPP networks with WS46=? failed: '%s'", error->message); g_error_free (error); goto out; } modes = mm_3gpp_parse_ws46_test_response (response, self, &error); if (!modes) { mm_obj_dbg (self, "parsing WS46=? response failed: '%s'", error->message); g_error_free (error); goto out; } for (i = 0; i < modes->len; i++) { MMModemMode mode; gchar *str; mode = g_array_index (modes, MMModemMode, i); ctx->mode |= mode; str = mm_modem_mode_build_string_from_mask (mode); mm_obj_dbg (self, "device allows (3GPP) mode combination: %s", str); g_free (str); } g_array_unref (modes); out: /* Now keep on with the loading, we may need CDMA-specific checks */ ctx->run_ws46 = FALSE; load_supported_modes_step (task); } static void supported_modes_cnti_ready (MMBroadbandModem *self, GAsyncResult *res, GTask *task) { LoadSupportedModesContext *ctx; const gchar *response; GError *error = NULL; ctx = g_task_get_task_data (task); response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error); if (!error) { MMModemMode mode = MM_MODEM_MODE_NONE; gchar *lower; lower = g_ascii_strdown (response, -1); if (g_strstr_len (lower, -1, "gsm") || g_strstr_len (lower, -1, "gprs") || g_strstr_len (lower, -1, "edge")) { mm_obj_dbg (self, "device allows (3GPP) 2G networks"); mode |= MM_MODEM_MODE_2G; } if (g_strstr_len (lower, -1, "umts") || g_strstr_len (lower, -1, "hsdpa") || g_strstr_len (lower, -1, "hsupa") || g_strstr_len (lower, -1, "hspa+")) { mm_obj_dbg (self, "device allows (3GPP) 3G networks"); mode |= MM_MODEM_MODE_3G; } if (g_strstr_len (lower, -1, "lte")) { mm_obj_dbg (self, "device allows (3GPP) 4G networks"); mode |= MM_MODEM_MODE_4G; } g_free (lower); /* If no expected ID found, log error */ if (mode == MM_MODEM_MODE_NONE) mm_obj_dbg (self, "invalid list of supported networks reported by *CNTI: '%s'", response); else ctx->mode |= mode; } else { mm_obj_dbg (self, "generic query of supported 3GPP networks with *CNTI failed: '%s'", error->message); g_error_free (error); } /* Now keep on with the loading */ ctx->run_cnti = FALSE; load_supported_modes_step (task); } static void load_supported_modes_step (GTask *task) { MMBroadbandModem *self; LoadSupportedModesContext *ctx; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); if (ctx->run_cnti) { mm_base_modem_at_command ( MM_BASE_MODEM (self), "*CNTI=2", 3, FALSE, (GAsyncReadyCallback)supported_modes_cnti_ready, task); return; } if (ctx->run_ws46) { mm_base_modem_at_command ( MM_BASE_MODEM (self), "+WS46=?", 3, TRUE, /* allow caching, it's a test command */ (GAsyncReadyCallback)supported_modes_ws46_test_ready, task); return; } if (ctx->run_gcap) { mm_base_modem_at_command ( MM_BASE_MODEM (self), "+GCAP", 3, TRUE, /* allow caching */ (GAsyncReadyCallback)supported_modes_gcap_ready, task); return; } /* All done. * If no mode found, error */ if (ctx->mode == MM_MODEM_MODE_NONE) g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't retrieve supported modes"); else g_task_return_int (task, ctx->mode); g_object_unref (task); } static void modem_load_supported_modes (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data) { LoadSupportedModesContext *ctx; GTask *task; mm_obj_dbg (self, "loading supported modes..."); ctx = g_new0 (LoadSupportedModesContext, 1); ctx->mode = MM_MODEM_MODE_NONE; if (mm_iface_modem_is_3gpp (self)) { /* Run +WS46=? and *CNTI=2 */ ctx->run_ws46 = TRUE; ctx->run_cnti = TRUE; } if (mm_iface_modem_is_cdma (self)) { /* Run +GCAP in order to know if the modem is CDMA1x only or CDMA1x/EV-DO */ ctx->run_gcap = TRUE; } task = g_task_new (self, NULL, callback, user_data); g_task_set_task_data (task, ctx, g_free); load_supported_modes_step (task); } /*****************************************************************************/ /* Supported IP families loading (Modem interface) */ static MMBearerIpFamily modem_load_supported_ip_families_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { GError *inner_error = NULL; gssize value; value = g_task_propagate_int (G_TASK (res), &inner_error); if (inner_error) { g_propagate_error (error, inner_error); return MM_BEARER_IP_FAMILY_NONE; } return (MMBearerIpFamily)value; } static void supported_ip_families_cgdcont_test_ready (MMBaseModem *self, GAsyncResult *res, GTask *task) { const gchar *response; GError *error = NULL; MMBearerIpFamily mask = MM_BEARER_IP_FAMILY_NONE; response = mm_base_modem_at_command_finish (self, res, &error); if (response) { GList *formats, *l; formats = mm_3gpp_parse_cgdcont_test_response (response, self, &error); for (l = formats; l; l = g_list_next (l)) mask |= ((MM3gppPdpContextFormat *)(l->data))->pdp_type; mm_3gpp_pdp_context_format_list_free (formats); } if (error) g_task_return_error (task, error); else g_task_return_int (task, mask); g_object_unref (task); } static void modem_load_supported_ip_families (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; mm_obj_dbg (self, "loading supported IP families..."); task = g_task_new (self, NULL, callback, user_data); if (mm_iface_modem_is_cdma_only (self)) { g_task_return_int (task, MM_BEARER_IP_FAMILY_IPV4); g_object_unref (task); return; } /* Query with CGDCONT=? */ mm_base_modem_at_command ( MM_BASE_MODEM (self), "+CGDCONT=?", 3, TRUE, /* allow caching, it's a test command */ (GAsyncReadyCallback)supported_ip_families_cgdcont_test_ready, task); } /*****************************************************************************/ /* Signal quality loading (Modem interface) */ static void qcdm_evdo_pilot_sets_log_handle (MMPortSerialQcdm *port, GByteArray *log_buffer, gpointer user_data) { MMBroadbandModem *self = MM_BROADBAND_MODEM (user_data); QcdmResult *result; uint32_t num_active = 0; uint32_t pilot_pn = 0; uint32_t pilot_energy = 0; int32_t rssi_dbm = 0; result = qcdm_log_item_evdo_pilot_sets_v2_new ((const char *) log_buffer->data, log_buffer->len, NULL); if (!result) return; if (!qcdm_log_item_evdo_pilot_sets_v2_get_num (result, QCDM_LOG_ITEM_EVDO_PILOT_SETS_V2_TYPE_ACTIVE, &num_active)) { qcdm_result_unref (result); return; } if (num_active > 0 && qcdm_log_item_evdo_pilot_sets_v2_get_pilot (result, QCDM_LOG_ITEM_EVDO_PILOT_SETS_V2_TYPE_ACTIVE, 0, &pilot_pn, &pilot_energy, &rssi_dbm)) { mm_obj_dbg (self, "EVDO active pilot RSSI: %ddBm", rssi_dbm); self->priv->evdo_pilot_rssi = rssi_dbm; } qcdm_result_unref (result); } typedef struct { MMPortSerial *at_port; MMPortSerial *qcdm_port; } SignalQualityContext; static void signal_quality_context_free (SignalQualityContext *ctx) { g_clear_object (&ctx->at_port); if (ctx->qcdm_port) { mm_port_serial_close (ctx->qcdm_port); g_object_unref (ctx->qcdm_port); } g_free (ctx); } static guint modem_load_signal_quality_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { gssize value; value = g_task_propagate_int (G_TASK (res), error); return value < 0 ? 0 : value; } static guint signal_quality_evdo_pilot_sets (MMBroadbandModem *self) { if (self->priv->modem_cdma_evdo_registration_state == MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN) return 0; if (self->priv->evdo_pilot_rssi >= 0) return 0; return MM_RSSI_TO_QUALITY (self->priv->evdo_pilot_rssi); } static void signal_quality_csq_ready (MMBroadbandModem *self, GAsyncResult *res, GTask *task) { GError *error = NULL; GVariant *result; const gchar *result_str; result = mm_base_modem_at_sequence_full_finish (MM_BASE_MODEM (self), res, NULL, &error); if (error) { g_task_return_error (task, error); g_object_unref (task); return; } result_str = g_variant_get_string (result, NULL); if (result_str) { /* Got valid reply */ int quality; int ber; result_str = mm_strip_tag (result_str, "+CSQ:"); if (sscanf (result_str, "%d, %d", &quality, &ber)) { if (quality == 99) { /* 99 can mean unknown, no service, etc. But the modem may * also only report CDMA 1x quality in CSQ, so try EVDO via * QCDM log messages too. */ quality = signal_quality_evdo_pilot_sets (self); } else { /* Normalize the quality */ quality = CLAMP (quality, 0, 31) * 100 / 31; } g_task_return_int (task, quality); g_object_unref (task); return; } } g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Could not parse signal quality results"); g_object_unref (task); } /* 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. */ static const MMBaseModemAtCommand signal_quality_csq_sequence[] = { { "+CSQ", 3, FALSE, mm_base_modem_response_processor_string_ignore_at_errors }, { "+CSQ?", 3, FALSE, mm_base_modem_response_processor_string_ignore_at_errors }, { NULL } }; static void signal_quality_csq (GTask *task) { MMBroadbandModem *self; SignalQualityContext *ctx; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); mm_base_modem_at_sequence_full ( MM_BASE_MODEM (self), MM_PORT_SERIAL_AT (ctx->at_port), signal_quality_csq_sequence, NULL, /* response_processor_context */ NULL, /* response_processor_context_free */ NULL, /* cancellable */ (GAsyncReadyCallback)signal_quality_csq_ready, task); } static guint normalize_ciev_cind_signal_quality (guint quality, guint min, guint max) { if (!max) { /* If we didn't get a max, assume it was 5. Note that we do allow * 0, meaning no signal at all. */ return (quality <= 5) ? (quality * 20) : 100; } if (quality >= min && quality <= max) return ((100 * (quality - min)) / (max - min)); /* Value out of range, assume no signal here. Some modems (Cinterion * for example) will send out-of-range values when they cannot get * the signal strength. */ return 0; } static void signal_quality_cind_ready (MMBroadbandModem *self, GAsyncResult *res, GTask *task) { GError *error = NULL; const gchar *result; GByteArray *indicators; guint quality = 0; result = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error); if (error) { g_clear_error (&error); goto try_csq; } indicators = mm_3gpp_parse_cind_read_response (result, &error); if (!indicators) { mm_obj_dbg (self, "could not parse CIND signal quality results: %s", error->message); g_clear_error (&error); goto try_csq; } if (indicators->len < self->priv->modem_cind_indicator_signal_quality) { mm_obj_dbg (self, "could not parse CIND signal quality results; signal " "index (%u) outside received range (0-%u)", self->priv->modem_cind_indicator_signal_quality, indicators->len); } else { quality = g_array_index (indicators, guint8, self->priv->modem_cind_indicator_signal_quality); quality = normalize_ciev_cind_signal_quality (quality, self->priv->modem_cind_min_signal_quality, self->priv->modem_cind_max_signal_quality); } g_byte_array_free (indicators, TRUE); if (quality > 0) { /* +CIND success */ g_task_return_int (task, quality); g_object_unref (task); return; } try_csq: /* Always fall back to +CSQ if for whatever reason +CIND failed. Also, * some QMI-based devices say they support signal via CIND, but always * report zero even though they have signal. So if we get zero signal * from +CIND, try CSQ too. (bgo #636040) */ signal_quality_csq (task); } static void signal_quality_cind (GTask *task) { MMBroadbandModem *self; SignalQualityContext *ctx; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); mm_base_modem_at_command_full (MM_BASE_MODEM (self), MM_PORT_SERIAL_AT (ctx->at_port), "+CIND?", 5, FALSE, FALSE, /* raw */ NULL, /* cancellable */ (GAsyncReadyCallback)signal_quality_cind_ready, task); } static void signal_quality_qcdm_ready (MMPortSerialQcdm *port, GAsyncResult *res, GTask *task) { QcdmResult *result; guint32 num = 0, quality = 0, i; gfloat best_db = -28; gint err = QCDM_SUCCESS; GByteArray *response; GError *error = NULL; response = mm_port_serial_qcdm_command_finish (port, res, &error); if (error) { g_task_return_error (task, error); g_object_unref (task); return; } /* Parse the response */ result = qcdm_cmd_pilot_sets_result ((const gchar *) response->data, response->len, &err); g_byte_array_unref (response); if (!result) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Failed to parse pilot sets command result: %d", err); g_object_unref (task); return; } qcdm_cmd_pilot_sets_result_get_num (result, QCDM_CMD_PILOT_SETS_TYPE_ACTIVE, &num); for (i = 0; i < num; i++) { guint32 pn_offset = 0, ecio = 0; gfloat db = 0; qcdm_cmd_pilot_sets_result_get_pilot (result, QCDM_CMD_PILOT_SETS_TYPE_ACTIVE, i, &pn_offset, &ecio, &db); best_db = MAX (db, best_db); } qcdm_result_unref (result); if (num > 0) { #define BEST_ECIO 3 #define WORST_ECIO 25 /* EC/IO dB ranges from roughly 0 to -31 dB. Lower == worse. We * really only care about -3 to -25 dB though, since that's about what * you'll see in real-world usage. */ best_db = CLAMP (ABS (best_db), BEST_ECIO, WORST_ECIO) - BEST_ECIO; quality = (guint32) (100 - (best_db * 100 / (WORST_ECIO - BEST_ECIO))); } g_task_return_int (task, quality); g_object_unref (task); } static void signal_quality_qcdm (GTask *task) { MMBroadbandModem *self; SignalQualityContext *ctx; GByteArray *pilot_sets; guint quality; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); /* If EVDO is active try that signal strength first */ quality = signal_quality_evdo_pilot_sets (self); if (quality > 0) { g_task_return_int (task, quality); g_object_unref (task); return; } /* Use CDMA1x pilot EC/IO if we can */ pilot_sets = g_byte_array_sized_new (25); pilot_sets->len = qcdm_cmd_pilot_sets_new ((char *) pilot_sets->data, 25); g_assert (pilot_sets->len); mm_port_serial_qcdm_command (MM_PORT_SERIAL_QCDM (ctx->qcdm_port), pilot_sets, 3, NULL, (GAsyncReadyCallback)signal_quality_qcdm_ready, task); g_byte_array_unref (pilot_sets); } static void modem_load_signal_quality (MMIfaceModem *_self, GAsyncReadyCallback callback, gpointer user_data) { MMBroadbandModem *self = MM_BROADBAND_MODEM (_self); SignalQualityContext *ctx; GError *error = NULL; GTask *task; mm_obj_dbg (self, "loading signal quality..."); ctx = g_new0 (SignalQualityContext, 1); task = g_task_new (self, NULL, callback, user_data); g_task_set_task_data (task, ctx, (GDestroyNotify)signal_quality_context_free); /* Check whether we can get a non-connected AT port */ ctx->at_port = (MMPortSerial *)mm_base_modem_get_best_at_port (MM_BASE_MODEM (self), &error); if (ctx->at_port) { if (!self->priv->modem_cind_disabled && self->priv->modem_cind_supported && CIND_INDICATOR_IS_VALID (self->priv->modem_cind_indicator_signal_quality)) signal_quality_cind (task); else signal_quality_csq (task); return; } /* If no best AT port available (all connected), try with QCDM ports */ ctx->qcdm_port = (MMPortSerial *)mm_base_modem_peek_port_qcdm (MM_BASE_MODEM (self)); if (ctx->qcdm_port) { g_clear_error (&error); /* Need to open QCDM port as it may be closed/blocked */ if (mm_port_serial_open (MM_PORT_SERIAL (ctx->qcdm_port), &error)) { g_object_ref (ctx->qcdm_port); signal_quality_qcdm (task); return; } ctx->qcdm_port = NULL; mm_obj_dbg (self, "couldn't open QCDM port: %s", error->message); } /* Return the error we got when getting best AT port */ g_task_return_error (task, error); g_object_unref (task); } /*****************************************************************************/ /* Load access technology (Modem interface) */ typedef struct { MMModemAccessTechnology access_technologies; guint mask; } AccessTechAndMask; static gboolean modem_load_access_technologies_finish (MMIfaceModem *self, GAsyncResult *res, MMModemAccessTechnology *access_technologies, guint *mask, GError **error) { AccessTechAndMask *tech; tech = g_task_propagate_pointer (G_TASK (res), error); if (!tech) return FALSE; *access_technologies = tech->access_technologies; *mask = tech->mask; g_free (tech); return TRUE; } typedef struct { MMPortSerialQcdm *port; guint32 opmode; guint32 sysmode; gboolean hybrid; gboolean wcdma_open; gboolean evdo_open; MMModemAccessTechnology fallback_act; guint fallback_mask; } AccessTechContext; static void access_tech_context_free (AccessTechContext *ctx) { if (ctx->port) { mm_port_serial_close (MM_PORT_SERIAL (ctx->port)); g_object_unref (ctx->port); } g_free (ctx); } static AccessTechAndMask * access_tech_and_mask_new (MMBroadbandModem *self, AccessTechContext *ctx) { AccessTechAndMask *tech; MMModemAccessTechnology act = MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN; guint mask = MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN; if (ctx->fallback_mask) { mm_obj_dbg (self, "fallback access technology: 0x%08x", ctx->fallback_act); act = ctx->fallback_act; mask = ctx->fallback_mask; goto done; } mm_obj_dbg (self, "QCDM operating mode: %d", ctx->opmode); mm_obj_dbg (self, "QCDM system mode: %d", ctx->sysmode); mm_obj_dbg (self, "QCDM hybrid pref: %d", ctx->hybrid); mm_obj_dbg (self, "QCDM WCDMA open: %d", ctx->wcdma_open); mm_obj_dbg (self, "QCDM EVDO open: %d", ctx->evdo_open); if (ctx->opmode == QCDM_CMD_CM_SUBSYS_STATE_INFO_OPERATING_MODE_ONLINE) { switch (ctx->sysmode) { case QCDM_CMD_CM_SUBSYS_STATE_INFO_SYSTEM_MODE_CDMA: if (!ctx->hybrid || !ctx->evdo_open) { act = MM_MODEM_ACCESS_TECHNOLOGY_1XRTT; mask = MM_IFACE_MODEM_CDMA_ALL_ACCESS_TECHNOLOGIES_MASK; break; } /* Fall through */ case QCDM_CMD_CM_SUBSYS_STATE_INFO_SYSTEM_MODE_HDR: /* Assume EVDOr0; can't yet determine r0 vs. rA with QCDM */ if (ctx->evdo_open) act = MM_MODEM_ACCESS_TECHNOLOGY_EVDO0; mask = MM_IFACE_MODEM_CDMA_ALL_ACCESS_TECHNOLOGIES_MASK; break; case QCDM_CMD_CM_SUBSYS_STATE_INFO_SYSTEM_MODE_GSM: case QCDM_CMD_CM_SUBSYS_STATE_INFO_SYSTEM_MODE_GW: if (ctx->wcdma_open) { /* Assume UMTS; can't yet determine UMTS/HSxPA/HSPA+ with QCDM */ act = MM_MODEM_ACCESS_TECHNOLOGY_UMTS; } else { /* Assume GPRS; can't yet determine GSM/GPRS/EDGE with QCDM */ act = MM_MODEM_ACCESS_TECHNOLOGY_GPRS; } mask = MM_IFACE_MODEM_3GPP_ALL_ACCESS_TECHNOLOGIES_MASK; break; case QCDM_CMD_CM_SUBSYS_STATE_INFO_SYSTEM_MODE_WCDMA: if (ctx->wcdma_open) { /* Assume UMTS; can't yet determine UMTS/HSxPA/HSPA+ with QCDM */ act = MM_MODEM_ACCESS_TECHNOLOGY_UMTS; } mask = MM_IFACE_MODEM_3GPP_ALL_ACCESS_TECHNOLOGIES_MASK; break; case QCDM_CMD_CM_SUBSYS_STATE_INFO_SYSTEM_MODE_LTE: act = MM_MODEM_ACCESS_TECHNOLOGY_LTE; mask = MM_IFACE_MODEM_3GPP_ALL_ACCESS_TECHNOLOGIES_MASK; break; default: break; } } done: tech = g_new0 (AccessTechAndMask, 1); tech->access_technologies = act; tech->mask = mask; return tech; } static void access_tech_qcdm_wcdma_ready (MMPortSerialQcdm *port, GAsyncResult *res, GTask *task) { MMBroadbandModem *self; AccessTechContext *ctx; QcdmResult *result; gint err = QCDM_SUCCESS; guint8 l1; GError *error = NULL; GByteArray *response; response = mm_port_serial_qcdm_command_finish (port, res, &error); if (error) { g_task_return_error (task, error); g_object_unref (task); return; } self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); /* Parse the response */ result = qcdm_cmd_wcdma_subsys_state_info_result ((const gchar *) response->data, response->len, &err); g_byte_array_unref (response); if (result) { qcdm_result_get_u8 (result, QCDM_CMD_WCDMA_SUBSYS_STATE_INFO_ITEM_L1_STATE, &l1); qcdm_result_unref (result); if (l1 == QCDM_WCDMA_L1_STATE_PCH || l1 == QCDM_WCDMA_L1_STATE_FACH || l1 == QCDM_WCDMA_L1_STATE_DCH || l1 == QCDM_WCDMA_L1_STATE_PCH_SLEEP) ctx->wcdma_open = TRUE; } g_task_return_pointer (task, access_tech_and_mask_new (MM_BROADBAND_MODEM (self), ctx), g_free); g_object_unref (task); } static void access_tech_qcdm_gsm_ready (MMPortSerialQcdm *port, GAsyncResult *res, GTask *task) { AccessTechContext *ctx; GByteArray *cmd; QcdmResult *result; gint err = QCDM_SUCCESS; guint8 opmode = 0; guint8 sysmode = 0; GError *error = NULL; GByteArray *response; response = mm_port_serial_qcdm_command_finish (port, res, &error); if (error) { g_task_return_error (task, error); g_object_unref (task); return; } /* Parse the response */ result = qcdm_cmd_gsm_subsys_state_info_result ((const gchar *) response->data, response->len, &err); g_byte_array_unref (response); if (!result) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Failed to parse GSM subsys command result: %d", err); g_object_unref (task); return; } ctx = g_task_get_task_data (task); qcdm_result_get_u8 (result, QCDM_CMD_GSM_SUBSYS_STATE_INFO_ITEM_CM_OP_MODE, &opmode); qcdm_result_get_u8 (result, QCDM_CMD_GSM_SUBSYS_STATE_INFO_ITEM_CM_SYS_MODE, &sysmode); qcdm_result_unref (result); ctx->opmode = opmode; ctx->sysmode = sysmode; /* WCDMA subsystem state */ cmd = g_byte_array_sized_new (50); cmd->len = qcdm_cmd_wcdma_subsys_state_info_new ((char *) cmd->data, 50); g_assert (cmd->len); mm_port_serial_qcdm_command (port, cmd, 3, NULL, (GAsyncReadyCallback)access_tech_qcdm_wcdma_ready, task); g_byte_array_unref (cmd); } static void access_tech_qcdm_hdr_ready (MMPortSerialQcdm *port, GAsyncResult *res, GTask *task) { MMBroadbandModem *self; AccessTechContext *ctx; QcdmResult *result; gint err = QCDM_SUCCESS; guint8 session = 0; guint8 almp = 0; GError *error = NULL; GByteArray *response; response = mm_port_serial_qcdm_command_finish (port, res, &error); if (error) { g_task_return_error (task, error); g_object_unref (task); return; } self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); /* Parse the response */ result = qcdm_cmd_hdr_subsys_state_info_result ((const gchar *) response->data, response->len, &err); g_byte_array_unref (response); if (result) { qcdm_result_get_u8 (result, QCDM_CMD_HDR_SUBSYS_STATE_INFO_ITEM_SESSION_STATE, &session); qcdm_result_get_u8 (result, QCDM_CMD_HDR_SUBSYS_STATE_INFO_ITEM_ALMP_STATE, &almp); qcdm_result_unref (result); if (session == QCDM_CMD_HDR_SUBSYS_STATE_INFO_SESSION_STATE_OPEN && (almp == QCDM_CMD_HDR_SUBSYS_STATE_INFO_ALMP_STATE_IDLE || almp == QCDM_CMD_HDR_SUBSYS_STATE_INFO_ALMP_STATE_CONNECTED)) ctx->evdo_open = TRUE; } g_task_return_pointer (task, access_tech_and_mask_new (self, ctx), g_free); g_object_unref (task); } static void access_tech_qcdm_cdma_ready (MMPortSerialQcdm *port, GAsyncResult *res, GTask *task) { AccessTechContext *ctx; GByteArray *cmd; QcdmResult *result; gint err = QCDM_SUCCESS; guint32 hybrid; GError *error = NULL; GByteArray *response; response = mm_port_serial_qcdm_command_finish (port, res, &error); if (error) { g_task_return_error (task, error); g_object_unref (task); return; } /* Parse the response */ result = qcdm_cmd_cm_subsys_state_info_result ((const gchar *) response->data, response->len, &err); g_byte_array_unref (response); if (!result) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Failed to parse CM subsys command result: %d", err); g_object_unref (task); return; } ctx = g_task_get_task_data (task); qcdm_result_get_u32 (result, QCDM_CMD_CM_SUBSYS_STATE_INFO_ITEM_OPERATING_MODE, &ctx->opmode); qcdm_result_get_u32 (result, QCDM_CMD_CM_SUBSYS_STATE_INFO_ITEM_SYSTEM_MODE, &ctx->sysmode); qcdm_result_get_u32 (result, QCDM_CMD_CM_SUBSYS_STATE_INFO_ITEM_HYBRID_PREF, &hybrid); qcdm_result_unref (result); ctx->hybrid = !!hybrid; /* HDR subsystem state */ cmd = g_byte_array_sized_new (50); cmd->len = qcdm_cmd_hdr_subsys_state_info_new ((char *) cmd->data, 50); g_assert (cmd->len); mm_port_serial_qcdm_command (port, cmd, 3, NULL, (GAsyncReadyCallback)access_tech_qcdm_hdr_ready, task); g_byte_array_unref (cmd); } static void access_tech_from_cdma_registration_state (MMBroadbandModem *self, AccessTechContext *ctx) { gboolean cdma1x_registered = FALSE; gboolean evdo_registered = FALSE; if (self->priv->modem_cdma_evdo_registration_state > MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN) evdo_registered = TRUE; if (self->priv->modem_cdma_cdma1x_registration_state > MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN) cdma1x_registered = TRUE; if (self->priv->modem_cdma_evdo_network_supported && evdo_registered) { ctx->fallback_act = MM_MODEM_ACCESS_TECHNOLOGY_EVDO0; ctx->fallback_mask = MM_IFACE_MODEM_CDMA_ALL_ACCESS_TECHNOLOGIES_MASK; } else if (self->priv->modem_cdma_cdma1x_network_supported && cdma1x_registered) { ctx->fallback_act = MM_MODEM_ACCESS_TECHNOLOGY_1XRTT; ctx->fallback_mask = MM_IFACE_MODEM_CDMA_ALL_ACCESS_TECHNOLOGIES_MASK; } mm_obj_dbg (self, "EVDO registration: %d", self->priv->modem_cdma_evdo_registration_state); mm_obj_dbg (self, "CDMA1x registration: %d", self->priv->modem_cdma_cdma1x_registration_state); mm_obj_dbg (self, "fallback access tech: 0x%08x", ctx->fallback_act); } static void modem_load_access_technologies (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data) { AccessTechContext *ctx; GTask *task; GByteArray *cmd; GError *error = NULL; /* For modems where only QCDM provides detailed information, try to * get access technologies via the various QCDM subsystems or from * registration state */ ctx = g_new0 (AccessTechContext, 1); ctx->port = mm_base_modem_peek_port_qcdm (MM_BASE_MODEM (self)); task = g_task_new (self, NULL, callback, user_data); g_task_set_task_data (task, ctx, (GDestroyNotify)access_tech_context_free); if (ctx->port) { /* Need to open QCDM port as it may be closed/blocked */ if (mm_port_serial_open (MM_PORT_SERIAL (ctx->port), &error)) { g_object_ref (ctx->port); mm_obj_dbg (self, "loading access technologies via QCDM..."); /* FIXME: we may want to run both the CDMA and 3GPP in sequence to ensure * that a multi-mode device that's in CDMA-mode but still has 3GPP capabilities * will get the correct access tech, since the 3GPP check is run first. */ if (mm_iface_modem_is_3gpp (self)) { cmd = g_byte_array_sized_new (50); cmd->len = qcdm_cmd_gsm_subsys_state_info_new ((char *) cmd->data, 50); g_assert (cmd->len); mm_port_serial_qcdm_command (ctx->port, cmd, 3, NULL, (GAsyncReadyCallback)access_tech_qcdm_gsm_ready, task); g_byte_array_unref (cmd); return; } if (mm_iface_modem_is_cdma (self)) { cmd = g_byte_array_sized_new (50); cmd->len = qcdm_cmd_cm_subsys_state_info_new ((char *) cmd->data, 50); g_assert (cmd->len); mm_port_serial_qcdm_command (ctx->port, cmd, 3, NULL, (GAsyncReadyCallback)access_tech_qcdm_cdma_ready, task); g_byte_array_unref (cmd); return; } g_assert_not_reached (); } ctx->port = NULL; mm_obj_dbg (self, "couldn't open QCDM port: %s", error->message); g_clear_error (&error); } /* Fall back if we don't have a QCDM port or it couldn't be opened */ if (mm_iface_modem_is_cdma (self)) { /* If we don't have a QCDM port but the modem is CDMA-only, then * guess access technologies from the registration information. */ access_tech_from_cdma_registration_state (MM_BROADBAND_MODEM (self), ctx); g_task_return_pointer (task, access_tech_and_mask_new (MM_BROADBAND_MODEM (self), ctx), g_free); } else { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "Cannot get 3GPP access technology without a QCDM port"); } g_object_unref (task); } /*****************************************************************************/ /* Setup/Cleanup unsolicited events (3GPP interface) */ static gboolean modem_3gpp_setup_cleanup_unsolicited_events_finish (MMIfaceModem3gpp *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void bearer_report_disconnected (MMBaseBearer *bearer, gpointer user_data) { gint profile_id; profile_id = GPOINTER_TO_INT (user_data); /* If we're told to disconnect a single context and this is not the * bearer associated to that context, ignore operation */ if ((profile_id != MM_3GPP_PROFILE_ID_UNKNOWN) && (mm_base_bearer_get_profile_id (bearer) != profile_id)) return; /* If already disconnected, ignore operation */ if (mm_base_bearer_get_status (bearer) == MM_BEARER_STATUS_DISCONNECTED) return; mm_obj_msg (bearer, "explicitly disconnected"); mm_base_bearer_report_connection_status (bearer, MM_BEARER_CONNECTION_STATUS_DISCONNECTED); } static void bearer_list_report_disconnections (MMBroadbandModem *self, gint profile_id) { g_autoptr(MMBearerList) list = NULL; g_object_get (self, MM_IFACE_MODEM_BEARER_LIST, &list, NULL); /* If empty bearer list, nothing else to do */ if (list) mm_bearer_list_foreach (list, (MMBearerListForeachFunc)bearer_report_disconnected, GINT_TO_POINTER (profile_id)); } static void cgev_process_detach (MMBroadbandModem *self, MM3gppCgev type) { if (type == MM_3GPP_CGEV_NW_DETACH) { mm_obj_msg (self, "network forced PS detach: all contexts have been deactivated"); bearer_list_report_disconnections (self, MM_3GPP_PROFILE_ID_UNKNOWN); return; } if (type == MM_3GPP_CGEV_ME_DETACH) { mm_obj_msg (self, "mobile equipment forced PS detach: all contexts have been deactivated"); bearer_list_report_disconnections (self, MM_3GPP_PROFILE_ID_UNKNOWN); return; } g_assert_not_reached (); } static void cgev_process_primary (MMBroadbandModem *self, MM3gppCgev type, const gchar *str) { GError *error = NULL; guint cid = 0; if (!mm_3gpp_parse_cgev_indication_primary (str, type, &cid, &error)) { mm_obj_warn (self, "couldn't parse cid info from +CGEV indication '%s': %s", str, error->message); g_error_free (error); return; } switch (type) { case MM_3GPP_CGEV_NW_ACT_PRIMARY: mm_obj_msg (self, "network request to activate context (cid %u)", cid); break; case MM_3GPP_CGEV_ME_ACT_PRIMARY: mm_obj_msg (self, "mobile equipment request to activate context (cid %u)", cid); break; case MM_3GPP_CGEV_NW_DEACT_PRIMARY: mm_obj_msg (self, "network request to deactivate context (cid %u)", cid); bearer_list_report_disconnections (self, (gint)cid); break; case MM_3GPP_CGEV_ME_DEACT_PRIMARY: mm_obj_msg (self, "mobile equipment request to deactivate context (cid %u)", cid); bearer_list_report_disconnections (self, (gint)cid); break; case MM_3GPP_CGEV_UNKNOWN: case MM_3GPP_CGEV_NW_DETACH: case MM_3GPP_CGEV_ME_DETACH: case MM_3GPP_CGEV_NW_CLASS: case MM_3GPP_CGEV_ME_CLASS: case MM_3GPP_CGEV_NW_ACT_SECONDARY: case MM_3GPP_CGEV_ME_ACT_SECONDARY: case MM_3GPP_CGEV_NW_DEACT_SECONDARY: case MM_3GPP_CGEV_ME_DEACT_SECONDARY: case MM_3GPP_CGEV_NW_DEACT_PDP: case MM_3GPP_CGEV_ME_DEACT_PDP: case MM_3GPP_CGEV_NW_MODIFY: case MM_3GPP_CGEV_ME_MODIFY: case MM_3GPP_CGEV_REJECT: case MM_3GPP_CGEV_NW_REACT: default: g_assert_not_reached (); break; } } static void cgev_process_secondary (MMBroadbandModem *self, MM3gppCgev type, const gchar *str) { GError *error = NULL; guint p_cid = 0; guint cid = 0; if (!mm_3gpp_parse_cgev_indication_secondary (str, type, &p_cid, &cid, NULL, &error)) { mm_obj_warn (self, "couldn't parse p_cid/cid info from +CGEV indication '%s': %s", str, error->message); g_error_free (error); return; } switch (type) { case MM_3GPP_CGEV_NW_ACT_SECONDARY: mm_obj_msg (self, "network request to activate secondary context (cid %u, primary cid %u)", cid, p_cid); break; case MM_3GPP_CGEV_ME_ACT_SECONDARY: mm_obj_msg (self, "mobile equipment request to activate secondary context (cid %u, primary cid %u)", cid, p_cid); break; case MM_3GPP_CGEV_NW_DEACT_SECONDARY: mm_obj_msg (self, "network request to deactivate secondary context (cid %u, primary cid %u)", cid, p_cid); bearer_list_report_disconnections (self, (gint)cid); break; case MM_3GPP_CGEV_ME_DEACT_SECONDARY: mm_obj_msg (self, "mobile equipment request to deactivate secondary context (cid %u, primary cid %u)", cid, p_cid); bearer_list_report_disconnections (self, (gint)cid); break; case MM_3GPP_CGEV_UNKNOWN: case MM_3GPP_CGEV_NW_DETACH: case MM_3GPP_CGEV_ME_DETACH: case MM_3GPP_CGEV_NW_CLASS: case MM_3GPP_CGEV_ME_CLASS: case MM_3GPP_CGEV_NW_ACT_PRIMARY: case MM_3GPP_CGEV_ME_ACT_PRIMARY: case MM_3GPP_CGEV_NW_DEACT_PRIMARY: case MM_3GPP_CGEV_ME_DEACT_PRIMARY: case MM_3GPP_CGEV_NW_DEACT_PDP: case MM_3GPP_CGEV_ME_DEACT_PDP: case MM_3GPP_CGEV_NW_MODIFY: case MM_3GPP_CGEV_ME_MODIFY: case MM_3GPP_CGEV_REJECT: case MM_3GPP_CGEV_NW_REACT: default: g_assert_not_reached (); break; } } static void cgev_process_pdp (MMBroadbandModem *self, MM3gppCgev type, const gchar *str) { GError *error = NULL; gchar *pdp_type = NULL; gchar *pdp_addr = NULL; guint cid = 0; if (!mm_3gpp_parse_cgev_indication_pdp (str, type, &pdp_type, &pdp_addr, &cid, &error)) { mm_obj_warn (self, "couldn't parse PDP info from +CGEV indication '%s': %s", str, error->message); g_error_free (error); return; } switch (type) { case MM_3GPP_CGEV_REJECT: mm_obj_msg (self, "network request to activate context (type %s, address %s) has been automatically rejected", pdp_type, pdp_addr); break; case MM_3GPP_CGEV_NW_REACT: /* NOTE: we don't currently notify about automatic reconnections like this one */ if (cid) mm_obj_msg (self, "network request to reactivate context (type %s, address %s, cid %u)", pdp_type, pdp_addr, cid); else mm_obj_msg (self, "network request to reactivate context (type %s, address %s, cid unknown)", pdp_type, pdp_addr); break; case MM_3GPP_CGEV_NW_DEACT_PDP: if (cid) { mm_obj_msg (self, "network request to deactivate context (type %s, address %s, cid %u)", pdp_type, pdp_addr, cid); bearer_list_report_disconnections (self, (gint)cid); } else mm_obj_msg (self, "network request to deactivate context (type %s, address %s, cid unknown)", pdp_type, pdp_addr); break; case MM_3GPP_CGEV_ME_DEACT_PDP: if (cid) { mm_obj_msg (self, "mobile equipment request to deactivate context (type %s, address %s, cid %u)", pdp_type, pdp_addr, cid); bearer_list_report_disconnections (self, (gint)cid); } else mm_obj_msg (self, "mobile equipment request to deactivate context (type %s, address %s, cid unknown)", pdp_type, pdp_addr); break; case MM_3GPP_CGEV_UNKNOWN: case MM_3GPP_CGEV_NW_DETACH: case MM_3GPP_CGEV_ME_DETACH: case MM_3GPP_CGEV_NW_CLASS: case MM_3GPP_CGEV_ME_CLASS: case MM_3GPP_CGEV_NW_ACT_PRIMARY: case MM_3GPP_CGEV_ME_ACT_PRIMARY: case MM_3GPP_CGEV_NW_ACT_SECONDARY: case MM_3GPP_CGEV_ME_ACT_SECONDARY: case MM_3GPP_CGEV_NW_DEACT_PRIMARY: case MM_3GPP_CGEV_ME_DEACT_PRIMARY: case MM_3GPP_CGEV_NW_DEACT_SECONDARY: case MM_3GPP_CGEV_ME_DEACT_SECONDARY: case MM_3GPP_CGEV_NW_MODIFY: case MM_3GPP_CGEV_ME_MODIFY: default: g_assert_not_reached (); break; } g_free (pdp_addr); g_free (pdp_type); } static void cgev_received (MMPortSerialAt *port, GMatchInfo *info, MMBroadbandModem *self) { gchar *str; MM3gppCgev type; str = mm_get_string_unquoted_from_match_info (info, 1); if (!str) return; type = mm_3gpp_parse_cgev_indication_action (str); switch (type) { case MM_3GPP_CGEV_NW_DETACH: case MM_3GPP_CGEV_ME_DETACH: cgev_process_detach (self, type); break; case MM_3GPP_CGEV_NW_ACT_PRIMARY: case MM_3GPP_CGEV_ME_ACT_PRIMARY: case MM_3GPP_CGEV_NW_DEACT_PRIMARY: case MM_3GPP_CGEV_ME_DEACT_PRIMARY: cgev_process_primary (self, type, str); break; case MM_3GPP_CGEV_NW_ACT_SECONDARY: case MM_3GPP_CGEV_ME_ACT_SECONDARY: case MM_3GPP_CGEV_NW_DEACT_SECONDARY: case MM_3GPP_CGEV_ME_DEACT_SECONDARY: cgev_process_secondary (self, type, str); break; case MM_3GPP_CGEV_NW_DEACT_PDP: case MM_3GPP_CGEV_ME_DEACT_PDP: case MM_3GPP_CGEV_REJECT: case MM_3GPP_CGEV_NW_REACT: cgev_process_pdp (self, type, str); break; case MM_3GPP_CGEV_NW_CLASS: case MM_3GPP_CGEV_ME_CLASS: case MM_3GPP_CGEV_NW_MODIFY: case MM_3GPP_CGEV_ME_MODIFY: /* ignore */ break; case MM_3GPP_CGEV_UNKNOWN: default: mm_obj_dbg (self, "unhandled +CGEV indication: %s", str); break; } g_free (str); } static void set_cgev_unsolicited_events_handlers (MMBroadbandModem *self, gboolean enable) { MMPortSerialAt *ports[2]; g_autoptr(GRegex) cgev_regex = NULL; guint i; cgev_regex = mm_3gpp_cgev_regex_get (); ports[0] = mm_base_modem_peek_port_primary (MM_BASE_MODEM (self)); ports[1] = mm_base_modem_peek_port_secondary (MM_BASE_MODEM (self)); /* Enable unsolicited events in given port */ for (i = 0; i < 2; i++) { if (!ports[i]) continue; /* Set/unset unsolicited CGEV event handler */ mm_obj_dbg (self, "%s 3GPP +CGEV unsolicited events handlers in %s", enable ? "setting" : "removing", mm_port_get_device (MM_PORT (ports[i]))); mm_port_serial_at_add_unsolicited_msg_handler ( ports[i], cgev_regex, enable ? (MMPortSerialAtUnsolicitedMsgFn) cgev_received : NULL, enable ? self : NULL, NULL); } } static void ciev_signal_received (MMBroadbandModem *self, GMatchInfo *match_info) { guint quality; if (!mm_get_uint_from_match_info (match_info, 2, &quality)) { mm_obj_dbg (self, "couldn't parse signal quality value from +CIEV"); return; } mm_iface_modem_update_signal_quality ( MM_IFACE_MODEM (self), normalize_ciev_cind_signal_quality (quality, self->priv->modem_cind_min_signal_quality, self->priv->modem_cind_max_signal_quality)); } static void ciev_received (MMPortSerialAt *port, GMatchInfo *match_info, MMBroadbandModem *self) { guint ind; gchar *item; item = mm_get_string_unquoted_from_match_info (match_info, 1); if (!item) return; /* numeric index? */ if (mm_get_uint_from_str (item, &ind)) { if (ind == self->priv->modem_cind_indicator_signal_quality) ciev_signal_received (self, match_info); } /* string index? */ else { if (g_str_equal (item, "signal")) ciev_signal_received (self, match_info); } g_free (item); } static void set_ciev_unsolicited_events_handlers (MMBroadbandModem *self, gboolean enable) { MMPortSerialAt *ports[2]; g_autoptr(GRegex) ciev_regex = NULL; guint i; ciev_regex = mm_3gpp_ciev_regex_get (); ports[0] = mm_base_modem_peek_port_primary (MM_BASE_MODEM (self)); ports[1] = mm_base_modem_peek_port_secondary (MM_BASE_MODEM (self)); /* Enable unsolicited events in given port */ for (i = 0; i < 2; i++) { if (!ports[i]) continue; /* Set/unset unsolicited CIEV event handler */ mm_obj_dbg (self, "%s 3GPP +CIEV unsolicited events handlers in %s", enable ? "setting" : "removing", mm_port_get_device (MM_PORT (ports[i]))); mm_port_serial_at_add_unsolicited_msg_handler ( ports[i], ciev_regex, enable ? (MMPortSerialAtUnsolicitedMsgFn) ciev_received : NULL, enable ? self : NULL, NULL); } } static void support_checked_setup_unsolicited_events (GTask *task) { MMBroadbandModem *self; self = g_task_get_source_object (task); if (self->priv->modem_cind_supported) set_ciev_unsolicited_events_handlers (self, TRUE); if (self->priv->modem_cgerep_supported) set_cgev_unsolicited_events_handlers (self, TRUE); g_task_return_boolean (task, TRUE); g_object_unref (task); } static void check_and_setup_3gpp_urc_support (GTask *task); static void cgerep_format_check_ready (MMBroadbandModem *self, GAsyncResult *res, GTask *task) { GError *error = NULL; const gchar *result; result = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error); if (!result) { mm_obj_dbg (self, "+CGEREP check failed: %s", error->message); mm_obj_dbg (self, "packet domain event reporting is unsupported"); g_error_free (error); goto out; } mm_obj_dbg (self, "packet domain event reporting is supported"); self->priv->modem_cgerep_supported = TRUE; out: /* go on with remaining checks */ check_and_setup_3gpp_urc_support (task); } static void cmer_format_check_ready (MMBroadbandModem *self, GAsyncResult *res, GTask *task) { MM3gppCmerMode supported_modes = MM_3GPP_CMER_MODE_NONE; MM3gppCmerInd supported_inds = MM_3GPP_CMER_IND_NONE; GError *error = NULL; const gchar *result; gchar *aux; result = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error); if (error || !mm_3gpp_parse_cmer_test_response (result, self, &supported_modes, &supported_inds, &error)) { mm_obj_dbg (self, "+CMER check failed: %s", error->message); mm_obj_dbg (self, "generic indications are unsupported"); g_error_free (error); goto out; } aux = mm_3gpp_cmer_mode_build_string_from_mask (supported_modes); mm_obj_dbg (self, "supported +CMER modes: %s", aux); g_free (aux); aux = mm_3gpp_cmer_ind_build_string_from_mask (supported_inds); mm_obj_dbg (self, "supported +CMER indication settings: %s", aux); g_free (aux); /* Flag +CMER supported values */ if (supported_modes & MM_3GPP_CMER_MODE_FORWARD_URCS) self->priv->modem_cmer_enable_mode = MM_3GPP_CMER_MODE_FORWARD_URCS; else if (supported_modes & MM_3GPP_CMER_MODE_BUFFER_URCS_IF_LINK_RESERVED) self->priv->modem_cmer_enable_mode = MM_3GPP_CMER_MODE_BUFFER_URCS_IF_LINK_RESERVED; else if (supported_modes & MM_3GPP_CMER_MODE_DISCARD_URCS_IF_LINK_RESERVED) self->priv->modem_cmer_enable_mode = MM_3GPP_CMER_MODE_DISCARD_URCS_IF_LINK_RESERVED; aux = mm_3gpp_cmer_mode_build_string_from_mask (self->priv->modem_cmer_enable_mode); mm_obj_dbg (self, "+CMER enable mode: %s", aux); g_free (aux); if (supported_modes & MM_3GPP_CMER_MODE_DISCARD_URCS) self->priv->modem_cmer_disable_mode = MM_3GPP_CMER_MODE_DISCARD_URCS; aux = mm_3gpp_cmer_mode_build_string_from_mask (self->priv->modem_cmer_disable_mode); mm_obj_dbg (self, "+CMER disable mode: %s", aux); g_free (aux); if (supported_inds & MM_3GPP_CMER_IND_ENABLE_NOT_CAUSED_BY_CIND) self->priv->modem_cmer_ind = MM_3GPP_CMER_IND_ENABLE_NOT_CAUSED_BY_CIND; else if (supported_inds & MM_3GPP_CMER_IND_ENABLE_ALL) self->priv->modem_cmer_ind = MM_3GPP_CMER_IND_ENABLE_ALL; aux = mm_3gpp_cmer_ind_build_string_from_mask (self->priv->modem_cmer_ind); mm_obj_dbg (self, "+CMER indication setting: %s", aux); g_free (aux); out: /* go on with remaining checks */ check_and_setup_3gpp_urc_support (task); } static void cind_format_check_ready (MMBroadbandModem *self, GAsyncResult *res, GTask *task) { GHashTable *indicators = NULL; GError *error = NULL; const gchar *result; MM3gppCindResponse *r; result = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error); if (error || !(indicators = mm_3gpp_parse_cind_test_response (result, &error))) { /* unsupported indications */ mm_obj_dbg (self, "+CIND check failed: %s", error->message); mm_obj_dbg (self, "generic indications are unsupported"); g_error_free (error); /* go on with remaining checks */ check_and_setup_3gpp_urc_support (task); return; } /* Mark CIND as being supported and find the proper indexes for the * indicators. */ self->priv->modem_cind_supported = TRUE; /* Check if we support signal quality indications */ r = g_hash_table_lookup (indicators, "signal"); if (r) { self->priv->modem_cind_indicator_signal_quality = mm_3gpp_cind_response_get_index (r); self->priv->modem_cind_min_signal_quality = mm_3gpp_cind_response_get_min (r); self->priv->modem_cind_max_signal_quality = mm_3gpp_cind_response_get_max (r); mm_obj_dbg (self, "signal quality indications via CIND are supported at index '%u' (min: %u, max: %u)", self->priv->modem_cind_indicator_signal_quality, self->priv->modem_cind_min_signal_quality, self->priv->modem_cind_max_signal_quality); } else self->priv->modem_cind_indicator_signal_quality = CIND_INDICATOR_INVALID; /* Check if we support roaming indications */ r = g_hash_table_lookup (indicators, "roam"); if (r) { self->priv->modem_cind_indicator_roaming = mm_3gpp_cind_response_get_index (r); mm_obj_dbg (self, "roaming indications via CIND are supported at index '%u'", self->priv->modem_cind_indicator_roaming); } else self->priv->modem_cind_indicator_roaming = CIND_INDICATOR_INVALID; /* Check if we support service indications */ r = g_hash_table_lookup (indicators, "service"); if (r) { self->priv->modem_cind_indicator_service = mm_3gpp_cind_response_get_index (r); mm_obj_dbg (self, "service indications via CIND are supported at index '%u'", self->priv->modem_cind_indicator_service); } else self->priv->modem_cind_indicator_service = CIND_INDICATOR_INVALID; g_hash_table_destroy (indicators); /* Check +CMER required format */ mm_base_modem_at_command (MM_BASE_MODEM (self), "+CMER=?", 3, TRUE, (GAsyncReadyCallback)cmer_format_check_ready, task); } static void check_and_setup_3gpp_urc_support (GTask *task) { MMBroadbandModem *self; self = g_task_get_source_object (task); /* Check support for +CIEV indications, managed with +CIND/+CMER */ if (!self->priv->modem_cind_disabled && !self->priv->modem_cind_support_checked) { mm_obj_dbg (self, "checking indicator support..."); self->priv->modem_cind_support_checked = TRUE; mm_base_modem_at_command (MM_BASE_MODEM (self), "+CIND=?", 3, TRUE, (GAsyncReadyCallback)cind_format_check_ready, task); return; } /* Check support for +CGEV indications, managed with +CGEREP */ if (!self->priv->modem_cgerep_support_checked) { mm_obj_dbg (self, "checking packet domain event reporting..."); self->priv->modem_cgerep_support_checked = TRUE; mm_base_modem_at_command (MM_BASE_MODEM (self), "+CGEREP=?", 3, TRUE, (GAsyncReadyCallback)cgerep_format_check_ready, task); return; } support_checked_setup_unsolicited_events (task); } static void modem_3gpp_setup_unsolicited_events (MMIfaceModem3gpp *self, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; task = g_task_new (self, NULL, callback, user_data); check_and_setup_3gpp_urc_support (task); } static void modem_3gpp_cleanup_unsolicited_events (MMIfaceModem3gpp *_self, GAsyncReadyCallback callback, gpointer user_data) { MMBroadbandModem *self = MM_BROADBAND_MODEM (_self); GTask *task; task = g_task_new (self, NULL, callback, user_data); if (!self->priv->modem_cind_disabled && self->priv->modem_cind_support_checked && self->priv->modem_cind_supported) set_ciev_unsolicited_events_handlers (self, FALSE); if (self->priv->modem_cgerep_supported) set_cgev_unsolicited_events_handlers (self, FALSE); g_task_return_boolean (task, TRUE); g_object_unref (task); } /*****************************************************************************/ /* Enabling/disabling unsolicited events (3GPP interface) */ typedef struct { gboolean enable; MMPortSerialAt *primary; MMPortSerialAt *secondary; gchar *cmer_command; gboolean cmer_primary_done; gboolean cmer_secondary_done; gchar *cgerep_command; gboolean cgerep_primary_done; gboolean cgerep_secondary_done; } UnsolicitedEventsContext; static void unsolicited_events_context_free (UnsolicitedEventsContext *ctx) { if (ctx->secondary) g_object_unref (ctx->secondary); if (ctx->primary) g_object_unref (ctx->primary); g_free (ctx->cgerep_command); g_free (ctx->cmer_command); g_free (ctx); } static gboolean modem_3gpp_enable_disable_unsolicited_events_finish (MMIfaceModem3gpp *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void run_unsolicited_events_setup (GTask *task); static void unsolicited_events_setup_ready (MMBroadbandModem *self, GAsyncResult *res, GTask *task) { UnsolicitedEventsContext *ctx; GError *error = NULL; ctx = g_task_get_task_data (task); if (!mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error)) { mm_obj_dbg (self, "couldn't %s event reporting: '%s'", ctx->enable ? "enable" : "disable", error->message); g_error_free (error); } /* Continue on next port/command */ run_unsolicited_events_setup (task); } static void run_unsolicited_events_setup (GTask *task) { MMBroadbandModem *self; UnsolicitedEventsContext *ctx; MMPortSerialAt *port = NULL; const gchar *command = NULL; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); /* CMER on primary port */ if (!ctx->cmer_primary_done && ctx->cmer_command && ctx->primary && !self->priv->modem_cind_disabled) { mm_obj_dbg (self, "%s +CIND event reporting in primary port...", ctx->enable ? "enabling" : "disabling"); ctx->cmer_primary_done = TRUE; command = ctx->cmer_command; port = ctx->primary; } /* CMER on secondary port */ else if (!ctx->cmer_secondary_done && ctx->cmer_command && ctx->secondary && !self->priv->modem_cind_disabled) { mm_obj_dbg (self, "%s +CIND event reporting in secondary port...", ctx->enable ? "enabling" : "disabling"); ctx->cmer_secondary_done = TRUE; command = ctx->cmer_command; port = ctx->secondary; } /* CGEREP on primary port */ else if (!ctx->cgerep_primary_done && ctx->cgerep_command && ctx->primary) { mm_obj_dbg (self, "%s +CGEV event reporting in primary port...", ctx->enable ? "enabling" : "disabling"); ctx->cgerep_primary_done = TRUE; command = ctx->cgerep_command; port = ctx->primary; } /* CGEREP on secondary port */ else if (!ctx->cgerep_secondary_done && ctx->cgerep_command && ctx->secondary) { mm_obj_dbg (self, "%s +CGEV event reporting in secondary port...", ctx->enable ? "enabling" : "disabling"); ctx->cgerep_secondary_done = TRUE; port = ctx->secondary; command = ctx->cgerep_command; } /* Enable unsolicited events in given port */ if (port && command) { mm_base_modem_at_command_full (MM_BASE_MODEM (self), port, command, 3, FALSE, FALSE, /* raw */ NULL, /* cancellable */ (GAsyncReadyCallback)unsolicited_events_setup_ready, task); return; } /* Fully done now */ g_task_return_boolean (task, TRUE); g_object_unref (task); } static void modem_3gpp_enable_unsolicited_events (MMIfaceModem3gpp *_self, GAsyncReadyCallback callback, gpointer user_data) { MMBroadbandModem *self = MM_BROADBAND_MODEM (_self); GTask *task; UnsolicitedEventsContext *ctx; task = g_task_new (self, NULL, callback, user_data); ctx = g_new0 (UnsolicitedEventsContext, 1); ctx->enable = TRUE; ctx->primary = mm_base_modem_get_port_primary (MM_BASE_MODEM (self)); ctx->secondary = mm_base_modem_get_port_secondary (MM_BASE_MODEM (self)); g_task_set_task_data (task, ctx, (GDestroyNotify)unsolicited_events_context_free); if (self->priv->modem_cind_support_checked && self->priv->modem_cind_supported) ctx->cmer_command = mm_3gpp_build_cmer_set_request (self->priv->modem_cmer_enable_mode, self->priv->modem_cmer_ind); if (self->priv->modem_cgerep_support_checked && self->priv->modem_cgerep_supported) ctx->cgerep_command = g_strdup ("+CGEREP=2"); run_unsolicited_events_setup (task); } static void modem_3gpp_disable_unsolicited_events (MMIfaceModem3gpp *_self, GAsyncReadyCallback callback, gpointer user_data) { MMBroadbandModem *self = MM_BROADBAND_MODEM (_self); GTask *task; UnsolicitedEventsContext *ctx; task = g_task_new (self, NULL, callback, user_data); ctx = g_new0 (UnsolicitedEventsContext, 1); ctx->primary = mm_base_modem_get_port_primary (MM_BASE_MODEM (self)); ctx->secondary = mm_base_modem_get_port_secondary (MM_BASE_MODEM (self)); g_task_set_task_data (task, ctx, (GDestroyNotify)unsolicited_events_context_free); if (self->priv->modem_cind_support_checked && self->priv->modem_cind_supported) ctx->cmer_command = mm_3gpp_build_cmer_set_request (self->priv->modem_cmer_disable_mode, MM_3GPP_CMER_IND_NONE); if (self->priv->modem_cgerep_support_checked && self->priv->modem_cgerep_supported) ctx->cgerep_command = g_strdup ("+CGEREP=0"); run_unsolicited_events_setup (task); } /*****************************************************************************/ /* Setting modem charset (Modem interface) */ typedef struct { MMModemCharset charset; /* Commands to try in the sequence: * First one with quotes * Second without. * + last NUL */ MMBaseModemAtCommandAlloc charset_commands[3]; } SetupCharsetContext; static void setup_charset_context_free (SetupCharsetContext *ctx) { mm_base_modem_at_command_alloc_clear (&ctx->charset_commands[0]); mm_base_modem_at_command_alloc_clear (&ctx->charset_commands[1]); g_free (ctx); } static gboolean modem_setup_charset_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void current_charset_query_ready (MMBroadbandModem *self, GAsyncResult *res, GTask *task) { SetupCharsetContext *ctx; GError *error = NULL; const gchar *response; ctx = g_task_get_task_data (task); response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error); if (!response) g_task_return_error (task, error); else { MMModemCharset current; const gchar *p; p = response; if (g_str_has_prefix (p, "+CSCS:")) p += 6; while (*p == ' ') p++; current = mm_modem_charset_from_string (p); if (ctx->charset != current) g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Modem failed to change character set to %s", mm_modem_charset_to_string (ctx->charset)); else { /* We'll keep track ourselves of the current charset. * TODO: Make this a property so that plugins can also store it. */ self->priv->modem_current_charset = current; g_task_return_boolean (task, TRUE); } } g_object_unref (task); } static void charset_change_ready (MMBroadbandModem *self, GAsyncResult *res, GTask *task) { GError *error = NULL; mm_base_modem_at_sequence_finish (MM_BASE_MODEM (self), res, NULL, &error); if (error) { g_task_return_error (task, error); g_object_unref (task); return; } /* Check whether we did properly set the charset */ mm_base_modem_at_command (MM_BASE_MODEM (self), "+CSCS?", 3, FALSE, (GAsyncReadyCallback)current_charset_query_ready, task); } static void modem_setup_charset (MMIfaceModem *self, MMModemCharset charset, GAsyncReadyCallback callback, gpointer user_data) { SetupCharsetContext *ctx; const gchar *charset_str; GTask *task; /* NOTE: we already notified that CDMA-only modems couldn't load supported * charsets, so we'll never get here in such a case */ g_assert (mm_iface_modem_is_cdma_only (self) == FALSE); /* Build charset string to use */ charset_str = mm_modem_charset_to_string (charset); if (!charset_str) { g_task_report_new_error (self, callback, user_data, modem_setup_charset, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Unhandled character set 0x%X", charset); return; } /* Setup context, including commands to try */ ctx = g_new0 (SetupCharsetContext, 1); ctx->charset = charset; /* First try, with quotes */ ctx->charset_commands[0].command = g_strdup_printf ("+CSCS=\"%s\"", charset_str); ctx->charset_commands[0].timeout = 3; ctx->charset_commands[0].allow_cached = FALSE; ctx->charset_commands[0].response_processor = mm_base_modem_response_processor_no_result; /* Second try. * Some modems puke if you include the quotes around the character * set name, so lets try it again without them. */ ctx->charset_commands[1].command = g_strdup_printf ("+CSCS=%s", charset_str); ctx->charset_commands[1].timeout = 3; ctx->charset_commands[1].allow_cached = FALSE; ctx->charset_commands[1].response_processor = mm_base_modem_response_processor_no_result; task = g_task_new (self, NULL, callback, user_data); g_task_set_task_data (task, ctx, (GDestroyNotify)setup_charset_context_free); /* Launch sequence */ mm_base_modem_at_sequence ( MM_BASE_MODEM (self), (const MMBaseModemAtCommand *)ctx->charset_commands, NULL, /* response_processor_context */ NULL, /* response_processor_context_free */ (GAsyncReadyCallback)charset_change_ready, task); } /*****************************************************************************/ /* Loading supported charsets (Modem interface) */ static MMModemCharset modem_load_supported_charsets_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { GError *inner_error = NULL; gssize value; value = g_task_propagate_int (G_TASK (res), &inner_error); if (inner_error) { g_propagate_error (error, inner_error); return MM_MODEM_CHARSET_UNKNOWN; } return (MMModemCharset)value; } static void cscs_format_check_ready (MMBaseModem *self, GAsyncResult *res, GTask *task) { MMModemCharset charsets = MM_MODEM_CHARSET_UNKNOWN; const gchar *response; GError *error = NULL; response = mm_base_modem_at_command_finish (self, res, &error); if (error) g_task_return_error (task, error); else if (!mm_3gpp_parse_cscs_test_response (response, &charsets)) g_task_return_new_error ( task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Failed to parse the supported character sets response"); else g_task_return_int (task, charsets); g_object_unref (task); } static void modem_load_supported_charsets (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; task = g_task_new (self, NULL, callback, user_data); /* CDMA-only modems don't need this */ if (mm_iface_modem_is_cdma_only (self)) { mm_obj_dbg (self, "skipping supported charset loading in CDMA-only modem..."); g_task_return_int (task, MM_MODEM_CHARSET_UNKNOWN); g_object_unref (task); return; } mm_base_modem_at_command (MM_BASE_MODEM (self), "+CSCS=?", 3, TRUE, (GAsyncReadyCallback)cscs_format_check_ready, task); } /*****************************************************************************/ /* configuring flow control (Modem interface) */ static gboolean modem_setup_flow_control_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void ifc_test_ready (MMBaseModem *_self, GAsyncResult *res, GTask *task) { MMBroadbandModem *self; GError *error = NULL; const gchar *response; const gchar *cmd; MMFlowControl flow_control_supported; MMFlowControl flow_control_selected = MM_FLOW_CONTROL_UNKNOWN; MMFlowControl flow_control_requested; gchar *flow_control_supported_str = NULL; gchar *flow_control_selected_str = NULL; MMPortSerialAt *port; self = MM_BROADBAND_MODEM (_self); /* Completely ignore errors in AT+IFC=? */ response = mm_base_modem_at_command_finish (_self, res, &error); if (!response) goto out; /* Parse response */ flow_control_supported = mm_parse_ifc_test_response (response, self, &error); if (flow_control_supported == MM_FLOW_CONTROL_UNKNOWN) goto out; flow_control_supported_str = mm_flow_control_build_string_from_mask (flow_control_supported); port = mm_base_modem_peek_best_at_port (_self, &error); if (!port) goto out; flow_control_requested = mm_port_serial_get_flow_control (MM_PORT_SERIAL (port)); if (flow_control_requested != MM_FLOW_CONTROL_UNKNOWN) { gchar *flow_control_requested_str; flow_control_requested_str = mm_flow_control_build_string_from_mask (flow_control_requested); /* If flow control settings requested via udev tag are not supported by * the modem, we trigger a fatal error */ if (!(flow_control_supported & flow_control_requested)) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "Explicitly requested flow control settings (%s) are not supported by the device (%s)", flow_control_requested_str, flow_control_supported_str); g_object_unref (task); g_free (flow_control_requested_str); g_free (flow_control_supported_str); return; } mm_obj_dbg (self, "flow control settings explicitly requested: %s", flow_control_requested_str); flow_control_selected = flow_control_requested; flow_control_selected_str = flow_control_requested_str; } else { /* If flow control is not set explicitly by udev tags, * we prefer the methods in this order: * RTS/CTS * XON/XOFF * None. */ if (flow_control_supported & MM_FLOW_CONTROL_RTS_CTS) flow_control_selected = MM_FLOW_CONTROL_RTS_CTS; else if (flow_control_supported & MM_FLOW_CONTROL_XON_XOFF) flow_control_selected = MM_FLOW_CONTROL_XON_XOFF; else if (flow_control_supported & MM_FLOW_CONTROL_NONE) flow_control_selected = MM_FLOW_CONTROL_NONE; else g_assert_not_reached (); flow_control_selected_str = mm_flow_control_build_string_from_mask (flow_control_selected); mm_obj_dbg (self, "flow control settings automatically selected: %s", flow_control_selected_str); } /* Select flow control for all connections */ self->priv->flow_control = flow_control_selected; g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_FLOW_CONTROL]); /* Set flow control settings and ignore result */ switch (flow_control_selected) { case MM_FLOW_CONTROL_RTS_CTS: cmd = "+IFC=2,2"; break; case MM_FLOW_CONTROL_XON_XOFF: cmd = "+IFC=1,1"; break; case MM_FLOW_CONTROL_NONE: cmd = "+IFC=0,0"; break; case MM_FLOW_CONTROL_UNKNOWN: default: g_assert_not_reached (); } mm_base_modem_at_command (_self, cmd, 3, FALSE, NULL, NULL); out: g_free (flow_control_supported_str); g_free (flow_control_selected_str); /* Ignore errors */ if (error) { mm_obj_dbg (self, "couldn't load supported flow control methods: %s", error->message); g_error_free (error); } g_task_return_boolean (task, TRUE); g_object_unref (task); } static void modem_setup_flow_control (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; task = g_task_new (self, NULL, callback, user_data); /* Query supported flow control methods */ mm_base_modem_at_command (MM_BASE_MODEM (self), "+IFC=?", 3, TRUE, (GAsyncReadyCallback)ifc_test_ready, task); } /*****************************************************************************/ /* Power state loading (Modem interface) */ static MMModemPowerState modem_load_power_state_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { GError *inner_error = NULL; gssize value; value = g_task_propagate_int (G_TASK (res), &inner_error); if (inner_error) { g_propagate_error (error, inner_error); return MM_MODEM_POWER_STATE_UNKNOWN; } return (MMModemPowerState)value; } static void cfun_query_ready (MMBaseModem *self, GAsyncResult *res, GTask *task) { const gchar *result; MMModemPowerState state; GError *error = NULL; result = mm_base_modem_at_command_finish (self, res, &error); if (!result || !mm_3gpp_parse_cfun_query_generic_response (result, &state, &error)) g_task_return_error (task, error); else g_task_return_int (task, state);; g_object_unref (task); } static void modem_load_power_state (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; task = g_task_new (self, NULL, callback, user_data); /* CDMA-only modems don't need this */ if (mm_iface_modem_is_cdma_only (self)) { mm_obj_dbg (self, "assuming full power state in CDMA-only modem..."); g_task_return_int (task, MM_MODEM_POWER_STATE_ON); g_object_unref (task); return; } mm_obj_dbg (self, "loading power state..."); mm_base_modem_at_command (MM_BASE_MODEM (self), "+CFUN?", 3, FALSE, (GAsyncReadyCallback)cfun_query_ready, task); } /*****************************************************************************/ /* Powering up the modem (Modem interface) */ static gboolean modem_power_up_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { /* By default, errors in the power up command are ignored. * Plugins wanting to treat power up errors should subclass the power up * handling. */ return TRUE; } static void modem_power_up (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; /* CDMA-only modems don't need this */ if (mm_iface_modem_is_cdma_only (self)) mm_obj_dbg (self, "skipping power-up in CDMA-only modem..."); else mm_base_modem_at_command (MM_BASE_MODEM (self), "+CFUN=1", 5, FALSE, NULL, NULL); task = g_task_new (self, NULL, callback, user_data); g_task_return_boolean (task, TRUE); g_object_unref (task); } /*****************************************************************************/ /* Reprobing the modem if the SIM changed across a power-off or power-down */ #define SIM_SWAP_CHECK_LOAD_RETRIES_MAX 3 typedef enum { SIM_SWAP_CHECK_STEP_FIRST, SIM_SWAP_CHECK_STEP_ICCID_CHANGED, SIM_SWAP_CHECK_STEP_IMSI_CHANGED, SIM_SWAP_CHECK_STEP_LAST, } SimSwapCheckStep; typedef struct { MMBaseSim *sim; guint retries; gchar *iccid; gboolean iccid_check_done; gchar *imsi; gboolean imsi_check_done; SimSwapCheckStep step; } SimSwapContext; static void sim_swap_context_free (SimSwapContext *ctx) { g_free (ctx->iccid); g_free (ctx->imsi); g_clear_object (&ctx->sim); g_slice_free (SimSwapContext, ctx); } static gboolean modem_check_for_sim_swap_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static gboolean load_sim_identifier (GTask *task); static gboolean load_sim_imsi (GTask *task); static void sim_swap_check_step (GTask *task); static void complete_sim_swap_check (GTask *task, const gchar *current) { MMBroadbandModem *self; SimSwapContext *ctx; const gchar *cached; const gchar *str; self = MM_BROADBAND_MODEM (g_task_get_source_object (task)); ctx = g_task_get_task_data (task); if (ctx->step == SIM_SWAP_CHECK_STEP_ICCID_CHANGED) { ctx->iccid_check_done = TRUE; cached = mm_gdbus_sim_get_sim_identifier (MM_GDBUS_SIM (ctx->sim)); str = "identifier"; } else if (ctx->step == SIM_SWAP_CHECK_STEP_IMSI_CHANGED) { ctx->imsi_check_done = TRUE; cached = mm_gdbus_sim_get_imsi (MM_GDBUS_SIM (ctx->sim)); str = "imsi"; } else g_assert_not_reached(); if (g_strcmp0 (current, cached) != 0) { mm_obj_msg (self, "SIM %s has changed: '%s' -> '%s'", str, mm_log_str_personal_info (cached ? cached : ""), mm_log_str_personal_info (current ? current : "")); mm_iface_modem_process_sim_event (MM_IFACE_MODEM (self)); ctx->step = SIM_SWAP_CHECK_STEP_LAST; } else { mm_obj_info (self, "SIM %s has not changed: %s", str, mm_log_str_personal_info (current)); ctx->step++; } sim_swap_check_step (task); } static void load_sim_step_ready (MMBaseSim *sim, GAsyncResult *res, GTask *task) { MMBroadbandModem *self; SimSwapContext *ctx; g_autofree gchar *current = NULL; g_autoptr(GError) error = NULL; const gchar *str; self = MM_BROADBAND_MODEM (g_task_get_source_object (task)); ctx = g_task_get_task_data (task); if (ctx->step == SIM_SWAP_CHECK_STEP_ICCID_CHANGED) { current = mm_base_sim_load_sim_identifier_finish (sim, res, &error); str = "identifier"; } else if (ctx->step == SIM_SWAP_CHECK_STEP_IMSI_CHANGED) { current = MM_BASE_SIM_GET_CLASS (sim)->load_imsi_finish (sim, res, &error); str = "imsi"; } else g_assert_not_reached(); if (error) { if (g_error_matches (error, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED)) { /* Skip checking this field right away */ ctx->step++; sim_swap_check_step (task); return; } if (ctx->retries > 0) { mm_obj_warn (self, "could not load SIM %s: %s (%d retries left)", str, error->message, ctx->retries); --ctx->retries; if (ctx->step == SIM_SWAP_CHECK_STEP_ICCID_CHANGED) g_timeout_add_seconds (1, (GSourceFunc) load_sim_identifier, task); else if (ctx->step == SIM_SWAP_CHECK_STEP_IMSI_CHANGED) g_timeout_add_seconds (1, (GSourceFunc) load_sim_imsi, task); else g_assert_not_reached(); return; } mm_obj_warn (self, "could not load SIM %s: %s", str, error->message); complete_sim_swap_check (task, NULL); return; } complete_sim_swap_check (task, current); } static gboolean load_sim_identifier (GTask *task) { SimSwapContext *ctx; ctx = g_task_get_task_data (task); mm_base_sim_load_sim_identifier (ctx->sim, (GAsyncReadyCallback)load_sim_step_ready, task); return G_SOURCE_REMOVE; } static gboolean load_sim_imsi (GTask *task) { SimSwapContext *ctx; ctx = g_task_get_task_data (task); if (MM_BASE_SIM_GET_CLASS (ctx->sim)->load_imsi && MM_BASE_SIM_GET_CLASS (ctx->sim)->load_imsi_finish) { MM_BASE_SIM_GET_CLASS (ctx->sim)->load_imsi (ctx->sim, (GAsyncReadyCallback)load_sim_step_ready, task); } else { ctx->step++; sim_swap_check_step (task); } return G_SOURCE_REMOVE; } static void sim_swap_check_step (GTask *task) { SimSwapContext *ctx; ctx = g_task_get_task_data (task); switch (ctx->step) { case SIM_SWAP_CHECK_STEP_FIRST: ctx->step++; /* fall-through */ case SIM_SWAP_CHECK_STEP_ICCID_CHANGED: ctx->retries = SIM_SWAP_CHECK_LOAD_RETRIES_MAX; load_sim_identifier (task); return; case SIM_SWAP_CHECK_STEP_IMSI_CHANGED: ctx->retries = SIM_SWAP_CHECK_LOAD_RETRIES_MAX; load_sim_imsi (task); return; case SIM_SWAP_CHECK_STEP_LAST: if (!ctx->iccid_check_done && !ctx->imsi_check_done) g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't do either ICCID or IMSI check"); else g_task_return_boolean (task, TRUE); g_object_unref (task); return; default: g_assert_not_reached (); } } static void modem_check_for_sim_swap (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; SimSwapContext *ctx; mm_obj_info (self, "checking if SIM was swapped..."); task = g_task_new (self, NULL, callback, user_data); ctx = g_slice_new0 (SimSwapContext); ctx->step = SIM_SWAP_CHECK_STEP_FIRST; g_task_set_task_data (task, ctx, (GDestroyNotify)sim_swap_context_free); g_object_get (self, MM_IFACE_MODEM_SIM, &ctx->sim, NULL); if (!ctx->sim) { MMModemState modem_state; modem_state = MM_MODEM_STATE_UNKNOWN; g_object_get (self, MM_IFACE_MODEM_STATE, &modem_state, NULL); if (modem_state == MM_MODEM_STATE_FAILED) { mm_obj_msg (self, "new SIM detected, handle as SIM hot-swap"); mm_iface_modem_process_sim_event (MM_IFACE_MODEM (self)); g_task_return_boolean (task, TRUE); } else { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "could not acquire SIM object"); } g_object_unref (task); return; } sim_swap_check_step (task); } /*****************************************************************************/ /* Sending a command to the modem (Modem interface) */ static const gchar * modem_command_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { return mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, error); } static void modem_command (MMIfaceModem *self, const gchar *cmd, guint timeout, GAsyncReadyCallback callback, gpointer user_data) { mm_base_modem_at_command (MM_BASE_MODEM (self), cmd, timeout, FALSE, callback, user_data); } /*****************************************************************************/ /* IMEI loading (3GPP interface) */ static gchar * modem_3gpp_load_imei_finish (MMIfaceModem3gpp *self, GAsyncResult *res, GError **error) { const gchar *result; gchar *imei = NULL; result = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, error); if (!result) return NULL; result = mm_strip_tag (result, "+CGSN:"); mm_parse_gsn (result, &imei, NULL, NULL); mm_obj_dbg (self, "loaded IMEI: %s", imei); return imei; } static void modem_3gpp_load_imei (MMIfaceModem3gpp *self, GAsyncReadyCallback callback, gpointer user_data) { mm_obj_dbg (self, "loading IMEI..."); mm_base_modem_at_command (MM_BASE_MODEM (self), "+CGSN", 3, TRUE, callback, user_data); } /*****************************************************************************/ /* Facility locks status loading (3GPP interface) */ typedef struct { guint current; MMModem3gppFacility facilities; MMModem3gppFacility locks; } LoadEnabledFacilityLocksContext; static void get_next_facility_lock_status (GTask *task); static MMModem3gppFacility modem_3gpp_load_enabled_facility_locks_finish (MMIfaceModem3gpp *self, GAsyncResult *res, GError **error) { GError *inner_error = NULL; gssize value; value = g_task_propagate_int (G_TASK (res), &inner_error); if (inner_error) { g_propagate_error (error, inner_error); return MM_MODEM_3GPP_FACILITY_NONE; } return (MMModem3gppFacility)value; } static void clck_single_query_ready (MMBaseModem *self, GAsyncResult *res, GTask *task) { LoadEnabledFacilityLocksContext *ctx; const gchar *response; gboolean enabled = FALSE; ctx = g_task_get_task_data (task); response = mm_base_modem_at_command_finish (self, res, NULL); if (response && mm_3gpp_parse_clck_write_response (response, &enabled) && enabled) { ctx->locks |= (1 << ctx->current); } else { /* On errors, we'll just assume disabled */ ctx->locks &= ~(1 << ctx->current); } /* And go on with the next one */ ctx->current++; get_next_facility_lock_status (task); } static void get_next_facility_lock_status (GTask *task) { MMBroadbandModem *self; LoadEnabledFacilityLocksContext *ctx; guint i; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); for (i = ctx->current; i < sizeof (MMModem3gppFacility) * 8; i++) { guint32 facility = 1u << i; /* Found the next one to query! */ if (ctx->facilities & facility) { gchar *cmd; /* Keep the current one */ ctx->current = i; /* Query current */ cmd = g_strdup_printf ("+CLCK=\"%s\",2", mm_3gpp_facility_to_acronym (facility)); mm_base_modem_at_command (MM_BASE_MODEM (self), cmd, 3, FALSE, (GAsyncReadyCallback)clck_single_query_ready, task); g_free (cmd); return; } } /* No more facilities to query, all done */ g_task_return_int (task, ctx->locks); g_object_unref (task); } static void clck_test_ready (MMBaseModem *self, GAsyncResult *res, GTask *task) { LoadEnabledFacilityLocksContext *ctx; const gchar *response; GError *error = NULL; response = mm_base_modem_at_command_finish (self, res, &error); if (!response) { g_task_return_error (task, error); g_object_unref (task); return; } ctx = g_task_get_task_data (task); if (!mm_3gpp_parse_clck_test_response (response, &ctx->facilities)) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't parse list of available lock facilities: '%s'", response); g_object_unref (task); return; } /* Ignore facility locks specified by the plugins */ if (MM_BROADBAND_MODEM (self)->priv->modem_3gpp_ignored_facility_locks) { gchar *str; str = mm_modem_3gpp_facility_build_string_from_mask (MM_BROADBAND_MODEM (self)->priv->modem_3gpp_ignored_facility_locks); mm_obj_dbg (self, "ignoring facility locks: '%s'", str); g_free (str); ctx->facilities &= ~MM_BROADBAND_MODEM (self)->priv->modem_3gpp_ignored_facility_locks; } /* Go on... */ get_next_facility_lock_status (task); } static void modem_3gpp_load_enabled_facility_locks (MMIfaceModem3gpp *self, GAsyncReadyCallback callback, gpointer user_data) { LoadEnabledFacilityLocksContext *ctx; GTask *task; ctx = g_new (LoadEnabledFacilityLocksContext, 1); ctx->facilities = MM_MODEM_3GPP_FACILITY_NONE; ctx->locks = MM_MODEM_3GPP_FACILITY_NONE; ctx->current = 0; task = g_task_new (self, NULL, callback, user_data); g_task_set_task_data (task, ctx, g_free); mm_obj_dbg (self, "loading enabled facility locks..."); mm_base_modem_at_command (MM_BASE_MODEM (self), "+CLCK=?", 3, TRUE, (GAsyncReadyCallback)clck_test_ready, task); } /*****************************************************************************/ /* Operator Code loading (3GPP interface) */ static gchar * modem_3gpp_load_operator_code_finish (MMIfaceModem3gpp *self, GAsyncResult *res, GError **error) { const gchar *result; gchar *operator_code = NULL; result = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, error); if (!result) return NULL; if (!mm_3gpp_parse_cops_read_response (result, NULL, /* mode */ NULL, /* format */ &operator_code, NULL, /* act */ self, error)) return NULL; mm_3gpp_normalize_operator (&operator_code, MM_BROADBAND_MODEM (self)->priv->modem_current_charset, self); if (operator_code) mm_obj_dbg (self, "loaded Operator Code: %s", operator_code); return operator_code; } static void modem_3gpp_load_operator_code (MMIfaceModem3gpp *self, GAsyncReadyCallback callback, gpointer user_data) { mm_obj_dbg (self, "loading Operator Code..."); mm_base_modem_at_command (MM_BASE_MODEM (self), "+COPS=3,2", 3, FALSE, NULL, NULL); mm_base_modem_at_command (MM_BASE_MODEM (self), "+COPS?", 3, FALSE, callback, user_data); } /*****************************************************************************/ /* Operator Name loading (3GPP interface) */ static gchar * modem_3gpp_load_operator_name_finish (MMIfaceModem3gpp *self, GAsyncResult *res, GError **error) { const gchar *result; gchar *operator_name = NULL; result = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, error); if (!result) return NULL; if (!mm_3gpp_parse_cops_read_response (result, NULL, /* mode */ NULL, /* format */ &operator_name, NULL, /* act */ self, error)) return NULL; mm_3gpp_normalize_operator (&operator_name, MM_BROADBAND_MODEM (self)->priv->modem_current_charset, self); if (operator_name) mm_obj_dbg (self, "loaded Operator Name: %s", operator_name); return operator_name; } static void modem_3gpp_load_operator_name (MMIfaceModem3gpp *self, GAsyncReadyCallback callback, gpointer user_data) { mm_obj_dbg (self, "loading Operator Name..."); mm_base_modem_at_command (MM_BASE_MODEM (self), "+COPS=3,0", 3, FALSE, NULL, NULL); mm_base_modem_at_command (MM_BASE_MODEM (self), "+COPS?", 3, FALSE, callback, user_data); } /*****************************************************************************/ /* UE mode of operation for EPS loading (3GPP interface) */ static MMModem3gppEpsUeModeOperation modem_3gpp_load_eps_ue_mode_operation_finish (MMIfaceModem3gpp *self, GAsyncResult *res, GError **error) { MMModem3gppEpsUeModeOperation mode; const gchar *result; result = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, error); if (!result || !mm_3gpp_parse_cemode_query_response (result, &mode, error)) return MM_MODEM_3GPP_EPS_UE_MODE_OPERATION_UNKNOWN; return mode; } static void modem_3gpp_load_eps_ue_mode_operation (MMIfaceModem3gpp *self, GAsyncReadyCallback callback, gpointer user_data) { mm_obj_dbg (self, "loading UE mode of operation for EPS..."); mm_base_modem_at_command (MM_BASE_MODEM (self), "+CEMODE?", 3, FALSE, callback, user_data); } /*****************************************************************************/ /* UE mode of operation for EPS settin (3GPP interface) */ static gboolean modem_3gpp_set_eps_ue_mode_operation_finish (MMIfaceModem3gpp *self, GAsyncResult *res, GError **error) { return !!mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, error); } static void modem_3gpp_set_eps_ue_mode_operation (MMIfaceModem3gpp *self, MMModem3gppEpsUeModeOperation mode, GAsyncReadyCallback callback, gpointer user_data) { gchar *cmd; mm_obj_dbg (self, "updating UE mode of operation for EPS..."); cmd = mm_3gpp_build_cemode_set_request (mode); mm_base_modem_at_command (MM_BASE_MODEM (self), cmd, 3, FALSE, callback, user_data); g_free (cmd); } /*****************************************************************************/ /* Set packet service state (3GPP interface) */ static gboolean modem_3gpp_set_packet_service_state_finish (MMIfaceModem3gpp *self, GAsyncResult *res, GError **error) { return !!mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, error); } static void modem_3gpp_set_packet_service_state (MMIfaceModem3gpp *self, MMModem3gppPacketServiceState state, GAsyncReadyCallback callback, gpointer user_data) { g_autofree gchar *cmd = NULL; cmd = mm_3gpp_build_cgatt_set_request (state); mm_base_modem_at_command (MM_BASE_MODEM (self), cmd, 10, FALSE, callback, user_data); } /*****************************************************************************/ /* Unsolicited registration messages handling (3GPP interface) */ static gboolean modem_3gpp_setup_unsolicited_registration_events_finish (MMIfaceModem3gpp *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void registration_state_changed (MMPortSerialAt *port, GMatchInfo *match_info, MMBroadbandModem *self) { MMModem3gppRegistrationState state = MM_MODEM_3GPP_REGISTRATION_STATE_UNKNOWN; gulong lac = 0, tac = 0, cell_id = 0; MMModemAccessTechnology act = MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN; gboolean cgreg = FALSE; gboolean cereg = FALSE; gboolean c5greg = FALSE; GError *error = NULL; if (!mm_3gpp_parse_creg_response (match_info, self, &state, &lac, &cell_id, &act, &cgreg, &cereg, &c5greg, &error)) { mm_obj_warn (self, "error parsing unsolicited registration: %s", error && error->message ? error->message : "(unknown)"); g_clear_error (&error); return; } /* Report new registration state and fix LAC/TAC. * According to 3GPP TS 27.007: * - If CREG reports 7 (LTE) then the field contains TAC * - CEREG/C5GREG always reports TAC */ if (cgreg) mm_iface_modem_3gpp_update_ps_registration_state (MM_IFACE_MODEM_3GPP (self), state, FALSE); else if (cereg) { tac = lac; lac = 0; mm_iface_modem_3gpp_update_eps_registration_state (MM_IFACE_MODEM_3GPP (self), state, FALSE); } else if (c5greg) { tac = lac; lac = 0; mm_iface_modem_3gpp_update_5gs_registration_state (MM_IFACE_MODEM_3GPP (self), state, FALSE); } else { if (act == MM_MODEM_ACCESS_TECHNOLOGY_LTE) { tac = lac; lac = 0; } mm_iface_modem_3gpp_update_cs_registration_state (MM_IFACE_MODEM_3GPP (self), state, FALSE); } /* Only update access technologies from CREG/CGREG response if the modem * doesn't have custom commands for access technology loading, otherwise * we fight with the custom commands. Plus CREG/CGREG access technologies * don't have fine-grained distinction between HSxPA or GPRS/EDGE, etc. */ if (MM_IFACE_MODEM_GET_INTERFACE (self)->load_access_technologies == modem_load_access_technologies || MM_IFACE_MODEM_GET_INTERFACE (self)->load_access_technologies == NULL) mm_iface_modem_3gpp_update_access_technologies (MM_IFACE_MODEM_3GPP (self), act); mm_iface_modem_3gpp_update_location (MM_IFACE_MODEM_3GPP (self), lac, tac, cell_id); } static void modem_3gpp_setup_unsolicited_registration_events (MMIfaceModem3gpp *self, GAsyncReadyCallback callback, gpointer user_data) { MMPortSerialAt *ports[2]; GPtrArray *array; guint i; guint j; GTask *task; ports[0] = mm_base_modem_peek_port_primary (MM_BASE_MODEM (self)); ports[1] = mm_base_modem_peek_port_secondary (MM_BASE_MODEM (self)); /* Set up CREG unsolicited message handlers in both ports */ array = mm_3gpp_creg_regex_get (FALSE); for (i = 0; i < 2; i++) { if (!ports[i]) continue; mm_obj_dbg (self, "setting up 3GPP unsolicited registration messages handlers in %s", mm_port_get_device (MM_PORT (ports[i]))); for (j = 0; j < array->len; j++) { mm_port_serial_at_add_unsolicited_msg_handler ( MM_PORT_SERIAL_AT (ports[i]), (GRegex *) g_ptr_array_index (array, j), (MMPortSerialAtUnsolicitedMsgFn)registration_state_changed, self, NULL); } } mm_3gpp_creg_regex_destroy (array); task = g_task_new (self, NULL, callback, user_data); g_task_return_boolean (task, TRUE); g_object_unref (task); } /*****************************************************************************/ /* Unsolicited registration messages cleaning up (3GPP interface) */ static gboolean modem_3gpp_cleanup_unsolicited_registration_events_finish (MMIfaceModem3gpp *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void modem_3gpp_cleanup_unsolicited_registration_events (MMIfaceModem3gpp *self, GAsyncReadyCallback callback, gpointer user_data) { MMPortSerialAt *ports[2]; GPtrArray *array; guint i; guint j; GTask *task; ports[0] = mm_base_modem_peek_port_primary (MM_BASE_MODEM (self)); ports[1] = mm_base_modem_peek_port_secondary (MM_BASE_MODEM (self)); /* Set up CREG unsolicited message handlers in both ports */ array = mm_3gpp_creg_regex_get (FALSE); for (i = 0; i < 2; i++) { if (!ports[i]) continue; mm_obj_dbg (self, "cleaning up unsolicited registration messages handlers in %s", mm_port_get_device (MM_PORT (ports[i]))); for (j = 0; j < array->len; j++) { mm_port_serial_at_add_unsolicited_msg_handler ( MM_PORT_SERIAL_AT (ports[i]), (GRegex *) g_ptr_array_index (array, j), NULL, NULL, NULL); } } mm_3gpp_creg_regex_destroy (array); task = g_task_new (self, NULL, callback, user_data); g_task_return_boolean (task, TRUE); g_object_unref (task); } /*****************************************************************************/ /* Scan networks (3GPP interface) */ static GList * modem_3gpp_scan_networks_finish (MMIfaceModem3gpp *self, GAsyncResult *res, GError **error) { const gchar *result; result = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, error); if (!result) return NULL; return mm_3gpp_parse_cops_test_response (result, MM_BROADBAND_MODEM (self)->priv->modem_current_charset, self, error); } static void modem_3gpp_scan_networks (MMIfaceModem3gpp *self, GAsyncReadyCallback callback, gpointer user_data) { mm_base_modem_at_command (MM_BASE_MODEM (self), "+COPS=?", 300, FALSE, callback, user_data); } /*****************************************************************************/ /* Register in network (3GPP interface) */ static gboolean modem_3gpp_register_in_network_finish (MMIfaceModem3gpp *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void cops_set_ready (MMBaseModem *self, GAsyncResult *res, GTask *task) { GError *error = NULL; if (!mm_base_modem_at_command_full_finish (MM_BASE_MODEM (self), res, &error)) g_task_return_error (task, error); else g_task_return_boolean (task, TRUE); g_object_unref (task); } static void cops_ascii_set_ready (MMBaseModem *_self, GAsyncResult *res, GTask *task) { MMBroadbandModem *self = MM_BROADBAND_MODEM (_self); g_autoptr(GError) error = NULL; if (!mm_base_modem_at_command_full_finish (_self, res, &error)) { /* If it failed with an unsupported error, retry with current modem charset */ if (g_error_matches (error, MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_NOT_SUPPORTED)) { g_autoptr(GError) enc_error = NULL; g_autofree gchar *operator_id_enc = NULL; gchar *operator_id; /* try to encode to current charset */ operator_id = g_task_get_task_data (task); operator_id_enc = mm_modem_charset_str_from_utf8 (operator_id, self->priv->modem_current_charset, FALSE, &enc_error); if (!operator_id_enc) { mm_obj_dbg (self, "couldn't convert operator id to current charset: %s", enc_error->message); g_task_return_error (task, g_steal_pointer (&error)); g_object_unref (task); return; } /* retry only if encoded string is different to the non-encoded one */ if (g_strcmp0 (operator_id, operator_id_enc) != 0) { g_autofree gchar *command = NULL; command = g_strdup_printf ("+COPS=1,2,\"%s\"", operator_id_enc); mm_base_modem_at_command_full (_self, mm_base_modem_peek_best_at_port (_self, NULL), command, 120, FALSE, FALSE, /* raw */ g_task_get_cancellable (task), (GAsyncReadyCallback)cops_set_ready, task); return; } } g_task_return_error (task, g_steal_pointer (&error)); } else g_task_return_boolean (task, TRUE); g_object_unref (task); } static void modem_3gpp_register_in_network (MMIfaceModem3gpp *self, const gchar *operator_id, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; gchar *command; task = g_task_new (self, cancellable, callback, user_data); /* Trigger automatic network registration if no explicit operator id given */ if (!operator_id) { /* Note that '+COPS=0,,' (same but with commas) won't work in some Nokia * phones */ mm_base_modem_at_command_full (MM_BASE_MODEM (self), mm_base_modem_peek_best_at_port (MM_BASE_MODEM (self), NULL), "+COPS=0", 120, FALSE, FALSE, /* raw */ cancellable, (GAsyncReadyCallback)cops_set_ready, task); return; } /* Store operator id in context, in case we need to retry with the current * modem charset */ g_task_set_task_data (task, g_strdup (operator_id), g_free); /* Use the operator id given in ASCII initially */ command = g_strdup_printf ("+COPS=1,2,\"%s\"", operator_id); mm_base_modem_at_command_full (MM_BASE_MODEM (self), mm_base_modem_peek_best_at_port (MM_BASE_MODEM (self), NULL), command, 120, FALSE, FALSE, /* raw */ cancellable, (GAsyncReadyCallback)cops_ascii_set_ready, task); g_free (command); } /*****************************************************************************/ /* Registration checks (3GPP interface) */ typedef struct { gboolean is_cs_supported; gboolean is_ps_supported; gboolean is_eps_supported; gboolean is_5gs_supported; gboolean run_cs; gboolean run_ps; gboolean run_eps; gboolean run_5gs; gboolean running_cs; gboolean running_ps; gboolean running_eps; gboolean running_5gs; GError *error_cs; GError *error_ps; GError *error_eps; GError *error_5gs; } RunRegistrationChecksContext; static void run_registration_checks_context_free (RunRegistrationChecksContext *ctx) { g_clear_error (&ctx->error_cs); g_clear_error (&ctx->error_ps); g_clear_error (&ctx->error_eps); g_clear_error (&ctx->error_5gs); g_free (ctx); } static gboolean modem_3gpp_run_registration_checks_finish (MMIfaceModem3gpp *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void run_registration_checks_context_step (GTask *task); static void run_registration_checks_context_set_error (RunRegistrationChecksContext *ctx, GError *error) { g_assert (error != NULL); if (ctx->running_cs) ctx->error_cs = error; else if (ctx->running_ps) ctx->error_ps = error; else if (ctx->running_eps) ctx->error_eps = error; else if (ctx->running_5gs) ctx->error_5gs = error; else g_assert_not_reached (); } static void registration_status_check_ready (MMBroadbandModem *self, GAsyncResult *res, GTask *task) { g_autoptr(GMatchInfo) match_info = NULL; RunRegistrationChecksContext *ctx; const gchar *response; GError *error = NULL; guint i; gboolean parsed; gboolean cgreg = FALSE; gboolean cereg = FALSE; gboolean c5greg = FALSE; MMModem3gppRegistrationState state = MM_MODEM_3GPP_REGISTRATION_STATE_UNKNOWN; MMModemAccessTechnology act = MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN; gulong lac = 0; gulong tac = 0; gulong cid = 0; ctx = g_task_get_task_data (task); /* Only one must be running */ g_assert ((ctx->running_cs + ctx->running_ps + ctx->running_eps + ctx->running_5gs) == 1); response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error); if (!response) { run_registration_checks_context_set_error (ctx, error); run_registration_checks_context_step (task); return; } /* Unsolicited registration status handlers will usually process the * response for us, but just in case they don't, do that here. */ if (!response[0]) { /* Done */ run_registration_checks_context_step (task); return; } /* Try to match the response */ for (i = 0; i < self->priv->modem_3gpp_registration_regex->len; i++) { if (g_regex_match ((GRegex *)g_ptr_array_index (self->priv->modem_3gpp_registration_regex, i), response, 0, &match_info)) break; g_clear_pointer (&match_info, g_match_info_free); } if (!match_info) { error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Unknown registration status response: '%s'", response); run_registration_checks_context_set_error (ctx, error); run_registration_checks_context_step (task); return; } parsed = mm_3gpp_parse_creg_response (match_info, self, &state, &lac, &cid, &act, &cgreg, &cereg, &c5greg, &error); if (!parsed) { if (!error) error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Error parsing registration response: '%s'", response); run_registration_checks_context_set_error (ctx, error); run_registration_checks_context_step (task); return; } /* Report new registration state and fix LAC/TAC. * According to 3GPP TS 27.007: * - If CREG reports 7 (LTE) then the field contains TAC * - CEREG always reports TAC */ if (cgreg) { if (ctx->running_cs) mm_obj_dbg (self, "got PS registration state when checking CS registration state"); else if (ctx->running_eps) mm_obj_dbg (self, "got PS registration state when checking EPS registration state"); else if (ctx->running_5gs) mm_obj_dbg (self, "got PS registration state when checking 5GS registration state"); mm_iface_modem_3gpp_update_ps_registration_state (MM_IFACE_MODEM_3GPP (self), state, FALSE); } else if (cereg) { tac = lac; lac = 0; if (ctx->running_cs) mm_obj_dbg (self, "got EPS registration state when checking CS registration state"); else if (ctx->running_ps) mm_obj_dbg (self, "got EPS registration state when checking PS registration state"); else if (ctx->running_5gs) mm_obj_dbg (self, "got EPS registration state when checking 5GS registration state"); mm_iface_modem_3gpp_update_eps_registration_state (MM_IFACE_MODEM_3GPP (self), state, FALSE); } else if (c5greg) { tac = lac; lac = 0; if (ctx->running_cs) mm_obj_dbg (self, "got 5GS registration state when checking CS registration state"); else if (ctx->running_ps) mm_obj_dbg (self, "got 5GS registration state when checking PS registration state"); else if (ctx->running_eps) mm_obj_dbg (self, "got 5GS registration state when checking EPS registration state"); mm_iface_modem_3gpp_update_5gs_registration_state (MM_IFACE_MODEM_3GPP (self), state, FALSE); } else { if (act == MM_MODEM_ACCESS_TECHNOLOGY_LTE) { tac = lac; lac = 0; } if (ctx->running_ps) mm_obj_dbg (self, "got CS registration state when checking PS registration state"); else if (ctx->running_eps) mm_obj_dbg (self, "got CS registration state when checking EPS registration state"); else if (ctx->running_5gs) mm_obj_dbg (self, "got CS registration state when checking 5GS registration state"); mm_iface_modem_3gpp_update_cs_registration_state (MM_IFACE_MODEM_3GPP (self), state, FALSE); } mm_iface_modem_3gpp_update_access_technologies (MM_IFACE_MODEM_3GPP (self), act); mm_iface_modem_3gpp_update_location (MM_IFACE_MODEM_3GPP (self), lac, tac, cid); run_registration_checks_context_step (task); } static void run_registration_checks_context_step (GTask *task) { MMBroadbandModem *self; RunRegistrationChecksContext *ctx; GError *error = NULL; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); ctx->running_cs = FALSE; ctx->running_ps = FALSE; ctx->running_eps = FALSE; ctx->running_5gs = FALSE; if (ctx->run_cs) { ctx->running_cs = TRUE; ctx->run_cs = FALSE; /* Check current CS-registration state. */ mm_base_modem_at_command (MM_BASE_MODEM (self), "+CREG?", 10, FALSE, (GAsyncReadyCallback)registration_status_check_ready, task); return; } if (ctx->run_ps) { ctx->running_ps = TRUE; ctx->run_ps = FALSE; /* Check current PS-registration state. */ mm_base_modem_at_command (MM_BASE_MODEM (self), "+CGREG?", 10, FALSE, (GAsyncReadyCallback)registration_status_check_ready, task); return; } if (ctx->run_eps) { ctx->running_eps = TRUE; ctx->run_eps = FALSE; /* Check current EPS-registration state. */ mm_base_modem_at_command (MM_BASE_MODEM (self), "+CEREG?", 10, FALSE, (GAsyncReadyCallback)registration_status_check_ready, task); return; } if (ctx->run_5gs) { ctx->running_5gs = TRUE; ctx->run_5gs = FALSE; /* Check current 5GS-registration state. */ mm_base_modem_at_command (MM_BASE_MODEM (self), "+C5GREG?", 10, FALSE, (GAsyncReadyCallback)registration_status_check_ready, task); return; } /* If all run checks returned errors we fail */ if ((ctx->is_cs_supported || ctx->is_ps_supported || ctx->is_eps_supported || ctx->is_5gs_supported) && (!ctx->is_cs_supported || ctx->error_cs) && (!ctx->is_ps_supported || ctx->error_ps) && (!ctx->is_eps_supported || ctx->error_eps) && (!ctx->is_5gs_supported || ctx->error_5gs)) { /* When reporting errors, prefer the 5GS, then EPS, then PS, then CS */ if (ctx->error_5gs) error = g_steal_pointer (&ctx->error_5gs); else if (ctx->error_eps) error = g_steal_pointer (&ctx->error_eps); else if (ctx->error_ps) error = g_steal_pointer (&ctx->error_ps); else if (ctx->error_cs) error = g_steal_pointer (&ctx->error_cs); else g_assert_not_reached (); } if (error) g_task_return_error (task, error); else g_task_return_boolean (task, TRUE); g_object_unref (task); } static void modem_3gpp_run_registration_checks (MMIfaceModem3gpp *self, gboolean is_cs_supported, gboolean is_ps_supported, gboolean is_eps_supported, gboolean is_5gs_supported, GAsyncReadyCallback callback, gpointer user_data) { RunRegistrationChecksContext *ctx; GTask *task; ctx = g_new0 (RunRegistrationChecksContext, 1); ctx->is_cs_supported = is_cs_supported; ctx->is_ps_supported = is_ps_supported; ctx->is_eps_supported = is_eps_supported; ctx->is_5gs_supported = is_5gs_supported; ctx->run_cs = is_cs_supported; ctx->run_ps = is_ps_supported; ctx->run_eps = is_eps_supported; ctx->run_5gs = is_5gs_supported; task = g_task_new (self, NULL, callback, user_data); g_task_set_task_data (task, ctx, (GDestroyNotify)run_registration_checks_context_free); run_registration_checks_context_step (task); } /*****************************************************************************/ /* Create initial EPS bearer object */ static MMBaseBearer * modem_3gpp_create_initial_eps_bearer (MMIfaceModem3gpp *self, MMBearerProperties *config) { MMBaseBearer *bearer; /* NOTE: by default we create a bearer object that is CONNECTED but which doesn't * have an associated data interface already set. This is so that upper layers don't * attempt connection through this bearer object. */ bearer = g_object_new (MM_TYPE_BASE_BEARER, MM_BASE_BEARER_MODEM, MM_BASE_MODEM (self), MM_BASE_BEARER_CONFIG, config, "bearer-type", MM_BEARER_TYPE_DEFAULT_ATTACH, "connected", TRUE, NULL); mm_base_bearer_export (bearer); return bearer; } /*****************************************************************************/ /* Enable/Disable unsolicited registration events (3GPP interface) */ typedef struct { gboolean enable; /* TRUE for enabling, FALSE for disabling */ gboolean run_cs; gboolean run_ps; gboolean run_eps; gboolean running_cs; gboolean running_ps; gboolean running_eps; GError *cs_error; GError *ps_error; GError *eps_error; gboolean secondary_sequence; gboolean secondary_done; } UnsolicitedRegistrationEventsContext; static void unsolicited_registration_events_context_free (UnsolicitedRegistrationEventsContext *ctx) { if (ctx->cs_error) g_error_free (ctx->cs_error); if (ctx->ps_error) g_error_free (ctx->ps_error); if (ctx->eps_error) g_error_free (ctx->eps_error); g_free (ctx); } static GTask * unsolicited_registration_events_task_new (MMBroadbandModem *self, gboolean enable, gboolean cs_supported, gboolean ps_supported, gboolean eps_supported, GAsyncReadyCallback callback, gpointer user_data) { UnsolicitedRegistrationEventsContext *ctx; GTask *task; ctx = g_new0 (UnsolicitedRegistrationEventsContext, 1); ctx->enable = enable; ctx->run_cs = cs_supported; ctx->run_ps = ps_supported; ctx->run_eps = eps_supported; task = g_task_new (self, NULL, callback, user_data); g_task_set_task_data (task, ctx, (GDestroyNotify)unsolicited_registration_events_context_free); return task; } static gboolean modem_3gpp_enable_disable_unsolicited_registration_events_finish (MMIfaceModem3gpp *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static MMBaseModemAtResponseProcessorResult parse_registration_setup_reply (MMBaseModem *self, gpointer none, const gchar *command, const gchar *response, gboolean last_command, const GError *error, GVariant **result, GError **result_error) { *result_error = NULL; /* If error, try next command */ if (error) { *result = NULL; return MM_BASE_MODEM_AT_RESPONSE_PROCESSOR_RESULT_CONTINUE; } /* Set COMMAND as result! */ *result = g_variant_new_string (command); return MM_BASE_MODEM_AT_RESPONSE_PROCESSOR_RESULT_SUCCESS; } static const MMBaseModemAtCommand cs_registration_sequence[] = { /* Enable unsolicited registration notifications in CS network, with location */ { "+CREG=2", 3, FALSE, parse_registration_setup_reply }, /* Enable unsolicited registration notifications in CS network, without location */ { "+CREG=1", 3, FALSE, parse_registration_setup_reply }, { NULL } }; static const MMBaseModemAtCommand cs_unregistration_sequence[] = { /* Disable unsolicited registration notifications in CS network */ { "+CREG=0", 3, FALSE, parse_registration_setup_reply }, { NULL } }; static const MMBaseModemAtCommand ps_registration_sequence[] = { /* Enable unsolicited registration notifications in PS network, with location */ { "+CGREG=2", 3, FALSE, parse_registration_setup_reply }, /* Enable unsolicited registration notifications in PS network, without location */ { "+CGREG=1", 3, FALSE, parse_registration_setup_reply }, { NULL } }; static const MMBaseModemAtCommand ps_unregistration_sequence[] = { /* Disable unsolicited registration notifications in PS network */ { "+CGREG=0", 3, FALSE, parse_registration_setup_reply }, { NULL } }; static const MMBaseModemAtCommand eps_registration_sequence[] = { /* Enable unsolicited registration notifications in EPS network, with location */ { "+CEREG=2", 3, FALSE, parse_registration_setup_reply }, /* Enable unsolicited registration notifications in EPS network, without location */ { "+CEREG=1", 3, FALSE, parse_registration_setup_reply }, { NULL } }; static const MMBaseModemAtCommand eps_unregistration_sequence[] = { /* Disable unsolicited registration notifications in PS network */ { "+CEREG=0", 3, FALSE, parse_registration_setup_reply }, { NULL } }; static void unsolicited_registration_events_context_step (GTask *task); static void unsolicited_registration_events_sequence_ready (MMBroadbandModem *self, GAsyncResult *res, GTask *task) { UnsolicitedRegistrationEventsContext *ctx; GError *error = NULL; GVariant *command; MMPortSerialAt *secondary; ctx = g_task_get_task_data (task); /* Only one must be running */ g_assert ((ctx->running_cs ? 1 : 0) + (ctx->running_ps ? 1 : 0) + (ctx->running_eps ? 1 : 0) == 1); if (ctx->secondary_done) { if (ctx->secondary_sequence) mm_base_modem_at_sequence_finish (MM_BASE_MODEM (self), res, NULL, &error); else mm_base_modem_at_command_full_finish (MM_BASE_MODEM (self), res, &error); if (error) { mm_obj_dbg (self, "%s unsolicited registration events in secondary port failed: %s", ctx->enable ? "enabling" : "disabling", error->message); /* Keep errors reported */ if (ctx->running_cs && !ctx->cs_error) ctx->cs_error = error; else if (ctx->running_ps && !ctx->ps_error) ctx->ps_error = error; else if (ctx->running_eps && !ctx->eps_error) ctx->eps_error = error; else g_error_free (error); } else { /* If successful in secondary port, cleanup primary error if any */ if (ctx->running_cs && ctx->cs_error) { g_error_free (ctx->cs_error); ctx->cs_error = NULL; } else if (ctx->running_ps && ctx->ps_error) { g_error_free (ctx->ps_error); ctx->ps_error = NULL; } else if (ctx->running_eps && ctx->eps_error) { g_error_free (ctx->eps_error); ctx->eps_error = NULL; } } /* Done with primary and secondary, keep on */ unsolicited_registration_events_context_step (task); return; } /* We just run the sequence in the primary port */ command = mm_base_modem_at_sequence_finish (MM_BASE_MODEM (self), res, NULL, &error); if (!command) { if (!error) error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "AT sequence failed"); mm_obj_dbg (self, "%s unsolicited registration events in primary port failed: %s", ctx->enable ? "enabling" : "disabling", error->message); /* Keep errors reported */ if (ctx->running_cs) ctx->cs_error = error; else if (ctx->running_ps) ctx->ps_error = error; else ctx->eps_error = error; /* Even if primary failed, go on and try to enable in secondary port */ } secondary = mm_base_modem_peek_port_secondary (MM_BASE_MODEM (self)); if (secondary) { const MMBaseModemAtCommand *registration_sequence = NULL; ctx->secondary_done = TRUE; /* Now use the same registration setup in secondary port, if any */ if (command) { mm_base_modem_at_command_full ( MM_BASE_MODEM (self), secondary, g_variant_get_string (command, NULL), 3, FALSE, FALSE, /* raw */ NULL, /* cancellable */ (GAsyncReadyCallback)unsolicited_registration_events_sequence_ready, task); return; } /* If primary failed, run the whole sequence in secondary */ ctx->secondary_sequence = TRUE; if (ctx->running_cs) registration_sequence = ctx->enable ? cs_registration_sequence : cs_unregistration_sequence; else if (ctx->running_ps) registration_sequence = ctx->enable ? ps_registration_sequence : ps_unregistration_sequence; else registration_sequence = ctx->enable ? eps_registration_sequence : eps_unregistration_sequence; mm_base_modem_at_sequence_full ( MM_BASE_MODEM (self), secondary, registration_sequence, NULL, /* response processor context */ NULL, /* response processor context free */ NULL, /* cancellable */ (GAsyncReadyCallback)unsolicited_registration_events_sequence_ready, task); return; } /* We're done */ unsolicited_registration_events_context_step (task); } static void unsolicited_registration_events_context_step (GTask *task) { MMBroadbandModem *self; UnsolicitedRegistrationEventsContext *ctx; GError *error = NULL; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); ctx->running_cs = FALSE; ctx->running_ps = FALSE; ctx->running_eps = FALSE; ctx->secondary_done = FALSE; if (ctx->run_cs) { ctx->running_cs = TRUE; ctx->run_cs = FALSE; mm_base_modem_at_sequence_full ( MM_BASE_MODEM (self), mm_base_modem_peek_port_primary (MM_BASE_MODEM (self)), ctx->enable ? cs_registration_sequence : cs_unregistration_sequence, NULL, /* response processor context */ NULL, /* response processor context free */ NULL, /* cancellable */ (GAsyncReadyCallback)unsolicited_registration_events_sequence_ready, task); return; } if (ctx->run_ps) { ctx->running_ps = TRUE; ctx->run_ps = FALSE; mm_base_modem_at_sequence_full ( MM_BASE_MODEM (self), mm_base_modem_peek_port_primary (MM_BASE_MODEM (self)), ctx->enable ? ps_registration_sequence : ps_unregistration_sequence, NULL, /* response processor context */ NULL, /* response processor context free */ NULL, /* cancellable */ (GAsyncReadyCallback)unsolicited_registration_events_sequence_ready, task); return; } if (ctx->run_eps) { ctx->running_eps = TRUE; ctx->run_eps = FALSE; mm_base_modem_at_sequence_full ( MM_BASE_MODEM (self), mm_base_modem_peek_port_primary (MM_BASE_MODEM (self)), ctx->enable ? eps_registration_sequence : eps_unregistration_sequence, NULL, /* response processor context */ NULL, /* response processor context free */ NULL, /* cancellable */ (GAsyncReadyCallback)unsolicited_registration_events_sequence_ready, task); return; } /* All done! * If we have any error reported, we'll propagate it. EPS errors take * precedence over PS errors and PS errors take precedence over CS errors. */ if (ctx->eps_error) { g_propagate_error (&error, ctx->eps_error); ctx->eps_error = NULL; } else if (ctx->ps_error) { g_propagate_error (&error, ctx->ps_error); ctx->ps_error = NULL; } else if (ctx->cs_error) { g_propagate_error (&error, ctx->cs_error); ctx->cs_error = NULL; } if (error) g_task_return_error (task, error); else g_task_return_boolean (task, TRUE); g_object_unref (task); } static void modem_3gpp_disable_unsolicited_registration_events (MMIfaceModem3gpp *self, gboolean cs_supported, gboolean ps_supported, gboolean eps_supported, GAsyncReadyCallback callback, gpointer user_data) { unsolicited_registration_events_context_step ( unsolicited_registration_events_task_new (MM_BROADBAND_MODEM (self), FALSE, cs_supported, ps_supported, eps_supported, callback, user_data)); } static void modem_3gpp_enable_unsolicited_registration_events (MMIfaceModem3gpp *self, gboolean cs_supported, gboolean ps_supported, gboolean eps_supported, GAsyncReadyCallback callback, gpointer user_data) { unsolicited_registration_events_context_step ( unsolicited_registration_events_task_new (MM_BROADBAND_MODEM (self), TRUE, cs_supported, ps_supported, eps_supported, callback, user_data)); } /*****************************************************************************/ /* Cancel USSD (3GPP/USSD interface) */ static gboolean modem_3gpp_ussd_cancel_finish (MMIfaceModem3gppUssd *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void cancel_command_ready (MMBroadbandModem *self, GAsyncResult *res, GTask *task) { GError *error = NULL; mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error); /* Complete the pending action, regardless of the CUSD result */ if (self->priv->pending_ussd_action) { GTask *pending_task; pending_task = self->priv->pending_ussd_action; self->priv->pending_ussd_action = NULL; g_task_return_new_error (pending_task, MM_CORE_ERROR, MM_CORE_ERROR_ABORTED, "USSD session was cancelled"); g_object_unref (pending_task); } mm_iface_modem_3gpp_ussd_update_state (MM_IFACE_MODEM_3GPP_USSD (self), MM_MODEM_3GPP_USSD_SESSION_STATE_IDLE); if (error) g_task_return_error (task, error); else g_task_return_boolean (task, TRUE); g_object_unref (task); } static void modem_3gpp_ussd_cancel (MMIfaceModem3gppUssd *self, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; task = g_task_new (self, NULL, callback, user_data); mm_base_modem_at_command (MM_BASE_MODEM (self), "+CUSD=2", 10, TRUE, (GAsyncReadyCallback)cancel_command_ready, task); } /*****************************************************************************/ /* Send command (3GPP/USSD interface) */ typedef struct { gchar *command; gboolean current_is_unencoded; gboolean encoded_used; gboolean unencoded_used; } Modem3gppUssdSendContext; static void modem_3gpp_ussd_send_context_free (Modem3gppUssdSendContext *ctx) { g_free (ctx->command); g_slice_free (Modem3gppUssdSendContext, ctx); } static gchar * modem_3gpp_ussd_send_finish (MMIfaceModem3gppUssd *self, GAsyncResult *res, GError **error) { return g_task_propagate_pointer (G_TASK (res), error); } static void modem_3gpp_ussd_context_step (MMBroadbandModem *self); static void cusd_process_string (MMBroadbandModem *self, const gchar *str); static void ussd_send_command_ready (MMBaseModem *_self, GAsyncResult *res) { MMBroadbandModem *self; Modem3gppUssdSendContext *ctx; GError *error = NULL; const gchar *response; self = MM_BROADBAND_MODEM (_self); response = mm_base_modem_at_command_finish (_self, res, &error); if (error) { /* Some immediate error happened when sending the USSD request */ mm_obj_dbg (self, "error sending USSD request: '%s'", error->message); g_error_free (error); if (self->priv->pending_ussd_action) { modem_3gpp_ussd_context_step (self); return; } } if (!self->priv->pending_ussd_action) { mm_obj_dbg (self, "USSD operation finished already via URCs"); return; } /* Cache the hint for the next time we send something */ ctx = g_task_get_task_data (self->priv->pending_ussd_action); if (!self->priv->use_unencoded_ussd && ctx->current_is_unencoded) { mm_obj_dbg (self, "will assume we want unencoded USSD commands"); self->priv->use_unencoded_ussd = TRUE; } else if (self->priv->use_unencoded_ussd && !ctx->current_is_unencoded) { mm_obj_dbg (self, "will assume we want encoded USSD commands"); self->priv->use_unencoded_ussd = FALSE; } if (response && response[0]) { response = mm_strip_tag (response, "+CUSD:"); cusd_process_string (self, response); } } static void modem_3gpp_ussd_context_send_encoded (MMBroadbandModem *self) { Modem3gppUssdSendContext *ctx; gchar *at_command = NULL; GError *error = NULL; guint scheme = 0; gchar *encoded; g_assert (self->priv->pending_ussd_action); ctx = g_task_get_task_data (self->priv->pending_ussd_action); /* Encode USSD command */ encoded = mm_iface_modem_3gpp_ussd_encode (MM_IFACE_MODEM_3GPP_USSD (self), ctx->command, &scheme, &error); if (!encoded) { GTask *task; task = self->priv->pending_ussd_action; self->priv->pending_ussd_action = NULL; mm_iface_modem_3gpp_ussd_update_state (MM_IFACE_MODEM_3GPP_USSD (self), MM_MODEM_3GPP_USSD_SESSION_STATE_IDLE); g_task_return_error (task, error); g_object_unref (task); return; } /* Build AT command */ ctx->encoded_used = TRUE; ctx->current_is_unencoded = FALSE; at_command = g_strdup_printf ("+CUSD=1,\"%s\",%d", encoded, scheme); g_free (encoded); mm_base_modem_at_command (MM_BASE_MODEM (self), at_command, 10, FALSE, (GAsyncReadyCallback)ussd_send_command_ready, NULL); g_free (at_command); } static void modem_3gpp_ussd_context_send_unencoded (MMBroadbandModem *self) { Modem3gppUssdSendContext *ctx; gchar *at_command = NULL; g_assert (self->priv->pending_ussd_action); ctx = g_task_get_task_data (self->priv->pending_ussd_action); /* Build AT command with action unencoded */ ctx->unencoded_used = TRUE; ctx->current_is_unencoded = TRUE; at_command = g_strdup_printf ("+CUSD=1,\"%s\",%d", ctx->command, MM_MODEM_GSM_USSD_SCHEME_7BIT); mm_base_modem_at_command (MM_BASE_MODEM (self), at_command, 10, FALSE, (GAsyncReadyCallback)ussd_send_command_ready, NULL); g_free (at_command); } static void modem_3gpp_ussd_context_step (MMBroadbandModem *self) { Modem3gppUssdSendContext *ctx; g_assert (self->priv->pending_ussd_action); ctx = g_task_get_task_data (self->priv->pending_ussd_action); if (ctx->encoded_used && ctx->unencoded_used) { GTask *task; task = self->priv->pending_ussd_action; self->priv->pending_ussd_action = NULL; mm_iface_modem_3gpp_ussd_update_state (MM_IFACE_MODEM_3GPP_USSD (self), MM_MODEM_3GPP_USSD_SESSION_STATE_IDLE); g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Sending USSD command failed"); g_object_unref (task); return; } if (self->priv->use_unencoded_ussd) { if (!ctx->unencoded_used) modem_3gpp_ussd_context_send_unencoded (self); else if (!ctx->encoded_used) modem_3gpp_ussd_context_send_encoded (self); else g_assert_not_reached (); } else { if (!ctx->encoded_used) modem_3gpp_ussd_context_send_encoded (self); else if (!ctx->unencoded_used) modem_3gpp_ussd_context_send_unencoded (self); else g_assert_not_reached (); } } static void modem_3gpp_ussd_send (MMIfaceModem3gppUssd *_self, const gchar *command, GAsyncReadyCallback callback, gpointer user_data) { MMBroadbandModem *self; GTask *task; Modem3gppUssdSendContext *ctx; self = MM_BROADBAND_MODEM (_self); task = g_task_new (self, NULL, callback, user_data); ctx = g_slice_new0 (Modem3gppUssdSendContext); ctx->command = g_strdup (command); g_task_set_task_data (task, ctx, (GDestroyNotify) modem_3gpp_ussd_send_context_free); /* Fail if there is an ongoing operation already */ if (self->priv->pending_ussd_action) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_IN_PROGRESS, "there is already an ongoing USSD operation"); g_object_unref (task); return; } /* Cache the action, as it may be completed via URCs */ self->priv->pending_ussd_action = task; mm_iface_modem_3gpp_ussd_update_state (_self, MM_MODEM_3GPP_USSD_SESSION_STATE_ACTIVE); modem_3gpp_ussd_context_step (self); } /*****************************************************************************/ /* USSD Encode/Decode (3GPP/USSD interface) */ static gchar * modem_3gpp_ussd_encode (MMIfaceModem3gppUssd *_self, const gchar *command, guint *scheme, GError **error) { MMBroadbandModem *self = MM_BROADBAND_MODEM (_self); g_autoptr(GByteArray) ussd_command = NULL; /* Encode to the current charset (as per AT+CSCS, which is what most modems * (except for Huawei it seems) will ask for. */ ussd_command = mm_modem_charset_bytearray_from_utf8 (command, self->priv->modem_current_charset, FALSE, error); if (!ussd_command) { g_prefix_error (error, "Failed to encode USSD command: "); return NULL; } /* The scheme value does NOT represent the encoding used to encode the string * we're giving. This scheme reflects the encoding that the modem should use when * sending the data out to the network. We're hardcoding this to GSM-7 because * USSD commands fit well in GSM-7, unlike USSD responses that may contain code * points that may only be encoded in UCS-2. */ *scheme = MM_MODEM_GSM_USSD_SCHEME_7BIT; /* convert to hex representation */ return (gchar *) mm_utils_bin2hexstr (ussd_command->data, ussd_command->len); } static gchar * modem_3gpp_ussd_decode (MMIfaceModem3gppUssd *self, const gchar *reply, GError **error) { MMBroadbandModem *broadband = MM_BROADBAND_MODEM (self); guint8 *bin = NULL; gsize bin_len = 0; g_autoptr(GByteArray) barray = NULL; bin = (guint8 *) mm_utils_hexstr2bin (reply, -1, &bin_len, error); if (!bin) { g_prefix_error (error, "Couldn't convert HEX string to binary: "); return NULL; } barray = g_byte_array_new_take (bin, bin_len); /* Decode from current charset (as per AT+CSCS, which is what most modems * (except for Huawei it seems) will ask for. */ return mm_modem_charset_bytearray_to_utf8 (barray, broadband->priv->modem_current_charset, FALSE, error); } /*****************************************************************************/ /* Setup/Cleanup unsolicited result codes (3GPP/USSD interface) */ static gboolean modem_3gpp_ussd_setup_cleanup_unsolicited_events_finish (MMIfaceModem3gppUssd *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static gchar * decode_ussd_response (MMBroadbandModem *self, const gchar *reply, GError **error) { gchar *p; gchar *str; gchar *decoded; /* Look for the first ',' */ p = strchr (reply, ','); if (!p) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Cannot decode USSD response (%s): missing field separator", reply); return NULL; } /* Assume the string is the next field, and strip quotes. While doing this, * we also skip any other additional field we may have afterwards */ if (p[1] == '"') { str = g_strdup (&p[2]); p = strchr (str, '"'); if (p) *p = '\0'; } else { str = g_strdup (&p[1]); p = strchr (str, ','); if (p) *p = '\0'; } /* If reply doesn't seem to be hex; just return itself... */ if (!mm_utils_ishexstr (str)) decoded = g_strdup (str); else decoded = mm_iface_modem_3gpp_ussd_decode (MM_IFACE_MODEM_3GPP_USSD (self), str, error); g_free (str); return decoded; } static void cusd_process_string (MMBroadbandModem *self, const gchar *str) { MMModem3gppUssdSessionState ussd_state = MM_MODEM_3GPP_USSD_SESSION_STATE_IDLE; GError *error = NULL; gint status; GTask *task; gchar *converted = NULL; /* If there is a pending action, it is ALWAYS completed here */ task = self->priv->pending_ussd_action; self->priv->pending_ussd_action = NULL; if (!str || !isdigit (*str)) { error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Invalid USSD message received: '%s'", str ? str : "(none)"); goto out; } status = g_ascii_digit_value (*str); switch (status) { case 0: /* no further action required */ converted = decode_ussd_response (self, str, &error); if (!converted) break; /* Response to the user's request? */ if (task) break; /* Network-initiated USSD-Notify */ mm_iface_modem_3gpp_ussd_update_network_notification (MM_IFACE_MODEM_3GPP_USSD (self), converted); g_clear_pointer (&converted, g_free); break; case 1: /* further action required */ ussd_state = MM_MODEM_3GPP_USSD_SESSION_STATE_USER_RESPONSE; converted = decode_ussd_response (self, str, &error); if (!converted) break; /* Response to the user's request? */ if (task) break; /* Network-initiated USSD-Request */ mm_iface_modem_3gpp_ussd_update_network_request (MM_IFACE_MODEM_3GPP_USSD (self), converted); g_clear_pointer (&converted, g_free); break; case 2: /* Response to the user's request? */ if (task) error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_ABORTED, "USSD terminated by network"); break; case 4: /* Response to the user's request? */ if (task) error = g_error_new (MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_NOT_SUPPORTED, "Operation not supported"); break; default: /* Response to the user's request? */ if (task) error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Unhandled USSD reply: %s (%d)", str, status); break; } out: mm_iface_modem_3gpp_ussd_update_state (MM_IFACE_MODEM_3GPP_USSD (self), ussd_state); /* Complete the pending action */ if (task) { if (error) g_task_return_error (task, error); else if (converted) g_task_return_pointer (task, converted, g_free); else g_assert_not_reached (); return; } /* If no pending task, just report the error */ if (error) { mm_obj_warn (self, "invalid USSD message: %s", error->message); g_error_free (error); } g_assert (!converted); } static void cusd_received (MMPortSerialAt *port, GMatchInfo *info, MMBroadbandModem *self) { gchar *str; mm_obj_dbg (self, "unsolicited USSD URC received"); str = g_match_info_fetch (info, 1); cusd_process_string (self, str); g_free (str); } static void set_unsolicited_result_code_handlers (MMIfaceModem3gppUssd *self, gboolean enable, GAsyncReadyCallback callback, gpointer user_data) { MMPortSerialAt *ports[2]; g_autoptr(GRegex) cusd_regex = NULL; guint i; GTask *task; cusd_regex = mm_3gpp_cusd_regex_get (); ports[0] = mm_base_modem_peek_port_primary (MM_BASE_MODEM (self)); ports[1] = mm_base_modem_peek_port_secondary (MM_BASE_MODEM (self)); /* Enable unsolicited result codes in given port */ for (i = 0; i < 2; i++) { if (!ports[i]) continue; /* Set/unset unsolicited CUSD event handler */ mm_obj_dbg (self, "%s unsolicited result code handlers in %s", enable ? "setting" : "removing", mm_port_get_device (MM_PORT (ports[i]))); mm_port_serial_at_add_unsolicited_msg_handler ( ports[i], cusd_regex, enable ? (MMPortSerialAtUnsolicitedMsgFn) cusd_received : NULL, enable ? self : NULL, NULL); } task = g_task_new (self, NULL, callback, user_data); g_task_return_boolean (task, TRUE); g_object_unref (task); } static void modem_3gpp_ussd_setup_unsolicited_events (MMIfaceModem3gppUssd *self, GAsyncReadyCallback callback, gpointer user_data) { set_unsolicited_result_code_handlers (self, TRUE, callback, user_data); } static void modem_3gpp_ussd_cleanup_unsolicited_events (MMIfaceModem3gppUssd *self, GAsyncReadyCallback callback, gpointer user_data) { set_unsolicited_result_code_handlers (self, FALSE, callback, user_data); } /*****************************************************************************/ /* Enable/Disable URCs (3GPP/USSD interface) */ static gboolean modem_3gpp_ussd_enable_disable_unsolicited_events_finish (MMIfaceModem3gppUssd *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void urc_enable_disable_ready (MMBroadbandModem *self, GAsyncResult *res, GTask *task) { GError *error = NULL; mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error); if (error) { g_task_return_error (task, error); g_object_unref (task); return; } g_task_return_boolean (task, TRUE); g_object_unref (task); } static void modem_3gpp_ussd_disable_unsolicited_events (MMIfaceModem3gppUssd *self, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; task = g_task_new (self, NULL, callback, user_data); mm_base_modem_at_command (MM_BASE_MODEM (self), "+CUSD=0", 3, TRUE, (GAsyncReadyCallback)urc_enable_disable_ready, task); } static void modem_3gpp_ussd_enable_unsolicited_events (MMIfaceModem3gppUssd *self, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; task = g_task_new (self, NULL, callback, user_data); mm_base_modem_at_command (MM_BASE_MODEM (self), "+CUSD=1", 3, TRUE, (GAsyncReadyCallback)urc_enable_disable_ready, task); } /*****************************************************************************/ /* Check if USSD supported (3GPP/USSD interface) */ static gboolean modem_3gpp_ussd_check_support_finish (MMIfaceModem3gppUssd *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void cusd_format_check_ready (MMBroadbandModem *self, GAsyncResult *res, GTask *task) { GError *error = NULL; mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error); if (error) { g_task_return_error (task, error); g_object_unref (task); return; } g_task_return_boolean (task, TRUE); g_object_unref (task); } static void modem_3gpp_ussd_check_support (MMIfaceModem3gppUssd *self, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; task = g_task_new (self, NULL, callback, user_data); /* Check USSD support */ mm_base_modem_at_command (MM_BASE_MODEM (self), "+CUSD=?", 3, TRUE, (GAsyncReadyCallback)cusd_format_check_ready, task); } /*****************************************************************************/ /* Check if Messaging supported (Messaging interface) */ static gboolean modem_messaging_check_support_finish (MMIfaceModemMessaging *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void cnmi_format_check_ready (MMBroadbandModem *self, GAsyncResult *res, GTask *task) { GError *error = NULL; mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error); if (error) { g_task_return_error (task, error); g_object_unref (task); return; } /* CNMI command is supported; assume we have full messaging capabilities */ g_task_return_boolean (task, TRUE); g_object_unref (task); } static void modem_messaging_check_support (MMIfaceModemMessaging *self, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; task = g_task_new (self, NULL, callback, user_data); /* We assume that CDMA-only modems don't have messaging capabilities */ if (mm_iface_modem_is_cdma_only (MM_IFACE_MODEM (self))) { g_task_return_new_error ( task, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "CDMA-only modems don't have messaging capabilities"); g_object_unref (task); return; } /* Check CNMI support */ mm_base_modem_at_command (MM_BASE_MODEM (self), "+CNMI=?", 3, TRUE, (GAsyncReadyCallback)cnmi_format_check_ready, task); } /*****************************************************************************/ /* Load supported SMS storages (Messaging interface) */ typedef struct { GArray *mem1; GArray *mem2; GArray *mem3; } SupportedStoragesResult; static void supported_storages_result_free (SupportedStoragesResult *result) { if (result->mem1) g_array_unref (result->mem1); if (result->mem2) g_array_unref (result->mem2); if (result->mem3) g_array_unref (result->mem3); g_free (result); } static gboolean modem_messaging_load_supported_storages_finish (MMIfaceModemMessaging *self, GAsyncResult *res, GArray **mem1, GArray **mem2, GArray **mem3, GError **error) { SupportedStoragesResult *result; result = g_task_propagate_pointer (G_TASK (res), error); if (!result) return FALSE; *mem1 = g_array_ref (result->mem1); *mem2 = g_array_ref (result->mem2); *mem3 = g_array_ref (result->mem3); supported_storages_result_free (result); return TRUE; } static void cpms_format_check_ready (MMBroadbandModem *self, GAsyncResult *res, GTask *task) { const gchar *response; GError *error = NULL; SupportedStoragesResult *result; response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error); if (error) { g_task_return_error (task, error); g_object_unref (task); return; } result = g_new0 (SupportedStoragesResult, 1); /* Parse reply */ if (!mm_3gpp_parse_cpms_test_response (response, &result->mem1, &result->mem2, &result->mem3, &error)) { supported_storages_result_free (result); g_task_return_error (task, error); g_object_unref (task); return; } g_task_return_pointer (task, result, (GDestroyNotify)supported_storages_result_free); g_object_unref (task); } static void modem_messaging_load_supported_storages (MMIfaceModemMessaging *self, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; task = g_task_new (self, NULL, callback, user_data); /* Check support storages */ mm_base_modem_at_command (MM_BASE_MODEM (self), "+CPMS=?", 3, TRUE, (GAsyncReadyCallback)cpms_format_check_ready, task); } /*****************************************************************************/ /* Init current SMS storages (Messaging interface) */ static gboolean modem_messaging_init_current_storages_finish (MMIfaceModemMessaging *_self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void cpms_query_ready (MMBroadbandModem *self, GAsyncResult *res, GTask *task) { const gchar *response; GError *error = NULL; MMSmsStorage mem1; MMSmsStorage mem2; response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error); if (error) { g_task_return_error (task, error); g_object_unref (task); return; } /* Parse reply */ if (!mm_3gpp_parse_cpms_query_response (response, &mem1, &mem2, &error)) { g_task_return_error (task, error); } else { gchar *aux; self->priv->current_sms_mem1_storage = mem1; self->priv->current_sms_mem2_storage = mem2; mm_obj_dbg (self, "current storages initialized:"); aux = mm_common_build_sms_storages_string (&mem1, 1); mm_obj_dbg (self, " mem1 (list/read/delete) storages: '%s'", aux); g_free (aux); aux = mm_common_build_sms_storages_string (&mem2, 1); mm_obj_dbg (self, " mem2 (write/send) storages: '%s'", aux); g_free (aux); g_task_return_boolean (task, TRUE); } g_object_unref (task); } static void modem_messaging_init_current_storages (MMIfaceModemMessaging *self, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; task = g_task_new (self, NULL, callback, user_data); /* Check support storages */ mm_base_modem_at_command (MM_BASE_MODEM (self), "+CPMS?", 3, TRUE, (GAsyncReadyCallback)cpms_query_ready, task); } /*****************************************************************************/ /* Lock/unlock SMS storage (Messaging interface implementation helper) * * The basic commands to work with SMS storages play with AT+CPMS and three * different storages: mem1, mem2 and mem3. * 'mem1' is the storage for reading, listing and deleting. * 'mem2' is the storage for writing and sending from storage. * 'mem3' is the storage for receiving. * * When a command is to be issued for a specific storage, we need a way to * lock the access so that other actions are forbidden until the current one * finishes. Just think of two sequential actions to store two different * SMS into 2 different storages. If the second action is run while the first * one is still running, we should issue a RETRY error. * * Note that mem3 cannot be locked; we just set the default mem3 and that's it. * * When we unlock the storage, we don't go back to the default storage * automatically, we just keep track of which is the current one and only go to * the default one if needed. */ void mm_broadband_modem_unlock_sms_storages (MMBroadbandModem *self, gboolean mem1, gboolean mem2) { if (mem1) { g_assert (self->priv->mem1_storage_locked); self->priv->mem1_storage_locked = FALSE; } if (mem2) { g_assert (self->priv->mem2_storage_locked); self->priv->mem2_storage_locked = FALSE; } } gboolean mm_broadband_modem_lock_sms_storages_finish (MMBroadbandModem *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } typedef struct { MMSmsStorage previous_mem1; gboolean mem1_locked; MMSmsStorage previous_mem2; gboolean mem2_locked; } LockSmsStoragesContext; static void lock_storages_cpms_set_ready (MMBaseModem *_self, GAsyncResult *res, GTask *task) { MMBroadbandModem *self = MM_BROADBAND_MODEM (_self); LockSmsStoragesContext *ctx; GError *error = NULL; ctx = g_task_get_task_data (task); mm_base_modem_at_command_finish (_self, res, &error); if (error) { /* Reset previous storages and set unlocked */ if (ctx->mem1_locked) { self->priv->current_sms_mem1_storage = ctx->previous_mem1; self->priv->mem1_storage_locked = FALSE; } if (ctx->mem2_locked) { self->priv->current_sms_mem2_storage = ctx->previous_mem2; self->priv->mem2_storage_locked = FALSE; } g_task_return_error (task, error); } else g_task_return_boolean (task, TRUE); g_object_unref (task); } void mm_broadband_modem_lock_sms_storages (MMBroadbandModem *self, MMSmsStorage mem1, /* reading/listing/deleting */ MMSmsStorage mem2, /* storing/sending */ GAsyncReadyCallback callback, gpointer user_data) { LockSmsStoragesContext *ctx; GTask *task; gchar *cmd = NULL; gchar *mem1_str = NULL; gchar *mem2_str = NULL; /* If storages are currently locked by someone else, just return an * error */ if ((mem1 != MM_SMS_STORAGE_UNKNOWN && self->priv->mem1_storage_locked) || (mem2 != MM_SMS_STORAGE_UNKNOWN && self->priv->mem2_storage_locked)) { g_task_report_new_error ( self, callback, user_data, mm_broadband_modem_lock_sms_storages, MM_CORE_ERROR, MM_CORE_ERROR_RETRY, "SMS storage currently locked, try again later"); return; } /* We allow locking either just one or both */ g_assert (mem1 != MM_SMS_STORAGE_UNKNOWN || mem2 != MM_SMS_STORAGE_UNKNOWN); ctx = g_new0 (LockSmsStoragesContext, 1); task = g_task_new (self, NULL, callback, user_data); g_task_set_task_data (task, ctx, g_free); /* Some modems may not support empty string parameters, then if mem1 is * UNKNOWN, and current sms mem1 storage has a valid value, we send again * the already locked mem1 value in place of an empty string. * This way we also avoid to confuse the environment of other sync operation * that have potentially locked mem1 previously. */ if (mem1 != MM_SMS_STORAGE_UNKNOWN) { ctx->mem1_locked = TRUE; ctx->previous_mem1 = self->priv->current_sms_mem1_storage; self->priv->current_sms_mem1_storage = mem1; self->priv->mem1_storage_locked = TRUE; } else if (self->priv->current_sms_mem1_storage != MM_SMS_STORAGE_UNKNOWN) { mm_obj_dbg (self, "given sms mem1 storage is unknown. Using current sms mem1 storage value '%s' instead", mm_sms_storage_get_string (self->priv->current_sms_mem1_storage)); } else { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_RETRY, "Cannot lock mem2 storage alone when current mem1 storage is unknown"); g_object_unref (task); return; } mem1_str = g_ascii_strup (mm_sms_storage_get_string (self->priv->current_sms_mem1_storage), -1); if (mem2 != MM_SMS_STORAGE_UNKNOWN) { ctx->mem2_locked = TRUE; ctx->previous_mem2 = self->priv->current_sms_mem2_storage; self->priv->mem2_storage_locked = TRUE; self->priv->current_sms_mem2_storage = mem2; mem2_str = g_ascii_strup (mm_sms_storage_get_string (self->priv->current_sms_mem2_storage), -1); } g_assert (mem1_str != NULL); /* We don't touch 'mem3' here */ mm_obj_dbg (self, "locking SMS storages to: mem1 (%s), mem2 (%s)...", mem1_str, mem2_str ? mem2_str : "none"); if (mem2_str) cmd = g_strdup_printf ("+CPMS=\"%s\",\"%s\"", mem1_str, mem2_str); else cmd = g_strdup_printf ("+CPMS=\"%s\"", mem1_str); mm_base_modem_at_command (MM_BASE_MODEM (self), cmd, 3, FALSE, (GAsyncReadyCallback)lock_storages_cpms_set_ready, task); g_free (mem1_str); g_free (mem2_str); g_free (cmd); } /*****************************************************************************/ /* Set default SMS storage (Messaging interface) */ static gboolean modem_messaging_set_default_storage_finish (MMIfaceModemMessaging *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void cpms_set_ready (MMBroadbandModem *self, GAsyncResult *res, GTask *task) { GError *error = NULL; mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error); if (error) g_task_return_error (task, error); else g_task_return_boolean (task, TRUE); g_object_unref (task); } static void modem_messaging_set_default_storage (MMIfaceModemMessaging *_self, MMSmsStorage storage, GAsyncReadyCallback callback, gpointer user_data) { MMBroadbandModem *self = MM_BROADBAND_MODEM (_self); gchar *cmd; gchar *mem1_str; gchar *mem_str; GTask *task; /* We provide the current sms storage for mem1 if not UNKNOWN */ if (self->priv->current_sms_mem1_storage == MM_SMS_STORAGE_UNKNOWN) { g_task_report_new_error (self, callback, user_data, modem_messaging_set_default_storage, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS, "Cannot set default storage when current mem1 storage is unknown"); return; } /* Set defaults as current */ self->priv->current_sms_mem2_storage = storage; mem1_str = g_ascii_strup (mm_sms_storage_get_string (self->priv->current_sms_mem1_storage), -1); mem_str = g_ascii_strup (mm_sms_storage_get_string (storage), -1); cmd = g_strdup_printf ("+CPMS=\"%s\",\"%s\",\"%s\"", mem1_str, mem_str, mem_str); task = g_task_new (self, NULL, callback, user_data); mm_base_modem_at_command (MM_BASE_MODEM (self), cmd, 3, FALSE, (GAsyncReadyCallback)cpms_set_ready, task); g_free (mem1_str); g_free (mem_str); g_free (cmd); } /*****************************************************************************/ /* Setup SMS format (Messaging interface) */ static gboolean modem_messaging_setup_sms_format_finish (MMIfaceModemMessaging *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void cmgf_set_ready (MMBroadbandModem *self, GAsyncResult *res, GTask *task) { GError *error = NULL; mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error); if (error) { mm_obj_dbg (self, "failed to set preferred SMS mode: %s; assuming text mode'", error->message); g_error_free (error); self->priv->modem_messaging_sms_pdu_mode = FALSE; } else mm_obj_dbg (self, "successfully set preferred SMS mode: '%s'", self->priv->modem_messaging_sms_pdu_mode ? "PDU" : "text"); g_task_return_boolean (task, TRUE); g_object_unref (task); } static void set_preferred_sms_format (MMBroadbandModem *self, GTask *task) { gchar *cmd; cmd = g_strdup_printf ("+CMGF=%s", self->priv->modem_messaging_sms_pdu_mode ? "0" : "1"); mm_base_modem_at_command (MM_BASE_MODEM (self), cmd, 3, TRUE, (GAsyncReadyCallback)cmgf_set_ready, task); g_free (cmd); } static void cmgf_format_check_ready (MMBroadbandModem *self, GAsyncResult *res, GTask *task) { GError *error = NULL; const gchar *response; gboolean sms_pdu_supported = FALSE; gboolean sms_text_supported = FALSE; response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error); if (error || !mm_3gpp_parse_cmgf_test_response (response, &sms_pdu_supported, &sms_text_supported, &error)) { mm_obj_dbg (self, "failed to query supported SMS modes: %s", error->message); g_error_free (error); } /* Only use text mode if PDU mode not supported */ self->priv->modem_messaging_sms_pdu_mode = TRUE; if (!sms_pdu_supported) { if (sms_text_supported) { mm_obj_dbg (self, "PDU mode not supported, will try to use Text mode"); self->priv->modem_messaging_sms_pdu_mode = FALSE; } else mm_obj_dbg (self, "neither PDU nor Text modes are reported as supported; " "will anyway default to PDU mode"); } self->priv->sms_supported_modes_checked = TRUE; set_preferred_sms_format (self, task); } static void modem_messaging_setup_sms_format (MMIfaceModemMessaging *self, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; task = g_task_new (self, NULL, callback, user_data); /* If we already checked for supported SMS types, go on to select the * preferred format. */ if (MM_BROADBAND_MODEM (self)->priv->sms_supported_modes_checked) { set_preferred_sms_format (MM_BROADBAND_MODEM (self), task); return; } /* Check supported SMS formats */ mm_base_modem_at_command (MM_BASE_MODEM (self), "+CMGF=?", 3, TRUE, (GAsyncReadyCallback)cmgf_format_check_ready, task); } /*****************************************************************************/ /* Setup/cleanup messaging related unsolicited events (Messaging interface) */ static gboolean modem_messaging_setup_cleanup_unsolicited_events_finish (MMIfaceModemMessaging *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } typedef struct { guint idx; } SmsPartContext; static void sms_part_ready (MMBroadbandModem *self, GAsyncResult *res, GTask *task) { SmsPartContext *ctx; MMSmsPart *part; MM3gppPduInfo *info; const gchar *response; GError *error = NULL; /* Always always always unlock mem1 storage. Warned you've been. */ mm_broadband_modem_unlock_sms_storages (self, TRUE, FALSE); response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error); if (error) { /* We're really ignoring this error afterwards, as we don't have a callback * passed to the async operation, so just log the error here. */ mm_obj_warn (self, "couldn't retrieve SMS part: '%s'", error->message); g_task_return_error (task, error); g_object_unref (task); return; } ctx = g_task_get_task_data (task); info = mm_3gpp_parse_cmgr_read_response (response, ctx->idx, &error); if (!info) { mm_obj_warn (self, "couldn't parse SMS part: '%s'", error->message); g_task_return_error (task, error); g_object_unref (task); return; } part = mm_sms_part_3gpp_new_from_pdu (info->index, info->pdu, self, &error); if (part) { mm_obj_dbg (self, "correctly parsed PDU (%d)", ctx->idx); mm_iface_modem_messaging_take_part (MM_IFACE_MODEM_MESSAGING (self), part, MM_SMS_STATE_RECEIVED, self->priv->modem_messaging_sms_default_storage); } else { /* Don't treat the error as critical */ mm_obj_dbg (self, "error parsing PDU (%d): %s", ctx->idx, error->message); g_error_free (error); } /* All done */ mm_3gpp_pdu_info_free (info); g_task_return_boolean (task, TRUE); g_object_unref (task); } static void indication_lock_storages_ready (MMBroadbandModem *self, GAsyncResult *res, GTask *task) { SmsPartContext *ctx; gchar *command; GError *error = NULL; if (!mm_broadband_modem_lock_sms_storages_finish (self, res, &error)) { /* TODO: we should either make this lock() never fail, by automatically * retrying after some time, or otherwise retry here. */ g_task_return_error (task, error); g_object_unref (task); return; } /* Storage now set and locked */ /* Retrieve the message */ ctx = g_task_get_task_data (task); command = g_strdup_printf ("+CMGR=%d", ctx->idx); mm_base_modem_at_command (MM_BASE_MODEM (self), command, 10, FALSE, (GAsyncReadyCallback)sms_part_ready, task); g_free (command); } static void cmti_received (MMPortSerialAt *port, GMatchInfo *info, MMBroadbandModem *self) { SmsPartContext *ctx; GTask *task; guint idx = 0; MMSmsStorage storage; gchar *str; if (!mm_get_uint_from_match_info (info, 2, &idx)) return; /* The match info gives us in which storage the index applies */ str = mm_get_string_unquoted_from_match_info (info, 1); storage = mm_common_get_sms_storage_from_string (str, NULL); if (storage == MM_SMS_STORAGE_UNKNOWN) { mm_obj_dbg (self, "skipping CMTI indication, unknown storage '%s' reported", str); g_free (str); return; } g_free (str); /* Don't signal multiple times if there are multiple CMTI notifications for a message */ if (mm_sms_list_has_part (self->priv->modem_messaging_sms_list, storage, idx)) { mm_obj_dbg (self, "skipping CMTI indication, part already processed"); return; } ctx = g_new (SmsPartContext, 1); ctx->idx = idx; task = g_task_new (self, NULL, NULL, NULL); g_task_set_task_data (task, ctx, g_free); /* First, request to set the proper storage to read from */ mm_broadband_modem_lock_sms_storages (self, storage, MM_SMS_STORAGE_UNKNOWN, (GAsyncReadyCallback)indication_lock_storages_ready, task); } static void cds_received (MMPortSerialAt *port, GMatchInfo *info, MMBroadbandModem *self) { GError *error = NULL; MMSmsPart *part; guint length; gchar *pdu; mm_obj_dbg (self, "got new non-stored message indication"); if (!mm_get_uint_from_match_info (info, 1, &length)) return; pdu = g_match_info_fetch (info, 2); if (!pdu) return; part = mm_sms_part_3gpp_new_from_pdu (SMS_PART_INVALID_INDEX, pdu, self, &error); if (part) { mm_obj_dbg (self, "correctly parsed non-stored PDU"); mm_iface_modem_messaging_take_part (MM_IFACE_MODEM_MESSAGING (self), part, MM_SMS_STATE_RECEIVED, MM_SMS_STORAGE_UNKNOWN); } else { /* Don't treat the error as critical */ mm_obj_dbg (self, "error parsing non-stored PDU: %s", error->message); g_error_free (error); } } static void set_messaging_unsolicited_events_handlers (MMIfaceModemMessaging *self, gboolean enable, GAsyncReadyCallback callback, gpointer user_data) { MMPortSerialAt *ports[2]; g_autoptr(GRegex) cmti_regex = NULL; g_autoptr(GRegex) cds_regex = NULL; guint i; GTask *task; cmti_regex = mm_3gpp_cmti_regex_get (); cds_regex = mm_3gpp_cds_regex_get (); ports[0] = mm_base_modem_peek_port_primary (MM_BASE_MODEM (self)); ports[1] = mm_base_modem_peek_port_secondary (MM_BASE_MODEM (self)); /* Add messaging unsolicited events handler for port primary and secondary */ for (i = 0; i < 2; i++) { if (!ports[i]) continue; /* Set/unset unsolicited CMTI event handler */ mm_obj_dbg (self, "%s messaging unsolicited events handlers in %s", enable ? "setting" : "removing", mm_port_get_device (MM_PORT (ports[i]))); mm_port_serial_at_add_unsolicited_msg_handler ( ports[i], cmti_regex, enable ? (MMPortSerialAtUnsolicitedMsgFn) cmti_received : NULL, enable ? self : NULL, NULL); mm_port_serial_at_add_unsolicited_msg_handler ( ports[i], cds_regex, enable ? (MMPortSerialAtUnsolicitedMsgFn) cds_received : NULL, enable ? self : NULL, NULL); } task = g_task_new (self, NULL, callback, user_data); g_task_return_boolean (task, TRUE); g_object_unref (task); } static void modem_messaging_setup_unsolicited_events (MMIfaceModemMessaging *self, GAsyncReadyCallback callback, gpointer user_data) { set_messaging_unsolicited_events_handlers (self, TRUE, callback, user_data); } static void modem_messaging_cleanup_unsolicited_events (MMIfaceModemMessaging *self, GAsyncReadyCallback callback, gpointer user_data) { set_messaging_unsolicited_events_handlers (self, FALSE, callback, user_data); } /*****************************************************************************/ /* Enable unsolicited events (SMS indications) (Messaging interface) */ static gboolean modem_messaging_enable_unsolicited_events_finish (MMIfaceModemMessaging *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static MMBaseModemAtResponseProcessorResult cnmi_response_processor (MMBaseModem *self, gpointer none, const gchar *command, const gchar *response, gboolean last_command, const GError *error, GVariant **result, GError **result_error) { *result = NULL; *result_error = NULL; if (error) { /* If we get a not-supported error and we're not in the last command, we * won't set 'result_error', so we'll keep on the sequence */ if (!g_error_matches (error, MM_MESSAGE_ERROR, MM_MESSAGE_ERROR_NOT_SUPPORTED) || last_command) { *result_error = g_error_copy (error); return MM_BASE_MODEM_AT_RESPONSE_PROCESSOR_RESULT_FAILURE; } return MM_BASE_MODEM_AT_RESPONSE_PROCESSOR_RESULT_CONTINUE; } return MM_BASE_MODEM_AT_RESPONSE_PROCESSOR_RESULT_SUCCESS; } static const MMBaseModemAtCommand cnmi_sequence[] = { { "+CNMI=2,1,2,1,0", 3, FALSE, cnmi_response_processor }, /* Many Qualcomm-based devices don't support of '1', despite * reporting they support it in the +CNMI=? response. But they do * accept '2'. */ { "+CNMI=2,1,2,2,0", 3, FALSE, cnmi_response_processor }, /* Last resort: turn off delivery status reports altogether */ { "+CNMI=2,1,2,0,0", 3, FALSE, cnmi_response_processor }, { NULL } }; static void modem_messaging_enable_unsolicited_events_secondary_ready (MMBaseModem *self, GAsyncResult *res, GTask *task) { GError *inner_error = NULL; MMPortSerialAt *secondary; secondary = mm_base_modem_peek_port_secondary (MM_BASE_MODEM (self)); /* Since the secondary is not required, we don't propagate the error anywhere */ mm_base_modem_at_sequence_full_finish (MM_BASE_MODEM (self), res, NULL, &inner_error); if (inner_error) { mm_obj_dbg (self, "failed to enable messaging unsolicited events on secondary port %s: %s", mm_port_get_device (MM_PORT (secondary)), inner_error->message); g_error_free (inner_error); } mm_obj_dbg (self, "messaging unsolicited events enabled on secondary port %s", mm_port_get_device (MM_PORT (secondary))); g_task_return_boolean (task, TRUE); g_object_unref (task); } static void modem_messaging_enable_unsolicited_events_primary_ready (MMBaseModem *self, GAsyncResult *res, GTask *task) { GError *inner_error = NULL; MMPortSerialAt *primary; MMPortSerialAt *secondary; primary = mm_base_modem_peek_port_primary (MM_BASE_MODEM (self)); secondary = mm_base_modem_peek_port_secondary (MM_BASE_MODEM (self)); mm_base_modem_at_sequence_full_finish (MM_BASE_MODEM (self), res, NULL, &inner_error); if (inner_error) { g_task_return_error (task, inner_error); g_object_unref (task); return; } mm_obj_dbg (self, "messaging unsolicited events enabled on primary port %s", mm_port_get_device (MM_PORT (primary))); /* Try to enable unsolicited events for secondary port */ if (secondary) { mm_obj_dbg (self, "enabling messaging unsolicited events on secondary port %s", mm_port_get_device (MM_PORT (secondary))); mm_base_modem_at_sequence_full ( MM_BASE_MODEM (self), secondary, cnmi_sequence, NULL, /* response_processor_context */ NULL, /* response_processor_context_free */ NULL, (GAsyncReadyCallback)modem_messaging_enable_unsolicited_events_secondary_ready, task); return; } g_task_return_boolean (task, TRUE); g_object_unref (task); } static void modem_messaging_enable_unsolicited_events (MMIfaceModemMessaging *self, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; MMPortSerialAt *primary; task = g_task_new (self, NULL, callback, user_data); primary = mm_base_modem_peek_port_primary (MM_BASE_MODEM (self)); /* Do nothing if the modem doesn't have any AT port (e.g. it could be * a QMI modem trying to enable the parent unsolicited messages) */ if (!primary) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "No AT port to enable messaging unsolicited events"); g_object_unref (task); return; } /* Enable unsolicited events for primary port */ mm_obj_dbg (self, "enabling messaging unsolicited events on primary port %s", mm_port_get_device (MM_PORT (primary))); mm_base_modem_at_sequence_full ( MM_BASE_MODEM (self), primary, cnmi_sequence, NULL, /* response_processor_context */ NULL, /* response_processor_context_free */ NULL, (GAsyncReadyCallback)modem_messaging_enable_unsolicited_events_primary_ready, task); } /*****************************************************************************/ /* Load initial list of SMS parts (Messaging interface) */ typedef struct { MMSmsStorage list_storage; } ListPartsContext; static gboolean modem_messaging_load_initial_sms_parts_finish (MMIfaceModemMessaging *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static MMSmsState sms_state_from_str (const gchar *str) { /* We merge unread and read messages in the same state */ if (strstr (str, "REC")) return MM_SMS_STATE_RECEIVED; /* look for 'unsent' BEFORE looking for 'sent' */ if (strstr (str, "UNSENT")) return MM_SMS_STATE_STORED; if (strstr (str, "SENT")) return MM_SMS_STATE_SENT; return MM_SMS_STATE_UNKNOWN; } static MMSmsPduType sms_pdu_type_from_str (const gchar *str) { /* We merge unread and read messages in the same state */ if (strstr (str, "REC")) return MM_SMS_PDU_TYPE_DELIVER; if (strstr (str, "STO")) return MM_SMS_PDU_TYPE_SUBMIT; return MM_SMS_PDU_TYPE_UNKNOWN; } static void sms_text_part_list_ready (MMBroadbandModem *self, GAsyncResult *res, GTask *task) { ListPartsContext *ctx; g_autoptr(GRegex) r = NULL; g_autoptr(GMatchInfo) match_info = NULL; const gchar *response; GError *error = NULL; response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error); if (error) { g_task_return_error (task, error); g_object_unref (task); return; } /* +CMGL: ,,,[alpha], */ r = g_regex_new ("\\+CMGL:\\s*(\\d+)\\s*,\\s*([^,]*),\\s*([^,]*),\\s*([^,]*),\\s*([^\\r\\n]*)\\r\\n([^\\r\\n]*)", 0, 0, NULL); g_assert (r); if (!g_regex_match (r, response, 0, &match_info)) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS, "Couldn't parse SMS list response"); g_object_unref (task); return; } ctx = g_task_get_task_data (task); while (g_match_info_matches (match_info)) { MMSmsPart *part; guint matches; guint idx; g_autofree gchar *number_enc = NULL; g_autofree gchar *number = NULL; g_autofree gchar *timestamp = NULL; g_autofree gchar *text_enc = NULL; g_autofree gchar *text = NULL; g_autofree gchar *stat = NULL; g_autoptr(GByteArray) raw = NULL; g_autoptr(GError) inner_error = NULL; matches = g_match_info_get_match_count (match_info); if (matches != 7) { mm_obj_dbg (self, "failed to match entire CMGL response (count %d)", matches); goto next; } if (!mm_get_uint_from_match_info (match_info, 1, &idx)) { mm_obj_dbg (self, "failed to convert message index"); goto next; } /* Get part state */ stat = mm_get_string_unquoted_from_match_info (match_info, 2); if (!stat) { mm_obj_dbg (self, "failed to get part status"); goto next; } /* Get and parse number */ number_enc = mm_get_string_unquoted_from_match_info (match_info, 3); if (!number_enc) { mm_obj_dbg (self, "failed to get message sender number"); goto next; } number = mm_modem_charset_str_to_utf8 (number_enc, -1, self->priv->modem_current_charset, FALSE, &inner_error); if (!number) { mm_obj_dbg (self, "failed to convert message sender number to UTF-8: %s", inner_error->message); goto next; } /* Get and parse timestamp (always expected in ASCII) */ timestamp = mm_get_string_unquoted_from_match_info (match_info, 5); if (timestamp && !g_str_is_ascii (timestamp)) { mm_obj_dbg (self, "failed to parse input timestamp as ASCII"); goto next; } /* Get and parse text */ text_enc = g_match_info_fetch (match_info, 6); text = mm_modem_charset_str_to_utf8 (text_enc, -1, self->priv->modem_current_charset, FALSE, &inner_error); if (!text) { mm_obj_dbg (self, "failed to convert message text to UTF-8: %s", inner_error->message); goto next; } /* The raw SMS data can only be GSM, UCS2, or unknown (8-bit), so we * need to convert to UCS2 here. */ raw = mm_modem_charset_bytearray_from_utf8 (text, MM_MODEM_CHARSET_UCS2, FALSE, NULL); g_assert (raw); /* all take() methods pass ownership of the value as well */ part = mm_sms_part_new (idx, sms_pdu_type_from_str (stat)); mm_sms_part_take_number (part, g_steal_pointer (&number)); mm_sms_part_take_timestamp (part, g_steal_pointer (×tamp)); mm_sms_part_take_text (part, g_steal_pointer (&text)); mm_sms_part_take_data (part, g_steal_pointer (&raw)); mm_sms_part_set_class (part, -1); mm_obj_dbg (self, "correctly parsed SMS list entry (%d)", idx); mm_iface_modem_messaging_take_part (MM_IFACE_MODEM_MESSAGING (self), part, sms_state_from_str (stat), ctx->list_storage); next: g_match_info_next (match_info, NULL); } /* We consider all done */ g_task_return_boolean (task, TRUE); g_object_unref (task); } static MMSmsState sms_state_from_index (guint index) { /* We merge unread and read messages in the same state */ switch (index) { case 0: /* received, unread */ case 1: /* received, read */ return MM_SMS_STATE_RECEIVED; case 2: return MM_SMS_STATE_STORED; case 3: return MM_SMS_STATE_SENT; default: return MM_SMS_STATE_UNKNOWN; } } static void sms_pdu_part_list_ready (MMBroadbandModem *self, GAsyncResult *res, GTask *task) { ListPartsContext *ctx; const gchar *response; GError *error = NULL; GList *info_list; GList *l; /* Always always always unlock mem1 storage. Warned you've been. */ mm_broadband_modem_unlock_sms_storages (self, TRUE, FALSE); response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error); if (error) { g_task_return_error (task, error); g_object_unref (task); return; } info_list = mm_3gpp_parse_pdu_cmgl_response (response, &error); if (error) { g_task_return_error (task, error); g_object_unref (task); return; } ctx = g_task_get_task_data (task); for (l = info_list; l; l = g_list_next (l)) { MM3gppPduInfo *info = l->data; MMSmsPart *part; part = mm_sms_part_3gpp_new_from_pdu (info->index, info->pdu, self, &error); if (part) { mm_obj_dbg (self, "correctly parsed PDU (%d)", info->index); mm_iface_modem_messaging_take_part (MM_IFACE_MODEM_MESSAGING (self), part, sms_state_from_index (info->status), ctx->list_storage); } else { /* Don't treat the error as critical */ mm_obj_dbg (self, "error parsing PDU (%d): %s", info->index, error->message); g_clear_error (&error); } } mm_3gpp_pdu_info_list_free (info_list); /* We consider all done */ g_task_return_boolean (task, TRUE); g_object_unref (task); } static void list_parts_lock_storages_ready (MMBroadbandModem *self, GAsyncResult *res, GTask *task) { GError *error = NULL; if (!mm_broadband_modem_lock_sms_storages_finish (self, res, &error)) { /* TODO: we should either make this lock() never fail, by automatically * retrying after some time, or otherwise retry here. */ g_task_return_error (task, error); g_object_unref (task); return; } /* Storage now set and locked */ /* Get SMS parts from ALL types. * Different command to be used if we are on Text or PDU mode */ mm_base_modem_at_command (MM_BASE_MODEM (self), (MM_BROADBAND_MODEM (self)->priv->modem_messaging_sms_pdu_mode ? "+CMGL=4" : "+CMGL=\"ALL\""), 120, FALSE, (GAsyncReadyCallback) (MM_BROADBAND_MODEM (self)->priv->modem_messaging_sms_pdu_mode ? sms_pdu_part_list_ready : sms_text_part_list_ready), task); } static void modem_messaging_load_initial_sms_parts (MMIfaceModemMessaging *self, MMSmsStorage storage, GAsyncReadyCallback callback, gpointer user_data) { ListPartsContext *ctx; GTask *task; ctx = g_new (ListPartsContext, 1); ctx->list_storage = storage; task = g_task_new (self, NULL, callback, user_data); g_task_set_task_data (task, ctx, g_free); mm_obj_dbg (self, "listing SMS parts in storage '%s'", mm_sms_storage_get_string (storage)); /* First, request to set the proper storage to read from */ mm_broadband_modem_lock_sms_storages (MM_BROADBAND_MODEM (self), storage, MM_SMS_STORAGE_UNKNOWN, (GAsyncReadyCallback)list_parts_lock_storages_ready, task); } /*****************************************************************************/ /* Create SMS (Messaging interface) */ static MMBaseSms * modem_messaging_create_sms (MMIfaceModemMessaging *self) { return mm_base_sms_new (MM_BASE_MODEM (self)); } /*****************************************************************************/ /* Check if Voice supported (Voice interface) */ static gboolean modem_voice_check_support_finish (MMIfaceModemVoice *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void ignore_sim_related_errors (GError **error) { g_assert (error && *error); if (g_error_matches (*error, MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_SIM_NOT_INSERTED) || g_error_matches (*error, MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_SIM_PIN) || g_error_matches (*error, MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_SIM_PUK) || g_error_matches (*error, MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_SIM_FAILURE) || g_error_matches (*error, MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_SIM_BUSY) || g_error_matches (*error, MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_SIM_WRONG)) { g_clear_error (error); } } static void clcc_format_check_ready (MMBroadbandModem *self, GAsyncResult *res, GTask *task) { /* +CLCC supported unless we got any error response */ self->priv->clcc_supported = !!mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, NULL); /* If +CLCC unsupported we disable polling in the parent directly */ g_object_set (self, MM_IFACE_MODEM_VOICE_PERIODIC_CALL_LIST_CHECK_DISABLED, !self->priv->clcc_supported, NULL); /* ATH command is supported; assume we have full voice capabilities */ g_task_return_boolean (task, TRUE); g_object_unref (task); } static void ath_format_check_ready (MMBroadbandModem *self, GAsyncResult *res, GTask *task) { GError *error = NULL; mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error); if (error) { /* Ignore some errors that the module may return when there is no SIM inserted or * if the SIM is PIN-locked. We do need the voice interface exposed even in those * cases, in order to support emergency calls */ ignore_sim_related_errors (&error); if (error) { g_task_return_error (task, error); g_object_unref (task); return; } } /* Also check if +CLCC is supported */ mm_base_modem_at_command (MM_BASE_MODEM (self), "+CLCC=?", 3, /* Do NOT cache as the reply may be different if PIN locked * or unlocked. E.g. we may not support +CLCC for emergency * voice calls. */ FALSE, (GAsyncReadyCallback)clcc_format_check_ready, task); } static void modem_voice_check_support (MMIfaceModemVoice *self, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; task = g_task_new (self, NULL, callback, user_data); /* We assume that all modems have voice capabilities, but ... */ /* Check ATH support */ mm_base_modem_at_command (MM_BASE_MODEM (self), "H", 3, FALSE, (GAsyncReadyCallback)ath_format_check_ready, task); } /*****************************************************************************/ /* Load full list of calls (Voice interface) */ static gboolean modem_voice_load_call_list_finish (MMIfaceModemVoice *self, GAsyncResult *res, GList **out_call_info_list, GError **error) { GList *call_info_list; GError *inner_error = NULL; call_info_list = g_task_propagate_pointer (G_TASK (res), &inner_error); if (inner_error) { g_assert (!call_info_list); g_propagate_error (error, inner_error); return FALSE; } *out_call_info_list = call_info_list; return TRUE; } static void clcc_ready (MMBaseModem *self, GAsyncResult *res, GTask *task) { const gchar *response; GError *error = NULL; GList *call_info_list = NULL; response = mm_base_modem_at_command_finish (self, res, &error); if (!response || !mm_3gpp_parse_clcc_response (response, self, &call_info_list, &error)) g_task_return_error (task, error); else g_task_return_pointer (task, call_info_list, (GDestroyNotify)mm_3gpp_call_info_list_free); g_object_unref (task); } static void modem_voice_load_call_list (MMIfaceModemVoice *self, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; task = g_task_new (self, NULL, callback, user_data); mm_base_modem_at_command (MM_BASE_MODEM (self), "+CLCC", 5, FALSE, (GAsyncReadyCallback)clcc_ready, task); } /*****************************************************************************/ /* Setup/cleanup voice related in-call unsolicited events (Voice interface) */ static gboolean modem_voice_setup_cleanup_in_call_unsolicited_events_finish (MMIfaceModemVoice *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void in_call_event_received (MMPortSerialAt *port, GMatchInfo *info, MMBroadbandModem *self) { MMCallInfo call_info; gchar *str; call_info.index = 0; call_info.direction = MM_CALL_DIRECTION_UNKNOWN; call_info.state = MM_CALL_STATE_TERMINATED; call_info.number = NULL; str = g_match_info_fetch (info, 1); mm_obj_dbg (self, "call terminated: %s", str); g_free (str); mm_iface_modem_voice_report_call (MM_IFACE_MODEM_VOICE (self), &call_info); } static void set_voice_in_call_unsolicited_events_handlers (MMBroadbandModem *self, PortsContext *ports_ctx, gboolean enable) { MMPortSerialAt *ports[2]; g_autoptr(GRegex) in_call_event_regex = NULL; guint i; in_call_event_regex = g_regex_new ("\\r\\n(NO CARRIER|BUSY|NO ANSWER|NO DIALTONE)(\\r)?\\r\\n$", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); ports[0] = MM_PORT_SERIAL_AT (ports_ctx->primary); ports[1] = MM_PORT_SERIAL_AT (ports_ctx->secondary); /* Enable unsolicited events in given port */ for (i = 0; i < 2; i++) { if (!ports[i]) continue; mm_obj_dbg (self, "%s voice in-call unsolicited events handlers in %s", enable ? "setting" : "removing", mm_port_get_device (MM_PORT (ports[i]))); mm_port_serial_at_add_unsolicited_msg_handler ( ports[i], in_call_event_regex, enable ? (MMPortSerialAtUnsolicitedMsgFn) in_call_event_received : NULL, enable ? self : NULL, NULL); } } static void modem_voice_setup_in_call_unsolicited_events (MMIfaceModemVoice *_self, GAsyncReadyCallback callback, gpointer user_data) { MMBroadbandModem *self; GTask *task; GError *error = NULL; self = MM_BROADBAND_MODEM (_self); if (!self->priv->in_call_ports_ctx) { PortsContext *ctx; mm_obj_dbg (self, "setting up in-call ports context"); ctx = ports_context_new (); if (!ports_context_open (self, ctx, FALSE, TRUE, FALSE, &error)) { ports_context_unref (ctx); g_prefix_error (&error, "Couldn't open ports in-call: "); } else { set_voice_in_call_unsolicited_events_handlers (self, ctx, TRUE); self->priv->in_call_ports_ctx = ctx; } } else mm_obj_dbg (self, "in-call ports context already set up"); task = g_task_new (self, NULL, callback, user_data); if (error) g_task_return_error (task, error); else g_task_return_boolean (task, TRUE); g_object_unref (task); } static void modem_voice_cleanup_in_call_unsolicited_events (MMIfaceModemVoice *_self, GAsyncReadyCallback callback, gpointer user_data) { MMBroadbandModem *self; GTask *task; self = MM_BROADBAND_MODEM (_self); if (self->priv->in_call_ports_ctx) { mm_obj_dbg (self, "cleaning up in-call ports context"); set_voice_in_call_unsolicited_events_handlers (self, self->priv->in_call_ports_ctx, FALSE); g_clear_pointer (&self->priv->in_call_ports_ctx, (GDestroyNotify) ports_context_unref); } else mm_obj_dbg (self, "in-call ports context already cleaned up"); task = g_task_new (self, NULL, callback, user_data); g_task_return_boolean (task, TRUE); g_object_unref (task); } /*****************************************************************************/ /* Setup/cleanup voice related unsolicited events (Voice interface) */ static gboolean modem_voice_setup_cleanup_unsolicited_events_finish (MMIfaceModemVoice *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void ccwa_received (MMPortSerialAt *port, GMatchInfo *info, MMBroadbandModem *self) { MMCallInfo call_info; gboolean indication_call_list_reload_enabled = FALSE; g_object_get (self, MM_IFACE_MODEM_VOICE_INDICATION_CALL_LIST_RELOAD_ENABLED, &indication_call_list_reload_enabled, NULL); if (indication_call_list_reload_enabled) { mm_obj_dbg (self, "call waiting, refreshing call list"); mm_iface_modem_voice_reload_all_calls (MM_IFACE_MODEM_VOICE (self), NULL, NULL); return; } call_info.index = 0; call_info.direction = MM_CALL_DIRECTION_INCOMING; call_info.state = MM_CALL_STATE_WAITING; call_info.number = mm_get_string_unquoted_from_match_info (info, 1); mm_obj_dbg (self, "call waiting (%s)", call_info.number); mm_iface_modem_voice_report_call (MM_IFACE_MODEM_VOICE (self), &call_info); g_free (call_info.number); } static void ring_received (MMPortSerialAt *port, GMatchInfo *info, MMBroadbandModem *self) { MMCallInfo call_info; gboolean indication_call_list_reload_enabled = FALSE; g_object_get (self, MM_IFACE_MODEM_VOICE_INDICATION_CALL_LIST_RELOAD_ENABLED, &indication_call_list_reload_enabled, NULL); if (indication_call_list_reload_enabled) { mm_obj_dbg (self, "ringing, refreshing call list"); mm_iface_modem_voice_reload_all_calls (MM_IFACE_MODEM_VOICE (self), NULL, NULL); return; } call_info.index = 0; call_info.direction = MM_CALL_DIRECTION_INCOMING; call_info.state = MM_CALL_STATE_RINGING_IN; call_info.number = NULL; mm_obj_dbg (self, "ringing"); mm_iface_modem_voice_report_call (MM_IFACE_MODEM_VOICE (self), &call_info); } static void cring_received (MMPortSerialAt *port, GMatchInfo *info, MMBroadbandModem *self) { MMCallInfo call_info; gchar *str; gboolean indication_call_list_reload_enabled = FALSE; g_object_get (self, MM_IFACE_MODEM_VOICE_INDICATION_CALL_LIST_RELOAD_ENABLED, &indication_call_list_reload_enabled, NULL); if (indication_call_list_reload_enabled) { mm_obj_dbg (self, "ringing, refreshing call list"); mm_iface_modem_voice_reload_all_calls (MM_IFACE_MODEM_VOICE (self), NULL, NULL); return; } /* We could have "VOICE" or "DATA". Now consider only "VOICE" */ str = mm_get_string_unquoted_from_match_info (info, 1); mm_obj_dbg (self, "ringing (%s)", str); g_free (str); call_info.index = 0; call_info.direction = MM_CALL_DIRECTION_INCOMING; call_info.state = MM_CALL_STATE_RINGING_IN; call_info.number = NULL; mm_iface_modem_voice_report_call (MM_IFACE_MODEM_VOICE (self), &call_info); } static void clip_received (MMPortSerialAt *port, GMatchInfo *info, MMBroadbandModem *self) { MMCallInfo call_info; gboolean indication_call_list_reload_enabled = FALSE; g_object_get (self, MM_IFACE_MODEM_VOICE_INDICATION_CALL_LIST_RELOAD_ENABLED, &indication_call_list_reload_enabled, NULL); if (indication_call_list_reload_enabled) { mm_obj_dbg (self, "ringing, refreshing call list"); mm_iface_modem_voice_reload_all_calls (MM_IFACE_MODEM_VOICE (self), NULL, NULL); return; } call_info.index = 0; call_info.direction = MM_CALL_DIRECTION_INCOMING; call_info.state = MM_CALL_STATE_RINGING_IN; call_info.number = mm_get_string_unquoted_from_match_info (info, 1); mm_obj_dbg (self, "ringing (%s)", call_info.number); mm_iface_modem_voice_report_call (MM_IFACE_MODEM_VOICE (self), &call_info); g_free (call_info.number); } static void set_voice_unsolicited_events_handlers (MMIfaceModemVoice *self, gboolean enable, GAsyncReadyCallback callback, gpointer user_data) { MMPortSerialAt *ports[2]; g_autoptr(GRegex) cring_regex = NULL; g_autoptr(GRegex) ring_regex = NULL; g_autoptr(GRegex) clip_regex = NULL; g_autoptr(GRegex) ccwa_regex = NULL; guint i; GTask *task; cring_regex = mm_voice_cring_regex_get (); ring_regex = mm_voice_ring_regex_get (); clip_regex = mm_voice_clip_regex_get (); ccwa_regex = mm_voice_ccwa_regex_get (); ports[0] = mm_base_modem_peek_port_primary (MM_BASE_MODEM (self)); ports[1] = mm_base_modem_peek_port_secondary (MM_BASE_MODEM (self)); /* Enable unsolicited events in given port */ for (i = 0; i < 2; i++) { if (!ports[i]) continue; /* Set/unset unsolicited CMTI event handler */ mm_obj_dbg (self, "%s voice unsolicited events handlers in %s", enable ? "setting" : "removing", mm_port_get_device (MM_PORT (ports[i]))); mm_port_serial_at_add_unsolicited_msg_handler ( ports[i], cring_regex, enable ? (MMPortSerialAtUnsolicitedMsgFn) cring_received : NULL, enable ? self : NULL, NULL); mm_port_serial_at_add_unsolicited_msg_handler ( ports[i], ring_regex, enable ? (MMPortSerialAtUnsolicitedMsgFn) ring_received : NULL, enable ? self : NULL, NULL); mm_port_serial_at_add_unsolicited_msg_handler ( ports[i], clip_regex, enable ? (MMPortSerialAtUnsolicitedMsgFn) clip_received : NULL, enable ? self : NULL, NULL); mm_port_serial_at_add_unsolicited_msg_handler ( ports[i], ccwa_regex, enable ? (MMPortSerialAtUnsolicitedMsgFn) ccwa_received : NULL, enable ? self : NULL, NULL); } task = g_task_new (self, NULL, callback, user_data); g_task_return_boolean (task, TRUE); g_object_unref (task); } static void modem_voice_setup_unsolicited_events (MMIfaceModemVoice *self, GAsyncReadyCallback callback, gpointer user_data) { set_voice_unsolicited_events_handlers (self, TRUE, callback, user_data); } static void modem_voice_cleanup_unsolicited_events (MMIfaceModemVoice *self, GAsyncReadyCallback callback, gpointer user_data) { set_voice_unsolicited_events_handlers (self, FALSE, callback, user_data); } /*****************************************************************************/ /* Enable unsolicited events (CALL indications) (Voice interface) */ typedef struct { gboolean enable; MMPortSerialAt *primary; MMPortSerialAt *secondary; gchar *clip_command; gboolean clip_primary_done; gboolean clip_secondary_done; gchar *crc_command; gboolean crc_primary_done; gboolean crc_secondary_done; gchar *ccwa_command; gboolean ccwa_primary_done; gboolean ccwa_secondary_done; } VoiceUnsolicitedEventsContext; static void voice_unsolicited_events_context_free (VoiceUnsolicitedEventsContext *ctx) { g_clear_object (&ctx->secondary); g_clear_object (&ctx->primary); g_free (ctx->clip_command); g_free (ctx->crc_command); g_free (ctx->ccwa_command); g_slice_free (VoiceUnsolicitedEventsContext, ctx); } static gboolean modem_voice_enable_disable_unsolicited_events_finish (MMIfaceModemVoice *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void run_voice_unsolicited_events_setup (GTask *task); static void voice_unsolicited_events_setup_ready (MMBroadbandModem *self, GAsyncResult *res, GTask *task) { VoiceUnsolicitedEventsContext *ctx; GError *error = NULL; ctx = g_task_get_task_data (task); if (!mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error)) { mm_obj_dbg (self, "couldn't %s voice event reporting: '%s'", ctx->enable ? "enable" : "disable", error->message); g_error_free (error); } /* Continue on next port/command */ run_voice_unsolicited_events_setup (task); } static void run_voice_unsolicited_events_setup (GTask *task) { MMBroadbandModem *self; VoiceUnsolicitedEventsContext *ctx; MMPortSerialAt *port = NULL; const gchar *command = NULL; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); /* CLIP on primary port */ if (!ctx->clip_primary_done && ctx->clip_command && ctx->primary) { mm_obj_dbg (self, "%s +CLIP calling line reporting in primary port...", ctx->enable ? "enabling" : "disabling"); ctx->clip_primary_done = TRUE; command = ctx->clip_command; port = ctx->primary; } /* CLIP on secondary port */ else if (!ctx->clip_secondary_done && ctx->clip_command && ctx->secondary) { mm_obj_dbg (self, "%s +CLIP calling line reporting in secondary port...", ctx->enable ? "enabling" : "disabling"); ctx->clip_secondary_done = TRUE; command = ctx->clip_command; port = ctx->secondary; } /* CRC on primary port */ else if (!ctx->crc_primary_done && ctx->crc_command && ctx->primary) { mm_obj_dbg (self, "%s +CRC extended format of incoming call indications in primary port...", ctx->enable ? "enabling" : "disabling"); ctx->crc_primary_done = TRUE; command = ctx->crc_command; port = ctx->primary; } /* CRC on secondary port */ else if (!ctx->crc_secondary_done && ctx->crc_command && ctx->secondary) { mm_obj_dbg (self, "%s +CRC extended format of incoming call indications in secondary port...", ctx->enable ? "enabling" : "disabling"); ctx->crc_secondary_done = TRUE; command = ctx->crc_command; port = ctx->secondary; } /* CCWA on primary port */ else if (!ctx->ccwa_primary_done && ctx->ccwa_command && ctx->primary) { mm_obj_dbg (self, "%s +CCWA call waiting indications in primary port...", ctx->enable ? "enabling" : "disabling"); ctx->ccwa_primary_done = TRUE; command = ctx->ccwa_command; port = ctx->primary; } /* CCWA on secondary port */ else if (!ctx->ccwa_secondary_done && ctx->ccwa_command && ctx->secondary) { mm_obj_dbg (self, "%s +CCWA call waiting indications in secondary port...", ctx->enable ? "enabling" : "disabling"); ctx->ccwa_secondary_done = TRUE; command = ctx->ccwa_command; port = ctx->secondary; } /* Enable/Disable unsolicited events in given port */ if (port && command) { mm_base_modem_at_command_full (MM_BASE_MODEM (self), port, command, 3, FALSE, FALSE, /* raw */ NULL, /* cancellable */ (GAsyncReadyCallback)voice_unsolicited_events_setup_ready, task); return; } /* Fully done now */ g_task_return_boolean (task, TRUE); g_object_unref (task); } static void modem_voice_enable_unsolicited_events (MMIfaceModemVoice *self, GAsyncReadyCallback callback, gpointer user_data) { VoiceUnsolicitedEventsContext *ctx; GTask *task; task = g_task_new (self, NULL, callback, user_data); ctx = g_slice_new0 (VoiceUnsolicitedEventsContext); ctx->enable = TRUE; ctx->primary = mm_base_modem_get_port_primary (MM_BASE_MODEM (self)); ctx->secondary = mm_base_modem_get_port_secondary (MM_BASE_MODEM (self)); /* enable +CLIP URCs with calling line identity */ ctx->clip_command = g_strdup ("+CLIP=1"); /* enable +CRING URCs instead of plain RING */ ctx->crc_command = g_strdup ("+CRC=1"); /* enable +CCWA call waiting indications */ ctx->ccwa_command = g_strdup ("+CCWA=1"); g_task_set_task_data (task, ctx, (GDestroyNotify) voice_unsolicited_events_context_free); run_voice_unsolicited_events_setup (task); } static void modem_voice_disable_unsolicited_events (MMIfaceModemVoice *self, GAsyncReadyCallback callback, gpointer user_data) { VoiceUnsolicitedEventsContext *ctx; GTask *task; task = g_task_new (self, NULL, callback, user_data); ctx = g_slice_new0 (VoiceUnsolicitedEventsContext); ctx->primary = mm_base_modem_get_port_primary (MM_BASE_MODEM (self)); ctx->secondary = mm_base_modem_get_port_secondary (MM_BASE_MODEM (self)); /* disable +CLIP URCs with calling line identity */ ctx->clip_command = g_strdup ("+CLIP=0"); /* disable +CRING URCs instead of plain RING */ ctx->crc_command = g_strdup ("+CRC=0"); /* disable +CCWA call waiting indications */ ctx->ccwa_command = g_strdup ("+CCWA=0"); g_task_set_task_data (task, ctx, (GDestroyNotify) voice_unsolicited_events_context_free); run_voice_unsolicited_events_setup (task); } /*****************************************************************************/ /* Create CALL (Voice interface) */ static MMBaseCall * modem_voice_create_call (MMIfaceModemVoice *_self, MMCallDirection direction, const gchar *number) { MMBroadbandModem *self = MM_BROADBAND_MODEM (_self); return mm_base_call_new (MM_BASE_MODEM (self), direction, number, /* If +CLCC is supported, we want no incoming timeout. * Also, we're able to support detailed call state updates without * additional vendor-specific commands. */ self->priv->clcc_supported, /* skip incoming timeout */ self->priv->clcc_supported, /* dialing->ringing supported */ self->priv->clcc_supported); /* ringing->active supported */ } /*****************************************************************************/ /* Hold and accept (Voice interface) */ static gboolean modem_voice_hold_and_accept_finish (MMIfaceModemVoice *self, GAsyncResult *res, GError **error) { return !!mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, error); } static void modem_voice_hold_and_accept (MMIfaceModemVoice *self, GAsyncReadyCallback callback, gpointer user_data) { mm_base_modem_at_command (MM_BASE_MODEM (self), "+CHLD=2", 20, FALSE, callback, user_data); } /*****************************************************************************/ /* Hangup and accept (Voice interface) */ static gboolean modem_voice_hangup_and_accept_finish (MMIfaceModemVoice *self, GAsyncResult *res, GError **error) { return !!mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, error); } static void modem_voice_hangup_and_accept (MMIfaceModemVoice *self, GAsyncReadyCallback callback, gpointer user_data) { mm_base_modem_at_command (MM_BASE_MODEM (self), "+CHLD=1", 20, FALSE, callback, user_data); } /*****************************************************************************/ /* Hangup all (Voice interface) */ static gboolean modem_voice_hangup_all_finish (MMIfaceModemVoice *self, GAsyncResult *res, GError **error) { return !!mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, error); } static void modem_voice_hangup_all (MMIfaceModemVoice *self, GAsyncReadyCallback callback, gpointer user_data) { mm_base_modem_at_command (MM_BASE_MODEM (self), "+CHUP", 3, FALSE, callback, user_data); } /*****************************************************************************/ /* Join multiparty (Voice interface) */ static gboolean modem_voice_join_multiparty_finish (MMIfaceModemVoice *self, GAsyncResult *res, GError **error) { return !!mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, error); } static void modem_voice_join_multiparty (MMIfaceModemVoice *self, GAsyncReadyCallback callback, gpointer user_data) { mm_base_modem_at_command (MM_BASE_MODEM (self), "+CHLD=3", 20, FALSE, callback, user_data); } /*****************************************************************************/ /* Leave multiparty (Voice interface) */ static gboolean modem_voice_leave_multiparty_finish (MMIfaceModemVoice *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void chld_leave_multiparty_ready (MMBaseModem *self, GAsyncResult *res, GTask *task) { GError *error = NULL; if (!mm_base_modem_at_command_finish (self, res, &error)) g_task_return_error (task, error); else g_task_return_boolean (task, TRUE); g_object_unref (task); } static void modem_voice_leave_multiparty (MMIfaceModemVoice *self, MMBaseCall *call, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; guint idx; gchar *cmd; task = g_task_new (self, NULL, callback, user_data); idx = mm_base_call_get_index (call); if (!idx) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_WRONG_STATE, "unknown call index"); g_object_unref (task); return; } cmd = g_strdup_printf ("+CHLD=2%u", idx); mm_base_modem_at_command (MM_BASE_MODEM (self), cmd, 20, FALSE, (GAsyncReadyCallback) chld_leave_multiparty_ready, task); g_free (cmd); } /*****************************************************************************/ /* Transfer (Voice interface) */ static gboolean modem_voice_transfer_finish (MMIfaceModemVoice *self, GAsyncResult *res, GError **error) { return !!mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, error); } static void modem_voice_transfer (MMIfaceModemVoice *self, GAsyncReadyCallback callback, gpointer user_data) { mm_base_modem_at_command (MM_BASE_MODEM (self), "+CHLD=4", 20, FALSE, callback, user_data); } /*****************************************************************************/ /* Call waiting setup (Voice interface) */ static gboolean modem_voice_call_waiting_setup_finish (MMIfaceModemVoice *self, GAsyncResult *res, GError **error) { return !!mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, error); } static void modem_voice_call_waiting_setup (MMIfaceModemVoice *self, gboolean enable, GAsyncReadyCallback callback, gpointer user_data) { gchar *cmd; /* Enabling or disabling the call waiting service will only be allowed when * the modem is registered in the network, and so, CCWA URC handling will * always be setup at this point (as it's part of the modem enabling phase). * So, just enable or disable the service (second field) but leaving URCs * (first field) always enabled. */ cmd = g_strdup_printf ("+CCWA=1,%u", enable); mm_base_modem_at_command (MM_BASE_MODEM (self), cmd, 60, FALSE, callback, user_data); g_free (cmd); } /*****************************************************************************/ /* Call waiting query (Voice interface) */ static gboolean modem_voice_call_waiting_query_finish (MMIfaceModemVoice *self, GAsyncResult *res, gboolean *status, GError **error) { const gchar *response; response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, error); if (!response) return FALSE; return mm_3gpp_parse_ccwa_service_query_response (response, self, status, error); } static void modem_voice_call_waiting_query (MMIfaceModemVoice *self, GAsyncReadyCallback callback, gpointer user_data) { /* This operation will only be allowed while enabled, and so, CCWA URC * handling would always be enabled at this point. So, just perform the * query, but leaving URCs enabled either way. */ mm_base_modem_at_command (MM_BASE_MODEM (self), "+CCWA=1,2", 60, FALSE, callback, user_data); } /*****************************************************************************/ /* ESN loading (CDMA interface) */ static gchar * modem_cdma_load_esn_finish (MMIfaceModemCdma *self, GAsyncResult *res, GError **error) { const gchar *result; gchar *esn = NULL; result = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, error); if (!result) return NULL; result = mm_strip_tag (result, "+GSN:"); mm_parse_gsn (result, NULL, NULL, &esn); mm_obj_dbg (self, "loaded ESN: %s", esn); return esn; } static void modem_cdma_load_esn (MMIfaceModemCdma *self, GAsyncReadyCallback callback, gpointer user_data) { mm_obj_dbg (self, "loading ESN..."); mm_base_modem_at_command (MM_BASE_MODEM (self), "+GSN", 3, TRUE, callback, user_data); } /*****************************************************************************/ /* MEID loading (CDMA interface) */ static gchar * modem_cdma_load_meid_finish (MMIfaceModemCdma *self, GAsyncResult *res, GError **error) { const gchar *result; gchar *meid = NULL; result = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, error); if (!result) return NULL; result = mm_strip_tag (result, "+GSN:"); mm_parse_gsn (result, NULL, &meid, NULL); mm_obj_dbg (self, "loaded MEID: %s", meid); return meid; } static void modem_cdma_load_meid (MMIfaceModemCdma *self, GAsyncReadyCallback callback, gpointer user_data) { /* Some devices return both the MEID and the ESN in the +GSN response */ mm_obj_dbg (self, "loading MEID..."); mm_base_modem_at_command (MM_BASE_MODEM (self), "+GSN", 3, TRUE, callback, user_data); } /*****************************************************************************/ /* Setup/Cleanup unsolicited events (CDMA interface) */ typedef struct { gboolean setup; MMPortSerialQcdm *qcdm; gboolean close_port; } CdmaUnsolicitedEventsContext; static void cdma_unsolicited_events_context_free (CdmaUnsolicitedEventsContext *ctx) { if (ctx->qcdm && ctx->close_port) mm_port_serial_close (MM_PORT_SERIAL (ctx->qcdm)); g_clear_object (&ctx->qcdm); g_free (ctx); } static void logcmd_qcdm_ready (MMPortSerialQcdm *port, GAsyncResult *res, GTask *task) { MMBroadbandModem *self; CdmaUnsolicitedEventsContext *ctx; QcdmResult *result; gint err = QCDM_SUCCESS; GByteArray *response; GError *error = NULL; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); response = mm_port_serial_qcdm_command_finish (port, res, &error); if (error) { ctx->close_port = TRUE; g_task_return_error (task, error); g_object_unref (task); return; } /* Parse the response */ result = qcdm_cmd_log_config_set_mask_result ((const gchar *) response->data, response->len, &err); g_byte_array_unref (response); if (!result) { ctx->close_port = TRUE; g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Failed to parse Log Config Set Mask command result: %d", err); g_object_unref (task); return; } mm_port_serial_qcdm_add_unsolicited_msg_handler (port, DM_LOG_ITEM_EVDO_PILOT_SETS_V2, ctx->setup ? qcdm_evdo_pilot_sets_log_handle : NULL, self, NULL); qcdm_result_unref (result); /* Balance the mm_port_seral_open() from modem_cdma_setup_cleanup_unsolicited_events(). * We want to close it in either case: * (a) we're cleaning up and setup opened the port * (b) if it was unexpectedly closed before cleanup and thus cleanup opened it * * Setup should leave the port open to allow log messages to be received * and sent to handlers. */ ctx->close_port = ctx->setup ? FALSE : TRUE; g_task_return_boolean (task, TRUE); g_object_unref (task); } static void modem_cdma_setup_cleanup_unsolicited_events (MMBroadbandModem *self, gboolean setup, GAsyncReadyCallback callback, gpointer user_data) { CdmaUnsolicitedEventsContext *ctx; GTask *task; GByteArray *logcmd; uint16_t log_items[] = { DM_LOG_ITEM_EVDO_PILOT_SETS_V2, 0 }; GError *error = NULL; ctx = g_new0 (CdmaUnsolicitedEventsContext, 1); ctx->setup = TRUE; task = g_task_new (self, NULL, callback, user_data); g_task_set_task_data (task, ctx, (GDestroyNotify)cdma_unsolicited_events_context_free); ctx->qcdm = mm_base_modem_get_port_qcdm (MM_BASE_MODEM (self)); if (!ctx->qcdm) { g_task_return_boolean (task, TRUE); g_object_unref (task); return; } /* Setup must open the QCDM port and keep it open to receive unsolicited * events. Cleanup expects the port to already be opened from setup, but * if not we still want to open it and try to disable log messages. */ if (setup || !mm_port_serial_is_open (MM_PORT_SERIAL (ctx->qcdm))) { if (!mm_port_serial_open (MM_PORT_SERIAL (ctx->qcdm), &error)) { g_task_return_boolean (task, TRUE); g_object_unref (task); return; } } logcmd = g_byte_array_sized_new (512); logcmd->len = qcdm_cmd_log_config_set_mask_new ((char *) logcmd->data, 512, 0x01, /* Equipment ID */ setup ? log_items : NULL); assert (logcmd->len); mm_port_serial_qcdm_command (ctx->qcdm, logcmd, 5, NULL, (GAsyncReadyCallback)logcmd_qcdm_ready, task); g_byte_array_unref (logcmd); } static gboolean modem_cdma_setup_cleanup_unsolicited_events_finish (MMIfaceModemCdma *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void modem_cdma_setup_unsolicited_events (MMIfaceModemCdma *self, GAsyncReadyCallback callback, gpointer user_data) { modem_cdma_setup_cleanup_unsolicited_events (MM_BROADBAND_MODEM (self), TRUE, callback, user_data); } static void modem_cdma_cleanup_unsolicited_events (MMIfaceModemCdma *self, GAsyncReadyCallback callback, gpointer user_data) { modem_cdma_setup_cleanup_unsolicited_events (MM_BROADBAND_MODEM (self), FALSE, callback, user_data); } /*****************************************************************************/ /* HDR state check (CDMA interface) */ typedef struct { guint8 hybrid_mode; guint8 session_state; guint8 almp_state; } HdrStateResults; static void hdr_state_cleanup_port (MMPortSerial *port) { mm_port_serial_close (port); g_object_unref (port); } static gboolean modem_cdma_get_hdr_state_finish (MMIfaceModemCdma *self, GAsyncResult *res, guint8 *hybrid_mode, guint8 *session_state, guint8 *almp_state, GError **error) { HdrStateResults *results; results = g_task_propagate_pointer (G_TASK (res), error); if (!results) return FALSE; *hybrid_mode = results->hybrid_mode; *session_state = results->session_state; *almp_state = results->almp_state; g_free (results); return TRUE; } static void hdr_subsys_state_info_ready (MMPortSerialQcdm *port, GAsyncResult *res, GTask *task) { QcdmResult *result; HdrStateResults *results; gint err = QCDM_SUCCESS; GError *error = NULL; GByteArray *response; response = mm_port_serial_qcdm_command_finish (port, res, &error); if (error) { g_task_return_error (task, error); g_object_unref (task); return; } /* Parse the response */ result = qcdm_cmd_hdr_subsys_state_info_result ((const gchar *) response->data, response->len, &err); g_byte_array_unref (response); if (!result) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Failed to parse HDR subsys state info command result: %d", err); g_object_unref (task); return; } /* Build results */ results = g_new0 (HdrStateResults, 1); qcdm_result_get_u8 (result, QCDM_CMD_HDR_SUBSYS_STATE_INFO_ITEM_HDR_HYBRID_MODE, &results->hybrid_mode); results->session_state = QCDM_CMD_HDR_SUBSYS_STATE_INFO_SESSION_STATE_CLOSED; qcdm_result_get_u8 (result, QCDM_CMD_HDR_SUBSYS_STATE_INFO_ITEM_SESSION_STATE, &results->session_state); results->almp_state = QCDM_CMD_HDR_SUBSYS_STATE_INFO_ALMP_STATE_INACTIVE; qcdm_result_get_u8 (result, QCDM_CMD_HDR_SUBSYS_STATE_INFO_ITEM_ALMP_STATE, &results->almp_state); qcdm_result_unref (result); g_task_return_pointer (task, results, g_free); g_object_unref (task); } static void modem_cdma_get_hdr_state (MMIfaceModemCdma *self, GAsyncReadyCallback callback, gpointer user_data) { MMPortSerialQcdm *qcdm; GTask *task; GByteArray *hdrstate; GError *error = NULL; task = g_task_new (self, NULL, callback, user_data); qcdm = mm_base_modem_peek_port_qcdm (MM_BASE_MODEM (self)); if (!qcdm) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "Cannot get HDR state without a QCDM port"); g_object_unref (task); return; } if (!mm_port_serial_open (MM_PORT_SERIAL (qcdm), &error)) { g_task_return_error (task, error); g_object_unref (task); return; } g_task_set_task_data (task, g_object_ref (qcdm), (GDestroyNotify) hdr_state_cleanup_port); /* Setup command */ hdrstate = g_byte_array_sized_new (25); hdrstate->len = qcdm_cmd_hdr_subsys_state_info_new ((gchar *) hdrstate->data, 25); g_assert (hdrstate->len); mm_port_serial_qcdm_command (qcdm, hdrstate, 3, NULL, (GAsyncReadyCallback)hdr_subsys_state_info_ready, task); g_byte_array_unref (hdrstate); } /*****************************************************************************/ /* Call Manager state check (CDMA interface) */ typedef struct { guint system_mode; guint operating_mode; } CallManagerStateResults; static void cm_state_cleanup_port (MMPortSerial *port) { mm_port_serial_close (port); g_object_unref (port); } static gboolean modem_cdma_get_call_manager_state_finish (MMIfaceModemCdma *self, GAsyncResult *res, guint *system_mode, guint *operating_mode, GError **error) { CallManagerStateResults *result; result = g_task_propagate_pointer (G_TASK (res), error); if (!result) return FALSE; *system_mode = result->system_mode; *operating_mode = result->operating_mode; g_free (result); return TRUE; } static void cm_subsys_state_info_ready (MMPortSerialQcdm *port, GAsyncResult *res, GTask *task) { QcdmResult *result; CallManagerStateResults *results; gint err = QCDM_SUCCESS; GError *error = NULL; GByteArray *response; response = mm_port_serial_qcdm_command_finish (port, res, &error); if (error) { g_task_return_error (task, error); g_object_unref (task); return; } /* Parse the response */ result = qcdm_cmd_cm_subsys_state_info_result ((const gchar *) response->data, response->len, &err); g_byte_array_unref (response); if (!result) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Failed to parse CM subsys state info command result: %d", err); g_object_unref (task); return; } /* Build results */ results = g_new0 (CallManagerStateResults, 1); qcdm_result_get_u32 (result, QCDM_CMD_CM_SUBSYS_STATE_INFO_ITEM_OPERATING_MODE, &results->operating_mode); qcdm_result_get_u32 (result, QCDM_CMD_CM_SUBSYS_STATE_INFO_ITEM_SYSTEM_MODE, &results->system_mode); qcdm_result_unref (result); g_task_return_pointer (task, results, g_free); g_object_unref (task); } static void modem_cdma_get_call_manager_state (MMIfaceModemCdma *self, GAsyncReadyCallback callback, gpointer user_data) { MMPortSerialQcdm *qcdm; GTask *task; GByteArray *cmstate; GError *error = NULL; task = g_task_new (self, NULL, callback, user_data); qcdm = mm_base_modem_peek_port_qcdm (MM_BASE_MODEM (self)); if (!qcdm) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "Cannot get call manager state without a QCDM port"); g_object_unref (task); return; } if (!mm_port_serial_open (MM_PORT_SERIAL (qcdm), &error)) { g_task_return_error (task, error); g_object_unref (task); return; } g_task_set_task_data (task, g_object_ref (qcdm), (GDestroyNotify) cm_state_cleanup_port); /* Setup command */ cmstate = g_byte_array_sized_new (25); cmstate->len = qcdm_cmd_cm_subsys_state_info_new ((gchar *) cmstate->data, 25); g_assert (cmstate->len); mm_port_serial_qcdm_command (qcdm, cmstate, 3, NULL, (GAsyncReadyCallback)cm_subsys_state_info_ready, task); g_byte_array_unref (cmstate); } /*****************************************************************************/ /* Serving System check (CDMA interface) */ typedef struct { guint sid; guint nid; guint class; guint band; } Cdma1xServingSystemResults; static void cdma1x_serving_system_state_cleanup_port (MMPortSerial *port) { mm_port_serial_close (port); g_object_unref (port); } static void cdma1x_serving_system_complete_and_free (GTask *task, guint sid, guint nid, guint class, guint band) { Cdma1xServingSystemResults *results; results = g_new0 (Cdma1xServingSystemResults, 1); results->sid = sid; results->band = band; results->class = class; results->nid = nid; g_task_return_pointer (task, results, g_free); g_object_unref (task); } static GError * cdma1x_serving_system_no_service_error (void) { /* NOTE: update get_cdma1x_serving_system_ready() in mm-iface-modem-cdma.c * if this error changes */ return g_error_new_literal (MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_NO_NETWORK, "No CDMA service"); } static gboolean modem_cdma_get_cdma1x_serving_system_finish (MMIfaceModemCdma *self, GAsyncResult *res, guint *class, guint *band, guint *sid, guint *nid, GError **error) { Cdma1xServingSystemResults *results; results = g_task_propagate_pointer (G_TASK (res), error); if (!results) return FALSE; *sid = results->sid; *nid = results->nid; *class = results->class; *band = results->band; g_free (results); return TRUE; } static void css_query_ready (MMIfaceModemCdma *self, GAsyncResult *res, GTask *task) { GError *error = NULL; const gchar *result; gint class = 0; gint sid = MM_MODEM_CDMA_SID_UNKNOWN; gint num; guchar band = 'Z'; gboolean class_ok = FALSE; gboolean band_ok = FALSE; gboolean success = FALSE; result = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error); if (error) { g_task_return_error (task, error); g_object_unref (task); return; } /* Strip any leading command tag and spaces */ result = mm_strip_tag (result, "+CSS:"); num = sscanf (result, "? , %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 { g_autoptr(GRegex) r = NULL; g_autoptr(GMatchInfo) match_info = NULL; /* Format is ",," */ r = g_regex_new ("\\s*([^,]*?)\\s*,\\s*([^,]*?)\\s*,\\s*(\\d+)", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); g_assert (r); g_regex_match (r, result, 0, &match_info); if (g_match_info_get_match_count (match_info) >= 3) { gint override_class = 0; gchar *str; /* band class */ str = g_match_info_fetch (match_info, 1); class = mm_cdma_normalize_class (str); g_free (str); /* band */ str = g_match_info_fetch (match_info, 2); band = mm_cdma_normalize_band (str, &override_class); if (override_class) class = override_class; g_free (str); /* sid */ str = g_match_info_fetch (match_info, 3); if (!mm_get_int_from_str (str, &sid)) sid = MM_MODEM_CDMA_SID_UNKNOWN; g_free (str); success = TRUE; } } if (!success) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Could not parse Serving System results"); g_object_unref (task); return; } /* Normalize the SID */ if (sid < 0 || sid > 32767) sid = MM_MODEM_CDMA_SID_UNKNOWN; 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 = MM_MODEM_CDMA_SID_UNKNOWN; /* 99999 means unknown/no service */ if (sid == MM_MODEM_CDMA_SID_UNKNOWN) { g_task_return_error (task, cdma1x_serving_system_no_service_error ()); g_object_unref (task); return; } /* No means to get NID with AT commands right now */ cdma1x_serving_system_complete_and_free (task, sid, MM_MODEM_CDMA_NID_UNKNOWN, class, band); } static void serving_system_query_css (GTask *task) { mm_base_modem_at_command (MM_BASE_MODEM (g_task_get_source_object (task)), "+CSS?", 3, FALSE, (GAsyncReadyCallback)css_query_ready, task); } static void qcdm_cdma_status_ready (MMPortSerialQcdm *port, GAsyncResult *res, GTask *task) { MMBroadbandModem *self; QcdmResult *result = NULL; guint32 sid = MM_MODEM_CDMA_SID_UNKNOWN; guint32 nid = MM_MODEM_CDMA_NID_UNKNOWN; guint32 rxstate = 0; gint err = QCDM_SUCCESS; GError *error = NULL; GByteArray *response; self = g_task_get_source_object (task); response = mm_port_serial_qcdm_command_finish (port, res, &error); if (error) { mm_obj_dbg (self, "failed to get cdma status: %s", error->message); g_clear_error (&error); /* Fall back to AT+CSS */ serving_system_query_css (task); return; } result = qcdm_cmd_cdma_status_result ((const gchar *) response->data, response->len, &err); if (!result) { if (err != QCDM_SUCCESS) mm_obj_dbg (self, "failed to parse cdma status command result: %d", err); g_byte_array_unref (response); /* Fall back to AT+CSS */ serving_system_query_css (task); return; } g_byte_array_unref (response); qcdm_result_get_u32 (result, QCDM_CMD_CDMA_STATUS_ITEM_RX_STATE, &rxstate); qcdm_result_get_u32 (result, QCDM_CMD_CDMA_STATUS_ITEM_SID, &sid); qcdm_result_get_u32 (result, QCDM_CMD_CDMA_STATUS_ITEM_NID, &nid); qcdm_result_unref (result); /* 99999 means unknown/no service */ if (rxstate == QCDM_CMD_CDMA_STATUS_RX_STATE_ENTERING_CDMA) { sid = MM_MODEM_CDMA_SID_UNKNOWN; nid = MM_MODEM_CDMA_NID_UNKNOWN; } mm_obj_dbg (self, "CDMA 1x Status RX state: %d", rxstate); mm_obj_dbg (self, "CDMA 1x Status SID: %d", sid); mm_obj_dbg (self, "CDMA 1x Status NID: %d", nid); cdma1x_serving_system_complete_and_free (task, sid, nid, 0, (sid == MM_MODEM_CDMA_SID_UNKNOWN) ? 0 : 'Z'); } static void modem_cdma_get_cdma1x_serving_system (MMIfaceModemCdma *self, GAsyncReadyCallback callback, gpointer user_data) { GError *error = NULL; GByteArray *cdma_status; GTask *task; MMPortSerialQcdm *qcdm; task = g_task_new (self, NULL, callback, user_data); qcdm = mm_base_modem_peek_port_qcdm (MM_BASE_MODEM (self)); if (!qcdm) { /* Fall back to AT+CSS */ serving_system_query_css (task); return; } if (!mm_port_serial_open (MM_PORT_SERIAL (qcdm), &error)) { mm_obj_dbg (self, "failed to open QCDM port for serving-system request: %s", error->message); g_error_free (error); /* Fall back to AT+CSS */ serving_system_query_css (task); return; } g_task_set_task_data (task, g_object_ref (qcdm), (GDestroyNotify) cdma1x_serving_system_state_cleanup_port); /* Setup command */ cdma_status = g_byte_array_sized_new (25); cdma_status->len = qcdm_cmd_cdma_status_new ((char *) cdma_status->data, 25); g_assert (cdma_status->len); mm_port_serial_qcdm_command (qcdm, cdma_status, 3, NULL, (GAsyncReadyCallback) qcdm_cdma_status_ready, task); g_byte_array_unref (cdma_status); } /*****************************************************************************/ /* Service status, analog/digital check (CDMA interface) */ static gboolean modem_cdma_get_service_status_finish (MMIfaceModemCdma *self, GAsyncResult *res, gboolean *has_cdma_service, GError **error) { GError *inner_error = NULL; gboolean value; value = g_task_propagate_boolean (G_TASK (res), &inner_error); if (inner_error) { g_propagate_error (error, inner_error); return FALSE; } *has_cdma_service = value; return TRUE; } static void cad_query_ready (MMIfaceModemCdma *self, GAsyncResult *res, GTask *task) { GError *error = NULL; const gchar *result; result = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error); if (error) g_task_return_error (task, error); else { guint cad; /* Strip any leading command tag and spaces */ result = mm_strip_tag (result, "+CAD:"); if (!mm_get_uint_from_str (result, &cad)) g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Failed to parse +CAD response '%s'", result); else /* 1 == CDMA service */ g_task_return_boolean (task, (cad == 1)); } g_object_unref (task); } static void modem_cdma_get_service_status (MMIfaceModemCdma *self, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; task = g_task_new (self, NULL, callback, user_data); mm_base_modem_at_command (MM_BASE_MODEM (self), "+CAD?", 3, FALSE, (GAsyncReadyCallback)cad_query_ready, task); } /*****************************************************************************/ /* Detailed registration state (CDMA interface) */ typedef struct { MMModemCdmaRegistrationState detailed_cdma1x_state; MMModemCdmaRegistrationState detailed_evdo_state; } DetailedRegistrationStateResults; typedef struct { MMPortSerialAt *port; MMModemCdmaRegistrationState cdma1x_state; MMModemCdmaRegistrationState evdo_state; } DetailedRegistrationStateContext; static DetailedRegistrationStateResults * detailed_registration_state_result_new (DetailedRegistrationStateContext *ctx) { DetailedRegistrationStateResults *results; results = g_new (DetailedRegistrationStateResults, 1); results->detailed_cdma1x_state = ctx->cdma1x_state; results->detailed_evdo_state = ctx->evdo_state; return results; } static void detailed_registration_state_context_free (DetailedRegistrationStateContext *ctx) { g_object_unref (ctx->port); g_free (ctx); } static gboolean modem_cdma_get_detailed_registration_state_finish (MMIfaceModemCdma *self, GAsyncResult *res, MMModemCdmaRegistrationState *detailed_cdma1x_state, MMModemCdmaRegistrationState *detailed_evdo_state, GError **error) { DetailedRegistrationStateResults *results; results = g_task_propagate_pointer (G_TASK (res), error); if (!results) return FALSE; *detailed_cdma1x_state = results->detailed_cdma1x_state; *detailed_evdo_state = results->detailed_evdo_state; g_free (results); return TRUE; } static void speri_ready (MMIfaceModemCdma *self, GAsyncResult *res, GTask *task) { DetailedRegistrationStateContext *ctx; gboolean roaming = FALSE; const gchar *response; GError *error = NULL; ctx = g_task_get_task_data (task); response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error); if (error) { /* silently discard SPERI errors */ g_error_free (error); g_task_return_pointer (task, detailed_registration_state_result_new (ctx), g_free); g_object_unref (task); return; } /* Try to parse the results */ response = mm_strip_tag (response, "$SPERI:"); if (!response || !mm_cdma_parse_eri (response, &roaming, NULL, NULL)) { mm_obj_warn (self, "couldn't parse SPERI response '%s'", response); g_task_return_pointer (task, detailed_registration_state_result_new (ctx), g_free); g_object_unref (task); return; } if (roaming) { /* Change the 1x and EVDO registration states to roaming if they were * anything other than UNKNOWN. */ if (ctx->cdma1x_state > MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN) ctx->cdma1x_state = MM_MODEM_CDMA_REGISTRATION_STATE_ROAMING; if (ctx->evdo_state > MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN) ctx->evdo_state = MM_MODEM_CDMA_REGISTRATION_STATE_ROAMING; } else { /* Change 1x and/or EVDO registration state to home if home/roaming wasn't previously known */ if (ctx->cdma1x_state == MM_MODEM_CDMA_REGISTRATION_STATE_REGISTERED) ctx->cdma1x_state = MM_MODEM_CDMA_REGISTRATION_STATE_HOME; if (ctx->evdo_state == MM_MODEM_CDMA_REGISTRATION_STATE_REGISTERED) ctx->evdo_state = MM_MODEM_CDMA_REGISTRATION_STATE_HOME; } g_task_return_pointer (task, detailed_registration_state_result_new (ctx), g_free); g_object_unref (task); } static void spservice_ready (MMIfaceModemCdma *self, GAsyncResult *res, GTask *task) { DetailedRegistrationStateContext *ctx; GError *error = NULL; const gchar *response; MMModemCdmaRegistrationState cdma1x_state; MMModemCdmaRegistrationState evdo_state; response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error); if (error) { g_task_return_error (task, error); g_object_unref (task); return; } /* Try to parse the results */ cdma1x_state = MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN; evdo_state = MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN; if (!mm_cdma_parse_spservice_read_response (response, &cdma1x_state, &evdo_state)) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't parse SPSERVICE response '%s'", response); g_object_unref (task); return; } ctx = g_task_get_task_data (task); /* Store new intermediate results */ ctx->cdma1x_state = cdma1x_state; ctx->evdo_state = evdo_state; /* If SPERI not supported, we're done */ if (!MM_BROADBAND_MODEM (self)->priv->has_speri) { g_task_return_pointer (task, detailed_registration_state_result_new (ctx), g_free); g_object_unref (task); return; } /* Get roaming status to override generic registration state */ mm_base_modem_at_command (MM_BASE_MODEM (self), "$SPERI?", 3, FALSE, (GAsyncReadyCallback)speri_ready, task); } static void modem_cdma_get_detailed_registration_state (MMIfaceModemCdma *self, MMModemCdmaRegistrationState cdma1x_state, MMModemCdmaRegistrationState evdo_state, GAsyncReadyCallback callback, gpointer user_data) { MMPortSerialAt *port; GError *error = NULL; DetailedRegistrationStateContext *ctx; GTask *task; /* The default implementation to get detailed registration state * requires the use of an AT port; so if we cannot get any, just * return the error */ port = mm_base_modem_peek_best_at_port (MM_BASE_MODEM (self), &error); if (!port) { g_task_report_error (self, callback, user_data, modem_cdma_get_detailed_registration_state, error); return; } /* Setup context */ ctx = g_new0 (DetailedRegistrationStateContext, 1); ctx->port = g_object_ref (port); ctx->cdma1x_state = cdma1x_state; ctx->evdo_state = evdo_state; task = g_task_new (self, NULL, callback, user_data); g_task_set_task_data (task, ctx, (GDestroyNotify)detailed_registration_state_context_free); /* NOTE: If we get this generic implementation of getting detailed * registration state called, we DO know that we have Sprint commands * supported, we checked it in setup_registration_checks() */ mm_base_modem_at_command (MM_BASE_MODEM (self), "+SPSERVICE?", 3, FALSE, (GAsyncReadyCallback)spservice_ready, task); } /*****************************************************************************/ /* Setup registration checks (CDMA interface) */ typedef struct { gboolean skip_qcdm_call_manager_step; gboolean skip_qcdm_hdr_step; gboolean skip_at_cdma_service_status_step; gboolean skip_at_cdma1x_serving_system_step; gboolean skip_detailed_registration_state; } SetupRegistrationChecksResults; typedef struct { gboolean has_qcdm_port; gboolean has_sprint_commands; } SetupRegistrationChecksContext; static SetupRegistrationChecksResults * setup_registration_checks_results_new (MMBroadbandModem *self, SetupRegistrationChecksContext *ctx) { SetupRegistrationChecksResults *results; results = g_new0 (SetupRegistrationChecksResults, 1); /* Skip QCDM steps if no QCDM port */ if (!ctx->has_qcdm_port) { mm_obj_dbg (self, "will skip all QCDM-based registration checks"); results->skip_qcdm_call_manager_step = TRUE; results->skip_qcdm_hdr_step = TRUE; } if (MM_IFACE_MODEM_CDMA_GET_INTERFACE (self)->get_detailed_registration_state == modem_cdma_get_detailed_registration_state) { /* Skip CDMA1x Serving System check if we have Sprint specific * commands AND if the default detailed registration checker * is the generic one. Implementations knowing that their * CSS response is undesired, should either setup NULL callbacks * for the specific step, or subclass this setup and return * FALSE themselves. */ if (ctx->has_sprint_commands) { mm_obj_dbg (self, "will skip CDMA1x Serving System check, we do have Sprint commands"); results->skip_at_cdma1x_serving_system_step = TRUE; } else { /* If there aren't Sprint specific commands, and the detailed * registration state getter wasn't subclassed, skip the step */ mm_obj_dbg (self, "will skip generic detailed registration check, we don't have Sprint commands"); results->skip_detailed_registration_state = TRUE; } } return results; } static gboolean modem_cdma_setup_registration_checks_finish (MMIfaceModemCdma *self, GAsyncResult *res, gboolean *skip_qcdm_call_manager_step, gboolean *skip_qcdm_hdr_step, gboolean *skip_at_cdma_service_status_step, gboolean *skip_at_cdma1x_serving_system_step, gboolean *skip_detailed_registration_state, GError **error) { SetupRegistrationChecksResults *results; results = g_task_propagate_pointer (G_TASK (res), error); if (!results) return FALSE; *skip_qcdm_call_manager_step = results->skip_qcdm_call_manager_step; *skip_qcdm_hdr_step = results->skip_qcdm_hdr_step; *skip_at_cdma_service_status_step = results->skip_at_cdma_service_status_step; *skip_at_cdma1x_serving_system_step = results->skip_at_cdma1x_serving_system_step; *skip_detailed_registration_state = results->skip_detailed_registration_state; g_free (results); return TRUE; } static void speri_check_ready (MMIfaceModemCdma *_self, GAsyncResult *res, GTask *task) { MMBroadbandModem *self = MM_BROADBAND_MODEM (_self); SetupRegistrationChecksContext *ctx; GError *error = NULL; ctx = g_task_get_task_data (task); mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error); if (error) g_error_free (error); else /* We DO have SPERI */ self->priv->has_speri = TRUE; /* All done */ self->priv->checked_sprint_support = TRUE; g_task_return_pointer (task, setup_registration_checks_results_new (self, ctx), g_free); g_object_unref (task); } static void spservice_check_ready (MMIfaceModemCdma *_self, GAsyncResult *res, GTask *task) { MMBroadbandModem *self = MM_BROADBAND_MODEM (_self); SetupRegistrationChecksContext *ctx; GError *error = NULL; ctx = g_task_get_task_data (task); mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error); if (error) { g_error_free (error); self->priv->checked_sprint_support = TRUE; g_task_return_pointer (task, setup_registration_checks_results_new (self, ctx), g_free); g_object_unref (task); return; } /* We DO have SPSERVICE, look for SPERI */ ctx->has_sprint_commands = TRUE; self->priv->has_spservice = TRUE; mm_base_modem_at_command (MM_BASE_MODEM (self), "$SPERI?", 3, FALSE, (GAsyncReadyCallback)speri_check_ready, task); } static void modem_cdma_setup_registration_checks (MMIfaceModemCdma *_self, GAsyncReadyCallback callback, gpointer user_data) { MMBroadbandModem *self = MM_BROADBAND_MODEM (_self); SetupRegistrationChecksContext *ctx; GTask *task; ctx = g_new0 (SetupRegistrationChecksContext, 1); /* Check if we have a QCDM port */ ctx->has_qcdm_port = !!mm_base_modem_peek_port_qcdm (MM_BASE_MODEM (self)); task = g_task_new (self, NULL, callback, user_data); g_task_set_task_data (task, ctx, g_free); /* If we have cached results of Sprint command checking, use them */ if (self->priv->checked_sprint_support) { ctx->has_sprint_commands = self->priv->has_spservice; /* Completes in idle */ g_task_return_pointer (task, setup_registration_checks_results_new (self, ctx), g_free); g_object_unref (task); return; } /* Otherwise, launch Sprint command checks. */ mm_base_modem_at_command (MM_BASE_MODEM (self), "+SPSERVICE?", 3, FALSE, (GAsyncReadyCallback)spservice_check_ready, task); } /*****************************************************************************/ /* Register in network (CDMA interface) */ typedef struct { MMBroadbandModem *self; GCancellable *cancellable; GTimer *timer; guint max_registration_time; } RegisterInCdmaNetworkContext; static void register_in_cdma_network_context_free (RegisterInCdmaNetworkContext *ctx) { /* If our cancellable reference is still around, clear it */ if (ctx->self->priv->modem_cdma_pending_registration_cancellable == ctx->cancellable) { g_clear_object (&ctx->self->priv->modem_cdma_pending_registration_cancellable); } if (ctx->timer) g_timer_destroy (ctx->timer); g_object_unref (ctx->cancellable); g_object_unref (ctx->self); g_free (ctx); } static gboolean modem_cdma_register_in_network_finish (MMIfaceModemCdma *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } #undef REG_IS_IDLE #define REG_IS_IDLE(state) \ (state == MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN) #undef REG_IS_DONE #define REG_IS_DONE(state) \ (state == MM_MODEM_CDMA_REGISTRATION_STATE_HOME || \ state == MM_MODEM_CDMA_REGISTRATION_STATE_ROAMING || \ state == MM_MODEM_CDMA_REGISTRATION_STATE_REGISTERED) static void run_cdma_registration_checks_ready (MMBroadbandModem *self, GAsyncResult *res, GTask *task); static gboolean run_cdma_registration_checks_again (GTask *task) { /* Get fresh registration state */ mm_iface_modem_cdma_run_registration_checks ( MM_IFACE_MODEM_CDMA (g_task_get_source_object (task)), (GAsyncReadyCallback)run_cdma_registration_checks_ready, task); return G_SOURCE_REMOVE; } static void run_cdma_registration_checks_ready (MMBroadbandModem *self, GAsyncResult *res, GTask *task) { RegisterInCdmaNetworkContext *ctx; GError *error = NULL; ctx = g_task_get_task_data (task); mm_iface_modem_cdma_run_registration_checks_finish (MM_IFACE_MODEM_CDMA (self), res, &error); if (error) { mm_obj_dbg (self, "CDMA registration check failed: %s", error->message); mm_iface_modem_cdma_update_cdma1x_registration_state ( MM_IFACE_MODEM_CDMA (self), MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN, MM_MODEM_CDMA_SID_UNKNOWN, MM_MODEM_CDMA_NID_UNKNOWN); mm_iface_modem_cdma_update_evdo_registration_state ( MM_IFACE_MODEM_CDMA (self), MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN); mm_iface_modem_cdma_update_access_technologies ( MM_IFACE_MODEM_CDMA (self), MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN); g_task_return_error (task, error); g_object_unref (task); return; } /* If we got registered in at least one CDMA network, end registration checks */ if (REG_IS_DONE (self->priv->modem_cdma_cdma1x_registration_state) || REG_IS_DONE (self->priv->modem_cdma_evdo_registration_state)) { mm_obj_dbg (self, "registered in a CDMA network (CDMA1x: '%s', EV-DO: '%s')", REG_IS_DONE (self->priv->modem_cdma_cdma1x_registration_state) ? "yes" : "no", REG_IS_DONE (self->priv->modem_cdma_evdo_registration_state) ? "yes" : "no"); g_task_return_boolean (task, TRUE); g_object_unref (task); return; } /* Don't spend too much time waiting to get registered */ if (g_timer_elapsed (ctx->timer, NULL) > ctx->max_registration_time) { mm_obj_dbg (self, "CDMA registration check timed out"); mm_iface_modem_cdma_update_cdma1x_registration_state ( MM_IFACE_MODEM_CDMA (self), MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN, MM_MODEM_CDMA_SID_UNKNOWN, MM_MODEM_CDMA_NID_UNKNOWN); mm_iface_modem_cdma_update_evdo_registration_state ( MM_IFACE_MODEM_CDMA (self), MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN); mm_iface_modem_cdma_update_access_technologies ( MM_IFACE_MODEM_CDMA (self), MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN); error = mm_mobile_equipment_error_for_code (MM_MOBILE_EQUIPMENT_ERROR_NETWORK_TIMEOUT, self); g_task_return_error (task, error); g_object_unref (task); return; } /* Check again in a few seconds. */ mm_obj_dbg (self, "not yet registered in a CDMA network... will recheck soon"); g_timeout_add_seconds (3, (GSourceFunc)run_cdma_registration_checks_again, task); } static void modem_cdma_register_in_network (MMIfaceModemCdma *_self, guint max_registration_time, GAsyncReadyCallback callback, gpointer user_data) { MMBroadbandModem *self = MM_BROADBAND_MODEM (_self); RegisterInCdmaNetworkContext *ctx; GTask *task; /* (Try to) cancel previous registration request */ if (self->priv->modem_cdma_pending_registration_cancellable) { g_cancellable_cancel (self->priv->modem_cdma_pending_registration_cancellable); g_clear_object (&self->priv->modem_cdma_pending_registration_cancellable); } ctx = g_new0 (RegisterInCdmaNetworkContext, 1); ctx->self = g_object_ref (self); ctx->max_registration_time = max_registration_time; ctx->cancellable = g_cancellable_new (); /* Keep an accessible reference to the cancellable, so that we can cancel * previous request when needed */ self->priv->modem_cdma_pending_registration_cancellable = g_object_ref (ctx->cancellable); /* Get fresh registration state */ ctx->timer = g_timer_new (); task = g_task_new (self, NULL, callback, user_data); g_task_set_task_data (task, ctx, (GDestroyNotify)register_in_cdma_network_context_free); mm_iface_modem_cdma_run_registration_checks ( _self, (GAsyncReadyCallback)run_cdma_registration_checks_ready, task); } /*****************************************************************************/ /* Load location capabilities (Location interface) */ static MMModemLocationSource modem_location_load_capabilities_finish (MMIfaceModemLocation *self, GAsyncResult *res, GError **error) { GError *inner_error = NULL; gssize value; value = g_task_propagate_int (G_TASK (res), &inner_error); if (inner_error) { g_propagate_error (error, inner_error); return MM_MODEM_LOCATION_SOURCE_NONE; } return (MMModemLocationSource)value; } static void modem_location_load_capabilities (MMIfaceModemLocation *self, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; task = g_task_new (self, NULL, callback, user_data); /* Default location capabilities supported by the generic broadband * implementation are only LAC-CI in 3GPP-enabled modems. And even this, * will only be true if the modem supports CREG/CGREG=2 */ if (!mm_iface_modem_is_3gpp (MM_IFACE_MODEM (self))) g_task_return_int (task, MM_MODEM_LOCATION_SOURCE_NONE); else g_task_return_int (task, MM_MODEM_LOCATION_SOURCE_3GPP_LAC_CI); g_object_unref (task); } /*****************************************************************************/ /* Enable location gathering (Location interface) */ static gboolean enable_location_gathering_finish (MMIfaceModemLocation *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void enable_location_gathering (MMIfaceModemLocation *self, MMModemLocationSource source, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; /* 3GPP modems need to re-run registration checks when enabling the 3GPP * location source, so that we get up to date LAC/CI location information. * Note that we don't care for when the registration checks get finished. */ if (source == MM_MODEM_LOCATION_SOURCE_3GPP_LAC_CI && mm_iface_modem_is_3gpp (MM_IFACE_MODEM (self))) { /* Reload registration to get LAC/CI */ mm_iface_modem_3gpp_run_registration_checks (MM_IFACE_MODEM_3GPP (self), NULL, NULL); /* Reload registration information to get MCC/MNC */ if (MM_BROADBAND_MODEM (self)->priv->modem_3gpp_registration_state == MM_MODEM_3GPP_REGISTRATION_STATE_HOME || MM_BROADBAND_MODEM (self)->priv->modem_3gpp_registration_state == MM_MODEM_3GPP_REGISTRATION_STATE_ROAMING) mm_iface_modem_3gpp_reload_current_registration_info (MM_IFACE_MODEM_3GPP (self), NULL, NULL); } /* Done we are */ task = g_task_new (self, NULL, callback, user_data); g_task_return_boolean (task, TRUE); g_object_unref (task); } /*****************************************************************************/ /* Load network time (Time interface) */ static gchar * modem_time_load_network_time_finish (MMIfaceModemTime *self, GAsyncResult *res, GError **error) { const gchar *response; gchar *result = NULL; response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, error); if (!response) return NULL; if (!mm_parse_cclk_response (response, &result, NULL, error)) return NULL; return result; } static void modem_time_load_network_time (MMIfaceModemTime *self, GAsyncReadyCallback callback, gpointer user_data) { mm_base_modem_at_command (MM_BASE_MODEM (self), "+CCLK?", 3, FALSE, callback, user_data); } /*****************************************************************************/ /* Load network timezone (Time interface) */ static MMNetworkTimezone * modem_time_load_network_timezone_finish (MMIfaceModemTime *self, GAsyncResult *res, GError **error) { const gchar *response; MMNetworkTimezone *tz = NULL; response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, error); if (!response) return NULL; if (!mm_parse_cclk_response (response, NULL, &tz, error)) return NULL; return tz; } static void modem_time_load_network_timezone (MMIfaceModemTime *self, GAsyncReadyCallback callback, gpointer user_data) { mm_base_modem_at_command (MM_BASE_MODEM (self), "+CCLK?", 3, FALSE, callback, user_data); } /*****************************************************************************/ /* Check support (Time interface) */ static const MMBaseModemAtCommand time_check_sequence[] = { { "+CTZU=1", 3, TRUE, mm_base_modem_response_processor_no_result_continue }, { "+CCLK?", 3, TRUE, mm_base_modem_response_processor_string }, { NULL } }; static gboolean modem_time_check_support_finish (MMIfaceModemTime *self, GAsyncResult *res, GError **error) { return !!mm_base_modem_at_sequence_finish (MM_BASE_MODEM (self), res, NULL, error); } static void modem_time_check_support (MMIfaceModemTime *self, GAsyncReadyCallback callback, gpointer user_data) { mm_base_modem_at_sequence (MM_BASE_MODEM (self), time_check_sequence, NULL, /* response_processor_context */ NULL, /* response_processor_context_free */ callback, user_data); } /*****************************************************************************/ /* Check support (Signal interface) */ static gboolean modem_signal_check_support_finish (MMIfaceModemSignal *self, GAsyncResult *res, GError **error) { return !!mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, error); } static void modem_signal_check_support (MMIfaceModemSignal *self, GAsyncReadyCallback callback, gpointer user_data) { mm_base_modem_at_command (MM_BASE_MODEM (self), "+CESQ=?", 3, TRUE, callback, user_data); } /*****************************************************************************/ /* Load extended signal information (Signal interface) */ static gboolean modem_signal_load_values_finish (MMIfaceModemSignal *self, GAsyncResult *res, MMSignal **cdma, MMSignal **evdo, MMSignal **gsm, MMSignal **umts, MMSignal **lte, MMSignal **nr5g, GError **error) { const gchar *response; response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, error); if (!response || !mm_3gpp_cesq_response_to_signal_info (response, self, gsm, umts, lte, error)) return FALSE; if (cdma) *cdma = NULL; if (evdo) *evdo = NULL; if (nr5g) *nr5g = NULL; return TRUE; } static void modem_signal_load_values (MMIfaceModemSignal *self, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { mm_base_modem_at_command (MM_BASE_MODEM (self), "+CESQ", 3, FALSE, callback, user_data); } /*****************************************************************************/ /* Check support (3GPP profile management interface) */ static gboolean modem_3gpp_profile_manager_check_support_finish (MMIfaceModem3gppProfileManager *self, GAsyncResult *res, gchar **index_field, GError **error) { if (g_task_propagate_boolean (G_TASK (res), error)) { *index_field = g_strdup ("profile-id");; return TRUE; } return FALSE; } static void profile_manager_cgdcont_test_ready (MMBaseModem *self, GAsyncResult *res, GTask *task) { GError *error = NULL; mm_base_modem_at_command_finish (self, res, &error); if (error) g_task_return_error (task, error); else g_task_return_boolean (task, TRUE); g_object_unref (task); } static void modem_3gpp_profile_manager_check_support (MMIfaceModem3gppProfileManager *self, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; task = g_task_new (self, NULL, callback, user_data); if (!mm_iface_modem_is_3gpp (MM_IFACE_MODEM (self))) { g_task_return_boolean (task, FALSE); g_object_unref (task); return; } /* Query with CGDCONT=? */ mm_base_modem_at_command ( MM_BASE_MODEM (self), "+CGDCONT=?", 3, TRUE, /* allow caching, it's a test command */ (GAsyncReadyCallback)profile_manager_cgdcont_test_ready, task); } /*****************************************************************************/ /* List profiles (3GPP profile management interface) */ typedef struct { GList *profiles; } ListProfilesContext; static void list_profiles_context_free (ListProfilesContext *ctx) { mm_3gpp_profile_list_free (ctx->profiles); g_slice_free (ListProfilesContext, ctx); } static gboolean modem_3gpp_profile_manager_list_profiles_finish (MMIfaceModem3gppProfileManager *self, GAsyncResult *res, GList **out_profiles, GError **error) { ListProfilesContext *ctx; if (!g_task_propagate_boolean (G_TASK (res), error)) return FALSE; ctx = g_task_get_task_data (G_TASK (res)); if (out_profiles) *out_profiles = g_steal_pointer (&ctx->profiles); return TRUE; } static void profile_manager_cgdcont_query_ready (MMBaseModem *self, GAsyncResult *res, GTask *task) { ListProfilesContext *ctx; const gchar *response; GError *error = NULL; GList *pdp_context_list; response = mm_base_modem_at_command_finish (self, res, &error); if (!response) { g_task_return_error (task, error); g_object_unref (task); return; } /* may return NULL without error if response is empty */ pdp_context_list = mm_3gpp_parse_cgdcont_read_response (response, &error); if (error) { g_task_return_error (task, error); g_object_unref (task); return; } ctx = g_slice_new0 (ListProfilesContext); g_task_set_task_data (task, ctx, (GDestroyNotify) list_profiles_context_free); ctx->profiles = mm_3gpp_profile_list_new_from_pdp_context_list (pdp_context_list); mm_3gpp_pdp_context_list_free (pdp_context_list); g_task_return_boolean (task, TRUE); g_object_unref (task); } static void modem_3gpp_profile_manager_list_profiles (MMIfaceModem3gppProfileManager *self, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; task = g_task_new (self, NULL, callback, user_data); /* Query with CGDCONT? */ mm_base_modem_at_command ( MM_BASE_MODEM (self), "+CGDCONT?", 3, FALSE, (GAsyncReadyCallback)profile_manager_cgdcont_query_ready, task); } /*****************************************************************************/ /* Check format (3GPP profile management interface) */ typedef struct { MMBearerIpFamily ip_type; guint min_profile_id; guint max_profile_id; } CheckFormatContext; static void check_format_context_free (CheckFormatContext *ctx) { g_slice_free (CheckFormatContext, ctx); } static gboolean modem_3gpp_profile_manager_check_format_finish (MMIfaceModem3gppProfileManager *self, GAsyncResult *res, gboolean *new_id, gint *min_profile_id, gint *max_profile_id, GEqualFunc *apn_cmp, MM3gppProfileCmpFlags *profile_cmp_flags, GError **error) { CheckFormatContext *ctx; if (!g_task_propagate_boolean (G_TASK (res), error)) return FALSE; ctx = g_task_get_task_data (G_TASK (res)); if (new_id) *new_id = TRUE; if (min_profile_id) *min_profile_id = (gint) ctx->min_profile_id; if (max_profile_id) *max_profile_id = (gint) ctx->max_profile_id; if (apn_cmp) *apn_cmp = (GEqualFunc) mm_3gpp_cmp_apn_name; if (profile_cmp_flags) *profile_cmp_flags = (MM_3GPP_PROFILE_CMP_FLAGS_NO_AUTH | MM_3GPP_PROFILE_CMP_FLAGS_NO_APN_TYPE | MM_3GPP_PROFILE_CMP_FLAGS_NO_ACCESS_TYPE_PREFERENCE | MM_3GPP_PROFILE_CMP_FLAGS_NO_ROAMING_ALLOWANCE | MM_3GPP_PROFILE_CMP_FLAGS_NO_PROFILE_SOURCE); return TRUE; } static void check_format_cgdcont_test_ready (MMBaseModem *self, GAsyncResult *res, GTask *task) { CheckFormatContext *ctx; const gchar *response; GList *format_list = NULL; g_autofree gchar *ip_family_str = NULL; g_autoptr(GError) error = NULL; gboolean checked = FALSE; ctx = g_task_get_task_data (task); ip_family_str = mm_bearer_ip_family_build_string_from_mask (ctx->ip_type); response = mm_base_modem_at_command_full_finish (self, res, &error); if (!response) mm_obj_dbg (self, "failed checking context definition format: %s", error->message); else { format_list = mm_3gpp_parse_cgdcont_test_response (response, self, &error); if (error) mm_obj_dbg (self, "error parsing +CGDCONT test response: %s", error->message); else if (mm_3gpp_pdp_context_format_list_find_range (format_list, ctx->ip_type, &ctx->min_profile_id, &ctx->max_profile_id)) checked = TRUE; } if (!checked) { ctx->min_profile_id = 1; ctx->max_profile_id = G_MAXINT-1; mm_obj_dbg (self, "unknown +CGDCONT format details for PDP type '%s', using defaults: minimum %d, maximum %d", ip_family_str, ctx->min_profile_id, ctx->max_profile_id); } else mm_obj_dbg (self, "+CGDCONT format details for PDP type '%s': minimum %d, maximum %d", ip_family_str, ctx->min_profile_id, ctx->max_profile_id); mm_3gpp_pdp_context_format_list_free (format_list); g_task_return_boolean (task, TRUE); g_object_unref (task); } static void modem_3gpp_profile_manager_check_format (MMIfaceModem3gppProfileManager *self, MMBearerIpFamily ip_type, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; CheckFormatContext *ctx; task = g_task_new (self, NULL, callback, user_data); ctx = g_slice_new0 (CheckFormatContext); ctx->ip_type = ip_type; g_task_set_task_data (task, ctx, (GDestroyNotify)check_format_context_free); mm_base_modem_at_command (MM_BASE_MODEM (self), "+CGDCONT=?", 3, TRUE, /* cached */ (GAsyncReadyCallback)check_format_cgdcont_test_ready, task); } /*****************************************************************************/ /* Delete profile (3GPP profile management interface) */ static gboolean modem_3gpp_profile_manager_delete_profile_finish (MMIfaceModem3gppProfileManager *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void profile_manager_cgdcont_reset_ready (MMBaseModem *self, GAsyncResult *res, GTask *task) { GError *error = NULL; gint profile_id; profile_id = GPOINTER_TO_INT (g_task_get_task_data (task)); if (!mm_base_modem_at_command_finish (self, res, &error)) { mm_obj_dbg (self, "attempting to reset context with id '%d' failed: %s", profile_id, error->message); g_task_return_error (task, error); } else { mm_obj_dbg (self, "reseted context with profile id '%d'", profile_id); g_task_return_boolean (task, TRUE); } g_object_unref (task); } static void profile_manager_cgdel_set_ready (MMBaseModem *self, GAsyncResult *res, GTask *task) { g_autoptr(GError) error = NULL; g_autofree gchar *cmd = NULL; gint profile_id; profile_id = GPOINTER_TO_INT (g_task_get_task_data (task)); if (mm_base_modem_at_command_finish (self, res, &error)) { mm_obj_dbg (self, "deleted context with profile id '%d'", profile_id); g_task_return_boolean (task, TRUE); g_object_unref (task); return; } mm_obj_dbg (self, "attempting to delete context with id '%d' failed: %s", profile_id, error->message); /* From 3GPP TS 27.007 (v16.3.0): * A special form of the set command, +CGDCONT= causes the values for * context number to become undefined. */ cmd = g_strdup_printf ("+CGDCONT=%d", profile_id); mm_base_modem_at_command ( MM_BASE_MODEM (self), cmd, 3, FALSE, (GAsyncReadyCallback)profile_manager_cgdcont_reset_ready, task); } static void modem_3gpp_profile_manager_delete_profile (MMIfaceModem3gppProfileManager *self, MM3gppProfile *profile, const gchar *index_field, GAsyncReadyCallback callback, gpointer user_data) { g_autofree gchar *cmd = NULL; GTask *task; gint profile_id; g_assert (g_strcmp0 (index_field, "profile-id") == 0); task = g_task_new (self, NULL, callback, user_data); profile_id = mm_3gpp_profile_get_profile_id (profile); g_assert (profile_id != MM_3GPP_PROFILE_ID_UNKNOWN); g_task_set_task_data (task, GINT_TO_POINTER (profile_id), NULL); cmd = g_strdup_printf ("+CGDEL=%d", profile_id); mm_base_modem_at_command ( MM_BASE_MODEM (self), cmd, 3, FALSE, (GAsyncReadyCallback)profile_manager_cgdel_set_ready, task); } /*****************************************************************************/ /* Deactivate profile (3GPP profile management interface) */ static gboolean modem_3gpp_profile_manager_check_activated_profile_finish (MMIfaceModem3gppProfileManager *self, GAsyncResult *res, gboolean *out_activated, GError **error) { GError *inner_error = NULL; gboolean result; result = g_task_propagate_boolean (G_TASK (res), &inner_error); if (inner_error) { g_propagate_error (error, inner_error); return FALSE; } if (out_activated) *out_activated = result; return TRUE; } static void check_activated_profile_cgact_query_ready (MMBaseModem *self, GAsyncResult *res, GTask *task) { MM3gppProfile *profile; const gchar *response; GError *error = NULL; GList *pdp_context_active_list = NULL; GList *l; gint profile_id; gboolean activated = FALSE; response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error); if (response) pdp_context_active_list = mm_3gpp_parse_cgact_read_response (response, &error); if (error) { g_task_return_error (task, error); g_object_unref (task); return; } profile = g_task_get_task_data (task); profile_id = mm_3gpp_profile_get_profile_id (profile); for (l = pdp_context_active_list; l; l = g_list_next (l)) { MM3gppPdpContextActive *iter = l->data; if ((gint)iter->cid == profile_id) { activated = iter->active; break; } } mm_3gpp_pdp_context_active_list_free (pdp_context_active_list); if (!l) g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_NOT_FOUND, "Profile '%d' not found in CGACT? response", profile_id); else g_task_return_boolean (task, activated); g_object_unref (task); } static void modem_3gpp_profile_manager_check_activated_profile (MMIfaceModem3gppProfileManager *self, MM3gppProfile *profile, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; gint profile_id; task = g_task_new (self, NULL, callback, user_data); g_task_set_task_data (task, g_object_ref (profile), g_object_unref); profile_id = mm_3gpp_profile_get_profile_id (profile); mm_obj_dbg (self, "checking if profile with id '%d' is already activated...", profile_id); mm_base_modem_at_command ( MM_BASE_MODEM (self), "+CGACT?", 3, FALSE, (GAsyncReadyCallback)check_activated_profile_cgact_query_ready, task); } /*****************************************************************************/ /* Deactivate profile (3GPP profile management interface) */ static gboolean modem_3gpp_profile_manager_deactivate_profile_finish (MMIfaceModem3gppProfileManager *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void deactivate_profile_cgact_set_ready (MMBaseModem *self, GAsyncResult *res, GTask *task) { GError *error = NULL; if (!mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error)) g_task_return_error (task, error); else g_task_return_boolean (task, TRUE); g_object_unref (task); } static void modem_3gpp_profile_manager_deactivate_profile (MMIfaceModem3gppProfileManager *self, MM3gppProfile *profile, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; gint profile_id; g_autofree gchar *cmd = NULL; task = g_task_new (self, NULL, callback, user_data); profile_id = mm_3gpp_profile_get_profile_id (profile); mm_obj_dbg (self, "deactivating profile with id '%d'...", profile_id); cmd = g_strdup_printf ("+CGACT=0,%d", profile_id); mm_base_modem_at_command ( MM_BASE_MODEM (self), cmd, MM_BASE_BEARER_DEFAULT_DISCONNECTION_TIMEOUT, FALSE, (GAsyncReadyCallback)deactivate_profile_cgact_set_ready, task); } /*****************************************************************************/ /* Store profile (3GPP profile management interface) */ static gboolean modem_3gpp_profile_manager_store_profile_finish (MMIfaceModem3gppProfileManager *self, GAsyncResult *res, gint *out_profile_id, MMBearerApnType *out_apn_type, GError **error) { if (!g_task_propagate_boolean (G_TASK (res), error)) return FALSE; if (out_profile_id) *out_profile_id = GPOINTER_TO_INT (g_task_get_task_data (G_TASK (res))); if (out_apn_type) *out_apn_type = MM_BEARER_APN_TYPE_NONE; return TRUE; } static void store_profile_cgdcont_set_ready (MMBaseModem *self, GAsyncResult *res, GTask *task) { GError *error = NULL; if (!mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error)) g_task_return_error (task, error); else g_task_return_boolean (task, TRUE); g_object_unref (task); } static void modem_3gpp_profile_manager_store_profile (MMIfaceModem3gppProfileManager *self, MM3gppProfile *profile, const gchar *index_field, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; gint profile_id; MMBearerIpFamily ip_type; const gchar *apn; const gchar *pdp_type; g_autofree gchar *ip_type_str = NULL; g_autofree gchar *quoted_apn = NULL; g_autofree gchar *cmd = NULL; g_assert (g_strcmp0 (index_field, "profile-id") == 0); task = g_task_new (self, NULL, callback, user_data); profile_id = mm_3gpp_profile_get_profile_id (profile); g_assert (profile_id != MM_3GPP_PROFILE_ID_UNKNOWN); g_task_set_task_data (task, GINT_TO_POINTER (profile_id), NULL); ip_type = mm_3gpp_profile_get_ip_type (profile); g_assert (ip_type != MM_BEARER_IP_FAMILY_NONE); g_assert (ip_type != MM_BEARER_IP_FAMILY_ANY); ip_type_str = mm_bearer_ip_family_build_string_from_mask (ip_type); pdp_type = mm_3gpp_get_pdp_type_from_ip_family (ip_type); g_assert (pdp_type); apn = mm_3gpp_profile_get_apn (profile); quoted_apn = mm_port_serial_at_quote_string (apn); mm_obj_dbg (self, "storing profile '%d': apn '%s', ip type '%s'", profile_id, apn, ip_type_str); cmd = g_strdup_printf ("+CGDCONT=%d,\"%s\",%s", profile_id, pdp_type, quoted_apn); mm_base_modem_at_command (MM_BASE_MODEM (self), cmd, 3, FALSE, (GAsyncReadyCallback) store_profile_cgdcont_set_ready, task); } /*****************************************************************************/ static const gchar *primary_init_sequence[] = { /* Ensure echo is off */ "E0", /* Get word responses */ "V1", /* Extended numeric codes */ "+CMEE=1", /* Report all call status */ "X4", /* Assert DCD when carrier detected */ "&C1", NULL }; static const gchar *secondary_init_sequence[] = { /* Ensure echo is off */ "E0", NULL }; static void setup_ports (MMBroadbandModem *self) { MMPortSerialAt *ports[2]; g_autoptr(GRegex) ciev_regex = NULL; g_autoptr(GRegex) cmti_regex = NULL; g_autoptr(GRegex) cusd_regex = NULL; GPtrArray *array; guint i; guint j; ports[0] = mm_base_modem_peek_port_primary (MM_BASE_MODEM (self)); ports[1] = mm_base_modem_peek_port_secondary (MM_BASE_MODEM (self)); if (ports[0]) g_object_set (ports[0], MM_PORT_SERIAL_AT_INIT_SEQUENCE, primary_init_sequence, NULL); if (ports[1]) g_object_set (ports[1], MM_PORT_SERIAL_AT_INIT_SEQUENCE, secondary_init_sequence, NULL); /* Cleanup all unsolicited message handlers in all AT ports */ array = mm_3gpp_creg_regex_get (FALSE); ciev_regex = mm_3gpp_ciev_regex_get (); cmti_regex = mm_3gpp_cmti_regex_get (); cusd_regex = mm_3gpp_cusd_regex_get (); for (i = 0; i < 2; i++) { if (!ports[i]) continue; for (j = 0; j < array->len; j++) mm_port_serial_at_add_unsolicited_msg_handler (MM_PORT_SERIAL_AT (ports[i]), (GRegex *)g_ptr_array_index (array, j), NULL, NULL, NULL); mm_port_serial_at_add_unsolicited_msg_handler (MM_PORT_SERIAL_AT (ports[i]), ciev_regex, NULL, NULL, NULL); mm_port_serial_at_add_unsolicited_msg_handler (MM_PORT_SERIAL_AT (ports[i]), cmti_regex, NULL, NULL, NULL); mm_port_serial_at_add_unsolicited_msg_handler (MM_PORT_SERIAL_AT (ports[i]), cusd_regex, NULL, NULL, NULL); } mm_3gpp_creg_regex_destroy (array); } /*****************************************************************************/ /* Initialization started/stopped */ static gboolean initialization_stopped (MMBroadbandModem *self, gpointer user_data, GError **error) { PortsContext *ctx = (PortsContext *)user_data; if (ctx) ports_context_unref (ctx); return TRUE; } static gpointer initialization_started_finish (MMBroadbandModem *self, GAsyncResult *res, GError **error) { return g_task_propagate_pointer (G_TASK (res), error); } static void initialization_started (MMBroadbandModem *self, GAsyncReadyCallback callback, gpointer user_data) { GError *error = NULL; GTask *task; PortsContext *ctx; task = g_task_new (self, NULL, callback, user_data); /* Open ports for initialization, just the primary AT port */ ctx = ports_context_new (); if (!ports_context_open (self, ctx, FALSE, FALSE, FALSE, &error)) { ports_context_unref (ctx); g_prefix_error (&error, "Couldn't open ports during modem initialization: "); g_task_return_error (task, error); } else g_task_return_pointer (task, ctx, (GDestroyNotify)ports_context_unref); g_object_unref (task); } /*****************************************************************************/ /* Disabling stopped */ static gboolean disabling_stopped (MMBroadbandModem *self, GError **error) { if (self->priv->enabled_ports_ctx) { ports_context_unref (self->priv->enabled_ports_ctx); self->priv->enabled_ports_ctx = NULL; } return TRUE; } /*****************************************************************************/ /* Initializing the modem (during first enabling) */ static gboolean enabling_modem_init_finish (MMBroadbandModem *self, GAsyncResult *res, GError **error) { return !!mm_base_modem_at_command_full_finish (MM_BASE_MODEM (self), res, error); } static void enabling_modem_init (MMBroadbandModem *self, GAsyncReadyCallback callback, gpointer user_data) { /* Init command. ITU rec v.250 (6.1.1) says: * The DTE should not include additional commands on the same command line * after the Z command because such commands may be ignored. * So run ATZ alone. */ mm_base_modem_at_command_full (MM_BASE_MODEM (self), mm_base_modem_peek_port_primary (MM_BASE_MODEM (self)), "Z", 6, FALSE, FALSE, NULL, /* cancellable */ callback, user_data); } /*****************************************************************************/ /* Enabling started */ typedef struct { PortsContext *ports; gboolean modem_init_required; } EnablingStartedContext; static void enabling_started_context_free (EnablingStartedContext *ctx) { ports_context_unref (ctx->ports); g_slice_free (EnablingStartedContext, ctx); } static gboolean enabling_started_finish (MMBroadbandModem *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static gboolean enabling_after_modem_init_timeout (GTask *task) { MMBroadbandModem *self; EnablingStartedContext *ctx; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); /* Reset init sequence enabled flags and run them explicitly */ g_assert (ctx->modem_init_required); g_object_set (ctx->ports->primary, MM_PORT_SERIAL_AT_INIT_SEQUENCE_ENABLED, TRUE, NULL); mm_port_serial_at_run_init_sequence (ctx->ports->primary); if (ctx->ports->secondary) { g_object_set (ctx->ports->secondary, MM_PORT_SERIAL_AT_INIT_SEQUENCE_ENABLED, TRUE, NULL); mm_port_serial_at_run_init_sequence (ctx->ports->secondary); } /* Store enabled ports context and complete */ self->priv->enabled_ports_ctx = ports_context_ref (ctx->ports); g_task_return_boolean (task, TRUE); g_object_unref (task); return G_SOURCE_REMOVE; } static void enabling_modem_init_ready (MMBroadbandModem *self, GAsyncResult *res, GTask *task) { GError *error = NULL; if (!MM_BROADBAND_MODEM_GET_CLASS (self)->enabling_modem_init_finish (self, res, &error)) { g_task_return_error (task, error); g_object_unref (task); return; } /* Specify that the modem init was run once */ self->priv->modem_init_run = TRUE; /* After the modem init sequence, give a 500ms period for the modem to settle */ mm_obj_dbg (self, "giving some time to settle the modem..."); g_timeout_add (500, (GSourceFunc)enabling_after_modem_init_timeout, task); } static void enabling_flash_done (MMPortSerial *port, GAsyncResult *res, GTask *task) { MMBroadbandModem *self; EnablingStartedContext *ctx; GError *error = NULL; if (!mm_port_serial_flash_finish (port, res, &error)) { g_prefix_error (&error, "Primary port flashing failed: "); g_task_return_error (task, error); g_object_unref (task); return; } self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); if (ctx->modem_init_required) { mm_obj_dbg (self, "running initialization sequence..."); MM_BROADBAND_MODEM_GET_CLASS (self)->enabling_modem_init (self, (GAsyncReadyCallback)enabling_modem_init_ready, task); return; } /* Store enabled ports context and complete */ self->priv->enabled_ports_ctx = ports_context_ref (ctx->ports); g_task_return_boolean (task, TRUE); g_object_unref (task); } static void enabling_started (MMBroadbandModem *self, GAsyncReadyCallback callback, gpointer user_data) { GError *error = NULL; EnablingStartedContext *ctx; GTask *task; ctx = g_slice_new0 (EnablingStartedContext); ctx->ports = ports_context_new (); /* Skip modem initialization if the device was hotplugged OR if we already * did it (i.e. don't reinitialize if the modem got disabled and enabled * again) */ if (self->priv->modem_init_run) mm_obj_dbg (self, "skipping initialization: not first enabling"); else if (mm_base_modem_get_hotplugged (MM_BASE_MODEM (self))) { self->priv->modem_init_run = TRUE; mm_obj_dbg (self, "skipping initialization: device hotplugged"); } else if (!MM_BROADBAND_MODEM_GET_CLASS (self)->enabling_modem_init || !MM_BROADBAND_MODEM_GET_CLASS (self)->enabling_modem_init_finish) mm_obj_dbg (self, "skipping initialization: not required"); else ctx->modem_init_required = TRUE; task = g_task_new (self, NULL, callback, user_data); g_task_set_task_data (task, ctx, (GDestroyNotify)enabling_started_context_free); /* Open ports for enabling, including secondary AT port and QCDM if available */ if (!ports_context_open (self, ctx->ports, ctx->modem_init_required, TRUE, TRUE, &error)) { g_prefix_error (&error, "Couldn't open ports during modem enabling: "); g_task_return_error (task, error); g_object_unref (task); return; } /* Ports were correctly opened, now flash the primary port */ mm_obj_dbg (self, "flashing primary AT port before enabling..."); mm_port_serial_flash (MM_PORT_SERIAL (ctx->ports->primary), 100, FALSE, (GAsyncReadyCallback)enabling_flash_done, task); } /*****************************************************************************/ /* First registration checks */ static void modem_3gpp_run_registration_checks_ready (MMIfaceModem3gpp *self, GAsyncResult *res) { GError *error = NULL; if (!mm_iface_modem_3gpp_run_registration_checks_finish (self, res, &error)) { mm_obj_warn (self, "initial 3GPP registration check failed: %s", error->message); g_error_free (error); return; } mm_obj_dbg (self, "initial 3GPP registration checks finished"); } static void modem_cdma_run_registration_checks_ready (MMIfaceModemCdma *self, GAsyncResult *res) { GError *error = NULL; if (!mm_iface_modem_cdma_run_registration_checks_finish (self, res, &error)) { mm_obj_warn (self, "initial CDMA registration check failed: %s", error->message); g_error_free (error); return; } mm_obj_dbg (self, "initial CDMA registration checks finished"); } static gboolean schedule_initial_registration_checks_cb (MMBroadbandModem *self) { if (mm_iface_modem_is_3gpp (MM_IFACE_MODEM (self))) mm_iface_modem_3gpp_run_registration_checks (MM_IFACE_MODEM_3GPP (self), (GAsyncReadyCallback) modem_3gpp_run_registration_checks_ready, NULL); if (mm_iface_modem_is_cdma (MM_IFACE_MODEM (self))) mm_iface_modem_cdma_run_registration_checks (MM_IFACE_MODEM_CDMA (self), (GAsyncReadyCallback) modem_cdma_run_registration_checks_ready, NULL); /* We got a full reference, so balance it out here */ g_object_unref (self); return G_SOURCE_REMOVE; } static void schedule_initial_registration_checks (MMBroadbandModem *self) { g_idle_add ((GSourceFunc) schedule_initial_registration_checks_cb, g_object_ref (self)); } /*****************************************************************************/ typedef enum { /* When user requests a disable operation, the process starts here */ DISABLING_STEP_FIRST, DISABLING_STEP_IFACE_SIMPLE_ABORT_ONGOING, DISABLING_STEP_WAIT_FOR_FINAL_STATE, DISABLING_STEP_DISCONNECT_BEARERS, /* When the disabling is launched due to a failed enable, the process * starts here */ DISABLING_STEP_FIRST_AFTER_ENABLE_FAILED, DISABLING_STEP_IFACE_SIMPLE, DISABLING_STEP_IFACE_FIRMWARE, DISABLING_STEP_IFACE_VOICE, DISABLING_STEP_IFACE_SIGNAL, DISABLING_STEP_IFACE_OMA, DISABLING_STEP_IFACE_TIME, DISABLING_STEP_IFACE_MESSAGING, DISABLING_STEP_IFACE_LOCATION, DISABLING_STEP_IFACE_CDMA, DISABLING_STEP_IFACE_3GPP_USSD, DISABLING_STEP_IFACE_3GPP_PROFILE_MANAGER, DISABLING_STEP_IFACE_3GPP, DISABLING_STEP_IFACE_MODEM, DISABLING_STEP_LAST, } DisablingStep; typedef struct { MMBroadbandModem *self; gboolean state_updates; DisablingStep step; MMModemState previous_state; gboolean disabled; } DisablingContext; static void disabling_step (GTask *task); static void disabling_context_free (DisablingContext *ctx) { GError *error = NULL; if (MM_BROADBAND_MODEM_GET_CLASS (ctx->self)->disabling_stopped && !MM_BROADBAND_MODEM_GET_CLASS (ctx->self)->disabling_stopped (ctx->self, &error)) { mm_obj_warn (ctx->self, "error when stopping the disabling sequence: %s", error->message); g_error_free (error); } /* Only perform state updates if we're asked to do so */ if (ctx->state_updates) { if (ctx->disabled) mm_iface_modem_update_state (MM_IFACE_MODEM (ctx->self), MM_MODEM_STATE_DISABLED, MM_MODEM_STATE_CHANGE_REASON_USER_REQUESTED); else if (ctx->previous_state != MM_MODEM_STATE_DISABLED) { /* Fallback to previous state */ mm_iface_modem_update_state (MM_IFACE_MODEM (ctx->self), ctx->previous_state, MM_MODEM_STATE_CHANGE_REASON_UNKNOWN); } } g_object_unref (ctx->self); g_free (ctx); } static gboolean common_disable_finish (MMBroadbandModem *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } #undef INTERFACE_DISABLE_READY_FN #define INTERFACE_DISABLE_READY_FN(NAME,TYPE,WARN_ERRORS) \ static void \ NAME##_disable_ready (MMBroadbandModem *self, \ GAsyncResult *result, \ GTask *task) \ { \ DisablingContext *ctx; \ g_autoptr(GError) error = NULL; \ \ if (!mm_##NAME##_disable_finish (TYPE (self), result, &error)) { \ if (WARN_ERRORS) \ mm_obj_warn (self, "couldn't disable interface: %s", error->message); \ else \ mm_obj_dbg (self, "couldn't disable interface: %s", error->message); \ } \ \ /* Go on to next step */ \ ctx = g_task_get_task_data (task); \ ctx->step++; \ disabling_step (task); \ } INTERFACE_DISABLE_READY_FN (iface_modem, MM_IFACE_MODEM, TRUE) INTERFACE_DISABLE_READY_FN (iface_modem_3gpp, MM_IFACE_MODEM_3GPP, TRUE) INTERFACE_DISABLE_READY_FN (iface_modem_3gpp_ussd, MM_IFACE_MODEM_3GPP_USSD, FALSE) INTERFACE_DISABLE_READY_FN (iface_modem_3gpp_profile_manager, MM_IFACE_MODEM_3GPP_PROFILE_MANAGER, FALSE) INTERFACE_DISABLE_READY_FN (iface_modem_cdma, MM_IFACE_MODEM_CDMA, TRUE) INTERFACE_DISABLE_READY_FN (iface_modem_location, MM_IFACE_MODEM_LOCATION, FALSE) INTERFACE_DISABLE_READY_FN (iface_modem_messaging, MM_IFACE_MODEM_MESSAGING, FALSE) INTERFACE_DISABLE_READY_FN (iface_modem_voice, MM_IFACE_MODEM_VOICE, FALSE) INTERFACE_DISABLE_READY_FN (iface_modem_signal, MM_IFACE_MODEM_SIGNAL, FALSE) INTERFACE_DISABLE_READY_FN (iface_modem_time, MM_IFACE_MODEM_TIME, FALSE) INTERFACE_DISABLE_READY_FN (iface_modem_oma, MM_IFACE_MODEM_OMA, FALSE) static void bearer_list_disconnect_bearers_ready (MMBearerList *list, GAsyncResult *res, GTask *task) { DisablingContext *ctx; GError *error = NULL; if (!mm_bearer_list_disconnect_bearers_finish (list, res, &error)) { g_task_return_error (task, error); g_object_unref (task); return; } /* Go on to next step */ ctx = g_task_get_task_data (task); ctx->step++; disabling_step (task); } static void disabling_wait_for_final_state_ready (MMIfaceModem *self, GAsyncResult *res, GTask *task) { DisablingContext *ctx; GError *error = NULL; ctx = g_task_get_task_data (task); ctx->previous_state = mm_iface_modem_wait_for_final_state_finish (self, res, &error); if (error) { g_task_return_error (task, error); g_object_unref (task); return; } switch (ctx->previous_state) { case MM_MODEM_STATE_UNKNOWN: case MM_MODEM_STATE_FAILED: case MM_MODEM_STATE_LOCKED: case MM_MODEM_STATE_DISABLED: /* Just return success, don't relaunch disabling. * Note that we do consider here UNKNOWN and FAILED status on purpose, * as the MMManager will try to disable every modem before removing * it. */ g_task_return_boolean (task, TRUE); g_object_unref (task); return; case MM_MODEM_STATE_INITIALIZING: case MM_MODEM_STATE_DISABLING: case MM_MODEM_STATE_ENABLING: case MM_MODEM_STATE_ENABLED: case MM_MODEM_STATE_SEARCHING: case MM_MODEM_STATE_REGISTERED: case MM_MODEM_STATE_DISCONNECTING: case MM_MODEM_STATE_CONNECTING: case MM_MODEM_STATE_CONNECTED: default: break; } /* We're in a final state now, go on */ g_assert (ctx->state_updates); mm_iface_modem_update_state (MM_IFACE_MODEM (ctx->self), MM_MODEM_STATE_DISABLING, MM_MODEM_STATE_CHANGE_REASON_USER_REQUESTED); ctx->step++; disabling_step (task); } static void disabling_step (GTask *task) { DisablingContext *ctx; ctx = g_task_get_task_data (task); switch (ctx->step) { case DISABLING_STEP_FIRST: ctx->step++; /* fall through */ case DISABLING_STEP_IFACE_SIMPLE_ABORT_ONGOING: /* Connection requests via the Simple interface must be aborted as soon * as possible, because certain steps may be explicitly waiting for new * state transitions and such. */ mm_iface_modem_simple_abort_ongoing (MM_IFACE_MODEM_SIMPLE (ctx->self)); ctx->step++; /* fall through */ case DISABLING_STEP_WAIT_FOR_FINAL_STATE: /* cancellability allowed at this point */ if (g_task_return_error_if_cancelled (task)) { g_object_unref (task); return; } mm_iface_modem_wait_for_final_state (MM_IFACE_MODEM (ctx->self), MM_MODEM_STATE_UNKNOWN, /* just any */ (GAsyncReadyCallback)disabling_wait_for_final_state_ready, task); return; case DISABLING_STEP_DISCONNECT_BEARERS: /* cancellability allowed at this point */ if (g_task_return_error_if_cancelled (task)) { g_object_unref (task); return; } if (ctx->self->priv->modem_bearer_list) { mm_bearer_list_disconnect_bearers ( ctx->self->priv->modem_bearer_list, NULL, /* all bearers */ (GAsyncReadyCallback)bearer_list_disconnect_bearers_ready, task); return; } ctx->step++; /* fall through */ case DISABLING_STEP_FIRST_AFTER_ENABLE_FAILED: /* From this point onwards, the disabling sequence will NEVER fail, all * errors will be treated as non-fatal, including a possible task * cancellation. */ g_task_set_check_cancellable (task, FALSE); ctx->step++; /* fall through */ case DISABLING_STEP_IFACE_SIMPLE: ctx->step++; /* fall through */ case DISABLING_STEP_IFACE_FIRMWARE: ctx->step++; /* fall through */ case DISABLING_STEP_IFACE_VOICE: if (ctx->self->priv->modem_voice_dbus_skeleton) { mm_obj_dbg (ctx->self, "modem has voice capabilities, disabling the Voice interface..."); mm_iface_modem_voice_disable (MM_IFACE_MODEM_VOICE (ctx->self), (GAsyncReadyCallback)iface_modem_voice_disable_ready, task); return; } ctx->step++; /* fall through */ case DISABLING_STEP_IFACE_SIGNAL: if (ctx->self->priv->modem_signal_dbus_skeleton) { mm_obj_dbg (ctx->self, "modem has extended signal reporting capabilities, disabling the Signal interface..."); mm_iface_modem_signal_disable (MM_IFACE_MODEM_SIGNAL (ctx->self), (GAsyncReadyCallback)iface_modem_signal_disable_ready, task); return; } ctx->step++; /* fall through */ case DISABLING_STEP_IFACE_OMA: if (ctx->self->priv->modem_oma_dbus_skeleton) { mm_obj_dbg (ctx->self, "modem has OMA capabilities, disabling the OMA interface..."); mm_iface_modem_oma_disable (MM_IFACE_MODEM_OMA (ctx->self), (GAsyncReadyCallback)iface_modem_oma_disable_ready, task); return; } ctx->step++; /* fall through */ case DISABLING_STEP_IFACE_TIME: if (ctx->self->priv->modem_time_dbus_skeleton) { mm_obj_dbg (ctx->self, "modem has time capabilities, disabling the Time interface..."); mm_iface_modem_time_disable (MM_IFACE_MODEM_TIME (ctx->self), (GAsyncReadyCallback)iface_modem_time_disable_ready, task); return; } ctx->step++; /* fall through */ case DISABLING_STEP_IFACE_MESSAGING: if (ctx->self->priv->modem_messaging_dbus_skeleton) { mm_obj_dbg (ctx->self, "modem has messaging capabilities, disabling the Messaging interface..."); mm_iface_modem_messaging_disable (MM_IFACE_MODEM_MESSAGING (ctx->self), (GAsyncReadyCallback)iface_modem_messaging_disable_ready, task); return; } ctx->step++; /* fall through */ case DISABLING_STEP_IFACE_LOCATION: if (ctx->self->priv->modem_location_dbus_skeleton) { mm_obj_dbg (ctx->self, "modem has location capabilities, disabling the Location interface..."); mm_iface_modem_location_disable (MM_IFACE_MODEM_LOCATION (ctx->self), (GAsyncReadyCallback)iface_modem_location_disable_ready, task); return; } ctx->step++; /* fall through */ case DISABLING_STEP_IFACE_CDMA: if (ctx->self->priv->modem_cdma_dbus_skeleton) { mm_obj_dbg (ctx->self, "modem has CDMA capabilities, disabling the Modem CDMA interface..."); mm_iface_modem_cdma_disable (MM_IFACE_MODEM_CDMA (ctx->self), (GAsyncReadyCallback)iface_modem_cdma_disable_ready, task); return; } ctx->step++; /* fall through */ case DISABLING_STEP_IFACE_3GPP_USSD: if (ctx->self->priv->modem_3gpp_ussd_dbus_skeleton) { mm_obj_dbg (ctx->self, "modem has 3GPP/USSD capabilities, disabling the Modem 3GPP/USSD interface..."); mm_iface_modem_3gpp_ussd_disable (MM_IFACE_MODEM_3GPP_USSD (ctx->self), (GAsyncReadyCallback)iface_modem_3gpp_ussd_disable_ready, task); return; } ctx->step++; /* fall through */ case DISABLING_STEP_IFACE_3GPP_PROFILE_MANAGER: if (ctx->self->priv->modem_3gpp_profile_manager_dbus_skeleton) { mm_obj_dbg (ctx->self, "modem has 3GPP profile management capabilities, disabling the Modem 3GPP Profile Manager interface..."); mm_iface_modem_3gpp_profile_manager_disable (MM_IFACE_MODEM_3GPP_PROFILE_MANAGER (ctx->self), (GAsyncReadyCallback)iface_modem_3gpp_profile_manager_disable_ready, task); return; } ctx->step++; /* fall through */ case DISABLING_STEP_IFACE_3GPP: if (ctx->self->priv->modem_3gpp_dbus_skeleton) { mm_obj_dbg (ctx->self, "modem has 3GPP capabilities, disabling the Modem 3GPP interface..."); mm_iface_modem_3gpp_disable (MM_IFACE_MODEM_3GPP (ctx->self), (GAsyncReadyCallback)iface_modem_3gpp_disable_ready, task); return; } ctx->step++; /* fall through */ case DISABLING_STEP_IFACE_MODEM: /* This skeleton may be NULL when mm_base_modem_disable() gets called at * the same time as modem object disposal. */ if (ctx->self->priv->modem_dbus_skeleton) { mm_obj_dbg (ctx->self, "disabling the Modem interface..."); mm_iface_modem_disable (MM_IFACE_MODEM (ctx->self), (GAsyncReadyCallback)iface_modem_disable_ready, task); return; } ctx->step++; /* fall through */ case DISABLING_STEP_LAST: /* All disabled without errors! */ ctx->disabled = TRUE; g_task_return_boolean (task, TRUE); g_object_unref (task); return; default: break; } g_assert_not_reached (); } static void common_disable (MMBroadbandModem *self, gboolean state_updates, DisablingStep first_step, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { DisablingContext *ctx; GTask *task; ctx = g_new0 (DisablingContext, 1); ctx->self = g_object_ref (self); ctx->state_updates = state_updates; ctx->step = first_step; task = g_task_new (self, cancellable, callback, user_data); g_task_set_task_data (task, ctx, (GDestroyNotify)disabling_context_free); disabling_step (task); } /* Implicit disabling after failed enable */ static gboolean enable_failed_finish (MMBroadbandModem *self, GAsyncResult *res, GError **error) { /* The implicit disabling should never ever fail */ g_assert (common_disable_finish (self, res, NULL)); return TRUE; } static void enable_failed (MMBroadbandModem *self, GAsyncReadyCallback callback, gpointer user_data) { common_disable (self, FALSE, /* don't perform state updates */ DISABLING_STEP_FIRST_AFTER_ENABLE_FAILED, NULL, /* no cancellable */ callback, user_data); } /* User-requested disable operation */ static gboolean disable_finish (MMBaseModem *self, GAsyncResult *res, GError **error) { return common_disable_finish (MM_BROADBAND_MODEM (self), res, error); } static void disable (MMBaseModem *self, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { common_disable (MM_BROADBAND_MODEM (self), TRUE, /* perform state updates */ DISABLING_STEP_FIRST, cancellable, callback, user_data); } /*****************************************************************************/ typedef enum { ENABLING_STEP_FIRST, ENABLING_STEP_WAIT_FOR_FINAL_STATE, ENABLING_STEP_STARTED, ENABLING_STEP_IFACE_MODEM, ENABLING_STEP_IFACE_3GPP, ENABLING_STEP_IFACE_3GPP_PROFILE_MANAGER, ENABLING_STEP_IFACE_3GPP_USSD, ENABLING_STEP_IFACE_CDMA, ENABLING_STEP_IFACE_LOCATION, ENABLING_STEP_IFACE_MESSAGING, ENABLING_STEP_IFACE_TIME, ENABLING_STEP_IFACE_SIGNAL, ENABLING_STEP_IFACE_OMA, ENABLING_STEP_IFACE_VOICE, ENABLING_STEP_IFACE_FIRMWARE, ENABLING_STEP_IFACE_SIMPLE, ENABLING_STEP_LAST, } EnablingStep; typedef struct { MMBroadbandModem *self; EnablingStep step; MMModemState previous_state; gboolean enabled; GError *saved_error; } EnablingContext; static void enabling_step (GTask *task); static void enabling_context_free (EnablingContext *ctx) { g_assert (!ctx->saved_error); if (ctx->enabled) mm_iface_modem_update_state (MM_IFACE_MODEM (ctx->self), MM_MODEM_STATE_ENABLED, MM_MODEM_STATE_CHANGE_REASON_USER_REQUESTED); else if (ctx->previous_state != MM_MODEM_STATE_ENABLED) { /* Fallback to previous state */ mm_iface_modem_update_state (MM_IFACE_MODEM (ctx->self), ctx->previous_state, MM_MODEM_STATE_CHANGE_REASON_UNKNOWN); } g_object_unref (ctx->self); g_free (ctx); } static gboolean enable_finish (MMBaseModem *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void enable_failed_ready (MMBroadbandModem *self, GAsyncResult *res, GTask *task) { EnablingContext *ctx; ctx = g_task_get_task_data (task); /* The disabling run after a failed enable will never fail */ g_assert (enable_failed_finish (self, res, NULL)); g_assert (ctx->saved_error); g_task_return_error (task, g_steal_pointer (&ctx->saved_error)); g_object_unref (task); } #undef INTERFACE_ENABLE_READY_FN #define INTERFACE_ENABLE_READY_FN(NAME,TYPE,FATAL_ERRORS) \ static void \ NAME##_enable_ready (MMBroadbandModem *self, \ GAsyncResult *result, \ GTask *task) \ { \ EnablingContext *ctx; \ g_autoptr(GError) error = NULL; \ \ ctx = g_task_get_task_data (task); \ \ if (!mm_##NAME##_enable_finish (TYPE (self), result, &error)) { \ if (FATAL_ERRORS) { \ mm_obj_warn (self, "couldn't enable interface: '%s'", error->message); \ g_assert (!ctx->saved_error); \ ctx->saved_error = g_steal_pointer (&error); \ mm_obj_dbg (self, "running implicit disable after failed enable..."); \ enable_failed (self, (GAsyncReadyCallback) enable_failed_ready, task); \ return; \ } \ \ mm_obj_dbg (self, "couldn't enable interface: '%s'", error->message); \ } \ \ /* Go on to next step */ \ ctx->step++; \ enabling_step (task); \ } INTERFACE_ENABLE_READY_FN (iface_modem, MM_IFACE_MODEM, TRUE) INTERFACE_ENABLE_READY_FN (iface_modem_3gpp, MM_IFACE_MODEM_3GPP, TRUE) INTERFACE_ENABLE_READY_FN (iface_modem_3gpp_profile_manager, MM_IFACE_MODEM_3GPP_PROFILE_MANAGER, FALSE) INTERFACE_ENABLE_READY_FN (iface_modem_3gpp_ussd, MM_IFACE_MODEM_3GPP_USSD, FALSE) INTERFACE_ENABLE_READY_FN (iface_modem_cdma, MM_IFACE_MODEM_CDMA, TRUE) INTERFACE_ENABLE_READY_FN (iface_modem_location, MM_IFACE_MODEM_LOCATION, FALSE) INTERFACE_ENABLE_READY_FN (iface_modem_messaging, MM_IFACE_MODEM_MESSAGING, FALSE) INTERFACE_ENABLE_READY_FN (iface_modem_voice, MM_IFACE_MODEM_VOICE, FALSE) INTERFACE_ENABLE_READY_FN (iface_modem_signal, MM_IFACE_MODEM_SIGNAL, FALSE) INTERFACE_ENABLE_READY_FN (iface_modem_time, MM_IFACE_MODEM_TIME, FALSE) INTERFACE_ENABLE_READY_FN (iface_modem_oma, MM_IFACE_MODEM_OMA, FALSE) static void enabling_started_ready (MMBroadbandModem *self, GAsyncResult *result, GTask *task) { EnablingContext *ctx; GError *error = NULL; if (!MM_BROADBAND_MODEM_GET_CLASS (self)->enabling_started_finish (self, result, &error)) { g_task_return_error (task, error); g_object_unref (task); return; } /* Go on to next step */ ctx = g_task_get_task_data (task); ctx->step++; enabling_step (task); } static void enabling_wait_for_final_state_ready (MMIfaceModem *self, GAsyncResult *res, GTask *task) { EnablingContext *ctx; GError *error = NULL; ctx = g_task_get_task_data (task); ctx->previous_state = mm_iface_modem_wait_for_final_state_finish (self, res, &error); if (error) { g_task_return_error (task, error); g_object_unref (task); return; } if (ctx->previous_state >= MM_MODEM_STATE_ENABLED) { /* Just return success, don't relaunch enabling */ g_task_return_boolean (task, TRUE); g_object_unref (task); return; } /* We're in a final state now, go on */ mm_iface_modem_update_state (MM_IFACE_MODEM (ctx->self), MM_MODEM_STATE_ENABLING, MM_MODEM_STATE_CHANGE_REASON_USER_REQUESTED); ctx->step++; enabling_step (task); } static void enabling_step (GTask *task) { EnablingContext *ctx; /* Don't run new steps if we're cancelled */ if (g_task_return_error_if_cancelled (task)) { g_object_unref (task); return; } ctx = g_task_get_task_data (task); switch (ctx->step) { case ENABLING_STEP_FIRST: ctx->step++; /* fall through */ case ENABLING_STEP_WAIT_FOR_FINAL_STATE: mm_iface_modem_wait_for_final_state (MM_IFACE_MODEM (ctx->self), MM_MODEM_STATE_UNKNOWN, /* just any */ (GAsyncReadyCallback)enabling_wait_for_final_state_ready, task); return; case ENABLING_STEP_STARTED: if (MM_BROADBAND_MODEM_GET_CLASS (ctx->self)->enabling_started && MM_BROADBAND_MODEM_GET_CLASS (ctx->self)->enabling_started_finish) { MM_BROADBAND_MODEM_GET_CLASS (ctx->self)->enabling_started (ctx->self, (GAsyncReadyCallback)enabling_started_ready, task); return; } ctx->step++; /* fall through */ case ENABLING_STEP_IFACE_MODEM: /* From now on, the failure to enable one of the mandatory interfaces * will trigger the implicit disabling process */ g_assert (ctx->self->priv->modem_dbus_skeleton != NULL); /* Enabling the Modem interface */ mm_iface_modem_enable (MM_IFACE_MODEM (ctx->self), g_task_get_cancellable (task), (GAsyncReadyCallback)iface_modem_enable_ready, task); return; case ENABLING_STEP_IFACE_3GPP: if (ctx->self->priv->modem_3gpp_dbus_skeleton) { mm_obj_dbg (ctx->self, "modem has 3GPP capabilities, enabling the Modem 3GPP interface..."); /* Enabling the Modem 3GPP interface */ mm_iface_modem_3gpp_enable (MM_IFACE_MODEM_3GPP (ctx->self), g_task_get_cancellable (task), (GAsyncReadyCallback)iface_modem_3gpp_enable_ready, task); return; } ctx->step++; /* fall through */ case ENABLING_STEP_IFACE_3GPP_PROFILE_MANAGER: if (ctx->self->priv->modem_3gpp_profile_manager_dbus_skeleton) { mm_obj_dbg (ctx->self, "modem has 3GPP profile management capabilities, enabling the Modem 3GPP Profile Manager interface..."); mm_iface_modem_3gpp_profile_manager_enable (MM_IFACE_MODEM_3GPP_PROFILE_MANAGER (ctx->self), (GAsyncReadyCallback)iface_modem_3gpp_profile_manager_enable_ready, task); return; } ctx->step++; /* fall through */ case ENABLING_STEP_IFACE_3GPP_USSD: if (ctx->self->priv->modem_3gpp_ussd_dbus_skeleton) { mm_obj_dbg (ctx->self, "modem has 3GPP/USSD capabilities, enabling the Modem 3GPP/USSD interface..."); mm_iface_modem_3gpp_ussd_enable (MM_IFACE_MODEM_3GPP_USSD (ctx->self), (GAsyncReadyCallback)iface_modem_3gpp_ussd_enable_ready, task); return; } ctx->step++; /* fall through */ case ENABLING_STEP_IFACE_CDMA: if (ctx->self->priv->modem_cdma_dbus_skeleton) { mm_obj_dbg (ctx->self, "modem has CDMA capabilities, enabling the Modem CDMA interface..."); /* Enabling the Modem CDMA interface */ mm_iface_modem_cdma_enable (MM_IFACE_MODEM_CDMA (ctx->self), g_task_get_cancellable (task), (GAsyncReadyCallback)iface_modem_cdma_enable_ready, task); return; } ctx->step++; /* fall through */ case ENABLING_STEP_IFACE_LOCATION: if (ctx->self->priv->modem_location_dbus_skeleton) { mm_obj_dbg (ctx->self, "modem has location capabilities, enabling the Location interface..."); /* Enabling the Modem Location interface */ mm_iface_modem_location_enable (MM_IFACE_MODEM_LOCATION (ctx->self), g_task_get_cancellable (task), (GAsyncReadyCallback)iface_modem_location_enable_ready, task); return; } ctx->step++; /* fall through */ case ENABLING_STEP_IFACE_MESSAGING: if (ctx->self->priv->modem_messaging_dbus_skeleton) { mm_obj_dbg (ctx->self, "modem has messaging capabilities, enabling the Messaging interface..."); /* Enabling the Modem Messaging interface */ mm_iface_modem_messaging_enable (MM_IFACE_MODEM_MESSAGING (ctx->self), g_task_get_cancellable (task), (GAsyncReadyCallback)iface_modem_messaging_enable_ready, task); return; } ctx->step++; /* fall through */ case ENABLING_STEP_IFACE_TIME: if (ctx->self->priv->modem_time_dbus_skeleton) { mm_obj_dbg (ctx->self, "modem has time capabilities, enabling the Time interface..."); /* Enabling the Modem Time interface */ mm_iface_modem_time_enable (MM_IFACE_MODEM_TIME (ctx->self), g_task_get_cancellable (task), (GAsyncReadyCallback)iface_modem_time_enable_ready, task); return; } ctx->step++; /* fall through */ case ENABLING_STEP_IFACE_SIGNAL: if (ctx->self->priv->modem_signal_dbus_skeleton) { mm_obj_dbg (ctx->self, "modem has extended signal reporting capabilities, enabling the Signal interface..."); /* Enabling the Modem Signal interface */ mm_iface_modem_signal_enable (MM_IFACE_MODEM_SIGNAL (ctx->self), g_task_get_cancellable (task), (GAsyncReadyCallback)iface_modem_signal_enable_ready, task); return; } ctx->step++; /* fall through */ case ENABLING_STEP_IFACE_OMA: if (ctx->self->priv->modem_oma_dbus_skeleton) { mm_obj_dbg (ctx->self, "modem has OMA capabilities, enabling the OMA interface..."); /* Enabling the Modem Oma interface */ mm_iface_modem_oma_enable (MM_IFACE_MODEM_OMA (ctx->self), g_task_get_cancellable (task), (GAsyncReadyCallback)iface_modem_oma_enable_ready, task); return; } ctx->step++; /* fall through */ case ENABLING_STEP_IFACE_VOICE: if (ctx->self->priv->modem_voice_dbus_skeleton) { mm_obj_dbg (ctx->self, "modem has voice capabilities, enabling the Voice interface..."); /* Enabling the Modem Voice interface */ mm_iface_modem_voice_enable (MM_IFACE_MODEM_VOICE (ctx->self), g_task_get_cancellable (task), (GAsyncReadyCallback)iface_modem_voice_enable_ready, task); return; } ctx->step++; /* fall through */ case ENABLING_STEP_IFACE_FIRMWARE: ctx->step++; /* fall through */ case ENABLING_STEP_IFACE_SIMPLE: ctx->step++; /* fall through */ case ENABLING_STEP_LAST: ctx->enabled = TRUE; /* Once all interfaces have been enabled, trigger registration checks in * 3GPP and CDMA modems. We have to do this at this point so that e.g. * location interface gets proper registration related info reported. * * We do this in an idle so that we don't mess up the logs at this point * with the new requests being triggered. */ schedule_initial_registration_checks (ctx->self); /* All enabled without errors! */ g_task_return_boolean (task, TRUE); g_object_unref (task); return; default: break; } g_assert_not_reached (); } static void enable (MMBaseModem *self, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; task = g_task_new (self, cancellable, callback, user_data); /* Check state before launching modem enabling */ switch (MM_BROADBAND_MODEM (self)->priv->modem_state) { case MM_MODEM_STATE_UNKNOWN: /* We should never have a UNKNOWN->ENABLED transition */ g_assert_not_reached (); break; case MM_MODEM_STATE_FAILED: g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_WRONG_STATE, "Cannot enable modem: initialization failed"); break; case MM_MODEM_STATE_LOCKED: g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_WRONG_STATE, "Cannot enable modem: device locked"); break; case MM_MODEM_STATE_INITIALIZING: case MM_MODEM_STATE_DISABLED: case MM_MODEM_STATE_DISABLING: { EnablingContext *ctx; ctx = g_new0 (EnablingContext, 1); ctx->self = MM_BROADBAND_MODEM (g_object_ref (self)); ctx->step = ENABLING_STEP_FIRST; g_task_set_task_data (task, ctx, (GDestroyNotify)enabling_context_free); enabling_step (task); return; } case MM_MODEM_STATE_ENABLING: g_assert_not_reached (); break; case MM_MODEM_STATE_ENABLED: case MM_MODEM_STATE_SEARCHING: case MM_MODEM_STATE_REGISTERED: case MM_MODEM_STATE_DISCONNECTING: case MM_MODEM_STATE_CONNECTING: case MM_MODEM_STATE_CONNECTED: /* Just return success, don't relaunch enabling */ g_task_return_boolean (task, TRUE); break; default: g_assert_not_reached (); } g_object_unref (task); } /*****************************************************************************/ #if defined WITH_SUSPEND_RESUME typedef enum { SYNCING_STEP_FIRST, SYNCING_STEP_NOTIFY, SYNCING_STEP_IFACE_MODEM, SYNCING_STEP_IFACE_3GPP, SYNCING_STEP_IFACE_TIME, SYNCING_STEP_LAST, } SyncingStep; typedef struct { SyncingStep step; } SyncingContext; static void syncing_step (GTask *task); static gboolean synchronize_finish (MMBaseModem *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void iface_modem_time_sync_ready (MMIfaceModemTime *self, GAsyncResult *res, GTask *task) { SyncingContext *ctx; g_autoptr(GError) error = NULL; ctx = g_task_get_task_data (task); if (!mm_iface_modem_time_sync_finish (self, res, &error)) mm_obj_warn (self, "time interface synchronization failed: %s", error->message); /* Go on to next step */ ctx->step++; syncing_step (task); } static void iface_modem_3gpp_sync_ready (MMIfaceModem3gpp *self, GAsyncResult *res, GTask *task) { SyncingContext *ctx; g_autoptr(GError) error = NULL; ctx = g_task_get_task_data (task); if (!mm_iface_modem_3gpp_sync_finish (self, res, &error)) mm_obj_warn (self, "3GPP interface synchronization failed: %s", error->message); /* Go on to next step */ ctx->step++; syncing_step (task); } static void iface_modem_sync_ready (MMIfaceModem *self, GAsyncResult *res, GTask *task) { SyncingContext *ctx; MMModemLock lock; g_autoptr(GError) error = NULL; ctx = g_task_get_task_data (task); if (!mm_iface_modem_sync_finish (self, res, &error)) mm_obj_warn (self, "modem interface synchronization failed: %s", error->message); /* The synchronization logic only runs on modems that were enabled before * the suspend/resume cycle, and therefore we should not get SIM-PIN locked * at this point, unless the SIM was swapped. */ lock = mm_iface_modem_get_unlock_required (self); if (lock == MM_MODEM_LOCK_UNKNOWN || lock == MM_MODEM_LOCK_SIM_PIN || lock == MM_MODEM_LOCK_SIM_PUK) { /* Abort the sync() operation right away, and report a new SIM event that will * disable the modem and trigger a full reprobe */ mm_obj_warn (self, "SIM is locked... synchronization aborted"); g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_ABORTED, "Locked SIM found during modem interface synchronization"); g_object_unref (task); return; } /* Not locked, go on to next step */ mm_obj_dbg (self, "modem unlocked, continue synchronization"); ctx->step++; syncing_step (task); return; } static void syncing_step (GTask *task) { MMBroadbandModem *self; SyncingContext *ctx; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); switch (ctx->step) { case SYNCING_STEP_FIRST: ctx->step++; /* fall through */ case SYNCING_STEP_NOTIFY: g_signal_emit (self, signals[SIGNAL_SYNC_NEEDED], 0); ctx->step++; /* fall through */ case SYNCING_STEP_IFACE_MODEM: /* * Start interface Modem synchronization. * We want to make sure that the SIM is unlocked and not swapped before * synchronizing other interfaces. */ if (!self->priv->modem_dbus_skeleton) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_ABORTED, "Synchronization aborted: no modem exposed in DBus"); g_object_unref (task); return; } mm_obj_msg (self, "resume synchronization state (%d/%d): modem interface sync", ctx->step, SYNCING_STEP_LAST); mm_iface_modem_sync (MM_IFACE_MODEM (self), (GAsyncReadyCallback)iface_modem_sync_ready, task); return; case SYNCING_STEP_IFACE_3GPP: /* Start 3GPP interface synchronization, only if modem was enabled. * We hardly depend on the registration and bearer status, * therefore we cannot continue with the other steps until * this one is finished. */ if (self->priv->modem_3gpp_dbus_skeleton && (self->priv->modem_state >= MM_MODEM_STATE_ENABLED)) { mm_obj_msg (self, "resume synchronization state (%d/%d): 3GPP interface sync", ctx->step, SYNCING_STEP_LAST); mm_iface_modem_3gpp_sync (MM_IFACE_MODEM_3GPP (self), (GAsyncReadyCallback)iface_modem_3gpp_sync_ready, task); return; } ctx->step++; /* fall through */ case SYNCING_STEP_IFACE_TIME: /* Start Time interface synchronization, only if modem was enabled */ if (self->priv->modem_time_dbus_skeleton && (self->priv->modem_state >= MM_MODEM_STATE_ENABLED)) { mm_obj_msg (self, "resume synchronization state (%d/%d): time interface sync", ctx->step, SYNCING_STEP_LAST); mm_iface_modem_time_sync (MM_IFACE_MODEM_TIME (self), (GAsyncReadyCallback)iface_modem_time_sync_ready, task); return; } ctx->step++; /* fall through */ case SYNCING_STEP_LAST: mm_obj_msg (self, "resume synchronization state (%d/%d): all done", ctx->step, SYNCING_STEP_LAST); /* We are done without errors! */ g_task_return_boolean (task, TRUE); g_object_unref (task); return; default: break; } g_assert_not_reached (); } /* 'sync' as function name conflicts with a declared function in unistd.h */ static void synchronize (MMBaseModem *self, GAsyncReadyCallback callback, gpointer user_data) { SyncingContext *ctx; GTask *task; task = g_task_new (MM_BROADBAND_MODEM (self), NULL, callback, user_data); /* Create SyncingContext */ ctx = g_new0 (SyncingContext, 1); ctx->step = SYNCING_STEP_FIRST; g_task_set_task_data (task, ctx, (GDestroyNotify)g_free); syncing_step (task); } #endif /*****************************************************************************/ typedef enum { INITIALIZE_STEP_FIRST, INITIALIZE_STEP_SETUP_PORTS, INITIALIZE_STEP_STARTED, INITIALIZE_STEP_SETUP_SIMPLE_STATUS, INITIALIZE_STEP_IFACE_MODEM, INITIALIZE_STEP_IFACE_3GPP, INITIALIZE_STEP_JUMP_TO_LIMITED, INITIALIZE_STEP_IFACE_3GPP_PROFILE_MANAGER, INITIALIZE_STEP_IFACE_3GPP_USSD, INITIALIZE_STEP_IFACE_CDMA, INITIALIZE_STEP_IFACE_MESSAGING, INITIALIZE_STEP_IFACE_TIME, INITIALIZE_STEP_IFACE_SIGNAL, INITIALIZE_STEP_IFACE_OMA, INITIALIZE_STEP_IFACE_SAR, INITIALIZE_STEP_FALLBACK_LIMITED, INITIALIZE_STEP_IFACE_LOCATION, INITIALIZE_STEP_IFACE_VOICE, INITIALIZE_STEP_IFACE_FIRMWARE, INITIALIZE_STEP_IFACE_SIMPLE, INITIALIZE_STEP_LAST, } InitializeStep; typedef struct { MMBroadbandModem *self; InitializeStep step; gpointer ports_ctx; } InitializeContext; static void initialize_step (GTask *task); static void initialize_context_free (InitializeContext *ctx) { GError *error = NULL; if (ctx->ports_ctx && MM_BROADBAND_MODEM_GET_CLASS (ctx->self)->initialization_stopped && !MM_BROADBAND_MODEM_GET_CLASS (ctx->self)->initialization_stopped (ctx->self, ctx->ports_ctx, &error)) { mm_obj_warn (ctx->self, "error when stopping the initialization sequence: %s", error->message); g_error_free (error); } g_object_unref (ctx->self); g_free (ctx); } static gboolean initialize_finish (MMBaseModem *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void initialization_started_ready (MMBroadbandModem *self, GAsyncResult *result, GTask *task) { InitializeContext *ctx; GError *error = NULL; gpointer ports_ctx; ctx = g_task_get_task_data (task); /* May return NULL without error */ ports_ctx = MM_BROADBAND_MODEM_GET_CLASS (self)->initialization_started_finish (self, result, &error); if (error) { mm_obj_warn (self, "couldn't start initialization: %s", error->message); g_error_free (error); /* There is no Modem interface yet, so just update the variable directly */ ctx->self->priv->modem_state = MM_MODEM_STATE_FAILED; /* Just jump to the last step */ ctx->step = INITIALIZE_STEP_LAST; initialize_step (task); return; } /* Keep the ctx for later use when stopping initialization */ ctx->ports_ctx = ports_ctx; /* Go on to next step */ ctx->step++; initialize_step (task); } static void iface_modem_initialize_ready (MMBroadbandModem *self, GAsyncResult *result, GTask *task) { InitializeContext *ctx; GError *error = NULL; ctx = g_task_get_task_data (task); /* If the modem interface fails to get initialized, we will move the modem * to a FAILED state. Note that in this case we still export the interface. */ if (!mm_iface_modem_initialize_finish (MM_IFACE_MODEM (self), result, &error)) { MMModemStateFailedReason failed_reason = MM_MODEM_STATE_FAILED_REASON_UNKNOWN; /* Report the new FAILED state */ mm_obj_warn (self, "modem couldn't be initialized: %s", error->message); if (g_error_matches (error, MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_SIM_NOT_INSERTED)) failed_reason = MM_MODEM_STATE_FAILED_REASON_SIM_MISSING; else if (g_error_matches (error, MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_SIM_FAILURE) || g_error_matches (error, MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_SIM_WRONG)) failed_reason = MM_MODEM_STATE_FAILED_REASON_SIM_MISSING; else if (g_error_matches (error, MM_CORE_ERROR, MM_CORE_ERROR_WRONG_SIM_STATE)) failed_reason = MM_MODEM_STATE_FAILED_REASON_ESIM_WITHOUT_PROFILES; else if (g_error_matches (error, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED)) failed_reason = MM_MODEM_STATE_FAILED_REASON_UNKNOWN_CAPABILITIES; g_error_free (error); mm_iface_modem_update_failed_state (MM_IFACE_MODEM (self), failed_reason); } else { /* bind simple properties */ mm_iface_modem_bind_simple_status (MM_IFACE_MODEM (self), self->priv->modem_simple_status); } /* Go on to next step */ ctx->step++; initialize_step (task); } #undef INTERFACE_INIT_READY_FN #define INTERFACE_INIT_READY_FN(NAME,TYPE,FATAL_ERRORS) \ static void \ NAME##_initialize_ready (MMBroadbandModem *self, \ GAsyncResult *result, \ GTask *task) \ { \ InitializeContext *ctx; \ GError *error = NULL; \ \ ctx = g_task_get_task_data (task); \ \ if (!mm_##NAME##_initialize_finish (TYPE (self), result, &error)) { \ if (FATAL_ERRORS) { \ mm_obj_warn (self, "couldn't initialize interface: '%s'", \ error->message); \ g_error_free (error); \ \ /* Report the new FAILED state */ \ mm_iface_modem_update_failed_state (MM_IFACE_MODEM (self), \ MM_MODEM_STATE_FAILED_REASON_UNKNOWN); \ \ /* Just jump to the last step */ \ ctx->step = INITIALIZE_STEP_LAST; \ initialize_step (task); \ return; \ } \ \ mm_obj_dbg (self, "couldn't initialize interface: '%s'", \ error->message); \ /* Just shutdown this interface */ \ mm_##NAME##_shutdown (TYPE (self)); \ g_error_free (error); \ } else { \ /* bind simple properties */ \ mm_##NAME##_bind_simple_status (TYPE (self), self->priv->modem_simple_status); \ } \ \ /* Go on to next step */ \ ctx->step++; \ initialize_step (task); \ } INTERFACE_INIT_READY_FN (iface_modem_3gpp, MM_IFACE_MODEM_3GPP, TRUE) INTERFACE_INIT_READY_FN (iface_modem_3gpp_profile_manager, MM_IFACE_MODEM_3GPP_PROFILE_MANAGER, FALSE) INTERFACE_INIT_READY_FN (iface_modem_3gpp_ussd, MM_IFACE_MODEM_3GPP_USSD, FALSE) INTERFACE_INIT_READY_FN (iface_modem_cdma, MM_IFACE_MODEM_CDMA, TRUE) INTERFACE_INIT_READY_FN (iface_modem_location, MM_IFACE_MODEM_LOCATION, FALSE) INTERFACE_INIT_READY_FN (iface_modem_messaging, MM_IFACE_MODEM_MESSAGING, FALSE) INTERFACE_INIT_READY_FN (iface_modem_voice, MM_IFACE_MODEM_VOICE, FALSE) INTERFACE_INIT_READY_FN (iface_modem_time, MM_IFACE_MODEM_TIME, FALSE) INTERFACE_INIT_READY_FN (iface_modem_signal, MM_IFACE_MODEM_SIGNAL, FALSE) INTERFACE_INIT_READY_FN (iface_modem_oma, MM_IFACE_MODEM_OMA, FALSE) INTERFACE_INIT_READY_FN (iface_modem_firmware, MM_IFACE_MODEM_FIRMWARE, FALSE) INTERFACE_INIT_READY_FN (iface_modem_sar, MM_IFACE_MODEM_SAR, FALSE) static void initialize_step (GTask *task) { InitializeContext *ctx; /* Don't run new steps if we're cancelled */ if (g_task_return_error_if_cancelled (task)) { g_object_unref (task); return; } ctx = g_task_get_task_data (task); switch (ctx->step) { case INITIALIZE_STEP_FIRST: ctx->step++; /* fall through */ case INITIALIZE_STEP_SETUP_PORTS: if (MM_BROADBAND_MODEM_GET_CLASS (ctx->self)->setup_ports) MM_BROADBAND_MODEM_GET_CLASS (ctx->self)->setup_ports (ctx->self); ctx->step++; /* fall through */ case INITIALIZE_STEP_STARTED: if (MM_BROADBAND_MODEM_GET_CLASS (ctx->self)->initialization_started && MM_BROADBAND_MODEM_GET_CLASS (ctx->self)->initialization_started_finish) { MM_BROADBAND_MODEM_GET_CLASS (ctx->self)->initialization_started (ctx->self, (GAsyncReadyCallback)initialization_started_ready, task); return; } ctx->step++; /* fall through */ case INITIALIZE_STEP_SETUP_SIMPLE_STATUS: /* Simple status must be created before any interface initialization, * so that interfaces add and bind the properties they want to export. */ if (!ctx->self->priv->modem_simple_status) ctx->self->priv->modem_simple_status = mm_simple_status_new (); ctx->step++; /* fall through */ case INITIALIZE_STEP_IFACE_MODEM: /* Initialize the Modem interface */ mm_iface_modem_initialize (MM_IFACE_MODEM (ctx->self), g_task_get_cancellable (task), (GAsyncReadyCallback)iface_modem_initialize_ready, task); return; case INITIALIZE_STEP_IFACE_3GPP: if (mm_iface_modem_is_3gpp (MM_IFACE_MODEM (ctx->self))) { /* Initialize the 3GPP interface */ mm_iface_modem_3gpp_initialize (MM_IFACE_MODEM_3GPP (ctx->self), g_task_get_cancellable (task), (GAsyncReadyCallback)iface_modem_3gpp_initialize_ready, task); return; } ctx->step++; /* fall through */ case INITIALIZE_STEP_JUMP_TO_LIMITED: if (ctx->self->priv->modem_state == MM_MODEM_STATE_LOCKED || ctx->self->priv->modem_state == MM_MODEM_STATE_FAILED) { /* Jump to the fallback step when locked or failed, we will allow some additional * interfaces even in locked or failed state. */ ctx->step = INITIALIZE_STEP_FALLBACK_LIMITED; initialize_step (task); return; } ctx->step++; /* fall through */ case INITIALIZE_STEP_IFACE_3GPP_PROFILE_MANAGER: if (mm_iface_modem_is_3gpp (MM_IFACE_MODEM (ctx->self))) { /* Initialize the 3GPP Profile Manager interface */ mm_iface_modem_3gpp_profile_manager_initialize (MM_IFACE_MODEM_3GPP_PROFILE_MANAGER (ctx->self), (GAsyncReadyCallback)iface_modem_3gpp_profile_manager_initialize_ready, task); return; } ctx->step++; /* fall through */ case INITIALIZE_STEP_IFACE_3GPP_USSD: if (mm_iface_modem_is_3gpp (MM_IFACE_MODEM (ctx->self))) { /* Initialize the 3GPP/USSD interface */ mm_iface_modem_3gpp_ussd_initialize (MM_IFACE_MODEM_3GPP_USSD (ctx->self), (GAsyncReadyCallback)iface_modem_3gpp_ussd_initialize_ready, task); return; } ctx->step++; /* fall through */ case INITIALIZE_STEP_IFACE_CDMA: if (mm_iface_modem_is_cdma (MM_IFACE_MODEM (ctx->self))) { /* Initialize the CDMA interface */ mm_iface_modem_cdma_initialize (MM_IFACE_MODEM_CDMA (ctx->self), g_task_get_cancellable (task), (GAsyncReadyCallback)iface_modem_cdma_initialize_ready, task); return; } ctx->step++; /* fall through */ case INITIALIZE_STEP_IFACE_MESSAGING: /* Initialize the Messaging interface */ mm_iface_modem_messaging_initialize (MM_IFACE_MODEM_MESSAGING (ctx->self), g_task_get_cancellable (task), (GAsyncReadyCallback)iface_modem_messaging_initialize_ready, task); return; case INITIALIZE_STEP_IFACE_TIME: /* Initialize the Time interface */ mm_iface_modem_time_initialize (MM_IFACE_MODEM_TIME (ctx->self), g_task_get_cancellable (task), (GAsyncReadyCallback)iface_modem_time_initialize_ready, task); return; case INITIALIZE_STEP_IFACE_SIGNAL: /* Initialize the Signal interface */ mm_iface_modem_signal_initialize (MM_IFACE_MODEM_SIGNAL (ctx->self), g_task_get_cancellable (task), (GAsyncReadyCallback)iface_modem_signal_initialize_ready, task); return; case INITIALIZE_STEP_IFACE_OMA: /* Initialize the Oma interface */ mm_iface_modem_oma_initialize (MM_IFACE_MODEM_OMA (ctx->self), g_task_get_cancellable (task), (GAsyncReadyCallback)iface_modem_oma_initialize_ready, task); return; case INITIALIZE_STEP_IFACE_SAR: /* Initialize the SAR interface */ mm_iface_modem_sar_initialize (MM_IFACE_MODEM_SAR (ctx->self), g_task_get_cancellable (task), (GAsyncReadyCallback)iface_modem_sar_initialize_ready, task); return; case INITIALIZE_STEP_FALLBACK_LIMITED: /* All the initialization steps after this one will be run both on * successful and locked/failed initializations. */ ctx->step++; /* fall through */ case INITIALIZE_STEP_IFACE_LOCATION: /* Initialize the Location interface */ mm_iface_modem_location_initialize (MM_IFACE_MODEM_LOCATION (ctx->self), g_task_get_cancellable (task), (GAsyncReadyCallback)iface_modem_location_initialize_ready, task); return; case INITIALIZE_STEP_IFACE_VOICE: /* Initialize the Voice interface */ mm_iface_modem_voice_initialize (MM_IFACE_MODEM_VOICE (ctx->self), g_task_get_cancellable (task), (GAsyncReadyCallback)iface_modem_voice_initialize_ready, task); return; case INITIALIZE_STEP_IFACE_FIRMWARE: /* Initialize the Firmware interface */ mm_iface_modem_firmware_initialize (MM_IFACE_MODEM_FIRMWARE (ctx->self), g_task_get_cancellable (task), (GAsyncReadyCallback)iface_modem_firmware_initialize_ready, task); return; case INITIALIZE_STEP_IFACE_SIMPLE: if (ctx->self->priv->modem_state != MM_MODEM_STATE_FAILED) mm_iface_modem_simple_initialize (MM_IFACE_MODEM_SIMPLE (ctx->self)); ctx->step++; /* fall through */ case INITIALIZE_STEP_LAST: if (ctx->self->priv->modem_state == MM_MODEM_STATE_FAILED) { GError *error = NULL; if (!ctx->self->priv->modem_dbus_skeleton) { /* ABORTED here specifies an extremely fatal error that will make the modem * not even exported in DBus */ error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_ABORTED, "Fatal error: modem is unusable"); } else { /* Fatal SIM, firmware, or modem failure :-( */ MMModemStateFailedReason reason; reason = mm_gdbus_modem_get_state_failed_reason (MM_GDBUS_MODEM (ctx->self->priv->modem_dbus_skeleton)); error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_WRONG_STATE, "Modem in failed state: %s", mm_modem_state_failed_reason_get_string (reason)); /* Ensure we only leave the Modem, Voice, Location and Firmware interfaces * around. A failure could be caused by firmware issues, which * a firmware update, switch, or provisioning could fix. We also * leave the Voice interface around so that we can attempt * emergency voice calls, and the Location interface so that we can use * GNSS without a SIM card */ mm_iface_modem_3gpp_profile_manager_shutdown (MM_IFACE_MODEM_3GPP_PROFILE_MANAGER (ctx->self)); mm_iface_modem_3gpp_ussd_shutdown (MM_IFACE_MODEM_3GPP_USSD (ctx->self)); mm_iface_modem_cdma_shutdown (MM_IFACE_MODEM_CDMA (ctx->self)); mm_iface_modem_signal_shutdown (MM_IFACE_MODEM_SIGNAL (ctx->self)); mm_iface_modem_messaging_shutdown (MM_IFACE_MODEM_MESSAGING (ctx->self)); mm_iface_modem_time_shutdown (MM_IFACE_MODEM_TIME (ctx->self)); mm_iface_modem_simple_shutdown (MM_IFACE_MODEM_SIMPLE (ctx->self)); } g_task_return_error (task, error); g_object_unref (task); return; } if (ctx->self->priv->modem_state == MM_MODEM_STATE_LOCKED) { /* We're locked :-/ */ g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_WRONG_STATE, "Modem is currently locked, " "cannot fully initialize"); g_object_unref (task); return; } /* All initialized without errors! * Set as disabled (a.k.a. initialized) */ mm_iface_modem_update_state (MM_IFACE_MODEM (ctx->self), MM_MODEM_STATE_DISABLED, MM_MODEM_STATE_CHANGE_REASON_UNKNOWN); g_task_return_boolean (task, TRUE); g_object_unref (task); return; default: break; } g_assert_not_reached (); } static void initialize (MMBaseModem *self, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; task = g_task_new (self, cancellable, callback, user_data); /* Check state before launching modem initialization */ switch (MM_BROADBAND_MODEM (self)->priv->modem_state) { case MM_MODEM_STATE_FAILED: /* NOTE: this will only happen if we ever support hot-plugging SIMs */ g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_WRONG_STATE, "Cannot initialize modem: " "device is unusable"); break; case MM_MODEM_STATE_UNKNOWN: case MM_MODEM_STATE_LOCKED: { InitializeContext *ctx; ctx = g_new0 (InitializeContext, 1); ctx->self = MM_BROADBAND_MODEM (g_object_ref (self)); ctx->step = INITIALIZE_STEP_FIRST; g_task_set_task_data (task, ctx, (GDestroyNotify)initialize_context_free); /* Set as being initialized, even if we were locked before */ mm_iface_modem_update_state (MM_IFACE_MODEM (self), MM_MODEM_STATE_INITIALIZING, MM_MODEM_STATE_CHANGE_REASON_UNKNOWN); initialize_step (task); return; } case MM_MODEM_STATE_INITIALIZING: g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_IN_PROGRESS, "Cannot initialize modem: " "already being initialized"); break; case MM_MODEM_STATE_DISABLED: case MM_MODEM_STATE_DISABLING: case MM_MODEM_STATE_ENABLING: case MM_MODEM_STATE_ENABLED: case MM_MODEM_STATE_SEARCHING: case MM_MODEM_STATE_REGISTERED: case MM_MODEM_STATE_DISCONNECTING: case MM_MODEM_STATE_CONNECTING: case MM_MODEM_STATE_CONNECTED: /* Just return success, don't relaunch initialization */ g_task_return_boolean (task, TRUE); break; default: g_assert_not_reached (); } g_object_unref (task); } /*****************************************************************************/ MMModemCharset mm_broadband_modem_get_current_charset (MMBroadbandModem *self) { return self->priv->modem_current_charset; } /*****************************************************************************/ static void bearer_count_multiplexed_connected (MMBaseBearer *bearer, guint *count) { /* The Multiplexed property is only set if connected, so it's enough to check * that one to see if we're connected and multiplexed */ if (mm_gdbus_bearer_get_multiplexed (MM_GDBUS_BEARER (bearer))) *count += 1; } gboolean mm_broadband_modem_get_active_multiplexed_bearers (MMBroadbandModem *self, guint *out_current, guint *out_max, GError **error) { g_autoptr(MMBearerList) list = NULL; guint max; guint count = 0; g_object_get (self, MM_IFACE_MODEM_BEARER_LIST, &list, NULL); if (!list) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Bearer list unavailable"); return FALSE; } max = mm_bearer_list_get_max_active_multiplexed (list); mm_bearer_list_foreach (list, (MMBearerListForeachFunc)bearer_count_multiplexed_connected, &count); g_assert (!(!max && count)); if (out_max) *out_max = max; if (out_current) *out_current = count; return TRUE; } /*****************************************************************************/ gchar * mm_broadband_modem_create_device_identifier (MMBroadbandModem *self, const gchar *ati, const gchar *ati1, GError **error) { gchar *device_identifier; /* do nothing if device has gone already */ if (!self->priv->modem_dbus_skeleton) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Modem interface skeleton unavailable"); return NULL; } device_identifier = mm_create_device_identifier ( mm_base_modem_get_vendor_id (MM_BASE_MODEM (self)), mm_base_modem_get_product_id (MM_BASE_MODEM (self)), self, ati, ati1, mm_gdbus_modem_get_equipment_identifier ( MM_GDBUS_MODEM (self->priv->modem_dbus_skeleton)), mm_gdbus_modem_get_revision ( MM_GDBUS_MODEM (self->priv->modem_dbus_skeleton)), mm_gdbus_modem_get_model ( MM_GDBUS_MODEM (self->priv->modem_dbus_skeleton)), mm_gdbus_modem_get_manufacturer ( MM_GDBUS_MODEM (self->priv->modem_dbus_skeleton))); if (!device_identifier) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Unable to generate a device identifier"); return NULL; } return device_identifier; } /*****************************************************************************/ MMBroadbandModem * mm_broadband_modem_new (const gchar *device, const gchar *physdev, const gchar **drivers, const gchar *plugin, guint16 vendor_id, guint16 product_id) { return g_object_new (MM_TYPE_BROADBAND_MODEM, MM_BASE_MODEM_DEVICE, device, MM_BASE_MODEM_PHYSDEV, physdev, MM_BASE_MODEM_DRIVERS, drivers, MM_BASE_MODEM_PLUGIN, plugin, MM_BASE_MODEM_VENDOR_ID, vendor_id, MM_BASE_MODEM_PRODUCT_ID, product_id, /* Generic bearer supports TTY only */ MM_BASE_MODEM_DATA_NET_SUPPORTED, FALSE, MM_BASE_MODEM_DATA_TTY_SUPPORTED, TRUE, NULL); } static void set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { MMBroadbandModem *self = MM_BROADBAND_MODEM (object); switch (prop_id) { case PROP_MODEM_DBUS_SKELETON: g_clear_object (&self->priv->modem_dbus_skeleton); self->priv->modem_dbus_skeleton = g_value_dup_object (value); break; case PROP_MODEM_3GPP_DBUS_SKELETON: g_clear_object (&self->priv->modem_3gpp_dbus_skeleton); self->priv->modem_3gpp_dbus_skeleton = g_value_dup_object (value); break; case PROP_MODEM_3GPP_PROFILE_MANAGER_DBUS_SKELETON: g_clear_object (&self->priv->modem_3gpp_profile_manager_dbus_skeleton); self->priv->modem_3gpp_profile_manager_dbus_skeleton = g_value_dup_object (value); break; case PROP_MODEM_3GPP_USSD_DBUS_SKELETON: g_clear_object (&self->priv->modem_3gpp_ussd_dbus_skeleton); self->priv->modem_3gpp_ussd_dbus_skeleton = g_value_dup_object (value); break; case PROP_MODEM_CDMA_DBUS_SKELETON: g_clear_object (&self->priv->modem_cdma_dbus_skeleton); self->priv->modem_cdma_dbus_skeleton = g_value_dup_object (value); break; case PROP_MODEM_SIMPLE_DBUS_SKELETON: g_clear_object (&self->priv->modem_simple_dbus_skeleton); self->priv->modem_simple_dbus_skeleton = g_value_dup_object (value); break; case PROP_MODEM_LOCATION_DBUS_SKELETON: g_clear_object (&self->priv->modem_location_dbus_skeleton); self->priv->modem_location_dbus_skeleton = g_value_dup_object (value); break; case PROP_MODEM_MESSAGING_DBUS_SKELETON: g_clear_object (&self->priv->modem_messaging_dbus_skeleton); self->priv->modem_messaging_dbus_skeleton = g_value_dup_object (value); break; case PROP_MODEM_VOICE_DBUS_SKELETON: g_clear_object (&self->priv->modem_voice_dbus_skeleton); self->priv->modem_voice_dbus_skeleton = g_value_dup_object (value); break; case PROP_MODEM_TIME_DBUS_SKELETON: g_clear_object (&self->priv->modem_time_dbus_skeleton); self->priv->modem_time_dbus_skeleton = g_value_dup_object (value); break; case PROP_MODEM_SIGNAL_DBUS_SKELETON: g_clear_object (&self->priv->modem_signal_dbus_skeleton); self->priv->modem_signal_dbus_skeleton = g_value_dup_object (value); break; case PROP_MODEM_OMA_DBUS_SKELETON: g_clear_object (&self->priv->modem_oma_dbus_skeleton); self->priv->modem_oma_dbus_skeleton = g_value_dup_object (value); break; case PROP_MODEM_FIRMWARE_DBUS_SKELETON: g_clear_object (&self->priv->modem_firmware_dbus_skeleton); self->priv->modem_firmware_dbus_skeleton = g_value_dup_object (value); break; case PROP_MODEM_SAR_DBUS_SKELETON: g_clear_object (&self->priv->modem_sar_dbus_skeleton); self->priv->modem_sar_dbus_skeleton = g_value_dup_object (value); break; case PROP_MODEM_SIM: g_clear_object (&self->priv->modem_sim); self->priv->modem_sim = g_value_dup_object (value); break; case PROP_MODEM_SIM_SLOTS: g_clear_pointer (&self->priv->modem_sim_slots, g_ptr_array_unref); self->priv->modem_sim_slots = g_value_dup_boxed (value); break; case PROP_MODEM_BEARER_LIST: g_clear_object (&self->priv->modem_bearer_list); self->priv->modem_bearer_list = g_value_dup_object (value); break; case PROP_MODEM_STATE: self->priv->modem_state = g_value_get_enum (value); break; case PROP_MODEM_3GPP_REGISTRATION_STATE: self->priv->modem_3gpp_registration_state = g_value_get_enum (value); break; case PROP_MODEM_3GPP_CS_NETWORK_SUPPORTED: self->priv->modem_3gpp_cs_network_supported = g_value_get_boolean (value); break; case PROP_MODEM_3GPP_PS_NETWORK_SUPPORTED: self->priv->modem_3gpp_ps_network_supported = g_value_get_boolean (value); break; case PROP_MODEM_3GPP_EPS_NETWORK_SUPPORTED: self->priv->modem_3gpp_eps_network_supported = g_value_get_boolean (value); break; case PROP_MODEM_3GPP_5GS_NETWORK_SUPPORTED: self->priv->modem_3gpp_5gs_network_supported = g_value_get_boolean (value); break; case PROP_MODEM_3GPP_IGNORED_FACILITY_LOCKS: self->priv->modem_3gpp_ignored_facility_locks = g_value_get_flags (value); break; case PROP_MODEM_3GPP_INITIAL_EPS_BEARER: g_clear_object (&self->priv->modem_3gpp_initial_eps_bearer); self->priv->modem_3gpp_initial_eps_bearer = g_value_dup_object (value); break; case PROP_MODEM_3GPP_PACKET_SERVICE_STATE: self->priv->modem_3gpp_packet_service_state = g_value_get_enum (value); break; case PROP_MODEM_CDMA_CDMA1X_REGISTRATION_STATE: self->priv->modem_cdma_cdma1x_registration_state = g_value_get_enum (value); break; case PROP_MODEM_CDMA_EVDO_REGISTRATION_STATE: self->priv->modem_cdma_evdo_registration_state = g_value_get_enum (value); break; case PROP_MODEM_CDMA_CDMA1X_NETWORK_SUPPORTED: self->priv->modem_cdma_cdma1x_network_supported = g_value_get_boolean (value); break; case PROP_MODEM_CDMA_EVDO_NETWORK_SUPPORTED: self->priv->modem_cdma_evdo_network_supported = g_value_get_boolean (value); break; case PROP_MODEM_MESSAGING_SMS_LIST: g_clear_object (&self->priv->modem_messaging_sms_list); self->priv->modem_messaging_sms_list = g_value_dup_object (value); break; case PROP_MODEM_MESSAGING_SMS_PDU_MODE: self->priv->modem_messaging_sms_pdu_mode = g_value_get_boolean (value); break; case PROP_MODEM_MESSAGING_SMS_DEFAULT_STORAGE: self->priv->modem_messaging_sms_default_storage = g_value_get_enum (value); break; case PROP_MODEM_LOCATION_ALLOW_GPS_UNMANAGED_ALWAYS: self->priv->modem_location_allow_gps_unmanaged_always = g_value_get_boolean (value); break; case PROP_MODEM_VOICE_CALL_LIST: g_clear_object (&self->priv->modem_voice_call_list); self->priv->modem_voice_call_list = g_value_dup_object (value); break; case PROP_MODEM_SIMPLE_STATUS: g_clear_object (&self->priv->modem_simple_status); self->priv->modem_simple_status = g_value_dup_object (value); break; case PROP_MODEM_SIM_HOT_SWAP_SUPPORTED: self->priv->sim_hot_swap_supported = g_value_get_boolean (value); break; case PROP_MODEM_PERIODIC_SIGNAL_CHECK_DISABLED: self->priv->periodic_signal_check_disabled = g_value_get_boolean (value); break; case PROP_MODEM_PERIODIC_ACCESS_TECH_CHECK_DISABLED: self->priv->periodic_access_tech_check_disabled = g_value_get_boolean (value); break; case PROP_MODEM_PERIODIC_CALL_LIST_CHECK_DISABLED: self->priv->periodic_call_list_check_disabled = g_value_get_boolean (value); break; case PROP_MODEM_INDICATION_CALL_LIST_RELOAD_ENABLED: self->priv->indication_call_list_reload_enabled = g_value_get_boolean (value); break; case PROP_MODEM_CARRIER_CONFIG_MAPPING: self->priv->carrier_config_mapping = g_value_dup_string (value); break; case PROP_MODEM_FIRMWARE_IGNORE_CARRIER: self->priv->modem_firmware_ignore_carrier = g_value_get_boolean (value); break; case PROP_FLOW_CONTROL: self->priv->flow_control = g_value_get_flags (value); break; case PROP_INDICATORS_DISABLED: self->priv->modem_cind_disabled = 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) { MMBroadbandModem *self = MM_BROADBAND_MODEM (object); switch (prop_id) { case PROP_MODEM_DBUS_SKELETON: g_value_set_object (value, self->priv->modem_dbus_skeleton); break; case PROP_MODEM_3GPP_DBUS_SKELETON: g_value_set_object (value, self->priv->modem_3gpp_dbus_skeleton); break; case PROP_MODEM_3GPP_PROFILE_MANAGER_DBUS_SKELETON: g_value_set_object (value, self->priv->modem_3gpp_profile_manager_dbus_skeleton); break; case PROP_MODEM_3GPP_USSD_DBUS_SKELETON: g_value_set_object (value, self->priv->modem_3gpp_ussd_dbus_skeleton); break; case PROP_MODEM_CDMA_DBUS_SKELETON: g_value_set_object (value, self->priv->modem_cdma_dbus_skeleton); break; case PROP_MODEM_SIMPLE_DBUS_SKELETON: g_value_set_object (value, self->priv->modem_simple_dbus_skeleton); break; case PROP_MODEM_LOCATION_DBUS_SKELETON: g_value_set_object (value, self->priv->modem_location_dbus_skeleton); break; case PROP_MODEM_MESSAGING_DBUS_SKELETON: g_value_set_object (value, self->priv->modem_messaging_dbus_skeleton); break; case PROP_MODEM_VOICE_DBUS_SKELETON: g_value_set_object (value, self->priv->modem_voice_dbus_skeleton); break; case PROP_MODEM_TIME_DBUS_SKELETON: g_value_set_object (value, self->priv->modem_time_dbus_skeleton); break; case PROP_MODEM_SIGNAL_DBUS_SKELETON: g_value_set_object (value, self->priv->modem_signal_dbus_skeleton); break; case PROP_MODEM_OMA_DBUS_SKELETON: g_value_set_object (value, self->priv->modem_oma_dbus_skeleton); break; case PROP_MODEM_FIRMWARE_DBUS_SKELETON: g_value_set_object (value, self->priv->modem_firmware_dbus_skeleton); break; case PROP_MODEM_SAR_DBUS_SKELETON: g_value_set_object (value, self->priv->modem_sar_dbus_skeleton); break; case PROP_MODEM_SIM: g_value_set_object (value, self->priv->modem_sim); break; case PROP_MODEM_SIM_SLOTS: g_value_set_boxed (value, self->priv->modem_sim_slots); break; case PROP_MODEM_BEARER_LIST: g_value_set_object (value, self->priv->modem_bearer_list); break; case PROP_MODEM_STATE: g_value_set_enum (value, self->priv->modem_state); break; case PROP_MODEM_3GPP_REGISTRATION_STATE: g_value_set_enum (value, self->priv->modem_3gpp_registration_state); break; case PROP_MODEM_3GPP_CS_NETWORK_SUPPORTED: g_value_set_boolean (value, self->priv->modem_3gpp_cs_network_supported); break; case PROP_MODEM_3GPP_PS_NETWORK_SUPPORTED: g_value_set_boolean (value, self->priv->modem_3gpp_ps_network_supported); break; case PROP_MODEM_3GPP_EPS_NETWORK_SUPPORTED: g_value_set_boolean (value, self->priv->modem_3gpp_eps_network_supported); break; case PROP_MODEM_3GPP_5GS_NETWORK_SUPPORTED: g_value_set_boolean (value, self->priv->modem_3gpp_5gs_network_supported); break; case PROP_MODEM_3GPP_IGNORED_FACILITY_LOCKS: g_value_set_flags (value, self->priv->modem_3gpp_ignored_facility_locks); break; case PROP_MODEM_3GPP_INITIAL_EPS_BEARER: g_value_set_object (value, self->priv->modem_3gpp_initial_eps_bearer); break; case PROP_MODEM_3GPP_PACKET_SERVICE_STATE: g_value_set_enum (value, self->priv->modem_3gpp_packet_service_state); break; case PROP_MODEM_CDMA_CDMA1X_REGISTRATION_STATE: g_value_set_enum (value, self->priv->modem_cdma_cdma1x_registration_state); break; case PROP_MODEM_CDMA_EVDO_REGISTRATION_STATE: g_value_set_enum (value, self->priv->modem_cdma_evdo_registration_state); break; case PROP_MODEM_CDMA_CDMA1X_NETWORK_SUPPORTED: g_value_set_boolean (value, self->priv->modem_cdma_cdma1x_network_supported); break; case PROP_MODEM_CDMA_EVDO_NETWORK_SUPPORTED: g_value_set_boolean (value, self->priv->modem_cdma_evdo_network_supported); break; case PROP_MODEM_MESSAGING_SMS_LIST: g_value_set_object (value, self->priv->modem_messaging_sms_list); break; case PROP_MODEM_MESSAGING_SMS_PDU_MODE: g_value_set_boolean (value, self->priv->modem_messaging_sms_pdu_mode); break; case PROP_MODEM_MESSAGING_SMS_DEFAULT_STORAGE: g_value_set_enum (value, self->priv->modem_messaging_sms_default_storage); break; case PROP_MODEM_LOCATION_ALLOW_GPS_UNMANAGED_ALWAYS: g_value_set_boolean (value, self->priv->modem_location_allow_gps_unmanaged_always); break; case PROP_MODEM_VOICE_CALL_LIST: g_value_set_object (value, self->priv->modem_voice_call_list); break; case PROP_MODEM_SIMPLE_STATUS: g_value_set_object (value, self->priv->modem_simple_status); break; case PROP_MODEM_SIM_HOT_SWAP_SUPPORTED: g_value_set_boolean (value, self->priv->sim_hot_swap_supported); break; case PROP_MODEM_PERIODIC_SIGNAL_CHECK_DISABLED: g_value_set_boolean (value, self->priv->periodic_signal_check_disabled); break; case PROP_MODEM_PERIODIC_ACCESS_TECH_CHECK_DISABLED: g_value_set_boolean (value, self->priv->periodic_access_tech_check_disabled); break; case PROP_MODEM_PERIODIC_CALL_LIST_CHECK_DISABLED: g_value_set_boolean (value, self->priv->periodic_call_list_check_disabled); break; case PROP_MODEM_INDICATION_CALL_LIST_RELOAD_ENABLED: g_value_set_boolean (value, self->priv->indication_call_list_reload_enabled); break; case PROP_MODEM_CARRIER_CONFIG_MAPPING: g_value_set_string (value, self->priv->carrier_config_mapping); break; case PROP_MODEM_FIRMWARE_IGNORE_CARRIER: g_value_set_boolean (value, self->priv->modem_firmware_ignore_carrier); break; case PROP_FLOW_CONTROL: g_value_set_flags (value, self->priv->flow_control); break; case PROP_INDICATORS_DISABLED: g_value_set_boolean (value, self->priv->modem_cind_disabled); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void mm_broadband_modem_init (MMBroadbandModem *self) { /* Initialize private data */ self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, MM_TYPE_BROADBAND_MODEM, MMBroadbandModemPrivate); self->priv->modem_state = MM_MODEM_STATE_UNKNOWN; self->priv->modem_3gpp_registration_regex = mm_3gpp_creg_regex_get (TRUE); self->priv->modem_current_charset = MM_MODEM_CHARSET_UNKNOWN; self->priv->modem_3gpp_registration_state = MM_MODEM_3GPP_REGISTRATION_STATE_UNKNOWN; self->priv->modem_3gpp_cs_network_supported = TRUE; self->priv->modem_3gpp_ps_network_supported = TRUE; self->priv->modem_3gpp_eps_network_supported = FALSE; self->priv->modem_3gpp_5gs_network_supported = FALSE; self->priv->modem_3gpp_ignored_facility_locks = MM_MODEM_3GPP_FACILITY_NONE; self->priv->modem_3gpp_packet_service_state = MM_MODEM_3GPP_PACKET_SERVICE_STATE_UNKNOWN; self->priv->modem_cdma_cdma1x_registration_state = MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN; self->priv->modem_cdma_evdo_registration_state = MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN; self->priv->modem_cdma_cdma1x_network_supported = TRUE; self->priv->modem_cdma_evdo_network_supported = TRUE; self->priv->modem_messaging_sms_default_storage = MM_SMS_STORAGE_UNKNOWN; self->priv->current_sms_mem1_storage = MM_SMS_STORAGE_UNKNOWN; self->priv->current_sms_mem2_storage = MM_SMS_STORAGE_UNKNOWN; self->priv->sim_hot_swap_supported = FALSE; self->priv->periodic_signal_check_disabled = FALSE; self->priv->periodic_access_tech_check_disabled = FALSE; self->priv->periodic_call_list_check_disabled = FALSE; self->priv->indication_call_list_reload_enabled = FALSE; self->priv->modem_cmer_enable_mode = MM_3GPP_CMER_MODE_NONE; self->priv->modem_cmer_disable_mode = MM_3GPP_CMER_MODE_NONE; self->priv->modem_cmer_ind = MM_3GPP_CMER_IND_NONE; self->priv->flow_control = MM_FLOW_CONTROL_NONE; } static void finalize (GObject *object) { MMBroadbandModem *self = MM_BROADBAND_MODEM (object); if (self->priv->enabled_ports_ctx) ports_context_unref (self->priv->enabled_ports_ctx); if (self->priv->sim_hot_swap_ports_ctx) ports_context_unref (self->priv->sim_hot_swap_ports_ctx); if (self->priv->in_call_ports_ctx) ports_context_unref (self->priv->in_call_ports_ctx); if (self->priv->modem_3gpp_registration_regex) mm_3gpp_creg_regex_destroy (self->priv->modem_3gpp_registration_regex); g_free (self->priv->carrier_config_mapping); G_OBJECT_CLASS (mm_broadband_modem_parent_class)->finalize (object); } static void dispose (GObject *object) { MMBroadbandModem *self = MM_BROADBAND_MODEM (object); if (self->priv->modem_dbus_skeleton) { mm_iface_modem_shutdown (MM_IFACE_MODEM (object)); g_clear_object (&self->priv->modem_dbus_skeleton); } if (self->priv->modem_3gpp_dbus_skeleton) { mm_iface_modem_3gpp_shutdown (MM_IFACE_MODEM_3GPP (object)); g_clear_object (&self->priv->modem_3gpp_dbus_skeleton); } if (self->priv->modem_3gpp_profile_manager_dbus_skeleton) { mm_iface_modem_3gpp_profile_manager_shutdown (MM_IFACE_MODEM_3GPP_PROFILE_MANAGER (object)); g_clear_object (&self->priv->modem_3gpp_profile_manager_dbus_skeleton); } if (self->priv->modem_3gpp_ussd_dbus_skeleton) { mm_iface_modem_3gpp_ussd_shutdown (MM_IFACE_MODEM_3GPP_USSD (object)); g_clear_object (&self->priv->modem_3gpp_ussd_dbus_skeleton); } if (self->priv->modem_cdma_dbus_skeleton) { mm_iface_modem_cdma_shutdown (MM_IFACE_MODEM_CDMA (object)); g_clear_object (&self->priv->modem_cdma_dbus_skeleton); } if (self->priv->modem_location_dbus_skeleton) { mm_iface_modem_location_shutdown (MM_IFACE_MODEM_LOCATION (object)); g_clear_object (&self->priv->modem_location_dbus_skeleton); } if (self->priv->modem_signal_dbus_skeleton) { mm_iface_modem_signal_shutdown (MM_IFACE_MODEM_SIGNAL (object)); g_clear_object (&self->priv->modem_signal_dbus_skeleton); } if (self->priv->modem_messaging_dbus_skeleton) { mm_iface_modem_messaging_shutdown (MM_IFACE_MODEM_MESSAGING (object)); g_clear_object (&self->priv->modem_messaging_dbus_skeleton); } if (self->priv->modem_voice_dbus_skeleton) { mm_iface_modem_voice_shutdown (MM_IFACE_MODEM_VOICE (object)); g_clear_object (&self->priv->modem_voice_dbus_skeleton); } if (self->priv->modem_time_dbus_skeleton) { mm_iface_modem_time_shutdown (MM_IFACE_MODEM_TIME (object)); g_clear_object (&self->priv->modem_time_dbus_skeleton); } if (self->priv->modem_simple_dbus_skeleton) { mm_iface_modem_simple_shutdown (MM_IFACE_MODEM_SIMPLE (object)); g_clear_object (&self->priv->modem_simple_dbus_skeleton); } if (self->priv->modem_firmware_dbus_skeleton) { mm_iface_modem_firmware_shutdown (MM_IFACE_MODEM_FIRMWARE (object)); g_clear_object (&self->priv->modem_firmware_dbus_skeleton); } if (self->priv->modem_sar_dbus_skeleton) { mm_iface_modem_sar_shutdown (MM_IFACE_MODEM_SAR (object)); g_clear_object (&self->priv->modem_sar_dbus_skeleton); } g_clear_object (&self->priv->modem_3gpp_initial_eps_bearer); g_clear_object (&self->priv->modem_sim); g_clear_pointer (&self->priv->modem_sim_slots, g_ptr_array_unref); g_clear_object (&self->priv->modem_bearer_list); g_clear_object (&self->priv->modem_messaging_sms_list); g_clear_object (&self->priv->modem_voice_call_list); g_clear_object (&self->priv->modem_simple_status); G_OBJECT_CLASS (mm_broadband_modem_parent_class)->dispose (object); } static void iface_modem_init (MMIfaceModem *iface) { /* Initialization steps */ iface->load_current_capabilities = modem_load_current_capabilities; iface->load_current_capabilities_finish = modem_load_current_capabilities_finish; iface->load_manufacturer = modem_load_manufacturer; iface->load_manufacturer_finish = modem_load_manufacturer_finish; iface->load_model = modem_load_model; iface->load_model_finish = modem_load_model_finish; iface->load_revision = modem_load_revision; iface->load_revision_finish = modem_load_revision_finish; iface->load_equipment_identifier = modem_load_equipment_identifier; iface->load_equipment_identifier_finish = modem_load_equipment_identifier_finish; iface->load_device_identifier = modem_load_device_identifier; iface->load_device_identifier_finish = modem_load_device_identifier_finish; iface->load_own_numbers = modem_load_own_numbers; iface->load_own_numbers_finish = modem_load_own_numbers_finish; iface->load_unlock_required = modem_load_unlock_required; iface->load_unlock_required_finish = modem_load_unlock_required_finish; iface->load_unlock_retries = load_unlock_retries; iface->load_unlock_retries_finish = load_unlock_retries_finish; iface->create_sim = modem_create_sim; iface->create_sim_finish = modem_create_sim_finish; iface->load_supported_modes = modem_load_supported_modes; iface->load_supported_modes_finish = modem_load_supported_modes_finish; iface->load_power_state = modem_load_power_state; iface->load_power_state_finish = modem_load_power_state_finish; iface->load_supported_ip_families = modem_load_supported_ip_families; iface->load_supported_ip_families_finish = modem_load_supported_ip_families_finish; iface->create_bearer_list = modem_create_bearer_list; /* Enabling steps */ iface->modem_power_up = modem_power_up; iface->modem_power_up_finish = modem_power_up_finish; iface->check_for_sim_swap = modem_check_for_sim_swap; iface->check_for_sim_swap_finish = modem_check_for_sim_swap_finish; iface->setup_flow_control = modem_setup_flow_control; iface->setup_flow_control_finish = modem_setup_flow_control_finish; iface->load_supported_charsets = modem_load_supported_charsets; iface->load_supported_charsets_finish = modem_load_supported_charsets_finish; iface->setup_charset = modem_setup_charset; iface->setup_charset_finish = modem_setup_charset_finish; /* Additional actions */ iface->load_signal_quality = modem_load_signal_quality; iface->load_signal_quality_finish = modem_load_signal_quality_finish; iface->create_bearer = modem_create_bearer; iface->create_bearer_finish = modem_create_bearer_finish; iface->command = modem_command; iface->command_finish = modem_command_finish; iface->load_access_technologies = modem_load_access_technologies; iface->load_access_technologies_finish = modem_load_access_technologies_finish; } static void iface_modem_3gpp_init (MMIfaceModem3gpp *iface) { /* Initialization steps */ iface->load_imei = modem_3gpp_load_imei; iface->load_imei_finish = modem_3gpp_load_imei_finish; iface->load_enabled_facility_locks = modem_3gpp_load_enabled_facility_locks; iface->load_enabled_facility_locks_finish = modem_3gpp_load_enabled_facility_locks_finish; iface->load_eps_ue_mode_operation = modem_3gpp_load_eps_ue_mode_operation; iface->load_eps_ue_mode_operation_finish = modem_3gpp_load_eps_ue_mode_operation_finish; /* Enabling steps */ iface->setup_unsolicited_events = modem_3gpp_setup_unsolicited_events; iface->setup_unsolicited_events_finish = modem_3gpp_setup_cleanup_unsolicited_events_finish; iface->enable_unsolicited_events = modem_3gpp_enable_unsolicited_events; iface->enable_unsolicited_events_finish = modem_3gpp_enable_disable_unsolicited_events_finish; iface->setup_unsolicited_registration_events = modem_3gpp_setup_unsolicited_registration_events; iface->setup_unsolicited_registration_events_finish = modem_3gpp_setup_unsolicited_registration_events_finish; iface->enable_unsolicited_registration_events = modem_3gpp_enable_unsolicited_registration_events; iface->enable_unsolicited_registration_events_finish = modem_3gpp_enable_disable_unsolicited_registration_events_finish; /* Disabling steps */ iface->disable_unsolicited_events = modem_3gpp_disable_unsolicited_events; iface->disable_unsolicited_events_finish = modem_3gpp_enable_disable_unsolicited_events_finish; iface->cleanup_unsolicited_events = modem_3gpp_cleanup_unsolicited_events; iface->cleanup_unsolicited_events_finish = modem_3gpp_setup_cleanup_unsolicited_events_finish; iface->disable_unsolicited_registration_events = modem_3gpp_disable_unsolicited_registration_events; iface->disable_unsolicited_registration_events_finish = modem_3gpp_enable_disable_unsolicited_registration_events_finish; iface->cleanup_unsolicited_registration_events = modem_3gpp_cleanup_unsolicited_registration_events; iface->cleanup_unsolicited_registration_events_finish = modem_3gpp_cleanup_unsolicited_registration_events_finish; /* Additional actions */ iface->load_operator_code = modem_3gpp_load_operator_code; iface->load_operator_code_finish = modem_3gpp_load_operator_code_finish; iface->load_operator_name = modem_3gpp_load_operator_name; iface->load_operator_name_finish = modem_3gpp_load_operator_name_finish; iface->run_registration_checks = modem_3gpp_run_registration_checks; iface->run_registration_checks_finish = modem_3gpp_run_registration_checks_finish; iface->register_in_network = modem_3gpp_register_in_network; iface->register_in_network_finish = modem_3gpp_register_in_network_finish; iface->scan_networks = modem_3gpp_scan_networks; iface->scan_networks_finish = modem_3gpp_scan_networks_finish; iface->set_eps_ue_mode_operation = modem_3gpp_set_eps_ue_mode_operation; iface->set_eps_ue_mode_operation_finish = modem_3gpp_set_eps_ue_mode_operation_finish; iface->create_initial_eps_bearer = modem_3gpp_create_initial_eps_bearer; iface->set_packet_service_state = modem_3gpp_set_packet_service_state; iface->set_packet_service_state_finish = modem_3gpp_set_packet_service_state_finish; } static void iface_modem_3gpp_profile_manager_init (MMIfaceModem3gppProfileManager *iface) { /* Initialization steps */ iface->check_support = modem_3gpp_profile_manager_check_support; iface->check_support_finish = modem_3gpp_profile_manager_check_support_finish; /* User actions */ iface->list_profiles = modem_3gpp_profile_manager_list_profiles; iface->list_profiles_finish = modem_3gpp_profile_manager_list_profiles_finish; iface->delete_profile = modem_3gpp_profile_manager_delete_profile; iface->delete_profile_finish = modem_3gpp_profile_manager_delete_profile_finish; iface->check_format = modem_3gpp_profile_manager_check_format; iface->check_format_finish = modem_3gpp_profile_manager_check_format_finish; iface->check_activated_profile = modem_3gpp_profile_manager_check_activated_profile; iface->check_activated_profile_finish = modem_3gpp_profile_manager_check_activated_profile_finish; iface->deactivate_profile = modem_3gpp_profile_manager_deactivate_profile; iface->deactivate_profile_finish = modem_3gpp_profile_manager_deactivate_profile_finish; iface->store_profile = modem_3gpp_profile_manager_store_profile; iface->store_profile_finish = modem_3gpp_profile_manager_store_profile_finish; } static void iface_modem_3gpp_ussd_init (MMIfaceModem3gppUssd *iface) { /* Initialization steps */ iface->check_support = modem_3gpp_ussd_check_support; iface->check_support_finish = modem_3gpp_ussd_check_support_finish; /* Enabling steps */ iface->setup_unsolicited_events = modem_3gpp_ussd_setup_unsolicited_events; iface->setup_unsolicited_events_finish = modem_3gpp_ussd_setup_cleanup_unsolicited_events_finish; iface->enable_unsolicited_events = modem_3gpp_ussd_enable_unsolicited_events; iface->enable_unsolicited_events_finish = modem_3gpp_ussd_enable_disable_unsolicited_events_finish; /* Disabling steps */ iface->cleanup_unsolicited_events_finish = modem_3gpp_ussd_setup_cleanup_unsolicited_events_finish; iface->cleanup_unsolicited_events = modem_3gpp_ussd_cleanup_unsolicited_events; iface->disable_unsolicited_events = modem_3gpp_ussd_disable_unsolicited_events; iface->disable_unsolicited_events_finish = modem_3gpp_ussd_enable_disable_unsolicited_events_finish; /* Additional actions */ iface->encode = modem_3gpp_ussd_encode; iface->decode = modem_3gpp_ussd_decode; iface->send = modem_3gpp_ussd_send; iface->send_finish = modem_3gpp_ussd_send_finish; iface->cancel = modem_3gpp_ussd_cancel; iface->cancel_finish = modem_3gpp_ussd_cancel_finish; } static void iface_modem_cdma_init (MMIfaceModemCdma *iface) { /* Initialization steps */ iface->load_esn = modem_cdma_load_esn; iface->load_esn_finish = modem_cdma_load_esn_finish; iface->load_meid = modem_cdma_load_meid; iface->load_meid_finish = modem_cdma_load_meid_finish; /* Registration check steps */ iface->setup_unsolicited_events = modem_cdma_setup_unsolicited_events; iface->setup_unsolicited_events_finish = modem_cdma_setup_cleanup_unsolicited_events_finish; iface->cleanup_unsolicited_events = modem_cdma_cleanup_unsolicited_events; iface->cleanup_unsolicited_events_finish = modem_cdma_setup_cleanup_unsolicited_events_finish; iface->setup_registration_checks = modem_cdma_setup_registration_checks; iface->setup_registration_checks_finish = modem_cdma_setup_registration_checks_finish; iface->get_call_manager_state = modem_cdma_get_call_manager_state; iface->get_call_manager_state_finish = modem_cdma_get_call_manager_state_finish; iface->get_hdr_state = modem_cdma_get_hdr_state; iface->get_hdr_state_finish = modem_cdma_get_hdr_state_finish; iface->get_service_status = modem_cdma_get_service_status; iface->get_service_status_finish = modem_cdma_get_service_status_finish; iface->get_cdma1x_serving_system = modem_cdma_get_cdma1x_serving_system; iface->get_cdma1x_serving_system_finish = modem_cdma_get_cdma1x_serving_system_finish; iface->get_detailed_registration_state = modem_cdma_get_detailed_registration_state; iface->get_detailed_registration_state_finish = modem_cdma_get_detailed_registration_state_finish; /* Additional actions */ iface->register_in_network = modem_cdma_register_in_network; iface->register_in_network_finish = modem_cdma_register_in_network_finish; } static void iface_modem_simple_init (MMIfaceModemSimple *iface) { } static void iface_modem_location_init (MMIfaceModemLocation *iface) { iface->load_capabilities = modem_location_load_capabilities; iface->load_capabilities_finish = modem_location_load_capabilities_finish; iface->enable_location_gathering = enable_location_gathering; iface->enable_location_gathering_finish = enable_location_gathering_finish; } static void iface_modem_messaging_init (MMIfaceModemMessaging *iface) { iface->check_support = modem_messaging_check_support; iface->check_support_finish = modem_messaging_check_support_finish; iface->load_supported_storages = modem_messaging_load_supported_storages; iface->load_supported_storages_finish = modem_messaging_load_supported_storages_finish; iface->set_default_storage = modem_messaging_set_default_storage; iface->set_default_storage_finish = modem_messaging_set_default_storage_finish; iface->setup_sms_format = modem_messaging_setup_sms_format; iface->setup_sms_format_finish = modem_messaging_setup_sms_format_finish; iface->load_initial_sms_parts = modem_messaging_load_initial_sms_parts; iface->load_initial_sms_parts_finish = modem_messaging_load_initial_sms_parts_finish; iface->setup_unsolicited_events = modem_messaging_setup_unsolicited_events; iface->setup_unsolicited_events_finish = modem_messaging_setup_cleanup_unsolicited_events_finish; iface->enable_unsolicited_events = modem_messaging_enable_unsolicited_events; iface->enable_unsolicited_events_finish = modem_messaging_enable_unsolicited_events_finish; iface->cleanup_unsolicited_events = modem_messaging_cleanup_unsolicited_events; iface->cleanup_unsolicited_events_finish = modem_messaging_setup_cleanup_unsolicited_events_finish; iface->create_sms = modem_messaging_create_sms; iface->init_current_storages = modem_messaging_init_current_storages; iface->init_current_storages_finish = modem_messaging_init_current_storages_finish; } static void iface_modem_voice_init (MMIfaceModemVoice *iface) { iface->check_support = modem_voice_check_support; iface->check_support_finish = modem_voice_check_support_finish; iface->setup_unsolicited_events = modem_voice_setup_unsolicited_events; iface->setup_unsolicited_events_finish = modem_voice_setup_cleanup_unsolicited_events_finish; iface->enable_unsolicited_events = modem_voice_enable_unsolicited_events; iface->enable_unsolicited_events_finish = modem_voice_enable_disable_unsolicited_events_finish; iface->disable_unsolicited_events = modem_voice_disable_unsolicited_events; iface->disable_unsolicited_events_finish = modem_voice_enable_disable_unsolicited_events_finish; iface->cleanup_unsolicited_events = modem_voice_cleanup_unsolicited_events; iface->cleanup_unsolicited_events_finish = modem_voice_setup_cleanup_unsolicited_events_finish; iface->setup_in_call_unsolicited_events = modem_voice_setup_in_call_unsolicited_events; iface->setup_in_call_unsolicited_events_finish = modem_voice_setup_cleanup_in_call_unsolicited_events_finish; iface->cleanup_in_call_unsolicited_events = modem_voice_cleanup_in_call_unsolicited_events; iface->cleanup_in_call_unsolicited_events_finish = modem_voice_setup_cleanup_in_call_unsolicited_events_finish; iface->create_call = modem_voice_create_call; iface->load_call_list = modem_voice_load_call_list; iface->load_call_list_finish = modem_voice_load_call_list_finish; iface->hold_and_accept = modem_voice_hold_and_accept; iface->hold_and_accept_finish = modem_voice_hold_and_accept_finish; iface->hangup_and_accept = modem_voice_hangup_and_accept; iface->hangup_and_accept_finish = modem_voice_hangup_and_accept_finish; iface->hangup_all = modem_voice_hangup_all; iface->hangup_all_finish = modem_voice_hangup_all_finish; iface->join_multiparty = modem_voice_join_multiparty; iface->join_multiparty_finish = modem_voice_join_multiparty_finish; iface->leave_multiparty = modem_voice_leave_multiparty; iface->leave_multiparty_finish = modem_voice_leave_multiparty_finish; iface->transfer = modem_voice_transfer; iface->transfer_finish = modem_voice_transfer_finish; iface->call_waiting_setup = modem_voice_call_waiting_setup; iface->call_waiting_setup_finish = modem_voice_call_waiting_setup_finish; iface->call_waiting_query = modem_voice_call_waiting_query; iface->call_waiting_query_finish = modem_voice_call_waiting_query_finish; } static void iface_modem_time_init (MMIfaceModemTime *iface) { iface->check_support = modem_time_check_support; iface->check_support_finish = modem_time_check_support_finish; iface->load_network_time = modem_time_load_network_time; iface->load_network_time_finish = modem_time_load_network_time_finish; iface->load_network_timezone = modem_time_load_network_timezone; iface->load_network_timezone_finish = modem_time_load_network_timezone_finish; } static void iface_modem_signal_init (MMIfaceModemSignal *iface) { iface->check_support = modem_signal_check_support; iface->check_support_finish = modem_signal_check_support_finish; iface->load_values = modem_signal_load_values; iface->load_values_finish = modem_signal_load_values_finish; } static void iface_modem_oma_init (MMIfaceModemOma *iface) { } static void iface_modem_firmware_init (MMIfaceModemFirmware *iface) { } static void iface_modem_sar_init (MMIfaceModemSar *iface) { } static void mm_broadband_modem_class_init (MMBroadbandModemClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); MMBaseModemClass *base_modem_class = MM_BASE_MODEM_CLASS (klass); g_type_class_add_private (object_class, sizeof (MMBroadbandModemPrivate)); /* Virtual methods */ object_class->set_property = set_property; object_class->get_property = get_property; object_class->dispose = dispose; object_class->finalize = finalize; base_modem_class->initialize = initialize; base_modem_class->initialize_finish = initialize_finish; base_modem_class->enable = enable; base_modem_class->enable_finish = enable_finish; base_modem_class->disable = disable; base_modem_class->disable_finish = disable_finish; #if defined WITH_SUSPEND_RESUME base_modem_class->sync = synchronize; base_modem_class->sync_finish = synchronize_finish; #endif klass->setup_ports = setup_ports; klass->initialization_started = initialization_started; klass->initialization_started_finish = initialization_started_finish; klass->initialization_stopped = initialization_stopped; klass->enabling_started = enabling_started; klass->enabling_started_finish = enabling_started_finish; klass->enabling_modem_init = enabling_modem_init; klass->enabling_modem_init_finish = enabling_modem_init_finish; klass->disabling_stopped = disabling_stopped; g_object_class_override_property (object_class, PROP_MODEM_DBUS_SKELETON, MM_IFACE_MODEM_DBUS_SKELETON); g_object_class_override_property (object_class, PROP_MODEM_3GPP_DBUS_SKELETON, MM_IFACE_MODEM_3GPP_DBUS_SKELETON); g_object_class_override_property (object_class, PROP_MODEM_3GPP_PROFILE_MANAGER_DBUS_SKELETON, MM_IFACE_MODEM_3GPP_PROFILE_MANAGER_DBUS_SKELETON); g_object_class_override_property (object_class, PROP_MODEM_3GPP_USSD_DBUS_SKELETON, MM_IFACE_MODEM_3GPP_USSD_DBUS_SKELETON); g_object_class_override_property (object_class, PROP_MODEM_CDMA_DBUS_SKELETON, MM_IFACE_MODEM_CDMA_DBUS_SKELETON); g_object_class_override_property (object_class, PROP_MODEM_SIMPLE_DBUS_SKELETON, MM_IFACE_MODEM_SIMPLE_DBUS_SKELETON); g_object_class_override_property (object_class, PROP_MODEM_LOCATION_DBUS_SKELETON, MM_IFACE_MODEM_LOCATION_DBUS_SKELETON); g_object_class_override_property (object_class, PROP_MODEM_MESSAGING_DBUS_SKELETON, MM_IFACE_MODEM_MESSAGING_DBUS_SKELETON); g_object_class_override_property (object_class, PROP_MODEM_VOICE_DBUS_SKELETON, MM_IFACE_MODEM_VOICE_DBUS_SKELETON); g_object_class_override_property (object_class, PROP_MODEM_TIME_DBUS_SKELETON, MM_IFACE_MODEM_TIME_DBUS_SKELETON); g_object_class_override_property (object_class, PROP_MODEM_SIGNAL_DBUS_SKELETON, MM_IFACE_MODEM_SIGNAL_DBUS_SKELETON); g_object_class_override_property (object_class, PROP_MODEM_OMA_DBUS_SKELETON, MM_IFACE_MODEM_OMA_DBUS_SKELETON); g_object_class_override_property (object_class, PROP_MODEM_SAR_DBUS_SKELETON, MM_IFACE_MODEM_SAR_DBUS_SKELETON); g_object_class_override_property (object_class, PROP_MODEM_FIRMWARE_DBUS_SKELETON, MM_IFACE_MODEM_FIRMWARE_DBUS_SKELETON); g_object_class_override_property (object_class, PROP_MODEM_SIM, MM_IFACE_MODEM_SIM); g_object_class_override_property (object_class, PROP_MODEM_SIM_SLOTS, MM_IFACE_MODEM_SIM_SLOTS); g_object_class_override_property (object_class, PROP_MODEM_BEARER_LIST, MM_IFACE_MODEM_BEARER_LIST); g_object_class_override_property (object_class, PROP_MODEM_STATE, MM_IFACE_MODEM_STATE); g_object_class_override_property (object_class, PROP_MODEM_3GPP_REGISTRATION_STATE, MM_IFACE_MODEM_3GPP_REGISTRATION_STATE); g_object_class_override_property (object_class, PROP_MODEM_3GPP_CS_NETWORK_SUPPORTED, MM_IFACE_MODEM_3GPP_CS_NETWORK_SUPPORTED); g_object_class_override_property (object_class, PROP_MODEM_3GPP_PS_NETWORK_SUPPORTED, MM_IFACE_MODEM_3GPP_PS_NETWORK_SUPPORTED); g_object_class_override_property (object_class, PROP_MODEM_3GPP_EPS_NETWORK_SUPPORTED, MM_IFACE_MODEM_3GPP_EPS_NETWORK_SUPPORTED); g_object_class_override_property (object_class, PROP_MODEM_3GPP_5GS_NETWORK_SUPPORTED, MM_IFACE_MODEM_3GPP_5GS_NETWORK_SUPPORTED); g_object_class_override_property (object_class, PROP_MODEM_3GPP_IGNORED_FACILITY_LOCKS, MM_IFACE_MODEM_3GPP_IGNORED_FACILITY_LOCKS); g_object_class_override_property (object_class, PROP_MODEM_3GPP_INITIAL_EPS_BEARER, MM_IFACE_MODEM_3GPP_INITIAL_EPS_BEARER); g_object_class_override_property (object_class, PROP_MODEM_3GPP_PACKET_SERVICE_STATE, MM_IFACE_MODEM_3GPP_PACKET_SERVICE_STATE); g_object_class_override_property (object_class, PROP_MODEM_CDMA_CDMA1X_REGISTRATION_STATE, MM_IFACE_MODEM_CDMA_CDMA1X_REGISTRATION_STATE); g_object_class_override_property (object_class, PROP_MODEM_CDMA_EVDO_REGISTRATION_STATE, MM_IFACE_MODEM_CDMA_EVDO_REGISTRATION_STATE); g_object_class_override_property (object_class, PROP_MODEM_CDMA_CDMA1X_NETWORK_SUPPORTED, MM_IFACE_MODEM_CDMA_CDMA1X_NETWORK_SUPPORTED); g_object_class_override_property (object_class, PROP_MODEM_CDMA_EVDO_NETWORK_SUPPORTED, MM_IFACE_MODEM_CDMA_EVDO_NETWORK_SUPPORTED); g_object_class_override_property (object_class, PROP_MODEM_MESSAGING_SMS_LIST, MM_IFACE_MODEM_MESSAGING_SMS_LIST); g_object_class_override_property (object_class, PROP_MODEM_MESSAGING_SMS_PDU_MODE, MM_IFACE_MODEM_MESSAGING_SMS_PDU_MODE); g_object_class_override_property (object_class, PROP_MODEM_MESSAGING_SMS_DEFAULT_STORAGE, MM_IFACE_MODEM_MESSAGING_SMS_DEFAULT_STORAGE); g_object_class_override_property (object_class, PROP_MODEM_LOCATION_ALLOW_GPS_UNMANAGED_ALWAYS, MM_IFACE_MODEM_LOCATION_ALLOW_GPS_UNMANAGED_ALWAYS); g_object_class_override_property (object_class, PROP_MODEM_VOICE_CALL_LIST, MM_IFACE_MODEM_VOICE_CALL_LIST); g_object_class_override_property (object_class, PROP_MODEM_SIMPLE_STATUS, MM_IFACE_MODEM_SIMPLE_STATUS); g_object_class_override_property (object_class, PROP_MODEM_SIM_HOT_SWAP_SUPPORTED, MM_IFACE_MODEM_SIM_HOT_SWAP_SUPPORTED); g_object_class_override_property (object_class, PROP_MODEM_PERIODIC_SIGNAL_CHECK_DISABLED, MM_IFACE_MODEM_PERIODIC_SIGNAL_CHECK_DISABLED); g_object_class_override_property (object_class, PROP_MODEM_PERIODIC_ACCESS_TECH_CHECK_DISABLED, MM_IFACE_MODEM_PERIODIC_ACCESS_TECH_CHECK_DISABLED); g_object_class_override_property (object_class, PROP_MODEM_PERIODIC_CALL_LIST_CHECK_DISABLED, MM_IFACE_MODEM_VOICE_PERIODIC_CALL_LIST_CHECK_DISABLED); g_object_class_override_property (object_class, PROP_MODEM_INDICATION_CALL_LIST_RELOAD_ENABLED, MM_IFACE_MODEM_VOICE_INDICATION_CALL_LIST_RELOAD_ENABLED); g_object_class_override_property (object_class, PROP_MODEM_CARRIER_CONFIG_MAPPING, MM_IFACE_MODEM_CARRIER_CONFIG_MAPPING); g_object_class_override_property (object_class, PROP_MODEM_FIRMWARE_IGNORE_CARRIER, MM_IFACE_MODEM_FIRMWARE_IGNORE_CARRIER); properties[PROP_FLOW_CONTROL] = g_param_spec_flags (MM_BROADBAND_MODEM_FLOW_CONTROL, "Flow control", "Flow control settings to use in the connected TTY", MM_TYPE_FLOW_CONTROL, MM_FLOW_CONTROL_NONE, G_PARAM_READWRITE); g_object_class_install_property (object_class, PROP_FLOW_CONTROL, properties[PROP_FLOW_CONTROL]); properties[PROP_INDICATORS_DISABLED] = g_param_spec_boolean (MM_BROADBAND_MODEM_INDICATORS_DISABLED, "Disable indicators", "Avoid explicitly setting up +CIND URCs", FALSE, G_PARAM_READWRITE); g_object_class_install_property (object_class, PROP_INDICATORS_DISABLED, properties[PROP_INDICATORS_DISABLED]); #if defined WITH_SUSPEND_RESUME signals[SIGNAL_SYNC_NEEDED] = g_signal_new (MM_BROADBAND_MODEM_SIGNAL_SYNC_NEEDED, G_OBJECT_CLASS_TYPE (object_class), G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (MMBroadbandModemClass, sync_needed), NULL, NULL, g_cclosure_marshal_generic, G_TYPE_NONE, 0); #endif } ModemManager-1.23.4-dev/src/mm-broadband-modem.h000066400000000000000000000160141456466623000213110ustar00rootroot00000000000000/* -*- 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 - 2011 Red Hat, Inc. * Copyright (C) 2015 - Marco Bascetta * Copyright (C) 2011 - 2022 Google, Inc. */ #ifndef MM_BROADBAND_MODEM_H #define MM_BROADBAND_MODEM_H #include #include #include #include "mm-modem-helpers.h" #include "mm-charsets.h" #include "mm-base-modem.h" #define MM_TYPE_BROADBAND_MODEM (mm_broadband_modem_get_type ()) #define MM_BROADBAND_MODEM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_BROADBAND_MODEM, MMBroadbandModem)) #define MM_BROADBAND_MODEM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_BROADBAND_MODEM, MMBroadbandModemClass)) #define MM_IS_BROADBAND_MODEM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_BROADBAND_MODEM)) #define MM_IS_BROADBAND_MODEM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_BROADBAND_MODEM)) #define MM_BROADBAND_MODEM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_BROADBAND_MODEM, MMBroadbandModemClass)) typedef struct _MMBroadbandModem MMBroadbandModem; typedef struct _MMBroadbandModemClass MMBroadbandModemClass; typedef struct _MMBroadbandModemPrivate MMBroadbandModemPrivate; #define MM_BROADBAND_MODEM_FLOW_CONTROL "broadband-modem-flow-control" #define MM_BROADBAND_MODEM_INDICATORS_DISABLED "broadband-modem-indicators-disabled" #if defined WITH_SUSPEND_RESUME # define MM_BROADBAND_MODEM_SIGNAL_SYNC_NEEDED "broadband-modem-sync-needed" #endif struct _MMBroadbandModem { MMBaseModem parent; MMBroadbandModemPrivate *priv; }; struct _MMBroadbandModemClass { MMBaseModemClass parent; /* Setup ports, e.g. to setup unsolicited response handlers. * Plugins which need specific setups should chain up parent's port setup * as well. */ void (* setup_ports) (MMBroadbandModem *self); /* First and last initialization steps. * Actually, this is not really the first step, setup_ports() is */ void (* initialization_started) (MMBroadbandModem *self, GAsyncReadyCallback callback, gpointer user_data); gpointer (* initialization_started_finish) (MMBroadbandModem *self, GAsyncResult *res, GError **error); gboolean (* initialization_stopped) (MMBroadbandModem *self, gpointer started_context, GError **error); /* First enabling step */ void (* enabling_started) (MMBroadbandModem *self, GAsyncReadyCallback callback, gpointer user_data); gboolean (* enabling_started_finish) (MMBroadbandModem *self, GAsyncResult *res, GError **error); /* Modem initialization. During the 'enabling' step, this setup will be * called in order to initialize the modem, only if it wasn't hotplugged, * as we assume that a hotplugged modem is already initialized. */ void (* enabling_modem_init) (MMBroadbandModem *self, GAsyncReadyCallback callback, gpointer user_data); gboolean (* enabling_modem_init_finish) (MMBroadbandModem *self, GAsyncResult *res, GError **error); /* Last disabling step */ gboolean (* disabling_stopped) (MMBroadbandModem *self, GError **error); #if defined WITH_SUSPEND_RESUME /* signals */ void (* sync_needed) (MMBroadbandModem *self); #endif }; GType mm_broadband_modem_get_type (void); G_DEFINE_AUTOPTR_CLEANUP_FUNC (MMBroadbandModem, g_object_unref) MMBroadbandModem *mm_broadband_modem_new (const gchar *device, const gchar *physdev, const gchar **drivers, const gchar *plugin, guint16 vendor_id, guint16 product_id); MMModemCharset mm_broadband_modem_get_current_charset (MMBroadbandModem *self); /* Create a unique device identifier string using the ATI and ATI1 replies and some * additional internal info */ gchar *mm_broadband_modem_create_device_identifier (MMBroadbandModem *self, const gchar *ati, const gchar *ati1, GError **error); /* Locking/unlocking SMS storages */ void mm_broadband_modem_lock_sms_storages (MMBroadbandModem *self, MMSmsStorage mem1, /* reading/listing/deleting */ MMSmsStorage mem2, /* storing/sending */ GAsyncReadyCallback callback, gpointer user_data); gboolean mm_broadband_modem_lock_sms_storages_finish (MMBroadbandModem *self, GAsyncResult *res, GError **error); void mm_broadband_modem_unlock_sms_storages (MMBroadbandModem *self, gboolean mem1, gboolean mem2); /* Helper to update SIM hot swap */ gboolean mm_broadband_modem_sim_hot_swap_ports_context_init (MMBroadbandModem *self, GError **error); void mm_broadband_modem_sim_hot_swap_ports_context_reset (MMBroadbandModem *self); /* Helper to manage multiplexed bearers */ gboolean mm_broadband_modem_get_active_multiplexed_bearers (MMBroadbandModem *self, guint *out_current, guint *out_max, GError **error); #endif /* MM_BROADBAND_MODEM_H */ ModemManager-1.23.4-dev/src/mm-call-list.c000066400000000000000000000211111456466623000201470ustar00rootroot00000000000000/* -*- 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) 2015 Marco Bascetta */ #include #include #include #include #include #include #include #define _LIBMM_INSIDE_MM #include #include "mm-iface-modem-messaging.h" #include "mm-call-list.h" #include "mm-base-call.h" #include "mm-log.h" G_DEFINE_TYPE (MMCallList, mm_call_list, G_TYPE_OBJECT) enum { PROP_0, PROP_MODEM, PROP_LAST }; static GParamSpec *properties[PROP_LAST]; enum { SIGNAL_CALL_ADDED, SIGNAL_CALL_DELETED, SIGNAL_LAST }; static guint signals[SIGNAL_LAST]; struct _MMCallListPrivate { /* The owner modem */ MMBaseModem *modem; /* List of call objects */ GList *list; }; /*****************************************************************************/ void mm_call_list_foreach (MMCallList *self, MMCallListForeachFunc callback, gpointer user_data) { GList *l; g_assert (callback); for (l = self->priv->list; l; l = g_list_next (l)) callback (MM_BASE_CALL (l->data), user_data); } /*****************************************************************************/ guint mm_call_list_get_count (MMCallList *self) { return g_list_length (self->priv->list); } GStrv mm_call_list_get_paths (MMCallList *self) { GStrv path_list = NULL; GList *l; guint i; path_list = g_new0 (gchar *, 1 + g_list_length (self->priv->list)); for (i = 0, l = self->priv->list; l; l = g_list_next (l)) { const gchar *path; /* Don't try to add NULL paths (not yet exported CALL objects) */ path = mm_base_call_get_path (MM_BASE_CALL (l->data)); if (path) path_list[i++] = g_strdup (path); } return path_list; } /*****************************************************************************/ MMBaseCall * mm_call_list_get_first_incoming_call (MMCallList *self, MMCallState incoming_state) { GList *l; g_assert (incoming_state == MM_CALL_STATE_RINGING_IN || incoming_state == MM_CALL_STATE_WAITING); for (l = self->priv->list; l; l = g_list_next (l)) { MMBaseCall *call; MMCallState state; MMCallDirection direction; call = MM_BASE_CALL (l->data); g_object_get (call, "state", &state, "direction", &direction, NULL); if (direction == MM_CALL_DIRECTION_INCOMING && state == incoming_state) { return call; } } return NULL; } /*****************************************************************************/ static guint cmp_call_by_path (MMBaseCall *call, const gchar *path) { return g_strcmp0 (mm_base_call_get_path (call), path); } MMBaseCall * mm_call_list_get_call (MMCallList *self, const gchar *call_path) { GList *l; l = g_list_find_custom (self->priv->list, (gpointer)call_path, (GCompareFunc)cmp_call_by_path); return (l ? MM_BASE_CALL (l->data) : NULL); } static guint cmp_call_by_index (MMBaseCall *call, guint8 *index) { return mm_base_call_get_index (call) - *index; } MMBaseCall * mm_call_list_get_call_by_index (MMCallList *self, guint8 index) { GList *l; l = g_list_find_custom (self->priv->list, (gpointer)&index, (GCompareFunc)cmp_call_by_index); return (l ? MM_BASE_CALL (l->data) : NULL); } gboolean mm_call_list_delete_call (MMCallList *self, const gchar *call_path, GError **error) { GList *l; MMBaseCall *call; l = g_list_find_custom (self->priv->list, (gpointer)call_path, (GCompareFunc)cmp_call_by_path); if (!l) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_NOT_FOUND, "No call found with path '%s'", call_path); return FALSE; } call = MM_BASE_CALL (l->data); mm_base_call_unexport (call); g_signal_emit (self, signals[SIGNAL_CALL_DELETED], 0, call_path); g_object_unref (call); self->priv->list = g_list_delete_link (self->priv->list, l); return TRUE; } /*****************************************************************************/ void mm_call_list_add_call (MMCallList *self, MMBaseCall *call) { self->priv->list = g_list_prepend (self->priv->list, g_object_ref (call)); g_signal_emit (self, signals[SIGNAL_CALL_ADDED], 0, mm_base_call_get_path (call), FALSE); } /*****************************************************************************/ MMCallList * mm_call_list_new (MMBaseModem *modem) { /* Create the object */ return g_object_new (MM_TYPE_CALL_LIST, MM_CALL_LIST_MODEM, modem, NULL); } static void set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { MMCallList *self = MM_CALL_LIST (object); switch (prop_id) { case PROP_MODEM: g_clear_object (&self->priv->modem); self->priv->modem = g_value_dup_object (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) { MMCallList *self = MM_CALL_LIST (object); switch (prop_id) { case PROP_MODEM: g_value_set_object (value, self->priv->modem); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void mm_call_list_init (MMCallList *self) { /* Initialize private data */ self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, MM_TYPE_CALL_LIST, MMCallListPrivate); } static void dispose (GObject *object) { MMCallList *self = MM_CALL_LIST (object); g_clear_object (&self->priv->modem); g_list_free_full (self->priv->list, g_object_unref); self->priv->list = NULL; G_OBJECT_CLASS (mm_call_list_parent_class)->dispose (object); } static void mm_call_list_class_init (MMCallListClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); g_type_class_add_private (object_class, sizeof (MMCallListPrivate)); /* Virtual methods */ object_class->get_property = get_property; object_class->set_property = set_property; object_class->dispose = dispose; /* Properties */ properties[PROP_MODEM] = g_param_spec_object (MM_CALL_LIST_MODEM, "Modem", "The Modem which owns this CALL list", MM_TYPE_BASE_MODEM, G_PARAM_READWRITE); g_object_class_install_property (object_class, PROP_MODEM, properties[PROP_MODEM]); /* Signals */ signals[SIGNAL_CALL_ADDED] = g_signal_new (MM_CALL_ADDED, G_OBJECT_CLASS_TYPE (object_class), G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (MMCallListClass, call_added), NULL, NULL, g_cclosure_marshal_generic, G_TYPE_NONE, 1, G_TYPE_STRING); signals[SIGNAL_CALL_DELETED] = g_signal_new (MM_CALL_DELETED, G_OBJECT_CLASS_TYPE (object_class), G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (MMCallListClass, call_deleted), NULL, NULL, g_cclosure_marshal_generic, G_TYPE_NONE, 1, G_TYPE_STRING); } ModemManager-1.23.4-dev/src/mm-call-list.h000066400000000000000000000064111456466623000201620ustar00rootroot00000000000000/* -*- 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) 2015 Marco Bascetta */ #ifndef MM_CALL_LIST_H #define MM_CALL_LIST_H #include #include #include "mm-base-modem.h" #include "mm-base-call.h" #define MM_TYPE_CALL_LIST (mm_call_list_get_type ()) #define MM_CALL_LIST(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_CALL_LIST, MMCallList)) #define MM_CALL_LIST_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_CALL_LIST, MMCallListClass)) #define MM_IS_CALL_LIST(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_CALL_LIST)) #define MM_IS_CALL_LIST_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_CALL_LIST)) #define MM_CALL_LIST_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_CALL_LIST, MMCallListClass)) typedef struct _MMCallList MMCallList; typedef struct _MMCallListClass MMCallListClass; typedef struct _MMCallListPrivate MMCallListPrivate; #define MM_CALL_LIST_MODEM "call-list-modem" #define MM_CALL_ADDED "call-added" #define MM_CALL_DELETED "call-deleted" struct _MMCallList { GObject parent; MMCallListPrivate *priv; }; struct _MMCallListClass { GObjectClass parent; /* Signals */ void (* call_added) (MMCallList *self, const gchar *call_path, gboolean received); void (* call_deleted) (MMCallList *self, const gchar *call_path); }; GType mm_call_list_get_type (void); G_DEFINE_AUTOPTR_CLEANUP_FUNC (MMCallList, g_object_unref) MMCallList *mm_call_list_new (MMBaseModem *modem); GStrv mm_call_list_get_paths (MMCallList *self); guint mm_call_list_get_count (MMCallList *self); void mm_call_list_add_call (MMCallList *self, MMBaseCall *call); MMBaseCall *mm_call_list_get_call (MMCallList *self, const gchar *call_path); MMBaseCall *mm_call_list_get_call_by_index (MMCallList *self, guint8 index); gboolean mm_call_list_delete_call (MMCallList *self, const gchar *call_path, GError **error); MMBaseCall *mm_call_list_get_first_incoming_call (MMCallList *self, MMCallState incoming_state); typedef void (* MMCallListForeachFunc) (MMBaseCall *call, gpointer user_data); void mm_call_list_foreach (MMCallList *self, MMCallListForeachFunc callback, gpointer user_data); #endif /* MM_CALL_LIST_H */ ModemManager-1.23.4-dev/src/mm-call-qmi.c000066400000000000000000000425261456466623000177770ustar00rootroot00000000000000/* -*- 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) 2021 Joel Selvaraj */ #include #include #include #include #include #include #include #define _LIBMM_INSIDE_MM #include #include "mm-broadband-modem-qmi.h" #include "mm-modem-helpers-qmi.h" #include "mm-iface-modem.h" #include "mm-iface-modem-voice.h" #include "mm-call-qmi.h" #include "mm-base-modem.h" #include "mm-log-object.h" G_DEFINE_TYPE (MMCallQmi, mm_call_qmi, MM_TYPE_BASE_CALL) /*****************************************************************************/ static gboolean ensure_qmi_client (MMCallQmi *self, QmiService service, QmiClient **o_client, GAsyncReadyCallback callback, gpointer user_data) { MMBaseModem *modem = NULL; QmiClient *client; MMPortQmi *port; g_object_get (self, MM_BASE_CALL_MODEM, &modem, NULL); g_assert (MM_IS_BASE_MODEM (modem)); port = mm_broadband_modem_qmi_peek_port_qmi (MM_BROADBAND_MODEM_QMI (modem)); g_object_unref (modem); if (!port) { g_task_report_new_error (self, callback, user_data, ensure_qmi_client, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't peek QMI port"); return FALSE; } client = mm_port_qmi_peek_client (port, service, MM_PORT_QMI_FLAG_DEFAULT); if (!client) { g_task_report_new_error (self, callback, user_data, ensure_qmi_client, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't peek client for service '%s'", qmi_service_get_string (service)); return FALSE; } *o_client = client; return TRUE; } /*****************************************************************************/ /* Start the call */ static gboolean call_start_finish (MMBaseCall *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void voice_dial_call_ready (QmiClientVoice *client, GAsyncResult *res, GTask *task) { QmiMessageVoiceDialCallOutput *output; GError *error = NULL; MMBaseCall *self; self = MM_BASE_CALL (g_task_get_source_object (task)); output = qmi_client_voice_dial_call_finish (client, res, &error); if (!output) { g_prefix_error (&error, "QMI operation failed: "); g_task_return_error (task, error); } else if (!qmi_message_voice_dial_call_output_get_result (output, &error)) { g_prefix_error (&error, "Couldn't create call: "); g_task_return_error (task, error); } else { guint8 call_id = 0; qmi_message_voice_dial_call_output_get_call_id (output, &call_id, NULL); if (call_id == 0) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS, "Invalid call index"); } else { mm_base_call_set_index (self, call_id); g_task_return_boolean (task, TRUE); } } if (output) qmi_message_voice_dial_call_output_unref (output); g_object_unref (task); } static void call_start (MMBaseCall *self, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { QmiClient *client = NULL; GTask *task; QmiMessageVoiceDialCallInput *input; /* Ensure Voice client */ if (!ensure_qmi_client (MM_CALL_QMI (self), QMI_SERVICE_VOICE, &client, callback, user_data)) return; task = g_task_new (self, NULL, callback, user_data); input = qmi_message_voice_dial_call_input_new (); qmi_message_voice_dial_call_input_set_calling_number ( input, mm_gdbus_call_get_number (MM_GDBUS_CALL (self)), NULL); mm_obj_dbg (self, "starting call"); qmi_client_voice_dial_call (QMI_CLIENT_VOICE (client), input, 90, NULL, (GAsyncReadyCallback) voice_dial_call_ready, task); qmi_message_voice_dial_call_input_unref (input); } /*****************************************************************************/ /* Accept the call */ static gboolean call_accept_finish (MMBaseCall *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void voice_answer_call_ready (QmiClientVoice *client, GAsyncResult *res, GTask *task) { QmiMessageVoiceAnswerCallOutput *output; GError *error = NULL; output = qmi_client_voice_answer_call_finish (client, res, &error); if (!output) { g_prefix_error (&error, "QMI operation failed: "); g_task_return_error (task, error); } else if (!qmi_message_voice_answer_call_output_get_result (output, &error)) { g_prefix_error (&error, "Couldn't accept call: "); g_task_return_error (task, error); } else { g_task_return_boolean (task, TRUE); } if (output) qmi_message_voice_answer_call_output_unref (output); g_object_unref (task); } static void call_accept (MMBaseCall *self, GAsyncReadyCallback callback, gpointer user_data) { QmiClient *client = NULL; GTask *task; guint8 call_id; QmiMessageVoiceAnswerCallInput *input; /* Ensure Voice client */ if (!ensure_qmi_client (MM_CALL_QMI (self), QMI_SERVICE_VOICE, &client, callback, user_data)) return; task = g_task_new (self, NULL, callback, user_data); call_id = mm_base_call_get_index (self); if (call_id == 0) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS, "Invalid call index"); g_object_unref (task); return; } input = qmi_message_voice_answer_call_input_new (); qmi_message_voice_answer_call_input_set_call_id ( input, call_id, NULL); mm_obj_dbg (self, "Accepting call with id: %u", call_id); qmi_client_voice_answer_call (QMI_CLIENT_VOICE (client), input, 5, NULL, (GAsyncReadyCallback) voice_answer_call_ready, task); qmi_message_voice_answer_call_input_unref (input); } /*****************************************************************************/ /* Hangup the call */ static gboolean call_hangup_finish (MMBaseCall *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void voice_end_call_ready (QmiClientVoice *client, GAsyncResult *res, GTask *task) { QmiMessageVoiceEndCallOutput *output; GError *error = NULL; output = qmi_client_voice_end_call_finish (client, res, &error); if (!output) { g_prefix_error (&error, "QMI operation failed: "); g_task_return_error (task, error); } else if (!qmi_message_voice_end_call_output_get_result (output, &error)) { g_prefix_error (&error, "Couldn't hangup call: "); g_task_return_error (task, error); } else { g_task_return_boolean (task, TRUE); } if (output) qmi_message_voice_end_call_output_unref (output); g_object_unref (task); } static void call_hangup (MMBaseCall *self, GAsyncReadyCallback callback, gpointer user_data) { QmiClient *client = NULL; GTask *task; guint8 call_id; QmiMessageVoiceEndCallInput *input; /* Ensure Voice client */ if (!ensure_qmi_client (MM_CALL_QMI (self), QMI_SERVICE_VOICE, &client, callback, user_data)) return; task = g_task_new (self, NULL, callback, user_data); call_id = mm_base_call_get_index (self); if (call_id == 0) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS, "Invalid call index"); g_object_unref (task); return; } input = qmi_message_voice_end_call_input_new (); qmi_message_voice_end_call_input_set_call_id ( input, call_id, NULL); mm_obj_dbg (self, "Hanging up call with id: %u", call_id); qmi_client_voice_end_call (QMI_CLIENT_VOICE (client), input, 5, NULL, (GAsyncReadyCallback) voice_end_call_ready, task); qmi_message_voice_end_call_input_unref (input); } /*****************************************************************************/ /* Send DTMF character */ typedef struct { QmiClient *client; guint8 call_id; } SendDtmfContext; static void send_dtmf_context_free (SendDtmfContext *ctx) { g_clear_object (&ctx->client); g_slice_free (SendDtmfContext, ctx); } static gboolean call_send_dtmf_finish (MMBaseCall *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void voice_stop_continuous_dtmf_ready (QmiClientVoice *client, GAsyncResult *res, GTask *task) { g_autoptr (QmiMessageVoiceStopContinuousDtmfOutput) output = NULL; GError *error = NULL; output = qmi_client_voice_stop_continuous_dtmf_finish (client, res, &error); if (!output) { g_prefix_error (&error, "QMI operation failed: "); g_task_return_error (task, error); } else if (!qmi_message_voice_stop_continuous_dtmf_output_get_result (output, &error)) { g_prefix_error (&error, "Couldn't send DTMF character: "); g_task_return_error (task, error); } else { g_task_return_boolean (task, TRUE); } g_object_unref (task); } static gboolean voice_stop_continuous_dtmf (GTask *task) { SendDtmfContext *ctx; GError *error = NULL; g_autoptr (QmiMessageVoiceStopContinuousDtmfInput) input = NULL; ctx = g_task_get_task_data (task); input = qmi_message_voice_stop_continuous_dtmf_input_new (); qmi_message_voice_stop_continuous_dtmf_input_set_data (input, ctx->call_id, &error); qmi_client_voice_stop_continuous_dtmf (QMI_CLIENT_VOICE (ctx->client), input, 5, NULL, (GAsyncReadyCallback) voice_stop_continuous_dtmf_ready, task); return G_SOURCE_REMOVE; } static void voice_start_continuous_dtmf_ready (QmiClientVoice *client, GAsyncResult *res, GTask *task) { g_autoptr(QmiMessageVoiceStartContinuousDtmfOutput) output = NULL; GError *error = NULL; output = qmi_client_voice_start_continuous_dtmf_finish (client, res, &error); if (!output) { g_prefix_error (&error, "QMI operation failed: "); g_task_return_error (task, error); g_object_unref (task); return; } if (!qmi_message_voice_start_continuous_dtmf_output_get_result (output, &error)) { g_prefix_error (&error, "Couldn't send DTMF character: "); g_task_return_error (task, error); g_object_unref (task); return; } /* Disable DTMF press after 500 ms */ g_timeout_add (500, (GSourceFunc) voice_stop_continuous_dtmf, task); } static void call_send_dtmf (MMBaseCall *self, const gchar *dtmf, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; GError *error = NULL; SendDtmfContext *ctx; QmiClient *client = NULL; guint8 call_id; g_autoptr (QmiMessageVoiceStartContinuousDtmfInput) input = NULL; /* Ensure Voice client */ if (!ensure_qmi_client (MM_CALL_QMI (self), QMI_SERVICE_VOICE, &client, callback, user_data)) return; task = g_task_new (self, NULL, callback, user_data); call_id = mm_base_call_get_index (self); if (call_id == 0) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS, "Invalid call index"); g_object_unref (task); return; } ctx = g_slice_new0 (SendDtmfContext); ctx->client = g_object_ref (client); ctx->call_id = call_id; g_task_set_task_data (task, ctx, (GDestroyNotify) send_dtmf_context_free); /* Send DTMF character as ASCII number */ input = qmi_message_voice_start_continuous_dtmf_input_new (); qmi_message_voice_start_continuous_dtmf_input_set_data (input, call_id, (guint8) dtmf[0], &error); if (error) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS, "DTMF data build failed"); g_object_unref (task); return; } qmi_client_voice_start_continuous_dtmf (QMI_CLIENT_VOICE (client), input, 5, NULL, (GAsyncReadyCallback) voice_start_continuous_dtmf_ready, task); } /*****************************************************************************/ MMBaseCall * mm_call_qmi_new (MMBaseModem *modem, MMCallDirection direction, const gchar *number) { return MM_BASE_CALL (g_object_new (MM_TYPE_CALL_QMI, MM_BASE_CALL_MODEM, modem, "direction", direction, "number", number, MM_BASE_CALL_SKIP_INCOMING_TIMEOUT, TRUE, MM_BASE_CALL_SUPPORTS_DIALING_TO_RINGING, TRUE, MM_BASE_CALL_SUPPORTS_RINGING_TO_ACTIVE, TRUE, NULL)); } static void mm_call_qmi_init (MMCallQmi *self) { } static void mm_call_qmi_class_init (MMCallQmiClass *klass) { MMBaseCallClass *base_call_class = MM_BASE_CALL_CLASS (klass); base_call_class->start = call_start; base_call_class->start_finish = call_start_finish; base_call_class->accept = call_accept; base_call_class->accept_finish = call_accept_finish; base_call_class->hangup = call_hangup; base_call_class->hangup_finish = call_hangup_finish; base_call_class->send_dtmf = call_send_dtmf; base_call_class->send_dtmf_finish = call_send_dtmf_finish; } ModemManager-1.23.4-dev/src/mm-call-qmi.h000066400000000000000000000034661456466623000200040ustar00rootroot00000000000000/* -*- 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) 2021 Joel Selvaraj */ #ifndef MM_CALL_QMI_H #define MM_CALL_QMI_H #include #include #define _LIBMM_INSIDE_MM #include #include "mm-base-call.h" #define MM_TYPE_CALL_QMI (mm_call_qmi_get_type ()) #define MM_CALL_QMI(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_CALL_QMI, MMCallQmi)) #define MM_CALL_QMI_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_CALL_QMI, MMCallQmiClass)) #define MM_IS_CALL_QMI(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_CALL_QMI)) #define MM_IS_CALL_QMI_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_CALL_QMI)) #define MM_CALL_QMI_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_CALL_QMI, MMCallQmiClass)) typedef struct _MMCallQmi MMCallQmi; typedef struct _MMCallQmiClass MMCallQmiClass; struct _MMCallQmi { MMBaseCall parent; }; struct _MMCallQmiClass { MMBaseCallClass parent; }; GType mm_call_qmi_get_type (void); G_DEFINE_AUTOPTR_CLEANUP_FUNC (MMCallQmi, g_object_unref) MMBaseCall *mm_call_qmi_new (MMBaseModem *modem, MMCallDirection direction, const gchar *number); #endif /* MM_CALL_QMI_H */ ModemManager-1.23.4-dev/src/mm-charsets.c000066400000000000000000001213141456466623000201050ustar00rootroot00000000000000/* -*- 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. * Copyright (C) 2020 Aleksander Morgado */ #include #include #include #include #include #include #define _LIBMM_INSIDE_MM #include #include "mm-charsets.h" #include "mm-log.h" /* Common fallback character when transliteration is enabled */ static const gchar *translit_fallback = "?"; /******************************************************************************/ /* Expected charset settings */ typedef struct { MMModemCharset charset; const gchar *gsm_name; const gchar *other_name; const gchar *iconv_name; } CharsetSettings; static const CharsetSettings charset_settings[] = { { MM_MODEM_CHARSET_UTF8, "UTF-8", "UTF8", "UTF-8" }, { MM_MODEM_CHARSET_UCS2, "UCS2", NULL, "UCS-2BE" }, { MM_MODEM_CHARSET_IRA, "IRA", "ASCII", "ASCII" }, { MM_MODEM_CHARSET_GSM, "GSM", NULL, NULL }, { MM_MODEM_CHARSET_8859_1, "8859-1", NULL, "ISO8859-1" }, { MM_MODEM_CHARSET_PCCP437, "PCCP437", "CP437", "CP437" }, { MM_MODEM_CHARSET_PCDN, "PCDN", "CP850", "CP850" }, { MM_MODEM_CHARSET_UTF16, "UTF-16", "UTF16", "UTF-16BE" }, }; MMModemCharset mm_modem_charset_from_string (const gchar *string) { guint i; g_return_val_if_fail (string != NULL, MM_MODEM_CHARSET_UNKNOWN); for (i = 0; i < G_N_ELEMENTS (charset_settings); i++) { if (strcasestr (string, charset_settings[i].gsm_name)) return charset_settings[i].charset; if (charset_settings[i].other_name && strcasestr (string, charset_settings[i].other_name)) return charset_settings[i].charset; } return MM_MODEM_CHARSET_UNKNOWN; } static const CharsetSettings * lookup_charset_settings (MMModemCharset charset) { guint i; g_return_val_if_fail (charset != MM_MODEM_CHARSET_UNKNOWN, NULL); for (i = 0; i < G_N_ELEMENTS (charset_settings); i++) { if (charset_settings[i].charset == charset) return &charset_settings[i]; } g_warn_if_reached (); return NULL; } const gchar * mm_modem_charset_to_string (MMModemCharset charset) { const CharsetSettings *settings; settings = lookup_charset_settings (charset); return settings ? settings->gsm_name : NULL; } /******************************************************************************/ /* GSM 03.38 encoding conversion stuff */ #define GSM_DEF_ALPHABET_SIZE 128 #define GSM_EXT_ALPHABET_SIZE 10 typedef struct GsmUtf8Mapping { gchar chars[3]; guint8 len; guint8 gsm; /* only used for extended GSM charset */ } GsmUtf8Mapping; #define ONE(a) { {a, 0x00, 0x00}, 1, 0 } #define TWO(a, b) { {a, b, 0x00}, 2, 0 } /** * gsm_def_utf8_alphabet: * * Mapping from GSM default alphabet to UTF-8. * * ETSI GSM 03.38, version 6.0.1, section 6.2.1; Default alphabet. Mapping to UCS-2. * Mapping according to http://unicode.org/Public/MAPPINGS/ETSI/GSM0338.TXT */ static const GsmUtf8Mapping gsm_def_utf8_alphabet[GSM_DEF_ALPHABET_SIZE] = { /* @ £ $ ¥ */ ONE(0x40), TWO(0xc2, 0xa3), ONE(0x24), TWO(0xc2, 0xa5), /* è é ù ì */ TWO(0xc3, 0xa8), TWO(0xc3, 0xa9), TWO(0xc3, 0xb9), TWO(0xc3, 0xac), /* ò Ç \n Ø */ TWO(0xc3, 0xb2), TWO(0xc3, 0x87), ONE(0x0a), TWO(0xc3, 0x98), /* ø \r Å å */ TWO(0xc3, 0xb8), ONE(0x0d), TWO(0xc3, 0x85), TWO(0xc3, 0xa5), /* Δ _ Φ Γ */ TWO(0xce, 0x94), ONE(0x5f), TWO(0xce, 0xa6), TWO(0xce, 0x93), /* Λ Ω Π Ψ */ TWO(0xce, 0x9b), TWO(0xce, 0xa9), TWO(0xce, 0xa0), TWO(0xce, 0xa8), /* Σ Θ Ξ Escape Code */ TWO(0xce, 0xa3), TWO(0xce, 0x98), TWO(0xce, 0x9e), ONE(0xa0), /* Æ æ ß É */ TWO(0xc3, 0x86), TWO(0xc3, 0xa6), TWO(0xc3, 0x9f), TWO(0xc3, 0x89), /* ' ' ! " # */ ONE(0x20), ONE(0x21), ONE(0x22), ONE(0x23), /* ¤ % & ' */ TWO(0xc2, 0xa4), ONE(0x25), ONE(0x26), ONE(0x27), /* ( ) * + */ ONE(0x28), ONE(0x29), ONE(0x2a), ONE(0x2b), /* , - . / */ ONE(0x2c), ONE(0x2d), ONE(0x2e), ONE(0x2f), /* 0 1 2 3 */ ONE(0x30), ONE(0x31), ONE(0x32), ONE(0x33), /* 4 5 6 7 */ ONE(0x34), ONE(0x35), ONE(0x36), ONE(0x37), /* 8 9 : ; */ ONE(0x38), ONE(0x39), ONE(0x3a), ONE(0x3b), /* < = > ? */ ONE(0x3c), ONE(0x3d), ONE(0x3e), ONE(0x3f), /* ¡ A B C */ TWO(0xc2, 0xa1), ONE(0x41), ONE(0x42), ONE(0x43), /* D E F G */ ONE(0x44), ONE(0x45), ONE(0x46), ONE(0x47), /* H I J K */ ONE(0x48), ONE(0x49), ONE(0x4a), ONE(0x4b), /* L M N O */ ONE(0x4c), ONE(0x4d), ONE(0x4e), ONE(0x4f), /* P Q R S */ ONE(0x50), ONE(0x51), ONE(0x52), ONE(0x53), /* T U V W */ ONE(0x54), ONE(0x55), ONE(0x56), ONE(0x57), /* X Y Z Ä */ ONE(0x58), ONE(0x59), ONE(0x5a), TWO(0xc3, 0x84), /* Ö Ñ Ü § */ TWO(0xc3, 0x96), TWO(0xc3, 0x91), TWO(0xc3, 0x9c), TWO(0xc2, 0xa7), /* ¿ a b c */ TWO(0xc2, 0xbf), ONE(0x61), ONE(0x62), ONE(0x63), /* d e f g */ ONE(0x64), ONE(0x65), ONE(0x66), ONE(0x67), /* h i j k */ ONE(0x68), ONE(0x69), ONE(0x6a), ONE(0x6b), /* l m n o */ ONE(0x6c), ONE(0x6d), ONE(0x6e), ONE(0x6f), /* p q r s */ ONE(0x70), ONE(0x71), ONE(0x72), ONE(0x73), /* t u v w */ ONE(0x74), ONE(0x75), ONE(0x76), ONE(0x77), /* x y z ä */ ONE(0x78), ONE(0x79), ONE(0x7a), TWO(0xc3, 0xa4), /* ö ñ ü à */ TWO(0xc3, 0xb6), TWO(0xc3, 0xb1), TWO(0xc3, 0xbc), TWO(0xc3, 0xa0) }; static guint8 gsm_def_char_to_utf8 (const guint8 gsm, guint8 out_utf8[2]) { g_return_val_if_fail (gsm < GSM_DEF_ALPHABET_SIZE, 0); memcpy (&out_utf8[0], &gsm_def_utf8_alphabet[gsm].chars[0], gsm_def_utf8_alphabet[gsm].len); return gsm_def_utf8_alphabet[gsm].len; } static gboolean utf8_to_gsm_def_char (const gchar *utf8, guint32 len, guint8 *out_gsm) { gint i; if (len > 0 && len < 4) { for (i = 0; i < GSM_DEF_ALPHABET_SIZE; i++) { if (gsm_def_utf8_alphabet[i].len == len) { if (memcmp (&gsm_def_utf8_alphabet[i].chars[0], utf8, len) == 0) { *out_gsm = i; return TRUE; } } } } return FALSE; } static gboolean translit_gsm_nul_byte (GByteArray *gsm) { guint i; guint n_replaces = 0; for (i = 0; i < gsm->len; i++) { if (gsm->data[i] == 0x00) { utf8_to_gsm_def_char (translit_fallback, strlen (translit_fallback), &gsm->data[i]); n_replaces++; } } return (n_replaces > 0); } #define EONE(a, g) { {a, 0x00, 0x00}, 1, g } #define ETHR(a, b, c, g) { {a, b, c}, 3, g } /** * gsm_ext_utf8_alphabet: * * Mapping from GSM extended alphabet to UTF-8. * */ static const GsmUtf8Mapping gsm_ext_utf8_alphabet[GSM_EXT_ALPHABET_SIZE] = { /* form feed ^ { } */ EONE(0x0c, 0x0a), EONE(0x5e, 0x14), EONE(0x7b, 0x28), EONE(0x7d, 0x29), /* \ [ ~ ] */ EONE(0x5c, 0x2f), EONE(0x5b, 0x3c), EONE(0x7e, 0x3d), EONE(0x5d, 0x3e), /* | € */ EONE(0x7c, 0x40), ETHR(0xe2, 0x82, 0xac, 0x65) }; #define GSM_ESCAPE_CHAR 0x1b static guint8 gsm_ext_char_to_utf8 (const guint8 gsm, guint8 out_utf8[3]) { int i; for (i = 0; i < GSM_EXT_ALPHABET_SIZE; i++) { if (gsm == gsm_ext_utf8_alphabet[i].gsm) { memcpy (&out_utf8[0], &gsm_ext_utf8_alphabet[i].chars[0], gsm_ext_utf8_alphabet[i].len); return gsm_ext_utf8_alphabet[i].len; } } return 0; } static gboolean utf8_to_gsm_ext_char (const gchar *utf8, guint32 len, guint8 *out_gsm) { int i; if (len > 0 && len < 4) { for (i = 0; i < GSM_EXT_ALPHABET_SIZE; i++) { if (gsm_ext_utf8_alphabet[i].len == len) { if (memcmp (&gsm_ext_utf8_alphabet[i].chars[0], utf8, len) == 0) { *out_gsm = gsm_ext_utf8_alphabet[i].gsm; return TRUE; } } } } return FALSE; } static guint8 utf8_to_gsm_char (const gchar *utf8, guint32 len, guint8 *out_gsm) { if (utf8_to_gsm_def_char (utf8, len, out_gsm)) return 1; if (utf8_to_gsm_ext_char (utf8, len, out_gsm)) return 2; return 0; } static guint8 * charset_gsm_unpacked_to_utf8 (const guint8 *gsm, guint32 len, gboolean translit, GError **error) { g_autoptr(GByteArray) utf8 = NULL; guint i; g_return_val_if_fail (gsm != NULL, NULL); g_return_val_if_fail (len < 4096, NULL); /* worst case initial length */ utf8 = g_byte_array_sized_new (len * 2 + 1); for (i = 0; i < len; i++) { guint8 uchars[4]; guint8 ulen = 0; /* * 0x00 is NULL (when followed only by 0x00 up to the * end of (fixed byte length) message, possibly also up to * FORM FEED. But 0x00 is also the code for COMMERCIAL AT * when some other character (CARRIAGE RETURN if nothing else) * comes after the 0x00. * http://unicode.org/Public/MAPPINGS/ETSI/GSM0338.TXT * * So, if we find a '@' (0x00) and all the next chars after that * are also 0x00, we can consider the string finished already. */ if (gsm[i] == 0x00) { gsize j; for (j = i + 1; j < len; j++) { if (gsm[j] != 0x00) break; } if (j == len) break; } if (gsm[i] == GSM_ESCAPE_CHAR) { /* Extended alphabet, decode next char */ if (i + 1 < len) { ulen = gsm_ext_char_to_utf8 (gsm[i + 1], uchars); if (ulen) i += 1; } } else { /* Default alphabet */ ulen = gsm_def_char_to_utf8 (gsm[i], uchars); } if (ulen) g_byte_array_append (utf8, &uchars[0], ulen); else if (translit) g_byte_array_append (utf8, (guint8 *) translit_fallback, strlen (translit_fallback)); else { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS, "Invalid conversion from GSM7"); return NULL; } } /* Always make sure returned string is NUL terminated */ g_byte_array_append (utf8, (guint8 *) "\0", 1); return g_byte_array_free (g_steal_pointer (&utf8), FALSE); } static guint8 * charset_utf8_to_unpacked_gsm (const gchar *utf8, gboolean translit, guint32 *out_len, GError **error) { g_autoptr(GByteArray) gsm = NULL; const gchar *c; const gchar *next; static const guint8 gesc = GSM_ESCAPE_CHAR; if (!utf8 || !g_utf8_validate (utf8, -1, NULL)) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS, "Couldn't convert UTF-8 to GSM: input UTF-8 validation failed"); return NULL; } /* worst case initial length */ gsm = g_byte_array_sized_new (g_utf8_strlen (utf8, -1) * 2 + 1); if (*utf8 == 0x00) { /* Zero-length string */ g_byte_array_append (gsm, (guint8 *) "\0", 1); if (out_len) *out_len = 0; return g_byte_array_free (g_steal_pointer (&gsm), FALSE); } next = utf8; c = utf8; while (next && *next) { guint8 gch = 0x3f; /* 0x3f == '?' */ next = g_utf8_next_char (c); /* Try escaped chars first, then default alphabet */ if (utf8_to_gsm_ext_char (c, next - c, &gch)) { /* Add the escape char */ g_byte_array_append (gsm, &gesc, 1); g_byte_array_append (gsm, &gch, 1); } else if (utf8_to_gsm_def_char (c, next - c, &gch)) { g_byte_array_append (gsm, &gch, 1); } else if (translit) { /* add ? */ g_byte_array_append (gsm, &gch, 1); } else { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS, "Couldn't convert UTF-8 char to GSM"); return NULL; } c = next; } /* Output length doesn't consider terminating NUL byte */ if (out_len) *out_len = gsm->len; /* Always make sure returned string is NUL terminated */ g_byte_array_append (gsm, (guint8 *) "\0", 1); return g_byte_array_free (g_steal_pointer (&gsm), FALSE); } /******************************************************************************/ /* Checks to see whether conversion to a target charset may be done without * any loss. */ static gboolean gsm_is_subset (gunichar c, const gchar *utf8, gsize ulen) { guint8 gsm; if (utf8_to_gsm_def_char (utf8, ulen, &gsm)) return TRUE; if (utf8_to_gsm_ext_char (utf8, ulen, &gsm)) return TRUE; return FALSE; } static gboolean ira_is_subset (gunichar c, const gchar *utf8, gsize ulen) { return (ulen == 1); } static gboolean ucs2_is_subset (gunichar c, const gchar *utf8, gsize ulen) { return (c <= 0xFFFF); } static gboolean utf16_is_subset (gunichar c, const gchar *utf8, gsize ulen) { return TRUE; } static gboolean iso88591_is_subset (gunichar c, const gchar *utf8, gsize ulen) { return (c <= 0xFF); } static gboolean pccp437_is_subset (gunichar c, const gchar *utf8, gsize ulen) { static const gunichar t[] = { 0x00c7, 0x00fc, 0x00e9, 0x00e2, 0x00e4, 0x00e0, 0x00e5, 0x00e7, 0x00ea, 0x00eb, 0x00e8, 0x00ef, 0x00ee, 0x00ec, 0x00c4, 0x00c5, 0x00c9, 0x00e6, 0x00c6, 0x00f4, 0x00f6, 0x00f2, 0x00fb, 0x00f9, 0x00ff, 0x00d6, 0x00dc, 0x00a2, 0x00a3, 0x00a5, 0x20a7, 0x0192, 0x00e1, 0x00ed, 0x00f3, 0x00fa, 0x00f1, 0x00d1, 0x00aa, 0x00ba, 0x00bf, 0x2310, 0x00ac, 0x00bd, 0x00bc, 0x00a1, 0x00ab, 0x00bb, 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556, 0x2555, 0x2563, 0x2551, 0x2557, 0x255d, 0x255c, 0x255b, 0x2510, 0x2514, 0x2534, 0x252c, 0x251c, 0x2500, 0x253c, 0x255e, 0x255f, 0x255a, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256c, 0x2567, 0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256b, 0x256a, 0x2518, 0x250c, 0x2588, 0x2584, 0x258c, 0x2590, 0x2580, 0x03b1, 0x00df, 0x0393, 0x03c0, 0x03a3, 0x03c3, 0x00b5, 0x03c4, 0x03a6, 0x0398, 0x03a9, 0x03b4, 0x221e, 0x03c6, 0x03b5, 0x2229, 0x2261, 0x00b1, 0x2265, 0x2264, 0x2320, 0x2321, 0x00f7, 0x2248, 0x00b0, 0x2219, 0x00b7, 0x221a, 0x207f, 0x00b2, 0x25a0, 0x00a0 }; guint i; if (c <= 0x7F) return TRUE; for (i = 0; i < G_N_ELEMENTS (t); i++) { if (c == t[i]) return TRUE; } return FALSE; } static gboolean pcdn_is_subset (gunichar c, const gchar *utf8, gsize ulen) { static const gunichar t[] = { 0x00c7, 0x00fc, 0x00e9, 0x00e2, 0x00e4, 0x00e0, 0x00e5, 0x00e7, 0x00ea, 0x00eb, 0x00e8, 0x00ef, 0x00ee, 0x00ec, 0x00c4, 0x00c5, 0x00c9, 0x00e6, 0x00c6, 0x00f4, 0x00f6, 0x00f2, 0x00fb, 0x00f9, 0x00ff, 0x00d6, 0x00dc, 0x00f8, 0x00a3, 0x00d8, 0x00d7, 0x0192, 0x00e1, 0x00ed, 0x00f3, 0x00fa, 0x00f1, 0x00d1, 0x00aa, 0x00ba, 0x00bf, 0x00ae, 0x00ac, 0x00bd, 0x00bc, 0x00a1, 0x00ab, 0x00bb, 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x00c1, 0x00c2, 0x00c0, 0x00a9, 0x2563, 0x2551, 0x2557, 0x255d, 0x00a2, 0x00a5, 0x2510, 0x2514, 0x2534, 0x252c, 0x251c, 0x2500, 0x253c, 0x00e3, 0x00c3, 0x255a, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256c, 0x00a4, 0x00f0, 0x00d0, 0x00ca, 0x00cb, 0x00c8, 0x0131, 0x00cd, 0x00ce, 0x00cf, 0x2518, 0x250c, 0x2588, 0x2584, 0x00a6, 0x00cc, 0x2580, 0x00d3, 0x00df, 0x00d4, 0x00d2, 0x00f5, 0x00d5, 0x00b5, 0x00fe, 0x00de, 0x00da, 0x00db, 0x00d9, 0x00fd, 0x00dd, 0x00af, 0x00b4, 0x00ad, 0x00b1, 0x2017, 0x00be, 0x00b6, 0x00a7, 0x00f7, 0x00b8, 0x00b0, 0x00a8, 0x00b7, 0x00b9, 0x00b3, 0x00b2, 0x25a0, 0x00a0 }; guint i; if (c <= 0x7F) return TRUE; for (i = 0; i < sizeof (t) / sizeof (t[0]); i++) { if (c == t[i]) return TRUE; } return FALSE; } typedef struct { MMModemCharset cs; gboolean (*func) (gunichar c, const gchar *utf8, gsize ulen); } SubsetEntry; const SubsetEntry subset_table[] = { { MM_MODEM_CHARSET_GSM, gsm_is_subset }, { MM_MODEM_CHARSET_IRA, ira_is_subset }, { MM_MODEM_CHARSET_UCS2, ucs2_is_subset }, { MM_MODEM_CHARSET_UTF16, utf16_is_subset }, { MM_MODEM_CHARSET_8859_1, iso88591_is_subset }, { MM_MODEM_CHARSET_PCCP437, pccp437_is_subset }, { MM_MODEM_CHARSET_PCDN, pcdn_is_subset }, }; gboolean mm_charset_can_convert_to (const gchar *utf8, MMModemCharset charset) { const gchar *p; guint i; g_return_val_if_fail (charset != MM_MODEM_CHARSET_UNKNOWN, FALSE); g_return_val_if_fail (utf8 != NULL, FALSE); if (charset == MM_MODEM_CHARSET_UTF8) return TRUE; /* Find the charset in our subset table */ for (i = 0; i < G_N_ELEMENTS (subset_table); i++) { if (subset_table[i].cs == charset) break; } g_return_val_if_fail (i < G_N_ELEMENTS (subset_table), FALSE); p = utf8; while (*p) { gunichar c; const char *end; c = g_utf8_get_char_validated (p, -1); g_return_val_if_fail (c != (gunichar) -1, 0); end = g_utf8_find_next_char (p, NULL); if (end == NULL) { /* Find the string terminating NULL */ end = p; while (*++end); } if (!subset_table[i].func (c, p, (end - p))) return FALSE; p = end; } return TRUE; } /******************************************************************************/ /* GSM-7 pack/unpack operations */ guint8 * mm_charset_gsm_unpack (const guint8 *gsm, guint32 num_septets, guint8 start_offset, /* in _bits_ */ guint32 *out_unpacked_len) { GByteArray *unpacked; guint i; unpacked = g_byte_array_sized_new (num_septets + 1); for (i = 0; i < num_septets; i++) { guint8 bits_here, bits_in_next, octet, offset, c; guint32 start_bit; start_bit = start_offset + (i * 7); /* Overall bit offset of char in buffer */ offset = start_bit % 8; /* Offset to start of char in this byte */ bits_here = offset ? (8 - offset) : 7; bits_in_next = 7 - bits_here; /* Grab bits in the current byte */ octet = gsm[start_bit / 8]; c = (octet >> offset) & (0xFF >> (8 - bits_here)); /* Grab any bits that spilled over to next byte */ if (bits_in_next) { octet = gsm[(start_bit / 8) + 1]; c |= (octet & (0xFF >> (8 - bits_in_next))) << bits_here; } g_byte_array_append (unpacked, &c, 1); } *out_unpacked_len = unpacked->len; return g_byte_array_free (unpacked, FALSE); } guint8 * mm_charset_gsm_pack (const guint8 *src, guint32 src_len, guint8 start_offset, guint32 *out_packed_len) { guint8 *packed; guint octet = 0, lshift, plen; guint i = 0; g_return_val_if_fail (start_offset < 8, NULL); plen = (src_len * 7) + start_offset; /* total length in bits */ if (plen % 8) plen += 8; plen /= 8; /* now in bytes */ packed = g_malloc0 (plen); for (i = 0, lshift = start_offset; i < src_len; i++) { packed[octet] |= (src[i] & 0x7F) << lshift; if (lshift > 1) { /* Grab the lost bits and add to next octet */ g_assert (octet + 1 < plen); packed[octet + 1] = (src[i] & 0x7F) >> (8 - lshift); } if (lshift) octet++; lshift = lshift ? lshift - 1 : 7; } if (out_packed_len) *out_packed_len = plen; return packed; } /*****************************************************************************/ /* Main conversion functions */ static guint8 * charset_iconv_from_utf8 (const gchar *utf8, const CharsetSettings *settings, gboolean translit, guint *out_size, GError **error) { g_autoptr(GError) inner_error = NULL; gsize bytes_written = 0; g_autofree guint8 *encoded = NULL; encoded = (guint8 *) g_convert (utf8, -1, settings->iconv_name, "UTF-8", NULL, &bytes_written, &inner_error); if (encoded) { if (out_size) *out_size = (guint) bytes_written; return g_steal_pointer (&encoded); } if (!translit) { g_propagate_error (error, g_steal_pointer (&inner_error)); g_prefix_error (error, "Couldn't convert from UTF-8 to %s: ", settings->gsm_name); return NULL; } encoded = (guint8 *) g_convert_with_fallback (utf8, -1, settings->iconv_name, "UTF-8", translit_fallback, NULL, &bytes_written, error); if (encoded) { if (out_size) *out_size = (guint) bytes_written; return g_steal_pointer (&encoded); } g_prefix_error (error, "Couldn't convert from UTF-8 to %s with translit: ", settings->gsm_name); return NULL; } GByteArray * mm_modem_charset_bytearray_from_utf8 (const gchar *utf8, MMModemCharset charset, gboolean translit, GError **error) { const CharsetSettings *settings; guint8 *encoded = NULL; guint encoded_size = 0; settings = lookup_charset_settings (charset); if (charset == MM_MODEM_CHARSET_UNKNOWN) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS, "Cannot convert from UTF-8: unknown target charset"); return NULL; } switch (charset) { case MM_MODEM_CHARSET_GSM: encoded = charset_utf8_to_unpacked_gsm (utf8, translit, &encoded_size, error); break; case MM_MODEM_CHARSET_IRA: case MM_MODEM_CHARSET_8859_1: case MM_MODEM_CHARSET_UTF8: case MM_MODEM_CHARSET_UCS2: case MM_MODEM_CHARSET_PCCP437: case MM_MODEM_CHARSET_PCDN: case MM_MODEM_CHARSET_UTF16: encoded = charset_iconv_from_utf8 (utf8, settings, translit, &encoded_size, error); break; case MM_MODEM_CHARSET_UNKNOWN: default: g_assert_not_reached (); } return g_byte_array_new_take (encoded, encoded_size); } gchar * mm_modem_charset_str_from_utf8 (const gchar *utf8, MMModemCharset charset, gboolean translit, GError **error) { g_autoptr(GByteArray) bytearray = NULL; if (charset == MM_MODEM_CHARSET_UNKNOWN) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS, "Cannot convert from UTF-8: unknown target charset"); return NULL; } bytearray = mm_modem_charset_bytearray_from_utf8 (utf8, charset, translit, error); if (!bytearray) return NULL; switch (charset) { case MM_MODEM_CHARSET_GSM: /* Note: strings encoded in unpacked GSM-7 can be used as plain * strings as long as the string doesn't contain character '@', which * is the one encoded as 0x00. At this point, we perform transliteration * of the NUL bytes in the GSM-7 bytearray, and we fail the operation * if one or more replacements were done and transliteration wasn't * requested */ if (translit_gsm_nul_byte (bytearray) && !translit) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS, "Cannot convert to GSM-7 string: transliteration required for embedded '@'"); return NULL; } /* fall through */ case MM_MODEM_CHARSET_IRA: case MM_MODEM_CHARSET_8859_1: case MM_MODEM_CHARSET_UTF8: case MM_MODEM_CHARSET_PCCP437: case MM_MODEM_CHARSET_PCDN: return (gchar *) g_byte_array_free (g_steal_pointer (&bytearray), FALSE); case MM_MODEM_CHARSET_UCS2: case MM_MODEM_CHARSET_UTF16: return mm_utils_bin2hexstr (bytearray->data, bytearray->len); default: case MM_MODEM_CHARSET_UNKNOWN: g_assert_not_reached (); } } static gchar * charset_iconv_to_utf8 (const guint8 *data, guint32 len, const CharsetSettings *settings, gboolean translit, GError **error) { g_autoptr(GError) inner_error = NULL; g_autofree gchar *utf8 = NULL; utf8 = g_convert ((const gchar *) data, len, "UTF-8", settings->iconv_name, NULL, NULL, &inner_error); if (utf8) return g_steal_pointer (&utf8); if (!translit) { g_propagate_error (error, g_steal_pointer (&inner_error)); g_prefix_error (error, "Couldn't convert from %s to UTF-8: ", settings->gsm_name); return NULL; } utf8 = g_convert_with_fallback ((const gchar *) data, len, "UTF-8", settings->iconv_name, translit_fallback, NULL, NULL, error); if (utf8) return g_steal_pointer (&utf8); g_prefix_error (error, "Couldn't convert from %s to UTF-8 with translit: ", settings->gsm_name); return NULL; } gchar * mm_modem_charset_bytearray_to_utf8 (GByteArray *bytearray, MMModemCharset charset, gboolean translit, GError **error) { const CharsetSettings *settings; g_autofree gchar *utf8 = NULL; if (charset == MM_MODEM_CHARSET_UNKNOWN) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS, "Cannot convert from UTF-8: unknown target charset"); return NULL; } settings = lookup_charset_settings (charset); switch (charset) { case MM_MODEM_CHARSET_GSM: utf8 = (gchar *) charset_gsm_unpacked_to_utf8 (bytearray->data, bytearray->len, translit, error); break; case MM_MODEM_CHARSET_IRA: case MM_MODEM_CHARSET_UTF8: case MM_MODEM_CHARSET_8859_1: case MM_MODEM_CHARSET_PCCP437: case MM_MODEM_CHARSET_PCDN: case MM_MODEM_CHARSET_UCS2: case MM_MODEM_CHARSET_UTF16: utf8 = charset_iconv_to_utf8 (bytearray->data, bytearray->len, settings, translit, error); break; case MM_MODEM_CHARSET_UNKNOWN: default: g_assert_not_reached (); } if (!utf8) { g_prefix_error (error, "Invalid conversion from %s to UTF-8: ", settings->gsm_name); return NULL; } if (!g_utf8_validate (utf8, -1, NULL)) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Invalid conversion from %s: invalid UTF-8", settings->gsm_name); return NULL; } return g_steal_pointer (&utf8); } gchar * mm_modem_charset_str_to_utf8 (const gchar *str, gssize len, MMModemCharset charset, gboolean translit, GError **error) { g_autoptr(GByteArray) bytearray = NULL; if (charset == MM_MODEM_CHARSET_UNKNOWN) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS, "Cannot convert from UTF-8: unknown target charset"); return NULL; } /* Note: if the input string is GSM-7 encoded and it contains the '@' * character, using -1 to indicate string length won't work properly, * as '@' is encoded as 0x00. Whenever possible, if using GSM-7, * give a proper len value or otherwise use the bytearray_to_utf8() * method instead. */ if (len < 0) len = strlen (str); switch (charset) { case MM_MODEM_CHARSET_GSM: case MM_MODEM_CHARSET_IRA: case MM_MODEM_CHARSET_8859_1: case MM_MODEM_CHARSET_UTF8: case MM_MODEM_CHARSET_PCCP437: case MM_MODEM_CHARSET_PCDN: bytearray = g_byte_array_sized_new (len); g_byte_array_append (bytearray, (const guint8 *)str, len); break; case MM_MODEM_CHARSET_UCS2: case MM_MODEM_CHARSET_UTF16: { guint8 *bin = NULL; gsize bin_len; bin = (guint8 *) mm_utils_hexstr2bin (str, len, &bin_len, error); if (!bin) return NULL; bytearray = g_byte_array_new_take (bin, bin_len); break; } case MM_MODEM_CHARSET_UNKNOWN: default: g_assert_not_reached (); } return mm_modem_charset_bytearray_to_utf8 (bytearray, charset, translit, error); } /******************************************************************************/ /* Runtime charset support via iconv() */ void mm_modem_charsets_init (void) { /* As test string, something we can convert to/from all the encodings */ static const gchar *default_test_str = "ModemManager"; guint i; mm_obj_dbg (NULL, "[charsets] detecting platform iconv() support..."); for (i = 0; i < G_N_ELEMENTS (charset_settings); i++) { g_autofree guint8 *enc = NULL; guint enc_size; g_autofree gchar *dec = NULL; if (!charset_settings[i].iconv_name) continue; enc = charset_iconv_from_utf8 (default_test_str, &charset_settings[i], FALSE, &enc_size, NULL); if (!enc) { mm_obj_dbg (NULL, "[charsets] %s: iconv conversion to charset not supported", charset_settings[i].iconv_name); continue; } dec = charset_iconv_to_utf8 (enc, enc_size, &charset_settings[i], FALSE, NULL); if (!enc) { mm_obj_dbg (NULL, "[charsets] %s: iconv conversion from charset not supported", charset_settings[i].iconv_name); continue; } mm_obj_dbg (NULL, "[charsets] %s: iconv conversion to/from charset is supported", charset_settings[i].iconv_name); } } static gchar ** util_split_text_gsm7 (const gchar *text, gsize text_len, gpointer log_object) { g_autoptr(GPtrArray) chunks = NULL; const gchar *walker; const char *end; const gchar *chunk_start; glong encoded_chunk_length; glong total_encoded_chunk_length; chunks = g_ptr_array_new_with_free_func ((GDestroyNotify)g_free); walker = text; chunk_start = text; encoded_chunk_length = 0; total_encoded_chunk_length = 0; while (walker && *walker) { guint8 symbol[2] = {0, 0}; glong written_bytes = 0; end = g_utf8_find_next_char (walker, NULL); if (end == NULL) { /* Find the string terminating NULL */ end = walker; while (*++end); } written_bytes = utf8_to_gsm_char (walker, (end - walker), symbol); /* If more than one chunk is needed, these have to be of 140 - 6 = 134 * bytes each, as additional space is needed for the UDH header. * That means up to 153 input characters can be packed: * 134 * 8 = 1072; 1072/7=153.14 */ if ((encoded_chunk_length + written_bytes) > 153) { g_ptr_array_add (chunks, g_strndup (chunk_start, walker - chunk_start)); chunk_start = walker; encoded_chunk_length = written_bytes; } else encoded_chunk_length += written_bytes; total_encoded_chunk_length += written_bytes; walker = g_utf8_next_char (walker); } /* No splitting needed? */ if (total_encoded_chunk_length <= 160) { gchar **out; out = g_new0 (gchar *, 2); out[0] = g_strdup (text); return out; } /* Otherwise, we do need the splitted chunks. Add the last one * with contents plus the last trailing NULL */ g_ptr_array_add (chunks, g_strndup (chunk_start, walker - chunk_start)); g_ptr_array_add (chunks, NULL); return (gchar **) g_ptr_array_free (g_steal_pointer (&chunks), FALSE); } static gchar ** util_split_text_utf16_or_ucs2 (const gchar *text, gsize text_len, gpointer log_object) { g_autoptr(GPtrArray) chunks = NULL; const gchar *walker; const gchar *chunk_start; glong encoded_chunk_length; glong total_encoded_chunk_length; chunks = g_ptr_array_new_with_free_func ((GDestroyNotify)g_free); walker = text; chunk_start = text; encoded_chunk_length = 0; total_encoded_chunk_length = 0; while (walker && *walker) { g_autofree gunichar2 *unichar2 = NULL; glong unichar2_written = 0; glong unichar2_written_bytes = 0; gunichar single; single = g_utf8_get_char (walker); unichar2 = g_ucs4_to_utf16 (&single, 1, NULL, &unichar2_written, NULL); g_assert (unichar2_written > 0); /* When splitting for UCS-2 encoding, only one single unichar2 will be * written, because all codepoints represented in UCS2 fit in the BMP. * When splitting for UTF-16, though, we may end up writing one or two * unichar2 (without or with surrogate pairs), because UTF-16 covers the * whole Unicode spectrum. */ unichar2_written_bytes = (unichar2_written * sizeof (gunichar2)); if ((encoded_chunk_length + unichar2_written_bytes) > 134) { g_ptr_array_add (chunks, g_strndup (chunk_start, walker - chunk_start)); chunk_start = walker; encoded_chunk_length = unichar2_written_bytes; } else encoded_chunk_length += unichar2_written_bytes; total_encoded_chunk_length += unichar2_written_bytes; walker = g_utf8_next_char (walker); } /* We have split the original string in chunks, where each chunk * does not require more than 134 bytes when encoded in UTF-16. * As a special case now, we consider the case that no splitting * is necessary, i.e. if the total amount of bytes after encoding * in UTF-16 is less or equal than 140. */ if (total_encoded_chunk_length <= 140) { gchar **out; out = g_new0 (gchar *, 2); out[0] = g_strdup (text); return out; } /* Otherwise, we do need the splitted chunks. Add the last one * with contents plus the last trailing NULL */ g_ptr_array_add (chunks, g_strndup (chunk_start, walker - chunk_start)); g_ptr_array_add (chunks, NULL); return (gchar **) g_ptr_array_free (g_steal_pointer (&chunks), FALSE); } gchar ** mm_charset_util_split_text (const gchar *text, MMModemCharset *charset, gpointer log_object) { if (!text) return NULL; /* Some info about the rules for splitting. * * The User Data can be up to 140 bytes in the SMS part: * 0) If we only need one chunk, it can be of up to 140 bytes. * If we need more than one chunk, these have to be of 140 - 6 = 134 * bytes each, as we need place for the UDH header. * 1) If we're using GSM7 encoding, this gives us up to 160 characters, * as we can pack 160 characters of 7bits each into 140 bytes. * 160 * 7 = 140 * 8 = 1120. * If we only have 134 bytes allowed, that would mean that we can pack * up to 153 input characters: * 134 * 8 = 1072; 1072/7=153.14 * 2) If we're using UCS2 encoding, we can pack up to 70 characters in * 140 bytes (each with 2 bytes), or up to 67 characters in 134 bytes. * 3) If we're using UTF-16 encoding (instead of UCS2), the amount of * characters we can pack is variable, depends on how the characters * are encoded in UTF-16 (e.g. if there are characters out of the BMP * we'll need surrogate pairs and a single character will need 4 bytes * instead of 2). * * This method does the split of the input string into N strings, so that * each of the strings can be placed in a SMS part. */ /* Check if we can do GSM encoding */ if (mm_charset_can_convert_to (text, MM_MODEM_CHARSET_GSM)) { *charset = MM_MODEM_CHARSET_GSM; return util_split_text_gsm7 (text, strlen (text), log_object); } /* Otherwise fallback to report UCS-2 and split supporting UTF-16 */ *charset = MM_MODEM_CHARSET_UTF16; return util_split_text_utf16_or_ucs2 (text, strlen (text), log_object); } ModemManager-1.23.4-dev/src/mm-charsets.h000066400000000000000000000117701456466623000201160ustar00rootroot00000000000000/* -*- 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. */ #ifndef MM_CHARSETS_H #define MM_CHARSETS_H #include /*****************************************************************************************/ typedef enum { MM_MODEM_CHARSET_UNKNOWN = 0, MM_MODEM_CHARSET_GSM = 1 << 0, MM_MODEM_CHARSET_IRA = 1 << 1, MM_MODEM_CHARSET_8859_1 = 1 << 2, MM_MODEM_CHARSET_UTF8 = 1 << 3, MM_MODEM_CHARSET_UCS2 = 1 << 4, MM_MODEM_CHARSET_PCCP437 = 1 << 5, MM_MODEM_CHARSET_PCDN = 1 << 6, MM_MODEM_CHARSET_UTF16 = 1 << 7, } MMModemCharset; const gchar *mm_modem_charset_to_string (MMModemCharset charset); MMModemCharset mm_modem_charset_from_string (const gchar *string); /*****************************************************************************************/ /* Checks whether conversion to the given charset may be done without errors */ gboolean mm_charset_can_convert_to (const gchar *utf8, MMModemCharset charset); guint8 *mm_charset_gsm_unpack (const guint8 *gsm, guint32 num_septets, guint8 start_offset, /* in bits */ guint32 *out_unpacked_len); guint8 *mm_charset_gsm_pack (const guint8 *src, guint32 src_len, guint8 start_offset, /* in bits */ guint32 *out_packed_len); /*****************************************************************************************/ /* * Convert the given UTF-8 encoded string into the given charset. * * The output is given as a bytearray, because the target charset may allow * embedded NUL bytes (e.g. UTF-16). * * The output encoded string is not guaranteed to be NUL-terminated, instead * the bytearray length itself gives the correct string length. */ GByteArray *mm_modem_charset_bytearray_from_utf8 (const gchar *utf8, MMModemCharset charset, gboolean translit, GError **error); /* * Convert the given UTF-8 encoded string into the given charset. * * The output is given as a C string, and those charsets that allow * embedded NUL bytes (e.g. UTF-16) will be hex-encoded. * * The output encoded string is guaranteed to be NUL-terminated, and so no * explicit output length is returned. */ gchar *mm_modem_charset_str_from_utf8 (const gchar *utf8, MMModemCharset charset, gboolean translit, GError **error); /* * Convert into an UTF-8 encoded string the input byte array, which is * encoded in the given charset. * * The output string is guaranteed to be valid UTF-8 and NUL-terminated. */ gchar *mm_modem_charset_bytearray_to_utf8 (GByteArray *bytearray, MMModemCharset charset, gboolean translit, GError **error); /* * Convert into an UTF-8 encoded string the input string, which is * encoded in the given charset. Those charsets that allow embedded NUL * bytes (e.g. UTF-16) need to be hex-encoded. * * If the input string is NUL-terminated, len may be given as -1; otherwise * len needs to specify the number of valid bytes in the input string. * * The output string is guaranteed to be valid UTF-8 and NUL-terminated. */ gchar *mm_modem_charset_str_to_utf8 (const gchar *str, gssize len, MMModemCharset charset, gboolean translit, GError **error); /*****************************************************************************************/ void mm_modem_charsets_init (void); /* * Select appropriate encoding and split an UTF-8 encoded input string * into N UTF-8 strings, so that each of the strings * can be encoded into 'charset' and placed in a SMS part. */ gchar **mm_charset_util_split_text (const gchar *text, MMModemCharset *charset, gpointer log_object); #endif /* MM_CHARSETS_H */ ModemManager-1.23.4-dev/src/mm-context.c000066400000000000000000000274031456466623000177610ustar00rootroot00000000000000/* -*- 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) 2012 Aleksander Morgado */ #include #include #include #define _LIBMM_INSIDE_MM #include #include "mm-context.h" /*****************************************************************************/ /* Application context */ #if defined WITH_UDEV || defined WITH_QRTR # define NO_AUTO_SCAN_OPTION_FLAG 0 # define NO_AUTO_SCAN_DEFAULT FALSE #else /* Keep the option when udev and QRTR disabled, just so that the unit test * setup can unconditionally use --no-auto-scan */ # define NO_AUTO_SCAN_OPTION_FLAG G_OPTION_FLAG_HIDDEN # define NO_AUTO_SCAN_DEFAULT TRUE #endif static gboolean help_flag; static gboolean version_flag; static gboolean debug; static MMFilterRule filter_policy = MM_FILTER_POLICY_STRICT; static gboolean no_auto_scan = NO_AUTO_SCAN_DEFAULT; static const gchar *initial_kernel_events; static gboolean filter_policy_option_arg (const gchar *option_name, const gchar *value, gpointer data, GError **error) { if (!g_ascii_strcasecmp (value, "allowlist-only") #ifndef MM_DISABLE_DEPRECATED || !g_ascii_strcasecmp (value, "whitelist-only") #endif ) { filter_policy = MM_FILTER_POLICY_ALLOWLIST_ONLY; return TRUE; } if (!g_ascii_strcasecmp (value, "strict")) { filter_policy = MM_FILTER_POLICY_STRICT; return TRUE; } g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Invalid filter policy value given: %s", value); return FALSE; } static const GOptionEntry entries[] = { { "filter-policy", 0, 0, G_OPTION_ARG_CALLBACK, filter_policy_option_arg, "Filter policy: one of ALLOWLIST-ONLY, STRICT", "[POLICY]" }, { "no-auto-scan", 0, NO_AUTO_SCAN_OPTION_FLAG, G_OPTION_ARG_NONE, &no_auto_scan, "Don't auto-scan looking for devices", NULL }, { "initial-kernel-events", 0, 0, G_OPTION_ARG_FILENAME, &initial_kernel_events, "Path to initial kernel events file", "[PATH]" }, { "debug", 0, 0, G_OPTION_ARG_NONE, &debug, "Run with extended debugging capabilities", NULL }, { "version", 'V', 0, G_OPTION_ARG_NONE, &version_flag, "Print version", NULL }, { "help", 'h', 0, G_OPTION_ARG_NONE, &help_flag, "Show help", NULL }, { NULL } }; gboolean mm_context_get_debug (void) { return debug; } const gchar * mm_context_get_initial_kernel_events (void) { return initial_kernel_events; } gboolean mm_context_get_no_auto_scan (void) { return no_auto_scan; } MMFilterRule mm_context_get_filter_policy (void) { return filter_policy; } /*****************************************************************************/ /* Log context */ static const gchar *log_level; static const gchar *log_file; static gboolean log_journal; static gboolean log_show_ts; static gboolean log_rel_ts; static gboolean log_personal_info; static const GOptionEntry log_entries[] = { { "log-level", 0, 0, G_OPTION_ARG_STRING, &log_level, "Log level: one of ERR, WARN, MSG, INFO, DEBUG", "[LEVEL]" }, { "log-file", 0, 0, G_OPTION_ARG_FILENAME, &log_file, "Path to log file", "[PATH]" }, #if defined WITH_SYSTEMD_JOURNAL { "log-journal", 0, 0, G_OPTION_ARG_NONE, &log_journal, "Log to systemd journal", NULL }, #endif { "log-timestamps", 0, 0, G_OPTION_ARG_NONE, &log_show_ts, "Show timestamps in log output", NULL }, { "log-relative-timestamps", 0, 0, G_OPTION_ARG_NONE, &log_rel_ts, "Use relative timestamps (from MM start)", NULL }, { "log-personal-info", 0, 0, G_OPTION_ARG_NONE, &log_personal_info, "Show personal info in logs", NULL }, { NULL } }; static GOptionGroup * log_get_option_group (void) { GOptionGroup *group; group = g_option_group_new ("log", "Logging options:", "Show logging options", NULL, NULL); g_option_group_add_entries (group, log_entries); return group; } const gchar * mm_context_get_log_level (void) { return log_level; } const gchar * mm_context_get_log_file (void) { return log_file; } gboolean mm_context_get_log_journal (void) { return log_journal; } gboolean mm_context_get_log_timestamps (void) { return log_show_ts; } gboolean mm_context_get_log_relative_timestamps (void) { return log_rel_ts; } gboolean mm_context_get_log_personal_info (void) { return log_personal_info; } /*****************************************************************************/ /* Test context */ static gboolean test_session; #if defined WITH_TESTS static gboolean test_enable; #endif #if !defined WITH_BUILTIN_PLUGINS static gchar *test_plugin_dir; #endif #if defined WITH_UDEV static gboolean test_no_udev; #endif #if defined WITH_SUSPEND_RESUME static gboolean test_no_suspend_resume; static gboolean test_quick_suspend_resume; static gboolean test_low_power_suspend_resume; #endif #if defined WITH_QRTR static gboolean test_no_qrtr; #endif static gboolean test_multiplex_requested; #if defined WITH_MBIM static gboolean test_mbimex_profile_management; #endif static const GOptionEntry test_entries[] = { { "test-session", 0, 0, G_OPTION_ARG_NONE, &test_session, "Run in session DBus", NULL }, #if defined WITH_TESTS { "test-enable", 0, 0, G_OPTION_ARG_NONE, &test_enable, "Enable the Test interface in the daemon", NULL }, #endif #if !defined WITH_BUILTIN_PLUGINS { "test-plugin-dir", 0, 0, G_OPTION_ARG_FILENAME, &test_plugin_dir, "Path to look for plugins", "[PATH]" }, #endif #if defined WITH_UDEV { "test-no-udev", 0, 0, G_OPTION_ARG_NONE, &test_no_udev, "Run without udev support even if available", NULL }, #endif #if defined WITH_SUSPEND_RESUME { "test-no-suspend-resume", 0, 0, G_OPTION_ARG_NONE, &test_no_suspend_resume, "Disable suspend/resume support at runtime even if available", NULL }, { "test-quick-suspend-resume", 0, 0, G_OPTION_ARG_NONE, &test_quick_suspend_resume, "Enable quick suspend/resume support for modems which stay on during host suspension", NULL }, { "test-low-power-suspend-resume", 0, 0, G_OPTION_ARG_NONE, &test_low_power_suspend_resume, "Enable support to put the modem in low power mode during suspend/resume", NULL }, #endif #if defined WITH_QRTR { "test-no-qrtr", 0, 0, G_OPTION_ARG_NONE, &test_no_qrtr, "Run without qrtr support even if available", NULL }, #endif { "test-multiplex-requested", 0, 0, G_OPTION_ARG_NONE, &test_multiplex_requested, "Default to request multiplex support if no explicitly given", NULL }, #if defined WITH_MBIM { "test-mbimex-profile-management", 0, 0, G_OPTION_ARG_NONE, &test_mbimex_profile_management, "Default to use profile management MBIM extensions", NULL }, #endif { NULL } }; static GOptionGroup * test_get_option_group (void) { GOptionGroup *group; group = g_option_group_new ("test", "Test options:", "Show Test options", NULL, NULL); g_option_group_add_entries (group, test_entries); return group; } gboolean mm_context_get_test_session (void) { return test_session; } #if defined WITH_TESTS gboolean mm_context_get_test_enable (void) { return test_enable; } #endif #if !defined WITH_BUILTIN_PLUGINS const gchar * mm_context_get_test_plugin_dir (void) { return test_plugin_dir ? test_plugin_dir : PLUGINDIR; } #endif #if defined WITH_UDEV gboolean mm_context_get_test_no_udev (void) { return test_no_udev; } #endif #if defined WITH_SUSPEND_RESUME gboolean mm_context_get_test_no_suspend_resume (void) { return test_no_suspend_resume; } gboolean mm_context_get_test_quick_suspend_resume (void) { return test_quick_suspend_resume; } gboolean mm_context_get_test_low_power_suspend_resume (void) { return test_low_power_suspend_resume; } #endif #if defined WITH_QRTR gboolean mm_context_get_test_no_qrtr (void) { return test_no_qrtr; } #endif gboolean mm_context_get_test_multiplex_requested (void) { return test_multiplex_requested; } #if defined WITH_MBIM gboolean mm_context_get_test_mbimex_profile_management (void) { return test_mbimex_profile_management; } #endif /*****************************************************************************/ static void print_version (void) { g_print ("ModemManager " MM_DIST_VERSION "\n" "Copyright (C) 2008-2023 The ModemManager authors\n" "License GPLv2+: GNU GPL version 2 or later \n" "This is free software: you are free to change and redistribute it.\n" "There is NO WARRANTY, to the extent permitted by law.\n" "\n"); } static void print_help (GOptionContext *context) { gchar *str; /* Always print --help-all */ str = g_option_context_get_help (context, FALSE, NULL); g_print ("%s", str); g_free (str); } void mm_context_init (gint argc, gchar **argv) { GError *error = NULL; GOptionContext *ctx; ctx = g_option_context_new (NULL); g_option_context_set_summary (ctx, "DBus system service to control mobile broadband modems."); g_option_context_add_main_entries (ctx, entries, NULL); g_option_context_add_group (ctx, log_get_option_group ()); g_option_context_add_group (ctx, test_get_option_group ()); g_option_context_set_help_enabled (ctx, FALSE); if (!g_option_context_parse (ctx, &argc, &argv, &error)) { g_printerr ("error: %s\n", error->message); g_error_free (error); exit (1); } if (version_flag) { print_version (); g_option_context_free (ctx); exit (EXIT_SUCCESS); } if (help_flag) { print_help (ctx); g_option_context_free (ctx); exit (EXIT_SUCCESS); } g_option_context_free (ctx); /* Additional setup to be done on debug mode */ if (debug) { log_level = "DEBUG"; if (!log_show_ts && !log_rel_ts) log_show_ts = TRUE; } /* Initial kernel events processing may only be used if autoscan is disabled */ #if defined WITH_UDEV || defined WITH_QRTR if (!no_auto_scan && initial_kernel_events) { g_printerr ("error: --initial-kernel-events must be used only if --no-auto-scan is also used\n"); exit (1); } # if defined WITH_UDEV /* Force skipping autoscan if running test without udev */ if (test_no_udev) no_auto_scan = TRUE; # endif # if defined WITH_QRTR /* Force skipping autoscan if running test without qrtr */ if (test_no_qrtr) no_auto_scan = TRUE; # endif #endif } ModemManager-1.23.4-dev/src/mm-context.h000066400000000000000000000045261456466623000177670ustar00rootroot00000000000000/* -*- 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) 2012 Aleksander Morgado */ #ifndef MM_CONTEXT_H #define MM_CONTEXT_H #include #include #include "mm-filter.h" #if !defined(MM_DIST_VERSION) # define MM_DIST_VERSION VERSION #endif void mm_context_init (gint argc, gchar **argv); gboolean mm_context_get_debug (void); const gchar *mm_context_get_initial_kernel_events (void); gboolean mm_context_get_no_auto_scan (void); /* Filter support */ MMFilterRule mm_context_get_filter_policy (void); /* Logging support */ const gchar *mm_context_get_log_level (void); const gchar *mm_context_get_log_file (void); gboolean mm_context_get_log_journal (void); gboolean mm_context_get_log_timestamps (void); gboolean mm_context_get_log_relative_timestamps (void); gboolean mm_context_get_log_personal_info (void); /* Testing support */ gboolean mm_context_get_test_session (void); #if defined WITH_TESTS gboolean mm_context_get_test_enable (void); #endif #if !defined WITH_BUILTIN_PLUGINS const gchar *mm_context_get_test_plugin_dir (void); #endif #if defined WITH_UDEV gboolean mm_context_get_test_no_udev (void); #endif #if defined WITH_SUSPEND_RESUME gboolean mm_context_get_test_no_suspend_resume (void); gboolean mm_context_get_test_quick_suspend_resume (void); gboolean mm_context_get_test_low_power_suspend_resume (void); #endif #if defined WITH_QRTR gboolean mm_context_get_test_no_qrtr (void); #endif gboolean mm_context_get_test_multiplex_requested (void); #if defined WITH_MBIM gboolean mm_context_get_test_mbimex_profile_management (void); #endif #endif /* MM_CONTEXT_H */ ModemManager-1.23.4-dev/src/mm-device.c000066400000000000000000000761541456466623000175430ustar00rootroot00000000000000/* -*- 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) 2012 Google, Inc. */ #include #include #include #include #include #include #define _LIBMM_INSIDE_MM #include #include "mm-device.h" #include "mm-plugin.h" #include "mm-log-object.h" static void log_object_iface_init (MMLogObjectInterface *iface); G_DEFINE_TYPE_EXTENDED (MMDevice, mm_device, G_TYPE_OBJECT, 0, G_IMPLEMENT_INTERFACE (MM_TYPE_LOG_OBJECT, log_object_iface_init)) enum { PROP_0, PROP_UID, PROP_PHYSDEV, PROP_OBJECT_MANAGER, PROP_PLUGIN, PROP_MODEM, PROP_HOTPLUGGED, PROP_VIRTUAL, PROP_INHIBITED, PROP_LAST }; enum { SIGNAL_PORT_GRABBED, SIGNAL_PORT_RELEASED, SIGNAL_LAST }; static GParamSpec *properties[PROP_LAST]; static guint signals[SIGNAL_LAST]; struct _MMDevicePrivate { /* Whether the device is real or virtual */ gboolean virtual; /* Unique id */ gchar *uid; /* Physdev path */ gchar *physdev; /* The object manager */ GDBusObjectManagerServer *object_manager; /* If USB, device vid/pid */ guint16 vendor; guint16 product; /* Subsystem vendor ID for PCI devices */ guint16 subsystem_vendor; /* Kernel drivers managing this device */ gchar **drivers; /* Best plugin to manage this device */ MMPlugin *plugin; /* Lists of port probes in the device */ GList *port_probes; GList *ignored_port_probes; /* The Modem object for this device */ MMBaseModem *modem; gulong modem_valid_id; /* Whether the device was hot-plugged. */ gboolean hotplugged; /* Whether the device is inhibited. */ gboolean inhibited; /* Virtual ports */ gchar **virtual_ports; /* Scheduled reprobe */ guint reprobe_id; }; /*****************************************************************************/ static MMPortProbe * probe_list_lookup_by_device (GList *port_probes, MMKernelDevice *kernel_port) { GList *l; for (l = port_probes; l; l = g_list_next (l)) { MMPortProbe *probe = MM_PORT_PROBE (l->data); if (mm_kernel_device_cmp (mm_port_probe_peek_port (probe), kernel_port)) return probe; } return NULL; } static MMPortProbe * probe_list_lookup_by_name (GList *port_probes, const gchar *subsystem, const gchar *name) { GList *l; for (l = port_probes; l; l = g_list_next (l)) { MMPortProbe *probe = MM_PORT_PROBE (l->data); MMKernelDevice *probe_device; probe_device = mm_port_probe_peek_port (probe); if ((g_strcmp0 (subsystem, mm_kernel_device_get_subsystem (probe_device)) == 0) && (g_strcmp0 (name, mm_kernel_device_get_name (probe_device)) == 0)) return probe; } return NULL; } static MMPortProbe * device_find_probe_with_device (MMDevice *self, MMKernelDevice *kernel_port, gboolean lookup_ignored) { MMPortProbe *probe; probe = probe_list_lookup_by_device (self->priv->port_probes, kernel_port); if (probe) return probe; if (!lookup_ignored) return NULL; return probe_list_lookup_by_device (self->priv->ignored_port_probes, kernel_port); } gboolean mm_device_owns_port (MMDevice *self, MMKernelDevice *kernel_port) { return !!device_find_probe_with_device (self, kernel_port, TRUE); } static MMPortProbe * device_find_probe_with_name (MMDevice *self, const gchar *subsystem, const gchar *name) { MMPortProbe *probe; probe = probe_list_lookup_by_name (self->priv->port_probes, subsystem, name); if (probe) return probe; return probe_list_lookup_by_name (self->priv->ignored_port_probes, subsystem, name); } gboolean mm_device_owns_port_name (MMDevice *self, const gchar *subsystem, const gchar *name) { return !!device_find_probe_with_name (self, subsystem, name); } static void add_port_driver (MMDevice *self, MMKernelDevice *kernel_port) { const gchar *driver; guint n_items; guint i; driver = mm_kernel_device_get_driver (kernel_port); if (!driver) return; n_items = (self->priv->drivers ? g_strv_length (self->priv->drivers) : 0); if (n_items > 0) { /* Add driver to our list of drivers, if not already there */ for (i = 0; self->priv->drivers[i]; i++) { if (g_str_equal (self->priv->drivers[i], driver)) { driver = NULL; break; } } } if (!driver) return; self->priv->drivers = g_realloc (self->priv->drivers, (n_items + 2) * sizeof (gchar *)); self->priv->drivers[n_items] = g_strdup (driver); self->priv->drivers[n_items + 1] = NULL; } void mm_device_grab_port (MMDevice *self, MMKernelDevice *kernel_port) { MMPortProbe *probe; MMKernelDevice *lower_port; if (mm_device_owns_port (self, kernel_port)) return; lower_port = mm_kernel_device_peek_lower_device (kernel_port); if (lower_port) { g_autoptr(GError) error = NULL; /* No port probing done, at this point this is not something we require * as all the virtual instantiated ports are net devices. We also avoid * emitting the PORT_GRABBED signal in the MMDevice, because that is * exclusively linked to a port being added to the list of probes, which * we don't do here. */ if (self->priv->modem && !mm_base_modem_grab_link_port (self->priv->modem, kernel_port, &error)) mm_obj_dbg (self, "fully ignoring link port %s from now on: %s", mm_kernel_device_get_name (kernel_port), error->message); return; } if (!g_strcmp0 ("net", mm_kernel_device_get_subsystem (kernel_port)) && mm_kernel_device_get_wwandev_sysfs_path (kernel_port)) { /* This is a wwan netdevice, possibly a multiplexed one. * Multiplexed wwan netdevices do not have a lower device, so they won't fall in the * previous check verified for virtual ports, but require the same management. * However, we need to make sure that the arrived netdevice is not the default one that * instead requires the standard flow: for doing this we check that the name of the * arrived netdevice is not the default one, found in the wwandev_sysfs_path */ if (!g_strstr_len (mm_kernel_device_get_wwandev_sysfs_path (kernel_port), -1, mm_kernel_device_get_name (kernel_port))) { g_autoptr(GError) error = NULL; mm_obj_dbg (self, "grabbing wwan multiplexed device %s", mm_kernel_device_get_name (kernel_port)); if (self->priv->modem && !mm_base_modem_grab_link_port (self->priv->modem, kernel_port, &error)) mm_obj_dbg (self, "fully ignoring link port %s from now on: %s", mm_kernel_device_get_name (kernel_port), error->message); return; } } /* Get the vendor/product IDs out of the first one that gives us * some valid value (it seems we may get NULL reported for VID in QMI * ports, e.g. Huawei E367) */ if (!self->priv->vendor && !self->priv->product) { self->priv->vendor = mm_kernel_device_get_physdev_vid (kernel_port); self->priv->product = mm_kernel_device_get_physdev_pid (kernel_port); } if (!self->priv->subsystem_vendor) self->priv->subsystem_vendor = mm_kernel_device_get_physdev_subsystem_vid (kernel_port); /* Add new port driver */ add_port_driver (self, kernel_port); /* Create and store new port probe */ probe = mm_port_probe_new (self, kernel_port); self->priv->port_probes = g_list_prepend (self->priv->port_probes, probe); /* Notify about the grabbed port */ g_signal_emit (self, signals[SIGNAL_PORT_GRABBED], 0, kernel_port); } void mm_device_release_port_name (MMDevice *self, const gchar *subsystem, const gchar *name) { MMPortProbe *probe; /* If modem exists, try to remove it as a link port. We also avoid emitting * the PORT_RELEASED signal in this case, as the link ports are not associated * to the port probe list */ if (self->priv->modem && mm_base_modem_release_link_port (self->priv->modem, subsystem, name, NULL)) return; probe = device_find_probe_with_name (self, subsystem, name); if (probe) { /* Found, remove from lists and destroy probe */ if (g_list_find (self->priv->port_probes, probe)) self->priv->port_probes = g_list_remove (self->priv->port_probes, probe); else if (g_list_find (self->priv->ignored_port_probes, probe)) self->priv->ignored_port_probes = g_list_remove (self->priv->ignored_port_probes, probe); else g_assert_not_reached (); g_signal_emit (self, signals[SIGNAL_PORT_RELEASED], 0, mm_port_probe_peek_port (probe)); g_object_unref (probe); } } void mm_device_ignore_port (MMDevice *self, MMKernelDevice *kernel_port) { MMPortProbe *probe; probe = device_find_probe_with_device (self, kernel_port, FALSE); if (probe) { /* Found, remove from list and add to the ignored list */ mm_obj_dbg (self, "fully ignoring port %s from now on", mm_kernel_device_get_name (kernel_port)); self->priv->port_probes = g_list_remove (self->priv->port_probes, probe); self->priv->ignored_port_probes = g_list_prepend (self->priv->ignored_port_probes, probe); } } /*****************************************************************************/ static void unexport_modem (MMDevice *self) { gchar *path; g_assert (MM_IS_BASE_MODEM (self->priv->modem)); g_assert (G_IS_DBUS_OBJECT_MANAGER (self->priv->object_manager)); path = g_strdup (g_dbus_object_get_object_path (G_DBUS_OBJECT (self->priv->modem))); if (path != NULL) { g_dbus_object_manager_server_unexport (self->priv->object_manager, path); g_object_set (self->priv->modem, MM_BASE_MODEM_CONNECTION, NULL, NULL); mm_obj_dbg (self, "unexported modem from path '%s'", path); g_free (path); } } /*****************************************************************************/ static void export_modem (MMDevice *self) { GDBusConnection *connection = NULL; gchar *path; g_assert (MM_IS_BASE_MODEM (self->priv->modem)); g_assert (G_IS_DBUS_OBJECT_MANAGER (self->priv->object_manager)); /* If modem not yet valid (not fully initialized), don't export it */ if (!mm_base_modem_get_valid (self->priv->modem)) { mm_obj_dbg (self, "modem not yet fully initialized"); return; } /* Don't export already exported modems */ g_object_get (self->priv->modem, "g-object-path", &path, NULL); if (path) { g_free (path); mm_obj_dbg (self, "modem already exported"); return; } /* No outstanding port tasks, so if the modem is valid we can export it */ path = g_strdup_printf (MM_DBUS_MODEM_PREFIX "/%d", mm_base_modem_get_dbus_id (self->priv->modem)); g_object_get (self->priv->object_manager, "connection", &connection, NULL); g_object_set (self->priv->modem, "g-object-path", path, MM_BASE_MODEM_CONNECTION, connection, NULL); g_object_unref (connection); g_dbus_object_manager_server_export (self->priv->object_manager, G_DBUS_OBJECT_SKELETON (self->priv->modem)); mm_obj_dbg (self, " exported modem at path '%s'", path); mm_obj_dbg (self, " plugin: %s", mm_base_modem_get_plugin (self->priv->modem)); mm_obj_dbg (self, " vid:pid: 0x%04X:0x%04X", (mm_base_modem_get_vendor_id (self->priv->modem) & 0xFFFF), (mm_base_modem_get_product_id (self->priv->modem) & 0xFFFF)); if (mm_base_modem_get_subsystem_vendor_id (self->priv->modem)) mm_obj_dbg (self, " subsystem vid: 0x%04X", (mm_base_modem_get_subsystem_vendor_id (self->priv->modem) & 0xFFFF)); if (self->priv->virtual) mm_obj_dbg (self, " virtual"); g_free (path); } /*****************************************************************************/ static void clear_modem (MMDevice *self) { if (self->priv->modem_valid_id) { g_signal_handler_disconnect (self->priv->modem, self->priv->modem_valid_id); self->priv->modem_valid_id = 0; } if (self->priv->modem) { /* Run dispose before unref-ing, in order to cleanup the SIM object, * if any (which also holds a reference to the modem object) */ g_object_run_dispose (G_OBJECT (self->priv->modem)); g_clear_object (&(self->priv->modem)); } } void mm_device_remove_modem (MMDevice *self) { if (!self->priv->modem) return; unexport_modem (self); clear_modem (self); } /*****************************************************************************/ #define REPROBE_SECS 2 static gboolean reprobe (MMDevice *self) { GError *error = NULL; self->priv->reprobe_id = 0; mm_obj_dbg (self, "Reprobing modem..."); if (!mm_device_create_modem (self, &error)) { mm_obj_warn (self, "could not recreate modem: %s", error->message); g_error_free (error); } else mm_obj_dbg (self, "modem recreated"); return G_SOURCE_REMOVE; } static void modem_valid (MMBaseModem *modem, GParamSpec *pspec, MMDevice *self) { if (!mm_base_modem_get_valid (modem)) { /* Modem no longer valid */ mm_device_remove_modem (self); if (mm_base_modem_get_reprobe (modem)) self->priv->reprobe_id = g_timeout_add_seconds (REPROBE_SECS, (GSourceFunc)reprobe, self); } else { /* Modem now valid, export it, but only if we really have it around. * It may happen that the initialization sequence fails because the * modem gets disconnected, and in that case we don't really need * to export it */ if (self->priv->modem) export_modem (self); else mm_obj_dbg (self, "not exporting modem; no longer available"); } } gboolean mm_device_create_modem (MMDevice *self, GError **error) { g_assert (self->priv->modem == NULL); if (self->priv->inhibited) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Device is inhibited"); return FALSE; } if (!self->priv->virtual) { if (!self->priv->port_probes) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Not creating a device without ports"); return FALSE; } mm_obj_msg (self, "creating modem with plugin '%s' and '%u' ports", mm_plugin_get_name (self->priv->plugin), g_list_length (self->priv->port_probes)); } else { if (!self->priv->virtual_ports) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Not creating a virtual device without ports"); return FALSE; } mm_obj_msg (self, "creating virtual modem with plugin '%s' and '%u' ports", mm_plugin_get_name (self->priv->plugin), g_strv_length (self->priv->virtual_ports)); } self->priv->modem = mm_plugin_create_modem (self->priv->plugin, self, error); if (self->priv->modem) /* We want to get notified when the modem becomes valid/invalid */ self->priv->modem_valid_id = g_signal_connect (self->priv->modem, "notify::" MM_BASE_MODEM_VALID, G_CALLBACK (modem_valid), self); return !!self->priv->modem; } /*****************************************************************************/ const gchar * mm_device_get_uid (MMDevice *self) { return self->priv->uid; } const gchar * mm_device_get_physdev (MMDevice *self) { return self->priv->physdev; } const gchar ** mm_device_get_drivers (MMDevice *self) { return (const gchar **)self->priv->drivers; } guint16 mm_device_get_vendor (MMDevice *self) { return self->priv->vendor; } guint16 mm_device_get_product (MMDevice *self) { return self->priv->product; } guint16 mm_device_get_subsystem_vendor (MMDevice *self) { return self->priv->subsystem_vendor; } void mm_device_set_plugin (MMDevice *self, GObject *plugin) { g_object_set (self, MM_DEVICE_PLUGIN, plugin, NULL); } GObject * mm_device_peek_plugin (MMDevice *self) { return (self->priv->plugin ? G_OBJECT (self->priv->plugin) : NULL); } GObject * mm_device_get_plugin (MMDevice *self) { return (self->priv->plugin ? G_OBJECT (g_object_ref (self->priv->plugin)) : NULL); } MMBaseModem * mm_device_peek_modem (MMDevice *self) { return (self->priv->modem ? MM_BASE_MODEM (self->priv->modem) : NULL); } MMBaseModem * mm_device_get_modem (MMDevice *self) { return (self->priv->modem ? MM_BASE_MODEM (g_object_ref (self->priv->modem)) : NULL); } GObject * mm_device_peek_port_probe (MMDevice *self, MMKernelDevice *kernel_port) { MMPortProbe *probe; probe = device_find_probe_with_device (self, kernel_port, FALSE); return (probe ? G_OBJECT (probe) : NULL); } GObject * mm_device_get_port_probe (MMDevice *self, MMKernelDevice *kernel_port) { MMPortProbe *probe; probe = device_find_probe_with_device (self, kernel_port, FALSE); return (probe ? G_OBJECT (g_object_ref (probe)) : NULL); } GList * mm_device_peek_port_probe_list (MMDevice *self) { return self->priv->port_probes; } GList * mm_device_get_port_probe_list (MMDevice *self) { return g_list_copy_deep (self->priv->port_probes, (GCopyFunc)g_object_ref, NULL); } gboolean mm_device_get_hotplugged (MMDevice *self) { return self->priv->hotplugged; } gboolean mm_device_get_inhibited (MMDevice *self) { return self->priv->inhibited; } /*****************************************************************************/ gboolean mm_device_inhibit_finish (MMDevice *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void inhibit_disable_ready (MMBaseModem *modem, GAsyncResult *res, GTask *task) { MMDevice *self; GError *error = NULL; self = g_task_get_source_object (task); if (!mm_base_modem_disable_finish (modem, res, &error)) g_task_return_error (task, error); else { g_cancellable_cancel (mm_base_modem_peek_cancellable (modem)); mm_device_remove_modem (self); g_task_return_boolean (task, TRUE); } g_object_unref (task); } void mm_device_inhibit (MMDevice *self, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; task = g_task_new (self, NULL, callback, user_data); /* We want to allow inhibiting only devices that are currently * exported in the bus, because otherwise we may be inhibiting * in the middle of port probing and that may lead to some ports * tracked inside the device object during inhibition and some * other ports tracked in the base manager. So, if the device * does not have a valid modem created and exposed, do not allow * the inhibition. */ if (!self->priv->modem || !g_dbus_object_get_object_path (G_DBUS_OBJECT (self->priv->modem))) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_WRONG_STATE, "Modem not exported in the bus"); g_object_unref (task); return; } /* Flag as inhibited right away */ g_assert (!self->priv->inhibited); self->priv->inhibited = TRUE; /* Make sure modem is disabled while inhibited */ mm_base_modem_disable (self->priv->modem, (GAsyncReadyCallback)inhibit_disable_ready, task); } gboolean mm_device_uninhibit (MMDevice *self, GError **error) { g_assert (self->priv->inhibited); self->priv->inhibited = FALSE; return mm_device_create_modem (self, error); } /*****************************************************************************/ void mm_device_virtual_grab_ports (MMDevice *self, const gchar **ports) { g_return_if_fail (ports != NULL); g_return_if_fail (self->priv->virtual); /* Setup drivers array */ self->priv->drivers = g_malloc (2 * sizeof (gchar *)); self->priv->drivers[0] = g_strdup ("virtual"); self->priv->drivers[1] = NULL; /* Keep virtual port names */ self->priv->virtual_ports = g_strdupv ((gchar **)ports); } const gchar ** mm_device_virtual_peek_ports (MMDevice *self) { g_return_val_if_fail (self->priv->virtual, NULL); return (const gchar **)self->priv->virtual_ports; } gboolean mm_device_is_virtual (MMDevice *self) { return self->priv->virtual; } /*****************************************************************************/ static gchar * log_object_build_id (MMLogObject *_self) { MMDevice *self; self = MM_DEVICE (_self); return g_strdup_printf ("device %s", self->priv->uid); } /*****************************************************************************/ MMDevice * mm_device_new (const gchar *uid, const gchar *physdev, gboolean hotplugged, gboolean virtual, GDBusObjectManagerServer *object_manager) { g_return_val_if_fail (uid != NULL, NULL); return MM_DEVICE (g_object_new (MM_TYPE_DEVICE, MM_DEVICE_UID, uid, MM_DEVICE_PHYSDEV, physdev, MM_DEVICE_HOTPLUGGED, hotplugged, MM_DEVICE_VIRTUAL, virtual, MM_DEVICE_OBJECT_MANAGER, object_manager, NULL)); } static void mm_device_init (MMDevice *self) { /* Initialize private data */ self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, MM_TYPE_DEVICE, MMDevicePrivate); } static void set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { MMDevice *self = MM_DEVICE (object); switch (prop_id) { case PROP_UID: /* construct only */ self->priv->uid = g_value_dup_string (value); break; case PROP_PHYSDEV: /* construct only */ self->priv->physdev = g_value_dup_string (value); break; case PROP_OBJECT_MANAGER: /* construct only */ self->priv->object_manager = g_value_dup_object (value); break; case PROP_PLUGIN: g_clear_object (&(self->priv->plugin)); self->priv->plugin = g_value_dup_object (value); break; case PROP_MODEM: clear_modem (self); self->priv->modem = g_value_dup_object (value); break; case PROP_HOTPLUGGED: self->priv->hotplugged = g_value_get_boolean (value); break; case PROP_VIRTUAL: self->priv->virtual = g_value_get_boolean (value); break; case PROP_INHIBITED: self->priv->inhibited = 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) { MMDevice *self = MM_DEVICE (object); switch (prop_id) { case PROP_UID: g_value_set_string (value, self->priv->uid); break; case PROP_PHYSDEV: g_value_set_object (value, self->priv->physdev); break; case PROP_OBJECT_MANAGER: g_value_set_object (value, self->priv->object_manager); break; case PROP_PLUGIN: g_value_set_object (value, self->priv->plugin); break; case PROP_MODEM: g_value_set_object (value, self->priv->modem); break; case PROP_HOTPLUGGED: g_value_set_boolean (value, self->priv->hotplugged); break; case PROP_VIRTUAL: g_value_set_boolean (value, self->priv->virtual); break; case PROP_INHIBITED: g_value_set_boolean (value, self->priv->inhibited); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void dispose (GObject *object) { MMDevice *self = MM_DEVICE (object); if (self->priv->reprobe_id) { g_source_remove (self->priv->reprobe_id); self->priv->reprobe_id = 0; } g_clear_object (&(self->priv->object_manager)); g_clear_object (&(self->priv->plugin)); g_list_free_full (self->priv->port_probes, g_object_unref); self->priv->port_probes = NULL; g_list_free_full (self->priv->ignored_port_probes, g_object_unref); self->priv->ignored_port_probes = NULL; clear_modem (self); G_OBJECT_CLASS (mm_device_parent_class)->dispose (object); } static void finalize (GObject *object) { MMDevice *self = MM_DEVICE (object); g_free (self->priv->physdev); g_free (self->priv->uid); g_strfreev (self->priv->drivers); g_strfreev (self->priv->virtual_ports); G_OBJECT_CLASS (mm_device_parent_class)->finalize (object); } static void log_object_iface_init (MMLogObjectInterface *iface) { iface->build_id = log_object_build_id; } static void mm_device_class_init (MMDeviceClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); g_type_class_add_private (object_class, sizeof (MMDevicePrivate)); /* Virtual methods */ object_class->get_property = get_property; object_class->set_property = set_property; object_class->finalize = finalize; object_class->dispose = dispose; properties[PROP_UID] = g_param_spec_string (MM_DEVICE_UID, "Unique ID", "Unique device id, e.g. the physical device sysfs path", NULL, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY); g_object_class_install_property (object_class, PROP_UID, properties[PROP_UID]); properties[PROP_PHYSDEV] = g_param_spec_string (MM_DEVICE_PHYSDEV, "Physdev", "Physical device path", NULL, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY); g_object_class_install_property (object_class, PROP_PHYSDEV, properties[PROP_PHYSDEV]); properties[PROP_OBJECT_MANAGER] = g_param_spec_object (MM_DEVICE_OBJECT_MANAGER, "Object manager", "GDBus object manager server", G_TYPE_DBUS_OBJECT_MANAGER_SERVER, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY); g_object_class_install_property (object_class, PROP_OBJECT_MANAGER, properties[PROP_OBJECT_MANAGER]); properties[PROP_PLUGIN] = g_param_spec_object (MM_DEVICE_PLUGIN, "Plugin", "Best plugin to manage this device", MM_TYPE_PLUGIN, G_PARAM_READWRITE); g_object_class_install_property (object_class, PROP_PLUGIN, properties[PROP_PLUGIN]); properties[PROP_MODEM] = g_param_spec_object (MM_DEVICE_MODEM, "Modem", "The modem object", MM_TYPE_BASE_MODEM, G_PARAM_READWRITE); g_object_class_install_property (object_class, PROP_MODEM, properties[PROP_MODEM]); properties[PROP_HOTPLUGGED] = g_param_spec_boolean (MM_DEVICE_HOTPLUGGED, "Hotplugged", "Whether the modem was hotplugged", FALSE, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY); g_object_class_install_property (object_class, PROP_HOTPLUGGED, properties[PROP_HOTPLUGGED]); properties[PROP_VIRTUAL] = g_param_spec_boolean (MM_DEVICE_VIRTUAL, "Virtual", "Whether the device is virtual or real", FALSE, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY); g_object_class_install_property (object_class, PROP_VIRTUAL, properties[PROP_VIRTUAL]); properties[PROP_INHIBITED] = g_param_spec_boolean (MM_DEVICE_INHIBITED, "Inhibited", "Whether the modem is inhibited", FALSE, G_PARAM_READWRITE); g_object_class_install_property (object_class, PROP_INHIBITED, properties[PROP_INHIBITED]); signals[SIGNAL_PORT_GRABBED] = g_signal_new (MM_DEVICE_PORT_GRABBED, G_OBJECT_CLASS_TYPE (object_class), G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (MMDeviceClass, port_grabbed), NULL, NULL, g_cclosure_marshal_generic, G_TYPE_NONE, 1, MM_TYPE_KERNEL_DEVICE); signals[SIGNAL_PORT_RELEASED] = g_signal_new (MM_DEVICE_PORT_RELEASED, G_OBJECT_CLASS_TYPE (object_class), G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (MMDeviceClass, port_released), NULL, NULL, g_cclosure_marshal_generic, G_TYPE_NONE, 1, MM_TYPE_KERNEL_DEVICE); } ModemManager-1.23.4-dev/src/mm-device.h000066400000000000000000000132771456466623000175450ustar00rootroot00000000000000/* -*- 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) 2012 Google, Inc. */ #ifndef MM_DEVICE_H #define MM_DEVICE_H #include #include #include "mm-kernel-device.h" #include "mm-base-modem.h" #define MM_TYPE_DEVICE (mm_device_get_type ()) #define MM_DEVICE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_DEVICE, MMDevice)) #define MM_DEVICE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_DEVICE, MMDeviceClass)) #define MM_IS_DEVICE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_DEVICE)) #define MM_IS_DEVICE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_DEVICE)) #define MM_DEVICE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_DEVICE, MMDeviceClass)) typedef struct _MMDevice MMDevice; typedef struct _MMDeviceClass MMDeviceClass; typedef struct _MMDevicePrivate MMDevicePrivate; #define MM_DEVICE_UID "uid" #define MM_DEVICE_PHYSDEV "physdev" #define MM_DEVICE_PLUGIN "plugin" #define MM_DEVICE_MODEM "modem" #define MM_DEVICE_HOTPLUGGED "hotplugged" #define MM_DEVICE_VIRTUAL "virtual" #define MM_DEVICE_INHIBITED "inhibited" #define MM_DEVICE_OBJECT_MANAGER "object-manager" #define MM_DEVICE_PORT_GRABBED "port-grabbed" #define MM_DEVICE_PORT_RELEASED "port-released" struct _MMDevice { GObject parent; MMDevicePrivate *priv; }; struct _MMDeviceClass { GObjectClass parent; /* signals */ void (* port_grabbed) (MMDevice *self, MMKernelDevice *port); void (* port_released) (MMDevice *self, MMKernelDevice *port); }; GType mm_device_get_type (void); G_DEFINE_AUTOPTR_CLEANUP_FUNC (MMDevice, g_object_unref) MMDevice *mm_device_new (const gchar *uid, const gchar *physdev, gboolean hotplugged, gboolean virtual, GDBusObjectManagerServer *object_manager); void mm_device_grab_port (MMDevice *self, MMKernelDevice *kernel_port); gboolean mm_device_owns_port (MMDevice *self, MMKernelDevice *kernel_port); void mm_device_ignore_port (MMDevice *self, MMKernelDevice *kernel_port); gboolean mm_device_owns_port_name (MMDevice *self, const gchar *subsystem, const gchar *name); void mm_device_release_port_name (MMDevice *self, const gchar *subsystem, const gchar *name); gboolean mm_device_create_modem (MMDevice *self, GError **error); void mm_device_remove_modem (MMDevice *self); void mm_device_inhibit (MMDevice *self, GAsyncReadyCallback callback, gpointer user_data); gboolean mm_device_inhibit_finish (MMDevice *self, GAsyncResult *res, GError **error); gboolean mm_device_uninhibit (MMDevice *self, GError **error); const gchar *mm_device_get_uid (MMDevice *self); const gchar *mm_device_get_physdev (MMDevice *self); const gchar **mm_device_get_drivers (MMDevice *self); guint16 mm_device_get_vendor (MMDevice *self); guint16 mm_device_get_product (MMDevice *self); guint16 mm_device_get_subsystem_vendor (MMDevice *self); void mm_device_set_plugin (MMDevice *self, GObject *plugin); GObject *mm_device_peek_plugin (MMDevice *self); GObject *mm_device_get_plugin (MMDevice *self); MMBaseModem *mm_device_peek_modem (MMDevice *self); MMBaseModem *mm_device_get_modem (MMDevice *self); GObject *mm_device_peek_port_probe (MMDevice *self, MMKernelDevice *kernel_port); GObject *mm_device_get_port_probe (MMDevice *self, MMKernelDevice *kernel_port); GList *mm_device_peek_port_probe_list (MMDevice *self); GList *mm_device_get_port_probe_list (MMDevice *self); gboolean mm_device_get_hotplugged (MMDevice *self); gboolean mm_device_get_inhibited (MMDevice *self); /* For testing purposes */ void mm_device_virtual_grab_ports (MMDevice *self, const gchar **ports); const gchar **mm_device_virtual_peek_ports (MMDevice *self); gboolean mm_device_is_virtual (MMDevice *self); #endif /* MM_DEVICE_H */ ModemManager-1.23.4-dev/src/mm-dispatcher-connection.c000066400000000000000000000174331456466623000225620ustar00rootroot00000000000000/* -*- 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) 2022 Aleksander Morgado */ #include #include #include #include "mm-errors-types.h" #include "mm-utils.h" #include "mm-log-object.h" #include "mm-dispatcher-connection.h" #if !defined CONNECTIONDIRPACKAGE # error CONNECTIONDIRPACKAGE must be defined at build time #endif #if !defined CONNECTIONDIRUSER # error CONNECTIONDIRUSER must be defined at build time #endif #define OPERATION_DESCRIPTION "connection status report" #define CONNECTED_STRING "connected" #define DISCONNECTED_STRING "disconnected" /* Maximum time a connection dispatcher command is allowed to run before * us killing it */ #define MAX_CONNECTION_EXEC_TIME_SECS 5 struct _MMDispatcherConnection { MMDispatcher parent; }; struct _MMDispatcherConnectionClass { MMDispatcherClass parent; }; G_DEFINE_TYPE (MMDispatcherConnection, mm_dispatcher_connection, MM_TYPE_DISPATCHER) /*****************************************************************************/ typedef struct { gchar *modem_dbus_path; gchar *bearer_dbus_path; gchar *data_port; gboolean connected; GList *dispatcher_scripts; GFile *current; guint n_failures; } ConnectionRunContext; static void connection_run_context_free (ConnectionRunContext *ctx) { g_assert (!ctx->current); g_free (ctx->modem_dbus_path); g_free (ctx->bearer_dbus_path); g_free (ctx->data_port); g_list_free_full (ctx->dispatcher_scripts, (GDestroyNotify)g_object_unref); g_slice_free (ConnectionRunContext, ctx); } gboolean mm_dispatcher_connection_run_finish (MMDispatcherConnection *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void connection_run_next (GTask *task); static void dispatcher_run_ready (MMDispatcher *self, GAsyncResult *res, GTask *task) { ConnectionRunContext *ctx; g_autoptr(GError) error = NULL; ctx = g_task_get_task_data (task); if (!mm_dispatcher_run_finish (self, res, &error)) { ctx->n_failures++; mm_obj_warn (self, "Cannot run " OPERATION_DESCRIPTION " operation from %s: %s", g_file_peek_path (ctx->current), error->message); } else mm_obj_dbg (self, OPERATION_DESCRIPTION " operation successfully from %s", g_file_peek_path (ctx->current)); g_clear_object (&ctx->current); connection_run_next (task); } static void connection_run_next (GTask *task) { MMDispatcherConnection *self; ConnectionRunContext *ctx; g_autofree gchar *path = NULL; GPtrArray *aux; g_auto(GStrv) argv = NULL; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); if (!ctx->dispatcher_scripts) { if (ctx->n_failures) g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Failed %u " OPERATION_DESCRIPTION " operations", ctx->n_failures); else g_task_return_boolean (task, TRUE); g_object_unref (task); return; } /* store current file reference in context */ ctx->current = ctx->dispatcher_scripts->data; ctx->dispatcher_scripts = g_list_delete_link (ctx->dispatcher_scripts, ctx->dispatcher_scripts); path = g_file_get_path (ctx->current); /* build argv */ aux = g_ptr_array_new (); g_ptr_array_add (aux, g_steal_pointer (&path)); g_ptr_array_add (aux, g_strdup (ctx->modem_dbus_path)); g_ptr_array_add (aux, g_strdup (ctx->bearer_dbus_path)); g_ptr_array_add (aux, g_strdup (ctx->data_port)); g_ptr_array_add (aux, g_strdup (ctx->connected ? CONNECTED_STRING : DISCONNECTED_STRING)); g_ptr_array_add (aux, NULL); argv = (GStrv) g_ptr_array_free (aux, FALSE); /* run */ mm_dispatcher_run (MM_DISPATCHER (self), argv, MAX_CONNECTION_EXEC_TIME_SECS, g_task_get_cancellable (task), (GAsyncReadyCallback) dispatcher_run_ready, task); } static gint dispatcher_script_cmp (GFile *a, GFile *b) { g_autofree gchar *a_name = NULL; g_autofree gchar *b_name = NULL; a_name = g_file_get_basename (a); b_name = g_file_get_basename (b); return g_strcmp0 (a_name, b_name); } void mm_dispatcher_connection_run (MMDispatcherConnection *self, const gchar *modem_dbus_path, const gchar *bearer_dbus_path, const gchar *data_port, gboolean connected, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; ConnectionRunContext *ctx; guint i; const gchar *enabled_dirs[] = { CONNECTIONDIRUSER, /* sysconfdir */ CONNECTIONDIRPACKAGE, /* libdir */ }; task = g_task_new (self, cancellable, callback, user_data); ctx = g_slice_new0 (ConnectionRunContext); ctx->modem_dbus_path = g_strdup (modem_dbus_path); ctx->bearer_dbus_path = g_strdup (bearer_dbus_path); ctx->data_port = g_strdup (data_port); ctx->connected = connected; g_task_set_task_data (task, ctx, (GDestroyNotify)connection_run_context_free); /* Iterate over all enabled dirs and collect all dispatcher script paths */ for (i = 0; i < G_N_ELEMENTS (enabled_dirs); i++) { g_autoptr(GFile) dir_file = NULL; g_autoptr(GFileEnumerator) enumerator = NULL; GFile *child; dir_file = g_file_new_for_path (enabled_dirs[i]); enumerator = g_file_enumerate_children (dir_file, G_FILE_ATTRIBUTE_STANDARD_NAME, G_FILE_QUERY_INFO_NONE, cancellable, NULL); if (!enumerator) continue; while (g_file_enumerator_iterate (enumerator, NULL, &child, cancellable, NULL) && child) ctx->dispatcher_scripts = g_list_prepend (ctx->dispatcher_scripts, g_object_ref (child)); } /* Sort all by filename, regardless of the directory where they're in */ ctx->dispatcher_scripts = g_list_sort (ctx->dispatcher_scripts, (GCompareFunc)dispatcher_script_cmp); connection_run_next (task); } /*****************************************************************************/ static void mm_dispatcher_connection_init (MMDispatcherConnection *self) { } static void mm_dispatcher_connection_class_init (MMDispatcherConnectionClass *class) { } MM_DEFINE_SINGLETON_GETTER (MMDispatcherConnection, mm_dispatcher_connection_get, MM_TYPE_DISPATCHER_CONNECTION, MM_DISPATCHER_OPERATION_DESCRIPTION, OPERATION_DESCRIPTION) ModemManager-1.23.4-dev/src/mm-dispatcher-connection.h000066400000000000000000000057001456466623000225610ustar00rootroot00000000000000/* -*- 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) 2022 Aleksander Morgado */ #ifndef MM_DISPATCHER_CONNECTION_H #define MM_DISPATCHER_CONNECTION_H #include #include #include "mm-dispatcher.h" #define MM_TYPE_DISPATCHER_CONNECTION (mm_dispatcher_connection_get_type ()) #define MM_DISPATCHER_CONNECTION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_DISPATCHER_CONNECTION, MMDispatcherConnection)) #define MM_DISPATCHER_CONNECTION_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_DISPATCHER_CONNECTION, MMDispatcherConnectionClass)) #define MM_IS_DISPATCHER_CONNECTION(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_DISPATCHER_CONNECTION)) #define MM_IS_DISPATCHER_CONNECTION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_DISPATCHER_CONNECTION)) #define MM_DISPATCHER_CONNECTION_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_DISPATCHER_CONNECTION, MMDispatcherConnectionClass)) typedef struct _MMDispatcherConnection MMDispatcherConnection; typedef struct _MMDispatcherConnectionClass MMDispatcherConnectionClass; typedef struct _MMDispatcherConnectionPrivate MMDispatcherConnectionPrivate; GType mm_dispatcher_connection_get_type (void); MMDispatcherConnection *mm_dispatcher_connection_get (void); void mm_dispatcher_connection_run (MMDispatcherConnection *self, const gchar *modem_dbus_path, const gchar *bearer_dbus_path, const gchar *data_port, gboolean connected, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); gboolean mm_dispatcher_connection_run_finish (MMDispatcherConnection *self, GAsyncResult *res, GError **error); #endif /* MM_DISPATCHER_CONNECTION_H */ ModemManager-1.23.4-dev/src/mm-dispatcher-fcc-unlock.c000066400000000000000000000115601456466623000224420ustar00rootroot00000000000000/* -*- 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) 2021-2022 Aleksander Morgado */ #include #include #include #include "mm-errors-types.h" #include "mm-utils.h" #include "mm-log-object.h" #include "mm-dispatcher-fcc-unlock.h" #if !defined FCCUNLOCKDIRPACKAGE # error FCCUNLOCKDIRPACKAGE must be defined at build time #endif #if !defined FCCUNLOCKDIRUSER # error FCCUNLOCKDIRUSER must be defined at build time #endif #define OPERATION_DESCRIPTION "fcc unlock" /* Maximum time a FCC unlock command is allowed to run before * us killing it */ #define MAX_FCC_UNLOCK_EXEC_TIME_SECS 5 struct _MMDispatcherFccUnlock { MMDispatcher parent; }; struct _MMDispatcherFccUnlockClass { MMDispatcherClass parent; }; G_DEFINE_TYPE (MMDispatcherFccUnlock, mm_dispatcher_fcc_unlock, MM_TYPE_DISPATCHER) /*****************************************************************************/ gboolean mm_dispatcher_fcc_unlock_run_finish (MMDispatcherFccUnlock *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void dispatcher_run_ready (MMDispatcher *self, GAsyncResult *res, GTask *task) { GError *error = NULL; if (!mm_dispatcher_run_finish (self, res, &error)) g_task_return_error (task, error); else g_task_return_boolean (task, TRUE); g_object_unref (task); } void mm_dispatcher_fcc_unlock_run (MMDispatcherFccUnlock *self, guint vid, guint pid, const gchar *modem_dbus_path, const GStrv modem_ports, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; guint i; g_autofree gchar *filename = NULL; const gchar *enabled_dirs[] = { FCCUNLOCKDIRUSER, /* sysconfdir */ FCCUNLOCKDIRPACKAGE, /* libdir */ }; task = g_task_new (self, cancellable, callback, user_data); filename = g_strdup_printf ("%04x:%04x", vid, pid); for (i = 0; i < G_N_ELEMENTS (enabled_dirs); i++) { GPtrArray *aux; g_auto(GStrv) argv = NULL; g_autofree gchar *path = NULL; g_autoptr(GFile) file = NULL; guint j; path = g_build_path (G_DIR_SEPARATOR_S, enabled_dirs[i], filename, NULL); file = g_file_new_for_path (path); /* If file exists, we attempt to use it */ if (!g_file_query_exists (file, cancellable)) { mm_obj_dbg (self, "Cannot run " OPERATION_DESCRIPTION " operation from %s: file doesn't exist", path); continue; } /* build argv */ aux = g_ptr_array_new (); g_ptr_array_add (aux, g_steal_pointer (&path)); g_ptr_array_add (aux, g_strdup (modem_dbus_path)); for (j = 0; modem_ports && modem_ports[j]; j++) g_ptr_array_add (aux, g_strdup (modem_ports[j])); g_ptr_array_add (aux, NULL); argv = (GStrv) g_ptr_array_free (aux, FALSE); /* run */ mm_dispatcher_run (MM_DISPATCHER (self), argv, MAX_FCC_UNLOCK_EXEC_TIME_SECS, cancellable, (GAsyncReadyCallback) dispatcher_run_ready, task); return; } g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, OPERATION_DESCRIPTION " operation launch aborted: no valid program found"); g_object_unref (task); } /*****************************************************************************/ static void mm_dispatcher_fcc_unlock_init (MMDispatcherFccUnlock *self) { } static void mm_dispatcher_fcc_unlock_class_init (MMDispatcherFccUnlockClass *class) { } MM_DEFINE_SINGLETON_GETTER (MMDispatcherFccUnlock, mm_dispatcher_fcc_unlock_get, MM_TYPE_DISPATCHER_FCC_UNLOCK, MM_DISPATCHER_OPERATION_DESCRIPTION, OPERATION_DESCRIPTION) ModemManager-1.23.4-dev/src/mm-dispatcher-fcc-unlock.h000066400000000000000000000056271456466623000224560ustar00rootroot00000000000000/* -*- 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) 2021-2022 Aleksander Morgado */ #ifndef MM_DISPATCHER_FCC_UNLOCK_H #define MM_DISPATCHER_FCC_UNLOCK_H #include #include #include "mm-dispatcher.h" #define MM_TYPE_DISPATCHER_FCC_UNLOCK (mm_dispatcher_fcc_unlock_get_type ()) #define MM_DISPATCHER_FCC_UNLOCK(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_DISPATCHER_FCC_UNLOCK, MMDispatcherFccUnlock)) #define MM_DISPATCHER_FCC_UNLOCK_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_DISPATCHER_FCC_UNLOCK, MMDispatcherFccUnlockClass)) #define MM_IS_DISPATCHER_FCC_UNLOCK(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_DISPATCHER_FCC_UNLOCK)) #define MM_IS_DISPATCHER_FCC_UNLOCK_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_DISPATCHER_FCC_UNLOCK)) #define MM_DISPATCHER_FCC_UNLOCK_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_DISPATCHER_FCC_UNLOCK, MMDispatcherFccUnlockClass)) typedef struct _MMDispatcherFccUnlock MMDispatcherFccUnlock; typedef struct _MMDispatcherFccUnlockClass MMDispatcherFccUnlockClass; typedef struct _MMDispatcherFccUnlockPrivate MMDispatcherFccUnlockPrivate; GType mm_dispatcher_fcc_unlock_get_type (void); MMDispatcherFccUnlock *mm_dispatcher_fcc_unlock_get (void); void mm_dispatcher_fcc_unlock_run (MMDispatcherFccUnlock *self, guint vid, guint pid, const gchar *modem_dbus_path, const GStrv ports, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); gboolean mm_dispatcher_fcc_unlock_run_finish (MMDispatcherFccUnlock *self, GAsyncResult *res, GError **error); #endif /* MM_DISPATCHER_FCC_UNLOCK_H */ ModemManager-1.23.4-dev/src/mm-dispatcher.c000066400000000000000000000266611456466623000204300ustar00rootroot00000000000000/* -*- 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) 2021-2022 Aleksander Morgado */ #include #include #include #include "mm-errors-types.h" #include "mm-utils.h" #include "mm-log-object.h" #include "mm-dispatcher.h" static void log_object_iface_init (MMLogObjectInterface *iface); G_DEFINE_TYPE_EXTENDED (MMDispatcher, mm_dispatcher, G_TYPE_OBJECT, 0, G_IMPLEMENT_INTERFACE (MM_TYPE_LOG_OBJECT, log_object_iface_init)) enum { PROP_0, PROP_OPERATION_DESCRIPTION, PROP_LAST }; static GParamSpec *properties[PROP_LAST]; struct _MMDispatcherPrivate { gchar *operation_description; GSubprocessLauncher *launcher; }; /*****************************************************************************/ static gchar * log_object_build_id (MMLogObject *_self) { MMDispatcher *self = MM_DISPATCHER (_self); return g_strdup_printf ("%s dispatcher", self->priv->operation_description); } /*****************************************************************************/ static gboolean validate_file (const gchar *path, GError **error) { g_autoptr(GFile) file = NULL; g_autoptr(GFileInfo) file_info = NULL; guint32 file_mode; guint32 file_uid; file = g_file_new_for_path (path); file_info = g_file_query_info (file, (G_FILE_ATTRIBUTE_STANDARD_SIZE "," G_FILE_ATTRIBUTE_STANDARD_SYMLINK_TARGET "," G_FILE_ATTRIBUTE_STANDARD_IS_SYMLINK "," G_FILE_ATTRIBUTE_UNIX_MODE "," G_FILE_ATTRIBUTE_UNIX_UID), G_FILE_QUERY_INFO_NONE, NULL, error); if (!file_info) return FALSE; if (g_file_info_get_is_symlink (file_info)) { const gchar *link_target; link_target = g_file_info_get_symlink_target (file_info); if (g_strcmp0 (link_target, "/dev/null") == 0) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_ABORTED, "Link '%s' to /dev/null is not executable", path); return FALSE; } } if (g_file_info_get_size (file_info) == 0) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_ABORTED, "File '%s' is empty", path); return FALSE; } file_uid = g_file_info_get_attribute_uint32 (file_info, G_FILE_ATTRIBUTE_UNIX_UID); if (file_uid != 0) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_ABORTED, "File '%s' not owned by root", path); return FALSE; } file_mode = g_file_info_get_attribute_uint32 (file_info, G_FILE_ATTRIBUTE_UNIX_MODE); if (!S_ISREG (file_mode)) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_ABORTED, "File '%s' is not regular", path); return FALSE; } if (file_mode & (S_IWGRP | S_IWOTH)) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_ABORTED, "File '%s' is writable by group or other", path); return FALSE; } if (file_mode & S_ISUID) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_ABORTED, "File '%s' is set-UID", path); return FALSE; } if (!(file_mode & S_IXUSR)) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_ABORTED, "File '%s' is not executable by the owner", path); return FALSE; } return TRUE; } /*****************************************************************************/ typedef struct { GSubprocess *subprocess; guint timeout_id; } RunContext; static void run_context_free (RunContext *ctx) { g_assert (!ctx->timeout_id); g_clear_object (&ctx->subprocess); g_slice_free (RunContext, ctx); } gboolean mm_dispatcher_run_finish (MMDispatcher *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static gboolean subprocess_wait_timed_out (GTask *task) { MMDispatcher *self; RunContext *ctx; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); mm_obj_warn (self, "forcing exit on %s operation", self->priv->operation_description); g_subprocess_force_exit (ctx->subprocess); ctx->timeout_id = 0; return G_SOURCE_REMOVE; } static void subprocess_wait_ready (GSubprocess *subprocess, GAsyncResult *res, GTask *task) { GError *error = NULL; MMDispatcher *self; RunContext *ctx; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); /* cleanup timeout before any return */ if (ctx->timeout_id) { g_source_remove (ctx->timeout_id); ctx->timeout_id = 0; } if (!g_subprocess_wait_finish (subprocess, res, &error)) { g_prefix_error (&error, "%s operation wait failed: ", self->priv->operation_description); g_task_return_error (task, error); } else if (!g_subprocess_get_successful (subprocess)) { if (g_subprocess_get_if_signaled (subprocess)) g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "%s operation aborted with signal %d", self->priv->operation_description, g_subprocess_get_term_sig (subprocess)); else if (g_subprocess_get_if_exited (subprocess)) g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "%s operation finished with status %d", self->priv->operation_description, g_subprocess_get_exit_status (subprocess)); else g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "%s operation failed", self->priv->operation_description); } else g_task_return_boolean (task, TRUE); g_object_unref (task); } void mm_dispatcher_run (MMDispatcher *self, const GStrv argv, guint timeout_secs, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; RunContext *ctx; GError *error = NULL; g_assert (g_strv_length (argv) >= 1); task = g_task_new (self, cancellable, callback, user_data); ctx = g_slice_new0 (RunContext); g_task_set_task_data (task, ctx, (GDestroyNotify) run_context_free); /* Validation checks to see if we should run it or not */ if (!validate_file (argv[0], &error)) { g_prefix_error (&error, "Cannot run %s operation from %s: ", self->priv->operation_description, argv[0]); g_task_return_error (task, error); g_object_unref (task); return; } /* create and launch subprocess */ ctx->subprocess = g_subprocess_launcher_spawnv (self->priv->launcher, (const gchar * const *)argv, &error); if (!ctx->subprocess) { g_prefix_error (&error, "%s operation launch from %s failed: ", self->priv->operation_description, argv[0]); g_task_return_error (task, error); g_object_unref (task); return; } /* setup timeout */ ctx->timeout_id = g_timeout_add_seconds (timeout_secs, (GSourceFunc)subprocess_wait_timed_out, task); /* wait for subprocess exit */ g_subprocess_wait_async (ctx->subprocess, cancellable, (GAsyncReadyCallback)subprocess_wait_ready, task); } /*****************************************************************************/ static void mm_dispatcher_init (MMDispatcher *self) { /* Initialize private data */ self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, MM_TYPE_DISPATCHER, MMDispatcherPrivate); /* Create launcher and inherit parent's environment */ self->priv->launcher = g_subprocess_launcher_new (G_SUBPROCESS_FLAGS_STDOUT_SILENCE | G_SUBPROCESS_FLAGS_STDERR_SILENCE); g_subprocess_launcher_set_environ (self->priv->launcher, NULL); } static void set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { MMDispatcher *self = MM_DISPATCHER (object); switch (prop_id) { case PROP_OPERATION_DESCRIPTION: /* construct only */ self->priv->operation_description = 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) { MMDispatcher *self = MM_DISPATCHER (object); switch (prop_id) { case PROP_OPERATION_DESCRIPTION: g_value_set_string (value, self->priv->operation_description); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void dispose (GObject *object) { MMDispatcher *self = MM_DISPATCHER (object); g_clear_object (&self->priv->launcher); G_OBJECT_CLASS (mm_dispatcher_parent_class)->dispose (object); } static void finalize (GObject *object) { MMDispatcher *self = MM_DISPATCHER (object); g_free (self->priv->operation_description); G_OBJECT_CLASS (mm_dispatcher_parent_class)->finalize (object); } static void log_object_iface_init (MMLogObjectInterface *iface) { iface->build_id = log_object_build_id; } static void mm_dispatcher_class_init (MMDispatcherClass *class) { GObjectClass *object_class = G_OBJECT_CLASS (class); g_type_class_add_private (object_class, sizeof (MMDispatcherPrivate)); object_class->get_property = get_property; object_class->set_property = set_property; object_class->dispose = dispose; object_class->finalize = finalize; properties[PROP_OPERATION_DESCRIPTION] = g_param_spec_string (MM_DISPATCHER_OPERATION_DESCRIPTION, "Operation description", "String describing the operation, to be used in logging", NULL, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY); g_object_class_install_property (object_class, PROP_OPERATION_DESCRIPTION, properties[PROP_OPERATION_DESCRIPTION]); } ModemManager-1.23.4-dev/src/mm-dispatcher.h000066400000000000000000000046051456466623000204270ustar00rootroot00000000000000/* -*- 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) 2021-2022 Aleksander Morgado */ #ifndef MM_DISPATCHER_H #define MM_DISPATCHER_H #include #include #define MM_TYPE_DISPATCHER (mm_dispatcher_get_type ()) #define MM_DISPATCHER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_DISPATCHER, MMDispatcher)) #define MM_DISPATCHER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_DISPATCHER, MMDispatcherClass)) #define MM_IS_DISPATCHER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_DISPATCHER)) #define MM_IS_DISPATCHER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_DISPATCHER)) #define MM_DISPATCHER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_DISPATCHER, MMDispatcherClass)) typedef struct _MMDispatcher MMDispatcher; typedef struct _MMDispatcherClass MMDispatcherClass; typedef struct _MMDispatcherPrivate MMDispatcherPrivate; #define MM_DISPATCHER_OPERATION_DESCRIPTION "operation-description" struct _MMDispatcher { GObject parent; MMDispatcherPrivate *priv; }; struct _MMDispatcherClass { GObjectClass parent; }; GType mm_dispatcher_get_type (void); G_DEFINE_AUTOPTR_CLEANUP_FUNC (MMDispatcher, g_object_unref) void mm_dispatcher_run (MMDispatcher *self, const GStrv argv, guint timeout_secs, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); gboolean mm_dispatcher_run_finish (MMDispatcher *self, GAsyncResult *res, GError **error); #endif /* MM_DISPATCHER_H */ ModemManager-1.23.4-dev/src/mm-error-helpers.c000066400000000000000000001243041456466623000210640ustar00rootroot00000000000000/* -*- 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 - 2012 Red Hat, Inc. * Copyright (C) 2011 - 2012 Google, Inc. */ #include #include "mm-error-helpers.h" #include "mm-errors-types.h" #include "mm-log.h" #include #if defined WITH_QMI # include # include "mm-modem-helpers-qmi.h" #endif #if defined WITH_MBIM # include # include "mm-modem-helpers-mbim.h" #endif /******************************************************************************/ static gchar * normalize_error_string (const gchar *str) { gchar *buf = NULL; guint i; guint j; /* Normalize the error code by stripping whitespace and odd characters */ buf = g_strdup (str); for (i = 0, j = 0; str[i]; i++) { if (isalnum (str[i])) buf[j++] = tolower (str[i]); } buf[j] = '\0'; return buf; } /******************************************************************************/ /* Core errors */ /* Human friendly messages for each error type */ static const gchar *core_error_messages[] = { [MM_CORE_ERROR_FAILED] = "Failed", [MM_CORE_ERROR_CANCELLED] = "Cancelled", [MM_CORE_ERROR_ABORTED] = "Aborted", [MM_CORE_ERROR_UNSUPPORTED] = "Unsupported", [MM_CORE_ERROR_NO_PLUGINS] = "No plugins", [MM_CORE_ERROR_UNAUTHORIZED] = "Unauthorized", [MM_CORE_ERROR_INVALID_ARGS] = "Invalid arguments", [MM_CORE_ERROR_IN_PROGRESS] = "In progress", [MM_CORE_ERROR_WRONG_STATE] = "Wrong state", [MM_CORE_ERROR_CONNECTED] = "Connected", [MM_CORE_ERROR_TOO_MANY] = "Too many", [MM_CORE_ERROR_NOT_FOUND] = "Not found", [MM_CORE_ERROR_RETRY] = "Retry", [MM_CORE_ERROR_EXISTS] = "Exists", [MM_CORE_ERROR_WRONG_SIM_STATE] = "Wrong SIM state", [MM_CORE_ERROR_RESET_AND_RETRY] = "Reset and retry", [MM_CORE_ERROR_TIMEOUT] = "Timed out", [MM_CORE_ERROR_PROTOCOL] = "Protocol failure", [MM_CORE_ERROR_THROTTLED] = "Throttled", }; static const gchar * core_error_get_string (MMCoreError code) { return (code < G_N_ELEMENTS (core_error_messages)) ? core_error_messages[code] : NULL; } /******************************************************************************/ /* Connection errors */ /* Human friendly messages for each error type */ static const gchar *connection_error_messages[] = { [MM_CONNECTION_ERROR_UNKNOWN] = "Unknown", [MM_CONNECTION_ERROR_NO_CARRIER] = "No carrier", [MM_CONNECTION_ERROR_NO_DIALTONE] = "No dialtone", [MM_CONNECTION_ERROR_BUSY] = "Busy", [MM_CONNECTION_ERROR_NO_ANSWER] = "No answer", }; static const gchar * connection_error_get_string (MMConnectionError code) { return (code < G_N_ELEMENTS (connection_error_messages)) ? connection_error_messages[code] : NULL; } GError * mm_connection_error_for_code (MMConnectionError code, gpointer log_object) { const gchar *description; description = connection_error_get_string (code); if (description) return g_error_new_literal (MM_CONNECTION_ERROR, code, description); /* Not found? Then, default to 'no carrier' */ mm_obj_dbg (log_object, "unknown connection error: %u", code); return g_error_new (MM_CONNECTION_ERROR, MM_CONNECTION_ERROR_NO_CARRIER, "Unknown connection error: %u", code); } /******************************************************************************/ /* Mobile equipment errors */ /* Human friendly messages for each error type */ static const gchar *me_error_messages[] = { [MM_MOBILE_EQUIPMENT_ERROR_PHONE_FAILURE] = "Phone failure", [MM_MOBILE_EQUIPMENT_ERROR_NO_CONNECTION] = "No connection to phone", [MM_MOBILE_EQUIPMENT_ERROR_LINK_RESERVED] = "Phone-adaptor link reserved", [MM_MOBILE_EQUIPMENT_ERROR_NOT_ALLOWED] = "Operation not allowed", [MM_MOBILE_EQUIPMENT_ERROR_NOT_SUPPORTED] = "Operation not supported", [MM_MOBILE_EQUIPMENT_ERROR_PH_SIM_PIN] = "PH-SIM PIN required", [MM_MOBILE_EQUIPMENT_ERROR_PH_FSIM_PIN] = "PH-FSIM PIN required", [MM_MOBILE_EQUIPMENT_ERROR_PH_FSIM_PUK] = "PH-FSIM PUK required", [MM_MOBILE_EQUIPMENT_ERROR_SIM_NOT_INSERTED] = "SIM not inserted", [MM_MOBILE_EQUIPMENT_ERROR_SIM_PIN] = "SIM PIN required", [MM_MOBILE_EQUIPMENT_ERROR_SIM_PUK] = "SIM PUK required", [MM_MOBILE_EQUIPMENT_ERROR_SIM_FAILURE] = "SIM failure", [MM_MOBILE_EQUIPMENT_ERROR_SIM_BUSY] = "SIM busy", [MM_MOBILE_EQUIPMENT_ERROR_SIM_WRONG] = "SIM wrong", [MM_MOBILE_EQUIPMENT_ERROR_INCORRECT_PASSWORD] = "Incorrect password", [MM_MOBILE_EQUIPMENT_ERROR_SIM_PIN2] = "SIM PIN2 required", [MM_MOBILE_EQUIPMENT_ERROR_SIM_PUK2] = "SIM PUK2 required", [MM_MOBILE_EQUIPMENT_ERROR_MEMORY_FULL] = "Memory full", [MM_MOBILE_EQUIPMENT_ERROR_INVALID_INDEX] = "Invalid index", [MM_MOBILE_EQUIPMENT_ERROR_NOT_FOUND] = "Not found", [MM_MOBILE_EQUIPMENT_ERROR_MEMORY_FAILURE] = "Memory failure", [MM_MOBILE_EQUIPMENT_ERROR_TEXT_TOO_LONG] = "Text string too long", [MM_MOBILE_EQUIPMENT_ERROR_INVALID_CHARS] = "Invalid characters in text string", [MM_MOBILE_EQUIPMENT_ERROR_DIAL_STRING_TOO_LONG] = "Dial string too long", [MM_MOBILE_EQUIPMENT_ERROR_DIAL_STRING_INVALID] = "Invalid characters in dial string", [MM_MOBILE_EQUIPMENT_ERROR_NO_NETWORK] = "No network service", [MM_MOBILE_EQUIPMENT_ERROR_NETWORK_TIMEOUT] = "Network timeout", [MM_MOBILE_EQUIPMENT_ERROR_NETWORK_NOT_ALLOWED] = "Network not allowed - emergency calls only", [MM_MOBILE_EQUIPMENT_ERROR_NETWORK_PIN] = "Network personalization PIN required", [MM_MOBILE_EQUIPMENT_ERROR_NETWORK_PUK] = "Network personalization PUK required", [MM_MOBILE_EQUIPMENT_ERROR_NETWORK_SUBSET_PIN] = "Network subset personalization PIN required", [MM_MOBILE_EQUIPMENT_ERROR_NETWORK_SUBSET_PUK] = "Network subset personalization PUK required", [MM_MOBILE_EQUIPMENT_ERROR_SERVICE_PIN] = "Service provider personalization PIN required", [MM_MOBILE_EQUIPMENT_ERROR_SERVICE_PUK] = "Service provider personalization PUK required", [MM_MOBILE_EQUIPMENT_ERROR_CORP_PIN] = "Corporate personalization PIN required", [MM_MOBILE_EQUIPMENT_ERROR_CORP_PUK] = "Corporate personalization PUK required", [MM_MOBILE_EQUIPMENT_ERROR_HIDDEN_KEY_REQUIRED] = "Hidden key required", [MM_MOBILE_EQUIPMENT_ERROR_EAP_METHOD_NOT_SUPPORTED] = "EAP method not supported", [MM_MOBILE_EQUIPMENT_ERROR_INCORRECT_PARAMETERS] = "Incorrect parameters", [MM_MOBILE_EQUIPMENT_ERROR_COMMAND_DISABLED] = "Command disabled", [MM_MOBILE_EQUIPMENT_ERROR_COMMAND_ABORTED] = "Command aborted", [MM_MOBILE_EQUIPMENT_ERROR_NOT_ATTACHED_RESTRICTED] = "Not attached restricted", [MM_MOBILE_EQUIPMENT_ERROR_NOT_ALLOWED_EMERGENCY_ONLY] = "Not allowed emergency only", [MM_MOBILE_EQUIPMENT_ERROR_NOT_ALLOWED_RESTRICTED] = "Not allowed restricted", [MM_MOBILE_EQUIPMENT_ERROR_FIXED_DIAL_NUMBER_ONLY] = "Fixed dial number only", [MM_MOBILE_EQUIPMENT_ERROR_TEMPORARILY_OUT_OF_SERVICE] = "Temporarily out of service", [MM_MOBILE_EQUIPMENT_ERROR_LANGUAGE_OR_ALPHABET_NOT_SUPPORTED] = "Language or alphabet not supported", [MM_MOBILE_EQUIPMENT_ERROR_UNEXPECTED_DATA_VALUE] = "Unexpected data value", [MM_MOBILE_EQUIPMENT_ERROR_SYSTEM_FAILURE] = "System failure", [MM_MOBILE_EQUIPMENT_ERROR_DATA_MISSING] = "Data missing", [MM_MOBILE_EQUIPMENT_ERROR_CALL_BARRED] = "Call barred", [MM_MOBILE_EQUIPMENT_ERROR_MESSAGE_WAITING_INDICATION_SUBSCRIPTION_FAILURE] = "Message waiting indication subscription failure", [MM_MOBILE_EQUIPMENT_ERROR_UNKNOWN] = "Unknown error", [MM_MOBILE_EQUIPMENT_ERROR_IMSI_UNKNOWN_IN_HSS] = "IMSI unknown in HLR/HSS", [MM_MOBILE_EQUIPMENT_ERROR_ILLEGAL_UE] = "Illegal MS/UE", [MM_MOBILE_EQUIPMENT_ERROR_IMSI_UNKNOWN_IN_VLR] = "IMSI unknown in VLR", [MM_MOBILE_EQUIPMENT_ERROR_IMEI_NOT_ACCEPTED] = "IMEI not accepted", [MM_MOBILE_EQUIPMENT_ERROR_ILLEGAL_ME] = "Illegal ME", [MM_MOBILE_EQUIPMENT_ERROR_PS_SERVICES_NOT_ALLOWED] = "PS services not allowed", [MM_MOBILE_EQUIPMENT_ERROR_PS_AND_NON_PS_SERVICES_NOT_ALLOWED] = "PS and non-PS services not allowed", [MM_MOBILE_EQUIPMENT_ERROR_UE_IDENTITY_NOT_DERIVED_FROM_NETWORK] = "UE identity not derived from network", [MM_MOBILE_EQUIPMENT_ERROR_IMPLICITLY_DETACHED] = "Implicitly detached", [MM_MOBILE_EQUIPMENT_ERROR_PLMN_NOT_ALLOWED] = "PLMN not allowed", [MM_MOBILE_EQUIPMENT_ERROR_AREA_NOT_ALLOWED] = "Location/tracking area not allowed", [MM_MOBILE_EQUIPMENT_ERROR_ROAMING_NOT_ALLOWED_IN_AREA] = "Roaming not allowed in this location/tracking area", [MM_MOBILE_EQUIPMENT_ERROR_PS_SERVICES_NOT_ALLOWED_IN_PLMN] = "PS services not allowed in PLMN", [MM_MOBILE_EQUIPMENT_ERROR_NO_CELLS_IN_AREA] = "No cells in location/tracking area", [MM_MOBILE_EQUIPMENT_ERROR_MSC_TEMPORARILY_NOT_REACHABLE] = "MSC temporarily not reachable", [MM_MOBILE_EQUIPMENT_ERROR_NETWORK_FAILURE_ATTACH] = "Network failure (attach)", [MM_MOBILE_EQUIPMENT_ERROR_CS_DOMAIN_UNAVAILABLE] = "CS domain unavailable", [MM_MOBILE_EQUIPMENT_ERROR_ESM_FAILURE] = "ESM failure", [MM_MOBILE_EQUIPMENT_ERROR_CONGESTION] = "Congestion", [MM_MOBILE_EQUIPMENT_ERROR_MBMS_BEARER_CAPABILITIES_INSUFFICIENT_FOR_SERVICE] = "MBMS bearer capabilities insufficient for service", [MM_MOBILE_EQUIPMENT_ERROR_NOT_AUTHORIZED_FOR_CSG] = "Not authorized for CSG", [MM_MOBILE_EQUIPMENT_ERROR_INSUFFICIENT_RESOURCES] = "Insufficient resources", [MM_MOBILE_EQUIPMENT_ERROR_MISSING_OR_UNKNOWN_APN] = "Missing or unknown APN", [MM_MOBILE_EQUIPMENT_ERROR_UNKNOWN_PDP_ADDRESS_OR_TYPE] = "Unknown PDP address or type", [MM_MOBILE_EQUIPMENT_ERROR_USER_AUTHENTICATION_FAILED] = "User authentication failed", [MM_MOBILE_EQUIPMENT_ERROR_ACTIVATION_REJECTED_BY_GGSN_OR_GW] = "Activation rejected by GGSN or GW", [MM_MOBILE_EQUIPMENT_ERROR_ACTIVATION_REJECTED_UNSPECIFIED] = "Activation rejected (unspecified)", [MM_MOBILE_EQUIPMENT_ERROR_SERVICE_OPTION_NOT_SUPPORTED] = "Service option not supported", [MM_MOBILE_EQUIPMENT_ERROR_SERVICE_OPTION_NOT_SUBSCRIBED] = "Requested service option not subscribed", [MM_MOBILE_EQUIPMENT_ERROR_SERVICE_OPTION_OUT_OF_ORDER] = "Service option temporarily out of order", [MM_MOBILE_EQUIPMENT_ERROR_NSAPI_OR_PTI_ALREADY_IN_USE] = "NSAPI/PTI already in use", [MM_MOBILE_EQUIPMENT_ERROR_REGULAR_DEACTIVATION] = "Regular deactivation", [MM_MOBILE_EQUIPMENT_ERROR_QOS_NOT_ACCEPTED] = "QoS not accepted", [MM_MOBILE_EQUIPMENT_ERROR_CALL_CANNOT_BE_IDENTIFIED] = "Call cannot be identified", [MM_MOBILE_EQUIPMENT_ERROR_CS_SERVICE_TEMPORARILY_UNAVAILABLE] = "CS service temporarily unavailable", [MM_MOBILE_EQUIPMENT_ERROR_FEATURE_NOT_SUPPORTED] = "Feature not supported", [MM_MOBILE_EQUIPMENT_ERROR_SEMANTIC_ERROR_IN_TFT_OPERATION] = "Semantic error in TFT operation", [MM_MOBILE_EQUIPMENT_ERROR_SYNTACTICAL_ERROR_IN_TFT_OPERATION] = "Syntactical error in TFT operation", [MM_MOBILE_EQUIPMENT_ERROR_UNKNOWN_PDP_CONTEXT] = "Unknown PDP context", [MM_MOBILE_EQUIPMENT_ERROR_SEMANTIC_ERRORS_IN_PACKET_FILTER] = "Semantic error in packet filter", [MM_MOBILE_EQUIPMENT_ERROR_SYNTACTICAL_ERROR_IN_PACKET_FILTER] = "Syntactical error in packet filter", [MM_MOBILE_EQUIPMENT_ERROR_PDP_CONTEXT_WITHOUT_TFT_ALREADY_ACTIVATED] = "PDP context without TFT already activated", [MM_MOBILE_EQUIPMENT_ERROR_MULTICAST_GROUP_MEMBERSHIP_TIMEOUT] = "Multicast group membership timeout", [MM_MOBILE_EQUIPMENT_ERROR_GPRS_UNKNOWN] = "Unspecified GPRS error", [MM_MOBILE_EQUIPMENT_ERROR_PDP_AUTH_FAILURE] = "PDP authentication failure", [MM_MOBILE_EQUIPMENT_ERROR_INVALID_MOBILE_CLASS] = "Invalid mobile class", [MM_MOBILE_EQUIPMENT_ERROR_LAST_PDN_DISCONNECTION_NOT_ALLOWED_LEGACY] = "Last PDN disconnection not allowed (legacy)", [MM_MOBILE_EQUIPMENT_ERROR_LAST_PDN_DISCONNECTION_NOT_ALLOWED] = "Last PDN disconnection not allowed", [MM_MOBILE_EQUIPMENT_ERROR_SEMANTICALLY_INCORRECT_MESSAGE] = "Semantically incorrect message", [MM_MOBILE_EQUIPMENT_ERROR_INVALID_MANDATORY_INFORMATION] = "Invalid mandatory information", [MM_MOBILE_EQUIPMENT_ERROR_MESSAGE_TYPE_NOT_IMPLEMENTED] = "Message type not implemented", [MM_MOBILE_EQUIPMENT_ERROR_CONDITIONAL_IE_ERROR] = "Conditional IE error", [MM_MOBILE_EQUIPMENT_ERROR_UNSPECIFIED_PROTOCOL_ERROR] = "Unspecified protocol error", [MM_MOBILE_EQUIPMENT_ERROR_OPERATOR_DETERMINED_BARRING] = "Operator determined barring", [MM_MOBILE_EQUIPMENT_ERROR_MAXIMUM_NUMBER_OF_BEARERS_REACHED] = "Maximum number of PDP/bearer contexts reached", [MM_MOBILE_EQUIPMENT_ERROR_REQUESTED_APN_NOT_SUPPORTED] = "Requested APN not supported", [MM_MOBILE_EQUIPMENT_ERROR_REQUEST_REJECTED_BCM_VIOLATION] = "Rejected BCM violation", [MM_MOBILE_EQUIPMENT_ERROR_UNSUPPORTED_QCI_OR_5QI_VALUE] = "Unsupported QCI/5QI value", [MM_MOBILE_EQUIPMENT_ERROR_USER_DATA_VIA_CONTROL_PLANE_CONGESTED] = "User data via control plane congested", [MM_MOBILE_EQUIPMENT_ERROR_SMS_PROVIDED_VIA_GPRS_IN_ROUTING_AREA] = "SMS provided via GPRS in routing area", [MM_MOBILE_EQUIPMENT_ERROR_INVALID_PTI_VALUE] = "Invalid PTI value", [MM_MOBILE_EQUIPMENT_ERROR_NO_BEARER_ACTIVATED] = "No bearer activated", [MM_MOBILE_EQUIPMENT_ERROR_MESSAGE_NOT_COMPATIBLE_WITH_PROTOCOL_STATE] = "Message not compatible with protocol state", [MM_MOBILE_EQUIPMENT_ERROR_RECOVERY_ON_TIMER_EXPIRY] = "Recovery on timer expiry", [MM_MOBILE_EQUIPMENT_ERROR_INVALID_TRANSACTION_ID_VALUE] = "Invalid transaction ID value", [MM_MOBILE_EQUIPMENT_ERROR_SERVICE_OPTION_NOT_AUTHORIZED_IN_PLMN] = "Service option not authorized in PLMN", [MM_MOBILE_EQUIPMENT_ERROR_NETWORK_FAILURE_ACTIVATION] = "Network failure (activation)", [MM_MOBILE_EQUIPMENT_ERROR_REACTIVATION_REQUESTED] = "Reactivation requested", [MM_MOBILE_EQUIPMENT_ERROR_IPV4_ONLY_ALLOWED] = "IPv4 only allowed", [MM_MOBILE_EQUIPMENT_ERROR_IPV6_ONLY_ALLOWED] = "IPv6 only allowed", [MM_MOBILE_EQUIPMENT_ERROR_SINGLE_ADDRESS_BEARERS_ONLY_ALLOWED] = "Single address bearers only allowed", [MM_MOBILE_EQUIPMENT_ERROR_COLLISION_WITH_NETWORK_INITIATED_REQUEST] = "Collision with network initiated request", [MM_MOBILE_EQUIPMENT_ERROR_IPV4V6_ONLY_ALLOWED] = "IPv4v6 only allowed", [MM_MOBILE_EQUIPMENT_ERROR_NON_IP_ONLY_ALLOWED] = "Non-IP only allowed", [MM_MOBILE_EQUIPMENT_ERROR_BEARER_HANDLING_UNSUPPORTED] = "Bearer handling unsupported", [MM_MOBILE_EQUIPMENT_ERROR_APN_RESTRICTION_INCOMPATIBLE] = "APN restriction incompatible", [MM_MOBILE_EQUIPMENT_ERROR_MULTIPLE_ACCESS_TO_PDN_CONNECTION_NOT_ALLOWED] = "Multiple access to PDN connection not allowed", [MM_MOBILE_EQUIPMENT_ERROR_ESM_INFORMATION_NOT_RECEIVED] = "ESM information not received", [MM_MOBILE_EQUIPMENT_ERROR_PDN_CONNECTION_NONEXISTENT] = "PDN connection nonexistent", [MM_MOBILE_EQUIPMENT_ERROR_MULTIPLE_PDN_CONNECTION_SAME_APN_NOT_ALLOWED] = "Multiple PDN connection to same APN not allowed", [MM_MOBILE_EQUIPMENT_ERROR_SEVERE_NETWORK_FAILURE] = "Severe network failure", [MM_MOBILE_EQUIPMENT_ERROR_INSUFFICIENT_RESOURCES_FOR_SLICE_AND_DNN] = "Insufficient resources for slice and DNN", [MM_MOBILE_EQUIPMENT_ERROR_UNSUPPORTED_SSC_MODE] = "Unsupported SSC mode", [MM_MOBILE_EQUIPMENT_ERROR_INSUFFICIENT_RESOURCES_FOR_SLICE] = "Insufficient resources for slice", [MM_MOBILE_EQUIPMENT_ERROR_MESSAGE_TYPE_NOT_COMPATIBLE_WITH_PROTOCOL_STATE] = "Message type not compatible with protocol state", [MM_MOBILE_EQUIPMENT_ERROR_IE_NOT_IMPLEMENTED] = "IE not implemented", [MM_MOBILE_EQUIPMENT_ERROR_N1_MODE_NOT_ALLOWED] = "N1 mode not allowed", [MM_MOBILE_EQUIPMENT_ERROR_RESTRICTED_SERVICE_AREA] = "Restricted service area", [MM_MOBILE_EQUIPMENT_ERROR_LADN_UNAVAILABLE] = "LADN unavailable", [MM_MOBILE_EQUIPMENT_ERROR_MISSING_OR_UNKNOWN_DNN_IN_SLICE] = "Missing or unknown DNN in slice", [MM_MOBILE_EQUIPMENT_ERROR_NGKSI_ALREADY_IN_USE] = "ngKSI already in use", [MM_MOBILE_EQUIPMENT_ERROR_PAYLOAD_NOT_FORWARDED] = "Payload not forwarded", [MM_MOBILE_EQUIPMENT_ERROR_NON_3GPP_ACCESS_TO_5GCN_NOT_ALLOWED] = "Non-3GPP access to 5GCN not allowed", [MM_MOBILE_EQUIPMENT_ERROR_SERVING_NETWORK_NOT_AUTHORIZED] = "Serving network not authorized", [MM_MOBILE_EQUIPMENT_ERROR_DNN_NOT_SUPPORTED_IN_SLICE] = "DNN not supported in slice", [MM_MOBILE_EQUIPMENT_ERROR_INSUFFICIENT_USER_PLANE_RESOURCES_FOR_PDU_SESSION] = "Insufficient user plane resources for PDU session", [MM_MOBILE_EQUIPMENT_ERROR_OUT_OF_LADN_SERVICE_AREA] = "Out of LADN service area", [MM_MOBILE_EQUIPMENT_ERROR_PTI_MISMATCH] = "PTI mismatch", [MM_MOBILE_EQUIPMENT_ERROR_MAX_DATA_RATE_FOR_USER_PLANE_INTEGRITY_TOO_LOW] = "Max data rate for user plane integrity too low", [MM_MOBILE_EQUIPMENT_ERROR_SEMANTIC_ERROR_IN_QOS_OPERATION] = "Semantic error in QoS operation", [MM_MOBILE_EQUIPMENT_ERROR_SYNTACTICAL_ERROR_IN_QOS_OPERATION] = "Syntactical error in QoS operation", [MM_MOBILE_EQUIPMENT_ERROR_INVALID_MAPPED_EPS_BEARER_IDENTITY] = "Invalid mapped EPS bearer identity", [MM_MOBILE_EQUIPMENT_ERROR_REDIRECTION_TO_5GCN_REQUIRED] = "Redirection to 5GCN required", [MM_MOBILE_EQUIPMENT_ERROR_REDIRECTION_TO_EPC_REQUIRED] = "Redirection to EPC required", [MM_MOBILE_EQUIPMENT_ERROR_TEMPORARILY_UNAUTHORIZED_FOR_SNPN] = "Temporarily unauthorized for SNPN", [MM_MOBILE_EQUIPMENT_ERROR_PERMANENTLY_UNAUTHORIZED_FOR_SNPN] = "Permanently unauthorized for SNPN", [MM_MOBILE_EQUIPMENT_ERROR_ETHERNET_ONLY_ALLOWED] = "Ethernet only allowed", [MM_MOBILE_EQUIPMENT_ERROR_UNAUTHORIZED_FOR_CAG] = "Unauthorized for CAG", [MM_MOBILE_EQUIPMENT_ERROR_NO_NETWORK_SLICES_AVAILABLE] = "No network slices available", [MM_MOBILE_EQUIPMENT_ERROR_WIRELINE_ACCESS_AREA_NOT_ALLOWED] = "Wireline access area not allowed", }; /* All generic ME errors should be < 255, as those are the only reserved ones in the 3GPP spec */ G_STATIC_ASSERT (G_N_ELEMENTS (me_error_messages) <= 256); static const gchar * me_error_get_string (MMMobileEquipmentError code) { return (code < G_N_ELEMENTS (me_error_messages)) ? me_error_messages[code] : NULL; } GError * mm_mobile_equipment_error_for_code (MMMobileEquipmentError code, gpointer log_object) { const gchar *description; description = me_error_get_string (code); if (description) return g_error_new_literal (MM_MOBILE_EQUIPMENT_ERROR, code, description); /* Not found? Then, default */ mm_obj_dbg (log_object, "unknown mobile equipment error: %u", code); return g_error_new (MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_UNKNOWN, "Unknown mobile equipment error: %u", code); } /******************************************************************************/ /* Serial errors */ static const gchar *serial_error_messages[] = { [MM_SERIAL_ERROR_UNKNOWN] = "Unknown", [MM_SERIAL_ERROR_OPEN_FAILED] = "Open failed", [MM_SERIAL_ERROR_SEND_FAILED] = "Send failed", [MM_SERIAL_ERROR_RESPONSE_TIMEOUT] = "Response timeout", [MM_SERIAL_ERROR_OPEN_FAILED_NO_DEVICE] = "Open failed, no device", [MM_SERIAL_ERROR_FLASH_FAILED] = "Flash failed", [MM_SERIAL_ERROR_NOT_OPEN] = "Not open", [MM_SERIAL_ERROR_PARSE_FAILED] = "Parse failed", [MM_SERIAL_ERROR_FRAME_NOT_FOUND] = "Frame not found", }; static const gchar * serial_error_get_string (MMSerialError code) { return (code < G_N_ELEMENTS (serial_error_messages)) ? serial_error_messages[code] : NULL; } /******************************************************************************/ /* Message errors * * The message errors are all >300, so we define a common offset for all error * types. */ #define MM_MESSAGE_ERROR_COMMON_OFFSET 300 /* Human friendly messages for each error type */ static const gchar *msg_error_messages[] = { [MM_MESSAGE_ERROR_ME_FAILURE - MM_MESSAGE_ERROR_COMMON_OFFSET] = "ME failure", [MM_MESSAGE_ERROR_SMS_SERVICE_RESERVED - MM_MESSAGE_ERROR_COMMON_OFFSET] = "SMS service reserved", [MM_MESSAGE_ERROR_NOT_ALLOWED - MM_MESSAGE_ERROR_COMMON_OFFSET] = "Operation not allowed", [MM_MESSAGE_ERROR_NOT_SUPPORTED - MM_MESSAGE_ERROR_COMMON_OFFSET] = "Operation not supported", [MM_MESSAGE_ERROR_INVALID_PDU_PARAMETER - MM_MESSAGE_ERROR_COMMON_OFFSET] = "Invalid PDU mode parameter", [MM_MESSAGE_ERROR_INVALID_TEXT_PARAMETER - MM_MESSAGE_ERROR_COMMON_OFFSET] = "Invalid text mode parameter", [MM_MESSAGE_ERROR_SIM_NOT_INSERTED - MM_MESSAGE_ERROR_COMMON_OFFSET] = "SIM not inserted", [MM_MESSAGE_ERROR_SIM_PIN - MM_MESSAGE_ERROR_COMMON_OFFSET] = "SIM PIN required", [MM_MESSAGE_ERROR_PH_SIM_PIN - MM_MESSAGE_ERROR_COMMON_OFFSET] = "PH-SIM PIN required", [MM_MESSAGE_ERROR_SIM_FAILURE - MM_MESSAGE_ERROR_COMMON_OFFSET] = "SIM failure", [MM_MESSAGE_ERROR_SIM_BUSY - MM_MESSAGE_ERROR_COMMON_OFFSET] = "SIM busy", [MM_MESSAGE_ERROR_SIM_WRONG - MM_MESSAGE_ERROR_COMMON_OFFSET] = "SIM wrong", [MM_MESSAGE_ERROR_SIM_PUK - MM_MESSAGE_ERROR_COMMON_OFFSET] = "SIM PUK required", [MM_MESSAGE_ERROR_SIM_PIN2 - MM_MESSAGE_ERROR_COMMON_OFFSET] = "SIM PIN2 required", [MM_MESSAGE_ERROR_SIM_PUK2 - MM_MESSAGE_ERROR_COMMON_OFFSET] = "SIM PUK2 required", [MM_MESSAGE_ERROR_MEMORY_FAILURE - MM_MESSAGE_ERROR_COMMON_OFFSET] = "Memory failure", [MM_MESSAGE_ERROR_INVALID_INDEX - MM_MESSAGE_ERROR_COMMON_OFFSET] = "Invalid index", [MM_MESSAGE_ERROR_MEMORY_FULL - MM_MESSAGE_ERROR_COMMON_OFFSET] = "Memory full", [MM_MESSAGE_ERROR_SMSC_ADDRESS_UNKNOWN - MM_MESSAGE_ERROR_COMMON_OFFSET] = "SMSC address unknown", [MM_MESSAGE_ERROR_NO_NETWORK - MM_MESSAGE_ERROR_COMMON_OFFSET] = "No network", [MM_MESSAGE_ERROR_NETWORK_TIMEOUT - MM_MESSAGE_ERROR_COMMON_OFFSET] = "Network timeout", [MM_MESSAGE_ERROR_NO_CNMA_ACK_EXPECTED - MM_MESSAGE_ERROR_COMMON_OFFSET] = "No CNMA acknowledgement expected", [MM_MESSAGE_ERROR_UNKNOWN - MM_MESSAGE_ERROR_COMMON_OFFSET] = "Unknown", }; /* All generic message errors should be <= 500 (500-common=200), as those are the only reserved ones in the 3GPP spec */ G_STATIC_ASSERT (G_N_ELEMENTS (msg_error_messages) <= 201); static const gchar * message_error_get_string (MMMessageError code) { return ((code >= MM_MESSAGE_ERROR_COMMON_OFFSET) && ((code - MM_MESSAGE_ERROR_COMMON_OFFSET) < G_N_ELEMENTS (msg_error_messages))) ? msg_error_messages[code - MM_MESSAGE_ERROR_COMMON_OFFSET] : NULL; } GError * mm_message_error_for_code (MMMessageError code, gpointer log_object) { const gchar *description; description = message_error_get_string (code); if (description) return g_error_new_literal (MM_MESSAGE_ERROR, code, description); /* Not found? Then, default */ mm_obj_dbg (log_object, "unknown message error: %u", code); return g_error_new (MM_MESSAGE_ERROR, MM_MESSAGE_ERROR_UNKNOWN, "Unknown message error: %u", code); } /******************************************************************************/ /* Mobile equipment and message errors as string * * The strings given here must be all lowercase, and stripped of special chars * and whitespaces. * * Not all errors are included, only the most generic ones. */ typedef struct { guint error_code; const gchar *error_string; } MeErrorString; static const MeErrorString me_error_strings[] = { { MM_MOBILE_EQUIPMENT_ERROR_PHONE_FAILURE, "phonefailure" }, { MM_MOBILE_EQUIPMENT_ERROR_NO_CONNECTION, "noconnection" }, { MM_MOBILE_EQUIPMENT_ERROR_LINK_RESERVED, "linkreserved" }, { MM_MOBILE_EQUIPMENT_ERROR_NOT_ALLOWED, "operationnotallowed" }, { MM_MOBILE_EQUIPMENT_ERROR_NOT_SUPPORTED, "operationnotsupported" }, { MM_MOBILE_EQUIPMENT_ERROR_SIM_NOT_INSERTED, "simnotinserted" }, { MM_MOBILE_EQUIPMENT_ERROR_SIM_PIN, "simpinrequired" }, { MM_MOBILE_EQUIPMENT_ERROR_SIM_PUK, "simpukrequired" }, { MM_MOBILE_EQUIPMENT_ERROR_SIM_FAILURE, "simfailure" }, { MM_MOBILE_EQUIPMENT_ERROR_SIM_BUSY, "simbusy" }, { MM_MOBILE_EQUIPMENT_ERROR_SIM_WRONG, "simwrong" }, { MM_MOBILE_EQUIPMENT_ERROR_INCORRECT_PASSWORD, "incorrectpassword" }, { MM_MOBILE_EQUIPMENT_ERROR_NOT_FOUND, "notfound" }, { MM_MOBILE_EQUIPMENT_ERROR_NO_NETWORK, "nonetwork" }, { MM_MOBILE_EQUIPMENT_ERROR_NETWORK_TIMEOUT, "timeout" }, { MM_MOBILE_EQUIPMENT_ERROR_INCORRECT_PARAMETERS, "incorrectparameters" }, }; GError * mm_mobile_equipment_error_for_string (const gchar *str, gpointer log_object) { g_autofree gchar *buf = NULL; guint i; g_assert (str != NULL); /* Look for the string */ buf = normalize_error_string (str); for (i = 0; i < G_N_ELEMENTS (me_error_strings); i++) { if (g_str_equal (me_error_strings[i].error_string, buf)) return mm_mobile_equipment_error_for_code ((MMMobileEquipmentError)me_error_strings[i].error_code, log_object); } /* Not found? then, default */ mm_obj_dbg (log_object, "unknown mobile equipment error string: '%s' (%s)", str, buf); return g_error_new (MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_UNKNOWN, "Unknown mobile equipment error string: %s", str); } static const MeErrorString msg_error_strings[] = { { MM_MESSAGE_ERROR_ME_FAILURE, "mefailure" }, { MM_MESSAGE_ERROR_SMS_SERVICE_RESERVED, "smsservicereserved" }, { MM_MESSAGE_ERROR_NOT_ALLOWED, "operationnotallowed" }, { MM_MESSAGE_ERROR_NOT_SUPPORTED, "operationnotsupported" }, { MM_MESSAGE_ERROR_INVALID_PDU_PARAMETER, "invalidpduparameter" }, { MM_MESSAGE_ERROR_INVALID_TEXT_PARAMETER, "invalidtextparameter" }, { MM_MESSAGE_ERROR_SIM_NOT_INSERTED, "simnotinserted" }, { MM_MESSAGE_ERROR_SIM_PIN, "simpinrequired" }, { MM_MESSAGE_ERROR_SIM_FAILURE, "simfailure" }, { MM_MESSAGE_ERROR_SIM_BUSY, "simbusy" }, { MM_MESSAGE_ERROR_SIM_WRONG, "simwrong" }, { MM_MESSAGE_ERROR_SIM_PUK, "simpukrequired" }, { MM_MESSAGE_ERROR_MEMORY_FAILURE, "memoryfailure" }, { MM_MESSAGE_ERROR_INVALID_INDEX, "invalidindex" }, { MM_MESSAGE_ERROR_MEMORY_FULL, "memoryfull" }, { MM_MESSAGE_ERROR_SMSC_ADDRESS_UNKNOWN, "smscaddressunknown" }, { MM_MESSAGE_ERROR_NO_NETWORK, "nonetwork" }, { MM_MESSAGE_ERROR_NETWORK_TIMEOUT, "networktimeout" }, }; GError * mm_message_error_for_string (const gchar *str, gpointer log_object) { g_autofree gchar *buf = NULL; guint i; g_assert (str != NULL); /* Look for the string */ buf = normalize_error_string (str); for (i = 0; i < G_N_ELEMENTS (msg_error_strings); i++) { if (g_str_equal (msg_error_strings[i].error_string, buf)) return mm_message_error_for_code ((MMMessageError)msg_error_strings[i].error_code, log_object); } /* Not found? then, default */ mm_obj_dbg (log_object, "unknown message error string: '%s' (%s)", str, buf); return g_error_new (MM_MESSAGE_ERROR, MM_MESSAGE_ERROR_UNKNOWN, "Unknown message error string: %s", str); } /******************************************************************************/ /* CDMA activation errors */ static const gchar *cdma_activation_error_messages[] = { [MM_CDMA_ACTIVATION_ERROR_NONE] = "None", [MM_CDMA_ACTIVATION_ERROR_UNKNOWN] = "Unknown", [MM_CDMA_ACTIVATION_ERROR_ROAMING] = "Roaming", [MM_CDMA_ACTIVATION_ERROR_WRONG_RADIO_INTERFACE] = "Wrong radio interface", [MM_CDMA_ACTIVATION_ERROR_COULD_NOT_CONNECT] = "Could not connect", [MM_CDMA_ACTIVATION_ERROR_SECURITY_AUTHENTICATION_FAILED] = "Security authentication failed", [MM_CDMA_ACTIVATION_ERROR_PROVISIONING_FAILED] = "Provisioning failed", [MM_CDMA_ACTIVATION_ERROR_NO_SIGNAL] = "No signal", [MM_CDMA_ACTIVATION_ERROR_TIMED_OUT] = "Timed out", [MM_CDMA_ACTIVATION_ERROR_START_FAILED] = "Start failed", }; static const gchar * cdma_activation_error_get_string (MMCdmaActivationError code) { return (code < G_N_ELEMENTS (cdma_activation_error_messages)) ? cdma_activation_error_messages[code] : NULL; } /******************************************************************************/ /* Carrier lock errors */ /* Human friendly messages for each error type */ static const gchar *carrier_lock_error_messages[] = { [MM_CARRIER_LOCK_ERROR_UNKNOWN] = "Unknown", [MM_CARRIER_LOCK_ERROR_INVALID_SIGNATURE] = "Invalid signature", [MM_CARRIER_LOCK_ERROR_INVALID_IMEI] = "Invalid IMEI", [MM_CARRIER_LOCK_ERROR_INVALID_TIMESTAMP] = "Invalid timestamp", [MM_CARRIER_LOCK_ERROR_NETWORK_LIST_TOO_LARGE] = "Network list too large", [MM_CARRIER_LOCK_ERROR_SIGNATURE_ALGORITHM_NOT_SUPPORTED] = "Signature algorithm not supported", [MM_CARRIER_LOCK_ERROR_FEATURE_NOT_SUPPORTED] = "Feature not supported", [MM_CARRIER_LOCK_ERROR_DECODE_OR_PARSING_ERROR] = "Decode or parsing error", }; static const gchar * carrier_lock_error_get_string (MMCarrierLockError code) { return (code < G_N_ELEMENTS (carrier_lock_error_messages)) ? carrier_lock_error_messages[code] : NULL; } /******************************************************************************/ /* Registered error mappings */ typedef struct { GQuark error_domain; gint error_code; } DomainCodePair; static guint domain_code_pair_hash_func (const DomainCodePair *pair) { gint64 val; /* The value of the GQuark error domain is selected during runtime based on the amount of * error types that are registered in the type system. */ val = ((gint64)pair->error_domain << 31) | pair->error_code; return g_int64_hash (&val); } static gboolean domain_code_pair_equal_func (const DomainCodePair *a, const DomainCodePair *b) { return (a->error_domain == b->error_domain) && (a->error_code == b->error_code); } static void domain_code_pair_free (DomainCodePair *pair) { g_slice_free (DomainCodePair, pair); } /* Map of a input domain/code error to an output domain/code error. This HT exists * for as long as the process is running, it is not explicitly freed on exit. */ static GHashTable *error_mappings; void mm_register_error_mapping (GQuark input_error_domain, gint input_error_code, GQuark output_error_domain, gint output_error_code) { DomainCodePair *input; DomainCodePair *output; if (G_UNLIKELY (!error_mappings)) error_mappings = g_hash_table_new_full ((GHashFunc)domain_code_pair_hash_func, (GEqualFunc)domain_code_pair_equal_func, (GDestroyNotify)domain_code_pair_free, (GDestroyNotify)domain_code_pair_free); input = g_slice_new0 (DomainCodePair); input->error_domain = input_error_domain; input->error_code = input_error_code; /* ensure no other error is registered with the same hash, we don't want or * expect dupicates*/ g_assert (!g_hash_table_lookup (error_mappings, input)); output = g_slice_new0 (DomainCodePair); output->error_domain = output_error_domain; output->error_code = output_error_code; g_hash_table_insert (error_mappings, input, output); } static GError * normalize_mapped_error (const GError *error) { DomainCodePair *output = NULL; const gchar *input_error_type; #if defined WITH_MBIM mm_register_mbim_errors (); #endif #if defined WITH_QMI mm_register_qmi_errors (); #endif #if defined WITH_QMI if (error->domain == QMI_CORE_ERROR) input_error_type = "QMI core"; else if (error->domain == QMI_PROTOCOL_ERROR) input_error_type = "QMI protocol"; else #endif #if defined WITH_MBIM if (error->domain == MBIM_CORE_ERROR) input_error_type = "MBIM core"; else if (error->domain == MBIM_PROTOCOL_ERROR) input_error_type = "MBIM protocol"; else if (error->domain == MBIM_STATUS_ERROR) input_error_type = "MBIM status"; else #endif input_error_type = "unknown domain"; if (error_mappings) { DomainCodePair input; input.error_domain = error->domain; input.error_code = error->code; output = g_hash_table_lookup (error_mappings, &input); } if (!output) return g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Unhandled %s error (%u): %s", input_error_type, error->code, error->message); return g_error_new (output->error_domain, output->error_code, "%s error: %s", input_error_type, error->message); } /******************************************************************************/ /* Takes a GError of any kind and ensures that the returned GError is a * MM-defined one. */ static GError * normalize_mm_error (const GError *error, const gchar *error_description, const gchar *error_type) { if (error_description) { GError *copy; copy = g_error_copy (error); /* Add error type name only if it isn't already the same string */ if (g_strcmp0 (copy->message, error_description) != 0) g_prefix_error (©, "%s: ", error_description); return copy; } return g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Unhandled %s error (%u): %s", error_type, error->code, error->message); } GError * mm_normalize_error (const GError *error) { g_assert (error); if (error->domain == G_IO_ERROR) { /* G_IO_ERROR_CANCELLED is an exception, because we map it to * MM_CORE_ERROR_CANCELLED implicitly when building the DBus error name. */ if (error->code == G_IO_ERROR_CANCELLED) return g_error_copy (error); return g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Unhandled GIO error (%u): %s", error->code, error->message); } /* Ensure all errors reported in the MM domains are known errors */ if (error->domain == MM_CORE_ERROR) return normalize_mm_error (error, core_error_get_string (error->code), "core"); if (error->domain == MM_MOBILE_EQUIPMENT_ERROR) return normalize_mm_error (error, me_error_get_string (error->code), "mobile equipment"); if (error->domain == MM_CONNECTION_ERROR) return normalize_mm_error (error, connection_error_get_string (error->code), "connection"); if (error->domain == MM_SERIAL_ERROR) return normalize_mm_error (error, serial_error_get_string (error->code), "serial"); if (error->domain == MM_MESSAGE_ERROR) return normalize_mm_error (error, message_error_get_string (error->code), "message"); if (error->domain == MM_CDMA_ACTIVATION_ERROR) return normalize_mm_error (error, cdma_activation_error_get_string (error->code), "CDMA activation"); if (error->domain == MM_CARRIER_LOCK_ERROR) return normalize_mm_error (error, carrier_lock_error_get_string (error->code), "carrier lock"); /* Normalize mapped errors */ return normalize_mapped_error (error); } /******************************************************************************/ void mm_dbus_method_invocation_take_error (GDBusMethodInvocation *invocation, GError *error) { g_autoptr(GError) taken = error; mm_dbus_method_invocation_return_gerror (invocation, taken); } void mm_dbus_method_invocation_return_error_literal (GDBusMethodInvocation *invocation, GQuark domain, gint code, const gchar *message) { g_autoptr(GError) error = NULL; error = g_error_new_literal (domain, code, message); mm_dbus_method_invocation_return_gerror (invocation, error); } void mm_dbus_method_invocation_return_error (GDBusMethodInvocation *invocation, GQuark domain, gint code, const gchar *format, ...) { va_list var_args; g_autoptr(GError) error = NULL; va_start (var_args, format); error = g_error_new_valist (domain, code, format, var_args); mm_dbus_method_invocation_return_gerror (invocation, error); va_end (var_args); } void mm_dbus_method_invocation_return_gerror (GDBusMethodInvocation *invocation, const GError *error) { g_dbus_method_invocation_take_error (invocation, mm_normalize_error (error)); } ModemManager-1.23.4-dev/src/mm-error-helpers.h000066400000000000000000000060041456466623000210650ustar00rootroot00000000000000/* -*- 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 - 2012 Red Hat, Inc. * Copyright (C) 2011 - 2012 Google, Inc. */ #ifndef MM_ERROR_HELPERS_H #define MM_ERROR_HELPERS_H #include #include #include GError *mm_connection_error_for_code (MMConnectionError code, gpointer log_object); GError *mm_mobile_equipment_error_for_code (MMMobileEquipmentError code, gpointer log_object); GError *mm_mobile_equipment_error_for_string (const gchar *str, gpointer log_object); GError *mm_message_error_for_code (MMMessageError code, gpointer log_object); GError *mm_message_error_for_string (const gchar *str, gpointer log_object); GError *mm_normalize_error (const GError *error); void mm_register_error_mapping (GQuark input_error_domain, gint input_error_code, GQuark output_error_domain, gint output_error_code); /* Replacements for the dbus method invocation completions but with our error normalization * procedure in place, so that we only report back MM-specific errors. */ void mm_dbus_method_invocation_take_error (GDBusMethodInvocation *invocation, GError *error); void mm_dbus_method_invocation_return_error_literal (GDBusMethodInvocation *invocation, GQuark domain, gint code, const gchar *message); void mm_dbus_method_invocation_return_error (GDBusMethodInvocation *invocation, GQuark domain, gint code, const gchar *format, ...) G_GNUC_PRINTF(4, 5);; void mm_dbus_method_invocation_return_gerror (GDBusMethodInvocation *invocation, const GError *error); #endif /* MM_ERROR_HELPERS_H */ ModemManager-1.23.4-dev/src/mm-filter.c000066400000000000000000000547041456466623000175660ustar00rootroot00000000000000/* -*- 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) 2017 Aleksander Morgado */ #include #include #include #include #include "mm-daemon-enums-types.h" #include "mm-filter.h" #include "mm-log-object.h" #define FILTER_PORT_MAYBE_FORBIDDEN "maybe-forbidden" static void log_object_iface_init (MMLogObjectInterface *iface); G_DEFINE_TYPE_EXTENDED (MMFilter, mm_filter, G_TYPE_OBJECT, 0, G_IMPLEMENT_INTERFACE (MM_TYPE_LOG_OBJECT, log_object_iface_init)) enum { PROP_0, PROP_ENABLED_RULES, LAST_PROP }; struct _MMFilterPrivate { MMFilterRule enabled_rules; GList *plugin_allowlist_tags; GArray *plugin_allowlist_vendor_ids; GArray *plugin_allowlist_product_ids; GArray *plugin_allowlist_subsystem_vendor_ids; }; /*****************************************************************************/ void mm_filter_register_plugin_allowlist_tag (MMFilter *self, const gchar *tag) { if (!g_list_find_custom (self->priv->plugin_allowlist_tags, tag, (GCompareFunc) g_strcmp0)) { mm_obj_dbg (self, "registered plugin allowlist tag: %s", tag); self->priv->plugin_allowlist_tags = g_list_prepend (self->priv->plugin_allowlist_tags, g_strdup (tag)); } } void mm_filter_register_plugin_allowlist_vendor_id (MMFilter *self, guint16 vid) { guint i; if (!self->priv->plugin_allowlist_vendor_ids) self->priv->plugin_allowlist_vendor_ids = g_array_sized_new (FALSE, FALSE, sizeof (guint16), 64); for (i = 0; i < self->priv->plugin_allowlist_vendor_ids->len; i++) { guint16 item; item = g_array_index (self->priv->plugin_allowlist_vendor_ids, guint16, i); if (item == vid) return; } g_array_append_val (self->priv->plugin_allowlist_vendor_ids, vid); mm_obj_dbg (self, "registered plugin allowlist vendor id: %04x", vid); } void mm_filter_register_plugin_allowlist_product_id (MMFilter *self, guint16 vid, guint16 pid) { mm_uint16_pair new_item; guint i; if (!self->priv->plugin_allowlist_product_ids) self->priv->plugin_allowlist_product_ids = g_array_sized_new (FALSE, FALSE, sizeof (mm_uint16_pair), 10); for (i = 0; i < self->priv->plugin_allowlist_product_ids->len; i++) { mm_uint16_pair *item; item = &g_array_index (self->priv->plugin_allowlist_product_ids, mm_uint16_pair, i); if (item->l == vid && item->r == pid) return; } new_item.l = vid; new_item.r = pid; g_array_append_val (self->priv->plugin_allowlist_product_ids, new_item); mm_obj_dbg (self, "registered plugin allowlist product id: %04x:%04x", vid, pid); } void mm_filter_register_plugin_allowlist_subsystem_vendor_id (MMFilter *self, guint16 vid, guint16 subsystem_vid) { mm_uint16_pair new_item; guint i; if (!self->priv->plugin_allowlist_subsystem_vendor_ids) self->priv->plugin_allowlist_subsystem_vendor_ids = g_array_sized_new (FALSE, FALSE, sizeof (mm_uint16_pair), 10); for (i = 0; i < self->priv->plugin_allowlist_subsystem_vendor_ids->len; i++) { mm_uint16_pair *item; item = &g_array_index (self->priv->plugin_allowlist_subsystem_vendor_ids, mm_uint16_pair, i); if (item->l == vid && item->r == subsystem_vid) return; } new_item.l = vid; new_item.r = subsystem_vid; g_array_append_val (self->priv->plugin_allowlist_subsystem_vendor_ids, new_item); mm_obj_dbg (self, "registered plugin allowlist subsystem vendor id: %04x:%04x", vid, subsystem_vid); } /*****************************************************************************/ gboolean mm_filter_port (MMFilter *self, MMKernelDevice *port, gboolean manual_scan) { const gchar *subsystem; const gchar *name; subsystem = mm_kernel_device_get_subsystem (port); name = mm_kernel_device_get_name (port); /* If the device is explicitly allowlisted, we process every port. Also * allow specifying this flag per-port instead of for the full device, e.g. * for platform tty ports where there's only one port anyway. */ if ((self->priv->enabled_rules & MM_FILTER_RULE_EXPLICIT_ALLOWLIST) && (mm_kernel_device_get_global_property_as_boolean (port, ID_MM_DEVICE_PROCESS) || mm_kernel_device_get_property_as_boolean (port, ID_MM_DEVICE_PROCESS))) { mm_obj_dbg (self, "(%s/%s) port allowed: device is allowlisted", subsystem, name); return TRUE; } /* If the device is explicitly ignored, we ignore every port. */ if ((self->priv->enabled_rules & MM_FILTER_RULE_EXPLICIT_BLOCKLIST) && (mm_kernel_device_get_global_property_as_boolean (port, ID_MM_DEVICE_IGNORE))) { mm_obj_dbg (self, "(%s/%s): port filtered: device is blocklisted", subsystem, name); return FALSE; } /* If the device is allowlisted by a plugin, we allow it. */ if (self->priv->enabled_rules & MM_FILTER_RULE_PLUGIN_ALLOWLIST) { GList *l; guint16 vid = 0; guint16 pid = 0; guint16 subsystem_vid = 0; for (l = self->priv->plugin_allowlist_tags; l; l = g_list_next (l)) { if (mm_kernel_device_get_global_property_as_boolean (port, (const gchar *)(l->data)) || mm_kernel_device_get_property_as_boolean (port, (const gchar *)(l->data))) { mm_obj_dbg (self, "(%s/%s) port allowed: device is allowlisted by plugin (tag)", subsystem, name); return TRUE; } } vid = mm_kernel_device_get_physdev_vid (port); if (vid) { pid = mm_kernel_device_get_physdev_pid (port); subsystem_vid = mm_kernel_device_get_physdev_subsystem_vid (port); } if (vid && pid && self->priv->plugin_allowlist_product_ids) { guint i; for (i = 0; i < self->priv->plugin_allowlist_product_ids->len; i++) { mm_uint16_pair *item; item = &g_array_index (self->priv->plugin_allowlist_product_ids, mm_uint16_pair, i); if (item->l == vid && item->r == pid) { mm_obj_dbg (self, "(%s/%s) port allowed: device is allowlisted by plugin (vid/pid)", subsystem, name); return TRUE; } } } if (vid && self->priv->plugin_allowlist_vendor_ids) { guint i; for (i = 0; i < self->priv->plugin_allowlist_vendor_ids->len; i++) { guint16 item; item = g_array_index (self->priv->plugin_allowlist_vendor_ids, guint16, i); if (item == vid) { mm_obj_dbg (self, "(%s/%s) port allowed: device is allowlisted by plugin (vid)", subsystem, name); return TRUE; } } } if (vid && subsystem_vid && self->priv->plugin_allowlist_subsystem_vendor_ids) { guint i; for (i = 0; i < self->priv->plugin_allowlist_subsystem_vendor_ids->len; i++) { mm_uint16_pair *item; item = &g_array_index (self->priv->plugin_allowlist_subsystem_vendor_ids, mm_uint16_pair, i); if (item->l == vid && item->r == subsystem_vid) { mm_obj_dbg (self, "(%s/%s) port allowed: device is allowlisted by plugin (vid/subsystem vid)", subsystem, name); return TRUE; } } } } /* If this is a QRTR device, we always allow it. This check comes before * checking for VIRTUAL since qrtr devices don't have a sysfs path, and the * check for VIRTUAL will return FALSE. */ if ((self->priv->enabled_rules & MM_FILTER_RULE_QRTR) && g_str_equal (subsystem, "qrtr")) { mm_obj_dbg (self, "(%s/%s) port allowed: qrtr device", subsystem, name); return TRUE; } /* If this is a virtual device, don't allow it */ if ((self->priv->enabled_rules & MM_FILTER_RULE_VIRTUAL) && (!mm_kernel_device_get_physdev_sysfs_path (port))) { mm_obj_dbg (self, "(%s/%s) port filtered: virtual device", subsystem, name); return FALSE; } /* If this is a net device, we always allow it */ if ((self->priv->enabled_rules & MM_FILTER_RULE_NET) && (g_strcmp0 (subsystem, "net") == 0)) { mm_obj_dbg (self, "(%s/%s) port allowed: net device", subsystem, name); return TRUE; } /* If this is a cdc-wdm device, we always allow it */ if ((self->priv->enabled_rules & MM_FILTER_RULE_USBMISC) && (g_strcmp0 (subsystem, "usbmisc") == 0)) { mm_obj_dbg (self, "(%s/%s) port allowed: usbmisc device", subsystem, name); return TRUE; } /* If this is a rpmsg channel device, we always allow it */ if ((self->priv->enabled_rules & MM_FILTER_RULE_RPMSG) && (g_strcmp0 (subsystem, "rpmsg") == 0)) { mm_obj_dbg (self, "(%s/%s) port allowed: rpmsg device", subsystem, name); return TRUE; } /* If this is a wwan port/device, we always allow it */ if ((self->priv->enabled_rules & MM_FILTER_RULE_WWAN) && (g_strcmp0 (subsystem, "wwan") == 0)) { mm_obj_dbg (self, "(%s/%s) port allowed: wwan device", subsystem, name); return TRUE; } /* If this is a tty device, we may allow it */ if ((self->priv->enabled_rules & MM_FILTER_RULE_TTY) && (g_strcmp0 (subsystem, "tty") == 0)) { const gchar *physdev_subsystem; const gchar *driver; /* Mixed blocklist/allowlist rules */ /* If the physdev is a 'platform' or 'pnp' device that's not allowlisted, ignore it */ physdev_subsystem = mm_kernel_device_get_physdev_subsystem (port); if ((self->priv->enabled_rules & MM_FILTER_RULE_TTY_PLATFORM_DRIVER) && (!g_strcmp0 (physdev_subsystem, "platform") || !g_strcmp0 (physdev_subsystem, "pci") || !g_strcmp0 (physdev_subsystem, "pnp") || !g_strcmp0 (physdev_subsystem, "sdio"))) { mm_obj_dbg (self, "(%s/%s): port filtered: tty platform driver", subsystem, name); return FALSE; } /* Allowlist rules last */ /* If the TTY kernel driver is one expected modem kernel driver, allow it */ driver = mm_kernel_device_get_driver (port); if ((self->priv->enabled_rules & MM_FILTER_RULE_TTY_DRIVER) && (!g_strcmp0 (driver, "option") || !g_strcmp0 (driver, "option1") || !g_strcmp0 (driver, "qcserial") || !g_strcmp0 (driver, "qcaux") || !g_strcmp0 (driver, "nozomi") || !g_strcmp0 (driver, "sierra"))) { mm_obj_dbg (self, "(%s/%s): port allowed: modem-specific kernel driver detected", subsystem, name); return TRUE; } /* * If the TTY kernel driver is cdc-acm and the interface is not * class=2/subclass=2/protocol=[1-6], forbidden. * * Otherwise, we'll require the modem to have more ports other * than the ttyACM one (see mm_filter_device_and_port()), because * there are lots of Arduino devices out there exposing a single * ttyACM port and wrongly claiming AT protocol support... * * Class definitions for Communication Devices 1.2 * Communications Interface Class Control Protocol Codes: * 00h | USB specification | No class specific protocol required * 01h | ITU-T V.250 | AT Commands: V.250 etc * 02h | PCCA-101 | AT Commands defined by PCCA-101 * 03h | PCCA-101 | AT Commands defined by PCCA-101 & Annex O * 04h | GSM 7.07 | AT Commands defined by GSM 07.07 * 05h | 3GPP 27.07 | AT Commands defined by 3GPP 27.007 * 06h | C-S0017-0 | AT Commands defined by TIA for CDMA * 07h | USB EEM | Ethernet Emulation Model * 08h-FDh | | RESERVED (future use) * FEh | | External Protocol: Commands defined by Command Set Functional Descriptor * FFh | USB Specification | Vendor-specific */ if ((self->priv->enabled_rules & MM_FILTER_RULE_TTY_ACM_INTERFACE) && (!g_strcmp0 (driver, "cdc_acm")) && ((mm_kernel_device_get_interface_class (port) != 2) || (mm_kernel_device_get_interface_subclass (port) != 2) || (mm_kernel_device_get_interface_protocol (port) < 1) || (mm_kernel_device_get_interface_protocol (port) > 6))) { mm_obj_dbg (self, "(%s/%s): port filtered: cdc-acm interface is not AT-capable", subsystem, name); return FALSE; } /* Default forbidden? flag the port as maybe-forbidden, and go on */ if (self->priv->enabled_rules & MM_FILTER_RULE_TTY_DEFAULT_FORBIDDEN) { g_object_set_data (G_OBJECT (port), FILTER_PORT_MAYBE_FORBIDDEN, GUINT_TO_POINTER (TRUE)); return TRUE; } g_assert_not_reached (); } /* Otherwise forbidden */ mm_obj_dbg (self, "(%s/%s) port filtered: forbidden port type", subsystem, name); return FALSE; } /*****************************************************************************/ static gboolean device_has_net_port (MMDevice *device) { GList *l; for (l = mm_device_peek_port_probe_list (device); l; l = g_list_next (l)) { if (!g_strcmp0 (mm_port_probe_get_port_subsys (MM_PORT_PROBE (l->data)), "net")) return TRUE; } return FALSE; } static gboolean device_has_multiple_ports (MMDevice *device) { return (g_list_length (mm_device_peek_port_probe_list (device)) > 1); } gboolean mm_filter_device_and_port (MMFilter *self, MMDevice *device, MMKernelDevice *port) { const gchar *subsystem; const gchar *name; const gchar *driver; /* If it wasn't flagged as maybe forbidden, there's nothing to do */ if (!GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (port), FILTER_PORT_MAYBE_FORBIDDEN))) return TRUE; subsystem = mm_kernel_device_get_subsystem (port); name = mm_kernel_device_get_name (port); /* Check whether this device holds a NET port in addition to this TTY */ if ((self->priv->enabled_rules & MM_FILTER_RULE_TTY_WITH_NET) && device_has_net_port (device)) { mm_obj_dbg (self, "(%s/%s): port allowed: device also exports a net interface", subsystem, name); return TRUE; } /* Check whether this device holds any other port in addition to the ttyACM port */ driver = mm_kernel_device_get_driver (port); if ((self->priv->enabled_rules & MM_FILTER_RULE_TTY_ACM_INTERFACE) && (!g_strcmp0 (driver, "cdc_acm")) && device_has_multiple_ports (device)) { mm_obj_dbg (self, "(%s/%s): port allowed: device exports multiple interfaces", subsystem, name); return TRUE; } mm_obj_dbg (self, "(%s/%s) port filtered: forbidden", subsystem, name); return FALSE; } /*****************************************************************************/ /* Use filter rule names as environment variables to control them on startup: * - MM_FILTER_RULE_XXX=1 to explicitly enable the rule. * - MM_FILTER_RULE_XXX=0 to explicitly disable the rule. */ static MMFilterRule filter_rule_env_process (MMFilterRule enabled_rules) { MMFilterRule updated_rules = enabled_rules; GFlagsClass *flags_class; guint i; flags_class = g_type_class_ref (MM_TYPE_FILTER_RULE); for (i = 0; (1 << i) & MM_FILTER_RULE_ALL; i++) { GFlagsValue *flags_value; const gchar *env_value; flags_value = g_flags_get_first_value (flags_class, (1 << i)); g_assert (flags_value); env_value = g_getenv (flags_value->value_name); if (!env_value) continue; if (g_str_equal (env_value, "0")) updated_rules &= ~(1 << i); else if (g_str_equal (env_value, "1")) updated_rules |= (1 << i); } g_type_class_unref (flags_class); return updated_rules; } /*****************************************************************************/ gboolean mm_filter_check_rule_enabled (MMFilter *self, MMFilterRule rule) { return !!(self->priv->enabled_rules & rule); } /*****************************************************************************/ static gchar * log_object_build_id (MMLogObject *_self) { return g_strdup ("filter"); } /*****************************************************************************/ /* If TTY rule enabled, DEFAULT_FORBIDDEN must be set. */ #define VALIDATE_RULE_TTY(rules) (!(rules & MM_FILTER_RULE_TTY) || (rules & (MM_FILTER_RULE_TTY_DEFAULT_FORBIDDEN))) MMFilter * mm_filter_new (MMFilterRule enabled_rules, GError **error) { MMFilter *self; MMFilterRule updated_rules; /* The input enabled rules are coming from predefined filter profiles. */ g_assert (VALIDATE_RULE_TTY (enabled_rules)); updated_rules = filter_rule_env_process (enabled_rules); if (!VALIDATE_RULE_TTY (updated_rules)) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS, "Invalid rules after processing envvars"); return NULL; } self = g_object_new (MM_TYPE_FILTER, MM_FILTER_ENABLED_RULES, updated_rules, NULL); #define RULE_ENABLED_STR(flag) ((self->priv->enabled_rules & flag) ? "yes" : "no") mm_obj_dbg (self, "created"); mm_obj_dbg (self, " explicit allowlist: %s", RULE_ENABLED_STR (MM_FILTER_RULE_EXPLICIT_ALLOWLIST)); mm_obj_dbg (self, " explicit blocklist: %s", RULE_ENABLED_STR (MM_FILTER_RULE_EXPLICIT_BLOCKLIST)); mm_obj_dbg (self, " plugin allowlist: %s", RULE_ENABLED_STR (MM_FILTER_RULE_PLUGIN_ALLOWLIST)); mm_obj_dbg (self, " qrtr devices allowed: %s", RULE_ENABLED_STR (MM_FILTER_RULE_QRTR)); mm_obj_dbg (self, " virtual devices forbidden: %s", RULE_ENABLED_STR (MM_FILTER_RULE_VIRTUAL)); mm_obj_dbg (self, " net devices allowed: %s", RULE_ENABLED_STR (MM_FILTER_RULE_NET)); mm_obj_dbg (self, " usbmisc devices allowed: %s", RULE_ENABLED_STR (MM_FILTER_RULE_USBMISC)); mm_obj_dbg (self, " rpmsg devices allowed: %s", RULE_ENABLED_STR (MM_FILTER_RULE_RPMSG)); mm_obj_dbg (self, " wwan devices allowed: %s", RULE_ENABLED_STR (MM_FILTER_RULE_WWAN)); if (self->priv->enabled_rules & MM_FILTER_RULE_TTY) { mm_obj_dbg (self, " tty devices:"); mm_obj_dbg (self, " platform driver check: %s", RULE_ENABLED_STR (MM_FILTER_RULE_TTY_PLATFORM_DRIVER)); mm_obj_dbg (self, " driver check: %s", RULE_ENABLED_STR (MM_FILTER_RULE_TTY_DRIVER)); mm_obj_dbg (self, " cdc-acm interface check: %s", RULE_ENABLED_STR (MM_FILTER_RULE_TTY_ACM_INTERFACE)); mm_obj_dbg (self, " with net check: %s", RULE_ENABLED_STR (MM_FILTER_RULE_TTY_WITH_NET)); if (self->priv->enabled_rules & MM_FILTER_RULE_TTY_DEFAULT_FORBIDDEN) mm_obj_dbg (self, " default: forbidden"); else g_assert_not_reached (); } else mm_obj_dbg (self, " tty devices: no"); #undef RULE_ENABLED_STR return self; } static void set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { MMFilter *self = MM_FILTER (object); switch (prop_id) { case PROP_ENABLED_RULES: self->priv->enabled_rules = g_value_get_flags (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) { MMFilter *self = MM_FILTER (object); switch (prop_id) { case PROP_ENABLED_RULES: g_value_set_flags (value, self->priv->enabled_rules); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void mm_filter_init (MMFilter *self) { self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, MM_TYPE_FILTER, MMFilterPrivate); } static void finalize (GObject *object) { MMFilter *self = MM_FILTER (object); g_clear_pointer (&self->priv->plugin_allowlist_vendor_ids, g_array_unref); g_clear_pointer (&self->priv->plugin_allowlist_product_ids, g_array_unref); g_clear_pointer (&self->priv->plugin_allowlist_subsystem_vendor_ids, g_array_unref); g_list_free_full (self->priv->plugin_allowlist_tags, g_free); G_OBJECT_CLASS (mm_filter_parent_class)->finalize (object); } static void log_object_iface_init (MMLogObjectInterface *iface) { iface->build_id = log_object_build_id; } static void mm_filter_class_init (MMFilterClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); g_type_class_add_private (object_class, sizeof (MMFilterPrivate)); /* Virtual methods */ object_class->set_property = set_property; object_class->get_property = get_property; object_class->finalize = finalize; g_object_class_install_property ( object_class, PROP_ENABLED_RULES, g_param_spec_flags (MM_FILTER_ENABLED_RULES, "Enabled rules", "Mask of rules enabled in the filter", MM_TYPE_FILTER_RULE, MM_FILTER_RULE_NONE, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); } ModemManager-1.23.4-dev/src/mm-filter.h000066400000000000000000000121371456466623000175650ustar00rootroot00000000000000/* -*- 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) 2017 Aleksander Morgado */ #ifndef MM_FILTER_H #define MM_FILTER_H #include #include #include "mm-device.h" #include "mm-kernel-device.h" #define MM_TYPE_FILTER (mm_filter_get_type ()) #define MM_FILTER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_FILTER, MMFilter)) #define MM_FILTER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_FILTER, MMFilterClass)) #define MM_IS_FILTER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_FILTER)) #define MM_IS_FILTER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), MM_TYPE_FILTER)) #define MM_FILTER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_FILTER, MMFilterClass)) #define MM_FILTER_ENABLED_RULES "enabled-rules" /* construct-only */ typedef struct _MMFilterPrivate MMFilterPrivate; typedef struct { GObject parent; MMFilterPrivate *priv; } MMFilter; typedef struct { GObjectClass parent; } MMFilterClass; GType mm_filter_get_type (void); G_DEFINE_AUTOPTR_CLEANUP_FUNC (MMFilter, g_object_unref) typedef enum { /*< underscore_name=mm_filter_rule >*/ MM_FILTER_RULE_NONE = 0, MM_FILTER_RULE_EXPLICIT_ALLOWLIST = 1 << 0, MM_FILTER_RULE_EXPLICIT_BLOCKLIST = 1 << 1, MM_FILTER_RULE_PLUGIN_ALLOWLIST = 1 << 2, MM_FILTER_RULE_QRTR = 1 << 3, MM_FILTER_RULE_VIRTUAL = 1 << 4, MM_FILTER_RULE_NET = 1 << 5, MM_FILTER_RULE_USBMISC = 1 << 6, MM_FILTER_RULE_RPMSG = 1 << 7, MM_FILTER_RULE_WWAN = 1 << 8, MM_FILTER_RULE_TTY = 1 << 9, MM_FILTER_RULE_TTY_PLATFORM_DRIVER = 1 << 10, MM_FILTER_RULE_TTY_DRIVER = 1 << 11, MM_FILTER_RULE_TTY_ACM_INTERFACE = 1 << 12, MM_FILTER_RULE_TTY_WITH_NET = 1 << 13, MM_FILTER_RULE_TTY_DEFAULT_FORBIDDEN = 1 << 14, } MMFilterRule; /* This is a stricter policy which will only automatically probe device ports * if they are allowed by any of the automatic allowlist rules. */ #define MM_FILTER_POLICY_STRICT \ (MM_FILTER_RULE_EXPLICIT_ALLOWLIST | \ MM_FILTER_RULE_EXPLICIT_BLOCKLIST | \ MM_FILTER_RULE_PLUGIN_ALLOWLIST | \ MM_FILTER_RULE_QRTR | \ MM_FILTER_RULE_VIRTUAL | \ MM_FILTER_RULE_NET | \ MM_FILTER_RULE_USBMISC | \ MM_FILTER_RULE_RPMSG | \ MM_FILTER_RULE_WWAN | \ MM_FILTER_RULE_TTY | \ MM_FILTER_RULE_TTY_PLATFORM_DRIVER | \ MM_FILTER_RULE_TTY_DRIVER | \ MM_FILTER_RULE_TTY_ACM_INTERFACE | \ MM_FILTER_RULE_TTY_WITH_NET | \ MM_FILTER_RULE_TTY_DEFAULT_FORBIDDEN) /* This policy only allows using device ports explicitly allowlisted via * udev rules. i.e. ModemManager won't do any kind of automatic probing. */ #define MM_FILTER_POLICY_ALLOWLIST_ONLY MM_FILTER_RULE_EXPLICIT_ALLOWLIST /* The strict policy has all supported rules */ #define MM_FILTER_RULE_ALL MM_FILTER_POLICY_STRICT MMFilter *mm_filter_new (MMFilterRule enabled_rules, GError **error); gboolean mm_filter_port (MMFilter *self, MMKernelDevice *port, gboolean manual_scan); gboolean mm_filter_device_and_port (MMFilter *self, MMDevice *device, MMKernelDevice *port); void mm_filter_register_plugin_allowlist_tag (MMFilter *self, const gchar *tag); void mm_filter_register_plugin_allowlist_vendor_id (MMFilter *self, guint16 vid); void mm_filter_register_plugin_allowlist_product_id (MMFilter *self, guint16 vid, guint16 pid); void mm_filter_register_plugin_allowlist_subsystem_vendor_id (MMFilter *self, guint16 vid, guint16 subsystem_vid); gboolean mm_filter_check_rule_enabled (MMFilter *self, MMFilterRule rule); #endif /* MM_FILTER_H */ ModemManager-1.23.4-dev/src/mm-iface-modem-3gpp-profile-manager.c000066400000000000000000002200401456466623000243500ustar00rootroot00000000000000/* -*- 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) 2021 Aleksander Morgado * Copyright (C) 2021 Google, Inc. */ #include #include #include #define _LIBMM_INSIDE_MM #include #include "mm-iface-modem.h" #include "mm-iface-modem-3gpp.h" #include "mm-iface-modem-3gpp-profile-manager.h" #include "mm-base-modem.h" #include "mm-log-object.h" #include "mm-error-helpers.h" #include "mm-log-helpers.h" #define SUPPORT_CHECKED_TAG "3gpp-profile-manager-support-checked-tag" #define SUPPORTED_TAG "3gpp-profile-manager-supported-tag" static GQuark support_checked_quark; static GQuark supported_quark; /*****************************************************************************/ /* Private data context */ #define PRIVATE_TAG "3gpp-profile-manager-private-tag" static GQuark private_quark; typedef struct { /* reported updates should be ignored */ gint update_ignored; /* throttle updated signal */ guint updated_timeout_source; } Private; static void private_free (Private *priv) { if (priv->updated_timeout_source) g_source_remove (priv->updated_timeout_source); g_slice_free (Private, priv); } static Private * get_private (MMIfaceModem3gppProfileManager *self) { Private *priv; if (G_UNLIKELY (!private_quark)) private_quark = g_quark_from_static_string (PRIVATE_TAG); priv = g_object_get_qdata (G_OBJECT (self), private_quark); if (!priv) { priv = g_slice_new0 (Private); g_object_set_qdata_full (G_OBJECT (self), private_quark, priv, (GDestroyNotify)private_free); } return priv; } /*****************************************************************************/ void mm_iface_modem_3gpp_profile_manager_bind_simple_status (MMIfaceModem3gppProfileManager *self, MMSimpleStatus *status) { /* Nothing shown in simple status */ } /*****************************************************************************/ void mm_iface_modem_3gpp_profile_manager_update_ignore_start (MMIfaceModem3gppProfileManager *self) { Private *priv; if (!GPOINTER_TO_UINT (g_object_get_qdata (G_OBJECT (self), supported_quark))) { mm_obj_dbg (self, "skipping profile manager update ignore start: unsupported"); return; } priv = get_private (self); g_assert_cmpint (priv->update_ignored, >=, 0); priv->update_ignored++; mm_obj_dbg (self, "ignoring profile manager updates during our own operations (%d ongoing)", priv->update_ignored); } void mm_iface_modem_3gpp_profile_manager_update_ignore_stop (MMIfaceModem3gppProfileManager *self) { Private *priv; if (!GPOINTER_TO_UINT (g_object_get_qdata (G_OBJECT (self), supported_quark))) { mm_obj_dbg (self, "skipping profile manager update ignore stop: unsupported"); return; } priv = get_private (self); g_assert_cmpint (priv->update_ignored, >, 0); priv->update_ignored--; if (priv->update_ignored > 0) mm_obj_dbg (self, "still ignoring profile manager updates during our own operations (%d ongoing)", priv->update_ignored); else mm_obj_dbg (self, "no longer ignoring profile manager updates during our own operations"); } /* Wait some ms before actually enabling back the update requests */ #define DELAYED_UPDATE_IGNORE_STOP_TIMEOUT_MS 100 static gboolean update_ignore_stop_delayed_cb (MMIfaceModem3gppProfileManager *self) /* full ref */ { mm_iface_modem_3gpp_profile_manager_update_ignore_stop (self); g_object_unref (self); return G_SOURCE_REMOVE; } void mm_iface_modem_3gpp_profile_manager_update_ignore_stop_delayed (MMIfaceModem3gppProfileManager *self) { if (!GPOINTER_TO_UINT (g_object_get_qdata (G_OBJECT (self), supported_quark))) { mm_obj_dbg (self, "skipping profile manager update ignore stop delayed: unsupported"); return; } mm_obj_dbg (self, "delayed request to stop ignoring profile manager updates"); g_timeout_add (DELAYED_UPDATE_IGNORE_STOP_TIMEOUT_MS, (GSourceFunc) update_ignore_stop_delayed_cb, g_object_ref (self)); } /*****************************************************************************/ /* Throttle the amount of "Updated" signals we emit, e.g. so that if we receive * multiple modem indications in a very short time span, we don't emit one signal * for each of them. */ #define UPDATED_TIMEOUT_SECS 2 static gboolean profile_manager_updated_emit (MMIfaceModem3gppProfileManager *self) { g_autoptr(MmGdbusModem3gppProfileManagerSkeleton) skeleton = NULL; Private *priv; priv = get_private (self); g_object_get (self, MM_IFACE_MODEM_3GPP_PROFILE_MANAGER_DBUS_SKELETON, &skeleton, NULL); if (skeleton) { mm_obj_info (self, "emitting profile manager updated..."); mm_gdbus_modem3gpp_profile_manager_emit_updated (MM_GDBUS_MODEM3GPP_PROFILE_MANAGER (skeleton)); } else { mm_obj_warn (self, "skipping profile manager updated signal: interface disabled"); } priv->updated_timeout_source = 0; return G_SOURCE_REMOVE; } void mm_iface_modem_3gpp_profile_manager_updated (MMIfaceModem3gppProfileManager *self) { Private *priv; priv = get_private (self); if (!GPOINTER_TO_UINT (g_object_get_qdata (G_OBJECT (self), supported_quark))) { mm_obj_info (self, "skipping profile manager updated signal: unsupported"); return; } if (priv->update_ignored > 0) { mm_obj_info (self, "skipping profile manager updated signal: ignored"); return; } if (priv->updated_timeout_source) { mm_obj_info (self, "skipping profile manager updated signal: one already scheduled"); return; } mm_obj_info (self, "profile manager updated signal scheduled"); priv->updated_timeout_source = g_timeout_add_seconds (UPDATED_TIMEOUT_SECS, (GSourceFunc) profile_manager_updated_emit, self); } /*****************************************************************************/ /* Set profile (3GPP profile management interface) */ typedef enum { SET_PROFILE_STEP_FIRST, SET_PROFILE_STEP_CHECK_FORMAT, SET_PROFILE_STEP_LIST_BEFORE, SET_PROFILE_STEP_SELECT_PROFILE, SET_PROFILE_STEP_CHECK_ACTIVATED_PROFILE, SET_PROFILE_STEP_DEACTIVATE_PROFILE, SET_PROFILE_STEP_STORE_PROFILE, SET_PROFILE_STEP_LIST_AFTER, SET_PROFILE_STEP_LAST, } SetProfileStep; typedef struct { SetProfileStep step; MM3gppProfile *requested; gchar *index_field; gchar *index_field_value_str; gboolean strict; gboolean new_id; gint min_profile_id; gint max_profile_id; GEqualFunc profile_apn_cmp; MM3gppProfileCmpFlags profile_cmp_flags; gint profile_id; MMBearerApnType apn_type; gchar *apn_type_str; GList *before_list; MM3gppProfile *stored; } SetProfileContext; static void set_profile_context_free (SetProfileContext *ctx) { mm_3gpp_profile_list_free (ctx->before_list); g_clear_object (&ctx->requested); g_clear_object (&ctx->stored); g_free (ctx->apn_type_str); g_free (ctx->index_field); g_free (ctx->index_field_value_str); g_slice_free (SetProfileContext, ctx); } MM3gppProfile * mm_iface_modem_3gpp_profile_manager_set_profile_finish (MMIfaceModem3gppProfileManager *self, GAsyncResult *res, GError **error) { return MM_3GPP_PROFILE (g_task_propagate_pointer (G_TASK (res), error)); } static void set_profile_step (GTask *task); static void profile_manager_get_profile_after_ready (MMIfaceModem3gppProfileManager *self, GAsyncResult *res, GTask *task) { SetProfileContext *ctx; GError *error = NULL; ctx = g_task_get_task_data (task); ctx->stored = mm_iface_modem_3gpp_profile_manager_get_profile_finish (self, res, &error); if (!ctx->stored) { g_prefix_error (&error, "Couldn't validate update of profile '%d': ", ctx->profile_id); g_task_return_error (task, error); g_object_unref (task); return; } ctx->step++; set_profile_step (task); } static void set_profile_step_list_after (GTask *task) { MMIfaceModem3gppProfileManager *self; SetProfileContext *ctx; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); mm_iface_modem_3gpp_profile_manager_get_profile ( self, ctx->profile_id, (GAsyncReadyCallback)profile_manager_get_profile_after_ready, task); } static void profile_manager_store_profile_ready (MMIfaceModem3gppProfileManager *self, GAsyncResult *res, GTask *task) { SetProfileContext *ctx; GError *error = NULL; gint profile_id; MMBearerApnType apn_type; ctx = g_task_get_task_data (task); if (!MM_IFACE_MODEM_3GPP_PROFILE_MANAGER_GET_INTERFACE (self)->store_profile_finish (self, res, &profile_id, &apn_type, &error)) { g_task_return_error (task, error); g_object_unref (task); return; } /* when creating a new profile with an unbound input profile id, store the * one received after store */ if (g_strcmp0 (ctx->index_field, "profile-id") == 0) { if (ctx->profile_id == MM_3GPP_PROFILE_ID_UNKNOWN) { ctx->profile_id = profile_id; g_free (ctx->index_field_value_str); ctx->index_field_value_str = g_strdup_printf ("%d", ctx->profile_id); } g_assert (ctx->profile_id == profile_id); } mm_obj_dbg (self, "stored profile '%s'", ctx->index_field_value_str); ctx->step++; set_profile_step (task); } static void set_profile_step_store_profile (GTask *task) { MMIfaceModem3gppProfileManager *self; SetProfileContext *ctx; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); g_assert (!ctx->stored); MM_IFACE_MODEM_3GPP_PROFILE_MANAGER_GET_INTERFACE (self)->store_profile ( self, ctx->requested, ctx->index_field, (GAsyncReadyCallback) profile_manager_store_profile_ready, task); } static void profile_manager_deactivate_profile_ready (MMIfaceModem3gppProfileManager *self, GAsyncResult *res, GTask *task) { SetProfileContext *ctx; g_autoptr(GError) error = NULL; ctx = g_task_get_task_data (task); /* profile deactivation errors aren't fatal per se */ if (!MM_IFACE_MODEM_3GPP_PROFILE_MANAGER_GET_INTERFACE (self)->deactivate_profile_finish (self, res, &error)) mm_obj_dbg (self, "couldn't deactivate profile '%s': %s", ctx->index_field_value_str, error->message); else mm_obj_dbg (self, "deactivated profile '%s'", ctx->index_field_value_str); ctx->step++; set_profile_step (task); } static void set_profile_step_deactivate_profile (GTask *task) { MMIfaceModem3gppProfileManager *self; SetProfileContext *ctx; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); if (!MM_IFACE_MODEM_3GPP_PROFILE_MANAGER_GET_INTERFACE (self)->deactivate_profile || !MM_IFACE_MODEM_3GPP_PROFILE_MANAGER_GET_INTERFACE (self)->deactivate_profile_finish) { mm_obj_dbg (self, "skipping profile deactivation"); ctx->step++; set_profile_step (task); return; } /* This profile deactivation is EXCLUSIVELY done for those profiles that * are connected in the modem but for which we don't have any connected * bearer tracked. This covers e.g. a clean recovery of a previous daemon * crash, and is now defined as a supported step in the core logic, instead * of doing it differently in the different plugins and protocols. */ MM_IFACE_MODEM_3GPP_PROFILE_MANAGER_GET_INTERFACE (self)->deactivate_profile ( self, ctx->requested, (GAsyncReadyCallback) profile_manager_deactivate_profile_ready, task); } static void profile_manager_check_activated_profile_ready (MMIfaceModem3gppProfileManager *self, GAsyncResult *res, GTask *task) { SetProfileContext *ctx; gboolean activated = TRUE; g_autoptr(GError) error = NULL; ctx = g_task_get_task_data (task); if (!MM_IFACE_MODEM_3GPP_PROFILE_MANAGER_GET_INTERFACE (self)->check_activated_profile_finish (self, res, &activated, &error)) { if (g_error_matches (error, MM_CORE_ERROR, MM_CORE_ERROR_NOT_FOUND)) { mm_obj_dbg (self, "profile '%s' is not activated: %s", ctx->index_field_value_str, error->message); ctx->step = SET_PROFILE_STEP_STORE_PROFILE; } else { mm_obj_dbg (self, "couldn't check if profile '%s' is activated: %s", ctx->index_field_value_str, error->message); ctx->step = SET_PROFILE_STEP_DEACTIVATE_PROFILE; } } else if (activated) { mm_obj_dbg (self, "profile '%s' is activated", ctx->index_field_value_str); ctx->step = SET_PROFILE_STEP_DEACTIVATE_PROFILE; } else { mm_obj_dbg (self, "profile '%s' is not activated", ctx->index_field_value_str); ctx->step = SET_PROFILE_STEP_STORE_PROFILE; } set_profile_step (task); } static gboolean profile_manager_fail_if_connected_bearer (MMIfaceModem3gppProfileManager *self, const gchar *index_field, gint profile_id, MMBearerApnType apn_type, GError **error) { g_autoptr(MMBearerList) bearer_list = NULL; g_autoptr(MMBaseBearer) bearer = NULL; g_object_get (self, MM_IFACE_MODEM_BEARER_LIST, &bearer_list, NULL); if (bearer_list) { if (g_strcmp0 (index_field, "profile-id") == 0) bearer = mm_bearer_list_find_by_profile_id (bearer_list, profile_id); else if (g_strcmp0 (index_field, "apn-type") == 0) bearer = mm_bearer_list_find_by_apn_type (bearer_list, apn_type); else g_assert_not_reached (); } /* If a bearer is found reporting the profile id we're targeting to use, * it means we have a known connected bearer, and we must abort the * operation right away. */ if (bearer) { if (g_strcmp0 (index_field, "profile-id") == 0) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_CONNECTED, "Cannot use profile %d: found an already connected bearer", profile_id); } else if (g_strcmp0 (index_field, "apn-type") == 0) { g_autofree gchar *apn_type_str = NULL; apn_type_str = mm_bearer_apn_type_build_string_from_mask (apn_type); g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_CONNECTED, "Cannot use profile %s: found an already connected bearer", apn_type_str); } else g_assert_not_reached (); return FALSE; } return TRUE; } static void set_profile_step_check_activated_profile (GTask *task) { MMIfaceModem3gppProfileManager *self; SetProfileContext *ctx; GError *error = NULL; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); g_assert (((g_strcmp0 (ctx->index_field, "profile-id") == 0) && (ctx->profile_id != MM_3GPP_PROFILE_ID_UNKNOWN)) || ((g_strcmp0 (ctx->index_field, "apn-type") == 0) && (ctx->apn_type != MM_BEARER_APN_TYPE_NONE))); /* First, a quick check on our own bearer list. If we have a known bearer * connected using the same profile id, we fail the operation right away. */ if (!profile_manager_fail_if_connected_bearer (self, ctx->index_field, ctx->profile_id, ctx->apn_type, &error)) { g_task_return_error (task, error); g_object_unref (task); return; } if (!MM_IFACE_MODEM_3GPP_PROFILE_MANAGER_GET_INTERFACE (self)->check_activated_profile || !MM_IFACE_MODEM_3GPP_PROFILE_MANAGER_GET_INTERFACE (self)->check_activated_profile_finish) { ctx->step = SET_PROFILE_STEP_DEACTIVATE_PROFILE; set_profile_step (task); return; } /* Second, an actual query to the modem, in order to trigger the profile * deactivation before we attempt to activate it again */ MM_IFACE_MODEM_3GPP_PROFILE_MANAGER_GET_INTERFACE (self)->check_activated_profile ( self, ctx->requested, (GAsyncReadyCallback) profile_manager_check_activated_profile_ready, task); } static void set_profile_step_select_profile_exact (GTask *task) { MMIfaceModem3gppProfileManager *self; SetProfileContext *ctx; GError *error = NULL; g_autoptr(MM3gppProfile) existing = NULL; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); /* Look for the exact profile we want to use */ if (g_strcmp0 (ctx->index_field, "profile-id") == 0) { g_assert (ctx->profile_id != MM_3GPP_PROFILE_ID_UNKNOWN); existing = mm_3gpp_profile_list_find_by_profile_id (ctx->before_list, ctx->profile_id, &error); } else if (g_strcmp0 (ctx->index_field, "apn-type") == 0) { g_assert (ctx->apn_type != MM_BEARER_APN_TYPE_NONE); existing = mm_3gpp_profile_list_find_by_apn_type (ctx->before_list, ctx->apn_type, &error); } else g_assert_not_reached (); if (!existing) { g_task_return_error (task, error); g_object_unref (task); return; } /* If the profile is 100% equal to what we require, nothing to do */ if (mm_3gpp_profile_cmp (existing, ctx->requested, ctx->profile_apn_cmp, ctx->profile_cmp_flags)) { mm_obj_dbg (self, "reusing profile '%s'", ctx->index_field_value_str); ctx->stored = g_object_ref (existing); } else mm_obj_dbg (self, "overwritting profile '%s'", ctx->index_field_value_str); ctx->step++; set_profile_step (task); } static void set_profile_step_select_profile_new (GTask *task) { MMIfaceModem3gppProfileManager *self; SetProfileContext *ctx; GError *error = NULL; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); g_assert (g_strcmp0 (ctx->index_field, "profile-id") == 0); g_assert (ctx->profile_id == MM_3GPP_PROFILE_ID_UNKNOWN); g_assert (ctx->strict); /* If strict set required, fail if we cannot find an empty profile id */ ctx->profile_id = mm_3gpp_profile_list_find_empty (ctx->before_list, ctx->min_profile_id, ctx->max_profile_id, &error); if (ctx->profile_id == MM_3GPP_PROFILE_ID_UNKNOWN) { g_task_return_error (task, error); g_object_unref (task); return; } /* store profile id in the requested profile */ mm_3gpp_profile_set_profile_id (ctx->requested, ctx->profile_id); mm_obj_dbg (self, "creating profile '%d'", ctx->profile_id); ctx->step++; set_profile_step (task); } static void set_profile_step_select_profile_best (GTask *task) { MMIfaceModem3gppProfileManager *self; SetProfileContext *ctx; gboolean overwritten = FALSE; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); g_assert (g_strcmp0 (ctx->index_field, "profile-id") == 0); g_assert (ctx->profile_id == MM_3GPP_PROFILE_ID_UNKNOWN); g_assert (!ctx->strict); ctx->profile_id = mm_3gpp_profile_list_find_best (ctx->before_list, ctx->requested, ctx->profile_apn_cmp, ctx->profile_cmp_flags, ctx->min_profile_id, ctx->max_profile_id, self, &ctx->stored, &overwritten); /* store profile id in the requested profile */ mm_3gpp_profile_set_profile_id (ctx->requested, ctx->profile_id); /* If we're reusing an already existing profile, we're done at this * point, no need to create a new one */ if (ctx->stored) mm_obj_dbg (self, "reusing profile '%d'", ctx->profile_id); else if (overwritten) mm_obj_dbg (self, "overwriting profile '%d'", ctx->profile_id); ctx->step++; set_profile_step (task); } static void profile_manager_list_profiles_before_ready (MMIfaceModem3gppProfileManager *self, GAsyncResult *res, GTask *task) { SetProfileContext *ctx; g_autoptr(GError) error = NULL; ctx = g_task_get_task_data (task); if (!mm_iface_modem_3gpp_profile_manager_list_profiles_finish (self, res, &ctx->before_list, &error)) mm_obj_dbg (self, "failed checking currently defined contexts: %s", error->message); ctx->step++; set_profile_step (task); } static void set_profile_step_list_before (GTask *task) { MMIfaceModem3gppProfileManager *self; self = g_task_get_source_object (task); mm_iface_modem_3gpp_profile_manager_list_profiles ( self, (GAsyncReadyCallback)profile_manager_list_profiles_before_ready, task); } static void set_profile_check_format_ready (MMIfaceModem3gppProfileManager *self, GAsyncResult *res, GTask *task) { SetProfileContext *ctx; ctx = g_task_get_task_data (task); if (!MM_IFACE_MODEM_3GPP_PROFILE_MANAGER_GET_INTERFACE (self)->check_format_finish ( self, res, &ctx->new_id, &ctx->min_profile_id, &ctx->max_profile_id, &ctx->profile_apn_cmp, &ctx->profile_cmp_flags, NULL)) { ctx->min_profile_id = 1; ctx->max_profile_id = G_MAXINT-1; mm_obj_dbg (self, "unknown context definition format; using defaults: minimum %d, maximum %d", ctx->min_profile_id, ctx->max_profile_id); } else mm_obj_dbg (self, "context definition format: minimum %d, maximum %d", ctx->min_profile_id, ctx->max_profile_id); ctx->step++; set_profile_step (task); } static void set_profile_step_check_format (GTask *task) { MMIfaceModem3gppProfileManager *self; SetProfileContext *ctx; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); MM_IFACE_MODEM_3GPP_PROFILE_MANAGER_GET_INTERFACE (self)->check_format ( self, mm_3gpp_profile_get_ip_type (ctx->requested), (GAsyncReadyCallback)set_profile_check_format_ready, task); } static void set_profile_step (GTask *task) { MMIfaceModem3gppProfileManager *self; SetProfileContext *ctx; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); switch (ctx->step) { case SET_PROFILE_STEP_FIRST: ctx->step++; /* Fall through */ case SET_PROFILE_STEP_CHECK_FORMAT: mm_obj_dbg (self, "set profile state (%d/%d): check format", ctx->step, SET_PROFILE_STEP_LAST); set_profile_step_check_format (task); return; case SET_PROFILE_STEP_LIST_BEFORE: mm_obj_dbg (self, "set profile state (%d/%d): list before", ctx->step, SET_PROFILE_STEP_LAST); set_profile_step_list_before (task); return; case SET_PROFILE_STEP_SELECT_PROFILE: if (((g_strcmp0 (ctx->index_field, "profile-id") == 0) && (ctx->profile_id != MM_3GPP_PROFILE_ID_UNKNOWN)) || (g_strcmp0 (ctx->index_field, "apn-type") == 0)) { mm_obj_dbg (self, "set profile state (%d/%d): select profile (exact)", ctx->step, SET_PROFILE_STEP_LAST); set_profile_step_select_profile_exact (task); return; } /* when using profile-id, allow non-strict and new */ if (!ctx->strict) { mm_obj_dbg (self, "set profile state (%d/%d): select profile (best)", ctx->step, SET_PROFILE_STEP_LAST); set_profile_step_select_profile_best (task); return; } if (ctx->new_id) { mm_obj_dbg (self, "set profile state (%d/%d): select profile (new)", ctx->step, SET_PROFILE_STEP_LAST); set_profile_step_select_profile_new (task); return; } mm_obj_dbg (self, "set profile state (%d/%d): select profile (none)", ctx->step, SET_PROFILE_STEP_LAST); ctx->step++; /* Fall through */ case SET_PROFILE_STEP_CHECK_ACTIVATED_PROFILE: /* If the modem/protocol doesn't allow preselecting the profile id of * a new profile we're going to create, then we won't have a profile id * set at this point. If so, just skip this step. */ if (((g_strcmp0 (ctx->index_field, "profile-id") == 0) && (ctx->profile_id != MM_3GPP_PROFILE_ID_UNKNOWN)) || (g_strcmp0 (ctx->index_field, "apn-type") == 0)) { mm_obj_dbg (self, "set profile state (%d/%d): check activated profile", ctx->step, SET_PROFILE_STEP_LAST); set_profile_step_check_activated_profile (task); return; } ctx->step++; /* Fall through */ case SET_PROFILE_STEP_DEACTIVATE_PROFILE: /* If the modem/protocol doesn't allow preselecting the profile id of * a new profile we're going to create, then we won't have a profile id * set at this point. If so, just skip this step. */ if (((g_strcmp0 (ctx->index_field, "profile-id") == 0) && (ctx->profile_id != MM_3GPP_PROFILE_ID_UNKNOWN)) || (g_strcmp0 (ctx->index_field, "apn-type") == 0)) { mm_obj_dbg (self, "set profile state (%d/%d): deactivate profile", ctx->step, SET_PROFILE_STEP_LAST); set_profile_step_deactivate_profile (task); return; } ctx->step++; /* Fall through */ case SET_PROFILE_STEP_STORE_PROFILE: /* if we're reusing an already existing profile, we can jump * to the last step now, there is no need to store any update */ if (ctx->stored) { mm_obj_dbg (self, "set profile state (%d/%d): profile already stored", ctx->step, SET_PROFILE_STEP_LAST); ctx->step = SET_PROFILE_STEP_LAST; set_profile_step (task); return; } mm_obj_dbg (self, "set profile state (%d/%d): store profile", ctx->step, SET_PROFILE_STEP_LAST); set_profile_step_store_profile (task); return; case SET_PROFILE_STEP_LIST_AFTER: mm_obj_dbg (self, "set profile state (%d/%d): list after", ctx->step, SET_PROFILE_STEP_LAST); set_profile_step_list_after (task); return; case SET_PROFILE_STEP_LAST: mm_obj_dbg (self, "set profile state (%d/%d): all done", ctx->step, SET_PROFILE_STEP_LAST); g_assert (ctx->stored); g_task_return_pointer (task, g_steal_pointer (&ctx->stored), g_object_unref); g_object_unref (task); return; default: g_assert_not_reached (); } } void mm_iface_modem_3gpp_profile_manager_set_profile (MMIfaceModem3gppProfileManager *self, MM3gppProfile *requested, const gchar *index_field, gboolean strict, GAsyncReadyCallback callback, gpointer user_data) { GError *error; GTask *task; SetProfileContext *ctx; MMBearerIpFamily ip_family; g_autoptr(GVariant) dict = NULL; g_autoptr(MM3gppProfile) requested_copy = NULL; task = g_task_new (self, NULL, callback, user_data); /* The MM3gppProfile passed to the SetProfileContext is going to * be modified, so we make a copy to preserve the original one. */ dict = mm_3gpp_profile_get_dictionary (requested); requested_copy = mm_3gpp_profile_new_from_dictionary (dict, &error); if (!requested_copy) { g_prefix_error (&error, "Couldn't copy 3GPP profile:"); g_task_return_error (task, error); g_object_unref (task); return; } ctx = g_slice_new0 (SetProfileContext); ctx->step = SET_PROFILE_STEP_FIRST; ctx->requested = g_object_ref (requested_copy); ctx->index_field = g_strdup (index_field); ctx->strict = strict; ctx->profile_id = mm_3gpp_profile_get_profile_id (ctx->requested); ctx->apn_type = mm_3gpp_profile_get_apn_type (ctx->requested); ctx->apn_type_str = mm_bearer_apn_type_build_string_from_mask (ctx->apn_type); g_task_set_task_data (task, ctx, (GDestroyNotify)set_profile_context_free); /* Validate input setup: * - allow 'profile-id' as index field, both strict and not strict. * - allow 'apn-type' as index field, always strict. */ if (g_strcmp0 (ctx->index_field, "profile-id") == 0) ctx->index_field_value_str = g_strdup_printf ("%d", ctx->profile_id); else if (g_strcmp0 (ctx->index_field, "apn-type") == 0) { g_assert (ctx->strict); ctx->index_field_value_str = g_strdup (ctx->apn_type_str); /* when using apn-type as index, the field is mandatory because both "create" * and "update" are actually the same operation. */ if (ctx->apn_type == MM_BEARER_APN_TYPE_NONE) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS, "Missing index field ('apn-type') in profile settings"); g_object_unref (task); return; } } else g_assert_not_reached (); /* normalize IP family right away */ ip_family = mm_3gpp_profile_get_ip_type (ctx->requested); mm_3gpp_normalize_ip_family (&ip_family, TRUE); mm_3gpp_profile_set_ip_type (ctx->requested, ip_family); set_profile_step (task); } /*****************************************************************************/ MM3gppProfile * mm_iface_modem_3gpp_profile_manager_get_profile_finish (MMIfaceModem3gppProfileManager *self, GAsyncResult *res, GError **error) { return MM_3GPP_PROFILE (g_task_propagate_pointer (G_TASK (res), error)); } static void get_profile_list_ready (MMIfaceModem3gppProfileManager *self, GAsyncResult *res, GTask *task) { GError *error = NULL; GList *profiles = NULL; gint profile_id; MM3gppProfile *profile; profile_id = GPOINTER_TO_INT (g_task_get_task_data (task)); if (!MM_IFACE_MODEM_3GPP_PROFILE_MANAGER_GET_INTERFACE (self)->list_profiles_finish (self, res, &profiles, &error)) { g_task_return_error (task, error); g_object_unref (task); return; } profile = mm_3gpp_profile_list_find_by_profile_id (profiles, profile_id, &error); mm_3gpp_profile_list_free (profiles); if (!profile) { g_task_return_error (task, error); g_object_unref (task); return; } g_task_return_pointer (task, profile, g_object_unref); g_object_unref (task); } static void get_profile_single_ready (MMIfaceModem3gppProfileManager *self, GAsyncResult *res, GTask *task) { GError *error = NULL; MM3gppProfile *profile; profile = MM_IFACE_MODEM_3GPP_PROFILE_MANAGER_GET_INTERFACE (self)->get_profile_finish (self, res, &error); if (!profile) g_task_return_error (task, error); else g_task_return_pointer (task, profile, g_object_unref); g_object_unref (task); } void mm_iface_modem_3gpp_profile_manager_get_profile (MMIfaceModem3gppProfileManager *self, gint profile_id, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; task = g_task_new (self, NULL, callback, user_data); if (MM_IFACE_MODEM_3GPP_PROFILE_MANAGER_GET_INTERFACE (self)->get_profile && MM_IFACE_MODEM_3GPP_PROFILE_MANAGER_GET_INTERFACE (self)->get_profile_finish) { MM_IFACE_MODEM_3GPP_PROFILE_MANAGER_GET_INTERFACE (self)->get_profile (self, profile_id, (GAsyncReadyCallback)get_profile_single_ready, task); return; } /* If there is no way to query one single profile, query all and filter */ g_task_set_task_data (task, GINT_TO_POINTER (profile_id), NULL); mm_iface_modem_3gpp_profile_manager_list_profiles (self, (GAsyncReadyCallback)get_profile_list_ready, task); } /*****************************************************************************/ typedef struct { GList *profiles; } ListProfilesContext; static void list_profiles_context_free (ListProfilesContext *ctx) { mm_3gpp_profile_list_free (ctx->profiles); g_slice_free (ListProfilesContext, ctx); } gboolean mm_iface_modem_3gpp_profile_manager_list_profiles_finish (MMIfaceModem3gppProfileManager *self, GAsyncResult *res, GList **out_profiles, GError **error) { ListProfilesContext *ctx; if (!g_task_propagate_boolean (G_TASK (res), error)) return FALSE; ctx = g_task_get_task_data (G_TASK (res)); if (out_profiles) *out_profiles = g_steal_pointer (&ctx->profiles); return TRUE; } static void internal_list_profiles_ready (MMIfaceModem3gppProfileManager *self, GAsyncResult *res, GTask *task) { ListProfilesContext *ctx; GError *error = NULL; ctx = g_slice_new0 (ListProfilesContext); g_task_set_task_data (task, ctx, (GDestroyNotify) list_profiles_context_free); if (!MM_IFACE_MODEM_3GPP_PROFILE_MANAGER_GET_INTERFACE (self)->list_profiles_finish (self, res, &ctx->profiles, &error)) g_task_return_error (task, error); else g_task_return_boolean (task, TRUE); g_object_unref (task); } void mm_iface_modem_3gpp_profile_manager_list_profiles (MMIfaceModem3gppProfileManager *self, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; task = g_task_new (self, NULL, callback, user_data); /* Internal calls to the list profile logic may be performed even if the 3GPP Profile Manager * interface is not exposed in DBus, therefore, make sure this logic exits cleanly if there * is no support for listing profiles */ if (!MM_IFACE_MODEM_3GPP_PROFILE_MANAGER_GET_INTERFACE (self)->list_profiles || !MM_IFACE_MODEM_3GPP_PROFILE_MANAGER_GET_INTERFACE (self)->list_profiles_finish) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "Listing profiles is unsupported"); g_object_unref (task); return; } MM_IFACE_MODEM_3GPP_PROFILE_MANAGER_GET_INTERFACE (self)->list_profiles ( MM_IFACE_MODEM_3GPP_PROFILE_MANAGER (self), (GAsyncReadyCallback)internal_list_profiles_ready, task); } /*****************************************************************************/ typedef struct { MmGdbusModem3gppProfileManager *skeleton; GDBusMethodInvocation *invocation; MMIfaceModem3gppProfileManager *self; } HandleListContext; static void handle_list_context_free (HandleListContext *ctx) { g_object_unref (ctx->skeleton); g_object_unref (ctx->invocation); g_object_unref (ctx->self); g_slice_free (HandleListContext, ctx); } static GVariant * build_list_profiles_result (MMIfaceModem3gppProfileManager *self, GList *profiles) { GVariantBuilder builder; GList *l; guint i; /* Build array of dicts */ g_variant_builder_init (&builder, G_VARIANT_TYPE ("aa{sv}")); for (l = profiles, i = 0; l; l = g_list_next (l), i++) { g_autoptr(GVariant) dict = NULL; MM3gppProfile *profile; profile = MM_3GPP_PROFILE (l->data); mm_obj_info (self, "profile %u:", i); mm_log_3gpp_profile (self, MM_LOG_LEVEL_INFO, " ", profile); dict = mm_3gpp_profile_get_dictionary (profile); g_variant_builder_add_value (&builder, dict); } return g_variant_ref_sink (g_variant_builder_end (&builder)); } static void list_profiles_ready (MMIfaceModem3gppProfileManager *self, GAsyncResult *res, HandleListContext *ctx) { GError *error = NULL; GList *profiles = NULL; g_autoptr(GVariant) dict_array = NULL; if (!mm_iface_modem_3gpp_profile_manager_list_profiles_finish (self, res, &profiles, &error)) { mm_obj_warn (self, "failed listing 3GPP profiles: %s", error->message); mm_dbus_method_invocation_take_error (ctx->invocation, error); handle_list_context_free (ctx); return; } mm_obj_info (self, "listed 3GPP profiles: %u found", g_list_length (profiles)); dict_array = build_list_profiles_result (self, profiles); mm_gdbus_modem3gpp_profile_manager_complete_list (ctx->skeleton, ctx->invocation, dict_array); mm_3gpp_profile_list_free (profiles); handle_list_context_free (ctx); } static void handle_list_auth_ready (MMBaseModem *self, GAsyncResult *res, HandleListContext *ctx) { GError *error = NULL; if (!mm_base_modem_authorize_finish (self, res, &error)) { mm_dbus_method_invocation_take_error (ctx->invocation, error); handle_list_context_free (ctx); return; } if (mm_iface_modem_abort_invocation_if_state_not_reached (MM_IFACE_MODEM (self), ctx->invocation, MM_MODEM_STATE_ENABLED)) { handle_list_context_free (ctx); return; } mm_obj_info (self, "processing user request to list 3GPP profiles..."); /* Don't call the class callback directly, use the common helper method * that is also used by other internal operations. */ mm_iface_modem_3gpp_profile_manager_list_profiles ( MM_IFACE_MODEM_3GPP_PROFILE_MANAGER (self), (GAsyncReadyCallback)list_profiles_ready, ctx); } static gboolean handle_list (MmGdbusModem3gppProfileManager *skeleton, GDBusMethodInvocation *invocation, MMIfaceModem3gppProfileManager *self) { HandleListContext *ctx; ctx = g_slice_new0 (HandleListContext); ctx->skeleton = g_object_ref (skeleton); ctx->invocation = g_object_ref (invocation); ctx->self = g_object_ref (self); mm_base_modem_authorize (MM_BASE_MODEM (self), invocation, MM_AUTHORIZATION_DEVICE_CONTROL, (GAsyncReadyCallback)handle_list_auth_ready, ctx); return TRUE; } /*****************************************************************************/ typedef struct { MmGdbusModem3gppProfileManager *skeleton; GDBusMethodInvocation *invocation; GVariant *requested_dictionary; MMIfaceModem3gppProfileManager *self; } HandleSetContext; static void handle_set_context_free (HandleSetContext *ctx) { g_clear_pointer (&ctx->requested_dictionary, g_variant_unref); g_object_unref (ctx->skeleton); g_object_unref (ctx->invocation); g_object_unref (ctx->self); g_slice_free (HandleSetContext, ctx); } static void set_profile_ready (MMIfaceModem3gppProfileManager *self, GAsyncResult *res, HandleSetContext *ctx) { GError *error = NULL; g_autoptr(MM3gppProfile) profile_stored = NULL; g_autoptr(GVariant) profile_dictionary = NULL; profile_stored = mm_iface_modem_3gpp_profile_manager_set_profile_finish (self, res, &error); if (!profile_stored) { mm_obj_warn (self, "failed setting 3GPP profile: %s", error->message); /* process profile manager updates right away on error */ mm_iface_modem_3gpp_profile_manager_update_ignore_stop (self); mm_dbus_method_invocation_take_error (ctx->invocation, error); handle_set_context_free (ctx); return; } mm_obj_info (self, "3GPP profile set:"); mm_log_3gpp_profile (self, MM_LOG_LEVEL_INFO, " ", profile_stored); /* delay processing profile manager updates on success */ mm_iface_modem_3gpp_profile_manager_update_ignore_stop_delayed (self); profile_dictionary = mm_3gpp_profile_get_dictionary (profile_stored); mm_gdbus_modem3gpp_profile_manager_complete_set (ctx->skeleton, ctx->invocation, profile_dictionary); handle_set_context_free (ctx); } static void handle_set_auth_ready (MMBaseModem *self, GAsyncResult *res, HandleSetContext *ctx) { const gchar *index_field; GError *error = NULL; g_autoptr(MM3gppProfile) profile_requested = NULL; if (!mm_base_modem_authorize_finish (self, res, &error)) { mm_dbus_method_invocation_take_error (ctx->invocation, error); handle_set_context_free (ctx); return; } if (mm_iface_modem_abort_invocation_if_state_not_reached (MM_IFACE_MODEM (self), ctx->invocation, MM_MODEM_STATE_ENABLED)) { handle_set_context_free (ctx); return; } if (!ctx->requested_dictionary) { mm_dbus_method_invocation_return_error_literal (ctx->invocation, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS, "Missing requested profile settings"); handle_set_context_free (ctx); return; } profile_requested = mm_3gpp_profile_new_from_dictionary (ctx->requested_dictionary, &error); if (!profile_requested) { mm_dbus_method_invocation_take_error (ctx->invocation, error); handle_set_context_free (ctx); return; } mm_obj_info (self, "processing user request to set 3GPP profile..."); mm_log_3gpp_profile (self, MM_LOG_LEVEL_INFO, " ", profile_requested); index_field = mm_gdbus_modem3gpp_profile_manager_get_index_field (ctx->skeleton); /* Start ignoring our own indications */ mm_iface_modem_3gpp_profile_manager_update_ignore_start (MM_IFACE_MODEM_3GPP_PROFILE_MANAGER (self)); /* Don't call the class callback directly, use the common helper method * that is also used by other internal operations. */ mm_iface_modem_3gpp_profile_manager_set_profile ( MM_IFACE_MODEM_3GPP_PROFILE_MANAGER (self), profile_requested, index_field, TRUE, /* strict always! */ (GAsyncReadyCallback)set_profile_ready, ctx); } static gboolean handle_set (MmGdbusModem3gppProfileManager *skeleton, GDBusMethodInvocation *invocation, GVariant *requested_dictionary, MMIfaceModem3gppProfileManager *self) { HandleSetContext *ctx; ctx = g_slice_new0 (HandleSetContext); ctx->skeleton = g_object_ref (skeleton); ctx->invocation = g_object_ref (invocation); ctx->self = g_object_ref (self); ctx->requested_dictionary = requested_dictionary ? g_variant_ref (requested_dictionary) : NULL; mm_base_modem_authorize (MM_BASE_MODEM (self), invocation, MM_AUTHORIZATION_DEVICE_CONTROL, (GAsyncReadyCallback)handle_set_auth_ready, ctx); return TRUE; } /*****************************************************************************/ typedef struct { MmGdbusModem3gppProfileManager *skeleton; GDBusMethodInvocation *invocation; GVariant *dictionary; MMIfaceModem3gppProfileManager *self; } HandleDeleteContext; static void handle_delete_context_free (HandleDeleteContext *ctx) { g_clear_pointer (&ctx->dictionary, g_variant_unref); g_object_unref (ctx->skeleton); g_object_unref (ctx->invocation); g_object_unref (ctx->self); g_slice_free (HandleDeleteContext, ctx); } static void delete_profile_ready (MMIfaceModem3gppProfileManager *self, GAsyncResult *res, HandleDeleteContext *ctx) { GError *error = NULL; if (!MM_IFACE_MODEM_3GPP_PROFILE_MANAGER_GET_INTERFACE (self)->delete_profile_finish (self, res, &error)) { mm_obj_warn (self, "failed deleting 3GPP profile: %s", error->message); /* process profile manager updates right away on error */ mm_iface_modem_3gpp_profile_manager_update_ignore_stop (self); mm_dbus_method_invocation_take_error (ctx->invocation, error); } else { mm_obj_info (self, "3GPP profile deleted"); /* delay processing profile manager updates on success */ mm_iface_modem_3gpp_profile_manager_update_ignore_stop_delayed (self); mm_gdbus_modem3gpp_profile_manager_complete_delete (ctx->skeleton, ctx->invocation); } handle_delete_context_free (ctx); } static void handle_delete_auth_ready (MMBaseModem *self, GAsyncResult *res, HandleDeleteContext *ctx) { const gchar *index_field; GError *error = NULL; g_autoptr(MM3gppProfile) profile = NULL; gint profile_id = MM_3GPP_PROFILE_ID_UNKNOWN; MMBearerApnType apn_type = MM_BEARER_APN_TYPE_NONE; if (!mm_base_modem_authorize_finish (self, res, &error)) { mm_dbus_method_invocation_take_error (ctx->invocation, error); handle_delete_context_free (ctx); return; } if (mm_iface_modem_abort_invocation_if_state_not_reached (MM_IFACE_MODEM (self), ctx->invocation, MM_MODEM_STATE_ENABLED)) { handle_delete_context_free (ctx); return; } if (!MM_IFACE_MODEM_3GPP_PROFILE_MANAGER_GET_INTERFACE (self)->delete_profile || !MM_IFACE_MODEM_3GPP_PROFILE_MANAGER_GET_INTERFACE (self)->delete_profile_finish) { mm_dbus_method_invocation_return_error_literal (ctx->invocation, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "Deleting profiles is not supported"); handle_delete_context_free (ctx); return; } if (!ctx->dictionary) { mm_dbus_method_invocation_return_error_literal (ctx->invocation, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS, "Missing profile settings"); handle_delete_context_free (ctx); return; } profile = mm_3gpp_profile_new_from_dictionary (ctx->dictionary, &error); if (!profile) { mm_dbus_method_invocation_take_error (ctx->invocation, error); handle_delete_context_free (ctx); return; } index_field = mm_gdbus_modem3gpp_profile_manager_get_index_field (ctx->skeleton); if (g_strcmp0 (index_field, "profile-id") == 0) { profile_id = mm_3gpp_profile_get_profile_id (profile); if (profile_id == MM_3GPP_PROFILE_ID_UNKNOWN) { mm_dbus_method_invocation_return_error_literal (ctx->invocation, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS, "Missing index field ('profile-id') in profile settings"); handle_delete_context_free (ctx); return; } } else if (g_strcmp0 (index_field, "apn-type") == 0) { apn_type = mm_3gpp_profile_get_apn_type (profile); if (apn_type == MM_BEARER_APN_TYPE_NONE) { mm_dbus_method_invocation_return_error_literal (ctx->invocation, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS, "Missing index field ('apn-type') in profile settings"); handle_delete_context_free (ctx); return; } } else g_assert_not_reached (); if (!profile_manager_fail_if_connected_bearer (MM_IFACE_MODEM_3GPP_PROFILE_MANAGER (self), index_field, profile_id, apn_type, &error)) { mm_dbus_method_invocation_take_error (ctx->invocation, error); handle_delete_context_free (ctx); return; } mm_obj_info (self, "processing user request to delete 3GPP profile..."); mm_log_3gpp_profile (self, MM_LOG_LEVEL_INFO, " ", profile); /* Start ignoring our own indications */ mm_iface_modem_3gpp_profile_manager_update_ignore_start (MM_IFACE_MODEM_3GPP_PROFILE_MANAGER (self)); MM_IFACE_MODEM_3GPP_PROFILE_MANAGER_GET_INTERFACE (self)->delete_profile ( MM_IFACE_MODEM_3GPP_PROFILE_MANAGER (self), profile, index_field, (GAsyncReadyCallback)delete_profile_ready, ctx); } static gboolean handle_delete (MmGdbusModem3gppProfileManager *skeleton, GDBusMethodInvocation *invocation, GVariant *dictionary, MMIfaceModem3gppProfileManager *self) { HandleDeleteContext *ctx; ctx = g_slice_new0 (HandleDeleteContext); ctx->skeleton = g_object_ref (skeleton); ctx->invocation = g_object_ref (invocation); ctx->self = g_object_ref (self); ctx->dictionary = g_variant_ref (dictionary); mm_base_modem_authorize (MM_BASE_MODEM (self), invocation, MM_AUTHORIZATION_DEVICE_CONTROL, (GAsyncReadyCallback)handle_delete_auth_ready, ctx); return TRUE; } /*****************************************************************************/ typedef struct _DisablingContext DisablingContext; static void interface_disabling_step (GTask *task); typedef enum { DISABLING_STEP_FIRST, DISABLING_STEP_DISABLE_UNSOLICITED_EVENTS, DISABLING_STEP_CLEANUP_UNSOLICITED_EVENTS, DISABLING_STEP_LAST } DisablingStep; struct _DisablingContext { DisablingStep step; MmGdbusModem3gppProfileManager *skeleton; }; static void disabling_context_free (DisablingContext *ctx) { g_clear_object (&ctx->skeleton); g_slice_free (DisablingContext, ctx); } gboolean mm_iface_modem_3gpp_profile_manager_disable_finish (MMIfaceModem3gppProfileManager *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void disable_unsolicited_events_ready (MMIfaceModem3gppProfileManager *self, GAsyncResult *res, GTask *task) { DisablingContext *ctx; g_autoptr(GError) error = NULL; MM_IFACE_MODEM_3GPP_PROFILE_MANAGER_GET_INTERFACE (self)->disable_unsolicited_events_finish (self, res, &error); if (error) { /* This error shouldn't be treated as critical */ mm_obj_dbg (self, "couldn't disable unsolicited profile management events: %s", error->message); } /* Go on to next step */ ctx = g_task_get_task_data (task); ctx->step++; interface_disabling_step (task); } static void cleanup_unsolicited_events_ready (MMIfaceModem3gppProfileManager *self, GAsyncResult *res, GTask *task) { DisablingContext *ctx; g_autoptr(GError) error = NULL; MM_IFACE_MODEM_3GPP_PROFILE_MANAGER_GET_INTERFACE (self)->cleanup_unsolicited_events_finish (self, res, &error); if (error) { /* This error shouldn't be treated as critical */ mm_obj_dbg (self, "couldn't cleanup unsolicited profile management events: %s", error->message); } /* Go on to next step */ ctx = g_task_get_task_data (task); ctx->step++; interface_disabling_step (task); } static void interface_disabling_step (GTask *task) { MMIfaceModem3gppProfileManager *self; DisablingContext *ctx; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); switch (ctx->step) { case DISABLING_STEP_FIRST: ctx->step++; /* fall through */ case DISABLING_STEP_DISABLE_UNSOLICITED_EVENTS: if (MM_IFACE_MODEM_3GPP_PROFILE_MANAGER_GET_INTERFACE (self)->disable_unsolicited_events && MM_IFACE_MODEM_3GPP_PROFILE_MANAGER_GET_INTERFACE (self)->disable_unsolicited_events_finish) { MM_IFACE_MODEM_3GPP_PROFILE_MANAGER_GET_INTERFACE (self)->disable_unsolicited_events ( self, (GAsyncReadyCallback)disable_unsolicited_events_ready, task); return; } ctx->step++; /* fall through */ case DISABLING_STEP_CLEANUP_UNSOLICITED_EVENTS: if (MM_IFACE_MODEM_3GPP_PROFILE_MANAGER_GET_INTERFACE (self)->cleanup_unsolicited_events && MM_IFACE_MODEM_3GPP_PROFILE_MANAGER_GET_INTERFACE (self)->cleanup_unsolicited_events_finish) { MM_IFACE_MODEM_3GPP_PROFILE_MANAGER_GET_INTERFACE (self)->cleanup_unsolicited_events ( self, (GAsyncReadyCallback)cleanup_unsolicited_events_ready, task); return; } ctx->step++; /* fall through */ case DISABLING_STEP_LAST: /* We are done without errors! */ g_task_return_boolean (task, TRUE); g_object_unref (task); return; default: break; } g_assert_not_reached (); } void mm_iface_modem_3gpp_profile_manager_disable (MMIfaceModem3gppProfileManager *self, GAsyncReadyCallback callback, gpointer user_data) { DisablingContext *ctx; GTask *task; ctx = g_slice_new0 (DisablingContext); ctx->step = DISABLING_STEP_FIRST; task = g_task_new (self, NULL, callback, user_data); g_task_set_task_data (task, ctx, (GDestroyNotify)disabling_context_free); g_object_get (self, MM_IFACE_MODEM_3GPP_PROFILE_MANAGER_DBUS_SKELETON, &ctx->skeleton, NULL); if (!ctx->skeleton) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't get interface skeleton"); g_object_unref (task); return; } interface_disabling_step (task); } /*****************************************************************************/ typedef struct _EnablingContext EnablingContext; static void interface_enabling_step (GTask *task); typedef enum { ENABLING_STEP_FIRST, ENABLING_STEP_SETUP_UNSOLICITED_EVENTS, ENABLING_STEP_ENABLE_UNSOLICITED_EVENTS, ENABLING_STEP_LAST } EnablingStep; struct _EnablingContext { EnablingStep step; MmGdbusModem3gppProfileManager *skeleton; }; static void enabling_context_free (EnablingContext *ctx) { g_clear_object (&ctx->skeleton); g_slice_free (EnablingContext, ctx); } gboolean mm_iface_modem_3gpp_profile_manager_enable_finish (MMIfaceModem3gppProfileManager *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void setup_unsolicited_events_ready (MMIfaceModem3gppProfileManager *self, GAsyncResult *res, GTask *task) { EnablingContext *ctx; g_autoptr(GError) error = NULL; MM_IFACE_MODEM_3GPP_PROFILE_MANAGER_GET_INTERFACE (self)->setup_unsolicited_events_finish (self, res, &error); if (error) { /* This error shouldn't be treated as critical */ mm_obj_dbg (self, "couldn't setup unsolicited profile management events: %s", error->message); } /* Go on to next step */ ctx = g_task_get_task_data (task); ctx->step++; interface_enabling_step (task); } static void enable_unsolicited_events_ready (MMIfaceModem3gppProfileManager *self, GAsyncResult *res, GTask *task) { EnablingContext *ctx; g_autoptr(GError) error = NULL; MM_IFACE_MODEM_3GPP_PROFILE_MANAGER_GET_INTERFACE (self)->enable_unsolicited_events_finish (self, res, &error); if (error) { /* This error shouldn't be treated as critical */ mm_obj_dbg (self, "couldn't enable unsolicited profile management events: %s", error->message); } /* Go on to next step */ ctx = g_task_get_task_data (task); ctx->step++; interface_enabling_step (task); } static void interface_enabling_step (GTask *task) { MMIfaceModem3gppProfileManager *self; EnablingContext *ctx; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); switch (ctx->step) { case ENABLING_STEP_FIRST: ctx->step++; /* fall through */ case ENABLING_STEP_SETUP_UNSOLICITED_EVENTS: if (MM_IFACE_MODEM_3GPP_PROFILE_MANAGER_GET_INTERFACE (self)->setup_unsolicited_events && MM_IFACE_MODEM_3GPP_PROFILE_MANAGER_GET_INTERFACE (self)->setup_unsolicited_events_finish) { MM_IFACE_MODEM_3GPP_PROFILE_MANAGER_GET_INTERFACE (self)->setup_unsolicited_events ( self, (GAsyncReadyCallback)setup_unsolicited_events_ready, task); return; } ctx->step++; /* fall through */ case ENABLING_STEP_ENABLE_UNSOLICITED_EVENTS: if (MM_IFACE_MODEM_3GPP_PROFILE_MANAGER_GET_INTERFACE (self)->enable_unsolicited_events && MM_IFACE_MODEM_3GPP_PROFILE_MANAGER_GET_INTERFACE (self)->enable_unsolicited_events_finish) { MM_IFACE_MODEM_3GPP_PROFILE_MANAGER_GET_INTERFACE (self)->enable_unsolicited_events ( self, (GAsyncReadyCallback)enable_unsolicited_events_ready, task); return; } ctx->step++; /* fall through */ case ENABLING_STEP_LAST: /* We are done without errors! */ g_task_return_boolean (task, TRUE); g_object_unref (task); return; default: break; } g_assert_not_reached (); } void mm_iface_modem_3gpp_profile_manager_enable (MMIfaceModem3gppProfileManager *self, GAsyncReadyCallback callback, gpointer user_data) { EnablingContext *ctx; GTask *task; ctx = g_slice_new0 (EnablingContext); ctx->step = ENABLING_STEP_FIRST; task = g_task_new (self, NULL, callback, user_data); g_task_set_task_data (task, ctx, (GDestroyNotify)enabling_context_free); g_object_get (self, MM_IFACE_MODEM_3GPP_PROFILE_MANAGER_DBUS_SKELETON, &ctx->skeleton, NULL); if (!ctx->skeleton) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't get interface skeleton"); g_object_unref (task); return; } interface_enabling_step (task); } /*****************************************************************************/ typedef struct _InitializationContext InitializationContext; static void interface_initialization_step (GTask *task); typedef enum { INITIALIZATION_STEP_FIRST, INITIALIZATION_STEP_CHECK_SUPPORT, INITIALIZATION_STEP_FAIL_IF_UNSUPPORTED, INITIALIZATION_STEP_LAST } InitializationStep; struct _InitializationContext { MmGdbusModem3gppProfileManager *skeleton; InitializationStep step; }; static void initialization_context_free (InitializationContext *ctx) { g_clear_object (&ctx->skeleton); g_slice_free (InitializationContext, ctx); } gboolean mm_iface_modem_3gpp_profile_manager_initialize_finish (MMIfaceModem3gppProfileManager *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void check_support_ready (MMIfaceModem3gppProfileManager *self, GAsyncResult *res, GTask *task) { InitializationContext *ctx; g_autoptr(GError) error = NULL; g_autofree gchar *index_field = NULL; ctx = g_task_get_task_data (task); if (!MM_IFACE_MODEM_3GPP_PROFILE_MANAGER_GET_INTERFACE (self)->check_support_finish (self, res, &index_field, &error)) { if (error) { /* This error shouldn't be treated as critical */ mm_obj_dbg (self, "profile management support check failed: %s", error->message); } } else { /* profile management is supported! */ mm_gdbus_modem3gpp_profile_manager_set_index_field (ctx->skeleton, index_field); g_object_set_qdata (G_OBJECT (self), supported_quark, GUINT_TO_POINTER (TRUE)); } /* Go on to next step */ ctx->step++; interface_initialization_step (task); } static void profile_manager_list_profiles_check_ready (MMIfaceModem3gppProfileManager *self, GAsyncResult *res, GTask *task) { InitializationContext *ctx; g_autoptr(GError) error = NULL; ctx = g_task_get_task_data (task); if (!mm_iface_modem_3gpp_profile_manager_list_profiles_finish (self, res, NULL, &error)) mm_obj_dbg (self, "profile management support check failed: couldn't load profile list: %s", error->message); else { /* profile management is supported! * We are here because the modem type did not define the check_support functions, * but we need anyway to set index_field, so let's use "profile-id" as default */ mm_gdbus_modem3gpp_profile_manager_set_index_field (ctx->skeleton, "profile-id"); g_object_set_qdata (G_OBJECT (self), supported_quark, GUINT_TO_POINTER (TRUE)); } ctx->step++; interface_initialization_step (task); } static void interface_initialization_step (GTask *task) { MMIfaceModem3gppProfileManager *self; InitializationContext *ctx; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); switch (ctx->step) { case INITIALIZATION_STEP_FIRST: /* Setup quarks if we didn't do it before */ if (G_UNLIKELY (!support_checked_quark)) support_checked_quark = (g_quark_from_static_string (SUPPORT_CHECKED_TAG)); if (G_UNLIKELY (!supported_quark)) supported_quark = (g_quark_from_static_string (SUPPORTED_TAG)); ctx->step++; /* fall through */ case INITIALIZATION_STEP_CHECK_SUPPORT: if (!GPOINTER_TO_UINT (g_object_get_qdata (G_OBJECT (self), support_checked_quark))) { /* Set the checked flag so that we don't run it again */ g_object_set_qdata (G_OBJECT (self), support_checked_quark, GUINT_TO_POINTER (TRUE)); /* Initially, assume we don't support it */ g_object_set_qdata (G_OBJECT (self), supported_quark, GUINT_TO_POINTER (FALSE)); if (MM_IFACE_MODEM_3GPP_PROFILE_MANAGER_GET_INTERFACE (self)->check_support && MM_IFACE_MODEM_3GPP_PROFILE_MANAGER_GET_INTERFACE (self)->check_support_finish) { MM_IFACE_MODEM_3GPP_PROFILE_MANAGER_GET_INTERFACE (self)->check_support ( self, (GAsyncReadyCallback)check_support_ready, task); return; } /* If there is no implementation to check support, try to query the list * explicitly; it may be the case that there is no other way to check for * support. */ mm_iface_modem_3gpp_profile_manager_list_profiles ( self, (GAsyncReadyCallback)profile_manager_list_profiles_check_ready, task); return; } ctx->step++; /* fall through */ case INITIALIZATION_STEP_FAIL_IF_UNSUPPORTED: if (!GPOINTER_TO_UINT (g_object_get_qdata (G_OBJECT (self), supported_quark))) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "Profile management not supported"); g_object_unref (task); return; } ctx->step++; /* fall through */ case INITIALIZATION_STEP_LAST: /* We are done without errors! */ /* Handle method invocations */ g_object_connect (ctx->skeleton, "signal::handle-list", G_CALLBACK (handle_list), self, "signal::handle-set", G_CALLBACK (handle_set), self, "signal::handle-delete", G_CALLBACK (handle_delete), self, NULL); /* Finally, export the new interface */ mm_gdbus_object_skeleton_set_modem3gpp_profile_manager (MM_GDBUS_OBJECT_SKELETON (self), MM_GDBUS_MODEM3GPP_PROFILE_MANAGER (ctx->skeleton)); g_task_return_boolean (task, TRUE); g_object_unref (task); return; default: break; } g_assert_not_reached (); } void mm_iface_modem_3gpp_profile_manager_initialize (MMIfaceModem3gppProfileManager *self, GAsyncReadyCallback callback, gpointer user_data) { InitializationContext *ctx; MmGdbusModem3gppProfileManager *skeleton = NULL; GTask *task; /* Did we already create it? */ g_object_get (self, MM_IFACE_MODEM_3GPP_PROFILE_MANAGER_DBUS_SKELETON, &skeleton, NULL); if (!skeleton) { skeleton = mm_gdbus_modem3gpp_profile_manager_skeleton_new (); g_object_set (self, MM_IFACE_MODEM_3GPP_PROFILE_MANAGER_DBUS_SKELETON, skeleton, NULL); } /* Perform async initialization here */ ctx = g_slice_new0 (InitializationContext); ctx->step = INITIALIZATION_STEP_FIRST; ctx->skeleton = skeleton; task = g_task_new (self, NULL, callback, user_data); g_task_set_task_data (task, ctx, (GDestroyNotify)initialization_context_free); interface_initialization_step (task); } void mm_iface_modem_3gpp_profile_manager_shutdown (MMIfaceModem3gppProfileManager *self) { /* Unexport DBus interface and remove the skeleton */ mm_gdbus_object_skeleton_set_modem3gpp_profile_manager (MM_GDBUS_OBJECT_SKELETON (self), NULL); g_object_set (self, MM_IFACE_MODEM_3GPP_PROFILE_MANAGER_DBUS_SKELETON, NULL, NULL); } /*****************************************************************************/ static void iface_modem_3gpp_profile_manager_init (gpointer g_iface) { static gboolean initialized = FALSE; if (initialized) return; /* Properties */ g_object_interface_install_property (g_iface, g_param_spec_object (MM_IFACE_MODEM_3GPP_PROFILE_MANAGER_DBUS_SKELETON, "3GPP Profile Manager DBus skeleton", "DBus skeleton for the 3GPP Profile Manager interface", MM_GDBUS_TYPE_MODEM3GPP_PROFILE_MANAGER_SKELETON, G_PARAM_READWRITE)); initialized = TRUE; } GType mm_iface_modem_3gpp_profile_manager_get_type (void) { static GType iface_modem_3gpp_profile_manager_type = 0; if (!G_UNLIKELY (iface_modem_3gpp_profile_manager_type)) { static const GTypeInfo info = { sizeof (MMIfaceModem3gppProfileManager), /* class_size */ iface_modem_3gpp_profile_manager_init, /* base_init */ NULL, /* base_finalize */ }; iface_modem_3gpp_profile_manager_type = g_type_register_static (G_TYPE_INTERFACE, "MMIfaceModem3gppProfileManager", &info, 0); g_type_interface_add_prerequisite (iface_modem_3gpp_profile_manager_type, MM_TYPE_IFACE_MODEM_3GPP); } return iface_modem_3gpp_profile_manager_type; } ModemManager-1.23.4-dev/src/mm-iface-modem-3gpp-profile-manager.h000066400000000000000000000423011456466623000243570ustar00rootroot00000000000000/* -*- 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) 2021 Aleksander Morgado * Copyright (C) 2021 Google, Inc. */ #ifndef MM_IFACE_MODEM_3GPP_PROFILE_MANAGER_H #define MM_IFACE_MODEM_3GPP_PROFILE_MANAGER_H #include #include #define _LIBMM_INSIDE_MM #include #define MM_TYPE_IFACE_MODEM_3GPP_PROFILE_MANAGER (mm_iface_modem_3gpp_profile_manager_get_type ()) #define MM_IFACE_MODEM_3GPP_PROFILE_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_IFACE_MODEM_3GPP_PROFILE_MANAGER, MMIfaceModem3gppProfileManager)) #define MM_IS_IFACE_MODEM_3GPP_PROFILE_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_IFACE_MODEM_3GPP_PROFILE_MANAGER)) #define MM_IFACE_MODEM_3GPP_PROFILE_MANAGER_GET_INTERFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE ((obj), MM_TYPE_IFACE_MODEM_3GPP_PROFILE_MANAGER, MMIfaceModem3gppProfileManager)) #define MM_IFACE_MODEM_3GPP_PROFILE_MANAGER_DBUS_SKELETON "iface-modem-3gpp-profile-manager-dbus-skeleton" typedef struct _MMIfaceModem3gppProfileManager MMIfaceModem3gppProfileManager; struct _MMIfaceModem3gppProfileManager { GTypeInterface g_iface; /* Check for profile management support (async) */ void (* check_support) (MMIfaceModem3gppProfileManager *self, GAsyncReadyCallback callback, gpointer user_data); gboolean (* check_support_finish) (MMIfaceModem3gppProfileManager *self, GAsyncResult *res, gchar **index_field, GError **error); /* Asynchronous setup of unsolicited events */ void (* setup_unsolicited_events) (MMIfaceModem3gppProfileManager *self, GAsyncReadyCallback callback, gpointer user_data); gboolean (* setup_unsolicited_events_finish) (MMIfaceModem3gppProfileManager *self, GAsyncResult *res, GError **error); /* Asynchronous enabling of unsolicited events */ void (* enable_unsolicited_events) (MMIfaceModem3gppProfileManager *self, GAsyncReadyCallback callback, gpointer user_data); gboolean (* enable_unsolicited_events_finish) (MMIfaceModem3gppProfileManager *self, GAsyncResult *res, GError **error); /* Asynchronous disabling of unsolicited events */ void (* disable_unsolicited_events) (MMIfaceModem3gppProfileManager *self, GAsyncReadyCallback callback, gpointer user_data); gboolean (* disable_unsolicited_events_finish) (MMIfaceModem3gppProfileManager *self, GAsyncResult *res, GError **error); /* Asynchronous cleaning up of unsolicited events */ void (* cleanup_unsolicited_events) (MMIfaceModem3gppProfileManager *self, GAsyncReadyCallback callback, gpointer user_data); gboolean (* cleanup_unsolicited_events_finish) (MMIfaceModem3gppProfileManager *self, GAsyncResult *res, GError **error); /* Get a single profile. * This is completely optional, and should be used by implementations that are able * to query one single profile settings. For all other implementations, it should be * set to NULL so that the generic implementation is used (listing all and lookup by * profile id) */ void (* get_profile) (MMIfaceModem3gppProfileManager *self, gint profile_id, GAsyncReadyCallback callback, gpointer user_data); MM3gppProfile * (* get_profile_finish) (MMIfaceModem3gppProfileManager *self, GAsyncResult *res, GError **error); /* List */ void (* list_profiles) (MMIfaceModem3gppProfileManager *self, GAsyncReadyCallback callback, gpointer user_data); gboolean (* list_profiles_finish) (MMIfaceModem3gppProfileManager *self, GAsyncResult *res, GList **profiles, GError **error); /* Delete */ void (* delete_profile) (MMIfaceModem3gppProfileManager *self, MM3gppProfile *requested, const gchar *index_field, GAsyncReadyCallback callback, gpointer user_data); gboolean (* delete_profile_finish) (MMIfaceModem3gppProfileManager *self, GAsyncResult *res, GError **error); /* * Check profile format (substep of 'set profiles') * * Before a profile is set, we would like to check the format of the set * operation, if possible. * * Expected outputs: * - new_id: whether new profiles can be created with a specific known id. * - min_profile_id: minimum supported profile id. * - max_profile_id: maximum supported profile id. * - apn_cmp: method to use when comparing APN strings. * - profile_cmp_flags: flags to use when comparing profile objects. * * The check is done per IP family, as the ranges may be different for each. */ void (* check_format) (MMIfaceModem3gppProfileManager *self, MMBearerIpFamily family, GAsyncReadyCallback callback, gpointer user_data); gboolean (* check_format_finish) (MMIfaceModem3gppProfileManager *self, GAsyncResult *res, gboolean *new_id, gint *min_profile_id, gint *max_profile_id, GEqualFunc *apn_cmp, MM3gppProfileCmpFlags *profile_cmp_flags, GError **error); /* Check activated profile (substep of 'set profiles') * * Before a profile is set, we may attempt to deactivate it first, but only * if there is no known bearer using it already and only if this check for * activation really reports the profile being already activated. * * The given profile MUST have profile-id set, so the set_profile() * implementation should only use it once the profile-id is known, never * before. * * This step is optional (method pointers can be initialized to NULL), so * that the deactivate profile step is done always. */ void (* check_activated_profile) (MMIfaceModem3gppProfileManager *self, MM3gppProfile *requested, GAsyncReadyCallback callback, gpointer user_data); gboolean (* check_activated_profile_finish) (MMIfaceModem3gppProfileManager *self, GAsyncResult *res, gboolean *out_activated, GError **error); /* Deactivate profile (substep of 'set profiles') * * Before a profile is set, we may attempt to deactivate it first, but only * if there is no known bearer using it already. * * The given profile MUST have profile-id set, so the set_profile() * implementation should only use it once the profile-id is known, never * before. */ void (* deactivate_profile) (MMIfaceModem3gppProfileManager *self, MM3gppProfile *requested, GAsyncReadyCallback callback, gpointer user_data); gboolean (* deactivate_profile_finish) (MMIfaceModem3gppProfileManager *self, GAsyncResult *res, GError **error); /* Store profile (substep of 'set profiles') */ void (* store_profile) (MMIfaceModem3gppProfileManager *self, MM3gppProfile *requested, const gchar *index_field, GAsyncReadyCallback callback, gpointer user_data); gboolean (* store_profile_finish) (MMIfaceModem3gppProfileManager *self, GAsyncResult *res, gint *out_profile_id, MMBearerApnType *out_apn_type, GError **error); }; GType mm_iface_modem_3gpp_profile_manager_get_type (void); G_DEFINE_AUTOPTR_CLEANUP_FUNC (MMIfaceModem3gppProfileManager, g_object_unref) /* Initialize profile manager interface (async) */ void mm_iface_modem_3gpp_profile_manager_initialize (MMIfaceModem3gppProfileManager *self, GAsyncReadyCallback callback, gpointer user_data); gboolean mm_iface_modem_3gpp_profile_manager_initialize_finish (MMIfaceModem3gppProfileManager *self, GAsyncResult *res, GError **error); /* Enable profile manager interface (async) */ void mm_iface_modem_3gpp_profile_manager_enable (MMIfaceModem3gppProfileManager *self, GAsyncReadyCallback callback, gpointer user_data); gboolean mm_iface_modem_3gpp_profile_manager_enable_finish (MMIfaceModem3gppProfileManager *self, GAsyncResult *res, GError **error); /* Disable profile manager interface (async) */ void mm_iface_modem_3gpp_profile_manager_disable (MMIfaceModem3gppProfileManager *self, GAsyncReadyCallback callback, gpointer user_data); gboolean mm_iface_modem_3gpp_profile_manager_disable_finish (MMIfaceModem3gppProfileManager *self, GAsyncResult *res, GError **error); /* Shutdown profile manager interface */ void mm_iface_modem_3gpp_profile_manager_shutdown (MMIfaceModem3gppProfileManager *self); /* Bind properties for simple GetStatus() */ void mm_iface_modem_3gpp_profile_manager_bind_simple_status (MMIfaceModem3gppProfileManager *self, MMSimpleStatus *status); /* Helpers to request ignorig Updated signal emission requests sent by implementations during * our own operations */ void mm_iface_modem_3gpp_profile_manager_update_ignore_start (MMIfaceModem3gppProfileManager *self); void mm_iface_modem_3gpp_profile_manager_update_ignore_stop (MMIfaceModem3gppProfileManager *self); void mm_iface_modem_3gpp_profile_manager_update_ignore_stop_delayed (MMIfaceModem3gppProfileManager *self); /* Helper to emit the Updated signal by implementations */ void mm_iface_modem_3gpp_profile_manager_updated (MMIfaceModem3gppProfileManager *self); /* Internal list profile management */ void mm_iface_modem_3gpp_profile_manager_get_profile (MMIfaceModem3gppProfileManager *self, gint profile_id, GAsyncReadyCallback callback, gpointer user_data); MM3gppProfile *mm_iface_modem_3gpp_profile_manager_get_profile_finish (MMIfaceModem3gppProfileManager *self, GAsyncResult *res, GError **error); void mm_iface_modem_3gpp_profile_manager_list_profiles (MMIfaceModem3gppProfileManager *self, GAsyncReadyCallback callback, gpointer user_data); gboolean mm_iface_modem_3gpp_profile_manager_list_profiles_finish (MMIfaceModem3gppProfileManager *self, GAsyncResult *res, GList **profiles, GError **error); void mm_iface_modem_3gpp_profile_manager_set_profile (MMIfaceModem3gppProfileManager *self, MM3gppProfile *requested, const gchar *index_field, gboolean strict, GAsyncReadyCallback callback, gpointer user_data); MM3gppProfile *mm_iface_modem_3gpp_profile_manager_set_profile_finish (MMIfaceModem3gppProfileManager *self, GAsyncResult *res, GError **error); #endif /* MM_IFACE_MODEM_3GPP_PROFILE_MANAGER_H */ ModemManager-1.23.4-dev/src/mm-iface-modem-3gpp-ussd.c000066400000000000000000000747561456466623000223030ustar00rootroot00000000000000/* -*- 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 Guido Guenther * Copyright (C) 2012 Google, Inc. */ #include #include #include #define _LIBMM_INSIDE_MM #include #include "mm-iface-modem.h" #include "mm-iface-modem-3gpp.h" #include "mm-iface-modem-3gpp-ussd.h" #include "mm-base-modem.h" #include "mm-modem-helpers.h" #include "mm-error-helpers.h" #include "mm-log-object.h" #define SUPPORT_CHECKED_TAG "3gpp-ussd-support-checked-tag" #define SUPPORTED_TAG "3gpp-ussd-supported-tag" static GQuark support_checked_quark; static GQuark supported_quark; /*****************************************************************************/ void mm_iface_modem_3gpp_ussd_bind_simple_status (MMIfaceModem3gppUssd *self, MMSimpleStatus *status) { /* Nothing shown in simple status */ } /*****************************************************************************/ typedef struct { MmGdbusModem3gppUssd *skeleton; GDBusMethodInvocation *invocation; MMIfaceModem3gppUssd *self; } HandleCancelContext; static void handle_cancel_context_free (HandleCancelContext *ctx) { g_object_unref (ctx->skeleton); g_object_unref (ctx->invocation); g_object_unref (ctx->self); g_free (ctx); } static void handle_cancel_ready (MMIfaceModem3gppUssd *self, GAsyncResult *res, HandleCancelContext *ctx) { GError *error = NULL; if (!MM_IFACE_MODEM_3GPP_USSD_GET_INTERFACE (self)->cancel_finish (self, res, &error)) mm_dbus_method_invocation_take_error (ctx->invocation, error); else mm_gdbus_modem3gpp_ussd_complete_cancel (ctx->skeleton, ctx->invocation); handle_cancel_context_free (ctx); } static void handle_cancel_auth_ready (MMBaseModem *self, GAsyncResult *res, HandleCancelContext *ctx) { GError *error = NULL; if (!mm_base_modem_authorize_finish (self, res, &error)) { mm_dbus_method_invocation_take_error (ctx->invocation, error); handle_cancel_context_free (ctx); return; } if (mm_iface_modem_abort_invocation_if_state_not_reached (MM_IFACE_MODEM (self), ctx->invocation, MM_MODEM_STATE_ENABLED)) { handle_cancel_context_free (ctx); return; } g_assert (MM_IFACE_MODEM_3GPP_USSD_GET_INTERFACE (self)->cancel != NULL); g_assert (MM_IFACE_MODEM_3GPP_USSD_GET_INTERFACE (self)->cancel_finish != NULL); MM_IFACE_MODEM_3GPP_USSD_GET_INTERFACE (self)->cancel ( MM_IFACE_MODEM_3GPP_USSD (self), (GAsyncReadyCallback)handle_cancel_ready, ctx); } static gboolean handle_cancel (MmGdbusModem3gppUssd *skeleton, GDBusMethodInvocation *invocation, MMIfaceModem3gppUssd *self) { HandleCancelContext *ctx; ctx = g_new (HandleCancelContext, 1); ctx->skeleton = g_object_ref (skeleton); ctx->invocation = g_object_ref (invocation); ctx->self = g_object_ref (self); mm_base_modem_authorize (MM_BASE_MODEM (self), invocation, MM_AUTHORIZATION_USSD, (GAsyncReadyCallback)handle_cancel_auth_ready, ctx); return TRUE; } /*****************************************************************************/ typedef struct { MmGdbusModem3gppUssd *skeleton; GDBusMethodInvocation *invocation; MMIfaceModem3gppUssd *self; gchar *command; } HandleRespondContext; static void handle_respond_context_free (HandleRespondContext *ctx) { g_object_unref (ctx->skeleton); g_object_unref (ctx->invocation); g_object_unref (ctx->self); g_free (ctx->command); g_free (ctx); } static void handle_respond_ready (MMIfaceModem3gppUssd *self, GAsyncResult *res, HandleRespondContext *ctx) { GError *error = NULL; gchar *reply; reply = MM_IFACE_MODEM_3GPP_USSD_GET_INTERFACE (self)->send_finish (self, res, &error); if (!reply) mm_dbus_method_invocation_take_error (ctx->invocation, error); else { mm_gdbus_modem3gpp_ussd_complete_respond (ctx->skeleton, ctx->invocation, reply); g_free (reply); } handle_respond_context_free (ctx); } static void handle_respond_auth_ready (MMBaseModem *self, GAsyncResult *res, HandleRespondContext *ctx) { GError *error = NULL; if (!mm_base_modem_authorize_finish (self, res, &error)) { mm_dbus_method_invocation_take_error (ctx->invocation, error); handle_respond_context_free (ctx); return; } if (mm_iface_modem_abort_invocation_if_state_not_reached (MM_IFACE_MODEM (self), ctx->invocation, MM_MODEM_STATE_ENABLED)) { handle_respond_context_free (ctx); return; } g_assert (MM_IFACE_MODEM_3GPP_USSD_GET_INTERFACE (self)->send != NULL); g_assert (MM_IFACE_MODEM_3GPP_USSD_GET_INTERFACE (self)->send_finish != NULL); switch (mm_gdbus_modem3gpp_ussd_get_state (ctx->skeleton)) { case MM_MODEM_3GPP_USSD_SESSION_STATE_ACTIVE: case MM_MODEM_3GPP_USSD_SESSION_STATE_IDLE: mm_dbus_method_invocation_return_error_literal (ctx->invocation, MM_CORE_ERROR, MM_CORE_ERROR_WRONG_STATE, "Cannot respond USSD: no active session"); break; case MM_MODEM_3GPP_USSD_SESSION_STATE_USER_RESPONSE: MM_IFACE_MODEM_3GPP_USSD_GET_INTERFACE (self)->send ( MM_IFACE_MODEM_3GPP_USSD (self), ctx->command, (GAsyncReadyCallback)handle_respond_ready, ctx); return; case MM_MODEM_3GPP_USSD_SESSION_STATE_UNKNOWN: default: /* We should never have a DBus request when in UNKNOWN state */ g_assert_not_reached (); break; } handle_respond_context_free (ctx); } static gboolean handle_respond (MmGdbusModem3gppUssd *skeleton, GDBusMethodInvocation *invocation, const gchar *command, MMIfaceModem3gppUssd *self) { HandleRespondContext *ctx; ctx = g_new (HandleRespondContext, 1); ctx->skeleton = g_object_ref (skeleton); ctx->invocation = g_object_ref (invocation); ctx->self = g_object_ref (self); ctx->command = g_strdup (command); mm_base_modem_authorize (MM_BASE_MODEM (self), invocation, MM_AUTHORIZATION_USSD, (GAsyncReadyCallback)handle_respond_auth_ready, ctx); return TRUE; } /*****************************************************************************/ typedef struct { MmGdbusModem3gppUssd *skeleton; GDBusMethodInvocation *invocation; MMIfaceModem3gppUssd *self; gchar *command; } HandleInitiateContext; static void handle_initiate_context_free (HandleInitiateContext *ctx) { g_object_unref (ctx->skeleton); g_object_unref (ctx->invocation); g_object_unref (ctx->self); g_free (ctx->command); g_free (ctx); } static void handle_initiate_ready (MMIfaceModem3gppUssd *self, GAsyncResult *res, HandleInitiateContext *ctx) { GError *error = NULL; gchar *reply; reply = MM_IFACE_MODEM_3GPP_USSD_GET_INTERFACE (self)->send_finish (self, res, &error); if (!reply) mm_dbus_method_invocation_take_error (ctx->invocation, error); else { mm_gdbus_modem3gpp_ussd_complete_initiate (ctx->skeleton, ctx->invocation, reply); g_free (reply); } handle_initiate_context_free (ctx); } static void handle_initiate_auth_ready (MMBaseModem *self, GAsyncResult *res, HandleInitiateContext *ctx) { GError *error = NULL; if (!mm_base_modem_authorize_finish (self, res, &error)) { mm_dbus_method_invocation_take_error (ctx->invocation, error); handle_initiate_context_free (ctx); return; } if (mm_iface_modem_abort_invocation_if_state_not_reached (MM_IFACE_MODEM (self), ctx->invocation, MM_MODEM_STATE_ENABLED)) { handle_initiate_context_free (ctx); return; } g_assert (MM_IFACE_MODEM_3GPP_USSD_GET_INTERFACE (self)->send != NULL); g_assert (MM_IFACE_MODEM_3GPP_USSD_GET_INTERFACE (self)->send_finish != NULL); switch (mm_gdbus_modem3gpp_ussd_get_state (ctx->skeleton)) { case MM_MODEM_3GPP_USSD_SESSION_STATE_ACTIVE: case MM_MODEM_3GPP_USSD_SESSION_STATE_USER_RESPONSE: mm_dbus_method_invocation_return_error_literal (ctx->invocation, MM_CORE_ERROR, MM_CORE_ERROR_WRONG_STATE, "Cannot initiate USSD: a session is already active"); break; case MM_MODEM_3GPP_USSD_SESSION_STATE_IDLE: MM_IFACE_MODEM_3GPP_USSD_GET_INTERFACE (self)->send ( MM_IFACE_MODEM_3GPP_USSD (self), ctx->command, (GAsyncReadyCallback)handle_initiate_ready, ctx); return; case MM_MODEM_3GPP_USSD_SESSION_STATE_UNKNOWN: default: /* We should never have a DBus request when in UNKNOWN state */ g_assert_not_reached (); break; } handle_initiate_context_free (ctx); } static gboolean handle_initiate (MmGdbusModem3gppUssd *skeleton, GDBusMethodInvocation *invocation, const gchar *command, MMIfaceModem3gppUssd *self) { HandleInitiateContext *ctx; ctx = g_new (HandleInitiateContext, 1); ctx->skeleton = g_object_ref (skeleton); ctx->invocation = g_object_ref (invocation); ctx->self = g_object_ref (self); ctx->command = g_strdup (command); mm_base_modem_authorize (MM_BASE_MODEM (self), invocation, MM_AUTHORIZATION_USSD, (GAsyncReadyCallback)handle_initiate_auth_ready, ctx); return TRUE; } /*****************************************************************************/ gchar * mm_iface_modem_3gpp_ussd_encode (MMIfaceModem3gppUssd *self, const gchar *command, guint *scheme, GError **error) { return MM_IFACE_MODEM_3GPP_USSD_GET_INTERFACE (self)->encode (self, command, scheme, error); } gchar * mm_iface_modem_3gpp_ussd_decode (MMIfaceModem3gppUssd *self, const gchar *reply, GError **error) { return MM_IFACE_MODEM_3GPP_USSD_GET_INTERFACE (self)->decode (self, reply, error); } /*****************************************************************************/ MMModem3gppUssdSessionState mm_iface_modem_3gpp_ussd_get_state (MMIfaceModem3gppUssd *self) { MmGdbusModem3gppUssd *skeleton = NULL; MMModem3gppUssdSessionState state; g_object_get (self, MM_IFACE_MODEM_3GPP_USSD_DBUS_SKELETON, &skeleton, NULL); if (!skeleton) return MM_MODEM_3GPP_USSD_SESSION_STATE_UNKNOWN; state = (MMModem3gppUssdSessionState) mm_gdbus_modem3gpp_ussd_get_state (skeleton); g_object_unref (skeleton); return state; } void mm_iface_modem_3gpp_ussd_update_state (MMIfaceModem3gppUssd *self, MMModem3gppUssdSessionState new_state) { MmGdbusModem3gppUssd *skeleton = NULL; MMModem3gppUssdSessionState old_state; g_object_get (self, MM_IFACE_MODEM_3GPP_USSD_DBUS_SKELETON, &skeleton, NULL); if (!skeleton) return; old_state = (MMModem3gppUssdSessionState) mm_gdbus_modem3gpp_ussd_get_state (skeleton); if (old_state != new_state) mm_gdbus_modem3gpp_ussd_set_state (skeleton, new_state); g_object_unref (skeleton); } void mm_iface_modem_3gpp_ussd_update_network_notification (MMIfaceModem3gppUssd *self, const gchar *network_notification) { MmGdbusModem3gppUssd *skeleton = NULL; g_object_get (self, MM_IFACE_MODEM_3GPP_USSD_DBUS_SKELETON, &skeleton, NULL); if (!skeleton) return; mm_gdbus_modem3gpp_ussd_set_network_notification (skeleton, network_notification); g_object_unref (skeleton); } void mm_iface_modem_3gpp_ussd_update_network_request (MMIfaceModem3gppUssd *self, const gchar *network_request) { MmGdbusModem3gppUssd *skeleton = NULL; g_object_get (self, MM_IFACE_MODEM_3GPP_USSD_DBUS_SKELETON, &skeleton, NULL); if (!skeleton) return; mm_gdbus_modem3gpp_ussd_set_network_request (skeleton, network_request); g_object_unref (skeleton); } /*****************************************************************************/ typedef struct _DisablingContext DisablingContext; static void interface_disabling_step (GTask *task); typedef enum { DISABLING_STEP_FIRST, DISABLING_STEP_DISABLE_UNSOLICITED_EVENTS, DISABLING_STEP_CLEANUP_UNSOLICITED_EVENTS, DISABLING_STEP_LAST } DisablingStep; struct _DisablingContext { DisablingStep step; MmGdbusModem3gppUssd *skeleton; }; static void disabling_context_free (DisablingContext *ctx) { if (ctx->skeleton) g_object_unref (ctx->skeleton); g_free (ctx); } gboolean mm_iface_modem_3gpp_ussd_disable_finish (MMIfaceModem3gppUssd *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void disable_unsolicited_events_ready (MMIfaceModem3gppUssd *self, GAsyncResult *res, GTask *task) { DisablingContext *ctx; GError *error = NULL; MM_IFACE_MODEM_3GPP_USSD_GET_INTERFACE (self)->disable_unsolicited_events_finish (self, res, &error); if (error) { /* This error shouldn't be treated as critical */ mm_obj_dbg (self, "couldn't disable unsolicited USSD events: %s", error->message); g_error_free (error); } /* Go on to next step */ ctx = g_task_get_task_data (task); ctx->step++; interface_disabling_step (task); } static void cleanup_unsolicited_events_ready (MMIfaceModem3gppUssd *self, GAsyncResult *res, GTask *task) { DisablingContext *ctx; GError *error = NULL; MM_IFACE_MODEM_3GPP_USSD_GET_INTERFACE (self)->cleanup_unsolicited_events_finish (self, res, &error); if (error) { /* This error shouldn't be treated as critical */ mm_obj_dbg (self, "couldn't cleanup unsolicited USSD events: %s", error->message); g_error_free (error); } /* Go on to next step */ ctx = g_task_get_task_data (task); ctx->step++; interface_disabling_step (task); } static void interface_disabling_step (GTask *task) { MMIfaceModem3gppUssd *self; DisablingContext *ctx; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); switch (ctx->step) { case DISABLING_STEP_FIRST: ctx->step++; /* fall through */ case DISABLING_STEP_DISABLE_UNSOLICITED_EVENTS: MM_IFACE_MODEM_3GPP_USSD_GET_INTERFACE (self)->disable_unsolicited_events ( self, (GAsyncReadyCallback)disable_unsolicited_events_ready, task); return; case DISABLING_STEP_CLEANUP_UNSOLICITED_EVENTS: MM_IFACE_MODEM_3GPP_USSD_GET_INTERFACE (self)->cleanup_unsolicited_events ( self, (GAsyncReadyCallback)cleanup_unsolicited_events_ready, task); return; case DISABLING_STEP_LAST: /* We are done without errors! */ mm_iface_modem_3gpp_ussd_update_state (MM_IFACE_MODEM_3GPP_USSD (self), MM_MODEM_3GPP_USSD_SESSION_STATE_UNKNOWN); g_task_return_boolean (task, TRUE); g_object_unref (task); return; default: break; } g_assert_not_reached (); } void mm_iface_modem_3gpp_ussd_disable (MMIfaceModem3gppUssd *self, GAsyncReadyCallback callback, gpointer user_data) { DisablingContext *ctx; GTask *task; ctx = g_new0 (DisablingContext, 1); ctx->step = DISABLING_STEP_FIRST; task = g_task_new (self, NULL, callback, user_data); g_task_set_task_data (task, ctx, (GDestroyNotify)disabling_context_free); g_object_get (self, MM_IFACE_MODEM_3GPP_USSD_DBUS_SKELETON, &ctx->skeleton, NULL); if (!ctx->skeleton) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't get interface skeleton"); g_object_unref (task); return; } interface_disabling_step (task); } /*****************************************************************************/ typedef struct _EnablingContext EnablingContext; static void interface_enabling_step (GTask *task); typedef enum { ENABLING_STEP_FIRST, ENABLING_STEP_SETUP_UNSOLICITED_EVENTS, ENABLING_STEP_ENABLE_UNSOLICITED_EVENTS, ENABLING_STEP_LAST } EnablingStep; struct _EnablingContext { EnablingStep step; MmGdbusModem3gppUssd *skeleton; }; static void enabling_context_free (EnablingContext *ctx) { if (ctx->skeleton) g_object_unref (ctx->skeleton); g_free (ctx); } gboolean mm_iface_modem_3gpp_ussd_enable_finish (MMIfaceModem3gppUssd *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void setup_unsolicited_events_ready (MMIfaceModem3gppUssd *self, GAsyncResult *res, GTask *task) { EnablingContext *ctx; GError *error = NULL; MM_IFACE_MODEM_3GPP_USSD_GET_INTERFACE (self)->setup_unsolicited_events_finish (self, res, &error); if (error) { /* This error shouldn't be treated as critical */ mm_obj_dbg (self, "couldn't setup unsolicited USSD events: %s", error->message); g_error_free (error); } /* Go on to next step */ ctx = g_task_get_task_data (task); ctx->step++; interface_enabling_step (task); } static void enable_unsolicited_events_ready (MMIfaceModem3gppUssd *self, GAsyncResult *res, GTask *task) { EnablingContext *ctx; GError *error = NULL; MM_IFACE_MODEM_3GPP_USSD_GET_INTERFACE (self)->enable_unsolicited_events_finish (self, res, &error); if (error) { /* This error shouldn't be treated as critical */ mm_obj_dbg (self, "couldn't enable unsolicited USSD events: %s", error->message); g_error_free (error); } /* Go on to next step */ ctx = g_task_get_task_data (task); ctx->step++; interface_enabling_step (task); } static void interface_enabling_step (GTask *task) { MMIfaceModem3gppUssd *self; EnablingContext *ctx; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); switch (ctx->step) { case ENABLING_STEP_FIRST: ctx->step++; /* fall through */ case ENABLING_STEP_SETUP_UNSOLICITED_EVENTS: MM_IFACE_MODEM_3GPP_USSD_GET_INTERFACE (self)->setup_unsolicited_events ( self, (GAsyncReadyCallback)setup_unsolicited_events_ready, task); return; case ENABLING_STEP_ENABLE_UNSOLICITED_EVENTS: MM_IFACE_MODEM_3GPP_USSD_GET_INTERFACE (self)->enable_unsolicited_events ( self, (GAsyncReadyCallback)enable_unsolicited_events_ready, task); return; case ENABLING_STEP_LAST: /* We are done without errors! */ mm_iface_modem_3gpp_ussd_update_state (MM_IFACE_MODEM_3GPP_USSD (self), MM_MODEM_3GPP_USSD_SESSION_STATE_IDLE); g_task_return_boolean (task, TRUE); g_object_unref (task); return; default: break; } g_assert_not_reached (); } void mm_iface_modem_3gpp_ussd_enable (MMIfaceModem3gppUssd *self, GAsyncReadyCallback callback, gpointer user_data) { EnablingContext *ctx; GTask *task; ctx = g_new0 (EnablingContext, 1); ctx->step = ENABLING_STEP_FIRST; task = g_task_new (self, NULL, callback, user_data); g_task_set_task_data (task, ctx, (GDestroyNotify)enabling_context_free); g_object_get (self, MM_IFACE_MODEM_3GPP_USSD_DBUS_SKELETON, &ctx->skeleton, NULL); if (!ctx->skeleton) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't get interface skeleton"); g_object_unref (task); return; } interface_enabling_step (task); } /*****************************************************************************/ typedef struct _InitializationContext InitializationContext; static void interface_initialization_step (GTask *task); typedef enum { INITIALIZATION_STEP_FIRST, INITIALIZATION_STEP_CHECK_SUPPORT, INITIALIZATION_STEP_FAIL_IF_UNSUPPORTED, INITIALIZATION_STEP_LAST } InitializationStep; struct _InitializationContext { MmGdbusModem3gppUssd *skeleton; InitializationStep step; }; static void initialization_context_free (InitializationContext *ctx) { g_object_unref (ctx->skeleton); g_free (ctx); } static void check_support_ready (MMIfaceModem3gppUssd *self, GAsyncResult *res, GTask *task) { InitializationContext *ctx; GError *error = NULL; if (!MM_IFACE_MODEM_3GPP_USSD_GET_INTERFACE (self)->check_support_finish (self, res, &error)) { if (error) { /* This error shouldn't be treated as critical */ mm_obj_dbg (self, "USSD support check failed: %s", error->message); g_error_free (error); } } else { /* USSD is supported! */ g_object_set_qdata (G_OBJECT (self), supported_quark, GUINT_TO_POINTER (TRUE)); } /* Go on to next step */ ctx = g_task_get_task_data (task); ctx->step++; interface_initialization_step (task); } static void interface_initialization_step (GTask *task) { MMIfaceModem3gppUssd *self; InitializationContext *ctx; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); switch (ctx->step) { case INITIALIZATION_STEP_FIRST: /* Setup quarks if we didn't do it before */ if (G_UNLIKELY (!support_checked_quark)) support_checked_quark = (g_quark_from_static_string ( SUPPORT_CHECKED_TAG)); if (G_UNLIKELY (!supported_quark)) supported_quark = (g_quark_from_static_string ( SUPPORTED_TAG)); ctx->step++; /* fall through */ case INITIALIZATION_STEP_CHECK_SUPPORT: if (!GPOINTER_TO_UINT (g_object_get_qdata (G_OBJECT (self), support_checked_quark))) { /* Set the checked flag so that we don't run it again */ g_object_set_qdata (G_OBJECT (self), support_checked_quark, GUINT_TO_POINTER (TRUE)); /* Initially, assume we don't support it */ g_object_set_qdata (G_OBJECT (self), supported_quark, GUINT_TO_POINTER (FALSE)); if (MM_IFACE_MODEM_3GPP_USSD_GET_INTERFACE (self)->check_support && MM_IFACE_MODEM_3GPP_USSD_GET_INTERFACE (self)->check_support_finish) { MM_IFACE_MODEM_3GPP_USSD_GET_INTERFACE (self)->check_support ( self, (GAsyncReadyCallback)check_support_ready, task); return; } /* If there is no implementation to check support, assume we DON'T * support it. */ } ctx->step++; /* fall through */ case INITIALIZATION_STEP_FAIL_IF_UNSUPPORTED: if (!GPOINTER_TO_UINT (g_object_get_qdata (G_OBJECT (self), supported_quark))) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "USSD not supported"); g_object_unref (task); return; } ctx->step++; /* fall through */ case INITIALIZATION_STEP_LAST: /* We are done without errors! */ /* Handle method invocations */ g_signal_connect (ctx->skeleton, "handle-initiate", G_CALLBACK (handle_initiate), self); g_signal_connect (ctx->skeleton, "handle-respond", G_CALLBACK (handle_respond), self); g_signal_connect (ctx->skeleton, "handle-cancel", G_CALLBACK (handle_cancel), self); /* Finally, export the new interface */ mm_gdbus_object_skeleton_set_modem3gpp_ussd (MM_GDBUS_OBJECT_SKELETON (self), MM_GDBUS_MODEM3GPP_USSD (ctx->skeleton)); g_task_return_boolean (task, TRUE); g_object_unref (task); return; default: break; } g_assert_not_reached (); } gboolean mm_iface_modem_3gpp_ussd_initialize_finish (MMIfaceModem3gppUssd *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } void mm_iface_modem_3gpp_ussd_initialize (MMIfaceModem3gppUssd *self, GAsyncReadyCallback callback, gpointer user_data) { InitializationContext *ctx; MmGdbusModem3gppUssd *skeleton = NULL; GTask *task; /* Did we already create it? */ g_object_get (self, MM_IFACE_MODEM_3GPP_USSD_DBUS_SKELETON, &skeleton, NULL); if (!skeleton) { skeleton = mm_gdbus_modem3gpp_ussd_skeleton_new (); /* Set all initial property defaults */ mm_gdbus_modem3gpp_ussd_set_state (skeleton, MM_MODEM_3GPP_USSD_SESSION_STATE_UNKNOWN); mm_gdbus_modem3gpp_ussd_set_network_notification (skeleton, NULL); mm_gdbus_modem3gpp_ussd_set_network_request (skeleton, NULL); g_object_set (self, MM_IFACE_MODEM_3GPP_USSD_DBUS_SKELETON, skeleton, NULL); } /* Perform async initialization here */ ctx = g_new0 (InitializationContext, 1); ctx->step = INITIALIZATION_STEP_FIRST; ctx->skeleton = skeleton; task = g_task_new (self, NULL, callback, user_data); g_task_set_task_data (task, ctx, (GDestroyNotify)initialization_context_free); interface_initialization_step (task); } void mm_iface_modem_3gpp_ussd_shutdown (MMIfaceModem3gppUssd *self) { /* Unexport DBus interface and remove the skeleton */ mm_gdbus_object_skeleton_set_modem3gpp_ussd (MM_GDBUS_OBJECT_SKELETON (self), NULL); g_object_set (self, MM_IFACE_MODEM_3GPP_USSD_DBUS_SKELETON, NULL, NULL); } /*****************************************************************************/ static void iface_modem_3gpp_ussd_init (gpointer g_iface) { static gboolean initialized = FALSE; if (initialized) return; /* Properties */ g_object_interface_install_property (g_iface, g_param_spec_object (MM_IFACE_MODEM_3GPP_USSD_DBUS_SKELETON, "3GPP DBus skeleton", "DBus skeleton for the 3GPP interface", MM_GDBUS_TYPE_MODEM3GPP_USSD_SKELETON, G_PARAM_READWRITE)); initialized = TRUE; } GType mm_iface_modem_3gpp_ussd_get_type (void) { static GType iface_modem_3gpp_ussd_type = 0; if (!G_UNLIKELY (iface_modem_3gpp_ussd_type)) { static const GTypeInfo info = { sizeof (MMIfaceModem3gppUssd), /* class_size */ iface_modem_3gpp_ussd_init, /* base_init */ NULL, /* base_finalize */ }; iface_modem_3gpp_ussd_type = g_type_register_static (G_TYPE_INTERFACE, "MMIfaceModem3gppUssd", &info, 0); g_type_interface_add_prerequisite (iface_modem_3gpp_ussd_type, MM_TYPE_IFACE_MODEM_3GPP); } return iface_modem_3gpp_ussd_type; } ModemManager-1.23.4-dev/src/mm-iface-modem-3gpp-ussd.h000066400000000000000000000203041456466623000222640ustar00rootroot00000000000000/* -*- 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 Guido Guenther * Copyright (C) 2012 Google, Inc. */ #ifndef MM_IFACE_MODEM_3GPP_USSD_H #define MM_IFACE_MODEM_3GPP_USSD_H #include #include #define _LIBMM_INSIDE_MM #include #define MM_TYPE_IFACE_MODEM_3GPP_USSD (mm_iface_modem_3gpp_ussd_get_type ()) #define MM_IFACE_MODEM_3GPP_USSD(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_IFACE_MODEM_3GPP_USSD, MMIfaceModem3gppUssd)) #define MM_IS_IFACE_MODEM_3GPP_USSD(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_IFACE_MODEM_3GPP_USSD)) #define MM_IFACE_MODEM_3GPP_USSD_GET_INTERFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE ((obj), MM_TYPE_IFACE_MODEM_3GPP_USSD, MMIfaceModem3gppUssd)) /* CBS data coding scheme - 3GPP TS 23.038 */ #define MM_MODEM_GSM_USSD_SCHEME_7BIT 0b00001111 #define MM_MODEM_GSM_USSD_SCHEME_UCS2 0b01001000 #define MM_IFACE_MODEM_3GPP_USSD_DBUS_SKELETON "iface-modem-3gpp-ussd-dbus-skeleton" typedef struct _MMIfaceModem3gppUssd MMIfaceModem3gppUssd; struct _MMIfaceModem3gppUssd { GTypeInterface g_iface; /* Check for USSD support (async) */ void (* check_support) (MMIfaceModem3gppUssd *self, GAsyncReadyCallback callback, gpointer user_data); gboolean (* check_support_finish) (MMIfaceModem3gppUssd *self, GAsyncResult *res, GError **error); /* Asynchronous setup of unsolicited events */ void (* setup_unsolicited_events) (MMIfaceModem3gppUssd *self, GAsyncReadyCallback callback, gpointer user_data); gboolean (* setup_unsolicited_events_finish) (MMIfaceModem3gppUssd *self, GAsyncResult *res, GError **error); /* Asynchronous enabling of unsolicited events */ void (* enable_unsolicited_events) (MMIfaceModem3gppUssd *self, GAsyncReadyCallback callback, gpointer user_data); gboolean (* enable_unsolicited_events_finish) (MMIfaceModem3gppUssd *self, GAsyncResult *res, GError **error); /* Asynchronous disabling of unsolicited events */ void (* disable_unsolicited_events) (MMIfaceModem3gppUssd *self, GAsyncReadyCallback callback, gpointer user_data); gboolean (* disable_unsolicited_events_finish) (MMIfaceModem3gppUssd *self, GAsyncResult *res, GError **error); /* Asynchronous cleaning up of unsolicited events */ void (* cleanup_unsolicited_events) (MMIfaceModem3gppUssd *self, GAsyncReadyCallback callback, gpointer user_data); gboolean (* cleanup_unsolicited_events_finish) (MMIfaceModem3gppUssd *self, GAsyncResult *res, GError **error); /* Encode/Decode */ gchar * (*encode) (MMIfaceModem3gppUssd *self, const gchar *command, guint *scheme, GError **error); gchar * (*decode) (MMIfaceModem3gppUssd *self, const gchar *reply, GError **error); /* Send command */ void (* send) (MMIfaceModem3gppUssd *self, const gchar *command, GAsyncReadyCallback callback, gpointer user_data); gchar * (* send_finish) (MMIfaceModem3gppUssd *self, GAsyncResult *res, GError **error); /* Cancel */ void (* cancel) (MMIfaceModem3gppUssd *self, GAsyncReadyCallback callback, gpointer user_data); gboolean (* cancel_finish) (MMIfaceModem3gppUssd *self, GAsyncResult *res, GError **error); }; GType mm_iface_modem_3gpp_ussd_get_type (void); G_DEFINE_AUTOPTR_CLEANUP_FUNC (MMIfaceModem3gppUssd, g_object_unref) /* Initialize USSD interface (async) */ void mm_iface_modem_3gpp_ussd_initialize (MMIfaceModem3gppUssd *self, GAsyncReadyCallback callback, gpointer user_data); gboolean mm_iface_modem_3gpp_ussd_initialize_finish (MMIfaceModem3gppUssd *self, GAsyncResult *res, GError **error); /* Enable USSD interface (async) */ void mm_iface_modem_3gpp_ussd_enable (MMIfaceModem3gppUssd *self, GAsyncReadyCallback callback, gpointer user_data); gboolean mm_iface_modem_3gpp_ussd_enable_finish (MMIfaceModem3gppUssd *self, GAsyncResult *res, GError **error); /* Disable USSD interface (async) */ void mm_iface_modem_3gpp_ussd_disable (MMIfaceModem3gppUssd *self, GAsyncReadyCallback callback, gpointer user_data); gboolean mm_iface_modem_3gpp_ussd_disable_finish (MMIfaceModem3gppUssd *self, GAsyncResult *res, GError **error); MMModem3gppUssdSessionState mm_iface_modem_3gpp_ussd_get_state (MMIfaceModem3gppUssd *self); /* Property updaters */ void mm_iface_modem_3gpp_ussd_update_state (MMIfaceModem3gppUssd *self, MMModem3gppUssdSessionState new_state); void mm_iface_modem_3gpp_ussd_update_network_notification (MMIfaceModem3gppUssd *self, const gchar *network_notification); void mm_iface_modem_3gpp_ussd_update_network_request (MMIfaceModem3gppUssd *self, const gchar *network_request); /* Encode/Decode USSD */ gchar *mm_iface_modem_3gpp_ussd_encode (MMIfaceModem3gppUssd *self, const gchar *command, guint *scheme, GError **error); gchar *mm_iface_modem_3gpp_ussd_decode (MMIfaceModem3gppUssd *self, const gchar *reply, GError **error); /* Shutdown USSD interface */ void mm_iface_modem_3gpp_ussd_shutdown (MMIfaceModem3gppUssd *self); /* Bind properties for simple GetStatus() */ void mm_iface_modem_3gpp_ussd_bind_simple_status (MMIfaceModem3gppUssd *self, MMSimpleStatus *status); #endif /* MM_IFACE_MODEM_3GPP_USSD_H */ ModemManager-1.23.4-dev/src/mm-iface-modem-3gpp.c000066400000000000000000004452001456466623000213110ustar00rootroot00000000000000/* -*- 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) 2011 - 2012 Google, Inc. * Copyright (c) 2022 Qualcomm Innovation Center, Inc. */ #include #include #include #define _LIBMM_INSIDE_MM #include #include "mm-iface-modem.h" #include "mm-iface-modem-location.h" #include "mm-iface-modem-3gpp.h" #include "mm-iface-modem-3gpp-profile-manager.h" #include "mm-base-modem.h" #include "mm-modem-helpers.h" #include "mm-error-helpers.h" #include "mm-log.h" #include "mm-log-helpers.h" #define SUBSYSTEM_3GPP "3gpp" /* When comparing EPS bearer settings take into account that: * - 'password' may not always be readable. * - 'apn-type' may not always be supported. * - 'access-type-preference' may not always be reported. * - 'profile-id' will not be known in the requested settings * - 'profile-name' might not be known in the requested settings * - we ignore settings not applicable to profiles, like 'allow-roaming' or * 'rm-protocol'. * - we apply very loose matching for all fields. */ #define MM_BEARER_PROPERTIES_CMP_FLAGS_EPS \ (MM_BEARER_PROPERTIES_CMP_FLAGS_LOOSE | \ MM_BEARER_PROPERTIES_CMP_FLAGS_NO_PROFILE_ID | \ MM_BEARER_PROPERTIES_CMP_FLAGS_NO_PROFILE_NAME | \ MM_BEARER_PROPERTIES_CMP_FLAGS_NO_PASSWORD | \ MM_BEARER_PROPERTIES_CMP_FLAGS_NO_ALLOW_ROAMING | \ MM_BEARER_PROPERTIES_CMP_FLAGS_NO_APN_TYPE | \ MM_BEARER_PROPERTIES_CMP_FLAGS_NO_RM_PROTOCOL | \ MM_BEARER_PROPERTIES_CMP_FLAGS_NO_ACCESS_TYPE_PREFERENCE | \ MM_BEARER_PROPERTIES_CMP_FLAGS_NO_ROAMING_ALLOWANCE) /*****************************************************************************/ /* Private data context */ #define PRIVATE_TAG "iface-modem-3gpp-private-tag" static GQuark private_quark; typedef struct { /* Interface enabled or disabled */ gboolean iface_enabled; /* Registration state */ MMModem3gppRegistrationState state_cs; MMModem3gppRegistrationState state_ps; MMModem3gppRegistrationState state_eps; MMModem3gppRegistrationState state_5gs; gboolean manual_registration; gchar *manual_registration_operator_id; GCancellable *pending_registration_cancellable; gboolean reloading_registration_info; /* Registration checks */ guint check_timeout_source; gboolean check_running; /* Packet service state */ gboolean packet_service_state_update_supported; } Private; static void private_free (Private *priv) { g_free (priv->manual_registration_operator_id); if (priv->pending_registration_cancellable) { g_cancellable_cancel (priv->pending_registration_cancellable); g_object_unref (priv->pending_registration_cancellable); } if (priv->check_timeout_source) g_source_remove (priv->check_timeout_source); g_slice_free (Private, priv); } static Private * get_private (MMIfaceModem3gpp *self) { Private *priv; if (G_UNLIKELY (!private_quark)) private_quark = g_quark_from_static_string (PRIVATE_TAG); priv = g_object_get_qdata (G_OBJECT (self), private_quark); if (!priv) { priv = g_slice_new0 (Private); priv->state_cs = MM_MODEM_3GPP_REGISTRATION_STATE_UNKNOWN; priv->state_ps = MM_MODEM_3GPP_REGISTRATION_STATE_UNKNOWN; priv->state_eps = MM_MODEM_3GPP_REGISTRATION_STATE_UNKNOWN; priv->state_5gs = MM_MODEM_3GPP_REGISTRATION_STATE_UNKNOWN; g_object_set_qdata_full (G_OBJECT (self), private_quark, priv, (GDestroyNotify)private_free); } return priv; } /*****************************************************************************/ #define GET_NETWORK_SUPPORTED(domain,DOMAIN) \ static gboolean \ get_##domain##_network_supported (MMIfaceModem3gpp *self) \ { \ gboolean supported = FALSE; \ \ g_object_get (self, \ MM_IFACE_MODEM_3GPP_##DOMAIN##_NETWORK_SUPPORTED, &supported, \ NULL); \ return supported; \ } GET_NETWORK_SUPPORTED (cs, CS) GET_NETWORK_SUPPORTED (ps, PS) GET_NETWORK_SUPPORTED (eps, EPS) GET_NETWORK_SUPPORTED (5gs, 5GS) /*****************************************************************************/ /* Helper method to wait for a final packet service state */ typedef struct { MMModem3gppPacketServiceState final_state; gulong state_changed_id; guint timeout_id; gulong cancellable_id; } WaitForPacketServiceStateContext; MMModem3gppPacketServiceState mm_iface_modem_3gpp_wait_for_packet_service_state_finish (MMIfaceModem3gpp *self, GAsyncResult *res, GError **error) { GError *inner_error = NULL; gssize value; value = g_task_propagate_int (G_TASK (res), &inner_error); if (inner_error) { g_propagate_error (error, inner_error); return MM_MODEM_3GPP_PACKET_SERVICE_STATE_UNKNOWN; } if (value == MM_MODEM_3GPP_PACKET_SERVICE_STATE_UNKNOWN) g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Unknown packet service state"); return (MMModem3gppPacketServiceState)value; } static void wait_for_packet_service_state_context_complete (GTask *task, MMModem3gppPacketServiceState state, GError *error) { MMIfaceModem3gpp *self; WaitForPacketServiceStateContext *ctx; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); g_assert (ctx->state_changed_id); if (g_signal_handler_is_connected (self, ctx->state_changed_id)) g_signal_handler_disconnect (self, ctx->state_changed_id); ctx->state_changed_id = 0; g_assert (ctx->timeout_id); g_source_remove (ctx->timeout_id); ctx->timeout_id = 0; if (!g_task_return_error_if_cancelled (task)) { if (ctx->cancellable_id) { g_cancellable_disconnect (g_task_get_cancellable (task), ctx->cancellable_id); ctx->cancellable_id = 0; } if (error) g_task_return_error (task, error); else g_task_return_int (task, state); } g_object_unref (task); } static void packet_service_wait_cancelled (GCancellable *cancellable, GTask *task) { MMIfaceModem3gpp *self; WaitForPacketServiceStateContext *ctx; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); mm_obj_dbg (self, "wait for packet service state '%s': cancelled", (ctx->final_state == MM_MODEM_3GPP_PACKET_SERVICE_STATE_UNKNOWN) ? "any" : mm_modem_3gpp_packet_service_state_get_string (ctx->final_state)); /* Given that the cancellable is the same one as in the task, we can complete the operation here * without specifying an exact error. The task will itself be completed with a cancelled error. */ g_assert (g_task_get_cancellable (task) == cancellable); wait_for_packet_service_state_context_complete (task, MM_MODEM_3GPP_PACKET_SERVICE_STATE_UNKNOWN, NULL); } static gboolean packet_service_wait_timeout (GTask *task) { MMIfaceModem3gpp *self; WaitForPacketServiceStateContext *ctx; GError *error; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); mm_obj_dbg (self, "wait for packet service state '%s': timed out", (ctx->final_state == MM_MODEM_3GPP_PACKET_SERVICE_STATE_UNKNOWN) ? "any" : mm_modem_3gpp_packet_service_state_get_string (ctx->final_state)); error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_RETRY, "Too much time waiting to get to a final packet service state"); wait_for_packet_service_state_context_complete (task, MM_MODEM_3GPP_PACKET_SERVICE_STATE_UNKNOWN, error); return G_SOURCE_REMOVE; } static void packet_service_state_changed (MMIfaceModem3gpp *self, GParamSpec *spec, GTask *task) { WaitForPacketServiceStateContext *ctx; MMModem3gppPacketServiceState state = MM_MODEM_3GPP_PACKET_SERVICE_STATE_UNKNOWN; g_object_get (self, MM_IFACE_MODEM_3GPP_PACKET_SERVICE_STATE, &state, NULL); /* Ignore unknown state explicitly during a wait operation */ if (state == MM_MODEM_3GPP_PACKET_SERVICE_STATE_UNKNOWN) return; ctx = g_task_get_task_data (task); /* If we want a specific final state and this is not the one we were * looking for, then skip */ if ((ctx->final_state != MM_MODEM_3GPP_PACKET_SERVICE_STATE_UNKNOWN) && (state != ctx->final_state)) return; mm_obj_dbg (self, "wait for packet service state '%s': finished", (ctx->final_state == MM_MODEM_3GPP_PACKET_SERVICE_STATE_UNKNOWN) ? "any" : mm_modem_3gpp_packet_service_state_get_string (ctx->final_state)); /* Done! */ wait_for_packet_service_state_context_complete (task, state, NULL); } void mm_iface_modem_3gpp_wait_for_packet_service_state (MMIfaceModem3gpp *self, MMModem3gppPacketServiceState final_state, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { MMModem3gppPacketServiceState state = MM_MODEM_3GPP_PACKET_SERVICE_STATE_UNKNOWN; WaitForPacketServiceStateContext *ctx; GTask *task; task = g_task_new (self, cancellable, callback, user_data); g_object_get (self, MM_IFACE_MODEM_3GPP_PACKET_SERVICE_STATE, &state, NULL); /* Is this the state we actually wanted? */ if (final_state == MM_MODEM_3GPP_PACKET_SERVICE_STATE_UNKNOWN || (state != MM_MODEM_3GPP_PACKET_SERVICE_STATE_UNKNOWN && state == final_state)) { g_task_return_int (task, state); g_object_unref (task); return; } /* Otherwise, we'll need to wait for the exact one we want */ ctx = g_new0 (WaitForPacketServiceStateContext, 1); ctx->final_state = final_state; g_task_set_task_data (task, ctx, g_free); /* Ownership of the task will be shared among the signal handler, the timeout, * and the cancellable. As soon as one of them is triggered, it should cancel the * other two. */ /* Want to get notified when packet service state changes */ ctx->state_changed_id = g_signal_connect (self, "notify::" MM_IFACE_MODEM_3GPP_PACKET_SERVICE_STATE, G_CALLBACK (packet_service_state_changed), task); /* But we don't want to wait forever */ ctx->timeout_id = g_timeout_add_seconds (10, (GSourceFunc)packet_service_wait_timeout, task); /* And we want it to be cancellable */ if (cancellable) { ctx->cancellable_id = g_cancellable_connect (cancellable, (GCallback) packet_service_wait_cancelled, task, NULL); /* Do nothing if already cancelled, packet_service_wait_cancelled() will already be called */ if (!ctx->cancellable_id) return; } mm_obj_dbg (self, "wait for packet service state '%s': started", (final_state == MM_MODEM_3GPP_PACKET_SERVICE_STATE_UNKNOWN) ? "any" : mm_modem_3gpp_packet_service_state_get_string (final_state)); } /*****************************************************************************/ void mm_iface_modem_3gpp_bind_simple_status (MMIfaceModem3gpp *self, MMSimpleStatus *status) { MmGdbusModem3gpp *skeleton; g_object_get (self, MM_IFACE_MODEM_3GPP_DBUS_SKELETON, &skeleton, NULL); if (!skeleton) return; g_object_bind_property (skeleton, "registration-state", status, MM_SIMPLE_PROPERTY_3GPP_REGISTRATION_STATE, G_BINDING_DEFAULT | G_BINDING_SYNC_CREATE); g_object_bind_property (skeleton, "operator-code", status, MM_SIMPLE_PROPERTY_3GPP_OPERATOR_CODE, G_BINDING_DEFAULT | G_BINDING_SYNC_CREATE); g_object_bind_property (skeleton, "operator-name", status, MM_SIMPLE_PROPERTY_3GPP_OPERATOR_NAME, G_BINDING_DEFAULT | G_BINDING_SYNC_CREATE); g_object_unref (skeleton); } /*****************************************************************************/ static MMModem3gppPacketServiceState get_consolidated_packet_service_state (MMIfaceModem3gpp *self) { Private *priv; priv = get_private (self); g_assert (!priv->packet_service_state_update_supported); /* If registered in any of PS, EPS or 5GS, then packet service domain is * implicitly attached. */ if (mm_modem_3gpp_registration_state_is_registered (priv->state_ps) || mm_modem_3gpp_registration_state_is_registered (priv->state_eps) || mm_modem_3gpp_registration_state_is_registered (priv->state_5gs)) return MM_MODEM_3GPP_PACKET_SERVICE_STATE_ATTACHED; if (mm_modem_3gpp_registration_state_is_registered (priv->state_cs)) return MM_MODEM_3GPP_PACKET_SERVICE_STATE_DETACHED; /* If not registered in any of CS, PS, EPS or 5GS, then packet service * domain is detached. */ return MM_MODEM_3GPP_PACKET_SERVICE_STATE_DETACHED; } static MMModem3gppRegistrationState get_consolidated_reg_state (MMIfaceModem3gpp *self) { Private *priv; priv = get_private (self); /* Some devices (Blackberries for example) will respond to +CGREG, but * return ERROR for +CREG, probably because their firmware is just stupid. * So here we prefer the +CREG response, but if we never got a successful * +CREG response, we'll take +CGREG instead. */ if (priv->state_cs == MM_MODEM_3GPP_REGISTRATION_STATE_HOME || priv->state_cs == MM_MODEM_3GPP_REGISTRATION_STATE_ROAMING) return priv->state_cs; if (priv->state_ps == MM_MODEM_3GPP_REGISTRATION_STATE_HOME || priv->state_ps == MM_MODEM_3GPP_REGISTRATION_STATE_ROAMING) return priv->state_ps; if (priv->state_eps == MM_MODEM_3GPP_REGISTRATION_STATE_HOME || priv->state_eps == MM_MODEM_3GPP_REGISTRATION_STATE_ROAMING) return priv->state_eps; if (priv->state_5gs == MM_MODEM_3GPP_REGISTRATION_STATE_HOME || priv->state_5gs == MM_MODEM_3GPP_REGISTRATION_STATE_ROAMING) return priv->state_5gs; /* Searching? */ if (priv->state_cs == MM_MODEM_3GPP_REGISTRATION_STATE_SEARCHING || priv->state_ps == MM_MODEM_3GPP_REGISTRATION_STATE_SEARCHING || priv->state_eps == MM_MODEM_3GPP_REGISTRATION_STATE_SEARCHING || priv->state_5gs == MM_MODEM_3GPP_REGISTRATION_STATE_SEARCHING) return MM_MODEM_3GPP_REGISTRATION_STATE_SEARCHING; #define REG_STATE_IS_UNKNOWN_IDLE_DENIED(state) \ (state == MM_MODEM_3GPP_REGISTRATION_STATE_UNKNOWN || \ state == MM_MODEM_3GPP_REGISTRATION_STATE_IDLE || \ state == MM_MODEM_3GPP_REGISTRATION_STATE_DENIED) /* If at least one state is DENIED and the others are UNKNOWN or IDLE, use DENIED */ if ((priv->state_cs == MM_MODEM_3GPP_REGISTRATION_STATE_DENIED || priv->state_ps == MM_MODEM_3GPP_REGISTRATION_STATE_DENIED || priv->state_eps == MM_MODEM_3GPP_REGISTRATION_STATE_DENIED || priv->state_5gs == MM_MODEM_3GPP_REGISTRATION_STATE_DENIED) && REG_STATE_IS_UNKNOWN_IDLE_DENIED (priv->state_cs) && REG_STATE_IS_UNKNOWN_IDLE_DENIED (priv->state_ps) && REG_STATE_IS_UNKNOWN_IDLE_DENIED (priv->state_eps) && REG_STATE_IS_UNKNOWN_IDLE_DENIED (priv->state_5gs)) return MM_MODEM_3GPP_REGISTRATION_STATE_DENIED; #undef REG_STATE_IS_UNKNOWN_IDLE_DENIED /* Emergency services? */ if (priv->state_cs == MM_MODEM_3GPP_REGISTRATION_STATE_EMERGENCY_ONLY || priv->state_ps == MM_MODEM_3GPP_REGISTRATION_STATE_EMERGENCY_ONLY || priv->state_eps == MM_MODEM_3GPP_REGISTRATION_STATE_EMERGENCY_ONLY || priv->state_5gs == MM_MODEM_3GPP_REGISTRATION_STATE_EMERGENCY_ONLY) return MM_MODEM_3GPP_REGISTRATION_STATE_EMERGENCY_ONLY; /* Support for additional registration states reported when on LTE/5GNR. * * For example, we may see the modem registered in LTE (EPS==HOME), and we * may get "SMS only" reported for CS. * * We give these states a very low priority w.r.t. the other ones as they * are really likely never used (i.e. we would get as consolidated the LTE * registration state, not the CS fall back state). * * We also warn in that case, because ideally we should always report the * LTE registration state first, not this one. */ if (priv->state_cs == MM_MODEM_3GPP_REGISTRATION_STATE_HOME_SMS_ONLY || priv->state_cs == MM_MODEM_3GPP_REGISTRATION_STATE_ROAMING_SMS_ONLY || priv->state_cs == MM_MODEM_3GPP_REGISTRATION_STATE_HOME_CSFB_NOT_PREFERRED || priv->state_cs == MM_MODEM_3GPP_REGISTRATION_STATE_ROAMING_CSFB_NOT_PREFERRED) { mm_obj_warn (self, "3GPP CSFB registration state is consolidated: %s", mm_modem_3gpp_registration_state_get_string (priv->state_cs)); return priv->state_cs; } /* Idle? */ if (priv->state_cs == MM_MODEM_3GPP_REGISTRATION_STATE_IDLE || priv->state_ps == MM_MODEM_3GPP_REGISTRATION_STATE_IDLE || priv->state_eps == MM_MODEM_3GPP_REGISTRATION_STATE_IDLE || priv->state_5gs == MM_MODEM_3GPP_REGISTRATION_STATE_IDLE) return MM_MODEM_3GPP_REGISTRATION_STATE_IDLE; return MM_MODEM_3GPP_REGISTRATION_STATE_UNKNOWN; } /*****************************************************************************/ typedef struct { MMIfaceModem3gpp *self; MmGdbusModem3gpp *skeleton; GCancellable *cancellable; gboolean force_registration; gchar *operator_id; GTimer *timer; guint max_registration_time; } RegisterInNetworkContext; static void register_in_network_context_free (RegisterInNetworkContext *ctx) { if (ctx->timer) g_timer_destroy (ctx->timer); if (ctx->cancellable) { Private *priv; /* Clear our cancellable if still around */ priv = get_private (ctx->self); if (priv->pending_registration_cancellable == ctx->cancellable) g_clear_object (&priv->pending_registration_cancellable); g_object_unref (ctx->cancellable); } g_free (ctx->operator_id); if (ctx->skeleton) g_object_unref (ctx->skeleton); g_object_unref (ctx->self); g_slice_free (RegisterInNetworkContext, ctx); } static void register_in_network_context_complete_failed (GTask *task, GError *error) { RegisterInNetworkContext *ctx; ctx = g_task_get_task_data (task); mm_iface_modem_3gpp_update_cs_registration_state (ctx->self, MM_MODEM_3GPP_REGISTRATION_STATE_IDLE, TRUE); mm_iface_modem_3gpp_update_ps_registration_state (ctx->self, MM_MODEM_3GPP_REGISTRATION_STATE_IDLE, TRUE); mm_iface_modem_3gpp_update_eps_registration_state (ctx->self, MM_MODEM_3GPP_REGISTRATION_STATE_IDLE, TRUE); mm_iface_modem_3gpp_update_5gs_registration_state (ctx->self, MM_MODEM_3GPP_REGISTRATION_STATE_IDLE, TRUE); mm_iface_modem_3gpp_apply_deferred_registration_state (ctx->self); mm_iface_modem_3gpp_update_access_technologies (ctx->self, MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN); mm_iface_modem_3gpp_update_location (ctx->self, 0, 0, 0); g_task_return_error (task, error); g_object_unref (task); } gboolean mm_iface_modem_3gpp_register_in_network_finish (MMIfaceModem3gpp *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static gboolean run_registration_checks (GTask *task); static void run_registration_checks_ready (MMIfaceModem3gpp *self, GAsyncResult *res, GTask *task) { RegisterInNetworkContext *ctx; GError *error = NULL; MMModem3gppRegistrationState current_registration_state; ctx = g_task_get_task_data (task); mm_iface_modem_3gpp_run_registration_checks_finish (MM_IFACE_MODEM_3GPP (self), res, &error); if (error) { mm_obj_info (self, "3GPP registration check failed: %s", error->message); register_in_network_context_complete_failed (task, error); return; } current_registration_state = get_consolidated_reg_state (ctx->self); /* If we got a final state and it's denied, we can assume the registration is * finished */ if (current_registration_state == MM_MODEM_3GPP_REGISTRATION_STATE_DENIED) { mm_obj_info (self, "registration denied"); register_in_network_context_complete_failed ( task, mm_mobile_equipment_error_for_code (MM_MOBILE_EQUIPMENT_ERROR_NETWORK_NOT_ALLOWED, self)); return; } /* If we got registered, end registration checks */ if (mm_modem_3gpp_registration_state_is_registered (current_registration_state)) { /* Request immediate access tech and signal update: we may have changed * from home to roaming or viceversa, both registered states, so there * wouldn't be an explicit refresh triggered from the modem interface as * the modem never got un-registered during the sequence. */ mm_iface_modem_refresh_signal (MM_IFACE_MODEM (ctx->self)); mm_obj_info (self, "currently registered in a 3GPP network"); g_task_return_boolean (task, TRUE); g_object_unref (task); return; } /* Don't spend too much time waiting to get registered */ if (g_timer_elapsed (ctx->timer, NULL) > ctx->max_registration_time) { mm_obj_info (self, "3GPP registration check timed out"); register_in_network_context_complete_failed ( task, mm_mobile_equipment_error_for_code (MM_MOBILE_EQUIPMENT_ERROR_NETWORK_TIMEOUT, self)); return; } /* If we're still waiting for automatic registration to complete or * fail, check again in a few seconds. * * This 3s timeout will catch results from automatic registrations as * well. */ mm_obj_dbg (self, "not yet registered in a 3GPP network... will recheck soon"); g_timeout_add_seconds (3, (GSourceFunc)run_registration_checks, task); } static gboolean run_registration_checks (GTask *task) { RegisterInNetworkContext *ctx; ctx = g_task_get_task_data (task); /* Get fresh registration state */ mm_iface_modem_3gpp_run_registration_checks ( ctx->self, (GAsyncReadyCallback)run_registration_checks_ready, task); return G_SOURCE_REMOVE; } static void register_in_network_ready (MMIfaceModem3gpp *self, GAsyncResult *res, GTask *task) { GError *error = NULL; if (!MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->register_in_network_finish (self, res, &error)) { /* Propagate error when trying to lock to network */ register_in_network_context_complete_failed (task, error); return; } /* Now try to gather current registration status until we're registered or * the time goes off */ run_registration_checks (task); } static void initial_registration_checks_ready (MMIfaceModem3gpp *self, GAsyncResult *res, GTask *task) { Private *priv; RegisterInNetworkContext *ctx; GError *error = NULL; const gchar *current_operator_code; MMModem3gppRegistrationState reg_state; priv = get_private (self); ctx = g_task_get_task_data (task); if (!mm_iface_modem_3gpp_run_registration_checks_finish (self, res, &error)) { mm_obj_info (self, "Initial 3GPP registration check failed: %s", error->message); g_error_free (error); /* Just continue as if nothing happened */ } current_operator_code = mm_gdbus_modem3gpp_get_operator_code (ctx->skeleton); reg_state = mm_gdbus_modem3gpp_get_registration_state (ctx->skeleton); /* Manual registration requested? */ if (ctx->operator_id) { /* If already registered manually with the requested operator, we're done */ if (!ctx->force_registration && (g_strcmp0 (current_operator_code, ctx->operator_id) == 0) && mm_modem_3gpp_registration_state_is_registered (reg_state) && priv->manual_registration) { mm_obj_info (self, "already registered manually in selected network '%s', manual registration not launched...", current_operator_code); g_task_return_boolean (task, TRUE); g_object_unref (task); return; } /* Manual registration to a new operator required */ mm_obj_info (self, "launching manual network registration in '%s'...", ctx->operator_id); g_free (priv->manual_registration_operator_id); priv->manual_registration_operator_id = g_strdup (ctx->operator_id); priv->manual_registration = TRUE; } /* Automatic registration requested? */ else { /* If the modem is already registered and the last time it was asked * automatic registration, we're done */ if (!ctx->force_registration && mm_modem_3gpp_registration_state_is_registered (reg_state) && !priv->manual_registration) { mm_obj_info (self, "already registered automatically in network '%s'," " automatic registration not launched...", current_operator_code ? current_operator_code : "unknown"); g_task_return_boolean (task, TRUE); g_object_unref (task); return; } /* Automatic registration to a new operator requested */ mm_obj_info (self, "launching automatic network registration..."); g_clear_pointer (&priv->manual_registration_operator_id, g_free); priv->manual_registration = FALSE; } ctx->cancellable = g_cancellable_new (); /* Keep an accessible reference to the cancellable, so that we can cancel * previous request when needed */ priv->pending_registration_cancellable = g_object_ref (ctx->cancellable); ctx->timer = g_timer_new (); MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->register_in_network ( self, ctx->operator_id, ctx->cancellable, (GAsyncReadyCallback)register_in_network_ready, task); } void mm_iface_modem_3gpp_register_in_network (MMIfaceModem3gpp *self, const gchar *operator_id, gboolean force_registration, guint max_registration_time, GAsyncReadyCallback callback, gpointer user_data) { Private *priv; RegisterInNetworkContext *ctx; GTask *task; GError *error = NULL; priv = get_private (self); ctx = g_slice_new0 (RegisterInNetworkContext); ctx->self = g_object_ref (self); ctx->force_registration = force_registration; ctx->operator_id = (operator_id && operator_id[0]) ? g_strdup (operator_id) : NULL; ctx->max_registration_time = max_registration_time; task = g_task_new (self, NULL, callback, user_data); g_task_set_task_data (task, ctx, (GDestroyNotify)register_in_network_context_free); g_object_get (self, MM_IFACE_MODEM_3GPP_DBUS_SKELETON, &ctx->skeleton, NULL); if (!ctx->skeleton) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't get interface skeleton"); g_object_unref (task); return; } /* Validate input MCC/MNC */ if (ctx->operator_id && !mm_3gpp_parse_operator_id (ctx->operator_id, NULL, NULL, NULL, &error)) { g_assert (error != NULL); g_task_return_error (task, error); g_object_unref (task); return; } /* (Try to) cancel previous registration request */ if (priv->pending_registration_cancellable) { g_cancellable_cancel (priv->pending_registration_cancellable); g_clear_object (&priv->pending_registration_cancellable); } /* Query initial registration state here in order to avoid re-registering. */ mm_iface_modem_3gpp_run_registration_checks ( self, (GAsyncReadyCallback)initial_registration_checks_ready, task); } /*****************************************************************************/ /* Request to reregister using the last settings */ #define REREGISTER_IN_NETWORK_TIMEOUT 120 gboolean mm_iface_modem_3gpp_reregister_in_network_finish (MMIfaceModem3gpp *self, GAsyncResult *res, GError **error) { return mm_iface_modem_3gpp_register_in_network_finish (self, res, error); } void mm_iface_modem_3gpp_reregister_in_network (MMIfaceModem3gpp *self, GAsyncReadyCallback callback, gpointer user_data) { Private *priv; /* Relaunch registration using the last used settings */ priv = get_private (self); mm_iface_modem_3gpp_register_in_network (self, priv->manual_registration_operator_id, TRUE, /* if already registered with same settings, force re-registration */ REREGISTER_IN_NETWORK_TIMEOUT, callback, user_data); } /*****************************************************************************/ typedef struct { MmGdbusModem3gpp *skeleton; GDBusMethodInvocation *invocation; MMIfaceModem3gpp *self; gchar *operator_id; } HandleRegisterContext; static void handle_register_context_free (HandleRegisterContext *ctx) { g_object_unref (ctx->skeleton); g_object_unref (ctx->invocation); g_object_unref (ctx->self); g_free (ctx->operator_id); g_slice_free (HandleRegisterContext, ctx); } static void handle_register_ready (MMIfaceModem3gpp *self, GAsyncResult *res, HandleRegisterContext *ctx) { GError *error = NULL; if (!mm_iface_modem_3gpp_register_in_network_finish (self, res, &error)) { if (ctx->operator_id && ctx->operator_id[0]) mm_obj_warn (self, "failed registering modem in '%s': %s", ctx->operator_id, error->message); else mm_obj_warn (self, "failed registering modem: %s", error->message); mm_dbus_method_invocation_take_error (ctx->invocation, error); } else { if (ctx->operator_id && ctx->operator_id[0]) mm_obj_info (self, "modem registered in '%s'", ctx->operator_id); else mm_obj_info (self, "modem registered"); mm_gdbus_modem3gpp_complete_register (ctx->skeleton, ctx->invocation); } handle_register_context_free (ctx); } static void handle_register_auth_ready (MMBaseModem *self, GAsyncResult *res, HandleRegisterContext *ctx) { MMModemState modem_state = MM_MODEM_STATE_UNKNOWN; GError *error = NULL; if (!mm_base_modem_authorize_finish (self, res, &error)) { mm_dbus_method_invocation_take_error (ctx->invocation, error); handle_register_context_free (ctx); return; } g_assert (MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->register_in_network != NULL); g_assert (MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->register_in_network_finish != NULL); g_object_get (self, MM_IFACE_MODEM_STATE, &modem_state, NULL); switch (modem_state) { case MM_MODEM_STATE_FAILED: case MM_MODEM_STATE_UNKNOWN: case MM_MODEM_STATE_INITIALIZING: case MM_MODEM_STATE_LOCKED: case MM_MODEM_STATE_DISABLED: case MM_MODEM_STATE_DISABLING: case MM_MODEM_STATE_ENABLING: mm_dbus_method_invocation_return_error_literal (ctx->invocation, MM_CORE_ERROR, MM_CORE_ERROR_WRONG_STATE, "Device not yet enabled"); handle_register_context_free (ctx); return; case MM_MODEM_STATE_ENABLED: case MM_MODEM_STATE_SEARCHING: case MM_MODEM_STATE_REGISTERED: if (ctx->operator_id && ctx->operator_id[0]) mm_obj_info (self, "processing user request to register modem in '%s'...", ctx->operator_id); else mm_obj_info (self, "processing user request to register modem..."); mm_iface_modem_3gpp_register_in_network (MM_IFACE_MODEM_3GPP (self), ctx->operator_id, FALSE, /* if already registered with same settings, do nothing */ 60, (GAsyncReadyCallback)handle_register_ready, ctx); return; case MM_MODEM_STATE_DISCONNECTING: case MM_MODEM_STATE_CONNECTING: case MM_MODEM_STATE_CONNECTED: mm_dbus_method_invocation_return_error_literal (ctx->invocation, MM_CORE_ERROR, MM_CORE_ERROR_WRONG_STATE, "Operation not allowed while modem is connected"); handle_register_context_free (ctx); return; default: g_assert_not_reached (); } } static gboolean handle_register (MmGdbusModem3gpp *skeleton, GDBusMethodInvocation *invocation, const gchar *operator_id, MMIfaceModem3gpp *self) { HandleRegisterContext *ctx; ctx = g_slice_new0 (HandleRegisterContext); ctx->skeleton = g_object_ref (skeleton); ctx->invocation = g_object_ref (invocation); ctx->self = g_object_ref (self); ctx->operator_id = g_strdup (operator_id); mm_base_modem_authorize (MM_BASE_MODEM (self), invocation, MM_AUTHORIZATION_DEVICE_CONTROL, (GAsyncReadyCallback)handle_register_auth_ready, ctx); return TRUE; } /*****************************************************************************/ typedef struct { MmGdbusModem3gpp *skeleton; GDBusMethodInvocation *invocation; MMIfaceModem3gpp *self; } HandleScanContext; static void handle_scan_context_free (HandleScanContext *ctx) { g_object_unref (ctx->skeleton); g_object_unref (ctx->invocation); g_object_unref (ctx->self); g_slice_free (HandleScanContext, ctx); } static GVariant * build_scan_networks_result (MMIfaceModem3gpp *self, GList *info_list) { GList *l; GVariantBuilder builder; g_variant_builder_init (&builder, G_VARIANT_TYPE ("aa{sv}")); for (l = info_list; l; l = g_list_next (l)) { MM3gppNetworkInfo *info = l->data; g_autofree gchar *access_tech_str = NULL; if (!info->operator_code) { g_warn_if_reached (); continue; } /* log results as INFO */ access_tech_str = mm_modem_access_technology_build_string_from_mask (info->access_tech); mm_obj_info (self, " mccmnc: %s, status: %s, access tech: %s, long name: %s, short name: %s", info->operator_code, mm_modem_3gpp_network_availability_get_string (info->status), access_tech_str, info->operator_long ? info->operator_long : "n/a", info->operator_short ? info->operator_short : "n/a"); g_variant_builder_open (&builder, G_VARIANT_TYPE ("a{sv}")); g_variant_builder_add (&builder, "{sv}", "operator-code", g_variant_new_string (info->operator_code)); g_variant_builder_add (&builder, "{sv}", "status", g_variant_new_uint32 (info->status)); g_variant_builder_add (&builder, "{sv}", "access-technology", g_variant_new_uint32 (info->access_tech)); if (info->operator_long) g_variant_builder_add (&builder, "{sv}", "operator-long", g_variant_new_string (info->operator_long)); if (info->operator_short) g_variant_builder_add (&builder, "{sv}", "operator-short", g_variant_new_string (info->operator_short)); g_variant_builder_close (&builder); } return g_variant_ref_sink (g_variant_builder_end (&builder)); } static void handle_scan_ready (MMIfaceModem3gpp *self, GAsyncResult *res, HandleScanContext *ctx) { GError *error = NULL; GList *info_list; g_autoptr(GVariant) dict_array = NULL; info_list = MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->scan_networks_finish (self, res, &error); if (error) { mm_obj_warn (self, "failed scanning networks: %s", error->message); mm_dbus_method_invocation_take_error (ctx->invocation, error); handle_scan_context_free (ctx); return; } mm_obj_info (self, "network scan performed: %u found", g_list_length (info_list)); dict_array = build_scan_networks_result (self, info_list); mm_gdbus_modem3gpp_complete_scan (ctx->skeleton, ctx->invocation, dict_array); mm_3gpp_network_info_list_free (info_list); handle_scan_context_free (ctx); } static void handle_scan_auth_ready (MMBaseModem *self, GAsyncResult *res, HandleScanContext *ctx) { GError *error = NULL; if (!mm_base_modem_authorize_finish (self, res, &error)) { mm_dbus_method_invocation_take_error (ctx->invocation, error); handle_scan_context_free (ctx); return; } /* If scanning is not implemented, report an error */ if (!MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->scan_networks || !MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->scan_networks_finish) { mm_dbus_method_invocation_return_error_literal (ctx->invocation, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "Cannot scan networks: operation not supported"); handle_scan_context_free (ctx); return; } if (mm_iface_modem_abort_invocation_if_state_not_reached (MM_IFACE_MODEM (self), ctx->invocation, MM_MODEM_STATE_ENABLED)) { handle_scan_context_free (ctx); return; } MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->scan_networks ( MM_IFACE_MODEM_3GPP (self), (GAsyncReadyCallback)handle_scan_ready, ctx); } static gboolean handle_scan (MmGdbusModem3gpp *skeleton, GDBusMethodInvocation *invocation, MMIfaceModem3gpp *self) { HandleScanContext *ctx; ctx = g_slice_new0 (HandleScanContext); ctx->skeleton = g_object_ref (skeleton); ctx->invocation = g_object_ref (invocation); ctx->self = g_object_ref (self); mm_base_modem_authorize (MM_BASE_MODEM (self), invocation, MM_AUTHORIZATION_DEVICE_CONTROL, (GAsyncReadyCallback)handle_scan_auth_ready, ctx); return TRUE; } /*****************************************************************************/ typedef struct { MmGdbusModem3gpp *skeleton; GDBusMethodInvocation *invocation; MMIfaceModem3gpp *self; MMModem3gppEpsUeModeOperation mode; } HandleSetEpsUeModeOperationContext; static void handle_set_eps_ue_mode_operation_context_free (HandleSetEpsUeModeOperationContext *ctx) { g_object_unref (ctx->skeleton); g_object_unref (ctx->invocation); g_object_unref (ctx->self); g_slice_free (HandleSetEpsUeModeOperationContext, ctx); } static void after_set_load_eps_ue_mode_operation_ready (MMIfaceModem3gpp *self, GAsyncResult *res, HandleSetEpsUeModeOperationContext *ctx) { MMModem3gppEpsUeModeOperation uemode; GError *error = NULL; uemode = MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->load_eps_ue_mode_operation_finish (self, res, &error); if (error) { mm_obj_warn (self, "failed reloading EPS UE mode of operation after update to '%s': %s", mm_modem_3gpp_eps_ue_mode_operation_get_string (ctx->mode), error->message); mm_dbus_method_invocation_take_error (ctx->invocation, error); handle_set_eps_ue_mode_operation_context_free (ctx); return; } if (uemode != ctx->mode) { mm_obj_info (self, "requested (%s) and reloaded (%s) EPS UE mode of operation don't match", mm_modem_3gpp_eps_ue_mode_operation_get_string (ctx->mode), mm_modem_3gpp_eps_ue_mode_operation_get_string (uemode)); mm_dbus_method_invocation_return_error_literal (ctx->invocation, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "EPS UE mode of operation wasn't updated"); handle_set_eps_ue_mode_operation_context_free (ctx); return; } mm_gdbus_modem3gpp_set_eps_ue_mode_operation (ctx->skeleton, uemode); mm_gdbus_modem3gpp_complete_set_eps_ue_mode_operation (ctx->skeleton, ctx->invocation); handle_set_eps_ue_mode_operation_context_free (ctx); } static void handle_set_eps_ue_mode_operation_ready (MMIfaceModem3gpp *self, GAsyncResult *res, HandleSetEpsUeModeOperationContext *ctx) { GError *error = NULL; if (!MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->set_eps_ue_mode_operation_finish (self, res, &error)) { mm_obj_warn (self, "failed setting EPS UE mode of operation to '%s': %s", mm_modem_3gpp_eps_ue_mode_operation_get_string (ctx->mode), error->message); mm_dbus_method_invocation_take_error (ctx->invocation, error); handle_set_eps_ue_mode_operation_context_free (ctx); return; } if (MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->load_eps_ue_mode_operation && MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->load_eps_ue_mode_operation_finish) { MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->load_eps_ue_mode_operation ( self, (GAsyncReadyCallback)after_set_load_eps_ue_mode_operation_ready, ctx); return; } /* Assume we're ok */ mm_obj_info (self, "EPS UE mode of operation set to '%s'", mm_modem_3gpp_eps_ue_mode_operation_get_string (ctx->mode)); mm_gdbus_modem3gpp_complete_set_eps_ue_mode_operation (ctx->skeleton, ctx->invocation); handle_set_eps_ue_mode_operation_context_free (ctx); } static void handle_set_eps_ue_mode_operation_auth_ready (MMBaseModem *self, GAsyncResult *res, HandleSetEpsUeModeOperationContext *ctx) { GError *error = NULL; if (!mm_base_modem_authorize_finish (self, res, &error)) { mm_dbus_method_invocation_take_error (ctx->invocation, error); handle_set_eps_ue_mode_operation_context_free (ctx); return; } /* Check if we already are in the requested mode */ if (mm_gdbus_modem3gpp_get_eps_ue_mode_operation (ctx->skeleton) == ctx->mode) { /* Nothing to do */ mm_gdbus_modem3gpp_complete_set_eps_ue_mode_operation (ctx->skeleton, ctx->invocation); handle_set_eps_ue_mode_operation_context_free (ctx); return; } /* If UE mode update is not implemented, report an error */ if (!MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->set_eps_ue_mode_operation || !MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->set_eps_ue_mode_operation_finish) { mm_dbus_method_invocation_return_error_literal (ctx->invocation, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "Cannot set UE mode of operation for EPS: operation not supported"); handle_set_eps_ue_mode_operation_context_free (ctx); return; } mm_obj_info (self, "processing user request to set EPS UE mode of operation to '%s'...", mm_modem_3gpp_eps_ue_mode_operation_get_string (ctx->mode)); MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->set_eps_ue_mode_operation ( MM_IFACE_MODEM_3GPP (self), ctx->mode, (GAsyncReadyCallback)handle_set_eps_ue_mode_operation_ready, ctx); } static gboolean handle_set_eps_ue_mode_operation (MmGdbusModem3gpp *skeleton, GDBusMethodInvocation *invocation, guint mode, MMIfaceModem3gpp *self) { HandleSetEpsUeModeOperationContext *ctx; ctx = g_slice_new0 (HandleSetEpsUeModeOperationContext); ctx->skeleton = g_object_ref (skeleton); ctx->invocation = g_object_ref (invocation); ctx->self = g_object_ref (self); ctx->mode = mode; mm_base_modem_authorize (MM_BASE_MODEM (self), invocation, MM_AUTHORIZATION_DEVICE_CONTROL, (GAsyncReadyCallback)handle_set_eps_ue_mode_operation_auth_ready, ctx); return TRUE; } /*****************************************************************************/ typedef struct { MmGdbusModem3gpp *skeleton; GDBusMethodInvocation *invocation; MMIfaceModem3gpp *self; GVariant *dictionary; MMBearerProperties *config; } HandleSetInitialEpsBearerSettingsContext; static void handle_set_initial_eps_bearer_settings_context_free (HandleSetInitialEpsBearerSettingsContext *ctx) { g_clear_object (&ctx->config); g_variant_unref (ctx->dictionary); g_object_unref (ctx->skeleton); g_object_unref (ctx->invocation); g_object_unref (ctx->self); g_slice_free (HandleSetInitialEpsBearerSettingsContext, ctx); } static void after_set_load_initial_eps_bearer_settings_ready (MMIfaceModem3gpp *self, GAsyncResult *res, HandleSetInitialEpsBearerSettingsContext *ctx) { GError *error = NULL; g_autoptr(MMBearerProperties) new_config = NULL; g_autoptr(GVariant) dictionary = NULL; new_config = MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->load_initial_eps_bearer_settings_finish (self, res, &error); if (error) { mm_obj_warn (self, "failed reloading initial EPS bearer settings after update: %s", error->message); mm_dbus_method_invocation_take_error (ctx->invocation, error); handle_set_initial_eps_bearer_settings_context_free (ctx); return; } if (!mm_bearer_properties_cmp (new_config, ctx->config, MM_BEARER_PROPERTIES_CMP_FLAGS_EPS)) { mm_obj_warn (self, "requested and reloaded initial EPS bearer settings don't match"); mm_obj_info (self, "reloaded initial EPS bearer settings:"); mm_log_bearer_properties (self, MM_LOG_LEVEL_INFO, " ", new_config); mm_dbus_method_invocation_return_error_literal (ctx->invocation, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Initial EPS bearer settings were not updated"); handle_set_initial_eps_bearer_settings_context_free (ctx); return; } dictionary = mm_bearer_properties_get_dictionary (new_config); mm_gdbus_modem3gpp_set_initial_eps_bearer_settings (ctx->skeleton, dictionary); mm_gdbus_modem3gpp_complete_set_initial_eps_bearer_settings (ctx->skeleton, ctx->invocation); handle_set_initial_eps_bearer_settings_context_free (ctx); } static void set_initial_eps_bearer_settings_ready (MMIfaceModem3gpp *self, GAsyncResult *res, HandleSetInitialEpsBearerSettingsContext *ctx) { GError *error = NULL; if (!MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->set_initial_eps_bearer_settings_finish (self, res, &error)) { mm_obj_warn (self, "failed setting initial EPS bearer settings: %s", error->message); /* process profile manager updates right away on error */ mm_iface_modem_3gpp_profile_manager_update_ignore_stop (MM_IFACE_MODEM_3GPP_PROFILE_MANAGER (self)); mm_dbus_method_invocation_take_error (ctx->invocation, error); handle_set_initial_eps_bearer_settings_context_free (ctx); return; } mm_obj_info (self, "initial EPS bearer settings updated"); /* delay processing profile manager updates on success */ mm_iface_modem_3gpp_profile_manager_update_ignore_stop_delayed (MM_IFACE_MODEM_3GPP_PROFILE_MANAGER (self)); if (MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->load_initial_eps_bearer_settings && MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->load_initial_eps_bearer_settings_finish) { MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->load_initial_eps_bearer_settings ( self, (GAsyncReadyCallback)after_set_load_initial_eps_bearer_settings_ready, ctx); return; } /* Assume we're ok */ mm_gdbus_modem3gpp_complete_set_initial_eps_bearer_settings (ctx->skeleton, ctx->invocation); handle_set_initial_eps_bearer_settings_context_free (ctx); } static void set_initial_eps_bearer_settings_auth_ready (MMBaseModem *self, GAsyncResult *res, HandleSetInitialEpsBearerSettingsContext *ctx) { gboolean force = FALSE; GError *error = NULL; GVariant *old_dictionary; g_autoptr(MMBearerProperties) old_config = NULL; if (!mm_base_modem_authorize_finish (self, res, &error)) { mm_dbus_method_invocation_take_error (ctx->invocation, error); handle_set_initial_eps_bearer_settings_context_free (ctx); return; } /* If UE mode update is not implemented, report an error */ if (!MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->set_initial_eps_bearer_settings || !MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->set_initial_eps_bearer_settings_finish) { mm_dbus_method_invocation_return_error_literal (ctx->invocation, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "Operation not supported"); handle_set_initial_eps_bearer_settings_context_free (ctx); return; } ctx->config = mm_bearer_properties_new_from_dictionary (ctx->dictionary, &error); if (!ctx->config) { mm_dbus_method_invocation_take_error (ctx->invocation, error); handle_set_initial_eps_bearer_settings_context_free (ctx); return; } force = mm_bearer_properties_get_force (ctx->config); mm_obj_info (self, "processing user request to set initial EPS bearer settings%s...", force ? " (forced)" : ""); mm_log_bearer_properties (self, MM_LOG_LEVEL_INFO, " ", ctx->config); old_dictionary = mm_gdbus_modem3gpp_get_initial_eps_bearer_settings (ctx->skeleton); if (old_dictionary) old_config = mm_bearer_properties_new_from_dictionary (old_dictionary, NULL); if (!force && old_config && mm_bearer_properties_cmp (ctx->config, old_config, MM_BEARER_PROPERTIES_CMP_FLAGS_EPS)) { mm_obj_info (self, "skipped setting initial EPS bearer settings: same configuration provided"); mm_gdbus_modem3gpp_complete_set_initial_eps_bearer_settings (ctx->skeleton, ctx->invocation); handle_set_initial_eps_bearer_settings_context_free (ctx); return; } /* Start ignoring our own indications */ mm_iface_modem_3gpp_profile_manager_update_ignore_start (MM_IFACE_MODEM_3GPP_PROFILE_MANAGER (self)); MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->set_initial_eps_bearer_settings ( MM_IFACE_MODEM_3GPP (self), ctx->config, (GAsyncReadyCallback)set_initial_eps_bearer_settings_ready, ctx); } static gboolean handle_set_initial_eps_bearer_settings (MmGdbusModem3gpp *skeleton, GDBusMethodInvocation *invocation, GVariant *dictionary, MMIfaceModem3gpp *self) { HandleSetInitialEpsBearerSettingsContext *ctx; ctx = g_slice_new0 (HandleSetInitialEpsBearerSettingsContext); ctx->skeleton = g_object_ref (skeleton); ctx->invocation = g_object_ref (invocation); ctx->self = g_object_ref (self); ctx->dictionary = g_variant_ref (dictionary); mm_base_modem_authorize (MM_BASE_MODEM (self), invocation, MM_AUTHORIZATION_DEVICE_CONTROL, (GAsyncReadyCallback)set_initial_eps_bearer_settings_auth_ready, ctx); return TRUE; } /*****************************************************************************/ typedef struct { MmGdbusModem3gpp *skeleton; GDBusMethodInvocation *invocation; MMIfaceModem3gpp *self; GVariant *dictionary; MMModem3gppFacility facility; gchar *facility_str; guint8 slot; gchar *control_key; } HandleDisableFacilityLockContext; static void handle_disable_facility_lock_context_free (HandleDisableFacilityLockContext *ctx) { g_object_unref (ctx->skeleton); g_object_unref (ctx->invocation); g_object_unref (ctx->self); g_variant_unref (ctx->dictionary); g_free (ctx->control_key); g_free (ctx->facility_str); g_slice_free (HandleDisableFacilityLockContext, ctx); } static void update_lock_info_ready (MMIfaceModem *modem, GAsyncResult *res, HandleDisableFacilityLockContext *ctx) { GError *error = NULL; mm_iface_modem_update_lock_info_finish (modem, res, &error); if (error) { mm_dbus_method_invocation_take_error (ctx->invocation, error); handle_disable_facility_lock_context_free (ctx); return; } mm_gdbus_modem3gpp_complete_disable_facility_lock (ctx->skeleton, ctx->invocation); handle_disable_facility_lock_context_free (ctx); } static void handle_disable_facility_lock_ready (MMIfaceModem3gpp *self, GAsyncResult *res, HandleDisableFacilityLockContext *ctx) { MMModem3gppFacility facilities; GError *error = NULL; if (!MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->disable_facility_lock_finish (self, res, &error)) { mm_obj_warn (self, "failed disabling facility lock '%s': %s", ctx->facility_str, error->message); mm_dbus_method_invocation_take_error (ctx->invocation, error); handle_disable_facility_lock_context_free (ctx); return; } mm_obj_info (self, "facility lock '%s' disabled", ctx->facility_str); /* Update the Enabled Facility Locks property in the DBus interface */ facilities = mm_gdbus_modem3gpp_get_enabled_facility_locks (ctx->skeleton); facilities &= ~ctx->facility; mm_gdbus_modem3gpp_set_enabled_facility_locks (ctx->skeleton, facilities); /* Recheck lock status after unlock code has been sent */ mm_iface_modem_update_lock_info (MM_IFACE_MODEM (self), MM_MODEM_LOCK_UNKNOWN, /* ask */ (GAsyncReadyCallback)update_lock_info_ready, ctx); } static void disable_facility_lock_auth_ready (MMBaseModem *self, GAsyncResult *res, HandleDisableFacilityLockContext *ctx) { GError *error = NULL; if (!mm_base_modem_authorize_finish (self, res, &error)) { mm_dbus_method_invocation_take_error (ctx->invocation, error); handle_disable_facility_lock_context_free (ctx); return; } /* If disable facility locks is not implemented, report an error */ if (!MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->disable_facility_lock || !MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->disable_facility_lock_finish) { mm_dbus_method_invocation_return_error_literal (ctx->invocation, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "Operation not supported"); handle_disable_facility_lock_context_free (ctx); return; } /* Parse properties dictionary */ if (!g_variant_is_of_type (ctx->dictionary, G_VARIANT_TYPE ("(us)"))) { mm_dbus_method_invocation_return_error_literal (ctx->invocation, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS, "Invalid parameters"); handle_disable_facility_lock_context_free (ctx); return; } /* Only modems with single slot or single configuration for all slots are supported */ ctx->slot = 1; g_variant_get (ctx->dictionary, "(us)", &ctx->facility, &ctx->control_key); /* Only four facility locks can be disabled: * - MM_MODEM_3GPP_FACILITY_NET_PERS (network personalization) * - MM_MODEM_3GPP_FACILITY_NET_SUB_PERS (network subset personalization) * - MM_MODEM_3GPP_FACILITY_PROVIDER_PERS (service provider personalization) * - MM_MODEM_3GPP_FACILITY_CORP_PERS (corporate personalization) */ if (ctx->facility != (ctx->facility & (MM_MODEM_3GPP_FACILITY_NET_PERS | MM_MODEM_3GPP_FACILITY_NET_SUB_PERS | MM_MODEM_3GPP_FACILITY_PROVIDER_PERS | MM_MODEM_3GPP_FACILITY_CORP_PERS))) { mm_dbus_method_invocation_return_error_literal (ctx->invocation, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS, "Invalid type of facility lock to disable or empty key"); handle_disable_facility_lock_context_free (ctx); return; } ctx->facility_str = mm_modem_3gpp_facility_build_string_from_mask (ctx->facility); mm_obj_info (self, "processing user request to disable facility lock '%s'...", ctx->facility_str); mm_obj_info (self, " control key: %s", mm_log_str_personal_info (ctx->control_key)); MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->disable_facility_lock ( MM_IFACE_MODEM_3GPP (self), ctx->facility, ctx->slot, ctx->control_key, (GAsyncReadyCallback)handle_disable_facility_lock_ready, ctx); } static gboolean handle_disable_facility_lock (MmGdbusModem3gpp *skeleton, GDBusMethodInvocation *invocation, GVariant *dictionary, MMIfaceModem3gpp *self) { HandleDisableFacilityLockContext *ctx; ctx = g_slice_new0 (HandleDisableFacilityLockContext); ctx->skeleton = g_object_ref (skeleton); ctx->invocation = g_object_ref (invocation); ctx->self = g_object_ref (self); ctx->dictionary = g_variant_ref (dictionary); mm_base_modem_authorize (MM_BASE_MODEM (self), invocation, MM_AUTHORIZATION_DEVICE_CONTROL, (GAsyncReadyCallback)disable_facility_lock_auth_ready, ctx); return TRUE; } /*****************************************************************************/ /* Set Packet Service State (internal) */ gboolean mm_iface_modem_3gpp_set_packet_service_state_finish (MMIfaceModem3gpp *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void set_packet_service_state_ready (MMIfaceModem3gpp *self, GAsyncResult *res, GTask *task) { GError *error = NULL; if (!MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->set_packet_service_state_finish (self, res, &error)) g_task_return_error (task, error); else g_task_return_boolean (task, TRUE); g_object_unref (task); } void mm_iface_modem_3gpp_set_packet_service_state (MMIfaceModem3gpp *self, MMModem3gppPacketServiceState packet_service_state, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; g_assert (packet_service_state == MM_MODEM_3GPP_PACKET_SERVICE_STATE_ATTACHED || packet_service_state == MM_MODEM_3GPP_PACKET_SERVICE_STATE_DETACHED); task = g_task_new (self, NULL, callback, user_data); if (!MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->set_packet_service_state || !MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->set_packet_service_state_finish) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "Explicit packet service attach/detach operation not supported"); g_object_unref (task); return; } MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->set_packet_service_state ( self, packet_service_state, (GAsyncReadyCallback)set_packet_service_state_ready, task); } /*****************************************************************************/ /* Set Packet Service State */ typedef struct { MMIfaceModem3gpp *self; MmGdbusModem3gpp *skeleton; GDBusMethodInvocation *invocation; MMModem3gppPacketServiceState packet_service_state; } HandlePacketServiceStateContext; static void handle_set_packet_service_state_context_free (HandlePacketServiceStateContext *ctx) { g_object_unref (ctx->invocation); g_object_unref (ctx->skeleton); g_object_unref (ctx->self); g_slice_free (HandlePacketServiceStateContext,ctx); } static void internal_set_packet_service_state_ready (MMIfaceModem3gpp *self, GAsyncResult *res, HandlePacketServiceStateContext *ctx) { GError *error = NULL; if (!mm_iface_modem_3gpp_set_packet_service_state_finish (self, res, &error)) { mm_obj_warn (self, "failed setting packet service state to '%s': %s", mm_modem_3gpp_packet_service_state_get_string (ctx->packet_service_state), error->message); mm_dbus_method_invocation_take_error (ctx->invocation, error); } else { mm_obj_info (self, "packet service state set to '%s'", mm_modem_3gpp_packet_service_state_get_string (ctx->packet_service_state)); mm_gdbus_modem3gpp_complete_set_packet_service_state (ctx->skeleton, ctx->invocation); } handle_set_packet_service_state_context_free (ctx); } static void set_packet_service_state_auth_ready (MMBaseModem *self, GAsyncResult *res, HandlePacketServiceStateContext *ctx) { GError *error = NULL; if (!mm_base_modem_authorize_finish (self, res, &error)) { mm_dbus_method_invocation_take_error (ctx->invocation, error); handle_set_packet_service_state_context_free (ctx); return; } if (mm_iface_modem_abort_invocation_if_state_not_reached (MM_IFACE_MODEM (self), ctx->invocation, MM_MODEM_STATE_ENABLED)) { handle_set_packet_service_state_context_free (ctx); return; } if ((ctx->packet_service_state != MM_MODEM_3GPP_PACKET_SERVICE_STATE_ATTACHED) && (ctx->packet_service_state != MM_MODEM_3GPP_PACKET_SERVICE_STATE_DETACHED)) { mm_dbus_method_invocation_return_error_literal (ctx->invocation, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS, "Invalid packet service state requested"); handle_set_packet_service_state_context_free (ctx); return; } mm_obj_info (self, "processing user request to set packet service state to '%s'...", mm_modem_3gpp_packet_service_state_get_string (ctx->packet_service_state)); mm_iface_modem_3gpp_set_packet_service_state (ctx->self, ctx->packet_service_state, (GAsyncReadyCallback)internal_set_packet_service_state_ready, ctx); } static gboolean handle_set_packet_service_state (MmGdbusModem3gpp *skeleton, GDBusMethodInvocation *invocation, MMModem3gppPacketServiceState packet_service_state, MMIfaceModem3gpp *self) { HandlePacketServiceStateContext *ctx; ctx = g_slice_new0 (HandlePacketServiceStateContext); ctx->skeleton = g_object_ref (skeleton); ctx->invocation = g_object_ref (invocation); ctx->self = g_object_ref (self); ctx->packet_service_state = packet_service_state; mm_base_modem_authorize (MM_BASE_MODEM (self), invocation, MM_AUTHORIZATION_DEVICE_CONTROL, (GAsyncReadyCallback)set_packet_service_state_auth_ready, ctx); return TRUE; } /*****************************************************************************/ typedef struct { MmGdbusModem3gpp *skeleton; GDBusMethodInvocation *invocation; MMIfaceModem3gpp *self; GVariant *dictionary; MMNr5gRegistrationSettings *settings; } HandleSetNr5gRegistrationSettingsContext; static void handle_set_nr5g_registration_settings_context_free (HandleSetNr5gRegistrationSettingsContext *ctx) { g_clear_object (&ctx->settings); g_variant_unref (ctx->dictionary); g_object_unref (ctx->skeleton); g_object_unref (ctx->invocation); g_object_unref (ctx->self); g_slice_free (HandleSetNr5gRegistrationSettingsContext, ctx); } static void after_set_load_nr5g_registration_settings_ready (MMIfaceModem3gpp *self, GAsyncResult *res, HandleSetNr5gRegistrationSettingsContext *ctx) { GError *error = NULL; g_autoptr(MMNr5gRegistrationSettings) new_settings = NULL; g_autoptr(GVariant) dictionary = NULL; new_settings = MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->load_nr5g_registration_settings_finish (self, res, &error); if (error) { mm_dbus_method_invocation_take_error (ctx->invocation, error); handle_set_nr5g_registration_settings_context_free (ctx); return; } mm_obj_info (self, "5GNR registration settings updated"); if (!mm_nr5g_registration_settings_cmp (new_settings, ctx->settings)) { mm_obj_info (self, "requested and reloaded 5GNR registration settings don't match"); mm_dbus_method_invocation_return_error_literal (ctx->invocation, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "5GNR registration settings were not updated"); handle_set_nr5g_registration_settings_context_free (ctx); return; } dictionary = mm_nr5g_registration_settings_get_dictionary (new_settings); mm_gdbus_modem3gpp_set_nr5g_registration_settings (ctx->skeleton, dictionary); mm_gdbus_modem3gpp_complete_set_nr5g_registration_settings (ctx->skeleton, ctx->invocation); handle_set_nr5g_registration_settings_context_free (ctx); } static void set_nr5g_registration_settings_ready (MMIfaceModem3gpp *self, GAsyncResult *res, HandleSetNr5gRegistrationSettingsContext *ctx) { GError *error = NULL; if (!MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->set_nr5g_registration_settings_finish (self, res, &error)) { mm_obj_warn (self, "failed setting 5GNR registration settings: %s", error->message); mm_dbus_method_invocation_take_error (ctx->invocation, error); handle_set_nr5g_registration_settings_context_free (ctx); return; } if (MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->load_nr5g_registration_settings && MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->load_nr5g_registration_settings_finish) { MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->load_nr5g_registration_settings ( self, (GAsyncReadyCallback)after_set_load_nr5g_registration_settings_ready, ctx); return; } /* Assume we're ok */ mm_obj_info (self, "5GNR registration settings updated"); mm_gdbus_modem3gpp_complete_set_nr5g_registration_settings (ctx->skeleton, ctx->invocation); handle_set_nr5g_registration_settings_context_free (ctx); } static void set_nr5g_registration_settings_auth_ready (MMBaseModem *self, GAsyncResult *res, HandleSetNr5gRegistrationSettingsContext *ctx) { GError *error = NULL; GVariant *old_dictionary; g_autoptr(MMNr5gRegistrationSettings) old_settings = NULL; MMModem3gppDrxCycle new_drx_cycle; MMModem3gppMicoMode new_mico_mode; if (!mm_base_modem_authorize_finish (self, res, &error)) { mm_dbus_method_invocation_take_error (ctx->invocation, error); handle_set_nr5g_registration_settings_context_free (ctx); return; } /* If 5GNR registration settings update is not implemented, report an error */ if (!MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->set_nr5g_registration_settings || !MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->set_nr5g_registration_settings_finish) { mm_dbus_method_invocation_return_error_literal (ctx->invocation, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "Operation not supported"); handle_set_nr5g_registration_settings_context_free (ctx); return; } ctx->settings = mm_nr5g_registration_settings_new_from_dictionary (ctx->dictionary, &error); if (!ctx->settings) { mm_dbus_method_invocation_take_error (ctx->invocation, error); handle_set_nr5g_registration_settings_context_free (ctx); return; } new_drx_cycle = mm_nr5g_registration_settings_get_drx_cycle (ctx->settings); if (new_drx_cycle == MM_MODEM_3GPP_DRX_CYCLE_UNSUPPORTED) { g_set_error (&error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS, "Invalid value for DRX cycle: %s", mm_modem_3gpp_drx_cycle_get_string (new_drx_cycle)); mm_dbus_method_invocation_take_error (ctx->invocation, error); handle_set_nr5g_registration_settings_context_free (ctx); return; } new_mico_mode = mm_nr5g_registration_settings_get_mico_mode (ctx->settings); if (new_mico_mode == MM_MODEM_3GPP_MICO_MODE_UNSUPPORTED) { g_set_error (&error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS, "Invalid value for MICO mode: %s", mm_modem_3gpp_mico_mode_get_string (new_mico_mode)); mm_dbus_method_invocation_take_error (ctx->invocation, error); handle_set_nr5g_registration_settings_context_free (ctx); return; } mm_obj_info (self, "processing user request to set 5GNR registration settings..."); old_dictionary = mm_gdbus_modem3gpp_get_nr5g_registration_settings (ctx->skeleton); if (old_dictionary) old_settings = mm_nr5g_registration_settings_new_from_dictionary (old_dictionary, NULL); if (old_settings && mm_nr5g_registration_settings_cmp (ctx->settings, old_settings)) { mm_obj_info (self, "skipped setting 5GNR registration settings: same configuration provided"); mm_gdbus_modem3gpp_complete_set_nr5g_registration_settings (ctx->skeleton, ctx->invocation); handle_set_nr5g_registration_settings_context_free (ctx); return; } MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->set_nr5g_registration_settings ( MM_IFACE_MODEM_3GPP (self), ctx->settings, (GAsyncReadyCallback)set_nr5g_registration_settings_ready, ctx); } static gboolean handle_set_nr5g_registration_settings (MmGdbusModem3gpp *skeleton, GDBusMethodInvocation *invocation, GVariant *dictionary, MMIfaceModem3gpp *self) { HandleSetNr5gRegistrationSettingsContext *ctx; ctx = g_slice_new0 (HandleSetNr5gRegistrationSettingsContext); ctx->skeleton = g_object_ref (skeleton); ctx->invocation = g_object_ref (invocation); ctx->self = g_object_ref (self); ctx->dictionary = g_variant_ref (dictionary); mm_base_modem_authorize (MM_BASE_MODEM (self), invocation, MM_AUTHORIZATION_DEVICE_CONTROL, (GAsyncReadyCallback)set_nr5g_registration_settings_auth_ready, ctx); return TRUE; } /*****************************************************************************/ gboolean mm_iface_modem_3gpp_run_registration_checks_finish (MMIfaceModem3gpp *self, GAsyncResult *res, GError **error) { g_assert (MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->run_registration_checks_finish != NULL); return MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->run_registration_checks_finish (self, res, error); } void mm_iface_modem_3gpp_run_registration_checks (MMIfaceModem3gpp *self, GAsyncReadyCallback callback, gpointer user_data) { gboolean is_cs_supported; gboolean is_ps_supported; gboolean is_eps_supported; gboolean is_5gs_supported; g_assert (MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->run_registration_checks != NULL); is_cs_supported = get_cs_network_supported (self); is_ps_supported = get_ps_network_supported (self); is_eps_supported = get_eps_network_supported (self); is_5gs_supported = get_5gs_network_supported (self); mm_obj_dbg (self, "running registration checks (CS: '%s', PS: '%s', EPS: '%s', 5GS: '%s')", is_cs_supported ? "yes" : "no", is_ps_supported ? "yes" : "no", is_eps_supported ? "yes" : "no", is_5gs_supported ? "yes" : "no"); MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->run_registration_checks (self, is_cs_supported, is_ps_supported, is_eps_supported, is_5gs_supported, callback, user_data); } /*****************************************************************************/ typedef struct { MmGdbusModem3gpp *skeleton; gboolean operator_code_loaded; gboolean operator_name_loaded; } ReloadCurrentRegistrationInfoContext; static void reload_current_registration_info_context_free (ReloadCurrentRegistrationInfoContext *ctx) { if (ctx->skeleton) g_object_unref (ctx->skeleton); g_slice_free (ReloadCurrentRegistrationInfoContext, ctx); } gboolean mm_iface_modem_3gpp_reload_current_registration_info_finish (MMIfaceModem3gpp *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void reload_current_registration_info_context_step (GTask *task); static void load_operator_name_ready (MMIfaceModem3gpp *self, GAsyncResult *res, GTask *task) { ReloadCurrentRegistrationInfoContext *ctx; GError *error = NULL; gchar *str; ctx = g_task_get_task_data (task); str = MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->load_operator_name_finish (self, res, &error); if (error) { mm_obj_warn (self, "couldn't load operator name: %s", error->message); g_error_free (error); } if (ctx->skeleton) mm_gdbus_modem3gpp_set_operator_name (ctx->skeleton, str); g_free (str); ctx->operator_name_loaded = TRUE; reload_current_registration_info_context_step (task); } static void load_operator_code_ready (MMIfaceModem3gpp *self, GAsyncResult *res, GTask *task) { ReloadCurrentRegistrationInfoContext *ctx; GError *error = NULL; gchar *str; ctx = g_task_get_task_data (task); str = MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->load_operator_code_finish (self, res, &error); if (error) { mm_obj_warn (self, "couldn't load operator code: %s", error->message); } else if (!mm_3gpp_parse_operator_id (str, NULL, NULL, NULL, &error)) { mm_obj_warn (self, "unexpected operator code string '%s': %s", str, error->message); g_clear_pointer (&str, g_free); } g_clear_error (&error); if (ctx->skeleton) mm_gdbus_modem3gpp_set_operator_code (ctx->skeleton, str); /* If we also implement the location interface, update the 3GPP location */ if (str && MM_IS_IFACE_MODEM_LOCATION (self)) mm_iface_modem_location_3gpp_update_operator_code (MM_IFACE_MODEM_LOCATION (self), str); g_free (str); ctx->operator_code_loaded = TRUE; reload_current_registration_info_context_step (task); } static void reload_current_registration_info_context_step (GTask *task) { MMIfaceModem3gpp *self; ReloadCurrentRegistrationInfoContext *ctx; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); if (!ctx->operator_code_loaded) { /* Launch operator code update */ MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->load_operator_code ( self, (GAsyncReadyCallback)load_operator_code_ready, task); return; } if (!ctx->operator_name_loaded) { /* Launch operator name update */ MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->load_operator_name ( self, (GAsyncReadyCallback)load_operator_name_ready, task); return; } /* If all are loaded, all done */ g_task_return_boolean (task, TRUE); g_object_unref (task); } void mm_iface_modem_3gpp_reload_current_registration_info (MMIfaceModem3gpp *self, GAsyncReadyCallback callback, gpointer user_data) { ReloadCurrentRegistrationInfoContext *ctx; GTask *task; ctx = g_slice_new0 (ReloadCurrentRegistrationInfoContext); task = g_task_new (self, NULL, callback, user_data); g_task_set_task_data (task, ctx, (GDestroyNotify)reload_current_registration_info_context_free); g_object_get (self, MM_IFACE_MODEM_3GPP_DBUS_SKELETON, &ctx->skeleton, NULL); if (!ctx->skeleton) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't get interface skeleton"); g_object_unref (task); return; } ctx->operator_code_loaded = !(MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->load_operator_code && MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->load_operator_code_finish); if (ctx->operator_code_loaded) { mm_gdbus_modem3gpp_set_operator_code (ctx->skeleton, NULL); if (MM_IS_IFACE_MODEM_LOCATION (self)) mm_iface_modem_location_3gpp_update_operator_code (MM_IFACE_MODEM_LOCATION (self), NULL); } ctx->operator_name_loaded = !(MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->load_operator_name && MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->load_operator_name_finish); if (ctx->operator_name_loaded) mm_gdbus_modem3gpp_set_operator_name (ctx->skeleton, NULL); reload_current_registration_info_context_step (task); } void mm_iface_modem_3gpp_clear_current_operator (MMIfaceModem3gpp *self) { MmGdbusModem3gpp *skeleton = NULL; g_object_get (self, MM_IFACE_MODEM_3GPP_DBUS_SKELETON, &skeleton, NULL); if (!skeleton) return; mm_gdbus_modem3gpp_set_operator_code (skeleton, NULL); mm_gdbus_modem3gpp_set_operator_name (skeleton, NULL); if (MM_IS_IFACE_MODEM_LOCATION (self)) mm_iface_modem_location_3gpp_update_operator_code (MM_IFACE_MODEM_LOCATION (self), NULL); } /*****************************************************************************/ void mm_iface_modem_3gpp_update_access_technologies (MMIfaceModem3gpp *self, MMModemAccessTechnology access_tech) { Private *priv; MMModem3gppRegistrationState state; priv = get_private (self); if (!priv->iface_enabled) return; g_object_get (self, MM_IFACE_MODEM_3GPP_REGISTRATION_STATE, &state, NULL); /* Even if registration state didn't change, report access technology, * but only if something valid to report */ if (mm_modem_3gpp_registration_state_is_registered (state) || priv->reloading_registration_info) { if (access_tech != MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN) mm_iface_modem_update_access_technologies (MM_IFACE_MODEM (self), access_tech, MM_IFACE_MODEM_3GPP_ALL_ACCESS_TECHNOLOGIES_MASK); } else mm_iface_modem_update_access_technologies (MM_IFACE_MODEM (self), MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN, MM_IFACE_MODEM_3GPP_ALL_ACCESS_TECHNOLOGIES_MASK); } void mm_iface_modem_3gpp_update_location (MMIfaceModem3gpp *self, gulong location_area_code, gulong tracking_area_code, gulong cell_id) { Private *priv; MMModem3gppRegistrationState state; priv = get_private (self); if (!priv->iface_enabled) return; if (!MM_IS_IFACE_MODEM_LOCATION (self)) return; g_object_get (self, MM_IFACE_MODEM_3GPP_REGISTRATION_STATE, &state, NULL); /* Even if registration state didn't change, report access technology or * location updates, but only if something valid to report. For the case * where we're registering (loading current registration info after a state * change to registered), we also allow LAC/CID updates. */ if (mm_modem_3gpp_registration_state_is_registered (state) || priv->reloading_registration_info) { if (location_area_code || tracking_area_code || cell_id) mm_iface_modem_location_3gpp_update_lac_tac_ci (MM_IFACE_MODEM_LOCATION (self), location_area_code, tracking_area_code, cell_id); } else mm_iface_modem_location_3gpp_clear (MM_IFACE_MODEM_LOCATION (self)); } /*****************************************************************************/ static void update_packet_service_state (MMIfaceModem3gpp *self, MMModem3gppPacketServiceState new_state); static void update_registration_reload_current_registration_info_ready (MMIfaceModem3gpp *self, GAsyncResult *res, gpointer user_data) { Private *priv; MMModem3gppRegistrationState new_state; priv = get_private (self); if (!priv->iface_enabled) return; new_state = GPOINTER_TO_UINT (user_data); /* Update packet service state if we don't support external updates */ if (!priv->packet_service_state_update_supported) update_packet_service_state (self, get_consolidated_packet_service_state (self)); mm_obj_msg (self, "3GPP registration state changed (registering -> %s)", mm_modem_3gpp_registration_state_get_string (new_state)); mm_obj_info (self, "consolidated registration state: cs '%s', ps '%s', eps '%s', 5gs '%s' --> '%s'", mm_modem_3gpp_registration_state_get_string (priv->state_cs), mm_modem_3gpp_registration_state_get_string (priv->state_ps), mm_modem_3gpp_registration_state_get_string (priv->state_eps), mm_modem_3gpp_registration_state_get_string (priv->state_5gs), mm_modem_3gpp_registration_state_get_string (new_state)); /* The properties in the interface are bound to the properties * in the skeleton, so just updating here is enough */ g_object_set (self, MM_IFACE_MODEM_3GPP_REGISTRATION_STATE, new_state, NULL); mm_iface_modem_update_subsystem_state (MM_IFACE_MODEM (self), SUBSYSTEM_3GPP, MM_MODEM_STATE_REGISTERED, MM_MODEM_STATE_CHANGE_REASON_UNKNOWN); priv->reloading_registration_info = FALSE; } static void update_non_registered_state (MMIfaceModem3gpp *self, MMModem3gppRegistrationState old_state, MMModem3gppRegistrationState new_state) { Private *priv; priv = get_private (self); if (!priv->iface_enabled) return; /* Not registered neither in home nor roaming network */ mm_iface_modem_3gpp_clear_current_operator (self); /* No packet service if we're not registered. This change is done * also when the device itself supports reporting the packet service * state updates. */ update_packet_service_state (self, MM_MODEM_3GPP_PACKET_SERVICE_STATE_DETACHED); /* The property in the interface is bound to the property * in the skeleton, so just updating here is enough */ g_object_set (self, MM_IFACE_MODEM_3GPP_REGISTRATION_STATE, new_state, NULL); mm_iface_modem_update_subsystem_state ( MM_IFACE_MODEM (self), SUBSYSTEM_3GPP, (new_state == MM_MODEM_3GPP_REGISTRATION_STATE_SEARCHING ? MM_MODEM_STATE_SEARCHING : MM_MODEM_STATE_ENABLED), MM_MODEM_STATE_CHANGE_REASON_UNKNOWN); } static void update_registration_state (MMIfaceModem3gpp *self, MMModem3gppRegistrationState new_state) { Private *priv; MMModem3gppRegistrationState old_state = MM_MODEM_3GPP_REGISTRATION_STATE_UNKNOWN; priv = get_private (self); if (!priv->iface_enabled) return; g_object_get (self, MM_IFACE_MODEM_3GPP_REGISTRATION_STATE, &old_state, NULL); /* Only set new state if different */ if (new_state == old_state) { MMModem3gppPacketServiceState old_packet_service_state = MM_MODEM_3GPP_PACKET_SERVICE_STATE_UNKNOWN; /* If packet service updates are expected, we can ignore the packet service state as that * info won't be used to build a consolidated packet service state */ if (priv->packet_service_state_update_supported) return; /* If packet service updates are not expected, also check whether there are changes * in the consolidate packet service state */ g_object_get (self, MM_IFACE_MODEM_3GPP_PACKET_SERVICE_STATE, &old_packet_service_state, NULL); if (old_packet_service_state == get_consolidated_packet_service_state (self)) return; } if (mm_modem_3gpp_registration_state_is_registered (new_state)) { MMModemState modem_state; /* If already reloading registration info, skip it */ if (priv->reloading_registration_info) return; /* If the modem isn't already enabled, this registration state update * could be due to a previously scheduled initial registration check * when the modem was being enabled. We need to ignore it as otherwise * it may cause an incorrect transition of the registration state and * modem state when the modem is being disabled or still going through * enable steps */ modem_state = MM_MODEM_STATE_UNKNOWN; g_object_get (self, MM_IFACE_MODEM_STATE, &modem_state, NULL); if (modem_state < MM_MODEM_STATE_ENABLED) { mm_obj_dbg (self, "3GPP registration state change ignored as modem isn't enabled"); return; } mm_obj_msg (self, "3GPP registration state changed (%s -> registering)", mm_modem_3gpp_registration_state_get_string (old_state)); /* Reload current registration info. ONLY update the state to REGISTERED * after having loaded operator code/name/subscription state */ priv->reloading_registration_info = TRUE; mm_iface_modem_3gpp_reload_current_registration_info ( self, (GAsyncReadyCallback)update_registration_reload_current_registration_info_ready, GUINT_TO_POINTER (new_state)); return; } mm_obj_msg (self, "3GPP registration state changed (%s -> %s)", mm_modem_3gpp_registration_state_get_string (old_state), mm_modem_3gpp_registration_state_get_string (new_state)); mm_obj_info (self, "consolidated registration state: cs '%s', ps '%s', eps '%s', 5gs '%s' --> '%s'", mm_modem_3gpp_registration_state_get_string (priv->state_cs), mm_modem_3gpp_registration_state_get_string (priv->state_ps), mm_modem_3gpp_registration_state_get_string (priv->state_eps), mm_modem_3gpp_registration_state_get_string (priv->state_5gs), mm_modem_3gpp_registration_state_get_string (new_state)); update_non_registered_state (self, old_state, new_state); } #define UPDATE_REGISTRATION_STATE(domain) \ void \ mm_iface_modem_3gpp_update_##domain##_registration_state (MMIfaceModem3gpp *self, \ MMModem3gppRegistrationState state, \ gboolean deferred) \ { \ Private *priv; \ \ if (!get_##domain##_network_supported (self)) \ return; \ \ priv = get_private (self); \ if (!priv->iface_enabled) \ return; \ priv->state_##domain = state; \ \ if (!deferred) \ mm_iface_modem_3gpp_apply_deferred_registration_state (self); \ } UPDATE_REGISTRATION_STATE (cs) UPDATE_REGISTRATION_STATE (ps) UPDATE_REGISTRATION_STATE (eps) UPDATE_REGISTRATION_STATE (5gs) void mm_iface_modem_3gpp_apply_deferred_registration_state (MMIfaceModem3gpp *self) { update_registration_state (self, get_consolidated_reg_state (self)); } /*****************************************************************************/ /* Packet service state as reported by the device */ static void update_packet_service_state (MMIfaceModem3gpp *self, MMModem3gppPacketServiceState new_state) { MMModem3gppPacketServiceState old_state = MM_MODEM_3GPP_PACKET_SERVICE_STATE_UNKNOWN; Private *priv; priv = get_private (self); if (!priv->iface_enabled) return; g_object_get (self, MM_IFACE_MODEM_3GPP_PACKET_SERVICE_STATE, &old_state, NULL); /* Only set new state if different */ if (old_state == new_state) return; mm_obj_msg (self, "3GPP packet service state changed (%s -> %s)", mm_modem_3gpp_packet_service_state_get_string (old_state), mm_modem_3gpp_packet_service_state_get_string (new_state)); /* The properties in the interface are bound to the properties * in the skeleton, so just updating here is enough */ g_object_set (self, MM_IFACE_MODEM_3GPP_PACKET_SERVICE_STATE, new_state, NULL); } void mm_iface_modem_3gpp_update_packet_service_state (MMIfaceModem3gpp *self, MMModem3gppPacketServiceState new_state) { Private *priv; priv = get_private (self); priv->packet_service_state_update_supported = TRUE; update_packet_service_state (self, new_state); } /*****************************************************************************/ /* Periodic registration checks */ #define REGISTRATION_CHECK_TIMEOUT_SEC 30 static void periodic_registration_checks_ready (MMIfaceModem3gpp *self, GAsyncResult *res) { Private *priv; GError *error = NULL; priv = get_private (self); mm_iface_modem_3gpp_run_registration_checks_finish (self, res, &error); if (error) { mm_obj_dbg (self, "couldn't refresh 3GPP registration status: %s", error->message); g_error_free (error); } priv->check_running = FALSE; } static gboolean periodic_registration_check (MMIfaceModem3gpp *self) { Private *priv; priv = get_private (self); /* Only launch a new one if not one running already */ if (!priv->check_running) { priv->check_running = TRUE; mm_iface_modem_3gpp_run_registration_checks ( self, (GAsyncReadyCallback)periodic_registration_checks_ready, NULL); } return G_SOURCE_CONTINUE; } static void periodic_registration_check_disable (MMIfaceModem3gpp *self) { Private *priv; priv = get_private (self); /* Do nothing if already disabled */ if (!priv->check_timeout_source) return; g_source_remove (priv->check_timeout_source); priv->check_timeout_source = 0; mm_obj_dbg (self, "periodic 3GPP registration checks disabled"); } static void periodic_registration_check_enable (MMIfaceModem3gpp *self) { Private *priv; priv = get_private (self); /* Do nothing if already enabled */ if (priv->check_timeout_source) return; /* Create context and keep it as object data */ mm_obj_dbg (self, "periodic 3GPP registration checks enabled"); priv->check_timeout_source = g_timeout_add_seconds (REGISTRATION_CHECK_TIMEOUT_SEC, (GSourceFunc)periodic_registration_check, self); } /*****************************************************************************/ void mm_iface_modem_3gpp_update_pco_list (MMIfaceModem3gpp *self, const GList *pco_list) { MmGdbusModem3gpp *skeleton = NULL; GVariantBuilder builder; GVariant *variant; const GList *iter; Private *priv; priv = get_private (self); if (!priv->iface_enabled) return; g_object_get (self, MM_IFACE_MODEM_3GPP_DBUS_SKELETON, &skeleton, NULL); if (!skeleton) return; g_variant_builder_init (&builder, G_VARIANT_TYPE ("a(ubay)")); for (iter = pco_list; iter; iter = g_list_next (iter)) { g_autoptr(GVariant) pco_variant = NULL; pco_variant = mm_pco_to_variant (MM_PCO (iter->data)); g_variant_builder_add_value (&builder, pco_variant); } variant = g_variant_ref_sink (g_variant_builder_end (&builder)); mm_gdbus_modem3gpp_set_pco (skeleton, variant); g_variant_unref (variant); g_object_unref (skeleton); } /*****************************************************************************/ void mm_iface_modem_3gpp_update_initial_eps_bearer (MMIfaceModem3gpp *self, MMBearerProperties *properties) { g_autoptr(MmGdbusModem3gppSkeleton) skeleton = NULL; g_autoptr(MMBaseBearer) old_bearer = NULL; g_autoptr(MMBaseBearer) new_bearer = NULL; g_object_get (self, MM_IFACE_MODEM_3GPP_DBUS_SKELETON, &skeleton, MM_IFACE_MODEM_3GPP_INITIAL_EPS_BEARER, &old_bearer, NULL); g_assert (skeleton); /* skip update? */ if ((!old_bearer && !properties) || (old_bearer && properties && mm_bearer_properties_cmp (properties, mm_base_bearer_peek_config (MM_BASE_BEARER (old_bearer)), MM_BEARER_PROPERTIES_CMP_FLAGS_EPS))) return; if (!properties) { mm_obj_dbg (self, "clearing initial EPS bearer..."); g_object_set (self, MM_IFACE_MODEM_3GPP_INITIAL_EPS_BEARER, NULL, NULL); mm_gdbus_modem3gpp_set_initial_eps_bearer (MM_GDBUS_MODEM3GPP (skeleton), NULL); return; } mm_obj_dbg (self, "updating initial EPS bearer..."); g_assert (MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->create_initial_eps_bearer); new_bearer = MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->create_initial_eps_bearer (self, properties); g_object_set (self, MM_IFACE_MODEM_3GPP_INITIAL_EPS_BEARER, new_bearer, NULL); mm_gdbus_modem3gpp_set_initial_eps_bearer (MM_GDBUS_MODEM3GPP (skeleton), mm_base_bearer_get_path (new_bearer)); } static void reload_initial_eps_bearer_ready (MMIfaceModem3gpp *self, GAsyncResult *res) { g_autoptr(MMBearerProperties) properties = NULL; g_autoptr(GError) error = NULL; properties = MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->load_initial_eps_bearer_finish (self, res, &error); if (!properties) { mm_obj_dbg (self, "couldn't load initial default bearer properties: %s", error->message); return; } mm_iface_modem_3gpp_update_initial_eps_bearer (self, properties); } void mm_iface_modem_3gpp_reload_initial_eps_bearer (MMIfaceModem3gpp *self) { if (get_eps_network_supported (self) && MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->load_initial_eps_bearer && MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->load_initial_eps_bearer_finish) { MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->load_initial_eps_bearer ( self, (GAsyncReadyCallback)reload_initial_eps_bearer_ready, NULL); } } /*****************************************************************************/ typedef struct _DisablingContext DisablingContext; static void interface_disabling_step (GTask *task); typedef enum { DISABLING_STEP_FIRST, DISABLING_STEP_INITIAL_EPS_BEARER, DISABLING_STEP_PERIODIC_REGISTRATION_CHECKS, DISABLING_STEP_DISABLE_UNSOLICITED_REGISTRATION_EVENTS, DISABLING_STEP_CLEANUP_UNSOLICITED_REGISTRATION_EVENTS, DISABLING_STEP_CLEANUP_UNSOLICITED_EVENTS, DISABLING_STEP_DISABLE_UNSOLICITED_EVENTS, DISABLING_STEP_REGISTRATION_STATE, DISABLING_STEP_LAST } DisablingStep; struct _DisablingContext { DisablingStep step; MmGdbusModem *skeleton; }; static void disabling_context_free (DisablingContext *ctx) { g_clear_object (&ctx->skeleton); g_slice_free (DisablingContext, ctx); } gboolean mm_iface_modem_3gpp_disable_finish (MMIfaceModem3gpp *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } #undef VOID_REPLY_READY_FN #define VOID_REPLY_READY_FN(NAME,DISPLAY) \ static void \ NAME##_ready (MMIfaceModem3gpp *self, \ GAsyncResult *res, \ GTask *task) \ { \ DisablingContext *ctx; \ g_autoptr(GError) error = NULL; \ \ MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->NAME##_finish (self, res, &error); \ if (error) \ mm_obj_dbg (self, "couldn't %s: %s", DISPLAY, error->message); \ \ /* Go on to next step */ \ ctx = g_task_get_task_data (task); \ ctx->step++; \ interface_disabling_step (task); \ } VOID_REPLY_READY_FN (cleanup_unsolicited_events, "cleanup unsolicited events") VOID_REPLY_READY_FN (disable_unsolicited_events, "disable unsolicited events") VOID_REPLY_READY_FN (cleanup_unsolicited_registration_events, "cleanup unsolicited registration events") VOID_REPLY_READY_FN (disable_unsolicited_registration_events, "disable unsolicited registration events") static void interface_disabling_step (GTask *task) { MMIfaceModem3gpp *self; Private *priv; DisablingContext *ctx; self = g_task_get_source_object (task); priv = get_private (self); ctx = g_task_get_task_data (task); switch (ctx->step) { case DISABLING_STEP_FIRST: ctx->step++; /* fall through */ case DISABLING_STEP_INITIAL_EPS_BEARER: mm_iface_modem_3gpp_update_initial_eps_bearer (self, NULL); ctx->step++; /* fall through */ case DISABLING_STEP_PERIODIC_REGISTRATION_CHECKS: /* Disable periodic registration checks, if they were set */ periodic_registration_check_disable (self); ctx->step++; /* fall through */ case DISABLING_STEP_DISABLE_UNSOLICITED_REGISTRATION_EVENTS: if (MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->disable_unsolicited_registration_events && MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->disable_unsolicited_registration_events_finish) { MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->disable_unsolicited_registration_events ( self, get_cs_network_supported (self), get_ps_network_supported (self), get_eps_network_supported (self), (GAsyncReadyCallback)disable_unsolicited_registration_events_ready, task); return; } ctx->step++; /* fall through */ case DISABLING_STEP_CLEANUP_UNSOLICITED_REGISTRATION_EVENTS: if (MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->cleanup_unsolicited_registration_events && MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->cleanup_unsolicited_registration_events_finish) { MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->cleanup_unsolicited_registration_events ( self, (GAsyncReadyCallback)cleanup_unsolicited_registration_events_ready, task); return; } ctx->step++; /* fall through */ case DISABLING_STEP_CLEANUP_UNSOLICITED_EVENTS: if (MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->cleanup_unsolicited_events && MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->cleanup_unsolicited_events_finish) { MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->cleanup_unsolicited_events ( self, (GAsyncReadyCallback)cleanup_unsolicited_events_ready, task); return; } ctx->step++; /* fall through */ case DISABLING_STEP_DISABLE_UNSOLICITED_EVENTS: if (MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->disable_unsolicited_events && MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->disable_unsolicited_events_finish) { MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->disable_unsolicited_events ( self, (GAsyncReadyCallback)disable_unsolicited_events_ready, task); return; } ctx->step++; /* fall through */ case DISABLING_STEP_REGISTRATION_STATE: update_packet_service_state (self, MM_MODEM_3GPP_PACKET_SERVICE_STATE_UNKNOWN); update_registration_state (self, MM_MODEM_3GPP_REGISTRATION_STATE_UNKNOWN); mm_iface_modem_3gpp_update_access_technologies (self, MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN); mm_iface_modem_3gpp_update_location (self, 0, 0, 0); ctx->step++; /* fall through */ case DISABLING_STEP_LAST: /* Interface state is assumed enabled until the very end of the disabling sequence, * so that updates are taken into account and not ignored. */ priv->iface_enabled = FALSE; /* We are done without errors! */ g_task_return_boolean (task, TRUE); g_object_unref (task); return; default: g_assert_not_reached (); } g_assert_not_reached (); } void mm_iface_modem_3gpp_disable (MMIfaceModem3gpp *self, GAsyncReadyCallback callback, gpointer user_data) { DisablingContext *ctx; GTask *task; ctx = g_slice_new0 (DisablingContext); ctx->step = DISABLING_STEP_FIRST; task = g_task_new (self, NULL, callback, user_data); g_task_set_task_data (task, ctx, (GDestroyNotify)disabling_context_free); g_object_get (self, MM_IFACE_MODEM_3GPP_DBUS_SKELETON, &ctx->skeleton, NULL); if (!ctx->skeleton) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't get interface skeleton"); g_object_unref (task); return; } interface_disabling_step (task); } /*****************************************************************************/ typedef struct _EnablingContext EnablingContext; static void interface_enabling_step (GTask *task); typedef enum { ENABLING_STEP_FIRST, ENABLING_STEP_SETUP_UNSOLICITED_EVENTS, ENABLING_STEP_ENABLE_UNSOLICITED_EVENTS, ENABLING_STEP_SETUP_UNSOLICITED_REGISTRATION_EVENTS, ENABLING_STEP_ENABLE_UNSOLICITED_REGISTRATION_EVENTS, ENABLING_STEP_INITIAL_EPS_BEARER, ENABLING_STEP_LAST } EnablingStep; struct _EnablingContext { EnablingStep step; MmGdbusModem3gpp *skeleton; }; static void enabling_context_free (EnablingContext *ctx) { g_clear_object (&ctx->skeleton); g_slice_free (EnablingContext, ctx); } gboolean mm_iface_modem_3gpp_enable_finish (MMIfaceModem3gpp *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void setup_unsolicited_events_ready (MMIfaceModem3gpp *self, GAsyncResult *res, GTask *task) { EnablingContext *ctx; g_autoptr(GError) error = NULL; ctx = g_task_get_task_data (task); MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->setup_unsolicited_events_finish (self, res, &error); if (error) { mm_obj_dbg (self, "setting up unsolicited events failed: %s", error->message); /* If we get an error setting up unsolicited events, don't even bother trying to * enable them. */ ctx->step = ENABLING_STEP_ENABLE_UNSOLICITED_EVENTS + 1; interface_enabling_step (task); return; } /* Go on to next step */ ctx->step++; interface_enabling_step (task); } static void enable_unsolicited_events_ready (MMIfaceModem3gpp *self, GAsyncResult *res, GTask *task) { EnablingContext *ctx; g_autoptr(GError) error = NULL; MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->enable_unsolicited_events_finish (self, res, &error); if (error) mm_obj_dbg (self, "enabling unsolicited events failed: %s", error->message); /* Go on to next step */ ctx = g_task_get_task_data (task); ctx->step++; interface_enabling_step (task); } static void setup_unsolicited_registration_events_ready (MMIfaceModem3gpp *self, GAsyncResult *res, GTask *task) { EnablingContext *ctx; g_autoptr(GError) error = NULL; ctx = g_task_get_task_data (task); MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->setup_unsolicited_registration_events_finish (self, res, &error); if (error) { mm_obj_dbg (self, "setting up unsolicited registration events failed: %s", error->message); /* If error, setup periodic registration checks */ periodic_registration_check_enable (self); /* If we get an error setting up unsolicited events, don't even bother trying to * enable them. */ ctx->step = ENABLING_STEP_ENABLE_UNSOLICITED_REGISTRATION_EVENTS + 1; interface_enabling_step (task); return; } /* Go on to next step */ ctx->step++; interface_enabling_step (task); } static void enable_unsolicited_registration_events_ready (MMIfaceModem3gpp *self, GAsyncResult *res, GTask *task) { EnablingContext *ctx; g_autoptr(GError) error = NULL; MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->enable_unsolicited_registration_events_finish (self, res, &error); if (error) { mm_obj_dbg (self, "enabling unsolicited registration events failed: %s", error->message); /* If error, setup periodic registration checks */ periodic_registration_check_enable (self); } /* Go on to next step */ ctx = g_task_get_task_data (task); ctx->step++; interface_enabling_step (task); } static void load_initial_eps_bearer_ready (MMIfaceModem3gpp *self, GAsyncResult *res, GTask *task) { EnablingContext *ctx; g_autoptr(MMBearerProperties) properties = NULL; g_autoptr(GError) error = NULL; ctx = g_task_get_task_data (task); properties = MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->load_initial_eps_bearer_finish (self, res, &error); if (!properties) mm_obj_dbg (self, "couldn't load initial default bearer properties: %s", error->message); else mm_iface_modem_3gpp_update_initial_eps_bearer (self, properties); /* Go on to next step */ ctx->step++; interface_enabling_step (task); } static void interface_enabling_step (GTask *task) { MMIfaceModem3gpp *self; Private *priv; EnablingContext *ctx; self = g_task_get_source_object (task); priv = get_private (self); ctx = g_task_get_task_data (task); /* Don't run new steps if we're cancelled */ if (g_task_return_error_if_cancelled (task)) { priv->iface_enabled = FALSE; g_object_unref (task); return; } switch (ctx->step) { case ENABLING_STEP_FIRST: /* Interface state is assumed enabled from the very beginning of the enabling sequence, * so that updates are taken into account and not ignored. */ priv->iface_enabled = TRUE; ctx->step++; /* fall through */ case ENABLING_STEP_SETUP_UNSOLICITED_EVENTS: if (MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->setup_unsolicited_events && MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->setup_unsolicited_events_finish) { MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->setup_unsolicited_events ( self, (GAsyncReadyCallback)setup_unsolicited_events_ready, task); return; } ctx->step++; /* fall through */ case ENABLING_STEP_ENABLE_UNSOLICITED_EVENTS: if (MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->enable_unsolicited_events && MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->enable_unsolicited_events_finish) { MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->enable_unsolicited_events ( self, (GAsyncReadyCallback)enable_unsolicited_events_ready, task); return; } ctx->step++; /* fall through */ case ENABLING_STEP_SETUP_UNSOLICITED_REGISTRATION_EVENTS: if (MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->setup_unsolicited_registration_events && MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->setup_unsolicited_registration_events_finish) { MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->setup_unsolicited_registration_events ( self, (GAsyncReadyCallback)setup_unsolicited_registration_events_ready, task); return; } ctx->step++; /* fall through */ case ENABLING_STEP_ENABLE_UNSOLICITED_REGISTRATION_EVENTS: if (MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->enable_unsolicited_registration_events && MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->enable_unsolicited_registration_events_finish) { MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->enable_unsolicited_registration_events ( self, get_cs_network_supported (self), get_ps_network_supported (self), get_eps_network_supported (self), (GAsyncReadyCallback)enable_unsolicited_registration_events_ready, task); return; } ctx->step++; /* fall through */ case ENABLING_STEP_INITIAL_EPS_BEARER: if (get_eps_network_supported (self) && MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->load_initial_eps_bearer && MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->load_initial_eps_bearer_finish) { MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->load_initial_eps_bearer ( self, (GAsyncReadyCallback)load_initial_eps_bearer_ready, task); return; } ctx->step++; /* fall through */ case ENABLING_STEP_LAST: /* We are done without errors! */ g_task_return_boolean (task, TRUE); g_object_unref (task); return; default: break; } g_assert_not_reached (); } void mm_iface_modem_3gpp_enable (MMIfaceModem3gpp *self, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { EnablingContext *ctx; GTask *task; ctx = g_slice_new0 (EnablingContext); ctx->step = ENABLING_STEP_FIRST; task = g_task_new (self, cancellable, callback, user_data); g_task_set_task_data (task, ctx, (GDestroyNotify)enabling_context_free); g_object_get (self, MM_IFACE_MODEM_3GPP_DBUS_SKELETON, &ctx->skeleton, NULL); if (!ctx->skeleton) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't get interface skeleton"); g_object_unref (task); return; } interface_enabling_step (task); } /*****************************************************************************/ #if defined WITH_SUSPEND_RESUME typedef struct _SyncingContext SyncingContext; static void interface_syncing_step (GTask *task); typedef enum { SYNCING_STEP_FIRST, SYNCING_STEP_REFRESH_3GPP_REGISTRATION, SYNCING_STEP_REFRESH_EPS_BEARER, SYNCING_STEP_LAST } SyncingStep; struct _SyncingContext { SyncingStep step; }; gboolean mm_iface_modem_3gpp_sync_finish (MMIfaceModem3gpp *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void sync_eps_bearer_ready (MMIfaceModem3gpp *self, GAsyncResult *res, GTask *task) { SyncingContext *ctx; g_autoptr(MMBearerProperties) properties = NULL; g_autoptr(GError) error = NULL; ctx = g_task_get_task_data (task); properties = MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->load_initial_eps_bearer_finish (self, res, &error); if (!properties) mm_obj_dbg (self, "couldn't refresh initial EPS bearer status: %s", error->message); else mm_iface_modem_3gpp_update_initial_eps_bearer (self, properties); /* Go on to next step */ ctx->step++; interface_syncing_step (task); } static void sync_eps_bearer (MMIfaceModem3gpp *self, GAsyncReadyCallback callback, GTask *task) { SyncingContext *ctx; /* Refresh EPS bearer if supported */ if (get_eps_network_supported (self) && MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->load_initial_eps_bearer && MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->load_initial_eps_bearer_finish) { MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->load_initial_eps_bearer ( self, callback, task); return; } /* If EPS is unsupported, just go to the next step */ ctx = g_task_get_task_data (task); ctx->step++; interface_syncing_step (task); } static void sync_registration_ready (MMIfaceModem3gpp *self, GAsyncResult *res, GTask *task) { SyncingContext *ctx; g_autoptr (GError) error = NULL; ctx = g_task_get_task_data (task); if (!mm_iface_modem_3gpp_run_registration_checks_finish (self, res, &error)) mm_obj_dbg (self, "couldn't synchronize 3GPP registration: %s", error->message); /* Go on to next step */ ctx->step++; interface_syncing_step(task); } static void interface_syncing_step (GTask *task) { MMIfaceModem3gpp *self; SyncingContext *ctx; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); switch (ctx->step) { case SYNCING_STEP_FIRST: ctx->step++; /* fall through */ case SYNCING_STEP_REFRESH_3GPP_REGISTRATION: /* * Refresh registration info to verify that the modem is still registered. * Wait until registration checks are complete before going to the next step. */ mm_iface_modem_3gpp_run_registration_checks ( self, (GAsyncReadyCallback)sync_registration_ready, task); return; case SYNCING_STEP_REFRESH_EPS_BEARER: /* * Refresh EPS bearer and wait until complete. * We want to make sure that the modem is fully enabled again * when we refresh the mobile data connection bearers. */ sync_eps_bearer ( self, (GAsyncReadyCallback)sync_eps_bearer_ready, task); return; case SYNCING_STEP_LAST: /* We are done without errors! */ g_task_return_boolean (task, TRUE); g_object_unref (task); return; default: break; } g_assert_not_reached (); } void mm_iface_modem_3gpp_sync (MMIfaceModem3gpp *self, GAsyncReadyCallback callback, gpointer user_data) { SyncingContext *ctx; GTask *task; /* Create SyncingContext */ ctx = g_new0 (SyncingContext, 1); ctx->step = SYNCING_STEP_FIRST; /* Create sync steps task and execute it */ task = g_task_new (self, NULL, callback, user_data); g_task_set_task_data (task, ctx, (GDestroyNotify)g_free); interface_syncing_step (task); } #endif /*****************************************************************************/ typedef struct _InitializationContext InitializationContext; static void interface_initialization_step (GTask *task); typedef enum { INITIALIZATION_STEP_FIRST, INITIALIZATION_STEP_ENABLED_FACILITY_LOCKS, INITIALIZATION_STEP_IMEI, INITIALIZATION_STEP_TEST_LOCKED_OR_FAILED, INITIALIZATION_STEP_EPS_UE_MODE_OPERATION, INITIALIZATION_STEP_EPS_INITIAL_BEARER_SETTINGS, INITIALIZATION_STEP_NR5G_REGISTRATION_SETTINGS, INITIALIZATION_STEP_CONNECT_SIGNALS, INITIALIZATION_STEP_LAST } InitializationStep; struct _InitializationContext { MmGdbusModem3gpp *skeleton; InitializationStep step; }; static void initialization_context_free (InitializationContext *ctx) { g_clear_object (&ctx->skeleton); g_slice_free (InitializationContext, ctx); } static void sim_pin_lock_enabled_cb (MMBaseSim *self, gboolean enabled, MmGdbusModem3gpp *skeleton) { MMModem3gppFacility facilities; facilities = mm_gdbus_modem3gpp_get_enabled_facility_locks (skeleton); if (enabled) facilities |= MM_MODEM_3GPP_FACILITY_SIM; else facilities &= ~MM_MODEM_3GPP_FACILITY_SIM; mm_gdbus_modem3gpp_set_enabled_facility_locks (skeleton, facilities); } static void load_nr5g_registration_settings_ready (MMIfaceModem3gpp *self, GAsyncResult *res, GTask *task) { InitializationContext *ctx; g_autoptr(GError) error = NULL; g_autoptr(MMNr5gRegistrationSettings) settings = NULL; ctx = g_task_get_task_data (task); settings = MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->load_nr5g_registration_settings_finish (self, res, &error); if (!settings) { mm_obj_dbg (self, "couldn't load 5GNR registration settings: %s", error->message); } else { g_autoptr(GVariant) dictionary = NULL; dictionary = mm_nr5g_registration_settings_get_dictionary (settings); mm_gdbus_modem3gpp_set_nr5g_registration_settings (ctx->skeleton, dictionary); } /* Go on to next step */ ctx->step++; interface_initialization_step (task); } static void load_initial_eps_bearer_settings_ready (MMIfaceModem3gpp *self, GAsyncResult *res, GTask *task) { InitializationContext *ctx; g_autoptr(MMBearerProperties) config = NULL; g_autoptr(GError) error = NULL; ctx = g_task_get_task_data (task); config = MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->load_initial_eps_bearer_settings_finish (self, res, &error); if (!config) mm_obj_dbg (self, "couldn't load initial EPS bearer settings: %s", error->message); else { g_autoptr(GVariant) dictionary = NULL; dictionary = mm_bearer_properties_get_dictionary (config); mm_gdbus_modem3gpp_set_initial_eps_bearer_settings (ctx->skeleton, dictionary); } /* Go on to next step */ ctx->step++; interface_initialization_step (task); } static void load_eps_ue_mode_operation_ready (MMIfaceModem3gpp *self, GAsyncResult *res, GTask *task) { InitializationContext *ctx; MMModem3gppEpsUeModeOperation uemode; g_autoptr(GError) error = NULL; ctx = g_task_get_task_data (task); uemode = MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->load_eps_ue_mode_operation_finish (self, res, &error); mm_gdbus_modem3gpp_set_eps_ue_mode_operation (ctx->skeleton, uemode); if (error) mm_obj_dbg (self, "couldn't load UE mode of operation for EPS: %s", error->message); /* Go on to next step */ ctx->step++; interface_initialization_step (task); } static void load_enabled_facility_locks_ready (MMIfaceModem3gpp *self, GAsyncResult *res, GTask *task) { InitializationContext *ctx; MMModem3gppFacility facilities; g_autoptr(GError) error = NULL; ctx = g_task_get_task_data (task); facilities = MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->load_enabled_facility_locks_finish (self, res, &error); mm_gdbus_modem3gpp_set_enabled_facility_locks (ctx->skeleton, facilities); if (error) mm_obj_dbg (self, "couldn't load facility locks: %s", error->message); else { g_autoptr(MMBaseSim) sim = NULL; /* We loaded the initial list of facility locks; but we do need to update * the SIM PIN lock status when that changes. We'll connect to the signal * which notifies about such update. There is no need to ref self as the * SIM itself is an object which exists as long as self exists. */ g_object_get (self, MM_IFACE_MODEM_SIM, &sim, NULL); if (sim) g_signal_connect (sim, MM_BASE_SIM_PIN_LOCK_ENABLED, G_CALLBACK (sim_pin_lock_enabled_cb), ctx->skeleton); } /* Go on to next step */ ctx->step++; interface_initialization_step (task); } static void load_imei_ready (MMIfaceModem3gpp *self, GAsyncResult *res, GTask *task) { InitializationContext *ctx; g_autoptr(GError) error = NULL; g_autofree gchar *imei = NULL; ctx = g_task_get_task_data (task); imei = MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->load_imei_finish (self, res, &error); mm_gdbus_modem3gpp_set_imei (ctx->skeleton, imei); if (error) mm_obj_dbg (self, "couldn't load IMEI: %s", error->message); /* Go on to next step */ ctx->step++; interface_initialization_step (task); } /*****************************************************************************/ typedef struct { MmGdbusModem3gpp *skeleton; GDBusMethodInvocation *invocation; MMIfaceModem3gpp *self; GVariant *data; } HandleSetCarrierLockContext; static void handle_set_carrier_lock_context_free (HandleSetCarrierLockContext *ctx) { g_object_unref (ctx->skeleton); g_object_unref (ctx->invocation); g_object_unref (ctx->self); g_variant_unref (ctx->data); g_free (ctx); } static void handle_set_carrier_lock_ready (MMIfaceModem3gpp *self, GAsyncResult *res, HandleSetCarrierLockContext *ctx) { GError *error = NULL; if (!MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->set_carrier_lock_finish (self, res, &error)) mm_dbus_method_invocation_take_error (ctx->invocation, error); else mm_gdbus_modem3gpp_complete_set_carrier_lock (ctx->skeleton, ctx->invocation); handle_set_carrier_lock_context_free (ctx); } static void handle_set_carrier_lock_auth_ready (MMBaseModem *self, GAsyncResult *res, HandleSetCarrierLockContext *ctx) { GError *error = NULL; const guint8 *data; gsize data_size; if (!mm_base_modem_authorize_finish (self, res, &error)) { mm_dbus_method_invocation_take_error (ctx->invocation, error); handle_set_carrier_lock_context_free (ctx); return; } /* If carrier lock is not implemented, report an error */ if (!MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->set_carrier_lock || !MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->set_carrier_lock_finish) { mm_dbus_method_invocation_return_error_literal (ctx->invocation, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "Cannot send set carrier lock request to modem: " "operation not supported"); handle_set_carrier_lock_context_free (ctx); return; } data = (const guint8 *) g_variant_get_fixed_array (ctx->data, &data_size, sizeof (guint8)); MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->set_carrier_lock (ctx->self, data, data_size, (GAsyncReadyCallback)handle_set_carrier_lock_ready, ctx); } static gboolean handle_set_carrier_lock (MmGdbusModem3gpp *skeleton, GDBusMethodInvocation *invocation, GVariant *data, MMIfaceModem3gpp *self) { HandleSetCarrierLockContext *ctx; ctx = g_new0 (HandleSetCarrierLockContext, 1); ctx->skeleton = g_object_ref (skeleton); ctx->invocation = g_object_ref (invocation); ctx->self = g_object_ref (self); ctx->data = g_variant_ref (data); mm_base_modem_authorize (MM_BASE_MODEM (self), invocation, MM_AUTHORIZATION_DEVICE_CONTROL, (GAsyncReadyCallback)handle_set_carrier_lock_auth_ready, ctx); return TRUE; } /*****************************************************************************/ static void interface_initialization_step (GTask *task) { MMIfaceModem3gpp *self; InitializationContext *ctx; MMModemState modem_state; /* Don't run new steps if we're cancelled */ if (g_task_return_error_if_cancelled (task)) { g_object_unref (task); return; } self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); switch (ctx->step) { case INITIALIZATION_STEP_FIRST: ctx->step++; /* fall through */ case INITIALIZATION_STEP_ENABLED_FACILITY_LOCKS: if (MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->load_enabled_facility_locks && MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->load_enabled_facility_locks_finish) { MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->load_enabled_facility_locks ( self, (GAsyncReadyCallback)load_enabled_facility_locks_ready, task); return; } ctx->step++; /* fall through */ case INITIALIZATION_STEP_IMEI: /* IMEI value is meant to be loaded only once during the whole * lifetime of the modem. Therefore, if we already have it loaded, * don't try to load it again. */ if (!mm_gdbus_modem3gpp_get_imei (ctx->skeleton) && MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->load_imei && MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->load_imei_finish) { MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->load_imei ( self, (GAsyncReadyCallback)load_imei_ready, task); return; } ctx->step++; /* fall through */ case INITIALIZATION_STEP_TEST_LOCKED_OR_FAILED: modem_state = MM_MODEM_STATE_UNKNOWN; g_object_get (self, MM_IFACE_MODEM_STATE, &modem_state, NULL); if (modem_state == MM_MODEM_STATE_LOCKED || modem_state == MM_MODEM_STATE_FAILED) { /* Skip some steps and export the interface if modem is locked or failed */ ctx->step = INITIALIZATION_STEP_LAST; interface_initialization_step (task); return; } ctx->step++; /* fall through */ case INITIALIZATION_STEP_EPS_UE_MODE_OPERATION: if (MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->load_eps_ue_mode_operation && MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->load_eps_ue_mode_operation_finish) { MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->load_eps_ue_mode_operation ( self, (GAsyncReadyCallback)load_eps_ue_mode_operation_ready, task); return; } ctx->step++; /* fall through */ case INITIALIZATION_STEP_EPS_INITIAL_BEARER_SETTINGS: if (MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->load_initial_eps_bearer_settings && MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->load_initial_eps_bearer_settings_finish) { MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->load_initial_eps_bearer_settings ( self, (GAsyncReadyCallback)load_initial_eps_bearer_settings_ready, task); return; } ctx->step++; /* fall through */ case INITIALIZATION_STEP_NR5G_REGISTRATION_SETTINGS: if (MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->load_nr5g_registration_settings && MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->load_nr5g_registration_settings_finish) { MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->load_nr5g_registration_settings ( self, (GAsyncReadyCallback)load_nr5g_registration_settings_ready, task); return; } ctx->step++; /* fall through */ case INITIALIZATION_STEP_CONNECT_SIGNALS: /* We are done without errors! */ /* Handle method invocations */ g_signal_connect (ctx->skeleton, "handle-register", G_CALLBACK (handle_register), self); g_signal_connect (ctx->skeleton, "handle-scan", G_CALLBACK (handle_scan), self); g_signal_connect (ctx->skeleton, "handle-set-eps-ue-mode-operation", G_CALLBACK (handle_set_eps_ue_mode_operation), self); g_signal_connect (ctx->skeleton, "handle-set-initial-eps-bearer-settings", G_CALLBACK (handle_set_initial_eps_bearer_settings), self); g_signal_connect (ctx->skeleton, "handle-set-packet-service-state", G_CALLBACK (handle_set_packet_service_state), self); g_signal_connect (ctx->skeleton, "handle-set-nr5g-registration-settings", G_CALLBACK (handle_set_nr5g_registration_settings), self); ctx->step++; /* fall through */ case INITIALIZATION_STEP_LAST: /* Always connect the signal to unlock modem */ g_signal_connect (ctx->skeleton, "handle-disable-facility-lock", G_CALLBACK (handle_disable_facility_lock), self); g_signal_connect (ctx->skeleton, "handle-set-carrier-lock", G_CALLBACK (handle_set_carrier_lock), self); /* Finally, export the new interface */ mm_gdbus_object_skeleton_set_modem3gpp (MM_GDBUS_OBJECT_SKELETON (self), MM_GDBUS_MODEM3GPP (ctx->skeleton)); g_task_return_boolean (task, TRUE); g_object_unref (task); return; default: break; } g_assert_not_reached (); } gboolean mm_iface_modem_3gpp_initialize_finish (MMIfaceModem3gpp *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } void mm_iface_modem_3gpp_initialize (MMIfaceModem3gpp *self, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { MmGdbusModem3gpp *skeleton = NULL; InitializationContext *ctx; GTask *task; /* Did we already create it? */ g_object_get (self, MM_IFACE_MODEM_3GPP_DBUS_SKELETON, &skeleton, NULL); if (!skeleton) { skeleton = mm_gdbus_modem3gpp_skeleton_new (); /* Set all initial property defaults */ mm_gdbus_modem3gpp_set_imei (skeleton, NULL); mm_gdbus_modem3gpp_set_operator_code (skeleton, NULL); mm_gdbus_modem3gpp_set_operator_name (skeleton, NULL); mm_gdbus_modem3gpp_set_enabled_facility_locks (skeleton, MM_MODEM_3GPP_FACILITY_NONE); mm_gdbus_modem3gpp_set_subscription_state (skeleton, MM_MODEM_3GPP_SUBSCRIPTION_STATE_UNKNOWN); mm_gdbus_modem3gpp_set_pco (skeleton, NULL); mm_gdbus_modem3gpp_set_initial_eps_bearer (skeleton, NULL); /* Bind our RegistrationState property */ g_object_bind_property (self, MM_IFACE_MODEM_3GPP_REGISTRATION_STATE, skeleton, "registration-state", G_BINDING_DEFAULT | G_BINDING_SYNC_CREATE); /* Bind our packet service state property */ g_object_bind_property (self, MM_IFACE_MODEM_3GPP_PACKET_SERVICE_STATE, skeleton, "packet-service-state", G_BINDING_DEFAULT | G_BINDING_SYNC_CREATE); g_object_set (self, MM_IFACE_MODEM_3GPP_DBUS_SKELETON, skeleton, NULL); } ctx = g_slice_new0 (InitializationContext); ctx->step = INITIALIZATION_STEP_FIRST; ctx->skeleton = skeleton; task = g_task_new (self, cancellable, callback, user_data); g_task_set_task_data (task, ctx, (GDestroyNotify)initialization_context_free); /* Perform async initialization here */ interface_initialization_step (task); } void mm_iface_modem_3gpp_shutdown (MMIfaceModem3gpp *self) { /* Unexport DBus interface and remove the skeleton */ mm_gdbus_object_skeleton_set_modem3gpp (MM_GDBUS_OBJECT_SKELETON (self), NULL); g_object_set (self, MM_IFACE_MODEM_3GPP_DBUS_SKELETON, NULL, NULL); } /*****************************************************************************/ static void iface_modem_3gpp_init (gpointer g_iface) { static gboolean initialized = FALSE; if (initialized) return; /* Properties */ g_object_interface_install_property (g_iface, g_param_spec_object (MM_IFACE_MODEM_3GPP_DBUS_SKELETON, "3GPP DBus skeleton", "DBus skeleton for the 3GPP interface", MM_GDBUS_TYPE_MODEM3GPP_SKELETON, G_PARAM_READWRITE)); g_object_interface_install_property (g_iface, g_param_spec_enum (MM_IFACE_MODEM_3GPP_REGISTRATION_STATE, "RegistrationState", "Registration state of the modem", MM_TYPE_MODEM_3GPP_REGISTRATION_STATE, MM_MODEM_3GPP_REGISTRATION_STATE_UNKNOWN, G_PARAM_READWRITE)); g_object_interface_install_property (g_iface, g_param_spec_boolean (MM_IFACE_MODEM_3GPP_CS_NETWORK_SUPPORTED, "CS network supported", "Whether the modem works in the CS network", TRUE, G_PARAM_READWRITE)); g_object_interface_install_property (g_iface, g_param_spec_boolean (MM_IFACE_MODEM_3GPP_PS_NETWORK_SUPPORTED, "PS network supported", "Whether the modem works in the PS network", TRUE, G_PARAM_READWRITE)); g_object_interface_install_property (g_iface, g_param_spec_boolean (MM_IFACE_MODEM_3GPP_EPS_NETWORK_SUPPORTED, "EPS network supported", "Whether the modem works in the EPS network", FALSE, G_PARAM_READWRITE)); g_object_interface_install_property (g_iface, g_param_spec_boolean (MM_IFACE_MODEM_3GPP_5GS_NETWORK_SUPPORTED, "5GS network supported", "Whether the modem works in the 5GS network", FALSE, G_PARAM_READWRITE)); g_object_interface_install_property (g_iface, g_param_spec_flags (MM_IFACE_MODEM_3GPP_IGNORED_FACILITY_LOCKS, "Ignored locks", "Ignored facility locks", MM_TYPE_MODEM_3GPP_FACILITY, MM_MODEM_3GPP_FACILITY_NONE, G_PARAM_READWRITE)); g_object_interface_install_property (g_iface, g_param_spec_object (MM_IFACE_MODEM_3GPP_INITIAL_EPS_BEARER, "Initial EPS bearer", "Initial EPS bearer setup during registration", MM_TYPE_BASE_BEARER, G_PARAM_READWRITE)); g_object_interface_install_property (g_iface, g_param_spec_enum (MM_IFACE_MODEM_3GPP_PACKET_SERVICE_STATE, "PacketServiceState", "Packet service state of the modem", MM_TYPE_MODEM_3GPP_PACKET_SERVICE_STATE, MM_MODEM_3GPP_PACKET_SERVICE_STATE_UNKNOWN, G_PARAM_READWRITE)); initialized = TRUE; } GType mm_iface_modem_3gpp_get_type (void) { static GType iface_modem_3gpp_type = 0; if (!G_UNLIKELY (iface_modem_3gpp_type)) { static const GTypeInfo info = { sizeof (MMIfaceModem3gpp), /* class_size */ iface_modem_3gpp_init, /* base_init */ NULL, /* base_finalize */ }; iface_modem_3gpp_type = g_type_register_static (G_TYPE_INTERFACE, "MMIfaceModem3gpp", &info, 0); g_type_interface_add_prerequisite (iface_modem_3gpp_type, MM_TYPE_IFACE_MODEM); } return iface_modem_3gpp_type; } ModemManager-1.23.4-dev/src/mm-iface-modem-3gpp.h000066400000000000000000000654341456466623000213250ustar00rootroot00000000000000/* -*- 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) 2011-2012 Google, Inc. * Copyright (c) 2022 Qualcomm Innovation Center, Inc. */ #ifndef MM_IFACE_MODEM_3GPP_H #define MM_IFACE_MODEM_3GPP_H #include #include #define _LIBMM_INSIDE_MM #include #include "mm-base-bearer.h" #include "mm-port-serial-at.h" #define MM_TYPE_IFACE_MODEM_3GPP (mm_iface_modem_3gpp_get_type ()) #define MM_IFACE_MODEM_3GPP(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_IFACE_MODEM_3GPP, MMIfaceModem3gpp)) #define MM_IS_IFACE_MODEM_3GPP(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_IFACE_MODEM_3GPP)) #define MM_IFACE_MODEM_3GPP_GET_INTERFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE ((obj), MM_TYPE_IFACE_MODEM_3GPP, MMIfaceModem3gpp)) #define MM_IFACE_MODEM_3GPP_DBUS_SKELETON "iface-modem-3gpp-dbus-skeleton" #define MM_IFACE_MODEM_3GPP_REGISTRATION_STATE "iface-modem-3gpp-registration-state" #define MM_IFACE_MODEM_3GPP_CS_NETWORK_SUPPORTED "iface-modem-3gpp-cs-network-supported" #define MM_IFACE_MODEM_3GPP_PS_NETWORK_SUPPORTED "iface-modem-3gpp-ps-network-supported" #define MM_IFACE_MODEM_3GPP_EPS_NETWORK_SUPPORTED "iface-modem-3gpp-eps-network-supported" #define MM_IFACE_MODEM_3GPP_5GS_NETWORK_SUPPORTED "iface-modem-3gpp-5gs-network-supported" #define MM_IFACE_MODEM_3GPP_IGNORED_FACILITY_LOCKS "iface-modem-3gpp-ignored-facility-locks" #define MM_IFACE_MODEM_3GPP_INITIAL_EPS_BEARER "iface-modem-3gpp-initial-eps-bearer" #define MM_IFACE_MODEM_3GPP_PACKET_SERVICE_STATE "iface-modem-3gpp-packet-service-state" #define MM_IFACE_MODEM_3GPP_ALL_ACCESS_TECHNOLOGIES_MASK \ (MM_MODEM_ACCESS_TECHNOLOGY_GSM | \ MM_MODEM_ACCESS_TECHNOLOGY_GSM_COMPACT | \ MM_MODEM_ACCESS_TECHNOLOGY_GPRS | \ MM_MODEM_ACCESS_TECHNOLOGY_EDGE | \ MM_MODEM_ACCESS_TECHNOLOGY_UMTS | \ MM_MODEM_ACCESS_TECHNOLOGY_HSDPA | \ MM_MODEM_ACCESS_TECHNOLOGY_HSUPA | \ MM_MODEM_ACCESS_TECHNOLOGY_HSPA | \ MM_MODEM_ACCESS_TECHNOLOGY_HSPA_PLUS | \ MM_MODEM_ACCESS_TECHNOLOGY_LTE | \ MM_MODEM_ACCESS_TECHNOLOGY_5GNR) typedef struct _MMIfaceModem3gpp MMIfaceModem3gpp; struct _MMIfaceModem3gpp { GTypeInterface g_iface; /* Loading of the IMEI property */ void (*load_imei) (MMIfaceModem3gpp *self, GAsyncReadyCallback callback, gpointer user_data); gchar * (*load_imei_finish) (MMIfaceModem3gpp *self, GAsyncResult *res, GError **error); /* Loading of the facility locks property */ void (*load_enabled_facility_locks) (MMIfaceModem3gpp *self, GAsyncReadyCallback callback, gpointer user_data); MMModem3gppFacility (*load_enabled_facility_locks_finish) (MMIfaceModem3gpp *self, GAsyncResult *res, GError **error); /* Loading of the UE mode of operation for EPS property */ void (* load_eps_ue_mode_operation) (MMIfaceModem3gpp *self, GAsyncReadyCallback callback, gpointer user_data); MMModem3gppEpsUeModeOperation (* load_eps_ue_mode_operation_finish) (MMIfaceModem3gpp *self, GAsyncResult *res, GError **error); /* Asynchronous setting up unsolicited events */ void (*setup_unsolicited_events) (MMIfaceModem3gpp *self, GAsyncReadyCallback callback, gpointer user_data); gboolean (*setup_unsolicited_events_finish) (MMIfaceModem3gpp *self, GAsyncResult *res, GError **error); /* Asynchronous enabling of unsolicited events */ void (*enable_unsolicited_events) (MMIfaceModem3gpp *self, GAsyncReadyCallback callback, gpointer user_data); gboolean (*enable_unsolicited_events_finish) (MMIfaceModem3gpp *self, GAsyncResult *res, GError **error); /* Asynchronous cleaning up of unsolicited events */ void (*cleanup_unsolicited_events) (MMIfaceModem3gpp *self, GAsyncReadyCallback callback, gpointer user_data); gboolean (*cleanup_unsolicited_events_finish) (MMIfaceModem3gpp *self, GAsyncResult *res, GError **error); /* Asynchronous disabling of unsolicited events */ void (*disable_unsolicited_events) (MMIfaceModem3gpp *self, GAsyncReadyCallback callback, gpointer user_data); gboolean (*disable_unsolicited_events_finish) (MMIfaceModem3gpp *self, GAsyncResult *res, GError **error); /* Setup unsolicited registration messages */ void (* setup_unsolicited_registration_events) (MMIfaceModem3gpp *self, GAsyncReadyCallback callback, gpointer user_data); gboolean (*setup_unsolicited_registration_events_finish) (MMIfaceModem3gpp *self, GAsyncResult *res, GError **error); /* Asynchronous enabling of unsolicited registration events */ void (*enable_unsolicited_registration_events) (MMIfaceModem3gpp *self, gboolean cs_supported, gboolean ps_supported, gboolean eps_supported, GAsyncReadyCallback callback, gpointer user_data); gboolean (*enable_unsolicited_registration_events_finish) (MMIfaceModem3gpp *self, GAsyncResult *res, GError **error); /* Cleanup unsolicited registration messages */ void (* cleanup_unsolicited_registration_events) (MMIfaceModem3gpp *self, GAsyncReadyCallback callback, gpointer user_data); gboolean (*cleanup_unsolicited_registration_events_finish) (MMIfaceModem3gpp *self, GAsyncResult *res, GError **error); /* Asynchronous disabling of unsolicited registration events */ void (*disable_unsolicited_registration_events) (MMIfaceModem3gpp *self, gboolean cs_supported, gboolean ps_supported, gboolean eps_supported, GAsyncReadyCallback callback, gpointer user_data); gboolean (*disable_unsolicited_registration_events_finish) (MMIfaceModem3gpp *self, GAsyncResult *res, GError **error); /* Asynchronous initial default EPS bearer loading */ void (*load_initial_eps_bearer) (MMIfaceModem3gpp *self, GAsyncReadyCallback callback, gpointer user_data); MMBearerProperties * (*load_initial_eps_bearer_finish) (MMIfaceModem3gpp *self, GAsyncResult *res, GError **error); /* Asynchronous initial default EPS bearer settings loading */ void (*load_initial_eps_bearer_settings) (MMIfaceModem3gpp *self, GAsyncReadyCallback callback, gpointer user_data); MMBearerProperties * (*load_initial_eps_bearer_settings_finish) (MMIfaceModem3gpp *self, GAsyncResult *res, GError **error); /* Asynchronous 5GNR registration settings loading */ void (*load_nr5g_registration_settings) (MMIfaceModem3gpp *self, GAsyncReadyCallback callback, gpointer user_data); MMNr5gRegistrationSettings * (*load_nr5g_registration_settings_finish) (MMIfaceModem3gpp *self, GAsyncResult *res, GError **error); /* Create initial default EPS bearer object */ MMBaseBearer * (*create_initial_eps_bearer) (MMIfaceModem3gpp *self, MMBearerProperties *properties); /* Run CS/PS/EPS/5GS registration state checks.. * Note that no registration state is returned, implementations should call * mm_iface_modem_3gpp_update_registration_state(). */ void (* run_registration_checks) (MMIfaceModem3gpp *self, gboolean is_cs_supported, gboolean is_ps_supported, gboolean is_eps_supported, gboolean is_5gs_supported, GAsyncReadyCallback callback, gpointer user_data); gboolean (*run_registration_checks_finish) (MMIfaceModem3gpp *self, GAsyncResult *res, GError **error); /* Try to register in the network */ void (* register_in_network) (MMIfaceModem3gpp *self, const gchar *operator_id, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); gboolean (*register_in_network_finish) (MMIfaceModem3gpp *self, GAsyncResult *res, GError **error); /* Loading of the Operator Code property */ void (*load_operator_code) (MMIfaceModem3gpp *self, GAsyncReadyCallback callback, gpointer user_data); gchar * (*load_operator_code_finish) (MMIfaceModem3gpp *self, GAsyncResult *res, GError **error); /* Loading of the Operator Name property */ void (*load_operator_name) (MMIfaceModem3gpp *self, GAsyncReadyCallback callback, gpointer user_data); gchar * (*load_operator_name_finish) (MMIfaceModem3gpp *self, GAsyncResult *res, GError **error); /* Scan current networks, expect a GList of MMModem3gppNetworkInfo */ void (* scan_networks) (MMIfaceModem3gpp *self, GAsyncReadyCallback callback, gpointer user_data); GList * (*scan_networks_finish) (MMIfaceModem3gpp *self, GAsyncResult *res, GError **error); /* Set UE mode of operation for EPS */ void (* set_eps_ue_mode_operation) (MMIfaceModem3gpp *self, MMModem3gppEpsUeModeOperation mode, GAsyncReadyCallback callback, gpointer user_data); gboolean (* set_eps_ue_mode_operation_finish) (MMIfaceModem3gpp *self, GAsyncResult *res, GError **error); /* Set initial EPS bearer settings */ void (* set_initial_eps_bearer_settings) (MMIfaceModem3gpp *self, MMBearerProperties *properties, GAsyncReadyCallback callback, gpointer user_data); gboolean (* set_initial_eps_bearer_settings_finish) (MMIfaceModem3gpp *self, GAsyncResult *res, GError **error); /* Remove modem personalization */ void (* disable_facility_lock) (MMIfaceModem3gpp *self, MMModem3gppFacility facility, guint8 slot, const gchar *control_key, GAsyncReadyCallback callback, gpointer user_data); gboolean (* disable_facility_lock_finish) (MMIfaceModem3gpp *self, GAsyncResult *res, GError **error); /* Set Packet service */ void (*set_packet_service_state) (MMIfaceModem3gpp *self, MMModem3gppPacketServiceState state, GAsyncReadyCallback callback, gpointer user_data); gboolean (*set_packet_service_state_finish) (MMIfaceModem3gpp *self, GAsyncResult *res, GError **error); /* Set 5GNR registration settings */ void (* set_nr5g_registration_settings) (MMIfaceModem3gpp *self, MMNr5gRegistrationSettings *settings, GAsyncReadyCallback callback, gpointer user_data); gboolean (* set_nr5g_registration_settings_finish) (MMIfaceModem3gpp *self, GAsyncResult *res, GError **error); /* Set carrier lock */ void (* set_carrier_lock) (MMIfaceModem3gpp *self, const guint8 *data, gsize data_size, GAsyncReadyCallback callback, gpointer user_data); gboolean (*set_carrier_lock_finish) (MMIfaceModem3gpp *self, GAsyncResult *res, GError **error); }; GType mm_iface_modem_3gpp_get_type (void); G_DEFINE_AUTOPTR_CLEANUP_FUNC (MMIfaceModem3gpp, g_object_unref) /* Initialize Modem 3GPP interface (async) */ void mm_iface_modem_3gpp_initialize (MMIfaceModem3gpp *self, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); gboolean mm_iface_modem_3gpp_initialize_finish (MMIfaceModem3gpp *self, GAsyncResult *res, GError **error); /* Enable Modem interface (async) */ void mm_iface_modem_3gpp_enable (MMIfaceModem3gpp *self, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); gboolean mm_iface_modem_3gpp_enable_finish (MMIfaceModem3gpp *self, GAsyncResult *res, GError **error); /* Disable Modem interface (async) */ void mm_iface_modem_3gpp_disable (MMIfaceModem3gpp *self, GAsyncReadyCallback callback, gpointer user_data); gboolean mm_iface_modem_3gpp_disable_finish (MMIfaceModem3gpp *self, GAsyncResult *res, GError **error); #if defined WITH_SUSPEND_RESUME /* Sync 3GPP interface (async) */ void mm_iface_modem_3gpp_sync (MMIfaceModem3gpp *self, GAsyncReadyCallback callback, gpointer user_data); gboolean mm_iface_modem_3gpp_sync_finish (MMIfaceModem3gpp *self, GAsyncResult *res, GError **error); #endif /* Shutdown Modem 3GPP interface */ void mm_iface_modem_3gpp_shutdown (MMIfaceModem3gpp *self); /* Objects implementing this interface can report new registration info, * access technologies and location. * * This may happen when handling unsolicited registration messages, or when * the interface asks to run registration state checks. * * The registration updates may be "deferred" so that they are applied all at * the same time. */ void mm_iface_modem_3gpp_update_cs_registration_state (MMIfaceModem3gpp *self, MMModem3gppRegistrationState state, gboolean deferred); void mm_iface_modem_3gpp_update_ps_registration_state (MMIfaceModem3gpp *self, MMModem3gppRegistrationState state, gboolean deferred); void mm_iface_modem_3gpp_update_eps_registration_state (MMIfaceModem3gpp *self, MMModem3gppRegistrationState state, gboolean deferred); void mm_iface_modem_3gpp_update_5gs_registration_state (MMIfaceModem3gpp *self, MMModem3gppRegistrationState state, gboolean deferred); void mm_iface_modem_3gpp_apply_deferred_registration_state (MMIfaceModem3gpp *self); void mm_iface_modem_3gpp_update_packet_service_state (MMIfaceModem3gpp *self, MMModem3gppPacketServiceState state); void mm_iface_modem_3gpp_update_subscription_state (MMIfaceModem3gpp *self, MMModem3gppSubscriptionState state); void mm_iface_modem_3gpp_update_access_technologies (MMIfaceModem3gpp *self, MMModemAccessTechnology access_tech); void mm_iface_modem_3gpp_update_location (MMIfaceModem3gpp *self, gulong location_area_code, gulong tracking_area_code, gulong cell_id); void mm_iface_modem_3gpp_update_pco_list (MMIfaceModem3gpp *self, const GList *pco_list); void mm_iface_modem_3gpp_update_initial_eps_bearer (MMIfaceModem3gpp *self, MMBearerProperties *properties); void mm_iface_modem_3gpp_reload_initial_eps_bearer (MMIfaceModem3gpp *self); /* Run all registration checks */ void mm_iface_modem_3gpp_run_registration_checks (MMIfaceModem3gpp *self, GAsyncReadyCallback callback, gpointer user_data); gboolean mm_iface_modem_3gpp_run_registration_checks_finish (MMIfaceModem3gpp *self, GAsyncResult *res, GError **error); /* Request to reload current registration information */ void mm_iface_modem_3gpp_reload_current_registration_info (MMIfaceModem3gpp *self, GAsyncReadyCallback callback, gpointer user_data); gboolean mm_iface_modem_3gpp_reload_current_registration_info_finish (MMIfaceModem3gpp *self, GAsyncResult *res, GError **error); void mm_iface_modem_3gpp_clear_current_operator (MMIfaceModem3gpp *self); /* Allow registering in the network */ void mm_iface_modem_3gpp_register_in_network (MMIfaceModem3gpp *self, const gchar *operator_id, gboolean force_registration, guint max_registration_time, GAsyncReadyCallback callback, gpointer user_data); gboolean mm_iface_modem_3gpp_register_in_network_finish (MMIfaceModem3gpp *self, GAsyncResult *res, GError **error); /* Allow re-registering in the network with last settings */ void mm_iface_modem_3gpp_reregister_in_network (MMIfaceModem3gpp *self, GAsyncReadyCallback callback, gpointer user_data); gboolean mm_iface_modem_3gpp_reregister_in_network_finish (MMIfaceModem3gpp *self, GAsyncResult *res, GError **error); /* Allow requesting packet service explicitly */ void mm_iface_modem_3gpp_set_packet_service_state (MMIfaceModem3gpp *self, MMModem3gppPacketServiceState packet_service_state, GAsyncReadyCallback callback, gpointer user_data); gboolean mm_iface_modem_3gpp_set_packet_service_state_finish (MMIfaceModem3gpp *self, GAsyncResult *res, GError **error); /* Allow waiting for packet service */ void mm_iface_modem_3gpp_wait_for_packet_service_state (MMIfaceModem3gpp *self, MMModem3gppPacketServiceState final_state, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); MMModem3gppPacketServiceState mm_iface_modem_3gpp_wait_for_packet_service_state_finish (MMIfaceModem3gpp *self, GAsyncResult *res, GError **error); /* Bind properties for simple GetStatus() */ void mm_iface_modem_3gpp_bind_simple_status (MMIfaceModem3gpp *self, MMSimpleStatus *status); #endif /* MM_IFACE_MODEM_3GPP_H */ ModemManager-1.23.4-dev/src/mm-iface-modem-cdma.c000066400000000000000000002147771456466623000213610ustar00rootroot00000000000000/* -*- 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) 2011 Google, Inc. */ #include #define _LIBMM_INSIDE_MM #include #include "libqcdm/src/commands.h" #include "mm-iface-modem.h" #include "mm-iface-modem-cdma.h" #include "mm-base-modem.h" #include "mm-modem-helpers.h" #include "mm-error-helpers.h" #include "mm-log-object.h" #define SUBSYSTEM_CDMA1X "cdma1x" #define SUBSYSTEM_EVDO "evdo" /*****************************************************************************/ /* Private data context */ #define PRIVATE_TAG "iface-modem-cdma-private-tag" static GQuark private_quark; typedef struct { gboolean activation_ongoing; } Private; static void private_free (Private *priv) { g_slice_free (Private, priv); } static Private * get_private (MMIfaceModemCdma *self) { Private *priv; if (G_UNLIKELY (!private_quark)) private_quark = g_quark_from_static_string (PRIVATE_TAG); priv = g_object_get_qdata (G_OBJECT (self), private_quark); if (!priv) { priv = g_slice_new0 (Private); g_object_set_qdata_full (G_OBJECT (self), private_quark, priv, (GDestroyNotify)private_free); } return priv; } /*****************************************************************************/ void mm_iface_modem_cdma_bind_simple_status (MMIfaceModemCdma *self, MMSimpleStatus *status) { MmGdbusModemCdma *skeleton; g_object_get (self, MM_IFACE_MODEM_CDMA_DBUS_SKELETON, &skeleton, NULL); if (!skeleton) return; g_object_bind_property (skeleton, "cdma1x-registration-state", status, MM_SIMPLE_PROPERTY_CDMA_CDMA1X_REGISTRATION_STATE, G_BINDING_DEFAULT | G_BINDING_SYNC_CREATE); g_object_bind_property (skeleton, "sid", status, MM_SIMPLE_PROPERTY_CDMA_SID, G_BINDING_DEFAULT | G_BINDING_SYNC_CREATE); g_object_bind_property (skeleton, "nid", status, MM_SIMPLE_PROPERTY_CDMA_NID, G_BINDING_DEFAULT | G_BINDING_SYNC_CREATE); g_object_bind_property (skeleton, "evdo-registration-state", status, MM_SIMPLE_PROPERTY_CDMA_EVDO_REGISTRATION_STATE, G_BINDING_DEFAULT | G_BINDING_SYNC_CREATE); g_object_unref (skeleton); } /*****************************************************************************/ typedef struct { MmGdbusModemCdma *skeleton; GDBusMethodInvocation *invocation; MMIfaceModemCdma *self; gchar *carrier; } HandleActivateContext; static void handle_activate_context_free (HandleActivateContext *ctx) { g_object_unref (ctx->skeleton); g_object_unref (ctx->invocation); g_object_unref (ctx->self); g_free (ctx->carrier); g_free (ctx); } static void handle_activate_ready (MMIfaceModemCdma *self, GAsyncResult *res, HandleActivateContext *ctx) { Private *priv; GError *error = NULL; priv = get_private (self); priv->activation_ongoing = FALSE; if (!MM_IFACE_MODEM_CDMA_GET_INTERFACE (self)->activate_finish (self, res,&error)) mm_dbus_method_invocation_take_error (ctx->invocation, error); else mm_gdbus_modem_cdma_complete_activate (ctx->skeleton, ctx->invocation); handle_activate_context_free (ctx); } static void handle_activate_auth_ready (MMBaseModem *self, GAsyncResult *res, HandleActivateContext *ctx) { Private *priv; MMModemState modem_state; GError *error = NULL; priv = get_private (MM_IFACE_MODEM_CDMA (self)); if (!mm_base_modem_authorize_finish (self, res, &error)) { mm_dbus_method_invocation_take_error (ctx->invocation, error); handle_activate_context_free (ctx); return; } /* Fail if we have already an activation ongoing */ if (priv->activation_ongoing) { mm_dbus_method_invocation_return_error_literal (ctx->invocation, MM_CORE_ERROR, MM_CORE_ERROR_IN_PROGRESS, "An activation operation is already in progress"); handle_activate_context_free (ctx); return; } /* If we're already activated, nothing to do */ if (mm_gdbus_modem_cdma_get_activation_state (ctx->skeleton) == MM_MODEM_CDMA_ACTIVATION_STATE_ACTIVATED) { mm_obj_dbg (self, "already activated"); mm_gdbus_modem_cdma_complete_activate (ctx->skeleton, ctx->invocation); handle_activate_context_free (ctx); return; } /* If activating OTA is not implemented, report an error */ if (!MM_IFACE_MODEM_CDMA_GET_INTERFACE (self)->activate || !MM_IFACE_MODEM_CDMA_GET_INTERFACE (self)->activate_finish) { mm_dbus_method_invocation_return_error_literal (ctx->invocation, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "Cannot perform OTA activation: operation not supported"); handle_activate_context_free (ctx); return; } /* Error if carrier code is empty */ if (!ctx->carrier || !ctx->carrier[0]) { mm_dbus_method_invocation_return_error_literal (ctx->invocation, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS, "Cannot perform OTA activation: invalid empty carrier code"); handle_activate_context_free (ctx); return; } modem_state = MM_MODEM_STATE_UNKNOWN; g_object_get (self, MM_IFACE_MODEM_STATE, &modem_state, NULL); switch (modem_state) { case MM_MODEM_STATE_FAILED: case MM_MODEM_STATE_UNKNOWN: case MM_MODEM_STATE_LOCKED: /* We should never have such request (interface wasn't exported yet) */ g_assert_not_reached (); break; case MM_MODEM_STATE_INITIALIZING: mm_dbus_method_invocation_return_error_literal (ctx->invocation, MM_CORE_ERROR, MM_CORE_ERROR_WRONG_STATE, "Cannot perform OTA activation: device not fully initialized yet"); handle_activate_context_free (ctx); return; case MM_MODEM_STATE_ENABLED: case MM_MODEM_STATE_SEARCHING: case MM_MODEM_STATE_REGISTERED: priv->activation_ongoing = TRUE; MM_IFACE_MODEM_CDMA_GET_INTERFACE (self)->activate ( MM_IFACE_MODEM_CDMA (self), ctx->carrier, (GAsyncReadyCallback)handle_activate_ready, ctx); return; case MM_MODEM_STATE_DISABLING: mm_dbus_method_invocation_return_error_literal (ctx->invocation, MM_CORE_ERROR, MM_CORE_ERROR_WRONG_STATE, "Cannot perform OTA activation: currently being disabled"); break; case MM_MODEM_STATE_ENABLING: case MM_MODEM_STATE_DISABLED: mm_dbus_method_invocation_return_error_literal (ctx->invocation, MM_CORE_ERROR, MM_CORE_ERROR_WRONG_STATE, "Cannot perform OTA activation: not enabled yet"); break; case MM_MODEM_STATE_DISCONNECTING: case MM_MODEM_STATE_CONNECTING: case MM_MODEM_STATE_CONNECTED: mm_dbus_method_invocation_return_error_literal (ctx->invocation, MM_CORE_ERROR, MM_CORE_ERROR_WRONG_STATE, "Cannot perform OTA activation: modem is connected"); break; default: g_assert_not_reached (); } handle_activate_context_free (ctx); } static gboolean handle_activate (MmGdbusModemCdma *skeleton, GDBusMethodInvocation *invocation, const gchar *carrier, MMIfaceModemCdma *self) { HandleActivateContext *ctx; ctx = g_new (HandleActivateContext, 1); ctx->skeleton = g_object_ref (skeleton); ctx->invocation = g_object_ref (invocation); ctx->self = g_object_ref (self); ctx->carrier = g_strdup (carrier); mm_base_modem_authorize (MM_BASE_MODEM (self), invocation, MM_AUTHORIZATION_DEVICE_CONTROL, (GAsyncReadyCallback)handle_activate_auth_ready, ctx); return TRUE; } /*****************************************************************************/ typedef struct { MmGdbusModemCdma *skeleton; GDBusMethodInvocation *invocation; MMIfaceModemCdma *self; GVariant *dictionary; } HandleActivateManualContext; static void handle_activate_manual_context_free (HandleActivateManualContext *ctx) { g_object_unref (ctx->skeleton); g_object_unref (ctx->invocation); g_object_unref (ctx->self); g_variant_unref (ctx->dictionary); g_free (ctx); } static void handle_activate_manual_ready (MMIfaceModemCdma *self, GAsyncResult *res, HandleActivateManualContext *ctx) { Private *priv; GError *error = NULL; priv = get_private (self); priv->activation_ongoing = FALSE; if (!MM_IFACE_MODEM_CDMA_GET_INTERFACE (self)->activate_manual_finish (self, res,&error)) mm_dbus_method_invocation_take_error (ctx->invocation, error); else mm_gdbus_modem_cdma_complete_activate_manual (ctx->skeleton, ctx->invocation); handle_activate_manual_context_free (ctx); } static void handle_activate_manual_auth_ready (MMBaseModem *self, GAsyncResult *res, HandleActivateManualContext *ctx) { MMCdmaManualActivationProperties *properties; Private *priv; MMModemState modem_state; GError *error = NULL; priv = get_private (MM_IFACE_MODEM_CDMA (self)); if (!mm_base_modem_authorize_finish (self, res, &error)) { mm_dbus_method_invocation_take_error (ctx->invocation, error); handle_activate_manual_context_free (ctx); return; } /* Fail if we have already an activation ongoing */ if (priv->activation_ongoing) { mm_dbus_method_invocation_return_error_literal (ctx->invocation, MM_CORE_ERROR, MM_CORE_ERROR_IN_PROGRESS, "An activation operation is already in progress"); handle_activate_manual_context_free (ctx); return; } /* If we're already activated, nothing to do */ if (mm_gdbus_modem_cdma_get_activation_state (ctx->skeleton) == MM_MODEM_CDMA_ACTIVATION_STATE_ACTIVATED) { mm_obj_dbg (self, "already activated"); mm_gdbus_modem_cdma_complete_activate_manual (ctx->skeleton, ctx->invocation); handle_activate_manual_context_free (ctx); return; } /* If manual activation is not implemented, report an error */ if (!MM_IFACE_MODEM_CDMA_GET_INTERFACE (self)->activate_manual || !MM_IFACE_MODEM_CDMA_GET_INTERFACE (self)->activate_manual_finish) { mm_dbus_method_invocation_return_error_literal (ctx->invocation, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "Cannot perform manual activation: operation not supported"); handle_activate_manual_context_free (ctx); return; } /* Parse input properties */ properties = mm_cdma_manual_activation_properties_new_from_dictionary (ctx->dictionary, &error); if (!properties) { mm_dbus_method_invocation_take_error (ctx->invocation, error); handle_activate_manual_context_free (ctx); return; } modem_state = MM_MODEM_STATE_UNKNOWN; g_object_get (self, MM_IFACE_MODEM_STATE, &modem_state, NULL); switch (modem_state) { case MM_MODEM_STATE_FAILED: case MM_MODEM_STATE_UNKNOWN: case MM_MODEM_STATE_LOCKED: /* We should never have such request (interface wasn't exported yet) */ g_assert_not_reached (); break; case MM_MODEM_STATE_INITIALIZING: mm_dbus_method_invocation_return_error_literal (ctx->invocation, MM_CORE_ERROR, MM_CORE_ERROR_WRONG_STATE, "Cannot perform manual activation: device not fully initialized yet"); break; case MM_MODEM_STATE_ENABLED: case MM_MODEM_STATE_SEARCHING: case MM_MODEM_STATE_REGISTERED: priv->activation_ongoing = TRUE; MM_IFACE_MODEM_CDMA_GET_INTERFACE (self)->activate_manual ( MM_IFACE_MODEM_CDMA (self), properties, (GAsyncReadyCallback)handle_activate_manual_ready, ctx); g_object_unref (properties); return; case MM_MODEM_STATE_DISABLING: mm_dbus_method_invocation_return_error_literal (ctx->invocation, MM_CORE_ERROR, MM_CORE_ERROR_WRONG_STATE, "Cannot perform manual activation: currently being disabled"); break; case MM_MODEM_STATE_ENABLING: case MM_MODEM_STATE_DISABLED: mm_dbus_method_invocation_return_error_literal (ctx->invocation, MM_CORE_ERROR, MM_CORE_ERROR_WRONG_STATE, "Cannot perform manual activation: not enabled yet"); break; case MM_MODEM_STATE_DISCONNECTING: case MM_MODEM_STATE_CONNECTING: case MM_MODEM_STATE_CONNECTED: mm_dbus_method_invocation_return_error_literal (ctx->invocation, MM_CORE_ERROR, MM_CORE_ERROR_WRONG_STATE, "Cannot perform manual activation: modem is connected"); break; default: g_assert_not_reached (); } g_object_unref (properties); handle_activate_manual_context_free (ctx); } static gboolean handle_activate_manual (MmGdbusModemCdma *skeleton, GDBusMethodInvocation *invocation, GVariant *dictionary, MMIfaceModemCdma *self) { HandleActivateManualContext *ctx; ctx = g_new (HandleActivateManualContext, 1); ctx->skeleton = g_object_ref (skeleton); ctx->invocation = g_object_ref (invocation); ctx->self = g_object_ref (self); ctx->dictionary = g_variant_ref (dictionary); mm_base_modem_authorize (MM_BASE_MODEM (self), invocation, MM_AUTHORIZATION_DEVICE_CONTROL, (GAsyncReadyCallback)handle_activate_manual_auth_ready, ctx); return TRUE; } /*****************************************************************************/ /* Register in the CDMA network. * Note that the registration in the CDMA network is usually automatic; so this * method will really just try to ensure the modem is registered. If not * registered, it will wait until it is, up to N seconds. */ gboolean mm_iface_modem_cdma_register_in_network_finish (MMIfaceModemCdma *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void register_in_network_ready (MMIfaceModemCdma *self, GAsyncResult *res, GTask *task) { GError *error = NULL; if (!MM_IFACE_MODEM_CDMA_GET_INTERFACE (self)->register_in_network_finish (self, res, &error)) g_task_return_error (task, error); else g_task_return_boolean (task, TRUE); g_object_unref (task); } void mm_iface_modem_cdma_register_in_network (MMIfaceModemCdma *self, guint max_registration_time, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; task = g_task_new (self, NULL, callback, user_data); MM_IFACE_MODEM_CDMA_GET_INTERFACE (self)->register_in_network ( self, max_registration_time, (GAsyncReadyCallback)register_in_network_ready, task); } /*****************************************************************************/ #define REGISTRATION_CHECK_TIMEOUT_SEC 30 #define REGISTRATION_CHECK_CONTEXT_TAG "cdma-registration-check-context-tag" static GQuark registration_check_context_quark; typedef struct _RunRegistrationChecksContext RunRegistrationChecksContext; static void registration_check_step (GTask *task); typedef enum { REGISTRATION_CHECK_STEP_FIRST, REGISTRATION_CHECK_STEP_SETUP_REGISTRATION_CHECKS, REGISTRATION_CHECK_STEP_QCDM_CALL_MANAGER_STATE, REGISTRATION_CHECK_STEP_QCDM_HDR_STATE, REGISTRATION_CHECK_STEP_QCDM_CDMA1X_SERVING_SYSTEM, REGISTRATION_CHECK_STEP_QCDM_LAST, REGISTRATION_CHECK_STEP_AT_CDMA_SERVICE_STATUS, REGISTRATION_CHECK_STEP_AT_CDMA1X_SERVING_SYSTEM, REGISTRATION_CHECK_STEP_AT_LAST, REGISTRATION_CHECK_STEP_DETAILED_REGISTRATION_STATE, REGISTRATION_CHECK_STEP_LAST, } RegistrationCheckStep; struct _RunRegistrationChecksContext { RegistrationCheckStep step; MMModemCdmaRegistrationState cdma1x_state; MMModemCdmaRegistrationState evdo_state; gboolean cdma1x_supported; gboolean evdo_supported; gboolean skip_qcdm_call_manager_step; gboolean skip_qcdm_hdr_step; gboolean skip_at_cdma_service_status_step; gboolean skip_at_cdma1x_serving_system_step; gboolean skip_detailed_registration_state; guint call_manager_system_mode; guint call_manager_operating_mode; guint8 hdr_session_state; guint8 hdr_almp_state; guint8 hdr_hybrid_mode; guint cdma1x_class; guint cdma1x_band; guint cdma1x_sid; guint cdma1x_nid; }; gboolean mm_iface_modem_cdma_run_registration_checks_finish (MMIfaceModemCdma *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void setup_registration_checks_ready (MMIfaceModemCdma *self, GAsyncResult *res, GTask *task) { RunRegistrationChecksContext *ctx; GError *error = NULL; ctx = g_task_get_task_data (task); if (!MM_IFACE_MODEM_CDMA_GET_INTERFACE (self)->setup_registration_checks_finish ( self, res, &ctx->skip_qcdm_call_manager_step, &ctx->skip_qcdm_hdr_step, &ctx->skip_at_cdma_service_status_step, &ctx->skip_at_cdma1x_serving_system_step, &ctx->skip_detailed_registration_state, &error)) { /* Make it fatal */ g_task_return_error (task, error); g_object_unref (task); return; } /* Go on to next step */ ctx->step++; registration_check_step (task); } static void get_call_manager_state_ready (MMIfaceModemCdma *self, GAsyncResult *res, GTask *task) { RunRegistrationChecksContext *ctx; GError *error = NULL; ctx = g_task_get_task_data (task); if (!MM_IFACE_MODEM_CDMA_GET_INTERFACE (self)->get_call_manager_state_finish ( self, res, &ctx->call_manager_system_mode, &ctx->call_manager_operating_mode, &error)) { mm_obj_dbg (self, "could not get call manager state: %s", error->message); g_error_free (error); /* Fallback to AT-based check */ ctx->step = REGISTRATION_CHECK_STEP_AT_CDMA_SERVICE_STATUS; registration_check_step (task); return; } /* If no CDMA service, just finish checks */ if (ctx->call_manager_operating_mode != QCDM_CMD_CM_SUBSYS_STATE_INFO_OPERATING_MODE_ONLINE) { ctx->step = REGISTRATION_CHECK_STEP_LAST; registration_check_step (task); return; } /* Go on to next step */ ctx->step++; registration_check_step (task); } static void get_hdr_state_ready (MMIfaceModemCdma *self, GAsyncResult *res, GTask *task) { RunRegistrationChecksContext *ctx; GError *error = NULL; ctx = g_task_get_task_data (task); if (!MM_IFACE_MODEM_CDMA_GET_INTERFACE (self)->get_hdr_state_finish ( self, res, &ctx->hdr_hybrid_mode, &ctx->hdr_session_state, &ctx->hdr_almp_state, &error)) { mm_obj_dbg (self, "could not get HDR state: %s", error->message); g_error_free (error); /* Fallback to AT-based check */ ctx->step = REGISTRATION_CHECK_STEP_AT_CDMA_SERVICE_STATUS; registration_check_step (task); return; } /* Go on to next step */ ctx->step++; registration_check_step (task); } static void parse_qcdm_results (GTask *task) { MMIfaceModemCdma *self; RunRegistrationChecksContext *ctx; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); mm_obj_dbg (self, "QCDM CM System Mode: %d", ctx->call_manager_system_mode); mm_obj_dbg (self, "QCDM HDR Hybrid Mode: %d", ctx->hdr_hybrid_mode); mm_obj_dbg (self, "QCDM HDR Session State: %d", ctx->hdr_session_state); mm_obj_dbg (self, "QCDM HDR ALMP State: %d", ctx->hdr_almp_state); /* Set QCDM-obtained registration info */ switch (ctx->call_manager_system_mode) { case QCDM_CMD_CM_SUBSYS_STATE_INFO_SYSTEM_MODE_CDMA: ctx->cdma1x_state = MM_MODEM_CDMA_REGISTRATION_STATE_REGISTERED; if ( ctx->hdr_hybrid_mode && ctx->hdr_session_state == QCDM_CMD_HDR_SUBSYS_STATE_INFO_SESSION_STATE_OPEN && ( ctx->hdr_almp_state == QCDM_CMD_HDR_SUBSYS_STATE_INFO_ALMP_STATE_IDLE || ctx->hdr_almp_state == QCDM_CMD_HDR_SUBSYS_STATE_INFO_ALMP_STATE_CONNECTED)) ctx->evdo_state = MM_MODEM_CDMA_REGISTRATION_STATE_REGISTERED; break; case QCDM_CMD_CM_SUBSYS_STATE_INFO_SYSTEM_MODE_HDR: ctx->evdo_state = MM_MODEM_CDMA_REGISTRATION_STATE_REGISTERED; break; case QCDM_CMD_CM_SUBSYS_STATE_INFO_SYSTEM_MODE_AMPS: case QCDM_CMD_CM_SUBSYS_STATE_INFO_SYSTEM_MODE_NO_SERVICE: case QCDM_CMD_CM_SUBSYS_STATE_INFO_SYSTEM_MODE_WCDMA: default: break; } if (ctx->cdma1x_state != MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN || ctx->evdo_state != MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN) /* Jump to get detailed registration state */ ctx->step = REGISTRATION_CHECK_STEP_DETAILED_REGISTRATION_STATE; else /* If no CDMA service, just finish checks */ ctx->step = REGISTRATION_CHECK_STEP_LAST; registration_check_step (task); } static void get_service_status_ready (MMIfaceModemCdma *self, GAsyncResult *res, GTask *task) { RunRegistrationChecksContext *ctx; GError *error = NULL; gboolean has_service = FALSE; ctx = g_task_get_task_data (task); if (!MM_IFACE_MODEM_CDMA_GET_INTERFACE (self)->get_service_status_finish (self, res, &has_service, &error)) { mm_obj_warn (self, "could not get service status: %s", error->message); g_task_return_error (task, error); g_object_unref (task); return; } if (!has_service) { /* There is no CDMA service at all, end registration checks */ mm_obj_dbg (self, "no CDMA service found"); ctx->step = REGISTRATION_CHECK_STEP_LAST; } else /* If we do have service, go on to next step */ ctx->step++; registration_check_step (task); } static void get_cdma1x_serving_system_ready (MMIfaceModemCdma *self, GAsyncResult *res, GTask *task) { RunRegistrationChecksContext *ctx; GError *error = NULL; ctx = g_task_get_task_data (task); /* Note: used for *both* AT and QCDM serving system checks */ if (!MM_IFACE_MODEM_CDMA_GET_INTERFACE (self)->get_cdma1x_serving_system_finish ( self, res, &ctx->cdma1x_class, &ctx->cdma1x_band, &ctx->cdma1x_sid, &ctx->cdma1x_nid, &error)) { /* Treat as fatal all errors except for no-network */ if (!g_error_matches (error, MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_NO_NETWORK)) { mm_obj_warn (self, "could not get serving system: %s", error->message); g_task_return_error (task, error); g_object_unref (task); return; } ctx->cdma1x_sid = MM_MODEM_CDMA_SID_UNKNOWN; ctx->cdma1x_nid = MM_MODEM_CDMA_NID_UNKNOWN; } /* TODO: not sure why we also take class/band here */ /* Go on to next step */ ctx->step++; registration_check_step (task); } static void parse_at_results (GTask *task) { MMIfaceModemCdma *self; RunRegistrationChecksContext *ctx; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); /* 99999 means unknown/no service */ if (ctx->cdma1x_sid == MM_MODEM_CDMA_SID_UNKNOWN && ctx->cdma1x_nid == MM_MODEM_CDMA_NID_UNKNOWN) { /* Not registered in CDMA network, end registration checks */ mm_obj_dbg (self, "no registered in any CDMA network"); ctx->step = REGISTRATION_CHECK_STEP_LAST; } else { /* We're registered on the CDMA 1x network (at least) */ ctx->cdma1x_state = MM_MODEM_CDMA_REGISTRATION_STATE_REGISTERED; /* Jump to get detailed registration state */ ctx->step = REGISTRATION_CHECK_STEP_DETAILED_REGISTRATION_STATE; } registration_check_step (task); } static void get_detailed_registration_state_ready (MMIfaceModemCdma *self, GAsyncResult *res, GTask *task) { RunRegistrationChecksContext *ctx; GError *error = NULL; MMModemCdmaRegistrationState detailed_cdma1x_state = MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN; MMModemCdmaRegistrationState detailed_evdo_state = MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN; ctx = g_task_get_task_data (task); if (!MM_IFACE_MODEM_CDMA_GET_INTERFACE (self)->get_detailed_registration_state_finish ( self, res, &detailed_cdma1x_state, &detailed_evdo_state, &error)) { /* This error is NOT fatal. If we get an error here, we'll just fallback * to the non-detailed values we already got. */ mm_obj_dbg (self, "could not get more detailed registration state: %s", error->message); } else { ctx->cdma1x_state = detailed_cdma1x_state; ctx->evdo_state = detailed_evdo_state; } /* Go on to next step */ ctx->step++; registration_check_step (task); } static void registration_check_step (GTask *task) { MMIfaceModemCdma *self; RunRegistrationChecksContext *ctx; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); switch (ctx->step) { case REGISTRATION_CHECK_STEP_FIRST: ctx->step++; /* fall through */ case REGISTRATION_CHECK_STEP_SETUP_REGISTRATION_CHECKS: /* Allow implementations to run an initial setup check. This setup allows * to specify which of the next steps will be completely skipped. Useful * when implementations have a best get_detailed_registration_state() * so that they just need that to be run. */ if (MM_IFACE_MODEM_CDMA_GET_INTERFACE (self)->setup_registration_checks && MM_IFACE_MODEM_CDMA_GET_INTERFACE (self)->setup_registration_checks_finish) { MM_IFACE_MODEM_CDMA_GET_INTERFACE (self)->setup_registration_checks ( self, (GAsyncReadyCallback)setup_registration_checks_ready, task); return; } ctx->step++; /* fall through */ case REGISTRATION_CHECK_STEP_QCDM_CALL_MANAGER_STATE: mm_obj_dbg (self, "starting QCDM-based registration checks..."); if (!ctx->skip_qcdm_call_manager_step && MM_IFACE_MODEM_CDMA_GET_INTERFACE (self)->get_call_manager_state && MM_IFACE_MODEM_CDMA_GET_INTERFACE (self)->get_call_manager_state_finish) { /* Start by trying to get the call manager state. */ MM_IFACE_MODEM_CDMA_GET_INTERFACE (self)->get_call_manager_state ( self, (GAsyncReadyCallback)get_call_manager_state_ready, task); return; } /* Fallback to AT-based check */ mm_obj_dbg (self, " skipping all QCDM-based checks and falling back to AT-based checks"); ctx->step = REGISTRATION_CHECK_STEP_AT_CDMA_SERVICE_STATUS; registration_check_step (task); return; case REGISTRATION_CHECK_STEP_QCDM_HDR_STATE: if (ctx->evdo_supported && !ctx->skip_qcdm_hdr_step && MM_IFACE_MODEM_CDMA_GET_INTERFACE (self)->get_hdr_state && MM_IFACE_MODEM_CDMA_GET_INTERFACE (self)->get_hdr_state_finish) { /* Get HDR (EVDO) state. */ MM_IFACE_MODEM_CDMA_GET_INTERFACE (self)->get_hdr_state ( self, (GAsyncReadyCallback)get_hdr_state_ready, task); return; } mm_obj_dbg (self, " skipping HDR check"); ctx->step++; /* fall through */ case REGISTRATION_CHECK_STEP_QCDM_CDMA1X_SERVING_SYSTEM: /* We only care about SID/NID here; nothing to do with registration * state. */ if (MM_IFACE_MODEM_CDMA_GET_INTERFACE (self)->get_cdma1x_serving_system && MM_IFACE_MODEM_CDMA_GET_INTERFACE (self)->get_cdma1x_serving_system_finish) { MM_IFACE_MODEM_CDMA_GET_INTERFACE (self)->get_cdma1x_serving_system ( self, (GAsyncReadyCallback)get_cdma1x_serving_system_ready, task); return; } mm_obj_dbg (self, " skipping CDMA1x serving system check"); ctx->step++; /* fall through */ case REGISTRATION_CHECK_STEP_QCDM_LAST: /* When we get all QCDM results, parse them */ parse_qcdm_results (task); return; case REGISTRATION_CHECK_STEP_AT_CDMA_SERVICE_STATUS: mm_obj_dbg (self, "starting AT-based registration checks"); /* If we don't have means to get service status, just assume we do have * CDMA service and keep on */ if (!ctx->skip_at_cdma_service_status_step && MM_IFACE_MODEM_CDMA_GET_INTERFACE (self)->get_service_status && MM_IFACE_MODEM_CDMA_GET_INTERFACE (self)->get_service_status_finish) { MM_IFACE_MODEM_CDMA_GET_INTERFACE (self)->get_service_status ( self, (GAsyncReadyCallback)get_service_status_ready, task); return; } mm_obj_dbg (self, " skipping CDMA service status check, assuming with service"); ctx->step++; /* fall through */ case REGISTRATION_CHECK_STEP_AT_CDMA1X_SERVING_SYSTEM: /* 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 these callbacks to NULL.. */ if (!ctx->skip_at_cdma1x_serving_system_step && MM_IFACE_MODEM_CDMA_GET_INTERFACE (self)->get_cdma1x_serving_system && MM_IFACE_MODEM_CDMA_GET_INTERFACE (self)->get_cdma1x_serving_system_finish) { MM_IFACE_MODEM_CDMA_GET_INTERFACE (self)->get_cdma1x_serving_system ( self, (GAsyncReadyCallback)get_cdma1x_serving_system_ready, task); return; } mm_obj_dbg (self, " skipping CDMA1x Serving System check"); ctx->step++; /* fall through */ case REGISTRATION_CHECK_STEP_AT_LAST: /* When we get all AT results, parse them */ parse_at_results (task); return; case REGISTRATION_CHECK_STEP_DETAILED_REGISTRATION_STATE: mm_obj_dbg (self, "starting detailed registration state check"); /* We let classes implementing this interface to look for more detailed * registration info. */ if (!ctx->skip_detailed_registration_state && MM_IFACE_MODEM_CDMA_GET_INTERFACE (self)->get_detailed_registration_state && MM_IFACE_MODEM_CDMA_GET_INTERFACE (self)->get_detailed_registration_state_finish) { /* We pass the CDMA1x/EVDO registration states we got up to now. * If the implementation can't improve the detail, it must either * return the values it already got as input, or issue an error, * and we'll assume it couldn't get any better value. */ MM_IFACE_MODEM_CDMA_GET_INTERFACE (self)->get_detailed_registration_state ( self, ctx->cdma1x_state, ctx->evdo_state, (GAsyncReadyCallback)get_detailed_registration_state_ready, task); return; } mm_obj_dbg (self, " skipping detailed registration state check"); ctx->step++; /* fall through */ case REGISTRATION_CHECK_STEP_LAST: /* We are done without errors! */ mm_obj_dbg (self, "all CDMA registration state checks done"); mm_iface_modem_cdma_update_cdma1x_registration_state (self, ctx->cdma1x_state, ctx->cdma1x_sid, ctx->cdma1x_nid); mm_iface_modem_cdma_update_evdo_registration_state (self, ctx->evdo_state); g_task_return_boolean (task, TRUE); g_object_unref (task); return; default: break; } g_assert_not_reached (); } static void custom_run_registration_checks_ready (MMIfaceModemCdma *self, GAsyncResult *res, GTask *task) { GError *error = NULL; if (!MM_IFACE_MODEM_CDMA_GET_INTERFACE (self)->run_registration_checks_finish (self, res, &error)) g_task_return_error (task, error); else g_task_return_boolean (task, TRUE); g_object_unref (task); } void mm_iface_modem_cdma_run_registration_checks (MMIfaceModemCdma *self, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; gboolean cdma1x_supported; gboolean evdo_supported; task = g_task_new (self, NULL, callback, user_data); g_object_get (self, MM_IFACE_MODEM_CDMA_EVDO_NETWORK_SUPPORTED, &evdo_supported, MM_IFACE_MODEM_CDMA_CDMA1X_NETWORK_SUPPORTED, &cdma1x_supported, NULL); mm_obj_dbg (self, "running registration checks (CDMA1x: '%s', EV-DO: '%s')", cdma1x_supported ? "yes" : "no", evdo_supported ? "yes" : "no"); if (MM_IFACE_MODEM_CDMA_GET_INTERFACE (self)->run_registration_checks && MM_IFACE_MODEM_CDMA_GET_INTERFACE (self)->run_registration_checks_finish) { /* Plugins implementing full custom registration checks shouldn't implement * sub-steps of the generic registration check sequence */ g_warn_if_fail (MM_IFACE_MODEM_CDMA_GET_INTERFACE (self)->setup_registration_checks != NULL); g_warn_if_fail (MM_IFACE_MODEM_CDMA_GET_INTERFACE (self)->setup_registration_checks_finish != NULL); g_warn_if_fail (MM_IFACE_MODEM_CDMA_GET_INTERFACE (self)->get_call_manager_state != NULL); g_warn_if_fail (MM_IFACE_MODEM_CDMA_GET_INTERFACE (self)->get_call_manager_state_finish != NULL); g_warn_if_fail (MM_IFACE_MODEM_CDMA_GET_INTERFACE (self)->get_hdr_state != NULL); g_warn_if_fail (MM_IFACE_MODEM_CDMA_GET_INTERFACE (self)->get_hdr_state_finish != NULL); g_warn_if_fail (MM_IFACE_MODEM_CDMA_GET_INTERFACE (self)->get_service_status != NULL); g_warn_if_fail (MM_IFACE_MODEM_CDMA_GET_INTERFACE (self)->get_service_status_finish != NULL); g_warn_if_fail (MM_IFACE_MODEM_CDMA_GET_INTERFACE (self)->get_cdma1x_serving_system != NULL); g_warn_if_fail (MM_IFACE_MODEM_CDMA_GET_INTERFACE (self)->get_cdma1x_serving_system_finish != NULL); g_warn_if_fail (MM_IFACE_MODEM_CDMA_GET_INTERFACE (self)->get_detailed_registration_state != NULL); g_warn_if_fail (MM_IFACE_MODEM_CDMA_GET_INTERFACE (self)->get_detailed_registration_state_finish != NULL); MM_IFACE_MODEM_CDMA_GET_INTERFACE (self)->run_registration_checks ( self, cdma1x_supported, evdo_supported, (GAsyncReadyCallback)custom_run_registration_checks_ready, task); } else { RunRegistrationChecksContext *ctx; ctx = g_new0 (RunRegistrationChecksContext, 1); ctx->cdma1x_state = MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN; ctx->evdo_state = MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN; ctx->call_manager_system_mode = QCDM_CMD_CM_SUBSYS_STATE_INFO_SYSTEM_MODE_NO_SERVICE; ctx->hdr_session_state = QCDM_CMD_HDR_SUBSYS_STATE_INFO_SESSION_STATE_CLOSED; ctx->hdr_almp_state = QCDM_CMD_HDR_SUBSYS_STATE_INFO_ALMP_STATE_INACTIVE; ctx->hdr_hybrid_mode = 0; ctx->evdo_supported = evdo_supported; ctx->cdma1x_supported = cdma1x_supported; g_task_set_task_data (task, ctx, g_free); registration_check_step (task); } } /*****************************************************************************/ void mm_iface_modem_cdma_update_access_technologies (MMIfaceModemCdma *self, MMModemAccessTechnology access_tech) { MMModemCdmaRegistrationState cdma1x_state; MMModemCdmaRegistrationState evdo_state; g_object_get (self, MM_IFACE_MODEM_CDMA_CDMA1X_REGISTRATION_STATE, &cdma1x_state, MM_IFACE_MODEM_CDMA_EVDO_REGISTRATION_STATE, &evdo_state, NULL); /* Even if registration state didn't change, report access technology, * but only if something valid to report */ if (cdma1x_state == MM_MODEM_CDMA_REGISTRATION_STATE_HOME || cdma1x_state == MM_MODEM_CDMA_REGISTRATION_STATE_ROAMING || cdma1x_state == MM_MODEM_CDMA_REGISTRATION_STATE_REGISTERED || evdo_state == MM_MODEM_CDMA_REGISTRATION_STATE_HOME || evdo_state == MM_MODEM_CDMA_REGISTRATION_STATE_ROAMING || evdo_state == MM_MODEM_CDMA_REGISTRATION_STATE_REGISTERED) { if (access_tech != MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN) mm_iface_modem_update_access_technologies (MM_IFACE_MODEM (self), access_tech, MM_IFACE_MODEM_CDMA_ALL_ACCESS_TECHNOLOGIES_MASK); } else mm_iface_modem_update_access_technologies (MM_IFACE_MODEM (self), MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN, MM_IFACE_MODEM_CDMA_ALL_ACCESS_TECHNOLOGIES_MASK); } void mm_iface_modem_cdma_update_evdo_registration_state (MMIfaceModemCdma *self, MMModemCdmaRegistrationState state) { MmGdbusModemCdma *skeleton = NULL; gboolean supported = FALSE; g_object_get (self, MM_IFACE_MODEM_CDMA_EVDO_NETWORK_SUPPORTED, &supported, MM_IFACE_MODEM_CDMA_DBUS_SKELETON, &skeleton, NULL); if (!skeleton) return; if (supported) { /* The property in the interface is bound to the property * in the skeleton, so just updating here is enough */ g_object_set (self, MM_IFACE_MODEM_CDMA_EVDO_REGISTRATION_STATE, state, NULL); switch (state) { case MM_MODEM_CDMA_REGISTRATION_STATE_REGISTERED: case MM_MODEM_CDMA_REGISTRATION_STATE_HOME: case MM_MODEM_CDMA_REGISTRATION_STATE_ROAMING: mm_iface_modem_update_subsystem_state (MM_IFACE_MODEM (self), SUBSYSTEM_EVDO, MM_MODEM_STATE_REGISTERED, MM_MODEM_STATE_CHANGE_REASON_UNKNOWN); break; case MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN: mm_iface_modem_update_subsystem_state (MM_IFACE_MODEM (self), SUBSYSTEM_EVDO, MM_MODEM_STATE_ENABLED, MM_MODEM_STATE_CHANGE_REASON_UNKNOWN); break; default: g_assert_not_reached (); break; } } g_object_unref (skeleton); } void mm_iface_modem_cdma_update_cdma1x_registration_state (MMIfaceModemCdma *self, MMModemCdmaRegistrationState state, guint sid, guint nid) { MmGdbusModemCdma *skeleton = NULL; gboolean supported = FALSE; g_object_get (self, MM_IFACE_MODEM_CDMA_CDMA1X_NETWORK_SUPPORTED, &supported, MM_IFACE_MODEM_CDMA_DBUS_SKELETON, &skeleton, NULL); if (!skeleton) return; if (supported) { /* The property in the interface is bound to the property * in the skeleton, so just updating here is enough */ g_object_set (self, MM_IFACE_MODEM_CDMA_CDMA1X_REGISTRATION_STATE, state, NULL); switch (state) { case MM_MODEM_CDMA_REGISTRATION_STATE_REGISTERED: case MM_MODEM_CDMA_REGISTRATION_STATE_HOME: case MM_MODEM_CDMA_REGISTRATION_STATE_ROAMING: mm_gdbus_modem_cdma_set_sid (skeleton, sid); mm_gdbus_modem_cdma_set_nid (skeleton, nid); mm_iface_modem_update_subsystem_state (MM_IFACE_MODEM (self), SUBSYSTEM_CDMA1X, MM_MODEM_STATE_REGISTERED, MM_MODEM_STATE_CHANGE_REASON_UNKNOWN); break; case MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN: if (mm_gdbus_modem_cdma_get_sid (skeleton) != MM_MODEM_CDMA_SID_UNKNOWN) mm_gdbus_modem_cdma_set_sid (skeleton, MM_MODEM_CDMA_SID_UNKNOWN); if (mm_gdbus_modem_cdma_get_nid (skeleton) != MM_MODEM_CDMA_NID_UNKNOWN) mm_gdbus_modem_cdma_set_nid (skeleton, MM_MODEM_CDMA_NID_UNKNOWN); mm_iface_modem_update_subsystem_state (MM_IFACE_MODEM (self), SUBSYSTEM_CDMA1X, MM_MODEM_STATE_ENABLED, MM_MODEM_STATE_CHANGE_REASON_UNKNOWN); break; default: g_assert_not_reached (); break; } } g_object_unref (skeleton); } /*****************************************************************************/ typedef struct { guint timeout_source; gboolean running; } RegistrationCheckContext; static void registration_check_context_free (RegistrationCheckContext *ctx) { if (ctx->timeout_source) g_source_remove (ctx->timeout_source); g_free (ctx); } static void periodic_registration_checks_ready (MMIfaceModemCdma *self, GAsyncResult *res) { RegistrationCheckContext *ctx; GError *error = NULL; mm_iface_modem_cdma_run_registration_checks_finish (self, res, &error); if (error) { mm_obj_dbg (self, "couldn't refresh CDMA registration status: %s", error->message); g_error_free (error); } /* Remove the running tag */ ctx = g_object_get_qdata (G_OBJECT (self), registration_check_context_quark); if (ctx) ctx->running = FALSE; } static gboolean periodic_registration_check (MMIfaceModemCdma *self) { RegistrationCheckContext *ctx; /* Only launch a new one if not one running already */ ctx = g_object_get_qdata (G_OBJECT (self), registration_check_context_quark); if (!ctx->running) { ctx->running = TRUE; mm_iface_modem_cdma_run_registration_checks ( self, (GAsyncReadyCallback)periodic_registration_checks_ready, NULL); } return G_SOURCE_CONTINUE; } static void periodic_registration_check_disable (MMIfaceModemCdma *self) { if (G_UNLIKELY (!registration_check_context_quark)) registration_check_context_quark = (g_quark_from_static_string ( REGISTRATION_CHECK_CONTEXT_TAG)); /* Overwriting the data will free the previous context */ g_object_set_qdata (G_OBJECT (self), registration_check_context_quark, NULL); mm_obj_dbg (self, "periodic CDMA registration checks disabled"); } static void periodic_registration_check_enable (MMIfaceModemCdma *self) { RegistrationCheckContext *ctx; if (G_UNLIKELY (!registration_check_context_quark)) registration_check_context_quark = (g_quark_from_static_string ( REGISTRATION_CHECK_CONTEXT_TAG)); ctx = g_object_get_qdata (G_OBJECT (self), registration_check_context_quark); /* If context is already there, we're already enabled */ if (ctx) return; /* Create context and keep it as object data */ mm_obj_dbg (self, "periodic CDMA registration checks enabled"); ctx = g_new0 (RegistrationCheckContext, 1); ctx->timeout_source = g_timeout_add_seconds (REGISTRATION_CHECK_TIMEOUT_SEC, (GSourceFunc)periodic_registration_check, self); g_object_set_qdata_full (G_OBJECT (self), registration_check_context_quark, ctx, (GDestroyNotify)registration_check_context_free); } /*****************************************************************************/ static GVariant * build_empty_dictionary (void) { GVariantBuilder builder; g_variant_builder_init (&builder, G_VARIANT_TYPE ("a{sv}")); return g_variant_builder_end (&builder); } void mm_iface_modem_cdma_update_activation_state (MMIfaceModemCdma *self, MMModemCdmaActivationState activation_state, const GError *activation_error) { MmGdbusModemCdma *skeleton = NULL; MMCdmaActivationError error = MM_CDMA_ACTIVATION_ERROR_NONE; g_object_get (self, MM_IFACE_MODEM_CDMA_DBUS_SKELETON, &skeleton, NULL); if (!skeleton) return; if (activation_error) { mm_obj_dbg (self, "activation failed: %s", activation_error->message); if (activation_error->domain == MM_CDMA_ACTIVATION_ERROR) error = activation_error->code; else { mm_obj_warn (self, "error given is not an activation error"); error = MM_CDMA_ACTIVATION_ERROR_UNKNOWN; } } /* Flush current change before signaling the state change, * so that clients get the proper state already in the * state-changed callback */ mm_gdbus_modem_cdma_set_activation_state (skeleton, activation_state); g_dbus_interface_skeleton_flush (G_DBUS_INTERFACE_SKELETON (skeleton)); /* We don't know what changed, so just return an empty dictionary for now */ mm_gdbus_modem_cdma_emit_activation_state_changed (skeleton, activation_state, error, build_empty_dictionary ()); g_object_unref (skeleton); } /*****************************************************************************/ typedef struct _DisablingContext DisablingContext; static void interface_disabling_step (GTask *task); typedef enum { DISABLING_STEP_FIRST, DISABLING_STEP_PERIODIC_REGISTRATION_CHECKS, DISABLING_STEP_DISABLE_UNSOLICITED_EVENTS, DISABLING_STEP_CLEANUP_UNSOLICITED_EVENTS, DISABLING_STEP_LAST } DisablingStep; struct _DisablingContext { DisablingStep step; MmGdbusModemCdma *skeleton; }; static void disabling_context_free (DisablingContext *ctx) { if (ctx->skeleton) g_object_unref (ctx->skeleton); g_free (ctx); } gboolean mm_iface_modem_cdma_disable_finish (MMIfaceModemCdma *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void disable_unsolicited_events_ready (MMIfaceModemCdma *self, GAsyncResult *res, GTask *task) { DisablingContext *ctx; GError *error = NULL; MM_IFACE_MODEM_CDMA_GET_INTERFACE (self)->disable_unsolicited_events_finish (self, res, &error); if (error) { mm_obj_dbg (self, "couldn't disable unsolicited events: %s", error->message); g_error_free (error); } /* Go on to next step */ ctx = g_task_get_task_data (task); ctx->step++; interface_disabling_step (task); } static void cleanup_unsolicited_events_ready (MMIfaceModemCdma *self, GAsyncResult *res, GTask *task) { DisablingContext *ctx; GError *error = NULL; MM_IFACE_MODEM_CDMA_GET_INTERFACE (self)->cleanup_unsolicited_events_finish (self, res, &error); if (error) { mm_obj_dbg (self, "couldn't cleanup unsolicited events: %s", error->message); g_error_free (error); } /* Go on to next step */ ctx = g_task_get_task_data (task); ctx->step++; interface_disabling_step (task); } static void interface_disabling_step (GTask *task) { MMIfaceModemCdma *self; DisablingContext *ctx; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); switch (ctx->step) { case DISABLING_STEP_FIRST: ctx->step++; /* fall through */ case DISABLING_STEP_PERIODIC_REGISTRATION_CHECKS: periodic_registration_check_disable (self); ctx->step++; /* fall through */ case DISABLING_STEP_DISABLE_UNSOLICITED_EVENTS: if (MM_IFACE_MODEM_CDMA_GET_INTERFACE (self)->disable_unsolicited_events && MM_IFACE_MODEM_CDMA_GET_INTERFACE (self)->disable_unsolicited_events_finish) { MM_IFACE_MODEM_CDMA_GET_INTERFACE (self)->disable_unsolicited_events ( self, (GAsyncReadyCallback)disable_unsolicited_events_ready, task); return; } ctx->step++; /* fall through */ case DISABLING_STEP_CLEANUP_UNSOLICITED_EVENTS: if (MM_IFACE_MODEM_CDMA_GET_INTERFACE (self)->cleanup_unsolicited_events && MM_IFACE_MODEM_CDMA_GET_INTERFACE (self)->cleanup_unsolicited_events_finish) { MM_IFACE_MODEM_CDMA_GET_INTERFACE (self)->cleanup_unsolicited_events ( self, (GAsyncReadyCallback)cleanup_unsolicited_events_ready, task); return; } ctx->step++; /* fall through */ case DISABLING_STEP_LAST: /* We are done without errors! */ g_task_return_boolean (task, TRUE); g_object_unref (task); return; default: break; } g_assert_not_reached (); } void mm_iface_modem_cdma_disable (MMIfaceModemCdma *self, GAsyncReadyCallback callback, gpointer user_data) { DisablingContext *ctx; GTask *task; ctx = g_new0 (DisablingContext, 1); ctx->step = DISABLING_STEP_FIRST; task = g_task_new (self, NULL, callback, user_data); g_task_set_task_data (task, ctx, (GDestroyNotify)disabling_context_free); g_object_get (self, MM_IFACE_MODEM_CDMA_DBUS_SKELETON, &ctx->skeleton, NULL); if (!ctx->skeleton) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't get interface skeleton"); g_object_unref (task); return; } interface_disabling_step (task); } /*****************************************************************************/ typedef struct _EnablingContext EnablingContext; static void interface_enabling_step (GTask *task); typedef enum { ENABLING_STEP_FIRST, ENABLING_STEP_SETUP_UNSOLICITED_EVENTS, ENABLING_STEP_ENABLE_UNSOLICITED_EVENTS, ENABLING_STEP_PERIODIC_REGISTRATION_CHECKS, ENABLING_STEP_LAST } EnablingStep; struct _EnablingContext { EnablingStep step; MmGdbusModemCdma *skeleton; }; static void enabling_context_free (EnablingContext *ctx) { if (ctx->skeleton) g_object_unref (ctx->skeleton); g_free (ctx); } gboolean mm_iface_modem_cdma_enable_finish (MMIfaceModemCdma *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void setup_unsolicited_events_ready (MMIfaceModemCdma *self, GAsyncResult *res, GTask *task) { EnablingContext *ctx; GError *error = NULL; MM_IFACE_MODEM_CDMA_GET_INTERFACE (self)->setup_unsolicited_events_finish (self, res, &error); if (error) { /* This error shouldn't be treated as critical */ mm_obj_dbg (self, "setting up unsolicited events failed: %s", error->message); g_error_free (error); } /* Go on to next step */ ctx = g_task_get_task_data (task); ctx->step++; interface_enabling_step (task); } static void enable_unsolicited_events_ready (MMIfaceModemCdma *self, GAsyncResult *res, GTask *task) { EnablingContext *ctx; GError *error = NULL; MM_IFACE_MODEM_CDMA_GET_INTERFACE (self)->enable_unsolicited_events_finish (self, res, &error); if (error) { /* This error shouldn't be treated as critical */ mm_obj_dbg (self, "enabling unsolicited events failed: %s", error->message); g_error_free (error); } /* Go on to next step */ ctx = g_task_get_task_data (task); ctx->step++; interface_enabling_step (task); } static void interface_enabling_step (GTask *task) { MMIfaceModemCdma *self; EnablingContext *ctx; /* Don't run new steps if we're cancelled */ if (g_task_return_error_if_cancelled (task)) { g_object_unref (task); return; } self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); switch (ctx->step) { case ENABLING_STEP_FIRST: ctx->step++; /* fall through */ case ENABLING_STEP_SETUP_UNSOLICITED_EVENTS: if (MM_IFACE_MODEM_CDMA_GET_INTERFACE (self)->setup_unsolicited_events && MM_IFACE_MODEM_CDMA_GET_INTERFACE (self)->setup_unsolicited_events_finish) { MM_IFACE_MODEM_CDMA_GET_INTERFACE (self)->setup_unsolicited_events ( self, (GAsyncReadyCallback)setup_unsolicited_events_ready, task); return; } ctx->step++; /* fall through */ case ENABLING_STEP_ENABLE_UNSOLICITED_EVENTS: if (MM_IFACE_MODEM_CDMA_GET_INTERFACE (self)->enable_unsolicited_events && MM_IFACE_MODEM_CDMA_GET_INTERFACE (self)->enable_unsolicited_events_finish) { MM_IFACE_MODEM_CDMA_GET_INTERFACE (self)->enable_unsolicited_events ( self, (GAsyncReadyCallback)enable_unsolicited_events_ready, task); return; } ctx->step++; /* fall through */ case ENABLING_STEP_PERIODIC_REGISTRATION_CHECKS: periodic_registration_check_enable (self); ctx->step++; /* fall through */ case ENABLING_STEP_LAST: /* We are done without errors! */ g_task_return_boolean (task, TRUE); g_object_unref (task); return; default: break; } g_assert_not_reached (); } void mm_iface_modem_cdma_enable (MMIfaceModemCdma *self, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { EnablingContext *ctx; GTask *task; ctx = g_new0 (EnablingContext, 1); ctx->step = ENABLING_STEP_FIRST; task = g_task_new (self, cancellable, callback, user_data); g_task_set_task_data (task, ctx, (GDestroyNotify)enabling_context_free); g_object_get (self, MM_IFACE_MODEM_CDMA_DBUS_SKELETON, &ctx->skeleton, NULL); if (!ctx->skeleton) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't get interface skeleton"); g_object_unref (task); return; } interface_enabling_step (task); } /*****************************************************************************/ typedef struct _InitializationContext InitializationContext; static void interface_initialization_step (GTask *task); typedef enum { INITIALIZATION_STEP_FIRST, INITIALIZATION_STEP_MEID, INITIALIZATION_STEP_ESN, INITIALIZATION_STEP_ACTIVATION_STATE, INITIALIZATION_STEP_LAST } InitializationStep; struct _InitializationContext { MmGdbusModemCdma *skeleton; InitializationStep step; }; static void initialization_context_free (InitializationContext *ctx) { g_object_unref (ctx->skeleton); g_free (ctx); } #undef STR_REPLY_READY_FN #define STR_REPLY_READY_FN(NAME,DISPLAY) \ static void \ load_##NAME##_ready (MMIfaceModemCdma *self, \ GAsyncResult *res, \ GTask *task) \ { \ InitializationContext *ctx; \ GError *error = NULL; \ gchar *val; \ \ ctx = g_task_get_task_data (task); \ \ val = MM_IFACE_MODEM_CDMA_GET_INTERFACE (self)->load_##NAME##_finish (self, res, &error); \ mm_gdbus_modem_cdma_set_##NAME (ctx->skeleton, val); \ g_free (val); \ \ if (error) { \ mm_obj_warn (self, "couldn't load %s: %s", DISPLAY, error->message); \ g_error_free (error); \ } \ \ /* Go on to next step */ \ ctx->step++; \ interface_initialization_step (task); \ } STR_REPLY_READY_FN (meid, "MEID") STR_REPLY_READY_FN (esn, "ESN") static void load_activation_state_ready (MMIfaceModemCdma *self, GAsyncResult *res, GTask *task) { InitializationContext *ctx; GError *error = NULL; MMModemCdmaActivationState state; ctx = g_task_get_task_data (task); state = MM_IFACE_MODEM_CDMA_GET_INTERFACE (self)->load_activation_state_finish (self, res, &error); mm_gdbus_modem_cdma_set_activation_state (ctx->skeleton, state); if (error) { mm_obj_warn (self, "couldn't load activation state: %s", error->message); g_error_free (error); } /* Go on to next step */ ctx->step++; interface_initialization_step (task); } static void interface_initialization_step (GTask *task) { MMIfaceModemCdma *self; InitializationContext *ctx; /* Don't run new steps if we're cancelled */ if (g_task_return_error_if_cancelled (task)) { g_object_unref (task); return; } self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); switch (ctx->step) { case INITIALIZATION_STEP_FIRST: ctx->step++; /* fall through */ case INITIALIZATION_STEP_MEID: /* MEID value is meant to be loaded only once during the whole * lifetime of the modem. Therefore, if we already have it loaded, * don't try to load it again. */ if (!mm_gdbus_modem_cdma_get_meid (ctx->skeleton) && MM_IFACE_MODEM_CDMA_GET_INTERFACE (self)->load_meid && MM_IFACE_MODEM_CDMA_GET_INTERFACE (self)->load_meid_finish) { MM_IFACE_MODEM_CDMA_GET_INTERFACE (self)->load_meid ( self, (GAsyncReadyCallback)load_meid_ready, task); return; } ctx->step++; /* fall through */ case INITIALIZATION_STEP_ESN: /* ESN value is meant to be loaded only once during the whole * lifetime of the modem. Therefore, if we already have it loaded, * don't try to load it again. */ if (!mm_gdbus_modem_cdma_get_esn (ctx->skeleton) && MM_IFACE_MODEM_CDMA_GET_INTERFACE (self)->load_esn && MM_IFACE_MODEM_CDMA_GET_INTERFACE (self)->load_esn_finish) { MM_IFACE_MODEM_CDMA_GET_INTERFACE (self)->load_esn ( self, (GAsyncReadyCallback)load_esn_ready, task); return; } ctx->step++; /* fall through */ case INITIALIZATION_STEP_ACTIVATION_STATE: /* Initial activation state is meant to be loaded only once during the * whole lifetime of the modem. Therefore, if we already have it loaded, * don't try to load it again. */ if (mm_gdbus_modem_cdma_get_activation_state (ctx->skeleton) == MM_MODEM_CDMA_ACTIVATION_STATE_UNKNOWN && MM_IFACE_MODEM_CDMA_GET_INTERFACE (self)->load_activation_state && MM_IFACE_MODEM_CDMA_GET_INTERFACE (self)->load_activation_state_finish) { MM_IFACE_MODEM_CDMA_GET_INTERFACE (self)->load_activation_state ( self, (GAsyncReadyCallback)load_activation_state_ready, task); return; } ctx->step++; /* fall through */ case INITIALIZATION_STEP_LAST: /* We are done without errors! */ /* Handle method invocations */ g_signal_connect (ctx->skeleton, "handle-activate", G_CALLBACK (handle_activate), self); g_signal_connect (ctx->skeleton, "handle-activate-manual", G_CALLBACK (handle_activate_manual), self); /* Finally, export the new interface */ mm_gdbus_object_skeleton_set_modem_cdma (MM_GDBUS_OBJECT_SKELETON (self), MM_GDBUS_MODEM_CDMA (ctx->skeleton)); g_task_return_boolean (task, TRUE); g_object_unref (task); return; default: break; } g_assert_not_reached (); } gboolean mm_iface_modem_cdma_initialize_finish (MMIfaceModemCdma *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } void mm_iface_modem_cdma_initialize (MMIfaceModemCdma *self, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { InitializationContext *ctx; MmGdbusModemCdma *skeleton = NULL; GTask *task; /* Did we already create it? */ g_object_get (self, MM_IFACE_MODEM_CDMA_DBUS_SKELETON, &skeleton, NULL); if (!skeleton) { skeleton = mm_gdbus_modem_cdma_skeleton_new (); /* Set all initial property defaults */ mm_gdbus_modem_cdma_set_meid (skeleton, NULL); mm_gdbus_modem_cdma_set_esn (skeleton, NULL); mm_gdbus_modem_cdma_set_sid (skeleton, MM_MODEM_CDMA_SID_UNKNOWN); mm_gdbus_modem_cdma_set_nid (skeleton, MM_MODEM_CDMA_NID_UNKNOWN); mm_gdbus_modem_cdma_set_activation_state (skeleton, MM_MODEM_CDMA_ACTIVATION_STATE_UNKNOWN); /* Bind our Registration State properties */ g_object_bind_property (self, MM_IFACE_MODEM_CDMA_CDMA1X_REGISTRATION_STATE, skeleton, "cdma1x-registration-state", G_BINDING_DEFAULT | G_BINDING_SYNC_CREATE); g_object_bind_property (self, MM_IFACE_MODEM_CDMA_EVDO_REGISTRATION_STATE, skeleton, "evdo-registration-state", G_BINDING_DEFAULT | G_BINDING_SYNC_CREATE); g_object_set (self, MM_IFACE_MODEM_CDMA_DBUS_SKELETON, skeleton, NULL); } /* Perform async initialization here */ ctx = g_new0 (InitializationContext, 1); ctx->step = INITIALIZATION_STEP_FIRST; ctx->skeleton = skeleton; task = g_task_new (self, cancellable, callback, user_data); g_task_set_task_data (task, ctx, (GDestroyNotify)initialization_context_free); interface_initialization_step (task); } void mm_iface_modem_cdma_shutdown (MMIfaceModemCdma *self) { /* Remove RegistrationCheckContext object to make sure any pending * invocation of periodic_registration_check is cancelled before the * DBus skeleton is removed. */ if (G_LIKELY (registration_check_context_quark)) g_object_set_qdata (G_OBJECT (self), registration_check_context_quark, NULL); /* Unexport DBus interface and remove the skeleton */ mm_gdbus_object_skeleton_set_modem_cdma (MM_GDBUS_OBJECT_SKELETON (self), NULL); g_object_set (self, MM_IFACE_MODEM_CDMA_DBUS_SKELETON, NULL, NULL); } /*****************************************************************************/ static void iface_modem_cdma_init (gpointer g_iface) { static gboolean initialized = FALSE; if (initialized) return; /* Properties */ g_object_interface_install_property (g_iface, g_param_spec_object (MM_IFACE_MODEM_CDMA_DBUS_SKELETON, "CDMA DBus skeleton", "DBus skeleton for the CDMA interface", MM_GDBUS_TYPE_MODEM_CDMA_SKELETON, G_PARAM_READWRITE)); g_object_interface_install_property (g_iface, g_param_spec_enum (MM_IFACE_MODEM_CDMA_CDMA1X_REGISTRATION_STATE, "CDMA1x Registration State", "Registration state of the modem in the CDMA1x network", MM_TYPE_MODEM_CDMA_REGISTRATION_STATE, MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN, G_PARAM_READWRITE)); g_object_interface_install_property (g_iface, g_param_spec_enum (MM_IFACE_MODEM_CDMA_EVDO_REGISTRATION_STATE, "EV-DO Registration State", "Registration state of the modem in the EV-DO network", MM_TYPE_MODEM_CDMA_REGISTRATION_STATE, MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN, G_PARAM_READWRITE)); g_object_interface_install_property (g_iface, g_param_spec_boolean (MM_IFACE_MODEM_CDMA_CDMA1X_NETWORK_SUPPORTED, "CDMA1x network supported", "Whether the modem works in the CDMA1x network", TRUE, G_PARAM_READWRITE)); g_object_interface_install_property (g_iface, g_param_spec_boolean (MM_IFACE_MODEM_CDMA_EVDO_NETWORK_SUPPORTED, "EV-DO network supported", "Whether the modem works in the EV-DO network", TRUE, G_PARAM_READWRITE)); initialized = TRUE; } GType mm_iface_modem_cdma_get_type (void) { static GType iface_modem_cdma_type = 0; if (!G_UNLIKELY (iface_modem_cdma_type)) { static const GTypeInfo info = { sizeof (MMIfaceModemCdma), /* class_size */ iface_modem_cdma_init, /* base_init */ NULL, /* base_finalize */ }; iface_modem_cdma_type = g_type_register_static (G_TYPE_INTERFACE, "MMIfaceModemCdma", &info, 0); g_type_interface_add_prerequisite (iface_modem_cdma_type, MM_TYPE_IFACE_MODEM); } return iface_modem_cdma_type; } ModemManager-1.23.4-dev/src/mm-iface-modem-cdma.h000066400000000000000000000400611456466623000213450ustar00rootroot00000000000000/* -*- 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) 2011 Google, Inc. */ #ifndef MM_IFACE_MODEM_CDMA_H #define MM_IFACE_MODEM_CDMA_H #include #include #define _LIBMM_INSIDE_MM #include #include "mm-port-serial-at.h" #define MM_TYPE_IFACE_MODEM_CDMA (mm_iface_modem_cdma_get_type ()) #define MM_IFACE_MODEM_CDMA(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_IFACE_MODEM_CDMA, MMIfaceModemCdma)) #define MM_IS_IFACE_MODEM_CDMA(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_IFACE_MODEM_CDMA)) #define MM_IFACE_MODEM_CDMA_GET_INTERFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE ((obj), MM_TYPE_IFACE_MODEM_CDMA, MMIfaceModemCdma)) #define MM_IFACE_MODEM_CDMA_DBUS_SKELETON "iface-modem-cdma-dbus-skeleton" #define MM_IFACE_MODEM_CDMA_CDMA1X_REGISTRATION_STATE "iface-modem-cdma-cdma1x-registration-state" #define MM_IFACE_MODEM_CDMA_EVDO_REGISTRATION_STATE "iface-modem-cdma-evdo-registration-state" #define MM_IFACE_MODEM_CDMA_EVDO_NETWORK_SUPPORTED "iface-modem-cdma-evdo-network-supported" #define MM_IFACE_MODEM_CDMA_CDMA1X_NETWORK_SUPPORTED "iface-modem-cdma-cdma1x-network-supported" #define MM_IFACE_MODEM_CDMA_ALL_ACCESS_TECHNOLOGIES_MASK \ (MM_IFACE_MODEM_CDMA_ALL_CDMA1X_ACCESS_TECHNOLOGIES_MASK | \ MM_IFACE_MODEM_CDMA_ALL_EVDO_ACCESS_TECHNOLOGIES_MASK) #define MM_IFACE_MODEM_CDMA_ALL_EVDO_ACCESS_TECHNOLOGIES_MASK \ (MM_MODEM_ACCESS_TECHNOLOGY_EVDO0 | \ MM_MODEM_ACCESS_TECHNOLOGY_EVDOA | \ MM_MODEM_ACCESS_TECHNOLOGY_EVDOB) #define MM_IFACE_MODEM_CDMA_ALL_CDMA1X_ACCESS_TECHNOLOGIES_MASK \ (MM_MODEM_ACCESS_TECHNOLOGY_1XRTT) typedef struct _MMIfaceModemCdma MMIfaceModemCdma; struct _MMIfaceModemCdma { GTypeInterface g_iface; /* Loading of the MEID property */ void (*load_meid) (MMIfaceModemCdma *self, GAsyncReadyCallback callback, gpointer user_data); gchar * (*load_meid_finish) (MMIfaceModemCdma *self, GAsyncResult *res, GError **error); /* Loading of the ESN property */ void (*load_esn) (MMIfaceModemCdma *self, GAsyncReadyCallback callback, gpointer user_data); gchar * (*load_esn_finish) (MMIfaceModemCdma *self, GAsyncResult *res, GError **error); /* Loading of the initial activation state */ void (* load_activation_state) (MMIfaceModemCdma *self, GAsyncReadyCallback callback, gpointer user_data); MMModemCdmaActivationState (* load_activation_state_finish) (MMIfaceModemCdma *self, GAsyncResult *res, GError **error); /* Asynchronous setting up unsolicited events */ void (* setup_unsolicited_events) (MMIfaceModemCdma *self, GAsyncReadyCallback callback, gpointer user_data); gboolean (* setup_unsolicited_events_finish) (MMIfaceModemCdma *self, GAsyncResult *res, GError **error); /* Asynchronous enabling of unsolicited events */ void (*enable_unsolicited_events) (MMIfaceModemCdma *self, GAsyncReadyCallback callback, gpointer user_data); gboolean (*enable_unsolicited_events_finish) (MMIfaceModemCdma *self, GAsyncResult *res, GError **error); /* Asynchronous cleaning up of unsolicited events */ void (* cleanup_unsolicited_events) (MMIfaceModemCdma *self, GAsyncReadyCallback callback, gpointer user_data); gboolean (* cleanup_unsolicited_events_finish) (MMIfaceModemCdma *self, GAsyncResult *res, GError **error); /* Asynchronous disabling of unsolicited events */ void (*disable_unsolicited_events) (MMIfaceModemCdma *self, GAsyncReadyCallback callback, gpointer user_data); gboolean (*disable_unsolicited_events_finish) (MMIfaceModemCdma *self, GAsyncResult *res, GError **error); /* OTA activation */ void (* activate) (MMIfaceModemCdma *self, const gchar *carrier_code, GAsyncReadyCallback callback, gpointer user_data); gboolean (* activate_finish) (MMIfaceModemCdma *self, GAsyncResult *res, GError **error); /* Manual activation */ void (* activate_manual) (MMIfaceModemCdma *self, MMCdmaManualActivationProperties *properties, GAsyncReadyCallback callback, gpointer user_data); gboolean (* activate_manual_finish) (MMIfaceModemCdma *self, GAsyncResult *res, GError **error); /* Try to register in the CDMA network. This implementation is just making * sure that the modem is registered, and if it's not it will wait until it * is. */ void (* register_in_network) (MMIfaceModemCdma *self, guint max_registration_time, GAsyncReadyCallback callback, gpointer user_data); gboolean (*register_in_network_finish) (MMIfaceModemCdma *self, GAsyncResult *res, GError **error); /* Run CDMA1x/EV-DO registration state checks.. * Note that no registration state is returned, implementations should call * mm_iface_modem_cdma_update_registration_state(). * * NOTE: Plugins implementing this method will NOT execute the generic * registration check logic involving setup_registration_checks(), * get_call_manager_state(), get_hdr_state(), get_service_status(), * get_cdma1x_serving_system() and get_detailed_registration_state(). * * In other words, it is fine to leave this callback to NULL if you want * the generic steps to run. This callback may be implemented if there * is a completely independent way/command which can gather both CDMA1x * and EV-DO registration states, SID and NID. */ void (* run_registration_checks) (MMIfaceModemCdma *self, gboolean cdma1x_supported, gboolean evdo_supported, GAsyncReadyCallback callback, gpointer user_data); gboolean (* run_registration_checks_finish) (MMIfaceModemCdma *self, GAsyncResult *res, GError **error); /* The following steps will only be run if run_registration_checks() is NOT * given by the object implementing the interface */ /* Setup registration checks */ void (* setup_registration_checks) (MMIfaceModemCdma *self, GAsyncReadyCallback callback, gpointer user_data); gboolean (* setup_registration_checks_finish) (MMIfaceModemCdma *self, GAsyncResult *res, gboolean *skip_qcdm_call_manager_step, gboolean *skip_qcdm_hdr_step, gboolean *skip_at_cdma_service_status_step, gboolean *skip_at_cdma1x_serving_system_step, gboolean *skip_detailed_registration_state, GError **error); /* Get call manager state */ void (* get_call_manager_state) (MMIfaceModemCdma *self, GAsyncReadyCallback callback, gpointer user_data); gboolean (* get_call_manager_state_finish) (MMIfaceModemCdma *self, GAsyncResult *res, guint *operating_mode, guint *system_mode, GError **error); /* Get HDR state */ void (* get_hdr_state) (MMIfaceModemCdma *self, GAsyncReadyCallback callback, gpointer user_data); gboolean (* get_hdr_state_finish) (MMIfaceModemCdma *self, GAsyncResult *res, guint8 *hybrid_mode, guint8 *session_state, guint8 *almp_state, GError **error); /* Get service status */ void (* get_service_status) (MMIfaceModemCdma *self, GAsyncReadyCallback callback, gpointer user_data); gboolean (* get_service_status_finish) (MMIfaceModemCdma *self, GAsyncResult *res, gboolean *has_cdma_service, GError **error); /* Get CDMA1x serving system */ void (* get_cdma1x_serving_system) (MMIfaceModemCdma *self, GAsyncReadyCallback callback, gpointer user_data); gboolean (* get_cdma1x_serving_system_finish) (MMIfaceModemCdma *self, GAsyncResult *res, guint *class, guint *band, guint *sid, guint *nid, GError **error); /* Get detailed registration state */ void (* get_detailed_registration_state) (MMIfaceModemCdma *self, MMModemCdmaRegistrationState cdma1x_state, MMModemCdmaRegistrationState evdo_state, GAsyncReadyCallback callback, gpointer user_data); gboolean (* get_detailed_registration_state_finish) (MMIfaceModemCdma *self, GAsyncResult *res, MMModemCdmaRegistrationState *detailed_cdma1x_state, MMModemCdmaRegistrationState *detailed_evdo_state, GError **error); }; GType mm_iface_modem_cdma_get_type (void); G_DEFINE_AUTOPTR_CLEANUP_FUNC (MMIfaceModemCdma, g_object_unref) /* Initialize CDMA interface (async) */ void mm_iface_modem_cdma_initialize (MMIfaceModemCdma *self, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); gboolean mm_iface_modem_cdma_initialize_finish (MMIfaceModemCdma *self, GAsyncResult *res, GError **error); /* Enable CDMA interface (async) */ void mm_iface_modem_cdma_enable (MMIfaceModemCdma *self, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); gboolean mm_iface_modem_cdma_enable_finish (MMIfaceModemCdma *self, GAsyncResult *res, GError **error); /* Disable CDMA interface (async) */ void mm_iface_modem_cdma_disable (MMIfaceModemCdma *self, GAsyncReadyCallback callback, gpointer user_data); gboolean mm_iface_modem_cdma_disable_finish (MMIfaceModemCdma *self, GAsyncResult *res, GError **error); /* Shutdown CDMA interface */ void mm_iface_modem_cdma_shutdown (MMIfaceModemCdma *self); /* Objects implementing this interface can report new registration states, * access technologies and activation state changes */ void mm_iface_modem_cdma_update_cdma1x_registration_state (MMIfaceModemCdma *self, MMModemCdmaRegistrationState state, guint sid, guint nid); void mm_iface_modem_cdma_update_evdo_registration_state (MMIfaceModemCdma *self, MMModemCdmaRegistrationState state); void mm_iface_modem_cdma_update_access_technologies (MMIfaceModemCdma *self, MMModemAccessTechnology access_tech); void mm_iface_modem_cdma_update_activation_state (MMIfaceModemCdma *self, MMModemCdmaActivationState activation_state, const GError *activation_error); /* Run all registration checks */ void mm_iface_modem_cdma_run_registration_checks (MMIfaceModemCdma *self, GAsyncReadyCallback callback, gpointer user_data); gboolean mm_iface_modem_cdma_run_registration_checks_finish (MMIfaceModemCdma *self, GAsyncResult *res, GError **error); /* Register in network */ void mm_iface_modem_cdma_register_in_network (MMIfaceModemCdma *self, guint max_registration_time, GAsyncReadyCallback callback, gpointer user_data); gboolean mm_iface_modem_cdma_register_in_network_finish (MMIfaceModemCdma *self, GAsyncResult *res, GError **error); /* Bind properties for simple GetStatus() */ void mm_iface_modem_cdma_bind_simple_status (MMIfaceModemCdma *self, MMSimpleStatus *status); #endif /* MM_IFACE_MODEM_CDMA_H */ ModemManager-1.23.4-dev/src/mm-iface-modem-firmware.c000066400000000000000000000512231456466623000222520ustar00rootroot00000000000000/* -*- 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) 2012 Google, Inc. */ #include #define _LIBMM_INSIDE_MM #include #include "mm-iface-modem.h" #include "mm-iface-modem-firmware.h" #include "mm-error-helpers.h" #include "mm-log-object.h" #if defined WITH_QMI # include "mm-broadband-modem-qmi.h" #endif #if defined WITH_MBIM # include "mm-broadband-modem-mbim.h" #endif /*****************************************************************************/ void mm_iface_modem_firmware_bind_simple_status (MMIfaceModemFirmware *self, MMSimpleStatus *status) { } /*****************************************************************************/ /* Handle the 'List' method from DBus */ typedef struct { MMIfaceModemFirmware *self; MmGdbusModemFirmware *skeleton; GDBusMethodInvocation *invocation; GList *list; MMFirmwareProperties *current; } HandleListContext; static void handle_list_context_free (HandleListContext *ctx) { if (ctx->list) g_list_free_full (ctx->list, g_object_unref); if (ctx->current) g_object_unref (ctx->current); g_object_unref (ctx->skeleton); g_object_unref (ctx->invocation); g_object_unref (ctx->self); g_slice_free (HandleListContext, ctx); } static void load_current_ready (MMIfaceModemFirmware *self, GAsyncResult *res, HandleListContext *ctx) { GVariantBuilder builder; GList *l; GError *error = NULL; ctx->current = MM_IFACE_MODEM_FIRMWARE_GET_INTERFACE (self)->load_current_finish (self, res, &error); if (!ctx->current) { /* Not found isn't fatal */ if (!g_error_matches (error, MM_CORE_ERROR, MM_CORE_ERROR_NOT_FOUND)) { mm_dbus_method_invocation_take_error (ctx->invocation, error); handle_list_context_free (ctx); return; } mm_obj_dbg (self, "couldn't load current firmware image: %s", error->message); g_clear_error (&error); } /* Build array of dicts */ g_variant_builder_init (&builder, G_VARIANT_TYPE ("aa{sv}")); for (l = ctx->list; l; l = g_list_next (l)) { GVariant *dict; dict = mm_firmware_properties_get_dictionary (MM_FIRMWARE_PROPERTIES (l->data)); g_variant_builder_add_value (&builder, dict); g_variant_unref (dict); } mm_gdbus_modem_firmware_complete_list ( ctx->skeleton, ctx->invocation, (ctx->current ? mm_firmware_properties_get_unique_id (ctx->current) : ""), g_variant_builder_end (&builder)); handle_list_context_free (ctx); } static void load_list_ready (MMIfaceModemFirmware *self, GAsyncResult *res, HandleListContext *ctx) { GError *error = NULL; ctx->list = MM_IFACE_MODEM_FIRMWARE_GET_INTERFACE (self)->load_list_finish (self, res, &error); if (!ctx->list) { /* Not found isn't fatal */ if (!g_error_matches (error, MM_CORE_ERROR, MM_CORE_ERROR_NOT_FOUND)) { mm_dbus_method_invocation_take_error (ctx->invocation, error); handle_list_context_free (ctx); return; } mm_obj_dbg (self, "couldn't load firmware image list: %s", error->message); g_clear_error (&error); } MM_IFACE_MODEM_FIRMWARE_GET_INTERFACE (self)->load_current (MM_IFACE_MODEM_FIRMWARE (self), (GAsyncReadyCallback)load_current_ready, ctx); } static void list_auth_ready (MMBaseModem *self, GAsyncResult *res, HandleListContext *ctx) { GError *error = NULL; if (!mm_base_modem_authorize_finish (self, res, &error)) { mm_dbus_method_invocation_take_error (ctx->invocation, error); handle_list_context_free (ctx); return; } if (!MM_IFACE_MODEM_FIRMWARE_GET_INTERFACE (self)->load_list || !MM_IFACE_MODEM_FIRMWARE_GET_INTERFACE (self)->load_list_finish || !MM_IFACE_MODEM_FIRMWARE_GET_INTERFACE (self)->load_current || !MM_IFACE_MODEM_FIRMWARE_GET_INTERFACE (self)->load_current_finish) { mm_dbus_method_invocation_return_error_literal (ctx->invocation, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "Cannot list firmware: operation not supported"); handle_list_context_free (ctx); return; } MM_IFACE_MODEM_FIRMWARE_GET_INTERFACE (self)->load_list (MM_IFACE_MODEM_FIRMWARE (self), (GAsyncReadyCallback)load_list_ready, ctx); } static gboolean handle_list (MmGdbusModemFirmware *skeleton, GDBusMethodInvocation *invocation, MMIfaceModemFirmware *self) { HandleListContext *ctx; ctx = g_slice_new0 (HandleListContext); ctx->skeleton = g_object_ref (skeleton); ctx->invocation = g_object_ref (invocation); ctx->self = g_object_ref (self); mm_base_modem_authorize (MM_BASE_MODEM (self), invocation, MM_AUTHORIZATION_FIRMWARE, (GAsyncReadyCallback)list_auth_ready, ctx); return TRUE; } /*****************************************************************************/ /* Handle the 'Select' method from DBus */ typedef struct { MMIfaceModemFirmware *self; MmGdbusModemFirmware *skeleton; GDBusMethodInvocation *invocation; gchar *name; } HandleSelectContext; static void handle_select_context_free (HandleSelectContext *ctx) { g_free (ctx->name); g_object_unref (ctx->skeleton); g_object_unref (ctx->invocation); g_object_unref (ctx->self); g_slice_free (HandleSelectContext, ctx); } static void change_current_ready (MMIfaceModemFirmware *self, GAsyncResult *res, HandleSelectContext *ctx) { GError *error = NULL; if (!MM_IFACE_MODEM_FIRMWARE_GET_INTERFACE (self)->change_current_finish (self, res, &error)) mm_dbus_method_invocation_take_error (ctx->invocation, error); else mm_gdbus_modem_firmware_complete_select (ctx->skeleton, ctx->invocation); handle_select_context_free (ctx); } static void select_auth_ready (MMBaseModem *self, GAsyncResult *res, HandleSelectContext *ctx) { GError *error = NULL; if (!mm_base_modem_authorize_finish (self, res, &error)) { mm_dbus_method_invocation_take_error (ctx->invocation, error); handle_select_context_free (ctx); return; } if (!MM_IFACE_MODEM_FIRMWARE_GET_INTERFACE (self)->change_current || !MM_IFACE_MODEM_FIRMWARE_GET_INTERFACE (self)->change_current_finish) { mm_dbus_method_invocation_return_error_literal (ctx->invocation, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "Cannot select firmware: operation not supported"); handle_select_context_free (ctx); return; } MM_IFACE_MODEM_FIRMWARE_GET_INTERFACE (self)->change_current (MM_IFACE_MODEM_FIRMWARE (self), ctx->name, (GAsyncReadyCallback)change_current_ready, ctx); } static gboolean handle_select (MmGdbusModemFirmware *skeleton, GDBusMethodInvocation *invocation, const gchar *name, MMIfaceModemFirmware *self) { HandleSelectContext *ctx; ctx = g_slice_new (HandleSelectContext); ctx->skeleton = g_object_ref (skeleton); ctx->invocation = g_object_ref (invocation); ctx->self = g_object_ref (self); ctx->name = g_strdup (name); mm_base_modem_authorize (MM_BASE_MODEM (self), invocation, MM_AUTHORIZATION_FIRMWARE, (GAsyncReadyCallback)select_auth_ready, ctx); return TRUE; } /*****************************************************************************/ typedef struct _InitializationContext InitializationContext; static void interface_initialization_step (GTask *task); typedef enum { INITIALIZATION_STEP_FIRST, INITIALIZATION_STEP_UPDATE_SETTINGS, INITIALIZATION_STEP_LAST } InitializationStep; struct _InitializationContext { MmGdbusModemFirmware *skeleton; InitializationStep step; }; static void initialization_context_free (InitializationContext *ctx) { g_object_unref (ctx->skeleton); g_free (ctx); } gboolean mm_iface_modem_firmware_initialize_finish (MMIfaceModemFirmware *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static gboolean add_generic_version (MMBaseModem *self, MMFirmwareUpdateSettings *update_settings, GError **error) { const gchar *firmware_revision; const gchar *carrier_revision = NULL; g_autofree gchar *combined = NULL; gboolean ignore_carrier = FALSE; firmware_revision = mm_iface_modem_get_revision (MM_IFACE_MODEM (self)); if (!firmware_revision) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Unknown revision"); return FALSE; } g_object_get (self, MM_IFACE_MODEM_FIRMWARE_IGNORE_CARRIER, &ignore_carrier, NULL); if (!ignore_carrier) mm_iface_modem_get_carrier_config (MM_IFACE_MODEM (self), NULL, &carrier_revision); if (!carrier_revision) { mm_firmware_update_settings_set_version (update_settings, firmware_revision); return TRUE; } combined = g_strdup_printf ("%s - %s", firmware_revision, carrier_revision); mm_firmware_update_settings_set_version (update_settings, combined); return TRUE; } GPtrArray * mm_iface_firmware_build_generic_device_ids (MMIfaceModemFirmware *self, GError **error) { static const gchar *supported_subsystems[] = { "USB", "PCI" }; guint16 vid; guint16 pid; guint16 rid; MMPort *primary = NULL; const gchar *subsystem; const gchar *carrier_config = NULL; g_autoptr(GPtrArray) ids = NULL; guint i; gboolean ignore_carrier = FALSE; vid = mm_base_modem_get_vendor_id (MM_BASE_MODEM (self)); pid = mm_base_modem_get_product_id (MM_BASE_MODEM (self)); #if defined WITH_QMI if (MM_IS_BROADBAND_MODEM_QMI (self)) primary = MM_PORT (mm_broadband_modem_qmi_peek_port_qmi (MM_BROADBAND_MODEM_QMI (self))); #endif #if defined WITH_MBIM if (!primary && MM_IS_BROADBAND_MODEM_MBIM (self)) primary = MM_PORT (mm_broadband_modem_mbim_peek_port_mbim (MM_BROADBAND_MODEM_MBIM (self))); #endif if (!primary) primary = MM_PORT (mm_base_modem_peek_port_primary (MM_BASE_MODEM (self))); g_assert (primary != NULL); rid = mm_kernel_device_get_physdev_revision (mm_port_peek_kernel_device (primary)); subsystem = mm_kernel_device_get_physdev_subsystem (mm_port_peek_kernel_device (primary)); if (!subsystem) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Unknown device subsystem"); return NULL; } for (i = 0; i < G_N_ELEMENTS (supported_subsystems); i++) { if (g_ascii_strcasecmp (supported_subsystems[i], subsystem) == 0) break; } if (i == G_N_ELEMENTS (supported_subsystems)) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Unsupported subsystem: %s", subsystem); return NULL; } g_object_get (self, MM_IFACE_MODEM_FIRMWARE_IGNORE_CARRIER, &ignore_carrier, NULL); if (!ignore_carrier) mm_iface_modem_get_carrier_config (MM_IFACE_MODEM (self), &carrier_config, NULL); ids = g_ptr_array_new_with_free_func (g_free); if (carrier_config) { g_autofree gchar *carrier = NULL; carrier = g_ascii_strup (carrier_config, -1); g_ptr_array_add (ids, g_strdup_printf ("%s\\VID_%04X&PID_%04X&REV_%04X&CARRIER_%s", supported_subsystems[i], vid, pid, rid, carrier)); } g_ptr_array_add (ids, g_strdup_printf ("%s\\VID_%04X&PID_%04X&REV_%04X", supported_subsystems[i], vid, pid, rid)); g_ptr_array_add (ids, g_strdup_printf ("%s\\VID_%04X&PID_%04X", supported_subsystems[i], vid, pid)); g_ptr_array_add (ids, g_strdup_printf ("%s\\VID_%04X", supported_subsystems[i], vid)); g_ptr_array_add (ids, NULL); return g_steal_pointer (&ids); } static void load_update_settings_ready (MMIfaceModemFirmware *self, GAsyncResult *res, GTask *task) { InitializationContext *ctx; MMFirmwareUpdateSettings *update_settings; GError *error = NULL; GVariant *variant = NULL; g_autoptr(GPtrArray) ids = NULL; ctx = g_task_get_task_data (task); update_settings = MM_IFACE_MODEM_FIRMWARE_GET_INTERFACE (self)->load_update_settings_finish (self, res, &error); if (!update_settings) { mm_obj_dbg (self, "couldn't load update settings: %s", error->message); g_error_free (error); goto out; } /* If the plugin didn't specify custom device ids, add the default ones ourselves */ if (!mm_firmware_update_settings_get_device_ids (update_settings)) { mm_obj_dbg (self, "No device ids set by plugin, adding generic ids"); ids = mm_iface_firmware_build_generic_device_ids (self, &error); if (error) { mm_obj_warn (self, "couldn't build device ids: %s", error->message); g_error_free (error); g_clear_object (&update_settings); goto out; } mm_firmware_update_settings_set_device_ids (update_settings, (const gchar **)ids->pdata); } /* If the plugin didn't specify custom version, add the default one ourselves */ if (!mm_firmware_update_settings_get_version (update_settings) && !add_generic_version (MM_BASE_MODEM (self), update_settings, &error)) { mm_obj_warn (self, "couldn't set version: %s", error->message); g_error_free (error); g_clear_object (&update_settings); goto out; } out: if (update_settings) { variant = mm_firmware_update_settings_get_variant (update_settings); g_object_unref (update_settings); } mm_gdbus_modem_firmware_set_update_settings (ctx->skeleton, variant); if (variant) g_variant_unref (variant); /* Go on to next step */ ctx->step++; interface_initialization_step (task); } static void interface_initialization_step (GTask *task) { MMIfaceModemFirmware *self; InitializationContext *ctx; /* Don't run new steps if we're cancelled */ if (g_task_return_error_if_cancelled (task)) { g_object_unref (task); return; } self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); switch (ctx->step) { case INITIALIZATION_STEP_FIRST: ctx->step++; /* fall through */ case INITIALIZATION_STEP_UPDATE_SETTINGS: if (MM_IFACE_MODEM_FIRMWARE_GET_INTERFACE (self)->load_update_settings && MM_IFACE_MODEM_FIRMWARE_GET_INTERFACE (self)->load_update_settings_finish) { MM_IFACE_MODEM_FIRMWARE_GET_INTERFACE (self)->load_update_settings ( self, (GAsyncReadyCallback)load_update_settings_ready, task); return; } ctx->step++; /* fall through */ case INITIALIZATION_STEP_LAST: /* We are done without errors! */ /* Handle method invocations */ g_object_connect (ctx->skeleton, "signal::handle-list", G_CALLBACK (handle_list), self, "signal::handle-select", G_CALLBACK (handle_select), self, NULL); /* Finally, export the new interface */ mm_gdbus_object_skeleton_set_modem_firmware (MM_GDBUS_OBJECT_SKELETON (self), MM_GDBUS_MODEM_FIRMWARE (ctx->skeleton)); g_task_return_boolean (task, TRUE); g_object_unref (task); return; default: break; } g_assert_not_reached (); } void mm_iface_modem_firmware_initialize (MMIfaceModemFirmware *self, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { InitializationContext *ctx; MmGdbusModemFirmware *skeleton = NULL; GTask *task; /* Did we already create it? */ g_object_get (self, MM_IFACE_MODEM_FIRMWARE_DBUS_SKELETON, &skeleton, NULL); if (!skeleton) { skeleton = mm_gdbus_modem_firmware_skeleton_new (); g_object_set (self, MM_IFACE_MODEM_FIRMWARE_DBUS_SKELETON, skeleton, NULL); } /* Perform async initialization here */ ctx = g_new0 (InitializationContext, 1); ctx->step = INITIALIZATION_STEP_FIRST; ctx->skeleton = skeleton; task = g_task_new (self, cancellable, callback, user_data); g_task_set_task_data (task, ctx, (GDestroyNotify)initialization_context_free); interface_initialization_step (task); } void mm_iface_modem_firmware_shutdown (MMIfaceModemFirmware *self) { g_return_if_fail (MM_IS_IFACE_MODEM_FIRMWARE (self)); /* Unexport DBus interface and remove the skeleton */ mm_gdbus_object_skeleton_set_modem_firmware (MM_GDBUS_OBJECT_SKELETON (self), NULL); g_object_set (self, MM_IFACE_MODEM_FIRMWARE_DBUS_SKELETON, NULL, NULL); } /*****************************************************************************/ static void iface_modem_firmware_init (gpointer g_iface) { static gboolean initialized = FALSE; if (initialized) return; /* Properties */ g_object_interface_install_property (g_iface, g_param_spec_object (MM_IFACE_MODEM_FIRMWARE_DBUS_SKELETON, "Firmware DBus skeleton", "DBus skeleton for the Firmware interface", MM_GDBUS_TYPE_MODEM_FIRMWARE_SKELETON, G_PARAM_READWRITE)); g_object_interface_install_property (g_iface, g_param_spec_boolean (MM_IFACE_MODEM_FIRMWARE_IGNORE_CARRIER, "Ignore carrier info in firmware details", "Whether carrier info (version, name) should be ignored when showing the firmware details", FALSE, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); initialized = TRUE; } GType mm_iface_modem_firmware_get_type (void) { static GType iface_modem_firmware_type = 0; if (!G_UNLIKELY (iface_modem_firmware_type)) { static const GTypeInfo info = { sizeof (MMIfaceModemFirmware), /* class_size */ iface_modem_firmware_init, /* base_init */ NULL, /* base_finalize */ }; iface_modem_firmware_type = g_type_register_static (G_TYPE_INTERFACE, "MMIfaceModemFirmware", &info, 0); g_type_interface_add_prerequisite (iface_modem_firmware_type, MM_TYPE_IFACE_MODEM); } return iface_modem_firmware_type; } ModemManager-1.23.4-dev/src/mm-iface-modem-firmware.h000066400000000000000000000110311456466623000222500ustar00rootroot00000000000000/* -*- 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) 2012 Google, Inc. */ #ifndef MM_IFACE_MODEM_FIRMWARE_H #define MM_IFACE_MODEM_FIRMWARE_H #include #include #define _LIBMM_INSIDE_MM #include #define MM_TYPE_IFACE_MODEM_FIRMWARE (mm_iface_modem_firmware_get_type ()) #define MM_IFACE_MODEM_FIRMWARE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_IFACE_MODEM_FIRMWARE, MMIfaceModemFirmware)) #define MM_IS_IFACE_MODEM_FIRMWARE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_IFACE_MODEM_FIRMWARE)) #define MM_IFACE_MODEM_FIRMWARE_GET_INTERFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE ((obj), MM_TYPE_IFACE_MODEM_FIRMWARE, MMIfaceModemFirmware)) #define MM_IFACE_MODEM_FIRMWARE_DBUS_SKELETON "iface-modem-firmware-dbus-skeleton" #define MM_IFACE_MODEM_FIRMWARE_IGNORE_CARRIER "iface-modem-firmware-ignore-carrier" typedef struct _MMIfaceModemFirmware MMIfaceModemFirmware; struct _MMIfaceModemFirmware { GTypeInterface g_iface; /* Get update settings (async) */ void (* load_update_settings) (MMIfaceModemFirmware *self, GAsyncReadyCallback callback, gpointer user_data); MMFirmwareUpdateSettings * (* load_update_settings_finish) (MMIfaceModemFirmware *self, GAsyncResult *res, GError **error); /* Get Firmware list (async) */ void (* load_list) (MMIfaceModemFirmware *self, GAsyncReadyCallback callback, gpointer user_data); GList * (* load_list_finish) (MMIfaceModemFirmware *self, GAsyncResult *res, GError **error); /* Get current firmware (async) */ void (* load_current) (MMIfaceModemFirmware *self, GAsyncReadyCallback callback, gpointer user_data); MMFirmwareProperties * (* load_current_finish) (MMIfaceModemFirmware *self, GAsyncResult *res, GError **error); /* Change current firmware (async) */ void (* change_current) (MMIfaceModemFirmware *self, const gchar *name, GAsyncReadyCallback callback, gpointer user_data); gboolean (* change_current_finish) (MMIfaceModemFirmware *self, GAsyncResult *res, GError **error); }; GType mm_iface_modem_firmware_get_type (void); G_DEFINE_AUTOPTR_CLEANUP_FUNC (MMIfaceModemFirmware, g_object_unref) /* Get generic device ids */ GPtrArray *mm_iface_firmware_build_generic_device_ids (MMIfaceModemFirmware *self, GError **error); /* Initialize Firmware interface (async) */ void mm_iface_modem_firmware_initialize (MMIfaceModemFirmware *self, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); gboolean mm_iface_modem_firmware_initialize_finish (MMIfaceModemFirmware *self, GAsyncResult *res, GError **error); /* Shutdown Firmware interface */ void mm_iface_modem_firmware_shutdown (MMIfaceModemFirmware *self); /* Bind properties for simple GetStatus() */ void mm_iface_modem_firmware_bind_simple_status (MMIfaceModemFirmware *self, MMSimpleStatus *status); #endif /* MM_IFACE_MODEM_FIRMWARE_H */ ModemManager-1.23.4-dev/src/mm-iface-modem-location.c000066400000000000000000002164001456466623000222460ustar00rootroot00000000000000/* -*- 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) 2012 Google, Inc. * Copyright (C) 2012 Lanedo GmbH * Copyright (C) 2012-2019 Aleksander Morgado */ #include #define _LIBMM_INSIDE_MM #include #include "mm-iface-modem.h" #include "mm-iface-modem-location.h" #include "mm-log-object.h" #include "mm-error-helpers.h" #include "mm-modem-helpers.h" #define MM_LOCATION_GPS_REFRESH_TIME_SECS 30 #define LOCATION_CONTEXT_TAG "location-context-tag" static GQuark location_context_quark; /*****************************************************************************/ void mm_iface_modem_location_bind_simple_status (MMIfaceModemLocation *self, MMSimpleStatus *status) { } /*****************************************************************************/ typedef struct { /* 3GPP location */ MMLocation3gpp *location_3gpp; /* GPS location */ time_t location_gps_nmea_last_time; MMLocationGpsNmea *location_gps_nmea; time_t location_gps_raw_last_time; MMLocationGpsRaw *location_gps_raw; /* CDMA BS location */ MMLocationCdmaBs *location_cdma_bs; } LocationContext; static void location_context_free (LocationContext *ctx) { if (ctx->location_3gpp) g_object_unref (ctx->location_3gpp); if (ctx->location_gps_nmea) g_object_unref (ctx->location_gps_nmea); if (ctx->location_gps_raw) g_object_unref (ctx->location_gps_raw); if (ctx->location_cdma_bs) g_object_unref (ctx->location_cdma_bs); g_free (ctx); } static void clear_location_context (MMIfaceModemLocation *self) { if (G_UNLIKELY (!location_context_quark)) location_context_quark = (g_quark_from_static_string ( LOCATION_CONTEXT_TAG)); /* Clear all location data */ g_object_set_qdata (G_OBJECT (self), location_context_quark, NULL); } static LocationContext * get_location_context (MMIfaceModemLocation *self) { LocationContext *ctx; if (G_UNLIKELY (!location_context_quark)) location_context_quark = (g_quark_from_static_string ( LOCATION_CONTEXT_TAG)); ctx = g_object_get_qdata (G_OBJECT (self), location_context_quark); if (!ctx) { /* Create context and keep it as object data */ ctx = g_new0 (LocationContext, 1); g_object_set_qdata_full ( G_OBJECT (self), location_context_quark, ctx, (GDestroyNotify)location_context_free); } return ctx; } /*****************************************************************************/ static GVariant * build_location_dictionary (GVariant *previous, MMLocation3gpp *location_3gpp, MMLocationGpsNmea *location_gps_nmea, MMLocationGpsRaw *location_gps_raw, MMLocationCdmaBs *location_cdma_bs) { GVariant *location_3gpp_value = NULL; GVariant *location_gps_nmea_value = NULL; GVariant *location_gps_raw_value = NULL; GVariant *location_cdma_bs_value = NULL; GVariantBuilder builder; /* If a previous dictionary given, parse its values */ if (previous) { guint source; GVariant *value; GVariantIter iter; g_variant_iter_init (&iter, previous); while (g_variant_iter_next (&iter, "{uv}", &source, &value)) { switch (source) { case MM_MODEM_LOCATION_SOURCE_3GPP_LAC_CI: g_assert (!location_3gpp_value); location_3gpp_value = value; break; case MM_MODEM_LOCATION_SOURCE_GPS_NMEA: g_assert (!location_gps_nmea_value); location_gps_nmea_value = value; break; case MM_MODEM_LOCATION_SOURCE_GPS_RAW: g_assert (!location_gps_raw_value); location_gps_raw_value = value; break; case MM_MODEM_LOCATION_SOURCE_CDMA_BS: g_assert (!location_cdma_bs_value); location_cdma_bs_value = value; break; case MM_MODEM_LOCATION_SOURCE_GPS_UNMANAGED: g_assert_not_reached (); case MM_MODEM_LOCATION_SOURCE_AGPS_MSA: g_assert_not_reached (); case MM_MODEM_LOCATION_SOURCE_AGPS_MSB: g_assert_not_reached (); default: g_warn_if_reached (); g_variant_unref (value); break; } } } /* Build the new one */ g_variant_builder_init (&builder, G_VARIANT_TYPE ("a{uv}")); /* If a new one given, use it */ if (location_3gpp) { if (location_3gpp_value) g_variant_unref (location_3gpp_value); location_3gpp_value = mm_location_3gpp_get_string_variant (location_3gpp); } if (location_3gpp_value) { g_assert (!g_variant_is_floating (location_3gpp_value)); g_variant_builder_add (&builder, "{uv}", MM_MODEM_LOCATION_SOURCE_3GPP_LAC_CI, location_3gpp_value); g_variant_unref (location_3gpp_value); } /* If a new one given, use it */ if (location_gps_nmea) { if (location_gps_nmea_value) g_variant_unref (location_gps_nmea_value); location_gps_nmea_value = mm_location_gps_nmea_get_string_variant (location_gps_nmea); } if (location_gps_nmea_value) { g_assert (!g_variant_is_floating (location_gps_nmea_value)); g_variant_builder_add (&builder, "{uv}", MM_MODEM_LOCATION_SOURCE_GPS_NMEA, location_gps_nmea_value); g_variant_unref (location_gps_nmea_value); } /* If a new one given, use it */ if (location_gps_raw) { if (location_gps_raw_value) g_variant_unref (location_gps_raw_value); location_gps_raw_value = mm_location_gps_raw_get_dictionary (location_gps_raw); } if (location_gps_raw_value) { g_assert (!g_variant_is_floating (location_gps_raw_value)); g_variant_builder_add (&builder, "{uv}", MM_MODEM_LOCATION_SOURCE_GPS_RAW, location_gps_raw_value); g_variant_unref (location_gps_raw_value); } /* If a new one given, use it */ if (location_cdma_bs) { if (location_cdma_bs_value) g_variant_unref (location_cdma_bs_value); location_cdma_bs_value = mm_location_cdma_bs_get_dictionary (location_cdma_bs); } if (location_cdma_bs_value) { g_assert (!g_variant_is_floating (location_cdma_bs_value)); g_variant_builder_add (&builder, "{uv}", MM_MODEM_LOCATION_SOURCE_CDMA_BS, location_cdma_bs_value); g_variant_unref (location_cdma_bs_value); } return g_variant_builder_end (&builder); } /*****************************************************************************/ static void notify_gps_location_update (MMIfaceModemLocation *self, MmGdbusModemLocation *skeleton, MMLocationGpsNmea *location_gps_nmea, MMLocationGpsRaw *location_gps_raw) { mm_obj_dbg (self, "GPS location updated"); /* We only update the property if we are supposed to signal * location */ if (mm_gdbus_modem_location_get_signals_location (skeleton)) mm_gdbus_modem_location_set_location ( skeleton, build_location_dictionary (mm_gdbus_modem_location_get_location (skeleton), NULL, location_gps_nmea, location_gps_raw, NULL)); } static void location_gps_update_nmea (MMIfaceModemLocation *self, const gchar *nmea_trace) { MmGdbusModemLocation *skeleton; LocationContext *ctx; gboolean update_nmea = FALSE; gboolean update_raw = FALSE; ctx = get_location_context (self); g_object_get (self, MM_IFACE_MODEM_LOCATION_DBUS_SKELETON, &skeleton, NULL); if (!skeleton) return; if (mm_gdbus_modem_location_get_enabled (skeleton) & MM_MODEM_LOCATION_SOURCE_GPS_NMEA) { g_assert (ctx->location_gps_nmea != NULL); if (mm_location_gps_nmea_add_trace (ctx->location_gps_nmea, nmea_trace) && (ctx->location_gps_nmea_last_time == 0 || time (NULL) - ctx->location_gps_nmea_last_time >= (glong)mm_gdbus_modem_location_get_gps_refresh_rate (skeleton))) { ctx->location_gps_nmea_last_time = time (NULL); update_nmea = TRUE; } } if (mm_gdbus_modem_location_get_enabled (skeleton) & MM_MODEM_LOCATION_SOURCE_GPS_RAW) { g_assert (ctx->location_gps_raw != NULL); if (mm_location_gps_raw_add_trace (ctx->location_gps_raw, nmea_trace) && (ctx->location_gps_raw_last_time == 0 || time (NULL) - ctx->location_gps_raw_last_time >= (glong)mm_gdbus_modem_location_get_gps_refresh_rate (skeleton))) { ctx->location_gps_raw_last_time = time (NULL); update_raw = TRUE; } } if (update_nmea || update_raw) notify_gps_location_update (self, skeleton, update_nmea ? ctx->location_gps_nmea : NULL, update_raw ? ctx->location_gps_raw : NULL); g_object_unref (skeleton); } void mm_iface_modem_location_gps_update (MMIfaceModemLocation *self, const gchar *nmea_trace) { /* Helper to debug GPS location related issues. Don't depend on a real GPS * fix for debugging, just use some random values to update */ #if 0 { const gchar *prefix = NULL; const gchar *lat = NULL; /* lat N/S just to test which one is used */ if (g_str_has_prefix (nmea_trace, "$GPGGA")) { prefix = "GPGGA"; lat = "S"; } else if (g_str_has_prefix (nmea_trace, "$GNGGA")) { prefix = "GNGGA"; lat = "N"; } if (prefix && lat) { g_autoptr(GString) str = NULL; g_autoptr(GDateTime) now = NULL; mm_obj_dbg (self, "GGA trace detected: %s", nmea_trace); now = g_date_time_new_now_utc (); str = g_string_new (""); g_string_append_printf (str, "$%s,%02u%02u%02u,4807.038,%s,01131.000,E,1,08,0.9,545.4,M,46.9,M,,*47", prefix, g_date_time_get_hour (now), g_date_time_get_minute (now), g_date_time_get_second (now), lat); location_gps_update_nmea (self, str->str); return; } } #endif location_gps_update_nmea (self, nmea_trace); } /*****************************************************************************/ static void notify_3gpp_location_update (MMIfaceModemLocation *self, MmGdbusModemLocation *skeleton, MMLocation3gpp *location_3gpp) { const gchar *operator_code; operator_code = mm_location_3gpp_get_operator_code (location_3gpp); mm_obj_dbg (self, "3GPP location updated " "(MCCMNC: '%s', location area code: '%04lX', tracking area code: '%06lX', cell ID: '%08lX')", operator_code ? operator_code : "", mm_location_3gpp_get_location_area_code (location_3gpp), mm_location_3gpp_get_tracking_area_code (location_3gpp), mm_location_3gpp_get_cell_id (location_3gpp)); /* We only update the property if we are supposed to signal * location */ if (mm_gdbus_modem_location_get_signals_location (skeleton)) mm_gdbus_modem_location_set_location ( skeleton, build_location_dictionary (mm_gdbus_modem_location_get_location (skeleton), location_3gpp, NULL, NULL, NULL)); } void mm_iface_modem_location_3gpp_update_operator_code (MMIfaceModemLocation *self, const gchar *operator_code) { MmGdbusModemLocation *skeleton; LocationContext *ctx; ctx = get_location_context (self); g_object_get (self, MM_IFACE_MODEM_LOCATION_DBUS_SKELETON, &skeleton, NULL); if (!skeleton) return; if (mm_gdbus_modem_location_get_enabled (skeleton) & MM_MODEM_LOCATION_SOURCE_3GPP_LAC_CI) { guint changed = 0; g_assert (ctx->location_3gpp != NULL); changed += mm_location_3gpp_set_operator_code (ctx->location_3gpp, operator_code); if (changed) notify_3gpp_location_update (self, skeleton, ctx->location_3gpp); } g_object_unref (skeleton); } void mm_iface_modem_location_3gpp_update_lac_tac_ci (MMIfaceModemLocation *self, gulong location_area_code, gulong tracking_area_code, gulong cell_id) { g_autoptr(MmGdbusModemLocationSkeleton) skeleton = NULL; LocationContext *ctx; guint changed = 0; gulong old_location_area_code; gulong old_tracking_area_code; gulong old_cell_id; g_object_get (self, MM_IFACE_MODEM_LOCATION_DBUS_SKELETON, &skeleton, NULL); if (!skeleton || !(mm_gdbus_modem_location_get_enabled (MM_GDBUS_MODEM_LOCATION (skeleton)) & MM_MODEM_LOCATION_SOURCE_3GPP_LAC_CI)) return; ctx = get_location_context (self); g_assert (ctx->location_3gpp != NULL); old_location_area_code = mm_location_3gpp_get_location_area_code (ctx->location_3gpp); old_tracking_area_code = mm_location_3gpp_get_tracking_area_code (ctx->location_3gpp); old_cell_id = mm_location_3gpp_get_cell_id (ctx->location_3gpp); /* Update LAC if given, and clear TAC unless a TAC is also given */ if (location_area_code) { if (old_location_area_code != location_area_code) { mm_obj_dbg (self, "3GPP location area code updated: '%04lX->%04lX'", old_location_area_code, location_area_code); mm_location_3gpp_set_location_area_code (ctx->location_3gpp, location_area_code); changed++; } if (!tracking_area_code) { if (old_tracking_area_code != 0) { mm_obj_dbg (self, "3GPP tracking area code cleared: '%06lX->%06lX'", old_tracking_area_code, tracking_area_code); mm_location_3gpp_set_tracking_area_code (ctx->location_3gpp, 0); changed++; } } } /* Update TAC if given, and clear LAC unless a LAC is also given */ if (tracking_area_code) { if (old_tracking_area_code != tracking_area_code) { mm_obj_dbg (self, "3GPP tracking area code updated: '%06lX->%06lX'", old_tracking_area_code, tracking_area_code); mm_location_3gpp_set_tracking_area_code (ctx->location_3gpp, tracking_area_code); changed++; } if (!location_area_code) { if (old_location_area_code != 0) { mm_obj_dbg (self, "3GPP location area code cleared: '%04lX->%04lX'", old_location_area_code, location_area_code); mm_location_3gpp_set_location_area_code (ctx->location_3gpp, 0); changed++; } } } /* Cell ID only updated if given. It is assumed that if LAC or TAC are given, CID is also given */ if (cell_id && (old_cell_id != cell_id)) { mm_obj_dbg (self, "3GPP cell id updated: '%08lX->%08lX'", old_cell_id, cell_id); mm_location_3gpp_set_cell_id (ctx->location_3gpp, cell_id); changed++; } if (changed) notify_3gpp_location_update (self, MM_GDBUS_MODEM_LOCATION (skeleton), ctx->location_3gpp); } void mm_iface_modem_location_3gpp_clear (MMIfaceModemLocation *self) { MmGdbusModemLocation *skeleton; LocationContext *ctx; ctx = get_location_context (self); g_object_get (self, MM_IFACE_MODEM_LOCATION_DBUS_SKELETON, &skeleton, NULL); if (!skeleton) return; if (mm_gdbus_modem_location_get_enabled (skeleton) & MM_MODEM_LOCATION_SOURCE_3GPP_LAC_CI) { g_assert (ctx->location_3gpp != NULL); if (mm_location_3gpp_reset (ctx->location_3gpp)) notify_3gpp_location_update (self, skeleton, ctx->location_3gpp); } g_object_unref (skeleton); } /*****************************************************************************/ static void notify_cdma_bs_location_update (MMIfaceModemLocation *self, MmGdbusModemLocation *skeleton, MMLocationCdmaBs *location_cdma_bs) { mm_obj_dbg (self, "CDMA base station location updated (longitude: '%lf', latitude: '%lf')", mm_location_cdma_bs_get_longitude (location_cdma_bs), mm_location_cdma_bs_get_latitude (location_cdma_bs)); /* We only update the property if we are supposed to signal * location */ if (mm_gdbus_modem_location_get_signals_location (skeleton)) mm_gdbus_modem_location_set_location ( skeleton, build_location_dictionary (mm_gdbus_modem_location_get_location (skeleton), NULL, NULL, NULL, location_cdma_bs)); } void mm_iface_modem_location_cdma_bs_update (MMIfaceModemLocation *self, gdouble longitude, gdouble latitude) { MmGdbusModemLocation *skeleton; LocationContext *ctx; ctx = get_location_context (self); g_object_get (self, MM_IFACE_MODEM_LOCATION_DBUS_SKELETON, &skeleton, NULL); if (!skeleton) return; if (mm_gdbus_modem_location_get_enabled (skeleton) & MM_MODEM_LOCATION_SOURCE_CDMA_BS) { if (mm_location_cdma_bs_set (ctx->location_cdma_bs, longitude, latitude)) notify_cdma_bs_location_update (self, skeleton, ctx->location_cdma_bs); } g_object_unref (skeleton); } void mm_iface_modem_location_cdma_bs_clear (MMIfaceModemLocation *self) { mm_iface_modem_location_cdma_bs_update (self, MM_LOCATION_LONGITUDE_UNKNOWN, MM_LOCATION_LATITUDE_UNKNOWN); } /*****************************************************************************/ static void update_location_source_status (MMIfaceModemLocation *self, MMModemLocationSource source, gboolean enabled) { MMModemLocationSource mask; MmGdbusModemLocation *skeleton = NULL; LocationContext *ctx; g_object_get (self, MM_IFACE_MODEM_LOCATION_DBUS_SKELETON, &skeleton, NULL); if (!skeleton) return; /* Update status in the interface */ mask = mm_gdbus_modem_location_get_enabled (skeleton); if (enabled) mask |= source; else mask &= ~source; /* Update status in the context */ ctx = get_location_context (self); switch (source) { case MM_MODEM_LOCATION_SOURCE_3GPP_LAC_CI: if (enabled) { if (!ctx->location_3gpp) ctx->location_3gpp = mm_location_3gpp_new (); } else g_clear_object (&ctx->location_3gpp); break; case MM_MODEM_LOCATION_SOURCE_GPS_NMEA: if (enabled) { if (!ctx->location_gps_nmea) ctx->location_gps_nmea = mm_location_gps_nmea_new (); } else g_clear_object (&ctx->location_gps_nmea); break; case MM_MODEM_LOCATION_SOURCE_GPS_RAW: if (enabled) { if (!ctx->location_gps_raw) ctx->location_gps_raw = mm_location_gps_raw_new (); } else g_clear_object (&ctx->location_gps_raw); break; case MM_MODEM_LOCATION_SOURCE_CDMA_BS: if (enabled) { if (!ctx->location_cdma_bs) ctx->location_cdma_bs = mm_location_cdma_bs_new (); } else g_clear_object (&ctx->location_cdma_bs); break; case MM_MODEM_LOCATION_SOURCE_GPS_UNMANAGED: case MM_MODEM_LOCATION_SOURCE_AGPS_MSA: case MM_MODEM_LOCATION_SOURCE_AGPS_MSB: case MM_MODEM_LOCATION_SOURCE_NONE: /* Nothing to setup in the context */ default: break; } mm_gdbus_modem_location_set_enabled (skeleton, mask); g_object_unref (skeleton); } /*****************************************************************************/ typedef struct { MmGdbusModemLocation *skeleton; MMModemLocationSource to_enable; MMModemLocationSource to_disable; MMModemLocationSource current; } SetupGatheringContext; static void setup_gathering_step (GTask *task); static void setup_gathering_context_free (SetupGatheringContext *ctx) { if (ctx->skeleton) g_object_unref (ctx->skeleton); g_free (ctx); } static gboolean setup_gathering_finish (MMIfaceModemLocation *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void enable_location_gathering_ready (MMIfaceModemLocation *self, GAsyncResult *res, GTask *task) { SetupGatheringContext *ctx; GError *error = NULL; ctx = g_task_get_task_data (task); if (!MM_IFACE_MODEM_LOCATION_GET_INTERFACE (self)->enable_location_gathering_finish (self, res, &error)) { gchar *str; update_location_source_status (self, ctx->current, FALSE); str = mm_modem_location_source_build_string_from_mask (ctx->current); g_prefix_error (&error, "Couldn't enable location '%s' gathering: ", str); g_task_return_error (task, error); g_object_unref (task); g_free (str); return; } /* Keep on with next ones... */ ctx->current = ctx->current << 1; setup_gathering_step (task); } static void disable_location_gathering_ready (MMIfaceModemLocation *self, GAsyncResult *res, GTask *task) { SetupGatheringContext *ctx; GError *error = NULL; ctx = g_task_get_task_data (task); if (!MM_IFACE_MODEM_LOCATION_GET_INTERFACE (self)->disable_location_gathering_finish (self, res, &error)) { gchar *str; /* Back to enabled then */ update_location_source_status (self, ctx->current, TRUE); str = mm_modem_location_source_build_string_from_mask (ctx->current); g_prefix_error (&error, "Couldn't disable location '%s' gathering: ", str); g_task_return_error (task, error); g_object_unref (task); g_free (str); return; } /* Keep on with next ones... */ ctx->current = ctx->current << 1; setup_gathering_step (task); } static void setup_gathering_step (GTask *task) { MMIfaceModemLocation *self; SetupGatheringContext *ctx; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); /* Are we done? */ if (ctx->to_enable == MM_MODEM_LOCATION_SOURCE_NONE && ctx->to_disable == MM_MODEM_LOCATION_SOURCE_NONE) { g_task_return_boolean (task, TRUE); g_object_unref (task); return; } while (ctx->current <= MM_MODEM_LOCATION_SOURCE_LAST) { gchar *source_str; if (ctx->to_enable & ctx->current) { /* Remove from mask */ ctx->to_enable &= ~ctx->current; /* We update the location source status before launching the * specific actions to enable the gathering, so that we are * able to get location updates while the gathering gets * enabled. */ update_location_source_status (self, ctx->current, TRUE); /* Plugins can run custom actions to enable location gathering */ if (MM_IFACE_MODEM_LOCATION_GET_INTERFACE (self)->enable_location_gathering && MM_IFACE_MODEM_LOCATION_GET_INTERFACE (self)->enable_location_gathering_finish) { MM_IFACE_MODEM_LOCATION_GET_INTERFACE (self)->enable_location_gathering ( MM_IFACE_MODEM_LOCATION (self), ctx->current, (GAsyncReadyCallback)enable_location_gathering_ready, task); return; } source_str = mm_modem_location_source_build_string_from_mask (ctx->current); mm_obj_dbg (self, "enabled location '%s' gathering...", source_str); g_free (source_str); } else if (ctx->to_disable & ctx->current) { /* Remove from mask */ ctx->to_disable &= ~ctx->current; update_location_source_status (self, ctx->current, FALSE); /* Plugins can run custom actions to disable location gathering */ if (MM_IFACE_MODEM_LOCATION_GET_INTERFACE (self)->disable_location_gathering && MM_IFACE_MODEM_LOCATION_GET_INTERFACE (self)->disable_location_gathering_finish) { MM_IFACE_MODEM_LOCATION_GET_INTERFACE (self)->disable_location_gathering ( MM_IFACE_MODEM_LOCATION (self), ctx->current, (GAsyncReadyCallback)disable_location_gathering_ready, task); return; } source_str = mm_modem_location_source_build_string_from_mask (ctx->current); mm_obj_dbg (self, "disabled location '%s' gathering...", source_str); g_free (source_str); } /* go on... */ ctx->current = ctx->current << 1; } /* We just need to finish now */ g_assert (ctx->to_enable == MM_MODEM_LOCATION_SOURCE_NONE); g_assert (ctx->to_disable == MM_MODEM_LOCATION_SOURCE_NONE); setup_gathering_step (task); } static void setup_gathering (MMIfaceModemLocation *self, MMModemLocationSource mask, GAsyncReadyCallback callback, gpointer user_data) { SetupGatheringContext *ctx; GTask *task; MMModemLocationSource currently_enabled; MMModemLocationSource source; gchar *str; gboolean allow_gps_unmanaged_always = FALSE; ctx = g_new (SetupGatheringContext, 1); task = g_task_new (self, NULL, callback, user_data); g_task_set_task_data (task, ctx, (GDestroyNotify)setup_gathering_context_free); g_object_get (self, MM_IFACE_MODEM_LOCATION_DBUS_SKELETON, &ctx->skeleton, MM_IFACE_MODEM_LOCATION_ALLOW_GPS_UNMANAGED_ALWAYS, &allow_gps_unmanaged_always, NULL); if (!ctx->skeleton) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't get interface skeleton"); g_object_unref (task); return; } /* Get current list of enabled sources */ currently_enabled = mm_gdbus_modem_location_get_enabled (ctx->skeleton); /* Reset the list of sources to enable or disable */ ctx->to_enable = MM_MODEM_LOCATION_SOURCE_NONE; ctx->to_disable = MM_MODEM_LOCATION_SOURCE_NONE; /* Loop through all known bits in the bitmask to enable/disable specific location sources */ for (source = MM_MODEM_LOCATION_SOURCE_FIRST; source <= MM_MODEM_LOCATION_SOURCE_LAST; source = source << 1) { /* skip unsupported sources */ if (!(mm_gdbus_modem_location_get_capabilities (ctx->skeleton) & source)) continue; str = mm_modem_location_source_build_string_from_mask (source); if (mask & source) { /* Source set in mask, need to enable if disabled */ if (currently_enabled & source) mm_obj_dbg (self, "location '%s' gathering is already enabled...", str); else ctx->to_enable |= source; } else { /* Source unset in mask, need to disable if enabled */ if (currently_enabled & source) ctx->to_disable |= source; else mm_obj_dbg (self, "location '%s' gathering is already disabled...", str); } g_free (str); } /* When standard GPS retrieval (RAW/NMEA) is enabled, we cannot enable the * UNMANAGED setup, and viceversa, unless explicitly allowed to do so by the * plugin implementation (e.g. if the RAW/NMEA sources don't use the same TTY * as the GPS UNMANAGED setup. */ if (!allow_gps_unmanaged_always && ((ctx->to_enable & MM_MODEM_LOCATION_SOURCE_GPS_UNMANAGED && currently_enabled & (MM_MODEM_LOCATION_SOURCE_GPS_RAW | MM_MODEM_LOCATION_SOURCE_GPS_NMEA)) || (ctx->to_enable & (MM_MODEM_LOCATION_SOURCE_GPS_RAW | MM_MODEM_LOCATION_SOURCE_GPS_NMEA) && currently_enabled & MM_MODEM_LOCATION_SOURCE_GPS_UNMANAGED) || (ctx->to_enable & (MM_MODEM_LOCATION_SOURCE_GPS_RAW | MM_MODEM_LOCATION_SOURCE_GPS_NMEA) && ctx->to_enable & MM_MODEM_LOCATION_SOURCE_GPS_UNMANAGED))) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Cannot have both unmanaged GPS and raw/nmea GPS enabled at the same time"); g_object_unref (task); return; } /* MSA A-GPS and MSB A-GPS cannot be set at the same time */ if ((ctx->to_enable & MM_MODEM_LOCATION_SOURCE_AGPS_MSA && currently_enabled & MM_MODEM_LOCATION_SOURCE_AGPS_MSB) || (ctx->to_enable & MM_MODEM_LOCATION_SOURCE_AGPS_MSB && currently_enabled & MM_MODEM_LOCATION_SOURCE_AGPS_MSA) || (ctx->to_enable & MM_MODEM_LOCATION_SOURCE_AGPS_MSA && ctx->to_enable & MM_MODEM_LOCATION_SOURCE_AGPS_MSB)) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Cannot have both MSA A-GPS and MSB A-GPS enabled at the same time"); g_object_unref (task); return; } if (ctx->to_enable != MM_MODEM_LOCATION_SOURCE_NONE) { str = mm_modem_location_source_build_string_from_mask (ctx->to_enable); mm_obj_dbg (self, "need to enable the following location sources: '%s'", str); g_free (str); } if (ctx->to_disable != MM_MODEM_LOCATION_SOURCE_NONE) { str = mm_modem_location_source_build_string_from_mask (ctx->to_disable); mm_obj_dbg (self, "need to disable the following location sources: '%s'", str); g_free (str); } /* Start enabling/disabling location sources */ ctx->current = MM_MODEM_LOCATION_SOURCE_FIRST; setup_gathering_step (task); } /*****************************************************************************/ typedef struct { MmGdbusModemLocation *skeleton; GDBusMethodInvocation *invocation; MMIfaceModemLocation *self; guint32 sources; gboolean signal_location; } HandleSetupContext; static void handle_setup_context_free (HandleSetupContext *ctx) { g_object_unref (ctx->skeleton); g_object_unref (ctx->invocation); g_object_unref (ctx->self); g_slice_free (HandleSetupContext, ctx); } static void setup_gathering_ready (MMIfaceModemLocation *self, GAsyncResult *res, HandleSetupContext *ctx) { GError *error = NULL; if (!setup_gathering_finish (self, res, &error)) mm_dbus_method_invocation_take_error (ctx->invocation, error); else mm_gdbus_modem_location_complete_setup (ctx->skeleton, ctx->invocation); handle_setup_context_free (ctx); } static void handle_setup_auth_ready (MMBaseModem *self, GAsyncResult *res, HandleSetupContext *ctx) { GError *error = NULL; MMModemState modem_state; MMModemLocationSource not_supported; MMModemLocationSource require_enabled; LocationContext *location_ctx; g_autofree gchar *str = NULL; if (!mm_base_modem_authorize_finish (self, res, &error)) { mm_dbus_method_invocation_take_error (ctx->invocation, error); handle_setup_context_free (ctx); return; } /* If any of the location sources being enabled is NOT supported, set error */ not_supported = ((mm_gdbus_modem_location_get_capabilities (ctx->skeleton) ^ ctx->sources) & ctx->sources); if (not_supported != MM_MODEM_LOCATION_SOURCE_NONE) { str = mm_modem_location_source_build_string_from_mask (not_supported); mm_dbus_method_invocation_return_error (ctx->invocation, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "Cannot enable unsupported location sources: '%s'", str); handle_setup_context_free (ctx); return; } modem_state = MM_MODEM_STATE_UNKNOWN; g_object_get (self, MM_IFACE_MODEM_STATE, &modem_state, NULL); /* Location sources that require any kind of network access may be * enabled only when the modem is enabled. Generic standalone GPS may be * enabled even without a SIM card */ require_enabled = ctx->sources & (MM_MODEM_LOCATION_SOURCE_3GPP_LAC_CI | MM_MODEM_LOCATION_SOURCE_CDMA_BS | MM_MODEM_LOCATION_SOURCE_AGPS_MSA | MM_MODEM_LOCATION_SOURCE_AGPS_MSB); if (require_enabled && (modem_state < MM_MODEM_STATE_ENABLED)) { str = mm_modem_location_source_build_string_from_mask (require_enabled); mm_dbus_method_invocation_return_error (ctx->invocation, MM_CORE_ERROR, MM_CORE_ERROR_WRONG_STATE, "Cannot enable location '%s': device not yet enabled", str); handle_setup_context_free (ctx); return; } /* Enable/disable location signaling */ location_ctx = get_location_context (ctx->self); if (mm_gdbus_modem_location_get_signals_location (ctx->skeleton) != ctx->signal_location) { mm_obj_dbg (self, "%s location signaling", ctx->signal_location ? "enabling" : "disabling"); mm_gdbus_modem_location_set_signals_location (ctx->skeleton, ctx->signal_location); if (ctx->signal_location) mm_gdbus_modem_location_set_location ( ctx->skeleton, build_location_dictionary (mm_gdbus_modem_location_get_location (ctx->skeleton), location_ctx->location_3gpp, location_ctx->location_gps_nmea, location_ctx->location_gps_raw, location_ctx->location_cdma_bs)); else mm_gdbus_modem_location_set_location ( ctx->skeleton, build_location_dictionary (NULL, NULL, NULL, NULL, NULL)); } str = mm_modem_location_source_build_string_from_mask (ctx->sources); mm_obj_dbg (self, "setting up location sources: '%s'", str); /* Go on to enable or disable the requested sources */ setup_gathering (ctx->self, ctx->sources, (GAsyncReadyCallback)setup_gathering_ready, ctx); } static gboolean handle_setup (MmGdbusModemLocation *skeleton, GDBusMethodInvocation *invocation, guint32 sources, gboolean signal_location, MMIfaceModemLocation *self) { HandleSetupContext *ctx; ctx = g_slice_new0 (HandleSetupContext); ctx->skeleton = g_object_ref (skeleton); ctx->invocation = g_object_ref (invocation); ctx->self = g_object_ref (self); ctx->sources = sources; ctx->signal_location = signal_location; mm_base_modem_authorize (MM_BASE_MODEM (self), invocation, MM_AUTHORIZATION_DEVICE_CONTROL, (GAsyncReadyCallback)handle_setup_auth_ready, ctx); return TRUE; } /*****************************************************************************/ typedef struct { MmGdbusModemLocation *skeleton; GDBusMethodInvocation *invocation; MMIfaceModemLocation *self; gchar *supl; } HandleSetSuplServerContext; static void handle_set_supl_server_context_free (HandleSetSuplServerContext *ctx) { g_object_unref (ctx->skeleton); g_object_unref (ctx->invocation); g_object_unref (ctx->self); g_free (ctx->supl); g_slice_free (HandleSetSuplServerContext, ctx); } static void set_supl_server_ready (MMIfaceModemLocation *self, GAsyncResult *res, HandleSetSuplServerContext *ctx) { GError *error = NULL; if (!MM_IFACE_MODEM_LOCATION_GET_INTERFACE (self)->set_supl_server_finish (self, res, &error)) mm_dbus_method_invocation_take_error (ctx->invocation, error); else { mm_gdbus_modem_location_set_supl_server (ctx->skeleton, ctx->supl); mm_gdbus_modem_location_complete_set_supl_server (ctx->skeleton, ctx->invocation); } handle_set_supl_server_context_free (ctx); } static void handle_set_supl_server_auth_ready (MMBaseModem *self, GAsyncResult *res, HandleSetSuplServerContext *ctx) { GError *error = NULL; if (!mm_base_modem_authorize_finish (self, res, &error)) { mm_dbus_method_invocation_take_error (ctx->invocation, error); handle_set_supl_server_context_free (ctx); return; } /* If A-GPS is NOT supported, set error */ if (!(mm_gdbus_modem_location_get_capabilities (ctx->skeleton) & (MM_MODEM_LOCATION_SOURCE_AGPS_MSA | MM_MODEM_LOCATION_SOURCE_AGPS_MSB))) { mm_dbus_method_invocation_return_error_literal (ctx->invocation, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "Cannot set SUPL server: A-GPS not supported"); handle_set_supl_server_context_free (ctx); return; } /* Validate SUPL address string: either FQDN:PORT or IP:PORT */ if (!mm_parse_supl_address (ctx->supl, NULL, NULL, NULL, &error)) { mm_dbus_method_invocation_return_gerror (ctx->invocation, error); handle_set_supl_server_context_free (ctx); return; } /* Check if plugin implements it */ if (!MM_IFACE_MODEM_LOCATION_GET_INTERFACE (self)->set_supl_server || !MM_IFACE_MODEM_LOCATION_GET_INTERFACE (self)->set_supl_server_finish) { mm_dbus_method_invocation_return_error_literal (ctx->invocation, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "Cannot set SUPL server: not implemented"); handle_set_supl_server_context_free (ctx); return; } /* Request to change SUPL server */ MM_IFACE_MODEM_LOCATION_GET_INTERFACE (self)->set_supl_server (ctx->self, ctx->supl, (GAsyncReadyCallback)set_supl_server_ready, ctx); } static gboolean handle_set_supl_server (MmGdbusModemLocation *skeleton, GDBusMethodInvocation *invocation, const gchar *supl, MMIfaceModemLocation *self) { HandleSetSuplServerContext *ctx; ctx = g_slice_new0 (HandleSetSuplServerContext); ctx->skeleton = g_object_ref (skeleton); ctx->invocation = g_object_ref (invocation); ctx->self = g_object_ref (self); ctx->supl = g_strdup (supl); mm_base_modem_authorize (MM_BASE_MODEM (self), invocation, MM_AUTHORIZATION_DEVICE_CONTROL, (GAsyncReadyCallback)handle_set_supl_server_auth_ready, ctx); return TRUE; } /*****************************************************************************/ typedef struct { MmGdbusModemLocation *skeleton; GDBusMethodInvocation *invocation; MMIfaceModemLocation *self; GVariant *datav; } HandleInjectAssistanceDataContext; static void handle_inject_assistance_data_context_free (HandleInjectAssistanceDataContext *ctx) { g_object_unref (ctx->skeleton); g_object_unref (ctx->invocation); g_object_unref (ctx->self); g_variant_unref (ctx->datav); g_slice_free (HandleInjectAssistanceDataContext, ctx); } static void inject_assistance_data_ready (MMIfaceModemLocation *self, GAsyncResult *res, HandleInjectAssistanceDataContext *ctx) { GError *error = NULL; if (!MM_IFACE_MODEM_LOCATION_GET_INTERFACE (self)->inject_assistance_data_finish (self, res, &error)) mm_dbus_method_invocation_take_error (ctx->invocation, error); else mm_gdbus_modem_location_complete_inject_assistance_data (ctx->skeleton, ctx->invocation); handle_inject_assistance_data_context_free (ctx); } static void handle_inject_assistance_data_auth_ready (MMBaseModem *self, GAsyncResult *res, HandleInjectAssistanceDataContext *ctx) { GError *error = NULL; const guint8 *data; gsize data_size; if (!mm_base_modem_authorize_finish (self, res, &error)) { mm_dbus_method_invocation_take_error (ctx->invocation, error); handle_inject_assistance_data_context_free (ctx); return; } /* If the type is NOT supported, set error */ if (mm_gdbus_modem_location_get_supported_assistance_data (ctx->skeleton) == MM_MODEM_LOCATION_ASSISTANCE_DATA_TYPE_NONE) { mm_dbus_method_invocation_return_error_literal (ctx->invocation, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "Cannot inject assistance data: ununsupported"); handle_inject_assistance_data_context_free (ctx); return; } /* Check if plugin implements it */ if (!MM_IFACE_MODEM_LOCATION_GET_INTERFACE (self)->inject_assistance_data || !MM_IFACE_MODEM_LOCATION_GET_INTERFACE (self)->inject_assistance_data_finish) { mm_dbus_method_invocation_return_error_literal (ctx->invocation, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "Cannot inject assistance data: not implemented"); handle_inject_assistance_data_context_free (ctx); return; } data = (const guint8 *) g_variant_get_fixed_array (ctx->datav, &data_size, sizeof (guint8)); /* Request to inject assistance data */ MM_IFACE_MODEM_LOCATION_GET_INTERFACE (self)->inject_assistance_data (ctx->self, data, data_size, (GAsyncReadyCallback)inject_assistance_data_ready, ctx); } static gboolean handle_inject_assistance_data (MmGdbusModemLocation *skeleton, GDBusMethodInvocation *invocation, GVariant *datav, MMIfaceModemLocation *self) { HandleInjectAssistanceDataContext *ctx; ctx = g_slice_new0 (HandleInjectAssistanceDataContext); ctx->skeleton = g_object_ref (skeleton); ctx->invocation = g_object_ref (invocation); ctx->self = g_object_ref (self); ctx->datav = g_variant_ref (datav); mm_base_modem_authorize (MM_BASE_MODEM (self), invocation, MM_AUTHORIZATION_DEVICE_CONTROL, (GAsyncReadyCallback)handle_inject_assistance_data_auth_ready, ctx); return TRUE; } /*****************************************************************************/ typedef struct { MmGdbusModemLocation *skeleton; GDBusMethodInvocation *invocation; MMIfaceModemLocation *self; guint rate; } HandleSetGpsRefreshRateContext; static void handle_set_gps_refresh_rate_context_free (HandleSetGpsRefreshRateContext *ctx) { g_object_unref (ctx->skeleton); g_object_unref (ctx->invocation); g_object_unref (ctx->self); g_slice_free (HandleSetGpsRefreshRateContext, ctx); } static void handle_set_gps_refresh_rate_auth_ready (MMBaseModem *self, GAsyncResult *res, HandleSetGpsRefreshRateContext *ctx) { GError *error = NULL; if (!mm_base_modem_authorize_finish (self, res, &error)) { mm_dbus_method_invocation_take_error (ctx->invocation, error); handle_set_gps_refresh_rate_context_free (ctx); return; } /* If GPS is NOT supported, set error */ if (!(mm_gdbus_modem_location_get_capabilities (ctx->skeleton) & ((MM_MODEM_LOCATION_SOURCE_GPS_RAW | MM_MODEM_LOCATION_SOURCE_GPS_NMEA)))) { mm_dbus_method_invocation_return_error_literal (ctx->invocation, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "Cannot set GPS refresh rate: GPS not supported"); handle_set_gps_refresh_rate_context_free (ctx); return; } /* Set the new rate in the interface */ mm_gdbus_modem_location_set_gps_refresh_rate (ctx->skeleton, ctx->rate); mm_gdbus_modem_location_complete_set_gps_refresh_rate (ctx->skeleton, ctx->invocation); handle_set_gps_refresh_rate_context_free (ctx); } static gboolean handle_set_gps_refresh_rate (MmGdbusModemLocation *skeleton, GDBusMethodInvocation *invocation, guint rate, MMIfaceModemLocation *self) { HandleSetGpsRefreshRateContext *ctx; ctx = g_slice_new0 (HandleSetGpsRefreshRateContext); ctx->skeleton = g_object_ref (skeleton); ctx->invocation = g_object_ref (invocation); ctx->self = g_object_ref (self); ctx->rate = rate; mm_base_modem_authorize (MM_BASE_MODEM (self), invocation, MM_AUTHORIZATION_DEVICE_CONTROL, (GAsyncReadyCallback)handle_set_gps_refresh_rate_auth_ready, ctx); return TRUE; } /*****************************************************************************/ typedef struct { MmGdbusModemLocation *skeleton; GDBusMethodInvocation *invocation; MMIfaceModemLocation *self; } HandleGetLocationContext; static void handle_get_location_context_free (HandleGetLocationContext *ctx) { g_object_unref (ctx->skeleton); g_object_unref (ctx->invocation); g_object_unref (ctx->self); g_slice_free (HandleGetLocationContext, ctx); } static void handle_get_location_auth_ready (MMBaseModem *self, GAsyncResult *res, HandleGetLocationContext *ctx) { LocationContext *location_ctx; GError *error = NULL; if (!mm_base_modem_authorize_finish (self, res, &error)) { mm_dbus_method_invocation_take_error (ctx->invocation, error); handle_get_location_context_free (ctx); return; } location_ctx = get_location_context (ctx->self); mm_gdbus_modem_location_complete_get_location ( ctx->skeleton, ctx->invocation, build_location_dictionary (NULL, location_ctx->location_3gpp, location_ctx->location_gps_nmea, location_ctx->location_gps_raw, location_ctx->location_cdma_bs)); handle_get_location_context_free (ctx); } static gboolean handle_get_location (MmGdbusModemLocation *skeleton, GDBusMethodInvocation *invocation, MMIfaceModemLocation *self) { HandleGetLocationContext *ctx; ctx = g_slice_new0 (HandleGetLocationContext); ctx->skeleton = g_object_ref (skeleton); ctx->invocation = g_object_ref (invocation); ctx->self = g_object_ref (self); mm_base_modem_authorize (MM_BASE_MODEM (self), invocation, MM_AUTHORIZATION_LOCATION, (GAsyncReadyCallback)handle_get_location_auth_ready, ctx); return TRUE; } /*****************************************************************************/ typedef struct _DisablingContext DisablingContext; static void interface_disabling_step (GTask *task); typedef enum { DISABLING_STEP_FIRST, DISABLING_STEP_DISABLE_GATHERING, DISABLING_STEP_LAST } DisablingStep; struct _DisablingContext { DisablingStep step; MmGdbusModemLocation *skeleton; }; static void disabling_context_free (DisablingContext *ctx) { if (ctx->skeleton) g_object_unref (ctx->skeleton); g_free (ctx); } gboolean mm_iface_modem_location_disable_finish (MMIfaceModemLocation *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void disabling_location_gathering_ready (MMIfaceModemLocation *self, GAsyncResult *res, GTask *task) { DisablingContext *ctx; GError *error = NULL; if (!setup_gathering_finish (self, res, &error)) { g_task_return_error (task, error); g_object_unref (task); return; } /* Go on to next step */ ctx = g_task_get_task_data (task); ctx->step++; interface_disabling_step (task); } static void interface_disabling_step (GTask *task) { MMIfaceModemLocation *self; DisablingContext *ctx; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); switch (ctx->step) { case DISABLING_STEP_FIRST: ctx->step++; /* fall through */ case DISABLING_STEP_DISABLE_GATHERING: /* We disable all sources here. It is true that the user may have enabled GPS * early before getting the modem enabled, and that we may have left it active * once the modem transitions to enabled state, but for the disabling phase * we reset everything. */ setup_gathering (self, MM_MODEM_LOCATION_SOURCE_NONE, (GAsyncReadyCallback)disabling_location_gathering_ready, task); return; case DISABLING_STEP_LAST: /* We are done without errors! */ clear_location_context (self); g_task_return_boolean (task, TRUE); g_object_unref (task); return; default: break; } g_assert_not_reached (); } void mm_iface_modem_location_disable (MMIfaceModemLocation *self, GAsyncReadyCallback callback, gpointer user_data) { DisablingContext *ctx; GTask *task; ctx = g_new0 (DisablingContext, 1); ctx->step = DISABLING_STEP_FIRST; task = g_task_new (self, NULL, callback, user_data); g_task_set_task_data (task, ctx, (GDestroyNotify)disabling_context_free); g_object_get (self, MM_IFACE_MODEM_LOCATION_DBUS_SKELETON, &ctx->skeleton, NULL); if (!ctx->skeleton) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't get interface skeleton"); g_object_unref (task); return; } interface_disabling_step (task); } /*****************************************************************************/ typedef struct _EnablingContext EnablingContext; static void interface_enabling_step (GTask *task); typedef enum { ENABLING_STEP_FIRST, ENABLING_STEP_ENABLE_GATHERING, ENABLING_STEP_LAST } EnablingStep; struct _EnablingContext { EnablingStep step; MmGdbusModemLocation *skeleton; }; static void enabling_context_free (EnablingContext *ctx) { if (ctx->skeleton) g_object_unref (ctx->skeleton); g_free (ctx); } gboolean mm_iface_modem_location_enable_finish (MMIfaceModemLocation *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void enabling_location_gathering_ready (MMIfaceModemLocation *self, GAsyncResult *res, GTask *task) { EnablingContext *ctx; GError *error = NULL; if (!setup_gathering_finish (self, res, &error)) { g_task_return_error (task, error); g_object_unref (task); return; } /* Go on to next step */ ctx = g_task_get_task_data (task); ctx->step++; interface_enabling_step (task); } static void interface_enabling_step (GTask *task) { MMIfaceModemLocation *self; EnablingContext *ctx; /* Don't run new steps if we're cancelled */ if (g_task_return_error_if_cancelled (task)) { g_object_unref (task); return; } self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); switch (ctx->step) { case ENABLING_STEP_FIRST: ctx->step++; /* fall through */ case ENABLING_STEP_ENABLE_GATHERING: { MMModemLocationSource default_sources; MMModemLocationSource currently_enabled; /* By default, we'll enable all NON-GPS sources */ default_sources = mm_gdbus_modem_location_get_capabilities (ctx->skeleton); default_sources &= ~(MM_MODEM_LOCATION_SOURCE_GPS_RAW | MM_MODEM_LOCATION_SOURCE_GPS_NMEA | MM_MODEM_LOCATION_SOURCE_GPS_UNMANAGED | MM_MODEM_LOCATION_SOURCE_AGPS_MSA | MM_MODEM_LOCATION_SOURCE_AGPS_MSB); /* If standalone GPS was already enabled, we keep it enabled */ currently_enabled = mm_gdbus_modem_location_get_enabled (ctx->skeleton); setup_gathering (self, default_sources | currently_enabled, (GAsyncReadyCallback)enabling_location_gathering_ready, task); return; } case ENABLING_STEP_LAST: /* We are done without errors! */ g_task_return_boolean (task, TRUE); g_object_unref (task); return; default: break; } g_assert_not_reached (); } void mm_iface_modem_location_enable (MMIfaceModemLocation *self, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { EnablingContext *ctx; GTask *task; ctx = g_new0 (EnablingContext, 1); ctx->step = ENABLING_STEP_FIRST; task = g_task_new (self, cancellable, callback, user_data); g_task_set_task_data (task, ctx, (GDestroyNotify)enabling_context_free); g_object_get (self, MM_IFACE_MODEM_LOCATION_DBUS_SKELETON, &ctx->skeleton, NULL); if (!ctx->skeleton) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't get interface skeleton"); g_object_unref (task); return; } interface_enabling_step (task); } /*****************************************************************************/ typedef struct _InitializationContext InitializationContext; static void interface_initialization_step (GTask *task); typedef enum { INITIALIZATION_STEP_FIRST, INITIALIZATION_STEP_CAPABILITIES, INITIALIZATION_STEP_VALIDATE_CAPABILITIES, INITIALIZATION_STEP_SUPL_SERVER, INITIALIZATION_STEP_SUPPORTED_ASSISTANCE_DATA, INITIALIZATION_STEP_ASSISTANCE_DATA_SERVERS, INITIALIZATION_STEP_GPS_REFRESH_RATE, INITIALIZATION_STEP_LAST } InitializationStep; struct _InitializationContext { MmGdbusModemLocation *skeleton; InitializationStep step; MMModemLocationSource capabilities; }; static void initialization_context_free (InitializationContext *ctx) { g_object_unref (ctx->skeleton); g_free (ctx); } static void load_assistance_data_servers_ready (MMIfaceModemLocation *self, GAsyncResult *res, GTask *task) { GError *error = NULL; InitializationContext *ctx; gchar **servers; ctx = g_task_get_task_data (task); servers = MM_IFACE_MODEM_LOCATION_GET_INTERFACE (self)->load_assistance_data_servers_finish (self, res, &error); if (error) { mm_obj_warn (self, "couldn't load assistance data servers: %s", error->message); g_error_free (error); } mm_gdbus_modem_location_set_assistance_data_servers (ctx->skeleton, (const gchar *const *)servers); g_strfreev (servers); /* Go on to next step */ ctx->step++; interface_initialization_step (task); } static void load_supported_assistance_data_ready (MMIfaceModemLocation *self, GAsyncResult *res, GTask *task) { GError *error = NULL; MMModemLocationAssistanceDataType mask; InitializationContext *ctx; ctx = g_task_get_task_data (task); mask = MM_IFACE_MODEM_LOCATION_GET_INTERFACE (self)->load_supported_assistance_data_finish (self, res, &error); if (error) { mm_obj_warn (self, "couldn't load supported assistance data types: %s", error->message); g_error_free (error); } mm_gdbus_modem_location_set_supported_assistance_data (ctx->skeleton, (guint32) mask); /* Go on to next step */ ctx->step++; interface_initialization_step (task); } static void load_supl_server_ready (MMIfaceModemLocation *self, GAsyncResult *res, GTask *task) { GError *error = NULL; gchar *supl; InitializationContext *ctx; ctx = g_task_get_task_data (task); supl = MM_IFACE_MODEM_LOCATION_GET_INTERFACE (self)->load_supl_server_finish (self, res, &error); if (error) { mm_obj_warn (self, "couldn't load SUPL server: %s", error->message); g_error_free (error); } mm_gdbus_modem_location_set_supl_server (ctx->skeleton, supl ? supl : ""); g_free (supl); /* Go on to next step */ ctx->step++; interface_initialization_step (task); } static void load_capabilities_ready (MMIfaceModemLocation *self, GAsyncResult *res, GTask *task) { GError *error = NULL; InitializationContext *ctx; ctx = g_task_get_task_data (task); ctx->capabilities = MM_IFACE_MODEM_LOCATION_GET_INTERFACE (self)->load_capabilities_finish (self, res, &error); if (error) { mm_obj_warn (self, "couldn't load location capabilities: %s", error->message); g_error_free (error); } mm_gdbus_modem_location_set_capabilities (ctx->skeleton, ctx->capabilities); /* Go on to next step */ ctx->step++; interface_initialization_step (task); } static void interface_initialization_step (GTask *task) { MMIfaceModemLocation *self; InitializationContext *ctx; /* Don't run new steps if we're cancelled */ if (g_task_return_error_if_cancelled (task)) { g_object_unref (task); return; } self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); switch (ctx->step) { case INITIALIZATION_STEP_FIRST: ctx->step++; /* fall through */ case INITIALIZATION_STEP_CAPABILITIES: /* Location capabilities value is meant to be loaded only once during * the whole lifetime of the modem. Therefore, if we already have it * loaded, don't try to load it again. */ if (!mm_gdbus_modem_location_get_capabilities (ctx->skeleton) && MM_IFACE_MODEM_LOCATION_GET_INTERFACE (self)->load_capabilities && MM_IFACE_MODEM_LOCATION_GET_INTERFACE (self)->load_capabilities_finish) { MM_IFACE_MODEM_LOCATION_GET_INTERFACE (self)->load_capabilities ( self, (GAsyncReadyCallback)load_capabilities_ready, task); return; } ctx->step++; /* fall through */ case INITIALIZATION_STEP_VALIDATE_CAPABILITIES: /* If the modem doesn't support any location capabilities, we won't export * the interface. We just report an UNSUPPORTED error. */ if (ctx->capabilities == MM_MODEM_LOCATION_SOURCE_NONE) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "The modem doesn't have location capabilities"); g_object_unref (task); return; } ctx->step++; /* fall through */ case INITIALIZATION_STEP_SUPL_SERVER: /* If the modem supports A-GPS, load SUPL server */ if ((ctx->capabilities & (MM_MODEM_LOCATION_SOURCE_AGPS_MSA | MM_MODEM_LOCATION_SOURCE_AGPS_MSB)) && MM_IFACE_MODEM_LOCATION_GET_INTERFACE (self)->load_supl_server && MM_IFACE_MODEM_LOCATION_GET_INTERFACE (self)->load_supl_server_finish) { MM_IFACE_MODEM_LOCATION_GET_INTERFACE (self)->load_supl_server ( self, (GAsyncReadyCallback)load_supl_server_ready, task); return; } ctx->step++; /* fall through */ case INITIALIZATION_STEP_SUPPORTED_ASSISTANCE_DATA: /* If the modem supports any GPS-related technology, check assistance data types supported */ if ((ctx->capabilities & (MM_MODEM_LOCATION_SOURCE_AGPS_MSA | MM_MODEM_LOCATION_SOURCE_AGPS_MSB | MM_MODEM_LOCATION_SOURCE_GPS_RAW | MM_MODEM_LOCATION_SOURCE_GPS_NMEA)) && MM_IFACE_MODEM_LOCATION_GET_INTERFACE (self)->load_supported_assistance_data && MM_IFACE_MODEM_LOCATION_GET_INTERFACE (self)->load_supported_assistance_data_finish) { MM_IFACE_MODEM_LOCATION_GET_INTERFACE (self)->load_supported_assistance_data ( self, (GAsyncReadyCallback)load_supported_assistance_data_ready, task); return; } ctx->step++; /* fall through */ case INITIALIZATION_STEP_ASSISTANCE_DATA_SERVERS: /* If any assistance data supported, load servers */ if ((mm_gdbus_modem_location_get_supported_assistance_data (ctx->skeleton) != MM_MODEM_LOCATION_ASSISTANCE_DATA_TYPE_NONE) && MM_IFACE_MODEM_LOCATION_GET_INTERFACE (self)->load_assistance_data_servers && MM_IFACE_MODEM_LOCATION_GET_INTERFACE (self)->load_assistance_data_servers_finish) { MM_IFACE_MODEM_LOCATION_GET_INTERFACE (self)->load_assistance_data_servers ( self, (GAsyncReadyCallback)load_assistance_data_servers_ready, task); return; } ctx->step++; /* fall through */ case INITIALIZATION_STEP_GPS_REFRESH_RATE: /* If we have GPS capabilities, expose the GPS refresh rate */ if (ctx->capabilities & ((MM_MODEM_LOCATION_SOURCE_GPS_RAW | MM_MODEM_LOCATION_SOURCE_GPS_NMEA))) /* Set the default rate in the interface */ mm_gdbus_modem_location_set_gps_refresh_rate (ctx->skeleton, MM_LOCATION_GPS_REFRESH_TIME_SECS); ctx->step++; /* fall through */ case INITIALIZATION_STEP_LAST: /* We are done without errors! */ /* Handle method invocations */ g_signal_connect (ctx->skeleton, "handle-setup", G_CALLBACK (handle_setup), self); g_signal_connect (ctx->skeleton, "handle-set-supl-server", G_CALLBACK (handle_set_supl_server), self); g_signal_connect (ctx->skeleton, "handle-inject-assistance-data", G_CALLBACK (handle_inject_assistance_data), self); g_signal_connect (ctx->skeleton, "handle-set-gps-refresh-rate", G_CALLBACK (handle_set_gps_refresh_rate), self); g_signal_connect (ctx->skeleton, "handle-get-location", G_CALLBACK (handle_get_location), self); /* Finally, export the new interface */ mm_gdbus_object_skeleton_set_modem_location (MM_GDBUS_OBJECT_SKELETON (self), MM_GDBUS_MODEM_LOCATION (ctx->skeleton)); g_task_return_boolean (task, TRUE); g_object_unref (task); return; default: break; } g_assert_not_reached (); } gboolean mm_iface_modem_location_initialize_finish (MMIfaceModemLocation *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } void mm_iface_modem_location_initialize (MMIfaceModemLocation *self, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { InitializationContext *ctx; MmGdbusModemLocation *skeleton = NULL; GTask *task; /* Did we already create it? */ g_object_get (self, MM_IFACE_MODEM_LOCATION_DBUS_SKELETON, &skeleton, NULL); if (!skeleton) { skeleton = mm_gdbus_modem_location_skeleton_new (); /* Set all initial property defaults */ mm_gdbus_modem_location_set_capabilities (skeleton, MM_MODEM_LOCATION_SOURCE_NONE); mm_gdbus_modem_location_set_supported_assistance_data (skeleton, MM_MODEM_LOCATION_ASSISTANCE_DATA_TYPE_NONE); mm_gdbus_modem_location_set_enabled (skeleton, MM_MODEM_LOCATION_SOURCE_NONE); mm_gdbus_modem_location_set_signals_location (skeleton, FALSE); mm_gdbus_modem_location_set_location (skeleton, build_location_dictionary (NULL, NULL, NULL, NULL, NULL)); g_object_set (self, MM_IFACE_MODEM_LOCATION_DBUS_SKELETON, skeleton, NULL); } /* Perform async initialization here */ ctx = g_new0 (InitializationContext, 1); ctx->capabilities = MM_MODEM_LOCATION_SOURCE_NONE; ctx->step = INITIALIZATION_STEP_FIRST; ctx->skeleton = skeleton; task = g_task_new (self, cancellable, callback, user_data); g_task_set_task_data (task, ctx, (GDestroyNotify)initialization_context_free); interface_initialization_step (task); } void mm_iface_modem_location_shutdown (MMIfaceModemLocation *self) { /* Unexport DBus interface and remove the skeleton */ mm_gdbus_object_skeleton_set_modem_location (MM_GDBUS_OBJECT_SKELETON (self), NULL); g_object_set (self, MM_IFACE_MODEM_LOCATION_DBUS_SKELETON, NULL, NULL); } /*****************************************************************************/ static void iface_modem_location_init (gpointer g_iface) { static gboolean initialized = FALSE; if (initialized) return; /* Properties */ g_object_interface_install_property (g_iface, g_param_spec_object (MM_IFACE_MODEM_LOCATION_DBUS_SKELETON, "Location DBus skeleton", "DBus skeleton for the Location interface", MM_GDBUS_TYPE_MODEM_LOCATION_SKELETON, G_PARAM_READWRITE)); g_object_interface_install_property (g_iface, g_param_spec_boolean (MM_IFACE_MODEM_LOCATION_ALLOW_GPS_UNMANAGED_ALWAYS, "Allow unmanaged GPS always", "Whether to always allow GPS unmanaged, even when raw/nmea GPS sources are enabled", FALSE, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); initialized = TRUE; } GType mm_iface_modem_location_get_type (void) { static GType iface_modem_location_type = 0; if (!G_UNLIKELY (iface_modem_location_type)) { static const GTypeInfo info = { sizeof (MMIfaceModemLocation), /* class_size */ iface_modem_location_init, /* base_init */ NULL, /* base_finalize */ }; iface_modem_location_type = g_type_register_static (G_TYPE_INTERFACE, "MMIfaceModemLocation", &info, 0); g_type_interface_add_prerequisite (iface_modem_location_type, MM_TYPE_IFACE_MODEM); } return iface_modem_location_type; } ModemManager-1.23.4-dev/src/mm-iface-modem-location.h000066400000000000000000000215411456466623000222530ustar00rootroot00000000000000/* -*- 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) 2012 Google, Inc. */ #ifndef MM_IFACE_MODEM_LOCATION_H #define MM_IFACE_MODEM_LOCATION_H #include #include #define _LIBMM_INSIDE_MM #include #define MM_TYPE_IFACE_MODEM_LOCATION (mm_iface_modem_location_get_type ()) #define MM_IFACE_MODEM_LOCATION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_IFACE_MODEM_LOCATION, MMIfaceModemLocation)) #define MM_IS_IFACE_MODEM_LOCATION(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_IFACE_MODEM_LOCATION)) #define MM_IFACE_MODEM_LOCATION_GET_INTERFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE ((obj), MM_TYPE_IFACE_MODEM_LOCATION, MMIfaceModemLocation)) #define MM_IFACE_MODEM_LOCATION_DBUS_SKELETON "iface-modem-location-dbus-skeleton" #define MM_IFACE_MODEM_LOCATION_ALLOW_GPS_UNMANAGED_ALWAYS "iface-modem-location-allow-gps-unmanaged-always" typedef struct _MMIfaceModemLocation MMIfaceModemLocation; struct _MMIfaceModemLocation { GTypeInterface g_iface; /* Loading of the Capabilities property */ void (*load_capabilities) (MMIfaceModemLocation *self, GAsyncReadyCallback callback, gpointer user_data); MMModemLocationSource (*load_capabilities_finish) (MMIfaceModemLocation *self, GAsyncResult *res, GError **error); /* Loading of the SuplServer property */ void (* load_supl_server) (MMIfaceModemLocation *self, GAsyncReadyCallback callback, gpointer user_data); gchar * (* load_supl_server_finish) (MMIfaceModemLocation *self, GAsyncResult *res, GError **error); /* Loading of the AssistanceDataServers property */ void (* load_assistance_data_servers) (MMIfaceModemLocation *self, GAsyncReadyCallback callback, gpointer user_data); gchar ** (* load_assistance_data_servers_finish) (MMIfaceModemLocation *self, GAsyncResult *res, GError **error); /* Loading of the SupportedAssistanceData property */ void (* load_supported_assistance_data) (MMIfaceModemLocation *self, GAsyncReadyCallback callback, gpointer user_data); MMModemLocationAssistanceDataType (* load_supported_assistance_data_finish) (MMIfaceModemLocation *self, GAsyncResult *res, GError **error); /* Enable location gathering (async) */ void (* enable_location_gathering) (MMIfaceModemLocation *self, MMModemLocationSource source, GAsyncReadyCallback callback, gpointer user_data); gboolean (*enable_location_gathering_finish) (MMIfaceModemLocation *self, GAsyncResult *res, GError **error); /* Disable location gathering (async) */ void (* disable_location_gathering) (MMIfaceModemLocation *self, MMModemLocationSource source, GAsyncReadyCallback callback, gpointer user_data); gboolean (*disable_location_gathering_finish) (MMIfaceModemLocation *self, GAsyncResult *res, GError **error); /* Set SUPL server (async) */ void (* set_supl_server) (MMIfaceModemLocation *self, const gchar *supl, GAsyncReadyCallback callback, gpointer user_data); gboolean (*set_supl_server_finish) (MMIfaceModemLocation *self, GAsyncResult *res, GError **error); /* Inject assistance data (async) */ void (* inject_assistance_data) (MMIfaceModemLocation *self, const guint8 *data, gsize data_size, GAsyncReadyCallback callback, gpointer user_data); gboolean (*inject_assistance_data_finish) (MMIfaceModemLocation *self, GAsyncResult *res, GError **error); }; GType mm_iface_modem_location_get_type (void); G_DEFINE_AUTOPTR_CLEANUP_FUNC (MMIfaceModemLocation, g_object_unref) /* Initialize Location interface (async) */ void mm_iface_modem_location_initialize (MMIfaceModemLocation *self, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); gboolean mm_iface_modem_location_initialize_finish (MMIfaceModemLocation *self, GAsyncResult *res, GError **error); /* Enable Location interface (async) */ void mm_iface_modem_location_enable (MMIfaceModemLocation *self, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); gboolean mm_iface_modem_location_enable_finish (MMIfaceModemLocation *self, GAsyncResult *res, GError **error); /* Disable Location interface (async) */ void mm_iface_modem_location_disable (MMIfaceModemLocation *self, GAsyncReadyCallback callback, gpointer user_data); gboolean mm_iface_modem_location_disable_finish (MMIfaceModemLocation *self, GAsyncResult *res, GError **error); /* Shutdown Location interface */ void mm_iface_modem_location_shutdown (MMIfaceModemLocation *self); /* Update 3GPP (LAC/CI) location */ void mm_iface_modem_location_3gpp_clear (MMIfaceModemLocation *self); void mm_iface_modem_location_3gpp_update_operator_code (MMIfaceModemLocation *self, const gchar *operator_code); void mm_iface_modem_location_3gpp_update_lac_tac_ci (MMIfaceModemLocation *self, gulong location_area_code, gulong tracking_area_code, gulong cell_id); /* Update GPS location */ void mm_iface_modem_location_gps_update (MMIfaceModemLocation *self, const gchar *nmea_trace); /* Update CDMA BS location */ void mm_iface_modem_location_cdma_bs_update (MMIfaceModemLocation *self, gdouble longitude, gdouble latitude); void mm_iface_modem_location_cdma_bs_clear (MMIfaceModemLocation *self); /* Bind properties for simple GetStatus() */ void mm_iface_modem_location_bind_simple_status (MMIfaceModemLocation *self, MMSimpleStatus *status); #endif /* MM_IFACE_MODEM_LOCATION_H */ ModemManager-1.23.4-dev/src/mm-iface-modem-messaging.c000066400000000000000000001401721456466623000224150ustar00rootroot00000000000000/* -*- 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) 2012 Google, Inc. */ #include #define _LIBMM_INSIDE_MM #include #include "mm-iface-modem.h" #include "mm-iface-modem-messaging.h" #include "mm-sms-list.h" #include "mm-error-helpers.h" #include "mm-log-object.h" #define SUPPORT_CHECKED_TAG "messaging-support-checked-tag" #define SUPPORTED_TAG "messaging-supported-tag" #define STORAGE_CONTEXT_TAG "messaging-storage-context-tag" static GQuark support_checked_quark; static GQuark supported_quark; static GQuark storage_context_quark; /*****************************************************************************/ guint8 mm_iface_modem_messaging_get_local_multipart_reference (MMIfaceModemMessaging *self, const gchar *number, GError **error) { MMSmsList *list = NULL; guint8 reference; guint8 first; /* Start by looking for a random number */ reference = g_random_int_range (1,255); /* Then, look for the given reference in user-created messages */ g_object_get (self, MM_IFACE_MODEM_MESSAGING_SMS_LIST, &list, NULL); if (!list) return reference; first = reference; do { if (!mm_sms_list_has_local_multipart_reference (list, number, reference)) { g_object_unref (list); return reference; } if (reference == 255) reference = 1; else reference++; } while (reference != first); g_object_unref (list); /* We were not able to find a new valid multipart reference :/ * return an error */ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_TOO_MANY, "Cannot create multipart SMS: No valid multipart reference " "available for destination number '%s'", number); return 0; } /*****************************************************************************/ void mm_iface_modem_messaging_bind_simple_status (MMIfaceModemMessaging *self, MMSimpleStatus *status) { } /*****************************************************************************/ MMBaseSms * mm_iface_modem_messaging_create_sms (MMIfaceModemMessaging *self) { g_assert (MM_IFACE_MODEM_MESSAGING_GET_INTERFACE (self)->create_sms != NULL); return MM_IFACE_MODEM_MESSAGING_GET_INTERFACE (self)->create_sms (self); } /*****************************************************************************/ typedef struct { GArray *supported_mem1; GArray *supported_mem2; GArray *supported_mem3; } StorageContext; static void storage_context_free (StorageContext *ctx) { if (ctx->supported_mem1) g_array_unref (ctx->supported_mem1); if (ctx->supported_mem2) g_array_unref (ctx->supported_mem2); if (ctx->supported_mem3) g_array_unref (ctx->supported_mem3); g_free (ctx); } static StorageContext * get_storage_context (MMIfaceModemMessaging *self) { StorageContext *ctx; if (G_UNLIKELY (!storage_context_quark)) storage_context_quark = (g_quark_from_static_string ( STORAGE_CONTEXT_TAG)); ctx = g_object_get_qdata (G_OBJECT (self), storage_context_quark); if (!ctx) { /* Create context and keep it as object data */ ctx = g_new0 (StorageContext, 1); g_object_set_qdata_full ( G_OBJECT (self), storage_context_quark, ctx, (GDestroyNotify)storage_context_free); } return ctx; } /*****************************************************************************/ typedef struct { MmGdbusModemMessaging *skeleton; GDBusMethodInvocation *invocation; MMIfaceModemMessaging *self; gchar *path; } HandleDeleteContext; static void handle_delete_context_free (HandleDeleteContext *ctx) { g_object_unref (ctx->skeleton); g_object_unref (ctx->invocation); g_object_unref (ctx->self); g_free (ctx->path); g_slice_free (HandleDeleteContext, ctx); } static void handle_delete_ready (MMSmsList *list, GAsyncResult *res, HandleDeleteContext *ctx) { GError *error = NULL; if (!mm_sms_list_delete_sms_finish (list, res, &error)) { mm_obj_warn (ctx->self, "failed deleting SMS message '%s': %s", ctx->path, error->message); mm_dbus_method_invocation_take_error (ctx->invocation, error); } else { mm_obj_info (ctx->self, "deleted SMS message '%s'", ctx->path); mm_gdbus_modem_messaging_complete_delete (ctx->skeleton, ctx->invocation); } handle_delete_context_free (ctx); } static void handle_delete_auth_ready (MMBaseModem *self, GAsyncResult *res, HandleDeleteContext *ctx) { g_autoptr(MMSmsList) list = NULL; GError *error = NULL; if (!mm_base_modem_authorize_finish (self, res, &error)) { mm_dbus_method_invocation_take_error (ctx->invocation, error); handle_delete_context_free (ctx); return; } /* We do allow deleting SMS messages while enabling or disabling, it doesn't * interfere with the state transition logic to do so. The main reason to allow * this is that during modem enabling we're emitting "Added" signals before we * reach the enabled state, and so users listening to the signal may want to * delete the SMS message as soon as it's read. */ if (mm_iface_modem_abort_invocation_if_state_not_reached (MM_IFACE_MODEM (self), ctx->invocation, MM_MODEM_STATE_DISABLING)) { handle_delete_context_free (ctx); return; } g_object_get (self, MM_IFACE_MODEM_MESSAGING_SMS_LIST, &list, NULL); if (!list) { mm_dbus_method_invocation_return_error_literal (ctx->invocation, MM_CORE_ERROR, MM_CORE_ERROR_WRONG_STATE, "Cannot delete SMS: missing SMS list"); handle_delete_context_free (ctx); return; } mm_obj_info (self, "processing user request to delete SMS message '%s'...", ctx->path); mm_sms_list_delete_sms (list, ctx->path, (GAsyncReadyCallback)handle_delete_ready, ctx); } static gboolean handle_delete (MmGdbusModemMessaging *skeleton, GDBusMethodInvocation *invocation, const gchar *path, MMIfaceModemMessaging *self) { HandleDeleteContext *ctx; ctx = g_slice_new0 (HandleDeleteContext); ctx->skeleton = g_object_ref (skeleton); ctx->invocation = g_object_ref (invocation); ctx->self = g_object_ref (self); ctx->path = g_strdup (path); mm_base_modem_authorize (MM_BASE_MODEM (self), invocation, MM_AUTHORIZATION_MESSAGING, (GAsyncReadyCallback)handle_delete_auth_ready, ctx); return TRUE; } /*****************************************************************************/ typedef struct { MmGdbusModemMessaging *skeleton; GDBusMethodInvocation *invocation; MMIfaceModemMessaging *self; GVariant *dictionary; } HandleCreateContext; static void handle_create_context_free (HandleCreateContext *ctx) { g_object_unref (ctx->skeleton); g_object_unref (ctx->invocation); g_object_unref (ctx->self); g_variant_unref (ctx->dictionary); g_slice_free (HandleCreateContext, ctx); } static void handle_create_auth_ready (MMBaseModem *self, GAsyncResult *res, HandleCreateContext *ctx) { GError *error = NULL; g_autoptr(MMSmsList) list = NULL; g_autoptr(MMSmsProperties) properties = NULL; g_autoptr(MMBaseSms) sms = NULL; if (!mm_base_modem_authorize_finish (self, res, &error)) { mm_dbus_method_invocation_take_error (ctx->invocation, error); handle_create_context_free (ctx); return; } if (mm_iface_modem_abort_invocation_if_state_not_reached (MM_IFACE_MODEM (self), ctx->invocation, MM_MODEM_STATE_ENABLED)) { handle_create_context_free (ctx); return; } /* Parse input properties */ properties = mm_sms_properties_new_from_dictionary (ctx->dictionary, &error); if (!properties) { mm_dbus_method_invocation_take_error (ctx->invocation, error); handle_create_context_free (ctx); return; } sms = mm_base_sms_new_from_properties (MM_BASE_MODEM (self), properties, &error); if (!sms) { mm_dbus_method_invocation_take_error (ctx->invocation, error); handle_create_context_free (ctx); return; } g_object_get (self, MM_IFACE_MODEM_MESSAGING_SMS_LIST, &list, NULL); if (!list) { mm_dbus_method_invocation_return_error_literal (ctx->invocation, MM_CORE_ERROR, MM_CORE_ERROR_WRONG_STATE, "Cannot create SMS: missing SMS list"); handle_create_context_free (ctx); return; } mm_obj_info (self, "processing user request to create SMS message..."); mm_sms_list_add_sms (list, sms); mm_gdbus_modem_messaging_complete_create (ctx->skeleton, ctx->invocation, mm_base_sms_get_path (sms)); mm_obj_info (self, "created SMS message: %s", mm_base_sms_get_path (sms)); handle_create_context_free (ctx); } static gboolean handle_create (MmGdbusModemMessaging *skeleton, GDBusMethodInvocation *invocation, GVariant *dictionary, MMIfaceModemMessaging *self) { HandleCreateContext *ctx; ctx = g_slice_new0 (HandleCreateContext); ctx->skeleton = g_object_ref (skeleton); ctx->invocation = g_object_ref (invocation); ctx->self = g_object_ref (self); ctx->dictionary = g_variant_ref (dictionary); mm_base_modem_authorize (MM_BASE_MODEM (self), invocation, MM_AUTHORIZATION_MESSAGING, (GAsyncReadyCallback)handle_create_auth_ready, ctx); return TRUE; } /*****************************************************************************/ static gboolean handle_list (MmGdbusModemMessaging *skeleton, GDBusMethodInvocation *invocation, MMIfaceModemMessaging *self) { g_auto(GStrv) paths = NULL; g_autoptr(MMSmsList) list = NULL; if (mm_iface_modem_abort_invocation_if_state_not_reached (MM_IFACE_MODEM (self), invocation, MM_MODEM_STATE_ENABLED)) return TRUE; g_object_get (self, MM_IFACE_MODEM_MESSAGING_SMS_LIST, &list, NULL); if (!list) { mm_dbus_method_invocation_return_error_literal (invocation, MM_CORE_ERROR, MM_CORE_ERROR_WRONG_STATE, "Cannot list SMS: missing SMS list"); return TRUE; } mm_obj_info (self, "processing user request to list SMS messages..."); paths = mm_sms_list_get_paths (list); mm_gdbus_modem_messaging_complete_list (skeleton, invocation, (const gchar *const *)paths); mm_obj_info (self, "reported %u SMS messages available", paths ? g_strv_length (paths) : 0); return TRUE; } /*****************************************************************************/ gboolean mm_iface_modem_messaging_take_part (MMIfaceModemMessaging *self, MMSmsPart *sms_part, MMSmsState state, MMSmsStorage storage) { g_autoptr(MMSmsList) list = NULL; g_autoptr(GError) error = NULL; gboolean added = FALSE; g_object_get (self, MM_IFACE_MODEM_MESSAGING_SMS_LIST, &list, NULL); if (list) { added = mm_sms_list_take_part (list, sms_part, state, storage, &error); if (!added) mm_obj_dbg (self, "couldn't take part in SMS list: %s", error->message); } /* If part wasn't taken, we need to free the part ourselves */ if (!added) mm_sms_part_free (sms_part); return added; } /*****************************************************************************/ static gboolean is_storage_supported (GArray *supported, MMSmsStorage preferred, const gchar *action, GError **error) { guint i; if (supported) { for (i = 0; i < supported->len; i++) { if (preferred == g_array_index (supported, MMSmsStorage, i)) return TRUE; } } g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "Storage '%s' is not supported for %s", mm_sms_storage_get_string (preferred), action); return FALSE; } gboolean mm_iface_modem_messaging_is_storage_supported_for_storing (MMIfaceModemMessaging *self, MMSmsStorage storage, GError **error) { /* mem2 is for storing */ return is_storage_supported ((get_storage_context (self))->supported_mem2, storage, "storing", error); } gboolean mm_iface_modem_messaging_is_storage_supported_for_receiving (MMIfaceModemMessaging *self, MMSmsStorage storage, GError **error) { /* mem3 is for receiving */ return is_storage_supported ((get_storage_context (self))->supported_mem3, storage, "receiving", error); } /*****************************************************************************/ static void update_message_list (MmGdbusModemMessaging *skeleton, MMSmsList *list) { gchar **paths; paths = mm_sms_list_get_paths (list); mm_gdbus_modem_messaging_set_messages (skeleton, (const gchar *const *)paths); g_strfreev (paths); g_dbus_interface_skeleton_flush (G_DBUS_INTERFACE_SKELETON (skeleton)); } static void sms_added (MMSmsList *list, const gchar *sms_path, gboolean received, MmGdbusModemMessaging *skeleton) { update_message_list (skeleton, list); mm_gdbus_modem_messaging_emit_added (skeleton, sms_path, received); } static void sms_deleted (MMSmsList *list, const gchar *sms_path, MmGdbusModemMessaging *skeleton) { update_message_list (skeleton, list); mm_gdbus_modem_messaging_emit_deleted (skeleton, sms_path); } /*****************************************************************************/ typedef struct _DisablingContext DisablingContext; static void interface_disabling_step (GTask *task); typedef enum { DISABLING_STEP_FIRST, DISABLING_STEP_DISABLE_UNSOLICITED_EVENTS, DISABLING_STEP_CLEANUP_UNSOLICITED_EVENTS, DISABLING_STEP_LAST } DisablingStep; struct _DisablingContext { DisablingStep step; MmGdbusModemMessaging *skeleton; }; static void disabling_context_free (DisablingContext *ctx) { if (ctx->skeleton) g_object_unref (ctx->skeleton); g_free (ctx); } gboolean mm_iface_modem_messaging_disable_finish (MMIfaceModemMessaging *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void disable_unsolicited_events_ready (MMIfaceModemMessaging *self, GAsyncResult *res, GTask *task) { DisablingContext *ctx; GError *error = NULL; MM_IFACE_MODEM_MESSAGING_GET_INTERFACE (self)->disable_unsolicited_events_finish (self, res, &error); if (error) { g_task_return_error (task, error); g_object_unref (task); return; } /* Go on to next step */ ctx = g_task_get_task_data (task); ctx->step++; interface_disabling_step (task); } static void cleanup_unsolicited_events_ready (MMIfaceModemMessaging *self, GAsyncResult *res, GTask *task) { DisablingContext *ctx; GError *error = NULL; MM_IFACE_MODEM_MESSAGING_GET_INTERFACE (self)->cleanup_unsolicited_events_finish (self, res, &error); if (error) { g_task_return_error (task, error); g_object_unref (task); return; } /* Go on to next step */ ctx = g_task_get_task_data (task); ctx->step++; interface_disabling_step (task); } static void interface_disabling_step (GTask *task) { MMIfaceModemMessaging *self; DisablingContext *ctx; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); switch (ctx->step) { case DISABLING_STEP_FIRST: ctx->step++; /* fall through */ case DISABLING_STEP_DISABLE_UNSOLICITED_EVENTS: /* Allow cleaning up unsolicited events */ if (MM_IFACE_MODEM_MESSAGING_GET_INTERFACE (self)->disable_unsolicited_events && MM_IFACE_MODEM_MESSAGING_GET_INTERFACE (self)->disable_unsolicited_events_finish) { MM_IFACE_MODEM_MESSAGING_GET_INTERFACE (self)->disable_unsolicited_events ( self, (GAsyncReadyCallback)disable_unsolicited_events_ready, task); return; } ctx->step++; /* fall through */ case DISABLING_STEP_CLEANUP_UNSOLICITED_EVENTS: /* Allow cleaning up unsolicited events */ if (MM_IFACE_MODEM_MESSAGING_GET_INTERFACE (self)->cleanup_unsolicited_events && MM_IFACE_MODEM_MESSAGING_GET_INTERFACE (self)->cleanup_unsolicited_events_finish) { MM_IFACE_MODEM_MESSAGING_GET_INTERFACE (self)->cleanup_unsolicited_events ( self, (GAsyncReadyCallback)cleanup_unsolicited_events_ready, task); return; } ctx->step++; /* fall through */ case DISABLING_STEP_LAST: /* Clear SMS list */ g_object_set (self, MM_IFACE_MODEM_MESSAGING_SMS_LIST, NULL, NULL); /* We are done without errors! */ g_task_return_boolean (task, TRUE); g_object_unref (task); return; default: break; } g_assert_not_reached (); } void mm_iface_modem_messaging_disable (MMIfaceModemMessaging *self, GAsyncReadyCallback callback, gpointer user_data) { DisablingContext *ctx; GTask *task; ctx = g_new0 (DisablingContext, 1); ctx->step = DISABLING_STEP_FIRST; task = g_task_new (self, NULL, callback, user_data); g_task_set_task_data (task, ctx, (GDestroyNotify)disabling_context_free); g_object_get (self, MM_IFACE_MODEM_MESSAGING_DBUS_SKELETON, &ctx->skeleton, NULL); if (!ctx->skeleton) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't get interface skeleton"); g_object_unref (task); return; } interface_disabling_step (task); } /*****************************************************************************/ typedef struct _EnablingContext EnablingContext; static void interface_enabling_step (GTask *task); typedef enum { ENABLING_STEP_FIRST, ENABLING_STEP_SETUP_SMS_FORMAT, ENABLING_STEP_STORAGE_DEFAULTS, ENABLING_STEP_LOAD_INITIAL_SMS_PARTS, ENABLING_STEP_SETUP_UNSOLICITED_EVENTS, ENABLING_STEP_ENABLE_UNSOLICITED_EVENTS, ENABLING_STEP_LAST } EnablingStep; struct _EnablingContext { EnablingStep step; MmGdbusModemMessaging *skeleton; guint mem1_storage_index; }; static void enabling_context_free (EnablingContext *ctx) { if (ctx->skeleton) g_object_unref (ctx->skeleton); g_free (ctx); } gboolean mm_iface_modem_messaging_enable_finish (MMIfaceModemMessaging *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void setup_sms_format_ready (MMIfaceModemMessaging *self, GAsyncResult *res, GTask *task) { EnablingContext *ctx; GError *error = NULL; MM_IFACE_MODEM_MESSAGING_GET_INTERFACE (self)->setup_sms_format_finish (self, res, &error); if (error) { g_task_return_error (task, error); g_object_unref (task); return; } /* Go on to next step */ ctx = g_task_get_task_data (task); ctx->step++; interface_enabling_step (task); } static void load_initial_sms_parts_from_storages (GTask *task); static void load_initial_sms_parts_ready (MMIfaceModemMessaging *self, GAsyncResult *res, GTask *task) { EnablingContext *ctx; GError *error = NULL; ctx = g_task_get_task_data (task); MM_IFACE_MODEM_MESSAGING_GET_INTERFACE (self)->load_initial_sms_parts_finish (self, res, &error); if (error) { StorageContext *storage_ctx; storage_ctx = get_storage_context (self); mm_obj_dbg (self, "couldn't load SMS parts from storage '%s': %s", mm_sms_storage_get_string (g_array_index (storage_ctx->supported_mem1, MMSmsStorage, ctx->mem1_storage_index)), error->message); g_error_free (error); } /* Go on with the storage iteration */ ctx->mem1_storage_index++; load_initial_sms_parts_from_storages (task); } static void set_default_storage_ready (MMIfaceModemMessaging *self, GAsyncResult *res, GTask *task) { EnablingContext *ctx; GError *error = NULL; if (!MM_IFACE_MODEM_MESSAGING_GET_INTERFACE (self)->set_default_storage_finish (self, res, &error)) { mm_obj_warn (self, "could not set default storage: %s", error->message); g_error_free (error); } /* Go on with next step */ ctx = g_task_get_task_data (task); ctx->step++; interface_enabling_step (task); } static void load_initial_sms_parts_from_storages (GTask *task) { MMIfaceModemMessaging *self; EnablingContext *ctx; gboolean all_loaded = FALSE; StorageContext *storage_ctx; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); storage_ctx = get_storage_context (self); if (!storage_ctx->supported_mem1 || ctx->mem1_storage_index >= storage_ctx->supported_mem1->len) all_loaded = TRUE; /* We'll skip the 'MT' storage, as that is a combination of 'SM' and 'ME'; but only if * this is not the only one in the list. */ else if ((g_array_index (storage_ctx->supported_mem1, MMSmsStorage, ctx->mem1_storage_index) == MM_SMS_STORAGE_MT) && (storage_ctx->supported_mem1->len > 1)) { ctx->mem1_storage_index++; if (ctx->mem1_storage_index >= storage_ctx->supported_mem1->len) all_loaded = TRUE; } if (all_loaded) { /* Go on with next step */ ctx->step++; interface_enabling_step (task); return; } MM_IFACE_MODEM_MESSAGING_GET_INTERFACE (self)->load_initial_sms_parts ( self, g_array_index (storage_ctx->supported_mem1, MMSmsStorage, ctx->mem1_storage_index), (GAsyncReadyCallback)load_initial_sms_parts_ready, task); } static void setup_unsolicited_events_ready (MMIfaceModemMessaging *self, GAsyncResult *res, GTask *task) { EnablingContext *ctx; GError *error = NULL; MM_IFACE_MODEM_MESSAGING_GET_INTERFACE (self)->setup_unsolicited_events_finish (self, res, &error); if (error) { g_task_return_error (task, error); g_object_unref (task); return; } /* Go on to next step */ ctx = g_task_get_task_data (task); ctx->step++; interface_enabling_step (task); } static void enable_unsolicited_events_ready (MMIfaceModemMessaging *self, GAsyncResult *res, GTask *task) { EnablingContext *ctx; GError *error = NULL; /* Not critical! */ if (!MM_IFACE_MODEM_MESSAGING_GET_INTERFACE (self)->enable_unsolicited_events_finish (self, res, &error)) { mm_obj_dbg (self, "couldn't enable unsolicited events: %s", error->message); g_error_free (error); } /* Go on with next step */ ctx = g_task_get_task_data (task); ctx->step++; interface_enabling_step (task); } static MMSmsStorage get_best_initial_default_sms_storage (MMIfaceModemMessaging *self) { StorageContext *storage_ctx; guint i; MMSmsStorage default_storages_preference[] = { MM_SMS_STORAGE_MT, /* MT=ME+SM */ MM_SMS_STORAGE_ME, MM_SMS_STORAGE_SM, MM_SMS_STORAGE_UNKNOWN }; storage_ctx = get_storage_context (self); for (i = 0; default_storages_preference[i] != MM_SMS_STORAGE_UNKNOWN; i++) { /* Check if the requested storage is really supported in both mem2 and mem3 */ if (is_storage_supported (storage_ctx->supported_mem2, default_storages_preference[i], "storing", NULL) && is_storage_supported (storage_ctx->supported_mem3, default_storages_preference[i], "receiving", NULL)) { break; } } return default_storages_preference[i]; } static MMSmsStorage get_single_default_sms_storage (MMIfaceModemMessaging *self) { StorageContext *storage_ctx; storage_ctx = get_storage_context (self); /* If there is one single storage supported for storing and receiving, just * use that one. */ if (storage_ctx->supported_mem2 && storage_ctx->supported_mem2->len == 1 && storage_ctx->supported_mem3 && storage_ctx->supported_mem3->len == 1) { MMSmsStorage storing_default; storing_default = g_array_index (storage_ctx->supported_mem2, MMSmsStorage, 0); if (storing_default == g_array_index (storage_ctx->supported_mem3, MMSmsStorage, 0)) return storing_default; } return MM_SMS_STORAGE_UNKNOWN; } static void interface_enabling_step (GTask *task) { MMIfaceModemMessaging *self; EnablingContext *ctx; /* Don't run new steps if we're cancelled */ if (g_task_return_error_if_cancelled (task)) { g_object_unref (task); return; } self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); switch (ctx->step) { case ENABLING_STEP_FIRST: { MMSmsList *list; list = mm_sms_list_new (MM_BASE_MODEM (self)); g_object_set (self, MM_IFACE_MODEM_MESSAGING_SMS_LIST, list, NULL); /* Connect to list's signals */ g_signal_connect (list, MM_SMS_ADDED, G_CALLBACK (sms_added), ctx->skeleton); g_signal_connect (list, MM_SMS_DELETED, G_CALLBACK (sms_deleted), ctx->skeleton); g_object_unref (list); ctx->step++; } /* fall through */ case ENABLING_STEP_SETUP_SMS_FORMAT: /* Allow setting SMS format to use */ if (MM_IFACE_MODEM_MESSAGING_GET_INTERFACE (self)->setup_sms_format && MM_IFACE_MODEM_MESSAGING_GET_INTERFACE (self)->setup_sms_format_finish) { MM_IFACE_MODEM_MESSAGING_GET_INTERFACE (self)->setup_sms_format ( self, (GAsyncReadyCallback)setup_sms_format_ready, task); return; } ctx->step++; /* fall through */ case ENABLING_STEP_STORAGE_DEFAULTS: { MMSmsStorage default_storage; /* Is there only one single storage supported? if so, we don't care if * setting default storage is implemented or not. */ default_storage = get_single_default_sms_storage (self); if (default_storage == MM_SMS_STORAGE_UNKNOWN) default_storage = get_best_initial_default_sms_storage (self); /* Already bound to the 'default-storage' property in the skeleton */ g_object_set (self, MM_IFACE_MODEM_MESSAGING_SMS_DEFAULT_STORAGE, default_storage, NULL); if (default_storage == MM_SMS_STORAGE_UNKNOWN) mm_obj_warn (self, "cannot set default storage, none of the suggested ones supported"); else if (MM_IFACE_MODEM_MESSAGING_GET_INTERFACE (self)->set_default_storage && MM_IFACE_MODEM_MESSAGING_GET_INTERFACE (self)->set_default_storage_finish) { MM_IFACE_MODEM_MESSAGING_GET_INTERFACE (self)->set_default_storage ( self, default_storage, (GAsyncReadyCallback)set_default_storage_ready, task); return; } ctx->step++; } /* fall through */ case ENABLING_STEP_LOAD_INITIAL_SMS_PARTS: /* Allow loading the initial list of SMS parts */ if (MM_IFACE_MODEM_MESSAGING_GET_INTERFACE (self)->load_initial_sms_parts && MM_IFACE_MODEM_MESSAGING_GET_INTERFACE (self)->load_initial_sms_parts_finish) { load_initial_sms_parts_from_storages (task); return; } ctx->step++; /* fall through */ case ENABLING_STEP_SETUP_UNSOLICITED_EVENTS: /* Allow setting up unsolicited events */ if (MM_IFACE_MODEM_MESSAGING_GET_INTERFACE (self)->setup_unsolicited_events && MM_IFACE_MODEM_MESSAGING_GET_INTERFACE (self)->setup_unsolicited_events_finish) { MM_IFACE_MODEM_MESSAGING_GET_INTERFACE (self)->setup_unsolicited_events ( self, (GAsyncReadyCallback)setup_unsolicited_events_ready, task); return; } ctx->step++; /* fall through */ case ENABLING_STEP_ENABLE_UNSOLICITED_EVENTS: /* Allow setting up unsolicited events */ if (MM_IFACE_MODEM_MESSAGING_GET_INTERFACE (self)->enable_unsolicited_events && MM_IFACE_MODEM_MESSAGING_GET_INTERFACE (self)->enable_unsolicited_events_finish) { MM_IFACE_MODEM_MESSAGING_GET_INTERFACE (self)->enable_unsolicited_events ( self, (GAsyncReadyCallback)enable_unsolicited_events_ready, task); return; } ctx->step++; /* fall through */ case ENABLING_STEP_LAST: /* We are done without errors! */ g_task_return_boolean (task, TRUE); g_object_unref (task); return; default: break; } g_assert_not_reached (); } void mm_iface_modem_messaging_enable (MMIfaceModemMessaging *self, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { EnablingContext *ctx; GTask *task; ctx = g_new0 (EnablingContext, 1); ctx->step = ENABLING_STEP_FIRST; task = g_task_new (self, cancellable, callback, user_data); g_task_set_task_data (task, ctx, (GDestroyNotify)enabling_context_free); g_object_get (self, MM_IFACE_MODEM_MESSAGING_DBUS_SKELETON, &ctx->skeleton, NULL); if (!ctx->skeleton) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't get interface skeleton"); g_object_unref (task); return; } interface_enabling_step (task); } /*****************************************************************************/ typedef struct _InitializationContext InitializationContext; static void interface_initialization_step (GTask *task); typedef enum { INITIALIZATION_STEP_FIRST, INITIALIZATION_STEP_CHECK_SUPPORT, INITIALIZATION_STEP_FAIL_IF_UNSUPPORTED, INITIALIZATION_STEP_LOAD_SUPPORTED_STORAGES, INITIALIZATION_STEP_INIT_CURRENT_STORAGES, INITIALIZATION_STEP_LAST } InitializationStep; struct _InitializationContext { MmGdbusModemMessaging *skeleton; InitializationStep step; }; static void initialization_context_free (InitializationContext *ctx) { g_object_unref (ctx->skeleton); g_free (ctx); } static void skip_unknown_storages (GArray *mem) { guint i = mem->len; if (!mem) return; /* Remove UNKNOWN from the list of supported storages */ while (i-- > 0) { if (g_array_index (mem, MMSmsStorage, i) == MM_SMS_STORAGE_UNKNOWN) g_array_remove_index (mem, i); } } static void load_supported_storages_ready (MMIfaceModemMessaging *self, GAsyncResult *res, GTask *task) { InitializationContext *ctx; StorageContext *storage_ctx; GError *error = NULL; ctx = g_task_get_task_data (task); storage_ctx = get_storage_context (self); if (!MM_IFACE_MODEM_MESSAGING_GET_INTERFACE (self)->load_supported_storages_finish ( self, res, &storage_ctx->supported_mem1, &storage_ctx->supported_mem2, &storage_ctx->supported_mem3, &error)) { mm_obj_dbg (self, "couldn't load supported storages: %s", error->message); g_error_free (error); } else { gchar *mem1; gchar *mem2; gchar *mem3; GArray *supported_storages; guint i; /* Never add unknown storages */ skip_unknown_storages (storage_ctx->supported_mem1); skip_unknown_storages (storage_ctx->supported_mem2); skip_unknown_storages (storage_ctx->supported_mem3); mem1 = mm_common_build_sms_storages_string ((MMSmsStorage *)(gpointer)storage_ctx->supported_mem1->data, storage_ctx->supported_mem1->len); mem2 = mm_common_build_sms_storages_string ((MMSmsStorage *)(gpointer)storage_ctx->supported_mem2->data, storage_ctx->supported_mem2->len); mem3 = mm_common_build_sms_storages_string ((MMSmsStorage *)(gpointer)storage_ctx->supported_mem3->data, storage_ctx->supported_mem3->len); mm_obj_dbg (self, "supported storages loaded:"); mm_obj_dbg (self, " mem1 (list/read/delete) storages: '%s'", mem1); mm_obj_dbg (self, " mem2 (write/send) storages: '%s'", mem2); mm_obj_dbg (self, " mem3 (reception) storages: '%s'", mem3); g_free (mem1); g_free (mem2); g_free (mem3); /* We set in the interface the list of storages which are allowed for * both write/send and receive */ supported_storages = g_array_sized_new (FALSE, FALSE, sizeof (guint32), storage_ctx->supported_mem2->len); for (i = 0; i < storage_ctx->supported_mem2->len; i++) { gboolean found = FALSE; guint j; for (j = 0; j < storage_ctx->supported_mem3->len && !found; j++) { if (g_array_index (storage_ctx->supported_mem3, MMSmsStorage, j) == g_array_index (storage_ctx->supported_mem2, MMSmsStorage, i)) found = TRUE; } if (found) { guint32 val; val = g_array_index (storage_ctx->supported_mem2, MMSmsStorage, i); g_array_append_val (supported_storages, val); } } mm_gdbus_modem_messaging_set_supported_storages ( ctx->skeleton, mm_common_sms_storages_garray_to_variant (supported_storages)); g_array_unref (supported_storages); } /* Go on to next step */ ctx->step++; interface_initialization_step (task); } static void check_support_ready (MMIfaceModemMessaging *self, GAsyncResult *res, GTask *task) { InitializationContext *ctx; GError *error = NULL; if (!MM_IFACE_MODEM_MESSAGING_GET_INTERFACE (self)->check_support_finish (self, res, &error)) { if (error) { /* This error shouldn't be treated as critical */ mm_obj_dbg (self, "messaging support check failed: %s", error->message); g_error_free (error); } } else { /* Messaging is supported! */ g_object_set_qdata (G_OBJECT (self), supported_quark, GUINT_TO_POINTER (TRUE)); } /* Go on to next step */ ctx = g_task_get_task_data (task); ctx->step++; interface_initialization_step (task); } static void init_current_storages_ready (MMIfaceModemMessaging *self, GAsyncResult *res, GTask *task) { InitializationContext *ctx; GError *error = NULL; if (!MM_IFACE_MODEM_MESSAGING_GET_INTERFACE (self)->init_current_storages_finish ( self, res, &error)) { mm_obj_dbg (self, "couldn't initialize current storages: %s", error->message); g_error_free (error); } else mm_obj_dbg (self, "current storages initialized"); /* Go on to next step */ ctx = g_task_get_task_data (task); ctx->step++; interface_initialization_step (task); } static void interface_initialization_step (GTask *task) { MMIfaceModemMessaging *self; InitializationContext *ctx; /* Don't run new steps if we're cancelled */ if (g_task_return_error_if_cancelled (task)) { g_object_unref (task); return; } self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); switch (ctx->step) { case INITIALIZATION_STEP_FIRST: /* Setup quarks if we didn't do it before */ if (G_UNLIKELY (!support_checked_quark)) support_checked_quark = (g_quark_from_static_string ( SUPPORT_CHECKED_TAG)); if (G_UNLIKELY (!supported_quark)) supported_quark = (g_quark_from_static_string ( SUPPORTED_TAG)); ctx->step++; /* fall through */ case INITIALIZATION_STEP_CHECK_SUPPORT: if (!GPOINTER_TO_UINT (g_object_get_qdata (G_OBJECT (self), support_checked_quark))) { /* Set the checked flag so that we don't run it again */ g_object_set_qdata (G_OBJECT (self), support_checked_quark, GUINT_TO_POINTER (TRUE)); /* Initially, assume we don't support it */ g_object_set_qdata (G_OBJECT (self), supported_quark, GUINT_TO_POINTER (FALSE)); if (MM_IFACE_MODEM_MESSAGING_GET_INTERFACE (self)->check_support && MM_IFACE_MODEM_MESSAGING_GET_INTERFACE (self)->check_support_finish) { MM_IFACE_MODEM_MESSAGING_GET_INTERFACE (self)->check_support ( self, (GAsyncReadyCallback)check_support_ready, task); return; } /* If there is no implementation to check support, assume we DON'T * support it. */ } ctx->step++; /* fall through */ case INITIALIZATION_STEP_FAIL_IF_UNSUPPORTED: if (!GPOINTER_TO_UINT (g_object_get_qdata (G_OBJECT (self), supported_quark))) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "Messaging not supported"); g_object_unref (task); return; } ctx->step++; /* fall through */ case INITIALIZATION_STEP_LOAD_SUPPORTED_STORAGES: if (MM_IFACE_MODEM_MESSAGING_GET_INTERFACE (self)->load_supported_storages && MM_IFACE_MODEM_MESSAGING_GET_INTERFACE (self)->load_supported_storages_finish) { MM_IFACE_MODEM_MESSAGING_GET_INTERFACE (self)->load_supported_storages ( self, (GAsyncReadyCallback)load_supported_storages_ready, task); return; } ctx->step++; /* fall through */ case INITIALIZATION_STEP_INIT_CURRENT_STORAGES: if (MM_IFACE_MODEM_MESSAGING_GET_INTERFACE (self)->init_current_storages && MM_IFACE_MODEM_MESSAGING_GET_INTERFACE (self)->init_current_storages_finish) { MM_IFACE_MODEM_MESSAGING_GET_INTERFACE (self)->init_current_storages ( self, (GAsyncReadyCallback)init_current_storages_ready, task); return; } ctx->step++; /* fall through */ case INITIALIZATION_STEP_LAST: /* We are done without errors! */ /* Handle method invocations */ g_signal_connect (ctx->skeleton, "handle-create", G_CALLBACK (handle_create), self); g_signal_connect (ctx->skeleton, "handle-delete", G_CALLBACK (handle_delete), self); g_signal_connect (ctx->skeleton, "handle-list", G_CALLBACK (handle_list), self); /* Finally, export the new interface */ mm_gdbus_object_skeleton_set_modem_messaging (MM_GDBUS_OBJECT_SKELETON (self), MM_GDBUS_MODEM_MESSAGING (ctx->skeleton)); g_task_return_boolean (task, TRUE); g_object_unref (task); return; default: break; } g_assert_not_reached (); } gboolean mm_iface_modem_messaging_initialize_finish (MMIfaceModemMessaging *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } void mm_iface_modem_messaging_initialize (MMIfaceModemMessaging *self, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { InitializationContext *ctx; MmGdbusModemMessaging *skeleton = NULL; GTask *task; /* Did we already create it? */ g_object_get (self, MM_IFACE_MODEM_MESSAGING_DBUS_SKELETON, &skeleton, NULL); if (!skeleton) { skeleton = mm_gdbus_modem_messaging_skeleton_new (); mm_gdbus_modem_messaging_set_supported_storages (skeleton, NULL); /* Bind our Default messaging property */ g_object_bind_property (self, MM_IFACE_MODEM_MESSAGING_SMS_DEFAULT_STORAGE, skeleton, "default-storage", G_BINDING_DEFAULT | G_BINDING_SYNC_CREATE); g_object_set (self, MM_IFACE_MODEM_MESSAGING_DBUS_SKELETON, skeleton, NULL); } /* Perform async initialization here */ ctx = g_new0 (InitializationContext, 1); ctx->step = INITIALIZATION_STEP_FIRST; ctx->skeleton = skeleton; task = g_task_new (self, cancellable, callback, user_data); g_task_set_task_data (task, ctx, (GDestroyNotify)initialization_context_free); interface_initialization_step (task); } void mm_iface_modem_messaging_shutdown (MMIfaceModemMessaging *self) { /* Unexport DBus interface and remove the skeleton */ mm_gdbus_object_skeleton_set_modem_messaging (MM_GDBUS_OBJECT_SKELETON (self), NULL); g_object_set (self, MM_IFACE_MODEM_MESSAGING_DBUS_SKELETON, NULL, NULL); } /*****************************************************************************/ static void iface_modem_messaging_init (gpointer g_iface) { static gboolean initialized = FALSE; if (initialized) return; /* Properties */ g_object_interface_install_property (g_iface, g_param_spec_object (MM_IFACE_MODEM_MESSAGING_DBUS_SKELETON, "Messaging DBus skeleton", "DBus skeleton for the Messaging interface", MM_GDBUS_TYPE_MODEM_MESSAGING_SKELETON, G_PARAM_READWRITE)); g_object_interface_install_property (g_iface, g_param_spec_object (MM_IFACE_MODEM_MESSAGING_SMS_LIST, "SMS list", "List of SMS objects managed in the interface", MM_TYPE_SMS_LIST, G_PARAM_READWRITE)); g_object_interface_install_property (g_iface, g_param_spec_boolean (MM_IFACE_MODEM_MESSAGING_SMS_PDU_MODE, "PDU mode", "Whether PDU mode should be used", FALSE, G_PARAM_READWRITE)); g_object_interface_install_property (g_iface, g_param_spec_enum (MM_IFACE_MODEM_MESSAGING_SMS_DEFAULT_STORAGE, "SMS default storage", "Default storage to be used when storing/receiving SMS messages", MM_TYPE_SMS_STORAGE, MM_SMS_STORAGE_ME, G_PARAM_READWRITE)); initialized = TRUE; } GType mm_iface_modem_messaging_get_type (void) { static GType iface_modem_messaging_type = 0; if (!G_UNLIKELY (iface_modem_messaging_type)) { static const GTypeInfo info = { sizeof (MMIfaceModemMessaging), /* class_size */ iface_modem_messaging_init, /* base_init */ NULL, /* base_finalize */ }; iface_modem_messaging_type = g_type_register_static (G_TYPE_INTERFACE, "MMIfaceModemMessaging", &info, 0); g_type_interface_add_prerequisite (iface_modem_messaging_type, MM_TYPE_IFACE_MODEM); } return iface_modem_messaging_type; } ModemManager-1.23.4-dev/src/mm-iface-modem-messaging.h000066400000000000000000000237761456466623000224340ustar00rootroot00000000000000/* -*- 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) 2012 Google, Inc. */ #ifndef MM_IFACE_MODEM_MESSAGING_H #define MM_IFACE_MODEM_MESSAGING_H #include #include #define _LIBMM_INSIDE_MM #include #include "mm-sms-part.h" #include "mm-base-sms.h" #define MM_TYPE_IFACE_MODEM_MESSAGING (mm_iface_modem_messaging_get_type ()) #define MM_IFACE_MODEM_MESSAGING(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_IFACE_MODEM_MESSAGING, MMIfaceModemMessaging)) #define MM_IS_IFACE_MODEM_MESSAGING(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_IFACE_MODEM_MESSAGING)) #define MM_IFACE_MODEM_MESSAGING_GET_INTERFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE ((obj), MM_TYPE_IFACE_MODEM_MESSAGING, MMIfaceModemMessaging)) #define MM_IFACE_MODEM_MESSAGING_DBUS_SKELETON "iface-modem-messaging-dbus-skeleton" #define MM_IFACE_MODEM_MESSAGING_SMS_LIST "iface-modem-messaging-sms-list" #define MM_IFACE_MODEM_MESSAGING_SMS_PDU_MODE "iface-modem-messaging-sms-pdu-mode" #define MM_IFACE_MODEM_MESSAGING_SMS_DEFAULT_STORAGE "iface-modem-messaging-sms-default-storage" typedef struct _MMIfaceModemMessaging MMIfaceModemMessaging; struct _MMIfaceModemMessaging { GTypeInterface g_iface; /* Check for Messaging support (async) */ void (* check_support) (MMIfaceModemMessaging *self, GAsyncReadyCallback callback, gpointer user_data); gboolean (*check_support_finish) (MMIfaceModemMessaging *self, GAsyncResult *res, GError **error); /* Load supported storages for... * mem1: listing/reading/deleting * mem2: writing/sending * mem3: receiving */ void (* load_supported_storages) (MMIfaceModemMessaging *self, GAsyncReadyCallback callback, gpointer user_data); gboolean (*load_supported_storages_finish) (MMIfaceModemMessaging *self, GAsyncResult *res, GArray **mem1, GArray **mem2, GArray **mem3, GError **error); /* Initializes the state of the storages */ void (* init_current_storages) (MMIfaceModemMessaging *self, GAsyncReadyCallback callback, gpointer user_data); gboolean (*init_current_storages_finish) (MMIfaceModemMessaging *self, GAsyncResult *res, GError **error); /* Set default storage (async) */ void (* set_default_storage) (MMIfaceModemMessaging *self, MMSmsStorage storage, GAsyncReadyCallback callback, gpointer user_data); gboolean (*set_default_storage_finish) (MMIfaceModemMessaging *self, GAsyncResult *res, GError **error); /* Setup SMS format (async) */ void (* setup_sms_format) (MMIfaceModemMessaging *self, GAsyncReadyCallback callback, gpointer user_data); gboolean (*setup_sms_format_finish) (MMIfaceModemMessaging *self, GAsyncResult *res, GError **error); /* Asynchronous setting up unsolicited SMS reception events */ void (*setup_unsolicited_events) (MMIfaceModemMessaging *self, GAsyncReadyCallback callback, gpointer user_data); gboolean (*setup_unsolicited_events_finish) (MMIfaceModemMessaging *self, GAsyncResult *res, GError **error); /* Asynchronous cleaning up of unsolicited SMS reception events */ void (*cleanup_unsolicited_events) (MMIfaceModemMessaging *self, GAsyncReadyCallback callback, gpointer user_data); gboolean (*cleanup_unsolicited_events_finish) (MMIfaceModemMessaging *self, GAsyncResult *res, GError **error); /* Asynchronous enabling unsolicited SMS reception events */ void (* enable_unsolicited_events) (MMIfaceModemMessaging *self, GAsyncReadyCallback callback, gpointer user_data); gboolean (* enable_unsolicited_events_finish) (MMIfaceModemMessaging *self, GAsyncResult *res, GError **error); /* Asynchronous disabling unsolicited SMS reception events */ void (* disable_unsolicited_events) (MMIfaceModemMessaging *self, GAsyncReadyCallback callback, gpointer user_data); gboolean (* disable_unsolicited_events_finish) (MMIfaceModemMessaging *self, GAsyncResult *res, GError **error); /* Load initial SMS parts (async). * Found parts need to be reported with take_part() */ void (* load_initial_sms_parts) (MMIfaceModemMessaging *self, MMSmsStorage storage, GAsyncReadyCallback callback, gpointer user_data); gboolean (*load_initial_sms_parts_finish) (MMIfaceModemMessaging *self, GAsyncResult *res, GError **error); /* Create SMS objects */ MMBaseSms * (* create_sms) (MMIfaceModemMessaging *self); }; GType mm_iface_modem_messaging_get_type (void); G_DEFINE_AUTOPTR_CLEANUP_FUNC (MMIfaceModemMessaging, g_object_unref) /* Initialize Messaging interface (async) */ void mm_iface_modem_messaging_initialize (MMIfaceModemMessaging *self, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); gboolean mm_iface_modem_messaging_initialize_finish (MMIfaceModemMessaging *self, GAsyncResult *res, GError **error); /* Enable Messaging interface (async) */ void mm_iface_modem_messaging_enable (MMIfaceModemMessaging *self, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); gboolean mm_iface_modem_messaging_enable_finish (MMIfaceModemMessaging *self, GAsyncResult *res, GError **error); /* Disable Messaging interface (async) */ void mm_iface_modem_messaging_disable (MMIfaceModemMessaging *self, GAsyncReadyCallback callback, gpointer user_data); gboolean mm_iface_modem_messaging_disable_finish (MMIfaceModemMessaging *self, GAsyncResult *res, GError **error); /* Shutdown Messaging interface */ void mm_iface_modem_messaging_shutdown (MMIfaceModemMessaging *self); /* Bind properties for simple GetStatus() */ void mm_iface_modem_messaging_bind_simple_status (MMIfaceModemMessaging *self, MMSimpleStatus *status); /* Report new SMS part */ gboolean mm_iface_modem_messaging_take_part (MMIfaceModemMessaging *self, MMSmsPart *sms_part, MMSmsState state, MMSmsStorage storage); /* Check storage support */ gboolean mm_iface_modem_messaging_is_storage_supported_for_storing (MMIfaceModemMessaging *self, MMSmsStorage storage, GError **error); gboolean mm_iface_modem_messaging_is_storage_supported_for_receiving (MMIfaceModemMessaging *self, MMSmsStorage storage, GError **error); /* SMS creation */ MMBaseSms *mm_iface_modem_messaging_create_sms (MMIfaceModemMessaging *self); /* Look for a new valid multipart reference */ guint8 mm_iface_modem_messaging_get_local_multipart_reference (MMIfaceModemMessaging *self, const gchar *number, GError **error); #endif /* MM_IFACE_MODEM_MESSAGING_H */ ModemManager-1.23.4-dev/src/mm-iface-modem-oma.c000066400000000000000000001235471456466623000212230ustar00rootroot00000000000000/* -*- 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) 2013 Google, Inc. */ #include #define _LIBMM_INSIDE_MM #include #include "mm-iface-modem.h" #include "mm-iface-modem-oma.h" #include "mm-error-helpers.h" #include "mm-log-object.h" #define SUPPORT_CHECKED_TAG "oma-support-checked-tag" #define SUPPORTED_TAG "oma-supported-tag" static GQuark support_checked_quark; static GQuark supported_quark; /*****************************************************************************/ void mm_iface_modem_oma_bind_simple_status (MMIfaceModemOma *self, MMSimpleStatus *status) { } /*****************************************************************************/ /* Manage the list of pending network-initiated sessions */ static void add_or_remove_pending_network_initiated_session (MMIfaceModemOma *self, gboolean add, MMOmaSessionType session_type, guint session_id) { MmGdbusModemOma *skeleton; GVariant *variant; GArray *array; guint i; g_assert (session_type != MM_OMA_SESSION_TYPE_UNKNOWN); g_object_get (self, MM_IFACE_MODEM_OMA_DBUS_SKELETON, &skeleton, NULL); if (!skeleton) return; variant = mm_gdbus_modem_oma_get_pending_network_initiated_sessions (skeleton); array = mm_common_oma_pending_network_initiated_sessions_variant_to_garray (variant); for (i = 0; i < array->len; i++) { MMOmaPendingNetworkInitiatedSession *session; session = &g_array_index (array, MMOmaPendingNetworkInitiatedSession, i); if (session->session_id == session_id) break; } /* If not in the array, and we want to add it, add it */ if (add && i == array->len) { MMOmaPendingNetworkInitiatedSession session; session.session_type = session_type; session.session_id = session_id; g_array_append_val (array, session); mm_gdbus_modem_oma_set_pending_network_initiated_sessions ( skeleton, mm_common_oma_pending_network_initiated_sessions_garray_to_variant (array)); } /* If found in the array, and we want to remove it, remove it */ else if (!add && i < array->len) { g_array_remove_index (array, i); mm_gdbus_modem_oma_set_pending_network_initiated_sessions ( skeleton, mm_common_oma_pending_network_initiated_sessions_garray_to_variant (array)); } g_object_unref (skeleton); g_array_unref (array); } void mm_iface_modem_oma_add_pending_network_initiated_session (MMIfaceModemOma *self, MMOmaSessionType session_type, guint session_id) { add_or_remove_pending_network_initiated_session (self, TRUE, session_type, session_id); } /*****************************************************************************/ /* New session state reported */ void mm_iface_modem_oma_update_session_state (MMIfaceModemOma *self, MMOmaSessionState new_session_state, MMOmaSessionStateFailedReason session_state_failed_reason) { MmGdbusModemOma *skeleton; MMOmaSessionState old_session_state; /* Make sure proper state vs failed reasons are given */ g_return_if_fail ((new_session_state != MM_OMA_SESSION_STATE_FAILED && session_state_failed_reason == MM_OMA_SESSION_STATE_FAILED_REASON_UNKNOWN) || (new_session_state == MM_OMA_SESSION_STATE_FAILED)); g_object_get (self, MM_IFACE_MODEM_OMA_DBUS_SKELETON, &skeleton, NULL); if (!skeleton) return; old_session_state = mm_gdbus_modem_oma_get_session_state (skeleton); if (old_session_state != new_session_state) { mm_obj_msg (self, "OMA session state changed (%s -> %s)", mm_oma_session_state_get_string (old_session_state), mm_oma_session_state_get_string (new_session_state)); /* Flush current change before signaling the state change, * so that clients get the proper state already in the * state-changed callback */ mm_gdbus_modem_oma_set_session_state (skeleton, new_session_state); g_dbus_interface_skeleton_flush (G_DBUS_INTERFACE_SKELETON (skeleton)); mm_gdbus_modem_oma_emit_session_state_changed ( skeleton, old_session_state, new_session_state, session_state_failed_reason); } } /*****************************************************************************/ /* Handle Setup() */ typedef struct { MmGdbusModemOma *skeleton; GDBusMethodInvocation *invocation; MMIfaceModemOma *self; guint32 features; } HandleSetupContext; static void handle_setup_context_free (HandleSetupContext *ctx) { g_object_unref (ctx->skeleton); g_object_unref (ctx->invocation); g_object_unref (ctx->self); g_slice_free (HandleSetupContext, ctx); } static void setup_ready (MMIfaceModemOma *self, GAsyncResult *res, HandleSetupContext *ctx) { GError *error = NULL; if (!MM_IFACE_MODEM_OMA_GET_INTERFACE (ctx->self)->setup_finish (self, res, &error)) mm_dbus_method_invocation_take_error (ctx->invocation, error); else { /* Update current features in the interface */ mm_gdbus_modem_oma_set_features (ctx->skeleton, ctx->features); mm_gdbus_modem_oma_complete_setup (ctx->skeleton, ctx->invocation); } handle_setup_context_free (ctx); } static void handle_setup_auth_ready (MMBaseModem *self, GAsyncResult *res, HandleSetupContext *ctx) { GError *error = NULL; MMModemState modem_state; gchar *str; if (!mm_base_modem_authorize_finish (self, res, &error)) { mm_dbus_method_invocation_take_error (ctx->invocation, error); handle_setup_context_free (ctx); return; } modem_state = MM_MODEM_STATE_UNKNOWN; g_object_get (self, MM_IFACE_MODEM_STATE, &modem_state, NULL); if (modem_state < MM_MODEM_STATE_ENABLED) { mm_dbus_method_invocation_return_error_literal (ctx->invocation, MM_CORE_ERROR, MM_CORE_ERROR_WRONG_STATE, "Cannot setup OMA: device not yet enabled"); handle_setup_context_free (ctx); return; } if (!MM_IFACE_MODEM_OMA_GET_INTERFACE (ctx->self)->setup || !MM_IFACE_MODEM_OMA_GET_INTERFACE (ctx->self)->setup_finish) { mm_dbus_method_invocation_return_error_literal (ctx->invocation, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "Cannot setup OMA: operation not supported"); handle_setup_context_free (ctx); return; } str = mm_oma_feature_build_string_from_mask (ctx->features); mm_obj_dbg (self, "setting up OMA features: '%s'", str); g_free (str); MM_IFACE_MODEM_OMA_GET_INTERFACE (ctx->self)->setup ( ctx->self, ctx->features, (GAsyncReadyCallback)setup_ready, ctx); } static gboolean handle_setup (MmGdbusModemOma *skeleton, GDBusMethodInvocation *invocation, guint32 features, MMIfaceModemOma *self) { HandleSetupContext *ctx; ctx = g_slice_new0 (HandleSetupContext); ctx->skeleton = g_object_ref (skeleton); ctx->invocation = g_object_ref (invocation); ctx->self = g_object_ref (self); ctx->features = features; mm_base_modem_authorize (MM_BASE_MODEM (self), invocation, MM_AUTHORIZATION_DEVICE_CONTROL, (GAsyncReadyCallback)handle_setup_auth_ready, ctx); return TRUE; } /*****************************************************************************/ /* Handle StartClientInitiatedSession() */ typedef struct { MmGdbusModemOma *skeleton; GDBusMethodInvocation *invocation; MMIfaceModemOma *self; MMOmaSessionType session_type; } HandleStartClientInitiatedSessionContext; static void handle_start_client_initiated_session_context_free (HandleStartClientInitiatedSessionContext *ctx) { g_object_unref (ctx->skeleton); g_object_unref (ctx->invocation); g_object_unref (ctx->self); g_slice_free (HandleStartClientInitiatedSessionContext, ctx); } static void start_client_initiated_session_ready (MMIfaceModemOma *self, GAsyncResult *res, HandleStartClientInitiatedSessionContext *ctx) { GError *error = NULL; if (!MM_IFACE_MODEM_OMA_GET_INTERFACE (ctx->self)->start_client_initiated_session_finish (self, res, &error)) mm_dbus_method_invocation_take_error (ctx->invocation, error); else { /* Update interface info */ mm_gdbus_modem_oma_set_session_type (ctx->skeleton, ctx->session_type); mm_iface_modem_oma_update_session_state (self, MM_OMA_SESSION_STATE_STARTED, MM_OMA_SESSION_STATE_FAILED_REASON_UNKNOWN); mm_gdbus_modem_oma_complete_start_client_initiated_session (ctx->skeleton, ctx->invocation); } handle_start_client_initiated_session_context_free (ctx); } static void handle_start_client_initiated_session_auth_ready (MMBaseModem *self, GAsyncResult *res, HandleStartClientInitiatedSessionContext *ctx) { GError *error = NULL; MMModemState modem_state; if (!mm_base_modem_authorize_finish (self, res, &error)) { mm_dbus_method_invocation_take_error (ctx->invocation, error); handle_start_client_initiated_session_context_free (ctx); return; } modem_state = MM_MODEM_STATE_UNKNOWN; g_object_get (self, MM_IFACE_MODEM_STATE, &modem_state, NULL); if (modem_state < MM_MODEM_STATE_ENABLED) { mm_dbus_method_invocation_return_error_literal (ctx->invocation, MM_CORE_ERROR, MM_CORE_ERROR_WRONG_STATE, "Cannot start client-initiated OMA session: " "device not yet enabled"); handle_start_client_initiated_session_context_free (ctx); return; } if (!MM_IFACE_MODEM_OMA_GET_INTERFACE (ctx->self)->start_client_initiated_session || !MM_IFACE_MODEM_OMA_GET_INTERFACE (ctx->self)->start_client_initiated_session_finish) { mm_dbus_method_invocation_return_error_literal (ctx->invocation, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "Cannot start client-initiated OMA session: " "operation not supported"); handle_start_client_initiated_session_context_free (ctx); return; } if (ctx->session_type != MM_OMA_SESSION_TYPE_CLIENT_INITIATED_DEVICE_CONFIGURE && ctx->session_type != MM_OMA_SESSION_TYPE_CLIENT_INITIATED_PRL_UPDATE && ctx->session_type != MM_OMA_SESSION_TYPE_CLIENT_INITIATED_HANDS_FREE_ACTIVATION) { mm_dbus_method_invocation_return_error (ctx->invocation, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "Cannot start client-initiated OMA session: " "invalid session type specified (%s)", mm_oma_session_type_get_string (ctx->session_type)); handle_start_client_initiated_session_context_free (ctx); return; } mm_obj_dbg (self, "starting client-initiated OMA session (%s)", mm_oma_session_type_get_string (ctx->session_type)); MM_IFACE_MODEM_OMA_GET_INTERFACE (ctx->self)->start_client_initiated_session ( ctx->self, ctx->session_type, (GAsyncReadyCallback)start_client_initiated_session_ready, ctx); } static gboolean handle_start_client_initiated_session (MmGdbusModemOma *skeleton, GDBusMethodInvocation *invocation, guint32 session_type, MMIfaceModemOma *self) { HandleStartClientInitiatedSessionContext *ctx; ctx = g_slice_new0 (HandleStartClientInitiatedSessionContext); ctx->skeleton = g_object_ref (skeleton); ctx->invocation = g_object_ref (invocation); ctx->self = g_object_ref (self); ctx->session_type = session_type; mm_base_modem_authorize (MM_BASE_MODEM (self), invocation, MM_AUTHORIZATION_DEVICE_CONTROL, (GAsyncReadyCallback)handle_start_client_initiated_session_auth_ready, ctx); return TRUE; } /*****************************************************************************/ /* Handle AcceptNetworkInitiatedSession() */ typedef struct { MmGdbusModemOma *skeleton; GDBusMethodInvocation *invocation; MMIfaceModemOma *self; MMOmaSessionType session_type; guint session_id; gboolean accept; } HandleAcceptNetworkInitiatedSessionContext; static void handle_accept_network_initiated_session_context_free (HandleAcceptNetworkInitiatedSessionContext *ctx) { g_object_unref (ctx->skeleton); g_object_unref (ctx->invocation); g_object_unref (ctx->self); g_slice_free (HandleAcceptNetworkInitiatedSessionContext, ctx); } static void accept_network_initiated_session_ready (MMIfaceModemOma *self, GAsyncResult *res, HandleAcceptNetworkInitiatedSessionContext *ctx) { GError *error = NULL; if (!MM_IFACE_MODEM_OMA_GET_INTERFACE (ctx->self)->accept_network_initiated_session_finish (self, res, &error)) mm_dbus_method_invocation_take_error (ctx->invocation, error); else { /* If accepted or rejected, remove from pending */ add_or_remove_pending_network_initiated_session (self, FALSE, ctx->session_type, ctx->session_id); /* If accepted, set as current */ if (ctx->accept) { mm_gdbus_modem_oma_set_session_type (ctx->skeleton, ctx->session_type); mm_iface_modem_oma_update_session_state (self, MM_OMA_SESSION_STATE_STARTED, MM_OMA_SESSION_STATE_FAILED_REASON_UNKNOWN); } mm_gdbus_modem_oma_complete_accept_network_initiated_session (ctx->skeleton, ctx->invocation); } handle_accept_network_initiated_session_context_free (ctx); } static MMOmaSessionType get_pending_network_initiated_session_type (MMIfaceModemOma *self, guint session_id) { MMOmaSessionType session_type = MM_OMA_SESSION_TYPE_UNKNOWN; MmGdbusModemOma *skeleton; g_object_get (self, MM_IFACE_MODEM_OMA_DBUS_SKELETON, &skeleton, NULL); if (skeleton) { GArray *array; guint i; array = (mm_common_oma_pending_network_initiated_sessions_variant_to_garray ( mm_gdbus_modem_oma_get_pending_network_initiated_sessions (skeleton))); for (i = 0; i < array->len && session_type == MM_OMA_SESSION_TYPE_UNKNOWN; i++) { MMOmaPendingNetworkInitiatedSession *session; session = &g_array_index (array, MMOmaPendingNetworkInitiatedSession, i); if (session->session_id == session_id) session_type = session->session_type; } g_array_unref (array); g_object_unref (skeleton); } return session_type; } static void handle_accept_network_initiated_session_auth_ready (MMBaseModem *self, GAsyncResult *res, HandleAcceptNetworkInitiatedSessionContext *ctx) { GError *error = NULL; MMModemState modem_state; if (!mm_base_modem_authorize_finish (self, res, &error)) { mm_dbus_method_invocation_take_error (ctx->invocation, error); handle_accept_network_initiated_session_context_free (ctx); return; } modem_state = MM_MODEM_STATE_UNKNOWN; g_object_get (self, MM_IFACE_MODEM_STATE, &modem_state, NULL); if (modem_state < MM_MODEM_STATE_ENABLED) { mm_dbus_method_invocation_return_error_literal (ctx->invocation, MM_CORE_ERROR, MM_CORE_ERROR_WRONG_STATE, "Cannot accept network-initiated OMA session: " "device not yet enabled"); handle_accept_network_initiated_session_context_free (ctx); return; } if (!MM_IFACE_MODEM_OMA_GET_INTERFACE (ctx->self)->accept_network_initiated_session || !MM_IFACE_MODEM_OMA_GET_INTERFACE (ctx->self)->accept_network_initiated_session_finish) { mm_dbus_method_invocation_return_error_literal (ctx->invocation, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "Cannot accept network-initiated OMA session: " "operation not supported"); handle_accept_network_initiated_session_context_free (ctx); return; } ctx->session_type = get_pending_network_initiated_session_type (ctx->self, ctx->session_id); if (ctx->session_type == MM_OMA_SESSION_TYPE_UNKNOWN) { mm_dbus_method_invocation_return_error (ctx->invocation, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "Cannot accept network-initiated OMA session: " "unknown session id (%u)", ctx->session_id); handle_accept_network_initiated_session_context_free (ctx); return; } mm_obj_dbg (self, "%s network-initiated OMA session (%s, %u)", ctx->accept ? "accepting" : "rejecting", mm_oma_session_type_get_string (ctx->session_type), ctx->session_id); MM_IFACE_MODEM_OMA_GET_INTERFACE (ctx->self)->accept_network_initiated_session ( ctx->self, ctx->session_id, ctx->accept, (GAsyncReadyCallback)accept_network_initiated_session_ready, ctx); } static gboolean handle_accept_network_initiated_session (MmGdbusModemOma *skeleton, GDBusMethodInvocation *invocation, guint32 session_id, gboolean accept, MMIfaceModemOma *self) { HandleAcceptNetworkInitiatedSessionContext *ctx; ctx = g_slice_new0 (HandleAcceptNetworkInitiatedSessionContext); ctx->skeleton = g_object_ref (skeleton); ctx->invocation = g_object_ref (invocation); ctx->self = g_object_ref (self); ctx->session_type = MM_OMA_SESSION_TYPE_UNKNOWN; ctx->session_id = session_id; ctx->accept = accept; mm_base_modem_authorize (MM_BASE_MODEM (self), invocation, MM_AUTHORIZATION_DEVICE_CONTROL, (GAsyncReadyCallback)handle_accept_network_initiated_session_auth_ready, ctx); return TRUE; } /*****************************************************************************/ /* Handle CancelSession() */ typedef struct { MmGdbusModemOma *skeleton; GDBusMethodInvocation *invocation; MMIfaceModemOma *self; } HandleCancelSessionContext; static void handle_cancel_session_context_free (HandleCancelSessionContext *ctx) { g_object_unref (ctx->skeleton); g_object_unref (ctx->invocation); g_object_unref (ctx->self); g_slice_free (HandleCancelSessionContext, ctx); } static void cancel_session_ready (MMIfaceModemOma *self, GAsyncResult *res, HandleCancelSessionContext *ctx) { GError *error = NULL; if (!MM_IFACE_MODEM_OMA_GET_INTERFACE (ctx->self)->cancel_session_finish (self, res, &error)) mm_dbus_method_invocation_take_error (ctx->invocation, error); else { /* Clear interface info when cancelled */ mm_gdbus_modem_oma_set_session_type (ctx->skeleton, MM_OMA_SESSION_TYPE_UNKNOWN); mm_iface_modem_oma_update_session_state (self, MM_OMA_SESSION_STATE_UNKNOWN, MM_OMA_SESSION_STATE_FAILED_REASON_UNKNOWN); mm_gdbus_modem_oma_complete_cancel_session (ctx->skeleton, ctx->invocation); } handle_cancel_session_context_free (ctx); } static void handle_cancel_session_auth_ready (MMBaseModem *self, GAsyncResult *res, HandleCancelSessionContext *ctx) { GError *error = NULL; MMModemState modem_state; if (!mm_base_modem_authorize_finish (self, res, &error)) { mm_dbus_method_invocation_take_error (ctx->invocation, error); handle_cancel_session_context_free (ctx); return; } modem_state = MM_MODEM_STATE_UNKNOWN; g_object_get (self, MM_IFACE_MODEM_STATE, &modem_state, NULL); if (modem_state < MM_MODEM_STATE_ENABLED) { mm_dbus_method_invocation_return_error_literal (ctx->invocation, MM_CORE_ERROR, MM_CORE_ERROR_WRONG_STATE, "Cannot cancel OMA session: device not yet enabled"); handle_cancel_session_context_free (ctx); return; } if (!MM_IFACE_MODEM_OMA_GET_INTERFACE (ctx->self)->cancel_session || !MM_IFACE_MODEM_OMA_GET_INTERFACE (ctx->self)->cancel_session_finish) { mm_dbus_method_invocation_return_error_literal (ctx->invocation, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "Cannot cancel OMA session: operation not supported"); handle_cancel_session_context_free (ctx); return; } mm_obj_dbg (self, "cancelling OMA session"); MM_IFACE_MODEM_OMA_GET_INTERFACE (ctx->self)->cancel_session ( ctx->self, (GAsyncReadyCallback)cancel_session_ready, ctx); } static gboolean handle_cancel_session (MmGdbusModemOma *skeleton, GDBusMethodInvocation *invocation, MMIfaceModemOma *self) { HandleCancelSessionContext *ctx; ctx = g_slice_new0 (HandleCancelSessionContext); ctx->skeleton = g_object_ref (skeleton); ctx->invocation = g_object_ref (invocation); ctx->self = g_object_ref (self); mm_base_modem_authorize (MM_BASE_MODEM (self), invocation, MM_AUTHORIZATION_DEVICE_CONTROL, (GAsyncReadyCallback)handle_cancel_session_auth_ready, ctx); return TRUE; } /*****************************************************************************/ typedef struct _DisablingContext DisablingContext; static void interface_disabling_step (GTask *task); typedef enum { DISABLING_STEP_FIRST, DISABLING_STEP_DISABLE_UNSOLICITED_EVENTS, DISABLING_STEP_CLEANUP_UNSOLICITED_EVENTS, DISABLING_STEP_LAST } DisablingStep; struct _DisablingContext { DisablingStep step; MmGdbusModemOma *skeleton; }; static void disabling_context_free (DisablingContext *ctx) { if (ctx->skeleton) g_object_unref (ctx->skeleton); g_free (ctx); } gboolean mm_iface_modem_oma_disable_finish (MMIfaceModemOma *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void disable_unsolicited_events_ready (MMIfaceModemOma *self, GAsyncResult *res, GTask *task) { DisablingContext *ctx; GError *error = NULL; MM_IFACE_MODEM_OMA_GET_INTERFACE (self)->disable_unsolicited_events_finish (self, res, &error); if (error) { g_task_return_error (task, error); g_object_unref (task); return; } /* Go on to next step */ ctx = g_task_get_task_data (task); ctx->step++; interface_disabling_step (task); } static void cleanup_unsolicited_events_ready (MMIfaceModemOma *self, GAsyncResult *res, GTask *task) { DisablingContext *ctx; GError *error = NULL; MM_IFACE_MODEM_OMA_GET_INTERFACE (self)->cleanup_unsolicited_events_finish (self, res, &error); if (error) { g_task_return_error (task, error); g_object_unref (task); return; } /* Go on to next step */ ctx = g_task_get_task_data (task); ctx->step++; interface_disabling_step (task); } static void interface_disabling_step (GTask *task) { MMIfaceModemOma *self; DisablingContext *ctx; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); switch (ctx->step) { case DISABLING_STEP_FIRST: ctx->step++; /* fall through */ case DISABLING_STEP_DISABLE_UNSOLICITED_EVENTS: /* Allow cleaning up unsolicited events */ if (MM_IFACE_MODEM_OMA_GET_INTERFACE (self)->disable_unsolicited_events && MM_IFACE_MODEM_OMA_GET_INTERFACE (self)->disable_unsolicited_events_finish) { MM_IFACE_MODEM_OMA_GET_INTERFACE (self)->disable_unsolicited_events ( self, (GAsyncReadyCallback)disable_unsolicited_events_ready, task); return; } ctx->step++; /* fall through */ case DISABLING_STEP_CLEANUP_UNSOLICITED_EVENTS: /* Allow cleaning up unsolicited events */ if (MM_IFACE_MODEM_OMA_GET_INTERFACE (self)->cleanup_unsolicited_events && MM_IFACE_MODEM_OMA_GET_INTERFACE (self)->cleanup_unsolicited_events_finish) { MM_IFACE_MODEM_OMA_GET_INTERFACE (self)->cleanup_unsolicited_events ( self, (GAsyncReadyCallback)cleanup_unsolicited_events_ready, task); return; } ctx->step++; /* fall through */ case DISABLING_STEP_LAST: /* We are done without errors! */ g_task_return_boolean (task, TRUE); g_object_unref (task); return; default: break; } g_assert_not_reached (); } void mm_iface_modem_oma_disable (MMIfaceModemOma *self, GAsyncReadyCallback callback, gpointer user_data) { DisablingContext *ctx; GTask *task; ctx = g_new0 (DisablingContext, 1); ctx->step = DISABLING_STEP_FIRST; task = g_task_new (self, NULL, callback, user_data); g_task_set_task_data (task, ctx, (GDestroyNotify)disabling_context_free); g_object_get (self, MM_IFACE_MODEM_OMA_DBUS_SKELETON, &ctx->skeleton, NULL); if (!ctx->skeleton) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't get interface skeleton"); g_object_unref (task); return; } interface_disabling_step (task); } /*****************************************************************************/ typedef struct _EnablingContext EnablingContext; static void interface_enabling_step (GTask *task); typedef enum { ENABLING_STEP_FIRST, ENABLING_STEP_LOAD_FEATURES, ENABLING_STEP_SETUP_UNSOLICITED_EVENTS, ENABLING_STEP_ENABLE_UNSOLICITED_EVENTS, ENABLING_STEP_LAST } EnablingStep; struct _EnablingContext { EnablingStep step; MmGdbusModemOma *skeleton; }; static void enabling_context_free (EnablingContext *ctx) { if (ctx->skeleton) g_object_unref (ctx->skeleton); g_free (ctx); } gboolean mm_iface_modem_oma_enable_finish (MMIfaceModemOma *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void load_features_ready (MMIfaceModemOma *self, GAsyncResult *res, GTask *task) { EnablingContext *ctx; GError *error = NULL; MMOmaFeature features; features = MM_IFACE_MODEM_OMA_GET_INTERFACE (self)->load_features_finish (self, res, &error); if (error) { g_task_return_error (task, error); g_object_unref (task); return; } ctx = g_task_get_task_data (task); /* Update in the interface */ mm_gdbus_modem_oma_set_features (ctx->skeleton, features); /* Go on to next step */ ctx->step++; interface_enabling_step (task); } static void setup_unsolicited_events_ready (MMIfaceModemOma *self, GAsyncResult *res, GTask *task) { EnablingContext *ctx; GError *error = NULL; MM_IFACE_MODEM_OMA_GET_INTERFACE (self)->setup_unsolicited_events_finish (self, res, &error); if (error) { g_task_return_error (task, error); g_object_unref (task); return; } /* Go on to next step */ ctx = g_task_get_task_data (task); ctx->step++; interface_enabling_step (task); } static void enable_unsolicited_events_ready (MMIfaceModemOma *self, GAsyncResult *res, GTask *task) { EnablingContext *ctx; GError *error = NULL; /* Not critical! */ if (!MM_IFACE_MODEM_OMA_GET_INTERFACE (self)->enable_unsolicited_events_finish (self, res, &error)) { mm_obj_dbg (self, "couldn't enable unsolicited events: %s", error->message); g_error_free (error); } /* Go on with next step */ ctx = g_task_get_task_data (task); ctx->step++; interface_enabling_step (task); } static void interface_enabling_step (GTask *task) { MMIfaceModemOma *self; EnablingContext *ctx; /* Don't run new steps if we're cancelled */ if (g_task_return_error_if_cancelled (task)) { g_object_unref (task); return; } self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); switch (ctx->step) { case ENABLING_STEP_FIRST: ctx->step++; /* fall through */ case ENABLING_STEP_LOAD_FEATURES: if (MM_IFACE_MODEM_OMA_GET_INTERFACE (self)->load_features && MM_IFACE_MODEM_OMA_GET_INTERFACE (self)->load_features_finish) { MM_IFACE_MODEM_OMA_GET_INTERFACE (self)->load_features ( self, (GAsyncReadyCallback)load_features_ready, task); return; } ctx->step++; /* fall through */ case ENABLING_STEP_SETUP_UNSOLICITED_EVENTS: /* Allow setting up unsolicited events */ if (MM_IFACE_MODEM_OMA_GET_INTERFACE (self)->setup_unsolicited_events && MM_IFACE_MODEM_OMA_GET_INTERFACE (self)->setup_unsolicited_events_finish) { MM_IFACE_MODEM_OMA_GET_INTERFACE (self)->setup_unsolicited_events ( self, (GAsyncReadyCallback)setup_unsolicited_events_ready, task); return; } ctx->step++; /* fall through */ case ENABLING_STEP_ENABLE_UNSOLICITED_EVENTS: /* Allow setting up unsolicited events */ if (MM_IFACE_MODEM_OMA_GET_INTERFACE (self)->enable_unsolicited_events && MM_IFACE_MODEM_OMA_GET_INTERFACE (self)->enable_unsolicited_events_finish) { MM_IFACE_MODEM_OMA_GET_INTERFACE (self)->enable_unsolicited_events ( self, (GAsyncReadyCallback)enable_unsolicited_events_ready, task); return; } ctx->step++; /* fall through */ case ENABLING_STEP_LAST: /* We are done without errors! */ g_task_return_boolean (task, TRUE); g_object_unref (task); return; default: break; } g_assert_not_reached (); } void mm_iface_modem_oma_enable (MMIfaceModemOma *self, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { EnablingContext *ctx; GTask *task; ctx = g_new0 (EnablingContext, 1); ctx->step = ENABLING_STEP_FIRST; task = g_task_new (self, cancellable, callback, user_data); g_task_set_task_data (task, ctx, (GDestroyNotify)enabling_context_free); g_object_get (self, MM_IFACE_MODEM_OMA_DBUS_SKELETON, &ctx->skeleton, NULL); if (!ctx->skeleton) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't get interface skeleton"); g_object_unref (task); return; } interface_enabling_step (task); } /*****************************************************************************/ typedef struct _InitializationContext InitializationContext; static void interface_initialization_step (GTask *task); typedef enum { INITIALIZATION_STEP_FIRST, INITIALIZATION_STEP_CHECK_SUPPORT, INITIALIZATION_STEP_FAIL_IF_UNSUPPORTED, INITIALIZATION_STEP_LAST } InitializationStep; struct _InitializationContext { MmGdbusModemOma *skeleton; InitializationStep step; }; static void initialization_context_free (InitializationContext *ctx) { g_object_unref (ctx->skeleton); g_free (ctx); } static void check_support_ready (MMIfaceModemOma *self, GAsyncResult *res, GTask *task) { InitializationContext *ctx; GError *error = NULL; if (!MM_IFACE_MODEM_OMA_GET_INTERFACE (self)->check_support_finish (self, res, &error)) { if (error) { /* This error shouldn't be treated as critical */ mm_obj_dbg (self, "OMA support check failed: %s", error->message); g_error_free (error); } } else { /* OMA is supported! */ g_object_set_qdata (G_OBJECT (self), supported_quark, GUINT_TO_POINTER (TRUE)); } /* Go on to next step */ ctx = g_task_get_task_data (task); ctx->step++; interface_initialization_step (task); } static void interface_initialization_step (GTask *task) { MMIfaceModemOma *self; InitializationContext *ctx; /* Don't run new steps if we're cancelled */ if (g_task_return_error_if_cancelled (task)) { g_object_unref (task); return; } self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); switch (ctx->step) { case INITIALIZATION_STEP_FIRST: /* Setup quarks if we didn't do it before */ if (G_UNLIKELY (!support_checked_quark)) support_checked_quark = (g_quark_from_static_string ( SUPPORT_CHECKED_TAG)); if (G_UNLIKELY (!supported_quark)) supported_quark = (g_quark_from_static_string ( SUPPORTED_TAG)); ctx->step++; /* fall through */ case INITIALIZATION_STEP_CHECK_SUPPORT: if (!GPOINTER_TO_UINT (g_object_get_qdata (G_OBJECT (self), support_checked_quark))) { /* Set the checked flag so that we don't run it again */ g_object_set_qdata (G_OBJECT (self), support_checked_quark, GUINT_TO_POINTER (TRUE)); /* Initially, assume we don't support it */ g_object_set_qdata (G_OBJECT (self), supported_quark, GUINT_TO_POINTER (FALSE)); if (MM_IFACE_MODEM_OMA_GET_INTERFACE (self)->check_support && MM_IFACE_MODEM_OMA_GET_INTERFACE (self)->check_support_finish) { MM_IFACE_MODEM_OMA_GET_INTERFACE (self)->check_support ( self, (GAsyncReadyCallback)check_support_ready, task); return; } /* If there is no implementation to check support, assume we DON'T * support it. */ } ctx->step++; /* fall through */ case INITIALIZATION_STEP_FAIL_IF_UNSUPPORTED: if (!GPOINTER_TO_UINT (g_object_get_qdata (G_OBJECT (self), supported_quark))) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "OMA not supported"); g_object_unref (task); return; } ctx->step++; /* fall through */ case INITIALIZATION_STEP_LAST: /* We are done without errors! */ /* Handle method invocations */ g_signal_connect (ctx->skeleton, "handle-setup", G_CALLBACK (handle_setup), self); g_signal_connect (ctx->skeleton, "handle-start-client-initiated-session", G_CALLBACK (handle_start_client_initiated_session), self); g_signal_connect (ctx->skeleton, "handle-accept-network-initiated-session", G_CALLBACK (handle_accept_network_initiated_session), self); g_signal_connect (ctx->skeleton, "handle-cancel-session", G_CALLBACK (handle_cancel_session), self); /* Finally, export the new interface */ mm_gdbus_object_skeleton_set_modem_oma (MM_GDBUS_OBJECT_SKELETON (self), MM_GDBUS_MODEM_OMA (ctx->skeleton)); g_task_return_boolean (task, TRUE); g_object_unref (task); return; default: break; } g_assert_not_reached (); } gboolean mm_iface_modem_oma_initialize_finish (MMIfaceModemOma *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } void mm_iface_modem_oma_initialize (MMIfaceModemOma *self, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { InitializationContext *ctx; MmGdbusModemOma *skeleton = NULL; GTask *task; /* Did we already create it? */ g_object_get (self, MM_IFACE_MODEM_OMA_DBUS_SKELETON, &skeleton, NULL); if (!skeleton) { skeleton = mm_gdbus_modem_oma_skeleton_new (); g_object_set (self, MM_IFACE_MODEM_OMA_DBUS_SKELETON, skeleton, NULL); /* Set all initial property defaults */ mm_gdbus_modem_oma_set_features (skeleton, MM_OMA_FEATURE_NONE); mm_gdbus_modem_oma_set_session_type (skeleton, MM_OMA_SESSION_TYPE_UNKNOWN); mm_gdbus_modem_oma_set_session_state (skeleton, MM_OMA_SESSION_STATE_UNKNOWN); mm_gdbus_modem_oma_set_pending_network_initiated_sessions (skeleton, mm_common_build_oma_pending_network_initiated_sessions_default ()); } /* Perform async initialization here */ ctx = g_new0 (InitializationContext, 1); ctx->step = INITIALIZATION_STEP_FIRST; ctx->skeleton = skeleton; task = g_task_new (self, cancellable, callback, user_data); g_task_set_task_data (task, ctx, (GDestroyNotify)initialization_context_free); interface_initialization_step (task); } void mm_iface_modem_oma_shutdown (MMIfaceModemOma *self) { /* Unexport DBus interface and remove the skeleton */ mm_gdbus_object_skeleton_set_modem_oma (MM_GDBUS_OBJECT_SKELETON (self), NULL); g_object_set (self, MM_IFACE_MODEM_OMA_DBUS_SKELETON, NULL, NULL); } /*****************************************************************************/ static void iface_modem_oma_init (gpointer g_iface) { static gboolean initialized = FALSE; if (initialized) return; /* Properties */ g_object_interface_install_property (g_iface, g_param_spec_object (MM_IFACE_MODEM_OMA_DBUS_SKELETON, "OMA DBus skeleton", "DBus skeleton for the OMA interface", MM_GDBUS_TYPE_MODEM_OMA_SKELETON, G_PARAM_READWRITE)); initialized = TRUE; } GType mm_iface_modem_oma_get_type (void) { static GType iface_modem_oma_type = 0; if (!G_UNLIKELY (iface_modem_oma_type)) { static const GTypeInfo info = { sizeof (MMIfaceModemOma), /* class_size */ iface_modem_oma_init, /* base_init */ NULL, /* base_finalize */ }; iface_modem_oma_type = g_type_register_static (G_TYPE_INTERFACE, "MMIfaceModemOma", &info, 0); g_type_interface_add_prerequisite (iface_modem_oma_type, MM_TYPE_IFACE_MODEM); } return iface_modem_oma_type; } ModemManager-1.23.4-dev/src/mm-iface-modem-oma.h000066400000000000000000000210541456466623000212160ustar00rootroot00000000000000/* -*- 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) 2013 Google, Inc. */ #ifndef MM_IFACE_MODEM_OMA_H #define MM_IFACE_MODEM_OMA_H #include #include #define _LIBMM_INSIDE_MM #include #define MM_TYPE_IFACE_MODEM_OMA (mm_iface_modem_oma_get_type ()) #define MM_IFACE_MODEM_OMA(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_IFACE_MODEM_OMA, MMIfaceModemOma)) #define MM_IS_IFACE_MODEM_OMA(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_IFACE_MODEM_OMA)) #define MM_IFACE_MODEM_OMA_GET_INTERFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE ((obj), MM_TYPE_IFACE_MODEM_OMA, MMIfaceModemOma)) #define MM_IFACE_MODEM_OMA_DBUS_SKELETON "iface-modem-oma-dbus-skeleton" typedef struct _MMIfaceModemOma MMIfaceModemOma; struct _MMIfaceModemOma { GTypeInterface g_iface; /* Check for Oma support (async) */ void (* check_support) (MMIfaceModemOma *self, GAsyncReadyCallback callback, gpointer user_data); gboolean (* check_support_finish) (MMIfaceModemOma *self, GAsyncResult *res, GError **error); /* Asynchronous setting up unsolicited events */ void (* setup_unsolicited_events) (MMIfaceModemOma *self, GAsyncReadyCallback callback, gpointer user_data); gboolean (* setup_unsolicited_events_finish) (MMIfaceModemOma *self, GAsyncResult *res, GError **error); /* Asynchronous cleaning up of unsolicited events */ void (* cleanup_unsolicited_events) (MMIfaceModemOma *self, GAsyncReadyCallback callback, gpointer user_data); gboolean (* cleanup_unsolicited_events_finish) (MMIfaceModemOma *self, GAsyncResult *res, GError **error); /* Asynchronous enabling unsolicited events */ void (* enable_unsolicited_events) (MMIfaceModemOma *self, GAsyncReadyCallback callback, gpointer user_data); gboolean (* enable_unsolicited_events_finish) (MMIfaceModemOma *self, GAsyncResult *res, GError **error); /* Asynchronous disabling unsolicited events */ void (* disable_unsolicited_events) (MMIfaceModemOma *self, GAsyncReadyCallback callback, gpointer user_data); gboolean (* disable_unsolicited_events_finish) (MMIfaceModemOma *self, GAsyncResult *res, GError **error); /* Get current features */ void (* load_features) (MMIfaceModemOma *self, GAsyncReadyCallback callback, gpointer user_data); MMOmaFeature (* load_features_finish) (MMIfaceModemOma *self, GAsyncResult *res, GError **error); /* Setup */ void (* setup) (MMIfaceModemOma *self, MMOmaFeature features, GAsyncReadyCallback callback, gpointer user_data); gboolean (* setup_finish) (MMIfaceModemOma *self, GAsyncResult *res, GError **error); /* Start client-initiated session */ void (* start_client_initiated_session) (MMIfaceModemOma *self, MMOmaSessionType session_type, GAsyncReadyCallback callback, gpointer user_data); gboolean (* start_client_initiated_session_finish) (MMIfaceModemOma *self, GAsyncResult *res, GError **error); /* Accept network-initiated session */ void (* accept_network_initiated_session) (MMIfaceModemOma *self, guint session_id, gboolean accept, GAsyncReadyCallback callback, gpointer user_data); gboolean (* accept_network_initiated_session_finish) (MMIfaceModemOma *self, GAsyncResult *res, GError **error); /* Cancel session */ void (* cancel_session) (MMIfaceModemOma *self, GAsyncReadyCallback callback, gpointer user_data); gboolean (* cancel_session_finish) (MMIfaceModemOma *self, GAsyncResult *res, GError **error); }; GType mm_iface_modem_oma_get_type (void); G_DEFINE_AUTOPTR_CLEANUP_FUNC (MMIfaceModemOma, g_object_unref) /* Initialize Oma interface (async) */ void mm_iface_modem_oma_initialize (MMIfaceModemOma *self, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); gboolean mm_iface_modem_oma_initialize_finish (MMIfaceModemOma *self, GAsyncResult *res, GError **error); /* Enable Oma interface (async) */ void mm_iface_modem_oma_enable (MMIfaceModemOma *self, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); gboolean mm_iface_modem_oma_enable_finish (MMIfaceModemOma *self, GAsyncResult *res, GError **error); /* Disable Oma interface (async) */ void mm_iface_modem_oma_disable (MMIfaceModemOma *self, GAsyncReadyCallback callback, gpointer user_data); gboolean mm_iface_modem_oma_disable_finish (MMIfaceModemOma *self, GAsyncResult *res, GError **error); /* Shutdown Oma interface */ void mm_iface_modem_oma_shutdown (MMIfaceModemOma *self); /* Bind properties for simple GetStatus() */ void mm_iface_modem_oma_bind_simple_status (MMIfaceModemOma *self, MMSimpleStatus *status); /* Report new pending network-initiated session */ void mm_iface_modem_oma_add_pending_network_initiated_session (MMIfaceModemOma *self, MMOmaSessionType session_type, guint session_id); /* Report new session state */ void mm_iface_modem_oma_update_session_state (MMIfaceModemOma *self, MMOmaSessionState session_state, MMOmaSessionStateFailedReason session_state_failed_reason); #endif /* MM_IFACE_MODEM_OMA_H */ ModemManager-1.23.4-dev/src/mm-iface-modem-sar.c000066400000000000000000000456471456466623000212400ustar00rootroot00000000000000/* -*- 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) 2021 Fibocom Wireless Inc. */ #include #define _LIBMM_INSIDE_MM #include #include "mm-iface-modem.h" #include "mm-iface-modem-sar.h" #include "mm-error-helpers.h" #include "mm-log-object.h" #define SUPPORT_CHECKED_TAG "sar-support-checked-tag" #define SUPPORTED_TAG "sar-supported-tag" static GQuark support_checked_quark; static GQuark supported_quark; /*****************************************************************************/ void mm_iface_modem_sar_bind_simple_status (MMIfaceModemSar *self, MMSimpleStatus *status) { } guint mm_iface_modem_sar_get_power_level (MMIfaceModemSar *self) { MmGdbusModemSar *skeleton = NULL; guint level; g_object_get (self, MM_IFACE_MODEM_SAR_DBUS_SKELETON, &skeleton, NULL); if (!skeleton) return 0; level = mm_gdbus_modem_sar_get_power_level (skeleton); g_object_unref (skeleton); return level; } /*****************************************************************************/ /* Handle Enable() */ typedef struct { MmGdbusModemSar *skeleton; GDBusMethodInvocation *invocation; MMIfaceModemSar *self; gboolean enable; } HandleEnableContext; static void handle_enable_context_free (HandleEnableContext *ctx) { g_object_unref (ctx->skeleton); g_object_unref (ctx->invocation); g_object_unref (ctx->self); g_slice_free (HandleEnableContext, ctx); } static void enable_ready (MMIfaceModemSar *self, GAsyncResult *res, HandleEnableContext *ctx) { GError *error = NULL; guint power_level = 0; if (!MM_IFACE_MODEM_SAR_GET_INTERFACE (ctx->self)->enable_finish (self, res, &power_level, &error)) { mm_obj_warn (self, "failed %s SAR: %s", ctx->enable ? "enabling" : "disabling", error->message); mm_dbus_method_invocation_take_error (ctx->invocation, error); } else { mm_obj_info (self, "%s SAR", ctx->enable ? "enabled" : "disabled"); mm_gdbus_modem_sar_set_state (ctx->skeleton, ctx->enable); if (ctx->enable) mm_gdbus_modem_sar_set_power_level (ctx->skeleton, power_level); mm_gdbus_modem_sar_complete_enable (ctx->skeleton, ctx->invocation); } handle_enable_context_free (ctx); } static void handle_enable_auth_ready (MMBaseModem *self, GAsyncResult *res, HandleEnableContext *ctx) { GError *error = NULL; if (!mm_base_modem_authorize_finish (self, res, &error)) { mm_dbus_method_invocation_take_error (ctx->invocation, error); handle_enable_context_free (ctx); return; } if (!MM_IFACE_MODEM_SAR_GET_INTERFACE (ctx->self)->enable || !MM_IFACE_MODEM_SAR_GET_INTERFACE (ctx->self)->enable_finish) { mm_dbus_method_invocation_return_error_literal (ctx->invocation, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "Cannot setup SAR: operation not supported"); handle_enable_context_free (ctx); return; } if (mm_gdbus_modem_sar_get_state (ctx->skeleton) == ctx->enable) { mm_gdbus_modem_sar_complete_enable (ctx->skeleton, ctx->invocation); handle_enable_context_free (ctx); return; } mm_obj_info (self, "processing user request to %s SAR...", ctx->enable ? "enable" : "disable"); MM_IFACE_MODEM_SAR_GET_INTERFACE (ctx->self)->enable (ctx->self, ctx->enable, (GAsyncReadyCallback)enable_ready, ctx); } static gboolean handle_enable (MmGdbusModemSar *skeleton, GDBusMethodInvocation *invocation, gboolean enable, MMIfaceModemSar *self) { HandleEnableContext *ctx; ctx = g_slice_new0 (HandleEnableContext); ctx->skeleton = g_object_ref (skeleton); ctx->invocation = g_object_ref (invocation); ctx->self = g_object_ref (self); ctx->enable = enable; mm_base_modem_authorize (MM_BASE_MODEM (self), invocation, MM_AUTHORIZATION_DEVICE_CONTROL, (GAsyncReadyCallback)handle_enable_auth_ready, ctx); return TRUE; } /*****************************************************************************/ /* Handle Set Power Level() */ typedef struct { MmGdbusModemSar *skeleton; GDBusMethodInvocation *invocation; MMIfaceModemSar *self; guint power_level; } HandleSetPowerLevelContext; static void handle_set_power_level_context_free (HandleSetPowerLevelContext *ctx) { g_object_unref (ctx->skeleton); g_object_unref (ctx->invocation); g_object_unref (ctx->self); g_slice_free (HandleSetPowerLevelContext, ctx); } static void set_power_level_ready (MMIfaceModemSar *self, GAsyncResult *res, HandleSetPowerLevelContext *ctx) { GError *error = NULL; if (!MM_IFACE_MODEM_SAR_GET_INTERFACE (ctx->self)->set_power_level_finish (self, res, &error)) { mm_obj_warn (self, "failed setting SAR power level to %u: %s", ctx->power_level, error->message); mm_dbus_method_invocation_take_error (ctx->invocation, error); } else { mm_obj_info (self, "SAR power level set to %u", ctx->power_level); mm_gdbus_modem_sar_set_power_level (ctx->skeleton, ctx->power_level); mm_gdbus_modem_sar_complete_set_power_level (ctx->skeleton, ctx->invocation); } handle_set_power_level_context_free (ctx); } static void handle_set_power_level_auth_ready (MMBaseModem *self, GAsyncResult *res, HandleSetPowerLevelContext *ctx) { GError *error = NULL; if (!mm_base_modem_authorize_finish (self, res, &error)) { mm_dbus_method_invocation_take_error (ctx->invocation, error); handle_set_power_level_context_free (ctx); return; } if (!MM_IFACE_MODEM_SAR_GET_INTERFACE (ctx->self)->set_power_level || !MM_IFACE_MODEM_SAR_GET_INTERFACE (ctx->self)->set_power_level_finish) { mm_dbus_method_invocation_return_error_literal (ctx->invocation, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "Cannot set SAR power level: operation not supported"); handle_set_power_level_context_free (ctx); return; } if (!mm_gdbus_modem_sar_get_state (ctx->skeleton)) { mm_dbus_method_invocation_return_error_literal (ctx->invocation, MM_CORE_ERROR, MM_CORE_ERROR_WRONG_STATE, "Cannot set SAR power level: SAR is disabled"); handle_set_power_level_context_free (ctx); return; } if (mm_gdbus_modem_sar_get_power_level (ctx->skeleton) == ctx->power_level) { mm_gdbus_modem_sar_complete_set_power_level (ctx->skeleton, ctx->invocation); handle_set_power_level_context_free (ctx); return; } mm_obj_info (self, "processing user request to set SAR power level to %u...", ctx->power_level); MM_IFACE_MODEM_SAR_GET_INTERFACE (ctx->self)->set_power_level ( ctx->self, ctx->power_level, (GAsyncReadyCallback)set_power_level_ready, ctx); } static gboolean handle_set_power_level (MmGdbusModemSar *skeleton, GDBusMethodInvocation *invocation, guint level, MMIfaceModemSar *self) { HandleSetPowerLevelContext *ctx; ctx = g_slice_new0 (HandleSetPowerLevelContext); ctx->skeleton = g_object_ref (skeleton); ctx->invocation = g_object_ref (invocation); ctx->self = g_object_ref (self); ctx->power_level = level; mm_base_modem_authorize (MM_BASE_MODEM (self), invocation, MM_AUTHORIZATION_DEVICE_CONTROL, (GAsyncReadyCallback)handle_set_power_level_auth_ready, ctx); return TRUE; } /*****************************************************************************/ typedef struct _InitializationContext InitializationContext; static void interface_initialization_step (GTask *task); typedef enum { INITIALIZATION_STEP_FIRST, INITIALIZATION_STEP_CHECK_SUPPORT, INITIALIZATION_STEP_FAIL_IF_UNSUPPORTED, INITIALIZATION_STEP_LOAD_STATE, INITIALIZATION_STEP_LOAD_POWER_LEVEL, INITIALIZATION_STEP_LAST } InitializationStep; struct _InitializationContext { MmGdbusModemSar *skeleton; InitializationStep step; }; static void initialization_context_free (InitializationContext *ctx) { g_object_unref (ctx->skeleton); g_free (ctx); } gboolean mm_iface_modem_sar_initialize_finish (MMIfaceModemSar *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void load_power_level_ready (MMIfaceModemSar *self, GAsyncResult *res, GTask *task) { InitializationContext *ctx; GError *error = NULL; guint level; if (!MM_IFACE_MODEM_SAR_GET_INTERFACE (self)->load_power_level_finish (self, res, &level, &error)) { mm_obj_warn (self, "loading SAR power level failed: %s", error->message); g_task_return_error (task, error); g_object_unref (task); return; } ctx = g_task_get_task_data (task); mm_gdbus_modem_sar_set_power_level (ctx->skeleton, level); /* Go on to next step */ ctx->step++; interface_initialization_step (task); } static void load_state_ready (MMIfaceModemSar *self, GAsyncResult *res, GTask *task) { InitializationContext *ctx; gboolean state; GError *error = NULL; if (!MM_IFACE_MODEM_SAR_GET_INTERFACE (self)->load_state_finish (self, res, &state, &error)) { mm_obj_warn (self, "loading SAR state failed: %s", error->message); g_task_return_error (task, error); g_object_unref (task); return; } ctx = g_task_get_task_data (task); mm_gdbus_modem_sar_set_state (ctx->skeleton, state); /* Go on to next step */ ctx->step++; interface_initialization_step (task); } static void check_support_ready (MMIfaceModemSar *self, GAsyncResult *res, GTask *task) { InitializationContext *ctx; g_autoptr(GError) error = NULL; if (!MM_IFACE_MODEM_SAR_GET_INTERFACE (self)->check_support_finish (self, res, &error)) { if (error) { /* This error shouldn't be treated as critical */ mm_obj_dbg (self, "SAR support check failed: %s", error->message); } } else { /* SAR is supported! */ g_object_set_qdata (G_OBJECT (self), supported_quark, GUINT_TO_POINTER (TRUE)); } /* Go on to next step */ ctx = g_task_get_task_data (task); ctx->step++; interface_initialization_step (task); } static void interface_initialization_step (GTask *task) { MMIfaceModemSar *self; InitializationContext *ctx; /* Don't run new steps if we're cancelled */ if (g_task_return_error_if_cancelled (task)) { g_object_unref (task); return; } self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); switch (ctx->step) { case INITIALIZATION_STEP_FIRST: /* Setup quarks if we didn't do it before */ if (G_UNLIKELY (!support_checked_quark)) support_checked_quark = (g_quark_from_static_string ( SUPPORT_CHECKED_TAG)); if (G_UNLIKELY (!supported_quark)) supported_quark = (g_quark_from_static_string ( SUPPORTED_TAG)); ctx->step++; /* fall through */ case INITIALIZATION_STEP_CHECK_SUPPORT: if (!GPOINTER_TO_UINT (g_object_get_qdata (G_OBJECT (self), support_checked_quark))) { /* Set the checked flag so that we don't run it again */ g_object_set_qdata (G_OBJECT (self), support_checked_quark, GUINT_TO_POINTER (TRUE)); /* Initially, assume we don't support it */ g_object_set_qdata (G_OBJECT (self), supported_quark, GUINT_TO_POINTER (FALSE)); if (MM_IFACE_MODEM_SAR_GET_INTERFACE (self)->check_support && MM_IFACE_MODEM_SAR_GET_INTERFACE (self)->check_support_finish) { MM_IFACE_MODEM_SAR_GET_INTERFACE (self)->check_support ( self, (GAsyncReadyCallback)check_support_ready, task); return; } /* If there is no implementation to check support, assume we DON'T * support it. */ } ctx->step++; /* fall through */ case INITIALIZATION_STEP_FAIL_IF_UNSUPPORTED: if (!GPOINTER_TO_UINT (g_object_get_qdata (G_OBJECT (self), supported_quark))) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "SAR not supported"); g_object_unref (task); return; } ctx->step++; /* fall through */ case INITIALIZATION_STEP_LOAD_STATE: if (MM_IFACE_MODEM_SAR_GET_INTERFACE (self)->load_state && MM_IFACE_MODEM_SAR_GET_INTERFACE (self)->load_state_finish) { MM_IFACE_MODEM_SAR_GET_INTERFACE (self)->load_state ( self, (GAsyncReadyCallback)load_state_ready, task); return; } ctx->step++; /* fall through */ case INITIALIZATION_STEP_LOAD_POWER_LEVEL: if (MM_IFACE_MODEM_SAR_GET_INTERFACE (self)->load_power_level && MM_IFACE_MODEM_SAR_GET_INTERFACE (self)->load_power_level_finish) { MM_IFACE_MODEM_SAR_GET_INTERFACE (self)->load_power_level ( self, (GAsyncReadyCallback)load_power_level_ready, task); return; } ctx->step++; /* fall through */ case INITIALIZATION_STEP_LAST: /* We are done without errors! */ /* Handle method invocations */ g_signal_connect (ctx->skeleton, "handle-enable", G_CALLBACK (handle_enable), self); g_signal_connect (ctx->skeleton, "handle-set-power-level", G_CALLBACK (handle_set_power_level), self); /* Finally, export the new interface */ mm_gdbus_object_skeleton_set_modem_sar (MM_GDBUS_OBJECT_SKELETON (self), MM_GDBUS_MODEM_SAR (ctx->skeleton)); g_task_return_boolean (task, TRUE); g_object_unref (task); return; default: break; } g_assert_not_reached (); } void mm_iface_modem_sar_initialize (MMIfaceModemSar *self, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { InitializationContext *ctx; MmGdbusModemSar *skeleton = NULL; GTask *task; /* Did we already create it? */ g_object_get (self, MM_IFACE_MODEM_SAR_DBUS_SKELETON, &skeleton, NULL); if (!skeleton) { skeleton = mm_gdbus_modem_sar_skeleton_new (); g_object_set (self, MM_IFACE_MODEM_SAR_DBUS_SKELETON, skeleton, NULL); } /* Perform async initialization here */ ctx = g_new0 (InitializationContext, 1); ctx->step = INITIALIZATION_STEP_FIRST; ctx->skeleton = skeleton; task = g_task_new (self, cancellable, callback, user_data); g_task_set_task_data (task, ctx, (GDestroyNotify)initialization_context_free); interface_initialization_step (task); } /*****************************************************************************/ void mm_iface_modem_sar_shutdown (MMIfaceModemSar *self) { /* Unexport DBus interface and remove the skeleton */ mm_gdbus_object_skeleton_set_modem_sar (MM_GDBUS_OBJECT_SKELETON (self), NULL); g_object_set (self, MM_IFACE_MODEM_SAR_DBUS_SKELETON, NULL, NULL); } /*****************************************************************************/ static void iface_modem_sar_init (gpointer g_iface) { static gboolean initialized = FALSE; if (initialized) return; /* Properties */ g_object_interface_install_property (g_iface, g_param_spec_object (MM_IFACE_MODEM_SAR_DBUS_SKELETON, "SAR DBus skeleton", "DBus skeleton for the SAR interface", MM_GDBUS_TYPE_MODEM_SAR_SKELETON, G_PARAM_READWRITE)); initialized = TRUE; } GType mm_iface_modem_sar_get_type (void) { static GType iface_modem_sar_type = 0; if (!G_UNLIKELY (iface_modem_sar_type)) { static const GTypeInfo info = { sizeof (MMIfaceModemSar), /* class_size */ iface_modem_sar_init, /* base_init */ NULL, /* base_finalize */ }; iface_modem_sar_type = g_type_register_static (G_TYPE_INTERFACE, "MMIfaceModemSar", &info, 0); g_type_interface_add_prerequisite (iface_modem_sar_type, MM_TYPE_IFACE_MODEM); } return iface_modem_sar_type; } ModemManager-1.23.4-dev/src/mm-iface-modem-sar.h000066400000000000000000000112041456466623000212230ustar00rootroot00000000000000/* -*- 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) 2021 Fibocom Wireless Inc. */ #ifndef MM_IFACE_MODEM_SAR_H #define MM_IFACE_MODEM_SAR_H #include #include #define _LIBMM_INSIDE_MM #include #define MM_TYPE_IFACE_MODEM_SAR (mm_iface_modem_sar_get_type ()) #define MM_IFACE_MODEM_SAR(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_IFACE_MODEM_SAR, MMIfaceModemSar)) #define MM_IS_IFACE_MODEM_SAR(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_IFACE_MODEM_SAR)) #define MM_IFACE_MODEM_SAR_GET_INTERFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE ((obj), MM_TYPE_IFACE_MODEM_SAR, MMIfaceModemSar)) #define MM_IFACE_MODEM_SAR_DBUS_SKELETON "iface-modem-sar-dbus-skeleton" typedef struct _MMIfaceModemSar MMIfaceModemSar; struct _MMIfaceModemSar { GTypeInterface g_iface; /* Check for SAR support (async) */ void (* check_support) (MMIfaceModemSar *self, GAsyncReadyCallback callback, gpointer user_data); gboolean (* check_support_finish) (MMIfaceModemSar *self, GAsyncResult *res, GError **error); /* Enable SAR (async) */ void (* enable) (MMIfaceModemSar *self, gboolean enable, GAsyncReadyCallback callback, gpointer user_data); gboolean (* enable_finish) (MMIfaceModemSar *self, GAsyncResult *res, guint *out_sar_power_level, GError **error); /* Get SAR state (async) */ void (* load_state) (MMIfaceModemSar *self, GAsyncReadyCallback callback, gpointer user_data); gboolean (* load_state_finish) (MMIfaceModemSar *self, GAsyncResult *res, gboolean *out_state, GError **error); /* Get power level (async) */ void (* load_power_level) (MMIfaceModemSar *self, GAsyncReadyCallback callback, gpointer user_data); gboolean (* load_power_level_finish) (MMIfaceModemSar *self, GAsyncResult *res, guint *out_power_level, GError **error); /* Set power level (async) */ void (* set_power_level) (MMIfaceModemSar *self, guint power_level, GAsyncReadyCallback callback, gpointer user_data); gboolean (* set_power_level_finish) (MMIfaceModemSar *self, GAsyncResult *res, GError **error); }; GType mm_iface_modem_sar_get_type (void); G_DEFINE_AUTOPTR_CLEANUP_FUNC (MMIfaceModemSar, g_object_unref) /* Initialize Sar interface (async) */ void mm_iface_modem_sar_initialize (MMIfaceModemSar *self, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); gboolean mm_iface_modem_sar_initialize_finish (MMIfaceModemSar *self, GAsyncResult *res, GError **error); /* Shutdown Sar interface */ void mm_iface_modem_sar_shutdown (MMIfaceModemSar *self); /* Bind properties for simple GetStatus() */ void mm_iface_modem_sar_bind_simple_status (MMIfaceModemSar*self, MMSimpleStatus *status); guint mm_iface_modem_sar_get_power_level (MMIfaceModemSar *self); #endif /* MM_IFACE_MODEM_SAR_H */ ModemManager-1.23.4-dev/src/mm-iface-modem-signal.c000066400000000000000000000716161456466623000217230ustar00rootroot00000000000000/* -*- 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) 2013-2021 Aleksander Morgado * Copyright (C) 2021 Intel Corporation */ #include #define _LIBMM_INSIDE_MM #include #include "mm-iface-modem.h" #include "mm-iface-modem-signal.h" #include "mm-error-helpers.h" #include "mm-log-object.h" #define SUPPORT_CHECKED_TAG "signal-support-checked-tag" #define SUPPORTED_TAG "signal-supported-tag" static GQuark support_checked_quark; static GQuark supported_quark; /*****************************************************************************/ /* Private data context */ #define PRIVATE_TAG "signal-private-tag" static GQuark private_quark; typedef struct { /* interface enabled */ gboolean enabled; /* polling-based reporting */ guint rate; guint timeout_source; /* threshold-based reporting */ guint rssi_threshold; gboolean error_rate_threshold; /* info logging control */ GTimer *info_log_timer; } Private; static void private_free (Private *priv) { if (priv->info_log_timer) g_timer_destroy (priv->info_log_timer); if (priv->timeout_source) g_source_remove (priv->timeout_source); g_slice_free (Private, priv); } static Private * get_private (MMIfaceModemSignal *self) { Private *priv; if (G_UNLIKELY (!private_quark)) private_quark = g_quark_from_static_string (PRIVATE_TAG); priv = g_object_get_qdata (G_OBJECT (self), private_quark); if (!priv) { priv = g_slice_new0 (Private); g_object_set_qdata_full (G_OBJECT (self), private_quark, priv, (GDestroyNotify)private_free); } return priv; } /*****************************************************************************/ void mm_iface_modem_signal_bind_simple_status (MMIfaceModemSignal *self, MMSimpleStatus *status) { } /*****************************************************************************/ /* Even if the signal refresh rate is higher than 300s, don't pollute the INFO * level log with so many updates, force a reduction of the rate to once every * 300s */ #define SIGNAL_QUALITY_PRINT_RATE_SECS 300 static void info_log_signal_quality (MMIfaceModemSignal *self, MMSignal *info, const gchar *rat) { Private *priv = NULL; g_autofree gchar *printable = NULL; priv = get_private (self); if (G_UNLIKELY (!priv->info_log_timer)) priv->info_log_timer = g_timer_new (); if (g_timer_elapsed (priv->info_log_timer, NULL) < SIGNAL_QUALITY_PRINT_RATE_SECS) return; g_timer_reset (priv->info_log_timer); printable = mm_signal_get_string (info); mm_obj_info (self, "%s: %s", rat, printable); }; /*****************************************************************************/ static void internal_signal_update (MMIfaceModemSignal *self, MMSignal *cdma, MMSignal *evdo, MMSignal *gsm, MMSignal *umts, MMSignal *lte, MMSignal *nr5g) { g_autoptr(GVariant) dict_cdma = NULL; g_autoptr(GVariant) dict_evdo = NULL; g_autoptr(GVariant) dict_gsm = NULL; g_autoptr(GVariant) dict_umts = NULL; g_autoptr(GVariant) dict_lte = NULL; g_autoptr(GVariant) dict_nr5g = NULL; g_autoptr(MmGdbusModemSignalSkeleton) skeleton = NULL; g_object_get (self, MM_IFACE_MODEM_SIGNAL_DBUS_SKELETON, &skeleton, NULL); if (!skeleton) { mm_obj_warn (self, "cannot update extended signal information: couldn't get interface skeleton"); return; } if (cdma) { mm_obj_dbg (self, "cdma extended signal information updated"); dict_cdma = mm_signal_get_dictionary (cdma); } mm_gdbus_modem_signal_set_cdma (MM_GDBUS_MODEM_SIGNAL (skeleton), dict_cdma); if (evdo) { mm_obj_dbg (self, "evdo extended signal information updated"); dict_evdo = mm_signal_get_dictionary (evdo); } mm_gdbus_modem_signal_set_evdo (MM_GDBUS_MODEM_SIGNAL (skeleton), dict_evdo); if (gsm) { mm_obj_dbg (self, "gsm extended signal information updated"); info_log_signal_quality (self, gsm, "gsm"); dict_gsm = mm_signal_get_dictionary (gsm); } mm_gdbus_modem_signal_set_gsm (MM_GDBUS_MODEM_SIGNAL (skeleton), dict_gsm); if (umts) { mm_obj_dbg (self, "umts extended signal information updated"); info_log_signal_quality (self, umts, "umts"); dict_umts = mm_signal_get_dictionary (umts); } mm_gdbus_modem_signal_set_umts (MM_GDBUS_MODEM_SIGNAL (skeleton), dict_umts); if (lte) { mm_obj_dbg (self, "lte extended signal information updated"); info_log_signal_quality (self, lte, "lte"); dict_lte = mm_signal_get_dictionary (lte); } mm_gdbus_modem_signal_set_lte (MM_GDBUS_MODEM_SIGNAL (skeleton), dict_lte); if (nr5g) { mm_obj_dbg (self, "5gnr extended signal information updated"); info_log_signal_quality (self, nr5g, "5gnr"); dict_nr5g = mm_signal_get_dictionary (nr5g); } mm_gdbus_modem_signal_set_nr5g (MM_GDBUS_MODEM_SIGNAL (skeleton), dict_nr5g); /* Flush right away */ g_dbus_interface_skeleton_flush (G_DBUS_INTERFACE_SKELETON (skeleton)); } void mm_iface_modem_signal_update (MMIfaceModemSignal *self, MMSignal *cdma, MMSignal *evdo, MMSignal *gsm, MMSignal *umts, MMSignal *lte, MMSignal *nr5g) { Private *priv; priv = get_private (self); if (!priv->enabled || (!priv->rate && !priv->rssi_threshold && !priv->error_rate_threshold)) { mm_obj_dbg (self, "skipping extended signal information update..."); return; } internal_signal_update (self, cdma, evdo, gsm, umts, lte, nr5g); } /*****************************************************************************/ static void check_interface_reset (MMIfaceModemSignal *self) { Private *priv; priv = get_private (self); if (!priv->enabled || (!priv->rate && !priv->rssi_threshold && !priv->error_rate_threshold)) { mm_obj_dbg (self, "reseting extended signal information..."); internal_signal_update (self, NULL, NULL, NULL, NULL, NULL, NULL); } } /*****************************************************************************/ /* Polling setup management */ static void load_values_ready (MMIfaceModemSignal *self, GAsyncResult *res) { g_autoptr(GError) error = NULL; g_autoptr(MMSignal) cdma = NULL; g_autoptr(MMSignal) evdo = NULL; g_autoptr(MMSignal) gsm = NULL; g_autoptr(MMSignal) umts = NULL; g_autoptr(MMSignal) lte = NULL; g_autoptr(MMSignal) nr5g = NULL; if (!MM_IFACE_MODEM_SIGNAL_GET_INTERFACE (self)->load_values_finish ( self, res, &cdma, &evdo, &gsm, &umts, <e, &nr5g, &error)) { mm_obj_warn (self, "couldn't reload extended signal information: %s", error->message); return; } mm_iface_modem_signal_update (self, cdma, evdo, gsm, umts, lte, nr5g); } static gboolean query_signal_values (MMIfaceModemSignal *self) { MM_IFACE_MODEM_SIGNAL_GET_INTERFACE (self)->load_values ( self, NULL, (GAsyncReadyCallback)load_values_ready, NULL); return G_SOURCE_CONTINUE; } static void polling_restart (MMIfaceModemSignal *self) { Private *priv; gboolean polling_setup; priv = get_private (self); polling_setup = (priv->enabled && priv->rate); if (polling_setup) mm_obj_info (self, "setting up extended signal information polling: rate %u seconds", priv->rate); else mm_obj_dbg (self, "cleaning up extended signal information polling"); /* Stop polling */ if (!polling_setup) { if (priv->timeout_source) { g_source_remove (priv->timeout_source); priv->timeout_source = 0; } return; } /* Start/restart polling */ if (priv->timeout_source) g_source_remove (priv->timeout_source); priv->timeout_source = g_timeout_add_seconds (priv->rate, (GSourceFunc) query_signal_values, self); /* Also launch right away */ query_signal_values (self); } /*****************************************************************************/ /* Thresholds setup management */ static gboolean thresholds_restart_finish (MMIfaceModemSignal *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void setup_thresholds_ready (MMIfaceModemSignal *self, GAsyncResult *res, GTask *task) { GError *error = NULL; if (!MM_IFACE_MODEM_SIGNAL_GET_INTERFACE (self)->setup_thresholds_finish (self, res, &error)) g_task_return_error (task, error); else { /* launch a query right away */ query_signal_values (self); g_task_return_boolean (task, TRUE); } g_object_unref (task); } static void thresholds_restart (MMIfaceModemSignal *self, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; Private *priv; gboolean threshold_setup; task = g_task_new (self, NULL, callback, user_data); if (!MM_IFACE_MODEM_SIGNAL_GET_INTERFACE (self)->setup_thresholds || !MM_IFACE_MODEM_SIGNAL_GET_INTERFACE (self)->setup_thresholds_finish) { g_task_return_boolean (task, TRUE); g_object_unref (task); return; } priv = get_private (self); threshold_setup = (priv->enabled && (priv->rssi_threshold || priv->error_rate_threshold)); mm_obj_dbg (self, "%s extended signal information thresholds: interface %s, rssi threshold %u dBm, error rate threshold %s", threshold_setup ? "setting up" : "cleaning up", priv->enabled ? "enabled" : "disabled", priv->rssi_threshold, priv->error_rate_threshold ? "enabled" : "disabled"); MM_IFACE_MODEM_SIGNAL_GET_INTERFACE (self)->setup_thresholds ( self, priv->rssi_threshold, priv->error_rate_threshold, (GAsyncReadyCallback)setup_thresholds_ready, task); } /*****************************************************************************/ typedef struct { GDBusMethodInvocation *invocation; MmGdbusModemSignal *skeleton; guint rate; } HandleSetupContext; static void handle_setup_context_free (HandleSetupContext *ctx) { g_object_unref (ctx->invocation); g_object_unref (ctx->skeleton); g_slice_free (HandleSetupContext, ctx); } static void handle_setup_auth_ready (MMBaseModem *_self, GAsyncResult *res, HandleSetupContext *ctx) { MMIfaceModemSignal *self = MM_IFACE_MODEM_SIGNAL (_self); GError *error = NULL; Private *priv; if (!mm_base_modem_authorize_finish (_self, res, &error)) { mm_dbus_method_invocation_take_error (ctx->invocation, error); handle_setup_context_free (ctx); return; } if (mm_iface_modem_abort_invocation_if_state_not_reached (MM_IFACE_MODEM (self), ctx->invocation, MM_MODEM_STATE_DISABLED)) { handle_setup_context_free (ctx); return; } priv = get_private (self); priv->rate = ctx->rate; polling_restart (self); check_interface_reset (self); mm_gdbus_modem_signal_set_rate (ctx->skeleton, ctx->rate); mm_gdbus_modem_signal_complete_setup (ctx->skeleton, ctx->invocation); handle_setup_context_free (ctx); } static gboolean handle_setup (MmGdbusModemSignal *skeleton, GDBusMethodInvocation *invocation, guint rate, MMIfaceModemSignal *self) { HandleSetupContext *ctx; ctx = g_slice_new0 (HandleSetupContext); ctx->invocation = g_object_ref (invocation); ctx->skeleton = g_object_ref (skeleton); ctx->rate = rate; mm_base_modem_authorize (MM_BASE_MODEM (self), invocation, MM_AUTHORIZATION_DEVICE_CONTROL, (GAsyncReadyCallback)handle_setup_auth_ready, ctx); return TRUE; } /*****************************************************************************/ typedef struct { GDBusMethodInvocation *invocation; MmGdbusModemSignal *skeleton; GVariant *settings; guint previous_rssi_threshold; gboolean previous_error_rate_threshold; } HandleSetupThresholdsContext; static void handle_setup_thresholds_context_free (HandleSetupThresholdsContext *ctx) { g_object_unref (ctx->invocation); g_object_unref (ctx->skeleton); if (ctx->settings) g_variant_unref (ctx->settings); g_slice_free (HandleSetupThresholdsContext, ctx); } static void setup_thresholds_restart_ready (MMIfaceModemSignal *self, GAsyncResult *res, HandleSetupThresholdsContext *ctx) { GError *error = NULL; Private *priv; priv = get_private (self); if (!thresholds_restart_finish (self, res, &error)) { priv->rssi_threshold = ctx->previous_rssi_threshold; priv->error_rate_threshold = ctx->previous_error_rate_threshold; mm_dbus_method_invocation_take_error (ctx->invocation, error); } else { check_interface_reset (self); mm_gdbus_modem_signal_set_rssi_threshold (ctx->skeleton, priv->rssi_threshold); mm_gdbus_modem_signal_set_error_rate_threshold (ctx->skeleton, priv->error_rate_threshold); mm_gdbus_modem_signal_complete_setup_thresholds (ctx->skeleton, ctx->invocation); } handle_setup_thresholds_context_free (ctx); } static void handle_setup_thresholds_auth_ready (MMBaseModem *_self, GAsyncResult *res, HandleSetupThresholdsContext *ctx) { g_autoptr(MMSignalThresholdProperties) properties = NULL; MMIfaceModemSignal *self = MM_IFACE_MODEM_SIGNAL (_self); GError *error = NULL; Private *priv; guint new_rssi_threshold; gboolean new_error_rate_threshold; priv = get_private (self); if (!mm_base_modem_authorize_finish (_self, res, &error)) { mm_dbus_method_invocation_take_error (ctx->invocation, error); handle_setup_thresholds_context_free (ctx); return; } if (!MM_IFACE_MODEM_SIGNAL_GET_INTERFACE (self)->setup_thresholds || !MM_IFACE_MODEM_SIGNAL_GET_INTERFACE (self)->setup_thresholds_finish) { mm_dbus_method_invocation_return_error_literal (ctx->invocation, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "Cannot setup thresholds: operation not supported"); handle_setup_thresholds_context_free (ctx); return; } if (mm_iface_modem_abort_invocation_if_state_not_reached (MM_IFACE_MODEM (self), ctx->invocation, MM_MODEM_STATE_DISABLED)) { handle_setup_thresholds_context_free (ctx); return; } properties = mm_signal_threshold_properties_new_from_dictionary (ctx->settings, &error); if (!properties) { mm_dbus_method_invocation_take_error (ctx->invocation, error); handle_setup_thresholds_context_free (ctx); return; } new_rssi_threshold = mm_signal_threshold_properties_get_rssi (properties); new_error_rate_threshold = mm_signal_threshold_properties_get_error_rate (properties); if ((new_rssi_threshold == priv->rssi_threshold) && (new_error_rate_threshold == priv->error_rate_threshold)) { mm_gdbus_modem_signal_complete_setup_thresholds (ctx->skeleton, ctx->invocation); handle_setup_thresholds_context_free (ctx); return; } ctx->previous_rssi_threshold = priv->rssi_threshold; ctx->previous_error_rate_threshold = priv->error_rate_threshold; priv->rssi_threshold = new_rssi_threshold; priv->error_rate_threshold = new_error_rate_threshold; thresholds_restart (self, (GAsyncReadyCallback)setup_thresholds_restart_ready, ctx); } static gboolean handle_setup_thresholds (MmGdbusModemSignal *skeleton, GDBusMethodInvocation *invocation, GVariant *settings, MMIfaceModemSignal *self) { HandleSetupThresholdsContext *ctx; ctx = g_slice_new0 (HandleSetupThresholdsContext); ctx->invocation = g_object_ref (invocation); ctx->skeleton = g_object_ref (skeleton); ctx->settings = g_variant_ref (settings); mm_base_modem_authorize (MM_BASE_MODEM (self), invocation, MM_AUTHORIZATION_DEVICE_CONTROL, (GAsyncReadyCallback)handle_setup_thresholds_auth_ready, ctx); return TRUE; } /*****************************************************************************/ /* Common enable/disable */ static gboolean common_enable_disable_finish (MMIfaceModemSignal *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void enable_disable_thresholds_restart_ready (MMIfaceModemSignal *self, GAsyncResult *res, GTask *task) { GError *error = NULL; if (!thresholds_restart_finish (self, res, &error)) g_task_return_error (task, error); else g_task_return_boolean (task, TRUE); g_object_unref (task); } static void common_enable_disable (MMIfaceModemSignal *self, gboolean enabled, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; Private *priv; task = g_task_new (self, cancellable, callback, user_data); priv = get_private (MM_IFACE_MODEM_SIGNAL (self)); priv->enabled = enabled; check_interface_reset (self); polling_restart (self); thresholds_restart (self, (GAsyncReadyCallback)enable_disable_thresholds_restart_ready, task); } /*****************************************************************************/ gboolean mm_iface_modem_signal_disable_finish (MMIfaceModemSignal *self, GAsyncResult *res, GError **error) { return common_enable_disable_finish (self, res, error); } void mm_iface_modem_signal_disable (MMIfaceModemSignal *self, GAsyncReadyCallback callback, gpointer user_data) { common_enable_disable (self, FALSE, NULL, callback, user_data); } /*****************************************************************************/ gboolean mm_iface_modem_signal_enable_finish (MMIfaceModemSignal *self, GAsyncResult *res, GError **error) { return common_enable_disable_finish (self, res, error); } void mm_iface_modem_signal_enable (MMIfaceModemSignal *self, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { common_enable_disable (self, TRUE, cancellable, callback, user_data); } /*****************************************************************************/ typedef struct _InitializationContext InitializationContext; static void interface_initialization_step (GTask *task); typedef enum { INITIALIZATION_STEP_FIRST, INITIALIZATION_STEP_CHECK_SUPPORT, INITIALIZATION_STEP_FAIL_IF_UNSUPPORTED, INITIALIZATION_STEP_LAST } InitializationStep; struct _InitializationContext { MmGdbusModemSignal *skeleton; InitializationStep step; }; static void initialization_context_free (InitializationContext *ctx) { g_object_unref (ctx->skeleton); g_slice_free (InitializationContext, ctx); } gboolean mm_iface_modem_signal_initialize_finish (MMIfaceModemSignal *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void check_support_ready (MMIfaceModemSignal *self, GAsyncResult *res, GTask *task) { InitializationContext *ctx; GError *error = NULL; if (!MM_IFACE_MODEM_SIGNAL_GET_INTERFACE (self)->check_support_finish (self, res, &error)) { if (error) { /* This error shouldn't be treated as critical */ mm_obj_dbg (self, "extended signal support check failed: %s", error->message); g_error_free (error); } } else { /* Signal is supported! */ g_object_set_qdata (G_OBJECT (self), supported_quark, GUINT_TO_POINTER (TRUE)); } /* Go on to next step */ ctx = g_task_get_task_data (task); ctx->step++; interface_initialization_step (task); } static void interface_initialization_step (GTask *task) { MMIfaceModemSignal *self; InitializationContext *ctx; /* Don't run new steps if we're cancelled */ if (g_task_return_error_if_cancelled (task)) { g_object_unref (task); return; } self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); switch (ctx->step) { case INITIALIZATION_STEP_FIRST: /* Setup quarks if we didn't do it before */ if (G_UNLIKELY (!support_checked_quark)) support_checked_quark = (g_quark_from_static_string ( SUPPORT_CHECKED_TAG)); if (G_UNLIKELY (!supported_quark)) supported_quark = (g_quark_from_static_string ( SUPPORTED_TAG)); ctx->step++; /* fall through */ case INITIALIZATION_STEP_CHECK_SUPPORT: if (!GPOINTER_TO_UINT (g_object_get_qdata (G_OBJECT (self), support_checked_quark))) { /* Set the checked flag so that we don't run it again */ g_object_set_qdata (G_OBJECT (self), support_checked_quark, GUINT_TO_POINTER (TRUE)); /* Initially, assume we don't support it */ g_object_set_qdata (G_OBJECT (self), supported_quark, GUINT_TO_POINTER (FALSE)); if (MM_IFACE_MODEM_SIGNAL_GET_INTERFACE (self)->check_support && MM_IFACE_MODEM_SIGNAL_GET_INTERFACE (self)->check_support_finish) { MM_IFACE_MODEM_SIGNAL_GET_INTERFACE (self)->check_support ( self, (GAsyncReadyCallback)check_support_ready, task); return; } /* If there is no implementation to check support, assume we DON'T * support it. */ } ctx->step++; /* fall through */ case INITIALIZATION_STEP_FAIL_IF_UNSUPPORTED: if (!GPOINTER_TO_UINT (g_object_get_qdata (G_OBJECT (self), supported_quark))) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "Extended Signal information not supported"); g_object_unref (task); return; } ctx->step++; /* fall through */ case INITIALIZATION_STEP_LAST: /* We are done without errors! */ /* Handle method invocations */ g_signal_connect (ctx->skeleton, "handle-setup", G_CALLBACK (handle_setup), self); g_signal_connect (ctx->skeleton, "handle-setup-thresholds", G_CALLBACK (handle_setup_thresholds), self); /* Finally, export the new interface */ mm_gdbus_object_skeleton_set_modem_signal (MM_GDBUS_OBJECT_SKELETON (self), MM_GDBUS_MODEM_SIGNAL (ctx->skeleton)); g_task_return_boolean (task, TRUE); g_object_unref (task); return; default: break; } g_assert_not_reached (); } void mm_iface_modem_signal_initialize (MMIfaceModemSignal *self, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { InitializationContext *ctx; MmGdbusModemSignal *skeleton = NULL; GTask *task; /* Did we already create it? */ g_object_get (self, MM_IFACE_MODEM_SIGNAL_DBUS_SKELETON, &skeleton, NULL); if (!skeleton) { skeleton = mm_gdbus_modem_signal_skeleton_new (); g_object_set (self, MM_IFACE_MODEM_SIGNAL_DBUS_SKELETON, skeleton, NULL); } /* Perform async initialization here */ ctx = g_slice_new0 (InitializationContext); ctx->step = INITIALIZATION_STEP_FIRST; ctx->skeleton = skeleton; task = g_task_new (self, cancellable, callback, user_data); g_task_set_task_data (task, ctx, (GDestroyNotify)initialization_context_free); interface_initialization_step (task); } void mm_iface_modem_signal_shutdown (MMIfaceModemSignal *self) { /* Unexport DBus interface and remove the skeleton */ mm_gdbus_object_skeleton_set_modem_signal (MM_GDBUS_OBJECT_SKELETON (self), NULL); g_object_set (self, MM_IFACE_MODEM_SIGNAL_DBUS_SKELETON, NULL, NULL); } /*****************************************************************************/ static void iface_modem_signal_init (gpointer g_iface) { static gboolean initialized = FALSE; if (initialized) return; /* Properties */ g_object_interface_install_property (g_iface, g_param_spec_object (MM_IFACE_MODEM_SIGNAL_DBUS_SKELETON, "Signal DBus skeleton", "DBus skeleton for the Signal interface", MM_GDBUS_TYPE_MODEM_SIGNAL_SKELETON, G_PARAM_READWRITE)); initialized = TRUE; } GType mm_iface_modem_signal_get_type (void) { static GType iface_modem_signal_type = 0; if (!G_UNLIKELY (iface_modem_signal_type)) { static const GTypeInfo info = { sizeof (MMIfaceModemSignal), /* class_size */ iface_modem_signal_init, /* base_init */ NULL, /* base_finalize */ }; iface_modem_signal_type = g_type_register_static (G_TYPE_INTERFACE, "MMIfaceModemSignal", &info, 0); g_type_interface_add_prerequisite (iface_modem_signal_type, MM_TYPE_IFACE_MODEM); } return iface_modem_signal_type; } ModemManager-1.23.4-dev/src/mm-iface-modem-signal.h000066400000000000000000000132241456466623000217170ustar00rootroot00000000000000/* -*- 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) 2013-2021 Aleksander Morgado * Copyright (C) 2021 Intel Corporation */ #ifndef MM_IFACE_MODEM_SIGNAL_H #define MM_IFACE_MODEM_SIGNAL_H #include #include #define _LIBMM_INSIDE_MM #include #define MM_TYPE_IFACE_MODEM_SIGNAL (mm_iface_modem_signal_get_type ()) #define MM_IFACE_MODEM_SIGNAL(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_IFACE_MODEM_SIGNAL, MMIfaceModemSignal)) #define MM_IS_IFACE_MODEM_SIGNAL(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_IFACE_MODEM_SIGNAL)) #define MM_IFACE_MODEM_SIGNAL_GET_INTERFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE ((obj), MM_TYPE_IFACE_MODEM_SIGNAL, MMIfaceModemSignal)) #define MM_IFACE_MODEM_SIGNAL_DBUS_SKELETON "iface-modem-signal-dbus-skeleton" typedef struct _MMIfaceModemSignal MMIfaceModemSignal; struct _MMIfaceModemSignal { GTypeInterface g_iface; /* Check for Messaging support (async) */ void (* check_support) (MMIfaceModemSignal *self, GAsyncReadyCallback callback, gpointer user_data); gboolean (*check_support_finish) (MMIfaceModemSignal *self, GAsyncResult *res, GError **error); /* Load all values */ void (* load_values) (MMIfaceModemSignal *self, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); gboolean (* load_values_finish) (MMIfaceModemSignal *self, GAsyncResult *res, MMSignal **cdma, MMSignal **evdo, MMSignal **gsm, MMSignal **umts, MMSignal **lte, MMSignal **nr5g, GError **error); /* Setup thresholds */ void (* setup_thresholds) (MMIfaceModemSignal *self, guint32 rssi_threshold, gboolean error_rate_threshold, GAsyncReadyCallback callback, gpointer user_data); gboolean (* setup_thresholds_finish) (MMIfaceModemSignal *self, GAsyncResult *res, GError **error); }; GType mm_iface_modem_signal_get_type (void); G_DEFINE_AUTOPTR_CLEANUP_FUNC (MMIfaceModemSignal, g_object_unref) /* Initialize Signal interface (async) */ void mm_iface_modem_signal_initialize (MMIfaceModemSignal *self, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); gboolean mm_iface_modem_signal_initialize_finish (MMIfaceModemSignal *self, GAsyncResult *res, GError **error); /* Enable Signal interface (async) */ void mm_iface_modem_signal_enable (MMIfaceModemSignal *self, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); gboolean mm_iface_modem_signal_enable_finish (MMIfaceModemSignal *self, GAsyncResult *res, GError **error); /* Disable Signal interface (async) */ void mm_iface_modem_signal_disable (MMIfaceModemSignal *self, GAsyncReadyCallback callback, gpointer user_data); gboolean mm_iface_modem_signal_disable_finish (MMIfaceModemSignal *self, GAsyncResult *res, GError **error); /* Shutdown Signal interface */ void mm_iface_modem_signal_shutdown (MMIfaceModemSignal *self); /* Bind properties for simple GetStatus() */ void mm_iface_modem_signal_bind_simple_status (MMIfaceModemSignal *self, MMSimpleStatus *status); /* Allow signal quality updates via indications */ void mm_iface_modem_signal_update (MMIfaceModemSignal *self, MMSignal *cdma, MMSignal *evdo, MMSignal *gsm, MMSignal *umts, MMSignal *lte, MMSignal *nr5g); #endif /* MM_IFACE_MODEM_SIGNAL_H */ ModemManager-1.23.4-dev/src/mm-iface-modem-simple.c000066400000000000000000001225441456466623000217340ustar00rootroot00000000000000/* -*- 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 - 2011 Red Hat, Inc. * Copyright (C) 2011 Google, Inc. * Copyright (c) 2022 Qualcomm Innovation Center, Inc. */ #include #define _LIBMM_INSIDE_MM #include #include "mm-bearer-list.h" #include "mm-base-sim.h" #include "mm-error-helpers.h" #include "mm-iface-modem.h" #include "mm-iface-modem-3gpp.h" #include "mm-iface-modem-cdma.h" #include "mm-iface-modem-simple.h" #include "mm-log-object.h" #include "mm-log-helpers.h" /*****************************************************************************/ /* Private data context */ #define PRIVATE_TAG "iface-modem-simple-private-tag" static GQuark private_quark; typedef struct { GCancellable *ongoing_connect; } Private; static void private_free (Private *priv) { g_clear_object (&priv->ongoing_connect); g_slice_free (Private, priv); } static Private * get_private (MMIfaceModemSimple *self) { Private *priv; if (G_UNLIKELY (!private_quark)) private_quark = g_quark_from_static_string (PRIVATE_TAG); priv = g_object_get_qdata (G_OBJECT (self), private_quark); if (!priv) { priv = g_slice_new0 (Private); g_object_set_qdata_full (G_OBJECT (self), private_quark, priv, (GDestroyNotify)private_free); } return priv; } /*****************************************************************************/ /* Abort ongoing requests, if any */ void mm_iface_modem_simple_abort_ongoing (MMIfaceModemSimple *self) { Private *priv; priv = get_private (self); if (priv->ongoing_connect) { g_cancellable_cancel (priv->ongoing_connect); g_clear_object (&priv->ongoing_connect); } } /*****************************************************************************/ /* Register in either a CDMA or a 3GPP network (or both) */ typedef struct { gchar *operator_id; guint remaining_tries_cdma; guint remaining_tries_3gpp; guint max_try_time; } RegisterInNetworkContext; static void register_in_network_context_free (RegisterInNetworkContext *ctx) { g_free (ctx->operator_id); g_slice_free (RegisterInNetworkContext, ctx); } static gboolean register_in_3gpp_or_cdma_network_finish (MMIfaceModemSimple *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void check_next_registration (GTask *task); static void register_in_cdma_network_ready (MMIfaceModemCdma *self, GAsyncResult *res, GTask *task) { RegisterInNetworkContext *ctx; ctx = g_task_get_task_data (task); ctx->remaining_tries_cdma--; if (!mm_iface_modem_cdma_register_in_network_finish (self, res, NULL)) { /* Retry check */ check_next_registration (task); return; } /* Registered we are! */ mm_obj_dbg (self, "registration in network: successful (cdma)"); g_task_return_boolean (task, TRUE); g_object_unref (task); } static void register_in_3gpp_network_ready (MMIfaceModem3gpp *self, GAsyncResult *res, GTask *task) { RegisterInNetworkContext *ctx; ctx = g_task_get_task_data (task); ctx->remaining_tries_3gpp--; if (!mm_iface_modem_3gpp_register_in_network_finish (self, res, NULL)) { /* Retry check */ check_next_registration (task); return; } /* Registered we are! */ mm_obj_dbg (self, "registration in network: successful (3gpp)"); g_task_return_boolean (task, TRUE); g_object_unref (task); } static void check_next_registration (GTask *task) { RegisterInNetworkContext *ctx; MMIfaceModemSimple *self; self = MM_IFACE_MODEM_SIMPLE (g_task_get_source_object (task)); ctx = g_task_get_task_data (task); if (g_task_return_error_if_cancelled (task)) { mm_obj_dbg (self, "registration in network: cancelled"); g_object_unref (task); return; } if (ctx->remaining_tries_cdma > ctx->remaining_tries_3gpp && ctx->remaining_tries_cdma > 0) { mm_iface_modem_cdma_register_in_network ( MM_IFACE_MODEM_CDMA (self), ctx->max_try_time, (GAsyncReadyCallback)register_in_cdma_network_ready, task); return; } if (ctx->remaining_tries_3gpp > 0) { mm_iface_modem_3gpp_register_in_network ( MM_IFACE_MODEM_3GPP (self), ctx->operator_id, FALSE, /* if already registered with same settings, do nothing */ ctx->max_try_time, (GAsyncReadyCallback)register_in_3gpp_network_ready, task); return; } /* No more tries of anything */ mm_obj_dbg (self, "registration in network: timed out"); g_task_return_error ( task, mm_mobile_equipment_error_for_code (MM_MOBILE_EQUIPMENT_ERROR_NETWORK_TIMEOUT, self)); g_object_unref (task); } static void register_in_3gpp_or_cdma_network (MMIfaceModemSimple *self, const gchar *operator_id, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { RegisterInNetworkContext *ctx; GTask *task; ctx = g_slice_new0 (RegisterInNetworkContext); ctx->operator_id = g_strdup (operator_id); /* 3GPP-only modems... */ if (mm_iface_modem_is_3gpp_only (MM_IFACE_MODEM (self))) { ctx->max_try_time = 60; ctx->remaining_tries_cdma = 0; ctx->remaining_tries_3gpp = 1; } /* CDMA-only modems... */ else if (mm_iface_modem_is_cdma_only (MM_IFACE_MODEM (self))) { ctx->max_try_time = 60; ctx->remaining_tries_cdma = 1; ctx->remaining_tries_3gpp = 0; } /* Mixed 3GPP+CDMA modems... */ else { ctx->max_try_time = 10; ctx->remaining_tries_cdma = 6; ctx->remaining_tries_3gpp = 6; } task = g_task_new (self, cancellable, callback, user_data); g_task_set_task_data (task, ctx, (GDestroyNotify)register_in_network_context_free); check_next_registration (task); } /*****************************************************************************/ /* Packet service attach in 3GPP network */ typedef enum { PACKET_SERVICE_ATTACH_IN_3GPP_NETWORK_STEP_FIRST, PACKET_SERVICE_ATTACH_IN_3GPP_NETWORK_STEP_WAIT_BEFORE, PACKET_SERVICE_ATTACH_IN_3GPP_NETWORK_STEP_SET, PACKET_SERVICE_ATTACH_IN_3GPP_NETWORK_STEP_WAIT_AFTER, PACKET_SERVICE_ATTACH_IN_3GPP_NETWORK_STEP_LAST, } PacketServiceAttachIn3gppNetworkStep; typedef struct { PacketServiceAttachIn3gppNetworkStep step; GError *error; } PacketServiceAttachIn3gppNetworkContext; static void packet_service_attach_in_3gpp_network_context_free (PacketServiceAttachIn3gppNetworkContext *ctx) { g_clear_error (&ctx->error); g_slice_free (PacketServiceAttachIn3gppNetworkContext, ctx); } static gboolean packet_service_attach_in_3gpp_network_finish (MMIfaceModemSimple *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void packet_service_attach_in_3gpp_network_step (GTask *task); static void set_packet_service_state_ready (MMIfaceModem3gpp *self, GAsyncResult *res, GTask *task) { PacketServiceAttachIn3gppNetworkContext *ctx; g_autoptr(GError) error = NULL; ctx = g_task_get_task_data (task); g_assert (ctx->error); if (!mm_iface_modem_3gpp_set_packet_service_state_finish (self, res, &error)) { /* On an unsupported error, return the original wait failure; otherwise * return the set error. */ if (!g_error_matches (error, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED)) { g_error_free (ctx->error); ctx->error = g_steal_pointer (&error); } /* Failures in the set are always fatal right away */ ctx->step = PACKET_SERVICE_ATTACH_IN_3GPP_NETWORK_STEP_LAST; } else { /* Retry if possible */ g_clear_error (&ctx->error); ctx->step++; } packet_service_attach_in_3gpp_network_step (task); } static void wait_for_packet_service_state_ready (MMIfaceModem3gpp *self, GAsyncResult *res, GTask *task) { PacketServiceAttachIn3gppNetworkContext *ctx; ctx = g_task_get_task_data (task); g_assert (!ctx->error); mm_iface_modem_3gpp_wait_for_packet_service_state_finish (self, res, &ctx->error); if (ctx->error) ctx->step++; else ctx->step = PACKET_SERVICE_ATTACH_IN_3GPP_NETWORK_STEP_LAST; packet_service_attach_in_3gpp_network_step (task); } static void packet_service_attach_in_3gpp_network_step (GTask *task) { PacketServiceAttachIn3gppNetworkContext *ctx; MMIfaceModemSimple *self; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); if (g_task_return_error_if_cancelled (task)) { mm_obj_dbg (self, "packet service attach in 3gpp network: cancelled"); g_object_unref (task); return; } switch (ctx->step) { case PACKET_SERVICE_ATTACH_IN_3GPP_NETWORK_STEP_FIRST: ctx->step++; /* fall through */ case PACKET_SERVICE_ATTACH_IN_3GPP_NETWORK_STEP_WAIT_BEFORE: g_assert (!ctx->error); mm_iface_modem_3gpp_wait_for_packet_service_state (MM_IFACE_MODEM_3GPP (self), MM_MODEM_3GPP_PACKET_SERVICE_STATE_ATTACHED, g_task_get_cancellable (task), (GAsyncReadyCallback)wait_for_packet_service_state_ready, task); return; case PACKET_SERVICE_ATTACH_IN_3GPP_NETWORK_STEP_SET: /* An explicit set will only be attempted if the packet service state wait failed */ g_assert (ctx->error); mm_iface_modem_3gpp_set_packet_service_state (MM_IFACE_MODEM_3GPP (self), MM_MODEM_3GPP_PACKET_SERVICE_STATE_ATTACHED, (GAsyncReadyCallback)set_packet_service_state_ready, task); return; case PACKET_SERVICE_ATTACH_IN_3GPP_NETWORK_STEP_WAIT_AFTER: g_assert (!ctx->error); mm_iface_modem_3gpp_wait_for_packet_service_state (MM_IFACE_MODEM_3GPP (self), MM_MODEM_3GPP_PACKET_SERVICE_STATE_ATTACHED, g_task_get_cancellable (task), (GAsyncReadyCallback)wait_for_packet_service_state_ready, task); return; case PACKET_SERVICE_ATTACH_IN_3GPP_NETWORK_STEP_LAST: if (ctx->error) { mm_obj_dbg (self, "packet service attach in 3gpp network: failed: %s", ctx->error->message); g_task_return_error (task, g_steal_pointer (&ctx->error)); } else { mm_obj_dbg (self, "packet service attach in 3gpp network: finished"); g_task_return_boolean (task, TRUE); } g_object_unref (task); return; default: g_assert_not_reached (); } } static void packet_service_attach_in_3gpp_network (MMIfaceModemSimple *self, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { PacketServiceAttachIn3gppNetworkContext *ctx; GTask *task; task = g_task_new (self, cancellable, callback, user_data); ctx = g_slice_new0 (PacketServiceAttachIn3gppNetworkContext); ctx->step = PACKET_SERVICE_ATTACH_IN_3GPP_NETWORK_STEP_FIRST; g_task_set_task_data (task, ctx, (GDestroyNotify)packet_service_attach_in_3gpp_network_context_free); packet_service_attach_in_3gpp_network_step (task); } /*****************************************************************************/ typedef enum { CONNECTION_STEP_FIRST, CONNECTION_STEP_UNLOCK_CHECK, CONNECTION_STEP_WAIT_FOR_INITIALIZED, CONNECTION_STEP_ENABLE, CONNECTION_STEP_WAIT_FOR_ENABLED, CONNECTION_STEP_WAIT_AFTER_ENABLED, CONNECTION_STEP_REGISTER, CONNECTION_STEP_PACKET_SERVICE_ATTACH, CONNECTION_STEP_BEARER, CONNECTION_STEP_CONNECT, CONNECTION_STEP_LAST } ConnectionStep; typedef struct { MmGdbusModemSimple *skeleton; GDBusMethodInvocation *invocation; MMIfaceModemSimple *self; ConnectionStep step; MMBearerList *bearer_list; /* Expected input properties */ GVariant *dictionary; MMSimpleConnectProperties *properties; /* Results to set */ MMBaseBearer *bearer; /* Cancellation */ GCancellable *cancellable; } ConnectionContext; static void cleanup_cancellation (ConnectionContext *ctx) { Private *priv; /* The ongoing connect cancellable and the one in the connection context * must be the same, as they're set together, so if the one in the * context doesn't exist, do nothing. */ if (!ctx->cancellable) return; /* If the ongoing connect cancellable is cancelled via the Simple.Disconnect * method, it won't exist in the private struct, so don't assume they * both exist. */ priv = get_private (ctx->self); g_clear_object (&priv->ongoing_connect); g_clear_object (&ctx->cancellable); } static void connection_context_free (ConnectionContext *ctx) { cleanup_cancellation (ctx); g_clear_object (&ctx->properties); g_clear_object (&ctx->bearer); g_clear_object (&ctx->bearer_list); g_variant_unref (ctx->dictionary); g_object_unref (ctx->skeleton); g_object_unref (ctx->invocation); g_object_unref (ctx->self); g_slice_free (ConnectionContext, ctx); } static void connection_step (ConnectionContext *ctx); static void connect_bearer_ready (MMBaseBearer *bearer, GAsyncResult *res, ConnectionContext *ctx) { GError *error = NULL; if (!mm_base_bearer_connect_finish (bearer, res, &error)) { mm_obj_warn (ctx->self, "couldn't connect bearer: %s", error->message); mm_dbus_method_invocation_take_error (ctx->invocation, error); connection_context_free (ctx); return; } ctx->step++; connection_step (ctx); } static void create_bearer_ready (MMIfaceModem *self, GAsyncResult *res, ConnectionContext *ctx) { GError *error = NULL; /* ownership for the caller */ ctx->bearer = mm_iface_modem_create_bearer_finish (self, res, &error); if (!ctx->bearer) { mm_obj_warn (ctx->self, "couldn't create bearer: %s", error->message); mm_dbus_method_invocation_take_error (ctx->invocation, error); connection_context_free (ctx); return; } ctx->step++; connection_step (ctx); } static void packet_service_attach_in_3gpp_network_ready (MMIfaceModemSimple *self, GAsyncResult *res, ConnectionContext *ctx) { GError *error = NULL; if (!packet_service_attach_in_3gpp_network_finish (self, res, &error)) { mm_obj_warn (ctx->self, "packet service attach in 3GPP network failed: %s", error->message); mm_dbus_method_invocation_take_error (ctx->invocation, error); connection_context_free (ctx); return; } ctx->step++; connection_step (ctx); } static void register_in_3gpp_or_cdma_network_ready (MMIfaceModemSimple *self, GAsyncResult *res, ConnectionContext *ctx) { GError *error = NULL; if (!register_in_3gpp_or_cdma_network_finish (self, res, &error)) { mm_obj_warn (ctx->self, "registration in network failed: %s", error->message); mm_dbus_method_invocation_take_error (ctx->invocation, error); connection_context_free (ctx); return; } ctx->step++; connection_step (ctx); } static gboolean wait_after_enabled_ready (ConnectionContext *ctx) { /* Just go on now */ ctx->step++; connection_step (ctx); return G_SOURCE_REMOVE; } static void wait_for_enabled_ready (MMIfaceModem *self, GAsyncResult *res, ConnectionContext *ctx) { GError *error = NULL; MMModemState state; state = mm_iface_modem_wait_for_final_state_finish (self, res, &error); if (error) { mm_obj_warn (ctx->self, "failed waiting for final state: %s", error->message); mm_dbus_method_invocation_take_error (ctx->invocation, error); connection_context_free (ctx); return; } if (state < MM_MODEM_STATE_ENABLED) { mm_obj_warn (ctx->self, "failed waiting for enabled state: %s", mm_modem_state_get_string (state)); mm_dbus_method_invocation_return_error (ctx->invocation, MM_CORE_ERROR, MM_CORE_ERROR_WRONG_STATE, "Couldn't wait for 'enabled': new state is '%s'", mm_modem_state_get_string (state)); connection_context_free (ctx); return; } ctx->step++; connection_step (ctx); } static void enable_ready (MMBaseModem *self, GAsyncResult *res, ConnectionContext *ctx) { GError *error = NULL; if (!mm_base_modem_enable_finish (MM_BASE_MODEM (self), res, &error)) { mm_obj_warn (ctx->self, "failed enabling modem: %s", error->message); mm_dbus_method_invocation_take_error (ctx->invocation, error); connection_context_free (ctx); return; } ctx->step++; connection_step (ctx); } static void wait_for_initialized_ready (MMIfaceModem *self, GAsyncResult *res, ConnectionContext *ctx) { GError *error = NULL; mm_iface_modem_wait_for_final_state_finish (self, res, &error); if (error) { mm_obj_warn (ctx->self, "failed waiting for final state: %s", error->message); mm_dbus_method_invocation_take_error (ctx->invocation, error); connection_context_free (ctx); return; } ctx->step++; connection_step (ctx); } static void send_pin_ready (MMBaseSim *sim, GAsyncResult *res, ConnectionContext *ctx) { GError *error = NULL; if (!mm_base_sim_send_pin_finish (sim, res, &error)) { mm_obj_warn (ctx->self, "failed sending SIM PIN: %s", error->message); mm_dbus_method_invocation_take_error (ctx->invocation, error); connection_context_free (ctx); return; } ctx->step++; connection_step (ctx); } static void update_lock_info_ready (MMIfaceModem *self, GAsyncResult *res, ConnectionContext *ctx) { GError *error = NULL; MMModemLock lock; g_autoptr(MMBaseSim) sim = NULL; lock = mm_iface_modem_update_lock_info_finish (self, res, &error); if (error) { mm_obj_warn (ctx->self, "failed updating lock info: %s", error->message); mm_dbus_method_invocation_take_error (ctx->invocation, error); connection_context_free (ctx); return; } /* If we are already unlocked, go on to next step. Note that we do also * allow SIM-PIN2/SIM-PUK2, as we don't need to unlock that in order to get * connected. */ if (lock == MM_MODEM_LOCK_NONE || lock == MM_MODEM_LOCK_SIM_PIN2 || lock == MM_MODEM_LOCK_SIM_PUK2) { ctx->step++; connection_step (ctx); return; } /* During simple connect we are only allowed to use SIM PIN */ if (lock != MM_MODEM_LOCK_SIM_PIN || !mm_simple_connect_properties_get_pin (ctx->properties)) { mm_obj_warn (ctx->self, "modem is locked with '%s'", mm_modem_lock_get_string (lock)); mm_dbus_method_invocation_return_error (ctx->invocation, MM_CORE_ERROR, MM_CORE_ERROR_UNAUTHORIZED, "Modem is locked with '%s' code; cannot unlock it", mm_modem_lock_get_string (lock)); connection_context_free (ctx); return; } /* Try to unlock the modem providing the PIN */ g_object_get (ctx->self, MM_IFACE_MODEM_SIM, &sim, NULL); if (!sim) { mm_obj_warn (ctx->self, "SIM is not accessible"); mm_dbus_method_invocation_return_error_literal (ctx->invocation, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Cannot unlock modem, couldn't get access to the SIM"); connection_context_free (ctx); return; } mm_base_sim_send_pin (sim, mm_simple_connect_properties_get_pin (ctx->properties), (GAsyncReadyCallback)send_pin_ready, ctx); } static gboolean completed_if_cancelled (ConnectionContext *ctx) { /* Do nothing if not cancelled */ if (!g_cancellable_is_cancelled (ctx->cancellable)) return FALSE; /* Otherwise cancellable is valid and it's cancelled */ mm_obj_warn (ctx->self, "connection attempt cancelled"); mm_dbus_method_invocation_return_error_literal (ctx->invocation, G_IO_ERROR, G_IO_ERROR_CANCELLED, "Connection attempt cancelled"); connection_context_free (ctx); return TRUE; } static gboolean setup_cancellation (ConnectionContext *ctx, GError **error) { Private *priv; /* Only one connection attempt by Simple.Connect() may be handled at a time */ priv = get_private (ctx->self); if (priv->ongoing_connect) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_IN_PROGRESS, "Connection request forbidden: operation already in progress"); return FALSE; } g_assert (!ctx->cancellable); ctx->cancellable = g_cancellable_new (); priv->ongoing_connect = g_object_ref (ctx->cancellable); return TRUE; } static void connection_step (ConnectionContext *ctx) { /* Early abort if operation is cancelled */ if (completed_if_cancelled (ctx)) return; switch (ctx->step) { case CONNECTION_STEP_FIRST: ctx->step++; /* fall through */ case CONNECTION_STEP_UNLOCK_CHECK: mm_obj_msg (ctx->self, "simple connect state (%d/%d): unlock check", ctx->step, CONNECTION_STEP_LAST); mm_iface_modem_update_lock_info (MM_IFACE_MODEM (ctx->self), MM_MODEM_LOCK_UNKNOWN, /* ask */ (GAsyncReadyCallback)update_lock_info_ready, ctx); return; case CONNECTION_STEP_WAIT_FOR_INITIALIZED: mm_obj_msg (ctx->self, "simple connect state (%d/%d): wait to get fully initialized", ctx->step, CONNECTION_STEP_LAST); mm_iface_modem_wait_for_final_state (MM_IFACE_MODEM (ctx->self), MM_MODEM_STATE_DISABLED, /* disabled == initialized */ (GAsyncReadyCallback)wait_for_initialized_ready, ctx); return; case CONNECTION_STEP_ENABLE: mm_obj_msg (ctx->self, "simple connect state (%d/%d): enable", ctx->step, CONNECTION_STEP_LAST); mm_base_modem_enable (MM_BASE_MODEM (ctx->self), (GAsyncReadyCallback)enable_ready, ctx); return; case CONNECTION_STEP_WAIT_FOR_ENABLED: mm_obj_msg (ctx->self, "simple connect state (%d/%d): wait to get fully enabled", ctx->step, CONNECTION_STEP_LAST); mm_iface_modem_wait_for_final_state (MM_IFACE_MODEM (ctx->self), MM_MODEM_STATE_UNKNOWN, /* just a final state */ (GAsyncReadyCallback)wait_for_enabled_ready, ctx); return; case CONNECTION_STEP_WAIT_AFTER_ENABLED: mm_obj_msg (ctx->self, "simple connect state (%d/%d): wait after enabled", ctx->step, CONNECTION_STEP_LAST); /* When we have just enabled, we want to give it some time before starting * the registration process, so that any pending registration update that may * have been scheduled during the enabling phase is applied. We don't want to * trigger a new automatic registration if e.g. we're already registered. */ g_timeout_add (100, (GSourceFunc)wait_after_enabled_ready, ctx); return; case CONNECTION_STEP_REGISTER: mm_obj_msg (ctx->self, "simple connect state (%d/%d): register", ctx->step, CONNECTION_STEP_LAST); if (mm_iface_modem_is_3gpp (MM_IFACE_MODEM (ctx->self)) || mm_iface_modem_is_cdma (MM_IFACE_MODEM (ctx->self))) { /* 3GPP or CDMA registration */ register_in_3gpp_or_cdma_network ( ctx->self, mm_simple_connect_properties_get_operator_id (ctx->properties), ctx->cancellable, (GAsyncReadyCallback)register_in_3gpp_or_cdma_network_ready, ctx); return; } /* If not 3GPP and not CDMA, this will possibly be a POTS modem, * which won't require any specific registration anywhere. */ ctx->step++; /* fall through */ case CONNECTION_STEP_PACKET_SERVICE_ATTACH: mm_obj_msg (ctx->self, "simple connect state (%d/%d): wait to get packet service state attached", ctx->step, CONNECTION_STEP_LAST); if (mm_iface_modem_is_3gpp (MM_IFACE_MODEM (ctx->self))) { packet_service_attach_in_3gpp_network ( ctx->self, ctx->cancellable, (GAsyncReadyCallback)packet_service_attach_in_3gpp_network_ready, ctx); return; } /* If not 3GPP, just go on */ ctx->step++; /* fall through */ case CONNECTION_STEP_BEARER: { g_autoptr(MMBearerProperties) bearer_properties = NULL; mm_obj_msg (ctx->self, "simple connect state (%d/%d): bearer", ctx->step, CONNECTION_STEP_LAST); bearer_properties = mm_simple_connect_properties_get_bearer_properties (ctx->properties); /* Check if the bearer we want to create is already in the list */ ctx->bearer = mm_bearer_list_find_by_properties (ctx->bearer_list, bearer_properties); if (!ctx->bearer) { mm_obj_dbg (ctx->self, "creating new bearer..."); mm_iface_modem_create_bearer (MM_IFACE_MODEM (ctx->self), bearer_properties, (GAsyncReadyCallback)create_bearer_ready, ctx); return; } mm_obj_dbg (ctx->self, "Using already existing bearer at '%s'...", mm_base_bearer_get_path (ctx->bearer)); ctx->step++; } /* fall through */ case CONNECTION_STEP_CONNECT: mm_obj_msg (ctx->self, "simple connect state (%d/%d): connect", ctx->step, CONNECTION_STEP_LAST); /* At this point, we can cleanup the cancellation point in the Simple interface, * because the bearer connection has its own cancellation setup. */ cleanup_cancellation (ctx); /* Wait... if we're already using an existing bearer, we need to check if it is * already connected; and if so, just don't do anything else */ if (mm_base_bearer_get_status (ctx->bearer) != MM_BEARER_STATUS_CONNECTED) { mm_base_bearer_connect (ctx->bearer, (GAsyncReadyCallback)connect_bearer_ready, ctx); return; } mm_obj_info (ctx->self, "bearer at '%s' is already connected...", mm_base_bearer_get_path (ctx->bearer)); ctx->step++; /* fall through */ case CONNECTION_STEP_LAST: mm_obj_msg (ctx->self, "simple connect state (%d/%d): all done", ctx->step, CONNECTION_STEP_LAST); /* All done, yey! */ mm_gdbus_modem_simple_complete_connect ( ctx->skeleton, ctx->invocation, mm_base_bearer_get_path (ctx->bearer)); connection_context_free (ctx); return; default: break; } g_assert_not_reached (); } static void connect_auth_ready (MMBaseModem *self, GAsyncResult *res, ConnectionContext *ctx) { GError *error = NULL; MMModemState current = MM_MODEM_STATE_UNKNOWN; if (!mm_base_modem_authorize_finish (self, res, &error)) { mm_dbus_method_invocation_take_error (ctx->invocation, error); connection_context_free (ctx); return; } ctx->properties = mm_simple_connect_properties_new_from_dictionary (ctx->dictionary, &error); if (!ctx->properties) { mm_dbus_method_invocation_take_error (ctx->invocation, error); connection_context_free (ctx); return; } mm_obj_info (self, "processing user request to connect modem..."); mm_log_simple_connect_properties (self, MM_LOG_LEVEL_INFO, " ", ctx->properties); if (!setup_cancellation (ctx, &error)) { mm_dbus_method_invocation_take_error (ctx->invocation, error); connection_context_free (ctx); return; } /* We may be able to skip some steps, so check that before doing anything */ g_object_get (self, MM_IFACE_MODEM_STATE, ¤t, MM_IFACE_MODEM_BEARER_LIST, &ctx->bearer_list, NULL); if (!ctx->bearer_list) { mm_dbus_method_invocation_return_error_literal (ctx->invocation, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't get the bearer list"); connection_context_free (ctx); return; } mm_obj_msg (self, "simple connect started..."); switch (current) { case MM_MODEM_STATE_FAILED: case MM_MODEM_STATE_UNKNOWN: case MM_MODEM_STATE_LOCKED: /* If we need unlocking, start from the very beginning */ ctx->step = CONNECTION_STEP_FIRST; break; case MM_MODEM_STATE_INITIALIZING: case MM_MODEM_STATE_DISABLING: /* If we are transitioning to the DISABLED (initialized) state, * wait to get there before going on */ ctx->step = CONNECTION_STEP_WAIT_FOR_INITIALIZED; break; case MM_MODEM_STATE_DISABLED: ctx->step = CONNECTION_STEP_ENABLE; break; case MM_MODEM_STATE_ENABLING: case MM_MODEM_STATE_DISCONNECTING: case MM_MODEM_STATE_SEARCHING: case MM_MODEM_STATE_CONNECTING: /* Wait to get to a final state before going on */ ctx->step = CONNECTION_STEP_WAIT_FOR_ENABLED; break; case MM_MODEM_STATE_ENABLED: case MM_MODEM_STATE_REGISTERED: case MM_MODEM_STATE_CONNECTED: /* If we are at least already enabled, start at the registration check * right away */ ctx->step = CONNECTION_STEP_REGISTER; break; default: g_assert_not_reached (); break; } connection_step (ctx); } static gboolean handle_connect (MmGdbusModemSimple *skeleton, GDBusMethodInvocation *invocation, GVariant *dictionary, MMIfaceModemSimple *self) { ConnectionContext *ctx; ctx = g_slice_new0 (ConnectionContext); ctx->skeleton = g_object_ref (skeleton); ctx->invocation = g_object_ref (invocation); ctx->self = g_object_ref (self); ctx->dictionary = g_variant_ref (dictionary); mm_base_modem_authorize (MM_BASE_MODEM (self), invocation, MM_AUTHORIZATION_DEVICE_CONTROL, (GAsyncReadyCallback)connect_auth_ready, ctx); return TRUE; } /*****************************************************************************/ typedef struct { MMIfaceModemSimple *self; MmGdbusModemSimple *skeleton; GDBusMethodInvocation *invocation; gchar *bearer_path; } DisconnectionContext; static void disconnection_context_free (DisconnectionContext *ctx) { g_object_unref (ctx->skeleton); g_object_unref (ctx->invocation); g_object_unref (ctx->self); g_free (ctx->bearer_path); g_slice_free (DisconnectionContext, ctx); } static void bearer_list_disconnect_bearers_ready (MMBearerList *bearer_list, GAsyncResult *res, DisconnectionContext *ctx) { GError *error = NULL; if (!mm_bearer_list_disconnect_bearers_finish (bearer_list, res, &error)) { mm_obj_warn (ctx->self, "failed to disconnect bearers: %s", error->message); mm_dbus_method_invocation_take_error (ctx->invocation, error); } else { mm_obj_info (ctx->self, "all requested bearers disconnected"); mm_gdbus_modem_simple_complete_disconnect (ctx->skeleton, ctx->invocation); } disconnection_context_free (ctx); } static void disconnect_auth_ready (MMBaseModem *self, GAsyncResult *res, DisconnectionContext *ctx) { g_autoptr(MMBearerList) list = NULL; GError *error = NULL; if (!mm_base_modem_authorize_finish (self, res, &error)) { mm_dbus_method_invocation_take_error (ctx->invocation, error); disconnection_context_free (ctx); return; } /* If not disconnecting a specific bearer, also cancel any ongoing * connection attempt. */ if (!ctx->bearer_path) mm_iface_modem_simple_abort_ongoing (MM_IFACE_MODEM_SIMPLE (self)); g_object_get (self, MM_IFACE_MODEM_BEARER_LIST, &list, NULL); if (!list) { mm_dbus_method_invocation_return_error_literal (ctx->invocation, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't get the bearer list"); disconnection_context_free (ctx); return; } if (ctx->bearer_path) mm_obj_info (self, "processing user request to disconnect modem: bearer '%s'", ctx->bearer_path); else mm_obj_info (self, "processing user request to disconnect modem: all bearers"); mm_bearer_list_disconnect_bearers (list, ctx->bearer_path, (GAsyncReadyCallback)bearer_list_disconnect_bearers_ready, ctx); } static gboolean handle_disconnect (MmGdbusModemSimple *skeleton, GDBusMethodInvocation *invocation, const gchar *bearer_path, MMIfaceModemSimple *self) { DisconnectionContext *ctx; ctx = g_slice_new0 (DisconnectionContext); ctx->skeleton = g_object_ref (skeleton); ctx->self = g_object_ref (self); ctx->invocation = g_object_ref (invocation); /* The Disconnect() method expects a valid object path given in bearer path, * it cannot be NULL, so we assume we get '/' when we're asked to disconnect * all connected bearers, as that is what mm_modem_simple_disconnect() does * when a NULL bearer path is given to that method. * * We will detect the '/' string and set the bearer path as NULL in the * context if so, and otherwise use the given input string as path */ if (g_strcmp0 (bearer_path, "/") != 0) ctx->bearer_path = g_strdup (bearer_path); mm_base_modem_authorize (MM_BASE_MODEM (self), invocation, MM_AUTHORIZATION_DEVICE_CONTROL, (GAsyncReadyCallback)disconnect_auth_ready, ctx); return TRUE; } /*****************************************************************************/ static gboolean handle_get_status (MmGdbusModemSimple *skeleton, GDBusMethodInvocation *invocation, MMIfaceModemSimple *self) { MMSimpleStatus *status = NULL; GVariant *dictionary; g_object_get (self, MM_IFACE_MODEM_SIMPLE_STATUS, &status, NULL); dictionary = mm_simple_status_get_dictionary (status); mm_gdbus_modem_simple_complete_get_status (skeleton, invocation, dictionary); g_variant_unref (dictionary); g_object_unref (status); return TRUE; } /*****************************************************************************/ void mm_iface_modem_simple_initialize (MMIfaceModemSimple *self) { MmGdbusModemSimple *skeleton = NULL; /* Did we already create it? */ g_object_get (self, MM_IFACE_MODEM_SIMPLE_DBUS_SKELETON, &skeleton, NULL); if (!skeleton) { skeleton = mm_gdbus_modem_simple_skeleton_new (); g_object_set (self, MM_IFACE_MODEM_SIMPLE_DBUS_SKELETON, skeleton, NULL); /* Handle method invocations */ g_signal_connect (skeleton, "handle-connect", G_CALLBACK (handle_connect), self); g_signal_connect (skeleton, "handle-disconnect", G_CALLBACK (handle_disconnect), self); g_signal_connect (skeleton, "handle-get-status", G_CALLBACK (handle_get_status), self); /* Finally, export the new interface */ mm_gdbus_object_skeleton_set_modem_simple (MM_GDBUS_OBJECT_SKELETON (self), MM_GDBUS_MODEM_SIMPLE (skeleton)); } g_object_unref (skeleton); } void mm_iface_modem_simple_shutdown (MMIfaceModemSimple *self) { /* Unexport DBus interface and remove the skeleton */ mm_gdbus_object_skeleton_set_modem_simple (MM_GDBUS_OBJECT_SKELETON (self), NULL); g_object_set (self, MM_IFACE_MODEM_SIMPLE_DBUS_SKELETON, NULL, NULL); } /*****************************************************************************/ static void iface_modem_simple_init (gpointer g_iface) { static gboolean initialized = FALSE; if (initialized) return; /* Properties */ g_object_interface_install_property (g_iface, g_param_spec_object (MM_IFACE_MODEM_SIMPLE_DBUS_SKELETON, "Simple DBus skeleton", "DBus skeleton for the Simple interface", MM_GDBUS_TYPE_MODEM_SIMPLE_SKELETON, G_PARAM_READWRITE)); g_object_interface_install_property (g_iface, g_param_spec_object (MM_IFACE_MODEM_SIMPLE_STATUS, "Simple status", "Compilation of status values", MM_TYPE_SIMPLE_STATUS, G_PARAM_READWRITE)); initialized = TRUE; } GType mm_iface_modem_simple_get_type (void) { static GType iface_modem_simple_type = 0; if (!G_UNLIKELY (iface_modem_simple_type)) { static const GTypeInfo info = { sizeof (MMIfaceModemSimple), /* class_size */ iface_modem_simple_init, /* base_init */ NULL, /* base_finalize */ }; iface_modem_simple_type = g_type_register_static (G_TYPE_INTERFACE, "MMIfaceModemSimple", &info, 0); g_type_interface_add_prerequisite (iface_modem_simple_type, MM_TYPE_IFACE_MODEM); } return iface_modem_simple_type; } ModemManager-1.23.4-dev/src/mm-iface-modem-simple.h000066400000000000000000000036541456466623000217410ustar00rootroot00000000000000/* -*- 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) 2011 Google, Inc. */ #ifndef MM_IFACE_MODEM_SIMPLE_H #define MM_IFACE_MODEM_SIMPLE_H #include #include #define MM_TYPE_IFACE_MODEM_SIMPLE (mm_iface_modem_simple_get_type ()) #define MM_IFACE_MODEM_SIMPLE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_IFACE_MODEM_SIMPLE, MMIfaceModemSimple)) #define MM_IS_IFACE_MODEM_SIMPLE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_IFACE_MODEM_SIMPLE)) #define MM_IFACE_MODEM_SIMPLE_GET_INTERFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE ((obj), MM_TYPE_IFACE_MODEM_SIMPLE, MMIfaceModemSimple)) #define MM_IFACE_MODEM_SIMPLE_DBUS_SKELETON "iface-modem-simple-dbus-skeleton" #define MM_IFACE_MODEM_SIMPLE_STATUS "iface-modem-simple-status" typedef struct _MMIfaceModemSimple MMIfaceModemSimple; struct _MMIfaceModemSimple { GTypeInterface g_iface; }; GType mm_iface_modem_simple_get_type (void); G_DEFINE_AUTOPTR_CLEANUP_FUNC (MMIfaceModemSimple, g_object_unref) /* Initialize Modem Simple interface */ void mm_iface_modem_simple_initialize (MMIfaceModemSimple *self); /* Abort ongoing operations in Modem Simple interface */ void mm_iface_modem_simple_abort_ongoing (MMIfaceModemSimple *self); /* Shutdown Modem Simple interface */ void mm_iface_modem_simple_shutdown (MMIfaceModemSimple *self); #endif /* MM_IFACE_MODEM_SIMPLE_H */ ModemManager-1.23.4-dev/src/mm-iface-modem-time.c000066400000000000000000001001461456466623000213730ustar00rootroot00000000000000/* -*- 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) 2012 Google, Inc. */ #include #define _LIBMM_INSIDE_MM #include #include "mm-iface-modem.h" #include "mm-iface-modem-time.h" #include "mm-error-helpers.h" #include "mm-log-object.h" #define SUPPORT_CHECKED_TAG "time-support-checked-tag" #define SUPPORTED_TAG "time-supported-tag" #define NETWORK_TIMEZONE_CONTEXT_TAG "time-network-timezone-context" static GQuark support_checked_quark; static GQuark supported_quark; static GQuark network_timezone_context_quark; /*****************************************************************************/ void mm_iface_modem_time_bind_simple_status (MMIfaceModemTime *self, MMSimpleStatus *status) { } /*****************************************************************************/ typedef struct { GDBusMethodInvocation *invocation; MmGdbusModemTime *skeleton; MMIfaceModemTime *self; } HandleGetNetworkTimeContext; static void handle_get_network_time_context_free (HandleGetNetworkTimeContext *ctx) { g_object_unref (ctx->invocation); g_object_unref (ctx->skeleton); g_object_unref (ctx->self); g_free (ctx); } static void load_network_time_ready (MMIfaceModemTime *self, GAsyncResult *res, HandleGetNetworkTimeContext *ctx) { gchar *time_str; GError *error = NULL; time_str = MM_IFACE_MODEM_TIME_GET_INTERFACE (self)->load_network_time_finish (self, res, &error); if (error) mm_dbus_method_invocation_take_error (ctx->invocation, error); else mm_gdbus_modem_time_complete_get_network_time (ctx->skeleton, ctx->invocation, time_str); g_free (time_str); handle_get_network_time_context_free (ctx); } static void handle_get_network_time_auth_ready (MMBaseModem *self, GAsyncResult *res, HandleGetNetworkTimeContext *ctx) { MMModemState state; GError *error = NULL; if (!mm_base_modem_authorize_finish (self, res, &error)) { mm_dbus_method_invocation_take_error (ctx->invocation, error); handle_get_network_time_context_free (ctx); return; } state = MM_MODEM_STATE_UNKNOWN; g_object_get (self, MM_IFACE_MODEM_STATE, &state, NULL); /* If we're not yet registered, we cannot get the network time */ if (state < MM_MODEM_STATE_REGISTERED) { mm_dbus_method_invocation_return_error_literal (ctx->invocation, MM_CORE_ERROR, MM_CORE_ERROR_WRONG_STATE, "Cannot load network time: not registered yet"); handle_get_network_time_context_free (ctx); return; } if (!MM_IFACE_MODEM_TIME_GET_INTERFACE (self)->load_network_time || !MM_IFACE_MODEM_TIME_GET_INTERFACE (self)->load_network_time_finish) { mm_dbus_method_invocation_return_error_literal (ctx->invocation, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "Cannot load network time: operation not supported"); handle_get_network_time_context_free (ctx); return; } MM_IFACE_MODEM_TIME_GET_INTERFACE (self)->load_network_time ( ctx->self, (GAsyncReadyCallback)load_network_time_ready, ctx); } static gboolean handle_get_network_time (MmGdbusModemTime *skeleton, GDBusMethodInvocation *invocation, MMIfaceModemTime *self) { HandleGetNetworkTimeContext *ctx; ctx = g_new (HandleGetNetworkTimeContext, 1); ctx->invocation = g_object_ref (invocation); ctx->skeleton = g_object_ref (skeleton); ctx->self = g_object_ref (self); mm_base_modem_authorize (MM_BASE_MODEM (self), invocation, MM_AUTHORIZATION_TIME, (GAsyncReadyCallback)handle_get_network_time_auth_ready, ctx); return TRUE; } /*****************************************************************************/ /* Network timezone loading */ /* * As soon as we're registered in a network, we try to check timezone * information up to NETWORK_TIMEZONE_POLL_RETRIES times. As soon as one of * the retries succeeds, we stop polling as we don't expect the timezone * information to change while registered in the same network. */ #define NETWORK_TIMEZONE_POLL_INTERVAL_SEC 5 #define NETWORK_TIMEZONE_POLL_RETRIES 6 typedef struct { gulong state_changed_id; MMModemState state; guint network_timezone_poll_id; guint network_timezone_poll_retries; } NetworkTimezoneContext; static void network_timezone_context_free (NetworkTimezoneContext *ctx) { /* Note: no need to remove signal connection here, we have already done it * in stop_network_timezone() when the logic is disabled (or will be done * automatically when the last modem object reference is dropped) */ if (ctx->network_timezone_poll_id) g_source_remove (ctx->network_timezone_poll_id); g_free (ctx); } static gboolean network_timezone_poll_cb (MMIfaceModemTime *self); static void update_network_timezone_dictionary (MMIfaceModemTime *self, MMNetworkTimezone *tz) { MmGdbusModemTime *skeleton = NULL; GVariant *dictionary; g_object_get (self, MM_IFACE_MODEM_TIME_DBUS_SKELETON, &skeleton, NULL); if (!skeleton) return; dictionary = mm_network_timezone_get_dictionary (tz); mm_gdbus_modem_time_set_network_timezone (skeleton, dictionary); if (dictionary) g_variant_unref (dictionary); g_object_unref (skeleton); } static void load_network_timezone_ready (MMIfaceModemTime *self, GAsyncResult *res) { g_autoptr(GError) error = NULL; MMNetworkTimezone *tz; /* Finish the async operation */ tz = MM_IFACE_MODEM_TIME_GET_INTERFACE (self)->load_network_timezone_finish (self, res, &error); if (!tz) { NetworkTimezoneContext *ctx; mm_obj_dbg (self, "couldn't load network timezone: %s", error->message); /* Note: may be NULL if the polling has been removed while processing the async operation */ ctx = (NetworkTimezoneContext *) g_object_get_qdata (G_OBJECT (self), network_timezone_context_quark); if (!ctx) return; /* Retry? */ ctx->network_timezone_poll_retries--; /* If no more retries, we don't do anything else */ if (ctx->network_timezone_poll_retries == 0 || !g_error_matches (error, MM_CORE_ERROR, MM_CORE_ERROR_RETRY)) { mm_obj_warn (self, "couldn't load network timezone from the current network"); return; } /* Otherwise, relaunch timeout to query a bit later */ ctx->network_timezone_poll_id = g_timeout_add_seconds (NETWORK_TIMEZONE_POLL_INTERVAL_SEC, (GSourceFunc)network_timezone_poll_cb, self); return; } /* Got final result properly, update the property in the skeleton */ update_network_timezone_dictionary (self, tz); g_object_unref (tz); } static gboolean network_timezone_poll_cb (MMIfaceModemTime *self) { NetworkTimezoneContext *ctx; ctx = (NetworkTimezoneContext *) g_object_get_qdata (G_OBJECT (self), network_timezone_context_quark); ctx->network_timezone_poll_id = 0; MM_IFACE_MODEM_TIME_GET_INTERFACE (self)->load_network_timezone ( self, (GAsyncReadyCallback)load_network_timezone_ready, NULL); return G_SOURCE_REMOVE; } static void start_network_timezone_poll (MMIfaceModemTime *self) { NetworkTimezoneContext *ctx; ctx = (NetworkTimezoneContext *) g_object_get_qdata (G_OBJECT (self), network_timezone_context_quark); mm_obj_dbg (self, "network timezone polling started"); ctx->network_timezone_poll_retries = NETWORK_TIMEZONE_POLL_RETRIES; ctx->network_timezone_poll_id = g_timeout_add_seconds (NETWORK_TIMEZONE_POLL_INTERVAL_SEC, (GSourceFunc)network_timezone_poll_cb, self); } static void stop_network_timezone_poll (MMIfaceModemTime *self) { NetworkTimezoneContext *ctx; ctx = (NetworkTimezoneContext *) g_object_get_qdata (G_OBJECT (self), network_timezone_context_quark); if (ctx->network_timezone_poll_id) { mm_obj_dbg (self, "network timezone polling stopped"); g_source_remove (ctx->network_timezone_poll_id); ctx->network_timezone_poll_id = 0; } } static void network_timezone_state_changed (MMIfaceModemTime *self) { NetworkTimezoneContext *ctx; MMModemState old_state; ctx = (NetworkTimezoneContext *) g_object_get_qdata (G_OBJECT (self), network_timezone_context_quark); old_state = ctx->state; g_object_get (self, MM_IFACE_MODEM_STATE, &ctx->state, NULL); /* If going from unregistered to registered, start polling */ if (ctx->state >= MM_MODEM_STATE_REGISTERED && old_state < MM_MODEM_STATE_REGISTERED) { start_network_timezone_poll (self); return; } /* If going from registered to unregistered, stop polling */ if (ctx->state < MM_MODEM_STATE_REGISTERED && old_state >= MM_MODEM_STATE_REGISTERED) { stop_network_timezone_poll (self); return; } } static void stop_network_timezone (MMIfaceModemTime *self) { NetworkTimezoneContext *ctx; ctx = (NetworkTimezoneContext *) g_object_get_qdata (G_OBJECT (self), network_timezone_context_quark); if (ctx) { /* Remove signal connection and then trigger context free */ if (ctx->state_changed_id) { g_signal_handler_disconnect (self, ctx->state_changed_id); ctx->state_changed_id = 0; } g_object_set_qdata (G_OBJECT (self), network_timezone_context_quark, NULL); } } static void start_network_timezone (MMIfaceModemTime *self) { NetworkTimezoneContext *ctx; /* If loading network timezone not supported, just finish here */ if (!MM_IFACE_MODEM_TIME_GET_INTERFACE (self)->load_network_timezone || !MM_IFACE_MODEM_TIME_GET_INTERFACE (self)->load_network_timezone_finish) { mm_obj_dbg (self, "loading network timezone is not supported"); return; } if (G_UNLIKELY (!network_timezone_context_quark)) network_timezone_context_quark = (g_quark_from_static_string (NETWORK_TIMEZONE_CONTEXT_TAG)); /* Cleanup context properly if it already exists, including the signal handler */ stop_network_timezone (self); ctx = g_new0 (NetworkTimezoneContext, 1); g_object_set_qdata_full (G_OBJECT (self), network_timezone_context_quark, ctx, (GDestroyNotify)network_timezone_context_free); /* Want to get notified when modem state changes to enable/disable * the polling logic. This signal is connected as long as the network timezone * logic is enabled. */ g_object_get (self, MM_IFACE_MODEM_STATE, &ctx->state, NULL); ctx->state_changed_id = g_signal_connect (self, "notify::" MM_IFACE_MODEM_STATE, G_CALLBACK (network_timezone_state_changed), NULL); /* If we're registered already, start timezone poll */ if (ctx->state >= MM_MODEM_STATE_REGISTERED) start_network_timezone_poll (self); } /*****************************************************************************/ void mm_iface_modem_time_update_network_time (MMIfaceModemTime *self, const gchar *network_time) { MmGdbusModemTime *skeleton; g_object_get (self, MM_IFACE_MODEM_TIME_DBUS_SKELETON, &skeleton, NULL); if (!skeleton) return; /* Notify about the updated network time */ mm_gdbus_modem_time_emit_network_time_changed (skeleton, network_time); g_object_unref (skeleton); } void mm_iface_modem_time_update_network_timezone (MMIfaceModemTime *self, MMNetworkTimezone *tz) { MmGdbusModemTime *skeleton; GVariant *dictionary; g_object_get (self, MM_IFACE_MODEM_TIME_DBUS_SKELETON, &skeleton, NULL); if (!skeleton) return; dictionary = mm_network_timezone_get_dictionary (tz); mm_gdbus_modem_time_set_network_timezone (skeleton, dictionary); if (dictionary) g_variant_unref (dictionary); g_object_unref (skeleton); } /*****************************************************************************/ typedef struct _DisablingContext DisablingContext; static void interface_disabling_step (GTask *task); typedef enum { DISABLING_STEP_FIRST, DISABLING_STEP_CANCEL_NETWORK_TIMEZONE_UPDATE, DISABLING_STEP_DISABLE_UNSOLICITED_EVENTS, DISABLING_STEP_CLEANUP_UNSOLICITED_EVENTS, DISABLING_STEP_LAST } DisablingStep; struct _DisablingContext { DisablingStep step; MmGdbusModemTime *skeleton; }; static void disabling_context_free (DisablingContext *ctx) { if (ctx->skeleton) g_object_unref (ctx->skeleton); g_free (ctx); } gboolean mm_iface_modem_time_disable_finish (MMIfaceModemTime *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void disable_unsolicited_events_ready (MMIfaceModemTime *self, GAsyncResult *res, GTask *task) { DisablingContext *ctx; GError *error = NULL; MM_IFACE_MODEM_TIME_GET_INTERFACE (self)->disable_unsolicited_events_finish (self, res, &error); if (error) { g_task_return_error (task, error); g_object_unref (task); return; } /* Go on to next step */ ctx = g_task_get_task_data (task); ctx->step++; interface_disabling_step (task); } static void cleanup_unsolicited_events_ready (MMIfaceModemTime *self, GAsyncResult *res, GTask *task) { DisablingContext *ctx; GError *error = NULL; MM_IFACE_MODEM_TIME_GET_INTERFACE (self)->cleanup_unsolicited_events_finish (self, res, &error); if (error) { g_task_return_error (task, error); g_object_unref (task); return; } /* Go on to next step */ ctx = g_task_get_task_data (task); ctx->step++; interface_disabling_step (task); } static void interface_disabling_step (GTask *task) { MMIfaceModemTime *self; DisablingContext *ctx; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); switch (ctx->step) { case DISABLING_STEP_FIRST: ctx->step++; /* fall through */ case DISABLING_STEP_CANCEL_NETWORK_TIMEZONE_UPDATE: /* Stop and cleanup context */ stop_network_timezone (self); ctx->step++; /* fall through */ case DISABLING_STEP_DISABLE_UNSOLICITED_EVENTS: /* Allow cleaning up unsolicited events */ if (MM_IFACE_MODEM_TIME_GET_INTERFACE (self)->disable_unsolicited_events && MM_IFACE_MODEM_TIME_GET_INTERFACE (self)->disable_unsolicited_events_finish) { MM_IFACE_MODEM_TIME_GET_INTERFACE (self)->disable_unsolicited_events ( self, (GAsyncReadyCallback)disable_unsolicited_events_ready, task); return; } ctx->step++; /* fall through */ case DISABLING_STEP_CLEANUP_UNSOLICITED_EVENTS: /* Allow cleaning up unsolicited events */ if (MM_IFACE_MODEM_TIME_GET_INTERFACE (self)->cleanup_unsolicited_events && MM_IFACE_MODEM_TIME_GET_INTERFACE (self)->cleanup_unsolicited_events_finish) { MM_IFACE_MODEM_TIME_GET_INTERFACE (self)->cleanup_unsolicited_events ( self, (GAsyncReadyCallback)cleanup_unsolicited_events_ready, task); return; } ctx->step++; /* fall through */ case DISABLING_STEP_LAST: /* We are done without errors! */ g_task_return_boolean (task, TRUE); g_object_unref (task); return; default: break; } g_assert_not_reached (); } void mm_iface_modem_time_disable (MMIfaceModemTime *self, GAsyncReadyCallback callback, gpointer user_data) { DisablingContext *ctx; GTask *task; ctx = g_new0 (DisablingContext, 1); ctx->step = DISABLING_STEP_FIRST; task = g_task_new (self, NULL, callback, user_data); g_task_set_task_data (task, ctx, (GDestroyNotify)disabling_context_free); g_object_get (self, MM_IFACE_MODEM_TIME_DBUS_SKELETON, &ctx->skeleton, NULL); if (!ctx->skeleton) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't get interface skeleton"); g_object_unref (task); return; } interface_disabling_step (task); } /*****************************************************************************/ typedef struct _EnablingContext EnablingContext; static void interface_enabling_step (GTask *task); typedef enum { ENABLING_STEP_FIRST, ENABLING_STEP_SETUP_NETWORK_TIMEZONE_RETRIEVAL, ENABLING_STEP_SETUP_UNSOLICITED_EVENTS, ENABLING_STEP_ENABLE_UNSOLICITED_EVENTS, ENABLING_STEP_LAST } EnablingStep; struct _EnablingContext { EnablingStep step; MmGdbusModemTime *skeleton; }; static void enabling_context_free (EnablingContext *ctx) { if (ctx->skeleton) g_object_unref (ctx->skeleton); g_free (ctx); } gboolean mm_iface_modem_time_enable_finish (MMIfaceModemTime *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void setup_unsolicited_events_ready (MMIfaceModemTime *self, GAsyncResult *res, GTask *task) { EnablingContext *ctx; GError *error = NULL; MM_IFACE_MODEM_TIME_GET_INTERFACE (self)->setup_unsolicited_events_finish (self, res, &error); if (error) { g_task_return_error (task, error); g_object_unref (task); return; } /* Go on to next step */ ctx = g_task_get_task_data (task); ctx->step++; interface_enabling_step (task); } static void enable_unsolicited_events_ready (MMIfaceModemTime *self, GAsyncResult *res, GTask *task) { EnablingContext *ctx; GError *error = NULL; /* Not critical! */ if (!MM_IFACE_MODEM_TIME_GET_INTERFACE (self)->enable_unsolicited_events_finish (self, res, &error)) { mm_obj_dbg (self, "couldn't enable unsolicited events: %s", error->message); g_error_free (error); } /* Go on with next step */ ctx = g_task_get_task_data (task); ctx->step++; interface_enabling_step (task); } static void interface_enabling_step (GTask *task) { MMIfaceModemTime *self; EnablingContext *ctx; /* Don't run new steps if we're cancelled */ if (g_task_return_error_if_cancelled (task)) { g_object_unref (task); return; } self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); switch (ctx->step) { case ENABLING_STEP_FIRST: ctx->step++; /* fall through */ case ENABLING_STEP_SETUP_NETWORK_TIMEZONE_RETRIEVAL: /* We start it and schedule it to run asynchronously */ start_network_timezone (self); ctx->step++; /* fall through */ case ENABLING_STEP_SETUP_UNSOLICITED_EVENTS: /* Allow setting up unsolicited events */ if (MM_IFACE_MODEM_TIME_GET_INTERFACE (self)->setup_unsolicited_events && MM_IFACE_MODEM_TIME_GET_INTERFACE (self)->setup_unsolicited_events_finish) { MM_IFACE_MODEM_TIME_GET_INTERFACE (self)->setup_unsolicited_events ( self, (GAsyncReadyCallback)setup_unsolicited_events_ready, task); return; } ctx->step++; /* fall through */ case ENABLING_STEP_ENABLE_UNSOLICITED_EVENTS: /* Allow setting up unsolicited events */ if (MM_IFACE_MODEM_TIME_GET_INTERFACE (self)->enable_unsolicited_events && MM_IFACE_MODEM_TIME_GET_INTERFACE (self)->enable_unsolicited_events_finish) { MM_IFACE_MODEM_TIME_GET_INTERFACE (self)->enable_unsolicited_events ( self, (GAsyncReadyCallback)enable_unsolicited_events_ready, task); return; } ctx->step++; /* fall through */ case ENABLING_STEP_LAST: /* We are done without errors! */ g_task_return_boolean (task, TRUE); g_object_unref (task); return; default: break; } g_assert_not_reached (); } void mm_iface_modem_time_enable (MMIfaceModemTime *self, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { EnablingContext *ctx; GTask *task; ctx = g_new0 (EnablingContext, 1); ctx->step = ENABLING_STEP_FIRST; task = g_task_new (self, cancellable, callback, user_data); g_task_set_task_data (task, ctx, (GDestroyNotify)enabling_context_free); g_object_get (self, MM_IFACE_MODEM_TIME_DBUS_SKELETON, &ctx->skeleton, NULL); if (!ctx->skeleton) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't get interface skeleton"); g_object_unref (task); return; } interface_enabling_step (task); } /*****************************************************************************/ #if defined WITH_SUSPEND_RESUME typedef struct _SyncingContext SyncingContext; static void interface_syncing_step (GTask *task); typedef enum { SYNCING_STEP_FIRST, SYNCING_STEP_REFRESH_NETWORK_TIMEZONE, SYNCING_STEP_LAST } SyncingStep; struct _SyncingContext { SyncingStep step; }; gboolean mm_iface_modem_time_sync_finish (MMIfaceModemTime *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void interface_syncing_step (GTask *task) { MMIfaceModemTime *self; SyncingContext *ctx; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); switch (ctx->step) { case SYNCING_STEP_FIRST: ctx->step++; /* fall through */ case SYNCING_STEP_REFRESH_NETWORK_TIMEZONE: /* We start it and schedule it to run asynchronously */ start_network_timezone (self); ctx->step++; /* fall through */ case SYNCING_STEP_LAST: /* We are done without errors! */ g_task_return_boolean (task, TRUE); g_object_unref (task); return; default: break; } g_assert_not_reached (); } void mm_iface_modem_time_sync (MMIfaceModemTime *self, GAsyncReadyCallback callback, gpointer user_data) { SyncingContext *ctx; GTask *task; /* Create SyncingContext */ ctx = g_new0 (SyncingContext, 1); ctx->step = SYNCING_STEP_FIRST; /* Create sync steps task and execute it */ task = g_task_new (self, NULL, callback, user_data); g_task_set_task_data (task, ctx, (GDestroyNotify)g_free); interface_syncing_step (task); } #endif /*****************************************************************************/ typedef struct _InitializationContext InitializationContext; static void interface_initialization_step (GTask *task); typedef enum { INITIALIZATION_STEP_FIRST, INITIALIZATION_STEP_CHECK_SUPPORT, INITIALIZATION_STEP_FAIL_IF_UNSUPPORTED, INITIALIZATION_STEP_LAST } InitializationStep; struct _InitializationContext { MmGdbusModemTime *skeleton; InitializationStep step; }; static void initialization_context_free (InitializationContext *ctx) { g_object_unref (ctx->skeleton); g_free (ctx); } static void check_support_ready (MMIfaceModemTime *self, GAsyncResult *res, GTask *task) { InitializationContext *ctx; GError *error = NULL; if (!MM_IFACE_MODEM_TIME_GET_INTERFACE (self)->check_support_finish (self, res, &error)) { if (error) { /* This error shouldn't be treated as critical */ mm_obj_dbg (self, "time support check failed: %s", error->message); g_error_free (error); } } else { /* Time is supported! */ g_object_set_qdata (G_OBJECT (self), supported_quark, GUINT_TO_POINTER (TRUE)); } /* Go on to next step */ ctx = g_task_get_task_data (task); ctx->step++; interface_initialization_step (task); } static void interface_initialization_step (GTask *task) { MMIfaceModemTime *self; InitializationContext *ctx; /* Don't run new steps if we're cancelled */ if (g_task_return_error_if_cancelled (task)) { g_object_unref (task); return; } self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); switch (ctx->step) { case INITIALIZATION_STEP_FIRST: /* Setup quarks if we didn't do it before */ if (G_UNLIKELY (!support_checked_quark)) support_checked_quark = (g_quark_from_static_string ( SUPPORT_CHECKED_TAG)); if (G_UNLIKELY (!supported_quark)) supported_quark = (g_quark_from_static_string ( SUPPORTED_TAG)); ctx->step++; /* fall through */ case INITIALIZATION_STEP_CHECK_SUPPORT: if (!GPOINTER_TO_UINT (g_object_get_qdata (G_OBJECT (self), support_checked_quark))) { /* Set the checked flag so that we don't run it again */ g_object_set_qdata (G_OBJECT (self), support_checked_quark, GUINT_TO_POINTER (TRUE)); /* Initially, assume we don't support it */ g_object_set_qdata (G_OBJECT (self), supported_quark, GUINT_TO_POINTER (FALSE)); if (MM_IFACE_MODEM_TIME_GET_INTERFACE (self)->check_support && MM_IFACE_MODEM_TIME_GET_INTERFACE (self)->check_support_finish) { MM_IFACE_MODEM_TIME_GET_INTERFACE (self)->check_support ( self, (GAsyncReadyCallback)check_support_ready, task); return; } /* If there is no implementation to check support, assume we DON'T * support it. */ } ctx->step++; /* fall through */ case INITIALIZATION_STEP_FAIL_IF_UNSUPPORTED: if (!GPOINTER_TO_UINT (g_object_get_qdata (G_OBJECT (self), supported_quark))) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "Time not supported"); g_object_unref (task); return; } ctx->step++; /* fall through */ case INITIALIZATION_STEP_LAST: /* We are done without errors! */ /* Handle method invocations */ g_signal_connect (ctx->skeleton, "handle-get-network-time", G_CALLBACK (handle_get_network_time), self); /* Finally, export the new interface */ mm_gdbus_object_skeleton_set_modem_time (MM_GDBUS_OBJECT_SKELETON (self), MM_GDBUS_MODEM_TIME (ctx->skeleton)); g_task_return_boolean (task, TRUE); g_object_unref (task); return; default: break; } g_assert_not_reached (); } gboolean mm_iface_modem_time_initialize_finish (MMIfaceModemTime *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } void mm_iface_modem_time_initialize (MMIfaceModemTime *self, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { InitializationContext *ctx; MmGdbusModemTime *skeleton = NULL; GTask *task; /* Did we already create it? */ g_object_get (self, MM_IFACE_MODEM_TIME_DBUS_SKELETON, &skeleton, NULL); if (!skeleton) { skeleton = mm_gdbus_modem_time_skeleton_new (); g_object_set (self, MM_IFACE_MODEM_TIME_DBUS_SKELETON, skeleton, NULL); } /* Perform async initialization here */ ctx = g_new0 (InitializationContext, 1); ctx->step = INITIALIZATION_STEP_FIRST; ctx->skeleton = skeleton; task = g_task_new (self, cancellable, callback, user_data); g_task_set_task_data (task, ctx, (GDestroyNotify)initialization_context_free); interface_initialization_step (task); } void mm_iface_modem_time_shutdown (MMIfaceModemTime *self) { /* Unexport DBus interface and remove the skeleton */ mm_gdbus_object_skeleton_set_modem_time (MM_GDBUS_OBJECT_SKELETON (self), NULL); g_object_set (self, MM_IFACE_MODEM_TIME_DBUS_SKELETON, NULL, NULL); } /*****************************************************************************/ static void iface_modem_time_init (gpointer g_iface) { static gboolean initialized = FALSE; if (initialized) return; /* Properties */ g_object_interface_install_property (g_iface, g_param_spec_object (MM_IFACE_MODEM_TIME_DBUS_SKELETON, "Time DBus skeleton", "DBus skeleton for the Time interface", MM_GDBUS_TYPE_MODEM_TIME_SKELETON, G_PARAM_READWRITE)); initialized = TRUE; } GType mm_iface_modem_time_get_type (void) { static GType iface_modem_time_type = 0; if (!G_UNLIKELY (iface_modem_time_type)) { static const GTypeInfo info = { sizeof (MMIfaceModemTime), /* class_size */ iface_modem_time_init, /* base_init */ NULL, /* base_finalize */ }; iface_modem_time_type = g_type_register_static (G_TYPE_INTERFACE, "MMIfaceModemTime", &info, 0); g_type_interface_add_prerequisite (iface_modem_time_type, MM_TYPE_IFACE_MODEM); } return iface_modem_time_type; } ModemManager-1.23.4-dev/src/mm-iface-modem-time.h000066400000000000000000000164131456466623000214030ustar00rootroot00000000000000/* -*- 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) 2012 Google, Inc. */ #ifndef MM_IFACE_MODEM_TIME_H #define MM_IFACE_MODEM_TIME_H #include #include #define _LIBMM_INSIDE_MM #include #define MM_TYPE_IFACE_MODEM_TIME (mm_iface_modem_time_get_type ()) #define MM_IFACE_MODEM_TIME(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_IFACE_MODEM_TIME, MMIfaceModemTime)) #define MM_IS_IFACE_MODEM_TIME(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_IFACE_MODEM_TIME)) #define MM_IFACE_MODEM_TIME_GET_INTERFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE ((obj), MM_TYPE_IFACE_MODEM_TIME, MMIfaceModemTime)) #define MM_IFACE_MODEM_TIME_DBUS_SKELETON "iface-modem-time-dbus-skeleton" typedef struct _MMIfaceModemTime MMIfaceModemTime; struct _MMIfaceModemTime { GTypeInterface g_iface; /* Check for Time support (async) */ void (* check_support) (MMIfaceModemTime *self, GAsyncReadyCallback callback, gpointer user_data); gboolean (* check_support_finish) (MMIfaceModemTime *self, GAsyncResult *res, GError **error); /* Get current network time */ void (* load_network_time) (MMIfaceModemTime *self, GAsyncReadyCallback callback, gpointer user_data); gchar * (* load_network_time_finish) (MMIfaceModemTime *self, GAsyncResult *res, GError **error); /* Loading of the network timezone property. This method may return * MM_CORE_ERROR_RETRY if the timezone cannot yet be loaded, so that * the interface retries later. */ void (* load_network_timezone) (MMIfaceModemTime *self, GAsyncReadyCallback callback, gpointer user_data); MMNetworkTimezone * (* load_network_timezone_finish) (MMIfaceModemTime *self, GAsyncResult *res, GError **error); /* Asynchronous setting up unsolicited events */ void (*setup_unsolicited_events) (MMIfaceModemTime *self, GAsyncReadyCallback callback, gpointer user_data); gboolean (*setup_unsolicited_events_finish) (MMIfaceModemTime *self, GAsyncResult *res, GError **error); /* Asynchronous cleaning up of unsolicited events */ void (*cleanup_unsolicited_events) (MMIfaceModemTime *self, GAsyncReadyCallback callback, gpointer user_data); gboolean (*cleanup_unsolicited_events_finish) (MMIfaceModemTime *self, GAsyncResult *res, GError **error); /* Asynchronous enabling unsolicited events */ void (* enable_unsolicited_events) (MMIfaceModemTime *self, GAsyncReadyCallback callback, gpointer user_data); gboolean (* enable_unsolicited_events_finish) (MMIfaceModemTime *self, GAsyncResult *res, GError **error); /* Asynchronous disabling unsolicited events */ void (* disable_unsolicited_events) (MMIfaceModemTime *self, GAsyncReadyCallback callback, gpointer user_data); gboolean (* disable_unsolicited_events_finish) (MMIfaceModemTime *self, GAsyncResult *res, GError **error); }; GType mm_iface_modem_time_get_type (void); G_DEFINE_AUTOPTR_CLEANUP_FUNC (MMIfaceModemTime, g_object_unref) /* Initialize Time interface (async) */ void mm_iface_modem_time_initialize (MMIfaceModemTime *self, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); gboolean mm_iface_modem_time_initialize_finish (MMIfaceModemTime *self, GAsyncResult *res, GError **error); /* Enable Time interface (async) */ void mm_iface_modem_time_enable (MMIfaceModemTime *self, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); gboolean mm_iface_modem_time_enable_finish (MMIfaceModemTime *self, GAsyncResult *res, GError **error); /* Disable Time interface (async) */ void mm_iface_modem_time_disable (MMIfaceModemTime *self, GAsyncReadyCallback callback, gpointer user_data); gboolean mm_iface_modem_time_disable_finish (MMIfaceModemTime *self, GAsyncResult *res, GError **error); /* Shutdown Time interface */ void mm_iface_modem_time_shutdown (MMIfaceModemTime *self); #if defined WITH_SUSPEND_RESUME /* Sync Time interface (async) */ void mm_iface_modem_time_sync (MMIfaceModemTime *self, GAsyncReadyCallback callback, gpointer user_data); gboolean mm_iface_modem_time_sync_finish (MMIfaceModemTime *self, GAsyncResult *res, GError **error); #endif /* Bind properties for simple GetStatus() */ void mm_iface_modem_time_bind_simple_status (MMIfaceModemTime *self, MMSimpleStatus *status); /* Implementations of the unsolicited events handling should call this method * to notify about the updated time */ void mm_iface_modem_time_update_network_time (MMIfaceModemTime *self, const gchar *network_time); void mm_iface_modem_time_update_network_timezone (MMIfaceModemTime *self, MMNetworkTimezone *tz); #endif /* MM_IFACE_MODEM_TIME_H */ ModemManager-1.23.4-dev/src/mm-iface-modem-voice.c000066400000000000000000003310651456466623000215500ustar00rootroot00000000000000/* -*- 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) 2015 Marco Bascetta * Copyright (C) 2019 Purism SPC */ #include #define _LIBMM_INSIDE_MM #include #include "mm-iface-modem.h" #include "mm-iface-modem-voice.h" #include "mm-call-list.h" #include "mm-error-helpers.h" #include "mm-log-object.h" #define CALL_LIST_POLLING_CONTEXT_TAG "voice-call-list-polling-context-tag" #define IN_CALL_EVENT_CONTEXT_TAG "voice-in-call-event-context-tag" static GQuark call_list_polling_context_quark; static GQuark in_call_event_context_quark; /*****************************************************************************/ void mm_iface_modem_voice_bind_simple_status (MMIfaceModemVoice *self, MMSimpleStatus *status) { } /*****************************************************************************/ gboolean mm_iface_modem_voice_authorize_outgoing_call (MMIfaceModemVoice *self, MMBaseCall *call, GError **error) { MmGdbusModemVoice *skeleton = NULL; MMBaseSim *sim = NULL; gboolean emergency_only = FALSE; gboolean call_allowed = FALSE; GError *inner_error = NULL; guint i; const gchar *number; static const gchar *always_valid_emergency_numbers[] = { "112", "911" }; static const gchar *no_sim_valid_emergency_numbers[] = { "000", "08", "110", "999", "118", "119" }; g_assert (mm_base_call_get_direction (call) == MM_CALL_DIRECTION_OUTGOING); number = mm_base_call_get_number (call); g_assert (number); g_object_get (self, MM_IFACE_MODEM_VOICE_DBUS_SKELETON, &skeleton, NULL); if (!skeleton) { g_set_error (&inner_error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "voice operations unsupported"); goto out; } g_object_get (skeleton, "emergency-only", &emergency_only, NULL); /* Identification of emergency numbers. 3GPP TS 22.101 * * a) 112 and 911 shall always be available. * b) Any emergency call number stored on a SIM/USIM when the SIM/USIM is * present. * c) 000, 08, 110, 999, 118 and 119 when a SIM/USIM is not present. * d) Additional emergency call numbers that may have been downloaded by * the serving network when the SIM/USIM is present. * * In ModemManager we're not flagging any call as being "emergency" or * "normal", but we can right away limit non-emergency calls if we're in * "emergency-only" mode. */ /* If we're not in emergency mode, the call (emergency or normal) is always allowed */ if (!emergency_only) { mm_obj_dbg (self, "voice call to %s allowed", number); call_allowed = TRUE; goto out; } for (i = 0; i < G_N_ELEMENTS (always_valid_emergency_numbers); i++) { if (g_strcmp0 (number, always_valid_emergency_numbers[i]) == 0) { mm_obj_dbg (self, "voice call to %s allowed: emergency call number always valid", number); call_allowed = TRUE; goto out; } } /* Check if we have a SIM */ g_object_get (self, MM_IFACE_MODEM_SIM, &sim, NULL); if (!sim) { /* If no SIM available, some additional numbers may be valid emergency numbers */ for (i = 0; i < G_N_ELEMENTS (no_sim_valid_emergency_numbers); i++) { if (g_strcmp0 (number, no_sim_valid_emergency_numbers[i]) == 0) { mm_obj_dbg (self, "voice call to %s allowed: emergency call number valid when no SIM", number); call_allowed = TRUE; goto out; } } mm_obj_dbg (self, "voice call to %s NOT allowed: not a valid emergency call number when no SIM", number); goto out; } /* Check if the number is programmed in EF_ECC */ if (mm_base_sim_is_emergency_number (sim, number)) { mm_obj_dbg (self, "voice call to %s allowed: emergency call number programmed in the SIM", number); call_allowed = TRUE; } else mm_obj_dbg (self, "voice call to %s NOT allowed: not a valid emergency call number programmed in the SIM", number); out: if (inner_error) g_propagate_error (error, inner_error); else if (!call_allowed) g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_UNAUTHORIZED, "only emergency calls allowed"); g_clear_object (&skeleton); g_clear_object (&sim); return call_allowed; } /*****************************************************************************/ /* new calls will inherit audio settings if the modem is already in-call state */ static void update_audio_settings_in_call (MMIfaceModemVoice *self, MMBaseCall *call); static MMBaseCall * create_incoming_call (MMIfaceModemVoice *self, const gchar *number) { MMBaseCall *call; g_assert (MM_IFACE_MODEM_VOICE_GET_INTERFACE (self)->create_call != NULL); call = MM_IFACE_MODEM_VOICE_GET_INTERFACE (self)->create_call (self, MM_CALL_DIRECTION_INCOMING, number); update_audio_settings_in_call (self, call); return call; } static MMBaseCall * create_outgoing_call_from_properties (MMIfaceModemVoice *self, MMCallProperties *properties, GError **error) { MMBaseCall *call; const gchar *number; /* Don't create CALL from properties if either number is missing */ number = mm_call_properties_get_number (properties) ; if (!number) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS, "Cannot create call: mandatory parameter 'number' is missing"); return NULL; } /* Create a call object as defined by the interface */ g_assert (MM_IFACE_MODEM_VOICE_GET_INTERFACE (self)->create_call != NULL); call = MM_IFACE_MODEM_VOICE_GET_INTERFACE (self)->create_call (self, MM_CALL_DIRECTION_OUTGOING, number); update_audio_settings_in_call (self, call); return call; } /*****************************************************************************/ /* Common helper to match call info against a known call object */ static gboolean match_single_call_info (MMIfaceModemVoice *self, const MMCallInfo *call_info, MMBaseCall *call) { MMCallState state; MMCallDirection direction; const gchar *number; guint idx; gboolean match_direction_and_state = FALSE; gboolean match_number = FALSE; gboolean match_index = FALSE; gboolean match_terminated = FALSE; /* try to look for a matching call by direction/number/index */ state = mm_base_call_get_state (call); direction = mm_base_call_get_direction (call); number = mm_base_call_get_number (call); idx = mm_base_call_get_index (call); /* Match index */ if (call_info->index && (call_info->index == idx)) match_index = TRUE; /* Match direction and state. * We cannot apply this match if both call info and call have an index set * and they're different already. */ if ((call_info->direction == direction) && (call_info->state == state) && (!call_info->index || !idx || match_index)) match_direction_and_state = TRUE; /* Match number */ if (call_info->number && number && g_strcmp0 (call_info->number, number) == 0) match_number = TRUE; /* Match special terminated event. * We cannot apply this match if the call is part of a multiparty * call, because we don't know which of the calls in the multiparty * is the one that finished. Must rely on other reports that do * provide call index. */ if ((call_info->state == MM_CALL_STATE_TERMINATED) && (call_info->direction == MM_CALL_DIRECTION_UNKNOWN) && !call_info->index && !call_info->number && !mm_base_call_get_multiparty (call)) match_terminated = TRUE; /* If no clear match, nothing to do */ if (!match_direction_and_state && !match_number && !match_index && !match_terminated) return FALSE; mm_obj_dbg (self, "call info matched (matched direction/state %s, matched number %s" ", matched index %s, matched terminated %s) with call at '%s'", match_direction_and_state ? "yes" : "no", match_number ? "yes" : "no", match_index ? "yes" : "no", match_terminated ? "yes" : "no", mm_base_call_get_path (call)); /* Early detect if a known incoming call that was created * from a plain CRING URC (i.e. without caller number) * needs to have the number provided. */ if (call_info->number && !number) { mm_obj_dbg (self, " number set: %s", call_info->number); mm_base_call_set_number (call, call_info->number); } /* Early detect if a known incoming/outgoing call does * not have a known call index yet. */ if (call_info->index && !idx) { mm_obj_dbg (self, " index set: %u", call_info->index); mm_base_call_set_index (call, call_info->index); } /* Update state if it changed */ if (call_info->state != state) { mm_obj_dbg (self, " state updated: %s", mm_call_state_get_string (call_info->state)); mm_base_call_change_state (call, call_info->state, MM_CALL_STATE_REASON_UNKNOWN); } /* refresh if incoming and new state is not terminated */ if ((call_info->state != MM_CALL_STATE_TERMINATED) && (direction == MM_CALL_DIRECTION_INCOMING)) { mm_obj_dbg (self, " incoming refreshed"); mm_base_call_incoming_refresh (call); } return TRUE; } /*****************************************************************************/ typedef struct { MMIfaceModemVoice *self; const MMCallInfo *call_info; } ReportCallForeachContext; static void report_call_foreach (MMBaseCall *call, ReportCallForeachContext *ctx) { /* Do nothing if already matched */ if (!ctx->call_info) return; /* fully ignore already terminated calls */ if (mm_base_call_get_state (call) == MM_CALL_STATE_TERMINATED) return; /* Reset call info in context if the call info matches an existing call */ if (match_single_call_info (ctx->self, ctx->call_info, call)) ctx->call_info = NULL; } void mm_iface_modem_voice_report_call (MMIfaceModemVoice *self, const MMCallInfo *call_info) { ReportCallForeachContext ctx = { 0 }; MMBaseCall *call = NULL; MMCallList *list = NULL; /* When reporting single call, the only mandatory parameter is the state: * - index is optional (e.g. unavailable when receiving +CLIP URCs) * - number is optional (e.g. unavailable when receiving +CRING URCs) * - direction is optional (e.g. unavailable when receiving some vendor-specific URCs) */ g_assert (call_info->state != MM_CALL_STATE_UNKNOWN); /* Early debugging of the call state update */ mm_obj_dbg (self, "call at index %u: direction %s, state %s, number %s", call_info->index, mm_call_direction_get_string (call_info->direction), mm_call_state_get_string (call_info->state), call_info->number ? call_info->number : "n/a"); g_object_get (MM_BASE_MODEM (self), MM_IFACE_MODEM_VOICE_CALL_LIST, &list, NULL); if (!list) { mm_obj_warn (self, "cannot process call state update: missing call list"); return; } /* Iterate over all known calls and try to match a known one */ ctx.self = self; ctx.call_info = call_info; mm_call_list_foreach (list, (MMCallListForeachFunc)report_call_foreach, &ctx); /* If call info matched with an existing one, the context call info would have been reseted */ if (!ctx.call_info) goto out; /* If call info didn't match with any known call, it may be because we're being * reported a NEW incoming call. If that's not the case, we'll ignore the report. */ if ((call_info->direction != MM_CALL_DIRECTION_INCOMING) || ((call_info->state != MM_CALL_STATE_WAITING) && (call_info->state != MM_CALL_STATE_RINGING_IN))) { mm_obj_dbg (self, "unhandled call state update reported: direction: %s, state %s", mm_call_direction_get_string (call_info->direction), mm_call_state_get_string (call_info->state)); goto out; } mm_obj_dbg (self, "creating new incoming call..."); call = create_incoming_call (self, call_info->number); /* Set the state */ mm_base_call_change_state (call, call_info->state, MM_CALL_STATE_REASON_INCOMING_NEW); /* Set the index, if known */ if (call_info->index) mm_base_call_set_index (call, call_info->index); /* Start its validity timeout */ mm_base_call_incoming_refresh (call); /* Only export once properly created */ mm_base_call_export (call); mm_call_list_add_call (list, call); g_object_unref (call); out: g_object_unref (list); } /*****************************************************************************/ /* Full current call list reporting * * This method receives as input a list with all the currently active calls, * including the specific state they're in. * * This method should: * - Check whether we're reporting a new call (i.e. not in our internal call * list yet). We'll create a new call object if so. * - Check whether any of the known calls has changed state, and if so, * update it. * - Check whether any of the known calls is NOT given in the input list of * call infos, which would mean the call is terminated. */ typedef struct { MMIfaceModemVoice *self; GList *call_info_list; } ReportAllCallsForeachContext; static void report_all_calls_foreach (MMBaseCall *call, ReportAllCallsForeachContext *ctx) { MMCallState state; GList *l; /* fully ignore already terminated calls */ state = mm_base_call_get_state (call); if (state == MM_CALL_STATE_TERMINATED) return; /* Iterate over the call info list */ for (l = ctx->call_info_list; l; l = g_list_next (l)) { MMCallInfo *call_info = (MMCallInfo *)(l->data); /* if match found, delete item from list and halt iteration right away */ if (match_single_call_info (ctx->self, call_info, call)) { ctx->call_info_list = g_list_delete_link (ctx->call_info_list, l); return; } } /* not found in list! this call is now terminated */ mm_obj_dbg (ctx->self, "call '%s' with direction %s, state %s, number '%s', index %u" " not found in list, terminating", mm_base_call_get_path (call), mm_call_direction_get_string (mm_base_call_get_direction (call)), mm_call_state_get_string (state), mm_base_call_get_number (call), mm_base_call_get_index (call)); mm_base_call_change_state (call, MM_CALL_STATE_TERMINATED, MM_CALL_STATE_REASON_UNKNOWN); } void mm_iface_modem_voice_report_all_calls (MMIfaceModemVoice *self, GList *call_info_list) { ReportAllCallsForeachContext ctx = { 0 }; MMCallList *list = NULL; GList *l; /* Early debugging of the full list of calls */ mm_obj_dbg (self, "reported %u ongoing calls", g_list_length (call_info_list)); for (l = call_info_list; l; l = g_list_next (l)) { MMCallInfo *call_info = (MMCallInfo *)(l->data); /* When reporting full list of calls, index and state are mandatory */ g_assert (call_info->index != 0); g_assert (call_info->state != MM_CALL_STATE_UNKNOWN); mm_obj_dbg (self, "call at index %u: direction %s, state %s, number %s", call_info->index, mm_call_direction_get_string (call_info->direction), mm_call_state_get_string (call_info->state), call_info->number ? call_info->number : "n/a"); } /* Retrieve list of known calls */ g_object_get (MM_BASE_MODEM (self), MM_IFACE_MODEM_VOICE_CALL_LIST, &list, NULL); if (!list) { mm_obj_warn (self, "cannot report all calls: missing call list"); return; } /* Iterate over all the calls already known to us. * Whenever a known call is updated, it will be removed from the call info list */ ctx.self = self; ctx.call_info_list = g_list_copy (call_info_list); mm_call_list_foreach (list, (MMCallListForeachFunc)report_all_calls_foreach, &ctx); /* Once processed, the call info list will have all calls that were unknown to * us, i.e. the new calls to create. We really only expect new incoming calls, so * we'll warn if we get any outgoing call reported here. */ for (l = ctx.call_info_list; l; l = g_list_next (l)) { MMCallInfo *call_info = (MMCallInfo *)(l->data); /* Ignore unknown terminated calls, because these be due to an already * processed event. */ if (call_info->state == MM_CALL_STATE_TERMINATED) continue; if (call_info->direction == MM_CALL_DIRECTION_OUTGOING) { mm_obj_warn (self, "unexpected outgoing call to number '%s' reported in call list: state %s", call_info->number ? call_info->number : "n/a", mm_call_state_get_string (call_info->state)); continue; } if (call_info->direction == MM_CALL_DIRECTION_INCOMING) { MMBaseCall *call; /* We only expect either RINGING-IN or WAITING states */ if ((call_info->state != MM_CALL_STATE_RINGING_IN) && (call_info->state != MM_CALL_STATE_WAITING)) { mm_obj_warn (self, "unexpected incoming call to number '%s' reported in call list: state %s", call_info->number ? call_info->number : "n/a", mm_call_state_get_string (call_info->state)); continue; } mm_obj_dbg (self, "creating new incoming call..."); call = create_incoming_call (self, call_info->number); /* Set the state and the index */ mm_base_call_change_state (call, call_info->state, MM_CALL_STATE_REASON_INCOMING_NEW); mm_base_call_set_index (call, call_info->index); /* Start its validity timeout */ mm_base_call_incoming_refresh (call); /* Only export once properly created */ mm_base_call_export (call); mm_call_list_add_call (list, call); g_object_unref (call); continue; } mm_obj_warn (self, "unexpected call to number '%s' reported in call list: state %s, direction unknown", call_info->number ? call_info->number : "n/a", mm_call_state_get_string (call_info->state)); } g_list_free (ctx.call_info_list); g_object_unref (list); } /*****************************************************************************/ /* Incoming DTMF reception, not associated to a specific call */ typedef struct { guint index; const gchar *dtmf; } ReceivedDtmfContext; static void received_dtmf_foreach (MMBaseCall *call, ReceivedDtmfContext *ctx) { if ((!ctx->index || (ctx->index == mm_base_call_get_index (call))) && (mm_base_call_get_state (call) == MM_CALL_STATE_ACTIVE)) mm_base_call_received_dtmf (call, ctx->dtmf); } void mm_iface_modem_voice_received_dtmf (MMIfaceModemVoice *self, guint index, const gchar *dtmf) { MMCallList *list = NULL; ReceivedDtmfContext ctx = { .index = index, .dtmf = dtmf }; /* Retrieve list of known calls */ g_object_get (MM_BASE_MODEM (self), MM_IFACE_MODEM_VOICE_CALL_LIST, &list, NULL); if (!list) { mm_obj_warn (self, "cannot report received DTMF: missing call list"); return; } mm_call_list_foreach (list, (MMCallListForeachFunc)received_dtmf_foreach, &ctx); g_object_unref (list); } /*****************************************************************************/ typedef struct { MmGdbusModemVoice *skeleton; GDBusMethodInvocation *invocation; MMIfaceModemVoice *self; gchar *path; } HandleDeleteContext; static void handle_delete_context_free (HandleDeleteContext *ctx) { g_object_unref (ctx->skeleton); g_object_unref (ctx->invocation); g_object_unref (ctx->self); g_free (ctx->path); g_free (ctx); } static void handle_delete_auth_ready (MMBaseModem *self, GAsyncResult *res, HandleDeleteContext *ctx) { MMCallList *list = NULL; GError *error = NULL; if (!mm_base_modem_authorize_finish (self, res, &error)) { mm_dbus_method_invocation_take_error (ctx->invocation, error); handle_delete_context_free (ctx); return; } g_object_get (self, MM_IFACE_MODEM_VOICE_CALL_LIST, &list, NULL); if (!list) { mm_dbus_method_invocation_return_error_literal (ctx->invocation, MM_CORE_ERROR, MM_CORE_ERROR_WRONG_STATE, "Cannot delete call: missing call list"); handle_delete_context_free (ctx); return; } if (!mm_call_list_delete_call (list, ctx->path, &error)) mm_dbus_method_invocation_take_error (ctx->invocation, error); else mm_gdbus_modem_voice_complete_delete_call (ctx->skeleton, ctx->invocation); handle_delete_context_free (ctx); g_object_unref (list); } static gboolean handle_delete (MmGdbusModemVoice *skeleton, GDBusMethodInvocation *invocation, const gchar *path, MMIfaceModemVoice *self) { HandleDeleteContext *ctx; ctx = g_new (HandleDeleteContext, 1); ctx->skeleton = g_object_ref (skeleton); ctx->invocation = g_object_ref (invocation); ctx->self = g_object_ref (self); ctx->path = g_strdup (path); mm_base_modem_authorize (MM_BASE_MODEM (self), invocation, MM_AUTHORIZATION_VOICE, (GAsyncReadyCallback)handle_delete_auth_ready, ctx); return TRUE; } /*****************************************************************************/ typedef struct { MmGdbusModemVoice *skeleton; GDBusMethodInvocation *invocation; MMIfaceModemVoice *self; GVariant *dictionary; } HandleCreateContext; static void handle_create_context_free (HandleCreateContext *ctx) { g_object_unref (ctx->skeleton); g_object_unref (ctx->invocation); g_object_unref (ctx->self); g_variant_unref (ctx->dictionary); g_free (ctx); } static void handle_create_auth_ready (MMBaseModem *self, GAsyncResult *res, HandleCreateContext *ctx) { MMCallList *list = NULL; GError *error = NULL; MMCallProperties *properties; MMBaseCall *call; if (!mm_base_modem_authorize_finish (self, res, &error)) { mm_dbus_method_invocation_take_error (ctx->invocation, error); handle_create_context_free (ctx); return; } /* Parse input properties */ properties = mm_call_properties_new_from_dictionary (ctx->dictionary, &error); if (!properties) { mm_dbus_method_invocation_take_error (ctx->invocation, error); handle_create_context_free (ctx); return; } call = create_outgoing_call_from_properties (MM_IFACE_MODEM_VOICE (self), properties, &error); if (!call) { g_object_unref (properties); mm_dbus_method_invocation_take_error (ctx->invocation, error); handle_create_context_free (ctx); return; } g_object_get (self, MM_IFACE_MODEM_VOICE_CALL_LIST, &list, NULL); if (!list) { g_object_unref (properties); g_object_unref (call); mm_dbus_method_invocation_return_error_literal (ctx->invocation, MM_CORE_ERROR, MM_CORE_ERROR_WRONG_STATE, "Cannot create call: missing call list"); handle_create_context_free (ctx); return; } /* Only export once properly created */ mm_base_call_export (call); mm_call_list_add_call (list, call); /* Complete the DBus call */ mm_gdbus_modem_voice_complete_create_call (ctx->skeleton, ctx->invocation, mm_base_call_get_path (call)); g_object_unref (call); g_object_unref (properties); g_object_unref (list); handle_create_context_free (ctx); } static gboolean handle_create (MmGdbusModemVoice *skeleton, GDBusMethodInvocation *invocation, GVariant *dictionary, MMIfaceModemVoice *self) { HandleCreateContext *ctx; ctx = g_new (HandleCreateContext, 1); ctx->skeleton = g_object_ref (skeleton); ctx->invocation = g_object_ref (invocation); ctx->self = g_object_ref (self); ctx->dictionary = g_variant_ref (dictionary); mm_base_modem_authorize (MM_BASE_MODEM (self), invocation, MM_AUTHORIZATION_VOICE, (GAsyncReadyCallback)handle_create_auth_ready, ctx); return TRUE; } /*****************************************************************************/ static gboolean handle_list (MmGdbusModemVoice *skeleton, GDBusMethodInvocation *invocation, MMIfaceModemVoice *self) { GStrv paths; MMCallList *list = NULL; g_object_get (self, MM_IFACE_MODEM_VOICE_CALL_LIST, &list, NULL); if (!list) { mm_dbus_method_invocation_return_error_literal (invocation, MM_CORE_ERROR, MM_CORE_ERROR_WRONG_STATE, "Cannot list call: missing call list"); return TRUE; } paths = mm_call_list_get_paths (list); mm_gdbus_modem_voice_complete_list_calls (skeleton, invocation, (const gchar *const *)paths); g_strfreev (paths); g_object_unref (list); return TRUE; } /*****************************************************************************/ typedef struct { MmGdbusModemVoice *skeleton; GDBusMethodInvocation *invocation; MMIfaceModemVoice *self; GList *active_calls; MMBaseCall *next_call; } HandleHoldAndAcceptContext; static void handle_hold_and_accept_context_free (HandleHoldAndAcceptContext *ctx) { g_list_free_full (ctx->active_calls, g_object_unref); g_clear_object (&ctx->next_call); g_object_unref (ctx->skeleton); g_object_unref (ctx->invocation); g_object_unref (ctx->self); g_slice_free (HandleHoldAndAcceptContext, ctx); } static void hold_and_accept_ready (MMIfaceModemVoice *self, GAsyncResult *res, HandleHoldAndAcceptContext *ctx) { GError *error = NULL; GList *l; if (!MM_IFACE_MODEM_VOICE_GET_INTERFACE (self)->hold_and_accept_finish (self, res, &error)) { mm_dbus_method_invocation_take_error (ctx->invocation, error); handle_hold_and_accept_context_free (ctx); return; } for (l = ctx->active_calls; l; l = g_list_next (l)) mm_base_call_change_state (MM_BASE_CALL (l->data), MM_CALL_STATE_HELD, MM_CALL_STATE_REASON_UNKNOWN); if (ctx->next_call) mm_base_call_change_state (ctx->next_call, MM_CALL_STATE_ACTIVE, MM_CALL_STATE_REASON_ACCEPTED); mm_gdbus_modem_voice_complete_hold_and_accept (ctx->skeleton, ctx->invocation); handle_hold_and_accept_context_free (ctx); } static void prepare_hold_and_accept_foreach (MMBaseCall *call, HandleHoldAndAcceptContext *ctx) { switch (mm_base_call_get_state (call)) { case MM_CALL_STATE_ACTIVE: ctx->active_calls = g_list_append (ctx->active_calls, g_object_ref (call)); break; case MM_CALL_STATE_WAITING: g_clear_object (&ctx->next_call); ctx->next_call = g_object_ref (call); break; case MM_CALL_STATE_HELD: if (!ctx->next_call) ctx->next_call = g_object_ref (call); break; case MM_CALL_STATE_UNKNOWN: case MM_CALL_STATE_DIALING: case MM_CALL_STATE_RINGING_IN: case MM_CALL_STATE_RINGING_OUT: case MM_CALL_STATE_TERMINATED: default: break; } } static void handle_hold_and_accept_auth_ready (MMBaseModem *self, GAsyncResult *res, HandleHoldAndAcceptContext *ctx) { GError *error = NULL; MMCallList *list = NULL; if (!mm_base_modem_authorize_finish (self, res, &error)) { mm_dbus_method_invocation_take_error (ctx->invocation, error); handle_hold_and_accept_context_free (ctx); return; } if (!MM_IFACE_MODEM_VOICE_GET_INTERFACE (self)->hold_and_accept || !MM_IFACE_MODEM_VOICE_GET_INTERFACE (self)->hold_and_accept_finish) { mm_dbus_method_invocation_return_error_literal (ctx->invocation, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "Cannot hold and accept: unsupported"); handle_hold_and_accept_context_free (ctx); return; } g_object_get (self, MM_IFACE_MODEM_VOICE_CALL_LIST, &list, NULL); if (!list) { mm_dbus_method_invocation_return_error_literal (ctx->invocation, MM_CORE_ERROR, MM_CORE_ERROR_WRONG_STATE, "Cannot hold and accept: missing call list"); handle_hold_and_accept_context_free (ctx); return; } mm_call_list_foreach (list, (MMCallListForeachFunc)prepare_hold_and_accept_foreach, ctx); g_object_unref (list); MM_IFACE_MODEM_VOICE_GET_INTERFACE (self)->hold_and_accept (MM_IFACE_MODEM_VOICE (self), (GAsyncReadyCallback)hold_and_accept_ready, ctx); } static gboolean handle_hold_and_accept (MmGdbusModemVoice *skeleton, GDBusMethodInvocation *invocation, MMIfaceModemVoice *self) { HandleHoldAndAcceptContext *ctx; ctx = g_slice_new0 (HandleHoldAndAcceptContext); ctx->skeleton = g_object_ref (skeleton); ctx->invocation = g_object_ref (invocation); ctx->self = g_object_ref (self); mm_base_modem_authorize (MM_BASE_MODEM (self), invocation, MM_AUTHORIZATION_VOICE, (GAsyncReadyCallback)handle_hold_and_accept_auth_ready, ctx); return TRUE; } /*****************************************************************************/ typedef struct { MmGdbusModemVoice *skeleton; GDBusMethodInvocation *invocation; MMIfaceModemVoice *self; GList *active_calls; MMBaseCall *next_call; } HandleHangupAndAcceptContext; static void handle_hangup_and_accept_context_free (HandleHangupAndAcceptContext *ctx) { g_list_free_full (ctx->active_calls, g_object_unref); g_clear_object (&ctx->next_call); g_object_unref (ctx->skeleton); g_object_unref (ctx->invocation); g_object_unref (ctx->self); g_slice_free (HandleHangupAndAcceptContext, ctx); } static void hangup_and_accept_ready (MMIfaceModemVoice *self, GAsyncResult *res, HandleHangupAndAcceptContext *ctx) { GError *error = NULL; GList *l; if (!MM_IFACE_MODEM_VOICE_GET_INTERFACE (self)->hangup_and_accept_finish (self, res, &error)) { mm_dbus_method_invocation_take_error (ctx->invocation, error); handle_hangup_and_accept_context_free (ctx); return; } for (l = ctx->active_calls; l; l = g_list_next (l)) mm_base_call_change_state (MM_BASE_CALL (l->data), MM_CALL_STATE_TERMINATED, MM_CALL_STATE_REASON_TERMINATED); if (ctx->next_call) mm_base_call_change_state (ctx->next_call, MM_CALL_STATE_ACTIVE, MM_CALL_STATE_REASON_ACCEPTED); mm_gdbus_modem_voice_complete_hangup_and_accept (ctx->skeleton, ctx->invocation); handle_hangup_and_accept_context_free (ctx); } static void prepare_hangup_and_accept_foreach (MMBaseCall *call, HandleHangupAndAcceptContext *ctx) { switch (mm_base_call_get_state (call)) { case MM_CALL_STATE_ACTIVE: ctx->active_calls = g_list_append (ctx->active_calls, g_object_ref (call)); break; case MM_CALL_STATE_WAITING: g_clear_object (&ctx->next_call); ctx->next_call = g_object_ref (call); break; case MM_CALL_STATE_HELD: if (!ctx->next_call) ctx->next_call = g_object_ref (call); break; case MM_CALL_STATE_UNKNOWN: case MM_CALL_STATE_DIALING: case MM_CALL_STATE_RINGING_IN: case MM_CALL_STATE_RINGING_OUT: case MM_CALL_STATE_TERMINATED: default: break; } } static void handle_hangup_and_accept_auth_ready (MMBaseModem *self, GAsyncResult *res, HandleHangupAndAcceptContext *ctx) { GError *error = NULL; MMCallList *list = NULL; if (!mm_base_modem_authorize_finish (self, res, &error)) { mm_dbus_method_invocation_take_error (ctx->invocation, error); handle_hangup_and_accept_context_free (ctx); return; } if (!MM_IFACE_MODEM_VOICE_GET_INTERFACE (self)->hangup_and_accept || !MM_IFACE_MODEM_VOICE_GET_INTERFACE (self)->hangup_and_accept_finish) { mm_dbus_method_invocation_return_error_literal (ctx->invocation, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "Cannot hangup and accept: unsupported"); handle_hangup_and_accept_context_free (ctx); return; } g_object_get (self, MM_IFACE_MODEM_VOICE_CALL_LIST, &list, NULL); if (!list) { mm_dbus_method_invocation_return_error_literal (ctx->invocation, MM_CORE_ERROR, MM_CORE_ERROR_WRONG_STATE, "Cannot hangup and accept: missing call list"); handle_hangup_and_accept_context_free (ctx); return; } mm_call_list_foreach (list, (MMCallListForeachFunc)prepare_hangup_and_accept_foreach, ctx); g_object_unref (list); MM_IFACE_MODEM_VOICE_GET_INTERFACE (self)->hangup_and_accept (MM_IFACE_MODEM_VOICE (self), (GAsyncReadyCallback)hangup_and_accept_ready, ctx); } static gboolean handle_hangup_and_accept (MmGdbusModemVoice *skeleton, GDBusMethodInvocation *invocation, MMIfaceModemVoice *self) { HandleHangupAndAcceptContext *ctx; ctx = g_slice_new0 (HandleHangupAndAcceptContext); ctx->skeleton = g_object_ref (skeleton); ctx->invocation = g_object_ref (invocation); ctx->self = g_object_ref (self); mm_base_modem_authorize (MM_BASE_MODEM (self), invocation, MM_AUTHORIZATION_VOICE, (GAsyncReadyCallback)handle_hangup_and_accept_auth_ready, ctx); return TRUE; } /*****************************************************************************/ typedef struct { MmGdbusModemVoice *skeleton; GDBusMethodInvocation *invocation; MMIfaceModemVoice *self; GList *calls; } HandleHangupAllContext; static void handle_hangup_all_context_free (HandleHangupAllContext *ctx) { g_list_free_full (ctx->calls, g_object_unref); g_object_unref (ctx->skeleton); g_object_unref (ctx->invocation); g_object_unref (ctx->self); g_slice_free (HandleHangupAllContext, ctx); } static void hangup_all_ready (MMIfaceModemVoice *self, GAsyncResult *res, HandleHangupAllContext *ctx) { GError *error = NULL; GList *l; if (!MM_IFACE_MODEM_VOICE_GET_INTERFACE (self)->hangup_all_finish (self, res, &error)) { mm_dbus_method_invocation_take_error (ctx->invocation, error); handle_hangup_all_context_free (ctx); return; } for (l = ctx->calls; l; l = g_list_next (l)) mm_base_call_change_state (MM_BASE_CALL (l->data), MM_CALL_STATE_TERMINATED, MM_CALL_STATE_REASON_TERMINATED); mm_gdbus_modem_voice_complete_hangup_all (ctx->skeleton, ctx->invocation); handle_hangup_all_context_free (ctx); } static void prepare_hangup_all_foreach (MMBaseCall *call, HandleHangupAllContext *ctx) { /* The implementation of this operation will usually be done with +CHUP, and we * know that +CHUP is implemented in different ways by different manufacturers. * * The 3GPP TS27.007 spec for +CHUP states that the "Execution command causes * the TA to hangup the current call of the MT." This sentence leaves a bit of open * interpretation to the implementors, because a current call can be considered only * the active ones, or otherwise any call (active, held or waiting). * * And so, the u-blox TOBY-L4 takes one interpretation and "In case of multiple * calls, all active calls will be released, while waiting and held calls are not". * * And the Cinterion PLS-8 takes a different interpretation and cancels all calls, * including the waiting and held ones. * * In this logic, we're going to terminate exclusively the ACTIVE calls only, and we * will leave the possible termination of waiting/held calls to be reported via * call state updates, e.g. +CLCC polling or other plugin-specific method. In the * case of the Cinterion PLS-8, we'll detect the termination of the waiting and * held calls via ^SLCC URCs. */ switch (mm_base_call_get_state (call)) { case MM_CALL_STATE_DIALING: case MM_CALL_STATE_RINGING_OUT: case MM_CALL_STATE_RINGING_IN: case MM_CALL_STATE_ACTIVE: ctx->calls = g_list_append (ctx->calls, g_object_ref (call)); break; case MM_CALL_STATE_WAITING: case MM_CALL_STATE_HELD: case MM_CALL_STATE_UNKNOWN: case MM_CALL_STATE_TERMINATED: default: break; } } static void handle_hangup_all_auth_ready (MMBaseModem *self, GAsyncResult *res, HandleHangupAllContext *ctx) { GError *error = NULL; MMCallList *list = NULL; if (!mm_base_modem_authorize_finish (self, res, &error)) { mm_dbus_method_invocation_take_error (ctx->invocation, error); handle_hangup_all_context_free (ctx); return; } if (!MM_IFACE_MODEM_VOICE_GET_INTERFACE (self)->hangup_all || !MM_IFACE_MODEM_VOICE_GET_INTERFACE (self)->hangup_all_finish) { mm_dbus_method_invocation_return_error_literal (ctx->invocation, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "Cannot hangup all: unsupported"); handle_hangup_all_context_free (ctx); return; } g_object_get (self, MM_IFACE_MODEM_VOICE_CALL_LIST, &list, NULL); if (!list) { mm_dbus_method_invocation_return_error_literal (ctx->invocation, MM_CORE_ERROR, MM_CORE_ERROR_WRONG_STATE, "Cannot hangup all: missing call list"); handle_hangup_all_context_free (ctx); return; } mm_call_list_foreach (list, (MMCallListForeachFunc)prepare_hangup_all_foreach, ctx); g_object_unref (list); MM_IFACE_MODEM_VOICE_GET_INTERFACE (self)->hangup_all (MM_IFACE_MODEM_VOICE (self), (GAsyncReadyCallback)hangup_all_ready, ctx); } static gboolean handle_hangup_all (MmGdbusModemVoice *skeleton, GDBusMethodInvocation *invocation, MMIfaceModemVoice *self) { HandleHangupAllContext *ctx; ctx = g_slice_new0 (HandleHangupAllContext); ctx->skeleton = g_object_ref (skeleton); ctx->invocation = g_object_ref (invocation); ctx->self = g_object_ref (self); mm_base_modem_authorize (MM_BASE_MODEM (self), invocation, MM_AUTHORIZATION_VOICE, (GAsyncReadyCallback)handle_hangup_all_auth_ready, ctx); return TRUE; } /*****************************************************************************/ typedef struct { MmGdbusModemVoice *skeleton; GDBusMethodInvocation *invocation; MMIfaceModemVoice *self; GList *calls; } HandleTransferContext; static void handle_transfer_context_free (HandleTransferContext *ctx) { g_list_free_full (ctx->calls, g_object_unref); g_object_unref (ctx->skeleton); g_object_unref (ctx->invocation); g_object_unref (ctx->self); g_slice_free (HandleTransferContext, ctx); } static void transfer_ready (MMIfaceModemVoice *self, GAsyncResult *res, HandleTransferContext *ctx) { GError *error = NULL; GList *l; if (!MM_IFACE_MODEM_VOICE_GET_INTERFACE (self)->transfer_finish (self, res, &error)) { mm_dbus_method_invocation_take_error (ctx->invocation, error); handle_transfer_context_free (ctx); return; } for (l = ctx->calls; l; l = g_list_next (l)) mm_base_call_change_state (MM_BASE_CALL (l->data), MM_CALL_STATE_TERMINATED, MM_CALL_STATE_REASON_TRANSFERRED); mm_gdbus_modem_voice_complete_transfer (ctx->skeleton, ctx->invocation); handle_transfer_context_free (ctx); } static void prepare_transfer_foreach (MMBaseCall *call, HandleTransferContext *ctx) { switch (mm_base_call_get_state (call)) { case MM_CALL_STATE_ACTIVE: case MM_CALL_STATE_HELD: ctx->calls = g_list_append (ctx->calls, g_object_ref (call)); break; case MM_CALL_STATE_UNKNOWN: case MM_CALL_STATE_DIALING: case MM_CALL_STATE_WAITING: case MM_CALL_STATE_RINGING_IN: case MM_CALL_STATE_RINGING_OUT: case MM_CALL_STATE_TERMINATED: default: break; } } static void handle_transfer_auth_ready (MMBaseModem *self, GAsyncResult *res, HandleTransferContext *ctx) { GError *error = NULL; MMCallList *list = NULL; if (!mm_base_modem_authorize_finish (self, res, &error)) { mm_dbus_method_invocation_take_error (ctx->invocation, error); handle_transfer_context_free (ctx); return; } if (!MM_IFACE_MODEM_VOICE_GET_INTERFACE (self)->transfer || !MM_IFACE_MODEM_VOICE_GET_INTERFACE (self)->transfer_finish) { mm_dbus_method_invocation_return_error_literal (ctx->invocation, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "Cannot transfer: unsupported"); handle_transfer_context_free (ctx); return; } g_object_get (self, MM_IFACE_MODEM_VOICE_CALL_LIST, &list, NULL); if (!list) { mm_dbus_method_invocation_return_error_literal (ctx->invocation, MM_CORE_ERROR, MM_CORE_ERROR_WRONG_STATE, "Cannot transfer: missing call list"); handle_transfer_context_free (ctx); return; } mm_call_list_foreach (list, (MMCallListForeachFunc)prepare_transfer_foreach, ctx); g_object_unref (list); MM_IFACE_MODEM_VOICE_GET_INTERFACE (self)->transfer (MM_IFACE_MODEM_VOICE (self), (GAsyncReadyCallback)transfer_ready, ctx); } static gboolean handle_transfer (MmGdbusModemVoice *skeleton, GDBusMethodInvocation *invocation, MMIfaceModemVoice *self) { HandleTransferContext *ctx; ctx = g_slice_new0 (HandleTransferContext); ctx->skeleton = g_object_ref (skeleton); ctx->invocation = g_object_ref (invocation); ctx->self = g_object_ref (self); mm_base_modem_authorize (MM_BASE_MODEM (self), invocation, MM_AUTHORIZATION_VOICE, (GAsyncReadyCallback)handle_transfer_auth_ready, ctx); return TRUE; } /*****************************************************************************/ typedef struct { MmGdbusModemVoice *skeleton; GDBusMethodInvocation *invocation; MMIfaceModemVoice *self; gboolean enable; } HandleCallWaitingSetupContext; static void handle_call_waiting_setup_context_free (HandleCallWaitingSetupContext *ctx) { g_object_unref (ctx->skeleton); g_object_unref (ctx->invocation); g_object_unref (ctx->self); g_slice_free (HandleCallWaitingSetupContext, ctx); } static void call_waiting_setup_ready (MMIfaceModemVoice *self, GAsyncResult *res, HandleCallWaitingSetupContext *ctx) { GError *error = NULL; if (!MM_IFACE_MODEM_VOICE_GET_INTERFACE (self)->call_waiting_setup_finish (self, res, &error)) { mm_dbus_method_invocation_take_error (ctx->invocation, error); handle_call_waiting_setup_context_free (ctx); return; } mm_gdbus_modem_voice_complete_call_waiting_setup (ctx->skeleton, ctx->invocation); handle_call_waiting_setup_context_free (ctx); } static void handle_call_waiting_setup_auth_ready (MMBaseModem *self, GAsyncResult *res, HandleCallWaitingSetupContext *ctx) { GError *error = NULL; if (!mm_base_modem_authorize_finish (self, res, &error)) { mm_dbus_method_invocation_take_error (ctx->invocation, error); handle_call_waiting_setup_context_free (ctx); return; } if (!MM_IFACE_MODEM_VOICE_GET_INTERFACE (self)->call_waiting_setup || !MM_IFACE_MODEM_VOICE_GET_INTERFACE (self)->call_waiting_setup_finish) { mm_dbus_method_invocation_return_error_literal (ctx->invocation, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "Cannot setup call waiting: unsupported"); handle_call_waiting_setup_context_free (ctx); return; } MM_IFACE_MODEM_VOICE_GET_INTERFACE (self)->call_waiting_setup (MM_IFACE_MODEM_VOICE (self), ctx->enable, (GAsyncReadyCallback)call_waiting_setup_ready, ctx); } static gboolean handle_call_waiting_setup (MmGdbusModemVoice *skeleton, GDBusMethodInvocation *invocation, gboolean enable, MMIfaceModemVoice *self) { HandleCallWaitingSetupContext *ctx; ctx = g_slice_new0 (HandleCallWaitingSetupContext); ctx->skeleton = g_object_ref (skeleton); ctx->invocation = g_object_ref (invocation); ctx->self = g_object_ref (self); ctx->enable = enable; mm_base_modem_authorize (MM_BASE_MODEM (self), invocation, MM_AUTHORIZATION_VOICE, (GAsyncReadyCallback)handle_call_waiting_setup_auth_ready, ctx); return TRUE; } /*****************************************************************************/ typedef struct { MmGdbusModemVoice *skeleton; GDBusMethodInvocation *invocation; MMIfaceModemVoice *self; gboolean enable; } HandleCallWaitingQueryContext; static void handle_call_waiting_query_context_free (HandleCallWaitingQueryContext *ctx) { g_object_unref (ctx->skeleton); g_object_unref (ctx->invocation); g_object_unref (ctx->self); g_slice_free (HandleCallWaitingQueryContext, ctx); } static void call_waiting_query_ready (MMIfaceModemVoice *self, GAsyncResult *res, HandleCallWaitingQueryContext *ctx) { GError *error = NULL; gboolean status = FALSE; if (!MM_IFACE_MODEM_VOICE_GET_INTERFACE (self)->call_waiting_query_finish (self, res, &status, &error)) { mm_dbus_method_invocation_take_error (ctx->invocation, error); handle_call_waiting_query_context_free (ctx); return; } mm_gdbus_modem_voice_complete_call_waiting_query (ctx->skeleton, ctx->invocation, status); handle_call_waiting_query_context_free (ctx); } static void handle_call_waiting_query_auth_ready (MMBaseModem *self, GAsyncResult *res, HandleCallWaitingQueryContext *ctx) { GError *error = NULL; if (!mm_base_modem_authorize_finish (self, res, &error)) { mm_dbus_method_invocation_take_error (ctx->invocation, error); handle_call_waiting_query_context_free (ctx); return; } if (!MM_IFACE_MODEM_VOICE_GET_INTERFACE (self)->call_waiting_query || !MM_IFACE_MODEM_VOICE_GET_INTERFACE (self)->call_waiting_query_finish) { mm_dbus_method_invocation_return_error_literal (ctx->invocation, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "Cannot query call waiting: unsupported"); handle_call_waiting_query_context_free (ctx); return; } MM_IFACE_MODEM_VOICE_GET_INTERFACE (self)->call_waiting_query (MM_IFACE_MODEM_VOICE (self), (GAsyncReadyCallback)call_waiting_query_ready, ctx); } static gboolean handle_call_waiting_query (MmGdbusModemVoice *skeleton, GDBusMethodInvocation *invocation, MMIfaceModemVoice *self) { HandleCallWaitingQueryContext *ctx; ctx = g_slice_new0 (HandleCallWaitingQueryContext); ctx->skeleton = g_object_ref (skeleton); ctx->invocation = g_object_ref (invocation); ctx->self = g_object_ref (self); mm_base_modem_authorize (MM_BASE_MODEM (self), invocation, MM_AUTHORIZATION_VOICE, (GAsyncReadyCallback)handle_call_waiting_query_auth_ready, ctx); return TRUE; } /*****************************************************************************/ /* Leave one of the calls from the multiparty call */ typedef struct { MMBaseCall *call; GList *other_calls; } LeaveMultipartyContext; static void leave_multiparty_context_free (LeaveMultipartyContext *ctx) { g_list_free_full (ctx->other_calls, g_object_unref); g_object_unref (ctx->call); g_slice_free (LeaveMultipartyContext, ctx); } static void prepare_leave_multiparty_foreach (MMBaseCall *call, LeaveMultipartyContext *ctx) { /* ignore call that is leaving */ if ((call == ctx->call) || (g_strcmp0 (mm_base_call_get_path (call), mm_base_call_get_path (ctx->call)) == 0)) return; /* ignore non-multiparty calls */ if (!mm_base_call_get_multiparty (call)) return; /* ignore calls not currently ongoing */ switch (mm_base_call_get_state (call)) { case MM_CALL_STATE_ACTIVE: case MM_CALL_STATE_HELD: ctx->other_calls = g_list_append (ctx->other_calls, g_object_ref (call)); break; case MM_CALL_STATE_UNKNOWN: case MM_CALL_STATE_DIALING: case MM_CALL_STATE_WAITING: case MM_CALL_STATE_RINGING_IN: case MM_CALL_STATE_RINGING_OUT: case MM_CALL_STATE_TERMINATED: default: break; } } gboolean mm_iface_modem_voice_leave_multiparty_finish (MMIfaceModemVoice *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void leave_multiparty_ready (MMIfaceModemVoice *self, GAsyncResult *res, GTask *task) { GError *error = NULL; LeaveMultipartyContext *ctx; ctx = g_task_get_task_data (task); if (!MM_IFACE_MODEM_VOICE_GET_INTERFACE (self)->leave_multiparty_finish (self, res, &error)) { g_task_return_error (task, error); g_object_unref (task); return; } /* If there is only one remaining call that was part of the multiparty, consider that * one also no longer part of any multiparty, and put it on hold right away */ if (g_list_length (ctx->other_calls) == 1) { mm_base_call_set_multiparty (MM_BASE_CALL (ctx->other_calls->data), FALSE); mm_base_call_change_state (MM_BASE_CALL (ctx->other_calls->data), MM_CALL_STATE_HELD, MM_CALL_STATE_REASON_UNKNOWN); } /* If there are still more than one calls in the multiparty, just change state of all * of them. */ else { GList *l; for (l = ctx->other_calls; l; l = g_list_next (l)) mm_base_call_change_state (MM_BASE_CALL (l->data), MM_CALL_STATE_HELD, MM_CALL_STATE_REASON_UNKNOWN); } /* The call that left would now be active */ mm_base_call_set_multiparty (ctx->call, FALSE); mm_base_call_change_state (ctx->call, MM_CALL_STATE_ACTIVE, MM_CALL_STATE_REASON_UNKNOWN); g_task_return_boolean (task, TRUE); g_object_unref (task); } void mm_iface_modem_voice_leave_multiparty (MMIfaceModemVoice *self, MMBaseCall *call, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; LeaveMultipartyContext *ctx; MMCallList *list = NULL; MMCallState call_state; task = g_task_new (self, NULL, callback, user_data); /* validate multiparty status */ if (!mm_base_call_get_multiparty (call)) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_WRONG_STATE, "this call is not part of a multiparty call"); g_object_unref (task); return; } /* validate call state */ call_state = mm_base_call_get_state (call); if ((call_state != MM_CALL_STATE_ACTIVE) && (call_state != MM_CALL_STATE_HELD)) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_WRONG_STATE, "invalid call state (%s): must be either active or held", mm_call_state_get_string (call_state)); g_object_unref (task); return; } if (!MM_IFACE_MODEM_VOICE_GET_INTERFACE (self)->leave_multiparty || !MM_IFACE_MODEM_VOICE_GET_INTERFACE (self)->leave_multiparty_finish) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "Cannot leave multiparty: unsupported"); g_object_unref (task); return; } g_object_get (self, MM_IFACE_MODEM_VOICE_CALL_LIST, &list, NULL); if (!list) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_WRONG_STATE, "Cannot leave multiparty: missing call list"); g_object_unref (task); return; } ctx = g_slice_new0 (LeaveMultipartyContext); ctx->call = g_object_ref (call); g_task_set_task_data (task, ctx, (GDestroyNotify) leave_multiparty_context_free); mm_call_list_foreach (list, (MMCallListForeachFunc)prepare_leave_multiparty_foreach, ctx); g_object_unref (list); MM_IFACE_MODEM_VOICE_GET_INTERFACE (self)->leave_multiparty (self, call, (GAsyncReadyCallback)leave_multiparty_ready, task); } /*****************************************************************************/ /* Join calls into a multiparty call */ typedef struct { MMBaseCall *call; GList *all_calls; gboolean added; } JoinMultipartyContext; static void join_multiparty_context_free (JoinMultipartyContext *ctx) { g_list_free_full (ctx->all_calls, g_object_unref); g_object_unref (ctx->call); g_slice_free (JoinMultipartyContext, ctx); } static void prepare_join_multiparty_foreach (MMBaseCall *call, JoinMultipartyContext *ctx) { /* always add call that is being added */ if ((call == ctx->call) || (g_strcmp0 (mm_base_call_get_path (call), mm_base_call_get_path (ctx->call)) == 0)) ctx->added = TRUE; /* ignore calls not currently ongoing */ switch (mm_base_call_get_state (call)) { case MM_CALL_STATE_ACTIVE: case MM_CALL_STATE_HELD: ctx->all_calls = g_list_append (ctx->all_calls, g_object_ref (call)); break; case MM_CALL_STATE_UNKNOWN: case MM_CALL_STATE_DIALING: case MM_CALL_STATE_RINGING_IN: case MM_CALL_STATE_RINGING_OUT: case MM_CALL_STATE_WAITING: case MM_CALL_STATE_TERMINATED: default: break; } } gboolean mm_iface_modem_voice_join_multiparty_finish (MMIfaceModemVoice *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void join_multiparty_ready (MMIfaceModemVoice *self, GAsyncResult *res, GTask *task) { GError *error = NULL; JoinMultipartyContext *ctx; GList *l; ctx = g_task_get_task_data (task); if (!MM_IFACE_MODEM_VOICE_GET_INTERFACE (self)->join_multiparty_finish (self, res, &error)) { g_task_return_error (task, error); g_object_unref (task); return; } for (l = ctx->all_calls; l; l = g_list_next (l)) { mm_base_call_set_multiparty (MM_BASE_CALL (l->data), TRUE); mm_base_call_change_state (MM_BASE_CALL (l->data), MM_CALL_STATE_ACTIVE, MM_CALL_STATE_REASON_UNKNOWN); } g_task_return_boolean (task, TRUE); g_object_unref (task); } void mm_iface_modem_voice_join_multiparty (MMIfaceModemVoice *self, MMBaseCall *call, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; JoinMultipartyContext *ctx; MMCallList *list = NULL; MMCallState call_state; task = g_task_new (self, NULL, callback, user_data); /* validate multiparty status */ if (mm_base_call_get_multiparty (call)) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_WRONG_STATE, "this call is already part of a multiparty call"); g_object_unref (task); return; } /* validate call state */ call_state = mm_base_call_get_state (call); if (call_state != MM_CALL_STATE_HELD) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_WRONG_STATE, "invalid call state (%s): must be held", mm_call_state_get_string (call_state)); g_object_unref (task); return; } if (!MM_IFACE_MODEM_VOICE_GET_INTERFACE (self)->join_multiparty || !MM_IFACE_MODEM_VOICE_GET_INTERFACE (self)->join_multiparty_finish) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "Cannot join multiparty: unsupported"); g_object_unref (task); return; } g_object_get (self, MM_IFACE_MODEM_VOICE_CALL_LIST, &list, NULL); if (!list) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_WRONG_STATE, "Cannot join multiparty: missing call list"); g_object_unref (task); return; } ctx = g_slice_new0 (JoinMultipartyContext); ctx->call = g_object_ref (call); g_task_set_task_data (task, ctx, (GDestroyNotify) join_multiparty_context_free); mm_call_list_foreach (list, (MMCallListForeachFunc)prepare_join_multiparty_foreach, ctx); g_object_unref (list); /* our logic makes sure that we would be adding the incoming call into the multiparty call */ g_assert (ctx->added); /* NOTE: we do not give the call we want to join, because the join operation acts on all * active/held calls. */ MM_IFACE_MODEM_VOICE_GET_INTERFACE (self)->join_multiparty (self, (GAsyncReadyCallback)join_multiparty_ready, task); } /*****************************************************************************/ /* In-call setup operation * * It will setup URC handlers for all in-call URCs, and also setup the audio * channel if the plugin requires to do so. */ typedef enum { IN_CALL_SETUP_STEP_FIRST, IN_CALL_SETUP_STEP_UNSOLICITED_EVENTS, IN_CALL_SETUP_STEP_AUDIO_CHANNEL, IN_CALL_SETUP_STEP_LAST, } InCallSetupStep; typedef struct { InCallSetupStep step; MMPort *audio_port; MMCallAudioFormat *audio_format; } InCallSetupContext; static void in_call_setup_context_free (InCallSetupContext *ctx) { g_clear_object (&ctx->audio_port); g_clear_object (&ctx->audio_format); g_slice_free (InCallSetupContext, ctx); } static gboolean in_call_setup_finish (MMIfaceModemVoice *self, GAsyncResult *res, MMPort **audio_port, /* optional */ MMCallAudioFormat **audio_format, /* optional */ GError **error) { InCallSetupContext *ctx; if (!g_task_propagate_boolean (G_TASK (res), error)) return FALSE; ctx = g_task_get_task_data (G_TASK (res)); if (audio_port) { *audio_port = ctx->audio_port; ctx->audio_port = NULL; } if (audio_format) { *audio_format = ctx->audio_format; ctx->audio_format = NULL; } return TRUE; } static void in_call_setup_context_step (GTask *task); static void setup_in_call_audio_channel_ready (MMIfaceModemVoice *self, GAsyncResult *res, GTask *task) { InCallSetupContext *ctx; GError *error = NULL; ctx = g_task_get_task_data (task); if (!MM_IFACE_MODEM_VOICE_GET_INTERFACE (self)->setup_in_call_audio_channel_finish (self, res, &ctx->audio_port, &ctx->audio_format, &error)) { mm_obj_warn (self, "couldn't setup in-call audio channel: %s", error->message); g_clear_error (&error); } ctx->step++; in_call_setup_context_step (task); } static void setup_in_call_unsolicited_events_ready (MMIfaceModemVoice *self, GAsyncResult *res, GTask *task) { InCallSetupContext *ctx; GError *error = NULL; ctx = g_task_get_task_data (task); if (!MM_IFACE_MODEM_VOICE_GET_INTERFACE (self)->setup_in_call_unsolicited_events_finish (self, res, &error)) { mm_obj_warn (self, "couldn't setup in-call unsolicited events: %s", error->message); g_clear_error (&error); } ctx->step++; in_call_setup_context_step (task); } static void in_call_setup_context_step (GTask *task) { MMIfaceModemVoice *self; InCallSetupContext *ctx; if (g_task_return_error_if_cancelled (task)) { g_object_unref (task); return; } self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); switch (ctx->step) { case IN_CALL_SETUP_STEP_FIRST: ctx->step++; /* fall-through */ case IN_CALL_SETUP_STEP_UNSOLICITED_EVENTS: if (MM_IFACE_MODEM_VOICE_GET_INTERFACE (self)->setup_in_call_unsolicited_events && MM_IFACE_MODEM_VOICE_GET_INTERFACE (self)->setup_in_call_unsolicited_events_finish) { MM_IFACE_MODEM_VOICE_GET_INTERFACE (self)->setup_in_call_unsolicited_events ( self, (GAsyncReadyCallback) setup_in_call_unsolicited_events_ready, task); return; } ctx->step++; /* fall-through */ case IN_CALL_SETUP_STEP_AUDIO_CHANNEL: if (MM_IFACE_MODEM_VOICE_GET_INTERFACE (self)->setup_in_call_audio_channel && MM_IFACE_MODEM_VOICE_GET_INTERFACE (self)->setup_in_call_audio_channel_finish) { MM_IFACE_MODEM_VOICE_GET_INTERFACE (self)->setup_in_call_audio_channel ( self, (GAsyncReadyCallback) setup_in_call_audio_channel_ready, task); return; } ctx->step++; /* fall-through */ case IN_CALL_SETUP_STEP_LAST: g_task_return_boolean (task, TRUE); g_object_unref (task); return; default: break; } g_assert_not_reached (); } static void in_call_setup (MMIfaceModemVoice *self, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; InCallSetupContext *ctx; ctx = g_slice_new0 (InCallSetupContext); ctx->step = IN_CALL_SETUP_STEP_FIRST; task = g_task_new (self, cancellable, callback, user_data); g_task_set_task_data (task, ctx, (GDestroyNotify) in_call_setup_context_free); in_call_setup_context_step (task); } /*****************************************************************************/ /* In-call cleanup operation * * It will cleanup audio channel settings and remove all in-call URC handlers. */ typedef enum { IN_CALL_CLEANUP_STEP_FIRST, IN_CALL_CLEANUP_STEP_AUDIO_CHANNEL, IN_CALL_CLEANUP_STEP_UNSOLICITED_EVENTS, IN_CALL_CLEANUP_STEP_LAST, } InCallCleanupStep; typedef struct { InCallCleanupStep step; } InCallCleanupContext; static gboolean in_call_cleanup_finish (MMIfaceModemVoice *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void in_call_cleanup_context_step (GTask *task); static void cleanup_in_call_unsolicited_events_ready (MMIfaceModemVoice *self, GAsyncResult *res, GTask *task) { InCallCleanupContext *ctx; GError *error = NULL; ctx = g_task_get_task_data (task); if (!MM_IFACE_MODEM_VOICE_GET_INTERFACE (self)->cleanup_in_call_unsolicited_events_finish (self, res, &error)) { mm_obj_warn (self, "couldn't cleanup in-call unsolicited events: %s", error->message); g_clear_error (&error); } ctx->step++; in_call_cleanup_context_step (task); } static void cleanup_in_call_audio_channel_ready (MMIfaceModemVoice *self, GAsyncResult *res, GTask *task) { InCallCleanupContext *ctx; GError *error = NULL; ctx = g_task_get_task_data (task); if (!MM_IFACE_MODEM_VOICE_GET_INTERFACE (self)->cleanup_in_call_audio_channel_finish (self, res, &error)) { mm_obj_warn (self, "couldn't cleanup in-call audio channel: %s", error->message); g_clear_error (&error); } ctx->step++; in_call_cleanup_context_step (task); } static void in_call_cleanup_context_step (GTask *task) { MMIfaceModemVoice *self; InCallCleanupContext *ctx; if (g_task_return_error_if_cancelled (task)) { g_object_unref (task); return; } self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); switch (ctx->step) { case IN_CALL_CLEANUP_STEP_FIRST: ctx->step++; /* fall-through */ case IN_CALL_CLEANUP_STEP_AUDIO_CHANNEL: if (MM_IFACE_MODEM_VOICE_GET_INTERFACE (self)->cleanup_in_call_audio_channel && MM_IFACE_MODEM_VOICE_GET_INTERFACE (self)->cleanup_in_call_audio_channel_finish) { MM_IFACE_MODEM_VOICE_GET_INTERFACE (self)->cleanup_in_call_audio_channel ( self, (GAsyncReadyCallback) cleanup_in_call_audio_channel_ready, task); return; } ctx->step++; /* fall-through */ case IN_CALL_CLEANUP_STEP_UNSOLICITED_EVENTS: if (MM_IFACE_MODEM_VOICE_GET_INTERFACE (self)->cleanup_in_call_unsolicited_events && MM_IFACE_MODEM_VOICE_GET_INTERFACE (self)->cleanup_in_call_unsolicited_events_finish) { MM_IFACE_MODEM_VOICE_GET_INTERFACE (self)->cleanup_in_call_unsolicited_events ( self, (GAsyncReadyCallback) cleanup_in_call_unsolicited_events_ready, task); return; } ctx->step++; /* fall-through */ case IN_CALL_CLEANUP_STEP_LAST: g_task_return_boolean (task, TRUE); g_object_unref (task); return; default: break; } g_assert_not_reached (); } static void in_call_cleanup (MMIfaceModemVoice *self, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; InCallCleanupContext *ctx; ctx = g_new0 (InCallCleanupContext, 1); ctx->step = IN_CALL_CLEANUP_STEP_FIRST; task = g_task_new (self, cancellable, callback, user_data); g_task_set_task_data (task, ctx, g_free); in_call_cleanup_context_step (task); } /*****************************************************************************/ /* In-call event handling logic * * This procedure will run a in-call setup async function whenever we detect * that there is at least one call that is ongoing. This setup function will * try to setup in-call unsolicited events as well as any audio channel * requirements. * * The procedure will run a in-call cleanup async function whenever we detect * that there are no longer any ongoing calls. The cleanup function will * cleanup the audio channel and remove the in-call unsolicited event handlers. */ typedef struct { guint check_id; GCancellable *setup_cancellable; GCancellable *cleanup_cancellable; gboolean in_call_state; MMPort *audio_port; MMCallAudioFormat *audio_format; } InCallEventContext; static void in_call_event_context_free (InCallEventContext *ctx) { if (ctx->check_id) g_source_remove (ctx->check_id); if (ctx->cleanup_cancellable) { g_cancellable_cancel (ctx->cleanup_cancellable); g_clear_object (&ctx->cleanup_cancellable); } if (ctx->setup_cancellable) { g_cancellable_cancel (ctx->setup_cancellable); g_clear_object (&ctx->setup_cancellable); } g_clear_object (&ctx->audio_port); g_clear_object (&ctx->audio_format); g_slice_free (InCallEventContext, ctx); } static InCallEventContext * get_in_call_event_context (MMIfaceModemVoice *self) { InCallEventContext *ctx; if (G_UNLIKELY (!in_call_event_context_quark)) in_call_event_context_quark = g_quark_from_static_string (IN_CALL_EVENT_CONTEXT_TAG); ctx = g_object_get_qdata (G_OBJECT (self), in_call_event_context_quark); if (!ctx) { /* Create context and keep it as object data */ ctx = g_slice_new0 (InCallEventContext); g_object_set_qdata_full ( G_OBJECT (self), in_call_event_context_quark, ctx, (GDestroyNotify)in_call_event_context_free); } return ctx; } static void call_list_foreach_audio_settings (MMBaseCall *call, InCallEventContext *ctx) { if (mm_base_call_get_state (call) != MM_CALL_STATE_TERMINATED) return; mm_base_call_change_audio_settings (call, ctx->audio_port, ctx->audio_format); } static void update_audio_settings_in_ongoing_calls (MMIfaceModemVoice *self) { MMCallList *list = NULL; InCallEventContext *ctx; ctx = get_in_call_event_context (self); g_object_get (MM_BASE_MODEM (self), MM_IFACE_MODEM_VOICE_CALL_LIST, &list, NULL); if (!list) { mm_obj_warn (self, "cannot update audio settings in active calls: missing internal call list"); return; } mm_call_list_foreach (list, (MMCallListForeachFunc) call_list_foreach_audio_settings, ctx); g_clear_object (&list); } static void update_audio_settings_in_call (MMIfaceModemVoice *self, MMBaseCall *call) { InCallEventContext *ctx; ctx = get_in_call_event_context (self); mm_base_call_change_audio_settings (call, ctx->audio_port, ctx->audio_format); } static void call_list_foreach_count_in_call (MMBaseCall *call, gpointer user_data) { guint *n_calls_in_call = (guint *)user_data; switch (mm_base_call_get_state (call)) { case MM_CALL_STATE_DIALING: case MM_CALL_STATE_RINGING_OUT: case MM_CALL_STATE_HELD: case MM_CALL_STATE_ACTIVE: *n_calls_in_call = *n_calls_in_call + 1; break; case MM_CALL_STATE_RINGING_IN: case MM_CALL_STATE_WAITING: /* NOTE: ringing-in and waiting calls are NOT yet in-call, e.g. there must * be no audio settings enabled and we must not enable in-call URC handling * yet. */ case MM_CALL_STATE_UNKNOWN: case MM_CALL_STATE_TERMINATED: default: break; } } static void in_call_cleanup_ready (MMIfaceModemVoice *self, GAsyncResult *res) { GError *error = NULL; InCallEventContext *ctx; ctx = get_in_call_event_context (self); if (!in_call_cleanup_finish (self, res, &error)) { /* ignore cancelled operations */ if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) mm_obj_warn (self, "cannot cleanup in-call modem state: %s", error->message); g_clear_error (&error); } else { mm_obj_dbg (self, "modem is no longer in-call state"); ctx->in_call_state = FALSE; g_clear_object (&ctx->audio_port); g_clear_object (&ctx->audio_format); } g_clear_object (&ctx->cleanup_cancellable); } static void in_call_setup_ready (MMIfaceModemVoice *self, GAsyncResult *res) { GError *error = NULL; InCallEventContext *ctx; ctx = get_in_call_event_context (self); if (!in_call_setup_finish (self, res, &ctx->audio_port, &ctx->audio_format, &error)) { /* ignore cancelled operations */ if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) mm_obj_warn (self, "cannot setup in-call modem state: %s", error->message); g_clear_error (&error); } else { mm_obj_dbg (self, "modem is now in-call state"); ctx->in_call_state = TRUE; update_audio_settings_in_ongoing_calls (self); } g_clear_object (&ctx->setup_cancellable); } static gboolean call_list_check_in_call_events (MMIfaceModemVoice *self) { InCallEventContext *ctx; MMCallList *list = NULL; guint n_calls_in_call = 0; ctx = get_in_call_event_context (self); ctx->check_id = 0; g_object_get (MM_BASE_MODEM (self), MM_IFACE_MODEM_VOICE_CALL_LIST, &list, NULL); if (!list) { mm_obj_warn (self, "cannot update in-call state: missing internal call list"); goto out; } mm_call_list_foreach (list, (MMCallListForeachFunc) call_list_foreach_count_in_call, &n_calls_in_call); /* Need to setup in-call events? */ if (n_calls_in_call > 0 && !ctx->in_call_state) { /* if setup already ongoing, do nothing */ if (ctx->setup_cancellable) goto out; /* cancel ongoing cleanup if any */ if (ctx->cleanup_cancellable) { g_cancellable_cancel (ctx->cleanup_cancellable); g_clear_object (&ctx->cleanup_cancellable); } /* run setup */ mm_obj_dbg (self, "setting up in-call state..."); ctx->setup_cancellable = g_cancellable_new (); in_call_setup (self, ctx->setup_cancellable, (GAsyncReadyCallback) in_call_setup_ready, NULL); goto out; } /* Need to cleanup in-call events? */ if (n_calls_in_call == 0 && ctx->in_call_state) { /* if cleanup already ongoing, do nothing */ if (ctx->cleanup_cancellable) goto out; /* cancel ongoing setup if any */ if (ctx->setup_cancellable) { g_cancellable_cancel (ctx->setup_cancellable); g_clear_object (&ctx->setup_cancellable); } /* run cleanup */ mm_obj_dbg (self, "cleaning up in-call state..."); ctx->cleanup_cancellable = g_cancellable_new (); in_call_cleanup (self, ctx->cleanup_cancellable, (GAsyncReadyCallback) in_call_cleanup_ready, NULL); goto out; } out: g_clear_object (&list); return G_SOURCE_REMOVE; } static void call_state_changed (MMIfaceModemVoice *self) { InCallEventContext *ctx; ctx = get_in_call_event_context (self); if (ctx->check_id) return; /* Process check for in-call events in an idle, so that we can combine * together in the same check multiple call state updates happening * at the same time for different calls (e.g. when swapping active/held * calls). */ ctx->check_id = g_idle_add ((GSourceFunc)call_list_check_in_call_events, self); } static void setup_in_call_event_handling (MMCallList *call_list, const gchar *call_path_added, MMIfaceModemVoice *self) { MMBaseCall *call; call = mm_call_list_get_call (call_list, call_path_added); g_assert (call); g_signal_connect_swapped (call, "state-changed", G_CALLBACK (call_state_changed), self); } /*****************************************************************************/ /* Call list polling logic * * The call list polling is exclusively used to detect detailed call state * updates while a call is being established. Therefore, if there is no call * being established (i.e. all terminated, unknown or active), then there is * no polling to do. * * Any time we add a new call to the list, we'll setup polling if it's not * already running, and the polling logic itself will decide when the polling * should stop. */ #define CALL_LIST_POLLING_TIMEOUT_SECS 2 typedef struct { guint polling_id; gboolean polling_ongoing; } CallListPollingContext; static void call_list_polling_context_free (CallListPollingContext *ctx) { if (ctx->polling_id) g_source_remove (ctx->polling_id); g_slice_free (CallListPollingContext, ctx); } static CallListPollingContext * get_call_list_polling_context (MMIfaceModemVoice *self) { CallListPollingContext *ctx; if (G_UNLIKELY (!call_list_polling_context_quark)) call_list_polling_context_quark = (g_quark_from_static_string ( CALL_LIST_POLLING_CONTEXT_TAG)); ctx = g_object_get_qdata (G_OBJECT (self), call_list_polling_context_quark); if (!ctx) { /* Create context and keep it as object data */ ctx = g_slice_new0 (CallListPollingContext); g_object_set_qdata_full ( G_OBJECT (self), call_list_polling_context_quark, ctx, (GDestroyNotify)call_list_polling_context_free); } return ctx; } static gboolean call_list_poll (MMIfaceModemVoice *self); static void load_call_list_ready (MMIfaceModemVoice *self, GAsyncResult *res) { CallListPollingContext *ctx; GList *call_info_list = NULL; GError *error = NULL; ctx = get_call_list_polling_context (self); ctx->polling_ongoing = FALSE; g_assert (MM_IFACE_MODEM_VOICE_GET_INTERFACE (self)->load_call_list_finish); if (!MM_IFACE_MODEM_VOICE_GET_INTERFACE (self)->load_call_list_finish (self, res, &call_info_list, &error)) { mm_obj_warn (self, "couldn't load call list: %s", error->message); g_error_free (error); } else { /* Always report the list even if NULL (it would mean no ongoing calls) */ mm_iface_modem_voice_report_all_calls (self, call_info_list); mm_3gpp_call_info_list_free (call_info_list); } /* setup the polling again, but only if it hasn't been done already while * we reported calls (e.g. a new incoming call may have been detected that * also triggers the poll setup) */ if (!ctx->polling_id) ctx->polling_id = g_timeout_add_seconds (CALL_LIST_POLLING_TIMEOUT_SECS, (GSourceFunc) call_list_poll, self); } static void call_list_foreach_count_establishing (MMBaseCall *call, gpointer user_data) { guint *n_calls_establishing = (guint *)user_data; switch (mm_base_call_get_state (call)) { case MM_CALL_STATE_DIALING: case MM_CALL_STATE_RINGING_OUT: case MM_CALL_STATE_RINGING_IN: case MM_CALL_STATE_HELD: case MM_CALL_STATE_WAITING: *n_calls_establishing = *n_calls_establishing + 1; break; case MM_CALL_STATE_ACTIVE: case MM_CALL_STATE_TERMINATED: case MM_CALL_STATE_UNKNOWN: default: break; } } static gboolean call_list_poll (MMIfaceModemVoice *self) { CallListPollingContext *ctx; MMCallList *list = NULL; guint n_calls_establishing = 0; ctx = get_call_list_polling_context (self); ctx->polling_id = 0; g_object_get (MM_BASE_MODEM (self), MM_IFACE_MODEM_VOICE_CALL_LIST, &list, NULL); if (!list) { mm_obj_warn (self, "Cannot poll call list: missing internal call list"); goto out; } mm_call_list_foreach (list, (MMCallListForeachFunc) call_list_foreach_count_establishing, &n_calls_establishing); /* If there is at least ONE call being established, we need the call list */ if (n_calls_establishing > 0) { mm_obj_dbg (self, "%u calls being established: call list polling required", n_calls_establishing); ctx->polling_ongoing = TRUE; g_assert (MM_IFACE_MODEM_VOICE_GET_INTERFACE (self)->load_call_list); MM_IFACE_MODEM_VOICE_GET_INTERFACE (self)->load_call_list (self, (GAsyncReadyCallback)load_call_list_ready, NULL); } else mm_obj_dbg (self, "no calls being established: call list polling stopped"); out: g_clear_object (&list); return G_SOURCE_REMOVE; } static void setup_call_list_polling (MMCallList *call_list, const gchar *call_path_added, MMIfaceModemVoice *self) { CallListPollingContext *ctx; ctx = get_call_list_polling_context (self); if (!ctx->polling_id && !ctx->polling_ongoing) ctx->polling_id = g_timeout_add_seconds (CALL_LIST_POLLING_TIMEOUT_SECS, (GSourceFunc) call_list_poll, self); } /*****************************************************************************/ /* Call list reload */ gboolean mm_iface_modem_voice_reload_all_calls_finish (MMIfaceModemVoice *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void reload_all_calls_ready (MMIfaceModemVoice *self, GAsyncResult *res, GTask *task) { GList *call_info_list = NULL; GError *error = NULL; g_assert (MM_IFACE_MODEM_VOICE_GET_INTERFACE (self)->load_call_list_finish); if (!MM_IFACE_MODEM_VOICE_GET_INTERFACE (self)->load_call_list_finish (self, res, &call_info_list, &error)) { mm_obj_warn (self, "couldn't reload call list: %s", error->message); g_task_return_error (task, error); } else { /* Always report the list even if NULL (it would mean no ongoing calls) */ mm_iface_modem_voice_report_all_calls (self, call_info_list); mm_3gpp_call_info_list_free (call_info_list); g_task_return_boolean (task, TRUE); } g_object_unref (task); } void mm_iface_modem_voice_reload_all_calls (MMIfaceModemVoice *self, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; task = g_task_new (self, NULL, callback, user_data); MM_IFACE_MODEM_VOICE_GET_INTERFACE (self)->load_call_list (self, (GAsyncReadyCallback)reload_all_calls_ready, task); } /*****************************************************************************/ static void update_call_list (MmGdbusModemVoice *skeleton, MMCallList *list) { gchar **paths; paths = mm_call_list_get_paths (list); mm_gdbus_modem_voice_set_calls (skeleton, (const gchar *const *)paths); g_strfreev (paths); g_dbus_interface_skeleton_flush (G_DBUS_INTERFACE_SKELETON (skeleton)); } static void call_added (MMCallList *list, const gchar *call_path, MmGdbusModemVoice *skeleton) { update_call_list (skeleton, list); mm_gdbus_modem_voice_emit_call_added (skeleton, call_path); } static void call_deleted (MMCallList *list, const gchar *call_path, MmGdbusModemVoice *skeleton) { update_call_list (skeleton, list); mm_gdbus_modem_voice_emit_call_deleted (skeleton, call_path); } /*****************************************************************************/ typedef struct _DisablingContext DisablingContext; static void interface_disabling_step (GTask *task); typedef enum { DISABLING_STEP_FIRST, DISABLING_STEP_DISABLE_UNSOLICITED_EVENTS, DISABLING_STEP_CLEANUP_UNSOLICITED_EVENTS, DISABLING_STEP_LAST } DisablingStep; struct _DisablingContext { DisablingStep step; MmGdbusModemVoice *skeleton; }; static void disabling_context_free (DisablingContext *ctx) { if (ctx->skeleton) g_object_unref (ctx->skeleton); g_free (ctx); } gboolean mm_iface_modem_voice_disable_finish (MMIfaceModemVoice *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void disable_unsolicited_events_ready (MMIfaceModemVoice *self, GAsyncResult *res, GTask *task) { DisablingContext *ctx; GError *error = NULL; MM_IFACE_MODEM_VOICE_GET_INTERFACE (self)->disable_unsolicited_events_finish (self, res, &error); if (error) { g_task_return_error (task, error); g_object_unref (task); return; } /* Go on to next step */ ctx = g_task_get_task_data (task); ctx->step++; interface_disabling_step (task); } static void cleanup_unsolicited_events_ready (MMIfaceModemVoice *self, GAsyncResult *res, GTask *task) { DisablingContext *ctx; GError *error = NULL; MM_IFACE_MODEM_VOICE_GET_INTERFACE (self)->cleanup_unsolicited_events_finish (self, res, &error); if (error) { g_task_return_error (task, error); g_object_unref (task); return; } /* Go on to next step */ ctx = g_task_get_task_data (task); ctx->step++; interface_disabling_step (task); } static void interface_disabling_step (GTask *task) { MMIfaceModemVoice *self; DisablingContext *ctx; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); switch (ctx->step) { case DISABLING_STEP_FIRST: ctx->step++; /* fall through */ case DISABLING_STEP_DISABLE_UNSOLICITED_EVENTS: /* Allow cleaning up unsolicited events */ if (MM_IFACE_MODEM_VOICE_GET_INTERFACE (self)->disable_unsolicited_events && MM_IFACE_MODEM_VOICE_GET_INTERFACE (self)->disable_unsolicited_events_finish) { MM_IFACE_MODEM_VOICE_GET_INTERFACE (self)->disable_unsolicited_events ( self, (GAsyncReadyCallback)disable_unsolicited_events_ready, task); return; } ctx->step++; /* fall through */ case DISABLING_STEP_CLEANUP_UNSOLICITED_EVENTS: /* Allow cleaning up unsolicited events */ if (MM_IFACE_MODEM_VOICE_GET_INTERFACE (self)->cleanup_unsolicited_events && MM_IFACE_MODEM_VOICE_GET_INTERFACE (self)->cleanup_unsolicited_events_finish) { MM_IFACE_MODEM_VOICE_GET_INTERFACE (self)->cleanup_unsolicited_events ( self, (GAsyncReadyCallback)cleanup_unsolicited_events_ready, task); return; } ctx->step++; /* fall through */ case DISABLING_STEP_LAST: /* We are done without errors! */ g_task_return_boolean (task, TRUE); g_object_unref (task); return; default: break; } g_assert_not_reached (); } void mm_iface_modem_voice_disable (MMIfaceModemVoice *self, GAsyncReadyCallback callback, gpointer user_data) { DisablingContext *ctx; GTask *task; ctx = g_new0 (DisablingContext, 1); ctx->step = DISABLING_STEP_FIRST; task = g_task_new (self, NULL, callback, user_data); g_task_set_task_data (task, ctx, (GDestroyNotify)disabling_context_free); g_object_get (self, MM_IFACE_MODEM_VOICE_DBUS_SKELETON, &ctx->skeleton, NULL); if (!ctx->skeleton) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't get interface skeleton"); g_object_unref (task); return; } interface_disabling_step (task); } /*****************************************************************************/ typedef struct _EnablingContext EnablingContext; static void interface_enabling_step (GTask *task); typedef enum { ENABLING_STEP_FIRST, ENABLING_STEP_SETUP_UNSOLICITED_EVENTS, ENABLING_STEP_ENABLE_UNSOLICITED_EVENTS, ENABLING_STEP_LAST } EnablingStep; struct _EnablingContext { EnablingStep step; MmGdbusModemVoice *skeleton; guint mem1_storage_index; }; static void enabling_context_free (EnablingContext *ctx) { if (ctx->skeleton) g_object_unref (ctx->skeleton); g_free (ctx); } gboolean mm_iface_modem_voice_enable_finish (MMIfaceModemVoice *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void setup_unsolicited_events_ready (MMIfaceModemVoice *self, GAsyncResult *res, GTask *task) { EnablingContext *ctx; GError *error = NULL; MM_IFACE_MODEM_VOICE_GET_INTERFACE (self)->setup_unsolicited_events_finish (self, res, &error); if (error) { g_task_return_error (task, error); g_object_unref (task); return; } /* Go on to next step */ ctx = g_task_get_task_data (task); ctx->step++; interface_enabling_step (task); } static void enable_unsolicited_events_ready (MMIfaceModemVoice *self, GAsyncResult *res, GTask *task) { EnablingContext *ctx; GError *error = NULL; /* Not critical! */ if (!MM_IFACE_MODEM_VOICE_GET_INTERFACE (self)->enable_unsolicited_events_finish (self, res, &error)) { mm_obj_dbg (self, "couldn't enable unsolicited events: %s", error->message); g_error_free (error); } /* Go on with next step */ ctx = g_task_get_task_data (task); ctx->step++; interface_enabling_step (task); } static void interface_enabling_step (GTask *task) { MMIfaceModemVoice *self; EnablingContext *ctx; /* Don't run new steps if we're cancelled */ if (g_task_return_error_if_cancelled (task)) { g_object_unref (task); return; } self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); switch (ctx->step) { case ENABLING_STEP_FIRST: ctx->step++; /* fall through */ case ENABLING_STEP_SETUP_UNSOLICITED_EVENTS: /* Allow setting up unsolicited events to get notified of incoming calls */ if (MM_IFACE_MODEM_VOICE_GET_INTERFACE (self)->setup_unsolicited_events && MM_IFACE_MODEM_VOICE_GET_INTERFACE (self)->setup_unsolicited_events_finish) { MM_IFACE_MODEM_VOICE_GET_INTERFACE (self)->setup_unsolicited_events ( self, (GAsyncReadyCallback)setup_unsolicited_events_ready, task); return; } ctx->step++; /* fall through */ case ENABLING_STEP_ENABLE_UNSOLICITED_EVENTS: /* Allow setting up unsolicited events to get notified of incoming calls */ if (MM_IFACE_MODEM_VOICE_GET_INTERFACE (self)->enable_unsolicited_events && MM_IFACE_MODEM_VOICE_GET_INTERFACE (self)->enable_unsolicited_events_finish) { MM_IFACE_MODEM_VOICE_GET_INTERFACE (self)->enable_unsolicited_events ( self, (GAsyncReadyCallback)enable_unsolicited_events_ready, task); return; } ctx->step++; /* fall through */ case ENABLING_STEP_LAST: /* We are done without errors! */ g_task_return_boolean (task, TRUE); g_object_unref (task); return; default: break; } g_assert_not_reached (); } void mm_iface_modem_voice_enable (MMIfaceModemVoice *self, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { EnablingContext *ctx; GTask *task; ctx = g_new0 (EnablingContext, 1); ctx->step = ENABLING_STEP_FIRST; task = g_task_new (self, cancellable, callback, user_data); g_task_set_task_data (task, ctx, (GDestroyNotify)enabling_context_free); g_object_get (self, MM_IFACE_MODEM_VOICE_DBUS_SKELETON, &ctx->skeleton, NULL); if (!ctx->skeleton) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't get interface skeleton"); g_object_unref (task); return; } interface_enabling_step (task); } /*****************************************************************************/ typedef struct _InitializationContext InitializationContext; static void interface_initialization_step (GTask *task); typedef enum { INITIALIZATION_STEP_FIRST, INITIALIZATION_STEP_CHECK_SUPPORT, INITIALIZATION_STEP_SETUP_CALL_LIST, INITIALIZATION_STEP_LAST } InitializationStep; struct _InitializationContext { MmGdbusModemVoice *skeleton; InitializationStep step; }; static void initialization_context_free (InitializationContext *ctx) { g_object_unref (ctx->skeleton); g_free (ctx); } static void check_support_ready (MMIfaceModemVoice *self, GAsyncResult *res, GTask *task) { InitializationContext *ctx; GError *error = NULL; if (!MM_IFACE_MODEM_VOICE_GET_INTERFACE (self)->check_support_finish (self, res, &error)) { if (error) { mm_obj_dbg (self, "voice support check failed: %s", error->message); g_error_free (error); } g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "Voice not supported"); g_object_unref (task); return; } /* Go on to next step */ ctx = g_task_get_task_data (task); ctx->step++; interface_initialization_step (task); } static void interface_initialization_step (GTask *task) { MMIfaceModemVoice *self; InitializationContext *ctx; /* Don't run new steps if we're cancelled */ if (g_task_return_error_if_cancelled (task)) { g_object_unref (task); return; } self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); switch (ctx->step) { case INITIALIZATION_STEP_FIRST: ctx->step++; /* fall through */ case INITIALIZATION_STEP_CHECK_SUPPORT: /* Always check voice support when we run initialization, because * the support may be different before and after SIM-PIN unlock. */ if (MM_IFACE_MODEM_VOICE_GET_INTERFACE (self)->check_support && MM_IFACE_MODEM_VOICE_GET_INTERFACE (self)->check_support_finish) { MM_IFACE_MODEM_VOICE_GET_INTERFACE (self)->check_support ( self, (GAsyncReadyCallback)check_support_ready, task); return; } g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "Voice not supported"); g_object_unref (task); return; case INITIALIZATION_STEP_SETUP_CALL_LIST: { MMCallList *list = NULL; g_object_get (self, MM_IFACE_MODEM_VOICE_CALL_LIST, &list, NULL); /* Create a new call list if not already available (this initialization * may be called multiple times) */ if (!list) { list = mm_call_list_new (MM_BASE_MODEM (self)); g_object_set (self, MM_IFACE_MODEM_VOICE_CALL_LIST, list, NULL); /* Connect to list's signals */ g_signal_connect (list, MM_CALL_ADDED, G_CALLBACK (call_added), ctx->skeleton); g_signal_connect (list, MM_CALL_DELETED, G_CALLBACK (call_deleted), ctx->skeleton); /* Setup monitoring for in-call event handling */ g_signal_connect (list, MM_CALL_ADDED, G_CALLBACK (setup_in_call_event_handling), self); } /* Unless we're told not to, setup call list polling logic */ if (MM_IFACE_MODEM_VOICE_GET_INTERFACE (self)->load_call_list && MM_IFACE_MODEM_VOICE_GET_INTERFACE (self)->load_call_list_finish) { gboolean periodic_call_list_check_disabled = FALSE; /* Cleanup any previously configured handler, before checking if we need to * add a new one, because the PERIODIC_CALL_LIST_CHECK_DISABLED flag may * change before and after SIM-PIN unlock */ g_signal_handlers_disconnect_by_func (list, G_CALLBACK (setup_call_list_polling), self); g_object_get (self, MM_IFACE_MODEM_VOICE_PERIODIC_CALL_LIST_CHECK_DISABLED, &periodic_call_list_check_disabled, NULL); if (!periodic_call_list_check_disabled) { mm_obj_dbg (self, "periodic call list polling will be used if supported"); g_signal_connect (list, MM_CALL_ADDED, G_CALLBACK (setup_call_list_polling), self); } } g_object_unref (list); ctx->step++; } /* fall through */ case INITIALIZATION_STEP_LAST: /* Setup all method handlers */ g_object_connect (ctx->skeleton, "signal::handle-create-call", G_CALLBACK (handle_create), self, "signal::handle-delete-call", G_CALLBACK (handle_delete), self, "signal::handle-list-calls", G_CALLBACK (handle_list), self, "signal::handle-hangup-and-accept", G_CALLBACK (handle_hangup_and_accept), self, "signal::handle-hold-and-accept", G_CALLBACK (handle_hold_and_accept), self, "signal::handle-hangup-all", G_CALLBACK (handle_hangup_all), self, "signal::handle-transfer", G_CALLBACK (handle_transfer), self, "signal::handle-call-waiting-setup", G_CALLBACK (handle_call_waiting_setup), self, "signal::handle-call-waiting-query", G_CALLBACK (handle_call_waiting_query), self, NULL); /* Finally, export the new interface */ mm_gdbus_object_skeleton_set_modem_voice (MM_GDBUS_OBJECT_SKELETON (self), MM_GDBUS_MODEM_VOICE (ctx->skeleton)); g_task_return_boolean (task, TRUE); g_object_unref (task); return; default: break; } g_assert_not_reached (); } gboolean mm_iface_modem_voice_initialize_finish (MMIfaceModemVoice *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static gboolean modem_state_to_emergency_only (GBinding *binding, const GValue *from_value, GValue *to_value) { MMModemState state; /* If the modem is REGISTERED, we allow any kind of call, otherwise * only emergency calls */ state = g_value_get_enum (from_value); g_value_set_boolean (to_value, (state < MM_MODEM_STATE_REGISTERED)); return TRUE; } void mm_iface_modem_voice_initialize (MMIfaceModemVoice *self, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { InitializationContext *ctx; MmGdbusModemVoice *skeleton = NULL; GTask *task; /* Did we already create it? */ g_object_get (self, MM_IFACE_MODEM_VOICE_DBUS_SKELETON, &skeleton, NULL); if (!skeleton) { skeleton = mm_gdbus_modem_voice_skeleton_new (); g_object_set (self, MM_IFACE_MODEM_VOICE_DBUS_SKELETON, skeleton, NULL); g_object_bind_property_full (self, MM_IFACE_MODEM_STATE, skeleton, "emergency-only", G_BINDING_DEFAULT | G_BINDING_SYNC_CREATE, (GBindingTransformFunc) modem_state_to_emergency_only, NULL, NULL, NULL); } /* Perform async initialization here */ ctx = g_new0 (InitializationContext, 1); ctx->step = INITIALIZATION_STEP_FIRST; ctx->skeleton = skeleton; task = g_task_new (self, cancellable, callback, user_data); g_task_set_task_data (task, ctx, (GDestroyNotify)initialization_context_free); interface_initialization_step (task); } void mm_iface_modem_voice_shutdown (MMIfaceModemVoice *self) { /* Unexport DBus interface and remove the skeleton */ mm_gdbus_object_skeleton_set_modem_voice (MM_GDBUS_OBJECT_SKELETON (self), NULL); g_object_set (self, MM_IFACE_MODEM_VOICE_DBUS_SKELETON, NULL, NULL); } /*****************************************************************************/ static void iface_modem_voice_init (gpointer g_iface) { static gboolean initialized = FALSE; if (initialized) return; /* Properties */ g_object_interface_install_property (g_iface, g_param_spec_object (MM_IFACE_MODEM_VOICE_DBUS_SKELETON, "Voice DBus skeleton", "DBus skeleton for the Voice interface", MM_GDBUS_TYPE_MODEM_VOICE_SKELETON, G_PARAM_READWRITE)); g_object_interface_install_property (g_iface, g_param_spec_object (MM_IFACE_MODEM_VOICE_CALL_LIST, "CALL list", "List of CALL objects managed in the interface", MM_TYPE_CALL_LIST, G_PARAM_READWRITE)); g_object_interface_install_property (g_iface, g_param_spec_boolean (MM_IFACE_MODEM_VOICE_PERIODIC_CALL_LIST_CHECK_DISABLED, "Periodic call list checks disabled", "Whether periodic call list check are disabled.", FALSE, G_PARAM_READWRITE)); g_object_interface_install_property (g_iface, g_param_spec_boolean (MM_IFACE_MODEM_VOICE_INDICATION_CALL_LIST_RELOAD_ENABLED, "Reload call list on call update", "Ignore call updates and forcefully reload all calls.", FALSE, G_PARAM_READWRITE)); initialized = TRUE; } GType mm_iface_modem_voice_get_type (void) { static GType iface_modem_voice_type = 0; if (!G_UNLIKELY (iface_modem_voice_type)) { static const GTypeInfo info = { sizeof (MMIfaceModemVoice), /* class_size */ iface_modem_voice_init, /* base_init */ NULL, /* base_finalize */ }; iface_modem_voice_type = g_type_register_static (G_TYPE_INTERFACE, "MMIfaceModemVoice", &info, 0); g_type_interface_add_prerequisite (iface_modem_voice_type, MM_TYPE_IFACE_MODEM); } return iface_modem_voice_type; } ModemManager-1.23.4-dev/src/mm-iface-modem-voice.h000066400000000000000000000372441456466623000215570ustar00rootroot00000000000000/* -*- 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) 2015 Marco Bascetta * Copyright (C) 2019 Purism SPC */ #ifndef MM_IFACE_MODEM_VOICE_H #define MM_IFACE_MODEM_VOICE_H #include #include #define _LIBMM_INSIDE_MM #include #include "mm-base-call.h" #define MM_TYPE_IFACE_MODEM_VOICE (mm_iface_modem_voice_get_type ()) #define MM_IFACE_MODEM_VOICE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_IFACE_MODEM_VOICE, MMIfaceModemVoice)) #define MM_IS_IFACE_MODEM_VOICE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_IFACE_MODEM_VOICE)) #define MM_IFACE_MODEM_VOICE_GET_INTERFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE ((obj), MM_TYPE_IFACE_MODEM_VOICE, MMIfaceModemVoice)) #define MM_IFACE_MODEM_VOICE_DBUS_SKELETON "iface-modem-voice-dbus-skeleton" #define MM_IFACE_MODEM_VOICE_CALL_LIST "iface-modem-voice-call-list" #define MM_IFACE_MODEM_VOICE_PERIODIC_CALL_LIST_CHECK_DISABLED "iface-modem-voice-periodic-call-list-check-disabled" #define MM_IFACE_MODEM_VOICE_INDICATION_CALL_LIST_RELOAD_ENABLED "iface-modem-voice-indication-call-list-reload-enabled" typedef struct _MMIfaceModemVoice MMIfaceModemVoice; struct _MMIfaceModemVoice { GTypeInterface g_iface; /* Check for Voice support (async) */ void (* check_support) (MMIfaceModemVoice *self, GAsyncReadyCallback callback, gpointer user_data); gboolean (*check_support_finish) (MMIfaceModemVoice *self, GAsyncResult *res, GError **error); /* Asynchronous setting up unsolicited CALL reception events */ void (*setup_unsolicited_events) (MMIfaceModemVoice *self, GAsyncReadyCallback callback, gpointer user_data); gboolean (*setup_unsolicited_events_finish) (MMIfaceModemVoice *self, GAsyncResult *res, GError **error); /* Asynchronous cleaning up of unsolicited CALL reception events */ void (*cleanup_unsolicited_events) (MMIfaceModemVoice *self, GAsyncReadyCallback callback, gpointer user_data); gboolean (*cleanup_unsolicited_events_finish) (MMIfaceModemVoice *self, GAsyncResult *res, GError **error); /* Asynchronous enabling unsolicited CALL reception events */ void (* enable_unsolicited_events) (MMIfaceModemVoice *self, GAsyncReadyCallback callback, gpointer user_data); gboolean (* enable_unsolicited_events_finish) (MMIfaceModemVoice *self, GAsyncResult *res, GError **error); /* Asynchronous disabling unsolicited CALL reception events */ void (* disable_unsolicited_events) (MMIfaceModemVoice *self, GAsyncReadyCallback callback, gpointer user_data); gboolean (* disable_unsolicited_events_finish) (MMIfaceModemVoice *self, GAsyncResult *res, GError **error); /* Asynchronous setup of in-call unsolicited events */ void (* setup_in_call_unsolicited_events) (MMIfaceModemVoice *self, GAsyncReadyCallback callback, gpointer user_data); gboolean (* setup_in_call_unsolicited_events_finish) (MMIfaceModemVoice *self, GAsyncResult *res, GError **error); /* Asynchronous cleanup of in-call unsolicited events */ void (* cleanup_in_call_unsolicited_events) (MMIfaceModemVoice *self, GAsyncReadyCallback callback, gpointer user_data); gboolean (* cleanup_in_call_unsolicited_events_finish) (MMIfaceModemVoice *self, GAsyncResult *res, GError **error); /* Asynchronous setup of in-call audio channel */ void (* setup_in_call_audio_channel) (MMIfaceModemVoice *self, GAsyncReadyCallback callback, gpointer user_data); gboolean (* setup_in_call_audio_channel_finish) (MMIfaceModemVoice *self, GAsyncResult *res, MMPort **audio_port, /* optional */ MMCallAudioFormat **audio_format, /* optional */ GError **error); /* Asynchronous cleanup of in-call audio channel */ void (* cleanup_in_call_audio_channel) (MMIfaceModemVoice *self, GAsyncReadyCallback callback, gpointer user_data); gboolean (* cleanup_in_call_audio_channel_finish) (MMIfaceModemVoice *self, GAsyncResult *res, GError **error); /* Load full list of calls (MMCallInfo list) */ void (* load_call_list) (MMIfaceModemVoice *self, GAsyncReadyCallback callback, gpointer user_data); gboolean (* load_call_list_finish) (MMIfaceModemVoice *self, GAsyncResult *res, GList **call_info_list, GError **error); /* Create call objects */ MMBaseCall * (* create_call) (MMIfaceModemVoice *self, MMCallDirection direction, const gchar *number); /* Hold and accept */ void (* hold_and_accept) (MMIfaceModemVoice *self, GAsyncReadyCallback callback, gpointer user_data); gboolean (* hold_and_accept_finish) (MMIfaceModemVoice *self, GAsyncResult *res, GError **error); /* Hangup and accept */ void (* hangup_and_accept) (MMIfaceModemVoice *self, GAsyncReadyCallback callback, gpointer user_data); gboolean (* hangup_and_accept_finish) (MMIfaceModemVoice *self, GAsyncResult *res, GError **error); /* Hangup all */ void (* hangup_all) (MMIfaceModemVoice *self, GAsyncReadyCallback callback, gpointer user_data); gboolean (* hangup_all_finish) (MMIfaceModemVoice *self, GAsyncResult *res, GError **error); /* Join multiparty */ void (* join_multiparty) (MMIfaceModemVoice *self, GAsyncReadyCallback callback, gpointer user_data); gboolean (* join_multiparty_finish) (MMIfaceModemVoice *self, GAsyncResult *res, GError **error); /* Leave multiparty */ void (* leave_multiparty) (MMIfaceModemVoice *self, MMBaseCall *call, GAsyncReadyCallback callback, gpointer user_data); gboolean (* leave_multiparty_finish) (MMIfaceModemVoice *self, GAsyncResult *res, GError **error); /* Transfer */ void (* transfer) (MMIfaceModemVoice *self, GAsyncReadyCallback callback, gpointer user_data); gboolean (* transfer_finish) (MMIfaceModemVoice *self, GAsyncResult *res, GError **error); /* Call waiting setup */ void (* call_waiting_setup) (MMIfaceModemVoice *self, gboolean enable, GAsyncReadyCallback callback, gpointer user_data); gboolean (* call_waiting_setup_finish) (MMIfaceModemVoice *self, GAsyncResult *res, GError **error); /* Call waiting query */ void (* call_waiting_query) (MMIfaceModemVoice *self, GAsyncReadyCallback callback, gpointer user_data); gboolean (* call_waiting_query_finish) (MMIfaceModemVoice *self, GAsyncResult *res, gboolean *status, GError **error); }; GType mm_iface_modem_voice_get_type (void); G_DEFINE_AUTOPTR_CLEANUP_FUNC (MMIfaceModemVoice, g_object_unref) /* Initialize Voice interface (async) */ void mm_iface_modem_voice_initialize (MMIfaceModemVoice *self, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); gboolean mm_iface_modem_voice_initialize_finish (MMIfaceModemVoice *self, GAsyncResult *res, GError **error); /* Enable Voice interface (async) */ void mm_iface_modem_voice_enable (MMIfaceModemVoice *self, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); gboolean mm_iface_modem_voice_enable_finish (MMIfaceModemVoice *self, GAsyncResult *res, GError **error); /* Disable Voice interface (async) */ void mm_iface_modem_voice_disable (MMIfaceModemVoice *self, GAsyncReadyCallback callback, gpointer user_data); gboolean mm_iface_modem_voice_disable_finish (MMIfaceModemVoice *self, GAsyncResult *res, GError **error); /* Shutdown Voice interface */ void mm_iface_modem_voice_shutdown (MMIfaceModemVoice *self); /* Bind properties for simple GetStatus() */ void mm_iface_modem_voice_bind_simple_status (MMIfaceModemVoice *self, MMSimpleStatus *status); /* Single call info reporting */ void mm_iface_modem_voice_report_call (MMIfaceModemVoice *self, const MMCallInfo *call_info); /* Full current call list reporting (MMCallInfo list) */ void mm_iface_modem_voice_report_all_calls (MMIfaceModemVoice *self, GList *call_info_list); /* Full reload of call list (async) */ void mm_iface_modem_voice_reload_all_calls (MMIfaceModemVoice *self, GAsyncReadyCallback callback, gpointer user_data); gboolean mm_iface_modem_voice_reload_all_calls_finish (MMIfaceModemVoice *self, GAsyncResult *res, GError **error); /* Report an incoming DTMF received */ void mm_iface_modem_voice_received_dtmf (MMIfaceModemVoice *self, guint index, const gchar *dtmf); /* Authorize outgoing call based on modem status and ECC list */ gboolean mm_iface_modem_voice_authorize_outgoing_call (MMIfaceModemVoice *self, MMBaseCall *call, GError **error); /* Join/Leave multiparty calls * * These actions are provided in the Call API, but implemented in the * modem Voice interface because they really affect multiple calls at * the same time. */ void mm_iface_modem_voice_join_multiparty (MMIfaceModemVoice *self, MMBaseCall *call, GAsyncReadyCallback callback, gpointer user_data); gboolean mm_iface_modem_voice_join_multiparty_finish (MMIfaceModemVoice *self, GAsyncResult *res, GError **error); void mm_iface_modem_voice_leave_multiparty (MMIfaceModemVoice *self, MMBaseCall *call, GAsyncReadyCallback callback, gpointer user_data); gboolean mm_iface_modem_voice_leave_multiparty_finish (MMIfaceModemVoice *self, GAsyncResult *res, GError **error); #endif /* MM_IFACE_MODEM_VOICE_H */ ModemManager-1.23.4-dev/src/mm-iface-modem.c000066400000000000000000007331761456466623000204560ustar00rootroot00000000000000/* -*- 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) 2011 Google, Inc. * Copyright (c) 2022 Qualcomm Innovation Center, Inc. */ #include #include #define _LIBMM_INSIDE_MM #include #include "mm-modem-helpers.h" #include "mm-iface-modem.h" #include "mm-iface-modem-3gpp.h" #include "mm-iface-modem-cdma.h" #include "mm-base-modem.h" #include "mm-base-modem-at.h" #include "mm-base-sim.h" #include "mm-bearer-list.h" #include "mm-private-boxed-types.h" #include "mm-error-helpers.h" #include "mm-log-object.h" #include "mm-log-helpers.h" #include "mm-context.h" #include "mm-dispatcher-fcc-unlock.h" #if defined WITH_QMI # include "mm-broadband-modem-qmi.h" #endif #if defined WITH_MBIM # include "mm-broadband-modem-mbim.h" #endif #define SIGNAL_QUALITY_RECENT_TIMEOUT_SEC 60 #define SIGNAL_CHECK_INITIAL_RETRIES 5 #define SIGNAL_CHECK_INITIAL_TIMEOUT_SEC 3 #define SIGNAL_CHECK_TIMEOUT_SEC 30 /* Make sure this amount of seconds is left between two power state transitions, * so that the modem can have time to process them properly. This is just a safe * measure taken because we know modems may report us that the power state * transition has already finished even if it hasn't. The timeout will really * only apply if doing many power state transitions quickly one after the other, * so this is just to cover that corner case. */ #define POWER_STATE_MIN_TIME_BETWEEN_UPDATES_SEC 2 /*****************************************************************************/ /* Private data context */ #define PRIVATE_TAG "modem-private-tag" static GQuark private_quark; typedef struct { /* Subsystem specific states */ GArray *subsystem_states; /* Signal quality recent flag handling */ guint signal_quality_recent_timeout_source; /* If both signal and access tech polling are either unsupported * or disabled, we'll automatically stop polling */ gboolean signal_quality_polling_supported; gboolean signal_quality_polling_disabled; gboolean access_technology_polling_supported; gboolean access_technology_polling_disabled; /* Signal quality and access tech polling support */ gboolean signal_check_enabled; guint signal_check_timeout_source; guint signal_check_initial_retries; gboolean signal_check_initial_done; gboolean signal_check_running; /* Initialization restart support */ guint restart_initialize_idle_id; /* SIM hot swap setup done flag */ gboolean sim_hot_swap_configured; /* Timer that tracks when the last power operation request was * performed, so that we can throttle the requests to the modem. */ GTimer *power_state_timer; } Private; static void private_free (Private *priv) { if (priv->subsystem_states) g_array_unref (priv->subsystem_states); if (priv->signal_quality_recent_timeout_source) g_source_remove (priv->signal_quality_recent_timeout_source); if (priv->signal_check_timeout_source) g_source_remove (priv->signal_check_timeout_source); if (priv->restart_initialize_idle_id) g_source_remove (priv->restart_initialize_idle_id); g_clear_pointer (&priv->power_state_timer, (GDestroyNotify) g_timer_destroy); g_slice_free (Private, priv); } static Private * get_private (MMIfaceModem *self) { Private *priv; if (G_UNLIKELY (!private_quark)) private_quark = g_quark_from_static_string (PRIVATE_TAG); priv = g_object_get_qdata (G_OBJECT (self), private_quark); if (!priv) { priv = g_slice_new0 (Private); /* Initially assume supported if load_access_technologies() is * implemented. If the plugin reports an UNSUPPORTED error we'll clear * this flag and no longer poll. */ priv->access_technology_polling_supported = (MM_IFACE_MODEM_GET_INTERFACE (self)->load_access_technologies && MM_IFACE_MODEM_GET_INTERFACE (self)->load_access_technologies_finish); /* Initially assume supported if load_signal_quality() is * implemented. If the plugin reports an UNSUPPORTED error we'll clear * this flag and no longer poll. */ priv->signal_quality_polling_supported = (MM_IFACE_MODEM_GET_INTERFACE (self)->load_signal_quality && MM_IFACE_MODEM_GET_INTERFACE (self)->load_signal_quality_finish); /* Get plugin-specific setup for the polling logic */ g_object_get (self, MM_IFACE_MODEM_PERIODIC_SIGNAL_CHECK_DISABLED, &priv->signal_quality_polling_disabled, MM_IFACE_MODEM_PERIODIC_ACCESS_TECH_CHECK_DISABLED, &priv->access_technology_polling_disabled, NULL); g_object_set_qdata_full (G_OBJECT (self), private_quark, priv, (GDestroyNotify)private_free); } return priv; } /*****************************************************************************/ gboolean mm_iface_modem_check_for_sim_swap_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void check_basic_sim_details_ready (MMIfaceModem *self, GAsyncResult *res, GTask *task) { g_autoptr(MMBaseSim) sim = NULL; GError *error = NULL; const gchar *old_iccid = NULL; const gchar *old_imsi = NULL; g_autofree gchar *current_iccid = NULL; g_autofree gchar *current_imsi = NULL; gboolean sim_inserted; if (!MM_IFACE_MODEM_GET_INTERFACE (self)->check_basic_sim_details_finish ( self, res, &sim_inserted, ¤t_iccid, ¤t_imsi, &error)) { mm_obj_warn (self, "SIM details check failed: %s", error->message); g_task_return_error (task, error); g_object_unref (task); return; } g_object_get (self, MM_IFACE_MODEM_SIM, &sim, NULL); if (sim) { old_iccid = mm_gdbus_sim_get_sim_identifier (MM_GDBUS_SIM (sim)); old_imsi = mm_gdbus_sim_get_imsi (MM_GDBUS_SIM (sim)); } if (!sim && !sim_inserted) { mm_obj_info (self, "No SIM inserted before and after"); } else if (sim && !sim_inserted) { mm_obj_info (self, "SIM removed"); mm_iface_modem_process_sim_event (self); } else if (!sim && sim_inserted) { mm_obj_info (self, "SIM inserted"); mm_iface_modem_process_sim_event (self); } else if ((g_strcmp0 (current_iccid, old_iccid) != 0) || (g_strcmp0 (current_imsi, old_imsi) != 0)) { mm_obj_info (self, "new SIM detected"); mm_obj_info (self, "ICCID: %s -> %s", mm_log_str_personal_info (old_iccid), mm_log_str_personal_info (current_iccid)); mm_obj_info (self, "IMSI: %s -> %s", mm_log_str_personal_info (old_imsi), mm_log_str_personal_info (current_imsi)); mm_iface_modem_process_sim_event (self); } else { mm_obj_info (self, "SIM not changed. ICCID: %s, IMSI: %s", mm_log_str_personal_info (current_iccid), mm_log_str_personal_info (current_imsi)); } g_task_return_boolean (task, TRUE); g_object_unref (task); } static void explicit_check_for_sim_swap_ready (MMIfaceModem *self, GAsyncResult *res, GTask *task) { GError *error = NULL; if (!MM_IFACE_MODEM_GET_INTERFACE (self)->check_for_sim_swap_finish (self, res, &error)) { mm_obj_warn (self, "SIM swap check failed: %s", error->message); g_task_return_error (task, error); } else { mm_obj_dbg (self, "SIM swap check completed"); g_task_return_boolean (task, TRUE); } g_object_unref (task); } void mm_iface_modem_check_for_sim_swap (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; task = g_task_new (self, NULL, callback, user_data); if (MM_IFACE_MODEM_GET_INTERFACE (self)->check_basic_sim_details && MM_IFACE_MODEM_GET_INTERFACE (self)->check_basic_sim_details_finish) { mm_obj_info (self, "started checking for basic SIM details..."); MM_IFACE_MODEM_GET_INTERFACE (self)->check_basic_sim_details ( self, (GAsyncReadyCallback)check_basic_sim_details_ready, task); return; } if (MM_IFACE_MODEM_GET_INTERFACE (self)->check_for_sim_swap && MM_IFACE_MODEM_GET_INTERFACE (self)->check_for_sim_swap_finish) { mm_obj_info (self, "started checking for SIM swap..."); MM_IFACE_MODEM_GET_INTERFACE (self)->check_for_sim_swap ( self, (GAsyncReadyCallback)explicit_check_for_sim_swap_ready, task); return; } mm_obj_info (self, "checking for SIM swap ignored: not implemented"); g_task_return_boolean (task, TRUE); g_object_unref (task); } /*****************************************************************************/ static void sim_slot_free (MMBaseSim *sim) { if (sim) g_object_unref (sim); } void mm_iface_modem_modify_sim (MMIfaceModem *self, guint slot_index, MMBaseSim *new_sim) { g_autoptr(MmGdbusModemSkeleton) skeleton = NULL; g_autoptr(GPtrArray) sim_slots_old = NULL; g_autoptr(GPtrArray) sim_slots_new = NULL; guint i; GPtrArray *sim_slot_paths_array; g_auto(GStrv) sim_slot_paths = NULL; g_object_get (self, MM_IFACE_MODEM_SIM_SLOTS, &sim_slots_old, MM_IFACE_MODEM_DBUS_SKELETON, &skeleton, NULL); if (!sim_slots_old) { mm_obj_warn (self, "failed to process SIM hot swap: couldn't load current list of SIM slots"); return; } if (!skeleton) return; sim_slot_paths_array = g_ptr_array_new (); sim_slots_new = g_ptr_array_new_with_free_func ((GDestroyNotify) sim_slot_free); for (i = 0; i < sim_slots_old->len; i++) { MMBaseSim *sim; const gchar *sim_path = NULL; if (i == slot_index) sim = new_sim; else sim = MM_BASE_SIM (g_ptr_array_index (sim_slots_old, i)); if (sim) { g_ptr_array_add (sim_slots_new, g_object_ref (sim)); sim_path = mm_base_sim_get_path (sim); } else g_ptr_array_add (sim_slots_new, NULL); if (sim_path) g_ptr_array_add (sim_slot_paths_array, g_strdup (sim_path)); else g_ptr_array_add (sim_slot_paths_array, g_strdup ("/")); } g_ptr_array_add (sim_slot_paths_array, NULL); sim_slot_paths = (GStrv) g_ptr_array_free (sim_slot_paths_array, FALSE); g_object_set (self, MM_IFACE_MODEM_SIM_SLOTS, sim_slots_new, NULL); mm_gdbus_modem_set_sim_slots (MM_GDBUS_MODEM (skeleton), (const gchar *const *) sim_slot_paths); } /*****************************************************************************/ static void after_sim_event_disable_ready (MMBaseModem *self, GAsyncResult *res) { g_autoptr(GError) error = NULL; mm_base_modem_disable_finish (self, res, &error); if (error) mm_obj_err (self, "failed to disable after SIM switch event: %s", error->message); /* set invalid either way, so that it's reprobed */ mm_base_modem_set_valid (self, FALSE); } void mm_iface_modem_process_sim_event (MMIfaceModem *self) { mm_obj_info (self, "Processing SIM event"); if (MM_IFACE_MODEM_GET_INTERFACE (self)->cleanup_sim_hot_swap) MM_IFACE_MODEM_GET_INTERFACE (self)->cleanup_sim_hot_swap (self); mm_base_modem_set_reprobe (MM_BASE_MODEM (self), TRUE); mm_base_modem_disable (MM_BASE_MODEM (self), (GAsyncReadyCallback) after_sim_event_disable_ready, NULL); } /*****************************************************************************/ void mm_iface_modem_bind_simple_status (MMIfaceModem *self, MMSimpleStatus *status) { MmGdbusModem *skeleton; g_object_get (self, MM_IFACE_MODEM_DBUS_SKELETON, &skeleton, NULL); if (!skeleton) return; g_object_bind_property (skeleton, "state", status, MM_SIMPLE_PROPERTY_STATE, G_BINDING_DEFAULT | G_BINDING_SYNC_CREATE); g_object_bind_property (skeleton, "signal-quality", status, MM_SIMPLE_PROPERTY_SIGNAL_QUALITY, G_BINDING_DEFAULT | G_BINDING_SYNC_CREATE); g_object_bind_property (skeleton, "current-bands", status, MM_SIMPLE_PROPERTY_CURRENT_BANDS, G_BINDING_DEFAULT | G_BINDING_SYNC_CREATE); g_object_bind_property (skeleton, "access-technologies", status, MM_SIMPLE_PROPERTY_ACCESS_TECHNOLOGIES, G_BINDING_DEFAULT | G_BINDING_SYNC_CREATE); g_object_unref (skeleton); } /*****************************************************************************/ /* Helper method to wait for a final state */ #define MODEM_STATE_IS_INTERMEDIATE(state) \ (state == MM_MODEM_STATE_INITIALIZING || \ state == MM_MODEM_STATE_DISABLING || \ state == MM_MODEM_STATE_ENABLING || \ state == MM_MODEM_STATE_DISCONNECTING || \ state == MM_MODEM_STATE_CONNECTING) typedef struct { MMModemState final_state; gulong state_changed_id; guint state_changed_wait_id; } WaitForFinalStateContext; static void wait_for_final_state_context_complete (GTask *task, MMModemState state, GError *error) { MMIfaceModem *self; WaitForFinalStateContext *ctx; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); /* The callback associated with 'task' may update the modem state. * Disconnect the signal handler for modem state changes before completing * 'task' in order to prevent state_changed from being invoked, which * invokes wait_for_final_state_context_complete again. */ if (ctx->state_changed_id) { /* may be automatically disconnected during dispose */ if (g_signal_handler_is_connected (self, ctx->state_changed_id)) g_signal_handler_disconnect (self, ctx->state_changed_id); ctx->state_changed_id = 0; } /* Remove any outstanding timeout on waiting for state change. */ if (ctx->state_changed_wait_id) { g_source_remove (ctx->state_changed_wait_id); ctx->state_changed_wait_id = 0; } if (error) g_task_return_error (task, error); else g_task_return_int (task, state); g_object_unref (task); } MMModemState mm_iface_modem_wait_for_final_state_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { GError *inner_error = NULL; gssize value; value = g_task_propagate_int (G_TASK (res), &inner_error); if (inner_error) { g_propagate_error (error, inner_error); return MM_MODEM_STATE_UNKNOWN; } if (value == MM_MODEM_STATE_UNKNOWN) g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Unknown modem state"); return (MMModemState)value; } static gboolean state_changed_wait_expired (GTask *task) { GError *error; error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_RETRY, "Too much time waiting to get to a final state"); wait_for_final_state_context_complete (task, MM_MODEM_STATE_UNKNOWN, error); return G_SOURCE_REMOVE; } static void state_changed (MMIfaceModem *self, GParamSpec *spec, GTask *task) { WaitForFinalStateContext *ctx; MMModemState state = MM_MODEM_STATE_UNKNOWN; g_object_get (self, MM_IFACE_MODEM_STATE, &state, NULL); /* Ignore unknown state explicitly during a wait operation */ if (state == MM_MODEM_STATE_UNKNOWN) return; /* Are we in a final state already? */ if (MODEM_STATE_IS_INTERMEDIATE (state)) return; ctx = g_task_get_task_data (task); /* If we want a specific final state and this is not the one we were * looking for, then skip */ if (ctx->final_state != MM_MODEM_STATE_UNKNOWN && state != ctx->final_state) return; /* Done! */ wait_for_final_state_context_complete (task, state, NULL); } void mm_iface_modem_wait_for_final_state (MMIfaceModem *self, MMModemState final_state, GAsyncReadyCallback callback, gpointer user_data) { MMModemState state = MM_MODEM_STATE_UNKNOWN; WaitForFinalStateContext *ctx; GTask *task; task = g_task_new (self, NULL, callback, user_data); g_object_get (self, MM_IFACE_MODEM_STATE, &state, NULL); /* Are we in a final state already? */ if (!MODEM_STATE_IS_INTERMEDIATE (state)) { /* Is this the state we actually wanted? */ if (final_state == MM_MODEM_STATE_UNKNOWN || (state != MM_MODEM_STATE_UNKNOWN && state == final_state)) { g_task_return_int (task, state); g_object_unref (task); return; } /* Otherwise, we'll need to wait for the exact one we want */ } ctx = g_new0 (WaitForFinalStateContext, 1); ctx->final_state = final_state; g_task_set_task_data (task, ctx, g_free); /* Want to get notified when modem state changes */ ctx->state_changed_id = g_signal_connect (self, "notify::" MM_IFACE_MODEM_STATE, G_CALLBACK (state_changed), task); /* But we don't want to wait forever */ ctx->state_changed_wait_id = g_timeout_add_seconds (10, (GSourceFunc)state_changed_wait_expired, task); } /*****************************************************************************/ gboolean mm_iface_modem_abort_invocation_if_state_not_reached (MMIfaceModem *self, GDBusMethodInvocation *invocation, MMModemState minimum_required) { MMModemState state = MM_MODEM_STATE_UNKNOWN; g_object_get (self, MM_IFACE_MODEM_STATE, &state, NULL); if (state >= minimum_required) return FALSE; mm_dbus_method_invocation_return_error (invocation, MM_CORE_ERROR, MM_CORE_ERROR_WRONG_STATE, "modem in %s state", mm_modem_state_get_string (state)); return TRUE; } /*****************************************************************************/ /* Helper method to load unlock required, considering retries */ /* If a SIM is known to exist, for e.g. if it was created during load_sim_slots, * persist a few more times before giving up on the SIM to be ready. There * are modems on which the SIM takes more than 15s to be ready, luckily, * they happen to be QMI modems where the SIM's iccid in load_sim_slots * lets us know that there is a sim */ #define MAX_UNLOCK_REQUIRED_RETRIES_NO_SIM 7 #define MAX_UNLOCK_REQUIRED_RETRIES_SIM_EXISTS 30 /* Time between retries */ #define UNLOAD_REQUIRED_RETRY_TIMEOUT_SECS 2 typedef struct { guint retries; guint max_retries; guint timeout_id; gulong cancellable_id; } InternalLoadUnlockRequiredContext; static void internal_load_unlock_required_context_free (InternalLoadUnlockRequiredContext *ctx) { g_assert (!ctx->timeout_id); g_assert (!ctx->cancellable_id); g_slice_free (InternalLoadUnlockRequiredContext, ctx); } static MMModemLock internal_load_unlock_required_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { GError *inner_error = NULL; gssize value; value = g_task_propagate_int (G_TASK (res), &inner_error); if (inner_error) { g_propagate_error (error, inner_error); return MM_MODEM_LOCK_UNKNOWN; } return (MMModemLock)value; } static void internal_load_unlock_required_context_step (GTask *task); static gboolean load_unlock_required_again (GTask *task) { InternalLoadUnlockRequiredContext *ctx; ctx = g_task_get_task_data (task); ctx->timeout_id = 0; g_assert (ctx->cancellable_id); g_cancellable_disconnect (g_task_get_cancellable (task), ctx->cancellable_id); ctx->cancellable_id = 0; /* Retry the step */ internal_load_unlock_required_context_step (task); return G_SOURCE_REMOVE; } static void load_unlock_required_again_cancelled (GCancellable *cancellable, GTask *task) { InternalLoadUnlockRequiredContext *ctx; MMIfaceModem *self; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); ctx->cancellable_id = 0; if (ctx->timeout_id) { g_source_remove (ctx->timeout_id); ctx->timeout_id = 0; } mm_obj_dbg (self, "unlock required check retries cancelled"); if (!g_task_return_error_if_cancelled (task)) g_assert_not_reached (); g_object_unref (task); } static void load_unlock_required_ready (MMIfaceModem *self, GAsyncResult *res, GTask *task) { InternalLoadUnlockRequiredContext *ctx; g_autoptr(GError) error = NULL; MMModemLock lock; ctx = g_task_get_task_data (task); lock = MM_IFACE_MODEM_GET_INTERFACE (self)->load_unlock_required_finish (self, res, &error); if (error) { mm_obj_dbg (self, "couldn't check if unlock required: %s", error->message); /* For several kinds of errors, just return them directly */ if (error->domain == MM_SERIAL_ERROR || g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED) || g_error_matches (error, MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_SIM_NOT_INSERTED) || g_error_matches (error, MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_SIM_FAILURE) || g_error_matches (error, MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_SIM_WRONG)) { g_task_return_error (task, g_steal_pointer (&error)); g_object_unref (task); return; } /* If the error indicates that retry logic needs to be reset... reset the retry count to 0 */ if (g_error_matches (error, MM_CORE_ERROR, MM_CORE_ERROR_RESET_AND_RETRY)) { ctx->retries = 0; mm_obj_info (self, "restarting unlock required check"); } /* For the remaining ones, retry if possible */ if (ctx->retries < ctx->max_retries) { ctx->retries++; /* Rate limit how often we log with INFO level */ if (ctx->retries % 5) mm_obj_dbg (self, "retrying (%u/%u) unlock required check", ctx->retries, ctx->max_retries); else mm_obj_info (self, "retrying (%u/%u) unlock required check", ctx->retries, ctx->max_retries); /* Ownership of the task will be shared between the timeout and the cancellable. As soon as one * of them is triggered, it should cancel the other. */ g_assert (ctx->cancellable_id == 0); ctx->cancellable_id = g_cancellable_connect (g_task_get_cancellable (task), (GCallback) load_unlock_required_again_cancelled, task, NULL); /* Do nothing if already cancelled, the callback will already be called */ if (!ctx->cancellable_id) return; g_assert (ctx->timeout_id == 0); ctx->timeout_id = g_timeout_add_seconds (UNLOAD_REQUIRED_RETRY_TIMEOUT_SECS, (GSourceFunc)load_unlock_required_again, task); return; } /* If reached max retries and still reporting error... default to SIM error */ g_task_return_new_error (task, MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_SIM_FAILURE, "Couldn't get SIM lock status after %u retries", ctx->retries); g_object_unref (task); return; } /* Got the lock value, return it */ g_task_return_int (task, lock); g_object_unref (task); } static void internal_load_unlock_required_context_step (GTask *task) { MMIfaceModem *self; InternalLoadUnlockRequiredContext *ctx; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); /* Don't run a new check if we were already cancelled */ if (g_task_return_error_if_cancelled (task)) { g_object_unref (task); return; } g_assert (ctx->cancellable_id == 0); g_assert (ctx->timeout_id == 0); MM_IFACE_MODEM_GET_INTERFACE (self)->load_unlock_required ( self, (ctx->retries >= ctx->max_retries), /* last_attempt? */ g_task_get_cancellable (task), (GAsyncReadyCallback) load_unlock_required_ready, task); } static guint load_unlock_required_max_retries (MMIfaceModem *self) { g_autoptr(MMBaseSim) sim = NULL; g_object_get (self, MM_IFACE_MODEM_SIM, &sim, NULL); return (sim ? MAX_UNLOCK_REQUIRED_RETRIES_SIM_EXISTS : MAX_UNLOCK_REQUIRED_RETRIES_NO_SIM); } static void internal_load_unlock_required (MMIfaceModem *self, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { InternalLoadUnlockRequiredContext *ctx; GTask *task; task = g_task_new (self, cancellable, callback, user_data); ctx = g_slice_new0 (InternalLoadUnlockRequiredContext); ctx->max_retries = load_unlock_required_max_retries (self); g_task_set_task_data (task, ctx, (GDestroyNotify)internal_load_unlock_required_context_free); if (!MM_IFACE_MODEM_GET_INTERFACE (self)->load_unlock_required || !MM_IFACE_MODEM_GET_INTERFACE (self)->load_unlock_required_finish) { /* Just assume that no lock is required */ g_task_return_boolean (task, TRUE); g_object_unref (task); return; } internal_load_unlock_required_context_step (task); } /*****************************************************************************/ static void bearer_list_updated (MMBearerList *bearer_list, GParamSpec *pspec, MMIfaceModem *self) { MmGdbusModem *skeleton; gchar **paths; g_object_get (self, MM_IFACE_MODEM_DBUS_SKELETON, &skeleton, NULL); if (!skeleton) return; paths = mm_bearer_list_get_paths (bearer_list); mm_gdbus_modem_set_bearers (skeleton, (const gchar *const *)paths); g_strfreev (paths); g_dbus_interface_skeleton_flush (G_DBUS_INTERFACE_SKELETON (skeleton)); g_object_unref (skeleton); } /*****************************************************************************/ static MMModemState get_consolidated_subsystem_state (MMIfaceModem *self); typedef struct { MMBaseBearer *self; guint others_connected; } CountOthersConnectedContext; static void bearer_list_count_others_connected (MMBaseBearer *bearer, CountOthersConnectedContext *ctx) { /* We can safely compare pointers here */ if (bearer != ctx->self && mm_base_bearer_get_status (bearer) == MM_BEARER_STATUS_CONNECTED) { ctx->others_connected++; } } static void bearer_status_changed (MMBaseBearer *bearer, GParamSpec *pspec, MMIfaceModem *self) { CountOthersConnectedContext ctx; MMBearerList *list = NULL; MMModemState state = MM_MODEM_STATE_UNKNOWN; g_object_get (self, MM_IFACE_MODEM_STATE, &state, MM_IFACE_MODEM_BEARER_LIST, &list, NULL); if (!list) return; if (state == MM_MODEM_STATE_DISABLING || state == MM_MODEM_STATE_ENABLING) { /* Don't log modem bearer-specific status changes if we're disabling * or enabling */ g_object_unref (list); return; } ctx.self = bearer; ctx.others_connected = 0; /* We now count how many *other* bearers are connected */ mm_bearer_list_foreach (list, (MMBearerListForeachFunc)bearer_list_count_others_connected, &ctx); /* If no other bearers are connected, change modem state */ if (!ctx.others_connected) { MMModemState new_state = MM_MODEM_STATE_UNKNOWN; switch (mm_base_bearer_get_status (bearer)) { case MM_BEARER_STATUS_CONNECTED: new_state = MM_MODEM_STATE_CONNECTED; break; case MM_BEARER_STATUS_CONNECTING: new_state = MM_MODEM_STATE_CONNECTING; break; case MM_BEARER_STATUS_DISCONNECTING: new_state = MM_MODEM_STATE_DISCONNECTING; break; case MM_BEARER_STATUS_DISCONNECTED: new_state = get_consolidated_subsystem_state (self); break; default: g_assert_not_reached (); } mm_iface_modem_update_state (self, new_state, MM_MODEM_STATE_CHANGE_REASON_USER_REQUESTED); } g_object_unref (list); } typedef struct { MMBearerList *list; } CreateBearerContext; static void create_bearer_context_free (CreateBearerContext *ctx) { if (ctx->list) g_object_unref (ctx->list); g_slice_free (CreateBearerContext, ctx); } MMBaseBearer * mm_iface_modem_create_bearer_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { return g_task_propagate_pointer (G_TASK (res), error); } static void create_bearer_ready (MMIfaceModem *self, GAsyncResult *res, GTask *task) { CreateBearerContext *ctx; MMBaseBearer *bearer; GError *error = NULL; bearer = MM_IFACE_MODEM_GET_INTERFACE (self)->create_bearer_finish (self, res, &error); if (error) { g_task_return_error (task, error); g_object_unref (task); return; } ctx = g_task_get_task_data (task); if (!mm_bearer_list_add_bearer (ctx->list, bearer, &error)) { g_task_return_error (task, error); g_object_unref (task); g_object_unref (bearer); return; } /* If bearer properly created and added to the list, follow its * status */ g_signal_connect (bearer, "notify::" MM_BASE_BEARER_STATUS, (GCallback)bearer_status_changed, self); g_task_return_pointer (task, bearer, g_object_unref); g_object_unref (task); } void mm_iface_modem_create_bearer (MMIfaceModem *self, MMBearerProperties *properties, GAsyncReadyCallback callback, gpointer user_data) { CreateBearerContext *ctx; GTask *task; ctx = g_slice_new (CreateBearerContext); task = g_task_new (self, NULL, callback, user_data); g_task_set_task_data (task, ctx, (GDestroyNotify)create_bearer_context_free); g_object_get (self, MM_IFACE_MODEM_BEARER_LIST, &ctx->list, NULL); if (!ctx->list) { g_task_return_new_error ( task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Cannot add new bearer: bearer list not found"); g_object_unref (task); return; } MM_IFACE_MODEM_GET_INTERFACE (self)->create_bearer ( self, properties, (GAsyncReadyCallback)create_bearer_ready, task); } /*****************************************************************************/ typedef struct { MmGdbusModem *skeleton; GDBusMethodInvocation *invocation; MMIfaceModem *self; GVariant *dictionary; } HandleCreateBearerContext; static void handle_create_bearer_context_free (HandleCreateBearerContext *ctx) { g_variant_unref (ctx->dictionary); g_object_unref (ctx->skeleton); g_object_unref (ctx->invocation); g_object_unref (ctx->self); g_slice_free (HandleCreateBearerContext, ctx); } static void handle_create_bearer_ready (MMIfaceModem *self, GAsyncResult *res, HandleCreateBearerContext *ctx) { g_autoptr(MMBaseBearer) bearer = NULL; GError *error = NULL; bearer = mm_iface_modem_create_bearer_finish (self, res, &error); if (!bearer) { mm_obj_warn (self, "failed creating bearer: %s", error->message); mm_dbus_method_invocation_take_error (ctx->invocation, error); } else { mm_obj_info (self, "created bearer: %s", mm_base_bearer_get_path (bearer)); mm_gdbus_modem_complete_create_bearer (ctx->skeleton, ctx->invocation, mm_base_bearer_get_path (bearer)); } handle_create_bearer_context_free (ctx); } static void handle_create_bearer_auth_ready (MMBaseModem *self, GAsyncResult *res, HandleCreateBearerContext *ctx) { g_autoptr(MMBearerProperties) properties = NULL; GError *error = NULL; if (!mm_base_modem_authorize_finish (self, res, &error)) { mm_dbus_method_invocation_take_error (ctx->invocation, error); handle_create_bearer_context_free (ctx); return; } if (mm_iface_modem_abort_invocation_if_state_not_reached (ctx->self, ctx->invocation, MM_MODEM_STATE_LOCKED)) { handle_create_bearer_context_free (ctx); return; } properties = mm_bearer_properties_new_from_dictionary (ctx->dictionary, &error); if (!properties) { mm_dbus_method_invocation_take_error (ctx->invocation, error); handle_create_bearer_context_free (ctx); return; } mm_obj_info (self, "processing user request to create bearer..."); mm_log_bearer_properties (self, MM_LOG_LEVEL_INFO, " ", properties); mm_iface_modem_create_bearer ( ctx->self, properties, (GAsyncReadyCallback)handle_create_bearer_ready, ctx); } static gboolean handle_create_bearer (MmGdbusModem *skeleton, GDBusMethodInvocation *invocation, GVariant *dictionary, MMIfaceModem *self) { HandleCreateBearerContext *ctx; ctx = g_slice_new0 (HandleCreateBearerContext); ctx->skeleton = g_object_ref (skeleton); ctx->invocation = g_object_ref (invocation); ctx->self = g_object_ref (self); ctx->dictionary = g_variant_ref (dictionary); mm_base_modem_authorize (MM_BASE_MODEM (self), invocation, MM_AUTHORIZATION_DEVICE_CONTROL, (GAsyncReadyCallback)handle_create_bearer_auth_ready, ctx); return TRUE; } /*****************************************************************************/ typedef struct { MmGdbusModem *skeleton; GDBusMethodInvocation *invocation; MMIfaceModem *self; gchar *cmd; guint timeout; } HandleCommandContext; static void handle_command_context_free (HandleCommandContext *ctx) { g_object_unref (ctx->skeleton); g_object_unref (ctx->invocation); g_object_unref (ctx->self); g_free (ctx->cmd); g_slice_free (HandleCommandContext, ctx); } static void command_ready (MMIfaceModem *self, GAsyncResult *res, HandleCommandContext *ctx) { GError *error = NULL; const gchar *result; result = MM_IFACE_MODEM_GET_INTERFACE (self)->command_finish (self, res, &error); if (error) { mm_obj_dbg (self, "failed running AT command '%s': %s", ctx->cmd, error->message); mm_dbus_method_invocation_take_error (ctx->invocation, error); } else { mm_obj_dbg (self, "AT command '%s' run: %s", ctx->cmd, result); mm_gdbus_modem_complete_command (ctx->skeleton, ctx->invocation, result); } handle_command_context_free (ctx); } static void handle_command_auth_ready (MMBaseModem *self, GAsyncResult *res, HandleCommandContext *ctx) { GError *error = NULL; if (!mm_base_modem_authorize_finish (self, res, &error)) { mm_dbus_method_invocation_take_error (ctx->invocation, error); handle_command_context_free (ctx); return; } #if ! defined WITH_AT_COMMAND_VIA_DBUS /* If we are not in Debug mode, report an error */ if (!mm_context_get_debug ()) { mm_dbus_method_invocation_return_error_literal (ctx->invocation, MM_CORE_ERROR, MM_CORE_ERROR_UNAUTHORIZED, "Operation only allowed in debug mode"); handle_command_context_free (ctx); return; } #endif /* If command is not implemented, report an error */ if (!MM_IFACE_MODEM_GET_INTERFACE (self)->command || !MM_IFACE_MODEM_GET_INTERFACE (self)->command_finish) { mm_dbus_method_invocation_return_error_literal (ctx->invocation, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "Operation not supported"); handle_command_context_free (ctx); return; } mm_obj_dbg (self, "processing user request to run AT command '%s'...", ctx->cmd); MM_IFACE_MODEM_GET_INTERFACE (self)->command (ctx->self, ctx->cmd, ctx->timeout, (GAsyncReadyCallback)command_ready, ctx); } static gboolean handle_command (MmGdbusModem *skeleton, GDBusMethodInvocation *invocation, const gchar *cmd, guint timeout, MMIfaceModem *self) { HandleCommandContext *ctx; ctx = g_slice_new0 (HandleCommandContext); ctx->skeleton = g_object_ref (skeleton); ctx->invocation = g_object_ref (invocation); ctx->self = g_object_ref (self); ctx->cmd = g_strdup (cmd); ctx->timeout = timeout; mm_base_modem_authorize (MM_BASE_MODEM (self), invocation, MM_AUTHORIZATION_DEVICE_CONTROL, (GAsyncReadyCallback)handle_command_auth_ready, ctx); return TRUE; } /*****************************************************************************/ typedef struct { MmGdbusModem *skeleton; GDBusMethodInvocation *invocation; MMIfaceModem *self; MMBearerList *list; gchar *bearer_path; MMBaseBearer *bearer; } HandleDeleteBearerContext; static void handle_delete_bearer_context_free (HandleDeleteBearerContext *ctx) { g_object_unref (ctx->skeleton); g_object_unref (ctx->invocation); g_object_unref (ctx->self); g_clear_object (&ctx->bearer); g_clear_object (&ctx->list); g_free (ctx->bearer_path); g_slice_free (HandleDeleteBearerContext, ctx); } static void delete_bearer_disconnect_ready (MMBaseBearer *bearer, GAsyncResult *res, HandleDeleteBearerContext *ctx) { GError *error = NULL; if (!mm_base_bearer_disconnect_finish (bearer, res, &error)) { mm_obj_warn (ctx->self, "failed disconnecting bearer '%s' before deleting: %s", ctx->bearer_path, error->message); mm_dbus_method_invocation_take_error (ctx->invocation, error); handle_delete_bearer_context_free (ctx); return; } if (!mm_bearer_list_delete_bearer (ctx->list, ctx->bearer_path, &error)) { mm_obj_warn (ctx->self, "failed deleting bearer '%s': %s", ctx->bearer_path, error->message); mm_dbus_method_invocation_take_error (ctx->invocation, error); } else { mm_obj_info (ctx->self, "deleted bearer '%s'", ctx->bearer_path); mm_gdbus_modem_complete_delete_bearer (ctx->skeleton, ctx->invocation); } handle_delete_bearer_context_free (ctx); } static void handle_delete_bearer_auth_ready (MMBaseModem *self, GAsyncResult *res, HandleDeleteBearerContext *ctx) { GError *error = NULL; if (!mm_base_modem_authorize_finish (self, res, &error)) { mm_dbus_method_invocation_take_error (ctx->invocation, error); handle_delete_bearer_context_free (ctx); return; } if (mm_iface_modem_abort_invocation_if_state_not_reached (ctx->self, ctx->invocation, MM_MODEM_STATE_LOCKED)) { handle_delete_bearer_context_free (ctx); return; } if (!g_str_has_prefix (ctx->bearer_path, MM_DBUS_BEARER_PREFIX)) { mm_dbus_method_invocation_return_error (ctx->invocation, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS, "Invalid path '%s'", ctx->bearer_path); handle_delete_bearer_context_free (ctx); return; } ctx->bearer = mm_bearer_list_find_by_path (ctx->list, ctx->bearer_path); if (!ctx->bearer) { mm_dbus_method_invocation_return_error (ctx->invocation, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS, "No bearer found with path '%s'", ctx->bearer_path); handle_delete_bearer_context_free (ctx); return; } mm_obj_info (self, "processing user request to delete bearer '%s'...", ctx->bearer_path); mm_base_bearer_disconnect (ctx->bearer, (GAsyncReadyCallback)delete_bearer_disconnect_ready, ctx); } static gboolean handle_delete_bearer (MmGdbusModem *skeleton, GDBusMethodInvocation *invocation, const gchar *bearer, MMIfaceModem *self) { HandleDeleteBearerContext *ctx; ctx = g_slice_new0 (HandleDeleteBearerContext); ctx->skeleton = g_object_ref (skeleton); ctx->invocation = g_object_ref (invocation); ctx->self = g_object_ref (self); ctx->bearer_path = g_strdup (bearer); g_object_get (self, MM_IFACE_MODEM_BEARER_LIST, &ctx->list, NULL); mm_base_modem_authorize (MM_BASE_MODEM (self), invocation, MM_AUTHORIZATION_DEVICE_CONTROL, (GAsyncReadyCallback)handle_delete_bearer_auth_ready, ctx); return TRUE; } /*****************************************************************************/ static gboolean handle_list_bearers (MmGdbusModem *skeleton, GDBusMethodInvocation *invocation, MMIfaceModem *self) { g_auto(GStrv) paths = NULL; g_autoptr(MMBearerList) list = NULL; if (mm_iface_modem_abort_invocation_if_state_not_reached (self, invocation, MM_MODEM_STATE_LOCKED)) return TRUE; g_object_get (self, MM_IFACE_MODEM_BEARER_LIST, &list, NULL); if (!list) { mm_dbus_method_invocation_return_error_literal (invocation, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Bearer list not found"); return TRUE; } paths = mm_bearer_list_get_paths (list); mm_gdbus_modem_complete_list_bearers (skeleton, invocation, (const gchar *const *)paths); return TRUE; } /*****************************************************************************/ typedef struct { MmGdbusModem *skeleton; GDBusMethodInvocation *invocation; MMIfaceModem *self; guint requested_sim_slot; } HandleSetPrimarySimSlotContext; static void handle_set_primary_sim_slot_context_free (HandleSetPrimarySimSlotContext *ctx) { g_clear_object (&ctx->skeleton); g_clear_object (&ctx->invocation); g_clear_object (&ctx->self); g_slice_free (HandleSetPrimarySimSlotContext, ctx); } static void set_primary_sim_slot_ready (MMIfaceModem *self, GAsyncResult *res, HandleSetPrimarySimSlotContext *ctx) { g_autoptr(GError) error = NULL; if (!MM_IFACE_MODEM_GET_INTERFACE (self)->set_primary_sim_slot_finish (self, res, &error)) { /* If the implementation returns EXISTS, we're already in the requested SIM slot, * so we can safely return a success on the operation and skip the reprobing */ if (!g_error_matches (error, MM_CORE_ERROR, MM_CORE_ERROR_EXISTS)) { mm_obj_warn (self, "failed setting primary SIM slot '%u': %s", ctx->requested_sim_slot, error->message); mm_dbus_method_invocation_take_error (ctx->invocation, g_steal_pointer (&error)); handle_set_primary_sim_slot_context_free (ctx); return; } mm_obj_dbg (self, "ignored request to set primary SIM slot '%u': already set", ctx->requested_sim_slot); } else { /* Notify about the SIM swap, which will disable and reprobe the device. * There is no need to update the PrimarySimSlot property, as this value will be * reloaded automatically during the reprobe. */ mm_obj_info (self, "primary SIM slot '%u' set", ctx->requested_sim_slot); mm_iface_modem_process_sim_event (self); } mm_gdbus_modem_complete_set_primary_sim_slot (ctx->skeleton, ctx->invocation); handle_set_primary_sim_slot_context_free (ctx); } static void handle_set_primary_sim_slot_auth_ready (MMBaseModem *self, GAsyncResult *res, HandleSetPrimarySimSlotContext *ctx) { GError *error = NULL; const gchar *const *sim_slot_paths; if (!mm_base_modem_authorize_finish (self, res, &error)) { mm_dbus_method_invocation_take_error (ctx->invocation, error); handle_set_primary_sim_slot_context_free (ctx); return; } /* If SIM switching is not implemented, report an error */ if (!MM_IFACE_MODEM_GET_INTERFACE (self)->set_primary_sim_slot || !MM_IFACE_MODEM_GET_INTERFACE (self)->set_primary_sim_slot_finish) { mm_dbus_method_invocation_return_error_literal (ctx->invocation, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "Operation not supported"); handle_set_primary_sim_slot_context_free (ctx); return; } /* Validate SIM slot number */ sim_slot_paths = mm_gdbus_modem_get_sim_slots (ctx->skeleton); if (!sim_slot_paths || (ctx->requested_sim_slot > g_strv_length ((gchar **)sim_slot_paths))) { mm_dbus_method_invocation_return_error_literal (ctx->invocation, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS, "Requested SIM slot number is out of bounds"); handle_set_primary_sim_slot_context_free (ctx); return; } mm_obj_info (self, "processing user request to set primary SIM slot '%u'...", ctx->requested_sim_slot); MM_IFACE_MODEM_GET_INTERFACE (self)->set_primary_sim_slot (MM_IFACE_MODEM (self), ctx->requested_sim_slot, (GAsyncReadyCallback)set_primary_sim_slot_ready, ctx); } static gboolean handle_set_primary_sim_slot (MmGdbusModem *skeleton, GDBusMethodInvocation *invocation, guint sim_slot, MMIfaceModem *self) { HandleSetPrimarySimSlotContext *ctx; ctx = g_slice_new0 (HandleSetPrimarySimSlotContext); ctx->skeleton = g_object_ref (skeleton); ctx->invocation = g_object_ref (invocation); ctx->self = g_object_ref (self); ctx->requested_sim_slot = sim_slot; mm_base_modem_authorize (MM_BASE_MODEM (self), invocation, MM_AUTHORIZATION_DEVICE_CONTROL, (GAsyncReadyCallback)handle_set_primary_sim_slot_auth_ready, ctx); return TRUE; } /*****************************************************************************/ typedef struct { MmGdbusModem *skeleton; GDBusMethodInvocation *invocation; MMIfaceModem *self; } HandleGetCellInfoContext; static void handle_get_cell_info_context_free (HandleGetCellInfoContext *ctx) { g_object_unref (ctx->skeleton); g_object_unref (ctx->invocation); g_object_unref (ctx->self); g_slice_free (HandleGetCellInfoContext, ctx); } static GVariant * get_cell_info_build_result (GList *info_list) { GList *l; GVariantBuilder builder; g_variant_builder_init (&builder, G_VARIANT_TYPE ("aa{sv}")); for (l = info_list; l; l = g_list_next (l)) { g_autoptr(GVariant) dict = NULL; dict = mm_cell_info_get_dictionary (MM_CELL_INFO (l->data)); g_variant_builder_add_value (&builder, dict); } return g_variant_ref_sink (g_variant_builder_end (&builder)); } static void get_cell_info_ready (MMIfaceModem *self, GAsyncResult *res, HandleGetCellInfoContext *ctx) { GError *error = NULL; GList *info_list; info_list = MM_IFACE_MODEM_GET_INTERFACE (self)->get_cell_info_finish (self, res, &error); if (error) { mm_obj_dbg (self, "failed retrieving cell info: %s", error->message); mm_dbus_method_invocation_take_error (ctx->invocation, error); } else { g_autoptr(GVariant) dict_array = NULL; mm_obj_dbg (self, "cell info retrieved"); dict_array = get_cell_info_build_result (info_list); mm_gdbus_modem_complete_get_cell_info (ctx->skeleton, ctx->invocation, dict_array); } g_list_free_full (info_list, (GDestroyNotify)g_object_unref); handle_get_cell_info_context_free (ctx); } static void handle_get_cell_info_auth_ready (MMBaseModem *self, GAsyncResult *res, HandleGetCellInfoContext *ctx) { GError *error = NULL; if (!mm_base_modem_authorize_finish (self, res, &error)) { mm_dbus_method_invocation_take_error (ctx->invocation, error); handle_get_cell_info_context_free (ctx); return; } /* If getting cell info is not implemented, report an error */ if (!MM_IFACE_MODEM_GET_INTERFACE (self)->get_cell_info || !MM_IFACE_MODEM_GET_INTERFACE (self)->get_cell_info_finish) { mm_dbus_method_invocation_return_error_literal (ctx->invocation, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "Cannot get cell info: operation not supported"); handle_get_cell_info_context_free (ctx); return; } if (mm_iface_modem_abort_invocation_if_state_not_reached (ctx->self, ctx->invocation, MM_MODEM_STATE_ENABLED)) { handle_get_cell_info_context_free (ctx); return; } mm_obj_info (self, "processing user request to retrieve cell info..."); MM_IFACE_MODEM_GET_INTERFACE (self)->get_cell_info (ctx->self, (GAsyncReadyCallback)get_cell_info_ready, ctx); } static gboolean handle_get_cell_info (MmGdbusModem *skeleton, GDBusMethodInvocation *invocation, MMIfaceModem *self) { HandleGetCellInfoContext *ctx; ctx = g_slice_new0 (HandleGetCellInfoContext); ctx->skeleton = g_object_ref (skeleton); ctx->invocation = g_object_ref (invocation); ctx->self = g_object_ref (self); mm_base_modem_authorize (MM_BASE_MODEM (self), invocation, MM_AUTHORIZATION_DEVICE_CONTROL, (GAsyncReadyCallback)handle_get_cell_info_auth_ready, ctx); return TRUE; } /*****************************************************************************/ void mm_iface_modem_update_own_numbers (MMIfaceModem *self, const GStrv own_numbers) { MmGdbusModem *skeleton = NULL; g_object_get (self, MM_IFACE_MODEM_DBUS_SKELETON, &skeleton, NULL); if (skeleton) { mm_gdbus_modem_set_own_numbers (skeleton, (const gchar * const *)own_numbers); g_object_unref (skeleton); } } /*****************************************************************************/ void mm_iface_modem_update_access_technologies (MMIfaceModem *self, MMModemAccessTechnology new_access_tech, guint32 mask) { MmGdbusModem *skeleton = NULL; MMModemAccessTechnology old_access_tech; MMModemAccessTechnology built_access_tech; g_object_get (self, MM_IFACE_MODEM_DBUS_SKELETON, &skeleton, NULL); /* Don't process updates if the interface is shut down */ if (!skeleton) return; old_access_tech = mm_gdbus_modem_get_access_technologies (skeleton); /* Build the new access tech */ built_access_tech = old_access_tech; built_access_tech &= ~mask; built_access_tech |= new_access_tech; if (built_access_tech != old_access_tech) { gchar *old_access_tech_string; gchar *new_access_tech_string; mm_gdbus_modem_set_access_technologies (skeleton, built_access_tech); /* Log */ old_access_tech_string = mm_modem_access_technology_build_string_from_mask (old_access_tech); new_access_tech_string = mm_modem_access_technology_build_string_from_mask (built_access_tech); mm_obj_info (self, "access technology changed (%s -> %s)", old_access_tech_string, new_access_tech_string); g_free (old_access_tech_string); g_free (new_access_tech_string); } g_object_unref (skeleton); } /*****************************************************************************/ static void signal_quality_recent_timeout_disable (MMIfaceModem *self) { Private *priv; priv = get_private (self); if (priv->signal_quality_recent_timeout_source) { g_source_remove (priv->signal_quality_recent_timeout_source); priv->signal_quality_recent_timeout_source = 0; } } static gboolean expire_signal_quality (MMIfaceModem *self) { g_autoptr(MmGdbusModemSkeleton) skeleton = NULL; Private *priv; g_object_get (self, MM_IFACE_MODEM_DBUS_SKELETON, &skeleton, NULL); if (skeleton) { GVariant *old; guint signal_quality = 0; gboolean recent = FALSE; old = mm_gdbus_modem_get_signal_quality (MM_GDBUS_MODEM (skeleton)); g_variant_get (old, "(ub)", &signal_quality, &recent); /* If value is already not recent, we're done */ if (recent) { mm_obj_dbg (self, "signal quality value not updated in %us, marking as not being recent", SIGNAL_QUALITY_RECENT_TIMEOUT_SEC); mm_gdbus_modem_set_signal_quality (MM_GDBUS_MODEM (skeleton), g_variant_new ("(ub)", signal_quality, FALSE)); } } /* Remove source id */ priv = get_private (self); priv->signal_quality_recent_timeout_source = 0; return G_SOURCE_REMOVE; } static void update_signal_quality (MMIfaceModem *self, guint signal_quality, gboolean expire) { g_autoptr(MmGdbusModemSkeleton) skeleton = NULL; Private *priv; g_object_get (self, MM_IFACE_MODEM_DBUS_SKELETON, &skeleton, NULL); /* Don't process updates if the interface is shut down */ if (!skeleton) return; priv = get_private (self); /* Note: we always set the new value, even if the signal quality level * is the same, in order to provide an up to date 'recent' flag. * The only exception being if 'expire' is FALSE; in that case we assume * the value won't expire and therefore can be considered obsolete * already. */ mm_gdbus_modem_set_signal_quality (MM_GDBUS_MODEM (skeleton), g_variant_new ("(ub)", signal_quality, expire)); mm_obj_dbg (self, "signal quality updated (%u)", signal_quality); /* Remove any previous expiration refresh timeout */ if (priv->signal_quality_recent_timeout_source) { g_source_remove (priv->signal_quality_recent_timeout_source); priv->signal_quality_recent_timeout_source = 0; } /* If we got a new expirable value, setup new timeout */ if (expire) priv->signal_quality_recent_timeout_source = (g_timeout_add_seconds ( SIGNAL_QUALITY_RECENT_TIMEOUT_SEC, (GSourceFunc)expire_signal_quality, self)); } void mm_iface_modem_update_signal_quality (MMIfaceModem *self, guint signal_quality) { update_signal_quality (self, signal_quality, TRUE); } /*****************************************************************************/ /* Signal info (quality and access technology) polling */ typedef enum { SIGNAL_CHECK_STEP_FIRST, SIGNAL_CHECK_STEP_SIGNAL_QUALITY, SIGNAL_CHECK_STEP_ACCESS_TECHNOLOGIES, SIGNAL_CHECK_STEP_LAST, } SignalCheckStep; typedef struct { /* Values polled in this iteration */ guint signal_quality; MMModemAccessTechnology access_technologies; guint access_technologies_mask; /* Steps triggered when polling active */ SignalCheckStep running_step; } SignalCheckContext; static void periodic_signal_check_disable (MMIfaceModem *self, gboolean clear); static gboolean periodic_signal_check_run (MMIfaceModem *self); static void periodic_signal_check_step (GTask *task); static void periodic_signal_check_complete (GTask *task) { MMIfaceModem *self; Private *priv; self = g_task_get_source_object (task); priv = get_private (self); g_assert (priv->signal_check_running); priv->signal_check_running = FALSE; g_task_return_boolean (task, TRUE); g_object_unref (task); } static void load_access_technologies_ready (MMIfaceModem *self, GAsyncResult *res, GTask *task) { g_autoptr(GError) error = NULL; Private *priv; SignalCheckContext *ctx; priv = get_private (self); ctx = g_task_get_task_data (task); if (!MM_IFACE_MODEM_GET_INTERFACE (self)->load_access_technologies_finish ( self, res, &ctx->access_technologies, &ctx->access_technologies_mask, &error)) { /* Did the plugin report that polling access technology is unsupported? */ if (g_error_matches (error, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED)) { mm_obj_dbg (self, "polling to refresh access technologies is unsupported"); priv->access_technology_polling_supported = FALSE; } /* Ignore logging any message if the error is in 'in-progress' */ else if (!g_error_matches (error, MM_CORE_ERROR, MM_CORE_ERROR_IN_PROGRESS)) mm_obj_dbg (self, "couldn't refresh access technologies: %s", error->message); } /* We may have been disabled while this command was running. */ else if (priv->signal_check_enabled) mm_iface_modem_update_access_technologies (self, ctx->access_technologies, ctx->access_technologies_mask); /* Go on */ ctx->running_step++; periodic_signal_check_step (task); } static void load_signal_quality_ready (MMIfaceModem *self, GAsyncResult *res, GTask *task) { g_autoptr(GError) error = NULL; Private *priv; SignalCheckContext *ctx; priv = get_private (self); ctx = g_task_get_task_data (task); ctx->signal_quality = MM_IFACE_MODEM_GET_INTERFACE (self)->load_signal_quality_finish (self, res, &error); if (error) { /* Did the plugin report that polling signal quality is unsupported? */ if (g_error_matches (error, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED)) { mm_obj_dbg (self, "polling to refresh signal quality is unsupported"); priv->signal_quality_polling_supported = FALSE; } /* Ignore logging any message if the error is in 'in-progress' */ else if (!g_error_matches (error, MM_CORE_ERROR, MM_CORE_ERROR_IN_PROGRESS)) mm_obj_dbg (self, "couldn't refresh signal quality: %s", error->message); } /* We may have been disabled while this command was running. */ else if (priv->signal_check_enabled) update_signal_quality (self, ctx->signal_quality, TRUE); /* Go on */ ctx->running_step++; periodic_signal_check_step (task); } static void periodic_signal_check_step (GTask *task) { MMIfaceModem *self; Private *priv; SignalCheckContext *ctx; self = g_task_get_source_object (task); priv = get_private (self); ctx = g_task_get_task_data (task); switch (ctx->running_step) { case SIGNAL_CHECK_STEP_FIRST: ctx->running_step++; /* fall-through */ case SIGNAL_CHECK_STEP_SIGNAL_QUALITY: if (priv->signal_check_enabled && priv->signal_quality_polling_supported && (!priv->signal_check_initial_done || !priv->signal_quality_polling_disabled)) { MM_IFACE_MODEM_GET_INTERFACE (self)->load_signal_quality ( self, (GAsyncReadyCallback)load_signal_quality_ready, task); return; } ctx->running_step++; /* fall-through */ case SIGNAL_CHECK_STEP_ACCESS_TECHNOLOGIES: if (priv->signal_check_enabled && priv->access_technology_polling_supported && (!priv->signal_check_initial_done || !priv->access_technology_polling_disabled)) { MM_IFACE_MODEM_GET_INTERFACE (self)->load_access_technologies ( self, (GAsyncReadyCallback)load_access_technologies_ready, task); return; } ctx->running_step++; /* fall-through */ case SIGNAL_CHECK_STEP_LAST: /* If we have been disabled while we were running the steps, we don't * do anything else. */ if (!priv->signal_check_enabled) { mm_obj_dbg (self, "periodic signal quality and access technology checks not rescheduled: disabled"); periodic_signal_check_complete (task); return; } /* Schedule when we poll next time. * Initially we poll at a higher frequency until we get valid signal * quality and access technology values. As soon as we get them, OR if * we made too many retries at a high frequency, we fallback to the * slower polling. */ if (!priv->signal_check_initial_done) { gboolean signal_quality_ready; gboolean access_technology_ready; /* Signal quality is ready if unsupported or if we got a valid * value reported */ signal_quality_ready = (!priv->signal_quality_polling_supported || (ctx->signal_quality != 0)); /* Access technology is ready if unsupported or if we got a valid * value reported */ access_technology_ready = (!priv->access_technology_polling_supported || ((ctx->access_technologies & ctx->access_technologies_mask) != MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN)); priv->signal_check_initial_done = ((signal_quality_ready && access_technology_ready) || (--priv->signal_check_initial_retries == 0)); } /* After running the initial check, if both signal quality and access tech * loading are either disabled or unsupported, we'll stop polling completely, * because they may be loaded asynchronously by unsolicited messages */ if (priv->signal_check_initial_done && (!priv->signal_quality_polling_supported || priv->signal_quality_polling_disabled) && (!priv->access_technology_polling_supported || priv->access_technology_polling_disabled)) { mm_obj_dbg (self, "periodic signal quality and access technology checks not rescheduled: unneeded or unsupported"); periodic_signal_check_disable (self, FALSE); } else { mm_obj_dbg (self, "periodic signal quality and access technology checks scheduled"); g_assert (!priv->signal_check_timeout_source); priv->signal_check_timeout_source = g_timeout_add_seconds (priv->signal_check_initial_done ? SIGNAL_CHECK_TIMEOUT_SEC : SIGNAL_CHECK_INITIAL_TIMEOUT_SEC, (GSourceFunc) periodic_signal_check_run, self); } periodic_signal_check_complete (task); return; default: g_assert_not_reached (); } } static gboolean periodic_signal_check_run (MMIfaceModem *self) { GTask *task; SignalCheckContext *ctx; Private *priv; priv = get_private (self); task = g_task_new (self, NULL, NULL, NULL); ctx = g_new0 (SignalCheckContext, 1); ctx->running_step = SIGNAL_CHECK_STEP_FIRST; ctx->signal_quality = 0; ctx->access_technologies = MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN; ctx->access_technologies_mask = MM_MODEM_ACCESS_TECHNOLOGY_ANY; g_task_set_task_data (task, ctx, (GDestroyNotify) g_free); g_assert (!priv->signal_check_running); priv->signal_check_running = TRUE; periodic_signal_check_step (task); /* Reset the source id as we're removing the timeout source */ if (priv->signal_check_timeout_source) priv->signal_check_timeout_source = 0; return G_SOURCE_REMOVE; } void mm_iface_modem_refresh_signal (MMIfaceModem *self) { Private *priv; priv = get_private (self); /* Don't refresh polling if we're not enabled */ if (!priv->signal_check_enabled) { mm_obj_dbg (self, "periodic signal check refresh ignored: checks not enabled"); return; } /* Don't refresh if we're already doing it */ if (priv->signal_check_running) { mm_obj_dbg (self, "periodic signal check refresh ignored: check already running"); return; } mm_obj_dbg (self, "periodic signal check refresh requested"); /* Remove the scheduled timeout as we're going to refresh * right away */ if (priv->signal_check_timeout_source) { g_source_remove (priv->signal_check_timeout_source); priv->signal_check_timeout_source = 0; } /* Reset refresh rate and initial retries when we're asked to refresh signal * so that we poll at a higher frequency */ priv->signal_check_initial_retries = SIGNAL_CHECK_INITIAL_RETRIES; priv->signal_check_initial_done = FALSE; /* Start sequence */ periodic_signal_check_run (self); } static void periodic_signal_check_disable (MMIfaceModem *self, gboolean clear) { Private *priv; priv = get_private (self); if (!priv->signal_check_enabled) return; /* Clear access technology and signal quality */ if (clear) { update_signal_quality (self, 0, FALSE); mm_iface_modem_update_access_technologies (self, MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN, MM_MODEM_ACCESS_TECHNOLOGY_ANY); } /* Remove scheduled timeout */ if (priv->signal_check_timeout_source) { g_source_remove (priv->signal_check_timeout_source); priv->signal_check_timeout_source = 0; } priv->signal_check_enabled = FALSE; mm_obj_dbg (self, "periodic signal checks disabled"); } static void periodic_signal_check_enable (MMIfaceModem *self) { Private *priv; priv = get_private (self); /* If polling access technology and signal quality not supported, don't even * bother trying. */ if (!priv->signal_quality_polling_supported && !priv->access_technology_polling_supported) { mm_obj_dbg (self, "not enabling periodic signal checks: unsupported"); return; } /* Log and flag as enabled */ if (!priv->signal_check_enabled) { mm_obj_dbg (self, "periodic signal checks enabled"); priv->signal_check_enabled = TRUE; } /* And refresh, which will trigger the first check at high frequency */ mm_iface_modem_refresh_signal (self); } /*****************************************************************************/ static void bearer_list_count_connected (MMBaseBearer *bearer, guint *count) { if (mm_base_bearer_get_status (bearer) == MM_BEARER_STATUS_CONNECTED) (*count)++; } static void update_state_internal (MMIfaceModem *self, MMModemState new_state, MMModemStateChangeReason reason, MMModemStateFailedReason failed_reason) { MMModemState old_state = MM_MODEM_STATE_UNKNOWN; g_autoptr(MmGdbusModemSkeleton) skeleton = NULL; g_autoptr(MMBearerList) bearer_list = NULL; g_object_get (self, MM_IFACE_MODEM_STATE, &old_state, MM_IFACE_MODEM_DBUS_SKELETON, &skeleton, MM_IFACE_MODEM_BEARER_LIST, &bearer_list, NULL); if (!skeleton) return; /* While connected we don't want registration status changes to change * the modem's state away from CONNECTED. */ if ((new_state == MM_MODEM_STATE_ENABLED || new_state == MM_MODEM_STATE_SEARCHING || new_state == MM_MODEM_STATE_REGISTERED) && bearer_list && old_state > MM_MODEM_STATE_REGISTERED) { guint connected = 0; mm_bearer_list_foreach (bearer_list, (MMBearerListForeachFunc)bearer_list_count_connected, &connected); if (connected > 0) /* Don't update state */ new_state = old_state; } /* Enabled may really be searching or registered */ if (new_state == MM_MODEM_STATE_ENABLED) new_state = get_consolidated_subsystem_state (self); /* Update state only if different */ if (new_state != old_state) { mm_obj_msg (self, "state changed (%s -> %s)", mm_modem_state_get_string (old_state), mm_modem_state_get_string (new_state)); /* The property in the interface is bound to the property * in the skeleton, so just updating here is enough */ g_object_set (self, MM_IFACE_MODEM_STATE, new_state, NULL); /* Signal status change */ if (skeleton) { /* Set failure reason */ if (failed_reason != mm_gdbus_modem_get_state_failed_reason (MM_GDBUS_MODEM (skeleton))) mm_gdbus_modem_set_state_failed_reason (MM_GDBUS_MODEM (skeleton), failed_reason); /* Flush current change before signaling the state change, * so that clients get the proper state already in the * state-changed callback */ g_dbus_interface_skeleton_flush (G_DBUS_INTERFACE_SKELETON (skeleton)); mm_gdbus_modem_emit_state_changed (MM_GDBUS_MODEM (skeleton), old_state, new_state, reason); } /* If we go to a registered/connected state (from unregistered), setup * signal quality and access technologies periodic retrieval */ if (new_state >= MM_MODEM_STATE_REGISTERED && old_state < MM_MODEM_STATE_REGISTERED) periodic_signal_check_enable (self); /* If we go from a registered/connected state to unregistered, * cleanup signal quality retrieval */ else if (old_state >= MM_MODEM_STATE_REGISTERED && new_state < MM_MODEM_STATE_REGISTERED) periodic_signal_check_disable (self, TRUE); } } void mm_iface_modem_update_state (MMIfaceModem *self, MMModemState new_state, MMModemStateChangeReason reason) { if (new_state == MM_MODEM_STATE_FAILED) { mm_iface_modem_update_failed_state (self, MM_MODEM_STATE_FAILED_REASON_UNKNOWN); return; } update_state_internal (self, new_state, reason, MM_MODEM_STATE_FAILED_REASON_NONE); } void mm_iface_modem_update_failed_state (MMIfaceModem *self, MMModemStateFailedReason failed_reason) { update_state_internal (self, MM_MODEM_STATE_FAILED, MM_MODEM_STATE_CHANGE_REASON_FAILURE, failed_reason); } /*****************************************************************************/ typedef struct { gchar *subsystem; MMModemState state; } SubsystemState; static void subsystem_state_clear (SubsystemState *s) { g_free (s->subsystem); } static MMModemState get_consolidated_subsystem_state (MMIfaceModem *self) { /* Use as initial state ENABLED, which is the minimum one expected. Do not * use the old modem state as initial state, as that would disallow reporting * e.g. ENABLED if the old state was REGISTERED (as ENABLED < REGISTERED). */ MMModemState consolidated = MM_MODEM_STATE_ENABLED; Private *priv; priv = get_private (self); /* Build consolidated state, expected fixes are: * - Enabled (meaning unregistered) --> Searching|Registered * - Searching --> Registered */ if (priv->subsystem_states) { guint i; for (i = 0; i < priv->subsystem_states->len; i++) { SubsystemState *s; s = &g_array_index (priv->subsystem_states, SubsystemState, i); if (s->state > consolidated) consolidated = s->state; } } return consolidated; } static MMModemState get_updated_consolidated_state (MMIfaceModem *self, MMModemState modem_state, const gchar *subsystem, MMModemState subsystem_state) { guint i; Private *priv; priv = get_private (self); /* Reported subsystem states will be REGISTRATION-related. This means * that we would only expect a subset of the states being reported for * the subsystem. Warn if we get others */ g_warn_if_fail (subsystem_state == MM_MODEM_STATE_ENABLED || subsystem_state == MM_MODEM_STATE_SEARCHING || subsystem_state == MM_MODEM_STATE_REGISTERED); if (!priv->subsystem_states) { priv->subsystem_states = g_array_sized_new (FALSE, FALSE, sizeof (SubsystemState), 2); g_array_set_clear_func (priv->subsystem_states, (GDestroyNotify)subsystem_state_clear); } /* Store new subsystem state */ for (i = 0; i < priv->subsystem_states->len; i++) { SubsystemState *s; s = &g_array_index (priv->subsystem_states, SubsystemState, i); if (g_str_equal (s->subsystem, subsystem)) { s->state = subsystem_state; break; } } /* If not found, insert new element */ if (i == priv->subsystem_states->len) { SubsystemState s; mm_obj_dbg (self, "will start keeping track of state for subsystem '%s'", subsystem); s.subsystem = g_strdup (subsystem); s.state = subsystem_state; g_array_append_val (priv->subsystem_states, s); } return get_consolidated_subsystem_state (self); } void mm_iface_modem_update_subsystem_state (MMIfaceModem *self, const gchar *subsystem, MMModemState new_state, MMModemStateChangeReason reason) { MMModemState consolidated; MMModemState state = MM_MODEM_STATE_UNKNOWN; g_object_get (self, MM_IFACE_MODEM_STATE, &state, NULL); /* We may have different subsystems being handled (e.g. 3GPP and CDMA), and * the registration status value is unique, so if we get subsystem-specific * state updates, we'll need to merge all to get a consolidated one. */ consolidated = get_updated_consolidated_state (self, state, subsystem, new_state); /* Don't update registration-related states while disabling/enabling */ if (state == MM_MODEM_STATE_ENABLING || state == MM_MODEM_STATE_DISABLING) return; mm_iface_modem_update_state (self, consolidated, reason); } /*****************************************************************************/ typedef struct { MmGdbusModem *skeleton; GDBusMethodInvocation *invocation; MMIfaceModem *self; gboolean enable; } HandleEnableContext; static void handle_enable_context_free (HandleEnableContext *ctx) { g_object_unref (ctx->skeleton); g_object_unref (ctx->invocation); g_object_unref (ctx->self); g_slice_free (HandleEnableContext, ctx); } static void enable_ready (MMBaseModem *self, GAsyncResult *res, HandleEnableContext *ctx) { GError *error = NULL; if (ctx->enable) { if (!mm_base_modem_enable_finish (self, res, &error)) { mm_obj_warn (self, "failed enabling modem: %s", error->message); mm_dbus_method_invocation_take_error (ctx->invocation, error); } else { mm_obj_info (self, "enabled modem"); mm_gdbus_modem_complete_enable (ctx->skeleton, ctx->invocation); } } else { if (!mm_base_modem_disable_finish (self, res, &error)) { mm_obj_warn (self, "failed disabling modem: %s", error->message); mm_dbus_method_invocation_take_error (ctx->invocation, error); } else { mm_obj_info (self, "disabled modem"); mm_gdbus_modem_complete_enable (ctx->skeleton, ctx->invocation); } } handle_enable_context_free (ctx); } static void handle_enable_auth_ready (MMBaseModem *self, GAsyncResult *res, HandleEnableContext *ctx) { GError *error = NULL; if (!mm_base_modem_authorize_finish (self, res, &error)) { mm_dbus_method_invocation_take_error (ctx->invocation, error); handle_enable_context_free (ctx); return; } if (mm_iface_modem_abort_invocation_if_state_not_reached (ctx->self, ctx->invocation, MM_MODEM_STATE_LOCKED)) { handle_enable_context_free (ctx); return; } if (ctx->enable) { mm_obj_info (self, "processing user request to enable modem..."); mm_base_modem_enable (self, (GAsyncReadyCallback)enable_ready, ctx); } else { mm_obj_info (self, "processing user request to disable modem..."); mm_base_modem_disable (self, (GAsyncReadyCallback)enable_ready, ctx); } } static gboolean handle_enable (MmGdbusModem *skeleton, GDBusMethodInvocation *invocation, gboolean enable, MMIfaceModem *self) { HandleEnableContext *ctx; ctx = g_slice_new0 (HandleEnableContext); ctx->skeleton = g_object_ref (skeleton); ctx->invocation = g_object_ref (invocation); ctx->self = g_object_ref (self); ctx->enable = enable; mm_base_modem_authorize (MM_BASE_MODEM (self), invocation, MM_AUTHORIZATION_DEVICE_CONTROL, (GAsyncReadyCallback)handle_enable_auth_ready, ctx); return TRUE; } /*****************************************************************************/ typedef struct { MmGdbusModem *skeleton; GDBusMethodInvocation *invocation; MMIfaceModem *self; MMModemPowerState power_state; gboolean disable_after_update; GError *saved_error; } HandleSetPowerStateContext; static void handle_set_power_state_context_free (HandleSetPowerStateContext *ctx) { g_assert (!ctx->saved_error); g_object_unref (ctx->skeleton); g_object_unref (ctx->invocation); g_object_unref (ctx->self); g_slice_free (HandleSetPowerStateContext, ctx); } static void disable_after_low_ready (MMBaseModem *self, GAsyncResult *res, HandleSetPowerStateContext *ctx) { g_autoptr(GError) error = NULL; if (!mm_base_modem_disable_finish (self, res, &error)) mm_obj_warn (self, "failed disabling modem during low-power mode sequence: %s", error->message); if (ctx->saved_error) mm_dbus_method_invocation_take_error (ctx->invocation, g_steal_pointer (&ctx->saved_error)); else if (error) mm_dbus_method_invocation_take_error (ctx->invocation, g_steal_pointer (&error)); else { mm_obj_info (self, "disabled modem"); mm_gdbus_modem_complete_set_power_state (ctx->skeleton, ctx->invocation); } handle_set_power_state_context_free (ctx); } static void disable_after_low (MMIfaceModem *self, HandleSetPowerStateContext *ctx) { mm_obj_info (self, "automatically disable modem after low-power mode..."); mm_base_modem_disable (MM_BASE_MODEM (self), (GAsyncReadyCallback)disable_after_low_ready, ctx); } static void set_power_state_ready (MMIfaceModem *self, GAsyncResult *res, HandleSetPowerStateContext *ctx) { GError *error = NULL; if (!mm_iface_modem_set_power_state_finish (self, res, &error)) { mm_obj_warn (self, "failed setting power state '%s': %s", mm_modem_power_state_get_string (ctx->power_state), error->message); if (ctx->disable_after_update) { ctx->saved_error = error; disable_after_low (self, ctx); return; } mm_dbus_method_invocation_take_error (ctx->invocation, error); handle_set_power_state_context_free (ctx); return; } mm_obj_info (self, "set power state '%s'", mm_modem_power_state_get_string (ctx->power_state)); if (ctx->disable_after_update) { disable_after_low (self, ctx); return; } mm_gdbus_modem_complete_set_power_state (ctx->skeleton, ctx->invocation); handle_set_power_state_context_free (ctx); } static void handle_set_power_state_auth_ready (MMBaseModem *self, GAsyncResult *res, HandleSetPowerStateContext *ctx) { MMModemState modem_state; GError *error = NULL; if (!mm_base_modem_authorize_finish (self, res, &error)) { mm_dbus_method_invocation_take_error (ctx->invocation, error); handle_set_power_state_context_free (ctx); return; } /* Only 'off', 'low' or 'up' expected */ if (ctx->power_state != MM_MODEM_POWER_STATE_LOW && ctx->power_state != MM_MODEM_POWER_STATE_ON && ctx->power_state != MM_MODEM_POWER_STATE_OFF) { mm_dbus_method_invocation_return_error (ctx->invocation, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS, "Unknown power state: '%s'", mm_modem_power_state_get_string (ctx->power_state)); handle_set_power_state_context_free (ctx); return; } modem_state = MM_MODEM_STATE_UNKNOWN; g_object_get (self, MM_IFACE_MODEM_STATE, &modem_state, NULL); /* Going into LOW is allowed even when enabled or connected, the modem will automatically * transition to disabled state in that case. */ if (ctx->power_state == MM_MODEM_POWER_STATE_LOW && modem_state > MM_MODEM_STATE_DISABLED) { mm_obj_info (self, "will automatically disable after setting low-power mode"); ctx->disable_after_update = TRUE; } /* Going into ON only allowed in disabled and failed states */ if (ctx->power_state == MM_MODEM_POWER_STATE_ON && modem_state != MM_MODEM_STATE_FAILED && modem_state != MM_MODEM_STATE_DISABLED) { mm_dbus_method_invocation_return_error_literal (ctx->invocation, MM_CORE_ERROR, MM_CORE_ERROR_WRONG_STATE, "Must be in disabled or failed state"); handle_set_power_state_context_free (ctx); return; } /* Going into OFF, only allowed if locked, disabled or failed */ if (ctx->power_state == MM_MODEM_POWER_STATE_OFF && modem_state != MM_MODEM_STATE_FAILED && modem_state != MM_MODEM_STATE_LOCKED && modem_state != MM_MODEM_STATE_DISABLED) { mm_dbus_method_invocation_return_error_literal (ctx->invocation, MM_CORE_ERROR, MM_CORE_ERROR_WRONG_STATE, "Modem either enabled or initializing"); handle_set_power_state_context_free (ctx); return; } mm_obj_info (self, "processing user request to set power state '%s'...", mm_modem_power_state_get_string (ctx->power_state)); mm_iface_modem_set_power_state (MM_IFACE_MODEM (self), ctx->power_state, (GAsyncReadyCallback)set_power_state_ready, ctx); } static gboolean handle_set_power_state (MmGdbusModem *skeleton, GDBusMethodInvocation *invocation, guint32 power_state, MMIfaceModem *self) { HandleSetPowerStateContext *ctx; ctx = g_slice_new0 (HandleSetPowerStateContext); ctx->skeleton = g_object_ref (skeleton); ctx->invocation = g_object_ref (invocation); ctx->self = g_object_ref (self); ctx->power_state = (MMModemPowerState)power_state; mm_base_modem_authorize (MM_BASE_MODEM (self), invocation, MM_AUTHORIZATION_DEVICE_CONTROL, (GAsyncReadyCallback)handle_set_power_state_auth_ready, ctx); return TRUE; } /*****************************************************************************/ typedef struct { MmGdbusModem *skeleton; GDBusMethodInvocation *invocation; MMIfaceModem *self; } HandleResetContext; static void handle_reset_context_free (HandleResetContext *ctx) { g_object_unref (ctx->skeleton); g_object_unref (ctx->invocation); g_object_unref (ctx->self); g_slice_free (HandleResetContext, ctx); } static void handle_reset_ready (MMIfaceModem *self, GAsyncResult *res, HandleResetContext *ctx) { GError *error = NULL; if (!MM_IFACE_MODEM_GET_INTERFACE (self)->reset_finish (self, res, &error)) { mm_obj_warn (self, "failed requesting modem reset: %s", error->message); mm_dbus_method_invocation_take_error (ctx->invocation, error); } else { mm_obj_info (self, "modem reset requested"); mm_gdbus_modem_complete_reset (ctx->skeleton, ctx->invocation); } handle_reset_context_free (ctx); } static void handle_reset_auth_ready (MMBaseModem *self, GAsyncResult *res, HandleResetContext *ctx) { GError *error = NULL; if (!mm_base_modem_authorize_finish (self, res, &error)) { mm_dbus_method_invocation_take_error (ctx->invocation, error); handle_reset_context_free (ctx); return; } /* If reseting is not implemented, report an error */ if (!MM_IFACE_MODEM_GET_INTERFACE (self)->reset || !MM_IFACE_MODEM_GET_INTERFACE (self)->reset_finish) { mm_dbus_method_invocation_return_error_literal (ctx->invocation, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "Operation not supported"); handle_reset_context_free (ctx); return; } mm_obj_info (self, "processing user request to reset modem..."); MM_IFACE_MODEM_GET_INTERFACE (self)->reset (MM_IFACE_MODEM (self), (GAsyncReadyCallback)handle_reset_ready, ctx); } static gboolean handle_reset (MmGdbusModem *skeleton, GDBusMethodInvocation *invocation, MMIfaceModem *self) { HandleResetContext *ctx; ctx = g_slice_new0 (HandleResetContext); ctx->skeleton = g_object_ref (skeleton); ctx->invocation = g_object_ref (invocation); ctx->self = g_object_ref (self); mm_base_modem_authorize (MM_BASE_MODEM (self), invocation, MM_AUTHORIZATION_DEVICE_CONTROL, (GAsyncReadyCallback)handle_reset_auth_ready, ctx); return TRUE; } /*****************************************************************************/ typedef struct { MmGdbusModem *skeleton; GDBusMethodInvocation *invocation; MMIfaceModem *self; gchar *code; } HandleFactoryResetContext; static void handle_factory_reset_context_free (HandleFactoryResetContext *ctx) { g_object_unref (ctx->skeleton); g_object_unref (ctx->invocation); g_object_unref (ctx->self); g_free (ctx->code); g_slice_free (HandleFactoryResetContext, ctx); } static void handle_factory_reset_ready (MMIfaceModem *self, GAsyncResult *res, HandleFactoryResetContext *ctx) { GError *error = NULL; if (!MM_IFACE_MODEM_GET_INTERFACE (self)->factory_reset_finish (self, res, &error)) { mm_obj_warn (self, "failed requesting modem factory reset: %s", error->message); mm_dbus_method_invocation_take_error (ctx->invocation, error); } else { mm_obj_info (self, "modem factory reset requested"); mm_gdbus_modem_complete_factory_reset (ctx->skeleton, ctx->invocation); } handle_factory_reset_context_free (ctx); } static void handle_factory_reset_auth_ready (MMBaseModem *self, GAsyncResult *res, HandleFactoryResetContext *ctx) { GError *error = NULL; if (!mm_base_modem_authorize_finish (self, res, &error)) { mm_dbus_method_invocation_take_error (ctx->invocation, error); handle_factory_reset_context_free (ctx); return; } /* If reseting is not implemented, report an error */ if (!MM_IFACE_MODEM_GET_INTERFACE (self)->factory_reset || !MM_IFACE_MODEM_GET_INTERFACE (self)->factory_reset_finish) { mm_dbus_method_invocation_return_error_literal (ctx->invocation, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "Operation not supported"); handle_factory_reset_context_free (ctx); return; } mm_obj_info (self, "processing user request to factory reset modem..."); MM_IFACE_MODEM_GET_INTERFACE (self)->factory_reset (MM_IFACE_MODEM (self), ctx->code, (GAsyncReadyCallback)handle_factory_reset_ready, ctx); } static gboolean handle_factory_reset (MmGdbusModem *skeleton, GDBusMethodInvocation *invocation, const gchar *code, MMIfaceModem *self) { HandleFactoryResetContext *ctx; ctx = g_slice_new0 (HandleFactoryResetContext); ctx->skeleton = g_object_ref (skeleton); ctx->invocation = g_object_ref (invocation); ctx->self = g_object_ref (self); ctx->code = g_strdup (code); mm_base_modem_authorize (MM_BASE_MODEM (self), invocation, MM_AUTHORIZATION_DEVICE_CONTROL, (GAsyncReadyCallback)handle_factory_reset_auth_ready, ctx); return TRUE; } /*****************************************************************************/ /* Current capabilities setting * * Setting capabilities allowed also in FAILED state. Just imagine a * 3GPP+3GPP2 modem in 3GPP-only mode without SIM, we should allow * changing caps to 3GPP2, which doesn't require SIM */ typedef struct { MmGdbusModem *skeleton; GDBusMethodInvocation *invocation; MMIfaceModem *self; MMModemCapability capabilities; gchar *capabilities_str; } HandleSetCurrentCapabilitiesContext; static void handle_set_current_capabilities_context_free (HandleSetCurrentCapabilitiesContext *ctx) { g_free (ctx->capabilities_str); g_object_unref (ctx->skeleton); g_object_unref (ctx->invocation); g_object_unref (ctx->self); g_slice_free (HandleSetCurrentCapabilitiesContext, ctx); } static void set_current_capabilities_ready (MMIfaceModem *self, GAsyncResult *res, HandleSetCurrentCapabilitiesContext *ctx) { GError *error = NULL; if (!MM_IFACE_MODEM_GET_INTERFACE (self)->set_current_capabilities_finish (self, res, &error)) { mm_obj_warn (self, "failed setting current capabilities to '%s': %s", ctx->capabilities_str, error->message); mm_dbus_method_invocation_take_error (ctx->invocation, error); } else { /* Capabilities updated: explicitly refresh signal and access technology */ mm_iface_modem_refresh_signal (self); mm_obj_info (self, "current capabilities set to '%s'", ctx->capabilities_str); mm_gdbus_modem_complete_set_current_capabilities (ctx->skeleton, ctx->invocation); } handle_set_current_capabilities_context_free (ctx); } static void handle_set_current_capabilities_auth_ready (MMBaseModem *self, GAsyncResult *res, HandleSetCurrentCapabilitiesContext *ctx) { GError *error = NULL; g_autoptr(GArray) supported = NULL; gboolean matched = FALSE; guint i; if (!mm_base_modem_authorize_finish (self, res, &error)) { mm_dbus_method_invocation_take_error (ctx->invocation, error); handle_set_current_capabilities_context_free (ctx); return; } /* Nothing to do if we already are in the requested setup */ if (mm_gdbus_modem_get_current_capabilities (ctx->skeleton) == ctx->capabilities) { mm_gdbus_modem_complete_set_current_capabilities (ctx->skeleton, ctx->invocation); handle_set_current_capabilities_context_free (ctx); return; } /* If setting current capabilities is not implemented, report an error */ if (!MM_IFACE_MODEM_GET_INTERFACE (self)->set_current_capabilities || !MM_IFACE_MODEM_GET_INTERFACE (self)->set_current_capabilities_finish) { mm_dbus_method_invocation_return_error_literal (ctx->invocation, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "Setting current capabilities not supported"); handle_set_current_capabilities_context_free (ctx); return; } /* Get list of supported capabilities */ supported = mm_common_capability_combinations_variant_to_garray (mm_gdbus_modem_get_supported_capabilities (ctx->skeleton)); /* Don't allow capability switching if only one item given in the supported list */ if (supported->len == 1) { mm_dbus_method_invocation_return_error_literal (ctx->invocation, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "Cannot change capabilities: only one combination supported"); handle_set_current_capabilities_context_free (ctx); return; } /* Check if the given combination is supported */ for (i = 0; !matched && i < supported->len; i++) { MMModemCapability supported_capability; supported_capability = g_array_index (supported, MMModemCapability, i); if (supported_capability == ctx->capabilities) matched = TRUE; } if (!matched) { mm_dbus_method_invocation_return_error_literal (ctx->invocation, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "The given combination of capabilities is not supported"); handle_set_current_capabilities_context_free (ctx); return; } ctx->capabilities_str = mm_modem_capability_build_string_from_mask (ctx->capabilities); mm_obj_info (self, "processing user request to set current capabilities to '%s'...", ctx->capabilities_str); MM_IFACE_MODEM_GET_INTERFACE (self)->set_current_capabilities ( MM_IFACE_MODEM (self), ctx->capabilities, (GAsyncReadyCallback)set_current_capabilities_ready, ctx); } static gboolean handle_set_current_capabilities (MmGdbusModem *skeleton, GDBusMethodInvocation *invocation, guint capabilities, MMIfaceModem *self) { HandleSetCurrentCapabilitiesContext *ctx; ctx = g_slice_new0 (HandleSetCurrentCapabilitiesContext); ctx->skeleton = g_object_ref (skeleton); ctx->invocation = g_object_ref (invocation); ctx->self = g_object_ref (self); ctx->capabilities = capabilities; mm_base_modem_authorize (MM_BASE_MODEM (self), invocation, MM_AUTHORIZATION_DEVICE_CONTROL, (GAsyncReadyCallback)handle_set_current_capabilities_auth_ready, ctx); return TRUE; } /*****************************************************************************/ /* Current bands setting */ #define AFTER_SET_LOAD_CURRENT_BANDS_RETRIES 5 #define AFTER_SET_LOAD_CURRENT_BANDS_TIMEOUT_SECS 1 typedef struct { MmGdbusModem *skeleton; GArray *bands_array; GArray *supported_bands_array; /* when ANY requested */ guint retries; } SetCurrentBandsContext; static void set_current_bands_context_free (SetCurrentBandsContext *ctx) { if (ctx->skeleton) g_object_unref (ctx->skeleton); if (ctx->bands_array) g_array_unref (ctx->bands_array); if (ctx->supported_bands_array) g_array_unref (ctx->supported_bands_array); g_slice_free (SetCurrentBandsContext, ctx); } gboolean mm_iface_modem_set_current_bands_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void set_current_bands_complete_with_defaults (GTask *task) { SetCurrentBandsContext *ctx; ctx = g_task_get_task_data (task); /* Never show just 'any' in the interface */ if (ctx->bands_array->len == 1 && g_array_index (ctx->bands_array, MMModemBand, 0) == MM_MODEM_BAND_ANY) { g_assert (ctx->supported_bands_array); g_array_unref (ctx->bands_array); ctx->bands_array = g_array_ref (ctx->supported_bands_array); } mm_common_bands_garray_sort (ctx->bands_array); mm_gdbus_modem_set_current_bands (ctx->skeleton, mm_common_bands_garray_to_variant (ctx->bands_array)); g_task_return_boolean (task, TRUE); g_object_unref (task); } static void set_current_bands_reload_schedule (GTask *task); static void after_set_load_current_bands_ready (MMIfaceModem *self, GAsyncResult *res, GTask *task) { SetCurrentBandsContext *ctx; GArray *current_bands; GError *error = NULL; GArray *requested_bands = NULL; ctx = g_task_get_task_data (task); current_bands = MM_IFACE_MODEM_GET_INTERFACE (self)->load_current_bands_finish (self, res, &error); if (!current_bands) { /* If we can retry, do it */ if (ctx->retries > 0) { mm_obj_dbg (self, "couldn't load current bands: %s (will retry)", error->message); g_clear_error (&error); set_current_bands_reload_schedule (task); goto out; } /* Errors when reloading bands won't be critical */ mm_obj_warn (self, "couldn't load current bands: %s", error->message); g_clear_error (&error); set_current_bands_complete_with_defaults (task); goto out; } if ((ctx->bands_array->len == 1) && (g_array_index (ctx->bands_array, MMModemBand, 0) == MM_MODEM_BAND_ANY)) requested_bands = g_array_ref (ctx->supported_bands_array); else requested_bands = g_array_ref (ctx->bands_array); /* Compare arrays */ if (!mm_common_bands_garray_cmp (current_bands, requested_bands)) { gchar *requested_str; gchar *current_str; /* If we can retry, do it */ if (ctx->retries > 0) { mm_obj_dbg (self, "reloaded current bands different to the requested ones (will retry)"); set_current_bands_reload_schedule (task); goto out; } requested_str = mm_common_build_bands_string ((const MMModemBand *)(gconstpointer)requested_bands->data, requested_bands->len); current_str = mm_common_build_bands_string ((const MMModemBand *)(gconstpointer)current_bands->data, current_bands->len); error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "reloaded current bands (%s) different to the requested ones (%s)", current_str, requested_str); g_free (requested_str); g_free (current_str); } /* Store as current the last loaded ones and set operation result */ mm_common_bands_garray_sort (current_bands); mm_gdbus_modem_set_current_bands (ctx->skeleton, mm_common_bands_garray_to_variant (current_bands)); if (error) g_task_return_error (task, error); else g_task_return_boolean (task, TRUE); g_object_unref (task); out: g_array_unref (requested_bands); g_array_unref (current_bands); } static gboolean set_current_bands_reload (GTask *task) { MMIfaceModem *self; SetCurrentBandsContext *ctx; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); g_assert (ctx->retries > 0); ctx->retries--; MM_IFACE_MODEM_GET_INTERFACE (self)->load_current_bands ( self, (GAsyncReadyCallback)after_set_load_current_bands_ready, task); return G_SOURCE_REMOVE; } static void set_current_bands_reload_schedule (GTask *task) { g_timeout_add_seconds (AFTER_SET_LOAD_CURRENT_BANDS_TIMEOUT_SECS, (GSourceFunc) set_current_bands_reload, task); } static void set_current_bands_ready (MMIfaceModem *self, GAsyncResult *res, GTask *task) { GError *error = NULL; if (!MM_IFACE_MODEM_GET_INTERFACE (self)->set_current_bands_finish (self, res, &error)) { g_task_return_error (task, error); g_object_unref (task); return; } if (MM_IFACE_MODEM_GET_INTERFACE (self)->load_current_bands && MM_IFACE_MODEM_GET_INTERFACE (self)->load_current_bands_finish) { set_current_bands_reload (task); return; } /* Default to the ones we requested */ set_current_bands_complete_with_defaults (task); } static gboolean validate_bands (const GArray *supported_bands_array, const GArray *bands_array, GError **error) { /* When the array has more than one element, there MUST NOT include ANY or * UNKNOWN */ if (bands_array->len > 1) { guint i; for (i = 0; i < bands_array->len; i++) { MMModemBand band; band = g_array_index (bands_array, MMModemBand, i); if (band == MM_MODEM_BAND_UNKNOWN || band == MM_MODEM_BAND_ANY) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS, "Wrong list of bands: " "'%s' should have been the only element in the list", mm_modem_band_get_string (band)); return FALSE; } if (supported_bands_array->len > 1 || (g_array_index (supported_bands_array, MMModemBand, 0) != MM_MODEM_BAND_ANY && g_array_index (supported_bands_array, MMModemBand, 0) != MM_MODEM_BAND_UNKNOWN)) { gboolean found = FALSE; guint j; /* The band given in allowed MUST be available in supported */ for (j = 0; !found && j < supported_bands_array->len; j++) { if (band == g_array_index (supported_bands_array, MMModemBand, j)) found = TRUE; } if (!found) { gchar *supported_bands_str; supported_bands_str = (mm_common_build_bands_string ( (const MMModemBand *)(gconstpointer)supported_bands_array->data, supported_bands_array->len)); g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS, "Given allowed band (%s) is not supported (%s)", mm_modem_band_get_string (band), supported_bands_str); g_free (supported_bands_str); return FALSE; } } } } return TRUE; } void mm_iface_modem_set_current_bands (MMIfaceModem *self, GArray *bands_array, GAsyncReadyCallback callback, gpointer user_data) { SetCurrentBandsContext *ctx; GArray *current_bands_array; GError *error = NULL; gchar *bands_string; GTask *task; /* If setting allowed bands is not implemented, report an error */ if (!MM_IFACE_MODEM_GET_INTERFACE (self)->set_current_bands || !MM_IFACE_MODEM_GET_INTERFACE (self)->set_current_bands_finish) { g_task_report_new_error (self, callback, user_data, mm_iface_modem_set_current_bands, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "Setting allowed bands not supported"); return; } /* Setup context */ ctx = g_slice_new0 (SetCurrentBandsContext); ctx->retries = AFTER_SET_LOAD_CURRENT_BANDS_RETRIES; task = g_task_new (self, NULL, callback, user_data); g_task_set_task_data (task, ctx, (GDestroyNotify)set_current_bands_context_free); g_object_get (self, MM_IFACE_MODEM_DBUS_SKELETON, &ctx->skeleton, NULL); if (!ctx->skeleton) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't get interface skeleton"); g_object_unref (task); return; } bands_string = mm_common_build_bands_string ((const MMModemBand *)(gpointer)bands_array->data, bands_array->len); /* Get list of supported bands */ ctx->supported_bands_array = (mm_common_bands_variant_to_garray ( mm_gdbus_modem_get_supported_bands (ctx->skeleton))); /* Set ctx->bands_array to target list of bands before comparing with current list * of bands. If input list of bands contains only ANY, target list of bands is set * to list of supported bands excluding ANY. */ if (bands_array->len == 1 && g_array_index (bands_array, MMModemBand, 0) == MM_MODEM_BAND_ANY) { guint i; for (i = 0; i < ctx->supported_bands_array->len; i++) { MMModemBand band = g_array_index (ctx->supported_bands_array, MMModemBand, i); if (band != MM_MODEM_BAND_ANY && band != MM_MODEM_BAND_UNKNOWN) { if (!ctx->bands_array) ctx->bands_array = g_array_sized_new (FALSE, FALSE, sizeof (MMModemBand), ctx->supported_bands_array->len); g_array_append_val (ctx->bands_array, band); } } } if (!ctx->bands_array) ctx->bands_array = g_array_ref (bands_array); /* Simply return if target list of bands equals to current list of bands */ current_bands_array = (mm_common_bands_variant_to_garray ( mm_gdbus_modem_get_current_bands (ctx->skeleton))); if (mm_common_bands_garray_cmp (ctx->bands_array, current_bands_array)) { mm_obj_dbg (self, "requested list of bands (%s) is equal to the current ones, skipping re-set", bands_string); g_free (bands_string); g_array_unref (current_bands_array); g_task_return_boolean (task, TRUE); g_object_unref (task); return; } /* Done comparison with current list of bands. Always use input list of bands * when setting bands */ if (ctx->bands_array != bands_array) { g_array_unref (ctx->bands_array); ctx->bands_array = g_array_ref (bands_array); } /* Validate input list of bands */ if (!validate_bands (ctx->supported_bands_array, ctx->bands_array, &error)) { mm_obj_dbg (self, "requested list of bands (%s) cannot be handled", bands_string); g_free (bands_string); g_array_unref (current_bands_array); g_task_return_error (task, error); g_object_unref (task); return; } mm_obj_dbg (self, "setting new list of bands: %s", bands_string); MM_IFACE_MODEM_GET_INTERFACE (self)->set_current_bands ( self, ctx->bands_array, (GAsyncReadyCallback)set_current_bands_ready, task); g_array_unref (current_bands_array); g_free (bands_string); } /*****************************************************************************/ typedef struct { MmGdbusModem *skeleton; GDBusMethodInvocation *invocation; MMIfaceModem *self; GVariant *bands; gchar *bands_str; } HandleSetCurrentBandsContext; static void handle_set_current_bands_context_free (HandleSetCurrentBandsContext *ctx) { g_free (ctx->bands_str); g_variant_unref (ctx->bands); g_object_unref (ctx->skeleton); g_object_unref (ctx->invocation); g_object_unref (ctx->self); g_slice_free (HandleSetCurrentBandsContext, ctx); } static void handle_set_current_bands_ready (MMIfaceModem *self, GAsyncResult *res, HandleSetCurrentBandsContext *ctx) { GError *error = NULL; if (!mm_iface_modem_set_current_bands_finish (self, res, &error)) { mm_obj_warn (self, "failed setting current bands to '%s': %s", ctx->bands_str, error->message); mm_dbus_method_invocation_take_error (ctx->invocation, error); } else { /* Bands updated: explicitly refresh signal and access technology */ mm_iface_modem_refresh_signal (self); mm_obj_info (self, "current bands set to '%s'", ctx->bands_str); mm_gdbus_modem_complete_set_current_bands (ctx->skeleton, ctx->invocation); } handle_set_current_bands_context_free (ctx); } static void handle_set_current_bands_auth_ready (MMBaseModem *self, GAsyncResult *res, HandleSetCurrentBandsContext *ctx) { g_autoptr(GArray) bands_array = NULL; GError *error = NULL; if (!mm_base_modem_authorize_finish (self, res, &error)) { mm_dbus_method_invocation_take_error (ctx->invocation, error); handle_set_current_bands_context_free (ctx); return; } if (mm_iface_modem_abort_invocation_if_state_not_reached (ctx->self, ctx->invocation, MM_MODEM_STATE_DISABLED)) { handle_set_current_bands_context_free (ctx); return; } bands_array = mm_common_bands_variant_to_garray (ctx->bands); ctx->bands_str = mm_common_build_bands_string ((const MMModemBand *)bands_array->data, bands_array->len); mm_obj_info (self, "processing user request to set current bands to '%s'...", ctx->bands_str); mm_iface_modem_set_current_bands (MM_IFACE_MODEM (self), bands_array, (GAsyncReadyCallback)handle_set_current_bands_ready, ctx); } static gboolean handle_set_current_bands (MmGdbusModem *skeleton, GDBusMethodInvocation *invocation, GVariant *bands_variant, MMIfaceModem *self) { HandleSetCurrentBandsContext *ctx; ctx = g_slice_new0 (HandleSetCurrentBandsContext); ctx->skeleton = g_object_ref (skeleton); ctx->invocation = g_object_ref (invocation); ctx->self = g_object_ref (self); ctx->bands = g_variant_ref (bands_variant); mm_base_modem_authorize (MM_BASE_MODEM (self), invocation, MM_AUTHORIZATION_DEVICE_CONTROL, (GAsyncReadyCallback)handle_set_current_bands_auth_ready, ctx); return TRUE; } /*****************************************************************************/ /* Set current modes */ #define AFTER_SET_LOAD_CURRENT_MODES_RETRIES 5 #define AFTER_SET_LOAD_CURRENT_MODES_TIMEOUT_SECS 1 typedef struct { MmGdbusModem *skeleton; MMModemMode allowed; MMModemMode preferred; guint retries; } SetCurrentModesContext; static void set_current_modes_context_free (SetCurrentModesContext *ctx) { if (ctx->skeleton) g_object_unref (ctx->skeleton); g_slice_free (SetCurrentModesContext, ctx); } gboolean mm_iface_modem_set_current_modes_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void set_current_modes_reload_schedule (GTask *task); static void after_set_load_current_modes_ready (MMIfaceModem *self, GAsyncResult *res, GTask *task) { SetCurrentModesContext *ctx; MMModemMode allowed = MM_MODEM_MODE_NONE; MMModemMode preferred = MM_MODEM_MODE_NONE; GError *error = NULL; ctx = g_task_get_task_data (task); if (!MM_IFACE_MODEM_GET_INTERFACE (self)->load_current_modes_finish (self, res, &allowed, &preferred, &error)) { /* If we can retry, do it */ if (ctx->retries > 0) { mm_obj_dbg (self, "couldn't load current allowed/preferred modes: %s", error->message); g_error_free (error); set_current_modes_reload_schedule (task); return; } /* Errors when getting allowed/preferred won't be critical */ mm_obj_warn (self, "couldn't load current allowed/preferred modes: %s", error->message); g_clear_error (&error); /* If errors getting allowed modes, default to the ones we asked for */ mm_gdbus_modem_set_current_modes (ctx->skeleton, g_variant_new ("(uu)", ctx->allowed, ctx->preferred)); goto out; } /* Store as current the last loaded ones and set operation result */ mm_gdbus_modem_set_current_modes (ctx->skeleton, g_variant_new ("(uu)", allowed, preferred)); /* Compare modes. If the requested one was ANY, we won't consider an error if the * result differs. */ if (((allowed != ctx->allowed) || (preferred != ctx->preferred)) && (ctx->allowed != MM_MODEM_MODE_ANY)) { gchar *requested_allowed_str; gchar *requested_preferred_str; gchar *current_allowed_str; gchar *current_preferred_str; /* If we can retry, do it */ if (ctx->retries > 0) { mm_obj_dbg (self, "reloaded current modes different to the requested ones (will retry)"); set_current_modes_reload_schedule (task); return; } requested_allowed_str = mm_modem_mode_build_string_from_mask (ctx->allowed); requested_preferred_str = mm_modem_mode_build_string_from_mask (ctx->preferred); current_allowed_str = mm_modem_mode_build_string_from_mask (allowed); current_preferred_str = mm_modem_mode_build_string_from_mask (preferred); error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "reloaded modes (allowed '%s' and preferred '%s') different " "to the requested ones (allowed '%s' and preferred '%s')", current_allowed_str, current_preferred_str, requested_allowed_str, requested_preferred_str); g_free (requested_allowed_str); g_free (requested_preferred_str); g_free (current_allowed_str); g_free (current_preferred_str); } out: if (error) g_task_return_error (task, error); else g_task_return_boolean (task, TRUE); g_object_unref (task); } static gboolean set_current_modes_reload (GTask *task) { MMIfaceModem *self; SetCurrentModesContext *ctx; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); g_assert (ctx->retries > 0); ctx->retries--; MM_IFACE_MODEM_GET_INTERFACE (self)->load_current_modes ( self, (GAsyncReadyCallback)after_set_load_current_modes_ready, task); return G_SOURCE_REMOVE; } static void set_current_modes_reload_schedule (GTask *task) { g_timeout_add_seconds (AFTER_SET_LOAD_CURRENT_MODES_TIMEOUT_SECS, (GSourceFunc) set_current_modes_reload, task); } static void set_current_modes_ready (MMIfaceModem *self, GAsyncResult *res, GTask *task) { SetCurrentModesContext *ctx; GError *error = NULL; if (!MM_IFACE_MODEM_GET_INTERFACE (self)->set_current_modes_finish (self, res, &error)) { g_task_return_error (task, error); g_object_unref (task); return; } if (MM_IFACE_MODEM_GET_INTERFACE (self)->load_current_modes && MM_IFACE_MODEM_GET_INTERFACE (self)->load_current_modes_finish) { set_current_modes_reload (task); return; } ctx = g_task_get_task_data (task); /* Default to the ones we requested */ mm_gdbus_modem_set_current_modes (ctx->skeleton, g_variant_new ("(uu)", ctx->allowed, ctx->preferred)); g_task_return_boolean (task, TRUE); g_object_unref (task); } void mm_iface_modem_set_current_modes (MMIfaceModem *self, MMModemMode allowed, MMModemMode preferred, GAsyncReadyCallback callback, gpointer user_data) { GArray *supported; SetCurrentModesContext *ctx; MMModemMode current_allowed = MM_MODEM_MODE_ANY; MMModemMode current_preferred = MM_MODEM_MODE_NONE; guint i; GTask *task; /* If setting allowed modes is not implemented, report an error */ if (!MM_IFACE_MODEM_GET_INTERFACE (self)->set_current_modes || !MM_IFACE_MODEM_GET_INTERFACE (self)->set_current_modes_finish) { g_task_report_new_error (self, callback, user_data, mm_iface_modem_set_current_modes, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "Setting allowed modes not supported"); return; } /* Setup context */ ctx = g_slice_new0 (SetCurrentModesContext); ctx->retries = AFTER_SET_LOAD_CURRENT_MODES_RETRIES; ctx->allowed = allowed; ctx->preferred = preferred; task = g_task_new (self, NULL, callback, user_data); g_task_set_task_data (task, ctx, (GDestroyNotify)set_current_modes_context_free); g_object_get (self, MM_IFACE_MODEM_DBUS_SKELETON, &ctx->skeleton, NULL); if (!ctx->skeleton) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't get interface skeleton"); g_object_unref (task); return; } /* Get list of supported modes */ supported = mm_common_mode_combinations_variant_to_garray ( mm_gdbus_modem_get_supported_modes (ctx->skeleton)); /* Don't allow mode switching if only one item given in the supported list */ if (supported->len == 1) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "Cannot change modes: only one combination supported"); g_object_unref (task); g_array_unref (supported); return; } if (allowed == MM_MODEM_MODE_ANY && preferred == MM_MODEM_MODE_NONE) { /* Allow allowed=ANY & preferred=NONE, all plugins should support it */ } else { gboolean matched = FALSE; /* Check if the given combination is supported */ for (i = 0; !matched && i < supported->len; i++) { MMModemModeCombination *supported_mode; supported_mode = &g_array_index (supported, MMModemModeCombination, i); if ((supported_mode->allowed == MM_MODEM_MODE_ANY && supported_mode->preferred == MM_MODEM_MODE_NONE) || (supported_mode->allowed == allowed && supported_mode->preferred == preferred)) { matched = TRUE; } } if (!matched) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "The given combination of allowed and preferred modes is not supported"); g_object_unref (task); g_array_unref (supported); return; } } g_array_unref (supported); /* Check if we already are in the requested setup */ g_variant_get (mm_gdbus_modem_get_current_modes (ctx->skeleton), "(uu)", ¤t_allowed, ¤t_preferred); if (current_allowed == allowed && current_preferred == preferred) { g_task_return_boolean (task, TRUE); g_object_unref (task); return; } /* Ensure preferred, if given, is a subset of allowed */ if ((allowed ^ preferred) & preferred) { gchar *preferred_str; gchar *allowed_str; preferred_str = mm_modem_mode_build_string_from_mask (preferred); allowed_str = mm_modem_mode_build_string_from_mask (allowed); g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "Preferred mode (%s) is not allowed (%s)", preferred_str, allowed_str); g_object_unref (task); g_free (preferred_str); g_free (allowed_str); return; } ctx->allowed = allowed; ctx->preferred = preferred; MM_IFACE_MODEM_GET_INTERFACE (self)->set_current_modes (self, allowed, preferred, (GAsyncReadyCallback)set_current_modes_ready, task); } /*****************************************************************************/ typedef struct { MmGdbusModem *skeleton; GDBusMethodInvocation *invocation; MMIfaceModem *self; MMModemMode allowed; MMModemMode preferred; gchar *allowed_str; gchar *preferred_str; } HandleSetCurrentModesContext; static void handle_set_current_modes_context_free (HandleSetCurrentModesContext *ctx) { g_free (ctx->preferred_str); g_free (ctx->allowed_str); g_object_unref (ctx->skeleton); g_object_unref (ctx->invocation); g_object_unref (ctx->self); g_slice_free (HandleSetCurrentModesContext, ctx); } static void handle_set_current_modes_ready (MMIfaceModem *self, GAsyncResult *res, HandleSetCurrentModesContext *ctx) { GError *error = NULL; if (!mm_iface_modem_set_current_modes_finish (self, res, &error)) { mm_obj_warn (self, "failed setting current modes to '%s' (preferred '%s'): %s", ctx->allowed_str, ctx->preferred_str, error->message); mm_dbus_method_invocation_take_error (ctx->invocation, error); } else { /* Modes updated: explicitly refresh signal and access technology */ mm_iface_modem_refresh_signal (self); mm_obj_info (self, "current modes set to '%s' (preferred '%s')", ctx->allowed_str, ctx->preferred_str); mm_gdbus_modem_complete_set_current_modes (ctx->skeleton, ctx->invocation); } handle_set_current_modes_context_free (ctx); } static void handle_set_current_modes_auth_ready (MMBaseModem *self, GAsyncResult *res, HandleSetCurrentModesContext *ctx) { GError *error = NULL; if (!mm_base_modem_authorize_finish (self, res, &error)) { mm_dbus_method_invocation_take_error (ctx->invocation, error); handle_set_current_modes_context_free (ctx); return; } if (mm_iface_modem_abort_invocation_if_state_not_reached (ctx->self, ctx->invocation, MM_MODEM_STATE_DISABLED)) { handle_set_current_modes_context_free (ctx); return; } ctx->allowed_str = mm_modem_mode_build_string_from_mask (ctx->allowed); ctx->preferred_str = mm_modem_mode_build_string_from_mask (ctx->preferred); mm_obj_info (self, "processing user request to set current modes to '%s' (preferred '%s')...", ctx->allowed_str, ctx->preferred_str); mm_iface_modem_set_current_modes (MM_IFACE_MODEM (self), ctx->allowed, ctx->preferred, (GAsyncReadyCallback)handle_set_current_modes_ready, ctx); } static gboolean handle_set_current_modes (MmGdbusModem *skeleton, GDBusMethodInvocation *invocation, GVariant *variant, MMIfaceModem *self) { HandleSetCurrentModesContext *ctx; ctx = g_slice_new0 (HandleSetCurrentModesContext); ctx->skeleton = g_object_ref (skeleton); ctx->invocation = g_object_ref (invocation); ctx->self = g_object_ref (self); g_variant_get (variant, "(uu)", &ctx->allowed, &ctx->preferred); mm_base_modem_authorize (MM_BASE_MODEM (self), invocation, MM_AUTHORIZATION_DEVICE_CONTROL, (GAsyncReadyCallback)handle_set_current_modes_auth_ready, ctx); return TRUE; } /*****************************************************************************/ static void reinitialize_ready (MMBaseModem *self, GAsyncResult *res) { g_autoptr(GError) error = NULL; mm_base_modem_initialize_finish (self, res, &error); if (error) mm_obj_warn (self, "reinitialization failed: %s", error->message); } static gboolean restart_initialize_idle (MMIfaceModem *self) { Private *priv; priv = get_private (self); mm_base_modem_initialize (MM_BASE_MODEM (self), (GAsyncReadyCallback) reinitialize_ready, NULL); priv->restart_initialize_idle_id = 0; return G_SOURCE_REMOVE; } static void restart_initialize_idle_disable (MMIfaceModem *self) { Private *priv; priv = get_private (self); if (priv->restart_initialize_idle_id) { g_source_remove (priv->restart_initialize_idle_id); priv->restart_initialize_idle_id = 0; } } static void set_lock_status (MMIfaceModem *self, MmGdbusModem *skeleton, MMModemLock lock) { MMModemLock old_lock; Private *priv; priv = get_private (self); old_lock = mm_gdbus_modem_get_unlock_required (skeleton); mm_gdbus_modem_set_unlock_required (skeleton, lock); if (lock == MM_MODEM_LOCK_UNKNOWN) mm_gdbus_modem_set_unlock_retries (skeleton, 0); /* We don't care about SIM-PIN2/SIM-PUK2 since the device is * operational without it. */ if (lock == MM_MODEM_LOCK_NONE || lock == MM_MODEM_LOCK_SIM_PIN2 || lock == MM_MODEM_LOCK_SIM_PUK2) { /* Notify transition from INITIALIZING/LOCKED to DISABLED */ if (old_lock != MM_MODEM_LOCK_NONE && old_lock != MM_MODEM_LOCK_SIM_PIN2 && old_lock != MM_MODEM_LOCK_SIM_PUK2) { /* Only restart initialization if leaving LOCKED. * If this is the case, we do NOT update the state yet, we wait * to be completely re-initialized to do so. */ if (old_lock != MM_MODEM_LOCK_UNKNOWN) { if (priv->restart_initialize_idle_id) g_source_remove (priv->restart_initialize_idle_id); priv->restart_initialize_idle_id = g_idle_add ((GSourceFunc)restart_initialize_idle, self); } } return; } if (old_lock == MM_MODEM_LOCK_UNKNOWN) { /* Notify transition from INITIALIZING to LOCKED */ mm_iface_modem_update_state (self, MM_MODEM_STATE_LOCKED, MM_MODEM_STATE_CHANGE_REASON_UNKNOWN); } } MMModemLock mm_iface_modem_get_unlock_required (MMIfaceModem *self) { MmGdbusModem *skeleton = NULL; MMModemLock lock; g_object_get (self, MM_IFACE_MODEM_DBUS_SKELETON, &skeleton, NULL); if (skeleton) { lock = mm_gdbus_modem_get_unlock_required (skeleton); g_object_unref (skeleton); } else lock = MM_MODEM_LOCK_UNKNOWN; return lock; } MMUnlockRetries * mm_iface_modem_get_unlock_retries (MMIfaceModem *self) { MmGdbusModem *skeleton = NULL; MMUnlockRetries *unlock_retries; g_object_get (self, MM_IFACE_MODEM_DBUS_SKELETON, &skeleton, NULL); if (skeleton) { GVariant *dictionary; dictionary = mm_gdbus_modem_get_unlock_retries (skeleton); unlock_retries = mm_unlock_retries_new_from_dictionary (dictionary); g_object_unref (skeleton); } else unlock_retries = mm_unlock_retries_new (); return unlock_retries; } static void update_unlock_retries (MMIfaceModem *self, MMUnlockRetries *unlock_retries) { MmGdbusModem *skeleton = NULL; GVariant *previous_dictionary; MMUnlockRetries *previous_unlock_retries; g_object_get (self, MM_IFACE_MODEM_DBUS_SKELETON, &skeleton, NULL); if (!skeleton) return; previous_dictionary = mm_gdbus_modem_get_unlock_retries (skeleton); previous_unlock_retries = mm_unlock_retries_new_from_dictionary (previous_dictionary); /* If they are different, update */ if (!mm_unlock_retries_cmp (unlock_retries, previous_unlock_retries)) { GVariant *new_dictionary; new_dictionary = mm_unlock_retries_get_dictionary (unlock_retries); mm_gdbus_modem_set_unlock_retries (skeleton, new_dictionary); g_variant_unref (new_dictionary); } g_object_unref (previous_unlock_retries); g_object_unref (skeleton); } typedef enum { UPDATE_LOCK_INFO_CONTEXT_STEP_FIRST = 0, UPDATE_LOCK_INFO_CONTEXT_STEP_LOCK, UPDATE_LOCK_INFO_CONTEXT_STEP_AFTER_UNLOCK, UPDATE_LOCK_INFO_CONTEXT_STEP_RETRIES, UPDATE_LOCK_INFO_CONTEXT_STEP_LAST } UpdateLockInfoContextStep; typedef struct { UpdateLockInfoContextStep step; MmGdbusModem *skeleton; MMModemLock lock; GError *saved_error; } UpdateLockInfoContext; static void update_lock_info_context_free (UpdateLockInfoContext *ctx) { /* saved error may exist if we were cancelled */ g_clear_pointer (&ctx->saved_error, g_error_free); g_clear_object (&ctx->skeleton); g_slice_free (UpdateLockInfoContext, ctx); } MMModemLock mm_iface_modem_update_lock_info_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { GError *inner_error = NULL; gssize value; value = g_task_propagate_int (G_TASK (res), &inner_error); if (inner_error) { g_propagate_error (error, inner_error); return MM_MODEM_LOCK_UNKNOWN; } return (MMModemLock)value; } static void update_lock_info_context_step (GTask *task); static void load_unlock_retries_ready (MMIfaceModem *self, GAsyncResult *res, GTask *task) { UpdateLockInfoContext *ctx; GError *error = NULL; MMUnlockRetries *unlock_retries; unlock_retries = MM_IFACE_MODEM_GET_INTERFACE (self)->load_unlock_retries_finish (self, res, &error); if (!unlock_retries) { mm_obj_dbg (self, "couldn't load unlock retries: %s", error->message); g_error_free (error); } else { /* Update the dictionary in the DBus interface */ update_unlock_retries (self, unlock_retries); g_object_unref (unlock_retries); } /* Go on to next step */ ctx = g_task_get_task_data (task); ctx->step++; update_lock_info_context_step (task); } static void modem_after_sim_unlock_ready (MMIfaceModem *self, GAsyncResult *res, GTask *task) { UpdateLockInfoContext *ctx; GError *error = NULL; if (!MM_IFACE_MODEM_GET_INTERFACE (self)->modem_after_sim_unlock_finish (self, res, &error)) { mm_obj_dbg (self, "after SIM unlock failed: %s", error->message); g_error_free (error); } /* Go on to next step */ ctx = g_task_get_task_data (task); ctx->step++; update_lock_info_context_step (task); } static void internal_load_unlock_required_ready (MMIfaceModem *self, GAsyncResult *res, GTask *task) { UpdateLockInfoContext *ctx; GError *error = NULL; ctx = g_task_get_task_data (task); ctx->lock = internal_load_unlock_required_finish (self, res, &error); if (error) { /* Treat several SIM related, serial and other core errors as critical * and abort the checks. These will end up moving the modem to a FAILED * state. */ if (error->domain == MM_SERIAL_ERROR || g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) { ctx->saved_error = error; ctx->step = UPDATE_LOCK_INFO_CONTEXT_STEP_LAST; update_lock_info_context_step (task); return; } if (g_error_matches (error, MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_SIM_NOT_INSERTED) || g_error_matches (error, MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_SIM_FAILURE) || g_error_matches (error, MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_SIM_WRONG)) { /* SIM errors are only critical in 3GPP-capable devices */ if (mm_iface_modem_is_3gpp (self)) { ctx->saved_error = error; ctx->step = UPDATE_LOCK_INFO_CONTEXT_STEP_LAST; update_lock_info_context_step (task); return; } /* For non 3GPP-capable devices, skip SIM errors */ mm_obj_info (self, "skipping SIM error in non 3GPP-capable device, assuming no lock is needed"); g_error_free (error); ctx->lock = MM_MODEM_LOCK_NONE; } else { mm_obj_dbg (self, "couldn't check if unlock required: %s", error->message); g_error_free (error); ctx->lock = MM_MODEM_LOCK_UNKNOWN; } } /* Go on to next step */ ctx->step++; update_lock_info_context_step (task); } static void update_lock_info_context_step (GTask *task) { MMIfaceModem *self; UpdateLockInfoContext *ctx; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); if (g_task_return_error_if_cancelled (task)) { mm_obj_dbg (self, "lock info update cancelled"); g_object_unref (task); return; } switch (ctx->step) { case UPDATE_LOCK_INFO_CONTEXT_STEP_FIRST: /* We need the skeleton around */ if (!ctx->skeleton) { ctx->saved_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't get interface skeleton"); ctx->step = UPDATE_LOCK_INFO_CONTEXT_STEP_LAST; update_lock_info_context_step (task); return; } ctx->step++; /* fall-through */ case UPDATE_LOCK_INFO_CONTEXT_STEP_LOCK: /* Don't re-ask if already known */ if (ctx->lock == MM_MODEM_LOCK_UNKNOWN) { /* If we're already unlocked, we're done */ internal_load_unlock_required ( self, g_task_get_cancellable (task), (GAsyncReadyCallback)internal_load_unlock_required_ready, task); return; } ctx->step++; /* fall-through */ case UPDATE_LOCK_INFO_CONTEXT_STEP_AFTER_UNLOCK: /* If we get that no lock is required, run the after SIM unlock step * in order to wait for the SIM to get ready. Skip waiting on * CDMA-only modems where we don't support a SIM. */ if (!mm_iface_modem_is_cdma_only (self) && (ctx->lock == MM_MODEM_LOCK_NONE || ctx->lock == MM_MODEM_LOCK_SIM_PIN2 || ctx->lock == MM_MODEM_LOCK_SIM_PUK2)) { if (MM_IFACE_MODEM_GET_INTERFACE (self)->modem_after_sim_unlock != NULL && MM_IFACE_MODEM_GET_INTERFACE (self)->modem_after_sim_unlock_finish != NULL) { mm_obj_dbg (self, "SIM is ready, running after SIM unlock step..."); MM_IFACE_MODEM_GET_INTERFACE (self)->modem_after_sim_unlock ( self, (GAsyncReadyCallback)modem_after_sim_unlock_ready, task); return; } /* If no way to run after SIM unlock step, we're done */ mm_obj_info (self, "SIM is ready, and no need for the after SIM unlock step..."); } ctx->step++; /* fall-through */ case UPDATE_LOCK_INFO_CONTEXT_STEP_RETRIES: /* Load unlock retries if possible */ if (MM_IFACE_MODEM_GET_INTERFACE (self)->load_unlock_retries && MM_IFACE_MODEM_GET_INTERFACE (self)->load_unlock_retries_finish) { MM_IFACE_MODEM_GET_INTERFACE (self)->load_unlock_retries ( self, (GAsyncReadyCallback)load_unlock_retries_ready, task); return; } ctx->step++; /* fall-through */ case UPDATE_LOCK_INFO_CONTEXT_STEP_LAST: if (ctx->saved_error) { set_lock_status (self, ctx->skeleton, MM_MODEM_LOCK_UNKNOWN); /* Return saved error */ g_task_return_error (task, ctx->saved_error); ctx->saved_error = NULL; } else { /* Update lock status and modem status if needed */ set_lock_status (self, ctx->skeleton, ctx->lock); g_task_return_int (task, ctx->lock); } g_object_unref (task); return; default: g_assert_not_reached (); } } void mm_iface_modem_update_lock_info (MMIfaceModem *self, MMModemLock known_lock, GAsyncReadyCallback callback, gpointer user_data) { UpdateLockInfoContext *ctx; GTask *task; ctx = g_slice_new0 (UpdateLockInfoContext); /* If the given lock is known, we will avoid re-asking for it */ ctx->lock = known_lock; task = g_task_new (self, mm_base_modem_peek_cancellable (MM_BASE_MODEM (self)), callback, user_data); g_task_set_task_data (task, ctx, (GDestroyNotify)update_lock_info_context_free); g_object_get (self, MM_IFACE_MODEM_DBUS_SKELETON, &ctx->skeleton, NULL); if (!ctx->skeleton) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't get interface skeleton"); g_object_unref (task); return; } update_lock_info_context_step (task); } /*****************************************************************************/ /* Set power state sequence */ typedef enum { SET_POWER_STATE_STEP_FIRST, SET_POWER_STATE_STEP_LOAD, SET_POWER_STATE_STEP_CHECK, SET_POWER_STATE_STEP_WAIT_BEFORE_UPDATE, SET_POWER_STATE_STEP_UPDATE, SET_POWER_STATE_STEP_FCC_UNLOCK, SET_POWER_STATE_STEP_AFTER_UPDATE, SET_POWER_STATE_STEP_LAST, } SetPowerStateStep; typedef struct { SetPowerStateStep step; MmGdbusModem *skeleton; GError *saved_error; gboolean fcc_unlock_attempted; MMModemPowerState requested_power_state; MMModemPowerState previous_cached_power_state; MMModemPowerState previous_real_power_state; void (*requested_power_setup) (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data); gboolean (*requested_power_setup_finish) (MMIfaceModem *self, GAsyncResult *res, GError **error); } SetPowerStateContext; static void set_power_state_context_free (SetPowerStateContext *ctx) { g_assert (!ctx->saved_error); if (ctx->skeleton) g_object_unref (ctx->skeleton); g_slice_free (SetPowerStateContext, ctx); } gboolean mm_iface_modem_set_power_state_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void set_power_state_step (GTask *task); static void modem_after_power_up_ready (MMIfaceModem *self, GAsyncResult *res, GTask *task) { SetPowerStateContext *ctx; ctx = g_task_get_task_data (task); g_assert (!ctx->saved_error); MM_IFACE_MODEM_GET_INTERFACE (self)->modem_after_power_up_finish (self, res, &ctx->saved_error); if (ctx->saved_error) mm_obj_info (self, "failure running after power up step: %s", ctx->saved_error->message); ctx->step++; set_power_state_step (task); } static void dispatcher_fcc_unlock_ready (MMDispatcherFccUnlock *dispatcher, GAsyncResult *res, GTask *task) { MMIfaceModem *self; SetPowerStateContext *ctx; g_autoptr(GError) error = NULL; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); if (!mm_dispatcher_fcc_unlock_run_finish (dispatcher, res, &error)) mm_obj_dbg (self, "couldn't run FCC unlock: %s", error->message); /* always retry, even on reported error */ ctx->step = SET_POWER_STATE_STEP_UPDATE; set_power_state_step (task); } static void fcc_unlock (GTask *task) { MMIfaceModem *self; MMDispatcherFccUnlock *dispatcher; MMModemPortInfo *port_infos; guint n_port_infos = 0; guint i; GPtrArray *aux; g_auto(GStrv) modem_ports = NULL; self = g_task_get_source_object (task); dispatcher = mm_dispatcher_fcc_unlock_get (); aux = g_ptr_array_new (); port_infos = mm_base_modem_get_port_infos (MM_BASE_MODEM (self), &n_port_infos); for (i = 0; i < n_port_infos; i++) { switch (port_infos[i].type) { case MM_MODEM_PORT_TYPE_AT: case MM_MODEM_PORT_TYPE_QMI: case MM_MODEM_PORT_TYPE_MBIM: g_ptr_array_add (aux, g_strdup (port_infos[i].name)); break; case MM_MODEM_PORT_TYPE_UNKNOWN: case MM_MODEM_PORT_TYPE_NET: case MM_MODEM_PORT_TYPE_QCDM: case MM_MODEM_PORT_TYPE_GPS: case MM_MODEM_PORT_TYPE_AUDIO: case MM_MODEM_PORT_TYPE_IGNORED: default: break; } } mm_modem_port_info_array_free (port_infos, n_port_infos); g_ptr_array_add (aux, NULL); modem_ports = (GStrv) g_ptr_array_free (aux, FALSE); mm_dispatcher_fcc_unlock_run (dispatcher, mm_base_modem_get_vendor_id (MM_BASE_MODEM (self)), mm_base_modem_get_product_id (MM_BASE_MODEM (self)), g_dbus_object_get_object_path (G_DBUS_OBJECT (self)), modem_ports, g_task_get_cancellable (task), (GAsyncReadyCallback)dispatcher_fcc_unlock_ready, task); } static void requested_power_setup_ready (MMIfaceModem *self, GAsyncResult *res, GTask *task) { SetPowerStateContext *ctx; Private *priv; ctx = g_task_get_task_data (task); priv = get_private (self); g_assert (!ctx->saved_error); if (!ctx->requested_power_setup_finish (self, res, &ctx->saved_error)) mm_obj_info (self, "couldn't update power state: %s", ctx->saved_error->message); /* Reset time of last power update */ g_timer_reset (priv->power_state_timer); ctx->step++; set_power_state_step (task); } static gboolean wait_before_update_ready (GTask *task) { SetPowerStateContext *ctx; ctx = g_task_get_task_data (task); ctx->step++; set_power_state_step (task); return G_SOURCE_REMOVE; } static void set_power_state_load_ready (MMIfaceModem *self, GAsyncResult *res, GTask *task) { SetPowerStateContext *ctx; g_autoptr(GError) error = NULL; ctx = g_task_get_task_data (task); ctx->previous_real_power_state = MM_IFACE_MODEM_GET_INTERFACE (self)->load_power_state_finish (self, res, &error); if (error) { mm_obj_dbg (self, "couldn't reload current power state: %s", error->message); /* Default to the cached one */ ctx->previous_real_power_state = ctx->previous_cached_power_state; } ctx->step++; set_power_state_step (task); } static void set_power_state_step (GTask *task) { MMIfaceModem *self; SetPowerStateContext *ctx; Private *priv; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); priv = get_private (self); switch (ctx->step) { case SET_POWER_STATE_STEP_FIRST: ctx->step++; /* fall-through */ case SET_POWER_STATE_STEP_LOAD: /* We cannot really rely on the power state value that we had cached before, * as the real power status of the modem may also be changed by rfkill. So, * before updating the current power state, re-check which is the real power * state. */ if (MM_IFACE_MODEM_GET_INTERFACE (self)->load_power_state && MM_IFACE_MODEM_GET_INTERFACE (self)->load_power_state_finish) { MM_IFACE_MODEM_GET_INTERFACE (self)->load_power_state (self, (GAsyncReadyCallback)set_power_state_load_ready, task); return; } /* If there is no way to load power state, just keep on assuming the cached * one is also the real one */ ctx->previous_real_power_state = ctx->previous_cached_power_state; ctx->step++; /* fall-through */ case SET_POWER_STATE_STEP_CHECK: /* Already done if we're in the desired power state */ if (ctx->previous_real_power_state == ctx->requested_power_state) { mm_obj_dbg (self, "no need to change power state: already '%s'", mm_modem_power_state_get_string (ctx->requested_power_state)); ctx->step = SET_POWER_STATE_STEP_LAST; set_power_state_step (task); return; } ctx->step++; /* fall-through */ case SET_POWER_STATE_STEP_WAIT_BEFORE_UPDATE: /* No wait if this is the first time */ if (!priv->power_state_timer) priv->power_state_timer = g_timer_new (); else { gdouble time_since_last_update_sec; time_since_last_update_sec = g_timer_elapsed (priv->power_state_timer, NULL); if (time_since_last_update_sec < (gdouble)POWER_STATE_MIN_TIME_BETWEEN_UPDATES_SEC) { guint wait_time_ms; /* Compute wait time in ms */ wait_time_ms = (guint)(((gdouble)POWER_STATE_MIN_TIME_BETWEEN_UPDATES_SEC - time_since_last_update_sec) * 1000.0); mm_obj_dbg (self, "waiting before updating power state: %ums", wait_time_ms); g_timeout_add (wait_time_ms, (GSourceFunc) wait_before_update_ready, task); return; } } mm_obj_dbg (self, "no need to wait before updating power state"); ctx->step++; /* fall-through */ case SET_POWER_STATE_STEP_UPDATE: mm_obj_dbg (self, "updating power state: '%s'...", mm_modem_power_state_get_string (ctx->requested_power_state)); /* Error if unsupported */ if (!ctx->requested_power_setup || !ctx->requested_power_setup_finish) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "Requested power transition is not supported by this modem"); g_object_unref (task); return; } ctx->requested_power_setup (self, (GAsyncReadyCallback)requested_power_setup_ready, task); return; case SET_POWER_STATE_STEP_FCC_UNLOCK: /* The FCC unlock operation and update retry should only be done * if requested by the implementation with MM_CORE_ERROR_RETRY. */ if ((ctx->requested_power_state == MM_MODEM_POWER_STATE_ON) && ctx->saved_error && g_error_matches (ctx->saved_error, MM_CORE_ERROR, MM_CORE_ERROR_RETRY) && !ctx->fcc_unlock_attempted) { mm_obj_dbg (self, "attempting FCC unlock..."); g_clear_error (&ctx->saved_error); ctx->fcc_unlock_attempted = TRUE; fcc_unlock (task); return; } ctx->step++; /* fall-through */ case SET_POWER_STATE_STEP_AFTER_UPDATE: if ((ctx->requested_power_state == MM_MODEM_POWER_STATE_ON) && !ctx->saved_error && MM_IFACE_MODEM_GET_INTERFACE (self)->modem_after_power_up && MM_IFACE_MODEM_GET_INTERFACE (self)->modem_after_power_up_finish) { mm_obj_dbg (self, "running after power up operation..."); MM_IFACE_MODEM_GET_INTERFACE (self)->modem_after_power_up (self, (GAsyncReadyCallback)modem_after_power_up_ready, task); return; } ctx->step++; /* fall-through */ case SET_POWER_STATE_STEP_LAST: if (ctx->saved_error) { /* If the real and cached ones are different, set the real one */ if (ctx->previous_cached_power_state != ctx->previous_real_power_state) mm_gdbus_modem_set_power_state (ctx->skeleton, ctx->previous_real_power_state); g_task_return_error (task, g_steal_pointer (&ctx->saved_error)); } else { mm_obj_msg (self, "power state updated: %s", mm_modem_power_state_get_string (ctx->requested_power_state)); mm_gdbus_modem_set_power_state (ctx->skeleton, ctx->requested_power_state); g_task_return_boolean (task, TRUE); } g_object_unref (task); return; default: g_assert_not_reached (); } } void mm_iface_modem_set_power_state (MMIfaceModem *self, MMModemPowerState power_state, GAsyncReadyCallback callback, gpointer user_data) { SetPowerStateContext *ctx; GTask *task; ctx = g_slice_new0 (SetPowerStateContext); ctx->step = SET_POWER_STATE_STEP_FIRST; ctx->requested_power_state = power_state; task = g_task_new (self, NULL, callback, user_data); g_task_set_task_data (task, ctx, (GDestroyNotify)set_power_state_context_free); g_object_get (self, MM_IFACE_MODEM_DBUS_SKELETON, &ctx->skeleton, NULL); if (!ctx->skeleton) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't get interface skeleton"); g_object_unref (task); return; } ctx->previous_cached_power_state = mm_gdbus_modem_get_power_state (ctx->skeleton); /* Setup requested operation */ switch (ctx->requested_power_state) { case MM_MODEM_POWER_STATE_OFF: ctx->requested_power_setup = MM_IFACE_MODEM_GET_INTERFACE (self)->modem_power_off; ctx->requested_power_setup_finish = MM_IFACE_MODEM_GET_INTERFACE (self)->modem_power_off_finish; break; case MM_MODEM_POWER_STATE_LOW: ctx->requested_power_setup = MM_IFACE_MODEM_GET_INTERFACE (self)->modem_power_down; ctx->requested_power_setup_finish = MM_IFACE_MODEM_GET_INTERFACE (self)->modem_power_down_finish; break; case MM_MODEM_POWER_STATE_ON: ctx->requested_power_setup = MM_IFACE_MODEM_GET_INTERFACE (self)->modem_power_up; ctx->requested_power_setup_finish = MM_IFACE_MODEM_GET_INTERFACE (self)->modem_power_up_finish; break; case MM_MODEM_POWER_STATE_UNKNOWN: default: g_assert_not_reached (); } set_power_state_step (task); } /*****************************************************************************/ /* MODEM DISABLING */ gboolean mm_iface_modem_disable_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } void mm_iface_modem_disable (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data) { MmGdbusModem *skeleton = NULL; GTask *task; g_object_get (self, MM_IFACE_MODEM_DBUS_SKELETON, &skeleton, NULL); /* * Set signal quality to 0% and access technologies to unknown since modem is disabled */ if (skeleton) { mm_gdbus_modem_set_signal_quality (MM_GDBUS_MODEM (skeleton), g_variant_new ("(ub)", 0, TRUE)); mm_gdbus_modem_set_access_technologies (MM_GDBUS_MODEM (skeleton), MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN); g_object_unref (skeleton); } /* Just complete, nothing to do */ task = g_task_new (self, NULL, callback, user_data); g_task_return_boolean (task, TRUE); g_object_unref (task); } /*****************************************************************************/ /* MODEM ENABLING */ typedef struct _EnablingContext EnablingContext; static void interface_enabling_step (GTask *task); typedef enum { ENABLING_STEP_FIRST, ENABLING_STEP_SET_POWER_STATE, ENABLING_STEP_CHECK_FOR_SIM_SWAP, ENABLING_STEP_FLOW_CONTROL, ENABLING_STEP_LAST } EnablingStep; struct _EnablingContext { EnablingStep step; MmGdbusModem *skeleton; }; static void enabling_context_free (EnablingContext *ctx) { if (ctx->skeleton) g_object_unref (ctx->skeleton); g_free (ctx); } gboolean mm_iface_modem_enable_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void enabling_set_power_state_ready (MMIfaceModem *self, GAsyncResult *res, GTask *task) { EnablingContext *ctx; GError *error = NULL; if (!mm_iface_modem_set_power_state_finish (self, res, &error)) { g_task_return_error (task, error); g_object_unref (task); return; } /* Go on to next step */ ctx = g_task_get_task_data (task); ctx->step++; interface_enabling_step (task); } static void check_for_sim_swap_ready (MMIfaceModem *self, GAsyncResult *res, GTask *task) { EnablingContext *ctx; g_autoptr(GError) error = NULL; if (!MM_IFACE_MODEM_GET_INTERFACE (self)->check_for_sim_swap_finish (self, res, &error)) mm_obj_dbg (self, "failed to check if SIM was swapped: %s", error->message); /* Go on to next step */ ctx = g_task_get_task_data (task); ctx->step++; interface_enabling_step (task); } static void setup_flow_control_ready (MMIfaceModem *self, GAsyncResult *res, GTask *task) { EnablingContext *ctx; GError *error = NULL; MM_IFACE_MODEM_GET_INTERFACE (self)->setup_flow_control_finish (self, res, &error); if (error) { g_task_return_error (task, error); g_object_unref (task); return; } /* Go on to next step */ ctx = g_task_get_task_data (task); ctx->step++; interface_enabling_step (task); } static const MMModemCharset best_charsets[] = { MM_MODEM_CHARSET_UTF8, MM_MODEM_CHARSET_UCS2, MM_MODEM_CHARSET_8859_1, MM_MODEM_CHARSET_IRA, MM_MODEM_CHARSET_GSM, MM_MODEM_CHARSET_UNKNOWN }; static void interface_enabling_step (GTask *task) { MMIfaceModem *self; EnablingContext *ctx; /* Don't run new steps if we're cancelled */ if (g_task_return_error_if_cancelled (task)) { g_object_unref (task); return; } self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); switch (ctx->step) { case ENABLING_STEP_FIRST: ctx->step++; /* fall-through */ case ENABLING_STEP_SET_POWER_STATE: mm_iface_modem_set_power_state (self, MM_MODEM_POWER_STATE_ON, (GAsyncReadyCallback)enabling_set_power_state_ready, task); return; case ENABLING_STEP_CHECK_FOR_SIM_SWAP: if (MM_IFACE_MODEM_GET_INTERFACE (self)->check_for_sim_swap && MM_IFACE_MODEM_GET_INTERFACE (self)->check_for_sim_swap_finish) { MM_IFACE_MODEM_GET_INTERFACE (self)->check_for_sim_swap ( self, (GAsyncReadyCallback)check_for_sim_swap_ready, task); return; } ctx->step++; /* fall-through */ case ENABLING_STEP_FLOW_CONTROL: if (MM_IFACE_MODEM_GET_INTERFACE (self)->setup_flow_control && MM_IFACE_MODEM_GET_INTERFACE (self)->setup_flow_control_finish) { MM_IFACE_MODEM_GET_INTERFACE (self)->setup_flow_control ( self, (GAsyncReadyCallback)setup_flow_control_ready, task); return; } ctx->step++; /* fall-through */ case ENABLING_STEP_LAST: /* We are done without errors! */ g_task_return_boolean (task, TRUE); g_object_unref (task); return; default: break; } g_assert_not_reached (); } void mm_iface_modem_enable (MMIfaceModem *self, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { EnablingContext *ctx; GTask *task; ctx = g_new0 (EnablingContext, 1); ctx->step = ENABLING_STEP_FIRST; task = g_task_new (self, cancellable, callback, user_data); g_task_set_task_data (task, ctx, (GDestroyNotify)enabling_context_free); g_object_get (self, MM_IFACE_MODEM_DBUS_SKELETON, &ctx->skeleton, NULL); if (!ctx->skeleton) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't get interface skeleton"); g_object_unref (task); return; } interface_enabling_step (task); } /*****************************************************************************/ /* MODEM SYNCHRONIZATION */ #if defined WITH_SUSPEND_RESUME typedef struct _SyncingContext SyncingContext; static void interface_syncing_step (GTask *task); typedef enum { SYNCING_STEP_FIRST, SYNCING_STEP_DETECT_SIM_SWAP, SYNCING_STEP_REFRESH_SIM_LOCK, SYNCING_STEP_REFRESH_SIGNAL_STRENGTH, SYNCING_STEP_REFRESH_BEARERS, SYNCING_STEP_LAST } SyncingStep; struct _SyncingContext { SyncingStep step; }; gboolean mm_iface_modem_sync_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void sync_all_bearers_ready (MMBearerList *bearer_list, GAsyncResult *res, GTask *task) { MMIfaceModem *self; SyncingContext *ctx; g_autoptr (GError) error = NULL; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); if (!mm_bearer_list_sync_all_bearers_finish (bearer_list, res, &error)) mm_obj_warn (self, "synchronizing all bearer status failed: %s", error->message); /* Go on to next step */ ctx->step++; interface_syncing_step (task); } static void reload_bearers (GTask *task) { MMIfaceModem *self; SyncingContext *ctx; g_autoptr(MMBearerList) bearer_list = NULL; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); g_object_get (self, MM_IFACE_MODEM_BEARER_LIST, &bearer_list, NULL); /* If no bearer list (e.g. none created or modem disabled), * go on to next step */ if (!bearer_list) { ctx->step++; interface_syncing_step (task); return; } mm_bearer_list_sync_all_bearers (bearer_list, (GAsyncReadyCallback)sync_all_bearers_ready, task); } static void sync_sim_lock_ready (MMIfaceModem *self, GAsyncResult *res, GTask *task) { SyncingContext *ctx; g_autoptr (GError) error = NULL; ctx = g_task_get_task_data (task); if (!MM_IFACE_MODEM_GET_INTERFACE (self)->load_unlock_required_finish (self, res, &error)) mm_obj_warn (self, "checking sim lock status failed: %s", error->message); /* Go on to next step */ ctx->step++; interface_syncing_step (task); } static void sync_detect_sim_swap_ready (MMIfaceModem *self, GAsyncResult *res, GTask *task) { SyncingContext *ctx; g_autoptr (GError) error = NULL; ctx = g_task_get_task_data (task); if (!mm_iface_modem_check_for_sim_swap_finish (self, res, &error)) mm_obj_warn (self, "checking sim swap failed: %s", error->message); /* Go on to next step */ ctx->step++; interface_syncing_step (task); } static void interface_syncing_step (GTask *task) { MMIfaceModem *self; SyncingContext *ctx; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); switch (ctx->step) { case SYNCING_STEP_FIRST: ctx->step++; /* fall through */ case SYNCING_STEP_DETECT_SIM_SWAP: /* * Detect possible SIM swaps. * Checking lock status in all cases after possible SIM swaps are detected. */ mm_iface_modem_check_for_sim_swap ( self, (GAsyncReadyCallback)sync_detect_sim_swap_ready, task); return; case SYNCING_STEP_REFRESH_SIM_LOCK: /* * Refresh SIM lock status and wait until complete. */ MM_IFACE_MODEM_GET_INTERFACE (self)->load_unlock_required ( self, FALSE, NULL, (GAsyncReadyCallback)sync_sim_lock_ready, task); return; case SYNCING_STEP_REFRESH_SIGNAL_STRENGTH: /* * Restart the signal strength and access technologies refresh sequence. */ mm_iface_modem_refresh_signal (self); ctx->step++; /* fall through */ case SYNCING_STEP_REFRESH_BEARERS: /* * Refresh bearers. */ reload_bearers (task); return; case SYNCING_STEP_LAST: /* We are done without errors! */ g_task_return_boolean (task, TRUE); g_object_unref (task); return; default: break; } g_assert_not_reached (); } void mm_iface_modem_sync (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data) { SyncingContext *ctx; GTask *task; /* Create SyncingContext */ ctx = g_new0 (SyncingContext, 1); ctx->step = SYNCING_STEP_FIRST; /* Create sync steps task and execute it */ task = g_task_new (self, NULL, callback, user_data); g_task_set_task_data (task, ctx, (GDestroyNotify)g_free); interface_syncing_step (task); } #endif /*****************************************************************************/ /* MODEM INITIALIZATION */ typedef struct _InitializationContext InitializationContext; static void interface_initialization_step (GTask *task); typedef enum { INITIALIZATION_STEP_FIRST, INITIALIZATION_STEP_CURRENT_CAPABILITIES, INITIALIZATION_STEP_SUPPORTED_CAPABILITIES, INITIALIZATION_STEP_SUPPORTED_CHARSETS, INITIALIZATION_STEP_CHARSET, INITIALIZATION_STEP_MANUFACTURER, INITIALIZATION_STEP_MODEL, INITIALIZATION_STEP_REVISION, INITIALIZATION_STEP_BEARERS, INITIALIZATION_STEP_CARRIER_CONFIG, INITIALIZATION_STEP_HARDWARE_REVISION, INITIALIZATION_STEP_EQUIPMENT_ID, INITIALIZATION_STEP_DEVICE_ID, INITIALIZATION_STEP_SUPPORTED_MODES, INITIALIZATION_STEP_SUPPORTED_BANDS, INITIALIZATION_STEP_SUPPORTED_IP_FAMILIES, INITIALIZATION_STEP_POWER_STATE, INITIALIZATION_STEP_CURRENT_MODES, INITIALIZATION_STEP_CURRENT_BANDS, INITIALIZATION_STEP_SIM_HOT_SWAP, INITIALIZATION_STEP_SIM_SLOTS, INITIALIZATION_STEP_UNLOCK_REQUIRED, INITIALIZATION_STEP_SIM, INITIALIZATION_STEP_SETUP_CARRIER_CONFIG, INITIALIZATION_STEP_OWN_NUMBERS, INITIALIZATION_STEP_VALIDATE_ESIM_STATUS, INITIALIZATION_STEP_LAST } InitializationStep; struct _InitializationContext { InitializationStep step; MmGdbusModem *skeleton; MMModemCharset supported_charsets; const MMModemCharset *current_charset; GError *fatal_error; }; static void initialization_context_free (InitializationContext *ctx) { g_assert (ctx->fatal_error == NULL); g_object_unref (ctx->skeleton); g_free (ctx); } #undef STR_REPLY_READY_FN #define STR_REPLY_READY_FN(NAME,DISPLAY) \ static void \ load_##NAME##_ready (MMIfaceModem *self, \ GAsyncResult *res, \ GTask *task) \ { \ InitializationContext *ctx; \ g_autoptr(GError) error = NULL; \ g_autofree gchar *val = NULL; \ \ ctx = g_task_get_task_data (task); \ \ val = MM_IFACE_MODEM_GET_INTERFACE (self)->load_##NAME##_finish (self, res, &error); \ mm_gdbus_modem_set_##NAME (ctx->skeleton, val); \ \ if (error) \ mm_obj_dbg (self, "couldn't load %s: %s", DISPLAY, error->message); \ \ /* Go on to next step */ \ ctx->step++; \ interface_initialization_step (task); \ } #undef UINT_REPLY_READY_FN #define UINT_REPLY_READY_FN(NAME,DISPLAY) \ static void \ load_##NAME##_ready (MMIfaceModem *self, \ GAsyncResult *res, \ GTask *task) \ { \ InitializationContext *ctx; \ g_autoptr(GError) error = NULL; \ \ ctx = g_task_get_task_data (task); \ \ mm_gdbus_modem_set_##NAME ( \ ctx->skeleton, \ MM_IFACE_MODEM_GET_INTERFACE (self)->load_##NAME##_finish (self, res, &error)); \ \ if (error) \ mm_obj_dbg (self, "couldn't load %s: %s", DISPLAY, error->message); \ \ /* Go on to next step */ \ ctx->step++; \ interface_initialization_step (task); \ } static void load_current_capabilities_ready (MMIfaceModem *self, GAsyncResult *res, GTask *task) { InitializationContext *ctx; MMModemCapability caps; g_autoptr(GError) error = NULL; ctx = g_task_get_task_data (task); caps = MM_IFACE_MODEM_GET_INTERFACE (self)->load_current_capabilities_finish (self, res, &error); if (error) { ctx->fatal_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "Failed to load current capabilities: %s", error->message); /* Jump to the last step */ ctx->step = INITIALIZATION_STEP_LAST; interface_initialization_step (task); return; } /* By default CS/PS/CDMA1X/EVDO network registration checks are the only * ones enabled, so fix them up based on capabilities, enabling EPS or 5GS * checks if required, and disabling CS/PS/CDMA1X/EVDO if required. */ if (caps & MM_MODEM_CAPABILITY_LTE) { mm_obj_dbg (self, "setting EPS network as supported"); g_object_set (G_OBJECT (self), MM_IFACE_MODEM_3GPP_EPS_NETWORK_SUPPORTED, TRUE, NULL); } if (caps & MM_MODEM_CAPABILITY_5GNR) { mm_obj_dbg (self, "setting 5GS network as supported"); g_object_set (G_OBJECT (self), MM_IFACE_MODEM_3GPP_5GS_NETWORK_SUPPORTED, TRUE, NULL); } if (!(caps & MM_MODEM_CAPABILITY_CDMA_EVDO)) { mm_obj_dbg (self, "setting CDMA1x/EVDO networks as unsupported"); g_object_set (G_OBJECT (self), MM_IFACE_MODEM_CDMA_CDMA1X_NETWORK_SUPPORTED, FALSE, MM_IFACE_MODEM_CDMA_EVDO_NETWORK_SUPPORTED, FALSE, NULL); } if (!(caps & MM_MODEM_CAPABILITY_GSM_UMTS)) { mm_obj_dbg (self, "setting CS/PS networks as unsupported"); g_object_set (G_OBJECT (self), MM_IFACE_MODEM_3GPP_CS_NETWORK_SUPPORTED, FALSE, MM_IFACE_MODEM_3GPP_PS_NETWORK_SUPPORTED, FALSE, NULL); } mm_gdbus_modem_set_current_capabilities (ctx->skeleton, caps); ctx->step++; interface_initialization_step (task); } static void load_supported_capabilities_ready (MMIfaceModem *self, GAsyncResult *res, GTask *task) { InitializationContext *ctx; GArray *supported_capabilities; g_autoptr(GError) error = NULL; ctx = g_task_get_task_data (task); supported_capabilities = MM_IFACE_MODEM_GET_INTERFACE (self)->load_supported_capabilities_finish (self, res, &error); if (error) { ctx->fatal_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "Failed to load supported capabilities: %s", error->message); /* Jump to the last step */ ctx->step = INITIALIZATION_STEP_LAST; interface_initialization_step (task); return; } /* Update supported caps */ mm_gdbus_modem_set_supported_capabilities (ctx->skeleton, mm_common_capability_combinations_garray_to_variant (supported_capabilities)); g_array_unref (supported_capabilities); ctx->step++; interface_initialization_step (task); } STR_REPLY_READY_FN (manufacturer, "manufacturer") STR_REPLY_READY_FN (model, "model") STR_REPLY_READY_FN (revision, "revision") STR_REPLY_READY_FN (hardware_revision, "hardware revision") STR_REPLY_READY_FN (equipment_identifier, "equipment identifier") STR_REPLY_READY_FN (device_identifier, "device identifier") static void load_supported_charsets_ready (MMIfaceModem *self, GAsyncResult *res, GTask *task) { InitializationContext *ctx; g_autoptr(GError) error = NULL; ctx = g_task_get_task_data (task); ctx->supported_charsets = MM_IFACE_MODEM_GET_INTERFACE (self)->load_supported_charsets_finish (self, res, &error); if (error) mm_obj_dbg (self, "couldn't load supported charsets: %s", error->message); /* Go on to next step */ ctx->step++; interface_initialization_step (task); } static void setup_charset_ready (MMIfaceModem *self, GAsyncResult *res, GTask *task) { InitializationContext *ctx; g_autoptr(GError) error = NULL; ctx = g_task_get_task_data (task); if (!MM_IFACE_MODEM_GET_INTERFACE (self)->setup_charset_finish (self, res, &error)) mm_obj_dbg (self, "couldn't set charset '%s': %s", mm_modem_charset_to_string (*ctx->current_charset), error->message); /* Will retry step with some other charset type */ else /* Done, go on to next step */ ctx->step++; interface_initialization_step (task); } static void load_supported_modes_ready (MMIfaceModem *self, GAsyncResult *res, GTask *task) { InitializationContext *ctx; g_autoptr(GError) error = NULL; GArray *modes_array; ctx = g_task_get_task_data (task); modes_array = MM_IFACE_MODEM_GET_INTERFACE (self)->load_supported_modes_finish (self, res, &error); if (modes_array != NULL) { mm_gdbus_modem_set_supported_modes (ctx->skeleton, mm_common_mode_combinations_garray_to_variant (modes_array)); g_array_unref (modes_array); } if (error) mm_obj_dbg (self, "couldn't load supported modes: %s", error->message); /* Go on to next step */ ctx->step++; interface_initialization_step (task); } static void load_supported_bands_ready (MMIfaceModem *self, GAsyncResult *res, GTask *task) { InitializationContext *ctx; g_autoptr(GError) error = NULL; GArray *bands_array; ctx = g_task_get_task_data (task); bands_array = MM_IFACE_MODEM_GET_INTERFACE (self)->load_supported_bands_finish (self, res, &error); if (bands_array) { mm_common_bands_garray_sort (bands_array); mm_gdbus_modem_set_supported_bands (ctx->skeleton, mm_common_bands_garray_to_variant (bands_array)); g_array_unref (bands_array); } if (error) mm_obj_dbg (self, "couldn't load supported bands: %s", error->message); /* Go on to next step */ ctx->step++; interface_initialization_step (task); } static void load_supported_ip_families_ready (MMIfaceModem *self, GAsyncResult *res, GTask *task) { InitializationContext *ctx; g_autoptr(GError) error = NULL; MMBearerIpFamily ip_families; ctx = g_task_get_task_data (task); ip_families = MM_IFACE_MODEM_GET_INTERFACE (self)->load_supported_ip_families_finish (self, res, &error); if (ip_families != MM_BEARER_IP_FAMILY_NONE) mm_gdbus_modem_set_supported_ip_families (ctx->skeleton, ip_families); if (error) mm_obj_dbg (self, "couldn't load supported IP families: %s", error->message); /* Go on to next step */ ctx->step++; interface_initialization_step (task); } UINT_REPLY_READY_FN (power_state, "power state") static void load_current_modes_ready (MMIfaceModem *self, GAsyncResult *res, GTask *task) { InitializationContext *ctx; MMModemMode allowed = MM_MODEM_MODE_NONE; MMModemMode preferred = MM_MODEM_MODE_NONE; g_autoptr(GError) error = NULL; ctx = g_task_get_task_data (task); if (!MM_IFACE_MODEM_GET_INTERFACE (self)->load_current_modes_finish (self, res, &allowed, &preferred, &error)) /* Errors when getting allowed/preferred won't be critical */ mm_obj_dbg (self, "couldn't load current allowed/preferred modes: %s", error->message); else mm_gdbus_modem_set_current_modes (ctx->skeleton, g_variant_new ("(uu)", allowed, preferred)); /* Done, Go on to next step */ ctx->step++; interface_initialization_step (task); } static void load_current_bands_ready (MMIfaceModem *self, GAsyncResult *res, GTask *task) { InitializationContext *ctx; GArray *current_bands; g_autoptr(GError) error = NULL; ctx = g_task_get_task_data (task); current_bands = MM_IFACE_MODEM_GET_INTERFACE (self)->load_current_bands_finish (self, res, &error); if (!current_bands) { /* Errors when getting current bands won't be critical */ mm_obj_dbg (self, "couldn't load current bands: %s", error->message); } else { GArray *filtered_bands; GArray *supported_bands; supported_bands = (mm_common_bands_variant_to_garray ( mm_gdbus_modem_get_supported_bands (ctx->skeleton))); filtered_bands = mm_filter_current_bands (supported_bands, current_bands); g_array_unref (current_bands); if (supported_bands) g_array_unref (supported_bands); if (filtered_bands) { mm_common_bands_garray_sort (filtered_bands); mm_gdbus_modem_set_current_bands (ctx->skeleton, mm_common_bands_garray_to_variant (filtered_bands)); g_array_unref (filtered_bands); } } /* Done, Go on to next step */ ctx->step++; interface_initialization_step (task); } static void setup_sim_hot_swap_ready (MMIfaceModem *self, GAsyncResult *res, GTask *task) { Private *priv; InitializationContext *ctx; g_autoptr(GError) error = NULL; priv = get_private (self); ctx = g_task_get_task_data (task); MM_IFACE_MODEM_GET_INTERFACE (self)->setup_sim_hot_swap_finish (self, res, &error); if (error) mm_obj_info (self, "SIM hot swap setup failed: %s", error->message); else { mm_obj_info (self, "SIM hot swap setup succeeded"); priv->sim_hot_swap_configured = TRUE; } /* Go on to next step */ ctx->step++; interface_initialization_step (task); } static void load_sim_slots_ready (MMIfaceModem *self, GAsyncResult *res, GTask *task) { InitializationContext *ctx; g_autoptr(GPtrArray) sim_slots = NULL; g_autoptr(GError) error = NULL; guint primary_sim_slot = 0; ctx = g_task_get_task_data (task); if (!MM_IFACE_MODEM_GET_INTERFACE (self)->load_sim_slots_finish (self, res, &sim_slots, &primary_sim_slot, &error)) mm_obj_dbg (self, "couldn't query SIM slots: %s", error->message); if (sim_slots) { MMBaseSim *primary_sim = NULL; GPtrArray *sim_slot_paths_array; g_auto(GStrv) sim_slot_paths = NULL; guint i; g_assert (primary_sim_slot); g_assert_cmpuint (primary_sim_slot, <=, sim_slots->len); sim_slot_paths_array = g_ptr_array_new (); for (i = 0; i < sim_slots->len; i++) { MMBaseSim *sim; const gchar *sim_path; sim = MM_BASE_SIM (g_ptr_array_index (sim_slots, i)); if (!sim) { g_ptr_array_add (sim_slot_paths_array, g_strdup ("/")); continue; } sim_path = mm_base_sim_get_path (sim); g_ptr_array_add (sim_slot_paths_array, g_strdup (sim_path)); } g_ptr_array_add (sim_slot_paths_array, NULL); sim_slot_paths = (GStrv) g_ptr_array_free (sim_slot_paths_array, FALSE); mm_gdbus_modem_set_sim_slots (ctx->skeleton, (const gchar *const *)sim_slot_paths); mm_gdbus_modem_set_primary_sim_slot (ctx->skeleton, primary_sim_slot); /* If loading SIM slots is supported, we also expose already the primary active SIM object */ if (primary_sim_slot) { primary_sim = g_ptr_array_index (sim_slots, primary_sim_slot - 1); if (primary_sim) g_object_bind_property (primary_sim, MM_BASE_SIM_PATH, ctx->skeleton, "sim", G_BINDING_DEFAULT | G_BINDING_SYNC_CREATE); } g_object_set (self, MM_IFACE_MODEM_SIM, primary_sim, MM_IFACE_MODEM_SIM_SLOTS, sim_slots, NULL); } /* Go on to next step */ ctx->step++; interface_initialization_step (task); } static void modem_update_lock_info_ready (MMIfaceModem *self, GAsyncResult *res, GTask *task) { InitializationContext *ctx; ctx = g_task_get_task_data (task); /* NOTE: we already propagated the lock state, no need to do it again */ mm_iface_modem_update_lock_info_finish (self, res, &ctx->fatal_error); if (ctx->fatal_error) { g_prefix_error (&ctx->fatal_error, "Couldn't check unlock status: "); /* Jump to the last step */ ctx->step = INITIALIZATION_STEP_LAST; } else /* Go on to next step */ ctx->step++; interface_initialization_step (task); } static void sim_new_ready (GAsyncInitable *initable, GAsyncResult *res, GTask *task) { MMIfaceModem *self; InitializationContext *ctx; MMBaseSim *sim; GError *error = NULL; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); sim = MM_IFACE_MODEM_GET_INTERFACE (self)->create_sim_finish (self, res, &error); if (error) { mm_obj_warn (self, "couldn't create SIM: %s", error->message); g_task_return_error (task, error); g_object_unref (task); return; } /* We may get error with !sim, when the implementation doesn't want to * handle any (e.g. CDMA) */ if (sim) { g_object_bind_property (sim, MM_BASE_SIM_PATH, ctx->skeleton, "sim", G_BINDING_DEFAULT | G_BINDING_SYNC_CREATE); g_object_set (self, MM_IFACE_MODEM_SIM, sim, NULL); g_object_unref (sim); } /* Go on to next step */ ctx->step++; interface_initialization_step (task); } static void sim_reinit_ready (MMBaseSim *sim, GAsyncResult *res, GTask *task) { MMIfaceModem *self; InitializationContext *ctx; g_autoptr(GError) error = NULL; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); if (!mm_base_sim_initialize_finish (sim, res, &error)) mm_obj_warn (self, "SIM re-initialization failed: %s", error ? error->message : "unknown error"); /* Go on to next step */ ctx->step++; interface_initialization_step (task); } static void setup_carrier_config_ready (MMIfaceModem *self, GAsyncResult *res, GTask *task) { InitializationContext *ctx; g_autoptr(GError) error = NULL; ctx = g_task_get_task_data (task); if (!MM_IFACE_MODEM_GET_INTERFACE (self)->setup_carrier_config_finish (self, res, &error)) mm_obj_warn (self, "couldn't setup carrier config: %s", error->message); /* Go on to next step */ ctx->step++; interface_initialization_step (task); } static void load_carrier_config_ready (MMIfaceModem *self, GAsyncResult *res, GTask *task) { InitializationContext *ctx; g_autoptr(GError) error = NULL; g_autofree gchar *name = NULL; g_autofree gchar *revision = NULL; ctx = g_task_get_task_data (task); if (!MM_IFACE_MODEM_GET_INTERFACE (self)->load_carrier_config_finish (self, res, &name, &revision, &error)) mm_obj_dbg (self, "couldn't load carrier config: %s", error->message); else { mm_gdbus_modem_set_carrier_configuration (ctx->skeleton, name); mm_gdbus_modem_set_carrier_configuration_revision (ctx->skeleton, revision); } /* Go on to next step */ ctx->step++; interface_initialization_step (task); } static void load_own_numbers_ready (MMIfaceModem *self, GAsyncResult *res, GTask *task) { InitializationContext *ctx; g_autoptr(GError) error = NULL; g_auto(GStrv) str_list = NULL; ctx = g_task_get_task_data (task); str_list = MM_IFACE_MODEM_GET_INTERFACE (self)->load_own_numbers_finish (self, res, &error); if (error) mm_obj_dbg (self, "couldn't load list of own numbers: %s", error->message); if (str_list) mm_gdbus_modem_set_own_numbers (ctx->skeleton, (const gchar *const *) str_list); /* Go on to next step */ ctx->step++; interface_initialization_step (task); } static void interface_initialization_step (GTask *task) { MMIfaceModem *self; InitializationContext *ctx; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); /* Don't run new steps if we're cancelled */ if (g_task_return_error_if_cancelled (task)) { /* Simply ignore any fatal error encountered as the initialization is cancelled anyway. */ g_clear_error (&ctx->fatal_error); g_object_unref (task); return; } switch (ctx->step) { case INITIALIZATION_STEP_FIRST: /* Load device if not done before */ if (!mm_gdbus_modem_get_device (ctx->skeleton)) { gchar *device; g_object_get (self, MM_BASE_MODEM_DEVICE, &device, NULL); mm_gdbus_modem_set_device (ctx->skeleton, device); g_free (device); } /* Load physdev path if not done before */ if (!mm_gdbus_modem_get_physdev (ctx->skeleton)) { gchar *physdev; g_object_get (self, MM_BASE_MODEM_PHYSDEV, &physdev, NULL); mm_gdbus_modem_set_physdev (ctx->skeleton, physdev); g_free (physdev); } /* Load driver if not done before */ if (!mm_gdbus_modem_get_drivers (ctx->skeleton)) { gchar **drivers; g_object_get (self, MM_BASE_MODEM_DRIVERS, &drivers, NULL); mm_gdbus_modem_set_drivers (ctx->skeleton, (const gchar * const *)drivers); g_strfreev (drivers); } /* Load plugin if not done before */ if (!mm_gdbus_modem_get_plugin (ctx->skeleton)) { gchar *plugin; g_object_get (self, MM_BASE_MODEM_PLUGIN, &plugin, NULL); mm_gdbus_modem_set_plugin (ctx->skeleton, plugin); g_free (plugin); } /* Load primary port if not done before */ if (!mm_gdbus_modem_get_primary_port (ctx->skeleton)) { MMPort *primary = NULL; #if defined WITH_QMI if (MM_IS_BROADBAND_MODEM_QMI (self)) primary = MM_PORT (mm_broadband_modem_qmi_peek_port_qmi (MM_BROADBAND_MODEM_QMI (self))); #endif #if defined WITH_MBIM if (!primary && MM_IS_BROADBAND_MODEM_MBIM (self)) primary = MM_PORT (mm_broadband_modem_mbim_peek_port_mbim (MM_BROADBAND_MODEM_MBIM (self))); #endif if (!primary) primary = MM_PORT (mm_base_modem_peek_port_primary (MM_BASE_MODEM (self))); g_assert (primary != NULL); mm_gdbus_modem_set_primary_port (ctx->skeleton, mm_port_get_device (primary)); } /* Load ports if not done before */ if (!mm_gdbus_modem_get_ports (ctx->skeleton)) { MMModemPortInfo *port_infos; guint n_port_infos; port_infos = mm_base_modem_get_port_infos (MM_BASE_MODEM (self), &n_port_infos); mm_gdbus_modem_set_ports (ctx->skeleton, mm_common_ports_array_to_variant (port_infos, n_port_infos)); mm_modem_port_info_array_free (port_infos, n_port_infos); } ctx->step++; /* fall-through */ case INITIALIZATION_STEP_CURRENT_CAPABILITIES: /* Current capabilities may change during runtime, i.e. if new firmware reloaded; but we'll * try to handle that by making sure the capabilities are cleared when the new firmware is * reloaded. So if we're asked to re-initialize, if we already have current capabilities loaded, * don't try to load them again. */ if (mm_gdbus_modem_get_current_capabilities (ctx->skeleton) == MM_MODEM_CAPABILITY_NONE && MM_IFACE_MODEM_GET_INTERFACE (self)->load_current_capabilities && MM_IFACE_MODEM_GET_INTERFACE (self)->load_current_capabilities_finish) { MM_IFACE_MODEM_GET_INTERFACE (self)->load_current_capabilities ( self, (GAsyncReadyCallback)load_current_capabilities_ready, task); return; } ctx->step++; /* fall-through */ case INITIALIZATION_STEP_SUPPORTED_CAPABILITIES: { GArray *supported_capabilities; supported_capabilities = (mm_common_capability_combinations_variant_to_garray ( mm_gdbus_modem_get_supported_capabilities (ctx->skeleton))); /* Supported capabilities are meant to be loaded only once during the whole * lifetime of the modem. Therefore, if we already have them loaded, * don't try to load them again. */ if (supported_capabilities->len == 0 || g_array_index (supported_capabilities, MMModemCapability, 0) == MM_MODEM_CAPABILITY_NONE) { MMModemCapability current; if (MM_IFACE_MODEM_GET_INTERFACE (self)->load_supported_capabilities && MM_IFACE_MODEM_GET_INTERFACE (self)->load_supported_capabilities_finish) { MM_IFACE_MODEM_GET_INTERFACE (self)->load_supported_capabilities ( self, (GAsyncReadyCallback)load_supported_capabilities_ready, task); g_array_unref (supported_capabilities); return; } /* If no specific way of getting modem capabilities, default to the current ones */ g_array_unref (supported_capabilities); supported_capabilities = g_array_sized_new (FALSE, FALSE, sizeof (MMModemCapability), 1); current = mm_gdbus_modem_get_current_capabilities (ctx->skeleton); g_array_append_val (supported_capabilities, current); mm_gdbus_modem_set_supported_capabilities ( ctx->skeleton, mm_common_capability_combinations_garray_to_variant (supported_capabilities)); } g_array_unref (supported_capabilities); ctx->step++; } /* fall-through */ case INITIALIZATION_STEP_SUPPORTED_CHARSETS: if (MM_IFACE_MODEM_GET_INTERFACE (self)->load_supported_charsets && MM_IFACE_MODEM_GET_INTERFACE (self)->load_supported_charsets_finish) { MM_IFACE_MODEM_GET_INTERFACE (self)->load_supported_charsets ( self, (GAsyncReadyCallback)load_supported_charsets_ready, task); return; } ctx->step++; /* fall-through */ case INITIALIZATION_STEP_CHARSET: /* Only try to set charsets if we were able to load supported ones */ if (ctx->supported_charsets > 0 && MM_IFACE_MODEM_GET_INTERFACE (self)->setup_charset && MM_IFACE_MODEM_GET_INTERFACE (self)->setup_charset_finish) { gboolean next_to_try = FALSE; while (!next_to_try) { if (!ctx->current_charset) /* Switch the device's charset; we prefer UTF-8, but UCS2 will do too */ ctx->current_charset = &best_charsets[0]; else /* Try with the next one */ ctx->current_charset++; if (*ctx->current_charset == MM_MODEM_CHARSET_UNKNOWN) break; if (ctx->supported_charsets & (*ctx->current_charset)) next_to_try = TRUE; } if (next_to_try) { MM_IFACE_MODEM_GET_INTERFACE (self)->setup_charset ( self, *ctx->current_charset, (GAsyncReadyCallback)setup_charset_ready, task); return; } mm_obj_warn (self, "Failed to find usable modem character set, let it to UNKNOWN"); } ctx->step++; /* fall-through */ case INITIALIZATION_STEP_MANUFACTURER: /* Manufacturer is meant to be loaded only once during the whole * lifetime of the modem. Therefore, if we already have them loaded, * don't try to load them again. */ if (mm_gdbus_modem_get_manufacturer (ctx->skeleton) == NULL && MM_IFACE_MODEM_GET_INTERFACE (self)->load_manufacturer && MM_IFACE_MODEM_GET_INTERFACE (self)->load_manufacturer_finish) { MM_IFACE_MODEM_GET_INTERFACE (self)->load_manufacturer ( self, (GAsyncReadyCallback)load_manufacturer_ready, task); return; } ctx->step++; /* fall-through */ case INITIALIZATION_STEP_MODEL: /* Model is meant to be loaded only once during the whole * lifetime of the modem. Therefore, if we already have them loaded, * don't try to load them again. */ if (mm_gdbus_modem_get_model (ctx->skeleton) == NULL && MM_IFACE_MODEM_GET_INTERFACE (self)->load_model && MM_IFACE_MODEM_GET_INTERFACE (self)->load_model_finish) { MM_IFACE_MODEM_GET_INTERFACE (self)->load_model ( self, (GAsyncReadyCallback)load_model_ready, task); return; } ctx->step++; /* fall-through */ case INITIALIZATION_STEP_REVISION: /* Revision is meant to be loaded only once during the whole * lifetime of the modem. Therefore, if we already have them loaded, * don't try to load them again. */ if (mm_gdbus_modem_get_revision (ctx->skeleton) == NULL && MM_IFACE_MODEM_GET_INTERFACE (self)->load_revision && MM_IFACE_MODEM_GET_INTERFACE (self)->load_revision_finish) { MM_IFACE_MODEM_GET_INTERFACE (self)->load_revision ( self, (GAsyncReadyCallback)load_revision_ready, task); return; } ctx->step++; /* fall-through */ case INITIALIZATION_STEP_BEARERS: { /* This step should be run always after having loaded the firmware revision * number, because certain modems may have multiplexing support only in * new releases. */ g_autoptr(MMBearerList) list = NULL; /* Bearers setup is meant to be loaded only once during the whole * lifetime of the modem, so check if it exists; and if it doesn't, * create it right away. */ g_object_get (self, MM_IFACE_MODEM_BEARER_LIST, &list, NULL); if (!list) { list = MM_IFACE_MODEM_GET_INTERFACE (self)->create_bearer_list (self); g_signal_connect (list, "notify::" MM_BEARER_LIST_NUM_BEARERS, G_CALLBACK (bearer_list_updated), self); mm_gdbus_modem_set_max_active_bearers ( ctx->skeleton, mm_bearer_list_get_max_active (list)); mm_gdbus_modem_set_max_active_multiplexed_bearers ( ctx->skeleton, mm_bearer_list_get_max_active_multiplexed (list)); /* MaxBearers set equal to MaxActiveBearers */ mm_gdbus_modem_set_max_bearers ( ctx->skeleton, mm_gdbus_modem_get_max_active_bearers (ctx->skeleton)); g_object_set (self, MM_IFACE_MODEM_BEARER_LIST, list, NULL); } ctx->step++; } /* fall-through */ case INITIALIZATION_STEP_CARRIER_CONFIG: /* Current carrier config is meant to be loaded only once during the whole * lifetime of the modem. Therefore, if we already have them loaded, * don't try to load them again. */ if (mm_gdbus_modem_get_carrier_configuration (ctx->skeleton) == NULL && MM_IFACE_MODEM_GET_INTERFACE (self)->load_carrier_config && MM_IFACE_MODEM_GET_INTERFACE (self)->load_carrier_config_finish) { MM_IFACE_MODEM_GET_INTERFACE (self)->load_carrier_config (self, (GAsyncReadyCallback)load_carrier_config_ready, task); return; } ctx->step++; /* fall-through */ case INITIALIZATION_STEP_HARDWARE_REVISION: /* HardwareRevision is meant to be loaded only once during the whole * lifetime of the modem. Therefore, if we already have them loaded, * don't try to load them again. */ if (mm_gdbus_modem_get_hardware_revision (ctx->skeleton) == NULL && MM_IFACE_MODEM_GET_INTERFACE (self)->load_hardware_revision && MM_IFACE_MODEM_GET_INTERFACE (self)->load_hardware_revision_finish) { MM_IFACE_MODEM_GET_INTERFACE (self)->load_hardware_revision ( self, (GAsyncReadyCallback)load_hardware_revision_ready, task); return; } ctx->step++; /* fall-through */ case INITIALIZATION_STEP_EQUIPMENT_ID: /* Equipment ID is meant to be loaded only once during the whole * lifetime of the modem. Therefore, if we already have them loaded, * don't try to load them again. */ if (mm_gdbus_modem_get_equipment_identifier (ctx->skeleton) == NULL && MM_IFACE_MODEM_GET_INTERFACE (self)->load_equipment_identifier && MM_IFACE_MODEM_GET_INTERFACE (self)->load_equipment_identifier_finish) { MM_IFACE_MODEM_GET_INTERFACE (self)->load_equipment_identifier ( self, (GAsyncReadyCallback)load_equipment_identifier_ready, task); return; } ctx->step++; /* fall-through */ case INITIALIZATION_STEP_DEVICE_ID: /* Device ID is meant to be loaded only once during the whole * lifetime of the modem. Therefore, if we already have them loaded, * don't try to load them again. */ if (mm_gdbus_modem_get_device_identifier (ctx->skeleton) == NULL && MM_IFACE_MODEM_GET_INTERFACE (self)->load_device_identifier && MM_IFACE_MODEM_GET_INTERFACE (self)->load_device_identifier_finish) { MM_IFACE_MODEM_GET_INTERFACE (self)->load_device_identifier ( self, (GAsyncReadyCallback)load_device_identifier_ready, task); return; } ctx->step++; /* fall-through */ case INITIALIZATION_STEP_SUPPORTED_MODES: if (MM_IFACE_MODEM_GET_INTERFACE (self)->load_supported_modes != NULL && MM_IFACE_MODEM_GET_INTERFACE (self)->load_supported_modes_finish != NULL) { GArray *supported_modes; MMModemModeCombination *mode = NULL; supported_modes = (mm_common_mode_combinations_variant_to_garray ( mm_gdbus_modem_get_supported_modes (ctx->skeleton))); /* Supported modes are meant to be loaded only once during the whole * lifetime of the modem. Therefore, if we already have them loaded, * don't try to load them again. */ if (supported_modes->len == 1) mode = &g_array_index (supported_modes, MMModemModeCombination, 0); if (supported_modes->len == 0 || (mode && mode->allowed == MM_MODEM_MODE_ANY && mode->preferred == MM_MODEM_MODE_NONE)) { MM_IFACE_MODEM_GET_INTERFACE (self)->load_supported_modes ( self, (GAsyncReadyCallback)load_supported_modes_ready, task); g_array_unref (supported_modes); return; } g_array_unref (supported_modes); } ctx->step++; /* fall-through */ case INITIALIZATION_STEP_SUPPORTED_BANDS: { GArray *supported_bands; supported_bands = (mm_common_bands_variant_to_garray ( mm_gdbus_modem_get_supported_bands (ctx->skeleton))); /* Supported bands are meant to be loaded only once during the whole * lifetime of the modem. Therefore, if we already have them loaded, * don't try to load them again. */ if (supported_bands->len == 0 || g_array_index (supported_bands, MMModemBand, 0) == MM_MODEM_BAND_UNKNOWN) { if (MM_IFACE_MODEM_GET_INTERFACE (self)->load_supported_bands && MM_IFACE_MODEM_GET_INTERFACE (self)->load_supported_bands_finish) { MM_IFACE_MODEM_GET_INTERFACE (self)->load_supported_bands ( self, (GAsyncReadyCallback)load_supported_bands_ready, task); g_array_unref (supported_bands); return; } /* Loading supported bands not implemented, default to UNKNOWN */ mm_gdbus_modem_set_supported_bands (ctx->skeleton, mm_common_build_bands_unknown ()); mm_gdbus_modem_set_current_bands (ctx->skeleton, mm_common_build_bands_unknown ()); } g_array_unref (supported_bands); ctx->step++; } /* fall-through */ case INITIALIZATION_STEP_SUPPORTED_IP_FAMILIES: /* Supported ip_families are meant to be loaded only once during the whole * lifetime of the modem. Therefore, if we already have them loaded, * don't try to load them again. */ if (MM_IFACE_MODEM_GET_INTERFACE (self)->load_supported_ip_families != NULL && MM_IFACE_MODEM_GET_INTERFACE (self)->load_supported_ip_families_finish != NULL && mm_gdbus_modem_get_supported_ip_families (ctx->skeleton) == MM_BEARER_IP_FAMILY_NONE) { MM_IFACE_MODEM_GET_INTERFACE (self)->load_supported_ip_families ( self, (GAsyncReadyCallback)load_supported_ip_families_ready, task); return; } ctx->step++; /* fall-through */ case INITIALIZATION_STEP_POWER_STATE: /* Initial power state is meant to be loaded only once. Therefore, if we * already have it loaded, don't try to load it again. */ if (mm_gdbus_modem_get_power_state (ctx->skeleton) == MM_MODEM_POWER_STATE_UNKNOWN) { if (MM_IFACE_MODEM_GET_INTERFACE (self)->load_power_state && MM_IFACE_MODEM_GET_INTERFACE (self)->load_power_state_finish) { MM_IFACE_MODEM_GET_INTERFACE (self)->load_power_state ( self, (GAsyncReadyCallback)load_power_state_ready, task); return; } /* We don't know how to load current power state; assume ON */ mm_gdbus_modem_set_power_state (ctx->skeleton, MM_MODEM_POWER_STATE_ON); } ctx->step++; /* fall-through */ case INITIALIZATION_STEP_CURRENT_MODES: { MMModemMode allowed = MM_MODEM_MODE_ANY; MMModemMode preferred = MM_MODEM_MODE_NONE; GVariant *aux; aux = mm_gdbus_modem_get_current_modes (ctx->skeleton); if (aux) g_variant_get (aux, "(uu)", &allowed, &preferred); /* Current modes are only meant to be loaded once, so if we have them * loaded already, just skip re-loading */ if (allowed == MM_MODEM_MODE_ANY && preferred == MM_MODEM_MODE_NONE) { GArray *supported; supported = (mm_common_mode_combinations_variant_to_garray ( mm_gdbus_modem_get_supported_modes (ctx->skeleton))); /* If there is only one item in the list of supported modes, we're done */ if (supported && supported->len == 1) { MMModemModeCombination *supported_mode; supported_mode = &g_array_index (supported, MMModemModeCombination, 0); mm_gdbus_modem_set_current_modes (ctx->skeleton, g_variant_new ("(uu)", supported_mode->allowed, supported_mode->preferred)); } else if (MM_IFACE_MODEM_GET_INTERFACE (self)->load_current_modes && MM_IFACE_MODEM_GET_INTERFACE (self)->load_current_modes_finish) { MM_IFACE_MODEM_GET_INTERFACE (self)->load_current_modes ( self, (GAsyncReadyCallback)load_current_modes_ready, task); if (supported) g_array_unref (supported); return; } if (supported) g_array_unref (supported); } ctx->step++; } /* fall-through */ case INITIALIZATION_STEP_CURRENT_BANDS: { GArray *current; current = (mm_common_bands_variant_to_garray ( mm_gdbus_modem_get_current_bands (ctx->skeleton))); /* Current bands are only meant to be loaded once, so if we have them * loaded already, just skip re-loading */ if (!current || (current->len == 1 && g_array_index (current, MMModemBand, 0) == MM_MODEM_BAND_UNKNOWN)) { if (MM_IFACE_MODEM_GET_INTERFACE (self)->load_current_bands && MM_IFACE_MODEM_GET_INTERFACE (self)->load_current_bands_finish) { MM_IFACE_MODEM_GET_INTERFACE (self)->load_current_bands ( self, (GAsyncReadyCallback)load_current_bands_ready, task); if (current) g_array_unref (current); return; } /* If no way to get current bands, default to what supported has */ mm_gdbus_modem_set_current_bands (ctx->skeleton, mm_gdbus_modem_get_supported_bands (ctx->skeleton)); } if (current) g_array_unref (current); ctx->step++; } /* fall-through */ case INITIALIZATION_STEP_SIM_HOT_SWAP: { Private *priv; priv = get_private (self); if (!priv->sim_hot_swap_configured && MM_IFACE_MODEM_GET_INTERFACE (self)->setup_sim_hot_swap && MM_IFACE_MODEM_GET_INTERFACE (self)->setup_sim_hot_swap_finish) { MM_IFACE_MODEM_GET_INTERFACE (self)->setup_sim_hot_swap ( MM_IFACE_MODEM (self), (GAsyncReadyCallback) setup_sim_hot_swap_ready, task); return; } ctx->step++; } /* fall-through */ case INITIALIZATION_STEP_SIM_SLOTS: /* If the modem doesn't need any SIM (not implemented by plugin, or not * needed in CDMA-only modems), or if we don't know how to query * for SIM slots */ if (!mm_gdbus_modem_get_sim_slots (ctx->skeleton) && !mm_iface_modem_is_cdma_only (self) && MM_IFACE_MODEM_GET_INTERFACE (self)->load_sim_slots && MM_IFACE_MODEM_GET_INTERFACE (self)->load_sim_slots_finish) { MM_IFACE_MODEM_GET_INTERFACE (self)->load_sim_slots (MM_IFACE_MODEM (self), (GAsyncReadyCallback)load_sim_slots_ready, task); return; } ctx->step++; /* fall-through */ case INITIALIZATION_STEP_UNLOCK_REQUIRED: { g_autoptr(MMBaseSim) sim = NULL; g_object_get (self, MM_IFACE_MODEM_SIM, &sim, NULL); /* If the current SIM is an eSIM without profiles, we ignore * unlock required. */ if (sim && mm_base_sim_is_esim_without_profiles (sim)) mm_obj_dbg (self, "not unlock required: eSIM without profiles"); else { /* Only check unlock required if we were previously not unlocked */ if (mm_gdbus_modem_get_unlock_required (ctx->skeleton) != MM_MODEM_LOCK_NONE) { mm_iface_modem_update_lock_info (self, MM_MODEM_LOCK_UNKNOWN, /* ask */ (GAsyncReadyCallback)modem_update_lock_info_ready, task); return; } } ctx->step++; } /* fall-through */ case INITIALIZATION_STEP_SIM: /* If the modem doesn't need any SIM (not implemented by plugin, or not * needed in CDMA-only modems) */ if (!mm_iface_modem_is_cdma_only (self) && MM_IFACE_MODEM_GET_INTERFACE (self)->create_sim && MM_IFACE_MODEM_GET_INTERFACE (self)->create_sim_finish) { MMBaseSim *sim = NULL; g_object_get (self, MM_IFACE_MODEM_SIM, &sim, NULL); if (!sim) { MM_IFACE_MODEM_GET_INTERFACE (self)->create_sim ( MM_IFACE_MODEM (self), (GAsyncReadyCallback)sim_new_ready, task); return; } /* If already available the sim object, relaunch initialization. * This will try to load any missing property value that couldn't be * retrieved before due to having the SIM locked. */ mm_base_sim_initialize (sim, g_task_get_cancellable (task), (GAsyncReadyCallback)sim_reinit_ready, task); g_object_unref (sim); return; } ctx->step++; /* fall-through */ case INITIALIZATION_STEP_SETUP_CARRIER_CONFIG: /* Setup and perform automatic carrier config switching as soon as the * SIM initialization has been performed, only applicable if there is * actually a SIM found with a valid IMSI read */ if (!mm_iface_modem_is_cdma_only (self) && MM_IFACE_MODEM_GET_INTERFACE (self)->setup_carrier_config && MM_IFACE_MODEM_GET_INTERFACE (self)->setup_carrier_config_finish) { g_autoptr(MMBaseSim) sim = NULL; g_autofree gchar *carrier_config_mapping = NULL; g_object_get (self, MM_IFACE_MODEM_SIM, &sim, MM_IFACE_MODEM_CARRIER_CONFIG_MAPPING, &carrier_config_mapping, NULL); /* If we have a SIM object, and carrier config switching is supported, * validate whether we're already using the best config or not. */ if (!sim) mm_obj_dbg (self, "not setting up carrier config: SIM not found"); else if (mm_base_sim_is_esim_without_profiles (sim)) mm_obj_dbg (self, "not setting up carrier config: eSIM without profiles"); else if (!carrier_config_mapping) mm_obj_dbg (self, "not setting up carrier config: mapping file not configured"); else { const gchar *imsi; imsi = mm_gdbus_sim_get_imsi (MM_GDBUS_SIM (sim)); if (!imsi) mm_obj_dbg (self, "not setting up carrier config: unknown IMSI"); else { MM_IFACE_MODEM_GET_INTERFACE (self)->setup_carrier_config (self, imsi, carrier_config_mapping, (GAsyncReadyCallback)setup_carrier_config_ready, task); return; } } } ctx->step++; /* fall-through */ case INITIALIZATION_STEP_OWN_NUMBERS: /* Own numbers is meant to be loaded only once during the whole * lifetime of the modem. Therefore, if we already have them loaded, * don't try to load them again. */ if (mm_gdbus_modem_get_own_numbers (ctx->skeleton) == NULL && MM_IFACE_MODEM_GET_INTERFACE (self)->load_own_numbers && MM_IFACE_MODEM_GET_INTERFACE (self)->load_own_numbers_finish) { MM_IFACE_MODEM_GET_INTERFACE (self)->load_own_numbers ( self, (GAsyncReadyCallback)load_own_numbers_ready, task); return; } ctx->step++; /* fall-through */ case INITIALIZATION_STEP_VALIDATE_ESIM_STATUS: { g_autoptr(MMBaseSim) sim = NULL; g_object_get (self, MM_IFACE_MODEM_SIM, &sim, NULL); /* If the current SIM is an eSIM without profiles, we transition to FAILED * status because the modem is really unusable. */ if (sim && mm_base_sim_is_esim_without_profiles (sim)) { g_clear_error (&ctx->fatal_error); ctx->fatal_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_WRONG_SIM_STATE, "eSIM without profiles detected"); } ctx->step++; } /* fall-through */ case INITIALIZATION_STEP_LAST: /* Setup all method handlers */ g_object_connect (ctx->skeleton, "signal::handle-set-current-capabilities", G_CALLBACK (handle_set_current_capabilities), self, "signal::handle-set-power-state", G_CALLBACK (handle_set_power_state), self, "signal::handle-reset", G_CALLBACK (handle_reset), self, "signal::handle-factory-reset", G_CALLBACK (handle_factory_reset), self, "signal::handle-create-bearer", G_CALLBACK (handle_create_bearer), self, "signal::handle-command", G_CALLBACK (handle_command), self, "signal::handle-delete-bearer", G_CALLBACK (handle_delete_bearer), self, "signal::handle-list-bearers", G_CALLBACK (handle_list_bearers), self, "signal::handle-enable", G_CALLBACK (handle_enable), self, "signal::handle-set-current-bands", G_CALLBACK (handle_set_current_bands), self, "signal::handle-set-current-modes", G_CALLBACK (handle_set_current_modes), self, "signal::handle-set-primary-sim-slot", G_CALLBACK (handle_set_primary_sim_slot), self, "signal::handle-get-cell-info", G_CALLBACK (handle_get_cell_info), self, NULL); /* Finally, export the new interface, even if we got errors, but only if not * done already */ if (!mm_gdbus_object_peek_modem (MM_GDBUS_OBJECT (self))) mm_gdbus_object_skeleton_set_modem (MM_GDBUS_OBJECT_SKELETON (self), MM_GDBUS_MODEM (ctx->skeleton)); if (ctx->fatal_error) g_task_return_error (task, g_steal_pointer (&ctx->fatal_error)); else g_task_return_boolean (task, TRUE); g_object_unref (task); return; default: break; } g_assert_not_reached (); } gboolean mm_iface_modem_initialize_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } void mm_iface_modem_initialize (MMIfaceModem *self, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { InitializationContext *ctx; MmGdbusModem *skeleton = NULL; GTask *task; /* Did we already create it? */ g_object_get (self, MM_IFACE_MODEM_DBUS_SKELETON, &skeleton, NULL); if (!skeleton) { skeleton = mm_gdbus_modem_skeleton_new (); /* Set all initial property defaults */ mm_gdbus_modem_set_sim (skeleton, NULL); mm_gdbus_modem_set_supported_capabilities (skeleton, mm_common_build_capability_combinations_none ()); mm_gdbus_modem_set_current_capabilities (skeleton, MM_MODEM_CAPABILITY_NONE); mm_gdbus_modem_set_max_bearers (skeleton, 0); mm_gdbus_modem_set_max_active_bearers (skeleton, 0); mm_gdbus_modem_set_manufacturer (skeleton, NULL); mm_gdbus_modem_set_model (skeleton, NULL); mm_gdbus_modem_set_revision (skeleton, NULL); mm_gdbus_modem_set_own_numbers (skeleton, NULL); mm_gdbus_modem_set_device_identifier (skeleton, NULL); mm_gdbus_modem_set_device (skeleton, NULL); mm_gdbus_modem_set_physdev (skeleton, NULL); mm_gdbus_modem_set_drivers (skeleton, NULL); mm_gdbus_modem_set_plugin (skeleton, NULL); mm_gdbus_modem_set_equipment_identifier (skeleton, NULL); mm_gdbus_modem_set_unlock_required (skeleton, MM_MODEM_LOCK_UNKNOWN); mm_gdbus_modem_set_unlock_retries (skeleton, 0); mm_gdbus_modem_set_access_technologies (skeleton, MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN); mm_gdbus_modem_set_signal_quality (skeleton, g_variant_new ("(ub)", 0, TRUE)); mm_gdbus_modem_set_supported_modes (skeleton, mm_common_build_mode_combinations_default ()); mm_gdbus_modem_set_current_modes (skeleton, g_variant_new ("(uu)", MM_MODEM_MODE_ANY, MM_MODEM_MODE_NONE)); mm_gdbus_modem_set_supported_bands (skeleton, mm_common_build_bands_unknown ()); mm_gdbus_modem_set_current_bands (skeleton, mm_common_build_bands_unknown ()); mm_gdbus_modem_set_supported_ip_families (skeleton, MM_BEARER_IP_FAMILY_NONE); mm_gdbus_modem_set_power_state (skeleton, MM_MODEM_POWER_STATE_UNKNOWN); mm_gdbus_modem_set_state_failed_reason (skeleton, MM_MODEM_STATE_FAILED_REASON_NONE); /* Bind our State property */ g_object_bind_property (self, MM_IFACE_MODEM_STATE, skeleton, "state", G_BINDING_DEFAULT | G_BINDING_SYNC_CREATE); g_object_set (self, MM_IFACE_MODEM_DBUS_SKELETON, skeleton, NULL); } /* Perform async initialization here */ ctx = g_new0 (InitializationContext, 1); ctx->step = INITIALIZATION_STEP_FIRST; ctx->skeleton = skeleton; task = g_task_new (self, NULL, callback, user_data); g_task_set_task_data (task, ctx, (GDestroyNotify)initialization_context_free); interface_initialization_step (task); } void mm_iface_modem_shutdown (MMIfaceModem *self) { /* Make sure signal polling is disabled. No real need to clear values, as * we're shutting down the interface anyway. */ periodic_signal_check_disable (self, FALSE); /* Make sure recent flag handling is disabled. */ signal_quality_recent_timeout_disable (self); /* Remove running restart initialization idle, if any */ restart_initialize_idle_disable (self); /* Cleanup SIM hot swap, if any */ if (MM_IFACE_MODEM_GET_INTERFACE (self)->cleanup_sim_hot_swap) MM_IFACE_MODEM_GET_INTERFACE (self)->cleanup_sim_hot_swap (self); /* Remove SIM object */ g_object_set (self, MM_IFACE_MODEM_SIM, NULL, NULL); /* Unexport DBus interface and remove the skeleton */ mm_gdbus_object_skeleton_set_modem (MM_GDBUS_OBJECT_SKELETON (self), NULL); g_object_set (self, MM_IFACE_MODEM_DBUS_SKELETON, NULL, NULL); } /*****************************************************************************/ MMModemAccessTechnology mm_iface_modem_get_access_technologies (MMIfaceModem *self) { MMModemAccessTechnology access_tech = MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN; MmGdbusModem *skeleton; g_object_get (self, MM_IFACE_MODEM_DBUS_SKELETON, &skeleton, NULL); if (skeleton) { access_tech = mm_gdbus_modem_get_access_technologies (skeleton); g_object_unref (skeleton); } return access_tech; } /*****************************************************************************/ static gboolean find_supported_mode (MMIfaceModem *self, MMModemMode mode, gboolean *only) { gboolean matched = FALSE; MmGdbusModem *skeleton; g_object_get (self, MM_IFACE_MODEM_DBUS_SKELETON, &skeleton, NULL); if (skeleton) { GArray *supported; guint i; guint n_unmatched = 0; supported = mm_common_mode_combinations_variant_to_garray ( mm_gdbus_modem_get_supported_modes (skeleton)); /* Check if the given mode is supported */ for (i = 0; i < supported->len; i++) { MMModemModeCombination *supported_mode; supported_mode = &g_array_index (supported, MMModemModeCombination, i); if (supported_mode->allowed & mode) { matched = TRUE; if (supported_mode->allowed != mode) n_unmatched++; } else n_unmatched++; if (matched && (only == NULL || n_unmatched > 0)) break; } if (only) *only = (n_unmatched == 0); g_array_unref (supported); g_object_unref (skeleton); } return matched; } gboolean mm_iface_modem_is_2g (MMIfaceModem *self) { return find_supported_mode (self, MM_MODEM_MODE_2G, NULL); } gboolean mm_iface_modem_is_2g_only (MMIfaceModem *self) { gboolean only; return (find_supported_mode (self, MM_MODEM_MODE_2G, &only) ? only : FALSE); } gboolean mm_iface_modem_is_3g (MMIfaceModem *self) { return find_supported_mode (self, MM_MODEM_MODE_3G, NULL); } gboolean mm_iface_modem_is_3g_only (MMIfaceModem *self) { gboolean only; return (find_supported_mode (self, MM_MODEM_MODE_3G, &only) ? only : FALSE); } gboolean mm_iface_modem_is_4g (MMIfaceModem *self) { return find_supported_mode (self, MM_MODEM_MODE_4G, NULL); } gboolean mm_iface_modem_is_4g_only (MMIfaceModem *self) { gboolean only; return (find_supported_mode (self, MM_MODEM_MODE_4G, &only) ? only : FALSE); } gboolean mm_iface_modem_is_5g (MMIfaceModem *self) { return find_supported_mode (self, MM_MODEM_MODE_5G, NULL); } gboolean mm_iface_modem_is_5g_only (MMIfaceModem *self) { gboolean only; return (find_supported_mode (self, MM_MODEM_MODE_5G, &only) ? only : FALSE); } /*****************************************************************************/ MMModemCapability mm_iface_modem_get_current_capabilities (MMIfaceModem *self) { MMModemCapability current = MM_MODEM_CAPABILITY_NONE; MmGdbusModem *skeleton; g_object_get (self, MM_IFACE_MODEM_DBUS_SKELETON, &skeleton, NULL); if (skeleton) { current = mm_gdbus_modem_get_current_capabilities (skeleton); g_object_unref (skeleton); } return current; } gboolean mm_iface_modem_is_3gpp (MMIfaceModem *self) { return (mm_iface_modem_get_current_capabilities (self) & MM_MODEM_CAPABILITY_3GPP); } gboolean mm_iface_modem_is_3gpp_lte (MMIfaceModem *self) { return (mm_iface_modem_get_current_capabilities (self) & MM_MODEM_CAPABILITY_LTE); } gboolean mm_iface_modem_is_3gpp_5gnr (MMIfaceModem *self) { return (mm_iface_modem_get_current_capabilities (self) & MM_MODEM_CAPABILITY_5GNR); } gboolean mm_iface_modem_is_cdma (MMIfaceModem *self) { return (mm_iface_modem_get_current_capabilities (self) & MM_MODEM_CAPABILITY_CDMA_EVDO); } gboolean mm_iface_modem_is_3gpp_only (MMIfaceModem *self) { MMModemCapability capabilities; capabilities = mm_iface_modem_get_current_capabilities (self); return (capabilities & MM_MODEM_CAPABILITY_3GPP) && !((MM_MODEM_CAPABILITY_3GPP ^ capabilities) & capabilities); } gboolean mm_iface_modem_is_cdma_only (MMIfaceModem *self) { return (mm_iface_modem_get_current_capabilities (self) == MM_MODEM_CAPABILITY_CDMA_EVDO); } /*****************************************************************************/ const gchar * mm_iface_modem_get_model (MMIfaceModem *self) { const gchar *model = NULL; MmGdbusModem *skeleton; g_object_get (self, MM_IFACE_MODEM_DBUS_SKELETON, &skeleton, NULL); if (skeleton) { model = mm_gdbus_modem_get_model (skeleton); g_object_unref (skeleton); } return model; } const gchar * mm_iface_modem_get_revision (MMIfaceModem *self) { const gchar *revision = NULL; MmGdbusModem *skeleton; g_object_get (self, MM_IFACE_MODEM_DBUS_SKELETON, &skeleton, NULL); if (skeleton) { revision = mm_gdbus_modem_get_revision (skeleton); g_object_unref (skeleton); } return revision; } gboolean mm_iface_modem_get_carrier_config (MMIfaceModem *self, const gchar **name, const gchar **revision) { MmGdbusModem *skeleton; g_object_get (self, MM_IFACE_MODEM_DBUS_SKELETON, &skeleton, NULL); if (!skeleton) return FALSE; if (name) *name = mm_gdbus_modem_get_carrier_configuration (skeleton); if (revision) *revision = mm_gdbus_modem_get_carrier_configuration_revision (skeleton); g_object_unref (skeleton); return TRUE; } /*****************************************************************************/ static void iface_modem_init (gpointer g_iface) { static gboolean initialized = FALSE; if (initialized) return; /* Properties */ g_object_interface_install_property (g_iface, g_param_spec_object (MM_IFACE_MODEM_DBUS_SKELETON, "Modem DBus skeleton", "DBus skeleton for the Modem interface", MM_GDBUS_TYPE_MODEM_SKELETON, G_PARAM_READWRITE)); g_object_interface_install_property (g_iface, g_param_spec_object (MM_IFACE_MODEM_SIM, "SIM", "SIM object", MM_TYPE_BASE_SIM, G_PARAM_READWRITE)); g_object_interface_install_property (g_iface, g_param_spec_boxed (MM_IFACE_MODEM_SIM_SLOTS, "SIM slots", "SIM objects in SIM slots", MM_TYPE_OBJECT_ARRAY, G_PARAM_READWRITE)); g_object_interface_install_property (g_iface, g_param_spec_enum (MM_IFACE_MODEM_STATE, "State", "State of the modem", MM_TYPE_MODEM_STATE, MM_MODEM_STATE_UNKNOWN, G_PARAM_READWRITE)); g_object_interface_install_property (g_iface, g_param_spec_object (MM_IFACE_MODEM_BEARER_LIST, "Bearer list", "List of bearers handled by the modem", MM_TYPE_BEARER_LIST, G_PARAM_READWRITE)); g_object_interface_install_property (g_iface, g_param_spec_boolean (MM_IFACE_MODEM_SIM_HOT_SWAP_SUPPORTED, "Sim Hot Swap Supported", "Whether the modem supports sim hot swap or not.", FALSE, G_PARAM_READWRITE)); g_object_interface_install_property (g_iface, g_param_spec_boolean (MM_IFACE_MODEM_PERIODIC_SIGNAL_CHECK_DISABLED, "Periodic signal quality check disabled", "Whether periodic signal quality check is disabled.", FALSE, G_PARAM_READWRITE)); g_object_interface_install_property (g_iface, g_param_spec_boolean (MM_IFACE_MODEM_PERIODIC_ACCESS_TECH_CHECK_DISABLED, "Periodic access technology check disabled", "Whether periodic access technology check is disabled.", FALSE, G_PARAM_READWRITE)); g_object_interface_install_property (g_iface, g_param_spec_string (MM_IFACE_MODEM_CARRIER_CONFIG_MAPPING, "Carrier config mapping table", "Path to the file including the carrier mapping for the module", NULL, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); initialized = TRUE; } GType mm_iface_modem_get_type (void) { static GType iface_modem_type = 0; if (!G_UNLIKELY (iface_modem_type)) { static const GTypeInfo info = { sizeof (MMIfaceModem), /* class_size */ iface_modem_init, /* base_init */ NULL, /* base_finalize */ }; iface_modem_type = g_type_register_static (G_TYPE_INTERFACE, "MMIfaceModem", &info, 0); g_type_interface_add_prerequisite (iface_modem_type, MM_TYPE_BASE_MODEM); } return iface_modem_type; } ModemManager-1.23.4-dev/src/mm-iface-modem.h000066400000000000000000001000061456466623000204370ustar00rootroot00000000000000/* -*- 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) 2011 Google, Inc. * Copyright (c) 2022 Qualcomm Innovation Center, Inc. */ #ifndef MM_IFACE_MODEM_H #define MM_IFACE_MODEM_H #include #include #define _LIBMM_INSIDE_MM #include #include "mm-charsets.h" #include "mm-port-serial-at.h" #include "mm-base-bearer.h" #include "mm-base-sim.h" #include "mm-bearer-list.h" #define MM_TYPE_IFACE_MODEM (mm_iface_modem_get_type ()) #define MM_IFACE_MODEM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_IFACE_MODEM, MMIfaceModem)) #define MM_IS_IFACE_MODEM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_IFACE_MODEM)) #define MM_IFACE_MODEM_GET_INTERFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE ((obj), MM_TYPE_IFACE_MODEM, MMIfaceModem)) #define MM_IFACE_MODEM_DBUS_SKELETON "iface-modem-dbus-skeleton" #define MM_IFACE_MODEM_STATE "iface-modem-state" #define MM_IFACE_MODEM_SIM "iface-modem-sim" #define MM_IFACE_MODEM_SIM_SLOTS "iface-modem-sim-slots" #define MM_IFACE_MODEM_BEARER_LIST "iface-modem-bearer-list" #define MM_IFACE_MODEM_SIM_HOT_SWAP_SUPPORTED "iface-modem-sim-hot-swap-supported" #define MM_IFACE_MODEM_CARRIER_CONFIG_MAPPING "iface-modem-carrier-config-mapping" #define MM_IFACE_MODEM_PERIODIC_SIGNAL_CHECK_DISABLED "iface-modem-periodic-signal-check-disabled" #define MM_IFACE_MODEM_PERIODIC_ACCESS_TECH_CHECK_DISABLED "iface-modem-periodic-access-tech-check-disabled" typedef struct _MMIfaceModem MMIfaceModem; struct _MMIfaceModem { GTypeInterface g_iface; /* Loading of the SupportedCapabilities property */ void (*load_supported_capabilities) (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data); GArray * (*load_supported_capabilities_finish) (MMIfaceModem *self, GAsyncResult *res, GError **error); /* Loading of the CurrentCapabilities property */ void (*load_current_capabilities) (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data); MMModemCapability (*load_current_capabilities_finish) (MMIfaceModem *self, GAsyncResult *res, GError **error); /* Loading of the Manufacturer property */ void (*load_manufacturer) (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data); gchar * (*load_manufacturer_finish) (MMIfaceModem *self, GAsyncResult *res, GError **error); /* Loading of the Model property */ void (*load_model) (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data); gchar * (*load_model_finish) (MMIfaceModem *self, GAsyncResult *res, GError **error); /* Loading of the Revision property */ void (*load_revision) (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data); gchar * (*load_revision_finish) (MMIfaceModem *self, GAsyncResult *res, GError **error); /* Loading of the HardwareRevision property */ void (*load_hardware_revision) (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data); gchar * (*load_hardware_revision_finish) (MMIfaceModem *self, GAsyncResult *res, GError **error); /* Loading of the EquipmentIdentifier property */ void (*load_equipment_identifier) (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data); gchar * (*load_equipment_identifier_finish) (MMIfaceModem *self, GAsyncResult *res, GError **error); /* Loading of the DeviceIdentifier property */ void (*load_device_identifier) (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data); gchar * (*load_device_identifier_finish) (MMIfaceModem *self, GAsyncResult *res, GError **error); /* Loading of the OwnNumbers property */ void (*load_own_numbers) (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data); GStrv (*load_own_numbers_finish) (MMIfaceModem *self, GAsyncResult *res, GError **error); /* Loading of the UnlockRequired property */ void (*load_unlock_required) (MMIfaceModem *self, gboolean last_attempt, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); MMModemLock (*load_unlock_required_finish) (MMIfaceModem *self, GAsyncResult *res, GError **error); /* Loading of the UnlockRetries property */ void (*load_unlock_retries) (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data); MMUnlockRetries * (*load_unlock_retries_finish) (MMIfaceModem *self, GAsyncResult *res, GError **error); /* Loading of the SupportedModes property */ void (*load_supported_modes) (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data); GArray * (*load_supported_modes_finish) (MMIfaceModem *self, GAsyncResult *res, GError **error); /* Loading of the Modes property */ void (*load_current_modes) (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data); gboolean (*load_current_modes_finish) (MMIfaceModem *self, GAsyncResult *res, MMModemMode *allowed, MMModemMode *preferred, GError **error); /* Loading of the SupportedBands property */ void (*load_supported_bands) (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data); GArray * (*load_supported_bands_finish) (MMIfaceModem *self, GAsyncResult *res, GError **error); /* Loading of the Bands property */ void (*load_current_bands) (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data); GArray * (*load_current_bands_finish) (MMIfaceModem *self, GAsyncResult *res, GError **error); /* Loading of the SupportedIpFamilies property */ void (* load_supported_ip_families) (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data); MMBearerIpFamily (* load_supported_ip_families_finish) (MMIfaceModem *self, GAsyncResult *res, GError **error); /* Loading of the PowerState property */ void (* load_power_state) (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data); MMModemPowerState (*load_power_state_finish) (MMIfaceModem *self, GAsyncResult *res, GError **error); /* Loading of the SignalQuality property */ void (*load_signal_quality) (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data); guint (*load_signal_quality_finish) (MMIfaceModem *self, GAsyncResult *res, GError **error); /* Loading of the AccessTechnologies property */ void (*load_access_technologies) (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data); gboolean (*load_access_technologies_finish) (MMIfaceModem *self, GAsyncResult *res, MMModemAccessTechnology *access_technologies, guint *mask, GError **error); /* Asynchronous reset operation */ void (*reset) (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data); gboolean (*reset_finish) (MMIfaceModem *self, GAsyncResult *res, GError **error); /* Asynchronous factory-reset operation */ void (*factory_reset) (MMIfaceModem *self, const gchar *code, GAsyncReadyCallback callback, gpointer user_data); gboolean (*factory_reset_finish) (MMIfaceModem *self, GAsyncResult *res, GError **error); /* Asynchronous command operation */ void (*command) (MMIfaceModem *self, const gchar *cmd, guint timeout, GAsyncReadyCallback callback, gpointer user_data); const gchar * (*command_finish) (MMIfaceModem *self, GAsyncResult *res, GError **error); /* Asynchronous capabilities setting operation */ void (*set_current_capabilities) (MMIfaceModem *self, MMModemCapability, GAsyncReadyCallback callback, gpointer user_data); gboolean (*set_current_capabilities_finish) (MMIfaceModem *self, GAsyncResult *res, GError **error); /* Asynchronous current band setting operation */ void (*set_current_bands) (MMIfaceModem *self, GArray *bands_array, GAsyncReadyCallback callback, gpointer user_data); gboolean (*set_current_bands_finish) (MMIfaceModem *self, GAsyncResult *res, GError **error); /* Asynchronous current mode setting operation */ void (*set_current_modes) (MMIfaceModem *self, MMModemMode modes, MMModemMode preferred, GAsyncReadyCallback callback, gpointer user_data); gboolean (*set_current_modes_finish) (MMIfaceModem *self, GAsyncResult *res, GError **error); /* Asynchronous method to wait for the SIM to be ready after having * unlocked it. */ void (*modem_after_sim_unlock) (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data); gboolean (*modem_after_sim_unlock_finish) (MMIfaceModem *self, GAsyncResult *res, GError **error); /* Asynchronous modem power-up operation */ void (*modem_power_up) (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data); gboolean (*modem_power_up_finish) (MMIfaceModem *self, GAsyncResult *res, GError **error); /* Asynchronous additional setup needed after power-up, * Plugins can implement this to provide custom setups. */ void (*modem_after_power_up) (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data); gboolean (*modem_after_power_up_finish) (MMIfaceModem *self, GAsyncResult *res, GError **error); /* Asynchronous check to see if the SIM was swapped. * Useful for when the modem changes power states since we might * not get the relevant notifications from the modem. */ void (*check_for_sim_swap) (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data); gboolean (*check_for_sim_swap_finish) (MMIfaceModem *self, GAsyncResult *res, GError **error); void (*check_basic_sim_details) (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data); gboolean (*check_basic_sim_details_finish) (MMIfaceModem *self, GAsyncResult *res, gboolean *sim_inserted, gchar **iccid, gchar **imsi, GError **error); /* Asynchronous flow control setup */ void (*setup_flow_control) (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data); gboolean (*setup_flow_control_finish) (MMIfaceModem *self, GAsyncResult *res, GError **error); /* Asynchronous loading of supported charsets */ void (*load_supported_charsets) (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data); MMModemCharset (*load_supported_charsets_finish) (MMIfaceModem *self, GAsyncResult *res, GError **error); /* Asynchronous charset setting setup */ void (*setup_charset) (MMIfaceModem *self, MMModemCharset charset, GAsyncReadyCallback callback, gpointer user_data); gboolean (*setup_charset_finish) (MMIfaceModem *self, GAsyncResult *res, GError **error); /* Asynchronous modem power-down operation */ void (*modem_power_down) (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data); gboolean (*modem_power_down_finish) (MMIfaceModem *self, GAsyncResult *res, GError **error); /* Asynchronous modem power-off operation */ void (*modem_power_off) (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data); gboolean (*modem_power_off_finish) (MMIfaceModem *self, GAsyncResult *res, GError **error); /* Create SIM */ void (*create_sim) (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data); MMBaseSim * (*create_sim_finish) (MMIfaceModem *self, GAsyncResult *res, GError **error); /* Create SIMs in all SIM slots */ void (* load_sim_slots) (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data); gboolean (* load_sim_slots_finish) (MMIfaceModem *self, GAsyncResult *res, GPtrArray **sim_slots, guint *primary_sim_slot, GError **error); /* Set primary SIM slot */ void (* set_primary_sim_slot) (MMIfaceModem *self, guint sim_slot, GAsyncReadyCallback callback, gpointer user_data); gboolean (* set_primary_sim_slot_finish) (MMIfaceModem *self, GAsyncResult *res, GError **error); /* Create bearer */ void (*create_bearer) (MMIfaceModem *self, MMBearerProperties *properties, GAsyncReadyCallback callback, gpointer user_data); MMBaseBearer * (*create_bearer_finish) (MMIfaceModem *self, GAsyncResult *res, GError **error); /* Create new bearer list object */ MMBearerList * (* create_bearer_list) (MMIfaceModem *self); /* Setup/cleanup SIM hot swap */ void (* setup_sim_hot_swap) (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data); gboolean (* setup_sim_hot_swap_finish) (MMIfaceModem *self, GAsyncResult *res, GError **error); void (* cleanup_sim_hot_swap) (MMIfaceModem *self); /* Load carrier config */ void (* load_carrier_config) (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data); gboolean (* load_carrier_config_finish) (MMIfaceModem *self, GAsyncResult *res, gchar **carrier_config_name, gchar **carrier_config_revision, GError **error); /* Setup carrier config based on IMSI */ void (* setup_carrier_config) (MMIfaceModem *self, const gchar *imsi, const gchar *carrier_config_mapping, GAsyncReadyCallback callback, gpointer user_data); gboolean (* setup_carrier_config_finish) (MMIfaceModem *self, GAsyncResult *res, GError **error); /* Asynchronous cell info retrieval operation */ void (* get_cell_info) (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data); GList * (* get_cell_info_finish) (MMIfaceModem *self, GAsyncResult *res, GError **error); }; GType mm_iface_modem_get_type (void); G_DEFINE_AUTOPTR_CLEANUP_FUNC (MMIfaceModem, g_object_unref) /* Helpers to query access technologies */ MMModemAccessTechnology mm_iface_modem_get_access_technologies (MMIfaceModem *self); /* Helpers to query capabilities */ MMModemCapability mm_iface_modem_get_current_capabilities (MMIfaceModem *self); gboolean mm_iface_modem_is_3gpp (MMIfaceModem *self); gboolean mm_iface_modem_is_3gpp_only (MMIfaceModem *self); gboolean mm_iface_modem_is_3gpp_lte (MMIfaceModem *self); gboolean mm_iface_modem_is_3gpp_5gnr (MMIfaceModem *self); gboolean mm_iface_modem_is_cdma (MMIfaceModem *self); gboolean mm_iface_modem_is_cdma_only (MMIfaceModem *self); /* Helpers to query supported modes */ gboolean mm_iface_modem_is_2g (MMIfaceModem *self); gboolean mm_iface_modem_is_2g_only (MMIfaceModem *self); gboolean mm_iface_modem_is_3g (MMIfaceModem *self); gboolean mm_iface_modem_is_3g_only (MMIfaceModem *self); gboolean mm_iface_modem_is_4g (MMIfaceModem *self); gboolean mm_iface_modem_is_4g_only (MMIfaceModem *self); gboolean mm_iface_modem_is_5g (MMIfaceModem *self); gboolean mm_iface_modem_is_5g_only (MMIfaceModem *self); /* Helpers to query properties */ const gchar *mm_iface_modem_get_model (MMIfaceModem *self); const gchar *mm_iface_modem_get_revision (MMIfaceModem *self); gboolean mm_iface_modem_get_carrier_config (MMIfaceModem *self, const gchar **name, const gchar **revision); /* Initialize Modem interface (async) */ void mm_iface_modem_initialize (MMIfaceModem *self, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); gboolean mm_iface_modem_initialize_finish (MMIfaceModem *self, GAsyncResult *res, GError **error); /* Enable Modem interface (async) */ void mm_iface_modem_enable (MMIfaceModem *self, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); gboolean mm_iface_modem_enable_finish (MMIfaceModem *self, GAsyncResult *res, GError **error); /* Disable Modem interface (async) */ void mm_iface_modem_disable (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data); gboolean mm_iface_modem_disable_finish (MMIfaceModem *self, GAsyncResult *res, GError **error); /* Shutdown Modem interface */ void mm_iface_modem_shutdown (MMIfaceModem *self); /* Helper to return an error when the modem is in failed state and so it * cannot process a given method invocation */ gboolean mm_iface_modem_abort_invocation_if_state_not_reached (MMIfaceModem *self, GDBusMethodInvocation *invocation, MMModemState minimum_required); #if defined WITH_SUSPEND_RESUME /* Sync Modem interface (async) */ void mm_iface_modem_sync (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data); gboolean mm_iface_modem_sync_finish (MMIfaceModem *self, GAsyncResult *res, GError **error); #endif /* Allow setting power state */ void mm_iface_modem_set_power_state (MMIfaceModem *self, MMModemPowerState power_state, GAsyncReadyCallback callback, gpointer user_data); gboolean mm_iface_modem_set_power_state_finish (MMIfaceModem *self, GAsyncResult *res, GError **error); /* Request lock info update. * It will not only return the lock status, but also set the property values * in the DBus interface. If 'known_lock' is given, that lock status will be * assumed. */ void mm_iface_modem_update_lock_info (MMIfaceModem *self, MMModemLock known_lock, GAsyncReadyCallback callback, gpointer user_data); MMModemLock mm_iface_modem_update_lock_info_finish (MMIfaceModem *self, GAsyncResult *res, GError **error); MMModemLock mm_iface_modem_get_unlock_required (MMIfaceModem *self); MMUnlockRetries *mm_iface_modem_get_unlock_retries (MMIfaceModem *self); /* Request signal quality check update. * It will not only return the signal quality status, but also set the property * values in the DBus interface. */ void mm_iface_modem_signal_quality_check (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data); guint mm_iface_modem_signal_quality_check_finish (MMIfaceModem *self, GAsyncResult *res, gboolean *recent, GError **error); /* Allow reporting new modem state */ void mm_iface_modem_update_subsystem_state (MMIfaceModem *self, const gchar *subsystem, MMModemState new_state, MMModemStateChangeReason reason); void mm_iface_modem_update_state (MMIfaceModem *self, MMModemState new_state, MMModemStateChangeReason reason); void mm_iface_modem_update_failed_state (MMIfaceModem *self, MMModemStateFailedReason failed_reason); /* Allow update own numbers */ void mm_iface_modem_update_own_numbers (MMIfaceModem *self, const GStrv own_numbers); /* Allow reporting new access tech */ void mm_iface_modem_update_access_technologies (MMIfaceModem *self, MMModemAccessTechnology access_tech, guint32 mask); /* Allow updating signal quality */ void mm_iface_modem_update_signal_quality (MMIfaceModem *self, guint signal_quality); /* Allow requesting to refresh signal via polling */ void mm_iface_modem_refresh_signal (MMIfaceModem *self); /* Allow setting allowed modes */ void mm_iface_modem_set_current_modes (MMIfaceModem *self, MMModemMode allowed, MMModemMode preferred, GAsyncReadyCallback callback, gpointer user_data); gboolean mm_iface_modem_set_current_modes_finish (MMIfaceModem *self, GAsyncResult *res, GError **error); /* Allow setting bands */ void mm_iface_modem_set_current_bands (MMIfaceModem *self, GArray *bands_array, GAsyncReadyCallback callback, gpointer user_data); gboolean mm_iface_modem_set_current_bands_finish (MMIfaceModem *self, GAsyncResult *res, GError **error); /* Allow creating bearers */ void mm_iface_modem_create_bearer (MMIfaceModem *self, MMBearerProperties *properties, GAsyncReadyCallback callback, gpointer user_data); MMBaseBearer *mm_iface_modem_create_bearer_finish (MMIfaceModem *self, GAsyncResult *res, GError **error); /* Helper method to wait for a final state */ void mm_iface_modem_wait_for_final_state (MMIfaceModem *self, MMModemState final_state, GAsyncReadyCallback callback, gpointer user_data); MMModemState mm_iface_modem_wait_for_final_state_finish (MMIfaceModem *self, GAsyncResult *res, GError **error); void mm_iface_modem_bind_simple_status (MMIfaceModem *self, MMSimpleStatus *status); /* Check if the SIM or eSIM profile has changed */ void mm_iface_modem_check_for_sim_swap (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data); gboolean mm_iface_modem_check_for_sim_swap_finish (MMIfaceModem *self, GAsyncResult *res, GError **error); void mm_iface_modem_modify_sim (MMIfaceModem *self, guint slot_index, MMBaseSim *new_sim); void mm_iface_modem_process_sim_event (MMIfaceModem *self); #endif /* MM_IFACE_MODEM_H */ ModemManager-1.23.4-dev/src/mm-log-helpers.c000066400000000000000000000050441456466623000205130ustar00rootroot00000000000000/* -*- 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) 2022 Google, Inc. */ #include "mm-log-helpers.h" static void common_log_print_array (gpointer log_object, MMLogLevel level, const gchar *prefix, GPtrArray *print_array) { guint i; mm_common_str_array_human_keys (print_array); for (i = 0; i < print_array->len; i++) { mm_obj_log (log_object, level, "%s%s", prefix, (const gchar *)g_ptr_array_index (print_array, i)); } } void mm_log_simple_connect_properties (gpointer log_object, MMLogLevel level, const gchar *prefix, MMSimpleConnectProperties *value) { g_autoptr(GPtrArray) print_array = NULL; if (!mm_log_check_level_enabled (level)) return; print_array = mm_simple_connect_properties_print (value, mm_log_get_show_personal_info ()); common_log_print_array (log_object, level, prefix, print_array); } void mm_log_bearer_properties (gpointer log_object, MMLogLevel level, const gchar *prefix, MMBearerProperties *value) { g_autoptr(GPtrArray) print_array = NULL; if (!mm_log_check_level_enabled (level)) return; print_array = mm_bearer_properties_print (value, mm_log_get_show_personal_info ()); common_log_print_array (log_object, level, prefix, print_array); } void mm_log_3gpp_profile (gpointer log_object, MMLogLevel level, const gchar *prefix, MM3gppProfile *value) { g_autoptr(GPtrArray) print_array = NULL; if (!mm_log_check_level_enabled (level)) return; print_array = mm_3gpp_profile_print (value, mm_log_get_show_personal_info ()); common_log_print_array (log_object, level, prefix, print_array); } ModemManager-1.23.4-dev/src/mm-log-helpers.h000066400000000000000000000032451456466623000205210ustar00rootroot00000000000000/* -*- 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) 2022 Google, Inc. */ #ifndef MM_LOG_HELPERS_H #define MM_LOG_HELPERS_H #include #define _LIBMM_INSIDE_MM #include #include "mm-log.h" void mm_log_simple_connect_properties (gpointer log_object, MMLogLevel level, const gchar *prefix, MMSimpleConnectProperties *value); void mm_log_bearer_properties (gpointer log_object, MMLogLevel level, const gchar *prefix, MMBearerProperties *value); void mm_log_3gpp_profile (gpointer log_object, MMLogLevel level, const gchar *prefix, MM3gppProfile *value); #endif /* MM_LOG_HELPERS_H */ ModemManager-1.23.4-dev/src/mm-log-object.c000066400000000000000000000043421456466623000203170ustar00rootroot00000000000000/* -*- 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) 2020 Aleksander Morgado */ #include "mm-log-object.h" G_DEFINE_INTERFACE (MMLogObject, mm_log_object, G_TYPE_OBJECT) /*****************************************************************************/ /* Private data context */ #define PRIVATE_TAG "log-object" static GQuark private_quark; typedef struct { gchar *owner_id; gchar *id; } Private; static void private_free (Private *priv) { g_free (priv->owner_id); g_free (priv->id); g_slice_free (Private, priv); } static Private * get_private (MMLogObject *self) { Private *priv; if (G_UNLIKELY (!private_quark)) private_quark = g_quark_from_static_string (PRIVATE_TAG); priv = g_object_get_qdata (G_OBJECT (self), private_quark); if (!priv) { priv = g_slice_new0 (Private); g_object_set_qdata_full (G_OBJECT (self), private_quark, priv, (GDestroyNotify)private_free); } return priv; } const gchar * mm_log_object_get_id (MMLogObject *self) { Private *priv; priv = get_private (self); if (!priv->id) { gchar *self_id; self_id = MM_LOG_OBJECT_GET_IFACE (self)->build_id (self); if (priv->owner_id) { priv->id = g_strdup_printf ("%s/%s", priv->owner_id, self_id); g_free (self_id); } else priv->id = self_id; } return priv->id; } void mm_log_object_set_owner_id (MMLogObject *self, const gchar *owner_id) { Private *priv; priv = get_private (self); g_free (priv->owner_id); priv->owner_id = g_strdup (owner_id); } static void mm_log_object_default_init (MMLogObjectInterface *iface) { } ModemManager-1.23.4-dev/src/mm-log-object.h000066400000000000000000000023061456466623000203220ustar00rootroot00000000000000/* -*- 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) 2020 Aleksander Morgado */ #ifndef MM_LOG_OBJECT_H #define MM_LOG_OBJECT_H #include #include #include "mm-log.h" #define MM_TYPE_LOG_OBJECT mm_log_object_get_type () G_DECLARE_INTERFACE (MMLogObject, mm_log_object, MM, LOG_OBJECT, GObject) struct _MMLogObjectInterface { GTypeInterface g_iface; gchar * (* build_id) (MMLogObject *self); }; const gchar *mm_log_object_get_id (MMLogObject *self); void mm_log_object_set_owner_id (MMLogObject *self, const gchar *owner_id); #endif /* MM_LOG_OBJECT_H */ ModemManager-1.23.4-dev/src/mm-log-test.h000066400000000000000000000023411456466623000200320ustar00rootroot00000000000000/* -*- 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) 2020 Aleksander Morgado */ #ifndef MM_LOG_TEST_H #define MM_LOG_TEST_H #include #include "mm-log.h" /* This is a common logging method to be used by all test applications */ void _mm_log (gpointer obj, const gchar *module, const gchar *loc, const gchar *func, MMLogLevel level, const gchar *fmt, ...) { va_list args; gchar *msg; if (!g_test_verbose ()) return; va_start (args, fmt); msg = g_strdup_vprintf (fmt, args); va_end (args); g_print ("%s\n", msg); g_free (msg); } #endif /* MM_LOG_TEST_H */ ModemManager-1.23.4-dev/src/mm-log.c000066400000000000000000000262451456466623000170610ustar00rootroot00000000000000/* -*- 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) 2011-2020 Red Hat, Inc. * Copyright (C) 2020 Aleksander Morgado * Copyright (c) 2022 Qualcomm Innovation Center, Inc. */ #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #define _LIBMM_INSIDE_MM #include #if defined WITH_QMI #include #endif #if defined WITH_MBIM #include #endif #if defined WITH_SYSTEMD_JOURNAL #define SD_JOURNAL_SUPPRESS_LOCATION #include #endif #include "mm-log.h" #include "mm-log-object.h" enum { TS_FLAG_NONE = 0, TS_FLAG_WALL, TS_FLAG_REL }; static gboolean ts_flags = TS_FLAG_NONE; static guint32 log_level = MM_LOG_LEVEL_MSG | MM_LOG_LEVEL_WARN | MM_LOG_LEVEL_ERR; static GTimeVal rel_start = { 0, 0 }; static int logfd = -1; static gboolean append_log_level_text = TRUE; static gboolean personal_info = FALSE; static void (*log_backend) (const char *loc, const char *func, int syslog_level, const char *message, size_t length); typedef struct { guint32 num; const char *name; } LogDesc; static const LogDesc level_descs[] = { { MM_LOG_LEVEL_ERR, "ERR" }, { MM_LOG_LEVEL_WARN | MM_LOG_LEVEL_ERR, "WARN" }, { MM_LOG_LEVEL_MSG | MM_LOG_LEVEL_WARN | MM_LOG_LEVEL_ERR, "MSG" }, { MM_LOG_LEVEL_INFO | MM_LOG_LEVEL_MSG | MM_LOG_LEVEL_WARN | MM_LOG_LEVEL_ERR, "INFO" }, { MM_LOG_LEVEL_DEBUG | MM_LOG_LEVEL_INFO | MM_LOG_LEVEL_MSG | MM_LOG_LEVEL_WARN | MM_LOG_LEVEL_ERR, "DEBUG" }, { 0, NULL } }; static GString *msgbuf = NULL; static gsize msgbuf_once = 0; static int mm_to_syslog_priority (MMLogLevel level) { switch (level) { case MM_LOG_LEVEL_ERR: return LOG_ERR; case MM_LOG_LEVEL_WARN: return LOG_WARNING; case MM_LOG_LEVEL_MSG: return LOG_NOTICE; case MM_LOG_LEVEL_INFO: return LOG_INFO; case MM_LOG_LEVEL_DEBUG: return LOG_DEBUG; default: break; } g_assert_not_reached (); return 0; } static MMLogLevel glib_level_to_mm_level (GLogLevelFlags level) { /* if the log was flagged as fatal (e.g. G_DEBUG=fatal-warnings), ignore * the fatal flag for logging purposes */ if (level & G_LOG_FLAG_FATAL) level &= ~G_LOG_FLAG_FATAL; switch (level) { case G_LOG_LEVEL_ERROR: case G_LOG_LEVEL_CRITICAL: return MM_LOG_LEVEL_ERR; case G_LOG_LEVEL_WARNING: return MM_LOG_LEVEL_WARN; case G_LOG_LEVEL_MESSAGE: return MM_LOG_LEVEL_MSG; case G_LOG_LEVEL_INFO: return MM_LOG_LEVEL_INFO; case G_LOG_LEVEL_DEBUG: return MM_LOG_LEVEL_DEBUG; case G_LOG_LEVEL_MASK: case G_LOG_FLAG_FATAL: case G_LOG_FLAG_RECURSION: default: g_assert_not_reached (); } } static const char * log_level_description (MMLogLevel level) { switch (level) { case MM_LOG_LEVEL_ERR: return ""; case MM_LOG_LEVEL_WARN: return ""; case MM_LOG_LEVEL_MSG: return ""; case MM_LOG_LEVEL_INFO: return ""; case MM_LOG_LEVEL_DEBUG: return ""; default: break; } g_assert_not_reached (); return NULL; } static void log_backend_file (const char *loc, const char *func, int syslog_level, const char *message, size_t length) { ssize_t ign; ign = write (logfd, message, length); if (ign) {} /* whatever; really shut up about unused result */ fsync (logfd); /* Make sure output is dumped to disk immediately */ } static void log_backend_syslog (const char *loc, const char *func, int syslog_level, const char *message, size_t length) { syslog (syslog_level, "%s", message); } #if defined WITH_SYSTEMD_JOURNAL static void log_backend_systemd_journal (const char *loc, const char *func, int syslog_level, const char *message, size_t length) { const char *line; size_t file_length; if (loc == NULL) { sd_journal_send ("MESSAGE=%s", message, "PRIORITY=%d", syslog_level, NULL); return; } line = strstr (loc, ":"); if (line) { file_length = line - loc; line++; } else { /* This is not supposed to happen but we must be prepared for this */ line = loc; file_length = 0; } sd_journal_send ("MESSAGE=%s", message, "PRIORITY=%d", syslog_level, "CODE_FUNC=%s", func, "CODE_FILE=%.*s", file_length, loc, "CODE_LINE=%s", line, NULL); } #endif gboolean mm_log_get_show_personal_info (void) { return personal_info; } gboolean mm_log_check_level_enabled (MMLogLevel level) { return (log_level & level); } void _mm_log (gpointer obj, const gchar *module, const gchar *loc, const gchar *func, MMLogLevel level, const gchar *fmt, ...) { va_list args; GTimeVal tv; if (!mm_log_check_level_enabled (level)) return; if (g_once_init_enter (&msgbuf_once)) { msgbuf = g_string_sized_new (512); g_once_init_leave (&msgbuf_once, 1); } else g_string_truncate (msgbuf, 0); if (append_log_level_text) g_string_append_printf (msgbuf, "%s ", log_level_description (level)); if (ts_flags == TS_FLAG_WALL) { g_get_current_time (&tv); g_string_append_printf (msgbuf, "[%09ld.%06ld] ", tv.tv_sec, tv.tv_usec); } else if (ts_flags == TS_FLAG_REL) { glong secs; glong usecs; g_get_current_time (&tv); secs = tv.tv_sec - rel_start.tv_sec; usecs = tv.tv_usec - rel_start.tv_usec; if (usecs < 0) { secs--; usecs += 1000000; } g_string_append_printf (msgbuf, "[%06ld.%06ld] ", secs, usecs); } #if defined MM_LOG_FUNC_LOC if (loc && func) g_string_append_printf (msgbuf, "[%s] %s(): ", loc, func); #endif if (obj) g_string_append_printf (msgbuf, "[%s] ", mm_log_object_get_id (MM_LOG_OBJECT (obj))); if (module) g_string_append_printf (msgbuf, "(%s) ", module); va_start (args, fmt); g_string_append_vprintf (msgbuf, fmt, args); va_end (args); g_string_append_c (msgbuf, '\n'); log_backend (loc, func, mm_to_syslog_priority (level), msgbuf->str, msgbuf->len); } static void log_handler (const gchar *log_domain, GLogLevelFlags glib_level, const gchar *message, gpointer ignored) { _mm_log (NULL, /* obj */ NULL, /* module */ NULL, /* loc */ NULL, /* func */ glib_level_to_mm_level (glib_level), "%s", message); } gboolean mm_log_set_level (const gchar *level, GError **error) { guint i; for (i = 0; i < G_N_ELEMENTS (level_descs); i++) { if (!g_ascii_strcasecmp (level_descs[i].name, level)) { log_level = level_descs[i].num; break; } } if (i == G_N_ELEMENTS (level_descs)) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS, "Unknown log level '%s'", level); return FALSE; } #if defined WITH_QMI qmi_utils_set_traces_enabled (log_level & MM_LOG_LEVEL_DEBUG ? TRUE : FALSE); #endif #if defined WITH_MBIM mbim_utils_set_traces_enabled (log_level & MM_LOG_LEVEL_DEBUG ? TRUE : FALSE); #endif return TRUE; } gboolean mm_log_setup (const gchar *level, const gchar *log_file, gboolean log_journal, gboolean show_timestamps, gboolean rel_timestamps, gboolean show_personal_info, GError **error) { /* levels */ if (level && strlen (level) && !mm_log_set_level (level, error)) return FALSE; personal_info = show_personal_info; if (show_timestamps) ts_flags = TS_FLAG_WALL; else if (rel_timestamps) ts_flags = TS_FLAG_REL; /* Grab start time for relative timestamps */ g_get_current_time (&rel_start); #if defined WITH_SYSTEMD_JOURNAL if (log_journal) { log_backend = log_backend_systemd_journal; append_log_level_text = FALSE; } else #endif if (!log_file) { openlog (G_LOG_DOMAIN, LOG_CONS | LOG_PID | LOG_PERROR, LOG_DAEMON); log_backend = log_backend_syslog; } else { logfd = open (log_file, O_CREAT | O_APPEND | O_WRONLY, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP); if (logfd < 0) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't open log file: (%d) %s", errno, strerror (errno)); return FALSE; } log_backend = log_backend_file; } g_log_set_handler (G_LOG_DOMAIN, G_LOG_LEVEL_MASK | G_LOG_FLAG_FATAL | G_LOG_FLAG_RECURSION, log_handler, NULL); #if defined WITH_QMI qmi_utils_set_show_personal_info (show_personal_info); g_log_set_handler ("Qmi", G_LOG_LEVEL_MASK | G_LOG_FLAG_FATAL | G_LOG_FLAG_RECURSION, log_handler, NULL); #endif #if defined WITH_QRTR g_log_set_handler ("Qrtr", G_LOG_LEVEL_MASK | G_LOG_FLAG_FATAL | G_LOG_FLAG_RECURSION, log_handler, NULL); #endif #if defined WITH_MBIM mbim_utils_set_show_personal_info (show_personal_info); g_log_set_handler ("Mbim", G_LOG_LEVEL_MASK | G_LOG_FLAG_FATAL | G_LOG_FLAG_RECURSION, log_handler, NULL); #endif return TRUE; } void mm_log_shutdown (void) { if (logfd < 0) closelog (); else close (logfd); } /******************************************************************************/ const gchar * mm_log_str_personal_info (const gchar *str) { return mm_common_str_personal_info (str, personal_info); } ModemManager-1.23.4-dev/src/mm-log.h000066400000000000000000000076331456466623000170660ustar00rootroot00000000000000/* -*- 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) 2011-2020 Red Hat, Inc. * Copyright (C) 2020-2022 Aleksander Morgado * Copyright (C) 2022 Google, Inc. */ #ifndef MM_LOG_H #define MM_LOG_H #include /* Log levels */ typedef enum { MM_LOG_LEVEL_ERR = 0x00000001, MM_LOG_LEVEL_WARN = 0x00000002, MM_LOG_LEVEL_MSG = 0x00000004, MM_LOG_LEVEL_INFO = 0x00000008, MM_LOG_LEVEL_DEBUG = 0x00000010, } MMLogLevel; #if defined MM_MODULE_NAME # define MM_LOG_MODULE_NAME MM_MODULE_NAME #else # define MM_LOG_MODULE_NAME (const gchar *)NULL #endif #define mm_obj_log(obj, level, ...) _mm_log (obj, MM_LOG_MODULE_NAME, G_STRLOC, G_STRFUNC, level, ## __VA_ARGS__ ) #define mm_obj_err(obj, ...) _mm_log (obj, MM_LOG_MODULE_NAME, G_STRLOC, G_STRFUNC, MM_LOG_LEVEL_ERR, ## __VA_ARGS__ ) #define mm_obj_warn(obj, ...) _mm_log (obj, MM_LOG_MODULE_NAME, G_STRLOC, G_STRFUNC, MM_LOG_LEVEL_WARN, ## __VA_ARGS__ ) #define mm_obj_msg(obj, ...) _mm_log (obj, MM_LOG_MODULE_NAME, G_STRLOC, G_STRFUNC, MM_LOG_LEVEL_MSG, ## __VA_ARGS__ ) #define mm_obj_info(obj, ...) _mm_log (obj, MM_LOG_MODULE_NAME, G_STRLOC, G_STRFUNC, MM_LOG_LEVEL_INFO, ## __VA_ARGS__ ) #define mm_obj_dbg(obj, ...) _mm_log (obj, MM_LOG_MODULE_NAME, G_STRLOC, G_STRFUNC, MM_LOG_LEVEL_DEBUG, ## __VA_ARGS__ ) /* only allow using non-object logging API if explicitly requested * (e.g. in the main daemon source) */ #if defined MM_LOG_NO_OBJECT # define mm_err(...) mm_obj_err (NULL, ## __VA_ARGS__ ) # define mm_warn(...) mm_obj_warn (NULL, ## __VA_ARGS__ ) # define mm_msg(...) mm_obj_msg (NULL, ## __VA_ARGS__ ) # define mm_info(...) mm_obj_info (NULL, ## __VA_ARGS__ ) # define mm_dbg(...) mm_obj_dbg (NULL, ## __VA_ARGS__ ) #endif #define mm_log_err_enabled() mm_log_check_level_enabled (MM_LOG_LEVEL_ERR) #define mm_log_warn_enabled() mm_log_check_level_enabled (MM_LOG_LEVEL_WARN) #define mm_log_msg_enabled() mm_log_check_level_enabled (MM_LOG_LEVEL_MSG) #define mm_log_info_enabled() mm_log_check_level_enabled (MM_LOG_LEVEL_INFO) #define mm_log_debug_enabled() mm_log_check_level_enabled (MM_LOG_LEVEL_DEBUG) void _mm_log (gpointer obj, const gchar *module, const gchar *loc, const gchar *func, MMLogLevel level, const gchar *fmt, ...) __attribute__((__format__ (__printf__, 6, 7))); gboolean mm_log_set_level (const gchar *level, GError **error); gboolean mm_log_setup (const gchar *level, const gchar *log_file, gboolean log_journal, gboolean show_ts, gboolean rel_ts, gboolean show_personal_info, GError **error); gboolean mm_log_check_level_enabled (MMLogLevel level); gboolean mm_log_get_show_personal_info (void); void mm_log_shutdown (void); /* Helper used when printing a string that may be personal * info. Depending on the settings, we may print it as-is, * or otherwise provide a fallback string. */ const gchar *mm_log_str_personal_info (const gchar *str); #endif /* MM_LOG_H */ ModemManager-1.23.4-dev/src/mm-modem-helpers-mbim.c000066400000000000000000002026301456466623000217550ustar00rootroot00000000000000/* -*- 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) 2013-2021 Aleksander Morgado */ #include "mm-modem-helpers-mbim.h" #include "mm-modem-helpers.h" #include "mm-enums-types.h" #include "mm-flags-types.h" #include "mm-errors-types.h" #include "mm-error-helpers.h" #include "mm-log-object.h" #include /*****************************************************************************/ MMModemCapability mm_modem_capability_from_mbim_device_caps (MbimCellularClass caps_cellular_class, MbimDataClass caps_data_class, const gchar *caps_custom_data_class) { MMModemCapability mask = 0; if (caps_cellular_class & MBIM_CELLULAR_CLASS_GSM) mask |= MM_MODEM_CAPABILITY_GSM_UMTS; #if 0 /* Disable until we add MBIM CDMA support */ if (caps_cellular_class & MBIM_CELLULAR_CLASS_CDMA) mask |= MM_MODEM_CAPABILITY_CDMA_EVDO; #endif if (caps_data_class & MBIM_DATA_CLASS_LTE) mask |= MM_MODEM_CAPABILITY_LTE; /* e.g. Gosuncn GM800 reports MBIM custom data class "5G/TDS" */ if ((caps_data_class & MBIM_DATA_CLASS_CUSTOM) && caps_custom_data_class) { if (strstr (caps_custom_data_class, "5G")) mask |= MM_MODEM_CAPABILITY_5GNR; } /* Support for devices with Microsoft extensions */ if (caps_data_class & (MBIM_DATA_CLASS_5G_NSA | MBIM_DATA_CLASS_5G_SA)) mask |= MM_MODEM_CAPABILITY_5GNR; return mask; } /*****************************************************************************/ MMModemLock mm_modem_lock_from_mbim_pin_type (MbimPinType pin_type) { switch (pin_type) { case MBIM_PIN_TYPE_PIN1: return MM_MODEM_LOCK_SIM_PIN; case MBIM_PIN_TYPE_PIN2: return MM_MODEM_LOCK_SIM_PIN2; case MBIM_PIN_TYPE_DEVICE_SIM_PIN: return MM_MODEM_LOCK_PH_SIM_PIN; case MBIM_PIN_TYPE_DEVICE_FIRST_SIM_PIN: return MM_MODEM_LOCK_PH_FSIM_PIN; case MBIM_PIN_TYPE_NETWORK_PIN: return MM_MODEM_LOCK_PH_NET_PIN; case MBIM_PIN_TYPE_NETWORK_SUBSET_PIN: return MM_MODEM_LOCK_PH_NETSUB_PIN; case MBIM_PIN_TYPE_SERVICE_PROVIDER_PIN: return MM_MODEM_LOCK_PH_SP_PIN; case MBIM_PIN_TYPE_CORPORATE_PIN: return MM_MODEM_LOCK_PH_CORP_PIN; case MBIM_PIN_TYPE_PUK1: return MM_MODEM_LOCK_SIM_PUK; case MBIM_PIN_TYPE_PUK2: return MM_MODEM_LOCK_SIM_PUK2; case MBIM_PIN_TYPE_DEVICE_FIRST_SIM_PUK: return MM_MODEM_LOCK_PH_FSIM_PUK; case MBIM_PIN_TYPE_NETWORK_PUK: return MM_MODEM_LOCK_PH_NET_PUK; case MBIM_PIN_TYPE_NETWORK_SUBSET_PUK: return MM_MODEM_LOCK_PH_NETSUB_PIN; case MBIM_PIN_TYPE_SERVICE_PROVIDER_PUK: return MM_MODEM_LOCK_PH_SP_PIN; case MBIM_PIN_TYPE_CORPORATE_PUK: return MM_MODEM_LOCK_PH_CORP_PUK; case MBIM_PIN_TYPE_SUBSIDY_PIN: case MBIM_PIN_TYPE_ADM: case MBIM_PIN_TYPE_NEV: case MBIM_PIN_TYPE_UNKNOWN: case MBIM_PIN_TYPE_CUSTOM: default: break; } return MM_MODEM_LOCK_UNKNOWN; } /*****************************************************************************/ MMModem3gppRegistrationState mm_modem_3gpp_registration_state_from_mbim_register_state (MbimRegisterState state) { switch (state) { case MBIM_REGISTER_STATE_DEREGISTERED: return MM_MODEM_3GPP_REGISTRATION_STATE_IDLE; case MBIM_REGISTER_STATE_SEARCHING: return MM_MODEM_3GPP_REGISTRATION_STATE_SEARCHING; case MBIM_REGISTER_STATE_HOME: return MM_MODEM_3GPP_REGISTRATION_STATE_HOME; case MBIM_REGISTER_STATE_ROAMING: case MBIM_REGISTER_STATE_PARTNER: return MM_MODEM_3GPP_REGISTRATION_STATE_ROAMING; case MBIM_REGISTER_STATE_DENIED: return MM_MODEM_3GPP_REGISTRATION_STATE_DENIED; case MBIM_REGISTER_STATE_UNKNOWN: default: return MM_MODEM_3GPP_REGISTRATION_STATE_UNKNOWN; } } /*****************************************************************************/ MMModem3gppPacketServiceState mm_modem_3gpp_packet_service_state_from_mbim_packet_service_state (MbimPacketServiceState state) { switch (state) { case MBIM_PACKET_SERVICE_STATE_ATTACHED: return MM_MODEM_3GPP_PACKET_SERVICE_STATE_ATTACHED; case MBIM_PACKET_SERVICE_STATE_ATTACHING: case MBIM_PACKET_SERVICE_STATE_DETACHING: case MBIM_PACKET_SERVICE_STATE_DETACHED: return MM_MODEM_3GPP_PACKET_SERVICE_STATE_DETACHED; case MBIM_PACKET_SERVICE_STATE_UNKNOWN: default: return MM_MODEM_3GPP_PACKET_SERVICE_STATE_UNKNOWN; } } /*****************************************************************************/ MMModemMode mm_modem_mode_from_mbim_data_class (MbimDataClass data_class, const gchar *caps_custom_data_class) { MMModemMode mask = MM_MODEM_MODE_NONE; /* 3GPP... */ if (data_class & (MBIM_DATA_CLASS_GPRS | MBIM_DATA_CLASS_EDGE)) mask |= MM_MODEM_MODE_2G; if (data_class & (MBIM_DATA_CLASS_UMTS | MBIM_DATA_CLASS_HSDPA | MBIM_DATA_CLASS_HSUPA)) mask |= MM_MODEM_MODE_3G; if (data_class & MBIM_DATA_CLASS_LTE) mask |= MM_MODEM_MODE_4G; if (data_class & (MBIM_DATA_CLASS_5G_NSA | MBIM_DATA_CLASS_5G_SA)) mask |= MM_MODEM_MODE_5G; /* Some modems (e.g. Telit FN990) reports MBIM custom data class "5G/TDS" */ if ((data_class & MBIM_DATA_CLASS_CUSTOM) && caps_custom_data_class) { if (strstr (caps_custom_data_class, "5G")) mask |= MM_MODEM_MODE_5G; } /* 3GPP2... */ if (data_class & MBIM_DATA_CLASS_1XRTT) mask |= MM_MODEM_MODE_2G; if (data_class & (MBIM_DATA_CLASS_1XEVDO | MBIM_DATA_CLASS_1XEVDO_REVA | MBIM_DATA_CLASS_1XEVDV | MBIM_DATA_CLASS_3XRTT | MBIM_DATA_CLASS_1XEVDO_REVB)) mask |= MM_MODEM_MODE_3G; if (data_class & MBIM_DATA_CLASS_UMB) mask |= MM_MODEM_MODE_4G; return mask; } MbimDataClass mm_mbim_data_class_from_modem_mode (MMModemMode modem_mode, gboolean is_3gpp, gboolean is_cdma) { MbimDataClass mask = 0; /* 3GPP... */ if (is_3gpp) { if (modem_mode & MM_MODEM_MODE_2G) mask |= (MBIM_DATA_CLASS_GPRS | MBIM_DATA_CLASS_EDGE); if (modem_mode & MM_MODEM_MODE_3G) mask |= (MBIM_DATA_CLASS_UMTS | MBIM_DATA_CLASS_HSDPA | MBIM_DATA_CLASS_HSUPA); if (modem_mode & MM_MODEM_MODE_4G) mask |= MBIM_DATA_CLASS_LTE; if (modem_mode & MM_MODEM_MODE_5G) mask |= (MBIM_DATA_CLASS_5G_NSA | MBIM_DATA_CLASS_5G_SA); } /* 3GPP2... */ if (is_cdma) { if (modem_mode & MM_MODEM_MODE_2G) mask |= MBIM_DATA_CLASS_1XRTT; if (modem_mode & MM_MODEM_MODE_3G) mask |= (MBIM_DATA_CLASS_1XEVDO | MBIM_DATA_CLASS_1XEVDO_REVA | MBIM_DATA_CLASS_1XEVDV | MBIM_DATA_CLASS_3XRTT | MBIM_DATA_CLASS_1XEVDO_REVB); if (modem_mode & MM_MODEM_MODE_4G) mask |= MBIM_DATA_CLASS_UMB; } return mask; } MbimDataClass mm_mbim_data_class_from_mbim_data_class_v3_and_subclass (MbimDataClassV3 data_class_v3, MbimDataSubclass data_subclass) { MbimDataClass data_class; data_class = data_class_v3 & ~(MBIM_DATA_CLASS_5G_NSA | MBIM_DATA_CLASS_5G_SA); if (data_class_v3 & MBIM_DATA_CLASS_V3_5G) { if (data_subclass & MBIM_DATA_SUBCLASS_5G_NR) data_class |= MBIM_DATA_CLASS_5G_SA; else if (data_subclass & (MBIM_DATA_SUBCLASS_5G_ENDC | MBIM_DATA_SUBCLASS_5G_NEDC | MBIM_DATA_SUBCLASS_5G_NGENDC)) data_class |= (MBIM_DATA_CLASS_5G_NSA | MBIM_DATA_CLASS_LTE); else if (data_subclass & MBIM_DATA_SUBCLASS_5G_ELTE) data_class |= MBIM_DATA_CLASS_LTE; } return data_class; } MMModemAccessTechnology mm_modem_access_technology_from_mbim_data_class (MbimDataClass data_class) { MMModemAccessTechnology mask = MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN; if (data_class & MBIM_DATA_CLASS_GPRS) mask |= MM_MODEM_ACCESS_TECHNOLOGY_GPRS; if (data_class & MBIM_DATA_CLASS_EDGE) mask |= MM_MODEM_ACCESS_TECHNOLOGY_EDGE; if (data_class & MBIM_DATA_CLASS_UMTS) mask |= MM_MODEM_ACCESS_TECHNOLOGY_UMTS; if (data_class & MBIM_DATA_CLASS_HSDPA) mask |= MM_MODEM_ACCESS_TECHNOLOGY_HSDPA; if (data_class & MBIM_DATA_CLASS_HSUPA) mask |= MM_MODEM_ACCESS_TECHNOLOGY_HSUPA; if (data_class & MBIM_DATA_CLASS_LTE) mask |= MM_MODEM_ACCESS_TECHNOLOGY_LTE; if (data_class & MBIM_DATA_CLASS_5G_NSA) mask |= (MM_MODEM_ACCESS_TECHNOLOGY_LTE | MM_MODEM_ACCESS_TECHNOLOGY_5GNR); if (data_class & MBIM_DATA_CLASS_5G_SA) mask |= MM_MODEM_ACCESS_TECHNOLOGY_5GNR; if (data_class & MBIM_DATA_CLASS_1XRTT) mask |= MM_MODEM_ACCESS_TECHNOLOGY_1XRTT; if (data_class & MBIM_DATA_CLASS_1XEVDO) mask |= MM_MODEM_ACCESS_TECHNOLOGY_EVDO0; if (data_class & MBIM_DATA_CLASS_1XEVDO_REVA) mask |= MM_MODEM_ACCESS_TECHNOLOGY_EVDOA; if (data_class & MBIM_DATA_CLASS_1XEVDO_REVB) mask |= MM_MODEM_ACCESS_TECHNOLOGY_EVDOB; /* Skip: * MBIM_DATA_CLASS_1XEVDV * MBIM_DATA_CLASS_3XRTT * MBIM_DATA_CLASS_UMB * MBIM_DATA_CLASS_CUSTOM */ return mask; } /*****************************************************************************/ MMModem3gppNetworkAvailability mm_modem_3gpp_network_availability_from_mbim_provider_state (MbimProviderState state) { /* MbimProviderState is a bitmask! * * We don't explicitly process MBIM_PROVIDER_STATE_PREFERRED, * MBIM_PROVIDER_STATE_PREFERRED_MULTICARRIER or MBIM_PROVIDER_STATE_HOME, * so we don't report at MM level the type of operator it is (home, * preferred or non-preferred), just its availability. */ if (state & MBIM_PROVIDER_STATE_REGISTERED) return MM_MODEM_3GPP_NETWORK_AVAILABILITY_CURRENT; if (state & MBIM_PROVIDER_STATE_FORBIDDEN) return MM_MODEM_3GPP_NETWORK_AVAILABILITY_FORBIDDEN; if (state & MBIM_PROVIDER_STATE_VISIBLE) return MM_MODEM_3GPP_NETWORK_AVAILABILITY_AVAILABLE; return MM_MODEM_3GPP_NETWORK_AVAILABILITY_UNKNOWN; } /*****************************************************************************/ GList * mm_3gpp_network_info_list_from_mbim_providers (const MbimProvider *const *providers, guint n_providers) { GList *info_list = NULL; guint i; g_return_val_if_fail (providers != NULL, NULL); for (i = 0; i < n_providers; i++) { MM3gppNetworkInfo *info; info = g_new0 (MM3gppNetworkInfo, 1); info->status = mm_modem_3gpp_network_availability_from_mbim_provider_state (providers[i]->provider_state); info->operator_long = g_strdup (providers[i]->provider_name); info->operator_short = g_strdup (providers[i]->provider_name); info->operator_code = g_strdup (providers[i]->provider_id); info->access_tech = mm_modem_access_technology_from_mbim_data_class (providers[i]->cellular_class); info_list = g_list_append (info_list, info); } return info_list; } /*****************************************************************************/ MbimPinType mbim_pin_type_from_mm_modem_3gpp_facility (MMModem3gppFacility facility) { switch (facility) { case MM_MODEM_3GPP_FACILITY_NET_PERS: return MBIM_PIN_TYPE_NETWORK_PIN; case MM_MODEM_3GPP_FACILITY_NET_SUB_PERS: return MBIM_PIN_TYPE_NETWORK_SUBSET_PIN; case MM_MODEM_3GPP_FACILITY_PROVIDER_PERS: return MBIM_PIN_TYPE_SERVICE_PROVIDER_PIN; case MM_MODEM_3GPP_FACILITY_CORP_PERS: return MBIM_PIN_TYPE_CORPORATE_PIN; case MM_MODEM_3GPP_FACILITY_SIM: return MBIM_PIN_TYPE_PIN1; case MM_MODEM_3GPP_FACILITY_FIXED_DIALING: return MBIM_PIN_TYPE_PIN2; case MM_MODEM_3GPP_FACILITY_PH_SIM: return MBIM_PIN_TYPE_DEVICE_SIM_PIN; case MM_MODEM_3GPP_FACILITY_PH_FSIM: return MBIM_PIN_TYPE_DEVICE_FIRST_SIM_PIN; case MM_MODEM_3GPP_FACILITY_NONE: default: return MBIM_PIN_TYPE_UNKNOWN; } } /*****************************************************************************/ MMModem3gppFacility mm_modem_3gpp_facility_from_mbim_pin_type (MbimPinType pin_type) { switch (pin_type) { case MBIM_PIN_TYPE_PIN1: case MBIM_PIN_TYPE_PUK1: return MM_MODEM_3GPP_FACILITY_SIM; case MBIM_PIN_TYPE_PIN2: case MBIM_PIN_TYPE_PUK2: return MM_MODEM_3GPP_FACILITY_FIXED_DIALING; case MBIM_PIN_TYPE_DEVICE_SIM_PIN: return MM_MODEM_3GPP_FACILITY_PH_SIM; case MBIM_PIN_TYPE_DEVICE_FIRST_SIM_PIN: case MBIM_PIN_TYPE_DEVICE_FIRST_SIM_PUK: return MM_MODEM_3GPP_FACILITY_PH_FSIM; case MBIM_PIN_TYPE_NETWORK_PIN: case MBIM_PIN_TYPE_NETWORK_PUK: return MM_MODEM_3GPP_FACILITY_NET_PERS; case MBIM_PIN_TYPE_NETWORK_SUBSET_PIN: case MBIM_PIN_TYPE_NETWORK_SUBSET_PUK: return MM_MODEM_3GPP_FACILITY_NET_SUB_PERS; case MBIM_PIN_TYPE_SERVICE_PROVIDER_PIN: case MBIM_PIN_TYPE_SERVICE_PROVIDER_PUK: return MM_MODEM_3GPP_FACILITY_PROVIDER_PERS; case MBIM_PIN_TYPE_CORPORATE_PIN: case MBIM_PIN_TYPE_CORPORATE_PUK: return MM_MODEM_3GPP_FACILITY_CORP_PERS; case MBIM_PIN_TYPE_SUBSIDY_PIN: case MBIM_PIN_TYPE_ADM: case MBIM_PIN_TYPE_NEV: case MBIM_PIN_TYPE_UNKNOWN: case MBIM_PIN_TYPE_CUSTOM: default: return MM_MODEM_3GPP_FACILITY_NONE; } } /*****************************************************************************/ MMBearerAllowedAuth mm_bearer_allowed_auth_from_mbim_auth_protocol (MbimAuthProtocol auth_protocol) { switch (auth_protocol) { case MBIM_AUTH_PROTOCOL_NONE: return MM_BEARER_ALLOWED_AUTH_NONE; case MBIM_AUTH_PROTOCOL_PAP: return MM_BEARER_ALLOWED_AUTH_PAP; case MBIM_AUTH_PROTOCOL_CHAP: return MM_BEARER_ALLOWED_AUTH_CHAP; case MBIM_AUTH_PROTOCOL_MSCHAPV2: return MM_BEARER_ALLOWED_AUTH_MSCHAPV2; default: return MM_BEARER_ALLOWED_AUTH_UNKNOWN; } } MbimAuthProtocol mm_bearer_allowed_auth_to_mbim_auth_protocol (MMBearerAllowedAuth bearer_auth, gpointer log_object, GError **error) { gchar *str; /* NOTE: the input is a BITMASK, so we try to find a "best match" */ if (bearer_auth == MM_BEARER_ALLOWED_AUTH_UNKNOWN) { mm_obj_dbg (log_object, "using default (CHAP) authentication method"); return MBIM_AUTH_PROTOCOL_CHAP; } if (bearer_auth & MM_BEARER_ALLOWED_AUTH_CHAP) return MBIM_AUTH_PROTOCOL_CHAP; if (bearer_auth & MM_BEARER_ALLOWED_AUTH_PAP) return MBIM_AUTH_PROTOCOL_PAP; if (bearer_auth & MM_BEARER_ALLOWED_AUTH_MSCHAPV2) return MBIM_AUTH_PROTOCOL_MSCHAPV2; if (bearer_auth & MM_BEARER_ALLOWED_AUTH_NONE) return MBIM_AUTH_PROTOCOL_NONE; str = mm_bearer_allowed_auth_build_string_from_mask (bearer_auth); g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "Unsupported authentication methods (%s)", str); g_free (str); return MBIM_AUTH_PROTOCOL_NONE; } /*****************************************************************************/ MMBearerApnType mm_bearer_apn_type_from_mbim_context_type (MbimContextType context_type) { switch (context_type) { case MBIM_CONTEXT_TYPE_INTERNET: return MM_BEARER_APN_TYPE_DEFAULT; case MBIM_CONTEXT_TYPE_VPN: return MM_BEARER_APN_TYPE_PRIVATE; case MBIM_CONTEXT_TYPE_VOICE: return MM_BEARER_APN_TYPE_VOICE; case MBIM_CONTEXT_TYPE_VIDEO_SHARE: return MM_BEARER_APN_TYPE_VIDEO_SHARE; case MBIM_CONTEXT_TYPE_PURCHASE: return MM_BEARER_APN_TYPE_PURCHASE; case MBIM_CONTEXT_TYPE_IMS: return MM_BEARER_APN_TYPE_IMS; case MBIM_CONTEXT_TYPE_MMS: return MM_BEARER_APN_TYPE_MMS; case MBIM_CONTEXT_TYPE_LOCAL: return MM_BEARER_APN_TYPE_LOCAL; case MBIM_CONTEXT_TYPE_ADMIN: return MM_BEARER_APN_TYPE_MANAGEMENT; case MBIM_CONTEXT_TYPE_APP: return MM_BEARER_APN_TYPE_APP; case MBIM_CONTEXT_TYPE_XCAP: return MM_BEARER_APN_TYPE_XCAP; case MBIM_CONTEXT_TYPE_TETHERING: return MM_BEARER_APN_TYPE_TETHERING; case MBIM_CONTEXT_TYPE_EMERGENCY_CALLING: return MM_BEARER_APN_TYPE_EMERGENCY; /* some types unused right now */ case MBIM_CONTEXT_TYPE_INVALID: case MBIM_CONTEXT_TYPE_NONE: default: return MM_BEARER_APN_TYPE_NONE; } } MbimContextType mm_bearer_apn_type_to_mbim_context_type (MMBearerApnType apn_type, gboolean mbim_extensions_supported, gpointer log_object, GError **error) { g_autofree gchar *str = NULL; /* NOTE: the input is a BITMASK, so we try to find a "best match" */ if (apn_type == MM_BEARER_APN_TYPE_NONE) { mm_obj_dbg (log_object, "using default (internet) APN type"); return MBIM_CONTEXT_TYPE_INTERNET; } if (apn_type & MM_BEARER_APN_TYPE_DEFAULT) return MBIM_CONTEXT_TYPE_INTERNET; if (apn_type & MM_BEARER_APN_TYPE_IMS) return MBIM_CONTEXT_TYPE_IMS; if (apn_type & MM_BEARER_APN_TYPE_MMS) return MBIM_CONTEXT_TYPE_MMS; if (apn_type & MM_BEARER_APN_TYPE_VOICE) return MBIM_CONTEXT_TYPE_VOICE; if (apn_type & MM_BEARER_APN_TYPE_PRIVATE) return MBIM_CONTEXT_TYPE_VPN; if (apn_type & MM_BEARER_APN_TYPE_PURCHASE) return MBIM_CONTEXT_TYPE_PURCHASE; if (apn_type & MM_BEARER_APN_TYPE_VIDEO_SHARE) return MBIM_CONTEXT_TYPE_VIDEO_SHARE; if (apn_type & MM_BEARER_APN_TYPE_LOCAL) return MBIM_CONTEXT_TYPE_LOCAL; if (mbim_extensions_supported) { if (apn_type & MM_BEARER_APN_TYPE_MANAGEMENT) return MBIM_CONTEXT_TYPE_ADMIN; if (apn_type & MM_BEARER_APN_TYPE_APP) return MBIM_CONTEXT_TYPE_APP; if (apn_type & MM_BEARER_APN_TYPE_XCAP) return MBIM_CONTEXT_TYPE_XCAP; if (apn_type & MM_BEARER_APN_TYPE_TETHERING) return MBIM_CONTEXT_TYPE_TETHERING; if (apn_type & MM_BEARER_APN_TYPE_EMERGENCY) return MBIM_CONTEXT_TYPE_EMERGENCY_CALLING; } else { if ((apn_type & MM_BEARER_APN_TYPE_MANAGEMENT) || (apn_type & MM_BEARER_APN_TYPE_APP) || (apn_type & MM_BEARER_APN_TYPE_XCAP) || (apn_type & MM_BEARER_APN_TYPE_TETHERING) || (apn_type & MM_BEARER_APN_TYPE_EMERGENCY)) { mm_obj_dbg (log_object, "MS extensions unsupported: " "fallback to using default (internet) APN type"); return MBIM_CONTEXT_TYPE_INTERNET; } } str = mm_bearer_apn_type_build_string_from_mask (apn_type); g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "Unsupported APN types (%s)", str); return MBIM_CONTEXT_TYPE_NONE; } /*****************************************************************************/ MMBearerIpFamily mm_bearer_ip_family_from_mbim_context_ip_type (MbimContextIpType ip_type) { switch (ip_type) { case MBIM_CONTEXT_IP_TYPE_IPV4: return MM_BEARER_IP_FAMILY_IPV4; case MBIM_CONTEXT_IP_TYPE_IPV6: return MM_BEARER_IP_FAMILY_IPV6; case MBIM_CONTEXT_IP_TYPE_IPV4V6: return MM_BEARER_IP_FAMILY_IPV4V6; case MBIM_CONTEXT_IP_TYPE_IPV4_AND_IPV6: return MM_BEARER_IP_FAMILY_IPV4 | MM_BEARER_IP_FAMILY_IPV6; case MBIM_CONTEXT_IP_TYPE_DEFAULT: default: return MM_BEARER_IP_FAMILY_NONE; } } MbimContextIpType mm_bearer_ip_family_to_mbim_context_ip_type (MMBearerIpFamily ip_family, GError **error) { gchar *str; /* NOTE: the input is a BITMASK, so we try to find a "best match" */ switch ((guint)ip_family) { case MM_BEARER_IP_FAMILY_IPV4: return MBIM_CONTEXT_IP_TYPE_IPV4; case MM_BEARER_IP_FAMILY_IPV6: return MBIM_CONTEXT_IP_TYPE_IPV6; case MM_BEARER_IP_FAMILY_IPV4V6: return MBIM_CONTEXT_IP_TYPE_IPV4V6; case (MM_BEARER_IP_FAMILY_IPV4 | MM_BEARER_IP_FAMILY_IPV6): return MBIM_CONTEXT_IP_TYPE_IPV4_AND_IPV6; case MM_BEARER_IP_FAMILY_NONE: case MM_BEARER_IP_FAMILY_ANY: /* A valid default IP family should have been specified */ g_assert_not_reached (); default: break; } str = mm_bearer_ip_family_build_string_from_mask (ip_family); g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "Unsupported IP type configuration: '%s'", str); g_free (str); return MBIM_CONTEXT_IP_TYPE_DEFAULT; } /*****************************************************************************/ gboolean mm_bearer_roaming_allowance_to_mbim_context_roaming_control (MMBearerRoamingAllowance mask, gpointer log_object, MbimContextRoamingControl *out_value, GError **error) { if (mask == MM_BEARER_ROAMING_ALLOWANCE_NONE) { mm_obj_dbg (log_object, "using default (all) roaming allowance"); *out_value = MBIM_CONTEXT_ROAMING_CONTROL_ALLOW_ALL; } else if (mask == MM_BEARER_ROAMING_ALLOWANCE_HOME) *out_value =MBIM_CONTEXT_ROAMING_CONTROL_HOME_ONLY; else if (mask == MM_BEARER_ROAMING_ALLOWANCE_PARTNER) *out_value =MBIM_CONTEXT_ROAMING_CONTROL_PARTNER_ONLY; else if (mask == MM_BEARER_ROAMING_ALLOWANCE_NON_PARTNER) *out_value =MBIM_CONTEXT_ROAMING_CONTROL_NON_PARTNER_ONLY; else if (mask == (MM_BEARER_ROAMING_ALLOWANCE_HOME | MM_BEARER_ROAMING_ALLOWANCE_PARTNER)) *out_value =MBIM_CONTEXT_ROAMING_CONTROL_HOME_AND_PARTNER; else if (mask == (MM_BEARER_ROAMING_ALLOWANCE_HOME | MM_BEARER_ROAMING_ALLOWANCE_NON_PARTNER)) *out_value =MBIM_CONTEXT_ROAMING_CONTROL_HOME_AND_NON_PARTNER; else if (mask == (MM_BEARER_ROAMING_ALLOWANCE_PARTNER | MM_BEARER_ROAMING_ALLOWANCE_NON_PARTNER)) *out_value =MBIM_CONTEXT_ROAMING_CONTROL_PARTNER_AND_NON_PARTNER; else if (mask == (MM_BEARER_ROAMING_ALLOWANCE_HOME | MM_BEARER_ROAMING_ALLOWANCE_PARTNER | MM_BEARER_ROAMING_ALLOWANCE_NON_PARTNER)) *out_value = MBIM_CONTEXT_ROAMING_CONTROL_ALLOW_ALL; else { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "Unsupported roaming allowance mask: 0x%x", mask); return FALSE; } return TRUE; } MMBearerRoamingAllowance mm_bearer_roaming_allowance_from_mbim_context_roaming_control (MbimContextRoamingControl value, GError **error) { switch (value) { case MBIM_CONTEXT_ROAMING_CONTROL_HOME_ONLY: return MM_BEARER_ROAMING_ALLOWANCE_HOME; case MBIM_CONTEXT_ROAMING_CONTROL_PARTNER_ONLY: return MM_BEARER_ROAMING_ALLOWANCE_PARTNER; case MBIM_CONTEXT_ROAMING_CONTROL_NON_PARTNER_ONLY: return MM_BEARER_ROAMING_ALLOWANCE_NON_PARTNER; case MBIM_CONTEXT_ROAMING_CONTROL_HOME_AND_PARTNER: return (MM_BEARER_ROAMING_ALLOWANCE_HOME | MM_BEARER_ROAMING_ALLOWANCE_PARTNER); case MBIM_CONTEXT_ROAMING_CONTROL_HOME_AND_NON_PARTNER: return (MM_BEARER_ROAMING_ALLOWANCE_HOME | MM_BEARER_ROAMING_ALLOWANCE_NON_PARTNER); case MBIM_CONTEXT_ROAMING_CONTROL_PARTNER_AND_NON_PARTNER: return (MM_BEARER_ROAMING_ALLOWANCE_PARTNER | MM_BEARER_ROAMING_ALLOWANCE_NON_PARTNER); case MBIM_CONTEXT_ROAMING_CONTROL_ALLOW_ALL: return (MM_BEARER_ROAMING_ALLOWANCE_HOME | MM_BEARER_ROAMING_ALLOWANCE_PARTNER | MM_BEARER_ROAMING_ALLOWANCE_NON_PARTNER); default: g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "Unsupported roaming control value: 0x%x", value); return MM_BEARER_ROAMING_ALLOWANCE_NONE; } } /*****************************************************************************/ gboolean mm_bearer_access_type_preference_to_mbim_context_media_type (MMBearerAccessTypePreference value, gpointer log_object, MbimContextMediaType *out_value, GError **error) { switch (value) { case MM_BEARER_ACCESS_TYPE_PREFERENCE_NONE: mm_obj_dbg (log_object, "using default (cellular only) context media type"); *out_value = MBIM_CONTEXT_MEDIA_TYPE_CELLULAR_ONLY; return TRUE; case MM_BEARER_ACCESS_TYPE_PREFERENCE_3GPP_ONLY: *out_value = MBIM_CONTEXT_MEDIA_TYPE_CELLULAR_ONLY; return TRUE; case MM_BEARER_ACCESS_TYPE_PREFERENCE_3GPP_PREFERRED: *out_value = MBIM_CONTEXT_MEDIA_TYPE_ALL; return TRUE; case MM_BEARER_ACCESS_TYPE_PREFERENCE_NON_3GPP_ONLY: *out_value = MBIM_CONTEXT_MEDIA_TYPE_WIFI_ONLY; return TRUE; default: g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "Unsupported roaming control value: 0x%x", value); return FALSE; } } gboolean mm_bearer_access_type_preference_from_mbim_context_media_type (MbimContextMediaType value, MMBearerAccessTypePreference *out_value, GError **error) { switch (value) { case MBIM_CONTEXT_MEDIA_TYPE_CELLULAR_ONLY: *out_value = MM_BEARER_ACCESS_TYPE_PREFERENCE_3GPP_ONLY; return TRUE; case MBIM_CONTEXT_MEDIA_TYPE_WIFI_ONLY: *out_value = MM_BEARER_ACCESS_TYPE_PREFERENCE_NON_3GPP_ONLY; return TRUE; case MBIM_CONTEXT_MEDIA_TYPE_ALL: *out_value = MM_BEARER_ACCESS_TYPE_PREFERENCE_3GPP_PREFERRED; return TRUE; default: g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "Unsupported roaming control value: 0x%x", value); return FALSE; } } /*****************************************************************************/ gboolean mm_boolean_from_mbim_context_state (MbimContextState value, gboolean *out_value, GError **error) { switch (value) { case MBIM_CONTEXT_STATE_DISABLED: *out_value = FALSE; return TRUE; case MBIM_CONTEXT_STATE_ENABLED: *out_value = TRUE; return TRUE; default: g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "Unsupported context state value: 0x%x", value); return FALSE; } } MbimContextState mm_boolean_to_mbim_context_state (gboolean value) { return (value ? MBIM_CONTEXT_STATE_ENABLED: MBIM_CONTEXT_STATE_DISABLED); } /*****************************************************************************/ MMBearerProfileSource mm_bearer_profile_source_from_mbim_context_source (MbimContextSource value, GError **error) { switch (value) { case MBIM_CONTEXT_SOURCE_ADMIN: return MM_BEARER_PROFILE_SOURCE_ADMIN; case MBIM_CONTEXT_SOURCE_USER: return MM_BEARER_PROFILE_SOURCE_USER; case MBIM_CONTEXT_SOURCE_OPERATOR: return MM_BEARER_PROFILE_SOURCE_OPERATOR; case MBIM_CONTEXT_SOURCE_MODEM: return MM_BEARER_PROFILE_SOURCE_MODEM; case MBIM_CONTEXT_SOURCE_DEVICE: return MM_BEARER_PROFILE_SOURCE_DEVICE; default: g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "Unsupported context source value: 0x%x", value); return MM_BEARER_PROFILE_SOURCE_UNKNOWN; } } gboolean mm_bearer_profile_source_to_mbim_context_source (MMBearerProfileSource value, gpointer log_object, MbimContextSource *out_value, GError **error) { switch (value) { case MM_BEARER_PROFILE_SOURCE_UNKNOWN: mm_obj_dbg (log_object, "using default (admin) context source"); *out_value = MBIM_CONTEXT_SOURCE_ADMIN; return TRUE; case MM_BEARER_PROFILE_SOURCE_ADMIN: *out_value = MBIM_CONTEXT_SOURCE_ADMIN; return TRUE; case MM_BEARER_PROFILE_SOURCE_USER: *out_value = MBIM_CONTEXT_SOURCE_USER; return TRUE; case MM_BEARER_PROFILE_SOURCE_OPERATOR: *out_value = MBIM_CONTEXT_SOURCE_OPERATOR; return TRUE; case MM_BEARER_PROFILE_SOURCE_MODEM: *out_value = MBIM_CONTEXT_SOURCE_MODEM; return TRUE; case MM_BEARER_PROFILE_SOURCE_DEVICE: *out_value = MBIM_CONTEXT_SOURCE_DEVICE; return TRUE; default: g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "Unsupported profile source value: 0x%x", value); return FALSE; } } /*****************************************************************************/ /* index in the array is the code point (8 possible values), and the actual * value is the lower limit of the error rate range. */ static const gdouble bit_error_rate_ranges[] = { 0.00, 0.20, 0.40, 0.80, 1.60, 3.20, 6.40, 12.80 }; static const gdouble frame_error_rate_ranges[] = { 0.00, 0.01, 0.10, 0.50, 1.00, 2.00, 4.00, 8.00 }; gboolean mm_signal_error_rate_percentage_from_coded_value (guint coded_value, gdouble *out_percentage, gboolean is_gsm, GError **error) { if ((is_gsm && (coded_value >= G_N_ELEMENTS (bit_error_rate_ranges))) || (!is_gsm && (coded_value >= G_N_ELEMENTS (frame_error_rate_ranges)))) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS, "error rate coded value out of range: %u", coded_value); return FALSE; } *out_percentage = (is_gsm ? bit_error_rate_ranges[coded_value] : frame_error_rate_ranges[coded_value]); return TRUE; } /*****************************************************************************/ gboolean mm_signal_rssi_from_coded_value (guint coded_value, gdouble *out_rssi, GError **error) { /* expected values between 0 and 31 */ if (coded_value > 31) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS, "rssi coded value out of range: %u", coded_value); return FALSE; } *out_rssi = (gdouble)coded_value - 113; return TRUE; } /*****************************************************************************/ gboolean mm_signal_rsrp_from_coded_value (guint coded_value, gdouble *out_rsrp, GError **error) { /* expected values between 0 and 126 */ if (coded_value > 126) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS, "rsrp coded value out of range: %u", coded_value); return FALSE; } *out_rsrp = (gdouble)coded_value - 156; return TRUE; } /*****************************************************************************/ gboolean mm_signal_snr_from_coded_value (guint coded_value, gdouble *out_snr, GError **error) { /* expected values between 0 and 126 */ if (coded_value > 127) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS, "snr coded value out of range: %u", coded_value); return FALSE; } *out_snr = ((gdouble)coded_value)/2 - 23; return TRUE; } /*****************************************************************************/ MMModem3gppMicoMode mm_modem_3gpp_mico_mode_from_mbim_mico_mode (MbimMicoMode mico_mode) { switch (mico_mode) { case MBIM_MICO_MODE_DISABLED: return MM_MODEM_3GPP_MICO_MODE_DISABLED; case MBIM_MICO_MODE_ENABLED: return MM_MODEM_3GPP_MICO_MODE_ENABLED; case MBIM_MICO_MODE_UNSUPPORTED: return MM_MODEM_3GPP_MICO_MODE_UNSUPPORTED; case MBIM_MICO_MODE_DEFAULT: /* default expected only in set requests */ default: return MM_MODEM_3GPP_MICO_MODE_UNKNOWN; } } MbimMicoMode mm_modem_3gpp_mico_mode_to_mbim_mico_mode (MMModem3gppMicoMode mico_mode) { switch (mico_mode) { case MM_MODEM_3GPP_MICO_MODE_DISABLED: return MBIM_MICO_MODE_DISABLED; case MM_MODEM_3GPP_MICO_MODE_ENABLED: return MBIM_MICO_MODE_ENABLED; case MM_MODEM_3GPP_MICO_MODE_UNSUPPORTED: return MBIM_MICO_MODE_UNSUPPORTED; case MM_MODEM_3GPP_MICO_MODE_UNKNOWN: default: return MBIM_MICO_MODE_DEFAULT; } } MMModem3gppDrxCycle mm_modem_3gpp_drx_cycle_from_mbim_drx_cycle (MbimDrxCycle drx_cycle) { switch (drx_cycle) { case MBIM_DRX_CYCLE_NOT_SUPPORTED: return MM_MODEM_3GPP_DRX_CYCLE_UNSUPPORTED; case MBIM_DRX_CYCLE_32: return MM_MODEM_3GPP_DRX_CYCLE_32; case MBIM_DRX_CYCLE_64: return MM_MODEM_3GPP_DRX_CYCLE_64; case MBIM_DRX_CYCLE_128: return MM_MODEM_3GPP_DRX_CYCLE_128; case MBIM_DRX_CYCLE_256: return MM_MODEM_3GPP_DRX_CYCLE_256; case MBIM_DRX_CYCLE_NOT_SPECIFIED: default: return MM_MODEM_3GPP_DRX_CYCLE_UNKNOWN; } } MbimDrxCycle mm_modem_3gpp_drx_cycle_to_mbim_drx_cycle (MMModem3gppDrxCycle drx_cycle) { switch (drx_cycle) { case MM_MODEM_3GPP_DRX_CYCLE_UNSUPPORTED: return MBIM_DRX_CYCLE_NOT_SUPPORTED; case MM_MODEM_3GPP_DRX_CYCLE_32: return MBIM_DRX_CYCLE_32; case MM_MODEM_3GPP_DRX_CYCLE_64: return MBIM_DRX_CYCLE_64; case MM_MODEM_3GPP_DRX_CYCLE_128: return MBIM_DRX_CYCLE_128; case MM_MODEM_3GPP_DRX_CYCLE_256: return MBIM_DRX_CYCLE_256; case MM_MODEM_3GPP_DRX_CYCLE_UNKNOWN: default: return MBIM_DRX_CYCLE_NOT_SPECIFIED; } } /*****************************************************************************/ MMSmsState mm_sms_state_from_mbim_message_status (MbimSmsStatus status) { switch (status) { case MBIM_SMS_STATUS_NEW: return MM_SMS_STATE_RECEIVED; case MBIM_SMS_STATUS_OLD: return MM_SMS_STATE_RECEIVED; case MBIM_SMS_STATUS_DRAFT: return MM_SMS_STATE_STORED; case MBIM_SMS_STATUS_SENT: return MM_SMS_STATE_SENT; default: break; } return MM_SMS_STATE_UNKNOWN; } /*****************************************************************************/ guint mm_signal_quality_from_mbim_signal_state (guint rssi, MbimRsrpSnrInfoArray *rsrp_snr, guint32 rsrp_snr_count, gpointer log_object) { guint quality; /* When MBIMEx is enabled we may get RSSI unset, but per access technology * RSRP available. When more than one access technology in use (e.g. 4G+5G in * 5G NSA), take the highest RSRP value reported. */ if (rssi == 99 && rsrp_snr && rsrp_snr_count) { guint i; gint max_rsrp = G_MININT; for (i = 0; i < rsrp_snr_count; i++) { MbimRsrpSnrInfo *info; info = rsrp_snr[i]; /* scale the value to dBm */ if (info->rsrp < 127) { gint rsrp; rsrp = -157 + info->rsrp; if (rsrp > max_rsrp) max_rsrp = rsrp; } } quality = MM_RSRP_TO_QUALITY (max_rsrp); mm_obj_dbg (log_object, "signal state update: %ddBm --> %u%%", max_rsrp, quality); } else { /* Normalize the quality. 99 means unknown, we default it to 0 */ quality = MM_CLAMP_HIGH (rssi == 99 ? 0 : rssi, 31) * 100 / 31; mm_obj_dbg (log_object, "signal state update: %u --> %u%%", rssi, quality); } return quality; } static MMSignal ** select_mbim_signal_with_data_class (MbimDataClass data_class, MMSignal **cdma, MMSignal **evdo, MMSignal **gsm, MMSignal **umts, MMSignal **lte, MMSignal **nr5g) { if (data_class & (MBIM_DATA_CLASS_5G_NSA | MBIM_DATA_CLASS_5G_SA)) return nr5g; if (data_class & (MBIM_DATA_CLASS_LTE)) return lte; if (data_class & (MBIM_DATA_CLASS_UMTS | MBIM_DATA_CLASS_HSDPA | MBIM_DATA_CLASS_HSUPA)) return umts; if (data_class & (MBIM_DATA_CLASS_GPRS | MBIM_DATA_CLASS_EDGE)) return gsm; if (data_class & (MBIM_DATA_CLASS_1XEVDO | MBIM_DATA_CLASS_1XEVDO_REVA | MBIM_DATA_CLASS_1XEVDV | MBIM_DATA_CLASS_3XRTT | MBIM_DATA_CLASS_1XEVDO_REVB)) return evdo; if (data_class & MBIM_DATA_CLASS_1XRTT) return cdma; return NULL; } gboolean mm_signal_from_mbim_signal_state (MbimDataClass data_class, guint coded_rssi, guint coded_error_rate, MbimRsrpSnrInfoArray *rsrp_snr, guint32 rsrp_snr_count, gpointer log_object, MMSignal **out_cdma, MMSignal **out_evdo, MMSignal **out_gsm, MMSignal **out_umts, MMSignal **out_lte, MMSignal **out_nr5g) { MMSignal **tmp; MMSignal **last_updated = NULL; guint n_out_updated = 0; if (out_cdma) *out_cdma = NULL; if (out_evdo) *out_evdo = NULL; if (out_gsm) *out_gsm = NULL; if (out_umts) *out_umts = NULL; if (out_lte) *out_lte = NULL; if (out_nr5g) *out_nr5g = NULL; /* When MBIMEx v2.0 is available, we get LTE+5GNR information reported * in the RSRP/SNR list of items. */ if (rsrp_snr && rsrp_snr_count) { guint i; for (i = 0; i < rsrp_snr_count; i++) { MbimRsrpSnrInfo *info; info = rsrp_snr[i]; tmp = select_mbim_signal_with_data_class (info->system_type, out_cdma, out_evdo, out_gsm, out_umts, out_lte, out_nr5g); if (!tmp || ((info->rsrp == 0xFFFFFFFF) && (info->snr == 0xFFFFFFFF))) continue; last_updated = tmp; n_out_updated++; *tmp = mm_signal_new (); mm_signal_set_rsrp (*tmp, MM_SIGNAL_UNKNOWN); if (info->rsrp != 0xFFFFFFFF) { g_autoptr(GError) error = NULL; gdouble rsrp; if (!mm_signal_rsrp_from_coded_value (info->rsrp, &rsrp, &error)) mm_obj_dbg (log_object, "couldn't convert RSRP coded value '%u': %s", info->rsrp, error->message); else mm_signal_set_rsrp (*tmp, rsrp); } mm_signal_set_snr (*tmp, MM_SIGNAL_UNKNOWN); if (info->snr != 0xFFFFFFFF) { g_autoptr(GError) error = NULL; gdouble snr; if (!mm_signal_snr_from_coded_value (info->snr, &snr, &error)) mm_obj_dbg (log_object, "couldn't convert SNR coded value '%u': %s", info->snr, error->message); else mm_signal_set_snr (*tmp, snr); } } } /* The MBIM v1.0 details (RSSI, error rate) will only be set if * the target access technology is known without any doubt. * E.g. if we are in 5GNSA (4G+5G), we will only set the fields * if one of them has valid values. If both have valid values, * we'll skip updating RSSI and error rate, as we wouldn't know * to which of them applies. */ if (n_out_updated > 1) return TRUE; if (n_out_updated == 0) { tmp = select_mbim_signal_with_data_class (data_class, out_cdma, out_evdo, out_gsm, out_umts, out_lte, out_nr5g); if (!tmp) return FALSE; *tmp = mm_signal_new (); } else { tmp = last_updated; g_assert (tmp && *tmp); } mm_signal_set_error_rate (*tmp, MM_SIGNAL_UNKNOWN); if (coded_error_rate != 99) { g_autoptr(GError) error = NULL; gdouble error_rate; if (!mm_signal_error_rate_percentage_from_coded_value (coded_error_rate, &error_rate, data_class == (MBIM_DATA_CLASS_GPRS | MBIM_DATA_CLASS_EDGE), &error)) mm_obj_dbg (log_object, "couldn't convert error rate coded value '%u': %s", coded_error_rate, error->message); else mm_signal_set_error_rate (*tmp, error_rate); } mm_signal_set_rssi (*tmp, MM_SIGNAL_UNKNOWN); if (coded_rssi != 99) { g_autoptr(GError) error = NULL; gdouble rssi; if (!mm_signal_rssi_from_coded_value (coded_rssi, &rssi, &error)) mm_obj_dbg (log_object, "couldn't convert RSSI coded value '%u': %s", coded_rssi, error->message); else mm_signal_set_rssi (*tmp, rssi); } return TRUE; } gboolean mm_signal_from_atds_signal_response (guint32 rssi, guint32 rscp, guint32 ecno, guint32 rsrq, guint32 rsrp, guint32 snr, MMSignal **out_gsm, MMSignal **out_umts, MMSignal **out_lte) { if (rscp <= 96) { *out_umts = mm_signal_new (); mm_signal_set_rscp (*out_umts, -120.0 + rscp); } if (ecno <= 49) { if (!*out_umts) *out_umts = mm_signal_new (); mm_signal_set_ecio (*out_umts, -24.0 + ((gdouble) ecno / 2)); } if (rsrq <= 34) { *out_lte = mm_signal_new (); mm_signal_set_rsrq (*out_lte, -19.5 + ((gdouble) rsrq / 2)); } if (rsrp <= 97) { if (!*out_lte) *out_lte = mm_signal_new (); mm_signal_set_rsrp (*out_lte, -140.0 + rsrp); } if (snr <= 35) { if (!*out_lte) *out_lte = mm_signal_new (); mm_signal_set_snr (*out_lte, -5.0 + snr); } /* RSSI may be given for all 2G, 3G or 4G so we detect to which one applies */ if (rssi <= 31) { gdouble value; value = -113.0 + (2 * rssi); if (*out_lte) mm_signal_set_rssi (*out_lte, value); else if (*out_umts) mm_signal_set_rssi (*out_umts, value); else { *out_gsm = mm_signal_new (); mm_signal_set_rssi (*out_gsm, value); } } if (!out_gsm && !out_umts && !out_lte) { return FALSE; } return TRUE; } /*****************************************************************************/ void mm_rf_info_free (MMRfInfo *rf_data) { g_free (rf_data); } void mm_rfim_info_list_free (GList *rfim_info_list) { g_list_free_full (rfim_info_list, (GDestroyNotify) mm_rf_info_free); } GList * mm_rfim_info_list_from_mbim_intel_rfim_frequency_value_array (MbimIntelRfimFrequencyValueArray *freq_info, guint freq_count, gpointer log_object) { GList *info_list = NULL; guint i; for (i = 0; i < freq_count; i++) { MMRfInfo *info; /* If Cell info value indicates radio off, then other parameters are invalid. * So those data will be ignored. */ if (freq_info[i]->serving_cell_info == MBIM_INTEL_SERVING_CELL_INFO_RADIO_OFF) continue; info = g_new0 (MMRfInfo, 1); info->serving_cell_type = MM_SERVING_CELL_TYPE_UNKNOWN; switch (freq_info[i]->serving_cell_info) { case MBIM_INTEL_SERVING_CELL_INFO_PCELL: info->serving_cell_type = MM_SERVING_CELL_TYPE_PCELL; break; case MBIM_INTEL_SERVING_CELL_INFO_SCELL: info->serving_cell_type = MM_SERVING_CELL_TYPE_SCELL; break; case MBIM_INTEL_SERVING_CELL_INFO_PSCELL: info->serving_cell_type = MM_SERVING_CELL_TYPE_PSCELL; break; case MBIM_INTEL_SERVING_CELL_INFO_SSCELL: info->serving_cell_type = MM_SERVING_CELL_TYPE_SSCELL; break; case MBIM_INTEL_SERVING_CELL_INFO_RADIO_OFF: default: info->serving_cell_type = MM_SERVING_CELL_TYPE_INVALID; break; } info->bandwidth = freq_info[i]->bandwidth; info->center_frequency = freq_info[i]->center_frequency; info_list = g_list_append (info_list, info); } return info_list; } typedef struct { guint8 band; gdouble fdl_low; guint32 n_offs_dl; guint32 range_dl1; guint32 range_dl2; } LteDlRangeData; static LteDlRangeData lte_dl_range_data [] = { { 1, 2110, 0, 0, 599 }, { 2, 1930, 600, 600, 1199 }, { 3, 1805, 1200, 1200, 1949 }, { 4, 2110, 1950, 1950, 2399 }, { 5, 869, 2400, 2400, 2649 }, { 6, 875, 2650, 2650, 2749 }, { 7, 2620, 2750, 2750, 3449 }, { 8, 925, 3450, 3450, 3799 }, { 9, 1844.9, 3800, 3800, 4149 }, { 10, 2110, 4150, 4150, 4749 }, { 11, 1475.9, 4750, 4750, 4949 }, { 12, 728, 5000, 5000, 5179 }, { 13, 746, 5180, 5180, 5279 }, { 14, 758, 5280, 5280, 5379 }, { 17, 734, 5730, 5730, 5849 }, { 18, 860, 5850, 5850, 5999 }, { 19, 875, 6000, 6000, 6149 }, { 20, 791, 6150, 6150, 6449 }, { 21, 1495.9, 6450, 6450, 6599 }, { 33, 1900, 36000, 36000, 36199 }, { 34, 2010, 36200, 36200, 36349 }, { 35, 1850, 36350, 36350, 36949 }, { 36, 1930, 36950, 36950, 37549 }, { 37, 1910, 37550, 37550, 37749 }, { 38, 2570, 37750, 37750, 38249 }, { 39, 1880, 38250, 38250, 38649 }, { 40, 2300, 38650, 38650, 39649 }, }; static gint earfcn_to_band_index (guint32 earfcn, gpointer log_object) { guint i; for (i = 0; i < G_N_ELEMENTS (lte_dl_range_data); i++) { if (lte_dl_range_data[i].range_dl1 <= earfcn && lte_dl_range_data[i].range_dl2 >= earfcn) { mm_obj_dbg (log_object, "found matching band index %u for earfcn %u", i, earfcn); return i; } } mm_obj_dbg (log_object, "earfcn %u not matched to any band index", earfcn); return -1; } gdouble mm_earfcn_to_frequency (guint32 earfcn, gpointer log_object) { gint i; i = earfcn_to_band_index (earfcn, log_object); if (i < 0) return 0.0; return 1.0e6 * (lte_dl_range_data[i].fdl_low + 0.1 * (earfcn - lte_dl_range_data[i].n_offs_dl)); } typedef struct { guint global_khz; guint range_offset; guint nrarfcn_offset; guint range_first; guint range_last; } NrRangeData ; static NrRangeData nr_range_data [] = { { 5, 0, 0, 0, 599999 }, { 15, 3000000, 600000, 600000, 2016666 }, { 60, 24250080, 2016667, 2016667, 3279165 }, }; static gint nrarfcn_to_range_index (guint32 nrarfcn, gpointer log_object) { guint i; for (i = 0; i < G_N_ELEMENTS (nr_range_data); i++) { if (nr_range_data[i].range_first <= nrarfcn && nr_range_data[i].range_last >= nrarfcn) { mm_obj_dbg (log_object, "found matching range index %u for nrarfcn %u", i, nrarfcn); return i; } } mm_obj_dbg (log_object, "nrarfcn %u not matched to any range index", nrarfcn); return -1; } gdouble mm_nrarfcn_to_frequency (guint32 nrarfcn, gpointer log_object) { gint i; i = nrarfcn_to_range_index (nrarfcn, log_object); if (i < 0) return 0.0; return 1.0e3 * (nr_range_data[i].range_offset + nr_range_data[i].global_khz * (nrarfcn - nr_range_data[i].nrarfcn_offset)); } /*****************************************************************************/ static const MMMobileEquipmentError mbim_nw_errors[] = { [MBIM_NW_ERROR_IMSI_UNKNOWN_IN_HLR] = MM_MOBILE_EQUIPMENT_ERROR_IMSI_UNKNOWN_IN_HSS, [MBIM_NW_ERROR_ILLEGAL_MS] = MM_MOBILE_EQUIPMENT_ERROR_ILLEGAL_UE, [MBIM_NW_ERROR_IMSI_UNKNOWN_IN_VLR] = MM_MOBILE_EQUIPMENT_ERROR_IMSI_UNKNOWN_IN_VLR, [MBIM_NW_ERROR_ILLEGAL_ME] = MM_MOBILE_EQUIPMENT_ERROR_ILLEGAL_ME, [MBIM_NW_ERROR_GPRS_NOT_ALLOWED] = MM_MOBILE_EQUIPMENT_ERROR_PS_SERVICES_NOT_ALLOWED, [MBIM_NW_ERROR_GPRS_AND_NON_GPRS_NOT_ALLOWED] = MM_MOBILE_EQUIPMENT_ERROR_PS_AND_NON_PS_SERVICES_NOT_ALLOWED, [MBIM_NW_ERROR_PLMN_NOT_ALLOWED] = MM_MOBILE_EQUIPMENT_ERROR_PLMN_NOT_ALLOWED, [MBIM_NW_ERROR_LOCATION_AREA_NOT_ALLOWED] = MM_MOBILE_EQUIPMENT_ERROR_AREA_NOT_ALLOWED, [MBIM_NW_ERROR_ROAMING_NOT_ALLOWED_IN_LOCATION_AREA] = MM_MOBILE_EQUIPMENT_ERROR_ROAMING_NOT_ALLOWED_IN_AREA, [MBIM_NW_ERROR_GPRS_NOT_ALLOWED_IN_PLMN] = MM_MOBILE_EQUIPMENT_ERROR_PS_SERVICES_NOT_ALLOWED_IN_PLMN, [MBIM_NW_ERROR_NO_CELLS_IN_LOCATION_AREA] = MM_MOBILE_EQUIPMENT_ERROR_NO_CELLS_IN_AREA, [MBIM_NW_ERROR_NETWORK_FAILURE] = MM_MOBILE_EQUIPMENT_ERROR_NETWORK_FAILURE_ATTACH, [MBIM_NW_ERROR_CONGESTION] = MM_MOBILE_EQUIPMENT_ERROR_CONGESTION, [MBIM_NW_ERROR_GSM_AUTHENTICATION_UNACCEPTABLE] = MM_MOBILE_EQUIPMENT_ERROR_USER_AUTHENTICATION_FAILED, [MBIM_NW_ERROR_NOT_AUTHORIZED_FOR_CSG] = MM_MOBILE_EQUIPMENT_ERROR_NOT_AUTHORIZED_FOR_CSG, [MBIM_NW_ERROR_INSUFFICIENT_RESOURCES] = MM_MOBILE_EQUIPMENT_ERROR_INSUFFICIENT_RESOURCES, [MBIM_NW_ERROR_MISSING_OR_UNKNOWN_APN] = MM_MOBILE_EQUIPMENT_ERROR_MISSING_OR_UNKNOWN_APN, [MBIM_NW_ERROR_UNKNOWN_PDP_ADDRESS_OR_TYPE] = MM_MOBILE_EQUIPMENT_ERROR_UNKNOWN_PDP_ADDRESS_OR_TYPE, [MBIM_NW_ERROR_USER_AUTHENTICATION_FAILED] = MM_MOBILE_EQUIPMENT_ERROR_USER_AUTHENTICATION_FAILED, [MBIM_NW_ERROR_ACTIVATION_REJECTED_BY_GGSN_OR_GW] = MM_MOBILE_EQUIPMENT_ERROR_ACTIVATION_REJECTED_BY_GGSN_OR_GW, [MBIM_NW_ERROR_ACTIVATION_REJECTED_UNSPECIFIED] = MM_MOBILE_EQUIPMENT_ERROR_ACTIVATION_REJECTED_UNSPECIFIED, [MBIM_NW_ERROR_SERVICE_OPTION_NOT_SUPPORTED] = MM_MOBILE_EQUIPMENT_ERROR_SERVICE_OPTION_NOT_SUPPORTED, [MBIM_NW_ERROR_REQUESTED_SERVICE_OPTION_NOT_SUBSCRIBED] = MM_MOBILE_EQUIPMENT_ERROR_SERVICE_OPTION_NOT_SUBSCRIBED, [MBIM_NW_ERROR_SERVICE_OPTION_TEMPORARILY_OUT_OF_ORDER] = MM_MOBILE_EQUIPMENT_ERROR_SERVICE_OPTION_OUT_OF_ORDER, [MBIM_NW_ERROR_MAXIMUM_NUMBER_OF_PDP_CONTEXTS_REACHED] = MM_MOBILE_EQUIPMENT_ERROR_MAXIMUM_NUMBER_OF_BEARERS_REACHED, [MBIM_NW_ERROR_REQUESTED_APN_NOT_SUPPORTED_IN_CURRENT_RAT_AND_PLMN] = MM_MOBILE_EQUIPMENT_ERROR_REQUESTED_APN_NOT_SUPPORTED, [MBIM_NW_ERROR_SEMANTICALLY_INCORRECT_MESSAGE] = MM_MOBILE_EQUIPMENT_ERROR_SEMANTICALLY_INCORRECT_MESSAGE, [MBIM_NW_ERROR_PROTOCOL_ERROR_UNSPECIFIED] = MM_MOBILE_EQUIPMENT_ERROR_UNSPECIFIED_PROTOCOL_ERROR, [MBIM_NW_ERROR_IMEI_NOT_ACCEPTED] = MM_MOBILE_EQUIPMENT_ERROR_IMEI_NOT_ACCEPTED, [MBIM_NW_ERROR_MS_IDENTITY_NOT_DERIVED_BY_NETWORK] = MM_MOBILE_EQUIPMENT_ERROR_UE_IDENTITY_NOT_DERIVED_FROM_NETWORK, [MBIM_NW_ERROR_IMPLICITLY_DETACHED] = MM_MOBILE_EQUIPMENT_ERROR_IMPLICITLY_DETACHED, [MBIM_NW_ERROR_MSC_TEMPORARILY_NOT_REACHABLE] = MM_MOBILE_EQUIPMENT_ERROR_MSC_TEMPORARILY_NOT_REACHABLE, [MBIM_NW_ERROR_NO_PDP_CONTEXT_ACTIVATED] = MM_MOBILE_EQUIPMENT_ERROR_NO_BEARER_ACTIVATED, [MBIM_NW_ERROR_PDP_TYPE_IPV4_ONLY_ALLOWED] = MM_MOBILE_EQUIPMENT_ERROR_IPV4_ONLY_ALLOWED, [MBIM_NW_ERROR_PDP_TYPE_IPV6_ONLY_ALLOWED] = MM_MOBILE_EQUIPMENT_ERROR_IPV6_ONLY_ALLOWED, [MBIM_NW_ERROR_INVALID_MANDATORY_INFORMATION] = MM_MOBILE_EQUIPMENT_ERROR_INVALID_MANDATORY_INFORMATION, [MBIM_NW_ERROR_MESSAGE_TYPE_NON_EXISTENT_OR_NOT_IMPLEMENTED] = MM_MOBILE_EQUIPMENT_ERROR_MESSAGE_TYPE_NOT_IMPLEMENTED, [MBIM_NW_ERROR_MESSAGE_TYPE_NOT_COMPATIBLE_WITH_PROTOCOL_STATE] = MM_MOBILE_EQUIPMENT_ERROR_MESSAGE_TYPE_NOT_COMPATIBLE_WITH_PROTOCOL_STATE, [MBIM_NW_ERROR_INFORMATION_ELEMENT_NON_EXISTENT_OR_NOT_IMPLEMENTED] = MM_MOBILE_EQUIPMENT_ERROR_IE_NOT_IMPLEMENTED, [MBIM_NW_ERROR_CONDITIONAL_IE_ERROR] = MM_MOBILE_EQUIPMENT_ERROR_CONDITIONAL_IE_ERROR, [MBIM_NW_ERROR_MESSAGE_NOT_COMPATIBLE_WITH_PROTOCOL_STATE] = MM_MOBILE_EQUIPMENT_ERROR_MESSAGE_NOT_COMPATIBLE_WITH_PROTOCOL_STATE, [MBIM_NW_ERROR_APN_RESTRICTION_VALUE_INCOMPATIBLE_WITH_ACTIVE_PDP_CONTEXT] = MM_MOBILE_EQUIPMENT_ERROR_APN_RESTRICTION_INCOMPATIBLE, [MBIM_NW_ERROR_MULTIPLE_ACCESSES_TO_A_PDN_CONNECTION_NOT_ALLOWED] = MM_MOBILE_EQUIPMENT_ERROR_MULTIPLE_ACCESS_TO_PDN_CONNECTION_NOT_ALLOWED, [MBIM_NW_ERROR_NONE] = MM_MOBILE_EQUIPMENT_ERROR_UNKNOWN, /* known unmapped errors */ /* MBIM_NW_ERROR_MAC_FAILURE */ /* MBIM_NW_ERROR_SYNCH_FAILURE */ }; GError * mm_error_from_mbim_nw_error (MbimNwError nw_error, gpointer log_object) { const gchar *msg; if (nw_error < G_N_ELEMENTS (mbim_nw_errors)) { MMMobileEquipmentError error_code; /* convert to mobile equipment error */ error_code = mbim_nw_errors[nw_error]; if (error_code) return mm_mobile_equipment_error_for_code (error_code, log_object); /* provide a nicer error message on unmapped errors */ msg = mbim_nw_error_get_string (nw_error); if (msg) return g_error_new (MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_UNKNOWN, "Unsupported error (%u): %s", nw_error, msg); } /* fallback */ return g_error_new_literal (MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_UNKNOWN, "Unknown error"); } /*****************************************************************************/ void mm_register_mbim_errors (void) { static gsize mbim_errors_registered = 0; if (!g_once_init_enter (&mbim_errors_registered)) return; /* MBIM core errors */ mm_register_error_mapping (MBIM_CORE_ERROR, MBIM_CORE_ERROR_FAILED, MM_CORE_ERROR, MM_CORE_ERROR_FAILED); mm_register_error_mapping (MBIM_CORE_ERROR, MBIM_CORE_ERROR_WRONG_STATE, MM_CORE_ERROR, MM_CORE_ERROR_WRONG_STATE); mm_register_error_mapping (MBIM_CORE_ERROR, MBIM_CORE_ERROR_TIMEOUT, MM_CORE_ERROR, MM_CORE_ERROR_TIMEOUT); mm_register_error_mapping (MBIM_CORE_ERROR, MBIM_CORE_ERROR_INVALID_ARGS, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS); mm_register_error_mapping (MBIM_CORE_ERROR, MBIM_CORE_ERROR_INVALID_MESSAGE, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS); mm_register_error_mapping (MBIM_CORE_ERROR, MBIM_CORE_ERROR_UNSUPPORTED, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED); mm_register_error_mapping (MBIM_CORE_ERROR, MBIM_CORE_ERROR_ABORTED, MM_CORE_ERROR, MM_CORE_ERROR_ABORTED); mm_register_error_mapping (MBIM_CORE_ERROR, MBIM_CORE_ERROR_UNKNOWN_STATE, MM_CORE_ERROR, MM_CORE_ERROR_WRONG_STATE); mm_register_error_mapping (MBIM_CORE_ERROR, MBIM_CORE_ERROR_INCOMPLETE_MESSAGE, MM_CORE_ERROR, MM_CORE_ERROR_PROTOCOL); /* MBIM protocol errors */ mm_register_error_mapping (MBIM_PROTOCOL_ERROR, MBIM_PROTOCOL_ERROR_INVALID, MM_CORE_ERROR, MM_CORE_ERROR_PROTOCOL); mm_register_error_mapping (MBIM_PROTOCOL_ERROR, MBIM_PROTOCOL_ERROR_TIMEOUT_FRAGMENT, MM_CORE_ERROR, MM_CORE_ERROR_PROTOCOL); mm_register_error_mapping (MBIM_PROTOCOL_ERROR, MBIM_PROTOCOL_ERROR_FRAGMENT_OUT_OF_SEQUENCE, MM_CORE_ERROR, MM_CORE_ERROR_PROTOCOL); mm_register_error_mapping (MBIM_PROTOCOL_ERROR, MBIM_PROTOCOL_ERROR_LENGTH_MISMATCH, MM_CORE_ERROR, MM_CORE_ERROR_PROTOCOL); mm_register_error_mapping (MBIM_PROTOCOL_ERROR, MBIM_PROTOCOL_ERROR_DUPLICATED_TID, MM_CORE_ERROR, MM_CORE_ERROR_PROTOCOL); mm_register_error_mapping (MBIM_PROTOCOL_ERROR, MBIM_PROTOCOL_ERROR_NOT_OPENED, MM_CORE_ERROR, MM_CORE_ERROR_PROTOCOL); mm_register_error_mapping (MBIM_PROTOCOL_ERROR, MBIM_PROTOCOL_ERROR_UNKNOWN, MM_CORE_ERROR, MM_CORE_ERROR_PROTOCOL); mm_register_error_mapping (MBIM_PROTOCOL_ERROR, MBIM_PROTOCOL_ERROR_CANCEL, MM_CORE_ERROR, MM_CORE_ERROR_PROTOCOL); mm_register_error_mapping (MBIM_PROTOCOL_ERROR, MBIM_PROTOCOL_ERROR_MAX_TRANSFER, MM_CORE_ERROR, MM_CORE_ERROR_PROTOCOL); /* MBIM status errors */ mm_register_error_mapping (MBIM_STATUS_ERROR, MBIM_STATUS_ERROR_BUSY, MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_PHONE_FAILURE); mm_register_error_mapping (MBIM_STATUS_ERROR, MBIM_STATUS_ERROR_FAILURE, MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_PHONE_FAILURE); mm_register_error_mapping (MBIM_STATUS_ERROR, MBIM_STATUS_ERROR_SIM_NOT_INSERTED, MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_SIM_NOT_INSERTED); mm_register_error_mapping (MBIM_STATUS_ERROR, MBIM_STATUS_ERROR_BAD_SIM, MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_SIM_WRONG); mm_register_error_mapping (MBIM_STATUS_ERROR, MBIM_STATUS_ERROR_PIN_REQUIRED, MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_SIM_PIN); /* MBIM_STATUS_ERROR_PIN_DISABLED */ mm_register_error_mapping (MBIM_STATUS_ERROR, MBIM_STATUS_ERROR_NOT_REGISTERED, MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_NO_NETWORK); mm_register_error_mapping (MBIM_STATUS_ERROR, MBIM_STATUS_ERROR_PROVIDERS_NOT_FOUND, MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_NO_NETWORK); mm_register_error_mapping (MBIM_STATUS_ERROR, MBIM_STATUS_ERROR_NO_DEVICE_SUPPORT, MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_NOT_SUPPORTED); mm_register_error_mapping (MBIM_STATUS_ERROR, MBIM_STATUS_ERROR_PROVIDER_NOT_VISIBLE, MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_NO_NETWORK); mm_register_error_mapping (MBIM_STATUS_ERROR, MBIM_STATUS_ERROR_DATA_CLASS_NOT_AVAILABLE, MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_INVALID_MOBILE_CLASS); mm_register_error_mapping (MBIM_STATUS_ERROR, MBIM_STATUS_ERROR_PACKET_SERVICE_DETACHED, MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_NO_CONNECTION); mm_register_error_mapping (MBIM_STATUS_ERROR, MBIM_STATUS_ERROR_MAX_ACTIVATED_CONTEXTS, MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_NO_CONNECTION); mm_register_error_mapping (MBIM_STATUS_ERROR, MBIM_STATUS_ERROR_NOT_INITIALIZED, MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_NO_CONNECTION); /* MBIM_STATUS_ERROR_VOICE_CALL_IN_PROGRESS */ mm_register_error_mapping (MBIM_STATUS_ERROR, MBIM_STATUS_ERROR_CONTEXT_NOT_ACTIVATED, MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_NO_BEARER_ACTIVATED); mm_register_error_mapping (MBIM_STATUS_ERROR, MBIM_STATUS_ERROR_SERVICE_NOT_ACTIVATED, MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_NO_BEARER_ACTIVATED); mm_register_error_mapping (MBIM_STATUS_ERROR, MBIM_STATUS_ERROR_INVALID_ACCESS_STRING, MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_MISSING_OR_UNKNOWN_APN); mm_register_error_mapping (MBIM_STATUS_ERROR, MBIM_STATUS_ERROR_INVALID_USER_NAME_PWD, MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_INCORRECT_PASSWORD); mm_register_error_mapping (MBIM_STATUS_ERROR, MBIM_STATUS_ERROR_RADIO_POWER_OFF, MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_NOT_ALLOWED); mm_register_error_mapping (MBIM_STATUS_ERROR, MBIM_STATUS_ERROR_INVALID_PARAMETERS, MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_INVALID_MANDATORY_INFORMATION); /* MBIM_STATUS_ERROR_READ_FAILURE */ /* MBIM_STATUS_ERROR_WRITE_FAILURE */ /* MBIM_STATUS_ERROR_NO_PHONEBOOK */ /* MBIM_STATUS_ERROR_PARAMETER_TOO_LONG */ /* MBIM_STATUS_ERROR_STK_BUSY */ mm_register_error_mapping (MBIM_STATUS_ERROR, MBIM_STATUS_ERROR_OPERATION_NOT_ALLOWED, MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_NOT_ALLOWED); mm_register_error_mapping (MBIM_STATUS_ERROR, MBIM_STATUS_ERROR_MEMORY_FAILURE, MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_MEMORY_FAILURE); mm_register_error_mapping (MBIM_STATUS_ERROR, MBIM_STATUS_ERROR_INVALID_MEMORY_INDEX, MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_INVALID_INDEX); mm_register_error_mapping (MBIM_STATUS_ERROR, MBIM_STATUS_ERROR_MEMORY_FULL, MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_MEMORY_FULL); mm_register_error_mapping (MBIM_STATUS_ERROR, MBIM_STATUS_ERROR_FILTER_NOT_SUPPORTED, MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_NOT_SUPPORTED); /* MBIM_STATUS_ERROR_DSS_INSTANCE_LIMIT */ mm_register_error_mapping (MBIM_STATUS_ERROR, MBIM_STATUS_ERROR_INVALID_DEVICE_SERVICE_OPERATION, MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_NOT_SUPPORTED); mm_register_error_mapping (MBIM_STATUS_ERROR, MBIM_STATUS_ERROR_AUTH_INCORRECT_AUTN, MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_USER_AUTHENTICATION_FAILED); /* MBIM_STATUS_ERROR_AUTH_SYNC_FAILURE */ /* MBIM_STATUS_ERROR_AUTH_AMF_NOT_SET */ mm_register_error_mapping (MBIM_STATUS_ERROR, MBIM_STATUS_ERROR_CONTEXT_NOT_SUPPORTED, MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_NOT_SUPPORTED); mm_register_error_mapping (MBIM_STATUS_ERROR, MBIM_STATUS_ERROR_SMS_UNKNOWN_SMSC_ADDRESS, MM_MESSAGE_ERROR, MM_MESSAGE_ERROR_SMSC_ADDRESS_UNKNOWN); mm_register_error_mapping (MBIM_STATUS_ERROR, MBIM_STATUS_ERROR_SMS_NETWORK_TIMEOUT, MM_MESSAGE_ERROR, MM_MESSAGE_ERROR_NETWORK_TIMEOUT); mm_register_error_mapping (MBIM_STATUS_ERROR, MBIM_STATUS_ERROR_SMS_LANG_NOT_SUPPORTED, MM_MESSAGE_ERROR, MM_MESSAGE_ERROR_NOT_SUPPORTED); mm_register_error_mapping (MBIM_STATUS_ERROR, MBIM_STATUS_ERROR_SMS_ENCODING_NOT_SUPPORTED, MM_MESSAGE_ERROR, MM_MESSAGE_ERROR_NOT_SUPPORTED); mm_register_error_mapping (MBIM_STATUS_ERROR, MBIM_STATUS_ERROR_SMS_FORMAT_NOT_SUPPORTED, MM_MESSAGE_ERROR, MM_MESSAGE_ERROR_NOT_SUPPORTED); mm_register_error_mapping (MBIM_STATUS_ERROR, MBIM_STATUS_ERROR_INVALID_SIGNATURE, MM_CARRIER_LOCK_ERROR, MM_CARRIER_LOCK_ERROR_INVALID_SIGNATURE); mm_register_error_mapping (MBIM_STATUS_ERROR, MBIM_STATUS_ERROR_INVALID_IMEI, MM_CARRIER_LOCK_ERROR, MM_CARRIER_LOCK_ERROR_INVALID_IMEI); mm_register_error_mapping (MBIM_STATUS_ERROR, MBIM_STATUS_ERROR_INVALID_TIMESTAMP, MM_CARRIER_LOCK_ERROR, MM_CARRIER_LOCK_ERROR_INVALID_TIMESTAMP); mm_register_error_mapping (MBIM_STATUS_ERROR, MBIM_STATUS_ERROR_NETWORK_LIST_TOO_LARGE, MM_CARRIER_LOCK_ERROR, MM_CARRIER_LOCK_ERROR_NETWORK_LIST_TOO_LARGE); mm_register_error_mapping (MBIM_STATUS_ERROR, MBIM_STATUS_ERROR_SIGNATURE_ALGORITHM_NOT_SUPPORTED, MM_CARRIER_LOCK_ERROR, MM_CARRIER_LOCK_ERROR_SIGNATURE_ALGORITHM_NOT_SUPPORTED); mm_register_error_mapping (MBIM_STATUS_ERROR, MBIM_STATUS_ERROR_FEATURE_NOT_SUPPORTED, MM_CARRIER_LOCK_ERROR, MM_CARRIER_LOCK_ERROR_FEATURE_NOT_SUPPORTED); mm_register_error_mapping (MBIM_STATUS_ERROR, MBIM_STATUS_ERROR_DECODE_OR_PARSING_ERROR, MM_CARRIER_LOCK_ERROR, MM_CARRIER_LOCK_ERROR_DECODE_OR_PARSING_ERROR); g_once_init_leave (&mbim_errors_registered, 1); } ModemManager-1.23.4-dev/src/mm-modem-helpers-mbim.h000066400000000000000000000256121456466623000217650ustar00rootroot00000000000000/* -*- 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) 2013 Aleksander Morgado */ #ifndef MM_MODEM_HELPERS_MBIM_H #define MM_MODEM_HELPERS_MBIM_H #include #include #define _LIBMM_INSIDE_MM #include #include /*****************************************************************************/ /* MBIM/BasicConnect to MM translations */ MMModemCapability mm_modem_capability_from_mbim_device_caps (MbimCellularClass caps_cellular_class, MbimDataClass caps_data_class, const gchar *caps_custom_data_class); MMModemLock mm_modem_lock_from_mbim_pin_type (MbimPinType pin_type); MMModem3gppRegistrationState mm_modem_3gpp_registration_state_from_mbim_register_state (MbimRegisterState state); MMModem3gppPacketServiceState mm_modem_3gpp_packet_service_state_from_mbim_packet_service_state (MbimPacketServiceState state); MbimDataClass mm_mbim_data_class_from_mbim_data_class_v3_and_subclass (MbimDataClassV3 data_class_v3, MbimDataSubclass data_subclass); MMModemMode mm_modem_mode_from_mbim_data_class (MbimDataClass data_class, const gchar *caps_custom_data_class); MbimDataClass mm_mbim_data_class_from_modem_mode (MMModemMode modem_mode, gboolean is_3gpp, gboolean is_cdma); MMModemAccessTechnology mm_modem_access_technology_from_mbim_data_class (MbimDataClass data_class); MMModem3gppNetworkAvailability mm_modem_3gpp_network_availability_from_mbim_provider_state (MbimProviderState state); GList *mm_3gpp_network_info_list_from_mbim_providers (const MbimProvider *const *providers, guint n_providers); MbimPinType mbim_pin_type_from_mm_modem_3gpp_facility (MMModem3gppFacility facility); MMModem3gppFacility mm_modem_3gpp_facility_from_mbim_pin_type (MbimPinType pin_type); MMBearerAllowedAuth mm_bearer_allowed_auth_from_mbim_auth_protocol (MbimAuthProtocol auth_protocol); MbimAuthProtocol mm_bearer_allowed_auth_to_mbim_auth_protocol (MMBearerAllowedAuth bearer_auth, gpointer log_object, GError **error); MMBearerIpFamily mm_bearer_ip_family_from_mbim_context_ip_type (MbimContextIpType ip_type); MbimContextIpType mm_bearer_ip_family_to_mbim_context_ip_type (MMBearerIpFamily ip_family, GError **error); MMBearerApnType mm_bearer_apn_type_from_mbim_context_type (MbimContextType context_type); MbimContextType mm_bearer_apn_type_to_mbim_context_type (MMBearerApnType apn_type, gboolean mbim_extensions_supported, gpointer log_object, GError **error); gboolean mm_bearer_roaming_allowance_to_mbim_context_roaming_control (MMBearerRoamingAllowance mask, gpointer log_object, MbimContextRoamingControl *out_value, GError **error); MMBearerRoamingAllowance mm_bearer_roaming_allowance_from_mbim_context_roaming_control (MbimContextRoamingControl value, GError **error); gboolean mm_bearer_access_type_preference_to_mbim_context_media_type (MMBearerAccessTypePreference value, gpointer log_object, MbimContextMediaType *out_value, GError **error); gboolean mm_bearer_access_type_preference_from_mbim_context_media_type (MbimContextMediaType value, MMBearerAccessTypePreference *out_value, GError **error); gboolean mm_boolean_from_mbim_context_state (MbimContextState value, gboolean *out_value, GError **error); MbimContextState mm_boolean_to_mbim_context_state (gboolean value); MMBearerProfileSource mm_bearer_profile_source_from_mbim_context_source (MbimContextSource value, GError **error); gboolean mm_bearer_profile_source_to_mbim_context_source (MMBearerProfileSource value, gpointer log_object, MbimContextSource *out_value, GError **error); gboolean mm_signal_error_rate_percentage_from_coded_value (guint coded_value, gdouble *out_percentage, gboolean is_gsm, GError **error); gboolean mm_signal_rssi_from_coded_value (guint coded_value, gdouble *out_rssi, GError **error); gboolean mm_signal_rsrp_from_coded_value (guint coded_value, gdouble *out_rsrp, GError **error); gboolean mm_signal_snr_from_coded_value (guint coded_value, gdouble *out_snr, GError **error); MMModem3gppMicoMode mm_modem_3gpp_mico_mode_from_mbim_mico_mode (MbimMicoMode mico_mode); MbimMicoMode mm_modem_3gpp_mico_mode_to_mbim_mico_mode (MMModem3gppMicoMode mico_mode); MMModem3gppDrxCycle mm_modem_3gpp_drx_cycle_from_mbim_drx_cycle (MbimDrxCycle drx_cycle); MbimDrxCycle mm_modem_3gpp_drx_cycle_to_mbim_drx_cycle (MMModem3gppDrxCycle drx_cycle); /*****************************************************************************/ /* MBIM/SMS to MM translations */ MMSmsState mm_sms_state_from_mbim_message_status (MbimSmsStatus status); /*****************************************************************************/ guint mm_signal_quality_from_mbim_signal_state (guint rssi, MbimRsrpSnrInfoArray *rsrp_snr, guint32 rsrp_snr_count, gpointer log_object); gboolean mm_signal_from_mbim_signal_state (MbimDataClass data_class, guint coded_rssi, guint coded_error_rate, MbimRsrpSnrInfoArray *rsrp_snr, guint32 rsrp_snr_count, gpointer log_object, MMSignal **out_cdma, MMSignal **out_evdo, MMSignal **out_gsm, MMSignal **out_umts, MMSignal **out_lte, MMSignal **out_nr5g); gboolean mm_signal_from_atds_signal_response (guint32 rssi, guint32 rscp, guint32 ecno, guint32 rsrq, guint32 rsrp, guint32 snr, MMSignal **out_gsm, MMSignal **out_umts, MMSignal **out_lte); /*****************************************************************************/ /* RF utilities */ /*****************************************************************************/ /* Value defined to allow tolerance in the center frequency comparison logic */ #define FREQUENCY_TOLERANCE_HZ 300 typedef struct { MMServingCellType serving_cell_type; guint32 bandwidth; guint64 center_frequency; } MMRfInfo; void mm_rf_info_free (MMRfInfo *rf_data); void mm_rfim_info_list_free (GList *rfim_info_list); GList *mm_rfim_info_list_from_mbim_intel_rfim_frequency_value_array (MbimIntelRfimFrequencyValueArray *freq_info, guint freq_count, gpointer log_object); gdouble mm_earfcn_to_frequency (guint32 earfcn, gpointer log_object); gdouble mm_nrarfcn_to_frequency (guint32 nrarfcn, gpointer log_object); /*****************************************************************************/ /* MM error translations */ void mm_register_mbim_errors (void); GError *mm_error_from_mbim_nw_error (MbimNwError nw_error, gpointer log_object); #endif /* MM_MODEM_HELPERS_MBIM_H */ ModemManager-1.23.4-dev/src/mm-modem-helpers-qmi.c000066400000000000000000005006601456466623000216230ustar00rootroot00000000000000/* -*- 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) 2012-2018 Google, Inc. * Copyright (C) 2018 Aleksander Morgado * Copyright (c) 2021 Qualcomm Innovation Center, Inc. */ #include #include #include #include "mm-modem-helpers-qmi.h" #include "mm-modem-helpers.h" #include "mm-error-helpers.h" #include "mm-enums-types.h" #include "mm-flags-types.h" #include "mm-log-object.h" /*****************************************************************************/ MMModemCapability mm_modem_capability_from_qmi_radio_interface (QmiDmsRadioInterface network, gpointer log_object) { switch (network) { case QMI_DMS_RADIO_INTERFACE_CDMA20001X: return MM_MODEM_CAPABILITY_CDMA_EVDO; case QMI_DMS_RADIO_INTERFACE_EVDO: return MM_MODEM_CAPABILITY_CDMA_EVDO; case QMI_DMS_RADIO_INTERFACE_GSM: return MM_MODEM_CAPABILITY_GSM_UMTS; case QMI_DMS_RADIO_INTERFACE_UMTS: return MM_MODEM_CAPABILITY_GSM_UMTS; case QMI_DMS_RADIO_INTERFACE_LTE: return MM_MODEM_CAPABILITY_LTE; case QMI_DMS_RADIO_INTERFACE_TDS: return MM_MODEM_CAPABILITY_TDS; case QMI_DMS_RADIO_INTERFACE_5GNR: return MM_MODEM_CAPABILITY_5GNR; default: mm_obj_warn (log_object, "unhandled QMI radio interface '%u'", (guint)network); return MM_MODEM_CAPABILITY_NONE; } } /*****************************************************************************/ MMModemMode mm_modem_mode_from_qmi_radio_interface (QmiDmsRadioInterface network, gpointer log_object) { switch (network) { case QMI_DMS_RADIO_INTERFACE_CDMA20001X: return MM_MODEM_MODE_2G; case QMI_DMS_RADIO_INTERFACE_EVDO: return MM_MODEM_MODE_3G; case QMI_DMS_RADIO_INTERFACE_GSM: return MM_MODEM_MODE_2G; case QMI_DMS_RADIO_INTERFACE_UMTS: return MM_MODEM_MODE_3G; case QMI_DMS_RADIO_INTERFACE_LTE: return MM_MODEM_MODE_4G; case QMI_DMS_RADIO_INTERFACE_5GNR: return MM_MODEM_MODE_5G; case QMI_DMS_RADIO_INTERFACE_TDS: default: mm_obj_warn (log_object, "unhandled QMI radio interface '%u'", (guint)network); return MM_MODEM_MODE_NONE; } } /*****************************************************************************/ /* pin1 TRUE for PIN1, FALSE for PIN2 */ MMModemLock mm_modem_lock_from_qmi_uim_pin_status (QmiDmsUimPinStatus status, gboolean pin1) { switch (status) { case QMI_DMS_UIM_PIN_STATUS_NOT_INITIALIZED: return MM_MODEM_LOCK_UNKNOWN; case QMI_DMS_UIM_PIN_STATUS_ENABLED_NOT_VERIFIED: return pin1 ? MM_MODEM_LOCK_SIM_PIN : MM_MODEM_LOCK_SIM_PIN2; case QMI_DMS_UIM_PIN_STATUS_ENABLED_VERIFIED: return MM_MODEM_LOCK_NONE; case QMI_DMS_UIM_PIN_STATUS_DISABLED: return MM_MODEM_LOCK_NONE; case QMI_DMS_UIM_PIN_STATUS_BLOCKED: return pin1 ? MM_MODEM_LOCK_SIM_PUK : MM_MODEM_LOCK_SIM_PUK2; case QMI_DMS_UIM_PIN_STATUS_PERMANENTLY_BLOCKED: return MM_MODEM_LOCK_UNKNOWN; case QMI_DMS_UIM_PIN_STATUS_UNBLOCKED: /* This state is possibly given when after an Unblock() operation has been performed. * We'll assume the PIN is verified after this. */ return MM_MODEM_LOCK_NONE; case QMI_DMS_UIM_PIN_STATUS_CHANGED: /* This state is possibly given when after an ChangePin() operation has been performed. * We'll assume the PIN is verified after this. */ return MM_MODEM_LOCK_NONE; default: return MM_MODEM_LOCK_UNKNOWN; } } /*****************************************************************************/ gboolean mm_pin_enabled_from_qmi_uim_pin_status (QmiDmsUimPinStatus status) { switch (status) { case QMI_DMS_UIM_PIN_STATUS_ENABLED_NOT_VERIFIED: case QMI_DMS_UIM_PIN_STATUS_ENABLED_VERIFIED: case QMI_DMS_UIM_PIN_STATUS_BLOCKED: case QMI_DMS_UIM_PIN_STATUS_PERMANENTLY_BLOCKED: case QMI_DMS_UIM_PIN_STATUS_UNBLOCKED: case QMI_DMS_UIM_PIN_STATUS_CHANGED: /* assume the PIN to be enabled then */ return TRUE; case QMI_DMS_UIM_PIN_STATUS_DISABLED: case QMI_DMS_UIM_PIN_STATUS_NOT_INITIALIZED: /* assume the PIN to be disabled then */ return FALSE; default: /* by default assume disabled */ return FALSE; } } /*****************************************************************************/ QmiDmsUimFacility mm_3gpp_facility_to_qmi_uim_facility (MMModem3gppFacility mm) { switch (mm) { case MM_MODEM_3GPP_FACILITY_PH_SIM: /* Not really sure about this one; it may be PH_FSIM? */ return QMI_DMS_UIM_FACILITY_PF; case MM_MODEM_3GPP_FACILITY_NET_PERS: return QMI_DMS_UIM_FACILITY_PN; case MM_MODEM_3GPP_FACILITY_NET_SUB_PERS: return QMI_DMS_UIM_FACILITY_PU; case MM_MODEM_3GPP_FACILITY_PROVIDER_PERS: return QMI_DMS_UIM_FACILITY_PP; case MM_MODEM_3GPP_FACILITY_CORP_PERS: return QMI_DMS_UIM_FACILITY_PC; case MM_MODEM_3GPP_FACILITY_NONE: case MM_MODEM_3GPP_FACILITY_SIM: case MM_MODEM_3GPP_FACILITY_FIXED_DIALING: case MM_MODEM_3GPP_FACILITY_PH_FSIM: default: /* Never try to ask for a facility we cannot translate */ g_assert_not_reached (); } } /*****************************************************************************/ typedef struct { QmiDmsBandCapability qmi_band; MMModemBand mm_band; } DmsBandsMap; static const DmsBandsMap dms_bands_map [] = { /* CDMA bands */ { (QMI_DMS_BAND_CAPABILITY_BC_0_A_SYSTEM | QMI_DMS_BAND_CAPABILITY_BC_0_B_SYSTEM), MM_MODEM_BAND_CDMA_BC0 }, { QMI_DMS_BAND_CAPABILITY_BC_1_ALL_BLOCKS, MM_MODEM_BAND_CDMA_BC1 }, { QMI_DMS_BAND_CAPABILITY_BC_2, MM_MODEM_BAND_CDMA_BC2 }, { QMI_DMS_BAND_CAPABILITY_BC_3_A_SYSTEM, MM_MODEM_BAND_CDMA_BC3 }, { QMI_DMS_BAND_CAPABILITY_BC_4_ALL_BLOCKS, MM_MODEM_BAND_CDMA_BC4 }, { QMI_DMS_BAND_CAPABILITY_BC_5_ALL_BLOCKS, MM_MODEM_BAND_CDMA_BC5 }, { QMI_DMS_BAND_CAPABILITY_BC_6, MM_MODEM_BAND_CDMA_BC6 }, { QMI_DMS_BAND_CAPABILITY_BC_7, MM_MODEM_BAND_CDMA_BC7 }, { QMI_DMS_BAND_CAPABILITY_BC_8, MM_MODEM_BAND_CDMA_BC8 }, { QMI_DMS_BAND_CAPABILITY_BC_9, MM_MODEM_BAND_CDMA_BC9 }, { QMI_DMS_BAND_CAPABILITY_BC_10, MM_MODEM_BAND_CDMA_BC10 }, { QMI_DMS_BAND_CAPABILITY_BC_11, MM_MODEM_BAND_CDMA_BC11 }, { QMI_DMS_BAND_CAPABILITY_BC_12, MM_MODEM_BAND_CDMA_BC12 }, { QMI_DMS_BAND_CAPABILITY_BC_14, MM_MODEM_BAND_CDMA_BC14 }, { QMI_DMS_BAND_CAPABILITY_BC_15, MM_MODEM_BAND_CDMA_BC15 }, { QMI_DMS_BAND_CAPABILITY_BC_16, MM_MODEM_BAND_CDMA_BC16 }, { QMI_DMS_BAND_CAPABILITY_BC_17, MM_MODEM_BAND_CDMA_BC17 }, { QMI_DMS_BAND_CAPABILITY_BC_18, MM_MODEM_BAND_CDMA_BC18 }, { QMI_DMS_BAND_CAPABILITY_BC_19, MM_MODEM_BAND_CDMA_BC19 }, /* GSM bands */ { QMI_DMS_BAND_CAPABILITY_GSM_DCS_1800, MM_MODEM_BAND_DCS }, { (QMI_DMS_BAND_CAPABILITY_GSM_900_PRIMARY | QMI_DMS_BAND_CAPABILITY_GSM_900_EXTENDED), MM_MODEM_BAND_EGSM }, { QMI_DMS_BAND_CAPABILITY_GSM_PCS_1900, MM_MODEM_BAND_PCS }, { QMI_DMS_BAND_CAPABILITY_GSM_850, MM_MODEM_BAND_G850 }, { QMI_DMS_BAND_CAPABILITY_GSM_450, MM_MODEM_BAND_G450 }, { QMI_DMS_BAND_CAPABILITY_GSM_480, MM_MODEM_BAND_G480 }, { QMI_DMS_BAND_CAPABILITY_GSM_750, MM_MODEM_BAND_G750 }, /* UMTS/WCDMA bands */ { QMI_DMS_BAND_CAPABILITY_WCDMA_2100, MM_MODEM_BAND_UTRAN_1 }, { QMI_DMS_BAND_CAPABILITY_WCDMA_DCS_1800, MM_MODEM_BAND_UTRAN_3 }, { QMI_DMS_BAND_CAPABILITY_WCDMA_PCS_1900, MM_MODEM_BAND_UTRAN_2 }, { QMI_DMS_BAND_CAPABILITY_WCDMA_1700_US, MM_MODEM_BAND_UTRAN_4 }, { QMI_DMS_BAND_CAPABILITY_WCDMA_800, MM_MODEM_BAND_UTRAN_6 }, { QMI_DMS_BAND_CAPABILITY_WCDMA_850_US, MM_MODEM_BAND_UTRAN_5 }, { QMI_DMS_BAND_CAPABILITY_WCDMA_900, MM_MODEM_BAND_UTRAN_8 }, { QMI_DMS_BAND_CAPABILITY_WCDMA_1700_JAPAN, MM_MODEM_BAND_UTRAN_9 }, { QMI_DMS_BAND_CAPABILITY_WCDMA_2600, MM_MODEM_BAND_UTRAN_7 }, { QMI_DMS_BAND_CAPABILITY_WCDMA_1500, MM_MODEM_BAND_UTRAN_11 }, { QMI_DMS_BAND_CAPABILITY_WCDMA_850_JAPAN, MM_MODEM_BAND_UTRAN_19 }, }; static void dms_add_qmi_bands (GArray *mm_bands, QmiDmsBandCapability qmi_bands, gpointer log_object) { static QmiDmsBandCapability qmi_bands_expected = 0; QmiDmsBandCapability not_expected; guint i; g_assert (mm_bands != NULL); /* Build mask of expected bands only once */ if (G_UNLIKELY (qmi_bands_expected == 0)) { for (i = 0; i < G_N_ELEMENTS (dms_bands_map); i++) { qmi_bands_expected |= dms_bands_map[i].qmi_band; } } /* Log about the bands that cannot be represented in ModemManager */ not_expected = ((qmi_bands_expected ^ qmi_bands) & qmi_bands); if (not_expected) { g_autofree gchar *aux = NULL; aux = qmi_dms_band_capability_build_string_from_mask (not_expected); mm_obj_dbg (log_object, "cannot add the following bands: '%s'", aux); } /* And add the expected ones */ for (i = 0; i < G_N_ELEMENTS (dms_bands_map); i++) { if (qmi_bands & dms_bands_map[i].qmi_band) g_array_append_val (mm_bands, dms_bands_map[i].mm_band); } } typedef struct { QmiDmsLteBandCapability qmi_band; MMModemBand mm_band; } DmsLteBandsMap; static const DmsLteBandsMap dms_lte_bands_map [] = { { QMI_DMS_LTE_BAND_CAPABILITY_EUTRAN_1, MM_MODEM_BAND_EUTRAN_1 }, { QMI_DMS_LTE_BAND_CAPABILITY_EUTRAN_2, MM_MODEM_BAND_EUTRAN_2 }, { QMI_DMS_LTE_BAND_CAPABILITY_EUTRAN_3, MM_MODEM_BAND_EUTRAN_3 }, { QMI_DMS_LTE_BAND_CAPABILITY_EUTRAN_4, MM_MODEM_BAND_EUTRAN_4 }, { QMI_DMS_LTE_BAND_CAPABILITY_EUTRAN_5, MM_MODEM_BAND_EUTRAN_5 }, { QMI_DMS_LTE_BAND_CAPABILITY_EUTRAN_6, MM_MODEM_BAND_EUTRAN_6 }, { QMI_DMS_LTE_BAND_CAPABILITY_EUTRAN_7, MM_MODEM_BAND_EUTRAN_7 }, { QMI_DMS_LTE_BAND_CAPABILITY_EUTRAN_8, MM_MODEM_BAND_EUTRAN_8 }, { QMI_DMS_LTE_BAND_CAPABILITY_EUTRAN_9, MM_MODEM_BAND_EUTRAN_9 }, { QMI_DMS_LTE_BAND_CAPABILITY_EUTRAN_10, MM_MODEM_BAND_EUTRAN_10 }, { QMI_DMS_LTE_BAND_CAPABILITY_EUTRAN_11, MM_MODEM_BAND_EUTRAN_11 }, { QMI_DMS_LTE_BAND_CAPABILITY_EUTRAN_12, MM_MODEM_BAND_EUTRAN_12 }, { QMI_DMS_LTE_BAND_CAPABILITY_EUTRAN_13, MM_MODEM_BAND_EUTRAN_13 }, { QMI_DMS_LTE_BAND_CAPABILITY_EUTRAN_14, MM_MODEM_BAND_EUTRAN_14 }, { QMI_DMS_LTE_BAND_CAPABILITY_EUTRAN_17, MM_MODEM_BAND_EUTRAN_17 }, { QMI_DMS_LTE_BAND_CAPABILITY_EUTRAN_18, MM_MODEM_BAND_EUTRAN_18 }, { QMI_DMS_LTE_BAND_CAPABILITY_EUTRAN_19, MM_MODEM_BAND_EUTRAN_19 }, { QMI_DMS_LTE_BAND_CAPABILITY_EUTRAN_20, MM_MODEM_BAND_EUTRAN_20 }, { QMI_DMS_LTE_BAND_CAPABILITY_EUTRAN_21, MM_MODEM_BAND_EUTRAN_21 }, { QMI_DMS_LTE_BAND_CAPABILITY_EUTRAN_24, MM_MODEM_BAND_EUTRAN_24 }, { QMI_DMS_LTE_BAND_CAPABILITY_EUTRAN_25, MM_MODEM_BAND_EUTRAN_25 }, { QMI_DMS_LTE_BAND_CAPABILITY_EUTRAN_26, MM_MODEM_BAND_EUTRAN_26 }, { QMI_DMS_LTE_BAND_CAPABILITY_EUTRAN_27, MM_MODEM_BAND_EUTRAN_27 }, { QMI_DMS_LTE_BAND_CAPABILITY_EUTRAN_28, MM_MODEM_BAND_EUTRAN_28 }, { QMI_DMS_LTE_BAND_CAPABILITY_EUTRAN_29, MM_MODEM_BAND_EUTRAN_29 }, { QMI_DMS_LTE_BAND_CAPABILITY_EUTRAN_30, MM_MODEM_BAND_EUTRAN_30 }, { QMI_DMS_LTE_BAND_CAPABILITY_EUTRAN_31, MM_MODEM_BAND_EUTRAN_31 }, { QMI_DMS_LTE_BAND_CAPABILITY_EUTRAN_32, MM_MODEM_BAND_EUTRAN_32 }, { QMI_DMS_LTE_BAND_CAPABILITY_EUTRAN_33, MM_MODEM_BAND_EUTRAN_33 }, { QMI_DMS_LTE_BAND_CAPABILITY_EUTRAN_34, MM_MODEM_BAND_EUTRAN_34 }, { QMI_DMS_LTE_BAND_CAPABILITY_EUTRAN_35, MM_MODEM_BAND_EUTRAN_35 }, { QMI_DMS_LTE_BAND_CAPABILITY_EUTRAN_36, MM_MODEM_BAND_EUTRAN_36 }, { QMI_DMS_LTE_BAND_CAPABILITY_EUTRAN_37, MM_MODEM_BAND_EUTRAN_37 }, { QMI_DMS_LTE_BAND_CAPABILITY_EUTRAN_38, MM_MODEM_BAND_EUTRAN_38 }, { QMI_DMS_LTE_BAND_CAPABILITY_EUTRAN_39, MM_MODEM_BAND_EUTRAN_39 }, { QMI_DMS_LTE_BAND_CAPABILITY_EUTRAN_40, MM_MODEM_BAND_EUTRAN_40 }, { QMI_DMS_LTE_BAND_CAPABILITY_EUTRAN_41, MM_MODEM_BAND_EUTRAN_41 }, { QMI_DMS_LTE_BAND_CAPABILITY_EUTRAN_42, MM_MODEM_BAND_EUTRAN_42 }, { QMI_DMS_LTE_BAND_CAPABILITY_EUTRAN_43, MM_MODEM_BAND_EUTRAN_43 } }; static void dms_add_qmi_lte_bands (GArray *mm_bands, QmiDmsLteBandCapability qmi_bands) { /* All QMI LTE bands have a counterpart in ModemManager, no need to check * for unexpected ones */ guint i; g_assert (mm_bands != NULL); for (i = 0; i < G_N_ELEMENTS (dms_lte_bands_map); i++) { if (qmi_bands & dms_lte_bands_map[i].qmi_band) g_array_append_val (mm_bands, dms_lte_bands_map[i].mm_band); } } static void dms_add_extended_qmi_lte_bands (GArray *mm_bands, GArray *extended_qmi_bands, gpointer log_object) { guint i; g_assert (mm_bands != NULL); if (!extended_qmi_bands) return; for (i = 0; i < extended_qmi_bands->len; i++) { guint16 val; val = g_array_index (extended_qmi_bands, guint16, i); /* MM_MODEM_BAND_EUTRAN_1 = 31, * ... * MM_MODEM_BAND_EUTRAN_71 = 101 */ if (val < 1 || val > 71) mm_obj_dbg (log_object, "unexpected LTE band supported by module: EUTRAN %u", val); else { MMModemBand band; band = (MMModemBand)(val + MM_MODEM_BAND_EUTRAN_1 - 1); g_array_append_val (mm_bands, band); } } } static void dms_add_qmi_nr5g_bands (GArray *mm_bands, GArray *qmi_bands, gpointer log_object) { guint i; g_assert (mm_bands != NULL); if (!qmi_bands) return; for (i = 0; i < qmi_bands->len; i++) { guint16 val; val = g_array_index (qmi_bands, guint16, i); /* MM_MODEM_BAND_NGRAN_1 = 301, * ... * MM_MODEM_BAND_NGRAN_261 = 561 */ if (val < 1 || val > 261) mm_obj_dbg (log_object, "unexpected NR5G band supported by module: NGRAN %u", val); else { MMModemBand band; band = (MMModemBand)(val + MM_MODEM_BAND_NGRAN_1 - 1); g_array_append_val (mm_bands, band); } } } GArray * mm_modem_bands_from_qmi_band_capabilities (QmiDmsBandCapability qmi_bands, QmiDmsLteBandCapability qmi_lte_bands, GArray *extended_qmi_lte_bands, GArray *qmi_nr5g_bands, gpointer log_object) { GArray *mm_bands; mm_bands = g_array_new (FALSE, FALSE, sizeof (MMModemBand)); dms_add_qmi_bands (mm_bands, qmi_bands, log_object); if (extended_qmi_lte_bands) dms_add_extended_qmi_lte_bands (mm_bands, extended_qmi_lte_bands, log_object); else dms_add_qmi_lte_bands (mm_bands, qmi_lte_bands); if (qmi_nr5g_bands) dms_add_qmi_nr5g_bands (mm_bands, qmi_nr5g_bands, log_object); return mm_bands; } /*****************************************************************************/ typedef struct { QmiNasBandPreference qmi_band; MMModemBand mm_band; } NasBandsMap; static const NasBandsMap nas_bands_map [] = { /* CDMA bands */ { (QMI_NAS_BAND_PREFERENCE_BC_0_A_SYSTEM | QMI_NAS_BAND_PREFERENCE_BC_0_B_SYSTEM), MM_MODEM_BAND_CDMA_BC0 }, { QMI_NAS_BAND_PREFERENCE_BC_1_ALL_BLOCKS, MM_MODEM_BAND_CDMA_BC1 }, { QMI_NAS_BAND_PREFERENCE_BC_2, MM_MODEM_BAND_CDMA_BC2 }, { QMI_NAS_BAND_PREFERENCE_BC_3_A_SYSTEM, MM_MODEM_BAND_CDMA_BC3 }, { QMI_NAS_BAND_PREFERENCE_BC_4_ALL_BLOCKS, MM_MODEM_BAND_CDMA_BC4 }, { QMI_NAS_BAND_PREFERENCE_BC_5_ALL_BLOCKS, MM_MODEM_BAND_CDMA_BC5 }, { QMI_NAS_BAND_PREFERENCE_BC_6, MM_MODEM_BAND_CDMA_BC6 }, { QMI_NAS_BAND_PREFERENCE_BC_7, MM_MODEM_BAND_CDMA_BC7 }, { QMI_NAS_BAND_PREFERENCE_BC_8, MM_MODEM_BAND_CDMA_BC8 }, { QMI_NAS_BAND_PREFERENCE_BC_9, MM_MODEM_BAND_CDMA_BC9 }, { QMI_NAS_BAND_PREFERENCE_BC_10, MM_MODEM_BAND_CDMA_BC10 }, { QMI_NAS_BAND_PREFERENCE_BC_11, MM_MODEM_BAND_CDMA_BC11 }, { QMI_NAS_BAND_PREFERENCE_BC_12, MM_MODEM_BAND_CDMA_BC12 }, { QMI_NAS_BAND_PREFERENCE_BC_14, MM_MODEM_BAND_CDMA_BC14 }, { QMI_NAS_BAND_PREFERENCE_BC_15, MM_MODEM_BAND_CDMA_BC15 }, { QMI_NAS_BAND_PREFERENCE_BC_16, MM_MODEM_BAND_CDMA_BC16 }, { QMI_NAS_BAND_PREFERENCE_BC_17, MM_MODEM_BAND_CDMA_BC17 }, { QMI_NAS_BAND_PREFERENCE_BC_18, MM_MODEM_BAND_CDMA_BC18 }, { QMI_NAS_BAND_PREFERENCE_BC_19, MM_MODEM_BAND_CDMA_BC19 }, /* GSM bands */ { QMI_NAS_BAND_PREFERENCE_GSM_DCS_1800, MM_MODEM_BAND_DCS }, { (QMI_NAS_BAND_PREFERENCE_GSM_900_PRIMARY | QMI_NAS_BAND_PREFERENCE_GSM_900_EXTENDED), MM_MODEM_BAND_EGSM }, { QMI_NAS_BAND_PREFERENCE_GSM_PCS_1900, MM_MODEM_BAND_PCS }, { QMI_NAS_BAND_PREFERENCE_GSM_850, MM_MODEM_BAND_G850 }, { QMI_NAS_BAND_PREFERENCE_GSM_450, MM_MODEM_BAND_G450 }, { QMI_NAS_BAND_PREFERENCE_GSM_480, MM_MODEM_BAND_G480 }, { QMI_NAS_BAND_PREFERENCE_GSM_750, MM_MODEM_BAND_G750 }, /* UMTS/WCDMA bands */ { QMI_NAS_BAND_PREFERENCE_WCDMA_2100, MM_MODEM_BAND_UTRAN_1 }, { QMI_NAS_BAND_PREFERENCE_WCDMA_DCS_1800, MM_MODEM_BAND_UTRAN_3 }, { QMI_NAS_BAND_PREFERENCE_WCDMA_PCS_1900, MM_MODEM_BAND_UTRAN_2 }, { QMI_NAS_BAND_PREFERENCE_WCDMA_1700_US, MM_MODEM_BAND_UTRAN_4 }, { QMI_NAS_BAND_PREFERENCE_WCDMA_800, MM_MODEM_BAND_UTRAN_6 }, { QMI_NAS_BAND_PREFERENCE_WCDMA_850_US, MM_MODEM_BAND_UTRAN_5 }, { QMI_NAS_BAND_PREFERENCE_WCDMA_900, MM_MODEM_BAND_UTRAN_8 }, { QMI_NAS_BAND_PREFERENCE_WCDMA_1700_JAPAN, MM_MODEM_BAND_UTRAN_9 }, { QMI_NAS_BAND_PREFERENCE_WCDMA_2600, MM_MODEM_BAND_UTRAN_7 }, { QMI_NAS_BAND_PREFERENCE_WCDMA_1500, MM_MODEM_BAND_UTRAN_11 }, { QMI_NAS_BAND_PREFERENCE_WCDMA_850_JAPAN, MM_MODEM_BAND_UTRAN_19 }, }; static void nas_add_qmi_bands (GArray *mm_bands, QmiNasBandPreference qmi_bands, gpointer log_object) { static QmiNasBandPreference qmi_bands_expected = 0; QmiNasBandPreference not_expected; guint i; g_assert (mm_bands != NULL); /* Build mask of expected bands only once */ if (G_UNLIKELY (qmi_bands_expected == 0)) { for (i = 0; i < G_N_ELEMENTS (nas_bands_map); i++) { qmi_bands_expected |= nas_bands_map[i].qmi_band; } } /* Log about the bands that cannot be represented in ModemManager */ not_expected = ((qmi_bands_expected ^ qmi_bands) & qmi_bands); if (not_expected) { g_autofree gchar *aux = NULL; aux = qmi_nas_band_preference_build_string_from_mask (not_expected); mm_obj_dbg (log_object, "cannot add the following bands: '%s'", aux); } /* And add the expected ones */ for (i = 0; i < G_N_ELEMENTS (nas_bands_map); i++) { if (qmi_bands & nas_bands_map[i].qmi_band) g_array_append_val (mm_bands, nas_bands_map[i].mm_band); } } typedef struct { QmiNasLteBandPreference qmi_band; MMModemBand mm_band; } NasLteBandsMap; static const NasLteBandsMap nas_lte_bands_map [] = { { QMI_NAS_LTE_BAND_PREFERENCE_EUTRAN_1, MM_MODEM_BAND_EUTRAN_1 }, { QMI_NAS_LTE_BAND_PREFERENCE_EUTRAN_2, MM_MODEM_BAND_EUTRAN_2 }, { QMI_NAS_LTE_BAND_PREFERENCE_EUTRAN_3, MM_MODEM_BAND_EUTRAN_3 }, { QMI_NAS_LTE_BAND_PREFERENCE_EUTRAN_4, MM_MODEM_BAND_EUTRAN_4 }, { QMI_NAS_LTE_BAND_PREFERENCE_EUTRAN_5, MM_MODEM_BAND_EUTRAN_5 }, { QMI_NAS_LTE_BAND_PREFERENCE_EUTRAN_6, MM_MODEM_BAND_EUTRAN_6 }, { QMI_NAS_LTE_BAND_PREFERENCE_EUTRAN_7, MM_MODEM_BAND_EUTRAN_7 }, { QMI_NAS_LTE_BAND_PREFERENCE_EUTRAN_8, MM_MODEM_BAND_EUTRAN_8 }, { QMI_NAS_LTE_BAND_PREFERENCE_EUTRAN_9, MM_MODEM_BAND_EUTRAN_9 }, { QMI_NAS_LTE_BAND_PREFERENCE_EUTRAN_10, MM_MODEM_BAND_EUTRAN_10 }, { QMI_NAS_LTE_BAND_PREFERENCE_EUTRAN_11, MM_MODEM_BAND_EUTRAN_11 }, { QMI_NAS_LTE_BAND_PREFERENCE_EUTRAN_12, MM_MODEM_BAND_EUTRAN_12 }, { QMI_NAS_LTE_BAND_PREFERENCE_EUTRAN_13, MM_MODEM_BAND_EUTRAN_13 }, { QMI_NAS_LTE_BAND_PREFERENCE_EUTRAN_14, MM_MODEM_BAND_EUTRAN_14 }, { QMI_NAS_LTE_BAND_PREFERENCE_EUTRAN_17, MM_MODEM_BAND_EUTRAN_17 }, { QMI_NAS_LTE_BAND_PREFERENCE_EUTRAN_18, MM_MODEM_BAND_EUTRAN_18 }, { QMI_NAS_LTE_BAND_PREFERENCE_EUTRAN_19, MM_MODEM_BAND_EUTRAN_19 }, { QMI_NAS_LTE_BAND_PREFERENCE_EUTRAN_20, MM_MODEM_BAND_EUTRAN_20 }, { QMI_NAS_LTE_BAND_PREFERENCE_EUTRAN_21, MM_MODEM_BAND_EUTRAN_21 }, { QMI_NAS_LTE_BAND_PREFERENCE_EUTRAN_24, MM_MODEM_BAND_EUTRAN_24 }, { QMI_NAS_LTE_BAND_PREFERENCE_EUTRAN_25, MM_MODEM_BAND_EUTRAN_25 }, { QMI_NAS_LTE_BAND_PREFERENCE_EUTRAN_26, MM_MODEM_BAND_EUTRAN_26 }, { QMI_NAS_LTE_BAND_PREFERENCE_EUTRAN_27, MM_MODEM_BAND_EUTRAN_27 }, { QMI_NAS_LTE_BAND_PREFERENCE_EUTRAN_28, MM_MODEM_BAND_EUTRAN_28 }, { QMI_NAS_LTE_BAND_PREFERENCE_EUTRAN_29, MM_MODEM_BAND_EUTRAN_29 }, { QMI_NAS_LTE_BAND_PREFERENCE_EUTRAN_30, MM_MODEM_BAND_EUTRAN_30 }, { QMI_NAS_LTE_BAND_PREFERENCE_EUTRAN_31, MM_MODEM_BAND_EUTRAN_31 }, { QMI_NAS_LTE_BAND_PREFERENCE_EUTRAN_32, MM_MODEM_BAND_EUTRAN_32 }, { QMI_NAS_LTE_BAND_PREFERENCE_EUTRAN_33, MM_MODEM_BAND_EUTRAN_33 }, { QMI_NAS_LTE_BAND_PREFERENCE_EUTRAN_34, MM_MODEM_BAND_EUTRAN_34 }, { QMI_NAS_LTE_BAND_PREFERENCE_EUTRAN_35, MM_MODEM_BAND_EUTRAN_35 }, { QMI_NAS_LTE_BAND_PREFERENCE_EUTRAN_36, MM_MODEM_BAND_EUTRAN_36 }, { QMI_NAS_LTE_BAND_PREFERENCE_EUTRAN_37, MM_MODEM_BAND_EUTRAN_37 }, { QMI_NAS_LTE_BAND_PREFERENCE_EUTRAN_38, MM_MODEM_BAND_EUTRAN_38 }, { QMI_NAS_LTE_BAND_PREFERENCE_EUTRAN_39, MM_MODEM_BAND_EUTRAN_39 }, { QMI_NAS_LTE_BAND_PREFERENCE_EUTRAN_40, MM_MODEM_BAND_EUTRAN_40 }, { QMI_NAS_LTE_BAND_PREFERENCE_EUTRAN_41, MM_MODEM_BAND_EUTRAN_41 }, { QMI_NAS_LTE_BAND_PREFERENCE_EUTRAN_42, MM_MODEM_BAND_EUTRAN_42 }, { QMI_NAS_LTE_BAND_PREFERENCE_EUTRAN_43, MM_MODEM_BAND_EUTRAN_43 } }; static void nas_add_qmi_lte_bands (GArray *mm_bands, QmiNasLteBandPreference qmi_bands) { /* All QMI LTE bands have a counterpart in ModemManager, no need to check * for unexpected ones */ guint i; g_assert (mm_bands != NULL); for (i = 0; i < G_N_ELEMENTS (nas_lte_bands_map); i++) { if (qmi_bands & nas_lte_bands_map[i].qmi_band) g_array_append_val (mm_bands, nas_lte_bands_map[i].mm_band); } } static void nas_add_extended_qmi_lte_bands (GArray *mm_bands, const guint64 *extended_qmi_lte_bands, guint extended_qmi_lte_bands_size, gpointer log_object) { guint i; g_assert (mm_bands != NULL); for (i = 0; i < extended_qmi_lte_bands_size; i++) { guint j; for (j = 0; j < 64; j++) { guint val; if (!(extended_qmi_lte_bands[i] & (((guint64) 1) << j))) continue; val = 1 + j + (i * 64); /* MM_MODEM_BAND_EUTRAN_1 = 31, * ... * MM_MODEM_BAND_EUTRAN_71 = 101 */ if (val < 1 || val > 71) mm_obj_dbg (log_object, "unexpected LTE band supported by module: EUTRAN %u", val); else { MMModemBand band; band = (val + MM_MODEM_BAND_EUTRAN_1 - 1); g_array_append_val (mm_bands, band); } } } } static void nas_add_qmi_nr5g_bands (GArray *mm_bands, const guint64 *qmi_nr5g_bands, guint qmi_nr5g_bands_size, gpointer log_object) { guint i; g_assert (mm_bands != NULL); for (i = 0; i < qmi_nr5g_bands_size; i++) { guint j; for (j = 0; j < 64; j++) { guint val; if (!(qmi_nr5g_bands[i] & (((guint64) 1) << j))) continue; val = 1 + j + (i * 64); /* MM_MODEM_BAND_NGRAN_1 = 301, * ... * MM_MODEM_BAND_NGRAN_261 = 561 */ if (val < 1 || val > 261) mm_obj_dbg (log_object, "unexpected NR5G band supported by module: NGRAN %u", val); else { MMModemBand band; band = (val + MM_MODEM_BAND_NGRAN_1 - 1); g_array_append_val (mm_bands, band); } } } } GArray * mm_modem_bands_from_qmi_band_preference (QmiNasBandPreference qmi_bands, QmiNasLteBandPreference qmi_lte_bands, const guint64 *extended_qmi_lte_bands, guint extended_qmi_lte_bands_size, const guint64 *qmi_nr5g_bands, guint qmi_nr5g_bands_size, gpointer log_object) { GArray *mm_bands; mm_bands = g_array_new (FALSE, FALSE, sizeof (MMModemBand)); nas_add_qmi_bands (mm_bands, qmi_bands, log_object); if (extended_qmi_lte_bands && extended_qmi_lte_bands_size) nas_add_extended_qmi_lte_bands (mm_bands, extended_qmi_lte_bands, extended_qmi_lte_bands_size, log_object); else nas_add_qmi_lte_bands (mm_bands, qmi_lte_bands); if (qmi_nr5g_bands && qmi_nr5g_bands_size) nas_add_qmi_nr5g_bands (mm_bands, qmi_nr5g_bands, qmi_nr5g_bands_size, log_object); return mm_bands; } void mm_modem_bands_to_qmi_band_preference (GArray *mm_bands, QmiNasBandPreference *qmi_bands, QmiNasLteBandPreference *qmi_lte_bands, guint64 *extended_qmi_lte_bands, guint extended_qmi_lte_bands_size, guint64 *qmi_nr5g_bands, guint qmi_nr5g_bands_size, gpointer log_object) { guint i; *qmi_bands = 0; *qmi_lte_bands = 0; if (extended_qmi_lte_bands) memset (extended_qmi_lte_bands, 0, extended_qmi_lte_bands_size * sizeof (guint64)); if (qmi_nr5g_bands) memset (qmi_nr5g_bands, 0, qmi_nr5g_bands_size * sizeof (guint64)); for (i = 0; i < mm_bands->len; i++) { MMModemBand band; band = g_array_index (mm_bands, MMModemBand, i); if (band >= MM_MODEM_BAND_EUTRAN_1 && band <= MM_MODEM_BAND_EUTRAN_71) { if (extended_qmi_lte_bands && extended_qmi_lte_bands_size) { /* Add extended LTE band preference */ guint val; guint j; guint k; /* it's really (band - MM_MODEM_BAND_EUTRAN_1 +1 -1), because * we want EUTRAN1 in index 0 */ val = band - MM_MODEM_BAND_EUTRAN_1; j = val / 64; g_assert (j < extended_qmi_lte_bands_size); k = val % 64; extended_qmi_lte_bands[j] |= ((guint64)1 << k); } else { /* Add LTE band preference */ guint j; for (j = 0; j < G_N_ELEMENTS (nas_lte_bands_map); j++) { if (nas_lte_bands_map[j].mm_band == band) { *qmi_lte_bands |= nas_lte_bands_map[j].qmi_band; break; } } if (j == G_N_ELEMENTS (nas_lte_bands_map)) mm_obj_dbg (log_object, "cannot add the following LTE band: '%s'", mm_modem_band_get_string (band)); } } else if (band >= MM_MODEM_BAND_NGRAN_1 && band <= MM_MODEM_BAND_NGRAN_261) { if (qmi_nr5g_bands && qmi_nr5g_bands_size) { /* Add NR5G band preference */ guint val; guint j; guint k; /* it's really (band - MM_MODEM_BAND_NGRAN_1 +1 -1), because * we want NGRAN1 in index 0 */ val = band - MM_MODEM_BAND_NGRAN_1; j = val / 64; g_assert (j < qmi_nr5g_bands_size); k = val % 64; qmi_nr5g_bands[j] |= ((guint64)1 << k); } } else { /* Add non-LTE band preference */ guint j; for (j = 0; j < G_N_ELEMENTS (nas_bands_map); j++) { if (nas_bands_map[j].mm_band == band) { *qmi_bands |= nas_bands_map[j].qmi_band; break; } } if (j == G_N_ELEMENTS (nas_bands_map)) mm_obj_dbg (log_object, "cannot add the following band: '%s'", mm_modem_band_get_string (band)); } } } /*****************************************************************************/ typedef struct { QmiNasActiveBand qmi_band; MMModemBand mm_band; } ActiveBandsMap; static const ActiveBandsMap active_bands_map [] = { /* CDMA bands */ { QMI_NAS_ACTIVE_BAND_BC_0, MM_MODEM_BAND_CDMA_BC0 }, { QMI_NAS_ACTIVE_BAND_BC_1, MM_MODEM_BAND_CDMA_BC1 }, { QMI_NAS_ACTIVE_BAND_BC_2, MM_MODEM_BAND_CDMA_BC2 }, { QMI_NAS_ACTIVE_BAND_BC_3, MM_MODEM_BAND_CDMA_BC3 }, { QMI_NAS_ACTIVE_BAND_BC_4, MM_MODEM_BAND_CDMA_BC4 }, { QMI_NAS_ACTIVE_BAND_BC_5, MM_MODEM_BAND_CDMA_BC5 }, { QMI_NAS_ACTIVE_BAND_BC_6, MM_MODEM_BAND_CDMA_BC6 }, { QMI_NAS_ACTIVE_BAND_BC_7, MM_MODEM_BAND_CDMA_BC7 }, { QMI_NAS_ACTIVE_BAND_BC_8, MM_MODEM_BAND_CDMA_BC8 }, { QMI_NAS_ACTIVE_BAND_BC_9, MM_MODEM_BAND_CDMA_BC9 }, { QMI_NAS_ACTIVE_BAND_BC_10, MM_MODEM_BAND_CDMA_BC10 }, { QMI_NAS_ACTIVE_BAND_BC_11, MM_MODEM_BAND_CDMA_BC11 }, { QMI_NAS_ACTIVE_BAND_BC_12, MM_MODEM_BAND_CDMA_BC12 }, { QMI_NAS_ACTIVE_BAND_BC_13, MM_MODEM_BAND_CDMA_BC13 }, { QMI_NAS_ACTIVE_BAND_BC_14, MM_MODEM_BAND_CDMA_BC14 }, { QMI_NAS_ACTIVE_BAND_BC_15, MM_MODEM_BAND_CDMA_BC15 }, { QMI_NAS_ACTIVE_BAND_BC_16, MM_MODEM_BAND_CDMA_BC16 }, { QMI_NAS_ACTIVE_BAND_BC_17, MM_MODEM_BAND_CDMA_BC17 }, { QMI_NAS_ACTIVE_BAND_BC_18, MM_MODEM_BAND_CDMA_BC18 }, { QMI_NAS_ACTIVE_BAND_BC_19, MM_MODEM_BAND_CDMA_BC19 }, /* GSM bands */ { QMI_NAS_ACTIVE_BAND_GSM_850, MM_MODEM_BAND_G850 }, { QMI_NAS_ACTIVE_BAND_GSM_900_EXTENDED, MM_MODEM_BAND_EGSM }, { QMI_NAS_ACTIVE_BAND_GSM_DCS_1800, MM_MODEM_BAND_DCS }, { QMI_NAS_ACTIVE_BAND_GSM_PCS_1900, MM_MODEM_BAND_PCS }, { QMI_NAS_ACTIVE_BAND_GSM_450, MM_MODEM_BAND_G450 }, { QMI_NAS_ACTIVE_BAND_GSM_480, MM_MODEM_BAND_G480 }, { QMI_NAS_ACTIVE_BAND_GSM_750, MM_MODEM_BAND_G750 }, /* WCDMA bands */ { QMI_NAS_ACTIVE_BAND_WCDMA_2100, MM_MODEM_BAND_UTRAN_1 }, { QMI_NAS_ACTIVE_BAND_WCDMA_PCS_1900, MM_MODEM_BAND_UTRAN_2 }, { QMI_NAS_ACTIVE_BAND_WCDMA_DCS_1800, MM_MODEM_BAND_UTRAN_3 }, { QMI_NAS_ACTIVE_BAND_WCDMA_1700_US, MM_MODEM_BAND_UTRAN_4 }, { QMI_NAS_ACTIVE_BAND_WCDMA_850, MM_MODEM_BAND_UTRAN_5 }, { QMI_NAS_ACTIVE_BAND_WCDMA_800, MM_MODEM_BAND_UTRAN_6 }, { QMI_NAS_ACTIVE_BAND_WCDMA_2600, MM_MODEM_BAND_UTRAN_7 }, { QMI_NAS_ACTIVE_BAND_WCDMA_900, MM_MODEM_BAND_UTRAN_8 }, { QMI_NAS_ACTIVE_BAND_WCDMA_1700_JAPAN, MM_MODEM_BAND_UTRAN_9 }, { QMI_NAS_ACTIVE_BAND_WCDMA_1500_JAPAN, MM_MODEM_BAND_UTRAN_11 }, { QMI_NAS_ACTIVE_BAND_WCDMA_850_JAPAN, MM_MODEM_BAND_UTRAN_19 }, /* LTE bands */ { QMI_NAS_ACTIVE_BAND_EUTRAN_1, MM_MODEM_BAND_EUTRAN_1 }, { QMI_NAS_ACTIVE_BAND_EUTRAN_2, MM_MODEM_BAND_EUTRAN_2 }, { QMI_NAS_ACTIVE_BAND_EUTRAN_3, MM_MODEM_BAND_EUTRAN_3 }, { QMI_NAS_ACTIVE_BAND_EUTRAN_4, MM_MODEM_BAND_EUTRAN_4 }, { QMI_NAS_ACTIVE_BAND_EUTRAN_5, MM_MODEM_BAND_EUTRAN_5 }, { QMI_NAS_ACTIVE_BAND_EUTRAN_6, MM_MODEM_BAND_EUTRAN_6 }, { QMI_NAS_ACTIVE_BAND_EUTRAN_7, MM_MODEM_BAND_EUTRAN_7 }, { QMI_NAS_ACTIVE_BAND_EUTRAN_8, MM_MODEM_BAND_EUTRAN_8 }, { QMI_NAS_ACTIVE_BAND_EUTRAN_9, MM_MODEM_BAND_EUTRAN_9 }, { QMI_NAS_ACTIVE_BAND_EUTRAN_10, MM_MODEM_BAND_EUTRAN_10 }, { QMI_NAS_ACTIVE_BAND_EUTRAN_11, MM_MODEM_BAND_EUTRAN_11 }, { QMI_NAS_ACTIVE_BAND_EUTRAN_12, MM_MODEM_BAND_EUTRAN_12 }, { QMI_NAS_ACTIVE_BAND_EUTRAN_13, MM_MODEM_BAND_EUTRAN_13 }, { QMI_NAS_ACTIVE_BAND_EUTRAN_14, MM_MODEM_BAND_EUTRAN_14 }, { QMI_NAS_ACTIVE_BAND_EUTRAN_17, MM_MODEM_BAND_EUTRAN_17 }, { QMI_NAS_ACTIVE_BAND_EUTRAN_18, MM_MODEM_BAND_EUTRAN_18 }, { QMI_NAS_ACTIVE_BAND_EUTRAN_19, MM_MODEM_BAND_EUTRAN_19 }, { QMI_NAS_ACTIVE_BAND_EUTRAN_20, MM_MODEM_BAND_EUTRAN_20 }, { QMI_NAS_ACTIVE_BAND_EUTRAN_21, MM_MODEM_BAND_EUTRAN_21 }, { QMI_NAS_ACTIVE_BAND_EUTRAN_24, MM_MODEM_BAND_EUTRAN_24 }, { QMI_NAS_ACTIVE_BAND_EUTRAN_25, MM_MODEM_BAND_EUTRAN_25 }, { QMI_NAS_ACTIVE_BAND_EUTRAN_33, MM_MODEM_BAND_EUTRAN_33 }, { QMI_NAS_ACTIVE_BAND_EUTRAN_34, MM_MODEM_BAND_EUTRAN_34 }, { QMI_NAS_ACTIVE_BAND_EUTRAN_35, MM_MODEM_BAND_EUTRAN_35 }, { QMI_NAS_ACTIVE_BAND_EUTRAN_36, MM_MODEM_BAND_EUTRAN_36 }, { QMI_NAS_ACTIVE_BAND_EUTRAN_37, MM_MODEM_BAND_EUTRAN_37 }, { QMI_NAS_ACTIVE_BAND_EUTRAN_38, MM_MODEM_BAND_EUTRAN_38 }, { QMI_NAS_ACTIVE_BAND_EUTRAN_39, MM_MODEM_BAND_EUTRAN_39 }, { QMI_NAS_ACTIVE_BAND_EUTRAN_40, MM_MODEM_BAND_EUTRAN_40 }, { QMI_NAS_ACTIVE_BAND_EUTRAN_41, MM_MODEM_BAND_EUTRAN_41 }, { QMI_NAS_ACTIVE_BAND_EUTRAN_42, MM_MODEM_BAND_EUTRAN_42 }, { QMI_NAS_ACTIVE_BAND_EUTRAN_43, MM_MODEM_BAND_EUTRAN_43 } }; static void add_active_bands (GArray *mm_bands, QmiNasActiveBand qmi_bands) { guint i; g_assert (mm_bands != NULL); for (i = 0; i < G_N_ELEMENTS (active_bands_map); i++) { if (qmi_bands == active_bands_map[i].qmi_band) { guint j; /* Avoid adding duplicate band entries */ for (j = 0; j < mm_bands->len; j++) { if (g_array_index (mm_bands, MMModemBand, j) == active_bands_map[i].mm_band) return; } g_array_append_val (mm_bands, active_bands_map[i].mm_band); return; } } } GArray * mm_modem_bands_from_qmi_rf_band_information_array (GArray *info_array) { GArray *mm_bands; mm_bands = g_array_new (FALSE, FALSE, sizeof (MMModemBand)); if (info_array) { guint i; for (i = 0; i < info_array->len; i++) { QmiMessageNasGetRfBandInformationOutputListElement *item; item = &g_array_index (info_array, QmiMessageNasGetRfBandInformationOutputListElement, i); add_active_bands (mm_bands, item->active_band_class); } } return mm_bands; } /*****************************************************************************/ MMModemAccessTechnology mm_modem_access_technology_from_qmi_radio_interface (QmiNasRadioInterface interface) { switch (interface) { case QMI_NAS_RADIO_INTERFACE_CDMA_1X: return MM_MODEM_ACCESS_TECHNOLOGY_1XRTT; case QMI_NAS_RADIO_INTERFACE_CDMA_1XEVDO: return MM_MODEM_ACCESS_TECHNOLOGY_EVDO0; case QMI_NAS_RADIO_INTERFACE_GSM: return MM_MODEM_ACCESS_TECHNOLOGY_GSM; case QMI_NAS_RADIO_INTERFACE_UMTS: return MM_MODEM_ACCESS_TECHNOLOGY_UMTS; case QMI_NAS_RADIO_INTERFACE_LTE: return MM_MODEM_ACCESS_TECHNOLOGY_LTE; case QMI_NAS_RADIO_INTERFACE_5GNR: return MM_MODEM_ACCESS_TECHNOLOGY_5GNR; case QMI_NAS_RADIO_INTERFACE_UNKNOWN: case QMI_NAS_RADIO_INTERFACE_TD_SCDMA: case QMI_NAS_RADIO_INTERFACE_AMPS: case QMI_NAS_RADIO_INTERFACE_NONE: default: return MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN; } } /*****************************************************************************/ MMModemAccessTechnology mm_modem_access_technologies_from_qmi_radio_interface_array (GArray *radio_interfaces) { MMModemAccessTechnology access_technology = 0; guint i; for (i = 0; i < radio_interfaces->len; i++) { QmiNasRadioInterface iface; iface = g_array_index (radio_interfaces, QmiNasRadioInterface, i); access_technology |= mm_modem_access_technology_from_qmi_radio_interface (iface); } return access_technology; } /*****************************************************************************/ MMModemAccessTechnology mm_modem_access_technology_from_qmi_data_capability (QmiNasDataCapability cap) { switch (cap) { case QMI_NAS_DATA_CAPABILITY_GPRS: return MM_MODEM_ACCESS_TECHNOLOGY_GPRS; case QMI_NAS_DATA_CAPABILITY_EDGE: return MM_MODEM_ACCESS_TECHNOLOGY_EDGE; case QMI_NAS_DATA_CAPABILITY_HSDPA: return MM_MODEM_ACCESS_TECHNOLOGY_HSDPA; case QMI_NAS_DATA_CAPABILITY_HSUPA: return MM_MODEM_ACCESS_TECHNOLOGY_HSUPA; case QMI_NAS_DATA_CAPABILITY_WCDMA: return MM_MODEM_ACCESS_TECHNOLOGY_UMTS; case QMI_NAS_DATA_CAPABILITY_CDMA: return MM_MODEM_ACCESS_TECHNOLOGY_1XRTT; case QMI_NAS_DATA_CAPABILITY_EVDO_REV_0: return MM_MODEM_ACCESS_TECHNOLOGY_EVDO0; case QMI_NAS_DATA_CAPABILITY_EVDO_REV_A: return MM_MODEM_ACCESS_TECHNOLOGY_EVDOA; case QMI_NAS_DATA_CAPABILITY_GSM: return MM_MODEM_ACCESS_TECHNOLOGY_GSM; case QMI_NAS_DATA_CAPABILITY_EVDO_REV_B: return MM_MODEM_ACCESS_TECHNOLOGY_EVDOB; case QMI_NAS_DATA_CAPABILITY_LTE: return MM_MODEM_ACCESS_TECHNOLOGY_LTE; case QMI_NAS_DATA_CAPABILITY_HSDPA_PLUS: case QMI_NAS_DATA_CAPABILITY_DC_HSDPA_PLUS: return MM_MODEM_ACCESS_TECHNOLOGY_HSPA_PLUS; case QMI_NAS_DATA_CAPABILITY_NONE: default: return MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN; } } /*****************************************************************************/ MMModemAccessTechnology mm_modem_access_technologies_from_qmi_data_capability_array (GArray *data_capabilities) { MMModemAccessTechnology access_technology = 0; guint i; for (i = 0; i < data_capabilities->len; i++) { QmiNasDataCapability cap; cap = g_array_index (data_capabilities, QmiNasDataCapability, i); access_technology |= mm_modem_access_technology_from_qmi_data_capability (cap); } return access_technology; } /*****************************************************************************/ MMModemMode mm_modem_mode_from_qmi_nas_radio_interface (QmiNasRadioInterface iface) { switch (iface) { case QMI_NAS_RADIO_INTERFACE_CDMA_1X: case QMI_NAS_RADIO_INTERFACE_GSM: return MM_MODEM_MODE_2G; case QMI_NAS_RADIO_INTERFACE_CDMA_1XEVDO: case QMI_NAS_RADIO_INTERFACE_UMTS: return MM_MODEM_MODE_3G; case QMI_NAS_RADIO_INTERFACE_LTE: return MM_MODEM_MODE_4G; case QMI_NAS_RADIO_INTERFACE_5GNR: return MM_MODEM_MODE_5G; case QMI_NAS_RADIO_INTERFACE_NONE: case QMI_NAS_RADIO_INTERFACE_AMPS: case QMI_NAS_RADIO_INTERFACE_TD_SCDMA: case QMI_NAS_RADIO_INTERFACE_UNKNOWN: default: return MM_MODEM_MODE_NONE; } } /*****************************************************************************/ MMModemMode mm_modem_mode_from_qmi_radio_technology_preference (QmiNasRadioTechnologyPreference qmi) { MMModemMode mode = MM_MODEM_MODE_NONE; if (qmi & QMI_NAS_RADIO_TECHNOLOGY_PREFERENCE_3GPP2) { /* Ignore AMPS, we really don't report CS mode in QMI modems */ if (qmi & QMI_NAS_RADIO_TECHNOLOGY_PREFERENCE_CDMA_OR_WCDMA) mode |= MM_MODEM_MODE_2G; /* CDMA */ if (qmi & QMI_NAS_RADIO_TECHNOLOGY_PREFERENCE_HDR) mode |= MM_MODEM_MODE_3G; /* EV-DO */ } if (qmi & QMI_NAS_RADIO_TECHNOLOGY_PREFERENCE_3GPP) { if (qmi & QMI_NAS_RADIO_TECHNOLOGY_PREFERENCE_AMPS_OR_GSM) mode |= MM_MODEM_MODE_2G; /* GSM */ if (qmi & QMI_NAS_RADIO_TECHNOLOGY_PREFERENCE_CDMA_OR_WCDMA) mode |= MM_MODEM_MODE_3G; /* WCDMA */ } if (qmi & QMI_NAS_RADIO_TECHNOLOGY_PREFERENCE_LTE) mode |= MM_MODEM_MODE_4G; return mode; } QmiNasRadioTechnologyPreference mm_modem_mode_to_qmi_radio_technology_preference (MMModemMode mode, gboolean is_cdma) { QmiNasRadioTechnologyPreference pref = 0; if (is_cdma) { pref |= QMI_NAS_RADIO_TECHNOLOGY_PREFERENCE_3GPP2; if (mode & MM_MODEM_MODE_2G) pref |= QMI_NAS_RADIO_TECHNOLOGY_PREFERENCE_CDMA_OR_WCDMA; /* CDMA */ if (mode & MM_MODEM_MODE_3G) pref |= QMI_NAS_RADIO_TECHNOLOGY_PREFERENCE_HDR; /* EV-DO */ } else { pref |= QMI_NAS_RADIO_TECHNOLOGY_PREFERENCE_3GPP; if (mode & MM_MODEM_MODE_2G) pref |= QMI_NAS_RADIO_TECHNOLOGY_PREFERENCE_AMPS_OR_GSM; /* GSM */ if (mode & MM_MODEM_MODE_3G) pref |= QMI_NAS_RADIO_TECHNOLOGY_PREFERENCE_CDMA_OR_WCDMA; /* WCDMA */ } if (mode & MM_MODEM_MODE_4G) pref |= QMI_NAS_RADIO_TECHNOLOGY_PREFERENCE_LTE; return pref; } /*****************************************************************************/ MMModemMode mm_modem_mode_from_qmi_rat_mode_preference (QmiNasRatModePreference qmi) { MMModemMode mode = MM_MODEM_MODE_NONE; if (qmi & QMI_NAS_RAT_MODE_PREFERENCE_CDMA_1X) mode |= MM_MODEM_MODE_2G; if (qmi & QMI_NAS_RAT_MODE_PREFERENCE_CDMA_1XEVDO) mode |= MM_MODEM_MODE_3G; if (qmi & QMI_NAS_RAT_MODE_PREFERENCE_GSM) mode |= MM_MODEM_MODE_2G; if (qmi & QMI_NAS_RAT_MODE_PREFERENCE_UMTS) mode |= MM_MODEM_MODE_3G; if (qmi & QMI_NAS_RAT_MODE_PREFERENCE_LTE) mode |= MM_MODEM_MODE_4G; if (qmi & QMI_NAS_RAT_MODE_PREFERENCE_5GNR) mode |= MM_MODEM_MODE_5G; return mode; } QmiNasRatModePreference mm_modem_mode_to_qmi_rat_mode_preference (MMModemMode mode, gboolean is_cdma, gboolean is_3gpp) { QmiNasRatModePreference pref = 0; if (is_cdma) { if (mode & MM_MODEM_MODE_2G) pref |= QMI_NAS_RAT_MODE_PREFERENCE_CDMA_1X; if (mode & MM_MODEM_MODE_3G) pref |= QMI_NAS_RAT_MODE_PREFERENCE_CDMA_1XEVDO; } if (is_3gpp) { if (mode & MM_MODEM_MODE_2G) pref |= QMI_NAS_RAT_MODE_PREFERENCE_GSM; if (mode & MM_MODEM_MODE_3G) pref |= QMI_NAS_RAT_MODE_PREFERENCE_UMTS; if (mode & MM_MODEM_MODE_4G) pref |= QMI_NAS_RAT_MODE_PREFERENCE_LTE; if (mode & MM_MODEM_MODE_5G) pref |= QMI_NAS_RAT_MODE_PREFERENCE_5GNR; } return pref; } /*****************************************************************************/ MMModemCapability mm_modem_capability_from_qmi_rat_mode_preference (QmiNasRatModePreference qmi) { MMModemCapability caps = MM_MODEM_CAPABILITY_NONE; if (qmi & QMI_NAS_RAT_MODE_PREFERENCE_CDMA_1X) caps |= MM_MODEM_CAPABILITY_CDMA_EVDO; if (qmi & QMI_NAS_RAT_MODE_PREFERENCE_CDMA_1XEVDO) caps |= MM_MODEM_CAPABILITY_CDMA_EVDO; if (qmi & QMI_NAS_RAT_MODE_PREFERENCE_GSM) caps |= MM_MODEM_CAPABILITY_GSM_UMTS; if (qmi & QMI_NAS_RAT_MODE_PREFERENCE_UMTS) caps |= MM_MODEM_CAPABILITY_GSM_UMTS; if (qmi & QMI_NAS_RAT_MODE_PREFERENCE_LTE) caps |= MM_MODEM_CAPABILITY_LTE; if (qmi & QMI_NAS_RAT_MODE_PREFERENCE_5GNR) caps |= MM_MODEM_CAPABILITY_5GNR; return caps; } QmiNasRatModePreference mm_modem_capability_to_qmi_rat_mode_preference (MMModemCapability caps) { QmiNasRatModePreference qmi = 0; if (caps & MM_MODEM_CAPABILITY_CDMA_EVDO) { qmi |= QMI_NAS_RAT_MODE_PREFERENCE_CDMA_1X; qmi |= QMI_NAS_RAT_MODE_PREFERENCE_CDMA_1XEVDO; } if (caps & MM_MODEM_CAPABILITY_GSM_UMTS) { qmi |= QMI_NAS_RAT_MODE_PREFERENCE_GSM; qmi |= QMI_NAS_RAT_MODE_PREFERENCE_UMTS; } if (caps & MM_MODEM_CAPABILITY_LTE) qmi |= QMI_NAS_RAT_MODE_PREFERENCE_LTE; if (caps & MM_MODEM_CAPABILITY_5GNR) qmi |= QMI_NAS_RAT_MODE_PREFERENCE_5GNR; return qmi; } /*****************************************************************************/ GArray * mm_modem_capability_to_qmi_acquisition_order_preference (MMModemCapability caps) { GArray *array; QmiNasRadioInterface value; array = g_array_new (FALSE, FALSE, sizeof (QmiNasRadioInterface)); if (caps & MM_MODEM_CAPABILITY_5GNR) { value = QMI_NAS_RADIO_INTERFACE_5GNR; g_array_append_val (array, value); } if (caps & MM_MODEM_CAPABILITY_LTE) { value = QMI_NAS_RADIO_INTERFACE_LTE; g_array_append_val (array, value); } if (caps & MM_MODEM_CAPABILITY_GSM_UMTS) { value = QMI_NAS_RADIO_INTERFACE_UMTS; g_array_append_val (array, value); value = QMI_NAS_RADIO_INTERFACE_GSM; g_array_append_val (array, value); } if (caps & MM_MODEM_CAPABILITY_CDMA_EVDO) { value = QMI_NAS_RADIO_INTERFACE_CDMA_1XEVDO; g_array_append_val (array, value); value = QMI_NAS_RADIO_INTERFACE_CDMA_1X; g_array_append_val (array, value); } return array; } static gboolean radio_interface_array_contains (GArray *array, QmiNasRadioInterface act) { guint i; for (i = 0; i < array->len; i++) { QmiNasRadioInterface value; value = g_array_index (array, QmiNasRadioInterface, i); if (value == act) return TRUE; } return FALSE; } static void radio_interface_array_add_missing (GArray *array, GArray *all) { guint i; for (i = 0; i < all->len; i++) { QmiNasRadioInterface value; value = g_array_index (all, QmiNasRadioInterface, i); if (!radio_interface_array_contains (array, value)) g_array_append_val (array, value); } } GArray * mm_modem_mode_to_qmi_acquisition_order_preference (MMModemMode allowed, MMModemMode preferred, GArray *all) { GArray *array; QmiNasRadioInterface preferred_radio = QMI_NAS_RADIO_INTERFACE_UNKNOWN; QmiNasRadioInterface value; array = g_array_sized_new (FALSE, FALSE, sizeof (QmiNasRadioInterface), all->len); #define PROCESS_ALLOWED_PREFERRED_MODE(MODE,RADIO) \ if ((allowed & MODE) && (radio_interface_array_contains (all, RADIO))) { \ if ((preferred == MODE) && (preferred_radio == QMI_NAS_RADIO_INTERFACE_UNKNOWN)) \ preferred_radio = RADIO; \ else { \ value = RADIO; \ g_array_append_val (array, value); \ } \ } PROCESS_ALLOWED_PREFERRED_MODE (MM_MODEM_MODE_5G, QMI_NAS_RADIO_INTERFACE_5GNR); PROCESS_ALLOWED_PREFERRED_MODE (MM_MODEM_MODE_4G, QMI_NAS_RADIO_INTERFACE_LTE); PROCESS_ALLOWED_PREFERRED_MODE (MM_MODEM_MODE_3G, QMI_NAS_RADIO_INTERFACE_UMTS); PROCESS_ALLOWED_PREFERRED_MODE (MM_MODEM_MODE_3G, QMI_NAS_RADIO_INTERFACE_CDMA_1XEVDO); PROCESS_ALLOWED_PREFERRED_MODE (MM_MODEM_MODE_2G, QMI_NAS_RADIO_INTERFACE_GSM); PROCESS_ALLOWED_PREFERRED_MODE (MM_MODEM_MODE_2G, QMI_NAS_RADIO_INTERFACE_CDMA_1X); #undef PROCESS_ALLOWED_PREFERRED_MODE if (preferred_radio != QMI_NAS_RADIO_INTERFACE_UNKNOWN) g_array_prepend_val (array, preferred_radio); /* the acquisition order preference is a TLV that must ALWAYS contain the * same list of QmiNasRadioInterface values, just with a different order. */ radio_interface_array_add_missing (array, all); g_assert_cmpuint (array->len, ==, all->len); return array; } /*****************************************************************************/ MMModemCapability mm_modem_capability_from_qmi_radio_technology_preference (QmiNasRadioTechnologyPreference qmi) { MMModemCapability caps = MM_MODEM_CAPABILITY_NONE; if (qmi & QMI_NAS_RADIO_TECHNOLOGY_PREFERENCE_3GPP2) { /* Skip AMPS */ if (qmi & QMI_NAS_RADIO_TECHNOLOGY_PREFERENCE_CDMA_OR_WCDMA) caps |= MM_MODEM_CAPABILITY_CDMA_EVDO; /* CDMA */ if (qmi & QMI_NAS_RADIO_TECHNOLOGY_PREFERENCE_HDR) caps |= MM_MODEM_CAPABILITY_CDMA_EVDO; /* EV-DO */ } if (qmi & QMI_NAS_RADIO_TECHNOLOGY_PREFERENCE_3GPP) { if (qmi & QMI_NAS_RADIO_TECHNOLOGY_PREFERENCE_AMPS_OR_GSM) caps |= MM_MODEM_CAPABILITY_GSM_UMTS; /* GSM */ if (qmi & QMI_NAS_RADIO_TECHNOLOGY_PREFERENCE_CDMA_OR_WCDMA) caps |= MM_MODEM_CAPABILITY_GSM_UMTS; /* WCDMA */ } if (qmi & QMI_NAS_RADIO_TECHNOLOGY_PREFERENCE_LTE) caps |= MM_MODEM_CAPABILITY_LTE; /* NOTE: no 5GNR defined in Technology Preference */ return caps; } QmiNasRadioTechnologyPreference mm_modem_capability_to_qmi_radio_technology_preference (MMModemCapability caps) { QmiNasRadioTechnologyPreference qmi = 0; if (caps & MM_MODEM_CAPABILITY_GSM_UMTS) { qmi |= QMI_NAS_RADIO_TECHNOLOGY_PREFERENCE_3GPP; qmi |= QMI_NAS_RADIO_TECHNOLOGY_PREFERENCE_AMPS_OR_GSM; qmi |= QMI_NAS_RADIO_TECHNOLOGY_PREFERENCE_CDMA_OR_WCDMA; } if (caps & MM_MODEM_CAPABILITY_CDMA_EVDO) { qmi |= QMI_NAS_RADIO_TECHNOLOGY_PREFERENCE_3GPP2; qmi |= QMI_NAS_RADIO_TECHNOLOGY_PREFERENCE_CDMA_OR_WCDMA; qmi |= QMI_NAS_RADIO_TECHNOLOGY_PREFERENCE_HDR; } if (caps & MM_MODEM_CAPABILITY_LTE) qmi |= QMI_NAS_RADIO_TECHNOLOGY_PREFERENCE_LTE; return qmi; } /*****************************************************************************/ #define ALL_3GPP2_BANDS \ (QMI_NAS_BAND_PREFERENCE_BC_0_A_SYSTEM | \ QMI_NAS_BAND_PREFERENCE_BC_0_B_SYSTEM | \ QMI_NAS_BAND_PREFERENCE_BC_1_ALL_BLOCKS | \ QMI_NAS_BAND_PREFERENCE_BC_2 | \ QMI_NAS_BAND_PREFERENCE_BC_3_A_SYSTEM | \ QMI_NAS_BAND_PREFERENCE_BC_4_ALL_BLOCKS | \ QMI_NAS_BAND_PREFERENCE_BC_5_ALL_BLOCKS | \ QMI_NAS_BAND_PREFERENCE_BC_6 | \ QMI_NAS_BAND_PREFERENCE_BC_7 | \ QMI_NAS_BAND_PREFERENCE_BC_8 | \ QMI_NAS_BAND_PREFERENCE_BC_9 | \ QMI_NAS_BAND_PREFERENCE_BC_10 | \ QMI_NAS_BAND_PREFERENCE_BC_11 | \ QMI_NAS_BAND_PREFERENCE_BC_12 | \ QMI_NAS_BAND_PREFERENCE_BC_14 | \ QMI_NAS_BAND_PREFERENCE_BC_15 | \ QMI_NAS_BAND_PREFERENCE_BC_16 | \ QMI_NAS_BAND_PREFERENCE_BC_17 | \ QMI_NAS_BAND_PREFERENCE_BC_18 | \ QMI_NAS_BAND_PREFERENCE_BC_19) #define ALL_3GPP_BANDS \ (QMI_NAS_BAND_PREFERENCE_GSM_DCS_1800 | \ QMI_NAS_BAND_PREFERENCE_GSM_900_EXTENDED | \ QMI_NAS_BAND_PREFERENCE_GSM_900_PRIMARY | \ QMI_NAS_BAND_PREFERENCE_GSM_450 | \ QMI_NAS_BAND_PREFERENCE_GSM_480 | \ QMI_NAS_BAND_PREFERENCE_GSM_750 | \ QMI_NAS_BAND_PREFERENCE_GSM_850 | \ QMI_NAS_BAND_PREFERENCE_GSM_900_RAILWAYS | \ QMI_NAS_BAND_PREFERENCE_GSM_PCS_1900 | \ QMI_NAS_BAND_PREFERENCE_WCDMA_2100 | \ QMI_NAS_BAND_PREFERENCE_WCDMA_PCS_1900 | \ QMI_NAS_BAND_PREFERENCE_WCDMA_DCS_1800 | \ QMI_NAS_BAND_PREFERENCE_WCDMA_1700_US | \ QMI_NAS_BAND_PREFERENCE_WCDMA_850_US | \ QMI_NAS_BAND_PREFERENCE_WCDMA_800 | \ QMI_NAS_BAND_PREFERENCE_WCDMA_2600 | \ QMI_NAS_BAND_PREFERENCE_WCDMA_900 | \ QMI_NAS_BAND_PREFERENCE_WCDMA_1700_JAPAN) MMModemCapability mm_modem_capability_from_qmi_band_preference (QmiNasBandPreference qmi) { MMModemCapability caps = MM_MODEM_CAPABILITY_NONE; if (qmi & ALL_3GPP_BANDS) caps |= MM_MODEM_CAPABILITY_GSM_UMTS; if (qmi & ALL_3GPP2_BANDS) caps |= MM_MODEM_CAPABILITY_CDMA_EVDO; return caps; } /*****************************************************************************/ MMModemMode mm_modem_mode_from_qmi_gsm_wcdma_acquisition_order_preference (QmiNasGsmWcdmaAcquisitionOrderPreference qmi, gpointer log_object) { switch (qmi) { case QMI_NAS_GSM_WCDMA_ACQUISITION_ORDER_PREFERENCE_AUTOMATIC: return MM_MODEM_MODE_NONE; case QMI_NAS_GSM_WCDMA_ACQUISITION_ORDER_PREFERENCE_GSM: return MM_MODEM_MODE_2G; case QMI_NAS_GSM_WCDMA_ACQUISITION_ORDER_PREFERENCE_WCDMA: return MM_MODEM_MODE_3G; default: mm_obj_dbg (log_object, "unknown acquisition order preference: '%s'", qmi_nas_gsm_wcdma_acquisition_order_preference_get_string (qmi)); return MM_MODEM_MODE_NONE; } } QmiNasGsmWcdmaAcquisitionOrderPreference mm_modem_mode_to_qmi_gsm_wcdma_acquisition_order_preference (MMModemMode mode, gpointer log_object) { g_autofree gchar *str = NULL; /* mode is not a mask in this case, only a value */ switch (mode) { case MM_MODEM_MODE_3G: return QMI_NAS_GSM_WCDMA_ACQUISITION_ORDER_PREFERENCE_WCDMA; case MM_MODEM_MODE_2G: return QMI_NAS_GSM_WCDMA_ACQUISITION_ORDER_PREFERENCE_GSM; case MM_MODEM_MODE_NONE: return QMI_NAS_GSM_WCDMA_ACQUISITION_ORDER_PREFERENCE_AUTOMATIC; case MM_MODEM_MODE_CS: case MM_MODEM_MODE_4G: case MM_MODEM_MODE_5G: case MM_MODEM_MODE_ANY: default: break; } str = mm_modem_mode_build_string_from_mask (mode); mm_obj_dbg (log_object, "unhandled modem mode: '%s'", str); return QMI_NAS_GSM_WCDMA_ACQUISITION_ORDER_PREFERENCE_AUTOMATIC; } /*****************************************************************************/ MMModem3gppRegistrationState mm_modem_3gpp_registration_state_from_qmi_registration_state (QmiNasAttachState attach_state, QmiNasRegistrationState registration_state, gboolean roaming) { if (attach_state == QMI_NAS_ATTACH_STATE_UNKNOWN) return MM_MODEM_3GPP_REGISTRATION_STATE_UNKNOWN; if (attach_state == QMI_NAS_ATTACH_STATE_DETACHED) return MM_MODEM_3GPP_REGISTRATION_STATE_IDLE; /* attached */ switch (registration_state) { case QMI_NAS_REGISTRATION_STATE_NOT_REGISTERED: return MM_MODEM_3GPP_REGISTRATION_STATE_IDLE; case QMI_NAS_REGISTRATION_STATE_REGISTERED: return (roaming ? MM_MODEM_3GPP_REGISTRATION_STATE_ROAMING : MM_MODEM_3GPP_REGISTRATION_STATE_HOME); case QMI_NAS_REGISTRATION_STATE_NOT_REGISTERED_SEARCHING: return MM_MODEM_3GPP_REGISTRATION_STATE_SEARCHING; case QMI_NAS_REGISTRATION_STATE_REGISTRATION_DENIED: return MM_MODEM_3GPP_REGISTRATION_STATE_DENIED; case QMI_NAS_REGISTRATION_STATE_UNKNOWN: default: return MM_MODEM_3GPP_REGISTRATION_STATE_UNKNOWN; } } /*****************************************************************************/ MMModemCdmaRegistrationState mm_modem_cdma_registration_state_from_qmi_registration_state (QmiNasRegistrationState registration_state) { switch (registration_state) { case QMI_NAS_REGISTRATION_STATE_REGISTERED: return MM_MODEM_CDMA_REGISTRATION_STATE_REGISTERED; case QMI_NAS_REGISTRATION_STATE_NOT_REGISTERED: case QMI_NAS_REGISTRATION_STATE_NOT_REGISTERED_SEARCHING: case QMI_NAS_REGISTRATION_STATE_REGISTRATION_DENIED: case QMI_NAS_REGISTRATION_STATE_UNKNOWN: default: return MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN; } } /*****************************************************************************/ MMModemCdmaActivationState mm_modem_cdma_activation_state_from_qmi_activation_state (QmiDmsActivationState state) { switch (state) { case QMI_DMS_ACTIVATION_STATE_NOT_ACTIVATED: return MM_MODEM_CDMA_ACTIVATION_STATE_NOT_ACTIVATED; case QMI_DMS_ACTIVATION_STATE_ACTIVATED: return MM_MODEM_CDMA_ACTIVATION_STATE_ACTIVATED; case QMI_DMS_ACTIVATION_STATE_CONNECTING: case QMI_DMS_ACTIVATION_STATE_CONNECTED: case QMI_DMS_ACTIVATION_STATE_OTASP_AUTHENTICATED: case QMI_DMS_ACTIVATION_STATE_OTASP_NAM: case QMI_DMS_ACTIVATION_STATE_OTASP_MDN: case QMI_DMS_ACTIVATION_STATE_OTASP_IMSI: case QMI_DMS_ACTIVATION_STATE_OTASP_PRL: case QMI_DMS_ACTIVATION_STATE_OTASP_SPC: return MM_MODEM_CDMA_ACTIVATION_STATE_ACTIVATING; case QMI_DMS_ACTIVATION_STATE_OTASP_COMMITED: return MM_MODEM_CDMA_ACTIVATION_STATE_PARTIALLY_ACTIVATED; default: return MM_MODEM_CDMA_ACTIVATION_STATE_UNKNOWN; } } /*****************************************************************************/ static void process_common_info (const gchar *info_name, QmiNasServiceStatus service_status, gboolean domain_valid, QmiNasNetworkServiceDomain domain, gboolean roaming_status_valid, QmiNasRoamingStatus roaming_status, gboolean forbidden_valid, gboolean forbidden, gboolean lac_valid, guint16 lac, gboolean tac_valid, guint16 tac, gboolean cid_valid, guint32 cid, gboolean network_id_valid, const gchar *mcc, const gchar *mnc, MMModem3gppRegistrationState *out_cs_registration_state, MMModem3gppRegistrationState *out_ps_registration_state, guint16 *out_lac, guint16 *out_tac, guint32 *out_cid, gchar **out_operator_id, gpointer log_object) { #define SET_OUTPUT(OUT, VAL) do { \ if (OUT) \ *(OUT) = (VAL); \ } while (0) /* If power save service status reported the modem is not actively searching * for a network, therefore "idle". */ if (service_status == QMI_NAS_SERVICE_STATUS_POWER_SAVE) { SET_OUTPUT (out_cs_registration_state, MM_MODEM_3GPP_REGISTRATION_STATE_IDLE); SET_OUTPUT (out_ps_registration_state, MM_MODEM_3GPP_REGISTRATION_STATE_IDLE); return; } /* If no service status reported, the modem is actively searching for a * network, therefore "searching". */ if (service_status == QMI_NAS_SERVICE_STATUS_NONE) { SET_OUTPUT (out_cs_registration_state, MM_MODEM_3GPP_REGISTRATION_STATE_SEARCHING); SET_OUTPUT (out_ps_registration_state, MM_MODEM_3GPP_REGISTRATION_STATE_SEARCHING); return; } /* When forbidden, the service status is usually reported as 'limited' or * 'limited-regional', e.g. allowing only emergency calls. If the * forbidden flag is explicitly reported, we report 'denied', otherwise * 'emergency-services-only' */ if (forbidden_valid && forbidden) { SET_OUTPUT (out_cs_registration_state, MM_MODEM_3GPP_REGISTRATION_STATE_DENIED); SET_OUTPUT (out_ps_registration_state, MM_MODEM_3GPP_REGISTRATION_STATE_DENIED); } else if (service_status == QMI_NAS_SERVICE_STATUS_LIMITED || service_status == QMI_NAS_SERVICE_STATUS_LIMITED_REGIONAL) { SET_OUTPUT (out_cs_registration_state, MM_MODEM_3GPP_REGISTRATION_STATE_EMERGENCY_ONLY); SET_OUTPUT (out_ps_registration_state, MM_MODEM_3GPP_REGISTRATION_STATE_EMERGENCY_ONLY); } /* On a successful registration (either home or roaming) we require a valid * domain and "available" service status */ else if (domain_valid && domain != QMI_NAS_NETWORK_SERVICE_DOMAIN_UNKNOWN && domain != QMI_NAS_NETWORK_SERVICE_DOMAIN_NONE && service_status == QMI_NAS_SERVICE_STATUS_AVAILABLE) { MMModem3gppRegistrationState tmp_registration_state; if (roaming_status_valid && roaming_status == QMI_NAS_ROAMING_STATUS_ON) tmp_registration_state = MM_MODEM_3GPP_REGISTRATION_STATE_ROAMING; else tmp_registration_state = MM_MODEM_3GPP_REGISTRATION_STATE_HOME; if (domain == QMI_NAS_NETWORK_SERVICE_DOMAIN_CS) { SET_OUTPUT (out_cs_registration_state, tmp_registration_state); SET_OUTPUT (out_ps_registration_state, MM_MODEM_3GPP_REGISTRATION_STATE_IDLE); } else if (domain == QMI_NAS_NETWORK_SERVICE_DOMAIN_PS) { SET_OUTPUT (out_cs_registration_state, MM_MODEM_3GPP_REGISTRATION_STATE_IDLE); SET_OUTPUT (out_ps_registration_state, tmp_registration_state); } else if (domain == QMI_NAS_NETWORK_SERVICE_DOMAIN_CS_PS) { SET_OUTPUT (out_cs_registration_state, tmp_registration_state); SET_OUTPUT (out_ps_registration_state, tmp_registration_state); } /* If we're registered either at home or roaming, try to get LAC/CID */ if (lac_valid) SET_OUTPUT (out_lac, lac); if (tac_valid) SET_OUTPUT (out_tac, tac); if (cid_valid) SET_OUTPUT (out_cid, cid); } else { /* Issue a warning in case this ever happens and this logic needs to be updated */ mm_obj_warn (log_object, "unexpected %s system info: domain valid %s, domain %s, service status %s", info_name, domain_valid ? "yes" : "no", qmi_nas_network_service_domain_get_string (domain), qmi_nas_service_status_get_string (service_status)); return; } /* Network ID report also in the case of registration denial */ if (network_id_valid) { *out_operator_id = g_malloc0 (7); memcpy (*out_operator_id, mcc, 3); if ((guint8)mnc[2] == 0xFF) memcpy (&((*out_operator_id)[3]), mnc, 2); else memcpy (&((*out_operator_id)[3]), mnc, 3); } } static void process_gsm_info (QmiMessageNasGetSystemInfoOutput *response_output, QmiIndicationNasSystemInfoOutput *indication_output, MMModem3gppRegistrationState *out_cs_registration_state, MMModem3gppRegistrationState *out_ps_registration_state, guint16 *out_lac, guint32 *out_cid, gchar **out_operator_id, gpointer log_object) { QmiNasServiceStatus service_status = QMI_NAS_SERVICE_STATUS_NONE; gboolean domain_valid = FALSE; QmiNasNetworkServiceDomain domain = QMI_NAS_NETWORK_SERVICE_DOMAIN_UNKNOWN; gboolean roaming_status_valid = FALSE; QmiNasRoamingStatus roaming_status = QMI_NAS_ROAMING_STATUS_OFF; gboolean forbidden_valid = FALSE; gboolean forbidden = FALSE; gboolean lac_valid = FALSE; guint16 lac = 0; gboolean cid_valid = FALSE; guint32 cid = 0; gboolean network_id_valid = FALSE; const gchar *mcc = NULL; const gchar *mnc = NULL; if (response_output) { /* if service status info not given, ACT is unsupported */ if (!qmi_message_nas_get_system_info_output_get_gsm_service_status ( response_output, &service_status, NULL, /* true_service_status */ NULL, /* preferred_data_path */ NULL)) return; qmi_message_nas_get_system_info_output_get_gsm_system_info_v2 ( response_output, &domain_valid, &domain, NULL, NULL, /* service_capability */ &roaming_status_valid, &roaming_status, &forbidden_valid, &forbidden, &lac_valid, &lac, &cid_valid, &cid, NULL, NULL, NULL, /* registration_reject_info */ &network_id_valid, &mcc, &mnc, NULL, NULL, /* egprs support */ NULL, NULL, /* dtm_support */ NULL); } else if (indication_output) { /* if service status info not given, ACT is unsupported */ if (!qmi_indication_nas_system_info_output_get_gsm_service_status ( indication_output, &service_status, NULL, /* true_service_status */ NULL, /* preferred_data_path */ NULL)) return; qmi_indication_nas_system_info_output_get_gsm_system_info_v2 ( indication_output, &domain_valid, &domain, NULL, NULL, /* service_capability */ &roaming_status_valid, &roaming_status, &forbidden_valid, &forbidden, &lac_valid, &lac, &cid_valid, &cid, NULL, NULL, NULL, /* registration_reject_info */ &network_id_valid, &mcc, &mnc, NULL, NULL, /* egprs support */ NULL, NULL, /* dtm_support */ NULL); } else g_assert_not_reached (); process_common_info ("GSM", service_status, domain_valid, domain, roaming_status_valid, roaming_status, forbidden_valid, forbidden, lac_valid, lac, FALSE, 0, cid_valid, cid, network_id_valid, mcc, mnc, out_cs_registration_state, out_ps_registration_state, out_lac, NULL, /* out_tac */ out_cid, out_operator_id, log_object); } static void process_wcdma_info (QmiMessageNasGetSystemInfoOutput *response_output, QmiIndicationNasSystemInfoOutput *indication_output, MMModem3gppRegistrationState *out_cs_registration_state, MMModem3gppRegistrationState *out_ps_registration_state, guint16 *out_lac, guint32 *out_cid, gchar **out_operator_id, gpointer log_object) { QmiNasServiceStatus service_status = QMI_NAS_SERVICE_STATUS_NONE; gboolean domain_valid = FALSE; QmiNasNetworkServiceDomain domain = QMI_NAS_NETWORK_SERVICE_DOMAIN_UNKNOWN; gboolean roaming_status_valid = FALSE; QmiNasRoamingStatus roaming_status = QMI_NAS_ROAMING_STATUS_OFF; gboolean forbidden_valid = FALSE; gboolean forbidden = FALSE; gboolean lac_valid = FALSE; guint16 lac = 0; gboolean cid_valid = FALSE; guint32 cid = 0; gboolean network_id_valid = FALSE; const gchar *mcc = NULL; const gchar *mnc = NULL; if (response_output) { /* if service status info not given, ACT is unsupported */ if (!qmi_message_nas_get_system_info_output_get_wcdma_service_status ( response_output, &service_status, NULL, /* true_service_status */ NULL, /* preferred_data_path */ NULL)) return; qmi_message_nas_get_system_info_output_get_wcdma_system_info_v2 ( response_output, &domain_valid, &domain, NULL, NULL, /* service_capability */ &roaming_status_valid, &roaming_status, &forbidden_valid, &forbidden, &lac_valid, &lac, &cid_valid, &cid, NULL, NULL, NULL, /* registration_reject_info */ &network_id_valid, &mcc, &mnc, NULL, NULL, /* hs_call_status */ NULL, NULL, /* hs_service */ NULL, NULL, /* primary_scrambling_code */ NULL); } else if (indication_output) { /* if service status info not given, ACT is unsupported */ if (!qmi_indication_nas_system_info_output_get_wcdma_service_status ( indication_output, &service_status, NULL, /* true_service_status */ NULL, /* preferred_data_path */ NULL)) return; qmi_indication_nas_system_info_output_get_wcdma_system_info_v2 ( indication_output, &domain_valid, &domain, NULL, NULL, /* service_capability */ &roaming_status_valid, &roaming_status, &forbidden_valid, &forbidden, &lac_valid, &lac, &cid_valid, &cid, NULL, NULL, NULL, /* registration_reject_info */ &network_id_valid, &mcc, &mnc, NULL, NULL, /* hs_call_status */ NULL, NULL, /* hs_service */ NULL, NULL, /* primary_scrambling_code */ NULL); } else g_assert_not_reached (); process_common_info ("WCDMA", service_status, domain_valid, domain, roaming_status_valid, roaming_status, forbidden_valid, forbidden, lac_valid, lac, FALSE, 0, cid_valid, cid, network_id_valid, mcc, mnc, out_cs_registration_state, out_ps_registration_state, out_lac, NULL, /* out_tac */ out_cid, out_operator_id, log_object); } static void process_lte_info (QmiMessageNasGetSystemInfoOutput *response_output, QmiIndicationNasSystemInfoOutput *indication_output, MMModem3gppRegistrationState *out_eps_registration_state, guint16 *out_tac, guint32 *out_cid, gchar **out_operator_id, gboolean *out_endc_available, gpointer log_object) { QmiNasServiceStatus service_status = QMI_NAS_SERVICE_STATUS_NONE; gboolean domain_valid = FALSE; QmiNasNetworkServiceDomain domain = QMI_NAS_NETWORK_SERVICE_DOMAIN_UNKNOWN; gboolean roaming_status_valid = FALSE; QmiNasRoamingStatus roaming_status = QMI_NAS_ROAMING_STATUS_OFF; gboolean forbidden_valid = FALSE; gboolean forbidden = FALSE; gboolean tac_valid = FALSE; guint16 tac = 0; gboolean cid_valid = FALSE; guint32 cid = 0; gboolean network_id_valid = FALSE; const gchar *mcc = NULL; const gchar *mnc = NULL; if (response_output) { /* if service status info not given, ACT is unsupported */ if (!qmi_message_nas_get_system_info_output_get_lte_service_status ( response_output, &service_status, NULL, /* true_service_status */ NULL, /* preferred_data_path */ NULL)) return; qmi_message_nas_get_system_info_output_get_lte_system_info_v2 ( response_output, &domain_valid, &domain, NULL, NULL, /* service_capability */ &roaming_status_valid, &roaming_status, &forbidden_valid, &forbidden, NULL, NULL, /* lac */ &cid_valid, &cid, NULL, NULL, NULL, /* registration_reject_info */ &network_id_valid, &mcc, &mnc, &tac_valid, &tac, NULL); qmi_message_nas_get_system_info_output_get_eutra_with_nr5g_availability ( response_output, out_endc_available, NULL); } else if (indication_output) { /* if service status info not given, ACT is unsupported */ if (!qmi_indication_nas_system_info_output_get_lte_service_status ( indication_output, &service_status, NULL, /* true_service_status */ NULL, /* preferred_data_path */ NULL)) return; qmi_indication_nas_system_info_output_get_lte_system_info_v2 ( indication_output, &domain_valid, &domain, NULL, NULL, /* service_capability */ &roaming_status_valid, &roaming_status, &forbidden_valid, &forbidden, NULL, NULL, /* lac */ &cid_valid, &cid, NULL, NULL, NULL, /* registration_reject_info */ &network_id_valid, &mcc, &mnc, &tac_valid, &tac, NULL); qmi_indication_nas_system_info_output_get_eutra_with_nr5g_availability ( indication_output, out_endc_available, NULL); } else g_assert_not_reached (); process_common_info ("LTE", service_status, domain_valid, domain, roaming_status_valid, roaming_status, forbidden_valid, forbidden, FALSE, 0, tac_valid, tac, cid_valid, cid, network_id_valid, mcc, mnc, NULL, /* out_cs_registration_state */ out_eps_registration_state, NULL, /* out_lac */ out_tac, out_cid, out_operator_id, log_object); } static void process_nr5g_info (QmiMessageNasGetSystemInfoOutput *response_output, QmiIndicationNasSystemInfoOutput *indication_output, MMModem3gppRegistrationState *out_5gs_registration_state, guint16 *out_tac, guint32 *out_cid, gchar **out_operator_id, gpointer log_object) { QmiNasServiceStatus service_status = QMI_NAS_SERVICE_STATUS_NONE; gboolean domain_valid = FALSE; QmiNasNetworkServiceDomain domain = QMI_NAS_NETWORK_SERVICE_DOMAIN_UNKNOWN; gboolean roaming_status_valid = FALSE; QmiNasRoamingStatus roaming_status = QMI_NAS_ROAMING_STATUS_OFF; gboolean forbidden_valid = FALSE; gboolean forbidden = FALSE; gboolean tac_valid = FALSE; guint16 tac = 0; gboolean cid_valid = FALSE; guint32 cid = 0; gboolean network_id_valid = FALSE; const gchar *mcc = NULL; const gchar *mnc = NULL; if (response_output) { /* if service status info not given, ACT is unsupported */ if (!qmi_message_nas_get_system_info_output_get_nr5g_service_status_info ( response_output, &service_status, NULL, /* true_service_status */ NULL, /* preferred_data_path */ NULL)) return; qmi_message_nas_get_system_info_output_get_nr5g_system_info ( response_output, &domain_valid, &domain, NULL, NULL, /* service_capability */ &roaming_status_valid, &roaming_status, &forbidden_valid, &forbidden, NULL, NULL, /* lac */ &cid_valid, &cid, NULL, NULL, NULL, /* registration_reject_info */ &network_id_valid, &mcc, &mnc, &tac_valid, &tac, NULL); } else if (indication_output) { /* if service status info not given, ACT is unsupported */ if (!qmi_indication_nas_system_info_output_get_nr5g_service_status_info ( indication_output, &service_status, NULL, /* true_service_status */ NULL, /* preferred_data_path */ NULL)) return; qmi_indication_nas_system_info_output_get_nr5g_system_info ( indication_output, &domain_valid, &domain, NULL, NULL, /* service_capability */ &roaming_status_valid, &roaming_status, &forbidden_valid, &forbidden, NULL, NULL, /* lac */ &cid_valid, &cid, NULL, NULL, NULL, /* registration_reject_info */ &network_id_valid, &mcc, &mnc, &tac_valid, &tac, NULL); } else g_assert_not_reached (); process_common_info ("NR5G", service_status, domain_valid, domain, roaming_status_valid, roaming_status, forbidden_valid, forbidden, FALSE, 0, tac_valid, tac, cid_valid, cid, network_id_valid, mcc, mnc, NULL, /* cs_registration_state */ out_5gs_registration_state, NULL, /* out_lac */ out_tac, out_cid, out_operator_id, log_object); } static MMModem3gppRegistrationState unregistered_state_fallback (MMModem3gppRegistrationState umts_state, MMModem3gppRegistrationState gsm_state) { /* For 3G and 2G unregistered states, we will select SEARCHING if one of them * reports it, otherwise DENIED if one of the reports it, and otherwise * the 3G one if not unknown and finally the 2G one otherwise. */ if (umts_state == MM_MODEM_3GPP_REGISTRATION_STATE_SEARCHING || gsm_state == MM_MODEM_3GPP_REGISTRATION_STATE_SEARCHING) return MM_MODEM_3GPP_REGISTRATION_STATE_SEARCHING; if (umts_state == MM_MODEM_3GPP_REGISTRATION_STATE_DENIED || gsm_state == MM_MODEM_3GPP_REGISTRATION_STATE_DENIED) return MM_MODEM_3GPP_REGISTRATION_STATE_DENIED; if (umts_state != MM_MODEM_3GPP_REGISTRATION_STATE_UNKNOWN) return umts_state; return gsm_state; } void mm_modem_registration_state_from_qmi_system_info (QmiMessageNasGetSystemInfoOutput *response_output, QmiIndicationNasSystemInfoOutput *indication_output, MMModem3gppRegistrationState *out_cs_registration_state, MMModem3gppRegistrationState *out_ps_registration_state, MMModem3gppRegistrationState *out_eps_registration_state, MMModem3gppRegistrationState *out_5gs_registration_state, guint16 *out_lac, guint16 *out_tac, guint32 *out_cid, gchar **out_operator_id, MMModemAccessTechnology *out_act, gpointer log_object) { MMModem3gppRegistrationState gsm_cs_registration_state = MM_MODEM_3GPP_REGISTRATION_STATE_UNKNOWN; MMModem3gppRegistrationState gsm_ps_registration_state = MM_MODEM_3GPP_REGISTRATION_STATE_UNKNOWN; MMModem3gppRegistrationState wcdma_cs_registration_state = MM_MODEM_3GPP_REGISTRATION_STATE_UNKNOWN; MMModem3gppRegistrationState wcdma_ps_registration_state = MM_MODEM_3GPP_REGISTRATION_STATE_UNKNOWN; MMModem3gppRegistrationState lte_eps_registration_state = MM_MODEM_3GPP_REGISTRATION_STATE_UNKNOWN; MMModem3gppRegistrationState nr5g_5gs_registration_state = MM_MODEM_3GPP_REGISTRATION_STATE_UNKNOWN; g_autofree gchar *gsm_operator_id = NULL; g_autofree gchar *wcdma_operator_id = NULL; g_autofree gchar *lte_operator_id = NULL; g_autofree gchar *nr5g_operator_id = NULL; guint16 gsm_lac = 0; guint32 gsm_cid = 0; guint16 wcdma_lac = 0; guint32 wcdma_cid = 0; guint16 lte_tac = 0; guint32 lte_cid = 0; guint16 nr5g_tac = 0; guint32 nr5g_cid = 0; gboolean endc_available = FALSE; gboolean reg_info_set = FALSE; /* Reset outputs */ *out_cs_registration_state = MM_MODEM_3GPP_REGISTRATION_STATE_UNKNOWN; *out_ps_registration_state = MM_MODEM_3GPP_REGISTRATION_STATE_UNKNOWN; *out_eps_registration_state = MM_MODEM_3GPP_REGISTRATION_STATE_UNKNOWN; *out_5gs_registration_state = MM_MODEM_3GPP_REGISTRATION_STATE_UNKNOWN; *out_lac = 0; *out_tac = 0; *out_cid = 0; *out_operator_id = NULL; *out_act = MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN; process_nr5g_info (response_output, indication_output, &nr5g_5gs_registration_state, &nr5g_tac, &nr5g_cid, &nr5g_operator_id, log_object); process_lte_info (response_output, indication_output, <e_eps_registration_state, <e_tac, <e_cid, <e_operator_id, &endc_available, log_object); process_wcdma_info (response_output, indication_output, &wcdma_cs_registration_state, &wcdma_ps_registration_state, &wcdma_lac, &wcdma_cid, &wcdma_operator_id, log_object); process_gsm_info (response_output, indication_output, &gsm_cs_registration_state, &gsm_ps_registration_state, &gsm_lac, &gsm_cid, &gsm_operator_id, log_object); #define REG_INFO_SET(TAC,LAC,CID,OPERATOR_ID) \ if (!reg_info_set) { \ reg_info_set = TRUE; \ *out_tac = (TAC); \ *out_lac = (LAC); \ *out_cid = (CID); \ *out_operator_id = g_steal_pointer (&(OPERATOR_ID)); \ } /* Process 5G data */ *out_5gs_registration_state = nr5g_5gs_registration_state; if (mm_modem_3gpp_registration_state_is_registered (nr5g_5gs_registration_state)) { REG_INFO_SET (nr5g_tac, 0, nr5g_cid, nr5g_operator_id) *out_act |= MM_MODEM_ACCESS_TECHNOLOGY_5GNR; } /* Process 4G data */ *out_eps_registration_state = lte_eps_registration_state; if (mm_modem_3gpp_registration_state_is_registered (lte_eps_registration_state)) { REG_INFO_SET (lte_tac, 0, lte_cid, lte_operator_id) *out_act |= MM_MODEM_ACCESS_TECHNOLOGY_LTE; if (endc_available) *out_act |= MM_MODEM_ACCESS_TECHNOLOGY_5GNR; } /* Process 2G/3G data */ if (mm_modem_3gpp_registration_state_is_registered (wcdma_ps_registration_state)) { *out_ps_registration_state = wcdma_ps_registration_state; *out_act |= MM_MODEM_ACCESS_TECHNOLOGY_UMTS; REG_INFO_SET (0, wcdma_lac, wcdma_cid, wcdma_operator_id) } else if (mm_modem_3gpp_registration_state_is_registered (gsm_ps_registration_state)) { *out_ps_registration_state = gsm_ps_registration_state; *out_act |= MM_MODEM_ACCESS_TECHNOLOGY_GPRS; REG_INFO_SET (0, gsm_lac, gsm_cid, gsm_operator_id) } else { *out_ps_registration_state = unregistered_state_fallback (wcdma_ps_registration_state, gsm_ps_registration_state); } if (mm_modem_3gpp_registration_state_is_registered (wcdma_cs_registration_state)) { *out_cs_registration_state = wcdma_cs_registration_state; *out_act |= MM_MODEM_ACCESS_TECHNOLOGY_UMTS; REG_INFO_SET (0, wcdma_lac, wcdma_cid, wcdma_operator_id) } else if (mm_modem_3gpp_registration_state_is_registered (gsm_cs_registration_state)) { *out_cs_registration_state = gsm_cs_registration_state; *out_act |= MM_MODEM_ACCESS_TECHNOLOGY_GSM; REG_INFO_SET (0, gsm_lac, gsm_cid, gsm_operator_id) } else { *out_cs_registration_state = unregistered_state_fallback (wcdma_cs_registration_state, gsm_cs_registration_state); } #undef REG_INFO_SET } /*****************************************************************************/ QmiWmsStorageType mm_sms_storage_to_qmi_storage_type (MMSmsStorage storage) { switch (storage) { case MM_SMS_STORAGE_SM: return QMI_WMS_STORAGE_TYPE_UIM; case MM_SMS_STORAGE_ME: return QMI_WMS_STORAGE_TYPE_NV; case MM_SMS_STORAGE_UNKNOWN: case MM_SMS_STORAGE_MT: case MM_SMS_STORAGE_SR: case MM_SMS_STORAGE_BM: case MM_SMS_STORAGE_TA: default: return QMI_WMS_STORAGE_TYPE_NONE; } } MMSmsStorage mm_sms_storage_from_qmi_storage_type (QmiWmsStorageType qmi_storage) { switch (qmi_storage) { case QMI_WMS_STORAGE_TYPE_UIM: return MM_SMS_STORAGE_SM; case QMI_WMS_STORAGE_TYPE_NV: return MM_SMS_STORAGE_ME; case QMI_WMS_STORAGE_TYPE_NONE: default: return MM_SMS_STORAGE_UNKNOWN; } } /*****************************************************************************/ MMSmsState mm_sms_state_from_qmi_message_tag (QmiWmsMessageTagType tag) { switch (tag) { case QMI_WMS_MESSAGE_TAG_TYPE_MT_READ: case QMI_WMS_MESSAGE_TAG_TYPE_MT_NOT_READ: return MM_SMS_STATE_RECEIVED; case QMI_WMS_MESSAGE_TAG_TYPE_MO_SENT: return MM_SMS_STATE_SENT; case QMI_WMS_MESSAGE_TAG_TYPE_MO_NOT_SENT: return MM_SMS_STATE_STORED; default: return MM_SMS_STATE_UNKNOWN; } } /*****************************************************************************/ QmiWdsAuthentication mm_bearer_allowed_auth_to_qmi_authentication (MMBearerAllowedAuth auth, gpointer log_object, GError **error) { QmiWdsAuthentication out; if (auth == MM_BEARER_ALLOWED_AUTH_UNKNOWN) { mm_obj_dbg (log_object, "using default (CHAP) authentication method"); return QMI_WDS_AUTHENTICATION_CHAP; } if (auth == MM_BEARER_ALLOWED_AUTH_NONE) return QMI_WDS_AUTHENTICATION_NONE; /* otherwise find a bitmask that matches the input bitmask */ out = QMI_WDS_AUTHENTICATION_NONE; if (auth & MM_BEARER_ALLOWED_AUTH_PAP) out |= QMI_WDS_AUTHENTICATION_PAP; if (auth & MM_BEARER_ALLOWED_AUTH_CHAP) out |= QMI_WDS_AUTHENTICATION_CHAP; /* and if the bitmask cannot be built, error out */ if (out == QMI_WDS_AUTHENTICATION_NONE) { g_autofree gchar *str = NULL; str = mm_bearer_allowed_auth_build_string_from_mask (auth); g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "Unsupported authentication methods (%s)", str); } return out; } MMBearerAllowedAuth mm_bearer_allowed_auth_from_qmi_authentication (QmiWdsAuthentication auth) { MMBearerAllowedAuth out = 0; /* Exact match for NONE */ if (auth == QMI_WDS_AUTHENTICATION_NONE) return MM_BEARER_ALLOWED_AUTH_NONE; if (auth & QMI_WDS_AUTHENTICATION_PAP) out |= MM_BEARER_ALLOWED_AUTH_PAP; if (auth & QMI_WDS_AUTHENTICATION_CHAP) out |= MM_BEARER_ALLOWED_AUTH_CHAP; return out; } MMBearerIpFamily mm_bearer_ip_family_from_qmi_ip_support_type (QmiWdsIpSupportType ip_support_type) { switch (ip_support_type) { case QMI_WDS_IP_SUPPORT_TYPE_IPV4: return MM_BEARER_IP_FAMILY_IPV4; case QMI_WDS_IP_SUPPORT_TYPE_IPV6: return MM_BEARER_IP_FAMILY_IPV6; case QMI_WDS_IP_SUPPORT_TYPE_IPV4V6: return MM_BEARER_IP_FAMILY_IPV4V6; default: return MM_BEARER_IP_FAMILY_NONE; } } MMBearerIpFamily mm_bearer_ip_family_from_qmi_pdp_type (QmiWdsPdpType pdp_type) { switch (pdp_type) { case QMI_WDS_PDP_TYPE_IPV4: return MM_BEARER_IP_FAMILY_IPV4; case QMI_WDS_PDP_TYPE_IPV6: return MM_BEARER_IP_FAMILY_IPV6; case QMI_WDS_PDP_TYPE_IPV4_OR_IPV6: return MM_BEARER_IP_FAMILY_IPV4V6; case QMI_WDS_PDP_TYPE_PPP: default: return MM_BEARER_IP_FAMILY_NONE; } } gboolean mm_bearer_ip_family_to_qmi_pdp_type (MMBearerIpFamily ip_family, QmiWdsPdpType *out_pdp_type) { switch (ip_family) { case MM_BEARER_IP_FAMILY_IPV4: *out_pdp_type = QMI_WDS_PDP_TYPE_IPV4; return TRUE; case MM_BEARER_IP_FAMILY_IPV6: *out_pdp_type = QMI_WDS_PDP_TYPE_IPV6; return TRUE; case MM_BEARER_IP_FAMILY_IPV4V6: *out_pdp_type = QMI_WDS_PDP_TYPE_IPV4_OR_IPV6; return TRUE; case MM_BEARER_IP_FAMILY_NON_IP: case MM_BEARER_IP_FAMILY_NONE: case MM_BEARER_IP_FAMILY_ANY: default: /* there is no valid conversion, so just return FALSE to indicate it */ return FALSE; } } QmiWdsApnTypeMask mm_bearer_apn_type_to_qmi_apn_type (MMBearerApnType apn_type, gpointer log_object) { guint64 value = 0; if (apn_type == MM_BEARER_APN_TYPE_NONE) { mm_obj_dbg (log_object, "using default (internet) APN type"); return QMI_WDS_APN_TYPE_MASK_DEFAULT; } if (apn_type & MM_BEARER_APN_TYPE_DEFAULT) value |= QMI_WDS_APN_TYPE_MASK_DEFAULT; if (apn_type & MM_BEARER_APN_TYPE_IMS) value |= QMI_WDS_APN_TYPE_MASK_IMS; if (apn_type & MM_BEARER_APN_TYPE_MMS) value |= QMI_WDS_APN_TYPE_MASK_MMS; if (apn_type & MM_BEARER_APN_TYPE_MANAGEMENT) value |= QMI_WDS_APN_TYPE_MASK_FOTA; if (apn_type & MM_BEARER_APN_TYPE_INITIAL) value |= QMI_WDS_APN_TYPE_MASK_IA; if (apn_type & MM_BEARER_APN_TYPE_EMERGENCY) value |= QMI_WDS_APN_TYPE_MASK_EMERGENCY; return value; } MMBearerApnType mm_bearer_apn_type_from_qmi_apn_type (QmiWdsApnTypeMask apn_type) { MMBearerApnType value = MM_BEARER_APN_TYPE_NONE; if (apn_type & QMI_WDS_APN_TYPE_MASK_DEFAULT) value |= MM_BEARER_APN_TYPE_DEFAULT; if (apn_type & QMI_WDS_APN_TYPE_MASK_IMS) value |= MM_BEARER_APN_TYPE_IMS; if (apn_type & QMI_WDS_APN_TYPE_MASK_MMS) value |= MM_BEARER_APN_TYPE_MMS; if (apn_type & QMI_WDS_APN_TYPE_MASK_FOTA) value |= MM_BEARER_APN_TYPE_MANAGEMENT; if (apn_type & QMI_WDS_APN_TYPE_MASK_IA) value |= MM_BEARER_APN_TYPE_INITIAL; if (apn_type & QMI_WDS_APN_TYPE_MASK_EMERGENCY) value |= MM_BEARER_APN_TYPE_EMERGENCY; return value; } /*****************************************************************************/ /* QMI/WDA to MM translations */ QmiDataEndpointType mm_port_net_driver_to_qmi_endpoint_type (const gchar *net_driver) { if (!g_strcmp0 (net_driver, "qmi_wwan")) return QMI_DATA_ENDPOINT_TYPE_HSUSB; if (!g_strcmp0 (net_driver, "mhi_net")) return QMI_DATA_ENDPOINT_TYPE_PCIE; if (!g_strcmp0 (net_driver, "ipa")) return QMI_DATA_ENDPOINT_TYPE_EMBEDDED; if (!g_strcmp0 (net_driver, "bam-dmux")) return QMI_DATA_ENDPOINT_TYPE_BAM_DMUX; return QMI_DATA_ENDPOINT_TYPE_UNKNOWN; } /*****************************************************************************/ /** * The only case where we need to apply some logic to decide what the current * capabilities are is when we have a multimode CDMA/EVDO+GSM/UMTS device, in * which case we'll check the SSP and TP current values to decide which * capabilities are present and which have been disabled. * * For all the other cases, the DMS capabilities are exactly the current ones, * as there would be no capability switching support. */ MMModemCapability mm_current_capability_from_qmi_current_capabilities_context (MMQmiCurrentCapabilitiesContext *ctx, gpointer log_object) { MMModemCapability tmp = MM_MODEM_CAPABILITY_NONE; g_autofree gchar *nas_ssp_mode_preference_str = NULL; g_autofree gchar *nas_tp_str = NULL; g_autofree gchar *dms_capabilities_str = NULL; g_autofree gchar *tmp_str = NULL; /* If not a multimode device, we're done */ if (!ctx->multimode) { if (ctx->dms_capabilities != MM_MODEM_CAPABILITY_NONE) tmp = ctx->dms_capabilities; /* SSP logic to gather capabilities uses the Mode Preference TLV if available */ else if (ctx->nas_ssp_mode_preference_mask) tmp = mm_modem_capability_from_qmi_rat_mode_preference (ctx->nas_ssp_mode_preference_mask); /* If no value retrieved from SSP, check TP. We only process TP * values if not 'auto' (0). */ else if (ctx->nas_tp_mask != QMI_NAS_RADIO_TECHNOLOGY_PREFERENCE_AUTO) tmp = mm_modem_capability_from_qmi_radio_technology_preference (ctx->nas_tp_mask); } else { /* We have a multimode CDMA/EVDO+GSM/UMTS device, check SSP and TP */ /* SSP logic to gather capabilities uses the Mode Preference TLV if available */ if (ctx->nas_ssp_mode_preference_mask) tmp = mm_modem_capability_from_qmi_rat_mode_preference (ctx->nas_ssp_mode_preference_mask); /* If no value retrieved from SSP, check TP. We only process TP * values if not 'auto' (0). */ else if (ctx->nas_tp_mask != QMI_NAS_RADIO_TECHNOLOGY_PREFERENCE_AUTO) tmp = mm_modem_capability_from_qmi_radio_technology_preference (ctx->nas_tp_mask); /* Final capabilities are the union of the active multimode capability * (GSM/UMTS or CDMA/EVDO or both or none) in TP or SSP and other supported device's capabilities. * If the Technology Preference was "auto" or unknown we just fall back * to the Get Capabilities response. */ if (tmp == MM_MODEM_CAPABILITY_NONE) tmp = ctx->dms_capabilities; else tmp = (tmp & MM_MODEM_CAPABILITY_MULTIMODE) | (MM_MODEM_CAPABILITY_MULTIMODE ^ ctx->dms_capabilities); } /* Log about the logic applied */ nas_ssp_mode_preference_str = qmi_nas_rat_mode_preference_build_string_from_mask (ctx->nas_ssp_mode_preference_mask); nas_tp_str = qmi_nas_radio_technology_preference_build_string_from_mask (ctx->nas_tp_mask); dms_capabilities_str = mm_modem_capability_build_string_from_mask (ctx->dms_capabilities); tmp_str = mm_modem_capability_build_string_from_mask (tmp); mm_obj_dbg (log_object, "Current capabilities built: '%s'\n" " SSP mode preference: '%s'\n" " TP: '%s'\n" " DMS Capabilities: '%s'", tmp_str, nas_ssp_mode_preference_str ? nas_ssp_mode_preference_str : "unknown", nas_tp_str ? nas_tp_str : "unknown", dms_capabilities_str); return tmp; } /*****************************************************************************/ /* Utility to build list of supported capabilities */ GArray * mm_supported_capabilities_from_qmi_supported_capabilities_context (MMQmiSupportedCapabilitiesContext *ctx, gpointer log_object) { GArray *supported_combinations; supported_combinations = g_array_sized_new (FALSE, FALSE, sizeof (MMModemCapability), 4); /* Add all possible supported capability combinations. * In order to avoid unnecessary modem reboots, we will only implement capabilities * switching only when switching GSM/UMTS+CDMA/EVDO multimode devices, and only if * we have support for the commands doing it. */ if ((ctx->nas_tp_supported || ctx->nas_ssp_supported) && ctx->multimode) { MMModemCapability single; /* Multimode GSM/UMTS+CDMA/EVDO+(LTE/5GNR) device switched to GSM/UMTS+(LTE/5GNR) device */ single = MM_MODEM_CAPABILITY_GSM_UMTS | (MM_MODEM_CAPABILITY_MULTIMODE ^ ctx->dms_capabilities); g_array_append_val (supported_combinations, single); /* Multimode GSM/UMTS+CDMA/EVDO+(LTE/5GNR) device switched to CDMA/EVDO+(LTE/5GNR) device */ single = MM_MODEM_CAPABILITY_CDMA_EVDO | (MM_MODEM_CAPABILITY_MULTIMODE ^ ctx->dms_capabilities); g_array_append_val (supported_combinations, single); /* * Multimode GSM/UMTS+CDMA/EVDO+(LTE/5GNR) device switched to (LTE/5GNR) device * * This case is required because we use the same methods and operations to * switch capabilities and modes. */ if ((single = (MM_MODEM_CAPABILITY_MULTIMODE ^ ctx->dms_capabilities))) g_array_append_val (supported_combinations, single); } /* Add the full mask itself */ g_array_append_val (supported_combinations, ctx->dms_capabilities); return supported_combinations; } /*****************************************************************************/ /* Utility to build list of supported modes */ GArray * mm_supported_modes_from_qmi_supported_modes_context (MMQmiSupportedModesContext *ctx, gpointer log_object) { g_autoptr(GArray) combinations = NULL; g_autoptr(GArray) all = NULL; MMModemModeCombination mode; /* Start with a mode including ALL */ mode.allowed = ctx->all; mode.preferred = MM_MODEM_MODE_NONE; all = g_array_sized_new (FALSE, FALSE, sizeof (MMModemModeCombination), 1); g_array_append_val (all, mode); /* If SSP and TP are not supported, ignore supported mode management */ if (!ctx->nas_ssp_supported && !ctx->nas_tp_supported) return g_steal_pointer (&all); combinations = g_array_new (FALSE, FALSE, sizeof (MMModemModeCombination)); #define ADD_MODE_PREFERENCE(MODE1, MODE2, MODE3, MODE4) do { \ mode.allowed = MODE1; \ if (MODE2 != MM_MODEM_MODE_NONE) { \ mode.allowed |= MODE2; \ if (MODE3 != MM_MODEM_MODE_NONE) { \ mode.allowed |= MODE3; \ if (MODE4 != MM_MODEM_MODE_NONE) \ mode.allowed |= MODE4; \ } \ if (ctx->nas_ssp_supported) { \ if (MODE3 != MM_MODEM_MODE_NONE) { \ if (MODE4 != MM_MODEM_MODE_NONE) { \ mode.preferred = MODE4; \ g_array_append_val (combinations, mode); \ } \ mode.preferred = MODE3; \ g_array_append_val (combinations, mode); \ } \ mode.preferred = MODE2; \ g_array_append_val (combinations, mode); \ mode.preferred = MODE1; \ g_array_append_val (combinations, mode); \ } else { \ mode.preferred = MM_MODEM_MODE_NONE; \ g_array_append_val (combinations, mode); \ } \ } else { \ mode.allowed = MODE1; \ mode.preferred = MM_MODEM_MODE_NONE; \ g_array_append_val (combinations, mode); \ } \ } while (0) /* 2G-only, 3G-only */ ADD_MODE_PREFERENCE (MM_MODEM_MODE_2G, MM_MODEM_MODE_NONE, MM_MODEM_MODE_NONE, MM_MODEM_MODE_NONE); ADD_MODE_PREFERENCE (MM_MODEM_MODE_3G, MM_MODEM_MODE_NONE, MM_MODEM_MODE_NONE, MM_MODEM_MODE_NONE); /* * This case is required because we use the same methods and operations to * switch capabilities and modes. For the LTE capability there is a direct * related 4G mode, and so we cannot select a '4G only' mode in this device * because we wouldn't be able to know the full list of current capabilities * if the device was rebooted, as we would only see LTE capability. So, * handle this special case so that the LTE/4G-only mode can exclusively be * selected as capability switching in this kind of devices. */ if (!ctx->multimode || !(ctx->current_capabilities & MM_MODEM_CAPABILITY_MULTIMODE)) { /* 4G-only */ ADD_MODE_PREFERENCE (MM_MODEM_MODE_4G, MM_MODEM_MODE_NONE, MM_MODEM_MODE_NONE, MM_MODEM_MODE_NONE); } /* 2G, 3G, 4G combinations */ ADD_MODE_PREFERENCE (MM_MODEM_MODE_2G, MM_MODEM_MODE_3G, MM_MODEM_MODE_NONE, MM_MODEM_MODE_NONE); ADD_MODE_PREFERENCE (MM_MODEM_MODE_2G, MM_MODEM_MODE_4G, MM_MODEM_MODE_NONE, MM_MODEM_MODE_NONE); ADD_MODE_PREFERENCE (MM_MODEM_MODE_3G, MM_MODEM_MODE_4G, MM_MODEM_MODE_NONE, MM_MODEM_MODE_NONE); ADD_MODE_PREFERENCE (MM_MODEM_MODE_2G, MM_MODEM_MODE_3G, MM_MODEM_MODE_4G, MM_MODEM_MODE_NONE); /* 5G related mode combinations are only supported when NAS SSP is supported, * as there is no 5G support in NAS TP. */ if (ctx->nas_ssp_supported) { /* Same reasoning as for the special 4G-only case above */ if (!ctx->multimode || !(ctx->current_capabilities & MM_MODEM_CAPABILITY_MULTIMODE)) { ADD_MODE_PREFERENCE (MM_MODEM_MODE_5G, MM_MODEM_MODE_NONE, MM_MODEM_MODE_NONE, MM_MODEM_MODE_NONE); ADD_MODE_PREFERENCE (MM_MODEM_MODE_4G, MM_MODEM_MODE_5G, MM_MODEM_MODE_NONE, MM_MODEM_MODE_NONE); } ADD_MODE_PREFERENCE (MM_MODEM_MODE_2G, MM_MODEM_MODE_5G, MM_MODEM_MODE_NONE, MM_MODEM_MODE_NONE); ADD_MODE_PREFERENCE (MM_MODEM_MODE_3G, MM_MODEM_MODE_5G, MM_MODEM_MODE_NONE, MM_MODEM_MODE_NONE); ADD_MODE_PREFERENCE (MM_MODEM_MODE_2G, MM_MODEM_MODE_3G, MM_MODEM_MODE_5G, MM_MODEM_MODE_NONE); ADD_MODE_PREFERENCE (MM_MODEM_MODE_2G, MM_MODEM_MODE_4G, MM_MODEM_MODE_5G, MM_MODEM_MODE_NONE); ADD_MODE_PREFERENCE (MM_MODEM_MODE_3G, MM_MODEM_MODE_4G, MM_MODEM_MODE_5G, MM_MODEM_MODE_NONE); ADD_MODE_PREFERENCE (MM_MODEM_MODE_2G, MM_MODEM_MODE_3G, MM_MODEM_MODE_4G, MM_MODEM_MODE_5G); } /* Filter out unsupported modes */ return mm_filter_supported_modes (all, combinations, log_object); } /*****************************************************************************/ MMOmaSessionType mm_oma_session_type_from_qmi_oma_session_type (QmiOmaSessionType qmi_session_type) { switch (qmi_session_type) { case QMI_OMA_SESSION_TYPE_CLIENT_INITIATED_DEVICE_CONFIGURE: return MM_OMA_SESSION_TYPE_CLIENT_INITIATED_DEVICE_CONFIGURE; case QMI_OMA_SESSION_TYPE_CLIENT_INITIATED_PRL_UPDATE: return MM_OMA_SESSION_TYPE_CLIENT_INITIATED_PRL_UPDATE; case QMI_OMA_SESSION_TYPE_CLIENT_INITIATED_HANDS_FREE_ACTIVATION: return MM_OMA_SESSION_TYPE_CLIENT_INITIATED_HANDS_FREE_ACTIVATION; case QMI_OMA_SESSION_TYPE_DEVICE_INITIATED_HANDS_FREE_ACTIVATION: return MM_OMA_SESSION_TYPE_DEVICE_INITIATED_HANDS_FREE_ACTIVATION; case QMI_OMA_SESSION_TYPE_NETWORK_INITIATED_PRL_UPDATE: return MM_OMA_SESSION_TYPE_NETWORK_INITIATED_PRL_UPDATE; case QMI_OMA_SESSION_TYPE_NETWORK_INITIATED_DEVICE_CONFIGURE: return MM_OMA_SESSION_TYPE_NETWORK_INITIATED_DEVICE_CONFIGURE; case QMI_OMA_SESSION_TYPE_DEVICE_INITIATED_PRL_UPDATE: return MM_OMA_SESSION_TYPE_DEVICE_INITIATED_PRL_UPDATE; default: return MM_OMA_SESSION_TYPE_UNKNOWN; } } QmiOmaSessionType mm_oma_session_type_to_qmi_oma_session_type (MMOmaSessionType mm_session_type) { switch (mm_session_type) { case MM_OMA_SESSION_TYPE_CLIENT_INITIATED_DEVICE_CONFIGURE: return QMI_OMA_SESSION_TYPE_CLIENT_INITIATED_DEVICE_CONFIGURE; case MM_OMA_SESSION_TYPE_CLIENT_INITIATED_PRL_UPDATE: return QMI_OMA_SESSION_TYPE_CLIENT_INITIATED_PRL_UPDATE; case MM_OMA_SESSION_TYPE_CLIENT_INITIATED_HANDS_FREE_ACTIVATION: return QMI_OMA_SESSION_TYPE_CLIENT_INITIATED_HANDS_FREE_ACTIVATION; case MM_OMA_SESSION_TYPE_DEVICE_INITIATED_HANDS_FREE_ACTIVATION: return QMI_OMA_SESSION_TYPE_DEVICE_INITIATED_HANDS_FREE_ACTIVATION; case MM_OMA_SESSION_TYPE_NETWORK_INITIATED_PRL_UPDATE: return QMI_OMA_SESSION_TYPE_NETWORK_INITIATED_PRL_UPDATE; case MM_OMA_SESSION_TYPE_NETWORK_INITIATED_DEVICE_CONFIGURE: return QMI_OMA_SESSION_TYPE_NETWORK_INITIATED_DEVICE_CONFIGURE; case MM_OMA_SESSION_TYPE_DEVICE_INITIATED_PRL_UPDATE: return QMI_OMA_SESSION_TYPE_DEVICE_INITIATED_PRL_UPDATE; case MM_OMA_SESSION_TYPE_UNKNOWN: default: g_assert_not_reached (); } } MMOmaSessionState mm_oma_session_state_from_qmi_oma_session_state (QmiOmaSessionState qmi_session_state) { /* Note: MM_OMA_SESSION_STATE_STARTED is not a state received from the modem */ switch (qmi_session_state) { case QMI_OMA_SESSION_STATE_COMPLETE_INFORMATION_UPDATED: case QMI_OMA_SESSION_STATE_COMPLETE_UPDATED_INFORMATION_UNAVAILABLE: return MM_OMA_SESSION_STATE_COMPLETED; case QMI_OMA_SESSION_STATE_FAILED: return MM_OMA_SESSION_STATE_FAILED; case QMI_OMA_SESSION_STATE_RETRYING: return MM_OMA_SESSION_STATE_RETRYING; case QMI_OMA_SESSION_STATE_CONNECTING: return MM_OMA_SESSION_STATE_CONNECTING; case QMI_OMA_SESSION_STATE_CONNECTED: return MM_OMA_SESSION_STATE_CONNECTED; case QMI_OMA_SESSION_STATE_AUTHENTICATED: return MM_OMA_SESSION_STATE_AUTHENTICATED; case QMI_OMA_SESSION_STATE_MDN_DOWNLOADED: return MM_OMA_SESSION_STATE_MDN_DOWNLOADED; case QMI_OMA_SESSION_STATE_MSID_DOWNLOADED: return MM_OMA_SESSION_STATE_MSID_DOWNLOADED; case QMI_OMA_SESSION_STATE_PRL_DOWNLOADED: return MM_OMA_SESSION_STATE_PRL_DOWNLOADED; case QMI_OMA_SESSION_STATE_MIP_PROFILE_DOWNLOADED: return MM_OMA_SESSION_STATE_MIP_PROFILE_DOWNLOADED; default: return MM_OMA_SESSION_STATE_UNKNOWN; } } /*****************************************************************************/ MMOmaSessionStateFailedReason mm_oma_session_state_failed_reason_from_qmi_oma_session_failed_reason (QmiOmaSessionFailedReason qmi_session_failed_reason) { switch (qmi_session_failed_reason) { case QMI_OMA_SESSION_FAILED_REASON_UNKNOWN: return MM_OMA_SESSION_STATE_FAILED_REASON_UNKNOWN; case QMI_OMA_SESSION_FAILED_REASON_NETWORK_UNAVAILABLE: return MM_OMA_SESSION_STATE_FAILED_REASON_NETWORK_UNAVAILABLE; case QMI_OMA_SESSION_FAILED_REASON_SERVER_UNAVAILABLE: return MM_OMA_SESSION_STATE_FAILED_REASON_SERVER_UNAVAILABLE; case QMI_OMA_SESSION_FAILED_REASON_AUTHENTICATION_FAILED: return MM_OMA_SESSION_STATE_FAILED_REASON_AUTHENTICATION_FAILED; case QMI_OMA_SESSION_FAILED_REASON_MAX_RETRY_EXCEEDED: return MM_OMA_SESSION_STATE_FAILED_REASON_MAX_RETRY_EXCEEDED; case QMI_OMA_SESSION_FAILED_REASON_SESSION_CANCELLED: return MM_OMA_SESSION_STATE_FAILED_REASON_SESSION_CANCELLED; default: return MM_OMA_SESSION_STATE_FAILED_REASON_UNKNOWN; } } /*****************************************************************************/ /* Convert between firmware unique ID (string) and QMI unique ID (16 bytes) * * The unique ID coming in the QMI message is a fixed-size 16 byte array, and its * format really depends on the manufacturer. But, if the manufacturer is nice enough * to use ASCII for this field, just use it ourselves as well, no need to obfuscate * the information we expose in our interfaces. * * We also need to do the conversion in the other way around, because when * selecting a new image to run we need to provide the QMI unique ID. */ #define EXPECTED_QMI_UNIQUE_ID_LENGTH 16 gchar * mm_qmi_unique_id_to_firmware_unique_id (GArray *qmi_unique_id, GError **error) { guint i; gboolean expect_nul_byte = FALSE; if (qmi_unique_id->len != EXPECTED_QMI_UNIQUE_ID_LENGTH) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "unexpected QMI unique ID length: %u (expected: %u)", qmi_unique_id->len, EXPECTED_QMI_UNIQUE_ID_LENGTH); return NULL; } for (i = 0; i < qmi_unique_id->len; i++) { guint8 val; val = g_array_index (qmi_unique_id, guint8, i); /* Check for ASCII chars */ if (g_ascii_isprint ((gchar) val)) { /* Halt iteration if we found an ASCII char after a NUL byte */ if (expect_nul_byte) break; /* good char */ continue; } /* Allow NUL bytes at the end of the array */ if (val == '\0' && i > 0) { if (!expect_nul_byte) expect_nul_byte = TRUE; continue; } /* Halt iteration, not something we can build as ASCII */ break; } if (i != qmi_unique_id->len) return mm_utils_bin2hexstr ((const guint8 *)qmi_unique_id->data, qmi_unique_id->len); return g_strndup ((const gchar *)qmi_unique_id->data, qmi_unique_id->len); } GArray * mm_firmware_unique_id_to_qmi_unique_id (const gchar *unique_id, GError **error) { guint len; GArray *qmi_unique_id; len = strlen (unique_id); /* The length will be exactly EXPECTED_QMI_UNIQUE_ID_LENGTH*2 if given in HEX */ if (len == (2 * EXPECTED_QMI_UNIQUE_ID_LENGTH)) { g_autofree guint8 *tmp = NULL; gsize tmp_len; tmp_len = 0; tmp = mm_utils_hexstr2bin (unique_id, -1, &tmp_len, error); if (!tmp) { g_prefix_error (error, "Unexpected character found in unique id: "); return NULL; } g_assert (tmp_len == EXPECTED_QMI_UNIQUE_ID_LENGTH); qmi_unique_id = g_array_sized_new (FALSE, FALSE, sizeof (guint8), tmp_len); g_array_insert_vals (qmi_unique_id, 0, tmp, tmp_len); return qmi_unique_id; } /* The length will be EXPECTED_QMI_UNIQUE_ID_LENGTH or less if given in ASCII */ if (len > 0 && len <= EXPECTED_QMI_UNIQUE_ID_LENGTH) { guint i; for (i = 0; i < len; i++) { if (!g_ascii_isprint (unique_id[i])) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Unexpected character found in unique id (not ASCII): %c", unique_id[i]); return NULL; } } qmi_unique_id = g_array_sized_new (FALSE, FALSE, sizeof (guint8), EXPECTED_QMI_UNIQUE_ID_LENGTH); g_array_set_size (qmi_unique_id, EXPECTED_QMI_UNIQUE_ID_LENGTH); memcpy (&qmi_unique_id->data[0], unique_id, len); if (len < EXPECTED_QMI_UNIQUE_ID_LENGTH) memset (&qmi_unique_id->data[len], 0, EXPECTED_QMI_UNIQUE_ID_LENGTH - len); return qmi_unique_id; } g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Unexpected unique id length: %u", len); return NULL; } /*****************************************************************************/ gboolean mm_qmi_uim_get_card_status_output_parse (gpointer log_object, QmiMessageUimGetCardStatusOutput *output, MMModemLock *o_lock, QmiUimPinState *o_pin1_state, guint *o_pin1_retries, guint *o_puk1_retries, QmiUimPinState *o_pin2_state, guint *o_pin2_retries, guint *o_puk2_retries, guint *o_pers_retries, GError **error) { QmiMessageUimGetCardStatusOutputCardStatusCardsElement *card; QmiMessageUimGetCardStatusOutputCardStatusCardsElementApplicationsElementV2 *app; GArray *cards; guint16 index_gw_primary = 0xFFFF; guint8 gw_primary_slot_i = 0; guint8 gw_primary_application_i = 0; MMModemLock lock = MM_MODEM_LOCK_UNKNOWN; /* This command supports MULTIPLE cards with MULTIPLE applications each. For our * purposes, we're going to consider as the SIM to use the one identified as * 'primary GW' exclusively. We don't really support Dual Sim Dual Standby yet. */ qmi_message_uim_get_card_status_output_get_card_status ( output, &index_gw_primary, NULL, /* index_1x_primary */ NULL, /* index_gw_secondary */ NULL, /* index_1x_secondary */ &cards, NULL); if (cards->len == 0) { g_set_error (error, QMI_CORE_ERROR, QMI_CORE_ERROR_FAILED, "No cards reported"); return FALSE; } /* Look for the primary GW slot and application. * If we don't have valid GW primary slot index and application index, assume * we're missing the SIM altogether */ gw_primary_slot_i = ((index_gw_primary & 0xFF00) >> 8); gw_primary_application_i = ((index_gw_primary & 0x00FF)); if (gw_primary_slot_i == 0xFF) { g_set_error (error, MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_SIM_NOT_INSERTED, "GW primary session index unknown"); return FALSE; } mm_obj_dbg (log_object, "GW primary session index: %u", gw_primary_slot_i); if (gw_primary_application_i == 0xFF) { g_set_error (error, MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_SIM_NOT_INSERTED, "GW primary application index unknown"); return FALSE; } mm_obj_dbg (log_object, "GW primary application index: %u", gw_primary_application_i); /* Validate slot index */ if (gw_primary_slot_i >= cards->len) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Invalid GW primary session index: %u", gw_primary_slot_i); return FALSE; } /* Get card at slot */ card = &g_array_index (cards, QmiMessageUimGetCardStatusOutputCardStatusCardsElement, gw_primary_slot_i); if (card->card_state == QMI_UIM_CARD_STATE_ABSENT) { g_set_error (error, MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_SIM_NOT_INSERTED, "No card found"); return FALSE; } if (card->card_state == QMI_UIM_CARD_STATE_ERROR) { const gchar *card_error; card_error = qmi_uim_card_error_get_string (card->error_code); g_set_error (error, MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_SIM_WRONG, "Card error: %s", card_error ? card_error : "unknown error"); return FALSE; } if (card->card_state != QMI_UIM_CARD_STATE_PRESENT) { g_set_error (error, MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_SIM_WRONG, "Card error: unexpected card state: 0x%x", card->card_state); return FALSE; } /* Card is present */ if (card->applications->len == 0) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "No applications reported in card"); return FALSE; } /* Validate application index */ if (gw_primary_application_i >= card->applications->len) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Invalid GW primary application index: %u", gw_primary_application_i); return FALSE; } app = &g_array_index (card->applications, QmiMessageUimGetCardStatusOutputCardStatusCardsElementApplicationsElementV2, gw_primary_application_i); if ((app->type != QMI_UIM_CARD_APPLICATION_TYPE_SIM) && (app->type != QMI_UIM_CARD_APPLICATION_TYPE_USIM)) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Unsupported application type found in GW primary application index: %s", qmi_uim_card_application_type_get_string (app->type)); return FALSE; } /* Illegal application state is fatal, consider it as a failed SIM right * away and don't even attempt to retry */ if (app->state == QMI_UIM_CARD_APPLICATION_STATE_ILLEGAL) { g_set_error (error, MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_SIM_WRONG, "Illegal SIM/USIM application state"); return FALSE; } /* If card not ready yet, return RETRY error. * If the application state reports needing PIN/PUk, consider that ready as * well, and let the logic fall down to check PIN1/PIN2. */ if (app->state != QMI_UIM_CARD_APPLICATION_STATE_READY && app->state != QMI_UIM_CARD_APPLICATION_STATE_PIN1_OR_UPIN_PIN_REQUIRED && app->state != QMI_UIM_CARD_APPLICATION_STATE_PUK1_OR_UPIN_PUK_REQUIRED && app->state != QMI_UIM_CARD_APPLICATION_STATE_CHECK_PERSONALIZATION_STATE && app->state != QMI_UIM_CARD_APPLICATION_STATE_PIN1_BLOCKED) { mm_obj_dbg (log_object, "neither SIM nor USIM are ready"); g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_RETRY, "SIM not ready yet (retry)"); return FALSE; } /* Report state and retries if requested to do so */ if (o_pin1_state) *o_pin1_state = app->pin1_state; if (o_pin1_retries) *o_pin1_retries = app->pin1_retries; if (o_puk1_retries) *o_puk1_retries = app->puk1_retries; if (o_pin2_state) *o_pin2_state = app->pin2_state; if (o_pin2_retries) *o_pin2_retries = app->pin2_retries; if (o_puk2_retries) *o_puk2_retries = app->puk2_retries; /* Early bail out if lock status isn't wanted at this point, so that we * don't fail with an error the unlock retries check */ if (!o_lock) return TRUE; /* Card is ready, what's the lock status? */ /* PIN1 */ switch (app->pin1_state) { case QMI_UIM_PIN_STATE_NOT_INITIALIZED: g_set_error (error, MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_SIM_WRONG, "SIM PIN/PUK status not known yet"); return FALSE; case QMI_UIM_PIN_STATE_PERMANENTLY_BLOCKED: g_set_error (error, MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_SIM_WRONG, "SIM PIN/PUK permanently blocked"); return FALSE; case QMI_UIM_PIN_STATE_ENABLED_NOT_VERIFIED: lock = MM_MODEM_LOCK_SIM_PIN; break; case QMI_UIM_PIN_STATE_BLOCKED: lock = MM_MODEM_LOCK_SIM_PUK; break; case QMI_UIM_PIN_STATE_DISABLED: case QMI_UIM_PIN_STATE_ENABLED_VERIFIED: lock = MM_MODEM_LOCK_NONE; break; default: g_set_error (error, MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_SIM_WRONG, "Unknown SIM PIN/PUK status"); return FALSE; } /* Personalization */ if (lock == MM_MODEM_LOCK_NONE && app->state == QMI_UIM_CARD_APPLICATION_STATE_CHECK_PERSONALIZATION_STATE) { if (app->personalization_state == QMI_UIM_CARD_APPLICATION_PERSONALIZATION_STATE_IN_PROGRESS || app->personalization_state == QMI_UIM_CARD_APPLICATION_PERSONALIZATION_STATE_UNKNOWN) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_RETRY, "Personalization check in progress"); return FALSE; } if (app->personalization_state == QMI_UIM_CARD_APPLICATION_PERSONALIZATION_STATE_CODE_REQUIRED || app->personalization_state == QMI_UIM_CARD_APPLICATION_PERSONALIZATION_STATE_PUK_CODE_REQUIRED) { gboolean pin; pin = app->personalization_state == QMI_UIM_CARD_APPLICATION_PERSONALIZATION_STATE_CODE_REQUIRED; switch (app->personalization_feature) { case QMI_UIM_CARD_APPLICATION_PERSONALIZATION_FEATURE_STATUS_GW_NETWORK: lock = (pin ? MM_MODEM_LOCK_PH_NET_PIN : MM_MODEM_LOCK_PH_NET_PUK); break; case QMI_UIM_CARD_APPLICATION_PERSONALIZATION_FEATURE_STATUS_GW_NETWORK_SUBSET: lock = (pin ? MM_MODEM_LOCK_PH_NETSUB_PIN : MM_MODEM_LOCK_PH_NETSUB_PUK); break; case QMI_UIM_CARD_APPLICATION_PERSONALIZATION_FEATURE_STATUS_GW_SERVICE_PROVIDER: lock = (pin ? MM_MODEM_LOCK_PH_SP_PIN : MM_MODEM_LOCK_PH_SP_PUK); break; case QMI_UIM_CARD_APPLICATION_PERSONALIZATION_FEATURE_STATUS_GW_CORPORATE: lock = (pin ? MM_MODEM_LOCK_PH_CORP_PIN : MM_MODEM_LOCK_PH_CORP_PUK); break; case QMI_UIM_CARD_APPLICATION_PERSONALIZATION_FEATURE_STATUS_GW_UIM: if (pin) { lock = MM_MODEM_LOCK_PH_SIM_PIN; break; } /* fall through */ case QMI_UIM_CARD_APPLICATION_PERSONALIZATION_FEATURE_STATUS_1X_NETWORK_TYPE_1: case QMI_UIM_CARD_APPLICATION_PERSONALIZATION_FEATURE_STATUS_1X_NETWORK_TYPE_2: case QMI_UIM_CARD_APPLICATION_PERSONALIZATION_FEATURE_STATUS_1X_HRPD: case QMI_UIM_CARD_APPLICATION_PERSONALIZATION_FEATURE_STATUS_1X_SERVICE_PROVIDER: case QMI_UIM_CARD_APPLICATION_PERSONALIZATION_FEATURE_STATUS_1X_CORPORATE: case QMI_UIM_CARD_APPLICATION_PERSONALIZATION_FEATURE_STATUS_1X_RUIM: case QMI_UIM_CARD_APPLICATION_PERSONALIZATION_FEATURE_STATUS_UNKNOWN: case QMI_UIM_CARD_APPLICATION_PERSONALIZATION_FEATURE_STATUS_GW_SERVICE_PROVIDER_NAME: case QMI_UIM_CARD_APPLICATION_PERSONALIZATION_FEATURE_STATUS_GW_SP_EHPLMN: case QMI_UIM_CARD_APPLICATION_PERSONALIZATION_FEATURE_STATUS_GW_ICCID: case QMI_UIM_CARD_APPLICATION_PERSONALIZATION_FEATURE_STATUS_GW_IMPI: case QMI_UIM_CARD_APPLICATION_PERSONALIZATION_FEATURE_STATUS_GW_NETWORK_SUBSET_SERVICE_PROVIDER: case QMI_UIM_CARD_APPLICATION_PERSONALIZATION_FEATURE_STATUS_GW_CARRIER: default: g_set_error (error, MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_SIM_WRONG, "Unsupported personalization feature"); return FALSE; } if (o_pers_retries) *o_pers_retries = app->personalization_retries; } } /* PIN2 */ if (lock == MM_MODEM_LOCK_NONE) { switch (app->pin2_state) { case QMI_UIM_PIN_STATE_NOT_INITIALIZED: mm_obj_warn (log_object, "SIM PIN2/PUK2 status not known yet"); break; case QMI_UIM_PIN_STATE_ENABLED_NOT_VERIFIED: lock = MM_MODEM_LOCK_SIM_PIN2; break; case QMI_UIM_PIN_STATE_PERMANENTLY_BLOCKED: mm_obj_warn (log_object, "PUK2 permanently blocked"); /* Fall through */ case QMI_UIM_PIN_STATE_BLOCKED: lock = MM_MODEM_LOCK_SIM_PUK2; break; case QMI_UIM_PIN_STATE_DISABLED: case QMI_UIM_PIN_STATE_ENABLED_VERIFIED: break; default: mm_obj_warn (log_object, "unknown SIM PIN2/PUK2 status"); break; } } *o_lock = lock; return TRUE; } /*****************************************************************************/ gboolean mm_qmi_uim_get_configuration_output_parse (gpointer log_object, QmiMessageUimGetConfigurationOutput *output, MMModem3gppFacility *o_lock, GError **error) { QmiMessageUimGetConfigurationOutputPersonalizationStatusElement *element; GArray *elements; guint idx; *o_lock = MM_MODEM_3GPP_FACILITY_NONE; if (!qmi_message_uim_get_configuration_output_get_personalization_status (output, &elements, error)) { g_prefix_error (error, "UIM Get Personalization Status failed: "); return FALSE; } for (idx = 0; idx < elements->len; idx++) { element = &g_array_index (elements, QmiMessageUimGetConfigurationOutputPersonalizationStatusElement, idx); switch (element->feature) { case QMI_UIM_CARD_APPLICATION_PERSONALIZATION_FEATURE_GW_NETWORK: *o_lock |= MM_MODEM_3GPP_FACILITY_NET_PERS; break; case QMI_UIM_CARD_APPLICATION_PERSONALIZATION_FEATURE_GW_NETWORK_SUBSET: *o_lock |= MM_MODEM_3GPP_FACILITY_NET_SUB_PERS; break; case QMI_UIM_CARD_APPLICATION_PERSONALIZATION_FEATURE_GW_SERVICE_PROVIDER: *o_lock |= MM_MODEM_3GPP_FACILITY_PROVIDER_PERS; break; case QMI_UIM_CARD_APPLICATION_PERSONALIZATION_FEATURE_GW_CORPORATE: *o_lock |= MM_MODEM_3GPP_FACILITY_CORP_PERS; break; case QMI_UIM_CARD_APPLICATION_PERSONALIZATION_FEATURE_GW_UIM: *o_lock |= MM_MODEM_3GPP_FACILITY_PH_SIM; break; case QMI_UIM_CARD_APPLICATION_PERSONALIZATION_FEATURE_1X_NETWORK_TYPE_1: case QMI_UIM_CARD_APPLICATION_PERSONALIZATION_FEATURE_1X_NETWORK_TYPE_2: case QMI_UIM_CARD_APPLICATION_PERSONALIZATION_FEATURE_1X_HRPD: case QMI_UIM_CARD_APPLICATION_PERSONALIZATION_FEATURE_1X_SERVICE_PROVIDER: case QMI_UIM_CARD_APPLICATION_PERSONALIZATION_FEATURE_1X_CORPORATE: case QMI_UIM_CARD_APPLICATION_PERSONALIZATION_FEATURE_1X_RUIM: case QMI_UIM_CARD_APPLICATION_PERSONALIZATION_FEATURE_GW_SERVICE_PROVIDER_NAME: case QMI_UIM_CARD_APPLICATION_PERSONALIZATION_FEATURE_GW_SP_EHPLMN: case QMI_UIM_CARD_APPLICATION_PERSONALIZATION_FEATURE_GW_ICCID: case QMI_UIM_CARD_APPLICATION_PERSONALIZATION_FEATURE_GW_IMPI: case QMI_UIM_CARD_APPLICATION_PERSONALIZATION_FEATURE_GW_NETWORK_SUBSET_SERVICE_PROVIDER: case QMI_UIM_CARD_APPLICATION_PERSONALIZATION_FEATURE_GW_CARRIER: mm_obj_dbg (log_object, "ignoring lock in UIM feature: %s", qmi_uim_card_application_personalization_feature_get_string (element->feature)); break; default: mm_obj_dbg (log_object, "ignoring lock in unhandled UIM feature: 0x%x", (guint)element->feature); } } return TRUE; } /*****************************************************************************/ gboolean qmi_personalization_feature_from_mm_modem_3gpp_facility (MMModem3gppFacility facility, QmiUimCardApplicationPersonalizationFeature *o_feature) { switch (facility) { case MM_MODEM_3GPP_FACILITY_NET_PERS: *o_feature = QMI_UIM_CARD_APPLICATION_PERSONALIZATION_FEATURE_GW_NETWORK; return TRUE; case MM_MODEM_3GPP_FACILITY_NET_SUB_PERS: *o_feature = QMI_UIM_CARD_APPLICATION_PERSONALIZATION_FEATURE_GW_NETWORK_SUBSET; return TRUE; case MM_MODEM_3GPP_FACILITY_PROVIDER_PERS: *o_feature = QMI_UIM_CARD_APPLICATION_PERSONALIZATION_FEATURE_GW_SERVICE_PROVIDER; return TRUE; case MM_MODEM_3GPP_FACILITY_CORP_PERS: *o_feature = QMI_UIM_CARD_APPLICATION_PERSONALIZATION_FEATURE_GW_CORPORATE; return TRUE; case MM_MODEM_3GPP_FACILITY_PH_SIM: *o_feature = QMI_UIM_CARD_APPLICATION_PERSONALIZATION_FEATURE_GW_UIM; return TRUE; case MM_MODEM_3GPP_FACILITY_NONE: case MM_MODEM_3GPP_FACILITY_SIM: case MM_MODEM_3GPP_FACILITY_FIXED_DIALING: case MM_MODEM_3GPP_FACILITY_PH_FSIM: default: return FALSE; } } /*****************************************************************************/ typedef struct { QmiWdsVerboseCallEndReasonInternal vcer; gboolean is_core_error; /* TRUE if MM_CORE_ERROR, FALSE if MM_MOBILE_EQUIPMENT_ERROR */ guint error_code; } InternalErrorMap; static const InternalErrorMap internal_error_map[] = { /* QMI_WDS_VERBOSE_CALL_END_REASON_INTERNAL_ERROR */ /* QMI_WDS_VERBOSE_CALL_END_REASON_INTERNAL_CALL_ENDED */ /* QMI_WDS_VERBOSE_CALL_END_REASON_INTERNAL_UNKNOWN_INTERNAL_CAUSE */ /* QMI_WDS_VERBOSE_CALL_END_REASON_INTERNAL_UNKNOWN_CAUSE */ /* QMI_WDS_VERBOSE_CALL_END_REASON_INTERNAL_CLOSE_IN_PROGRESS */ /* QMI_WDS_VERBOSE_CALL_END_REASON_INTERNAL_NETWORK_INITIATED_TERMINATION */ /* QMI_WDS_VERBOSE_CALL_END_REASON_INTERNAL_APP_PREEMPTED */ { QMI_WDS_VERBOSE_CALL_END_REASON_INTERNAL_PDN_IPV4_CALL_DISALLOWED, FALSE, MM_MOBILE_EQUIPMENT_ERROR_IPV6_ONLY_ALLOWED }, { QMI_WDS_VERBOSE_CALL_END_REASON_INTERNAL_PDN_IPV4_CALL_THROTTLED, TRUE, MM_CORE_ERROR_THROTTLED }, { QMI_WDS_VERBOSE_CALL_END_REASON_INTERNAL_PDN_IPV6_CALL_DISALLOWED, FALSE, MM_MOBILE_EQUIPMENT_ERROR_IPV4_ONLY_ALLOWED }, { QMI_WDS_VERBOSE_CALL_END_REASON_INTERNAL_PDN_IPV6_CALL_THROTTLED, TRUE, MM_CORE_ERROR_THROTTLED }, /* QMI_WDS_VERBOSE_CALL_END_REASON_INTERNAL_MODEM_RESTART */ /* QMI_WDS_VERBOSE_CALL_END_REASON_INTERNAL_PDP_PPP_NOT_SUPPORTED */ /* QMI_WDS_VERBOSE_CALL_END_REASON_INTERNAL_UNPREFERRED_RAT */ /* QMI_WDS_VERBOSE_CALL_END_REASON_INTERNAL_PHYSICAL_LINK_CLOSE_IN_PROGRESS */ /* QMI_WDS_VERBOSE_CALL_END_REASON_INTERNAL_APN_PENDING_HANDOVER */ /* QMI_WDS_VERBOSE_CALL_END_REASON_INTERNAL_PROFILE_BEARER_INCOMPATIBLE */ /* QMI_WDS_VERBOSE_CALL_END_REASON_INTERNAL_MMGDSI_CARD_EVENT */ /* QMI_WDS_VERBOSE_CALL_END_REASON_INTERNAL_LPM_OR_POWER_DOWN */ { QMI_WDS_VERBOSE_CALL_END_REASON_INTERNAL_APN_DISABLED, FALSE, MM_MOBILE_EQUIPMENT_ERROR_MISSING_OR_UNKNOWN_APN }, /* QMI_WDS_VERBOSE_CALL_END_REASON_INTERNAL_MPIT_EXPIRED */ /* QMI_WDS_VERBOSE_CALL_END_REASON_INTERNAL_IPV6_ADDRESS_TRANSFER_FAILED */ /* QMI_WDS_VERBOSE_CALL_END_REASON_INTERNAL_TRAT_SWAP_FAILED */ /* QMI_WDS_VERBOSE_CALL_END_REASON_INTERNAL_EHRPD_TO_HRPD_FALLBACK */ { QMI_WDS_VERBOSE_CALL_END_REASON_INTERNAL_MANDATORY_APN_DISABLED, FALSE, MM_MOBILE_EQUIPMENT_ERROR_MISSING_OR_UNKNOWN_APN }, /* QMI_WDS_VERBOSE_CALL_END_REASON_INTERNAL_MIP_CONFIG_FAILURE */ /* QMI_WDS_VERBOSE_CALL_END_REASON_INTERNAL_PDN_INACTIVITY_TIMER_EXPIRED */ /* QMI_WDS_VERBOSE_CALL_END_REASON_INTERNAL_MAX_V4_CONNECTIONS */ /* QMI_WDS_VERBOSE_CALL_END_REASON_INTERNAL_MAX_V6_CONNECTIONS */ { QMI_WDS_VERBOSE_CALL_END_REASON_INTERNAL_APN_MISMATCH, FALSE, MM_MOBILE_EQUIPMENT_ERROR_MISSING_OR_UNKNOWN_APN }, /* QMI_WDS_VERBOSE_CALL_END_REASON_INTERNAL_IP_VERSION_MISMATCH: NOTE: this one is treated in a special way */ /* QMI_WDS_VERBOSE_CALL_END_REASON_INTERNAL_DUN_CALL_DISALLOWED */ /* QMI_WDS_VERBOSE_CALL_END_REASON_INTERNAL_INVALID_PROFILE */ /* QMI_WDS_VERBOSE_CALL_END_REASON_INTERNAL_EPC_NONEPC_TRANSITION */ /* QMI_WDS_VERBOSE_CALL_END_REASON_INTERNAL_INVALID_PROFILE_ID */ /* QMI_WDS_VERBOSE_CALL_END_REASON_INTERNAL_CALL_ALREADY_PRESENT */ /* QMI_WDS_VERBOSE_CALL_END_REASON_INTERNAL_INTERFACE_IN_USE */ /* QMI_WDS_VERBOSE_CALL_END_REASON_INTERNAL_IP_PDP_MISMATCH */ { QMI_WDS_VERBOSE_CALL_END_REASON_INTERNAL_APN_DISALLOWED_ON_ROAMING, FALSE, MM_MOBILE_EQUIPMENT_ERROR_MISSING_OR_UNKNOWN_APN }, /* QMI_WDS_VERBOSE_CALL_END_REASON_INTERNAL_APN_PARAMETER_CHANGE */ /* QMI_WDS_VERBOSE_CALL_END_REASON_INTERNAL_INTERFACE_IN_USE_CONFIG_MATCH */ { QMI_WDS_VERBOSE_CALL_END_REASON_INTERNAL_NULL_APN_DISALLOWED, FALSE, MM_MOBILE_EQUIPMENT_ERROR_MISSING_OR_UNKNOWN_APN }, /* QMI_WDS_VERBOSE_CALL_END_REASON_INTERNAL_THERMAL_MITIGATION */ /* QMI_WDS_VERBOSE_CALL_END_REASON_INTERNAL_SUBS_ID_MISMATCH */ /* QMI_WDS_VERBOSE_CALL_END_REASON_INTERNAL_DATA_SETTINGS_DISABLED */ /* QMI_WDS_VERBOSE_CALL_END_REASON_INTERNAL_DATA_ROAMING_SETTINGS_DISABLED */ { QMI_WDS_VERBOSE_CALL_END_REASON_INTERNAL_APN_FORMAT_INVALID, FALSE, MM_MOBILE_EQUIPMENT_ERROR_MISSING_OR_UNKNOWN_APN }, /* QMI_WDS_VERBOSE_CALL_END_REASON_INTERNAL_DDS_CALL_ABORT */ /* QMI_WDS_VERBOSE_CALL_END_REASON_INTERNAL_VALIDATION_FAILURE */ }; static GError * error_from_wds_verbose_call_end_reason_internal (QmiWdsVerboseCallEndReasonInternal vcer_reason, const gchar *vcer_reason_str, MMBearerIpFamily ip_type, gpointer log_object) { guint i; /* Try to normalize the "IP version mismatch" error based on the IP type being connected. But * leave the original reason string to clearly show that we did this translation. */ if (vcer_reason == QMI_WDS_VERBOSE_CALL_END_REASON_INTERNAL_IP_VERSION_MISMATCH) { if (ip_type == MM_BEARER_IP_FAMILY_IPV4) vcer_reason = QMI_WDS_VERBOSE_CALL_END_REASON_INTERNAL_PDN_IPV4_CALL_DISALLOWED; else if (ip_type == MM_BEARER_IP_FAMILY_IPV6) vcer_reason = QMI_WDS_VERBOSE_CALL_END_REASON_INTERNAL_PDN_IPV6_CALL_DISALLOWED; } for (i = 0; i < G_N_ELEMENTS (internal_error_map); i++) { if (internal_error_map[i].vcer == vcer_reason) { GError *error; g_assert (vcer_reason_str); if (internal_error_map[i].is_core_error) error = g_error_new_literal (MM_CORE_ERROR, internal_error_map[i].error_code, vcer_reason_str); else { error = mm_mobile_equipment_error_for_code (internal_error_map[i].error_code, log_object); g_prefix_error (&error, "%s: ", vcer_reason_str); } return error; } } return NULL; } /*****************************************************************************/ static const MMMobileEquipmentError qmi_vcer_3gpp_errors[] = { [QMI_WDS_VERBOSE_CALL_END_REASON_3GPP_OPERATOR_DETERMINED_BARRING] = MM_MOBILE_EQUIPMENT_ERROR_OPERATOR_DETERMINED_BARRING, [QMI_WDS_VERBOSE_CALL_END_REASON_3GPP_INSUFFICIENT_RESOURCES] = MM_MOBILE_EQUIPMENT_ERROR_INSUFFICIENT_RESOURCES, [QMI_WDS_VERBOSE_CALL_END_REASON_3GPP_UNKNOWN_APN] = MM_MOBILE_EQUIPMENT_ERROR_MISSING_OR_UNKNOWN_APN, [QMI_WDS_VERBOSE_CALL_END_REASON_3GPP_UNKNOWN_PDP] = MM_MOBILE_EQUIPMENT_ERROR_UNKNOWN_PDP_ADDRESS_OR_TYPE, [QMI_WDS_VERBOSE_CALL_END_REASON_3GPP_AUTHENTICATION_FAILED] = MM_MOBILE_EQUIPMENT_ERROR_USER_AUTHENTICATION_FAILED, [QMI_WDS_VERBOSE_CALL_END_REASON_3GPP_GGSN_REJECT] = MM_MOBILE_EQUIPMENT_ERROR_ACTIVATION_REJECTED_BY_GGSN_OR_GW, [QMI_WDS_VERBOSE_CALL_END_REASON_3GPP_ACTIVATION_REJECT] = MM_MOBILE_EQUIPMENT_ERROR_ACTIVATION_REJECTED_UNSPECIFIED, [QMI_WDS_VERBOSE_CALL_END_REASON_3GPP_OPTION_NOT_SUPPORTED] = MM_MOBILE_EQUIPMENT_ERROR_SERVICE_OPTION_NOT_SUPPORTED, [QMI_WDS_VERBOSE_CALL_END_REASON_3GPP_OPTION_UNSUBSCRIBED] = MM_MOBILE_EQUIPMENT_ERROR_SERVICE_OPTION_NOT_SUBSCRIBED, [QMI_WDS_VERBOSE_CALL_END_REASON_3GPP_OPTION_TEMPORARILY_OUT_OF_ORDER] = MM_MOBILE_EQUIPMENT_ERROR_SERVICE_OPTION_OUT_OF_ORDER, [QMI_WDS_VERBOSE_CALL_END_REASON_3GPP_NSAPI_ALREADY_USED] = MM_MOBILE_EQUIPMENT_ERROR_NSAPI_OR_PTI_ALREADY_IN_USE, [QMI_WDS_VERBOSE_CALL_END_REASON_3GPP_REGULAR_DEACTIVATION] = MM_MOBILE_EQUIPMENT_ERROR_REGULAR_DEACTIVATION, [QMI_WDS_VERBOSE_CALL_END_REASON_3GPP_QOS_NOT_ACCEPTED] = MM_MOBILE_EQUIPMENT_ERROR_QOS_NOT_ACCEPTED, [QMI_WDS_VERBOSE_CALL_END_REASON_3GPP_NETWORK_FAILURE] = MM_MOBILE_EQUIPMENT_ERROR_NETWORK_FAILURE_ATTACH, [QMI_WDS_VERBOSE_CALL_END_REASON_3GPP_REATTACH_REQUIRED] = MM_MOBILE_EQUIPMENT_ERROR_NETWORK_FAILURE_ATTACH, [QMI_WDS_VERBOSE_CALL_END_REASON_3GPP_FEATURE_NOT_SUPPORTED] = MM_MOBILE_EQUIPMENT_ERROR_FEATURE_NOT_SUPPORTED, [QMI_WDS_VERBOSE_CALL_END_REASON_3GPP_TFT_SEMANTIC_ERROR] = MM_MOBILE_EQUIPMENT_ERROR_SEMANTIC_ERROR_IN_TFT_OPERATION, [QMI_WDS_VERBOSE_CALL_END_REASON_3GPP_TFT_SYNTAX_ERROR] = MM_MOBILE_EQUIPMENT_ERROR_SYNTACTICAL_ERROR_IN_TFT_OPERATION, [QMI_WDS_VERBOSE_CALL_END_REASON_3GPP_UNKNOWN_PDP_CONTEXT] = MM_MOBILE_EQUIPMENT_ERROR_UNKNOWN_PDP_CONTEXT, [QMI_WDS_VERBOSE_CALL_END_REASON_3GPP_FILTER_SEMANTIC_ERROR] = MM_MOBILE_EQUIPMENT_ERROR_SEMANTIC_ERRORS_IN_PACKET_FILTER, [QMI_WDS_VERBOSE_CALL_END_REASON_3GPP_FILTER_SYNTAX_ERROR] = MM_MOBILE_EQUIPMENT_ERROR_SYNTACTICAL_ERROR_IN_PACKET_FILTER, [QMI_WDS_VERBOSE_CALL_END_REASON_3GPP_PDP_WITHOUT_ACTIVE_TFT] = MM_MOBILE_EQUIPMENT_ERROR_PDP_CONTEXT_WITHOUT_TFT_ALREADY_ACTIVATED, [QMI_WDS_VERBOSE_CALL_END_REASON_3GPP_IPV4_ONLY_ALLOWED] = MM_MOBILE_EQUIPMENT_ERROR_IPV4_ONLY_ALLOWED, [QMI_WDS_VERBOSE_CALL_END_REASON_3GPP_IPV6_ONLY_ALLOWED] = MM_MOBILE_EQUIPMENT_ERROR_IPV6_ONLY_ALLOWED, [QMI_WDS_VERBOSE_CALL_END_REASON_3GPP_SINGLE_ADDRESS_BEARER_ONLY] = MM_MOBILE_EQUIPMENT_ERROR_SINGLE_ADDRESS_BEARERS_ONLY_ALLOWED, [QMI_WDS_VERBOSE_CALL_END_REASON_3GPP_ESM_INFO_NOT_RECEIVED] = MM_MOBILE_EQUIPMENT_ERROR_ESM_INFORMATION_NOT_RECEIVED, [QMI_WDS_VERBOSE_CALL_END_REASON_3GPP_PDN_CONNECTION_DOES_NOT_EXIST] = MM_MOBILE_EQUIPMENT_ERROR_PDN_CONNECTION_NONEXISTENT, [QMI_WDS_VERBOSE_CALL_END_REASON_3GPP_MULTIPLE_CONNECTION_TO_SAME_PDN_NOT_ALLOWED] = MM_MOBILE_EQUIPMENT_ERROR_MULTIPLE_PDN_CONNECTION_SAME_APN_NOT_ALLOWED, [QMI_WDS_VERBOSE_CALL_END_REASON_3GPP_INVALID_TRANSACTION_ID] = MM_MOBILE_EQUIPMENT_ERROR_INVALID_TRANSACTION_ID_VALUE, [QMI_WDS_VERBOSE_CALL_END_REASON_3GPP_MESSAGE_INCORRECT_SEMANTIC] = MM_MOBILE_EQUIPMENT_ERROR_SEMANTICALLY_INCORRECT_MESSAGE, [QMI_WDS_VERBOSE_CALL_END_REASON_3GPP_INVALID_MANDATORY_INFO] = MM_MOBILE_EQUIPMENT_ERROR_INVALID_MANDATORY_INFORMATION, [QMI_WDS_VERBOSE_CALL_END_REASON_3GPP_MESSAGE_TYPE_UNSUPPORTED] = MM_MOBILE_EQUIPMENT_ERROR_MESSAGE_TYPE_NOT_IMPLEMENTED, [QMI_WDS_VERBOSE_CALL_END_REASON_3GPP_MESSAGE_TYPE_NONCOMPATIBLE_STATE] = MM_MOBILE_EQUIPMENT_ERROR_MESSAGE_TYPE_NOT_COMPATIBLE_WITH_PROTOCOL_STATE, [QMI_WDS_VERBOSE_CALL_END_REASON_3GPP_UNKNOWN_INFO_ELEMENT] = MM_MOBILE_EQUIPMENT_ERROR_IE_NOT_IMPLEMENTED, [QMI_WDS_VERBOSE_CALL_END_REASON_3GPP_CONDITIONAL_IE_ERROR] = MM_MOBILE_EQUIPMENT_ERROR_CONDITIONAL_IE_ERROR, [QMI_WDS_VERBOSE_CALL_END_REASON_3GPP_MESSAGE_AND_PROTOCOL_STATE_UNCOMPATIBLE] = MM_MOBILE_EQUIPMENT_ERROR_MESSAGE_NOT_COMPATIBLE_WITH_PROTOCOL_STATE, [QMI_WDS_VERBOSE_CALL_END_REASON_3GPP_PROTOCOL_ERROR] = MM_MOBILE_EQUIPMENT_ERROR_UNSPECIFIED_PROTOCOL_ERROR, /* unmapped errors */ /* QMI_WDS_VERBOSE_CALL_END_REASON_3GPP_LLC_SNDCP_FAILURE */ /* QMI_WDS_VERBOSE_CALL_END_REASON_3GPP_APN_TYPE_CONFLICT */ /* QMI_WDS_VERBOSE_CALL_END_REASON_3GPP_INVALID_PROXY_CALL_SESSION_CONTROL_FUNCTION_ADDRESS */ }; static GError * error_from_wds_verbose_call_end_reason_3gpp (QmiWdsVerboseCallEndReason3gpp vcer_reason, const gchar *vcer_reason_str, gpointer log_object) { MMMobileEquipmentError error_code; GError *error; error_code = (vcer_reason < G_N_ELEMENTS (qmi_vcer_3gpp_errors)) ? qmi_vcer_3gpp_errors[vcer_reason] : 0; if (!error_code) return NULL; g_assert (vcer_reason_str); error = mm_mobile_equipment_error_for_code (error_code, log_object); g_prefix_error (&error, "%s: ", vcer_reason_str); return error; } GError * mm_error_from_wds_verbose_call_end_reason (QmiWdsVerboseCallEndReasonType vcer_type, guint vcer_reason, MMBearerIpFamily ip_type, gpointer log_object) { GError *error = NULL; const gchar *vcer_type_str; const gchar *vcer_reason_str; vcer_type_str = qmi_wds_verbose_call_end_reason_type_get_string (vcer_type); vcer_reason_str = qmi_wds_verbose_call_end_reason_get_string (vcer_type, vcer_reason); mm_obj_msg (log_object, "verbose call end reason (%u,%d): [%s] %s", vcer_type, vcer_reason, vcer_type_str, vcer_reason_str); switch (vcer_type) { case QMI_WDS_VERBOSE_CALL_END_REASON_TYPE_INTERNAL: error = error_from_wds_verbose_call_end_reason_internal (vcer_reason, vcer_reason_str, ip_type, log_object); break; case QMI_WDS_VERBOSE_CALL_END_REASON_TYPE_3GPP: error = error_from_wds_verbose_call_end_reason_3gpp (vcer_reason, vcer_reason_str, log_object); break; case QMI_WDS_VERBOSE_CALL_END_REASON_TYPE_MIP: case QMI_WDS_VERBOSE_CALL_END_REASON_TYPE_CM: case QMI_WDS_VERBOSE_CALL_END_REASON_TYPE_PPP: case QMI_WDS_VERBOSE_CALL_END_REASON_TYPE_EHRPD: case QMI_WDS_VERBOSE_CALL_END_REASON_TYPE_IPV6: default: break; } if (error) return error; return g_error_new (MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_UNKNOWN, "Call failed: %s error: %s", vcer_type_str ? vcer_type_str : "unknown", vcer_reason_str ? vcer_reason_str : "unknown"); } /*****************************************************************************/ gboolean mm_error_from_qmi_loc_indication_status (QmiLocIndicationStatus status, GError **error) { switch (status) { case QMI_LOC_INDICATION_STATUS_SUCCESS: return TRUE; case QMI_LOC_INDICATION_STATUS_GENERAL_FAILURE: g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "LOC service: general failure"); return FALSE; case QMI_LOC_INDICATION_STATUS_UNSUPPORTED: g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "LOC service: unsupported"); return FALSE; case QMI_LOC_INDICATION_STATUS_INVALID_PARAMETER: g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS, "LOC service: invalid parameter"); return FALSE; case QMI_LOC_INDICATION_STATUS_ENGINE_BUSY: g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_IN_PROGRESS, "LOC service: engine busy"); return FALSE; case QMI_LOC_INDICATION_STATUS_PHONE_OFFLINE: g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_WRONG_STATE, "LOC service: phone offline"); return FALSE; case QMI_LOC_INDICATION_STATUS_TIMEOUT: g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_ABORTED, "LOC service: timeout"); return FALSE; default: g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "LOC service: unknown failure"); return FALSE; } } /*****************************************************************************/ void mm_register_qmi_errors (void) { static gsize qmi_errors_registered = 0; if (!g_once_init_enter (&qmi_errors_registered)) return; /* QMI core errors */ mm_register_error_mapping (QMI_CORE_ERROR, QMI_CORE_ERROR_FAILED, MM_CORE_ERROR, MM_CORE_ERROR_FAILED); mm_register_error_mapping (QMI_CORE_ERROR, QMI_CORE_ERROR_WRONG_STATE, MM_CORE_ERROR, MM_CORE_ERROR_WRONG_STATE); mm_register_error_mapping (QMI_CORE_ERROR, QMI_CORE_ERROR_TIMEOUT, MM_CORE_ERROR, MM_CORE_ERROR_TIMEOUT); mm_register_error_mapping (QMI_CORE_ERROR, QMI_CORE_ERROR_INVALID_ARGS, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS); mm_register_error_mapping (QMI_CORE_ERROR, QMI_CORE_ERROR_INVALID_MESSAGE, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS); mm_register_error_mapping (QMI_CORE_ERROR, QMI_CORE_ERROR_TLV_NOT_FOUND, MM_CORE_ERROR, MM_CORE_ERROR_NOT_FOUND); mm_register_error_mapping (QMI_CORE_ERROR, QMI_CORE_ERROR_TLV_TOO_LONG, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS); mm_register_error_mapping (QMI_CORE_ERROR, QMI_CORE_ERROR_UNSUPPORTED, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED); mm_register_error_mapping (QMI_CORE_ERROR, QMI_CORE_ERROR_TLV_EMPTY, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS); mm_register_error_mapping (QMI_CORE_ERROR, QMI_CORE_ERROR_UNEXPECTED_MESSAGE, MM_CORE_ERROR, MM_CORE_ERROR_WRONG_STATE); mm_register_error_mapping (QMI_CORE_ERROR, QMI_CORE_ERROR_INVALID_DATA, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS); /* QMI protocol errors */ /* This should never happen, because QMI operations won't fail on this type of * error. But still, just in case, treat it as an error during normalization. */ mm_register_error_mapping (QMI_PROTOCOL_ERROR, QMI_PROTOCOL_ERROR_NONE, MM_CORE_ERROR, MM_CORE_ERROR_FAILED); /* This is not really an error in an operation, it is the modem reporting that the * operation was a no-op. For the purposes of returning errors to the user, we will * normalize it either way. */ mm_register_error_mapping (QMI_PROTOCOL_ERROR, QMI_PROTOCOL_ERROR_NO_EFFECT, MM_CORE_ERROR, MM_CORE_ERROR_FAILED); mm_register_error_mapping (QMI_PROTOCOL_ERROR, QMI_PROTOCOL_ERROR_MALFORMED_MESSAGE, MM_CORE_ERROR, MM_CORE_ERROR_PROTOCOL); mm_register_error_mapping (QMI_PROTOCOL_ERROR, QMI_PROTOCOL_ERROR_NO_MEMORY, MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_MEMORY_FULL); mm_register_error_mapping (QMI_PROTOCOL_ERROR, QMI_PROTOCOL_ERROR_INTERNAL, MM_CORE_ERROR, MM_CORE_ERROR_FAILED); mm_register_error_mapping (QMI_PROTOCOL_ERROR, QMI_PROTOCOL_ERROR_ABORTED, MM_CORE_ERROR, MM_CORE_ERROR_ABORTED); mm_register_error_mapping (QMI_PROTOCOL_ERROR, QMI_PROTOCOL_ERROR_CLIENT_IDS_EXHAUSTED, MM_CORE_ERROR, MM_CORE_ERROR_PROTOCOL); mm_register_error_mapping (QMI_PROTOCOL_ERROR, QMI_PROTOCOL_ERROR_UNABORTABLE_TRANSACTION, MM_CORE_ERROR, MM_CORE_ERROR_PROTOCOL); mm_register_error_mapping (QMI_PROTOCOL_ERROR, QMI_PROTOCOL_ERROR_INVALID_CLIENT_ID, MM_CORE_ERROR, MM_CORE_ERROR_PROTOCOL); /* QMI_PROTOCOL_ERROR_NO_THRESHOLDS_PROVIDED */ /* QMI_PROTOCOL_ERROR_INVALID_HANDLE */ /* QMI_PROTOCOL_ERROR_INVALID_PROFILE */ /* QMI_PROTOCOL_ERROR_INVALID_PIN_ID */ mm_register_error_mapping (QMI_PROTOCOL_ERROR, QMI_PROTOCOL_ERROR_INCORRECT_PIN, MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_SIM_PIN); mm_register_error_mapping (QMI_PROTOCOL_ERROR, QMI_PROTOCOL_ERROR_NO_NETWORK_FOUND, MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_NO_NETWORK); /* QMI_PROTOCOL_ERROR_CALL_FAILED */ /* QMI_PROTOCOL_ERROR_OUT_OF_CALL */ /* QMI_PROTOCOL_ERROR_NOT_PROVISIONED */ mm_register_error_mapping (QMI_PROTOCOL_ERROR, QMI_PROTOCOL_ERROR_MISSING_ARGUMENT, MM_CORE_ERROR, MM_CORE_ERROR_PROTOCOL); mm_register_error_mapping (QMI_PROTOCOL_ERROR, QMI_PROTOCOL_ERROR_ARGUMENT_TOO_LONG, MM_CORE_ERROR, MM_CORE_ERROR_PROTOCOL); mm_register_error_mapping (QMI_PROTOCOL_ERROR, QMI_PROTOCOL_ERROR_INVALID_TRANSACTION_ID, MM_CORE_ERROR, MM_CORE_ERROR_PROTOCOL); mm_register_error_mapping (QMI_PROTOCOL_ERROR, QMI_PROTOCOL_ERROR_DEVICE_IN_USE, MM_CORE_ERROR, MM_CORE_ERROR_RETRY); mm_register_error_mapping (QMI_PROTOCOL_ERROR, QMI_PROTOCOL_ERROR_NETWORK_UNSUPPORTED, MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_NOT_SUPPORTED); mm_register_error_mapping (QMI_PROTOCOL_ERROR, QMI_PROTOCOL_ERROR_DEVICE_UNSUPPORTED, MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_NOT_SUPPORTED); /* QMI_PROTOCOL_ERROR_NO_FREE_PROFILE */ mm_register_error_mapping (QMI_PROTOCOL_ERROR, QMI_PROTOCOL_ERROR_INVALID_PDP_TYPE, MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_UNKNOWN_PDP_ADDRESS_OR_TYPE); /* QMI_PROTOCOL_ERROR_INVALID_TECHNOLOGY_PREFERENCE */ /* QMI_PROTOCOL_ERROR_INVALID_PROFILE_TYPE */ /* QMI_PROTOCOL_ERROR_INVALID_SERVICE_TYPE */ /* QMI_PROTOCOL_ERROR_INVALID_REGISTER_ACTION */ /* QMI_PROTOCOL_ERROR_INVALID_PS_ATTACH_ACTION */ mm_register_error_mapping (QMI_PROTOCOL_ERROR, QMI_PROTOCOL_ERROR_AUTHENTICATION_FAILED, MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_USER_AUTHENTICATION_FAILED); mm_register_error_mapping (QMI_PROTOCOL_ERROR, QMI_PROTOCOL_ERROR_PIN_BLOCKED, MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_SIM_PUK); mm_register_error_mapping (QMI_PROTOCOL_ERROR, QMI_PROTOCOL_ERROR_PIN_ALWAYS_BLOCKED, MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_SIM_FAILURE); mm_register_error_mapping (QMI_PROTOCOL_ERROR, QMI_PROTOCOL_ERROR_UIM_UNINITIALIZED, MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_SIM_FAILURE); /* QMI_PROTOCOL_ERROR_MAXIMUM_QOS_REQUESTS_IN_USE */ /* QMI_PROTOCOL_ERROR_INCORRECT_FLOW_FILTER */ /* QMI_PROTOCOL_ERROR_NETWORK_QOS_UNAWARE */ /* QMI_PROTOCOL_ERROR_INVALID_QOS_ID */ mm_register_error_mapping (QMI_PROTOCOL_ERROR, QMI_PROTOCOL_ERROR_REQUESTED_NUMBER_UNSUPPORTED, MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_NOT_SUPPORTED); mm_register_error_mapping (QMI_PROTOCOL_ERROR, QMI_PROTOCOL_ERROR_INTERFACE_NOT_FOUND, MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_NOT_FOUND); /* QMI_PROTOCOL_ERROR_FLOW_SUSPENDED */ mm_register_error_mapping (QMI_PROTOCOL_ERROR, QMI_PROTOCOL_ERROR_INVALID_DATA_FORMAT, MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_PHONE_FAILURE); mm_register_error_mapping (QMI_PROTOCOL_ERROR, QMI_PROTOCOL_ERROR_GENERAL_ERROR, MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_PHONE_FAILURE); mm_register_error_mapping (QMI_PROTOCOL_ERROR, QMI_PROTOCOL_ERROR_UNKNOWN_ERROR, MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_UNKNOWN); mm_register_error_mapping (QMI_PROTOCOL_ERROR, QMI_PROTOCOL_ERROR_INVALID_ARGUMENT, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS); mm_register_error_mapping (QMI_PROTOCOL_ERROR, QMI_PROTOCOL_ERROR_INVALID_INDEX, MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_INVALID_INDEX); mm_register_error_mapping (QMI_PROTOCOL_ERROR, QMI_PROTOCOL_ERROR_NO_ENTRY, MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_NOT_FOUND); mm_register_error_mapping (QMI_PROTOCOL_ERROR, QMI_PROTOCOL_ERROR_DEVICE_STORAGE_FULL, MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_MEMORY_FULL); mm_register_error_mapping (QMI_PROTOCOL_ERROR, QMI_PROTOCOL_ERROR_DEVICE_NOT_READY, MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_PHONE_FAILURE); mm_register_error_mapping (QMI_PROTOCOL_ERROR, QMI_PROTOCOL_ERROR_NETWORK_NOT_READY, MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_NO_NETWORK); /* QMI_PROTOCOL_ERROR_WMS_CAUSE_CODE */ /* QMI_PROTOCOL_ERROR_WMS_MESSAGE_NOT_SENT */ /* QMI_PROTOCOL_ERROR_WMS_MESSAGE_DELIVERY_FAILURE */ /* QMI_PROTOCOL_ERROR_WMS_INVALID_MESSAGE_ID */ /* QMI_PROTOCOL_ERROR_WMS_ENCODING */ /* QMI_PROTOCOL_ERROR_AUTHENTICATION_LOCK */ mm_register_error_mapping (QMI_PROTOCOL_ERROR, QMI_PROTOCOL_ERROR_INVALID_TRANSITION, MM_CORE_ERROR, MM_CORE_ERROR_WRONG_STATE); /* QMI_PROTOCOL_ERROR_NOT_MCAST_INTERFACE */ /* QMI_PROTOCOL_ERROR_MAXIMUM_MCAST_REQUESTS_IN_USE */ /* QMI_PROTOCOL_ERROR_INVALID_MCAST_HANDLE */ /* QMI_PROTOCOL_ERROR_INVALID_IP_FAMILY_PREFERENCE */ /* QMI_PROTOCOL_ERROR_SESSION_INACTIVE */ /* QMI_PROTOCOL_ERROR_SESSION_INVALID */ /* QMI_PROTOCOL_ERROR_SESSION_OWNERSHIP */ mm_register_error_mapping (QMI_PROTOCOL_ERROR, QMI_PROTOCOL_ERROR_INSUFFICIENT_RESOURCES, MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_INSUFFICIENT_RESOURCES); /* QMI_PROTOCOL_ERROR_DISABLED */ mm_register_error_mapping (QMI_PROTOCOL_ERROR, QMI_PROTOCOL_ERROR_INVALID_OPERATION, MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_NOT_SUPPORTED); mm_register_error_mapping (QMI_PROTOCOL_ERROR, QMI_PROTOCOL_ERROR_INVALID_QMI_COMMAND, MM_CORE_ERROR, MM_CORE_ERROR_PROTOCOL); /* QMI_PROTOCOL_ERROR_WMS_T_PDU_TYPE */ /* QMI_PROTOCOL_ERROR_WMS_SMSC_ADDRESS */ mm_register_error_mapping (QMI_PROTOCOL_ERROR, QMI_PROTOCOL_ERROR_INFORMATION_UNAVAILABLE, MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_INVALID_MANDATORY_INFORMATION); mm_register_error_mapping (QMI_PROTOCOL_ERROR, QMI_PROTOCOL_ERROR_SEGMENT_TOO_LONG, MM_CORE_ERROR, MM_CORE_ERROR_PROTOCOL); mm_register_error_mapping (QMI_PROTOCOL_ERROR, QMI_PROTOCOL_ERROR_SEGMENT_ORDER, MM_CORE_ERROR, MM_CORE_ERROR_PROTOCOL); mm_register_error_mapping (QMI_PROTOCOL_ERROR, QMI_PROTOCOL_ERROR_BUNDLING_NOT_SUPPORTED, MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_NOT_SUPPORTED); /* QMI_PROTOCOL_ERROR_OPERATION_PARTIAL_FAILURE */ /* QMI_PROTOCOL_ERROR_POLICY_MISMATCH */ mm_register_error_mapping (QMI_PROTOCOL_ERROR, QMI_PROTOCOL_ERROR_SIM_FILE_NOT_FOUND, MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_NOT_FOUND); /* QMI_PROTOCOL_ERROR_EXTENDED_INTERNAL */ mm_register_error_mapping (QMI_PROTOCOL_ERROR, QMI_PROTOCOL_ERROR_ACCESS_DENIED, MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_NOT_ALLOWED); mm_register_error_mapping (QMI_PROTOCOL_ERROR, QMI_PROTOCOL_ERROR_HARDWARE_RESTRICTED, MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_NOT_ALLOWED); mm_register_error_mapping (QMI_PROTOCOL_ERROR, QMI_PROTOCOL_ERROR_ACK_NOT_SENT, MM_CORE_ERROR, MM_CORE_ERROR_WRONG_STATE); mm_register_error_mapping (QMI_PROTOCOL_ERROR, QMI_PROTOCOL_ERROR_INJECT_TIMEOUT, MM_CORE_ERROR, MM_CORE_ERROR_TIMEOUT); mm_register_error_mapping (QMI_PROTOCOL_ERROR, QMI_PROTOCOL_ERROR_INCOMPATIBLE_STATE, MM_CORE_ERROR, MM_CORE_ERROR_WRONG_STATE); /* QMI_PROTOCOL_ERROR_FDN_RESTRICT */ /* QMI_PROTOCOL_ERROR_SUPS_FAILURE_CASE */ mm_register_error_mapping (QMI_PROTOCOL_ERROR, QMI_PROTOCOL_ERROR_NO_RADIO, MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_NOT_ALLOWED); mm_register_error_mapping (QMI_PROTOCOL_ERROR, QMI_PROTOCOL_ERROR_NOT_SUPPORTED, MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_NOT_SUPPORTED); /* QMI_PROTOCOL_ERROR_NO_SUBSCRIPTION */ /* QMI_PROTOCOL_ERROR_CARD_CALL_CONTROL_FAILED */ mm_register_error_mapping (QMI_PROTOCOL_ERROR, QMI_PROTOCOL_ERROR_NETWORK_ABORTED, MM_CORE_ERROR, MM_CORE_ERROR_ABORTED); /* QMI_PROTOCOL_ERROR_MSG_BLOCKED */ /* QMI_PROTOCOL_ERROR_INVALID_SESSION_TYPE */ /* QMI_PROTOCOL_ERROR_INVALID_PB_TYPE */ mm_register_error_mapping (QMI_PROTOCOL_ERROR, QMI_PROTOCOL_ERROR_NO_SIM, MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_SIM_NOT_INSERTED); /* QMI_PROTOCOL_ERROR_PB_NOT_READY */ mm_register_error_mapping (QMI_PROTOCOL_ERROR, QMI_PROTOCOL_ERROR_PIN_RESTRICTION, MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_SIM_PIN); mm_register_error_mapping (QMI_PROTOCOL_ERROR, QMI_PROTOCOL_ERROR_PIN2_RESTRICTION, MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_SIM_PIN2); mm_register_error_mapping (QMI_PROTOCOL_ERROR, QMI_PROTOCOL_ERROR_PUK_RESTRICTION, MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_SIM_PUK); mm_register_error_mapping (QMI_PROTOCOL_ERROR, QMI_PROTOCOL_ERROR_PUK2_RESTRICTION, MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_SIM_PUK2); mm_register_error_mapping (QMI_PROTOCOL_ERROR, QMI_PROTOCOL_ERROR_PB_ACCESS_RESTRICTED, MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_NOT_ALLOWED); mm_register_error_mapping (QMI_PROTOCOL_ERROR, QMI_PROTOCOL_ERROR_PB_DELETE_IN_PROGRESS, MM_CORE_ERROR, MM_CORE_ERROR_IN_PROGRESS); mm_register_error_mapping (QMI_PROTOCOL_ERROR, QMI_PROTOCOL_ERROR_PB_TEXT_TOO_LONG, MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_TEXT_TOO_LONG); mm_register_error_mapping (QMI_PROTOCOL_ERROR, QMI_PROTOCOL_ERROR_PB_NUMBER_TOO_LONG, MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_DIAL_STRING_TOO_LONG); mm_register_error_mapping (QMI_PROTOCOL_ERROR, QMI_PROTOCOL_ERROR_PB_HIDDEN_KEY_RESTRICTION, MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_NOT_ALLOWED); /* QMI_PROTOCOL_ERROR_PB_NOT_AVAILABLE */ mm_register_error_mapping (QMI_PROTOCOL_ERROR, QMI_PROTOCOL_ERROR_DEVICE_MEMORY_ERROR, MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_MEMORY_FAILURE); mm_register_error_mapping (QMI_PROTOCOL_ERROR, QMI_PROTOCOL_ERROR_NO_PERMISSION, MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_NOT_ALLOWED); /* QMI_PROTOCOL_ERROR_TOO_SOON */ /* QMI_PROTOCOL_ERROR_TIME_NOT_ACQUIRED */ mm_register_error_mapping (QMI_PROTOCOL_ERROR, QMI_PROTOCOL_ERROR_OPERATION_IN_PROGRESS, MM_CORE_ERROR, MM_CORE_ERROR_IN_PROGRESS); /* QMI_PROTOCOL_ERROR_FW_WRITE_FAILED */ /* QMI_PROTOCOL_ERROR_FW_INFO_READ_FAILED */ mm_register_error_mapping (QMI_PROTOCOL_ERROR, QMI_PROTOCOL_ERROR_FW_FILE_NOT_FOUND, MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_NOT_FOUND); mm_register_error_mapping (QMI_PROTOCOL_ERROR, QMI_PROTOCOL_ERROR_FW_DIR_NOT_FOUND, MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_NOT_FOUND); /* QMI_PROTOCOL_ERROR_FW_ALREADY_ACTIVATED */ /* QMI_PROTOCOL_ERROR_FW_CANNOT_GENERIC_IMAGE */ /* QMI_PROTOCOL_ERROR_FW_FILE_OPEN_FAILED */ /* QMI_PROTOCOL_ERROR_FW_UPDATE_DISCONTINUOUS_FRAME */ /* QMI_PROTOCOL_ERROR_FW_UPDATE_FAILED */ /* QMI_PROTOCOL_ERROR_CAT_EVENT_REGISTRATION_FAILED */ /* QMI_PROTOCOL_ERROR_CAT_INVALID_TERMINAL_RESPONSE */ /* QMI_PROTOCOL_ERROR_CAT_INVALID_ENVELOPE_COMMAND */ /* QMI_PROTOCOL_ERROR_CAT_ENVELOPE_COMMAND_BUSY */ /* QMI_PROTOCOL_ERROR_CAT_ENVELOPE_COMMAND_FAILED */ g_once_init_leave (&qmi_errors_registered, 1); } ModemManager-1.23.4-dev/src/mm-modem-helpers-qmi.h000066400000000000000000000372341456466623000216320ustar00rootroot00000000000000/* -*- 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) 2012 Google, Inc. * Copyright (c) 2021 Qualcomm Innovation Center, Inc. */ #ifndef MM_MODEM_HELPERS_QMI_H #define MM_MODEM_HELPERS_QMI_H #include #include #include #include "mm-port.h" #define MM_MODEM_CAPABILITY_MULTIMODE (MM_MODEM_CAPABILITY_GSM_UMTS | MM_MODEM_CAPABILITY_CDMA_EVDO) /*****************************************************************************/ /* QMI/DMS to MM translations */ MMModemCapability mm_modem_capability_from_qmi_radio_interface (QmiDmsRadioInterface network, gpointer log_object); MMModemMode mm_modem_mode_from_qmi_radio_interface (QmiDmsRadioInterface network, gpointer log_object); MMModemLock mm_modem_lock_from_qmi_uim_pin_status (QmiDmsUimPinStatus status, gboolean pin1); gboolean mm_pin_enabled_from_qmi_uim_pin_status (QmiDmsUimPinStatus status); QmiDmsUimFacility mm_3gpp_facility_to_qmi_uim_facility (MMModem3gppFacility mm); GArray *mm_modem_bands_from_qmi_band_capabilities (QmiDmsBandCapability qmi_bands, QmiDmsLteBandCapability qmi_lte_bands, GArray *extended_qmi_lte_bands, GArray *qmi_nr5g_bands, gpointer log_object); /*****************************************************************************/ /* QMI/NAS to MM translations */ MMModemAccessTechnology mm_modem_access_technology_from_qmi_radio_interface (QmiNasRadioInterface interface); MMModemAccessTechnology mm_modem_access_technologies_from_qmi_radio_interface_array (GArray *radio_interfaces); MMModemAccessTechnology mm_modem_access_technology_from_qmi_data_capability (QmiNasDataCapability cap); MMModemAccessTechnology mm_modem_access_technologies_from_qmi_data_capability_array (GArray *data_capabilities); MMModemMode mm_modem_mode_from_qmi_nas_radio_interface (QmiNasRadioInterface iface); MMModemMode mm_modem_mode_from_qmi_radio_technology_preference (QmiNasRadioTechnologyPreference qmi); QmiNasRadioTechnologyPreference mm_modem_mode_to_qmi_radio_technology_preference (MMModemMode mode, gboolean is_cdma); MMModemMode mm_modem_mode_from_qmi_rat_mode_preference (QmiNasRatModePreference qmi); QmiNasRatModePreference mm_modem_mode_to_qmi_rat_mode_preference (MMModemMode mode, gboolean is_cdma, gboolean is_3gpp); MMModemCapability mm_modem_capability_from_qmi_rat_mode_preference (QmiNasRatModePreference qmi); QmiNasRatModePreference mm_modem_capability_to_qmi_rat_mode_preference (MMModemCapability caps); GArray *mm_modem_capability_to_qmi_acquisition_order_preference (MMModemCapability caps); GArray *mm_modem_mode_to_qmi_acquisition_order_preference (MMModemMode allowed, MMModemMode preferred, GArray *all); MMModemCapability mm_modem_capability_from_qmi_radio_technology_preference (QmiNasRadioTechnologyPreference qmi); QmiNasRadioTechnologyPreference mm_modem_capability_to_qmi_radio_technology_preference (MMModemCapability caps); MMModemCapability mm_modem_capability_from_qmi_band_preference (QmiNasBandPreference qmi); MMModemMode mm_modem_mode_from_qmi_gsm_wcdma_acquisition_order_preference (QmiNasGsmWcdmaAcquisitionOrderPreference qmi, gpointer log_object); QmiNasGsmWcdmaAcquisitionOrderPreference mm_modem_mode_to_qmi_gsm_wcdma_acquisition_order_preference (MMModemMode mode, gpointer log_object); GArray *mm_modem_bands_from_qmi_rf_band_information_array (GArray *info_array); GArray *mm_modem_bands_from_qmi_band_preference (QmiNasBandPreference qmi_bands, QmiNasLteBandPreference qmi_lte_bands, const guint64 *extended_qmi_lte_bands, guint extended_qmi_lte_bands_size, const guint64 *qmi_nr5g_bands, guint qmi_nr5g_bands_size, gpointer log_object); void mm_modem_bands_to_qmi_band_preference (GArray *mm_bands, QmiNasBandPreference *qmi_bands, QmiNasLteBandPreference *qmi_lte_bands, guint64 *extended_qmi_lte_bands, guint extended_qmi_lte_bands_size, guint64 *qmi_nr5g_bands, guint qmi_nr5g_bands_size, gpointer log_object); MMModem3gppRegistrationState mm_modem_3gpp_registration_state_from_qmi_registration_state (QmiNasAttachState attach_state, QmiNasRegistrationState registration_state, gboolean roaming); MMModemCdmaRegistrationState mm_modem_cdma_registration_state_from_qmi_registration_state (QmiNasRegistrationState registration_state); MMModemCdmaActivationState mm_modem_cdma_activation_state_from_qmi_activation_state (QmiDmsActivationState state); /*****************************************************************************/ /* QMI NAS System Info processor */ void mm_modem_registration_state_from_qmi_system_info (QmiMessageNasGetSystemInfoOutput *response_output, QmiIndicationNasSystemInfoOutput *indication_output, MMModem3gppRegistrationState *out_cs_registration_state, MMModem3gppRegistrationState *out_ps_registration_state, MMModem3gppRegistrationState *out_eps_registration_state, MMModem3gppRegistrationState *out_5gs_registration_state, guint16 *out_lac, guint16 *out_tac, guint32 *out_cid, gchar **out_operator_id, MMModemAccessTechnology *out_act, gpointer log_object); /*****************************************************************************/ /* QMI/WMS to MM translations */ QmiWmsStorageType mm_sms_storage_to_qmi_storage_type (MMSmsStorage storage); MMSmsStorage mm_sms_storage_from_qmi_storage_type (QmiWmsStorageType qmi_storage); MMSmsState mm_sms_state_from_qmi_message_tag (QmiWmsMessageTagType tag); /*****************************************************************************/ /* QMI/WDS to MM translations */ QmiWdsAuthentication mm_bearer_allowed_auth_to_qmi_authentication (MMBearerAllowedAuth auth, gpointer log_object, GError **error); MMBearerAllowedAuth mm_bearer_allowed_auth_from_qmi_authentication (QmiWdsAuthentication auth); MMBearerIpFamily mm_bearer_ip_family_from_qmi_ip_support_type (QmiWdsIpSupportType ip_support_type); MMBearerIpFamily mm_bearer_ip_family_from_qmi_pdp_type (QmiWdsPdpType pdp_type); gboolean mm_bearer_ip_family_to_qmi_pdp_type (MMBearerIpFamily ip_family, QmiWdsPdpType *out_pdp_type); QmiWdsApnTypeMask mm_bearer_apn_type_to_qmi_apn_type (MMBearerApnType apn_type, gpointer log_object); MMBearerApnType mm_bearer_apn_type_from_qmi_apn_type (QmiWdsApnTypeMask apn_type); /*****************************************************************************/ /* QMI/WDA to MM translations */ QmiDataEndpointType mm_port_net_driver_to_qmi_endpoint_type (const gchar *net_driver); /*****************************************************************************/ /* QMI/OMA to MM translations */ MMOmaSessionType mm_oma_session_type_from_qmi_oma_session_type (QmiOmaSessionType qmi_session_type); QmiOmaSessionType mm_oma_session_type_to_qmi_oma_session_type (MMOmaSessionType mm_session_type); MMOmaSessionState mm_oma_session_state_from_qmi_oma_session_state (QmiOmaSessionState qmi_session_state); MMOmaSessionStateFailedReason mm_oma_session_state_failed_reason_from_qmi_oma_session_failed_reason (QmiOmaSessionFailedReason qmi_session_failed_reason); /*****************************************************************************/ /* Utility to gather current capabilities from various sources */ typedef struct { /* Whether this is a multimode device or not */ gboolean multimode; /* NAS System Selection Preference */ QmiNasRatModePreference nas_ssp_mode_preference_mask; /* NAS Technology Preference */ QmiNasRadioTechnologyPreference nas_tp_mask; /* DMS Capabilities */ MMModemCapability dms_capabilities; } MMQmiCurrentCapabilitiesContext; MMModemCapability mm_current_capability_from_qmi_current_capabilities_context (MMQmiCurrentCapabilitiesContext *ctx, gpointer log_object); /*****************************************************************************/ /* Utility to build list of supported capabilities from various sources */ typedef struct { /* Whether this is a multimode device or not */ gboolean multimode; /* NAS System Selection Preference */ gboolean nas_ssp_supported; /* NAS Technology Preference */ gboolean nas_tp_supported; /* DMS Capabilities */ MMModemCapability dms_capabilities; } MMQmiSupportedCapabilitiesContext; GArray *mm_supported_capabilities_from_qmi_supported_capabilities_context (MMQmiSupportedCapabilitiesContext *ctx, gpointer log_object); /*****************************************************************************/ /* Utility to build list of supported modes from various sources */ typedef struct { /* Whether this is a multimode device or not */ gboolean multimode; /* NAS System Selection Preference */ gboolean nas_ssp_supported; /* NAS Technology Preference */ gboolean nas_tp_supported; /* Mask with all supported modes */ MMModemMode all; /* Current Capabilities */ MMModemCapability current_capabilities; } MMQmiSupportedModesContext; GArray *mm_supported_modes_from_qmi_supported_modes_context (MMQmiSupportedModesContext *ctx, gpointer log_object); /*****************************************************************************/ /* QMI unique id manipulation */ gchar *mm_qmi_unique_id_to_firmware_unique_id (GArray *qmi_unique_id, GError **error); GArray *mm_firmware_unique_id_to_qmi_unique_id (const gchar *unique_id, GError **error); /*****************************************************************************/ /* Common UIM Get Card Status parsing */ gboolean mm_qmi_uim_get_card_status_output_parse (gpointer log_object, QmiMessageUimGetCardStatusOutput *output, MMModemLock *o_lock, QmiUimPinState *o_pin1_state, guint *o_pin1_retries, guint *o_puk1_retries, QmiUimPinState *o_pin2_state, guint *o_pin2_retries, guint *o_puk2_retries, guint *o_pers_retries, GError **error); /*****************************************************************************/ /* UIM Get Configuration parsing */ gboolean mm_qmi_uim_get_configuration_output_parse (gpointer log_object, QmiMessageUimGetConfigurationOutput *output, MMModem3gppFacility *o_lock, GError **error); gboolean qmi_personalization_feature_from_mm_modem_3gpp_facility (MMModem3gppFacility facility, QmiUimCardApplicationPersonalizationFeature *o_feature); /*****************************************************************************/ /* MM error translations */ void mm_register_qmi_errors (void); GError *mm_error_from_wds_verbose_call_end_reason (QmiWdsVerboseCallEndReasonType vcer_type, guint vcer_reason, MMBearerIpFamily ip_type, gpointer log_object); gboolean mm_error_from_qmi_loc_indication_status (QmiLocIndicationStatus status, GError **error); #endif /* MM_MODEM_HELPERS_QMI_H */ ModemManager-1.23.4-dev/src/mm-modem-helpers.c000066400000000000000000005261661456466623000210500ustar00rootroot00000000000000/* -*- 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 - 2012 Red Hat, Inc. * Copyright (C) 2012 Google, Inc. * Copyright (c) 2021 Qualcomm Innovation Center, Inc. */ #include #include #include #include #include #include #include #include #include #define _LIBMM_INSIDE_MM #include #include "mm-sms-part.h" #include "mm-common-helpers.h" #include "mm-modem-helpers.h" #include "mm-helper-enums-types.h" #include "mm-log-object.h" /*****************************************************************************/ gchar * mm_strip_quotes (gchar *str) { gsize len; if (!str) return NULL; len = strlen (str); if ((len >= 2) && (str[0] == '"') && (str[len - 1] == '"')) { str[0] = ' '; str[len - 1] = ' '; } return g_strstrip (str); } const gchar * mm_strip_tag (const gchar *str, const gchar *cmd) { const gchar *p = str; if (p) { if (!strncmp (p, cmd, strlen (cmd))) p += strlen (cmd); while (isspace (*p)) p++; } return p; } /*****************************************************************************/ gchar ** mm_split_string_groups (const gchar *str) { GPtrArray *array; const gchar *start; const gchar *next; array = g_ptr_array_new (); /* * Manually parse splitting groups. Groups may be single elements, or otherwise * lists given between parenthesis, e.g.: * * ("SM","ME"),("SM","ME"),("SM","ME") * "SM","SM","SM" * "SM",("SM","ME"),("SM","ME") */ /* Iterate string splitting groups */ for (start = str; start; start = next) { gchar *item; gssize len = -1; /* skip leading whitespaces */ while (*start == ' ') start++; if (*start == '(') { start++; next = strchr (start, ')'); if (next) { len = next - start; next = strchr (next, ','); if (next) next++; } } else { next = strchr (start, ','); if (next) { len = next - start; next++; } } if (len < 0) item = g_strdup (start); else item = g_strndup (start, len); g_ptr_array_add (array, item); } if (array->len > 0) { g_ptr_array_add (array, NULL); return (gchar **) g_ptr_array_free (array, FALSE); } g_ptr_array_unref (array); return NULL; } /*****************************************************************************/ static int uint_compare_func (gconstpointer a, gconstpointer b) { return (*(guint *)a - *(guint *)b); } GArray * mm_parse_uint_list (const gchar *str, GError **error) { GArray *array; gchar *dupstr; gchar *aux; GError *inner_error = NULL; if (!str || !str[0]) return NULL; /* Parses into a GArray of guints, the list of numbers given in the string, * also supporting number intervals. * E.g.: * 1-6 --> 1,2,3,4,5,6 * 1,2,4-6 --> 1,2,4,5,6 */ array = g_array_new (FALSE, FALSE, sizeof (guint)); aux = dupstr = g_strdup (str); while (aux) { gchar *next; gchar *interval; next = strchr (aux, ','); if (next) { *next = '\0'; next++; } interval = strchr (aux, '-'); if (interval) { guint start = 0; guint stop = 0; *interval = '\0'; interval++; if (!mm_get_uint_from_str (aux, &start)) { inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "couldn't parse interval start integer: '%s'", aux); goto out; } if (!mm_get_uint_from_str (interval, &stop)) { inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "couldn't parse interval stop integer: '%s'", interval); goto out; } if (start > stop) { inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "interval start (%u) cannot be bigger than interval stop (%u)", start, stop); goto out; } for (; start <= stop; start++) g_array_append_val (array, start); } else { guint num; if (!mm_get_uint_from_str (aux, &num)) { inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "couldn't parse integer: '%s'", aux); goto out; } g_array_append_val (array, num); } aux = next; } if (!array->len) inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "couldn't parse list of integers: '%s'", str); else g_array_sort (array, uint_compare_func); out: g_free (dupstr); if (inner_error) { g_propagate_error (error, inner_error); g_array_unref (array); return NULL; } return array; } /*****************************************************************************/ guint mm_count_bits_set (gulong number) { guint c; for (c = 0; number; c++) number &= number - 1; return c; } guint mm_find_bit_set (gulong number) { guint c = 0; for (c = 0; !(number & 0x1); c++) number >>= 1; return c; } /*****************************************************************************/ gchar * mm_create_device_identifier (guint vid, guint pid, gpointer log_object, const gchar *ati, const gchar *ati1, const gchar *gsn, const gchar *revision, const gchar *model, const gchar *manf) { g_autoptr(GString) devid = NULL; g_autoptr(GString) msg = NULL; g_autoptr(GChecksum) sum = NULL; const gchar *ret; gchar *p = NULL; gchar str_vid[10], str_pid[10]; /* Build up the device identifier */ devid = g_string_sized_new (50); if (ati) g_string_append (devid, ati); if (ati1) { /* Only append "ATI1" if it's differnet than "ATI" */ if (!ati || (strcmp (ati, ati1) != 0)) g_string_append (devid, ati1); } if (gsn) g_string_append (devid, gsn); if (revision) g_string_append (devid, revision); if (model) g_string_append (devid, model); if (manf) g_string_append (devid, manf); if (!strlen (devid->str)) return NULL; p = devid->str; msg = g_string_sized_new (strlen (devid->str) + 17); sum = g_checksum_new (G_CHECKSUM_SHA1); if (vid) { snprintf (str_vid, sizeof (str_vid) - 1, "%08x", vid); g_checksum_update (sum, (const guchar *) &str_vid[0], strlen (str_vid)); g_string_append_printf (msg, "%08x", vid); } if (pid) { snprintf (str_pid, sizeof (str_pid) - 1, "%08x", pid); g_checksum_update (sum, (const guchar *) &str_pid[0], strlen (str_pid)); g_string_append_printf (msg, "%08x", pid); } while (*p) { /* Strip spaces and linebreaks */ if (!isblank (*p) && !isspace (*p) && isascii (*p)) { g_checksum_update (sum, (const guchar *) p, 1); g_string_append_c (msg, *p); } p++; } ret = g_checksum_get_string (sum); mm_obj_dbg (log_object, "device identifier built: %s -> %s", msg->str, ret); return g_strdup (ret); } /*****************************************************************************/ guint mm_netmask_to_cidr (const gchar *netmask) { guint32 num = 0; inet_pton (AF_INET, netmask, &num); return mm_count_bits_set (num); } /*****************************************************************************/ GArray * mm_filter_current_bands (const GArray *supported_bands, const GArray *current_bands) { /* We will assure that the list given in 'current' bands maps the list * given in 'supported' bands, unless 'UNKNOWN' or 'ANY' is given, of * course */ guint i; GArray *filtered; if (!supported_bands || supported_bands->len == 0 || !current_bands || current_bands->len == 0) return NULL; if (supported_bands->len == 1 && (g_array_index (supported_bands, MMModemBand, 0) == MM_MODEM_BAND_UNKNOWN || g_array_index (supported_bands, MMModemBand, 0) == MM_MODEM_BAND_ANY)) return NULL; if (current_bands->len == 1 && (g_array_index (current_bands, MMModemBand, 0) == MM_MODEM_BAND_UNKNOWN || g_array_index (current_bands, MMModemBand, 0) == MM_MODEM_BAND_ANY)) return NULL; filtered = g_array_sized_new (FALSE, FALSE, sizeof (MMModemBand), current_bands->len); for (i = 0; i < current_bands->len; i++) { guint j; for (j = 0; j < supported_bands->len; j++) { if (g_array_index (supported_bands, MMModemBand, j) == g_array_index (current_bands, MMModemBand, i)) { g_array_append_val (filtered, g_array_index (current_bands, MMModemBand, i)); /* Found */ break; } } } if (filtered->len == 0) { g_array_unref (filtered); return NULL; } return filtered; } /*****************************************************************************/ GArray * mm_filter_supported_modes (const GArray *all, const GArray *supported_combinations, gpointer log_object) { MMModemModeCombination all_item; guint i; GArray *filtered_combinations; g_return_val_if_fail (all != NULL, NULL); g_return_val_if_fail (all->len == 1, NULL); g_return_val_if_fail (supported_combinations != NULL, NULL); mm_obj_dbg (log_object, "filtering %u supported mode combinations with %u modes", supported_combinations->len, all->len); all_item = g_array_index (all, MMModemModeCombination, 0); g_return_val_if_fail (all_item.allowed != MM_MODEM_MODE_NONE, NULL); /* We will filter out all combinations which have modes not listed in 'all' */ filtered_combinations = g_array_sized_new (FALSE, FALSE, sizeof (MMModemModeCombination), supported_combinations->len); for (i = 0; i < supported_combinations->len; i++) { MMModemModeCombination *mode; mode = &g_array_index (supported_combinations, MMModemModeCombination, i); if (!(mode->allowed & ~all_item.allowed)) { /* Compare only 'allowed', *not* preferred. If there is at least one item with allowed * containing all supported modes, we're already good to go. This allows us to have a * default with preferred != NONE (e.g. Wavecom 2G modem with allowed=CS+2G and * preferred=2G */ g_array_append_val (filtered_combinations, *mode); } } if (filtered_combinations->len == 0) mm_obj_warn (log_object, "all supported mode combinations were filtered out"); mm_obj_dbg (log_object, "device supports %u different mode combinations", filtered_combinations->len); return filtered_combinations; } /*****************************************************************************/ static const gchar bcd_chars[] = "0123456789\0\0\0\0\0\0"; gchar * mm_bcd_to_string (const guint8 *bcd, gsize bcd_len, gboolean low_nybble_first) { GString *str; gsize i; g_return_val_if_fail (bcd != NULL, NULL); str = g_string_sized_new (bcd_len * 2 + 1); for (i = 0 ; i < bcd_len; i++) { if (low_nybble_first) str = g_string_append_c (str, bcd_chars[bcd[i] & 0xF]); str = g_string_append_c (str, bcd_chars[(bcd[i] >> 4) & 0xF]); if (!low_nybble_first) str = g_string_append_c (str, bcd_chars[bcd[i] & 0xF]); } return g_string_free (str, FALSE); } /*****************************************************************************/ GRegex * mm_voice_ring_regex_get (void) { /* Example: * RING */ return g_regex_new ("\\r\\nRING(?:\\r)?\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); } GRegex * mm_voice_cring_regex_get (void) { /* Example: * +CRING: VOICE * +CRING: DATA */ return g_regex_new ("\\r\\n\\+CRING:\\s*(\\S+)\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); } GRegex * mm_voice_clip_regex_get (void) { /* * Only first 2 fields are mandatory: * +CLIP: ,[,,[,[][,]]] * * Example: * +CLIP: "+393351391306",145,,,,0 * \_ Number \_ Type */ return g_regex_new ("\\r\\n\\+CLIP:\\s*([^,\\s]*)\\s*,\\s*(\\d+)\\s*,?(.*)\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); } GRegex * mm_voice_ccwa_regex_get (void) { /* * Only first 3 fields are mandatory, but we read only the first one * +CCWA: ,,,[][,[,,[,]]] * * Example: * +CCWA: "+393351391306",145,1 * \_ Number \_ Type */ return g_regex_new ("\\r\\n\\+CCWA:\\s*([^,\\s]*)\\s*,\\s*(\\d+)\\s*,\\s*(\\d+)\\s*,?(.*)\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); } static void call_info_free (MMCallInfo *info) { if (!info) return; g_free (info->number); g_slice_free (MMCallInfo, info); } gboolean mm_3gpp_parse_clcc_response (const gchar *str, gpointer log_object, GList **out_list, GError **error) { g_autoptr(GRegex) r = NULL; g_autoptr(GMatchInfo) match_info = NULL; GList *list = NULL; GError *inner_error = NULL; static const MMCallDirection call_direction[] = { [0] = MM_CALL_DIRECTION_OUTGOING, [1] = MM_CALL_DIRECTION_INCOMING, }; static const MMCallState call_state[] = { [0] = MM_CALL_STATE_ACTIVE, [1] = MM_CALL_STATE_HELD, [2] = MM_CALL_STATE_DIALING, /* Dialing (MOC) */ [3] = MM_CALL_STATE_RINGING_OUT, /* Alerting (MOC) */ [4] = MM_CALL_STATE_RINGING_IN, /* Incoming (MTC) */ [5] = MM_CALL_STATE_WAITING, /* Waiting (MTC) */ /* This next call state number isn't defined by 3GPP, because it * doesn't make sense to have it when reporting a full list of calls * via +CLCC (i.e. the absence of the call would mean it's terminated). * But, this value may be used by other implementations (e.g. SimTech * plugin) to report that a call is terminated even when the full * call list isn't being reported. So, let's support it in the generic, * parser, even if not strictly standard. */ [6] = MM_CALL_STATE_TERMINATED, }; g_assert (out_list); /* * 1 2 3 4 5 6 7 8 9 10 * +CLCC: ,,,,[,,[,[,[,]]]] * +CLCC: ,,,,[,,[,[,[,]]]] * ... */ r = g_regex_new ("\\+CLCC:\\s*(\\d+),\\s*(\\d+),\\s*(\\d+),\\s*(\\d+),\\s*(\\d+)" /* mandatory fields */ "(?:,\\s*([^,]*),\\s*(\\d+)" /* number and type */ "(?:,\\s*([^,]*)" /* alpha */ "(?:,\\s*(\\d*)" /* priority */ "(?:,\\s*(\\d*)" /* CLI validity */ ")?)?)?)?$", G_REGEX_RAW | G_REGEX_MULTILINE | G_REGEX_NEWLINE_CRLF, G_REGEX_MATCH_NEWLINE_CRLF, NULL); g_assert (r != NULL); g_regex_match_full (r, str, strlen (str), 0, 0, &match_info, &inner_error); if (inner_error) goto out; /* Parse the results */ while (g_match_info_matches (match_info)) { MMCallInfo *call_info; guint aux; call_info = g_slice_new0 (MMCallInfo); if (!mm_get_uint_from_match_info (match_info, 1, &call_info->index)) { mm_obj_warn (log_object, "couldn't parse call index from +CLCC line"); goto next; } if (!mm_get_uint_from_match_info (match_info, 2, &aux) || (aux >= G_N_ELEMENTS (call_direction))) { mm_obj_warn (log_object, "couldn't parse call direction from +CLCC line"); goto next; } call_info->direction = call_direction[aux]; if (!mm_get_uint_from_match_info (match_info, 3, &aux) || (aux >= G_N_ELEMENTS (call_state))) { mm_obj_warn (log_object, "couldn't parse call state from +CLCC line"); goto next; } call_info->state = call_state[aux]; if (!mm_get_uint_from_match_info (match_info, 4, &aux)) { mm_obj_warn (log_object, "couldn't parse mode from +CLCC line"); goto next; } /* * Skip calls in Fax-only and DATA-only mode (3GPP TS 27.007): * 0: Voice * 1: Data * 2: Fax * 3: Voice followed by data, voice mode * 4: Alternating voice/data, voice mode * 5: Alternating voice/fax, voice mode * 6: Voice followed by data, data mode * 7: Alternating voice/data, data mode * 8: Alternating voice/fax, fax mode * 9: unknown */ if (aux != 0 && aux != 3 && aux != 4 && aux != 5) { mm_obj_dbg (log_object, "+CLCC line is not a voice call, skipping."); goto next; } if (g_match_info_get_match_count (match_info) >= 7) call_info->number = mm_get_string_unquoted_from_match_info (match_info, 6); list = g_list_append (list, call_info); call_info = NULL; next: call_info_free (call_info); g_match_info_next (match_info, NULL); } out: if (inner_error) { mm_3gpp_call_info_list_free (list); g_propagate_error (error, inner_error); return FALSE; } *out_list = list; return TRUE; } void mm_3gpp_call_info_list_free (GList *call_info_list) { g_list_free_full (call_info_list, (GDestroyNotify) call_info_free); } /*************************************************************************/ static MMFlowControl flow_control_array_to_mask (GArray *array, const gchar *item, gpointer log_object) { MMFlowControl mask = MM_FLOW_CONTROL_UNKNOWN; guint i; for (i = 0; i < array->len; i++) { guint mode; mode = g_array_index (array, guint, i); switch (mode) { case 0: mm_obj_dbg (log_object, "%s supports no flow control", item); mask |= MM_FLOW_CONTROL_NONE; break; case 1: mm_obj_dbg (log_object, "%s supports XON/XOFF flow control", item); mask |= MM_FLOW_CONTROL_XON_XOFF; break; case 2: mm_obj_dbg (log_object, "%s supports RTS/CTS flow control", item); mask |= MM_FLOW_CONTROL_RTS_CTS; break; default: break; } } return mask; } static MMFlowControl flow_control_match_info_to_mask (GMatchInfo *match_info, guint index, const gchar *item, gpointer log_object, GError **error) { MMFlowControl mask = MM_FLOW_CONTROL_UNKNOWN; gchar *aux = NULL; GArray *array = NULL; if (!(aux = mm_get_string_unquoted_from_match_info (match_info, index))) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Error retrieving list of supported %s flow control methods", item); goto out; } if (!(array = mm_parse_uint_list (aux, error))) { g_prefix_error (error, "Error parsing list of supported %s flow control methods: ", item); goto out; } if ((mask = flow_control_array_to_mask (array, item, log_object)) == MM_FLOW_CONTROL_UNKNOWN) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "No known %s flow control method given", item); goto out; } out: g_clear_pointer (&aux, g_free); g_clear_pointer (&array, g_array_unref); return mask; } MMFlowControl mm_parse_ifc_test_response (const gchar *response, gpointer log_object, GError **error) { g_autoptr(GRegex) r = NULL; g_autoptr(GMatchInfo) match_info = NULL; GError *inner_error = NULL; MMFlowControl te_mask = MM_FLOW_CONTROL_UNKNOWN; MMFlowControl ta_mask = MM_FLOW_CONTROL_UNKNOWN; MMFlowControl mask = MM_FLOW_CONTROL_UNKNOWN; r = g_regex_new ("(?:\\+IFC:)?\\s*\\((.*)\\),\\((.*)\\)(?:\\r\\n)?", 0, 0, NULL); g_assert (r != NULL); g_regex_match_full (r, response, strlen (response), 0, 0, &match_info, &inner_error); if (inner_error) goto out; if (!g_match_info_matches (match_info)) { inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't match response"); goto out; } /* Parse TE flow control methods */ if ((te_mask = flow_control_match_info_to_mask (match_info, 1, "TE", log_object, &inner_error)) == MM_FLOW_CONTROL_UNKNOWN) goto out; /* Parse TA flow control methods */ if ((ta_mask = flow_control_match_info_to_mask (match_info, 2, "TA", log_object, &inner_error)) == MM_FLOW_CONTROL_UNKNOWN) goto out; /* Only those methods in both TA and TE will be the ones we report */ mask = te_mask & ta_mask; out: if (inner_error) g_propagate_error (error, inner_error); return mask; } MMFlowControl mm_flow_control_from_string (const gchar *str, GError **error) { GFlagsClass *flags_class; guint value; guint i; flags_class = G_FLAGS_CLASS (g_type_class_ref (MM_TYPE_FLOW_CONTROL)); for (i = 0; flags_class->values[i].value_nick; i++) { if (!g_ascii_strcasecmp (str, flags_class->values[i].value_nick)) { value = flags_class->values[i].value; g_type_class_unref (flags_class); return value; } } g_type_class_unref (flags_class); g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS, "Couldn't match '%s' with a valid MMFlowControl value", str); return MM_FLOW_CONTROL_UNKNOWN; } /*************************************************************************/ gboolean mm_modem_3gpp_registration_state_is_registered (MMModem3gppRegistrationState state) { switch (state) { case MM_MODEM_3GPP_REGISTRATION_STATE_HOME: case MM_MODEM_3GPP_REGISTRATION_STATE_ROAMING: case MM_MODEM_3GPP_REGISTRATION_STATE_HOME_SMS_ONLY: case MM_MODEM_3GPP_REGISTRATION_STATE_ROAMING_SMS_ONLY: case MM_MODEM_3GPP_REGISTRATION_STATE_EMERGENCY_ONLY: case MM_MODEM_3GPP_REGISTRATION_STATE_HOME_CSFB_NOT_PREFERRED: case MM_MODEM_3GPP_REGISTRATION_STATE_ROAMING_CSFB_NOT_PREFERRED: case MM_MODEM_3GPP_REGISTRATION_STATE_ATTACHED_RLOS: return TRUE; case MM_MODEM_3GPP_REGISTRATION_STATE_IDLE: case MM_MODEM_3GPP_REGISTRATION_STATE_SEARCHING: case MM_MODEM_3GPP_REGISTRATION_STATE_DENIED: case MM_MODEM_3GPP_REGISTRATION_STATE_UNKNOWN: default: return FALSE; } } /*************************************************************************/ static const gchar *creg_regex[] = { /* +CREG: (GSM 07.07 CREG=1 unsolicited) */ [0] = "\\+(CREG|CGREG|CEREG|C5GREG):\\s*0*([0-9])", /* +CREG: , (GSM 07.07 CREG=1 solicited) */ [1] = "\\+(CREG|CGREG|CEREG|C5GREG):\\s*0*([0-9]),\\s*0*([0-9])", /* +CREG: ,, (GSM 07.07 CREG=2 unsolicited) */ [2] = "\\+(CREG|CGREG|CEREG):\\s*0*([0-9]),\\s*([^,\\s]*)\\s*,\\s*([^,\\s]*)", /* +CREG: ,,, (GSM 07.07 solicited and some CREG=2 unsolicited) */ [3] = "\\+(CREG|CGREG|CEREG):\\s*([0-9]),\\s*([0-9])\\s*,\\s*([^,]*)\\s*,\\s*([^,\\s]*)", [4] = "\\+(CREG|CGREG|CEREG):\\s*0*([0-9]),\\s*0*([0-9])\\s*,\\s*(\"[^,]*\")\\s*,\\s*(\"[^,\\s]*\")", /* +CREG: ,,, (ETSI 27.007 CREG=2 unsolicited) */ [5] = "\\+(CREG|CGREG|CEREG):\\s*([0-9])\\s*,\\s*([^,\\s]*)\\s*,\\s*([^,\\s]*)\\s*,\\s*([0-9])", [6] = "\\+(CREG|CGREG|CEREG):\\s*0*([0-9])\\s*,\\s*(\"[^,\\s]*\")\\s*,\\s*(\"[^,\\s]*\")\\s*,\\s*0*([0-9])", /* +CREG: ,,,, (ETSI 27.007 solicited and some CREG=2 unsolicited) */ [7] = "\\+(CREG|CGREG|CEREG):\\s*0*([0-9]),\\s*0*([0-9])\\s*,\\s*([^,\\s]*)\\s*,\\s*([^,\\s]*)\\s*,\\s*0*([0-9])", /* +CREG: ,,,,, (Samsung Wave S8500) */ /* '+CREG: 2,1,000B,2816, B, C2816OK' */ [8] = "\\+(CREG|CGREG):\\s*0*([0-9]),\\s*0*([0-9])\\s*,\\s*([^,\\s]*)\\s*,\\s*([^,\\s]*)\\s*,\\s*([^,\\s]*)\\s*,\\s*[^,\\s]*", /* +CREG: ,,,, (ETSI 27.007 v9.20 CREG=2 unsolicited with RAC) */ [9] = "\\+(CREG|CGREG):\\s*0*([0-9])\\s*,\\s*([^,\\s]*)\\s*,\\s*([^,\\s]*)\\s*,\\s*0*([0-9])\\s*,\\s*([^,\\s]*)", /* +CEREG: ,,,, (ETSI 27.007 v8.6 CREG=2 unsolicited with RAC) */ [10] = "\\+(CEREG):\\s*0*([0-9])\\s*,\\s*([^,\\s]*)\\s*,\\s*([^,\\s]*)\\s*,\\s*([^,\\s]*)\\s*,\\s*0*([0-9])", /* +CEREG: ,,,,, (ETSI 27.007 v8.6 CREG=2 solicited with RAC) */ [11] = "\\+(CEREG):\\s*0*([0-9]),\\s*0*([0-9])\\s*,\\s*([^,\\s]*)\\s*,\\s*([^,\\s]*)\\s*,\\s*([^,\\s]*)\\s*,\\s*0*([0-9])", /* +C5GREG: ,,,,, (ETSI 27.007 CREG=2 unsolicited) */ [12] = "\\+(C5GREG):\\s*([0-9]+)\\s*,\\s*([^,\\s]*)\\s*,\\s*([^,\\s]*)\\s*,\\s*([0-9]+)\\s*,\\s*([^,\\s]*)\\s*,\\s*([^,\\s]*)", /* +C5GREG: ,,,,,, (ETSI 27.007 solicited) */ [13] = "\\+(C5GREG):\\s*([0-9]+)\\s*,\\s*([0-9+])\\s*,\\s*([^,\\s]*)\\s*,\\s*([^,\\s]*)\\s*,\\s*([0-9]+)\\s*,\\s*([^,\\s]*)\\s*,\\s*([^,\\s]*)", }; GPtrArray * mm_3gpp_creg_regex_get (gboolean solicited) { GPtrArray *array; guint i; array = g_ptr_array_sized_new (G_N_ELEMENTS (creg_regex)); for (i = 0; i < G_N_ELEMENTS (creg_regex); i++) { GRegex *regex; g_autofree gchar *pattern = NULL; if (solicited) { pattern = g_strdup_printf ("%s$", creg_regex[i]); regex = g_regex_new (pattern, G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); } else { pattern = g_strdup_printf ("\\r\\n%s\\r\\n", creg_regex[i]); regex = g_regex_new (pattern, G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); } g_assert (regex); g_ptr_array_add (array, regex); } return array; } void mm_3gpp_creg_regex_destroy (GPtrArray *array) { g_ptr_array_foreach (array, (GFunc) g_regex_unref, NULL); g_ptr_array_free (array, TRUE); } /*************************************************************************/ GRegex * mm_3gpp_ciev_regex_get (void) { return g_regex_new ("\\r\\n\\+CIEV: (.*),(\\d)\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); } /*************************************************************************/ GRegex * mm_3gpp_cgev_regex_get (void) { return g_regex_new ("\\r\\n\\+CGEV:\\s*(.*)\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); } /*************************************************************************/ GRegex * mm_3gpp_cusd_regex_get (void) { return g_regex_new ("\\r\\n\\+CUSD:\\s*(.*)\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); } /*************************************************************************/ GRegex * mm_3gpp_cmti_regex_get (void) { return g_regex_new ("\\r\\n\\+CMTI:\\s*\"(\\S+)\",\\s*(\\d+)\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); } GRegex * mm_3gpp_cds_regex_get (void) { /* Example: * +CDS: 2407914356060013F10659098136395339F6219011707193802190117071938030 */ return g_regex_new ("\\r\\n\\+CDS:\\s*(\\d+)\\r\\n(.*)\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); } /*************************************************************************/ /* AT+WS46=? response parser * * More than one numeric ID may appear in the list, that's why * they are checked separately. * * NOTE: ignore WS46 prefix or it will break Cinterion handling. * * For the specific case of '25', we will check if any other mode supports * 4G, and if there is none, we'll remove 4G caps from it. This is needed * because pre-LTE modems used '25' to report GERAN+URAN instead of the * new '29' value since LTE modems are around. */ typedef struct { guint ws46; MMModemMode mode; } Ws46Mode; /* 3GPP TS 27.007 v16.3.0, section 5.9: select wireless network +WS46 */ static const Ws46Mode ws46_modes[] = { /* GSM Digital Cellular Systems (GERAN only) */ { 12, MM_MODEM_MODE_2G }, /* UTRAN only */ { 22, MM_MODEM_MODE_3G }, /* 3GPP Systems (GERAN, UTRAN and E-UTRAN) */ { 25, MM_MODEM_MODE_ANY }, /* E-UTRAN only */ { 28, MM_MODEM_MODE_4G }, /* GERAN and UTRAN */ { 29, MM_MODEM_MODE_2G | MM_MODEM_MODE_3G }, /* GERAN and E-UTRAN */ { 30, MM_MODEM_MODE_2G | MM_MODEM_MODE_4G }, /* UTRAN and E-UTRAN */ { 31, MM_MODEM_MODE_3G | MM_MODEM_MODE_4G }, /* GERAN, UTRAN, E-UTRAN and NG-RAN */ { 35, MM_MODEM_MODE_2G | MM_MODEM_MODE_3G | MM_MODEM_MODE_4G | MM_MODEM_MODE_5G }, /* NG-RAN only */ { 36, MM_MODEM_MODE_5G }, /* E-UTRAN and NG-RAN */ { 37, MM_MODEM_MODE_4G | MM_MODEM_MODE_5G }, /* UTRAN, E-UTRAN and NG-RAN */ { 38, MM_MODEM_MODE_3G | MM_MODEM_MODE_4G | MM_MODEM_MODE_5G }, /* GERAN, E-UTRAN and NG-RAN */ { 39, MM_MODEM_MODE_2G | MM_MODEM_MODE_4G | MM_MODEM_MODE_5G }, /* UTRAN and NG-RAN */ { 40, MM_MODEM_MODE_3G | MM_MODEM_MODE_5G }, /* GERAN, UTRAN and NG-RAN */ { 41, MM_MODEM_MODE_2G | MM_MODEM_MODE_3G | MM_MODEM_MODE_5G }, /* GERAN and NG-RAN */ { 42, MM_MODEM_MODE_2G | MM_MODEM_MODE_5G }, }; GArray * mm_3gpp_parse_ws46_test_response (const gchar *response, gpointer log_object, GError **error) { g_autoptr(GRegex) r = NULL; g_autoptr(GMatchInfo) match_info = NULL; GArray *modes = NULL; GArray *tech_values = NULL; GError *inner_error = NULL; gchar *full_list = NULL; guint val; guint i; guint j; gboolean supported_5g = FALSE; gboolean supported_4g = FALSE; gboolean supported_3g = FALSE; gboolean supported_2g = FALSE; gboolean supported_mode_25 = FALSE; gboolean supported_mode_29 = FALSE; r = g_regex_new ("(?:\\+WS46:)?\\s*\\((.*)\\)(?:\\r\\n)?", 0, 0, NULL); g_assert (r != NULL); g_regex_match_full (r, response, strlen (response), 0, 0, &match_info, &inner_error); if (inner_error) goto out; if (!g_match_info_matches (match_info)) { inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't match response"); goto out; } if (!(full_list = mm_get_string_unquoted_from_match_info (match_info, 1))) { inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Error parsing full list string"); goto out; } if (!(tech_values = mm_parse_uint_list (full_list, &inner_error))) goto out; modes = g_array_new (FALSE, FALSE, sizeof (MMModemMode)); for (i = 0; i < tech_values->len; i++) { val = g_array_index (tech_values, guint, i); for (j = 0; j < G_N_ELEMENTS (ws46_modes); j++) { if (ws46_modes[j].ws46 == val) { if (val == 25) supported_mode_25 = TRUE; else { if (val == 29) supported_mode_29 = TRUE; if (ws46_modes[j].mode & MM_MODEM_MODE_5G) supported_5g = TRUE; if (ws46_modes[j].mode & MM_MODEM_MODE_4G) supported_4g = TRUE; if (ws46_modes[j].mode & MM_MODEM_MODE_3G) supported_3g = TRUE; if (ws46_modes[j].mode & MM_MODEM_MODE_2G) supported_2g = TRUE; g_array_append_val (modes, ws46_modes[j].mode); } break; } } if (j == G_N_ELEMENTS (ws46_modes)) mm_obj_warn (log_object, "Unknown +WS46 mode reported: %u", val); } if (supported_mode_25) { MMModemMode mode_25; mode_25 = MM_MODEM_MODE_2G | MM_MODEM_MODE_3G; if (supported_4g) { mode_25 |= MM_MODEM_MODE_4G; g_array_append_val (modes, mode_25); } else if (!supported_mode_29) g_array_append_val (modes, mode_25); } if (modes->len == 0) { inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "No valid modes reported"); g_clear_pointer (&modes, g_array_unref); goto out; } /* Fixup the ANY value, based on which are the supported modes */ for (i = 0; i < modes->len; i++) { MMModemMode *mode; mode = &g_array_index (modes, MMModemMode, i); if (*mode == MM_MODEM_MODE_ANY) { *mode = 0; if (supported_2g) *mode |= MM_MODEM_MODE_2G; if (supported_3g) *mode |= MM_MODEM_MODE_3G; if (supported_4g) *mode |= MM_MODEM_MODE_4G; if (supported_5g) *mode |= MM_MODEM_MODE_5G; if (*mode == 0) { inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "No way to fixup the ANY value"); g_clear_pointer (&modes, g_array_unref); goto out; } } } out: if (tech_values) g_array_unref (tech_values); g_free (full_list); if (inner_error) { g_propagate_error (error, inner_error); return NULL; } g_assert (modes && modes->len); return modes; } /*************************************************************************/ static void mm_3gpp_network_info_free (MM3gppNetworkInfo *info) { g_free (info->operator_long); g_free (info->operator_short); g_free (info->operator_code); g_free (info); } void mm_3gpp_network_info_list_free (GList *info_list) { g_list_free_full (info_list, (GDestroyNotify) mm_3gpp_network_info_free); } static MMModem3gppNetworkAvailability get_mm_network_availability_from_3gpp_network_availability (guint val, gpointer log_object) { /* The network availability status in the MM API and in the 3GPP specs take the * same numeric values, but we map them with an enum as it's much safer if new * values are defined by 3GPP in the future. */ switch (val) { case 1: return MM_MODEM_3GPP_NETWORK_AVAILABILITY_AVAILABLE; case 2: return MM_MODEM_3GPP_NETWORK_AVAILABILITY_CURRENT; case 3: return MM_MODEM_3GPP_NETWORK_AVAILABILITY_FORBIDDEN; default: break; } mm_obj_warn (log_object, "unknown network availability value: %u", val); return MM_MODEM_3GPP_NETWORK_AVAILABILITY_UNKNOWN; } static MMModemAccessTechnology get_mm_access_tech_from_etsi_access_tech (guint val, gpointer log_object) { /* See ETSI TS 27.007 */ switch (val) { case 0: /* GSM */ case 8: /* EC-GSM-IoT (A/Gb mode) */ return MM_MODEM_ACCESS_TECHNOLOGY_GSM; case 1: return MM_MODEM_ACCESS_TECHNOLOGY_GSM_COMPACT; case 2: return MM_MODEM_ACCESS_TECHNOLOGY_UMTS; case 3: return MM_MODEM_ACCESS_TECHNOLOGY_EDGE; case 4: return MM_MODEM_ACCESS_TECHNOLOGY_HSDPA; case 5: return MM_MODEM_ACCESS_TECHNOLOGY_HSUPA; case 6: return MM_MODEM_ACCESS_TECHNOLOGY_HSPA; case 7: /* E-UTRAN */ case 9: /* E-UTRAN (NB-S1) */ case 10: /* E-UTRA connected to a 5GCN */ return MM_MODEM_ACCESS_TECHNOLOGY_LTE; case 11: /* NR connected to a 5G CN */ case 12: /* NG-RAN */ return MM_MODEM_ACCESS_TECHNOLOGY_5GNR; case 13: /* E-UTRA-NR dual connectivity */ return (MM_MODEM_ACCESS_TECHNOLOGY_5GNR | MM_MODEM_ACCESS_TECHNOLOGY_LTE); default: break; } mm_obj_warn (log_object, "unknown access technology value: %u", val); return MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN; } GList * mm_3gpp_parse_cops_test_response (const gchar *reply, MMModemCharset cur_charset, gpointer log_object, GError **error) { g_autoptr(GRegex) r = NULL; g_autoptr(GMatchInfo) match_info = NULL; GList *info_list = 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_CORE_ERROR, MM_CORE_ERROR_FAILED, "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); g_assert (r); /* If we didn't get any hits, try the pre-UMTS format match */ if (!g_regex_match (r, reply, 0, &match_info)) { g_clear_pointer (&r, g_regex_unref); g_clear_pointer (&match_info, g_match_info_free); /* 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); g_assert (r); g_regex_match (r, reply, 0, &match_info); umts_format = FALSE; } /* Parse the results */ while (g_match_info_matches (match_info)) { MM3gppNetworkInfo *info; guint net_value = 0; gboolean valid = FALSE; info = g_new0 (MM3gppNetworkInfo, 1); /* the regex makes sure this is a number, it won't fail */ mm_get_uint_from_match_info (match_info, 1, &net_value); info->status = get_mm_network_availability_from_3gpp_network_availability (net_value, log_object); info->operator_long = mm_get_string_unquoted_from_match_info (match_info, 2); info->operator_short = mm_get_string_unquoted_from_match_info (match_info, 3); info->operator_code = mm_get_string_unquoted_from_match_info (match_info, 4); /* The returned strings may be given in e.g. UCS2 */ mm_3gpp_normalize_operator (&info->operator_long, cur_charset, log_object); mm_3gpp_normalize_operator (&info->operator_short, cur_charset, log_object); mm_3gpp_normalize_operator (&info->operator_code, cur_charset, log_object); /* Only try for access technology with UMTS-format matches. * If none give, assume GSM */ if (umts_format) { guint act_value = 0; /* the regex makes sure this is a number, it won't fail */ mm_get_uint_from_match_info (match_info, 5, &act_value); info->access_tech = get_mm_access_tech_from_etsi_access_tech (act_value, log_object); } else info->access_tech = MM_MODEM_ACCESS_TECHNOLOGY_GSM; /* 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. */ if (info->operator_code && (strlen (info->operator_code) >= 5)) { gchar *tmp; valid = TRUE; tmp = info->operator_code; while (*tmp) { if (!isdigit (*tmp) && (*tmp != '-')) { valid = FALSE; break; } tmp++; } } if (valid) { g_autofree gchar *access_tech_str = NULL; access_tech_str = mm_modem_access_technology_build_string_from_mask (info->access_tech); mm_obj_dbg (log_object, "found network '%s' ('%s','%s'); availability: %s, access tech: %s", info->operator_code, info->operator_short ? info->operator_short : "no short name", info->operator_long ? info->operator_long : "no long name", mm_modem_3gpp_network_availability_get_string (info->status), access_tech_str); info_list = g_list_prepend (info_list, info); } else mm_3gpp_network_info_free (info); g_match_info_next (match_info, NULL); } return info_list; } /*************************************************************************/ gboolean mm_3gpp_parse_cops_read_response (const gchar *response, guint *out_mode, guint *out_format, gchar **out_operator, MMModemAccessTechnology *out_act, gpointer log_object, GError **error) { g_autoptr(GRegex) r = NULL; g_autoptr(GMatchInfo) match_info = NULL; GError *inner_error = NULL; guint mode = 0; guint format = 0; gchar *operator = NULL; guint actval = 0; MMModemAccessTechnology act = MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN; g_assert (out_mode || out_format || out_operator || out_act); /* We assume the response to be either: * +COPS: ,, * or: * +COPS: ,,, */ r = g_regex_new ("\\+COPS:\\s*(\\d+),(\\d+),([^,]*)(?:,(\\d+))?(?:\\r\\n)?", 0, 0, NULL); g_assert (r != NULL); g_regex_match_full (r, response, strlen (response), 0, 0, &match_info, &inner_error); if (inner_error) goto out; if (!g_match_info_matches (match_info)) { inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't match response"); goto out; } if (out_mode && !mm_get_uint_from_match_info (match_info, 1, &mode)) { inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Error parsing mode"); goto out; } if (out_format && !mm_get_uint_from_match_info (match_info, 2, &format)) { inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Error parsing format"); goto out; } if (out_operator && !(operator = mm_get_string_unquoted_from_match_info (match_info, 3))) { inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Error parsing operator"); goto out; } /* AcT is optional */ if (out_act && g_match_info_get_match_count (match_info) >= 5) { if (!mm_get_uint_from_match_info (match_info, 4, &actval)) { inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Error parsing AcT"); goto out; } act = get_mm_access_tech_from_etsi_access_tech (actval, log_object); } out: if (inner_error) { g_free (operator); g_propagate_error (error, inner_error); return FALSE; } if (out_mode) *out_mode = mode; if (out_format) *out_format = format; if (out_operator) *out_operator = operator; if (out_act) *out_act = act; return TRUE; } /*************************************************************************/ /* Logic to compare two APN names */ gboolean mm_3gpp_cmp_apn_name (const gchar *requested, const gchar *existing) { size_t requested_len; size_t existing_len; /* If both empty, that's a good match */ if ((!existing || !existing[0]) && (!requested || !requested[0])) return TRUE; /* Both must be given to compare properly */ if (!existing || !existing[0] || !requested || !requested[0]) return FALSE; requested_len = strlen (requested); /* * 1) The requested APN should be at least the prefix of the existing one. */ if (g_ascii_strncasecmp (existing, requested, requested_len) != 0) return FALSE; /* * 2) If the existing one is actually the same as the requested one (i.e. * there are no more different chars in the existing one), we're done. */ if (existing[requested_len] == '\0') return TRUE; existing_len = strlen (existing); /* 3) Special handling for PDP contexts reported by u-blox modems once the * contexts have been activated at least once: * "ac.vodafone.es.MNC001.MCC214.GPRS" should match "ac.vodafone.es" */ if ((existing_len > (requested_len + 14)) && g_ascii_strncasecmp (&existing[requested_len], ".mnc", 4) == 0 && g_ascii_strncasecmp (&existing[requested_len + 7], ".mcc", 4) == 0) return TRUE; /* No match */ return FALSE; } /*************************************************************************/ gboolean mm_3gpp_pdp_context_format_list_find_range (GList *pdp_format_list, MMBearerIpFamily ip_family, guint *out_min_cid, guint *out_max_cid) { GList *l; for (l = pdp_format_list; l; l = g_list_next (l)) { MM3gppPdpContextFormat *format = l->data; /* Found exact PDP type? */ if (format->pdp_type == ip_family) { if (out_min_cid) *out_min_cid = format->min_cid; if (out_max_cid) *out_max_cid = format->max_cid; return TRUE; } } return FALSE; } /*************************************************************************/ MM3gppProfile * mm_3gpp_profile_new_from_pdp_context (MM3gppPdpContext *pdp_context) { MM3gppProfile *profile; profile = mm_3gpp_profile_new (); mm_3gpp_profile_set_profile_id (profile, pdp_context->cid); mm_3gpp_profile_set_apn (profile, pdp_context->apn); mm_3gpp_profile_set_ip_type (profile, pdp_context->pdp_type); return profile; } GList * mm_3gpp_profile_list_new_from_pdp_context_list (GList *pdp_context_list) { GList *profile_list = NULL; GList *l; for (l = pdp_context_list; l; l = g_list_next (l)) { MM3gppPdpContext *pdp_context; MM3gppProfile *profile; pdp_context = (MM3gppPdpContext *)l->data; profile = mm_3gpp_profile_new_from_pdp_context (pdp_context); profile_list = g_list_append (profile_list, profile); } return profile_list; } void mm_3gpp_profile_list_free (GList *profile_list) { g_list_free_full (profile_list, g_object_unref); } MM3gppProfile * mm_3gpp_profile_list_find_by_profile_id (GList *profile_list, gint profile_id, GError **error) { GList *l; for (l = profile_list; l; l = g_list_next (l)) { MM3gppProfile *iter_profile = l->data; if (mm_3gpp_profile_get_profile_id (iter_profile) == profile_id) return g_object_ref (iter_profile); } g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_NOT_FOUND, "Profile '%d' not found", profile_id); return NULL; } MM3gppProfile * mm_3gpp_profile_list_find_by_apn_type (GList *profile_list, MMBearerApnType apn_type, GError **error) { g_autofree gchar *apn_type_str = NULL; GList *l; for (l = profile_list; l; l = g_list_next (l)) { MM3gppProfile *iter_profile = l->data; if (mm_3gpp_profile_get_apn_type (iter_profile) == apn_type) return g_object_ref (iter_profile); } apn_type_str = mm_bearer_apn_type_build_string_from_mask (apn_type); g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_NOT_FOUND, "Profile '%s' not found", apn_type_str); return NULL; } gint mm_3gpp_profile_list_find_empty (GList *profile_list, gint min_profile_id, gint max_profile_id, GError **error) { GList *l; gint profile_id; profile_id = min_profile_id; for (l = profile_list; l; l = g_list_next (l)) { MM3gppProfile *iter_profile = l->data; gint iter_profile_id; iter_profile_id = mm_3gpp_profile_get_profile_id (iter_profile); if (iter_profile_id > profile_id) break; if (iter_profile_id == profile_id) profile_id++; } if (profile_id > max_profile_id) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_NOT_FOUND, "No empty profile available"); return MM_3GPP_PROFILE_ID_UNKNOWN; } return profile_id; } gint mm_3gpp_profile_list_find_best (GList *profile_list, MM3gppProfile *requested, GEqualFunc cmp_apn, MM3gppProfileCmpFlags cmp_flags, gint min_profile_id, gint max_profile_id, gpointer log_object, MM3gppProfile **out_reused, gboolean *out_overwritten) { GList *l; MMBearerIpFamily requested_ip_type; gint prev_profile_id = 0; gint unused_profile_id = 0; gint max_found_profile_id = 0; gint max_allowed_profile_id = 0; gint blank_profile_id = 0; g_assert (out_reused); g_assert (out_overwritten); requested_ip_type = mm_3gpp_profile_get_ip_type (requested); /* When looking for exact profile matches we should not compare * the profile id, as the requested profile won't have one set */ cmp_flags |= MM_3GPP_PROFILE_CMP_FLAGS_NO_PROFILE_ID; /* Look for the exact PDP context we want */ for (l = profile_list; l; l = g_list_next (l)) { MM3gppProfile *iter_profile = l->data; MMBearerIpFamily iter_ip_type; const gchar *iter_apn; gint iter_profile_id; iter_profile_id = mm_3gpp_profile_get_profile_id (iter_profile); if (iter_profile_id < min_profile_id) { mm_obj_dbg (log_object, "skipping context at profile %d: out of bounds", iter_profile_id); continue; } /* Always prefer an exact match; compare all supported fields except for profile id */ if (mm_3gpp_profile_cmp (iter_profile, requested, cmp_apn, cmp_flags)) { mm_obj_dbg (log_object, "found exact context at profile %d", iter_profile_id); *out_reused = g_object_ref (iter_profile); *out_overwritten = FALSE; return iter_profile_id; } /* Same PDP type but with no APN set? we may use that one if no exact match found */ iter_ip_type = mm_3gpp_profile_get_ip_type (iter_profile); iter_apn = mm_3gpp_profile_get_apn (iter_profile); if ((iter_ip_type == requested_ip_type) && (!iter_apn || !iter_apn[0]) && !blank_profile_id) blank_profile_id = iter_profile_id; /* If an unused CID was not found yet and the previous CID is not (CID - 1), * this means that (previous CID + 1) is an unused CID that can be used. * This logic will allow us using unused CIDs that are available in the gaps * between already defined contexts. */ if (!unused_profile_id && prev_profile_id && ((prev_profile_id + 1) < iter_profile_id)) unused_profile_id = prev_profile_id + 1; /* Update previous CID value to the current CID for use in the next loop, * unless an unused CID was already found. */ if (!unused_profile_id) prev_profile_id = iter_profile_id; /* Update max CID if we found a bigger one */ if (max_found_profile_id < iter_profile_id) max_found_profile_id = iter_profile_id; } /* Try to use an unused CID detected in between the already defined contexts */ if (unused_profile_id) { mm_obj_dbg (log_object, "found unused profile %d", unused_profile_id); *out_reused = NULL; *out_overwritten = FALSE; return unused_profile_id; } /* If the max existing CID found during CGDCONT? is below the max allowed * CID, then we can use the next available CID because it's an unused one. */ max_allowed_profile_id = max_profile_id; if (max_found_profile_id && (max_found_profile_id < max_allowed_profile_id)) { mm_obj_dbg (log_object, "found unused profile %d (<%d)", max_found_profile_id + 1, max_allowed_profile_id); *out_reused = NULL; *out_overwritten = FALSE; return (max_found_profile_id + 1); } /* Rewrite a context defined with no APN, if any */ if (blank_profile_id) { mm_obj_dbg (log_object, "rewriting profile %d with empty APN", blank_profile_id); *out_reused = NULL; *out_overwritten = TRUE; return blank_profile_id; } /* Rewrite the last existing one found */ if (max_found_profile_id) { mm_obj_dbg (log_object, "rewriting last profile %d detected", max_found_profile_id); *out_reused = NULL; *out_overwritten = TRUE; return max_found_profile_id; } /* Otherwise, just fallback to min CID */ mm_obj_dbg (log_object, "falling back to profile %d", min_profile_id); *out_reused = NULL; *out_overwritten = TRUE; return min_profile_id; } /*************************************************************************/ static void mm_3gpp_pdp_context_format_free (MM3gppPdpContextFormat *format) { g_slice_free (MM3gppPdpContextFormat, format); } void mm_3gpp_pdp_context_format_list_free (GList *pdp_format_list) { g_list_free_full (pdp_format_list, (GDestroyNotify) mm_3gpp_pdp_context_format_free); } GList * mm_3gpp_parse_cgdcont_test_response (const gchar *response, gpointer log_object, GError **error) { g_autoptr(GRegex) r = NULL; g_autoptr(GMatchInfo) match_info = NULL; GError *inner_error = NULL; GList *list = NULL; if (!response || !g_str_has_prefix (response, "+CGDCONT:")) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Missing +CGDCONT prefix"); return NULL; } r = g_regex_new ("\\+CGDCONT:\\s*\\(\\s*(\\d+)\\s*-?\\s*(\\d+)?[^\\)]*\\)\\s*,\\s*\\(?\"(\\S+)\"", G_REGEX_DOLLAR_ENDONLY | G_REGEX_RAW, 0, &inner_error); g_assert (r != NULL); g_regex_match_full (r, response, strlen (response), 0, 0, &match_info, &inner_error); while (!inner_error && g_match_info_matches (match_info)) { gchar *pdp_type_str; guint min_cid; guint max_cid; MMBearerIpFamily pdp_type; /* Read PDP type */ pdp_type_str = mm_get_string_unquoted_from_match_info (match_info, 3); pdp_type = mm_3gpp_get_ip_family_from_pdp_type (pdp_type_str); if (pdp_type == MM_BEARER_IP_FAMILY_NONE) mm_obj_dbg (log_object, "unhandled PDP type in CGDCONT=? reply: '%s'", pdp_type_str); else { /* Read min CID */ if (!mm_get_uint_from_match_info (match_info, 1, &min_cid)) mm_obj_warn (log_object, "invalid min CID in CGDCONT=? reply for PDP type '%s'", pdp_type_str); else { MM3gppPdpContextFormat *format; /* Read max CID: Optional! If no value given, we default to min CID */ if (!mm_get_uint_from_match_info (match_info, 2, &max_cid)) max_cid = min_cid; format = g_slice_new (MM3gppPdpContextFormat); format->pdp_type = pdp_type; format->min_cid = min_cid; format->max_cid = max_cid; list = g_list_prepend (list, format); } } g_free (pdp_type_str); g_match_info_next (match_info, &inner_error); } if (inner_error) { mm_obj_warn (log_object, "unexpected error matching +CGDCONT response: '%s'", inner_error->message); g_error_free (inner_error); } return list; } /*************************************************************************/ static void mm_3gpp_pdp_context_free (MM3gppPdpContext *pdp) { g_free (pdp->apn); g_slice_free (MM3gppPdpContext, pdp); } void mm_3gpp_pdp_context_list_free (GList *list) { g_list_free_full (list, (GDestroyNotify) mm_3gpp_pdp_context_free); } static gint mm_3gpp_pdp_context_cmp (MM3gppPdpContext *a, MM3gppPdpContext *b) { return (a->cid - b->cid); } GList * mm_3gpp_parse_cgdcont_read_response (const gchar *reply, GError **error) { GError *inner_error = NULL; g_autoptr(GRegex) r = NULL; g_autoptr(GMatchInfo) match_info = NULL; GList *list = NULL; if (!reply || !reply[0]) /* No APNs configured, all done */ return NULL; r = g_regex_new ("\\+CGDCONT:\\s*(\\d+)\\s*,([^, \\)]*)\\s*,([^,\\s\\)]*)", G_REGEX_DOLLAR_ENDONLY | G_REGEX_RAW, 0, NULL); g_assert (r); g_regex_match_full (r, reply, strlen (reply), 0, 0, &match_info, &inner_error); while (!inner_error && g_match_info_matches (match_info)) { gchar *str; MMBearerIpFamily ip_family; str = mm_get_string_unquoted_from_match_info (match_info, 2); ip_family = mm_3gpp_get_ip_family_from_pdp_type (str); if (ip_family != MM_BEARER_IP_FAMILY_NONE) { MM3gppPdpContext *pdp; pdp = g_slice_new0 (MM3gppPdpContext); if (!mm_get_uint_from_match_info (match_info, 1, &pdp->cid)) { inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't parse CID from reply: '%s'", reply); break; } pdp->pdp_type = ip_family; pdp->apn = mm_get_string_unquoted_from_match_info (match_info, 3); list = g_list_prepend (list, pdp); } g_free (str); g_match_info_next (match_info, &inner_error); } if (inner_error) { mm_3gpp_pdp_context_list_free (list); g_propagate_error (error, inner_error); g_prefix_error (error, "Couldn't properly parse list of PDP contexts. "); return NULL; } return g_list_sort (list, (GCompareFunc)mm_3gpp_pdp_context_cmp); } /*************************************************************************/ static void mm_3gpp_pdp_context_active_free (MM3gppPdpContextActive *pdp_active) { g_slice_free (MM3gppPdpContextActive, pdp_active); } void mm_3gpp_pdp_context_active_list_free (GList *pdp_active_list) { g_list_free_full (pdp_active_list, (GDestroyNotify) mm_3gpp_pdp_context_active_free); } gint mm_3gpp_pdp_context_active_cmp (MM3gppPdpContextActive *a, MM3gppPdpContextActive *b) { return (a->cid - b->cid); } GList * mm_3gpp_parse_cgact_read_response (const gchar *reply, GError **error) { g_autoptr(GRegex) r = NULL; g_autoptr(GMatchInfo) match_info = NULL; GError *inner_error = NULL; GList *list = NULL; if (!reply || !reply[0]) /* Nothing configured, all done */ return NULL; r = g_regex_new ("\\+CGACT:\\s*(\\d+),(\\d+)", G_REGEX_DOLLAR_ENDONLY | G_REGEX_RAW, 0, &inner_error); g_assert (r); g_regex_match_full (r, reply, strlen (reply), 0, 0, &match_info, &inner_error); while (!inner_error && g_match_info_matches (match_info)) { MM3gppPdpContextActive *pdp_active; guint cid = 0; guint aux = 0; if (!mm_get_uint_from_match_info (match_info, 1, &cid)) { inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't parse CID from reply: '%s'", reply); break; } if (!mm_get_uint_from_match_info (match_info, 2, &aux) || (aux != 0 && aux != 1)) { inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't parse context status from reply: '%s'", reply); break; } pdp_active = g_slice_new0 (MM3gppPdpContextActive); pdp_active->cid = cid; pdp_active->active = (gboolean) aux; list = g_list_prepend (list, pdp_active); g_match_info_next (match_info, &inner_error); } if (inner_error) { mm_3gpp_pdp_context_active_list_free (list); g_propagate_error (error, inner_error); g_prefix_error (error, "Couldn't properly parse list of active/inactive PDP contexts. "); return NULL; } list = g_list_sort (list, (GCompareFunc) mm_3gpp_pdp_context_active_cmp); return list; } /*************************************************************************/ static gboolean item_is_lac_not_stat (GMatchInfo *info, guint32 item) { gchar *str; gboolean is_lac = FALSE; /* A will always be a single digit, without quotes */ str = g_match_info_fetch (info, item); g_assert (str); is_lac = (strchr (str, '"') || strlen (str) > 1); g_free (str); return is_lac; } gboolean mm_3gpp_parse_creg_response (GMatchInfo *info, gpointer log_object, MMModem3gppRegistrationState *out_reg_state, gulong *out_lac, gulong *out_ci, MMModemAccessTechnology *out_act, gboolean *out_cgreg, gboolean *out_cereg, gboolean *out_c5greg, GError **error) { gint n_matches, act = -1; guint stat = 0; guint64 lac = 0, ci = 0; guint istat = 0, ilac = 0, ici = 0, iact = 0; gchar *str; g_assert (info != NULL); g_assert (out_reg_state != NULL); g_assert (out_lac != NULL); g_assert (out_ci != NULL); g_assert (out_act != NULL); g_assert (out_cgreg != NULL); g_assert (out_cereg != NULL); g_assert (out_c5greg != NULL); str = g_match_info_fetch (info, 1); *out_cgreg = (str && strstr (str, "CGREG")) ? TRUE : FALSE; *out_cereg = (str && strstr (str, "CEREG")) ? TRUE : FALSE; *out_c5greg = (str && strstr (str, "C5GREG")) ? TRUE : FALSE; g_free (str); /* Normally the number of matches could be used to determine what each * item is, but we have overlap in one case. */ n_matches = g_match_info_get_match_count (info); if (n_matches == 3) { /* CREG=1: +CREG: */ istat = 2; } else if (n_matches == 4) { /* Solicited response: +CREG: , */ istat = 3; } else if (n_matches == 5) { /* CREG=2 (GSM 07.07): +CREG: ,, */ istat = 2; ilac = 3; ici = 4; } else if (n_matches == 6) { /* CREG=2 (ETSI 27.007): +CREG: ,,, * CREG=2 (non-standard): +CREG: ,,, */ /* Check if the third item is the LAC to distinguish the two cases */ if (item_is_lac_not_stat (info, 3)) { istat = 2; ilac = 3; ici = 4; iact = 5; } else { istat = 3; ilac = 4; ici = 5; } } else if (n_matches == 7) { /* CREG=2 (solicited): +CREG: ,,,, * CREG=2 (unsolicited with RAC): +CREG: ,,,, * CEREG=2 (solicited): +CEREG: ,,,, * CEREG=2 (unsolicited with RAC): +CEREG: ,,,, */ if (*out_cereg) { /* Check if the third item is the LAC to distinguish the two cases */ if (item_is_lac_not_stat (info, 3)) { istat = 2; ilac = 3; } else { istat = 3; ilac = 4; } ici = 5; iact = 6; } else { /* Check if the third item is the LAC to distinguish the two cases */ if (item_is_lac_not_stat (info, 3)) { istat = 2; ilac = 3; ici = 4; iact = 5; } else { istat = 3; ilac = 4; ici = 5; iact = 6; } } } else if (n_matches == 8) { /* CEREG=2 (solicited with RAC): +CEREG: ,,,,, * C5GREG=2 (unsolicited): +C5GREG: ,,,,, */ if (*out_cereg) { istat = 3; ilac = 4; ici = 6; iact = 7; } else if (*out_c5greg) { istat = 2; ilac = 3; ici = 4; iact = 5; } } else if (n_matches == 9) { /* C5GREG=2 (solicited): +C5GREG: ,,,,,, */ istat = 3; ilac = 4; ici = 5; iact = 6; } /* Status */ if (!mm_get_uint_from_match_info (info, istat, &stat)) { g_set_error_literal (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Could not parse the registration status response"); return FALSE; } /* 'attached RLOS' is the last valid state */ if (stat > MM_MODEM_3GPP_REGISTRATION_STATE_ATTACHED_RLOS) { mm_obj_warn (log_object, "unknown registration state value '%u'", stat); stat = MM_MODEM_3GPP_REGISTRATION_STATE_UNKNOWN; } /* Location Area Code/Tracking Area Code * FIXME: some phones apparently swap the LAC bytes (LG, SonyEricsson, * Sagem). Need to handle that. */ if (ilac) mm_get_u64_from_hex_match_info (info, ilac, &lac); /* Cell ID */ if (ici) mm_get_u64_from_hex_match_info (info, ici, &ci); /* Access Technology */ if (iact) mm_get_int_from_match_info (info, iact, &act); *out_reg_state = (MMModem3gppRegistrationState) stat; if (stat != MM_MODEM_3GPP_REGISTRATION_STATE_UNKNOWN) { /* Don't fill in lac/ci/act if the device's state is unknown */ *out_lac = (gulong)lac; *out_ci = (gulong)ci; *out_act = (act >= 0 ? get_mm_access_tech_from_etsi_access_tech (act, log_object) : MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN); } return TRUE; } /*************************************************************************/ #define CMGF_TAG "+CMGF:" gboolean mm_3gpp_parse_cmgf_test_response (const gchar *reply, gboolean *sms_pdu_supported, gboolean *sms_text_supported, GError **error) { g_autoptr(GRegex) r = NULL; g_autoptr(GMatchInfo) match_info = NULL; gchar *s; guint32 min = -1; guint32 max = -1; /* Strip whitespace and response tag */ if (g_str_has_prefix (reply, CMGF_TAG)) reply += strlen (CMGF_TAG); while (isspace (*reply)) reply++; r = g_regex_new ("\\(?\\s*(\\d+)\\s*[-,]?\\s*(\\d+)?\\s*\\)?", 0, 0, error); if (!r) return FALSE; if (!g_regex_match (r, reply, 0, &match_info)) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Failed to parse CMGF query result '%s'", reply); return FALSE; } s = g_match_info_fetch (match_info, 1); if (s) min = atoi (s); g_free (s); s = g_match_info_fetch (match_info, 2); if (s) max = atoi (s); g_free (s); /* CMGF=0 for PDU mode */ *sms_pdu_supported = (min == 0); /* CMGF=1 for Text mode */ *sms_text_supported = (max >= 1); return TRUE; } /*************************************************************************/ MM3gppPduInfo * mm_3gpp_parse_cmgr_read_response (const gchar *reply, guint index, GError **error) { g_autoptr(GRegex) r = NULL; g_autoptr(GMatchInfo) match_info = NULL; gint count; gint status; gchar *pdu; MM3gppPduInfo *info = NULL; /* +CMGR: ,,(whitespace) */ /* The and fields are matched, but not currently used */ r = g_regex_new ("\\+CMGR:\\s*(\\d+)\\s*,([^,]*),\\s*(\\d+)\\s*([^\\r\\n]*)", 0, 0, NULL); g_assert (r); if (!g_regex_match (r, reply, 0, &match_info)) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Failed to parse CMGR read result: response didn't match '%s'", reply); return NULL; } /* g_match_info_get_match_count includes match #0 */ if ((count = g_match_info_get_match_count (match_info)) != 5) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Failed to match CMGR fields (matched %d) '%s'", count, reply); return NULL; } if (!mm_get_int_from_match_info (match_info, 1, &status)) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Failed to extract CMGR status field '%s'", reply); return NULL; } pdu = mm_get_string_unquoted_from_match_info (match_info, 4); if (!pdu) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Failed to extract CMGR pdu field '%s'", reply); return NULL; } info = g_new0 (MM3gppPduInfo, 1); info->index = index; info->status = status; info->pdu = pdu; return info; } /*****************************************************************************/ /* AT+CRSM response parser */ gboolean mm_3gpp_parse_crsm_response (const gchar *reply, guint *sw1, guint *sw2, gchar **hex, GError **error) { g_autoptr(GRegex) r = NULL; g_autoptr(GMatchInfo) match_info = NULL; g_assert (sw1 != NULL); g_assert (sw2 != NULL); g_assert (hex != NULL); *sw1 = 0; *sw2 = 0; *hex = NULL; if (!reply || !g_str_has_prefix (reply, "+CRSM:")) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Missing +CRSM prefix"); return FALSE; } r = g_regex_new ("\\+CRSM:\\s*(\\d+)\\s*,\\s*(\\d+)\\s*,\\s*\"?([0-9a-fA-F]+)\"?", G_REGEX_RAW, 0, NULL); g_assert (r != NULL); if (g_regex_match (r, reply, 0, &match_info) && mm_get_uint_from_match_info (match_info, 1, sw1) && mm_get_uint_from_match_info (match_info, 2, sw2)) { *hex = mm_get_string_unquoted_from_match_info (match_info, 3); } if (*hex == NULL) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Failed to parse CRSM query result '%s'", reply); return FALSE; } return TRUE; } /*************************************************************************/ /* CGCONTRDP=N response parser */ static gboolean split_local_address_and_subnet (const gchar *str, gchar **local_address, gchar **subnet) { const gchar *separator; guint count = 0; /* E.g. split: "2.43.2.44.255.255.255.255" * into: * local address: "2.43.2.44", * subnet: "255.255.255.255" */ g_assert (str); g_assert (local_address); g_assert (subnet); separator = str; while (1) { separator = strchr (separator, '.'); if (separator) { count++; if (count == 4) { if (local_address) *local_address = g_strndup (str, (separator - str)); if (subnet) *subnet = g_strdup (++separator); return TRUE; } separator++; continue; } /* Not even the full IP? report error parsing */ if (count < 3) return FALSE; if (count == 3) { if (local_address) *local_address = g_strdup (str); if (subnet) *subnet = NULL; return TRUE; } } } gboolean mm_3gpp_parse_cgcontrdp_response (const gchar *response, guint *out_cid, guint *out_bearer_id, gchar **out_apn, gchar **out_local_address, gchar **out_subnet, gchar **out_gateway_address, gchar **out_dns_primary_address, gchar **out_dns_secondary_address, GError **error) { g_autoptr(GRegex) r = NULL; g_autoptr(GMatchInfo) match_info = NULL; GError *inner_error = NULL; guint cid = 0; guint bearer_id = 0; g_autofree gchar *apn = NULL; g_autofree gchar *local_address_and_subnet = NULL; g_autofree gchar *local_address = NULL; g_autofree gchar *subnet = NULL; g_autofree gchar *gateway_address = NULL; g_autofree gchar *dns_primary_address = NULL; g_autofree gchar *dns_secondary_address = NULL; guint field_format_extra_index = 0; /* Response may be e.g.: * +CGCONTRDP: 4,5,"ibox.tim.it.mnc001.mcc222.gprs","2.197.17.49.255.255.255.255","2.197.17.49","10.207.43.46","10.206.56.132","0.0.0.0","0.0.0.0",0 * * We assume only ONE line is returned; because we request +CGCONTRDP with * a specific N CID. Also, we don't parse all fields, we stop after * secondary DNS. * * Only the 3 first parameters (cid, bearer id, apn) are mandatory in the * response, all the others are optional, but, we'll anyway assume that APN * may be empty. * * The format of the response changed in TS 27.007 v9.4.0, we try to detect * both formats ('a' if >= v9.4.0, 'b' if < v9.4.0) with a single regex here. */ r = g_regex_new ("\\+CGCONTRDP: " "(\\d+),(\\d+),([^,]*)" /* cid, bearer id, apn */ "(?:,([^,]*))?" /* (a)ip+mask or (b)ip */ "(?:,([^,]*))?" /* (a)gateway or (b)mask */ "(?:,([^,]*))?" /* (a)dns1 or (b)gateway */ "(?:,([^,]*))?" /* (a)dns2 or (b)dns1 */ "(?:,([^,]*))?" /* (a)p-cscf primary or (b)dns2 */ "(?:,(.*))?" /* others, ignored */ "(?:\\r\\n)?", 0, 0, NULL); g_assert (r != NULL); g_regex_match_full (r, response, strlen (response), 0, 0, &match_info, &inner_error); if (inner_error) { g_propagate_error (error, inner_error); return FALSE; } if (!g_match_info_matches (match_info)) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS, "Couldn't match +CGCONTRDP response"); return FALSE; } if (out_cid && !mm_get_uint_from_match_info (match_info, 1, &cid)) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Error parsing cid"); return FALSE; } if (out_bearer_id && !mm_get_uint_from_match_info (match_info, 2, &bearer_id)) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Error parsing bearer id"); return FALSE; } /* Remaining strings are optional or empty allowed */ apn = mm_get_string_unquoted_from_match_info (match_info, 3); /* * The +CGCONTRDP=[cid] response format before version TS 27.007 v9.4.0 had * the subnet in its own comma-separated field. Try to detect that. */ local_address_and_subnet = mm_get_string_unquoted_from_match_info (match_info, 4); if (local_address_and_subnet && !split_local_address_and_subnet (local_address_and_subnet, &local_address, &subnet)) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Error parsing local address and subnet"); return FALSE; } /* If we don't have a subnet in field 4, we're using the old format with subnet in an extra field */ if (!subnet) { subnet = mm_get_string_unquoted_from_match_info (match_info, 5); field_format_extra_index = 1; } gateway_address = mm_get_string_unquoted_from_match_info (match_info, 5 + field_format_extra_index); dns_primary_address = mm_get_string_unquoted_from_match_info (match_info, 6 + field_format_extra_index); dns_secondary_address = mm_get_string_unquoted_from_match_info (match_info, 7 + field_format_extra_index); if (out_cid) *out_cid = cid; if (out_bearer_id) *out_bearer_id = bearer_id; if (out_apn) *out_apn = g_steal_pointer (&apn); /* Local address and subnet may always be retrieved, even if not requested * by the caller, as we need them to know which +CGCONTRDP=[cid] response is * being parsed. So make sure we free them if not needed. */ if (out_local_address) *out_local_address = g_steal_pointer (&local_address); if (out_subnet) *out_subnet = g_steal_pointer (&subnet); if (out_gateway_address) *out_gateway_address = g_steal_pointer (&gateway_address); if (out_dns_primary_address) *out_dns_primary_address = g_steal_pointer (&dns_primary_address); if (out_dns_secondary_address) *out_dns_secondary_address = g_steal_pointer (&dns_secondary_address); return TRUE; } /*************************************************************************/ gboolean mm_3gpp_parse_cfun_query_response (const gchar *response, guint *out_state, GError **error) { g_autoptr(GRegex) r = NULL; g_autoptr(GMatchInfo) match_info = NULL; GError *inner_error = NULL; guint state = G_MAXUINT; g_assert (out_state != NULL); /* Response may be e.g.: * +CFUN: 1,0 * ..but we don't care about the second number */ r = g_regex_new ("\\+CFUN: (\\d+)(?:,(?:\\d+))?(?:\\r\\n)?", 0, 0, NULL); g_assert (r != NULL); g_regex_match_full (r, response, strlen (response), 0, 0, &match_info, &inner_error); if (inner_error) goto out; if (!g_match_info_matches (match_info)) { inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't parse +CFUN response: %s", response); goto out; } if (!mm_get_uint_from_match_info (match_info, 1, &state)) { inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't read power state value"); goto out; } *out_state = state; out: if (inner_error) { g_propagate_error (error, inner_error); return FALSE; } return TRUE; } /*****************************************************************************/ /* +CESQ response parser */ gboolean mm_3gpp_parse_cesq_response (const gchar *response, guint *out_rxlev, guint *out_ber, guint *out_rscp, guint *out_ecn0, guint *out_rsrq, guint *out_rsrp, GError **error) { g_autoptr(GRegex) r = NULL; g_autoptr(GMatchInfo) match_info = NULL; GError *inner_error = NULL; guint rxlev = 99; guint ber = 99; guint rscp = 255; guint ecn0 = 255; guint rsrq = 255; guint rsrp = 255; gboolean success = FALSE; g_assert (out_rxlev); g_assert (out_ber); g_assert (out_rscp); g_assert (out_ecn0); g_assert (out_rsrq); g_assert (out_rsrp); /* Response may be e.g.: * +CESQ: 99,99,255,255,20,80 */ r = g_regex_new ("\\+CESQ: (\\d+),(\\d+),(\\d+),(\\d+),(\\d+),(\\d+)(?:\\r\\n)?", 0, 0, NULL); g_assert (r != NULL); g_regex_match_full (r, response, strlen (response), 0, 0, &match_info, &inner_error); if (!inner_error && g_match_info_matches (match_info)) { if (!mm_get_uint_from_match_info (match_info, 1, &rxlev)) { inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't read RXLEV"); goto out; } if (!mm_get_uint_from_match_info (match_info, 2, &ber)) { inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't read BER"); goto out; } if (!mm_get_uint_from_match_info (match_info, 3, &rscp)) { inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't read RSCP"); goto out; } if (!mm_get_uint_from_match_info (match_info, 4, &ecn0)) { inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't read Ec/N0"); goto out; } if (!mm_get_uint_from_match_info (match_info, 5, &rsrq)) { inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't read RSRQ"); goto out; } if (!mm_get_uint_from_match_info (match_info, 6, &rsrp)) { inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't read RSRP"); goto out; } success = TRUE; } out: if (inner_error) { g_propagate_error (error, inner_error); return FALSE; } if (!success) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't parse +CESQ response: %s", response); return FALSE; } *out_rxlev = rxlev; *out_ber = ber; *out_rscp = rscp; *out_ecn0 = ecn0; *out_rsrq = rsrq; *out_rsrp = rsrp; return TRUE; } gboolean mm_3gpp_rxlev_to_rssi (guint rxlev, gpointer log_object, gdouble *out_rssi) { if (rxlev <= 63) { *out_rssi = -111.0 + rxlev; return TRUE; } if (rxlev != 99) mm_obj_warn (log_object, "unexpected rxlev: %u", rxlev); return FALSE; } gboolean mm_3gpp_rscp_level_to_rscp (guint rscp_level, gpointer log_object, gdouble *out_rscp) { if (rscp_level <= 96) { *out_rscp = -121.0 + rscp_level; return TRUE; } if (rscp_level != 255) mm_obj_warn (log_object, "unexpected rscp level: %u", rscp_level); return FALSE; } gboolean mm_3gpp_ecn0_level_to_ecio (guint ecn0_level, gpointer log_object, gdouble *out_ecio) { if (ecn0_level <= 49) { *out_ecio = -24.5 + (((gdouble) ecn0_level) * 0.5); return TRUE; } if (ecn0_level != 255) mm_obj_warn (log_object, "unexpected Ec/N0 level: %u", ecn0_level); return FALSE; } gboolean mm_3gpp_rsrq_level_to_rsrq (guint rsrq_level, gpointer log_object, gdouble *out_rsrq) { if (rsrq_level <= 34) { *out_rsrq = -20.0 + (((gdouble) rsrq_level) * 0.5); return TRUE; } if (rsrq_level != 255) mm_obj_warn (log_object, "unexpected RSRQ level: %u", rsrq_level); return FALSE; } gboolean mm_3gpp_rsrp_level_to_rsrp (guint rsrp_level, gpointer log_object, gdouble *out_rsrp) { if (rsrp_level <= 97) { *out_rsrp = -141.0 + rsrp_level; return TRUE; } if (rsrp_level != 255) mm_obj_warn (log_object, "unexpected RSRP level: %u", rsrp_level); return FALSE; } gboolean mm_3gpp_cesq_response_to_signal_info (const gchar *response, gpointer log_object, MMSignal **out_gsm, MMSignal **out_umts, MMSignal **out_lte, GError **error) { guint rxlev = 0; guint ber = 0; guint rscp_level = 0; guint ecn0_level = 0; guint rsrq_level = 0; guint rsrp_level = 0; gdouble rssi = -G_MAXDOUBLE; gdouble rscp = -G_MAXDOUBLE; gdouble ecio = -G_MAXDOUBLE; gdouble rsrq = -G_MAXDOUBLE; gdouble rsrp = -G_MAXDOUBLE; MMSignal *gsm = NULL; MMSignal *umts = NULL; MMSignal *lte = NULL; if (!mm_3gpp_parse_cesq_response (response, &rxlev, &ber, &rscp_level, &ecn0_level, &rsrq_level, &rsrp_level, error)) return FALSE; /* GERAN RSSI */ if (mm_3gpp_rxlev_to_rssi (rxlev, log_object, &rssi)) { gsm = mm_signal_new (); mm_signal_set_rssi (gsm, rssi); } /* ignore BER */ /* UMTS RSCP */ if (mm_3gpp_rscp_level_to_rscp (rscp_level, log_object, &rscp)) { umts = mm_signal_new (); mm_signal_set_rscp (umts, rscp); } /* UMTS EcIo (assumed EcN0) */ if (mm_3gpp_ecn0_level_to_ecio (ecn0_level, log_object, &ecio)) { if (!umts) umts = mm_signal_new (); mm_signal_set_ecio (umts, ecio); } /* LTE RSRQ */ if (mm_3gpp_rsrq_level_to_rsrq (rsrq_level, log_object, &rsrq)) { lte = mm_signal_new (); mm_signal_set_rsrq (lte, rsrq); } /* LTE RSRP */ if (mm_3gpp_rsrp_level_to_rsrp (rsrp_level, log_object, &rsrp)) { if (!lte) lte = mm_signal_new (); mm_signal_set_rsrp (lte, rsrp); } if (!gsm && !umts && !lte) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't build detailed signal info"); return FALSE; } if (gsm) *out_gsm = gsm; if (umts) *out_umts = umts; if (lte) *out_lte = lte; return TRUE; } gboolean mm_3gpp_parse_cfun_query_generic_response (const gchar *response, MMModemPowerState *out_state, GError **error) { guint state; if (!mm_3gpp_parse_cfun_query_response (response, &state, error)) return FALSE; switch (state) { case 0: *out_state = MM_MODEM_POWER_STATE_OFF; return TRUE; case 1: *out_state = MM_MODEM_POWER_STATE_ON; return TRUE; case 4: *out_state = MM_MODEM_POWER_STATE_LOW; return TRUE; default: g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Unknown +CFUN state: %u", state); return FALSE; } } static MMModem3gppEpsUeModeOperation cemode_values[] = { [0] = MM_MODEM_3GPP_EPS_UE_MODE_OPERATION_PS_2, [1] = MM_MODEM_3GPP_EPS_UE_MODE_OPERATION_CSPS_1, [2] = MM_MODEM_3GPP_EPS_UE_MODE_OPERATION_CSPS_2, [3] = MM_MODEM_3GPP_EPS_UE_MODE_OPERATION_PS_1, }; gchar * mm_3gpp_build_cemode_set_request (MMModem3gppEpsUeModeOperation mode) { guint i; g_return_val_if_fail (mode != MM_MODEM_3GPP_EPS_UE_MODE_OPERATION_UNKNOWN, NULL); for (i = 0; i < G_N_ELEMENTS (cemode_values); i++) { if (mode == cemode_values[i]) return g_strdup_printf ("+CEMODE=%u", i); } g_assert_not_reached (); return NULL; } gboolean mm_3gpp_parse_cemode_query_response (const gchar *response, MMModem3gppEpsUeModeOperation *out_mode, GError **error) { guint value = 0; response = mm_strip_tag (response, "+CEMODE:"); if (mm_get_uint_from_str (response, &value) && value < G_N_ELEMENTS (cemode_values)) { if (out_mode) *out_mode = cemode_values[value]; return TRUE; } g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't parse UE mode of operation: '%s' (value %u)", response, value); return FALSE; } /*************************************************************************/ /* CCWA service query response parser */ gboolean mm_3gpp_parse_ccwa_service_query_response (const gchar *response, gpointer log_object, gboolean *status, GError **error) { g_autoptr(GRegex) r = NULL; g_autoptr(GMatchInfo) match_info = NULL; GError *inner_error = NULL; gint class_1_status = -1; /* * AT+CCWA=[,] * +CCWA: , * [+CCWA: , * [...]] * OK * * If is 255 it applies to ALL classes. * * We're only interested in class 1 (voice) */ r = g_regex_new ("\\+CCWA:\\s*(\\d+),\\s*(\\d+)$", G_REGEX_RAW | G_REGEX_MULTILINE | G_REGEX_NEWLINE_CRLF, G_REGEX_MATCH_NEWLINE_CRLF, NULL); g_assert (r != NULL); g_regex_match_full (r, response, strlen (response), 0, 0, &match_info, &inner_error); if (inner_error) goto out; /* Parse the results */ while (g_match_info_matches (match_info)) { guint st; guint class; if (!mm_get_uint_from_match_info (match_info, 2, &class)) mm_obj_warn (log_object, "couldn't parse class from +CCWA line"); else if (class == 1 || class == 255) { if (!mm_get_uint_from_match_info (match_info, 1, &st)) mm_obj_warn (log_object, "couldn't parse status from +CCWA line"); else { class_1_status = st; break; } } g_match_info_next (match_info, NULL); } out: if (inner_error) { g_propagate_error (error, inner_error); return FALSE; } if (class_1_status < 0) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_NOT_FOUND, "call waiting status for voice class missing"); return FALSE; } if (class_1_status != 0 && class_1_status != 1) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "call waiting status for voice class invalid: %d", class_1_status); return FALSE; } if (status) *status = (gboolean) class_1_status; return TRUE; } /*************************************************************************/ /* CGATT helpers */ gchar * mm_3gpp_build_cgatt_set_request (MMModem3gppPacketServiceState state) { guint cgatt_action; switch (state) { case MM_MODEM_3GPP_PACKET_SERVICE_STATE_ATTACHED: cgatt_action = 1; break; case MM_MODEM_3GPP_PACKET_SERVICE_STATE_DETACHED: cgatt_action = 0; break; case MM_MODEM_3GPP_PACKET_SERVICE_STATE_UNKNOWN: default: return NULL; } return g_strdup_printf ("+CGATT=%u", cgatt_action); } /*************************************************************************/ static MMSmsStorage storage_from_str (const gchar *str) { if (g_str_equal (str, "SM")) return MM_SMS_STORAGE_SM; if (g_str_equal (str, "ME")) return MM_SMS_STORAGE_ME; if (g_str_equal (str, "MT")) return MM_SMS_STORAGE_MT; if (g_str_equal (str, "SR")) return MM_SMS_STORAGE_SR; if (g_str_equal (str, "BM")) return MM_SMS_STORAGE_BM; if (g_str_equal (str, "TA")) return MM_SMS_STORAGE_TA; return MM_SMS_STORAGE_UNKNOWN; } gboolean mm_3gpp_parse_cpms_test_response (const gchar *reply, GArray **mem1, GArray **mem2, GArray **mem3, GError **error) { guint i; g_autoptr(GRegex) r = NULL; g_autoptr(GArray) tmp1 = NULL; g_autoptr(GArray) tmp2 = NULL; g_autoptr(GArray) tmp3 = NULL; g_auto(GStrv) split = NULL; g_assert (mem1 != NULL); g_assert (mem2 != NULL); g_assert (mem3 != NULL); #define N_EXPECTED_GROUPS 3 split = mm_split_string_groups (mm_strip_tag (reply, "+CPMS:")); if (!split) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't split +CPMS test response in groups"); return FALSE; } if (g_strv_length (split) != N_EXPECTED_GROUPS) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Cannot parse +CPMS test response: invalid number of groups (%u != %u)", g_strv_length (split), N_EXPECTED_GROUPS); return FALSE; } r = g_regex_new ("\\s*\"([^,\\)]+)\"\\s*", 0, 0, NULL); g_assert (r); for (i = 0; i < N_EXPECTED_GROUPS; i++) { g_autoptr(GMatchInfo) match_info = NULL; GArray *array; /* We always return a valid array, even if it may be empty */ array = g_array_new (FALSE, FALSE, sizeof (MMSmsStorage)); /* Got a range group to match */ if (g_regex_match (r, split[i], 0, &match_info)) { while (g_match_info_matches (match_info)) { gchar *str; str = g_match_info_fetch (match_info, 1); if (str) { MMSmsStorage storage; storage = storage_from_str (str); g_array_append_val (array, storage); g_free (str); } g_match_info_next (match_info, NULL); } } if (!tmp1) tmp1 = array; else if (!tmp2) tmp2 = array; else if (!tmp3) tmp3 = array; else g_assert_not_reached (); } /* Only return TRUE if all sets have been parsed correctly * (even if the arrays may be empty) */ if (tmp1 && tmp2 && tmp3) { *mem1 = g_steal_pointer (&tmp1); *mem2 = g_steal_pointer (&tmp2); *mem3 = g_steal_pointer (&tmp3); return TRUE; } g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Cannot parse +CPMS test response: mem1 %s, mem2 %s, mem3 %s", tmp1 ? "yes" : "no", tmp2 ? "yes" : "no", tmp3 ? "yes" : "no"); return FALSE; } /********************************************************************** * AT+CPMS? * +CPMS: ,,,,,, ,, */ #define CPMS_QUERY_REGEX "\\+CPMS:\\s*\"(?P.*)\",[0-9]+,[0-9]+,\"(?P.*)\",[0-9]+,[0-9]+,\"(?P.*)\",[0-9]+,[0-9]" gboolean mm_3gpp_parse_cpms_query_response (const gchar *reply, MMSmsStorage *memr, MMSmsStorage *memw, GError **error) { g_autoptr(GRegex) r = NULL; g_autoptr(GMatchInfo) match_info = NULL; r = g_regex_new (CPMS_QUERY_REGEX, G_REGEX_RAW, 0, NULL); g_assert (r); if (!g_regex_match (r, reply, 0, &match_info)) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Could not parse CPMS query response '%s'", reply); return FALSE; } if (!g_match_info_matches (match_info)) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Could not find matches in CPMS query reply '%s'", reply); return FALSE; } if (!mm_3gpp_get_cpms_storage_match (match_info, "memr", memr, error)) return FALSE; if (!mm_3gpp_get_cpms_storage_match (match_info, "memw", memw, error)) return FALSE; return TRUE; } gboolean mm_3gpp_get_cpms_storage_match (GMatchInfo *match_info, const gchar *match_name, MMSmsStorage *storage, GError **error) { gboolean ret = TRUE; gchar *str = NULL; str = g_match_info_fetch_named (match_info, match_name); if (str == NULL || str[0] == '\0') { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Could not find '%s' from CPMS reply", match_name); ret = FALSE; } else { *storage = storage_from_str (str); } g_free (str); return ret; } /*************************************************************************/ gboolean mm_3gpp_parse_cscs_test_response (const gchar *reply, MMModemCharset *out_charsets) { g_autoptr(GRegex) r = NULL; g_autoptr(GMatchInfo) match_info = NULL; MMModemCharset charsets = MM_MODEM_CHARSET_UNKNOWN; gchar *p; gchar *str; gboolean success = FALSE; g_return_val_if_fail (reply != NULL, FALSE); g_return_val_if_fail (out_charsets != NULL, FALSE); /* Find the first '(' or '"'; the general format is: * * +CSCS: ("IRA","GSM","UCS2") * * but some devices (some Blackberries) don't include the (). */ p = strchr (reply, '('); if (p) p++; else { p = strchr (reply, '"'); if (!p) return FALSE; } /* Now parse each charset */ r = g_regex_new ("\\s*([^,\\)]+)\\s*", 0, 0, NULL); g_assert (r); if (g_regex_match (r, p, 0, &match_info)) { while (g_match_info_matches (match_info)) { str = g_match_info_fetch (match_info, 1); charsets |= mm_modem_charset_from_string (str); g_free (str); g_match_info_next (match_info, NULL); success = TRUE; } } if (success) *out_charsets = charsets; return success; } /*************************************************************************/ gboolean mm_3gpp_parse_clck_test_response (const gchar *reply, MMModem3gppFacility *out_facilities) { g_autoptr(GRegex) r = NULL; g_autoptr(GMatchInfo) match_info = NULL; g_return_val_if_fail (reply != NULL, FALSE); g_return_val_if_fail (out_facilities != NULL, FALSE); /* the general format is: * * +CLCK: ("SC","AO","AI","PN") */ reply = mm_strip_tag (reply, "+CLCK:"); /* Now parse each facility */ r = g_regex_new ("\\s*\"([^,\\)]+)\"\\s*", 0, 0, NULL); g_assert (r != NULL); *out_facilities = MM_MODEM_3GPP_FACILITY_NONE; if (g_regex_match (r, reply, 0, &match_info)) { while (g_match_info_matches (match_info)) { gchar *str; str = g_match_info_fetch (match_info, 1); if (str) { *out_facilities |= mm_3gpp_acronym_to_facility (str); g_free (str); } g_match_info_next (match_info, NULL); } } return (*out_facilities != MM_MODEM_3GPP_FACILITY_NONE); } /*************************************************************************/ gboolean mm_3gpp_parse_clck_write_response (const gchar *reply, gboolean *enabled) { g_autoptr(GRegex) r = NULL; g_autoptr(GMatchInfo) match_info = NULL; g_return_val_if_fail (reply != NULL, FALSE); g_return_val_if_fail (enabled != NULL, FALSE); reply = mm_strip_tag (reply, "+CLCK:"); r = g_regex_new ("\\s*([01])\\s*", 0, 0, NULL); g_assert (r != NULL); if (g_regex_match (r, reply, 0, &match_info)) { g_autofree gchar *str = NULL; str = g_match_info_fetch (match_info, 1); if (str) { /* We're trying to match either '0' or '1', * so we don't expect any other thing */ if (*str == '0') *enabled = FALSE; else if (*str == '1') *enabled = TRUE; else g_assert_not_reached (); return TRUE; } } return FALSE; } /*************************************************************************/ GStrv mm_3gpp_parse_cnum_exec_response (const gchar *reply) { g_autoptr(GPtrArray) array = NULL; g_autoptr(GRegex) r = NULL; g_autoptr(GMatchInfo) match_info = NULL; /* Empty strings also return NULL list */ if (!reply || !reply[0]) return NULL; r = g_regex_new ("\\+CNUM:\\s*((\"([^\"]|(\\\"))*\")|([^,]*)),\"(?\\S+)\",\\d", G_REGEX_UNGREEDY, 0, NULL); g_assert (r != NULL); array = g_ptr_array_new (); g_regex_match (r, reply, 0, &match_info); while (g_match_info_matches (match_info)) { g_autofree gchar *number = NULL; number = g_match_info_fetch_named (match_info, "num"); if (number && number[0]) g_ptr_array_add (array, g_steal_pointer (&number)); g_match_info_next (match_info, NULL); } if (!array->len) return NULL; g_ptr_array_add (array, NULL); return (GStrv) g_ptr_array_free (g_steal_pointer (&array), FALSE); } /*************************************************************************/ gchar * mm_3gpp_build_cmer_set_request (MM3gppCmerMode mode, MM3gppCmerInd ind) { guint mode_val; guint ind_val; if (mode == MM_3GPP_CMER_MODE_DISCARD_URCS) return g_strdup ("+CMER=0"); if (mode < MM_3GPP_CMER_MODE_DISCARD_URCS || mode > MM_3GPP_CMER_MODE_FORWARD_URCS) return NULL; mode_val = mm_find_bit_set (mode); if (ind < MM_3GPP_CMER_IND_DISABLE || ind > MM_3GPP_CMER_IND_ENABLE_ALL) return NULL; ind_val = mm_find_bit_set (ind); return g_strdup_printf ("+CMER=%u,0,0,%u", mode_val, ind_val); } gboolean mm_3gpp_parse_cmer_test_response (const gchar *response, gpointer log_object, MM3gppCmerMode *out_supported_modes, MM3gppCmerInd *out_supported_inds, GError **error) { gchar **split; GError *inner_error = NULL; GArray *array_supported_modes = NULL; GArray *array_supported_inds = NULL; gchar *aux = NULL; gboolean ret = FALSE; MM3gppCmerMode supported_modes = 0; MM3gppCmerInd supported_inds = 0; guint i; /* * AT+CMER=? * +CMER: 1,0,0,(0-1),0 * * AT+CMER=? * +CMER: (0-3),(0),(0),(0-1),(0-1) * * AT+CMER=? * +CMER: (1,2),0,0,(0-1),0 */ split = mm_split_string_groups (mm_strip_tag (response, "+CMER:")); if (!split) { inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't split +CMER test response in groups"); goto out; } /* We want 1st and 4th groups */ if (g_strv_length (split) < 4) { inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Missing groups in +CMER test response (%u < 4)", g_strv_length (split)); goto out; } /* Modes in 1st group */ if (!(array_supported_modes = mm_parse_uint_list (split[0], &inner_error))) goto out; g_clear_pointer (&aux, g_free); /* Ind settings in 4th group */ if (!(array_supported_inds = mm_parse_uint_list (split[3], &inner_error))) goto out; g_clear_pointer (&aux, g_free); for (i = 0; i < array_supported_modes->len; i++) { guint mode_val; mode_val = g_array_index (array_supported_modes, guint, i); if (mode_val <= 3) supported_modes |= (MM3gppCmerMode) (1 << mode_val); else mm_obj_dbg (log_object, "unknown +CMER mode reported: %u", mode_val); } for (i = 0; i < array_supported_inds->len; i++) { guint ind_val; ind_val = g_array_index (array_supported_inds, guint, i); if (ind_val <= 2) supported_inds |= (MM3gppCmerInd) (1 << ind_val); else mm_obj_dbg (log_object, "unknown +CMER ind reported: %u", ind_val); } if (out_supported_modes) *out_supported_modes = supported_modes; if (out_supported_inds) *out_supported_inds = supported_inds; ret = TRUE; out: if (array_supported_modes) g_array_unref (array_supported_modes); if (array_supported_inds) g_array_unref (array_supported_inds); g_clear_pointer (&aux, g_free); g_strfreev (split); if (inner_error) g_propagate_error (error, inner_error); return ret; } /*************************************************************************/ struct MM3gppCindResponse { gchar *desc; guint idx; gint min; gint max; }; static MM3gppCindResponse * cind_response_new (const gchar *desc, guint idx, gint min, gint max) { MM3gppCindResponse *r; gchar *p; g_return_val_if_fail (desc != NULL, NULL); r = g_malloc0 (sizeof (MM3gppCindResponse)); /* Strip quotes */ r->desc = p = g_malloc0 (strlen (desc) + 1); while (*desc) { if (*desc != '"' && !isspace (*desc)) *p++ = tolower (*desc); desc++; } r->idx = idx; r->max = max; r->min = min; return r; } static void cind_response_free (MM3gppCindResponse *r) { g_return_if_fail (r != NULL); g_free (r->desc); memset (r, 0, sizeof (MM3gppCindResponse)); g_free (r); } const gchar * mm_3gpp_cind_response_get_desc (MM3gppCindResponse *r) { g_return_val_if_fail (r != NULL, NULL); return r->desc; } guint mm_3gpp_cind_response_get_index (MM3gppCindResponse *r) { g_return_val_if_fail (r != NULL, 0); return r->idx; } gint mm_3gpp_cind_response_get_min (MM3gppCindResponse *r) { g_return_val_if_fail (r != NULL, -1); return r->min; } gint mm_3gpp_cind_response_get_max (MM3gppCindResponse *r) { g_return_val_if_fail (r != NULL, -1); return r->max; } #define CIND_TAG "+CIND:" GHashTable * mm_3gpp_parse_cind_test_response (const gchar *reply, GError **error) { g_autoptr(GRegex) r = NULL; g_autoptr(GMatchInfo) match_info = NULL; GHashTable *hash; guint idx = 1; g_return_val_if_fail (reply != NULL, NULL); /* Strip whitespace and response tag */ if (g_str_has_prefix (reply, CIND_TAG)) reply += strlen (CIND_TAG); while (isspace (*reply)) reply++; r = g_regex_new ("\\(([^,]*),\\((\\d+)[-,](\\d+).*\\)", G_REGEX_UNGREEDY, 0, NULL); g_assert (r); hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify) cind_response_free); if (g_regex_match (r, reply, 0, &match_info)) { while (g_match_info_matches (match_info)) { MM3gppCindResponse *resp; gchar *desc, *tmp; gint min = 0, max = 0; desc = g_match_info_fetch (match_info, 1); tmp = g_match_info_fetch (match_info, 2); min = atoi (tmp); g_free (tmp); tmp = g_match_info_fetch (match_info, 3); max = atoi (tmp); g_free (tmp); resp = cind_response_new (desc, idx++, min, max); if (resp) g_hash_table_insert (hash, g_strdup (resp->desc), resp); g_free (desc); g_match_info_next (match_info, NULL); } } return hash; } /*************************************************************************/ GByteArray * mm_3gpp_parse_cind_read_response (const gchar *reply, GError **error) { g_autoptr(GRegex) r = NULL; g_autoptr(GMatchInfo) match_info = NULL; GByteArray *array = NULL; GError *inner_error = NULL; guint8 t; g_return_val_if_fail (reply != NULL, NULL); if (!g_str_has_prefix (reply, CIND_TAG)) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Could not parse the +CIND response '%s': no CIND tag found", reply); return NULL; } reply = mm_strip_tag (reply, CIND_TAG); r = g_regex_new ("(\\d+)[^0-9]+", G_REGEX_UNGREEDY, 0, NULL); g_assert (r != NULL); if (!g_regex_match (r, reply, 0, &match_info)) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Could not parse the +CIND response '%s': didn't match", reply); return NULL; } array = g_byte_array_sized_new (g_match_info_get_match_count (match_info)); /* Add a zero element so callers can use 1-based indexes returned by * mm_3gpp_cind_response_get_index(). */ t = 0; g_byte_array_append (array, &t, 1); while (!inner_error && g_match_info_matches (match_info)) { g_autofree gchar *str = NULL; guint val = 0; str = g_match_info_fetch (match_info, 1); if (mm_get_uint_from_str (str, &val) && val < 255) { t = (guint8) val; g_byte_array_append (array, &t, 1); } else { inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Could not parse the +CIND response: invalid index '%s'", str); } g_match_info_next (match_info, NULL); } if (inner_error) { g_propagate_error (error, inner_error); g_clear_pointer (&array, g_byte_array_unref); } return array; } /*************************************************************************/ /* +CGEV indication parser * * We provide full parsing support, including parameters, for these messages: * +CGEV: NW DETACH * +CGEV: ME DETACH * +CGEV: NW PDN ACT * +CGEV: ME PDN ACT [,[,]] * +CGEV: NW ACT , , * +CGEV: ME ACT , , * +CGEV: NW DEACT , , [] * +CGEV: ME DEACT , , [] * +CGEV: NW PDN DEACT * +CGEV: ME PDN DEACT * +CGEV: NW DEACT , , * +CGEV: ME DEACT , , * +CGEV: REJECT , * +CGEV: NW REACT , , [] * * We don't provide parameter parsing for these messages: * +CGEV: NW CLASS * +CGEV: ME CLASS * +CGEV: NW MODIFY , , * +CGEV: ME MODIFY , , */ static gboolean deact_secondary (const gchar *str) { /* We need to detect the ME/NW DEACT format. * Either, * +CGEV: NW DEACT , , [] * +CGEV: ME DEACT , , [] * or, * +CGEV: NW DEACT , , * +CGEV: ME DEACT , , */ str = strstr (str, "DEACT") + 5; while (*str == ' ') str++; /* We will look for because we know it's NUMERIC */ return g_ascii_isdigit (*str); } MM3gppCgev mm_3gpp_parse_cgev_indication_action (const gchar *str) { str = mm_strip_tag (str, "+CGEV:"); if (g_str_has_prefix (str, "NW DETACH")) return MM_3GPP_CGEV_NW_DETACH; if (g_str_has_prefix (str, "ME DETACH")) return MM_3GPP_CGEV_ME_DETACH; if (g_str_has_prefix (str, "NW CLASS")) return MM_3GPP_CGEV_NW_CLASS; if (g_str_has_prefix (str, "ME CLASS")) return MM_3GPP_CGEV_ME_CLASS; if (g_str_has_prefix (str, "NW PDN ACT")) return MM_3GPP_CGEV_NW_ACT_PRIMARY; if (g_str_has_prefix (str, "ME PDN ACT")) return MM_3GPP_CGEV_ME_ACT_PRIMARY; if (g_str_has_prefix (str, "NW ACT")) return MM_3GPP_CGEV_NW_ACT_SECONDARY; if (g_str_has_prefix (str, "ME ACT")) return MM_3GPP_CGEV_ME_ACT_SECONDARY; if (g_str_has_prefix (str, "NW DEACT")) return (deact_secondary (str) ? MM_3GPP_CGEV_NW_DEACT_SECONDARY : MM_3GPP_CGEV_NW_DEACT_PDP); if (g_str_has_prefix (str, "ME DEACT")) return (deact_secondary (str) ? MM_3GPP_CGEV_ME_DEACT_SECONDARY : MM_3GPP_CGEV_ME_DEACT_PDP); if (g_str_has_prefix (str, "NW PDN DEACT")) return MM_3GPP_CGEV_NW_DEACT_PRIMARY; if (g_str_has_prefix (str, "ME PDN DEACT")) return MM_3GPP_CGEV_ME_DEACT_PRIMARY; if (g_str_has_prefix (str, "NW MODIFY")) return MM_3GPP_CGEV_NW_MODIFY; if (g_str_has_prefix (str, "ME MODIFY")) return MM_3GPP_CGEV_ME_MODIFY; if (g_str_has_prefix (str, "NW REACT")) return MM_3GPP_CGEV_NW_REACT; if (g_str_has_prefix (str, "REJECT")) return MM_3GPP_CGEV_REJECT; return MM_3GPP_CGEV_UNKNOWN; } /* * +CGEV: NW DEACT , , [] * +CGEV: ME DEACT , , [] * +CGEV: REJECT , * +CGEV: NW REACT , , [] */ gboolean mm_3gpp_parse_cgev_indication_pdp (const gchar *str, MM3gppCgev type, gchar **out_pdp_type, gchar **out_pdp_addr, guint *out_cid, GError **error) { g_autoptr(GRegex) r = NULL; g_autoptr(GMatchInfo) match_info = NULL; GError *inner_error = NULL; g_autofree gchar *pdp_type = NULL; g_autofree gchar *pdp_addr = NULL; guint cid = 0; g_assert (type == MM_3GPP_CGEV_REJECT || type == MM_3GPP_CGEV_NW_REACT || type == MM_3GPP_CGEV_NW_DEACT_PDP || type == MM_3GPP_CGEV_ME_DEACT_PDP); r = g_regex_new ("(?:" "REJECT|" "NW REACT|" "NW DEACT|ME DEACT" ")\\s*([^,]*),\\s*([^,]*)(?:,\\s*([0-9]+))?", 0, 0, NULL); g_assert (r); str = mm_strip_tag (str, "+CGEV:"); g_regex_match_full (r, str, strlen (str), 0, 0, &match_info, &inner_error); if (inner_error) { g_propagate_error (error, inner_error); return FALSE; } if (!g_match_info_matches (match_info)) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't match response"); return FALSE; } if (out_pdp_type && !(pdp_type = mm_get_string_unquoted_from_match_info (match_info, 1))) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Error parsing PDP type"); return FALSE; } if (out_pdp_addr && !(pdp_addr = mm_get_string_unquoted_from_match_info (match_info, 2))) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Error parsing PDP addr"); return FALSE; } /* CID is optional */ if (out_cid && (g_match_info_get_match_count (match_info) >= 4) && !mm_get_uint_from_match_info (match_info, 3, &cid)) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Error parsing CID"); return FALSE; } if (out_pdp_type) *out_pdp_type = g_steal_pointer (&pdp_type); if (out_pdp_addr) *out_pdp_addr = g_steal_pointer (&pdp_addr); if (out_cid) *out_cid = cid; return TRUE; } /* * +CGEV: NW PDN ACT * +CGEV: ME PDN ACT [,[,]] * +CGEV: NW PDN DEACT * +CGEV: ME PDN DEACT * * NOTE: the special case of a "ME PDN ACT" notification with the additional * and fields is telling us that was NOT connected * but was connected instead, which may happen when trying to * connect a IPv4v6 context but the modem ended up connecting a IPv4-only or * IPv6-only context instead. We are right now ignoring this, and assuming the * that we requested is the one reported as connected. */ gboolean mm_3gpp_parse_cgev_indication_primary (const gchar *str, MM3gppCgev type, guint *out_cid, GError **error) { g_autoptr(GRegex) r = NULL; g_autoptr(GMatchInfo) match_info = NULL; GError *inner_error = NULL; guint cid = 0; g_assert ((type == MM_3GPP_CGEV_NW_ACT_PRIMARY) || (type == MM_3GPP_CGEV_ME_ACT_PRIMARY) || (type == MM_3GPP_CGEV_NW_DEACT_PRIMARY) || (type == MM_3GPP_CGEV_ME_DEACT_PRIMARY)); r = g_regex_new ("(?:" "NW PDN ACT|ME PDN ACT|" "NW PDN DEACT|ME PDN DEACT|" ")\\s*([0-9]+)", 0, 0, NULL); str = mm_strip_tag (str, "+CGEV:"); g_regex_match_full (r, str, strlen (str), 0, 0, &match_info, &inner_error); if (inner_error) goto out; if (!g_match_info_matches (match_info)) { inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't match response"); goto out; } if (out_cid && !mm_get_uint_from_match_info (match_info, 1, &cid)) { inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Error parsing CID"); goto out; } out: if (inner_error) { g_propagate_error (error, inner_error); return FALSE; } if (out_cid) *out_cid = cid; return TRUE; } /* * +CGEV: NW ACT , , * +CGEV: ME ACT , , * +CGEV: NW DEACT , , * +CGEV: ME DEACT , , */ gboolean mm_3gpp_parse_cgev_indication_secondary (const gchar *str, MM3gppCgev type, guint *out_p_cid, guint *out_cid, guint *out_event_type, GError **error) { g_autoptr(GRegex) r = NULL; g_autoptr(GMatchInfo) match_info = NULL; GError *inner_error = NULL; guint p_cid = 0; guint cid = 0; guint event_type = 0; g_assert (type == MM_3GPP_CGEV_NW_ACT_SECONDARY || type == MM_3GPP_CGEV_ME_ACT_SECONDARY || type == MM_3GPP_CGEV_NW_DEACT_SECONDARY || type == MM_3GPP_CGEV_ME_DEACT_SECONDARY); r = g_regex_new ("(?:" "NW ACT|ME ACT|" "NW DEACT|ME DEACT" ")\\s*([0-9]+),\\s*([0-9]+),\\s*([0-9]+)", 0, 0, NULL); str = mm_strip_tag (str, "+CGEV:"); g_regex_match_full (r, str, strlen (str), 0, 0, &match_info, &inner_error); if (inner_error) goto out; if (!g_match_info_matches (match_info)) { inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't match response"); goto out; } if (out_p_cid && !mm_get_uint_from_match_info (match_info, 1, &p_cid)) { inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Error parsing primary CID"); goto out; } if (out_cid && !mm_get_uint_from_match_info (match_info, 2, &cid)) { inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Error parsing secondary CID"); goto out; } if (out_event_type && !mm_get_uint_from_match_info (match_info, 3, &event_type)) { inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Error parsing event type"); goto out; } out: if (inner_error) { g_propagate_error (error, inner_error); return FALSE; } if (out_p_cid) *out_p_cid = p_cid; if (out_cid) *out_cid = cid; if (out_event_type) *out_event_type = event_type; return TRUE; } /*************************************************************************/ void mm_3gpp_pdu_info_free (MM3gppPduInfo *info) { g_free (info->pdu); g_free (info); } void mm_3gpp_pdu_info_list_free (GList *info_list) { g_list_free_full (info_list, (GDestroyNotify)mm_3gpp_pdu_info_free); } GList * mm_3gpp_parse_pdu_cmgl_response (const gchar *str, GError **error) { g_autoptr(GRegex) r = NULL; g_autoptr(GMatchInfo) match_info = NULL; GError *inner_error = NULL; GList *list = NULL; /* * +CMGL: , , [], * or * +CMGL: , , * * We just read , and the PDU itself. */ r = g_regex_new ("\\+CMGL:\\s*(\\d+)\\s*,\\s*(\\d+)\\s*,(.*)\\r\\n([^\\r\\n]*)(\\r\\n)?", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); g_assert (r != NULL); g_regex_match_full (r, str, strlen (str), 0, 0, &match_info, &inner_error); while (!inner_error && g_match_info_matches (match_info)) { MM3gppPduInfo *info; info = g_new0 (MM3gppPduInfo, 1); if (mm_get_int_from_match_info (match_info, 1, &info->index) && mm_get_int_from_match_info (match_info, 2, &info->status) && (info->pdu = mm_get_string_unquoted_from_match_info (match_info, 4)) != NULL) { /* Append to our list of results and keep on */ list = g_list_append (list, info); g_match_info_next (match_info, &inner_error); } else { mm_3gpp_pdu_info_free (info); inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Error parsing +CMGL response: '%s'", str); } } if (inner_error) { g_propagate_error (error, inner_error); mm_3gpp_pdu_info_list_free (list); return NULL; } return list; } /*************************************************************************/ /* Map two letter facility codes into flag values. There are * many more facilities defined (for various flavors of call * barring); we only map the ones we care about. */ typedef struct { MMModem3gppFacility facility; const gchar *acronym; } FacilityAcronym; static const FacilityAcronym facility_acronyms[] = { { MM_MODEM_3GPP_FACILITY_SIM, "SC" }, { MM_MODEM_3GPP_FACILITY_PH_SIM, "PS" }, { MM_MODEM_3GPP_FACILITY_PH_FSIM, "PF" }, { MM_MODEM_3GPP_FACILITY_FIXED_DIALING, "FD" }, { MM_MODEM_3GPP_FACILITY_NET_PERS, "PN" }, { MM_MODEM_3GPP_FACILITY_NET_SUB_PERS, "PU" }, { MM_MODEM_3GPP_FACILITY_PROVIDER_PERS, "PP" }, { MM_MODEM_3GPP_FACILITY_CORP_PERS, "PC" } }; MMModem3gppFacility mm_3gpp_acronym_to_facility (const gchar *str) { guint i; for (i = 0; i < G_N_ELEMENTS (facility_acronyms); i++) { if (g_str_equal (facility_acronyms[i].acronym, str)) return facility_acronyms[i].facility; } return MM_MODEM_3GPP_FACILITY_NONE; } const gchar * mm_3gpp_facility_to_acronym (MMModem3gppFacility facility) { guint i; for (i = 0; i < G_N_ELEMENTS (facility_acronyms); i++) { if (facility_acronyms[i].facility == facility) return facility_acronyms[i].acronym; } return NULL; } /*************************************************************************/ MMModemAccessTechnology mm_string_to_access_tech (const gchar *string) { MMModemAccessTechnology act = MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN; gsize len; g_return_val_if_fail (string != NULL, MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN); /* We're returning a MASK of technologies found; so we can include more * than one technology in the result */ if (strcasestr (string, "LTE") || strcasestr (string, "4G")) act |= MM_MODEM_ACCESS_TECHNOLOGY_LTE; if (strcasestr (string, "HSPA+")) act |= MM_MODEM_ACCESS_TECHNOLOGY_HSPA_PLUS; else if (strcasestr (string, "HSPA")) act |= MM_MODEM_ACCESS_TECHNOLOGY_HSPA; if (strcasestr (string, "HSUPA")) act |= MM_MODEM_ACCESS_TECHNOLOGY_HSUPA; if (strcasestr (string, "HSDPA")) act |= MM_MODEM_ACCESS_TECHNOLOGY_HSDPA; if (strcasestr (string, "UMTS") || strcasestr (string, "3G") || strcasestr (string, "WCDMA")) act |= MM_MODEM_ACCESS_TECHNOLOGY_UMTS; if (strcasestr (string, "EDGE")) act |= MM_MODEM_ACCESS_TECHNOLOGY_EDGE; if (strcasestr (string, "GPRS")) act |= MM_MODEM_ACCESS_TECHNOLOGY_GPRS; if (strcasestr (string, "GSM")) act |= MM_MODEM_ACCESS_TECHNOLOGY_GSM; if (strcasestr (string, "EvDO Rel0")) act |= MM_MODEM_ACCESS_TECHNOLOGY_EVDO0; if (strcasestr (string, "EvDO RelA")) act |= MM_MODEM_ACCESS_TECHNOLOGY_EVDOA; if (strcasestr (string, "EvDO RelB")) act |= MM_MODEM_ACCESS_TECHNOLOGY_EVDOB; if (strcasestr (string, "1xRTT") || strcasestr (string, "CDMA2000 1X")) act |= MM_MODEM_ACCESS_TECHNOLOGY_1XRTT; /* Check "EVDO" and "CDMA" as standalone strings since their characters * are included in other strings too. */ len = strlen (string); if (strncmp (string, "EVDO", 4) && (len >= 4 && !isalnum (string[4]))) act |= MM_MODEM_ACCESS_TECHNOLOGY_EVDO0; if (strncmp (string, "CDMA", 4) && (len >= 4 && !isalnum (string[4]))) act |= MM_MODEM_ACCESS_TECHNOLOGY_1XRTT; if (strncmp (string, "CDMA-EVDO", 9) && (len >= 9 && !isalnum (string[9]))) act |= MM_MODEM_ACCESS_TECHNOLOGY_1XRTT | MM_MODEM_ACCESS_TECHNOLOGY_EVDO0; return act; } /*************************************************************************/ void mm_3gpp_normalize_operator (gchar **operator, MMModemCharset cur_charset, gpointer log_object) { g_autofree gchar *normalized = NULL; g_assert (operator); if (*operator == NULL) return; /* Despite +CSCS? may claim supporting UCS2, Some modems (e.g. Huawei) * always report the operator name in ASCII in a +COPS response. */ if (cur_charset != MM_MODEM_CHARSET_UNKNOWN) { g_autoptr(GError) error = NULL; normalized = mm_modem_charset_str_to_utf8 (*operator, -1, cur_charset, TRUE, &error); if (normalized) goto out; mm_obj_dbg (log_object, "couldn't convert operator string '%s' from charset '%s': %s", *operator, mm_modem_charset_to_string (cur_charset), error->message); } /* Charset is unknown or there was an error in conversion; try to see * if the contents we got are valid UTF-8 already. */ if (g_utf8_validate (*operator, -1, NULL)) normalized = g_strdup (*operator); out: /* Some modems (Novatel LTE) return the operator name as "Unknown" when * it fails to obtain the operator name. Return NULL in such case. */ if (!normalized || g_ascii_strcasecmp (normalized, "unknown") == 0) { /* If normalization failed, just cleanup the string */ g_clear_pointer (operator, g_free); return; } mm_obj_dbg (log_object, "operator normalized '%s'->'%s'", *operator, normalized); g_clear_pointer (operator, g_free); *operator = g_steal_pointer (&normalized); } /*************************************************************************/ const gchar * mm_3gpp_get_pdp_type_from_ip_family (MMBearerIpFamily family) { switch (family) { case MM_BEARER_IP_FAMILY_IPV4: return "IP"; case MM_BEARER_IP_FAMILY_IPV6: return "IPV6"; case MM_BEARER_IP_FAMILY_IPV4V6: return "IPV4V6"; case MM_BEARER_IP_FAMILY_NON_IP: return "Non-IP"; case MM_BEARER_IP_FAMILY_NONE: case MM_BEARER_IP_FAMILY_ANY: default: return NULL; } } MMBearerIpFamily mm_3gpp_get_ip_family_from_pdp_type (const gchar *pdp_type) { if (!pdp_type) return MM_BEARER_IP_FAMILY_NONE; if (g_str_equal (pdp_type, "IP")) return MM_BEARER_IP_FAMILY_IPV4; if (g_str_equal (pdp_type, "IPV4")) return MM_BEARER_IP_FAMILY_IPV4; if (g_str_equal (pdp_type, "IPV6")) return MM_BEARER_IP_FAMILY_IPV6; if (g_str_equal (pdp_type, "IPV4V6")) return MM_BEARER_IP_FAMILY_IPV4V6; if (g_str_equal (pdp_type, "Non-IP")) return MM_BEARER_IP_FAMILY_NON_IP; return MM_BEARER_IP_FAMILY_NONE; } gboolean mm_3gpp_normalize_ip_family (MMBearerIpFamily *family, gboolean from_user) { /* To address limitations in reading IP_TYPE information (in some cases) for * profile requests, default to IPv4v6 (dual-stack) for profile requests and * IPv4 only for user requests (to ensure backward compatibility) if nothing * specific is requested. This ensures network compatibility across IPv4 and IPv6 networks, * preventing potential connectivity issues in IPv6 environments. */ if (*family == MM_BEARER_IP_FAMILY_NONE || *family == MM_BEARER_IP_FAMILY_ANY) { *family = from_user ? MM_BEARER_IP_FAMILY_IPV4 : MM_BEARER_IP_FAMILY_IPV4V6; return TRUE; } /* no need to normalize */ return FALSE; } /*************************************************************************/ /* ICCID validation */ /* * 89: telecom (2 digits) * cc: country (2 digits) * oo: operator (2 digits) * aaaaaaaaaaaaa: operator-specific account number (13 digits) * x: checksum (1 digit) */ char * mm_3gpp_parse_iccid (const char *raw_iccid, GError **error) { gboolean swap; char *buf, *swapped = NULL; gsize len = 0; int i; g_return_val_if_fail (raw_iccid != NULL, NULL); /* Skip spaces and quotes */ while (raw_iccid && *raw_iccid && (isspace (*raw_iccid) || *raw_iccid == '"')) raw_iccid++; /* Make sure the buffer is only digits or 'F' */ buf = g_strdup (raw_iccid); for (len = 0; buf[len]; len++) { /* Digit values allowed anywhere */ if (g_ascii_isdigit (buf[len])) continue; /* There are operators (e.g. the Chinese CMCC operator) that abuse the * fact that 4 bits are used to store the BCD encoded numbers, and also * use the [A-F] range as valid characters for the ICCID. Explicitly * allow this range in the operator-specific part. */ if (len >= 6 && g_ascii_isxdigit (buf[len])) { /* canonicalize hex digit */ buf[len] = g_ascii_toupper (buf[len]); continue; } if (buf[len] == '\"') { buf[len] = 0; break; } /* Invalid character */ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "ICCID response contained invalid character '%c' at index '%zu'", buf[len], len); goto error; } /* ICCIDs are 19 or 20 digits long */ if (len < 19 || len > 20) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Invalid ICCID response size (was %zd, expected 19 or 20)", len); goto error; } /* The leading two digits of an ICCID is the major industry identifier and * should be '89' for telecommunication purposes according to ISO/IEC 7812. */ if (buf[0] == '8' && buf[1] == '9') { swap = FALSE; } else if (buf[0] == '9' && buf[1] == '8') { /* swapped digits are only expected in raw +CRSM responses, which must all * be 20-bytes long */ if (len != 20) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Invalid ICCID response size while swap needed (expected 20)"); goto error; } swap = TRUE; } else { /* FIXME: Instead of erroring out, revisit this solution if we find any SIM * that doesn't use '89' as the major industry identifier of the ICCID. */ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Invalid ICCID response (leading two digits are not 89)"); goto error; } if (swap) { /* Swap digits in the ICCID response to get the actual ICCID, each * group of 2 digits is reversed. * * 21436587 -> 12345678 */ g_assert (len == 20); swapped = g_malloc0 (21); for (i = 0; i < 10; i++) { swapped[i * 2] = buf[(i * 2) + 1]; swapped[(i * 2) + 1] = buf[i * 2]; } } else swapped = g_strdup (buf); /* Zero out the F for 19 digit ICCIDs */ if (swapped[len - 1] == 'F') swapped[len - 1] = 0; g_free (buf); return swapped; error: g_free (buf); g_free (swapped); return NULL; } /*************************************************************************/ gboolean mm_3gpp_parse_operator_id (const gchar *operator_id, guint16 *mcc, guint16 *mnc, gboolean *three_digit_mnc, GError **error) { guint len; guint i; gchar aux[4]; guint16 tmp; g_assert (operator_id != NULL); len = strlen (operator_id); if (len != 5 && len != 6) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Operator ID must have 5 or 6 digits"); return FALSE; } for (i = 0; i < len; i++) { if (!g_ascii_isdigit (operator_id[i])) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Operator ID must only contain digits"); return FALSE; } } memcpy (&aux[0], operator_id, 3); aux[3] = '\0'; tmp = atoi (aux); if (tmp == 0) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "MCC must not be zero"); return FALSE; } if (mcc) *mcc = tmp; if (mnc) { if (len == 5) { memcpy (&aux[0], &operator_id[3], 2); aux[2] = '\0'; } else memcpy (&aux[0], &operator_id[3], 3); *mnc = atoi (aux); } if (three_digit_mnc) *three_digit_mnc = len == 6; return TRUE; } /*************************************************************************/ /* Emergency numbers (+CRSM output) */ GStrv mm_3gpp_parse_emergency_numbers (const char *raw, GError **error) { gsize rawlen; guint8 *bin; gsize binlen; gsize max_items; GPtrArray *out; guint i; /* The emergency call code is of a variable length with a maximum length of * 6 digits. Each emergency call code is coded on three bytes, with each * digit within the code being coded on four bits. If a code of less that 6 * digits is chosen, then the unused nibbles shall be set to 'F'. */ rawlen = strlen (raw); if (!rawlen) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS, "empty emergency numbers list"); return NULL; } if (rawlen % 6 != 0) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS, "invalid raw emergency numbers list length: %" G_GSIZE_FORMAT, rawlen); return NULL; } bin = mm_utils_hexstr2bin (raw, -1, &binlen, error); if (!bin) { g_prefix_error (error, "invalid raw emergency numbers list contents: "); return NULL; } max_items = binlen / 3; out = g_ptr_array_sized_new (max_items + 1); for (i = 0; i < max_items; i++) { gchar *number; number = mm_bcd_to_string (&bin[i*3], 3, TRUE /* low_nybble_first */); if (number && number[0]) g_ptr_array_add (out, number); else g_free (number); } g_free (bin); if (!out->len) { g_ptr_array_unref (out); g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "uninitialized emergency numbers list"); return NULL; } g_ptr_array_add (out, NULL); return (GStrv) g_ptr_array_free (out, FALSE); } /*************************************************************************/ gboolean mm_cdma_parse_spservice_read_response (const gchar *reply, MMModemCdmaRegistrationState *out_cdma_1x_state, MMModemCdmaRegistrationState *out_evdo_state) { const gchar *p; g_return_val_if_fail (reply != NULL, FALSE); g_return_val_if_fail (out_cdma_1x_state != NULL, FALSE); g_return_val_if_fail (out_evdo_state != NULL, FALSE); p = mm_strip_tag (reply, "+SPSERVICE:"); if (!isdigit (*p)) return FALSE; switch (atoi (p)) { case 0: /* no service */ *out_cdma_1x_state = MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN; *out_evdo_state = MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN; break; case 1: /* 1xRTT */ *out_cdma_1x_state = MM_MODEM_CDMA_REGISTRATION_STATE_REGISTERED; *out_evdo_state = MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN; break; case 2: /* EVDO rev 0 */ case 3: /* EVDO rev A */ *out_cdma_1x_state = MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN; *out_evdo_state = MM_MODEM_CDMA_REGISTRATION_STATE_REGISTERED; break; default: return FALSE; } return TRUE; } /*************************************************************************/ typedef struct { gint num; gboolean roam_ind; const gchar *banner; } EriItem; /* NOTE: these may be Sprint-specific for now... */ static const EriItem eris[] = { { 0, TRUE, "Digital or Analog Roaming" }, { 1, FALSE, "Home" }, { 2, TRUE, "Digital or Analog Roaming" }, { 3, TRUE, "Out of neighborhood" }, { 4, TRUE, "Out of building" }, { 5, TRUE, "Preferred system" }, { 6, TRUE, "Available System" }, { 7, TRUE, "Alliance Partner" }, { 8, TRUE, "Premium Partner" }, { 9, TRUE, "Full Service Functionality" }, { 10, TRUE, "Partial Service Functionality" }, { 64, TRUE, "Preferred system" }, { 65, TRUE, "Available System" }, { 66, TRUE, "Alliance Partner" }, { 67, TRUE, "Premium Partner" }, { 68, TRUE, "Full Service Functionality" }, { 69, TRUE, "Partial Service Functionality" }, { 70, TRUE, "Analog A" }, { 71, TRUE, "Analog B" }, { 72, TRUE, "CDMA 800 A" }, { 73, TRUE, "CDMA 800 B" }, { 74, TRUE, "International Roaming" }, { 75, TRUE, "Extended Network" }, { 76, FALSE, "Campus" }, { 77, FALSE, "In Building" }, { 78, TRUE, "Regional" }, { 79, TRUE, "Community" }, { 80, TRUE, "Business" }, { 81, TRUE, "Zone 1" }, { 82, TRUE, "Zone 2" }, { 83, TRUE, "National" }, { 84, TRUE, "Local" }, { 85, TRUE, "City" }, { 86, TRUE, "Government" }, { 87, TRUE, "USA" }, { 88, TRUE, "State" }, { 89, TRUE, "Resort" }, { 90, TRUE, "Headquarters" }, { 91, TRUE, "Personal" }, { 92, FALSE, "Home" }, { 93, TRUE, "Residential" }, { 94, TRUE, "University" }, { 95, TRUE, "College" }, { 96, TRUE, "Hotel Guest" }, { 97, TRUE, "Rental" }, { 98, FALSE, "Corporate" }, { 99, FALSE, "Home Provider" }, { 100, FALSE, "Campus" }, { 101, FALSE, "In Building" }, { 102, TRUE, "Regional" }, { 103, TRUE, "Community" }, { 104, TRUE, "Business" }, { 105, TRUE, "Zone 1" }, { 106, TRUE, "Zone 2" }, { 107, TRUE, "National" }, { 108, TRUE, "Local" }, { 109, TRUE, "City" }, { 110, TRUE, "Government" }, { 111, TRUE, "USA" }, { 112, TRUE, "State" }, { 113, TRUE, "Resort" }, { 114, TRUE, "Headquarters" }, { 115, TRUE, "Personal" }, { 116, FALSE, "Home" }, { 117, TRUE, "Residential" }, { 118, TRUE, "University" }, { 119, TRUE, "College" }, { 120, TRUE, "Hotel Guest" }, { 121, TRUE, "Rental" }, { 122, FALSE, "Corporate" }, { 123, FALSE, "Home Provider" }, { 124, TRUE, "International" }, { 125, TRUE, "International" }, { 126, TRUE, "International" }, { 127, FALSE, "Premium Service" }, { 128, FALSE, "Enhanced Service" }, { 129, FALSE, "Enhanced Digital" }, { 130, FALSE, "Enhanced Roaming" }, { 131, FALSE, "Alliance Service" }, { 132, FALSE, "Alliance Network" }, { 133, FALSE, "Data Roaming" }, /* Sprint: Vision Roaming */ { 134, FALSE, "Extended Service" }, { 135, FALSE, "Expanded Services" }, { 136, FALSE, "Expanded Network" }, { 137, TRUE, "Premium Service" }, { 138, TRUE, "Enhanced Service" }, { 139, TRUE, "Enhanced Digital" }, { 140, TRUE, "Enhanced Roaming" }, { 141, TRUE, "Alliance Service" }, { 142, TRUE, "Alliance Network" }, { 143, TRUE, "Data Roaming" }, /* Sprint: Vision Roaming */ { 144, TRUE, "Extended Service" }, { 145, TRUE, "Expanded Services" }, { 146, TRUE, "Expanded Network" }, { 147, TRUE, "Premium Service" }, { 148, TRUE, "Enhanced Service" }, { 149, TRUE, "Enhanced Digital" }, { 150, TRUE, "Enhanced Roaming" }, { 151, TRUE, "Alliance Service" }, { 152, TRUE, "Alliance Network" }, { 153, TRUE, "Data Roaming" }, /* Sprint: Vision Roaming */ { 154, TRUE, "Extended Service" }, { 155, TRUE, "Expanded Services" }, { 156, TRUE, "Expanded Network" }, { 157, TRUE, "Premium International" }, { 158, TRUE, "Premium International" }, { 159, TRUE, "Premium International" }, { 160, TRUE, NULL }, { 161, TRUE, NULL }, { 162, FALSE, NULL }, { 163, FALSE, NULL }, { 164, FALSE, "Extended Voice/Data Network" }, { 165, FALSE, "Extended Voice/Data Network" }, { 166, TRUE, "Extended Voice/Data Network" }, { 167, FALSE, "Extended Broadband" }, { 168, FALSE, "Extended Broadband" }, { 169, TRUE, "Extended Broadband" }, { 170, FALSE, "Extended Data" }, { 171, FALSE, "Extended Data" }, { 172, TRUE, "Extended Data" }, { 173, FALSE, "Extended Data Network" }, { 174, FALSE, "Extended Data Network" }, { 175, TRUE, "Extended Data Network" }, { 176, FALSE, "Extended Network" }, { 177, FALSE, "Extended Network" }, { 178, TRUE, "Extended Network" }, { 179, FALSE, "Extended Service" }, { 180, TRUE, "Extended Service" }, { 181, FALSE, "Extended Voice" }, { 182, FALSE, "Extended Voice" }, { 183, TRUE, "Extended Voice" }, { 184, FALSE, "Extended Voice/Data" }, { 185, FALSE, "Extended Voice/Data" }, { 186, TRUE, "Extended Voice/Data" }, { 187, FALSE, "Extended Voice Network" }, { 188, FALSE, "Extended Voice Network" }, { 189, TRUE, "Extended Voice Network" }, { 190, FALSE, "Extended Voice/Data" }, { 191, FALSE, "Extended Voice/Data" }, { 192, TRUE, "Extended Voice/Data" }, { 193, TRUE, "International" }, { 194, FALSE, "International Services" }, { 195, FALSE, "International Voice" }, { 196, FALSE, "International Voice/Data" }, { 197, FALSE, "International Voice/Data" }, { 198, TRUE, "International Voice/Data" }, { 199, FALSE, "Extended Voice/Data Network" }, { 200, TRUE, "Extended Voice/Data Network" }, { 201, TRUE, "Extended Voice/Data Network" }, { 202, FALSE, "Extended Broadband" }, { 203, TRUE, "Extended Broadband" }, { 204, TRUE, "Extended Broadband" }, { 205, FALSE, "Extended Data" }, { 206, TRUE, "Extended Data" }, { 207, TRUE, "Extended Data" }, { 208, FALSE, "Extended Data Network" }, { 209, TRUE, "Extended Data Network" }, { 210, TRUE, "Extended Data Network" }, { 211, FALSE, "Extended Network" }, { 212, TRUE, "Extended Network" }, { 213, FALSE, "Extended Service" }, { 214, TRUE, "Extended Service" }, { 215, TRUE, "Extended Service" }, { 216, FALSE, "Extended Voice" }, { 217, TRUE, "Extended Voice" }, { 218, TRUE, "Extended Voice" }, { 219, FALSE, "Extended Voice/Data" }, { 220, TRUE, "Extended Voice/Data" }, { 221, TRUE, "Extended Voice/Data" }, { 222, FALSE, "Extended Voice Network" }, { 223, FALSE, "Extended Voice Network" }, { 224, TRUE, "Extended Voice Network" }, { 225, FALSE, "Extended Voice/Data" }, { 226, TRUE, "Extended Voice/Data" }, { 227, TRUE, "Extended Voice/Data" }, { 228, TRUE, "International" }, { 229, TRUE, "International" }, { 230, TRUE, "International Services" }, { 231, TRUE, "International Voice" }, { 232, FALSE, "International Voice/Data" }, { 233, TRUE, "International Voice/Data" }, { 234, TRUE, "International Voice/Data" }, { 235, TRUE, "Premium International" }, { 236, TRUE, NULL }, { 237, TRUE, NULL }, { 238, FALSE, NULL }, { 239, FALSE, NULL }, { -1, FALSE, NULL }, }; gboolean mm_cdma_parse_eri (const gchar *reply, gboolean *out_roaming, guint *out_ind, const gchar **out_desc) { guint ind; const EriItem *iter = &eris[0]; gboolean found = FALSE; g_return_val_if_fail (reply != NULL, FALSE); g_return_val_if_fail (out_roaming != NULL, FALSE); if (mm_get_uint_from_str (reply, &ind)) { if (out_ind) *out_ind = ind; while (iter->num != -1) { if ((guint)iter->num == ind) { *out_roaming = iter->roam_ind; if (out_desc) *out_desc = iter->banner; found = TRUE; break; } iter++; } } return found; } /*************************************************************************/ gboolean mm_cdma_parse_crm_test_response (const gchar *reply, MMModemCdmaRmProtocol *min, MMModemCdmaRmProtocol *max, GError **error) { g_autoptr(GRegex) r = NULL; g_autoptr(GMatchInfo) match_info = NULL; gboolean result = FALSE; GError *match_error = NULL; /* Expected reply format is: * ---> AT+CRM=? * <--- +CRM: (0-2) */ r = g_regex_new ("\\+CRM:\\s*\\((\\d+)-(\\d+)\\)", G_REGEX_DOLLAR_ENDONLY | G_REGEX_RAW, 0, error); g_assert (r != NULL); if (g_regex_match_full (r, reply, strlen (reply), 0, 0, &match_info, &match_error)) { gchar *aux; guint min_val = 0; guint max_val = 0; aux = g_match_info_fetch (match_info, 1); min_val = (guint) atoi (aux); g_free (aux); aux = g_match_info_fetch (match_info, 2); max_val = (guint) atoi (aux); g_free (aux); if (min_val == 0 || max_val == 0 || min_val >= max_val) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't parse CRM range: " "Unexpected range of RM protocols (%u,%u)", min_val, max_val); } else { *min = mm_cdma_get_rm_protocol_from_index (min_val, error); if (*min != MM_MODEM_CDMA_RM_PROTOCOL_UNKNOWN) { *max = mm_cdma_get_rm_protocol_from_index (max_val, error); if (*max != MM_MODEM_CDMA_RM_PROTOCOL_UNKNOWN) result = TRUE; } } } else if (match_error) { g_propagate_error (error, match_error); } else { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't parse CRM range: response didn't match (%s)", reply); } return result; } /*************************************************************************/ MMModemCdmaRmProtocol mm_cdma_get_rm_protocol_from_index (guint index, GError **error) { guint protocol; /* just adding 1 from the index value should give us the enum */ protocol = index + 1 ; if (protocol > MM_MODEM_CDMA_RM_PROTOCOL_STU_III) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Unexpected RM protocol index (%u)", index); protocol = MM_MODEM_CDMA_RM_PROTOCOL_UNKNOWN; } return (MMModemCdmaRmProtocol)protocol; } guint mm_cdma_get_index_from_rm_protocol (MMModemCdmaRmProtocol protocol, GError **error) { if (protocol == MM_MODEM_CDMA_RM_PROTOCOL_UNKNOWN) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Unexpected RM protocol (%s)", mm_modem_cdma_rm_protocol_get_string (protocol)); return 0; } /* just subtracting 1 from the enum value should give us the index */ return (protocol - 1); } /*************************************************************************/ gint mm_cdma_normalize_class (const gchar *orig_class) { gchar 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; } /*************************************************************************/ gchar mm_cdma_normalize_band (const gchar *long_band, gint *out_class) { gchar 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') { gchar tmp[2] = { band, '\0' }; *out_class = mm_cdma_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'; } /*************************************************************************/ /* Caller must strip any "+GSN:" or "+CGSN" from @gsn */ gboolean mm_parse_gsn (const char *gsn, gchar **out_imei, gchar **out_meid, gchar **out_esn) { gchar **items, **iter; gchar *meid = NULL, *esn = NULL, *imei = NULL, *p; gboolean success = FALSE; if (!gsn || !gsn[0]) return FALSE; /* IMEI is 15 numeric digits */ /* ESNs take one of two formats: * (1) 7 or 8 hexadecimal digits * (2) 10 or 11 decimal digits * * In addition, leading zeros may be present or absent, and hexadecimal * ESNs may or may not be prefixed with "0x". */ /* MEIDs take one of two formats: * (1) 14 hexadecimal digits, sometimes padded to 16 digits with leading zeros * (2) 18 decimal digits * * As with ESNs, leading zeros may be present or absent, and hexadecimal * MEIDs may or may not be prefixed with "0x". */ items = g_strsplit_set (gsn, "\r\n\t: ,", 0); for (iter = items; iter && *iter && (!esn || !meid); iter++) { gboolean expect_hex = FALSE, is_hex, is_digit; gchar *s = *iter; guint len = 0; if (!s[0]) continue; if (g_str_has_prefix (s, "0x") || g_str_has_prefix (s, "0X")) { expect_hex = TRUE; s += 2; /* Skip any leading zeros */ while (*s == '0') s++; } /* Check whether all digits are hex or decimal */ is_hex = is_digit = TRUE; p = s; while (*p && (is_hex || is_digit)) { if (!g_ascii_isxdigit (*p)) is_hex = FALSE; if (!g_ascii_isdigit (*p)) is_digit = FALSE; p++, len++; } /* Note that some hex strings are also valid digit strings */ if (is_hex) { if (len == 7 || len == 8) { /* ESN */ if (!esn) { if (len == 7) esn = g_strdup_printf ("0%s", s); else esn = g_strdup (s); } } else if (len == 14) { /* MEID */ if (!meid) meid = g_strdup (s); } } if (is_digit) { if (!is_hex) g_warn_if_fail (expect_hex == FALSE); if (len == 15) { if (!imei) imei = g_strdup (s); } /* Decimal ESN/MEID unhandled for now; conversion from decimal to * hex isn't a straight dec->hex conversion, as the first 2 digits * of the ESN and first 3 digits of the MEID are the manufacturer * identifier and must be converted separately from serial number * and then concatenated with it. */ } } g_strfreev (items); success = meid || esn || imei; if (out_imei) *out_imei = imei; else g_free (imei); if (out_meid) *out_meid = meid; else g_free (meid); if (out_esn) *out_esn = esn; else g_free (esn); return success; } /*****************************************************************************/ /* +CCLK response parser */ gboolean mm_parse_cclk_response (const char *response, gchar **iso8601p, MMNetworkTimezone **tzp, GError **error) { g_autoptr(GRegex) r = NULL; g_autoptr(GMatchInfo) match_info = NULL; GError *match_error = NULL; guint year = 0; guint month = 0; guint day = 0; guint hour = 0; guint minute = 0; guint second = 0; gint tz = 0; g_assert (iso8601p || tzp); /* at least one */ /* Sample replies: * +CCLK: "15/03/05,14:14:26-32" * +CCLK: 17/07/26,11:42:15+01 */ r = g_regex_new ("\\+CCLK:\\s*\"?(\\d+)/(\\d+)/(\\d+),(\\d+):(\\d+):(\\d+)([-+]\\d+)?\"?", 0, 0, NULL); g_assert (r != NULL); if (!g_regex_match_full (r, response, -1, 0, 0, &match_info, &match_error)) { if (match_error) { g_propagate_error (error, match_error); g_prefix_error (error, "Could not parse +CCLK results: "); } else { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't match +CCLK reply: %s", response); } return FALSE; } /* Remember that g_match_info_get_match_count() includes match #0 */ g_assert (g_match_info_get_match_count (match_info) >= 7); /* Parse mandatory date and time fields */ if (!mm_get_uint_from_match_info (match_info, 1, &year) || !mm_get_uint_from_match_info (match_info, 2, &month) || !mm_get_uint_from_match_info (match_info, 3, &day) || !mm_get_uint_from_match_info (match_info, 4, &hour) || !mm_get_uint_from_match_info (match_info, 5, &minute) || !mm_get_uint_from_match_info (match_info, 6, &second)) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Failed to parse +CCLK reply: %s", response); return FALSE; } /* Read optional time zone offset; if not given assume UTC (tz = 0). * Note that timezone offset is given in 15 minute intervals. */ if ((g_match_info_get_match_count (match_info) >= 8) && (!mm_get_int_from_match_info (match_info, 7, &tz))) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Failed to parse timezone in +CCLK reply: %s", response); return FALSE; } /* Adjust year to support YYYY format, as per +CSDF in 3GPP TS 27.007. Also, * don't assume the reported date is actually the current real one, as some * devices report an initial date of e.g. January 1st 1980. */ if (year < 100) { if (year >= 70) year += 1900; else year += 2000; } if (tzp) { *tzp = mm_network_timezone_new (); mm_network_timezone_set_offset (*tzp, tz * 15); } if (iso8601p) { /* Return ISO-8601 format date/time string */ *iso8601p = mm_new_iso8601_time (year, month, day, hour, minute, second, TRUE, (tz * 15), error); return (*iso8601p != NULL); } return TRUE; } /*****************************************************************************/ /* +CSIM response parser */ #define MM_MIN_SIM_RETRY_HEX 0x63C0 #define MM_MAX_SIM_RETRY_HEX 0x63CF gint mm_parse_csim_response (const gchar *response, GError **error) { g_autoptr(GRegex) r = NULL; g_autoptr(GMatchInfo) match_info = NULL; g_autofree gchar *str_code = NULL; gint retries = -1; guint hex_code; GError *inner_error = NULL; r = g_regex_new ("\\+CSIM:\\s*[0-9]+,\\s*\".*([0-9a-fA-F]{4})\"", G_REGEX_RAW, 0, NULL); g_regex_match (r, response, 0, &match_info); if (!g_match_info_matches (match_info)) { inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Could not recognize +CSIM response '%s'", response); goto out; } str_code = mm_get_string_unquoted_from_match_info (match_info, 1); if (str_code == NULL) { inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Could not find expected string code in response '%s'", response); goto out; } if (!mm_get_uint_from_hex_str (str_code, &hex_code)) { inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Could not recognize expected hex code in response '%s'", response); goto out; } switch (hex_code) { case 0x6300: inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "SIM verification failed"); goto out; case 0x6983: inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "SIM authentication method blocked"); goto out; case 0x6984: inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "SIM reference data invalidated"); goto out; case 0x6A86: inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Incorrect parameters in SIM request"); goto out; case 0x6A88: inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "SIM reference data not found"); goto out; default: break; } if (hex_code < MM_MIN_SIM_RETRY_HEX || hex_code > MM_MAX_SIM_RETRY_HEX) { inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Unknown error returned '0x%04x'", hex_code); goto out; } retries = (gint)(hex_code - MM_MIN_SIM_RETRY_HEX); out: if (inner_error) { g_propagate_error (error, inner_error); return -1; } g_assert (retries >= 0); return retries; } /*****************************************************************************/ gboolean mm_parse_supl_address (const gchar *supl, gchar **out_fqdn, guint32 *out_ip, guint16 *out_port, GError **error) { gboolean valid = FALSE; gchar **split; guint port; guint32 ip; split = g_strsplit (supl, ":", -1); if (g_strv_length (split) != 2) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS, "Invalid SUPL address format: expected FQDN:PORT or IP:PORT"); goto out; } if (!mm_get_uint_from_str (split[1], &port)) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS, "Invalid SUPL port number specified: not a number: %s", split[1]); goto out; } if (port == 0 || port > G_MAXUINT16) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS, "Invalid SUPL port number specified: out of range: %u", port); goto out; } /* Port is valid */ if (out_port) *out_port = (guint16) port; /* Try to parse first item as IP */ if (inet_pton (AF_INET, split[0], &ip) <= 0) { /* Otherwise, assume it's a domain name */ if (out_fqdn) *out_fqdn = g_strdup (split[0]); if (out_ip) *out_ip = 0; } else { if (out_ip) *out_ip = ip; if (out_fqdn) *out_fqdn = NULL; } valid = TRUE; out: g_strfreev (split); return valid; } /*****************************************************************************/ gboolean mm_sim_parse_cpol_query_response (const gchar *response, guint *out_index, gchar **out_operator_code, gboolean *out_gsm_act, gboolean *out_gsm_compact_act, gboolean *out_utran_act, gboolean *out_eutran_act, gboolean *out_ngran_act, guint *out_act_count, GError **error) { g_autoptr(GMatchInfo) match_info = NULL; g_autoptr(GRegex) r = NULL; g_autofree gchar *operator_code = NULL; guint format = 0; guint act = 0; guint match_count; r = g_regex_new ("\\+CPOL:\\s*(\\d+),\\s*(\\d+),\\s*\"?(\\d+)\"?" "(?:,\\s*(\\d+))?" /* GSM_AcTn */ "(?:,\\s*(\\d+))?" /* GSM_Compact_AcTn */ "(?:,\\s*(\\d+))?" /* UTRAN_AcTn */ "(?:,\\s*(\\d+))?" /* E-UTRAN_AcTn */ "(?:,\\s*(\\d+))?", /* NG-RAN_AcTn */ G_REGEX_RAW, 0, NULL); g_regex_match (r, response, 0, &match_info); if (!g_match_info_matches (match_info)) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't parse +CPOL reply: %s", response); return FALSE; } match_count = g_match_info_get_match_count (match_info); /* Remember that g_match_info_get_match_count() includes match #0 */ g_assert (match_count >= 4); if (!mm_get_uint_from_match_info (match_info, 2, &format) || !(operator_code = mm_get_string_unquoted_from_match_info (match_info, 3))) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't parse +CPOL reply parameters: %s", response); return FALSE; } if (format != 2) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "+CPOL reply not using numeric operator code: %s", response); return FALSE; } if (out_index) if (!mm_get_uint_from_match_info (match_info, 1, out_index)) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't parse +CPOL index: %s", response); return FALSE; } if (out_operator_code) *out_operator_code = g_steal_pointer (&operator_code); if (out_gsm_act) *out_gsm_act = match_count >= 5 && mm_get_uint_from_match_info (match_info, 4, &act) && act != 0; if (out_gsm_compact_act) *out_gsm_compact_act = match_count >= 6 && mm_get_uint_from_match_info (match_info, 5, &act) && act != 0; if (out_utran_act) *out_utran_act = match_count >= 7 && mm_get_uint_from_match_info (match_info, 6, &act) && act != 0; if (out_eutran_act) *out_eutran_act = match_count >= 8 && mm_get_uint_from_match_info (match_info, 7, &act) && act != 0; if (out_ngran_act) *out_ngran_act = match_count >= 9 && mm_get_uint_from_match_info (match_info, 8, &act) && act != 0; /* number of access technologies (0...5) in modem response */ if (out_act_count) *out_act_count = match_count - 4; return TRUE; } gboolean mm_sim_parse_cpol_test_response (const gchar *response, guint *out_min_index, guint *out_max_index, GError **error) { g_autoptr(GMatchInfo) match_info = NULL; g_autoptr(GRegex) r = NULL; guint match_count; guint min_index; guint max_index; r = g_regex_new ("\\+CPOL:\\s*\\((\\d+)\\s*-\\s*(\\d+)\\)", G_REGEX_RAW, 0, NULL); g_regex_match (r, response, 0, &match_info); if (!g_match_info_matches (match_info)) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't parse +CPOL=? reply: %s", response); return FALSE; } match_count = g_match_info_get_match_count (match_info); /* Remember that g_match_info_get_match_count() includes match #0 */ g_assert (match_count >= 3); if (!mm_get_uint_from_match_info (match_info, 1, &min_index) || !mm_get_uint_from_match_info (match_info, 2, &max_index)) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't parse indices in +CPOL=? reply: %s", response); return FALSE; } if (out_min_index) *out_min_index = min_index; if (out_max_index) *out_max_index = max_index; return TRUE; } #define EID_BYTE_LENGTH 16 gchar * mm_decode_eid (const gchar *eid, gsize eid_len) { if (eid_len != EID_BYTE_LENGTH) return NULL; return mm_bcd_to_string ((const guint8 *) eid, eid_len, FALSE /* low_nybble_first */); } ModemManager-1.23.4-dev/src/mm-modem-helpers.h000066400000000000000000000665311456466623000210500ustar00rootroot00000000000000/* -*- 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 - 2012 Red Hat, Inc. * Copyright (C) 2012 Google, Inc. */ #ifndef MM_MODEM_HELPERS_H #define MM_MODEM_HELPERS_H #include #define _LIBMM_INSIDE_MM #include #include "glib-object.h" #include "mm-charsets.h" /* NOTE: * We will use the following nomenclature for the different AT commands referred * - AT+SOMETHING --> "Exec" command * - AT+SOMETHING? --> "Read" command * - AT+SOMETHING=X,X --> "Write" command * - AT+SOMETHING=? --> "Test" command */ /*****************************************************************************/ /* Common utilities */ /*****************************************************************************/ #define MM_MODEM_CAPABILITY_3GPP \ (MM_MODEM_CAPABILITY_GSM_UMTS | \ MM_MODEM_CAPABILITY_LTE | \ MM_MODEM_CAPABILITY_5GNR) gchar *mm_strip_quotes (gchar *str); const gchar *mm_strip_tag (const gchar *str, const gchar *cmd); gchar **mm_split_string_groups (const gchar *str); GArray *mm_parse_uint_list (const gchar *str, GError **error); guint mm_count_bits_set (gulong number); guint mm_find_bit_set (gulong number); gchar *mm_create_device_identifier (guint vid, guint pid, gpointer log_object, const gchar *ati, const gchar *ati1, const gchar *gsn, const gchar *revision, const gchar *model, const gchar *manf); guint mm_netmask_to_cidr (const gchar *netmask); GArray *mm_filter_current_bands (const GArray *supported_bands, const GArray *current_bands); GArray *mm_filter_supported_modes (const GArray *all, const GArray *supported_combinations, gpointer log_object); gchar *mm_bcd_to_string (const guint8 *bcd, gsize bcd_len, gboolean low_nybble_first); /*****************************************************************************/ /* VOICE specific helpers and utilities */ /*****************************************************************************/ GRegex *mm_voice_ring_regex_get (void); GRegex *mm_voice_cring_regex_get (void); GRegex *mm_voice_clip_regex_get (void); GRegex *mm_voice_ccwa_regex_get (void); /* +CLCC response parser */ typedef struct { guint index; MMCallDirection direction; MMCallState state; gchar *number; /* optional */ } MMCallInfo; gboolean mm_3gpp_parse_clcc_response (const gchar *str, gpointer log_object, GList **out_list, GError **error); void mm_3gpp_call_info_list_free (GList *call_info_list); /*****************************************************************************/ /* SERIAL specific helpers and utilities */ /* AT+IFC=? response parser. * For simplicity, we'll only consider flow control methods available in both * TE and TA. */ typedef enum { /*< underscore_name=mm_flow_control >*/ MM_FLOW_CONTROL_UNKNOWN = 0, MM_FLOW_CONTROL_NONE = 1 << 0, /* IFC=0,0 */ MM_FLOW_CONTROL_XON_XOFF = 1 << 1, /* IFC=1,1 */ MM_FLOW_CONTROL_RTS_CTS = 1 << 2, /* IFC=2,2 */ } MMFlowControl; MMFlowControl mm_parse_ifc_test_response (const gchar *response, gpointer log_object, GError **error); MMFlowControl mm_flow_control_from_string (const gchar *str, GError **error); /*****************************************************************************/ /* 3GPP specific helpers and utilities */ /*****************************************************************************/ gboolean mm_modem_3gpp_registration_state_is_registered (MMModem3gppRegistrationState state); /* Common Regex getters */ GPtrArray *mm_3gpp_creg_regex_get (gboolean solicited); void mm_3gpp_creg_regex_destroy (GPtrArray *array); GRegex *mm_3gpp_ciev_regex_get (void); GRegex *mm_3gpp_cgev_regex_get (void); GRegex *mm_3gpp_cusd_regex_get (void); GRegex *mm_3gpp_cmti_regex_get (void); GRegex *mm_3gpp_cds_regex_get (void); /* AT+WS46=? response parser: returns array of MMModemMode values */ GArray *mm_3gpp_parse_ws46_test_response (const gchar *response, gpointer log_object, GError **error); /* AT+COPS=? (network scan) response parser */ typedef struct { MMModem3gppNetworkAvailability status; gchar *operator_long; gchar *operator_short; gchar *operator_code; /* mandatory */ MMModemAccessTechnology access_tech; } MM3gppNetworkInfo; void mm_3gpp_network_info_list_free (GList *info_list); GList *mm_3gpp_parse_cops_test_response (const gchar *reply, MMModemCharset cur_charset, gpointer log_object, GError **error); /* AT+COPS? (current operator) response parser */ gboolean mm_3gpp_parse_cops_read_response (const gchar *response, guint *out_mode, guint *out_format, gchar **out_operator, MMModemAccessTechnology *out_act, gpointer log_object, GError **error); /* Logic to compare two APN names */ gboolean mm_3gpp_cmp_apn_name (const gchar *requested, const gchar *existing); /* AT+CGDCONT=? (PDP context format) test parser */ typedef struct { guint min_cid; guint max_cid; MMBearerIpFamily pdp_type; } MM3gppPdpContextFormat; void mm_3gpp_pdp_context_format_list_free (GList *pdp_format_list); GList *mm_3gpp_parse_cgdcont_test_response (const gchar *reply, gpointer log_object, GError **error); gboolean mm_3gpp_pdp_context_format_list_find_range (GList *pdp_format_list, MMBearerIpFamily ip_family, guint *out_min_cid, guint *out_max_cid); /* AT+CGDCONT? (PDP context query) response parser */ typedef struct { guint cid; MMBearerIpFamily pdp_type; gchar *apn; } MM3gppPdpContext; void mm_3gpp_pdp_context_list_free (GList *pdp_list); GList *mm_3gpp_parse_cgdcont_read_response (const gchar *reply, GError **error); /* AT+CGACT? (active PDP context query) response parser */ typedef struct { guint cid; gboolean active; } MM3gppPdpContextActive; void mm_3gpp_pdp_context_active_list_free (GList *pdp_active_list); gint mm_3gpp_pdp_context_active_cmp (MM3gppPdpContextActive *a, MM3gppPdpContextActive *b); GList *mm_3gpp_parse_cgact_read_response (const gchar *reply, GError **error); /* CREG/CGREG response/unsolicited message parser */ gboolean mm_3gpp_parse_creg_response (GMatchInfo *info, gpointer log_object, MMModem3gppRegistrationState *out_reg_state, gulong *out_lac, gulong *out_ci, MMModemAccessTechnology *out_act, gboolean *out_cgreg, gboolean *out_cereg, gboolean *out_c5greg, GError **error); /* AT+CMGF=? (SMS message format) response parser */ gboolean mm_3gpp_parse_cmgf_test_response (const gchar *reply, gboolean *sms_pdu_supported, gboolean *sms_text_supported, GError **error); /* AT+CPMS=? (Preferred SMS storage) response parser */ gboolean mm_3gpp_parse_cpms_test_response (const gchar *reply, GArray **mem1, GArray **mem2, GArray **mem3, GError **error); /* AT+CPMS? (Current SMS storage) response parser */ gboolean mm_3gpp_parse_cpms_query_response (const gchar *reply, MMSmsStorage *mem1, MMSmsStorage *mem2, GError** error); gboolean mm_3gpp_get_cpms_storage_match (GMatchInfo *match_info, const gchar *match_name, MMSmsStorage *storage, GError **error); /* AT+CSCS=? (Supported charsets) response parser */ gboolean mm_3gpp_parse_cscs_test_response (const gchar *reply, MMModemCharset *out_charsets); /* AT+CLCK=? (Supported locks) response parser */ gboolean mm_3gpp_parse_clck_test_response (const gchar *reply, MMModem3gppFacility *out_facilities); /* AT+CLCK=X,X,X... (Current locks) response parser */ gboolean mm_3gpp_parse_clck_write_response (const gchar *reply, gboolean *enabled); /* AT+CNUM (Own numbers) response parser */ GStrv mm_3gpp_parse_cnum_exec_response (const gchar *reply); /* AT+CMER=? (Mobile Equipment Event Reporting) response parser */ typedef enum { /*< underscore_name=mm_3gpp_cmer_mode >*/ MM_3GPP_CMER_MODE_NONE = 0, MM_3GPP_CMER_MODE_DISCARD_URCS = 1 << 0, MM_3GPP_CMER_MODE_DISCARD_URCS_IF_LINK_RESERVED = 1 << 1, MM_3GPP_CMER_MODE_BUFFER_URCS_IF_LINK_RESERVED = 1 << 2, MM_3GPP_CMER_MODE_FORWARD_URCS = 1 << 3, } MM3gppCmerMode; typedef enum { /*< underscore_name=mm_3gpp_cmer_ind >*/ MM_3GPP_CMER_IND_NONE = 0, /* no indicator event reporting */ MM_3GPP_CMER_IND_DISABLE = 1 << 0, /* Only indicator events that are not caused by +CIND */ MM_3GPP_CMER_IND_ENABLE_NOT_CAUSED_BY_CIND = 1 << 1, /* All indicator events */ MM_3GPP_CMER_IND_ENABLE_ALL = 1 << 2, } MM3gppCmerInd; gchar *mm_3gpp_build_cmer_set_request (MM3gppCmerMode mode, MM3gppCmerInd ind); gboolean mm_3gpp_parse_cmer_test_response (const gchar *reply, gpointer log_object, MM3gppCmerMode *supported_modes, MM3gppCmerInd *supported_inds, GError **error); /* AT+CIND=? (Supported indicators) response parser */ typedef struct MM3gppCindResponse MM3gppCindResponse; GHashTable *mm_3gpp_parse_cind_test_response (const gchar *reply, GError **error); const gchar *mm_3gpp_cind_response_get_desc (MM3gppCindResponse *r); guint mm_3gpp_cind_response_get_index (MM3gppCindResponse *r); gint mm_3gpp_cind_response_get_min (MM3gppCindResponse *r); gint mm_3gpp_cind_response_get_max (MM3gppCindResponse *r); /* AT+CIND? (Current indicators) response parser */ GByteArray *mm_3gpp_parse_cind_read_response (const gchar *reply, GError **error); /* +CGEV indication parser */ typedef enum { MM_3GPP_CGEV_UNKNOWN, MM_3GPP_CGEV_NW_DETACH, MM_3GPP_CGEV_ME_DETACH, MM_3GPP_CGEV_NW_CLASS, MM_3GPP_CGEV_ME_CLASS, MM_3GPP_CGEV_NW_ACT_PRIMARY, MM_3GPP_CGEV_ME_ACT_PRIMARY, MM_3GPP_CGEV_NW_ACT_SECONDARY, MM_3GPP_CGEV_ME_ACT_SECONDARY, MM_3GPP_CGEV_NW_DEACT_PRIMARY, MM_3GPP_CGEV_ME_DEACT_PRIMARY, MM_3GPP_CGEV_NW_DEACT_SECONDARY, MM_3GPP_CGEV_ME_DEACT_SECONDARY, MM_3GPP_CGEV_NW_DEACT_PDP, MM_3GPP_CGEV_ME_DEACT_PDP, MM_3GPP_CGEV_NW_MODIFY, MM_3GPP_CGEV_ME_MODIFY, MM_3GPP_CGEV_REJECT, MM_3GPP_CGEV_NW_REACT, } MM3gppCgev; MM3gppCgev mm_3gpp_parse_cgev_indication_action (const gchar *str); gboolean mm_3gpp_parse_cgev_indication_pdp (const gchar *str, MM3gppCgev type, gchar **out_pdp_type, gchar **out_pdp_addr, guint *out_cid, GError **error); gboolean mm_3gpp_parse_cgev_indication_primary (const gchar *str, MM3gppCgev type, guint *out_cid, GError **error); gboolean mm_3gpp_parse_cgev_indication_secondary (const gchar *str, MM3gppCgev type, guint *out_p_cid, guint *out_cid, guint *out_event_type, GError **error); /* AT+CMGL=4 (list sms parts) response parser */ typedef struct { gint index; gint status; gchar *pdu; } MM3gppPduInfo; void mm_3gpp_pdu_info_free (MM3gppPduInfo *info); void mm_3gpp_pdu_info_list_free (GList *info_list); GList *mm_3gpp_parse_pdu_cmgl_response (const gchar *str, GError **error); /* AT+CMGR (Read message) response parser */ MM3gppPduInfo *mm_3gpp_parse_cmgr_read_response (const gchar *reply, guint index, GError **error); /* AT+CRSM response parser */ gboolean mm_3gpp_parse_crsm_response (const gchar *reply, guint *sw1, guint *sw2, gchar **hex, GError **error); /* AT+CGCONTRDP=N response parser */ gboolean mm_3gpp_parse_cgcontrdp_response (const gchar *response, guint *out_cid, guint *out_bearer_id, gchar **out_apn, gchar **out_local_address, gchar **out_subnet, gchar **out_gateway_address, gchar **out_dns_primary_address, gchar **out_dns_secondary_address, GError **error); /* CFUN? response parser * Note: a custom method with values not translated into MMModemPowerState is * provided, because they may be vendor specific. */ gboolean mm_3gpp_parse_cfun_query_response (const gchar *response, guint *out_state, GError **error); gboolean mm_3gpp_parse_cfun_query_generic_response (const gchar *response, MMModemPowerState *out_state, GError **error); /* +CESQ response parser */ gboolean mm_3gpp_parse_cesq_response (const gchar *response, guint *out_rxlev, guint *out_ber, guint *out_rscp, guint *out_ecn0, guint *out_rsrq, guint *out_rsrp, GError **error); gboolean mm_3gpp_cesq_response_to_signal_info (const gchar *response, gpointer log_object, MMSignal **out_gsm, MMSignal **out_umts, MMSignal **out_lte, GError **error); /* CEMODE? response parser */ gchar *mm_3gpp_build_cemode_set_request (MMModem3gppEpsUeModeOperation mode); gboolean mm_3gpp_parse_cemode_query_response (const gchar *response, MMModem3gppEpsUeModeOperation *out_mode, GError **error); /* CCWA service query response parser */ gboolean mm_3gpp_parse_ccwa_service_query_response (const gchar *response, gpointer log_object, gboolean *status, GError **error); /* CGATT helpers */ gchar *mm_3gpp_build_cgatt_set_request (MMModem3gppPacketServiceState state); /* Additional 3GPP-specific helpers */ MMModem3gppFacility mm_3gpp_acronym_to_facility (const gchar *str); const gchar *mm_3gpp_facility_to_acronym (MMModem3gppFacility facility); MMModemAccessTechnology mm_string_to_access_tech (const gchar *string); void mm_3gpp_normalize_operator (gchar **operator, MMModemCharset cur_charset, gpointer log_object); gboolean mm_3gpp_parse_operator_id (const gchar *operator_id, guint16 *mcc, guint16 *mnc, gboolean *three_digit_mnc, GError **error); const gchar *mm_3gpp_get_pdp_type_from_ip_family (MMBearerIpFamily family); MMBearerIpFamily mm_3gpp_get_ip_family_from_pdp_type (const gchar *pdp_type); gboolean mm_3gpp_normalize_ip_family (MMBearerIpFamily *family, gboolean from_user); char *mm_3gpp_parse_iccid (const char *raw_iccid, GError **error); gboolean mm_3gpp_rscp_level_to_rscp (guint rscp_level, gpointer log_object, gdouble *out_rscp); gboolean mm_3gpp_rxlev_to_rssi (guint rxlev, gpointer log_object, gdouble *out_rssi); gboolean mm_3gpp_ecn0_level_to_ecio (guint ecn0_level, gpointer log_object, gdouble *out_ecio); gboolean mm_3gpp_rsrq_level_to_rsrq (guint rsrq_level, gpointer log_object, gdouble *out_rsrq); gboolean mm_3gpp_rsrp_level_to_rsrp (guint rsrp_level, gpointer log_object, gdouble *out_rsrp); GStrv mm_3gpp_parse_emergency_numbers (const char *raw, GError **error); /* PDP context -> profile */ MM3gppProfile *mm_3gpp_profile_new_from_pdp_context (MM3gppPdpContext *pdp_context); /* Profile list operations */ GList *mm_3gpp_profile_list_new_from_pdp_context_list (GList *pdp_context_list); void mm_3gpp_profile_list_free (GList *profile_list); gint mm_3gpp_profile_list_find_empty (GList *profile_list, gint min_profile_id, gint max_profile_id, GError **error); gint mm_3gpp_profile_list_find_best (GList *profile_list, MM3gppProfile *requested, GEqualFunc cmp_apn, MM3gppProfileCmpFlags cmp_flags, gint min_profile_id, gint max_profile_id, gpointer log_object, MM3gppProfile **out_reused, gboolean *out_overwritten); MM3gppProfile *mm_3gpp_profile_list_find_by_profile_id (GList *profile_list, gint profile_id, GError **error); MM3gppProfile *mm_3gpp_profile_list_find_by_apn_type (GList *profile_list, MMBearerApnType apn_type, GError **error); /*****************************************************************************/ /* CDMA specific helpers and utilities */ /*****************************************************************************/ /* AT+SPSERVICE? response parser */ gboolean mm_cdma_parse_spservice_read_response (const gchar *reply, MMModemCdmaRegistrationState *out_cdma_1x_state, MMModemCdmaRegistrationState *out_evdo_state); /* Generic ERI response parser */ gboolean mm_cdma_parse_eri (const gchar *reply, gboolean *out_roaming, guint32 *out_ind, const gchar **out_desc); /* AT+CRM=? response parser */ gboolean mm_cdma_parse_crm_test_response (const gchar *reply, MMModemCdmaRmProtocol *min, MMModemCdmaRmProtocol *max, GError **error); /* Additional CDMA-specific helpers */ #define MM_MODEM_CDMA_SID_UNKNOWN 99999 #define MM_MODEM_CDMA_NID_UNKNOWN 99999 MMModemCdmaRmProtocol mm_cdma_get_rm_protocol_from_index (guint index, GError **error); guint mm_cdma_get_index_from_rm_protocol (MMModemCdmaRmProtocol protocol, GError **error); gint mm_cdma_normalize_class (const gchar *orig_class); gchar mm_cdma_normalize_band (const gchar *long_band, gint *out_class); gboolean mm_parse_gsn (const char *gsn, gchar **out_imei, gchar **out_meid, gchar **out_esn); /* +CCLK response parser */ gboolean mm_parse_cclk_response (const gchar *response, gchar **iso8601p, MMNetworkTimezone **tzp, GError **error); /* +CSIM response parser */ gint mm_parse_csim_response (const gchar *response, GError **error); gboolean mm_parse_supl_address (const gchar *supl, gchar **out_fqdn, guint32 *out_ip, guint16 *out_port, GError **error); /*****************************************************************************/ /* SIM specific helpers and utilities */ /*****************************************************************************/ /* +CPOL? response parser (for a single entry) - accepts only numeric operator format*/ gboolean mm_sim_parse_cpol_query_response (const gchar *response, guint *out_index, gchar **out_operator_code, gboolean *out_gsm_act, gboolean *out_gsm_compact_act, gboolean *out_utran_act, gboolean *out_eutran_act, gboolean *out_ngran_act, guint *out_act_count, GError **error); /* +CPOL=? response parser for getting supported min and max index */ gboolean mm_sim_parse_cpol_test_response (const gchar *response, guint *out_min_index, guint *out_max_index, GError **error); /*****************************************************************************/ /* Useful when clamp-ing an unsigned integer with implicit low limit set to 0, * and in order to avoid -Wtype-limits warnings. */ #define MM_CLAMP_HIGH(x, high) (((x) > (high)) ? (high) : (x)) /*****************************************************************************/ /* Signal quality percentage from different sources */ /* Limit the value betweeen [-113,-51] and scale it to a percentage */ #define MM_RSSI_TO_QUALITY(rssi) \ (guint8)(100 - ((CLAMP (rssi, -113, -51) + 51) * 100 / (-113 + 51))) /* Limit the value betweeen [-110,-60] and scale it to a percentage */ #define MM_RSRP_TO_QUALITY(rsrp) \ (guint8)(100 - ((CLAMP (rsrp, -110, -60) + 60) * 100 / (-110 + 60))) /*****************************************************************************/ /* Helper function to decode eid read from esim */ gchar *mm_decode_eid (const gchar *eid, gsize eid_len); #endif /* MM_MODEM_HELPERS_H */ ModemManager-1.23.4-dev/src/mm-netlink.c000066400000000000000000000316211456466623000177360ustar00rootroot00000000000000/* -*- 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: * * Basic netlink support based on the QmiNetPortManagerRmnet from libqmi: * Copyright (C) 2020 Eric Caruso * Copyright (C) 2020 Andrew Lassalle * * Copyright (C) 2021 Aleksander Morgado */ #include #include #include #include #include #include #include #include #include "mm-log-object.h" #include "mm-utils.h" #include "mm-netlink.h" struct _MMNetlink { GObject parent; /* Netlink socket */ GSocket *socket; GSource *source; /* Netlink state */ guint current_sequence_id; GHashTable *transactions; }; struct _MMNetlinkClass { GObjectClass parent_class; }; static void log_object_iface_init (MMLogObjectInterface *iface); G_DEFINE_TYPE_EXTENDED (MMNetlink, mm_netlink, G_TYPE_OBJECT, 0, G_IMPLEMENT_INTERFACE (MM_TYPE_LOG_OBJECT, log_object_iface_init)) /*****************************************************************************/ /* * Netlink message construction functions */ typedef GByteArray NetlinkMessage; typedef struct { struct nlmsghdr msghdr; struct ifinfomsg ifreq; } NetlinkHeader; static NetlinkHeader * netlink_message_header (NetlinkMessage *msg) { return (NetlinkHeader *) (msg->data); } static guint get_pos_of_next_attr (NetlinkMessage *msg) { return NLMSG_ALIGN (msg->len); } static void append_netlink_attribute (NetlinkMessage *msg, gushort type, gconstpointer value, gushort len) { guint attr_len; guint old_len; guint next_attr_rel_pos; char *next_attr_abs_pos; struct rtattr new_attr; /* Expand the buffer to hold the new attribute */ attr_len = RTA_ALIGN (RTA_LENGTH (len)); old_len = msg->len; next_attr_rel_pos = get_pos_of_next_attr (msg); g_byte_array_set_size (msg, next_attr_rel_pos + attr_len); /* fill new bytes with zero, since some padding is added between attributes. */ memset ((char *) msg->data + old_len, 0, msg->len - old_len); new_attr.rta_type = type; new_attr.rta_len = RTA_LENGTH (len); next_attr_abs_pos = (char *) msg->data + next_attr_rel_pos; memcpy (next_attr_abs_pos, &new_attr, sizeof (struct rtattr)); if (value) memcpy (RTA_DATA (next_attr_abs_pos), value, len); /* Update the total netlink message length */ netlink_message_header (msg)->msghdr.nlmsg_len = msg->len; } static void append_netlink_attribute_uint32 (NetlinkMessage *msg, gushort type, guint32 value) { append_netlink_attribute (msg, type, &value, sizeof (value)); } static NetlinkMessage * netlink_message_new (guint ifindex, guint16 type) { NetlinkMessage *msg; NetlinkHeader *hdr; int size = sizeof (NetlinkHeader); msg = g_byte_array_new (); g_byte_array_set_size (msg, size); memset ((char *) msg->data, 0, size); hdr = netlink_message_header (msg); hdr->msghdr.nlmsg_len = msg->len; hdr->msghdr.nlmsg_type = type; hdr->msghdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; hdr->ifreq.ifi_family = AF_UNSPEC; hdr->ifreq.ifi_index = ifindex; return msg; } static NetlinkMessage * netlink_message_new_setlink (guint ifindex, gboolean up, guint mtu) { NetlinkMessage *msg; NetlinkHeader *hdr; msg = netlink_message_new (ifindex, RTM_SETLINK); hdr = netlink_message_header (msg); hdr->ifreq.ifi_flags = up ? IFF_UP : 0; hdr->ifreq.ifi_change = IFF_UP; if (mtu) append_netlink_attribute_uint32 (msg, IFLA_MTU, mtu); return msg; } static void netlink_message_free (NetlinkMessage *msg) { g_byte_array_unref (msg); } /*****************************************************************************/ /* Netlink transactions */ typedef struct { MMNetlink *self; guint32 sequence_id; GSource *timeout_source; GTask *completion_task; } Transaction; static gboolean transaction_timed_out (Transaction *tr) { GTask *task; guint32 sequence_id; task = g_steal_pointer (&tr->completion_task); sequence_id = tr->sequence_id; g_hash_table_remove (tr->self->transactions, GUINT_TO_POINTER (tr->sequence_id)); g_task_return_new_error (task, G_IO_ERROR, G_IO_ERROR_TIMED_OUT, "Netlink message with sequence ID %u timed out", sequence_id); g_object_unref (task); return G_SOURCE_REMOVE; } static void transaction_complete_with_error (Transaction *tr, GError *error) { GTask *task; task = g_steal_pointer (&tr->completion_task); g_hash_table_remove (tr->self->transactions, GUINT_TO_POINTER (tr->sequence_id)); g_task_return_error (task, error); g_object_unref (task); } static void transaction_complete (Transaction *tr, gint saved_errno) { GTask *task; guint32 sequence_id; task = g_steal_pointer (&tr->completion_task); sequence_id = tr->sequence_id; g_hash_table_remove (tr->self->transactions, GUINT_TO_POINTER (tr->sequence_id)); if (!saved_errno) { g_task_return_boolean (task, TRUE); } else { g_task_return_new_error (task, G_IO_ERROR, g_io_error_from_errno (saved_errno), "Netlink message with transaction %u failed", sequence_id); } g_object_unref (task); } static void transaction_free (Transaction *tr) { g_assert (tr->completion_task == NULL); g_source_destroy (tr->timeout_source); g_source_unref (tr->timeout_source); g_slice_free (Transaction, tr); } static Transaction * transaction_new (MMNetlink *self, NetlinkMessage *msg, guint timeout, GTask *task) { Transaction *tr; tr = g_slice_new0 (Transaction); tr->self = self; tr->sequence_id = ++self->current_sequence_id; netlink_message_header (msg)->msghdr.nlmsg_seq = tr->sequence_id; if (timeout) { tr->timeout_source = g_timeout_source_new_seconds (timeout); g_source_set_callback (tr->timeout_source, (GSourceFunc) transaction_timed_out, tr, NULL); g_source_attach (tr->timeout_source, g_main_context_get_thread_default ()); } tr->completion_task = g_object_ref (task); g_hash_table_insert (self->transactions, GUINT_TO_POINTER (tr->sequence_id), tr); return tr; } /*****************************************************************************/ gboolean mm_netlink_setlink_finish (MMNetlink *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } void mm_netlink_setlink (MMNetlink *self, guint ifindex, gboolean up, guint mtu, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; NetlinkMessage *msg; Transaction *tr; gssize bytes_sent; GError *error = NULL; task = g_task_new (self, cancellable, callback, user_data); if (!self->socket) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "netlink support not available"); g_object_unref (task); return; } msg = netlink_message_new_setlink (ifindex, up, mtu); /* The task ownership is transferred to the transaction. */ tr = transaction_new (self, msg, 5, task); bytes_sent = g_socket_send (self->socket, (const gchar *) msg->data, msg->len, cancellable, &error); netlink_message_free (msg); if (bytes_sent < 0) transaction_complete_with_error (tr, error); g_object_unref (task); } /*****************************************************************************/ static gboolean netlink_message_cb (GSocket *socket, GIOCondition condition, MMNetlink *self) { g_autoptr(GError) error = NULL; gchar buf[512]; gssize bytes_received; guint buffer_len; struct nlmsghdr *hdr; if (condition & G_IO_HUP || condition & G_IO_ERR) { mm_obj_warn (self, "socket connection closed"); return G_SOURCE_REMOVE; } bytes_received = g_socket_receive (socket, buf, sizeof (buf), NULL, &error); if (bytes_received < 0) { mm_obj_warn (self, "socket i/o failure: %s", error->message); return G_SOURCE_REMOVE; } buffer_len = (guint) bytes_received; for (hdr = (struct nlmsghdr *) buf; NLMSG_OK (hdr, buffer_len); NLMSG_NEXT (hdr, buffer_len)) { Transaction *tr; struct nlmsgerr *err; if (hdr->nlmsg_type != NLMSG_ERROR) continue; tr = g_hash_table_lookup (self->transactions, GUINT_TO_POINTER (hdr->nlmsg_seq)); if (!tr) continue; err = NLMSG_DATA (buf); transaction_complete (tr, err->error); } return G_SOURCE_CONTINUE; } static gboolean setup_netlink_socket (MMNetlink *self, GError **error) { gint socket_fd; socket_fd = socket (AF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE); if (socket_fd < 0) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Failed to create netlink socket"); return FALSE; } self->socket = g_socket_new_from_fd (socket_fd, error); if (!self->socket) { close (socket_fd); return FALSE; } self->source = g_socket_create_source (self->socket, G_IO_IN | G_IO_ERR | G_IO_HUP, NULL); g_source_set_callback (self->source, (GSourceFunc) netlink_message_cb, self, NULL); g_source_attach (self->source, NULL); return TRUE; } /*****************************************************************************/ static gchar * log_object_build_id (MMLogObject *_self) { return g_strdup ("netlink"); } /********************************************************************/ static void mm_netlink_init (MMNetlink *self) { g_autoptr(GError) error = NULL; if (!setup_netlink_socket (self, &error)) { mm_obj_warn (self, "couldn't setup netlink socket: %s", error->message); return; } self->current_sequence_id = 0; self->transactions = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, (GDestroyNotify) transaction_free); } static void dispose (GObject *object) { MMNetlink *self = MM_NETLINK (object); g_assert (g_hash_table_size (self->transactions) == 0); g_clear_pointer (&self->transactions, g_hash_table_unref); if (self->source) g_source_destroy (self->source); g_clear_pointer (&self->source, g_source_unref); g_clear_object (&self->socket); G_OBJECT_CLASS (mm_netlink_parent_class)->dispose (object); } static void log_object_iface_init (MMLogObjectInterface *iface) { iface->build_id = log_object_build_id; } static void mm_netlink_class_init (MMNetlinkClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->dispose = dispose; } MM_DEFINE_SINGLETON_GETTER (MMNetlink, mm_netlink_get, MM_TYPE_NETLINK); /* ---------------------------------------------------------------------------------------------------- */ ModemManager-1.23.4-dev/src/mm-netlink.h000066400000000000000000000044561456466623000177510ustar00rootroot00000000000000/* -*- 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: * * Basic netlink support from libqmi: * Copyright (C) 2020 Eric Caruso * Copyright (C) 2020 Andrew Lassalle * * Copyright (C) 2021 Aleksander Morgado */ #ifndef MM_NETLINK_H #define MM_NETLINK_H #include #include G_BEGIN_DECLS #define MM_TYPE_NETLINK (mm_netlink_get_type ()) #define MM_NETLINK(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), MM_TYPE_NETLINK, MMNetlink)) #define MM_NETLINK_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), MM_TYPE_NETLINK, MMNetlinkClass)) #define MM_NETLINK_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), MM_TYPE_NETLINK, MMNetlinkClass)) #define MM_IS_NETLINK(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), MM_TYPE_NETLINK)) #define MM_IS_NETLINK_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), MM_TYPE_NETLINK)) typedef struct _MMNetlink MMNetlink; typedef struct _MMNetlinkClass MMNetlinkClass; GType mm_netlink_get_type (void) G_GNUC_CONST; MMNetlink *mm_netlink_get (void); void mm_netlink_setlink (MMNetlink *self, guint ifindex, gboolean up, guint mtu, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); gboolean mm_netlink_setlink_finish (MMNetlink *self, GAsyncResult *res, GError **error); G_END_DECLS #endif /* MM_MODEM_HELPERS_NETLINK_H */ ModemManager-1.23.4-dev/src/mm-plugin-manager.c000066400000000000000000002337431456466623000212110ustar00rootroot00000000000000/* -*- 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 - 2012 Red Hat, Inc. * Copyright (C) 2012 Google, Inc. * Copyright (C) 2011 - 2019 Aleksander Morgado */ #include #include #include #include #include #include #include #include "mm-plugin-manager.h" #include "mm-plugin.h" #include "mm-shared.h" #include "mm-utils.h" #include "mm-log-object.h" #if defined WITH_BUILTIN_PLUGINS # include "mm-builtin-plugins.h" #else # define SHARED_PREFIX "libmm-shared" # define PLUGIN_PREFIX "libmm-plugin" #endif static void initable_iface_init (GInitableIface *iface); static void log_object_iface_init (MMLogObjectInterface *iface); G_DEFINE_TYPE_EXTENDED (MMPluginManager, mm_plugin_manager, G_TYPE_OBJECT, 0, G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, initable_iface_init) G_IMPLEMENT_INTERFACE (MM_TYPE_LOG_OBJECT, log_object_iface_init)) enum { PROP_0, #if !defined WITH_BUILTIN_PLUGINS PROP_PLUGIN_DIR, #endif PROP_FILTER, LAST_PROP }; struct _MMPluginManagerPrivate { #if !defined WITH_BUILTIN_PLUGINS /* Path to look for plugins */ gchar *plugin_dir; #endif /* Device filter */ MMFilter *filter; /* This list contains all plugins except for the generic one, order is not * important. It is loaded once when the program starts, and the list is NOT * expected to change after that.*/ GList *plugins; /* Last, the generic plugin. */ MMPlugin *generic; /* List of ongoing device support checks */ GList *device_contexts; /* Full list of subsystems requested by the registered plugins */ gchar **subsystems; }; /*****************************************************************************/ /* Build plugin list for a single port */ static GList * plugin_manager_build_plugins_list (MMPluginManager *self, MMDevice *device, MMKernelDevice *port) { GList *list = NULL; GList *l; gboolean supported_found = FALSE; for (l = self->priv->plugins; l && !supported_found; l = g_list_next (l)) { MMPluginSupportsHint hint; hint = mm_plugin_discard_port_early (MM_PLUGIN (l->data), device, port); switch (hint) { case MM_PLUGIN_SUPPORTS_HINT_UNSUPPORTED: /* Fully discard */ break; case MM_PLUGIN_SUPPORTS_HINT_MAYBE: /* Maybe supported, add to tail of list */ list = g_list_append (list, g_object_ref (l->data)); break; case MM_PLUGIN_SUPPORTS_HINT_LIKELY: /* Likely supported, add to head of list */ list = g_list_prepend (list, g_object_ref (l->data)); break; case MM_PLUGIN_SUPPORTS_HINT_SUPPORTED: /* Really supported, clean existing list and add it alone */ if (list) { g_list_free_full (list, g_object_unref); list = NULL; } list = g_list_prepend (list, g_object_ref (l->data)); /* This will end the loop as well */ supported_found = TRUE; break; default: g_assert_not_reached (); } } /* Add the generic plugin at the end of the list */ if (self->priv->generic) list = g_list_append (list, g_object_ref (self->priv->generic)); return list; } /*****************************************************************************/ /* Common context for async operations * * The DeviceContext and PortContext structs are not proper objects, and that * means that they cannot be given as core parameter of GIO async results. * Instead, we'll use the MMPluginManager as that core parameter always, and * we'll pass along a common context with all the remaining details as user * data. */ typedef struct _DeviceContext DeviceContext; typedef struct _PortContext PortContext; static DeviceContext *device_context_ref (DeviceContext *device_context); static void device_context_unref (DeviceContext *device_context); static PortContext *port_context_ref (PortContext *port_context); static void port_context_unref (PortContext *port_context); typedef struct { MMPluginManager *self; DeviceContext *device_context; PortContext *port_context; GTask *task; } CommonAsyncContext; static void common_async_context_free (CommonAsyncContext *common) { if (common->port_context) port_context_unref (common->port_context); if (common->device_context) device_context_unref (common->device_context); if (common->self) g_object_unref (common->self); if (common->task) g_object_unref (common->task); g_slice_free (CommonAsyncContext, common); } static CommonAsyncContext * common_async_context_new (MMPluginManager *self, DeviceContext *device_context, PortContext *port_context, GTask *task) { CommonAsyncContext *common; common = g_slice_new0 (CommonAsyncContext); common->self = (self ? g_object_ref (self) : NULL); common->device_context = (device_context ? device_context_ref (device_context) : NULL); common->port_context = (port_context ? port_context_ref (port_context) : NULL); common->task = (task ? g_object_ref (task) : NULL); return common; } /*****************************************************************************/ /* Port context */ /* Default time to defer probing checks */ #define DEFER_TIMEOUT_SECS 3 /* * Port context * * This structure holds all the probing information related to a single port. */ struct _PortContext { /* Reference counting */ volatile gint ref_count; /* The name of the context */ gchar *name; /* The device where the port is*/ MMDevice *device; /* The reported kernel port object */ MMKernelDevice *port; /* The operation task */ GTask *task; /* Internal ancellable */ GCancellable *cancellable; /* Timer tracking how much time is required for the port support check */ GTimer *timer; /* This list contains all the plugins that have to be tested with a given * port. The list is created once when the task is started, and is never * modified afterwards. */ GList *plugins; /* This is the current plugin being tested. If NULL, there are no more * plugins to try. */ GList *current; /* A best plugin has been found for this port. */ MMPlugin *best_plugin; /* A plugin was suggested for this port. */ MMPlugin *suggested_plugin; /* The probe has been deferred */ guint defer_id; /* The probe must be deferred until a result is suggested by other * port probe results (e.g. for WWAN ports). */ gboolean defer_until_suggested; }; static void port_context_unref (PortContext *port_context) { if (g_atomic_int_dec_and_test (&port_context->ref_count)) { /* There must never be a deferred task scheduled for this port */ g_assert (port_context->defer_id == 0); /* The port support check task must have been completed previously */ g_assert (!port_context->task); if (port_context->best_plugin) g_object_unref (port_context->best_plugin); if (port_context->suggested_plugin) g_object_unref (port_context->suggested_plugin); if (port_context->plugins) g_list_free_full (port_context->plugins, g_object_unref); if (port_context->cancellable) g_object_unref (port_context->cancellable); g_free (port_context->name); g_timer_destroy (port_context->timer); g_object_unref (port_context->port); g_object_unref (port_context->device); g_slice_free (PortContext, port_context); } } static PortContext * port_context_ref (PortContext *port_context) { g_atomic_int_inc (&port_context->ref_count); return port_context; } static MMPlugin * port_context_run_finish (MMPluginManager *self, GAsyncResult *res, GError **error) { return g_task_propagate_pointer (G_TASK (res), error); } static void port_context_complete (PortContext *port_context) { MMPluginManager *self; GTask *task; /* If already completed, do nothing */ if (!port_context->task) return; /* Steal the task from the context, we only will complete once */ task = port_context->task; port_context->task = NULL; /* Log about the time required to complete the checks */ self = g_task_get_source_object (task); mm_obj_dbg (self, "task %s: finished in '%lf' seconds", port_context->name, g_timer_elapsed (port_context->timer, NULL)); if (!port_context->best_plugin) g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "Unsupported"); else g_task_return_pointer (task, g_object_ref (port_context->best_plugin), g_object_unref); g_object_unref (task); } static void port_context_next (PortContext *port_context); static void port_context_supported (PortContext *port_context, MMPlugin *plugin) { MMPluginManager *self; g_assert (plugin); self = g_task_get_source_object (port_context->task); mm_obj_dbg (self, "task %s: found best plugin for port (%s)", port_context->name, mm_plugin_get_name (plugin)); /* Found a best plugin, store it to return it */ port_context->best_plugin = g_object_ref (plugin); port_context_complete (port_context); } static gboolean port_context_defer_ready (PortContext *port_context) { port_context->defer_id = 0; port_context_next (port_context); return G_SOURCE_REMOVE; } static void port_context_set_suggestion (PortContext *port_context, MMPlugin *suggested_plugin) { MMPluginManager *self; gboolean forbidden_icera; /* Plugin suggestions serve two different purposes here: * 1) Finish all the probes which were deferred until suggested. * 2) Suggest to other probes which plugin to test next. * * The exception here is when we suggest the GENERIC plugin. * In this case, only purpose (1) is applied, this is, only * the deferred until suggested probes get finished. */ /* Do nothing if already best plugin found, or if a plugin has already been * suggested before */ if (port_context->best_plugin || port_context->suggested_plugin) return; /* There may not be a task at this point, so be gentle */ self = port_context->task ? g_task_get_source_object (port_context->task) : NULL; /* Complete tasks which were deferred until suggested */ if (port_context->defer_until_suggested) { /* Reset the defer until suggested flag; we consider this * cancelled probe completed now. */ port_context->defer_until_suggested = FALSE; if (suggested_plugin) { mm_obj_dbg (self, "task %s: deferred task completed, got suggested plugin (%s)", port_context->name, mm_plugin_get_name (suggested_plugin)); /* Advance to the suggested plugin and re-check support there */ port_context->suggested_plugin = g_object_ref (suggested_plugin); port_context->current = g_list_find (port_context->current, port_context->suggested_plugin); /* Schedule checking support */ g_assert (port_context->defer_id == 0); port_context->defer_id = g_idle_add ((GSourceFunc) port_context_defer_ready, port_context); return; } mm_obj_dbg (self, "task %s: deferred task completed, no suggested plugin", port_context->name); port_context_complete (port_context); return; } /* If no plugin being suggested, done */ if (!suggested_plugin) return; /* The GENERIC plugin is NEVER suggested to others */ if (mm_plugin_is_generic (suggested_plugin)) return; /* If the plugin has MM_PLUGIN_FORBIDDEN_ICERA set, we do *not* suggest * the plugin to others. Icera devices may not reply to the icera probing * in all ports, so if other ports need to be tested for icera support, * they should all go on. */ g_object_get (suggested_plugin, MM_PLUGIN_FORBIDDEN_ICERA, &forbidden_icera, NULL); if (forbidden_icera) return; /* We should *not* cancel probing in the port if the plugin being * checked right now is not the one being suggested. Each port * should run its probing independently, and we'll later decide * which result applies to the whole device. */ mm_obj_dbg (self, "task %s: got suggested plugin (%s)", port_context->name, mm_plugin_get_name (suggested_plugin)); port_context->suggested_plugin = g_object_ref (suggested_plugin); } static void port_context_unsupported (PortContext *port_context, MMPlugin *plugin) { MMPluginManager *self; g_assert (plugin); self = g_task_get_source_object (port_context->task); /* If there is no suggested plugin, go on to the next one */ if (!port_context->suggested_plugin) { port_context->current = g_list_next (port_context->current); port_context_next (port_context); return; } /* If the plugin that just completed the support check claims * not to support this port, but this plugin is clearly the * right plugin since it claimed this port's physical modem, * just cancel the port probing and avoid more tests. */ if (port_context->suggested_plugin == plugin) { mm_obj_dbg (self, "task %s: ignoring port unsupported by physical modem's plugin", port_context->name); port_context_complete (port_context); return; } /* The last plugin we tried is NOT the one we got suggested, so * directly check support with the suggested plugin. If we * already checked its support, it won't be checked again. */ port_context->current = g_list_find (port_context->current, port_context->suggested_plugin); port_context_next (port_context); } static void port_context_defer (PortContext *port_context) { MMPluginManager *self; self = g_task_get_source_object (port_context->task); /* Try with the suggested one after being deferred */ if (port_context->suggested_plugin) { mm_obj_dbg (self, "task %s: deferring support check (%s suggested)", port_context->name, mm_plugin_get_name (MM_PLUGIN (port_context->suggested_plugin))); port_context->current = g_list_find (port_context->current, port_context->suggested_plugin); } else mm_obj_dbg (self, "task %s: deferring support check", port_context->name); /* Schedule checking support. * * In this case we don't pass a port context reference because we're able * to fully cancel the timeout ourselves. */ port_context->defer_id = g_timeout_add_seconds (DEFER_TIMEOUT_SECS, (GSourceFunc) port_context_defer_ready, port_context); } static void port_context_defer_until_suggested (PortContext *port_context, MMPlugin *plugin) { MMPluginManager *self; g_assert (plugin); self = g_task_get_source_object (port_context->task); /* If we arrived here and we already have a plugin suggested, use it */ if (port_context->suggested_plugin) { /* We can finish this context */ if (port_context->suggested_plugin == plugin) { mm_obj_dbg (self, "task %s: completed, got suggested plugin (%s)", port_context->name, mm_plugin_get_name (port_context->suggested_plugin)); /* Store best plugin and end operation */ port_context->best_plugin = g_object_ref (port_context->suggested_plugin); port_context_complete (port_context); return; } /* Recheck support in deferred task */ mm_obj_dbg (self, "task %s: re-checking support on deferred task, got suggested plugin (%s)", port_context->name, mm_plugin_get_name (port_context->suggested_plugin)); port_context->current = g_list_find (port_context->current, port_context->suggested_plugin); port_context_next (port_context); return; } /* We are deferred until a suggested plugin is given. If last supports task * of a given device is finished without finding a best plugin, this task * will get finished reporting unsupported. */ mm_obj_dbg (self, "task %s: deferring support check until result suggested", port_context->name); port_context->defer_until_suggested = TRUE; } static void plugin_supports_port_ready (MMPlugin *plugin, GAsyncResult *res, PortContext *port_context) { MMPluginManager *self; MMPluginSupportsResult support_result; GError *error = NULL; self = g_task_get_source_object (port_context->task); /* Get supports check results */ support_result = mm_plugin_supports_port_finish (plugin, res, &error); if (error) { g_assert_cmpuint (support_result, ==, MM_PLUGIN_SUPPORTS_PORT_UNKNOWN); mm_obj_warn (self, "task %s: error when checking support with plugin '%s': %s", port_context->name, mm_plugin_get_name (plugin), error->message); g_error_free (error); } switch (support_result) { case MM_PLUGIN_SUPPORTS_PORT_SUPPORTED: port_context_supported (port_context, plugin); break; case MM_PLUGIN_SUPPORTS_PORT_UNKNOWN: case MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED: port_context_unsupported (port_context, plugin); break; case MM_PLUGIN_SUPPORTS_PORT_DEFER: port_context_defer (port_context); break; case MM_PLUGIN_SUPPORTS_PORT_DEFER_UNTIL_SUGGESTED: port_context_defer_until_suggested (port_context, plugin); break; default: g_assert_not_reached (); } /* We received a full reference, to make sure the context was always * valid during the async call */ port_context_unref (port_context); } static void port_context_next (PortContext *port_context) { MMPluginManager *self; MMPlugin *plugin; self = g_task_get_source_object (port_context->task); /* If we're cancelled, done */ if (g_cancellable_is_cancelled (port_context->cancellable)) { port_context_complete (port_context); return; } /* Already checked all plugins? */ if (!port_context->current) { port_context_complete (port_context); return; } /* Ask the current plugin to check support of this port. * * A full new reference to the port context is given as user data to the * async method because we want to make sure the context is still valid * once the method finishes. */ plugin = MM_PLUGIN (port_context->current->data); mm_obj_dbg (self, "task %s: checking with plugin '%s'", port_context->name, mm_plugin_get_name (plugin)); mm_plugin_supports_port (plugin, port_context->device, port_context->port, port_context->cancellable, (GAsyncReadyCallback) plugin_supports_port_ready, port_context_ref (port_context)); } static gboolean port_context_cancel (PortContext *port_context) { MMPluginManager *self; /* Port context cancellation, which only makes sense if the context is * actually being run, so just exit if it isn't. */ if (!port_context->task) return FALSE; /* If cancelled already, do nothing */ if (g_cancellable_is_cancelled (port_context->cancellable)) return FALSE; self = g_task_get_source_object (port_context->task); mm_obj_dbg (self, "task %s: cancellation requested", port_context->name); /* Make sure we hold a port context reference while cancelling, as the * cancellable signal handlers may end up unref-ing our last reference * otherwise. */ port_context_ref (port_context); { /* The port context is cancelled now */ g_cancellable_cancel (port_context->cancellable); /* If the task was deferred, we can cancel and complete it right away */ if (port_context->defer_id) { g_source_remove (port_context->defer_id); port_context->defer_id = 0; port_context_complete (port_context); } /* If the task was deferred until a result is suggested, we can also * complete it right away */ else if (port_context->defer_until_suggested) port_context_complete (port_context); /* else, the task may be currently checking support with a given plugin */ } port_context_unref (port_context); return TRUE; } static void port_context_run (MMPluginManager *self, PortContext *port_context, GList *plugins, MMPlugin *suggested, GAsyncReadyCallback callback, gpointer user_data) { g_assert (!port_context->task); g_assert (!port_context->defer_id); g_assert (!port_context->plugins); g_assert (!port_context->current); g_assert (!port_context->suggested_plugin); /* Setup plugins to probe and first one to check. */ port_context->plugins = g_list_copy_deep (plugins, (GCopyFunc) g_object_ref, NULL); port_context->current = port_context->plugins; /* If we got one suggested, it will be the first one */ if (suggested) { port_context->suggested_plugin = g_object_ref (suggested); port_context->current = g_list_find (port_context->current, port_context->suggested_plugin); if (!port_context->current) mm_obj_warn (self, "task %s: suggested plugin (%s) not among the ones to test", port_context->name, mm_plugin_get_name (suggested)); } /* Log the list of plugins found and specify which are the ones that are going * to be run */ { gboolean suggested_found = FALSE; GList *l; mm_obj_dbg (self, "task %s: found '%u' plugins to try", port_context->name, g_list_length (port_context->plugins)); for (l = port_context->plugins; l; l = g_list_next (l)) { MMPlugin *plugin; plugin = MM_PLUGIN (l->data); if (suggested_found) { mm_obj_dbg (self, "task %s: may try with plugin '%s'", port_context->name, mm_plugin_get_name (plugin)); continue; } if (suggested && l == port_context->current) { suggested_found = TRUE; mm_obj_dbg (self, "task %s: will try with plugin '%s' (suggested)", port_context->name, mm_plugin_get_name (plugin)); continue; } if (suggested && !suggested_found) { mm_obj_dbg (self, "task %s: won't try with plugin '%s' (skipped)", port_context->name, mm_plugin_get_name (plugin)); continue; } mm_obj_dbg (self, "task %s: will try with plugin '%s'", port_context->name, mm_plugin_get_name (plugin)); } } /* The full port context is now cancellable. We pass this cancellable also * to the inner GTask, so that if we're cancelled we always return a * cancellation error, regardless of what the standard logic does. */ port_context->cancellable = g_cancellable_new (); /* Create an inner task for the port context. The result we expect is the * best plugin found for the port. */ port_context->task = g_task_new (self, port_context->cancellable, callback, user_data); mm_obj_dbg (self, "task %s: started", port_context->name); /* Go probe with the first plugin */ port_context_next (port_context); } static PortContext * port_context_new (MMPluginManager *self, const gchar *parent_name, MMDevice *device, MMKernelDevice *port) { PortContext *port_context; port_context = g_slice_new0 (PortContext); port_context->ref_count = 1; port_context->device = g_object_ref (device); port_context->port = g_object_ref (port); port_context->timer = g_timer_new (); /* Set context name */ port_context->name = g_strdup_printf ("%s,%s", parent_name, mm_kernel_device_get_name (port)); return port_context; } /*****************************************************************************/ /* Device context */ /* Time to wait for ports to appear before starting to probe the first one */ #define MIN_WAIT_TIME_MSECS 2000 /* Time to wait for other ports to appear once the first port is exposed * (needs to be > MIN_WAIT_TIME_MSECS!!) */ #define MIN_PROBING_TIME_MSECS 4000 /* Additional time to wait for other ports to appear after the last port is * exposed in the system. Longer time when not using udev, as we rely on * mmcli --report-kernel-event events to report new port additions, e.g. * via openwrt hotplug scripts. */ #if defined WITH_UDEV # define EXTRA_PROBING_TIME_MSECS 2000 #else # define EXTRA_PROBING_TIME_MSECS 4000 #endif /* The wait time we define must always be less than the probing time */ G_STATIC_ASSERT (MIN_WAIT_TIME_MSECS < MIN_PROBING_TIME_MSECS); /* * Device context * * This structure holds all the information related to a single device. This * information includes references to all port contexts generated in the device, * as well as a reference to the parent plugin manager object and the async * task to complete when finished. */ struct _DeviceContext { /* Reference counting */ volatile gint ref_count; /* The name of the context */ gchar *name; /* The plugin manager */ MMPluginManager *self; /* The device for which we're looking support */ MMDevice *device; /* The operation task */ GTask *task; /* Internal cancellable */ GCancellable *cancellable; /* Timer tracking how much time is required for the device support check */ GTimer *timer; /* The best plugin at a given moment. Once the last port task finishes, this * will be the one being returned in the async result */ MMPlugin *best_plugin; /* Minimum wait time. No port probing can start before this timeout expires. * Once the timeout is expired, the id is reset to 0. */ guint min_wait_time_id; /* Port support check contexts waiting to be run after min wait time */ GList *wait_port_contexts; /* Minimum probing time, which is a timeout initialized as soon as the first * port is added to the device context. The device support check task cannot * be finished before this timeout expires. Once the timeout is expired, the * id is reset to 0. */ guint min_probing_time_id; /* Extra probing time, which is a timeout refreshed every time a new port * is added to the device context. The device support check task cannot be * finished before this timeout expires. Once the timeout is expired, the id * is reset to 0. */ guint extra_probing_time_id; /* Signal connection ids for the grabbed/released signals from the device. * These are the signals that will give us notifications of what ports are * available (or suddenly unavailable) in the device. */ gulong grabbed_id; gulong released_id; /* Port support check contexts being run */ GList *port_contexts; }; static void device_context_unref (DeviceContext *device_context) { if (g_atomic_int_dec_and_test (&device_context->ref_count)) { /* When the last reference is gone there must be no source scheduled and no * pending port tasks. */ g_assert (!device_context->grabbed_id); g_assert (!device_context->released_id); g_assert (!device_context->min_wait_time_id); g_assert (!device_context->min_probing_time_id); g_assert (!device_context->extra_probing_time_id); g_assert (!device_context->port_contexts); /* The device support check task must have been completed previously */ g_assert (!device_context->task); g_free (device_context->name); g_timer_destroy (device_context->timer); if (device_context->cancellable) g_object_unref (device_context->cancellable); if (device_context->best_plugin) g_object_unref (device_context->best_plugin); g_object_unref (device_context->device); g_object_unref (device_context->self); g_slice_free (DeviceContext, device_context); } } static DeviceContext * device_context_ref (DeviceContext *device_context) { g_atomic_int_inc (&device_context->ref_count); return device_context; } static PortContext * device_context_peek_running_port_context (DeviceContext *device_context, MMKernelDevice *port) { GList *l; for (l = device_context->port_contexts; l; l = g_list_next (l)) { PortContext *port_context; port_context = (PortContext *)(l->data); if ((port_context->port == port) || (!g_strcmp0 (mm_kernel_device_get_name (port_context->port), mm_kernel_device_get_name (port)))) return port_context; } return NULL; } static PortContext * device_context_peek_waiting_port_context (DeviceContext *device_context, MMKernelDevice *port) { GList *l; for (l = device_context->wait_port_contexts; l; l = g_list_next (l)) { PortContext *port_context; port_context = (PortContext *)(l->data); if ((port_context->port == port) || (!g_strcmp0 (mm_kernel_device_get_name (port_context->port), mm_kernel_device_get_name (port)))) return port_context; } return NULL; } static MMPlugin * device_context_run_finish (MMPluginManager *self, GAsyncResult *res, GError **error) { return MM_PLUGIN (g_task_propagate_pointer (G_TASK (res), error)); } static void device_context_complete (DeviceContext *device_context) { MMPluginManager *self; GTask *task; self = g_task_get_source_object (device_context->task); /* If the context is completed before the 2500ms minimum probing time, we need to wait * until that happens, so that we give enough time to udev/hotplug to report the * new port additions. */ if (device_context->min_probing_time_id) { mm_obj_dbg (self, "task %s: all port probings completed, but not reached min probing time yet", device_context->name); return; } /* If the context is completed less than 1500ms before the last port was exposed, * wait some more. */ if (device_context->extra_probing_time_id) { mm_obj_dbg (self, "task %s: all port probings completed, but not reached extra probing time yet", device_context->name); return; } /* Steal the task from the context */ g_assert (device_context->task); task = device_context->task; device_context->task = NULL; /* Log about the time required to complete the checks */ mm_obj_dbg (self, "task %s: finished in '%lf' seconds", device_context->name, g_timer_elapsed (device_context->timer, NULL)); /* Remove signal handlers */ if (device_context->grabbed_id) { g_signal_handler_disconnect (device_context->device, device_context->grabbed_id); device_context->grabbed_id = 0; } if (device_context->released_id) { g_signal_handler_disconnect (device_context->device, device_context->released_id); device_context->released_id = 0; } /* On completion, the minimum wait time must have been already elapsed */ g_assert (!device_context->min_wait_time_id); /* Task completion */ if (!device_context->best_plugin) g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "not supported by any plugin"); else g_task_return_pointer (task, g_object_ref (device_context->best_plugin), g_object_unref); g_object_unref (task); } static void device_context_suggest_plugin (DeviceContext *device_context, PortContext *port_context, MMPlugin *suggested_plugin) { GList *l; GList *listdup; /* If the suggested plugin is NULL, we'll propagate the suggestion only if all * the port contexts are deferred until suggested. */ if (!suggested_plugin) { for (l = device_context->port_contexts; l; l = g_list_next (l)) { PortContext *other_port_context = (PortContext *)(l->data); /* Do not propagate NULL if we find at least one probe which is not * waiting for the suggested plugin */ if (other_port_context != port_context && !other_port_context->defer_until_suggested) return; } } /* Do the suggestion propagation. * Shallow copy, just so that we can iterate safely without worrying about the * original list being modified while hte completions happen */ listdup = g_list_copy (device_context->port_contexts); for (l = listdup; l; l = g_list_next (l)) port_context_set_suggestion ((PortContext *)(l->data), suggested_plugin); g_list_free (listdup); } static void device_context_set_best_plugin (DeviceContext *device_context, PortContext *port_context, MMPlugin *best_plugin) { MMPluginManager *self; self = g_task_get_source_object (device_context->task); if (!best_plugin) { /* If the port appeared after an already probed port, which decided that * the Generic plugin was the best one (which is by default not initially * suggested), we'll end up arriving here. Don't ignore it, it may well * be a wwan port that we do need to grab. */ if (device_context->best_plugin) { mm_obj_dbg (self, "task %s: assuming port can be handled by the '%s' plugin", port_context->name, mm_plugin_get_name (device_context->best_plugin)); return; } /* Unsupported error, this is generic when we cannot find a plugin */ mm_obj_dbg (self, "task %s: not supported by any plugin" , port_context->name); /* Tell the device to ignore this port */ mm_device_ignore_port (device_context->device, port_context->port); /* If this is the last valid probe which was running (i.e. the last one * not being deferred-until-suggested), cancel all remaining ones. */ device_context_suggest_plugin (device_context, port_context, NULL); return; } /* Store the plugin as the best one in the device if this is the first * result we got. Also, if the previously suggested plugin was the GENERIC * one and now we're reporting a more specific one, use the new one. */ if (!device_context->best_plugin || (mm_plugin_is_generic (device_context->best_plugin) && device_context->best_plugin != best_plugin)) { /* Only log best plugin if it's not the generic one */ if (!mm_plugin_is_generic (best_plugin)) mm_obj_dbg (self, "task %s: found best plugin: %s", port_context->name, mm_plugin_get_name (best_plugin)); /* Store and suggest this plugin also to other port probes */ device_context->best_plugin = g_object_ref (best_plugin); device_context_suggest_plugin (device_context, port_context, best_plugin); return; } /* Warn if the best plugin found for this port differs from the * best plugin found for the the first probed port */ if (!g_str_equal (mm_plugin_get_name (device_context->best_plugin), mm_plugin_get_name (best_plugin))) { /* Icera modems may not reply to the icera probing in all ports. We handle this by * checking the forbidden/allowed icera flags in both the current and the expected * plugins. If either of these plugins requires icera and the other doesn't, we * pick the Icera one as best plugin. */ gboolean previous_forbidden_icera; gboolean previous_allowed_icera; gboolean new_forbidden_icera; gboolean new_allowed_icera; g_object_get (device_context->best_plugin, MM_PLUGIN_ALLOWED_ICERA, &previous_allowed_icera, MM_PLUGIN_FORBIDDEN_ICERA, &previous_forbidden_icera, NULL); g_assert (previous_allowed_icera == FALSE || previous_forbidden_icera == FALSE); g_object_get (best_plugin, MM_PLUGIN_ALLOWED_ICERA, &new_allowed_icera, MM_PLUGIN_FORBIDDEN_ICERA, &new_forbidden_icera, NULL); g_assert (new_allowed_icera == FALSE || new_forbidden_icera == FALSE); if (previous_allowed_icera && new_forbidden_icera) mm_obj_warn (self, "task %s: will use plugin '%s' instead of '%s', modem is icera-capable", port_context->name, mm_plugin_get_name (device_context->best_plugin), mm_plugin_get_name (best_plugin)); else if (new_allowed_icera && previous_forbidden_icera) { mm_obj_warn (self, "task %s: overriding previously selected device plugin '%s' with '%s', modem is icera-capable", port_context->name, mm_plugin_get_name (device_context->best_plugin), mm_plugin_get_name (best_plugin)); g_object_unref (device_context->best_plugin); device_context->best_plugin = g_object_ref (best_plugin); } else mm_obj_warn (self, "task %s: plugin mismatch error (device reports '%s', port reports '%s')", port_context->name, mm_plugin_get_name (device_context->best_plugin), mm_plugin_get_name (best_plugin)); return; } /* Device plugin equal to best plugin */ mm_obj_dbg (self, "task %s: best plugin matches device reported one: %s", port_context->name, mm_plugin_get_name (best_plugin)); } static void device_context_continue (DeviceContext *device_context) { MMPluginManager *self; GList *l; GString *s = NULL; guint n = 0; guint n_active = 0; self = g_task_get_source_object (device_context->task); /* If there are no running port contexts around, we're free to finish */ if (!device_context->port_contexts) { mm_obj_dbg (self, "task %s: no more ports to probe", device_context->name); device_context_complete (device_context); return; } /* We'll count how many port contexts are 'active' (i.e. not deferred * until a result is suggested). Also, prepare to log about the pending * ports */ for (l = device_context->port_contexts; l; l = g_list_next (l)) { PortContext *port_context = (PortContext *) (l->data); const gchar *portname; portname = mm_kernel_device_get_name (port_context->port); if (!s) s = g_string_new (portname); else g_string_append_printf (s, ", %s", portname); /* Active? */ if (!port_context->defer_until_suggested) n_active++; n++; } g_assert (n > 0 && s); mm_obj_dbg (self, "task %s: still %u running probes (%u active): %s", device_context->name, n, n_active, s->str); g_string_free (s, TRUE); if (n_active == 0) { mm_obj_dbg (self, "task %s: no active tasks to probe", device_context->name); device_context_suggest_plugin (device_context, NULL, NULL); } } static void port_context_run_ready (MMPluginManager *self, GAsyncResult *res, CommonAsyncContext *common) { GError *error = NULL; MMPlugin *best_plugin; /* Returns a full reference to the best plugin */ best_plugin = port_context_run_finish (self, res, &error); if (!best_plugin) { /* The only error we can ignore is UNSUPPORTED */ if (g_error_matches (error, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED)) { /* This error is not critical */ device_context_set_best_plugin (common->device_context, common->port_context, NULL); } else mm_obj_warn (self, "task %s: failed: %s", common->port_context->name, error->message); g_error_free (error); } else { /* Set the plugin as the best one in the device context */ device_context_set_best_plugin (common->device_context, common->port_context, best_plugin); g_object_unref (best_plugin); } /* We MUST have the port context in the list at this point, because we're * going to remove the reference, so assert if this is not true. The caller * must always make sure that the port_context is available in the list */ g_assert (g_list_find (common->device_context->port_contexts, common->port_context)); common->device_context->port_contexts = g_list_remove (common->device_context->port_contexts, common->port_context); port_context_unref (common->port_context); /* Continue the device context logic */ device_context_continue (common->device_context); /* Cleanup the context of the async operation */ common_async_context_free (common); } static gboolean device_context_min_probing_time_elapsed (DeviceContext *device_context) { MMPluginManager *self; device_context->min_probing_time_id = 0; self = g_task_get_source_object (device_context->task); mm_obj_dbg (self, "task %s: min probing time elapsed", device_context->name); /* Wakeup the device context logic */ device_context_continue (device_context); return G_SOURCE_REMOVE; } static gboolean device_context_extra_probing_time_elapsed (DeviceContext *device_context) { MMPluginManager *self; device_context->extra_probing_time_id = 0; self = g_task_get_source_object (device_context->task); mm_obj_dbg (self, "task %s: extra probing time elapsed", device_context->name); /* Wakeup the device context logic */ device_context_continue (device_context); return G_SOURCE_REMOVE; } static void device_context_run_port_context (DeviceContext *device_context, PortContext *port_context) { GList *plugins; MMPlugin *suggested = NULL; MMPluginManager *self; /* Recover plugin manager */ self = MM_PLUGIN_MANAGER (device_context->self); /* Setup plugins to probe and first one to check. * Make sure this plugins list is built after the MIN WAIT TIME has been expired * (so that per-driver filters work correctly) */ plugins = plugin_manager_build_plugins_list (self, device_context->device, port_context->port); /* If we got one already set in the device context, it will be the first one, * unless it is the generic plugin */ if (device_context->best_plugin && !mm_plugin_is_generic (device_context->best_plugin)) suggested = device_context->best_plugin; port_context_run (self, port_context, plugins, suggested, (GAsyncReadyCallback) port_context_run_ready, common_async_context_new (self, device_context, port_context, NULL)); g_list_free_full (plugins, g_object_unref); } static gboolean device_context_min_wait_time_elapsed (DeviceContext *device_context) { MMPluginManager *self; GList *l; GList *tmp; self = device_context->self; device_context->min_wait_time_id = 0; mm_obj_dbg (self, "task %s: min wait time elapsed", device_context->name); /* Move list of port contexts out of the wait list */ g_assert (!device_context->port_contexts); tmp = device_context->wait_port_contexts; device_context->wait_port_contexts = NULL; /* Launch supports check for each port in the Plugin Manager */ for (l = tmp; l; l = g_list_next (l)) { PortContext *port_context = (PortContext *)(l->data); if (!mm_filter_device_and_port (self->priv->filter, port_context->device, port_context->port)) { /* If port is filtered, unref it right away */ port_context_unref (port_context); } else { /* If port not filtered, store and run it */ device_context->port_contexts = g_list_append (device_context->port_contexts, port_context); device_context_run_port_context (device_context, port_context); } } g_list_free (tmp); return G_SOURCE_REMOVE; } static void device_context_port_released (DeviceContext *device_context, MMKernelDevice *port) { MMPluginManager *self; PortContext *port_context; self = g_task_get_source_object (device_context->task); mm_obj_dbg (self, "task %s: port released: %s", device_context->name, mm_kernel_device_get_name (port)); /* Check if there's a waiting port context */ port_context = device_context_peek_waiting_port_context (device_context, port); if (port_context) { /* We didn't run the port context yet, we can remove it right away */ device_context->wait_port_contexts = g_list_remove (device_context->wait_port_contexts, port_context); port_context_unref (port_context); return; } /* Now, check running port contexts, which will need cancellation if found */ port_context = device_context_peek_running_port_context (device_context, port); if (port_context) { /* Request cancellation of this single port, will be completed asynchronously */ port_context_cancel (port_context); return; } /* This is not something worth warning. If the probing task has already * been finished, it will already be removed from the list */ mm_obj_dbg (self, "task %s: port wasn't found: %s", device_context->name, mm_kernel_device_get_name (port)); } static void device_context_port_grabbed (DeviceContext *device_context, MMKernelDevice *port) { MMPluginManager *self; PortContext *port_context; /* Recover plugin manager */ self = MM_PLUGIN_MANAGER (device_context->self); mm_obj_dbg (self, "task %s: port grabbed: %s", device_context->name, mm_kernel_device_get_name (port)); /* Ignore if for any reason we still have it in the running list */ port_context = device_context_peek_running_port_context (device_context, port); if (port_context) { mm_obj_warn (self, "task %s: port context already being processed", device_context->name); return; } /* Ignore if for any reason we still have it in the waiting list */ port_context = device_context_peek_waiting_port_context (device_context, port); if (port_context) { mm_obj_warn (self, "task %s: port context already scheduled", device_context->name); return; } /* Refresh the extra probing timeout. */ if (device_context->extra_probing_time_id) g_source_remove (device_context->extra_probing_time_id); device_context->extra_probing_time_id = g_timeout_add (EXTRA_PROBING_TIME_MSECS, (GSourceFunc) device_context_extra_probing_time_elapsed, device_context); /* Setup a new port context for the newly grabbed port */ port_context = port_context_new (self, device_context->name, device_context->device, port); mm_obj_dbg (self, "task %s: new support task for port", port_context->name); /* Îf still waiting the min wait time, store it in the waiting list */ if (device_context->min_wait_time_id) { mm_obj_dbg (self, "task %s: deferred until min wait time elapsed", port_context->name); /* Store the port reference in the list within the device */ device_context->wait_port_contexts = g_list_prepend (device_context->wait_port_contexts, port_context); return; } /* Store the port reference in the list within the device */ device_context->port_contexts = g_list_prepend (device_context->port_contexts, port_context) ; /* If the port has been grabbed after the min wait timeout expired, launch * probing directly */ device_context_run_port_context (device_context, port_context); } static gboolean device_context_cancel (DeviceContext *device_context) { MMPluginManager *self; /* If cancelled already, do nothing */ if (g_cancellable_is_cancelled (device_context->cancellable)) return FALSE; self = g_task_get_source_object (device_context->task); mm_obj_dbg (self, "task %s: cancellation requested", device_context->name); /* The device context is cancelled now */ g_cancellable_cancel (device_context->cancellable); /* Remove all port contexts in the waiting list. This will allow early cancellation * if it arrives before the min wait time has elapsed */ if (device_context->wait_port_contexts) { g_assert (!device_context->port_contexts); g_list_free_full (device_context->wait_port_contexts, (GDestroyNotify) port_context_unref); device_context->wait_port_contexts = NULL; } /* Cancel all ongoing port contexts, if they're not already cancelled */ if (device_context->port_contexts) { g_assert (!device_context->wait_port_contexts); /* Request cancellation, will be completed asynchronously */ g_list_foreach (device_context->port_contexts, (GFunc) port_context_cancel, NULL); } /* Cancel all timeouts */ if (device_context->min_wait_time_id) { g_source_remove (device_context->min_wait_time_id); device_context->min_wait_time_id = 0; } if (device_context->min_probing_time_id) { g_source_remove (device_context->min_probing_time_id); device_context->min_probing_time_id = 0; } if (device_context->extra_probing_time_id) { g_source_remove (device_context->extra_probing_time_id); device_context->extra_probing_time_id = 0; } /* Wakeup the device context logic. If we were still waiting for the * min probing time, this will complete the device context. */ device_context_continue (device_context); return TRUE; } static void device_context_run (MMPluginManager *self, DeviceContext *device_context, GAsyncReadyCallback callback, gpointer user_data) { g_assert (!device_context->task); g_assert (!device_context->grabbed_id); g_assert (!device_context->released_id); g_assert (!device_context->min_wait_time_id); g_assert (!device_context->min_probing_time_id); g_assert (!device_context->extra_probing_time_id); /* Connect to device port grabbed/released notifications from the device */ device_context->grabbed_id = g_signal_connect_swapped (device_context->device, MM_DEVICE_PORT_GRABBED, G_CALLBACK (device_context_port_grabbed), device_context); device_context->released_id = g_signal_connect_swapped (device_context->device, MM_DEVICE_PORT_RELEASED, G_CALLBACK (device_context_port_released), device_context); /* Set the initial waiting timeout. We don't want to probe any port before * this timeout expires, so that we get as many ports added in the device * as possible. If we don't do this, some plugin filters won't work properly, * like the 'forbidden-drivers' one. */ device_context->min_wait_time_id = g_timeout_add (MIN_WAIT_TIME_MSECS, (GSourceFunc) device_context_min_wait_time_elapsed, device_context); /* Set the initial probing timeout. We force the probing time of the device to * be at least this amount of time, so that the kernel has enough time to * bring up ports. Given that we launch this only when the first port of the * device has been exposed in udev, this timeout effectively means that we * leave up to 2s to the remaining ports to appear. */ device_context->min_probing_time_id = g_timeout_add (MIN_PROBING_TIME_MSECS, (GSourceFunc) device_context_min_probing_time_elapsed, device_context); /* The full device context is now cancellable. We pass this cancellable also * to the inner GTask, so that if we're cancelled we always return a * cancellation error, regardless of what the standard logic does. */ device_context->cancellable = g_cancellable_new (); /* Create an inner task for the device context. We'll complete this task when * the last port has been probed. */ device_context->task = g_task_new (self, device_context->cancellable, callback, user_data); } static DeviceContext * device_context_new (MMPluginManager *self, MMDevice *device) { static gulong unique_task_id = 0; DeviceContext *device_context; /* Create new device context and store the task */ device_context = g_slice_new0 (DeviceContext); device_context->ref_count = 1; device_context->self = g_object_ref (self); device_context->device = g_object_ref (device); device_context->timer = g_timer_new (); /* Set context name (just for logging) */ device_context->name = g_strdup_printf ("%lu", unique_task_id++); return device_context; } /*****************************************************************************/ /* Look for plugin to support the given device * * This operation is initiated when the new MMDevice is detected. Once that * happens, a new support check for the whole device will arrive at the plugin * manager. * * Ports in the device, though, are added dynamically and automatically * afterwards once the device support check has been created. It is the device * support check context itself adding the newly added ports. * * The device support check task is finished once all port support check tasks * have also been finished. * * Given that the ports are added dynamically, there is some minimum duration * for the device support check task, otherwise we may end up not detecting * any port. * * The device support check tasks are stored also in the plugin manager, so * that the cancellation API doesn't require anything more specific than the * device for which the support check task should be cancelled. */ MMPlugin * mm_plugin_manager_device_support_check_finish (MMPluginManager *self, GAsyncResult *res, GError **error) { return MM_PLUGIN (g_task_propagate_pointer (G_TASK (res), error)); } static DeviceContext * plugin_manager_peek_device_context (MMPluginManager *self, MMDevice *device) { GList *l; for (l = self->priv->device_contexts; l; l = g_list_next (l)) { DeviceContext *device_context; device_context = (DeviceContext *)(l->data); if ((device == device_context->device) || (!g_strcmp0 (mm_device_get_uid (device_context->device), mm_device_get_uid (device)))) return device_context; } return NULL; } gboolean mm_plugin_manager_device_support_check_cancel (MMPluginManager *self, MMDevice *device) { DeviceContext *device_context; /* If the device context isn't found, ignore the cancellation request. */ device_context = plugin_manager_peek_device_context (self, device); if (!device_context) return FALSE; /* Request cancellation, will be completed asynchronously */ return device_context_cancel (device_context); } static void device_context_run_ready (MMPluginManager *self, GAsyncResult *res, CommonAsyncContext *common) { GError *error = NULL; MMPlugin *best_plugin; /* We get a full reference back */ best_plugin = device_context_run_finish (self, res, &error); /* * Once the task is finished, we can also remove it from the plugin manager * list. We MUST have the port context in the list at this point, because * we're going to dispose the reference, so assert if this is not true. */ g_assert (g_list_find (common->self->priv->device_contexts, common->device_context)); common->self->priv->device_contexts = g_list_remove (common->self->priv->device_contexts, common->device_context); device_context_unref (common->device_context); /* Report result or error once removed from our internal list */ if (!best_plugin) g_task_return_error (common->task, error); else g_task_return_pointer (common->task, best_plugin, g_object_unref); common_async_context_free (common); } void mm_plugin_manager_device_support_check (MMPluginManager *self, MMDevice *device, GAsyncReadyCallback callback, gpointer user_data) { DeviceContext *device_context; GTask *task; /* * Create a new task for the device support check request. * * Note that we handle cancellations ourselves, as we don't want the caller * to be required to keep track of a GCancellable for each of these tasks. */ task = g_task_new (self, NULL, callback, user_data); /* Fail if there is already a task for the same device */ device_context = plugin_manager_peek_device_context (self, device); if (device_context) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_IN_PROGRESS, "Device support check task already available for device '%s'", mm_device_get_uid (device)); g_object_unref (task); return; } /* Create new device context */ device_context = device_context_new (self, device); /* Track the device context in the list within the plugin manager. */ self->priv->device_contexts = g_list_prepend (self->priv->device_contexts, device_context); mm_obj_dbg (self, "task %s: new support task for device: %s", device_context->name, mm_device_get_uid (device_context->device)); /* Run device context */ device_context_run (self, device_context, (GAsyncReadyCallback) device_context_run_ready, common_async_context_new (self, device_context, NULL, task)); g_object_unref (task); } /*****************************************************************************/ /* Look for plugin */ MMPlugin * mm_plugin_manager_peek_plugin (MMPluginManager *self, const gchar *plugin_name) { GList *l; if (self->priv->generic && g_str_equal (plugin_name, mm_plugin_get_name (self->priv->generic))) return self->priv->generic; for (l = self->priv->plugins; l; l = g_list_next (l)) { MMPlugin *plugin = MM_PLUGIN (l->data); if (g_str_equal (plugin_name, mm_plugin_get_name (plugin))) return plugin; } return NULL; } /*****************************************************************************/ const gchar ** mm_plugin_manager_get_subsystems (MMPluginManager *self) { return (const gchar **) self->priv->subsystems; } /*****************************************************************************/ static void register_plugin_allowlist_tags (MMPluginManager *self, MMPlugin *plugin) { const gchar **tags; guint i; if (!mm_filter_check_rule_enabled (self->priv->filter, MM_FILTER_RULE_PLUGIN_ALLOWLIST)) return; tags = mm_plugin_get_allowed_udev_tags (plugin); for (i = 0; tags && tags[i]; i++) mm_filter_register_plugin_allowlist_tag (self->priv->filter, tags[i]); } static void register_plugin_allowlist_vendor_ids (MMPluginManager *self, MMPlugin *plugin) { const guint16 *vendor_ids; guint i; if (!mm_filter_check_rule_enabled (self->priv->filter, MM_FILTER_RULE_PLUGIN_ALLOWLIST)) return; vendor_ids = mm_plugin_get_allowed_vendor_ids (plugin); for (i = 0; vendor_ids && vendor_ids[i]; i++) mm_filter_register_plugin_allowlist_vendor_id (self->priv->filter, vendor_ids[i]); } static void register_plugin_allowlist_product_ids (MMPluginManager *self, MMPlugin *plugin) { const mm_uint16_pair *product_ids; guint i; if (!mm_filter_check_rule_enabled (self->priv->filter, MM_FILTER_RULE_PLUGIN_ALLOWLIST)) return; product_ids = mm_plugin_get_allowed_product_ids (plugin); for (i = 0; product_ids && product_ids[i].l; i++) mm_filter_register_plugin_allowlist_product_id (self->priv->filter, product_ids[i].l, product_ids[i].r); } static void register_plugin_allowlist_subsystem_vendor_ids (MMPluginManager *self, MMPlugin *plugin) { const mm_uint16_pair *subsystem_vendor_ids; guint i; if (!mm_filter_check_rule_enabled (self->priv->filter, MM_FILTER_RULE_PLUGIN_ALLOWLIST)) return; subsystem_vendor_ids = mm_plugin_get_allowed_subsystem_vendor_ids (plugin); for (i = 0; subsystem_vendor_ids && subsystem_vendor_ids[i].l; i++) mm_filter_register_plugin_allowlist_subsystem_vendor_id (self->priv->filter, subsystem_vendor_ids[i].l, subsystem_vendor_ids[i].r); } static gboolean track_plugin (MMPluginManager *self, MMPlugin *plugin, GPtrArray **subsystems, GError **error) { const gchar **plugin_subsystems; guint i; /* Ignore plugins that don't specify subsystems */ plugin_subsystems = mm_plugin_get_allowed_subsystems (plugin); if (!plugin_subsystems) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Allowed subsystems not specified"); return FALSE; } /* Process generic plugin */ if (mm_plugin_is_generic (plugin)) { if (self->priv->generic) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Another generic plugin already registered"); return FALSE; } self->priv->generic = g_object_ref (plugin); } else self->priv->plugins = g_list_append (self->priv->plugins, g_object_ref (plugin)); /* Track required subsystems, avoiding duplicates in the list */ for (i = 0; plugin_subsystems[i]; i++) { if (!g_ptr_array_find_with_equal_func (*subsystems, plugin_subsystems[i], g_str_equal, NULL)) g_ptr_array_add (*subsystems, g_strdup (plugin_subsystems[i])); } /* Register plugin allowlist rules in filter, if any */ register_plugin_allowlist_tags (self, plugin); register_plugin_allowlist_vendor_ids (self, plugin); register_plugin_allowlist_product_ids (self, plugin); register_plugin_allowlist_subsystem_vendor_ids (self, plugin); return TRUE; } static gboolean validate_tracked_plugins (MMPluginManager *self, GPtrArray *subsystems_take, GError **error) { g_autofree gchar *subsystems_str = NULL; /* Check the generic plugin once all looped */ if (!self->priv->generic) mm_obj_dbg (self, "generic plugin not loaded"); /* Treat as error if we don't find any plugin */ if (!self->priv->plugins && !self->priv->generic) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_NO_PLUGINS, "no plugins found"); return FALSE; } /* Validate required subsystems */ if (!subsystems_take->len) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_NO_PLUGINS, "empty list of subsystems required by plugins"); return FALSE; } /* Add trailing NULL and store as GStrv */ g_ptr_array_add (subsystems_take, NULL); self->priv->subsystems = (gchar **) g_ptr_array_free (subsystems_take, FALSE); subsystems_str = g_strjoinv (", ", self->priv->subsystems); mm_obj_dbg (self, "successfully loaded %u plugins registering %u subsystems: %s", g_list_length (self->priv->plugins) + !!self->priv->generic, g_strv_length (self->priv->subsystems), subsystems_str); return TRUE; } #if !defined WITH_BUILTIN_PLUGINS static MMPlugin * load_external_plugin (MMPluginManager *self, const gchar *path) { MMPlugin *plugin = NULL; GModule *module; MMPluginCreateFunc plugin_create_func; gint *major_plugin_version; gint *minor_plugin_version; gchar *path_display; /* Get printable UTF-8 string of the path */ path_display = g_filename_display_name (path); module = g_module_open (path, 0); if (!module) { mm_obj_warn (self, "could not load plugin '%s': %s", path_display, g_module_error ()); goto out; } if (!g_module_symbol (module, "mm_plugin_major_version", (gpointer *) &major_plugin_version)) { mm_obj_warn (self, "could not load plugin '%s': Missing major version info", path_display); goto out; } if (*major_plugin_version != MM_PLUGIN_MAJOR_VERSION) { mm_obj_warn (self, "could not load plugin '%s': Plugin major version %d, %d is required", path_display, *major_plugin_version, MM_PLUGIN_MAJOR_VERSION); goto out; } if (!g_module_symbol (module, "mm_plugin_minor_version", (gpointer *) &minor_plugin_version)) { mm_obj_warn (self, "could not load plugin '%s': Missing minor version info", path_display); goto out; } if (*minor_plugin_version != MM_PLUGIN_MINOR_VERSION) { mm_obj_warn (self, "could not load plugin '%s': Plugin minor version %d, %d is required", path_display, *minor_plugin_version, MM_PLUGIN_MINOR_VERSION); goto out; } if (!g_module_symbol (module, "mm_plugin_create", (gpointer *) &plugin_create_func)) { mm_obj_warn (self, "could not load plugin '%s': %s", path_display, g_module_error ()); goto out; } plugin = (*plugin_create_func) (); if (plugin) mm_obj_dbg (self, "loaded plugin '%s' from '%s'", mm_plugin_get_name (plugin), path_display); else mm_obj_warn (self, "could not load plugin '%s': initialization failed", path_display); out: if (module && !plugin) g_module_close (module); g_free (path_display); return plugin; } static void load_external_shared (MMPluginManager *self, const gchar *path) { GModule *module; gchar *path_display; const gchar **shared_name = NULL; gint *major_shared_version; gint *minor_shared_version; gboolean success = FALSE; /* Get printable UTF-8 string of the path */ path_display = g_filename_display_name (path); module = g_module_open (path, 0); if (!module) { mm_obj_warn (self, "could not load shared '%s': %s", path_display, g_module_error ()); goto out; } if (!g_module_symbol (module, "mm_shared_major_version", (gpointer *) &major_shared_version)) { mm_obj_warn (self, "could not load shared '%s': Missing major version info", path_display); goto out; } if (*major_shared_version != MM_SHARED_MAJOR_VERSION) { mm_obj_warn (self, "could not load shared '%s': Shared major version %d, %d is required", path_display, *major_shared_version, MM_SHARED_MAJOR_VERSION); goto out; } if (!g_module_symbol (module, "mm_shared_minor_version", (gpointer *) &minor_shared_version)) { mm_obj_warn (self, "could not load shared '%s': Missing minor version info", path_display); goto out; } if (*minor_shared_version != MM_SHARED_MINOR_VERSION) { mm_obj_warn (self, "could not load shared '%s': Shared minor version %d, %d is required", path_display, *minor_shared_version, MM_SHARED_MINOR_VERSION); goto out; } if (!g_module_symbol (module, "mm_shared_name", (gpointer *) &shared_name)) { mm_obj_warn (self, "could not load shared '%s': Missing name", path_display); goto out; } mm_obj_dbg (self, "loaded shared '%s' utils from '%s'", *shared_name, path_display); success = TRUE; out: if (module && !success) g_module_close (module); g_free (path_display); } static gboolean load_external_plugins (MMPluginManager *self, GError **error) { GDir *dir = NULL; const gchar *fname; GList *shared_paths = NULL; GList *plugin_paths = NULL; GList *l; GPtrArray *subsystems = NULL; g_autofree gchar *plugindir_display = NULL; gboolean valid_plugins = FALSE; if (!g_module_supported ()) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "modules are not supported on your platform!"); goto out; } /* Get printable UTF-8 string of the path */ plugindir_display = g_filename_display_name (self->priv->plugin_dir); mm_obj_dbg (self, "looking for plugins in '%s'", plugindir_display); dir = g_dir_open (self->priv->plugin_dir, 0, NULL); if (!dir) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_NO_PLUGINS, "plugin directory '%s' not found", plugindir_display); goto out; } while ((fname = g_dir_read_name (dir)) != NULL) { if (!g_str_has_suffix (fname, G_MODULE_SUFFIX)) continue; if (g_str_has_prefix (fname, SHARED_PREFIX)) shared_paths = g_list_prepend (shared_paths, g_module_build_path (self->priv->plugin_dir, fname)); else if (g_str_has_prefix (fname, PLUGIN_PREFIX)) plugin_paths = g_list_prepend (plugin_paths, g_module_build_path (self->priv->plugin_dir, fname)); } /* Load all shared utils */ for (l = shared_paths; l; l = g_list_next (l)) load_external_shared (self, (const gchar *)(l->data)); /* Load all plugins */ subsystems = g_ptr_array_new (); for (l = plugin_paths; l; l = g_list_next (l)) { g_autoptr(MMPlugin) plugin = NULL; g_autoptr(GError) inner_error = NULL; plugin = load_external_plugin (self, (const gchar *)(l->data)); if (!plugin) continue; if (!track_plugin (self, plugin, &subsystems, &inner_error)) mm_obj_warn (self, "ignored plugin '%s': %s", mm_plugin_get_name (plugin), inner_error->message); } valid_plugins = validate_tracked_plugins (self, subsystems, error); out: g_list_free_full (shared_paths, g_free); g_list_free_full (plugin_paths, g_free); if (dir) g_dir_close (dir); return valid_plugins; } #else static gboolean load_builtin_plugins (MMPluginManager *self, GError **error) { GList *builtin_plugins; GList *l; GPtrArray *subsystems = NULL; subsystems = g_ptr_array_new (); builtin_plugins = mm_builtin_plugins_load (); for (l = builtin_plugins; l; l = g_list_next (l)) { gboolean tracked; MMPlugin *plugin; plugin = MM_PLUGIN (l->data); mm_obj_dbg (self, "loaded builtin plugin '%s'", mm_plugin_get_name (plugin)); tracked = track_plugin (self, plugin, &subsystems, NULL); g_assert (tracked); } g_list_free_full (builtin_plugins, (GDestroyNotify)g_object_unref); return validate_tracked_plugins (self, subsystems, error); } #endif /* !WITH_BUILTIN_PLUGINS */ /*****************************************************************************/ static gchar * log_object_build_id (MMLogObject *_self) { return g_strdup ("plugin-manager"); } /*****************************************************************************/ MMPluginManager * mm_plugin_manager_new (MMFilter *filter, #if !defined WITH_BUILTIN_PLUGINS const gchar *plugin_dir, #endif GError **error) { return g_initable_new (MM_TYPE_PLUGIN_MANAGER, NULL, error, MM_PLUGIN_MANAGER_FILTER, filter, #if !defined WITH_BUILTIN_PLUGINS MM_PLUGIN_MANAGER_PLUGIN_DIR, plugin_dir, #endif NULL); } static void mm_plugin_manager_init (MMPluginManager *self) { /* Initialize opaque pointer to private data */ self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, MM_TYPE_PLUGIN_MANAGER, MMPluginManagerPrivate); } static void set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { MMPluginManagerPrivate *priv = MM_PLUGIN_MANAGER (object)->priv; switch (prop_id) { #if !defined WITH_BUILTIN_PLUGINS case PROP_PLUGIN_DIR: g_free (priv->plugin_dir); priv->plugin_dir = g_value_dup_string (value); break; #endif case PROP_FILTER: priv->filter = g_value_dup_object (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) { MMPluginManagerPrivate *priv = MM_PLUGIN_MANAGER (object)->priv; switch (prop_id) { #if !defined WITH_BUILTIN_PLUGINS case PROP_PLUGIN_DIR: g_value_set_string (value, priv->plugin_dir); break; #endif case PROP_FILTER: g_value_set_object (value, priv->filter); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static gboolean initable_init (GInitable *initable, GCancellable *cancellable, GError **error) { #if defined WITH_BUILTIN_PLUGINS return load_builtin_plugins (MM_PLUGIN_MANAGER (initable), error); #else /* Load the list of plugins from the filesystem*/ return load_external_plugins (MM_PLUGIN_MANAGER (initable), error); #endif } static void dispose (GObject *object) { MMPluginManager *self = MM_PLUGIN_MANAGER (object); g_list_free_full (g_steal_pointer (&self->priv->plugins), g_object_unref); g_clear_object (&self->priv->generic); g_clear_object (&self->priv->filter); g_clear_pointer (&self->priv->subsystems, g_strfreev); #if !defined WITH_BUILTIN_PLUGINS g_clear_pointer (&self->priv->plugin_dir, g_free); #endif G_OBJECT_CLASS (mm_plugin_manager_parent_class)->dispose (object); } static void initable_iface_init (GInitableIface *iface) { iface->init = initable_init; } static void log_object_iface_init (MMLogObjectInterface *iface) { iface->build_id = log_object_build_id; } static void mm_plugin_manager_class_init (MMPluginManagerClass *manager_class) { GObjectClass *object_class = G_OBJECT_CLASS (manager_class); g_type_class_add_private (object_class, sizeof (MMPluginManagerPrivate)); /* Virtual methods */ object_class->dispose = dispose; object_class->set_property = set_property; object_class->get_property = get_property; /* Properties */ #if !defined WITH_BUILTIN_PLUGINS g_object_class_install_property (object_class, PROP_PLUGIN_DIR, g_param_spec_string (MM_PLUGIN_MANAGER_PLUGIN_DIR, "Plugin directory", "Where to look for plugins", NULL, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); #endif g_object_class_install_property (object_class, PROP_FILTER, g_param_spec_object (MM_PLUGIN_MANAGER_FILTER, "Filter", "Device filter", MM_TYPE_FILTER, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); } ModemManager-1.23.4-dev/src/mm-plugin-manager.h000066400000000000000000000071131456466623000212040ustar00rootroot00000000000000/* -*- 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 - 2011 Red Hat, Inc. * Copyright (C) 2011 Aleksander Morgado */ #ifndef MM_PLUGIN_MANAGER_H #define MM_PLUGIN_MANAGER_H #include #include "mm-device.h" #include "mm-plugin.h" #include "mm-filter.h" #include "mm-base-modem.h" #define MM_TYPE_PLUGIN_MANAGER (mm_plugin_manager_get_type ()) #define MM_PLUGIN_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_PLUGIN_MANAGER, MMPluginManager)) #define MM_PLUGIN_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_PLUGIN_MANAGER, MMPluginManagerClass)) #define MM_IS_PLUGIN_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_PLUGIN_MANAGER)) #define MM_IS_PLUGIN_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), MM_TYPE_PLUGIN_MANAGER)) #define MM_PLUGIN_MANAGER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_PLUGIN_MANAGER, MMPluginManagerClass)) #if !defined WITH_BUILTIN_PLUGINS # define MM_PLUGIN_MANAGER_PLUGIN_DIR "plugin-dir" /* Construct-only */ #endif #define MM_PLUGIN_MANAGER_FILTER "filter" /* Construct-only */ typedef struct _MMPluginManager MMPluginManager; typedef struct _MMPluginManagerClass MMPluginManagerClass; typedef struct _MMPluginManagerPrivate MMPluginManagerPrivate; struct _MMPluginManager { GObject parent; MMPluginManagerPrivate *priv; }; struct _MMPluginManagerClass { GObjectClass parent; }; GType mm_plugin_manager_get_type (void); G_DEFINE_AUTOPTR_CLEANUP_FUNC (MMPluginManager, g_object_unref) MMPluginManager *mm_plugin_manager_new (MMFilter *filter, #if !defined WITH_BUILTIN_PLUGINS const gchar *plugindir, #endif GError **error); void mm_plugin_manager_device_support_check (MMPluginManager *self, MMDevice *device, GAsyncReadyCallback callback, gpointer user_data); gboolean mm_plugin_manager_device_support_check_cancel (MMPluginManager *self, MMDevice *device); MMPlugin * mm_plugin_manager_device_support_check_finish (MMPluginManager *self, GAsyncResult *res, GError **error); MMPlugin *mm_plugin_manager_peek_plugin (MMPluginManager *self, const gchar *plugin_name); const gchar **mm_plugin_manager_get_subsystems (MMPluginManager *self); #endif /* MM_PLUGIN_MANAGER_H */ ModemManager-1.23.4-dev/src/mm-plugin.c000066400000000000000000002021371456466623000175720ustar00rootroot00000000000000/* -*- 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 - 2012 Red Hat, Inc. * Copyright (C) 2012 Google, Inc. */ #define _GNU_SOURCE /* for strcasestr */ #include #include #include #include #include #include #include #include "mm-plugin.h" #include "mm-device.h" #include "mm-kernel-device.h" #include "mm-kernel-device-generic.h" #include "mm-port-serial-at.h" #include "mm-port-serial-qcdm.h" #include "mm-serial-parsers.h" #include "mm-private-boxed-types.h" #include "mm-log-object.h" #include "mm-daemon-enums-types.h" #if defined WITH_QMI # include "mm-broadband-modem-qmi.h" #endif #if defined WITH_MBIM # include "mm-broadband-modem-mbim.h" #endif static void log_object_iface_init (MMLogObjectInterface *iface); G_DEFINE_TYPE_EXTENDED (MMPlugin, mm_plugin, G_TYPE_OBJECT, 0, G_IMPLEMENT_INTERFACE (MM_TYPE_LOG_OBJECT, log_object_iface_init)) /* Virtual port corresponding to the embedded modem */ static const gchar *virtual_port[] = {"smd0", NULL}; #define HAS_POST_PROBING_FILTERS(self) \ (self->priv->vendor_strings || \ self->priv->product_strings || \ self->priv->forbidden_product_strings || \ self->priv->allowed_icera || \ self->priv->forbidden_icera || \ self->priv->allowed_xmm || \ self->priv->forbidden_xmm || \ self->priv->custom_init) struct _MMPluginPrivate { gchar *name; GHashTable *tasks; gboolean is_generic; /* Pre-probing filters */ gchar **subsystems; gchar **drivers; gchar **forbidden_drivers; guint16 *vendor_ids; mm_uint16_pair *product_ids; mm_uint16_pair *subsystem_vendor_ids; mm_uint16_pair *forbidden_product_ids; gchar **udev_tags; /* Post probing filters */ gchar **vendor_strings; mm_str_pair *product_strings; mm_str_pair *forbidden_product_strings; gboolean allowed_icera; gboolean forbidden_icera; gboolean allowed_xmm; gboolean forbidden_xmm; /* Probing setup */ gboolean at; gboolean single_at; gboolean qcdm; gboolean qcdm_required; gboolean qmi; gboolean mbim; gboolean icera_probe; gboolean xmm_probe; MMPortProbeAtCommand *custom_at_probe; guint64 send_delay; gboolean remove_echo; gboolean send_lf; /* Probing setup and/or post-probing filter. * Plugins may use this method to decide whether they support a given * port or not, so should also be considered kind of post-probing filter. */ MMAsyncMethod *custom_init; }; enum { PROP_0, PROP_NAME, PROP_IS_GENERIC, PROP_ALLOWED_SUBSYSTEMS, PROP_ALLOWED_DRIVERS, PROP_FORBIDDEN_DRIVERS, PROP_ALLOWED_VENDOR_IDS, PROP_ALLOWED_PRODUCT_IDS, PROP_ALLOWED_SUBSYSTEM_VENDOR_IDS, PROP_FORBIDDEN_PRODUCT_IDS, PROP_ALLOWED_VENDOR_STRINGS, PROP_ALLOWED_PRODUCT_STRINGS, PROP_FORBIDDEN_PRODUCT_STRINGS, PROP_ALLOWED_UDEV_TAGS, PROP_ALLOWED_AT, PROP_ALLOWED_SINGLE_AT, PROP_ALLOWED_QCDM, PROP_REQUIRED_QCDM, PROP_ALLOWED_QMI, PROP_ALLOWED_MBIM, PROP_ICERA_PROBE, PROP_ALLOWED_ICERA, PROP_FORBIDDEN_ICERA, PROP_XMM_PROBE, PROP_ALLOWED_XMM, PROP_FORBIDDEN_XMM, PROP_CUSTOM_AT_PROBE, PROP_CUSTOM_INIT, PROP_SEND_DELAY, PROP_REMOVE_ECHO, PROP_SEND_LF, LAST_PROP }; /*****************************************************************************/ const gchar * mm_plugin_get_name (MMPlugin *self) { return self->priv->name; } const gchar ** mm_plugin_get_allowed_subsystems (MMPlugin *self) { return (const gchar **) self->priv->subsystems; } const gchar ** mm_plugin_get_allowed_udev_tags (MMPlugin *self) { return (const gchar **) self->priv->udev_tags; } const guint16 * mm_plugin_get_allowed_vendor_ids (MMPlugin *self) { return self->priv->vendor_ids; } const mm_uint16_pair * mm_plugin_get_allowed_product_ids (MMPlugin *self) { return self->priv->product_ids; } const mm_uint16_pair * mm_plugin_get_allowed_subsystem_vendor_ids (MMPlugin *self) { return self->priv->subsystem_vendor_ids; } gboolean mm_plugin_is_generic (MMPlugin *self) { return self->priv->is_generic; } /*****************************************************************************/ static gboolean device_file_exists (const char *name) { char *devfile; struct stat s; int result; devfile = g_strdup_printf ("/dev/%s", name); result = stat (devfile, &s); g_free (devfile); return (0 == result) ? TRUE : FALSE; } static gboolean is_virtual_port (const gchar *device_name) { guint idx; /* Detect any modems accessible through the list of virtual ports */ for (idx = 0; virtual_port[idx]; idx++) { if (strcmp (device_name, virtual_port[idx])) continue; if (!device_file_exists (virtual_port[idx])) continue; return TRUE; } return FALSE; } /* Returns TRUE if the support check request was filtered out */ static gboolean apply_subsystem_filter (MMPlugin *self, MMKernelDevice *port) { if (self->priv->subsystems) { const gchar *subsys; guint i; subsys = mm_kernel_device_get_subsystem (port); for (i = 0; self->priv->subsystems[i]; i++) { if (g_str_equal (subsys, self->priv->subsystems[i])) break; } /* If we didn't match any subsystem: unsupported */ if (!self->priv->subsystems[i]) return TRUE; } return FALSE; } /* Returns TRUE if the support check request was filtered out */ static gboolean apply_pre_probing_filters (MMPlugin *self, MMDevice *device, MMKernelDevice *port, gboolean *need_vendor_probing, gboolean *need_product_probing) { guint16 vendor; guint16 product; guint16 subsystem_vendor; gboolean product_filtered = FALSE; gboolean vendor_filtered = FALSE; gboolean subsystem_vendor_filtered = FALSE; guint i; *need_vendor_probing = FALSE; *need_product_probing = FALSE; /* The plugin may specify that only some subsystems are supported. If that * is the case, filter by subsystem */ if (apply_subsystem_filter (self, port)) { mm_obj_dbg (self, "port %s filtered by subsystem", mm_kernel_device_get_name (port)); return TRUE; } /* The plugin may specify that only some drivers are supported, or that some * drivers are not supported. If that is the case, filter by driver. * * The QMI and MBIM *forbidden* drivers filter is implicit. This is, if the * plugin doesn't explicitly specify that QMI is allowed and we find a QMI * port, the plugin will filter the device. Same for MBIM. * * The opposite, though, is not applicable. If the plugin specifies that QMI * is allowed, we won't take that as a mandatory requirement to look for the * QMI driver (as the plugin may handle non-QMI modems as well) */ if (self->priv->drivers || self->priv->forbidden_drivers || !self->priv->qmi || !self->priv->mbim) { static const gchar *virtual_drivers [] = { "virtual", NULL }; const gchar **drivers; /* Detect any modems accessible through the list of virtual ports */ drivers = (is_virtual_port (mm_kernel_device_get_name (port)) ? virtual_drivers : mm_device_get_drivers (device)); /* If error retrieving driver: unsupported */ if (!drivers) { mm_obj_dbg (self, "port %s filtered as couldn't retrieve drivers", mm_kernel_device_get_name (port)); return TRUE; } /* Filtering by allowed drivers */ if (self->priv->drivers) { gboolean found = FALSE; for (i = 0; self->priv->drivers[i] && !found; i++) { guint j; for (j = 0; drivers[j] && !found; j++) { if (g_str_equal (drivers[j], self->priv->drivers[i])) found = TRUE; } } /* If we didn't match any driver: unsupported */ if (!found) { mm_obj_dbg (self, "port %s filtered by drivers", mm_kernel_device_get_name (port)); return TRUE; } } /* Filtering by forbidden drivers */ if (self->priv->forbidden_drivers) { for (i = 0; self->priv->forbidden_drivers[i]; i++) { guint j; for (j = 0; drivers[j]; j++) { /* If we match a forbidden driver: unsupported */ if (g_str_equal (drivers[j], self->priv->forbidden_drivers[i])) { mm_obj_dbg (self, "port %s filtered by forbidden drivers", mm_kernel_device_get_name (port)); return TRUE; } } } } /* Implicit filter for forbidden QMI driver */ if (!self->priv->qmi) { guint j; for (j = 0; drivers[j]; j++) { /* If we match the QMI driver: unsupported */ if (g_str_equal (drivers[j], "qmi_wwan")) { mm_obj_dbg (self, "port %s filtered by implicit QMI driver", mm_kernel_device_get_name (port)); return TRUE; } } } /* Implicit filter for forbidden MBIM driver */ if (!self->priv->mbim) { guint j; for (j = 0; drivers[j]; j++) { /* If we match the MBIM driver: unsupported */ if (g_str_equal (drivers[j], "cdc_mbim")) { mm_obj_dbg (self, "port %s filtered by implicit MBIM driver", mm_kernel_device_get_name (port)); return TRUE; } } } } vendor = mm_device_get_vendor (device); product = mm_device_get_product (device); subsystem_vendor = mm_device_get_subsystem_vendor (device); /* The plugin may specify that only some vendor IDs are supported. If that * is the case, filter by vendor ID. */ if (self->priv->vendor_ids) { /* If we didn't get any vendor: filtered */ if (!vendor) vendor_filtered = TRUE; else { for (i = 0; self->priv->vendor_ids[i]; i++) if (vendor == self->priv->vendor_ids[i]) break; /* If we didn't match any vendor: filtered */ if (!self->priv->vendor_ids[i]) vendor_filtered = TRUE; } } /* The plugin may specify that only some product IDs are supported. If * that is the case, filter by vendor+product ID pair */ if (self->priv->product_ids) { /* If we didn't get any product: filtered */ if (!product || !vendor) product_filtered = TRUE; else { for (i = 0; self->priv->product_ids[i].l; i++) if (vendor == self->priv->product_ids[i].l && product == self->priv->product_ids[i].r) break; /* If we didn't match any product: filtered */ if (!self->priv->product_ids[i].l) product_filtered = TRUE; } /* When both vendor ids and product ids are given, it may be the case that * we're allowing a full VID1 and only a subset of another VID2, so try to * handle that properly. */ if (vendor_filtered && !product_filtered) vendor_filtered = FALSE; if (product_filtered && self->priv->vendor_ids && !vendor_filtered) product_filtered = FALSE; } /* The plugin may specify that a set of vendor IDs is valid only when going * with a specific subsystem vendor IDs (PCI modems). * If that is the case, filter by vendor+subsystem vendor ID pair */ if (subsystem_vendor && self->priv->subsystem_vendor_ids) { for (i = 0; self->priv->subsystem_vendor_ids[i].l; i++) if (vendor == self->priv->subsystem_vendor_ids[i].l && subsystem_vendor == self->priv->subsystem_vendor_ids[i].r) { /* If device was filtered by vendor, we override that value, since * we want to give priority to vendor/subsystem vendor match */ vendor_filtered = FALSE; break; } /* If we didn't match any vendor/subsystem vendor: filtered */ if (!self->priv->subsystem_vendor_ids[i].l) subsystem_vendor_filtered = TRUE; } /* If we got filtered by vendor/product/subsystem IDs; mark it as unsupported only if: * a) we do not have vendor or product strings to compare with (i.e. plugin * doesn't have explicit vendor/product strings * b) the port is NOT an AT port which we can use for AT probing */ if ((vendor_filtered || product_filtered || subsystem_vendor_filtered) && ((!self->priv->vendor_strings && !self->priv->product_strings && !self->priv->forbidden_product_strings) || g_str_equal (mm_kernel_device_get_subsystem (port), "net") || g_str_has_prefix (mm_kernel_device_get_name (port), "cdc-wdm"))) { mm_obj_dbg (self, "port %s filtered by vendor/product IDs", mm_kernel_device_get_name (port)); return TRUE; } /* The plugin may specify that some product IDs are not supported. If * that is the case, filter by forbidden vendor+product ID pair */ if (self->priv->forbidden_product_ids && product && vendor) { for (i = 0; self->priv->forbidden_product_ids[i].l; i++) { if (vendor == self->priv->forbidden_product_ids[i].l && product == self->priv->forbidden_product_ids[i].r) { mm_obj_dbg (self, "port %s filtered by forbidden vendor/product IDs", mm_kernel_device_get_name (port)); return TRUE; } } } /* Check if we need vendor/product string probing * Only require these probings if the corresponding filters are given, and: * 1) if there was no vendor/product ID probing * 2) if there was vendor/product ID probing but we got filtered * * In other words, don't require vendor/product string probing if the plugin * already had vendor/product ID filters and we actually passed those. */ if ((!self->priv->vendor_ids && !self->priv->product_ids) || vendor_filtered || product_filtered || subsystem_vendor_filtered) { /* If product strings related filters around, we need to probe for both * vendor and product strings */ if (self->priv->product_strings || self->priv->forbidden_product_strings) { *need_vendor_probing = TRUE; *need_product_probing = TRUE; } /* If only vendor string filter is needed, only probe for vendor string */ else if (self->priv->vendor_strings) *need_vendor_probing = TRUE; } /* The plugin may specify that only ports with some given udev tags are * supported. If that is the case, filter by udev tag */ if (self->priv->udev_tags) { for (i = 0; self->priv->udev_tags[i]; i++) { /* Check if the port or device was tagged */ if (mm_kernel_device_get_global_property_as_boolean (port, self->priv->udev_tags[i])) break; } /* If we didn't match any udev tag: unsupported */ if (!self->priv->udev_tags[i]) { mm_obj_dbg (self, "port %s filtered by udev tags", mm_kernel_device_get_name (port)); return TRUE; } } return FALSE; } /* Returns TRUE if the support check request was filtered out */ static gboolean apply_post_probing_filters (MMPlugin *self, MMPortProbeFlag flags, MMPortProbe *probe) { gboolean vendor_filtered = FALSE; guint i; /* The plugin may specify that only some vendor strings are supported. If * that is the case, filter by vendor string. */ if ((flags & MM_PORT_PROBE_AT_VENDOR) && self->priv->vendor_strings) { const gchar *vendor; vendor = mm_port_probe_get_vendor (probe); /* If we didn't get any vendor: filtered */ if (!vendor) vendor_filtered = TRUE; else { for (i = 0; self->priv->vendor_strings[i]; i++) { gboolean found; gchar *casefolded; casefolded = g_utf8_casefold (self->priv->vendor_strings[i], -1); found = !!strstr (vendor, casefolded); g_free (casefolded); if (found) break; } /* If we didn't match any vendor: filtered */ if (!self->priv->vendor_strings[i]) vendor_filtered = TRUE; } if (vendor_filtered) { if (!self->priv->product_strings) { mm_obj_dbg (self, "port %s filtered by vendor strings", mm_port_probe_get_port_name (probe)); return TRUE; } } else /* Vendor matched */ return FALSE; } /* The plugin may specify that only some vendor+product string pairs are * supported or unsupported. If that is the case, filter by product * string */ if ((flags & MM_PORT_PROBE_AT_PRODUCT) && (self->priv->product_strings || self->priv->forbidden_product_strings)) { const gchar *vendor; const gchar *product; vendor = mm_port_probe_get_vendor (probe); product = mm_port_probe_get_product (probe); if (self->priv->product_strings) { /* If we didn't get any vendor or product: filtered */ if (!vendor || !product) { mm_obj_dbg (self, "port %s filtered as no vendor/product strings given", mm_port_probe_get_port_name (probe)); return TRUE; } else { for (i = 0; self->priv->product_strings[i].l; i++) { gboolean found; gchar *casefolded_vendor; gchar *casefolded_product; casefolded_vendor = g_utf8_casefold (self->priv->product_strings[i].l, -1); casefolded_product = g_utf8_casefold (self->priv->product_strings[i].r, -1); found = (!!strstr (vendor, casefolded_vendor) && !!strstr (product, casefolded_product)); g_free (casefolded_vendor); g_free (casefolded_product); if (found) break; } /* If we didn't match any product: unsupported */ if (!self->priv->product_strings[i].l) { mm_obj_dbg (self, "port %s filtered by vendor/product strings", mm_port_probe_get_port_name (probe)); return TRUE; } } } if (self->priv->forbidden_product_strings && vendor && product) { for (i = 0; self->priv->forbidden_product_strings[i].l; i++) { gboolean found; gchar *casefolded_vendor; gchar *casefolded_product; casefolded_vendor = g_utf8_casefold (self->priv->forbidden_product_strings[i].l, -1); casefolded_product = g_utf8_casefold (self->priv->forbidden_product_strings[i].r, -1); found = (!!strstr (vendor, casefolded_vendor) && !!strstr (product, casefolded_product)); g_free (casefolded_vendor); g_free (casefolded_product); if (found) { /* If we match a forbidden product: unsupported */ mm_obj_dbg (self, "port %s filtered by forbidden vendor/product strings", mm_port_probe_get_port_name (probe)); return TRUE; } } } /* Keep on with next filters */ } /* The plugin may specify that only Icera-based modems are supported. * If that is the case, filter by allowed Icera support */ if (self->priv->allowed_icera && !mm_port_probe_is_icera (probe)) { /* Unsupported! */ mm_obj_dbg (self, "port %s filtered as modem is not icera", mm_port_probe_get_port_name (probe)); return TRUE; } /* The plugin may specify that Icera-based modems are NOT supported. * If that is the case, filter by forbidden Icera support */ if (self->priv->forbidden_icera && mm_port_probe_is_icera (probe)) { /* Unsupported! */ mm_obj_dbg (self, "port %s filtered as modem is icera", mm_port_probe_get_port_name (probe)); return TRUE; } /* The plugin may specify that only Xmm-based modems are supported. * If that is the case, filter by allowed Xmm support */ if (self->priv->allowed_xmm && !mm_port_probe_is_xmm (probe)) { /* Unsupported! */ mm_obj_dbg (self, "port %s filtered as modem is not XMM", mm_port_probe_get_port_name (probe)); return TRUE; } /* The plugin may specify that Xmm-based modems are NOT supported. * If that is the case, filter by forbidden Xmm support */ if (self->priv->forbidden_xmm && mm_port_probe_is_xmm (probe)) { /* Unsupported! */ mm_obj_dbg (self, "port %s filtered as modem is XMM", mm_port_probe_get_port_name (probe)); return TRUE; } return FALSE; } /* Context for the asynchronous probing operation */ typedef struct { MMPlugin *self; MMDevice *device; MMPortProbeFlag flags; } PortProbeRunContext; static void port_probe_run_context_free (PortProbeRunContext *ctx) { g_object_unref (ctx->device); g_object_unref (ctx->self); g_slice_free (PortProbeRunContext, ctx); } static void port_probe_run_ready (MMPortProbe *probe, GAsyncResult *probe_result, GTask *task) { GError *error = NULL; MMPluginSupportsResult result = MM_PLUGIN_SUPPORTS_PORT_UNKNOWN; PortProbeRunContext *ctx; if (!mm_port_probe_run_finish (probe, probe_result, &error)) { /* Probing failed saying the port is unsupported. This is not to be * treated as a generic error, the plugin is just telling us as nicely * as it can that the port is not supported, so don't warn these cases. */ if (g_error_matches (error, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED)) { result = MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED; goto out; } /* Probing failed but the plugin tells us to retry; so we'll defer the * probing a bit */ if (g_error_matches (error, MM_CORE_ERROR, MM_CORE_ERROR_RETRY)) { result = MM_PLUGIN_SUPPORTS_PORT_DEFER; goto out; } /* For remaining errors, just propagate them */ g_task_return_error (task, error); g_object_unref (task); return; } /* Probing succeeded, recover context */ ctx = g_task_get_task_data (task); /* Apply post probing filters */ if (!apply_post_probing_filters (ctx->self, ctx->flags, probe)) { /* Port is supported! */ result = MM_PLUGIN_SUPPORTS_PORT_SUPPORTED; /* If we were looking for AT ports, and the port is AT, * and we were told that only one AT port is expected, cancel AT * probings in the other available support tasks of the SAME * device. */ if (ctx->self->priv->single_at && ctx->flags & MM_PORT_PROBE_AT && mm_port_probe_is_at (probe)) { GList *l; for (l = mm_device_peek_port_probe_list (ctx->device); l; l = g_list_next (l)) { if (l->data != probe) mm_port_probe_run_cancel_at_probing (MM_PORT_PROBE (l->data)); } } } else /* Filtered by post probing filters */ result = MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED; out: /* Complete action */ g_clear_error (&error); g_task_return_int (task, result); g_object_unref (task); } G_STATIC_ASSERT (MM_PLUGIN_SUPPORTS_PORT_UNKNOWN == -1); MMPluginSupportsResult mm_plugin_supports_port_finish (MMPlugin *self, GAsyncResult *result, GError **error) { GError *inner_error = NULL; gssize value; g_return_val_if_fail (MM_IS_PLUGIN (self), MM_PLUGIN_SUPPORTS_PORT_UNKNOWN); g_return_val_if_fail (G_IS_TASK (result), MM_PLUGIN_SUPPORTS_PORT_UNKNOWN); value = g_task_propagate_int (G_TASK (result), &inner_error); if (inner_error) { g_propagate_error (error, inner_error); return MM_PLUGIN_SUPPORTS_PORT_UNKNOWN; } return (MMPluginSupportsResult)value; } void mm_plugin_supports_port (MMPlugin *self, MMDevice *device, MMKernelDevice *port, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { MMPortProbe *probe = NULL; GTask *task; PortProbeRunContext *ctx; gboolean need_vendor_probing; gboolean need_product_probing; MMPortProbeFlag subsystem_expected_flags; MMPortProbeFlag plugin_expected_flags; MMPortProbeFlag probe_run_flags; gchar *probe_list_str; g_return_if_fail (MM_IS_PLUGIN (self)); g_return_if_fail (MM_IS_DEVICE (device)); g_return_if_fail (MM_IS_KERNEL_DEVICE (port)); /* Create new cancellable task */ task = g_task_new (self, cancellable, callback, user_data); /* Apply filters before launching the probing */ if (apply_pre_probing_filters (self, device, port, &need_vendor_probing, &need_product_probing)) { /* Filtered! */ g_task_return_int (task, MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED); g_object_unref (task); return; } /* Need to launch new probing */ probe = MM_PORT_PROBE (mm_device_peek_port_probe (device, port)); if (!probe) { /* This may happen if the ports get removed from the device while * probing is ongoing */ g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "(%s) Missing port probe for port (%s/%s)", self->priv->name, mm_kernel_device_get_subsystem (port), mm_kernel_device_get_name (port)); g_object_unref (task); return; } /* Build mask of flags based on subsystem */ subsystem_expected_flags = MM_PORT_PROBE_NONE; if (g_str_equal (mm_kernel_device_get_subsystem (port), "tty")) subsystem_expected_flags |= (MM_PORT_PROBE_AT | MM_PORT_PROBE_QCDM); else if (g_str_equal (mm_kernel_device_get_subsystem (port), "usbmisc")) subsystem_expected_flags |= (MM_PORT_PROBE_QMI | MM_PORT_PROBE_MBIM | MM_PORT_PROBE_AT); else if (g_str_equal (mm_kernel_device_get_subsystem (port), "rpmsg")) subsystem_expected_flags |= (MM_PORT_PROBE_AT | MM_PORT_PROBE_QMI); else if (g_str_equal (mm_kernel_device_get_subsystem (port), "wwan")) subsystem_expected_flags |= (MM_PORT_PROBE_QMI | MM_PORT_PROBE_MBIM | MM_PORT_PROBE_AT | MM_PORT_PROBE_QCDM); #if defined WITH_QRTR else if (g_str_equal (mm_kernel_device_get_subsystem (port), "qrtr")) subsystem_expected_flags |= MM_PORT_PROBE_QMI; #endif /* Build mask of flags based on plugin */ plugin_expected_flags = MM_PORT_PROBE_NONE; if (self->priv->at || self->priv->single_at) plugin_expected_flags |= MM_PORT_PROBE_AT; if (self->priv->qcdm || self->priv->qcdm_required) plugin_expected_flags |= MM_PORT_PROBE_QCDM; if (self->priv->qmi) plugin_expected_flags |= MM_PORT_PROBE_QMI; if (self->priv->mbim) plugin_expected_flags |= MM_PORT_PROBE_MBIM; /* Initial list of probe flags based on plugin and subsystem */ probe_run_flags = subsystem_expected_flags & plugin_expected_flags; /* For potential AT ports, check for more things */ if (probe_run_flags & MM_PORT_PROBE_AT) { if (need_vendor_probing) probe_run_flags |= MM_PORT_PROBE_AT_VENDOR; if (need_product_probing) probe_run_flags |= MM_PORT_PROBE_AT_PRODUCT; if (self->priv->icera_probe || self->priv->allowed_icera || self->priv->forbidden_icera) probe_run_flags |= MM_PORT_PROBE_AT_ICERA; if (self->priv->xmm_probe || self->priv->allowed_xmm || self->priv->forbidden_xmm) probe_run_flags |= MM_PORT_PROBE_AT_XMM; } /* If no explicit probing was required, just request to grab it without * probing anything. This happens for all net ports and e.g. for cdc-wdm * ports which do not need QMI/MBIM probing. */ if (probe_run_flags == MM_PORT_PROBE_NONE) { mm_obj_dbg (self, "probing of port %s deferred until result suggested", mm_kernel_device_get_name (port)); g_task_return_int (task, MM_PLUGIN_SUPPORTS_PORT_DEFER_UNTIL_SUGGESTED); g_object_unref (task); return; } /* If a modem is already available and the plugin says that only one AT port is * expected, check if we alredy got the single AT port. And if so, we know this * port being probed won't be AT. */ if (self->priv->single_at && mm_port_probe_list_has_at_port (mm_device_peek_port_probe_list (device)) && !mm_port_probe_is_at (probe)) { mm_obj_dbg (self, "not setting up AT probing tasks in port %s: " "modem already has the expected single AT port", mm_kernel_device_get_name (port)); /* Assuming it won't be an AT port. We still run the probe anyway, in * case we need to check for other port types (e.g. QCDM) */ mm_port_probe_set_result_at (probe, FALSE); } /* Setup async call context */ ctx = g_slice_new0 (PortProbeRunContext); ctx->self = g_object_ref (self); ctx->device = g_object_ref (device); ctx->flags = probe_run_flags; /* Store context in task */ g_task_set_task_data (task, ctx, (GDestroyNotify) port_probe_run_context_free); /* Launch the probe */ probe_list_str = mm_port_probe_flag_build_string_from_mask (ctx->flags); mm_obj_dbg (self, "probes required for port %s: '%s'", mm_kernel_device_get_name (port), probe_list_str); g_free (probe_list_str); mm_port_probe_run (probe, ctx->flags, self->priv->send_delay, self->priv->remove_echo, self->priv->send_lf, self->priv->custom_at_probe, self->priv->custom_init, self->priv->qcdm_required, cancellable, (GAsyncReadyCallback) port_probe_run_ready, task); } /*****************************************************************************/ MMPluginSupportsHint mm_plugin_discard_port_early (MMPlugin *self, MMDevice *device, MMKernelDevice *port) { gboolean need_vendor_probing = FALSE; gboolean need_product_probing = FALSE; /* If fully filtered by pre-probing filters, port unsupported */ if (apply_pre_probing_filters (self, device, port, &need_vendor_probing, &need_product_probing)) return MM_PLUGIN_SUPPORTS_HINT_UNSUPPORTED; /* If there are no post-probing filters, this plugin is the only one (except * for the generic one) which will grab the port */ if (!HAS_POST_PROBING_FILTERS (self)) return MM_PLUGIN_SUPPORTS_HINT_SUPPORTED; /* If no vendor/product probing needed, plugin is likely supported */ if (!need_vendor_probing && !need_product_probing) return MM_PLUGIN_SUPPORTS_HINT_LIKELY; /* If vendor/product probing is needed, plugin may be supported */ return MM_PLUGIN_SUPPORTS_HINT_MAYBE; } /*****************************************************************************/ MMBaseModem * mm_plugin_create_modem (MMPlugin *self, MMDevice *device, GError **error) { MMBaseModem *modem; GList *port_probes = NULL; const gchar **virtual_ports = NULL; const gchar **drivers; if (!mm_device_is_virtual (device)) port_probes = mm_device_peek_port_probe_list (device); else virtual_ports = mm_device_virtual_peek_ports (device); drivers = mm_device_get_drivers (device); /* Let the plugin create the modem from the port probe results */ modem = MM_PLUGIN_GET_CLASS (self)->create_modem (MM_PLUGIN (self), mm_device_get_uid (device), mm_device_get_physdev (device), drivers, mm_device_get_vendor (device), mm_device_get_product (device), mm_device_get_subsystem_vendor (device), port_probes, error); if (!modem) return NULL; mm_base_modem_set_hotplugged (modem, mm_device_get_hotplugged (device)); if (port_probes) { GList *l; /* Grab each port */ for (l = port_probes; l; l = g_list_next (l)) { GError *inner_error = NULL; MMPortProbe *probe; gboolean grabbed = FALSE; gboolean force_ignored = FALSE; const gchar *subsys; const gchar *name; const gchar *driver; MMPortType port_type; probe = MM_PORT_PROBE (l->data); subsys = mm_port_probe_get_port_subsys (probe); name = mm_port_probe_get_port_name (probe); port_type = mm_port_probe_get_port_type (probe); driver = mm_kernel_device_get_driver (mm_port_probe_peek_port (probe)); /* If grabbing a port fails, just warn. We'll decide if the modem is * valid or not when all ports get organized */ /* We apply again the subsystem filter, as the port may have been * probed and accepted by the generic plugin, which is overwritten * by the specific one when needed. */ if (apply_subsystem_filter (self, mm_port_probe_peek_port (probe))) { inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "unsupported subsystem: '%s'", subsys); goto next; } /* Ports that are explicitly ignored will be grabbed as ignored */ if (mm_port_probe_is_ignored (probe)) { mm_obj_dbg (self, "port %s is explicitly ignored", name); force_ignored = TRUE; goto grab_port; } /* Force network ignore rules for devices that use qmi_wwan */ if (drivers && g_strv_contains (drivers, "qmi_wwan")) { #if defined WITH_QMI if (MM_IS_BROADBAND_MODEM_QMI (modem) && port_type == MM_PORT_TYPE_NET && g_strcmp0 (driver, "qmi_wwan") != 0) { /* Non-QMI net ports are ignored in QMI modems */ mm_obj_dbg (self, "ignoring non-QMI net port %s in QMI modem", name); force_ignored = TRUE; goto grab_port; } if (!MM_IS_BROADBAND_MODEM_QMI (modem) && port_type == MM_PORT_TYPE_NET && g_strcmp0 (driver, "qmi_wwan") == 0) { /* QMI net ports are ignored in non-QMI modems */ mm_obj_dbg (self, "ignoring QMI net port %s in non-QMI modem", name); force_ignored = TRUE; goto grab_port; } #else if (port_type == MM_PORT_TYPE_NET && g_strcmp0 (driver, "qmi_wwan") == 0) { /* QMI net ports are ignored if QMI support not built */ mm_obj_dbg (self, "ignoring QMI net port %s as QMI support isn't available", name); force_ignored = TRUE; goto grab_port; } #endif } /* Force network ignore rules for devices that use cdc_mbim */ if (drivers && g_strv_contains (drivers, "cdc_mbim")) { #if defined WITH_MBIM if (MM_IS_BROADBAND_MODEM_MBIM (modem) && port_type == MM_PORT_TYPE_NET && g_strcmp0 (driver, "cdc_mbim") != 0) { /* Non-MBIM net ports are ignored in MBIM modems */ mm_obj_dbg (self, "ignoring non-MBIM net port %s in MBIM modem", name); force_ignored = TRUE; goto grab_port; } if (!MM_IS_BROADBAND_MODEM_MBIM (modem) && port_type == MM_PORT_TYPE_NET && g_strcmp0 (driver, "cdc_mbim") == 0) { /* MBIM net ports are ignored in non-MBIM modems */ mm_obj_dbg (self, "ignoring MBIM net port %s in non-MBIM modem", name); force_ignored = TRUE; goto grab_port; } #else if (port_type == MM_PORT_TYPE_NET && g_strcmp0 (driver, "cdc_mbim") == 0) { mm_obj_dbg (self, "ignoring MBIM net port %s as MBIM support isn't available", name); force_ignored = TRUE; goto grab_port; } #endif } grab_port: if (force_ignored) grabbed = mm_base_modem_grab_port (modem, mm_port_probe_peek_port (probe), MM_PORT_TYPE_IGNORED, MM_PORT_SERIAL_AT_FLAG_NONE, &inner_error); else if (MM_PLUGIN_GET_CLASS (self)->grab_port) grabbed = MM_PLUGIN_GET_CLASS (self)->grab_port (MM_PLUGIN (self), modem, probe, &inner_error); else grabbed = mm_base_modem_grab_port (modem, mm_port_probe_peek_port (probe), mm_port_probe_get_port_type (probe), MM_PORT_SERIAL_AT_FLAG_NONE, &inner_error); next: if (!grabbed) { mm_obj_warn (self, "could not grab port %s: %s", name, inner_error ? inner_error->message : "unknown error"); /* An ABORTED error is emitted exclusively when the port grabbing operation * detects that a REQUIRED port is unusable. */ if (g_error_matches (inner_error, MM_CORE_ERROR, MM_CORE_ERROR_ABORTED)) { g_propagate_error (error, inner_error); g_clear_object (&modem); return NULL; } g_clear_error (&inner_error); } } } else if (virtual_ports) { guint i; for (i = 0; virtual_ports[i]; i++) { GError *inner_error = NULL; MMKernelDevice *kernel_device; MMKernelEventProperties *properties; properties = mm_kernel_event_properties_new (); mm_kernel_event_properties_set_action (properties, "add"); mm_kernel_event_properties_set_subsystem (properties, "virtual"); mm_kernel_event_properties_set_name (properties, virtual_ports[i]); /* Give an empty set of rules, because we don't want them to be * loaded from the udev rules path (as there may not be any * installed yet). */ kernel_device = mm_kernel_device_generic_new_with_rules (properties, NULL, &inner_error); if (!kernel_device) { mm_obj_warn (self, "could not create generic device for virtual port %s: %s", virtual_ports[i], inner_error ? inner_error->message : "unknown error"); g_clear_error (&inner_error); } else if (!mm_base_modem_grab_port (modem, kernel_device, MM_PORT_TYPE_AT, MM_PORT_SERIAL_AT_FLAG_NONE, &inner_error)) { mm_obj_warn (self, "could not grab virtual port %s: %s", virtual_ports[i], inner_error ? inner_error->message : "unknown error"); g_clear_error (&inner_error); } if (kernel_device) g_object_unref (kernel_device); g_object_unref (properties); } } /* If organizing ports fails, consider the modem invalid */ if (!mm_base_modem_organize_ports (modem, error)) g_clear_object (&modem); return modem; } /*****************************************************************************/ static gchar * log_object_build_id (MMLogObject *_self) { MMPlugin *self; g_autofree gchar *plugin_name_lowercase = NULL; self = MM_PLUGIN (_self); plugin_name_lowercase = g_ascii_strdown (self->priv->name, -1); return g_strdup_printf ("plugin/%s", plugin_name_lowercase); } /*****************************************************************************/ static void mm_plugin_init (MMPlugin *self) { self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, MM_TYPE_PLUGIN, MMPluginPrivate); /* Defaults */ self->priv->send_delay = 100000; self->priv->remove_echo = TRUE; self->priv->send_lf = FALSE; } static void set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { MMPlugin *self = MM_PLUGIN (object); switch (prop_id) { case PROP_NAME: /* Construct only */ self->priv->name = g_value_dup_string (value); break; case PROP_IS_GENERIC: /* Construct only */ self->priv->is_generic = g_value_get_boolean (value); break; case PROP_ALLOWED_SUBSYSTEMS: /* Construct only */ self->priv->subsystems = g_value_dup_boxed (value); break; case PROP_ALLOWED_DRIVERS: /* Construct only */ self->priv->drivers = g_value_dup_boxed (value); break; case PROP_FORBIDDEN_DRIVERS: /* Construct only */ self->priv->forbidden_drivers = g_value_dup_boxed (value); break; case PROP_ALLOWED_VENDOR_IDS: /* Construct only */ self->priv->vendor_ids = g_value_dup_boxed (value); break; case PROP_ALLOWED_PRODUCT_IDS: /* Construct only */ self->priv->product_ids = g_value_dup_boxed (value); break; case PROP_ALLOWED_SUBSYSTEM_VENDOR_IDS: /* Construct only */ self->priv->subsystem_vendor_ids = g_value_dup_boxed (value); break; case PROP_FORBIDDEN_PRODUCT_IDS: /* Construct only */ self->priv->forbidden_product_ids = g_value_dup_boxed (value); break; case PROP_ALLOWED_VENDOR_STRINGS: /* Construct only */ self->priv->vendor_strings = g_value_dup_boxed (value); break; case PROP_ALLOWED_PRODUCT_STRINGS: /* Construct only */ self->priv->product_strings = g_value_dup_boxed (value); break; case PROP_FORBIDDEN_PRODUCT_STRINGS: /* Construct only */ self->priv->forbidden_product_strings = g_value_dup_boxed (value); break; case PROP_ALLOWED_UDEV_TAGS: /* Construct only */ self->priv->udev_tags = g_value_dup_boxed (value); break; case PROP_ALLOWED_AT: /* Construct only */ self->priv->at = g_value_get_boolean (value); break; case PROP_ALLOWED_SINGLE_AT: /* Construct only */ self->priv->single_at = g_value_get_boolean (value); break; case PROP_ALLOWED_QCDM: /* Construct only */ self->priv->qcdm = g_value_get_boolean (value); break; case PROP_REQUIRED_QCDM: /* Construct only */ self->priv->qcdm_required = g_value_get_boolean (value); break; case PROP_ALLOWED_QMI: /* Construct only */ self->priv->qmi = g_value_get_boolean (value); break; case PROP_ALLOWED_MBIM: /* Construct only */ self->priv->mbim = g_value_get_boolean (value); break; case PROP_ICERA_PROBE: /* Construct only */ self->priv->icera_probe = g_value_get_boolean (value); break; case PROP_ALLOWED_ICERA: /* Construct only */ self->priv->allowed_icera = g_value_get_boolean (value); break; case PROP_FORBIDDEN_ICERA: /* Construct only */ self->priv->forbidden_icera = g_value_get_boolean (value); break; case PROP_XMM_PROBE: /* Construct only */ self->priv->xmm_probe = g_value_get_boolean (value); break; case PROP_ALLOWED_XMM: /* Construct only */ self->priv->allowed_xmm = g_value_get_boolean (value); break; case PROP_FORBIDDEN_XMM: /* Construct only */ self->priv->forbidden_xmm = g_value_get_boolean (value); break; case PROP_CUSTOM_AT_PROBE: /* Construct only */ self->priv->custom_at_probe = g_value_dup_boxed (value); break; case PROP_CUSTOM_INIT: /* Construct only */ self->priv->custom_init = g_value_dup_boxed (value); break; case PROP_SEND_DELAY: /* Construct only */ self->priv->send_delay = (guint64)g_value_get_uint64 (value); break; case PROP_REMOVE_ECHO: /* Construct only */ self->priv->remove_echo = g_value_get_boolean (value); break; case PROP_SEND_LF: /* Construct only */ self->priv->send_lf = 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) { MMPlugin *self = MM_PLUGIN (object); switch (prop_id) { case PROP_NAME: g_value_set_string (value, self->priv->name); break; case PROP_IS_GENERIC: g_value_set_boolean (value, self->priv->is_generic); break; case PROP_ALLOWED_SUBSYSTEMS: g_value_set_boxed (value, self->priv->subsystems); break; case PROP_ALLOWED_DRIVERS: g_value_set_boxed (value, self->priv->drivers); break; case PROP_FORBIDDEN_DRIVERS: g_value_set_boxed (value, self->priv->forbidden_drivers); break; case PROP_ALLOWED_VENDOR_IDS: g_value_set_boxed (value, self->priv->vendor_ids); break; case PROP_ALLOWED_PRODUCT_IDS: g_value_set_boxed (value, self->priv->product_ids); break; case PROP_ALLOWED_SUBSYSTEM_VENDOR_IDS: g_value_set_boxed (value, self->priv->subsystem_vendor_ids); break; case PROP_FORBIDDEN_PRODUCT_IDS: g_value_set_boxed (value, self->priv->forbidden_product_ids); break; case PROP_ALLOWED_VENDOR_STRINGS: g_value_set_boxed (value, self->priv->vendor_strings); break; case PROP_ALLOWED_PRODUCT_STRINGS: g_value_set_boxed (value, self->priv->product_strings); break; case PROP_FORBIDDEN_PRODUCT_STRINGS: g_value_set_boxed (value, self->priv->forbidden_product_strings); break; case PROP_ALLOWED_AT: g_value_set_boolean (value, self->priv->at); break; case PROP_ALLOWED_SINGLE_AT: g_value_set_boolean (value, self->priv->single_at); break; case PROP_ALLOWED_QCDM: g_value_set_boolean (value, self->priv->qcdm); break; case PROP_REQUIRED_QCDM: g_value_set_boolean (value, self->priv->qcdm_required); break; case PROP_ALLOWED_QMI: g_value_set_boolean (value, self->priv->qmi); break; case PROP_ALLOWED_MBIM: g_value_set_boolean (value, self->priv->mbim); break; case PROP_ALLOWED_UDEV_TAGS: g_value_set_boxed (value, self->priv->udev_tags); break; case PROP_ICERA_PROBE: g_value_set_boolean (value, self->priv->icera_probe); break; case PROP_ALLOWED_ICERA: g_value_set_boolean (value, self->priv->allowed_icera); break; case PROP_FORBIDDEN_ICERA: g_value_set_boolean (value, self->priv->forbidden_icera); break; case PROP_XMM_PROBE: g_value_set_boolean (value, self->priv->xmm_probe); break; case PROP_ALLOWED_XMM: g_value_set_boolean (value, self->priv->allowed_xmm); break; case PROP_FORBIDDEN_XMM: g_value_set_boolean (value, self->priv->forbidden_xmm); break; case PROP_CUSTOM_AT_PROBE: g_value_set_boxed (value, self->priv->custom_at_probe); break; case PROP_CUSTOM_INIT: g_value_set_boxed (value, self->priv->custom_init); break; case PROP_SEND_DELAY: g_value_set_uint64 (value, self->priv->send_delay); break; case PROP_REMOVE_ECHO: g_value_set_boolean (value, self->priv->remove_echo); break; case PROP_SEND_LF: g_value_set_boolean (value, self->priv->send_lf); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void finalize (GObject *object) { MMPlugin *self = MM_PLUGIN (object); g_free (self->priv->name); #define _g_boxed_free0(t,p) if (p) g_boxed_free (t, p) _g_boxed_free0 (G_TYPE_STRV, self->priv->subsystems); _g_boxed_free0 (G_TYPE_STRV, self->priv->drivers); _g_boxed_free0 (G_TYPE_STRV, self->priv->forbidden_drivers); _g_boxed_free0 (MM_TYPE_UINT16_ARRAY, self->priv->vendor_ids); _g_boxed_free0 (MM_TYPE_UINT16_PAIR_ARRAY, self->priv->product_ids); _g_boxed_free0 (MM_TYPE_UINT16_PAIR_ARRAY, self->priv->subsystem_vendor_ids); _g_boxed_free0 (MM_TYPE_UINT16_PAIR_ARRAY, self->priv->forbidden_product_ids); _g_boxed_free0 (G_TYPE_STRV, self->priv->udev_tags); _g_boxed_free0 (G_TYPE_STRV, self->priv->vendor_strings); _g_boxed_free0 (MM_TYPE_STR_PAIR_ARRAY, self->priv->product_strings); _g_boxed_free0 (MM_TYPE_STR_PAIR_ARRAY, self->priv->forbidden_product_strings); _g_boxed_free0 (MM_TYPE_POINTER_ARRAY, self->priv->custom_at_probe); _g_boxed_free0 (MM_TYPE_ASYNC_METHOD, self->priv->custom_init); #undef _g_boxed_free0 G_OBJECT_CLASS (mm_plugin_parent_class)->finalize (object); } static void log_object_iface_init (MMLogObjectInterface *iface) { iface->build_id = log_object_build_id; } static void mm_plugin_class_init (MMPluginClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); g_type_class_add_private (object_class, sizeof (MMPluginPrivate)); /* 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_NAME, "Name", "Name", NULL, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); g_object_class_install_property (object_class, PROP_IS_GENERIC, g_param_spec_boolean (MM_PLUGIN_IS_GENERIC, "Generic", "Whether the plugin is the generic one", FALSE, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); g_object_class_install_property (object_class, PROP_ALLOWED_SUBSYSTEMS, g_param_spec_boxed (MM_PLUGIN_ALLOWED_SUBSYSTEMS, "Allowed subsystems", "List of subsystems this plugin can support, " "should be an array of strings finished with 'NULL'", G_TYPE_STRV, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); g_object_class_install_property (object_class, PROP_ALLOWED_DRIVERS, g_param_spec_boxed (MM_PLUGIN_ALLOWED_DRIVERS, "Allowed drivers", "List of drivers this plugin can support, " "should be an array of strings finished with 'NULL'", G_TYPE_STRV, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); g_object_class_install_property (object_class, PROP_FORBIDDEN_DRIVERS, g_param_spec_boxed (MM_PLUGIN_FORBIDDEN_DRIVERS, "Forbidden drivers", "List of drivers this plugin cannot support, " "should be an array of strings finished with 'NULL'", G_TYPE_STRV, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); g_object_class_install_property (object_class, PROP_ALLOWED_VENDOR_IDS, g_param_spec_boxed (MM_PLUGIN_ALLOWED_VENDOR_IDS, "Allowed vendor IDs", "List of vendor IDs this plugin can support, " "should be an array of guint16 finished with '0'", MM_TYPE_UINT16_ARRAY, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); g_object_class_install_property (object_class, PROP_ALLOWED_PRODUCT_IDS, g_param_spec_boxed (MM_PLUGIN_ALLOWED_PRODUCT_IDS, "Allowed product IDs", "List of vendor+product ID pairs this plugin can support, " "should be an array of mm_uint16_pair finished with '0,0'", MM_TYPE_UINT16_PAIR_ARRAY, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); g_object_class_install_property (object_class, PROP_ALLOWED_SUBSYSTEM_VENDOR_IDS, g_param_spec_boxed (MM_PLUGIN_ALLOWED_SUBSYSTEM_VENDOR_IDS, "Allowed subsystem vendor IDs", "List of vendor+subsystem vendor ID pairs this plugin can support, " "should be an array of mm_uint16_pair finished with '0,0'", MM_TYPE_UINT16_PAIR_ARRAY, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); g_object_class_install_property (object_class, PROP_FORBIDDEN_PRODUCT_IDS, g_param_spec_boxed (MM_PLUGIN_FORBIDDEN_PRODUCT_IDS, "Forbidden product IDs", "List of vendor+product ID pairs this plugin cannot support, " "should be an array of mm_uint16_pair finished with '0,0'", MM_TYPE_UINT16_PAIR_ARRAY, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); g_object_class_install_property (object_class, PROP_ALLOWED_VENDOR_STRINGS, g_param_spec_boxed (MM_PLUGIN_ALLOWED_VENDOR_STRINGS, "Allowed vendor strings", "List of vendor strings this plugin can support, " "should be an array of strings finished with 'NULL'", G_TYPE_STRV, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); g_object_class_install_property (object_class, PROP_ALLOWED_PRODUCT_STRINGS, g_param_spec_boxed (MM_PLUGIN_ALLOWED_PRODUCT_STRINGS, "Allowed product strings", "List of vendor+product string pairs this plugin can support, " "should be an array of mm_str_pair finished with 'NULL,NULL'", MM_TYPE_STR_PAIR_ARRAY, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); g_object_class_install_property (object_class, PROP_FORBIDDEN_PRODUCT_STRINGS, g_param_spec_boxed (MM_PLUGIN_FORBIDDEN_PRODUCT_STRINGS, "Forbidden product strings", "List of vendor+product string pairs this plugin cannot support, " "should be an array of mm_str_pair finished with 'NULL,NULL'", MM_TYPE_STR_PAIR_ARRAY, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); g_object_class_install_property (object_class, PROP_ALLOWED_UDEV_TAGS, g_param_spec_boxed (MM_PLUGIN_ALLOWED_UDEV_TAGS, "Allowed Udev tags", "List of udev tags this plugin may expect, " "should be an array of strings finished with 'NULL'", G_TYPE_STRV, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); g_object_class_install_property (object_class, PROP_ALLOWED_AT, g_param_spec_boolean (MM_PLUGIN_ALLOWED_AT, "Allowed AT", "Whether AT ports are allowed in this plugin", FALSE, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); g_object_class_install_property (object_class, PROP_ALLOWED_SINGLE_AT, g_param_spec_boolean (MM_PLUGIN_ALLOWED_SINGLE_AT, "Allowed single AT", "Whether just a single AT port is allowed in this plugin. ", FALSE, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); g_object_class_install_property (object_class, PROP_ALLOWED_QCDM, g_param_spec_boolean (MM_PLUGIN_ALLOWED_QCDM, "Allowed QCDM", "Whether QCDM ports are allowed in this plugin", FALSE, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); g_object_class_install_property (object_class, PROP_REQUIRED_QCDM, g_param_spec_boolean (MM_PLUGIN_REQUIRED_QCDM, "Required QCDM", "Whether QCDM ports are required in this plugin", FALSE, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); g_object_class_install_property (object_class, PROP_ALLOWED_QMI, g_param_spec_boolean (MM_PLUGIN_ALLOWED_QMI, "Allowed QMI", "Whether QMI ports are allowed in this plugin", FALSE, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); g_object_class_install_property (object_class, PROP_ALLOWED_MBIM, g_param_spec_boolean (MM_PLUGIN_ALLOWED_MBIM, "Allowed MBIM", "Whether MBIM ports are allowed in this plugin", FALSE, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); g_object_class_install_property (object_class, PROP_ICERA_PROBE, g_param_spec_boolean (MM_PLUGIN_ICERA_PROBE, "Icera probe", "Request to probe for Icera support.", FALSE, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); g_object_class_install_property (object_class, PROP_ALLOWED_ICERA, g_param_spec_boolean (MM_PLUGIN_ALLOWED_ICERA, "Allowed Icera", "Whether Icera support is allowed in this plugin.", FALSE, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); g_object_class_install_property (object_class, PROP_FORBIDDEN_ICERA, g_param_spec_boolean (MM_PLUGIN_FORBIDDEN_ICERA, "Allowed Icera", "Whether Icera support is forbidden in this plugin.", FALSE, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); g_object_class_install_property (object_class, PROP_XMM_PROBE, g_param_spec_boolean (MM_PLUGIN_XMM_PROBE, "XMM probe", "Request to probe for XMM support.", FALSE, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); g_object_class_install_property (object_class, PROP_ALLOWED_XMM, g_param_spec_boolean (MM_PLUGIN_ALLOWED_XMM, "Allowed XMM", "Whether XMM support is allowed in this plugin.", FALSE, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); g_object_class_install_property (object_class, PROP_FORBIDDEN_XMM, g_param_spec_boolean (MM_PLUGIN_FORBIDDEN_XMM, "Allowed XMM", "Whether XMM support is forbidden in this plugin.", FALSE, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); g_object_class_install_property (object_class, PROP_CUSTOM_AT_PROBE, g_param_spec_boxed (MM_PLUGIN_CUSTOM_AT_PROBE, "Custom AT Probe", "Custom set of commands to probe for AT support, " "should be an array of MMPortProbeAtCommand structs " "finished with 'NULL'", MM_TYPE_POINTER_ARRAY, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); g_object_class_install_property (object_class, PROP_CUSTOM_INIT, g_param_spec_boxed (MM_PLUGIN_CUSTOM_INIT, "Custom initialization", "Asynchronous method setup which contains the " "custom initializations this plugin needs.", MM_TYPE_ASYNC_METHOD, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); g_object_class_install_property (object_class, PROP_SEND_DELAY, g_param_spec_uint64 (MM_PLUGIN_SEND_DELAY, "Send delay", "Send delay for characters in the AT port, " "in microseconds", 0, G_MAXUINT64, 100000, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); g_object_class_install_property (object_class, PROP_REMOVE_ECHO, g_param_spec_boolean (MM_PLUGIN_REMOVE_ECHO, "Remove echo", "Remove echo out of the AT responses", TRUE, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); g_object_class_install_property (object_class, PROP_SEND_LF, g_param_spec_boolean (MM_PLUGIN_SEND_LF, "Send LF", "Send line-feed at the end of each AT command sent", FALSE, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); } ModemManager-1.23.4-dev/src/mm-plugin.h000066400000000000000000000160071456466623000175760ustar00rootroot00000000000000/* -*- 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 - 2012 Red Hat, Inc. * Copyright (C) 2012 Google, Inc. */ #ifndef MM_PLUGIN_H #define MM_PLUGIN_H #include #include #include #include "mm-base-modem.h" #include "mm-port.h" #include "mm-port-probe.h" #include "mm-device.h" #include "mm-kernel-device.h" #define MM_PLUGIN_MAJOR_VERSION 5 #define MM_PLUGIN_MINOR_VERSION 0 #define MM_SHARED_MAJOR_VERSION 1 #define MM_SHARED_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_PLUGIN_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_PLUGIN, MMPluginClass)) #define MM_IS_PLUGIN(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_PLUGIN)) #define MM_IS_PLUGIN_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_PLUGIN)) #define MM_PLUGIN_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_PLUGIN, MMPluginClass)) #define MM_PLUGIN_NAME "name" #define MM_PLUGIN_IS_GENERIC "is-generic" #define MM_PLUGIN_ALLOWED_SUBSYSTEMS "allowed-subsystems" #define MM_PLUGIN_ALLOWED_DRIVERS "allowed-drivers" #define MM_PLUGIN_FORBIDDEN_DRIVERS "forbidden-drivers" #define MM_PLUGIN_ALLOWED_VENDOR_IDS "allowed-vendor-ids" #define MM_PLUGIN_ALLOWED_PRODUCT_IDS "allowed-product-ids" #define MM_PLUGIN_ALLOWED_SUBSYSTEM_VENDOR_IDS "allowed-subsystem-vendor-ids" #define MM_PLUGIN_FORBIDDEN_PRODUCT_IDS "forbidden-product-ids" #define MM_PLUGIN_ALLOWED_VENDOR_STRINGS "allowed-vendor-strings" #define MM_PLUGIN_ALLOWED_PRODUCT_STRINGS "allowed-product-strings" #define MM_PLUGIN_FORBIDDEN_PRODUCT_STRINGS "forbidden-product-strings" #define MM_PLUGIN_ALLOWED_UDEV_TAGS "allowed-udev-tags" #define MM_PLUGIN_ALLOWED_AT "allowed-at" #define MM_PLUGIN_ALLOWED_SINGLE_AT "allowed-single-at" #define MM_PLUGIN_ALLOWED_QCDM "allowed-qcdm" #define MM_PLUGIN_REQUIRED_QCDM "required-qcdm" #define MM_PLUGIN_ALLOWED_QMI "allowed-qmi" #define MM_PLUGIN_ALLOWED_MBIM "allowed-mbim" #define MM_PLUGIN_ICERA_PROBE "icera-probe" #define MM_PLUGIN_ALLOWED_ICERA "allowed-icera" #define MM_PLUGIN_FORBIDDEN_ICERA "forbidden-icera" #define MM_PLUGIN_XMM_PROBE "xmm-probe" #define MM_PLUGIN_ALLOWED_XMM "allowed-xmm" #define MM_PLUGIN_FORBIDDEN_XMM "forbidden-xmm" #define MM_PLUGIN_CUSTOM_INIT "custom-init" #define MM_PLUGIN_CUSTOM_AT_PROBE "custom-at-probe" #define MM_PLUGIN_SEND_DELAY "send-delay" #define MM_PLUGIN_REMOVE_ECHO "remove-echo" #define MM_PLUGIN_SEND_LF "send-lf" typedef enum { MM_PLUGIN_SUPPORTS_PORT_UNKNOWN = -1, MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED, MM_PLUGIN_SUPPORTS_PORT_DEFER, MM_PLUGIN_SUPPORTS_PORT_DEFER_UNTIL_SUGGESTED, MM_PLUGIN_SUPPORTS_PORT_SUPPORTED } MMPluginSupportsResult; typedef enum { MM_PLUGIN_SUPPORTS_HINT_UNSUPPORTED, MM_PLUGIN_SUPPORTS_HINT_MAYBE, MM_PLUGIN_SUPPORTS_HINT_LIKELY, MM_PLUGIN_SUPPORTS_HINT_SUPPORTED, } MMPluginSupportsHint; typedef struct _MMPlugin MMPlugin; typedef struct _MMPluginClass MMPluginClass; typedef struct _MMPluginPrivate MMPluginPrivate; typedef MMPlugin *(*MMPluginCreateFunc) (void); struct _MMPlugin { GObject parent; MMPluginPrivate *priv; }; struct _MMPluginClass { GObjectClass parent; /* Plugins need to provide a method to create a modem object given * a list of port probes (Mandatory) */ MMBaseModem *(*create_modem) (MMPlugin *self, const gchar *uid, const gchar *physdev, const gchar **drivers, guint16 vendor, guint16 product, guint16 subsystem_vendor, GList *probes, GError **error); /* Plugins need to provide a method to grab independent ports * identified by port probes (Optional) */ gboolean (*grab_port) (MMPlugin *self, MMBaseModem *modem, MMPortProbe *probe, GError **error); }; GType mm_plugin_get_type (void); G_DEFINE_AUTOPTR_CLEANUP_FUNC (MMPlugin, g_object_unref) const gchar *mm_plugin_get_name (MMPlugin *self); const gchar **mm_plugin_get_allowed_subsystems (MMPlugin *self); const gchar **mm_plugin_get_allowed_udev_tags (MMPlugin *self); const guint16 *mm_plugin_get_allowed_vendor_ids (MMPlugin *self); const mm_uint16_pair *mm_plugin_get_allowed_product_ids (MMPlugin *self); const mm_uint16_pair *mm_plugin_get_allowed_subsystem_vendor_ids (MMPlugin *self); gboolean mm_plugin_is_generic (MMPlugin *self); /* This method will run all pre-probing filters, to see if we can discard this * plugin from the probing logic as soon as possible. */ MMPluginSupportsHint mm_plugin_discard_port_early (MMPlugin *self, MMDevice *device, MMKernelDevice *port); void mm_plugin_supports_port (MMPlugin *self, MMDevice *device, MMKernelDevice *port, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); MMPluginSupportsResult mm_plugin_supports_port_finish (MMPlugin *self, GAsyncResult *result, GError **error); MMBaseModem *mm_plugin_create_modem (MMPlugin *self, MMDevice *device, GError **error); #endif /* MM_PLUGIN_H */ ModemManager-1.23.4-dev/src/mm-port-mbim.c000066400000000000000000000725111456466623000202030ustar00rootroot00000000000000/* -*- 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) 2013-2021 Aleksander Morgado */ #include #include #include #if defined WITH_QMI # include #endif #include #include #include "mm-port-mbim.h" #include "mm-port-net.h" #include "mm-log-object.h" G_DEFINE_TYPE (MMPortMbim, mm_port_mbim, MM_TYPE_PORT) enum { SIGNAL_NOTIFICATION, SIGNAL_LAST }; static guint signals[SIGNAL_LAST] = { 0 }; struct _MMPortMbimPrivate { gboolean in_progress; MbimDevice *mbim_device; /* monitoring */ gulong notification_monitoring_id; gulong timeout_monitoring_id; gulong removed_monitoring_id; #if defined WITH_QMI && QMI_MBIM_QMUX_SUPPORTED gboolean qmi_supported; QmiDevice *qmi_device; GList *qmi_clients; #endif }; /*****************************************************************************/ #if defined WITH_QMI && QMI_MBIM_QMUX_SUPPORTED gboolean mm_port_mbim_supports_qmi (MMPortMbim *self) { return !!self->priv->qmi_device; } QmiClient * mm_port_mbim_peek_qmi_client (MMPortMbim *self, QmiService service) { GList *l; for (l = self->priv->qmi_clients; l; l = g_list_next (l)) { QmiClient *qmi_client = QMI_CLIENT (l->data); if (qmi_client_get_service (qmi_client) == service) return qmi_client; } return NULL; } QmiClient * mm_port_mbim_get_qmi_client (MMPortMbim *self, QmiService service) { QmiClient *client; client = mm_port_mbim_peek_qmi_client (self, service); return (client ? g_object_ref (client) : NULL); } gboolean mm_port_mbim_allocate_qmi_client_finish (MMPortMbim *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void allocate_client_ready (QmiDevice *qmi_device, GAsyncResult *res, GTask *task) { MMPortMbim *self; QmiClient *qmi_client; GError *error = NULL; self = g_task_get_source_object (task); qmi_client = qmi_device_allocate_client_finish (qmi_device, res, &error); if (!qmi_client) { g_prefix_error (&error, "Couldn't create QMI client for service '%s': ", qmi_service_get_string ((QmiService) GPOINTER_TO_INT (g_task_get_task_data (task)))); g_task_return_error (task, error); } else { /* Store the client in our internal list */ self->priv->qmi_clients = g_list_prepend (self->priv->qmi_clients, qmi_client); g_task_return_boolean (task, TRUE); } g_object_unref (task); } void mm_port_mbim_allocate_qmi_client (MMPortMbim *self, QmiService service, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; task = g_task_new (self, cancellable, callback, user_data); if (!mm_port_mbim_is_open (self)) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_WRONG_STATE, "Port is closed"); g_object_unref (task); return; } if (!mm_port_mbim_supports_qmi (self)) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "Port doesn't support QMI over MBIM"); g_object_unref (task); return; } if (!!mm_port_mbim_peek_qmi_client (self, service)) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_EXISTS, "Client for service '%s' already allocated", qmi_service_get_string (service)); g_object_unref (task); return; } g_task_set_task_data (task, GINT_TO_POINTER (service), NULL); qmi_device_allocate_client (self->priv->qmi_device, service, QMI_CID_NONE, 10, cancellable, (GAsyncReadyCallback)allocate_client_ready, task); } #endif /*****************************************************************************/ typedef struct { gchar *link_name; guint session_id; } SetupLinkResult; static void setup_link_result_free (SetupLinkResult *ctx) { g_free (ctx->link_name); g_slice_free (SetupLinkResult, ctx); } gchar * mm_port_mbim_setup_link_finish (MMPortMbim *self, GAsyncResult *res, guint *session_id, GError **error) { SetupLinkResult *result; gchar *link_name; result = g_task_propagate_pointer (G_TASK (res), error); if (!result) return NULL; if (session_id) *session_id = result->session_id; link_name = g_steal_pointer (&result->link_name); setup_link_result_free (result); return link_name; } static void device_add_link_ready (MbimDevice *device, GAsyncResult *res, GTask *task) { SetupLinkResult *result; GError *error = NULL; result = g_slice_new0 (SetupLinkResult); result->link_name = mbim_device_add_link_finish (device, res, &result->session_id, &error); if (!result->link_name) { g_prefix_error (&error, "failed to add link for device: "); g_task_return_error (task, error); setup_link_result_free (result); } else g_task_return_pointer (task, result, (GDestroyNotify)setup_link_result_free); g_object_unref (task); } void mm_port_mbim_setup_link (MMPortMbim *self, MMPort *data, const gchar *link_prefix_hint, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; task = g_task_new (self, NULL, callback, user_data); if (!self->priv->mbim_device) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_WRONG_STATE, "Port is not open"); g_object_unref (task); return; } mbim_device_add_link (self->priv->mbim_device, MBIM_DEVICE_SESSION_ID_AUTOMATIC, mm_kernel_device_get_name (mm_port_peek_kernel_device (data)), link_prefix_hint, NULL, (GAsyncReadyCallback) device_add_link_ready, task); } /*****************************************************************************/ gboolean mm_port_mbim_cleanup_link_finish (MMPortMbim *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void device_delete_link_ready (MbimDevice *device, GAsyncResult *res, GTask *task) { GError *error = NULL; if (!mbim_device_delete_link_finish (device, res, &error)) g_task_return_error (task, error); else g_task_return_boolean (task, TRUE); g_object_unref (task); } void mm_port_mbim_cleanup_link (MMPortMbim *self, const gchar *link_name, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; task = g_task_new (self, NULL, callback, user_data); if (!self->priv->mbim_device) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_WRONG_STATE, "Port is not open"); g_object_unref (task); return; } mbim_device_delete_link (self->priv->mbim_device, link_name, NULL, (GAsyncReadyCallback) device_delete_link_ready, task); } /*****************************************************************************/ typedef struct { MbimDevice *device; MMPort *data; } ResetContext; static void reset_context_free (ResetContext *ctx) { g_clear_object (&ctx->device); g_clear_object (&ctx->data); g_slice_free (ResetContext, ctx); } gboolean mm_port_mbim_reset_finish (MMPortMbim *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void delete_all_links_ready (MbimDevice *device, GAsyncResult *res, GTask *task) { MMPortMbim *self; GError *error = NULL; self = g_task_get_source_object (task); /* link deletion not fatal */ if (!mbim_device_delete_all_links_finish (device, res, &error)) { mm_obj_dbg (self, "couldn't delete all links: %s", error->message); g_clear_error (&error); } g_task_return_boolean (task, TRUE); g_object_unref (task); } static void reset_device_new_ready (GObject *source, GAsyncResult *res, GTask *task) { MMPortMbim *self; ResetContext *ctx; GError *error = NULL; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); ctx->device = mbim_device_new_finish (res, &error); if (!ctx->device) { g_task_return_error (task, error); g_object_unref (task); return; } /* first, delete all links found, if any */ mm_obj_dbg (self, "deleting all links in data interface '%s'", mm_port_get_device (ctx->data)); mbim_device_delete_all_links (ctx->device, mm_port_get_device (ctx->data), NULL, (GAsyncReadyCallback)delete_all_links_ready, task); } void mm_port_mbim_reset (MMPortMbim *self, MMPort *data, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; ResetContext *ctx; g_autoptr(GFile) file = NULL; g_autofree gchar *fullpath = NULL; task = g_task_new (self, NULL, callback, user_data); if (self->priv->mbim_device) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_WRONG_STATE, "Port is already open"); g_object_unref (task); return; } ctx = g_slice_new0 (ResetContext); ctx->data = g_object_ref (data); g_task_set_task_data (task, ctx, (GDestroyNotify) reset_context_free); fullpath = g_strdup_printf ("/dev/%s", mm_port_get_device (MM_PORT (self))); file = g_file_new_for_path (fullpath); mbim_device_new (file, NULL, (GAsyncReadyCallback) reset_device_new_ready, task); } /*****************************************************************************/ static void reset_monitoring (MMPortMbim *self, MbimDevice *mbim_device) { if (self->priv->notification_monitoring_id && mbim_device) { g_signal_handler_disconnect (mbim_device, self->priv->notification_monitoring_id); self->priv->notification_monitoring_id = 0; } if (self->priv->timeout_monitoring_id && mbim_device) { g_signal_handler_disconnect (mbim_device, self->priv->timeout_monitoring_id); self->priv->timeout_monitoring_id = 0; } if (self->priv->removed_monitoring_id && mbim_device) { g_signal_handler_disconnect (mbim_device, self->priv->removed_monitoring_id); self->priv->removed_monitoring_id = 0; } } static void consecutive_timeouts_updated_cb (MMPortMbim *self, GParamSpec *pspec, MbimDevice *mbim_device) { g_signal_emit_by_name (self, MM_PORT_SIGNAL_TIMED_OUT, mbim_device_get_consecutive_timeouts (mbim_device)); } static void device_removed_cb (MMPortMbim *self) { g_signal_emit_by_name (self, MM_PORT_SIGNAL_REMOVED); } static void notification_cb (MMPortMbim *self, MbimMessage *notification) { g_signal_emit (self, signals[SIGNAL_NOTIFICATION], 0, notification); } static void setup_monitoring (MMPortMbim *self, MbimDevice *mbim_device) { g_assert (mbim_device); reset_monitoring (self, mbim_device); g_assert (!self->priv->notification_monitoring_id); self->priv->notification_monitoring_id = g_signal_connect_swapped (mbim_device, MBIM_DEVICE_SIGNAL_INDICATE_STATUS, G_CALLBACK (notification_cb), self); g_assert (!self->priv->timeout_monitoring_id); self->priv->timeout_monitoring_id = g_signal_connect_swapped (mbim_device, "notify::" MBIM_DEVICE_CONSECUTIVE_TIMEOUTS, G_CALLBACK (consecutive_timeouts_updated_cb), self); g_assert (!self->priv->removed_monitoring_id); self->priv->removed_monitoring_id = g_signal_connect_swapped (mbim_device, MBIM_DEVICE_SIGNAL_REMOVED, G_CALLBACK (device_removed_cb), self); } /*****************************************************************************/ gboolean mm_port_mbim_open_finish (MMPortMbim *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } #if defined WITH_QMI && QMI_MBIM_QMUX_SUPPORTED static void qmi_device_open_ready (QmiDevice *dev, GAsyncResult *res, GTask *task) { GError *error = NULL; MMPortMbim *self; self = g_task_get_source_object (task); if (!qmi_device_open_finish (dev, res, &error)) { mm_obj_dbg (self, "error: couldn't open QmiDevice: %s", error->message); g_error_free (error); g_clear_object (&self->priv->qmi_device); /* Ignore error and complete */ mm_obj_msg (self, "MBIM device is not QMI capable"); self->priv->qmi_supported = FALSE; } else { mm_obj_msg (self, "MBIM device is QMI capable"); } self->priv->in_progress = FALSE; g_task_return_boolean (task, TRUE); g_object_unref (task); } static void qmi_device_new_ready (GObject *unused, GAsyncResult *res, GTask *task) { GError *error = NULL; MMPortMbim *self; self = g_task_get_source_object (task); self->priv->qmi_device = qmi_device_new_finish (res, &error); if (!self->priv->qmi_device) { mm_obj_dbg (self, "error: couldn't create QmiDevice: %s", error->message); g_error_free (error); /* Ignore error and complete */ mm_obj_msg (self, "MBIM device is not QMI capable"); self->priv->qmi_supported = FALSE; self->priv->in_progress = FALSE; g_task_return_boolean (task, TRUE); g_object_unref (task); return; } /* Try to open using QMI over MBIM */ mm_obj_dbg (self, "trying to open QMI over MBIM device..."); qmi_device_open (self->priv->qmi_device, (QMI_DEVICE_OPEN_FLAGS_PROXY | QMI_DEVICE_OPEN_FLAGS_MBIM | QMI_DEVICE_OPEN_FLAGS_VERSION_INFO | QMI_DEVICE_OPEN_FLAGS_EXPECT_INDICATIONS), 15, g_task_get_cancellable (task), (GAsyncReadyCallback)qmi_device_open_ready, task); } static void mbim_query_device_services_ready (MbimDevice *device, GAsyncResult *res, GTask *task) { MMPortMbim *self; MbimMessage *response; GError *error = NULL; MbimDeviceServiceElement **device_services; guint32 device_services_count; GFile *file; self = g_task_get_source_object (task); response = mbim_device_command_finish (device, res, &error); if (response && mbim_message_response_get_result (response, MBIM_MESSAGE_TYPE_COMMAND_DONE, &error) && mbim_message_device_services_response_parse ( response, &device_services_count, NULL, /* max_dss_sessions */ &device_services, &error)) { guint32 i; /* Look for the QMI service */ for (i = 0; i < device_services_count; i++) { if (mbim_uuid_to_service (&device_services[i]->device_service_id) == MBIM_SERVICE_QMI) break; } /* If we were able to successfully list device services and none of them * is the QMI service, we'll skip trying to check QMI support. */ if (i == device_services_count) self->priv->qmi_supported = FALSE; mbim_device_service_element_array_free (device_services); } else { /* Ignore error */ mm_obj_dbg (self, "Couldn't query device services, will attempt QMI open anyway: %s", error->message); g_error_free (error); } if (response) mbim_message_unref (response); /* File path of the device */ file = G_FILE (g_task_get_task_data (task)); if (!file || !self->priv->qmi_supported) { mm_obj_msg (self, "MBIM device is not QMI capable"); self->priv->in_progress = FALSE; g_task_return_boolean (task, TRUE); g_object_unref (task); return; } /* Attempt to create and open the QMI device */ mm_obj_dbg (self, "checking if QMI over MBIM is supported..."); qmi_device_new (file, g_task_get_cancellable (task), (GAsyncReadyCallback) qmi_device_new_ready, task); } static void mbim_query_device_services (GTask *task) { MbimMessage *message; MMPortMbim *self; self = g_task_get_source_object (task); message = mbim_message_device_services_query_new (NULL); mbim_device_command (self->priv->mbim_device, message, 20, NULL, (GAsyncReadyCallback)mbim_query_device_services_ready, task); mbim_message_unref (message); } #endif static void mbim_device_open_ready (MbimDevice *mbim_device, GAsyncResult *res, GTask *task) { GError *error = NULL; MMPortMbim *self; self = g_task_get_source_object (task); if (!mbim_device_open_full_finish (mbim_device, res, &error)) { g_clear_object (&self->priv->mbim_device); self->priv->in_progress = FALSE; g_task_return_error (task, error); g_object_unref (task); return; } mm_obj_dbg (self, "MBIM device is now open"); setup_monitoring (self, mbim_device); #if defined WITH_QMI && QMI_MBIM_QMUX_SUPPORTED if (self->priv->qmi_supported) { mbim_query_device_services (task); return; } #endif self->priv->in_progress = FALSE; g_task_return_boolean (task, TRUE); g_object_unref (task); } static void mbim_device_new_ready (GObject *unused, GAsyncResult *res, GTask *task) { GError *error = NULL; MMPortMbim *self; self = g_task_get_source_object (task); self->priv->mbim_device = mbim_device_new_finish (res, &error); if (!self->priv->mbim_device) { g_task_return_error (task, error); g_object_unref (task); return; } /* Now open the MBIM device */ mbim_device_open_full (self->priv->mbim_device, MBIM_DEVICE_OPEN_FLAGS_PROXY | MBIM_DEVICE_OPEN_FLAGS_MS_MBIMEX_V3, 45, g_task_get_cancellable (task), (GAsyncReadyCallback)mbim_device_open_ready, task); } void mm_port_mbim_open (MMPortMbim *self, #if defined WITH_QMI && QMI_MBIM_QMUX_SUPPORTED gboolean try_qmi_over_mbim, #endif GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { GFile *file; gchar *fullpath; GTask *task; g_return_if_fail (MM_IS_PORT_MBIM (self)); task = g_task_new (self, cancellable, callback, user_data); if (self->priv->in_progress) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_IN_PROGRESS, "MBIM device open/close operation in progress"); g_object_unref (task); return; } if (self->priv->mbim_device) { g_task_return_boolean (task, TRUE); g_object_unref (task); return; } fullpath = g_strdup_printf ("/dev/%s", mm_port_get_device (MM_PORT (self))); file = g_file_new_for_path (fullpath); #if defined WITH_QMI && QMI_MBIM_QMUX_SUPPORTED /* If we want to try QMI over MBIM, store the GFile as task data */ if (try_qmi_over_mbim) g_task_set_task_data (task, g_object_ref (file), g_object_unref); #endif self->priv->in_progress = TRUE; mbim_device_new (file, cancellable, (GAsyncReadyCallback)mbim_device_new_ready, task); g_free (fullpath); g_object_unref (file); } /*****************************************************************************/ gboolean mm_port_mbim_is_open (MMPortMbim *self) { g_return_val_if_fail (MM_IS_PORT_MBIM (self), FALSE); return !!self->priv->mbim_device; } /*****************************************************************************/ typedef struct { MbimDevice *mbim_device; #if defined WITH_QMI && QMI_MBIM_QMUX_SUPPORTED QmiDevice *qmi_device; #endif } PortMbimCloseContext; static void port_mbim_close_context_free (PortMbimCloseContext *ctx) { g_clear_object (&ctx->mbim_device); #if defined WITH_QMI && QMI_MBIM_QMUX_SUPPORTED g_clear_object (&ctx->qmi_device); #endif g_slice_free (PortMbimCloseContext, ctx); } gboolean mm_port_mbim_close_finish (MMPortMbim *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void mbim_device_close_ready (MbimDevice *mbim_device, GAsyncResult *res, GTask *task) { GError *error = NULL; MMPortMbim *self; self = g_task_get_source_object (task); g_assert (!self->priv->mbim_device); self->priv->in_progress = FALSE; reset_monitoring (self, mbim_device); if (!mbim_device_close_finish (mbim_device, res, &error)) g_task_return_error (task, error); else g_task_return_boolean (task, TRUE); g_object_unref (task); } static void port_mbim_device_close (GTask *task) { PortMbimCloseContext *ctx; ctx = g_task_get_task_data (task); g_assert (ctx->mbim_device); mbim_device_close (ctx->mbim_device, 5, NULL, (GAsyncReadyCallback)mbim_device_close_ready, task); } #if defined WITH_QMI && QMI_MBIM_QMUX_SUPPORTED static void qmi_device_close_ready (QmiDevice *qmi_device, GAsyncResult *res, GTask *task) { GError *error = NULL; MMPortMbim *self; self = g_task_get_source_object (task); if (!qmi_device_close_finish (qmi_device, res, &error)) { mm_obj_warn (self, "Couldn't properly close QMI device: %s", error->message); g_error_free (error); } port_mbim_device_close (task); } #endif void mm_port_mbim_close (MMPortMbim *self, GAsyncReadyCallback callback, gpointer user_data) { PortMbimCloseContext *ctx; GTask *task; g_return_if_fail (MM_IS_PORT_MBIM (self)); task = g_task_new (self, NULL, callback, user_data); if (self->priv->in_progress) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_IN_PROGRESS, "MBIM device open/close operation in progress"); g_object_unref (task); return; } if (!self->priv->mbim_device) { g_task_return_boolean (task, TRUE); g_object_unref (task); return; } self->priv->in_progress = TRUE; /* Store device(s) to close in the context */ ctx = g_slice_new0 (PortMbimCloseContext); ctx->mbim_device = g_steal_pointer (&self->priv->mbim_device); g_task_set_task_data (task, ctx, (GDestroyNotify)port_mbim_close_context_free); #if defined WITH_QMI && QMI_MBIM_QMUX_SUPPORTED if (self->priv->qmi_device) { GList *l; /* Release all allocated clients */ for (l = self->priv->qmi_clients; l; l = g_list_next (l)) { QmiClient *qmi_client = QMI_CLIENT (l->data); mm_obj_dbg (self, "Releasing client for service '%s'...", qmi_service_get_string (qmi_client_get_service (qmi_client))); qmi_device_release_client (self->priv->qmi_device, qmi_client, QMI_DEVICE_RELEASE_CLIENT_FLAGS_RELEASE_CID, 3, NULL, NULL, NULL); } g_list_free_full (self->priv->qmi_clients, g_object_unref); self->priv->qmi_clients = NULL; ctx->qmi_device = g_steal_pointer (&self->priv->qmi_device); qmi_device_close_async (ctx->qmi_device, 5, NULL, (GAsyncReadyCallback)qmi_device_close_ready, task); return; } #endif port_mbim_device_close (task); } /*****************************************************************************/ MbimDevice * mm_port_mbim_peek_device (MMPortMbim *self) { g_return_val_if_fail (MM_IS_PORT_MBIM (self), NULL); return self->priv->mbim_device; } /*****************************************************************************/ MMPortMbim * mm_port_mbim_new (const gchar *name, MMPortSubsys subsys) { return MM_PORT_MBIM (g_object_new (MM_TYPE_PORT_MBIM, MM_PORT_DEVICE, name, MM_PORT_SUBSYS, subsys, MM_PORT_TYPE, MM_PORT_TYPE_MBIM, NULL)); } static void mm_port_mbim_init (MMPortMbim *self) { self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, MM_TYPE_PORT_MBIM, MMPortMbimPrivate); #if defined WITH_QMI && QMI_MBIM_QMUX_SUPPORTED /* By default, always assume that QMI is supported, we'll later check if * that's true or not. */ self->priv->qmi_supported = TRUE; #endif } static void dispose (GObject *object) { MMPortMbim *self = MM_PORT_MBIM (object); #if defined WITH_QMI && QMI_MBIM_QMUX_SUPPORTED g_list_free_full (self->priv->qmi_clients, g_object_unref); self->priv->qmi_clients = NULL; g_clear_object (&self->priv->qmi_device); #endif /* Clear device object */ reset_monitoring (self, self->priv->mbim_device); g_clear_object (&self->priv->mbim_device); G_OBJECT_CLASS (mm_port_mbim_parent_class)->dispose (object); } static void mm_port_mbim_class_init (MMPortMbimClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); g_type_class_add_private (object_class, sizeof (MMPortMbimPrivate)); /* Virtual methods */ object_class->dispose = dispose; signals[SIGNAL_NOTIFICATION] = g_signal_new (MM_PORT_MBIM_SIGNAL_NOTIFICATION, G_OBJECT_CLASS_TYPE (G_OBJECT_CLASS (klass)), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (MMPortMbimClass, notification), NULL, NULL, g_cclosure_marshal_generic, G_TYPE_NONE, 1, MBIM_TYPE_MESSAGE); } ModemManager-1.23.4-dev/src/mm-port-mbim.h000066400000000000000000000133441456466623000202070ustar00rootroot00000000000000/* -*- 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) 2013-2021 Aleksander Morgado */ #ifndef MM_PORT_MBIM_H #define MM_PORT_MBIM_H #include #include #include #include #include #if defined WITH_QMI # include #endif #include "mm-port.h" #define MM_TYPE_PORT_MBIM (mm_port_mbim_get_type ()) #define MM_PORT_MBIM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_PORT_MBIM, MMPortMbim)) #define MM_PORT_MBIM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_PORT_MBIM, MMPortMbimClass)) #define MM_IS_PORT_MBIM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_PORT_MBIM)) #define MM_IS_PORT_MBIM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_PORT_MBIM)) #define MM_PORT_MBIM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_PORT_MBIM, MMPortMbimClass)) #define MM_PORT_MBIM_SIGNAL_NOTIFICATION "notification" typedef struct _MMPortMbim MMPortMbim; typedef struct _MMPortMbimClass MMPortMbimClass; typedef struct _MMPortMbimPrivate MMPortMbimPrivate; struct _MMPortMbim { MMPort parent; MMPortMbimPrivate *priv; }; struct _MMPortMbimClass { MMPortClass parent; /* signals */ void (* notification) (MMPortMbim *port, MbimMessage *notification); }; GType mm_port_mbim_get_type (void); G_DEFINE_AUTOPTR_CLEANUP_FUNC (MMPortMbim, g_object_unref) MMPortMbim *mm_port_mbim_new (const gchar *name, MMPortSubsys subsys); void mm_port_mbim_open (MMPortMbim *self, #if defined WITH_QMI && QMI_MBIM_QMUX_SUPPORTED gboolean try_qmi_over_mbim, #endif GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); gboolean mm_port_mbim_open_finish (MMPortMbim *self, GAsyncResult *res, GError **error); gboolean mm_port_mbim_is_open (MMPortMbim *self); void mm_port_mbim_close (MMPortMbim *self, GAsyncReadyCallback callback, gpointer user_data); gboolean mm_port_mbim_close_finish (MMPortMbim *self, GAsyncResult *res, GError **error); #if defined WITH_QMI && QMI_MBIM_QMUX_SUPPORTED gboolean mm_port_mbim_supports_qmi (MMPortMbim *self); QmiClient *mm_port_mbim_peek_qmi_client (MMPortMbim *self, QmiService service); QmiClient *mm_port_mbim_get_qmi_client (MMPortMbim *self, QmiService service); void mm_port_mbim_allocate_qmi_client (MMPortMbim *self, QmiService service, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); gboolean mm_port_mbim_allocate_qmi_client_finish (MMPortMbim *self, GAsyncResult *res, GError **error); #endif MbimDevice *mm_port_mbim_peek_device (MMPortMbim *self); void mm_port_mbim_setup_link (MMPortMbim *self, MMPort *data, const gchar *link_prefix_hint, GAsyncReadyCallback callback, gpointer user_data); gchar *mm_port_mbim_setup_link_finish (MMPortMbim *self, GAsyncResult *res, guint *session_id, GError **error); void mm_port_mbim_cleanup_link (MMPortMbim *self, const gchar *link_name, GAsyncReadyCallback callback, gpointer user_data); gboolean mm_port_mbim_cleanup_link_finish (MMPortMbim *self, GAsyncResult *res, GError **error); void mm_port_mbim_reset (MMPortMbim *self, MMPort *data, GAsyncReadyCallback callback, gpointer user_data); gboolean mm_port_mbim_reset_finish (MMPortMbim *self, GAsyncResult *res, GError **error); #endif /* MM_PORT_MBIM_H */ ModemManager-1.23.4-dev/src/mm-port-net.c000066400000000000000000000073741456466623000200520ustar00rootroot00000000000000/* -*- 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) 2021 - Aleksander Morgado */ #include #include #include #include "mm-port-net.h" #include "mm-log-object.h" #include "mm-netlink.h" G_DEFINE_TYPE (MMPortNet, mm_port_net, MM_TYPE_PORT) struct _MMPortNetPrivate { guint ifindex; }; static void ensure_ifindex (MMPortNet *self) { if (!self->priv->ifindex) { self->priv->ifindex = if_nametoindex (mm_port_get_device (MM_PORT (self))); if (!self->priv->ifindex) mm_obj_warn (self, "couldn't get interface index"); else mm_obj_dbg (self, "interface index: %u", self->priv->ifindex); } } /*****************************************************************************/ gboolean mm_port_net_link_setup_finish (MMPortNet *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void netlink_setlink_ready (MMNetlink *netlink, GAsyncResult *res, GTask *task) { GError *error = NULL; if (!mm_netlink_setlink_finish (netlink, res, &error)) { g_prefix_error (&error, "netlink operation failed: "); g_task_return_error (task, error); } else g_task_return_boolean (task, TRUE); g_object_unref (task); } void mm_port_net_link_setup (MMPortNet *self, gboolean up, guint mtu, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; task = g_task_new (self, cancellable, callback, user_data); ensure_ifindex (self); if (!self->priv->ifindex) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "no valid interface index found for %s", mm_port_get_device (MM_PORT (self))); g_object_unref (task); return; } mm_netlink_setlink (mm_netlink_get (), /* singleton */ self->priv->ifindex, up, mtu, cancellable, (GAsyncReadyCallback) netlink_setlink_ready, task); } /*****************************************************************************/ MMPortNet * mm_port_net_new (const gchar *name) { return MM_PORT_NET (g_object_new (MM_TYPE_PORT_NET, MM_PORT_DEVICE, name, MM_PORT_SUBSYS, MM_PORT_SUBSYS_NET, MM_PORT_TYPE, MM_PORT_TYPE_NET, NULL)); } static void mm_port_net_init (MMPortNet *self) { self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, MM_TYPE_PORT_NET, MMPortNetPrivate); } static void mm_port_net_class_init (MMPortNetClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); g_type_class_add_private (object_class, sizeof (MMPortNetPrivate)); } ModemManager-1.23.4-dev/src/mm-port-net.h000066400000000000000000000046761456466623000200610ustar00rootroot00000000000000/* -*- 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) 2021 Aleksander Morgado */ #ifndef MM_PORT_NET_H #define MM_PORT_NET_H #include #include #include #include "mm-port.h" /* Default MTU expected in a wwan interface */ #define MM_PORT_NET_MTU_DEFAULT 1500 #define MM_TYPE_PORT_NET (mm_port_net_get_type ()) #define MM_PORT_NET(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_PORT_NET, MMPortNet)) #define MM_PORT_NET_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_PORT_NET, MMPortNetClass)) #define MM_IS_PORT_NET(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_PORT_NET)) #define MM_IS_PORT_NET_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_PORT_NET)) #define MM_PORT_NET_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_PORT_NET, MMPortNetClass)) typedef struct _MMPortNet MMPortNet; typedef struct _MMPortNetClass MMPortNetClass; typedef struct _MMPortNetPrivate MMPortNetPrivate; struct _MMPortNet { MMPort parent; MMPortNetPrivate *priv; }; struct _MMPortNetClass { MMPortClass parent; }; GType mm_port_net_get_type (void); G_DEFINE_AUTOPTR_CLEANUP_FUNC (MMPortNet, g_object_unref) MMPortNet *mm_port_net_new (const gchar *name); void mm_port_net_link_setup (MMPortNet *self, gboolean up, guint mtu, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); gboolean mm_port_net_link_setup_finish (MMPortNet *self, GAsyncResult *res, GError **error); #endif /* MM_PORT_NET_H */ ModemManager-1.23.4-dev/src/mm-port-probe-at.c000066400000000000000000000062431456466623000207670ustar00rootroot00000000000000/* -*- 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 - 2011 Red Hat, Inc. * Copyright (C) 2011 - 2012 Aleksander Morgado * Copyright (C) 2012 Google, Inc. */ #define _GNU_SOURCE /* for strcasestr */ #include #include #include #include #include "mm-log.h" #include "mm-port-probe.h" #include "mm-port-probe-at.h" #include "mm-serial-parsers.h" /* ---- AT probing ---- */ gboolean mm_port_probe_response_processor_is_at (const gchar *command, const gchar *response, gboolean last_command, const GError *error, GVariant **result, GError **result_error) { if (error) { /* Timeout errors are the only ones not fatal; * they will just go on to the next command. */ if (g_error_matches (error, MM_SERIAL_ERROR, MM_SERIAL_ERROR_RESPONSE_TIMEOUT)) { return FALSE; } /* If error is NOT known by the parser, or if the error is actually * the generic parsing filter error, request to abort */ if (!mm_serial_parser_v1_is_known_error (error) || g_error_matches (error, MM_SERIAL_ERROR, MM_SERIAL_ERROR_PARSE_FAILED)) { *result_error = g_error_copy (error); g_prefix_error (result_error, "Fatal error parsing AT reply: "); return FALSE; } /* If the modem returned a recognizable error, * it can do AT commands */ *result = g_variant_new_boolean (TRUE); return TRUE; } /* No error reported, valid AT port! */ *result = g_variant_new_boolean (TRUE); return TRUE; } /* ---- String probing ---- */ gboolean mm_port_probe_response_processor_string (const gchar *command, const gchar *response, gboolean last_command, const GError *error, GVariant **result, GError **result_error) { gchar *str; if (error) /* Try with the next command, if any */ return FALSE; str = g_strstrip (g_strdelimit (g_strdup (response), "\r\n", ' ')); *result = g_variant_new_string (str); g_free (str); return TRUE; } ModemManager-1.23.4-dev/src/mm-port-probe-at.h000066400000000000000000000067161456466623000210010ustar00rootroot00000000000000/* -*- 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) 2011 - 2012 Aleksander Morgado * Copyright (C) 2012 Google, Inc. */ #ifndef MM_PORT_PROBE_AT_H #define MM_PORT_PROBE_AT_H #include /* The response processor. The expected result depends on the * probing type: * - AT --> Boolean * - Vendor --> String * - Product --> String * * TRUE must be returned when the operation is to be considered successful, * and a result may be given. * * FALSE must be returned when: * - A GError is propagated into result_error, which will be treated as a * critical error and therefore the operation will be aborted. * - When no result_error is given, to instruct the operation to go on with * the next scheduled command. * * A special case to consider is the initialization commands, used by * some plugins. In this case, there is no expected result, but plugins may * set an optional boolean result, specifying whether the port is an AT port * or not. * - Initialization --> None or Boolean * When the initialization is considered enough, TRUE is returned, and * FALSE otherwise. */ typedef gboolean (* MMPortProbeAtResponseProcessor) (const gchar *command, const gchar *response, gboolean last_command, const GError *error, GVariant **result, GError **result_error); /* Struct to configure port probing commands */ typedef struct { /* The AT command */ const gchar *command; /* Timeout of the command, in seconds */ guint timeout; /* The response processor */ MMPortProbeAtResponseProcessor response_processor; } MMPortProbeAtCommand; /* Common helper response processors */ /* Every string received as response, will be set as result */ gboolean mm_port_probe_response_processor_string (const gchar *command, const gchar *response, gboolean last_command, const GError *error, GVariant **result, GError **result_error); /* Generic response parser for AT probing, useful also in custom init commands */ gboolean mm_port_probe_response_processor_is_at (const gchar *command, const gchar *response, gboolean last_command, const GError *error, GVariant **result, GError **result_error); #endif /* MM_PORT_PROBE_AT_H */ ModemManager-1.23.4-dev/src/mm-port-probe.c000066400000000000000000002136761456466623000203770ustar00rootroot00000000000000/* -*- 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 - 2018 Red Hat, Inc. * Copyright (C) 2011 - 2018 Aleksander Morgado */ #include "config.h" #include #include #include #include #include #include #include "mm-port-probe.h" #include "mm-log-object.h" #include "mm-port-serial-at.h" #include "mm-port-serial.h" #include "mm-serial-parsers.h" #include "mm-port-probe-at.h" #include "libqcdm/src/commands.h" #include "libqcdm/src/utils.h" #include "libqcdm/src/errors.h" #include "mm-port-serial-qcdm.h" #include "mm-daemon-enums-types.h" #if defined WITH_QMI #include "mm-port-qmi.h" #endif #if defined WITH_QRTR #include "mm-kernel-device-qrtr.h" #endif #if defined WITH_MBIM #include "mm-port-mbim.h" #endif /* * Steps and flow of the Probing process: * ----> AT Serial Open * |----> Custom Init * |----> AT? * |----> Vendor * |----> Product * |----> Is Icera? * |----> Is Xmm? * ----> QCDM Serial Open * |----> QCDM? * ----> QMI Device Open * |----> QMI Version Info check * ----> MBIM Device Open * |----> MBIM capabilities check */ static void log_object_iface_init (MMLogObjectInterface *iface); G_DEFINE_TYPE_EXTENDED (MMPortProbe, mm_port_probe, G_TYPE_OBJECT, 0, G_IMPLEMENT_INTERFACE (MM_TYPE_LOG_OBJECT, log_object_iface_init)) enum { PROP_0, PROP_DEVICE, PROP_PORT, PROP_LAST }; static GParamSpec *properties[PROP_LAST]; struct _MMPortProbePrivate { /* Properties */ MMDevice *device; MMKernelDevice *port; /* Probing results */ guint32 flags; gboolean is_at; gboolean is_qcdm; gchar *vendor; gchar *product; gboolean is_icera; gboolean is_xmm; gboolean is_qmi; gboolean is_mbim; /* From udev tags */ gboolean is_ignored; gboolean is_gps; gboolean is_audio; gboolean maybe_at; gboolean maybe_qcdm; gboolean maybe_qmi; gboolean maybe_mbim; /* Current probing task. Only one can be available at a time */ GTask *task; }; /*****************************************************************************/ /* Probe task completions. * Always make sure that the stored task is NULL when the task is completed. */ static gboolean port_probe_task_return_error_if_cancelled (MMPortProbe *self) { GTask *task; task = self->priv->task; self->priv->task = NULL; if (g_task_return_error_if_cancelled (task)) { g_object_unref (task); return TRUE; } self->priv->task = task; return FALSE; } static void port_probe_task_return_error (MMPortProbe *self, GError *error) { GTask *task; task = self->priv->task; self->priv->task = NULL; g_task_return_error (task, error); g_object_unref (task); } static void port_probe_task_return_boolean (MMPortProbe *self, gboolean result) { GTask *task; task = self->priv->task; self->priv->task = NULL; g_task_return_boolean (task, result); g_object_unref (task); } /*****************************************************************************/ void mm_port_probe_set_result_at (MMPortProbe *self, gboolean at) { self->priv->is_at = at; self->priv->flags |= MM_PORT_PROBE_AT; if (self->priv->is_at) { mm_obj_dbg (self, "port is AT-capable"); /* Also set as not a QCDM/QMI/MBIM port */ self->priv->is_qcdm = FALSE; self->priv->is_qmi = FALSE; self->priv->is_mbim = FALSE; self->priv->flags |= (MM_PORT_PROBE_QCDM | MM_PORT_PROBE_QMI | MM_PORT_PROBE_MBIM); } else { mm_obj_dbg (self, "port is not AT-capable"); self->priv->vendor = NULL; self->priv->product = NULL; self->priv->is_icera = FALSE; self->priv->is_xmm = FALSE; self->priv->flags |= (MM_PORT_PROBE_AT_VENDOR | MM_PORT_PROBE_AT_PRODUCT | MM_PORT_PROBE_AT_ICERA | MM_PORT_PROBE_AT_XMM); } } void mm_port_probe_set_result_at_vendor (MMPortProbe *self, const gchar *at_vendor) { if (at_vendor) { mm_obj_dbg (self, "vendor probing finished"); self->priv->vendor = g_utf8_casefold (at_vendor, -1); self->priv->flags |= MM_PORT_PROBE_AT_VENDOR; } else { mm_obj_dbg (self, "couldn't probe for vendor string"); self->priv->vendor = NULL; self->priv->product = NULL; self->priv->flags |= (MM_PORT_PROBE_AT_VENDOR | MM_PORT_PROBE_AT_PRODUCT); } } void mm_port_probe_set_result_at_product (MMPortProbe *self, const gchar *at_product) { if (at_product) { mm_obj_dbg (self, "product probing finished"); self->priv->product = g_utf8_casefold (at_product, -1); self->priv->flags |= MM_PORT_PROBE_AT_PRODUCT; } else { mm_obj_dbg (self, "couldn't probe for product string"); self->priv->product = NULL; self->priv->flags |= MM_PORT_PROBE_AT_PRODUCT; } } void mm_port_probe_set_result_at_icera (MMPortProbe *self, gboolean is_icera) { if (is_icera) { mm_obj_dbg (self, "modem is Icera-based"); self->priv->is_icera = TRUE; self->priv->flags |= MM_PORT_PROBE_AT_ICERA; } else { mm_obj_dbg (self, "modem is probably not Icera-based"); self->priv->is_icera = FALSE; self->priv->flags |= MM_PORT_PROBE_AT_ICERA; } } void mm_port_probe_set_result_at_xmm (MMPortProbe *self, gboolean is_xmm) { if (is_xmm) { mm_obj_dbg (self, "modem is XMM-based"); self->priv->is_xmm = TRUE; self->priv->flags |= MM_PORT_PROBE_AT_XMM; } else { mm_obj_dbg (self, "modem is probably not XMM-based"); self->priv->is_xmm = FALSE; self->priv->flags |= MM_PORT_PROBE_AT_XMM; } } void mm_port_probe_set_result_qcdm (MMPortProbe *self, gboolean qcdm) { self->priv->is_qcdm = qcdm; self->priv->flags |= MM_PORT_PROBE_QCDM; if (self->priv->is_qcdm) { mm_obj_dbg (self, "port is QCDM-capable"); /* Also set as not an AT/QMI/MBIM port */ self->priv->is_at = FALSE; self->priv->is_qmi = FALSE; self->priv->is_mbim = FALSE; self->priv->vendor = NULL; self->priv->product = NULL; self->priv->is_icera = FALSE; self->priv->is_xmm = FALSE; self->priv->flags |= (MM_PORT_PROBE_AT | MM_PORT_PROBE_AT_VENDOR | MM_PORT_PROBE_AT_PRODUCT | MM_PORT_PROBE_AT_ICERA | MM_PORT_PROBE_AT_XMM | MM_PORT_PROBE_QMI | MM_PORT_PROBE_MBIM); } else mm_obj_dbg (self, "port is not QCDM-capable"); } void mm_port_probe_set_result_qmi (MMPortProbe *self, gboolean qmi) { self->priv->is_qmi = qmi; self->priv->flags |= MM_PORT_PROBE_QMI; if (self->priv->is_qmi) { mm_obj_dbg (self, "port is QMI-capable"); /* Also set as not an AT/QCDM/MBIM port */ self->priv->is_at = FALSE; self->priv->is_qcdm = FALSE; self->priv->is_mbim = FALSE; self->priv->vendor = NULL; self->priv->product = NULL; self->priv->flags |= (MM_PORT_PROBE_AT | MM_PORT_PROBE_AT_VENDOR | MM_PORT_PROBE_AT_PRODUCT | MM_PORT_PROBE_AT_ICERA | MM_PORT_PROBE_AT_XMM | MM_PORT_PROBE_QCDM | MM_PORT_PROBE_MBIM); } else mm_obj_dbg (self, "port is not QMI-capable"); } void mm_port_probe_set_result_mbim (MMPortProbe *self, gboolean mbim) { self->priv->is_mbim = mbim; self->priv->flags |= MM_PORT_PROBE_MBIM; if (self->priv->is_mbim) { mm_obj_dbg (self, "port is MBIM-capable"); /* Also set as not an AT/QCDM/QMI port */ self->priv->is_at = FALSE; self->priv->is_qcdm = FALSE; self->priv->is_qmi = FALSE; self->priv->vendor = NULL; self->priv->product = NULL; self->priv->flags |= (MM_PORT_PROBE_AT | MM_PORT_PROBE_AT_VENDOR | MM_PORT_PROBE_AT_PRODUCT | MM_PORT_PROBE_AT_ICERA | MM_PORT_PROBE_AT_XMM | MM_PORT_PROBE_QCDM | MM_PORT_PROBE_QMI); } else mm_obj_dbg (self, "port is not MBIM-capable"); } /*****************************************************************************/ typedef struct { /* ---- Generic task context ---- */ guint32 flags; guint source_id; GCancellable *cancellable; /* ---- Serial probing specific context ---- */ guint buffer_full_id; MMPortSerial *serial; /* ---- AT probing specific context ---- */ GCancellable *at_probing_cancellable; gulong at_probing_cancellable_linked; /* Send delay for AT commands */ guint64 at_send_delay; /* Flag to leave/remove echo in AT responses */ gboolean at_remove_echo; /* Flag to send line-feed at the end of AT commands */ gboolean at_send_lf; /* Number of times we tried to open the AT port */ guint at_open_tries; /* Custom initialization setup */ gboolean at_custom_init_run; MMPortProbeAtCustomInit at_custom_init; MMPortProbeAtCustomInitFinish at_custom_init_finish; /* Custom commands to look for AT support */ const MMPortProbeAtCommand *at_custom_probe; /* Current group of AT commands to be sent */ const MMPortProbeAtCommand *at_commands; /* Seconds between each AT command sent in the group */ guint at_commands_wait_secs; /* Current AT Result processor */ void (* at_result_processor) (MMPortProbe *self, GVariant *result); #if defined WITH_QMI /* ---- QMI probing specific context ---- */ MMPortQmi *port_qmi; #endif #if defined WITH_MBIM /* ---- MBIM probing specific context ---- */ MMPortMbim *mbim_port; #endif /* ---- QCDM probing specific context ---- */ gboolean qcdm_required; } PortProbeRunContext; static gboolean serial_probe_at (MMPortProbe *self); static gboolean serial_probe_qcdm (MMPortProbe *self); static void serial_probe_schedule (MMPortProbe *self); static void port_probe_run_context_free (PortProbeRunContext *ctx) { if (ctx->cancellable && ctx->at_probing_cancellable_linked) { g_cancellable_disconnect (ctx->cancellable, ctx->at_probing_cancellable_linked); ctx->at_probing_cancellable_linked = 0; } if (ctx->source_id) { g_source_remove (ctx->source_id); ctx->source_id = 0; } if (ctx->serial && ctx->buffer_full_id) { g_signal_handler_disconnect (ctx->serial, ctx->buffer_full_id); ctx->buffer_full_id = 0; } if (ctx->serial) { if (mm_port_serial_is_open (ctx->serial)) mm_port_serial_close (ctx->serial); g_object_unref (ctx->serial); } #if defined WITH_QMI if (ctx->port_qmi) { /* We should have closed it cleanly before */ g_assert (!mm_port_qmi_is_open (ctx->port_qmi)); g_object_unref (ctx->port_qmi); } #endif #if defined WITH_MBIM if (ctx->mbim_port) { /* We should have closed it cleanly before */ g_assert (!mm_port_mbim_is_open (ctx->mbim_port)); g_object_unref (ctx->mbim_port); } #endif if (ctx->at_probing_cancellable) g_object_unref (ctx->at_probing_cancellable); if (ctx->cancellable) g_object_unref (ctx->cancellable); g_slice_free (PortProbeRunContext, ctx); } /***************************************************************/ /* QMI & MBIM */ static gboolean wdm_probe (MMPortProbe *self); #if defined WITH_QMI static void qmi_port_close_ready (MMPortQmi *qmi_port, GAsyncResult *res, MMPortProbe *self) { PortProbeRunContext *ctx; g_assert (self->priv->task); ctx = g_task_get_task_data (self->priv->task); mm_port_qmi_close_finish (qmi_port, res, NULL); /* Keep on */ ctx->source_id = g_idle_add ((GSourceFunc) wdm_probe, self); } static void port_qmi_open_ready (MMPortQmi *port_qmi, GAsyncResult *res, MMPortProbe *self) { GError *error = NULL; PortProbeRunContext *ctx; gboolean is_qmi; g_assert (self->priv->task); ctx = g_task_get_task_data (self->priv->task); is_qmi = mm_port_qmi_open_finish (port_qmi, res, &error); if (!is_qmi) { mm_obj_dbg (self, "error checking QMI support: %s", error ? error->message : "unknown error"); g_clear_error (&error); } /* Set probing result */ mm_port_probe_set_result_qmi (self, is_qmi); mm_port_qmi_close (ctx->port_qmi, (GAsyncReadyCallback) qmi_port_close_ready, self); } #endif /* WITH_QMI */ static void wdm_probe_qmi (MMPortProbe *self) { PortProbeRunContext *ctx; g_assert (self->priv->task); ctx = g_task_get_task_data (self->priv->task); #if defined WITH_QMI /* Create a port and try to open it */ mm_obj_dbg (self, "probing QMI..."); #if defined WITH_QRTR if (MM_IS_KERNEL_DEVICE_QRTR (self->priv->port)) { g_autoptr(QrtrNode) node = NULL; node = mm_kernel_device_qrtr_get_node (MM_KERNEL_DEVICE_QRTR (self->priv->port)); /* Will set MM_PORT_SUBSYS_QRTR when creating the mm-port */ ctx->port_qmi = mm_port_qmi_new_from_node (mm_kernel_device_get_name (self->priv->port), node); } else #endif /* WITH_QRTR */ { MMPortSubsys subsys = MM_PORT_SUBSYS_USBMISC; if (g_str_equal (mm_kernel_device_get_subsystem (self->priv->port), "rpmsg")) subsys = MM_PORT_SUBSYS_RPMSG; ctx->port_qmi = mm_port_qmi_new (mm_kernel_device_get_name (self->priv->port), subsys); } mm_port_qmi_open (ctx->port_qmi, FALSE, g_task_get_cancellable (self->priv->task), (GAsyncReadyCallback) port_qmi_open_ready, self); #else /* If not compiled with QMI support, just assume we won't have any QMI port */ mm_port_probe_set_result_qmi (self, FALSE); ctx->source_id = g_idle_add ((GSourceFunc) wdm_probe, self); #endif /* WITH_QMI */ } #if defined WITH_MBIM static void mbim_port_close_ready (MMPortMbim *mbim_port, GAsyncResult *res, MMPortProbe *self) { PortProbeRunContext *ctx; g_assert (self->priv->task); ctx = g_task_get_task_data (self->priv->task); mm_port_mbim_close_finish (mbim_port, res, NULL); /* Keep on */ ctx->source_id = g_idle_add ((GSourceFunc) wdm_probe, self); } static void mbim_port_open_ready (MMPortMbim *mbim_port, GAsyncResult *res, MMPortProbe *self) { GError *error = NULL; PortProbeRunContext *ctx; gboolean is_mbim; g_assert (self->priv->task); ctx = g_task_get_task_data (self->priv->task); is_mbim = mm_port_mbim_open_finish (mbim_port, res, &error); if (!is_mbim) { mm_obj_dbg (self, "error checking MBIM support: %s", error ? error->message : "unknown error"); g_clear_error (&error); } /* Set probing result */ mm_port_probe_set_result_mbim (self, is_mbim); mm_port_mbim_close (ctx->mbim_port, (GAsyncReadyCallback) mbim_port_close_ready, self); } #endif /* WITH_MBIM */ static void wdm_probe_mbim (MMPortProbe *self) { PortProbeRunContext *ctx; g_assert (self->priv->task); ctx = g_task_get_task_data (self->priv->task); #if defined WITH_MBIM mm_obj_dbg (self, "probing MBIM..."); /* Create a port and try to open it */ ctx->mbim_port = mm_port_mbim_new (mm_kernel_device_get_name (self->priv->port), MM_PORT_SUBSYS_USBMISC); mm_port_mbim_open (ctx->mbim_port, #if defined WITH_QMI && QMI_MBIM_QMUX_SUPPORTED FALSE, /* Don't check QMI over MBIM support at this stage */ #endif g_task_get_cancellable (self->priv->task), (GAsyncReadyCallback) mbim_port_open_ready, self); #else /* If not compiled with MBIM support, just assume we won't have any MBIM port */ mm_port_probe_set_result_mbim (self, FALSE); ctx->source_id = g_idle_add ((GSourceFunc) wdm_probe, self); #endif /* WITH_MBIM */ } static gboolean wdm_probe (MMPortProbe *self) { PortProbeRunContext *ctx; g_assert (self->priv->task); ctx = g_task_get_task_data (self->priv->task); ctx->source_id = 0; /* If already cancelled, do nothing else */ if (port_probe_task_return_error_if_cancelled (self)) return G_SOURCE_REMOVE; /* QMI probing needed? */ if ((ctx->flags & MM_PORT_PROBE_QMI) && !(self->priv->flags & MM_PORT_PROBE_QMI)) { wdm_probe_qmi (self); return G_SOURCE_REMOVE; } /* MBIM probing needed */ if ((ctx->flags & MM_PORT_PROBE_MBIM) && !(self->priv->flags & MM_PORT_PROBE_MBIM)) { wdm_probe_mbim (self); return G_SOURCE_REMOVE; } /* All done now */ port_probe_task_return_boolean (self, TRUE); return G_SOURCE_REMOVE; } /***************************************************************/ static void common_serial_port_setup (MMPortProbe *self, MMPortSerial *serial) { const gchar *flow_control_tag; if (mm_kernel_device_has_property (self->priv->port, ID_MM_TTY_BAUDRATE)) g_object_set (serial, MM_PORT_SERIAL_BAUD, mm_kernel_device_get_property_as_int (self->priv->port, ID_MM_TTY_BAUDRATE), NULL); flow_control_tag = mm_kernel_device_get_property (self->priv->port, ID_MM_TTY_FLOW_CONTROL); if (flow_control_tag) { MMFlowControl flow_control; GError *error = NULL; flow_control = mm_flow_control_from_string (flow_control_tag, &error); if (flow_control == MM_FLOW_CONTROL_UNKNOWN) { mm_obj_warn (self, "unsupported flow control settings in port: %s", error->message); g_error_free (error); } else { g_object_set (serial, MM_PORT_SERIAL_FLOW_CONTROL, flow_control, NULL); } } } /***************************************************************/ /* QCDM */ static void serial_probe_qcdm_parse_response (MMPortSerialQcdm *port, GAsyncResult *res, MMPortProbe *self) { QcdmResult *result; gint err = QCDM_SUCCESS; gboolean is_qcdm = FALSE; gboolean retry = FALSE; GError *error = NULL; GByteArray *response; PortProbeRunContext *ctx; ctx = g_task_get_task_data (self->priv->task); /* If already cancelled, do nothing else */ if (port_probe_task_return_error_if_cancelled (self)) return; response = mm_port_serial_qcdm_command_finish (port, res, &error); if (!error) { /* Parse the response */ result = qcdm_cmd_version_info_result ((const gchar *) response->data, response->len, &err); if (!result) { mm_obj_warn (self, "failed to parse QCDM version info command result: %d", err); retry = TRUE; } else { /* yay, probably a QCDM port */ is_qcdm = TRUE; qcdm_result_unref (result); } g_byte_array_unref (response); } else if (g_error_matches (error, MM_SERIAL_ERROR, MM_SERIAL_ERROR_PARSE_FAILED)) { /* Failed to unescape QCDM packet: don't retry */ mm_obj_dbg (self, "QCDM parsing error: %s", error->message); g_error_free (error); } else { if (!g_error_matches (error, MM_SERIAL_ERROR, MM_SERIAL_ERROR_RESPONSE_TIMEOUT)) mm_obj_dbg (self, "QCDM probe error: (%d) %s", error->code, error->message); g_error_free (error); retry = TRUE; } if (retry) { GByteArray *cmd2; cmd2 = g_object_steal_data (G_OBJECT (self), "cmd2"); if (cmd2) { /* second try */ mm_port_serial_qcdm_command (MM_PORT_SERIAL_QCDM (ctx->serial), cmd2, 3, NULL, (GAsyncReadyCallback) serial_probe_qcdm_parse_response, self); g_byte_array_unref (cmd2); return; } /* no more retries left */ } /* Set probing result */ mm_port_probe_set_result_qcdm (self, is_qcdm); /* Reschedule probing */ serial_probe_schedule (self); } static gboolean serial_probe_qcdm (MMPortProbe *self) { GError *error = NULL; GByteArray *verinfo = NULL; GByteArray *verinfo2; gint len; guint8 marker = 0x7E; MMPortSubsys subsys = MM_PORT_SUBSYS_TTY; PortProbeRunContext *ctx; g_assert (self->priv->task); ctx = g_task_get_task_data (self->priv->task); ctx->source_id = 0; /* If already cancelled, do nothing else */ if (port_probe_task_return_error_if_cancelled (self)) return G_SOURCE_REMOVE; /* If the plugin specifies QCDM is not required, we can right away complete the QCDM * probing task. */ if (!ctx->qcdm_required) { mm_obj_dbg (self, "Maybe a QCDM port, but plugin does not require probing and grabbing..."); /* If we had a port type hint, flag the port as QCDM capable but ignored. Otherwise, * no QCDM capable and not ignored. The outcome is really the same, i.e. the port is not * used any more, but the way it's reported in DBus will be different (i.e. "ignored" vs "unknown" */ if (self->priv->maybe_qcdm) { mm_port_probe_set_result_qcdm (self, TRUE); self->priv->is_ignored = TRUE; } else mm_port_probe_set_result_qcdm (self, FALSE); /* Reschedule probing */ serial_probe_schedule (self); return G_SOURCE_REMOVE; } mm_obj_dbg (self, "probing QCDM..."); /* If open, close the AT port */ if (ctx->serial) { /* Explicitly clear the buffer full signal handler */ if (ctx->buffer_full_id) { g_signal_handler_disconnect (ctx->serial, ctx->buffer_full_id); ctx->buffer_full_id = 0; } mm_port_serial_close (ctx->serial); g_object_unref (ctx->serial); } if (g_str_equal (mm_kernel_device_get_subsystem (self->priv->port), "wwan")) subsys = MM_PORT_SUBSYS_WWAN; /* Open the QCDM port */ ctx->serial = MM_PORT_SERIAL (mm_port_serial_qcdm_new (mm_kernel_device_get_name (self->priv->port), subsys)); if (!ctx->serial) { port_probe_task_return_error (self, g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "(%s/%s) Couldn't create QCDM port", mm_kernel_device_get_subsystem (self->priv->port), mm_kernel_device_get_name (self->priv->port))); return G_SOURCE_REMOVE; } /* Setup port if needed */ common_serial_port_setup (self, ctx->serial); /* Try to open the port */ if (!mm_port_serial_open (ctx->serial, &error)) { port_probe_task_return_error (self, g_error_new (MM_SERIAL_ERROR, MM_SERIAL_ERROR_OPEN_FAILED, "(%s/%s) Failed to open QCDM port: %s", mm_kernel_device_get_subsystem (self->priv->port), mm_kernel_device_get_name (self->priv->port), (error ? error->message : "unknown error"))); g_clear_error (&error); return G_SOURCE_REMOVE; } /* Build up the probe command; 0x7E is the frame marker, so put one at the * beginning of the buffer to ensure that the device discards any AT * commands that probing might have sent earlier. Should help devices * respond more quickly and speed up QCDM probing. */ verinfo = g_byte_array_sized_new (10); g_byte_array_append (verinfo, &marker, 1); len = qcdm_cmd_version_info_new ((char *) (verinfo->data + 1), 9); if (len <= 0) { g_byte_array_unref (verinfo); port_probe_task_return_error (self, g_error_new (MM_SERIAL_ERROR, MM_SERIAL_ERROR_OPEN_FAILED, "(%s/%s) Failed to create QCDM version info command", mm_kernel_device_get_subsystem (self->priv->port), mm_kernel_device_get_name (self->priv->port))); return G_SOURCE_REMOVE; } verinfo->len = len + 1; /* Queuing the command takes ownership over it; save it for the second try */ verinfo2 = g_byte_array_sized_new (verinfo->len); g_byte_array_append (verinfo2, verinfo->data, verinfo->len); g_object_set_data_full (G_OBJECT (self), "cmd2", verinfo2, (GDestroyNotify) g_byte_array_unref); mm_port_serial_qcdm_command (MM_PORT_SERIAL_QCDM (ctx->serial), verinfo, 3, NULL, (GAsyncReadyCallback) serial_probe_qcdm_parse_response, self); g_byte_array_unref (verinfo); return G_SOURCE_REMOVE; } /***************************************************************/ /* AT */ static const gchar *non_at_strings[] = { /* Option Icera-based devices */ "option/faema_", "os_logids.h", /* Sierra CnS port */ "NETWORK SERVICE CHANGE", NULL }; static const guint8 zerobuf[32] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; static gboolean is_non_at_response (const guint8 *data, gsize len) { const gchar **iter; gsize iter_len; gsize i; /* Some devices (observed on a ZTE branded "QUALCOMM INCORPORATED" model * "154") spew NULLs from some ports. */ for (i = 0; (len >= sizeof (zerobuf)) && (i < len - sizeof (zerobuf)); i++) { if (!memcmp (&data[i], zerobuf, sizeof (zerobuf))) return TRUE; } /* Check for a well-known non-AT response. There are some ports (eg many * Icera-based chipsets, Qualcomm Gobi devices before their firmware is * loaded, Sierra CnS ports) that just shouldn't be probed for AT capability * if we get a certain response since that response means they aren't AT * ports. Also, kernel bugs (at least with 2.6.31 and 2.6.32) trigger port * flow control kernel oopses if we read too much data for these ports. */ for (iter = &non_at_strings[0]; iter && *iter; iter++) { /* Search in the response for the item; the response could have embedded * nulls so we can't use memcmp() or strstr() on the whole response. */ iter_len = strlen (*iter); for (i = 0; (len >= iter_len) && (i < len - iter_len); i++) { if (!memcmp (&data[i], *iter, iter_len)) return TRUE; } } return FALSE; } static void serial_probe_at_xmm_result_processor (MMPortProbe *self, GVariant *result) { if (result) { /* If any result given, it must be a string */ g_assert (g_variant_is_of_type (result, G_VARIANT_TYPE_STRING)); if (strstr (g_variant_get_string (result, NULL), "XACT:")) { mm_port_probe_set_result_at_xmm (self, TRUE); return; } } mm_port_probe_set_result_at_xmm (self, FALSE); } static void serial_probe_at_icera_result_processor (MMPortProbe *self, GVariant *result) { if (result) { /* If any result given, it must be a string */ g_assert (g_variant_is_of_type (result, G_VARIANT_TYPE_STRING)); if (strstr (g_variant_get_string (result, NULL), "%IPSYS:")) { mm_port_probe_set_result_at_icera (self, TRUE); return; } } mm_port_probe_set_result_at_icera (self, FALSE); } static void serial_probe_at_product_result_processor (MMPortProbe *self, GVariant *result) { if (result) { /* If any result given, it must be a string */ g_assert (g_variant_is_of_type (result, G_VARIANT_TYPE_STRING)); mm_port_probe_set_result_at_product (self, g_variant_get_string (result, NULL)); return; } mm_port_probe_set_result_at_product (self, NULL); } static void serial_probe_at_vendor_result_processor (MMPortProbe *self, GVariant *result) { if (result) { /* If any result given, it must be a string */ g_assert (g_variant_is_of_type (result, G_VARIANT_TYPE_STRING)); mm_port_probe_set_result_at_vendor (self, g_variant_get_string (result, NULL)); return; } mm_port_probe_set_result_at_vendor (self, NULL); } static void serial_probe_at_result_processor (MMPortProbe *self, GVariant *result) { if (result) { /* If any result given, it must be a boolean */ g_assert (g_variant_is_of_type (result, G_VARIANT_TYPE_BOOLEAN)); if (g_variant_get_boolean (result)) { mm_port_probe_set_result_at (self, TRUE); return; } } mm_port_probe_set_result_at (self, FALSE); } static void serial_probe_at_parse_response (MMPortSerialAt *port, GAsyncResult *res, MMPortProbe *self) { GVariant *result = NULL; GError *result_error = NULL; g_autofree gchar *response = NULL; GError *error = NULL; PortProbeRunContext *ctx; g_assert (self->priv->task); ctx = g_task_get_task_data (self->priv->task); /* If already cancelled, do nothing else */ if (port_probe_task_return_error_if_cancelled (self)) return; /* If AT probing cancelled, end this partial probing */ if (g_cancellable_is_cancelled (ctx->at_probing_cancellable)) { mm_obj_dbg (self, "no need to keep on probing the port for AT support"); ctx->at_result_processor (self, NULL); serial_probe_schedule (self); return; } response = mm_port_serial_at_command_finish (port, res, &error); if (!ctx->at_commands->response_processor (ctx->at_commands->command, response, !!ctx->at_commands[1].command, error, &result, &result_error)) { /* Were we told to abort the whole probing? */ if (result_error) { port_probe_task_return_error (self, g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "(%s/%s) error while probing AT features: %s", mm_kernel_device_get_subsystem (self->priv->port), mm_kernel_device_get_name (self->priv->port), result_error->message)); goto out; } /* Go on to next command */ ctx->at_commands++; if (!ctx->at_commands->command) { /* Was it the last command in the group? If so, * end this partial probing */ ctx->at_result_processor (self, NULL); /* Reschedule */ serial_probe_schedule (self); goto out; } /* Schedule the next command in the probing group */ if (ctx->at_commands_wait_secs == 0) ctx->source_id = g_idle_add ((GSourceFunc) serial_probe_at, self); else { mm_obj_dbg (self, "re-scheduling next command in probing group in %u seconds...", ctx->at_commands_wait_secs); ctx->source_id = g_timeout_add_seconds (ctx->at_commands_wait_secs, (GSourceFunc) serial_probe_at, self); } goto out; } /* Run result processor. * Note that custom init commands are allowed to not return anything */ ctx->at_result_processor (self, result); /* Reschedule probing */ serial_probe_schedule (self); out: g_clear_pointer (&result, g_variant_unref); g_clear_error (&error); g_clear_error (&result_error); } static gboolean serial_probe_at (MMPortProbe *self) { PortProbeRunContext *ctx; g_assert (self->priv->task); ctx = g_task_get_task_data (self->priv->task); ctx->source_id = 0; /* If already cancelled, do nothing else */ if (port_probe_task_return_error_if_cancelled (self)) return G_SOURCE_REMOVE; /* If AT probing cancelled, end this partial probing */ if (g_cancellable_is_cancelled (ctx->at_probing_cancellable)) { mm_obj_dbg (self, "no need to launch probing for AT support"); ctx->at_result_processor (self, NULL); serial_probe_schedule (self); return G_SOURCE_REMOVE; } mm_port_serial_at_command ( MM_PORT_SERIAL_AT (ctx->serial), ctx->at_commands->command, ctx->at_commands->timeout, FALSE, FALSE, ctx->at_probing_cancellable, (GAsyncReadyCallback)serial_probe_at_parse_response, self); return G_SOURCE_REMOVE; } static const MMPortProbeAtCommand at_probing[] = { { "AT", 3, mm_port_probe_response_processor_is_at }, { "AT", 3, mm_port_probe_response_processor_is_at }, { "AT", 3, mm_port_probe_response_processor_is_at }, { "AT", 3, mm_port_probe_response_processor_is_at }, { "AT", 3, mm_port_probe_response_processor_is_at }, { "AT", 3, mm_port_probe_response_processor_is_at }, { NULL } }; static const MMPortProbeAtCommand vendor_probing[] = { { "+CGMI", 3, mm_port_probe_response_processor_string }, { "+GMI", 3, mm_port_probe_response_processor_string }, { "I", 3, mm_port_probe_response_processor_string }, { NULL } }; static const MMPortProbeAtCommand product_probing[] = { { "+CGMM", 3, mm_port_probe_response_processor_string }, { "+GMM", 3, mm_port_probe_response_processor_string }, { "I", 3, mm_port_probe_response_processor_string }, { NULL } }; static const MMPortProbeAtCommand icera_probing[] = { { "%IPSYS?", 3, mm_port_probe_response_processor_string }, { "%IPSYS?", 3, mm_port_probe_response_processor_string }, { "%IPSYS?", 3, mm_port_probe_response_processor_string }, { NULL } }; static const MMPortProbeAtCommand xmm_probing[] = { { "+XACT=?", 3, mm_port_probe_response_processor_string }, { NULL } }; static void at_custom_init_ready (MMPortProbe *self, GAsyncResult *res) { GError *error = NULL; PortProbeRunContext *ctx; g_assert (self->priv->task); ctx = g_task_get_task_data (self->priv->task); if (!ctx->at_custom_init_finish (self, res, &error)) { /* All errors propagated up end up forcing an UNSUPPORTED result */ port_probe_task_return_error (self, error); return; } /* Keep on with remaining probings */ ctx->at_custom_init_run = TRUE; serial_probe_schedule (self); } /***************************************************************/ static void serial_probe_schedule (MMPortProbe *self) { PortProbeRunContext *ctx; g_assert (self->priv->task); ctx = g_task_get_task_data (self->priv->task); /* If already cancelled, do nothing else */ if (port_probe_task_return_error_if_cancelled (self)) return; /* If we got some custom initialization setup requested, go on with it * first. We completely ignore the custom initialization if the serial port * that we receive in the context isn't an AT port (e.g. if it was flagged * as not being an AT port early) */ if (!ctx->at_custom_init_run && ctx->at_custom_init && ctx->at_custom_init_finish && MM_IS_PORT_SERIAL_AT (ctx->serial)) { ctx->at_custom_init (self, MM_PORT_SERIAL_AT (ctx->serial), ctx->at_probing_cancellable, (GAsyncReadyCallback) at_custom_init_ready, NULL); return; } /* Cleanup */ ctx->at_result_processor = NULL; ctx->at_commands = NULL; ctx->at_commands_wait_secs = 0; /* AT check requested and not already probed? */ if ((ctx->flags & MM_PORT_PROBE_AT) && !(self->priv->flags & MM_PORT_PROBE_AT)) { /* Prepare AT probing */ if (ctx->at_custom_probe) ctx->at_commands = ctx->at_custom_probe; else ctx->at_commands = at_probing; ctx->at_result_processor = serial_probe_at_result_processor; } /* Vendor requested and not already probed? */ else if ((ctx->flags & MM_PORT_PROBE_AT_VENDOR) && !(self->priv->flags & MM_PORT_PROBE_AT_VENDOR)) { /* Prepare AT vendor probing */ ctx->at_result_processor = serial_probe_at_vendor_result_processor; ctx->at_commands = vendor_probing; } /* Product requested and not already probed? */ else if ((ctx->flags & MM_PORT_PROBE_AT_PRODUCT) && !(self->priv->flags & MM_PORT_PROBE_AT_PRODUCT)) { /* Prepare AT product probing */ ctx->at_result_processor = serial_probe_at_product_result_processor; ctx->at_commands = product_probing; } /* Icera support check requested and not already done? */ else if ((ctx->flags & MM_PORT_PROBE_AT_ICERA) && !(self->priv->flags & MM_PORT_PROBE_AT_ICERA)) { /* Prepare AT product probing */ ctx->at_result_processor = serial_probe_at_icera_result_processor; ctx->at_commands = icera_probing; /* By default, wait 2 seconds between ICERA probing retries */ ctx->at_commands_wait_secs = 2; } /* XMM support check requested and not already done? */ else if ((ctx->flags & MM_PORT_PROBE_AT_XMM) && !(self->priv->flags & MM_PORT_PROBE_AT_XMM)) { /* Prepare AT product probing */ ctx->at_result_processor = serial_probe_at_xmm_result_processor; ctx->at_commands = xmm_probing; } /* If a next AT group detected, go for it */ if (ctx->at_result_processor && ctx->at_commands) { ctx->source_id = g_idle_add ((GSourceFunc) serial_probe_at, self); return; } /* QCDM requested and not already probed? */ if ((ctx->flags & MM_PORT_PROBE_QCDM) && !(self->priv->flags & MM_PORT_PROBE_QCDM)) { ctx->source_id = g_idle_add ((GSourceFunc) serial_probe_qcdm, self); return; } /* All done! */ port_probe_task_return_boolean (self, TRUE); } static void serial_flash_ready (MMPortSerial *port, GAsyncResult *res, MMPortProbe *self) { mm_port_serial_flash_finish (port, res, NULL); /* Schedule probing */ serial_probe_schedule (self); } static void serial_buffer_full (MMPortSerial *serial, GByteArray *buffer, MMPortProbe *self) { PortProbeRunContext *ctx; if (!is_non_at_response (buffer->data, buffer->len)) return; g_assert (self->priv->task); ctx = g_task_get_task_data (self->priv->task); mm_obj_dbg (self, "serial buffer full"); /* Don't explicitly close the AT port, just end the AT probing * (or custom init probing) */ mm_port_probe_set_result_at (self, FALSE); g_cancellable_cancel (ctx->at_probing_cancellable); } static gboolean serial_parser_filter_cb (gpointer filter, gpointer user_data, GString *response, GError **error) { if (is_non_at_response ((const guint8 *) response->str, response->len)) { g_set_error (error, MM_SERIAL_ERROR, MM_SERIAL_ERROR_PARSE_FAILED, "Not an AT response"); return FALSE; } return TRUE; } static gboolean serial_open_at (MMPortProbe *self) { GError *error = NULL; PortProbeRunContext *ctx; g_assert (self->priv->task); ctx = g_task_get_task_data (self->priv->task); ctx->source_id = 0; /* If already cancelled, do nothing else */ if (port_probe_task_return_error_if_cancelled (self)) return G_SOURCE_REMOVE; /* Create AT serial port if not done before */ if (!ctx->serial) { gpointer parser; MMPortSubsys subsys = MM_PORT_SUBSYS_TTY; if (g_str_equal (mm_kernel_device_get_subsystem (self->priv->port), "usbmisc")) subsys = MM_PORT_SUBSYS_USBMISC; else if (g_str_equal (mm_kernel_device_get_subsystem (self->priv->port), "rpmsg")) subsys = MM_PORT_SUBSYS_RPMSG; else if (g_str_equal (mm_kernel_device_get_subsystem (self->priv->port), "wwan")) subsys = MM_PORT_SUBSYS_WWAN; ctx->serial = MM_PORT_SERIAL (mm_port_serial_at_new (mm_kernel_device_get_name (self->priv->port), subsys)); if (!ctx->serial) { port_probe_task_return_error (self, g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "(%s/%s) couldn't create AT port", mm_kernel_device_get_subsystem (self->priv->port), mm_kernel_device_get_name (self->priv->port))); return G_SOURCE_REMOVE; } g_object_set (ctx->serial, MM_PORT_SERIAL_SPEW_CONTROL, TRUE, MM_PORT_SERIAL_SEND_DELAY, (guint64)(subsys == MM_PORT_SUBSYS_TTY ? ctx->at_send_delay : 0), MM_PORT_SERIAL_AT_REMOVE_ECHO, ctx->at_remove_echo, MM_PORT_SERIAL_AT_SEND_LF, ctx->at_send_lf, NULL); common_serial_port_setup (self, ctx->serial); parser = mm_serial_parser_v1_new (); mm_serial_parser_v1_add_filter (parser, serial_parser_filter_cb, NULL); mm_port_serial_at_set_response_parser (MM_PORT_SERIAL_AT (ctx->serial), mm_serial_parser_v1_parse, parser, mm_serial_parser_v1_destroy); } /* Try to open the port */ if (!mm_port_serial_open (ctx->serial, &error)) { /* Abort if maximum number of open tries reached */ if (++ctx->at_open_tries > 4) { /* took too long to open the port; give up */ port_probe_task_return_error (self, g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "(%s/%s) failed to open port after 4 tries", mm_kernel_device_get_subsystem (self->priv->port), mm_kernel_device_get_name (self->priv->port))); g_clear_error (&error); return G_SOURCE_REMOVE; } if (g_error_matches (error, MM_SERIAL_ERROR, MM_SERIAL_ERROR_OPEN_FAILED_NO_DEVICE)) { /* this is nozomi being dumb; try again */ ctx->source_id = g_timeout_add_seconds (1, (GSourceFunc) serial_open_at, self); g_clear_error (&error); return G_SOURCE_REMOVE; } port_probe_task_return_error (self, g_error_new (MM_SERIAL_ERROR, MM_SERIAL_ERROR_OPEN_FAILED, "(%s/%s) failed to open port: %s", mm_kernel_device_get_subsystem (self->priv->port), mm_kernel_device_get_name (self->priv->port), (error ? error->message : "unknown error"))); g_clear_error (&error); return G_SOURCE_REMOVE; } /* success, start probing */ ctx->buffer_full_id = g_signal_connect (ctx->serial, "buffer-full", G_CALLBACK (serial_buffer_full), self); mm_port_serial_flash (MM_PORT_SERIAL (ctx->serial), 100, TRUE, (GAsyncReadyCallback) serial_flash_ready, self); return G_SOURCE_REMOVE; } static void at_cancellable_cancel (GCancellable *cancellable, PortProbeRunContext *ctx) { /* Avoid trying to disconnect cancellable on the handler, or we'll deadlock */ ctx->at_probing_cancellable_linked = 0; g_cancellable_cancel (ctx->at_probing_cancellable); } gboolean mm_port_probe_run_cancel_at_probing (MMPortProbe *self) { PortProbeRunContext *ctx; g_return_val_if_fail (MM_IS_PORT_PROBE (self), FALSE); if (!self->priv->task) return FALSE; ctx = g_task_get_task_data (self->priv->task); if (g_cancellable_is_cancelled (ctx->at_probing_cancellable)) return FALSE; mm_obj_dbg (self, "requested to cancel all AT probing"); g_cancellable_cancel (ctx->at_probing_cancellable); return TRUE; } gboolean mm_port_probe_run_finish (MMPortProbe *self, GAsyncResult *result, GError **error) { g_return_val_if_fail (MM_IS_PORT_PROBE (self), FALSE); g_return_val_if_fail (G_IS_TASK (result), FALSE); return g_task_propagate_boolean (G_TASK (result), error); } void mm_port_probe_run (MMPortProbe *self, MMPortProbeFlag flags, guint64 at_send_delay, gboolean at_remove_echo, gboolean at_send_lf, const MMPortProbeAtCommand *at_custom_probe, const MMAsyncMethod *at_custom_init, gboolean qcdm_required, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { PortProbeRunContext *ctx; gchar *probe_list_str; guint32 i; g_return_if_fail (MM_IS_PORT_PROBE (self)); g_return_if_fail (flags != MM_PORT_PROBE_NONE); g_return_if_fail (callback != NULL); /* Shouldn't schedule more than one probing at a time */ g_assert (self->priv->task == NULL); self->priv->task = g_task_new (self, cancellable, callback, user_data); /* Task context */ ctx = g_slice_new0 (PortProbeRunContext); ctx->at_send_delay = at_send_delay; ctx->at_remove_echo = at_remove_echo; ctx->at_send_lf = at_send_lf; ctx->flags = MM_PORT_PROBE_NONE; ctx->at_custom_probe = at_custom_probe; ctx->at_custom_init = at_custom_init ? (MMPortProbeAtCustomInit)at_custom_init->async : NULL; ctx->at_custom_init_finish = at_custom_init ? (MMPortProbeAtCustomInitFinish)at_custom_init->finish : NULL; ctx->qcdm_required = qcdm_required; ctx->cancellable = cancellable ? g_object_ref (cancellable) : NULL; /* The context will be owned by the task */ g_task_set_task_data (self->priv->task, ctx, (GDestroyNotify) port_probe_run_context_free); /* If we're told to completely ignore the port, don't do any probing */ if (self->priv->is_ignored) { mm_obj_dbg (self, "port probing finished: skipping for ignored port"); port_probe_task_return_boolean (self, TRUE); return; } /* If this is a port flagged as a GPS port, don't do any other probing */ if (self->priv->is_gps) { mm_obj_dbg (self, "GPS port detected"); mm_port_probe_set_result_at (self, FALSE); mm_port_probe_set_result_qcdm (self, FALSE); mm_port_probe_set_result_qmi (self, FALSE); mm_port_probe_set_result_mbim (self, FALSE); } /* If this is a port flagged as an audio port, don't do any other probing */ if (self->priv->is_audio) { mm_obj_dbg (self, "audio port detected"); mm_port_probe_set_result_at (self, FALSE); mm_port_probe_set_result_qcdm (self, FALSE); mm_port_probe_set_result_qmi (self, FALSE); mm_port_probe_set_result_mbim (self, FALSE); } /* If this is a port flagged as being an AT port, don't do any other probing */ if (self->priv->maybe_at) { mm_obj_dbg (self, "no QCDM/QMI/MBIM probing in possible AT port"); mm_port_probe_set_result_qcdm (self, FALSE); mm_port_probe_set_result_qmi (self, FALSE); mm_port_probe_set_result_mbim (self, FALSE); } /* If this is a port flagged as being a QCDM port, don't do any other probing */ if (self->priv->maybe_qcdm) { mm_obj_dbg (self, "no AT/QMI/MBIM probing in possible QCDM port"); mm_port_probe_set_result_at (self, FALSE); mm_port_probe_set_result_qmi (self, FALSE); mm_port_probe_set_result_mbim (self, FALSE); } /* If this is a port flagged as being a QMI port, don't do any other probing */ if (self->priv->maybe_qmi) { mm_obj_dbg (self, "no AT/QCDM/MBIM probing in possible QMI port"); mm_port_probe_set_result_at (self, FALSE); mm_port_probe_set_result_qcdm (self, FALSE); mm_port_probe_set_result_mbim (self, FALSE); } /* If this is a port flagged as being a MBIM port, don't do any other probing */ if (self->priv->maybe_mbim) { mm_obj_dbg (self, "no AT/QCDM/QMI probing in possible MBIM port"); mm_port_probe_set_result_at (self, FALSE); mm_port_probe_set_result_qcdm (self, FALSE); mm_port_probe_set_result_qmi (self, FALSE); } /* Check if we already have the requested probing results. * We will fix here the 'ctx->flags' so that we only request probing * for the missing things. */ for (i = MM_PORT_PROBE_AT; i <= MM_PORT_PROBE_MBIM; i = (i << 1)) { if ((flags & i) && !(self->priv->flags & i)) ctx->flags += i; } /* All requested probings already available? If so, we're done */ if (!ctx->flags) { mm_obj_dbg (self, "port probing finished: no more probings needed"); port_probe_task_return_boolean (self, TRUE); return; } /* Log the probes scheduled to be run */ probe_list_str = mm_port_probe_flag_build_string_from_mask (ctx->flags); mm_obj_dbg (self, "launching port probing: '%s'", probe_list_str); g_free (probe_list_str); /* If any AT probing is needed, start by opening as AT port */ if (ctx->flags & MM_PORT_PROBE_AT || ctx->flags & MM_PORT_PROBE_AT_VENDOR || ctx->flags & MM_PORT_PROBE_AT_PRODUCT || ctx->flags & MM_PORT_PROBE_AT_ICERA || ctx->flags & MM_PORT_PROBE_AT_XMM) { ctx->at_probing_cancellable = g_cancellable_new (); /* If the main cancellable is cancelled, so will be the at-probing one */ if (cancellable) ctx->at_probing_cancellable_linked = g_cancellable_connect (cancellable, (GCallback) at_cancellable_cancel, ctx, NULL); ctx->source_id = g_idle_add ((GSourceFunc) serial_open_at, self); return; } /* If QCDM probing needed, start by opening as QCDM port */ if (ctx->flags & MM_PORT_PROBE_QCDM) { ctx->source_id = g_idle_add ((GSourceFunc) serial_probe_qcdm, self); return; } /* If QMI/MBIM probing needed, go on */ if (ctx->flags & MM_PORT_PROBE_QMI || ctx->flags & MM_PORT_PROBE_MBIM) { ctx->source_id = g_idle_add ((GSourceFunc) wdm_probe, self); return; } /* Shouldn't happen */ g_assert_not_reached (); } gboolean mm_port_probe_is_at (MMPortProbe *self) { const gchar *subsys; g_return_val_if_fail (MM_IS_PORT_PROBE (self), FALSE); subsys = mm_kernel_device_get_subsystem (self->priv->port); if (g_str_equal (subsys, "net")) return FALSE; return (self->priv->flags & MM_PORT_PROBE_AT ? self->priv->is_at : FALSE); } gboolean mm_port_probe_list_has_at_port (GList *list) { GList *l; for (l = list; l; l = g_list_next (l)){ MMPortProbe *probe = MM_PORT_PROBE (l->data); if (!probe->priv->is_ignored && probe->priv->flags & MM_PORT_PROBE_AT && probe->priv->is_at) return TRUE; } return FALSE; } gboolean mm_port_probe_is_qcdm (MMPortProbe *self) { g_return_val_if_fail (MM_IS_PORT_PROBE (self), FALSE); return (self->priv->flags & MM_PORT_PROBE_QCDM ? self->priv->is_qcdm : FALSE); } gboolean mm_port_probe_is_qmi (MMPortProbe *self) { g_return_val_if_fail (MM_IS_PORT_PROBE (self), FALSE); return (self->priv->flags & MM_PORT_PROBE_QMI ? self->priv->is_qmi : FALSE); } gboolean mm_port_probe_list_has_qmi_port (GList *list) { GList *l; for (l = list; l; l = g_list_next (l)) { MMPortProbe *probe = MM_PORT_PROBE (l->data); if (!probe->priv->is_ignored && mm_port_probe_is_qmi (probe)) return TRUE; } return FALSE; } gboolean mm_port_probe_is_mbim (MMPortProbe *self) { g_return_val_if_fail (MM_IS_PORT_PROBE (self), FALSE); return (self->priv->flags & MM_PORT_PROBE_MBIM ? self->priv->is_mbim : FALSE); } gboolean mm_port_probe_list_has_mbim_port (GList *list) { GList *l; for (l = list; l; l = g_list_next (l)) { MMPortProbe *probe = MM_PORT_PROBE (l->data); if (!probe->priv->is_ignored && mm_port_probe_is_mbim (probe)) return TRUE; } return FALSE; } MMPortType mm_port_probe_get_port_type (MMPortProbe *self) { const gchar *subsys; g_return_val_if_fail (MM_IS_PORT_PROBE (self), FALSE); subsys = mm_kernel_device_get_subsystem (self->priv->port); if (g_str_equal (subsys, "net")) return MM_PORT_TYPE_NET; #if defined WITH_QMI if (self->priv->flags & MM_PORT_PROBE_QMI && self->priv->is_qmi) return MM_PORT_TYPE_QMI; #endif #if defined WITH_MBIM if (self->priv->flags & MM_PORT_PROBE_MBIM && self->priv->is_mbim) return MM_PORT_TYPE_MBIM; #endif if (self->priv->flags & MM_PORT_PROBE_QCDM && self->priv->is_qcdm) return MM_PORT_TYPE_QCDM; if (self->priv->flags & MM_PORT_PROBE_AT && self->priv->is_at) return MM_PORT_TYPE_AT; if (self->priv->is_gps) return MM_PORT_TYPE_GPS; if (self->priv->is_audio) return MM_PORT_TYPE_AUDIO; return MM_PORT_TYPE_UNKNOWN; } MMDevice * mm_port_probe_peek_device (MMPortProbe *self) { g_return_val_if_fail (MM_IS_PORT_PROBE (self), NULL); return self->priv->device; } MMDevice * mm_port_probe_get_device (MMPortProbe *self) { g_return_val_if_fail (MM_IS_PORT_PROBE (self), NULL); return MM_DEVICE (g_object_ref (self->priv->device)); } MMKernelDevice * mm_port_probe_peek_port (MMPortProbe *self) { g_return_val_if_fail (MM_IS_PORT_PROBE (self), NULL); return self->priv->port; }; MMKernelDevice * mm_port_probe_get_port (MMPortProbe *self) { g_return_val_if_fail (MM_IS_PORT_PROBE (self), NULL); return MM_KERNEL_DEVICE (g_object_ref (self->priv->port)); }; const gchar * mm_port_probe_get_vendor (MMPortProbe *self) { g_return_val_if_fail (MM_IS_PORT_PROBE (self), FALSE); return (self->priv->flags & MM_PORT_PROBE_AT_VENDOR ? self->priv->vendor : NULL); } const gchar * mm_port_probe_get_product (MMPortProbe *self) { g_return_val_if_fail (MM_IS_PORT_PROBE (self), FALSE); return (self->priv->flags & MM_PORT_PROBE_AT_PRODUCT ? self->priv->product : NULL); } gboolean mm_port_probe_is_icera (MMPortProbe *self) { g_return_val_if_fail (MM_IS_PORT_PROBE (self), FALSE); return (self->priv->flags & MM_PORT_PROBE_AT_ICERA ? self->priv->is_icera : FALSE); } gboolean mm_port_probe_list_is_icera (GList *probes) { GList *l; for (l = probes; l; l = g_list_next (l)) { if (mm_port_probe_is_icera (MM_PORT_PROBE (l->data))) return TRUE; } return FALSE; } gboolean mm_port_probe_is_xmm (MMPortProbe *self) { g_return_val_if_fail (MM_IS_PORT_PROBE (self), FALSE); return (self->priv->flags & MM_PORT_PROBE_AT_XMM ? self->priv->is_xmm : FALSE); } gboolean mm_port_probe_list_is_xmm (GList *probes) { GList *l; for (l = probes; l; l = g_list_next (l)) { if (mm_port_probe_is_xmm (MM_PORT_PROBE (l->data))) return TRUE; } return FALSE; } gboolean mm_port_probe_is_ignored (MMPortProbe *self) { g_return_val_if_fail (MM_IS_PORT_PROBE (self), FALSE); return self->priv->is_ignored; } const gchar * mm_port_probe_get_port_name (MMPortProbe *self) { g_return_val_if_fail (MM_IS_PORT_PROBE (self), NULL); return mm_kernel_device_get_name (self->priv->port); } const gchar * mm_port_probe_get_port_subsys (MMPortProbe *self) { g_return_val_if_fail (MM_IS_PORT_PROBE (self), NULL); return mm_kernel_device_get_subsystem (self->priv->port); } static void initialize_port_type_hints (MMPortProbe *self) { g_autoptr(GString) udev_tags = NULL; guint n_udev_hints = 0; gboolean auto_maybe_qmi = FALSE; gboolean auto_maybe_mbim = FALSE; gboolean auto_maybe_at = FALSE; gboolean auto_maybe_qcdm = FALSE; gboolean auto_ignored = FALSE; #define ADD_HINT_FROM_UDEV_TAG(TAG, FIELD) do { \ if (!self->priv->FIELD && \ mm_kernel_device_get_property_as_boolean (self->priv->port, TAG)) { \ mm_obj_dbg (self, "port type hint detected in udev tag: %s", TAG); \ self->priv->FIELD = TRUE; \ n_udev_hints++; \ if (!udev_tags) \ udev_tags = g_string_new (TAG); \ else \ g_string_append_printf (udev_tags, ", %s", TAG); \ } \ } while (0) /* Process udev-configured port type hints */ ADD_HINT_FROM_UDEV_TAG (ID_MM_PORT_TYPE_GPS, is_gps); ADD_HINT_FROM_UDEV_TAG (ID_MM_PORT_TYPE_AUDIO, is_audio); ADD_HINT_FROM_UDEV_TAG (ID_MM_PORT_TYPE_AT_PRIMARY, maybe_at); ADD_HINT_FROM_UDEV_TAG (ID_MM_PORT_TYPE_AT_SECONDARY, maybe_at); ADD_HINT_FROM_UDEV_TAG (ID_MM_PORT_TYPE_AT_PPP, maybe_at); ADD_HINT_FROM_UDEV_TAG (ID_MM_PORT_TYPE_QCDM, maybe_qcdm); ADD_HINT_FROM_UDEV_TAG (ID_MM_PORT_TYPE_QMI, maybe_qmi); ADD_HINT_FROM_UDEV_TAG (ID_MM_PORT_TYPE_MBIM, maybe_mbim); /* Warn if more than one given at the same time */ if (n_udev_hints > 1) mm_obj_warn (self, "multiple incompatible port type hints configured via udev: %s", udev_tags->str); /* Process automatic port type hints, and warn if the hint doesn't match the * one provided via udev. The udev-provided hints are always preferred. */ if (!g_strcmp0 (mm_kernel_device_get_subsystem (self->priv->port), "usbmisc")) { const gchar *driver; driver = mm_kernel_device_get_driver (self->priv->port); if (!g_strcmp0 (driver, "qmi_wwan")) { mm_obj_dbg (self, "port may be QMI based on the driver in use"); auto_maybe_qmi = TRUE; } else if (!g_strcmp0 (driver, "cdc_mbim")) { mm_obj_dbg (self, "port may be MBIM based on the driver in use"); auto_maybe_mbim = TRUE; } else { mm_obj_dbg (self, "port may be AT based on the driver in use: %s", driver); auto_maybe_at = TRUE; } } else if (!g_strcmp0 (mm_kernel_device_get_subsystem (self->priv->port), "wwan")) { /* Linux >= 5.14 has at 'type' attribute specifying the type of port */ if (mm_kernel_device_has_attribute (self->priv->port, "type")) { const gchar *type; type = mm_kernel_device_get_attribute (self->priv->port, "type"); if (!g_strcmp0 (type, "AT")) { mm_obj_dbg (self, "port may be AT based on the wwan type attribute"); auto_maybe_at = TRUE; } else if (!g_strcmp0 (type, "MBIM")) { mm_obj_dbg (self, "port may be MBIM based on the wwan type attribute"); auto_maybe_mbim = TRUE; } else if (!g_strcmp0 (type, "QMI")) { mm_obj_dbg (self, "port may be QMI based on the wwan type attribute"); auto_maybe_qmi = TRUE; } else if (!g_strcmp0 (type, "QCDM")) { mm_obj_dbg (self, "port may be QCDM based on the wwan type attribute"); auto_maybe_qcdm = TRUE; } else if (!g_strcmp0 (type, "FIREHOSE")) { mm_obj_dbg (self, "port may be FIREHOSE based on the wwan type attribute"); auto_ignored = TRUE; } } /* Linux 5.13 does not have 'type' attribute yet, match kernel name instead */ else { const gchar *name; name = mm_kernel_device_get_name (self->priv->port); if (g_str_has_suffix (name, "AT")) { mm_obj_dbg (self, "port may be AT based on the wwan device name"); auto_maybe_at = TRUE; } else if (g_str_has_suffix (name, "MBIM")) { mm_obj_dbg (self, "port may be MBIM based on the wwan device name"); auto_maybe_mbim = TRUE; } else if (g_str_has_suffix (name, "QMI")) { mm_obj_dbg (self, "port may be QMI based on the wwan device name"); auto_maybe_qmi = TRUE; } else if (g_str_has_suffix (name, "QCDM")) { mm_obj_dbg (self, "port may be QCDM based on the wwan device name"); auto_maybe_qcdm = TRUE; } else if (g_str_has_suffix (name, "FIREHOSE")) { mm_obj_dbg (self, "port may be FIREHOSE based on the wwan device name"); auto_ignored = TRUE; } } } g_assert ((auto_maybe_qmi + auto_maybe_mbim + auto_maybe_at + auto_maybe_qcdm + auto_ignored) <= 1); #define PROCESS_AUTO_HINTS(TYPE, FIELD) do { \ if (auto_##FIELD) { \ if (n_udev_hints > 0 && !self->priv->FIELD) \ mm_obj_warn (self, "overriding type in possible " TYPE " port with udev tag: %s", udev_tags->str); \ else \ self->priv->FIELD = TRUE; \ } \ } while (0) PROCESS_AUTO_HINTS ("QMI", maybe_qmi); PROCESS_AUTO_HINTS ("MBIM", maybe_mbim); PROCESS_AUTO_HINTS ("AT", maybe_at); PROCESS_AUTO_HINTS ("QCDM", maybe_qcdm); #undef PROCESS_AUTO_HINTS mm_obj_dbg (self, "port type hints loaded: AT %s, QMI %s, MBIM %s, QCDM %s, AUDIO %s, GPS %s", self->priv->maybe_at ? "yes" : "no", self->priv->maybe_qmi ? "yes" : "no", self->priv->maybe_mbim ? "yes" : "no", self->priv->maybe_qcdm ? "yes" : "no", self->priv->is_audio ? "yes" : "no", self->priv->is_gps ? "yes" : "no"); /* Regardless of the type, the port may be ignored */ if (mm_kernel_device_get_property_as_boolean (self->priv->port, ID_MM_PORT_IGNORE)) { mm_obj_dbg (self, "port is ignored via udev tag"); self->priv->is_ignored = TRUE; } else if (auto_ignored) { mm_obj_dbg (self, "port is ignored via automatic rules"); self->priv->is_ignored = TRUE; } } /*****************************************************************************/ static gchar * log_object_build_id (MMLogObject *_self) { MMPortProbe *self; self = MM_PORT_PROBE (_self); return g_strdup_printf ("%s/probe", mm_kernel_device_get_name (self->priv->port)); } /*****************************************************************************/ MMPortProbe * mm_port_probe_new (MMDevice *device, MMKernelDevice *port) { return MM_PORT_PROBE (g_object_new (MM_TYPE_PORT_PROBE, MM_PORT_PROBE_DEVICE, device, MM_PORT_PROBE_PORT, port, NULL)); } static void mm_port_probe_init (MMPortProbe *self) { self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, MM_TYPE_PORT_PROBE, MMPortProbePrivate); } static void set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { MMPortProbe *self = MM_PORT_PROBE (object); switch (prop_id) { case PROP_DEVICE: /* construct only, no new reference! */ self->priv->device = g_value_get_object (value); break; case PROP_PORT: /* construct only */ self->priv->port = g_value_dup_object (value); initialize_port_type_hints (self); 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) { MMPortProbe *self = MM_PORT_PROBE (object); switch (prop_id) { case PROP_DEVICE: g_value_set_object (value, self->priv->device); break; case PROP_PORT: g_value_set_object (value, self->priv->port); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void finalize (GObject *object) { MMPortProbe *self = MM_PORT_PROBE (object); /* We should never have a task here */ g_assert (self->priv->task == NULL); g_free (self->priv->vendor); g_free (self->priv->product); G_OBJECT_CLASS (mm_port_probe_parent_class)->finalize (object); } static void dispose (GObject *object) { MMPortProbe *self = MM_PORT_PROBE (object); /* We didn't get a reference to the device */ self->priv->device = NULL; g_clear_object (&self->priv->port); G_OBJECT_CLASS (mm_port_probe_parent_class)->dispose (object); } static void log_object_iface_init (MMLogObjectInterface *iface) { iface->build_id = log_object_build_id; } static void mm_port_probe_class_init (MMPortProbeClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); g_type_class_add_private (object_class, sizeof (MMPortProbePrivate)); /* Virtual methods */ object_class->get_property = get_property; object_class->set_property = set_property; object_class->finalize = finalize; object_class->dispose = dispose; properties[PROP_DEVICE] = g_param_spec_object (MM_PORT_PROBE_DEVICE, "Device", "Device owning this probe", MM_TYPE_DEVICE, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY); g_object_class_install_property (object_class, PROP_DEVICE, properties[PROP_DEVICE]); properties[PROP_PORT] = g_param_spec_object (MM_PORT_PROBE_PORT, "Port", "kernel device object of the port", MM_TYPE_KERNEL_DEVICE, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY); g_object_class_install_property (object_class, PROP_PORT, properties[PROP_PORT]); } ModemManager-1.23.4-dev/src/mm-port-probe.h000066400000000000000000000152421456466623000203710ustar00rootroot00000000000000/* -*- 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 - 2018 Red Hat, Inc. * Copyright (C) 2011 - 2018 Aleksander Morgado */ #ifndef MM_PORT_PROBE_H #define MM_PORT_PROBE_H #include "config.h" #include #include #include #include "mm-private-boxed-types.h" #include "mm-port-probe-at.h" #include "mm-port-serial-at.h" #include "mm-kernel-device.h" #include "mm-device.h" #define MM_TYPE_PORT_PROBE (mm_port_probe_get_type ()) #define MM_PORT_PROBE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_PORT_PROBE, MMPortProbe)) #define MM_PORT_PROBE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_PORT_PROBE, MMPortProbeClass)) #define MM_IS_PORT_PROBE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_PORT_PROBE)) #define MM_IS_PORT_PROBE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_PORT_PROBE)) #define MM_PORT_PROBE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_PORT_PROBE, MMPortProbeClass)) /* Flags to request port probing */ typedef enum { /*< underscore_name=mm_port_probe_flag >*/ MM_PORT_PROBE_NONE = 0, MM_PORT_PROBE_AT = 1 << 0, MM_PORT_PROBE_AT_VENDOR = 1 << 1, MM_PORT_PROBE_AT_PRODUCT = 1 << 2, MM_PORT_PROBE_AT_ICERA = 1 << 3, MM_PORT_PROBE_AT_XMM = 1 << 4, MM_PORT_PROBE_QCDM = 1 << 5, MM_PORT_PROBE_QMI = 1 << 6, MM_PORT_PROBE_MBIM = 1 << 7, } MMPortProbeFlag; typedef struct _MMPortProbe MMPortProbe; typedef struct _MMPortProbeClass MMPortProbeClass; typedef struct _MMPortProbePrivate MMPortProbePrivate; #define MM_PORT_PROBE_DEVICE "device" #define MM_PORT_PROBE_PORT "port" struct _MMPortProbe { GObject parent; MMPortProbePrivate *priv; }; struct _MMPortProbeClass { GObjectClass parent; }; /* Custom AT probing initialization setup. * Plugins can use this to configure how AT ports need to get initialized. * It also helps to implement plugin-specific checks, as plugins can set * their own probing results on the 'probe' object. */ typedef void (* MMPortProbeAtCustomInit) (MMPortProbe *probe, MMPortSerialAt *port, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); typedef gboolean (* MMPortProbeAtCustomInitFinish) (MMPortProbe *probe, GAsyncResult *result, GError **error); GType mm_port_probe_get_type (void); G_DEFINE_AUTOPTR_CLEANUP_FUNC (MMPortProbe, g_object_unref) MMPortProbe *mm_port_probe_new (MMDevice *device, MMKernelDevice *port); MMDevice *mm_port_probe_peek_device (MMPortProbe *self); MMDevice *mm_port_probe_get_device (MMPortProbe *self); MMKernelDevice *mm_port_probe_peek_port (MMPortProbe *self); MMKernelDevice *mm_port_probe_get_port (MMPortProbe *self); const gchar *mm_port_probe_get_port_name (MMPortProbe *self); const gchar *mm_port_probe_get_port_subsys (MMPortProbe *self); /* Probing result setters */ void mm_port_probe_set_result_at (MMPortProbe *self, gboolean at); void mm_port_probe_set_result_at_vendor (MMPortProbe *self, const gchar *at_vendor); void mm_port_probe_set_result_at_product (MMPortProbe *self, const gchar *at_product); void mm_port_probe_set_result_at_icera (MMPortProbe *self, gboolean is_icera); void mm_port_probe_set_result_at_xmm (MMPortProbe *self, gboolean is_xmm); void mm_port_probe_set_result_qcdm (MMPortProbe *self, gboolean qcdm); void mm_port_probe_set_result_qmi (MMPortProbe *self, gboolean qmi); void mm_port_probe_set_result_mbim (MMPortProbe *self, gboolean mbim); /* Run probing */ void mm_port_probe_run (MMPortProbe *self, MMPortProbeFlag flags, guint64 at_send_delay, gboolean at_remove_echo, gboolean at_send_lf, const MMPortProbeAtCommand *at_custom_probe, const MMAsyncMethod *at_custom_init, gboolean qcdm_required, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); gboolean mm_port_probe_run_finish (MMPortProbe *self, GAsyncResult *result, GError **error); gboolean mm_port_probe_run_cancel_at_probing (MMPortProbe *self); /* Probing result getters */ MMPortType mm_port_probe_get_port_type (MMPortProbe *self); gboolean mm_port_probe_is_at (MMPortProbe *self); gboolean mm_port_probe_is_qcdm (MMPortProbe *self); gboolean mm_port_probe_is_qmi (MMPortProbe *self); gboolean mm_port_probe_is_mbim (MMPortProbe *self); const gchar *mm_port_probe_get_vendor (MMPortProbe *self); const gchar *mm_port_probe_get_product (MMPortProbe *self); gboolean mm_port_probe_is_icera (MMPortProbe *self); gboolean mm_port_probe_is_xmm (MMPortProbe *self); gboolean mm_port_probe_is_ignored (MMPortProbe *self); /* Additional helpers */ gboolean mm_port_probe_list_has_at_port (GList *list); gboolean mm_port_probe_list_has_qmi_port (GList *list); gboolean mm_port_probe_list_has_mbim_port (GList *list); gboolean mm_port_probe_list_is_icera (GList *list); gboolean mm_port_probe_list_is_xmm (GList *list); #endif /* MM_PORT_PROBE_H */ ModemManager-1.23.4-dev/src/mm-port-qmi.c000066400000000000000000003303331456466623000200440ustar00rootroot00000000000000/* -*- 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) 2012-2021 Google, Inc. * Copyright (C) 2021 Aleksander Morgado */ #include #include #include #include #include #include #include "mm-port-qmi.h" #include "mm-port-net.h" #include "mm-port-enums-types.h" #include "mm-modem-helpers-qmi.h" #include "mm-log-object.h" /* as internally defined in the kernel */ #define RMNET_MAX_PACKET_SIZE 16384 #define MHI_NET_MTU_DEFAULT 16384 G_DEFINE_TYPE (MMPortQmi, mm_port_qmi, MM_TYPE_PORT) #if defined WITH_QRTR enum { PROP_0, PROP_NODE, PROP_LAST }; static GParamSpec *properties[PROP_LAST]; #endif typedef struct { QmiService service; QmiClient *client; guint flag; } ServiceInfo; struct _MMPortQmiPrivate { gboolean in_progress; QmiDevice *qmi_device; GList *services; gchar *net_driver; gchar *net_sysfs_path; guint net_preallocated_links_requested; #if defined WITH_QRTR QrtrNode *node; #endif /* port monitoring */ gulong timeout_monitoring_id; gulong removed_monitoring_id; /* endpoint info */ QmiDataEndpointType endpoint_type; gint endpoint_interface_number; /* kernel data mode */ MMPortQmiKernelDataMode kernel_data_modes; /* wda settings */ gboolean wda_unsupported; QmiWdaLinkLayerProtocol llp; QmiWdaDataAggregationProtocol dap; guint max_multiplexed_links; /* preallocated links */ guint preallocated_links_needed; MMPort *preallocated_links_main; GArray *preallocated_links; GList *preallocated_links_setup_pending; /* first multiplex setup */ gboolean first_multiplex_setup; }; /*****************************************************************************/ static QmiClient * lookup_client (MMPortQmi *self, QmiService service, guint flag, gboolean steal) { GList *l; for (l = self->priv->services; l; l = g_list_next (l)) { ServiceInfo *info = l->data; if (info->service == service && info->flag == flag) { QmiClient *found; found = info->client; if (steal) { self->priv->services = g_list_delete_link (self->priv->services, l); g_free (info); } return found; } } return NULL; } QmiClient * mm_port_qmi_peek_client (MMPortQmi *self, QmiService service, guint flag) { return lookup_client (self, service, flag, FALSE); } QmiClient * mm_port_qmi_get_client (MMPortQmi *self, QmiService service, guint flag) { QmiClient *client; client = mm_port_qmi_peek_client (self, service, flag); return (client ? g_object_ref (client) : NULL); } /*****************************************************************************/ static void initialize_endpoint_info (MMPortQmi *self) { MMKernelDevice *kernel_device; kernel_device = mm_port_peek_kernel_device (MM_PORT (self)); self->priv->endpoint_type = mm_port_net_driver_to_qmi_endpoint_type (self->priv->net_driver); switch (self->priv->endpoint_type) { case QMI_DATA_ENDPOINT_TYPE_HSUSB: g_assert (kernel_device); self->priv->endpoint_interface_number = mm_kernel_device_get_interface_number (kernel_device); break; case QMI_DATA_ENDPOINT_TYPE_EMBEDDED: self->priv->endpoint_interface_number = 1; break; case QMI_DATA_ENDPOINT_TYPE_PCIE: /* Qualcomm magic number */ self->priv->endpoint_interface_number = 4; break; case QMI_DATA_ENDPOINT_TYPE_UNDEFINED: case QMI_DATA_ENDPOINT_TYPE_HSIC: case QMI_DATA_ENDPOINT_TYPE_BAM_DMUX: case QMI_DATA_ENDPOINT_TYPE_UNKNOWN: default: self->priv->endpoint_interface_number = 0; break; } mm_obj_dbg (self, "endpoint info updated: type '%s', interface number '%u'", qmi_data_endpoint_type_get_string (self->priv->endpoint_type), self->priv->endpoint_interface_number); } QmiDataEndpointType mm_port_qmi_get_endpoint_type (MMPortQmi *self) { return self->priv->endpoint_type; } guint mm_port_qmi_get_endpoint_interface_number (MMPortQmi *self) { return self->priv->endpoint_interface_number; } void mm_port_qmi_get_endpoint_info (MMPortQmi *self, MMQmiDataEndpoint *out_endpoint) { out_endpoint->type = self->priv->endpoint_type; out_endpoint->interface_number = self->priv->endpoint_interface_number; out_endpoint->sio_port = QMI_SIO_PORT_NONE; } /*****************************************************************************/ static void reset_monitoring (MMPortQmi *self, QmiDevice *qmi_device) { if (self->priv->timeout_monitoring_id && qmi_device) { g_signal_handler_disconnect (qmi_device, self->priv->timeout_monitoring_id); self->priv->timeout_monitoring_id = 0; } if (self->priv->removed_monitoring_id && qmi_device) { g_signal_handler_disconnect (qmi_device, self->priv->removed_monitoring_id); self->priv->removed_monitoring_id = 0; } } static void consecutive_timeouts_updated_cb (MMPortQmi *self, GParamSpec *pspec, QmiDevice *qmi_device) { g_signal_emit_by_name (self, MM_PORT_SIGNAL_TIMED_OUT, qmi_device_get_consecutive_timeouts (qmi_device)); } static void device_removed_cb (MMPortQmi *self) { g_signal_emit_by_name (self, MM_PORT_SIGNAL_REMOVED); } static void setup_monitoring (MMPortQmi *self, QmiDevice *qmi_device) { g_assert (qmi_device); reset_monitoring (self, qmi_device); g_assert (!self->priv->timeout_monitoring_id); self->priv->timeout_monitoring_id = g_signal_connect_swapped (qmi_device, "notify::" QMI_DEVICE_CONSECUTIVE_TIMEOUTS, G_CALLBACK (consecutive_timeouts_updated_cb), self); g_assert (!self->priv->removed_monitoring_id); self->priv->removed_monitoring_id = g_signal_connect_swapped (qmi_device, QMI_DEVICE_SIGNAL_REMOVED, G_CALLBACK (device_removed_cb), self); } /*****************************************************************************/ void mm_port_qmi_release_client (MMPortQmi *self, QmiService service, MMPortQmiFlag flag) { QmiClient *client; if (!self->priv->qmi_device) return; client = lookup_client (self, service, flag, TRUE); if (!client) return; mm_obj_dbg (self, "explicitly releasing client for service '%s'...", qmi_service_get_string (service)); qmi_device_release_client (self->priv->qmi_device, client, QMI_DEVICE_RELEASE_CLIENT_FLAGS_RELEASE_CID, 3, NULL, NULL, NULL); g_object_unref (client); } /*****************************************************************************/ typedef struct { ServiceInfo *info; } AllocateClientContext; static void allocate_client_context_free (AllocateClientContext *ctx) { if (ctx->info) { g_assert (ctx->info->client == NULL); g_free (ctx->info); } g_free (ctx); } gboolean mm_port_qmi_allocate_client_finish (MMPortQmi *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void allocate_client_ready (QmiDevice *qmi_device, GAsyncResult *res, GTask *task) { MMPortQmi *self; AllocateClientContext *ctx; GError *error = NULL; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); ctx->info->client = qmi_device_allocate_client_finish (qmi_device, res, &error); if (!ctx->info->client) { g_prefix_error (&error, "Couldn't create client for service '%s': ", qmi_service_get_string (ctx->info->service)); g_task_return_error (task, error); } else { /* Move the service info to our internal list */ self->priv->services = g_list_prepend (self->priv->services, ctx->info); ctx->info = NULL; g_task_return_boolean (task, TRUE); } g_object_unref (task); } void mm_port_qmi_allocate_client (MMPortQmi *self, QmiService service, guint flag, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { AllocateClientContext *ctx; GTask *task; task = g_task_new (self, cancellable, callback, user_data); if (!mm_port_qmi_is_open (self)) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_WRONG_STATE, "Port is closed"); g_object_unref (task); return; } if (!!mm_port_qmi_peek_client (self, service, flag)) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_EXISTS, "Client for service '%s' already allocated", qmi_service_get_string (service)); g_object_unref (task); return; } ctx = g_new0 (AllocateClientContext, 1); ctx->info = g_new0 (ServiceInfo, 1); ctx->info->service = service; ctx->info->flag = flag; g_task_set_task_data (task, ctx, (GDestroyNotify)allocate_client_context_free); qmi_device_allocate_client (self->priv->qmi_device, service, QMI_CID_NONE, 10, cancellable, (GAsyncReadyCallback)allocate_client_ready, task); } /*****************************************************************************/ typedef struct { gchar *link_name; guint mux_id; gboolean setup; } PreallocatedLinkInfo; static void preallocated_link_info_clear (PreallocatedLinkInfo *info) { g_free (info->link_name); } static void delete_preallocated_links (QmiDevice *qmi_device, GArray *preallocated_links) { guint i; /* This link deletion cleanup may fail if the main interface is up * (a limitation of qmi_wwan in some kernel versions). It's just a minor * inconvenience really, if MM restarts they'll be all removed during * initialization anyway */ for (i = 0; i < preallocated_links->len; i++) { PreallocatedLinkInfo *info; info = &g_array_index (preallocated_links, PreallocatedLinkInfo, i); qmi_device_delete_link (qmi_device, info->link_name, info->mux_id, NULL, NULL, NULL); } } static guint count_preallocated_links_setup (MMPortQmi *self) { guint i; guint count = 0; for (i = 0; self->priv->preallocated_links && (i < self->priv->preallocated_links->len); i++) { PreallocatedLinkInfo *info; info = &g_array_index (self->priv->preallocated_links, PreallocatedLinkInfo, i); if (info->setup) count++; } return count; } static gboolean release_preallocated_link (MMPortQmi *self, const gchar *link_name, guint mux_id, GError **error) { guint i; for (i = 0; self->priv->preallocated_links && (i < self->priv->preallocated_links->len); i++) { PreallocatedLinkInfo *info; info = &g_array_index (self->priv->preallocated_links, PreallocatedLinkInfo, i); if (!info->setup || (g_strcmp0 (info->link_name, link_name) != 0) || (info->mux_id != mux_id)) continue; info->setup = FALSE; return TRUE; } g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "No preallocated link found to release"); return FALSE; } static gboolean acquire_preallocated_link (MMPortQmi *self, MMPort *main, gchar **link_name, guint *mux_id, GError **error) { guint i; if (!self->priv->qmi_device) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_ABORTED, "port is closed"); return FALSE; } if (!self->priv->preallocated_links || !self->priv->preallocated_links_main) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "No preallocated links available"); return FALSE; } if ((main != self->priv->preallocated_links_main) && (g_strcmp0 (mm_port_get_device (main), mm_port_get_device (self->priv->preallocated_links_main)) != 0)) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Preallocated links available in 'net/%s', not in 'net/%s'", mm_port_get_device (self->priv->preallocated_links_main), mm_port_get_device (main)); return FALSE; } for (i = 0; i < self->priv->preallocated_links->len; i++) { PreallocatedLinkInfo *info; info = &g_array_index (self->priv->preallocated_links, PreallocatedLinkInfo, i); if (info->setup) continue; info->setup = TRUE; *link_name = g_strdup (info->link_name); *mux_id = info->mux_id; return TRUE; } g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "No more preallocated links available"); return FALSE; } /*****************************************************************************/ typedef struct { QmiDevice *qmi_device; gchar *link_prefix_hint; QmiDeviceAddLinkFlags flags; MMPort *data; GArray *preallocated_links; } InitializePreallocatedLinksContext; static void initialize_preallocated_links_context_free (InitializePreallocatedLinksContext *ctx) { if (ctx->preallocated_links) { delete_preallocated_links (ctx->qmi_device, ctx->preallocated_links); g_array_unref (ctx->preallocated_links); } g_clear_pointer (&ctx->link_prefix_hint, g_free); g_object_unref (ctx->qmi_device); g_object_unref (ctx->data); g_slice_free (InitializePreallocatedLinksContext, ctx); } static GArray * initialize_preallocated_links_finish (MMPortQmi *self, GAsyncResult *res, GError **error) { return g_task_propagate_pointer (G_TASK (res), error); } static void initialize_preallocated_links_next (GTask *task); static void device_add_link_preallocated_ready (QmiDevice *device, GAsyncResult *res, GTask *task) { MMPortQmi *self; InitializePreallocatedLinksContext *ctx; GError *error = NULL; PreallocatedLinkInfo info = { NULL, 0, FALSE }; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); info.link_name = qmi_device_add_link_with_flags_finish (device, res, &info.mux_id, &error); if (!info.link_name) { g_prefix_error (&error, "failed to add preallocated link (%u/%u) for device: ", ctx->preallocated_links->len + 1, self->priv->preallocated_links_needed); g_task_return_error (task, error); return; } g_array_append_val (ctx->preallocated_links, info); initialize_preallocated_links_next (task); } static void initialize_preallocated_links_next (GTask *task) { MMPortQmi *self; InitializePreallocatedLinksContext *ctx; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); /* if we were closed while allocating, bad thing, abort */ if (!self->priv->qmi_device) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_ABORTED, "port is closed"); g_object_unref (task); return; } g_assert (self->priv->preallocated_links_needed > 0); if (ctx->preallocated_links->len == (guint) self->priv->preallocated_links_needed) { g_task_return_pointer (task, g_steal_pointer (&ctx->preallocated_links), (GDestroyNotify)g_array_unref); g_object_unref (task); return; } qmi_device_add_link_with_flags (self->priv->qmi_device, ctx->preallocated_links->len + 1, mm_kernel_device_get_name (mm_port_peek_kernel_device (ctx->data)), ctx->link_prefix_hint, ctx->flags, NULL, (GAsyncReadyCallback) device_add_link_preallocated_ready, task); } static void initialize_preallocated_links (MMPortQmi *self, const gchar *link_prefix_hint, QmiDeviceAddLinkFlags flags, GAsyncReadyCallback callback, gpointer user_data) { InitializePreallocatedLinksContext *ctx; GTask *task; task = g_task_new (self, NULL, callback, user_data); ctx = g_slice_new0 (InitializePreallocatedLinksContext); ctx->qmi_device = g_object_ref (self->priv->qmi_device); ctx->link_prefix_hint = g_strdup (link_prefix_hint); ctx->flags = flags; ctx->data = g_object_ref (self->priv->preallocated_links_main); ctx->preallocated_links = g_array_sized_new (FALSE, FALSE, sizeof (PreallocatedLinkInfo), self->priv->preallocated_links_needed); g_array_set_clear_func (ctx->preallocated_links, (GDestroyNotify)preallocated_link_info_clear); g_task_set_task_data (task, ctx, (GDestroyNotify)initialize_preallocated_links_context_free); initialize_preallocated_links_next (task); } /*****************************************************************************/ typedef struct { MMPort *main; gchar *link_name; guint mux_id; } SetupLinkContext; static void setup_link_context_free (SetupLinkContext *ctx) { g_free (ctx->link_name); g_clear_object (&ctx->main); g_slice_free (SetupLinkContext, ctx); } gchar * mm_port_qmi_setup_link_finish (MMPortQmi *self, GAsyncResult *res, guint *mux_id, GError **error) { SetupLinkContext *ctx; if (!g_task_propagate_boolean (G_TASK (res), error)) return NULL; ctx = g_task_get_task_data (G_TASK (res)); if (mux_id) *mux_id = ctx->mux_id; return g_steal_pointer (&ctx->link_name); } static void device_add_link_ready (QmiDevice *device, GAsyncResult *res, GTask *task) { SetupLinkContext *ctx; GError *error = NULL; ctx = g_task_get_task_data (task); ctx->link_name = qmi_device_add_link_with_flags_finish (device, res, &ctx->mux_id, &error); if (!ctx->link_name) { g_prefix_error (&error, "failed to add link for device: "); g_task_return_error (task, error); } else g_task_return_boolean (task, TRUE); g_object_unref (task); } static void setup_preallocated_link (GTask *task) { MMPortQmi *self; SetupLinkContext *ctx; GError *error = NULL; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); if (!acquire_preallocated_link (self, ctx->main, &ctx->link_name, &ctx->mux_id, &error)) g_task_return_error (task, error); else g_task_return_boolean (task, TRUE); g_object_unref (task); } static void initialize_preallocated_links_ready (MMPortQmi *self, GAsyncResult *res, GTask *task) { g_autoptr(GError) error = NULL; g_assert (!self->priv->preallocated_links); self->priv->preallocated_links = initialize_preallocated_links_finish (self, res, &error); if (!self->priv->preallocated_links) { /* We need to fail this task and all the additional tasks also pending */ g_task_return_error (task, g_error_copy (error)); g_object_unref (task); while (self->priv->preallocated_links_setup_pending) { g_task_return_error (self->priv->preallocated_links_setup_pending->data, g_error_copy (error)); g_object_unref (self->priv->preallocated_links_setup_pending->data); self->priv->preallocated_links_setup_pending = g_list_delete_link (self->priv->preallocated_links_setup_pending, self->priv->preallocated_links_setup_pending); } /* and reset back the main, because we're not really initialized */ g_clear_object (&self->priv->preallocated_links_main); return; } /* Now we know preallocated links are available, complete our task and all the pending ones */ setup_preallocated_link (task); while (self->priv->preallocated_links_setup_pending) { setup_preallocated_link (self->priv->preallocated_links_setup_pending->data); self->priv->preallocated_links_setup_pending = g_list_delete_link (self->priv->preallocated_links_setup_pending, self->priv->preallocated_links_setup_pending); } } static QmiDeviceAddLinkFlags get_rmnet_device_add_link_flags (MMPortQmi *self) { QmiDeviceAddLinkFlags flags = QMI_DEVICE_ADD_LINK_FLAGS_NONE; g_autofree gchar *flags_str = NULL; if (g_strcmp0 (self->priv->net_driver, "ipa") == 0) { g_autofree gchar *tx_sysfs_path = NULL; g_autofree gchar *rx_sysfs_path = NULL; g_autofree gchar *tx_sysfs_str = NULL; g_autofree gchar *rx_sysfs_str = NULL; tx_sysfs_path = g_build_filename (self->priv->net_sysfs_path, "device", "feature", "tx_offload", NULL); rx_sysfs_path = g_build_filename (self->priv->net_sysfs_path, "device", "feature", "rx_offload", NULL); if (g_file_get_contents (rx_sysfs_path, &rx_sysfs_str, NULL, NULL) && rx_sysfs_str) { if (g_str_has_prefix (rx_sysfs_str, "MAPv4")) flags |= QMI_DEVICE_ADD_LINK_FLAGS_INGRESS_MAP_CKSUMV4; else if (g_str_has_prefix (rx_sysfs_str, "MAPv5")) flags |= QMI_DEVICE_ADD_LINK_FLAGS_INGRESS_MAP_CKSUMV5; } if (g_file_get_contents (tx_sysfs_path, &tx_sysfs_str, NULL, NULL) && tx_sysfs_str) { if (g_str_has_prefix (tx_sysfs_str, "MAPv4")) flags |= QMI_DEVICE_ADD_LINK_FLAGS_EGRESS_MAP_CKSUMV4; else if (g_str_has_prefix (tx_sysfs_str, "MAPv5")) flags |= QMI_DEVICE_ADD_LINK_FLAGS_EGRESS_MAP_CKSUMV5; } } if (g_strcmp0 (self->priv->net_driver, "qmi_wwan") == 0 || g_strcmp0 (self->priv->net_driver, "mhi_net") == 0) { QmiWdaDataAggregationProtocol dap; dap = mm_port_qmi_get_data_aggregation_protocol (self); if (dap == QMI_WDA_DATA_AGGREGATION_PROTOCOL_QMAPV5) flags |= (QMI_DEVICE_ADD_LINK_FLAGS_INGRESS_MAP_CKSUMV5 | QMI_DEVICE_ADD_LINK_FLAGS_EGRESS_MAP_CKSUMV5); else if (dap == QMI_WDA_DATA_AGGREGATION_PROTOCOL_QMAPV4) flags |= (QMI_DEVICE_ADD_LINK_FLAGS_INGRESS_MAP_CKSUMV4 | QMI_DEVICE_ADD_LINK_FLAGS_EGRESS_MAP_CKSUMV4); } flags_str = qmi_device_add_link_flags_build_string_from_mask (flags); mm_obj_dbg (self, "Creating RMNET link with flags: %s", flags_str); return flags; } void mm_port_qmi_setup_link (MMPortQmi *self, MMPort *data, const gchar *link_prefix_hint, GAsyncReadyCallback callback, gpointer user_data) { SetupLinkContext *ctx; GTask *task; task = g_task_new (self, NULL, callback, user_data); if (!self->priv->qmi_device) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_WRONG_STATE, "Port is not open"); g_object_unref (task); return; } if (!(self->priv->kernel_data_modes & (MM_PORT_QMI_KERNEL_DATA_MODE_MUX_RMNET | MM_PORT_QMI_KERNEL_DATA_MODE_MUX_QMIWWAN))) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_WRONG_STATE, "Multiplex support not available in kernel"); g_object_unref (task); return; } if (!MM_PORT_QMI_DAP_IS_SUPPORTED_QMAP (self->priv->dap)) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_WRONG_STATE, "Aggregation not enabled"); g_object_unref (task); return; } ctx = g_slice_new0 (SetupLinkContext); ctx->main = g_object_ref (data); ctx->mux_id = QMI_DEVICE_MUX_ID_UNBOUND; g_task_set_task_data (task, ctx, (GDestroyNotify) setup_link_context_free); /* If we're requested to use preallocated links, do it right away */ if (self->priv->preallocated_links_needed > 0) { if (self->priv->preallocated_links) { setup_preallocated_link (task); return; } /* We must make sure we don't run this procedure in parallel (e.g. if multiple * connection attempts reach at the same time), so if we're told the preallocated * links are already being initialized (main is set) but the array didn't exist, * queue our task for completion once we're fully initialized */ if (self->priv->preallocated_links_main) { self->priv->preallocated_links_setup_pending = g_list_append (self->priv->preallocated_links_setup_pending, task); return; } /* Store main to flag that we're initializing preallocated links */ self->priv->preallocated_links_main = g_object_ref (data); initialize_preallocated_links (self, link_prefix_hint, get_rmnet_device_add_link_flags (self), (GAsyncReadyCallback) initialize_preallocated_links_ready, task); return; } /* No preallocated links required and using rmnet, just try to add link in the QmiDevice */ if (self->priv->kernel_data_modes & MM_PORT_QMI_KERNEL_DATA_MODE_MUX_RMNET) { qmi_device_add_link_with_flags (self->priv->qmi_device, QMI_DEVICE_MUX_ID_AUTOMATIC, mm_kernel_device_get_name (mm_port_peek_kernel_device (data)), link_prefix_hint, get_rmnet_device_add_link_flags (self), NULL, (GAsyncReadyCallback) device_add_link_ready, task); return; } g_assert_not_reached (); } /*****************************************************************************/ gboolean mm_port_qmi_cleanup_link_finish (MMPortQmi *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void device_delete_link_ready (QmiDevice *device, GAsyncResult *res, GTask *task) { GError *error = NULL; if (!qmi_device_delete_link_finish (device, res, &error)) g_task_return_error (task, error); else g_task_return_boolean (task, TRUE); g_object_unref (task); } void mm_port_qmi_cleanup_link (MMPortQmi *self, const gchar *link_name, guint mux_id, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; GError *error = NULL; task = g_task_new (self, NULL, callback, user_data); if (!self->priv->qmi_device) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_WRONG_STATE, "Port is not open"); g_object_unref (task); return; } if (!(self->priv->kernel_data_modes & (MM_PORT_QMI_KERNEL_DATA_MODE_MUX_RMNET | MM_PORT_QMI_KERNEL_DATA_MODE_MUX_QMIWWAN))) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_WRONG_STATE, "Multiplex support not available in kernel"); g_object_unref (task); return; } if (!MM_PORT_QMI_DAP_IS_SUPPORTED_QMAP (self->priv->dap)) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_WRONG_STATE, "Aggregation not enabled"); g_object_unref (task); return; } /* If using preallocated links, just release one */ if (self->priv->preallocated_links_needed > 0) { if (!release_preallocated_link (self, link_name, mux_id, &error)) g_task_return_error (task, error); else g_task_return_boolean (task, TRUE); g_object_unref (task); return; } /* When using rmnet, just try to delete the link from the QmiDevice */ if (self->priv->kernel_data_modes & MM_PORT_QMI_KERNEL_DATA_MODE_MUX_RMNET) { qmi_device_delete_link (self->priv->qmi_device, link_name, mux_id, NULL, (GAsyncReadyCallback) device_delete_link_ready, task); return; } g_assert_not_reached (); } /*****************************************************************************/ typedef struct { QmiDevice *device; MMPort *data; } InternalResetContext; static void internal_reset_context_free (InternalResetContext *ctx) { g_clear_object (&ctx->device); g_clear_object (&ctx->data); g_slice_free (InternalResetContext, ctx); } static gboolean internal_reset_finish (MMPortQmi *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void delete_all_links_ready (QmiDevice *device, GAsyncResult *res, GTask *task) { MMPortQmi *self; InternalResetContext *ctx; GError *error = NULL; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); /* link deletion not fatal, it may happen if in 802.3 already */ if (!qmi_device_delete_all_links_finish (device, res, &error)) { mm_obj_dbg (self, "couldn't delete all links: %s", error->message); g_clear_error (&error); } /* expected data format only applicable to qmi_wwan */ if (g_strcmp0 (self->priv->net_driver, "qmi_wwan") == 0) { mm_obj_dbg (self, "reseting expected kernel data format to 802.3 in data interface '%s'", mm_port_get_device (MM_PORT (ctx->data))); if (!qmi_device_set_expected_data_format (ctx->device, QMI_DEVICE_EXPECTED_DATA_FORMAT_802_3, &error)) { g_task_return_error (task, error); g_object_unref (task); return; } } g_task_return_boolean (task, TRUE); g_object_unref (task); } static void net_link_down_ready (MMPortNet *data, GAsyncResult *res, GTask *task) { MMPortQmi *self; InternalResetContext *ctx; GError *error = NULL; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); if (!mm_port_net_link_setup_finish (data, res, &error)) { g_task_return_error (task, error); g_object_unref (task); return; } /* first, delete all links found, if any */ mm_obj_dbg (self, "deleting all links in data interface '%s'", mm_port_get_device (ctx->data)); qmi_device_delete_all_links (ctx->device, mm_port_get_device (ctx->data), NULL, (GAsyncReadyCallback)delete_all_links_ready, task); } static void internal_reset (MMPortQmi *self, MMPort *data, QmiDevice *device, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; InternalResetContext *ctx; guint mtu; task = g_task_new (self, NULL, callback, user_data); ctx = g_slice_new0 (InternalResetContext); ctx->data = g_object_ref (data); ctx->device = g_object_ref (device); g_task_set_task_data (task, ctx, (GDestroyNotify) internal_reset_context_free); /* mhi_net has a custom default MTU set by the kernel driver */ if (g_strcmp0 (self->priv->net_driver, "mhi_net") == 0) mtu = MHI_NET_MTU_DEFAULT; else mtu = MM_PORT_NET_MTU_DEFAULT; /* first, bring down main interface */ mm_obj_dbg (self, "bringing down data interface '%s'", mm_port_get_device (ctx->data)); mm_port_net_link_setup (MM_PORT_NET (ctx->data), FALSE, mtu, NULL, (GAsyncReadyCallback) net_link_down_ready, task); } /*****************************************************************************/ typedef struct { QmiDevice *device; MMPort *data; } ResetContext; static void reset_context_free (ResetContext *ctx) { g_clear_object (&ctx->device); g_clear_object (&ctx->data); g_slice_free (ResetContext, ctx); } gboolean mm_port_qmi_reset_finish (MMPortQmi *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void internal_reset_ready (MMPortQmi *self, GAsyncResult *res, GTask *task) { GError *error = NULL; if (!internal_reset_finish (self, res, &error)) g_task_return_error (task, error); else g_task_return_boolean (task, TRUE); g_object_unref (task); } static void reset_device_new_ready (GObject *source, GAsyncResult *res, GTask *task) { MMPortQmi *self; ResetContext *ctx; GError *error = NULL; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); ctx->device = qmi_device_new_finish (res, &error); if (!ctx->device) { g_task_return_error (task, error); g_object_unref (task); return; } internal_reset (self, ctx->data, ctx->device, (GAsyncReadyCallback) internal_reset_ready, task); } void mm_port_qmi_reset (MMPortQmi *self, MMPort *data, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; ResetContext *ctx; g_autoptr(GFile) file = NULL; g_autofree gchar *fullpath = NULL; task = g_task_new (self, NULL, callback, user_data); if (self->priv->qmi_device) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_WRONG_STATE, "Port is already open"); g_object_unref (task); return; } ctx = g_slice_new0 (ResetContext); ctx->data = g_object_ref (data); g_task_set_task_data (task, ctx, (GDestroyNotify) reset_context_free); fullpath = g_strdup_printf ("/dev/%s", mm_port_get_device (MM_PORT (self))); file = g_file_new_for_path (fullpath); qmi_device_new (file, NULL, (GAsyncReadyCallback) reset_device_new_ready, task); } /*****************************************************************************/ MMPortQmiKernelDataMode mm_port_qmi_get_kernel_data_modes (MMPortQmi *self) { return self->priv->kernel_data_modes; } QmiWdaLinkLayerProtocol mm_port_qmi_get_link_layer_protocol (MMPortQmi *self) { return self->priv->llp; } QmiWdaDataAggregationProtocol mm_port_qmi_get_data_aggregation_protocol (MMPortQmi *self) { return self->priv->dap; } guint mm_port_qmi_get_max_multiplexed_links (MMPortQmi *self) { return self->priv->max_multiplexed_links; } /*****************************************************************************/ static MMPortQmiKernelDataMode load_current_kernel_data_modes (MMPortQmi *self, QmiDevice *device) { /* For BAM-DMUX based setups, raw-ip only and no multiplexing */ if (g_strcmp0 (self->priv->net_driver, "bam-dmux") == 0) return MM_PORT_QMI_KERNEL_DATA_MODE_RAW_IP; /* For IPA based setups, always rmnet multiplexing */ if (g_strcmp0 (self->priv->net_driver, "ipa") == 0) return MM_PORT_QMI_KERNEL_DATA_MODE_MUX_RMNET; /* For USB based setups, query kernel */ if (g_strcmp0 (self->priv->net_driver, "qmi_wwan") == 0) { switch (qmi_device_get_expected_data_format (device, NULL)) { case QMI_DEVICE_EXPECTED_DATA_FORMAT_QMAP_PASS_THROUGH: return MM_PORT_QMI_KERNEL_DATA_MODE_MUX_RMNET; case QMI_DEVICE_EXPECTED_DATA_FORMAT_RAW_IP: if (qmi_device_check_link_supported (device, NULL)) return (MM_PORT_QMI_KERNEL_DATA_MODE_RAW_IP | MM_PORT_QMI_KERNEL_DATA_MODE_MUX_QMIWWAN); return MM_PORT_QMI_KERNEL_DATA_MODE_RAW_IP; case QMI_DEVICE_EXPECTED_DATA_FORMAT_UNKNOWN: /* If the expected data format is unknown, it means the kernel in use * doesn't have support for querying it; therefore it's 802.3 */ case QMI_DEVICE_EXPECTED_DATA_FORMAT_802_3: return MM_PORT_QMI_KERNEL_DATA_MODE_802_3; default: g_assert_not_reached (); return MM_PORT_QMI_KERNEL_DATA_MODE_NONE; } } if (g_strcmp0 (self->priv->net_driver, "mhi_net") == 0) return (MM_PORT_QMI_KERNEL_DATA_MODE_RAW_IP | MM_PORT_QMI_KERNEL_DATA_MODE_MUX_RMNET); /* For any driver, assume raw-ip only */ return MM_PORT_QMI_KERNEL_DATA_MODE_RAW_IP; } static MMPortQmiKernelDataMode load_supported_kernel_data_modes (MMPortQmi *self, QmiDevice *device) { /* For BAM-DMUX based setups, raw-ip only and no multiplexing */ if (g_strcmp0 (self->priv->net_driver, "bam-dmux") == 0) return MM_PORT_QMI_KERNEL_DATA_MODE_RAW_IP; /* For IPA based setups, always rmnet multiplexing */ if (g_strcmp0 (self->priv->net_driver, "ipa") == 0) return MM_PORT_QMI_KERNEL_DATA_MODE_MUX_RMNET; /* For USB based setups, we may have all supported */ if (g_strcmp0 (self->priv->net_driver, "qmi_wwan") == 0) { MMPortQmiKernelDataMode supported = MM_PORT_QMI_KERNEL_DATA_MODE_802_3; /* If raw-ip is not supported, muxing is also not supported */ if (qmi_device_check_expected_data_format_supported (device, QMI_DEVICE_EXPECTED_DATA_FORMAT_RAW_IP, NULL)) { supported |= MM_PORT_QMI_KERNEL_DATA_MODE_RAW_IP; /* We switch to raw-ip to see if we can do link management with qmi_wwan. * This switch would not truly be required, but the logic afterwards is robust * enough to support this, nothing to worry about */ if (qmi_device_set_expected_data_format (device, QMI_DEVICE_EXPECTED_DATA_FORMAT_RAW_IP, NULL) && qmi_device_check_link_supported (device, NULL)) supported |= MM_PORT_QMI_KERNEL_DATA_MODE_MUX_QMIWWAN; if (qmi_device_check_expected_data_format_supported (device, QMI_DEVICE_EXPECTED_DATA_FORMAT_QMAP_PASS_THROUGH, NULL)) supported |= MM_PORT_QMI_KERNEL_DATA_MODE_MUX_RMNET; } return supported; } /* PCIe based setups support both raw ip and QMAP through rmnet */ if (g_strcmp0 (self->priv->net_driver, "mhi_net") == 0) return (MM_PORT_QMI_KERNEL_DATA_MODE_RAW_IP | MM_PORT_QMI_KERNEL_DATA_MODE_MUX_RMNET); /* For any driver, assume raw-ip only */ return MM_PORT_QMI_KERNEL_DATA_MODE_RAW_IP; } /*****************************************************************************/ #define DEFAULT_QMI_WWAN_PREALLOCATED_LINKS 4 #define DEFAULT_DOWNLINK_DATA_AGGREGATION_MAX_SIZE 32768 #define DEFAULT_DOWNLINK_DATA_AGGREGATION_MAX_SIZE_QMI_WWAN_RMNET 16384 #define DEFAULT_DOWNLINK_DATA_AGGREGATION_MAX_DATAGRAMS 32 typedef struct { MMPortQmiKernelDataMode kernel_data_mode; QmiWdaLinkLayerProtocol wda_llp; QmiWdaDataAggregationProtocol wda_dap; } DataFormatCombination; static const DataFormatCombination data_format_combinations[] = { { MM_PORT_QMI_KERNEL_DATA_MODE_MUX_RMNET, QMI_WDA_LINK_LAYER_PROTOCOL_RAW_IP, QMI_WDA_DATA_AGGREGATION_PROTOCOL_QMAPV5 }, { MM_PORT_QMI_KERNEL_DATA_MODE_MUX_RMNET, QMI_WDA_LINK_LAYER_PROTOCOL_RAW_IP, QMI_WDA_DATA_AGGREGATION_PROTOCOL_QMAPV4 }, { MM_PORT_QMI_KERNEL_DATA_MODE_MUX_RMNET, QMI_WDA_LINK_LAYER_PROTOCOL_RAW_IP, QMI_WDA_DATA_AGGREGATION_PROTOCOL_QMAP }, { MM_PORT_QMI_KERNEL_DATA_MODE_MUX_QMIWWAN, QMI_WDA_LINK_LAYER_PROTOCOL_RAW_IP, QMI_WDA_DATA_AGGREGATION_PROTOCOL_QMAPV5 }, { MM_PORT_QMI_KERNEL_DATA_MODE_MUX_QMIWWAN, QMI_WDA_LINK_LAYER_PROTOCOL_RAW_IP, QMI_WDA_DATA_AGGREGATION_PROTOCOL_QMAP }, { MM_PORT_QMI_KERNEL_DATA_MODE_RAW_IP, QMI_WDA_LINK_LAYER_PROTOCOL_RAW_IP, QMI_WDA_DATA_AGGREGATION_PROTOCOL_DISABLED }, { MM_PORT_QMI_KERNEL_DATA_MODE_802_3, QMI_WDA_LINK_LAYER_PROTOCOL_802_3, QMI_WDA_DATA_AGGREGATION_PROTOCOL_DISABLED }, }; typedef enum { INTERNAL_SETUP_DATA_FORMAT_STEP_FIRST, INTERNAL_SETUP_DATA_FORMAT_STEP_ALLOCATE_WDA_CLIENT, INTERNAL_SETUP_DATA_FORMAT_STEP_SUPPORTED_KERNEL_DATA_MODES, INTERNAL_SETUP_DATA_FORMAT_STEP_RETRY, INTERNAL_SETUP_DATA_FORMAT_STEP_CURRENT_KERNEL_DATA_MODES, INTERNAL_SETUP_DATA_FORMAT_STEP_ALLOCATE_DPM_CLIENT, INTERNAL_SETUP_DATA_FORMAT_STEP_DPM_OPEN, INTERNAL_SETUP_DATA_FORMAT_STEP_GET_WDA_DATA_FORMAT, INTERNAL_SETUP_DATA_FORMAT_STEP_QUERY_DONE, INTERNAL_SETUP_DATA_FORMAT_STEP_CHECK_DATA_FORMAT_COMBINATION, INTERNAL_SETUP_DATA_FORMAT_STEP_SYNC_WDA_DATA_FORMAT, INTERNAL_SETUP_DATA_FORMAT_STEP_SETUP_MAIN_MTU, INTERNAL_SETUP_DATA_FORMAT_STEP_SYNC_KERNEL_DATA_MODE, INTERNAL_SETUP_DATA_FORMAT_STEP_LAST, } InternalSetupDataFormatStep; typedef struct { QmiDevice *device; MMPort *data; MMPortQmiSetupDataFormatAction action; InternalSetupDataFormatStep step; gboolean use_endpoint; gint data_format_combination_i; /* kernel data modes */ MMPortQmiKernelDataMode kernel_data_modes_current; MMPortQmiKernelDataMode kernel_data_modes_requested; MMPortQmiKernelDataMode kernel_data_modes_supported; /* configured device data format */ QmiClient *wda; QmiClient *dpm; QmiWdaLinkLayerProtocol wda_llp_current; QmiWdaLinkLayerProtocol wda_llp_requested; QmiWdaDataAggregationProtocol wda_ul_dap_current; QmiWdaDataAggregationProtocol wda_ul_dap_requested; QmiWdaDataAggregationProtocol wda_dl_dap_current; QmiWdaDataAggregationProtocol wda_dl_dap_requested; guint32 wda_dl_dap_max_datagrams_current; guint32 wda_dl_dap_max_size_current; gboolean wda_dap_supported; } InternalSetupDataFormatContext; static void internal_setup_data_format_context_free (InternalSetupDataFormatContext *ctx) { if (ctx->wda && ctx->device) qmi_device_release_client (ctx->device, ctx->wda, QMI_DEVICE_RELEASE_CLIENT_FLAGS_RELEASE_CID, 3, NULL, NULL, NULL); if (ctx->dpm && ctx->device) qmi_device_release_client (ctx->device, ctx->dpm, QMI_DEVICE_RELEASE_CLIENT_FLAGS_RELEASE_CID, 3, NULL, NULL, NULL); g_clear_object (&ctx->wda); g_clear_object (&ctx->dpm); g_clear_object (&ctx->data); g_clear_object (&ctx->device); g_slice_free (InternalSetupDataFormatContext, ctx); } static void internal_setup_data_format_propagate_link_setup (GTask *task, guint *out_max_multiplexed_links, guint *out_preallocated_links) { MMPortQmi *self; InternalSetupDataFormatContext *ctx; guint max_multiplexed_links; guint preallocated_links; if (!out_max_multiplexed_links && !out_preallocated_links) return; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); if (!ctx->wda_dap_supported) { max_multiplexed_links = 0; preallocated_links = 0; mm_obj_dbg (self, "wda data aggregation protocol unsupported: no multiplexed bearers allowed"); } else { /* if multiplex backend may be rmnet, MAX-MIN */ if (ctx->kernel_data_modes_supported & MM_PORT_QMI_KERNEL_DATA_MODE_MUX_RMNET) { max_multiplexed_links = 1 + (QMI_DEVICE_MUX_ID_MAX - QMI_DEVICE_MUX_ID_MIN); preallocated_links = (self->priv->net_preallocated_links_requested > max_multiplexed_links) ? max_multiplexed_links : self->priv->net_preallocated_links_requested; mm_obj_dbg (self, "rmnet link management supported: %u multiplexed bearers allowed, %u links preallocated", max_multiplexed_links, preallocated_links); } /* if multiplex backend may be qmi_wwan, the max preallocated amount :/ */ else if (ctx->kernel_data_modes_supported & MM_PORT_QMI_KERNEL_DATA_MODE_MUX_QMIWWAN) { preallocated_links = (self->priv->net_preallocated_links_requested > 0) ? self->priv->net_preallocated_links_requested : DEFAULT_QMI_WWAN_PREALLOCATED_LINKS; max_multiplexed_links = preallocated_links; mm_obj_dbg (self, "qmi_wwan link management supported: %u multiplexed bearers allowed, %u links preallocated", max_multiplexed_links, preallocated_links); } else { max_multiplexed_links = 0; preallocated_links = 0; mm_obj_dbg (self, "link management unsupported: no multiplexed bearers allowed"); } } if (out_max_multiplexed_links) *out_max_multiplexed_links = max_multiplexed_links; if (out_preallocated_links) *out_preallocated_links = preallocated_links; } static gboolean internal_setup_data_format_finish (MMPortQmi *self, GAsyncResult *res, MMPortQmiKernelDataMode *out_kernel_data_modes, QmiWdaLinkLayerProtocol *out_llp, QmiWdaDataAggregationProtocol *out_dap, guint *out_max_multiplexed_links, guint *out_preallocated_links, GError **error) { InternalSetupDataFormatContext *ctx; if (!g_task_propagate_boolean (G_TASK (res), error)) return FALSE; ctx = g_task_get_task_data (G_TASK (res)); *out_kernel_data_modes = ctx->kernel_data_modes_current; *out_llp = ctx->wda_llp_current; g_assert (ctx->wda_dl_dap_current == ctx->wda_ul_dap_current); *out_dap = ctx->wda_dl_dap_current; internal_setup_data_format_propagate_link_setup (G_TASK (res), out_max_multiplexed_links, out_preallocated_links); return TRUE; } static void internal_setup_data_format_context_step (GTask *task); static void sync_kernel_data_mode (GTask *task) { MMPortQmi *self; InternalSetupDataFormatContext *ctx; GError *error = NULL; g_autofree gchar *kernel_data_modes_current_str = NULL; g_autofree gchar *kernel_data_modes_requested_str = NULL; QmiDeviceExpectedDataFormat expected_data_format_requested = QMI_DEVICE_EXPECTED_DATA_FORMAT_UNKNOWN; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); kernel_data_modes_current_str = mm_port_qmi_kernel_data_mode_build_string_from_mask (ctx->kernel_data_modes_current); kernel_data_modes_requested_str = mm_port_qmi_kernel_data_mode_build_string_from_mask (ctx->kernel_data_modes_requested); mm_obj_dbg (self, "Updating kernel expected data format: %s -> %s", kernel_data_modes_current_str, kernel_data_modes_requested_str); if (ctx->kernel_data_modes_requested & MM_PORT_QMI_KERNEL_DATA_MODE_MUX_RMNET) expected_data_format_requested = QMI_DEVICE_EXPECTED_DATA_FORMAT_QMAP_PASS_THROUGH; else if (ctx->kernel_data_modes_requested & MM_PORT_QMI_KERNEL_DATA_MODE_MUX_QMIWWAN) expected_data_format_requested = QMI_DEVICE_EXPECTED_DATA_FORMAT_RAW_IP; else if (ctx->kernel_data_modes_requested & MM_PORT_QMI_KERNEL_DATA_MODE_RAW_IP) expected_data_format_requested = QMI_DEVICE_EXPECTED_DATA_FORMAT_RAW_IP; else if (ctx->kernel_data_modes_requested & MM_PORT_QMI_KERNEL_DATA_MODE_802_3) expected_data_format_requested = QMI_DEVICE_EXPECTED_DATA_FORMAT_802_3; else g_assert_not_reached (); if (!qmi_device_set_expected_data_format (ctx->device, expected_data_format_requested, &error)) { g_task_return_error (task, error); g_object_unref (task); return; } /* request reload */ ctx->kernel_data_modes_current = MM_PORT_QMI_KERNEL_DATA_MODE_NONE; /* Go on to next step */ ctx->step++; internal_setup_data_format_context_step (task); } static void main_mtu_ready (MMPortNet *data, GAsyncResult *res, GTask *task) { MMPortQmi *self; InternalSetupDataFormatContext *ctx; GError *error = NULL; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); if (!mm_port_net_link_setup_finish (data, res, &error)) { mm_obj_dbg (self, "failed to setup main MTU: %s", error->message); g_clear_error (&error); } /* Go on to next step */ ctx->step++; internal_setup_data_format_context_step (task); } static void setup_main_mtu (GTask *task) { MMPortQmi *self; InternalSetupDataFormatContext *ctx; guint mtu = MM_PORT_NET_MTU_DEFAULT; GError *error = NULL; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); /* qmi_wwan multiplexing logic requires main mtu set to the maximum data * aggregation size */ if (ctx->kernel_data_modes_requested & (MM_PORT_QMI_KERNEL_DATA_MODE_MUX_RMNET | MM_PORT_QMI_KERNEL_DATA_MODE_MUX_QMIWWAN)) { /* Load current max datagram size supported */ if (MM_PORT_QMI_DAP_IS_SUPPORTED_QMAP (ctx->wda_dl_dap_requested)) { mtu = ctx->wda_dl_dap_max_size_current; if ((ctx->kernel_data_modes_requested & MM_PORT_QMI_KERNEL_DATA_MODE_MUX_RMNET) && (mtu > RMNET_MAX_PACKET_SIZE)) { mm_obj_dbg (self, "mtu limited to maximum rmnet packet size"); mtu = RMNET_MAX_PACKET_SIZE; } } /* If no max aggregation size was specified by the modem (e.g. if we requested QMAP * aggregation protocol but the modem doesn't support it), skip */ if (!mtu) { mm_obj_dbg (self, "ignoring main mtu setup"); ctx->step++; internal_setup_data_format_context_step (task); return; } } /* Main MTU change can only be changed while in 802-3 */ if (!(ctx->kernel_data_modes_current & MM_PORT_QMI_KERNEL_DATA_MODE_802_3)) { mm_obj_dbg (self, "Updating kernel expected data format to 802-3 temporarily for main mtu setup"); if (!qmi_device_set_expected_data_format (ctx->device, QMI_DEVICE_EXPECTED_DATA_FORMAT_802_3, &error)) { g_prefix_error (&error, "Failed setting up 802.3 kernel data format before main mtu change: "); g_task_return_error (task, error); g_object_unref (task); return; } /* the sync kernel data mode step will fix this appropriately */ ctx->kernel_data_modes_current = MM_PORT_QMI_KERNEL_DATA_MODE_802_3; } mm_obj_dbg (self, "setting up main mtu: %u bytes", mtu); mm_port_net_link_setup (MM_PORT_NET (ctx->data), FALSE, mtu, NULL, (GAsyncReadyCallback) main_mtu_ready, task); } static void set_data_format_ready (QmiClientWda *client, GAsyncResult *res, GTask *task) { InternalSetupDataFormatContext *ctx; g_autoptr(QmiMessageWdaSetDataFormatOutput) output = NULL; g_autoptr(GError) error = NULL; ctx = g_task_get_task_data (task); output = qmi_client_wda_set_data_format_finish (client, res, &error); if (!output || !qmi_message_wda_set_data_format_output_get_result (output, &error)) { g_task_return_error (task, g_steal_pointer (&error)); g_object_unref (task); return; } /* store max aggregation size so that the main MTU logic works */ qmi_message_wda_set_data_format_output_get_downlink_data_aggregation_max_size (output, &ctx->wda_dl_dap_max_size_current, NULL); /* request reload */ ctx->wda_llp_current = QMI_WDA_LINK_LAYER_PROTOCOL_UNKNOWN; ctx->wda_ul_dap_current = QMI_WDA_DATA_AGGREGATION_PROTOCOL_DISABLED; ctx->wda_dl_dap_current = QMI_WDA_DATA_AGGREGATION_PROTOCOL_DISABLED; /* Go on to next step */ ctx->step++; internal_setup_data_format_context_step (task); } static void sync_wda_data_format (GTask *task) { MMPortQmi *self; InternalSetupDataFormatContext *ctx; g_autoptr(QmiMessageWdaSetDataFormatInput) input = NULL; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); if (ctx->wda_llp_current != ctx->wda_llp_requested) mm_obj_dbg (self, "Updating device link layer protocol: %s -> %s", qmi_wda_link_layer_protocol_get_string (ctx->wda_llp_current), qmi_wda_link_layer_protocol_get_string (ctx->wda_llp_requested)); if (ctx->wda_ul_dap_current != ctx->wda_ul_dap_requested) mm_obj_dbg (self, "Updating device uplink data aggregation protocol: %s -> %s", qmi_wda_data_aggregation_protocol_get_string (ctx->wda_ul_dap_current), qmi_wda_data_aggregation_protocol_get_string (ctx->wda_ul_dap_requested)); if (ctx->wda_dl_dap_current != ctx->wda_dl_dap_requested) mm_obj_dbg (self, "Updating device downlink data aggregation protocol: %s -> %s", qmi_wda_data_aggregation_protocol_get_string (ctx->wda_dl_dap_current), qmi_wda_data_aggregation_protocol_get_string (ctx->wda_dl_dap_requested)); input = qmi_message_wda_set_data_format_input_new (); qmi_message_wda_set_data_format_input_set_link_layer_protocol (input, ctx->wda_llp_requested, NULL); qmi_message_wda_set_data_format_input_set_uplink_data_aggregation_protocol (input, ctx->wda_ul_dap_requested, NULL); qmi_message_wda_set_data_format_input_set_downlink_data_aggregation_protocol (input, ctx->wda_dl_dap_requested, NULL); if (ctx->wda_dl_dap_requested != QMI_WDA_DATA_AGGREGATION_PROTOCOL_DISABLED) { if ((g_strcmp0 (self->priv->net_driver, "qmi_wwan") == 0) && (ctx->kernel_data_modes_supported & MM_PORT_QMI_KERNEL_DATA_MODE_MUX_RMNET)) qmi_message_wda_set_data_format_input_set_downlink_data_aggregation_max_size (input, DEFAULT_DOWNLINK_DATA_AGGREGATION_MAX_SIZE_QMI_WWAN_RMNET, NULL); else qmi_message_wda_set_data_format_input_set_downlink_data_aggregation_max_size (input, DEFAULT_DOWNLINK_DATA_AGGREGATION_MAX_SIZE, NULL); qmi_message_wda_set_data_format_input_set_downlink_data_aggregation_max_datagrams (input, DEFAULT_DOWNLINK_DATA_AGGREGATION_MAX_DATAGRAMS, NULL); } if (ctx->use_endpoint) qmi_message_wda_set_data_format_input_set_endpoint_info (input, self->priv->endpoint_type, self->priv->endpoint_interface_number, NULL); qmi_client_wda_set_data_format (QMI_CLIENT_WDA (ctx->wda), input, 10, g_task_get_cancellable (task), (GAsyncReadyCallback) set_data_format_ready, task); } static gboolean setup_data_format_completed (GTask *task) { MMPortQmi *self; InternalSetupDataFormatContext *ctx; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); /* if aggregation enabled we require link management supported; this covers the * case of old qmi_wwan drivers where add_mux/del_mux wasn't available yet */ if ((MM_PORT_QMI_DAP_IS_SUPPORTED_QMAP (ctx->wda_dl_dap_requested)) && (!(ctx->kernel_data_modes_current & (MM_PORT_QMI_KERNEL_DATA_MODE_MUX_RMNET | MM_PORT_QMI_KERNEL_DATA_MODE_MUX_QMIWWAN)))) { mm_obj_dbg (self, "cannot enable data aggregation: link management unsupported"); return FALSE; } /* check whether the current and requested ones are the same */ if ((ctx->kernel_data_modes_current & ctx->kernel_data_modes_requested) && (ctx->wda_llp_current == ctx->wda_llp_requested) && (ctx->wda_ul_dap_current == ctx->wda_ul_dap_requested) && (ctx->wda_dl_dap_current == ctx->wda_dl_dap_requested)) { g_task_return_boolean (task, TRUE); g_object_unref (task); return TRUE; } return FALSE; } static void check_data_format_combination (GTask *task) { MMPortQmi *self; InternalSetupDataFormatContext *ctx; gboolean first_iteration; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); first_iteration = (ctx->data_format_combination_i < 0); if (!first_iteration && setup_data_format_completed (task)) return; /* go on to the next supported combination */ for (++ctx->data_format_combination_i; ctx->data_format_combination_i <= (gint)G_N_ELEMENTS (data_format_combinations); ctx->data_format_combination_i++) { const DataFormatCombination *combination; g_autofree gchar *kernel_data_mode_str = NULL; combination = &data_format_combinations[ctx->data_format_combination_i]; if (!(ctx->kernel_data_modes_supported & combination->kernel_data_mode)) continue; if ((MM_PORT_QMI_DAP_IS_SUPPORTED_QMAP (combination->wda_dap)) && ((!ctx->wda_dap_supported) || (ctx->action != MM_PORT_QMI_SETUP_DATA_FORMAT_ACTION_SET_MULTIPLEX))) continue; kernel_data_mode_str = mm_port_qmi_kernel_data_mode_build_string_from_mask (combination->kernel_data_mode); mm_obj_dbg (self, "selected data format setup:"); mm_obj_dbg (self, " kernel data mode: %s", kernel_data_mode_str); mm_obj_dbg (self, " link layer protocol: %s", qmi_wda_link_layer_protocol_get_string (combination->wda_llp)); mm_obj_dbg (self, " aggregation protocol: %s", qmi_wda_data_aggregation_protocol_get_string (combination->wda_dap)); ctx->kernel_data_modes_requested = combination->kernel_data_mode; ctx->wda_llp_requested = combination->wda_llp; ctx->wda_ul_dap_requested = combination->wda_dap; ctx->wda_dl_dap_requested = combination->wda_dap; if (first_iteration && setup_data_format_completed (task)) return; /* Go on to next step */ ctx->step++; internal_setup_data_format_context_step (task); return; } g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "No more data format combinations supported"); g_object_unref (task); } static gboolean process_data_format_output (MMPortQmi *self, QmiMessageWdaGetDataFormatOutput *output, InternalSetupDataFormatContext *ctx, GError **error) { /* Let's consider the lack o the LLP TLV a hard error; it really would be strange * a module supporting WDA Get Data Format but not containing the LLP info */ if (!qmi_message_wda_get_data_format_output_get_link_layer_protocol (output, &ctx->wda_llp_current, error)) return FALSE; /* QMAP assumed supported if both uplink and downlink TLVs are given */ ctx->wda_dap_supported = TRUE; if (!qmi_message_wda_get_data_format_output_get_uplink_data_aggregation_protocol (output, &ctx->wda_ul_dap_current, NULL)) ctx->wda_dap_supported = FALSE; if (!qmi_message_wda_get_data_format_output_get_downlink_data_aggregation_protocol (output, &ctx->wda_dl_dap_current, NULL)) ctx->wda_dap_supported = FALSE; ctx->wda_dl_dap_max_size_current = 0; ctx->wda_dl_dap_max_datagrams_current = 0; if (ctx->wda_dl_dap_current != QMI_WDA_DATA_AGGREGATION_PROTOCOL_DISABLED) { qmi_message_wda_get_data_format_output_get_downlink_data_aggregation_max_size (output, &ctx->wda_dl_dap_max_size_current, NULL); qmi_message_wda_get_data_format_output_get_downlink_data_aggregation_max_datagrams (output, &ctx->wda_dl_dap_max_datagrams_current, NULL); } return TRUE; } static void get_data_format_ready (QmiClientWda *client, GAsyncResult *res, GTask *task) { MMPortQmi *self; InternalSetupDataFormatContext *ctx; g_autoptr(QmiMessageWdaGetDataFormatOutput) output = NULL; g_autoptr(GError) error = NULL; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); output = qmi_client_wda_get_data_format_finish (client, res, &error); if (!output || !qmi_message_wda_get_data_format_output_get_result (output, &error) || !process_data_format_output (self, output, ctx, &error)) { /* A 'missing argument' error when querying data format is seen in new * devices like the Quectel RM500Q, requiring the 'endpoint info' TLV. * When this happens, retry the step with the missing TLV. * * Note that this is not an additional step, we're still in the * GET_WDA_DATA_FORMAT step. */ if (g_error_matches (error, QMI_PROTOCOL_ERROR, QMI_PROTOCOL_ERROR_MISSING_ARGUMENT) && (self->priv->endpoint_type != QMI_DATA_ENDPOINT_TYPE_UNDEFINED)) { /* retry same step with endpoint info */ ctx->use_endpoint = TRUE; internal_setup_data_format_context_step (task); return; } /* otherwise, fatal */ g_task_return_error (task, g_steal_pointer (&error)); g_object_unref (task); return; } /* Go on to next step */ ctx->step++; internal_setup_data_format_context_step (task); } static void dpm_open_port_ready (QmiClientDpm *client, GAsyncResult *res, GTask *task) { g_autoptr(QmiMessageDpmOpenPortOutput) output = NULL; InternalSetupDataFormatContext *ctx; g_autoptr(GError) error = NULL; ctx = g_task_get_task_data (task); output = qmi_client_dpm_open_port_finish (client, res, &error); if (!output || !qmi_message_dpm_open_port_output_get_result (output, &error)) { g_task_return_error (task, g_steal_pointer (&error)); g_object_unref (task); return; } /* Go on to next step */ ctx->step++; internal_setup_data_format_context_step (task); } static void dpm_open_port (GTask *task) { MMPortQmi *self; InternalSetupDataFormatContext *ctx; QmiMessageDpmOpenPortInputHardwareDataPortsElement hw_port; g_autoptr(GArray) hw_data_ports = NULL; g_autoptr(QmiMessageDpmOpenPortInput) input = NULL; g_autofree gchar *tx_sysfs_path = NULL; g_autofree gchar *rx_sysfs_path = NULL; g_autofree gchar *tx_sysfs_str = NULL; g_autofree gchar *rx_sysfs_str = NULL; guint tx_id = 0; guint rx_id = 0; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); tx_sysfs_path = g_build_filename (self->priv->net_sysfs_path, "device", "modem", "tx_endpoint_id", NULL); rx_sysfs_path = g_build_filename (self->priv->net_sysfs_path, "device", "modem", "rx_endpoint_id", NULL); if (g_file_get_contents (rx_sysfs_path, &rx_sysfs_str, NULL, NULL) && g_file_get_contents (tx_sysfs_path, &tx_sysfs_str, NULL, NULL)) { if (rx_sysfs_str && tx_sysfs_str) { mm_get_uint_from_str (rx_sysfs_str, &rx_id); mm_get_uint_from_str (tx_sysfs_str, &tx_id); } } if (tx_id == 0 || rx_id == 0) { mm_obj_warn (self, "Unable to read TX and RX endpoint IDs from sysfs. skipping automatic DPM port opening."); /* Go on to next step */ ctx->step++; internal_setup_data_format_context_step (task); return; } mm_obj_dbg (self, "Opening DPM port with TX ID: %u and RX ID: %u", tx_id, rx_id); /* The modem TX endpoint connects with the IPA's RX port and the modem RX endpoint connects with the IPA's TX port. */ hw_port.rx_endpoint_number = tx_id; hw_port.tx_endpoint_number = rx_id; hw_port.endpoint_type = self->priv->endpoint_type; hw_port.interface_number = self->priv->endpoint_interface_number; hw_data_ports = g_array_new (FALSE, FALSE, sizeof (QmiMessageDpmOpenPortInputHardwareDataPortsElement)); g_array_append_val (hw_data_ports, hw_port); input = qmi_message_dpm_open_port_input_new (); qmi_message_dpm_open_port_input_set_hardware_data_ports (input, hw_data_ports, NULL); qmi_client_dpm_open_port (QMI_CLIENT_DPM (ctx->dpm), input, 10, g_task_get_cancellable (task), (GAsyncReadyCallback) dpm_open_port_ready, task); } static void allocate_client_dpm_ready (QmiDevice *device, GAsyncResult *res, GTask *task) { InternalSetupDataFormatContext *ctx; GError *error = NULL; ctx = g_task_get_task_data (task); ctx->dpm = qmi_device_allocate_client_finish (device, res, &error); if (!ctx->dpm) { g_task_return_error (task, error); g_object_unref (task); return; } /* Go on to next step */ ctx->step++; internal_setup_data_format_context_step (task); } static void allocate_client_wda_ready (QmiDevice *device, GAsyncResult *res, GTask *task) { InternalSetupDataFormatContext *ctx; GError *error = NULL; ctx = g_task_get_task_data (task); ctx->wda = qmi_device_allocate_client_finish (device, res, &error); if (!ctx->wda) { g_task_return_error (task, error); g_object_unref (task); return; } /* Go on to next step */ ctx->step++; internal_setup_data_format_context_step (task); } static void internal_setup_data_format_context_step (GTask *task) { MMPortQmi *self; InternalSetupDataFormatContext *ctx; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); switch (ctx->step) { case INTERNAL_SETUP_DATA_FORMAT_STEP_FIRST: ctx->step++; /* Fall through */ case INTERNAL_SETUP_DATA_FORMAT_STEP_ALLOCATE_WDA_CLIENT: /* Allocate new WDA client, only on first loop iteration */ g_assert (!ctx->wda); qmi_device_allocate_client (ctx->device, QMI_SERVICE_WDA, QMI_CID_NONE, 10, g_task_get_cancellable (task), (GAsyncReadyCallback) allocate_client_wda_ready, task); return; case INTERNAL_SETUP_DATA_FORMAT_STEP_SUPPORTED_KERNEL_DATA_MODES: /* Load kernel data format capabilities, only on first loop iteration */ ctx->kernel_data_modes_supported = load_supported_kernel_data_modes (self, ctx->device); ctx->step++; /* Fall through */ case INTERNAL_SETUP_DATA_FORMAT_STEP_RETRY: ctx->step++; /* Fall through */ case INTERNAL_SETUP_DATA_FORMAT_STEP_CURRENT_KERNEL_DATA_MODES: /* Only reload kernel data modes if it was updated or on first loop */ if (ctx->kernel_data_modes_current == MM_PORT_QMI_KERNEL_DATA_MODE_NONE) ctx->kernel_data_modes_current = load_current_kernel_data_modes (self, ctx->device); ctx->step++; /* Fall through */ case INTERNAL_SETUP_DATA_FORMAT_STEP_ALLOCATE_DPM_CLIENT: /* Only allocate new DPM client on first loop */ if ((g_strcmp0 (self->priv->net_driver, "ipa") == 0) && (ctx->data_format_combination_i < 0)) { g_assert (!ctx->dpm); qmi_device_allocate_client (ctx->device, QMI_SERVICE_DPM, QMI_CID_NONE, 10, g_task_get_cancellable (task), (GAsyncReadyCallback) allocate_client_dpm_ready, task); return; } ctx->step++; /* Fall through */ case INTERNAL_SETUP_DATA_FORMAT_STEP_DPM_OPEN: /* Only for IPA based setups, open dpm port */ if (g_strcmp0 (self->priv->net_driver, "ipa") == 0) { dpm_open_port (task); return; } ctx->step++; /* Fall through */ case INTERNAL_SETUP_DATA_FORMAT_STEP_GET_WDA_DATA_FORMAT: /* Only reload WDA data format if it was updated or on first loop */ if (ctx->wda_llp_current == QMI_WDA_LINK_LAYER_PROTOCOL_UNKNOWN) { g_autoptr(QmiMessageWdaGetDataFormatInput) input = NULL; if (ctx->use_endpoint) { input = qmi_message_wda_get_data_format_input_new (); qmi_message_wda_get_data_format_input_set_endpoint_info (input, self->priv->endpoint_type, self->priv->endpoint_interface_number, NULL); } qmi_client_wda_get_data_format (QMI_CLIENT_WDA (ctx->wda), input, 10, g_task_get_cancellable (task), (GAsyncReadyCallback) get_data_format_ready, task); return; } ctx->step++; /* Fall through */ case INTERNAL_SETUP_DATA_FORMAT_STEP_QUERY_DONE: { g_autofree gchar *kernel_data_modes_str = NULL; kernel_data_modes_str = mm_port_qmi_kernel_data_mode_build_string_from_mask (ctx->kernel_data_modes_current); mm_obj_dbg (self, "current data format setup:"); mm_obj_dbg (self, " kernel data modes: %s", kernel_data_modes_str); mm_obj_dbg (self, " link layer protocol: %s", qmi_wda_link_layer_protocol_get_string (ctx->wda_llp_current)); mm_obj_dbg (self, " aggregation protocol ul: %s", qmi_wda_data_aggregation_protocol_get_string (ctx->wda_ul_dap_current)); mm_obj_dbg (self, " aggregation protocol dl: %s", qmi_wda_data_aggregation_protocol_get_string (ctx->wda_dl_dap_current)); if (ctx->action == MM_PORT_QMI_SETUP_DATA_FORMAT_ACTION_QUERY) { g_task_return_boolean (task, TRUE); g_object_unref (task); return; } ctx->step++; } /* Fall through */ case INTERNAL_SETUP_DATA_FORMAT_STEP_CHECK_DATA_FORMAT_COMBINATION: /* This step is the one that may complete the async operation * successfully */ check_data_format_combination (task); return; case INTERNAL_SETUP_DATA_FORMAT_STEP_SYNC_WDA_DATA_FORMAT: if ((ctx->wda_llp_current != ctx->wda_llp_requested) || (ctx->wda_ul_dap_current != ctx->wda_ul_dap_requested) || (ctx->wda_dl_dap_current != ctx->wda_dl_dap_requested)) { sync_wda_data_format (task); return; } ctx->step++; /* Fall through */ case INTERNAL_SETUP_DATA_FORMAT_STEP_SETUP_MAIN_MTU: /* qmi_wwan add_mux/del_mux based logic requires main MTU set to the maximum * data aggregation size reported by the modem. */ if (g_strcmp0 (self->priv->net_driver, "qmi_wwan") == 0) { setup_main_mtu (task); return; } ctx->step++; /* Fall through */ case INTERNAL_SETUP_DATA_FORMAT_STEP_SYNC_KERNEL_DATA_MODE: if (!(ctx->kernel_data_modes_current & ctx->kernel_data_modes_requested)) { sync_kernel_data_mode (task); return; } ctx->step++; /* Fall through */ case INTERNAL_SETUP_DATA_FORMAT_STEP_LAST: /* jump back to first step to reload current state after * the updates have been done */ ctx->step = INTERNAL_SETUP_DATA_FORMAT_STEP_RETRY; internal_setup_data_format_context_step (task); return; default: g_assert_not_reached (); } } static void internal_setup_data_format (MMPortQmi *self, QmiDevice *device, MMPort *data, /* may be NULL in query */ MMPortQmiSetupDataFormatAction action, GAsyncReadyCallback callback, gpointer user_data) { InternalSetupDataFormatContext *ctx; GTask *task; task = g_task_new (self, NULL, callback, user_data); if (!device) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_WRONG_STATE, "Port must be open to setup data format"); g_object_unref (task); return; } if (self->priv->wda_unsupported) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "Setting up data format is not supported"); g_object_unref (task); return; } ctx = g_slice_new0 (InternalSetupDataFormatContext); ctx->device = g_object_ref (device); ctx->data = data ? g_object_ref (data) : NULL; ctx->action = action; ctx->step = INTERNAL_SETUP_DATA_FORMAT_STEP_FIRST; ctx->data_format_combination_i = -1; ctx->kernel_data_modes_current = MM_PORT_QMI_KERNEL_DATA_MODE_NONE; ctx->kernel_data_modes_requested = MM_PORT_QMI_KERNEL_DATA_MODE_NONE; ctx->kernel_data_modes_supported = MM_PORT_QMI_KERNEL_DATA_MODE_NONE; ctx->wda_llp_current = QMI_WDA_LINK_LAYER_PROTOCOL_UNKNOWN; ctx->wda_llp_requested = QMI_WDA_LINK_LAYER_PROTOCOL_UNKNOWN; ctx->wda_ul_dap_current = QMI_WDA_DATA_AGGREGATION_PROTOCOL_DISABLED; ctx->wda_ul_dap_requested = QMI_WDA_DATA_AGGREGATION_PROTOCOL_DISABLED; ctx->wda_dl_dap_current = QMI_WDA_DATA_AGGREGATION_PROTOCOL_DISABLED; ctx->wda_dl_dap_requested = QMI_WDA_DATA_AGGREGATION_PROTOCOL_DISABLED; if (mm_port_get_subsys (MM_PORT (self)) == MM_PORT_SUBSYS_QRTR) ctx->use_endpoint = TRUE; g_task_set_task_data (task, ctx, (GDestroyNotify) internal_setup_data_format_context_free); internal_setup_data_format_context_step (task); } /*****************************************************************************/ typedef struct { MMPort *data; QmiDevice *device; MMPortQmiSetupDataFormatAction action; } SetupDataFormatContext; static void setup_data_format_context_free (SetupDataFormatContext *ctx) { g_clear_object (&ctx->device); g_clear_object (&ctx->data); g_slice_free (SetupDataFormatContext, ctx); } gboolean mm_port_qmi_setup_data_format_finish (MMPortQmi *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void internal_setup_data_format_ready (MMPortQmi *self, GAsyncResult *res, GTask *task) { GError *error = NULL; if (!internal_setup_data_format_finish (self, res, &self->priv->kernel_data_modes, &self->priv->llp, &self->priv->dap, NULL, /* not expected to update */ NULL, /* not expected to update */ &error)) g_task_return_error (task, error); else { self->priv->first_multiplex_setup = FALSE; g_task_return_boolean (task, TRUE); } g_object_unref (task); } static void setup_data_format_internal_reset_ready (MMPortQmi *self, GAsyncResult *res, GTask *task) { SetupDataFormatContext *ctx; GError *error = NULL; ctx = g_task_get_task_data (task); if (!internal_reset_finish (self, res, &error)) { g_prefix_error (&error, "Couldn't reset interface before setting up data format: "); g_task_return_error (task, error); g_object_unref (task); return; } /* call internal method with the already open QmiDevice */ internal_setup_data_format (self, ctx->device, ctx->data, ctx->action, (GAsyncReadyCallback)internal_setup_data_format_ready, task); } static guint count_links_setup (MMPortQmi *self, MMPort *data) { if (self->priv->kernel_data_modes & MM_PORT_QMI_KERNEL_DATA_MODE_MUX_RMNET) { g_autoptr(GPtrArray) links = NULL; g_autoptr(GError) error = NULL; if (!qmi_device_list_links (self->priv->qmi_device, mm_port_get_device (data), &links, &error)) { mm_obj_warn (self, "couldn't list links in %s: %s", mm_port_get_device (data), error->message); return 0; } if (links) return links->len; /* No list of links returned, so there are none */ return 0; } if (self->priv->kernel_data_modes & MM_PORT_QMI_KERNEL_DATA_MODE_MUX_QMIWWAN) return count_preallocated_links_setup (self); return 0; } void mm_port_qmi_setup_data_format (MMPortQmi *self, MMPort *data, MMPortQmiSetupDataFormatAction action, GAsyncReadyCallback callback, gpointer user_data) { SetupDataFormatContext *ctx; GTask *task; /* External calls are never query */ g_assert (action != MM_PORT_QMI_SETUP_DATA_FORMAT_ACTION_QUERY); g_assert (MM_IS_PORT (data)); task = g_task_new (self, NULL, callback, user_data); if (!self->priv->qmi_device) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_WRONG_STATE, "Port not open"); g_object_unref (task); return; } if (self->priv->wda_unsupported) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "Setting up data format is unsupported"); g_object_unref (task); return; } if ((action == MM_PORT_QMI_SETUP_DATA_FORMAT_ACTION_SET_MULTIPLEX) && (self->priv->kernel_data_modes & (MM_PORT_QMI_KERNEL_DATA_MODE_MUX_RMNET | MM_PORT_QMI_KERNEL_DATA_MODE_MUX_QMIWWAN)) && MM_PORT_QMI_DAP_IS_SUPPORTED_QMAP (self->priv->dap)) { mm_obj_dbg (self, "multiplex support already available when setting up data format"); /* If this is the first time that multiplex is used, perform anyway the internal reset operation, so that the links are properly managed */ if (!self->priv->first_multiplex_setup) { g_task_return_boolean (task, TRUE); g_object_unref (task); return; } } if ((action == MM_PORT_QMI_SETUP_DATA_FORMAT_ACTION_SET_DEFAULT) && (((self->priv->kernel_data_modes & MM_PORT_QMI_KERNEL_DATA_MODE_RAW_IP) && (self->priv->llp == QMI_WDA_LINK_LAYER_PROTOCOL_RAW_IP)) || ((self->priv->kernel_data_modes & MM_PORT_QMI_KERNEL_DATA_MODE_802_3) && (self->priv->llp == QMI_WDA_LINK_LAYER_PROTOCOL_802_3))) && !MM_PORT_QMI_DAP_IS_SUPPORTED_QMAP (self->priv->dap)) { mm_obj_dbg (self, "multiplex support already disabled when setting up data format"); g_task_return_boolean (task, TRUE); g_object_unref (task); return; } /* support switching from multiplex to non-multiplex, but only if there are no active * links allocated */ if ((action == MM_PORT_QMI_SETUP_DATA_FORMAT_ACTION_SET_DEFAULT) && MM_PORT_QMI_DAP_IS_SUPPORTED_QMAP (self->priv->dap)) { guint n_links_setup; n_links_setup = count_links_setup (self, data); if (n_links_setup > 0) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_WRONG_STATE, "Cannot switch to non-multiplex setup: %u links already setup exist", n_links_setup); g_object_unref (task); return; } } ctx = g_slice_new0 (SetupDataFormatContext); ctx->data = g_object_ref (data); ctx->device = g_object_ref (self->priv->qmi_device); ctx->action = action; g_task_set_task_data (task, ctx, (GDestroyNotify)setup_data_format_context_free); internal_reset (self, data, ctx->device, (GAsyncReadyCallback)setup_data_format_internal_reset_ready, task); } /*****************************************************************************/ typedef enum { PORT_OPEN_STEP_FIRST, PORT_OPEN_STEP_CHECK_OPENING, PORT_OPEN_STEP_CHECK_ALREADY_OPEN, PORT_OPEN_STEP_DEVICE_NEW, PORT_OPEN_STEP_OPEN_WITHOUT_DATA_FORMAT, PORT_OPEN_STEP_SETUP_DATA_FORMAT, PORT_OPEN_STEP_CLOSE_BEFORE_OPEN_WITH_DATA_FORMAT, PORT_OPEN_STEP_OPEN_WITH_DATA_FORMAT, PORT_OPEN_STEP_LAST } PortOpenStep; typedef struct { QmiDevice *device; GError *error; PortOpenStep step; gboolean set_data_format; MMPortQmiKernelDataMode kernel_data_modes; gboolean ctl_raw_ip_unsupported; } PortOpenContext; static void port_open_context_free (PortOpenContext *ctx) { g_assert (!ctx->error); g_clear_object (&ctx->device); g_slice_free (PortOpenContext, ctx); } gboolean mm_port_qmi_open_finish (MMPortQmi *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void port_open_step (GTask *task); static void port_open_complete_with_error (GTask *task) { MMPortQmi *self; PortOpenContext *ctx; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); g_assert (ctx->error); self->priv->in_progress = FALSE; g_task_return_error (task, g_steal_pointer (&ctx->error)); g_object_unref (task); } static void qmi_device_close_on_error_ready (QmiDevice *qmi_device, GAsyncResult *res, GTask *task) { MMPortQmi *self; g_autoptr(GError) error = NULL; self = g_task_get_source_object (task); if (!qmi_device_close_finish (qmi_device, res, &error)) mm_obj_warn (self, "Couldn't close QMI device after failed open sequence: %s", error->message); port_open_complete_with_error (task); } static void qmi_device_open_second_ready (QmiDevice *qmi_device, GAsyncResult *res, GTask *task) { MMPortQmi *self; PortOpenContext *ctx; g_autoptr(GError) error = NULL; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); if (!qmi_device_open_finish (qmi_device, res, &error)) { /* Not all devices support raw-ip, which is the first thing we try * by default. Detect this case, and retry with 802.3 if so. */ if ((g_strcmp0 (self->priv->net_driver, "qmi_wwan") == 0) && g_error_matches (error, QMI_PROTOCOL_ERROR, QMI_PROTOCOL_ERROR_INVALID_DATA_FORMAT) && (ctx->kernel_data_modes & MM_PORT_QMI_KERNEL_DATA_MODE_RAW_IP)) { /* switch to 802.3 right away, so that the logic can successfully go on after that */ qmi_device_set_expected_data_format (qmi_device, QMI_DEVICE_EXPECTED_DATA_FORMAT_802_3, NULL); ctx->ctl_raw_ip_unsupported = TRUE; port_open_step (task); return; } /* Otherwise, fatal */ ctx->error = g_steal_pointer (&error); } else { /* If the open with CTL data format is sucessful, update all settings * that we would have received with the internal setup data format * process */ self->priv->kernel_data_modes = ctx->kernel_data_modes; if (ctx->kernel_data_modes & MM_PORT_QMI_KERNEL_DATA_MODE_RAW_IP) self->priv->llp = QMI_WDA_LINK_LAYER_PROTOCOL_RAW_IP; else if (ctx->kernel_data_modes & MM_PORT_QMI_KERNEL_DATA_MODE_802_3) self->priv->llp = QMI_WDA_LINK_LAYER_PROTOCOL_802_3; else g_assert_not_reached (); self->priv->dap = QMI_WDA_DATA_AGGREGATION_PROTOCOL_DISABLED; self->priv->max_multiplexed_links = 0; } /* In both error and success, we go to last step */ ctx->step++; port_open_step (task); } static void qmi_device_close_to_reopen_ready (QmiDevice *qmi_device, GAsyncResult *res, GTask *task) { MMPortQmi *self; PortOpenContext *ctx; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); if (!qmi_device_close_finish (qmi_device, res, &ctx->error)) { mm_obj_warn (self, "Couldn't close QMI device to reopen it"); ctx->step = PORT_OPEN_STEP_LAST; } else ctx->step++; port_open_step (task); } static void open_internal_setup_data_format_ready (MMPortQmi *self, GAsyncResult *res, GTask *task) { PortOpenContext *ctx; g_autoptr(GError) error = NULL; ctx = g_task_get_task_data (task); if (!internal_setup_data_format_finish (self, res, &self->priv->kernel_data_modes, &self->priv->llp, &self->priv->dap, &self->priv->max_multiplexed_links, &self->priv->preallocated_links_needed, &error)) { /* Continue with fallback to LLP requested via CTL */ mm_obj_warn (self, "Couldn't setup data format: %s", error->message); self->priv->wda_unsupported = TRUE; ctx->step++; } else { /* on success, we're done */ ctx->step = PORT_OPEN_STEP_LAST; } port_open_step (task); } static void qmi_device_open_first_ready (QmiDevice *qmi_device, GAsyncResult *res, GTask *task) { PortOpenContext *ctx; ctx = g_task_get_task_data (task); if (!qmi_device_open_finish (qmi_device, res, &ctx->error)) /* Error opening the device */ ctx->step = PORT_OPEN_STEP_LAST; else if (!ctx->set_data_format) /* If not setting data format, we're done */ ctx->step = PORT_OPEN_STEP_LAST; else /* Go on to next step */ ctx->step++; port_open_step (task); } static void qmi_device_new_ready (GObject *unused, GAsyncResult *res, GTask *task) { PortOpenContext *ctx; ctx = g_task_get_task_data (task); /* Store the device in the context until the operation is fully done, * so that we return IN_PROGRESS errors until we finish this async * operation. */ ctx->device = qmi_device_new_finish (res, &ctx->error); if (!ctx->device) /* Error creating the device */ ctx->step = PORT_OPEN_STEP_LAST; else /* Go on to next step */ ctx->step++; port_open_step (task); } static void port_open_step (GTask *task) { MMPortQmi *self; PortOpenContext *ctx; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); switch (ctx->step) { case PORT_OPEN_STEP_FIRST: mm_obj_dbg (self, "Opening QMI device..."); self->priv->first_multiplex_setup = TRUE; ctx->step++; /* Fall through */ case PORT_OPEN_STEP_CHECK_OPENING: mm_obj_dbg (self, "Checking if QMI device already opening..."); if (self->priv->in_progress) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_IN_PROGRESS, "QMI device open/close operation in progress"); g_object_unref (task); return; } ctx->step++; /* Fall through */ case PORT_OPEN_STEP_CHECK_ALREADY_OPEN: mm_obj_dbg (self, "Checking if QMI device already open..."); if (self->priv->qmi_device) { g_task_return_boolean (task, TRUE); g_object_unref (task); return; } ctx->step++; /* Fall through */ case PORT_OPEN_STEP_DEVICE_NEW: /* We flag in this point that we're opening. From now on, if we stop * for whatever reason, we should clear this flag. We do this by ensuring * that all callbacks go through the LAST step for completing. */ self->priv->in_progress = TRUE; #if defined WITH_QRTR if (self->priv->node) { mm_obj_dbg (self, "Creating QMI device from QRTR node..."); qmi_device_new_from_node (self->priv->node, g_task_get_cancellable (task), (GAsyncReadyCallback) qmi_device_new_ready, task); return; } #endif { g_autoptr(GFile) file = NULL; g_autofree gchar *fullpath = NULL; fullpath = g_strdup_printf ("/dev/%s", mm_port_get_device (MM_PORT (self))); file = g_file_new_for_path (fullpath); mm_obj_dbg (self, "Creating QMI device..."); qmi_device_new (file, g_task_get_cancellable (task), (GAsyncReadyCallback) qmi_device_new_ready, task); return; } case PORT_OPEN_STEP_OPEN_WITHOUT_DATA_FORMAT: if (!self->priv->wda_unsupported || !ctx->set_data_format) { QmiDeviceOpenFlags open_flags; g_autofree gchar *open_flags_str = NULL; /* Now open the QMI device without any data format CTL flag */ open_flags = (QMI_DEVICE_OPEN_FLAGS_VERSION_INFO | QMI_DEVICE_OPEN_FLAGS_PROXY); open_flags_str = qmi_device_open_flags_build_string_from_mask (open_flags); mm_obj_dbg (self, "Opening device with flags: %s...", open_flags_str); qmi_device_open (ctx->device, open_flags, 45, g_task_get_cancellable (task), (GAsyncReadyCallback) qmi_device_open_first_ready, task); return; } ctx->step++; /* Fall through */ case PORT_OPEN_STEP_SETUP_DATA_FORMAT: if (qmi_device_is_open (ctx->device)) { internal_setup_data_format (self, ctx->device, NULL, MM_PORT_QMI_SETUP_DATA_FORMAT_ACTION_QUERY, (GAsyncReadyCallback) open_internal_setup_data_format_ready, task); return; } ctx->step++; /* Fall through */ case PORT_OPEN_STEP_CLOSE_BEFORE_OPEN_WITH_DATA_FORMAT: /* This fallback only applies when WDA unsupported */ if (qmi_device_is_open (ctx->device)) { mm_obj_dbg (self, "Closing device to reopen it right away..."); qmi_device_close_async (ctx->device, 5, g_task_get_cancellable (task), (GAsyncReadyCallback) qmi_device_close_to_reopen_ready, task); return; } ctx->step++; /* Fall through */ case PORT_OPEN_STEP_OPEN_WITH_DATA_FORMAT: { QmiDeviceOpenFlags open_flags; g_autofree gchar *open_flags_str = NULL; /* Common open flags */ open_flags = (QMI_DEVICE_OPEN_FLAGS_VERSION_INFO | QMI_DEVICE_OPEN_FLAGS_PROXY | QMI_DEVICE_OPEN_FLAGS_NET_NO_QOS_HEADER); ctx->kernel_data_modes = load_current_kernel_data_modes (self, ctx->device); /* Skip trying raw-ip if we already tried and it failed */ if (ctx->ctl_raw_ip_unsupported) ctx->kernel_data_modes &= ~MM_PORT_QMI_KERNEL_DATA_MODE_RAW_IP; /* Need to reopen setting 802.3/raw-ip using CTL */ if (ctx->kernel_data_modes & MM_PORT_QMI_KERNEL_DATA_MODE_RAW_IP) open_flags |= QMI_DEVICE_OPEN_FLAGS_NET_RAW_IP; else if (ctx->kernel_data_modes & MM_PORT_QMI_KERNEL_DATA_MODE_802_3) open_flags |= QMI_DEVICE_OPEN_FLAGS_NET_802_3; else { /* Set error and jump to last step, so that we cleanly close the device * in case we need to reopen it right away */ ctx->error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Unexpected kernel data mode: cannot setup using CTL"); ctx->step = PORT_OPEN_STEP_LAST; port_open_step (task); return; } open_flags_str = qmi_device_open_flags_build_string_from_mask (open_flags); mm_obj_dbg (self, "Reopening device with flags: %s...", open_flags_str); qmi_device_open (ctx->device, open_flags, 10, g_task_get_cancellable (task), (GAsyncReadyCallback) qmi_device_open_second_ready, task); return; } case PORT_OPEN_STEP_LAST: if (ctx->error) { mm_obj_dbg (self, "QMI port open operation failed: %s", ctx->error->message); if (ctx->device) { qmi_device_close_async (ctx->device, 5, NULL, (GAsyncReadyCallback) qmi_device_close_on_error_ready, task); return; } port_open_complete_with_error (task); return; } mm_obj_dbg (self, "QMI port open operation finished successfully"); /* Store device in private info */ g_assert (ctx->device); g_assert (!self->priv->qmi_device); self->priv->qmi_device = g_object_ref (ctx->device); setup_monitoring (self, ctx->device); self->priv->in_progress = FALSE; g_task_return_boolean (task, TRUE); g_object_unref (task); return; default: g_assert_not_reached (); } } void mm_port_qmi_open (MMPortQmi *self, gboolean set_data_format, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { PortOpenContext *ctx; GTask *task; ctx = g_slice_new0 (PortOpenContext); ctx->step = PORT_OPEN_STEP_FIRST; ctx->set_data_format = set_data_format; ctx->kernel_data_modes = MM_PORT_QMI_KERNEL_DATA_MODE_NONE; task = g_task_new (self, cancellable, callback, user_data); g_task_set_task_data (task, ctx, (GDestroyNotify)port_open_context_free); port_open_step (task); } /*****************************************************************************/ gboolean mm_port_qmi_is_open (MMPortQmi *self) { g_return_val_if_fail (MM_IS_PORT_QMI (self), FALSE); return !!self->priv->qmi_device; } /*****************************************************************************/ /* Sets network details that the QMI port should be aware of before even * a data connection is started. */ void mm_port_qmi_set_net_details (MMPortQmi *self, MMPort *first_net) { MMKernelDevice *first_net_dev; first_net_dev = mm_port_peek_kernel_device (first_net); g_assert (MM_IS_PORT_QMI (self)); g_assert (!self->priv->net_driver); self->priv->net_driver = g_strdup (mm_kernel_device_get_driver (first_net_dev)); g_assert (!self->priv->net_sysfs_path); self->priv->net_sysfs_path = g_strdup (mm_kernel_device_get_sysfs_path (first_net_dev)); g_assert (!self->priv->net_preallocated_links_requested); self->priv->net_preallocated_links_requested = mm_kernel_device_get_global_property_as_int (first_net_dev, "ID_MM_QMI_PREALLOCATED_LINKS"); initialize_endpoint_info (self); } /*****************************************************************************/ typedef struct { QmiDevice *qmi_device; } PortQmiCloseContext; static void port_qmi_close_context_free (PortQmiCloseContext *ctx) { g_clear_object (&ctx->qmi_device); g_slice_free (PortQmiCloseContext, ctx); } gboolean mm_port_qmi_close_finish (MMPortQmi *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void qmi_device_close_ready (QmiDevice *qmi_device, GAsyncResult *res, GTask *task) { GError *error = NULL; MMPortQmi *self; self = g_task_get_source_object (task); g_assert (!self->priv->qmi_device); self->priv->in_progress = FALSE; if (!qmi_device_close_finish (qmi_device, res, &error)) g_task_return_error (task, error); else g_task_return_boolean (task, TRUE); g_object_unref (task); } void mm_port_qmi_close (MMPortQmi *self, GAsyncReadyCallback callback, gpointer user_data) { PortQmiCloseContext *ctx; GTask *task; GList *l; g_return_if_fail (MM_IS_PORT_QMI (self)); task = g_task_new (self, NULL, callback, user_data); if (self->priv->in_progress) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_IN_PROGRESS, "QMI device open/close operation in progress"); g_object_unref (task); return; } if (!self->priv->qmi_device) { g_task_return_boolean (task, TRUE); g_object_unref (task); return; } self->priv->in_progress = TRUE; /* Store device to close in the context */ ctx = g_slice_new0 (PortQmiCloseContext); ctx->qmi_device = g_steal_pointer (&self->priv->qmi_device); g_task_set_task_data (task, ctx, (GDestroyNotify)port_qmi_close_context_free); /* Reset monitoring logic */ reset_monitoring (self, ctx->qmi_device); /* Release all allocated clients */ for (l = self->priv->services; l; l = g_list_next (l)) { ServiceInfo *info = l->data; mm_obj_dbg (self, "Releasing client for service '%s'...", qmi_service_get_string (info->service)); qmi_device_release_client (ctx->qmi_device, info->client, QMI_DEVICE_RELEASE_CLIENT_FLAGS_RELEASE_CID, 3, NULL, NULL, NULL); g_clear_object (&info->client); } g_list_free_full (self->priv->services, g_free); self->priv->services = NULL; /* Cleanup preallocated links, if any */ if (self->priv->preallocated_links) { delete_preallocated_links (ctx->qmi_device, self->priv->preallocated_links); g_clear_pointer (&self->priv->preallocated_links, g_array_unref); } g_clear_object (&self->priv->preallocated_links_main); qmi_device_close_async (ctx->qmi_device, 5, NULL, (GAsyncReadyCallback)qmi_device_close_ready, task); } /*****************************************************************************/ MMPortQmi * mm_port_qmi_new (const gchar *name, MMPortSubsys subsys) { return MM_PORT_QMI (g_object_new (MM_TYPE_PORT_QMI, MM_PORT_DEVICE, name, MM_PORT_SUBSYS, subsys, MM_PORT_TYPE, MM_PORT_TYPE_QMI, NULL)); } #if defined WITH_QRTR MMPortQmi * mm_port_qmi_new_from_node (const gchar *name, QrtrNode *node) { return MM_PORT_QMI (g_object_new (MM_TYPE_PORT_QMI, "node", node, MM_PORT_DEVICE, name, MM_PORT_SUBSYS, MM_PORT_SUBSYS_QRTR, MM_PORT_TYPE, MM_PORT_TYPE_QMI, NULL)); } #endif static void mm_port_qmi_init (MMPortQmi *self) { self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, MM_TYPE_PORT_QMI, MMPortQmiPrivate); } #if defined WITH_QRTR static void set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { MMPortQmi *self = MM_PORT_QMI (object); switch (prop_id) { case PROP_NODE: g_clear_object (&self->priv->node); self->priv->node = g_value_dup_object (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) { MMPortQmi *self = MM_PORT_QMI (object); switch (prop_id) { case PROP_NODE: g_value_set_object (value, self->priv->node); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } #endif /* defined WITH_QRTR */ static void dispose (GObject *object) { MMPortQmi *self = MM_PORT_QMI (object); GList *l; /* Deallocate all clients */ for (l = self->priv->services; l; l = g_list_next (l)) { ServiceInfo *info = l->data; if (info->client) g_object_unref (info->client); } g_list_free_full (self->priv->services, g_free); self->priv->services = NULL; /* Cleanup preallocated links, if any */ if (self->priv->preallocated_links && self->priv->qmi_device) delete_preallocated_links (self->priv->qmi_device, self->priv->preallocated_links); g_clear_pointer (&self->priv->preallocated_links, g_array_unref); g_clear_object (&self->priv->preallocated_links_main); /* Clear node object */ #if defined WITH_QRTR g_clear_object (&self->priv->node); #endif /* Clear device object */ reset_monitoring (self, self->priv->qmi_device); g_clear_object (&self->priv->qmi_device); g_clear_pointer (&self->priv->net_driver, g_free); g_clear_pointer (&self->priv->net_sysfs_path, g_free); G_OBJECT_CLASS (mm_port_qmi_parent_class)->dispose (object); } static void mm_port_qmi_class_init (MMPortQmiClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); g_type_class_add_private (object_class, sizeof (MMPortQmiPrivate)); /* Virtual methods */ object_class->dispose = dispose; #if defined WITH_QRTR object_class->get_property = get_property; object_class->set_property = set_property; properties[PROP_NODE] = g_param_spec_object ("node", "Qrtr Node", "Qrtr node to be probed", QRTR_TYPE_NODE, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY); g_object_class_install_properties (object_class, PROP_LAST, properties); #endif } ModemManager-1.23.4-dev/src/mm-port-qmi.h000066400000000000000000000210361456466623000200460ustar00rootroot00000000000000/* -*- 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) 2012 Google, Inc. */ #ifndef MM_PORT_QMI_H #define MM_PORT_QMI_H #include #include #include #include #include #include "mm-port.h" typedef enum { /*< underscore_name=mm_port_qmi_kernel_data_mode >*/ MM_PORT_QMI_KERNEL_DATA_MODE_NONE = 0, /* ethernet packets over the main network interface */ MM_PORT_QMI_KERNEL_DATA_MODE_802_3 = 1 << 0, /* raw-ip packets over the main network interface */ MM_PORT_QMI_KERNEL_DATA_MODE_RAW_IP = 1 << 1, /* multiplexing support setup with rmnet */ MM_PORT_QMI_KERNEL_DATA_MODE_MUX_RMNET = 1 << 2, /* multiplexing support setup with qmi_wwan add_mux/del_mux */ MM_PORT_QMI_KERNEL_DATA_MODE_MUX_QMIWWAN = 1 << 3, } MMPortQmiKernelDataMode; #define MM_PORT_QMI_DAP_IS_SUPPORTED_QMAP(dap) \ (dap == QMI_WDA_DATA_AGGREGATION_PROTOCOL_QMAPV5 || \ dap == QMI_WDA_DATA_AGGREGATION_PROTOCOL_QMAPV4 || \ dap == QMI_WDA_DATA_AGGREGATION_PROTOCOL_QMAP) #define MM_TYPE_PORT_QMI (mm_port_qmi_get_type ()) #define MM_PORT_QMI(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_PORT_QMI, MMPortQmi)) #define MM_PORT_QMI_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_PORT_QMI, MMPortQmiClass)) #define MM_IS_PORT_QMI(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_PORT_QMI)) #define MM_IS_PORT_QMI_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_PORT_QMI)) #define MM_PORT_QMI_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_PORT_QMI, MMPortQmiClass)) typedef struct _MMPortQmi MMPortQmi; typedef struct _MMPortQmiClass MMPortQmiClass; typedef struct _MMPortQmiPrivate MMPortQmiPrivate; struct _MMPortQmi { MMPort parent; MMPortQmiPrivate *priv; }; struct _MMPortQmiClass { MMPortClass parent; }; GType mm_port_qmi_get_type (void); G_DEFINE_AUTOPTR_CLEANUP_FUNC (MMPortQmi, g_object_unref) MMPortQmi *mm_port_qmi_new (const gchar *name, MMPortSubsys subsys); #if defined WITH_QRTR MMPortQmi *mm_port_qmi_new_from_node (const gchar *name, QrtrNode *node); #endif void mm_port_qmi_open (MMPortQmi *self, gboolean set_data_format, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); gboolean mm_port_qmi_open_finish (MMPortQmi *self, GAsyncResult *res, GError **error); gboolean mm_port_qmi_is_open (MMPortQmi *self); void mm_port_qmi_close (MMPortQmi *self, GAsyncReadyCallback callback, gpointer user_data); gboolean mm_port_qmi_close_finish (MMPortQmi *self, GAsyncResult *res, GError **error); void mm_port_qmi_set_net_details (MMPortQmi *self, MMPort *first_net); typedef enum { MM_PORT_QMI_FLAG_DEFAULT = 0, MM_PORT_QMI_FLAG_WDS_IPV4 = 1, MM_PORT_QMI_FLAG_WDS_IPV6 = 2, } MMPortQmiFlag; void mm_port_qmi_allocate_client (MMPortQmi *self, QmiService service, guint flag, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); gboolean mm_port_qmi_allocate_client_finish (MMPortQmi *self, GAsyncResult *res, GError **error); void mm_port_qmi_release_client (MMPortQmi *self, QmiService service, MMPortQmiFlag flag); QmiClient *mm_port_qmi_peek_client (MMPortQmi *self, QmiService service, guint flag); QmiClient *mm_port_qmi_get_client (MMPortQmi *self, QmiService service, guint flag); typedef struct { QmiDataEndpointType type; guint interface_number; QmiSioPort sio_port; } MMQmiDataEndpoint; QmiDataEndpointType mm_port_qmi_get_endpoint_type (MMPortQmi *self); guint mm_port_qmi_get_endpoint_interface_number (MMPortQmi *self); void mm_port_qmi_get_endpoint_info (MMPortQmi *self, MMQmiDataEndpoint *out_endpoint); MMPortQmiKernelDataMode mm_port_qmi_get_kernel_data_modes (MMPortQmi *self); QmiWdaLinkLayerProtocol mm_port_qmi_get_link_layer_protocol (MMPortQmi *self); QmiWdaDataAggregationProtocol mm_port_qmi_get_data_aggregation_protocol (MMPortQmi *self); guint mm_port_qmi_get_max_multiplexed_links (MMPortQmi *self); typedef enum { MM_PORT_QMI_SETUP_DATA_FORMAT_ACTION_QUERY, MM_PORT_QMI_SETUP_DATA_FORMAT_ACTION_SET_DEFAULT, MM_PORT_QMI_SETUP_DATA_FORMAT_ACTION_SET_MULTIPLEX, } MMPortQmiSetupDataFormatAction; void mm_port_qmi_setup_data_format (MMPortQmi *self, MMPort *data, MMPortQmiSetupDataFormatAction action, GAsyncReadyCallback callback, gpointer user_data); gboolean mm_port_qmi_setup_data_format_finish (MMPortQmi *self, GAsyncResult *res, GError **error); void mm_port_qmi_setup_link (MMPortQmi *self, MMPort *data, const gchar *link_prefix_hint, GAsyncReadyCallback callback, gpointer user_data); gchar *mm_port_qmi_setup_link_finish (MMPortQmi *self, GAsyncResult *res, guint *mux_id, GError **error); void mm_port_qmi_cleanup_link (MMPortQmi *self, const gchar *link_name, guint mux_id, GAsyncReadyCallback callback, gpointer user_data); gboolean mm_port_qmi_cleanup_link_finish (MMPortQmi *self, GAsyncResult *res, GError **error); void mm_port_qmi_reset (MMPortQmi *self, MMPort *data, GAsyncReadyCallback callback, gpointer user_data); gboolean mm_port_qmi_reset_finish (MMPortQmi *self, GAsyncResult *res, GError **error); #endif /* MM_PORT_QMI_H */ ModemManager-1.23.4-dev/src/mm-port-serial-at.c000066400000000000000000000521621456466623000211400ustar00rootroot00000000000000/* -*- 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 #include #include #include #include "mm-port-serial-at.h" #include "mm-log-object.h" G_DEFINE_TYPE (MMPortSerialAt, mm_port_serial_at, MM_TYPE_PORT_SERIAL) enum { PROP_0, PROP_REMOVE_ECHO, PROP_INIT_SEQUENCE_ENABLED, PROP_INIT_SEQUENCE, PROP_SEND_LF, LAST_PROP }; struct _MMPortSerialAtPrivate { /* Response parser data */ MMPortSerialAtResponseParserFn response_parser_fn; gpointer response_parser_user_data; GDestroyNotify response_parser_notify; GSList *unsolicited_msg_handlers; MMPortSerialAtFlag flags; /* Properties */ gboolean remove_echo; guint init_sequence_enabled; gchar **init_sequence; gboolean send_lf; }; /*****************************************************************************/ gchar * mm_port_serial_at_quote_string (const char *string) { int len, i; gchar *quoted, *pos; if (string == NULL) len = 0; else len = strlen (string); quoted = g_malloc (3 + 3 * len); /* worst case */ pos = quoted; *pos++ = '"'; for (i = 0 ; i < len; i++) { if (string[i] < 0x20 || string[i] == '"' || string[i] == '\\') pos += sprintf (pos, "\\%02X", string[i]); else *pos++ = string[i]; } *pos++ = '"'; *pos++ = '\0'; return quoted; } void mm_port_serial_at_set_response_parser (MMPortSerialAt *self, MMPortSerialAtResponseParserFn fn, gpointer user_data, GDestroyNotify notify) { g_return_if_fail (MM_IS_PORT_SERIAL_AT (self)); if (self->priv->response_parser_notify) self->priv->response_parser_notify (self->priv->response_parser_user_data); self->priv->response_parser_fn = fn; self->priv->response_parser_user_data = user_data; self->priv->response_parser_notify = notify; } void mm_port_serial_at_remove_echo (GByteArray *response) { guint i; if (response->len <= 2) return; for (i = 0; i < (response->len - 1); i++) { /* If there is any content before the first * , assume it's echo or garbage, and skip it */ if (response->data[i] == '\r' && response->data[i + 1] == '\n') { if (i > 0) g_byte_array_remove_range (response, 0, i); /* else, good, we're already started with */ break; } } } static MMPortSerialResponseType parse_response (MMPortSerial *port, GByteArray *response, GByteArray **parsed_response, GError **error) { MMPortSerialAt *self = MM_PORT_SERIAL_AT (port); GString *string; gsize parsed_len; GError *inner_error = NULL; g_return_val_if_fail (self->priv->response_parser_fn != NULL, FALSE); /* Remove echo */ if (self->priv->remove_echo) mm_port_serial_at_remove_echo (response); /* If there's no response to receive, we're done; e.g. if we only got * unsolicited messages */ if (!response->len) return MM_PORT_SERIAL_RESPONSE_NONE; /* Construct the string that AT-parsing functions expect */ string = g_string_sized_new (response->len + 1); g_string_append_len (string, (const char *) response->data, response->len); /* Fully cleanup the response array, we'll consider the contents we got * as the full reply that the command may expect. */ g_byte_array_remove_range (response, 0, response->len); /* Parse it; returns FALSE if there is nothing we can do with this * response yet. */ if (!self->priv->response_parser_fn (self->priv->response_parser_user_data, string, self, &inner_error)) { /* Copy what we got back in the response buffer. */ g_byte_array_append (response, (const guint8 *) string->str, string->len); g_string_free (string, TRUE); return MM_PORT_SERIAL_RESPONSE_NONE; } /* If we got an error, propagate it without any further response string */ if (inner_error) { g_string_free (string, TRUE); g_propagate_error (error, inner_error); return MM_PORT_SERIAL_RESPONSE_ERROR; } /* Otherwise, build a new GByteArray considered as parsed response */ parsed_len = string->len; *parsed_response = g_byte_array_new_take ((guint8 *) g_string_free (string, FALSE), parsed_len); return MM_PORT_SERIAL_RESPONSE_BUFFER; } /*****************************************************************************/ typedef struct { GRegex *regex; MMPortSerialAtUnsolicitedMsgFn callback; gboolean enable; gpointer user_data; GDestroyNotify notify; } MMAtUnsolicitedMsgHandler; static gint unsolicited_msg_handler_cmp (MMAtUnsolicitedMsgHandler *handler, GRegex *regex) { return g_strcmp0 (g_regex_get_pattern (handler->regex), g_regex_get_pattern (regex)); } void mm_port_serial_at_add_unsolicited_msg_handler (MMPortSerialAt *self, GRegex *regex, MMPortSerialAtUnsolicitedMsgFn callback, gpointer user_data, GDestroyNotify notify) { GSList *existing; MMAtUnsolicitedMsgHandler *handler; g_return_if_fail (MM_IS_PORT_SERIAL_AT (self)); g_return_if_fail (regex != NULL); existing = g_slist_find_custom (self->priv->unsolicited_msg_handlers, regex, (GCompareFunc)unsolicited_msg_handler_cmp); if (existing) { handler = existing->data; /* We OVERWRITE any existing one, so if any context data existing, free it */ if (handler->notify) handler->notify (handler->user_data); } else { /* The new handler is always PREPENDED, so that e.g. plugins can provide * more specific matches for URCs that are also handled by the generic * plugin. */ handler = g_slice_new (MMAtUnsolicitedMsgHandler); handler->regex = g_regex_ref (regex); self->priv->unsolicited_msg_handlers = g_slist_prepend (self->priv->unsolicited_msg_handlers, handler); } handler->callback = callback; handler->enable = TRUE; handler->user_data = user_data; handler->notify = notify; } void mm_port_serial_at_enable_unsolicited_msg_handler (MMPortSerialAt *self, GRegex *regex, gboolean enable) { GSList *existing; MMAtUnsolicitedMsgHandler *handler; g_return_if_fail (MM_IS_PORT_SERIAL_AT (self)); g_return_if_fail (regex != NULL); existing = g_slist_find_custom (self->priv->unsolicited_msg_handlers, regex, (GCompareFunc)unsolicited_msg_handler_cmp); if (existing) { handler = existing->data; handler->enable = enable; } } 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 (MMPortSerial *port, GByteArray *response) { MMPortSerialAt *self = MM_PORT_SERIAL_AT (port); GSList *iter; /* Remove echo */ if (self->priv->remove_echo) mm_port_serial_at_remove_echo (response); for (iter = self->priv->unsolicited_msg_handlers; iter; iter = iter->next) { MMAtUnsolicitedMsgHandler *handler = (MMAtUnsolicitedMsgHandler *) iter->data; g_autoptr(GMatchInfo) match_info = NULL; gboolean matches; if (!handler->enable) continue; matches = g_regex_match_full (handler->regex, (const char *) response->data, 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); } } if (matches) { /* Remove matches */ g_autofree gchar *str = NULL; gint result_len = response->len; str = g_regex_replace_eval (handler->regex, (const char *) response->data, response->len, 0, 0, remove_eval_cb, &result_len, NULL); g_byte_array_remove_range (response, 0, response->len); g_byte_array_append (response, (const guint8 *) str, result_len); } } } /*****************************************************************************/ static GByteArray * at_command_to_byte_array (const char *command, gboolean is_raw, gboolean send_lf) { GByteArray *buf; int cmdlen; g_return_val_if_fail (command != NULL, NULL); cmdlen = strlen (command); buf = g_byte_array_sized_new (cmdlen + 4); if (!is_raw) { /* Make sure there's an AT in the front */ if (!g_str_has_prefix (command, "AT")) g_byte_array_append (buf, (const guint8 *) "AT", 2); } g_byte_array_append (buf, (const guint8 *) command, cmdlen); if (!is_raw) { /* Make sure there's a trailing carriage return */ if ((cmdlen == 0) || (command[cmdlen - 1] != '\r' && (cmdlen == 1 || command[cmdlen - 2] != '\r'))) g_byte_array_append (buf, (const guint8 *) "\r", 1); if (send_lf) { /* Make sure there's a trailing line-feed */ if ((cmdlen == 0) || (command[cmdlen - 1] != '\n' && (cmdlen == 1 || command[cmdlen - 2] != '\n'))) g_byte_array_append (buf, (const guint8 *) "\n", 1); } } return buf; } gchar * mm_port_serial_at_command_finish (MMPortSerialAt *self, GAsyncResult *res, GError **error) { return g_task_propagate_pointer (G_TASK (res), error); } static void serial_command_ready (MMPortSerial *port, GAsyncResult *res, GTask *task) { GByteArray *response_buffer; GError *error = NULL; GString *response; gchar *str; response_buffer = mm_port_serial_command_finish (port, res, &error); if (!response_buffer) { g_task_return_error (task, error); g_object_unref (task); return; } /* Build a GString just with the response we need, and clear the * processed range from the response buffer */ response = g_string_new_len ((const gchar *)response_buffer->data, response_buffer->len); if (response_buffer->len > 0) g_byte_array_remove_range (response_buffer, 0, response_buffer->len); g_byte_array_unref (response_buffer); str = g_string_free (response, FALSE); g_task_return_pointer (task, str, g_free); g_object_unref (task); } void mm_port_serial_at_command (MMPortSerialAt *self, const char *command, guint32 timeout_seconds, gboolean is_raw, gboolean allow_cached, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { GByteArray *buf; GTask *task; g_return_if_fail (self != NULL); g_return_if_fail (MM_IS_PORT_SERIAL_AT (self)); g_return_if_fail (command != NULL); buf = at_command_to_byte_array (command, is_raw, (mm_port_get_subsys (MM_PORT (self)) == MM_PORT_SUBSYS_TTY ? self->priv->send_lf : TRUE)); g_return_if_fail (buf != NULL); task = g_task_new (self, NULL, callback, user_data); mm_port_serial_command (MM_PORT_SERIAL (self), buf, timeout_seconds, allow_cached, is_raw, /* raw commands always run next, never queued last */ cancellable, (GAsyncReadyCallback)serial_command_ready, task); g_byte_array_unref (buf); } static void debug_log (MMPortSerial *self, const gchar *prefix, const gchar *buf, gsize len) { static GString *debug = NULL; const char *s; 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, ""); else if (*s == '\n') g_string_append (debug, ""); else g_string_append_printf (debug, "\\%u", (guint8) (*s & 0xFF)); s++; } g_string_append_c (debug, '\''); mm_obj_dbg (self, "%s", debug->str); g_string_truncate (debug, 0); } void mm_port_serial_at_set_flags (MMPortSerialAt *self, MMPortSerialAtFlag flags) { g_return_if_fail (self != NULL); g_return_if_fail (MM_IS_PORT_SERIAL_AT (self)); /* MM_PORT_SERIAL_AT_FLAG_NONE_NO_GENERIC is not expected */ g_return_if_fail (flags <= (MM_PORT_SERIAL_AT_FLAG_PRIMARY | MM_PORT_SERIAL_AT_FLAG_SECONDARY | MM_PORT_SERIAL_AT_FLAG_PPP | MM_PORT_SERIAL_AT_FLAG_GPS_CONTROL)); self->priv->flags = flags; } MMPortSerialAtFlag mm_port_serial_at_get_flags (MMPortSerialAt *self) { g_return_val_if_fail (self != NULL, MM_PORT_SERIAL_AT_FLAG_NONE); g_return_val_if_fail (MM_IS_PORT_SERIAL_AT (self), MM_PORT_SERIAL_AT_FLAG_NONE); return self->priv->flags; } /*****************************************************************************/ void mm_port_serial_at_run_init_sequence (MMPortSerialAt *self) { guint i; if (!self->priv->init_sequence) return; mm_obj_dbg (self, "running init sequence..."); /* Just queue the init commands, don't wait for reply */ for (i = 0; self->priv->init_sequence[i]; i++) { mm_port_serial_at_command (self, self->priv->init_sequence[i], 3, FALSE, FALSE, NULL, NULL, NULL); } } static void config (MMPortSerial *_self) { MMPortSerialAt *self = MM_PORT_SERIAL_AT (_self); if (self->priv->init_sequence_enabled) mm_port_serial_at_run_init_sequence (self); } /*****************************************************************************/ MMPortSerialAt * mm_port_serial_at_new (const char *name, MMPortSubsys subsys) { return MM_PORT_SERIAL_AT (g_object_new (MM_TYPE_PORT_SERIAL_AT, MM_PORT_DEVICE, name, MM_PORT_SUBSYS, subsys, MM_PORT_TYPE, MM_PORT_TYPE_AT, NULL)); } static void mm_port_serial_at_init (MMPortSerialAt *self) { self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, MM_TYPE_PORT_SERIAL_AT, MMPortSerialAtPrivate); /* By default, remove echo */ self->priv->remove_echo = TRUE; /* By default, run init sequence during first port opening */ self->priv->init_sequence_enabled = TRUE; /* By default, don't send line feed */ self->priv->send_lf = FALSE; } static void set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { MMPortSerialAt *self = MM_PORT_SERIAL_AT (object); switch (prop_id) { case PROP_REMOVE_ECHO: self->priv->remove_echo = g_value_get_boolean (value); break; case PROP_INIT_SEQUENCE_ENABLED: self->priv->init_sequence_enabled = g_value_get_boolean (value); break; case PROP_INIT_SEQUENCE: g_strfreev (self->priv->init_sequence); self->priv->init_sequence = g_value_dup_boxed (value); break; case PROP_SEND_LF: self->priv->send_lf = 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) { MMPortSerialAt *self = MM_PORT_SERIAL_AT (object); switch (prop_id) { case PROP_REMOVE_ECHO: g_value_set_boolean (value, self->priv->remove_echo); break; case PROP_INIT_SEQUENCE_ENABLED: g_value_set_boolean (value, self->priv->init_sequence_enabled); break; case PROP_INIT_SEQUENCE: g_value_set_boxed (value, self->priv->init_sequence); break; case PROP_SEND_LF: g_value_set_boolean (value, self->priv->send_lf); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void finalize (GObject *object) { MMPortSerialAt *self = MM_PORT_SERIAL_AT (object); while (self->priv->unsolicited_msg_handlers) { MMAtUnsolicitedMsgHandler *handler = (MMAtUnsolicitedMsgHandler *) self->priv->unsolicited_msg_handlers->data; if (handler->notify) handler->notify (handler->user_data); g_regex_unref (handler->regex); g_slice_free (MMAtUnsolicitedMsgHandler, handler); self->priv->unsolicited_msg_handlers = g_slist_delete_link (self->priv->unsolicited_msg_handlers, self->priv->unsolicited_msg_handlers); } if (self->priv->response_parser_notify) self->priv->response_parser_notify (self->priv->response_parser_user_data); g_strfreev (self->priv->init_sequence); G_OBJECT_CLASS (mm_port_serial_at_parent_class)->finalize (object); } static void mm_port_serial_at_class_init (MMPortSerialAtClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); MMPortSerialClass *serial_class = MM_PORT_SERIAL_CLASS (klass); g_type_class_add_private (object_class, sizeof (MMPortSerialAtPrivate)); /* Virtual methods */ object_class->set_property = set_property; object_class->get_property = get_property; object_class->finalize = finalize; serial_class->parse_unsolicited = parse_unsolicited; serial_class->parse_response = parse_response; serial_class->debug_log = debug_log; serial_class->config = config; g_object_class_install_property (object_class, PROP_REMOVE_ECHO, g_param_spec_boolean (MM_PORT_SERIAL_AT_REMOVE_ECHO, "Remove echo", "Built-in echo removal should be applied", TRUE, G_PARAM_READWRITE)); g_object_class_install_property (object_class, PROP_INIT_SEQUENCE_ENABLED, g_param_spec_boolean (MM_PORT_SERIAL_AT_INIT_SEQUENCE_ENABLED, "Init sequence enabled", "Whether the initialization sequence should be run", TRUE, G_PARAM_READWRITE)); g_object_class_install_property (object_class, PROP_INIT_SEQUENCE, g_param_spec_boxed (MM_PORT_SERIAL_AT_INIT_SEQUENCE, "Init sequence", "Initialization sequence", G_TYPE_STRV, G_PARAM_READWRITE)); g_object_class_install_property (object_class, PROP_SEND_LF, g_param_spec_boolean (MM_PORT_SERIAL_AT_SEND_LF, "Send LF", "Send line-feed at the end of each AT command sent", FALSE, G_PARAM_READWRITE)); } ModemManager-1.23.4-dev/src/mm-port-serial-at.h000066400000000000000000000141641456466623000211450ustar00rootroot00000000000000/* -*- 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_PORT_SERIAL_AT_H #define MM_PORT_SERIAL_AT_H #include #include #include "mm-port-serial.h" #define MM_TYPE_PORT_SERIAL_AT (mm_port_serial_at_get_type ()) #define MM_PORT_SERIAL_AT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_PORT_SERIAL_AT, MMPortSerialAt)) #define MM_PORT_SERIAL_AT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_PORT_SERIAL_AT, MMPortSerialAtClass)) #define MM_IS_PORT_SERIAL_AT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_PORT_SERIAL_AT)) #define MM_IS_PORT_SERIAL_AT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_PORT_SERIAL_AT)) #define MM_PORT_SERIAL_AT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_PORT_SERIAL_AT, MMPortSerialAtClass)) typedef struct _MMPortSerialAt MMPortSerialAt; typedef struct _MMPortSerialAtClass MMPortSerialAtClass; typedef struct _MMPortSerialAtPrivate MMPortSerialAtPrivate; /* AT port flags; for example consider a device with two AT ports (ACM0 and ACM1) * which could have the following layouts: * * ACM0(PRIMARY | PPP), ACM1(SECONDARY): port 0 is used for command and status * and for PPP data; while connected port 1 is used for command and status * ACM0(PPP), ACM1(PRIMARY): port 1 is always used for command and status, and * only when connecting is port 0 opened for dialing (ATD) and PPP */ typedef enum { /*< underscore_name=mm_port_serial_at_flag >*/ MM_PORT_SERIAL_AT_FLAG_NONE = 0, /* This port is preferred for command and status */ MM_PORT_SERIAL_AT_FLAG_PRIMARY = 1 << 0, /* Use port for command and status if the primary port is connected */ MM_PORT_SERIAL_AT_FLAG_SECONDARY = 1 << 1, /* This port should be used for PPP */ MM_PORT_SERIAL_AT_FLAG_PPP = 1 << 2, /* This port should be used for GPS control */ MM_PORT_SERIAL_AT_FLAG_GPS_CONTROL = 1 << 3, /* Helper flag to allow plugins specify that generic tags shouldn't be * applied */ MM_PORT_SERIAL_AT_FLAG_NONE_NO_GENERIC = 1 << 4, } MMPortSerialAtFlag; typedef gboolean (*MMPortSerialAtResponseParserFn) (gpointer user_data, GString *response, gpointer log_object, GError **error); typedef void (*MMPortSerialAtUnsolicitedMsgFn) (MMPortSerialAt *port, GMatchInfo *match_info, gpointer user_data); #define MM_PORT_SERIAL_AT_REMOVE_ECHO "remove-echo" #define MM_PORT_SERIAL_AT_INIT_SEQUENCE_ENABLED "init-sequence-enabled" #define MM_PORT_SERIAL_AT_INIT_SEQUENCE "init-sequence" #define MM_PORT_SERIAL_AT_SEND_LF "send-lf" struct _MMPortSerialAt { MMPortSerial parent; MMPortSerialAtPrivate *priv; }; struct _MMPortSerialAtClass { MMPortSerialClass parent; }; GType mm_port_serial_at_get_type (void); G_DEFINE_AUTOPTR_CLEANUP_FUNC (MMPortSerialAt, g_object_unref) MMPortSerialAt *mm_port_serial_at_new (const char *name, MMPortSubsys subsys); void mm_port_serial_at_add_unsolicited_msg_handler (MMPortSerialAt *self, GRegex *regex, MMPortSerialAtUnsolicitedMsgFn callback, gpointer user_data, GDestroyNotify notify); void mm_port_serial_at_enable_unsolicited_msg_handler (MMPortSerialAt *self, GRegex *regex, gboolean enable); void mm_port_serial_at_set_response_parser (MMPortSerialAt *self, MMPortSerialAtResponseParserFn fn, gpointer user_data, GDestroyNotify notify); void mm_port_serial_at_command (MMPortSerialAt *self, const char *command, guint32 timeout_seconds, gboolean is_raw, gboolean allow_cached, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); gchar *mm_port_serial_at_command_finish (MMPortSerialAt *self, GAsyncResult *res, GError **error); /* * Convert a string into a quoted and escaped string. Returns a new * allocated string. Follows ITU V.250 5.4.2.2 "String constants". */ gchar *mm_port_serial_at_quote_string (const char *string); /* Just for unit tests */ void mm_port_serial_at_remove_echo (GByteArray *response); void mm_port_serial_at_set_flags (MMPortSerialAt *self, MMPortSerialAtFlag flags); MMPortSerialAtFlag mm_port_serial_at_get_flags (MMPortSerialAt *self); /* Tell the port to run its init sequence, if any, right away */ void mm_port_serial_at_run_init_sequence (MMPortSerialAt *self); #endif /* MM_PORT_SERIAL_AT_H */ ModemManager-1.23.4-dev/src/mm-port-serial-gps.c000066400000000000000000000151701456466623000213230ustar00rootroot00000000000000/* -*- 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) 2012 Aleksander Morgado */ #include #include #include #include #include "mm-port-serial-gps.h" #include "mm-log-object.h" G_DEFINE_TYPE (MMPortSerialGps, mm_port_serial_gps, MM_TYPE_PORT_SERIAL) struct _MMPortSerialGpsPrivate { /* Trace handler data */ MMPortSerialGpsTraceFn callback; gpointer user_data; GDestroyNotify notify; /* Regex for all known traces */ GRegex *known_traces_regex; }; /*****************************************************************************/ void mm_port_serial_gps_add_trace_handler (MMPortSerialGps *self, MMPortSerialGpsTraceFn callback, gpointer user_data, GDestroyNotify notify) { g_return_if_fail (MM_IS_PORT_SERIAL_GPS (self)); if (self->priv->notify) self->priv->notify (self->priv->user_data); self->priv->callback = callback; self->priv->user_data = user_data; self->priv->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 MMPortSerialResponseType parse_response (MMPortSerial *port, GByteArray *response, GByteArray **parsed_response, GError **error) { MMPortSerialGps *self = MM_PORT_SERIAL_GPS (port); g_autoptr(GMatchInfo) match_info = NULL; gboolean matches; gchar *str; gint result_len; guint i; for (i = 0; i < response->len; i++) { /* If there is any content before the first $, * assume it's garbage, and skip it */ if (response->data[i] == '$') { if (i > 0) g_byte_array_remove_range (response, 0, i); /* else, good, we're already started with $ */ break; } } matches = g_regex_match_full (self->priv->known_traces_regex, (const gchar *) response->data, response->len, 0, 0, &match_info, NULL); if (self->priv->callback) { while (g_match_info_matches (match_info)) { gchar *trace; trace = g_match_info_fetch (match_info, 0); if (trace) { self->priv->callback (self, trace, self->priv->user_data); g_free (trace); } g_match_info_next (match_info, NULL); } } if (!matches) return MM_PORT_SERIAL_RESPONSE_NONE; /* Remove matches */ result_len = response->len; str = g_regex_replace_eval (self->priv->known_traces_regex, (const char *) response->data, response->len, 0, 0, remove_eval_cb, &result_len, NULL); /* Cleanup response buffer */ g_byte_array_remove_range (response, 0, response->len); /* Build parsed response */ *parsed_response = g_byte_array_new_take ((guint8 *)str, result_len); return TRUE; } /*****************************************************************************/ static void debug_log (MMPortSerial *self, const gchar *prefix, const gchar *buf, gsize len) { static GString *debug = NULL; const gchar *s; 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, ""); else if (*s == '\n') g_string_append (debug, ""); else g_string_append_printf (debug, "\\%u", (guint8) (*s & 0xFF)); s++; } g_string_append_c (debug, '\''); mm_obj_dbg (self, "%s", debug->str); g_string_truncate (debug, 0); } /*****************************************************************************/ MMPortSerialGps * mm_port_serial_gps_new (const char *name) { return MM_PORT_SERIAL_GPS (g_object_new (MM_TYPE_PORT_SERIAL_GPS, MM_PORT_DEVICE, name, MM_PORT_SUBSYS, MM_PORT_SUBSYS_TTY, MM_PORT_TYPE, MM_PORT_TYPE_GPS, NULL)); } static void mm_port_serial_gps_init (MMPortSerialGps *self) { self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, MM_TYPE_PORT_SERIAL_GPS, MMPortSerialGpsPrivate); /* We'll assume that all traces start with the dollar sign and end with \r\n */ self->priv->known_traces_regex = g_regex_new ("\\$.*\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); } static void finalize (GObject *object) { MMPortSerialGps *self = MM_PORT_SERIAL_GPS (object); if (self->priv->notify) self->priv->notify (self->priv->user_data); g_regex_unref (self->priv->known_traces_regex); G_OBJECT_CLASS (mm_port_serial_gps_parent_class)->finalize (object); } static void mm_port_serial_gps_class_init (MMPortSerialGpsClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); MMPortSerialClass *serial_class = MM_PORT_SERIAL_CLASS (klass); g_type_class_add_private (object_class, sizeof (MMPortSerialGpsPrivate)); /* Virtual methods */ object_class->finalize = finalize; serial_class->parse_response = parse_response; serial_class->debug_log = debug_log; } ModemManager-1.23.4-dev/src/mm-port-serial-gps.h000066400000000000000000000046031456466623000213270ustar00rootroot00000000000000/* -*- 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) 2012 - Aleksander Morgado */ #ifndef MM_PORT_SERIAL_GPS_H #define MM_PORT_SERIAL_GPS_H #include #include #include "mm-port-serial.h" #define MM_TYPE_PORT_SERIAL_GPS (mm_port_serial_gps_get_type ()) #define MM_PORT_SERIAL_GPS(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_PORT_SERIAL_GPS, MMPortSerialGps)) #define MM_PORT_SERIAL_GPS_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_PORT_SERIAL_GPS, MMPortSerialGpsClass)) #define MM_IS_PORT_SERIAL_GPS(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_PORT_SERIAL_GPS)) #define MM_IS_PORT_SERIAL_GPS_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_PORT_SERIAL_GPS)) #define MM_PORT_SERIAL_GPS_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_PORT_SERIAL_GPS, MMPortSerialGpsClass)) typedef struct _MMPortSerialGps MMPortSerialGps; typedef struct _MMPortSerialGpsClass MMPortSerialGpsClass; typedef struct _MMPortSerialGpsPrivate MMPortSerialGpsPrivate; typedef void (*MMPortSerialGpsTraceFn) (MMPortSerialGps *port, const gchar *trace, gpointer user_data); struct _MMPortSerialGps { MMPortSerial parent; MMPortSerialGpsPrivate *priv; }; struct _MMPortSerialGpsClass { MMPortSerialClass parent; }; GType mm_port_serial_gps_get_type (void); G_DEFINE_AUTOPTR_CLEANUP_FUNC (MMPortSerialGps, g_object_unref) MMPortSerialGps *mm_port_serial_gps_new (const char *name); void mm_port_serial_gps_add_trace_handler (MMPortSerialGps *self, MMPortSerialGpsTraceFn callback, gpointer user_data, GDestroyNotify notify); #endif /* MM_PORT_SERIAL_GPS_H */ ModemManager-1.23.4-dev/src/mm-port-serial-qcdm.c000066400000000000000000000342621456466623000214610ustar00rootroot00000000000000/* -*- 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 #include #include #include #include #include #include "mm-port-serial-qcdm.h" #include "libqcdm/src/com.h" #include "libqcdm/src/utils.h" #include "libqcdm/src/errors.h" #include "libqcdm/src/dm-commands.h" #include "mm-log-object.h" G_DEFINE_TYPE (MMPortSerialQcdm, mm_port_serial_qcdm, MM_TYPE_PORT_SERIAL) struct _MMPortSerialQcdmPrivate { GSList *unsolicited_msg_handlers; }; /*****************************************************************************/ static gboolean find_qcdm_start (GByteArray *response, gsize *start) { guint i; gint last = -1; /* Look for 3 bytes and a QCDM frame marker, ie enough data for a valid * frame. There will usually be three cases here; (1) a QCDM frame * starting with data and terminated by 0x7E, and (2) a QCDM frame starting * with 0x7E and ending with 0x7E, and (3) a non-QCDM frame that still * uses HDLC framing (like Sierra CnS) that starts and ends with 0x7E. */ for (i = 0; i < response->len; i++) { /* Marker found */ if (response->data[i] == 0x7E) { /* If we didn't get an initial marker, count at least 3 bytes since * origin; if we did get an initial marker, count at least 3 bytes * since the marker. */ if (((last == -1) && (i >= 3)) || ((last >= 0) && (i > (guint)(last + 3)))) { /* Got a full QCDM frame; 3 non-0x7E bytes and a terminator */ if (start) *start = last + 1; return TRUE; } /* Save position of the last QCDM frame marker */ last = i; } } return FALSE; } static MMPortSerialResponseType parse_qcdm (GByteArray *response, gboolean want_log, GByteArray **parsed_response, GError **error) { gsize start = 0; gsize used = 0; gsize unescaped_len = 0; guint8 *unescaped_buffer; qcdmbool more = FALSE; /* Get the offset into the buffer of where the QCDM frame starts */ if (!find_qcdm_start (response, &start)) { /* Discard the unparsable data right away, we do need a QCDM * start, and anything that comes before it is unknown data * that we'll never use. */ return MM_PORT_SERIAL_RESPONSE_NONE; } /* If there is anything before the start marker, remove it */ g_byte_array_remove_range (response, 0, start); if (response->len == 0) return MM_PORT_SERIAL_RESPONSE_NONE; /* Try to decapsulate the response into a buffer */ unescaped_buffer = g_malloc (1024); if (!dm_decapsulate_buffer ((const char *)(response->data), response->len, (char *)unescaped_buffer, 1024, &unescaped_len, &used, &more)) { /* Report an error right away. Not being able to decapsulate a QCDM * packet once we got message start marker likely means that this * data that we got is not a QCDM message. */ g_set_error (error, MM_SERIAL_ERROR, MM_SERIAL_ERROR_PARSE_FAILED, "Failed to unescape QCDM packet"); g_free (unescaped_buffer); return MM_PORT_SERIAL_RESPONSE_ERROR; } if (more) { /* Need more data, we leave the original byte array untouched so that * we can retry later when more data arrives. */ g_free (unescaped_buffer); return MM_PORT_SERIAL_RESPONSE_NONE; } if (want_log && unescaped_buffer[0] != DIAG_CMD_LOG) { /* If we only want log items and this isn't one, don't remove this * DM packet from the buffer. */ g_free (unescaped_buffer); return MM_PORT_SERIAL_RESPONSE_NONE; } /* Successfully decapsulated the DM command. We'll build a new byte array * with the response, and leave the input buffer cleaned up. */ g_assert (unescaped_len <= 1024); unescaped_buffer = g_realloc (unescaped_buffer, unescaped_len); *parsed_response = g_byte_array_new_take (unescaped_buffer, unescaped_len); /* Remove the data we used from the input buffer, leaving out any * additional data that may already been received (e.g. from the following * message). */ g_byte_array_remove_range (response, 0, used); return MM_PORT_SERIAL_RESPONSE_BUFFER; } static MMPortSerialResponseType parse_response (MMPortSerial *port, GByteArray *response, GByteArray **parsed_response, GError **error) { return parse_qcdm (response, FALSE, parsed_response, error); } /*****************************************************************************/ GByteArray * mm_port_serial_qcdm_command_finish (MMPortSerialQcdm *self, GAsyncResult *res, GError **error) { return g_task_propagate_pointer (G_TASK (res), error); } static void serial_command_ready (MMPortSerial *port, GAsyncResult *res, GTask *task) { GByteArray *response; GError *error = NULL; response = mm_port_serial_command_finish (port, res, &error); if (!response) g_task_return_error (task, error); else g_task_return_pointer (task, response, (GDestroyNotify)g_byte_array_unref); g_object_unref (task); } void mm_port_serial_qcdm_command (MMPortSerialQcdm *self, GByteArray *command, guint32 timeout_seconds, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; g_return_if_fail (MM_IS_PORT_SERIAL_QCDM (self)); g_return_if_fail (command != NULL); task = g_task_new (self, cancellable, callback, user_data); /* 'command' is expected to be already CRC-ed and escaped */ mm_port_serial_command (MM_PORT_SERIAL (self), command, timeout_seconds, FALSE, /* never cached */ FALSE, /* always queued last */ cancellable, (GAsyncReadyCallback)serial_command_ready, task); } static void debug_log (MMPortSerial *self, const gchar *prefix, const gchar *buf, gsize len) { static GString *debug = NULL; const gchar *s = buf; if (!debug) debug = g_string_sized_new (512); g_string_append (debug, prefix); while (len--) g_string_append_printf (debug, " %02x", (guint8) (*s++ & 0xFF)); mm_obj_dbg (self, "%s", debug->str); g_string_truncate (debug, 0); } /*****************************************************************************/ typedef struct { guint log_code; MMPortSerialQcdmUnsolicitedMsgFn callback; gboolean enable; gpointer user_data; GDestroyNotify notify; } MMQcdmUnsolicitedMsgHandler; static gint unsolicited_msg_handler_cmp (MMQcdmUnsolicitedMsgHandler *handler, gpointer log_code) { return handler->log_code - GPOINTER_TO_UINT (log_code); } void mm_port_serial_qcdm_add_unsolicited_msg_handler (MMPortSerialQcdm *self, guint log_code, MMPortSerialQcdmUnsolicitedMsgFn callback, gpointer user_data, GDestroyNotify notify) { GSList *existing; MMQcdmUnsolicitedMsgHandler *handler; g_return_if_fail (MM_IS_PORT_SERIAL_QCDM (self)); g_return_if_fail (log_code > 0 && log_code <= G_MAXUINT16); existing = g_slist_find_custom (self->priv->unsolicited_msg_handlers, GUINT_TO_POINTER (log_code), (GCompareFunc)unsolicited_msg_handler_cmp); if (existing) { handler = existing->data; /* We OVERWRITE any existing one, so if any context data existing, free it */ if (handler->notify) handler->notify (handler->user_data); } else { handler = g_slice_new (MMQcdmUnsolicitedMsgHandler); self->priv->unsolicited_msg_handlers = g_slist_append (self->priv->unsolicited_msg_handlers, handler); handler->log_code = log_code; } handler->callback = callback; handler->enable = TRUE; handler->user_data = user_data; handler->notify = notify; } void mm_port_serial_qcdm_enable_unsolicited_msg_handler (MMPortSerialQcdm *self, guint log_code, gboolean enable) { GSList *existing; MMQcdmUnsolicitedMsgHandler *handler; g_return_if_fail (MM_IS_PORT_SERIAL_QCDM (self)); g_return_if_fail (log_code > 0 && log_code <= G_MAXUINT16); existing = g_slist_find_custom (self->priv->unsolicited_msg_handlers, GUINT_TO_POINTER (log_code), (GCompareFunc)unsolicited_msg_handler_cmp); if (existing) { handler = existing->data; handler->enable = enable; } } static void parse_unsolicited (MMPortSerial *port, GByteArray *response) { MMPortSerialQcdm *self = MM_PORT_SERIAL_QCDM (port); GByteArray *log_buffer = NULL; GSList *iter; if (parse_qcdm (response, TRUE, &log_buffer, NULL) != MM_PORT_SERIAL_RESPONSE_BUFFER) { return; } /* These should be guaranteed by parse_qcdm() */ g_return_if_fail (log_buffer); g_return_if_fail (log_buffer->len > 0); g_return_if_fail (log_buffer->data[0] == DIAG_CMD_LOG); if (log_buffer->len < sizeof (DMCmdLog)) return; for (iter = self->priv->unsolicited_msg_handlers; iter; iter = iter->next) { MMQcdmUnsolicitedMsgHandler *handler = (MMQcdmUnsolicitedMsgHandler *) iter->data; DMCmdLog *log_cmd = (DMCmdLog *) log_buffer->data; if (!handler->enable) continue; if (handler->log_code != le16toh (log_cmd->log_code)) continue; if (handler->callback) handler->callback (self, log_buffer, handler->user_data); } } /*****************************************************************************/ static gboolean config_fd (MMPortSerial *port, int fd, GError **error) { int err; err = qcdm_port_setup (fd); if (err != QCDM_SUCCESS) { g_set_error (error, MM_SERIAL_ERROR, MM_SERIAL_ERROR_OPEN_FAILED, "Failed to open QCDM port: %d", err); return FALSE; } return TRUE; } /*****************************************************************************/ MMPortSerialQcdm * mm_port_serial_qcdm_new (const char *name, MMPortSubsys subsys) { return MM_PORT_SERIAL_QCDM (g_object_new (MM_TYPE_PORT_SERIAL_QCDM, MM_PORT_DEVICE, name, MM_PORT_SUBSYS, subsys, MM_PORT_TYPE, MM_PORT_TYPE_QCDM, MM_PORT_SERIAL_SEND_DELAY, (guint64) 0, NULL)); } MMPortSerialQcdm * mm_port_serial_qcdm_new_fd (int fd) { MMPortSerialQcdm *port; char *name; name = g_strdup_printf ("port%d", fd); port = MM_PORT_SERIAL_QCDM (g_object_new (MM_TYPE_PORT_SERIAL_QCDM, MM_PORT_DEVICE, name, MM_PORT_SUBSYS, MM_PORT_SUBSYS_TTY, MM_PORT_TYPE, MM_PORT_TYPE_QCDM, MM_PORT_SERIAL_FD, fd, MM_PORT_SERIAL_SEND_DELAY, (guint64) 0, NULL)); g_free (name); return port; } static void mm_port_serial_qcdm_init (MMPortSerialQcdm *self) { self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, MM_TYPE_PORT_SERIAL_QCDM, MMPortSerialQcdmPrivate); } static void finalize (GObject *object) { MMPortSerialQcdm *self = MM_PORT_SERIAL_QCDM (object); while (self->priv->unsolicited_msg_handlers) { MMQcdmUnsolicitedMsgHandler *handler = (MMQcdmUnsolicitedMsgHandler *) self->priv->unsolicited_msg_handlers->data; if (handler->notify) handler->notify (handler->user_data); g_slice_free (MMQcdmUnsolicitedMsgHandler, handler); self->priv->unsolicited_msg_handlers = g_slist_delete_link (self->priv->unsolicited_msg_handlers, self->priv->unsolicited_msg_handlers); } G_OBJECT_CLASS (mm_port_serial_qcdm_parent_class)->finalize (object); } static void mm_port_serial_qcdm_class_init (MMPortSerialQcdmClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); MMPortSerialClass *port_class = MM_PORT_SERIAL_CLASS (klass); g_type_class_add_private (object_class, sizeof (MMPortSerialQcdmPrivate)); /* Virtual methods */ object_class->finalize = finalize; port_class->parse_unsolicited = parse_unsolicited; port_class->parse_response = parse_response; port_class->config_fd = config_fd; port_class->debug_log = debug_log; } ModemManager-1.23.4-dev/src/mm-port-serial-qcdm.h000066400000000000000000000071331456466623000214630ustar00rootroot00000000000000/* -*- 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_PORT_SERIAL_QCDM_H #define MM_PORT_SERIAL_QCDM_H #include #include #include "mm-port-serial.h" #define MM_TYPE_PORT_SERIAL_QCDM (mm_port_serial_qcdm_get_type ()) #define MM_PORT_SERIAL_QCDM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_PORT_SERIAL_QCDM, MMPortSerialQcdm)) #define MM_PORT_SERIAL_QCDM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_PORT_SERIAL_QCDM, MMPortSerialQcdmClass)) #define MM_IS_PORT_SERIAL_QCDM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_PORT_SERIAL_QCDM)) #define MM_IS_PORT_SERIAL_QCDM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_PORT_SERIAL_QCDM)) #define MM_PORT_SERIAL_QCDM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_PORT_SERIAL_QCDM, MMPortSerialQcdmClass)) typedef struct _MMPortSerialQcdm MMPortSerialQcdm; typedef struct _MMPortSerialQcdmClass MMPortSerialQcdmClass; typedef struct _MMPortSerialQcdmPrivate MMPortSerialQcdmPrivate; struct _MMPortSerialQcdm { MMPortSerial parent; MMPortSerialQcdmPrivate *priv; }; struct _MMPortSerialQcdmClass { MMPortSerialClass parent; }; GType mm_port_serial_qcdm_get_type (void); G_DEFINE_AUTOPTR_CLEANUP_FUNC (MMPortSerialQcdm, g_object_unref) MMPortSerialQcdm *mm_port_serial_qcdm_new (const char *name, MMPortSubsys subsys); MMPortSerialQcdm *mm_port_serial_qcdm_new_fd (int fd); void mm_port_serial_qcdm_command (MMPortSerialQcdm *self, GByteArray *command, guint32 timeout_seconds, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); GByteArray *mm_port_serial_qcdm_command_finish (MMPortSerialQcdm *self, GAsyncResult *res, GError **error); typedef void (*MMPortSerialQcdmUnsolicitedMsgFn) (MMPortSerialQcdm *port, GByteArray *log_buffer, gpointer user_data); void mm_port_serial_qcdm_add_unsolicited_msg_handler (MMPortSerialQcdm *self, guint log_code, MMPortSerialQcdmUnsolicitedMsgFn callback, gpointer user_data, GDestroyNotify notify); void mm_port_serial_qcdm_enable_unsolicited_msg_handler (MMPortSerialQcdm *self, guint log_code, gboolean enable); #endif /* MM_PORT_SERIAL_QCDM_H */ ModemManager-1.23.4-dev/src/mm-port-serial.c000066400000000000000000002066201456466623000205360ustar00rootroot00000000000000/* -*- 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. */ #define _GNU_SOURCE /* for strcasestr() */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "mm-port-serial.h" #include "mm-log-object.h" #include "mm-helper-enums-types.h" static gboolean port_serial_queue_process (gpointer data); static void port_serial_schedule_queue_process (MMPortSerial *self, guint timeout_ms); static void port_serial_close_force (MMPortSerial *self); static void port_serial_reopen_cancel (MMPortSerial *self); static void port_serial_set_cached_reply (MMPortSerial *self, const GByteArray *command, const GByteArray *response); G_DEFINE_TYPE (MMPortSerial, mm_port_serial, MM_TYPE_PORT) enum { PROP_0, PROP_BAUD, PROP_BITS, PROP_PARITY, PROP_STOPBITS, PROP_FLOW_CONTROL, PROP_SEND_DELAY, PROP_FD, PROP_SPEW_CONTROL, PROP_FLASH_OK, LAST_PROP }; enum { BUFFER_FULL, FORCED_CLOSE, LAST_SIGNAL }; static guint signals[LAST_SIGNAL] = { 0 }; #define SERIAL_BUF_SIZE 2048 struct _MMPortSerialPrivate { guint32 open_count; gboolean forced_close; int fd; GHashTable *reply_cache; GQueue *queue; GByteArray *response; /* For real ports, iochannel, and we implement the eagain limit */ GIOChannel *iochannel; guint iochannel_id; /* For unix-socket based ports, socket */ GSocket *socket; GSource *socket_source; guint baud; guint bits; char parity; guint stopbits; MMFlowControl flow_control; guint64 send_delay; gboolean spew_control; gboolean flash_ok; guint queue_id; guint timeout_id; GCancellable *cancellable; gulong cancellable_id; guint n_consecutive_timeouts; guint connected_id; GTask *flash_task; GTask *reopen_task; }; /*****************************************************************************/ /* Command */ typedef struct { GByteArray *command; guint32 timeout; gboolean allow_cached; guint32 eagain_count; guint32 idx; gboolean started; gboolean done; } CommandContext; static void command_context_free (CommandContext *ctx) { g_byte_array_unref (ctx->command); g_slice_free (CommandContext, ctx); } GByteArray * mm_port_serial_command_finish (MMPortSerial *self, GAsyncResult *res, GError **error) { return g_task_propagate_pointer (G_TASK (res), error); } void mm_port_serial_command (MMPortSerial *self, GByteArray *command, guint32 timeout_seconds, gboolean allow_cached, gboolean run_next, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { CommandContext *ctx; GTask *task; g_return_if_fail (MM_IS_PORT_SERIAL (self)); g_return_if_fail (command != NULL); /* Setup command context */ ctx = g_slice_new0 (CommandContext); task = g_task_new (self, cancellable, callback, user_data); g_task_set_task_data (task, ctx, (GDestroyNotify)command_context_free); ctx->command = g_byte_array_ref (command); ctx->allow_cached = allow_cached; ctx->timeout = timeout_seconds; /* Only accept about 3 seconds of EAGAIN for this command */ if (self->priv->send_delay && mm_port_get_subsys (MM_PORT (self)) == MM_PORT_SUBSYS_TTY) ctx->eagain_count = 3000000 / self->priv->send_delay; else ctx->eagain_count = 1000; if (self->priv->open_count == 0) { g_task_return_new_error (task, MM_SERIAL_ERROR, MM_SERIAL_ERROR_SEND_FAILED, "Sending command failed: device is not open"); g_object_unref (task); return; } /* Clear the cached value for this command if not asking for cached value */ if (!allow_cached) port_serial_set_cached_reply (self, ctx->command, NULL); /* If requested to run next, push to the head of the queue so that it really is * the next one sent */ if (run_next) g_queue_push_head (self->priv->queue, task); else g_queue_push_tail (self->priv->queue, task); if (g_queue_get_length (self->priv->queue) == 1) port_serial_schedule_queue_process (self, 0); } /*****************************************************************************/ static gboolean parse_baudrate (guint baudrate_num, guint *out_baudrate_speed) { guint speed; switch (baudrate_num) { 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 230400: speed = B230400; break; case 460800: speed = B460800; break; case 921600: speed = B921600; break; default: return FALSE; } if (out_baudrate_speed) *out_baudrate_speed = speed; return TRUE; } static int parse_bits (guint i) { int bits = -1; switch (i) { case 5: bits = CS5; break; case 6: bits = CS6; break; case 7: bits = CS7; break; case 8: bits = CS8; break; default: g_assert_not_reached (); } return bits; } static int parse_parity (char c) { int parity = -1; 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_assert_not_reached (); } return parity; } static int parse_stopbits (guint i) { int stopbits = -1; switch (i) { case 1: stopbits = 0; break; case 2: stopbits = CSTOPB; break; default: g_assert_not_reached (); } return stopbits; } static gboolean internal_tcsetattr (MMPortSerial *self, gint fd, const struct termios *options, GError **error) { guint count; struct termios other; #define MAX_TCSETATTR_RETRIES 4 for (count = 0; count < MAX_TCSETATTR_RETRIES; count++) { /* try to set the new port attributes */ errno = 0; if (tcsetattr (fd, TCSANOW, options) == 0) break; /* hard error if not EAGAIN */ if (errno != EAGAIN) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "couldn't set serial port attributes: %s", g_strerror (errno)); return FALSE; } /* try a few times if EAGAIN */ g_usleep (100000); } /* too many retries? */ if (count == MAX_TCSETATTR_RETRIES) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "couldn't set serial port attributes: too many retries (%u)", count); return FALSE; } /* tcsetattr() returns 0 if any of the requested attributes could be set, * so we should double-check that all were set and log if not. Just with * debug level, as we're ignoring this issue all together anyway. */ memset (&other, 0, sizeof (struct termios)); errno = 0; if (tcgetattr (fd, &other) != 0) mm_obj_dbg (self, "couldn't get serial port attributes after setting them: %s", g_strerror (errno)); else if (memcmp (options, &other, sizeof (struct termios)) != 0) mm_obj_dbg (self, "port attributes not fully set"); #undef MAX_TCSETATTR_RETRIES return TRUE; } static gboolean set_flow_control_termios (MMPortSerial *self, MMFlowControl flow_control, struct termios *options) { gboolean had_xon_xoff; gboolean had_rts_cts; tcflag_t iflag_orig, cflag_orig; iflag_orig = options->c_iflag; cflag_orig = options->c_cflag; had_xon_xoff = !!(options->c_iflag & (IXON | IXOFF)); options->c_iflag &= ~(IXON | IXOFF | IXANY); had_rts_cts = !!(options->c_cflag & (CRTSCTS)); options->c_cflag &= ~(CRTSCTS); /* setup the requested flags */ switch (flow_control) { case MM_FLOW_CONTROL_XON_XOFF: mm_obj_dbg (self, "enabling XON/XOFF flow control"); options->c_iflag |= (IXON | IXOFF | IXANY); break; case MM_FLOW_CONTROL_RTS_CTS: mm_obj_dbg (self, "enabling RTS/CTS flow control"); options->c_cflag |= (CRTSCTS); break; case MM_FLOW_CONTROL_NONE: case MM_FLOW_CONTROL_UNKNOWN: if (had_xon_xoff) mm_obj_dbg (self, "disabling XON/XOFF flow control"); if (had_rts_cts) mm_obj_dbg (self, "disabling RTS/CTS flow control"); break; default: g_assert_not_reached (); } return iflag_orig != options->c_iflag || cflag_orig != options->c_cflag; } static gboolean real_config_fd (MMPortSerial *self, int fd, GError **error) { struct termios stbuf; guint speed; gint bits; gint parity; gint stopbits; /* No setup if not a tty */ if (mm_port_get_subsys (MM_PORT (self)) != MM_PORT_SUBSYS_TTY) return TRUE; mm_obj_dbg (self, "setting up baudrate: %u", self->priv->baud); if (!parse_baudrate (self->priv->baud, &speed) || speed == B0) { mm_obj_warn (self, "baudrate invalid: %u; defaulting to 57600", self->priv->baud); speed = B57600; } bits = parse_bits (self->priv->bits); parity = parse_parity (self->priv->parity); stopbits = parse_stopbits (self->priv->stopbits); memset (&stbuf, 0, sizeof (struct termios)); if (tcgetattr (fd, &stbuf) != 0) mm_obj_warn (self, "error getting serial port attributes: %s", g_strerror (errno)); stbuf.c_cflag &= ~(CBAUD | CSIZE | CSTOPB | PARENB | PARODD | CRTSCTS); stbuf.c_iflag &= ~(IGNCR | ICRNL | IUCLC | INPCK | IXON | IXOFF | IXANY ); stbuf.c_oflag &= ~(OPOST | OLCUC | OCRNL | ONLCR | ONLRET); stbuf.c_lflag &= ~(ICANON | ECHO | ECHOE | ECHONL); stbuf.c_cc[VMIN] = 1; stbuf.c_cc[VTIME] = 0; stbuf.c_cc[VEOF] = 1; /* Ignore parity/framing errors */ stbuf.c_iflag |= IGNPAR; /* Set up port speed and serial attributes and enable receiver in local mode */ stbuf.c_cflag |= (bits | parity | stopbits | CLOCAL | CREAD); errno = 0; if (cfsetispeed (&stbuf, speed) != 0) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "%s: failed to set serial port input speed; errno %d", __func__, errno); return FALSE; } errno = 0; if (cfsetospeed (&stbuf, speed) != 0) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "%s: failed to set serial port output speed; errno %d", __func__, errno); return FALSE; } if (self->priv->flow_control != MM_FLOW_CONTROL_UNKNOWN) { gchar *str; str = mm_flow_control_build_string_from_mask (self->priv->flow_control); mm_obj_dbg (self, "flow control explicitly requested for device is: %s", str ? str : "unknown"); g_free (str); } else mm_obj_dbg (self, "no flow control explicitly requested for device"); set_flow_control_termios (self, self->priv->flow_control, &stbuf); return internal_tcsetattr (self, fd, &stbuf, error); } static void serial_debug (MMPortSerial *self, const gchar *prefix, const gchar *buf, gsize len) { g_return_if_fail (len > 0); if (MM_PORT_SERIAL_GET_CLASS (self)->debug_log) MM_PORT_SERIAL_GET_CLASS (self)->debug_log (self, prefix, buf, len); } static gboolean port_serial_process_command (MMPortSerial *self, CommandContext *ctx, GError **error) { const gchar *p; gsize written; gssize send_len; if (self->priv->iochannel == NULL && self->priv->socket == NULL) { g_set_error_literal (error, MM_SERIAL_ERROR, MM_SERIAL_ERROR_SEND_FAILED, "Sending command failed: device is not enabled"); return FALSE; } if (mm_port_get_connected (MM_PORT (self))) { g_set_error_literal (error, MM_SERIAL_ERROR, MM_SERIAL_ERROR_SEND_FAILED, "Sending command failed: device is connected"); return FALSE; } /* Only print command the first time */ if (ctx->started == FALSE) { ctx->started = TRUE; serial_debug (self, "-->", (const gchar *) ctx->command->data, ctx->command->len); } if (self->priv->send_delay == 0 || mm_port_get_subsys (MM_PORT (self)) != MM_PORT_SUBSYS_TTY) { /* Send the whole command in one write */ send_len = (gssize)ctx->command->len; p = (gchar *)ctx->command->data; } else { /* Send just one byte of the command */ send_len = 1; p = (gchar *)&ctx->command->data[ctx->idx]; } /* GIOChannel based setup */ if (self->priv->iochannel) { GIOStatus write_status; /* Send N bytes of the command */ write_status = g_io_channel_write_chars (self->priv->iochannel, p, send_len, &written, error); switch (write_status) { case G_IO_STATUS_ERROR: g_prefix_error (error, "Sending command failed: "); return FALSE; case G_IO_STATUS_EOF: /* We shouldn't get EOF when writing */ g_assert_not_reached (); break; case G_IO_STATUS_NORMAL: if (written > 0) { ctx->idx += written; break; } /* If written == 0 treat as EAGAIN */ /* Fall through */ case G_IO_STATUS_AGAIN: /* We're in a non-blocking channel and therefore we're up to receive * EAGAIN; just retry in this case. */ ctx->eagain_count--; if (ctx->eagain_count <= 0) { /* If we reach the limit of EAGAIN errors, treat as a timeout error. */ self->priv->n_consecutive_timeouts++; g_signal_emit_by_name (self, MM_PORT_SIGNAL_TIMED_OUT, self->priv->n_consecutive_timeouts); g_set_error (error, MM_SERIAL_ERROR, MM_SERIAL_ERROR_SEND_FAILED, "Sending command failed: '%s'", g_strerror (errno)); return FALSE; } break; default: g_assert_not_reached (); } } /* Socket based setup */ else if (self->priv->socket) { GError *inner_error = NULL; gssize bytes_sent; /* Send N bytes of the command */ bytes_sent = g_socket_send (self->priv->socket, p, send_len, NULL, &inner_error); if (bytes_sent < 0) { /* Non-EWOULDBLOCK error? */ if (!g_error_matches (inner_error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK)) { g_propagate_error (error, inner_error); g_prefix_error (error, "Sending command failed: "); return FALSE; } /* We're in a non-blocking socket and therefore we're up to receive * EWOULDBLOCK; just retry in this case. */ g_error_free (inner_error); ctx->eagain_count--; if (ctx->eagain_count <= 0) { /* If we reach the limit of EAGAIN errors, treat as a timeout error. */ self->priv->n_consecutive_timeouts++; g_signal_emit_by_name (self, MM_PORT_SIGNAL_TIMED_OUT, self->priv->n_consecutive_timeouts); g_set_error (error, MM_SERIAL_ERROR, MM_SERIAL_ERROR_SEND_FAILED, "Sending command failed: '%s'", g_strerror (errno)); return FALSE; } /* Just keep on, will retry... */ written = 0; } else written = bytes_sent; ctx->idx += written; } else g_assert_not_reached (); if (ctx->idx >= ctx->command->len) ctx->done = TRUE; return TRUE; } static void port_serial_set_cached_reply (MMPortSerial *self, const GByteArray *command, const GByteArray *response) { g_return_if_fail (self != NULL); g_return_if_fail (MM_IS_PORT_SERIAL (self)); g_return_if_fail (command != NULL); if (response) { GByteArray *cmd_copy = g_byte_array_sized_new (command->len); GByteArray *rsp_copy = g_byte_array_sized_new (response->len); g_byte_array_append (cmd_copy, command->data, command->len); g_byte_array_append (rsp_copy, response->data, response->len); g_hash_table_insert (self->priv->reply_cache, cmd_copy, rsp_copy); } else g_hash_table_remove (self->priv->reply_cache, command); } static const GByteArray * port_serial_get_cached_reply (MMPortSerial *self, GByteArray *command) { return (const GByteArray *)g_hash_table_lookup (self->priv->reply_cache, command); } static void port_serial_schedule_queue_process (MMPortSerial *self, guint timeout_ms) { if (self->priv->timeout_id) { /* A command is already in progress */ return; } if (self->priv->queue_id) { /* Already scheduled */ return; } if (timeout_ms) self->priv->queue_id = g_timeout_add (timeout_ms, port_serial_queue_process, self); else self->priv->queue_id = g_idle_add (port_serial_queue_process, self); } static void port_serial_got_response (MMPortSerial *self, GByteArray *parsed_response, GError *error) { /* Either one or the other, not both */ g_assert ((parsed_response && !error) || (!parsed_response && error)); if (self->priv->timeout_id) { g_source_remove (self->priv->timeout_id); self->priv->timeout_id = 0; } if (self->priv->cancellable_id) { g_assert (self->priv->cancellable != NULL); g_cancellable_disconnect (self->priv->cancellable, self->priv->cancellable_id); self->priv->cancellable_id = 0; } g_clear_object (&self->priv->cancellable); /* The completion of the command context may end up fully disposing the * serial port object. In order to cope with that, we make sure we have * our own reference to the object while the completion and follow up * setup runs. */ g_object_ref (self); { GTask *task; task = g_queue_pop_head (self->priv->queue); if (task) { /* Complete the command context with the appropriate result */ if (error) { g_task_return_error (task, g_steal_pointer (&error)); } else { CommandContext *ctx; ctx = g_task_get_task_data (task); if (ctx->allow_cached) port_serial_set_cached_reply (self, ctx->command, parsed_response); g_task_return_pointer (task, g_byte_array_ref (parsed_response), (GDestroyNotify) g_byte_array_unref); } g_object_unref (task); } if (!g_queue_is_empty (self->priv->queue)) port_serial_schedule_queue_process (self, 0); } g_object_unref (self); g_clear_error (&error); } static gboolean port_serial_timed_out (gpointer data) { MMPortSerial *self = MM_PORT_SERIAL (data); GError *error; self->priv->timeout_id = 0; /* Update number of consecutive timeouts found */ self->priv->n_consecutive_timeouts++; /* 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. */ error = g_error_new_literal (MM_SERIAL_ERROR, MM_SERIAL_ERROR_RESPONSE_TIMEOUT, "Serial command timed out"); /* Make sure we have a valid reference when emitting the signal */ g_object_ref (self); { port_serial_got_response (self, NULL, error); /* Emit a timed out signal, used by upper layers to identify a disconnected * serial port */ g_signal_emit_by_name (self, MM_PORT_SIGNAL_TIMED_OUT, self->priv->n_consecutive_timeouts); } g_object_unref (self); return G_SOURCE_REMOVE; } static void port_serial_response_wait_cancelled (GCancellable *cancellable, MMPortSerial *self) { GError *error; /* We don't want to call disconnect () while in the signal handler */ self->priv->cancellable_id = 0; /* 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 cancelled command. Not sure what to do here. */ error = g_error_new_literal (G_IO_ERROR, G_IO_ERROR_CANCELLED, "Waiting for the reply cancelled"); /* Note: may complete last operation and unref the MMPortSerial */ port_serial_got_response (self, NULL, error); } static gboolean port_serial_queue_process (gpointer data) { MMPortSerial *self = MM_PORT_SERIAL (data); CommandContext *ctx; GTask *task; GCancellable *cancellable; GError *error = NULL; self->priv->queue_id = 0; task = g_queue_peek_head (self->priv->queue); if (!task) return G_SOURCE_REMOVE; ctx = g_task_get_task_data (task); if (ctx->allow_cached) { const GByteArray *cached; cached = port_serial_get_cached_reply (self, ctx->command); if (cached) { GByteArray *parsed_response; parsed_response = g_byte_array_sized_new (cached->len); g_byte_array_append (parsed_response, cached->data, cached->len); /* Note: may complete last operation and unref the MMPortSerial */ port_serial_got_response (self, parsed_response, NULL); g_byte_array_unref (parsed_response); return G_SOURCE_REMOVE; } /* Cached reply wasn't found, keep on */ } /* If error, report it */ if (!port_serial_process_command (self, ctx, &error)) { /* Note: may complete last operation and unref the MMPortSerial */ port_serial_got_response (self, NULL, error); return G_SOURCE_REMOVE; } /* Schedule the next byte of the command to be sent */ if (!ctx->done) { port_serial_schedule_queue_process (self, (mm_port_get_subsys (MM_PORT (self)) == MM_PORT_SUBSYS_TTY ? self->priv->send_delay / 1000 : 0)); return G_SOURCE_REMOVE; } /* Setup the cancellable so that we can stop waiting for a response */ cancellable = g_task_get_cancellable (task); if (cancellable) { gulong cancellable_id; self->priv->cancellable = g_object_ref (cancellable); /* If the GCancellable is already cancelled here, the callback will be * called right away, and a GError will be propagated as response. In * this case we need to completely avoid doing anything else with the * MMPortSerial, as it may already be disposed. * So, use an intermediate variable to store the cancellable id, and * just return without further processing if we're already cancelled. */ cancellable_id = g_cancellable_connect (cancellable, (GCallback)port_serial_response_wait_cancelled, self, NULL); if (!cancellable_id) return G_SOURCE_REMOVE; self->priv->cancellable_id = cancellable_id; } /* If the command is finished being sent, schedule the timeout */ self->priv->timeout_id = g_timeout_add_seconds (ctx->timeout, port_serial_timed_out, self); return G_SOURCE_REMOVE; } static void parse_response_buffer (MMPortSerial *self) { GError *error = NULL; GByteArray *parsed_response = NULL; /* Parse unsolicited messages in the subclass. * * If any message found, it's processed immediately and the message is * removed from the response buffer. */ if (MM_PORT_SERIAL_GET_CLASS (self)->parse_unsolicited) MM_PORT_SERIAL_GET_CLASS (self)->parse_unsolicited (self, self->priv->response); /* Parse response in the subclass. * * Returns TRUE either if an error is provided or if we really have the * response to process. The parsed string is returned already out of the * response buffer, and the response buffer is cleaned up accordingly. */ g_assert (MM_PORT_SERIAL_GET_CLASS (self)->parse_response != NULL); switch (MM_PORT_SERIAL_GET_CLASS (self)->parse_response (self, self->priv->response, &parsed_response, &error)) { case MM_PORT_SERIAL_RESPONSE_BUFFER: /* We have a valid response to process */ g_assert (parsed_response); self->priv->n_consecutive_timeouts = 0; /* Note: may complete last operation and unref the MMPortSerial */ port_serial_got_response (self, parsed_response, NULL); g_byte_array_unref (parsed_response); break; case MM_PORT_SERIAL_RESPONSE_ERROR: /* We have an error to process */ g_assert (error); self->priv->n_consecutive_timeouts = 0; /* Note: may complete last operation and unref the MMPortSerial */ port_serial_got_response (self, NULL, error); break; case MM_PORT_SERIAL_RESPONSE_NONE: /* Nothing to do this time */ break; default: g_assert_not_reached (); } } static gboolean common_input_available (MMPortSerial *self, GIOCondition condition) { char buf[SERIAL_BUF_SIZE + 1]; gsize bytes_read; GIOStatus status = G_IO_STATUS_NORMAL; CommandContext *ctx; GTask *task; GError *error = NULL; gboolean iterate = TRUE; gboolean keep_source = G_SOURCE_CONTINUE; if (condition & G_IO_HUP) { mm_obj_dbg (self, "unexpected port hangup!"); if (self->priv->response->len) g_byte_array_remove_range (self->priv->response, 0, self->priv->response->len); /* The completion of the commands with an error may end up fully disposing the * serial port object. In order to cope with that, we make sure we have * our own reference to the object while the close runs. */ g_object_ref (self); { port_serial_close_force (self); } g_object_unref (self); return G_SOURCE_REMOVE; } if (condition & G_IO_ERR) { if (self->priv->response->len) g_byte_array_remove_range (self->priv->response, 0, self->priv->response->len); return G_SOURCE_CONTINUE; } /* Don't read any input if the current command isn't done being sent yet */ task = g_queue_peek_nth (self->priv->queue, 0); ctx = task ? g_task_get_task_data (task) : NULL; if (ctx && (ctx->started == TRUE) && (ctx->done == FALSE)) return G_SOURCE_CONTINUE; while (iterate) { bytes_read = 0; if (self->priv->iochannel) { status = g_io_channel_read_chars (self->priv->iochannel, buf, SERIAL_BUF_SIZE, &bytes_read, &error); if (status == G_IO_STATUS_ERROR) { if (error) mm_obj_warn (self, "read error: %s", error->message); g_clear_error (&error); } } else if (self->priv->socket) { gssize sbytes_read; sbytes_read = g_socket_receive (self->priv->socket, buf, SERIAL_BUF_SIZE, NULL, /* cancellable */ &error); if (sbytes_read < 0) { bytes_read = 0; if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK)) status = G_IO_STATUS_AGAIN; else status = G_IO_STATUS_ERROR; mm_obj_warn (self, "receive error: %s", error->message); g_clear_error (&error); } else { bytes_read = (gsize) sbytes_read; status = G_IO_STATUS_NORMAL; } } /* If no bytes read, just wait for more data */ if (bytes_read == 0) break; g_assert (bytes_read > 0); serial_debug (self, "<--", buf, bytes_read); g_byte_array_append (self->priv->response, (const guint8 *) buf, bytes_read); /* See if we can parse anything. The response parsing may actually * schedule the completion of a serial command, and that in turn may end * up fully disposing this serial port object. In order to cope with * that we make sure we have our own reference to the object while the * response buffer operation is run, and then we check ourselves whether * we should be keeping this socket/iochannel source or not. */ g_object_ref (self); { /* Make sure the response doesn't grow too long */ if ((self->priv->response->len > SERIAL_BUF_SIZE) && self->priv->spew_control) { /* Notify listeners and then trim the buffer */ g_signal_emit (self, signals[BUFFER_FULL], 0, self->priv->response); g_byte_array_remove_range (self->priv->response, 0, (SERIAL_BUF_SIZE / 2)); } parse_response_buffer (self); /* If we didn't end up closing the iochannel/socket in the previous * operation, we keep this source. */ keep_source = ((self->priv->iochannel_id > 0 || self->priv->socket_source != NULL) ? G_SOURCE_CONTINUE : G_SOURCE_REMOVE); /* If we're keeping the source and we still may have bytes to read, * iterate. */ iterate = ((keep_source == G_SOURCE_CONTINUE) && (bytes_read == SERIAL_BUF_SIZE || status == G_IO_STATUS_AGAIN)); } g_object_unref (self); } return keep_source; } static gboolean iochannel_input_available (GIOChannel *iochannel, GIOCondition condition, gpointer data) { return common_input_available (MM_PORT_SERIAL (data), condition); } static gboolean socket_input_available (GSocket *socket, GIOCondition condition, gpointer data) { return common_input_available (MM_PORT_SERIAL (data), condition); } static void data_watch_enable (MMPortSerial *self, gboolean enable) { if (self->priv->iochannel_id) { if (enable) g_warn_if_fail (self->priv->iochannel_id == 0); g_source_remove (self->priv->iochannel_id); self->priv->iochannel_id = 0; } if (self->priv->socket_source) { if (enable) g_warn_if_fail (self->priv->socket_source == NULL); g_source_destroy (self->priv->socket_source); g_source_unref (self->priv->socket_source); self->priv->socket_source = NULL; } if (enable) { if (self->priv->iochannel) { self->priv->iochannel_id = g_io_add_watch (self->priv->iochannel, G_IO_IN | G_IO_ERR | G_IO_HUP, iochannel_input_available, self); } else if (self->priv->socket) { self->priv->socket_source = g_socket_create_source (self->priv->socket, G_IO_IN | G_IO_ERR | G_IO_HUP, NULL); g_source_set_callback (self->priv->socket_source, (GSourceFunc)socket_input_available, self, NULL); g_source_attach (self->priv->socket_source, NULL); } else g_warn_if_reached (); } } static void port_connected (MMPortSerial *self, GParamSpec *pspec, gpointer user_data) { gboolean connected; if (!self->priv->iochannel && !self->priv->socket) 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 (self->priv->fd >= 0 && ioctl (self->priv->fd, (connected ? TIOCNXCL : TIOCEXCL)) < 0) { mm_obj_warn (self, "could not %s serial port lock: %s", connected ? "drop" : "re-acquire", g_strerror (errno)); if (!connected) { // FIXME: do something here, maybe try again in a few seconds or // close the port and error out? } } /* When connected ignore let PPP have all the data */ data_watch_enable (self, !connected); } gboolean mm_port_serial_open (MMPortSerial *self, GError **error) { char *devfile; const char *device; struct serial_struct sinfo = { 0 }; GTimeVal tv_start, tv_end; int errno_save = 0; g_return_val_if_fail (MM_IS_PORT_SERIAL (self), FALSE); device = mm_port_get_device (MM_PORT (self)); if (self->priv->forced_close) { g_set_error (error, MM_SERIAL_ERROR, MM_SERIAL_ERROR_OPEN_FAILED, "Could not open serial device %s: it has been forced close", device); return FALSE; } if (self->priv->reopen_task) { g_set_error (error, MM_SERIAL_ERROR, MM_SERIAL_ERROR_OPEN_FAILED, "Could not open serial device %s: reopen operation in progress", device); return FALSE; } if (mm_port_get_connected (MM_PORT (self))) { g_set_error (error, MM_SERIAL_ERROR, MM_SERIAL_ERROR_OPEN_FAILED, "Could not open serial device %s: port is connected", device); return FALSE; } if (self->priv->open_count) { /* Already open */ goto success; } mm_obj_dbg (self, "opening serial port..."); g_get_current_time (&tv_start); /* Non-socket setup needs the fd open */ if (mm_port_get_subsys (MM_PORT (self)) != MM_PORT_SUBSYS_UNIX) { /* Only open a new file descriptor if we weren't given one already */ if (self->priv->fd < 0) { devfile = g_strdup_printf ("/dev/%s", device); errno = 0; self->priv->fd = open (devfile, O_RDWR | O_EXCL | O_NONBLOCK | O_NOCTTY); errno_save = errno; g_free (devfile); } if (self->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_ERROR_OPEN_FAILED_NO_DEVICE : MM_SERIAL_ERROR_OPEN_FAILED, "Could not open serial device %s: %s", device, strerror (errno_save)); return FALSE; } } /* Serial port specific setup */ if (mm_port_get_subsys (MM_PORT (self)) == MM_PORT_SUBSYS_TTY) { /* Try to lock serial device */ if (ioctl (self->priv->fd, TIOCEXCL) < 0) { errno_save = errno; g_set_error (error, MM_SERIAL_ERROR, MM_SERIAL_ERROR_OPEN_FAILED, "Could not lock serial device %s: %s", device, strerror (errno_save)); goto error; } /* Flush any waiting IO */ tcflush (self->priv->fd, TCIOFLUSH); /* Don't wait for pending data when closing the port; this can cause some * stupid devices that don't respond to URBs on a particular port to hang * for 30 seconds when probing fails. See GNOME bug #630670. */ if (ioctl (self->priv->fd, TIOCGSERIAL, &sinfo) == 0) { sinfo.closing_wait = ASYNC_CLOSING_WAIT_NONE; if (ioctl (self->priv->fd, TIOCSSERIAL, &sinfo) < 0) mm_obj_warn (self, "couldn't set serial port closing_wait to none: %s", g_strerror (errno)); } } g_warn_if_fail (MM_PORT_SERIAL_GET_CLASS (self)->config_fd); if (self->priv->fd >= 0 && mm_port_get_subsys (MM_PORT (self)) != MM_PORT_SUBSYS_WWAN && !MM_PORT_SERIAL_GET_CLASS (self)->config_fd (self, self->priv->fd, error)) { mm_obj_dbg (self, "failed to configure serial device"); goto error; } g_get_current_time (&tv_end); if (tv_end.tv_sec - tv_start.tv_sec > 7) mm_obj_warn (self, "open blocked by driver for more than 7 seconds!"); if (mm_port_get_subsys (MM_PORT (self)) != MM_PORT_SUBSYS_UNIX) { /* Create new GIOChannel */ self->priv->iochannel = g_io_channel_unix_new (self->priv->fd); /* We don't want UTF-8 encoding, we're playing with raw binary data */ g_io_channel_set_encoding (self->priv->iochannel, NULL, NULL); /* We don't want to get the channel buffered */ g_io_channel_set_buffered (self->priv->iochannel, FALSE); /* We don't want to get blocked while writing stuff */ if (!g_io_channel_set_flags (self->priv->iochannel, G_IO_FLAG_NONBLOCK, error)) { g_prefix_error (error, "Cannot set non-blocking channel: "); goto error; } } else { GSocketAddress *address; /* Create new GSocket */ self->priv->socket = g_socket_new (G_SOCKET_FAMILY_UNIX, G_SOCKET_TYPE_STREAM, G_SOCKET_PROTOCOL_DEFAULT, error); if (!self->priv->socket) { g_prefix_error (error, "Cannot create socket: "); goto error; } /* Non-blocking socket */ g_socket_set_blocking (self->priv->socket, FALSE); /* By default, abstract socket */ address = (g_unix_socket_address_new_with_type ( device, -1, (g_str_has_prefix (device, "abstract:") ? G_UNIX_SOCKET_ADDRESS_ABSTRACT : G_UNIX_SOCKET_ADDRESS_PATH))); /* Connect to address */ if (!g_socket_connect (self->priv->socket, address, NULL, error)) { g_prefix_error (error, "Cannot connect socket: "); g_object_unref (address); goto error; } g_object_unref (address); } /* Reading watch enable */ data_watch_enable (self, TRUE); g_warn_if_fail (self->priv->connected_id == 0); self->priv->connected_id = g_signal_connect (self, "notify::" MM_PORT_CONNECTED, G_CALLBACK (port_connected), NULL); success: self->priv->open_count++; mm_obj_dbg (self, "device open count is %d (open)", self->priv->open_count); /* Run additional port config if just opened */ if (self->priv->open_count == 1 && MM_PORT_SERIAL_GET_CLASS (self)->config) MM_PORT_SERIAL_GET_CLASS (self)->config (self); return TRUE; error: mm_obj_warn (self, "failed to open serial device"); if (self->priv->iochannel) { g_io_channel_unref (self->priv->iochannel); self->priv->iochannel = NULL; } if (self->priv->socket) { g_socket_close (self->priv->socket, NULL); g_object_unref (self->priv->socket); self->priv->socket = NULL; } if (self->priv->fd >= 0) { close (self->priv->fd); self->priv->fd = -1; } return FALSE; } gboolean mm_port_serial_is_open (MMPortSerial *self) { g_return_val_if_fail (self != NULL, FALSE); g_return_val_if_fail (MM_IS_PORT_SERIAL (self), FALSE); return !!self->priv->open_count; } static void _close_internal (MMPortSerial *self, gboolean force) { guint i; g_return_if_fail (MM_IS_PORT_SERIAL (self)); if (force) self->priv->open_count = 0; else { g_return_if_fail (self->priv->open_count > 0); self->priv->open_count--; } mm_obj_dbg (self, "device open count is %d (close)", self->priv->open_count); if (self->priv->open_count > 0) return; if (self->priv->connected_id) { /* Don't assume it's always connected, because it may be automatically connected during * object disposal, and this method is also called in finalize() */ if (g_signal_handler_is_connected (self, self->priv->connected_id)) g_signal_handler_disconnect (self, self->priv->connected_id); self->priv->connected_id = 0; } mm_port_serial_flash_cancel (self); if (self->priv->iochannel || self->priv->socket) { GTimeVal tv_start, tv_end; struct serial_struct sinfo = { 0 }; mm_obj_dbg (self, "closing serial port..."); mm_port_set_connected (MM_PORT (self), FALSE); g_get_current_time (&tv_start); /* Serial port specific setup */ if (self->priv->fd >= 0 && mm_port_get_subsys (MM_PORT (self)) == MM_PORT_SUBSYS_TTY) { /* Paranoid: ensure our closing_wait value is still set so we ignore * pending data when closing the port. See GNOME bug #630670. */ if (ioctl (self->priv->fd, TIOCGSERIAL, &sinfo) == 0) { if (sinfo.closing_wait != ASYNC_CLOSING_WAIT_NONE) { mm_obj_warn (self, "serial port closing_wait was reset!"); sinfo.closing_wait = ASYNC_CLOSING_WAIT_NONE; if (ioctl (self->priv->fd, TIOCSSERIAL, &sinfo) < 0) mm_obj_warn (self, "couldn't set serial port closing_wait to none: %s", g_strerror (errno)); } } tcflush (self->priv->fd, TCIOFLUSH); } /* Destroy channel */ if (self->priv->iochannel) { data_watch_enable (self, FALSE); /* unref() without g_io_channel_shutdown() to destroy the channel * without closing the fd. The close() is called explicitly after. */ g_io_channel_unref (self->priv->iochannel); self->priv->iochannel = NULL; } /* Close fd, if any */ if (self->priv->fd >= 0) { close (self->priv->fd); self->priv->fd = -1; } /* Destroy socket */ if (self->priv->socket) { data_watch_enable (self, FALSE); g_socket_close (self->priv->socket, NULL); g_object_unref (self->priv->socket); self->priv->socket = NULL; } g_get_current_time (&tv_end); mm_obj_dbg (self, "serial port closed"); /* Some ports don't respond to data and when close is called * the serial layer waits up to 30 second (closing_wait) for * that data to send before giving up and returning from close(). * Log that. See GNOME bug #630670 for more details. */ if (tv_end.tv_sec - tv_start.tv_sec > 7) mm_obj_warn (self, "close blocked by driver for more than 7 seconds!"); } /* Clear the command queue */ for (i = 0; i < g_queue_get_length (self->priv->queue); i++) { GTask *task; task = g_queue_peek_nth (self->priv->queue, i); g_task_return_new_error (task, MM_SERIAL_ERROR, MM_SERIAL_ERROR_SEND_FAILED, "Serial port is now closed"); g_object_unref (task); } g_queue_clear (self->priv->queue); if (self->priv->timeout_id) { g_source_remove (self->priv->timeout_id); self->priv->timeout_id = 0; } if (self->priv->queue_id) { g_source_remove (self->priv->queue_id); self->priv->queue_id = 0; } if (self->priv->cancellable_id) { g_assert (self->priv->cancellable != NULL); g_cancellable_disconnect (self->priv->cancellable, self->priv->cancellable_id); self->priv->cancellable_id = 0; } g_clear_object (&self->priv->cancellable); } void mm_port_serial_close (MMPortSerial *self) { g_return_if_fail (MM_IS_PORT_SERIAL (self)); if (!self->priv->forced_close) _close_internal (self, FALSE); } static void port_serial_close_force (MMPortSerial *self) { g_return_if_fail (MM_IS_PORT_SERIAL (self)); /* If already forced to close, return */ if (self->priv->forced_close) return; mm_obj_dbg (self, "forced to close port"); /* Mark as having forced the close, so that we don't warn about incorrect * open counts */ self->priv->forced_close = TRUE; /* Cancel port reopening if one is running */ port_serial_reopen_cancel (self); /* If already closed, done */ if (self->priv->open_count > 0) { _close_internal (self, TRUE); /* Notify about the forced close status */ g_signal_emit (self, signals[FORCED_CLOSE], 0); } } /*****************************************************************************/ /* Reopen */ typedef struct { guint initial_open_count; guint reopen_id; } ReopenContext; static void reopen_context_free (ReopenContext *ctx) { if (ctx->reopen_id) g_source_remove (ctx->reopen_id); g_slice_free (ReopenContext, ctx); } gboolean mm_port_serial_reopen_finish (MMPortSerial *port, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void port_serial_reopen_cancel (MMPortSerial *self) { GTask *task; if (!self->priv->reopen_task) return; /* Recover task */ task = self->priv->reopen_task; self->priv->reopen_task = NULL; g_task_return_new_error (task, G_IO_ERROR, G_IO_ERROR_CANCELLED, "Reopen cancelled"); g_object_unref (task); } static gboolean reopen_do (MMPortSerial *self) { GTask *task; ReopenContext *ctx; GError *error = NULL; guint i; /* Recover task */ g_assert (self->priv->reopen_task != NULL); task = self->priv->reopen_task; self->priv->reopen_task = NULL; ctx = g_task_get_task_data (task); ctx->reopen_id = 0; for (i = 0; i < ctx->initial_open_count; i++) { if (!mm_port_serial_open (self, &error)) { g_prefix_error (&error, "Couldn't reopen port (%u): ", i); break; } } if (error) { /* An error during port reopening may mean that the device is * already gone. Note that we won't get a HUP in the TTY when * the port is gone during the reopen wait time, because there's * no channel I/O monitoring in place. * * If we ever see this, we'll flag the port as forced close right * away, because the open count would anyway be broken afterwards. */ port_serial_close_force (self); g_task_return_error (task, error); } else g_task_return_boolean (task, TRUE); g_object_unref (task); return G_SOURCE_REMOVE; } void mm_port_serial_reopen (MMPortSerial *self, guint32 reopen_time, GAsyncReadyCallback callback, gpointer user_data) { ReopenContext *ctx; GTask *task; guint i; g_return_if_fail (MM_IS_PORT_SERIAL (self)); /* Setup context */ ctx = g_slice_new0 (ReopenContext); ctx->initial_open_count = self->priv->open_count; task = g_task_new (self, NULL, callback, user_data); g_task_set_task_data (task, ctx, (GDestroyNotify)reopen_context_free); if (self->priv->forced_close) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Serial port has been forced close."); g_object_unref (task); return; } /* If already reopening, halt */ if (self->priv->reopen_task) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_IN_PROGRESS, "Modem is already being reopened"); g_object_unref (task); return; } mm_obj_dbg (self, "reopening port (%u)", ctx->initial_open_count); for (i = 0; i < ctx->initial_open_count; i++) mm_port_serial_close (self); if (reopen_time > 0) ctx->reopen_id = g_timeout_add (reopen_time, (GSourceFunc)reopen_do, self); else ctx->reopen_id = g_idle_add ((GSourceFunc)reopen_do, self); /* Store context in private info */ self->priv->reopen_task = task; } static gboolean get_speed (MMPortSerial *self, speed_t *speed, GError **error) { struct termios options; g_assert (self->priv->fd >= 0); memset (&options, 0, sizeof (struct termios)); if (tcgetattr (self->priv->fd, &options) != 0) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "%s: tcgetattr() error %d", __func__, errno); return FALSE; } *speed = cfgetospeed (&options); return TRUE; } static gboolean set_speed (MMPortSerial *self, speed_t speed, GError **error) { struct termios options; g_assert (self->priv->fd >= 0); memset (&options, 0, sizeof (struct termios)); if (tcgetattr (self->priv->fd, &options) != 0) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "%s: tcgetattr() error %d", __func__, errno); return FALSE; } cfsetispeed (&options, speed); cfsetospeed (&options, speed); options.c_cflag |= (CLOCAL | CREAD); return internal_tcsetattr (self, self->priv->fd, &options, error); } /*****************************************************************************/ /* Flash */ typedef struct { speed_t current_speed; guint flash_id; } FlashContext; static void flash_context_free (FlashContext *ctx) { if (ctx->flash_id) g_source_remove (ctx->flash_id); g_slice_free (FlashContext, ctx); } gboolean mm_port_serial_flash_finish (MMPortSerial *port, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static gboolean flash_cancel_cb (GTask *task) { g_task_return_new_error (task, G_IO_ERROR, G_IO_ERROR_CANCELLED, "Flash cancelled"); g_object_unref (task); return G_SOURCE_REMOVE; } void mm_port_serial_flash_cancel (MMPortSerial *self) { FlashContext *ctx; GTask *task; /* Do nothing if there is no flash task */ if (!self->priv->flash_task) return; /* Recover task */ task = self->priv->flash_task; self->priv->flash_task = NULL; /* If flash operation is scheduled, unschedule it */ ctx = g_task_get_task_data (task); if (ctx->flash_id) { g_source_remove (ctx->flash_id); ctx->flash_id = 0; } /* Schedule task to be cancelled in an idle. * We do NOT want this cancellation to happen right away, * because the object reference in the flashing task may * be the last one valid. */ g_idle_add ((GSourceFunc)flash_cancel_cb, task); } static gboolean flash_do (MMPortSerial *self) { GTask *task; FlashContext *ctx; GError *error = NULL; /* Recover task */ g_assert (self->priv->flash_task != NULL); task = self->priv->flash_task; self->priv->flash_task = NULL; ctx = g_task_get_task_data (task); ctx->flash_id = 0; if (self->priv->flash_ok && mm_port_get_subsys (MM_PORT (self)) == MM_PORT_SUBSYS_TTY) { if (ctx->current_speed) { if (!set_speed (self, ctx->current_speed, &error)) g_assert (error); } else { error = g_error_new_literal (MM_SERIAL_ERROR, MM_SERIAL_ERROR_FLASH_FAILED, "Failed to retrieve current speed"); } } if (error) g_task_return_error (task, error); else g_task_return_boolean (task, TRUE); g_object_unref (task); return G_SOURCE_REMOVE; } void mm_port_serial_flash (MMPortSerial *self, guint32 flash_time, gboolean ignore_errors, GAsyncReadyCallback callback, gpointer user_data) { FlashContext *ctx; GTask *task; GError *error = NULL; gboolean success; g_return_if_fail (MM_IS_PORT_SERIAL (self)); /* Setup context */ ctx = g_slice_new0 (FlashContext); task = g_task_new (self, NULL, callback, user_data); g_task_set_task_data (task, ctx, (GDestroyNotify)flash_context_free); if (!mm_port_serial_is_open (self)) { g_task_return_new_error (task, MM_SERIAL_ERROR, MM_SERIAL_ERROR_NOT_OPEN, "The serial port is not open."); g_object_unref (task); return; } if (self->priv->flash_task) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_IN_PROGRESS, "Modem is already being flashed."); g_object_unref (task); return; } /* Flashing only in TTY */ if (!self->priv->flash_ok || mm_port_get_subsys (MM_PORT (self)) != MM_PORT_SUBSYS_TTY) { self->priv->flash_task = task; ctx->flash_id = g_idle_add ((GSourceFunc)flash_do, self); return; } /* Grab current speed so we can reset it after flashing */ success = get_speed (self, &ctx->current_speed, &error); if (!success && !ignore_errors) { g_task_return_error (task, error); g_object_unref (task); return; } g_clear_error (&error); success = set_speed (self, B0, &error); if (!success && !ignore_errors) { g_task_return_error (task, error); g_object_unref (task); return; } g_clear_error (&error); self->priv->flash_task = task; ctx->flash_id = g_timeout_add (flash_time, (GSourceFunc)flash_do, self); } /*****************************************************************************/ gboolean mm_port_serial_set_flow_control (MMPortSerial *self, MMFlowControl flow_control, GError **error) { struct termios options; gchar *flow_control_str = NULL; GError *inner_error = NULL; /* retrieve current settings */ memset (&options, 0, sizeof (struct termios)); if (tcgetattr (self->priv->fd, &options) != 0) { inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "couldn't get serial port attributes: %s", g_strerror (errno)); goto out; } flow_control_str = mm_flow_control_build_string_from_mask (flow_control); /* Return if current settings are already what we want */ if (!set_flow_control_termios (self, flow_control, &options)) { mm_obj_dbg (self, "no need to change flow control settings: already %s", flow_control_str); goto out; } if (!internal_tcsetattr (self, self->priv->fd, &options, &inner_error)) goto out; mm_obj_dbg (self, "flow control settings updated to %s", flow_control_str); out: g_free (flow_control_str); if (inner_error) { g_propagate_error (error, inner_error); return FALSE; } return TRUE; } MMFlowControl mm_port_serial_get_flow_control (MMPortSerial *self) { return self->priv->flow_control; } /*****************************************************************************/ MMPortSerial * mm_port_serial_new (const char *name, MMPortType ptype) { return MM_PORT_SERIAL (g_object_new (MM_TYPE_PORT_SERIAL, MM_PORT_DEVICE, name, MM_PORT_SUBSYS, MM_PORT_SUBSYS_TTY, MM_PORT_TYPE, ptype, NULL)); } static gboolean ba_equal (gconstpointer v1, gconstpointer v2) { const GByteArray *a = v1; const GByteArray *b = v2; if (!a && b) return -1; else if (a && !b) return 1; else if (!a && !b) return 0; g_assert (a && b); if (a->len < b->len) return -1; else if (a->len > b->len) return 1; g_assert (a->len == b->len); return !memcmp (a->data, b->data, a->len); } static guint ba_hash (gconstpointer v) { /* 31 bit hash function */ const GByteArray *array = v; guint32 i, h = (const signed char) array->data[0]; for (i = 1; i < array->len; i++) h = (h << 5) - h + (const signed char) array->data[i]; return h; } static void ba_free (gpointer v) { g_byte_array_unref ((GByteArray *) v); } static void mm_port_serial_init (MMPortSerial *self) { self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, MM_TYPE_PORT_SERIAL, MMPortSerialPrivate); self->priv->reply_cache = g_hash_table_new_full (ba_hash, ba_equal, ba_free, ba_free); self->priv->fd = -1; self->priv->baud = 57600; self->priv->bits = 8; self->priv->parity = 'n'; self->priv->stopbits = 1; self->priv->flow_control = MM_FLOW_CONTROL_UNKNOWN; self->priv->send_delay = 1000; self->priv->queue = g_queue_new (); self->priv->response = g_byte_array_sized_new (500); } static void set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { MMPortSerial *self = MM_PORT_SERIAL (object); switch (prop_id) { case PROP_FD: self->priv->fd = g_value_get_int (value); break; case PROP_BAUD: self->priv->baud = g_value_get_uint (value); break; case PROP_BITS: self->priv->bits = g_value_get_uint (value); break; case PROP_PARITY: self->priv->parity = g_value_get_schar (value); break; case PROP_STOPBITS: self->priv->stopbits = g_value_get_uint (value); break; case PROP_FLOW_CONTROL: self->priv->flow_control = g_value_get_flags (value); break; case PROP_SEND_DELAY: self->priv->send_delay = g_value_get_uint64 (value); break; case PROP_SPEW_CONTROL: self->priv->spew_control = g_value_get_boolean (value); break; case PROP_FLASH_OK: self->priv->flash_ok = 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) { MMPortSerial *self = MM_PORT_SERIAL (object); switch (prop_id) { case PROP_FD: g_value_set_int (value, self->priv->fd); break; case PROP_BAUD: g_value_set_uint (value, self->priv->baud); break; case PROP_BITS: g_value_set_uint (value, self->priv->bits); break; case PROP_PARITY: g_value_set_schar (value, self->priv->parity); break; case PROP_STOPBITS: g_value_set_uint (value, self->priv->stopbits); break; case PROP_FLOW_CONTROL: g_value_set_flags (value, self->priv->flow_control); break; case PROP_SEND_DELAY: g_value_set_uint64 (value, self->priv->send_delay); break; case PROP_SPEW_CONTROL: g_value_set_boolean (value, self->priv->spew_control); break; case PROP_FLASH_OK: g_value_set_boolean (value, self->priv->flash_ok); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void finalize (GObject *object) { MMPortSerial *self = MM_PORT_SERIAL (object); port_serial_close_force (MM_PORT_SERIAL (object)); mm_port_serial_flash_cancel (MM_PORT_SERIAL (object)); /* These are disposed during port closing */ g_assert (self->priv->iochannel == NULL); g_assert (self->priv->iochannel_id == 0); g_assert (self->priv->socket == NULL); g_assert (self->priv->socket_source == NULL); if (self->priv->timeout_id) g_source_remove (self->priv->timeout_id); if (self->priv->queue_id) g_source_remove (self->priv->queue_id); g_hash_table_destroy (self->priv->reply_cache); g_byte_array_unref (self->priv->response); g_queue_free (self->priv->queue); G_OBJECT_CLASS (mm_port_serial_parent_class)->finalize (object); } static void mm_port_serial_class_init (MMPortSerialClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); g_type_class_add_private (object_class, sizeof (MMPortSerialPrivate)); /* Virtual methods */ object_class->set_property = set_property; object_class->get_property = get_property; object_class->finalize = finalize; klass->config_fd = real_config_fd; /* Properties */ g_object_class_install_property (object_class, PROP_FD, g_param_spec_int (MM_PORT_SERIAL_FD, "File descriptor", "File descriptor", -1, G_MAXINT, -1, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); g_object_class_install_property (object_class, PROP_BAUD, g_param_spec_uint (MM_PORT_SERIAL_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_PORT_SERIAL_BITS, "Bits", "Bits", 5, 8, 8, G_PARAM_READWRITE)); g_object_class_install_property (object_class, PROP_PARITY, g_param_spec_char (MM_PORT_SERIAL_PARITY, "Parity", "Parity", 'E', 'o', 'n', G_PARAM_READWRITE)); g_object_class_install_property (object_class, PROP_STOPBITS, g_param_spec_uint (MM_PORT_SERIAL_STOPBITS, "Stopbits", "Stopbits", 1, 2, 1, G_PARAM_READWRITE)); g_object_class_install_property (object_class, PROP_FLOW_CONTROL, g_param_spec_flags (MM_PORT_SERIAL_FLOW_CONTROL, "FlowControl", "Select flow control", MM_TYPE_FLOW_CONTROL, MM_FLOW_CONTROL_UNKNOWN, G_PARAM_READWRITE)); g_object_class_install_property (object_class, PROP_SEND_DELAY, g_param_spec_uint64 (MM_PORT_SERIAL_SEND_DELAY, "SendDelay", "Send delay for each byte in microseconds", 0, G_MAXUINT64, 0, G_PARAM_READWRITE)); g_object_class_install_property (object_class, PROP_SPEW_CONTROL, g_param_spec_boolean (MM_PORT_SERIAL_SPEW_CONTROL, "SpewControl", "Spew control", FALSE, G_PARAM_READWRITE)); g_object_class_install_property (object_class, PROP_FLASH_OK, g_param_spec_boolean (MM_PORT_SERIAL_FLASH_OK, "FlashOk", "Flashing the port (0 baud for a short period) " "is allowed.", TRUE, G_PARAM_READWRITE)); /* Signals */ signals[BUFFER_FULL] = g_signal_new ("buffer-full", G_OBJECT_CLASS_TYPE (object_class), G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (MMPortSerialClass, buffer_full), NULL, NULL, g_cclosure_marshal_generic, G_TYPE_NONE, 1, G_TYPE_POINTER); signals[FORCED_CLOSE] = g_signal_new ("forced-close", G_OBJECT_CLASS_TYPE (object_class), G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (MMPortSerialClass, forced_close), NULL, NULL, g_cclosure_marshal_generic, G_TYPE_NONE, 0); } ModemManager-1.23.4-dev/src/mm-port-serial.h000066400000000000000000000155461456466623000205500ustar00rootroot00000000000000/* -*- 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_PORT_SERIAL_H #define MM_PORT_SERIAL_H #include #include #include #include "mm-modem-helpers.h" #include "mm-port.h" #define MM_TYPE_PORT_SERIAL (mm_port_serial_get_type ()) #define MM_PORT_SERIAL(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_PORT_SERIAL, MMPortSerial)) #define MM_PORT_SERIAL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_PORT_SERIAL, MMPortSerialClass)) #define MM_IS_PORT_SERIAL(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_PORT_SERIAL)) #define MM_IS_PORT_SERIAL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_PORT_SERIAL)) #define MM_PORT_SERIAL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_PORT_SERIAL, MMPortSerialClass)) #define MM_PORT_SERIAL_BAUD "baud" #define MM_PORT_SERIAL_BITS "bits" #define MM_PORT_SERIAL_PARITY "parity" #define MM_PORT_SERIAL_STOPBITS "stopbits" #define MM_PORT_SERIAL_FLOW_CONTROL "flowcontrol" #define MM_PORT_SERIAL_SEND_DELAY "send-delay" #define MM_PORT_SERIAL_FD "fd" /* Construct-only */ #define MM_PORT_SERIAL_SPEW_CONTROL "spew-control" #define MM_PORT_SERIAL_FLASH_OK "flash-ok" typedef enum { MM_PORT_SERIAL_RESPONSE_NONE, MM_PORT_SERIAL_RESPONSE_BUFFER, MM_PORT_SERIAL_RESPONSE_ERROR, } MMPortSerialResponseType; typedef struct _MMPortSerial MMPortSerial; typedef struct _MMPortSerialClass MMPortSerialClass; typedef struct _MMPortSerialPrivate MMPortSerialPrivate; struct _MMPortSerial { MMPort parent; MMPortSerialPrivate *priv; }; struct _MMPortSerialClass { MMPortClass parent; /* Called for subclasses to parse unsolicited responses. If any recognized * unsolicited response is found, it should be removed from the 'response' * byte array before returning. */ void (*parse_unsolicited) (MMPortSerial *self, GByteArray *response); /* * Called to parse the device's response to a command or determine if the * response was an error response. * * If the response indicates an error, @MM_PORT_SERIAL_RESPONSE_ERROR will * be returned and an appropriate GError set in @error. * * If the response indicates a valid response, @MM_PORT_SERIAL_RESPONSE_BUFFER * will be returned, and a newly allocated GByteArray set in @parsed_response. * * If there is no response, @MM_PORT_SERIAL_RESPONSE_NONE will be returned, * and neither @error nor @parsed_response will be set. * * The implementation is allowed to cleanup the @response byte array, e.g. to * just remove 1 single response if more than one found. */ MMPortSerialResponseType (*parse_response) (MMPortSerial *self, GByteArray *response, GByteArray **parsed_response, GError **error); /* Called to configure the serial port fd after it's opened. On error, should * return FALSE and set 'error' as appropriate. */ gboolean (*config_fd) (MMPortSerial *self, int fd, GError **error); /* Called to configure the serial port after it's opened. Errors, if any, * should get ignored. */ void (*config) (MMPortSerial *self); void (*debug_log) (MMPortSerial *self, const gchar *prefix, const gchar *buf, gsize len); /* Signals */ void (*buffer_full) (MMPortSerial *port, const GByteArray *buffer); void (*forced_close) (MMPortSerial *port); }; GType mm_port_serial_get_type (void); G_DEFINE_AUTOPTR_CLEANUP_FUNC (MMPortSerial, g_object_unref) MMPortSerial *mm_port_serial_new (const char *name, MMPortType ptype); /* Keep in mind that port open/close is refcounted, so ensure that * open/close calls are properly balanced. */ gboolean mm_port_serial_is_open (MMPortSerial *self); gboolean mm_port_serial_open (MMPortSerial *self, GError **error); void mm_port_serial_close (MMPortSerial *self); /* Reopen(), async */ void mm_port_serial_reopen (MMPortSerial *self, guint32 reopen_time, GAsyncReadyCallback callback, gpointer user_data); gboolean mm_port_serial_reopen_finish (MMPortSerial *port, GAsyncResult *res, GError **error); void mm_port_serial_flash (MMPortSerial *self, guint32 flash_time, gboolean ignore_errors, GAsyncReadyCallback callback, gpointer user_data); gboolean mm_port_serial_flash_finish (MMPortSerial *self, GAsyncResult *res, GError **error); void mm_port_serial_flash_cancel (MMPortSerial *self); void mm_port_serial_command (MMPortSerial *self, GByteArray *command, guint32 timeout_seconds, gboolean allow_cached, gboolean run_next, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); GByteArray *mm_port_serial_command_finish (MMPortSerial *self, GAsyncResult *res, GError **error); gboolean mm_port_serial_set_flow_control (MMPortSerial *self, MMFlowControl flow_control, GError **error); MMFlowControl mm_port_serial_get_flow_control (MMPortSerial *self); #endif /* MM_PORT_SERIAL_H */ ModemManager-1.23.4-dev/src/mm-port.c000066400000000000000000000205301456466623000172530ustar00rootroot00000000000000/* -*- 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 #include #include #include #include "mm-port.h" #include "mm-port-enums-types.h" #include "mm-log-object.h" static void log_object_iface_init (MMLogObjectInterface *iface); G_DEFINE_TYPE_EXTENDED (MMPort, mm_port, G_TYPE_OBJECT, 0, G_IMPLEMENT_INTERFACE (MM_TYPE_LOG_OBJECT, log_object_iface_init)) enum { PROP_0, PROP_DEVICE, PROP_SUBSYS, PROP_TYPE, PROP_CONNECTED, PROP_KERNEL_DEVICE, LAST_PROP }; enum { TIMED_OUT, REMOVED, LAST_SIGNAL }; static guint signals[LAST_SIGNAL] = { 0 }; struct _MMPortPrivate { gchar *device; MMPortSubsys subsys; MMPortType ptype; gboolean connected; MMKernelDevice *kernel_device; }; /*****************************************************************************/ 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 self->priv->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 self->priv->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 self->priv->ptype; } 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 self->priv->connected; } void mm_port_set_connected (MMPort *self, gboolean connected) { g_return_if_fail (self != NULL); g_return_if_fail (MM_IS_PORT (self)); if (self->priv->connected != connected) { self->priv->connected = connected; g_object_notify (G_OBJECT (self), MM_PORT_CONNECTED); mm_obj_dbg (self, "port now %s", connected ? "connected" : "disconnected"); } } MMKernelDevice * mm_port_peek_kernel_device (MMPort *self) { g_return_val_if_fail (MM_IS_PORT (self), NULL); return self->priv->kernel_device; } /*****************************************************************************/ static gchar * log_object_build_id (MMLogObject *_self) { MMPort *self; self = MM_PORT (_self); return g_strdup_printf ("%s/%s", mm_port_get_device (self), mm_port_type_get_string (mm_port_get_port_type (self))); } /*****************************************************************************/ static void mm_port_init (MMPort *self) { self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, MM_TYPE_PORT, MMPortPrivate); } static void set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { MMPort *self = MM_PORT (object); switch (prop_id) { case PROP_DEVICE: /* Construct only */ self->priv->device = g_value_dup_string (value); break; case PROP_SUBSYS: /* Construct only */ self->priv->subsys = g_value_get_uint (value); break; case PROP_TYPE: /* Construct only */ self->priv->ptype = g_value_get_uint (value); break; case PROP_CONNECTED: self->priv->connected = g_value_get_boolean (value); break; case PROP_KERNEL_DEVICE: /* Not construct only, but only set once */ g_assert (!self->priv->kernel_device); self->priv->kernel_device = g_value_dup_object (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) { MMPort *self = MM_PORT (object); switch (prop_id) { case PROP_DEVICE: g_value_set_string (value, self->priv->device); break; case PROP_SUBSYS: g_value_set_uint (value, self->priv->subsys); break; case PROP_TYPE: g_value_set_uint (value, self->priv->ptype); break; case PROP_CONNECTED: g_value_set_boolean (value, self->priv->connected); break; case PROP_KERNEL_DEVICE: g_value_set_object (value, self->priv->kernel_device); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void finalize (GObject *object) { MMPort *self = MM_PORT (object); g_free (self->priv->device); G_OBJECT_CLASS (mm_port_parent_class)->finalize (object); } static void dispose (GObject *object) { MMPort *self = MM_PORT (object); g_clear_object (&self->priv->kernel_device); G_OBJECT_CLASS (mm_port_parent_class)->dispose (object); } static void log_object_iface_init (MMLogObjectInterface *iface) { iface->build_id = log_object_build_id; } 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->set_property = set_property; object_class->get_property = get_property; object_class->finalize = finalize; object_class->dispose = dispose; 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_CONNECTED, g_param_spec_boolean (MM_PORT_CONNECTED, "Connected", "Is connected for data and not usable for control", FALSE, G_PARAM_READWRITE)); g_object_class_install_property (object_class, PROP_KERNEL_DEVICE, g_param_spec_object (MM_PORT_KERNEL_DEVICE, "Kernel device", "kernel device object", MM_TYPE_KERNEL_DEVICE, G_PARAM_READWRITE)); signals[TIMED_OUT] = g_signal_new (MM_PORT_SIGNAL_TIMED_OUT, G_OBJECT_CLASS_TYPE (object_class), G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (MMPortClass, timed_out), NULL, NULL, g_cclosure_marshal_generic, G_TYPE_NONE, 1, G_TYPE_UINT); signals[REMOVED] = g_signal_new (MM_PORT_SIGNAL_REMOVED, G_OBJECT_CLASS_TYPE (object_class), G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (MMPortClass, removed), NULL, NULL, g_cclosure_marshal_generic, G_TYPE_NONE, 0); } ModemManager-1.23.4-dev/src/mm-port.h000066400000000000000000000060101456466623000172550ustar00rootroot00000000000000/* -*- 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 #include #include #include "mm-kernel-device.h" typedef enum { /*< underscore_name=mm_port_subsys >*/ MM_PORT_SUBSYS_UNKNOWN = 0x0, MM_PORT_SUBSYS_TTY, MM_PORT_SUBSYS_NET, MM_PORT_SUBSYS_USBMISC, MM_PORT_SUBSYS_UNIX, MM_PORT_SUBSYS_QRTR, MM_PORT_SUBSYS_RPMSG, MM_PORT_SUBSYS_WWAN, MM_PORT_SUBSYS_LAST = MM_PORT_SUBSYS_WWAN /*< skip >*/ } MMPortSubsys; typedef enum { /*< underscore_name=mm_port_type >*/ MM_PORT_TYPE_UNKNOWN = 0x0, MM_PORT_TYPE_IGNORED, MM_PORT_TYPE_NET, MM_PORT_TYPE_AT, MM_PORT_TYPE_QCDM, MM_PORT_TYPE_GPS, MM_PORT_TYPE_QMI, MM_PORT_TYPE_MBIM, MM_PORT_TYPE_AUDIO, MM_PORT_TYPE_LAST = MM_PORT_TYPE_AUDIO /*< skip >*/ } 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_CONNECTED "connected" #define MM_PORT_KERNEL_DEVICE "kernel-device" #define MM_PORT_SIGNAL_TIMED_OUT "timed-out" #define MM_PORT_SIGNAL_REMOVED "removed" typedef struct _MMPort MMPort; typedef struct _MMPortClass MMPortClass; typedef struct _MMPortPrivate MMPortPrivate; struct _MMPort { GObject parent; MMPortPrivate *priv; }; struct _MMPortClass { GObjectClass parent; /* signals */ void (* timed_out) (MMPort *port, guint n_consecutive_replies); void (* removed) (MMPort *port); }; GType mm_port_get_type (void); G_DEFINE_AUTOPTR_CLEANUP_FUNC (MMPort, g_object_unref) const gchar *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_connected (MMPort *self); void mm_port_set_connected (MMPort *self, gboolean connected); MMKernelDevice *mm_port_peek_kernel_device (MMPort *self); #endif /* MM_PORT_H */ ModemManager-1.23.4-dev/src/mm-private-boxed-types.c000066400000000000000000000141111456466623000222000ustar00rootroot00000000000000/* -*- 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) 2012 - Google, Inc. */ #include "mm-private-boxed-types.h" #include "string.h" static guint16 * uint16_array_copy (guint16 *array) { guint16 *dup; guint i; if (!array) return NULL; /* Get 0-terminated array size */ for (i = 0; array[i]; i++); dup = g_new (guint16, i + 1); memcpy (dup, array, i * sizeof (guint16)); dup[i] = 0; return dup; } GType mm_uint16_array_get_type (void) { static gsize g_define_type_id_initialized = 0; if (g_once_init_enter (&g_define_type_id_initialized)) { GType g_define_type_id = g_boxed_type_register_static (g_intern_static_string ("MMUint16Array"), (GBoxedCopyFunc) uint16_array_copy, (GBoxedFreeFunc) g_free); g_once_init_leave (&g_define_type_id_initialized, g_define_type_id); } return g_define_type_id_initialized; } static mm_uint16_pair * uint16_pair_array_copy (mm_uint16_pair *array) { mm_uint16_pair *dup; guint i; if (!array) return NULL; /* Get 0-terminated array size */ for (i = 0; array[i].l; i++); dup = g_new (mm_uint16_pair, i + 1); memcpy (dup, array, i * sizeof (mm_uint16_pair)); dup[i].l = 0; dup[i].r = 0; return dup; } GType mm_uint16_pair_array_get_type (void) { static gsize g_define_type_id_initialized = 0; if (g_once_init_enter (&g_define_type_id_initialized)) { GType g_define_type_id = g_boxed_type_register_static (g_intern_static_string ("MMUint16PairArray"), (GBoxedCopyFunc) uint16_pair_array_copy, (GBoxedFreeFunc) g_free); g_once_init_leave (&g_define_type_id_initialized, g_define_type_id); } return g_define_type_id_initialized; } static void str_pair_array_free (mm_str_pair *array) { guint i; for (i = 0; array[i].l; i++) { g_free (array[i].l); g_free (array[i].r); } g_free (array); } static mm_str_pair * str_pair_array_copy (mm_str_pair *array) { mm_str_pair *dup; guint i; if (!array) return NULL; /* Get NULL-terminated array size */ for (i = 0; array[i].l; i++); dup = g_new (mm_str_pair, i + 1); for (i = 0; array[i].l; i++) { dup[i].l = g_strdup (array[i].l); dup[i].r = g_strdup (array[i].r); } dup[i].l = NULL; dup[i].r = NULL; return dup; } GType mm_str_pair_array_get_type (void) { static gsize g_define_type_id_initialized = 0; if (g_once_init_enter (&g_define_type_id_initialized)) { GType g_define_type_id = g_boxed_type_register_static (g_intern_static_string ("MMStrPairArray"), (GBoxedCopyFunc) str_pair_array_copy, (GBoxedFreeFunc) str_pair_array_free); g_once_init_leave (&g_define_type_id_initialized, g_define_type_id); } return g_define_type_id_initialized; } static gpointer * pointer_array_copy (gpointer *array) { gpointer *dup; guint i; if (!array) return NULL; /* Get NULL-terminated array size */ for (i = 0; array[i]; i++); dup = g_new (gpointer, i + 1); memcpy (dup, array, i * sizeof (gpointer)); dup[i] = NULL; return dup; } GType mm_pointer_array_get_type (void) { static gsize g_define_type_id_initialized = 0; if (g_once_init_enter (&g_define_type_id_initialized)) { GType g_define_type_id = g_boxed_type_register_static (g_intern_static_string ("MMPointerArray"), (GBoxedCopyFunc) pointer_array_copy, (GBoxedFreeFunc) g_free); g_once_init_leave (&g_define_type_id_initialized, g_define_type_id); } return g_define_type_id_initialized; } static GPtrArray * object_array_copy (GPtrArray *object_array) { return g_ptr_array_ref (object_array); } static void object_array_free (GPtrArray *object_array) { g_ptr_array_unref (object_array); } GType mm_object_array_get_type (void) { static gsize g_define_type_id_initialized = 0; if (g_once_init_enter (&g_define_type_id_initialized)) { GType g_define_type_id = g_boxed_type_register_static (g_intern_static_string ("MMObjectArray"), (GBoxedCopyFunc) object_array_copy, (GBoxedFreeFunc) object_array_free); g_once_init_leave (&g_define_type_id_initialized, g_define_type_id); } return g_define_type_id_initialized; } static void async_method_free (MMAsyncMethod *method) { g_slice_free (MMAsyncMethod, method); } static MMAsyncMethod * async_method_copy (MMAsyncMethod *original) { MMAsyncMethod *copy; if (!original) return NULL; copy = g_slice_new (MMAsyncMethod); copy->async = original->async; copy->finish = original->finish; return copy; } GType mm_async_method_get_type (void) { static gsize g_define_type_id_initialized = 0; if (g_once_init_enter (&g_define_type_id_initialized)) { GType g_define_type_id = g_boxed_type_register_static (g_intern_static_string ("MMAsyncMethod"), (GBoxedCopyFunc) async_method_copy, (GBoxedFreeFunc) async_method_free); g_once_init_leave (&g_define_type_id_initialized, g_define_type_id); } return g_define_type_id_initialized; } ModemManager-1.23.4-dev/src/mm-private-boxed-types.h000066400000000000000000000032201456466623000222040ustar00rootroot00000000000000/* -*- 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) 2012 - Google, Inc. */ #ifndef __MM_PRIVATE_BOXED_TYPES_H__ #define __MM_PRIVATE_BOXED_TYPES_H__ #include G_BEGIN_DECLS GType mm_uint16_array_get_type (void) G_GNUC_CONST; #define MM_TYPE_UINT16_ARRAY (mm_uint16_array_get_type ()) typedef struct { guint16 l; guint16 r; } mm_uint16_pair; GType mm_uint16_pair_array_get_type (void) G_GNUC_CONST; #define MM_TYPE_UINT16_PAIR_ARRAY (mm_uint16_pair_array_get_type ()) typedef struct { gchar *l; gchar *r; } mm_str_pair; GType mm_str_pair_array_get_type (void) G_GNUC_CONST; #define MM_TYPE_STR_PAIR_ARRAY (mm_str_pair_array_get_type ()) GType mm_pointer_array_get_type (void) G_GNUC_CONST; #define MM_TYPE_POINTER_ARRAY (mm_pointer_array_get_type ()) GType mm_object_array_get_type (void) G_GNUC_CONST; #define MM_TYPE_OBJECT_ARRAY (mm_object_array_get_type ()) typedef struct { GCallback async; GCallback finish; } MMAsyncMethod; GType mm_async_method_get_type (void) G_GNUC_CONST; #define MM_TYPE_ASYNC_METHOD (mm_async_method_get_type ()) G_END_DECLS #endif /* __MM_PRIVATE_BOXED_TYPES_H__ */ ModemManager-1.23.4-dev/src/mm-qrtr-bus-watcher.c000066400000000000000000000271251456466623000215100ustar00rootroot00000000000000/* -*- 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 2020 Google LLC */ #include #include #include #include "mm-utils.h" #include "mm-qrtr-bus-watcher.h" static void log_object_iface_init (MMLogObjectInterface *iface); G_DEFINE_TYPE_EXTENDED (MMQrtrBusWatcher, mm_qrtr_bus_watcher, G_TYPE_OBJECT, 0, G_IMPLEMENT_INTERFACE (MM_TYPE_LOG_OBJECT, log_object_iface_init)) struct _MMQrtrBusWatcherPrivate { QrtrBus *qrtr_bus; guint node_added_id; guint node_removed_id; /* Map of NodeNumber -> QRTR nodes available */ GHashTable *nodes; }; enum { QRTR_DEVICE_ADDED, QRTR_DEVICE_REMOVED, LAST_SIGNAL, }; static guint signals[LAST_SIGNAL] = { 0 }; /*****************************************************************************/ static gchar * log_object_build_id (MMLogObject *_self) { return g_strdup ("qrtr-bus-watcher"); } /*****************************************************************************/ typedef struct { MMQrtrBusWatcher *self; QrtrNode *node; } DeviceContext; static void device_context_free (DeviceContext *ctx) { g_object_unref (ctx->self); g_object_unref (ctx->node); g_slice_free (DeviceContext, ctx); } static void qrtr_node_services_ready (QrtrNode *node, GAsyncResult *res, DeviceContext *ctx) { guint32 node_id; node_id = qrtr_node_get_id (node); if (!qrtr_node_wait_for_services_finish (node, res, NULL)) { mm_obj_dbg (ctx->self, "qrtr node %u doesn't have required services to be considered a control node", node_id); g_hash_table_remove (ctx->self->priv->nodes, GUINT_TO_POINTER (node_id)); device_context_free (ctx); return; } mm_obj_dbg (ctx->self, "qrtr services ready for node %u", node_id); g_signal_emit (ctx->self, signals[QRTR_DEVICE_ADDED], 0, node_id); device_context_free (ctx); } static void handle_qrtr_node_added (QrtrBus *qrtr_bus, guint32 node_id, MMQrtrBusWatcher *self) { g_autoptr(QrtrNode) node = NULL; g_autoptr(GArray) services = NULL; DeviceContext *ctx; static const QmiService required_services[] = { QMI_SERVICE_WDS, QMI_SERVICE_NAS, QMI_SERVICE_DMS }; mm_obj_dbg (self, "qrtr node %u added", node_id); node = qrtr_bus_get_node (qrtr_bus, node_id); if (!node) { mm_obj_warn (self, "cannot find node %u", node_id); return; } if (g_hash_table_contains (self->priv->nodes, GUINT_TO_POINTER (node_id))) { mm_obj_warn (self, "qrtr node %u was previously added", node_id); return; } /* a full node reference now owned by the hash table */ g_hash_table_insert (self->priv->nodes, GUINT_TO_POINTER (node_id), g_object_ref (node)); mm_obj_dbg (self, "waiting for modem services on node %u", node_id); /* Check if the node provides services to be sure the node represents a * modem. */ services = g_array_sized_new (FALSE, FALSE, sizeof (QmiService), G_N_ELEMENTS (required_services)); g_array_append_vals (services, required_services, G_N_ELEMENTS (required_services)); /* Setup command context */ ctx = g_slice_new0 (DeviceContext); ctx->self = g_object_ref (self); ctx->node = g_object_ref (node); qrtr_node_wait_for_services (node, services, 1000, /* ms */ NULL, (GAsyncReadyCallback) qrtr_node_services_ready, ctx); } static void handle_qrtr_node_removed (QrtrBus *qrtr_bus, guint32 node_id, MMQrtrBusWatcher *self) { QrtrNode *node; node = qrtr_bus_get_node (qrtr_bus, node_id); if (!node) { mm_obj_warn (self, "cannot find node %u", node_id); return; } g_hash_table_remove (self->priv->nodes, GUINT_TO_POINTER (node_id)); mm_obj_dbg (self, "qrtr node %u removed", node_id); g_signal_emit (self, signals[QRTR_DEVICE_REMOVED], 0, node_id); } /*****************************************************************************/ QrtrNode * mm_qrtr_bus_watcher_peek_node (MMQrtrBusWatcher *self, guint32 node_id) { g_assert (MM_IS_QRTR_BUS_WATCHER (self)); return g_hash_table_lookup (self->priv->nodes, GUINT_TO_POINTER (node_id)); } /*****************************************************************************/ gboolean mm_qrtr_bus_watcher_start_finish (MMQrtrBusWatcher *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } typedef struct { MMQrtrBusWatcher *self; QrtrNode *node; } ProcessExistingNodes; static gboolean process_existing_nodes_idle (ProcessExistingNodes *ctx) { handle_qrtr_node_added ( ctx->self->priv->qrtr_bus, qrtr_node_get_id (ctx->node), ctx->self); g_object_unref (ctx->self); g_object_unref (ctx->node); g_slice_free (ProcessExistingNodes, ctx); return G_SOURCE_REMOVE; } static void process_existing_nodes (MMQrtrBusWatcher *self) { GList *nodes, *l; QrtrNode *node; ProcessExistingNodes *ctx; nodes = qrtr_bus_peek_nodes (self->priv->qrtr_bus); for (l = nodes; l; l = g_list_next (l)) { node = l->data; ctx = g_slice_new (ProcessExistingNodes); ctx->self = g_object_ref (self); ctx->node = g_object_ref (node); g_idle_add ((GSourceFunc) process_existing_nodes_idle, ctx); } } static void qrtr_bus_ready (GObject *source, GAsyncResult *res, GTask *task) { MMQrtrBusWatcher *self; GError *error = NULL; self = g_task_get_source_object (task); self->priv->qrtr_bus = qrtr_bus_new_finish (res, &error); if (!self->priv->qrtr_bus) { g_task_return_error (task, error); g_object_unref (task); return; } /* Listen for bus events */ self->priv->node_added_id = g_signal_connect (self->priv->qrtr_bus, QRTR_BUS_SIGNAL_NODE_ADDED, G_CALLBACK (handle_qrtr_node_added), self); self->priv->node_removed_id = g_signal_connect (self->priv->qrtr_bus, QRTR_BUS_SIGNAL_NODE_REMOVED, G_CALLBACK (handle_qrtr_node_removed), self); process_existing_nodes (self); g_task_return_boolean (task, TRUE); g_object_unref (task); } void mm_qrtr_bus_watcher_start (MMQrtrBusWatcher *self, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; task = g_task_new (self, NULL, callback, user_data); qrtr_bus_new (0, /* disable initial lookup wait */ NULL, (GAsyncReadyCallback)qrtr_bus_ready, task); } /*****************************************************************************/ MMQrtrBusWatcher * mm_qrtr_bus_watcher_new (void) { return MM_QRTR_BUS_WATCHER (g_object_new (MM_TYPE_QRTR_BUS_WATCHER, NULL)); } static void mm_qrtr_bus_watcher_init (MMQrtrBusWatcher *self) { /* Initialize opaque pointer to private data */ self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, MM_TYPE_QRTR_BUS_WATCHER, MMQrtrBusWatcherPrivate); /* Setup internal lists of device and node objects */ self->priv->nodes = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, (GDestroyNotify) g_object_unref); } static void finalize (GObject *object) { MMQrtrBusWatcher *self = MM_QRTR_BUS_WATCHER (object); g_hash_table_destroy (self->priv->nodes); if (self->priv->node_added_id) g_signal_handler_disconnect (self->priv->qrtr_bus, self->priv->node_added_id); if (self->priv->node_removed_id) g_signal_handler_disconnect (self->priv->qrtr_bus, self->priv->node_removed_id); g_clear_object (&self->priv->qrtr_bus); G_OBJECT_CLASS (mm_qrtr_bus_watcher_parent_class)->finalize (object); } static void log_object_iface_init (MMLogObjectInterface *iface) { iface->build_id = log_object_build_id; } static void mm_qrtr_bus_watcher_class_init (MMQrtrBusWatcherClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); g_type_class_add_private (object_class, sizeof (MMQrtrBusWatcherPrivate)); /* Virtual methods */ object_class->finalize = finalize; /** * QrtrBusWatcher::qrtr-device-added: * @self: the #QrtrBusWatcher * @node: the node ID of the modem that is added * * The ::qrtr-device-added signal is emitted when a new qrtr modem is * available on the QRTR bus. */ signals[QRTR_DEVICE_ADDED] = g_signal_new (MM_QRTR_BUS_WATCHER_DEVICE_ADDED, G_OBJECT_CLASS_TYPE (object_class), G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (MMQrtrBusWatcherClass, qrtr_device_added), NULL, /* accumulator */ NULL, /* accumulator data */ g_cclosure_marshal_generic, G_TYPE_NONE, 1, G_TYPE_UINT); /** * QrtrBusWatcher::qrtr-device-removed: * @self: the #QrtrBusWatcher * @node: the node ID of the modem that is removed * * The ::qrtr-device-removed signal is emitted when a qrtr modem deregisters * all services from the QRTR bus. */ signals[QRTR_DEVICE_REMOVED] = g_signal_new (MM_QRTR_BUS_WATCHER_DEVICE_REMOVED, G_OBJECT_CLASS_TYPE (object_class), G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (MMQrtrBusWatcherClass, qrtr_device_removed), NULL, /* accumulator */ NULL, /* accumulator data */ g_cclosure_marshal_generic, G_TYPE_NONE, 1, G_TYPE_UINT); } ModemManager-1.23.4-dev/src/mm-qrtr-bus-watcher.h000066400000000000000000000054201456466623000215070ustar00rootroot00000000000000/* -*- 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 2020 Google LLC */ #ifndef MM_QRTR_BUS_WATCHER_H #define MM_QRTR_BUS_WATCHER_H #include #include #include G_BEGIN_DECLS #define MM_TYPE_QRTR_BUS_WATCHER (mm_qrtr_bus_watcher_get_type ()) #define MM_QRTR_BUS_WATCHER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_QRTR_BUS_WATCHER, MMQrtrBusWatcher)) #define MM_QRTR_BUS_WATCHER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_QRTR_BUS_WATCHER, MMQrtrBusWatcherClass)) #define MM_IS_QRTR_BUS_WATCHER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_QRTR_BUS_WATCHER)) #define MM_IS_QRTR_BUS_WATCHER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_QRTR_BUS_WATCHER)) #define MM_QRTR_BUS_WATCHER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_QRTR_BUS_WATCHER, MMQrtrBusWatcherClass)) #define MM_QRTR_BUS_WATCHER_DEVICE_ADDED "qrtr-device-added" #define MM_QRTR_BUS_WATCHER_DEVICE_REMOVED "qrtr-device-removed" typedef struct _MMQrtrBusWatcher MMQrtrBusWatcher; typedef struct _MMQrtrBusWatcherClass MMQrtrBusWatcherClass; typedef struct _MMQrtrBusWatcherPrivate MMQrtrBusWatcherPrivate; struct _MMQrtrBusWatcher { GObject parent; MMQrtrBusWatcherPrivate *priv; }; struct _MMQrtrBusWatcherClass { GObjectClass parent; void (* qrtr_device_added) (MMQrtrBusWatcher *bus_watcher); void (* qrtr_device_removed) (MMQrtrBusWatcher *bus_watcher); }; GType mm_qrtr_bus_watcher_get_type (void); G_DEFINE_AUTOPTR_CLEANUP_FUNC (MMQrtrBusWatcher, g_object_unref) MMQrtrBusWatcher *mm_qrtr_bus_watcher_new (void); void mm_qrtr_bus_watcher_start (MMQrtrBusWatcher *self, GAsyncReadyCallback callback, gpointer user_data); gboolean mm_qrtr_bus_watcher_start_finish (MMQrtrBusWatcher *self, GAsyncResult *res, GError **error); QrtrNode *mm_qrtr_bus_watcher_peek_node (MMQrtrBusWatcher *self, guint32 node_id); G_END_DECLS #endif /* MM_QRTR_BUS_WATCHER_H */ ModemManager-1.23.4-dev/src/mm-serial-parsers.c000066400000000000000000000334461456466623000212350ustar00rootroot00000000000000/* -*- 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 #include #include "mm-error-helpers.h" #include "mm-serial-parsers.h" #include "mm-log-object.h" /* Clean up the response by removing control characters like etc */ static void response_clean (GString *response) { char *s; /* Ends with one or more '' */ s = response->str + response->len - 1; while ((s > response->str) && (*s == '\n') && (*(s - 1) == '\r')) { g_string_truncate (response, response->len - 2); s -= 2; } /* Contains duplicate '' */ s = response->str; while ((response->len >= 2) && (*s == '\r') && (*(s + 1) == '\r')) { g_string_erase (response, 0, 1); s = response->str; } /* Starts with one or more '' */ 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 { /* Regular expressions for successful replies */ GRegex *regex_ok; GRegex *regex_connect; GRegex *regex_sms; GRegex *regex_custom_successful; /* Regular expressions for error replies */ GRegex *regex_cme_error; GRegex *regex_cms_error; GRegex *regex_cme_error_str; GRegex *regex_cms_error_str; GRegex *regex_ezx_error; GRegex *regex_unknown_error; GRegex *regex_connect_failed; GRegex *regex_na; GRegex *regex_custom_error; /* User-provided parser filter */ mm_serial_parser_v1_filter_fn filter_callback; gpointer filter_user_data; } 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_sms = g_regex_new ("\\r\\n>\\s*$", flags, 0, NULL); parser->regex_cme_error = g_regex_new ("\\r\\n\\+CME ERROR:\\s*(\\d+)\\r\\n", flags, 0, NULL); parser->regex_cms_error = g_regex_new ("\\r\\n\\+CMS ERROR:\\s*(\\d+)\\r\\n", flags, 0, NULL); parser->regex_cme_error_str = g_regex_new ("\\r\\n\\+CME ERROR:\\s*([^\\n\\r]+)\\r\\n", flags, 0, NULL); parser->regex_cms_error_str = g_regex_new ("\\r\\n\\+CMS ERROR:\\s*([^\\n\\r]+)\\r\\n", flags, 0, NULL); parser->regex_ezx_error = g_regex_new ("\\r\\nMODEM ERROR:\\s*(\\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); /* Samsung Z810 may reply "NA" to report a not-available error */ parser->regex_na = g_regex_new ("\\r\\nNA\\r\\n", flags, 0, NULL); parser->regex_custom_successful = NULL; parser->regex_custom_error = NULL; parser->filter_callback = NULL; parser->filter_user_data = NULL; return parser; } void mm_serial_parser_v1_set_custom_regex (gpointer data, GRegex *successful, GRegex *error) { MMSerialParserV1 *parser = (MMSerialParserV1 *) data; g_return_if_fail (parser != NULL); if (parser->regex_custom_successful) g_regex_unref (parser->regex_custom_successful); if (parser->regex_custom_error) g_regex_unref (parser->regex_custom_error); parser->regex_custom_successful = successful ? g_regex_ref (successful) : NULL; parser->regex_custom_error = error ? g_regex_ref (error) : NULL; } void mm_serial_parser_v1_add_filter (gpointer data, mm_serial_parser_v1_filter_fn callback, gpointer user_data) { MMSerialParserV1 *parser = (MMSerialParserV1 *) data; g_return_if_fail (parser != NULL); parser->filter_callback = callback; parser->filter_user_data = user_data; } gboolean mm_serial_parser_v1_parse (gpointer data, GString *response, gpointer log_object, GError **error) { MMSerialParserV1 *parser = (MMSerialParserV1 *) data; GMatchInfo *match_info = NULL; GError *local_error = NULL; gboolean found = FALSE; char *str = NULL; g_return_val_if_fail (parser != NULL, FALSE); g_return_val_if_fail (response != NULL, FALSE); /* Skip NUL bytes if they are found leading the response */ while (response->len > 0 && response->str[0] == '\0') g_string_erase (response, 0, 1); if (G_UNLIKELY (!response->len)) return FALSE; /* First, apply custom filter if any */ if (parser->filter_callback && !parser->filter_callback (parser, parser->filter_user_data, response, &local_error)) { g_assert (local_error != NULL); mm_obj_dbg (log_object, "response filtered in serial port: %s", local_error->message); g_propagate_error (error, local_error); response_clean (response); return TRUE; } /* Then, check for successful responses */ /* Custom successful replies first, if any */ if (parser->regex_custom_successful) { found = g_regex_match_full (parser->regex_custom_successful, response->str, response->len, 0, 0, NULL, NULL); } if (!found) { found = g_regex_match_full (parser->regex_ok, response->str, response->len, 0, 0, NULL, NULL); if (found) remove_matches (parser->regex_ok, response); } if (!found) { found = g_regex_match_full (parser->regex_connect, response->str, response->len, 0, 0, NULL, NULL); } if (!found) { found = g_regex_match_full (parser->regex_sms, response->str, response->len, 0, 0, NULL, NULL); } if (found) { response_clean (response); return TRUE; } /* Now failures */ /* Custom error matches first, if any */ if (parser->regex_custom_error) { found = g_regex_match_full (parser->regex_custom_error, response->str, response->len, 0, 0, &match_info, NULL); if (found) { str = g_match_info_fetch (match_info, 1); g_assert (str); local_error = mm_mobile_equipment_error_for_code (atoi (str), log_object); goto done; } g_clear_pointer (&match_info, g_match_info_free); } /* Numeric CME errors */ found = g_regex_match_full (parser->regex_cme_error, response->str, response->len, 0, 0, &match_info, NULL); if (found) { str = g_match_info_fetch (match_info, 1); g_assert (str); local_error = mm_mobile_equipment_error_for_code (atoi (str), log_object); goto done; } g_clear_pointer (&match_info, g_match_info_free); /* Numeric CMS errors */ found = g_regex_match_full (parser->regex_cms_error, response->str, response->len, 0, 0, &match_info, NULL); if (found) { str = g_match_info_fetch (match_info, 1); g_assert (str); local_error = mm_message_error_for_code (atoi (str), log_object); goto done; } g_clear_pointer (&match_info, g_match_info_free); /* String CME errors */ found = g_regex_match_full (parser->regex_cme_error_str, response->str, response->len, 0, 0, &match_info, NULL); if (found) { str = g_match_info_fetch (match_info, 1); g_assert (str); local_error = mm_mobile_equipment_error_for_string (str, log_object); goto done; } g_clear_pointer (&match_info, g_match_info_free); /* String CMS errors */ found = g_regex_match_full (parser->regex_cms_error_str, response->str, response->len, 0, 0, &match_info, NULL); if (found) { str = g_match_info_fetch (match_info, 1); g_assert (str); local_error = mm_message_error_for_string (str, log_object); goto done; } g_clear_pointer (&match_info, g_match_info_free); /* Motorola EZX errors */ found = g_regex_match_full (parser->regex_ezx_error, response->str, response->len, 0, 0, &match_info, NULL); if (found) { str = g_match_info_fetch (match_info, 1); g_assert (str); local_error = mm_mobile_equipment_error_for_code (MM_MOBILE_EQUIPMENT_ERROR_UNKNOWN, log_object); goto done; } g_clear_pointer (&match_info, g_match_info_free); /* Last resort; unknown error */ found = g_regex_match_full (parser->regex_unknown_error, response->str, response->len, 0, 0, &match_info, NULL); if (found) { local_error = mm_mobile_equipment_error_for_code (MM_MOBILE_EQUIPMENT_ERROR_UNKNOWN, log_object); goto done; } g_clear_pointer (&match_info, g_match_info_free); /* Connection failures */ found = g_regex_match_full (parser->regex_connect_failed, response->str, response->len, 0, 0, &match_info, NULL); if (found) { MMConnectionError code; str = g_match_info_fetch (match_info, 1); g_assert (str); if (!strcmp (str, "NO CARRIER")) code = MM_CONNECTION_ERROR_NO_CARRIER; else if (!strcmp (str, "BUSY")) code = MM_CONNECTION_ERROR_BUSY; else if (!strcmp (str, "NO ANSWER")) code = MM_CONNECTION_ERROR_NO_ANSWER; else if (!strcmp (str, "NO DIALTONE")) code = MM_CONNECTION_ERROR_NO_DIALTONE; else { /* uhm... make something up (yes, ok, lie!). */ code = MM_CONNECTION_ERROR_NO_CARRIER; } local_error = mm_connection_error_for_code (code, log_object); goto done; } g_clear_pointer (&match_info, g_match_info_free); /* NA error */ found = g_regex_match_full (parser->regex_na, response->str, response->len, 0, 0, &match_info, NULL); if (found) { /* Assume NA means 'Not Allowed' :) */ local_error = g_error_new (MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_NOT_ALLOWED, "Not Allowed"); goto done; } done: g_free (str); g_clear_pointer (&match_info, g_match_info_free); if (found) response_clean (response); if (local_error) { mm_obj_dbg (log_object, "operation failure: %d (%s)", local_error->code, local_error->message); g_propagate_error (error, local_error); } return found; } gboolean mm_serial_parser_v1_is_known_error (const GError *error) { /* Need to return TRUE for the kind of errors that this parser may set */ return (error->domain == MM_MOBILE_EQUIPMENT_ERROR || error->domain == MM_CONNECTION_ERROR || error->domain == MM_MESSAGE_ERROR || g_error_matches (error, MM_SERIAL_ERROR, MM_SERIAL_ERROR_PARSE_FAILED)); } 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_sms); g_regex_unref (parser->regex_cme_error); g_regex_unref (parser->regex_cms_error); g_regex_unref (parser->regex_cme_error_str); g_regex_unref (parser->regex_cms_error_str); g_regex_unref (parser->regex_ezx_error); g_regex_unref (parser->regex_unknown_error); g_regex_unref (parser->regex_connect_failed); g_regex_unref (parser->regex_na); if (parser->regex_custom_successful) g_regex_unref (parser->regex_custom_successful); if (parser->regex_custom_error) g_regex_unref (parser->regex_custom_error); g_slice_free (MMSerialParserV1, data); } ModemManager-1.23.4-dev/src/mm-serial-parsers.h000066400000000000000000000037621456466623000212400ustar00rootroot00000000000000/* -*- 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 gpointer mm_serial_parser_v1_new (void); void mm_serial_parser_v1_set_custom_regex (gpointer data, GRegex *successful, GRegex *error); gboolean mm_serial_parser_v1_parse (gpointer parser, GString *response, gpointer log_object, GError **error); void mm_serial_parser_v1_destroy (gpointer parser); gboolean mm_serial_parser_v1_is_known_error (const GError *error); /* Parser filter: when FALSE returned, error should be set. This error will be * reported to the response listener right away. */ typedef gboolean (* mm_serial_parser_v1_filter_fn) (gpointer data, gpointer user_data, GString *response, GError **error); void mm_serial_parser_v1_add_filter (gpointer data, mm_serial_parser_v1_filter_fn callback, gpointer user_data); #endif /* MM_SERIAL_PARSERS_H */ ModemManager-1.23.4-dev/src/mm-shared-qmi.c000066400000000000000000010175521456466623000203340ustar00rootroot00000000000000/* -*- 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) 2018 Aleksander Morgado * Copyright (c) 2021-2022 Qualcomm Innovation Center, Inc. */ #include #include #include #include #include #define _LIBMM_INSIDE_MM #include #include #include "mm-log-object.h" #include "mm-iface-modem.h" #include "mm-iface-modem-3gpp.h" #include "mm-iface-modem-location.h" #include "mm-sim-qmi.h" #include "mm-shared-qmi.h" #include "mm-modem-helpers-qmi.h" /* Default session id to use in LOC operations */ #define DEFAULT_LOC_SESSION_ID 0x10 /* Default description for the default configuration of the firmware */ #define DEFAULT_CONFIG_DESCRIPTION "default" /*****************************************************************************/ /* Private data context */ #define PRIVATE_TAG "shared-qmi-private-tag" static GQuark private_quark; typedef enum { FEATURE_UNKNOWN, FEATURE_UNSUPPORTED, FEATURE_SUPPORTED, } Feature; typedef struct { GArray *id; QmiPdcConfigurationType config_type; guint32 token; guint32 version; gchar *description; guint32 total_size; } ConfigInfo; static void config_info_clear (ConfigInfo *config_info) { g_array_unref (config_info->id); g_free (config_info->description); } typedef struct { /* Capabilities & modes helpers */ gboolean multimode; MMModemCapability current_capabilities; GArray *supported_radio_interfaces; Feature feature_nas_tp; Feature feature_nas_ssp; Feature feature_nas_ssp_extended_lte_band_preference; Feature feature_nas_ssp_acquisition_order_preference; GArray *feature_nas_ssp_acquisition_order_preference_array; GArray *supported_bands; /* Location helpers */ MMIfaceModemLocation *iface_modem_location_parent; MMModemLocationSource enabled_sources; QmiClient *pds_client; gulong pds_location_event_report_indication_id; QmiClient *loc_client; gulong loc_location_nmea_indication_id; gchar **loc_assistance_data_servers; guint32 loc_assistance_data_max_file_size; guint32 loc_assistance_data_max_part_size; /* Carrier config helpers */ gboolean config_active_default; GArray *config_list; gint config_active_i; /* Slot status monitoring */ GArray *slots_status; QmiClient *uim_client; gulong uim_slot_status_indication_id; gulong uim_refresh_indication_id; guint uim_refresh_start_timeout_id; } Private; static void private_free (Private *priv) { if (priv->config_list) g_array_unref (priv->config_list); if (priv->supported_bands) g_array_unref (priv->supported_bands); if (priv->supported_radio_interfaces) g_array_unref (priv->supported_radio_interfaces); if (priv->pds_location_event_report_indication_id) g_signal_handler_disconnect (priv->pds_client, priv->pds_location_event_report_indication_id); if (priv->pds_client) g_object_unref (priv->pds_client); if (priv->loc_location_nmea_indication_id) g_signal_handler_disconnect (priv->loc_client, priv->loc_location_nmea_indication_id); if (priv->loc_client) g_object_unref (priv->loc_client); if (priv->uim_slot_status_indication_id) g_signal_handler_disconnect (priv->uim_client, priv->uim_slot_status_indication_id); if (priv->slots_status) g_array_unref (priv->slots_status); if (priv->uim_refresh_indication_id) g_signal_handler_disconnect (priv->uim_client, priv->uim_refresh_indication_id); if (priv->uim_client) g_object_unref (priv->uim_client); if (priv->uim_refresh_start_timeout_id) g_source_remove (priv->uim_refresh_start_timeout_id); if (priv->feature_nas_ssp_acquisition_order_preference_array) g_array_unref (priv->feature_nas_ssp_acquisition_order_preference_array); g_strfreev (priv->loc_assistance_data_servers); g_slice_free (Private, priv); } static Private * get_private (MMSharedQmi *self) { Private *priv; if (G_UNLIKELY (!private_quark)) private_quark = g_quark_from_static_string (PRIVATE_TAG); priv = g_object_get_qdata (G_OBJECT (self), private_quark); if (!priv) { priv = g_slice_new0 (Private); priv->feature_nas_tp = FEATURE_UNKNOWN; priv->feature_nas_ssp = FEATURE_UNKNOWN; priv->feature_nas_ssp_extended_lte_band_preference = FEATURE_UNKNOWN; priv->feature_nas_ssp_acquisition_order_preference = FEATURE_UNKNOWN; priv->config_active_i = -1; /* Setup parent class' MMIfaceModemLocation */ g_assert (MM_SHARED_QMI_GET_INTERFACE (self)->peek_parent_location_interface); priv->iface_modem_location_parent = MM_SHARED_QMI_GET_INTERFACE (self)->peek_parent_location_interface (self); g_object_set_qdata_full (G_OBJECT (self), private_quark, priv, (GDestroyNotify)private_free); } return priv; } /*****************************************************************************/ /* Register in network (3GPP interface) */ /* wait this amount of time at most if we don't get the serving system * indication earlier */ #define REGISTER_IN_NETWORK_TIMEOUT_SECS 25 typedef struct { guint timeout_id; gulong serving_system_indication_id; GCancellable *cancellable; gulong cancellable_id; QmiClientNas *client; } RegisterInNetworkContext; static void register_in_network_context_free (RegisterInNetworkContext *ctx) { g_assert (!ctx->cancellable_id); g_assert (!ctx->timeout_id); if (ctx->client) { g_assert (!ctx->serving_system_indication_id); g_object_unref (ctx->client); } g_clear_object (&ctx->cancellable); g_slice_free (RegisterInNetworkContext, ctx); } gboolean mm_shared_qmi_3gpp_register_in_network_finish (MMIfaceModem3gpp *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void register_in_network_cancelled (GCancellable *cancellable, GTask *task) { RegisterInNetworkContext *ctx; ctx = g_task_get_task_data (task); g_assert (ctx->cancellable); if (ctx->cancellable_id) ctx->cancellable_id = 0; g_assert (ctx->timeout_id); g_source_remove (ctx->timeout_id); ctx->timeout_id = 0; g_assert (ctx->client); g_assert (ctx->serving_system_indication_id); g_signal_handler_disconnect (ctx->client, ctx->serving_system_indication_id); ctx->serving_system_indication_id = 0; g_task_return_error_if_cancelled (task); g_object_unref (task); } static gboolean register_in_network_timeout (GTask *task) { RegisterInNetworkContext *ctx; ctx = g_task_get_task_data (task); g_assert (ctx->timeout_id); ctx->timeout_id = 0; g_assert (ctx->client); g_assert (ctx->serving_system_indication_id); g_signal_handler_disconnect (ctx->client, ctx->serving_system_indication_id); ctx->serving_system_indication_id = 0; if (ctx->cancellable && ctx->cancellable_id) { g_cancellable_disconnect (ctx->cancellable, ctx->cancellable_id); ctx->cancellable_id = 0; } /* the 3GPP interface will take care of checking if the registration is * the one we asked for */ g_task_return_boolean (task, TRUE); g_object_unref (task); return G_SOURCE_REMOVE; } static void register_in_network_ready (GTask *task, QmiIndicationNasServingSystemOutput *output) { RegisterInNetworkContext *ctx; QmiNasRegistrationState registration_state; /* ignore indication updates reporting "searching" */ qmi_indication_nas_serving_system_output_get_serving_system ( output, ®istration_state, NULL, /* cs_attach_state */ NULL, /* ps_attach_state */ NULL, /* selected_network */ NULL, /* radio_interfaces */ NULL); if (registration_state == QMI_NAS_REGISTRATION_STATE_NOT_REGISTERED_SEARCHING) return; ctx = g_task_get_task_data (task); g_assert (ctx->client); g_assert (ctx->serving_system_indication_id); g_signal_handler_disconnect (ctx->client, ctx->serving_system_indication_id); ctx->serving_system_indication_id = 0; g_assert (ctx->timeout_id); g_source_remove (ctx->timeout_id); ctx->timeout_id = 0; if (ctx->cancellable && ctx->cancellable_id) { g_cancellable_disconnect (ctx->cancellable, ctx->cancellable_id); ctx->cancellable_id = 0; } /* the 3GPP interface will take care of checking if the registration is * the one we asked for */ g_task_return_boolean (task, TRUE); g_object_unref (task); } static void initiate_network_register_ready (QmiClientNas *client, GAsyncResult *res, GTask *task) { GError *error = NULL; QmiMessageNasInitiateNetworkRegisterOutput *output; RegisterInNetworkContext *ctx; ctx = g_task_get_task_data (task); output = qmi_client_nas_initiate_network_register_finish (client, res, &error); if (!output || !qmi_message_nas_initiate_network_register_output_get_result (output, &error)) { /* No effect would mean we're already in the desired network */ if (g_error_matches (error, QMI_PROTOCOL_ERROR, QMI_PROTOCOL_ERROR_NO_EFFECT)) { g_task_return_boolean (task, TRUE); g_error_free (error); } else { g_prefix_error (&error, "Couldn't initiate network register: "); g_task_return_error (task, error); } g_object_unref (task); goto out; } /* Registration attempt started, now we need to monitor "serving system" indications * to get notified when the registration changed. Note that we won't need to process * the indication, because we already have that logic setup (and it runs before this * new signal handler), we just need to get notified of when it happens. We will also * setup a maximum operation timeuot plus a cancellability point, as this operation * may be explicitly cancelled by the 3GPP interface if a new registration request * arrives while the current one is being processed. * * Task is shared among cancellable, indication and timeout. The first one triggered * will cancel the others. */ ctx->serving_system_indication_id = g_signal_connect_swapped (client, "serving-system", G_CALLBACK (register_in_network_ready), task); ctx->timeout_id = g_timeout_add_seconds (REGISTER_IN_NETWORK_TIMEOUT_SECS, (GSourceFunc) register_in_network_timeout, task); /* The cancellable may already be cancelled, and if so the given callback will be called * right away. So make sure this cancellable is always configured last, so that it clears the * timeout or signal handler upon early cancellation. */ if (ctx->cancellable) ctx->cancellable_id = g_cancellable_connect (ctx->cancellable, G_CALLBACK (register_in_network_cancelled), task, NULL); out: if (output) qmi_message_nas_initiate_network_register_output_unref (output); } static void register_in_network_inr (GTask *task, QmiClient *client, GCancellable *cancellable, guint16 mcc, guint16 mnc, gboolean mnc_pcs_digit) { QmiMessageNasInitiateNetworkRegisterInput *input; input = qmi_message_nas_initiate_network_register_input_new (); if (mcc) { /* If the user sent a specific network to use, lock it in. */ qmi_message_nas_initiate_network_register_input_set_action ( input, QMI_NAS_NETWORK_REGISTER_TYPE_MANUAL, NULL); qmi_message_nas_initiate_network_register_input_set_manual_registration_info_3gpp ( input, mcc, mnc, QMI_NAS_RADIO_INTERFACE_UNKNOWN, /* don't change radio interface */ NULL); if (mnc_pcs_digit && mnc < 100) qmi_message_nas_initiate_network_register_input_set_mnc_pcs_digit_include_status ( input, mnc_pcs_digit, NULL ); } else { /* Otherwise, automatic registration */ qmi_message_nas_initiate_network_register_input_set_action ( input, QMI_NAS_NETWORK_REGISTER_TYPE_AUTOMATIC, NULL); } qmi_client_nas_initiate_network_register ( QMI_CLIENT_NAS (client), input, 120, cancellable, (GAsyncReadyCallback)initiate_network_register_ready, task); qmi_message_nas_initiate_network_register_input_unref (input); } static void set_system_selection_preference_ready (QmiClientNas *client, GAsyncResult *res, GTask *task) { GError *error = NULL; QmiMessageNasSetSystemSelectionPreferenceOutput *output; output = qmi_client_nas_set_system_selection_preference_finish (client, res, &error); if (!output || !qmi_message_nas_set_system_selection_preference_output_get_result (output, &error)) { if (!g_error_matches (error, QMI_PROTOCOL_ERROR, QMI_PROTOCOL_ERROR_NO_EFFECT)) { g_prefix_error (&error, "Couldn't set network selection preference: "); g_task_return_error (task, error); goto out; } g_error_free (error); } g_task_return_boolean (task, TRUE); out: g_object_unref (task); if (output) qmi_message_nas_set_system_selection_preference_output_unref (output); } static void register_in_network_sssp (GTask *task, QmiClient *client, GCancellable *cancellable, guint16 mcc, guint16 mnc, gboolean mnc_pcs_digit) { QmiMessageNasSetSystemSelectionPreferenceInput *input; input = qmi_message_nas_set_system_selection_preference_input_new (); qmi_message_nas_set_system_selection_preference_input_set_network_selection_preference ( input, mcc ? QMI_NAS_NETWORK_SELECTION_PREFERENCE_MANUAL : QMI_NAS_NETWORK_SELECTION_PREFERENCE_AUTOMATIC, mcc, mnc, NULL); if (mnc_pcs_digit && mnc < 100) qmi_message_nas_set_system_selection_preference_input_set_mnc_pcs_digit_include_status ( input, mnc_pcs_digit, NULL ); qmi_client_nas_set_system_selection_preference ( QMI_CLIENT_NAS (client), input, 120, cancellable, (GAsyncReadyCallback)set_system_selection_preference_ready, task); qmi_message_nas_set_system_selection_preference_input_unref (input); } void mm_shared_qmi_3gpp_register_in_network (MMIfaceModem3gpp *self, const gchar *operator_id, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; RegisterInNetworkContext *ctx; guint16 mcc = 0; guint16 mnc = 0; gboolean mnc_pcs_digit = FALSE; QmiClient *client = NULL; GError *error = NULL; Private *priv = NULL; /* Get NAS client */ if (!mm_shared_qmi_ensure_client (MM_SHARED_QMI (self), QMI_SERVICE_NAS, &client, callback, user_data)) return; task = g_task_new (self, cancellable, callback, user_data); ctx = g_slice_new0 (RegisterInNetworkContext); ctx->client = QMI_CLIENT_NAS (g_object_ref (client)); ctx->cancellable = cancellable ? g_object_ref (cancellable) : NULL; g_task_set_task_data (task, ctx, (GDestroyNotify)register_in_network_context_free); /* Parse input MCC/MNC */ if (operator_id && !mm_3gpp_parse_operator_id (operator_id, &mcc, &mnc, &mnc_pcs_digit, &error)) { g_assert (error != NULL); g_task_return_error (task, error); g_object_unref (task); return; } priv = get_private (MM_SHARED_QMI (self)); if (priv->feature_nas_ssp == FEATURE_SUPPORTED) register_in_network_sssp (task, client, cancellable, mcc, mnc, mnc_pcs_digit); else register_in_network_inr (task, client, cancellable, mcc, mnc, mnc_pcs_digit); } /*****************************************************************************/ /* Current capabilities setting (Modem interface) */ typedef enum { SET_CURRENT_CAPABILITIES_STEP_FIRST, SET_CURRENT_CAPABILITIES_STEP_NAS_SYSTEM_SELECTION_PREFERENCE, SET_CURRENT_CAPABILITIES_STEP_NAS_TECHNOLOGY_PREFERENCE, SET_CURRENT_CAPABILITIES_STEP_RESET, SET_CURRENT_CAPABILITIES_STEP_LAST, } SetCurrentCapabilitiesStep; typedef struct { QmiClientNas *client; MMModemCapability capabilities; gboolean capabilities_updated; SetCurrentCapabilitiesStep step; } SetCurrentCapabilitiesContext; static void set_current_capabilities_context_free (SetCurrentCapabilitiesContext *ctx) { g_object_unref (ctx->client); g_slice_free (SetCurrentCapabilitiesContext, ctx); } gboolean mm_shared_qmi_set_current_capabilities_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void set_current_capabilities_step (GTask *task); static void set_current_capabilities_reset_ready (MMIfaceModem *self, GAsyncResult *res, GTask *task) { SetCurrentCapabilitiesContext *ctx; GError *error = NULL; ctx = g_task_get_task_data (task); if (!mm_shared_qmi_reset_finish (self, res, &error)) { g_task_return_error (task, error); g_object_unref (task); return; } ctx->step++; set_current_capabilities_step (task); } static void set_current_capabilities_set_technology_preference_ready (QmiClientNas *client, GAsyncResult *res, GTask *task) { SetCurrentCapabilitiesContext *ctx; QmiMessageNasSetTechnologyPreferenceOutput *output = NULL; GError *error = NULL; ctx = g_task_get_task_data (task); output = qmi_client_nas_set_technology_preference_finish (client, res, &error); if (!output || !qmi_message_nas_set_technology_preference_output_get_result (output, &error)) { /* A no-effect error here is not a real error */ if (!g_error_matches (error, QMI_PROTOCOL_ERROR, QMI_PROTOCOL_ERROR_NO_EFFECT)) { g_task_return_error (task, error); g_object_unref (task); goto out; } /* no effect, just end operation without reset */ g_clear_error (&error); ctx->step = SET_CURRENT_CAPABILITIES_STEP_LAST; set_current_capabilities_step (task); goto out; } /* success! */ ctx->step = SET_CURRENT_CAPABILITIES_STEP_RESET; set_current_capabilities_step (task); out: if (output) qmi_message_nas_set_technology_preference_output_unref (output); } static void set_current_capabilities_technology_preference (GTask *task) { SetCurrentCapabilitiesContext *ctx; QmiMessageNasSetTechnologyPreferenceInput *input; QmiNasRadioTechnologyPreference pref; ctx = g_task_get_task_data (task); pref = mm_modem_capability_to_qmi_radio_technology_preference (ctx->capabilities); if (!pref) { gchar *str; str = mm_modem_capability_build_string_from_mask (ctx->capabilities); g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Unhandled capabilities setting: '%s'", str); g_object_unref (task); g_free (str); return; } input = qmi_message_nas_set_technology_preference_input_new (); qmi_message_nas_set_technology_preference_input_set_current (input, pref, QMI_NAS_PREFERENCE_DURATION_PERMANENT, NULL); qmi_client_nas_set_technology_preference ( ctx->client, input, 5, NULL, (GAsyncReadyCallback)set_current_capabilities_set_technology_preference_ready, task); qmi_message_nas_set_technology_preference_input_unref (input); } static void set_current_capabilities_set_system_selection_preference_ready (QmiClientNas *client, GAsyncResult *res, GTask *task) { SetCurrentCapabilitiesContext *ctx; QmiMessageNasSetSystemSelectionPreferenceOutput *output = NULL; GError *error = NULL; ctx = g_task_get_task_data (task); output = qmi_client_nas_set_system_selection_preference_finish (client, res, &error); if (!output || !qmi_message_nas_set_system_selection_preference_output_get_result (output, &error)) { g_task_return_error (task, error); g_object_unref (task); goto out; } /* success! */ ctx->step = SET_CURRENT_CAPABILITIES_STEP_RESET; set_current_capabilities_step (task); out: if (output) qmi_message_nas_set_system_selection_preference_output_unref (output); } static void set_current_capabilities_system_selection_preference (GTask *task) { SetCurrentCapabilitiesContext *ctx; QmiMessageNasSetSystemSelectionPreferenceInput *input; QmiNasRatModePreference pref; ctx = g_task_get_task_data (task); pref = mm_modem_capability_to_qmi_rat_mode_preference (ctx->capabilities); if (!pref) { gchar *str; str = mm_modem_capability_build_string_from_mask (ctx->capabilities); g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Unhandled capabilities setting: '%s'", str); g_object_unref (task); g_free (str); return; } input = qmi_message_nas_set_system_selection_preference_input_new (); qmi_message_nas_set_system_selection_preference_input_set_mode_preference (input, pref, NULL); qmi_message_nas_set_system_selection_preference_input_set_change_duration (input, QMI_NAS_CHANGE_DURATION_PERMANENT, NULL); qmi_client_nas_set_system_selection_preference ( ctx->client, input, 5, NULL, (GAsyncReadyCallback)set_current_capabilities_set_system_selection_preference_ready, task); qmi_message_nas_set_system_selection_preference_input_unref (input); } static void set_current_capabilities_step (GTask *task) { MMSharedQmi *self; Private *priv; SetCurrentCapabilitiesContext *ctx; self = g_task_get_source_object (task); priv = get_private (MM_SHARED_QMI (self)); ctx = g_task_get_task_data (task); switch (ctx->step) { case SET_CURRENT_CAPABILITIES_STEP_FIRST: /* Error out early if both unsupported */ if ((priv->feature_nas_ssp != FEATURE_SUPPORTED) && (priv->feature_nas_tp != FEATURE_SUPPORTED)) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "Setting capabilities is not supported by this device"); g_object_unref (task); return; } ctx->step++; /* fall-through */ case SET_CURRENT_CAPABILITIES_STEP_NAS_SYSTEM_SELECTION_PREFERENCE: if (priv->feature_nas_ssp == FEATURE_SUPPORTED) { set_current_capabilities_system_selection_preference (task); return; } ctx->step++; /* fall-through */ case SET_CURRENT_CAPABILITIES_STEP_NAS_TECHNOLOGY_PREFERENCE: if (priv->feature_nas_tp == FEATURE_SUPPORTED) { set_current_capabilities_technology_preference (task); return; } ctx->step++; /* fall-through */ case SET_CURRENT_CAPABILITIES_STEP_RESET: mm_shared_qmi_reset (MM_IFACE_MODEM (self), (GAsyncReadyCallback)set_current_capabilities_reset_ready, task); return; case SET_CURRENT_CAPABILITIES_STEP_LAST: g_task_return_boolean (task, TRUE); g_object_unref (task); return; default: g_assert_not_reached (); } } void mm_shared_qmi_set_current_capabilities (MMIfaceModem *self, MMModemCapability capabilities, GAsyncReadyCallback callback, gpointer user_data) { Private *priv; SetCurrentCapabilitiesContext *ctx; GTask *task; QmiClient *client = NULL; if (!mm_shared_qmi_ensure_client (MM_SHARED_QMI (self), QMI_SERVICE_NAS, &client, callback, user_data)) return; priv = get_private (MM_SHARED_QMI (self)); g_assert (priv->feature_nas_tp != FEATURE_UNKNOWN); g_assert (priv->feature_nas_ssp != FEATURE_UNKNOWN); ctx = g_slice_new0 (SetCurrentCapabilitiesContext); ctx->client = QMI_CLIENT_NAS (g_object_ref (client)); ctx->capabilities = capabilities; ctx->step = SET_CURRENT_CAPABILITIES_STEP_FIRST; task = g_task_new (self, NULL, callback, user_data); g_task_set_task_data (task, ctx, (GDestroyNotify)set_current_capabilities_context_free); set_current_capabilities_step (task); } /*****************************************************************************/ /* Current capabilities (Modem interface) */ typedef enum { LOAD_CURRENT_CAPABILITIES_STEP_FIRST, LOAD_CURRENT_CAPABILITIES_STEP_NAS_SYSTEM_SELECTION_PREFERENCE, LOAD_CURRENT_CAPABILITIES_STEP_NAS_TECHNOLOGY_PREFERENCE, LOAD_CURRENT_CAPABILITIES_STEP_DMS_GET_CAPABILITIES, LOAD_CURRENT_CAPABILITIES_STEP_LAST, } LoadCurrentCapabilitiesStep; typedef struct { QmiClientNas *nas_client; QmiClientDms *dms_client; LoadCurrentCapabilitiesStep step; MMQmiCurrentCapabilitiesContext capabilities_context; } LoadCurrentCapabilitiesContext; MMModemCapability mm_shared_qmi_load_current_capabilities_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { GError *inner_error = NULL; gssize value; value = g_task_propagate_int (G_TASK (res), &inner_error); if (inner_error) { g_propagate_error (error, inner_error); return MM_MODEM_CAPABILITY_NONE; } return (MMModemCapability)value; } static void load_current_capabilities_context_free (LoadCurrentCapabilitiesContext *ctx) { g_object_unref (ctx->nas_client); g_object_unref (ctx->dms_client); g_slice_free (LoadCurrentCapabilitiesContext, ctx); } static void load_current_capabilities_step (GTask *task); static void load_current_capabilities_get_capabilities_ready (QmiClientDms *client, GAsyncResult *res, GTask *task) { MMSharedQmi *self; Private *priv; LoadCurrentCapabilitiesContext *ctx; QmiMessageDmsGetCapabilitiesOutput *output = NULL; GError *error = NULL; guint i; GArray *radio_interface_list; self = g_task_get_source_object (task); priv = get_private (self); ctx = g_task_get_task_data (task); output = qmi_client_dms_get_capabilities_finish (client, res, &error); if (!output) { g_prefix_error (&error, "QMI operation failed: "); goto out; } if (!qmi_message_dms_get_capabilities_output_get_result (output, &error)) { g_prefix_error (&error, "Couldn't get Capabilities: "); goto out; } qmi_message_dms_get_capabilities_output_get_info ( output, NULL, /* info_max_tx_channel_rate */ NULL, /* info_max_rx_channel_rate */ NULL, /* info_data_service_capability */ NULL, /* info_sim_capability */ &radio_interface_list, NULL); /* Cache supported radio interfaces */ g_assert (!priv->supported_radio_interfaces); priv->supported_radio_interfaces = g_array_ref (radio_interface_list); for (i = 0; i < radio_interface_list->len; i++) ctx->capabilities_context.dms_capabilities |= mm_modem_capability_from_qmi_radio_interface (g_array_index (radio_interface_list, QmiDmsRadioInterface, i), self); out: if (output) qmi_message_dms_get_capabilities_output_unref (output); /* Failure in DMS Get Capabilities is fatal */ if (error) { g_task_return_error (task, error); g_object_unref (task); return; } ctx->step++; load_current_capabilities_step (task); } static void load_current_capabilities_get_technology_preference_ready (QmiClientNas *client, GAsyncResult *res, GTask *task) { MMSharedQmi *self; Private *priv; LoadCurrentCapabilitiesContext *ctx; QmiMessageNasGetTechnologyPreferenceOutput *output = NULL; GError *error = NULL; self = g_task_get_source_object (task); priv = get_private (MM_SHARED_QMI (self)); ctx = g_task_get_task_data (task); output = qmi_client_nas_get_technology_preference_finish (client, res, &error); if (!output) { mm_obj_dbg (self, "QMI operation failed: %s", error->message); g_error_free (error); priv->feature_nas_tp = FEATURE_UNSUPPORTED; } else if (!qmi_message_nas_get_technology_preference_output_get_result (output, &error)) { mm_obj_dbg (self, "couldn't get technology preference: %s", error->message); g_error_free (error); priv->feature_nas_tp = FEATURE_UNSUPPORTED; } else { qmi_message_nas_get_technology_preference_output_get_active ( output, &ctx->capabilities_context.nas_tp_mask, NULL, /* duration */ NULL); priv->feature_nas_tp = FEATURE_SUPPORTED; } if (output) qmi_message_nas_get_technology_preference_output_unref (output); ctx->step++; load_current_capabilities_step (task); } static void load_current_capabilities_get_system_selection_preference_ready (QmiClientNas *client, GAsyncResult *res, GTask *task) { MMSharedQmi *self; Private *priv; LoadCurrentCapabilitiesContext *ctx; QmiMessageNasGetSystemSelectionPreferenceOutput *output = NULL; GError *error = NULL; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); priv = get_private (MM_SHARED_QMI (self)); priv->feature_nas_ssp = FEATURE_UNSUPPORTED; priv->feature_nas_ssp_extended_lte_band_preference = FEATURE_UNSUPPORTED; priv->feature_nas_ssp_acquisition_order_preference = FEATURE_UNSUPPORTED; output = qmi_client_nas_get_system_selection_preference_finish (client, res, &error); if (!output) { mm_obj_dbg (self, "QMI operation failed: %s", error->message); g_error_free (error); } else if (!qmi_message_nas_get_system_selection_preference_output_get_result (output, &error)) { mm_obj_dbg (self, "couldn't get system selection preference: %s", error->message); g_error_free (error); } else { GArray *acquisition_order_preference_array = NULL; /* SSP is supported, perform feature checks */ priv->feature_nas_ssp = FEATURE_SUPPORTED; if (qmi_message_nas_get_system_selection_preference_output_get_extended_lte_band_preference (output, NULL, NULL, NULL, NULL, NULL)) priv->feature_nas_ssp_extended_lte_band_preference = FEATURE_SUPPORTED; if (qmi_message_nas_get_system_selection_preference_output_get_acquisition_order_preference (output, &acquisition_order_preference_array, NULL) && acquisition_order_preference_array && acquisition_order_preference_array->len) { priv->feature_nas_ssp_acquisition_order_preference = FEATURE_SUPPORTED; priv->feature_nas_ssp_acquisition_order_preference_array = g_array_ref (acquisition_order_preference_array); } qmi_message_nas_get_system_selection_preference_output_get_mode_preference ( output, &ctx->capabilities_context.nas_ssp_mode_preference_mask, NULL); } if (output) qmi_message_nas_get_system_selection_preference_output_unref (output); ctx->step++; load_current_capabilities_step (task); } static void load_current_capabilities_step (GTask *task) { MMSharedQmi *self; Private *priv; LoadCurrentCapabilitiesContext *ctx; self = g_task_get_source_object (task); priv = get_private (MM_SHARED_QMI (self)); ctx = g_task_get_task_data (task); switch (ctx->step) { case LOAD_CURRENT_CAPABILITIES_STEP_FIRST: ctx->step++; /* fall-through */ case LOAD_CURRENT_CAPABILITIES_STEP_NAS_SYSTEM_SELECTION_PREFERENCE: qmi_client_nas_get_system_selection_preference ( ctx->nas_client, NULL, 5, NULL, (GAsyncReadyCallback)load_current_capabilities_get_system_selection_preference_ready, task); return; case LOAD_CURRENT_CAPABILITIES_STEP_NAS_TECHNOLOGY_PREFERENCE: qmi_client_nas_get_technology_preference ( ctx->nas_client, NULL, 5, NULL, (GAsyncReadyCallback)load_current_capabilities_get_technology_preference_ready, task); return; case LOAD_CURRENT_CAPABILITIES_STEP_DMS_GET_CAPABILITIES: qmi_client_dms_get_capabilities ( ctx->dms_client, NULL, 5, NULL, (GAsyncReadyCallback)load_current_capabilities_get_capabilities_ready, task); return; case LOAD_CURRENT_CAPABILITIES_STEP_LAST: g_assert (priv->feature_nas_tp != FEATURE_UNKNOWN); g_assert (priv->feature_nas_ssp != FEATURE_UNKNOWN); /* At this point we can already know if this is a multimode device or not */ if ((ctx->capabilities_context.dms_capabilities & MM_MODEM_CAPABILITY_MULTIMODE) == MM_MODEM_CAPABILITY_MULTIMODE) priv->multimode = ctx->capabilities_context.multimode = TRUE; priv->current_capabilities = mm_current_capability_from_qmi_current_capabilities_context (&ctx->capabilities_context, self); if (priv->current_capabilities == MM_MODEM_CAPABILITY_NONE) g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "modem has no current capabilities"); else g_task_return_int (task, priv->current_capabilities); g_object_unref (task); return; default: g_assert_not_reached (); } } void mm_shared_qmi_load_current_capabilities (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data) { LoadCurrentCapabilitiesContext *ctx; GTask *task; QmiClient *nas_client = NULL; QmiClient *dms_client = NULL; Private *priv; /* * We assume that DMS Get Capabilities reports always the same result, * that will include all capabilities supported by the device regardless * of which ones are configured at the moment. E.g. for the Load Supported * Capabilities we base the logic exclusively on this method's output. * * We then consider 3 different cases: * a) If the device supports NAS System Selection Preference, we use the * "mode preference" TLV to select currently enabled capabilities. * b) If the device supports NAS Technology Preference (older devices), * we use this method to select currently enabled capabilities. * c) If none of those messages is supported we don't allow swiching * capabilities. */ if (!mm_shared_qmi_ensure_client (MM_SHARED_QMI (self), QMI_SERVICE_NAS, &nas_client, callback, user_data)) return; if (!mm_shared_qmi_ensure_client (MM_SHARED_QMI (self), QMI_SERVICE_DMS, &dms_client, callback, user_data)) return; /* Current capabilities is the first thing run, and will only be run once per modem, * so we should here check support for the optional features. */ priv = get_private (MM_SHARED_QMI (self)); g_assert (priv->feature_nas_tp == FEATURE_UNKNOWN); g_assert (priv->feature_nas_ssp == FEATURE_UNKNOWN); ctx = g_slice_new0 (LoadCurrentCapabilitiesContext); ctx->nas_client = QMI_CLIENT_NAS (g_object_ref (nas_client)); ctx->dms_client = QMI_CLIENT_DMS (g_object_ref (dms_client)); ctx->step = LOAD_CURRENT_CAPABILITIES_STEP_FIRST; task = g_task_new (self, NULL, callback, user_data); g_task_set_task_data (task, ctx, (GDestroyNotify)load_current_capabilities_context_free); load_current_capabilities_step (task); } /*****************************************************************************/ /* Supported capabilities (Modem interface) */ GArray * mm_shared_qmi_load_supported_capabilities_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { return g_task_propagate_pointer (G_TASK (res), error); } void mm_shared_qmi_load_supported_capabilities (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; Private *priv; GArray *supported_combinations; guint i; MMQmiSupportedCapabilitiesContext ctx = { 0 }; task = g_task_new (self, NULL, callback, user_data); /* List of radio interfaces preloaded in current capabilities */ priv = get_private (MM_SHARED_QMI (self)); if (!priv->supported_radio_interfaces) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "cannot load current capabilities without radio interface information"); g_object_unref (task); return; } /* Build mask with all supported capabilities */ ctx.dms_capabilities = MM_MODEM_CAPABILITY_NONE; for (i = 0; i < priv->supported_radio_interfaces->len; i++) ctx.dms_capabilities |= mm_modem_capability_from_qmi_radio_interface (g_array_index (priv->supported_radio_interfaces, QmiDmsRadioInterface, i), self); ctx.nas_tp_supported = (priv->feature_nas_tp == FEATURE_SUPPORTED); ctx.nas_ssp_supported = (priv->feature_nas_ssp == FEATURE_SUPPORTED); ctx.multimode = priv->multimode; /* Build list of supported combinations */ supported_combinations = mm_supported_capabilities_from_qmi_supported_capabilities_context (&ctx, self); g_task_return_pointer (task, supported_combinations, (GDestroyNotify) g_array_unref); g_object_unref (task); } /*****************************************************************************/ /* Load model (Modem interface) */ gchar * mm_shared_qmi_load_model_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { return g_task_propagate_pointer (G_TASK (res), error); } static void dms_get_model_ready (QmiClientDms *client, GAsyncResult *res, GTask *task) { QmiMessageDmsGetModelOutput *output = NULL; GError *error = NULL; output = qmi_client_dms_get_model_finish (client, res, &error); if (!output) { g_prefix_error (&error, "QMI operation failed: "); g_task_return_error (task, error); } else if (!qmi_message_dms_get_model_output_get_result (output, &error)) { g_prefix_error (&error, "Couldn't get Model: "); g_task_return_error (task, error); } else { const gchar *str; qmi_message_dms_get_model_output_get_model (output, &str, NULL); g_task_return_pointer (task, g_strdup (str), g_free); } if (output) qmi_message_dms_get_model_output_unref (output); g_object_unref (task); } void mm_shared_qmi_load_model (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data) { QmiClient *client = NULL; if (!mm_shared_qmi_ensure_client (MM_SHARED_QMI (self), QMI_SERVICE_DMS, &client, callback, user_data)) return; mm_obj_dbg (self, "loading model..."); qmi_client_dms_get_model (QMI_CLIENT_DMS (client), NULL, 5, NULL, (GAsyncReadyCallback)dms_get_model_ready, g_task_new (self, NULL, callback, user_data)); } /*****************************************************************************/ /* Allowed modes setting (Modem interface) */ typedef struct { QmiClientNas *client; MMModemMode allowed; MMModemMode preferred; } SetCurrentModesContext; static void set_current_modes_context_free (SetCurrentModesContext *ctx) { g_object_unref (ctx->client); g_slice_free (SetCurrentModesContext, ctx); } gboolean mm_shared_qmi_set_current_modes_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void set_current_modes_technology_preference_ready (QmiClientNas *client, GAsyncResult *res, GTask *task) { QmiMessageNasSetTechnologyPreferenceOutput *output = NULL; GError *error = NULL; output = qmi_client_nas_set_technology_preference_finish (client, res, &error); if (!output || (!qmi_message_nas_set_technology_preference_output_get_result (output, &error) && !g_error_matches (error, QMI_PROTOCOL_ERROR, QMI_PROTOCOL_ERROR_NO_EFFECT))) { g_task_return_error (task, error); } else { g_clear_error (&error); g_task_return_boolean (task, TRUE); } g_object_unref (task); if (output) qmi_message_nas_set_technology_preference_output_unref (output); } static void set_current_modes_technology_preference (GTask *task) { MMIfaceModem *self; SetCurrentModesContext *ctx; QmiMessageNasSetTechnologyPreferenceInput *input; QmiNasRadioTechnologyPreference pref; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); if (ctx->preferred != MM_MODEM_MODE_NONE) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Cannot set specific preferred mode"); g_object_unref (task); return; } pref = mm_modem_mode_to_qmi_radio_technology_preference (ctx->allowed, mm_iface_modem_is_cdma (self)); if (!pref) { gchar *str; str = mm_modem_mode_build_string_from_mask (ctx->allowed); g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Unhandled allowed mode setting: '%s'", str); g_object_unref (task); g_free (str); return; } input = qmi_message_nas_set_technology_preference_input_new (); qmi_message_nas_set_technology_preference_input_set_current (input, pref, QMI_NAS_PREFERENCE_DURATION_PERMANENT, NULL); qmi_client_nas_set_technology_preference ( ctx->client, input, 5, NULL, /* cancellable */ (GAsyncReadyCallback)set_current_modes_technology_preference_ready, task); qmi_message_nas_set_technology_preference_input_unref (input); } static void set_current_modes_system_selection_preference_ready (QmiClientNas *client, GAsyncResult *res, GTask *task) { QmiMessageNasSetSystemSelectionPreferenceOutput *output = NULL; GError *error = NULL; output = qmi_client_nas_set_system_selection_preference_finish (client, res, &error); if (!output || !qmi_message_nas_set_system_selection_preference_output_get_result (output, &error)) g_task_return_error (task, error); else g_task_return_boolean (task, TRUE); g_object_unref (task); if (output) qmi_message_nas_set_system_selection_preference_output_unref (output); } static void set_current_modes_system_selection_preference (GTask *task) { MMIfaceModem *self; Private *priv; SetCurrentModesContext *ctx; QmiMessageNasSetSystemSelectionPreferenceInput *input; QmiNasRatModePreference pref; self = g_task_get_source_object (task); priv = get_private (MM_SHARED_QMI (self)); ctx = g_task_get_task_data (task); input = qmi_message_nas_set_system_selection_preference_input_new (); qmi_message_nas_set_system_selection_preference_input_set_change_duration (input, QMI_NAS_CHANGE_DURATION_PERMANENT, NULL); /* Preferred modes */ if (ctx->preferred != MM_MODEM_MODE_NONE) { if (priv->feature_nas_ssp_acquisition_order_preference == FEATURE_SUPPORTED) { GArray *array; /* Acquisition order array */ array = mm_modem_mode_to_qmi_acquisition_order_preference (ctx->allowed, ctx->preferred, priv->feature_nas_ssp_acquisition_order_preference_array); g_assert (array); qmi_message_nas_set_system_selection_preference_input_set_acquisition_order_preference (input, array, NULL); g_array_unref (array); } /* Only set GSM/WCDMA acquisition order preference if both 2G and 3G given as allowed */ if (mm_iface_modem_is_3gpp (self) && ((ctx->allowed & (MM_MODEM_MODE_2G | MM_MODEM_MODE_3G)) == (MM_MODEM_MODE_2G | MM_MODEM_MODE_3G))) { QmiNasGsmWcdmaAcquisitionOrderPreference order; order = mm_modem_mode_to_qmi_gsm_wcdma_acquisition_order_preference (ctx->preferred, self); qmi_message_nas_set_system_selection_preference_input_set_gsm_wcdma_acquisition_order_preference (input, order, NULL); } } /* Allowed modes */ pref = mm_modem_mode_to_qmi_rat_mode_preference (ctx->allowed, mm_iface_modem_is_cdma (self), mm_iface_modem_is_3gpp (self)); qmi_message_nas_set_system_selection_preference_input_set_mode_preference (input, pref, NULL); qmi_client_nas_set_system_selection_preference ( ctx->client, input, 5, NULL, /* cancellable */ (GAsyncReadyCallback)set_current_modes_system_selection_preference_ready, task); qmi_message_nas_set_system_selection_preference_input_unref (input); } void mm_shared_qmi_set_current_modes (MMIfaceModem *self, MMModemMode allowed, MMModemMode preferred, GAsyncReadyCallback callback, gpointer user_data) { SetCurrentModesContext *ctx; GTask *task; QmiClient *client = NULL; Private *priv; if (!mm_shared_qmi_ensure_client (MM_SHARED_QMI (self), QMI_SERVICE_NAS, &client, callback, user_data)) return; ctx = g_slice_new0 (SetCurrentModesContext); ctx->client = QMI_CLIENT_NAS (g_object_ref (client)); if (allowed == MM_MODEM_MODE_ANY && ctx->preferred == MM_MODEM_MODE_NONE) { ctx->allowed = MM_MODEM_MODE_NONE; if (mm_iface_modem_is_2g (self)) ctx->allowed |= MM_MODEM_MODE_2G; if (mm_iface_modem_is_3g (self)) ctx->allowed |= MM_MODEM_MODE_3G; if (mm_iface_modem_is_4g (self)) ctx->allowed |= MM_MODEM_MODE_4G; if (mm_iface_modem_is_5g (self)) ctx->allowed |= MM_MODEM_MODE_5G; ctx->preferred = MM_MODEM_MODE_NONE; } else { ctx->allowed = allowed; ctx->preferred = preferred; } task = g_task_new (self, NULL, callback, user_data); g_task_set_task_data (task, ctx, (GDestroyNotify)set_current_modes_context_free); priv = get_private (MM_SHARED_QMI (self)); if (priv->feature_nas_ssp == FEATURE_SUPPORTED) { set_current_modes_system_selection_preference (task); return; } if (priv->feature_nas_tp == FEATURE_SUPPORTED) { set_current_modes_technology_preference (task); return; } g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "Setting allowed modes is not supported by this device"); g_object_unref (task); } /*****************************************************************************/ /* Load current modes (Modem interface) */ typedef struct { QmiClientNas *client; } LoadCurrentModesContext; typedef struct { MMModemMode allowed; MMModemMode preferred; } LoadCurrentModesResult; static void load_current_modes_context_free (LoadCurrentModesContext *ctx) { g_object_unref (ctx->client); g_free (ctx); } gboolean mm_shared_qmi_load_current_modes_finish (MMIfaceModem *self, GAsyncResult *res, MMModemMode *allowed, MMModemMode *preferred, GError **error) { LoadCurrentModesResult *result; result = g_task_propagate_pointer (G_TASK (res), error); if (!result) return FALSE; *allowed = result->allowed; *preferred = result->preferred; g_free (result); return TRUE; } static MMModemMode filter_modes_by_supported_radio_interfaces (MMSharedQmi *self, GArray *supported_radio_interfaces, MMModemMode allowed_modes) { MMModemMode modem_modes; guint i; modem_modes = MM_MODEM_MODE_NONE; for (i = 0; i < supported_radio_interfaces->len; i++) modem_modes |= mm_modem_mode_from_qmi_radio_interface (g_array_index (supported_radio_interfaces, QmiDmsRadioInterface, i), self); return allowed_modes & modem_modes; } static void get_technology_preference_ready (QmiClientNas *client, GAsyncResult *res, GTask *task) { MMSharedQmi *self; Private *priv; LoadCurrentModesResult *result = NULL; QmiMessageNasGetTechnologyPreferenceOutput *output = NULL; GError *error = NULL; MMModemMode allowed; QmiNasRadioTechnologyPreference preference_mask; self = g_task_get_source_object (task); priv = get_private (self); output = qmi_client_nas_get_technology_preference_finish (client, res, &error); if (!output || !qmi_message_nas_get_technology_preference_output_get_result (output, &error)) { g_task_return_error (task, error); goto out; } qmi_message_nas_get_technology_preference_output_get_active ( output, &preference_mask, NULL, /* duration */ NULL); allowed = mm_modem_mode_from_qmi_radio_technology_preference (preference_mask); if (allowed == MM_MODEM_MODE_NONE) { gchar *str; str = qmi_nas_radio_technology_preference_build_string_from_mask (preference_mask); g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Unsupported modes reported: '%s'", str); g_free (str); goto out; } g_assert (priv->supported_radio_interfaces); allowed = filter_modes_by_supported_radio_interfaces (self, priv->supported_radio_interfaces, allowed); /* We got a valid value from here */ result = g_new (LoadCurrentModesResult, 1); result->allowed = allowed; result->preferred = MM_MODEM_MODE_NONE; g_task_return_pointer (task, result, g_free); out: if (output) qmi_message_nas_get_technology_preference_output_unref (output); g_object_unref (task); } static void load_current_modes_technology_preference (GTask *task) { LoadCurrentModesContext *ctx; ctx = g_task_get_task_data (task); qmi_client_nas_get_technology_preference ( ctx->client, NULL, /* no input */ 5, NULL, /* cancellable */ (GAsyncReadyCallback)get_technology_preference_ready, task); } static void load_current_modes_system_selection_preference_ready (QmiClientNas *client, GAsyncResult *res, GTask *task) { MMSharedQmi *self; Private *priv; LoadCurrentModesResult *result = NULL; QmiMessageNasGetSystemSelectionPreferenceOutput *output = NULL; GError *error = NULL; QmiNasRatModePreference mode_preference_mask = 0; MMModemMode allowed; self = g_task_get_source_object (task); priv = get_private (self); output = qmi_client_nas_get_system_selection_preference_finish (client, res, &error); if (!output || !qmi_message_nas_get_system_selection_preference_output_get_result (output, &error)) { g_task_return_error (task, error); goto out; } if (!qmi_message_nas_get_system_selection_preference_output_get_mode_preference ( output, &mode_preference_mask, NULL)) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Mode preference not reported in system selection preference"); goto out; } allowed = mm_modem_mode_from_qmi_rat_mode_preference (mode_preference_mask); if (allowed == MM_MODEM_MODE_NONE) { gchar *str; str = qmi_nas_rat_mode_preference_build_string_from_mask (mode_preference_mask); g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Unsupported modes reported: '%s'", str); g_free (str); goto out; } g_assert (priv->supported_radio_interfaces); allowed = filter_modes_by_supported_radio_interfaces (self, priv->supported_radio_interfaces, allowed); /* We got a valid value from here */ result = g_new (LoadCurrentModesResult, 1); result->allowed = allowed; result->preferred = MM_MODEM_MODE_NONE; /* If acquisition order preference is available, always use that first */ if (priv->feature_nas_ssp_acquisition_order_preference == FEATURE_SUPPORTED) { GArray *array; if (qmi_message_nas_get_system_selection_preference_output_get_acquisition_order_preference (output, &array, NULL) && array->len > 0) { guint i; /* The array of preference contains the preference of the full list of supported * access technologies, regardless of whether they're enabled or not. So, look for * the first one that is flagged as enabled, not just the first one in the array. */ for (i = 0; i < array->len; i++) { MMModemMode mode; mode = mm_modem_mode_from_qmi_nas_radio_interface (g_array_index (array, QmiNasRadioInterface, i)); if (allowed == mode) break; if (allowed & mode) { result->preferred = mode; break; } } } } /* For 2G+3G only rely on the GSM/WCDMA acquisition order preference TLV */ else if (mode_preference_mask == (QMI_NAS_RAT_MODE_PREFERENCE_GSM | QMI_NAS_RAT_MODE_PREFERENCE_UMTS)) { QmiNasGsmWcdmaAcquisitionOrderPreference gsm_or_wcdma; if (qmi_message_nas_get_system_selection_preference_output_get_gsm_wcdma_acquisition_order_preference ( output, &gsm_or_wcdma, NULL)) result->preferred = mm_modem_mode_from_qmi_gsm_wcdma_acquisition_order_preference (gsm_or_wcdma, self); } g_task_return_pointer (task, result, g_free); out: if (output) qmi_message_nas_get_system_selection_preference_output_unref (output); g_object_unref (task); } static void load_current_modes_system_selection_preference (GTask *task) { LoadCurrentModesContext *ctx; ctx = g_task_get_task_data (task); qmi_client_nas_get_system_selection_preference ( ctx->client, NULL, /* no input */ 5, NULL, /* cancellable */ (GAsyncReadyCallback)load_current_modes_system_selection_preference_ready, task); } void mm_shared_qmi_load_current_modes (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data) { Private *priv; LoadCurrentModesContext *ctx; GTask *task; QmiClient *client = NULL; if (!mm_shared_qmi_ensure_client (MM_SHARED_QMI (self), QMI_SERVICE_NAS, &client, callback, user_data)) return; ctx = g_new0 (LoadCurrentModesContext, 1); ctx->client = QMI_CLIENT_NAS (g_object_ref (client)); task = g_task_new (self, NULL, callback, user_data); g_task_set_task_data (task, ctx, (GDestroyNotify)load_current_modes_context_free); priv = get_private (MM_SHARED_QMI (self)); if (priv->feature_nas_ssp != FEATURE_UNSUPPORTED) { load_current_modes_system_selection_preference (task); return; } if (priv->feature_nas_tp != FEATURE_UNSUPPORTED) { load_current_modes_technology_preference (task); return; } /* Default to supported */ g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "Loading current modes is not supported by this device"); g_object_unref (task); } /*****************************************************************************/ /* Supported modes (Modem interface) */ GArray * mm_shared_qmi_load_supported_modes_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { return g_task_propagate_pointer (G_TASK (res), error); } void mm_shared_qmi_load_supported_modes (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; Private *priv; MMQmiSupportedModesContext ctx = { 0 }; guint i; GArray *combinations; task = g_task_new (self, NULL, callback, user_data); priv = get_private (MM_SHARED_QMI (self)); g_assert (priv->supported_radio_interfaces); g_assert (priv->current_capabilities != MM_MODEM_CAPABILITY_NONE); /* Build all, based on the supported radio interfaces */ ctx.all = MM_MODEM_MODE_NONE; for (i = 0; i < priv->supported_radio_interfaces->len; i++) ctx.all |= mm_modem_mode_from_qmi_radio_interface (g_array_index (priv->supported_radio_interfaces, QmiDmsRadioInterface, i), self); /* Filter out those unsupported by the current capabilities */ if (!(priv->current_capabilities & (MM_MODEM_CAPABILITY_GSM_UMTS | MM_MODEM_CAPABILITY_CDMA_EVDO))) ctx.all &= ~(MM_MODEM_MODE_2G | MM_MODEM_MODE_3G); else if (!(priv->current_capabilities & MM_MODEM_CAPABILITY_LTE)) ctx.all &= ~MM_MODEM_MODE_4G; else if (!(priv->current_capabilities & MM_MODEM_CAPABILITY_5GNR)) ctx.all &= ~MM_MODEM_MODE_5G; ctx.nas_ssp_supported = (priv->feature_nas_ssp == FEATURE_SUPPORTED); ctx.nas_tp_supported = (priv->feature_nas_tp == FEATURE_SUPPORTED); ctx.current_capabilities = priv->current_capabilities; ctx.multimode = priv->multimode; combinations = mm_supported_modes_from_qmi_supported_modes_context (&ctx, self); g_task_return_pointer (task, combinations, (GDestroyNotify) g_array_unref); g_object_unref (task); } /*****************************************************************************/ /* Load supported bands (Modem interface) */ GArray * mm_shared_qmi_load_supported_bands_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { return (GArray *) g_task_propagate_pointer (G_TASK (res), error); } static void dms_get_band_capabilities_ready (QmiClientDms *client, GAsyncResult *res, GTask *task) { MMSharedQmi *self; Private *priv; QmiMessageDmsGetBandCapabilitiesOutput *output; GError *error = NULL; GArray *mm_bands = NULL; QmiDmsBandCapability qmi_bands = 0; QmiDmsLteBandCapability qmi_lte_bands = 0; GArray *extended_qmi_lte_bands = NULL; GArray *qmi_nr5g_bands = NULL; self = g_task_get_source_object (task); priv = get_private (self); output = qmi_client_dms_get_band_capabilities_finish (client, res, &error); if (!output || !qmi_message_dms_get_band_capabilities_output_get_result (output, &error)) { g_prefix_error (&error, "Couldn't get band capabilities: "); goto out; } qmi_message_dms_get_band_capabilities_output_get_band_capability ( output, &qmi_bands, NULL); qmi_message_dms_get_band_capabilities_output_get_lte_band_capability ( output, &qmi_lte_bands, NULL); qmi_message_dms_get_band_capabilities_output_get_extended_lte_band_capability ( output, &extended_qmi_lte_bands, NULL); qmi_message_dms_get_band_capabilities_output_get_nr5g_band_capability ( output, &qmi_nr5g_bands, NULL); mm_bands = mm_modem_bands_from_qmi_band_capabilities (qmi_bands, qmi_lte_bands, extended_qmi_lte_bands, qmi_nr5g_bands, self); if (mm_bands->len == 0) { g_clear_pointer (&mm_bands, g_array_unref); error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't parse the list of supported bands"); goto out; } /* Cache the result */ g_clear_pointer (&priv->supported_bands, g_array_unref); priv->supported_bands = g_array_ref (mm_bands); out: if (output) qmi_message_dms_get_band_capabilities_output_unref (output); if (error) g_task_return_error (task, error); else if (mm_bands) g_task_return_pointer (task, mm_bands, (GDestroyNotify)g_array_unref); else g_assert_not_reached (); g_object_unref (task); } void mm_shared_qmi_load_supported_bands (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; QmiClient *client = NULL; if (!mm_shared_qmi_ensure_client (MM_SHARED_QMI (self), QMI_SERVICE_DMS, &client, callback, user_data)) return; task = g_task_new (self, NULL, callback, user_data); qmi_client_dms_get_band_capabilities (QMI_CLIENT_DMS (client), NULL, 5, NULL, (GAsyncReadyCallback)dms_get_band_capabilities_ready, task); } /*****************************************************************************/ /* Load current bands (Modem interface) */ GArray * mm_shared_qmi_load_current_bands_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { return (GArray *) g_task_propagate_pointer (G_TASK (res), error); } static void load_bands_get_system_selection_preference_ready (QmiClientNas *client, GAsyncResult *res, GTask *task) { MMSharedQmi *self; Private *priv; QmiMessageNasGetSystemSelectionPreferenceOutput *output = NULL; GError *error = NULL; GArray *mm_bands = NULL; QmiNasBandPreference band_preference_mask = 0; QmiNasLteBandPreference lte_band_preference_mask = 0; guint64 extended_lte_band_preference[4] = { 0 }; guint extended_lte_band_preference_size = 0; guint64 nr5g_sa_band_preference[8] = { 0 }; guint64 nr5g_nsa_band_preference[8] = { 0 }; guint64 nr5g_band_preference[8] = { 0 }; guint nr5g_band_preference_size = 0; self = g_task_get_source_object (task); priv = get_private (self); output = qmi_client_nas_get_system_selection_preference_finish (client, res, &error); if (!output || !qmi_message_nas_get_system_selection_preference_output_get_result (output, &error)) { g_prefix_error (&error, "Couldn't get system selection preference: "); goto out; } qmi_message_nas_get_system_selection_preference_output_get_band_preference ( output, &band_preference_mask, NULL); qmi_message_nas_get_system_selection_preference_output_get_lte_band_preference ( output, <e_band_preference_mask, NULL); if ((priv->feature_nas_ssp_extended_lte_band_preference == FEATURE_SUPPORTED) && qmi_message_nas_get_system_selection_preference_output_get_extended_lte_band_preference ( output, &extended_lte_band_preference[0], &extended_lte_band_preference[1], &extended_lte_band_preference[2], &extended_lte_band_preference[3], NULL)) extended_lte_band_preference_size = G_N_ELEMENTS (extended_lte_band_preference); if (qmi_message_nas_get_system_selection_preference_output_get_nr5g_sa_band_preference ( output, &nr5g_sa_band_preference[0], &nr5g_sa_band_preference[1], &nr5g_sa_band_preference[2], &nr5g_sa_band_preference[3], &nr5g_sa_band_preference[4], &nr5g_sa_band_preference[5], &nr5g_sa_band_preference[6], &nr5g_sa_band_preference[7], NULL) || qmi_message_nas_get_system_selection_preference_output_get_nr5g_nsa_band_preference ( output, &nr5g_nsa_band_preference[0], &nr5g_nsa_band_preference[1], &nr5g_nsa_band_preference[2], &nr5g_nsa_band_preference[3], &nr5g_nsa_band_preference[4], &nr5g_nsa_band_preference[5], &nr5g_nsa_band_preference[6], &nr5g_nsa_band_preference[7], NULL)) { guint i; nr5g_band_preference_size = G_N_ELEMENTS (nr5g_band_preference); for (i = 0; i < nr5g_band_preference_size; i++) nr5g_band_preference[i] = nr5g_sa_band_preference[i] | nr5g_nsa_band_preference[i]; } mm_bands = mm_modem_bands_from_qmi_band_preference (band_preference_mask, lte_band_preference_mask, extended_lte_band_preference_size ? extended_lte_band_preference : NULL, extended_lte_band_preference_size, nr5g_band_preference_size ? nr5g_band_preference : NULL, nr5g_band_preference_size, self); if (mm_bands->len == 0) { g_clear_pointer (&mm_bands, g_array_unref); error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't parse the list of current bands"); } out: if (output) qmi_message_nas_get_system_selection_preference_output_unref (output); if (error) g_task_return_error (task, error); else if (mm_bands) g_task_return_pointer (task, mm_bands, (GDestroyNotify)g_array_unref); else g_assert_not_reached (); g_object_unref (task); } void mm_shared_qmi_load_current_bands (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; QmiClient *client = NULL; if (!mm_shared_qmi_ensure_client (MM_SHARED_QMI (self), QMI_SERVICE_NAS, &client, callback, user_data)) return; task = g_task_new (self, NULL, callback, user_data); qmi_client_nas_get_system_selection_preference ( QMI_CLIENT_NAS (client), NULL, /* no input */ 5, NULL, /* cancellable */ (GAsyncReadyCallback)load_bands_get_system_selection_preference_ready, task); } /*****************************************************************************/ /* Set current bands (Modem interface) */ gboolean mm_shared_qmi_set_current_bands_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void bands_set_system_selection_preference_ready (QmiClientNas *client, GAsyncResult *res, GTask *task) { QmiMessageNasSetSystemSelectionPreferenceOutput *output = NULL; GError *error = NULL; output = qmi_client_nas_set_system_selection_preference_finish (client, res, &error); if (!output || !qmi_message_nas_set_system_selection_preference_output_get_result (output, &error)) { g_prefix_error (&error, "Couldn't set system selection preference: "); g_task_return_error (task, error); } else g_task_return_boolean (task, TRUE); if (output) qmi_message_nas_set_system_selection_preference_output_unref (output); g_object_unref (task); } void mm_shared_qmi_set_current_bands (MMIfaceModem *self, GArray *bands_array, GAsyncReadyCallback callback, gpointer user_data) { QmiMessageNasSetSystemSelectionPreferenceInput *input; Private *priv; GTask *task; QmiClient *client = NULL; QmiNasBandPreference qmi_bands = 0; QmiNasLteBandPreference qmi_lte_bands = 0; guint64 extended_qmi_lte_bands[4] = { 0 }; guint64 qmi_nr5g_bands[8] = { 0 }; if (!mm_shared_qmi_ensure_client (MM_SHARED_QMI (self), QMI_SERVICE_NAS, &client, callback, user_data)) return; task = g_task_new (self, NULL, callback, user_data); priv = get_private (MM_SHARED_QMI (self)); /* Handle ANY separately */ if (bands_array->len == 1 && g_array_index (bands_array, MMModemBand, 0) == MM_MODEM_BAND_ANY) { if (!priv->supported_bands) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Cannot handle 'ANY' if supported bands are unknown"); g_object_unref (task); return; } bands_array = priv->supported_bands; } mm_modem_bands_to_qmi_band_preference (bands_array, &qmi_bands, &qmi_lte_bands, priv->feature_nas_ssp_extended_lte_band_preference == FEATURE_SUPPORTED ? extended_qmi_lte_bands : NULL, G_N_ELEMENTS (extended_qmi_lte_bands), qmi_nr5g_bands, G_N_ELEMENTS (qmi_nr5g_bands), self); input = qmi_message_nas_set_system_selection_preference_input_new (); qmi_message_nas_set_system_selection_preference_input_set_band_preference (input, qmi_bands, NULL); if (mm_iface_modem_is_3gpp_lte (self)) { if (priv->feature_nas_ssp_extended_lte_band_preference == FEATURE_SUPPORTED) qmi_message_nas_set_system_selection_preference_input_set_extended_lte_band_preference ( input, extended_qmi_lte_bands[0], extended_qmi_lte_bands[1], extended_qmi_lte_bands[2], extended_qmi_lte_bands[3], NULL); else qmi_message_nas_set_system_selection_preference_input_set_lte_band_preference (input, qmi_lte_bands, NULL); } qmi_message_nas_set_system_selection_preference_input_set_nr5g_sa_band_preference ( input, qmi_nr5g_bands[0], qmi_nr5g_bands[1], qmi_nr5g_bands[2], qmi_nr5g_bands[3], qmi_nr5g_bands[4], qmi_nr5g_bands[5], qmi_nr5g_bands[6], qmi_nr5g_bands[7], NULL); qmi_message_nas_set_system_selection_preference_input_set_nr5g_nsa_band_preference ( input, qmi_nr5g_bands[0], qmi_nr5g_bands[1], qmi_nr5g_bands[2], qmi_nr5g_bands[3], qmi_nr5g_bands[4], qmi_nr5g_bands[5], qmi_nr5g_bands[6], qmi_nr5g_bands[7], NULL); qmi_message_nas_set_system_selection_preference_input_set_change_duration (input, QMI_NAS_CHANGE_DURATION_PERMANENT, NULL); qmi_client_nas_set_system_selection_preference ( QMI_CLIENT_NAS (client), input, 5, NULL, /* cancellable */ (GAsyncReadyCallback)bands_set_system_selection_preference_ready, task); qmi_message_nas_set_system_selection_preference_input_unref (input); } /*****************************************************************************/ /* Reset (Modem interface) */ gboolean mm_shared_qmi_reset_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void reset_set_operating_mode_reset_ready (QmiClientDms *client, GAsyncResult *res, GTask *task) { MMSharedQmi *self; QmiMessageDmsSetOperatingModeOutput *output; GError *error = NULL; self = g_task_get_source_object (task); output = qmi_client_dms_set_operating_mode_finish (client, res, &error); if (!output || !qmi_message_dms_set_operating_mode_output_get_result (output, &error)) { g_task_return_error (task, error); } else { mm_obj_msg (self, "rebooting now"); g_task_return_boolean (task, TRUE); } if (output) qmi_message_dms_set_operating_mode_output_unref (output); g_object_unref (task); } static void reset_set_operating_mode_offline_ready (QmiClientDms *client, GAsyncResult *res, GTask *task) { QmiMessageDmsSetOperatingModeInput *input; QmiMessageDmsSetOperatingModeOutput *output; GError *error = NULL; output = qmi_client_dms_set_operating_mode_finish (client, res, &error); if (!output) { g_task_return_error (task, error); g_object_unref (task); return; } if (!qmi_message_dms_set_operating_mode_output_get_result (output, &error)) { g_task_return_error (task, error); g_object_unref (task); qmi_message_dms_set_operating_mode_output_unref (output); return; } qmi_message_dms_set_operating_mode_output_unref (output); /* Now, go into reset mode. This will fully reboot the modem, and the current * modem object should get disposed. */ input = qmi_message_dms_set_operating_mode_input_new (); qmi_message_dms_set_operating_mode_input_set_mode (input, QMI_DMS_OPERATING_MODE_RESET, NULL); qmi_client_dms_set_operating_mode (client, input, 20, NULL, (GAsyncReadyCallback)reset_set_operating_mode_reset_ready, task); qmi_message_dms_set_operating_mode_input_unref (input); } void mm_shared_qmi_reset (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data) { QmiMessageDmsSetOperatingModeInput *input; GTask *task; QmiClient *client; if (!mm_shared_qmi_ensure_client (MM_SHARED_QMI (self), QMI_SERVICE_DMS, &client, callback, user_data)) return; task = g_task_new (self, NULL, callback, user_data); /* Now, go into offline mode */ input = qmi_message_dms_set_operating_mode_input_new (); qmi_message_dms_set_operating_mode_input_set_mode (input, QMI_DMS_OPERATING_MODE_OFFLINE, NULL); qmi_client_dms_set_operating_mode (QMI_CLIENT_DMS (client), input, 20, NULL, (GAsyncReadyCallback)reset_set_operating_mode_offline_ready, task); qmi_message_dms_set_operating_mode_input_unref (input); } /*****************************************************************************/ /* Factory reset (Modem interface) */ gboolean mm_shared_qmi_factory_reset_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void dms_restore_factory_defaults_ready (QmiClientDms *client, GAsyncResult *res, GTask *task) { QmiMessageDmsRestoreFactoryDefaultsOutput *output = NULL; GError *error = NULL; output = qmi_client_dms_restore_factory_defaults_finish (client, res, &error); if (!output) { g_prefix_error (&error, "QMI operation failed: "); g_task_return_error (task, error); } else if (!qmi_message_dms_restore_factory_defaults_output_get_result (output, &error)) { g_prefix_error (&error, "Couldn't restore factory defaults: "); g_task_return_error (task, error); } else g_task_return_boolean (task, TRUE); if (output) qmi_message_dms_restore_factory_defaults_output_unref (output); g_object_unref (task); } void mm_shared_qmi_factory_reset (MMIfaceModem *self, const gchar *code, GAsyncReadyCallback callback, gpointer user_data) { QmiMessageDmsRestoreFactoryDefaultsInput *input; GTask *task; QmiClient *client = NULL; GError *error = NULL; if (!mm_shared_qmi_ensure_client (MM_SHARED_QMI (self), QMI_SERVICE_DMS, &client, callback, user_data)) return; task = g_task_new (self, NULL, callback, user_data); input = qmi_message_dms_restore_factory_defaults_input_new (); if (!qmi_message_dms_restore_factory_defaults_input_set_service_programming_code ( input, code, &error)) { qmi_message_dms_restore_factory_defaults_input_unref (input); g_task_return_error (task, error); g_object_unref (task); return; } mm_obj_dbg (self, "performing a factory reset..."); qmi_client_dms_restore_factory_defaults (QMI_CLIENT_DMS (client), input, 10, NULL, (GAsyncReadyCallback)dms_restore_factory_defaults_ready, task); } /*****************************************************************************/ /* Setup carrier config (Modem interface) */ #define SETUP_CARRIER_CONFIG_STEP_TIMEOUT_SECS 10 #define GENERIC_CONFIG_FALLBACK "generic" typedef enum { SETUP_CARRIER_CONFIG_STEP_FIRST, SETUP_CARRIER_CONFIG_STEP_FIND_REQUESTED, SETUP_CARRIER_CONFIG_STEP_CHECK_CHANGE_NEEDED, SETUP_CARRIER_CONFIG_STEP_UPDATE_CURRENT, SETUP_CARRIER_CONFIG_STEP_ACTIVATE_CURRENT, SETUP_CARRIER_CONFIG_STEP_LAST, } SetupCarrierConfigStep; typedef struct { SetupCarrierConfigStep step; QmiClientPdc *client; GKeyFile *keyfile; gchar *imsi; gint config_requested_i; gchar *config_requested; guint token; guint timeout_id; gulong set_selected_config_indication_id; gulong activate_config_indication_id; } SetupCarrierConfigContext; /* Allow to cleanup action setup right away, without being tied * to the lifecycle of the GTask */ static void setup_carrier_config_context_cleanup_action (SetupCarrierConfigContext *ctx) { if (ctx->activate_config_indication_id) { g_signal_handler_disconnect (ctx->client, ctx->activate_config_indication_id); ctx->activate_config_indication_id = 0; } if (ctx->set_selected_config_indication_id) { g_signal_handler_disconnect (ctx->client, ctx->set_selected_config_indication_id); ctx->set_selected_config_indication_id = 0; } if (ctx->timeout_id) { g_source_remove (ctx->timeout_id); ctx->timeout_id = 0; } } static void setup_carrier_config_context_free (SetupCarrierConfigContext *ctx) { setup_carrier_config_context_cleanup_action (ctx); g_free (ctx->config_requested); g_free (ctx->imsi); g_key_file_unref (ctx->keyfile); g_clear_object (&ctx->client); g_slice_free (SetupCarrierConfigContext, ctx); } gboolean mm_shared_qmi_setup_carrier_config_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void setup_carrier_config_step (GTask *task); static void setup_carrier_config_abort (GTask *task, GError *error) { SetupCarrierConfigContext *ctx; ctx = g_task_get_task_data (task); setup_carrier_config_context_cleanup_action (ctx); g_task_return_error (task, error); g_object_unref (task); } static gboolean setup_carrier_config_timeout_no_error (GTask *task) { SetupCarrierConfigContext *ctx; ctx = g_task_get_task_data (task); g_assert (ctx->timeout_id); ctx->timeout_id = 0; setup_carrier_config_context_cleanup_action (ctx); ctx->step++; setup_carrier_config_step (task); return G_SOURCE_REMOVE; } static gboolean setup_carrier_config_timeout (GTask *task) { SetupCarrierConfigContext *ctx; ctx = g_task_get_task_data (task); g_assert (ctx->timeout_id); ctx->timeout_id = 0; setup_carrier_config_abort (task, g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_ABORTED, "Operation timed out")); return G_SOURCE_REMOVE; } static void activate_config_indication (QmiClientPdc *client, QmiIndicationPdcActivateConfigOutput *output, GTask *task) { SetupCarrierConfigContext *ctx; GError *error = NULL; guint16 error_code = 0; ctx = g_task_get_task_data (task); if (!qmi_indication_pdc_activate_config_output_get_indication_result (output, &error_code, &error)) { setup_carrier_config_abort (task, error); return; } if (error_code != 0) { setup_carrier_config_abort (task, g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "couldn't activate config: %s", qmi_protocol_error_get_string ((QmiProtocolError) error_code))); return; } /* Go on */ setup_carrier_config_context_cleanup_action (ctx); ctx->step++; setup_carrier_config_step (task); } static void activate_config_ready (QmiClientPdc *client, GAsyncResult *res, GTask *task) { QmiMessagePdcActivateConfigOutput *output; SetupCarrierConfigContext *ctx; GError *error = NULL; ctx = g_task_get_task_data (task); output = qmi_client_pdc_activate_config_finish (client, res, &error); if (!output || !qmi_message_pdc_activate_config_output_get_result (output, &error)) { setup_carrier_config_abort (task, error); goto out; } /* When we activate the config, if the operation is successful, we'll just * see the modem going away completely. So, do not consider an error the timeout * waiting for the Activate Config indication, as that is actually a good * thing. */ ctx->timeout_id = g_timeout_add_seconds (SETUP_CARRIER_CONFIG_STEP_TIMEOUT_SECS, (GSourceFunc) setup_carrier_config_timeout_no_error, task); ctx->activate_config_indication_id = g_signal_connect (ctx->client, "activate-config", G_CALLBACK (activate_config_indication), task); out: if (output) qmi_message_pdc_activate_config_output_unref (output); } static void set_selected_config_indication (QmiClientPdc *client, QmiIndicationPdcSetSelectedConfigOutput *output, GTask *task) { SetupCarrierConfigContext *ctx; GError *error = NULL; guint16 error_code = 0; ctx = g_task_get_task_data (task); if (!qmi_indication_pdc_set_selected_config_output_get_indication_result (output, &error_code, &error)) { setup_carrier_config_abort (task, error); return; } if (error_code != 0) { setup_carrier_config_abort (task, g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "couldn't set selected config: %s", qmi_protocol_error_get_string ((QmiProtocolError) error_code))); return; } /* Go on */ setup_carrier_config_context_cleanup_action (ctx); ctx->step++; setup_carrier_config_step (task); } static void set_selected_config_ready (QmiClientPdc *client, GAsyncResult *res, GTask *task) { QmiMessagePdcSetSelectedConfigOutput *output; SetupCarrierConfigContext *ctx; GError *error = NULL; ctx = g_task_get_task_data (task); output = qmi_client_pdc_set_selected_config_finish (client, res, &error); if (!output || !qmi_message_pdc_set_selected_config_output_get_result (output, &error)) { setup_carrier_config_abort (task, error); goto out; } ctx->timeout_id = g_timeout_add_seconds (SETUP_CARRIER_CONFIG_STEP_TIMEOUT_SECS, (GSourceFunc) setup_carrier_config_timeout, task); ctx->set_selected_config_indication_id = g_signal_connect (ctx->client, "set-selected-config", G_CALLBACK (set_selected_config_indication), task); out: if (output) qmi_message_pdc_set_selected_config_output_unref (output); } static gint select_newest_carrier_config (MMSharedQmi *self, gint config_a_i, gint config_b_i) { Private *priv; ConfigInfo *config_a; ConfigInfo *config_b; priv = get_private (self); config_a = &g_array_index (priv->config_list, ConfigInfo, config_a_i); config_b = &g_array_index (priv->config_list, ConfigInfo, config_b_i); g_assert (!g_strcmp0 (config_a->description, config_b->description)); if (config_a->version > config_b->version) return config_a_i; if (config_b->version > config_a->version) return config_b_i; /* if both are equal, return the first one found always */ return config_a_i; } static void find_requested_carrier_config (GTask *task) { SetupCarrierConfigContext *ctx; MMSharedQmi *self; Private *priv; gchar mccmnc[7]; gchar *group; gint config_fallback_i = -1; gchar *config_fallback = NULL; ctx = g_task_get_task_data (task); self = MM_SHARED_QMI (g_task_get_source_object (task)); priv = get_private (self); /* Only one group expected per file, so get the start one */ group = g_key_file_get_start_group (ctx->keyfile); /* Match generic configuration */ config_fallback = g_key_file_get_string (ctx->keyfile, group, GENERIC_CONFIG_FALLBACK, NULL); mm_obj_dbg (self, "fallback carrier configuration %sfound in group '%s'", config_fallback ? "" : "not ", group); /* First, try to match 6 MCCMNC digits (3-digit MNCs) */ strncpy (mccmnc, ctx->imsi, 6); mccmnc[6] = '\0'; ctx->config_requested = g_key_file_get_string (ctx->keyfile, group, mccmnc, NULL); if (!ctx->config_requested) { /* If not found, try to match 5 MCCMNC digits (2-digit MNCs) */ mccmnc[5] = '\0'; ctx->config_requested = g_key_file_get_string (ctx->keyfile, group, mccmnc, NULL); } mm_obj_dbg (self, "requested carrier configuration %sfound for '%s' in group '%s': %s", ctx->config_requested ? "" : "not ", mccmnc, group, ctx->config_requested ? ctx->config_requested : "n/a"); if (!ctx->config_requested && !config_fallback) { setup_carrier_config_abort (task, g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_NOT_FOUND, "no valid configuration found in group '%s'", group)); goto out; } /* Now, look for the configurations among the ones available in the device */ if (priv->config_list) { guint i; for (i = 0; i < priv->config_list->len; i++) { ConfigInfo *config; config = &g_array_index (priv->config_list, ConfigInfo, i); if (ctx->config_requested && !g_strcmp0 (ctx->config_requested, config->description)) { mm_obj_dbg (self, "requested carrier configuration '%s' is available (version 0x%08x, size %u bytes)", config->description, config->version, config->total_size); if (ctx->config_requested_i < 0) ctx->config_requested_i = i; else ctx->config_requested_i = select_newest_carrier_config (self, ctx->config_requested_i, i); } if (config_fallback && !g_strcmp0 (config_fallback, config->description)) { mm_obj_dbg (self, "fallback carrier configuration '%s' is available (version 0x%08x, size %u bytes)", config->description, config->version, config->total_size); if (config_fallback_i < 0) config_fallback_i = i; else config_fallback_i = select_newest_carrier_config (self, config_fallback_i, i); } } } /* Fail operation if we didn't find the one we want */ if ((ctx->config_requested_i < 0) && (config_fallback_i < 0)) { setup_carrier_config_abort (task, g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "carrier configurations (requested '%s', fallback '%s') are not available", ctx->config_requested, config_fallback)); goto out; } /* If the mapping expects a given config, but the config isn't installed, * we fallback to generic */ if (ctx->config_requested_i < 0) { ConfigInfo *config; g_assert (config_fallback_i >= 0); config = &g_array_index (priv->config_list, ConfigInfo, config_fallback_i); mm_obj_dbg (self, "using fallback carrier configuration '%s' (version 0x%08x, size %u bytes)", config->description, config->version, config->total_size); g_free (ctx->config_requested); ctx->config_requested = config_fallback; ctx->config_requested_i = config_fallback_i; config_fallback = NULL; } else { ConfigInfo *config; config = &g_array_index (priv->config_list, ConfigInfo, ctx->config_requested_i); mm_obj_dbg (self, "using requested carrier configuration '%s' (version 0x%08x, size %u bytes)", config->description, config->version, config->total_size); } ctx->step++; setup_carrier_config_step (task); out: g_free (config_fallback); g_free (group); } static void setup_carrier_config_step (GTask *task) { MMSharedQmi *self; SetupCarrierConfigContext *ctx; Private *priv; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); priv = get_private (self); switch (ctx->step) { case SETUP_CARRIER_CONFIG_STEP_FIRST: ctx->step++; /* fall-through */ case SETUP_CARRIER_CONFIG_STEP_FIND_REQUESTED: find_requested_carrier_config (task); return; case SETUP_CARRIER_CONFIG_STEP_CHECK_CHANGE_NEEDED: g_assert (ctx->config_requested_i >= 0); g_assert (priv->config_active_i >= 0 || priv->config_active_default); if (ctx->config_requested_i == priv->config_active_i) { mm_obj_msg (self, "carrier config switching not needed: already using '%s'", ctx->config_requested); ctx->step = SETUP_CARRIER_CONFIG_STEP_LAST; setup_carrier_config_step (task); return; } ctx->step++; /* fall-through */ case SETUP_CARRIER_CONFIG_STEP_UPDATE_CURRENT: { g_autoptr(QmiMessagePdcSetSelectedConfigInput) input = NULL; ConfigInfo *requested_config; ConfigInfo *active_config; requested_config = &g_array_index (priv->config_list, ConfigInfo, ctx->config_requested_i); active_config = (priv->config_active_default ? NULL : &g_array_index (priv->config_list, ConfigInfo, priv->config_active_i)); mm_obj_warn (self, "carrier config switching needed: '%s' -> '%s'", active_config ? active_config->description : DEFAULT_CONFIG_DESCRIPTION, requested_config->description); input = qmi_message_pdc_set_selected_config_input_new (); qmi_message_pdc_set_selected_config_input_set_type_with_id_v2 (input, requested_config->config_type, requested_config->id, NULL); qmi_message_pdc_set_selected_config_input_set_token (input, ctx->token++, NULL); qmi_client_pdc_set_selected_config (ctx->client, input, 10, NULL, (GAsyncReadyCallback)set_selected_config_ready, task); return; } case SETUP_CARRIER_CONFIG_STEP_ACTIVATE_CURRENT: { g_autoptr(QmiMessagePdcActivateConfigInput) input = NULL; ConfigInfo *requested_config; requested_config = &g_array_index (priv->config_list, ConfigInfo, ctx->config_requested_i); input = qmi_message_pdc_activate_config_input_new (); qmi_message_pdc_activate_config_input_set_config_type (input, requested_config->config_type, NULL); qmi_message_pdc_activate_config_input_set_token (input, ctx->token++, NULL); qmi_client_pdc_activate_config (ctx->client, input, 10, NULL, (GAsyncReadyCallback) activate_config_ready, task); return; } case SETUP_CARRIER_CONFIG_STEP_LAST: g_task_return_boolean (task, TRUE); g_object_unref (task); break; default: g_assert_not_reached (); } } void mm_shared_qmi_setup_carrier_config (MMIfaceModem *self, const gchar *imsi, const gchar *carrier_config_mapping, GAsyncReadyCallback callback, gpointer user_data) { SetupCarrierConfigContext *ctx; GTask *task; QmiClient *client = NULL; GError *error = NULL; g_assert (imsi); g_assert (carrier_config_mapping); task = g_task_new (self, NULL, callback, user_data); ctx = g_slice_new0 (SetupCarrierConfigContext); ctx->step = SETUP_CARRIER_CONFIG_STEP_FIRST; ctx->imsi = g_strdup (imsi); ctx->keyfile = g_key_file_new (); ctx->config_requested_i = -1; g_task_set_task_data (task, ctx, (GDestroyNotify)setup_carrier_config_context_free); /* Load mapping keyfile */ if (!g_key_file_load_from_file (ctx->keyfile, carrier_config_mapping, G_KEY_FILE_NONE, &error)) { g_task_return_error (task, error); g_object_unref (task); return; } /* Load PDC client */ client = mm_shared_qmi_peek_client (MM_SHARED_QMI (self), QMI_SERVICE_PDC, MM_PORT_QMI_FLAG_DEFAULT, NULL); if (!client) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "QMI PDC not supported"); g_object_unref (task); return; } ctx->client = QMI_CLIENT_PDC (g_object_ref (client)); setup_carrier_config_step (task); } /*****************************************************************************/ /* Load carrier config (Modem interface) */ #define LOAD_CARRIER_CONFIG_STEP_TIMEOUT_SECS 5 typedef enum { LOAD_CARRIER_CONFIG_STEP_FIRST, LOAD_CARRIER_CONFIG_STEP_LIST_CONFIGS, LOAD_CARRIER_CONFIG_STEP_QUERY_CURRENT, LOAD_CARRIER_CONFIG_STEP_LAST, } LoadCarrierConfigStep; typedef struct { LoadCarrierConfigStep step; QmiClientPdc *client; GArray *config_list; guint configs_loaded; gboolean config_active_default; gint config_active_i; guint token; guint timeout_id; gulong list_configs_indication_id; gulong get_selected_config_indication_id; gulong get_config_info_indication_id; } LoadCarrierConfigContext; /* Allow to cleanup action load right away, without being tied * to the lifecycle of the GTask */ static void load_carrier_config_context_cleanup_action (LoadCarrierConfigContext *ctx) { if (ctx->get_selected_config_indication_id) { g_signal_handler_disconnect (ctx->client, ctx->get_selected_config_indication_id); ctx->get_selected_config_indication_id = 0; } if (ctx->get_config_info_indication_id) { g_signal_handler_disconnect (ctx->client, ctx->get_config_info_indication_id); ctx->get_config_info_indication_id = 0; } if (ctx->list_configs_indication_id) { g_signal_handler_disconnect (ctx->client, ctx->list_configs_indication_id); ctx->list_configs_indication_id = 0; } if (ctx->timeout_id) { g_source_remove (ctx->timeout_id); ctx->timeout_id = 0; } } static void load_carrier_config_context_free (LoadCarrierConfigContext *ctx) { load_carrier_config_context_cleanup_action (ctx); if (ctx->config_list) g_array_unref (ctx->config_list); g_clear_object (&ctx->client); g_slice_free (LoadCarrierConfigContext, ctx); } gboolean mm_shared_qmi_load_carrier_config_finish (MMIfaceModem *self, GAsyncResult *res, gchar **carrier_config_name, gchar **carrier_config_revision, GError **error) { Private *priv; if (!g_task_propagate_boolean (G_TASK (res), error)) return FALSE; priv = get_private (MM_SHARED_QMI (self)); g_assert (priv->config_active_i >= 0 || priv->config_active_default); if (priv->config_active_i >= 0) { ConfigInfo *config; config = &g_array_index (priv->config_list, ConfigInfo, priv->config_active_i); *carrier_config_name = g_strdup (config->description); *carrier_config_revision = g_strdup_printf ("%08X", config->version); } else if (priv->config_active_default) { *carrier_config_name = g_strdup (DEFAULT_CONFIG_DESCRIPTION); *carrier_config_revision = NULL; } else g_assert_not_reached (); return TRUE; } static void load_carrier_config_step (GTask *task); static void load_carrier_config_abort (GTask *task, GError *error) { LoadCarrierConfigContext *ctx; ctx = g_task_get_task_data (task); load_carrier_config_context_cleanup_action (ctx); g_task_return_error (task, error); g_object_unref (task); } static gboolean load_carrier_config_timeout (GTask *task) { LoadCarrierConfigContext *ctx; ctx = g_task_get_task_data (task); g_assert (ctx->timeout_id); ctx->timeout_id = 0; load_carrier_config_abort (task, g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_ABORTED, "Operation timed out")); return G_SOURCE_REMOVE; } static void get_selected_config_indication (QmiClientPdc *client, QmiIndicationPdcGetSelectedConfigOutput *output, GTask *task) { MMSharedQmi *self; LoadCarrierConfigContext *ctx; GArray *active_id = NULL; GError *error = NULL; guint16 error_code = 0; guint i; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); if (!qmi_indication_pdc_get_selected_config_output_get_indication_result (output, &error_code, &error)) { load_carrier_config_abort (task, error); return; } if (error_code != 0 && error_code != QMI_PROTOCOL_ERROR_NOT_PROVISIONED) { /* No configs active */ load_carrier_config_abort (task, g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "couldn't get selected config: %s", qmi_protocol_error_get_string ((QmiProtocolError) error_code))); return; } qmi_indication_pdc_get_selected_config_output_get_active_id (output, &active_id, NULL); if (!active_id) { mm_obj_dbg (self, "no carrier config currently selected (default in use)"); ctx->config_active_default = TRUE; goto next; } g_assert (ctx->config_list); g_assert (ctx->config_list->len); for (i = 0; i < ctx->config_list->len; i++) { ConfigInfo *config; config = &g_array_index (ctx->config_list, ConfigInfo, i); if ((config->id->len == active_id->len) && !memcmp (config->id->data, active_id->data, active_id->len)) { ctx->config_active_i = i; break; } } if (i == ctx->config_list->len) { load_carrier_config_abort (task, g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "couldn't find currently selected config")); return; } next: /* Go on */ load_carrier_config_context_cleanup_action (ctx); ctx->step++; load_carrier_config_step (task); } static void get_selected_config_ready (QmiClientPdc *client, GAsyncResult *res, GTask *task) { QmiMessagePdcGetSelectedConfigOutput *output; LoadCarrierConfigContext *ctx; GError *error = NULL; ctx = g_task_get_task_data (task); output = qmi_client_pdc_get_selected_config_finish (client, res, &error); if (!output || !qmi_message_pdc_get_selected_config_output_get_result (output, &error)) { load_carrier_config_abort (task, error); goto out; } ctx->timeout_id = g_timeout_add_seconds (LOAD_CARRIER_CONFIG_STEP_TIMEOUT_SECS, (GSourceFunc) load_carrier_config_timeout, task); ctx->get_selected_config_indication_id = g_signal_connect (ctx->client, "get-selected-config", G_CALLBACK (get_selected_config_indication), task); out: if (output) qmi_message_pdc_get_selected_config_output_unref (output); } static void get_config_info_indication (QmiClientPdc *client, QmiIndicationPdcGetConfigInfoOutput *output, GTask *task) { LoadCarrierConfigContext *ctx; GError *error = NULL; ConfigInfo *current_config = NULL; guint32 token; const gchar *description; guint i; guint16 error_code = 0; ctx = g_task_get_task_data (task); if (!qmi_indication_pdc_get_config_info_output_get_indication_result (output, &error_code, &error)) { load_carrier_config_abort (task, error); return; } if (error_code != 0) { load_carrier_config_abort (task, g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "couldn't get config info: %s", qmi_protocol_error_get_string ((QmiProtocolError) error_code))); return; } if (!qmi_indication_pdc_get_config_info_output_get_token (output, &token, &error)) { load_carrier_config_abort (task, error); return; } /* Look for the current config in the list, match by token */ for (i = 0; i < ctx->config_list->len; i++) { current_config = &g_array_index (ctx->config_list, ConfigInfo, i); if (current_config->token == token) break; } /* Ignore if not found in the list */ if (i == ctx->config_list->len) return; /* Ignore if already set */ if (current_config->description) return; /* Store total size, version and description of the current config */ if (!qmi_indication_pdc_get_config_info_output_get_total_size (output, ¤t_config->total_size, &error) || !qmi_indication_pdc_get_config_info_output_get_version (output, ¤t_config->version, &error) || !qmi_indication_pdc_get_config_info_output_get_description (output, &description, &error)) { load_carrier_config_abort (task, error); return; } current_config->description = g_strdup (description); ctx->configs_loaded++; /* If not all loaded, wait for more */ if (ctx->configs_loaded < ctx->config_list->len) return; /* Go on */ load_carrier_config_context_cleanup_action (ctx); ctx->step++; load_carrier_config_step (task); } static void list_configs_indication (QmiClientPdc *client, QmiIndicationPdcListConfigsOutput *output, GTask *task) { MMSharedQmi *self; LoadCarrierConfigContext *ctx; GError *error = NULL; GArray *configs = NULL; guint i; guint16 error_code = 0; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); if (!qmi_indication_pdc_list_configs_output_get_indication_result (output, &error_code, &error)) { load_carrier_config_abort (task, error); return; } if (error_code != 0) { load_carrier_config_abort (task, g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "couldn't list configs: %s", qmi_protocol_error_get_string ((QmiProtocolError) error_code))); return; } if (!qmi_indication_pdc_list_configs_output_get_configs (output, &configs, &error)) { load_carrier_config_abort (task, error); return; } /* If no configs are installed, the module is running with the default one */ if (!configs || !configs->len) { ctx->config_active_default = TRUE; ctx->step = LOAD_CARRIER_CONFIG_STEP_LAST; load_carrier_config_step (task); return; } /* Preallocate config list and request details for each */ mm_obj_dbg (self, "found %u carrier configurations...", configs->len); ctx->config_list = g_array_sized_new (FALSE, TRUE, sizeof (ConfigInfo), configs->len); g_array_set_size (ctx->config_list, configs->len); g_array_set_clear_func (ctx->config_list, (GDestroyNotify) config_info_clear); ctx->get_config_info_indication_id = g_signal_connect (ctx->client, "get-config-info", G_CALLBACK (get_config_info_indication), task); for (i = 0; i < configs->len; i++) { ConfigInfo *current_info; QmiIndicationPdcListConfigsOutputConfigsElement *element; g_autoptr(QmiMessagePdcGetConfigInfoInput) input = NULL; element = &g_array_index (configs, QmiIndicationPdcListConfigsOutputConfigsElement, i); current_info = &g_array_index (ctx->config_list, ConfigInfo, i); current_info->token = ctx->token++; current_info->id = g_array_ref (element->id); current_info->config_type = element->config_type; input = qmi_message_pdc_get_config_info_input_new (); qmi_message_pdc_get_config_info_input_set_type_with_id_v2 (input, element->config_type, current_info->id, NULL); qmi_message_pdc_get_config_info_input_set_token (input, current_info->token, NULL); qmi_client_pdc_get_config_info (ctx->client, input, 10, NULL, NULL, NULL); /* ignore response! */ } } static void list_configs_ready (QmiClientPdc *client, GAsyncResult *res, GTask *task) { QmiMessagePdcListConfigsOutput *output; LoadCarrierConfigContext *ctx; GError *error = NULL; ctx = g_task_get_task_data (task); output = qmi_client_pdc_list_configs_finish (client, res, &error); if (!output || !qmi_message_pdc_list_configs_output_get_result (output, &error)) { load_carrier_config_abort (task, error); goto out; } ctx->timeout_id = g_timeout_add_seconds (LOAD_CARRIER_CONFIG_STEP_TIMEOUT_SECS, (GSourceFunc) load_carrier_config_timeout, task); ctx->list_configs_indication_id = g_signal_connect (ctx->client, "list-configs", G_CALLBACK (list_configs_indication), task); out: if (output) qmi_message_pdc_list_configs_output_unref (output); } static void load_carrier_config_step (GTask *task) { LoadCarrierConfigContext *ctx; Private *priv; ctx = g_task_get_task_data (task); priv = get_private (g_task_get_source_object (task)); switch (ctx->step) { case LOAD_CARRIER_CONFIG_STEP_FIRST: ctx->step++; /* fall-through */ case LOAD_CARRIER_CONFIG_STEP_LIST_CONFIGS: { QmiMessagePdcListConfigsInput *input; input = qmi_message_pdc_list_configs_input_new (); qmi_message_pdc_list_configs_input_set_config_type (input, QMI_PDC_CONFIGURATION_TYPE_SOFTWARE, NULL); qmi_message_pdc_list_configs_input_set_token (input, ctx->token++, NULL); qmi_client_pdc_list_configs (ctx->client, input, 5, NULL, (GAsyncReadyCallback)list_configs_ready, task); qmi_message_pdc_list_configs_input_unref (input); return; } case LOAD_CARRIER_CONFIG_STEP_QUERY_CURRENT: { QmiMessagePdcGetSelectedConfigInput *input; input = qmi_message_pdc_get_selected_config_input_new (); qmi_message_pdc_get_selected_config_input_set_config_type (input, QMI_PDC_CONFIGURATION_TYPE_SOFTWARE, NULL); qmi_message_pdc_get_selected_config_input_set_token (input, ctx->token++, NULL); qmi_client_pdc_get_selected_config (ctx->client, input, 5, NULL, (GAsyncReadyCallback)get_selected_config_ready, task); qmi_message_pdc_get_selected_config_input_unref (input); return; } case LOAD_CARRIER_CONFIG_STEP_LAST: /* We will now store the loaded information so that we can later on use it * if needed during the automatic carrier config switching operation */ g_assert (priv->config_active_i < 0 && !priv->config_active_default); g_assert (ctx->config_active_i >= 0 || ctx->config_active_default); priv->config_list = ctx->config_list ? g_array_ref (ctx->config_list) : NULL; priv->config_active_i = ctx->config_active_i; priv->config_active_default = ctx->config_active_default; g_task_return_boolean (task, TRUE); g_object_unref (task); break; default: g_assert_not_reached (); } } void mm_shared_qmi_load_carrier_config (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data) { LoadCarrierConfigContext *ctx; GTask *task; QmiClient *client = NULL; task = g_task_new (self, NULL, callback, user_data); ctx = g_slice_new0 (LoadCarrierConfigContext); ctx->step = LOAD_CARRIER_CONFIG_STEP_FIRST; ctx->config_active_i = -1; g_task_set_task_data (task, ctx, (GDestroyNotify)load_carrier_config_context_free); /* Load PDC client */ client = mm_shared_qmi_peek_client (MM_SHARED_QMI (self), QMI_SERVICE_PDC, MM_PORT_QMI_FLAG_DEFAULT, NULL); if (!client) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "QMI PDC not supported"); g_object_unref (task); return; } ctx->client = QMI_CLIENT_PDC (g_object_ref (client)); load_carrier_config_step (task); } /*****************************************************************************/ /* Load SIM slots (modem interface) */ typedef struct { QmiClientUim *client_uim; GPtrArray *sim_slots; guint active_slot_number; guint active_logical_id; } LoadSimSlotsContext; static void load_sim_slots_context_free (LoadSimSlotsContext *ctx) { g_clear_pointer (&ctx->sim_slots, g_ptr_array_unref); g_clear_object (&ctx->client_uim); g_slice_free (LoadSimSlotsContext, ctx); } static void sim_slot_free (MMBaseSim *sim) { if (sim) g_object_unref (sim); } gboolean mm_shared_qmi_load_sim_slots_finish (MMIfaceModem *self, GAsyncResult *res, GPtrArray **sim_slots, guint *primary_sim_slot, GError **error) { LoadSimSlotsContext *ctx; if (!g_task_propagate_boolean (G_TASK (res), error)) return FALSE; ctx = g_task_get_task_data (G_TASK (res)); if (sim_slots) *sim_slots = g_steal_pointer (&ctx->sim_slots); if (primary_sim_slot) *primary_sim_slot = ctx->active_slot_number; return TRUE; } static void uim_get_slot_status_ready (QmiClientUim *client, GAsyncResult *res, GTask *task) { g_autoptr(QmiMessageUimGetSlotStatusOutput) output = NULL; LoadSimSlotsContext *ctx; MMIfaceModem *self; Private *priv; g_autoptr(GError) error = NULL; GArray *physical_slots = NULL; GArray *ext_information = NULL; GArray *slot_eids = NULL; guint i; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); priv = get_private (MM_SHARED_QMI (self)); output = qmi_client_uim_get_slot_status_finish (client, res, &error); if (!output || !qmi_message_uim_get_slot_status_output_get_result (output, &error) || !qmi_message_uim_get_slot_status_output_get_physical_slot_status (output, &physical_slots, &error)) { if (g_error_matches (error, QMI_PROTOCOL_ERROR, QMI_PROTOCOL_ERROR_DEVICE_UNSUPPORTED) || g_error_matches (error, QMI_PROTOCOL_ERROR, QMI_PROTOCOL_ERROR_INVALID_QMI_COMMAND) || g_error_matches (error, QMI_PROTOCOL_ERROR, QMI_PROTOCOL_ERROR_NOT_SUPPORTED)) g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "QMI SIM slot switch operation not supported"); else g_task_return_error (task, g_steal_pointer (&error)); g_object_unref (task); return; } /* Store the slot status before loading all sim slots. * We will use this to check if a hotswap requires a reprobe. */ if (priv->slots_status) g_array_unref (priv->slots_status); priv->slots_status = g_array_ref (physical_slots); /* It's fine if we don't have EID information, but it should be well-formed if present. If it's malformed, * there is probably a modem firmware bug. */ if (qmi_message_uim_get_slot_status_output_get_physical_slot_information (output, &ext_information, NULL) && qmi_message_uim_get_slot_status_output_get_slot_eid (output, &slot_eids, NULL) && (ext_information->len != physical_slots->len || slot_eids->len != physical_slots->len)) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "UIM Get Slot Status returned malformed response"); g_object_unref (task); return; } ctx->sim_slots = g_ptr_array_new_full (physical_slots->len, (GDestroyNotify) sim_slot_free); for (i = 0; i < physical_slots->len; i++) { QmiPhysicalSlotStatusSlot *slot_status; QmiPhysicalSlotInformationSlot *slot_info; MMBaseSim *sim; g_autofree gchar *raw_iccid = NULL; g_autofree gchar *iccid = NULL; g_autofree gchar *eid = NULL; g_autoptr(GError) inner_error = NULL; gboolean sim_active = FALSE; /* Store active slot info */ slot_status = &g_array_index (physical_slots, QmiPhysicalSlotStatusSlot, i); if (slot_status->physical_slot_status == QMI_UIM_SLOT_STATE_ACTIVE) { sim_active = TRUE; ctx->active_logical_id = slot_status->logical_slot; ctx->active_slot_number = i + 1; } if (!slot_status->iccid->len) { mm_obj_dbg (self, "not creating SIM object: no SIM in slot %u", i + 1); g_ptr_array_add (ctx->sim_slots, NULL); continue; } /* This is supposed to be BCD, but some carriers have non-spec-compliant ICCIDs that use * A-F characters as part of the operator-specific part of the ICCID. Parse it as hex and * let mm_3gpp_parse_iccid take care of handling the A-F characters properly. */ raw_iccid = mm_utils_bin2hexstr ((const guint8 *)slot_status->iccid->data, slot_status->iccid->len); if (!raw_iccid) { mm_obj_warn (self, "not creating SIM object: failed to convert ICCID from BCD"); g_ptr_array_add (ctx->sim_slots, NULL); continue; } iccid = mm_3gpp_parse_iccid (raw_iccid, &inner_error); if (!iccid) { mm_obj_warn (self, "not creating SIM object: couldn't parse SIM iccid: %s", inner_error->message); g_ptr_array_add (ctx->sim_slots, NULL); continue; } if (ext_information && slot_eids) { slot_info = &g_array_index (ext_information, QmiPhysicalSlotInformationSlot, i); if (slot_info->is_euicc) { QmiSlotEidElement *slot_eid_element; slot_eid_element = &g_array_index (slot_eids, QmiSlotEidElement, i); if (slot_eid_element->eid->len) eid = mm_decode_eid (slot_eid_element->eid->data, slot_eid_element->eid->len); if (!eid) mm_obj_dbg (self, "SIM in slot %d is marked as eUICC, but has malformed EID", i + 1); } } sim = mm_sim_qmi_new_initialized (MM_BASE_MODEM (self), TRUE, /* consider DMS UIM deprecated if we're creating SIM slots */ i + 1, /* slot number is the array index starting at 1 */ sim_active, iccid, NULL, /* imsi unknown */ eid, /* may be NULL, which is fine */ NULL, /* operator id unknown */ NULL, /* operator name unknown */ NULL); /* emergency numbers unknown */ g_ptr_array_add (ctx->sim_slots, sim); } g_assert_cmpuint (ctx->sim_slots->len, ==, physical_slots->len); g_task_return_boolean (task, TRUE); g_object_unref (task); } void mm_shared_qmi_load_sim_slots (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data) { LoadSimSlotsContext *ctx; GTask *task; QmiClient *client = NULL; if (!mm_shared_qmi_ensure_client (MM_SHARED_QMI (self), QMI_SERVICE_UIM, &client, callback, user_data)) return; task = g_task_new (self, NULL, callback, user_data); ctx = g_slice_new0 (LoadSimSlotsContext); ctx->client_uim = QMI_CLIENT_UIM (g_object_ref (client)); g_task_set_task_data (task, ctx, (GDestroyNotify) load_sim_slots_context_free); qmi_client_uim_get_slot_status (ctx->client_uim, NULL, 10, NULL, (GAsyncReadyCallback) uim_get_slot_status_ready, task); } /*****************************************************************************/ /* Set Primary SIM slot (modem interface) */ gboolean mm_shared_qmi_set_primary_sim_slot_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void uim_switch_slot_ready (QmiClientUim *client, GAsyncResult *res, GTask *task) { g_autoptr(QmiMessageUimSwitchSlotOutput) output = NULL; g_autoptr(GError) error = NULL; MMIfaceModem *self; self = g_task_get_source_object (task); output = qmi_client_uim_switch_slot_finish (client, res, &error); if (!output || !qmi_message_uim_switch_slot_output_get_result (output, &error)) { if (g_error_matches (error, QMI_PROTOCOL_ERROR, QMI_PROTOCOL_ERROR_NO_EFFECT)) g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_EXISTS, "SIM slot switch operation not needed"); else g_task_return_error (task, g_steal_pointer (&error)); } else { mm_obj_msg (self, "SIM slot switch operation request successful"); g_task_return_boolean (task, TRUE); } g_object_unref (task); } static void uim_switch_get_slot_status_ready (QmiClientUim *client, GAsyncResult *res, GTask *task) { g_autoptr(QmiMessageUimGetSlotStatusOutput) output = NULL; g_autoptr(QmiMessageUimSwitchSlotInput) input = NULL; MMIfaceModem *self; g_autoptr(GError) error = NULL; GArray *physical_slots = NULL; guint i; guint active_logical_id = 0; guint active_slot_number; guint slot_number; self = g_task_get_source_object (task); slot_number = GPOINTER_TO_UINT (g_task_get_task_data (task)); output = qmi_client_uim_get_slot_status_finish (client, res, &error); if (!output || !qmi_message_uim_get_slot_status_output_get_result (output, &error) || !qmi_message_uim_get_slot_status_output_get_physical_slot_status (output, &physical_slots, &error)) { if (g_error_matches (error, QMI_PROTOCOL_ERROR, QMI_PROTOCOL_ERROR_DEVICE_UNSUPPORTED) || g_error_matches (error, QMI_PROTOCOL_ERROR, QMI_PROTOCOL_ERROR_INVALID_QMI_COMMAND) || g_error_matches (error, QMI_PROTOCOL_ERROR, QMI_PROTOCOL_ERROR_NOT_SUPPORTED)) g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "QMI SIM slot switch operation not supported"); else g_task_return_error (task, g_steal_pointer (&error)); g_object_unref (task); return; } for (i = 0; i < physical_slots->len; i++) { QmiPhysicalSlotStatusSlot *slot_status; /* We look for the currently ACTIVE SIM card only! */ slot_status = &g_array_index (physical_slots, QmiPhysicalSlotStatusSlot, i); if (slot_status->physical_slot_status != QMI_UIM_SLOT_STATE_ACTIVE) continue; active_logical_id = slot_status->logical_slot; active_slot_number = i + 1; } if (!active_logical_id) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "couldn't find active slot logical ID"); g_object_unref (task); return; } if (active_slot_number == slot_number) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_EXISTS, "SIM slot switch operation not needed"); g_object_unref (task); return; } mm_obj_dbg (self, "requesting active logical id %d switch to SIM slot %u", active_logical_id, slot_number); input = qmi_message_uim_switch_slot_input_new (); qmi_message_uim_switch_slot_input_set_logical_slot (input, (guint8) active_logical_id, NULL); qmi_message_uim_switch_slot_input_set_physical_slot (input, slot_number, NULL); qmi_client_uim_switch_slot (client, input, 10, NULL, (GAsyncReadyCallback) uim_switch_slot_ready, task); } void mm_shared_qmi_set_primary_sim_slot (MMIfaceModem *self, guint sim_slot, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; QmiClient *client = NULL; if (!mm_shared_qmi_ensure_client (MM_SHARED_QMI (self), QMI_SERVICE_UIM, &client, callback, user_data)) return; task = g_task_new (self, NULL, callback, user_data); g_task_set_task_data (task, GUINT_TO_POINTER (sim_slot), NULL); qmi_client_uim_get_slot_status (QMI_CLIENT_UIM (client), NULL, 10, NULL, (GAsyncReadyCallback) uim_switch_get_slot_status_ready, task); } /*****************************************************************************/ /* UIM refresh indication handling */ #define REFRESH_START_TIMEOUT_SECS 3 static void uim_refresh_complete (QmiClientUim *client, QmiUimSessionType session_type) { g_autoptr(QmiMessageUimRefreshCompleteInput) refresh_complete_input = NULL; GArray *placeholder_aid; placeholder_aid = g_array_new (FALSE, FALSE, sizeof (guint8)); refresh_complete_input = qmi_message_uim_refresh_complete_input_new (); qmi_message_uim_refresh_complete_input_set_session ( refresh_complete_input, session_type, placeholder_aid, /* ignored */ NULL); qmi_message_uim_refresh_complete_input_set_info ( refresh_complete_input, TRUE, NULL); qmi_client_uim_refresh_complete ( client, refresh_complete_input, 10, NULL, NULL, NULL); g_array_unref (placeholder_aid); } static gboolean uim_start_refresh_timeout (MMSharedQmi *self) { Private *priv; priv = get_private (self); priv->uim_refresh_start_timeout_id = 0; mm_obj_dbg (self, "refresh start timed out; trigger SIM change check"); mm_iface_modem_check_for_sim_swap (MM_IFACE_MODEM (self), NULL, NULL); return G_SOURCE_REMOVE; } static void uim_refresh_indication_cb (QmiClientUim *client, QmiIndicationUimRefreshOutput *output, MMSharedQmi *self) { QmiUimRefreshStage stage; QmiUimRefreshMode mode; QmiUimSessionType session_type; Private *priv; g_autoptr(GError) error = NULL; priv = get_private (self); if (!qmi_indication_uim_refresh_output_get_event (output, &stage, &mode, &session_type, NULL, NULL, &error)) { mm_obj_warn (self, "couldn't process UIM refresh indication: %s", error->message); return; } mm_obj_dbg (self, "refresh indication received: session type '%s', stage '%s', mode '%s'", qmi_uim_session_type_get_string (session_type), qmi_uim_refresh_stage_get_string (stage), qmi_uim_refresh_mode_get_string (mode)); /* Support only the first slot for now. Primary GW provisioning is used in old modems. */ if (session_type != QMI_UIM_SESSION_TYPE_CARD_SLOT_1 && session_type != QMI_UIM_SESSION_TYPE_PRIMARY_GW_PROVISIONING) { mm_obj_warn (self, "refresh session type not supported: %s", qmi_uim_session_type_get_string (session_type)); return; } /* Currently we handle UICC Reset type refresh, which can be used * in profile switch scenarios, and Init Full FCN type refresh for * SIM IMSI switch scenarios. In other cases we just trigger 'refresh * complete' during start phase. Signal to notify about potential SIM * profile switch is triggered when the refresh is ending. If it were * triggered in start phase, reading SIM files seems to fail with * an internal error. * * It's possible that 'end-with-success' stage never appears. For that, * we start a timer at 'start' stage and if it expires, the SIM change * check is triggered anyway. */ if (stage == QMI_UIM_REFRESH_STAGE_START) { if (mode == QMI_UIM_REFRESH_MODE_RESET || mode == QMI_UIM_REFRESH_MODE_INIT_FULL_FCN) { if (!priv->uim_refresh_start_timeout_id) priv->uim_refresh_start_timeout_id = g_timeout_add_seconds (REFRESH_START_TIMEOUT_SECS, (GSourceFunc)uim_start_refresh_timeout, self); } else uim_refresh_complete (client, session_type); } else if (stage == QMI_UIM_REFRESH_STAGE_END_WITH_SUCCESS) { if (mode == QMI_UIM_REFRESH_MODE_RESET || mode == QMI_UIM_REFRESH_MODE_INIT_FULL_FCN) { if (priv->uim_refresh_start_timeout_id) { g_source_remove (priv->uim_refresh_start_timeout_id); priv->uim_refresh_start_timeout_id = 0; } mm_iface_modem_check_for_sim_swap (MM_IFACE_MODEM (self), NULL, NULL); } } } /*****************************************************************************/ /* UIM slot status indication handling */ /* Modifies the sim at slot == index+1, based on the content of slot_status. * Primarily used when a hotswap occurs on the inactive slot */ static void update_sim_from_slot_status (MMSharedQmi *self, QmiPhysicalSlotStatusSlot *slot_status, guint slot_index) { g_autoptr(MMBaseSim) sim = NULL; g_autofree gchar *raw_iccid = NULL; g_autofree gchar *iccid = NULL; g_autoptr(GError) inner_error = NULL; mm_obj_dbg (self, "Updating sim at slot %d", slot_index + 1); /* If sim is not present, ask mm_iface_modem to clear the sim object */ if (slot_status->physical_card_status != QMI_UIM_PHYSICAL_CARD_STATE_PRESENT) { mm_iface_modem_modify_sim (MM_IFACE_MODEM (self), slot_index, NULL); return; } raw_iccid = mm_utils_bin2hexstr ((const guint8 *)slot_status->iccid->data, slot_status->iccid->len); if (!raw_iccid) { mm_obj_warn (self, "not creating SIM object: failed to convert ICCID from BCD"); return; } iccid = mm_3gpp_parse_iccid (raw_iccid, &inner_error); if (!iccid) { mm_obj_warn (self, "not creating SIM object: couldn't parse SIM iccid: %s", inner_error->message); return; } sim = mm_sim_qmi_new_initialized (MM_BASE_MODEM (self), TRUE, /* consider DMS UIM deprecated if we're creating SIM slots */ slot_index + 1, /* slot number is the array index starting at 1 */ slot_status->physical_slot_status, /* is_active */ iccid, NULL, /* imsi unknown */ NULL, /* eid unknown */ NULL, /* operator id unknown */ NULL, /* operator name unknown */ NULL); /* emergency numbers unknown */ mm_iface_modem_modify_sim (MM_IFACE_MODEM (self), slot_index, sim); } /* Checks for equality of two slots. */ static gboolean slot_status_equal (QmiPhysicalSlotStatusSlot *slot_a, QmiPhysicalSlotStatusSlot *slot_b) { guint j; if (slot_a->physical_slot_status != slot_b->physical_slot_status) return FALSE; if (slot_a->physical_card_status != slot_b->physical_card_status) return FALSE; if (slot_a->iccid->len != slot_b->iccid->len) return FALSE; for (j = 0; j < slot_a->iccid->len; j++) { if (g_array_index (slot_a->iccid, guint8, j) != g_array_index (slot_b->iccid, guint8, j)) return FALSE; } return TRUE; } /* Checks for equality of QmiPhysicalSlotStatusSlot arrays. * The number of elements in each array is expected to be the number of sim slots in the modem.*/ static gboolean slot_array_status_equal (GArray *slots_status1, GArray *slots_status2, gboolean check_active_slots_only) { guint i; if (!slots_status1 && !slots_status2) return TRUE; if (!slots_status1 || !slots_status2 || slots_status1->len != slots_status2->len) return FALSE; for (i = 0; i < slots_status1->len; i++) { /* Compare slot at index i from slots_status1 and slots_status2 */ QmiPhysicalSlotStatusSlot *slot_a; QmiPhysicalSlotStatusSlot *slot_b; slot_a = &g_array_index (slots_status1, QmiPhysicalSlotStatusSlot, i); slot_b = &g_array_index (slots_status2, QmiPhysicalSlotStatusSlot, i); /* Check that slot_a and slot_b have the same slot status (i.e. active or inactive) */ if (slot_a->physical_slot_status != slot_b->physical_slot_status) return FALSE; /* Once slot_a and slot_b are confirmed to have the same physical slot status, * we will ignore inactive slots if check_active_slots_only is set. */ if (check_active_slots_only && slot_a->physical_slot_status != QMI_UIM_SLOT_STATE_ACTIVE) { g_assert (slot_a->physical_slot_status == slot_b->physical_slot_status); continue; } if (!slot_status_equal (slot_a, slot_b)) return FALSE; } return TRUE; } static void uim_slot_status_indication_cb (QmiClientUim *client, QmiIndicationUimSlotStatusOutput *output, MMSharedQmi *self) { GArray *new_slots_status = NULL; Private *priv; guint i; g_autoptr(GError) error = NULL; priv = get_private (self); mm_obj_dbg (self, "received slot status indication"); if (!priv->slots_status) { mm_obj_dbg (self, "initial slot status is not loaded yet"); return; } if (!qmi_indication_uim_slot_status_output_get_physical_slot_status (output, &new_slots_status, &error)) { mm_obj_warn (self, "could not process slot status indication: %s", error->message); return; } /* A slot status indication means that * 1) The physical slot to logical slot mapping has changed as a * result of switching the slot. or, * 2) A card has been removed from, or inserted to, the physical slot. or, * 3) A physical slot is powered up or down. */ /* Reprobe if the active slot changed or the information in an * active slot's status changed */ if (!slot_array_status_equal (priv->slots_status, new_slots_status, TRUE)) { mm_obj_dbg (self, "An active slot had a status change, will reprobe the modem"); mm_iface_modem_process_sim_event (MM_IFACE_MODEM (self)); return; } /* An inactive slot changed, we won't be reprobing the modem. * Instead, we will ask mm_iface_modem to update the sim object. * Iterate over each slot to identify the slots that changed state.*/ for (i = 0; i < priv->slots_status->len; i++) { QmiPhysicalSlotStatusSlot *old_slot; QmiPhysicalSlotStatusSlot *new_slot; old_slot = &g_array_index (priv->slots_status, QmiPhysicalSlotStatusSlot, i); new_slot = &g_array_index (new_slots_status, QmiPhysicalSlotStatusSlot, i); if (!slot_status_equal (old_slot, new_slot)) { mm_obj_dbg (self, "Slot %d (inactive) had a status change. Will update sims, but not reprobe", i + 1); update_sim_from_slot_status (self, new_slot, i); } } g_clear_pointer (&priv->slots_status, g_array_unref); priv->slots_status = g_array_ref (new_slots_status); } /*****************************************************************************/ /* SIM hot swap setup */ typedef enum { SETUP_SIM_HOT_SWAP_STEP_FIRST, SETUP_SIM_HOT_SWAP_STEP_UIM_REGISTER_SLOT_STATUS, SETUP_SIM_HOT_SWAP_STEP_UIM_CHECK_SLOT_STATUS, SETUP_SIM_HOT_SWAP_STEP_UIM_SLOT_STATUS_INDICATION, SETUP_SIM_HOT_SWAP_STEP_UIM_REFRESH_REGISTER_ALL, SETUP_SIM_HOT_SWAP_STEP_UIM_REFRESH_REGISTER_ICCID, SETUP_SIM_HOT_SWAP_STEP_UIM_REFRESH_REGISTER_IMSI, SETUP_SIM_HOT_SWAP_STEP_UIM_REFRESH_INDICATION, SETUP_SIM_HOT_SWAP_STEP_LAST, } SetupSimHotSwapStep; typedef struct { SetupSimHotSwapStep step; gboolean register_slot_status_supported; gboolean get_slot_status_supported; gboolean refresh_all_supported; gboolean refresh_file_supported; QmiClient *uim_client; gulong uim_slot_status_indication_id; gulong uim_refresh_indication_id; } SetupSimHotSwapContext; static void setup_sim_hot_swap_step (GTask *task); static void setup_sim_hot_swap_context_free (SetupSimHotSwapContext *ctx) { if (ctx->uim_client && ctx->uim_slot_status_indication_id) g_signal_handler_disconnect (ctx->uim_client, ctx->uim_slot_status_indication_id); if (ctx->uim_client && ctx->uim_refresh_indication_id) g_signal_handler_disconnect (ctx->uim_client, ctx->uim_refresh_indication_id); g_clear_object (&ctx->uim_client); g_slice_free (SetupSimHotSwapContext, ctx); } gboolean mm_shared_qmi_setup_sim_hot_swap_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void uim_refresh_register_file_ready (QmiClientUim *client, GAsyncResult *res, GTask *task) { MMSharedQmi *self; g_autoptr(QmiMessageUimRefreshRegisterOutput) output = NULL; g_autoptr(GError) error = NULL; SetupSimHotSwapContext *ctx; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); output = qmi_client_uim_refresh_register_finish (client, res, &error); if (!output || !qmi_message_uim_refresh_register_output_get_result (output, &error)) mm_obj_dbg (self, "file refresh registration using 'refresh register' failed: %s", error->message); else { mm_obj_dbg (self, "file refresh registered using 'refresh register'"); ctx->refresh_file_supported = TRUE; } /* Go on to next step */ ctx->step++; setup_sim_hot_swap_step (task); } static void uim_refresh_register_file (GTask *task, guint16 file_id, const guint16 *file_path, gsize file_path_len) { MMSharedQmi *self; SetupSimHotSwapContext *ctx; QmiMessageUimRefreshRegisterInputInfoFilesElement file_element; guint8 val; gsize i; g_autoptr(QmiMessageUimRefreshRegisterInput) refresh_register_input = NULL; g_autoptr(GArray) placeholder_aid = NULL; g_autoptr(GArray) file = NULL; g_autoptr(GArray) file_element_path = NULL; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); /* This is the last resort if 'refresh register all' does not work. It works * on some older modems. Those modems may not also support QMI_UIM_SESSION_TYPE_CARD_SLOT_1 * so we'll use QMI_UIM_SESSION_TYPE_PRIMARY_GW_PROVISIONING */ mm_obj_dbg (self, "register for refresh file indication"); placeholder_aid = g_array_new (FALSE, FALSE, sizeof (guint8)); file = g_array_sized_new (FALSE, FALSE, sizeof (QmiMessageUimRefreshRegisterInputInfoFilesElement), 1); file_element_path = g_array_sized_new (FALSE, FALSE, sizeof (guint8), file_path_len * 2); for (i = 0; i < file_path_len; ++i) { val = file_path[i] & 0xFF; g_array_append_val (file_element_path, val); val = (file_path[i] >> 8) & 0xFF; g_array_append_val (file_element_path, val); } memset (&file_element, 0, sizeof (file_element)); file_element.file_id = file_id; file_element.path = file_element_path; g_array_append_val (file, file_element); refresh_register_input = qmi_message_uim_refresh_register_input_new (); qmi_message_uim_refresh_register_input_set_info (refresh_register_input, TRUE, FALSE, file, NULL); qmi_message_uim_refresh_register_input_set_session (refresh_register_input, QMI_UIM_SESSION_TYPE_PRIMARY_GW_PROVISIONING, placeholder_aid, NULL); qmi_client_uim_refresh_register (QMI_CLIENT_UIM (ctx->uim_client), refresh_register_input, 10, NULL, (GAsyncReadyCallback) uim_refresh_register_file_ready, task); } /* Refresh registration and event handling. * This is used for detecting ICCID and IMSI change. */ static void uim_refresh_register_all_ready (QmiClientUim *client, GAsyncResult *res, GTask *task) { g_autoptr(QmiMessageUimRefreshRegisterAllOutput) output = NULL; g_autoptr(GError) error = NULL; MMIfaceModem *self; SetupSimHotSwapContext *ctx; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); output = qmi_client_uim_refresh_register_all_finish (client, res, &error); if (!output || !qmi_message_uim_refresh_register_all_output_get_result (output, &error)) { mm_obj_dbg (self, "refresh register all operation failed: %s", error->message); } else { /* Jump to setup refresh indication signal */ mm_obj_dbg (self, "registered for all SIM refresh events"); ctx->refresh_all_supported = TRUE; } ctx->step++; setup_sim_hot_swap_step (task); } static void uim_check_get_slot_status_ready (QmiClientUim *client, GAsyncResult *res, GTask *task) { g_autoptr(QmiMessageUimGetSlotStatusOutput) output = NULL; g_autoptr(GError) error = NULL; MMIfaceModem *self; SetupSimHotSwapContext *ctx; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); output = qmi_client_uim_get_slot_status_finish (client, res, &error); if (!output || !qmi_message_uim_get_slot_status_output_get_result (output, &error)) { mm_obj_dbg (self, "slot status retrieval failed: %s", error->message); } else { /* Go on to next step */ mm_obj_dbg (self, "slot status retrieval succeeded"); ctx->get_slot_status_supported = TRUE; } ctx->step++; setup_sim_hot_swap_step (task); } static void uim_register_events_ready (QmiClientUim *client, GAsyncResult *res, GTask *task) { g_autoptr(QmiMessageUimRegisterEventsOutput) output = NULL; g_autoptr(GError) error = NULL; MMIfaceModem *self; SetupSimHotSwapContext *ctx; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); /* If event registration fails, go on with initialization. In that case * we cannot use slot status indications to detect eUICC profile switches. */ output = qmi_client_uim_register_events_finish (client, res, &error); if (!output || !qmi_message_uim_register_events_output_get_result (output, &error)) { mm_obj_dbg (self, "not registered for slot status indications: %s", error->message); } else { mm_obj_dbg (self, "registered for slot status indications"); ctx->register_slot_status_supported = TRUE; } /* Go on to next step */ ctx->step++; setup_sim_hot_swap_step (task); } static void setup_sim_hot_swap_step (GTask *task) { MMSharedQmi *self; Private *priv; SetupSimHotSwapContext *ctx; self = g_task_get_source_object (task); priv = get_private (MM_SHARED_QMI (self)); ctx = g_task_get_task_data (task); switch (ctx->step) { case SETUP_SIM_HOT_SWAP_STEP_FIRST: ctx->step++; /* fall-through */ case SETUP_SIM_HOT_SWAP_STEP_UIM_REGISTER_SLOT_STATUS: { g_autoptr(QmiMessageUimRegisterEventsInput) register_events_input = NULL; mm_obj_dbg (self, "setup SIM hot swap (%u/%u): registering for slot status indications...", ctx->step, SETUP_SIM_HOT_SWAP_STEP_LAST); register_events_input = qmi_message_uim_register_events_input_new (); qmi_message_uim_register_events_input_set_event_registration_mask (register_events_input, QMI_UIM_EVENT_REGISTRATION_FLAG_PHYSICAL_SLOT_STATUS, NULL); qmi_client_uim_register_events (QMI_CLIENT_UIM (ctx->uim_client), register_events_input, 10, NULL, (GAsyncReadyCallback) uim_register_events_ready, task); return; } case SETUP_SIM_HOT_SWAP_STEP_UIM_CHECK_SLOT_STATUS: if (ctx->register_slot_status_supported) { mm_obj_dbg (self, "setup SIM hot swap (%u/%u): checking slot status support...", ctx->step, SETUP_SIM_HOT_SWAP_STEP_LAST); /* Successful registration does not mean that the modem actually sends * physical slot status indications; invoke Get Slot Status to find out if * the modem really supports slot status. */ qmi_client_uim_get_slot_status (QMI_CLIENT_UIM (ctx->uim_client), NULL, 10, NULL, (GAsyncReadyCallback) uim_check_get_slot_status_ready, task); return; } mm_obj_dbg (self, "setup SIM hot swap (%u/%u): no need to check for slot status support...", ctx->step, SETUP_SIM_HOT_SWAP_STEP_LAST); ctx->step++; /* fall-through */ case SETUP_SIM_HOT_SWAP_STEP_UIM_SLOT_STATUS_INDICATION: if (ctx->register_slot_status_supported && ctx->get_slot_status_supported) { mm_obj_dbg (self, "setup SIM hot swap (%u/%u): monitoring slot status indications...", ctx->step, SETUP_SIM_HOT_SWAP_STEP_LAST); ctx->uim_slot_status_indication_id = g_signal_connect (ctx->uim_client, "slot-status", G_CALLBACK (uim_slot_status_indication_cb), self); } else mm_obj_dbg (self, "setup SIM hot swap (%u/%u): no need to monitor for slot status indications...", ctx->step, SETUP_SIM_HOT_SWAP_STEP_LAST); ctx->step++; /* fall-through */ case SETUP_SIM_HOT_SWAP_STEP_UIM_REFRESH_REGISTER_ALL: { g_autoptr(QmiMessageUimRefreshRegisterAllInput) refresh_register_all_input = NULL; g_autoptr(GArray) placeholder_aid = NULL; mm_obj_dbg (self, "setup SIM hot swap (%u/%u): registering for all refresh events...", ctx->step, SETUP_SIM_HOT_SWAP_STEP_LAST); placeholder_aid = g_array_new (FALSE, FALSE, sizeof (guint8)); refresh_register_all_input = qmi_message_uim_refresh_register_all_input_new (); qmi_message_uim_refresh_register_all_input_set_info (refresh_register_all_input, TRUE, NULL); qmi_message_uim_refresh_register_all_input_set_session (refresh_register_all_input, QMI_UIM_SESSION_TYPE_PRIMARY_GW_PROVISIONING, placeholder_aid, NULL); qmi_client_uim_refresh_register_all (QMI_CLIENT_UIM (ctx->uim_client), refresh_register_all_input, 10, NULL, (GAsyncReadyCallback) uim_refresh_register_all_ready, task); return; } case SETUP_SIM_HOT_SWAP_STEP_UIM_REFRESH_REGISTER_ICCID: /* If refresh all not supported, register for a single file refresh notification */ if (!ctx->refresh_all_supported) { const guint16 file_path[] = { 0x3F00 }; mm_obj_dbg (self, "setup SIM hot swap (%u/%u): registering for SIM ICCID refresh events...", ctx->step, SETUP_SIM_HOT_SWAP_STEP_LAST); uim_refresh_register_file (task, 0x2FE2, file_path, G_N_ELEMENTS (file_path)); return; } mm_obj_dbg (self, "setup SIM hot swap (%u/%u): no need to register for SIM ICCID refresh events", ctx->step, SETUP_SIM_HOT_SWAP_STEP_LAST); ctx->step++; /* fall-through */ case SETUP_SIM_HOT_SWAP_STEP_UIM_REFRESH_REGISTER_IMSI: /* If refresh all not supported, register for a single file refresh notification */ if (!ctx->refresh_all_supported) { const guint16 file_path[] = { 0x3F00, 0x7FFF }; mm_obj_dbg (self, "setup SIM hot swap (%u/%u): registering for SIM IMSI refresh events...", ctx->step, SETUP_SIM_HOT_SWAP_STEP_LAST); uim_refresh_register_file (task, 0x6F07, file_path, G_N_ELEMENTS (file_path)); return; } mm_obj_dbg (self, "setup SIM hot swap (%u/%u): no need to register for SIM IMSI refresh events...", ctx->step, SETUP_SIM_HOT_SWAP_STEP_LAST); ctx->step++; /* fall-through */ case SETUP_SIM_HOT_SWAP_STEP_UIM_REFRESH_INDICATION: if (ctx->refresh_all_supported || ctx->refresh_file_supported) { mm_obj_dbg (self, "setup SIM hot swap (%u/%u): monitoring refresh indications...", ctx->step, SETUP_SIM_HOT_SWAP_STEP_LAST); ctx->uim_refresh_indication_id = g_signal_connect (ctx->uim_client, "refresh", G_CALLBACK (uim_refresh_indication_cb), self); } else mm_obj_dbg (self, "setup SIM hot swap (%u/%u): no need to monitor for refresh indications...", ctx->step, SETUP_SIM_HOT_SWAP_STEP_LAST); ctx->step++; /* fall-through */ case SETUP_SIM_HOT_SWAP_STEP_LAST: if (ctx->refresh_all_supported || ctx->refresh_file_supported || (ctx->register_slot_status_supported && ctx->get_slot_status_supported)) { /* at least one method was supported, transfer state to the private * info and return success */ g_assert (!priv->uim_client); priv->uim_client = g_steal_pointer (&ctx->uim_client); if (ctx->uim_slot_status_indication_id) { g_assert (!priv->uim_slot_status_indication_id); priv->uim_slot_status_indication_id = ctx->uim_slot_status_indication_id; ctx->uim_slot_status_indication_id = 0; } if (ctx->uim_refresh_indication_id) { g_assert (!priv->uim_refresh_indication_id); priv->uim_refresh_indication_id = ctx->uim_refresh_indication_id; ctx->uim_refresh_indication_id = 0; } mm_obj_dbg (self, "setup SIM hot swap (%u/%u): successfully finished", ctx->step, SETUP_SIM_HOT_SWAP_STEP_LAST); g_task_return_boolean (task, TRUE); } else { mm_obj_dbg (self, "setup SIM hot swap (%u/%u): failed", ctx->step, SETUP_SIM_HOT_SWAP_STEP_LAST); g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't setup SIM hot swap"); } g_object_unref (task); return; default: g_assert_not_reached (); } } void mm_shared_qmi_setup_sim_hot_swap (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; QmiClient *client = NULL; SetupSimHotSwapContext *ctx; if (!mm_shared_qmi_ensure_client (MM_SHARED_QMI (self), QMI_SERVICE_UIM, &client, callback, user_data)) return; task = g_task_new (self, NULL, callback, user_data); ctx = g_slice_new0 (SetupSimHotSwapContext); ctx->step = SETUP_SIM_HOT_SWAP_STEP_FIRST; ctx->uim_client = g_object_ref (client); g_task_set_task_data (task, ctx, (GDestroyNotify)setup_sim_hot_swap_context_free); setup_sim_hot_swap_step (task); } /*****************************************************************************/ /* Set packet service state (3GPP interface) */ typedef struct { QmiClientNas *client; MMModem3gppPacketServiceState packet_service_state; } SetPacketServiceStateContext; static void set_packet_service_state_context_free (SetPacketServiceStateContext *ctx) { g_object_unref (ctx->client); g_slice_free (SetPacketServiceStateContext, ctx); } gboolean mm_shared_qmi_set_packet_service_state_finish (MMIfaceModem3gpp *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void set_packet_service_state_ia_ready (QmiClientNas *client, GAsyncResult *res, GTask *task) { g_autoptr(GError) error = NULL; g_autoptr(QmiMessageNasAttachDetachOutput) output = NULL; output = qmi_client_nas_attach_detach_finish (client, res, &error); if ((!output || !qmi_message_nas_attach_detach_output_get_result (output, &error)) && !g_error_matches (error, QMI_PROTOCOL_ERROR, QMI_PROTOCOL_ERROR_NO_EFFECT)) { g_prefix_error (&error, "Couldn't set packet service state: "); g_task_return_error (task, g_steal_pointer (&error)); } else g_task_return_boolean (task, TRUE); g_object_unref (task); } static void set_packet_service_state_ia (GTask *task) { g_autoptr(QmiMessageNasAttachDetachInput) input = NULL; SetPacketServiceStateContext *ctx; input = qmi_message_nas_attach_detach_input_new (); ctx = g_task_get_task_data (task); if (ctx->packet_service_state == MM_MODEM_3GPP_PACKET_SERVICE_STATE_ATTACHED) qmi_message_nas_attach_detach_input_set_action (input, QMI_NAS_PS_ATTACH_ACTION_ATTACH, NULL); else if (ctx->packet_service_state == MM_MODEM_3GPP_PACKET_SERVICE_STATE_DETACHED) qmi_message_nas_attach_detach_input_set_action (input, QMI_NAS_PS_ATTACH_ACTION_DETACH, NULL); else g_assert_not_reached (); qmi_client_nas_attach_detach ( ctx->client, input, 5, NULL, (GAsyncReadyCallback)set_packet_service_state_ia_ready, task); } static void set_packet_service_state_sssp_ready (QmiClientNas *client, GAsyncResult *res, GTask *task) { g_autoptr(GError) error = NULL; g_autoptr(QmiMessageNasSetSystemSelectionPreferenceOutput) output = NULL; output = qmi_client_nas_set_system_selection_preference_finish (client, res, &error); if ((!output || !qmi_message_nas_set_system_selection_preference_output_get_result (output, &error)) && !g_error_matches (error, QMI_PROTOCOL_ERROR, QMI_PROTOCOL_ERROR_NO_EFFECT)) { g_prefix_error (&error, "Couldn't set packet service state: "); g_task_return_error (task, g_steal_pointer (&error)); } else g_task_return_boolean (task, TRUE); g_object_unref (task); } static void set_packet_service_state_sssp (GTask *task) { g_autoptr(QmiMessageNasSetSystemSelectionPreferenceInput) input = NULL; SetPacketServiceStateContext *ctx; input = qmi_message_nas_set_system_selection_preference_input_new (); ctx = g_task_get_task_data (task); if (ctx->packet_service_state == MM_MODEM_3GPP_PACKET_SERVICE_STATE_ATTACHED) qmi_message_nas_set_system_selection_preference_input_set_service_domain_preference ( input, QMI_NAS_SERVICE_DOMAIN_PREFERENCE_PS_ATTACH, NULL); else if (ctx->packet_service_state == MM_MODEM_3GPP_PACKET_SERVICE_STATE_DETACHED) qmi_message_nas_set_system_selection_preference_input_set_service_domain_preference ( input, QMI_NAS_SERVICE_DOMAIN_PREFERENCE_PS_DETACH, NULL); else g_assert_not_reached (); qmi_client_nas_set_system_selection_preference ( ctx->client, input, 5, NULL, (GAsyncReadyCallback)set_packet_service_state_sssp_ready, task); } void mm_shared_qmi_set_packet_service_state (MMIfaceModem3gpp *self, MMModem3gppPacketServiceState packet_service_state, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; SetPacketServiceStateContext *ctx; QmiClient *client = NULL; Private *priv = NULL; g_assert ((packet_service_state == MM_MODEM_3GPP_PACKET_SERVICE_STATE_ATTACHED) || (packet_service_state == MM_MODEM_3GPP_PACKET_SERVICE_STATE_DETACHED)); /* Get NAS client */ if (!mm_shared_qmi_ensure_client (MM_SHARED_QMI (self), QMI_SERVICE_NAS, &client, callback, user_data)) return; task = g_task_new (self, NULL, callback, user_data); ctx = g_slice_new0 (SetPacketServiceStateContext); ctx->client = QMI_CLIENT_NAS (g_object_ref (client)); ctx->packet_service_state = packet_service_state; g_task_set_task_data (task, ctx, (GDestroyNotify)set_packet_service_state_context_free); priv = get_private (MM_SHARED_QMI (self)); if (priv->feature_nas_ssp == FEATURE_SUPPORTED) set_packet_service_state_sssp (task); else set_packet_service_state_ia (task); } /*****************************************************************************/ /* Location: Set SUPL server */ typedef struct { QmiClient *client; gchar *supl; glong indication_id; guint timeout_id; } SetSuplServerContext; static void set_supl_server_context_free (SetSuplServerContext *ctx) { if (ctx->client) { if (ctx->timeout_id) g_source_remove (ctx->timeout_id); if (ctx->indication_id) g_signal_handler_disconnect (ctx->client, ctx->indication_id); g_object_unref (ctx->client); } g_free (ctx->supl); g_slice_free (SetSuplServerContext, ctx); } static GArray * parse_as_utf16_url (const gchar *supl) { GArray *url; gchar *utf16; gsize utf16_len; utf16 = g_convert (supl, -1, "UTF-16BE", "UTF-8", NULL, &utf16_len, NULL); url = g_array_append_vals (g_array_sized_new (FALSE, FALSE, sizeof (guint8), utf16_len), utf16, utf16_len); g_free (utf16); return url; } gboolean mm_shared_qmi_location_set_supl_server_finish (MMIfaceModemLocation *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void pds_set_agps_config_ready (QmiClientPds *client, GAsyncResult *res, GTask *task) { QmiMessagePdsSetAgpsConfigOutput *output; GError *error = NULL; output = qmi_client_pds_set_agps_config_finish (client, res, &error); if (!output) { g_prefix_error (&error, "QMI operation failed: "); g_task_return_error (task, error); g_object_unref (task); return; } if (!qmi_message_pds_set_agps_config_output_get_result (output, &error)) g_task_return_error (task, error); else g_task_return_boolean (task, TRUE); g_object_unref (task); qmi_message_pds_set_agps_config_output_unref (output); } static void pds_set_supl_server (GTask *task) { MMSharedQmi *self; SetSuplServerContext *ctx; QmiMessagePdsSetAgpsConfigInput *input; guint32 ip; guint16 port; GArray *url; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); input = qmi_message_pds_set_agps_config_input_new (); /* For multimode devices, prefer UMTS by default */ if (mm_iface_modem_is_3gpp (MM_IFACE_MODEM (self))) qmi_message_pds_set_agps_config_input_set_network_mode (input, QMI_PDS_NETWORK_MODE_UMTS, NULL); else if (mm_iface_modem_is_cdma (MM_IFACE_MODEM (self))) qmi_message_pds_set_agps_config_input_set_network_mode (input, QMI_PDS_NETWORK_MODE_CDMA, NULL); if (mm_parse_supl_address (ctx->supl, NULL, &ip, &port, NULL)) qmi_message_pds_set_agps_config_input_set_location_server_address (input, ip, port, NULL); else { url = parse_as_utf16_url (ctx->supl); qmi_message_pds_set_agps_config_input_set_location_server_url (input, url, NULL); g_array_unref (url); } qmi_client_pds_set_agps_config ( QMI_CLIENT_PDS (ctx->client), input, 10, NULL, /* cancellable */ (GAsyncReadyCallback)pds_set_agps_config_ready, task); qmi_message_pds_set_agps_config_input_unref (input); } static gboolean loc_location_set_server_indication_timed_out (GTask *task) { SetSuplServerContext *ctx; ctx = g_task_get_task_data (task); ctx->timeout_id = 0; g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_ABORTED, "Failed to receive indication with the server update result"); g_object_unref (task); return G_SOURCE_REMOVE; } static void loc_location_set_server_indication_cb (QmiClientLoc *client, QmiIndicationLocSetServerOutput *output, GTask *task) { QmiLocIndicationStatus status; GError *error = NULL; if (!qmi_indication_loc_set_server_output_get_indication_status (output, &status, &error)) { g_prefix_error (&error, "QMI operation failed: "); goto out; } mm_error_from_qmi_loc_indication_status (status, &error); out: if (error) g_task_return_error (task, error); else g_task_return_boolean (task, TRUE); g_object_unref (task); } static void loc_set_server_ready (QmiClientLoc *client, GAsyncResult *res, GTask *task) { SetSuplServerContext *ctx; QmiMessageLocSetServerOutput *output; GError *error = NULL; ctx = g_task_get_task_data (task); output = qmi_client_loc_set_server_finish (client, res, &error); if (!output) { g_prefix_error (&error, "QMI operation failed: "); g_task_return_error (task, error); g_object_unref (task); return; } if (!qmi_message_loc_set_server_output_get_result (output, &error)) { g_task_return_error (task, error); g_object_unref (task); qmi_message_loc_set_server_output_unref (output); return; } /* The task ownership is shared between signal and timeout; the one which is * scheduled first will cancel the other. */ ctx->indication_id = g_signal_connect (ctx->client, "set-server", G_CALLBACK (loc_location_set_server_indication_cb), task); ctx->timeout_id = g_timeout_add_seconds (10, (GSourceFunc)loc_location_set_server_indication_timed_out, task); qmi_message_loc_set_server_output_unref (output); } static void loc_set_supl_server (GTask *task) { MMSharedQmi *self; SetSuplServerContext *ctx; QmiMessageLocSetServerInput *input; guint32 ip; guint16 port; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); input = qmi_message_loc_set_server_input_new (); /* For multimode devices, prefer UMTS by default */ if (mm_iface_modem_is_3gpp (MM_IFACE_MODEM (self))) qmi_message_loc_set_server_input_set_server_type (input, QMI_LOC_SERVER_TYPE_UMTS_SLP, NULL); else if (mm_iface_modem_is_cdma (MM_IFACE_MODEM (self))) qmi_message_loc_set_server_input_set_server_type (input, QMI_LOC_SERVER_TYPE_CDMA_PDE, NULL); if (mm_parse_supl_address (ctx->supl, NULL, &ip, &port, NULL)) qmi_message_loc_set_server_input_set_ipv4 (input, ip, (guint32) port, NULL); else qmi_message_loc_set_server_input_set_url (input, ctx->supl, NULL); qmi_client_loc_set_server ( QMI_CLIENT_LOC (ctx->client), input, 10, NULL, /* cancellable */ (GAsyncReadyCallback)loc_set_server_ready, task); qmi_message_loc_set_server_input_unref (input); } void mm_shared_qmi_location_set_supl_server (MMIfaceModemLocation *self, const gchar *supl, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; SetSuplServerContext *ctx; QmiClient *client = NULL; task = g_task_new (self, NULL, callback, user_data); ctx = g_slice_new0 (SetSuplServerContext); ctx->supl = g_strdup (supl); g_task_set_task_data (task, ctx, (GDestroyNotify)set_supl_server_context_free); /* Prefer PDS */ client = mm_shared_qmi_peek_client (MM_SHARED_QMI (self), QMI_SERVICE_PDS, MM_PORT_QMI_FLAG_DEFAULT, NULL); if (client) { ctx->client = g_object_ref (client); pds_set_supl_server (task); return; } /* Otherwise LOC */ client = mm_shared_qmi_peek_client (MM_SHARED_QMI (self), QMI_SERVICE_LOC, MM_PORT_QMI_FLAG_DEFAULT, NULL); if (client) { ctx->client = g_object_ref (client); loc_set_supl_server (task); return; } g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't find any PDS/LOC client"); g_object_unref (task); } /*****************************************************************************/ /* Location: Load SUPL server */ typedef struct { QmiClient *client; glong indication_id; guint timeout_id; } LoadSuplServerContext; static void load_supl_server_context_free (LoadSuplServerContext *ctx) { if (ctx->client) { if (ctx->timeout_id) g_source_remove (ctx->timeout_id); if (ctx->indication_id) g_signal_handler_disconnect (ctx->client, ctx->indication_id); g_object_unref (ctx->client); } g_slice_free (LoadSuplServerContext, ctx); } gchar * mm_shared_qmi_location_load_supl_server_finish (MMIfaceModemLocation *self, GAsyncResult *res, GError **error) { return g_task_propagate_pointer (G_TASK (res), error); } static void pds_get_agps_config_ready (QmiClientPds *client, GAsyncResult *res, GTask *task) { QmiMessagePdsGetAgpsConfigOutput *output; GError *error = NULL; guint32 ip = 0; guint32 port = 0; GArray *url = NULL; gchar *str = NULL; output = qmi_client_pds_get_agps_config_finish (client, res, &error); if (!output) { g_prefix_error (&error, "QMI operation failed: "); goto out; } if (!qmi_message_pds_get_agps_config_output_get_result (output, &error)) goto out; /* Prefer IP/PORT to URL */ if (qmi_message_pds_get_agps_config_output_get_location_server_address ( output, &ip, &port, NULL) && ip != 0 && port != 0) { struct in_addr a = { .s_addr = ip }; gchar buf[INET_ADDRSTRLEN + 1]; memset (buf, 0, sizeof (buf)); if (!inet_ntop (AF_INET, &a, buf, sizeof (buf) - 1)) { error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Cannot convert numeric IP address (%u) to string", ip); goto out; } str = g_strdup_printf ("%s:%u", buf, port); goto out; } if (qmi_message_pds_get_agps_config_output_get_location_server_url ( output, &url, NULL) && url->len > 0) { str = g_convert (url->data, url->len, "UTF-8", "UTF-16BE", NULL, NULL, NULL); } if (!str) str = g_strdup (""); out: if (error) g_task_return_error (task, error); else { g_assert (str); g_task_return_pointer (task, str, g_free); } g_object_unref (task); if (output) qmi_message_pds_get_agps_config_output_unref (output); } static void pds_load_supl_server (GTask *task) { MMSharedQmi *self; LoadSuplServerContext *ctx; QmiMessagePdsGetAgpsConfigInput *input; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); input = qmi_message_pds_get_agps_config_input_new (); /* For multimode devices, prefer UMTS by default */ if (mm_iface_modem_is_3gpp (MM_IFACE_MODEM (self))) qmi_message_pds_get_agps_config_input_set_network_mode (input, QMI_PDS_NETWORK_MODE_UMTS, NULL); else if (mm_iface_modem_is_cdma (MM_IFACE_MODEM (self))) qmi_message_pds_get_agps_config_input_set_network_mode (input, QMI_PDS_NETWORK_MODE_CDMA, NULL); qmi_client_pds_get_agps_config ( QMI_CLIENT_PDS (ctx->client), input, 10, NULL, /* cancellable */ (GAsyncReadyCallback)pds_get_agps_config_ready, task); qmi_message_pds_get_agps_config_input_unref (input); } static gboolean loc_location_get_server_indication_timed_out (GTask *task) { LoadSuplServerContext *ctx; ctx = g_task_get_task_data (task); ctx->timeout_id = 0; g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_ABORTED, "Failed to receive indication with the current server settings"); g_object_unref (task); return G_SOURCE_REMOVE; } static void loc_location_get_server_indication_cb (QmiClientLoc *client, QmiIndicationLocGetServerOutput *output, GTask *task) { QmiLocIndicationStatus status; const gchar *url = NULL; guint32 ipv4_address = 0; guint16 ipv4_port = 0; GError *error = NULL; gchar *str = NULL; if (!qmi_indication_loc_get_server_output_get_indication_status (output, &status, &error)) { g_prefix_error (&error, "QMI operation failed: "); goto out; } if (!mm_error_from_qmi_loc_indication_status (status, &error)) goto out; /* Prefer IP/PORT to URL */ if (qmi_indication_loc_get_server_output_get_ipv4 ( output, &ipv4_address, &ipv4_port, NULL) && ipv4_address != 0 && ipv4_port != 0) { struct in_addr a = { .s_addr = ipv4_address }; gchar buf[INET_ADDRSTRLEN + 1]; memset (buf, 0, sizeof (buf)); if (!inet_ntop (AF_INET, &a, buf, sizeof (buf) - 1)) { error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Cannot convert numeric IP address (%u) to string", ipv4_address); goto out; } str = g_strdup_printf ("%s:%u", buf, ipv4_port); goto out; } if (qmi_indication_loc_get_server_output_get_url ( output, &url, NULL) && url && url [0]) { str = g_strdup (url); } if (!str) str = g_strdup (""); out: if (error) g_task_return_error (task, error); else { g_assert (str); g_task_return_pointer (task, str, g_free); } g_object_unref (task); } static void loc_get_server_ready (QmiClientLoc *client, GAsyncResult *res, GTask *task) { LoadSuplServerContext *ctx; QmiMessageLocGetServerOutput *output; GError *error = NULL; ctx = g_task_get_task_data (task); output = qmi_client_loc_get_server_finish (client, res, &error); if (!output) { g_prefix_error (&error, "QMI operation failed: "); g_task_return_error (task, error); g_object_unref (task); return; } if (!qmi_message_loc_get_server_output_get_result (output, &error)) { g_task_return_error (task, error); g_object_unref (task); qmi_message_loc_get_server_output_unref (output); return; } /* The task ownership is shared between signal and timeout; the one which is * scheduled first will cancel the other. */ ctx->indication_id = g_signal_connect (ctx->client, "get-server", G_CALLBACK (loc_location_get_server_indication_cb), task); ctx->timeout_id = g_timeout_add_seconds (10, (GSourceFunc)loc_location_get_server_indication_timed_out, task); qmi_message_loc_get_server_output_unref (output); } static void loc_load_supl_server (GTask *task) { MMSharedQmi *self; LoadSuplServerContext *ctx; QmiMessageLocGetServerInput *input; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); input = qmi_message_loc_get_server_input_new (); /* For multimode devices, prefer UMTS by default */ if (mm_iface_modem_is_3gpp (MM_IFACE_MODEM (self))) qmi_message_loc_get_server_input_set_server_type (input, QMI_LOC_SERVER_TYPE_UMTS_SLP, NULL); else if (mm_iface_modem_is_cdma (MM_IFACE_MODEM (self))) qmi_message_loc_get_server_input_set_server_type (input, QMI_LOC_SERVER_TYPE_CDMA_PDE, NULL); qmi_message_loc_get_server_input_set_server_address_type ( input, (QMI_LOC_SERVER_ADDRESS_TYPE_IPV4 | QMI_LOC_SERVER_ADDRESS_TYPE_URL), NULL); qmi_client_loc_get_server ( QMI_CLIENT_LOC (ctx->client), input, 10, NULL, /* cancellable */ (GAsyncReadyCallback)loc_get_server_ready, task); qmi_message_loc_get_server_input_unref (input); } void mm_shared_qmi_location_load_supl_server (MMIfaceModemLocation *self, GAsyncReadyCallback callback, gpointer user_data) { QmiClient *client; GTask *task; LoadSuplServerContext *ctx; task = g_task_new (self, NULL, callback, user_data); ctx = g_slice_new0 (LoadSuplServerContext); g_task_set_task_data (task, ctx, (GDestroyNotify)load_supl_server_context_free); /* Prefer PDS */ client = mm_shared_qmi_peek_client (MM_SHARED_QMI (self), QMI_SERVICE_PDS, MM_PORT_QMI_FLAG_DEFAULT, NULL); if (client) { ctx->client = g_object_ref (client); pds_load_supl_server (task); return; } /* Otherwise LOC */ client = mm_shared_qmi_peek_client (MM_SHARED_QMI (self), QMI_SERVICE_LOC, MM_PORT_QMI_FLAG_DEFAULT, NULL); if (client) { ctx->client = g_object_ref (client); loc_load_supl_server (task); return; } g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't find any PDS/LOC client"); g_object_unref (task); } /*****************************************************************************/ /* Location: internal helper: stop gps engine */ static gboolean stop_gps_engine_finish (MMSharedQmi *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void pds_gps_service_state_stop_ready (QmiClientPds *client, GAsyncResult *res, GTask *task) { MMSharedQmi *self; Private *priv; QmiMessagePdsSetGpsServiceStateOutput *output; GError *error = NULL; output = qmi_client_pds_set_gps_service_state_finish (client, res, &error); if (!output) { g_prefix_error (&error, "QMI operation failed: "); g_task_return_error (task, error); g_object_unref (task); return; } if (!qmi_message_pds_set_gps_service_state_output_get_result (output, &error)) { if (!g_error_matches (error, QMI_PROTOCOL_ERROR, QMI_PROTOCOL_ERROR_NO_EFFECT)) { g_prefix_error (&error, "Couldn't set GPS service state: "); g_task_return_error (task, error); g_object_unref (task); qmi_message_pds_set_gps_service_state_output_unref (output); return; } g_error_free (error); } qmi_message_pds_set_gps_service_state_output_unref (output); self = g_task_get_source_object (task); priv = get_private (self); if (priv->pds_client) { if (priv->pds_location_event_report_indication_id != 0) { g_signal_handler_disconnect (priv->pds_client, priv->pds_location_event_report_indication_id); priv->pds_location_event_report_indication_id = 0; } g_clear_object (&priv->pds_client); } g_task_return_boolean (task, TRUE); g_object_unref (task); } static void loc_stop_ready (QmiClientLoc *client, GAsyncResult *res, GTask *task) { MMSharedQmi *self; Private *priv; QmiMessageLocStopOutput *output; GError *error = NULL; output = qmi_client_loc_stop_finish (client, res, &error); if (!output) { g_prefix_error (&error, "QMI operation failed: "); g_task_return_error (task, error); g_object_unref (task); return; } if (!qmi_message_loc_stop_output_get_result (output, &error)) { g_prefix_error (&error, "Couldn't stop GPS engine: "); g_task_return_error (task, error); g_object_unref (task); qmi_message_loc_stop_output_unref (output); return; } qmi_message_loc_stop_output_unref (output); self = g_task_get_source_object (task); priv = get_private (self); if (priv->loc_client) { if (priv->loc_location_nmea_indication_id != 0) { g_signal_handler_disconnect (priv->loc_client, priv->loc_location_nmea_indication_id); priv->loc_location_nmea_indication_id = 0; } g_clear_object (&priv->loc_client); } g_task_return_boolean (task, TRUE); g_object_unref (task); } static void stop_gps_engine (MMSharedQmi *self, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; Private *priv; priv = get_private (self); task = g_task_new (self, NULL, callback, user_data); if (priv->pds_client) { QmiMessagePdsSetGpsServiceStateInput *input; input = qmi_message_pds_set_gps_service_state_input_new (); qmi_message_pds_set_gps_service_state_input_set_state (input, FALSE, NULL); qmi_client_pds_set_gps_service_state ( QMI_CLIENT_PDS (priv->pds_client), input, 10, NULL, /* cancellable */ (GAsyncReadyCallback)pds_gps_service_state_stop_ready, task); qmi_message_pds_set_gps_service_state_input_unref (input); return; } if (priv->loc_client) { QmiMessageLocStopInput *input; input = qmi_message_loc_stop_input_new (); qmi_message_loc_stop_input_set_session_id (input, DEFAULT_LOC_SESSION_ID, NULL); qmi_client_loc_stop (QMI_CLIENT_LOC (priv->loc_client), input, 10, NULL, (GAsyncReadyCallback) loc_stop_ready, task); qmi_message_loc_stop_input_unref (input); return; } g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't find any PDS/LOC client"); g_object_unref (task); } /*****************************************************************************/ /* Location: internal helpers: NMEA indication callbacks */ static void pds_location_event_report_indication_cb (QmiClientPds *client, QmiIndicationPdsEventReportOutput *output, MMSharedQmi *self) { QmiPdsPositionSessionStatus session_status; const gchar *nmea; if (qmi_indication_pds_event_report_output_get_position_session_status ( output, &session_status, NULL)) { mm_obj_dbg (self, "[GPS] session status changed: '%s'", qmi_pds_position_session_status_get_string (session_status)); } if (qmi_indication_pds_event_report_output_get_nmea_position ( output, &nmea, NULL)) { mm_obj_dbg (self, "[NMEA] %s", nmea); mm_iface_modem_location_gps_update (MM_IFACE_MODEM_LOCATION (self), nmea); } } static void loc_location_nmea_indication_cb (QmiClientLoc *client, QmiIndicationLocNmeaOutput *output, MMSharedQmi *self) { const gchar *nmea = NULL; qmi_indication_loc_nmea_output_get_nmea_string (output, &nmea, NULL); if (!nmea) return; mm_obj_dbg (self, "[NMEA] %s", nmea); mm_iface_modem_location_gps_update (MM_IFACE_MODEM_LOCATION (self), nmea); } /*****************************************************************************/ /* Location: internal helper: setup minimum required NMEA traces */ typedef struct { QmiClientLoc *client; guint timeout_id; gulong indication_id; } SetupRequiredNmeaTracesContext; static void setup_required_nmea_traces_cleanup_action (SetupRequiredNmeaTracesContext *ctx) { if (ctx->indication_id) { g_signal_handler_disconnect (ctx->client, ctx->indication_id); ctx->indication_id = 0; } if (ctx->timeout_id) { g_source_remove (ctx->timeout_id); ctx->timeout_id = 0; } } static void setup_required_nmea_traces_context_free (SetupRequiredNmeaTracesContext *ctx) { setup_required_nmea_traces_cleanup_action (ctx); g_clear_object (&ctx->client); g_slice_free (SetupRequiredNmeaTracesContext, ctx); } static gboolean setup_required_nmea_traces_finish (MMSharedQmi *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static gboolean setup_required_nmea_traces_timeout (GTask *task) { SetupRequiredNmeaTracesContext *ctx; ctx = g_task_get_task_data (task); g_assert (ctx->timeout_id); setup_required_nmea_traces_cleanup_action (ctx); g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_ABORTED, "Operation timed out"); g_object_unref (task); return G_SOURCE_REMOVE; } static void loc_set_nmea_types_indication_cb (QmiClientLoc *client, QmiIndicationLocSetNmeaTypesOutput *output, GTask *task) { SetupRequiredNmeaTracesContext *ctx; QmiLocIndicationStatus status; GError *error = NULL; ctx = g_task_get_task_data (task); g_assert (ctx->indication_id); setup_required_nmea_traces_cleanup_action (ctx); if (!qmi_indication_loc_set_nmea_types_output_get_indication_status (output, &status, &error) || !mm_error_from_qmi_loc_indication_status (status, &error)) g_task_return_error (task, error); else g_task_return_boolean (task, TRUE); g_object_unref (task); } static void loc_set_nmea_types_ready (QmiClientLoc *client, GAsyncResult *res, GTask *task) { SetupRequiredNmeaTracesContext *ctx; GError *error = NULL; g_autoptr(QmiMessageLocSetNmeaTypesOutput) output = NULL; output = qmi_client_loc_set_nmea_types_finish (client, res, &error); if (!output || !qmi_message_loc_set_nmea_types_output_get_result (output, &error)) { g_task_return_error (task, error); g_object_unref (task); return; } /* The task ownership is shared between signal and timeout; the one which is * scheduled first will cancel the other. */ ctx = g_task_get_task_data (task); g_assert (!ctx->indication_id); ctx->indication_id = g_signal_connect (ctx->client, "set-nmea-types", G_CALLBACK (loc_set_nmea_types_indication_cb), task); g_assert (!ctx->timeout_id); ctx->timeout_id = g_timeout_add_seconds (10, (GSourceFunc)setup_required_nmea_traces_timeout, task); } static void loc_get_nmea_types_indication_cb (QmiClientLoc *client, QmiIndicationLocGetNmeaTypesOutput *output, GTask *task) { SetupRequiredNmeaTracesContext *ctx; QmiLocIndicationStatus status; QmiLocNmeaType nmea_types_mask = 0; QmiLocNmeaType desired_nmea_types_mask = (QMI_LOC_NMEA_TYPE_GGA | QMI_LOC_NMEA_TYPE_GSA | QMI_LOC_NMEA_TYPE_GSV); GError *error = NULL; g_autoptr(QmiMessageLocSetNmeaTypesInput) input = NULL; ctx = g_task_get_task_data (task); g_assert (ctx->indication_id); setup_required_nmea_traces_cleanup_action (ctx); if (!qmi_indication_loc_get_nmea_types_output_get_indication_status (output, &status, &error) || !mm_error_from_qmi_loc_indication_status (status, &error)) { g_task_return_error (task, error); g_object_unref (task); return; } qmi_indication_loc_get_nmea_types_output_get_nmea_types (output, &nmea_types_mask, NULL); /* If the configured NMEA types already include GGA, GSV and GSA, we're fine. For raw * GPS sources GGA is the only required one, the other two are given for completeness */ if ((nmea_types_mask & desired_nmea_types_mask) == desired_nmea_types_mask) { g_task_return_boolean (task, TRUE); g_object_unref (task); return; } input = qmi_message_loc_set_nmea_types_input_new (); qmi_message_loc_set_nmea_types_input_set_nmea_types (input, (nmea_types_mask | desired_nmea_types_mask), NULL); qmi_client_loc_set_nmea_types (ctx->client, input, 10, NULL, (GAsyncReadyCallback)loc_set_nmea_types_ready, task); } static void loc_get_nmea_types_ready (QmiClientLoc *client, GAsyncResult *res, GTask *task) { SetupRequiredNmeaTracesContext *ctx; GError *error = NULL; g_autoptr(QmiMessageLocGetNmeaTypesOutput) output = NULL; output = qmi_client_loc_get_nmea_types_finish (client, res, &error); if (!output || !qmi_message_loc_get_nmea_types_output_get_result (output, &error)) { g_task_return_error (task, error); g_object_unref (task); return; } /* The task ownership is shared between signal and timeout; the one which is * scheduled first will cancel the other. */ ctx = g_task_get_task_data (task); g_assert (!ctx->indication_id); ctx->indication_id = g_signal_connect (ctx->client, "get-nmea-types", G_CALLBACK (loc_get_nmea_types_indication_cb), task); g_assert (!ctx->timeout_id); ctx->timeout_id = g_timeout_add_seconds (10, (GSourceFunc)setup_required_nmea_traces_timeout, task); } static void setup_required_nmea_traces (MMSharedQmi *self, GAsyncReadyCallback callback, gpointer user_data) { QmiClient *client; GTask *task; task = g_task_new (self, NULL, callback, user_data); /* If using PDS, no further setup required */ client = mm_shared_qmi_peek_client (MM_SHARED_QMI (self), QMI_SERVICE_PDS, MM_PORT_QMI_FLAG_DEFAULT, NULL); if (client) { g_task_return_boolean (task, TRUE); g_object_unref (task); return; } /* Otherwise LOC */ client = mm_shared_qmi_peek_client (MM_SHARED_QMI (self), QMI_SERVICE_LOC, MM_PORT_QMI_FLAG_DEFAULT, NULL); if (client) { SetupRequiredNmeaTracesContext *ctx; ctx = g_slice_new0 (SetupRequiredNmeaTracesContext); ctx->client = QMI_CLIENT_LOC (g_object_ref (client)); g_task_set_task_data (task, ctx, (GDestroyNotify)setup_required_nmea_traces_context_free); qmi_client_loc_get_nmea_types (ctx->client, NULL, 10, NULL, (GAsyncReadyCallback)loc_get_nmea_types_ready, task); return; } g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't find any PDS/LOC client"); g_object_unref (task); } /*****************************************************************************/ /* Location: internal helper: start gps engine */ static gboolean start_gps_engine_finish (MMSharedQmi *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void pds_ser_location_ready (QmiClientPds *client, GAsyncResult *res, GTask *task) { MMSharedQmi *self; Private *priv; QmiMessagePdsSetEventReportOutput *output; GError *error = NULL; output = qmi_client_pds_set_event_report_finish (client, res, &error); if (!output) { g_prefix_error (&error, "QMI operation failed: "); g_task_return_error (task, error); g_object_unref (task); return; } if (!qmi_message_pds_set_event_report_output_get_result (output, &error)) { g_prefix_error (&error, "Couldn't set event report: "); g_task_return_error (task, error); g_object_unref (task); qmi_message_pds_set_event_report_output_unref (output); return; } qmi_message_pds_set_event_report_output_unref (output); self = g_task_get_source_object (task); priv = get_private (self); g_assert (!priv->pds_client); g_assert (priv->pds_location_event_report_indication_id == 0); priv->pds_client = QMI_CLIENT (g_object_ref (client)); priv->pds_location_event_report_indication_id = g_signal_connect (priv->pds_client, "event-report", G_CALLBACK (pds_location_event_report_indication_cb), self); g_task_return_boolean (task, TRUE); g_object_unref (task); } static void pds_auto_tracking_state_start_ready (QmiClientPds *client, GAsyncResult *res, GTask *task) { QmiMessagePdsSetEventReportInput *input; QmiMessagePdsSetAutoTrackingStateOutput *output = NULL; GError *error = NULL; output = qmi_client_pds_set_auto_tracking_state_finish (client, res, &error); if (!output) { g_prefix_error (&error, "QMI operation failed: "); g_task_return_error (task, error); g_object_unref (task); return; } if (!qmi_message_pds_set_auto_tracking_state_output_get_result (output, &error)) { if (!g_error_matches (error, QMI_PROTOCOL_ERROR, QMI_PROTOCOL_ERROR_NO_EFFECT)) { g_prefix_error (&error, "Couldn't set auto-tracking state: "); g_task_return_error (task, error); g_object_unref (task); qmi_message_pds_set_auto_tracking_state_output_unref (output); return; } g_error_free (error); } qmi_message_pds_set_auto_tracking_state_output_unref (output); /* Only gather standard NMEA traces */ input = qmi_message_pds_set_event_report_input_new (); qmi_message_pds_set_event_report_input_set_nmea_position_reporting (input, TRUE, NULL); qmi_client_pds_set_event_report ( client, input, 5, NULL, (GAsyncReadyCallback)pds_ser_location_ready, task); qmi_message_pds_set_event_report_input_unref (input); } static void pds_gps_service_state_start_ready (QmiClientPds *client, GAsyncResult *res, GTask *task) { QmiMessagePdsSetAutoTrackingStateInput *input; QmiMessagePdsSetGpsServiceStateOutput *output; GError *error = NULL; output = qmi_client_pds_set_gps_service_state_finish (client, res, &error); if (!output) { g_prefix_error (&error, "QMI operation failed: "); g_task_return_error (task, error); g_object_unref (task); return; } if (!qmi_message_pds_set_gps_service_state_output_get_result (output, &error)) { if (!g_error_matches (error, QMI_PROTOCOL_ERROR, QMI_PROTOCOL_ERROR_NO_EFFECT)) { g_prefix_error (&error, "Couldn't set GPS service state: "); g_task_return_error (task, error); g_object_unref (task); qmi_message_pds_set_gps_service_state_output_unref (output); return; } g_error_free (error); } qmi_message_pds_set_gps_service_state_output_unref (output); /* Enable auto-tracking for a continuous fix */ input = qmi_message_pds_set_auto_tracking_state_input_new (); qmi_message_pds_set_auto_tracking_state_input_set_state (input, TRUE, NULL); qmi_client_pds_set_auto_tracking_state ( client, input, 10, NULL, /* cancellable */ (GAsyncReadyCallback)pds_auto_tracking_state_start_ready, task); qmi_message_pds_set_auto_tracking_state_input_unref (input); } static void loc_register_events_ready (QmiClientLoc *client, GAsyncResult *res, GTask *task) { MMSharedQmi *self; Private *priv; QmiMessageLocRegisterEventsOutput *output; GError *error = NULL; output = qmi_client_loc_register_events_finish (client, res, &error); if (!output) { g_prefix_error (&error, "QMI operation failed: "); g_task_return_error (task, error); g_object_unref (task); return; } if (!qmi_message_loc_register_events_output_get_result (output, &error)) { g_prefix_error (&error, "Couldn't not register tracking events: "); g_task_return_error (task, error); g_object_unref (task); qmi_message_loc_register_events_output_unref (output); return; } qmi_message_loc_register_events_output_unref (output); self = g_task_get_source_object (task); priv = get_private (self); g_assert (!priv->loc_client); g_assert (!priv->loc_location_nmea_indication_id); priv->loc_client = QMI_CLIENT (g_object_ref (client)); priv->loc_location_nmea_indication_id = g_signal_connect (client, "nmea", G_CALLBACK (loc_location_nmea_indication_cb), self); g_task_return_boolean (task, TRUE); g_object_unref (task); } static void loc_start_ready (QmiClientLoc *client, GAsyncResult *res, GTask *task) { QmiMessageLocRegisterEventsInput *input; QmiMessageLocStartOutput *output; GError *error = NULL; output = qmi_client_loc_start_finish (client, res, &error); if (!output) { g_prefix_error (&error, "QMI operation failed: "); g_task_return_error (task, error); g_object_unref (task); return; } if (!qmi_message_loc_start_output_get_result (output, &error)) { g_prefix_error (&error, "Couldn't start GPS engine: "); g_task_return_error (task, error); g_object_unref (task); qmi_message_loc_start_output_unref (output); return; } qmi_message_loc_start_output_unref (output); input = qmi_message_loc_register_events_input_new (); qmi_message_loc_register_events_input_set_event_registration_mask ( input, QMI_LOC_EVENT_REGISTRATION_FLAG_NMEA, NULL); qmi_client_loc_register_events (client, input, 10, NULL, (GAsyncReadyCallback) loc_register_events_ready, task); qmi_message_loc_register_events_input_unref (input); } static void start_gps_engine (MMSharedQmi *self, GAsyncReadyCallback callback, gpointer user_data) { QmiClient *client; GTask *task; task = g_task_new (self, NULL, callback, user_data); /* Prefer PDS */ client = mm_shared_qmi_peek_client (MM_SHARED_QMI (self), QMI_SERVICE_PDS, MM_PORT_QMI_FLAG_DEFAULT, NULL); if (client) { QmiMessagePdsSetGpsServiceStateInput *input; input = qmi_message_pds_set_gps_service_state_input_new (); qmi_message_pds_set_gps_service_state_input_set_state (input, TRUE, NULL); qmi_client_pds_set_gps_service_state ( QMI_CLIENT_PDS (client), input, 10, NULL, /* cancellable */ (GAsyncReadyCallback)pds_gps_service_state_start_ready, task); qmi_message_pds_set_gps_service_state_input_unref (input); return; } /* Otherwise LOC */ client = mm_shared_qmi_peek_client (MM_SHARED_QMI (self), QMI_SERVICE_LOC, MM_PORT_QMI_FLAG_DEFAULT, NULL); if (client) { QmiMessageLocStartInput *input; input = qmi_message_loc_start_input_new (); qmi_message_loc_start_input_set_session_id (input, DEFAULT_LOC_SESSION_ID, NULL); qmi_message_loc_start_input_set_intermediate_report_state (input, QMI_LOC_INTERMEDIATE_REPORT_STATE_DISABLE, NULL); qmi_message_loc_start_input_set_minimum_interval_between_position_reports (input, 1000, NULL); qmi_message_loc_start_input_set_fix_recurrence_type (input, QMI_LOC_FIX_RECURRENCE_TYPE_REQUEST_PERIODIC_FIXES, NULL); qmi_client_loc_start (QMI_CLIENT_LOC (client), input, 10, NULL, (GAsyncReadyCallback) loc_start_ready, task); qmi_message_loc_start_input_unref (input); return; } g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't find any PDS/LOC client"); g_object_unref (task); } /*****************************************************************************/ /* Location: internal helper: select operation mode (msa/msb/standalone) */ typedef enum { GPS_OPERATION_MODE_UNKNOWN, GPS_OPERATION_MODE_STANDALONE, GPS_OPERATION_MODE_AGPS_MSA, GPS_OPERATION_MODE_AGPS_MSB, } GpsOperationMode; typedef struct { QmiClient *client; GpsOperationMode mode; glong indication_id; guint timeout_id; } SetGpsOperationModeContext; static void set_gps_operation_mode_context_free (SetGpsOperationModeContext *ctx) { if (ctx->client) { if (ctx->timeout_id) g_source_remove (ctx->timeout_id); if (ctx->indication_id) g_signal_handler_disconnect (ctx->client, ctx->indication_id); g_object_unref (ctx->client); } g_slice_free (SetGpsOperationModeContext, ctx); } static gboolean set_gps_operation_mode_finish (MMSharedQmi *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void pds_set_default_tracking_session_ready (QmiClientPds *client, GAsyncResult *res, GTask *task) { MMSharedQmi *self; SetGpsOperationModeContext *ctx; QmiMessagePdsSetDefaultTrackingSessionOutput *output; GError *error = NULL; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); output = qmi_client_pds_set_default_tracking_session_finish (client, res, &error); if (!output) { g_prefix_error (&error, "QMI operation failed: "); g_task_return_error (task, error); g_object_unref (task); return; } if (!qmi_message_pds_set_default_tracking_session_output_get_result (output, &error)) { g_prefix_error (&error, "Couldn't set default tracking session: "); g_task_return_error (task, error); g_object_unref (task); qmi_message_pds_set_default_tracking_session_output_unref (output); return; } qmi_message_pds_set_default_tracking_session_output_unref (output); switch (ctx->mode) { case GPS_OPERATION_MODE_AGPS_MSA: mm_obj_dbg (self, "MSA A-GPS operation mode enabled"); break; case GPS_OPERATION_MODE_AGPS_MSB: mm_obj_dbg (self, "MSB A-GPS operation mode enabled"); break; case GPS_OPERATION_MODE_STANDALONE: mm_obj_dbg (self, "standalone mode enabled (A-GPS disabled)"); break; case GPS_OPERATION_MODE_UNKNOWN: default: g_assert_not_reached (); } g_task_return_boolean (task, TRUE); g_object_unref (task); } static void pds_get_default_tracking_session_ready (QmiClientPds *client, GAsyncResult *res, GTask *task) { MMSharedQmi *self; SetGpsOperationModeContext *ctx; QmiMessagePdsSetDefaultTrackingSessionInput *input; QmiMessagePdsGetDefaultTrackingSessionOutput *output; GError *error = NULL; QmiPdsOperatingMode session_operation; guint8 data_timeout; guint32 interval; guint32 accuracy_threshold; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); output = qmi_client_pds_get_default_tracking_session_finish (client, res, &error); if (!output) { g_prefix_error (&error, "QMI operation failed: "); g_task_return_error (task, error); g_object_unref (task); return; } if (!qmi_message_pds_get_default_tracking_session_output_get_result (output, &error)) { g_prefix_error (&error, "Couldn't get default tracking session: "); g_task_return_error (task, error); g_object_unref (task); qmi_message_pds_get_default_tracking_session_output_unref (output); return; } qmi_message_pds_get_default_tracking_session_output_get_info ( output, &session_operation, &data_timeout, &interval, &accuracy_threshold, NULL); qmi_message_pds_get_default_tracking_session_output_unref (output); if (ctx->mode == GPS_OPERATION_MODE_AGPS_MSA) { if (session_operation == QMI_PDS_OPERATING_MODE_MS_ASSISTED) { mm_obj_dbg (self, "MSA A-GPS already enabled"); g_task_return_boolean (task, TRUE); g_object_unref (task); return; } mm_obj_dbg (self, "need to enable MSA A-GPS"); session_operation = QMI_PDS_OPERATING_MODE_MS_ASSISTED; } else if (ctx->mode == GPS_OPERATION_MODE_AGPS_MSB) { if (session_operation == QMI_PDS_OPERATING_MODE_MS_BASED) { mm_obj_dbg (self, "MSB A-GPS already enabled"); g_task_return_boolean (task, TRUE); g_object_unref (task); return; } mm_obj_dbg (self, "need to enable MSB A-GPS"); session_operation = QMI_PDS_OPERATING_MODE_MS_BASED; } else if (ctx->mode == GPS_OPERATION_MODE_STANDALONE) { if (session_operation == QMI_PDS_OPERATING_MODE_STANDALONE) { mm_obj_dbg (self, "A-GPS already disabled"); g_task_return_boolean (task, TRUE); g_object_unref (task); return; } mm_obj_dbg (self, "need to disable A-GPS"); session_operation = QMI_PDS_OPERATING_MODE_STANDALONE; } else g_assert_not_reached (); input = qmi_message_pds_set_default_tracking_session_input_new (); qmi_message_pds_set_default_tracking_session_input_set_info ( input, session_operation, data_timeout, interval, accuracy_threshold, NULL); qmi_client_pds_set_default_tracking_session ( client, input, 10, NULL, /* cancellable */ (GAsyncReadyCallback)pds_set_default_tracking_session_ready, task); qmi_message_pds_set_default_tracking_session_input_unref (input); } static gboolean loc_location_operation_mode_indication_timed_out (GTask *task) { SetGpsOperationModeContext *ctx; ctx = g_task_get_task_data (task); ctx->timeout_id = 0; g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_ABORTED, "Failed to receive operation mode indication"); g_object_unref (task); return G_SOURCE_REMOVE; } static void loc_location_set_operation_mode_indication_cb (QmiClientLoc *client, QmiIndicationLocSetOperationModeOutput *output, GTask *task) { MMSharedQmi *self; SetGpsOperationModeContext *ctx; QmiLocIndicationStatus status; GError *error = NULL; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); if (!qmi_indication_loc_set_operation_mode_output_get_indication_status (output, &status, &error)) { g_prefix_error (&error, "QMI operation failed: "); g_task_return_error (task, error); g_object_unref (task); return; } if (!mm_error_from_qmi_loc_indication_status (status, &error)) { g_task_return_error (task, error); g_object_unref (task); return; } switch (ctx->mode) { case GPS_OPERATION_MODE_AGPS_MSA: mm_obj_dbg (self, "MSA A-GPS operation mode enabled"); break; case GPS_OPERATION_MODE_AGPS_MSB: mm_obj_dbg (self, "MSB A-GPS operation mode enabled"); break; case GPS_OPERATION_MODE_STANDALONE: mm_obj_dbg (self, "standalone mode enabled (A-GPS disabled)"); break; case GPS_OPERATION_MODE_UNKNOWN: default: g_assert_not_reached (); } g_task_return_boolean (task, TRUE); g_object_unref (task); } static void loc_set_operation_mode_ready (QmiClientLoc *client, GAsyncResult *res, GTask *task) { SetGpsOperationModeContext *ctx; QmiMessageLocSetOperationModeOutput *output; GError *error = NULL; ctx = g_task_get_task_data (task); output = qmi_client_loc_set_operation_mode_finish (client, res, &error); if (!output) { g_prefix_error (&error, "QMI operation failed: "); g_task_return_error (task, error); g_object_unref (task); return; } if (!qmi_message_loc_set_operation_mode_output_get_result (output, &error)) { g_task_return_error (task, error); g_object_unref (task); qmi_message_loc_set_operation_mode_output_unref (output); return; } /* The task ownership is shared between signal and timeout; the one which is * scheduled first will cancel the other. */ ctx->indication_id = g_signal_connect (ctx->client, "set-operation-mode", G_CALLBACK (loc_location_set_operation_mode_indication_cb), task); ctx->timeout_id = g_timeout_add_seconds (10, (GSourceFunc)loc_location_operation_mode_indication_timed_out, task); qmi_message_loc_set_operation_mode_output_unref (output); } static void loc_location_get_operation_mode_indication_cb (QmiClientLoc *client, QmiIndicationLocGetOperationModeOutput *output, GTask *task) { MMSharedQmi *self; SetGpsOperationModeContext *ctx; QmiLocIndicationStatus status; GError *error = NULL; QmiLocOperationMode mode = QMI_LOC_OPERATION_MODE_DEFAULT; QmiMessageLocSetOperationModeInput *input; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); if (!qmi_indication_loc_get_operation_mode_output_get_indication_status (output, &status, &error)) { g_prefix_error (&error, "QMI operation failed: "); g_task_return_error (task, error); g_object_unref (task); return; } if (!mm_error_from_qmi_loc_indication_status (status, &error)) { g_task_return_error (task, error); g_object_unref (task); return; } qmi_indication_loc_get_operation_mode_output_get_operation_mode (output, &mode, NULL); if (ctx->mode == GPS_OPERATION_MODE_AGPS_MSA) { if (mode == QMI_LOC_OPERATION_MODE_MSA) { mm_obj_dbg (self, "MSA A-GPS already enabled"); g_task_return_boolean (task, TRUE); g_object_unref (task); return; } mm_obj_dbg (self, "need to enable MSA A-GPS"); mode = QMI_LOC_OPERATION_MODE_MSA; } else if (ctx->mode == GPS_OPERATION_MODE_AGPS_MSB) { if (mode == QMI_LOC_OPERATION_MODE_MSB) { mm_obj_dbg (self, "MSB A-GPS already enabled"); g_task_return_boolean (task, TRUE); g_object_unref (task); return; } mm_obj_dbg (self, "need to enable MSB A-GPS"); mode = QMI_LOC_OPERATION_MODE_MSB; } else if (ctx->mode == GPS_OPERATION_MODE_STANDALONE) { if (mode == QMI_LOC_OPERATION_MODE_STANDALONE) { mm_obj_dbg (self, "A-GPS already disabled"); g_task_return_boolean (task, TRUE); g_object_unref (task); return; } mm_obj_dbg (self, "need to disable A-GPS"); mode = QMI_LOC_OPERATION_MODE_STANDALONE; } else g_assert_not_reached (); if (ctx->timeout_id) { g_source_remove (ctx->timeout_id); ctx->timeout_id = 0; } if (ctx->indication_id) { g_signal_handler_disconnect (ctx->client, ctx->indication_id); ctx->indication_id = 0; } input = qmi_message_loc_set_operation_mode_input_new (); qmi_message_loc_set_operation_mode_input_set_operation_mode (input, mode, NULL); qmi_client_loc_set_operation_mode ( QMI_CLIENT_LOC (ctx->client), input, 10, NULL, /* cancellable */ (GAsyncReadyCallback)loc_set_operation_mode_ready, task); qmi_message_loc_set_operation_mode_input_unref (input); } static void loc_get_operation_mode_ready (QmiClientLoc *client, GAsyncResult *res, GTask *task) { SetGpsOperationModeContext *ctx; QmiMessageLocGetOperationModeOutput *output; GError *error = NULL; ctx = g_task_get_task_data (task); output = qmi_client_loc_get_operation_mode_finish (client, res, &error); if (!output) { g_prefix_error (&error, "QMI operation failed: "); g_task_return_error (task, error); g_object_unref (task); return; } if (!qmi_message_loc_get_operation_mode_output_get_result (output, &error)) { g_task_return_error (task, error); g_object_unref (task); qmi_message_loc_get_operation_mode_output_unref (output); return; } /* The task ownership is shared between signal and timeout; the one which is * scheduled first will cancel the other. */ ctx->indication_id = g_signal_connect (ctx->client, "get-operation-mode", G_CALLBACK (loc_location_get_operation_mode_indication_cb), task); ctx->timeout_id = g_timeout_add_seconds (10, (GSourceFunc)loc_location_operation_mode_indication_timed_out, task); qmi_message_loc_get_operation_mode_output_unref (output); } static void set_gps_operation_mode (MMSharedQmi *self, GpsOperationMode mode, GAsyncReadyCallback callback, gpointer user_data) { SetGpsOperationModeContext *ctx; GTask *task; QmiClient *client; task = g_task_new (self, NULL, callback, user_data); ctx = g_slice_new0 (SetGpsOperationModeContext); ctx->mode = mode; g_task_set_task_data (task, ctx, (GDestroyNotify)set_gps_operation_mode_context_free); /* Prefer PDS */ client = mm_shared_qmi_peek_client (MM_SHARED_QMI (self), QMI_SERVICE_PDS, MM_PORT_QMI_FLAG_DEFAULT, NULL); if (client) { ctx->client = g_object_ref (client); qmi_client_pds_get_default_tracking_session ( QMI_CLIENT_PDS (ctx->client), NULL, 10, NULL, /* cancellable */ (GAsyncReadyCallback)pds_get_default_tracking_session_ready, task); return; } /* Otherwise LOC */ client = mm_shared_qmi_peek_client (MM_SHARED_QMI (self), QMI_SERVICE_LOC, MM_PORT_QMI_FLAG_DEFAULT, NULL); if (client) { ctx->client = g_object_ref (client); qmi_client_loc_get_operation_mode ( QMI_CLIENT_LOC (ctx->client), NULL, 10, NULL, /* cancellable */ (GAsyncReadyCallback)loc_get_operation_mode_ready, task); return; } g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't find any PDS/LOC client"); g_object_unref (task); } /*****************************************************************************/ /* Location: disable */ gboolean mm_shared_qmi_disable_location_gathering_finish (MMIfaceModemLocation *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void stop_gps_engine_ready (MMSharedQmi *self, GAsyncResult *res, GTask *task) { MMModemLocationSource source; Private *priv; GError *error = NULL; if (!stop_gps_engine_finish (self, res, &error)) { g_task_return_error (task, error); g_object_unref (task); return; } source = (MMModemLocationSource) GPOINTER_TO_UINT (g_task_get_task_data (task)); priv = get_private (self); priv->enabled_sources &= ~source; g_task_return_boolean (task, TRUE); g_object_unref (task); } static void set_gps_operation_mode_standalone_ready (MMSharedQmi *self, GAsyncResult *res, GTask *task) { MMModemLocationSource source; Private *priv; GError *error = NULL; if (!set_gps_operation_mode_finish (self, res, &error)) { g_task_return_error (task, error); g_object_unref (task); return; } source = (MMModemLocationSource) GPOINTER_TO_UINT (g_task_get_task_data (task)); priv = get_private (self); priv->enabled_sources &= ~source; g_task_return_boolean (task, TRUE); g_object_unref (task); } void mm_shared_qmi_disable_location_gathering (MMIfaceModemLocation *_self, MMModemLocationSource source, GAsyncReadyCallback callback, gpointer user_data) { MMSharedQmi *self; Private *priv; GTask *task; MMModemLocationSource tmp; self = MM_SHARED_QMI (_self); priv = get_private (self); task = g_task_new (self, NULL, callback, user_data); g_task_set_task_data (task, GUINT_TO_POINTER (source), NULL); /* NOTE: no parent disable_location_gathering() implementation */ if (!(source & (MM_MODEM_LOCATION_SOURCE_GPS_NMEA | MM_MODEM_LOCATION_SOURCE_GPS_RAW | MM_MODEM_LOCATION_SOURCE_AGPS_MSA | MM_MODEM_LOCATION_SOURCE_AGPS_MSB))) { g_task_return_boolean (task, TRUE); g_object_unref (task); return; } g_assert (!(priv->pds_client && priv->loc_client)); /* Disable A-GPS? */ if (source == MM_MODEM_LOCATION_SOURCE_AGPS_MSA || source == MM_MODEM_LOCATION_SOURCE_AGPS_MSB) { set_gps_operation_mode (self, GPS_OPERATION_MODE_STANDALONE, (GAsyncReadyCallback)set_gps_operation_mode_standalone_ready, task); return; } /* If no more GPS sources enabled, stop GPS */ tmp = priv->enabled_sources; tmp &= ~source; if (!(tmp & (MM_MODEM_LOCATION_SOURCE_GPS_NMEA | MM_MODEM_LOCATION_SOURCE_GPS_RAW))) { stop_gps_engine (self, (GAsyncReadyCallback)stop_gps_engine_ready, task); return; } /* Otherwise, we have more GPS sources enabled, we shouldn't stop GPS, just * return */ priv->enabled_sources &= ~source; g_task_return_boolean (task, TRUE); g_object_unref (task); } /*****************************************************************************/ /* Location: enable */ gboolean mm_shared_qmi_enable_location_gathering_finish (MMIfaceModemLocation *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void start_gps_engine_ready (MMSharedQmi *self, GAsyncResult *res, GTask *task) { MMModemLocationSource source; Private *priv; GError *error = NULL; if (!start_gps_engine_finish (self, res, &error)) { g_task_return_error (task, error); g_object_unref (task); return; } source = (MMModemLocationSource) GPOINTER_TO_UINT (g_task_get_task_data (task)); priv = get_private (self); priv->enabled_sources |= source; g_task_return_boolean (task, TRUE); g_object_unref (task); } static void setup_required_nmea_traces_ready (MMSharedQmi *self, GAsyncResult *res, GTask *task) { g_autoptr(GError) error = NULL; /* don't treat this error as fatal */ if (!setup_required_nmea_traces_finish (self, res, &error)) mm_obj_warn (self, "couldn't setup required NMEA traces: %s", error->message); start_gps_engine (self, (GAsyncReadyCallback)start_gps_engine_ready, task); } static void set_gps_operation_mode_agps_ready (MMSharedQmi *self, GAsyncResult *res, GTask *task) { MMModemLocationSource source; Private *priv; GError *error = NULL; if (!set_gps_operation_mode_finish (self, res, &error)) { g_task_return_error (task, error); g_object_unref (task); return; } source = (MMModemLocationSource) GPOINTER_TO_UINT (g_task_get_task_data (task)); priv = get_private (self); priv->enabled_sources |= source; g_task_return_boolean (task, TRUE); g_object_unref (task); } static void parent_enable_location_gathering_ready (MMIfaceModemLocation *_self, GAsyncResult *res, GTask *task) { MMSharedQmi *self = MM_SHARED_QMI (_self); Private *priv; MMModemLocationSource source; GError *error = NULL; priv = get_private (self); if (!priv->iface_modem_location_parent->enable_location_gathering_finish (_self, res, &error)) { g_task_return_error (task, error); g_object_unref (task); return; } source = (MMModemLocationSource) GPOINTER_TO_UINT (g_task_get_task_data (task)); /* We only consider GPS related sources in this shared QMI implementation */ if (!(source & (MM_MODEM_LOCATION_SOURCE_GPS_NMEA | MM_MODEM_LOCATION_SOURCE_GPS_RAW | MM_MODEM_LOCATION_SOURCE_AGPS_MSA | MM_MODEM_LOCATION_SOURCE_AGPS_MSB))) { g_task_return_boolean (task, TRUE); g_object_unref (task); return; } /* Enabling MSA A-GPS? */ if (source == MM_MODEM_LOCATION_SOURCE_AGPS_MSA) { set_gps_operation_mode (self, GPS_OPERATION_MODE_AGPS_MSA, (GAsyncReadyCallback)set_gps_operation_mode_agps_ready, task); return; } /* Enabling MSB A-GPS? */ if (source == MM_MODEM_LOCATION_SOURCE_AGPS_MSB) { set_gps_operation_mode (self, GPS_OPERATION_MODE_AGPS_MSB, (GAsyncReadyCallback)set_gps_operation_mode_agps_ready, task); return; } /* Only setup NMEA traces and start GPS engine if not done already */ if (!(priv->enabled_sources & (MM_MODEM_LOCATION_SOURCE_GPS_NMEA | MM_MODEM_LOCATION_SOURCE_GPS_RAW))) { setup_required_nmea_traces (self, (GAsyncReadyCallback)setup_required_nmea_traces_ready, task); return; } /* GPS already started, we're done */ priv->enabled_sources |= source; g_task_return_boolean (task, TRUE); g_object_unref (task); } void mm_shared_qmi_enable_location_gathering (MMIfaceModemLocation *self, MMModemLocationSource source, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; Private *priv; task = g_task_new (self, NULL, callback, user_data); g_task_set_task_data (task, GUINT_TO_POINTER (source), NULL); priv = get_private (MM_SHARED_QMI (self)); g_assert (priv->iface_modem_location_parent); g_assert (priv->iface_modem_location_parent->enable_location_gathering); g_assert (priv->iface_modem_location_parent->enable_location_gathering_finish); /* Chain up parent's gathering enable */ priv->iface_modem_location_parent->enable_location_gathering ( self, source, (GAsyncReadyCallback)parent_enable_location_gathering_ready, task); } /*****************************************************************************/ /* Location: load capabilities */ MMModemLocationSource mm_shared_qmi_location_load_capabilities_finish (MMIfaceModemLocation *self, GAsyncResult *res, GError **error) { GError *inner_error = NULL; gssize value; value = g_task_propagate_int (G_TASK (res), &inner_error); if (inner_error) { g_propagate_error (error, inner_error); return MM_MODEM_LOCATION_SOURCE_NONE; } return (MMModemLocationSource)value; } static void parent_load_capabilities_ready (MMIfaceModemLocation *self, GAsyncResult *res, GTask *task) { MMModemLocationSource sources; GError *error = NULL; Private *priv; priv = get_private (MM_SHARED_QMI (self)); sources = priv->iface_modem_location_parent->load_capabilities_finish (self, res, &error); if (error) { g_task_return_error (task, error); g_object_unref (task); return; } /* Now our own checks */ /* If we have support for the PDS or LOC client, GPS and A-GPS location is supported */ if ((mm_shared_qmi_peek_client (MM_SHARED_QMI (self), QMI_SERVICE_PDS, MM_PORT_QMI_FLAG_DEFAULT, NULL)) || (mm_shared_qmi_peek_client (MM_SHARED_QMI (self), QMI_SERVICE_LOC, MM_PORT_QMI_FLAG_DEFAULT, NULL))) sources |= (MM_MODEM_LOCATION_SOURCE_GPS_NMEA | MM_MODEM_LOCATION_SOURCE_GPS_RAW | MM_MODEM_LOCATION_SOURCE_AGPS_MSA | MM_MODEM_LOCATION_SOURCE_AGPS_MSB); /* So we're done, complete */ g_task_return_int (task, sources); g_object_unref (task); } void mm_shared_qmi_location_load_capabilities (MMIfaceModemLocation *self, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; Private *priv; task = g_task_new (self, NULL, callback, user_data); priv = get_private (MM_SHARED_QMI (self)); g_assert (priv->iface_modem_location_parent); g_assert (priv->iface_modem_location_parent->load_capabilities); g_assert (priv->iface_modem_location_parent->load_capabilities_finish); priv->iface_modem_location_parent->load_capabilities (self, (GAsyncReadyCallback)parent_load_capabilities_ready, task); } /*****************************************************************************/ /* Location: load supported assistance data */ gchar ** mm_shared_qmi_location_load_assistance_data_servers_finish (MMIfaceModemLocation *self, GAsyncResult *res, GError **error) { return g_task_propagate_pointer (G_TASK (res), error); } void mm_shared_qmi_location_load_assistance_data_servers (MMIfaceModemLocation *self, GAsyncReadyCallback callback, gpointer user_data) { Private *priv; GTask *task; priv = get_private (MM_SHARED_QMI (self)); task = g_task_new (self, NULL, callback, user_data); g_task_return_pointer (task, g_strdupv (priv->loc_assistance_data_servers), (GDestroyNotify) g_strfreev); g_object_unref (task); } /*****************************************************************************/ /* Location: load supported assistance data */ typedef struct { QmiClientLoc *client; glong indication_id; guint timeout_id; } LoadSupportedAssistanceDataContext; static void load_supported_assistance_data_context_free (LoadSupportedAssistanceDataContext *ctx) { if (ctx->client) { if (ctx->timeout_id) g_source_remove (ctx->timeout_id); if (ctx->indication_id) g_signal_handler_disconnect (ctx->client, ctx->indication_id); g_object_unref (ctx->client); } g_slice_free (LoadSupportedAssistanceDataContext, ctx); } MMModemLocationAssistanceDataType mm_shared_qmi_location_load_supported_assistance_data_finish (MMIfaceModemLocation *self, GAsyncResult *res, GError **error) { GError *inner_error = NULL; gssize value; value = g_task_propagate_int (G_TASK (res), &inner_error); if (inner_error) { g_propagate_error (error, inner_error); return MM_MODEM_LOCATION_ASSISTANCE_DATA_TYPE_NONE; } return (MMModemLocationAssistanceDataType)value; } static gboolean loc_location_get_predicted_orbits_data_source_indication_timed_out (GTask *task) { LoadSupportedAssistanceDataContext *ctx; ctx = g_task_get_task_data (task); ctx->timeout_id = 0; g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_ABORTED, "Failed to receive indication with the predicted orbits data source"); g_object_unref (task); return G_SOURCE_REMOVE; } static void loc_location_get_predicted_orbits_data_source_indication_cb (QmiClientLoc *client, QmiIndicationLocGetPredictedOrbitsDataSourceOutput *output, GTask *task) { MMSharedQmi *self; Private *priv; QmiLocIndicationStatus status; GError *error = NULL; GArray *server_list = NULL; gboolean supported = FALSE; if (!qmi_indication_loc_get_predicted_orbits_data_source_output_get_indication_status (output, &status, &error)) { g_prefix_error (&error, "QMI operation failed: "); goto out; } if (!mm_error_from_qmi_loc_indication_status (status, &error)) goto out; self = g_task_get_source_object (task); priv = get_private (self); if (qmi_indication_loc_get_predicted_orbits_data_source_output_get_server_list ( output, &server_list, NULL) && server_list->len > 0) { guint i; GPtrArray *tmp; tmp = g_ptr_array_sized_new (server_list->len + 1); for (i = 0; i < server_list->len; i++) { const gchar *server; server = g_array_index (server_list, gchar *, i); g_ptr_array_add (tmp, g_strdup (server)); } g_ptr_array_add (tmp, NULL); g_assert (!priv->loc_assistance_data_servers); priv->loc_assistance_data_servers = (gchar **) g_ptr_array_free (tmp, FALSE); supported = TRUE; } if (qmi_indication_loc_get_predicted_orbits_data_source_output_get_allowed_sizes ( output, &priv->loc_assistance_data_max_file_size, &priv->loc_assistance_data_max_part_size, NULL) && priv->loc_assistance_data_max_file_size > 0 && priv->loc_assistance_data_max_part_size > 0) { supported = TRUE; } out: if (error) g_task_return_error (task, error); else if (!supported) g_task_return_int (task, MM_MODEM_LOCATION_ASSISTANCE_DATA_TYPE_NONE); else g_task_return_int (task, MM_MODEM_LOCATION_ASSISTANCE_DATA_TYPE_XTRA); g_object_unref (task); } static void loc_location_get_predicted_orbits_data_source_ready (QmiClientLoc *client, GAsyncResult *res, GTask *task) { LoadSupportedAssistanceDataContext *ctx; QmiMessageLocGetPredictedOrbitsDataSourceOutput *output; GError *error = NULL; ctx = g_task_get_task_data (task); output = qmi_client_loc_get_predicted_orbits_data_source_finish (client, res, &error); if (!output) { g_prefix_error (&error, "QMI operation failed: "); g_task_return_error (task, error); g_object_unref (task); return; } if (!qmi_message_loc_get_predicted_orbits_data_source_output_get_result (output, &error)) { g_task_return_error (task, error); g_object_unref (task); qmi_message_loc_get_predicted_orbits_data_source_output_unref (output); return; } /* The task ownership is shared between signal and timeout; the one which is * scheduled first will cancel the other. */ ctx->indication_id = g_signal_connect (ctx->client, "get-predicted-orbits-data-source", G_CALLBACK (loc_location_get_predicted_orbits_data_source_indication_cb), task); ctx->timeout_id = g_timeout_add_seconds (10, (GSourceFunc)loc_location_get_predicted_orbits_data_source_indication_timed_out, task); qmi_message_loc_get_predicted_orbits_data_source_output_unref (output); } void mm_shared_qmi_location_load_supported_assistance_data (MMIfaceModemLocation *self, GAsyncReadyCallback callback, gpointer user_data) { LoadSupportedAssistanceDataContext *ctx; GTask *task; QmiClient *client; task = g_task_new (self, NULL, callback, user_data); /* If no LOC client, no assistance data right away */ client = mm_shared_qmi_peek_client (MM_SHARED_QMI (self), QMI_SERVICE_LOC, MM_PORT_QMI_FLAG_DEFAULT, NULL); if (!client) { g_task_return_int (task, MM_MODEM_LOCATION_ASSISTANCE_DATA_TYPE_NONE); g_object_unref (task); return; } ctx = g_slice_new0 (LoadSupportedAssistanceDataContext); ctx->client = QMI_CLIENT_LOC (g_object_ref (client)); g_task_set_task_data (task, ctx, (GDestroyNotify)load_supported_assistance_data_context_free); qmi_client_loc_get_predicted_orbits_data_source (ctx->client, NULL, 10, NULL, (GAsyncReadyCallback)loc_location_get_predicted_orbits_data_source_ready, task); } /*****************************************************************************/ /* Location: inject assistance data */ #define MAX_BYTES_PER_REQUEST 1024 typedef struct { QmiClientLoc *client; guint8 *data; goffset data_size; gulong total_parts; guint32 part_size; glong indication_id; guint timeout_id; goffset i; gulong n_part; } InjectAssistanceDataContext; static void inject_assistance_data_context_free (InjectAssistanceDataContext *ctx) { if (ctx->client) { if (ctx->timeout_id) g_source_remove (ctx->timeout_id); if (ctx->indication_id) g_signal_handler_disconnect (ctx->client, ctx->indication_id); g_object_unref (ctx->client); } g_free (ctx->data); g_slice_free (InjectAssistanceDataContext, ctx); } gboolean mm_shared_qmi_location_inject_assistance_data_finish (MMIfaceModemLocation *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static gboolean loc_location_inject_data_indication_timed_out (GTask *task) { InjectAssistanceDataContext *ctx; ctx = g_task_get_task_data (task); ctx->timeout_id = 0; g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_ABORTED, "Failed to receive indication with the server update result"); g_object_unref (task); return G_SOURCE_REMOVE; } static void inject_xtra_data_next (GTask *task); static void loc_location_inject_xtra_data_indication_cb (QmiClientLoc *client, QmiIndicationLocInjectXtraDataOutput *output, GTask *task) { InjectAssistanceDataContext *ctx; QmiLocIndicationStatus status; GError *error = NULL; if (!qmi_indication_loc_inject_xtra_data_output_get_indication_status (output, &status, &error)) { g_prefix_error (&error, "QMI operation failed: "); goto out; } mm_error_from_qmi_loc_indication_status (status, &error); out: if (error) { g_task_return_error (task, error); g_object_unref (task); return; } ctx = g_task_get_task_data (task); g_source_remove (ctx->timeout_id); ctx->timeout_id = 0; g_signal_handler_disconnect (ctx->client, ctx->indication_id); ctx->indication_id = 0; inject_xtra_data_next (task); } static void inject_xtra_data_ready (QmiClientLoc *client, GAsyncResult *res, GTask *task) { QmiMessageLocInjectXtraDataOutput *output; InjectAssistanceDataContext *ctx; GError *error = NULL; ctx = g_task_get_task_data (task); output = qmi_client_loc_inject_xtra_data_finish (client, res, &error); if (!output || !qmi_message_loc_inject_xtra_data_output_get_result (output, &error)) { g_task_return_error (task, error); g_object_unref (task); goto out; } /* The task ownership is shared between signal and timeout; the one which is * scheduled first will cancel the other. */ ctx->indication_id = g_signal_connect (ctx->client, "inject-xtra-data", G_CALLBACK (loc_location_inject_xtra_data_indication_cb), task); ctx->timeout_id = g_timeout_add_seconds (10, (GSourceFunc)loc_location_inject_data_indication_timed_out, task); out: if (output) qmi_message_loc_inject_xtra_data_output_unref (output); } static void inject_xtra_data_next (GTask *task) { MMSharedQmi *self; QmiMessageLocInjectXtraDataInput *input; InjectAssistanceDataContext *ctx; goffset total_bytes_left; gsize count; GArray *data; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); g_assert (ctx->data_size >= ctx->i); total_bytes_left = ctx->data_size - ctx->i; if (total_bytes_left == 0) { g_task_return_boolean (task, TRUE); g_object_unref (task); return; } ctx->n_part++; count = (total_bytes_left >= ctx->part_size) ? ctx->part_size : total_bytes_left; input = qmi_message_loc_inject_xtra_data_input_new (); qmi_message_loc_inject_xtra_data_input_set_total_size ( input, (guint32)ctx->data_size, NULL); qmi_message_loc_inject_xtra_data_input_set_total_parts ( input, (guint16)ctx->total_parts, NULL); qmi_message_loc_inject_xtra_data_input_set_part_number ( input, (guint16)ctx->n_part, NULL); data = g_array_append_vals (g_array_sized_new (FALSE, FALSE, sizeof (guint8), count), &(ctx->data[ctx->i]), count); qmi_message_loc_inject_xtra_data_input_set_part_data ( input, data, NULL); g_array_unref (data); ctx->i += count; mm_obj_dbg (self, "injecting xtra data: %" G_GSIZE_FORMAT " bytes (%u/%u)", count, (guint) ctx->n_part, (guint) ctx->total_parts); qmi_client_loc_inject_xtra_data (ctx->client, input, 10, NULL, (GAsyncReadyCallback) inject_xtra_data_ready, task); qmi_message_loc_inject_xtra_data_input_unref (input); } static void inject_xtra_data (GTask *task) { InjectAssistanceDataContext *ctx; ctx = g_task_get_task_data (task); g_assert (ctx->timeout_id == 0); g_assert (ctx->indication_id == 0); ctx->n_part = 0; ctx->i = 0; inject_xtra_data_next (task); } static void inject_assistance_data_next (GTask *task); static void loc_location_inject_predicted_orbits_data_indication_cb (QmiClientLoc *client, QmiIndicationLocInjectPredictedOrbitsDataOutput *output, GTask *task) { InjectAssistanceDataContext *ctx; QmiLocIndicationStatus status; GError *error = NULL; if (!qmi_indication_loc_inject_predicted_orbits_data_output_get_indication_status (output, &status, &error)) { g_prefix_error (&error, "QMI operation failed: "); goto out; } mm_error_from_qmi_loc_indication_status (status, &error); out: if (error) { g_task_return_error (task, error); g_object_unref (task); return; } ctx = g_task_get_task_data (task); g_source_remove (ctx->timeout_id); ctx->timeout_id = 0; g_signal_handler_disconnect (ctx->client, ctx->indication_id); ctx->indication_id = 0; inject_assistance_data_next (task); } static void inject_predicted_orbits_data_ready (QmiClientLoc *client, GAsyncResult *res, GTask *task) { QmiMessageLocInjectPredictedOrbitsDataOutput *output; InjectAssistanceDataContext *ctx; GError *error = NULL; ctx = g_task_get_task_data (task); output = qmi_client_loc_inject_predicted_orbits_data_finish (client, res, &error); if (!output || !qmi_message_loc_inject_predicted_orbits_data_output_get_result (output, &error)) { /* Try with InjectXtra if InjectPredictedOrbits is unsupported */ if (g_error_matches (error, QMI_PROTOCOL_ERROR, QMI_PROTOCOL_ERROR_NOT_SUPPORTED)) { g_error_free (error); inject_xtra_data (task); goto out; } g_prefix_error (&error, "QMI operation failed: "); g_task_return_error (task, error); g_object_unref (task); goto out; } /* The task ownership is shared between signal and timeout; the one which is * scheduled first will cancel the other. */ ctx->indication_id = g_signal_connect (ctx->client, "inject-predicted-orbits-data", G_CALLBACK (loc_location_inject_predicted_orbits_data_indication_cb), task); ctx->timeout_id = g_timeout_add_seconds (10, (GSourceFunc)loc_location_inject_data_indication_timed_out, task); out: if (output) qmi_message_loc_inject_predicted_orbits_data_output_unref (output); } static void inject_assistance_data_next (GTask *task) { MMSharedQmi *self; QmiMessageLocInjectPredictedOrbitsDataInput *input; InjectAssistanceDataContext *ctx; goffset total_bytes_left; gsize count; GArray *data; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); g_assert (ctx->data_size >= ctx->i); total_bytes_left = ctx->data_size - ctx->i; if (total_bytes_left == 0) { g_task_return_boolean (task, TRUE); g_object_unref (task); return; } ctx->n_part++; count = (total_bytes_left >= ctx->part_size) ? ctx->part_size : total_bytes_left; input = qmi_message_loc_inject_predicted_orbits_data_input_new (); qmi_message_loc_inject_predicted_orbits_data_input_set_format_type ( input, QMI_LOC_PREDICTED_ORBITS_DATA_FORMAT_XTRA, NULL); qmi_message_loc_inject_predicted_orbits_data_input_set_total_size ( input, (guint32)ctx->data_size, NULL); qmi_message_loc_inject_predicted_orbits_data_input_set_total_parts ( input, (guint16)ctx->total_parts, NULL); qmi_message_loc_inject_predicted_orbits_data_input_set_part_number ( input, (guint16)ctx->n_part, NULL); data = g_array_append_vals (g_array_sized_new (FALSE, FALSE, sizeof (guint8), count), &(ctx->data[ctx->i]), count); qmi_message_loc_inject_predicted_orbits_data_input_set_part_data ( input, data, NULL); g_array_unref (data); ctx->i += count; mm_obj_dbg (self, "injecting predicted orbits data: %" G_GSIZE_FORMAT " bytes (%u/%u)", count, (guint) ctx->n_part, (guint) ctx->total_parts); qmi_client_loc_inject_predicted_orbits_data (ctx->client, input, 10, NULL, (GAsyncReadyCallback) inject_predicted_orbits_data_ready, task); qmi_message_loc_inject_predicted_orbits_data_input_unref (input); } void mm_shared_qmi_location_inject_assistance_data (MMIfaceModemLocation *self, const guint8 *data, gsize data_size, GAsyncReadyCallback callback, gpointer user_data) { InjectAssistanceDataContext *ctx; QmiClient *client; GTask *task; Private *priv; if (!mm_shared_qmi_ensure_client (MM_SHARED_QMI (self), QMI_SERVICE_LOC, &client, callback, user_data)) return; priv = get_private (MM_SHARED_QMI (self)); task = g_task_new (self, NULL, callback, user_data); ctx = g_slice_new0 (InjectAssistanceDataContext); ctx->client = QMI_CLIENT_LOC (g_object_ref (client)); ctx->data = g_memdup (data, data_size); ctx->data_size = data_size; ctx->part_size = ((priv->loc_assistance_data_max_part_size > 0) ? priv->loc_assistance_data_max_part_size : MAX_BYTES_PER_REQUEST); g_task_set_task_data (task, ctx, (GDestroyNotify) inject_assistance_data_context_free); if ((ctx->data_size > (G_MAXUINT16 * ctx->part_size)) || ((priv->loc_assistance_data_max_file_size > 0) && (ctx->data_size > priv->loc_assistance_data_max_file_size))) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_TOO_MANY, "Assistance data file is too big"); g_object_unref (task); return; } ctx->total_parts = (ctx->data_size / ctx->part_size); if (ctx->data_size % ctx->part_size) ctx->total_parts++; g_assert (ctx->total_parts <= G_MAXUINT16); mm_obj_dbg (self, "injecting gpsOneXTRA data (%" G_GOFFSET_FORMAT " bytes)...", ctx->data_size); inject_assistance_data_next (task); } /*****************************************************************************/ QmiClient * mm_shared_qmi_peek_client (MMSharedQmi *self, QmiService service, MMPortQmiFlag flag, GError **error) { g_assert (MM_SHARED_QMI_GET_INTERFACE (self)->peek_client); return MM_SHARED_QMI_GET_INTERFACE (self)->peek_client (self, service, flag, error); } gboolean mm_shared_qmi_ensure_client (MMSharedQmi *self, QmiService service, QmiClient **o_client, GAsyncReadyCallback callback, gpointer user_data) { GError *error = NULL; QmiClient *client; client = mm_shared_qmi_peek_client (self, service, MM_PORT_QMI_FLAG_DEFAULT, &error); if (!client) { g_task_report_error (self, callback, user_data, mm_shared_qmi_ensure_client, error); return FALSE; } *o_client = client; return TRUE; } static void shared_qmi_init (gpointer g_iface) { } GType mm_shared_qmi_get_type (void) { static GType shared_qmi_type = 0; if (!G_UNLIKELY (shared_qmi_type)) { static const GTypeInfo info = { sizeof (MMSharedQmi), /* class_size */ shared_qmi_init, /* base_init */ NULL, /* base_finalize */ }; shared_qmi_type = g_type_register_static (G_TYPE_INTERFACE, "MMSharedQmi", &info, 0); g_type_interface_add_prerequisite (shared_qmi_type, MM_TYPE_IFACE_MODEM); g_type_interface_add_prerequisite (shared_qmi_type, MM_TYPE_IFACE_MODEM_3GPP); g_type_interface_add_prerequisite (shared_qmi_type, MM_TYPE_IFACE_MODEM_LOCATION); } return shared_qmi_type; } ModemManager-1.23.4-dev/src/mm-shared-qmi.h000066400000000000000000000540651456466623000203400ustar00rootroot00000000000000/* -*- 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) 2018 Aleksander Morgado * Copyright (c) 2022 Qualcomm Innovation Center, Inc. */ #ifndef MM_SHARED_QMI_H #define MM_SHARED_QMI_H #include #include #define _LIBMM_INSIDE_MM #include #include #include "mm-iface-modem.h" #include "mm-iface-modem-3gpp.h" #include "mm-iface-modem-location.h" #include "mm-port-qmi.h" #define MM_TYPE_SHARED_QMI (mm_shared_qmi_get_type ()) #define MM_SHARED_QMI(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_SHARED_QMI, MMSharedQmi)) #define MM_IS_SHARED_QMI(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_SHARED_QMI)) #define MM_SHARED_QMI_GET_INTERFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE ((obj), MM_TYPE_SHARED_QMI, MMSharedQmi)) typedef struct _MMSharedQmi MMSharedQmi; struct _MMSharedQmi { GTypeInterface g_iface; QmiClient * (* peek_client) (MMSharedQmi *self, QmiService service, MMPortQmiFlag flag, GError **error); /* Peek location interface of the parent class of the object */ MMIfaceModemLocation * (* peek_parent_location_interface) (MMSharedQmi *self); }; GType mm_shared_qmi_get_type (void); G_DEFINE_AUTOPTR_CLEANUP_FUNC (MMSharedQmi, g_object_unref) QmiClient *mm_shared_qmi_peek_client (MMSharedQmi *self, QmiService service, MMPortQmiFlag flag, GError **error); gboolean mm_shared_qmi_ensure_client (MMSharedQmi *self, QmiService service, QmiClient **o_client, GAsyncReadyCallback callback, gpointer user_data); /* Shared QMI 3GPP operations */ void mm_shared_qmi_3gpp_register_in_network (MMIfaceModem3gpp *self, const gchar *operator_id, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); gboolean mm_shared_qmi_3gpp_register_in_network_finish (MMIfaceModem3gpp *self, GAsyncResult *res, GError **error); void mm_shared_qmi_set_packet_service_state (MMIfaceModem3gpp *self, MMModem3gppPacketServiceState packet_service_state, GAsyncReadyCallback callback, gpointer user_data); gboolean mm_shared_qmi_set_packet_service_state_finish (MMIfaceModem3gpp *self, GAsyncResult *res, GError **error); /* Shared QMI device management support */ void mm_shared_qmi_load_supported_capabilities (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data); GArray *mm_shared_qmi_load_supported_capabilities_finish (MMIfaceModem *self, GAsyncResult *res, GError **error); void mm_shared_qmi_load_current_capabilities (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data); MMModemCapability mm_shared_qmi_load_current_capabilities_finish (MMIfaceModem *self, GAsyncResult *res, GError **error); void mm_shared_qmi_set_current_capabilities (MMIfaceModem *self, MMModemCapability capabilities, GAsyncReadyCallback callback, gpointer user_data); gboolean mm_shared_qmi_set_current_capabilities_finish (MMIfaceModem *self, GAsyncResult *res, GError **error); void mm_shared_qmi_load_model (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data); gchar *mm_shared_qmi_load_model_finish (MMIfaceModem *self, GAsyncResult *res, GError **error); void mm_shared_qmi_load_supported_modes (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data); GArray *mm_shared_qmi_load_supported_modes_finish (MMIfaceModem *self, GAsyncResult *res, GError **error); void mm_shared_qmi_load_current_modes (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data); gboolean mm_shared_qmi_load_current_modes_finish (MMIfaceModem *self, GAsyncResult *res, MMModemMode *allowed, MMModemMode *preferred, GError **error); void mm_shared_qmi_set_current_modes (MMIfaceModem *self, MMModemMode allowed, MMModemMode preferred, GAsyncReadyCallback callback, gpointer user_data); gboolean mm_shared_qmi_set_current_modes_finish (MMIfaceModem *self, GAsyncResult *res, GError **error); void mm_shared_qmi_load_supported_bands (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data); GArray *mm_shared_qmi_load_supported_bands_finish (MMIfaceModem *self, GAsyncResult *res, GError **error); void mm_shared_qmi_load_current_bands (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data); GArray *mm_shared_qmi_load_current_bands_finish (MMIfaceModem *self, GAsyncResult *res, GError **error); void mm_shared_qmi_set_current_bands (MMIfaceModem *self, GArray *bands_array, GAsyncReadyCallback callback, gpointer user_data); gboolean mm_shared_qmi_set_current_bands_finish (MMIfaceModem *self, GAsyncResult *res, GError **error); void mm_shared_qmi_reset (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data); gboolean mm_shared_qmi_reset_finish (MMIfaceModem *self, GAsyncResult *res, GError **error); void mm_shared_qmi_factory_reset (MMIfaceModem *self, const gchar *code, GAsyncReadyCallback callback, gpointer user_data); gboolean mm_shared_qmi_factory_reset_finish (MMIfaceModem *self, GAsyncResult *res, GError **error); void mm_shared_qmi_load_carrier_config (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data); gboolean mm_shared_qmi_load_carrier_config_finish (MMIfaceModem *self, GAsyncResult *res, gchar **carrier_config_name, gchar **carrier_config_revision, GError **error); void mm_shared_qmi_setup_carrier_config (MMIfaceModem *self, const gchar *imsi, const gchar *carrier_config_mapping, GAsyncReadyCallback callback, gpointer user_data); gboolean mm_shared_qmi_setup_carrier_config_finish (MMIfaceModem *self, GAsyncResult *res, GError **error); void mm_shared_qmi_load_sim_slots (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data); gboolean mm_shared_qmi_load_sim_slots_finish (MMIfaceModem *self, GAsyncResult *res, GPtrArray **sim_slots, guint *primary_sim_slot, GError **error); void mm_shared_qmi_set_primary_sim_slot (MMIfaceModem *self, guint sim_slot, GAsyncReadyCallback callback, gpointer user_data); gboolean mm_shared_qmi_set_primary_sim_slot_finish (MMIfaceModem *self, GAsyncResult *res, GError **error); void mm_shared_qmi_setup_sim_hot_swap (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data); gboolean mm_shared_qmi_setup_sim_hot_swap_finish (MMIfaceModem *self, GAsyncResult *res, GError **error); /* Shared QMI location support */ void mm_shared_qmi_location_load_capabilities (MMIfaceModemLocation *self, GAsyncReadyCallback callback, gpointer user_data); MMModemLocationSource mm_shared_qmi_location_load_capabilities_finish (MMIfaceModemLocation *self, GAsyncResult *res, GError **error); void mm_shared_qmi_enable_location_gathering (MMIfaceModemLocation *_self, MMModemLocationSource source, GAsyncReadyCallback callback, gpointer user_data); gboolean mm_shared_qmi_enable_location_gathering_finish (MMIfaceModemLocation *self, GAsyncResult *res, GError **error); void mm_shared_qmi_disable_location_gathering (MMIfaceModemLocation *_self, MMModemLocationSource source, GAsyncReadyCallback callback, gpointer user_data); gboolean mm_shared_qmi_disable_location_gathering_finish (MMIfaceModemLocation *self, GAsyncResult *res, GError **error); void mm_shared_qmi_location_load_supl_server (MMIfaceModemLocation *self, GAsyncReadyCallback callback, gpointer user_data); gchar *mm_shared_qmi_location_load_supl_server_finish (MMIfaceModemLocation *self, GAsyncResult *res, GError **error); void mm_shared_qmi_location_set_supl_server (MMIfaceModemLocation *self, const gchar *supl, GAsyncReadyCallback callback, gpointer user_data); gboolean mm_shared_qmi_location_set_supl_server_finish (MMIfaceModemLocation *self, GAsyncResult *res, GError **error); void mm_shared_qmi_location_load_supported_assistance_data (MMIfaceModemLocation *self, GAsyncReadyCallback callback, gpointer user_data); MMModemLocationAssistanceDataType mm_shared_qmi_location_load_supported_assistance_data_finish (MMIfaceModemLocation *self, GAsyncResult *res, GError **error); void mm_shared_qmi_location_inject_assistance_data (MMIfaceModemLocation *self, const guint8 *data, gsize data_size, GAsyncReadyCallback callback, gpointer user_data); gboolean mm_shared_qmi_location_inject_assistance_data_finish (MMIfaceModemLocation *self, GAsyncResult *res, GError **error); void mm_shared_qmi_location_load_assistance_data_servers (MMIfaceModemLocation *self, GAsyncReadyCallback callback, gpointer user_data); gchar **mm_shared_qmi_location_load_assistance_data_servers_finish (MMIfaceModemLocation *self, GAsyncResult *res, GError **error); #endif /* MM_SHARED_QMI_H */ ModemManager-1.23.4-dev/src/mm-shared.h000066400000000000000000000000001456466623000175300ustar00rootroot00000000000000ModemManager-1.23.4-dev/src/mm-sim-mbim.c000066400000000000000000001702711456466623000200110ustar00rootroot00000000000000/* -*- 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) 2013 Aleksander Morgado * Copyright (C) 2022 Google Inc. */ #include #include #include #include #include #include #include #define _LIBMM_INSIDE_MM #include #include "mm-broadband-modem-mbim.h" #include "mm-error-helpers.h" #include "mm-iface-modem.h" #include "mm-log-object.h" #include "mm-modem-helpers-mbim.h" #include "mm-sim-mbim.h" #define MS_UICC_LOW_LEVEL_SUPPORTED_VERSION 0x01 G_DEFINE_TYPE (MMSimMbim, mm_sim_mbim, MM_TYPE_BASE_SIM) struct _MMSimMbimPrivate { gboolean preload; GCancellable *preload_cancellable; GError *preload_error; gchar *imsi; gchar *iccid; GError *iccid_error; MMSimType sim_type; MMSimEsimStatus esim_status; MMSimRemovability removability; GByteArray *application_id; GError *application_id_error; /* need to get notified when a full sync of the info * is needed, so that we clear the preloaded data. */ guint modem_sync_needed_id; }; /*****************************************************************************/ static gboolean peek_device (gpointer self, MbimDevice **o_device, GAsyncReadyCallback callback, gpointer user_data) { MMBaseModem *modem = NULL; MMPortMbim *port; g_object_get (G_OBJECT (self), MM_BASE_SIM_MODEM, &modem, NULL); g_assert (MM_IS_BASE_MODEM (modem)); port = mm_broadband_modem_mbim_peek_port_mbim (MM_BROADBAND_MODEM_MBIM (modem)); g_object_unref (modem); if (!port) { g_task_report_new_error (self, callback, user_data, peek_device, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't peek MBIM port"); return FALSE; } *o_device = mm_port_mbim_peek_device (port); return TRUE; } static void update_modem_unlock_retries (MMSimMbim *self, MbimPinType pin_type, guint32 remaining_attempts) { MMBaseModem *modem = NULL; g_object_get (G_OBJECT (self), MM_BASE_SIM_MODEM, &modem, NULL); g_assert (MM_IS_BASE_MODEM (modem)); mm_broadband_modem_mbim_set_unlock_retries (MM_BROADBAND_MODEM_MBIM (modem), mm_modem_lock_from_mbim_pin_type (pin_type), remaining_attempts); g_object_unref (modem); } /*****************************************************************************/ /* Monitor modem sync signal */ #if defined WITH_SUSPEND_RESUME static void reset_subscriber_info (MMSimMbim *self); static void clear_modem_sync_monitor (MMSimMbim *self) { g_autoptr(MMBaseModem) modem = NULL; if (!self->priv->modem_sync_needed_id) return; g_object_get (G_OBJECT (self), MM_BASE_SIM_MODEM, &modem, NULL); if (g_signal_handler_is_connected (modem, self->priv->modem_sync_needed_id)) g_signal_handler_disconnect (modem, self->priv->modem_sync_needed_id); self->priv->modem_sync_needed_id = 0; } static void setup_modem_sync_monitor (MMSimMbim *self) { g_autoptr(MMBaseModem) modem = NULL; if (self->priv->modem_sync_needed_id) return; g_object_get (G_OBJECT (self), MM_BASE_SIM_MODEM, &modem, NULL); self->priv->modem_sync_needed_id = g_signal_connect_swapped (modem, MM_BROADBAND_MODEM_SIGNAL_SYNC_NEEDED, G_CALLBACK (reset_subscriber_info), self); } #endif /* WITH_SUSPEND_RESUME */ /*****************************************************************************/ /* Preload subscriber info */ static void reset_subscriber_info (MMSimMbim *self) { /* Request to stop any ongoing preload attempt */ g_cancellable_cancel (self->priv->preload_cancellable); g_clear_object (&self->priv->preload_cancellable); /* And reset the info right away */ self->priv->preload = FALSE; g_clear_error (&self->priv->preload_error); g_clear_pointer (&self->priv->imsi, g_free); g_clear_pointer (&self->priv->iccid, g_free); g_clear_error (&self->priv->iccid_error); self->priv->sim_type = MM_SIM_TYPE_UNKNOWN; self->priv->esim_status = MM_SIM_ESIM_STATUS_UNKNOWN; self->priv->removability = MM_SIM_REMOVABILITY_UNKNOWN; g_clear_pointer (&self->priv->application_id, g_byte_array_unref); g_clear_error (&self->priv->application_id_error); } static gboolean preload_subscriber_info_finish (MMSimMbim *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void application_list_query_ready (MbimDevice *device, GAsyncResult *res, GTask *task) { MMSimMbim *self; g_autoptr(MbimMessage) response = NULL; guint32 version; guint32 application_count; guint32 active_application_index; g_autoptr(MbimUiccApplicationArray) applications = NULL; g_autoptr(GError) error = NULL; self = g_task_get_source_object (task); response = mbim_device_command_finish (device, res, &error); if (g_task_return_error_if_cancelled (task)) { g_object_unref (task); return; } g_clear_pointer (&self->priv->application_id, g_byte_array_unref); g_clear_error (&self->priv->application_id_error); self->priv->application_id_error = g_steal_pointer (&error); if (response && mbim_message_response_get_result (response, MBIM_MESSAGE_TYPE_COMMAND_DONE, &self->priv->application_id_error) && mbim_message_ms_uicc_low_level_access_application_list_response_parse ( response, &version, &application_count, &active_application_index, NULL, /* application_list_size_bytes */ &applications, &self->priv->application_id_error)) { mm_obj_dbg (self, "processed MS UICC low level access application list response"); if (version != MS_UICC_LOW_LEVEL_SUPPORTED_VERSION) self->priv->application_id_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "Application list version %u is not supported", version); else if (active_application_index >= application_count) self->priv->application_id_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS, "Invalid active application index: %u >= %u", active_application_index, application_count); else self->priv->application_id = g_byte_array_append (g_byte_array_sized_new (applications[active_application_index]->application_id_size), applications[active_application_index]->application_id, applications[active_application_index]->application_id_size); } /* At this point we just complete, as all the info and errors have already * been stored */ g_clear_object (&self->priv->preload_cancellable); g_task_return_boolean (task, TRUE); g_object_unref (task); } static void uicc_application_list (GTask *task, MbimDevice *device) { g_autoptr(MbimMessage) message = NULL; message = mbim_message_ms_uicc_low_level_access_application_list_query_new (NULL); mbim_device_command (device, message, 10, NULL, (GAsyncReadyCallback)application_list_query_ready, task); } static void subscriber_ready_status_ready (MbimDevice *device, GAsyncResult *res, GTask *task) { MMSimMbim *self; g_autoptr(MbimMessage) response = NULL; g_autofree gchar *raw_iccid = NULL; g_autoptr(GError) error = NULL; self = g_task_get_source_object (task); response = mbim_device_command_finish (device, res, &error); if (g_task_return_error_if_cancelled (task)) { g_object_unref (task); return; } g_clear_error (&self->priv->preload_error); g_clear_pointer (&self->priv->imsi, g_free); g_clear_pointer (&self->priv->iccid, g_free); g_clear_error (&self->priv->iccid_error); self->priv->preload_error = g_steal_pointer (&error); if (response && mbim_message_response_get_result (response, MBIM_MESSAGE_TYPE_COMMAND_DONE, &self->priv->preload_error)) { if (mbim_device_check_ms_mbimex_version (device, 3, 0)) { MbimSubscriberReadyStatusFlag flags = MBIM_SUBSCRIBER_READY_STATUS_FLAG_NONE; MbimSubscriberReadyState ready_state = MBIM_SUBSCRIBER_READY_STATE_NOT_INITIALIZED; if (!mbim_message_ms_basic_connect_v3_subscriber_ready_status_response_parse ( response, &ready_state, &flags, &self->priv->imsi, &raw_iccid, NULL, /* ready_info */ NULL, /* telephone_numbers_count */ NULL, /* telephone_numbers */ &self->priv->preload_error)) g_prefix_error (&self->priv->preload_error, "Failed processing MBIMEx v3.0 subscriber ready status response: "); else { mm_obj_dbg (self, "processed MBIMEx v3.0 subscriber ready status response"); /* SIM type */ if (flags & MBIM_SUBSCRIBER_READY_STATUS_FLAG_ESIM) { self->priv->sim_type = MM_SIM_TYPE_ESIM; /* eSIM status */ if (ready_state == MBIM_SUBSCRIBER_READY_STATE_INITIALIZED) self->priv->esim_status = MM_SIM_ESIM_STATUS_WITH_PROFILES; else if (ready_state == MBIM_SUBSCRIBER_READY_STATE_NO_ESIM_PROFILE) self->priv->esim_status = MM_SIM_ESIM_STATUS_NO_PROFILES; else mm_obj_warn (self, "couldn't load esim status: %s", mbim_subscriber_ready_state_get_string (ready_state)); } else self->priv->sim_type = MM_SIM_TYPE_PHYSICAL; /* Removability */ if (flags & MBIM_SUBSCRIBER_READY_STATUS_FLAG_SIM_REMOVABILITY_KNOWN) { if (flags & MBIM_SUBSCRIBER_READY_STATUS_FLAG_SIM_REMOVABLE) self->priv->removability = MM_SIM_REMOVABILITY_REMOVABLE; else self->priv->removability = MM_SIM_REMOVABILITY_NOT_REMOVABLE; } } } else { if (!mbim_message_subscriber_ready_status_response_parse ( response, NULL, /* ready_state */ &self->priv->imsi, &raw_iccid, NULL, /* ready_info */ NULL, /* telephone_numbers_count */ NULL, /* telephone_numbers */ &self->priv->preload_error)) g_prefix_error (&self->priv->preload_error, "Failed processing subscriber ready status response: "); else mm_obj_dbg (self, "processed subscriber ready status response"); } if (raw_iccid) self->priv->iccid = mm_3gpp_parse_iccid (raw_iccid, &self->priv->iccid_error); } /* Go on to preload next contents */ uicc_application_list (task, device); } static void subscriber_ready_status (GTask *task, MbimDevice *device) { g_autoptr(MbimMessage) message = NULL; message = mbim_message_subscriber_ready_status_query_new (NULL); mbim_device_command (device, message, 10, NULL, (GAsyncReadyCallback)subscriber_ready_status_ready, task); } static void preload_subscriber_info (MMSimMbim *self, GAsyncReadyCallback callback, gpointer user_data) { g_autoptr(GCancellable) cancellable = NULL; GTask *task; MbimDevice *device; if (!peek_device (self, &device, callback, user_data)) return; cancellable = g_cancellable_new (); task = g_task_new (self, cancellable, callback, user_data); /* only preload one single time; the info of the SIM should not * change during runtime, unless we're handling hotplug events */ if (self->priv->preload) { g_task_return_boolean (task, TRUE); g_object_unref (task); return; } self->priv->preload = TRUE; g_assert (!self->priv->preload_cancellable); self->priv->preload_cancellable = g_steal_pointer (&cancellable); #if defined WITH_SUSPEND_RESUME /* If modem reports sync needed, we will reset the preloaded info */ setup_modem_sync_monitor (self); #endif subscriber_ready_status (task, device); } /*****************************************************************************/ /* Load SIM identifier */ static gchar * load_sim_identifier_finish (MMBaseSim *_self, GAsyncResult *res, GError **error) { MMSimMbim *self = MM_SIM_MBIM (_self); if (!preload_subscriber_info_finish (self, res, error)) return NULL; if (self->priv->iccid_error) { g_propagate_error (error, g_error_copy (self->priv->iccid_error)); return NULL; } if (self->priv->preload_error) { g_propagate_error (error, g_error_copy (self->priv->preload_error)); return NULL; } if (!self->priv->iccid) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "SIM iccid not available"); return NULL; } return g_strdup (self->priv->iccid); } static void load_sim_identifier (MMBaseSim *self, GAsyncReadyCallback callback, gpointer user_data) { preload_subscriber_info (MM_SIM_MBIM (self), callback, user_data); } /*****************************************************************************/ /* Load IMSI */ static gchar * load_imsi_finish (MMBaseSim *_self, GAsyncResult *res, GError **error) { MMSimMbim *self = MM_SIM_MBIM (_self); if (!preload_subscriber_info_finish (self, res, error)) return NULL; if (self->priv->preload_error) { g_propagate_error (error, g_error_copy (self->priv->preload_error)); return NULL; } if (!self->priv->imsi) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "SIM imsi not available"); return NULL; } return g_strdup (self->priv->imsi); } static void load_imsi (MMBaseSim *self, GAsyncReadyCallback callback, gpointer user_data) { preload_subscriber_info (MM_SIM_MBIM (self), callback, user_data); } /*****************************************************************************/ /* Load SIM identifier */ static MMSimType load_sim_type_finish (MMBaseSim *_self, GAsyncResult *res, GError **error) { MMSimMbim *self = MM_SIM_MBIM (_self); if (!preload_subscriber_info_finish (self, res, error)) return MM_SIM_TYPE_UNKNOWN; if (self->priv->preload_error) { g_propagate_error (error, g_error_copy (self->priv->preload_error)); return MM_SIM_TYPE_UNKNOWN; } return self->priv->sim_type; } static void load_sim_type (MMBaseSim *self, GAsyncReadyCallback callback, gpointer user_data) { preload_subscriber_info (MM_SIM_MBIM (self), callback, user_data); } /*****************************************************************************/ /* Load eSIM status */ static MMSimEsimStatus load_esim_status_finish (MMBaseSim *_self, GAsyncResult *res, GError **error) { MMSimMbim *self = MM_SIM_MBIM (_self); if (!preload_subscriber_info_finish (self, res, error)) return MM_SIM_ESIM_STATUS_UNKNOWN; if (self->priv->preload_error) { g_propagate_error (error, g_error_copy (self->priv->preload_error)); return MM_SIM_ESIM_STATUS_UNKNOWN; } return self->priv->esim_status; } static void load_esim_status (MMBaseSim *self, GAsyncReadyCallback callback, gpointer user_data) { preload_subscriber_info (MM_SIM_MBIM (self), callback, user_data); } /*****************************************************************************/ /* Load eSIM status */ static MMSimRemovability load_removability_finish (MMBaseSim *_self, GAsyncResult *res, GError **error) { MMSimMbim *self = MM_SIM_MBIM (_self); if (!preload_subscriber_info_finish (self, res, error)) return MM_SIM_REMOVABILITY_UNKNOWN; if (self->priv->preload_error) { g_propagate_error (error, g_error_copy (self->priv->preload_error)); return MM_SIM_REMOVABILITY_UNKNOWN; } return self->priv->removability; } static void load_removability (MMBaseSim *self, GAsyncReadyCallback callback, gpointer user_data) { preload_subscriber_info (MM_SIM_MBIM (self), callback, user_data); } /*****************************************************************************/ /* Load EID */ #define UICC_STATUS_OK 144 #define EID_APDU_HEADER 5 typedef enum { ESIM_CHECK_STEP_FIRST, ESIM_CHECK_STEP_UICC_OPEN_CHANNEL, ESIM_CHECK_STEP_UICC_GET_APDU, ESIM_CHECK_STEP_UICC_CLOSE_CHANNEL, ESIM_CHECK_STEP_LAST } EsimCheckStep; typedef struct { EsimCheckStep step; guint32 channel; guint32 channel_grp; gchar *eid; GError *saved_error; } EsimCheckContext; static void esim_check_context_free (EsimCheckContext *ctx) { g_assert (!ctx->saved_error); g_free (ctx->eid); g_free (ctx); } static gchar * load_eid_finish (MMBaseSim *self, GAsyncResult *res, GError **error) { return g_task_propagate_pointer (G_TASK (res), error); } static void esim_check_step (MbimDevice *device, GTask *task); static void check_uicc_close_channel_ready (MbimDevice *device, GAsyncResult *res, GTask *task) { g_autoptr(MbimMessage) response = NULL; g_autoptr(GError) error = NULL; guint32 status; EsimCheckContext *ctx; ctx = g_task_get_task_data (task); response = mbim_device_command_finish (device, res, &error); if (!response || !mbim_message_response_get_result (response, MBIM_MESSAGE_TYPE_COMMAND_DONE, &error) || !mbim_message_ms_uicc_low_level_access_close_channel_response_parse (response, &status, &error)) { /* if we have a saved error, prefer that one */ if (!ctx->saved_error) ctx->saved_error = g_steal_pointer (&error); } else if (status != UICC_STATUS_OK) { /* if we have a saved error, prefer that one */ if (!ctx->saved_error) ctx->saved_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "UICC close channel failed"); } /* go on to next step */ ctx->step++; esim_check_step (device, task); } static void check_uicc_apdu_ready (MbimDevice *device, GAsyncResult *res, GTask *task) { g_autoptr(MbimMessage) response = NULL; GError *error = NULL; guint32 status; guint32 apdu_response_size; const guint8 *apdu_response = NULL; EsimCheckContext *ctx; ctx = g_task_get_task_data (task); response = mbim_device_command_finish (device, res, &error); if (!response || !mbim_message_response_get_result (response, MBIM_MESSAGE_TYPE_COMMAND_DONE, &error) || !mbim_message_ms_uicc_low_level_access_apdu_response_parse ( response, &status, &apdu_response_size, &apdu_response, &error)) ctx->saved_error = error; else { ctx->eid = mm_decode_eid ((const gchar *)(apdu_response + EID_APDU_HEADER), apdu_response_size - EID_APDU_HEADER); if (!ctx->eid) ctx->saved_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS, "Invalid APDU response: unable to decode EID"); } /* always go on to the close channel step, even on error */ ctx->step++; esim_check_step (device, task); } static void check_uicc_open_channel_ready (MbimDevice *device, GAsyncResult *res, GTask *task) { g_autoptr(MbimMessage) response = NULL; GError *error = NULL; guint32 status; guint32 channel; EsimCheckContext *ctx; ctx = g_task_get_task_data (task); response = mbim_device_command_finish (device, res, &error); if (!response || !mbim_message_response_get_result (response, MBIM_MESSAGE_TYPE_COMMAND_DONE, &error) || !mbim_message_ms_uicc_low_level_access_open_channel_response_parse ( response, &status, &channel, NULL, NULL, &error)) { ctx->saved_error = error; ctx->step = ESIM_CHECK_STEP_LAST; } else if (status != UICC_STATUS_OK) { ctx->saved_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "UICC open channel failed"); ctx->step = ESIM_CHECK_STEP_LAST; } else { /* channel is open, from now on we'll need to always explicitly close, * even on errors */ ctx->channel = channel; ctx->step++; } esim_check_step (device, task); } static void esim_check_step (MbimDevice *device, GTask *task) { MMSimMbim *self; EsimCheckContext *ctx; g_autoptr(MbimMessage) message = NULL; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); /* Don't run new steps if we're cancelled */ if (g_task_return_error_if_cancelled (task)) { g_object_unref (task); return; } switch (ctx->step) { case ESIM_CHECK_STEP_FIRST: ctx->step++; /* fall through */ case ESIM_CHECK_STEP_UICC_OPEN_CHANNEL: { const guint8 app_id[] = {0xa0, 0x00, 0x00, 0x05, 0x59, 0x10, 0x10, 0xff, 0xff, 0xff, 0xff, 0x89, 0x00, 0x00, 0x01, 0x00}; /* Channel group is used to bundle all logical channels opened and for * future reference to close */ ctx->channel_grp = 1; mm_obj_dbg (self, "opening UICC channel..."); message = mbim_message_ms_uicc_low_level_access_open_channel_set_new ( sizeof (app_id), app_id, 4, /* SelectP2Arg: Return File Control Parameters(FCP) template. * Refer 11.1.13 of of the ETSI TS 102 221 technical specification. */ ctx->channel_grp, NULL); mbim_device_command (device, message, 10, NULL, (GAsyncReadyCallback)check_uicc_open_channel_ready, task); return; } case ESIM_CHECK_STEP_UICC_GET_APDU: { const guint8 apdu_cmd[] = {0x81, 0xe2, 0x91, 0x00, 0x06, 0xbf, 0x3e, 0x03, 0x5c, 0x01, 0x5a, 0x00}; mm_obj_dbg (self, "reading EID..."); message = mbim_message_ms_uicc_low_level_access_apdu_set_new ( ctx->channel, MBIM_UICC_SECURE_MESSAGING_NONE, MBIM_UICC_CLASS_BYTE_TYPE_EXTENDED, sizeof (apdu_cmd), apdu_cmd, NULL); mbim_device_command (device, message, 10, NULL, (GAsyncReadyCallback)check_uicc_apdu_ready, task); return; } case ESIM_CHECK_STEP_UICC_CLOSE_CHANNEL: mm_obj_dbg (self, "closing UICC channel..."); message = mbim_message_ms_uicc_low_level_access_close_channel_set_new ( ctx->channel, ctx->channel_grp, NULL); mbim_device_command (device, message, 10, NULL, (GAsyncReadyCallback)check_uicc_close_channel_ready, task); return; case ESIM_CHECK_STEP_LAST: if (ctx->saved_error) g_task_return_error (task, g_steal_pointer (&ctx->saved_error)); else if (ctx->eid) g_task_return_pointer (task, g_steal_pointer (&ctx->eid), g_free); else g_assert_not_reached (); g_object_unref (task); return; default: break; } g_assert_not_reached (); } static void load_eid (MMBaseSim *self, GAsyncReadyCallback callback, gpointer user_data) { MbimDevice *device; GTask *task; EsimCheckContext *ctx; if (!peek_device (self, &device, callback, user_data)) return; task = g_task_new (self, NULL, callback, user_data); ctx = g_new0 (EsimCheckContext, 1); ctx->step = ESIM_CHECK_STEP_FIRST; g_task_set_task_data (task, ctx, (GDestroyNotify)esim_check_context_free); esim_check_step (device, task); } /*****************************************************************************/ /* Load operator identifier */ static gchar * load_operator_identifier_finish (MMBaseSim *self, GAsyncResult *res, GError **error) { return g_task_propagate_pointer (G_TASK (res), error); } static void load_operator_identifier_ready (MbimDevice *device, GAsyncResult *res, GTask *task) { MbimMessage *response; GError *error = NULL; MbimProvider *provider; response = mbim_device_command_finish (device, res, &error); if (response && mbim_message_response_get_result (response, MBIM_MESSAGE_TYPE_COMMAND_DONE, &error) && mbim_message_home_provider_response_parse ( response, &provider, &error)) { g_task_return_pointer (task, g_strdup (provider->provider_id), g_free); mbim_provider_free (provider); } else g_task_return_error (task, error); g_object_unref (task); if (response) mbim_message_unref (response); } static void load_operator_identifier (MMBaseSim *self, GAsyncReadyCallback callback, gpointer user_data) { MbimDevice *device; MbimMessage *message; GTask *task; if (!peek_device (self, &device, callback, user_data)) return; task = g_task_new (self, NULL, callback, user_data); message = mbim_message_home_provider_query_new (NULL); mbim_device_command (device, message, 30, NULL, (GAsyncReadyCallback)load_operator_identifier_ready, task); mbim_message_unref (message); } /*****************************************************************************/ /* Load operator name */ static gchar * load_operator_name_finish (MMBaseSim *self, GAsyncResult *res, GError **error) { return g_task_propagate_pointer (G_TASK (res), error); } static void load_operator_name_ready (MbimDevice *device, GAsyncResult *res, GTask *task) { MbimMessage *response; GError *error = NULL; MbimProvider *provider; response = mbim_device_command_finish (device, res, &error); if (response && mbim_message_response_get_result (response, MBIM_MESSAGE_TYPE_COMMAND_DONE, &error) && mbim_message_home_provider_response_parse ( response, &provider, &error)) { g_task_return_pointer (task, g_strdup (provider->provider_name), g_free); mbim_provider_free (provider); } else g_task_return_error (task, error); g_object_unref (task); if (response) mbim_message_unref (response); } static void load_operator_name (MMBaseSim *self, GAsyncReadyCallback callback, gpointer user_data) { MbimDevice *device; MbimMessage *message; GTask *task; if (!peek_device (self, &device, callback, user_data)) return; task = g_task_new (self, NULL, callback, user_data); message = mbim_message_home_provider_query_new (NULL); mbim_device_command (device, message, 30, NULL, (GAsyncReadyCallback)load_operator_name_ready, task); mbim_message_unref (message); } /*****************************************************************************/ /* Common method to read transparent files */ typedef struct { MbimDevice *device; GByteArray *file_path; } CommonReadBinaryContext; static void common_read_binary_context_free (CommonReadBinaryContext *ctx) { g_byte_array_unref (ctx->file_path); g_object_unref (ctx->device); g_slice_free (CommonReadBinaryContext, ctx); } static GByteArray * common_read_binary_finish (MMSimMbim *self, GAsyncResult *res, GError **error) { return g_task_propagate_pointer (G_TASK (res), error); } static void read_binary_query_ready (MbimDevice *device, GAsyncResult *res, GTask *task) { g_autoptr(MbimMessage) response = NULL; GError *error = NULL; const guint8 *data; guint32 data_size; response = mbim_device_command_finish (device, res, &error); if (!response || !mbim_message_response_get_result (response, MBIM_MESSAGE_TYPE_COMMAND_DONE, &error) || !mbim_message_ms_uicc_low_level_access_read_binary_response_parse ( response, NULL, /* version */ NULL, /* status_word_1 */ NULL, /* status_word_2 */ &data_size, &data, &error)) g_task_return_error (task, error); else if (!data_size || !data) g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "data not available"); else g_task_return_pointer (task, g_byte_array_append (g_byte_array_sized_new (data_size), data, data_size), (GDestroyNotify)g_byte_array_unref); g_object_unref (task); } static void file_status_query_ready (MbimDevice *device, GAsyncResult *res, GTask *task) { g_autoptr(MbimMessage) request = NULL; g_autoptr(MbimMessage) response = NULL; MMSimMbim *self; CommonReadBinaryContext *ctx; GError *error = NULL; guint32 file_item_count; guint32 file_item_size; guint64 read_size; self = g_task_get_source_object (task); ctx = (CommonReadBinaryContext *) g_task_get_task_data (task); response = mbim_device_command_finish (device, res, &error); if (!response || !mbim_message_response_get_result (response, MBIM_MESSAGE_TYPE_COMMAND_DONE, &error) || !mbim_message_ms_uicc_low_level_access_file_status_response_parse ( response, NULL, /* version */ NULL, /* status_word_1 */ NULL, /* status_word_2 */ NULL, /* file_accessibility */ NULL, /* file_type */ NULL, /* file_structure */ &file_item_count, &file_item_size, NULL, /* access_condition_read */ NULL, /* access_condition_update */ NULL, /* access_condition_activate */ NULL, /* access_condition_deactivate */ &error)) { g_task_return_error (task, error); g_object_unref (task); return; } if (!file_item_size || !file_item_count) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "file contents not available"); g_object_unref (task); return; } /* Fail if we attempt to read too much. UICC operations can read up to * 255 bytes at a time, and the SIM files we try to process are all * (or at least should be) small. Use a 64bit value to avoid overflowing * if the modem returns weird size/count values. */ read_size = file_item_size * file_item_count; if (read_size > 255) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "file size too big: item size %u, item count %u", file_item_size, file_item_count); g_object_unref (task); return; } request = mbim_message_ms_uicc_low_level_access_read_binary_query_new (MS_UICC_LOW_LEVEL_SUPPORTED_VERSION, self->priv->application_id->len, self->priv->application_id->data, ctx->file_path->len, ctx->file_path->data, 0, /* read_offset */ (guint32)read_size, NULL, /* local_pin */ 0, /* data_size */ NULL, /* data */ NULL); mbim_device_command (ctx->device, request, 10, NULL, (GAsyncReadyCallback)read_binary_query_ready, task); } static void read_binary_subscriber_info_ready (MMSimMbim *self, GAsyncResult *res, GTask *task) { g_autoptr(MbimMessage) request = NULL; CommonReadBinaryContext *ctx; GError *error = NULL; ctx = (CommonReadBinaryContext *) g_task_get_task_data (task); if (!preload_subscriber_info_finish (self, res, &error)) { g_task_return_error (task, error); g_object_unref (task); return; } if (self->priv->application_id_error) { g_task_return_error (task, g_error_copy (self->priv->application_id_error)); g_object_unref (task); return; } if (!self->priv->application_id) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "UICC application id not available"); g_object_unref (task); return; } request = mbim_message_ms_uicc_low_level_access_file_status_query_new (MS_UICC_LOW_LEVEL_SUPPORTED_VERSION, self->priv->application_id->len, self->priv->application_id->data, ctx->file_path->len, ctx->file_path->data, NULL); mbim_device_command (ctx->device, request, 10, NULL, (GAsyncReadyCallback)file_status_query_ready, task); } static void common_read_binary (MMSimMbim *self, const guint8 *file_path, gsize file_path_size, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; MbimDevice *device; CommonReadBinaryContext *ctx; if (!peek_device (self, &device, callback, user_data)) return; task = g_task_new (self, NULL, callback, user_data); ctx = g_slice_new0 (CommonReadBinaryContext); ctx->device = g_object_ref (device); ctx->file_path = g_byte_array_append (g_byte_array_sized_new (file_path_size), file_path, file_path_size); g_task_set_task_data (task, ctx, (GDestroyNotify) common_read_binary_context_free); preload_subscriber_info (self, (GAsyncReadyCallback) read_binary_subscriber_info_ready, task); } /*****************************************************************************/ /* Read GID1 */ static GByteArray * load_gid1_finish (MMBaseSim *self, GAsyncResult *res, GError **error) { return g_task_propagate_pointer (G_TASK (res), error); } static void parent_load_gid1_ready (MMBaseSim *self, GAsyncResult *res, GTask *task) { g_autoptr(GError) error = NULL; GByteArray *value; value = MM_BASE_SIM_CLASS(mm_sim_mbim_parent_class)->load_gid1_finish (self, res, &error); if (value) { g_task_return_pointer (task, value, (GDestroyNotify)g_byte_array_unref); } else { mm_obj_dbg (self, "failed reading GID1 using AT: %s", error->message); g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Failed reading GID1 from SIM card"); } g_object_unref (task); } static void common_read_binary_gid1_ready (MMSimMbim *self, GAsyncResult *res, GTask *task) { g_autoptr(GError) error = NULL; GByteArray *value; value = common_read_binary_finish (self, res, &error); if (value) { g_task_return_pointer (task, value, (GDestroyNotify)g_byte_array_unref); g_object_unref (task); return; } /* Fallback to parent implementation if possible */ mm_obj_dbg (self, "failed reading GID1 using MBIM: %s", error->message); MM_BASE_SIM_CLASS(mm_sim_mbim_parent_class)->load_gid1 (MM_BASE_SIM (self), (GAsyncReadyCallback)parent_load_gid1_ready, task); } static void load_gid1 (MMBaseSim *self, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; const guint8 file_path[] = { 0x7F, 0xFF, 0x6F, 0x3E }; task = g_task_new (self, NULL, callback, user_data); common_read_binary (MM_SIM_MBIM (self), file_path, G_N_ELEMENTS (file_path), (GAsyncReadyCallback)common_read_binary_gid1_ready, task); } /*****************************************************************************/ /* Read GID2 */ static GByteArray * load_gid2_finish (MMBaseSim *self, GAsyncResult *res, GError **error) { return g_task_propagate_pointer (G_TASK (res), error); } static void parent_load_gid2_ready (MMBaseSim *self, GAsyncResult *res, GTask *task) { g_autoptr(GError) error = NULL; GByteArray *value; value = MM_BASE_SIM_CLASS(mm_sim_mbim_parent_class)->load_gid2_finish (self, res, &error); if (value) { g_task_return_pointer (task, value, (GDestroyNotify)g_byte_array_unref); } else { mm_obj_dbg (self, "failed reading GID2 using AT: %s", error->message); g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Failed reading GID2 from SIM card"); } g_object_unref (task); } static void common_read_binary_gid2_ready (MMSimMbim *self, GAsyncResult *res, GTask *task) { g_autoptr(GError) error = NULL; GByteArray *value; value = common_read_binary_finish (self, res, &error); if (value) { g_task_return_pointer (task, value, (GDestroyNotify)g_byte_array_unref); g_object_unref (task); return; } /* Fallback to parent implementation if possible */ mm_obj_dbg (self, "failed reading GID2 using MBIM: %s", error->message); MM_BASE_SIM_CLASS(mm_sim_mbim_parent_class)->load_gid2 (MM_BASE_SIM (self), (GAsyncReadyCallback)parent_load_gid2_ready, task); } static void load_gid2 (MMBaseSim *self, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; const guint8 file_path[] = { 0x7F, 0xFF, 0x6F, 0x3F }; task = g_task_new (self, NULL, callback, user_data); common_read_binary (MM_SIM_MBIM (self), file_path, G_N_ELEMENTS (file_path), (GAsyncReadyCallback)common_read_binary_gid2_ready, task); } /*****************************************************************************/ /* Send PIN */ static gboolean send_pin_finish (MMBaseSim *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void pin_set_enter_ready (MbimDevice *device, GAsyncResult *res, GTask *task) { MMSimMbim *self; GError *error = NULL; MbimMessage *response; gboolean success; MbimPinType pin_type; MbimPinState pin_state; guint32 remaining_attempts; self = g_task_get_source_object (task); response = mbim_device_command_finish (device, res, &error); if (response) { success = mbim_message_response_get_result (response, MBIM_MESSAGE_TYPE_COMMAND_DONE, &error); if (mbim_message_pin_response_parse (response, &pin_type, &pin_state, &remaining_attempts, NULL)) { update_modem_unlock_retries (self, pin_type, remaining_attempts); if (!success) { /* Sending PIN failed, build a better error to report */ if (pin_type == MBIM_PIN_TYPE_PIN1 && pin_state == MBIM_PIN_STATE_LOCKED) { g_error_free (error); error = mm_mobile_equipment_error_for_code (MM_MOBILE_EQUIPMENT_ERROR_INCORRECT_PASSWORD, self); } else if (pin_type == MBIM_PIN_TYPE_PUK1 && pin_state == MBIM_PIN_STATE_LOCKED) { g_error_free (error); error = mm_mobile_equipment_error_for_code (MM_MOBILE_EQUIPMENT_ERROR_SIM_PUK, self); } } } mbim_message_unref (response); } if (error) { g_task_return_error (task, error); } else { g_task_return_boolean (task, TRUE); /* Reset cached SIM subscriber info */ reset_subscriber_info (self); } g_object_unref (task); } static void send_pin (MMBaseSim *self, const gchar *pin, GAsyncReadyCallback callback, gpointer user_data) { MbimDevice *device; MbimMessage *message; GTask *task; GError *error = NULL; if (!peek_device (self, &device, callback, user_data)) return; task = g_task_new (self, NULL, callback, user_data); mm_obj_dbg (self, "sending PIN..."); message = (mbim_message_pin_set_new ( MBIM_PIN_TYPE_PIN1, MBIM_PIN_OPERATION_ENTER, pin, "", &error)); if (!message) { g_task_return_error (task, error); g_object_unref (task); return; } mbim_device_command (device, message, 10, NULL, (GAsyncReadyCallback)pin_set_enter_ready, task); mbim_message_unref (message); } /*****************************************************************************/ /* Send PUK */ static gboolean send_puk_finish (MMBaseSim *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void puk_set_enter_ready (MbimDevice *device, GAsyncResult *res, GTask *task) { MMSimMbim *self; GError *error = NULL; MbimMessage *response; gboolean success; MbimPinType pin_type; MbimPinState pin_state; guint32 remaining_attempts; self = g_task_get_source_object (task); response = mbim_device_command_finish (device, res, &error); if (response) { success = mbim_message_response_get_result (response, MBIM_MESSAGE_TYPE_COMMAND_DONE, &error); if (mbim_message_pin_response_parse (response, &pin_type, &pin_state, &remaining_attempts, NULL)) { update_modem_unlock_retries (self, pin_type, remaining_attempts); if (!success) { /* Sending PUK failed, build a better error to report */ if (pin_type == MBIM_PIN_TYPE_PUK1 && pin_state == MBIM_PIN_STATE_LOCKED) { g_error_free (error); if (remaining_attempts == 0) error = mm_mobile_equipment_error_for_code (MM_MOBILE_EQUIPMENT_ERROR_SIM_WRONG, self); else error = mm_mobile_equipment_error_for_code (MM_MOBILE_EQUIPMENT_ERROR_INCORRECT_PASSWORD, self); } } } mbim_message_unref (response); } if (error) { g_task_return_error (task, error); } else { g_task_return_boolean (task, TRUE); /* Reset cached SIM subscriber info */ reset_subscriber_info (self); } g_object_unref (task); } static void send_puk (MMBaseSim *self, const gchar *puk, const gchar *new_pin, GAsyncReadyCallback callback, gpointer user_data) { MbimDevice *device; MbimMessage *message; GTask *task; GError *error = NULL; if (!peek_device (self, &device, callback, user_data)) return; task = g_task_new (self, NULL, callback, user_data); mm_obj_dbg (self, "sending PUK..."); message = (mbim_message_pin_set_new ( MBIM_PIN_TYPE_PUK1, MBIM_PIN_OPERATION_ENTER, puk, new_pin, &error)); if (!message) { g_task_return_error (task, error); g_object_unref (task); return; } mbim_device_command (device, message, 10, NULL, (GAsyncReadyCallback)puk_set_enter_ready, task); mbim_message_unref (message); } /*****************************************************************************/ /* Enable PIN */ static gboolean enable_pin_finish (MMBaseSim *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void pin_set_enable_ready (MbimDevice *device, GAsyncResult *res, GTask *task) { MMSimMbim *self; GError *error = NULL; MbimMessage *response; MbimPinType pin_type; guint32 remaining_attempts; self = g_task_get_source_object (task); response = mbim_device_command_finish (device, res, &error); if (response) { mbim_message_response_get_result (response, MBIM_MESSAGE_TYPE_COMMAND_DONE, &error); if (mbim_message_pin_response_parse (response, &pin_type, NULL, &remaining_attempts, NULL)) update_modem_unlock_retries (self, pin_type, remaining_attempts); mbim_message_unref (response); } if (error) { if (g_error_matches (error, MBIM_STATUS_ERROR, MBIM_STATUS_ERROR_PIN_REQUIRED)) { g_error_free (error); error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_WRONG_STATE, "Need to be unlocked to allow enabling/disabling PIN"); } g_task_return_error (task, error); } else g_task_return_boolean (task, TRUE); g_object_unref (task); } static void enable_pin (MMBaseSim *self, const gchar *pin, gboolean enabled, GAsyncReadyCallback callback, gpointer user_data) { MbimDevice *device; MbimMessage *message; GTask *task; GError *error = NULL; if (!peek_device (self, &device, callback, user_data)) return; task = g_task_new (self, NULL, callback, user_data); mm_obj_dbg (self, "%s PIN ...", enabled ? "enabling" : "disabling"); message = (mbim_message_pin_set_new ( MBIM_PIN_TYPE_PIN1, enabled ? MBIM_PIN_OPERATION_ENABLE : MBIM_PIN_OPERATION_DISABLE, pin, "", &error)); if (!message) { g_task_return_error (task, error); g_object_unref (task); return; } mbim_device_command (device, message, 10, NULL, (GAsyncReadyCallback)pin_set_enable_ready, task); mbim_message_unref (message); } /*****************************************************************************/ /* Change PIN */ static gboolean change_pin_finish (MMBaseSim *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void pin_set_change_ready (MbimDevice *device, GAsyncResult *res, GTask *task) { MMSimMbim *self; GError *error = NULL; MbimMessage *response; MbimPinType pin_type; guint32 remaining_attempts; self = g_task_get_source_object (task); response = mbim_device_command_finish (device, res, &error); if (response) { mbim_message_response_get_result (response, MBIM_MESSAGE_TYPE_COMMAND_DONE, &error); if (mbim_message_pin_response_parse (response, &pin_type, NULL, &remaining_attempts, NULL)) update_modem_unlock_retries (self, pin_type, remaining_attempts); mbim_message_unref (response); } if (error) { if (g_error_matches (error, MBIM_STATUS_ERROR, MBIM_STATUS_ERROR_PIN_REQUIRED)) { g_error_free (error); error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_WRONG_STATE, "Need to be unlocked to allow changing PIN"); } g_task_return_error (task, error); } else g_task_return_boolean (task, TRUE); g_object_unref (task); } static void change_pin (MMBaseSim *self, const gchar *old_pin, const gchar *new_pin, GAsyncReadyCallback callback, gpointer user_data) { MbimDevice *device; MbimMessage *message; GTask *task; GError *error = NULL; if (!peek_device (self, &device, callback, user_data)) return; task = g_task_new (self, NULL, callback, user_data); mm_obj_dbg (self, "changing PIN..."); message = (mbim_message_pin_set_new ( MBIM_PIN_TYPE_PIN1, MBIM_PIN_OPERATION_CHANGE, old_pin, new_pin, &error)); if (!message) { g_task_return_error (task, error); g_object_unref (task); return; } mbim_device_command (device, message, 10, NULL, (GAsyncReadyCallback)pin_set_change_ready, task); mbim_message_unref (message); } /*****************************************************************************/ MMBaseSim * mm_sim_mbim_new_finish (GAsyncResult *res, GError **error) { GObject *source; GObject *sim; source = g_async_result_get_source_object (res); sim = g_async_initable_new_finish (G_ASYNC_INITABLE (source), res, error); g_object_unref (source); if (!sim) return NULL; /* Only export valid SIMs */ mm_base_sim_export (MM_BASE_SIM (sim)); return MM_BASE_SIM (sim); } void mm_sim_mbim_new (MMBaseModem *modem, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { g_async_initable_new_async (MM_TYPE_SIM_MBIM, G_PRIORITY_DEFAULT, cancellable, callback, user_data, MM_BASE_SIM_MODEM, modem, "active", TRUE, /* by default always active */ NULL); } MMBaseSim * mm_sim_mbim_new_initialized (MMBaseModem *modem, guint slot_number, gboolean active, MMSimType sim_type, MMSimEsimStatus esim_status, const gchar *sim_identifier, const gchar *imsi, const gchar *eid, const gchar *operator_identifier, const gchar *operator_name, const GStrv emergency_numbers) { MMBaseSim *sim; sim = MM_BASE_SIM (g_object_new (MM_TYPE_SIM_MBIM, MM_BASE_SIM_MODEM, modem, MM_BASE_SIM_SLOT_NUMBER, slot_number, "active", active, "sim-type", sim_type, "esim-status", esim_status, "sim-identifier", sim_identifier, "eid", eid, "operator-identifier", operator_identifier, "operator-name", operator_name, "emergency-numbers", emergency_numbers, NULL)); mm_base_sim_export (sim); return sim; } static void mm_sim_mbim_init (MMSimMbim *self) { /* Initialize private data */ self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, MM_TYPE_SIM_MBIM, MMSimMbimPrivate); reset_subscriber_info (self); } static void finalize (GObject *object) { MMSimMbim *self = MM_SIM_MBIM (object); reset_subscriber_info (self); G_OBJECT_CLASS (mm_sim_mbim_parent_class)->finalize (object); } static void dispose (GObject *object) { #if defined WITH_SUSPEND_RESUME MMSimMbim *self = MM_SIM_MBIM (object); clear_modem_sync_monitor (self); #endif G_OBJECT_CLASS (mm_sim_mbim_parent_class)->dispose (object); } static void mm_sim_mbim_class_init (MMSimMbimClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); MMBaseSimClass *base_sim_class = MM_BASE_SIM_CLASS (klass); g_type_class_add_private (object_class, sizeof (MMSimMbimPrivate)); object_class->finalize = finalize; object_class->dispose = dispose; base_sim_class->load_sim_identifier = load_sim_identifier; base_sim_class->load_sim_identifier_finish = load_sim_identifier_finish; base_sim_class->load_imsi = load_imsi; base_sim_class->load_imsi_finish = load_imsi_finish; base_sim_class->load_eid = load_eid; base_sim_class->load_eid_finish = load_eid_finish; base_sim_class->load_operator_identifier = load_operator_identifier; base_sim_class->load_operator_identifier_finish = load_operator_identifier_finish; base_sim_class->load_operator_name = load_operator_name; base_sim_class->load_operator_name_finish = load_operator_name_finish; base_sim_class->load_sim_type = load_sim_type; base_sim_class->load_sim_type_finish = load_sim_type_finish; base_sim_class->load_esim_status = load_esim_status; base_sim_class->load_esim_status_finish = load_esim_status_finish; base_sim_class->load_removability = load_removability; base_sim_class->load_removability_finish = load_removability_finish; base_sim_class->load_gid1 = load_gid1; base_sim_class->load_gid1_finish = load_gid1_finish; base_sim_class->load_gid2 = load_gid2; base_sim_class->load_gid2_finish = load_gid2_finish; base_sim_class->send_pin = send_pin; base_sim_class->send_pin_finish = send_pin_finish; base_sim_class->send_puk = send_puk; base_sim_class->send_puk_finish = send_puk_finish; base_sim_class->enable_pin = enable_pin; base_sim_class->enable_pin_finish = enable_pin_finish; base_sim_class->change_pin = change_pin; base_sim_class->change_pin_finish = change_pin_finish; } ModemManager-1.23.4-dev/src/mm-sim-mbim.h000066400000000000000000000054171456466623000200150ustar00rootroot00000000000000/* -*- 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) 2013 Aleksander Morgado */ #ifndef MM_SIM_MBIM_H #define MM_SIM_MBIM_H #include #include #include "mm-base-sim.h" #define MM_TYPE_SIM_MBIM (mm_sim_mbim_get_type ()) #define MM_SIM_MBIM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_SIM_MBIM, MMSimMbim)) #define MM_SIM_MBIM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_SIM_MBIM, MMSimMbimClass)) #define MM_IS_SIM_MBIM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_SIM_MBIM)) #define MM_IS_SIM_MBIM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_SIM_MBIM)) #define MM_SIM_MBIM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_SIM_MBIM, MMSimMbimClass)) typedef struct _MMSimMbim MMSimMbim; typedef struct _MMSimMbimClass MMSimMbimClass; typedef struct _MMSimMbimPrivate MMSimMbimPrivate; struct _MMSimMbim { MMBaseSim parent; MMSimMbimPrivate *priv; }; struct _MMSimMbimClass { MMBaseSimClass parent; }; GType mm_sim_mbim_get_type (void); G_DEFINE_AUTOPTR_CLEANUP_FUNC (MMSimMbim, g_object_unref) void mm_sim_mbim_new (MMBaseModem *modem, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); MMBaseSim *mm_sim_mbim_new_finish (GAsyncResult *res, GError **error); MMBaseSim *mm_sim_mbim_new_initialized (MMBaseModem *modem, guint slot_number, gboolean active, MMSimType sim_type, MMSimEsimStatus esim_status, const gchar *sim_identifier, const gchar *imsi, const gchar *eid, const gchar *operator_identifier, const gchar *operator_name, const GStrv emergency_numbers); #endif /* MM_SIM_MBIM_H */ ModemManager-1.23.4-dev/src/mm-sim-qmi.c000066400000000000000000002036321456466623000176510ustar00rootroot00000000000000/* -*- 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) 2012 Google, Inc. * Copyright (C) 2016 Aleksander Morgado * Copyright (c) 2022 Qualcomm Innovation Center, Inc. */ #include #include #include #include #include #include #include #define _LIBMM_INSIDE_MM #include #include "mm-broadband-modem-qmi.h" #include "mm-log-object.h" #include "mm-sim-qmi.h" #include "mm-modem-helpers-qmi.h" #include "mm-shared-qmi.h" G_DEFINE_TYPE (MMSimQmi, mm_sim_qmi, MM_TYPE_BASE_SIM) enum { PROP_0, PROP_DMS_UIM_DEPRECATED, PROP_LAST }; static GParamSpec *properties[PROP_LAST]; struct _MMSimQmiPrivate { gboolean dms_uim_deprecated; gchar *imsi; }; static const guint16 mf_file_path[] = { 0x3F00 }; static const guint16 adf_file_path[] = { 0x3F00, 0x7FFF }; /*****************************************************************************/ static gboolean ensure_qmi_client (GTask *task, MMSimQmi *self, QmiService service, QmiClient **o_client) { MMBaseModem *modem = NULL; QmiClient *client; g_autoptr(GError) error = NULL; g_object_get (self, MM_BASE_SIM_MODEM, &modem, NULL); g_assert (MM_IS_BASE_MODEM (modem)); g_assert (MM_IS_SHARED_QMI (modem)); client = mm_shared_qmi_peek_client (MM_SHARED_QMI (modem), service, MM_PORT_QMI_FLAG_DEFAULT, &error); g_object_unref (modem); if (!client) { if (task) { g_task_return_error (task, g_steal_pointer (&error)); g_object_unref (task); } return FALSE; } *o_client = client; return TRUE; } /*****************************************************************************/ /* Wait for SIM ready */ #define SIM_READY_CHECKS_MAX 5 #define SIM_READY_CHECKS_TIMEOUT_SECS 1 typedef struct { QmiClient *client_uim; guint ready_checks_n; } WaitSimReadyContext; static void wait_sim_ready_context_free (WaitSimReadyContext *ctx) { g_clear_object (&ctx->client_uim); g_slice_free (WaitSimReadyContext, ctx); } static gboolean wait_sim_ready_finish (MMBaseSim *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void sim_ready_check (GTask *task); static gboolean sim_ready_retry_cb (GTask *task) { sim_ready_check (task); return G_SOURCE_REMOVE; } static void sim_ready_retry (GTask *task) { g_timeout_add_seconds (SIM_READY_CHECKS_TIMEOUT_SECS, (GSourceFunc) sim_ready_retry_cb, task); } static void uim_get_card_status_ready (QmiClientUim *client, GAsyncResult *res, GTask *task) { g_autoptr(QmiMessageUimGetCardStatusOutput) output = NULL; g_autoptr(GError) error = NULL; MMSimQmi *self; self = g_task_get_source_object (task); output = qmi_client_uim_get_card_status_finish (client, res, &error); if (!output || !qmi_message_uim_get_card_status_output_get_result (output, &error) || (!mm_qmi_uim_get_card_status_output_parse (self, output, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, &error) && (g_error_matches (error, MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_SIM_NOT_INSERTED) || g_error_matches (error, MM_CORE_ERROR, MM_CORE_ERROR_RETRY)))) { mm_obj_dbg (self, "sim not yet considered ready... retrying"); sim_ready_retry (task); return; } /* SIM is considered ready now */ mm_obj_dbg (self, "sim is ready"); g_task_return_boolean (task, TRUE); g_object_unref (task); } static void sim_ready_check (GTask *task) { WaitSimReadyContext *ctx; MMSimQmi *self; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); ctx->ready_checks_n++; if (ctx->ready_checks_n == SIM_READY_CHECKS_MAX) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "failed waiting for SIM readiness"); g_object_unref (task); return; } mm_obj_dbg (self, "checking SIM readiness"); qmi_client_uim_get_card_status (QMI_CLIENT_UIM (ctx->client_uim), NULL, 5, NULL, (GAsyncReadyCallback) uim_get_card_status_ready, task); } static void wait_sim_ready (MMBaseSim *_self, GAsyncReadyCallback callback, gpointer user_data) { QmiClient *client; MMSimQmi *self; GTask *task; WaitSimReadyContext *ctx; self = MM_SIM_QMI (_self); task = g_task_new (self, NULL, callback, user_data); mm_obj_dbg (self, "waiting for SIM to be ready..."); if (!self->priv->dms_uim_deprecated) { g_task_return_boolean (task, TRUE); g_object_unref (task); return; } if (!ensure_qmi_client (task, self, QMI_SERVICE_UIM, &client)) return; ctx = g_slice_new0 (WaitSimReadyContext); ctx->client_uim = g_object_ref (client); g_task_set_task_data (task, ctx, (GDestroyNotify) wait_sim_ready_context_free); sim_ready_check (task); } /*****************************************************************************/ /* Load SIM ID (ICCID) */ static GArray * uim_read_finish (QmiClientUim *client, GAsyncResult *res, GError **error) { return g_task_propagate_pointer (G_TASK (res), error); } static void uim_read_ready (QmiClientUim *client, GAsyncResult *res, GTask *task) { QmiMessageUimReadTransparentOutput *output; GError *error = NULL; output = qmi_client_uim_read_transparent_finish (client, res, &error); if (!output) { g_prefix_error (&error, "QMI operation failed: "); g_task_return_error (task, error); } else if (!qmi_message_uim_read_transparent_output_get_result (output, &error)) { g_prefix_error (&error, "Couldn't read data from UIM: "); g_task_return_error (task, error); } else { GArray *read_result = NULL; qmi_message_uim_read_transparent_output_get_read_result (output, &read_result, NULL); if (read_result) g_task_return_pointer (task, g_array_ref (read_result), (GDestroyNotify) g_array_unref); else g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Read malformed data from UIM"); } if (output) qmi_message_uim_read_transparent_output_unref (output); g_object_unref (task); } static void uim_read (MMSimQmi *self, guint16 file_id, const guint16 *file_path, gsize file_path_len, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; QmiClient *client = NULL; GArray *file_path_bytes; gsize i; QmiMessageUimReadTransparentInput *input; GArray *aid; task = g_task_new (self, NULL, callback, user_data); if (!ensure_qmi_client (task, self, QMI_SERVICE_UIM, &client)) return; file_path_bytes = g_array_sized_new (FALSE, FALSE, 1, file_path_len * 2); for (i = 0; i < file_path_len; ++i) { guint8 byte; byte = file_path[i] & 0xFF; g_array_append_val (file_path_bytes, byte); byte = (file_path[i] >> 8) & 0xFF; g_array_append_val (file_path_bytes, byte); } input = qmi_message_uim_read_transparent_input_new (); aid = g_array_new (FALSE, FALSE, sizeof (guint8)); /* empty AID */ qmi_message_uim_read_transparent_input_set_session ( input, QMI_UIM_SESSION_TYPE_PRIMARY_GW_PROVISIONING, aid, NULL); g_array_unref (aid); qmi_message_uim_read_transparent_input_set_file (input, file_id, file_path_bytes, NULL); qmi_message_uim_read_transparent_input_set_read_information (input, 0, 0, NULL); g_array_unref (file_path_bytes); qmi_client_uim_read_transparent (QMI_CLIENT_UIM (client), input, 10, NULL, (GAsyncReadyCallback)uim_read_ready, task); qmi_message_uim_read_transparent_input_unref (input); } static gchar * load_sim_identifier_finish (MMBaseSim *self, GAsyncResult *res, GError **error) { return g_task_propagate_pointer (G_TASK (res), error); } static void uim_get_iccid_ready (QmiClientUim *client, GAsyncResult *res, GTask *task) { GError *error = NULL; g_autoptr(GArray) read_result = NULL; g_autofree gchar *raw_iccid = NULL; gchar *iccid; read_result = uim_read_finish (client, res, &error); if (!read_result) { g_task_return_error (task, error); g_object_unref (task); return; } raw_iccid = mm_utils_bin2hexstr ((const guint8 *) read_result->data, read_result->len); g_assert (raw_iccid); iccid = mm_3gpp_parse_iccid (raw_iccid, &error); if (!iccid) g_task_return_error (task, error); else g_task_return_pointer (task, iccid, g_free); g_object_unref (task); } static void uim_get_iccid (MMSimQmi *self, GTask *task) { uim_read (self, 0x2FE2, mf_file_path, G_N_ELEMENTS (mf_file_path), (GAsyncReadyCallback)uim_get_iccid_ready, task); } static void dms_uim_get_iccid_ready (QmiClientDms *client, GAsyncResult *res, GTask *task) { QmiMessageDmsUimGetIccidOutput *output = NULL; GError *error = NULL; output = qmi_client_dms_uim_get_iccid_finish (client, res, &error); if (!output) { g_prefix_error (&error, "QMI operation failed: "); g_task_return_error (task, error); } else if (!qmi_message_dms_uim_get_iccid_output_get_result (output, &error)) { g_prefix_error (&error, "Couldn't get UIM ICCID: "); g_task_return_error (task, error); } else { const gchar *str = NULL; qmi_message_dms_uim_get_iccid_output_get_iccid (output, &str, NULL); g_task_return_pointer (task, g_strdup (str), g_free); } if (output) qmi_message_dms_uim_get_iccid_output_unref (output); g_object_unref (task); } static void dms_uim_get_iccid (MMSimQmi *self, GTask *task) { QmiClient *client = NULL; if (!ensure_qmi_client (task, self, QMI_SERVICE_DMS, &client)) return; qmi_client_dms_uim_get_iccid (QMI_CLIENT_DMS (client), NULL, 5, NULL, (GAsyncReadyCallback)dms_uim_get_iccid_ready, task); } static void load_sim_identifier (MMBaseSim *_self, GAsyncReadyCallback callback, gpointer user_data) { MMSimQmi *self; GTask *task; self = MM_SIM_QMI (_self); task = g_task_new (self, NULL, callback, user_data); mm_obj_dbg (self, "loading SIM identifier..."); if (!self->priv->dms_uim_deprecated) dms_uim_get_iccid (self, task); else uim_get_iccid (self, task); } /*****************************************************************************/ /* Load IMSI */ static gchar * load_imsi_finish (MMBaseSim *self, GAsyncResult *res, GError **error) { return g_task_propagate_pointer (G_TASK (res), error); } static void uim_get_imsi_ready (QmiClientUim *client, GAsyncResult *res, GTask *task) { MMSimQmi *self; GError *error = NULL; g_autoptr(GArray) read_result = NULL; g_autofree gchar *imsi = NULL; self = g_task_get_source_object (task); read_result = uim_read_finish (client, res, &error); if (!read_result) { g_task_return_error (task, error); g_object_unref (task); return; } imsi = mm_bcd_to_string ((const guint8 *) read_result->data, read_result->len, TRUE /* low_nybble_first */); g_assert (imsi); if (strlen (imsi) < 3) g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "IMSI is malformed"); else { /* EFimsi contains a length byte, follwed by a nibble for parity, * and then followed by the actual IMSI in BCD. After converting * the BCD into a decimal string, we simply skip the first 3 * decimal digits to obtain the IMSI. */ /* Cache IMSI */ g_free (self->priv->imsi); self->priv->imsi = g_strdup (imsi + 3); g_task_return_pointer (task, g_strdup (imsi + 3), g_free); } g_object_unref (task); } static void uim_get_imsi (MMSimQmi *self, GTask *task) { uim_read (self, 0x6F07, adf_file_path, G_N_ELEMENTS (adf_file_path), (GAsyncReadyCallback)uim_get_imsi_ready, task); } static void dms_uim_get_imsi_ready (QmiClientDms *client, GAsyncResult *res, GTask *task) { MMSimQmi *self; QmiMessageDmsUimGetImsiOutput *output = NULL; GError *error = NULL; self = g_task_get_source_object (task); output = qmi_client_dms_uim_get_imsi_finish (client, res, &error); if (!output) { g_prefix_error (&error, "QMI operation failed: "); g_task_return_error (task, error); } else if (!qmi_message_dms_uim_get_imsi_output_get_result (output, &error)) { g_prefix_error (&error, "Couldn't get UIM IMSI: "); g_task_return_error (task, error); } else { const gchar *str = NULL; qmi_message_dms_uim_get_imsi_output_get_imsi (output, &str, NULL); /* Cache IMSI */ g_free (self->priv->imsi); self->priv->imsi = g_strdup (str); g_task_return_pointer (task, g_strdup (str), g_free); } if (output) qmi_message_dms_uim_get_imsi_output_unref (output); g_object_unref (task); } static void dms_uim_get_imsi (MMSimQmi *self, GTask *task) { QmiClient *client = NULL; if (!ensure_qmi_client (task, self, QMI_SERVICE_DMS, &client)) return; qmi_client_dms_uim_get_imsi (QMI_CLIENT_DMS (client), NULL, 5, NULL, (GAsyncReadyCallback)dms_uim_get_imsi_ready, task); } static void load_imsi (MMBaseSim *_self, GAsyncReadyCallback callback, gpointer user_data) { MMSimQmi *self; GTask *task; self = MM_SIM_QMI (_self); task = g_task_new (self, NULL, callback, user_data); mm_obj_dbg (self, "loading IMSI..."); if (!self->priv->dms_uim_deprecated) dms_uim_get_imsi (self, task); else uim_get_imsi (self, task); } /*****************************************************************************/ /* Load GID1 and GID2 */ static GByteArray * common_load_gid_finish (MMBaseSim *self, GAsyncResult *res, GError **error) { return g_task_propagate_pointer (G_TASK (res), error); } static void uim_get_gid_ready (QmiClientUim *client, GAsyncResult *res, GTask *task) { GError *error = NULL; g_autoptr(GArray) read_result = NULL; read_result = uim_read_finish (client, res, &error); if (!read_result) g_task_return_error (task, error); else g_task_return_pointer (task, g_byte_array_append (g_byte_array_sized_new (read_result->len), (const guint8 *)(read_result->data), read_result->len), (GDestroyNotify)g_byte_array_unref); g_object_unref (task); } static void common_load_gid (MMBaseSim *self, guint16 file_id, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; task = g_task_new (self, NULL, callback, user_data); uim_read (MM_SIM_QMI (self), file_id, adf_file_path, G_N_ELEMENTS (adf_file_path), (GAsyncReadyCallback)uim_get_gid_ready, task); } static GByteArray * load_gid1_finish (MMBaseSim *self, GAsyncResult *res, GError **error) { return common_load_gid_finish (self, res, error); } static void load_gid1 (MMBaseSim *self, GAsyncReadyCallback callback, gpointer user_data) { common_load_gid (self, 0x6F3E, callback, user_data); } static GByteArray * load_gid2_finish (MMBaseSim *self, GAsyncResult *res, GError **error) { return common_load_gid_finish (self, res, error); } static void load_gid2 (MMBaseSim *self, GAsyncReadyCallback callback, gpointer user_data) { common_load_gid (self, 0x6F3F, callback, user_data); } /*****************************************************************************/ /* Load operator identifier */ static gchar * load_operator_identifier_finish (MMBaseSim *self, GAsyncResult *res, GError **error) { return g_task_propagate_pointer (G_TASK (res), error); } static void uim_read_efad_ready (QmiClientUim *client, GAsyncResult *res, GTask *task) { MMSimQmi *self; GError *error = NULL; g_autoptr(GArray) read_result = NULL; guint mnc_length; self = g_task_get_source_object (task); read_result = uim_read_finish (client, res, &error); if (!read_result) { g_task_return_error (task, error); g_object_unref (task); return; } if (read_result->len < 4) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Unexpected response length reading EFad: %u", read_result->len); g_object_unref (task); return; } /* MNC length is byte 4 of this SIM file */ mnc_length = read_result->data[3]; if (mnc_length == 2 || mnc_length == 3) { g_task_return_pointer (task, g_strndup (self->priv->imsi, 3 + mnc_length), g_free); } else { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "SIM returned invalid MNC length %d (should be either 2 or 3)", mnc_length); } g_object_unref (task); } static void load_operator_identifier (MMBaseSim *_self, GAsyncReadyCallback callback, gpointer user_data) { MMSimQmi *self; GTask *task; self = MM_SIM_QMI (_self); task = g_task_new (self, NULL, callback, user_data); mm_obj_dbg (self, "loading SIM operator identifier..."); if (!self->priv->imsi) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't load SIM operator identifier without IMSI"); g_object_unref (task); return; } uim_read (self, 0x6FAD, adf_file_path, G_N_ELEMENTS (adf_file_path), (GAsyncReadyCallback)uim_read_efad_ready, task); } /*****************************************************************************/ /* Load operator name */ static gchar * load_operator_name_finish (MMBaseSim *self, GAsyncResult *res, GError **error) { return g_task_propagate_pointer (G_TASK (res), error); } static gchar * parse_spn (const guint8 *bin, gsize len, GError **error) { g_autoptr(GByteArray) bin_array = NULL; gsize binlen; /* Remove the FF filler at the end */ binlen = len; while (binlen > 1 && bin[binlen - 1] == 0xff) binlen--; if (binlen <= 1) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "SIM returned empty spn"); return NULL; } /* Setup as bytearray. * First byte is metadata; remainder is GSM-7 unpacked into octets; convert to UTF8 */ bin_array = g_byte_array_sized_new (binlen - 1); g_byte_array_append (bin_array, bin + 1, binlen - 1); return mm_modem_charset_bytearray_to_utf8 (bin_array, MM_MODEM_CHARSET_GSM, FALSE, error); } static void uim_read_efspn_ready (QmiClientUim *client, GAsyncResult *res, GTask *task) { GError *error = NULL; g_autoptr(GArray) read_result = NULL; gchar *spn; read_result = uim_read_finish (client, res, &error); if (!read_result) { g_task_return_error (task, error); g_object_unref (task); return; } spn = parse_spn ((const guint8 *) read_result->data, read_result->len, &error); if (!spn) { g_task_return_error (task, error); } else { g_task_return_pointer (task, spn, g_free); } g_object_unref (task); } static void load_operator_name (MMBaseSim *_self, GAsyncReadyCallback callback, gpointer user_data) { MMSimQmi *self; GTask *task; self = MM_SIM_QMI (_self); task = g_task_new (self, NULL, callback, user_data); mm_obj_dbg (self, "loading SIM operator name..."); uim_read (self, 0x6F46, adf_file_path, G_N_ELEMENTS (adf_file_path), (GAsyncReadyCallback)uim_read_efspn_ready, task); } /*****************************************************************************/ /* Load preferred networks */ static GList * load_preferred_networks_finish (MMBaseSim *self, GAsyncResult *res, GError **error) { return g_task_propagate_pointer (G_TASK (res), error); } static GList * parse_get_preferred_networks (QmiMessageNasGetPreferredNetworksOutput *output) { GList *result = NULL; GArray *preferred_nets_array = NULL; GArray *preferred_nets_mnc_pcs_digit_array = NULL; guint i; if (qmi_message_nas_get_preferred_networks_output_get_preferred_networks (output, &preferred_nets_array, NULL)) { qmi_message_nas_get_preferred_networks_output_get_mnc_pcs_digit_include_status (output, &preferred_nets_mnc_pcs_digit_array, NULL); for (i = 0; i < preferred_nets_array->len; i++) { QmiMessageNasGetPreferredNetworksOutputPreferredNetworksElement *net; QmiMessageNasGetPreferredNetworksOutputMncPcsDigitIncludeStatusElement *mnc_pcs_digit = NULL; MMSimPreferredNetwork *new_item; g_autofree gchar *operator_code = NULL; MMModemAccessTechnology act = MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN; net = &g_array_index (preferred_nets_array, QmiMessageNasGetPreferredNetworksOutputPreferredNetworksElement, i); if (preferred_nets_mnc_pcs_digit_array && i < preferred_nets_mnc_pcs_digit_array->len) mnc_pcs_digit = &g_array_index (preferred_nets_mnc_pcs_digit_array, QmiMessageNasGetPreferredNetworksOutputMncPcsDigitIncludeStatusElement, i); new_item = mm_sim_preferred_network_new (); if (net->mnc > 99 || (mnc_pcs_digit != NULL && mnc_pcs_digit->includes_pcs_digit)) operator_code = g_strdup_printf ("%03d%03d", net->mcc, net->mnc); else operator_code = g_strdup_printf ("%03d%02d", net->mcc, net->mnc); mm_sim_preferred_network_set_operator_code (new_item, operator_code); if (net->radio_access_technology & QMI_NAS_PLMN_ACCESS_TECHNOLOGY_IDENTIFIER_GSM) act |= MM_MODEM_ACCESS_TECHNOLOGY_GSM; if (net->radio_access_technology & QMI_NAS_PLMN_ACCESS_TECHNOLOGY_IDENTIFIER_GSM_COMPACT) act |= MM_MODEM_ACCESS_TECHNOLOGY_GSM_COMPACT; if (net->radio_access_technology & QMI_NAS_PLMN_ACCESS_TECHNOLOGY_IDENTIFIER_UTRAN) act |= MM_MODEM_ACCESS_TECHNOLOGY_UMTS; if (net->radio_access_technology & QMI_NAS_PLMN_ACCESS_TECHNOLOGY_IDENTIFIER_EUTRAN) act |= MM_MODEM_ACCESS_TECHNOLOGY_LTE; if (net->radio_access_technology & QMI_NAS_PLMN_ACCESS_TECHNOLOGY_IDENTIFIER_NGRAN) act |= MM_MODEM_ACCESS_TECHNOLOGY_5GNR; mm_sim_preferred_network_set_access_technology (new_item, act); result = g_list_append (result, new_item); } } return result; } static void load_preferred_networks_ready (QmiClientNas *client, GAsyncResult *res, GTask *task) { QmiMessageNasGetPreferredNetworksOutput *output; GError *error = NULL; output = qmi_client_nas_get_preferred_networks_finish (client, res, &error); if (!output) { g_prefix_error (&error, "QMI operation failed: "); g_task_return_error (task, error); } else if (!qmi_message_nas_get_preferred_networks_output_get_result (output, &error)) { g_prefix_error (&error, "Couldn't get preferred networks: "); g_task_return_error (task, error); } else g_task_return_pointer (task, parse_get_preferred_networks (output), (GDestroyNotify) mm_sim_preferred_network_list_free); if (output) qmi_message_nas_get_preferred_networks_output_unref (output); g_object_unref (task); } static void load_preferred_networks (MMBaseSim *self, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; QmiClient *client = NULL; task = g_task_new (self, NULL, callback, user_data); if (!ensure_qmi_client (task, MM_SIM_QMI (self), QMI_SERVICE_NAS, &client)) return; mm_obj_dbg (self, "loading preferred network list..."); qmi_client_nas_get_preferred_networks (QMI_CLIENT_NAS (client), NULL, 5, NULL, (GAsyncReadyCallback)load_preferred_networks_ready, task); } /*****************************************************************************/ /* Set preferred networks */ typedef struct { /* Preferred network list to be set, used for after-check comparison */ GList *set_list; } SetPreferredNetworksContext; static void set_preferred_network_context_free (SetPreferredNetworksContext *ctx) { g_list_free_full (ctx->set_list, (GDestroyNotify) mm_sim_preferred_network_free); g_slice_free (SetPreferredNetworksContext, ctx); } static gboolean set_preferred_networks_finish (MMBaseSim *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void set_preferred_networks_reload_ready (MMBaseSim *self, GAsyncResult *res, GTask *task) { GError *error = NULL; GList *loaded_list; GList *loaded_iter; GList *set_iter; SetPreferredNetworksContext *ctx; ctx = g_task_get_task_data (task); loaded_list = load_preferred_networks_finish (self, res, &error); if (error) { mm_obj_warn (self, "couldn't reload list of preferred networks: %s", error->message); g_task_return_error (task, error); g_object_unref (task); return; } /* Compare the set and loaded network list for differences */ loaded_iter = loaded_list; set_iter = ctx->set_list; while (loaded_iter && set_iter) { const gchar *loaded_op_code; const gchar *set_op_code; MMModemAccessTechnology loaded_act; MMModemAccessTechnology set_act; loaded_op_code = mm_sim_preferred_network_get_operator_code (loaded_iter->data); set_op_code = mm_sim_preferred_network_get_operator_code (set_iter->data); loaded_act = mm_sim_preferred_network_get_access_technology (loaded_iter->data); set_act = mm_sim_preferred_network_get_access_technology (set_iter->data); /* Operator code mismatch is never expected, but check it just in case */ if (g_strcmp0 (loaded_op_code, set_op_code)) { mm_obj_warn (self, "operator code mismatch, expected '%s' loaded '%s'", set_op_code, loaded_op_code); error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Mismatch in requested and set operator code"); break; } /* Check if there are access technology bits requested but unset */ if ((loaded_act & set_act) != set_act) { MMModemAccessTechnology unset = set_act & ~loaded_act; mm_obj_warn (self, "access technologies '%s' not set for operator code '%s'", mm_modem_access_technology_build_string_from_mask (unset), set_op_code); error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "Access technology unsupported by modem or SIM"); break; } loaded_iter = g_list_next (loaded_iter); set_iter = g_list_next (set_iter); } if (!error && loaded_iter == NULL && set_iter != NULL) { /* Not all networks were written; some modems silently discard networks * that exceed the SIM card capacity. */ error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_TOO_MANY, "Too many networks; %u networks written", g_list_length (loaded_list)); } if (error) { /* Update the PreferredNetworks property to real SIM contents */ mm_gdbus_sim_set_preferred_networks (MM_GDBUS_SIM (self), mm_sim_preferred_network_list_get_variant (loaded_list)); g_task_return_error (task, error); } else g_task_return_boolean (task, TRUE); g_list_free_full (loaded_list, (GDestroyNotify) mm_sim_preferred_network_free); g_object_unref (task); } static void set_preferred_networks_ready (QmiClientNas *client, GAsyncResult *res, GTask *task) { QmiMessageNasSetPreferredNetworksOutput *output; GError *error = NULL; MMBaseSim *self; self = g_task_get_source_object (task); output = qmi_client_nas_set_preferred_networks_finish (client, res, &error); if (!output) { g_prefix_error (&error, "QMI operation failed: "); g_task_return_error (task, error); } else if (!qmi_message_nas_set_preferred_networks_output_get_result (output, &error)) { g_prefix_error (&error, "Couldn't set preferred networks: "); g_task_return_error (task, error); } else { /* Reload the networks from modem to check whether everything was written */ load_preferred_networks (self, (GAsyncReadyCallback) set_preferred_networks_reload_ready, task); qmi_message_nas_set_preferred_networks_output_unref (output); return; } if (output) qmi_message_nas_set_preferred_networks_output_unref (output); g_object_unref (task); } static void set_preferred_networks (MMBaseSim *self, GList *preferred_network_list, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; QmiMessageNasSetPreferredNetworksInput *input; QmiClient *client = NULL; GArray *preferred_nets_array; GArray *preferred_nets_mnc_pcs_digit_array; SetPreferredNetworksContext *ctx; task = g_task_new (self, NULL, callback, user_data); if (!ensure_qmi_client (task, MM_SIM_QMI (self), QMI_SERVICE_NAS, &client)) return; ctx = g_slice_new0 (SetPreferredNetworksContext); ctx->set_list = mm_sim_preferred_network_list_copy (preferred_network_list); g_task_set_task_data (task, ctx, (GDestroyNotify) set_preferred_network_context_free); mm_obj_dbg (self, "setting preferred networks..."); input = qmi_message_nas_set_preferred_networks_input_new (); preferred_nets_array = g_array_new (FALSE, TRUE, sizeof (QmiMessageNasSetPreferredNetworksInputPreferredNetworksElement)); preferred_nets_mnc_pcs_digit_array = g_array_new (FALSE, TRUE, sizeof (QmiMessageNasSetPreferredNetworksInputMncPcsDigitIncludeStatusElement)); while (preferred_network_list) { QmiMessageNasSetPreferredNetworksInputPreferredNetworksElement preferred_nets_element; QmiMessageNasSetPreferredNetworksInputMncPcsDigitIncludeStatusElement pcs_digit_element; const gchar *operator_code; MMModemAccessTechnology act; memset (&preferred_nets_element, 0, sizeof (preferred_nets_element)); memset (&pcs_digit_element, 0, sizeof (pcs_digit_element)); operator_code = mm_sim_preferred_network_get_operator_code (preferred_network_list->data); act = mm_sim_preferred_network_get_access_technology (preferred_network_list->data); if (mm_3gpp_parse_operator_id (operator_code, &preferred_nets_element.mcc, &preferred_nets_element.mnc, &pcs_digit_element.includes_pcs_digit, NULL)) { pcs_digit_element.mcc = preferred_nets_element.mcc; pcs_digit_element.mnc = preferred_nets_element.mnc; preferred_nets_element.radio_access_technology = QMI_NAS_PLMN_ACCESS_TECHNOLOGY_IDENTIFIER_UNSPECIFIED; if (act & MM_MODEM_ACCESS_TECHNOLOGY_GSM) preferred_nets_element.radio_access_technology |= QMI_NAS_PLMN_ACCESS_TECHNOLOGY_IDENTIFIER_GSM; if (act & MM_MODEM_ACCESS_TECHNOLOGY_GSM_COMPACT) preferred_nets_element.radio_access_technology |= QMI_NAS_PLMN_ACCESS_TECHNOLOGY_IDENTIFIER_GSM_COMPACT; if (act & MM_MODEM_ACCESS_TECHNOLOGY_UMTS) preferred_nets_element.radio_access_technology |= QMI_NAS_PLMN_ACCESS_TECHNOLOGY_IDENTIFIER_UTRAN; if (act & MM_MODEM_ACCESS_TECHNOLOGY_LTE) preferred_nets_element.radio_access_technology |= QMI_NAS_PLMN_ACCESS_TECHNOLOGY_IDENTIFIER_EUTRAN; if (act & MM_MODEM_ACCESS_TECHNOLOGY_5GNR) preferred_nets_element.radio_access_technology |= QMI_NAS_PLMN_ACCESS_TECHNOLOGY_IDENTIFIER_NGRAN; g_array_append_val (preferred_nets_array, preferred_nets_element); g_array_append_val (preferred_nets_mnc_pcs_digit_array, pcs_digit_element); } preferred_network_list = g_list_next (preferred_network_list); } qmi_message_nas_set_preferred_networks_input_set_preferred_networks (input, preferred_nets_array, NULL); qmi_message_nas_set_preferred_networks_input_set_mnc_pcs_digit_include_status (input, preferred_nets_mnc_pcs_digit_array, NULL); /* Always clear any pre-existing networks */ qmi_message_nas_set_preferred_networks_input_set_clear_previous_preferred_networks (input, TRUE, NULL); qmi_client_nas_set_preferred_networks (QMI_CLIENT_NAS (client), input, 5, NULL, (GAsyncReadyCallback)set_preferred_networks_ready, task); qmi_message_nas_set_preferred_networks_input_unref (input); g_array_unref (preferred_nets_array); g_array_unref (preferred_nets_mnc_pcs_digit_array); } /*****************************************************************************/ /* Send PIN */ static GError * pin_qmi_error_to_mobile_equipment_error (GError *qmi_error) { GError *me_error = NULL; if (g_error_matches (qmi_error, QMI_PROTOCOL_ERROR, QMI_PROTOCOL_ERROR_INCORRECT_PIN)) { me_error = g_error_new_literal (MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_INCORRECT_PASSWORD, qmi_error->message); } else if (g_error_matches (qmi_error, QMI_PROTOCOL_ERROR, QMI_PROTOCOL_ERROR_PIN_BLOCKED)) { me_error = g_error_new_literal (MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_SIM_PUK, qmi_error->message); } if (me_error) { g_error_free (qmi_error); return me_error; } return qmi_error; } static gboolean send_pin_finish (MMBaseSim *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void uim_verify_pin_ready (QmiClientUim *client, GAsyncResult *res, GTask *task) { QmiMessageUimVerifyPinOutput *output = NULL; GError *error = NULL; output = qmi_client_uim_verify_pin_finish (client, res, &error); if (!output) { g_prefix_error (&error, "QMI operation failed: "); g_task_return_error (task, error); } else if (!qmi_message_uim_verify_pin_output_get_result (output, &error)) { g_prefix_error (&error, "Couldn't verify PIN: "); g_task_return_error (task, pin_qmi_error_to_mobile_equipment_error (error)); } else g_task_return_boolean (task, TRUE); if (output) qmi_message_uim_verify_pin_output_unref (output); g_object_unref (task); } static void uim_verify_pin (MMSimQmi *self, GTask *task) { QmiMessageUimVerifyPinInput *input; QmiClient *client = NULL; GArray *aid; if (!ensure_qmi_client (task, self, QMI_SERVICE_UIM, &client)) return; input = qmi_message_uim_verify_pin_input_new (); qmi_message_uim_verify_pin_input_set_info ( input, QMI_UIM_PIN_ID_PIN1, g_task_get_task_data (task), NULL); aid = g_array_new (FALSE, FALSE, sizeof (guint8)); /* empty AID */ qmi_message_uim_verify_pin_input_set_session ( input, QMI_UIM_SESSION_TYPE_CARD_SLOT_1, aid, NULL); g_array_unref (aid); qmi_client_uim_verify_pin (QMI_CLIENT_UIM (client), input, 5, NULL, (GAsyncReadyCallback) uim_verify_pin_ready, task); qmi_message_uim_verify_pin_input_unref (input); } static void dms_uim_verify_pin_ready (QmiClientDms *client, GAsyncResult *res, GTask *task) { QmiMessageDmsUimVerifyPinOutput *output = NULL; GError *error = NULL; output = qmi_client_dms_uim_verify_pin_finish (client, res, &error); if (!output) { g_prefix_error (&error, "QMI operation failed: "); g_task_return_error (task, error); } else if (!qmi_message_dms_uim_verify_pin_output_get_result (output, &error)) { g_prefix_error (&error, "Couldn't verify PIN: "); g_task_return_error (task, pin_qmi_error_to_mobile_equipment_error (error)); } else g_task_return_boolean (task, TRUE); if (output) qmi_message_dms_uim_verify_pin_output_unref (output); g_object_unref (task); } static void dms_uim_verify_pin (MMSimQmi *self, GTask *task) { QmiMessageDmsUimVerifyPinInput *input; QmiClient *client = NULL; if (!ensure_qmi_client (NULL, self, QMI_SERVICE_DMS, &client)) { /* Very unlikely that this will ever happen, but anyway, try with * UIM service instead */ uim_verify_pin (self, task); return; } mm_obj_dbg (self, "sending PIN..."); input = qmi_message_dms_uim_verify_pin_input_new (); qmi_message_dms_uim_verify_pin_input_set_info ( input, QMI_DMS_UIM_PIN_ID_PIN, g_task_get_task_data (task), NULL); qmi_client_dms_uim_verify_pin (QMI_CLIENT_DMS (client), input, 5, NULL, (GAsyncReadyCallback) dms_uim_verify_pin_ready, task); qmi_message_dms_uim_verify_pin_input_unref (input); } static void send_pin (MMBaseSim *_self, const gchar *pin, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; MMSimQmi *self; self = MM_SIM_QMI (_self); task = g_task_new (self, NULL, callback, user_data); g_task_set_task_data (task, g_strdup (pin), g_free); mm_obj_dbg (self, "verifying PIN..."); if (!self->priv->dms_uim_deprecated) dms_uim_verify_pin (self, task); else uim_verify_pin (self, task); } /*****************************************************************************/ /* Send PUK */ typedef struct { gchar *puk; gchar *new_pin; } UnblockPinContext; static void unblock_pin_context_free (UnblockPinContext *ctx) { g_free (ctx->puk); g_free (ctx->new_pin); g_slice_free (UnblockPinContext, ctx); } static gboolean send_puk_finish (MMBaseSim *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void uim_unblock_pin_ready (QmiClientUim *client, GAsyncResult *res, GTask *task) { QmiMessageUimUnblockPinOutput *output = NULL; GError *error = NULL; output = qmi_client_uim_unblock_pin_finish (client, res, &error); if (!output) { g_prefix_error (&error, "QMI operation failed: "); g_task_return_error (task, error); } else if (!qmi_message_uim_unblock_pin_output_get_result (output, &error)) { g_prefix_error (&error, "Couldn't unblock PIN: "); g_task_return_error (task, pin_qmi_error_to_mobile_equipment_error (error)); } else g_task_return_boolean (task, TRUE); if (output) qmi_message_uim_unblock_pin_output_unref (output); g_object_unref (task); } static void uim_unblock_pin (MMSimQmi *self, GTask *task) { QmiMessageUimUnblockPinInput *input; QmiClient *client = NULL; UnblockPinContext *ctx; GArray *aid; if (!ensure_qmi_client (task, self, QMI_SERVICE_UIM, &client)) return; ctx = g_task_get_task_data (task); input = qmi_message_uim_unblock_pin_input_new (); qmi_message_uim_unblock_pin_input_set_info ( input, QMI_UIM_PIN_ID_PIN1, ctx->puk, ctx->new_pin, NULL); aid = g_array_new (FALSE, FALSE, sizeof (guint8)); /* empty AID */ qmi_message_uim_unblock_pin_input_set_session ( input, QMI_UIM_SESSION_TYPE_CARD_SLOT_1, aid, NULL); g_array_unref (aid); qmi_client_uim_unblock_pin (QMI_CLIENT_UIM (client), input, 5, NULL, (GAsyncReadyCallback) uim_unblock_pin_ready, task); qmi_message_uim_unblock_pin_input_unref (input); } static void dms_uim_unblock_pin_ready (QmiClientDms *client, GAsyncResult *res, GTask *task) { QmiMessageDmsUimUnblockPinOutput *output = NULL; GError *error = NULL; output = qmi_client_dms_uim_unblock_pin_finish (client, res, &error); if (!output) { g_prefix_error (&error, "QMI operation failed: "); g_task_return_error (task, error); } else if (!qmi_message_dms_uim_unblock_pin_output_get_result (output, &error)) { g_prefix_error (&error, "Couldn't unblock PIN: "); g_task_return_error (task, pin_qmi_error_to_mobile_equipment_error (error)); } else g_task_return_boolean (task, TRUE); if (output) qmi_message_dms_uim_unblock_pin_output_unref (output); g_object_unref (task); } static void dms_uim_unblock_pin (MMSimQmi *self, GTask *task) { QmiMessageDmsUimUnblockPinInput *input; QmiClient *client = NULL; UnblockPinContext *ctx; if (!ensure_qmi_client (NULL, self, QMI_SERVICE_DMS, &client)) { /* Very unlikely that this will ever happen, but anyway, try with * UIM service instead */ uim_unblock_pin (self, task); return; } ctx = g_task_get_task_data (task); input = qmi_message_dms_uim_unblock_pin_input_new (); qmi_message_dms_uim_unblock_pin_input_set_info ( input, QMI_DMS_UIM_PIN_ID_PIN, ctx->puk, ctx->new_pin, NULL); qmi_client_dms_uim_unblock_pin (QMI_CLIENT_DMS (client), input, 5, NULL, (GAsyncReadyCallback)dms_uim_unblock_pin_ready, task); qmi_message_dms_uim_unblock_pin_input_unref (input); } static void send_puk (MMBaseSim *_self, const gchar *puk, const gchar *new_pin, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; UnblockPinContext *ctx; MMSimQmi *self; self = MM_SIM_QMI (_self); task = g_task_new (self, NULL, callback, user_data); ctx = g_slice_new (UnblockPinContext); ctx->puk = g_strdup (puk); ctx->new_pin = g_strdup (new_pin); g_task_set_task_data (task, ctx, (GDestroyNotify) unblock_pin_context_free); mm_obj_dbg (self, "unblocking PIN..."); if (!self->priv->dms_uim_deprecated) dms_uim_unblock_pin (self, task); else uim_unblock_pin (self, task); } /*****************************************************************************/ /* Change PIN */ typedef struct { gchar *old_pin; gchar *new_pin; } ChangePinContext; static void change_pin_context_free (ChangePinContext *ctx) { g_free (ctx->old_pin); g_free (ctx->new_pin); g_slice_free (ChangePinContext, ctx); } static gboolean change_pin_finish (MMBaseSim *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void uim_change_pin_ready (QmiClientUim *client, GAsyncResult *res, GTask *task) { QmiMessageUimChangePinOutput *output = NULL; GError *error = NULL; output = qmi_client_uim_change_pin_finish (client, res, &error); if (!output) { g_prefix_error (&error, "QMI operation failed: "); g_task_return_error (task, error); } else if (!qmi_message_uim_change_pin_output_get_result (output, &error)) { g_prefix_error (&error, "Couldn't change PIN: "); g_task_return_error (task, pin_qmi_error_to_mobile_equipment_error (error)); } else g_task_return_boolean (task, TRUE); if (output) qmi_message_uim_change_pin_output_unref (output); g_object_unref (task); } static void uim_change_pin (MMSimQmi *self, GTask *task) { QmiMessageUimChangePinInput *input; QmiClient *client = NULL; ChangePinContext *ctx; GArray *aid; if (!ensure_qmi_client (task, self, QMI_SERVICE_UIM, &client)) return; ctx = g_task_get_task_data (task); input = qmi_message_uim_change_pin_input_new (); qmi_message_uim_change_pin_input_set_info ( input, QMI_UIM_PIN_ID_PIN1, ctx->old_pin, ctx->new_pin, NULL); aid = g_array_new (FALSE, FALSE, sizeof (guint8)); /* empty AID */ qmi_message_uim_change_pin_input_set_session ( input, QMI_UIM_SESSION_TYPE_CARD_SLOT_1, aid, NULL); g_array_unref (aid); qmi_client_uim_change_pin (QMI_CLIENT_UIM (client), input, 5, NULL, (GAsyncReadyCallback) uim_change_pin_ready, task); qmi_message_uim_change_pin_input_unref (input); } static void dms_uim_change_pin_ready (QmiClientDms *client, GAsyncResult *res, GTask *task) { QmiMessageDmsUimChangePinOutput *output = NULL; GError *error = NULL; output = qmi_client_dms_uim_change_pin_finish (client, res, &error); if (!output) { g_prefix_error (&error, "QMI operation failed: "); g_task_return_error (task, error); } else if (!qmi_message_dms_uim_change_pin_output_get_result (output, &error)) { g_prefix_error (&error, "Couldn't change PIN: "); g_task_return_error (task, pin_qmi_error_to_mobile_equipment_error (error)); } else g_task_return_boolean (task, TRUE); if (output) qmi_message_dms_uim_change_pin_output_unref (output); g_object_unref (task); } static void dms_uim_change_pin (MMSimQmi *self, GTask *task) { QmiMessageDmsUimChangePinInput *input; QmiClient *client = NULL; ChangePinContext *ctx; if (!ensure_qmi_client (NULL, self, QMI_SERVICE_DMS, &client)) { /* Very unlikely that this will ever happen, but anyway, try with * UIM service instead */ uim_change_pin (self, task); return; } ctx = g_task_get_task_data (task); input = qmi_message_dms_uim_change_pin_input_new (); qmi_message_dms_uim_change_pin_input_set_info ( input, QMI_DMS_UIM_PIN_ID_PIN, ctx->old_pin, ctx->new_pin, NULL); qmi_client_dms_uim_change_pin (QMI_CLIENT_DMS (client), input, 5, NULL, (GAsyncReadyCallback) dms_uim_change_pin_ready, task); qmi_message_dms_uim_change_pin_input_unref (input); } static void change_pin (MMBaseSim *_self, const gchar *old_pin, const gchar *new_pin, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; ChangePinContext *ctx; MMSimQmi *self; self = MM_SIM_QMI (_self); task = g_task_new (self, NULL, callback, user_data); ctx = g_slice_new (ChangePinContext); ctx->old_pin = g_strdup (old_pin); ctx->new_pin = g_strdup (new_pin); g_task_set_task_data (task, ctx, (GDestroyNotify) change_pin_context_free); mm_obj_dbg (self, "changing PIN..."); if (!self->priv->dms_uim_deprecated) dms_uim_change_pin (self, task); else uim_change_pin (self, task); } /*****************************************************************************/ /* Enable PIN */ typedef struct { gchar *pin; gboolean enabled; } EnablePinContext; static void enable_pin_context_free (EnablePinContext *ctx) { g_free (ctx->pin); g_slice_free (EnablePinContext, ctx); } static gboolean enable_pin_finish (MMBaseSim *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void uim_set_pin_protection_ready (QmiClientUim *client, GAsyncResult *res, GTask *task) { QmiMessageUimSetPinProtectionOutput *output = NULL; GError *error = NULL; output = qmi_client_uim_set_pin_protection_finish (client, res, &error); if (!output) { g_prefix_error (&error, "QMI operation failed: "); g_task_return_error (task, error); } else if (!qmi_message_uim_set_pin_protection_output_get_result (output, &error)) { g_prefix_error (&error, "Couldn't enable PIN: "); g_task_return_error (task, pin_qmi_error_to_mobile_equipment_error (error)); } else g_task_return_boolean (task, TRUE); if (output) qmi_message_uim_set_pin_protection_output_unref (output); g_object_unref (task); } static void uim_enable_pin (MMSimQmi *self, GTask *task) { QmiMessageUimSetPinProtectionInput *input; QmiClient *client = NULL; EnablePinContext *ctx; GArray *aid; if (!ensure_qmi_client (task, MM_SIM_QMI (self), QMI_SERVICE_UIM, &client)) return; ctx = g_task_get_task_data (task); input = qmi_message_uim_set_pin_protection_input_new (); qmi_message_uim_set_pin_protection_input_set_info ( input, QMI_UIM_PIN_ID_PIN1, ctx->enabled, ctx->pin, NULL); aid = g_array_new (FALSE, FALSE, sizeof (guint8)); /* empty AID */ qmi_message_uim_set_pin_protection_input_set_session ( input, QMI_UIM_SESSION_TYPE_CARD_SLOT_1, aid, NULL); g_array_unref (aid); qmi_client_uim_set_pin_protection (QMI_CLIENT_UIM (client), input, 5, NULL, (GAsyncReadyCallback)uim_set_pin_protection_ready, task); qmi_message_uim_set_pin_protection_input_unref (input); } static void dms_uim_set_pin_protection_ready (QmiClientDms *client, GAsyncResult *res, GTask *task) { QmiMessageDmsUimSetPinProtectionOutput *output = NULL; GError *error = NULL; output = qmi_client_dms_uim_set_pin_protection_finish (client, res, &error); if (!output) { g_prefix_error (&error, "QMI operation failed: "); g_task_return_error (task, error); } else if (!qmi_message_dms_uim_set_pin_protection_output_get_result (output, &error)) { g_prefix_error (&error, "Couldn't enable PIN: "); g_task_return_error (task, pin_qmi_error_to_mobile_equipment_error (error)); } else g_task_return_boolean (task, TRUE); if (output) qmi_message_dms_uim_set_pin_protection_output_unref (output); g_object_unref (task); } static void dms_uim_enable_pin (MMSimQmi *self, GTask *task) { QmiMessageDmsUimSetPinProtectionInput *input; QmiClient *client = NULL; EnablePinContext *ctx; if (!ensure_qmi_client (NULL, MM_SIM_QMI (self), QMI_SERVICE_DMS, &client)) { /* Very unlikely that this will ever happen, but anyway, try with * UIM service instead */ uim_enable_pin (self, task); return; } ctx = g_task_get_task_data (task); input = qmi_message_dms_uim_set_pin_protection_input_new (); qmi_message_dms_uim_set_pin_protection_input_set_info ( input, QMI_DMS_UIM_PIN_ID_PIN, ctx->enabled, ctx->pin, NULL); qmi_client_dms_uim_set_pin_protection (QMI_CLIENT_DMS (client), input, 5, NULL, (GAsyncReadyCallback)dms_uim_set_pin_protection_ready, task); qmi_message_dms_uim_set_pin_protection_input_unref (input); } static void enable_pin (MMBaseSim *_self, const gchar *pin, gboolean enabled, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; EnablePinContext *ctx; MMSimQmi *self; self = MM_SIM_QMI (_self); task = g_task_new (self, NULL, callback, user_data); ctx = g_slice_new (EnablePinContext); ctx->pin = g_strdup (pin); ctx->enabled = enabled; g_task_set_task_data (task, ctx, (GDestroyNotify) enable_pin_context_free); mm_obj_dbg (self, "%s PIN...", enabled ? "enabling" : "disabling"); if (!self->priv->dms_uim_deprecated) dms_uim_enable_pin (self, task); else uim_enable_pin (self, task); } /*****************************************************************************/ MMBaseSim * mm_sim_qmi_new_finish (GAsyncResult *res, GError **error) { GObject *source; GObject *sim; source = g_async_result_get_source_object (res); sim = g_async_initable_new_finish (G_ASYNC_INITABLE (source), res, error); g_object_unref (source); if (!sim) return NULL; /* Only export valid SIMs */ mm_base_sim_export (MM_BASE_SIM (sim)); return MM_BASE_SIM (sim); } void mm_sim_qmi_new (MMBaseModem *modem, gboolean dms_uim_deprecated, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { g_async_initable_new_async (MM_TYPE_SIM_QMI, G_PRIORITY_DEFAULT, cancellable, callback, user_data, MM_BASE_SIM_MODEM, modem, MM_SIM_QMI_DMS_UIM_DEPRECATED, dms_uim_deprecated, "active", TRUE, /* by default always active */ NULL); } MMBaseSim * mm_sim_qmi_new_initialized (MMBaseModem *modem, gboolean dms_uim_deprecated, guint slot_number, gboolean active, const gchar *sim_identifier, const gchar *imsi, const gchar *eid, const gchar *operator_identifier, const gchar *operator_name, const GStrv emergency_numbers) { MMBaseSim *sim; sim = MM_BASE_SIM (g_object_new (MM_TYPE_SIM_QMI, MM_BASE_SIM_MODEM, modem, MM_SIM_QMI_DMS_UIM_DEPRECATED, dms_uim_deprecated, MM_BASE_SIM_SLOT_NUMBER, slot_number, "active", active, "sim-identifier", sim_identifier, "imsi", imsi, "eid", eid, "operator-identifier", operator_identifier, "operator-name", operator_name, "emergency-numbers", emergency_numbers, NULL)); mm_base_sim_export (sim); return sim; } /*****************************************************************************/ static void mm_sim_qmi_init (MMSimQmi *self) { /* Initialize private data */ self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, MM_TYPE_SIM_QMI, MMSimQmiPrivate); } static void set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { MMSimQmi *self = MM_SIM_QMI (object); switch (prop_id) { case PROP_DMS_UIM_DEPRECATED: self->priv->dms_uim_deprecated = 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) { MMSimQmi *self = MM_SIM_QMI (object); switch (prop_id) { case PROP_DMS_UIM_DEPRECATED: g_value_set_boolean (value, self->priv->dms_uim_deprecated); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void finalize (GObject *object) { MMSimQmi *self = MM_SIM_QMI (object); g_free (self->priv->imsi); G_OBJECT_CLASS (mm_sim_qmi_parent_class)->finalize (object); } static void mm_sim_qmi_class_init (MMSimQmiClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); MMBaseSimClass *base_sim_class = MM_BASE_SIM_CLASS (klass); g_type_class_add_private (object_class, sizeof (MMSimQmiPrivate)); object_class->get_property = get_property; object_class->set_property = set_property; object_class->finalize = finalize; base_sim_class->wait_sim_ready = wait_sim_ready; base_sim_class->wait_sim_ready_finish = wait_sim_ready_finish; base_sim_class->load_sim_identifier = load_sim_identifier; base_sim_class->load_sim_identifier_finish = load_sim_identifier_finish; base_sim_class->load_imsi = load_imsi; base_sim_class->load_imsi_finish = load_imsi_finish; base_sim_class->load_operator_identifier = load_operator_identifier; base_sim_class->load_operator_identifier_finish = load_operator_identifier_finish; base_sim_class->load_operator_name = load_operator_name; base_sim_class->load_operator_name_finish = load_operator_name_finish; base_sim_class->load_gid1 = load_gid1; base_sim_class->load_gid1_finish = load_gid1_finish; base_sim_class->load_gid2 = load_gid2; base_sim_class->load_gid2_finish = load_gid2_finish; base_sim_class->load_preferred_networks = load_preferred_networks; base_sim_class->load_preferred_networks_finish = load_preferred_networks_finish; base_sim_class->set_preferred_networks = set_preferred_networks; base_sim_class->set_preferred_networks_finish = set_preferred_networks_finish; base_sim_class->send_pin = send_pin; base_sim_class->send_pin_finish = send_pin_finish; base_sim_class->send_puk = send_puk; base_sim_class->send_puk_finish = send_puk_finish; base_sim_class->change_pin = change_pin; base_sim_class->change_pin_finish = change_pin_finish; base_sim_class->enable_pin = enable_pin; base_sim_class->enable_pin_finish = enable_pin_finish; properties[PROP_DMS_UIM_DEPRECATED] = g_param_spec_boolean (MM_SIM_QMI_DMS_UIM_DEPRECATED, "DMS UIM deprecated", "Whether DMS UIM commands should be skipped", FALSE, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY); g_object_class_install_property (object_class, PROP_DMS_UIM_DEPRECATED, properties[PROP_DMS_UIM_DEPRECATED]); } ModemManager-1.23.4-dev/src/mm-sim-qmi.h000066400000000000000000000054271456466623000176600ustar00rootroot00000000000000/* -*- 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) 2012 Google, Inc. */ #ifndef MM_SIM_QMI_H #define MM_SIM_QMI_H #include #include #include "mm-base-sim.h" #define MM_TYPE_SIM_QMI (mm_sim_qmi_get_type ()) #define MM_SIM_QMI(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_SIM_QMI, MMSimQmi)) #define MM_SIM_QMI_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_SIM_QMI, MMSimQmiClass)) #define MM_IS_SIM_QMI(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_SIM_QMI)) #define MM_IS_SIM_QMI_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_SIM_QMI)) #define MM_SIM_QMI_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_SIM_QMI, MMSimQmiClass)) typedef struct _MMSimQmi MMSimQmi; typedef struct _MMSimQmiClass MMSimQmiClass; typedef struct _MMSimQmiPrivate MMSimQmiPrivate; #define MM_SIM_QMI_DMS_UIM_DEPRECATED "dms-uim-deprecated" struct _MMSimQmi { MMBaseSim parent; MMSimQmiPrivate *priv; }; struct _MMSimQmiClass { MMBaseSimClass parent; }; GType mm_sim_qmi_get_type (void); G_DEFINE_AUTOPTR_CLEANUP_FUNC (MMSimQmi, g_object_unref) void mm_sim_qmi_new (MMBaseModem *modem, gboolean dms_uim_deprecated, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); MMBaseSim *mm_sim_qmi_new_finish (GAsyncResult *res, GError **error); MMBaseSim *mm_sim_qmi_new_initialized (MMBaseModem *modem, gboolean dms_uim_deprecated, guint slot_number, gboolean active, const gchar *sim_identifier, const gchar *imsi, const gchar *eid, const gchar *operator_identifier, const gchar *operator_name, const GStrv emergency_numbers); #endif /* MM_SIM_QMI_H */ ModemManager-1.23.4-dev/src/mm-sleep-monitor-powerd.c000066400000000000000000000130541456466623000223650ustar00rootroot00000000000000/* -*- 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. * * 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. * * (C) Copyright 2022 Google, Inc. * Author: Rukun Mao * Original code from ./mm-sleep-monitor-systemd.c */ #include "config.h" #include #include #include #include #include #include "mm-log-object.h" #include "mm-utils.h" #include "mm-sleep-monitor.h" #define PD_NAME "org.chromium.PowerManager" #define PD_PATH "/org/chromium/PowerManager" #define PD_INTERFACE "org.chromium.PowerManager" struct _MMSleepMonitor { GObject parent_instance; GDBusProxy *pd_proxy; }; struct _MMSleepMonitorClass { GObjectClass parent_class; void (*sleeping) (MMSleepMonitor *monitor); void (*resuming) (MMSleepMonitor *monitor); }; enum { SLEEPING, RESUMING, LAST_SIGNAL, }; static guint signals[LAST_SIGNAL] = {0}; static void log_object_iface_init (MMLogObjectInterface *iface); G_DEFINE_TYPE_EXTENDED (MMSleepMonitor, mm_sleep_monitor, G_TYPE_OBJECT, 0, G_IMPLEMENT_INTERFACE (MM_TYPE_LOG_OBJECT, log_object_iface_init)) /*****************************************************************************/ static gchar * log_object_build_id (MMLogObject *_self) { return g_strdup ("sleep-monitor-powerd"); } /********************************************************************/ static void signal_cb (GDBusProxy *proxy, const gchar *sendername, const gchar *signalname, GVariant *args, gpointer data) { MMSleepMonitor *self = data; if (proxy == self->pd_proxy) { if (strcmp (signalname, "SuspendImminent") == 0) { mm_obj_msg (self, "system suspend signal from powerd"); g_signal_emit (self, signals[SLEEPING], 0); } else if (strcmp (signalname, "SuspendDone") == 0) { mm_obj_msg (self, "system resume signal from powerd"); g_signal_emit (self, signals[RESUMING], 0); } } } static void on_pd_proxy_acquired (GObject *object, GAsyncResult *res, MMSleepMonitor *self) { GError *error = NULL; self->pd_proxy = g_dbus_proxy_new_for_bus_finish (res, &error); if (!self->pd_proxy) { mm_obj_warn (self, "failed to acquire powerd proxy: %s", error->message); g_clear_error (&error); return; } g_signal_connect (self->pd_proxy, "g-signal", G_CALLBACK (signal_cb), self); } static void mm_sleep_monitor_init (MMSleepMonitor *self) { g_dbus_proxy_new_for_bus (G_BUS_TYPE_SYSTEM, G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START | G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES, NULL, PD_NAME, PD_PATH, PD_INTERFACE, NULL, (GAsyncReadyCallback) on_pd_proxy_acquired, self); } static void finalize (GObject *object) { MMSleepMonitor *self = MM_SLEEP_MONITOR (object); if (self->pd_proxy) g_object_unref (self->pd_proxy); if (G_OBJECT_CLASS (mm_sleep_monitor_parent_class)->finalize != NULL) G_OBJECT_CLASS (mm_sleep_monitor_parent_class)->finalize (object); } static void log_object_iface_init (MMLogObjectInterface *iface) { iface->build_id = log_object_build_id; } static void mm_sleep_monitor_class_init (MMSleepMonitorClass *klass) { GObjectClass *gobject_class; gobject_class = G_OBJECT_CLASS (klass); gobject_class->finalize = finalize; signals[SLEEPING] = g_signal_new (MM_SLEEP_MONITOR_SLEEPING, MM_TYPE_SLEEP_MONITOR, G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (MMSleepMonitorClass, sleeping), NULL, /* accumulator */ NULL, /* accumulator data */ g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); signals[RESUMING] = g_signal_new (MM_SLEEP_MONITOR_RESUMING, MM_TYPE_SLEEP_MONITOR, G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (MMSleepMonitorClass, resuming), NULL, /* accumulator */ NULL, /* accumulator data */ g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); } MM_DEFINE_SINGLETON_GETTER (MMSleepMonitor, mm_sleep_monitor_get, MM_TYPE_SLEEP_MONITOR); /* ---------------------------------------------------------------------------------------------------- */ ModemManager-1.23.4-dev/src/mm-sleep-monitor-systemd.c000066400000000000000000000204601456466623000225540ustar00rootroot00000000000000/* -*- 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. * * 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. * * (C) Copyright 2012 Red Hat, Inc. * Author: Matthias Clasen */ #include "config.h" #include #include #include #include #include #include #include "mm-log-object.h" #include "mm-utils.h" #include "mm-sleep-monitor.h" #define SD_NAME "org.freedesktop.login1" #define SD_PATH "/org/freedesktop/login1" #define SD_INTERFACE "org.freedesktop.login1.Manager" struct _MMSleepMonitor { GObject parent_instance; GDBusProxy *sd_proxy; gint inhibit_fd; }; struct _MMSleepMonitorClass { GObjectClass parent_class; void (*sleeping) (MMSleepMonitor *monitor); void (*resuming) (MMSleepMonitor *monitor); }; enum { SLEEPING, RESUMING, LAST_SIGNAL, }; static guint signals[LAST_SIGNAL] = {0}; static void log_object_iface_init (MMLogObjectInterface *iface); G_DEFINE_TYPE_EXTENDED (MMSleepMonitor, mm_sleep_monitor, G_TYPE_OBJECT, 0, G_IMPLEMENT_INTERFACE (MM_TYPE_LOG_OBJECT, log_object_iface_init)) /*****************************************************************************/ static gchar * log_object_build_id (MMLogObject *_self) { return g_strdup ("sleep-monitor-systemd"); } /********************************************************************/ static gboolean drop_inhibitor (MMSleepMonitor *self) { if (self->inhibit_fd >= 0) { mm_obj_dbg (self, "dropping systemd sleep inhibitor"); close (self->inhibit_fd); self->inhibit_fd = -1; return TRUE; } return FALSE; } static void inhibit_done (GObject *source, GAsyncResult *result, gpointer user_data) { GDBusProxy *sd_proxy = G_DBUS_PROXY (source); MMSleepMonitor *self = user_data; GError *error = NULL; GVariant *res; GUnixFDList *fd_list; res = g_dbus_proxy_call_with_unix_fd_list_finish (sd_proxy, &fd_list, result, &error); if (!res) { mm_obj_warn (self, "inhibit failed: %s", error->message); g_error_free (error); } else { if (!fd_list || g_unix_fd_list_get_length (fd_list) != 1) mm_obj_warn (self, "didn't get a single fd back"); self->inhibit_fd = g_unix_fd_list_get (fd_list, 0, NULL); mm_obj_dbg (self, "inhibitor fd is %d", self->inhibit_fd); g_object_unref (fd_list); g_variant_unref (res); } } static void take_inhibitor (MMSleepMonitor *self) { g_assert (self->inhibit_fd == -1); mm_obj_dbg (self, "taking systemd sleep inhibitor"); g_dbus_proxy_call_with_unix_fd_list (self->sd_proxy, "Inhibit", g_variant_new ("(ssss)", "sleep", "ModemManager", _("ModemManager needs to reset devices"), "delay"), 0, G_MAXINT, NULL, NULL, inhibit_done, self); } static void signal_cb (GDBusProxy *proxy, const gchar *sendername, const gchar *signalname, GVariant *args, gpointer data) { MMSleepMonitor *self = data; gboolean is_about_to_suspend; if (strcmp (signalname, "PrepareForSleep") != 0) return; g_variant_get (args, "(b)", &is_about_to_suspend); if (is_about_to_suspend) { mm_obj_msg (self, "system is about to suspend"); g_signal_emit (self, signals[SLEEPING], 0); drop_inhibitor (self); } else { mm_obj_msg (self, "system is resuming"); take_inhibitor (self); g_signal_emit (self, signals[RESUMING], 0); } } static void name_owner_cb (GObject *object, GParamSpec *pspec, gpointer user_data) { GDBusProxy *proxy = G_DBUS_PROXY (object); MMSleepMonitor *self = MM_SLEEP_MONITOR (user_data); char *owner; g_assert (proxy == self->sd_proxy); owner = g_dbus_proxy_get_name_owner (proxy); if (owner) take_inhibitor (self); else drop_inhibitor (self); g_free (owner); } static void on_proxy_acquired (GObject *object, GAsyncResult *res, MMSleepMonitor *self) { GError *error = NULL; char *owner; self->sd_proxy = g_dbus_proxy_new_for_bus_finish (res, &error); if (!self->sd_proxy) { mm_obj_warn (self, "failed to acquire logind proxy: %s", error->message); g_clear_error (&error); return; } g_signal_connect (self->sd_proxy, "notify::g-name-owner", G_CALLBACK (name_owner_cb), self); g_signal_connect (self->sd_proxy, "g-signal", G_CALLBACK (signal_cb), self); owner = g_dbus_proxy_get_name_owner (self->sd_proxy); if (owner) take_inhibitor (self); g_free (owner); } static void mm_sleep_monitor_init (MMSleepMonitor *self) { self->inhibit_fd = -1; g_dbus_proxy_new_for_bus (G_BUS_TYPE_SYSTEM, G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START | G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES, NULL, SD_NAME, SD_PATH, SD_INTERFACE, NULL, (GAsyncReadyCallback) on_proxy_acquired, self); } static void finalize (GObject *object) { MMSleepMonitor *self = MM_SLEEP_MONITOR (object); drop_inhibitor (self); if (self->sd_proxy) g_object_unref (self->sd_proxy); if (G_OBJECT_CLASS (mm_sleep_monitor_parent_class)->finalize != NULL) G_OBJECT_CLASS (mm_sleep_monitor_parent_class)->finalize (object); } static void log_object_iface_init (MMLogObjectInterface *iface) { iface->build_id = log_object_build_id; } static void mm_sleep_monitor_class_init (MMSleepMonitorClass *klass) { GObjectClass *gobject_class; gobject_class = G_OBJECT_CLASS (klass); gobject_class->finalize = finalize; signals[SLEEPING] = g_signal_new (MM_SLEEP_MONITOR_SLEEPING, MM_TYPE_SLEEP_MONITOR, G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (MMSleepMonitorClass, sleeping), NULL, /* accumulator */ NULL, /* accumulator data */ g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); signals[RESUMING] = g_signal_new (MM_SLEEP_MONITOR_RESUMING, MM_TYPE_SLEEP_MONITOR, G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (MMSleepMonitorClass, resuming), NULL, /* accumulator */ NULL, /* accumulator data */ g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); } MM_DEFINE_SINGLETON_GETTER (MMSleepMonitor, mm_sleep_monitor_get, MM_TYPE_SLEEP_MONITOR); /* ---------------------------------------------------------------------------------------------------- */ ModemManager-1.23.4-dev/src/mm-sleep-monitor.h000066400000000000000000000034141456466623000210730ustar00rootroot00000000000000/* -*- 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. * * (C) Copyright 2012 Red Hat, Inc. * Author: Matthias Clasen * Original code imported from NetworkManager. */ #ifndef __MM_SLEEP_MONITOR_H__ #define __MM_SLEEP_MONITOR_H__ #include G_BEGIN_DECLS #define MM_TYPE_SLEEP_MONITOR (mm_sleep_monitor_get_type ()) #define MM_SLEEP_MONITOR(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), MM_TYPE_SLEEP_MONITOR, MMSleepMonitor)) #define MM_SLEEP_MONITOR_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), MM_TYPE_SLEEP_MONITOR, MMSleepMonitorClass)) #define MM_SLEEP_MONITOR_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), MM_TYPE_SLEEP_MONITOR, MMSleepMonitorClass)) #define MM_IS_SLEEP_MONITOR(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), MM_TYPE_SLEEP_MONITOR)) #define MM_IS_SLEEP_MONITOR_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), MM_TYPE_SLEEP_MONITOR)) #define MM_SLEEP_MONITOR_SLEEPING "sleeping" #define MM_SLEEP_MONITOR_RESUMING "resuming" typedef struct _MMSleepMonitor MMSleepMonitor; typedef struct _MMSleepMonitorClass MMSleepMonitorClass; GType mm_sleep_monitor_get_type (void) G_GNUC_CONST; MMSleepMonitor *mm_sleep_monitor_get (void); G_END_DECLS #endif /* __MM_SLEEP_MONITOR_H__ */ ModemManager-1.23.4-dev/src/mm-sms-list.c000066400000000000000000000365661456466623000200620ustar00rootroot00000000000000/* -*- 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) 2012 Google, Inc. */ #include #include #include #include #include #include #include #define _LIBMM_INSIDE_MM #include #include "mm-iface-modem-messaging.h" #include "mm-sms-list.h" #include "mm-base-sms.h" #include "mm-log-object.h" static void log_object_iface_init (MMLogObjectInterface *iface); G_DEFINE_TYPE_EXTENDED (MMSmsList, mm_sms_list, G_TYPE_OBJECT, 0, G_IMPLEMENT_INTERFACE (MM_TYPE_LOG_OBJECT, log_object_iface_init)) enum { PROP_0, PROP_MODEM, PROP_LAST }; static GParamSpec *properties[PROP_LAST]; enum { SIGNAL_ADDED, SIGNAL_DELETED, SIGNAL_LAST }; static guint signals[SIGNAL_LAST]; struct _MMSmsListPrivate { /* The owner modem */ MMBaseModem *modem; /* List of sms objects */ GList *list; }; /*****************************************************************************/ gboolean mm_sms_list_has_local_multipart_reference (MMSmsList *self, const gchar *number, guint8 reference) { GList *l; /* No one should look for multipart reference 0, which isn't valid */ g_assert (reference != 0); for (l = self->priv->list; l; l = g_list_next (l)) { MMBaseSms *sms = MM_BASE_SMS (l->data); if (mm_base_sms_is_multipart (sms) && mm_gdbus_sms_get_pdu_type (MM_GDBUS_SMS (sms)) == MM_SMS_PDU_TYPE_SUBMIT && mm_base_sms_get_storage (sms) != MM_SMS_STORAGE_UNKNOWN && mm_base_sms_get_multipart_reference (sms) == reference && g_str_equal (mm_gdbus_sms_get_number (MM_GDBUS_SMS (sms)), number)) { /* Yes, the SMS list has an SMS with the same destination number * and multipart reference */ return TRUE; } } return FALSE; } /*****************************************************************************/ guint mm_sms_list_get_count (MMSmsList *self) { return g_list_length (self->priv->list); } GStrv mm_sms_list_get_paths (MMSmsList *self) { GStrv path_list = NULL; GList *l; guint i; path_list = g_new0 (gchar *, 1 + g_list_length (self->priv->list)); for (i = 0, l = self->priv->list; l; l = g_list_next (l)) { const gchar *path; /* Don't try to add NULL paths (not yet exported SMS objects) */ path = mm_base_sms_get_path (MM_BASE_SMS (l->data)); if (path) path_list[i++] = g_strdup (path); } return path_list; } /*****************************************************************************/ gboolean mm_sms_list_delete_sms_finish (MMSmsList *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static guint cmp_sms_by_path (MMBaseSms *sms, const gchar *path) { return g_strcmp0 (mm_base_sms_get_path (sms), path); } static void delete_ready (MMBaseSms *sms, GAsyncResult *res, GTask *task) { MMSmsList *self; const gchar *path; GError *error = NULL; GList *l; if (!mm_base_sms_delete_finish (sms, res, &error)) { /* We report the error */ g_task_return_error (task, error); g_object_unref (task); return; } self = g_task_get_source_object (task); path = g_task_get_task_data (task); /* The SMS was properly deleted, we now remove it from our list */ l = g_list_find_custom (self->priv->list, path, (GCompareFunc)cmp_sms_by_path); if (l) { g_object_unref (MM_BASE_SMS (l->data)); self->priv->list = g_list_delete_link (self->priv->list, l); } /* We don't need to unref the SMS any more, but we can use the * reference we got in the method, which is the one kept alive * during the async operation. */ mm_base_sms_unexport (sms); g_signal_emit (self, signals[SIGNAL_DELETED], 0, path); g_task_return_boolean (task, TRUE); g_object_unref (task); } void mm_sms_list_delete_sms (MMSmsList *self, const gchar *sms_path, GAsyncReadyCallback callback, gpointer user_data) { GList *l; GTask *task; l = g_list_find_custom (self->priv->list, (gpointer)sms_path, (GCompareFunc)cmp_sms_by_path); if (!l) { g_task_report_new_error (self, callback, user_data, mm_sms_list_delete_sms, MM_CORE_ERROR, MM_CORE_ERROR_NOT_FOUND, "No SMS found with path '%s'", sms_path); return; } /* Delete all SMS parts */ task = g_task_new (self, NULL, callback, user_data); g_task_set_task_data (task, g_strdup (sms_path), g_free); mm_base_sms_delete (MM_BASE_SMS (l->data), (GAsyncReadyCallback)delete_ready, task); } /*****************************************************************************/ void mm_sms_list_add_sms (MMSmsList *self, MMBaseSms *sms) { self->priv->list = g_list_prepend (self->priv->list, g_object_ref (sms)); g_signal_emit (self, signals[SIGNAL_ADDED], 0, mm_base_sms_get_path (sms), FALSE); } /*****************************************************************************/ static guint cmp_sms_by_concat_reference (MMBaseSms *sms, gpointer user_data) { if (!mm_base_sms_is_multipart (sms)) return -1; return (GPOINTER_TO_UINT (user_data) - mm_base_sms_get_multipart_reference (sms)); } typedef struct { guint part_index; MMSmsStorage storage; } PartIndexAndStorage; static guint cmp_sms_by_part_index_and_storage (MMBaseSms *sms, PartIndexAndStorage *ctx) { return !(mm_base_sms_get_storage (sms) == ctx->storage && mm_base_sms_has_part_index (sms, ctx->part_index)); } static gboolean take_singlepart (MMSmsList *self, MMSmsPart *part, MMSmsState state, MMSmsStorage storage, GError **error) { MMBaseSms *sms; sms = mm_base_sms_singlepart_new (self->priv->modem, state, storage, part, error); if (!sms) return FALSE; self->priv->list = g_list_prepend (self->priv->list, sms); g_signal_emit (self, signals[SIGNAL_ADDED], 0, mm_base_sms_get_path (sms), state == MM_SMS_STATE_RECEIVED); return TRUE; } static gboolean take_multipart (MMSmsList *self, MMSmsPart *part, MMSmsState state, MMSmsStorage storage, GError **error) { GList *l; MMBaseSms *sms; guint concat_reference; concat_reference = mm_sms_part_get_concat_reference (part); l = g_list_find_custom (self->priv->list, GUINT_TO_POINTER (concat_reference), (GCompareFunc)cmp_sms_by_concat_reference); if (l) { /* Try to take the part */ mm_obj_dbg (self, "found existing multipart SMS object with reference '%u': adding new part", concat_reference); return mm_base_sms_multipart_take_part (MM_BASE_SMS (l->data), part, error); } /* Create new Multipart */ sms = mm_base_sms_multipart_new (self->priv->modem, state, storage, concat_reference, mm_sms_part_get_concat_max (part), part, error); if (!sms) return FALSE; mm_obj_dbg (self, "creating new multipart SMS object: need to receive %u parts with reference '%u'", mm_sms_part_get_concat_max (part), concat_reference); self->priv->list = g_list_prepend (self->priv->list, sms); g_signal_emit (self, signals[SIGNAL_ADDED], 0, mm_base_sms_get_path (sms), (state == MM_SMS_STATE_RECEIVED || state == MM_SMS_STATE_RECEIVING)); return TRUE; } gboolean mm_sms_list_has_part (MMSmsList *self, MMSmsStorage storage, guint index) { PartIndexAndStorage ctx; if (storage == MM_SMS_STORAGE_UNKNOWN || index == SMS_PART_INVALID_INDEX) return FALSE; ctx.part_index = index; ctx.storage = storage; return !!g_list_find_custom (self->priv->list, &ctx, (GCompareFunc)cmp_sms_by_part_index_and_storage); } gboolean mm_sms_list_take_part (MMSmsList *self, MMSmsPart *part, MMSmsState state, MMSmsStorage storage, GError **error) { /* Ensure we don't have already taken a part with the same index */ if (mm_sms_list_has_part (self, storage, mm_sms_part_get_index (part))) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "A part with index %u was already taken", mm_sms_part_get_index (part)); return FALSE; } /* Did we just get a part of a multi-part SMS? */ if (mm_sms_part_should_concat (part)) { if (mm_sms_part_get_index (part) != SMS_PART_INVALID_INDEX) mm_obj_dbg (self, "SMS part at '%s/%u' is from a multipart SMS (reference: '%u', sequence: '%u/%u')", mm_sms_storage_get_string (storage), mm_sms_part_get_index (part), mm_sms_part_get_concat_reference (part), mm_sms_part_get_concat_sequence (part), mm_sms_part_get_concat_max (part)); else mm_obj_dbg (self, "SMS part (not stored) is from a multipart SMS (reference: '%u', sequence: '%u/%u')", mm_sms_part_get_concat_reference (part), mm_sms_part_get_concat_sequence (part), mm_sms_part_get_concat_max (part)); return take_multipart (self, part, state, storage, error); } /* Otherwise, we build a whole new single-part MMSms just from this part */ if (mm_sms_part_get_index (part) != SMS_PART_INVALID_INDEX) mm_obj_dbg (self, "SMS part at '%s/%u' is from a singlepart SMS", mm_sms_storage_get_string (storage), mm_sms_part_get_index (part)); else mm_obj_dbg (self, "SMS part (not stored) is from a singlepart SMS"); return take_singlepart (self, part, state, storage, error); } /*****************************************************************************/ static gchar * log_object_build_id (MMLogObject *_self) { return g_strdup ("sms-list"); } /*****************************************************************************/ MMSmsList * mm_sms_list_new (MMBaseModem *modem) { /* Create the object */ return g_object_new (MM_TYPE_SMS_LIST, MM_SMS_LIST_MODEM, modem, NULL); } static void set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { MMSmsList *self = MM_SMS_LIST (object); switch (prop_id) { case PROP_MODEM: g_clear_object (&self->priv->modem); self->priv->modem = g_value_dup_object (value); if (self->priv->modem) { /* Set owner ID */ mm_log_object_set_owner_id (MM_LOG_OBJECT (self), mm_log_object_get_id (MM_LOG_OBJECT (self->priv->modem))); } 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) { MMSmsList *self = MM_SMS_LIST (object); switch (prop_id) { case PROP_MODEM: g_value_set_object (value, self->priv->modem); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void mm_sms_list_init (MMSmsList *self) { /* Initialize private data */ self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, MM_TYPE_SMS_LIST, MMSmsListPrivate); } static void dispose (GObject *object) { MMSmsList *self = MM_SMS_LIST (object); g_clear_object (&self->priv->modem); g_list_free_full (self->priv->list, g_object_unref); self->priv->list = NULL; G_OBJECT_CLASS (mm_sms_list_parent_class)->dispose (object); } static void log_object_iface_init (MMLogObjectInterface *iface) { iface->build_id = log_object_build_id; } static void mm_sms_list_class_init (MMSmsListClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); g_type_class_add_private (object_class, sizeof (MMSmsListPrivate)); /* Virtual methods */ object_class->get_property = get_property; object_class->set_property = set_property; object_class->dispose = dispose; /* Properties */ properties[PROP_MODEM] = g_param_spec_object (MM_SMS_LIST_MODEM, "Modem", "The Modem which owns this SMS list", MM_TYPE_BASE_MODEM, G_PARAM_READWRITE); g_object_class_install_property (object_class, PROP_MODEM, properties[PROP_MODEM]); /* Signals */ signals[SIGNAL_ADDED] = g_signal_new (MM_SMS_ADDED, G_OBJECT_CLASS_TYPE (object_class), G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (MMSmsListClass, sms_added), NULL, NULL, g_cclosure_marshal_generic, G_TYPE_NONE, 2, G_TYPE_STRING, G_TYPE_BOOLEAN); signals[SIGNAL_DELETED] = g_signal_new (MM_SMS_DELETED, G_OBJECT_CLASS_TYPE (object_class), G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (MMSmsListClass, sms_deleted), NULL, NULL, g_cclosure_marshal_generic, G_TYPE_NONE, 1, G_TYPE_STRING); } ModemManager-1.23.4-dev/src/mm-sms-list.h000066400000000000000000000064351456466623000200570ustar00rootroot00000000000000/* -*- 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) 2012 Google, Inc. */ #ifndef MM_SMS_LIST_H #define MM_SMS_LIST_H #include #include #include "mm-base-modem.h" #include "mm-sms-part.h" #define MM_TYPE_SMS_LIST (mm_sms_list_get_type ()) #define MM_SMS_LIST(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_SMS_LIST, MMSmsList)) #define MM_SMS_LIST_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_SMS_LIST, MMSmsListClass)) #define MM_IS_SMS_LIST(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_SMS_LIST)) #define MM_IS_SMS_LIST_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_SMS_LIST)) #define MM_SMS_LIST_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_SMS_LIST, MMSmsListClass)) typedef struct _MMSmsList MMSmsList; typedef struct _MMSmsListClass MMSmsListClass; typedef struct _MMSmsListPrivate MMSmsListPrivate; #define MM_SMS_LIST_MODEM "sms-list-modem" #define MM_SMS_ADDED "sms-added" #define MM_SMS_DELETED "sms-deleted" struct _MMSmsList { GObject parent; MMSmsListPrivate *priv; }; struct _MMSmsListClass { GObjectClass parent; /* Signals */ void (*sms_added) (MMSmsList *self, const gchar *sms_path, gboolean received); void (*sms_deleted) (MMSmsList *self, const gchar *sms_path); }; GType mm_sms_list_get_type (void); G_DEFINE_AUTOPTR_CLEANUP_FUNC (MMSmsList, g_object_unref) MMSmsList *mm_sms_list_new (MMBaseModem *modem); GStrv mm_sms_list_get_paths (MMSmsList *self); guint mm_sms_list_get_count (MMSmsList *self); gboolean mm_sms_list_has_part (MMSmsList *self, MMSmsStorage storage, guint index); gboolean mm_sms_list_take_part (MMSmsList *self, MMSmsPart *part, MMSmsState state, MMSmsStorage storage, GError **error); void mm_sms_list_add_sms (MMSmsList *self, MMBaseSms *sms); void mm_sms_list_delete_sms (MMSmsList *self, const gchar *sms_path, GAsyncReadyCallback callback, gpointer user_data); gboolean mm_sms_list_delete_sms_finish (MMSmsList *self, GAsyncResult *res, GError **error); gboolean mm_sms_list_has_local_multipart_reference (MMSmsList *self, const gchar *number, guint8 reference); #endif /* MM_SMS_LIST_H */ ModemManager-1.23.4-dev/src/mm-sms-mbim.c000066400000000000000000000243741456466623000200250ustar00rootroot00000000000000/* -*- 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) 2013 Aleksander Morgado */ #include #include #include #include #include #include #include #define _LIBMM_INSIDE_MM #include #include "mm-broadband-modem-mbim.h" #include "mm-modem-helpers-mbim.h" #include "mm-iface-modem-messaging.h" #include "mm-sms-mbim.h" #include "mm-base-modem.h" #include "mm-log-object.h" #include "mm-sms-part-3gpp.h" G_DEFINE_TYPE (MMSmsMbim, mm_sms_mbim, MM_TYPE_BASE_SMS) /*****************************************************************************/ static gboolean peek_device (gpointer self, MbimDevice **o_device, GAsyncReadyCallback callback, gpointer user_data) { MMBaseModem *modem = NULL; g_object_get (G_OBJECT (self), MM_BASE_SMS_MODEM, &modem, NULL); g_assert (MM_IS_BASE_MODEM (modem)); if (o_device) { MMPortMbim *port; port = mm_broadband_modem_mbim_peek_port_mbim (MM_BROADBAND_MODEM_MBIM (modem)); if (!port) { g_task_report_new_error (self, callback, user_data, peek_device, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't peek MBIM port"); g_object_unref (modem); return FALSE; } *o_device = mm_port_mbim_peek_device (port); } g_object_unref (modem); return TRUE; } /*****************************************************************************/ /* Send the SMS */ typedef struct { MMBaseModem *modem; MbimDevice *device; GList *current; } SmsSendContext; static void sms_send_context_free (SmsSendContext *ctx) { g_object_unref (ctx->device); g_object_unref (ctx->modem); g_slice_free (SmsSendContext, ctx); } static gboolean sms_send_finish (MMBaseSms *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void sms_send_next_part (GTask *task); static void sms_send_set_ready (MbimDevice *device, GAsyncResult *res, GTask *task) { SmsSendContext *ctx; MbimMessage *response; GError *error = NULL; guint32 message_reference; ctx = g_task_get_task_data (task); response = mbim_device_command_finish (device, res, &error); if (response && mbim_message_response_get_result (response, MBIM_MESSAGE_TYPE_COMMAND_DONE, &error) && mbim_message_sms_send_response_parse ( response, &message_reference, &error)) { mm_sms_part_set_message_reference ((MMSmsPart *)ctx->current->data, message_reference); } if (response) mbim_message_unref (response); if (error) { g_prefix_error (&error, "Couldn't send SMS part: "); g_task_return_error (task, error); g_object_unref (task); return; } /* Go on with next part */ ctx->current = g_list_next (ctx->current); sms_send_next_part (task); } static void sms_send_next_part (GTask *task) { MMSmsMbim *self; SmsSendContext *ctx; MbimMessage *message; guint8 *pdu; guint pdulen = 0; guint msgstart = 0; GError *error = NULL; MbimSmsPduSendRecord send_record; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); if (!ctx->current) { /* Done we are */ g_task_return_boolean (task, TRUE); g_object_unref (task); return; } /* Get PDU */ pdu = mm_sms_part_3gpp_get_submit_pdu ((MMSmsPart *)ctx->current->data, &pdulen, &msgstart, self, &error); if (!pdu) { g_task_return_error (task, error); g_object_unref (task); return; } send_record.pdu_data_size = pdulen; send_record.pdu_data = pdu; message = mbim_message_sms_send_set_new (MBIM_SMS_FORMAT_PDU, &send_record, NULL, NULL); mbim_device_command (ctx->device, message, MM_BASE_SMS_DEFAULT_SEND_TIMEOUT, NULL, (GAsyncReadyCallback)sms_send_set_ready, task); mbim_message_unref (message); g_free (pdu); } static void sms_send (MMBaseSms *self, GAsyncReadyCallback callback, gpointer user_data) { SmsSendContext *ctx; MbimDevice *device; GTask *task; if (!peek_device (self, &device, callback, user_data)) return; /* Setup the context */ ctx = g_slice_new0 (SmsSendContext); ctx->device = g_object_ref (device); g_object_get (self, MM_BASE_SMS_MODEM, &ctx->modem, NULL); task = g_task_new (self, NULL, callback, user_data); g_task_set_task_data (task, ctx, (GDestroyNotify)sms_send_context_free); ctx->current = mm_base_sms_get_parts (self);; sms_send_next_part (task); } /*****************************************************************************/ typedef struct { MMBaseModem *modem; MbimDevice *device; GList *current; guint n_failed; } SmsDeletePartsContext; static void sms_delete_parts_context_free (SmsDeletePartsContext *ctx) { g_object_unref (ctx->device); g_object_unref (ctx->modem); g_slice_free (SmsDeletePartsContext, ctx); } static gboolean sms_delete_finish (MMBaseSms *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void delete_next_part (GTask *task); static void sms_delete_set_ready (MbimDevice *device, GAsyncResult *res, GTask *task) { MMSmsMbim *self; SmsDeletePartsContext *ctx; MbimMessage *response; GError *error = NULL; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); response = mbim_device_command_finish (device, res, &error); if (response && mbim_message_response_get_result (response, MBIM_MESSAGE_TYPE_COMMAND_DONE, &error)) mbim_message_sms_delete_response_parse (response, &error); if (response) mbim_message_unref (response); if (error) { ctx->n_failed++; mm_obj_dbg (self, "couldn't delete SMS part with index %u: %s", mm_sms_part_get_index ((MMSmsPart *)ctx->current->data), error->message); g_error_free (error); } /* We reset the index, as there is no longer that part */ mm_sms_part_set_index ((MMSmsPart *)ctx->current->data, SMS_PART_INVALID_INDEX); ctx->current = g_list_next (ctx->current); delete_next_part (task); } static void delete_next_part (GTask *task) { SmsDeletePartsContext *ctx; MbimMessage *message; ctx = g_task_get_task_data (task); /* Skip non-stored parts */ while (ctx->current && mm_sms_part_get_index ((MMSmsPart *)ctx->current->data) == SMS_PART_INVALID_INDEX) ctx->current = g_list_next (ctx->current); /* If all removed, we're done */ if (!ctx->current) { if (ctx->n_failed > 0) g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't delete %u parts from this SMS", ctx->n_failed); else g_task_return_boolean (task, TRUE); g_object_unref (task); return; } message = mbim_message_sms_delete_set_new (MBIM_SMS_FLAG_INDEX, (guint32)mm_sms_part_get_index ((MMSmsPart *)ctx->current->data), NULL); mbim_device_command (ctx->device, message, 10, NULL, (GAsyncReadyCallback)sms_delete_set_ready, task); mbim_message_unref (message); } static void sms_delete (MMBaseSms *self, GAsyncReadyCallback callback, gpointer user_data) { SmsDeletePartsContext *ctx; MbimDevice *device; GTask *task; if (!peek_device (self, &device, callback, user_data)) return; ctx = g_slice_new0 (SmsDeletePartsContext); ctx->device = g_object_ref (device); g_object_get (self, MM_BASE_SMS_MODEM, &ctx->modem, NULL); task = g_task_new (self, NULL, callback, user_data); g_task_set_task_data (task, ctx, (GDestroyNotify)sms_delete_parts_context_free); /* Go on deleting parts */ ctx->current = mm_base_sms_get_parts (self); delete_next_part (task); } /*****************************************************************************/ MMBaseSms * mm_sms_mbim_new (MMBaseModem *modem) { return MM_BASE_SMS (g_object_new (MM_TYPE_SMS_MBIM, MM_BASE_SMS_MODEM, modem, NULL)); } static void mm_sms_mbim_init (MMSmsMbim *self) { } static void mm_sms_mbim_class_init (MMSmsMbimClass *klass) { MMBaseSmsClass *base_sms_class = MM_BASE_SMS_CLASS (klass); base_sms_class->store = NULL; base_sms_class->store_finish = NULL; base_sms_class->send = sms_send; base_sms_class->send_finish = sms_send_finish; base_sms_class->delete = sms_delete; base_sms_class->delete_finish = sms_delete_finish; } ModemManager-1.23.4-dev/src/mm-sms-mbim.h000066400000000000000000000033101456466623000200150ustar00rootroot00000000000000/* -*- 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) 2013 Aleksander Morgado */ #ifndef MM_SMS_MBIM_H #define MM_SMS_MBIM_H #include #include #define _LIBMM_INSIDE_MM #include #include "mm-base-sms.h" #define MM_TYPE_SMS_MBIM (mm_sms_mbim_get_type ()) #define MM_SMS_MBIM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_SMS_MBIM, MMSmsMbim)) #define MM_SMS_MBIM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_SMS_MBIM, MMSmsMbimClass)) #define MM_IS_SMS_MBIM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_SMS_MBIM)) #define MM_IS_SMS_MBIM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_SMS_MBIM)) #define MM_SMS_MBIM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_SMS_MBIM, MMSmsMbimClass)) typedef struct _MMSmsMbim MMSmsMbim; typedef struct _MMSmsMbimClass MMSmsMbimClass; struct _MMSmsMbim { MMBaseSms parent; }; struct _MMSmsMbimClass { MMBaseSmsClass parent; }; GType mm_sms_mbim_get_type (void); G_DEFINE_AUTOPTR_CLEANUP_FUNC (MMSmsMbim, g_object_unref) MMBaseSms *mm_sms_mbim_new (MMBaseModem *modem); #endif /* MM_SMS_MBIM_H */ ModemManager-1.23.4-dev/src/mm-sms-part-3gpp.c000066400000000000000000001231631456466623000207120ustar00rootroot00000000000000/* -*- 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) 2011 - 2012 Red Hat, Inc. * Copyright (C) 2012 Google, Inc. */ #include #include #include #include #define _LIBMM_INSIDE_MM #include #include "mm-common-helpers.h" #include "mm-helper-enums-types.h" #include "mm-sms-part-3gpp.h" #include "mm-charsets.h" #include "mm-log.h" #define PDU_SIZE 200 #define SMS_TP_MTI_MASK 0x03 #define SMS_TP_MTI_SMS_DELIVER 0x00 #define SMS_TP_MTI_SMS_SUBMIT 0x01 #define SMS_TP_MTI_SMS_STATUS_REPORT 0x02 #define SMS_NUMBER_TYPE_MASK 0x70 #define SMS_NUMBER_TYPE_UNKNOWN 0x00 #define SMS_NUMBER_TYPE_INTL 0x10 #define SMS_NUMBER_TYPE_ALPHA 0x50 #define SMS_NUMBER_PLAN_MASK 0x0f #define SMS_NUMBER_PLAN_TELEPHONE 0x01 #define SMS_TP_MMS 0x04 #define SMS_TP_SRI 0x20 #define SMS_TP_UDHI 0x40 #define SMS_TP_RP 0x80 #define SMS_DCS_CODING_MASK 0xec #define SMS_DCS_CODING_DEFAULT 0x00 #define SMS_DCS_CODING_8BIT 0x04 #define SMS_DCS_CODING_UCS2 0x08 #define SMS_DCS_CLASS_VALID 0x10 #define SMS_DCS_CLASS_MASK 0x03 #define SMS_TIMESTAMP_LEN 7 #define SMS_MIN_PDU_LEN (7 + SMS_TIMESTAMP_LEN) static char sms_bcd_chars[] = "0123456789*#abc\0\0"; static void sms_semi_octets_to_bcd_string (char *dest, const guint8 *octets, int num_octets) { int i; for (i = 0 ; i < num_octets; i++) { *dest++ = sms_bcd_chars[octets[i] & 0xf]; *dest++ = sms_bcd_chars[(octets[i] >> 4) & 0xf]; } *dest++ = '\0'; } static gboolean char_to_bcd (char in, guint8 *out) { guint32 z; if (isdigit (in)) { *out = in - 0x30; return TRUE; } for (z = 10; z < 16; z++) { if (in == sms_bcd_chars[z]) { *out = z; return TRUE; } } return FALSE; } static gsize sms_string_to_bcd_semi_octets (guint8 *buf, gsize buflen, const char *string) { guint i; guint8 bcd; gsize addrlen, slen; addrlen = slen = strlen (string); if (addrlen % 2) addrlen++; g_return_val_if_fail (buflen >= addrlen, 0); for (i = 0; i < addrlen; i += 2) { if (!char_to_bcd (string[i], &bcd)) return 0; buf[i / 2] = bcd & 0xF; if (i >= slen - 1) { /* PDU address gets padded with 0xF if string is odd length */ bcd = 0xF; } else if (!char_to_bcd (string[i + 1], &bcd)) return 0; buf[i / 2] |= bcd << 4; } return addrlen / 2; } /* len is in semi-octets */ static gchar * sms_decode_address (const guint8 *address, gint len_digits, GError **error) { guint8 addrtype, addrplan; gchar *utf8; addrtype = address[0] & SMS_NUMBER_TYPE_MASK; addrplan = address[0] & SMS_NUMBER_PLAN_MASK; address++; if (addrtype == SMS_NUMBER_TYPE_ALPHA) { g_autoptr(GByteArray) unpacked_array = NULL; guint8 *unpacked = NULL; guint32 unpacked_len; unpacked = mm_charset_gsm_unpack (address, (len_digits * 4) / 7, 0, &unpacked_len); unpacked_array = g_byte_array_new_take (unpacked, unpacked_len); utf8 = mm_modem_charset_bytearray_to_utf8 (unpacked_array, MM_MODEM_CHARSET_GSM, FALSE, error); } else if (addrtype == SMS_NUMBER_TYPE_INTL && addrplan == SMS_NUMBER_PLAN_TELEPHONE) { /* International telphone number, format as "+1234567890" */ utf8 = g_malloc (len_digits + 3); /* '+' + digits + possible trailing 0xf + NUL */ utf8[0] = '+'; sms_semi_octets_to_bcd_string (utf8 + 1, address, (len_digits + 1) / 2); } else { /* * All non-alphanumeric types and plans are just digits, but * don't apply any special formatting if we don't know the * format. */ utf8 = g_malloc (len_digits + 2); /* digits + possible trailing 0xf + NUL */ sms_semi_octets_to_bcd_string (utf8, address, (len_digits + 1) / 2); } return utf8; } static gchar * sms_decode_timestamp (const guint8 *timestamp, GError **error) { /* ISO8601 format: YYYY-MM-DDTHH:MM:SS+HHMM */ guint year, month, day, hour, minute, second; gint quarters, offset_minutes; year = 2000 + ((timestamp[0] & 0xf) * 10) + ((timestamp[0] >> 4) & 0xf); month = ((timestamp[1] & 0xf) * 10) + ((timestamp[1] >> 4) & 0xf); day = ((timestamp[2] & 0xf) * 10) + ((timestamp[2] >> 4) & 0xf); hour = ((timestamp[3] & 0xf) * 10) + ((timestamp[3] >> 4) & 0xf); minute = ((timestamp[4] & 0xf) * 10) + ((timestamp[4] >> 4) & 0xf); second = ((timestamp[5] & 0xf) * 10) + ((timestamp[5] >> 4) & 0xf); quarters = ((timestamp[6] & 0x7) * 10) + ((timestamp[6] >> 4) & 0xf); offset_minutes = quarters * 15; if (timestamp[6] & 0x08) offset_minutes = -1 * offset_minutes; return mm_new_iso8601_time (year, month, day, hour, minute, second, TRUE, offset_minutes, error); } static MMSmsEncoding sms_encoding_type (int dcs) { MMSmsEncoding scheme = MM_SMS_ENCODING_UNKNOWN; switch ((dcs >> 4) & 0xf) { /* General data coding group */ case 0: case 1: case 2: case 3: switch (dcs & 0x0c) { case 0x08: scheme = MM_SMS_ENCODING_UCS2; break; case 0x00: /* reserved - spec says to treat it as default alphabet */ /* Fall through */ case 0x0c: scheme = MM_SMS_ENCODING_GSM7; break; case 0x04: scheme = MM_SMS_ENCODING_8BIT; break; default: g_assert_not_reached (); } break; /* Message waiting group (default alphabet) */ case 0xc: case 0xd: scheme = MM_SMS_ENCODING_GSM7; break; /* Message waiting group (UCS2 alphabet) */ case 0xe: scheme = MM_SMS_ENCODING_UCS2; break; /* Data coding/message class group */ case 0xf: switch (dcs & 0x04) { case 0x00: scheme = MM_SMS_ENCODING_GSM7; break; case 0x04: scheme = MM_SMS_ENCODING_8BIT; break; default: g_assert_not_reached (); } break; /* Reserved coding group values - spec says to treat it as default alphabet */ default: scheme = MM_SMS_ENCODING_GSM7; break; } return scheme; } static gchar * sms_decode_text (const guint8 *text, int len, MMSmsEncoding encoding, int bit_offset, gpointer log_object, GError **error) { if (!text || len == 0) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Skipping SMS text: SMS text has no elements to decode"); return NULL; } if (encoding == MM_SMS_ENCODING_GSM7) { g_autoptr(GByteArray) unpacked_array = NULL; guint8 *unpacked = NULL; guint32 unpacked_len; gchar *utf8; unpacked = mm_charset_gsm_unpack ((const guint8 *) text, len, bit_offset, &unpacked_len); unpacked_array = g_byte_array_new_take (unpacked, unpacked_len); utf8 = mm_modem_charset_bytearray_to_utf8 (unpacked_array, MM_MODEM_CHARSET_GSM, FALSE, error); if (utf8) mm_obj_dbg (log_object, "converted SMS part text from GSM-7 to UTF-8: %s", utf8); return utf8; } /* Always assume UTF-16 instead of UCS-2! */ if (encoding == MM_SMS_ENCODING_UCS2) { g_autoptr(GByteArray) bytearray = NULL; gchar *utf8; bytearray = g_byte_array_append (g_byte_array_sized_new (len), (const guint8 *)text, len); utf8 = mm_modem_charset_bytearray_to_utf8 (bytearray, MM_MODEM_CHARSET_UTF16, FALSE, error); if (utf8) mm_obj_dbg (log_object, "converted SMS part text from UTF-16BE to UTF-8: %s", utf8); return utf8; } g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't convert SMS part contents from %s to UTF-8", mm_sms_encoding_get_string (encoding)); return NULL; } static guint relative_to_validity (guint8 relative) { if (relative <= 143) return (relative + 1) * 5; if (relative <= 167) return 720 + (relative - 143) * 30; return (relative - 166) * 1440; } static guint8 validity_to_relative (guint validity) { if (validity == 0) return 167; /* 24 hours */ if (validity <= 720) { /* 5 minute units up to 12 hours */ if (validity % 5) validity += 5; return (validity / 5) - 1; } if (validity > 720 && validity <= 1440) { /* 12 hours + 30 minute units up to 1 day */ if (validity % 30) validity += 30; /* round up to next 30 minutes */ validity = MIN (validity, 1440); return 143 + ((validity - 720) / 30); } if (validity > 1440 && validity <= 43200) { /* 2 days up to 1 month */ if (validity % 1440) validity += 1440; /* round up to next day */ validity = MIN (validity, 43200); return 167 + ((validity - 1440) / 1440); } /* 43200 = 30 days in minutes * 10080 = 7 days in minutes * 635040 = 63 weeks in minutes * 40320 = 4 weeks in minutes */ if (validity > 43200 && validity <= 635040) { /* 5 weeks up to 63 weeks */ if (validity % 10080) validity += 10080; /* round up to next week */ validity = MIN (validity, 635040); return 196 + ((validity - 40320) / 10080); } return 255; /* 63 weeks */ } MMSmsPart * mm_sms_part_3gpp_new_from_pdu (guint index, const gchar *hexpdu, gpointer log_object, GError **error) { g_autofree guint8 *pdu = NULL; gsize pdu_len; /* Convert PDU from hex to binary */ pdu = mm_utils_hexstr2bin (hexpdu, -1, &pdu_len, error); if (!pdu) { g_prefix_error (error, "Couldn't convert 3GPP PDU from hex to binary: "); return NULL; } return mm_sms_part_3gpp_new_from_binary_pdu (index, pdu, pdu_len, log_object, FALSE, error); } MMSmsPart * mm_sms_part_3gpp_new_from_binary_pdu (guint index, const guint8 *pdu, gsize pdu_len, gpointer log_object, gboolean transfer_route, GError **error) { MMSmsPart *sms_part; guint8 pdu_type; guint offset; guint smsc_addr_size_bytes; guint tp_addr_size_digits; guint tp_addr_size_bytes; guint8 validity_format = 0; gboolean has_udh = FALSE; /* The following offsets are OPTIONAL, as STATUS REPORTs may not have * them; we use '0' to indicate their absence */ guint tp_pid_offset = 0; guint tp_dcs_offset = 0; guint tp_user_data_len_offset = 0; MMSmsEncoding user_data_encoding = MM_SMS_ENCODING_UNKNOWN; gchar *address; /* Create the new MMSmsPart */ sms_part = mm_sms_part_new (index, MM_SMS_PDU_TYPE_UNKNOWN); if (index != SMS_PART_INVALID_INDEX) mm_obj_dbg (log_object, "parsing PDU (%u)...", index); else mm_obj_dbg (log_object, "parsing PDU..."); #define PDU_SIZE_CHECK(required_size, check_descr_str) \ if (pdu_len < required_size) { \ g_set_error (error, \ MM_CORE_ERROR, \ MM_CORE_ERROR_FAILED, \ "PDU too short, %s: %" G_GSIZE_FORMAT " < %u", \ check_descr_str, \ pdu_len, \ required_size); \ mm_sms_part_free (sms_part); \ return NULL; \ } offset = 0; if (!transfer_route) { /* ---------------------------------------------------------------------- */ /* SMSC, in address format, precedes the TPDU * First byte represents the number of BYTES for the address value */ PDU_SIZE_CHECK (1, "cannot read SMSC address length"); smsc_addr_size_bytes = pdu[offset++]; if (smsc_addr_size_bytes > 0) { PDU_SIZE_CHECK (offset + smsc_addr_size_bytes, "cannot read SMSC address"); /* SMSC may not be given in DELIVER PDUs */ address = sms_decode_address (&pdu[1], 2 * (smsc_addr_size_bytes - 1), error); if (!address) { g_prefix_error (error, "Couldn't read SMSC address: "); mm_sms_part_free (sms_part); return NULL; } mm_sms_part_take_smsc (sms_part, g_steal_pointer (&address)); mm_obj_dbg (log_object, " SMSC address parsed: '%s'", mm_sms_part_get_smsc (sms_part)); offset += smsc_addr_size_bytes; } else mm_obj_dbg (log_object, " no SMSC address given"); } else mm_obj_dbg (log_object, " This is a transfer-route message"); /* ---------------------------------------------------------------------- */ /* TP-MTI (1 byte) */ PDU_SIZE_CHECK (offset + 1, "cannot read TP-MTI"); pdu_type = (pdu[offset] & SMS_TP_MTI_MASK); switch (pdu_type) { case SMS_TP_MTI_SMS_DELIVER: mm_obj_dbg (log_object, " deliver type PDU detected"); mm_sms_part_set_pdu_type (sms_part, MM_SMS_PDU_TYPE_DELIVER); break; case SMS_TP_MTI_SMS_SUBMIT: mm_obj_dbg (log_object, " submit type PDU detected"); mm_sms_part_set_pdu_type (sms_part, MM_SMS_PDU_TYPE_SUBMIT); break; case SMS_TP_MTI_SMS_STATUS_REPORT: mm_obj_dbg (log_object, " status report type PDU detected"); mm_sms_part_set_pdu_type (sms_part, MM_SMS_PDU_TYPE_STATUS_REPORT); break; default: mm_sms_part_free (sms_part); g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Unhandled message type: 0x%02x", pdu_type); return NULL; } /* Delivery report was requested? */ if (pdu[offset] & 0x20) mm_sms_part_set_delivery_report_request (sms_part, TRUE); /* PDU with validity? (only in SUBMIT PDUs) */ if (pdu_type == SMS_TP_MTI_SMS_SUBMIT) validity_format = pdu[offset] & 0x18; /* PDU with user data header? */ if (pdu[offset] & 0x40) has_udh = TRUE; offset++; /* ---------------------------------------------------------------------- */ /* TP-MR (1 byte, in STATUS_REPORT and SUBMIT PDUs */ if (pdu_type == SMS_TP_MTI_SMS_STATUS_REPORT || pdu_type == SMS_TP_MTI_SMS_SUBMIT) { PDU_SIZE_CHECK (offset + 1, "cannot read message reference"); mm_obj_dbg (log_object, " message reference: %u", (guint)pdu[offset]); mm_sms_part_set_message_reference (sms_part, pdu[offset]); offset++; } /* ---------------------------------------------------------------------- */ /* TP-DA or TP-OA or TP-RA * First byte represents the number of DIGITS in the number. * Round the sender address length up to an even number of * semi-octets, and thus an integral number of octets. */ PDU_SIZE_CHECK (offset + 1, "cannot read number of digits in number"); tp_addr_size_digits = pdu[offset++]; tp_addr_size_bytes = (tp_addr_size_digits + 1) >> 1; mm_obj_dbg (log_object, " address size: %u digits (%u bytes)", tp_addr_size_digits, tp_addr_size_bytes); if (tp_addr_size_bytes == 0) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't read address: field missing"); mm_sms_part_free (sms_part); return NULL; } /* +1 due to the Type of Address byte */ PDU_SIZE_CHECK (offset + 1 + tp_addr_size_bytes, "cannot read number"); address = sms_decode_address (&pdu[offset], tp_addr_size_digits, error); if (!address) { g_prefix_error (error, "Couldn't read address: "); mm_sms_part_free (sms_part); return NULL; } mm_sms_part_take_number (sms_part, g_steal_pointer (&address)); mm_obj_dbg (log_object, " number parsed: %s", mm_sms_part_get_number (sms_part)); offset += (1 + tp_addr_size_bytes); /* ---------------------------------------------------------------------- */ /* Get timestamps and indexes for TP-PID, TP-DCS and TP-UDL/TP-UD */ if (pdu_type == SMS_TP_MTI_SMS_DELIVER) { gchar *str = NULL; PDU_SIZE_CHECK (offset + 9, "cannot read PID/DCS/Timestamp"); /* 1+1+7=9 */ /* ------ TP-PID (1 byte) ------ */ tp_pid_offset = offset++; /* ------ TP-DCS (1 byte) ------ */ tp_dcs_offset = offset++; /* ------ Timestamp (7 bytes) ------ */ str = sms_decode_timestamp (&pdu[offset], error); if (!str) { mm_sms_part_free (sms_part); return NULL; } mm_sms_part_take_timestamp (sms_part, str); offset += 7; tp_user_data_len_offset = offset; } else if (pdu_type == SMS_TP_MTI_SMS_SUBMIT) { PDU_SIZE_CHECK (offset + 2 + !!validity_format, "cannot read PID/DCS/Validity"); /* 1+1=2 */ /* ------ TP-PID (1 byte) ------ */ tp_pid_offset = offset++; /* ------ TP-DCS (1 byte) ------ */ tp_dcs_offset = offset++; /* ----------- TP-Validity-Period (1 byte) ----------- */ if (validity_format) { switch (validity_format) { case 0x10: mm_obj_dbg (log_object, " validity available, format relative"); mm_sms_part_set_validity_relative (sms_part, relative_to_validity (pdu[offset])); offset++; break; case 0x08: /* TODO: support enhanced format; GSM 03.40 */ mm_obj_dbg (log_object, " validity available, format enhanced (not implemented)"); /* 7 bytes for enhanced validity */ offset += 7; break; case 0x18: /* TODO: support absolute format; GSM 03.40 */ mm_obj_dbg (log_object, " validity available, format absolute (not implemented)"); /* 7 bytes for absolute validity */ offset += 7; break; default: /* Cannot happen as we AND with the 0x18 mask */ g_assert_not_reached (); } } tp_user_data_len_offset = offset; } else if (pdu_type == SMS_TP_MTI_SMS_STATUS_REPORT) { gchar *str = NULL; /* We have 2 timestamps in status report PDUs: * first, the timestamp for when the PDU was received in the SMSC * second, the timestamp for when the PDU was forwarded by the SMSC */ PDU_SIZE_CHECK (offset + 15, "cannot read Timestamps/TP-STATUS"); /* 7+7+1=15 */ /* ------ Timestamp (7 bytes) ------ */ str = sms_decode_timestamp (&pdu[offset], error); if (!str) { mm_sms_part_free (sms_part); return NULL; } mm_sms_part_take_timestamp (sms_part, str); offset += 7; /* ------ Discharge Timestamp (7 bytes) ------ */ str = sms_decode_timestamp (&pdu[offset], error); if (!str) { mm_sms_part_free (sms_part); return NULL; } mm_sms_part_take_discharge_timestamp (sms_part, str); offset += 7; /* ----- TP-STATUS (1 byte) ------ */ mm_obj_dbg (log_object, " delivery state: %u", (guint)pdu[offset]); mm_sms_part_set_delivery_state (sms_part, pdu[offset]); offset++; /* ------ TP-PI (1 byte) OPTIONAL ------ */ if (offset < pdu_len) { guint next_optional_field_offset = offset + 1; /* TP-PID? */ if (pdu[offset] & 0x01) tp_pid_offset = next_optional_field_offset++; /* TP-DCS? */ if (pdu[offset] & 0x02) tp_dcs_offset = next_optional_field_offset++; /* TP-UserData? */ if (pdu[offset] & 0x04) tp_user_data_len_offset = next_optional_field_offset; } } else g_assert_not_reached (); if (tp_pid_offset > 0) { PDU_SIZE_CHECK (tp_pid_offset + 1, "cannot read TP-PID"); mm_obj_dbg (log_object, " PID: %u", (guint)pdu[tp_pid_offset]); } /* Grab user data encoding and message class */ if (tp_dcs_offset > 0) { PDU_SIZE_CHECK (tp_dcs_offset + 1, "cannot read TP-DCS"); /* Encoding given in the 'alphabet' bits */ user_data_encoding = sms_encoding_type (pdu[tp_dcs_offset]); switch (user_data_encoding) { case MM_SMS_ENCODING_GSM7: mm_obj_dbg (log_object, " user data encoding is GSM7"); break; case MM_SMS_ENCODING_UCS2: mm_obj_dbg (log_object, " user data encoding is UCS2"); break; case MM_SMS_ENCODING_8BIT: mm_obj_dbg (log_object, " user data encoding is 8bit"); break; case MM_SMS_ENCODING_UNKNOWN: mm_obj_dbg (log_object, " user data encoding is unknown"); break; default: g_assert_not_reached (); } mm_sms_part_set_encoding (sms_part, user_data_encoding); /* Class */ if (pdu[tp_dcs_offset] & SMS_DCS_CLASS_VALID) mm_sms_part_set_class (sms_part, pdu[tp_dcs_offset] & SMS_DCS_CLASS_MASK); } if (tp_user_data_len_offset > 0) { guint tp_user_data_size_elements; guint tp_user_data_size_bytes; guint tp_user_data_offset; guint bit_offset; PDU_SIZE_CHECK (tp_user_data_len_offset + 1, "cannot read TP-UDL"); tp_user_data_size_elements = pdu[tp_user_data_len_offset]; mm_obj_dbg (log_object, " user data length: %u elements", tp_user_data_size_elements); if (user_data_encoding == MM_SMS_ENCODING_GSM7) tp_user_data_size_bytes = (7 * (tp_user_data_size_elements + 1 )) / 8; else tp_user_data_size_bytes = tp_user_data_size_elements; mm_obj_dbg (log_object, " user data length: %u bytes", tp_user_data_size_bytes); tp_user_data_offset = tp_user_data_len_offset + 1; PDU_SIZE_CHECK (tp_user_data_offset + tp_user_data_size_bytes, "cannot read TP-UD"); bit_offset = 0; if (has_udh) { guint udhl_elements; guint udhl, end; PDU_SIZE_CHECK (tp_user_data_offset + 1, "cannot read UDH length"); udhl = pdu[tp_user_data_offset] + 1; end = tp_user_data_offset + udhl; PDU_SIZE_CHECK (tp_user_data_offset + udhl, "cannot read UDH"); for (offset = tp_user_data_offset + 1; (offset + 1) < end;) { guint8 ie_id, ie_len; ie_id = pdu[offset++]; ie_len = pdu[offset++]; switch (ie_id) { case 0x00: if (offset + 2 >= end) break; /* * Ignore the IE if one of the following is true: * - it claims to be part 0 of M * - it claims to be part N of M, N > M */ if (pdu[offset + 2] == 0 || pdu[offset + 2] > pdu[offset + 1]) break; mm_sms_part_set_concat_reference (sms_part, pdu[offset]); mm_sms_part_set_concat_max (sms_part, pdu[offset + 1]); mm_sms_part_set_concat_sequence (sms_part, pdu[offset + 2]); break; case 0x08: if (offset + 3 >= end) break; /* Concatenated short message, 16-bit reference */ if (pdu[offset + 3] == 0 || pdu[offset + 3] > pdu[offset + 2]) break; mm_sms_part_set_concat_reference (sms_part, (pdu[offset] << 8) | pdu[offset + 1]); mm_sms_part_set_concat_max (sms_part,pdu[offset + 2]); mm_sms_part_set_concat_sequence (sms_part, pdu[offset + 3]); break; default: break; } offset += ie_len; } /* * Move past the user data headers to prevent it from being * decoded into garbage text. */ tp_user_data_offset += udhl; tp_user_data_size_bytes -= udhl; if (user_data_encoding == MM_SMS_ENCODING_GSM7) { /* * Find the number of bits we need to add to the length of the * user data to get a multiple of 7 (the padding). */ bit_offset = (7 - udhl % 7) % 7; udhl_elements = (udhl * 8 + bit_offset) / 7; } else udhl_elements = udhl; if (udhl_elements >= tp_user_data_size_elements) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "udhl length (%u) is greater than data size (%u)", udhl_elements, tp_user_data_size_elements); mm_sms_part_free (sms_part); return NULL; } tp_user_data_size_elements -= udhl_elements; } switch (user_data_encoding) { case MM_SMS_ENCODING_GSM7: case MM_SMS_ENCODING_UCS2: { gchar *text; /* Otherwise if it's 7-bit or UCS2 we can decode it */ mm_obj_dbg (log_object, "decoding SMS text with %u elements", tp_user_data_size_elements); text = sms_decode_text (&pdu[tp_user_data_offset], tp_user_data_size_elements, user_data_encoding, bit_offset, log_object, error); if (!text) { mm_sms_part_free (sms_part); return NULL; } mm_sms_part_take_text (sms_part, text); break; } case MM_SMS_ENCODING_8BIT: case MM_SMS_ENCODING_UNKNOWN: default: { GByteArray *raw; mm_obj_dbg (log_object, "skipping SMS text: unknown encoding (0x%02X)", user_data_encoding); PDU_SIZE_CHECK (tp_user_data_offset + tp_user_data_size_bytes, "cannot read user data"); /* 8-bit encoding is usually binary data, and we have no idea what * actual encoding the data is in so we can't convert it. */ raw = g_byte_array_sized_new (tp_user_data_size_bytes); g_byte_array_append (raw, &pdu[tp_user_data_offset], tp_user_data_size_bytes); mm_sms_part_take_data (sms_part, raw); break; } } } return sms_part; } /** * mm_sms_part_3gpp_encode_address: * * @address: the phone number to encode * @buf: the buffer to encode @address in * @buflen: the size of @buf * @is_smsc: if %TRUE encode size as number of octets of address information, * otherwise if %FALSE encode size as number of digits of @address * * Returns: the size in bytes of the data added to @buf **/ guint mm_sms_part_3gpp_encode_address (const gchar *address, guint8 *buf, gsize buflen, gboolean is_smsc) { gsize len; g_return_val_if_fail (address != NULL, 0); g_return_val_if_fail (buf != NULL, 0); g_return_val_if_fail (buflen >= 2, 0); /* Handle number type & plan */ buf[1] = 0x80; /* Bit 7 always 1 */ if (address[0] == '+') { buf[1] |= SMS_NUMBER_TYPE_INTL; address++; } buf[1] |= SMS_NUMBER_PLAN_TELEPHONE; len = sms_string_to_bcd_semi_octets (&buf[2], buflen, address); if (is_smsc) buf[0] = len + 1; /* addr length + size byte */ else buf[0] = strlen (address); /* number of digits in address */ return len ? len + 2 : 0; /* addr length + size byte + number type/plan */ } /** * mm_sms_part_3gpp_get_submit_pdu: * * @part: the SMS message part * @out_pdulen: on success, the size of the returned PDU in bytes * @out_msgstart: on success, the byte index in the returned PDU where the * message starts (ie, skipping the SMSC length byte and address, if present) * @error: on error, filled with the error that occurred * * Constructs a single-part SMS message with the given details, preferring to * use the UCS2 character set when the message will fit, otherwise falling back * to the GSM character set. * * Returns: the constructed PDU data on success, or %NULL on error **/ guint8 * mm_sms_part_3gpp_get_submit_pdu (MMSmsPart *part, guint *out_pdulen, guint *out_msgstart, gpointer log_object, GError **error) { guint8 *pdu; guint len, offset = 0; guint shift = 0; guint8 *udl_ptr; MMSmsEncoding encoding; g_return_val_if_fail (mm_sms_part_get_number (part) != NULL, NULL); g_return_val_if_fail (mm_sms_part_get_text (part) != NULL || mm_sms_part_get_data (part) != NULL, NULL); if (mm_sms_part_get_pdu_type (part) != MM_SMS_PDU_TYPE_SUBMIT) { g_set_error (error, MM_MESSAGE_ERROR, MM_MESSAGE_ERROR_INVALID_PDU_PARAMETER, "Invalid PDU type to generate a 'submit' PDU: '%s'", mm_sms_pdu_type_get_string (mm_sms_part_get_pdu_type (part))); return NULL; } mm_obj_dbg (log_object, "creating PDU for part..."); /* Build up the PDU */ pdu = g_malloc0 (PDU_SIZE); if (mm_sms_part_get_smsc (part)) { mm_obj_dbg (log_object, " adding SMSC to PDU..."); len = mm_sms_part_3gpp_encode_address (mm_sms_part_get_smsc (part), pdu, PDU_SIZE, TRUE); if (len == 0) { g_set_error (error, MM_MESSAGE_ERROR, MM_MESSAGE_ERROR_INVALID_PDU_PARAMETER, "Invalid SMSC address '%s'", mm_sms_part_get_smsc (part)); goto error; } offset += len; } else { /* No SMSC, use default */ pdu[offset++] = 0x00; } if (out_msgstart) *out_msgstart = offset; /* ----------- First BYTE ----------- */ pdu[offset] = 0; /* TP-VP present; format RELATIVE */ if (mm_sms_part_get_validity_relative (part) > 0) { mm_obj_dbg (log_object, " adding validity to PDU..."); pdu[offset] |= 0x10; } /* Concatenation sequence only found in multipart SMS */ if (mm_sms_part_get_concat_sequence (part)) { mm_obj_dbg (log_object, " adding UDHI to PDU..."); pdu[offset] |= 0x40; /* UDHI */ } /* Delivery report requested in singlepart messages or in the last PDU of * multipart messages */ if (mm_sms_part_get_delivery_report_request (part) && (!mm_sms_part_get_concat_sequence (part) || mm_sms_part_get_concat_max (part) == mm_sms_part_get_concat_sequence (part))) { mm_obj_dbg (log_object, " requesting delivery report..."); pdu[offset] |= 0x20; } /* TP-MTI = SMS-SUBMIT */ pdu[offset++] |= 0x01; /* ----------- TP-MR (1 byte) ----------- */ pdu[offset++] = 0x00; /* TP-Message-Reference: filled by device */ /* ----------- Destination address ----------- */ len = mm_sms_part_3gpp_encode_address (mm_sms_part_get_number (part), &pdu[offset], PDU_SIZE - offset, FALSE); if (len == 0) { g_set_error (error, MM_MESSAGE_ERROR, MM_MESSAGE_ERROR_INVALID_PDU_PARAMETER, "Invalid number '%s'", mm_sms_part_get_number (part)); goto error; } offset += len; /* ----------- TP-PID (1 byte) ----------- */ pdu[offset++] = 0x00; /* ----------- TP-DCS (1 byte) ----------- */ pdu[offset] = 0x00; if (mm_sms_part_get_class (part) >= 0 && mm_sms_part_get_class (part) <= 3) { mm_obj_dbg (log_object, " using class %d...", mm_sms_part_get_class (part)); pdu[offset] |= SMS_DCS_CLASS_VALID; pdu[offset] |= mm_sms_part_get_class (part); } encoding = mm_sms_part_get_encoding (part); switch (encoding) { case MM_SMS_ENCODING_UCS2: mm_obj_dbg (log_object, " using UCS2 encoding..."); pdu[offset] |= SMS_DCS_CODING_UCS2; break; case MM_SMS_ENCODING_GSM7: mm_obj_dbg (log_object, " using GSM7 encoding..."); pdu[offset] |= SMS_DCS_CODING_DEFAULT; /* GSM */ break; case MM_SMS_ENCODING_8BIT: case MM_SMS_ENCODING_UNKNOWN: default: mm_obj_dbg (log_object, " using 8bit encoding..."); pdu[offset] |= SMS_DCS_CODING_8BIT; break; } offset++; /* ----------- TP-Validity-Period (1 byte): 4 days ----------- */ /* Only if TP-VPF was set in first byte */ if (mm_sms_part_get_validity_relative (part) > 0) pdu[offset++] = validity_to_relative (mm_sms_part_get_validity_relative (part)); /* ----------- TP-User-Data-Length ----------- */ /* Set to zero initially, and keep a ptr for easy access later */ udl_ptr = &pdu[offset]; pdu[offset++] = 0; /* Build UDH */ if (mm_sms_part_get_concat_sequence (part)) { mm_obj_dbg (log_object, " adding UDH header in PDU... (reference: %u, max: %u, sequence: %u)", mm_sms_part_get_concat_reference (part), mm_sms_part_get_concat_max (part), mm_sms_part_get_concat_sequence (part)); pdu[offset++] = 0x05; /* udh len */ pdu[offset++] = 0x00; /* mid */ pdu[offset++] = 0x03; /* data len */ pdu[offset++] = (guint8)mm_sms_part_get_concat_reference (part); pdu[offset++] = (guint8)mm_sms_part_get_concat_max (part); pdu[offset++] = (guint8)mm_sms_part_get_concat_sequence (part); /* if a UDH is present and the data encoding is the default 7-bit * alphabet, the user data must be 7-bit word aligned after the * UDH. This means up to 6 bits of zeros need to be inserted at the * start of the message. * * In our case the UDH is 6 bytes long, 48bits. The next multiple of * 7 is therefore 49, so we only need to include one bit of padding. */ shift = 1; } if (encoding == MM_SMS_ENCODING_GSM7) { g_autoptr(GByteArray) unpacked = NULL; g_autofree guint8 *packed = NULL; guint32 packlen = 0; unpacked = mm_modem_charset_bytearray_from_utf8 (mm_sms_part_get_text (part), MM_MODEM_CHARSET_GSM, FALSE, error); if (!unpacked) goto error; if (unpacked->len == 0) { g_set_error_literal (error, MM_MESSAGE_ERROR, MM_MESSAGE_ERROR_INVALID_PDU_PARAMETER, "Failed to convert message text to GSM"); goto error; } /* Set real data length, in septets * If we had UDH, add 7 septets */ *udl_ptr = mm_sms_part_get_concat_sequence (part) ? (7 + unpacked->len) : unpacked->len; mm_obj_dbg (log_object, " user data length is %u septets (%s UDH)", *udl_ptr, mm_sms_part_get_concat_sequence (part) ? "with" : "without"); packed = mm_charset_gsm_pack (unpacked->data, unpacked->len, shift, &packlen); if (!packed || packlen == 0) { g_set_error_literal (error, MM_MESSAGE_ERROR, MM_MESSAGE_ERROR_INVALID_PDU_PARAMETER, "Failed to pack message text to GSM"); goto error; } if (offset + packlen > PDU_SIZE) { g_set_error (error, MM_MESSAGE_ERROR, MM_MESSAGE_ERROR_INVALID_PDU_PARAMETER, "Packed user data is too large for PDU (want %d bytes total, have %d)", offset + packlen, PDU_SIZE); goto error; } memcpy (&pdu[offset], packed, packlen); offset += packlen; } else if (encoding == MM_SMS_ENCODING_UCS2) { g_autoptr(GByteArray) array = NULL; g_autoptr(GError) inner_error = NULL; /* Always assume UTF-16 instead of UCS-2! */ array = mm_modem_charset_bytearray_from_utf8 (mm_sms_part_get_text (part), MM_MODEM_CHARSET_UTF16, FALSE, &inner_error); if (!array) { g_set_error (error, MM_MESSAGE_ERROR, MM_MESSAGE_ERROR_INVALID_PDU_PARAMETER, "Failed to convert message text to UTF-16: %s", inner_error->message); goto error; } /* Set real data length, in octets * If we had UDH, add 6 octets */ *udl_ptr = mm_sms_part_get_concat_sequence (part) ? (6 + array->len) : array->len; mm_obj_dbg (log_object, " user data length is %u octets (%s UDH)", *udl_ptr, mm_sms_part_get_concat_sequence (part) ? "with" : "without"); if (offset + array->len > PDU_SIZE) { g_set_error (error, MM_MESSAGE_ERROR, MM_MESSAGE_ERROR_INVALID_PDU_PARAMETER, "User data is too large for PDU (want %d bytes total, have %d)", offset + array->len, PDU_SIZE); goto error; } memcpy (&pdu[offset], array->data, array->len); offset += array->len; } else if (mm_sms_part_get_encoding (part) == MM_SMS_ENCODING_8BIT) { const GByteArray *data; data = mm_sms_part_get_data (part); /* Set real data length, in octets * If we had UDH, add 6 octets */ *udl_ptr = mm_sms_part_get_concat_sequence (part) ? (6 + data->len) : data->len; mm_obj_dbg (log_object, " binary user data length is %u octets (%s UDH)", *udl_ptr, mm_sms_part_get_concat_sequence (part) ? "with" : "without"); if (offset + data->len > PDU_SIZE) { g_set_error (error, MM_MESSAGE_ERROR, MM_MESSAGE_ERROR_INVALID_PDU_PARAMETER, "User data is too large for PDU (want %d bytes total, have %d)", offset + data->len, PDU_SIZE); goto error; } memcpy (&pdu[offset], data->data, data->len); offset += data->len; } else g_assert_not_reached (); if (out_pdulen) *out_pdulen = offset; return pdu; error: g_free (pdu); return NULL; } GByteArray ** mm_sms_part_3gpp_util_split_data (const guint8 *data, gsize data_len) { GByteArray **out; /* Some info about the rules for splitting. * * The User Data can be up to 140 bytes in the SMS part: * 0) If we only need one chunk, it can be of up to 140 bytes. * If we need more than one chunk, these have to be of 140 - 6 = 134 * bytes each, as we need place for the UDH header. */ if (data_len <= 140) { out = g_new0 (GByteArray *, 2); out[0] = g_byte_array_append (g_byte_array_sized_new (data_len), data, data_len); } else { guint n_chunks; guint i; guint j; n_chunks = data_len / 134; if (data_len % 134 != 0) n_chunks ++; out = g_new0 (GByteArray *, n_chunks + 1); for (i = 0, j = 0; i < n_chunks; i++, j+= 134) { out[i] = g_byte_array_append (g_byte_array_sized_new (134), &data[j], MIN (data_len - j, 134)); } } return out; } ModemManager-1.23.4-dev/src/mm-sms-part-3gpp.h000066400000000000000000000045051456466623000207150ustar00rootroot00000000000000/* -*- 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) 2011 - 2012 Red Hat, Inc. * Copyright (C) 2013 Google, Inc. */ #ifndef MM_SMS_PART_3GPP_H #define MM_SMS_PART_3GPP_H #include #include #include "mm-sms-part.h" MMSmsPart *mm_sms_part_3gpp_new_from_pdu (guint index, const gchar *hexpdu, gpointer log_object, GError **error); MMSmsPart *mm_sms_part_3gpp_new_from_binary_pdu (guint index, const guint8 *pdu, gsize pdu_len, gpointer log_object, gboolean transfer_route, GError **error); guint8 *mm_sms_part_3gpp_get_submit_pdu (MMSmsPart *part, guint *out_pdulen, guint *out_msgstart, gpointer log_object, GError **error); /* For testcases only */ guint mm_sms_part_3gpp_encode_address (const gchar *address, guint8 *buf, gsize buflen, gboolean is_smsc); GByteArray **mm_sms_part_3gpp_util_split_data (const guint8 *data, gsize data_len); #endif /* MM_SMS_PART_3GPP_H */ ModemManager-1.23.4-dev/src/mm-sms-part-cdma.c000066400000000000000000001733611456466623000207520ustar00rootroot00000000000000/* -*- 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) 2013 Google, Inc. */ #include "mm-sms-part.h" #include #include #include #include #define _LIBMM_INSIDE_MM #include #include "mm-charsets.h" #include "mm-sms-part-cdma.h" #include "mm-log-object.h" /* * Documentation that you may want to have around: * * 3GPP2 C.S0015-B: Short Message Service (SMS) for Wideband Spread Spectrum * Systems. * * 3GPP2 C.R1001-G: Administration of Parameter Value Assignments for CDMA2000 * Spread Spectrum Standards. * * 3GPP2 X.S0004-550-E: Mobile Application Part (MAP). * * 3GPP2 C.S0005-E: Upper Layer (Layer 3) Signaling Standard for CDMA2000 * Spread Spectrum Systems. * * 3GPP2 N.S0005-O: Cellular Radiotelecommunications Intersystem Operations. */ /* 3GPP2 C.S0015-B, section 3.4, table 3.4-1 */ typedef enum { MESSAGE_TYPE_POINT_TO_POINT = 0, MESSAGE_TYPE_BROADCAST = 1, MESSAGE_TYPE_ACKNOWLEDGE = 2 } MessageType; /* 3GPP2 C.S0015-B, section 3.4.3, table 3.4.3-1 */ typedef enum { PARAMETER_ID_TELESERVICE_ID = 0, PARAMETER_ID_SERVICE_CATEGORY = 1, PARAMETER_ID_ORIGINATING_ADDRESS = 2, PARAMETER_ID_ORIGINATING_SUBADDRESS = 3, PARAMETER_ID_DESTINATION_ADDRESS = 4, PARAMETER_ID_DESTINATION_SUBADDRESS = 5, PARAMETER_ID_BEARER_REPLY_OPTION = 6, PARAMETER_ID_CAUSE_CODES = 7, PARAMETER_ID_BEARER_DATA = 8 } ParameterId; /* 3GPP2 C.S0015-B, section 3.4.3.3 */ typedef enum { DIGIT_MODE_DTMF = 0, DIGIT_MODE_ASCII = 1 } DigitMode; /* 3GPP2 C.S0015-B, section 3.4.3.3 */ typedef enum { NUMBER_MODE_DIGIT = 0, NUMBER_MODE_DATA_NETWORK_ADDRESS = 1 } NumberMode; /* 3GPP2 C.S0005-E, section 2.7.1.3.2.4, table 2.7.1.3.2.4-2 */ typedef enum { NUMBER_TYPE_UNKNOWN = 0, NUMBER_TYPE_INTERNATIONAL = 1, NUMBER_TYPE_NATIONAL = 2, NUMBER_TYPE_NETWORK_SPECIFIC = 3, NUMBER_TYPE_SUBSCRIBER = 4, /* 5 reserved */ NUMBER_TYPE_ABBREVIATED = 6, /* 7 reserved */ } NumberType; /* 3GPP2 C.S0015-B, section 3.4.3.3, table 3.4.3.3-1 */ typedef enum { DATA_NETWORK_ADDRESS_TYPE_UNKNOWN = 0, DATA_NETWORK_ADDRESS_TYPE_INTERNET_PROTOCOL = 1, DATA_NETWORK_ADDRESS_TYPE_INTERNET_EMAIL_ADDRESS = 2 } DataNetworkAddressType; /* 3GPP2 C.S0005-E, section 2.7.1.3.2.4, table 2.7.1.3.2.4-3 */ typedef enum { NUMBERING_PLAN_UNKNOWN = 0, NUMBERING_PLAN_ISDN = 1, NUMBERING_PLAN_DATA = 3, NUMBERING_PLAN_TELEX = 4, NUMBERING_PLAN_PRIVATE = 9, /* 15 reserved */ } NumberingPlan; /* 3GPP2 C.S0015-B, section 3.4.3.6 */ typedef enum { ERROR_CLASS_NO_ERROR = 0, /* 1 reserved */ ERROR_CLASS_TEMPORARY = 2, ERROR_CLASS_PERMANENT = 3 } ErrorClass; /* 3GPP2 N.S0005-O, section 6.5.2.125*/ typedef enum { CAUSE_CODE_NETWORK_PROBLEM_ADDRESS_VACANT = 0, CAUSE_CODE_NETWORK_PROBLEM_ADDRESS_TRANSLATION_FAILURE = 1, CAUSE_CODE_NETWORK_PROBLEM_NETWORK_RESOURCE_OUTAGE = 2, CAUSE_CODE_NETWORK_PROBLEM_NETWORK_FAILURE = 3, CAUSE_CODE_NETWORK_PROBLEM_INVALID_TELESERVICE_ID = 4, CAUSE_CODE_NETWORK_PROBLEM_OTHER = 5, /* 6 to 31 reserved, treat as CAUSE_CODE_NETWORK_PROBLEM_OTHER */ CAUSE_CODE_TERMINAL_PROBLEM_NO_PAGE_RESPONSE = 32, CAUSE_CODE_TERMINAL_PROBLEM_DESTINATION_BUSY = 33, CAUSE_CODE_TERMINAL_PROBLEM_NO_ACKNOWLEDGMENT = 34, CAUSE_CODE_TERMINAL_PROBLEM_DESTINATION_RESOURCE_SHORTAGE = 35, CAUSE_CODE_TERMINAL_PROBLEM_SMS_DELIVERY_POSTPONED = 36, CAUSE_CODE_TERMINAL_PROBLEM_DESTINATION_OUT_OF_SERVICE = 37, CAUSE_CODE_TERMINAL_PROBLEM_DESTINATION_NO_LONGER_AT_THIS_ADDRESS = 38, CAUSE_CODE_TERMINAL_PROBLEM_OTHER = 39, /* 40 to 47 reserved, treat as CAUSE_CODE_TERMINAL_PROBLEM_OTHER */ /* 48 to 63 reserved, treat as CAUSE_CODE_TERMINAL_PROBLEM_SMS_DELIVERY_POSTPONED */ CAUSE_CODE_RADIO_INTERFACE_PROBLEM_RESOURCE_SHORTAGE = 64, CAUSE_CODE_RADIO_INTERFACE_PROBLEM_INCOMPATIBILITY = 65, CAUSE_CODE_RADIO_INTERFACE_PROBLEM_OTHER = 66, /* 67 to 95 reserved, treat as CAUSE_CODE_RADIO_INTERFACE_PROBLEM_OTHER */ CAUSE_CODE_GENERAL_PROBLEM_ENCODING = 96, CAUSE_CODE_GENERAL_PROBLEM_SMS_ORIGINATION_DENIED = 97, CAUSE_CODE_GENERAL_PROBLEM_SMS_TERMINATION_DENIED = 98, CAUSE_CODE_GENERAL_PROBLEM_SUPPLEMENTARY_SERVICE_NOT_SUPPORTED = 99, CAUSE_CODE_GENERAL_PROBLEM_SMS_NOT_SUPPORTED = 100, /* 101 reserved */ CAUSE_CODE_GENERAL_PROBLEM_MISSING_EXPECTED_PARAMETER = 102, CAUSE_CODE_GENERAL_PROBLEM_MISSING_MANDATORY_PARAMETER = 103, CAUSE_CODE_GENERAL_PROBLEM_UNRECOGNIZED_PARAMETER_VALUE = 104, CAUSE_CODE_GENERAL_PROBLEM_UNEXPECTED_PARAMETER_VALUE = 105, CAUSE_CODE_GENERAL_PROBLEM_USER_DATA_SIZE_ERROR = 106, CAUSE_CODE_GENERAL_PROBLEM_OTHER = 107, /* 108 to 223 reserved, treat as CAUSE_CODE_GENERAL_PROBLEM_OTHER */ /* 224 to 255 reserved for TIA/EIA-41 extension, otherwise treat as CAUSE_CODE_GENERAL_PROBLEM_OTHER */ } CauseCode; /* 3GPP2 C.S0015-B, section 4.5, table 4.5-1 */ typedef enum { SUBPARAMETER_ID_MESSAGE_ID = 0, SUBPARAMETER_ID_USER_DATA = 1, SUBPARAMETER_ID_USER_RESPONSE_CODE = 2, SUBPARAMETER_ID_MESSAGE_CENTER_TIME_STAMP = 3, SUBPARAMETER_ID_VALIDITY_PERIOD_ABSOLUTE = 4, SUBPARAMETER_ID_VALIDITY_PERIOD_RELATIVE = 5, SUBPARAMETER_ID_DEFERRED_DELIVERY_TIME_ABSOLUTE = 6, SUBPARAMETER_ID_DEFERRED_DELIVERY_TIME_RELATIVE = 7, SUBPARAMETER_ID_PRIORITY_INDICATOR = 8, SUBPARAMETER_ID_PRIVACY_INDICATOR = 9, SUBPARAMETER_ID_REPLY_OPTION = 10, SUBPARAMETER_ID_NUMBER_OF_MESSAGES = 11, SUBPARAMETER_ID_ALERT_ON_MESSAGE_DELIVERY = 12, SUBPARAMETER_ID_LANGUAGE_INDICATOR = 13, SUBPARAMETER_ID_CALL_BACK_NUMBER = 14, SUBPARAMETER_ID_MESSAGE_DISPLAY_MODE = 15, SUBPARAMETER_ID_MULTIPLE_ENCODING_USER_DATA = 16, SUBPARAMETER_ID_MESSAGE_DEPOSIT_INDEX = 17, SUBPARAMETER_ID_SERVICE_CATEGORY_PROGRAM_DATA = 18, SUBPARAMETER_ID_SERVICE_CATEGORY_PROGRAM_RESULT = 19, SUBPARAMETER_ID_MESSAGE_STATUS = 20, SUBPARAMETER_ID_TP_FAILURE_CAUSE = 21, SUBPARAMETER_ID_ENHANCED_VMN = 22, SUBPARAMETER_ID_ENHANCED_VMN_ACK = 23, } SubparameterId; /* 3GPP2 C.S0015-B, section 4.5.1, table 4.5.1-1 */ typedef enum { TELESERVICE_MESSAGE_TYPE_UNKNOWN = 0, TELESERVICE_MESSAGE_TYPE_DELIVER = 1, TELESERVICE_MESSAGE_TYPE_SUBMIT = 2, TELESERVICE_MESSAGE_TYPE_CANCELLATION = 3, TELESERVICE_MESSAGE_TYPE_DELIVERY_ACKNOWLEDGEMENT = 4, TELESERVICE_MESSAGE_TYPE_USER_ACKNOWLEDGEMENT = 5, TELESERVICE_MESSAGE_TYPE_READ_ACKNOWLEDGEMENT = 6, } TeleserviceMessageType; /* C.R1001-G, section 9.1, table 9.1-1 */ typedef enum { ENCODING_OCTET = 0, ENCODING_EXTENDED_PROTOCOL_MESSAGE = 1, ENCODING_ASCII_7BIT = 2, ENCODING_IA5 = 3, ENCODING_UNICODE = 4, ENCODING_SHIFT_JIS = 5, ENCODING_KOREAN = 6, ENCODING_LATIN_HEBREW = 7, ENCODING_LATIN = 8, ENCODING_GSM_7BIT = 9, ENCODING_GSM_DCS = 10, } Encoding; static const gchar * encoding_to_string (Encoding encoding) { static const gchar *encoding_str[] = { "octet", "extend protocol message", "7-bit ASCII", "IA5", "unicode", "shift-j is", "korean", "latin/hebrew", "latin", "7-bit GSM", "GSM data coding scheme" }; if (encoding >= ENCODING_OCTET && encoding <= ENCODING_GSM_DCS) return encoding_str[encoding]; return "unknown"; } /*****************************************************************************/ /* Read bits; o_bits < 8; n_bits <= 8 * * Byte 0 Byte 1 * [7|6|5|4|3|2|1|0] [7|6|5|4|3|2|1|0] * * o_bits+n_bits <= 16 * */ static guint8 read_bits (const guint8 *bytes, guint8 o_bits, guint8 n_bits) { guint8 bits_in_first; guint8 bits_in_second; g_assert (o_bits < 8); g_assert (n_bits <= 8); g_assert (o_bits + n_bits <= 16); /* Read only from the first byte */ if (o_bits + n_bits <= 8) return (bytes[0] >> (8 - o_bits - n_bits)) & ((1 << n_bits) - 1); /* Read (8 - o_bits) from the first byte and (n_bits - (8 - o_bits)) from the second byte */ bits_in_first = 8 - o_bits; bits_in_second = n_bits - bits_in_first; return (read_bits (&bytes[0], o_bits, bits_in_first) << bits_in_second) | read_bits (&bytes[1], 0, bits_in_second); } /*****************************************************************************/ /* Cause code to delivery state */ static MMSmsDeliveryState cause_code_to_delivery_state (guint8 error_class, guint8 cause_code) { guint delivery_state = 0; switch (error_class) { case ERROR_CLASS_NO_ERROR: return MM_SMS_DELIVERY_STATE_COMPLETED_RECEIVED; case ERROR_CLASS_TEMPORARY: delivery_state += 0x300; break; case ERROR_CLASS_PERMANENT: delivery_state += 0x200; break; default: return MM_SMS_DELIVERY_STATE_UNKNOWN; } /* Fixes for unknown cause codes */ if (cause_code >= 6 && cause_code <= 31) /* 6 to 31 reserved, treat as CAUSE_CODE_NETWORK_PROBLEM_OTHER */ delivery_state += CAUSE_CODE_NETWORK_PROBLEM_OTHER; else if (cause_code >= 40 && cause_code <= 47) /* 40 to 47 reserved, treat as CAUSE_CODE_TERMINAL_PROBLEM_OTHER */ delivery_state += CAUSE_CODE_TERMINAL_PROBLEM_OTHER; else if (cause_code >= 48 && cause_code <= 63) /* 48 to 63 reserved, treat as CAUSE_CODE_TERMINAL_PROBLEM_SMS_DELIVERY_POSTPONED */ delivery_state += CAUSE_CODE_TERMINAL_PROBLEM_SMS_DELIVERY_POSTPONED; else if (cause_code >= 67 && cause_code <= 95) /* 67 to 95 reserved, treat as CAUSE_CODE_RADIO_INTERFACE_PROBLEM_OTHER */ delivery_state += CAUSE_CODE_RADIO_INTERFACE_PROBLEM_OTHER; else if (cause_code == 101) /* 101 reserved */ delivery_state += CAUSE_CODE_GENERAL_PROBLEM_OTHER; else if (cause_code >= 108) /* cause_code <= 255 is always true */ /* 108 to 223 reserved, treat as CAUSE_CODE_GENERAL_PROBLEM_OTHER * 224 to 255 reserved for TIA/EIA-41 extension, otherwise treat as CAUSE_CODE_GENERAL_PROBLEM_OTHER */ delivery_state += CAUSE_CODE_GENERAL_PROBLEM_OTHER; else /* direct relationship */ delivery_state += cause_code; return (MMSmsDeliveryState) delivery_state; } /*****************************************************************************/ MMSmsPart * mm_sms_part_cdma_new_from_pdu (guint index, const gchar *hexpdu, gpointer log_object, GError **error) { g_autofree guint8 *pdu = NULL; gsize pdu_len; /* Convert PDU from hex to binary */ pdu = mm_utils_hexstr2bin (hexpdu, -1, &pdu_len, error); if (!pdu) { g_prefix_error (error, "Couldn't convert CDMA PDU from hex to binary: "); return NULL; } return mm_sms_part_cdma_new_from_binary_pdu (index, pdu, pdu_len, log_object, error); } struct Parameter { guint8 parameter_id; guint8 parameter_len; guint8 parameter_value[]; } __attribute__((packed)); static void read_teleservice_id (MMSmsPart *sms_part, const struct Parameter *parameter, gpointer log_object) { guint16 teleservice_id; g_assert (parameter->parameter_id == PARAMETER_ID_TELESERVICE_ID); if (parameter->parameter_len != 2) { mm_obj_dbg (log_object, " invalid teleservice ID length found (%u != 2): ignoring", parameter->parameter_len); return; } memcpy (&teleservice_id, ¶meter->parameter_value[0], 2); teleservice_id = GUINT16_FROM_BE (teleservice_id); switch (teleservice_id){ case MM_SMS_CDMA_TELESERVICE_ID_CMT91: case MM_SMS_CDMA_TELESERVICE_ID_WPT: case MM_SMS_CDMA_TELESERVICE_ID_WMT: case MM_SMS_CDMA_TELESERVICE_ID_VMN: case MM_SMS_CDMA_TELESERVICE_ID_WAP: case MM_SMS_CDMA_TELESERVICE_ID_WEMT: case MM_SMS_CDMA_TELESERVICE_ID_SCPT: case MM_SMS_CDMA_TELESERVICE_ID_CATPT: break; default: mm_obj_dbg (log_object, " invalid teleservice ID found (%u): ignoring", teleservice_id); return; } mm_obj_dbg (log_object, " teleservice ID: %s (%u)", mm_sms_cdma_teleservice_id_get_string (teleservice_id), teleservice_id); mm_sms_part_set_cdma_teleservice_id (sms_part, (MMSmsCdmaTeleserviceId)teleservice_id); } static void read_service_category (MMSmsPart *sms_part, const struct Parameter *parameter, gpointer log_object) { guint16 service_category; g_assert (parameter->parameter_id == PARAMETER_ID_SERVICE_CATEGORY); if (parameter->parameter_len != 2) { mm_obj_dbg (log_object, " invalid service category length found (%u != 2): ignoring", parameter->parameter_len); return; } memcpy (&service_category, ¶meter->parameter_value[0], 2); service_category = GUINT16_FROM_BE (service_category); switch (service_category) { case MM_SMS_CDMA_SERVICE_CATEGORY_EMERGENCY_BROADCAST: case MM_SMS_CDMA_SERVICE_CATEGORY_ADMINISTRATIVE: case MM_SMS_CDMA_SERVICE_CATEGORY_MAINTENANCE: case MM_SMS_CDMA_SERVICE_CATEGORY_GENERAL_NEWS_LOCAL: case MM_SMS_CDMA_SERVICE_CATEGORY_GENERAL_NEWS_REGIONAL: case MM_SMS_CDMA_SERVICE_CATEGORY_GENERAL_NEWS_NATIONAL: case MM_SMS_CDMA_SERVICE_CATEGORY_GENERAL_NEWS_INTERNATIONAL: case MM_SMS_CDMA_SERVICE_CATEGORY_BUSINESS_NEWS_LOCAL: case MM_SMS_CDMA_SERVICE_CATEGORY_BUSINESS_NEWS_REGIONAL: case MM_SMS_CDMA_SERVICE_CATEGORY_BUSINESS_NEWS_NATIONAL: case MM_SMS_CDMA_SERVICE_CATEGORY_BUSINESS_NEWS_INTERNATIONAL: case MM_SMS_CDMA_SERVICE_CATEGORY_SPORTS_NEWS_LOCAL: case MM_SMS_CDMA_SERVICE_CATEGORY_SPORTS_NEWS_REGIONAL: case MM_SMS_CDMA_SERVICE_CATEGORY_SPORTS_NEWS_NATIONAL: case MM_SMS_CDMA_SERVICE_CATEGORY_SPORTS_NEWS_INTERNATIONAL: case MM_SMS_CDMA_SERVICE_CATEGORY_ENTERTAINMENT_NEWS_LOCAL: case MM_SMS_CDMA_SERVICE_CATEGORY_ENTERTAINMENT_NEWS_REGIONAL: case MM_SMS_CDMA_SERVICE_CATEGORY_ENTERTAINMENT_NEWS_NATIONAL: case MM_SMS_CDMA_SERVICE_CATEGORY_ENTERTAINMENT_NEWS_INTERNATIONAL: case MM_SMS_CDMA_SERVICE_CATEGORY_LOCAL_WEATHER: case MM_SMS_CDMA_SERVICE_CATEGORY_TRAFFIC_REPORT: case MM_SMS_CDMA_SERVICE_CATEGORY_FLIGHT_SCHEDULES: case MM_SMS_CDMA_SERVICE_CATEGORY_RESTAURANTS: case MM_SMS_CDMA_SERVICE_CATEGORY_LODGINGS: case MM_SMS_CDMA_SERVICE_CATEGORY_RETAIL_DIRECTORY: case MM_SMS_CDMA_SERVICE_CATEGORY_ADVERTISEMENTS: case MM_SMS_CDMA_SERVICE_CATEGORY_STOCK_QUOTES: case MM_SMS_CDMA_SERVICE_CATEGORY_EMPLOYMENT: case MM_SMS_CDMA_SERVICE_CATEGORY_HOSPITALS: case MM_SMS_CDMA_SERVICE_CATEGORY_TECHNOLOGY_NEWS: case MM_SMS_CDMA_SERVICE_CATEGORY_MULTICATEGORY: case MM_SMS_CDMA_SERVICE_CATEGORY_CMAS_PRESIDENTIAL_ALERT: case MM_SMS_CDMA_SERVICE_CATEGORY_CMAS_EXTREME_THREAT: case MM_SMS_CDMA_SERVICE_CATEGORY_CMAS_SEVERE_THREAT: case MM_SMS_CDMA_SERVICE_CATEGORY_CMAS_CHILD_ABDUCTION_EMERGENCY: case MM_SMS_CDMA_SERVICE_CATEGORY_CMAS_TEST: break; default: mm_obj_dbg (log_object, " invalid service category found (%u): ignoring", service_category); return; } mm_obj_dbg (log_object, " service category: %s (%u)", mm_sms_cdma_service_category_get_string (service_category), service_category); mm_sms_part_set_cdma_service_category (sms_part, (MMSmsCdmaServiceCategory)service_category); } static guint8 dtmf_to_ascii (guint8 dtmf, gpointer log_object) { static const gchar dtmf_to_ascii_digits[13] = { '\0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '*', '#' }; if (dtmf > 0 && dtmf < 13) return dtmf_to_ascii_digits[dtmf]; mm_obj_dbg (log_object, " invalid dtmf digit: %u", dtmf); return '\0'; } static void read_address (MMSmsPart *sms_part, const struct Parameter *parameter, gpointer log_object) { guint8 digit_mode; guint8 number_mode; guint8 number_type; guint8 numbering_plan; guint8 num_fields; guint byte_offset = 0; guint bit_offset = 0; guint i; gchar *number = NULL; #define OFFSETS_UPDATE(n_bits) do { \ bit_offset += n_bits; \ if (bit_offset >= 8) { \ bit_offset-=8; \ byte_offset++; \ } \ } while (0) #define PARAMETER_SIZE_CHECK(required_size) \ if (parameter->parameter_len < required_size) { \ mm_obj_dbg (log_object, " cannot read address, need at least %u bytes (got %u)", \ required_size, \ parameter->parameter_len); \ return; \ } /* Readability of digit mode and number mode (first 2 bits, i.e. first byte) */ PARAMETER_SIZE_CHECK (1); /* Digit mode */ digit_mode = read_bits (¶meter->parameter_value[byte_offset], bit_offset, 1); OFFSETS_UPDATE (1); g_assert (digit_mode <= 1); switch (digit_mode) { case DIGIT_MODE_DTMF: mm_obj_dbg (log_object, " digit mode: dtmf"); break; case DIGIT_MODE_ASCII: mm_obj_dbg (log_object, " digit mode: ascii"); break; default: g_assert_not_reached (); } /* Number mode */ number_mode = read_bits (¶meter->parameter_value[byte_offset], bit_offset, 1); OFFSETS_UPDATE (1); switch (number_mode) { case NUMBER_MODE_DIGIT: mm_obj_dbg (log_object, " number mode: digit"); break; case NUMBER_MODE_DATA_NETWORK_ADDRESS: mm_obj_dbg (log_object, " number mode: data network address"); break; default: g_assert_not_reached (); } /* Number type */ if (digit_mode == DIGIT_MODE_ASCII) { /* No need for readability check, still in first byte always */ number_type = read_bits (¶meter->parameter_value[byte_offset], bit_offset, 3); OFFSETS_UPDATE (3); switch (number_type) { case NUMBER_TYPE_UNKNOWN: mm_obj_dbg (log_object, " number type: unknown"); break; case NUMBER_TYPE_INTERNATIONAL: mm_obj_dbg (log_object, " number type: international"); break; case NUMBER_TYPE_NATIONAL: mm_obj_dbg (log_object, " number type: national"); break; case NUMBER_TYPE_NETWORK_SPECIFIC: mm_obj_dbg (log_object, " number type: specific"); break; case NUMBER_TYPE_SUBSCRIBER: mm_obj_dbg (log_object, " number type: subscriber"); break; case NUMBER_TYPE_ABBREVIATED: mm_obj_dbg (log_object, " number type: abbreviated"); break; default: mm_obj_dbg (log_object, " number type unknown (%u)", number_type); break; } } else number_type = 0xFF; /* Numbering plan */ if (digit_mode == DIGIT_MODE_ASCII && number_mode == NUMBER_MODE_DIGIT) { /* Readability of numbering plan; may go to second byte */ PARAMETER_SIZE_CHECK (byte_offset + 1 + ((bit_offset + 4) / 8)); numbering_plan = read_bits (¶meter->parameter_value[byte_offset], bit_offset, 4); OFFSETS_UPDATE (4); switch (numbering_plan) { case NUMBERING_PLAN_UNKNOWN: mm_obj_dbg (log_object, " numbering plan: unknown"); break; case NUMBERING_PLAN_ISDN: mm_obj_dbg (log_object, " numbering plan: isdn"); break; case NUMBERING_PLAN_DATA: mm_obj_dbg (log_object, " numbering plan: data"); break; case NUMBERING_PLAN_TELEX: mm_obj_dbg (log_object, " numbering plan: telex"); break; case NUMBERING_PLAN_PRIVATE: mm_obj_dbg (log_object, " numbering plan: private"); break; default: mm_obj_dbg (log_object, " numbering plan unknown (%u)", numbering_plan); break; } } else numbering_plan = 0xFF; /* Readability of num_fields; will go to third byte (((bit_offset + 8) / 8) == 1) */ PARAMETER_SIZE_CHECK (byte_offset + 2); num_fields = read_bits (¶meter->parameter_value[byte_offset], bit_offset, 8); OFFSETS_UPDATE (8); mm_obj_dbg (log_object, " num fields: %u", num_fields); /* Address string */ if (digit_mode == DIGIT_MODE_DTMF) { /* DTMF */ PARAMETER_SIZE_CHECK (byte_offset + 1 + ((bit_offset + (num_fields * 4)) / 8)); number = g_malloc (num_fields + 1); for (i = 0; i < num_fields; i++) { number[i] = dtmf_to_ascii (read_bits (¶meter->parameter_value[byte_offset], bit_offset, 4), log_object); OFFSETS_UPDATE (4); } number[i] = '\0'; } else if (number_mode == NUMBER_MODE_DIGIT) { /* ASCII * TODO: should we expose numbering plan and number type? */ PARAMETER_SIZE_CHECK (byte_offset + 1 + ((bit_offset + (num_fields * 8)) / 8)); number = g_malloc (num_fields + 1); for (i = 0; i < num_fields; i++) { number[i] = read_bits (¶meter->parameter_value[byte_offset], bit_offset, 8); OFFSETS_UPDATE (8); } number[i] = '\0'; } else if (number_type == DATA_NETWORK_ADDRESS_TYPE_INTERNET_EMAIL_ADDRESS) { /* Internet e-mail address (ASCII) */ PARAMETER_SIZE_CHECK (byte_offset + 1 + ((bit_offset + (num_fields * 8)) / 8)); number = g_malloc (num_fields + 1); for (i = 0; i < num_fields; i++) { number[i] = read_bits (¶meter->parameter_value[byte_offset], bit_offset, 8); OFFSETS_UPDATE (8); } number[i] = '\0'; } else if (number_type == DATA_NETWORK_ADDRESS_TYPE_INTERNET_PROTOCOL) { GString *str; /* Binary data network address (most significant first) * For now, just print the hex string (e.g. FF:01...) */ PARAMETER_SIZE_CHECK (byte_offset + 1 + ((bit_offset + (num_fields * 8)) / 8)); str = g_string_sized_new (num_fields * 2); for (i = 0; i < num_fields; i++) { g_string_append_printf (str, "%.2X", read_bits (¶meter->parameter_value[byte_offset], bit_offset, 8)); OFFSETS_UPDATE (8); } number = g_string_free (str, FALSE); } else mm_obj_dbg (log_object, " data network address number type unknown (%u)", number_type); mm_obj_dbg (log_object, " address: %s", number); mm_sms_part_set_number (sms_part, number); g_free (number); #undef OFFSETS_UPDATE #undef PARAMETER_SIZE_CHECK } static void read_bearer_reply_option (MMSmsPart *sms_part, const struct Parameter *parameter, gpointer log_object) { guint8 sequence; g_assert (parameter->parameter_id == PARAMETER_ID_BEARER_REPLY_OPTION); if (parameter->parameter_len != 1) { mm_obj_dbg (log_object, " invalid bearer reply option length found (%u != 1): ignoring", parameter->parameter_len); return; } sequence = read_bits (¶meter->parameter_value[0], 0, 6); mm_obj_dbg (log_object, " sequence: %u", sequence); mm_sms_part_set_message_reference (sms_part, sequence); } static void read_cause_codes (MMSmsPart *sms_part, const struct Parameter *parameter, gpointer log_object) { guint8 sequence; guint8 error_class; guint8 cause_code; MMSmsDeliveryState delivery_state; g_assert (parameter->parameter_id == PARAMETER_ID_CAUSE_CODES); if (parameter->parameter_len != 1 && parameter->parameter_len != 2) { mm_obj_dbg (log_object, " invalid cause codes length found (%u): ignoring", parameter->parameter_len); return; } sequence = read_bits (¶meter->parameter_value[0], 0, 6); mm_obj_dbg (log_object, " sequence: %u", sequence); error_class = read_bits (¶meter->parameter_value[0], 6, 2); mm_obj_dbg (log_object, " error class: %u", error_class); if (error_class != ERROR_CLASS_NO_ERROR) { if (parameter->parameter_len != 2) { mm_obj_dbg (log_object, " invalid cause codes length found (%u != 2): ignoring", parameter->parameter_len); return; } cause_code = parameter->parameter_value[1]; mm_obj_dbg (log_object, " cause code: %u", cause_code); } else cause_code = 0; delivery_state = cause_code_to_delivery_state (error_class, cause_code); mm_obj_dbg (log_object, " delivery state: %s", mm_sms_delivery_state_get_string (delivery_state)); mm_sms_part_set_message_reference (sms_part, sequence); mm_sms_part_set_delivery_state (sms_part, delivery_state); } static void read_bearer_data_message_identifier (MMSmsPart *sms_part, const struct Parameter *subparameter, gpointer log_object) { guint8 message_type; guint16 message_id; guint8 header_ind; g_assert (subparameter->parameter_id == SUBPARAMETER_ID_MESSAGE_ID); if (subparameter->parameter_len != 3) { mm_obj_dbg (log_object, " invalid message identifier length found (%u): ignoring", subparameter->parameter_len); return; } message_type = read_bits (&subparameter->parameter_value[0], 0, 4); switch (message_type) { case TELESERVICE_MESSAGE_TYPE_UNKNOWN: mm_obj_dbg (log_object, " message type: unknown"); break; case TELESERVICE_MESSAGE_TYPE_DELIVER: mm_obj_dbg (log_object, " message type: deliver"); mm_sms_part_set_pdu_type (sms_part, MM_SMS_PDU_TYPE_CDMA_DELIVER); break; case TELESERVICE_MESSAGE_TYPE_SUBMIT: mm_obj_dbg (log_object, " message type: submit"); mm_sms_part_set_pdu_type (sms_part, MM_SMS_PDU_TYPE_CDMA_SUBMIT); break; case TELESERVICE_MESSAGE_TYPE_CANCELLATION: mm_obj_dbg (log_object, " message type: cancellation"); mm_sms_part_set_pdu_type (sms_part, MM_SMS_PDU_TYPE_CDMA_CANCELLATION); break; case TELESERVICE_MESSAGE_TYPE_DELIVERY_ACKNOWLEDGEMENT: mm_obj_dbg (log_object, " message type: delivery acknowledgement"); mm_sms_part_set_pdu_type (sms_part, MM_SMS_PDU_TYPE_CDMA_DELIVERY_ACKNOWLEDGEMENT); break; case TELESERVICE_MESSAGE_TYPE_USER_ACKNOWLEDGEMENT: mm_obj_dbg (log_object, " message type: user acknowledgement"); mm_sms_part_set_pdu_type (sms_part, MM_SMS_PDU_TYPE_CDMA_USER_ACKNOWLEDGEMENT); break; case TELESERVICE_MESSAGE_TYPE_READ_ACKNOWLEDGEMENT: mm_obj_dbg (log_object, " message type: read acknowledgement"); mm_sms_part_set_pdu_type (sms_part, MM_SMS_PDU_TYPE_CDMA_READ_ACKNOWLEDGEMENT); break; default: mm_obj_dbg (log_object, " message type unknown (%u)", message_type); break; } message_id = ((read_bits (&subparameter->parameter_value[0], 4, 8) << 8) | (read_bits (&subparameter->parameter_value[1], 4, 8))); message_id = GUINT16_FROM_BE (message_id); mm_sms_part_set_message_id (sms_part, message_id); mm_obj_dbg (log_object, " message id: %u", (guint) message_id); header_ind = read_bits (&subparameter->parameter_value[2], 4, 1); mm_obj_dbg (log_object, " header indicator: %u", header_ind); } static void read_bearer_data_user_data (MMSmsPart *sms_part, const struct Parameter *subparameter, gpointer log_object) { guint8 message_encoding; guint8 message_type = 0; guint8 num_fields; guint wdp_total_segments; guint wdp_segment_number; guint byte_offset = 0; guint bit_offset = 0; #define OFFSETS_UPDATE(n_bits) do { \ bit_offset += n_bits; \ if (bit_offset >= 8) { \ bit_offset-=8; \ byte_offset++; \ } \ } while (0) #define SUBPARAMETER_SIZE_CHECK_BITS(required_bits) \ do { \ guint required_bytes; \ \ required_bytes = byte_offset + ((bit_offset + required_bits) / 8); \ if ((bit_offset + required_bits) % 8) \ required_bytes++; \ if (subparameter->parameter_len < required_bytes) { \ mm_obj_dbg (log_object, " cannot read user data, need at least %u bytes (got %u)", \ required_bytes, \ subparameter->parameter_len); \ return; \ } \ } while (0) g_assert (subparameter->parameter_id == SUBPARAMETER_ID_USER_DATA); /* Message encoding */ SUBPARAMETER_SIZE_CHECK_BITS (5); message_encoding = read_bits (&subparameter->parameter_value[byte_offset], bit_offset, 5); OFFSETS_UPDATE (5); mm_obj_dbg (log_object, " message encoding: %s", encoding_to_string (message_encoding)); /* Message type, only if extended protocol message */ if (message_encoding == ENCODING_EXTENDED_PROTOCOL_MESSAGE) { SUBPARAMETER_SIZE_CHECK_BITS (8); message_type = read_bits (&subparameter->parameter_value[byte_offset], bit_offset, 8); OFFSETS_UPDATE (8); mm_obj_dbg (log_object, " message type: %u", message_type); } /* Number of fields */ SUBPARAMETER_SIZE_CHECK_BITS (8); num_fields = read_bits (&subparameter->parameter_value[byte_offset], bit_offset, 8); OFFSETS_UPDATE (8); mm_obj_dbg (log_object, " num fields: %u", num_fields); /* Now, process actual text or data */ switch (message_encoding) { case ENCODING_OCTET: { GByteArray *data; guint i; SUBPARAMETER_SIZE_CHECK_BITS (num_fields * 8); data = g_byte_array_sized_new (num_fields); g_byte_array_set_size (data, num_fields); for (i = 0; i < num_fields; i++) { data->data[i] = read_bits (&subparameter->parameter_value[byte_offset], bit_offset, 8); OFFSETS_UPDATE (8); } if ((mm_sms_part_get_cdma_teleservice_id (sms_part) == MM_SMS_CDMA_TELESERVICE_ID_WAP) && (num_fields >= 3) && (data->data[0] == 0x00)) { /* This is a CDMA WAP WDP message with a segmentation header, as * defined in section 6.5 of WAP-256-WDP-20010614-a */ wdp_total_segments = data->data[1]; wdp_segment_number = data->data[2]; mm_obj_dbg (log_object, " WAP WDP Payload, segment: %d total: %d", wdp_segment_number, wdp_total_segments); /* Use message id as the reference number, since it is the same * across message sets*/ mm_sms_part_set_concat_reference (sms_part, mm_sms_part_get_message_id (sms_part)); mm_sms_part_set_concat_max (sms_part, wdp_total_segments); /* Segment Number is 0-indexed, concat_sequence expects 1-indexed values */ mm_sms_part_set_concat_sequence (sms_part, wdp_segment_number + 1); if (wdp_segment_number == 0) { /* Remove the 3 byte segmentation header as well as the 16 bit source and dest port fields */ g_byte_array_remove_range (data, 0, 7); } else { /* Remove segmentation header from additional segments to merge cleanly */ g_byte_array_remove_range (data, 0, 3); } } mm_obj_dbg (log_object, " data: (%u bytes)", num_fields); mm_sms_part_take_data (sms_part, data); break; } case ENCODING_ASCII_7BIT: { gchar *text; guint i; if (num_fields == 0) { mm_obj_dbg (log_object, " text: ''"); mm_sms_part_set_text (sms_part, ""); break; } SUBPARAMETER_SIZE_CHECK_BITS (num_fields * 7); text = g_malloc (num_fields + 1); for (i = 0; i < num_fields; i++) { text[i] = read_bits (&subparameter->parameter_value[byte_offset], bit_offset, 7); OFFSETS_UPDATE (7); } text[i] = '\0'; mm_obj_dbg (log_object, " text: '%s'", text); mm_sms_part_take_text (sms_part, text); break; } case ENCODING_LATIN: { gchar *latin; gchar *text; guint i; if (num_fields == 0) { mm_obj_dbg (log_object, " text: ''"); mm_sms_part_set_text (sms_part, ""); break; } SUBPARAMETER_SIZE_CHECK_BITS (num_fields * 8); latin = g_malloc (num_fields + 1); for (i = 0; i < num_fields; i++) { latin[i] = read_bits (&subparameter->parameter_value[byte_offset], bit_offset, 8); OFFSETS_UPDATE (8); } latin[i] = '\0'; text = g_convert (latin, -1, "UTF-8", "ISO-8859-1", NULL, NULL, NULL); if (!text) { mm_obj_dbg (log_object, " text/data: ignored (latin to UTF-8 conversion error)"); } else { mm_obj_dbg (log_object, " text: '%s'", text); mm_sms_part_take_text (sms_part, text); } g_free (latin); break; } case ENCODING_UNICODE: { gchar *utf16; gchar *text; guint i; guint num_bytes; if (num_fields == 0) { mm_obj_dbg (log_object, " text: ''"); mm_sms_part_set_text (sms_part, ""); break; } /* 2 bytes per field! */ num_bytes = num_fields * 2; SUBPARAMETER_SIZE_CHECK_BITS (num_bytes * 8); utf16 = g_malloc (num_bytes); for (i = 0; i < num_bytes; i++) { utf16[i] = read_bits (&subparameter->parameter_value[byte_offset], bit_offset, 8); OFFSETS_UPDATE (8); } text = g_convert (utf16, num_bytes, "UTF-8", "UCS-2BE", NULL, NULL, NULL); if (!text) { mm_obj_dbg (log_object, " text/data: ignored (UTF-16 to UTF-8 conversion error)"); } else { mm_obj_dbg (log_object, " text: '%s'", text); mm_sms_part_take_text (sms_part, text); } g_free (utf16); break; } default: mm_obj_dbg (log_object, " text/data: ignored (unsupported encoding)"); } #undef OFFSETS_UPDATE #undef SUBPARAMETER_SIZE_CHECK_BITS } static void read_bearer_data (MMSmsPart *sms_part, const struct Parameter *parameter, gpointer log_object) { guint offset; #define PARAMETER_SIZE_CHECK(required_size) \ if (parameter->parameter_len < required_size) { \ mm_obj_dbg (log_object, " cannot read bearer data, need at least %u bytes (got %u)", \ required_size, \ parameter->parameter_len); \ return; \ } offset = 0; while (offset < parameter->parameter_len) { const struct Parameter *subparameter; PARAMETER_SIZE_CHECK (offset + 2); subparameter = (const struct Parameter *)¶meter->parameter_value[offset]; offset += 2; PARAMETER_SIZE_CHECK (offset + subparameter->parameter_len); offset += subparameter->parameter_len; switch (subparameter->parameter_id) { case SUBPARAMETER_ID_MESSAGE_ID: mm_obj_dbg (log_object, " reading message ID..."); read_bearer_data_message_identifier (sms_part, subparameter, log_object); break; case SUBPARAMETER_ID_USER_DATA: mm_obj_dbg (log_object, " reading user data..."); read_bearer_data_user_data (sms_part, subparameter, log_object); break; case SUBPARAMETER_ID_USER_RESPONSE_CODE: mm_obj_dbg (log_object, " skipping user response code..."); break; case SUBPARAMETER_ID_MESSAGE_CENTER_TIME_STAMP: mm_obj_dbg (log_object, " skipping message center timestamp..."); break; case SUBPARAMETER_ID_VALIDITY_PERIOD_ABSOLUTE: mm_obj_dbg (log_object, " skipping absolute validity period..."); break; case SUBPARAMETER_ID_VALIDITY_PERIOD_RELATIVE: mm_obj_dbg (log_object, " skipping relative validity period..."); break; case SUBPARAMETER_ID_DEFERRED_DELIVERY_TIME_ABSOLUTE: mm_obj_dbg (log_object, " skipping absolute deferred delivery time..."); break; case SUBPARAMETER_ID_DEFERRED_DELIVERY_TIME_RELATIVE: mm_obj_dbg (log_object, " skipping relative deferred delivery time..."); break; case SUBPARAMETER_ID_PRIORITY_INDICATOR: mm_obj_dbg (log_object, " skipping priority indicator..."); break; case SUBPARAMETER_ID_PRIVACY_INDICATOR: mm_obj_dbg (log_object, " skipping privacy indicator..."); break; case SUBPARAMETER_ID_REPLY_OPTION: mm_obj_dbg (log_object, " skipping reply option..."); break; case SUBPARAMETER_ID_NUMBER_OF_MESSAGES: mm_obj_dbg (log_object, " skipping number of messages..."); break; case SUBPARAMETER_ID_ALERT_ON_MESSAGE_DELIVERY: mm_obj_dbg (log_object, " skipping alert on message delivery..."); break; case SUBPARAMETER_ID_LANGUAGE_INDICATOR: mm_obj_dbg (log_object, " skipping language indicator..."); break; case SUBPARAMETER_ID_CALL_BACK_NUMBER: mm_obj_dbg (log_object, " skipping call back number..."); break; case SUBPARAMETER_ID_MESSAGE_DISPLAY_MODE: mm_obj_dbg (log_object, " skipping message display mode..."); break; case SUBPARAMETER_ID_MULTIPLE_ENCODING_USER_DATA: mm_obj_dbg (log_object, " skipping multiple encoding user data..."); break; case SUBPARAMETER_ID_MESSAGE_DEPOSIT_INDEX: mm_obj_dbg (log_object, " skipping message deposit index..."); break; case SUBPARAMETER_ID_SERVICE_CATEGORY_PROGRAM_DATA: mm_obj_dbg (log_object, " skipping service category program data..."); break; case SUBPARAMETER_ID_SERVICE_CATEGORY_PROGRAM_RESULT: mm_obj_dbg (log_object, " skipping service category program result..."); break; case SUBPARAMETER_ID_MESSAGE_STATUS: mm_obj_dbg (log_object, " skipping message status..."); break; case SUBPARAMETER_ID_TP_FAILURE_CAUSE: mm_obj_dbg (log_object, " skipping TP failure case..."); break; case SUBPARAMETER_ID_ENHANCED_VMN: mm_obj_dbg (log_object, " skipping enhanced vmn..."); break; case SUBPARAMETER_ID_ENHANCED_VMN_ACK: mm_obj_dbg (log_object, " skipping enhanced vmn ack..."); break; default: mm_obj_dbg (log_object, " unknown subparameter found: '%u' (ignoring)", subparameter->parameter_id); break; } } #undef PARAMETER_SIZE_CHECK } MMSmsPart * mm_sms_part_cdma_new_from_binary_pdu (guint index, const guint8 *pdu, gsize pdu_len, gpointer log_object, GError **error) { MMSmsPart *sms_part; guint offset; guint message_type; /* Create the new MMSmsPart */ sms_part = mm_sms_part_new (index, MM_SMS_PDU_TYPE_UNKNOWN); if (index != SMS_PART_INVALID_INDEX) mm_obj_dbg (log_object, "parsing CDMA PDU (%u)...", index); else mm_obj_dbg (log_object, "parsing CDMA PDU..."); #define PDU_SIZE_CHECK(required_size, check_descr_str) \ if (pdu_len < required_size) { \ g_set_error (error, \ MM_CORE_ERROR, \ MM_CORE_ERROR_FAILED, \ "CDMA PDU too short, %s: %" G_GSIZE_FORMAT " < %u", \ check_descr_str, \ pdu_len, \ required_size); \ mm_sms_part_free (sms_part); \ return NULL; \ } offset = 0; /* First byte: SMS message type */ PDU_SIZE_CHECK (offset + 1, "cannot read SMS message type"); message_type = pdu[offset++]; switch (message_type) { case MESSAGE_TYPE_POINT_TO_POINT: case MESSAGE_TYPE_BROADCAST: case MESSAGE_TYPE_ACKNOWLEDGE: break; default: g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Invalid SMS message type (%u)", message_type); mm_sms_part_free (sms_part); return NULL; } /* Now walk parameters one by one */ while (offset < pdu_len) { const struct Parameter *parameter; PDU_SIZE_CHECK (offset + 2, "cannot read parameter header"); parameter = (const struct Parameter *)&pdu[offset]; offset += 2; PDU_SIZE_CHECK (offset + parameter->parameter_len, "cannot read parameter value"); offset += parameter->parameter_len; switch (parameter->parameter_id) { case PARAMETER_ID_TELESERVICE_ID: mm_obj_dbg (log_object, " reading teleservice ID..."); read_teleservice_id (sms_part, parameter, log_object); break; case PARAMETER_ID_SERVICE_CATEGORY: mm_obj_dbg (log_object, " reading service category..."); read_service_category (sms_part, parameter, log_object); break; case PARAMETER_ID_ORIGINATING_ADDRESS: mm_obj_dbg (log_object, " reading originating address..."); if (mm_sms_part_get_number (sms_part)) mm_obj_dbg (log_object, " cannot read originating address; an address field was already read"); else read_address (sms_part, parameter, log_object); break; case PARAMETER_ID_ORIGINATING_SUBADDRESS: mm_obj_dbg (log_object, " skipping originating subaddress..."); break; case PARAMETER_ID_DESTINATION_ADDRESS: mm_obj_dbg (log_object, " reading destination address..."); if (mm_sms_part_get_number (sms_part)) mm_obj_dbg (log_object, " cannot read destination address; an address field was already read"); else read_address (sms_part, parameter, log_object); break; case PARAMETER_ID_DESTINATION_SUBADDRESS: mm_obj_dbg (log_object, " skipping destination subaddress..."); break; case PARAMETER_ID_BEARER_REPLY_OPTION: mm_obj_dbg (log_object, " reading bearer reply option..."); read_bearer_reply_option (sms_part, parameter, log_object); break; case PARAMETER_ID_CAUSE_CODES: mm_obj_dbg (log_object, " reading cause codes..."); read_cause_codes (sms_part, parameter, log_object); break; case PARAMETER_ID_BEARER_DATA: mm_obj_dbg (log_object, " reading bearer data..."); read_bearer_data (sms_part, parameter, log_object); break; default: mm_obj_dbg (log_object, " unknown parameter found: '%u' (ignoring)", parameter->parameter_id); break; } } /* Check mandatory parameters */ switch (message_type) { case MESSAGE_TYPE_POINT_TO_POINT: if (mm_sms_part_get_cdma_teleservice_id (sms_part) == MM_SMS_CDMA_TELESERVICE_ID_UNKNOWN) mm_obj_dbg (log_object, " mandatory parameter missing: teleservice ID not found or invalid in point-to-point message"); break; case MESSAGE_TYPE_BROADCAST: if (mm_sms_part_get_cdma_service_category (sms_part) == MM_SMS_CDMA_SERVICE_CATEGORY_UNKNOWN) mm_obj_dbg (log_object, " mandatory parameter missing: service category not found or invalid in broadcast message"); break; case MESSAGE_TYPE_ACKNOWLEDGE: if (mm_sms_part_get_message_reference (sms_part) == 0) mm_obj_dbg (log_object, " mandatory parameter missing: cause codes not found or invalid in acknowledge message"); break; default: break; } #undef PDU_SIZE_CHECK return sms_part; } /*****************************************************************************/ /* Write bits; o_bits < 8; n_bits <= 8 * * Byte 0 Byte 1 * [7|6|5|4|3|2|1|0] [7|6|5|4|3|2|1|0] * * o_bits+n_bits <= 16 * * NOTE! The bits being set should be 0 initially. */ static void write_bits (guint8 *bytes, guint8 o_bits, guint8 n_bits, guint8 bits) { guint8 bits_in_first; guint8 bits_in_second; g_assert (o_bits < 8); g_assert (n_bits <= 8); g_assert (o_bits + n_bits <= 16); /* Write only in the first byte */ if (o_bits + n_bits <= 8) { bytes[0] |= (bits & ((1 << n_bits) - 1)) << (8 - o_bits - n_bits); return; } /* Write (8 - o_bits) in the first byte and (n_bits - (8 - o_bits)) in the second byte */ bits_in_first = 8 - o_bits; bits_in_second = n_bits - bits_in_first; write_bits (&bytes[0], o_bits, bits_in_first, (bits >> bits_in_second)); write_bits (&bytes[1], 0, bits_in_second, bits); } /*****************************************************************************/ static guint8 dtmf_from_ascii (guint8 ascii, gpointer log_object) { if (ascii >= '1' && ascii <= '9') return ascii - '0'; if (ascii == '0') return 10; if (ascii == '*') return 11; if (ascii == '#') return 12; mm_obj_dbg (log_object, " invalid ascii digit in dtmf conversion: %c", ascii); return 0; } static gboolean write_teleservice_id (MMSmsPart *part, guint8 *pdu, guint *absolute_offset, gpointer log_object, GError **error) { guint16 aux16; mm_obj_dbg (log_object, " writing teleservice ID..."); if (mm_sms_part_get_cdma_teleservice_id (part) != MM_SMS_CDMA_TELESERVICE_ID_WMT) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "Teleservice '%s' not supported", mm_sms_cdma_teleservice_id_get_string ( mm_sms_part_get_cdma_teleservice_id (part))); return FALSE; } mm_obj_dbg (log_object, " teleservice ID: %s (%u)", mm_sms_cdma_teleservice_id_get_string (MM_SMS_CDMA_TELESERVICE_ID_WMT), MM_SMS_CDMA_TELESERVICE_ID_WMT); /* Teleservice ID: WMT always */ pdu[0] = PARAMETER_ID_TELESERVICE_ID; pdu[1] = 2; /* parameter_len, always 2 */ aux16 = GUINT16_TO_BE (MM_SMS_CDMA_TELESERVICE_ID_WMT); memcpy (&pdu[2], &aux16, 2); *absolute_offset += 4; return TRUE; } static gboolean write_destination_address (MMSmsPart *part, guint8 *pdu, guint *absolute_offset, gpointer log_object, GError **error) { const gchar *number; guint bit_offset; guint byte_offset; guint n_digits; guint i; mm_obj_dbg (log_object, " writing destination address..."); #define OFFSETS_UPDATE(n_bits) do { \ bit_offset += n_bits; \ if (bit_offset >= 8) { \ bit_offset-=8; \ byte_offset++; \ } \ } while (0) number = mm_sms_part_get_number (part); n_digits = strlen (number); pdu[0] = PARAMETER_ID_DESTINATION_ADDRESS; /* Write parameter length at the end */ byte_offset = 2; bit_offset = 0; /* Digit mode: DTMF always */ mm_obj_dbg (log_object, " digit mode: dtmf"); write_bits (&pdu[byte_offset], bit_offset, 1, DIGIT_MODE_DTMF); OFFSETS_UPDATE (1); /* Number mode: DIGIT always */ mm_obj_dbg (log_object, " number mode: digit"); write_bits (&pdu[byte_offset], bit_offset, 1, NUMBER_MODE_DIGIT); OFFSETS_UPDATE (1); /* Number type and numbering plan only needed in ASCII digit mode, so skip */ /* Number of fields */ if (n_digits > 256) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "Number too long (max 256 digits, %u given)", n_digits); return FALSE; } mm_obj_dbg (log_object, " num fields: %u", n_digits); write_bits (&pdu[byte_offset], bit_offset, 8, n_digits); OFFSETS_UPDATE (8); /* Actual DTMF encoded number */ mm_obj_dbg (log_object, " address: %s", number); for (i = 0; i < n_digits; i++) { guint8 dtmf; dtmf = dtmf_from_ascii (number[i], log_object); if (!dtmf) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "Unsupported character in number: '%c'. Cannot convert to DTMF", number[i]); return FALSE; } write_bits (&pdu[byte_offset], bit_offset, 4, dtmf); OFFSETS_UPDATE (4); } #undef OFFSETS_UPDATE /* Write parameter length (remove header length to offset) */ byte_offset += !!bit_offset - 2; if (byte_offset > 256) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "Number too long (max 256 bytes, %u given)", byte_offset); return FALSE; } pdu[1] = byte_offset; *absolute_offset += (2 + pdu[1]); return TRUE; } static gboolean write_bearer_data_message_identifier (MMSmsPart *part, guint8 *pdu, guint *parameter_offset, gpointer log_object, GError **error) { pdu[0] = SUBPARAMETER_ID_MESSAGE_ID; pdu[1] = 3; /* subparameter_len, always 3 */ mm_obj_dbg (log_object, " writing message identifier: submit"); /* Message type */ write_bits (&pdu[2], 0, 4, TELESERVICE_MESSAGE_TYPE_SUBMIT); /* Skip adding a message id; assume it's filled in by device */ /* And no need for a header ind value, always false */ *parameter_offset += 5; return TRUE; } static GByteArray * decide_best_encoding (const gchar *text, gpointer log_object, guint *num_fields, guint *num_bits_per_field, Encoding *encoding, GError **error) { g_autoptr(GByteArray) barray = NULL; MMModemCharset target_charset = MM_MODEM_CHARSET_UNKNOWN; guint len; len = strlen (text); if (mm_charset_can_convert_to (text, MM_MODEM_CHARSET_IRA)) target_charset = MM_MODEM_CHARSET_IRA; else if (mm_charset_can_convert_to (text, MM_MODEM_CHARSET_8859_1)) target_charset = MM_MODEM_CHARSET_8859_1; else target_charset = MM_MODEM_CHARSET_UCS2; barray = mm_modem_charset_bytearray_from_utf8 (text, target_charset, FALSE, error); if (!barray) { g_prefix_error (error, "Couldn't decide best encoding: "); return NULL; } if (target_charset == MM_MODEM_CHARSET_IRA) { *num_fields = len; *num_bits_per_field = 7; *encoding = ENCODING_ASCII_7BIT; } else if (target_charset == MM_MODEM_CHARSET_8859_1) { *num_fields = barray->len; *num_bits_per_field = 8; *encoding = ENCODING_LATIN; } else if (target_charset == MM_MODEM_CHARSET_UCS2) { *num_fields = barray->len / 2; *num_bits_per_field = 16; *encoding = ENCODING_UNICODE; } else g_assert_not_reached (); return g_steal_pointer (&barray); } static gboolean write_bearer_data_user_data (MMSmsPart *part, guint8 *pdu, guint *parameter_offset, gpointer log_object, GError **error) { const gchar *text; const GByteArray *data; guint bit_offset = 0; guint byte_offset = 0; guint num_fields; guint num_bits_per_field; guint i; Encoding encoding; GByteArray *converted = NULL; const GByteArray *aux; guint num_bits_per_iter; mm_obj_dbg (log_object, " writing user data..."); #define OFFSETS_UPDATE(n_bits) do { \ bit_offset += n_bits; \ if (bit_offset >= 8) { \ bit_offset-=8; \ byte_offset++; \ } \ } while (0) text = mm_sms_part_get_text (part); data = mm_sms_part_get_data (part); g_assert (text || data); g_assert (!(!text && !data)); pdu[0] = SUBPARAMETER_ID_USER_DATA; /* Write parameter length at the end */ byte_offset = 2; bit_offset = 0; /* Text or Data */ if (text) { converted = decide_best_encoding (text, log_object, &num_fields, &num_bits_per_field, &encoding, error); if (!converted) return FALSE; aux = (const GByteArray *)converted; } else { aux = data; num_fields = data->len; num_bits_per_field = 8; encoding = ENCODING_OCTET; } /* Message encoding*/ mm_obj_dbg (log_object, " message encoding: %s", encoding_to_string (encoding)); write_bits (&pdu[byte_offset], bit_offset, 5, encoding); OFFSETS_UPDATE (5); /* Number of fields */ if (num_fields > 256) { if (converted) g_byte_array_unref (converted); g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "Data too long (max 256 fields, %u given)", num_fields); return FALSE; } mm_obj_dbg (log_object, " num fields: %u", num_fields); write_bits (&pdu[byte_offset], bit_offset, 8, num_fields); OFFSETS_UPDATE (8); /* For ASCII-7, write 7 bits in each iteration; for the remaining ones * go byte per byte */ if (text) mm_obj_dbg (log_object, " text: '%s'", text); else mm_obj_dbg (log_object, " data: (%u bytes)", num_fields); num_bits_per_iter = num_bits_per_field < 8 ? num_bits_per_field : 8; for (i = 0; i < aux->len; i++) { write_bits (&pdu[byte_offset], bit_offset, num_bits_per_iter, aux->data[i]); OFFSETS_UPDATE (num_bits_per_iter); } if (converted) g_byte_array_unref (converted); #undef OFFSETS_UPDATE /* Write subparameter length (remove header length to offset) */ byte_offset += !!bit_offset - 2; if (byte_offset > 256) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "Data or Text too long (max 256 bytes, %u given)", byte_offset); return FALSE; } pdu[1] = byte_offset; *parameter_offset += (2 + pdu[1]); return TRUE; } static gboolean write_bearer_data (MMSmsPart *part, guint8 *pdu, guint *absolute_offset, gpointer log_object, GError **error) { GError *inner_error = NULL; guint offset = 0; mm_obj_dbg (log_object, " writing bearer data..."); pdu[0] = PARAMETER_ID_BEARER_DATA; /* Write parameter length at the end */ offset = 2; if (!write_bearer_data_message_identifier (part, &pdu[offset], &offset, log_object, &inner_error)) mm_obj_dbg (log_object, "error writing message identifier: %s", inner_error->message); else if (!write_bearer_data_user_data (part, &pdu[offset], &offset, log_object, &inner_error)) mm_obj_dbg (log_object, "error writing user data: %s", inner_error->message); if (inner_error) { g_propagate_error (error, inner_error); g_prefix_error (error, "Error writing bearer data: "); return FALSE; } /* Write parameter length (remove header length to offset) */ offset -= 2; if (offset > 256) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "Bearer data too long (max 256 bytes, %u given)", offset); return FALSE; } pdu[1] = offset; *absolute_offset += (2 + pdu[1]); return TRUE; } guint8 * mm_sms_part_cdma_get_submit_pdu (MMSmsPart *part, guint *out_pdulen, gpointer log_object, GError **error) { GError *inner_error = NULL; guint offset = 0; guint8 *pdu; g_return_val_if_fail (mm_sms_part_get_number (part) != NULL, NULL); g_return_val_if_fail (mm_sms_part_get_text (part) != NULL || mm_sms_part_get_data (part) != NULL, NULL); if (mm_sms_part_get_pdu_type (part) != MM_SMS_PDU_TYPE_CDMA_SUBMIT) { g_set_error (error, MM_MESSAGE_ERROR, MM_MESSAGE_ERROR_INVALID_PDU_PARAMETER, "Invalid PDU type to generate a 'submit' PDU: '%s'", mm_sms_pdu_type_get_string (mm_sms_part_get_pdu_type (part))); return NULL; } mm_obj_dbg (log_object, "creating PDU for part..."); /* Current max size estimations: * Message type: 1 byte * Teleservice ID: 5 bytes * Destination address: 2 + 256 bytes * Bearer data: 2 + 256 bytes */ pdu = g_malloc0 (1024); /* First byte: SMS message type */ pdu[offset++] = MESSAGE_TYPE_POINT_TO_POINT; if (!write_teleservice_id (part, &pdu[offset], &offset, log_object, &inner_error)) mm_obj_dbg (log_object, "error writing teleservice ID: %s", inner_error->message); else if (!write_destination_address (part, &pdu[offset], &offset, log_object, &inner_error)) mm_obj_dbg (log_object, "error writing destination address: %s", inner_error->message); else if (!write_bearer_data (part, &pdu[offset], &offset, log_object, &inner_error)) mm_obj_dbg (log_object, "error writing bearer data: %s", inner_error->message); if (inner_error) { g_propagate_error (error, inner_error); g_prefix_error (error, "Cannot create CDMA SMS part: "); g_free (pdu); return NULL; } *out_pdulen = offset; return pdu; } ModemManager-1.23.4-dev/src/mm-sms-part-cdma.h000066400000000000000000000033011456466623000207410ustar00rootroot00000000000000/* -*- 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) 2013 Google, Inc. */ #ifndef MM_SMS_PART_CDMA_H #define MM_SMS_PART_CDMA_H #include #include #include "mm-sms-part.h" MMSmsPart *mm_sms_part_cdma_new_from_pdu (guint index, const gchar *hexpdu, gpointer log_object, GError **error); MMSmsPart *mm_sms_part_cdma_new_from_binary_pdu (guint index, const guint8 *pdu, gsize pdu_len, gpointer log_object, GError **error); guint8 *mm_sms_part_cdma_get_submit_pdu (MMSmsPart *part, guint *out_pdulen, gpointer log_object, GError **error); #endif /* MM_SMS_PART_CDMA_H */ ModemManager-1.23.4-dev/src/mm-sms-part.c000066400000000000000000000133711456466623000200420ustar00rootroot00000000000000/* -*- 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) 2011 - 2012 Red Hat, Inc. * Copyright (C) 2012 Google, Inc. */ #include #include #include #include #define _LIBMM_INSIDE_MM #include #include "mm-sms-part.h" #include "mm-charsets.h" #include "mm-log.h" struct _MMSmsPart { guint index; MMSmsPduType pdu_type; gchar *smsc; gchar *timestamp; gchar *discharge_timestamp; gchar *number; gchar *text; MMSmsEncoding encoding; GByteArray *data; gint class; guint validity_relative; gboolean delivery_report_request; guint message_reference; guint message_id; /* NOT a MMSmsDeliveryState, which just includes the known values */ guint delivery_state; gboolean should_concat; guint concat_reference; guint concat_max; guint concat_sequence; /* CDMA specific */ MMSmsCdmaTeleserviceId cdma_teleservice_id; MMSmsCdmaServiceCategory cdma_service_category; }; void mm_sms_part_free (MMSmsPart *self) { g_free (self->discharge_timestamp); g_free (self->timestamp); g_free (self->smsc); g_free (self->number); g_free (self->text); if (self->data) g_byte_array_unref (self->data); g_slice_free (MMSmsPart, self); } #define PART_GET_FUNC(type, name) \ type \ mm_sms_part_get_##name (MMSmsPart *self) \ { \ return self->name; \ } #define PART_SET_FUNC(type, name) \ void \ mm_sms_part_set_##name (MMSmsPart *self, \ type value) \ { \ self->name = value; \ } #define PART_SET_TAKE_STR_FUNC(name) \ void \ mm_sms_part_set_##name (MMSmsPart *self, \ const gchar *value) \ { \ g_free (self->name); \ self->name = g_strdup (value); \ } \ \ void \ mm_sms_part_take_##name (MMSmsPart *self, \ gchar *value) \ { \ g_free (self->name); \ self->name = value; \ } PART_GET_FUNC (guint, index) PART_SET_FUNC (guint, index) PART_GET_FUNC (MMSmsPduType, pdu_type) PART_SET_FUNC (MMSmsPduType, pdu_type) PART_GET_FUNC (const gchar *, smsc) PART_SET_TAKE_STR_FUNC (smsc) PART_GET_FUNC (const gchar *, number) PART_SET_TAKE_STR_FUNC (number) PART_GET_FUNC (const gchar *, timestamp) PART_SET_TAKE_STR_FUNC (timestamp) PART_GET_FUNC (const gchar *, discharge_timestamp) PART_SET_TAKE_STR_FUNC (discharge_timestamp) PART_GET_FUNC (guint, concat_max) PART_SET_FUNC (guint, concat_max) PART_GET_FUNC (guint, concat_sequence) PART_SET_FUNC (guint, concat_sequence) PART_GET_FUNC (const gchar *, text) PART_SET_TAKE_STR_FUNC (text) PART_GET_FUNC (MMSmsEncoding, encoding) PART_SET_FUNC (MMSmsEncoding, encoding) PART_GET_FUNC (gint, class) PART_SET_FUNC (gint, class) PART_GET_FUNC (guint, validity_relative) PART_SET_FUNC (guint, validity_relative) PART_GET_FUNC (gboolean, delivery_report_request) PART_SET_FUNC (gboolean, delivery_report_request) PART_GET_FUNC (guint, message_id) PART_SET_FUNC (guint, message_id) PART_GET_FUNC (guint, message_reference) PART_SET_FUNC (guint, message_reference) PART_GET_FUNC (guint, delivery_state) PART_SET_FUNC (guint, delivery_state) PART_GET_FUNC (guint, concat_reference) void mm_sms_part_set_concat_reference (MMSmsPart *self, guint value) { self->should_concat = TRUE; self->concat_reference = value; } PART_GET_FUNC (const GByteArray *, data) void mm_sms_part_set_data (MMSmsPart *self, GByteArray *value) { if (self->data) g_byte_array_unref (self->data); self->data = (value ? g_byte_array_ref (value) : NULL); } void mm_sms_part_take_data (MMSmsPart *self, GByteArray *value) { if (self->data) g_byte_array_unref (self->data); self->data = value; } gboolean mm_sms_part_should_concat (MMSmsPart *self) { return self->should_concat; } PART_GET_FUNC (MMSmsCdmaTeleserviceId, cdma_teleservice_id) PART_SET_FUNC (MMSmsCdmaTeleserviceId, cdma_teleservice_id) PART_GET_FUNC (MMSmsCdmaServiceCategory, cdma_service_category) PART_SET_FUNC (MMSmsCdmaServiceCategory, cdma_service_category) MMSmsPart * mm_sms_part_new (guint index, MMSmsPduType pdu_type) { MMSmsPart *sms_part; sms_part = g_slice_new0 (MMSmsPart); sms_part->index = index; sms_part->pdu_type = pdu_type; sms_part->encoding = MM_SMS_ENCODING_UNKNOWN; sms_part->delivery_state = MM_SMS_DELIVERY_STATE_UNKNOWN; sms_part->cdma_teleservice_id = MM_SMS_CDMA_TELESERVICE_ID_UNKNOWN; sms_part->cdma_service_category = MM_SMS_CDMA_SERVICE_CATEGORY_UNKNOWN; sms_part->class = -1; return sms_part; } ModemManager-1.23.4-dev/src/mm-sms-part.h000066400000000000000000000175661456466623000200610ustar00rootroot00000000000000/* -*- 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 - 2012 Red Hat, Inc. * Copyright (C) 2012 Google, Inc. */ #ifndef MM_SMS_PART_H #define MM_SMS_PART_H #include #include /* Despite 3GPP TS 23.038 specifies that Unicode SMS messages are * encoded in UCS-2, UTF-16 encoding is commonly used instead on many * modern platforms to allow encoding code points that fall outside the * Basic Multilingual Plane (BMP), such as Emoji. Most of the UCS-2 * code points are identical to their equivalent UTF-16 code points. * In UTF-16, non-BMP code points are encoded in a pair of surrogate * code points (i.e. a high surrogate in 0xD800..0xDBFF, followed by a * low surrogate in 0xDC00..0xDFFF). An isolated surrogate code point * has no general interpretation in UTF-16, but could be a valid * (though unmapped) code point in UCS-2. * * The current implementation in ModemManager just assumes that whenever * possible (i.e. when parsing received PDUs or when creating submit * PDUs) UTF-16 will be used instead of plain UCS-2 (even if the PDUs * report the encoding as UCS-2). */ typedef enum { /*< underscore_name=mm_sms_encoding >*/ MM_SMS_ENCODING_UNKNOWN = 0x0, MM_SMS_ENCODING_GSM7, MM_SMS_ENCODING_8BIT, MM_SMS_ENCODING_UCS2, } MMSmsEncoding; typedef struct _MMSmsPart MMSmsPart; #define SMS_PART_INVALID_INDEX G_MAXUINT #define MM_SMS_PART_IS_3GPP(part) \ (mm_sms_part_get_pdu_type (part) >= MM_SMS_PDU_TYPE_DELIVER && \ mm_sms_part_get_pdu_type (part) <= MM_SMS_PDU_TYPE_STATUS_REPORT) #define MM_SMS_PART_IS_CDMA(part) \ (mm_sms_part_get_pdu_type (part) >= MM_SMS_PDU_TYPE_CDMA_DELIVER && \ mm_sms_part_get_pdu_type (part) <= MM_SMS_PDU_TYPE_CDMA_READ_ACKNOWLEDGEMENT) MMSmsPart *mm_sms_part_new (guint index, MMSmsPduType type); void mm_sms_part_free (MMSmsPart *part); G_DEFINE_AUTOPTR_CLEANUP_FUNC (MMSmsPart, mm_sms_part_free) guint mm_sms_part_get_index (MMSmsPart *part); void mm_sms_part_set_index (MMSmsPart *part, guint index); MMSmsPduType mm_sms_part_get_pdu_type (MMSmsPart *part); void mm_sms_part_set_pdu_type (MMSmsPart *part, MMSmsPduType type); const gchar *mm_sms_part_get_smsc (MMSmsPart *part); void mm_sms_part_set_smsc (MMSmsPart *part, const gchar *smsc); void mm_sms_part_take_smsc (MMSmsPart *part, gchar *smsc); const gchar *mm_sms_part_get_number (MMSmsPart *part); void mm_sms_part_set_number (MMSmsPart *part, const gchar *number); void mm_sms_part_take_number (MMSmsPart *part, gchar *number); const gchar *mm_sms_part_get_timestamp (MMSmsPart *part); void mm_sms_part_set_timestamp (MMSmsPart *part, const gchar *timestamp); void mm_sms_part_take_timestamp (MMSmsPart *part, gchar *timestamp); const gchar *mm_sms_part_get_discharge_timestamp (MMSmsPart *part); void mm_sms_part_set_discharge_timestamp (MMSmsPart *part, const gchar *timestamp); void mm_sms_part_take_discharge_timestamp (MMSmsPart *part, gchar *timestamp); const gchar *mm_sms_part_get_text (MMSmsPart *part); void mm_sms_part_set_text (MMSmsPart *part, const gchar *text); void mm_sms_part_take_text (MMSmsPart *part, gchar *text); const GByteArray *mm_sms_part_get_data (MMSmsPart *part); void mm_sms_part_set_data (MMSmsPart *part, GByteArray *data); void mm_sms_part_take_data (MMSmsPart *part, GByteArray *data); MMSmsEncoding mm_sms_part_get_encoding (MMSmsPart *part); void mm_sms_part_set_encoding (MMSmsPart *part, MMSmsEncoding encoding); gint mm_sms_part_get_class (MMSmsPart *part); void mm_sms_part_set_class (MMSmsPart *part, gint class); guint mm_sms_part_get_validity_relative (MMSmsPart *part); void mm_sms_part_set_validity_relative (MMSmsPart *part, guint validity); guint mm_sms_part_get_delivery_state (MMSmsPart *part); void mm_sms_part_set_delivery_state (MMSmsPart *part, guint delivery_state); guint mm_sms_part_get_message_reference (MMSmsPart *part); void mm_sms_part_set_message_reference (MMSmsPart *part, guint message_reference); gboolean mm_sms_part_get_delivery_report_request (MMSmsPart *part); void mm_sms_part_set_delivery_report_request (MMSmsPart *part, gboolean delivery_report_request); guint mm_sms_part_get_message_id (MMSmsPart *part); void mm_sms_part_set_message_id (MMSmsPart *part, guint message_id); guint mm_sms_part_get_concat_reference (MMSmsPart *part); void mm_sms_part_set_concat_reference (MMSmsPart *part, guint concat_reference); guint mm_sms_part_get_concat_max (MMSmsPart *part); void mm_sms_part_set_concat_max (MMSmsPart *part, guint concat_max); guint mm_sms_part_get_concat_sequence (MMSmsPart *part); void mm_sms_part_set_concat_sequence (MMSmsPart *part, guint concat_sequence); gboolean mm_sms_part_should_concat (MMSmsPart *part); /* CDMA specific */ MMSmsCdmaTeleserviceId mm_sms_part_get_cdma_teleservice_id (MMSmsPart *part); void mm_sms_part_set_cdma_teleservice_id (MMSmsPart *part, MMSmsCdmaTeleserviceId cdma_teleservice_id); MMSmsCdmaServiceCategory mm_sms_part_get_cdma_service_category (MMSmsPart *part); void mm_sms_part_set_cdma_service_category (MMSmsPart *part, MMSmsCdmaServiceCategory cdma_service_category); #endif /* MM_SMS_PART_H */ ModemManager-1.23.4-dev/src/mm-sms-qmi.c000066400000000000000000000621501456466623000176610ustar00rootroot00000000000000/* -*- 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) 2012 Google, Inc. */ #include #include #include #include #include #include #include #define _LIBMM_INSIDE_MM #include #include "mm-broadband-modem-qmi.h" #include "mm-modem-helpers-qmi.h" #include "mm-iface-modem.h" #include "mm-iface-modem-messaging.h" #include "mm-sms-qmi.h" #include "mm-base-modem.h" #include "mm-sms-part-3gpp.h" #include "mm-sms-part-cdma.h" #include "mm-log-object.h" G_DEFINE_TYPE (MMSmsQmi, mm_sms_qmi, MM_TYPE_BASE_SMS) /*****************************************************************************/ static gboolean ensure_qmi_client (MMSmsQmi *self, QmiService service, QmiClient **o_client, GAsyncReadyCallback callback, gpointer user_data) { MMBaseModem *modem = NULL; QmiClient *client; MMPortQmi *port; g_object_get (self, MM_BASE_SMS_MODEM, &modem, NULL); g_assert (MM_IS_BASE_MODEM (modem)); port = mm_broadband_modem_qmi_peek_port_qmi (MM_BROADBAND_MODEM_QMI (modem)); g_object_unref (modem); if (!port) { g_task_report_new_error (self, callback, user_data, ensure_qmi_client, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't peek QMI port"); return FALSE; } client = mm_port_qmi_peek_client (port, service, MM_PORT_QMI_FLAG_DEFAULT); if (!client) { g_task_report_new_error (self, callback, user_data, ensure_qmi_client, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't peek client for service '%s'", qmi_service_get_string (service)); return FALSE; } *o_client = client; return TRUE; } /*****************************************************************************/ static gboolean check_sms_type_support (MMSmsQmi *self, MMBaseModem *modem, MMSmsPart *first_part, GError **error) { if (MM_SMS_PART_IS_3GPP (first_part) && !mm_iface_modem_is_3gpp (MM_IFACE_MODEM (modem))) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "Non-3GPP modem doesn't support 3GPP SMS"); return FALSE; } if (MM_SMS_PART_IS_CDMA (first_part) && !mm_iface_modem_is_cdma (MM_IFACE_MODEM (modem))) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "Non-CDMA modem doesn't support CDMA SMS"); return FALSE; } return TRUE; } /*****************************************************************************/ /* Store the SMS */ typedef struct { MMBaseModem *modem; QmiClientWms *client; MMSmsStorage storage; GList *current; } SmsStoreContext; static void sms_store_context_free (SmsStoreContext *ctx) { g_object_unref (ctx->client); g_object_unref (ctx->modem); g_slice_free (SmsStoreContext, ctx); } static gboolean sms_store_finish (MMBaseSms *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void sms_store_next_part (GTask *task); static void store_ready (QmiClientWms *client, GAsyncResult *res, GTask *task) { MMBaseSms *self; SmsStoreContext *ctx; QmiMessageWmsRawWriteOutput *output = NULL; GError *error = NULL; GList *parts; guint32 idx; output = qmi_client_wms_raw_write_finish (client, res, &error); if (!output) { g_prefix_error (&error, "QMI operation failed: "); g_task_return_error (task, error); g_object_unref (task); return; } if (!qmi_message_wms_raw_write_output_get_result (output, &error)) { qmi_message_wms_raw_write_output_unref (output); g_prefix_error (&error, "Couldn't write SMS part: "); g_task_return_error (task, error); g_object_unref (task); return; } self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); qmi_message_wms_raw_write_output_get_memory_index ( output, &idx, NULL); qmi_message_wms_raw_write_output_unref (output); /* Set the index in the part we hold */ parts = mm_base_sms_get_parts (self); mm_sms_part_set_index ((MMSmsPart *)parts->data, (guint)idx); /* Go on with next one */ ctx->current = g_list_next (ctx->current); sms_store_next_part (task); } static void sms_store_next_part (GTask *task) { MMSmsQmi *self; SmsStoreContext *ctx; QmiMessageWmsRawWriteInput *input; guint8 *pdu = NULL; guint pdulen = 0; guint msgstart = 0; GArray *array; GError *error = NULL; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); if (!ctx->current) { /* Done we are */ g_task_return_boolean (task, TRUE); g_object_unref (task); return; } /* Get PDU */ if (MM_SMS_PART_IS_3GPP ((MMSmsPart *)ctx->current->data)) pdu = mm_sms_part_3gpp_get_submit_pdu ((MMSmsPart *)ctx->current->data, &pdulen, &msgstart, self, &error); else if (MM_SMS_PART_IS_CDMA ((MMSmsPart *)ctx->current->data)) pdu = mm_sms_part_cdma_get_submit_pdu ((MMSmsPart *)ctx->current->data, &pdulen, self, &error); if (!pdu) { if (error) g_task_return_error (task, error); else g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Unknown or unsupported PDU type in SMS part: %s", mm_sms_pdu_type_get_string ( mm_sms_part_get_pdu_type ( (MMSmsPart *)ctx->current->data))); g_object_unref (task); return; } /* Convert to GArray */ array = g_array_append_vals (g_array_sized_new (FALSE, FALSE, sizeof (guint8), pdulen), pdu, pdulen); g_free (pdu); /* Create input bundle and send the QMI request */ input = qmi_message_wms_raw_write_input_new (); qmi_message_wms_raw_write_input_set_raw_message_data ( input, mm_sms_storage_to_qmi_storage_type (ctx->storage), (MM_SMS_PART_IS_3GPP ((MMSmsPart *)ctx->current->data) ? QMI_WMS_MESSAGE_FORMAT_GSM_WCDMA_POINT_TO_POINT : QMI_WMS_MESSAGE_FORMAT_CDMA), array, NULL); qmi_client_wms_raw_write (ctx->client, input, 5, NULL, (GAsyncReadyCallback)store_ready, task); qmi_message_wms_raw_write_input_unref (input); g_array_unref (array); } static void sms_store (MMBaseSms *self, MMSmsStorage storage, GAsyncReadyCallback callback, gpointer user_data) { SmsStoreContext *ctx; QmiClient *client = NULL; GError *error = NULL; GTask *task; /* Ensure WMS client */ if (!ensure_qmi_client (MM_SMS_QMI (self), QMI_SERVICE_WMS, &client, callback, user_data)) return; /* Setup the context */ ctx = g_slice_new0 (SmsStoreContext); ctx->client = QMI_CLIENT_WMS (g_object_ref (client)); ctx->storage = storage; g_object_get (self, MM_BASE_SMS_MODEM, &ctx->modem, NULL); ctx->current = mm_base_sms_get_parts (self); task = g_task_new (self, NULL, callback, user_data); g_task_set_task_data (task, ctx, (GDestroyNotify)sms_store_context_free); /* Check whether we support the given SMS type */ if (!check_sms_type_support (MM_SMS_QMI (self), ctx->modem, (MMSmsPart *)ctx->current->data, &error)) { g_task_return_error (task, error); g_object_unref (task); return; } /* Go on */ sms_store_next_part (task); } /*****************************************************************************/ /* Send the SMS */ typedef struct { MMBaseModem *modem; QmiClientWms *client; gboolean from_storage; GList *current; } SmsSendContext; static void sms_send_context_free (SmsSendContext *ctx) { g_object_unref (ctx->client); g_object_unref (ctx->modem); g_slice_free (SmsSendContext, ctx); } static gboolean sms_send_finish (MMBaseSms *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void sms_send_next_part (GTask *task); static void send_generic_ready (QmiClientWms *client, GAsyncResult *res, GTask *task) { MMSmsQmi *self; SmsSendContext *ctx; QmiMessageWmsRawSendOutput *output = NULL; GError *error = NULL; guint16 message_id; self = g_task_get_source_object (task); output = qmi_client_wms_raw_send_finish (client, res, &error); if (!output) { g_prefix_error (&error, "QMI operation failed: "); g_task_return_error (task, error); g_object_unref (task); return; } if (!qmi_message_wms_raw_send_output_get_result (output, &error)) { QmiWmsGsmUmtsRpCause rp_cause; QmiWmsGsmUmtsTpCause tp_cause; if (qmi_message_wms_raw_send_output_get_gsm_wcdma_cause_info ( output, &rp_cause, &tp_cause, NULL)) { mm_obj_warn (self, "couldn't send SMS; RP cause (%u): %s; TP cause (%u): %s", rp_cause, qmi_wms_gsm_umts_rp_cause_get_string (rp_cause), tp_cause, qmi_wms_gsm_umts_tp_cause_get_string (tp_cause)); } qmi_message_wms_raw_send_output_unref (output); g_prefix_error (&error, "Couldn't write SMS part: "); g_task_return_error (task, error); g_object_unref (task); return; } ctx = g_task_get_task_data (task); if (qmi_message_wms_raw_send_output_get_message_id (output, &message_id, NULL)) mm_sms_part_set_message_reference ((MMSmsPart *)ctx->current->data, message_id); qmi_message_wms_raw_send_output_unref (output); /* Go on with next part */ ctx->current = g_list_next (ctx->current); sms_send_next_part (task); } static void sms_send_generic (GTask *task) { MMSmsQmi *self; SmsSendContext *ctx; QmiMessageWmsRawSendInput *input; guint8 *pdu = NULL; guint pdulen = 0; guint msgstart = 0; GArray *array; GError *error = NULL; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); /* Get PDU */ if (MM_SMS_PART_IS_3GPP ((MMSmsPart *)ctx->current->data)) pdu = mm_sms_part_3gpp_get_submit_pdu ((MMSmsPart *)ctx->current->data, &pdulen, &msgstart, self, &error); else if (MM_SMS_PART_IS_CDMA ((MMSmsPart *)ctx->current->data)) pdu = mm_sms_part_cdma_get_submit_pdu ((MMSmsPart *)ctx->current->data, &pdulen, self, &error); if (!pdu) { if (error) g_task_return_error (task, error); else g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Unknown or unsupported PDU type in SMS part: %s", mm_sms_pdu_type_get_string ( mm_sms_part_get_pdu_type ( (MMSmsPart *)ctx->current->data))); g_object_unref (task); return; } /* Convert to GArray */ array = g_array_append_vals (g_array_sized_new (FALSE, FALSE, sizeof (guint8), pdulen), pdu, pdulen); g_free (pdu); input = qmi_message_wms_raw_send_input_new (); qmi_message_wms_raw_send_input_set_raw_message_data ( input, (MM_SMS_PART_IS_3GPP ((MMSmsPart *)ctx->current->data) ? QMI_WMS_MESSAGE_FORMAT_GSM_WCDMA_POINT_TO_POINT : QMI_WMS_MESSAGE_FORMAT_CDMA), array, NULL); qmi_client_wms_raw_send (ctx->client, input, MM_BASE_SMS_DEFAULT_SEND_TIMEOUT, NULL, (GAsyncReadyCallback)send_generic_ready, task); qmi_message_wms_raw_send_input_unref (input); g_array_unref (array); } static void send_from_storage_ready (QmiClientWms *client, GAsyncResult *res, GTask *task) { MMSmsQmi *self; SmsSendContext *ctx; QmiMessageWmsSendFromMemoryStorageOutput *output = NULL; GError *error = NULL; guint16 message_id; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); output = qmi_client_wms_send_from_memory_storage_finish (client, res, &error); if (!output) { if (g_error_matches (error, QMI_CORE_ERROR, QMI_CORE_ERROR_UNSUPPORTED)) { mm_obj_dbg (self, "couldn't send SMS from storage: %s; trying generic send...", error->message); g_error_free (error); ctx->from_storage = FALSE; sms_send_next_part (task); return; } /* Fatal error */ g_prefix_error (&error, "QMI operation failed: "); g_task_return_error (task, error); g_object_unref (task); return; } if (!qmi_message_wms_send_from_memory_storage_output_get_result (output, &error)) { if (g_error_matches (error, QMI_PROTOCOL_ERROR, QMI_PROTOCOL_ERROR_INVALID_QMI_COMMAND)) { mm_obj_dbg (self, "couldn't send SMS from storage: %s; trying generic send...", error->message); g_error_free (error); ctx->from_storage = FALSE; sms_send_next_part (task); } else { QmiWmsGsmUmtsRpCause rp_cause; QmiWmsGsmUmtsTpCause tp_cause; QmiWmsCdmaCauseCode cdma_cause_code; QmiWmsCdmaErrorClass cdma_error_class; if (qmi_message_wms_send_from_memory_storage_output_get_gsm_wcdma_cause_info ( output, &rp_cause, &tp_cause, NULL)) { mm_obj_warn (self, "couldn't send SMS; RP cause (%u): %s; TP cause (%u): %s", rp_cause, qmi_wms_gsm_umts_rp_cause_get_string (rp_cause), tp_cause, qmi_wms_gsm_umts_tp_cause_get_string (tp_cause)); } if (qmi_message_wms_send_from_memory_storage_output_get_cdma_cause_code ( output, &cdma_cause_code, NULL)) { mm_obj_warn (self, "couldn't send SMS; cause code (%u): %s", cdma_cause_code, qmi_wms_cdma_cause_code_get_string (cdma_cause_code)); } if (qmi_message_wms_send_from_memory_storage_output_get_cdma_error_class ( output, &cdma_error_class, NULL)) { mm_obj_warn (self, "couldn't send SMS; error class (%u): %s", cdma_error_class, qmi_wms_cdma_error_class_get_string (cdma_error_class)); } g_prefix_error (&error, "Couldn't write SMS part: "); g_task_return_error (task, error); g_object_unref (task); } qmi_message_wms_send_from_memory_storage_output_unref (output); return; } if (qmi_message_wms_send_from_memory_storage_output_get_message_id (output, &message_id, NULL)) mm_sms_part_set_message_reference ((MMSmsPart *)ctx->current->data, message_id); qmi_message_wms_send_from_memory_storage_output_unref (output); /* Go on with next part */ ctx->current = g_list_next (ctx->current); sms_send_next_part (task); } static void sms_send_from_storage (GTask *task) { MMBaseSms *self; SmsSendContext *ctx; QmiMessageWmsSendFromMemoryStorageInput *input; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); input = qmi_message_wms_send_from_memory_storage_input_new (); qmi_message_wms_send_from_memory_storage_input_set_information ( input, mm_sms_storage_to_qmi_storage_type (mm_base_sms_get_storage (self)), mm_sms_part_get_index ((MMSmsPart *)ctx->current->data), (MM_SMS_PART_IS_3GPP ((MMSmsPart *)ctx->current->data) ? QMI_WMS_MESSAGE_MODE_GSM_WCDMA : QMI_WMS_MESSAGE_MODE_CDMA), NULL); qmi_client_wms_send_from_memory_storage ( ctx->client, input, MM_BASE_SMS_DEFAULT_SEND_TIMEOUT, NULL, (GAsyncReadyCallback)send_from_storage_ready, task); qmi_message_wms_send_from_memory_storage_input_unref (input); } static void sms_send_next_part (GTask *task) { SmsSendContext *ctx; ctx = g_task_get_task_data (task); if (!ctx->current) { /* Done we are */ g_task_return_boolean (task, TRUE); g_object_unref (task); return; } /* Send from storage? */ if (ctx->from_storage) sms_send_from_storage (task); else sms_send_generic (task); } static void sms_send (MMBaseSms *self, GAsyncReadyCallback callback, gpointer user_data) { SmsSendContext *ctx; QmiClient *client = NULL; GError *error = NULL; GTask *task; /* Ensure WMS client */ if (!ensure_qmi_client (MM_SMS_QMI (self), QMI_SERVICE_WMS, &client, callback, user_data)) return; /* Setup the context */ ctx = g_slice_new0 (SmsSendContext); ctx->client = QMI_CLIENT_WMS (g_object_ref (client)); g_object_get (self, MM_BASE_SMS_MODEM, &ctx->modem, NULL); /* If the SMS is STORED, try to send from storage */ ctx->from_storage = (mm_base_sms_get_storage (self) != MM_SMS_STORAGE_UNKNOWN); ctx->current = mm_base_sms_get_parts (self); task = g_task_new (self, NULL, callback, user_data); g_task_set_task_data (task, ctx, (GDestroyNotify)sms_send_context_free); /* Check whether we support the given SMS type */ if (!check_sms_type_support (MM_SMS_QMI (self), ctx->modem, (MMSmsPart *)ctx->current->data, &error)) { g_task_return_error (task, error); g_object_unref (task); return; } sms_send_next_part (task); } /*****************************************************************************/ typedef struct { MMBaseModem *modem; QmiClientWms *client; GList *current; guint n_failed; } SmsDeletePartsContext; static void sms_delete_parts_context_free (SmsDeletePartsContext *ctx) { g_object_unref (ctx->client); g_object_unref (ctx->modem); g_slice_free (SmsDeletePartsContext, ctx); } static gboolean sms_delete_finish (MMBaseSms *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void delete_next_part (GTask *task); static void delete_part_ready (QmiClientWms *client, GAsyncResult *res, GTask *task) { MMSmsQmi *self; SmsDeletePartsContext *ctx; QmiMessageWmsDeleteOutput *output = NULL; GError *error = NULL; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); output = qmi_client_wms_delete_finish (client, res, &error); if (!output) { ctx->n_failed++; mm_obj_dbg (self, "QMI operation failed: couldn't delete SMS part with index %u: %s", mm_sms_part_get_index ((MMSmsPart *)ctx->current->data), error->message); g_error_free (error); } else if (!qmi_message_wms_delete_output_get_result (output, &error)) { ctx->n_failed++; mm_obj_dbg (self, "couldn't delete SMS part with index %u: %s", mm_sms_part_get_index ((MMSmsPart *)ctx->current->data), error->message); g_error_free (error); } if (output) qmi_message_wms_delete_output_unref (output); /* We reset the index, as there is no longer that part */ mm_sms_part_set_index ((MMSmsPart *)ctx->current->data, SMS_PART_INVALID_INDEX); ctx->current = g_list_next (ctx->current); delete_next_part (task); } static void delete_next_part (GTask *task) { MMBaseSms *self; SmsDeletePartsContext *ctx; QmiMessageWmsDeleteInput *input; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); /* Skip non-stored parts */ while (ctx->current && mm_sms_part_get_index ((MMSmsPart *)ctx->current->data) == SMS_PART_INVALID_INDEX) ctx->current = g_list_next (ctx->current); /* If all removed, we're done */ if (!ctx->current) { if (ctx->n_failed > 0) g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't delete %u parts from this SMS", ctx->n_failed); else g_task_return_boolean (task, TRUE); g_object_unref (task); return; } input = qmi_message_wms_delete_input_new (); qmi_message_wms_delete_input_set_memory_storage ( input, mm_sms_storage_to_qmi_storage_type (mm_base_sms_get_storage (self)), NULL); qmi_message_wms_delete_input_set_memory_index ( input, (guint32)mm_sms_part_get_index ((MMSmsPart *)ctx->current->data), NULL); qmi_message_wms_delete_input_set_message_mode ( input, (MM_SMS_PART_IS_3GPP ((MMSmsPart *)ctx->current->data) ? QMI_WMS_MESSAGE_MODE_GSM_WCDMA: QMI_WMS_MESSAGE_MODE_CDMA), NULL); qmi_client_wms_delete (ctx->client, input, 5, NULL, (GAsyncReadyCallback)delete_part_ready, task); qmi_message_wms_delete_input_unref (input); } static void sms_delete (MMBaseSms *self, GAsyncReadyCallback callback, gpointer user_data) { SmsDeletePartsContext *ctx; QmiClient *client = NULL; GTask *task; /* Ensure WMS client */ if (!ensure_qmi_client (MM_SMS_QMI (self), QMI_SERVICE_WMS, &client, callback, user_data)) return; ctx = g_slice_new0 (SmsDeletePartsContext); ctx->client = QMI_CLIENT_WMS (g_object_ref (client)); g_object_get (self, MM_BASE_SMS_MODEM, &ctx->modem, NULL); task = g_task_new (self, NULL, callback, user_data); g_task_set_task_data (task, ctx, (GDestroyNotify)sms_delete_parts_context_free); /* Go on deleting parts */ ctx->current = mm_base_sms_get_parts (self); delete_next_part (task); } /*****************************************************************************/ MMBaseSms * mm_sms_qmi_new (MMBaseModem *modem) { return MM_BASE_SMS (g_object_new (MM_TYPE_SMS_QMI, MM_BASE_SMS_MODEM, modem, NULL)); } static void mm_sms_qmi_init (MMSmsQmi *self) { } static void mm_sms_qmi_class_init (MMSmsQmiClass *klass) { MMBaseSmsClass *base_sms_class = MM_BASE_SMS_CLASS (klass); base_sms_class->store = sms_store; base_sms_class->store_finish = sms_store_finish; base_sms_class->send = sms_send; base_sms_class->send_finish = sms_send_finish; base_sms_class->delete = sms_delete; base_sms_class->delete_finish = sms_delete_finish; } ModemManager-1.23.4-dev/src/mm-sms-qmi.h000066400000000000000000000033131456466623000176620ustar00rootroot00000000000000/* -*- 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: * * Author: Aleksander Morgado * * Copyright (C) 2012 Google, Inc. */ #ifndef MM_SMS_QMI_H #define MM_SMS_QMI_H #include #include #define _LIBMM_INSIDE_MM #include #include "mm-base-sms.h" #define MM_TYPE_SMS_QMI (mm_sms_qmi_get_type ()) #define MM_SMS_QMI(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_SMS_QMI, MMSmsQmi)) #define MM_SMS_QMI_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_SMS_QMI, MMSmsQmiClass)) #define MM_IS_SMS_QMI(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_SMS_QMI)) #define MM_IS_SMS_QMI_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_SMS_QMI)) #define MM_SMS_QMI_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_SMS_QMI, MMSmsQmiClass)) typedef struct _MMSmsQmi MMSmsQmi; typedef struct _MMSmsQmiClass MMSmsQmiClass; struct _MMSmsQmi { MMBaseSms parent; }; struct _MMSmsQmiClass { MMBaseSmsClass parent; }; GType mm_sms_qmi_get_type (void); G_DEFINE_AUTOPTR_CLEANUP_FUNC (MMSmsQmi, g_object_unref) MMBaseSms *mm_sms_qmi_new (MMBaseModem *modem); #endif /* MM_SMS_QMI_H */ ModemManager-1.23.4-dev/src/mm-utils.h000066400000000000000000000103531456466623000174360ustar00rootroot00000000000000/* -*- 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: * * Singleton support imported from NetworkManager. * (C) Copyright 2014 Red Hat, Inc. * * GPtrArray lookup with GEqualFunc imported from GLib 2.48 */ #ifndef MM_UTILS_H #define MM_UTILS_H #include #include #include "mm-log-object.h" /*****************************************************************************/ #define MM_DEFINE_SINGLETON_INSTANCE(TYPE) \ static TYPE *singleton_instance; #define MM_DEFINE_SINGLETON_WEAK_REF(TYPE) \ static void \ _singleton_instance_weak_ref_cb (gpointer data, \ GObject *where_the_object_was) \ { \ mm_obj_dbg (singleton_instance, "singleton disposed"); \ singleton_instance = NULL; \ } \ static inline void \ mm_singleton_instance_weak_ref_register (void) \ { \ g_object_weak_ref (G_OBJECT (singleton_instance), _singleton_instance_weak_ref_cb, NULL); \ } #define MM_DEFINE_SINGLETON_DESTRUCTOR(TYPE) \ static void __attribute__((destructor)) \ _singleton_destructor (void) \ { \ if (singleton_instance) { \ if (G_OBJECT (singleton_instance)->ref_count > 1) \ mm_obj_dbg (singleton_instance, "singleton disowned"); \ g_object_unref (singleton_instance); \ } \ } /* By default, the getter will assert that the singleton will be created only once. You can * change this by redefining MM_DEFINE_SINGLETON_ALLOW_MULTIPLE. */ #ifndef MM_DEFINE_SINGLETON_ALLOW_MULTIPLE #define MM_DEFINE_SINGLETON_ALLOW_MULTIPLE FALSE #endif #define MM_DEFINE_SINGLETON_GETTER(TYPE, GETTER, GTYPE, ...) \ MM_DEFINE_SINGLETON_INSTANCE (TYPE) \ MM_DEFINE_SINGLETON_WEAK_REF (TYPE) \ TYPE * \ GETTER (void) \ { \ if (G_UNLIKELY (!singleton_instance)) { \ static char _already_created = FALSE; \ \ g_assert (!_already_created || (MM_DEFINE_SINGLETON_ALLOW_MULTIPLE)); \ _already_created = TRUE; \ singleton_instance = (g_object_new (GTYPE, ##__VA_ARGS__, NULL)); \ g_assert (singleton_instance); \ mm_singleton_instance_weak_ref_register (); \ mm_obj_dbg (singleton_instance, "singleton created"); \ } \ return singleton_instance; \ } \ MM_DEFINE_SINGLETON_DESTRUCTOR(TYPE) #endif /* MM_UTILS_H */ ModemManager-1.23.4-dev/src/plugins/000077500000000000000000000000001456466623000171755ustar00rootroot00000000000000ModemManager-1.23.4-dev/src/plugins/README.txt000066400000000000000000000100421456466623000206700ustar00rootroot00000000000000 The following list shows the relationship among the different plugins provided by default by ModemManager. For each of the plugin types, the list of modem objects created by the plugin is given. * Altair: ** MMBroadbandModemAltairLte * Anydata: ** MMBroadbandModemQmi (generic) ** MMBroadbandModemAnydata * Cinterion: ** MMBroadbandModemQmiCinterion ** MMBroadbandModemCinterion * Dell: ** MMBroadbandModemQmi (generic) ** MMBroadbandModemFoxconnT77w968 (from the Foxconn utils) ** MMBroadbandModemMbimXmm (from the XMM utils) ** MMBroadbandModemMbim (generic) ** MMBroadbandModemNovatel (from the Novatel utils) ** MMBroadbandModemSierra (from the Sierra Legacy utils) ** MMBroadbandModemTelit (from the Telit utils) ** MMBroadbandModemXmm (from the XMM utils) ** MMBroadbandModem (generic) * D-Link: ** MMBroadbandModemQmi (generic) ** MMBroadbandModem (generic) * Fibocom: ** MMBroadbandModemMbimXmm (from the XMM utils) ** MMBroadbandModemMbim (generic) ** MMBroadbandModemXmm (from the XMM utils) ** MMBroadbandModem (generic) * Foxconn: ** MMBroadbandModemQmi (generic) ** MMBroadbandModemFoxconnT77w968 ** MMBroadbandModemMbim (generic) ** MMBroadbandModem (generic) * Generic: ** MMBroadbandModemQmi (generic) ** MMBroadbandModemMbim (generic) ** MMBroadbandModem (generic) * Gosuncn: ** MMBroadbandModemQmi (generic) ** MMBroadbandModemMbim (generic) ** MMBroadbandModem (generic) * Haier: ** MMBroadbandModem (generic) * Huawei: ** MMBroadbandModemQmi (generic) ** MMBroadbandModemMbim (generic) ** MMBroadbandModemHuawei * Icera (no explicit plugin): ** MMBroadbandModemIcera * Iridium: ** MMBroadbandModemIridium * Linktop: ** MMBroadbandModemLinktop * Longcheer: ** MMBroadbandModemLongcheer * MBM: ** MMBroadbandModemMbim (generic) ** MMBroadbandModemMbm * Motorola: ** MMBroadbandModemMotorola * Mtk: ** MMBroadbandModemMtk * Nokia: ** MMBroadbandModemNokia * Nokia Icera: ** MMBroadbandModemIcera (from the Icera utils) * Novatel: ** MMBroadbandModemQmi (generic) ** MMBroadbandModemNovatel * Novatel LTE: ** MMBroadbandModemNovatelLte * Option: ** MMBroadbandModemOption * Option HSO: ** MMBroadbandModemHso * Pantech: ** MMBroadbandModemQmi (generic) ** MMBroadbandModemPantech * Quectel: ** MMBroadbandModemQmiQuectel ** MMBroadbandModemQuectel * Samsung: ** MMBroadbandModemSamsung (subclassed from the Icera utils) * Sierra Legacy: ** MMBroadbandModemSierraIcera (subclassed from the Icera utils) ** MMBroadbandModemSierra * Sierra: ** MMBroadbandModemQmi (generic) ** MMBroadbandModemMbim (generic) ** MMBroadbandModem (generic) * Simtech: ** MMBroadbandModemQmiSimtech ** MMBroadbandModemSimtech * Telit: ** MMBroadbandModemQmi (generic) ** MMBroadbandModemMbimTelit ** MMBroadbandModemTelit * Thuraya ** MMBroadbandModemThuraya * TP-Link: ** MMBroadbandModemQmi (generic) ** MMBroadbandModem (generic) * u-blox: ** MMBroadbandModemUblox * via: ** MMBroadbandModemVia * wavecom: ** MMBroadbandModemWavecom * x22x: ** MMBroadbandModemQmi (generic) ** MMBroadbandModemX22x * XMM (no explicit plugin): ** MMBroadbandModemMbimXmm ** MMBroadbandModemXmm * ZTE ** MMBroadbandModemQmi (generic) ** MMBroadbandModemMbim (generic) ** MMBroadbandModemZteIcera (subclassed from the Icera utils) ** MMBroadbandModemZte ModemManager-1.23.4-dev/src/plugins/altair/000077500000000000000000000000001456466623000204515ustar00rootroot00000000000000ModemManager-1.23.4-dev/src/plugins/altair/mm-broadband-bearer-altair-lte.c000066400000000000000000000303701456466623000264350ustar00rootroot00000000000000/* -*- 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) 2013 Altair Semiconductor * * Author: Ori Inbar */ #include #include #include #include #include #include #include #define _LIBMM_INSIDE_MM #include #include "mm-base-modem-at.h" #include "mm-broadband-bearer-altair-lte.h" #include "mm-iface-modem-3gpp.h" #include "mm-log.h" #include "mm-modem-helpers.h" #define CONNECTION_CHECK_TIMEOUT_SEC 5 #define STATCM_TAG "%STATCM:" G_DEFINE_TYPE (MMBroadbandBearerAltairLte, mm_broadband_bearer_altair_lte, MM_TYPE_BROADBAND_BEARER); /*****************************************************************************/ /* 3GPP Connect sequence */ typedef struct { MMBaseModem *modem; MMPortSerialAt *primary; MMPort *data; } DetailedConnectContext; static void detailed_connect_context_free (DetailedConnectContext *ctx) { g_object_unref (ctx->data); g_object_unref (ctx->primary); g_object_unref (ctx->modem); g_free (ctx); } static MMBearerConnectResult * connect_3gpp_finish (MMBroadbandBearer *self, GAsyncResult *res, GError **error) { return g_task_propagate_pointer (G_TASK (res), error); } static void connect_3gpp_connect_ready (MMBaseModem *modem, GAsyncResult *res, GTask *task) { DetailedConnectContext *ctx; const gchar *result; GError *error = NULL; MMBearerIpConfig *config; result = mm_base_modem_at_command_full_finish (modem, res, &error); if (!result) { g_task_return_error (task, error); g_object_unref (task); return; } ctx = g_task_get_task_data (task); config = mm_bearer_ip_config_new (); mm_bearer_ip_config_set_method (config, MM_BEARER_IP_METHOD_DHCP); /* Set operation result */ g_task_return_pointer ( task, mm_bearer_connect_result_new (ctx->data, config, config), (GDestroyNotify)mm_bearer_connect_result_unref); g_object_unref (task); g_object_unref (config); } static void connect_3gpp_apnsettings_ready (MMBaseModem *modem, GAsyncResult *res, GTask *task) { DetailedConnectContext *ctx; const gchar *result; GError *error = NULL; result = mm_base_modem_at_command_full_finish (modem, res, &error); if (!result) { g_prefix_error (&error, "setting APN failed: "); g_task_return_error (task, error); g_object_unref (task); return; } ctx = g_task_get_task_data (task); mm_base_modem_at_command_full (ctx->modem, ctx->primary, "%DPDNACT=1", MM_BASE_BEARER_DEFAULT_CONNECTION_TIMEOUT, /* timeout */ FALSE, /* allow_cached */ FALSE, /* is_raw */ g_task_get_cancellable (task), (GAsyncReadyCallback)connect_3gpp_connect_ready, task); /* user_data */ } static void connect_3gpp (MMBroadbandBearer *self, MMBroadbandModem *modem, MMPortSerialAt *primary, MMPortSerialAt *secondary, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { DetailedConnectContext *ctx; gchar *command, *apn; MMBearerProperties *config; MMModem3gppRegistrationState registration_state; MMPort *data; GTask *task; /* There is a known firmware bug that can leave the modem unusable if a * connect attempt is made when out of coverage. So, fail without trying. */ g_object_get (modem, MM_IFACE_MODEM_3GPP_REGISTRATION_STATE, ®istration_state, NULL); if (registration_state == MM_MODEM_3GPP_REGISTRATION_STATE_UNKNOWN) { g_task_report_new_error (self, callback, user_data, connect_3gpp, MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_NO_NETWORK, "Out of coverage, can't connect."); return; } /* Don't allow a connect while we detach from the network to process SIM * refresh. * */ if (mm_broadband_modem_altair_lte_is_sim_refresh_detach_in_progress (modem)) { mm_obj_dbg (self, "detached from network to process SIM refresh, failing connect request"); g_task_report_new_error (self, callback, user_data, connect_3gpp, MM_CORE_ERROR, MM_CORE_ERROR_RETRY, "Detached from network to process SIM refresh, can't connect."); return; } data = mm_base_modem_peek_best_data_port (MM_BASE_MODEM (modem), MM_PORT_TYPE_NET); if (!data) { g_task_report_new_error (self, callback, user_data, connect_3gpp, MM_CORE_ERROR, MM_CORE_ERROR_CONNECTED, "Couldn't connect: no available net port available"); return; } ctx = g_new0 (DetailedConnectContext, 1); ctx->modem = MM_BASE_MODEM (g_object_ref (modem)); ctx->primary = g_object_ref (primary); ctx->data = g_object_ref (data); task = g_task_new (self, cancellable, callback, user_data); g_task_set_task_data (task, ctx, (GDestroyNotify)detailed_connect_context_free); config = mm_base_bearer_peek_config (MM_BASE_BEARER (self)); apn = mm_port_serial_at_quote_string (mm_bearer_properties_get_apn (config)); command = g_strdup_printf ("%%APNN=%s", apn); g_free (apn); mm_base_modem_at_command_full (ctx->modem, ctx->primary, command, 10, /* timeout */ FALSE, /* allow_cached */ FALSE, /* is_raw */ cancellable, (GAsyncReadyCallback)connect_3gpp_apnsettings_ready, task); /* user_data */ g_free (command); } /*****************************************************************************/ /* 3GPP Disconnect sequence */ typedef struct { MMBaseModem *modem; MMPortSerialAt *primary; MMPort *data; } DetailedDisconnectContext; static gboolean disconnect_3gpp_finish (MMBroadbandBearer *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void detailed_disconnect_context_free (DetailedDisconnectContext *ctx) { g_object_unref (ctx->data); g_object_unref (ctx->primary); g_object_unref (ctx->modem); g_free (ctx); } static void disconnect_3gpp_check_status (MMBaseModem *modem, GAsyncResult *res, GTask *task) { const gchar *result; GError *error = NULL; result = mm_base_modem_at_command_full_finish (modem, res, &error); if (!result) g_task_return_error (task, error); else g_task_return_boolean (task, TRUE); g_object_unref (task); } static void disconnect_3gpp (MMBroadbandBearer *self, MMBroadbandModem *modem, MMPortSerialAt *primary, MMPortSerialAt *secondary, MMPort *data, guint cid, GAsyncReadyCallback callback, gpointer user_data) { DetailedDisconnectContext *ctx; MMModem3gppRegistrationState registration_state; GTask *task; /* There is a known firmware bug that can leave the modem unusable if a * disconnect attempt is made when out of coverage. So, fail without trying. */ g_object_get (modem, MM_IFACE_MODEM_3GPP_REGISTRATION_STATE, ®istration_state, NULL); if (registration_state == MM_MODEM_3GPP_REGISTRATION_STATE_UNKNOWN) { g_task_report_new_error (self, callback, user_data, disconnect_3gpp, MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_NO_NETWORK, "Out of coverage, can't disconnect."); return; } ctx = g_new0 (DetailedDisconnectContext, 1); ctx->modem = MM_BASE_MODEM (g_object_ref (modem)); ctx->primary = g_object_ref (primary); ctx->data = g_object_ref (data); task = g_task_new (self, NULL, callback, user_data); g_task_set_task_data (task, ctx, (GDestroyNotify)detailed_disconnect_context_free); mm_base_modem_at_command_full (ctx->modem, ctx->primary, "%DPDNACT=0", MM_BASE_BEARER_DEFAULT_DISCONNECTION_TIMEOUT, /* timeout */ FALSE, /* allow_cached */ FALSE, /* is_raw */ NULL, /* cancellable */ (GAsyncReadyCallback)disconnect_3gpp_check_status, task); /* user_data */ } /*****************************************************************************/ MMBaseBearer * mm_broadband_bearer_altair_lte_new_finish (GAsyncResult *res, GError **error) { GObject *bearer; GObject *source; source = g_async_result_get_source_object (res); bearer = g_async_initable_new_finish (G_ASYNC_INITABLE (source), res, error); g_object_unref (source); if (!bearer) return NULL; /* Only export valid bearers */ mm_base_bearer_export (MM_BASE_BEARER (bearer)); return MM_BASE_BEARER (bearer); } void mm_broadband_bearer_altair_lte_new (MMBroadbandModemAltairLte *modem, MMBearerProperties *config, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { g_async_initable_new_async ( MM_TYPE_BROADBAND_BEARER_ALTAIR_LTE, G_PRIORITY_DEFAULT, cancellable, callback, user_data, MM_BASE_BEARER_MODEM, modem, MM_BASE_BEARER_CONFIG, config, NULL); } static void mm_broadband_bearer_altair_lte_init (MMBroadbandBearerAltairLte *self) { } static void mm_broadband_bearer_altair_lte_class_init (MMBroadbandBearerAltairLteClass *klass) { MMBaseBearerClass *base_bearer_class = MM_BASE_BEARER_CLASS (klass); MMBroadbandBearerClass *broadband_bearer_class = MM_BROADBAND_BEARER_CLASS (klass); base_bearer_class->load_connection_status = NULL; base_bearer_class->load_connection_status_finish = NULL; #if defined WITH_SUSPEND_RESUME base_bearer_class->reload_connection_status = NULL; base_bearer_class->reload_connection_status_finish = NULL; #endif broadband_bearer_class->connect_3gpp = connect_3gpp; broadband_bearer_class->connect_3gpp_finish = connect_3gpp_finish; broadband_bearer_class->disconnect_3gpp = disconnect_3gpp; broadband_bearer_class->disconnect_3gpp_finish = disconnect_3gpp_finish; } ModemManager-1.23.4-dev/src/plugins/altair/mm-broadband-bearer-altair-lte.h000066400000000000000000000053771456466623000264530ustar00rootroot00000000000000/* -*- 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) 2013 Altair Semiconductor * * Author: Ori Inbar */ #ifndef MM_BROADBAND_BEARER_ALTAIR_LTE_H #define MM_BROADBAND_BEARER_ALTAIR_LTE_H #include #include #define _LIBMM_INSIDE_MM #include #include "mm-broadband-bearer.h" #include "mm-broadband-modem-altair-lte.h" #define MM_TYPE_BROADBAND_BEARER_ALTAIR_LTE (mm_broadband_bearer_altair_lte_get_type ()) #define MM_BROADBAND_BEARER_ALTAIR_LTE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_BROADBAND_BEARER_ALTAIR_LTE, MMBroadbandBearerAltairLte)) #define MM_BROADBAND_BEARER_ALTAIR_LTE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_BROADBAND_BEARER_ALTAIR_LTE, MMBroadbandBearerAltairLteClass)) #define MM_IS_BROADBAND_BEARER_ALTAIR_LTE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_BROADBAND_BEARER_ALTAIR_LTE)) #define MM_IS_BROADBAND_BEARER_ALTAIR_LTE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_BROADBAND_BEARER_ALTAIR_LTE)) #define MM_BROADBAND_BEARER_ALTAIR_LTE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_BROADBAND_BEARER_ALTAIR_LTE, MMBroadbandBearerAltairLteClass)) typedef struct _MMBroadbandBearerAltairLte MMBroadbandBearerAltairLte; typedef struct _MMBroadbandBearerAltairLteClass MMBroadbandBearerAltairLteClass; struct _MMBroadbandBearerAltairLte { MMBroadbandBearer parent; }; struct _MMBroadbandBearerAltairLteClass { MMBroadbandBearerClass parent; }; GType mm_broadband_bearer_altair_lte_get_type (void); /* Default 3GPP bearer creation implementation */ void mm_broadband_bearer_altair_lte_new (MMBroadbandModemAltairLte *modem, MMBearerProperties *properties, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); MMBaseBearer *mm_broadband_bearer_altair_lte_new_finish (GAsyncResult *res, GError **error); #endif /* MM_BROADBAND_BEARER_ALTAIR_LTE_H */ ModemManager-1.23.4-dev/src/plugins/altair/mm-broadband-modem-altair-lte.c000066400000000000000000001271151456466623000263020ustar00rootroot00000000000000/* -*- 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) 2013 Altair Semiconductor * * Author: Ori Inbar */ #include #include #include #include #include #include #include "ModemManager.h" #define _LIBMM_INSIDE_MM #include #include "mm-base-modem-at.h" #include "mm-broadband-bearer-altair-lte.h" #include "mm-broadband-modem-altair-lte.h" #include "mm-errors-types.h" #include "mm-iface-modem.h" #include "mm-iface-modem-3gpp.h" #include "mm-iface-modem-3gpp-ussd.h" #include "mm-iface-modem-messaging.h" #include "mm-log-object.h" #include "mm-modem-helpers.h" #include "mm-modem-helpers-altair-lte.h" #include "mm-serial-parsers.h" #include "mm-bearer-list.h" static void iface_modem_init (MMIfaceModem *iface); static void iface_modem_3gpp_init (MMIfaceModem3gpp *iface); static void iface_modem_3gpp_ussd_init (MMIfaceModem3gppUssd *iface); static void iface_modem_messaging_init (MMIfaceModemMessaging *iface); G_DEFINE_TYPE_EXTENDED (MMBroadbandModemAltairLte, mm_broadband_modem_altair_lte, MM_TYPE_BROADBAND_MODEM, 0, G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM, iface_modem_init) G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_3GPP, iface_modem_3gpp_init) G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_3GPP_USSD, iface_modem_3gpp_ussd_init) G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_MESSAGING, iface_modem_messaging_init)) struct _MMBroadbandModemAltairLtePrivate { /* Regex for SIM refresh notifications */ GRegex *sim_refresh_regex; /* Timer that goes off 10s after the last SIM refresh notification. * This indicates that there are no more SIM refreshes and we should * reregister the device.*/ guint sim_refresh_timer_id; /* Flag indicating that we are detaching from the network to process SIM * refresh. This is used to prevent connect requests while we're in this * state.*/ gboolean sim_refresh_detach_in_progress; /* Regex for bearer related notifications */ GRegex *statcm_regex; /* Regex for PCO notifications */ GRegex *pcoinfo_regex; GList *pco_list; }; static MMIfaceModem3gpp *iface_modem_3gpp_parent; /*****************************************************************************/ /* Modem power down (Modem interface) */ static gboolean modem_power_down_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { return !!mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, error); } static void modem_power_down (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data) { mm_base_modem_at_command (MM_BASE_MODEM (self), "+CFUN=4", 20, FALSE, callback, user_data); } /*****************************************************************************/ /* Create Bearer (Modem interface) */ static MMBaseBearer * modem_create_bearer_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { return g_task_propagate_pointer (G_TASK (res), error); } static void broadband_bearer_new_ready (GObject *source, GAsyncResult *res, GTask *task) { MMBaseBearer *bearer = NULL; GError *error = NULL; bearer = mm_broadband_bearer_altair_lte_new_finish (res, &error); if (!bearer) g_task_return_error (task, error); else g_task_return_pointer (task, bearer, g_object_unref); g_object_unref (task); } static void modem_create_bearer (MMIfaceModem *self, MMBearerProperties *properties, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; task = g_task_new (self, NULL, callback, user_data); /* We just create a MMBroadbandBearer */ mm_broadband_bearer_altair_lte_new (MM_BROADBAND_MODEM_ALTAIR_LTE (self), properties, NULL, /* cancellable */ (GAsyncReadyCallback)broadband_bearer_new_ready, task); } /*****************************************************************************/ /* Load unlock retries (Modem interface) */ static MMUnlockRetries * load_unlock_retries_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { return g_task_propagate_pointer (G_TASK (res), error); } static void load_unlock_retries_ready (MMBaseModem *self, GAsyncResult *res, GTask *task) { const gchar *response; GError *error = NULL; gint pin1, puk1, pin2, puk2; response = mm_base_modem_at_command_finish (self, res, &error); if (!response) { g_task_return_error (task, error); g_object_unref (task); return; } response = mm_strip_tag (response, "%CPININFO:"); if (sscanf (response, " %d, %d, %d, %d", &pin1, &puk1, &pin2, &puk2) == 4) { MMUnlockRetries *retries; retries = mm_unlock_retries_new (); mm_unlock_retries_set (retries, MM_MODEM_LOCK_SIM_PIN, pin1); mm_unlock_retries_set (retries, MM_MODEM_LOCK_SIM_PUK, puk1); mm_unlock_retries_set (retries, MM_MODEM_LOCK_SIM_PIN2, pin2); mm_unlock_retries_set (retries, MM_MODEM_LOCK_SIM_PUK2, puk2); g_task_return_pointer (task, retries, g_object_unref); } else { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Invalid unlock retries response: '%s'", response); } g_object_unref (task); } static void load_unlock_retries (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; task = g_task_new (self, NULL, callback, user_data); mm_base_modem_at_command (MM_BASE_MODEM (self), "%CPININFO", 3, FALSE, (GAsyncReadyCallback)load_unlock_retries_ready, task); } /*****************************************************************************/ /* Load current capabilities (Modem interface) */ static MMModemCapability load_current_capabilities_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { GError *inner_error = NULL; gssize value; value = g_task_propagate_int (G_TASK (res), &inner_error); if (inner_error) { g_propagate_error (error, inner_error); return MM_MODEM_CAPABILITY_NONE; } return (MMModemCapability)value; } static void load_current_capabilities (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; task = g_task_new (self, NULL, callback, user_data); /* This modem is LTE only.*/ g_task_return_int (task, MM_MODEM_CAPABILITY_LTE); g_object_unref (task); } /*****************************************************************************/ /* Load supported bands (Modem interface) */ static GArray * load_supported_bands_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { return g_task_propagate_pointer (G_TASK (res), error); } #define BANDCAP_TAG "%BANDCAP: " static void load_supported_bands_done (MMIfaceModem *self, GAsyncResult *res, GTask *task) { GArray *bands; const gchar *response; GError *error = NULL; response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error); if (!response) { g_task_return_error (task, error); g_object_unref (task); return; } /* * Response is "%BANDCAP: ,[...]" */ response = mm_strip_tag (response, BANDCAP_TAG); bands = mm_altair_parse_bands_response (response); if (!bands) { g_task_return_new_error ( task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Failed to parse supported bands response"); g_object_unref (task); return; } g_task_return_pointer (task, bands, (GDestroyNotify)g_array_unref); g_object_unref (task); } static void load_supported_bands (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; task = g_task_new (self, NULL, callback, user_data); mm_base_modem_at_command ( MM_BASE_MODEM (self), "%BANDCAP=", 3, FALSE, (GAsyncReadyCallback)load_supported_bands_done, task); } /*****************************************************************************/ /* Load current bands (Modem interface) */ static GArray * load_current_bands_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { return g_task_propagate_pointer (G_TASK (res), error); } #define CFGBANDS_TAG "Bands: " static void load_current_bands_done (MMIfaceModem *self, GAsyncResult *res, GTask *task) { GArray *bands; const gchar *response; GError *error = NULL; response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error); if (!response) { g_task_return_error (task, error); g_object_unref (task); return; } /* * Response is "Bands: ,[...]" */ response = mm_strip_tag (response, CFGBANDS_TAG); bands = mm_altair_parse_bands_response (response); if (!bands) { g_task_return_new_error ( task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Failed to parse current bands response"); g_object_unref (task); return; } g_task_return_pointer (task, bands, (GDestroyNotify)g_array_unref); g_object_unref (task); } static void load_current_bands (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; task = g_task_new (self, NULL, callback, user_data); mm_base_modem_at_command ( MM_BASE_MODEM (self), "%GETCFG=\"BAND\"", 3, FALSE, (GAsyncReadyCallback)load_current_bands_done, task); } /*****************************************************************************/ /* Reset (Modem interface) */ static gboolean reset_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { return !!mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, error); } static void reset (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data) { mm_base_modem_at_command (MM_BASE_MODEM (self), "ATZ", 3, FALSE, callback, user_data); } /*****************************************************************************/ /* Run registration checks (3GPP interface) */ static gboolean modem_3gpp_run_registration_checks_finish (MMIfaceModem3gpp *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void simulate_unprovisioned_subscription_pco_update (MMBroadbandModemAltairLte *self) { MMPco *pco; /* Simulate a %PCOINFO notification issued to the IMS PDN that indicates an * unprovisioned Verizon SIM. See mm_altair_parse_vendor_pco_info() for the * detailed format of a %PCOINFO response. * * 1,FF00,13018405 is constructed as follows: * * 1: CID for IMS PDN * FF 00: Container ID for the Verizon-specific PCO content * 13 01 84: Binary coded decimal representation of Verizon MCC/MNC 311/084 * 05: Value indicating an unprovisioned SIM */ pco = mm_altair_parse_vendor_pco_info ("%PCOINFO: 1,FF00,13018405", NULL); g_assert (pco != NULL); self->priv->pco_list = mm_pco_list_add (self->priv->pco_list, pco); mm_iface_modem_3gpp_update_pco_list (MM_IFACE_MODEM_3GPP (self), self->priv->pco_list); g_object_unref (pco); } static void run_registration_checks_subscription_state_ready (MMIfaceModem3gpp *self, GAsyncResult *res, GTask *task) { GError *error = NULL; const gchar *at_response; gchar *ceer_response; /* If the AT+CEER command fails, or we fail to obtain a valid result, we * ignore the error. This allows the registration attempt to continue. * So, the async response from this function is *always* True. */ at_response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error); if (!at_response) { g_assert (error); mm_obj_warn (self, "AT+CEER failed: %s", error->message); g_error_free (error); g_task_return_boolean (task, TRUE); g_object_unref (task); return; } ceer_response = mm_altair_parse_ceer_response (at_response, &error); if (!ceer_response) { g_assert (error); mm_obj_warn (self, "Failed to parse AT+CEER response: %s", error->message); g_error_free (error); g_task_return_boolean (task, TRUE); g_object_unref (task); return; } if (g_strcmp0 ("EPS_AND_NON_EPS_SERVICES_NOT_ALLOWED", ceer_response) == 0) { mm_obj_dbg (self, "registration failed due to unprovisioned SIM"); simulate_unprovisioned_subscription_pco_update (MM_BROADBAND_MODEM_ALTAIR_LTE (self)); } else { mm_obj_dbg (self, "failed to find a better reason for registration failure"); } g_task_return_boolean (task, TRUE); g_object_unref (task); g_free (ceer_response); } static void run_registration_checks_ready (MMIfaceModem3gpp *self, GAsyncResult *res, GTask *task) { GError *error = NULL; gboolean success; g_assert (iface_modem_3gpp_parent->run_registration_checks_finish); success = iface_modem_3gpp_parent->run_registration_checks_finish (self, res, &error); if (!success) { g_assert (error); g_task_return_error (task, error); g_object_unref (task); return; } mm_obj_dbg (self, "checking if SIM is unprovisioned (ignoring registration state)"); mm_base_modem_at_command (MM_BASE_MODEM (self), "+CEER", 6, FALSE, (GAsyncReadyCallback) run_registration_checks_subscription_state_ready, task); } static void modem_3gpp_run_registration_checks (MMIfaceModem3gpp *self, gboolean is_cs_supported, gboolean is_ps_supported, gboolean is_eps_supported, gboolean is_5gs_supported, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; task = g_task_new (self, NULL, callback, user_data); g_assert (iface_modem_3gpp_parent->run_registration_checks); iface_modem_3gpp_parent->run_registration_checks (self, is_cs_supported, is_ps_supported, is_eps_supported, is_5gs_supported, (GAsyncReadyCallback) run_registration_checks_ready, task); } /*****************************************************************************/ /* Register in network (3GPP interface) */ static gboolean modem_3gpp_register_in_network_finish (MMIfaceModem3gpp *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void cmatt_ready (MMBaseModem *self, GAsyncResult *res, GTask *task) { GError *error = NULL; if (!mm_base_modem_at_command_finish (self, res, &error)) g_task_return_error (task, error); else g_task_return_boolean (task, TRUE); g_object_unref (task); } static void modem_3gpp_register_in_network (MMIfaceModem3gpp *self, const gchar *operator_id, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; task = g_task_new (self, cancellable, callback, user_data); if (operator_id) { /* Currently only VZW is supported */ g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "Setting a specific operator ID is not supported"); g_object_unref (task); return; } mm_base_modem_at_command (MM_BASE_MODEM (self), "%CMATT=1", 3, FALSE, /* allow cached */ (GAsyncReadyCallback)cmatt_ready, task); } /*****************************************************************************/ /* SIMREFRESH unsolicited event handler */ static void altair_reregister_ready (MMBaseModem *self, GAsyncResult *res, gpointer user_data) { if (!mm_base_modem_at_command_finish (self, res, NULL)) mm_obj_dbg (self, "failed to reregister modem"); else mm_obj_dbg (self, "modem reregistered successfully"); MM_BROADBAND_MODEM_ALTAIR_LTE (self)->priv->sim_refresh_detach_in_progress = FALSE; } static void altair_deregister_ready (MMBaseModem *self, GAsyncResult *res, gpointer user_data) { if (!mm_base_modem_at_command_finish (self, res, NULL)) { mm_obj_dbg (self, "deregister modem failed"); MM_BROADBAND_MODEM_ALTAIR_LTE (self)->priv->sim_refresh_detach_in_progress = FALSE; return; } mm_obj_dbg (self, "deregistered modem, now reregistering"); /* Register */ mm_base_modem_at_command ( self, "%CMATT=1", 10, FALSE, /* allow_cached */ (GAsyncReadyCallback)altair_reregister_ready, NULL); } static void altair_load_own_numbers_ready (MMIfaceModem *iface_modem, GAsyncResult *res, MMBroadbandModemAltairLte *self) { GError *error = NULL; GStrv str_list; str_list = MM_IFACE_MODEM_GET_INTERFACE (self)->load_own_numbers_finish (MM_IFACE_MODEM (self), res, &error); if (error) { mm_obj_warn (self, "Couldn't reload Own Numbers: '%s'", error->message); g_error_free (error); } if (str_list) { mm_iface_modem_update_own_numbers (iface_modem, str_list); g_strfreev (str_list); } /* Set this flag to prevent connect requests from being processed while we * detach from the network.*/ self->priv->sim_refresh_detach_in_progress = TRUE; /* Deregister */ mm_obj_dbg (self, "reregistering modem"); mm_base_modem_at_command ( MM_BASE_MODEM (self), "%CMATT=0", 10, FALSE, /* allow_cached */ (GAsyncReadyCallback)altair_deregister_ready, NULL); } static gboolean altair_sim_refresh_timer_expired (MMBroadbandModemAltairLte *self) { mm_obj_dbg (self, "no more SIM refreshes, reloading own numbers and reregistering modem"); g_assert (MM_IFACE_MODEM_GET_INTERFACE (self)->load_own_numbers); g_assert (MM_IFACE_MODEM_GET_INTERFACE (self)->load_own_numbers_finish); MM_IFACE_MODEM_GET_INTERFACE (self)->load_own_numbers ( MM_IFACE_MODEM (self), (GAsyncReadyCallback)altair_load_own_numbers_ready, self); self->priv->sim_refresh_timer_id = 0; return G_SOURCE_REMOVE; } static void altair_sim_refresh_changed (MMPortSerialAt *port, GMatchInfo *match_info, MMBroadbandModemAltairLte *self) { mm_obj_dbg (self, "received SIM refresh notification"); if (self->priv->sim_refresh_timer_id) { g_source_remove (self->priv->sim_refresh_timer_id); } self->priv->sim_refresh_timer_id = g_timeout_add_seconds(10, (GSourceFunc)altair_sim_refresh_timer_expired, self); } typedef enum { MM_STATCM_ALTAIR_LTE_DEREGISTERED = 0, MM_STATCM_ALTAIR_LTE_REGISTERED = 1, MM_STATCM_ALTAIR_PDN_CONNECTED = 3, MM_STATCM_ALTAIR_PDN_DISCONNECTED = 4, } MMStatcmAltair; static void bearer_list_report_disconnect_status_foreach (MMBaseBearer *bearer, gpointer *user_data) { mm_base_bearer_report_connection_status (bearer, MM_BEARER_CONNECTION_STATUS_DISCONNECTED); } /*****************************************************************************/ /* STATCM unsolicited event handler */ static void altair_statcm_changed (MMPortSerialAt *port, GMatchInfo *match_info, MMBroadbandModemAltairLte *self) { gint pdn_event = 0; MMBearerList *list = NULL; mm_get_int_from_match_info (match_info, 1, &pdn_event); mm_obj_dbg (self, "PDN event detected: %d", pdn_event); /* Currently we only care about bearer disconnection */ if (pdn_event == MM_STATCM_ALTAIR_PDN_DISCONNECTED) { /* If empty bearer list, nothing else to do */ g_object_get (self, MM_IFACE_MODEM_BEARER_LIST, &list, NULL); if (!list) return; mm_bearer_list_foreach (list, (MMBearerListForeachFunc)bearer_list_report_disconnect_status_foreach, NULL); g_object_unref (list); } } /*****************************************************************************/ /* Setup/Cleanup unsolicited events (3GPP interface) */ static void altair_pco_info_changed (MMPortSerialAt *port, GMatchInfo *match_info, MMBroadbandModemAltairLte *self); static void set_3gpp_unsolicited_events_handlers (MMBroadbandModemAltairLte *self, gboolean enable) { MMPortSerialAt *ports[2]; guint i; ports[0] = mm_base_modem_peek_port_primary (MM_BASE_MODEM (self)); ports[1] = mm_base_modem_peek_port_secondary (MM_BASE_MODEM (self)); /* Enable/disable unsolicited events in given port */ for (i = 0; i < G_N_ELEMENTS (ports); i++) { if (!ports[i]) continue; /* SIM refresh handler */ mm_port_serial_at_add_unsolicited_msg_handler ( ports[i], self->priv->sim_refresh_regex, enable ? (MMPortSerialAtUnsolicitedMsgFn)altair_sim_refresh_changed : NULL, enable ? self : NULL, NULL); /* bearer mode related */ mm_port_serial_at_add_unsolicited_msg_handler ( ports[i], self->priv->statcm_regex, enable ? (MMPortSerialAtUnsolicitedMsgFn)altair_statcm_changed : NULL, enable ? self : NULL, NULL); /* PCO info handler */ mm_port_serial_at_add_unsolicited_msg_handler ( ports[i], self->priv->pcoinfo_regex, enable ? (MMPortSerialAtUnsolicitedMsgFn)altair_pco_info_changed : NULL, enable ? self : NULL, NULL); } } static gboolean modem_3gpp_setup_cleanup_unsolicited_events_finish (MMIfaceModem3gpp *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void parent_3gpp_setup_unsolicited_events_ready (MMIfaceModem3gpp *self, GAsyncResult *res, GTask *task) { GError *error = NULL; if (!iface_modem_3gpp_parent->setup_unsolicited_events_finish (self, res, &error)) g_task_return_error (task, error); else { /* Our own setup now */ set_3gpp_unsolicited_events_handlers (MM_BROADBAND_MODEM_ALTAIR_LTE (self), TRUE); g_task_return_boolean (task, TRUE); } g_object_unref (task); } static void modem_3gpp_setup_unsolicited_events (MMIfaceModem3gpp *self, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; task = g_task_new (self, NULL, callback, user_data); /* Chain up parent's setup */ iface_modem_3gpp_parent->setup_unsolicited_events ( self, (GAsyncReadyCallback)parent_3gpp_setup_unsolicited_events_ready, task); } static void parent_3gpp_cleanup_unsolicited_events_ready (MMIfaceModem3gpp *self, GAsyncResult *res, GTask *task) { GError *error = NULL; if (!iface_modem_3gpp_parent->cleanup_unsolicited_events_finish (self, res, &error)) g_task_return_error (task, error); else g_task_return_boolean (task, TRUE); g_object_unref (task); } static void modem_3gpp_cleanup_unsolicited_events (MMIfaceModem3gpp *self, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; task = g_task_new (self, NULL, callback, user_data); /* Our own cleanup first */ set_3gpp_unsolicited_events_handlers (MM_BROADBAND_MODEM_ALTAIR_LTE (self), FALSE); /* And now chain up parent's cleanup */ iface_modem_3gpp_parent->cleanup_unsolicited_events ( self, (GAsyncReadyCallback)parent_3gpp_cleanup_unsolicited_events_ready, task); } /*****************************************************************************/ /* Enabling unsolicited events (3GPP interface) */ static const MMBaseModemAtCommand unsolicited_events_enable_sequence[] = { { "%STATCM=1", 10, FALSE, mm_base_modem_response_processor_no_result_continue }, { "%NOTIFYEV=\"SIMREFRESH\",1", 10, FALSE, NULL }, { "%PCOINFO=1", 10, FALSE, NULL }, { NULL } }; static gboolean modem_3gpp_enable_unsolicited_events_finish (MMIfaceModem3gpp *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void own_enable_unsolicited_events_ready (MMBaseModem *self, GAsyncResult *res, GTask *task) { GError *error = NULL; mm_base_modem_at_sequence_finish (self, res, NULL, &error); if (error) g_task_return_error (task, error); else g_task_return_boolean (task, TRUE); g_object_unref (task); } static void parent_enable_unsolicited_events_ready (MMIfaceModem3gpp *self, GAsyncResult *res, GTask *task) { GError *error = NULL; if (!iface_modem_3gpp_parent->enable_unsolicited_events_finish (self, res, &error)) { g_task_return_error (task, error); g_object_unref (task); return; } /* Our own enable now */ mm_base_modem_at_sequence ( MM_BASE_MODEM (self), unsolicited_events_enable_sequence, NULL, /* response_processor_context */ NULL, /* response_processor_context_free */ (GAsyncReadyCallback)own_enable_unsolicited_events_ready, task); } static void modem_3gpp_enable_unsolicited_events (MMIfaceModem3gpp *self, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; task = g_task_new (self, NULL, callback, user_data); /* Chain up parent's enable */ iface_modem_3gpp_parent->enable_unsolicited_events ( self, (GAsyncReadyCallback)parent_enable_unsolicited_events_ready, task); } /*****************************************************************************/ /* Disabling unsolicited events (3GPP interface) */ static const MMBaseModemAtCommand unsolicited_events_disable_sequence[] = { { "%STATCM=0", 10, FALSE, NULL }, { "%NOTIFYEV=\"SIMREFRESH\",0", 10, FALSE, NULL }, { "%PCOINFO=0", 10, FALSE, NULL }, { NULL } }; static gboolean modem_3gpp_disable_unsolicited_events_finish (MMIfaceModem3gpp *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void parent_disable_unsolicited_events_ready (MMIfaceModem3gpp *self, GAsyncResult *res, GTask *task) { GError *error = NULL; if (!iface_modem_3gpp_parent->disable_unsolicited_events_finish (self, res, &error)) g_task_return_error (task, error); else g_task_return_boolean (task, TRUE); g_object_unref (task); } static void own_disable_unsolicited_events_ready (MMBaseModem *self, GAsyncResult *res, GTask *task) { GError *error = NULL; mm_base_modem_at_sequence_finish (self, res, NULL, &error); if (error) { g_task_return_error (task, error); g_object_unref (task); return; } /* Next, chain up parent's disable */ iface_modem_3gpp_parent->disable_unsolicited_events ( MM_IFACE_MODEM_3GPP (self), (GAsyncReadyCallback)parent_disable_unsolicited_events_ready, task); } static void modem_3gpp_disable_unsolicited_events (MMIfaceModem3gpp *self, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; task = g_task_new (self, NULL, callback, user_data); /* Our own disable first */ mm_base_modem_at_sequence ( MM_BASE_MODEM (self), unsolicited_events_disable_sequence, NULL, /* response_processor_context */ NULL, /* response_processor_context_free */ (GAsyncReadyCallback)own_disable_unsolicited_events_ready, task); } /*****************************************************************************/ /* Operator Code loading (3GPP interface) */ static gchar * modem_3gpp_load_operator_code_finish (MMIfaceModem3gpp *self, GAsyncResult *res, GError **error) { const gchar *result; gchar *operator_code = NULL; result = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, error); if (!result) return NULL; if (!mm_3gpp_parse_cops_read_response (result, NULL, /* mode */ NULL, /* format */ &operator_code, NULL, /* act */ self, error)) return NULL; return operator_code; } static void modem_3gpp_load_operator_code (MMIfaceModem3gpp *self, GAsyncReadyCallback callback, gpointer user_data) { mm_base_modem_at_command (MM_BASE_MODEM (self), "+COPS=3,2", 6, FALSE, NULL, NULL); mm_base_modem_at_command (MM_BASE_MODEM (self), "+COPS?", 6, FALSE, callback, user_data); } /*****************************************************************************/ /* Operator Name loading (3GPP interface) */ static gchar * modem_3gpp_load_operator_name_finish (MMIfaceModem3gpp *self, GAsyncResult *res, GError **error) { const gchar *result; gchar *operator_name = NULL; result = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, error); if (!result) return NULL; if (!mm_3gpp_parse_cops_read_response (result, NULL, /* mode */ NULL, /* format */ &operator_name, NULL, /* act */ self, error)) return NULL; mm_3gpp_normalize_operator (&operator_name, MM_MODEM_CHARSET_UNKNOWN, self); return operator_name; } static void modem_3gpp_load_operator_name (MMIfaceModem3gpp *self, GAsyncReadyCallback callback, gpointer user_data) { mm_base_modem_at_command (MM_BASE_MODEM (self), "+COPS=3,0", 6, FALSE, NULL, NULL); mm_base_modem_at_command (MM_BASE_MODEM (self), "+COPS?", 6, FALSE, callback, user_data); } /*****************************************************************************/ /* PCOINFO unsolicited event handler */ static void altair_pco_info_changed (MMPortSerialAt *port, GMatchInfo *match_info, MMBroadbandModemAltairLte *self) { const gchar *pco_info; MMPco *pco; g_autoptr(GError) error = NULL; pco_info = g_match_info_fetch (match_info, 0); /* ignore if empty */ if (!pco_info || !pco_info[0]) return; mm_obj_dbg (self, "parsing vendor PCO info: %s", pco_info); pco = mm_altair_parse_vendor_pco_info (pco_info, &error); if (!pco) { mm_obj_warn (self, "error parsing vendor PCO info: %s", error->message); return; } self->priv->pco_list = mm_pco_list_add (self->priv->pco_list, pco); mm_iface_modem_3gpp_update_pco_list (MM_IFACE_MODEM_3GPP (self), self->priv->pco_list); g_object_unref (pco); } /*****************************************************************************/ /* Generic ports open/close context */ static const gchar *primary_init_sequence[] = { /* Extended numeric codes */ "+CMEE=1", NULL }; static void setup_ports (MMBroadbandModem *self) { MMPortSerialAt *primary; /* Call parent's setup ports first always */ MM_BROADBAND_MODEM_CLASS (mm_broadband_modem_altair_lte_parent_class)->setup_ports (self); primary = mm_base_modem_peek_port_primary (MM_BASE_MODEM (self)); if (!primary) return; g_object_set (primary, MM_PORT_SERIAL_SEND_DELAY, (guint64) 0, MM_PORT_SERIAL_AT_SEND_LF, TRUE, MM_PORT_SERIAL_AT_INIT_SEQUENCE, primary_init_sequence, NULL); } /*****************************************************************************/ MMBroadbandModemAltairLte * mm_broadband_modem_altair_lte_new (const gchar *device, const gchar *physdev, const gchar **drivers, const gchar *plugin, guint16 vendor_id, guint16 product_id) { return g_object_new (MM_TYPE_BROADBAND_MODEM_ALTAIR_LTE, MM_BASE_MODEM_DEVICE, device, MM_BASE_MODEM_PHYSDEV, physdev, MM_BASE_MODEM_DRIVERS, drivers, MM_BASE_MODEM_PLUGIN, plugin, MM_BASE_MODEM_VENDOR_ID, vendor_id, MM_BASE_MODEM_PRODUCT_ID, product_id, /* Altair bearer supports NET only */ MM_BASE_MODEM_DATA_NET_SUPPORTED, TRUE, MM_BASE_MODEM_DATA_TTY_SUPPORTED, FALSE, /* Since this is an LTE-only modem - don't bother query * anything else */ MM_IFACE_MODEM_3GPP_CS_NETWORK_SUPPORTED, FALSE, MM_IFACE_MODEM_3GPP_PS_NETWORK_SUPPORTED, FALSE, MM_IFACE_MODEM_3GPP_EPS_NETWORK_SUPPORTED, TRUE, NULL); } gboolean mm_broadband_modem_altair_lte_is_sim_refresh_detach_in_progress (MMBroadbandModem *self) { return MM_BROADBAND_MODEM_ALTAIR_LTE (self)->priv->sim_refresh_detach_in_progress; } static void mm_broadband_modem_altair_lte_init (MMBroadbandModemAltairLte *self) { /* Initialize private data */ self->priv = G_TYPE_INSTANCE_GET_PRIVATE ((self), MM_TYPE_BROADBAND_MODEM_ALTAIR_LTE, MMBroadbandModemAltairLtePrivate); self->priv->sim_refresh_regex = g_regex_new ("\\r\\n\\%NOTIFYEV:\\s*\"?SIMREFRESH\"?,?(\\d*)\\r+\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); self->priv->sim_refresh_detach_in_progress = FALSE; self->priv->sim_refresh_timer_id = 0; self->priv->statcm_regex = g_regex_new ("\\r\\n\\%STATCM:\\s*(\\d*),?(\\d*)\\r+\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); self->priv->pcoinfo_regex = g_regex_new ("\\r\\n\\%PCOINFO:\\s*(\\d*),([^,\\s]*),([^,\\s]*)\\r+\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); } static void finalize (GObject *object) { MMBroadbandModemAltairLte *self = MM_BROADBAND_MODEM_ALTAIR_LTE (object); if (self->priv->sim_refresh_timer_id) g_source_remove (self->priv->sim_refresh_timer_id); g_regex_unref (self->priv->sim_refresh_regex); g_regex_unref (self->priv->statcm_regex); g_regex_unref (self->priv->pcoinfo_regex); G_OBJECT_CLASS (mm_broadband_modem_altair_lte_parent_class)->finalize (object); } static void iface_modem_init (MMIfaceModem *iface) { iface->modem_power_down = modem_power_down; iface->modem_power_down_finish = modem_power_down_finish; iface->create_bearer = modem_create_bearer; iface->create_bearer_finish = modem_create_bearer_finish; iface->load_unlock_retries = load_unlock_retries; iface->load_unlock_retries_finish = load_unlock_retries_finish; iface->load_current_capabilities = load_current_capabilities; iface->load_current_capabilities_finish = load_current_capabilities_finish; iface->load_supported_bands = load_supported_bands; iface->load_supported_bands_finish = load_supported_bands_finish; iface->load_current_bands = load_current_bands; iface->load_current_bands_finish = load_current_bands_finish; iface->load_access_technologies = NULL; iface->load_access_technologies_finish = NULL; iface->reset = reset; iface->reset_finish = reset_finish; iface->load_supported_charsets = NULL; iface->load_supported_charsets_finish = NULL; iface->setup_charset = NULL; iface->setup_charset_finish = NULL; iface->setup_flow_control = NULL; iface->setup_flow_control_finish = NULL; } static void iface_modem_3gpp_ussd_init (MMIfaceModem3gppUssd *iface) { /* we don't have USSD support */ iface->check_support = NULL; iface->check_support_finish = NULL; } static void iface_modem_3gpp_init (MMIfaceModem3gpp *iface) { iface_modem_3gpp_parent = g_type_interface_peek_parent (iface); iface->setup_unsolicited_events = modem_3gpp_setup_unsolicited_events; iface->setup_unsolicited_events_finish = modem_3gpp_setup_cleanup_unsolicited_events_finish; iface->cleanup_unsolicited_events = modem_3gpp_cleanup_unsolicited_events; iface->cleanup_unsolicited_events_finish = modem_3gpp_setup_cleanup_unsolicited_events_finish; iface->enable_unsolicited_events = modem_3gpp_enable_unsolicited_events; iface->enable_unsolicited_events_finish = modem_3gpp_enable_unsolicited_events_finish; iface->disable_unsolicited_events = modem_3gpp_disable_unsolicited_events; iface->disable_unsolicited_events_finish = modem_3gpp_disable_unsolicited_events_finish; iface->register_in_network = modem_3gpp_register_in_network; iface->register_in_network_finish = modem_3gpp_register_in_network_finish; iface->run_registration_checks = modem_3gpp_run_registration_checks; iface->run_registration_checks_finish = modem_3gpp_run_registration_checks_finish; /* Scanning is not currently supported */ iface->scan_networks = NULL; iface->scan_networks_finish = NULL; /* Additional actions */ iface->load_operator_code = modem_3gpp_load_operator_code; iface->load_operator_code_finish = modem_3gpp_load_operator_code_finish; iface->load_operator_name = modem_3gpp_load_operator_name; iface->load_operator_name_finish = modem_3gpp_load_operator_name_finish; } static void iface_modem_messaging_init (MMIfaceModemMessaging *iface) { /* Currently no messaging is implemented - so skip checking*/ iface->check_support = NULL; iface->check_support_finish = NULL; } static void mm_broadband_modem_altair_lte_class_init (MMBroadbandModemAltairLteClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); MMBroadbandModemClass *broadband_modem_class = MM_BROADBAND_MODEM_CLASS (klass); g_type_class_add_private (object_class, sizeof (MMBroadbandModemAltairLtePrivate)); object_class->finalize = finalize; broadband_modem_class->setup_ports = setup_ports; /* The Altair LTE modem reboots itself upon receiving an ATZ command. We * need to skip the default implementation in MMBroadbandModem to prevent * an ATZ command from being issued as part of the modem initialization * sequence when enabling the modem. */ broadband_modem_class->enabling_modem_init = NULL; broadband_modem_class->enabling_modem_init_finish = NULL; } ModemManager-1.23.4-dev/src/plugins/altair/mm-broadband-modem-altair-lte.h000066400000000000000000000052721456466623000263060ustar00rootroot00000000000000/* -*- 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) 2013 Altair Semiconductor * * Author: Ori Inbar */ #ifndef MM_BROADBAND_MODEM_ALTAIR_LTE_H #define MM_BROADBAND_MODEM_ALTAIR_LTE_H #include "mm-broadband-modem.h" #define MM_TYPE_BROADBAND_MODEM_ALTAIR_LTE (mm_broadband_modem_altair_lte_get_type ()) #define MM_BROADBAND_MODEM_ALTAIR_LTE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_BROADBAND_MODEM_ALTAIR_LTE, MMBroadbandModemAltairLte)) #define MM_BROADBAND_MODEM_ALTAIR_LTE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_BROADBAND_MODEM_ALTAIR_LTE, MMBroadbandModemAltairLteClass)) #define MM_IS_BROADBAND_MODEM_ALTAIR_LTE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_BROADBAND_MODEM_ALTAIR_LTE)) #define MM_IS_BROADBAND_MODEM_ALTAIR_LTE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_BROADBAND_MODEM_ALTAIR_LTE)) #define MM_BROADBAND_MODEM_ALTAIR_LTE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_BROADBAND_MODEM_ALTAIR_LTE, MMBroadbandModemAltairLteClass)) typedef struct _MMBroadbandModemAltairLte MMBroadbandModemAltairLte; typedef struct _MMBroadbandModemAltairLteClass MMBroadbandModemAltairLteClass; typedef struct _MMBroadbandModemAltairLtePrivate MMBroadbandModemAltairLtePrivate; struct _MMBroadbandModemAltairLte { MMBroadbandModem parent; MMBroadbandModemAltairLtePrivate *priv; }; struct _MMBroadbandModemAltairLteClass{ MMBroadbandModemClass parent; }; GType mm_broadband_modem_altair_lte_get_type (void); MMBroadbandModemAltairLte *mm_broadband_modem_altair_lte_new (const gchar *device, const gchar *physdev, const gchar **drivers, const gchar *plugin, guint16 vendor_id, guint16 product_id); gboolean mm_broadband_modem_altair_lte_is_sim_refresh_detach_in_progress (MMBroadbandModem *self); #endif /* MM_BROADBAND_MODEM_ALTAIR_LTE_H */ ModemManager-1.23.4-dev/src/plugins/altair/mm-modem-helpers-altair-lte.c000066400000000000000000000210461456466623000260240ustar00rootroot00000000000000/* -*- 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) 2013 Google Inc. * */ #include #include #include #define _LIBMM_INSIDE_MM #include #include "mm-modem-helpers-altair-lte.h" #define MM_ALTAIR_IMS_PDN_CID 1 #define MM_ALTAIR_INTERNET_PDN_CID 3 /*****************************************************************************/ /* Bands response parser */ GArray * mm_altair_parse_bands_response (const gchar *response) { gchar **split; GArray *bands; guint i; /* * Response is "[,...]" */ split = g_strsplit_set (response, ",", -1); if (!split) return NULL; bands = g_array_sized_new (FALSE, FALSE, sizeof (MMModemBand), g_strv_length (split)); for (i = 0; split[i]; i++) { guint32 band_value; MMModemBand band; band_value = (guint32)strtoul (split[i], NULL, 10); band = MM_MODEM_BAND_EUTRAN_1 - 1 + band_value; /* Due to a firmware issue, the modem may incorrectly includes 0 in the * bands response. We thus ignore any band value outside the range of * E-UTRAN operating bands. */ if (band >= MM_MODEM_BAND_EUTRAN_1 && band <= MM_MODEM_BAND_EUTRAN_44) g_array_append_val (bands, band); } g_strfreev (split); return bands; } /*****************************************************************************/ /* +CEER response parser */ gchar * mm_altair_parse_ceer_response (const gchar *response, GError **error) { g_autoptr(GRegex) r = NULL; g_autoptr(GMatchInfo) match_info = NULL; gchar *ceer_response = NULL; /* First accept an empty response as the no error case. Sometimes, the only * response to the AT+CEER query is an OK. */ if (g_strcmp0 ("", response) == 0) { return g_strdup (""); } /* The response we are interested in looks so: * +CEER: EPS_AND_NON_EPS_SERVICES_NOT_ALLOWED */ r = g_regex_new ("\\+CEER:\\s*(\\w*)?", G_REGEX_RAW, 0, NULL); g_assert (r != NULL); if (!g_regex_match (r, response, 0, &match_info)) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Could not parse +CEER response"); return NULL; } if (g_match_info_matches (match_info)) { ceer_response = mm_get_string_unquoted_from_match_info (match_info, 1); if (!ceer_response) ceer_response = g_strdup (""); } return ceer_response; } /*****************************************************************************/ /* %CGINFO="cid",1 response parser */ gint mm_altair_parse_cid (const gchar *response, GError **error) { g_autoptr(GRegex) regex = NULL; g_autoptr(GMatchInfo) match_info = NULL; guint cid = -1; regex = g_regex_new ("\\%CGINFO:\\s*(\\d+)", G_REGEX_RAW, 0, NULL); g_assert (regex); if (!g_regex_match_full (regex, response, strlen (response), 0, 0, &match_info, error)) return -1; if (!mm_get_uint_from_match_info (match_info, 1, &cid)) g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Failed to parse %%CGINFO=\"cid\",1 response"); return cid; } /*****************************************************************************/ /* %PCOINFO response parser */ MMPco * mm_altair_parse_vendor_pco_info (const gchar *pco_info, GError **error) { g_autoptr(GRegex) regex = NULL; g_autoptr(GMatchInfo) match_info = NULL; MMPco *pco = NULL; gint num_matches; if (!pco_info || !pco_info[0]) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS, "No PCO info given"); return NULL; } /* Expected %PCOINFO response: * * Solicited response: %PCOINFO:,[,[,]] * Unsolicited response: %PCOINFO:,[,] */ regex = g_regex_new ("\\%PCOINFO:(?:\\s*\\d+\\s*,)?(\\d+)\\s*(,([^,\\)]*),([0-9A-Fa-f]*))?", G_REGEX_DOLLAR_ENDONLY | G_REGEX_RAW, 0, NULL); g_assert (regex); if (!g_regex_match_full (regex, pco_info, strlen (pco_info), 0, 0, &match_info, error)) return NULL; num_matches = g_match_info_get_match_count (match_info); if (num_matches != 5) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Failed to parse substrings, number of matches: %d", num_matches); return NULL; } while (g_match_info_matches (match_info)) { guint pco_cid; g_autofree gchar *pco_id = NULL; g_autofree gchar *pco_payload = NULL; g_autofree guint8 *pco_payload_bytes = NULL; gsize pco_payload_bytes_len; guint8 pco_prefix[6]; GByteArray *pco_raw; gsize pco_raw_len; if (!mm_get_uint_from_match_info (match_info, 1, &pco_cid)) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't parse CID from PCO info: '%s'", pco_info); break; } /* We are only interested in IMS and Internet PDN PCO. */ if (pco_cid != MM_ALTAIR_IMS_PDN_CID && pco_cid != MM_ALTAIR_INTERNET_PDN_CID) { g_match_info_next (match_info, error); continue; } pco_id = mm_get_string_unquoted_from_match_info (match_info, 3); if (!pco_id) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't parse PCO ID from PCO info: '%s'", pco_info); break; } if (g_strcmp0 (pco_id, "FF00")) { g_match_info_next (match_info, error); continue; } pco_payload = mm_get_string_unquoted_from_match_info (match_info, 4); if (!pco_payload) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't parse PCO payload from PCO info: '%s'", pco_info); break; } pco_payload_bytes = mm_utils_hexstr2bin (pco_payload, -1, &pco_payload_bytes_len, error); if (!pco_payload_bytes) { g_prefix_error (error, "Invalid PCO payload from PCO info '%s': ", pco_info); break; } /* Protocol Configuration Options (PCO) is an information element with an * identifier (IEI) 0x27 and contains between 3 and 253 octets. See 3GPP TS * 24.008 for more details on PCO. * * NOTE: The standard uses one-based indexing, but to better correlate to the * code, zero-based indexing is used in the description hereinafter. * * Octet | Value * --------+-------------------------------------------- * 0 | PCO IEI (= 0x27) * 1 | Length of PCO contents (= total length - 2) * 2 | bit 7 : ext * | bit 6 to 3 : spare (= 0b0000) * | bit 2 to 0 : Configuration protocol * 3 to 4 | Element 1 ID * 5 | Length of element 1 contents * 6 to m | Element 1 contents * ... | */ pco_raw_len = sizeof (pco_prefix) + pco_payload_bytes_len; pco_prefix[0] = 0x27; pco_prefix[1] = pco_raw_len - 2; pco_prefix[2] = 0x80; /* Verizon uses element ID 0xFF00 for carrier-specific PCO content. */ pco_prefix[3] = 0xFF; pco_prefix[4] = 0x00; pco_prefix[5] = pco_payload_bytes_len; pco_raw = g_byte_array_sized_new (pco_raw_len); g_byte_array_append (pco_raw, pco_prefix, sizeof (pco_prefix)); g_byte_array_append (pco_raw, pco_payload_bytes, pco_payload_bytes_len); pco = mm_pco_new (); mm_pco_set_session_id (pco, pco_cid); mm_pco_set_complete (pco, TRUE); mm_pco_set_data (pco, pco_raw->data, pco_raw->len); break; } return pco; } ModemManager-1.23.4-dev/src/plugins/altair/mm-modem-helpers-altair-lte.h000066400000000000000000000023541456466623000260320ustar00rootroot00000000000000/* -*- 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) 2013 Google Inc. * */ #ifndef MM_MODEM_HELPERS_ALTAIR_H #define MM_MODEM_HELPERS_ALTAIR_H #include #define _LIBMM_INSIDE_MM #include /* Bands response parser */ GArray *mm_altair_parse_bands_response (const gchar *response); /* +CEER response parser */ gchar *mm_altair_parse_ceer_response (const gchar *response, GError **error); /* %CGINFO="cid",1 response parser */ gint mm_altair_parse_cid (const gchar *response, GError **error); /* %PCOINFO response parser */ MMPco *mm_altair_parse_vendor_pco_info (const gchar *pco_info, GError **error); #endif /* MM_MODEM_HELPERS_ALTAIR_H */ ModemManager-1.23.4-dev/src/plugins/altair/mm-plugin-altair-lte.c000066400000000000000000000070241456466623000245610ustar00rootroot00000000000000/* -*- 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. * * 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. * * Copyright (C) 2013 Altair Semiconductor * * Author: Ori Inbar */ #include #include #include "mm-plugin-common.h" #include "mm-private-boxed-types.h" #include "mm-broadband-modem-altair-lte.h" #include "mm-log.h" #define MM_TYPE_PLUGIN_ALTAIR_LTE mm_plugin_altair_lte_get_type () MM_DEFINE_PLUGIN (ALTAIR_LTE, altair_lte, AltairLte) /*****************************************************************************/ /* Custom commands for AT probing */ /* Increase the response timeout for probe commands since some altair modems take longer to respond after a reset. */ static const MMPortProbeAtCommand custom_at_probe[] = { { "AT", 7, mm_port_probe_response_processor_is_at }, { "AT", 7, mm_port_probe_response_processor_is_at }, { "AT", 7, mm_port_probe_response_processor_is_at }, { NULL } }; /*****************************************************************************/ static MMBaseModem * create_modem (MMPlugin *self, const gchar *uid, const gchar *physdev, const gchar **drivers, guint16 vendor, guint16 product, guint16 subsystem_vendor, GList *probes, GError **error) { return MM_BASE_MODEM (mm_broadband_modem_altair_lte_new (uid, physdev, drivers, mm_plugin_get_name (self), vendor, product)); } /*****************************************************************************/ MM_PLUGIN_NAMED_CREATOR_SCOPE MMPlugin * mm_plugin_create_altair_lte (void) { static const gchar *subsystems[] = { "tty", "net", NULL }; static const mm_uint16_pair products[] = { { 0x216f, 0x0047 }, /* Altair NPe */ { 0, 0 } }; return MM_PLUGIN ( g_object_new (MM_TYPE_PLUGIN_ALTAIR_LTE, MM_PLUGIN_NAME, MM_MODULE_NAME, MM_PLUGIN_ALLOWED_SUBSYSTEMS, subsystems, MM_PLUGIN_ALLOWED_PRODUCT_IDS, products, MM_PLUGIN_CUSTOM_AT_PROBE, custom_at_probe, MM_PLUGIN_ALLOWED_SINGLE_AT, TRUE, MM_PLUGIN_SEND_LF, TRUE, NULL)); } static void mm_plugin_altair_lte_init (MMPluginAltairLte *self) { } static void mm_plugin_altair_lte_class_init (MMPluginAltairLteClass *klass) { MMPluginClass *plugin_class = MM_PLUGIN_CLASS (klass); plugin_class->create_modem = create_modem; } ModemManager-1.23.4-dev/src/plugins/altair/tests/000077500000000000000000000000001456466623000216135ustar00rootroot00000000000000ModemManager-1.23.4-dev/src/plugins/altair/tests/test-modem-helpers-altair-lte.c000066400000000000000000000144041456466623000275340ustar00rootroot00000000000000/* -*- 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) 2013 Google Inc. * */ #include #include #include #include #include #include #include #define _LIBMM_INSIDE_MM #include #include "mm-modem-helpers-altair-lte.h" /*****************************************************************************/ /* Test bands response parsing */ static void test_parse_bands (void) { GArray *bands; bands = mm_altair_parse_bands_response (""); g_assert (bands != NULL); g_assert_cmpuint (bands->len, ==, 0); g_array_free (bands, TRUE); /* 0 and 45 are outside the range of E-UTRAN operating bands and should be ignored. */ bands = mm_altair_parse_bands_response ("0, 0, 1, 4,13,44,45"); g_assert (bands != NULL); g_assert_cmpuint (bands->len, ==, 4); g_assert_cmpuint (g_array_index (bands, MMModemBand, 0), ==, MM_MODEM_BAND_EUTRAN_1); g_assert_cmpuint (g_array_index (bands, MMModemBand, 1), ==, MM_MODEM_BAND_EUTRAN_4); g_assert_cmpuint (g_array_index (bands, MMModemBand, 2), ==, MM_MODEM_BAND_EUTRAN_13); g_assert_cmpuint (g_array_index (bands, MMModemBand, 3), ==, MM_MODEM_BAND_EUTRAN_44); g_array_free (bands, TRUE); } /*****************************************************************************/ /* Test +CEER responses */ typedef struct { const gchar *str; const gchar *result; } CeerTest; static const CeerTest ceer_tests[] = { { "", "" }, /* Special case, sometimes the response is empty, treat it as a good response. */ { "+CEER:", "" }, { "+CEER: EPS_AND_NON_EPS_SERVICES_NOT_ALLOWED", "EPS_AND_NON_EPS_SERVICES_NOT_ALLOWED" }, { "+CEER: NO_SUITABLE_CELLS_IN_TRACKING_AREA", "NO_SUITABLE_CELLS_IN_TRACKING_AREA" }, { "WRONG RESPONSE", NULL }, { NULL, NULL } }; static void test_ceer (void) { guint i; for (i = 0; ceer_tests[i].str; ++i) { GError *error = NULL; gchar *result; result = mm_altair_parse_ceer_response (ceer_tests[i].str, &error); if (ceer_tests[i].result) { g_assert_cmpstr (ceer_tests[i].result, ==, result); g_assert_no_error (error); g_free (result); } else { g_assert (result == NULL); g_assert (error != NULL); g_error_free (error); } } } static void test_parse_cid (void) { g_assert (mm_altair_parse_cid ("%CGINFO: 2", NULL) == 2); g_assert (mm_altair_parse_cid ("%CGINFO:blah", NULL) == -1); } /*****************************************************************************/ /* Test %PCOINFO responses */ typedef struct { const gchar *pco_info; guint32 session_id; gsize pco_data_size; guint8 pco_data[50]; } TestValidPcoInfo; static const TestValidPcoInfo good_pco_infos[] = { /* Valid PCO values */ { "%PCOINFO: 1,1,FF00,13018400", 1, 10, { 0x27, 0x08, 0x80, 0xFF, 0x00, 0x04, 0x13, 0x01, 0x84, 0x00 } }, { "%PCOINFO: 1,1,FF00,13018403", 1, 10, { 0x27, 0x08, 0x80, 0xFF, 0x00, 0x04, 0x13, 0x01, 0x84, 0x03 } }, { "%PCOINFO: 1,1,FF00,13018405", 1, 10, { 0x27, 0x08, 0x80, 0xFF, 0x00, 0x04, 0x13, 0x01, 0x84, 0x05 } }, { "%PCOINFO: 1,3,FF00,13018400", 3, 10, { 0x27, 0x08, 0x80, 0xFF, 0x00, 0x04, 0x13, 0x01, 0x84, 0x00 } }, { "%PCOINFO: 1,3,FF00,13018403", 3, 10, { 0x27, 0x08, 0x80, 0xFF, 0x00, 0x04, 0x13, 0x01, 0x84, 0x03 } }, { "%PCOINFO: 1,3,FF00,13018405", 3, 10, { 0x27, 0x08, 0x80, 0xFF, 0x00, 0x04, 0x13, 0x01, 0x84, 0x05 } }, { "%PCOINFO:1,FF00,13018400", 1, 10, { 0x27, 0x08, 0x80, 0xFF, 0x00, 0x04, 0x13, 0x01, 0x84, 0x00 } }, { "%PCOINFO:1,FF00,13018403", 1, 10, { 0x27, 0x08, 0x80, 0xFF, 0x00, 0x04, 0x13, 0x01, 0x84, 0x03 } }, { "%PCOINFO:1,FF00,13018405", 1, 10, { 0x27, 0x08, 0x80, 0xFF, 0x00, 0x04, 0x13, 0x01, 0x84, 0x05 } }, /* Different payload */ { "%PCOINFO: 1,3,FF00,130185", 3, 9, { 0x27, 0x07, 0x80, 0xFF, 0x00, 0x03, 0x13, 0x01, 0x85 } }, /* Multiline PCO info */ { "%PCOINFO: 1,2,FF00,13018400\r\n%PCOINFO: 1,3,FF00,13018403", 3, 10, { 0x27, 0x08, 0x80, 0xFF, 0x00, 0x04, 0x13, 0x01, 0x84, 0x03 } }, }; static const gchar *bad_pco_infos[] = { /* Different container */ "%PCOINFO: 1,3,F000,13018401", /* Ingood CID */ "%PCOINFO: 1,2,FF00,13018401", /* Bad PCO info */ "%PCOINFO: blah,blah,FF00,13018401", /* Bad PCO payload */ "%PCOINFO: 1,1,FF00,130184011", }; static void test_parse_vendor_pco_info (void) { MMPco *pco; guint i; for (i = 0; i < G_N_ELEMENTS (good_pco_infos); ++i) { const guint8 *pco_data; gsize pco_data_size; pco = mm_altair_parse_vendor_pco_info (good_pco_infos[i].pco_info, NULL); g_assert (pco != NULL); g_assert_cmpuint (mm_pco_get_session_id (pco), ==, good_pco_infos[i].session_id); g_assert (mm_pco_is_complete (pco)); pco_data = mm_pco_get_data (pco, &pco_data_size); g_assert (pco_data != NULL); g_assert_cmpuint (pco_data_size, ==, good_pco_infos[i].pco_data_size); g_assert_cmpint (memcmp (pco_data, good_pco_infos[i].pco_data, pco_data_size), ==, 0); g_object_unref (pco); } for (i = 0; i < G_N_ELEMENTS (bad_pco_infos); ++i) { pco = mm_altair_parse_vendor_pco_info (bad_pco_infos[i], NULL); g_assert (pco == NULL); } } int main (int argc, char **argv) { setlocale (LC_ALL, ""); g_test_init (&argc, &argv, NULL); g_test_add_func ("/MM/altair/parse_bands", test_parse_bands); g_test_add_func ("/MM/altair/ceer", test_ceer); g_test_add_func ("/MM/altair/parse_cid", test_parse_cid); g_test_add_func ("/MM/altair/parse_vendor_pco_info", test_parse_vendor_pco_info); return g_test_run (); } ModemManager-1.23.4-dev/src/plugins/anydata/000077500000000000000000000000001456466623000206165ustar00rootroot00000000000000ModemManager-1.23.4-dev/src/plugins/anydata/mm-broadband-modem-anydata.c000066400000000000000000000324041456466623000260260ustar00rootroot00000000000000/* -*- 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 - 2012 Red Hat, Inc. * Copyright (C) 2012 Aleksander Morgado */ #include #include #include #include #include #include #include "ModemManager.h" #include "mm-serial-parsers.h" #include "mm-log-object.h" #include "mm-modem-helpers.h" #include "mm-errors-types.h" #include "mm-base-modem-at.h" #include "mm-broadband-modem-anydata.h" #include "mm-iface-modem.h" #include "mm-iface-modem-cdma.h" static void iface_modem_init (MMIfaceModem *iface); static void iface_modem_cdma_init (MMIfaceModemCdma *iface); G_DEFINE_TYPE_EXTENDED (MMBroadbandModemAnydata, mm_broadband_modem_anydata, MM_TYPE_BROADBAND_MODEM, 0, G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM, iface_modem_init) G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_CDMA, iface_modem_cdma_init)) /*****************************************************************************/ /* Detailed registration state (CDMA interface) */ typedef struct { MMModemCdmaRegistrationState detailed_cdma1x_state; MMModemCdmaRegistrationState detailed_evdo_state; } DetailedRegistrationStateResults; static gboolean get_detailed_registration_state_finish (MMIfaceModemCdma *self, GAsyncResult *res, MMModemCdmaRegistrationState *detailed_cdma1x_state, MMModemCdmaRegistrationState *detailed_evdo_state, GError **error) { DetailedRegistrationStateResults *results; results = g_task_propagate_pointer (G_TASK (res), error); if (!results) return FALSE; *detailed_cdma1x_state = results->detailed_cdma1x_state; *detailed_evdo_state = results->detailed_evdo_state; g_free (results); return TRUE; } static void hstate_ready (MMIfaceModemCdma *self, GAsyncResult *res, GTask *task) { DetailedRegistrationStateResults *results; GError *error = NULL; const gchar *response; g_autoptr(GRegex) r = NULL; g_autoptr(GMatchInfo) match_info = NULL; results = g_task_get_task_data (task); response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error); if (error) { /* Leave superclass' reg state alone if AT*HSTATE isn't supported */ g_error_free (error); g_task_return_pointer (task, g_memdup (results, sizeof (*results)), g_free); g_object_unref (task); return; } response = mm_strip_tag (response, "*HSTATE:"); /* Format is ",,,,,,..." */ 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); g_assert (r != NULL); g_regex_match (r, response, 0, &match_info); if (g_match_info_get_match_count (match_info) >= 6) { guint val = 0; gint dbm = 0; /* dBm is between -106 (worst) and -20.7 (best) */ mm_get_int_from_match_info (match_info, 6, &dbm); /* Parse the EVDO radio state */ if (mm_get_uint_from_match_info (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) results->detailed_evdo_state = MM_MODEM_CDMA_REGISTRATION_STATE_REGISTERED; break; case 4: /* ACCESS */ case 5: /* CONNECT */ results->detailed_evdo_state = MM_MODEM_CDMA_REGISTRATION_STATE_REGISTERED; break; default: mm_obj_warn (self, "unknown *HSTATE (%d); assuming no service", val); /* fall through */ case 0: /* NO SERVICE */ case 1: /* ACQUISITION */ case 2: /* SYNC */ break; } } } g_task_return_pointer (task, g_memdup (results, sizeof (*results)), g_free); g_object_unref (task); } static void state_ready (MMIfaceModemCdma *self, GAsyncResult *res, GTask *task) { DetailedRegistrationStateResults *results; GError *error = NULL; const gchar *response; g_autoptr(GRegex) r = NULL; g_autoptr(GMatchInfo) match_info = NULL; response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error); if (error) { g_task_return_error (task, error); g_object_unref (task); return; } results = g_task_get_task_data (task); response = mm_strip_tag (response, "*STATE:"); /* Format is ",,,,,,..." */ 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); g_assert (r != NULL); g_regex_match (r, response, 0, &match_info); if (g_match_info_get_match_count (match_info) >= 6) { guint val = 0; gint dbm = 0; /* dBm is between -106 (worst) and -20.7 (best) */ mm_get_int_from_match_info (match_info, 6, &dbm); /* Parse the 1x radio state */ if (mm_get_uint_from_match_info (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) results->detailed_cdma1x_state = MM_MODEM_CDMA_REGISTRATION_STATE_REGISTERED; break; case 2: /* ACCESS */ case 3: /* PAGING */ case 4: /* TRAFFIC */ results->detailed_cdma1x_state = MM_MODEM_CDMA_REGISTRATION_STATE_REGISTERED; break; default: mm_obj_warn (self, "unknown *HSTATE (%d); assuming no service", val); /* fall through */ case 0: /* NO SERVICE */ break; } } } /* Try for EVDO state too */ mm_base_modem_at_command (MM_BASE_MODEM (self), "*HSTATE?", 3, FALSE, (GAsyncReadyCallback)hstate_ready, task); } static void get_detailed_registration_state (MMIfaceModemCdma *self, MMModemCdmaRegistrationState cdma1x_state, MMModemCdmaRegistrationState evdo_state, GAsyncReadyCallback callback, gpointer user_data) { DetailedRegistrationStateResults *results; GTask *task; results = g_new (DetailedRegistrationStateResults, 1); results->detailed_cdma1x_state = cdma1x_state; results->detailed_evdo_state = evdo_state; task = g_task_new (self, NULL, callback, user_data); g_task_set_task_data (task, results, g_free); mm_base_modem_at_command (MM_BASE_MODEM (self), "*STATE?", 3, FALSE, (GAsyncReadyCallback)state_ready, task); } /*****************************************************************************/ /* Reset (Modem interface) */ static gboolean reset_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { return !!mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, error); } static void reset (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data) { mm_base_modem_at_command (MM_BASE_MODEM (self), "*RESET", 3, FALSE, callback, user_data); } /*****************************************************************************/ /* Setup ports (Broadband modem class) */ static void setup_ports (MMBroadbandModem *self) { MMPortSerialAt *ports[2]; g_autoptr(GRegex) active_regex = NULL; g_autoptr(GRegex) inactive_regex = NULL; g_autoptr(GRegex) dormant_regex = NULL; g_autoptr(GRegex) offline_regex = NULL; g_autoptr(GRegex) regreq_regex = NULL; g_autoptr(GRegex) authreq_regex = NULL; guint i; /* Call parent's setup ports first always */ MM_BROADBAND_MODEM_CLASS (mm_broadband_modem_anydata_parent_class)->setup_ports (self); ports[0] = mm_base_modem_peek_port_primary (MM_BASE_MODEM (self)); ports[1] = mm_base_modem_peek_port_secondary (MM_BASE_MODEM (self)); /* Data call has connected */ active_regex = g_regex_new ("\\r\\n\\*ACTIVE:(.*)\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); /* Data call disconnected */ inactive_regex = g_regex_new ("\\r\\n\\*INACTIVE:(.*)\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); /* Modem is now dormant */ dormant_regex = g_regex_new ("\\r\\n\\*DORMANT:(.*)\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); /* Network acquisition fail */ offline_regex = g_regex_new ("\\r\\n\\*OFFLINE:(.*)\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); /* Registration fail */ regreq_regex = g_regex_new ("\\r\\n\\*REGREQ:(.*)\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); /* Authentication fail */ authreq_regex = g_regex_new ("\\r\\n\\*AUTHREQ:(.*)\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); /* Now reset the unsolicited messages */ for (i = 0; i < G_N_ELEMENTS (ports); i++) { if (!ports[i]) continue; /* Data state notifications */ mm_port_serial_at_add_unsolicited_msg_handler (MM_PORT_SERIAL_AT (ports[i]), active_regex, NULL, NULL, NULL); mm_port_serial_at_add_unsolicited_msg_handler (MM_PORT_SERIAL_AT (ports[i]), inactive_regex, NULL, NULL, NULL); mm_port_serial_at_add_unsolicited_msg_handler (MM_PORT_SERIAL_AT (ports[i]), dormant_regex, NULL, NULL, NULL); /* Abnormal state notifications * * FIXME: set 1X/EVDO registration state to UNKNOWN when these * notifications are received? */ mm_port_serial_at_add_unsolicited_msg_handler (MM_PORT_SERIAL_AT (ports[i]), offline_regex, NULL, NULL, NULL); mm_port_serial_at_add_unsolicited_msg_handler (MM_PORT_SERIAL_AT (ports[i]), regreq_regex, NULL, NULL, NULL); mm_port_serial_at_add_unsolicited_msg_handler (MM_PORT_SERIAL_AT (ports[i]), authreq_regex, NULL, NULL, NULL); } } /*****************************************************************************/ MMBroadbandModemAnydata * mm_broadband_modem_anydata_new (const gchar *device, const gchar *physdev, const gchar **drivers, const gchar *plugin, guint16 vendor_id, guint16 product_id) { return g_object_new (MM_TYPE_BROADBAND_MODEM_ANYDATA, MM_BASE_MODEM_DEVICE, device, MM_BASE_MODEM_PHYSDEV, physdev, MM_BASE_MODEM_DRIVERS, drivers, MM_BASE_MODEM_PLUGIN, plugin, MM_BASE_MODEM_VENDOR_ID, vendor_id, MM_BASE_MODEM_PRODUCT_ID, product_id, /* Generic bearer supports TTY only */ MM_BASE_MODEM_DATA_NET_SUPPORTED, FALSE, MM_BASE_MODEM_DATA_TTY_SUPPORTED, TRUE, NULL); } static void mm_broadband_modem_anydata_init (MMBroadbandModemAnydata *self) { } static void iface_modem_init (MMIfaceModem *iface) { iface->reset = reset; iface->reset_finish = reset_finish; } static void iface_modem_cdma_init (MMIfaceModemCdma *iface) { iface->get_cdma1x_serving_system = NULL; iface->get_cdma1x_serving_system_finish = NULL; iface->get_detailed_registration_state = get_detailed_registration_state; iface->get_detailed_registration_state_finish = get_detailed_registration_state_finish; } static void mm_broadband_modem_anydata_class_init (MMBroadbandModemAnydataClass *klass) { MMBroadbandModemClass *broadband_modem_class = MM_BROADBAND_MODEM_CLASS (klass); broadband_modem_class->setup_ports = setup_ports; } ModemManager-1.23.4-dev/src/plugins/anydata/mm-broadband-modem-anydata.h000066400000000000000000000046551456466623000260420ustar00rootroot00000000000000/* -*- 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 - 2012 Red Hat, Inc. * Copyright (C) 2012 - Aleksander Morgado */ #ifndef MM_BROADBAND_MODEM_ANYDATA_H #define MM_BROADBAND_MODEM_ANYDATA_H #include "mm-broadband-modem.h" #define MM_TYPE_BROADBAND_MODEM_ANYDATA (mm_broadband_modem_anydata_get_type ()) #define MM_BROADBAND_MODEM_ANYDATA(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_BROADBAND_MODEM_ANYDATA, MMBroadbandModemAnydata)) #define MM_BROADBAND_MODEM_ANYDATA_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_BROADBAND_MODEM_ANYDATA, MMBroadbandModemAnydataClass)) #define MM_IS_BROADBAND_MODEM_ANYDATA(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_BROADBAND_MODEM_ANYDATA)) #define MM_IS_BROADBAND_MODEM_ANYDATA_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_BROADBAND_MODEM_ANYDATA)) #define MM_BROADBAND_MODEM_ANYDATA_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_BROADBAND_MODEM_ANYDATA, MMBroadbandModemAnydataClass)) typedef struct _MMBroadbandModemAnydata MMBroadbandModemAnydata; typedef struct _MMBroadbandModemAnydataClass MMBroadbandModemAnydataClass; struct _MMBroadbandModemAnydata { MMBroadbandModem parent; }; struct _MMBroadbandModemAnydataClass{ MMBroadbandModemClass parent; }; GType mm_broadband_modem_anydata_get_type (void); MMBroadbandModemAnydata *mm_broadband_modem_anydata_new (const gchar *device, const gchar *physdev, const gchar **drivers, const gchar *plugin, guint16 vendor_id, guint16 product_id); #endif /* MM_BROADBAND_MODEM_ANYDATA_H */ ModemManager-1.23.4-dev/src/plugins/anydata/mm-plugin-anydata.c000066400000000000000000000066431456466623000243170ustar00rootroot00000000000000/* -*- 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 - 2012 Red Hat, Inc. * Copyright (C) 2012 Aleksander Morgado */ #include #include #define _LIBMM_INSIDE_MM #include #include "mm-log-object.h" #include "mm-plugin-common.h" #include "mm-broadband-modem-anydata.h" #if defined WITH_QMI #include "mm-broadband-modem-qmi.h" #endif #define MM_TYPE_PLUGIN_ANYDATA mm_plugin_anydata_get_type () MM_DEFINE_PLUGIN (ANYDATA, anydata, Anydata) /*****************************************************************************/ static MMBaseModem * create_modem (MMPlugin *self, const gchar *uid, const gchar *physdev, const gchar **drivers, guint16 vendor, guint16 product, guint16 subsystem_vendor, GList *probes, GError **error) { #if defined WITH_QMI if (mm_port_probe_list_has_qmi_port (probes)) { mm_obj_dbg (self, "QMI-powered AnyDATA modem found..."); return MM_BASE_MODEM (mm_broadband_modem_qmi_new (uid, physdev, drivers, mm_plugin_get_name (self), vendor, product)); } #endif return MM_BASE_MODEM (mm_broadband_modem_anydata_new (uid, physdev, drivers, mm_plugin_get_name (self), vendor, product)); } /*****************************************************************************/ MM_PLUGIN_NAMED_CREATOR_SCOPE MMPlugin * mm_plugin_create_anydata (void) { static const gchar *subsystems[] = { "tty", "net", "usbmisc", NULL }; static const guint16 vendor_ids[] = { 0x16d5, 0 }; return MM_PLUGIN ( g_object_new (MM_TYPE_PLUGIN_ANYDATA, MM_PLUGIN_NAME, MM_MODULE_NAME, MM_PLUGIN_ALLOWED_SUBSYSTEMS, subsystems, MM_PLUGIN_ALLOWED_VENDOR_IDS, vendor_ids, MM_PLUGIN_ALLOWED_AT, TRUE, MM_PLUGIN_REQUIRED_QCDM, TRUE, MM_PLUGIN_ALLOWED_QMI, TRUE, NULL)); } static void mm_plugin_anydata_init (MMPluginAnydata *self) { } static void mm_plugin_anydata_class_init (MMPluginAnydataClass *klass) { MMPluginClass *plugin_class = MM_PLUGIN_CLASS (klass); plugin_class->create_modem = create_modem; } ModemManager-1.23.4-dev/src/plugins/broadmobi/000077500000000000000000000000001456466623000211335ustar00rootroot00000000000000ModemManager-1.23.4-dev/src/plugins/broadmobi/77-mm-broadmobi-port-types.rules000066400000000000000000000016501456466623000271350ustar00rootroot00000000000000# do not edit this file, it will be overwritten on update ACTION!="add|change|move|bind", GOTO="mm_broadmobi_port_types_end" SUBSYSTEMS=="usb", ATTRS{idVendor}=="2020", GOTO="mm_broadmobi_port_types" GOTO="mm_broadmobi_port_types_end" LABEL="mm_broadmobi_port_types" SUBSYSTEMS=="usb", ATTRS{bInterfaceNumber}=="?*", ENV{.MM_USBIFNUM}="$attr{bInterfaceNumber}" # BroadMobi BM818 ATTRS{idVendor}=="2020", ATTRS{idProduct}=="2060", ENV{.MM_USBIFNUM}=="00", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_QCDM}="1" ATTRS{idVendor}=="2020", ATTRS{idProduct}=="2060", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1" ATTRS{idVendor}=="2020", ATTRS{idProduct}=="2060", ENV{.MM_USBIFNUM}=="02", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1" ATTRS{idVendor}=="2020", ATTRS{idProduct}=="2060", ENV{.MM_USBIFNUM}=="03", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1" LABEL="mm_broadmobi_port_types_end"ModemManager-1.23.4-dev/src/plugins/broadmobi/mm-plugin-broadmobi.c000066400000000000000000000065311456466623000251450ustar00rootroot00000000000000/* -*- 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) 2020 Aleksander Morgado */ #include #include #define _LIBMM_INSIDE_MM #include #include "mm-log-object.h" #include "mm-plugin-common.h" #include "mm-broadband-modem.h" #if defined WITH_QMI # include "mm-broadband-modem-qmi.h" #endif #define MM_TYPE_PLUGIN_BROADMOBI mm_plugin_broadmobi_get_type () MM_DEFINE_PLUGIN (BROADMOBI, broadmobi, Broadmobi) /*****************************************************************************/ static MMBaseModem * create_modem (MMPlugin *self, const gchar *uid, const gchar *physdev, const gchar **drivers, guint16 vendor, guint16 product, guint16 subsystem_vendor, GList *probes, GError **error) { #if defined WITH_QMI if (mm_port_probe_list_has_qmi_port (probes)) { mm_obj_dbg (self, "QMI-powered BroadMobi modem found..."); return MM_BASE_MODEM (mm_broadband_modem_qmi_new (uid, physdev, drivers, mm_plugin_get_name (self), vendor, product)); } #endif return MM_BASE_MODEM (mm_broadband_modem_new (uid, physdev, drivers, mm_plugin_get_name (self), vendor, product)); } /*****************************************************************************/ MM_PLUGIN_NAMED_CREATOR_SCOPE MMPlugin * mm_plugin_create_broadmobi (void) { static const gchar *subsystems[] = { "tty", "net", "usbmisc", NULL }; static const guint16 vendor_ids[] = { 0x2020, 0 }; return MM_PLUGIN ( g_object_new (MM_TYPE_PLUGIN_BROADMOBI, MM_PLUGIN_NAME, MM_MODULE_NAME, MM_PLUGIN_ALLOWED_SUBSYSTEMS, subsystems, MM_PLUGIN_ALLOWED_VENDOR_IDS, vendor_ids, MM_PLUGIN_ALLOWED_AT, TRUE, MM_PLUGIN_ALLOWED_QCDM, TRUE, MM_PLUGIN_ALLOWED_QMI, TRUE, NULL)); } static void mm_plugin_broadmobi_init (MMPluginBroadmobi *self) { } static void mm_plugin_broadmobi_class_init (MMPluginBroadmobiClass *klass) { MMPluginClass *plugin_class = MM_PLUGIN_CLASS (klass); plugin_class->create_modem = create_modem; } ModemManager-1.23.4-dev/src/plugins/cinterion/000077500000000000000000000000001456466623000211675ustar00rootroot00000000000000ModemManager-1.23.4-dev/src/plugins/cinterion/77-mm-cinterion-port-types.rules000066400000000000000000000104531456466623000272260ustar00rootroot00000000000000# do not edit this file, it will be overwritten on update ACTION!="add|change|move|bind", GOTO="mm_cinterion_port_types_end" SUBSYSTEMS=="usb", ATTRS{idVendor}=="1e2d", GOTO="mm_cinterion_port_types" GOTO="mm_cinterion_port_types_end" LABEL="mm_cinterion_port_types" SUBSYSTEMS=="usb", ATTRS{bInterfaceNumber}=="?*", ENV{.MM_USBIFNUM}="$attr{bInterfaceNumber}" # PHS8 ATTRS{idVendor}=="1e2d", ATTRS{idProduct}=="0053", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_GPS}="1" # PLS8 port types # ttyACM0 (if #0): AT port # ttyACM1 (if #2): AT port # ttyACM2 (if #4): GPS data port # ttyACM3 (if #6): unknown # ttyACM4 (if #8): unknown ATTRS{idVendor}=="1e2d", ATTRS{idProduct}=="0061", ENV{.MM_USBIFNUM}=="00", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PPP}="1" ATTRS{idVendor}=="1e2d", ATTRS{idProduct}=="0061", ENV{.MM_USBIFNUM}=="02", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1" ATTRS{idVendor}=="1e2d", ATTRS{idProduct}=="0061", ENV{.MM_USBIFNUM}=="04", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_GPS}="1" ATTRS{idVendor}=="1e2d", ATTRS{idProduct}=="0061", ENV{.MM_USBIFNUM}=="06", ENV{ID_MM_PORT_IGNORE}="1" ATTRS{idVendor}=="1e2d", ATTRS{idProduct}=="0061", ENV{.MM_USBIFNUM}=="08", ENV{ID_MM_PORT_IGNORE}="1" # PLS62 family non-mbim enumeration uses alternate settings for 2G band management ATTRS{idVendor}=="1e2d", ATTRS{idProduct}=="005b", ENV{ID_MM_CINTERION_MODEM_FAMILY}="imt" # PLS62 family non-mbim enumeration # ttyACM0 (if #0): AT port # ttyACM1 (if #2): AT port # ttyACM2 (if #4): can be AT or GNSS in some models # ttyACM3 (if #6): AT port (but just ignore) # ttyACM4 (if #8): DIAG/QCDM ATTRS{idVendor}=="1e2d", ATTRS{idProduct}=="005b", ENV{.MM_USBIFNUM}=="00", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1" ATTRS{idVendor}=="1e2d", ATTRS{idProduct}=="005b", ENV{.MM_USBIFNUM}=="02", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1" ATTRS{idVendor}=="1e2d", ATTRS{idProduct}=="005b", ENV{.MM_USBIFNUM}=="04", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_GPS}="1" ATTRS{idVendor}=="1e2d", ATTRS{idProduct}=="005b", ENV{.MM_USBIFNUM}=="06", ENV{ID_MM_PORT_IGNORE}="1" ATTRS{idVendor}=="1e2d", ATTRS{idProduct}=="005b", ENV{.MM_USBIFNUM}=="08", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_QCDM}="1" # PLS62 family mbim enumeration # ttyACM0 (if #0): AT port # ttyACM1 (if #2): AT port # ttyACM2 (if #4): can be AT or GNSS in some models # ttyACM3 (if #6): AT port (but just ignore) # ttyACM4 (if #8): DIAG/QCDM ATTRS{idVendor}=="1e2d", ATTRS{idProduct}=="005d", ENV{.MM_USBIFNUM}=="00", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1" ATTRS{idVendor}=="1e2d", ATTRS{idProduct}=="005d", ENV{.MM_USBIFNUM}=="02", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1" ATTRS{idVendor}=="1e2d", ATTRS{idProduct}=="005d", ENV{.MM_USBIFNUM}=="04", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_GPS}="1" ATTRS{idVendor}=="1e2d", ATTRS{idProduct}=="005d", ENV{.MM_USBIFNUM}=="06", ENV{ID_MM_PORT_IGNORE}="1" ATTRS{idVendor}=="1e2d", ATTRS{idProduct}=="005d", ENV{.MM_USBIFNUM}=="08", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_QCDM}="1" # PLS63 # ttyACM0 (if #0): AT port # ttyACM1 (if #2): AT port # ttyACM2 (if #4): GPS data port # ttyACM3 (if #6): DIAG/QCDM ATTRS{idVendor}=="1e2d", ATTRS{idProduct}=="0069", ENV{.MM_USBIFNUM}=="00", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1" ATTRS{idVendor}=="1e2d", ATTRS{idProduct}=="0069", ENV{.MM_USBIFNUM}=="02", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1" ATTRS{idVendor}=="1e2d", ATTRS{idProduct}=="0069", ENV{.MM_USBIFNUM}=="04", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_GPS}="1" ATTRS{idVendor}=="1e2d", ATTRS{idProduct}=="0069", ENV{.MM_USBIFNUM}=="06", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_QCDM}="1" # PLS83 # ttyACM0 (if #0): AT port # ttyACM1 (if #2): AT port # ttyACM2 (if #4): GPS data port # ttyACM3 (if #6): DIAG/QCDM ATTRS{idVendor}=="1e2d", ATTRS{idProduct}=="006F", ENV{.MM_USBIFNUM}=="00", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1" ATTRS{idVendor}=="1e2d", ATTRS{idProduct}=="006F", ENV{.MM_USBIFNUM}=="02", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1" ATTRS{idVendor}=="1e2d", ATTRS{idProduct}=="006F", ENV{.MM_USBIFNUM}=="04", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_GPS}="1" ATTRS{idVendor}=="1e2d", ATTRS{idProduct}=="006F", ENV{.MM_USBIFNUM}=="06", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_QCDM}="1" LABEL="mm_cinterion_port_types_end" ModemManager-1.23.4-dev/src/plugins/cinterion/mm-broadband-bearer-cinterion.c000066400000000000000000000656071456466623000271220ustar00rootroot00000000000000/* -*- 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) 2016 Trimble Navigation Limited * Author: Matthew Stanger */ #include #include #include #include #include #include #include #include #include "mm-base-modem-at.h" #include "mm-broadband-bearer-cinterion.h" #include "mm-log-object.h" #include "mm-modem-helpers.h" #include "mm-modem-helpers-cinterion.h" #include "mm-daemon-enums-types.h" G_DEFINE_TYPE (MMBroadbandBearerCinterion, mm_broadband_bearer_cinterion, MM_TYPE_BROADBAND_BEARER) /*****************************************************************************/ /* WWAN interface mapping */ typedef struct { guint swwan_index; guint usb_iface_num; } UsbInterfaceConfig; /* Map SWWAN index, USB interface number and preferred PDP context. * * The expected USB interface mapping is: * INTERFACE=usb0 -> ID_USB_INTERFACE_NUM=0a * INTERFACE=usb1 -> ID_USB_INTERFACE_NUM=0c * INTERFACE=usb0 -> ID_USB_INTERFACE_NUM=08 (PLSx3w) */ static const UsbInterfaceConfig usb_interface_configs[] = { { .swwan_index = 1, .usb_iface_num = 0x0a, }, { .swwan_index = 2, .usb_iface_num = 0x0c, }, { .swwan_index = 1, .usb_iface_num = 0x08, }, }; static gint get_usb_interface_config_index (MMPort *data, GError **error) { guint usb_iface_num; guint i; usb_iface_num = (guint) mm_kernel_device_get_interface_number (mm_port_peek_kernel_device (data)); for (i = 0; i < G_N_ELEMENTS (usb_interface_configs); i++) { if (usb_interface_configs[i].usb_iface_num == usb_iface_num) return (gint) i; } g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Unsupported WWAN interface: unexpected interface number: 0x%02x", usb_iface_num); return -1; } /*****************************************************************************/ /* Connection status loading * NOTE: only CONNECTED or DISCONNECTED should be reported here. */ static MMBearerConnectionStatus load_connection_status_finish (MMBaseBearer *bearer, GAsyncResult *res, GError **error) { GError *inner_error = NULL; gssize aux; aux = g_task_propagate_int (G_TASK (res), &inner_error); if (inner_error) { g_propagate_error (error, inner_error); return MM_BEARER_CONNECTION_STATUS_UNKNOWN; } return (MMBearerConnectionStatus) aux; } typedef struct { guint cid; guint retries; gboolean delay; gboolean retry; } LoadConnectionContext; static void load_connection_context_free (LoadConnectionContext *ctx) { g_slice_free (LoadConnectionContext, ctx); } static gboolean swwan_check_status (GTask *task); static void swwan_check_status_ready (MMBaseModem *modem, GAsyncResult *res, GTask *task) { MMBroadbandBearerCinterion *self; const gchar *response; GError *error = NULL; MMBearerConnectionStatus status; LoadConnectionContext *ctx; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); response = mm_base_modem_at_command_finish (modem, res, &error); if (!response) { g_task_return_error (task, error); goto out; } status = mm_cinterion_parse_swwan_response (response, ctx->cid, self, &error); if (status == MM_BEARER_CONNECTION_STATUS_UNKNOWN) { g_task_return_error (task, error); goto out; } else if (ctx->retry && status == MM_BEARER_CONNECTION_STATUS_DISCONNECTED) { mm_obj_dbg (self, "check status retry"); if (ctx->retries == 0) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "CID %u status check retry exceeded", ctx->cid); goto out; } else { if (ctx->delay) { g_timeout_add_seconds (1, (GSourceFunc)swwan_check_status, task); } else { g_idle_add ((GSourceFunc)swwan_check_status, task); } ctx->retries--; return; } } g_assert (status == MM_BEARER_CONNECTION_STATUS_DISCONNECTED || status == MM_BEARER_CONNECTION_STATUS_CONNECTED); g_task_return_int (task, (gssize) status); out: g_object_unref (task); } static gboolean swwan_check_status (GTask *task) { MMBroadbandBearerCinterion *bearer; g_autoptr(MMBaseModem) modem = NULL; bearer = g_task_get_source_object (task); g_object_get (bearer, MM_BASE_BEARER_MODEM, &modem, NULL); mm_base_modem_at_command (modem, "^SWWAN?", 5, FALSE, (GAsyncReadyCallback) swwan_check_status_ready, task); return G_SOURCE_REMOVE; } static void load_connection_status_by_cid (MMBroadbandBearerCinterion *bearer, gint cid, gboolean delay, gboolean retry, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; LoadConnectionContext *ctx; task = g_task_new (bearer, NULL, callback, user_data); if (cid == MM_3GPP_PROFILE_ID_UNKNOWN) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Unknown profile id to check connection status"); g_object_unref (task); return; } ctx = g_slice_new0 (LoadConnectionContext); g_task_set_task_data (task, ctx, (GDestroyNotify) load_connection_context_free); /* Setup context */ ctx->cid = cid; ctx->retries = 5; ctx->delay = delay; ctx->retry = retry; /* Some modems require a delay before querying the SWWAN status * This is only needed for step DIAL_3GPP_CONTEXT_STEP_VALIDATE_CONNECTION * and DISCONNECT_3GPP_CONTEXT_STEP_CONNECTION_STATUS. */ if (delay) { g_timeout_add_seconds (1, (GSourceFunc)swwan_check_status, task); } else { g_idle_add ((GSourceFunc)swwan_check_status, task); } } static void load_connection_status (MMBaseBearer *bearer, GAsyncReadyCallback callback, gpointer user_data) { load_connection_status_by_cid (MM_BROADBAND_BEARER_CINTERION (bearer), mm_base_bearer_get_profile_id (bearer), FALSE, FALSE, callback, user_data); } /******************************************************************************/ /* Dial 3GPP */ typedef enum { DIAL_3GPP_CONTEXT_STEP_FIRST = 0, DIAL_3GPP_CONTEXT_STEP_AUTH, DIAL_3GPP_CONTEXT_STEP_START_SWWAN, DIAL_3GPP_CONTEXT_STEP_VALIDATE_CONNECTION, DIAL_3GPP_CONTEXT_STEP_LAST, } Dial3gppContextStep; typedef struct { MMBroadbandBearerCinterion *self; MMBaseModem *modem; MMPortSerialAt *primary; guint cid; MMPort *data; gint usb_interface_config_index; Dial3gppContextStep step; } Dial3gppContext; static void dial_3gpp_context_free (Dial3gppContext *ctx) { g_object_unref (ctx->modem); g_object_unref (ctx->self); g_object_unref (ctx->primary); g_clear_object (&ctx->data); g_slice_free (Dial3gppContext, ctx); } static MMPort * dial_3gpp_finish (MMBroadbandBearer *self, GAsyncResult *res, GError **error) { return MM_PORT (g_task_propagate_pointer (G_TASK (res), error)); } static void dial_3gpp_context_step (GTask *task); static void dial_connection_status_ready (MMBroadbandBearerCinterion *self, GAsyncResult *res, GTask *task) { MMBearerConnectionStatus status; Dial3gppContext *ctx; GError *error = NULL; ctx = (Dial3gppContext *) g_task_get_task_data (task); status = load_connection_status_finish (MM_BASE_BEARER (self), res, &error); if (status == MM_BEARER_CONNECTION_STATUS_UNKNOWN) { g_task_return_error (task, error); g_object_unref (task); return; } if (status == MM_BEARER_CONNECTION_STATUS_DISCONNECTED) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "CID %u is reported disconnected", ctx->cid); g_object_unref (task); return; } g_assert (status == MM_BEARER_CONNECTION_STATUS_CONNECTED); /* Go to next step */ ctx->step++; dial_3gpp_context_step (task); } static void common_dial_operation_ready (MMBaseModem *modem, GAsyncResult *res, GTask *task) { Dial3gppContext *ctx; GError *error = NULL; ctx = (Dial3gppContext *) g_task_get_task_data (task); if (!mm_base_modem_at_command_full_finish (modem, res, &error)) { g_task_return_error (task, error); g_object_unref (task); return; } /* Go to next step */ ctx->step++; dial_3gpp_context_step (task); } static void swwan_dial_operation_ready (MMBaseModem *modem, GAsyncResult *res, MMBroadbandBearerCinterion *self) /* full ref! */ { GError *error = NULL; if (!mm_base_modem_at_command_full_finish (modem, res, &error)) { mm_obj_warn (self, "data connection attempt failed: %s", error->message); mm_base_bearer_report_connection_status (MM_BASE_BEARER (self), MM_BEARER_CONNECTION_STATUS_DISCONNECTED); g_error_free (error); } g_object_unref (self); } static void handle_cancel_dial (GTask *task) { Dial3gppContext *ctx; gchar *command; ctx = (Dial3gppContext *) g_task_get_task_data (task); /* Disconnect, may not succeed. Will not check response on cancel */ command = g_strdup_printf ("^SWWAN=0,%u,%u", ctx->cid, usb_interface_configs[ctx->usb_interface_config_index].swwan_index); mm_base_modem_at_command_full (ctx->modem, ctx->primary, command, 3, FALSE, FALSE, NULL, NULL, NULL); g_free (command); } static void dial_3gpp_context_step (GTask *task) { MMBroadbandBearerCinterion *self; Dial3gppContext *ctx; MMCinterionModemFamily modem_family; gboolean default_swwan_behavior; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); /* Check for cancellation */ if (g_task_return_error_if_cancelled (task)) { handle_cancel_dial (task); g_object_unref (task); return; } modem_family = mm_broadband_modem_cinterion_get_family (MM_BROADBAND_MODEM_CINTERION (ctx->modem)); default_swwan_behavior = modem_family == MM_CINTERION_MODEM_FAMILY_DEFAULT; switch (ctx->step) { case DIAL_3GPP_CONTEXT_STEP_FIRST: ctx->step++; /* fall through */ case DIAL_3GPP_CONTEXT_STEP_AUTH: { g_autofree gchar *command = NULL; command = mm_cinterion_build_auth_string (self, modem_family, mm_base_bearer_peek_config (MM_BASE_BEARER (ctx->self)), ctx->cid); if (command) { mm_obj_dbg (self, "dial step %u/%u: authenticating...", ctx->step, DIAL_3GPP_CONTEXT_STEP_LAST); /* Send SGAUTH write, if User & Pass are provided. * advance to next state by callback */ mm_base_modem_at_command_full (ctx->modem, ctx->primary, command, 10, FALSE, FALSE, NULL, (GAsyncReadyCallback) common_dial_operation_ready, task); return; } mm_obj_dbg (self, "dial step %u/%u: authentication not required", ctx->step, DIAL_3GPP_CONTEXT_STEP_LAST); ctx->step++; } /* fall through */ case DIAL_3GPP_CONTEXT_STEP_START_SWWAN: { g_autofree gchar *command = NULL; mm_obj_dbg (self, "dial step %u/%u: starting SWWAN interface %u connection...", ctx->step, DIAL_3GPP_CONTEXT_STEP_LAST, usb_interface_configs[ctx->usb_interface_config_index].swwan_index); command = g_strdup_printf ("^SWWAN=1,%u,%u", ctx->cid, usb_interface_configs[ctx->usb_interface_config_index].swwan_index); if (default_swwan_behavior) { mm_base_modem_at_command_full (ctx->modem, ctx->primary, command, MM_BASE_BEARER_DEFAULT_CONNECTION_TIMEOUT, FALSE, FALSE, NULL, (GAsyncReadyCallback) common_dial_operation_ready, task); return; } /* We "jump" to the last step here here since the modem expects the * DHCP discover packet while ^SWWAN runs. If the command fails, * we'll mark the bearer disconnected later in the callback. */ mm_base_modem_at_command_full (ctx->modem, ctx->primary, command, MM_BASE_BEARER_DEFAULT_CONNECTION_TIMEOUT, FALSE, FALSE, NULL, (GAsyncReadyCallback) swwan_dial_operation_ready, g_object_ref (self)); ctx->step = DIAL_3GPP_CONTEXT_STEP_LAST; dial_3gpp_context_step (task); return; } case DIAL_3GPP_CONTEXT_STEP_VALIDATE_CONNECTION: g_assert (default_swwan_behavior); mm_obj_dbg (self, "dial step %u/%u: checking SWWAN interface %u status...", ctx->step, DIAL_3GPP_CONTEXT_STEP_LAST, usb_interface_configs[ctx->usb_interface_config_index].swwan_index); load_connection_status_by_cid (ctx->self, (gint) ctx->cid, TRUE, TRUE, (GAsyncReadyCallback) dial_connection_status_ready, task); return; case DIAL_3GPP_CONTEXT_STEP_LAST: mm_obj_dbg (self, "dial step %u/%u: finished", ctx->step, DIAL_3GPP_CONTEXT_STEP_LAST); g_task_return_pointer (task, g_object_ref (ctx->data), g_object_unref); g_object_unref (task); return; default: g_assert_not_reached (); } } static void dial_3gpp (MMBroadbandBearer *self, MMBaseModem *modem, MMPortSerialAt *primary, guint cid, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; Dial3gppContext *ctx; GError *error = NULL; g_assert (primary != NULL); /* Setup task and create connection context */ task = g_task_new (self, cancellable, callback, user_data); ctx = g_slice_new0 (Dial3gppContext); g_task_set_task_data (task, ctx, (GDestroyNotify) dial_3gpp_context_free); /* Setup context */ ctx->self = MM_BROADBAND_BEARER_CINTERION (g_object_ref (self)); ctx->modem = g_object_ref (modem); ctx->primary = g_object_ref (primary); ctx->cid = cid; ctx->step = DIAL_3GPP_CONTEXT_STEP_FIRST; /* Get a net port to setup the connection on */ ctx->data = mm_base_modem_peek_best_data_port (MM_BASE_MODEM (modem), MM_PORT_TYPE_NET); if (!ctx->data) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_NOT_FOUND, "No valid data port found to launch connection"); g_object_unref (task); return; } g_object_ref (ctx->data); /* Validate configuration */ ctx->usb_interface_config_index = get_usb_interface_config_index (ctx->data, &error); if (ctx->usb_interface_config_index < 0) { g_task_return_error (task, error); g_object_unref (task); return; } /* Run! */ dial_3gpp_context_step (task); } /*****************************************************************************/ /* Disconnect 3GPP */ typedef enum { DISCONNECT_3GPP_CONTEXT_STEP_FIRST, DISCONNECT_3GPP_CONTEXT_STEP_STOP_SWWAN, DISCONNECT_3GPP_CONTEXT_STEP_CONNECTION_STATUS, DISCONNECT_3GPP_CONTEXT_STEP_LAST, } Disconnect3gppContextStep; typedef struct { MMBroadbandBearerCinterion *self; MMBaseModem *modem; MMPortSerialAt *primary; MMPort *data; guint cid; gint usb_interface_config_index; Disconnect3gppContextStep step; } Disconnect3gppContext; static void disconnect_3gpp_context_free (Disconnect3gppContext *ctx) { g_object_unref (ctx->data); g_object_unref (ctx->primary); g_object_unref (ctx->self); g_object_unref (ctx->modem); g_slice_free (Disconnect3gppContext, ctx); } static gboolean disconnect_3gpp_finish (MMBroadbandBearer *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void disconnect_3gpp_context_step (GTask *task); static void disconnect_connection_status_ready (MMBroadbandBearerCinterion *self, GAsyncResult *res, GTask *task) { MMBearerConnectionStatus status; Disconnect3gppContext *ctx; GError *error = NULL; ctx = (Disconnect3gppContext *) g_task_get_task_data (task); status = load_connection_status_finish (MM_BASE_BEARER (self), res, &error); switch (status) { case MM_BEARER_CONNECTION_STATUS_UNKNOWN: /* Assume disconnected */ mm_obj_dbg (self, "couldn't get CID %u status, assume disconnected: %s", ctx->cid, error->message); g_clear_error (&error); break; case MM_BEARER_CONNECTION_STATUS_DISCONNECTED: break; case MM_BEARER_CONNECTION_STATUS_CONNECTED: g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "CID %u is reported connected", ctx->cid); g_object_unref (task); return; case MM_BEARER_CONNECTION_STATUS_DISCONNECTING: case MM_BEARER_CONNECTION_STATUS_CONNECTION_FAILED: default: g_assert_not_reached (); } /* Go on to next step */ ctx->step++; disconnect_3gpp_context_step (task); } static void swwan_disconnect_ready (MMBaseModem *modem, GAsyncResult *res, GTask *task) { Disconnect3gppContext *ctx; ctx = (Disconnect3gppContext *) g_task_get_task_data (task); /* We don't bother to check error or response here since, ctx flow's * next step checks it */ mm_base_modem_at_command_full_finish (modem, res, NULL); /* Go on to next step */ ctx->step++; disconnect_3gpp_context_step (task); } static void disconnect_3gpp_context_step (GTask *task) { MMBroadbandBearerCinterion *self; Disconnect3gppContext *ctx; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); switch (ctx->step) { case DISCONNECT_3GPP_CONTEXT_STEP_FIRST: ctx->step++; /* fall through */ case DISCONNECT_3GPP_CONTEXT_STEP_STOP_SWWAN: { gchar *command; command = g_strdup_printf ("^SWWAN=0,%u,%u", ctx->cid, usb_interface_configs[ctx->usb_interface_config_index].swwan_index); mm_obj_dbg (self, "disconnect step %u/%u: disconnecting PDP CID %u...", ctx->step, DISCONNECT_3GPP_CONTEXT_STEP_LAST, ctx->cid); mm_base_modem_at_command_full (ctx->modem, ctx->primary, command, MM_BASE_BEARER_DEFAULT_DISCONNECTION_TIMEOUT, FALSE, FALSE, NULL, (GAsyncReadyCallback) swwan_disconnect_ready, task); g_free (command); return; } case DISCONNECT_3GPP_CONTEXT_STEP_CONNECTION_STATUS: mm_obj_dbg (self, "disconnect step %u/%u: checking SWWAN interface %u status...", ctx->step, DISCONNECT_3GPP_CONTEXT_STEP_LAST, usb_interface_configs[ctx->usb_interface_config_index].swwan_index); load_connection_status_by_cid (MM_BROADBAND_BEARER_CINTERION (ctx->self), (gint) ctx->cid, TRUE, FALSE, (GAsyncReadyCallback) disconnect_connection_status_ready, task); return; case DISCONNECT_3GPP_CONTEXT_STEP_LAST: mm_obj_dbg (self, "disconnect step %u/%u: finished", ctx->step, DISCONNECT_3GPP_CONTEXT_STEP_LAST); g_task_return_boolean (task, TRUE); g_object_unref (task); return; default: g_assert_not_reached (); } } static void disconnect_3gpp (MMBroadbandBearer *self, MMBroadbandModem *modem, MMPortSerialAt *primary, MMPortSerialAt *secondary, MMPort *data, guint cid, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; Disconnect3gppContext *ctx; GError *error = NULL; g_assert (primary != NULL); g_assert (data != NULL); /* Setup task and create connection context */ task = g_task_new (self, NULL, callback, user_data); ctx = g_slice_new0 (Disconnect3gppContext); g_task_set_task_data (task, ctx, (GDestroyNotify) disconnect_3gpp_context_free); /* Setup context */ ctx->self = MM_BROADBAND_BEARER_CINTERION (g_object_ref (self)); ctx->modem = MM_BASE_MODEM (g_object_ref (modem)); ctx->primary = g_object_ref (primary); ctx->data = g_object_ref (data); ctx->cid = cid; ctx->step = DISCONNECT_3GPP_CONTEXT_STEP_FIRST; /* Validate configuration */ ctx->usb_interface_config_index = get_usb_interface_config_index (data, &error); if (ctx->usb_interface_config_index < 0) { g_task_return_error (task, error); g_object_unref (task); return; } /* Start */ disconnect_3gpp_context_step (task); } /*****************************************************************************/ /* Setup and Init Bearers */ MMBaseBearer * mm_broadband_bearer_cinterion_new_finish (GAsyncResult *res, GError **error) { GObject *bearer; GObject *source; source = g_async_result_get_source_object (res); bearer = g_async_initable_new_finish (G_ASYNC_INITABLE (source), res, error); g_object_unref (source); if (!bearer) return NULL; /* Only export valid bearers */ mm_base_bearer_export (MM_BASE_BEARER (bearer)); return MM_BASE_BEARER (bearer); } void mm_broadband_bearer_cinterion_new (MMBroadbandModemCinterion *modem, MMBearerProperties *config, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { g_async_initable_new_async ( MM_TYPE_BROADBAND_BEARER_CINTERION, G_PRIORITY_DEFAULT, cancellable, callback, user_data, MM_BASE_BEARER_MODEM, modem, MM_BASE_BEARER_CONFIG, config, NULL); } static void mm_broadband_bearer_cinterion_init (MMBroadbandBearerCinterion *self) { } static void mm_broadband_bearer_cinterion_class_init (MMBroadbandBearerCinterionClass *klass) { MMBaseBearerClass *base_bearer_class = MM_BASE_BEARER_CLASS (klass); MMBroadbandBearerClass *broadband_bearer_class = MM_BROADBAND_BEARER_CLASS (klass); base_bearer_class->load_connection_status = load_connection_status; base_bearer_class->load_connection_status_finish = load_connection_status_finish; #if defined WITH_SUSPEND_RESUME base_bearer_class->reload_connection_status = load_connection_status; base_bearer_class->reload_connection_status_finish = load_connection_status_finish; #endif broadband_bearer_class->dial_3gpp = dial_3gpp; broadband_bearer_class->dial_3gpp_finish = dial_3gpp_finish; broadband_bearer_class->disconnect_3gpp = disconnect_3gpp; broadband_bearer_class->disconnect_3gpp_finish = disconnect_3gpp_finish; } ModemManager-1.23.4-dev/src/plugins/cinterion/mm-broadband-bearer-cinterion.h000066400000000000000000000053721456466623000271200ustar00rootroot00000000000000/* -*- 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) 2016 Trimble Navigation Limited * Author: Matthew Stanger */ #ifndef MM_BROADBAND_BEARER_CINTERION_H #define MM_BROADBAND_BEARER_CINTERION_H #include #include #include "mm-broadband-bearer.h" #include "mm-broadband-modem-cinterion.h" #define MM_TYPE_BROADBAND_BEARER_CINTERION (mm_broadband_bearer_cinterion_get_type ()) #define MM_BROADBAND_BEARER_CINTERION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_BROADBAND_BEARER_CINTERION, MMBroadbandBearerCinterion)) #define MM_BROADBAND_BEARER_CINTERION_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_BROADBAND_BEARER_CINTERION, MMBroadbandBearerCinterionClass)) #define MM_IS_BROADBAND_BEARER_CINTERION(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_BROADBAND_BEARER_CINTERION)) #define MM_IS_BROADBAND_BEARER_CINTERION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_BROADBAND_BEARER_CINTERION)) #define MM_BROADBAND_BEARER_CINTERION_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_BROADBAND_BEARER_CINTERION, MMBroadbandBearerCinterionClass)) typedef struct _MMBroadbandBearerCinterion MMBroadbandBearerCinterion; typedef struct _MMBroadbandBearerCinterionClass MMBroadbandBearerCinterionClass; struct _MMBroadbandBearerCinterion { MMBroadbandBearer parent; }; struct _MMBroadbandBearerCinterionClass { MMBroadbandBearerClass parent; }; GType mm_broadband_bearer_cinterion_get_type (void); void mm_broadband_bearer_cinterion_new (MMBroadbandModemCinterion *modem, MMBearerProperties *config, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); MMBaseBearer *mm_broadband_bearer_cinterion_new_finish (GAsyncResult *res, GError **error); #endif /* MM_BROADBAND_BEARER_CINTERION_H */ ModemManager-1.23.4-dev/src/plugins/cinterion/mm-broadband-modem-cinterion.c000066400000000000000000003751361456466623000267640ustar00rootroot00000000000000/* -*- 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) 2011 Ammonit Measurement GmbH * Copyright (C) 2011 Google Inc. * Copyright (C) 2016 Trimble Navigation Limited * Author: Aleksander Morgado * Contributor: Matthew Stanger */ #include #include #include #include #include #include #include "ModemManager.h" #include "mm-modem-helpers.h" #include "mm-serial-parsers.h" #include "mm-log-object.h" #include "mm-errors-types.h" #include "mm-iface-modem.h" #include "mm-iface-modem-3gpp.h" #include "mm-iface-modem-messaging.h" #include "mm-iface-modem-location.h" #include "mm-iface-modem-voice.h" #include "mm-base-modem-at.h" #include "mm-broadband-modem-cinterion.h" #include "mm-modem-helpers-cinterion.h" #include "mm-shared-cinterion.h" #include "mm-broadband-bearer-cinterion.h" #include "mm-iface-modem-signal.h" static void iface_modem_init (MMIfaceModem *iface); static void iface_modem_3gpp_init (MMIfaceModem3gpp *iface); static void iface_modem_messaging_init (MMIfaceModemMessaging *iface); static void iface_modem_location_init (MMIfaceModemLocation *iface); static void iface_modem_voice_init (MMIfaceModemVoice *iface); static void iface_modem_time_init (MMIfaceModemTime *iface); static void iface_modem_signal_init (MMIfaceModemSignal *iface); static void shared_cinterion_init (MMSharedCinterion *iface); static MMIfaceModem *iface_modem_parent; static MMIfaceModem3gpp *iface_modem_3gpp_parent; static MMIfaceModemLocation *iface_modem_location_parent; static MMIfaceModemVoice *iface_modem_voice_parent; static MMIfaceModemTime *iface_modem_time_parent; static MMIfaceModemSignal *iface_modem_signal_parent; G_DEFINE_TYPE_EXTENDED (MMBroadbandModemCinterion, mm_broadband_modem_cinterion, MM_TYPE_BROADBAND_MODEM, 0, G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM, iface_modem_init) G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_3GPP, iface_modem_3gpp_init) G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_MESSAGING, iface_modem_messaging_init) G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_LOCATION, iface_modem_location_init) G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_VOICE, iface_modem_voice_init) G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_TIME, iface_modem_time_init) G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_SIGNAL, iface_modem_signal_init) G_IMPLEMENT_INTERFACE (MM_TYPE_SHARED_CINTERION, shared_cinterion_init)) typedef enum { FEATURE_SUPPORT_UNKNOWN, FEATURE_NOT_SUPPORTED, FEATURE_SUPPORTED, } FeatureSupport; struct _MMBroadbandModemCinterionPrivate { /* Command to go into sleep mode */ gchar *sleep_mode_cmd; /* Cached supported bands in Cinterion format */ guint supported_bands[MM_CINTERION_RB_BLOCK_N]; /* Cached supported modes for SMS setup */ GArray *cnmi_supported_mode; GArray *cnmi_supported_mt; GArray *cnmi_supported_bm; GArray *cnmi_supported_ds; GArray *cnmi_supported_bfr; /* Cached supported rats for SXRAT */ GArray *sxrat_supported_rat; GArray *sxrat_supported_pref1; /* ignore regex */ GRegex *sysstart_regex; /* +CIEV indications as configured via AT^SIND */ GRegex *ciev_regex; /* +CIEV indication for simlocal configured via AT^SIND */ GRegex *simlocal_regex; /* Ignore SIM hotswap SCKS msg, until ready */ GRegex *scks_regex; /* Flags for feature support checks */ FeatureSupport swwan_support; FeatureSupport sind_psinfo_support; FeatureSupport smoni_support; FeatureSupport sind_simstatus_support; FeatureSupport sxrat_support; /* Mode combination to apply if "any" requested */ MMModemMode any_allowed; /* Flags for model-based behaviors */ MMCinterionModemFamily modem_family; MMCinterionRadioBandFormat rb_format; /* Initial EPS bearer context number */ gint initial_eps_bearer_cid; }; /*****************************************************************************/ MMCinterionModemFamily mm_broadband_modem_cinterion_get_family (MMBroadbandModemCinterion *self) { return self->priv->modem_family; } /*****************************************************************************/ /* Check support (Signal interface) */ static gboolean signal_check_support_finish (MMIfaceModemSignal *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void parent_signal_check_support_ready (MMIfaceModemSignal *self, GAsyncResult *res, GTask *task) { GError *error = NULL; if (!iface_modem_signal_parent->check_support_finish (self, res, &error)) g_task_return_error (task, error); else g_task_return_boolean (task, TRUE); g_object_unref (task); } static void check_smoni_support (MMBaseModem *_self, GAsyncResult *res, GTask *task) { MMBroadbandModemCinterion *self = MM_BROADBAND_MODEM_CINTERION (_self); /* Fetch the result to the SMONI test. If no response given (error triggered), assume unsupported */ if (mm_base_modem_at_command_finish (_self, res, NULL)) { mm_obj_dbg (self, "SMONI supported"); self->priv->smoni_support = FEATURE_SUPPORTED; g_task_return_boolean (task, TRUE); g_object_unref (task); return; } mm_obj_dbg (self, "SMONI unsupported"); self->priv->smoni_support = FEATURE_NOT_SUPPORTED; /* Otherwise, check if the parent CESQ-based implementation works */ g_assert (iface_modem_signal_parent->check_support && iface_modem_signal_parent->check_support_finish); iface_modem_signal_parent->check_support (MM_IFACE_MODEM_SIGNAL (self), (GAsyncReadyCallback) parent_signal_check_support_ready, task); } static void signal_check_support (MMIfaceModemSignal *self, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; task = g_task_new (self, NULL, callback, user_data); mm_base_modem_at_command (MM_BASE_MODEM (self), "^SMONI=?", 3, TRUE, (GAsyncReadyCallback) check_smoni_support, task); } /*****************************************************************************/ /* Load extended signal information (Signal interface) */ static gboolean signal_load_values_finish (MMIfaceModemSignal *_self, GAsyncResult *res, MMSignal **cdma, MMSignal **evdo, MMSignal **gsm, MMSignal **umts, MMSignal **lte, MMSignal **nr5g, GError **error) { MMBroadbandModemCinterion *self = MM_BROADBAND_MODEM_CINTERION (_self); const gchar *response; if (self->priv->smoni_support == FEATURE_NOT_SUPPORTED) return iface_modem_signal_parent->load_values_finish (_self, res, cdma, evdo, gsm, umts, lte, nr5g, error); response = mm_base_modem_at_command_finish (MM_BASE_MODEM (_self), res, error); if (!response || !mm_cinterion_smoni_response_to_signal_info (response, gsm, umts, lte, error)) return FALSE; if (cdma) *cdma = NULL; if (evdo) *evdo = NULL; if (nr5g) *nr5g = NULL; return TRUE; } static void signal_load_values (MMIfaceModemSignal *_self, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { MMBroadbandModemCinterion *self = MM_BROADBAND_MODEM_CINTERION (_self); if (self->priv->smoni_support == FEATURE_SUPPORTED) { mm_base_modem_at_command (MM_BASE_MODEM (self), "^SMONI", 3, FALSE, callback, user_data); return; } /* ^SMONI not supported, fallback to the parent */ iface_modem_signal_parent->load_values (_self, cancellable, callback, user_data); } /*****************************************************************************/ /* Enable unsolicited events (SMS indications) (Messaging interface) */ static gboolean messaging_enable_unsolicited_events_finish (MMIfaceModemMessaging *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void cnmi_test_ready (MMBaseModem *self, GAsyncResult *res, GTask *task) { GError *error = NULL; if (!mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error)) g_task_return_error (task, error); else g_task_return_boolean (task, TRUE); g_object_unref (task); } static gboolean value_supported (const GArray *array, const guint value) { guint i; if (!array) return FALSE; for (i = 0; i < array->len; i++) { if (g_array_index (array, guint, i) == value) return TRUE; } return FALSE; } static void messaging_enable_unsolicited_events (MMIfaceModemMessaging *_self, GAsyncReadyCallback callback, gpointer user_data) { MMBroadbandModemCinterion *self = MM_BROADBAND_MODEM_CINTERION (_self); GString *cmd; GError *error = NULL; GTask *task; task = g_task_new (self, NULL, callback, user_data); /* AT+CNMI=,[[,[,[,]]]] */ cmd = g_string_new ("+CNMI="); /* Mode 2 or 1 */ if (value_supported (self->priv->cnmi_supported_mode, 2)) g_string_append_printf (cmd, "%u,", 2); else if (value_supported (self->priv->cnmi_supported_mode, 1)) g_string_append_printf (cmd, "%u,", 1); else { error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "SMS settings don't accept [2,1] "); goto out; } /* mt 2 or 1 */ if (value_supported (self->priv->cnmi_supported_mt, 2)) g_string_append_printf (cmd, "%u,", 2); else if (value_supported (self->priv->cnmi_supported_mt, 1)) g_string_append_printf (cmd, "%u,", 1); else { error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "SMS settings don't accept [2,1] "); goto out; } /* bm 2 or 0 */ if (value_supported (self->priv->cnmi_supported_bm, 2)) g_string_append_printf (cmd, "%u,", 2); else if (value_supported (self->priv->cnmi_supported_bm, 0)) g_string_append_printf (cmd, "%u,", 0); else { error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "SMS settings don't accept [2,0] "); goto out; } /* ds 2, 1 or 0 */ if (value_supported (self->priv->cnmi_supported_ds, 2)) g_string_append_printf (cmd, "%u,", 2); else if (value_supported (self->priv->cnmi_supported_ds, 1)) g_string_append_printf (cmd, "%u,", 1); else if (value_supported (self->priv->cnmi_supported_ds, 0)) g_string_append_printf (cmd, "%u,", 0); else { error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "SMS settings don't accept [2,1,0] "); goto out; } /* bfr 1 */ if (value_supported (self->priv->cnmi_supported_bfr, 1)) g_string_append_printf (cmd, "%u", 1); /* otherwise, skip setting it */ out: /* Early error report */ if (error) { g_task_return_error (task, error); g_object_unref (task); g_string_free (cmd, TRUE); return; } mm_base_modem_at_command (MM_BASE_MODEM (self), cmd->str, 3, FALSE, (GAsyncReadyCallback)cnmi_test_ready, task); g_string_free (cmd, TRUE); } /*****************************************************************************/ /* Check if Messaging supported (Messaging interface) */ static gboolean messaging_check_support_finish (MMIfaceModemMessaging *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void cnmi_format_check_ready (MMBaseModem *_self, GAsyncResult *res, GTask *task) { MMBroadbandModemCinterion *self = MM_BROADBAND_MODEM_CINTERION (_self); GError *error = NULL; const gchar *response; response = mm_base_modem_at_command_finish (_self, res, &error); if (error) { g_task_return_error (task, error); g_object_unref (task); return; } /* Parse */ if (!mm_cinterion_parse_cnmi_test (response, &self->priv->cnmi_supported_mode, &self->priv->cnmi_supported_mt, &self->priv->cnmi_supported_bm, &self->priv->cnmi_supported_ds, &self->priv->cnmi_supported_bfr, &error)) { mm_obj_warn (self, "error reading SMS setup: %s", error->message); g_error_free (error); } /* CNMI command is supported; assume we have full messaging capabilities */ g_task_return_boolean (task, TRUE); g_object_unref (task); } static void messaging_check_support (MMIfaceModemMessaging *self, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; task = g_task_new (self, NULL, callback, user_data); /* We assume that CDMA-only modems don't have messaging capabilities */ if (mm_iface_modem_is_cdma_only (MM_IFACE_MODEM (self))) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "CDMA-only modems don't have messaging capabilities"); g_object_unref (task); return; } /* Check CNMI support */ mm_base_modem_at_command (MM_BASE_MODEM (self), "+CNMI=?", 3, TRUE, (GAsyncReadyCallback)cnmi_format_check_ready, task); } /*****************************************************************************/ /* Power down */ static gboolean modem_power_down_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void sleep_ready (MMBaseModem *self, GAsyncResult *res, GTask *task) { g_autoptr(GError) error = NULL; if (!mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error)) mm_obj_dbg (self, "couldn't send power down command: %s", error->message); g_task_return_boolean (task, TRUE); g_object_unref (task); } static void send_sleep_mode_command (GTask *task) { MMBroadbandModemCinterion *self; self = g_task_get_source_object (task); if (self->priv->sleep_mode_cmd && self->priv->sleep_mode_cmd[0]) { mm_base_modem_at_command (MM_BASE_MODEM (self), self->priv->sleep_mode_cmd, 5, FALSE, (GAsyncReadyCallback)sleep_ready, task); return; } /* No default command; just finish without sending anything */ g_task_return_boolean (task, TRUE); g_object_unref (task); } static void supported_functionality_status_query_ready (MMBaseModem *_self, GAsyncResult *res, GTask *task) { MMBroadbandModemCinterion *self = MM_BROADBAND_MODEM_CINTERION (_self); const gchar *response; g_autoptr(GError) error = NULL; g_assert (self->priv->sleep_mode_cmd == NULL); response = mm_base_modem_at_command_finish (_self, res, &error); if (!response) { mm_obj_warn (self, "couldn't query supported functionality status: %s", error->message); self->priv->sleep_mode_cmd = g_strdup (""); } else { /* We need to get which power-off command to use to put the modem in low * power mode (with serial port open for AT commands, but with RF switched * off). According to the documentation of various Cinterion modems, some * support AT+CFUN=4 (HC25) and those which don't support it can use * AT+CFUN=7 (CYCLIC SLEEP mode with 2s timeout after last character * received in the serial port). * * So, just look for '4' in the reply; if not found, look for '7', and if * not found, report warning and don't use any. */ if (strstr (response, "4") != NULL) { mm_obj_dbg (self, "device supports CFUN=4 sleep mode"); self->priv->sleep_mode_cmd = g_strdup ("+CFUN=4"); } else if (strstr (response, "7") != NULL) { mm_obj_dbg (self, "device supports CFUN=7 sleep mode"); self->priv->sleep_mode_cmd = g_strdup ("+CFUN=7"); } else { mm_obj_warn (self, "unknown functionality mode to go into sleep mode"); self->priv->sleep_mode_cmd = g_strdup (""); } } send_sleep_mode_command (task); } static void modem_power_down (MMIfaceModem *_self, GAsyncReadyCallback callback, gpointer user_data) { MMBroadbandModemCinterion *self = MM_BROADBAND_MODEM_CINTERION (_self); GTask *task; task = g_task_new (self, NULL, callback, user_data); /* If sleep command already decided, use it. */ if (self->priv->sleep_mode_cmd) send_sleep_mode_command (task); else mm_base_modem_at_command ( MM_BASE_MODEM (self), "+CFUN=?", 3, FALSE, (GAsyncReadyCallback)supported_functionality_status_query_ready, task); } /*****************************************************************************/ /* Modem Power Off */ #define MAX_POWER_OFF_WAIT_TIME_SECS 20 typedef struct { MMPortSerialAt *port; GRegex *shutdown_regex; gboolean shutdown_received; gboolean smso_replied; gboolean serial_open; guint timeout_id; } PowerOffContext; static void power_off_context_free (PowerOffContext *ctx) { if (ctx->serial_open) mm_port_serial_close (MM_PORT_SERIAL (ctx->port)); if (ctx->timeout_id) g_source_remove (ctx->timeout_id); mm_port_serial_at_add_unsolicited_msg_handler (ctx->port, ctx->shutdown_regex, NULL, NULL, NULL); g_object_unref (ctx->port); g_regex_unref (ctx->shutdown_regex); g_slice_free (PowerOffContext, ctx); } static gboolean modem_power_off_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void complete_power_off (GTask *task) { PowerOffContext *ctx; ctx = g_task_get_task_data (task); if (!ctx->shutdown_received || !ctx->smso_replied) return; /* remove timeout right away */ g_assert (ctx->timeout_id); g_source_remove (ctx->timeout_id); ctx->timeout_id = 0; g_task_return_boolean (task, TRUE); g_object_unref (task); } static void smso_ready (MMBaseModem *self, GAsyncResult *res, GTask *task) { PowerOffContext *ctx; GError *error = NULL; ctx = g_task_get_task_data (task); if (!mm_base_modem_at_command_full_finish (MM_BASE_MODEM (self), res, &error)) { g_task_return_error (task, error); g_object_unref (task); return; } /* Set as replied and see if we can complete */ ctx->smso_replied = TRUE; complete_power_off (task); } static void shutdown_received (MMPortSerialAt *port, GMatchInfo *match_info, GTask *task) { PowerOffContext *ctx; ctx = g_task_get_task_data (task); /* Cleanup handler right away, we don't want it called any more */ mm_port_serial_at_add_unsolicited_msg_handler (port, ctx->shutdown_regex, NULL, NULL, NULL); /* Set as received and see if we can complete */ ctx->shutdown_received = TRUE; complete_power_off (task); } static gboolean power_off_timeout_cb (GTask *task) { PowerOffContext *ctx; ctx = g_task_get_task_data (task); ctx->timeout_id = 0; /* The SMSO reply should have come earlier */ g_warn_if_fail (ctx->smso_replied == TRUE); /* Cleanup handler right away, we no longer want to receive it */ mm_port_serial_at_add_unsolicited_msg_handler (ctx->port, ctx->shutdown_regex, NULL, NULL, NULL); g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Power off operation timed out"); g_object_unref (task); return G_SOURCE_REMOVE; } static void modem_power_off (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; PowerOffContext *ctx; GError *error = NULL; task = g_task_new (self, NULL, callback, user_data); ctx = g_slice_new0 (PowerOffContext); ctx->port = mm_base_modem_get_port_primary (MM_BASE_MODEM (self)); ctx->shutdown_regex = g_regex_new ("\\r\\n\\^SHUTDOWN\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); ctx->timeout_id = g_timeout_add_seconds (MAX_POWER_OFF_WAIT_TIME_SECS, (GSourceFunc)power_off_timeout_cb, task); g_task_set_task_data (task, ctx, (GDestroyNotify) power_off_context_free); /* We'll need to wait for a ^SHUTDOWN before returning the action, which is * when the modem tells us that it is ready to be shutdown */ mm_port_serial_at_add_unsolicited_msg_handler ( ctx->port, ctx->shutdown_regex, (MMPortSerialAtUnsolicitedMsgFn)shutdown_received, task, NULL); /* In order to get the ^SHUTDOWN notification, we must keep the port open * during the wait time */ ctx->serial_open = mm_port_serial_open (MM_PORT_SERIAL (ctx->port), &error); if (G_UNLIKELY (error)) { g_task_return_error (task, error); g_object_unref (task); return; } /* Note: we'll use a timeout < MAX_POWER_OFF_WAIT_TIME_SECS for the AT command, * so we're sure that the AT command reply will always come before the timeout * fires */ g_assert (MAX_POWER_OFF_WAIT_TIME_SECS > 5); mm_base_modem_at_command_full (MM_BASE_MODEM (self), ctx->port, "^SMSO", 5, FALSE, /* allow_cached */ FALSE, /* is_raw */ NULL, /* cancellable */ (GAsyncReadyCallback)smso_ready, task); } /*****************************************************************************/ /* Access technologies polling */ static gboolean load_access_technologies_finish (MMIfaceModem *self, GAsyncResult *res, MMModemAccessTechnology *access_technologies, guint *mask, GError **error) { GError *inner_error = NULL; gssize val; val = g_task_propagate_int (G_TASK (res), &inner_error); if (inner_error) { g_propagate_error (error, inner_error); return FALSE; } *access_technologies = (MMModemAccessTechnology) val; *mask = MM_MODEM_ACCESS_TECHNOLOGY_ANY; return TRUE; } static void smong_query_ready (MMBaseModem *self, GAsyncResult *res, GTask *task) { const gchar *response; GError *error = NULL; MMModemAccessTechnology access_tech; response = mm_base_modem_at_command_finish (self, res, &error); if (!response || !mm_cinterion_parse_smong_response (response, &access_tech, &error)) g_task_return_error (task, error); else g_task_return_int (task, (gssize) access_tech); g_object_unref (task); } static void load_access_technologies (MMIfaceModem *_self, GAsyncReadyCallback callback, gpointer user_data) { MMBroadbandModemCinterion *self = MM_BROADBAND_MODEM_CINTERION (_self); GTask *task; task = g_task_new (self, NULL, callback, user_data); /* Abort access technology polling if ^SIND psinfo URCs are enabled */ if (self->priv->sind_psinfo_support == FEATURE_SUPPORTED) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "No need to poll access technologies"); g_object_unref (task); return; } mm_base_modem_at_command ( MM_BASE_MODEM (self), "^SMONG", 3, FALSE, (GAsyncReadyCallback)smong_query_ready, task); } /*****************************************************************************/ /* Disable unsolicited events (3GPP interface) */ static gboolean modem_3gpp_disable_unsolicited_events_finish (MMIfaceModem3gpp *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void parent_disable_unsolicited_events_ready (MMIfaceModem3gpp *self, GAsyncResult *res, GTask *task) { g_autoptr(GError) error = NULL; if (!iface_modem_3gpp_parent->disable_unsolicited_events_finish (self, res, &error)) mm_obj_warn (self, "couldn't disable parent 3GPP unsolicited events: %s", error->message); g_task_return_boolean (task, TRUE); g_object_unref (task); } static void parent_disable_unsolicited_messages (GTask *task) { /* Chain up parent's disable */ iface_modem_3gpp_parent->disable_unsolicited_events ( MM_IFACE_MODEM_3GPP (g_task_get_source_object (task)), (GAsyncReadyCallback)parent_disable_unsolicited_events_ready, task); } static void sind_psinfo_disable_ready (MMBaseModem *self, GAsyncResult *res, GTask *task) { g_autoptr(GError) error = NULL; if (!mm_base_modem_at_command_finish (self, res, &error)) mm_obj_warn (self, "Couldn't disable ^SIND psinfo notifications: %s", error->message); parent_disable_unsolicited_messages (task); } static void modem_3gpp_disable_unsolicited_events (MMIfaceModem3gpp *_self, GAsyncReadyCallback callback, gpointer user_data) { MMBroadbandModemCinterion *self; GTask *task; self = MM_BROADBAND_MODEM_CINTERION (_self); task = g_task_new (self, NULL, callback, user_data); if (self->priv->sind_psinfo_support == FEATURE_SUPPORTED) { /* Disable access technology update reporting */ mm_base_modem_at_command (MM_BASE_MODEM (self), "AT^SIND=\"psinfo\",0", 3, FALSE, (GAsyncReadyCallback)sind_psinfo_disable_ready, task); return; } parent_disable_unsolicited_messages (task); } /*****************************************************************************/ /* Enable unsolicited events (3GPP interface) */ static gboolean modem_3gpp_enable_unsolicited_events_finish (MMIfaceModem3gpp *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void sind_psinfo_enable_ready (MMBaseModem *_self, GAsyncResult *res, GTask *task) { MMBroadbandModemCinterion *self; g_autoptr(GError) error = NULL; const gchar *response; guint mode; guint val; self = MM_BROADBAND_MODEM_CINTERION (_self); if (!(response = mm_base_modem_at_command_finish (_self, res, &error))) { /* something went wrong, disable indicator */ self->priv->sind_psinfo_support = FEATURE_NOT_SUPPORTED; mm_obj_warn (self, "couldn't enable ^SIND psinfo notifications: %s", error->message); } else if (!mm_cinterion_parse_sind_response (response, NULL, &mode, &val, &error)) { /* problem with parsing, disable indicator */ self->priv->sind_psinfo_support = FEATURE_NOT_SUPPORTED; mm_obj_warn (self, "couldn't parse ^SIND psinfo response: %s", error->message); } else { /* Report initial access technology gathered right away */ mm_obj_dbg (self, "reporting initial access technologies..."); mm_iface_modem_update_access_technologies (MM_IFACE_MODEM (self), mm_cinterion_get_access_technology_from_sind_psinfo (val, self), MM_IFACE_MODEM_3GPP_ALL_ACCESS_TECHNOLOGIES_MASK); } g_task_return_boolean (task, TRUE); g_object_unref (task); } static void set_urc_dest_port_ready (MMBaseModem *_self, GAsyncResult *res, GTask *task) { MMBroadbandModemCinterion *self; g_autoptr(GError) error = NULL; self = MM_BROADBAND_MODEM_CINTERION (_self); if (!mm_base_modem_at_command_finish (MM_BASE_MODEM (_self), res, &error)) mm_obj_dbg (self, "couldn't guarantee unsolicited events are sent to the correct port: %s", error->message); if (self->priv->sind_psinfo_support == FEATURE_SUPPORTED) { /* Enable access technology update reporting */ mm_base_modem_at_command (MM_BASE_MODEM (self), "AT^SIND=\"psinfo\",1", 3, FALSE, (GAsyncReadyCallback)sind_psinfo_enable_ready, task); return; } g_task_return_boolean (task, TRUE); g_object_unref (task); } static void parent_enable_unsolicited_events_ready (MMIfaceModem3gpp *self, GAsyncResult *res, GTask *task) { g_autoptr(GError) error = NULL; if (!iface_modem_3gpp_parent->enable_unsolicited_events_finish (self, res, &error)) mm_obj_warn (self, "couldn't enable parent 3GPP unsolicited events: %s", error->message); /* Make sure unsolicited events are sent to an AT port (PLS9 can default to DATA port) */ mm_base_modem_at_command (MM_BASE_MODEM (self), "^SCFG=\"URC/DstIfc\",\"app\"", 5, FALSE, (GAsyncReadyCallback)set_urc_dest_port_ready, task); } static void modem_3gpp_enable_unsolicited_events (MMIfaceModem3gpp *self, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; task = g_task_new (self, NULL, callback, user_data); /* Chain up parent's enable */ iface_modem_3gpp_parent->enable_unsolicited_events ( self, (GAsyncReadyCallback)parent_enable_unsolicited_events_ready, task); } /*****************************************************************************/ /* Setup/Cleanup unsolicited events (3GPP interface) */ static void sind_ciev_received (MMPortSerialAt *port, GMatchInfo *match_info, MMBroadbandModemCinterion *self) { guint val = 0; gchar *indicator; indicator = mm_get_string_unquoted_from_match_info (match_info, 1); if (!mm_get_uint_from_match_info (match_info, 2, &val)) mm_obj_dbg (self, "couldn't parse indicator '%s' value", indicator); else { mm_obj_dbg (self, "received indicator '%s' update: %u", indicator, val); if (g_strcmp0 (indicator, "psinfo") == 0) { mm_iface_modem_update_access_technologies (MM_IFACE_MODEM (self), mm_cinterion_get_access_technology_from_sind_psinfo (val, self), MM_IFACE_MODEM_3GPP_ALL_ACCESS_TECHNOLOGIES_MASK); } } g_free (indicator); } static void set_unsolicited_events_handlers (MMBroadbandModemCinterion *self, gboolean enable) { MMPortSerialAt *ports[2]; guint i; ports[0] = mm_base_modem_peek_port_primary (MM_BASE_MODEM (self)); ports[1] = mm_base_modem_peek_port_secondary (MM_BASE_MODEM (self)); /* Enable unsolicited events in given port */ for (i = 0; i < G_N_ELEMENTS (ports); i++) { if (!ports[i]) continue; mm_port_serial_at_add_unsolicited_msg_handler ( ports[i], self->priv->ciev_regex, enable ? (MMPortSerialAtUnsolicitedMsgFn)sind_ciev_received : NULL, enable ? self : NULL, NULL); } } static gboolean modem_3gpp_setup_cleanup_unsolicited_events_finish (MMIfaceModem3gpp *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void parent_setup_unsolicited_events_ready (MMIfaceModem3gpp *self, GAsyncResult *res, GTask *task) { GError *error = NULL; if (!iface_modem_3gpp_parent->setup_unsolicited_events_finish (self, res, &error)) g_task_return_error (task, error); else { /* Our own setup now */ set_unsolicited_events_handlers (MM_BROADBAND_MODEM_CINTERION (self), TRUE); g_task_return_boolean (task, TRUE); } g_object_unref (task); } static void modem_3gpp_setup_unsolicited_events (MMIfaceModem3gpp *self, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; task = g_task_new (self, NULL, callback, user_data); /* Chain up parent's setup */ iface_modem_3gpp_parent->setup_unsolicited_events ( self, (GAsyncReadyCallback)parent_setup_unsolicited_events_ready, task); } static void parent_cleanup_unsolicited_events_ready (MMIfaceModem3gpp *self, GAsyncResult *res, GTask *task) { GError *error = NULL; if (!iface_modem_3gpp_parent->cleanup_unsolicited_events_finish (self, res, &error)) g_task_return_error (task, error); else g_task_return_boolean (task, TRUE); g_object_unref (task); } static void modem_3gpp_cleanup_unsolicited_events (MMIfaceModem3gpp *self, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; task = g_task_new (self, NULL, callback, user_data); /* Our own cleanup first */ set_unsolicited_events_handlers (MM_BROADBAND_MODEM_CINTERION (self), FALSE); /* And now chain up parent's cleanup */ iface_modem_3gpp_parent->cleanup_unsolicited_events ( self, (GAsyncReadyCallback)parent_cleanup_unsolicited_events_ready, task); } /*****************************************************************************/ /* Common operation to load expected CID for the initial EPS bearer */ static gboolean load_initial_eps_bearer_cid_finish (MMBroadbandModemCinterion *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void scfg_prov_cfg_query_ready (MMBaseModem *_self, GAsyncResult *res, GTask *task) { MMBroadbandModemCinterion *self = MM_BROADBAND_MODEM_CINTERION (_self); g_autoptr(GError) error = NULL; const gchar *response; response = mm_base_modem_at_command_finish (_self, res, &error); if (!response) mm_obj_dbg (self, "couldn't query MNO profiles: %s", error->message); else if (!mm_cinterion_provcfg_response_to_cid (response, MM_BROADBAND_MODEM_CINTERION (self)->priv->modem_family, mm_broadband_modem_get_current_charset (MM_BROADBAND_MODEM (self)), self, &self->priv->initial_eps_bearer_cid, &error)) mm_obj_dbg (self, "failed processing list of MNO profiles: %s", error->message); if (self->priv->initial_eps_bearer_cid < 0) { mm_obj_dbg (self, "using default EPS bearer context id: 1"); self->priv->initial_eps_bearer_cid = 1; } else mm_obj_dbg (self, "loaded EPS bearer context id from list of MNO profiles: %d", self->priv->initial_eps_bearer_cid); /* This operation really never fails */ g_task_return_boolean (task, TRUE); g_object_unref (task); } static void load_initial_eps_bearer_cid (MMBroadbandModemCinterion *self, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; g_assert (self->priv->initial_eps_bearer_cid < 0); task = g_task_new (self, NULL, callback, user_data); mm_base_modem_at_command (MM_BASE_MODEM (self), "^SCFG=\"MEopMode/Prov/Cfg\"", 20, FALSE, (GAsyncReadyCallback)scfg_prov_cfg_query_ready, task); } /*****************************************************************************/ /* Set initial EPS bearer settings */ typedef enum { SET_INITIAL_EPS_STEP_FIRST = 0, SET_INITIAL_EPS_STEP_CHECK_MODE, SET_INITIAL_EPS_STEP_RF_OFF, SET_INITIAL_EPS_STEP_APN, SET_INITIAL_EPS_STEP_AUTH, SET_INITIAL_EPS_STEP_RF_ON, SET_INITIAL_EPS_STEP_LAST, } SetInitialEpsStep; typedef struct { MMBearerProperties *properties; SetInitialEpsStep step; guint initial_cfun_mode; GError *saved_error; } SetInitialEpsContext; static void set_initial_eps_context_free (SetInitialEpsContext *ctx) { g_assert (!ctx->saved_error); g_object_unref (ctx->properties); g_slice_free (SetInitialEpsContext, ctx); } static gboolean modem_3gpp_set_initial_eps_bearer_settings_finish (MMIfaceModem3gpp *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void set_initial_eps_step (GTask *task); static void set_initial_eps_rf_on_ready (MMBaseModem *self, GAsyncResult *res, GTask *task) { g_autoptr(GError) error = NULL; SetInitialEpsContext *ctx; ctx = (SetInitialEpsContext *) g_task_get_task_data (task); if (!mm_base_modem_at_command_finish (self, res, &error)) { mm_obj_warn (self, "couldn't set RF back on: %s", error->message); if (!ctx->saved_error) ctx->saved_error = g_steal_pointer (&error); } /* Go to next step */ ctx->step++; set_initial_eps_step (task); } static void set_initial_eps_auth_ready (MMBaseModem *_self, GAsyncResult *res, GTask *task) { MMBroadbandModemCinterion *self = MM_BROADBAND_MODEM_CINTERION (_self); SetInitialEpsContext *ctx; ctx = (SetInitialEpsContext *) g_task_get_task_data (task); if (!mm_base_modem_at_command_finish (_self, res, &ctx->saved_error)) { mm_obj_warn (self, "couldn't configure context %d auth settings: %s", self->priv->initial_eps_bearer_cid, ctx->saved_error->message); /* Fallback to recover RF before returning the error */ ctx->step = SET_INITIAL_EPS_STEP_RF_ON; } else { /* Go to next step */ ctx->step++; } set_initial_eps_step (task); } static void set_initial_eps_cgdcont_ready (MMBaseModem *_self, GAsyncResult *res, GTask *task) { MMBroadbandModemCinterion *self = MM_BROADBAND_MODEM_CINTERION (_self); SetInitialEpsContext *ctx; ctx = (SetInitialEpsContext *) g_task_get_task_data (task); if (!mm_base_modem_at_command_finish (_self, res, &ctx->saved_error)) { mm_obj_warn (self, "couldn't configure context %d settings: %s", self->priv->initial_eps_bearer_cid, ctx->saved_error->message); /* Fallback to recover RF before returning the error */ ctx->step = SET_INITIAL_EPS_STEP_RF_ON; } else { /* Go to next step */ ctx->step++; } set_initial_eps_step (task); } static void set_initial_eps_rf_off_ready (MMBaseModem *self, GAsyncResult *res, GTask *task) { GError *error = NULL; SetInitialEpsContext *ctx; ctx = (SetInitialEpsContext *) g_task_get_task_data (task); if (!mm_base_modem_at_command_finish (self, res, &error)) { mm_obj_warn (self, "couldn't set RF off: %s", error->message); g_task_return_error (task, error); g_object_unref (task); return; } /* Go to next step */ ctx->step++; set_initial_eps_step (task); } static void set_initial_eps_cfun_mode_load_ready (MMBaseModem *self, GAsyncResult *res, GTask *task) { GError *error = NULL; const gchar *response; SetInitialEpsContext *ctx; guint mode; ctx = (SetInitialEpsContext *) g_task_get_task_data (task); response = mm_base_modem_at_command_finish (self, res, &error); if (!response || !mm_3gpp_parse_cfun_query_response (response, &mode, &error)) { mm_obj_warn (self, "couldn't load initial functionality mode: %s", error->message); g_task_return_error (task, error); g_object_unref (task); return; } mm_obj_dbg (self, "current functionality mode: %u", mode); if (mode != 1 && mode != 4) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_WRONG_STATE, "cannot setup the default LTE bearer settings: " "the SIM must be powered"); g_task_return_error (task, error); g_object_unref (task); return; } ctx->initial_cfun_mode = mode; ctx->step++; set_initial_eps_step (task); } static void set_initial_eps_step (GTask *task) { MMBroadbandModemCinterion *self; SetInitialEpsContext *ctx; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); switch (ctx->step) { case SET_INITIAL_EPS_STEP_FIRST: ctx->step++; /* fall through */ case SET_INITIAL_EPS_STEP_CHECK_MODE: mm_base_modem_at_command ( MM_BASE_MODEM (self), "+CFUN?", 5, FALSE, (GAsyncReadyCallback)set_initial_eps_cfun_mode_load_ready, task); return; case SET_INITIAL_EPS_STEP_RF_OFF: if (ctx->initial_cfun_mode != 4) { mm_base_modem_at_command ( MM_BASE_MODEM (self), "+CFUN=4", 5, FALSE, (GAsyncReadyCallback)set_initial_eps_rf_off_ready, task); return; } ctx->step++; /* fall through */ case SET_INITIAL_EPS_STEP_APN: { const gchar *apn; g_autofree gchar *quoted_apn = NULL; g_autofree gchar *apn_cmd = NULL; const gchar *ip_family_str; MMBearerIpFamily ip_family; ip_family = mm_bearer_properties_get_ip_type (ctx->properties); if (ip_family == MM_BEARER_IP_FAMILY_NONE || ip_family == MM_BEARER_IP_FAMILY_ANY) ip_family = MM_BEARER_IP_FAMILY_IPV4; ip_family_str = mm_3gpp_get_pdp_type_from_ip_family (ip_family); apn = mm_bearer_properties_get_apn (ctx->properties); mm_obj_dbg (self, "context %d with APN '%s' and PDP type '%s'", self->priv->initial_eps_bearer_cid, apn, ip_family_str); quoted_apn = mm_port_serial_at_quote_string (apn); apn_cmd = g_strdup_printf ("+CGDCONT=%u,\"%s\",%s", self->priv->initial_eps_bearer_cid, ip_family_str, quoted_apn); mm_base_modem_at_command ( MM_BASE_MODEM (self), apn_cmd, 20, FALSE, (GAsyncReadyCallback)set_initial_eps_cgdcont_ready, task); return; } case SET_INITIAL_EPS_STEP_AUTH: { g_autofree gchar *auth_cmd = NULL; auth_cmd = mm_cinterion_build_auth_string (self, MM_BROADBAND_MODEM_CINTERION (self)->priv->modem_family, ctx->properties, self->priv->initial_eps_bearer_cid); mm_base_modem_at_command ( MM_BASE_MODEM (self), auth_cmd, 20, FALSE, (GAsyncReadyCallback)set_initial_eps_auth_ready, task); return; } case SET_INITIAL_EPS_STEP_RF_ON: if (ctx->initial_cfun_mode == 1) { mm_base_modem_at_command ( MM_BASE_MODEM (self), "+CFUN=1", 5, FALSE, (GAsyncReadyCallback)set_initial_eps_rf_on_ready, task); return; } ctx->step++; /* fall through */ case SET_INITIAL_EPS_STEP_LAST: if (ctx->saved_error) g_task_return_error (task, g_steal_pointer (&ctx->saved_error)); else g_task_return_boolean (task, TRUE); g_object_unref (task); return; default: g_assert_not_reached (); } } static void modem_3gpp_set_initial_eps_bearer_settings (MMIfaceModem3gpp *self, MMBearerProperties *properties, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; SetInitialEpsContext *ctx; task = g_task_new (self, NULL, callback, user_data); /* The initial EPS bearer settings should have already been loaded */ g_assert (MM_BROADBAND_MODEM_CINTERION (self)->priv->initial_eps_bearer_cid >= 0); /* Setup context */ ctx = g_slice_new0 (SetInitialEpsContext); ctx->properties = g_object_ref (properties); ctx->step = SET_INITIAL_EPS_STEP_FIRST; g_task_set_task_data (task, ctx, (GDestroyNotify) set_initial_eps_context_free); set_initial_eps_step (task); } /*****************************************************************************/ /* Common initial EPS bearer info loading for both: * - runtime status * - configuration settings */ typedef enum { COMMON_LOAD_INITIAL_EPS_STEP_FIRST = 0, COMMON_LOAD_INITIAL_EPS_STEP_PROFILE, COMMON_LOAD_INITIAL_EPS_STEP_APN, COMMON_LOAD_INITIAL_EPS_STEP_AUTH, COMMON_LOAD_INITIAL_EPS_STEP_LAST, } CommonLoadInitialEpsStep; typedef struct { MMBearerProperties *properties; CommonLoadInitialEpsStep step; gboolean runtime; } CommonLoadInitialEpsContext; static void common_load_initial_eps_context_free (CommonLoadInitialEpsContext *ctx) { g_clear_object (&ctx->properties); g_slice_free (CommonLoadInitialEpsContext, ctx); } static MMBearerProperties * common_load_initial_eps_bearer_finish (MMIfaceModem3gpp *self, GAsyncResult *res, GError **error) { return MM_BEARER_PROPERTIES (g_task_propagate_pointer (G_TASK (res), error)); } static void common_load_initial_eps_step (GTask *task); static void common_load_initial_eps_auth_ready (MMBaseModem *_self, GAsyncResult *res, GTask *task) { MMBroadbandModemCinterion *self = MM_BROADBAND_MODEM_CINTERION (_self); const gchar *response; CommonLoadInitialEpsContext *ctx; g_autoptr(GError) error = NULL; MMBearerAllowedAuth auth = MM_BEARER_ALLOWED_AUTH_UNKNOWN; g_autofree gchar *username = NULL; ctx = (CommonLoadInitialEpsContext *) g_task_get_task_data (task); response = mm_base_modem_at_command_finish (_self, res, &error); if (!response) mm_obj_dbg (self, "couldn't load context %d auth settings: %s", self->priv->initial_eps_bearer_cid, error->message); else if (!mm_cinterion_parse_sgauth_response (response, self->priv->initial_eps_bearer_cid, &auth, &username, &error)) mm_obj_dbg (self, "couldn't parse context %d auth settings: %s", self->priv->initial_eps_bearer_cid, error->message); else { mm_bearer_properties_set_allowed_auth (ctx->properties, auth); mm_bearer_properties_set_user (ctx->properties, username); } /* Go to next step */ ctx->step++; common_load_initial_eps_step (task); } static void common_load_initial_eps_load_cid_ready (MMBroadbandModemCinterion *self, GAsyncResult *res, GTask *task) { CommonLoadInitialEpsContext *ctx; ctx = (CommonLoadInitialEpsContext *) g_task_get_task_data (task); load_initial_eps_bearer_cid_finish (self, res, NULL); g_assert (self->priv->initial_eps_bearer_cid >= 0); /* Go to next step */ ctx->step++; common_load_initial_eps_step (task); } static void common_load_initial_eps_cgcontrdp_ready (MMBaseModem *_self, GAsyncResult *res, GTask *task) { MMBroadbandModemCinterion *self = MM_BROADBAND_MODEM_CINTERION (_self); const gchar *response; CommonLoadInitialEpsContext *ctx; g_autofree gchar *apn = NULL; g_autoptr(GError) error = NULL; ctx = (CommonLoadInitialEpsContext *) g_task_get_task_data (task); /* errors aren't fatal */ response = mm_base_modem_at_command_finish (_self, res, &error); if (!response) mm_obj_dbg (self, "couldn't load context %d settings: %s", self->priv->initial_eps_bearer_cid, error->message); else if (!mm_3gpp_parse_cgcontrdp_response (response, NULL, NULL, &apn, NULL, NULL, NULL, NULL, NULL, &error)) mm_obj_dbg (self, "couldn't parse CGDCONTRDP response: %s", error->message); else mm_bearer_properties_set_apn (ctx->properties, apn); /* Go to next step */ ctx->step++; common_load_initial_eps_step (task); } static void common_load_initial_eps_cgdcont_ready (MMBaseModem *_self, GAsyncResult *res, GTask *task) { MMBroadbandModemCinterion *self = MM_BROADBAND_MODEM_CINTERION (_self); const gchar *response; CommonLoadInitialEpsContext *ctx; g_autoptr(GError) error = NULL; ctx = (CommonLoadInitialEpsContext *) g_task_get_task_data (task); /* errors aren't fatal */ response = mm_base_modem_at_command_finish (_self, res, &error); if (!response) mm_obj_dbg (self, "couldn't load context %d status: %s", self->priv->initial_eps_bearer_cid, error->message); else { GList *context_list; context_list = mm_3gpp_parse_cgdcont_read_response (response, &error); if (!context_list) if (error) mm_obj_dbg (self, "couldn't parse CGDCONT response: %s", error->message); else mm_obj_dbg (self, "No PDP contexts found."); else { GList *l; for (l = context_list; l; l = g_list_next (l)) { MM3gppPdpContext *pdp = l->data; if (pdp->cid == (guint) self->priv->initial_eps_bearer_cid) { mm_bearer_properties_set_ip_type (ctx->properties, pdp->pdp_type); mm_bearer_properties_set_apn (ctx->properties, pdp->apn ? pdp->apn : ""); break; } } if (!l) mm_obj_dbg (self, "no status reported for context %d", self->priv->initial_eps_bearer_cid); mm_3gpp_pdp_context_list_free (context_list); } } /* Go to next step */ ctx->step++; common_load_initial_eps_step (task); } static void common_load_initial_eps_step (GTask *task) { MMBroadbandModemCinterion *self; CommonLoadInitialEpsContext *ctx; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); switch (ctx->step) { case COMMON_LOAD_INITIAL_EPS_STEP_FIRST: ctx->step++; /* fall through */ case COMMON_LOAD_INITIAL_EPS_STEP_PROFILE: /* Initial EPS bearer CID initialization run once only */ if (G_UNLIKELY (self->priv->initial_eps_bearer_cid < 0)) { load_initial_eps_bearer_cid ( self, (GAsyncReadyCallback)common_load_initial_eps_load_cid_ready, task); return; } ctx->step++; /* fall through */ case COMMON_LOAD_INITIAL_EPS_STEP_APN: if (ctx->runtime) { g_autofree gchar *cmd = NULL; cmd = g_strdup_printf ("+CGCONTRDP=%u", self->priv->initial_eps_bearer_cid); mm_base_modem_at_command ( MM_BASE_MODEM (self), cmd, 20, FALSE, (GAsyncReadyCallback)common_load_initial_eps_cgcontrdp_ready, task); } else { mm_base_modem_at_command ( MM_BASE_MODEM (self), "+CGDCONT?", 20, FALSE, (GAsyncReadyCallback)common_load_initial_eps_cgdcont_ready, task); } return; case COMMON_LOAD_INITIAL_EPS_STEP_AUTH: mm_base_modem_at_command ( MM_BASE_MODEM (self), "^SGAUTH?", 20, FALSE, (GAsyncReadyCallback)common_load_initial_eps_auth_ready, task); return; case COMMON_LOAD_INITIAL_EPS_STEP_LAST: g_task_return_pointer (task, g_steal_pointer (&ctx->properties), g_object_unref); g_object_unref (task); return; default: g_assert_not_reached (); } } static void common_load_initial_eps_bearer (MMIfaceModem3gpp *self, gboolean runtime, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; CommonLoadInitialEpsContext *ctx; task = g_task_new (self, NULL, callback, user_data); /* Setup context */ ctx = g_slice_new0 (CommonLoadInitialEpsContext); ctx->runtime = runtime; ctx->properties = mm_bearer_properties_new (); ctx->step = COMMON_LOAD_INITIAL_EPS_STEP_FIRST; g_task_set_task_data (task, ctx, (GDestroyNotify) common_load_initial_eps_context_free); common_load_initial_eps_step (task); } /*****************************************************************************/ /* Initial EPS bearer runtime status loading */ static MMBearerProperties * modem_3gpp_load_initial_eps_bearer_finish (MMIfaceModem3gpp *self, GAsyncResult *res, GError **error) { return common_load_initial_eps_bearer_finish (self, res, error); } static void modem_3gpp_load_initial_eps_bearer (MMIfaceModem3gpp *self, GAsyncReadyCallback callback, gpointer user_data) { common_load_initial_eps_bearer (self, TRUE, callback, user_data); } /*****************************************************************************/ /* Initial EPS bearer settings loading -> set configuration */ static MMBearerProperties * modem_3gpp_load_initial_eps_bearer_settings_finish (MMIfaceModem3gpp *self, GAsyncResult *res, GError **error) { return common_load_initial_eps_bearer_finish (self, res, error); } static void modem_3gpp_load_initial_eps_bearer_settings (MMIfaceModem3gpp *self, GAsyncReadyCallback callback, gpointer user_data) { common_load_initial_eps_bearer (self, FALSE, callback, user_data); } /*****************************************************************************/ /* Load supported modes (Modem interface) */ static GArray * load_supported_modes_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { return g_task_propagate_pointer (G_TASK (res), error); } static void parent_load_supported_modes_ready (MMIfaceModem *self, GAsyncResult *res, GTask *task) { GError *error = NULL; GArray *all; GArray *combinations; GArray *filtered; MMModemModeCombination mode; all = iface_modem_parent->load_supported_modes_finish (self, res, &error); if (!all) { g_task_return_error (task, error); g_object_unref (task); return; } /* Build list of combinations */ combinations = g_array_sized_new (FALSE, FALSE, sizeof (MMModemModeCombination), 3); /* 2G only */ mode.allowed = MM_MODEM_MODE_2G; mode.preferred = MM_MODEM_MODE_NONE; g_array_append_val (combinations, mode); /* 3G only */ mode.allowed = MM_MODEM_MODE_3G; mode.preferred = MM_MODEM_MODE_NONE; g_array_append_val (combinations, mode); if (mm_iface_modem_is_4g (self)) { /* 4G only */ mode.allowed = MM_MODEM_MODE_4G; mode.preferred = MM_MODEM_MODE_NONE; g_array_append_val (combinations, mode); /* 2G, 3G and 4G */ mode.allowed = (MM_MODEM_MODE_2G | MM_MODEM_MODE_3G | MM_MODEM_MODE_4G); mode.preferred = MM_MODEM_MODE_NONE; g_array_append_val (combinations, mode); } else { /* 2G and 3G */ mode.allowed = (MM_MODEM_MODE_2G | MM_MODEM_MODE_3G); mode.preferred = MM_MODEM_MODE_NONE; g_array_append_val (combinations, mode); } /* Filter out those unsupported modes */ filtered = mm_filter_supported_modes (all, combinations, self); g_array_unref (all); g_array_unref (combinations); g_task_return_pointer (task, filtered, (GDestroyNotify) g_array_unref); g_object_unref (task); } static void sxrat_load_supported_modes_ready (MMBroadbandModemCinterion *self, GTask *task) { GArray *combinations; MMModemModeCombination mode; g_assert (self->priv->sxrat_supported_rat); g_assert (self->priv->sxrat_supported_pref1); /* Build list of combinations */ combinations = g_array_sized_new (FALSE, FALSE, sizeof (MMModemModeCombination), 3); if (value_supported (self->priv->sxrat_supported_rat, 0)) { /* 2G only */ mode.allowed = MM_MODEM_MODE_2G; mode.preferred = MM_MODEM_MODE_NONE; g_array_append_val (combinations, mode); } if (value_supported (self->priv->sxrat_supported_rat, 1)) { /* 2G+3G with none preferred */ mode.allowed = (MM_MODEM_MODE_2G | MM_MODEM_MODE_3G); mode.preferred = MM_MODEM_MODE_NONE; g_array_append_val (combinations, mode); self->priv->any_allowed = mode.allowed; if (value_supported (self->priv->sxrat_supported_pref1, 0)) { /* 2G preferred */ mode.preferred = MM_MODEM_MODE_2G; g_array_append_val (combinations, mode); } if (value_supported (self->priv->sxrat_supported_pref1, 2)) { /* 3G preferred */ mode.preferred = MM_MODEM_MODE_3G; g_array_append_val (combinations, mode); } } if (value_supported (self->priv->sxrat_supported_rat, 2)) { /* 3G only */ mode.allowed = MM_MODEM_MODE_3G; mode.preferred = MM_MODEM_MODE_NONE; g_array_append_val (combinations, mode); } if (value_supported (self->priv->sxrat_supported_rat, 3)) { /* 4G only */ mode.allowed = MM_MODEM_MODE_4G; mode.preferred = MM_MODEM_MODE_NONE; g_array_append_val (combinations, mode); } if (value_supported (self->priv->sxrat_supported_rat, 4)) { /* 3G+4G with none preferred */ mode.allowed = (MM_MODEM_MODE_3G | MM_MODEM_MODE_4G); mode.preferred = MM_MODEM_MODE_NONE; g_array_append_val (combinations, mode); self->priv->any_allowed = mode.allowed; if (value_supported (self->priv->sxrat_supported_pref1, 2)) { /* 3G preferred */ mode.preferred = MM_MODEM_MODE_3G; g_array_append_val (combinations, mode); } if (value_supported (self->priv->sxrat_supported_pref1, 3)) { /* 4G preferred */ mode.preferred = MM_MODEM_MODE_4G; g_array_append_val (combinations, mode); } } if (value_supported (self->priv->sxrat_supported_rat, 5)) { /* 2G+4G with none preferred */ mode.allowed = (MM_MODEM_MODE_2G | MM_MODEM_MODE_4G); mode.preferred = MM_MODEM_MODE_NONE; g_array_append_val (combinations, mode); self->priv->any_allowed = mode.allowed; if (value_supported (self->priv->sxrat_supported_pref1, 0)) { /* 2G preferred */ mode.preferred = MM_MODEM_MODE_2G; g_array_append_val (combinations, mode); } if (value_supported (self->priv->sxrat_supported_pref1, 3)) { /* 4G preferred */ mode.preferred = MM_MODEM_MODE_4G; g_array_append_val (combinations, mode); } } if (value_supported (self->priv->sxrat_supported_rat, 6)) { /* 2G+3G+4G with none preferred */ mode.allowed = (MM_MODEM_MODE_2G | MM_MODEM_MODE_3G | MM_MODEM_MODE_4G); mode.preferred = MM_MODEM_MODE_NONE; g_array_append_val (combinations, mode); self->priv->any_allowed = mode.allowed; if (value_supported (self->priv->sxrat_supported_pref1, 0)) { /* 2G preferred */ mode.preferred = MM_MODEM_MODE_2G; g_array_append_val (combinations, mode); } if (value_supported (self->priv->sxrat_supported_pref1, 2)) { /* 3G preferred */ mode.preferred = MM_MODEM_MODE_3G; g_array_append_val (combinations, mode); } if (value_supported (self->priv->sxrat_supported_pref1, 3)) { /* 4G preferred */ mode.preferred = MM_MODEM_MODE_4G; g_array_append_val (combinations, mode); } } g_task_return_pointer (task, combinations, (GDestroyNotify) g_array_unref); g_object_unref (task); } static void sxrat_test_ready (MMBaseModem *_self, GAsyncResult *res, GTask *task) { MMBroadbandModemCinterion *self = MM_BROADBAND_MODEM_CINTERION (_self); g_autoptr(GError) error = NULL; const gchar *response; response = mm_base_modem_at_command_finish (_self, res, &error); if (!error) { mm_cinterion_parse_sxrat_test (response, &self->priv->sxrat_supported_rat, &self->priv->sxrat_supported_pref1, NULL, &error); if (!error) { self->priv->sxrat_support = FEATURE_SUPPORTED; sxrat_load_supported_modes_ready (self, task); return; } mm_obj_warn (self, "error reading SXRAT response: %s", error->message); } self->priv->sxrat_support = FEATURE_NOT_SUPPORTED; /* Run parent's loading in case SXRAT is not supported */ iface_modem_parent->load_supported_modes ( MM_IFACE_MODEM (self), (GAsyncReadyCallback)parent_load_supported_modes_ready, task); } static void load_supported_modes (MMIfaceModem *_self, GAsyncReadyCallback callback, gpointer user_data) { MMBroadbandModemCinterion *self = MM_BROADBAND_MODEM_CINTERION (_self); GTask *task; task = g_task_new (self, NULL, callback, user_data); /* First check SXRAT support, if not already done */ if (self->priv->sxrat_support == FEATURE_SUPPORT_UNKNOWN) { mm_base_modem_at_command (MM_BASE_MODEM (self), "^SXRAT=?", 3, TRUE, (GAsyncReadyCallback)sxrat_test_ready, task); return; } if (self->priv->sxrat_support == FEATURE_SUPPORTED) { sxrat_load_supported_modes_ready (self, task); return; } /* Run parent's loading */ iface_modem_parent->load_supported_modes ( MM_IFACE_MODEM (self), (GAsyncReadyCallback)parent_load_supported_modes_ready, task); } /*****************************************************************************/ /* Set current modes (Modem interface) */ static gboolean set_current_modes_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void set_current_modes_reregister_in_network_ready (MMIfaceModem3gpp *self, GAsyncResult *res, GTask *task) { GError *error = NULL; if (!mm_iface_modem_3gpp_reregister_in_network_finish (self, res, &error)) g_task_return_error (task, error); else g_task_return_boolean (task, TRUE); g_object_unref (task); } static void allowed_access_technology_update_ready (MMBroadbandModemCinterion *self, GAsyncResult *res, GTask *task) { GError *error = NULL; mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error); if (error) g_task_return_error (task, error); else g_task_return_boolean (task, TRUE); g_object_unref (task); } static void cops_set_current_modes (MMBroadbandModemCinterion *self, MMModemMode allowed, MMModemMode preferred, GTask *task) { gchar *command; g_assert (preferred == MM_MODEM_MODE_NONE); /* We will try to simulate the possible allowed modes here. The * Cinterion devices do not seem to allow setting preferred access * technology in devices, but they allow restricting to a given * one: * - 2G-only is forced by forcing GERAN RAT (AcT=0) * - 3G-only is forced by forcing UTRAN RAT (AcT=2) * - 4G-only is forced by forcing E-UTRAN RAT (AcT=7) * - for the remaining ones, we default to automatic selection of RAT, * which is based on the quality of the connection. */ if (mm_iface_modem_is_4g (MM_IFACE_MODEM (self)) && allowed == MM_MODEM_MODE_4G) command = g_strdup ("+COPS=,,,7"); else if (mm_iface_modem_is_3g (MM_IFACE_MODEM (self)) && allowed == MM_MODEM_MODE_3G) command = g_strdup ("+COPS=,,,2"); else if (mm_iface_modem_is_2g (MM_IFACE_MODEM (self)) && allowed == MM_MODEM_MODE_2G) command = g_strdup ("+COPS=,,,0"); else { /* For any other combination (e.g. ANY or no AcT given, defaults to Auto. For this case, we cannot provide * AT+COPS=,,, (i.e. just without a last value). Instead, we need to * re-run the last manual/automatic selection command which succeeded, * (or auto by default if none was launched) */ mm_iface_modem_3gpp_reregister_in_network (MM_IFACE_MODEM_3GPP (self), (GAsyncReadyCallback) set_current_modes_reregister_in_network_ready, task); return; } mm_base_modem_at_command ( MM_BASE_MODEM (self), command, 20, FALSE, (GAsyncReadyCallback)allowed_access_technology_update_ready, task); g_free (command); } static void sxrat_set_current_modes (MMBroadbandModemCinterion *self, MMModemMode allowed, MMModemMode preferred, GTask *task) { gchar *command; GError *error = NULL; g_assert (self->priv->any_allowed != MM_MODEM_MODE_NONE); /* Handle ANY */ if (allowed == MM_MODEM_MODE_ANY) allowed = self->priv->any_allowed; command = mm_cinterion_build_sxrat_set_command (allowed, preferred, &error); if (!command) { g_task_return_error (task, error); g_object_unref (task); return; } mm_base_modem_at_command ( MM_BASE_MODEM (self), command, 30, FALSE, (GAsyncReadyCallback)allowed_access_technology_update_ready, task); g_free (command); } static void set_current_modes (MMIfaceModem *_self, MMModemMode allowed, MMModemMode preferred, GAsyncReadyCallback callback, gpointer user_data) { MMBroadbandModemCinterion *self = MM_BROADBAND_MODEM_CINTERION (_self); GTask *task; task = g_task_new (self, NULL, callback, user_data); if (self->priv->sxrat_support == FEATURE_SUPPORTED) sxrat_set_current_modes (self, allowed, preferred, task); else if (self->priv->sxrat_support == FEATURE_NOT_SUPPORTED) cops_set_current_modes (self, allowed, preferred, task); else g_assert_not_reached (); } /*****************************************************************************/ /* Supported bands (Modem interface) */ static GArray * load_supported_bands_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { return g_task_propagate_pointer (G_TASK (res), error); } static void scfg_test_ready (MMBaseModem *_self, GAsyncResult *res, GTask *task) { MMBroadbandModemCinterion *self = MM_BROADBAND_MODEM_CINTERION (_self); const gchar *response; GError *error = NULL; GArray *bands; response = mm_base_modem_at_command_finish (_self, res, &error); if (!response || !mm_cinterion_parse_scfg_test (response, self->priv->modem_family, mm_broadband_modem_get_current_charset (MM_BROADBAND_MODEM (self)), &bands, &self->priv->rb_format, &error)) g_task_return_error (task, error); else { if (!mm_cinterion_build_band (bands, NULL, FALSE, self->priv->rb_format, self->priv->modem_family, self->priv->supported_bands, &error)) g_task_return_error (task, error); else g_task_return_pointer (task, bands, (GDestroyNotify)g_array_unref); } g_object_unref (task); } static void load_supported_bands (MMIfaceModem *_self, GAsyncReadyCallback callback, gpointer user_data) { MMBroadbandModemCinterion *self = MM_BROADBAND_MODEM_CINTERION (_self); GTask *task; MMPort *primary; MMKernelDevice *port; const gchar *family = NULL; /* Lookup for the tag specifying which modem family the current device belongs */ primary = MM_PORT (mm_base_modem_peek_port_primary (MM_BASE_MODEM (self))); port = mm_port_peek_kernel_device (primary); family = mm_kernel_device_get_global_property (port, "ID_MM_CINTERION_MODEM_FAMILY"); /* if the property is not set, default family */ self->priv->modem_family = MM_CINTERION_MODEM_FAMILY_DEFAULT; /* set used family also in the string for mm_obj_dbg */ if (!family) family = "default"; if (g_ascii_strcasecmp (family, "imt") == 0) self->priv->modem_family = MM_CINTERION_MODEM_FAMILY_IMT; else if (g_ascii_strcasecmp (family, "default") != 0) { mm_obj_dbg (self, "cinterion modem family '%s' unknown", family); family = "default"; } mm_obj_dbg (self, "Using cinterion %s modem family", family); task = g_task_new (_self, NULL, callback, user_data); mm_base_modem_at_command (MM_BASE_MODEM (_self), "AT^SCFG=?", 3, FALSE, (GAsyncReadyCallback)scfg_test_ready, task); } /*****************************************************************************/ /* Load current bands (Modem interface) */ static GArray * load_current_bands_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { return g_task_propagate_pointer (G_TASK (res), error); } static void get_band_ready (MMBaseModem *_self, GAsyncResult *res, GTask *task) { MMBroadbandModemCinterion *self = MM_BROADBAND_MODEM_CINTERION (_self); const gchar *response; GError *error = NULL; GArray *bands = NULL; response = mm_base_modem_at_command_finish (_self, res, &error); if (!response || !mm_cinterion_parse_scfg_response (response, self->priv->modem_family, mm_broadband_modem_get_current_charset (MM_BROADBAND_MODEM (self)), &bands, self->priv->rb_format, &error)) g_task_return_error (task, error); else g_task_return_pointer (task, bands, (GDestroyNotify) g_array_unref); g_object_unref (task); } static void load_current_bands (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; task = g_task_new (self, NULL, callback, user_data); /* The timeout in this command is extremely large, because there are some * modules like the EGS5 that build the response based on the current network * registration, and that implies the module needs to be registered. If for * any reason there is no serving network where to register, the response * comes after a very long time, up to 100s. */ mm_base_modem_at_command (MM_BASE_MODEM (self), "AT^SCFG?", 120, FALSE, (GAsyncReadyCallback)get_band_ready, task); } /*****************************************************************************/ /* Set current bands (Modem interface) */ typedef struct { MMBaseModemAtCommandAlloc *cmds; } SetCurrentBandsContext; static void set_current_bands_context_free (SetCurrentBandsContext *ctx) { if (ctx->cmds) { guint i; for (i = 0; ctx->cmds[i].command; i++) mm_base_modem_at_command_alloc_clear (&ctx->cmds[i]); g_free (ctx->cmds); } g_slice_free (SetCurrentBandsContext, ctx); } static gboolean set_current_bands_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void scfg_set_ready (MMBaseModem *self, GAsyncResult *res, GTask *task) { GError *error = NULL; if (!mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error)) g_task_return_error (task, error); else g_task_return_boolean (task, TRUE); g_object_unref (task); } static void scfg_set_ready_sequence (MMBaseModem *self, GAsyncResult *res, GTask *task) { GError *error = NULL; mm_base_modem_at_sequence_finish (self, res, NULL, &error); if (error) g_task_return_error (task, error); else g_task_return_boolean (task, TRUE); g_object_unref (task); } static void set_bands_3g (GTask *task, GArray *bands_array) { MMBroadbandModemCinterion *self; GError *error = NULL; guint band[MM_CINTERION_RB_BLOCK_N] = { 0 }; self = g_task_get_source_object (task); if (!mm_cinterion_build_band (bands_array, self->priv->supported_bands, FALSE, /* 2G and 3G */ self->priv->rb_format, self->priv->modem_family, band, &error)) { g_task_return_error (task, error); g_object_unref (task); return; } if (self->priv->rb_format == MM_CINTERION_RADIO_BAND_FORMAT_SINGLE) { g_autofree gchar *cmd = NULL; /* Following the setup: * AT^SCFG="Radion/Band", * We will set the preferred band equal to the allowed band, so that we force * the modem to connect at that specific frequency only. Note that we will be * passing a number here! * * The optional field is set to 1, so that changes take effect * immediately. */ cmd = g_strdup_printf ("^SCFG=\"Radio/Band\",%u,1", band[MM_CINTERION_RB_BLOCK_LEGACY]); mm_base_modem_at_command (MM_BASE_MODEM (self), cmd, 15, FALSE, (GAsyncReadyCallback)scfg_set_ready, task); return; } if (self->priv->rb_format == MM_CINTERION_RADIO_BAND_FORMAT_MULTIPLE) { SetCurrentBandsContext *ctx; ctx = g_slice_new0 (SetCurrentBandsContext); g_task_set_task_data (task, ctx, (GDestroyNotify)set_current_bands_context_free); if (self->priv->modem_family == MM_CINTERION_MODEM_FAMILY_IMT) { g_autofree gchar *bandstr2G = NULL; g_autofree gchar *bandstr3G = NULL; g_autofree gchar *bandstr4G = NULL; g_autofree gchar *bandstr2G_enc = NULL; g_autofree gchar *bandstr3G_enc = NULL; g_autofree gchar *bandstr4G_enc = NULL; bandstr2G = g_strdup_printf ("0x%08X", band[MM_CINTERION_RB_BLOCK_GSM]); bandstr3G = g_strdup_printf ("0x%08X", band[MM_CINTERION_RB_BLOCK_UMTS]); bandstr4G = g_strdup_printf ("0x%08X", band[MM_CINTERION_RB_BLOCK_LTE_LOW]); bandstr2G_enc = mm_modem_charset_str_from_utf8 (bandstr2G, mm_broadband_modem_get_current_charset (MM_BROADBAND_MODEM (self)), FALSE, &error); if (!bandstr2G_enc) { g_prefix_error (&error, "Couldn't convert 2G band string to current charset: "); g_task_return_error (task, error); g_object_unref (task); return; } bandstr3G_enc = mm_modem_charset_str_from_utf8 (bandstr3G, mm_broadband_modem_get_current_charset (MM_BROADBAND_MODEM (self)), FALSE, &error); if (!bandstr3G_enc) { g_prefix_error (&error, "Couldn't convert 3G band string to current charset: "); g_task_return_error (task, error); g_object_unref (task); return; } bandstr4G_enc = mm_modem_charset_str_from_utf8 (bandstr4G, mm_broadband_modem_get_current_charset (MM_BROADBAND_MODEM (self)), FALSE, &error); if (!bandstr4G_enc) { g_prefix_error (&error, "Couldn't convert 4G band string to current charset: "); g_task_return_error (task, error); g_object_unref (task); return; } ctx->cmds = g_new0 (MMBaseModemAtCommandAlloc, 3 + 1); ctx->cmds[0].command = g_strdup_printf ("^SCFG=\"Radio/Band/2G\",\"%s\"", bandstr2G_enc); ctx->cmds[1].command = g_strdup_printf ("^SCFG=\"Radio/Band/3G\",\"%s\"", bandstr3G_enc); ctx->cmds[2].command = g_strdup_printf ("^SCFG=\"Radio/Band/4G\",\"%s\"", bandstr4G_enc); ctx->cmds[0].timeout = ctx->cmds[1].timeout = ctx->cmds[2].timeout = 60; } else { ctx->cmds = g_new0 (MMBaseModemAtCommandAlloc, 3 + 1); ctx->cmds[0].command = g_strdup_printf ("^SCFG=\"Radio/Band/2G\",\"%08x\",,1", band[MM_CINTERION_RB_BLOCK_GSM]); ctx->cmds[1].command = g_strdup_printf ("^SCFG=\"Radio/Band/3G\",\"%08x\",,1", band[MM_CINTERION_RB_BLOCK_UMTS]); ctx->cmds[2].command = g_strdup_printf ("^SCFG=\"Radio/Band/4G\",\"%08x\",\"%08x\",1", band[MM_CINTERION_RB_BLOCK_LTE_LOW], band[MM_CINTERION_RB_BLOCK_LTE_HIGH]); ctx->cmds[0].timeout = ctx->cmds[1].timeout = ctx->cmds[2].timeout = 15; } mm_base_modem_at_sequence (MM_BASE_MODEM (self), (const MMBaseModemAtCommand *)ctx->cmds, NULL, NULL, (GAsyncReadyCallback)scfg_set_ready_sequence, task); return; } g_assert_not_reached (); } static void set_bands_2g (GTask *task, GArray *bands_array) { MMBroadbandModemCinterion *self; GError *error = NULL; guint band[MM_CINTERION_RB_BLOCK_N] = { 0 }; g_autofree gchar *cmd = NULL; g_autofree gchar *bandstr = NULL; g_autofree gchar *bandstr_enc = NULL; self = g_task_get_source_object (task); if (!mm_cinterion_build_band (bands_array, self->priv->supported_bands, TRUE, /* 2G only */ MM_CINTERION_RADIO_BAND_FORMAT_SINGLE, 0, band, &error)) { g_task_return_error (task, error); g_object_unref (task); return; } /* Build string with the value, in the proper charset */ bandstr = g_strdup_printf ("%u", band[MM_CINTERION_RB_BLOCK_LEGACY]); bandstr_enc = mm_modem_charset_str_from_utf8 (bandstr, mm_broadband_modem_get_current_charset (MM_BROADBAND_MODEM (self)), FALSE, &error); if (!bandstr_enc) { g_prefix_error (&error, "Couldn't convert band string to current charset: "); g_task_return_error (task, error); g_object_unref (task); return; } /* Following the setup: * AT^SCFG="Radion/Band",, * We will set the preferred band equal to the allowed band, so that we force * the modem to connect at that specific frequency only. Note that we will be * passing double-quote enclosed strings here! */ cmd = g_strdup_printf ("^SCFG=\"Radio/Band\",\"%s\",\"%s\"", bandstr_enc, bandstr_enc); mm_base_modem_at_command (MM_BASE_MODEM (self), cmd, 15, FALSE, (GAsyncReadyCallback)scfg_set_ready, task); } static void set_current_bands (MMIfaceModem *self, GArray *bands_array, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; /* The bands that we get here are previously validated by the interface, and * that means that ALL the bands given here were also given in the list of * supported bands. BUT BUT, that doesn't mean that the exact list of bands * will end up being valid, as not all combinations are possible. E.g, * Cinterion modems supporting only 2G have specific combinations allowed. */ task = g_task_new (self, NULL, callback, user_data); if (mm_iface_modem_is_3g (self)) set_bands_3g (task, bands_array); else set_bands_2g (task, bands_array); } /*****************************************************************************/ /* Flow control */ static gboolean setup_flow_control_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void setup_flow_control_ready (MMBaseModem *self, GAsyncResult *res, GTask *task) { GError *error = NULL; if (!mm_base_modem_at_command_finish (self, res, &error)) /* Let the error be critical. We DO need RTS/CTS in order to have * proper modem disabling. */ g_task_return_error (task, error); else g_task_return_boolean (task, TRUE); g_object_unref (task); } static void setup_flow_control (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; task = g_task_new (self, NULL, callback, user_data); /* We need to enable RTS/CTS so that CYCLIC SLEEP mode works */ g_object_set (self, MM_BROADBAND_MODEM_FLOW_CONTROL, MM_FLOW_CONTROL_RTS_CTS, NULL); mm_base_modem_at_command (MM_BASE_MODEM (self), "\\Q3", 3, FALSE, (GAsyncReadyCallback)setup_flow_control_ready, task); } /*****************************************************************************/ /* Load unlock retries (Modem interface) */ typedef struct { MMUnlockRetries *retries; guint i; } LoadUnlockRetriesContext; typedef struct { MMModemLock lock; const gchar *command; } UnlockRetriesMap; static const UnlockRetriesMap unlock_retries_map [] = { { MM_MODEM_LOCK_SIM_PIN, "^SPIC=\"SC\"" }, { MM_MODEM_LOCK_SIM_PUK, "^SPIC=\"SC\",1" }, { MM_MODEM_LOCK_SIM_PIN2, "^SPIC=\"P2\"" }, { MM_MODEM_LOCK_SIM_PUK2, "^SPIC=\"P2\",1" }, { MM_MODEM_LOCK_PH_FSIM_PIN, "^SPIC=\"PS\"" }, { MM_MODEM_LOCK_PH_FSIM_PUK, "^SPIC=\"PS\",1" }, { MM_MODEM_LOCK_PH_NET_PIN, "^SPIC=\"PN\"" }, { MM_MODEM_LOCK_PH_NET_PUK, "^SPIC=\"PN\",1" }, }; static void load_unlock_retries_context_free (LoadUnlockRetriesContext *ctx) { g_object_unref (ctx->retries); g_slice_free (LoadUnlockRetriesContext, ctx); } static MMUnlockRetries * load_unlock_retries_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { return g_task_propagate_pointer (G_TASK (res), error); } static void load_unlock_retries_context_step (GTask *task); static void spic_ready (MMBaseModem *self, GAsyncResult *res, GTask *task) { LoadUnlockRetriesContext *ctx; const gchar *response; g_autoptr(GError) error = NULL; ctx = g_task_get_task_data (task); response = mm_base_modem_at_command_finish (self, res, &error); if (!response) { mm_obj_dbg (self, "Couldn't load retry count for lock '%s': %s", mm_modem_lock_get_string (unlock_retries_map[ctx->i].lock), error->message); } else { guint val; response = mm_strip_tag (response, "^SPIC:"); if (!mm_get_uint_from_str (response, &val)) mm_obj_dbg (self, "couldn't parse retry count value for lock '%s'", mm_modem_lock_get_string (unlock_retries_map[ctx->i].lock)); else mm_unlock_retries_set (ctx->retries, unlock_retries_map[ctx->i].lock, val); } /* Go to next lock value */ ctx->i++; load_unlock_retries_context_step (task); } static void load_unlock_retries_context_step (GTask *task) { MMBroadbandModemCinterion *self; LoadUnlockRetriesContext *ctx; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); if (ctx->i == G_N_ELEMENTS (unlock_retries_map)) { g_task_return_pointer (task, g_object_ref (ctx->retries), g_object_unref); g_object_unref (task); return; } mm_base_modem_at_command ( MM_BASE_MODEM (self), unlock_retries_map[ctx->i].command, 3, FALSE, (GAsyncReadyCallback)spic_ready, task); } static void load_unlock_retries (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; LoadUnlockRetriesContext *ctx; task = g_task_new (self, NULL, callback, user_data); ctx = g_slice_new0 (LoadUnlockRetriesContext); ctx->retries = mm_unlock_retries_new (); ctx->i = 0; g_task_set_task_data (task, ctx, (GDestroyNotify)load_unlock_retries_context_free); load_unlock_retries_context_step (task); } /*****************************************************************************/ /* After SIM unlock (Modem interface) */ #define MAX_AFTER_SIM_UNLOCK_RETRIES 15 typedef enum { CINTERION_SIM_STATUS_REMOVED = 0, CINTERION_SIM_STATUS_INSERTED = 1, CINTERION_SIM_STATUS_INIT_COMPLETED = 5, } CinterionSimStatus; typedef struct { guint retries; guint timeout_id; } AfterSimUnlockContext; static gboolean after_sim_unlock_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void after_sim_unlock_context_step (GTask *task); static gboolean simstatus_timeout_cb (GTask *task) { AfterSimUnlockContext *ctx; ctx = g_task_get_task_data (task); ctx->timeout_id = 0; after_sim_unlock_context_step (task); return G_SOURCE_REMOVE; } static void simstatus_check_ready (MMBaseModem *self, GAsyncResult *res, GTask *task) { AfterSimUnlockContext *ctx; const gchar *response; response = mm_base_modem_at_command_finish (self, res, NULL); if (response) { gchar *descr = NULL; guint val = 0; if (mm_cinterion_parse_sind_response (response, &descr, NULL, &val, NULL) && g_str_equal (descr, "simstatus") && val == CINTERION_SIM_STATUS_INIT_COMPLETED) { /* SIM ready! */ g_free (descr); g_task_return_boolean (task, TRUE); g_object_unref (task); return; } g_free (descr); } /* Need to retry after 1 sec */ ctx = g_task_get_task_data (task); g_assert (ctx->timeout_id == 0); ctx->timeout_id = g_timeout_add_seconds (1, (GSourceFunc)simstatus_timeout_cb, task); } static void after_sim_unlock_context_step (GTask *task) { MMBroadbandModemCinterion *self; AfterSimUnlockContext *ctx; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); /* if not supported or too much wait, skip */ if (self->priv->sind_simstatus_support != FEATURE_SUPPORTED || ctx->retries == 0) { g_task_return_boolean (task, TRUE); g_object_unref (task); return; } /* Recheck */ ctx->retries--; mm_base_modem_at_command (MM_BASE_MODEM (self), "^SIND=\"simstatus\",2", 3, FALSE, (GAsyncReadyCallback)simstatus_check_ready, task); } static void sind_indicators_ready (MMBaseModem *_self, GAsyncResult *res, GTask *task) { MMBroadbandModemCinterion *self; g_autoptr(GError) error = NULL; const gchar *response; self = MM_BROADBAND_MODEM_CINTERION (_self); if (!(response = mm_base_modem_at_command_finish (_self, res, &error))) { self->priv->sind_psinfo_support = FEATURE_NOT_SUPPORTED; mm_obj_dbg (self, "psinfo support? no"); self->priv->sind_simstatus_support = FEATURE_NOT_SUPPORTED; mm_obj_dbg (self, "simstatus support? no"); g_task_return_boolean (task, TRUE); g_object_unref (task); return; } if (g_regex_match_simple ("\\(\\s*psinfo\\s*,", response, 0, 0)) self->priv->sind_psinfo_support = FEATURE_SUPPORTED; mm_obj_dbg (self, "psinfo support? %s", self->priv->sind_psinfo_support == FEATURE_SUPPORTED ? "yes":"no"); if (g_regex_match_simple ("\\(\\s*simstatus\\s*,", response, 0, 0)) self->priv->sind_simstatus_support = FEATURE_SUPPORTED; mm_obj_dbg (self, "simstatus support? %s", self->priv->sind_simstatus_support == FEATURE_SUPPORTED ? "yes":"no"); after_sim_unlock_context_step (task); } static void after_sim_unlock (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; AfterSimUnlockContext *ctx; task = g_task_new (self, NULL, callback, user_data); ctx = g_new0 (AfterSimUnlockContext, 1); ctx->retries = MAX_AFTER_SIM_UNLOCK_RETRIES; g_task_set_task_data (task, ctx, g_free); /* check which indicators are available */ mm_base_modem_at_command (MM_BASE_MODEM (self), "AT^SIND=?", 3, FALSE, (GAsyncReadyCallback)sind_indicators_ready, task); } /*****************************************************************************/ /* Setup SIM hot swap (Modem interface) */ static void cinterion_scks_unsolicited_handler (MMPortSerialAt *port, GMatchInfo *match_info, MMBroadbandModemCinterion *self) { guint scks; if (!mm_get_uint_from_match_info (match_info, 1, &scks)) return; switch (scks) { case 0: mm_obj_msg (self, "SIM removal detected"); break; case 1: mm_obj_msg (self, "SIM insertion detected"); break; case 2: mm_obj_msg (self, "SIM interface hardware deactivated (potentially non-electrically compatible SIM inserted)"); break; case 3: mm_obj_msg (self, "SIM interface hardware deactivated (technical problem, no precise diagnosis)"); break; default: g_assert_not_reached (); break; } mm_iface_modem_process_sim_event (MM_IFACE_MODEM (self)); } static gboolean modem_setup_sim_hot_swap_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void cinterion_hot_swap_init_ready (MMBaseModem *_self, GAsyncResult *res, GTask *task) { MMBroadbandModemCinterion *self = MM_BROADBAND_MODEM_CINTERION (_self); g_autoptr(GError) error = NULL; MMPortSerialAt *primary; MMPortSerialAt *secondary; if (!mm_base_modem_at_command_finish (_self, res, &error)) { g_prefix_error (&error, "Could not enable SCKS: "); g_task_return_error (task, g_steal_pointer (&error)); g_object_unref (task); return; } mm_obj_dbg (self, "SIM hot swap detect successfully enabled"); primary = mm_base_modem_peek_port_primary (MM_BASE_MODEM (self)); mm_port_serial_at_add_unsolicited_msg_handler ( primary, self->priv->scks_regex, (MMPortSerialAtUnsolicitedMsgFn) cinterion_scks_unsolicited_handler, self, NULL); secondary = mm_base_modem_peek_port_secondary (MM_BASE_MODEM (self)); if (secondary) mm_port_serial_at_add_unsolicited_msg_handler ( secondary, self->priv->scks_regex, (MMPortSerialAtUnsolicitedMsgFn) cinterion_scks_unsolicited_handler, self, NULL); if (!mm_broadband_modem_sim_hot_swap_ports_context_init (MM_BROADBAND_MODEM (self), &error)) mm_obj_warn (self, "failed to initialize SIM hot swap ports context: %s", error->message); g_task_return_boolean (task, TRUE); g_object_unref (task); } static void modem_setup_sim_hot_swap (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; mm_obj_dbg (self, "Enabling SCKS URCs for SIM hot swap detection"); task = g_task_new (self, NULL, callback, user_data); mm_base_modem_at_command (MM_BASE_MODEM (self), "^SCKS=1", 3, FALSE, (GAsyncReadyCallback) cinterion_hot_swap_init_ready, task); } /*****************************************************************************/ /* SIM hot swap cleanup (Modem interface) */ static void modem_cleanup_sim_hot_swap (MMIfaceModem *self) { mm_broadband_modem_sim_hot_swap_ports_context_reset (MM_BROADBAND_MODEM (self)); } /*****************************************************************************/ /* Create Bearer (Modem interface) */ static MMBaseBearer * cinterion_modem_create_bearer_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { return g_task_propagate_pointer (G_TASK (res), error); } static void broadband_bearer_cinterion_new_ready (GObject *unused, GAsyncResult *res, GTask *task) { MMBaseBearer *bearer; GError *error = NULL; bearer = mm_broadband_bearer_cinterion_new_finish (res, &error); if (!bearer) g_task_return_error (task, error); else g_task_return_pointer (task, bearer, g_object_unref); g_object_unref (task); } static void broadband_bearer_new_ready (GObject *unused, GAsyncResult *res, GTask *task) { MMBaseBearer *bearer; GError *error = NULL; bearer = mm_broadband_bearer_new_finish (res, &error); if (!bearer) g_task_return_error (task, error); else g_task_return_pointer (task, bearer, g_object_unref); g_object_unref (task); } static void common_create_bearer (GTask *task) { MMBroadbandModemCinterion *self; self = g_task_get_source_object (task); switch (self->priv->swwan_support) { case FEATURE_NOT_SUPPORTED: mm_obj_dbg (self, "^SWWAN not supported, creating default bearer..."); mm_broadband_bearer_new (MM_BROADBAND_MODEM (self), g_task_get_task_data (task), NULL, /* cancellable */ (GAsyncReadyCallback)broadband_bearer_new_ready, task); return; case FEATURE_SUPPORTED: mm_obj_dbg (self, "^SWWAN supported, creating cinterion bearer..."); mm_broadband_bearer_cinterion_new (MM_BROADBAND_MODEM_CINTERION (self), g_task_get_task_data (task), NULL, /* cancellable */ (GAsyncReadyCallback)broadband_bearer_cinterion_new_ready, task); return; case FEATURE_SUPPORT_UNKNOWN: default: g_assert_not_reached (); } } static void swwan_test_ready (MMBaseModem *_self, GAsyncResult *res, GTask *task) { MMBroadbandModemCinterion *self = MM_BROADBAND_MODEM_CINTERION (_self); /* Fetch the result to the SWWAN test. If no response given (error triggered), * assume unsupported */ if (!mm_base_modem_at_command_finish (_self, res, NULL)) { mm_obj_dbg (self, "SWWAN unsupported"); self->priv->swwan_support = FEATURE_NOT_SUPPORTED; } else { mm_obj_dbg (self, "SWWAN supported"); self->priv->swwan_support = FEATURE_SUPPORTED; } /* Go on and create the bearer */ common_create_bearer (task); } static void cinterion_modem_create_bearer (MMIfaceModem *_self, MMBearerProperties *properties, GAsyncReadyCallback callback, gpointer user_data) { MMBroadbandModemCinterion *self = MM_BROADBAND_MODEM_CINTERION (_self); GTask *task; task = g_task_new (self, NULL, callback, user_data); g_task_set_task_data (task, g_object_ref (properties), g_object_unref); /* Newer Cinterion modems may support SWWAN, which is the same as WWAN. * Check to see if current modem supports it.*/ if (self->priv->swwan_support != FEATURE_SUPPORT_UNKNOWN) { common_create_bearer (task); return; } /* If we don't have a data port, don't even bother checking for ^SWWAN * support. */ if (!mm_base_modem_peek_best_data_port (MM_BASE_MODEM (self), MM_PORT_TYPE_NET)) { mm_obj_dbg (self, "skipping ^SWWAN check as no data port is available"); self->priv->swwan_support = FEATURE_NOT_SUPPORTED; common_create_bearer (task); return; } mm_obj_dbg (self, "checking ^SWWAN support..."); mm_base_modem_at_command (MM_BASE_MODEM (self), "^SWWAN=?", 6, TRUE, /* may be cached */ (GAsyncReadyCallback) swwan_test_ready, task); } /*****************************************************************************/ static void setup_ports (MMBroadbandModem *_self) { MMBroadbandModemCinterion *self = (MM_BROADBAND_MODEM_CINTERION (_self)); MMPortSerialAt *ports[2]; guint i; /* Call parent's setup ports first always */ MM_BROADBAND_MODEM_CLASS (mm_broadband_modem_cinterion_parent_class)->setup_ports (_self); ports[0] = mm_base_modem_peek_port_primary (MM_BASE_MODEM (self)); ports[1] = mm_base_modem_peek_port_secondary (MM_BASE_MODEM (self)); for (i = 0; i < G_N_ELEMENTS (ports); i++) { if (!ports[i]) continue; mm_port_serial_at_add_unsolicited_msg_handler ( ports[i], self->priv->sysstart_regex, NULL, NULL, NULL); mm_port_serial_at_add_unsolicited_msg_handler ( ports[i], self->priv->scks_regex, NULL, NULL, NULL); mm_port_serial_at_add_unsolicited_msg_handler ( ports[i], self->priv->simlocal_regex, NULL, NULL, NULL); } } /*****************************************************************************/ MMBroadbandModemCinterion * mm_broadband_modem_cinterion_new (const gchar *device, const gchar *physdev, const gchar **drivers, const gchar *plugin, guint16 vendor_id, guint16 product_id) { return g_object_new (MM_TYPE_BROADBAND_MODEM_CINTERION, MM_BASE_MODEM_DEVICE, device, MM_BASE_MODEM_PHYSDEV, physdev, MM_BASE_MODEM_DRIVERS, drivers, MM_BASE_MODEM_PLUGIN, plugin, MM_BASE_MODEM_VENDOR_ID, vendor_id, MM_BASE_MODEM_PRODUCT_ID, product_id, /* Generic bearer (TTY) or Cinterion bearer (NET) supported */ MM_BASE_MODEM_DATA_NET_SUPPORTED, TRUE, MM_BASE_MODEM_DATA_TTY_SUPPORTED, TRUE, MM_IFACE_MODEM_SIM_HOT_SWAP_SUPPORTED, TRUE, NULL); } static void mm_broadband_modem_cinterion_init (MMBroadbandModemCinterion *self) { /* Initialize private data */ self->priv = G_TYPE_INSTANCE_GET_PRIVATE ((self), MM_TYPE_BROADBAND_MODEM_CINTERION, MMBroadbandModemCinterionPrivate); /* Initialize private variables */ self->priv->initial_eps_bearer_cid = -1; self->priv->sind_psinfo_support = FEATURE_SUPPORT_UNKNOWN; self->priv->swwan_support = FEATURE_SUPPORT_UNKNOWN; self->priv->smoni_support = FEATURE_SUPPORT_UNKNOWN; self->priv->sind_simstatus_support = FEATURE_SUPPORT_UNKNOWN; self->priv->sxrat_support = FEATURE_SUPPORT_UNKNOWN; self->priv->ciev_regex = g_regex_new ("\\r\\n\\+CIEV:\\s*([a-z]+),(\\d+)\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); self->priv->sysstart_regex = g_regex_new ("\\r\\n\\^SYSSTART.*\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); self->priv->scks_regex = g_regex_new ("\\^SCKS:\\s*([0-3])\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); self->priv->simlocal_regex = g_regex_new ("\\r\\n\\+CIEV:\\s*simlocal,((\\d,)*\\d)\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); self->priv->any_allowed = MM_MODEM_MODE_NONE; } static void finalize (GObject *object) { MMBroadbandModemCinterion *self = MM_BROADBAND_MODEM_CINTERION (object); g_free (self->priv->sleep_mode_cmd); if (self->priv->cnmi_supported_mode) g_array_unref (self->priv->cnmi_supported_mode); if (self->priv->cnmi_supported_mt) g_array_unref (self->priv->cnmi_supported_mt); if (self->priv->cnmi_supported_bm) g_array_unref (self->priv->cnmi_supported_bm); if (self->priv->cnmi_supported_ds) g_array_unref (self->priv->cnmi_supported_ds); if (self->priv->cnmi_supported_bfr) g_array_unref (self->priv->cnmi_supported_bfr); if (self->priv->sxrat_supported_rat) g_array_unref (self->priv->sxrat_supported_rat); if (self->priv->sxrat_supported_pref1) g_array_unref (self->priv->sxrat_supported_pref1); g_regex_unref (self->priv->ciev_regex); g_regex_unref (self->priv->sysstart_regex); g_regex_unref (self->priv->scks_regex); g_regex_unref (self->priv->simlocal_regex); G_OBJECT_CLASS (mm_broadband_modem_cinterion_parent_class)->finalize (object); } /*****************************************************************************/ /* Load SIM slots (modem interface) */ typedef struct { GPtrArray *sim_slots; guint number_slots; guint active_slot_index; /* range [1,number_slots] */ } LoadSimSlotsContext; static void load_sim_slots_context_free (LoadSimSlotsContext *ctx) { g_clear_pointer (&ctx->sim_slots, g_ptr_array_unref); g_slice_free (LoadSimSlotsContext, ctx); } static void sim_slot_free (MMBaseSim *sim) { if (sim) g_object_unref (sim); } static gboolean load_sim_slots_finish (MMIfaceModem *self, GAsyncResult *res, GPtrArray **sim_slots, guint *primary_sim_slot, GError **error) { LoadSimSlotsContext *ctx; if (!g_task_propagate_boolean (G_TASK (res), error)) return FALSE; ctx = g_task_get_task_data (G_TASK (res)); if (sim_slots) *sim_slots = g_steal_pointer (&ctx->sim_slots); if (primary_sim_slot) *primary_sim_slot = ctx->active_slot_index; return TRUE; } static void scfg_query_ready (MMBaseModem *_self, GAsyncResult *res, GTask *task) { MMBroadbandModemCinterion *self = MM_BROADBAND_MODEM_CINTERION (_self); LoadSimSlotsContext *ctx; MMBaseSim *active_sim; const gchar *response; GError *error = NULL; guint active_slot; ctx = g_task_get_task_data (task); response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error); if (!response || !mm_cinterion_parse_scfg_sim_response (response, &active_slot, &error)) { g_task_return_error (task, error); return; } mm_obj_info (self, "active SIM slot request successful"); ctx->active_slot_index = active_slot; active_sim = g_ptr_array_index (ctx->sim_slots, active_slot - 1); if (active_sim != NULL) g_object_set (G_OBJECT (active_sim), "active", TRUE, NULL); g_task_return_boolean (task, TRUE); g_object_unref (task); } static void cinterion_simlocal_unsolicited_handler (MMPortSerialAt *port, GMatchInfo *match_info, MMBroadbandModemCinterion *self) { g_autoptr(GError) error = NULL; g_autoptr(GArray) available = NULL; g_autoptr(GPtrArray) sim_slots = NULL; g_autofree gchar *response = NULL; guint i; response = g_match_info_fetch (match_info, 1); if (!response || !mm_cinterion_get_available_from_simlocal (response, &available, &error)) { mm_obj_warn (self, "Could not parse list of available SIMs: %s", error->message); return; } g_object_get (self, MM_IFACE_MODEM_SIM_SLOTS, &sim_slots, NULL); for (i = 0; i < sim_slots->len; i++) { MMBaseSim *sim; gboolean is_available; sim = g_ptr_array_index (sim_slots, i); is_available = g_array_index (available, gboolean, i); if (sim == NULL && is_available) { mm_obj_info (self, "SIM in slot %i inserted", i + 1); sim = mm_base_sim_new_initialized (MM_BASE_MODEM (self), i + 1, FALSE, NULL, NULL, NULL, NULL, NULL, NULL); mm_iface_modem_modify_sim (MM_IFACE_MODEM (self), i, sim); } else if (sim != NULL && !is_available) { mm_obj_info (self, "SIM in slot %i removed", i + 1); mm_iface_modem_modify_sim (MM_IFACE_MODEM (self), i, NULL); } } } static void cinterion_slot_availability_init_ready (MMBaseModem *_self, GAsyncResult *res, GTask *task) { MMBroadbandModemCinterion *self = MM_BROADBAND_MODEM_CINTERION (_self); const gchar *response; MMPortSerialAt *primary; MMPortSerialAt *secondary; LoadSimSlotsContext *ctx; g_autoptr(GArray) available = NULL; g_autoptr(GError) error = NULL; guint i; response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error); if (!response || !mm_cinterion_parse_sind_simlocal_response (response, &available, &error)) { g_prefix_error (&error, "Could not enable simlocal: "); g_task_return_error (task, g_steal_pointer (&error)); g_object_unref (task); return; } primary = mm_base_modem_peek_port_primary (MM_BASE_MODEM (self)); mm_port_serial_at_add_unsolicited_msg_handler ( primary, self->priv->simlocal_regex, (MMPortSerialAtUnsolicitedMsgFn) cinterion_simlocal_unsolicited_handler, self, NULL); secondary = mm_base_modem_peek_port_secondary (MM_BASE_MODEM (self)); if (secondary) mm_port_serial_at_add_unsolicited_msg_handler ( secondary, self->priv->simlocal_regex, (MMPortSerialAtUnsolicitedMsgFn) cinterion_simlocal_unsolicited_handler, self, NULL); mm_obj_info (self, "SIM availability change with simlocal successfully enabled"); ctx = g_task_get_task_data (task); ctx->number_slots = available->len; ctx->sim_slots = g_ptr_array_new_full (ctx->number_slots, (GDestroyNotify)sim_slot_free); for (i = 0; i < ctx->number_slots; i++) { MMBaseSim *sim = NULL; gboolean is_available; is_available = g_array_index (available, gboolean, i); if (is_available) sim = mm_base_sim_new_initialized (MM_BASE_MODEM (self), i + 1, FALSE, NULL, NULL, NULL, NULL, NULL, NULL); g_ptr_array_add (ctx->sim_slots, sim); } mm_base_modem_at_command (MM_BASE_MODEM (self), "^SCFG?", 10, FALSE, (GAsyncReadyCallback)scfg_query_ready, task); } static void load_sim_slots (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; LoadSimSlotsContext *ctx; task = g_task_new (self, NULL, callback, user_data); ctx = g_slice_new0 (LoadSimSlotsContext); g_task_set_task_data (task, ctx, (GDestroyNotify)load_sim_slots_context_free); mm_base_modem_at_command (MM_BASE_MODEM (self), "^SIND=\"simlocal\",1", 3, FALSE, (GAsyncReadyCallback)cinterion_slot_availability_init_ready, task); } static void set_primary_sim_slot_ready (MMBaseModem *_self, GAsyncResult *res, GTask *task) { GError *error = NULL; if (error) g_task_return_error (task, error); else g_task_return_boolean (task, TRUE); g_object_unref (task); } static void set_primary_sim_slot (MMIfaceModem *self, guint sim_slot, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; g_autofree gchar *cmd = NULL; task = g_task_new (self, NULL, callback, user_data); cmd = g_strdup_printf ("^SCFG=\"SIM/CS\",\"SIM_%i\"", sim_slot); mm_base_modem_at_command (MM_BASE_MODEM (self), cmd, 10, FALSE, (GAsyncReadyCallback)set_primary_sim_slot_ready, task); } static gboolean set_primary_sim_slot_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void iface_modem_init (MMIfaceModem *iface) { iface_modem_parent = g_type_interface_peek_parent (iface); iface->create_bearer = cinterion_modem_create_bearer; iface->create_bearer_finish = cinterion_modem_create_bearer_finish; iface->load_supported_modes = load_supported_modes; iface->load_supported_modes_finish = load_supported_modes_finish; iface->set_current_modes = set_current_modes; iface->set_current_modes_finish = set_current_modes_finish; iface->load_supported_bands = load_supported_bands; iface->load_supported_bands_finish = load_supported_bands_finish; iface->load_current_bands = load_current_bands; iface->load_current_bands_finish = load_current_bands_finish; iface->set_current_bands = set_current_bands; iface->set_current_bands_finish = set_current_bands_finish; iface->load_access_technologies = load_access_technologies; iface->load_access_technologies_finish = load_access_technologies_finish; iface->setup_flow_control = setup_flow_control; iface->setup_flow_control_finish = setup_flow_control_finish; iface->modem_after_sim_unlock = after_sim_unlock; iface->modem_after_sim_unlock_finish = after_sim_unlock_finish; iface->load_unlock_retries = load_unlock_retries; iface->load_unlock_retries_finish = load_unlock_retries_finish; iface->reset = mm_shared_cinterion_modem_reset; iface->reset_finish = mm_shared_cinterion_modem_reset_finish; iface->modem_power_down = modem_power_down; iface->modem_power_down_finish = modem_power_down_finish; iface->modem_power_off = modem_power_off; iface->modem_power_off_finish = modem_power_off_finish; iface->setup_sim_hot_swap = modem_setup_sim_hot_swap; iface->setup_sim_hot_swap_finish = modem_setup_sim_hot_swap_finish; iface->cleanup_sim_hot_swap = modem_cleanup_sim_hot_swap; iface->load_sim_slots = load_sim_slots; iface->load_sim_slots_finish = load_sim_slots_finish; iface->set_primary_sim_slot = set_primary_sim_slot; iface->set_primary_sim_slot_finish = set_primary_sim_slot_finish; } static MMIfaceModem * peek_parent_interface (MMSharedCinterion *self) { return iface_modem_parent; } static void iface_modem_3gpp_init (MMIfaceModem3gpp *iface) { iface_modem_3gpp_parent = g_type_interface_peek_parent (iface); iface->enable_unsolicited_events = modem_3gpp_enable_unsolicited_events; iface->enable_unsolicited_events_finish = modem_3gpp_enable_unsolicited_events_finish; iface->disable_unsolicited_events = modem_3gpp_disable_unsolicited_events; iface->disable_unsolicited_events_finish = modem_3gpp_disable_unsolicited_events_finish; iface->setup_unsolicited_events = modem_3gpp_setup_unsolicited_events; iface->setup_unsolicited_events_finish = modem_3gpp_setup_cleanup_unsolicited_events_finish; iface->cleanup_unsolicited_events = modem_3gpp_cleanup_unsolicited_events; iface->cleanup_unsolicited_events_finish = modem_3gpp_setup_cleanup_unsolicited_events_finish; iface->load_initial_eps_bearer = modem_3gpp_load_initial_eps_bearer; iface->load_initial_eps_bearer_finish = modem_3gpp_load_initial_eps_bearer_finish; iface->load_initial_eps_bearer_settings = modem_3gpp_load_initial_eps_bearer_settings; iface->load_initial_eps_bearer_settings_finish = modem_3gpp_load_initial_eps_bearer_settings_finish; iface->set_initial_eps_bearer_settings = modem_3gpp_set_initial_eps_bearer_settings; iface->set_initial_eps_bearer_settings_finish = modem_3gpp_set_initial_eps_bearer_settings_finish; } static void iface_modem_messaging_init (MMIfaceModemMessaging *iface) { iface->check_support = messaging_check_support; iface->check_support_finish = messaging_check_support_finish; iface->enable_unsolicited_events = messaging_enable_unsolicited_events; iface->enable_unsolicited_events_finish = messaging_enable_unsolicited_events_finish; } static void iface_modem_location_init (MMIfaceModemLocation *iface) { iface_modem_location_parent = g_type_interface_peek_parent (iface); iface->load_capabilities = mm_shared_cinterion_location_load_capabilities; iface->load_capabilities_finish = mm_shared_cinterion_location_load_capabilities_finish; iface->enable_location_gathering = mm_shared_cinterion_enable_location_gathering; iface->enable_location_gathering_finish = mm_shared_cinterion_enable_location_gathering_finish; iface->disable_location_gathering = mm_shared_cinterion_disable_location_gathering; iface->disable_location_gathering_finish = mm_shared_cinterion_disable_location_gathering_finish; } static MMIfaceModemLocation * peek_parent_location_interface (MMSharedCinterion *self) { return iface_modem_location_parent; } static void iface_modem_voice_init (MMIfaceModemVoice *iface) { iface_modem_voice_parent = g_type_interface_peek_parent (iface); iface->create_call = mm_shared_cinterion_create_call; iface->check_support = mm_shared_cinterion_voice_check_support; iface->check_support_finish = mm_shared_cinterion_voice_check_support_finish; iface->enable_unsolicited_events = mm_shared_cinterion_voice_enable_unsolicited_events; iface->enable_unsolicited_events_finish = mm_shared_cinterion_voice_enable_unsolicited_events_finish; iface->disable_unsolicited_events = mm_shared_cinterion_voice_disable_unsolicited_events; iface->disable_unsolicited_events_finish = mm_shared_cinterion_voice_disable_unsolicited_events_finish; iface->setup_unsolicited_events = mm_shared_cinterion_voice_setup_unsolicited_events; iface->setup_unsolicited_events_finish = mm_shared_cinterion_voice_setup_unsolicited_events_finish; iface->cleanup_unsolicited_events = mm_shared_cinterion_voice_cleanup_unsolicited_events; iface->cleanup_unsolicited_events_finish = mm_shared_cinterion_voice_cleanup_unsolicited_events_finish; } static MMIfaceModemVoice * peek_parent_voice_interface (MMSharedCinterion *self) { return iface_modem_voice_parent; } static void iface_modem_time_init (MMIfaceModemTime *iface) { iface_modem_time_parent = g_type_interface_peek_parent (iface); iface->setup_unsolicited_events = mm_shared_cinterion_time_setup_unsolicited_events; iface->setup_unsolicited_events_finish = mm_shared_cinterion_time_setup_unsolicited_events_finish; iface->cleanup_unsolicited_events = mm_shared_cinterion_time_cleanup_unsolicited_events; iface->cleanup_unsolicited_events_finish = mm_shared_cinterion_time_cleanup_unsolicited_events_finish; } static MMIfaceModemTime * peek_parent_time_interface (MMSharedCinterion *self) { return iface_modem_time_parent; } static void shared_cinterion_init (MMSharedCinterion *iface) { iface->peek_parent_interface = peek_parent_interface; iface->peek_parent_location_interface = peek_parent_location_interface; iface->peek_parent_voice_interface = peek_parent_voice_interface; iface->peek_parent_time_interface = peek_parent_time_interface; } static void iface_modem_signal_init (MMIfaceModemSignal *iface) { iface_modem_signal_parent = g_type_interface_peek_parent (iface); iface->check_support = signal_check_support; iface->check_support_finish = signal_check_support_finish; iface->load_values = signal_load_values; iface->load_values_finish = signal_load_values_finish; } static void mm_broadband_modem_cinterion_class_init (MMBroadbandModemCinterionClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); MMBroadbandModemClass *broadband_modem_class = MM_BROADBAND_MODEM_CLASS (klass); g_type_class_add_private (object_class, sizeof (MMBroadbandModemCinterionPrivate)); /* Virtual methods */ object_class->finalize = finalize; broadband_modem_class->setup_ports = setup_ports; } ModemManager-1.23.4-dev/src/plugins/cinterion/mm-broadband-modem-cinterion.h000066400000000000000000000053651456466623000267630ustar00rootroot00000000000000/* -*- 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) 2011 Ammonit Measurement GmbH * Copyright (C) 2011 Google Inc. * Author: Aleksander Morgado */ #ifndef MM_BROADBAND_MODEM_CINTERION_H #define MM_BROADBAND_MODEM_CINTERION_H #include "mm-broadband-modem.h" #include "mm-modem-helpers-cinterion.h" #define MM_TYPE_BROADBAND_MODEM_CINTERION (mm_broadband_modem_cinterion_get_type ()) #define MM_BROADBAND_MODEM_CINTERION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_BROADBAND_MODEM_CINTERION, MMBroadbandModemCinterion)) #define MM_BROADBAND_MODEM_CINTERION_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_BROADBAND_MODEM_CINTERION, MMBroadbandModemCinterionClass)) #define MM_IS_BROADBAND_MODEM_CINTERION(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_BROADBAND_MODEM_CINTERION)) #define MM_IS_BROADBAND_MODEM_CINTERION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_BROADBAND_MODEM_CINTERION)) #define MM_BROADBAND_MODEM_CINTERION_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_BROADBAND_MODEM_CINTERION, MMBroadbandModemCinterionClass)) typedef struct _MMBroadbandModemCinterion MMBroadbandModemCinterion; typedef struct _MMBroadbandModemCinterionClass MMBroadbandModemCinterionClass; typedef struct _MMBroadbandModemCinterionPrivate MMBroadbandModemCinterionPrivate; struct _MMBroadbandModemCinterion { MMBroadbandModem parent; MMBroadbandModemCinterionPrivate *priv; }; struct _MMBroadbandModemCinterionClass{ MMBroadbandModemClass parent; }; GType mm_broadband_modem_cinterion_get_type (void); MMBroadbandModemCinterion *mm_broadband_modem_cinterion_new (const gchar *device, const gchar *physdev, const gchar **drivers, const gchar *plugin, guint16 vendor_id, guint16 product_id); MMCinterionModemFamily mm_broadband_modem_cinterion_get_family (MMBroadbandModemCinterion * modem); #endif /* MM_BROADBAND_MODEM_CINTERION_H */ ModemManager-1.23.4-dev/src/plugins/cinterion/mm-broadband-modem-mbim-cinterion.c000066400000000000000000000162251456466623000276750ustar00rootroot00000000000000/* -*- 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) 2021 Aleksander Morgado */ #include #include #include #include #include #include #include "ModemManager.h" #include "mm-log.h" #include "mm-errors-types.h" #include "mm-iface-modem.h" #include "mm-iface-modem-location.h" #include "mm-iface-modem-voice.h" #include "mm-broadband-modem-mbim-cinterion.h" #include "mm-shared-cinterion.h" static void iface_modem_init (MMIfaceModem *iface); static void iface_modem_location_init (MMIfaceModemLocation *iface); static void iface_modem_voice_init (MMIfaceModemVoice *iface); static void iface_modem_time_init (MMIfaceModemTime *iface); static void shared_cinterion_init (MMSharedCinterion *iface); static MMIfaceModem *iface_modem_parent; static MMIfaceModemLocation *iface_modem_location_parent; static MMIfaceModemVoice *iface_modem_voice_parent; static MMIfaceModemTime *iface_modem_time_parent; G_DEFINE_TYPE_EXTENDED (MMBroadbandModemMbimCinterion, mm_broadband_modem_mbim_cinterion, MM_TYPE_BROADBAND_MODEM_MBIM, 0, G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM, iface_modem_init) G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_LOCATION, iface_modem_location_init) G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_VOICE, iface_modem_voice_init) G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_TIME, iface_modem_time_init) G_IMPLEMENT_INTERFACE (MM_TYPE_SHARED_CINTERION, shared_cinterion_init)) /*****************************************************************************/ MMBroadbandModemMbimCinterion * mm_broadband_modem_mbim_cinterion_new (const gchar *device, const gchar *physdev, const gchar **drivers, const gchar *plugin, guint16 vendor_id, guint16 product_id) { return g_object_new (MM_TYPE_BROADBAND_MODEM_MBIM_CINTERION, MM_BASE_MODEM_DEVICE, device, MM_BASE_MODEM_PHYSDEV, physdev, MM_BASE_MODEM_DRIVERS, drivers, MM_BASE_MODEM_PLUGIN, plugin, MM_BASE_MODEM_VENDOR_ID, vendor_id, MM_BASE_MODEM_PRODUCT_ID, product_id, /* MBIM bearer supports NET only */ MM_BASE_MODEM_DATA_NET_SUPPORTED, TRUE, MM_BASE_MODEM_DATA_TTY_SUPPORTED, FALSE, MM_IFACE_MODEM_SIM_HOT_SWAP_SUPPORTED, TRUE, MM_IFACE_MODEM_PERIODIC_SIGNAL_CHECK_DISABLED, TRUE, MM_BROADBAND_MODEM_MBIM_INTEL_FIRMWARE_UPDATE_UNSUPPORTED, TRUE, NULL); } static void mm_broadband_modem_mbim_cinterion_init (MMBroadbandModemMbimCinterion *self) { } static void iface_modem_init (MMIfaceModem *iface) { iface_modem_parent = g_type_interface_peek_parent (iface); iface->reset = mm_shared_cinterion_modem_reset; iface->reset_finish = mm_shared_cinterion_modem_reset_finish; } static MMIfaceModem * peek_parent_interface (MMSharedCinterion *self) { return iface_modem_parent; } static void iface_modem_location_init (MMIfaceModemLocation *iface) { iface_modem_location_parent = g_type_interface_peek_parent (iface); iface->load_capabilities = mm_shared_cinterion_location_load_capabilities; iface->load_capabilities_finish = mm_shared_cinterion_location_load_capabilities_finish; iface->enable_location_gathering = mm_shared_cinterion_enable_location_gathering; iface->enable_location_gathering_finish = mm_shared_cinterion_enable_location_gathering_finish; iface->disable_location_gathering = mm_shared_cinterion_disable_location_gathering; iface->disable_location_gathering_finish = mm_shared_cinterion_disable_location_gathering_finish; } static MMIfaceModemLocation * peek_parent_location_interface (MMSharedCinterion *self) { return iface_modem_location_parent; } static void iface_modem_voice_init (MMIfaceModemVoice *iface) { iface_modem_voice_parent = g_type_interface_peek_parent (iface); iface->create_call = mm_shared_cinterion_create_call; iface->check_support = mm_shared_cinterion_voice_check_support; iface->check_support_finish = mm_shared_cinterion_voice_check_support_finish; iface->enable_unsolicited_events = mm_shared_cinterion_voice_enable_unsolicited_events; iface->enable_unsolicited_events_finish = mm_shared_cinterion_voice_enable_unsolicited_events_finish; iface->disable_unsolicited_events = mm_shared_cinterion_voice_disable_unsolicited_events; iface->disable_unsolicited_events_finish = mm_shared_cinterion_voice_disable_unsolicited_events_finish; iface->setup_unsolicited_events = mm_shared_cinterion_voice_setup_unsolicited_events; iface->setup_unsolicited_events_finish = mm_shared_cinterion_voice_setup_unsolicited_events_finish; iface->cleanup_unsolicited_events = mm_shared_cinterion_voice_cleanup_unsolicited_events; iface->cleanup_unsolicited_events_finish = mm_shared_cinterion_voice_cleanup_unsolicited_events_finish; } static MMIfaceModemVoice * peek_parent_voice_interface (MMSharedCinterion *self) { return iface_modem_voice_parent; } static void iface_modem_time_init (MMIfaceModemTime *iface) { iface_modem_time_parent = g_type_interface_peek_parent (iface); iface->setup_unsolicited_events = mm_shared_cinterion_time_setup_unsolicited_events; iface->setup_unsolicited_events_finish = mm_shared_cinterion_time_setup_unsolicited_events_finish; iface->cleanup_unsolicited_events = mm_shared_cinterion_time_cleanup_unsolicited_events; iface->cleanup_unsolicited_events_finish = mm_shared_cinterion_time_cleanup_unsolicited_events_finish; } static MMIfaceModemTime * peek_parent_time_interface (MMSharedCinterion *self) { return iface_modem_time_parent; } static void shared_cinterion_init (MMSharedCinterion *iface) { iface->peek_parent_interface = peek_parent_interface; iface->peek_parent_location_interface = peek_parent_location_interface; iface->peek_parent_voice_interface = peek_parent_voice_interface; iface->peek_parent_time_interface = peek_parent_time_interface; } static void mm_broadband_modem_mbim_cinterion_class_init (MMBroadbandModemMbimCinterionClass *klass) { } ModemManager-1.23.4-dev/src/plugins/cinterion/mm-broadband-modem-mbim-cinterion.h000066400000000000000000000051661456466623000277040ustar00rootroot00000000000000/* -*- 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) 2021 Aleksander Morgado */ #ifndef MM_BROADBAND_MODEM_MBIM_CINTERION_MBIM_H #define MM_BROADBAND_MODEM_MBIM_CINTERION_MBIM_H #include "mm-broadband-modem-mbim.h" #define MM_TYPE_BROADBAND_MODEM_MBIM_CINTERION (mm_broadband_modem_mbim_cinterion_get_type ()) #define MM_BROADBAND_MODEM_MBIM_CINTERION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_BROADBAND_MODEM_MBIM_CINTERION, MMBroadbandModemMbimCinterion)) #define MM_BROADBAND_MODEM_MBIM_CINTERION_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_BROADBAND_MODEM_MBIM_CINTERION, MMBroadbandModemMbimCinterionClass)) #define MM_IS_BROADBAND_MODEM_MBIM_CINTERION(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_BROADBAND_MODEM_MBIM_CINTERION)) #define MM_IS_BROADBAND_MODEM_MBIM_CINTERION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_BROADBAND_MODEM_MBIM_CINTERION)) #define MM_BROADBAND_MODEM_MBIM_CINTERION_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_BROADBAND_MODEM_MBIM_CINTERION, MMBroadbandModemMbimCinterionClass)) typedef struct _MMBroadbandModemMbimCinterion MMBroadbandModemMbimCinterion; typedef struct _MMBroadbandModemMbimCinterionClass MMBroadbandModemMbimCinterionClass; struct _MMBroadbandModemMbimCinterion { MMBroadbandModemMbim parent; }; struct _MMBroadbandModemMbimCinterionClass{ MMBroadbandModemMbimClass parent; }; GType mm_broadband_modem_mbim_cinterion_get_type (void); MMBroadbandModemMbimCinterion *mm_broadband_modem_mbim_cinterion_new (const gchar *device, const gchar *physdev, const gchar **drivers, const gchar *plugin, guint16 vendor_id, guint16 product_id); #endif /* MM_BROADBAND_MODEM_MBIM_CINTERION_H */ ModemManager-1.23.4-dev/src/plugins/cinterion/mm-broadband-modem-qmi-cinterion.c000066400000000000000000000160041456466623000275320ustar00rootroot00000000000000/* -*- 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) 2014 Ammonit Measurement GmbH * Author: Aleksander Morgado */ #include #include #include #include #include #include #include "ModemManager.h" #include "mm-log.h" #include "mm-errors-types.h" #include "mm-iface-modem.h" #include "mm-iface-modem-location.h" #include "mm-iface-modem-voice.h" #include "mm-broadband-modem-qmi-cinterion.h" #include "mm-shared-cinterion.h" static void iface_modem_init (MMIfaceModem *iface); static void iface_modem_location_init (MMIfaceModemLocation *iface); static void iface_modem_voice_init (MMIfaceModemVoice *iface); static void iface_modem_time_init (MMIfaceModemTime *iface); static void shared_cinterion_init (MMSharedCinterion *iface); static MMIfaceModem *iface_modem_parent; static MMIfaceModemLocation *iface_modem_location_parent; static MMIfaceModemVoice *iface_modem_voice_parent; static MMIfaceModemTime *iface_modem_time_parent; G_DEFINE_TYPE_EXTENDED (MMBroadbandModemQmiCinterion, mm_broadband_modem_qmi_cinterion, MM_TYPE_BROADBAND_MODEM_QMI, 0, G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM, iface_modem_init) G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_LOCATION, iface_modem_location_init) G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_VOICE, iface_modem_voice_init) G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_TIME, iface_modem_time_init) G_IMPLEMENT_INTERFACE (MM_TYPE_SHARED_CINTERION, shared_cinterion_init)) /*****************************************************************************/ MMBroadbandModemQmiCinterion * mm_broadband_modem_qmi_cinterion_new (const gchar *device, const gchar *physdev, const gchar **drivers, const gchar *plugin, guint16 vendor_id, guint16 product_id) { return g_object_new (MM_TYPE_BROADBAND_MODEM_QMI_CINTERION, MM_BASE_MODEM_DEVICE, device, MM_BASE_MODEM_PHYSDEV, physdev, MM_BASE_MODEM_DRIVERS, drivers, MM_BASE_MODEM_PLUGIN, plugin, MM_BASE_MODEM_VENDOR_ID, vendor_id, MM_BASE_MODEM_PRODUCT_ID, product_id, /* QMI bearer supports NET only */ MM_BASE_MODEM_DATA_NET_SUPPORTED, TRUE, MM_BASE_MODEM_DATA_TTY_SUPPORTED, FALSE, MM_IFACE_MODEM_SIM_HOT_SWAP_SUPPORTED, TRUE, NULL); } static void mm_broadband_modem_qmi_cinterion_init (MMBroadbandModemQmiCinterion *self) { } static void iface_modem_init (MMIfaceModem *iface) { iface_modem_parent = g_type_interface_peek_parent (iface); iface->reset = mm_shared_cinterion_modem_reset; iface->reset_finish = mm_shared_cinterion_modem_reset_finish; } static MMIfaceModem * peek_parent_interface (MMSharedCinterion *self) { return iface_modem_parent; } static void iface_modem_location_init (MMIfaceModemLocation *iface) { iface_modem_location_parent = g_type_interface_peek_parent (iface); iface->load_capabilities = mm_shared_cinterion_location_load_capabilities; iface->load_capabilities_finish = mm_shared_cinterion_location_load_capabilities_finish; iface->enable_location_gathering = mm_shared_cinterion_enable_location_gathering; iface->enable_location_gathering_finish = mm_shared_cinterion_enable_location_gathering_finish; iface->disable_location_gathering = mm_shared_cinterion_disable_location_gathering; iface->disable_location_gathering_finish = mm_shared_cinterion_disable_location_gathering_finish; } static MMIfaceModemLocation * peek_parent_location_interface (MMSharedCinterion *self) { return iface_modem_location_parent; } static void iface_modem_voice_init (MMIfaceModemVoice *iface) { iface_modem_voice_parent = g_type_interface_peek_parent (iface); iface->create_call = mm_shared_cinterion_create_call; iface->check_support = mm_shared_cinterion_voice_check_support; iface->check_support_finish = mm_shared_cinterion_voice_check_support_finish; iface->enable_unsolicited_events = mm_shared_cinterion_voice_enable_unsolicited_events; iface->enable_unsolicited_events_finish = mm_shared_cinterion_voice_enable_unsolicited_events_finish; iface->disable_unsolicited_events = mm_shared_cinterion_voice_disable_unsolicited_events; iface->disable_unsolicited_events_finish = mm_shared_cinterion_voice_disable_unsolicited_events_finish; iface->setup_unsolicited_events = mm_shared_cinterion_voice_setup_unsolicited_events; iface->setup_unsolicited_events_finish = mm_shared_cinterion_voice_setup_unsolicited_events_finish; iface->cleanup_unsolicited_events = mm_shared_cinterion_voice_cleanup_unsolicited_events; iface->cleanup_unsolicited_events_finish = mm_shared_cinterion_voice_cleanup_unsolicited_events_finish; } static MMIfaceModemVoice * peek_parent_voice_interface (MMSharedCinterion *self) { return iface_modem_voice_parent; } static void iface_modem_time_init (MMIfaceModemTime *iface) { iface_modem_time_parent = g_type_interface_peek_parent (iface); iface->setup_unsolicited_events = mm_shared_cinterion_time_setup_unsolicited_events; iface->setup_unsolicited_events_finish = mm_shared_cinterion_time_setup_unsolicited_events_finish; iface->cleanup_unsolicited_events = mm_shared_cinterion_time_cleanup_unsolicited_events; iface->cleanup_unsolicited_events_finish = mm_shared_cinterion_time_cleanup_unsolicited_events_finish; } static MMIfaceModemTime * peek_parent_time_interface (MMSharedCinterion *self) { return iface_modem_time_parent; } static void shared_cinterion_init (MMSharedCinterion *iface) { iface->peek_parent_interface = peek_parent_interface; iface->peek_parent_location_interface = peek_parent_location_interface; iface->peek_parent_voice_interface = peek_parent_voice_interface; iface->peek_parent_time_interface = peek_parent_time_interface; } static void mm_broadband_modem_qmi_cinterion_class_init (MMBroadbandModemQmiCinterionClass *klass) { } ModemManager-1.23.4-dev/src/plugins/cinterion/mm-broadband-modem-qmi-cinterion.h000066400000000000000000000051411456466623000275370ustar00rootroot00000000000000/* -*- 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) 2014 Ammonit Measurement GmbH * Author: Aleksander Morgado */ #ifndef MM_BROADBAND_MODEM_QMI_CINTERION_QMI_H #define MM_BROADBAND_MODEM_QMI_CINTERION_QMI_H #include "mm-broadband-modem-qmi.h" #define MM_TYPE_BROADBAND_MODEM_QMI_CINTERION (mm_broadband_modem_qmi_cinterion_get_type ()) #define MM_BROADBAND_MODEM_QMI_CINTERION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_BROADBAND_MODEM_QMI_CINTERION, MMBroadbandModemQmiCinterion)) #define MM_BROADBAND_MODEM_QMI_CINTERION_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_BROADBAND_MODEM_QMI_CINTERION, MMBroadbandModemQmiCinterionClass)) #define MM_IS_BROADBAND_MODEM_QMI_CINTERION(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_BROADBAND_MODEM_QMI_CINTERION)) #define MM_IS_BROADBAND_MODEM_QMI_CINTERION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_BROADBAND_MODEM_QMI_CINTERION)) #define MM_BROADBAND_MODEM_QMI_CINTERION_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_BROADBAND_MODEM_QMI_CINTERION, MMBroadbandModemQmiCinterionClass)) typedef struct _MMBroadbandModemQmiCinterion MMBroadbandModemQmiCinterion; typedef struct _MMBroadbandModemQmiCinterionClass MMBroadbandModemQmiCinterionClass; struct _MMBroadbandModemQmiCinterion { MMBroadbandModemQmi parent; }; struct _MMBroadbandModemQmiCinterionClass{ MMBroadbandModemQmiClass parent; }; GType mm_broadband_modem_qmi_cinterion_get_type (void); MMBroadbandModemQmiCinterion *mm_broadband_modem_qmi_cinterion_new (const gchar *device, const gchar *physdev, const gchar **drivers, const gchar *plugin, guint16 vendor_id, guint16 product_id); #endif /* MM_BROADBAND_MODEM_QMI_CINTERION_H */ ModemManager-1.23.4-dev/src/plugins/cinterion/mm-modem-helpers-cinterion.c000066400000000000000000002206761456466623000265100ustar00rootroot00000000000000/* -*- 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) 2014 Aleksander Morgado * Copyright (C) 2016 Trimble Navigation Limited * Copyright (C) 2016 Matthew Stanger * Copyright (C) 2019 Purism SPC */ #include #include #include #include #include "ModemManager.h" #define _LIBMM_INSIDE_MM #include #include "mm-log-object.h" #include "mm-charsets.h" #include "mm-errors-types.h" #include "mm-modem-helpers-cinterion.h" #include "mm-modem-helpers.h" #include "mm-common-helpers.h" #include "mm-port-serial-at.h" /* Setup relationship between the 3G band bitmask in the modem and the bitmask * in ModemManager. */ typedef struct { guint32 cinterion_band_flag; MMModemBand mm_band; } CinterionBand; typedef struct { MMCinterionRbBlock cinterion_band_block; guint32 cinterion_band_flag; MMModemBand mm_band; } CinterionBandEx; /* Table checked in PLS8-X/E/J/V/US, HC25 & PHS8 references. The table includes 2/3/4G * frequencies. Depending on which one is configured, one access technology or * the other will be used. This may conflict with the allowed mode configuration * set, so you shouldn't for example set 3G frequency bands, and then use a * 2G-only allowed mode. */ static const CinterionBand cinterion_bands[] = { { (1 << 0), MM_MODEM_BAND_EGSM }, { (1 << 1), MM_MODEM_BAND_DCS }, { (1 << 2), MM_MODEM_BAND_G850 }, { (1 << 3), MM_MODEM_BAND_PCS }, { (1 << 4), MM_MODEM_BAND_UTRAN_1 }, { (1 << 5), MM_MODEM_BAND_UTRAN_2 }, { (1 << 6), MM_MODEM_BAND_UTRAN_5 }, { (1 << 7), MM_MODEM_BAND_UTRAN_8 }, { (1 << 8), MM_MODEM_BAND_UTRAN_6 }, { (1 << 9), MM_MODEM_BAND_UTRAN_4 }, { (1 << 10), MM_MODEM_BAND_UTRAN_19 }, { (1 << 12), MM_MODEM_BAND_UTRAN_3 }, { (1 << 13), MM_MODEM_BAND_EUTRAN_1 }, { (1 << 14), MM_MODEM_BAND_EUTRAN_2 }, { (1 << 15), MM_MODEM_BAND_EUTRAN_3 }, { (1 << 16), MM_MODEM_BAND_EUTRAN_4 }, { (1 << 17), MM_MODEM_BAND_EUTRAN_5 }, { (1 << 18), MM_MODEM_BAND_EUTRAN_7 }, { (1 << 19), MM_MODEM_BAND_EUTRAN_8 }, { (1 << 20), MM_MODEM_BAND_EUTRAN_17 }, { (1 << 21), MM_MODEM_BAND_EUTRAN_20 }, { (1 << 22), MM_MODEM_BAND_EUTRAN_13 }, { (1 << 24), MM_MODEM_BAND_EUTRAN_19 } }; static const CinterionBandEx cinterion_bands_ex[] = { { MM_CINTERION_RB_BLOCK_GSM, 0x00000001, MM_MODEM_BAND_EGSM }, { MM_CINTERION_RB_BLOCK_GSM, 0x00000002, MM_MODEM_BAND_DCS }, { MM_CINTERION_RB_BLOCK_GSM, 0x00000004, MM_MODEM_BAND_G850 }, { MM_CINTERION_RB_BLOCK_GSM, 0x00000008, MM_MODEM_BAND_PCS }, { MM_CINTERION_RB_BLOCK_UMTS, 0x00000001, MM_MODEM_BAND_UTRAN_1 }, { MM_CINTERION_RB_BLOCK_UMTS, 0x00000002, MM_MODEM_BAND_UTRAN_2 }, { MM_CINTERION_RB_BLOCK_UMTS, 0x00000004, MM_MODEM_BAND_UTRAN_3 }, { MM_CINTERION_RB_BLOCK_UMTS, 0x00000008, MM_MODEM_BAND_UTRAN_4 }, { MM_CINTERION_RB_BLOCK_UMTS, 0x00000010, MM_MODEM_BAND_UTRAN_5 }, { MM_CINTERION_RB_BLOCK_UMTS, 0x00000020, MM_MODEM_BAND_UTRAN_6 }, { MM_CINTERION_RB_BLOCK_UMTS, 0x00000080, MM_MODEM_BAND_UTRAN_8 }, { MM_CINTERION_RB_BLOCK_UMTS, 0x00000100, MM_MODEM_BAND_UTRAN_9 }, { MM_CINTERION_RB_BLOCK_UMTS, 0x00040000, MM_MODEM_BAND_UTRAN_19 }, { MM_CINTERION_RB_BLOCK_LTE_LOW, 0x00000001, MM_MODEM_BAND_EUTRAN_1 }, { MM_CINTERION_RB_BLOCK_LTE_LOW, 0x00000002, MM_MODEM_BAND_EUTRAN_2 }, { MM_CINTERION_RB_BLOCK_LTE_LOW, 0x00000004, MM_MODEM_BAND_EUTRAN_3 }, { MM_CINTERION_RB_BLOCK_LTE_LOW, 0x00000008, MM_MODEM_BAND_EUTRAN_4 }, { MM_CINTERION_RB_BLOCK_LTE_LOW, 0x00000010, MM_MODEM_BAND_EUTRAN_5 }, { MM_CINTERION_RB_BLOCK_LTE_LOW, 0x00000040, MM_MODEM_BAND_EUTRAN_7 }, { MM_CINTERION_RB_BLOCK_LTE_LOW, 0x00000080, MM_MODEM_BAND_EUTRAN_8 }, { MM_CINTERION_RB_BLOCK_LTE_LOW, 0x00000800, MM_MODEM_BAND_EUTRAN_12 }, { MM_CINTERION_RB_BLOCK_LTE_LOW, 0x00001000, MM_MODEM_BAND_EUTRAN_13 }, { MM_CINTERION_RB_BLOCK_LTE_LOW, 0x00010000, MM_MODEM_BAND_EUTRAN_17 }, { MM_CINTERION_RB_BLOCK_LTE_LOW, 0x00020000, MM_MODEM_BAND_EUTRAN_18 }, { MM_CINTERION_RB_BLOCK_LTE_LOW, 0x00040000, MM_MODEM_BAND_EUTRAN_19 }, { MM_CINTERION_RB_BLOCK_LTE_LOW, 0x00080000, MM_MODEM_BAND_EUTRAN_20 }, { MM_CINTERION_RB_BLOCK_LTE_LOW, 0x02000000, MM_MODEM_BAND_EUTRAN_26 }, { MM_CINTERION_RB_BLOCK_LTE_LOW, 0x08000000, MM_MODEM_BAND_EUTRAN_28 }, { MM_CINTERION_RB_BLOCK_LTE_LOW, 0x10000000, MM_MODEM_BAND_EUTRAN_29 }, { MM_CINTERION_RB_BLOCK_LTE_HIGH, 0x00000020, MM_MODEM_BAND_EUTRAN_38 }, { MM_CINTERION_RB_BLOCK_LTE_HIGH, 0x00000040, MM_MODEM_BAND_EUTRAN_39 }, { MM_CINTERION_RB_BLOCK_LTE_HIGH, 0x00000080, MM_MODEM_BAND_EUTRAN_40 }, { MM_CINTERION_RB_BLOCK_LTE_HIGH, 0x00000100, MM_MODEM_BAND_EUTRAN_41 } }; static const CinterionBandEx cinterion_bands_imt[] = { { MM_CINTERION_RB_BLOCK_GSM, 0x00000004, MM_MODEM_BAND_EGSM }, { MM_CINTERION_RB_BLOCK_GSM, 0x00000010, MM_MODEM_BAND_DCS }, { MM_CINTERION_RB_BLOCK_GSM, 0x00000020, MM_MODEM_BAND_PCS }, { MM_CINTERION_RB_BLOCK_GSM, 0x00000040, MM_MODEM_BAND_G850 }, { MM_CINTERION_RB_BLOCK_UMTS, 0x00000001, MM_MODEM_BAND_UTRAN_1 }, { MM_CINTERION_RB_BLOCK_UMTS, 0x00000002, MM_MODEM_BAND_UTRAN_2 }, { MM_CINTERION_RB_BLOCK_UMTS, 0x00000008, MM_MODEM_BAND_UTRAN_4 }, { MM_CINTERION_RB_BLOCK_UMTS, 0x00000010, MM_MODEM_BAND_UTRAN_5 }, { MM_CINTERION_RB_BLOCK_UMTS, 0x00000080, MM_MODEM_BAND_UTRAN_8 }, { MM_CINTERION_RB_BLOCK_UMTS, 0x00000100, MM_MODEM_BAND_UTRAN_9 }, { MM_CINTERION_RB_BLOCK_UMTS, 0x00040000, MM_MODEM_BAND_UTRAN_19 }, { MM_CINTERION_RB_BLOCK_LTE_LOW, 0x00000001, MM_MODEM_BAND_EUTRAN_1 }, { MM_CINTERION_RB_BLOCK_LTE_LOW, 0x00000002, MM_MODEM_BAND_EUTRAN_2 }, { MM_CINTERION_RB_BLOCK_LTE_LOW, 0x00000004, MM_MODEM_BAND_EUTRAN_3 }, { MM_CINTERION_RB_BLOCK_LTE_LOW, 0x00000008, MM_MODEM_BAND_EUTRAN_4 }, { MM_CINTERION_RB_BLOCK_LTE_LOW, 0x00000010, MM_MODEM_BAND_EUTRAN_5 }, { MM_CINTERION_RB_BLOCK_LTE_LOW, 0x00000040, MM_MODEM_BAND_EUTRAN_7 }, { MM_CINTERION_RB_BLOCK_LTE_LOW, 0x00000080, MM_MODEM_BAND_EUTRAN_8 }, { MM_CINTERION_RB_BLOCK_LTE_LOW, 0x00000800, MM_MODEM_BAND_EUTRAN_12 }, { MM_CINTERION_RB_BLOCK_LTE_LOW, 0x00020000, MM_MODEM_BAND_EUTRAN_18 }, { MM_CINTERION_RB_BLOCK_LTE_LOW, 0x00040000, MM_MODEM_BAND_EUTRAN_19 }, { MM_CINTERION_RB_BLOCK_LTE_LOW, 0x00080000, MM_MODEM_BAND_EUTRAN_20 }, { MM_CINTERION_RB_BLOCK_LTE_LOW, 0x08000000, MM_MODEM_BAND_EUTRAN_28 } }; /* Check valid combinations in 2G-only devices */ #define VALIDATE_2G_BAND(cinterion_mask) \ (cinterion_mask == 1 || \ cinterion_mask == 2 || \ cinterion_mask == 4 || \ cinterion_mask == 8 || \ cinterion_mask == 3 || \ cinterion_mask == 5 || \ cinterion_mask == 10 || \ cinterion_mask == 12 || \ cinterion_mask == 15) /*****************************************************************************/ /* ^SCFG (3G+LTE) test parser * * Example 3G: * AT^SCFG=? * ... * ^SCFG: "MEShutdown/OnIgnition",("on","off") * ^SCFG: "Radio/Band",("1-511","0-1") * ^SCFG: "Radio/NWSM",("0","1","2") * ... * ^SCFG: "Radio/Band\",("1"-"147") * * Example LTE1 (GSM charset): * AT^SCFG=? * ... * ^SCFG: "Radio/Band/2G",("0x00000004"-"0x00000074") * ^SCFG: "Radio/Band/3G",("0x00000001"-"0x0004019B") * ^SCFG: "Radio/Band/4G",("0x00000001"-"0x080E08DF") * ... * * Example LTE1 (UCS2 charset): * AT^SCFG=? * ... * ^SCFG: "Radio/Band/2G",("0030007800300030003000300030003000300034"-"0030007800300030003000300030003000370034") * ^SCFG: "Radio/Band/3G",("0030007800300030003000300030003000300031"-"0030007800300030003000340030003100390042") * ^SCFG: "Radio/Band/4G",("0030007800300030003000300030003000300031"-"0030007800300038003000450030003800440046") * ... * * Example LTE2 (all charsets): * AT^SCFG=? * ... * ^SCFG: "Radio/Band/2G",("00000001-0000000f"),,("0","1") * ^SCFG: "Radio/Band/3G",("00000001-000400b5"),,("0","1") * ^SCFG: "Radio/Band/4G",("00000001-8a0e00d5"),("00000002-000001e2"),("0","1") * ... */ static void parse_bands (guint bandlist, GArray **bands, MMCinterionRbBlock block, MMCinterionModemFamily modem_family) { guint i; const CinterionBandEx *ref_bands; guint nb_ref_bands; if (!bandlist) return; if (modem_family == MM_CINTERION_MODEM_FAMILY_IMT) { ref_bands = cinterion_bands_imt; nb_ref_bands = G_N_ELEMENTS (cinterion_bands_imt); } else { ref_bands = cinterion_bands_ex; nb_ref_bands = G_N_ELEMENTS (cinterion_bands_ex); } for (i = 0; i < nb_ref_bands; i++) { if (block == ref_bands[i].cinterion_band_block && (bandlist & ref_bands[i].cinterion_band_flag)) { if (G_UNLIKELY (!*bands)) *bands = g_array_sized_new (FALSE, FALSE, sizeof (MMModemBand), 23); g_array_append_val (*bands, ref_bands[i].mm_band); } } } static guint take_and_convert_from_matched_string (gchar *str, MMModemCharset charset, MMCinterionModemFamily modem_family, GError **error) { guint val = 0; g_autofree gchar *utf8 = NULL; g_autofree gchar *taken_str = str; if (!taken_str) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS, "Couldn't convert to integer number: no input string"); return 0; } if (modem_family == MM_CINTERION_MODEM_FAMILY_IMT) { utf8 = mm_modem_charset_str_to_utf8 (taken_str, -1, charset, FALSE, error); if (!utf8) { g_prefix_error (error, "Couldn't convert to integer number: "); return 0; } } if (!mm_get_uint_from_hex_str (utf8 ? utf8 : taken_str, &val)) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't convert to integer number: wrong hex encoding: %s", utf8 ? utf8 : taken_str); return 0; } return val; } gboolean mm_cinterion_parse_scfg_test (const gchar *response, MMCinterionModemFamily modem_family, MMModemCharset charset, GArray **supported_bands, MMCinterionRadioBandFormat *format, GError **error) { g_autoptr(GRegex) r1 = NULL; g_autoptr(GMatchInfo) match_info1 = NULL; g_autoptr(GRegex) r2 = NULL; g_autoptr(GMatchInfo) match_info2 = NULL; GError *inner_error = NULL; GArray *bands = NULL; g_assert (format); if (!response) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Missing response"); return FALSE; } r1 = g_regex_new ("\\^SCFG:\\s*\"Radio/Band\",\\((?:\")?([0-9]*)(?:\")?-(?:\")?([0-9]*)(?:\")?.*\\)", G_REGEX_DOLLAR_ENDONLY | G_REGEX_RAW, 0, NULL); g_assert (r1 != NULL); g_regex_match_full (r1, response, strlen (response), 0, 0, &match_info1, &inner_error); if (inner_error) goto finish; if (g_match_info_matches (match_info1)) { g_autofree gchar *maxbandstr = NULL; guint maxband = 0; *format = MM_CINTERION_RADIO_BAND_FORMAT_SINGLE; maxbandstr = mm_get_string_unquoted_from_match_info (match_info1, 2); if (maxbandstr) mm_get_uint_from_str (maxbandstr, &maxband); if (maxband == 0) { inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't parse ^SCFG=? response"); } else { guint i; for (i = 0; i < G_N_ELEMENTS (cinterion_bands); i++) { if (maxband & cinterion_bands[i].cinterion_band_flag) { if (G_UNLIKELY (!bands)) bands = g_array_sized_new (FALSE, FALSE, sizeof (MMModemBand), 9); g_array_append_val (bands, cinterion_bands[i].mm_band); } } } goto finish; } r2 = g_regex_new ("\\^SCFG:\\s*\"Radio/Band/([234]G)\"," "\\(\"?([0-9A-Fa-fx]*)\"?-\"?([0-9A-Fa-fx]*)\"?\\)" "(,*\\(\"?([0-9A-Fa-fx]*)\"?-\"?([0-9A-Fa-fx]*)\"?\\))?", 0, 0, NULL); g_assert (r2 != NULL); g_regex_match_full (r2, response, strlen (response), 0, 0, &match_info2, &inner_error); if (inner_error) goto finish; while (g_match_info_matches (match_info2)) { g_autofree gchar *techstr = NULL; guint maxband; *format = MM_CINTERION_RADIO_BAND_FORMAT_MULTIPLE; techstr = mm_get_string_unquoted_from_match_info (match_info2, 1); if (g_strcmp0 (techstr, "2G") == 0) { maxband = take_and_convert_from_matched_string (mm_get_string_unquoted_from_match_info (match_info2, 3), charset, modem_family, &inner_error); if (inner_error) break; parse_bands (maxband, &bands, MM_CINTERION_RB_BLOCK_GSM, modem_family); } else if (g_strcmp0 (techstr, "3G") == 0) { maxband = take_and_convert_from_matched_string (mm_get_string_unquoted_from_match_info (match_info2, 3), charset, modem_family, &inner_error); if (inner_error) break; parse_bands (maxband, &bands, MM_CINTERION_RB_BLOCK_UMTS, modem_family); } else if (g_strcmp0 (techstr, "4G") == 0) { maxband = take_and_convert_from_matched_string (mm_get_string_unquoted_from_match_info (match_info2, 3), charset, modem_family, &inner_error); if (inner_error) break; parse_bands (maxband, &bands, MM_CINTERION_RB_BLOCK_LTE_LOW, modem_family); if (modem_family == MM_CINTERION_MODEM_FAMILY_DEFAULT) { maxband = take_and_convert_from_matched_string (mm_get_string_unquoted_from_match_info (match_info2, 6), charset, modem_family, &inner_error); if (inner_error) break; parse_bands (maxband, &bands, MM_CINTERION_RB_BLOCK_LTE_HIGH, modem_family); } } else { inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't parse ^SCFG=? response"); break; } g_match_info_next (match_info2, NULL); } finish: /* set error only if not already given */ if (!bands && !inner_error) inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "No valid bands found in ^SCFG=? response"); if (inner_error) { g_propagate_error (error, inner_error); return FALSE; } g_assert (bands != NULL && bands->len > 0); *supported_bands = bands; return TRUE; } /*****************************************************************************/ /* ^SCFG response parser (2 types: 2G/3G and LTE) * * Example (3G): * AT^SCFG="Radio/Band" * ^SCFG: "Radio/Band",127 * * Example (2G, UCS-2): * AT+SCFG="Radio/Band" * ^SCFG: "Radio/Band","0031","0031" * * Example (2G): * AT+SCFG="Radio/Band" * ^SCFG: "Radio/Band","3","3" * * Example LTE1 (GSM charset): * AT^SCFG=? * ... * ^SCFG: "Radio/Band/2G","0x00000074" * ^SCFG: "Radio/Band/3G","0x0004019B" * ^SCFG: "Radio/Band/4G","0x080E08DF" * ... * AT^SCFG=? * ... * Example LTE1 (UCS2 charset): * AT^SCFG=? * ... * ^SCFG: "Radio/Band/2G","0030007800300030003000300030003000370034" * ^SCFG: "Radio/Band/3G","0030007800300030003000340030003100390042" * ^SCFG: "Radio/Band/4G","0030007800300038003000450030003800440046" * ... * Example LTE2 (all charsets): * AT^SCFG=? * ... * ^SCFG: "Radio/Band/2G","0000000f" * ^SCFG: "Radio/Band/3G","000400b5" * ^SCFG: "Radio/Band/4G","8a0e00d5","000000e2" * ... */ gboolean mm_cinterion_parse_scfg_response (const gchar *response, MMCinterionModemFamily modem_family, MMModemCharset charset, GArray **current_bands, MMCinterionRadioBandFormat format, GError **error) { g_autoptr(GRegex) r = NULL; g_autoptr(GMatchInfo) match_info = NULL; GError *inner_error = NULL; GArray *bands = NULL; if (!response) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Missing response"); return FALSE; } if (format == MM_CINTERION_RADIO_BAND_FORMAT_SINGLE) { r = g_regex_new ("\\^SCFG:\\s*\"Radio/Band\",\\s*\"?([0-9a-fA-F]*)\"?", 0, 0, NULL); g_assert (r != NULL); g_regex_match_full (r, response, strlen (response), 0, 0, &match_info, &inner_error); if (inner_error) goto finish; if (g_match_info_matches (match_info)) { g_autofree gchar *currentstr = NULL; guint current = 0; currentstr = mm_get_string_unquoted_from_match_info (match_info, 1); if (currentstr) mm_get_uint_from_str (currentstr, ¤t); if (current == 0) { inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't parse ^SCFG? response"); } else { guint i; for (i = 0; i < G_N_ELEMENTS (cinterion_bands); i++) { if (current & cinterion_bands[i].cinterion_band_flag) { if (G_UNLIKELY (!bands)) bands = g_array_sized_new (FALSE, FALSE, sizeof (MMModemBand), 9); g_array_append_val (bands, cinterion_bands[i].mm_band); } } } } } else if (format == MM_CINTERION_RADIO_BAND_FORMAT_MULTIPLE) { r = g_regex_new ("\\^SCFG:\\s*\"Radio/Band/([234]G)\",\"?([0-9A-Fa-fx]*)\"?,?\"?([0-9A-Fa-fx]*)?\"?", 0, 0, NULL); g_assert (r != NULL); g_regex_match_full (r, response, strlen (response), 0, 0, &match_info, &inner_error); if (inner_error) goto finish; while (g_match_info_matches (match_info)) { g_autofree gchar *techstr = NULL; guint current; techstr = mm_get_string_unquoted_from_match_info (match_info, 1); if (g_strcmp0 (techstr, "2G") == 0) { current = take_and_convert_from_matched_string (mm_get_string_unquoted_from_match_info (match_info, 2), charset, modem_family, &inner_error); if (inner_error) break; parse_bands (current, &bands, MM_CINTERION_RB_BLOCK_GSM, modem_family); } else if (g_strcmp0 (techstr, "3G") == 0) { current = take_and_convert_from_matched_string (mm_get_string_unquoted_from_match_info (match_info, 2), charset, modem_family, &inner_error); if (inner_error) break; parse_bands (current, &bands, MM_CINTERION_RB_BLOCK_UMTS, modem_family); } else if (g_strcmp0 (techstr, "4G") == 0) { current = take_and_convert_from_matched_string (mm_get_string_unquoted_from_match_info (match_info, 2), charset, modem_family, &inner_error); if (inner_error) break; parse_bands (current, &bands, MM_CINTERION_RB_BLOCK_LTE_LOW, modem_family); if (modem_family == MM_CINTERION_MODEM_FAMILY_DEFAULT) { current = take_and_convert_from_matched_string (mm_get_string_unquoted_from_match_info (match_info, 3), charset, modem_family, &inner_error); if (inner_error) break; parse_bands (current, &bands, MM_CINTERION_RB_BLOCK_LTE_HIGH, modem_family); } } else { inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't parse ^SCFG? response"); break; } g_match_info_next (match_info, NULL); } } else g_assert_not_reached (); finish: /* set error only if not already given */ if (!bands && !inner_error) inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "No valid bands found in ^SCFG response"); if (inner_error) { g_propagate_error (error, inner_error); return FALSE; } g_assert (bands != NULL && bands->len > 0); *current_bands = bands; return TRUE; } /*****************************************************************************/ /* ^SCFG response sim parser * * Example: * ... * ^SCFG: "SIM/CS","SIM_1" * ... */ gboolean mm_cinterion_parse_scfg_sim_response (const gchar *response, guint *active_slot, GError **error) { g_autoptr(GRegex) r = NULL; g_autoptr(GMatchInfo) match_info = NULL; GError *inner_error = NULL; if (!response) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Missing response"); return FALSE; } r = g_regex_new ("\\^SCFG:\\s*\"SIM/CS\",\".*?(\\d)\"", 0, 0, NULL); g_assert (r != NULL); g_regex_match_full (r, response, strlen (response), 0, 0, &match_info, &inner_error); if (inner_error) { g_prefix_error (&inner_error, "No valid SIM/CS entry found in ^SCFG response: "); g_propagate_error (error, inner_error); return FALSE; } if (!mm_get_uint_from_match_info (match_info, 1, active_slot)) { g_prefix_error (&inner_error, "Could not parse SIM slot index: "); g_propagate_error (error, inner_error); return FALSE; } return TRUE; } gboolean mm_cinterion_get_available_from_simlocal (const gchar *response, GArray **available, GError **error) { g_autoptr(GArray) tmp_available = NULL; GError *inner_error = NULL; if (!response) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Missing response"); return FALSE; } tmp_available = mm_parse_uint_list (response, &inner_error); if (inner_error) { g_propagate_error (error, inner_error); return FALSE; } *available = g_steal_pointer (&tmp_available); return TRUE; } /*****************************************************************************/ /* ^SIND response simlocal parser * * Example: * ^SIND: simlocal,1,0,0 */ gboolean mm_cinterion_parse_sind_simlocal_response (const gchar *response, GArray **available, GError **error) { g_autoptr(GRegex) r = NULL; g_autoptr(GMatchInfo) match_info = NULL; g_autofree gchar *str = NULL; GError *inner_error = NULL; if (!response) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Missing response"); return FALSE; } r = g_regex_new ("\\^SIND:\\s*simlocal,\\d+,((\\d,)*\\d)", 0, 0, NULL); g_assert (r != NULL); g_regex_match_full (r, response, strlen (response), 0, 0, &match_info, &inner_error); if (inner_error) { g_prefix_error (&inner_error, "No valid SIM/CS entry found in ^SCFG response: "); g_propagate_error (error, inner_error); return FALSE; } if (!g_match_info_matches (match_info)) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't match SIM slot index"); return FALSE; } str = g_match_info_fetch (match_info, 1); if (!mm_cinterion_get_available_from_simlocal (str, available, &inner_error)) { g_propagate_error (error, inner_error); return FALSE; } return TRUE; } /*****************************************************************************/ /* +CNMI test parser * * Example (PHS8): * AT+CNMI=? * +CNMI: (0,1,2),(0,1),(0,2),(0),(1) */ gboolean mm_cinterion_parse_cnmi_test (const gchar *response, GArray **supported_mode, GArray **supported_mt, GArray **supported_bm, GArray **supported_ds, GArray **supported_bfr, GError **error) { g_autoptr(GRegex) r = NULL; g_autoptr(GMatchInfo) match_info = NULL; g_autoptr(GArray) tmp_supported_mode = NULL; g_autoptr(GArray) tmp_supported_mt = NULL; g_autoptr(GArray) tmp_supported_bm = NULL; g_autoptr(GArray) tmp_supported_ds = NULL; g_autoptr(GArray) tmp_supported_bfr = NULL; GError *inner_error = NULL; if (!response) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Missing response"); return FALSE; } r = g_regex_new ("\\+CNMI:\\s*\\((.*)\\),\\((.*)\\),\\((.*)\\),\\((.*)\\),\\((.*)\\)", G_REGEX_DOLLAR_ENDONLY | G_REGEX_RAW, 0, NULL); g_assert (r != NULL); g_regex_match_full (r, response, strlen (response), 0, 0, &match_info, &inner_error); if (!inner_error && g_match_info_matches (match_info)) { if (supported_mode) { g_autofree gchar *str = NULL; str = mm_get_string_unquoted_from_match_info (match_info, 1); tmp_supported_mode = mm_parse_uint_list (str, &inner_error); if (inner_error) goto out; } if (supported_mt) { g_autofree gchar *str = NULL; str = mm_get_string_unquoted_from_match_info (match_info, 2); tmp_supported_mt = mm_parse_uint_list (str, &inner_error); if (inner_error) goto out; } if (supported_bm) { g_autofree gchar *str = NULL; str = mm_get_string_unquoted_from_match_info (match_info, 3); tmp_supported_bm = mm_parse_uint_list (str, &inner_error); if (inner_error) goto out; } if (supported_ds) { g_autofree gchar *str = NULL; str = mm_get_string_unquoted_from_match_info (match_info, 4); tmp_supported_ds = mm_parse_uint_list (str, &inner_error); if (inner_error) goto out; } if (supported_bfr) { g_autofree gchar *str = NULL; str = mm_get_string_unquoted_from_match_info (match_info, 5); tmp_supported_bfr = mm_parse_uint_list (str, &inner_error); if (inner_error) goto out; } } out: if (inner_error) { g_propagate_error (error, inner_error); return FALSE; } if (supported_mode) *supported_mode = g_steal_pointer (&tmp_supported_mode); if (supported_mt) *supported_mt = g_steal_pointer (&tmp_supported_mt); if (supported_bm) *supported_bm = g_steal_pointer (&tmp_supported_bm); if (supported_ds) *supported_ds = g_steal_pointer (&tmp_supported_ds); if (supported_bfr) *supported_bfr = g_steal_pointer (&tmp_supported_bfr); return TRUE; } /*****************************************************************************/ /* ^SXRAT test parser * * Example (ELS61-E2): * AT^SXRAT=? * ^SXRAT: (0-6),(0,2,3),(0,2,3) */ gboolean mm_cinterion_parse_sxrat_test (const gchar *response, GArray **supported_rat, GArray **supported_pref1, GArray **supported_pref2, GError **error) { g_autoptr(GRegex) r = NULL; g_autoptr(GMatchInfo) match_info = NULL; GError *inner_error = NULL; GArray *tmp_supported_rat = NULL; GArray *tmp_supported_pref1 = NULL; GArray *tmp_supported_pref2 = NULL; if (!response) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Missing response"); return FALSE; } r = g_regex_new ("\\^SXRAT:\\s*\\(([^\\)]*)\\),\\(([^\\)]*)\\)(,\\(([^\\)]*)\\))?(?:\\r\\n)?", G_REGEX_DOLLAR_ENDONLY | G_REGEX_RAW, 0, NULL); g_assert (r != NULL); g_regex_match_full (r, response, strlen (response), 0, 0, &match_info, &inner_error); if (!inner_error && g_match_info_matches (match_info)) { if (supported_rat) { g_autofree gchar *str = NULL; str = mm_get_string_unquoted_from_match_info (match_info, 1); tmp_supported_rat = mm_parse_uint_list (str, &inner_error); if (inner_error) goto out; } if (supported_pref1) { g_autofree gchar *str = NULL; str = mm_get_string_unquoted_from_match_info (match_info, 2); tmp_supported_pref1 = mm_parse_uint_list (str, &inner_error); if (inner_error) goto out; } if (supported_pref2) { g_autofree gchar *str = NULL; /* this match is optional */ str = mm_get_string_unquoted_from_match_info (match_info, 4); if (str) { tmp_supported_pref2 = mm_parse_uint_list (str, &inner_error); if (inner_error) goto out; } } } out: if (inner_error) { g_clear_pointer (&tmp_supported_rat, g_array_unref); g_clear_pointer (&tmp_supported_pref1, g_array_unref); g_clear_pointer (&tmp_supported_pref2, g_array_unref); g_propagate_error (error, inner_error); return FALSE; } if (supported_rat) *supported_rat = tmp_supported_rat; if (supported_pref1) *supported_pref1 = tmp_supported_pref1; if (supported_pref2) *supported_pref2 = tmp_supported_pref2; return TRUE; } /*****************************************************************************/ /* Build Cinterion-specific band value */ gboolean mm_cinterion_build_band (GArray *bands, guint *supported, gboolean only_2g, MMCinterionRadioBandFormat format, MMCinterionModemFamily modem_family, guint *out_band, GError **error) { guint band[MM_CINTERION_RB_BLOCK_N] = { 0 }; if (format == MM_CINTERION_RADIO_BAND_FORMAT_SINGLE) { /* The special case of ANY should be treated separately. */ if (bands->len == 1 && g_array_index (bands, MMModemBand, 0) == MM_MODEM_BAND_ANY) { if (supported) band[MM_CINTERION_RB_BLOCK_LEGACY] = supported[MM_CINTERION_RB_BLOCK_LEGACY]; } else { guint i; for (i = 0; i < G_N_ELEMENTS (cinterion_bands); i++) { guint j; for (j = 0; j < bands->len; j++) { if (g_array_index (bands, MMModemBand, j) == cinterion_bands[i].mm_band) { band[MM_CINTERION_RB_BLOCK_LEGACY] |= cinterion_bands[i].cinterion_band_flag; break; } } } /* 2G-only modems only support a subset of the possible band * combinations. Detect it early and error out. */ if (only_2g && !VALIDATE_2G_BAND (band[MM_CINTERION_RB_BLOCK_LEGACY])) band[MM_CINTERION_RB_BLOCK_LEGACY] = 0; } if (band[MM_CINTERION_RB_BLOCK_LEGACY] == 0) { g_autofree gchar *bands_string = NULL; bands_string = mm_common_build_bands_string ((MMModemBand *)(gpointer)bands->data, bands->len); g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "The given band combination is not supported: '%s'", bands_string); return FALSE; } } else { /* format == MM_CINTERION_RADIO_BAND_FORMAT_MULTIPLE */ if (bands->len == 1 && g_array_index (bands, MMModemBand, 0) == MM_MODEM_BAND_ANY) { if (supported) memcpy (band, supported, sizeof (guint) * MM_CINTERION_RB_BLOCK_N); } else { guint i; const CinterionBandEx *ref_bands; guint nb_ref_bands; if (modem_family == MM_CINTERION_MODEM_FAMILY_IMT) { ref_bands = cinterion_bands_imt; nb_ref_bands = G_N_ELEMENTS (cinterion_bands_imt); } else { ref_bands = cinterion_bands_ex; nb_ref_bands = G_N_ELEMENTS (cinterion_bands_ex); } for (i = 0; i < nb_ref_bands; i++) { guint j; for (j = 0; j < bands->len; j++) { if (g_array_index (bands, MMModemBand, j) == ref_bands[i].mm_band) { band[ref_bands[i].cinterion_band_block] |= ref_bands[i].cinterion_band_flag; break; } } } } /* this modem family does not allow disabling all bands in a given technology through this command */ if (modem_family == MM_CINTERION_MODEM_FAMILY_IMT && (!band[MM_CINTERION_RB_BLOCK_GSM] || !band[MM_CINTERION_RB_BLOCK_UMTS] || !band[MM_CINTERION_RB_BLOCK_LTE_LOW])) { g_autofree gchar *bands_string = NULL; bands_string = mm_common_build_bands_string ((MMModemBand *)(gpointer)bands->data, bands->len); g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "The given band combination is not supported: '%s'", bands_string); return FALSE; } } memcpy (out_band, band, sizeof (guint) * MM_CINTERION_RB_BLOCK_N); return TRUE; } /*****************************************************************************/ /* Single ^SIND response parser */ gboolean mm_cinterion_parse_sind_response (const gchar *response, gchar **description, guint *mode, guint *value, GError **error) { g_autoptr(GRegex) r = NULL; g_autoptr(GMatchInfo) match_info = NULL; guint errors = 0; if (!response) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Missing response"); return FALSE; } r = g_regex_new ("\\^SIND:\\s*(.*),(\\d+),(\\d+)(\\r\\n)?", 0, 0, NULL); g_assert (r != NULL); if (g_regex_match (r, response, 0, &match_info)) { if (description) { *description = mm_get_string_unquoted_from_match_info (match_info, 1); if (*description == NULL) errors++; } if (mode && !mm_get_uint_from_match_info (match_info, 2, mode)) errors++; if (value && !mm_get_uint_from_match_info (match_info, 3, value)) errors++; } else errors++; if (errors > 0) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Failed parsing ^SIND response"); return FALSE; } return TRUE; } /*****************************************************************************/ /* ^SWWAN read parser * * Description: Parses , [, ] or CME ERROR from SWWAN. * * The method returns a MMSwwanState with the connection status of a single * PDP context, the one being queried via the cid given as input. * * Note that we use CID for matching because the WWAN adapter field is optional * it seems. * * Read Command * AT^SWWAN? * Response(s) * [^SWWAN: , [, ]] * [^SWWAN: ...] * OK * ERROR * +CME ERROR: * * Examples: * OK - If no WWAN connection is active, then read command just returns OK * ^SWWAN: 3,1,1 - 3rd PDP Context, Activated, First WWAN Adaptor * +CME ERROR: ? - */ enum { MM_SWWAN_STATE_DISCONNECTED = 0, MM_SWWAN_STATE_CONNECTED = 1, }; MMBearerConnectionStatus mm_cinterion_parse_swwan_response (const gchar *response, guint cid, gpointer log_object, GError **error) { g_autoptr(GRegex) r = NULL; g_autoptr(GMatchInfo) match_info = NULL; GError *inner_error = NULL; MMBearerConnectionStatus status; g_assert (response); /* If no WWAN connection is active, then ^SWWAN read command just returns OK * (which we receive as an empty string) */ if (!response[0]) return MM_BEARER_CONNECTION_STATUS_DISCONNECTED; if (!g_str_has_prefix (response, "^SWWAN:")) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't parse ^SWWAN response: '%s'", response); return MM_BEARER_CONNECTION_STATUS_UNKNOWN; } r = g_regex_new ("\\^SWWAN:\\s*(\\d+),\\s*(\\d+)(?:,\\s*(\\d+))?(?:\\r\\n)?", G_REGEX_DOLLAR_ENDONLY | G_REGEX_RAW, 0, NULL); g_assert (r != NULL); status = MM_BEARER_CONNECTION_STATUS_UNKNOWN; g_regex_match_full (r, response, strlen (response), 0, 0, &match_info, &inner_error); while (!inner_error && g_match_info_matches (match_info)) { guint read_state; guint read_cid; if (!mm_get_uint_from_match_info (match_info, 1, &read_cid)) mm_obj_warn (log_object, "couldn't read cid in ^SWWAN response: %s", response); else if (!mm_get_uint_from_match_info (match_info, 2, &read_state)) mm_obj_warn (log_object, "couldn't read state in ^SWWAN response: %s", response); else if (read_cid == cid) { if (read_state == MM_SWWAN_STATE_CONNECTED) { status = MM_BEARER_CONNECTION_STATUS_CONNECTED; break; } if (read_state == MM_SWWAN_STATE_DISCONNECTED) { status = MM_BEARER_CONNECTION_STATUS_DISCONNECTED; break; } mm_obj_warn (log_object, "invalid state read in ^SWWAN response: %u", read_state); break; } g_match_info_next (match_info, &inner_error); } if (status == MM_BEARER_CONNECTION_STATUS_UNKNOWN) g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "No state returned for CID %u", cid); return status; } /*****************************************************************************/ /* ^SGAUTH response parser */ /* at^sgauth? * ^SGAUTH: 1,2,"vf" * ^SGAUTH: 3,0,"" * ^SGAUTH: 4,0 * * OK */ gboolean mm_cinterion_parse_sgauth_response (const gchar *response, guint cid, MMBearerAllowedAuth *out_auth, gchar **out_username, GError **error) { g_autoptr(GRegex) r = NULL; g_autoptr(GMatchInfo) match_info = NULL; r = g_regex_new ("\\^SGAUTH:\\s*(\\d+),(\\d+),?\"?([a-zA-Z0-9_-]+)?\"?", 0, 0, NULL); g_assert (r != NULL); g_regex_match_full (r, response, strlen (response), 0, 0, &match_info, NULL); while (g_match_info_matches (match_info)) { guint sgauth_cid = 0; if (mm_get_uint_from_match_info (match_info, 1, &sgauth_cid) && (sgauth_cid == cid)) { guint cinterion_auth_type = 0; mm_get_uint_from_match_info (match_info, 2, &cinterion_auth_type); *out_auth = mm_auth_type_from_cinterion_auth_type (cinterion_auth_type); *out_username = mm_get_string_unquoted_from_match_info (match_info, 3); return TRUE; } g_match_info_next (match_info, NULL); } g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_NOT_FOUND, "Auth settings for context %u not found", cid); return FALSE; } /*****************************************************************************/ /* ^SMONG response parser */ static gboolean get_access_technology_from_smong_gprs_status (guint gprs_status, MMModemAccessTechnology *out, GError **error) { switch (gprs_status) { case 0: *out = MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN; return TRUE; case 1: case 2: *out = MM_MODEM_ACCESS_TECHNOLOGY_GPRS; return TRUE; case 3: case 4: *out = MM_MODEM_ACCESS_TECHNOLOGY_EDGE; return TRUE; default: break; } g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS, "Couldn't get network capabilities, " "unsupported GPRS status value: '%u'", gprs_status); return FALSE; } gboolean mm_cinterion_parse_smong_response (const gchar *response, MMModemAccessTechnology *access_tech, GError **error) { guint value = 0; GError *inner_error = NULL; g_autoptr(GMatchInfo) match_info = NULL; g_autoptr(GRegex) regex = NULL; /* The AT^SMONG command returns a cell info table, where the second * column identifies the "GPRS status", which is exactly what we want. * So we'll try to read that second number in the values row. * * AT^SMONG * GPRS Monitor * BCCH G PBCCH PAT MCC MNC NOM TA RAC # Cell # * 0776 1 - - 214 03 2 00 01 * OK */ regex = g_regex_new (".*GPRS Monitor(?:\r\n)*" "BCCH\\s*G.*\\r\\n" "\\s*(\\d+)\\s*(\\d+)\\s*", G_REGEX_DOLLAR_ENDONLY | G_REGEX_RAW, 0, NULL); g_assert (regex); g_regex_match_full (regex, response, strlen (response), 0, 0, &match_info, &inner_error); if (inner_error) { g_prefix_error (&inner_error, "Failed to match AT^SMONG response: "); g_propagate_error (error, inner_error); return FALSE; } if (!g_match_info_matches (match_info) || !mm_get_uint_from_match_info (match_info, 2, &value)) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't read 'GPRS status' field from AT^SMONG response"); return FALSE; } return get_access_technology_from_smong_gprs_status (value, access_tech, error); } /*****************************************************************************/ /* ^SIND psinfo helper */ MMModemAccessTechnology mm_cinterion_get_access_technology_from_sind_psinfo (guint val, gpointer log_object) { switch (val) { case 0: return MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN; case 1: case 2: return MM_MODEM_ACCESS_TECHNOLOGY_GPRS; case 3: case 4: return MM_MODEM_ACCESS_TECHNOLOGY_EDGE; case 5: case 6: return MM_MODEM_ACCESS_TECHNOLOGY_UMTS; case 7: case 8: return MM_MODEM_ACCESS_TECHNOLOGY_HSDPA; case 9: case 10: return (MM_MODEM_ACCESS_TECHNOLOGY_HSDPA | MM_MODEM_ACCESS_TECHNOLOGY_HSUPA); case 16: case 17: return MM_MODEM_ACCESS_TECHNOLOGY_LTE; default: mm_obj_dbg (log_object, "unable to identify access technology from psinfo reported value: %u", val); return MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN; } } /*****************************************************************************/ /* ^SLCC psinfo helper */ GRegex * mm_cinterion_get_slcc_regex (void) { /* The list of active calls displayed with this URC will always be terminated * with an empty line preceded by prefix "^SLCC: ", in order to indicate the end * of the list. */ return g_regex_new ("\\r\\n(\\^SLCC: .*\\r\\n)*\\^SLCC: \\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); } static void cinterion_call_info_free (MMCallInfo *info) { if (!info) return; g_free (info->number); g_slice_free (MMCallInfo, info); } gboolean mm_cinterion_parse_slcc_list (const gchar *str, gpointer log_object, GList **out_list, GError **error) { g_autoptr(GRegex) r = NULL; g_autoptr(GMatchInfo) match_info = NULL; GList *list = NULL; GError *inner_error = NULL; static const MMCallDirection cinterion_call_direction[] = { [0] = MM_CALL_DIRECTION_OUTGOING, [1] = MM_CALL_DIRECTION_INCOMING, }; static const MMCallState cinterion_call_state[] = { [0] = MM_CALL_STATE_ACTIVE, [1] = MM_CALL_STATE_HELD, [2] = MM_CALL_STATE_DIALING, /* Dialing (MOC) */ [3] = MM_CALL_STATE_RINGING_OUT, /* Alerting (MOC) */ [4] = MM_CALL_STATE_RINGING_IN, /* Incoming (MTC) */ [5] = MM_CALL_STATE_WAITING, /* Waiting (MTC) */ }; g_assert (out_list); /* * 1 2 3 4 5 6 7 8 9 * ^SLCC: , , , , , [, , [,]] * [^SLCC: , , , , , [, , [,]]] * [... ] * ^SLCC : */ r = g_regex_new ("\\^SLCC:\\s*(\\d+),\\s*(\\d+),\\s*(\\d+),\\s*(\\d+),\\s*(\\d+),\\s*(\\d+)" /* mandatory fields */ "(?:,\\s*([^,]*),\\s*(\\d+)" /* number and type */ "(?:,\\s*([^,]*)" /* alpha */ ")?)?$", G_REGEX_RAW | G_REGEX_MULTILINE | G_REGEX_NEWLINE_CRLF, G_REGEX_MATCH_NEWLINE_CRLF, NULL); g_assert (r != NULL); g_regex_match_full (r, str, strlen (str), 0, 0, &match_info, &inner_error); if (inner_error) goto out; /* Parse the results */ while (g_match_info_matches (match_info)) { MMCallInfo *call_info; guint aux; call_info = g_slice_new0 (MMCallInfo); if (!mm_get_uint_from_match_info (match_info, 1, &call_info->index)) { mm_obj_warn (log_object, "couldn't parse call index from ^SLCC line"); goto next; } if (!mm_get_uint_from_match_info (match_info, 2, &aux) || (aux >= G_N_ELEMENTS (cinterion_call_direction))) { mm_obj_warn (log_object, "couldn't parse call direction from ^SLCC line"); goto next; } call_info->direction = cinterion_call_direction[aux]; if (!mm_get_uint_from_match_info (match_info, 3, &aux) || (aux >= G_N_ELEMENTS (cinterion_call_state))) { mm_obj_warn (log_object, "couldn't parse call state from ^SLCC line"); goto next; } call_info->state = cinterion_call_state[aux]; if (g_match_info_get_match_count (match_info) >= 8) call_info->number = mm_get_string_unquoted_from_match_info (match_info, 7); list = g_list_append (list, call_info); call_info = NULL; next: cinterion_call_info_free (call_info); g_match_info_next (match_info, NULL); } out: if (inner_error) { mm_cinterion_call_info_list_free (list); g_propagate_error (error, inner_error); return FALSE; } *out_list = list; return TRUE; } void mm_cinterion_call_info_list_free (GList *call_info_list) { g_list_free_full (call_info_list, (GDestroyNotify) cinterion_call_info_free); } /*****************************************************************************/ /* +CTZU URC helpers */ GRegex * mm_cinterion_get_ctzu_regex (void) { /* * From PLS-8 AT command spec: * +CTZU:, [, ] * E.g.: * +CTZU: "19/07/09,10:19:15",+08,1 */ return g_regex_new ("\\r\\n\\+CTZU:\\s*\"(\\d+)\\/(\\d+)\\/(\\d+),(\\d+):(\\d+):(\\d+)\",([\\-\\+\\d]+)(?:,(\\d+))?(?:\\r\\n)?", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); } gboolean mm_cinterion_parse_ctzu_urc (GMatchInfo *match_info, gchar **iso8601p, MMNetworkTimezone **tzp, GError **error) { gboolean ret = TRUE; guint year = 0, month = 0, day = 0, hour = 0, minute = 0, second = 0, dst = 0; gint tz = 0; if (!mm_get_uint_from_match_info (match_info, 1, &year) || !mm_get_uint_from_match_info (match_info, 2, &month) || !mm_get_uint_from_match_info (match_info, 3, &day) || !mm_get_uint_from_match_info (match_info, 4, &hour) || !mm_get_uint_from_match_info (match_info, 5, &minute) || !mm_get_uint_from_match_info (match_info, 6, &second) || !mm_get_int_from_match_info (match_info, 7, &tz)) { g_set_error_literal (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Failed to parse +CTZU URC"); return FALSE; } /* adjust year */ if (year < 100) year += 2000; /* * tz = timezone offset in 15 minute intervals */ if (iso8601p) { /* Return ISO-8601 format date/time string */ *iso8601p = mm_new_iso8601_time (year, month, day, hour, minute, second, TRUE, tz * 15, error); ret = (*iso8601p != NULL); } if (tzp) { *tzp = mm_network_timezone_new (); mm_network_timezone_set_offset (*tzp, tz * 15); } /* dst flag is optional in the URC * * tz = timezone offset in 15 minute intervals * dst = daylight adjustment, 0 = none, 1 = 1 hour, 2 = 2 hours */ if (tzp && mm_get_uint_from_match_info (match_info, 8, &dst)) mm_network_timezone_set_dst_offset (*tzp, dst * 60); return ret; } /*****************************************************************************/ /* ^SMONI response parser */ gboolean mm_cinterion_parse_smoni_query_response (const gchar *response, MMCinterionRadioGen *out_tech, gdouble *out_rssi, gdouble *out_ecn0, gdouble *out_rscp, gdouble *out_rsrp, gdouble *out_rsrq, GError **error) { g_autoptr(GRegex) r = NULL; g_autoptr(GRegex) pre = NULL; g_autoptr(GMatchInfo) match_info = NULL; g_autoptr(GMatchInfo) match_info_pre = NULL; GError *inner_error = NULL; MMCinterionRadioGen tech = MM_CINTERION_RADIO_GEN_NONE; gdouble rssi = -G_MAXDOUBLE; gdouble ecn0 = -G_MAXDOUBLE; gdouble rscp = -G_MAXDOUBLE; gdouble rsrq = -G_MAXDOUBLE; gdouble rsrp = -G_MAXDOUBLE; gboolean success = FALSE; g_assert (out_tech); g_assert (out_rssi); g_assert (out_ecn0); g_assert (out_rscp); g_assert (out_rsrp); g_assert (out_rsrq); g_assert (out_rssi); /* Possible Responses: * 2G * ^SMONI: 2G,ARFCN,BCCH,MCC,MNC,LAC,cell,C1,C2,NCC,BCC,GPRS,Conn_state // registered * ^SMONI: 2G,ARFCN,BCCH,MCC,MNC,LAC,cell,C1,C2,NCC,BCC,GPRS,ARFCN,TS,timAdv,dBm,Q,ChMod // searching * ^SMONI: 2G,ARFCN,BCCH,MCC,MNC,LAC,cell,C1,C2,NCC,BCC,GPRS,PWR,RXLev,ARFCN,TS,timAdv,dBm,Q,ChMod // limsrv * ^SMONI: 2G,ARFCN,BCCH,MCC,MNC,LAC,cell,C1,C2,NCC,BCC,GPRS,ARFCN,TS,timAdv,dBm,Q,ChMod // dedicated channel * * ^SMONI: 2G,71,-61,262,02,0143,83BA,33,33,3,6,G,NOCONN * ^^^ * ^SMONI: 2G,SEARCH,SEARCH * ^SMONI: 2G,673,-89,262,07,4EED,A500,16,16,7,4,G,5,-107,LIMSRV * ^^^ ^^^^ RXLev dBm * ^SMONI: 2G,673,-80,262,07,4EED,A500,35,35,7,4,G,643,4,0,-80,0,S_FR * ^^^ ^^^ dBm: Receiving level of the traffic channel carrier in dBm * BCCH: Receiving level of the BCCH carrier in dBm (level is limited from -110dBm to -47dBm) * -> rssi for 2G, directly without mm_3gpp_rxlev_to_rssi * * * 3G * ^SMONI: 3G,UARFCN,PSC,EC/n0,RSCP,MCC,MNC,LAC,cell,SQual,SRxLev,,Conn_state", * ^SMONI: 3G,UARFCN,PSC,EC/n0,RSCP,MCC,MNC,LAC,cell,SQual,SRxLev,PhysCh, SF,Slot,EC/n0,RSCP,ComMod,HSUPA,HSDPA", * ^SMONI: 3G,UARFCN,PSC,EC/n0,RSCP,MCC,MNC,LAC,cell,SQual,SRxLev,PhysCh, SF,Slot,EC/n0,RSCP,ComMod,HSUPA,HSDPA", * ^SMONI: 3G,UARFCN,PSC,EC/n0,RSCP,MCC,MNC,LAC,cell,SQual,SRxLev,PhysCh, SF,Slot,EC/n0,RSCP,ComMod,HSUPA,HSDPA", * * ^SMONI: 3G,10564,296,-7.5,-79,262,02,0143,00228FF,-92,-78,NOCONN * ^^^^ ^^^ * ^SMONI: 3G,SEARCH,SEARCH * ^SMONI: 3G,10564,96,-7.5,-79,262,02,0143,00228FF,-92,-78,LIMSRV * ^^^^ ^^^ * ^SMONI: 3G,10737,131,-5,-93,260,01,7D3D,C80BC9A,--,--,----,---,-,-5,-93,0,01,06 * ^^ ^^^ * RSCP: Received Signal Code Power in dBm -> no need for mm_3gpp_rscp_level_to_rscp * EC/n0: EC/n0 Carrier to noise ratio in dB = measured Ec/Io value in dB. Please refer to 3GPP 25.133, section 9.1.2.3, Table 9.9 for details on the mapping from EC/n0 to EC/Io. * -> direct value, without need for mm_3gpp_ecn0_level_to_ecio * * * 4G * ^SMONI: 4G,EARFCN,Band,DL bandwidth,UL bandwidth,Mode,MCC,MNC,TAC,Global Cell ID,Physical Cell ID,Srxlev,RSRP,RSRQ,Conn_state * ^SMONI: 4G,EARFCN,Band,DL bandwidth,UL bandwidth,Mode,MCC,MNC,TAC,Global Cell ID,Physical Cell ID,Srxlev,RSRP,RSRQ,Conn_state * ^SMONI: 4G,EARFCN,Band,DL bandwidth,UL bandwidth,Mode,MCC,MNC,TAC,Global Cell ID,Physical Cell ID,Srxlev,RSRP,RSRQ,Conn_state * ^SMONI: 4G,EARFCN,Band,DL bandwidth,UL bandwidth,Mode,MCC,MNC,TAC,Global Cell ID,Physical Cell ID,TX_power,RSRP,RSRQ,Conn_state * * ^SMONI: 4G,6300,20,10,10,FDD,262,02,BF75,0345103,350,33,-94,-7,NOCONN * ^^^ ^^ * ^SMONI: 4G,SEARCH * ^SMONI: 4G,6300,20,10,10,FDD,262,02,BF75,0345103,350,33,-94,-7,LIMSRV * ^^^ ^^ * ^SMONI: 4G,6300,20,10,10,FDD,262,02,BF75,0345103,350,90,-94,-7,CONN * ^^^ ^^ * RSRP Reference Signal Received Power (see 3GPP 36.214 Section 5.1.1.) -> directly the value without mm_3gpp_rsrq_level_to_rsrp * RSRQ Reference Signal Received Quality (see 3GPP 36.214 Section 5.1.2.) -> directly the value without mm_3gpp_rsrq_level_to_rsrq */ if (g_regex_match_simple ("\\^SMONI:\\s*[234]G,SEARCH", response, 0, 0)) { success = TRUE; goto out; } pre = g_regex_new ("\\^SMONI:\\s*([234])", 0, 0, NULL); g_assert (pre != NULL); g_regex_match_full (pre, response, strlen (response), 0, 0, &match_info_pre, &inner_error); if (!inner_error && g_match_info_matches (match_info_pre)) { if (!mm_get_uint_from_match_info (match_info_pre, 1, &tech)) { inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't read tech"); goto out; } #define FLOAT "([-+]?[0-9]+\\.?[0-9]*)" switch (tech) { case MM_CINTERION_RADIO_GEN_2G: r = g_regex_new ("\\^SMONI:\\s*2G,(\\d+),"FLOAT, 0, 0, NULL); g_assert (r != NULL); g_regex_match_full (r, response, strlen (response), 0, 0, &match_info, &inner_error); if (!inner_error && g_match_info_matches (match_info)) { /* skip ARFCN */ if (!mm_get_double_from_match_info (match_info, 2, &rssi)) { inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't read BCCH=rssi"); goto out; } } break; case MM_CINTERION_RADIO_GEN_3G: r = g_regex_new ("\\^SMONI:\\s*3G,(\\d+),(\\d+),"FLOAT","FLOAT, 0, 0, NULL); g_assert (r != NULL); g_regex_match_full (r, response, strlen (response), 0, 0, &match_info, &inner_error); if (!inner_error && g_match_info_matches (match_info)) { /* skip UARFCN */ /* skip PSC (Primary scrambling code) */ if (!mm_get_double_from_match_info (match_info, 3, &ecn0)) { inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't read EcN0"); goto out; } if (!mm_get_double_from_match_info (match_info, 4, &rscp)) { inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't read RSCP"); goto out; } } break; case MM_CINTERION_RADIO_GEN_4G: r = g_regex_new ("\\^SMONI:\\s*4G,(\\d+),(\\d+),(\\d+),(\\d+),(\\w+),(\\d+),(\\d+),(\\w+),(\\w+),(\\d+),([^,]*),"FLOAT","FLOAT, 0, 0, NULL); g_assert (r != NULL); g_regex_match_full (r, response, strlen (response), 0, 0, &match_info, &inner_error); if (!inner_error && g_match_info_matches (match_info)) { /* skip EARFCN */ /* skip Band */ /* skip DL bandwidth */ /* skip UL bandwidth */ /* skip Mode */ /* skip MCC */ /* skip MNC */ /* skip TAC */ /* skip Global Cell ID */ /* skip Physical Cell ID */ /* skip Srxlev/TX_power */ if (!mm_get_double_from_match_info (match_info, 12, &rsrp)) { inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't read RSRQ"); goto out; } if (!mm_get_double_from_match_info (match_info, 13, &rsrq)) { inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't read RSRP"); goto out; } } break; case MM_CINTERION_RADIO_GEN_NONE: default: goto out; } #undef FLOAT success = TRUE; } out: if (inner_error) { g_propagate_error (error, inner_error); return FALSE; } if (!success) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't parse ^SMONI response: %s", response); return FALSE; } *out_tech = tech; *out_rssi = rssi; *out_rscp = rscp; *out_ecn0 = ecn0; *out_rsrq = rsrq; *out_rsrp = rsrp; return TRUE; } /*****************************************************************************/ /* Get extended signal information */ gboolean mm_cinterion_smoni_response_to_signal_info (const gchar *response, MMSignal **out_gsm, MMSignal **out_umts, MMSignal **out_lte, GError **error) { MMCinterionRadioGen tech = MM_CINTERION_RADIO_GEN_NONE; gdouble rssi = MM_SIGNAL_UNKNOWN; gdouble ecn0 = MM_SIGNAL_UNKNOWN; gdouble rscp = MM_SIGNAL_UNKNOWN; gdouble rsrq = MM_SIGNAL_UNKNOWN; gdouble rsrp = MM_SIGNAL_UNKNOWN; MMSignal *gsm = NULL; MMSignal *umts = NULL; MMSignal *lte = NULL; if (!mm_cinterion_parse_smoni_query_response (response, &tech, &rssi, &ecn0, &rscp, &rsrp, &rsrq, error)) return FALSE; switch (tech) { case MM_CINTERION_RADIO_GEN_2G: gsm = mm_signal_new (); mm_signal_set_rssi (gsm, rssi); break; case MM_CINTERION_RADIO_GEN_3G: umts = mm_signal_new (); mm_signal_set_rscp (umts, rscp); mm_signal_set_ecio (umts, ecn0); /* UMTS EcIo (assumed EcN0) */ break; case MM_CINTERION_RADIO_GEN_4G: lte = mm_signal_new (); mm_signal_set_rsrp (lte, rsrp); mm_signal_set_rsrq (lte, rsrq); break; case MM_CINTERION_RADIO_GEN_NONE: /* not registered, searching */ break; /* no error case */ default: /* should not happen, so if it does, error */ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't build detailed signal info"); return FALSE; } if (out_gsm) *out_gsm = gsm; if (out_umts) *out_umts = umts; if (out_lte) *out_lte = lte; return TRUE; } /*****************************************************************************/ /* provider cfg information to CID number for EPS initial settings */ /* * at^scfg="MEopMode/Prov/Cfg" * ^SCFG: "MEopMode/Prov/Cfg","vdfde" * ^SCFG: "MEopMode/Prov/Cfg","attus" * ^SCFG: "MEopMode/Prov/Cfg","2" -> PLS8-X vzw * ^SCFG: "MEopMode/Prov/Cfg","vzwdcus" -> PLAS9-x vzw * ^SCFG: "MEopMode/Prov/Cfg","tmode" -> t-mob germany * OK */ gboolean mm_cinterion_provcfg_response_to_cid (const gchar *response, MMCinterionModemFamily modem_family, MMModemCharset charset, gpointer log_object, gint *cid, GError **error) { g_autoptr(GRegex) r = NULL; g_autoptr(GMatchInfo) match_info = NULL; g_autofree gchar *mno = NULL; GError *inner_error = NULL; r = g_regex_new ("\\^SCFG:\\s*\"MEopMode/Prov/Cfg\",\\s*\"([0-9a-zA-Z*]*)\"", 0, 0, NULL); g_assert (r != NULL); g_regex_match_full (r, response, strlen (response), 0, 0, &match_info, &inner_error); if (inner_error) { g_prefix_error (&inner_error, "Failed to match Prov/Cfg response: "); g_propagate_error (error, inner_error); return FALSE; } if (!g_match_info_matches (match_info)) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't match Prov/Cfg response"); return FALSE; } mno = mm_get_string_unquoted_from_match_info (match_info, 1); if (mno && modem_family == MM_CINTERION_MODEM_FAMILY_IMT) { gchar *mno_utf8; mno_utf8 = mm_modem_charset_str_to_utf8 (mno, -1, charset, FALSE, error); if (!mno_utf8) return FALSE; g_free (mno); mno = mno_utf8; } mm_obj_dbg (log_object, "current mno: %s", mno ? mno : "none"); /* for Cinterion LTE modules, some CID numbers have special meaning. * This is dictated by the chipset and by the MNO: * - the chipset uses a special one, CID 1, as a LTE combined attach chipset * - the MNOs can define the sequence and number of APN to be used for their network. * This takes priority over the chipset preferences, and therefore for some of them * the CID for the initial EPS context must be changed. */ if (g_strcmp0 (mno, "2") == 0 || g_strcmp0 (mno, "vzwdcus") == 0) *cid = 3; else if (g_strcmp0 (mno, "tmode") == 0) *cid = 2; else *cid = 1; return TRUE; } /*****************************************************************************/ /* Auth related helpers */ typedef enum { BEARER_CINTERION_AUTH_UNKNOWN = -1, BEARER_CINTERION_AUTH_NONE = 0, BEARER_CINTERION_AUTH_PAP = 1, BEARER_CINTERION_AUTH_CHAP = 2, BEARER_CINTERION_AUTH_MSCHAPV2 = 3, } BearerCinterionAuthType; static BearerCinterionAuthType parse_auth_type (MMBearerAllowedAuth mm_auth) { switch (mm_auth) { case MM_BEARER_ALLOWED_AUTH_NONE: return BEARER_CINTERION_AUTH_NONE; case MM_BEARER_ALLOWED_AUTH_PAP: return BEARER_CINTERION_AUTH_PAP; case MM_BEARER_ALLOWED_AUTH_CHAP: return BEARER_CINTERION_AUTH_CHAP; case MM_BEARER_ALLOWED_AUTH_MSCHAPV2: return BEARER_CINTERION_AUTH_MSCHAPV2; case MM_BEARER_ALLOWED_AUTH_UNKNOWN: case MM_BEARER_ALLOWED_AUTH_MSCHAP: case MM_BEARER_ALLOWED_AUTH_EAP: default: return BEARER_CINTERION_AUTH_UNKNOWN; } } MMBearerAllowedAuth mm_auth_type_from_cinterion_auth_type (guint cinterion_auth) { switch (cinterion_auth) { case BEARER_CINTERION_AUTH_NONE: return MM_BEARER_ALLOWED_AUTH_NONE; case BEARER_CINTERION_AUTH_PAP: return MM_BEARER_ALLOWED_AUTH_PAP; case BEARER_CINTERION_AUTH_CHAP: return MM_BEARER_ALLOWED_AUTH_CHAP; default: return MM_BEARER_ALLOWED_AUTH_UNKNOWN; } } /* Cinterion authentication is done with the command AT^SGAUTH, whose syntax depends on the modem family, as follow: - AT^SGAUTH=[, [, , ]] for the IMT family - AT^SGAUTH=[, [, , ]] for the rest */ gchar * mm_cinterion_build_auth_string (gpointer log_object, MMCinterionModemFamily modem_family, MMBearerProperties *config, guint cid) { MMBearerAllowedAuth auth; BearerCinterionAuthType encoded_auth = BEARER_CINTERION_AUTH_UNKNOWN; gboolean has_user; gboolean has_passwd; const gchar *user; const gchar *passwd; g_autofree gchar *quoted_user = NULL; g_autofree gchar *quoted_passwd = NULL; user = mm_bearer_properties_get_user (config); passwd = mm_bearer_properties_get_password (config); auth = mm_bearer_properties_get_allowed_auth (config); has_user = (user && user[0]); has_passwd = (passwd && passwd[0]); encoded_auth = parse_auth_type (auth); /* No explicit auth type requested? */ if (encoded_auth == BEARER_CINTERION_AUTH_UNKNOWN) { if (!has_user && !has_passwd) { /* If no user/passwd given, default to 'none' */ mm_obj_dbg (log_object, "APN user/password and authentication type not given: defaulting to 'none'"); encoded_auth = BEARER_CINTERION_AUTH_NONE; } else { /* If user/passwd given, default to CHAP (more common than PAP) */ mm_obj_dbg (log_object, "APN user/password given but no authentication type explicitly requested: defaulting to 'CHAP'"); encoded_auth = BEARER_CINTERION_AUTH_CHAP; } } /* When 'none' requested, we won't require user/password */ if (encoded_auth == BEARER_CINTERION_AUTH_NONE) { if (has_user || has_passwd) mm_obj_warn (log_object, "APN user/password given but 'none' authentication requested"); if (modem_family == MM_CINTERION_MODEM_FAMILY_IMT) return g_strdup_printf ("^SGAUTH=%u,%d,\"\",\"\"", cid, encoded_auth); return g_strdup_printf ("^SGAUTH=%u,%d", cid, encoded_auth); } quoted_user = mm_port_serial_at_quote_string (user ? user : ""); quoted_passwd = mm_port_serial_at_quote_string (passwd ? passwd : ""); if (modem_family == MM_CINTERION_MODEM_FAMILY_IMT) return g_strdup_printf ("^SGAUTH=%u,%d,%s,%s", cid, encoded_auth, quoted_user, quoted_passwd); return g_strdup_printf ("^SGAUTH=%u,%d,%s,%s", cid, encoded_auth, quoted_passwd, quoted_user); } /*****************************************************************************/ /* ^SXRAT set command builder */ /* Index of the array is the centerion-specific sxrat value */ static const MMModemMode sxrat_combinations[] = { [0] = ( MM_MODEM_MODE_2G ), [1] = ( MM_MODEM_MODE_2G | MM_MODEM_MODE_3G ), [2] = ( MM_MODEM_MODE_3G ), [3] = ( MM_MODEM_MODE_4G ), [4] = ( MM_MODEM_MODE_3G | MM_MODEM_MODE_4G ), [5] = ( MM_MODEM_MODE_2G | MM_MODEM_MODE_4G ), [6] = ( MM_MODEM_MODE_2G | MM_MODEM_MODE_3G | MM_MODEM_MODE_4G ), }; static gboolean append_sxrat_rat_value (GString *str, MMModemMode mode, GError **error) { guint i; for (i = 0; i < G_N_ELEMENTS (sxrat_combinations); i++) { if (sxrat_combinations[i] == mode) { g_string_append_printf (str, "%u", i); return TRUE; } } g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "No AcT value matches requested mode"); return FALSE; } gchar * mm_cinterion_build_sxrat_set_command (MMModemMode allowed, MMModemMode preferred, GError **error) { GString *command; command = g_string_new ("^SXRAT="); if (!append_sxrat_rat_value (command, allowed, error)) { g_string_free (command, TRUE); return NULL; } if (preferred != MM_MODEM_MODE_NONE) { if (mm_count_bits_set (preferred) != 1) { *error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "AcT preferred value should be a single AcT"); g_string_free (command, TRUE); return NULL; } g_string_append (command, ","); if (!append_sxrat_rat_value (command, preferred, error)) { g_string_free (command, TRUE); return NULL; } } return g_string_free (command, FALSE); } ModemManager-1.23.4-dev/src/plugins/cinterion/mm-modem-helpers-cinterion.h000066400000000000000000000255521456466623000265110ustar00rootroot00000000000000/* -*- 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) 2014 Aleksander Morgado * Copyright (C) 2016 Trimble Navigation Limited * Copyright (C) 2016 Matthew Stanger * Copyright (C) 2019 Purism SPC */ #ifndef MM_MODEM_HELPERS_CINTERION_H #define MM_MODEM_HELPERS_CINTERION_H #include #include #include #define _LIBMM_INSIDE_MM #include typedef enum { MM_CINTERION_MODEM_FAMILY_DEFAULT = 0, MM_CINTERION_MODEM_FAMILY_IMT = 1, } MMCinterionModemFamily; typedef enum { MM_CINTERION_RADIO_BAND_FORMAT_SINGLE = 0, MM_CINTERION_RADIO_BAND_FORMAT_MULTIPLE = 1, } MMCinterionRadioBandFormat; typedef enum { MM_CINTERION_RB_BLOCK_LEGACY = 0, MM_CINTERION_RB_BLOCK_GSM = 0, MM_CINTERION_RB_BLOCK_UMTS = 1, MM_CINTERION_RB_BLOCK_LTE_LOW = 2, MM_CINTERION_RB_BLOCK_LTE_HIGH = 3, MM_CINTERION_RB_BLOCK_N = 4 } MMCinterionRbBlock; typedef enum { MM_CINTERION_RADIO_GEN_NONE = 0, MM_CINTERION_RADIO_GEN_2G = 2, MM_CINTERION_RADIO_GEN_3G = 3, MM_CINTERION_RADIO_GEN_4G = 4, } MMCinterionRadioGen; /*****************************************************************************/ /* ^SCFG test parser */ gboolean mm_cinterion_parse_scfg_test (const gchar *response, MMCinterionModemFamily modem_family, MMModemCharset charset, GArray **supported_bands, MMCinterionRadioBandFormat *format, GError **error); /*****************************************************************************/ /* ^SCFG response parser */ gboolean mm_cinterion_parse_scfg_response (const gchar *response, MMCinterionModemFamily modem_family, MMModemCharset charset, GArray **bands, MMCinterionRadioBandFormat format, GError **error); /*****************************************************************************/ /* ^SCFG response sim parser */ gboolean mm_cinterion_parse_scfg_sim_response (const gchar *response, guint *sim_count, GError **error); /*****************************************************************************/ /* ^SIND simlocal helper */ gboolean mm_cinterion_get_available_from_simlocal (const gchar *response, GArray **available, GError **error); /*****************************************************************************/ /* ^SIND response simlocal parser */ gboolean mm_cinterion_parse_sind_simlocal_response (const gchar *response, GArray **available, GError **error); /*****************************************************************************/ /* +CNMI test parser */ gboolean mm_cinterion_parse_cnmi_test (const gchar *response, GArray **supported_mode, GArray **supported_mt, GArray **supported_bm, GArray **supported_ds, GArray **supported_bfr, GError **error); /*****************************************************************************/ /* ^SXRAT test parser */ gboolean mm_cinterion_parse_sxrat_test (const gchar *response, GArray **supported_rat, GArray **supported_pref1, GArray **supported_pref2, GError **error); /*****************************************************************************/ /* Build Cinterion-specific band value */ gboolean mm_cinterion_build_band (GArray *bands, guint *supported, gboolean only_2g, MMCinterionRadioBandFormat format, MMCinterionModemFamily modem_family, guint *out_band, GError **error); /*****************************************************************************/ /* Single ^SIND response parser */ gboolean mm_cinterion_parse_sind_response (const gchar *response, gchar **description, guint *mode, guint *value, GError **error); /*****************************************************************************/ /* ^SWWAN response parser */ MMBearerConnectionStatus mm_cinterion_parse_swwan_response (const gchar *response, guint swwan_index, gpointer log_object, GError **error); /*****************************************************************************/ /* ^SGAUTH response parser */ gboolean mm_cinterion_parse_sgauth_response (const gchar *response, guint cid, MMBearerAllowedAuth *out_auth, gchar **out_username, GError **error); /*****************************************************************************/ /* ^SMONG response parser */ gboolean mm_cinterion_parse_smong_response (const gchar *response, MMModemAccessTechnology *access_tech, GError **error); /*****************************************************************************/ /* ^SIND psinfo helper */ MMModemAccessTechnology mm_cinterion_get_access_technology_from_sind_psinfo (guint val, gpointer log_object); /*****************************************************************************/ /* ^SLCC URC helpers */ GRegex *mm_cinterion_get_slcc_regex (void); /* MMCallInfo list management */ gboolean mm_cinterion_parse_slcc_list (const gchar *str, gpointer log_object, GList **out_list, GError **error); void mm_cinterion_call_info_list_free (GList *call_info_list); /*****************************************************************************/ /* +CTZU URC helpers */ GRegex *mm_cinterion_get_ctzu_regex (void); gboolean mm_cinterion_parse_ctzu_urc (GMatchInfo *match_info, gchar **iso8601p, MMNetworkTimezone **tzp, GError **error); /*****************************************************************************/ /* ^SMONI helper */ gboolean mm_cinterion_parse_smoni_query_response (const gchar *response, MMCinterionRadioGen *out_tech, gdouble *out_rssi, gdouble *out_ecn0, gdouble *out_rscp, gdouble *out_rsrp, gdouble *out_rsrq, GError **error); gboolean mm_cinterion_smoni_response_to_signal_info (const gchar *response, MMSignal **out_gsm, MMSignal **out_umts, MMSignal **out_lte, GError **error); /*****************************************************************************/ /* ^SCFG="MEopMode/Prov/Cfg" helper */ gboolean mm_cinterion_provcfg_response_to_cid (const gchar *response, MMCinterionModemFamily modem_family, MMModemCharset charset, gpointer log_object, gint *cid, GError **error); /*****************************************************************************/ /* Auth related helpers */ MMBearerAllowedAuth mm_auth_type_from_cinterion_auth_type (guint cinterion_auth); gchar *mm_cinterion_build_auth_string (gpointer log_object, MMCinterionModemFamily modem_family, MMBearerProperties *config, guint cid); /*****************************************************************************/ /* ^SXRAT set command helper */ gchar *mm_cinterion_build_sxrat_set_command (MMModemMode allowed, MMModemMode preferred, GError **error); #endif /* MM_MODEM_HELPERS_CINTERION_H */ ModemManager-1.23.4-dev/src/plugins/cinterion/mm-plugin-cinterion.c000066400000000000000000000215621456466623000252360ustar00rootroot00000000000000/* -*- 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. * * 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. * * Copyright (C) 2011 Ammonit Measurement GmbH * Copyright (C) 2011 - 2012 Google Inc. * Author: Aleksander Morgado */ #include #include #define _LIBMM_INSIDE_MM #include #include #include "mm-plugin-common.h" #include "mm-broadband-modem-cinterion.h" #include "mm-log-object.h" #if defined WITH_QMI #include "mm-broadband-modem-qmi-cinterion.h" #endif #if defined WITH_MBIM #include "mm-broadband-modem-mbim-cinterion.h" #endif #define MM_TYPE_PLUGIN_CINTERION mm_plugin_cinterion_get_type () MM_DEFINE_PLUGIN (CINTERION, cinterion, Cinterion) /*****************************************************************************/ /* Custom init */ /* helps shorten a long if */ #define CHECK_PORT_HAS_TAG(p, t) ( \ mm_kernel_device_get_property_as_boolean (mm_port_probe_peek_port (p), t) ) static gboolean cinterion_custom_init_finish (MMPortProbe *probe, GAsyncResult *result, GError **error) { return g_task_propagate_boolean (G_TASK (result), error); } /* is_port_already_tagged checks whether a port provided by probe has * already tags assigned. */ static gboolean is_port_already_tagged (MMPortProbe *probe) { if (CHECK_PORT_HAS_TAG (probe, ID_MM_PORT_TYPE_AT_PRIMARY) || CHECK_PORT_HAS_TAG (probe, ID_MM_PORT_TYPE_AT_SECONDARY) || CHECK_PORT_HAS_TAG (probe, ID_MM_PORT_TYPE_AT_PPP) || CHECK_PORT_HAS_TAG (probe, ID_MM_PORT_TYPE_GPS) ) { return TRUE; } return FALSE; } static void sqport_ready (MMPortSerialAt *port, GAsyncResult *res, GTask *task) { MMPortProbe *probe; g_autofree gchar *response = NULL; probe = g_task_get_source_object (task); /* Ignore errors, just avoid tagging */ response = mm_port_serial_at_command_finish (port, res, NULL); if (response) { /* A valid reply to AT^SQPORT tells us this is an AT port already */ mm_port_probe_set_result_at (probe, TRUE); if (strstr (response, "Application")) g_object_set_data (G_OBJECT (probe), ID_MM_PORT_TYPE_AT_PRIMARY, GUINT_TO_POINTER (TRUE)); else if (strstr (response, "Modem")) g_object_set_data (G_OBJECT (probe), ID_MM_PORT_TYPE_AT_PPP, GUINT_TO_POINTER (TRUE)); } g_task_return_boolean (task, TRUE); g_object_unref (task); } static void cinterion_custom_init (MMPortProbe *probe, MMPortSerialAt *port, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; task = g_task_new (probe, cancellable, callback, user_data); /* if the port is already tagged then it means that, most likely, udev * has sorted things out. in that case it is not needed to run SQPORT? * to try to guess whether this is AT-capable port. lets skip it. */ if (is_port_already_tagged (probe)) { g_task_return_boolean (task, TRUE); g_object_unref (task); return; } /* alright, no tags on this port whatsoever so lets figure things out * using SQPORT? */ mm_port_serial_at_command ( port, "AT^SQPORT?", 3, FALSE, /* raw */ FALSE, /* allow cached */ cancellable, (GAsyncReadyCallback) sqport_ready, task); } /*****************************************************************************/ static MMBaseModem * create_modem (MMPlugin *self, const gchar *uid, const gchar *physdev, const gchar **drivers, guint16 vendor, guint16 product, guint16 subsystem_vendor, GList *probes, GError **error) { #if defined WITH_QMI if (mm_port_probe_list_has_qmi_port (probes)) { mm_obj_dbg (self, "QMI-powered Cinterion modem found..."); return MM_BASE_MODEM (mm_broadband_modem_qmi_cinterion_new (uid, physdev, drivers, mm_plugin_get_name (self), vendor, product)); } #endif #if defined WITH_MBIM if (mm_port_probe_list_has_mbim_port (probes)) { mm_obj_dbg (self, "MBIM-powered Cinterion modem found..."); return MM_BASE_MODEM (mm_broadband_modem_mbim_cinterion_new (uid, physdev, drivers, mm_plugin_get_name (self), vendor, product)); } #endif return MM_BASE_MODEM (mm_broadband_modem_cinterion_new (uid, physdev, drivers, mm_plugin_get_name (self), vendor, product)); } static gboolean grab_port (MMPlugin *self, MMBaseModem *modem, MMPortProbe *probe, GError **error) { MMPortSerialAtFlag pflags = MM_PORT_SERIAL_AT_FLAG_NONE; MMPortType ptype; ptype = mm_port_probe_get_port_type (probe); if (g_object_get_data (G_OBJECT (probe), ID_MM_PORT_TYPE_AT_PRIMARY)) { mm_obj_dbg (self, "(%s/%s)' Port flagged as primary", mm_port_probe_get_port_subsys (probe), mm_port_probe_get_port_name (probe)); pflags = MM_PORT_SERIAL_AT_FLAG_PRIMARY; } else if (g_object_get_data (G_OBJECT (probe), ID_MM_PORT_TYPE_AT_PPP)) { mm_obj_dbg (self, "(%s/%s)' Port flagged as PPP", mm_port_probe_get_port_subsys (probe), mm_port_probe_get_port_name (probe)); pflags = MM_PORT_SERIAL_AT_FLAG_PPP; } /* else unknown or set via generic udev tags */ return mm_base_modem_grab_port (modem, mm_port_probe_peek_port (probe), ptype, pflags, error); } /*****************************************************************************/ MM_PLUGIN_NAMED_CREATOR_SCOPE MMPlugin * mm_plugin_create_cinterion (void) { static const gchar *subsystems[] = { "tty", "net", "usbmisc", "wwan", NULL }; static const gchar *vendor_strings[] = { "cinterion", "siemens", NULL }; static const guint16 vendor_ids[] = { 0x1e2d, 0x0681, 0x1269, 0 }; static const MMAsyncMethod custom_init = { .async = G_CALLBACK (cinterion_custom_init), .finish = G_CALLBACK (cinterion_custom_init_finish), }; return MM_PLUGIN ( g_object_new (MM_TYPE_PLUGIN_CINTERION, MM_PLUGIN_NAME, MM_MODULE_NAME, MM_PLUGIN_ALLOWED_SUBSYSTEMS, subsystems, MM_PLUGIN_ALLOWED_VENDOR_STRINGS, vendor_strings, MM_PLUGIN_ALLOWED_VENDOR_IDS, vendor_ids, MM_PLUGIN_ALLOWED_AT, TRUE, MM_PLUGIN_ALLOWED_QMI, TRUE, MM_PLUGIN_ALLOWED_MBIM, TRUE, MM_PLUGIN_CUSTOM_INIT, &custom_init, NULL)); } static void mm_plugin_cinterion_init (MMPluginCinterion *self) { } static void mm_plugin_cinterion_class_init (MMPluginCinterionClass *klass) { MMPluginClass *plugin_class = MM_PLUGIN_CLASS (klass); plugin_class->create_modem = create_modem; plugin_class->grab_port = grab_port; } ModemManager-1.23.4-dev/src/plugins/cinterion/mm-shared-cinterion.c000066400000000000000000001640661456466623000252150ustar00rootroot00000000000000/* -*- 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) 2014 Ammonit Measurement GmbH * Copyright (C) 2014 - 2018 Aleksander Morgado * Copyright (C) 2019 Purism SPC */ #include #include #include #define _LIBMM_INSIDE_MM #include #include "mm-log-object.h" #include "mm-iface-modem.h" #include "mm-iface-modem-location.h" #include "mm-base-modem.h" #include "mm-base-modem-at.h" #include "mm-shared-cinterion.h" #include "mm-modem-helpers-cinterion.h" /*****************************************************************************/ /* Private data context */ #define PRIVATE_TAG "shared-cinterion-private-tag" static GQuark private_quark; typedef enum { FEATURE_SUPPORT_UNKNOWN, FEATURE_NOT_SUPPORTED, FEATURE_SUPPORTED, } FeatureSupport; typedef struct { /* modem */ MMIfaceModem *iface_modem_parent; /* location */ MMIfaceModemLocation *iface_modem_location_parent; MMModemLocationSource supported_sources; MMModemLocationSource enabled_sources; FeatureSupport sgpss_support; FeatureSupport sgpsc_support; /* voice */ MMIfaceModemVoice *iface_modem_voice_parent; FeatureSupport slcc_support; GRegex *slcc_regex; /* time */ MMIfaceModemTime *iface_modem_time_parent; GRegex *ctzu_regex; } Private; static void private_free (Private *ctx) { g_regex_unref (ctx->ctzu_regex); g_regex_unref (ctx->slcc_regex); g_slice_free (Private, ctx); } static Private * get_private (MMSharedCinterion *self) { Private *priv; if (G_UNLIKELY (!private_quark)) private_quark = (g_quark_from_static_string (PRIVATE_TAG)); priv = g_object_get_qdata (G_OBJECT (self), private_quark); if (!priv) { priv = g_slice_new (Private); priv->supported_sources = MM_MODEM_LOCATION_SOURCE_NONE; priv->enabled_sources = MM_MODEM_LOCATION_SOURCE_NONE; priv->sgpss_support = FEATURE_SUPPORT_UNKNOWN; priv->sgpsc_support = FEATURE_SUPPORT_UNKNOWN; priv->slcc_support = FEATURE_SUPPORT_UNKNOWN; priv->slcc_regex = mm_cinterion_get_slcc_regex (); priv->ctzu_regex = mm_cinterion_get_ctzu_regex (); /* Setup parent class' MMIfaceModem, MMIfaceModemLocation, MMIfaceModemVoice * and MMIfaceModemTime */ g_assert (MM_SHARED_CINTERION_GET_INTERFACE (self)->peek_parent_interface); priv->iface_modem_parent = MM_SHARED_CINTERION_GET_INTERFACE (self)->peek_parent_interface (self); g_assert (MM_SHARED_CINTERION_GET_INTERFACE (self)->peek_parent_location_interface); priv->iface_modem_location_parent = MM_SHARED_CINTERION_GET_INTERFACE (self)->peek_parent_location_interface (self); g_assert (MM_SHARED_CINTERION_GET_INTERFACE (self)->peek_parent_voice_interface); priv->iface_modem_voice_parent = MM_SHARED_CINTERION_GET_INTERFACE (self)->peek_parent_voice_interface (self); g_assert (MM_SHARED_CINTERION_GET_INTERFACE (self)->peek_parent_time_interface); priv->iface_modem_time_parent = MM_SHARED_CINTERION_GET_INTERFACE (self)->peek_parent_time_interface (self); g_object_set_qdata_full (G_OBJECT (self), private_quark, priv, (GDestroyNotify)private_free); } return priv; } /*****************************************************************************/ /* Modem interface */ gboolean mm_shared_cinterion_modem_reset_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void modem_reset_at_ready (MMBaseModem *self, GAsyncResult *res, GTask *task) { GError *error = NULL; if (!mm_base_modem_at_command_finish (self, res, &error)) g_task_return_error (task, error); else g_task_return_boolean (task, TRUE); g_object_unref (task); } static void modem_reset_at (GTask *task) { MMSharedCinterion *self; self = g_task_get_source_object (task); mm_base_modem_at_command (MM_BASE_MODEM (self), "+CFUN=1,1", 3, FALSE, (GAsyncReadyCallback) modem_reset_at_ready, task); } static void parent_modem_reset_ready (MMIfaceModem *self, GAsyncResult *res, GTask *task) { Private *priv; priv = get_private (MM_SHARED_CINTERION (self)); if (!priv->iface_modem_parent->reset_finish (self, res, NULL)) { modem_reset_at (task); return; } g_task_return_boolean (task, TRUE); g_object_unref (task); } void mm_shared_cinterion_modem_reset (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; Private *priv; priv = get_private (MM_SHARED_CINTERION (self)); task = g_task_new (self, NULL, callback, user_data); if (priv->iface_modem_parent->reset && priv->iface_modem_parent->reset_finish) { priv->iface_modem_parent->reset (self, (GAsyncReadyCallback) parent_modem_reset_ready, task); return; } modem_reset_at (task); } /*****************************************************************************/ /* GPS trace received */ static void trace_received (MMPortSerialGps *port, const gchar *trace, MMIfaceModemLocation *self) { mm_iface_modem_location_gps_update (self, trace); } /*****************************************************************************/ /* Location capabilities loading (Location interface) */ MMModemLocationSource mm_shared_cinterion_location_load_capabilities_finish (MMIfaceModemLocation *self, GAsyncResult *res, GError **error) { GError *inner_error = NULL; gssize aux; aux = g_task_propagate_int (G_TASK (res), &inner_error); if (inner_error) { g_propagate_error (error, inner_error); return MM_MODEM_LOCATION_SOURCE_NONE; } return (MMModemLocationSource) aux; } static void probe_gps_features (GTask *task); static void sgpsc_test_ready (MMBaseModem *self, GAsyncResult *res, GTask *task) { Private *priv; priv = get_private (MM_SHARED_CINTERION (self)); if (!mm_base_modem_at_command_finish (self, res, NULL)) priv->sgpsc_support = FEATURE_NOT_SUPPORTED; else { /* ^SGPSC supported! */ priv->sgpsc_support = FEATURE_SUPPORTED; /* It may happen that the modem was started with GPS already enabled, or * maybe ModemManager got rebooted and it was left enabled before. We'll * make sure that it is disabled when we initialize the modem. */ mm_base_modem_at_command (MM_BASE_MODEM (self), "AT^SGPSC=\"Engine\",\"0\"", 3, FALSE, NULL, NULL); mm_base_modem_at_command (MM_BASE_MODEM (self), "AT^SGPSC=\"Power/Antenna\",\"off\"", 3, FALSE, NULL, NULL); mm_base_modem_at_command (MM_BASE_MODEM (self), "AT^SGPSC=\"NMEA/Output\",\"off\"", 3, FALSE, NULL, NULL); } probe_gps_features (task); } static void sgpss_test_ready (MMBaseModem *self, GAsyncResult *res, GTask *task) { Private *priv; priv = get_private (MM_SHARED_CINTERION (self)); if (!mm_base_modem_at_command_finish (self, res, NULL)) priv->sgpss_support = FEATURE_NOT_SUPPORTED; else { /* ^SGPSS supported! */ priv->sgpss_support = FEATURE_SUPPORTED; /* Flag ^SGPSC as unsupported, even if it may be supported, so that we * only use one set of commands to enable/disable GPS. */ priv->sgpsc_support = FEATURE_NOT_SUPPORTED; /* It may happen that the modem was started with GPS already enabled, or * maybe ModemManager got rebooted and it was left enabled before. We'll * make sure that it is disabled when we initialize the modem. */ mm_base_modem_at_command (MM_BASE_MODEM (self), "AT^SGPSS=0", 3, FALSE, NULL, NULL); } probe_gps_features (task); } static void probe_gps_features (GTask *task) { MMSharedCinterion *self; MMModemLocationSource sources; Private *priv; self = MM_SHARED_CINTERION (g_task_get_source_object (task)); priv = get_private (self); /* Need to check if SGPSS supported... */ if (priv->sgpss_support == FEATURE_SUPPORT_UNKNOWN) { mm_base_modem_at_command (MM_BASE_MODEM (self), "AT^SGPSS=?", 3, TRUE, (GAsyncReadyCallback) sgpss_test_ready, task); return; } /* Need to check if SGPSC supported... */ if (priv->sgpsc_support == FEATURE_SUPPORT_UNKNOWN) { mm_base_modem_at_command (MM_BASE_MODEM (self), "AT^SGPSC=?", 3, TRUE, (GAsyncReadyCallback) sgpsc_test_ready, task); return; } /* All GPS features probed */ /* Recover parent sources */ sources = GPOINTER_TO_UINT (g_task_get_task_data (task)); if (priv->sgpss_support == FEATURE_SUPPORTED || priv->sgpsc_support == FEATURE_SUPPORTED) { mm_obj_dbg (self, "GPS commands supported: GPS capabilities enabled"); /* We only flag as supported by this implementation those sources not already * supported by the parent implementation */ if (!(sources & MM_MODEM_LOCATION_SOURCE_GPS_NMEA)) priv->supported_sources |= MM_MODEM_LOCATION_SOURCE_GPS_NMEA; if (!(sources & MM_MODEM_LOCATION_SOURCE_GPS_RAW)) priv->supported_sources |= MM_MODEM_LOCATION_SOURCE_GPS_RAW; if (!(sources & MM_MODEM_LOCATION_SOURCE_GPS_UNMANAGED)) priv->supported_sources |= MM_MODEM_LOCATION_SOURCE_GPS_UNMANAGED; sources |= priv->supported_sources; /* Add handler for the NMEA traces in the GPS data port */ mm_port_serial_gps_add_trace_handler (mm_base_modem_peek_port_gps (MM_BASE_MODEM (self)), (MMPortSerialGpsTraceFn)trace_received, self, NULL); } else mm_obj_dbg (self, "no GPS command supported: no GPS capabilities"); g_task_return_int (task, (gssize) sources); g_object_unref (task); } static void parent_load_capabilities_ready (MMIfaceModemLocation *self, GAsyncResult *res, GTask *task) { MMModemLocationSource sources; GError *error = NULL; Private *priv; priv = get_private (MM_SHARED_CINTERION (self)); sources = priv->iface_modem_location_parent->load_capabilities_finish (self, res, &error); if (error) { g_task_return_error (task, error); g_object_unref (task); return; } /* Now our own check. If we don't have any GPS port, we're done */ if (!mm_base_modem_peek_port_gps (MM_BASE_MODEM (self))) { mm_obj_dbg (self, "no GPS data port found: no GPS capabilities"); g_task_return_int (task, sources); g_object_unref (task); return; } /* Cache sources supported by the parent */ g_task_set_task_data (task, GUINT_TO_POINTER (sources), NULL); /* Probe all GPS features */ probe_gps_features (task); } void mm_shared_cinterion_location_load_capabilities (MMIfaceModemLocation *self, GAsyncReadyCallback callback, gpointer user_data) { Private *priv; GTask *task; priv = get_private (MM_SHARED_CINTERION (self)); task = g_task_new (self, NULL, callback, user_data); g_assert (priv->iface_modem_location_parent); g_assert (priv->iface_modem_location_parent->load_capabilities); g_assert (priv->iface_modem_location_parent->load_capabilities_finish); priv->iface_modem_location_parent->load_capabilities (self, (GAsyncReadyCallback)parent_load_capabilities_ready, task); } /*****************************************************************************/ /* Disable location gathering (Location interface) */ typedef enum { DISABLE_LOCATION_GATHERING_GPS_STEP_FIRST, DISABLE_LOCATION_GATHERING_GPS_STEP_SGPSS, DISABLE_LOCATION_GATHERING_GPS_STEP_SGPSC_ENGINE, DISABLE_LOCATION_GATHERING_GPS_STEP_SGPSC_ANTENNA, DISABLE_LOCATION_GATHERING_GPS_STEP_SGPSC_OUTPUT, DISABLE_LOCATION_GATHERING_GPS_STEP_LAST, } DisableLocationGatheringGpsStep; typedef struct { MMModemLocationSource source; DisableLocationGatheringGpsStep gps_step; GError *sgpss_error; GError *sgpsc_error; } DisableLocationGatheringContext; static void disable_location_gathering_context_free (DisableLocationGatheringContext *ctx) { if (ctx->sgpss_error) g_error_free (ctx->sgpss_error); if (ctx->sgpsc_error) g_error_free (ctx->sgpsc_error); g_slice_free (DisableLocationGatheringContext, ctx); } gboolean mm_shared_cinterion_disable_location_gathering_finish (MMIfaceModemLocation *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void disable_location_gathering_context_gps_step (GTask *task); static void disable_sgpsc_ready (MMBaseModem *self, GAsyncResult *res, GTask *task) { DisableLocationGatheringContext *ctx; GError *error = NULL; ctx = (DisableLocationGatheringContext *) g_task_get_task_data (task); /* Store error, if not one available already, and continue */ if (!mm_base_modem_at_command_finish (self, res, &error)) { if (!ctx->sgpsc_error) ctx->sgpsc_error = error; else g_error_free (error); } ctx->gps_step++; disable_location_gathering_context_gps_step (task); } static void disable_sgpss_ready (MMBaseModem *self, GAsyncResult *res, GTask *task) { DisableLocationGatheringContext *ctx; ctx = (DisableLocationGatheringContext *) g_task_get_task_data (task); /* Store error, if any, and continue */ g_assert (!ctx->sgpss_error); mm_base_modem_at_command_finish (self, res, &ctx->sgpss_error); ctx->gps_step++; disable_location_gathering_context_gps_step (task); } static void disable_location_gathering_context_gps_step (GTask *task) { DisableLocationGatheringContext *ctx; MMSharedCinterion *self; Private *priv; self = MM_SHARED_CINTERION (g_task_get_source_object (task)); priv = get_private (self); ctx = (DisableLocationGatheringContext *) g_task_get_task_data (task); /* Only one of both supported */ g_assert ((priv->sgpss_support == FEATURE_SUPPORTED) || (priv->sgpsc_support == FEATURE_SUPPORTED)); g_assert (!((priv->sgpss_support == FEATURE_SUPPORTED) && (priv->sgpsc_support == FEATURE_SUPPORTED))); switch (ctx->gps_step) { case DISABLE_LOCATION_GATHERING_GPS_STEP_FIRST: ctx->gps_step++; /* fall through */ case DISABLE_LOCATION_GATHERING_GPS_STEP_SGPSS: if (priv->sgpss_support == FEATURE_SUPPORTED) { mm_base_modem_at_command (MM_BASE_MODEM (self), "AT^SGPSS=0", 3, FALSE, (GAsyncReadyCallback) disable_sgpss_ready, task); return; } ctx->gps_step++; /* fall through */ case DISABLE_LOCATION_GATHERING_GPS_STEP_SGPSC_ENGINE: if (priv->sgpsc_support == FEATURE_SUPPORTED) { /* Engine off */ mm_base_modem_at_command (MM_BASE_MODEM (self), "AT^SGPSC=\"Engine\",\"0\"", 3, FALSE, (GAsyncReadyCallback) disable_sgpsc_ready, task); return; } ctx->gps_step++; /* fall through */ case DISABLE_LOCATION_GATHERING_GPS_STEP_SGPSC_ANTENNA: if (priv->sgpsc_support == FEATURE_SUPPORTED) { /* Antenna off */ mm_base_modem_at_command (MM_BASE_MODEM (self), "AT^SGPSC=\"Power/Antenna\",\"off\"", 3, FALSE, (GAsyncReadyCallback) disable_sgpsc_ready, task); return; } ctx->gps_step++; /* fall through */ case DISABLE_LOCATION_GATHERING_GPS_STEP_SGPSC_OUTPUT: if (priv->sgpsc_support == FEATURE_SUPPORTED) { /* NMEA output off */ mm_base_modem_at_command (MM_BASE_MODEM (self), "AT^SGPSC=\"NMEA/Output\",\"off\"", 3, FALSE, (GAsyncReadyCallback) disable_sgpsc_ready, task); return; } ctx->gps_step++; /* fall through */ case DISABLE_LOCATION_GATHERING_GPS_STEP_LAST: /* Only use the GPS port in NMEA/RAW setups */ if (ctx->source & (MM_MODEM_LOCATION_SOURCE_GPS_NMEA | MM_MODEM_LOCATION_SOURCE_GPS_RAW)) { MMPortSerialGps *gps_port; /* Even if we get an error here, we try to close the GPS port */ gps_port = mm_base_modem_peek_port_gps (MM_BASE_MODEM (self)); if (gps_port) mm_port_serial_close (MM_PORT_SERIAL (gps_port)); } if (ctx->sgpss_error) { g_task_return_error (task, ctx->sgpss_error); g_clear_error (&ctx->sgpss_error); } else if (ctx->sgpsc_error) { g_task_return_error (task, ctx->sgpsc_error); g_clear_error (&ctx->sgpsc_error); } else { priv->enabled_sources &= ~ctx->source; g_task_return_boolean (task, TRUE); } g_object_unref (task); return; default: g_assert_not_reached (); } } static void parent_disable_location_gathering_ready (MMIfaceModemLocation *self, GAsyncResult *res, GTask *task) { GError *error = NULL; Private *priv; priv = get_private (MM_SHARED_CINTERION (self)); g_assert (priv->iface_modem_location_parent); if (!priv->iface_modem_location_parent->disable_location_gathering_finish (self, res, &error)) g_task_return_error (task, error); else g_task_return_boolean (task, TRUE); g_object_unref (task); } void mm_shared_cinterion_disable_location_gathering (MMIfaceModemLocation *self, MMModemLocationSource source, GAsyncReadyCallback callback, gpointer user_data) { DisableLocationGatheringContext *ctx; MMModemLocationSource enabled_sources; Private *priv; GTask *task; task = g_task_new (self, NULL, callback, user_data); priv = get_private (MM_SHARED_CINTERION (self)); g_assert (priv->iface_modem_location_parent); /* Only consider request if it applies to one of the sources we are * supporting, otherwise run parent disable */ if (!(priv->supported_sources & source)) { /* If disabling implemented by the parent, run it. */ if (priv->iface_modem_location_parent->disable_location_gathering && priv->iface_modem_location_parent->disable_location_gathering_finish) { priv->iface_modem_location_parent->disable_location_gathering (self, source, (GAsyncReadyCallback)parent_disable_location_gathering_ready, task); return; } /* Otherwise, we're done */ g_task_return_boolean (task, TRUE); g_object_unref (task); return; } /* We only expect GPS sources here */ g_assert (source & (MM_MODEM_LOCATION_SOURCE_GPS_NMEA | MM_MODEM_LOCATION_SOURCE_GPS_RAW | MM_MODEM_LOCATION_SOURCE_GPS_UNMANAGED)); /* Flag as disabled to see how many others we would have left enabled */ enabled_sources = priv->enabled_sources; enabled_sources &= ~source; /* If there are still GPS-related sources enabled, do nothing else */ if (enabled_sources & (MM_MODEM_LOCATION_SOURCE_GPS_NMEA | MM_MODEM_LOCATION_SOURCE_GPS_RAW | MM_MODEM_LOCATION_SOURCE_GPS_UNMANAGED)) { priv->enabled_sources &= ~source; g_task_return_boolean (task, TRUE); g_object_unref (task); return; } /* Stop GPS engine if all GPS-related sources are disabled */ ctx = g_slice_new0 (DisableLocationGatheringContext); ctx->source = source; ctx->gps_step = DISABLE_LOCATION_GATHERING_GPS_STEP_FIRST; g_task_set_task_data (task, ctx, (GDestroyNotify) disable_location_gathering_context_free); disable_location_gathering_context_gps_step (task); } /*****************************************************************************/ /* Enable location gathering (Location interface) */ /* We will retry the SGPSC command that enables the Engine */ #define MAX_SGPSC_ENGINE_RETRIES 3 /* Cinterion asks for 100ms some time between GPS commands, but we'll give up * to 2000ms before setting the Engine configuration as 100ms didn't seem always * enough (we would get +CME ERROR: 767 errors reported). */ #define GPS_COMMAND_TIMEOUT_DEFAULT_MS 100 #define GPS_COMMAND_TIMEOUT_ENGINE_MS 2000 typedef enum { ENABLE_LOCATION_GATHERING_GPS_STEP_FIRST, ENABLE_LOCATION_GATHERING_GPS_STEP_SGPSS, ENABLE_LOCATION_GATHERING_GPS_STEP_SGPSC_OUTPUT, ENABLE_LOCATION_GATHERING_GPS_STEP_SGPSC_ANTENNA, ENABLE_LOCATION_GATHERING_GPS_STEP_SGPSC_ENGINE, ENABLE_LOCATION_GATHERING_GPS_STEP_LAST, } EnableLocationGatheringGpsStep; typedef struct { MMModemLocationSource source; EnableLocationGatheringGpsStep gps_step; guint sgpsc_engine_retries; } EnableLocationGatheringContext; static void enable_location_gathering_context_free (EnableLocationGatheringContext *ctx) { g_slice_free (EnableLocationGatheringContext, ctx); } gboolean mm_shared_cinterion_enable_location_gathering_finish (MMIfaceModemLocation *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void enable_location_gathering_context_gps_step (GTask *task); static gboolean enable_location_gathering_context_gps_step_schedule_cb (GTask *task) { /* Run the scheduled step */ enable_location_gathering_context_gps_step (task); return G_SOURCE_REMOVE; } static void enable_sgpsc_or_sgpss_ready (MMBaseModem *self, GAsyncResult *res, GTask *task) { EnableLocationGatheringContext *ctx; GError *error = NULL; ctx = (EnableLocationGatheringContext *) g_task_get_task_data (task); if (!mm_base_modem_at_command_finish (self, res, &error)) { /* The GPS setup may sometimes report "+CME ERROR 767" when enabling the * Engine; so we'll run some retries of the same command ourselves. */ if (ctx->gps_step == ENABLE_LOCATION_GATHERING_GPS_STEP_SGPSC_ENGINE) { ctx->sgpsc_engine_retries++; mm_obj_dbg (self, "GPS engine setup failed (%u/%u)", ctx->sgpsc_engine_retries, MAX_SGPSC_ENGINE_RETRIES); if (ctx->sgpsc_engine_retries < MAX_SGPSC_ENGINE_RETRIES) { g_clear_error (&error); goto schedule; } } g_task_return_error (task, error); g_object_unref (task); return; } /* Go on to next step */ ctx->gps_step++; schedule: g_timeout_add (ctx->gps_step == ENABLE_LOCATION_GATHERING_GPS_STEP_SGPSC_ENGINE ? GPS_COMMAND_TIMEOUT_ENGINE_MS : GPS_COMMAND_TIMEOUT_DEFAULT_MS, (GSourceFunc) enable_location_gathering_context_gps_step_schedule_cb, task); } static void enable_location_gathering_context_gps_step (GTask *task) { EnableLocationGatheringContext *ctx; MMSharedCinterion *self; Private *priv; self = MM_SHARED_CINTERION (g_task_get_source_object (task)); priv = get_private (self); ctx = (EnableLocationGatheringContext *) g_task_get_task_data (task); /* Only one of both supported */ g_assert ((priv->sgpss_support == FEATURE_SUPPORTED) || (priv->sgpsc_support == FEATURE_SUPPORTED)); g_assert (!((priv->sgpss_support == FEATURE_SUPPORTED) && (priv->sgpsc_support == FEATURE_SUPPORTED))); switch (ctx->gps_step) { case ENABLE_LOCATION_GATHERING_GPS_STEP_FIRST: ctx->gps_step++; /* fall through */ case ENABLE_LOCATION_GATHERING_GPS_STEP_SGPSS: if (priv->sgpss_support == FEATURE_SUPPORTED) { mm_base_modem_at_command (MM_BASE_MODEM (self), "AT^SGPSS=4", 3, FALSE, (GAsyncReadyCallback) enable_sgpsc_or_sgpss_ready, task); return; } ctx->gps_step++; /* fall through */ case ENABLE_LOCATION_GATHERING_GPS_STEP_SGPSC_OUTPUT: if (priv->sgpsc_support == FEATURE_SUPPORTED) { /* NMEA output off */ mm_base_modem_at_command (MM_BASE_MODEM (self), "AT^SGPSC=\"NMEA/Output\",\"on\"", 3, FALSE, (GAsyncReadyCallback) enable_sgpsc_or_sgpss_ready, task); return; } ctx->gps_step++; /* fall through */ case ENABLE_LOCATION_GATHERING_GPS_STEP_SGPSC_ANTENNA: if (priv->sgpsc_support == FEATURE_SUPPORTED) { /* Antenna off */ mm_base_modem_at_command (MM_BASE_MODEM (self), "AT^SGPSC=\"Power/Antenna\",\"on\"", 3, FALSE, (GAsyncReadyCallback) enable_sgpsc_or_sgpss_ready, task); return; } ctx->gps_step++; /* fall through */ case ENABLE_LOCATION_GATHERING_GPS_STEP_SGPSC_ENGINE: if (priv->sgpsc_support == FEATURE_SUPPORTED) { /* Engine off */ mm_base_modem_at_command (MM_BASE_MODEM (self), "AT^SGPSC=\"Engine\",\"1\"", 3, FALSE, (GAsyncReadyCallback) enable_sgpsc_or_sgpss_ready, task); return; } ctx->gps_step++; /* fall through */ case ENABLE_LOCATION_GATHERING_GPS_STEP_LAST: /* Only use the GPS port in NMEA/RAW setups */ if (ctx->source & (MM_MODEM_LOCATION_SOURCE_GPS_NMEA | MM_MODEM_LOCATION_SOURCE_GPS_RAW)) { MMPortSerialGps *gps_port; GError *error = NULL; gps_port = mm_base_modem_peek_port_gps (MM_BASE_MODEM (self)); if (!gps_port || !mm_port_serial_open (MM_PORT_SERIAL (gps_port), &error)) { if (error) g_task_return_error (task, error); else g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't open raw GPS serial port"); g_object_unref (task); return; } } /* Success */ priv->enabled_sources |= ctx->source; g_task_return_boolean (task, TRUE); g_object_unref (task); return; default: g_assert_not_reached (); } } static void parent_enable_location_gathering_ready (MMIfaceModemLocation *self, GAsyncResult *res, GTask *task) { GError *error = NULL; Private *priv; priv = get_private (MM_SHARED_CINTERION (self)); g_assert (priv->iface_modem_location_parent); if (!priv->iface_modem_location_parent->enable_location_gathering_finish (self, res, &error)) g_task_return_error (task, error); else g_task_return_boolean (task, TRUE); g_object_unref (task); } void mm_shared_cinterion_enable_location_gathering (MMIfaceModemLocation *self, MMModemLocationSource source, GAsyncReadyCallback callback, gpointer user_data) { Private *priv; GTask *task; EnableLocationGatheringContext *ctx; task = g_task_new (self, NULL, callback, user_data); priv = get_private (MM_SHARED_CINTERION (self)); g_assert (priv->iface_modem_location_parent); g_assert (priv->iface_modem_location_parent->enable_location_gathering); g_assert (priv->iface_modem_location_parent->enable_location_gathering_finish); /* Only consider request if it applies to one of the sources we are * supporting, otherwise run parent enable */ if (!(priv->supported_sources & source)) { priv->iface_modem_location_parent->enable_location_gathering (self, source, (GAsyncReadyCallback)parent_enable_location_gathering_ready, task); return; } /* We only expect GPS sources here */ g_assert (source & (MM_MODEM_LOCATION_SOURCE_GPS_NMEA | MM_MODEM_LOCATION_SOURCE_GPS_RAW | MM_MODEM_LOCATION_SOURCE_GPS_UNMANAGED)); /* If GPS already started, store new flag and we're done */ if (priv->enabled_sources & (MM_MODEM_LOCATION_SOURCE_GPS_NMEA | MM_MODEM_LOCATION_SOURCE_GPS_RAW | MM_MODEM_LOCATION_SOURCE_GPS_UNMANAGED)) { priv->enabled_sources |= source; g_task_return_boolean (task, TRUE); g_object_unref (task); return; } ctx = g_slice_new0 (EnableLocationGatheringContext); ctx->source = source; ctx->gps_step = ENABLE_LOCATION_GATHERING_GPS_STEP_FIRST; g_task_set_task_data (task, ctx, (GDestroyNotify) enable_location_gathering_context_free); enable_location_gathering_context_gps_step (task); } /*****************************************************************************/ MMBaseCall * mm_shared_cinterion_create_call (MMIfaceModemVoice *self, MMCallDirection direction, const gchar *number) { Private *priv; /* If ^SLCC is supported create a cinterion call object */ priv = get_private (MM_SHARED_CINTERION (self)); if (priv->slcc_support == FEATURE_SUPPORTED) { mm_obj_dbg (self, "created new call with ^SLCC support"); return mm_base_call_new (MM_BASE_MODEM (self), direction, number, /* When SLCC is supported we have support for detailed * call list events via call list report URCs */ TRUE, /* incoming timeout not required */ TRUE, /* dialing->ringing supported */ TRUE); /* ringing->active supported */ } /* otherwise, run parent's generic base call logic */ g_assert (priv->iface_modem_voice_parent); g_assert (priv->iface_modem_voice_parent->create_call); return priv->iface_modem_voice_parent->create_call (self, direction, number); } /*****************************************************************************/ /* Common enable/disable voice unsolicited events */ typedef struct { gboolean enable; MMPortSerialAt *primary; MMPortSerialAt *secondary; gchar *slcc_command; gboolean slcc_primary_done; gboolean slcc_secondary_done; } VoiceUnsolicitedEventsContext; static void voice_unsolicited_events_context_free (VoiceUnsolicitedEventsContext *ctx) { g_clear_object (&ctx->secondary); g_clear_object (&ctx->primary); g_free (ctx->slcc_command); g_slice_free (VoiceUnsolicitedEventsContext, ctx); } static gboolean common_voice_enable_disable_unsolicited_events_finish (MMSharedCinterion *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void run_voice_enable_disable_unsolicited_events (GTask *task); static void slcc_command_ready (MMBaseModem *self, GAsyncResult *res, GTask *task) { VoiceUnsolicitedEventsContext *ctx; g_autoptr(GError) error = NULL; ctx = g_task_get_task_data (task); if (!mm_base_modem_at_command_full_finish (self, res, &error)) mm_obj_dbg (self, "couldn't %s ^SLCC reporting: %s", ctx->enable ? "enable" : "disable", error->message); /* Continue on next port */ run_voice_enable_disable_unsolicited_events (task); } static void run_voice_enable_disable_unsolicited_events (GTask *task) { MMSharedCinterion *self; Private *priv; VoiceUnsolicitedEventsContext *ctx; MMPortSerialAt *port = NULL; self = MM_SHARED_CINTERION (g_task_get_source_object (task)); priv = get_private (self); ctx = g_task_get_task_data (task); /* If not ^SLCC supported, we're done */ if (priv->slcc_support == FEATURE_NOT_SUPPORTED) { g_task_return_boolean (task, TRUE); g_object_unref (task); return; } if (!ctx->slcc_primary_done && ctx->primary) { mm_obj_dbg (self, "%s ^SLCC extended list of current calls reporting in primary port...", ctx->enable ? "enabling" : "disabling"); ctx->slcc_primary_done = TRUE; port = ctx->primary; } else if (!ctx->slcc_secondary_done && ctx->secondary) { mm_obj_dbg (self, "%s ^SLCC extended list of current calls reporting in secondary port...", ctx->enable ? "enabling" : "disabling"); ctx->slcc_secondary_done = TRUE; port = ctx->secondary; } if (port) { mm_base_modem_at_command_full (MM_BASE_MODEM (self), port, ctx->slcc_command, 3, FALSE, FALSE, NULL, (GAsyncReadyCallback)slcc_command_ready, task); return; } /* Fully done now */ g_task_return_boolean (task, TRUE); g_object_unref (task); } static void common_voice_enable_disable_unsolicited_events (MMSharedCinterion *self, gboolean enable, GAsyncReadyCallback callback, gpointer user_data) { VoiceUnsolicitedEventsContext *ctx; GTask *task; task = g_task_new (self, NULL, callback, user_data); ctx = g_slice_new0 (VoiceUnsolicitedEventsContext); ctx->enable = enable; if (enable) ctx->slcc_command = g_strdup ("^SLCC=1"); else ctx->slcc_command = g_strdup ("^SLCC=0"); ctx->primary = mm_base_modem_get_port_primary (MM_BASE_MODEM (self)); ctx->secondary = mm_base_modem_get_port_secondary (MM_BASE_MODEM (self)); g_task_set_task_data (task, ctx, (GDestroyNotify) voice_unsolicited_events_context_free); run_voice_enable_disable_unsolicited_events (task); } /*****************************************************************************/ /* Disable unsolicited events (Voice interface) */ gboolean mm_shared_cinterion_voice_disable_unsolicited_events_finish (MMIfaceModemVoice *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void parent_voice_disable_unsolicited_events_ready (MMIfaceModemVoice *self, GAsyncResult *res, GTask *task) { g_autoptr(GError) error = NULL; Private *priv; priv = get_private (MM_SHARED_CINTERION (self)); if (!priv->iface_modem_voice_parent->disable_unsolicited_events_finish (self, res, &error)) mm_obj_warn (self, "couldn't disable parent voice unsolicited events: %s", error->message); g_task_return_boolean (task, TRUE); g_object_unref (task); } static void voice_disable_unsolicited_events_ready (MMSharedCinterion *self, GAsyncResult *res, GTask *task) { Private *priv; g_autoptr(GError) error = NULL; if (!common_voice_enable_disable_unsolicited_events_finish (self, res, &error)) mm_obj_warn (self, "couldn't disable Cinterion-specific voice unsolicited events: %s", error->message); priv = get_private (MM_SHARED_CINTERION (self)); g_assert (priv->iface_modem_voice_parent); g_assert (priv->iface_modem_voice_parent->disable_unsolicited_events); g_assert (priv->iface_modem_voice_parent->disable_unsolicited_events_finish); /* Chain up parent's disable */ priv->iface_modem_voice_parent->disable_unsolicited_events ( MM_IFACE_MODEM_VOICE (self), (GAsyncReadyCallback)parent_voice_disable_unsolicited_events_ready, task); } void mm_shared_cinterion_voice_disable_unsolicited_events (MMIfaceModemVoice *self, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; task = g_task_new (self, NULL, callback, user_data); /* our own disabling first */ common_voice_enable_disable_unsolicited_events (MM_SHARED_CINTERION (self), FALSE, (GAsyncReadyCallback) voice_disable_unsolicited_events_ready, task); } /*****************************************************************************/ /* Enable unsolicited events (Voice interface) */ gboolean mm_shared_cinterion_voice_enable_unsolicited_events_finish (MMIfaceModemVoice *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void voice_enable_unsolicited_events_ready (MMSharedCinterion *self, GAsyncResult *res, GTask *task) { g_autoptr(GError) error = NULL; if (!common_voice_enable_disable_unsolicited_events_finish (self, res, &error)) mm_obj_warn (self, "couldn't enable Cinterion-specific voice unsolicited events: %s", error->message); g_task_return_boolean (task, TRUE); g_object_unref (task); } static void parent_voice_enable_unsolicited_events_ready (MMIfaceModemVoice *self, GAsyncResult *res, GTask *task) { g_autoptr(GError) error = NULL; Private *priv; priv = get_private (MM_SHARED_CINTERION (self)); if (!priv->iface_modem_voice_parent->enable_unsolicited_events_finish (self, res, &error)) mm_obj_warn (self, "couldn't enable parent voice unsolicited events: %s", error->message); /* our own enabling next */ common_voice_enable_disable_unsolicited_events (MM_SHARED_CINTERION (self), TRUE, (GAsyncReadyCallback) voice_enable_unsolicited_events_ready, task); } void mm_shared_cinterion_voice_enable_unsolicited_events (MMIfaceModemVoice *self, GAsyncReadyCallback callback, gpointer user_data) { Private *priv; GTask *task; task = g_task_new (self, NULL, callback, user_data); priv = get_private (MM_SHARED_CINTERION (self)); g_assert (priv->iface_modem_voice_parent); g_assert (priv->iface_modem_voice_parent->enable_unsolicited_events); g_assert (priv->iface_modem_voice_parent->enable_unsolicited_events_finish); /* chain up parent's enable first */ priv->iface_modem_voice_parent->enable_unsolicited_events ( self, (GAsyncReadyCallback)parent_voice_enable_unsolicited_events_ready, task); } /*****************************************************************************/ /* Common setup/cleanup voice unsolicited events */ static void slcc_received (MMPortSerialAt *port, GMatchInfo *match_info, MMSharedCinterion *self) { g_autofree gchar *full = NULL; g_autoptr(GError) error = NULL; GList *call_info_list = NULL; full = g_match_info_fetch (match_info, 0); if (!mm_cinterion_parse_slcc_list (full, self, &call_info_list, &error)) mm_obj_warn (self, "couldn't parse ^SLCC list: %s", error->message); else mm_iface_modem_voice_report_all_calls (MM_IFACE_MODEM_VOICE (self), call_info_list); mm_cinterion_call_info_list_free (call_info_list); } static void common_voice_setup_cleanup_unsolicited_events (MMSharedCinterion *self, gboolean enable) { Private *priv; MMPortSerialAt *ports[2]; guint i; priv = get_private (MM_SHARED_CINTERION (self)); ports[0] = mm_base_modem_peek_port_primary (MM_BASE_MODEM (self)); ports[1] = mm_base_modem_peek_port_secondary (MM_BASE_MODEM (self)); for (i = 0; i < G_N_ELEMENTS (ports); i++) { if (!ports[i]) continue; mm_port_serial_at_add_unsolicited_msg_handler (ports[i], priv->slcc_regex, enable ? (MMPortSerialAtUnsolicitedMsgFn)slcc_received : NULL, enable ? self : NULL, NULL); } } /*****************************************************************************/ /* Cleanup unsolicited events (Voice interface) */ gboolean mm_shared_cinterion_voice_cleanup_unsolicited_events_finish (MMIfaceModemVoice *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void parent_voice_cleanup_unsolicited_events_ready (MMIfaceModemVoice *self, GAsyncResult *res, GTask *task) { g_autoptr(GError) error = NULL; Private *priv; priv = get_private (MM_SHARED_CINTERION (self)); if (!priv->iface_modem_voice_parent->cleanup_unsolicited_events_finish (self, res, &error)) mm_obj_warn (self, "couldn't cleanup parent voice unsolicited events: %s", error->message); g_task_return_boolean (task, TRUE); g_object_unref (task); } void mm_shared_cinterion_voice_cleanup_unsolicited_events (MMIfaceModemVoice *self, GAsyncReadyCallback callback, gpointer user_data) { Private *priv; GTask *task; task = g_task_new (self, NULL, callback, user_data); priv = get_private (MM_SHARED_CINTERION (self)); g_assert (priv->iface_modem_voice_parent); g_assert (priv->iface_modem_voice_parent->cleanup_unsolicited_events); g_assert (priv->iface_modem_voice_parent->cleanup_unsolicited_events_finish); /* our own cleanup first */ common_voice_setup_cleanup_unsolicited_events (MM_SHARED_CINTERION (self), FALSE); /* Chain up parent's cleanup */ priv->iface_modem_voice_parent->cleanup_unsolicited_events ( self, (GAsyncReadyCallback)parent_voice_cleanup_unsolicited_events_ready, task); } /*****************************************************************************/ /* Setup unsolicited events (Voice interface) */ gboolean mm_shared_cinterion_voice_setup_unsolicited_events_finish (MMIfaceModemVoice *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void parent_voice_setup_unsolicited_events_ready (MMIfaceModemVoice *self, GAsyncResult *res, GTask *task) { g_autoptr(GError) error = NULL; Private *priv; priv = get_private (MM_SHARED_CINTERION (self)); if (!priv->iface_modem_voice_parent->setup_unsolicited_events_finish (self, res, &error)) mm_obj_warn (self, "Couldn't setup parent voice unsolicited events: %s", error->message); /* our own setup next */ common_voice_setup_cleanup_unsolicited_events (MM_SHARED_CINTERION (self), TRUE); g_task_return_boolean (task, TRUE); g_object_unref (task); } void mm_shared_cinterion_voice_setup_unsolicited_events (MMIfaceModemVoice *self, GAsyncReadyCallback callback, gpointer user_data) { Private *priv; GTask *task; task = g_task_new (self, NULL, callback, user_data); priv = get_private (MM_SHARED_CINTERION (self)); g_assert (priv->iface_modem_voice_parent); g_assert (priv->iface_modem_voice_parent->setup_unsolicited_events); g_assert (priv->iface_modem_voice_parent->setup_unsolicited_events_finish); /* chain up parent's setup first */ priv->iface_modem_voice_parent->setup_unsolicited_events ( self, (GAsyncReadyCallback)parent_voice_setup_unsolicited_events_ready, task); } /*****************************************************************************/ /* Check if Voice supported (Voice interface) */ gboolean mm_shared_cinterion_voice_check_support_finish (MMIfaceModemVoice *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void slcc_format_check_ready (MMBroadbandModem *self, GAsyncResult *res, GTask *task) { Private *priv; priv = get_private (MM_SHARED_CINTERION (self)); /* ^SLCC supported unless we got any error response */ priv->slcc_support = (!!mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, NULL) ? FEATURE_SUPPORTED : FEATURE_NOT_SUPPORTED); /* If ^SLCC supported we won't need polling in the parent */ g_object_set (self, MM_IFACE_MODEM_VOICE_PERIODIC_CALL_LIST_CHECK_DISABLED, (priv->slcc_support == FEATURE_SUPPORTED), NULL); /* ^SLCC command is supported; assume we have full voice capabilities */ g_task_return_boolean (task, TRUE); g_object_unref (task); } static void parent_voice_check_support_ready (MMIfaceModemVoice *self, GAsyncResult *res, GTask *task) { Private *priv; GError *error = NULL; priv = get_private (MM_SHARED_CINTERION (self)); if (!priv->iface_modem_voice_parent->check_support_finish (self, res, &error)) { g_task_return_error (task, error); g_object_unref (task); return; } /* voice is supported, check if ^SLCC is available */ mm_base_modem_at_command (MM_BASE_MODEM (self), "^SLCC=?", 3, /* Do NOT cache as the reply may be different if PIN locked * or unlocked. E.g. we may not support ^SLCC for emergency * voice calls. */ FALSE, (GAsyncReadyCallback) slcc_format_check_ready, task); } void mm_shared_cinterion_voice_check_support (MMIfaceModemVoice *self, GAsyncReadyCallback callback, gpointer user_data) { Private *priv; GTask *task; task = g_task_new (self, NULL, callback, user_data); priv = get_private (MM_SHARED_CINTERION (self)); g_assert (priv->iface_modem_voice_parent); g_assert (priv->iface_modem_voice_parent->check_support); g_assert (priv->iface_modem_voice_parent->check_support_finish); /* chain up parent's setup first */ priv->iface_modem_voice_parent->check_support ( self, (GAsyncReadyCallback)parent_voice_check_support_ready, task); } /*****************************************************************************/ /* Common setup/cleanup time unsolicited events */ static void ctzu_received (MMPortSerialAt *port, GMatchInfo *match_info, MMSharedCinterion *self) { g_autofree gchar *iso8601 = NULL; g_autoptr(MMNetworkTimezone) tz = NULL; g_autoptr(GError) error = NULL; if (!mm_cinterion_parse_ctzu_urc (match_info, &iso8601, &tz, &error)) { mm_obj_dbg (self, "couldn't process +CTZU URC: %s", error->message); return; } mm_obj_dbg (self, "+CTZU URC received: %s", iso8601); mm_iface_modem_time_update_network_time (MM_IFACE_MODEM_TIME (self), iso8601); mm_iface_modem_time_update_network_timezone (MM_IFACE_MODEM_TIME (self), tz); } static void common_time_setup_cleanup_unsolicited_events (MMSharedCinterion *self, gboolean enable) { Private *priv; MMPortSerialAt *ports[2]; guint i; priv = get_private (MM_SHARED_CINTERION (self)); ports[0] = mm_base_modem_peek_port_primary (MM_BASE_MODEM (self)); ports[1] = mm_base_modem_peek_port_secondary (MM_BASE_MODEM (self)); mm_obj_dbg (self, "%s up time unsolicited events...", enable ? "setting" : "cleaning"); for (i = 0; i < G_N_ELEMENTS (ports); i++) { if (!ports[i]) continue; mm_port_serial_at_add_unsolicited_msg_handler (ports[i], priv->ctzu_regex, enable ? (MMPortSerialAtUnsolicitedMsgFn)ctzu_received : NULL, enable ? self : NULL, NULL); } } /*****************************************************************************/ /* Cleanup unsolicited events (Time interface) */ gboolean mm_shared_cinterion_time_cleanup_unsolicited_events_finish (MMIfaceModemTime *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void parent_time_cleanup_unsolicited_events_ready (MMIfaceModemTime *self, GAsyncResult *res, GTask *task) { g_autoptr(GError) error = NULL; Private *priv; priv = get_private (MM_SHARED_CINTERION (self)); if (!priv->iface_modem_time_parent->cleanup_unsolicited_events_finish (self, res, &error)) mm_obj_warn (self, "couldn't cleanup parent time unsolicited events: %s", error->message); g_task_return_boolean (task, TRUE); g_object_unref (task); } void mm_shared_cinterion_time_cleanup_unsolicited_events (MMIfaceModemTime *self, GAsyncReadyCallback callback, gpointer user_data) { Private *priv; GTask *task; task = g_task_new (self, NULL, callback, user_data); priv = get_private (MM_SHARED_CINTERION (self)); g_assert (priv->iface_modem_time_parent); /* our own cleanup first */ common_time_setup_cleanup_unsolicited_events (MM_SHARED_CINTERION (self), FALSE); if (priv->iface_modem_time_parent->cleanup_unsolicited_events && priv->iface_modem_time_parent->cleanup_unsolicited_events_finish) { /* Chain up parent's cleanup */ priv->iface_modem_time_parent->cleanup_unsolicited_events ( self, (GAsyncReadyCallback)parent_time_cleanup_unsolicited_events_ready, task); return; } g_task_return_boolean (task, TRUE); g_object_unref (task); } /*****************************************************************************/ /* Setup unsolicited events (Time interface) */ gboolean mm_shared_cinterion_time_setup_unsolicited_events_finish (MMIfaceModemTime *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void own_time_setup_unsolicited_events (GTask *task) { MMSharedCinterion *self; self = g_task_get_source_object (task); /* our own setup next */ common_time_setup_cleanup_unsolicited_events (MM_SHARED_CINTERION (self), TRUE); g_task_return_boolean (task, TRUE); g_object_unref (task); } static void parent_time_setup_unsolicited_events_ready (MMIfaceModemTime *self, GAsyncResult *res, GTask *task) { g_autoptr(GError) error = NULL; Private *priv; priv = get_private (MM_SHARED_CINTERION (self)); if (!priv->iface_modem_time_parent->cleanup_unsolicited_events_finish (self, res, &error)) mm_obj_warn (self, "Couldn't cleanup parent time unsolicited events: %s", error->message); own_time_setup_unsolicited_events (task); } void mm_shared_cinterion_time_setup_unsolicited_events (MMIfaceModemTime *self, GAsyncReadyCallback callback, gpointer user_data) { Private *priv; GTask *task; task = g_task_new (self, NULL, callback, user_data); priv = get_private (MM_SHARED_CINTERION (self)); g_assert (priv->iface_modem_time_parent); if (priv->iface_modem_time_parent->setup_unsolicited_events && priv->iface_modem_time_parent->setup_unsolicited_events_finish) { /* chain up parent's setup first */ priv->iface_modem_time_parent->setup_unsolicited_events ( self, (GAsyncReadyCallback)parent_time_setup_unsolicited_events_ready, task); return; } own_time_setup_unsolicited_events (task); } /*****************************************************************************/ static void shared_cinterion_init (gpointer g_iface) { } GType mm_shared_cinterion_get_type (void) { static GType shared_cinterion_type = 0; if (!G_UNLIKELY (shared_cinterion_type)) { static const GTypeInfo info = { sizeof (MMSharedCinterion), /* class_size */ shared_cinterion_init, /* base_init */ NULL, /* base_finalize */ }; shared_cinterion_type = g_type_register_static (G_TYPE_INTERFACE, "MMSharedCinterion", &info, 0); g_type_interface_add_prerequisite (shared_cinterion_type, MM_TYPE_IFACE_MODEM); g_type_interface_add_prerequisite (shared_cinterion_type, MM_TYPE_IFACE_MODEM_VOICE); g_type_interface_add_prerequisite (shared_cinterion_type, MM_TYPE_IFACE_MODEM_TIME); g_type_interface_add_prerequisite (shared_cinterion_type, MM_TYPE_IFACE_MODEM_LOCATION); } return shared_cinterion_type; } ModemManager-1.23.4-dev/src/plugins/cinterion/mm-shared-cinterion.h000066400000000000000000000226321456466623000252120ustar00rootroot00000000000000/* -*- 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) 2014 Ammonit Measurement GmbH * Copyright (C) 2014 - 2018 Aleksander Morgado * Copyright (C) 2019 Purism SPC */ #ifndef MM_SHARED_CINTERION_H #define MM_SHARED_CINTERION_H #include #include #define _LIBMM_INSIDE_MM #include #include "mm-broadband-modem.h" #include "mm-iface-modem.h" #include "mm-iface-modem-location.h" #include "mm-iface-modem-voice.h" #include "mm-iface-modem-time.h" #define MM_TYPE_SHARED_CINTERION (mm_shared_cinterion_get_type ()) #define MM_SHARED_CINTERION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_SHARED_CINTERION, MMSharedCinterion)) #define MM_IS_SHARED_CINTERION(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_SHARED_CINTERION)) #define MM_SHARED_CINTERION_GET_INTERFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE ((obj), MM_TYPE_SHARED_CINTERION, MMSharedCinterion)) typedef struct _MMSharedCinterion MMSharedCinterion; struct _MMSharedCinterion { GTypeInterface g_iface; /* Peek modem interface of the parent class of the object */ MMIfaceModem * (* peek_parent_interface) (MMSharedCinterion *self); /* Peek location interface of the parent class of the object */ MMIfaceModemLocation * (* peek_parent_location_interface) (MMSharedCinterion *self); /* Peek voice interface of the parent class of the object */ MMIfaceModemVoice * (* peek_parent_voice_interface) (MMSharedCinterion *self); /* Peek time interface of the parent class of the object */ MMIfaceModemTime * (* peek_parent_time_interface) (MMSharedCinterion *self); }; GType mm_shared_cinterion_get_type (void); /*****************************************************************************/ /* Modem interface */ void mm_shared_cinterion_modem_reset (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data); gboolean mm_shared_cinterion_modem_reset_finish (MMIfaceModem *self, GAsyncResult *res, GError **error); /*****************************************************************************/ /* Location interface */ void mm_shared_cinterion_location_load_capabilities (MMIfaceModemLocation *self, GAsyncReadyCallback callback, gpointer user_data); MMModemLocationSource mm_shared_cinterion_location_load_capabilities_finish (MMIfaceModemLocation *self, GAsyncResult *res, GError **error); void mm_shared_cinterion_enable_location_gathering (MMIfaceModemLocation *self, MMModemLocationSource source, GAsyncReadyCallback callback, gpointer user_data); gboolean mm_shared_cinterion_enable_location_gathering_finish (MMIfaceModemLocation *self, GAsyncResult *res, GError **error); void mm_shared_cinterion_disable_location_gathering (MMIfaceModemLocation *self, MMModemLocationSource source, GAsyncReadyCallback callback, gpointer user_data); gboolean mm_shared_cinterion_disable_location_gathering_finish (MMIfaceModemLocation *self, GAsyncResult *res, GError **error); /*****************************************************************************/ /* Voice interface */ MMBaseCall *mm_shared_cinterion_create_call (MMIfaceModemVoice *self, MMCallDirection direction, const gchar *number); void mm_shared_cinterion_voice_check_support (MMIfaceModemVoice *self, GAsyncReadyCallback callback, gpointer user_data); gboolean mm_shared_cinterion_voice_check_support_finish (MMIfaceModemVoice *self, GAsyncResult *res, GError **error); void mm_shared_cinterion_voice_setup_unsolicited_events (MMIfaceModemVoice *self, GAsyncReadyCallback callback, gpointer user_data); gboolean mm_shared_cinterion_voice_setup_unsolicited_events_finish (MMIfaceModemVoice *self, GAsyncResult *res, GError **error); void mm_shared_cinterion_voice_cleanup_unsolicited_events (MMIfaceModemVoice *self, GAsyncReadyCallback callback, gpointer user_data); gboolean mm_shared_cinterion_voice_cleanup_unsolicited_events_finish (MMIfaceModemVoice *self, GAsyncResult *res, GError **error); void mm_shared_cinterion_voice_enable_unsolicited_events (MMIfaceModemVoice *self, GAsyncReadyCallback callback, gpointer user_data); gboolean mm_shared_cinterion_voice_enable_unsolicited_events_finish (MMIfaceModemVoice *self, GAsyncResult *res, GError **error); void mm_shared_cinterion_voice_disable_unsolicited_events (MMIfaceModemVoice *self, GAsyncReadyCallback callback, gpointer user_data); gboolean mm_shared_cinterion_voice_disable_unsolicited_events_finish (MMIfaceModemVoice *self, GAsyncResult *res, GError **error); /*****************************************************************************/ /* Time interface */ void mm_shared_cinterion_time_setup_unsolicited_events (MMIfaceModemTime *self, GAsyncReadyCallback callback, gpointer user_data); gboolean mm_shared_cinterion_time_setup_unsolicited_events_finish (MMIfaceModemTime *self, GAsyncResult *res, GError **error); void mm_shared_cinterion_time_cleanup_unsolicited_events (MMIfaceModemTime *self, GAsyncReadyCallback callback, gpointer user_data); gboolean mm_shared_cinterion_time_cleanup_unsolicited_events_finish (MMIfaceModemTime *self, GAsyncResult *res, GError **error); #endif /* MM_SHARED_CINTERION_H */ ModemManager-1.23.4-dev/src/plugins/cinterion/tests/000077500000000000000000000000001456466623000223315ustar00rootroot00000000000000ModemManager-1.23.4-dev/src/plugins/cinterion/tests/test-modem-helpers-cinterion.c000066400000000000000000002571371456466623000302220ustar00rootroot00000000000000/* -*- 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) 2014 Aleksander Morgado */ #include #include #include #include #define _LIBMM_INSIDE_MM #include #include #include "mm-log-test.h" #include "mm-modem-helpers.h" #include "mm-modem-helpers-cinterion.h" #define g_assert_cmpfloat_tolerance(val1, val2, tolerance) \ g_assert_cmpfloat (fabs (val1 - val2), <, tolerance) /*****************************************************************************/ /* Test ^SCFG test responses */ static void common_test_scfg (const gchar *response, GArray *expected_bands, MMModemCharset charset, MMCinterionModemFamily modem_family) { GArray *bands = NULL; gchar *expected_bands_str; gchar *bands_str; GError *error = NULL; gboolean res; MMCinterionRadioBandFormat format; res = mm_cinterion_parse_scfg_test (response, modem_family, charset, &bands, &format, &error); g_assert_no_error (error); g_assert (res == TRUE); g_assert (bands != NULL); mm_common_bands_garray_sort (bands); mm_common_bands_garray_sort (expected_bands); expected_bands_str = mm_common_build_bands_string ((const MMModemBand *)(gconstpointer)expected_bands->data, expected_bands->len); bands_str = mm_common_build_bands_string ((const MMModemBand *)(gconstpointer)bands->data, bands->len); /* Instead of comparing the array one by one, compare the strings built from the mask * (we get a nicer error if it fails) */ g_assert_cmpstr (bands_str, ==, expected_bands_str); g_free (bands_str); g_free (expected_bands_str); g_array_unref (bands); } static void test_scfg (void) { GArray *expected_bands; MMModemBand single; const gchar *response = "^SCFG: \"Audio/Loop\",(\"0\",\"1\")\r\n" "^SCFG: \"Call/ECC\",(\"0\"-\"255\")\r\n" "^SCFG: \"Call/Speech/Codec\",(\"0\",\"1\")\r\n" "^SCFG: \"GPRS/Auth\",(\"0\",\"1\",\"2\")\r\n" "^SCFG: \"GPRS/AutoAttach\",(\"disabled\",\"enabled\")\r\n" "^SCFG: \"GPRS/MaxDataRate/HSDPA\",(\"0\",\"1\")\r\n" "^SCFG: \"GPRS/MaxDataRate/HSUPA\",(\"0\",\"1\")\r\n" "^SCFG: \"Ident/Manufacturer\",(25)\r\n" "^SCFG: \"Ident/Product\",(25)\r\n" "^SCFG: \"MEopMode/Airplane\",(\"off\",\"on\")\r\n" "^SCFG: \"MEopMode/CregRoam\",(\"0\",\"1\")\r\n" "^SCFG: \"MEopMode/CFUN\",(\"0\",\"1\")\r\n" "^SCFG: \"MEopMode/PowerMgmt/LCI\",(\"disabled\",\"enabled\")\r\n" "^SCFG: \"MEopMode/PowerMgmt/VExt\",(\"high\",\"low\")\r\n" "^SCFG: \"MEopMode/PwrSave\",(\"disabled\",\"enabled\"),(\"0-600\"),(\"1-36000\")\r\n" "^SCFG: \"MEopMode/RingOnData\",(\"on\",\"off\")\r\n" "^SCFG: \"MEopMode/RingUrcOnCall\",(\"on\",\"off\")\r\n" "^SCFG: \"MEShutdown/OnIgnition\",(\"on\",\"off\")\r\n" "^SCFG: \"Radio/Band\",(\"1-511\",\"0-1\")\r\n" "^SCFG: \"Radio/NWSM\",(\"0\",\"1\",\"2\")\r\n" "^SCFG: \"Radio/OutputPowerReduction\",(\"4\"-\"8\")\r\n" "^SCFG: \"Serial/USB/DDD\",(\"0\",\"1\"),(\"0\"),(4),(4),(4),(63),(63),(4)\r\n" "^SCFG: \"URC/DstIfc\",(\"mdm\",\"app\")\r\n" "^SCFG: \"URC/Datamode/Ringline\",(\"off\",\"on\")\r\n" "^SCFG: \"URC/Ringline\",(\"off\",\"local\",\"asc0\",\"wakeup\")\r\n" "^SCFG: \"URC/Ringline/ActiveTime\",(\"0\",\"1\",\"2\",\"keep\")\r\n"; expected_bands = g_array_sized_new (FALSE, FALSE, sizeof (MMModemBand), 9); single = MM_MODEM_BAND_EGSM, g_array_append_val (expected_bands, single); single = MM_MODEM_BAND_DCS, g_array_append_val (expected_bands, single); single = MM_MODEM_BAND_PCS, g_array_append_val (expected_bands, single); single = MM_MODEM_BAND_G850, g_array_append_val (expected_bands, single); single = MM_MODEM_BAND_UTRAN_1, g_array_append_val (expected_bands, single); single = MM_MODEM_BAND_UTRAN_2, g_array_append_val (expected_bands, single); single = MM_MODEM_BAND_UTRAN_5, g_array_append_val (expected_bands, single); single = MM_MODEM_BAND_UTRAN_8, g_array_append_val (expected_bands, single); single = MM_MODEM_BAND_UTRAN_6, g_array_append_val (expected_bands, single); common_test_scfg (response, expected_bands, MM_MODEM_CHARSET_UNKNOWN, MM_CINTERION_MODEM_FAMILY_DEFAULT); g_array_unref (expected_bands); } static void test_scfg_ehs5 (void) { GArray *expected_bands; MMModemBand single; const gchar *response = "^SCFG: \"Audio/Loop\",(\"0\",\"1\")\r\n" "^SCFG: \"Call/ECC\",(\"0\"-\"255\")\r\n" "^SCFG: \"Call/Ecall/AckTimeout\",(\"0-2147483646\")\r\n" "^SCFG: \"Call/Ecall/Callback\",(\"0\",\"1\")\r\n" "^SCFG: \"Call/Ecall/CallbackTimeout\",(\"0-2147483646\")\r\n" "^SCFG: \"Call/Ecall/Msd\",(\"280\")\r\n" "^SCFG: \"Call/Ecall/Pullmode\",(\"0\",\"1\")\r\n" "^SCFG: \"Call/Ecall/SessionTimeout\",(\"0-2147483646\")\r\n" "^SCFG: \"Call/Ecall/StartTimeout\",(\"0-2147483646\")\r\n" "^SCFG: \"Call/Speech/Codec\",(\"0\",\"1\")\r\n" "^SCFG: \"GPRS/AutoAttach\",(\"disabled\",\"enabled\")\r\n" "^SCFG: \"Gpio/mode/ASC1\",(\"std\",\"gpio\",\"rsv\")\r\n" "^SCFG: \"Gpio/mode/DAI\",(\"std\",\"gpio\",\"rsv\")\r\n" "^SCFG: \"Gpio/mode/DCD0\",(\"std\",\"gpio\",\"rsv\")\r\n" "^SCFG: \"Gpio/mode/DSR0\",(\"std\",\"gpio\",\"rsv\")\r\n" "^SCFG: \"Gpio/mode/DTR0\",(\"std\",\"gpio\",\"rsv\")\r\n" "^SCFG: \"Gpio/mode/FSR\",(\"std\",\"gpio\",\"rsv\")\r\n" "^SCFG: \"Gpio/mode/PULSE\",(\"std\",\"gpio\",\"rsv\")\r\n" "^SCFG: \"Gpio/mode/PWM\",(\"std\",\"gpio\",\"rsv\")\r\n" "^SCFG: \"Gpio/mode/RING0\",(\"std\",\"gpio\",\"rsv\")\r\n" "^SCFG: \"Gpio/mode/SPI\",(\"std\",\"gpio\",\"rsv\")\r\n" "^SCFG: \"Gpio/mode/SYNC\",(\"std\",\"gpio\",\"rsv\")\r\n" "^SCFG: \"Ident/Manufacturer\",(25)\r\n" "^SCFG: \"Ident/Product\",(25)\r\n" "^SCFG: \"MEShutdown/Fso\",(\"0\",\"1\")\r\n" "^SCFG: \"MEShutdown/sVsup/threshold\",(\"-4\",\"-3\",\"-2\",\"-1\",\"0\",\"1\",\"2\",\"3\",\"4\"),(\"0\")\r\n" "^SCFG: \"MEopMode/CFUN\",(\"0\",\"1\"),(\"1\",\"4\")\r\n" "^SCFG: \"MEopMode/Dormancy\",(\"0\",\"1\")\r\n" "^SCFG: \"MEopMode/SoR\",(\"off\",\"on\")\r\n" "^SCFG: \"Radio/Band\",(\"1\"-\"147\")\r\n" "^SCFG: \"Radio/Mtpl\",(\"0\"-\"3\"),(\"1\"-\"8\"),(\"1\",\"8\"),(\"18\"-\"33\"),(\"18\"-\"27\")\r\n" "^SCFG: \"Radio/Mtpl\",(\"0\"-\"3\"),(\"1\"-\"8\"),(\"16\",\"32\",\"64\",\"128\",\"256\"),(\"18\"-\"24\")\r\n" "^SCFG: \"Radio/Mtpl\",(\"0\"-\"3\"),(\"1\"-\"8\"),(\"2\",\"4\"),(\"18\"-\"30\"),(\"18\"-\"26\")\r\n" "^SCFG: \"Radio/OutputPowerReduction\",(\"0\",\"1\",\"2\",\"3\",\"4\")\r\n" "^SCFG: \"Serial/Interface/Allocation\",(\"0\",\"1\",\"2\"),(\"0\",\"1\",\"2\")\r\n" "^SCFG: \"Serial/USB/DDD\",(\"0\",\"1\"),(\"0\"),(4),(4),(4),(63),(63),(4)\r\n" "^SCFG: \"Tcp/IRT\",(\"1\"-\"60\")\r\n" "^SCFG: \"Tcp/MR\",(\"1\"-\"30\")\r\n" "^SCFG: \"Tcp/OT\",(\"1\"-\"6000\")\r\n" "^SCFG: \"Tcp/WithURCs\",(\"on\",\"off\")\r\n" "^SCFG: \"Trace/Syslog/OTAP\",(\"0\",\"1\"),(\"null\",\"asc0\",\"asc1\",\"usb\",\"usb1\",\"usb2\",\"usb3\",\"usb4\",\"usb5\",\"file\",\"udp\",\"system\"),(\"1\"-\"65535\"),(125),(\"buffered\",\"secure\"),(\"off\",\"on\")\r\n" "^SCFG: \"URC/Ringline\",(\"off\",\"local\",\"asc0\")\r\n" "^SCFG: \"URC/Ringline/ActiveTime\",(\"0\",\"1\",\"2\")\r\n" "^SCFG: \"Userware/Autostart\",(\"0\",\"1\")\r\n" "^SCFG: \"Userware/Autostart/Delay\",(\"0\"-\"10000\")\r\n" "^SCFG: \"Userware/DebugInterface\",(\"0\"-\"255\")|(\"FE80::\"-\"FE80::FFFFFFFFFFFFFFFF\"),(\"0\"-\"255\")|(\"FE80::\"-\"FE80::FFFFFFFFFFFFFFFF\"),(\"0\",\"1\")\r\n" "^SCFG: \"Userware/DebugMode\",(\"off\",\"on\")\r\n" "^SCFG: \"Userware/Passwd\",(\"0\"-\"8\")\r\n" "^SCFG: \"Userware/Stdout\",(\"null\",\"asc0\",\"asc1\",\"usb\",\"usb1\",\"usb2\",\"usb3\",\"usb4\",\"usb5\",\"file\",\"udp\",\"system\"),(\"1\"-\"65535\"),(\"0\"-\"125\"),(\"buffered\",\"secure\"),(\"off\",\"on\")\r\n" "^SCFG: \"Userware/Watchdog\",(\"0\",\"1\",\"2\")\r\n"; expected_bands = g_array_sized_new (FALSE, FALSE, sizeof (MMModemBand), 4); single = MM_MODEM_BAND_EGSM, g_array_append_val (expected_bands, single); single = MM_MODEM_BAND_DCS, g_array_append_val (expected_bands, single); single = MM_MODEM_BAND_UTRAN_1, g_array_append_val (expected_bands, single); single = MM_MODEM_BAND_UTRAN_8, g_array_append_val (expected_bands, single); common_test_scfg (response, expected_bands, MM_MODEM_CHARSET_UNKNOWN, MM_CINTERION_MODEM_FAMILY_DEFAULT); g_array_unref (expected_bands); } static void test_scfg_pls62_gsm (void) { GArray *expected_bands; MMModemBand single; const gchar *response = "^SCFG: \"MEopMode/Prov/AutoSelect\",(\"off\",\"on\")\r\n" "^SCFG: \"MEopMode/Prov/Cfg\",(\"fallback\",\"attus\")\r\n" "^SCFG: \"Serial/Ifc\",(\"Current\",\"ASC0\",\"USB0\",\"USB1\",\"USB2\",\"MUX1\",\"MUX2\",\"MUX3\",\"0\"),(\"0\",\"3\"),(\"1200\",\"2400\",\"4800\",\"9600\",\"19200\",\"38400\",\"57600\",\"115200\",\"230400\",\"460800\",\"500000\",\"750000\",\"921600\"),(\"0)\r\n" "^SCFG: \"RemoteWakeUp/Ports\",(\"current\",\"powerup\"),(\"asc0\",\"acm1\",\"acm2\",\"acm3\",\"rmnet0\",\"rmnet1\")\r\n" "^SCFG: \"Gpio/mode/ASC1\",(\"std\",\"gpio\",\"rsv\")\r\n" "^SCFG: \"Gpio/mode/DCD0\",(\"std\",\"gpio\",\"rsv\")\r\n" "^SCFG: \"Gpio/mode/DSR0\",(\"std\",\"gpio\",\"rsv\")\r\n" "^SCFG: \"Gpio/mode/DTR0\",(\"std\",\"gpio\",\"rsv\")\r\n" "^SCFG: \"Gpio/mode/FSR\",(\"std\",\"gpio\",\"rsv\")\r\n" "^SCFG: \"Gpio/mode/PULSE\",(\"std\",\"gpio\",\"rsv\")\r\n" "^SCFG: \"Gpio/mode/PWM\",(\"std\",\"gpio\",\"rsv\")\r\n" "^SCFG: \"Gpio/mode/HWAKEUP\",(\"std\",\"gpio\",\"rsv\")\r\n" "^SCFG: \"Gpio/mode/RING0\",(\"std\",\"gpio\",\"rsv\")\r\n" "^SCFG: \"Gpio/mode/SPI\",(\"std\",\"gpio\",\"rsv\")\r\n" "^SCFG: \"Gpio/mode/SYNC\",(\"std\",\"gpio\",\"rsv\")\r\n" "^SCFG: \"GPRS/AutoAttach\",(\"disabled\",\"enabled\")\r\n" "^SCFG: \"Ident/Manufacturer\",(25)\r\n" "^SCFG: \"Ident/Product\",(25)\r\n" "^SCFG: \"MEopMode/SoR\",(\"off\",\"on\")\r\n" "^SCFG: \"MEopMode/CregRoam\",(\"0\",\"1\")\r\n" "^SCFG: \"MeOpMode/SRPOM\",(\"0\",\"1\")\r\n" "^SCFG: \"MEopMode/RingOnData\",(\"off\",\"on\")\r\n" "^SCFG: \"MEShutdown/Fso\",(\"0\",\"1\")\r\n" "^SCFG: \"MEShutdown/sVsup/threshold\",(\"-4\",\"-3\",\"-2\",\"-1\",\"0\",\"1\",\"2\",\"3\",\"4\"),(\"0\")\r\n" "^SCFG: \"Radio/Band/2G\",(\"0x00000004\"-\"0x00000074\")\r\n" "^SCFG: \"Radio/Band/3G\",(\"0x00000001\"-\"0x0004019B\")\r\n" "^SCFG: \"Radio/Band/4G\",(\"0x00000001\"-\"0x080E08DF\")\r\n" "^SCFG: \"Radio/Mtpl/2G\",(\"0\"-\"3\"),(\"1\"-\"8\"),(\"0x00000004\",\"0x00000010\",\"0x00000020\",\"0x00000040\"),,(\"18\"-\"33\"),(\"18\"-\"27\")\r\n" "^SCFG: \"Radio/Mtpl/3G\",(\"0\"-\"3\"),(\"1\"-\"8\"),(\"0x00000001\",\"0x00000002\",\"0x00000008\",\"0x00000010\",\"0x00000080\",\"0x00000100\",\"0x00040000\"),,(\"18\"-\"24\")\r\n" "^SCFG: \"Radio/Mtpl/4G\",(\"0\"-\"3\"),(\"1\"-\"8\"),(\"0x00000001\",\"0x00000002\",\"0x00000004\",\"0x00000008\",\"0x00000010\",\"0x00000040\",\"0x00000080\",\"0x00000800\",\"0x00020000\",\"0x00040000\",\"0x00080000\",\"0x08000000\"),,(\"18)\r\n" "^SCFG: \"Radio/OutputPowerReduction\",(\"0\",\"1\",\"2\",\"3\",\"4\")\r\n" "^SCFG: \"Serial/Interface/Allocation\",(\"0\",\"1\"),(\"0\",\"1\")\r\n" "^SCFG: \"Serial/USB/DDD\",(\"0\",\"1\"),(\"0\"),(4),(4),(4),(63),(63),(4)\r\n" "^SCFG: \"Tcp/IRT\",(\"1\"-\"60\")\r\n" "^SCFG: \"Tcp/MR\",(\"2\"-\"30\")\r\n" "^SCFG: \"Tcp/OT\",(\"1\"-\"6000\")\r\n" "^SCFG: \"Tcp/WithURCs\",(\"on\",\"off\")\r\n" "^SCFG: \"Trace/Syslog/OTAP\",(\"0\",\"1\"),(\"null\",\"asc0\",\"asc1\",\"usb\",\"usb1\",\"usb2\",\"file\",\"system\"),(\"1\"-\"65535\"),(125),(\"buffered\",\"secure\"),(\"off\",\"on\")\r\n" "^SCFG: \"Urc/Ringline\",(\"off\",\"local\",\"asc0\",\"wakeup\")\r\n" "^SCFG: \"Urc/Ringline/ActiveTime\",(\"0\",\"1\",\"2\")\r\n" "^SCFG: \"Userware/Autostart\",(\"0\",\"1\")\r\n" "^SCFG: \"Userware/Autostart/Delay\",(\"0\"-\"10000\")\r\n" "^SCFG: \"Userware/DebugInterface\",(\"0\"-\"255\")|(\"FE80::\"-\"FE80::FFFFFFFFFFFFFFFF\"),(\"0\"-\"255\")|(\"FE80::\"-\"FE80::FFFFFFFFFFFFFFFF\"),(\"0\",\"1\")\r\n" "^SCFG: \"Userware/DebugMode\",(\"off\",\"on\")\r\n" "^SCFG: \"Userware/Passwd\",(\"0\"-\"8\")\r\n" "^SCFG: \"Userware/Stdout\",(\"null\",\"asc0\",\"asc1\",\"usb\",\"usb1\",\"usb2\",\"file\",\"system\"),(\"1\"-\"65535\"),(\"0\"-\"125\"),(\"buffered\",\"secure\"),(\"off\",\"on\")\r\n" "^SCFG: \"Userware/Watchdog\",(\"0\",\"1\",\"2\")\r\n" "^SCFG: \"MEopMode/ExpectDTR\",(\"current\",\"powerup\"),(\"asc0\",\"acm1\",\"acm2\",\"acm3\")\r\n"; expected_bands = g_array_sized_new (FALSE, FALSE, sizeof (MMModemBand), 23); single = MM_MODEM_BAND_EGSM, g_array_append_val (expected_bands, single); single = MM_MODEM_BAND_DCS, g_array_append_val (expected_bands, single); single = MM_MODEM_BAND_PCS, g_array_append_val (expected_bands, single); single = MM_MODEM_BAND_G850, g_array_append_val (expected_bands, single); single = MM_MODEM_BAND_UTRAN_1, g_array_append_val (expected_bands, single); single = MM_MODEM_BAND_UTRAN_2, g_array_append_val (expected_bands, single); single = MM_MODEM_BAND_UTRAN_4, g_array_append_val (expected_bands, single); single = MM_MODEM_BAND_UTRAN_5, g_array_append_val (expected_bands, single); single = MM_MODEM_BAND_UTRAN_8, g_array_append_val (expected_bands, single); single = MM_MODEM_BAND_UTRAN_9, g_array_append_val (expected_bands, single); single = MM_MODEM_BAND_UTRAN_19, g_array_append_val (expected_bands, single); single = MM_MODEM_BAND_EUTRAN_1, g_array_append_val (expected_bands, single); single = MM_MODEM_BAND_EUTRAN_2, g_array_append_val (expected_bands, single); single = MM_MODEM_BAND_EUTRAN_3, g_array_append_val (expected_bands, single); single = MM_MODEM_BAND_EUTRAN_4, g_array_append_val (expected_bands, single); single = MM_MODEM_BAND_EUTRAN_5, g_array_append_val (expected_bands, single); single = MM_MODEM_BAND_EUTRAN_7, g_array_append_val (expected_bands, single); single = MM_MODEM_BAND_EUTRAN_8, g_array_append_val (expected_bands, single); single = MM_MODEM_BAND_EUTRAN_12, g_array_append_val (expected_bands, single); single = MM_MODEM_BAND_EUTRAN_18, g_array_append_val (expected_bands, single); single = MM_MODEM_BAND_EUTRAN_19, g_array_append_val (expected_bands, single); single = MM_MODEM_BAND_EUTRAN_20, g_array_append_val (expected_bands, single); single = MM_MODEM_BAND_EUTRAN_28, g_array_append_val (expected_bands, single); common_test_scfg (response, expected_bands, MM_MODEM_CHARSET_GSM, MM_CINTERION_MODEM_FAMILY_IMT); g_array_unref (expected_bands); } static void test_scfg_pls62_ucs2 (void) { GArray *expected_bands; MMModemBand single; const gchar *response = "^SCFG: \"MEopMode/Prov/AutoSelect\",(\"006F00660066\",\"006F006E\")\r\n" "^SCFG: \"MEopMode/Prov/Cfg\",(\"fallback\",\"attus\")\r\n" "^SCFG: \"Serial/Ifc\",(\"00430075007200720065006E0074\",\"0041005300430030\",\"0055005300420030\",\"0055005300420031\",\"0055005300420032\",\"004D005500580031\",\"004D005500580032\",\"004D005500580033\",\"0030\"),(\"0030\",\"0033)\r\n" "^SCFG: \"RemoteWakeUp/Ports\",(\"00630075007200720065006E0074\",\"0070006F00770065007200750070\"),(\"0061007300630030\",\"00610063006D0031\",\"00610063006D0032\",\"00610063006D0033\",\"0072006D006E006500740030\",\"0072006D0)\r\n" "^SCFG: \"Gpio/mode/ASC1\",(\"007300740064\",\"006700700069006F\",\"007200730076\")\r\n" "^SCFG: \"Gpio/mode/DCD0\",(\"007300740064\",\"006700700069006F\",\"007200730076\")\r\n" "^SCFG: \"Gpio/mode/DSR0\",(\"007300740064\",\"006700700069006F\",\"007200730076\")\r\n" "^SCFG: \"Gpio/mode/DTR0\",(\"007300740064\",\"006700700069006F\",\"007200730076\")\r\n" "^SCFG: \"Gpio/mode/FSR\",(\"007300740064\",\"006700700069006F\",\"007200730076\")\r\n" "^SCFG: \"Gpio/mode/PULSE\",(\"007300740064\",\"006700700069006F\",\"007200730076\")\r\n" "^SCFG: \"Gpio/mode/PWM\",(\"007300740064\",\"006700700069006F\",\"007200730076\")\r\n" "^SCFG: \"Gpio/mode/HWAKEUP\",(\"007300740064\",\"006700700069006F\",\"007200730076\")\r\n" "^SCFG: \"Gpio/mode/RING0\",(\"007300740064\",\"006700700069006F\",\"007200730076\")\r\n" "^SCFG: \"Gpio/mode/SPI\",(\"007300740064\",\"006700700069006F\",\"007200730076\")\r\n" "^SCFG: \"Gpio/mode/SYNC\",(\"007300740064\",\"006700700069006F\",\"007200730076\")\r\n" "^SCFG: \"GPRS/AutoAttach\",(\"00640069007300610062006C00650064\",\"0065006E00610062006C00650064\")\r\n" "^SCFG: \"Ident/Manufacturer\",(25)\r\n" "^SCFG: \"Ident/Product\",(25)\r\n" "^SCFG: \"MEopMode/SoR\",(\"006F00660066\",\"006F006E\")\r\n" "^SCFG: \"MEopMode/CregRoam\",(\"0030\",\"0031\")\r\n" "^SCFG: \"MeOpMode/SRPOM\",(\"0030\",\"0031\")\r\n" "^SCFG: \"MEopMode/RingOnData\",(\"006F00660066\",\"006F006E\")\r\n" "^SCFG: \"MEShutdown/Fso\",(\"0030\",\"0031\")\r\n" "^SCFG: \"MEShutdown/sVsup/threshold\",(\"002D0034\",\"002D0033\",\"002D0032\",\"002D0031\",\"0030\",\"0031\",\"0032\",\"0033\",\"0034\"),(\"0030\")\r\n" "^SCFG: \"Radio/Band/2G\",(\"0030007800300030003000300030003000300034\"-\"0030007800300030003000300030003000370034\")\r\n" "^SCFG: \"Radio/Band/3G\",(\"0030007800300030003000300030003000300031\"-\"0030007800300030003000340030003100390042\")\r\n" "^SCFG: \"Radio/Band/4G\",(\"0030007800300030003000300030003000300031\"-\"0030007800300038003000450030003800440046\")\r\n" "^SCFG: \"Radio/Mtpl/2G\",(\"00300022002D00220033\"),(\"00310022002D00220038\"),(\"00300078003000300030003000300030003000340022002C002200300078003000300030003000300030003100300022002C0022003000780030003000300030003)\r\n" "^SCFG: \"Radio/Mtpl/3G\",(\"00300022002D00220033\"),(\"00310022002D00220038\"),(\"00300078003000300030003000300030003000310022002C002200300078003000300030003000300030003000320022002C0022003000780030003000300030003)\r\n" "^SCFG: \"Radio/Mtpl/4G\",(\"00300022002D00220033\"),(\"00310022002D00220038\"),(\"00310022002D00220038\"),,(\"003100380022002D002200320033\")\r\n" "^SCFG: \"Radio/OutputPowerReduction\",(\"0030\",\"0031\",\"0032\",\"0033\",\"0034\")\r\n" "^SCFG: \"Serial/Interface/Allocation\",(\"0030\",\"0031\"),(\"0030\",\"0031\")\r\n" "^SCFG: \"Serial/USB/DDD\",(\"0030\",\"0031\"),(\"0030\"),(4),(4),(4),(63),(63),(4)\r\n" "^SCFG: \"Tcp/IRT\",(\"0031\"-\"00360030\")\r\n" "^SCFG: \"Tcp/MR\",(\"0032\"-\"00330030\")\r\n" "^SCFG: \"Tcp/OT\",(\"0031\"-\"0036003000300030\")\r\n" "^SCFG: \"Tcp/WithURCs\",(\"006F006E\",\"006F00660066\")\r\n" "^SCFG: \"Trace/Syslog/OTAP\",(\"0030\",\"0031\"),(\"006E0075006C006C\",\"0061007300630030\",\"0061007300630031\",\"007500730062\",\"0075007300620031\",\"0075007300620032\",\"00660069006C0065\",\"00730079007300740065006D\"),(\"003)\r\n" "^SCFG: \"Urc/Ringline\",(\"006F00660066\",\"006C006F00630061006C\",\"0061007300630030\",\"00770061006B006500750070\")\r\n" "^SCFG: \"Urc/Ringline/ActiveTime\",(\"0030\",\"0031\",\"0032\")\r\n" "^SCFG: \"Userware/Autostart\",(\"0030\",\"0031\")\r\n" "^SCFG: \"Userware/Autostart/Delay\",(\"00300022002D002200310030003000300030\")\r\n" "^SCFG: \"Userware/DebugInterface\",(\"0030\"-\"003200350035\")|(\"0046004500380030003A003A\"-\"0046004500380030003A003A0046004600460046004600460046004600460046004600460046004600460046\"),(\"0030\"-\"003200350035\")|(\"004)\r\n" "^SCFG: \"Userware/DebugMode\",(\"006F00660066\",\"006F006E\")\r\n" "^SCFG: \"Userware/Passwd\",(\"0030\"-\"0038\")\r\n" "^SCFG: \"Userware/Stdout\",(\"006E0075006C006C\",\"0061007300630030\",\"0061007300630031\",\"007500730062\",\"0075007300620031\",\"0075007300620032\",\"00660069006C0065\",\"00730079007300740065006D\"),(\"0031\"-\"00360035003500)\r\n" "^SCFG: \"Userware/Watchdog\",(\"0030\",\"0031\",\"0032\")\r\n" "^SCFG: \"MEopMode/ExpectDTR\",(\"00630075007200720065006E0074\",\"0070006F00770065007200750070\"),(\"0061007300630030\",\"00610063006D0031\",\"00610063006D0032\",\"00610063006D0033\")\r\n"; expected_bands = g_array_sized_new (FALSE, FALSE, sizeof (MMModemBand), 23); single = MM_MODEM_BAND_EGSM, g_array_append_val (expected_bands, single); single = MM_MODEM_BAND_DCS, g_array_append_val (expected_bands, single); single = MM_MODEM_BAND_PCS, g_array_append_val (expected_bands, single); single = MM_MODEM_BAND_G850, g_array_append_val (expected_bands, single); single = MM_MODEM_BAND_UTRAN_1, g_array_append_val (expected_bands, single); single = MM_MODEM_BAND_UTRAN_2, g_array_append_val (expected_bands, single); single = MM_MODEM_BAND_UTRAN_4, g_array_append_val (expected_bands, single); single = MM_MODEM_BAND_UTRAN_5, g_array_append_val (expected_bands, single); single = MM_MODEM_BAND_UTRAN_8, g_array_append_val (expected_bands, single); single = MM_MODEM_BAND_UTRAN_9, g_array_append_val (expected_bands, single); single = MM_MODEM_BAND_UTRAN_19, g_array_append_val (expected_bands, single); single = MM_MODEM_BAND_EUTRAN_1, g_array_append_val (expected_bands, single); single = MM_MODEM_BAND_EUTRAN_2, g_array_append_val (expected_bands, single); single = MM_MODEM_BAND_EUTRAN_3, g_array_append_val (expected_bands, single); single = MM_MODEM_BAND_EUTRAN_4, g_array_append_val (expected_bands, single); single = MM_MODEM_BAND_EUTRAN_5, g_array_append_val (expected_bands, single); single = MM_MODEM_BAND_EUTRAN_7, g_array_append_val (expected_bands, single); single = MM_MODEM_BAND_EUTRAN_8, g_array_append_val (expected_bands, single); single = MM_MODEM_BAND_EUTRAN_12, g_array_append_val (expected_bands, single); single = MM_MODEM_BAND_EUTRAN_18, g_array_append_val (expected_bands, single); single = MM_MODEM_BAND_EUTRAN_19, g_array_append_val (expected_bands, single); single = MM_MODEM_BAND_EUTRAN_20, g_array_append_val (expected_bands, single); single = MM_MODEM_BAND_EUTRAN_28, g_array_append_val (expected_bands, single); common_test_scfg (response, expected_bands, MM_MODEM_CHARSET_UCS2, MM_CINTERION_MODEM_FAMILY_IMT); g_array_unref (expected_bands); } static void test_scfg_alas5 (void) { GArray *expected_bands; MMModemBand single; const gchar *response = "^SCFG: \"Audio/Loop\",(\"0\",\"1\")\r\n" "^SCFG: \"Audio/SvTone\",(\"0-2047\")\r\n" "^SCFG: \"Call/Ecall/AckTimeout\",(\"0-60000\")\r\n" "^SCFG: \"Call/Ecall/BlockSMSPP\",(\"0\",\"1\")\r\n" "^SCFG: \"Call/Ecall/Callback\",(\"0\",\"1\")\r\n" "^SCFG: \"Call/Ecall/CallbackTimeout\",(\"0-86400000\")\r\n" "^SCFG: \"Call/Ecall/Force\",(\"0\",\"1\",\"2\")\r\n" "^SCFG: \"Call/Ecall/Msd\",(280)\r\n" "^SCFG: \"Call/Ecall/Pullmode\",(\"0\",\"1\")\r\n" "^SCFG: \"Call/Ecall/SessionTimeout\",(\"0-300000\")\r\n" "^SCFG: \"Call/Ecall/StartTimeout\",(\"0-600000\")\r\n" "^SCFG: \"Call/ECC\",(\"0\"-\"255\")\r\n" "^SCFG: \"Call/Speech/Codec\",(\"0\",\"2\")\r\n" "^SCFG: \"GPRS/Auth\",(\"0\",\"1\",\"2\")\r\n" "^SCFG: \"GPRS/AutoAttach\",(\"disabled\",\"enabled\")\r\n" "^SCFG: \"GPRS/MTU/Mode\",(\"0-1\")\r\n" "^SCFG: \"GPRS/MTU/Size\",(\"1280-4096\")\r\n" "^SCFG: \"MEopMode/CFUN\",(\"0\",\"1\")\r\n" "^SCFG: \"MEopMode/CregRoam\",(\"0\",\"1\")\r\n" "^SCFG: \"MEopMode/Dormancy\",(\"0\",\"1\",\"9\")\r\n" "^SCFG: \"MEopMode/DTM/Mode\",(\"0\",\"1\",\"2\")\r\n" "^SCFG: \"MEopMode/ExpectDTR\",(\"current\",\"powerup\"),(\"acm0\",\"acm1\",\"acm2\",\"acm3\",\"diag\",\"mbim\",\"asc0\")\r\n" "^SCFG: \"MEopMode/FGI/Split\",(\"0\",\"1\")\r\n" "^SCFG: \"MEopMode/IMS\",(\"0\",\"1\")\r\n" "^SCFG: \"MEopMode/NonBlock/Cops\",(\"0\",\"1\")\r\n" "^SCFG: \"MEopMode/PowerMgmt/LCI\",(\"disabled\",\"enabled\"),(\"GPIO1\",\"GPIO3\",\"GPIO4\",\"GPIO5\",\"GPIO6\",\"GPIO7\",\"GPIO8\",\"GPIO11\",\"GPIO12\",\"GPIO13\",\"GPIO14\",\"GPIO15\",\"GPIO16\",\"GPIO17\",\"GPIO22\")\r\n" "^SCFG: \"MEopMode/Prov/AutoFallback\",(\"on\",\"off\")\r\n" "^SCFG: \"MEopMode/Prov/AutoSelect\",(\"on\",\"off\")\r\n" "^SCFG: \"MEopMode/Prov/Cfg\",(\"vdfde\",\"tmode\",\"clarobr\",\"telenorno\",\"telenorse\",\"vdfpt\",\"fallb3gpp*\",\"vdfww\",\"vdfes\",\"swisscomch\",\"eeuk\",\"orangero\",\"orangees\",\"tefde\",\"telenordk\",\"timit\",\"tn1de\",\"tefes\",\"tels)\r\n" "^SCFG: \"MEopMode/PwrSave\",(\"disabled\",\"enabled\"),(\"0-36000\"),(\"0-36000\"),(\"CPU-A\",\"CPU-M\"),(\"powerup\",\"current\")\r\n" "^SCFG: \"MEopMode/SRPOM\",(\"0\",\"1\")\r\n" "^SCFG: \"MEopMode/USB/KeepData\",(\"current\",\"powerup\"),(\"acm0\",\"acm1\",\"acm2\",\"acm3\",\"diag\",\"mbim\",\"asc0\")\r\n" "^SCFG: \"MEShutdown/OnIgnition\",(\"on\",\"off\")\r\n" "^SCFG: \"MEShutdown/Timer\",(\"off\",\"0\"-\"525600\")\r\n" "^SCFG: \"Misc/CId\",(290)\r\n" "^SCFG: \"Radio/Band/2G\",(\"00000001-0000000f\"),,(\"0\",\"1\")\r\n" "^SCFG: \"Radio/Band/3G\",(\"00000001-000400b5\"),,(\"0\",\"1\")\r\n" "^SCFG: \"Radio/Band/4G\",(\"00000001-8a0e00d5\"),(\"00000002-000001e2\"),(\"0\",\"1\")\r\n" "^SCFG: \"Radio/CNS\",(\"0\",\"1\")\r\n" "^SCFG: \"Radio/Mtpl\",(\"0-1\"),(\"1-8\")\r\n" "^SCFG: \"Radio/Mtpl/2G\",(\"2-3\"),(\"1-8\"),(\"00000001-0000000f\"),,(\"18-33\"),(\"18-27\")\r\n" "^SCFG: \"Radio/Mtpl/3G\",(\"2-3\"),(\"1-8\"),(\"00000001-000000b5\"),,(\"18-24\")\r\n" "^SCFG: \"Radio/Mtpl/4G\",(\"2-3\"),(\"1-8\"),(\"00000001-8a0e00d5\"),(\"00000002-000000e2\"),(\"18-24\")\r\n" "^SCFG: \"Radio/OutputPowerReduction\",(\"4\"-\"8\")\r\n" "^SCFG: \"RemoteWakeUp/Event/ASC\",(\"none\",\"GPIO1\",\"GPIO3\",\"GPIO4\",\"GPIO5\",\"GPIO6\",\"GPIO7\",\"GPIO8\",\"GPIO11\",\"GPIO12\",\"GPIO13\",\"GPIO14\",\"GPIO15\",\"GPIO16\",\"GPIO17\",\"GPIO22\")\r\n" "^SCFG: \"RemoteWakeUp/Event/URC\",(\"none\",\"GPIO1\",\"GPIO3\",\"GPIO4\",\"GPIO5\",\"GPIO6\",\"GPIO7\",\"GPIO8\",\"GPIO11\",\"GPIO12\",\"GPIO13\",\"GPIO14\",\"GPIO15\",\"GPIO16\",\"GPIO17\",\"GPIO22\")\r\n" "^SCFG: \"RemoteWakeUp/Event/USB\",(\"none\",\"GPIO1\",\"GPIO3\",\"GPIO4\",\"GPIO5\",\"GPIO6\",\"GPIO7\",\"GPIO8\",\"GPIO11\",\"GPIO12\",\"GPIO13\",\"GPIO14\",\"GPIO15\",\"GPIO16\",\"GPIO17\",\"GPIO22\")\r\n" "^SCFG: \"RemoteWakeUp/Ports\",(\"current\",\"powerup\"),(\"acm0\",\"acm1\",\"acm2\",\"acm3\",\"diag\",\"mbim\",\"asc0\")\r\n" "^SCFG: \"RemoteWakeUp/Pulse\",(\"1\"-\"100\")\r\n" "^SCFG: \"Serial/USB/DDD\",(\"0-1\"),(\"0\"),(\"0001-ffff\"),(\"0000-ffff\"),(\"0000-ffff\"),(63),(63),(4)\r\n" "^SCFG: \"SIM/CS\",(\"NOSIM\",\"SIM1\",\"SIM2\")\r\n" "^SCFG: \"SMS/4GPREF\",(\"IMS\",\"CSPS\")\r\n" "^SCFG: \"SMS/AutoAck\",(\"0\",\"1\")\r\n" "^SCFG: \"SMS/RETRM\",(\"1-45\")\r\n" "^SCFG: \"URC/Ringline\",(\"off\",\"local\",\"asc0\")\r\n" "^SCFG: \"URC/Ringline/ActiveTime\",(\"2\",\"on\",\"off\")\r\n"; expected_bands = g_array_sized_new (FALSE, FALSE, sizeof (MMModemBand), 23); single = MM_MODEM_BAND_EGSM, g_array_append_val (expected_bands, single); single = MM_MODEM_BAND_DCS, g_array_append_val (expected_bands, single); single = MM_MODEM_BAND_PCS, g_array_append_val (expected_bands, single); single = MM_MODEM_BAND_G850, g_array_append_val (expected_bands, single); single = MM_MODEM_BAND_UTRAN_1, g_array_append_val (expected_bands, single); single = MM_MODEM_BAND_UTRAN_3, g_array_append_val (expected_bands, single); // single = MM_MODEM_BAND_UTRAN_5, g_array_append_val (expected_bands, single); single = MM_MODEM_BAND_UTRAN_6, g_array_append_val (expected_bands, single); // single = MM_MODEM_BAND_UTRAN_8, g_array_append_val (expected_bands, single); single = MM_MODEM_BAND_UTRAN_19, g_array_append_val (expected_bands, single); single = MM_MODEM_BAND_EUTRAN_1, g_array_append_val (expected_bands, single); single = MM_MODEM_BAND_EUTRAN_3, g_array_append_val (expected_bands, single); single = MM_MODEM_BAND_EUTRAN_5, g_array_append_val (expected_bands, single); single = MM_MODEM_BAND_EUTRAN_7, g_array_append_val (expected_bands, single); single = MM_MODEM_BAND_EUTRAN_8, g_array_append_val (expected_bands, single); single = MM_MODEM_BAND_EUTRAN_18, g_array_append_val (expected_bands, single); single = MM_MODEM_BAND_EUTRAN_19, g_array_append_val (expected_bands, single); single = MM_MODEM_BAND_EUTRAN_20, g_array_append_val (expected_bands, single); single = MM_MODEM_BAND_EUTRAN_26, g_array_append_val (expected_bands, single); single = MM_MODEM_BAND_EUTRAN_28, g_array_append_val (expected_bands, single); single = MM_MODEM_BAND_EUTRAN_38, g_array_append_val (expected_bands, single); single = MM_MODEM_BAND_EUTRAN_39, g_array_append_val (expected_bands, single); single = MM_MODEM_BAND_EUTRAN_40, g_array_append_val (expected_bands, single); single = MM_MODEM_BAND_EUTRAN_41, g_array_append_val (expected_bands, single); common_test_scfg (response, expected_bands, MM_MODEM_CHARSET_GSM, MM_CINTERION_MODEM_FAMILY_DEFAULT); g_array_unref (expected_bands); } /*****************************************************************************/ /* Test ^SCFG responses */ static void common_test_scfg_response (const gchar *response, MMModemCharset charset, GArray *expected_bands, MMCinterionModemFamily modem_family, MMCinterionRadioBandFormat rbf) { GArray *bands = NULL; gchar *expected_bands_str; gchar *bands_str; GError *error = NULL; gboolean res; res = mm_cinterion_parse_scfg_response (response, modem_family, charset, &bands, rbf, &error); g_assert_no_error (error); g_assert (res == TRUE); g_assert (bands != NULL); mm_common_bands_garray_sort (bands); mm_common_bands_garray_sort (expected_bands); expected_bands_str = mm_common_build_bands_string ((const MMModemBand *)(gconstpointer)expected_bands->data, expected_bands->len); bands_str = mm_common_build_bands_string ((const MMModemBand *)(gconstpointer)bands->data, bands->len); /* Instead of comparing the array one by one, compare the strings built from the mask * (we get a nicer error if it fails) */ g_assert_cmpstr (bands_str, ==, expected_bands_str); g_free (bands_str); g_free (expected_bands_str); g_array_unref (bands); } static void test_scfg_response_2g (void) { GArray *expected_bands; MMModemBand single; const gchar *response = "^SCFG: \"Radio/Band\",\"3\",\"3\"\r\n" "\r\n"; expected_bands = g_array_sized_new (FALSE, FALSE, sizeof (MMModemBand), 9); single = MM_MODEM_BAND_EGSM, g_array_append_val (expected_bands, single); single = MM_MODEM_BAND_DCS, g_array_append_val (expected_bands, single); common_test_scfg_response (response, MM_MODEM_CHARSET_UNKNOWN, expected_bands, MM_CINTERION_MODEM_FAMILY_DEFAULT, MM_CINTERION_RADIO_BAND_FORMAT_SINGLE); g_array_unref (expected_bands); } static void test_scfg_response_3g (void) { GArray *expected_bands; MMModemBand single; const gchar *response = "^SCFG: \"Radio/Band\",127\r\n" "\r\n"; expected_bands = g_array_sized_new (FALSE, FALSE, sizeof (MMModemBand), 9); single = MM_MODEM_BAND_EGSM, g_array_append_val (expected_bands, single); single = MM_MODEM_BAND_DCS, g_array_append_val (expected_bands, single); single = MM_MODEM_BAND_PCS, g_array_append_val (expected_bands, single); single = MM_MODEM_BAND_G850, g_array_append_val (expected_bands, single); single = MM_MODEM_BAND_UTRAN_1, g_array_append_val (expected_bands, single); single = MM_MODEM_BAND_UTRAN_2, g_array_append_val (expected_bands, single); single = MM_MODEM_BAND_UTRAN_5, g_array_append_val (expected_bands, single); common_test_scfg_response (response, MM_MODEM_CHARSET_UNKNOWN, expected_bands, MM_CINTERION_MODEM_FAMILY_DEFAULT, MM_CINTERION_RADIO_BAND_FORMAT_SINGLE); g_array_unref (expected_bands); } static void test_scfg_response_pls62_gsm (void) { GArray *expected_bands; MMModemBand single; const gchar *response = "^SCFG: \"MEopMode/Prov/AutoSelect\",\"off\"\r\n" "^SCFG: \"MEopMode/Prov/Cfg\",\"attus\"\r\n" "^SCFG: \"Serial/Ifc\",\"0\"\r\n" "^SCFG: \"RemoteWakeUp/Ports\",\"current\"\r\n" "^SCFG: \"RemoteWakeUp/Ports\",\"powerup\"\r\n" "^SCFG: \"Gpio/mode/ASC1\",\"gpio\"\r\n" "^SCFG: \"Gpio/mode/DCD0\",\"gpio\"\r\n" "^SCFG: \"Gpio/mode/DSR0\",\"gpio\"\r\n" "^SCFG: \"Gpio/mode/DTR0\",\"gpio\"\r\n" "^SCFG: \"Gpio/mode/FSR\",\"gpio\"\r\n" "^SCFG: \"Gpio/mode/PULSE\",\"gpio\"\r\n" "^SCFG: \"Gpio/mode/PWM\",\"gpio\"\r\n" "^SCFG: \"Gpio/mode/HWAKEUP\",\"gpio\"\r\n" "^SCFG: \"Gpio/mode/RING0\",\"gpio\"\r\n" "^SCFG: \"Gpio/mode/SPI\",\"gpio\"\r\n" "^SCFG: \"Gpio/mode/SYNC\",\"gpio\"\r\n" "^SCFG: \"GPRS/AutoAttach\",\"enabled\"\r\n" "^SCFG: \"Ident/Manufacturer\",\"Cinterion\"\r\n" "^SCFG: \"Ident/Product\",\"PLS62-W\"\r\n" "^SCFG: \"MEopMode/SoR\",\"off\"\r\n" "^SCFG: \"MEopMode/CregRoam\",\"0\"\r\n" "^SCFG: \"MeOpMode/SRPOM\",\"0\"\r\n" "^SCFG: \"MEopMode/RingOnData\",\"off\"\r\n" "^SCFG: \"MEShutdown/Fso\",\"0\"\r\n" "^SCFG: \"MEShutdown/sVsup/threshold\",\"0\",\"0\"\r\n" "^SCFG: \"Radio/Band/2G\",\"0x00000014\"\r\n" "^SCFG: \"Radio/Band/3G\",\"0x00000182\"\r\n" "^SCFG: \"Radio/Band/4G\",\"0x080E0000\"\r\n" "^SCFG: \"Radio/Mtpl/2G\",\"0\"\r\n" "^SCFG: \"Radio/Mtpl/3G\",\"0\"\r\n" "^SCFG: \"Radio/Mtpl/4G\",\"0\"\r\n" "^SCFG: \"Radio/OutputPowerReduction\",\"4\"\r\n" "^SCFG: \"Serial/Interface/Allocation\",\"0\",\"0\"\r\n" "^SCFG: \"Serial/USB/DDD\",\"0\",\"0\",\"0409\",\"1E2D\",\"005B\",\"Cinterion Wireless Modules\",\"PLSx\",\"\"\r\n" "^SCFG: \"Tcp/IRT\",\"3\"\r\n" "^SCFG: \"Tcp/MR\",\"10\"\r\n" "^SCFG: \"Tcp/OT\",\"6000\"\r\n" "^SCFG: \"Tcp/WithURCs\",\"on\"\r\n" "^SCFG: \"Trace/Syslog/OTAP\",\"0\"\r\n" "^SCFG: \"Urc/Ringline\",\"local\"\r\n" "^SCFG: \"Urc/Ringline/ActiveTime\",\"2\"\r\n" "^SCFG: \"Userware/Autostart\",\"0\"\r\n" "^SCFG: \"Userware/Autostart/Delay\",\"0\"\r\n" "^SCFG: \"Userware/DebugInterface\",\"0.0.0.0\",\"0.0.0.0\",\"0\"\r\n" "^SCFG: \"Userware/DebugMode\",\"off\"\r\n" "^SCFG: \"Userware/Passwd\",\r\n" "^SCFG: \"Userware/Stdout\",\"null\",,,,\"off\"\r\n" "^SCFG: \"Userware/Watchdog\",\"0\"\r\n" "^SCFG: \"MEopMode/ExpectDTR\",\"current\"\r\n" "^SCFG: \"MEopMode/ExpectDTR\",\"powerup\"\r\n"; expected_bands = g_array_sized_new (FALSE, FALSE, sizeof (MMModemBand), 9); single = MM_MODEM_BAND_EGSM, g_array_append_val (expected_bands, single); single = MM_MODEM_BAND_DCS, g_array_append_val (expected_bands, single); single = MM_MODEM_BAND_UTRAN_2, g_array_append_val (expected_bands, single); single = MM_MODEM_BAND_UTRAN_8, g_array_append_val (expected_bands, single); single = MM_MODEM_BAND_UTRAN_9, g_array_append_val (expected_bands, single); single = MM_MODEM_BAND_EUTRAN_18, g_array_append_val (expected_bands, single); single = MM_MODEM_BAND_EUTRAN_19, g_array_append_val (expected_bands, single); single = MM_MODEM_BAND_EUTRAN_20, g_array_append_val (expected_bands, single); single = MM_MODEM_BAND_EUTRAN_28, g_array_append_val (expected_bands, single); common_test_scfg_response (response, MM_MODEM_CHARSET_GSM, expected_bands, MM_CINTERION_MODEM_FAMILY_IMT, MM_CINTERION_RADIO_BAND_FORMAT_MULTIPLE); g_array_unref (expected_bands); } static void test_scfg_response_pls62_ucs2 (void) { GArray *expected_bands; MMModemBand single; const gchar *response = "^SCFG: \"MEopMode/Prov/AutoSelect\",\"006F00660066\"\r\n" "^SCFG: \"MEopMode/Prov/Cfg\",\"00610074007400750073\"\r\n" "^SCFG: \"Serial/Ifc\",\"0\"\r\n" "^SCFG: \"RemoteWakeUp/Ports\",\"00630075007200720065006E0074\"\r\n" "^SCFG: \"RemoteWakeUp/Ports\",\"0070006F00770065007200750070\"\r\n" "^SCFG: \"Gpio/mode/ASC1\",\"006700700069006F\"\r\n" "^SCFG: \"Gpio/mode/DCD0\",\"006700700069006F\"\r\n" "^SCFG: \"Gpio/mode/DSR0\",\"006700700069006F\"\r\n" "^SCFG: \"Gpio/mode/DTR0\",\"006700700069006F\"\r\n" "^SCFG: \"Gpio/mode/FSR\",\"006700700069006F\"\r\n" "^SCFG: \"Gpio/mode/PULSE\",\"006700700069006F\"\r\n" "^SCFG: \"Gpio/mode/PWM\",\"006700700069006F\"\r\n" "^SCFG: \"Gpio/mode/HWAKEUP\",\"006700700069006F\"\r\n" "^SCFG: \"Gpio/mode/RING0\",\"006700700069006F\"\r\n" "^SCFG: \"Gpio/mode/SPI\",\"006700700069006F\"\r\n" "^SCFG: \"Gpio/mode/SYNC\",\"006700700069006F\"\r\n" "^SCFG: \"GPRS/AutoAttach\",\"0065006E00610062006C00650064\"\r\n" "^SCFG: \"Ident/Manufacturer\",\"Cinterion\"\r\n" "^SCFG: \"Ident/Product\",\"PLS62-W\"\r\n" "^SCFG: \"MEopMode/SoR\",\"006F00660066\"\r\n" "^SCFG: \"MEopMode/CregRoam\",\"0030\"\r\n" "^SCFG: \"MeOpMode/SRPOM\",\"0030\"\r\n" "^SCFG: \"MEopMode/RingOnData\",\"006F00660066\"\r\n" "^SCFG: \"MEShutdown/Fso\",\"0030\"\r\n" "^SCFG: \"MEShutdown/sVsup/threshold\",\"0030\",\"0030\"\r\n" "^SCFG: \"Radio/Band/2G\",\"0030007800300030003000300030003000310034\"\r\n" "^SCFG: \"Radio/Band/3G\",\"0030007800300030003000300030003100380032\"\r\n" "^SCFG: \"Radio/Band/4G\",\"0030007800300038003000450030003000300030\"\r\n" "^SCFG: \"Radio/Mtpl/2G\",\"0030\"\r\n" "^SCFG: \"Radio/Mtpl/3G\",\"0030\"\r\n" "^SCFG: \"Radio/Mtpl/4G\",\"0030\"\r\n" "^SCFG: \"Radio/OutputPowerReduction\",\"0034\"\r\n" "^SCFG: \"Serial/Interface/Allocation\",\"0030\",\"0030\"\r\n" "^SCFG: \"Serial/USB/DDD\",\"0030\",\"0030\",\"0030003400300039\",\"0031004500320044\",\"0030003000350042\",\"00430069006E0074006500720069006F006E00200057006900720065006C0065007300730020004D006F00640075006C00650073\",\"005\"\r\n" "^SCFG: \"Tcp/IRT\",\"0033\"\r\n" "^SCFG: \"Tcp/MR\",\"00310030\"\r\n" "^SCFG: \"Tcp/OT\",\"0036003000300030\"\r\n" "^SCFG: \"Tcp/WithURCs\",\"006F006E\"\r\n" "^SCFG: \"Trace/Syslog/OTAP\",\"0030\"\r\n" "^SCFG: \"Urc/Ringline\",\"006C006F00630061006C\"\r\n" "^SCFG: \"Urc/Ringline/ActiveTime\",\"0032\"\r\n" "^SCFG: \"Userware/Autostart\",\"0030\"\r\n" "^SCFG: \"Userware/Autostart/Delay\",\"0030\"\r\n" "^SCFG: \"Userware/DebugInterface\",\"0030002E0030002E0030002E0030\",\"0030002E0030002E0030002E0030\",\"0030\"\r\n" "^SCFG: \"Userware/DebugMode\",\"006F00660066\"\r\n" "^SCFG: \"Userware/Passwd\",\r\n" "^SCFG: \"Userware/Stdout\",\"006E0075006C006C\",,,,\"006F00660066\"\r\n" "^SCFG: \"Userware/Watchdog\",\"0030\"\r\n" "^SCFG: \"MEopMode/ExpectDTR\",\"00630075007200720065006E0074\"\r\n" "^SCFG: \"MEopMode/ExpectDTR\",\"0070006F00770065007200750070\"\r\n"; expected_bands = g_array_sized_new (FALSE, FALSE, sizeof (MMModemBand), 9); single = MM_MODEM_BAND_EGSM, g_array_append_val (expected_bands, single); single = MM_MODEM_BAND_DCS, g_array_append_val (expected_bands, single); single = MM_MODEM_BAND_UTRAN_2, g_array_append_val (expected_bands, single); single = MM_MODEM_BAND_UTRAN_8, g_array_append_val (expected_bands, single); single = MM_MODEM_BAND_UTRAN_9, g_array_append_val (expected_bands, single); single = MM_MODEM_BAND_EUTRAN_18, g_array_append_val (expected_bands, single); single = MM_MODEM_BAND_EUTRAN_19, g_array_append_val (expected_bands, single); single = MM_MODEM_BAND_EUTRAN_20, g_array_append_val (expected_bands, single); single = MM_MODEM_BAND_EUTRAN_28, g_array_append_val (expected_bands, single); common_test_scfg_response (response, MM_MODEM_CHARSET_UCS2, expected_bands, MM_CINTERION_MODEM_FAMILY_IMT, MM_CINTERION_RADIO_BAND_FORMAT_MULTIPLE); g_array_unref (expected_bands); } static void test_scfg_response_alas5 (void) { GArray *expected_bands; MMModemBand single; const gchar *response = "^SCFG: \"Audio/Loop\",\"0\"\r\n" "^SCFG: \"Audio/SvTone\",\"0\"\r\n" "^SCFG: \"Call/Ecall/AckTimeout\",\"5000\"\r\n" "^SCFG: \"Call/Ecall/BlockSMSPP\",\"0\"\r\n" "^SCFG: \"Call/Ecall/Callback\",\"0\"\r\n" "^SCFG: \"Call/Ecall/CallbackTimeout\",\"43200000\"\r\n" "^SCFG: \"Call/Ecall/Force\",\"1\"\r\n" "^SCFG: \"Call/Ecall/Msd\",\"\"\r\n" "^SCFG: \"Call/Ecall/Pullmode\",\"0\"\r\n" "^SCFG: \"Call/Ecall/SessionTimeout\",\"20000\"\r\n" "^SCFG: \"Call/Ecall/StartTimeout\",\"5000\"\r\n" "^SCFG: \"Call/ECC\",\"0\"\r\n" "^SCFG: \"Call/Speech/Codec\",\"0\"\r\n" "^SCFG: \"GPRS/Auth\",\"2\"\r\n" "^SCFG: \"GPRS/AutoAttach\",\"enabled\"\r\n" "^SCFG: \"GPRS/MTU/Mode\",\"0\"\r\n" "^SCFG: \"GPRS/MTU/Size\",1500\r\n" "^SCFG: \"MEopMode/CFUN\",\"1\",\"1\"\r\n" "^SCFG: \"MEopMode/CregRoam\",\"0\"\r\n" "^SCFG: \"MEopMode/Dormancy\",\"0\",\"0\"\r\n" "^SCFG: \"MEopMode/DTM/Mode\",\"2\"\r\n" "^SCFG: \"MEopMode/ExpectDTR\",\"current\",\"acm0\",\"acm1\",\"acm2\",\"acm3\",\"mbim\",\"asc0\"\r\n" "^SCFG: \"MEopMode/ExpectDTR\",\"powerup\",\"acm0\",\"acm1\",\"acm2\",\"acm3\",\"mbim\",\"asc0\"\r\n" "^SCFG: \"MEopMode/FGI/Split\",\"1\"\r\n" "^SCFG: \"MEopMode/IMS\",\"1\"\r\n" "^SCFG: \"MEopMode/NonBlock/Cops\",\"0\"\r\n" "^SCFG: \"MEopMode/PowerMgmt/LCI\",\"disabled\"\r\n" "^SCFG: \"MEopMode/Prov/AutoFallback\",\"off\"\r\n" "^SCFG: \"MEopMode/Prov/AutoSelect\",\"on\"\r\n" "^SCFG: \"MEopMode/Prov/Cfg\",\"vdfde\"\r\n" "^SCFG: \"MEopMode/PwrSave\",\"enabled\",\"52\",\"50\",\"CPU-A\",\"powerup\"\r\n" "^SCFG: \"MEopMode/PwrSave\",\"enabled\",\"52\",\"50\",\"CPU-A\",\"current\"\r\n" "^SCFG: \"MEopMode/PwrSave\",\"enabled\",\"0\",\"0\",\"CPU-M\",\"powerup\"\r\n" "^SCFG: \"MEopMode/PwrSave\",\"enabled\",\"0\",\"0\",\"CPU-M\",\"current\"\r\n" "^SCFG: \"MEopMode/SRPOM\",\"0\"\r\n" "^SCFG: \"MEopMode/USB/KeepData\",\"current\",\"acm0\",\"acm1\",\"acm2\",\"acm3\",\"diag\",\"mbim\",\"asc0\"\r\n" "^SCFG: \"MEopMode/USB/KeepData\",\"powerup\",\"acm0\",\"acm1\",\"acm2\",\"acm3\",\"diag\",\"mbim\",\"asc0\"\r\n" "^SCFG: \"MEShutdown/OnIgnition\",\"off\"\r\n" "^SCFG: \"MEShutdown/Timer\",\"off\"\r\n" "^SCFG: \"Misc/CId\",\"\"\r\n" "^SCFG: \"Radio/Band/2G\",\"0000000f\"\r\n" "^SCFG: \"Radio/Band/3G\",\"000400b5\"\r\n" "^SCFG: \"Radio/Band/4G\",\"8a0e00d5\",\"000000e2\"\r\n" "^SCFG: \"Radio/CNS\",\"0\"\r\n" "^SCFG: \"Radio/Mtpl\",\"0\"\r\n" "^SCFG: \"Radio/Mtpl/2G\",\"0\"\r\n" "^SCFG: \"Radio/Mtpl/3G\",\"0\"\r\n" "^SCFG: \"Radio/Mtpl/4G\",\"0\"\r\n" "^SCFG: \"Radio/OutputPowerReduction\",\"4\"\r\n" "^SCFG: \"RemoteWakeUp/Event/ASC\",\"none\"\r\n" "^SCFG: \"RemoteWakeUp/Event/URC\",\"none\"\r\n" "^SCFG: \"RemoteWakeUp/Event/USB\",\"GPIO4\"\r\n" "^SCFG: \"RemoteWakeUp/Ports\",\"current\",\"acm0\",\"acm1\",\"acm2\",\"acm3\",\"diag\",\"mbim\",\"asc0\"\r\n" "^SCFG: \"RemoteWakeUp/Ports\",\"powerup\",\"acm0\",\"acm1\",\"acm2\",\"acm3\",\"diag\",\"mbim\",\"asc0\"\r\n" "^SCFG: \"RemoteWakeUp/Pulse\",\"10\"\r\n" "^SCFG: \"Serial/USB/DDD\",\"0\",\"0\",\"0409\",\"1e2d\",\"0065\",\"Cinterion\",\"LTE Modem\",\"8d8f\"\r\n" "^SCFG: \"SIM/CS\",\"SIM1\"\r\n" "^SCFG: \"SMS/4GPREF\",\"IMS\"\r\n" "^SCFG: \"SMS/AutoAck\",\"0\"\r\n" "^SCFG: \"SMS/RETRM\",\"30\"\r\n" "^SCFG: \"URC/Ringline\",\"local\"\r\n" "^SCFG: \"URC/Ringline/ActiveTime\",\"2\"\r\n"; expected_bands = g_array_sized_new (FALSE, FALSE, sizeof (MMModemBand), 25); single = MM_MODEM_BAND_EGSM, g_array_append_val (expected_bands, single); single = MM_MODEM_BAND_DCS, g_array_append_val (expected_bands, single); single = MM_MODEM_BAND_PCS, g_array_append_val (expected_bands, single); single = MM_MODEM_BAND_G850, g_array_append_val (expected_bands, single); single = MM_MODEM_BAND_UTRAN_1, g_array_append_val (expected_bands, single); single = MM_MODEM_BAND_UTRAN_3, g_array_append_val (expected_bands, single); // single = MM_MODEM_BAND_UTRAN_5, g_array_append_val (expected_bands, single); single = MM_MODEM_BAND_UTRAN_6, g_array_append_val (expected_bands, single); // single = MM_MODEM_BAND_UTRAN_8, g_array_append_val (expected_bands, single); single = MM_MODEM_BAND_UTRAN_19, g_array_append_val (expected_bands, single); single = MM_MODEM_BAND_EUTRAN_1, g_array_append_val (expected_bands, single); single = MM_MODEM_BAND_EUTRAN_3, g_array_append_val (expected_bands, single); single = MM_MODEM_BAND_EUTRAN_5, g_array_append_val (expected_bands, single); single = MM_MODEM_BAND_EUTRAN_7, g_array_append_val (expected_bands, single); single = MM_MODEM_BAND_EUTRAN_8, g_array_append_val (expected_bands, single); single = MM_MODEM_BAND_EUTRAN_18, g_array_append_val (expected_bands, single); single = MM_MODEM_BAND_EUTRAN_19, g_array_append_val (expected_bands, single); single = MM_MODEM_BAND_EUTRAN_20, g_array_append_val (expected_bands, single); single = MM_MODEM_BAND_EUTRAN_26, g_array_append_val (expected_bands, single); single = MM_MODEM_BAND_EUTRAN_28, g_array_append_val (expected_bands, single); single = MM_MODEM_BAND_EUTRAN_38, g_array_append_val (expected_bands, single); single = MM_MODEM_BAND_EUTRAN_39, g_array_append_val (expected_bands, single); single = MM_MODEM_BAND_EUTRAN_40, g_array_append_val (expected_bands, single); common_test_scfg_response (response, MM_MODEM_CHARSET_GSM, expected_bands, MM_CINTERION_MODEM_FAMILY_DEFAULT, MM_CINTERION_RADIO_BAND_FORMAT_MULTIPLE); g_array_unref (expected_bands); } /*****************************************************************************/ /* Test ^SCFG test */ static void compare_arrays (const GArray *supported, const GArray *expected) { guint i; g_assert_cmpuint (supported->len, ==, expected->len); for (i = 0; i < supported->len; i++) { gboolean found = FALSE; guint j; for (j = 0; j < expected->len && !found; j++) { if (g_array_index (supported, guint, i) == g_array_index (expected, guint, j)) found = TRUE; } g_assert (found); } } static void common_test_cnmi (const gchar *response, const GArray *expected_mode, const GArray *expected_mt, const GArray *expected_bm, const GArray *expected_ds, const GArray *expected_bfr) { GArray *supported_mode = NULL; GArray *supported_mt = NULL; GArray *supported_bm = NULL; GArray *supported_ds = NULL; GArray *supported_bfr = NULL; GError *error = NULL; gboolean res; g_assert (expected_mode != NULL); g_assert (expected_mt != NULL); g_assert (expected_bm != NULL); g_assert (expected_ds != NULL); g_assert (expected_bfr != NULL); res = mm_cinterion_parse_cnmi_test (response, &supported_mode, &supported_mt, &supported_bm, &supported_ds, &supported_bfr, &error); g_assert_no_error (error); g_assert (res == TRUE); g_assert (supported_mode != NULL); g_assert (supported_mt != NULL); g_assert (supported_bm != NULL); g_assert (supported_ds != NULL); g_assert (supported_bfr != NULL); compare_arrays (supported_mode, expected_mode); compare_arrays (supported_mt, expected_mt); compare_arrays (supported_bm, expected_bm); compare_arrays (supported_ds, expected_ds); compare_arrays (supported_bfr, expected_bfr); g_array_unref (supported_mode); g_array_unref (supported_mt); g_array_unref (supported_bm); g_array_unref (supported_ds); g_array_unref (supported_bfr); } static void test_cnmi_phs8 (void) { GArray *expected_mode; GArray *expected_mt; GArray *expected_bm; GArray *expected_ds; GArray *expected_bfr; guint val; const gchar *response = "+CNMI: (0,1,2),(0,1),(0,2),(0),(1)\r\n" "\r\n"; expected_mode = g_array_sized_new (FALSE, FALSE, sizeof (guint), 3); val = 0, g_array_append_val (expected_mode, val); val = 1, g_array_append_val (expected_mode, val); val = 2, g_array_append_val (expected_mode, val); expected_mt = g_array_sized_new (FALSE, FALSE, sizeof (guint), 2); val = 0, g_array_append_val (expected_mt, val); val = 1, g_array_append_val (expected_mt, val); expected_bm = g_array_sized_new (FALSE, FALSE, sizeof (guint), 2); val = 0, g_array_append_val (expected_bm, val); val = 2, g_array_append_val (expected_bm, val); expected_ds = g_array_sized_new (FALSE, FALSE, sizeof (guint), 1); val = 0, g_array_append_val (expected_ds, val); expected_bfr = g_array_sized_new (FALSE, FALSE, sizeof (guint), 1); val = 1, g_array_append_val (expected_bfr, val); common_test_cnmi (response, expected_mode, expected_mt, expected_bm, expected_ds, expected_bfr); g_array_unref (expected_mode); g_array_unref (expected_mt); g_array_unref (expected_bm); g_array_unref (expected_ds); g_array_unref (expected_bfr); } static void test_cnmi_other (void) { GArray *expected_mode; GArray *expected_mt; GArray *expected_bm; GArray *expected_ds; GArray *expected_bfr; guint val; const gchar *response = "+CNMI: (0-3),(0,1),(0,2,3),(0,2),(1)\r\n" "\r\n"; expected_mode = g_array_sized_new (FALSE, FALSE, sizeof (guint), 3); val = 0, g_array_append_val (expected_mode, val); val = 1, g_array_append_val (expected_mode, val); val = 2, g_array_append_val (expected_mode, val); val = 3, g_array_append_val (expected_mode, val); expected_mt = g_array_sized_new (FALSE, FALSE, sizeof (guint), 2); val = 0, g_array_append_val (expected_mt, val); val = 1, g_array_append_val (expected_mt, val); expected_bm = g_array_sized_new (FALSE, FALSE, sizeof (guint), 2); val = 0, g_array_append_val (expected_bm, val); val = 2, g_array_append_val (expected_bm, val); val = 3, g_array_append_val (expected_bm, val); expected_ds = g_array_sized_new (FALSE, FALSE, sizeof (guint), 1); val = 0, g_array_append_val (expected_ds, val); val = 2, g_array_append_val (expected_ds, val); expected_bfr = g_array_sized_new (FALSE, FALSE, sizeof (guint), 1); val = 1, g_array_append_val (expected_bfr, val); common_test_cnmi (response, expected_mode, expected_mt, expected_bm, expected_ds, expected_bfr); g_array_unref (expected_mode); g_array_unref (expected_mt); g_array_unref (expected_bm); g_array_unref (expected_ds); g_array_unref (expected_bfr); } /*****************************************************************************/ /* Test ^SWWAN read */ #define SWWAN_TEST_MAX_CIDS 2 typedef struct { guint cid; MMBearerConnectionStatus state; } PdpContextState; typedef struct { const gchar *response; PdpContextState expected_items[SWWAN_TEST_MAX_CIDS]; gboolean skip_test_other_cids; } SwwanTest; /* Note: all tests are based on checking CIDs 2 and 3 */ static const SwwanTest swwan_tests[] = { /* No active PDP context reported (all disconnected) */ { .response = "", .expected_items = { { .cid = 2, .state = MM_BEARER_CONNECTION_STATUS_DISCONNECTED }, { .cid = 3, .state = MM_BEARER_CONNECTION_STATUS_DISCONNECTED } }, /* Don't test other CIDs because for those we would also return * DISCONNECTED, not UNKNOWN. */ .skip_test_other_cids = TRUE }, /* Single PDP context active (short version without interface index) */ { .response = "^SWWAN: 3,1\r\n", .expected_items = { { .cid = 2, .state = MM_BEARER_CONNECTION_STATUS_UNKNOWN }, { .cid = 3, .state = MM_BEARER_CONNECTION_STATUS_CONNECTED } } }, /* Single PDP context active (long version with interface index) */ { .response = "^SWWAN: 3,1,1\r\n", .expected_items = { { .cid = 2, .state = MM_BEARER_CONNECTION_STATUS_UNKNOWN }, { .cid = 3, .state = MM_BEARER_CONNECTION_STATUS_CONNECTED } } }, /* Single PDP context inactive (short version without interface index) */ { .response = "^SWWAN: 3,0\r\n", .expected_items = { { .cid = 2, .state = MM_BEARER_CONNECTION_STATUS_UNKNOWN }, { .cid = 3, .state = MM_BEARER_CONNECTION_STATUS_DISCONNECTED } } }, /* Single PDP context inactive (long version with interface index) */ { .response = "^SWWAN: 3,0,1\r\n", .expected_items = { { .cid = 2, .state = MM_BEARER_CONNECTION_STATUS_UNKNOWN }, { .cid = 3, .state = MM_BEARER_CONNECTION_STATUS_DISCONNECTED } } }, /* Multiple PDP contexts active (short version without interface index) */ { .response = "^SWWAN: 2,1\r\n^SWWAN: 3,1\r\n", .expected_items = { { .cid = 2, .state = MM_BEARER_CONNECTION_STATUS_CONNECTED }, { .cid = 3, .state = MM_BEARER_CONNECTION_STATUS_CONNECTED } } }, /* Multiple PDP contexts active (long version with interface index) */ { .response = "^SWWAN: 2,1,3\r\n^SWWAN: 3,1,1\r\n", .expected_items = { { .cid = 2, .state = MM_BEARER_CONNECTION_STATUS_CONNECTED }, { .cid = 3, .state = MM_BEARER_CONNECTION_STATUS_CONNECTED } } }, /* Multiple PDP contexts inactive (short version without interface index) */ { .response = "^SWWAN: 2,0\r\n^SWWAN: 3,0\r\n", .expected_items = { { .cid = 2, .state = MM_BEARER_CONNECTION_STATUS_DISCONNECTED }, { .cid = 3, .state = MM_BEARER_CONNECTION_STATUS_DISCONNECTED } } }, /* Multiple PDP contexts inactive (long version with interface index) */ { .response = "^SWWAN: 2,0,3\r\n^SWWAN: 3,0,1\r\n", .expected_items = { { .cid = 2, .state = MM_BEARER_CONNECTION_STATUS_DISCONNECTED }, { .cid = 3, .state = MM_BEARER_CONNECTION_STATUS_DISCONNECTED } } }, /* Multiple PDP contexts active/inactive (short version without interface index) */ { .response = "^SWWAN: 2,0\r\n^SWWAN: 3,1\r\n", .expected_items = { { .cid = 2, .state = MM_BEARER_CONNECTION_STATUS_DISCONNECTED }, { .cid = 3, .state = MM_BEARER_CONNECTION_STATUS_CONNECTED } } }, /* Multiple PDP contexts active/inactive (long version with interface index) */ { .response = "^SWWAN: 2,0,3\r\n^SWWAN: 3,1,1\r\n", .expected_items = { { .cid = 2, .state = MM_BEARER_CONNECTION_STATUS_DISCONNECTED }, { .cid = 3, .state = MM_BEARER_CONNECTION_STATUS_CONNECTED } } } }; static void test_swwan_pls8 (void) { MMBearerConnectionStatus read_state; GError *error = NULL; guint i; /* Base tests for successful responses */ for (i = 0; i < G_N_ELEMENTS (swwan_tests); i++) { guint j; /* Query for the expected items (CIDs 2 and 3) */ for (j = 0; j < SWWAN_TEST_MAX_CIDS; j++) { read_state = mm_cinterion_parse_swwan_response (swwan_tests[i].response, swwan_tests[i].expected_items[j].cid, NULL, &error); if (swwan_tests[i].expected_items[j].state == MM_BEARER_CONNECTION_STATUS_UNKNOWN) { g_assert_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED); g_clear_error (&error); } else g_assert_no_error (error); g_assert_cmpint (read_state, ==, swwan_tests[i].expected_items[j].state); } /* Query for a CID which isn't replied (e.g. 12) */ if (!swwan_tests[i].skip_test_other_cids) { read_state = mm_cinterion_parse_swwan_response (swwan_tests[i].response, 12, NULL, &error); g_assert_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED); g_assert_cmpint (read_state, ==, MM_BEARER_CONNECTION_STATUS_UNKNOWN); g_clear_error (&error); } } /* Additional tests for errors */ read_state = mm_cinterion_parse_swwan_response ("^GARBAGE", 2, NULL, &error); g_assert_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED); g_assert_cmpint (read_state, ==, MM_BEARER_CONNECTION_STATUS_UNKNOWN); g_clear_error (&error); } /*****************************************************************************/ /* Test ^SIND responses */ static void common_test_sind_response (const gchar *response, const gchar *expected_description, guint expected_mode, guint expected_value) { GError *error = NULL; gboolean res; gchar *description; guint mode; guint value; res = mm_cinterion_parse_sind_response (response, &description, &mode, &value, &error); g_assert_no_error (error); g_assert (res == TRUE); g_assert_cmpstr (description, ==, expected_description); g_assert_cmpuint (mode, ==, expected_mode); g_assert_cmpuint (value, ==, expected_value); g_free (description); } static void test_sind_response_simstatus (void) { common_test_sind_response ("^SIND: simstatus,1,5", "simstatus", 1, 5); } /*****************************************************************************/ /* Test ^SMONG responses */ static void common_test_smong_response (const gchar *response, gboolean success, MMModemAccessTechnology expected_access_tech) { GError *error = NULL; gboolean res; MMModemAccessTechnology access_tech; res = mm_cinterion_parse_smong_response (response, &access_tech, &error); if (success) { g_assert_no_error (error); g_assert (res); g_assert_cmpuint (access_tech, ==, expected_access_tech); } else { g_assert (error); g_assert (!res); } } static void test_smong_response_tc63i (void) { const gchar *response = "\r\n" "GPRS Monitor\r\n" "BCCH G PBCCH PAT MCC MNC NOM TA RAC # Cell #\r\n" "0073 1 - - 262 02 2 00 01\r\n"; common_test_smong_response (response, TRUE, MM_MODEM_ACCESS_TECHNOLOGY_GPRS); } static void test_smong_response_other (void) { const gchar *response = "\r\n" "GPRS Monitor\r\n" "\r\n" "BCCH G PBCCH PAT MCC MNC NOM TA RAC # Cell #\r\n" " 44 1 - - 234 10 - - - \r\n"; common_test_smong_response (response, TRUE, MM_MODEM_ACCESS_TECHNOLOGY_GPRS); } static void test_smong_response_no_match (void) { const gchar *response = "\r\n" "GPRS Monitor\r\n" "\r\n" "BCCH K PBCCH PAT MCC MNC NOM TA RAC # Cell #\r\n" " 44 1 - - 234 10 - - - \r\n"; common_test_smong_response (response, FALSE, MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN); } /*****************************************************************************/ /* Test ^SLCC URCs */ static void common_test_slcc_urc (const gchar *urc, const MMCallInfo *expected_call_info_list, guint expected_call_info_list_size) { g_autoptr(GRegex) slcc_regex = NULL; g_autoptr(GMatchInfo) match_info = NULL; g_autofree gchar *str = NULL; GError *error = NULL; GList *call_info_list = NULL; GList *l; gboolean result; slcc_regex = mm_cinterion_get_slcc_regex (); /* Same matching logic as done in MMSerialPortAt when processing URCs! */ result = g_regex_match_full (slcc_regex, urc, -1, 0, 0, &match_info, &error); g_assert_no_error (error); g_assert (result); /* read full matched content */ str = g_match_info_fetch (match_info, 0); g_assert (str); result = mm_cinterion_parse_slcc_list (str, NULL, &call_info_list, &error); g_assert_no_error (error); g_assert (result); g_debug ("found %u calls", g_list_length (call_info_list)); if (expected_call_info_list) { g_assert (call_info_list); g_assert_cmpuint (g_list_length (call_info_list), ==, expected_call_info_list_size); } else g_assert (!call_info_list); for (l = call_info_list; l; l = g_list_next (l)) { const MMCallInfo *call_info = (const MMCallInfo *)(l->data); gboolean found = FALSE; guint i; g_debug ("call at index %u: direction %s, state %s, number %s", call_info->index, mm_call_direction_get_string (call_info->direction), mm_call_state_get_string (call_info->state), call_info->number ? call_info->number : "n/a"); for (i = 0; !found && i < expected_call_info_list_size; i++) found = ((call_info->index == expected_call_info_list[i].index) && (call_info->direction == expected_call_info_list[i].direction) && (call_info->state == expected_call_info_list[i].state) && (g_strcmp0 (call_info->number, expected_call_info_list[i].number) == 0)); g_assert (found); } mm_cinterion_call_info_list_free (call_info_list); } static void test_slcc_urc_empty (void) { const gchar *urc = "\r\n^SLCC: \r\n"; common_test_slcc_urc (urc, NULL, 0); } static void test_slcc_urc_single (void) { static const MMCallInfo expected_call_info_list[] = { { 1, MM_CALL_DIRECTION_INCOMING, MM_CALL_STATE_ACTIVE, (gchar *) "123456789" } }; const gchar *urc = "\r\n^SLCC: 1,1,0,0,0,0,\"123456789\",161" "\r\n^SLCC: \r\n"; common_test_slcc_urc (urc, expected_call_info_list, G_N_ELEMENTS (expected_call_info_list)); } static void test_slcc_urc_multiple (void) { static const MMCallInfo expected_call_info_list[] = { { 1, MM_CALL_DIRECTION_INCOMING, MM_CALL_STATE_ACTIVE, NULL }, { 2, MM_CALL_DIRECTION_INCOMING, MM_CALL_STATE_ACTIVE, (gchar *) "123456789" }, { 3, MM_CALL_DIRECTION_INCOMING, MM_CALL_STATE_ACTIVE, (gchar *) "987654321" }, }; const gchar *urc = "\r\n^SLCC: 1,1,0,0,1,0" /* number unknown */ "\r\n^SLCC: 2,1,0,0,1,0,\"123456789\",161" "\r\n^SLCC: 3,1,0,0,1,0,\"987654321\",161,\"Alice\"" "\r\n^SLCC: \r\n"; common_test_slcc_urc (urc, expected_call_info_list, G_N_ELEMENTS (expected_call_info_list)); } static void test_slcc_urc_complex (void) { static const MMCallInfo expected_call_info_list[] = { { 1, MM_CALL_DIRECTION_INCOMING, MM_CALL_STATE_ACTIVE, (gchar *) "123456789" }, { 2, MM_CALL_DIRECTION_INCOMING, MM_CALL_STATE_WAITING, (gchar *) "987654321" }, }; const gchar *urc = "\r\n^CIEV: 1,0" /* some different URC before our match */ "\r\n^SLCC: 1,1,0,0,0,0,\"123456789\",161" "\r\n^SLCC: 2,1,5,0,0,0,\"987654321\",161" "\r\n^SLCC: \r\n" "\r\n^CIEV: 1,0" /* some different URC after our match */; common_test_slcc_urc (urc, expected_call_info_list, G_N_ELEMENTS (expected_call_info_list)); } /*****************************************************************************/ /* Test +CTZU URCs */ static void common_test_ctzu_urc (const gchar *urc, const gchar *expected_iso8601, gint expected_offset, gint expected_dst_offset) { g_autoptr(GRegex) ctzu_regex = NULL; g_autoptr(GMatchInfo) match_info = NULL; g_autofree gchar *iso8601 = NULL; GError *error = NULL; gboolean result; MMNetworkTimezone *tz = NULL; ctzu_regex = mm_cinterion_get_ctzu_regex (); /* Same matching logic as done in MMSerialPortAt when processing URCs! */ result = g_regex_match_full (ctzu_regex, urc, -1, 0, 0, &match_info, &error); g_assert_no_error (error); g_assert (result); result = mm_cinterion_parse_ctzu_urc (match_info, &iso8601, &tz, &error); g_assert_no_error (error); g_assert (result); g_assert (iso8601); g_assert_cmpstr (expected_iso8601, ==, iso8601); g_assert (tz); g_assert_cmpint (expected_offset, ==, mm_network_timezone_get_offset (tz)); if (expected_dst_offset >= 0) g_assert_cmpuint ((guint)expected_dst_offset, ==, mm_network_timezone_get_dst_offset (tz)); g_object_unref (tz); } static void test_ctzu_urc_simple (void) { const gchar *urc = "\r\n+CTZU: \"19/07/09,11:15:40\",+08\r\n"; const gchar *expected_iso8601 = "2019-07-09T11:15:40+02"; gint expected_offset = 120; gint expected_dst_offset = -1; /* not given */ common_test_ctzu_urc (urc, expected_iso8601, expected_offset, expected_dst_offset); } static void test_ctzu_urc_full (void) { const gchar *urc = "\r\n+CTZU: \"19/07/09,11:15:40\",+08,1\r\n"; const gchar *expected_iso8601 = "2019-07-09T11:15:40+02"; gint expected_offset = 120; gint expected_dst_offset = 60; common_test_ctzu_urc (urc, expected_iso8601, expected_offset, expected_dst_offset); } /*****************************************************************************/ /* Test ^SMONI responses */ typedef struct { const gchar *str; MMCinterionRadioGen tech; gdouble rssi; gdouble ecn0; gdouble rscp; gdouble rsrp; gdouble rsrq; } SMoniResponseTest; static const SMoniResponseTest smoni_response_tests[] = { { .str = "^SMONI: 2G,71,-61,262,02,0143,83BA,33,33,3,6,G,NOCONN", .tech = MM_CINTERION_RADIO_GEN_2G, .rssi = -61.0, .ecn0 = 0.0, .rscp = 0.0, .rsrp = 0.0, .rsrq = 0.0 }, { .str = "^SMONI: 2G,SEARCH,SEARCH", .tech = MM_CINTERION_RADIO_GEN_NONE, .rssi = 0.0, .ecn0 = 0.0, .rscp = 0.0, .rsrp = 0.0, .rsrq = 0.0 }, { .str = "^SMONI: 2G,673,-89,262,07,4EED,A500,16,16,7,4,G,5,-107,LIMSRV", .tech = MM_CINTERION_RADIO_GEN_2G, .rssi = -89.0, .ecn0 = 0.0, .rscp = 0.0, .rsrp = 0.0, .rsrq = 0.0 }, { .str = "^SMONI: 2G,673,-80,262,07,4EED,A500,35,35,7,4,G,643,4,0,-80,0,S_FR", .tech = MM_CINTERION_RADIO_GEN_2G, .rssi = -80.0, .ecn0 = 0.0, .rscp = 0.0, .rsrp = 0.0, .rsrq = 0.0 }, { .str = "^SMONI: 3G,10564,296,-7.5,-79,262,02,0143,00228FF,-92,-78,NOCONN", .tech = MM_CINTERION_RADIO_GEN_3G, .rssi = 0.0, .ecn0 = -7.5, .rscp = -79.0, .rsrp = 0.0, .rsrq = 0.0 }, { .str = "^SMONI: 3G,SEARCH,SEARCH", .tech = MM_CINTERION_RADIO_GEN_NONE, .rssi = 0.0, .ecn0 = 0, .rscp = 0, .rsrp = 0.0, .rsrq = 0.0 }, { .str = "^SMONI: 3G,10564,96,-6.5,-77,262,02,0143,00228FF,-92,-78,LIMSRV", .tech = MM_CINTERION_RADIO_GEN_3G, .rssi = 0.0, .ecn0 = -6.5, .rscp = -77.0, .rsrp = 0.0, .rsrq = 0.0 }, { .str = "^SMONI: 3G,10737,131,-5,-93,260,01,7D3D,C80BC9A,--,--,----,---,-,-5,-93,0,01,06", .tech = MM_CINTERION_RADIO_GEN_3G, .rssi = 0.0, .ecn0 = -5.0, .rscp = -93.0, .rsrp = 0.0, .rsrq = 0.0 }, { .str = "^SMONI: 4G,6300,20,10,10,FDD,262,02,BF75,0345103,350,33,-94,-7,NOCONN", .tech = MM_CINTERION_RADIO_GEN_4G, .rssi = 0.0, .ecn0 = 0.0, .rscp = 0.0, .rsrp = -94.0, .rsrq = -7.0 }, { .str = "^SMONI: 4G,SEARCH", .tech = MM_CINTERION_RADIO_GEN_NONE, .rssi = 0.0, .ecn0 = 0.0, .rscp = 0.0, .rsrp = 0.0, .rsrq = 0.0 }, { .str = "^SMONI: 4G,6300,20,10,10,FDD,262,02,BF75,0345103,350,33,-90,-6,LIMSRV", .tech = MM_CINTERION_RADIO_GEN_4G, .rssi = 0.0, .ecn0 = 0.0, .rscp = 0.0, .rsrp = -90.0, .rsrq = -6.0 }, { .str = "^SMONI: 4G,6300,20,10,10,FDD,262,02,BF75,0345103,350,90,-101,-7,CONN", .tech = MM_CINTERION_RADIO_GEN_4G, .rssi = 0.0, .ecn0 = 0.0, .rscp = 0.0, .rsrp = -101.0, .rsrq = -7.0 }, { .str = "^SMONI: 4G,2850,7,20,20,FDD,262,02,C096,027430F,275,11,-114,-9,NOCONN", .tech = MM_CINTERION_RADIO_GEN_4G, .rssi = 0.0, .ecn0 = 0.0, .rscp = 0.0, .rsrp = -114.0, .rsrq = -9.0 }, { .str = "^SMONI: 4G,2850,7,20,20,FDD,262,02,C096,027430F,275,-,-113,-8,CONN", .tech = MM_CINTERION_RADIO_GEN_4G, .rssi = 0.0, .ecn0 = 0.0, .rscp = 0.0, .rsrp = -113.0, .rsrq = -8.0 } }; static void test_smoni_response (void) { guint i; for (i = 0; i < G_N_ELEMENTS (smoni_response_tests); i++) { GError *error = NULL; gboolean success; MMCinterionRadioGen tech = MM_CINTERION_RADIO_GEN_NONE; gdouble rssi = MM_SIGNAL_UNKNOWN; gdouble ecn0 = MM_SIGNAL_UNKNOWN; gdouble rscp = MM_SIGNAL_UNKNOWN; gdouble rsrp = MM_SIGNAL_UNKNOWN; gdouble rsrq = MM_SIGNAL_UNKNOWN; success = mm_cinterion_parse_smoni_query_response (smoni_response_tests[i].str, &tech, &rssi, &ecn0, &rscp, &rsrp, &rsrq, &error); g_assert_no_error (error); g_assert (success); g_assert_cmpuint (smoni_response_tests[i].tech, ==, tech); switch (smoni_response_tests[i].tech) { case MM_CINTERION_RADIO_GEN_2G: g_assert_cmpfloat_tolerance (rssi, smoni_response_tests[i].rssi, 0.1); break; case MM_CINTERION_RADIO_GEN_3G: g_assert_cmpfloat_tolerance (ecn0, smoni_response_tests[i].ecn0, 0.1); g_assert_cmpfloat_tolerance (rscp, smoni_response_tests[i].rscp, 0.1); break; case MM_CINTERION_RADIO_GEN_4G: g_assert_cmpfloat_tolerance (rsrp, smoni_response_tests[i].rsrp, 0.1); g_assert_cmpfloat_tolerance (rsrq, smoni_response_tests[i].rsrq, 0.1); break; case MM_CINTERION_RADIO_GEN_NONE: default: break; } } } static void test_smoni_response_to_signal (void) { guint i; for (i = 0; i < G_N_ELEMENTS (smoni_response_tests); i++) { GError *error = NULL; gboolean success; MMSignal *gsm = NULL; MMSignal *umts = NULL; MMSignal *lte = NULL; success = mm_cinterion_smoni_response_to_signal_info (smoni_response_tests[i].str, &gsm, &umts, <e, &error); g_assert_no_error (error); g_assert (success); switch (smoni_response_tests[i].tech) { case MM_CINTERION_RADIO_GEN_2G: g_assert (gsm); g_assert_cmpfloat_tolerance (mm_signal_get_rssi (gsm), smoni_response_tests[i].rssi, 0.1); g_object_unref (gsm); g_assert (!umts); g_assert (!lte); break; case MM_CINTERION_RADIO_GEN_3G: g_assert (umts); g_assert_cmpfloat_tolerance (mm_signal_get_rscp (umts), smoni_response_tests[i].rscp, 0.1); g_assert_cmpfloat_tolerance (mm_signal_get_ecio (umts), smoni_response_tests[i].ecn0, 0.1); g_object_unref (umts); g_assert (!gsm); g_assert (!lte); break; case MM_CINTERION_RADIO_GEN_4G: g_assert (lte); g_assert_cmpfloat_tolerance (mm_signal_get_rsrp (lte), smoni_response_tests[i].rsrp, 0.1); g_assert_cmpfloat_tolerance (mm_signal_get_rsrq (lte), smoni_response_tests[i].rsrq, 0.1); g_object_unref (lte); g_assert (!gsm); g_assert (!umts); break; case MM_CINTERION_RADIO_GEN_NONE: default: g_assert (!gsm); g_assert (!umts); g_assert (!lte); break; } } } /*****************************************************************************/ /* Test ^SCFG="MEopMode/Prov/Cfg" responses */ typedef struct { const gchar *str; MMCinterionModemFamily modem_family; gboolean success; guint expected_cid; } ProvcfgResponseTest; static const ProvcfgResponseTest provcfg_response_tests[] = { { .str = "^SCFG: \"MEopMode/Prov/Cfg\",\"vdfde\"", .modem_family = MM_CINTERION_MODEM_FAMILY_DEFAULT, .success = TRUE, .expected_cid = 1, }, { .str = "* ^SCFG: \"MEopMode/Prov/Cfg\",\"attus\"", .modem_family = MM_CINTERION_MODEM_FAMILY_IMT, .success = TRUE, .expected_cid = 1, }, { .str = "* ^SCFG: \"MEopMode/Prov/Cfg\",\"2\"", .modem_family = MM_CINTERION_MODEM_FAMILY_DEFAULT, .success = TRUE, .expected_cid = 3, }, { .str = "* ^SCFG: \"MEopMode/Prov/Cfg\",\"vzwdcus\"", .modem_family = MM_CINTERION_MODEM_FAMILY_DEFAULT, .success = TRUE, .expected_cid = 3, }, { .str = "* ^SCFG: \"MEopMode/Prov/Cfg\",\"tmode\"", .modem_family = MM_CINTERION_MODEM_FAMILY_DEFAULT, .success = TRUE, .expected_cid = 2, }, { .str = "* ^SCFG: \"MEopMode/Prov/Cfg\",\"fallback*\"", .modem_family = MM_CINTERION_MODEM_FAMILY_DEFAULT, .success = TRUE, .expected_cid = 1, }, { /* commas not allowed by the regex */ .str = "* ^SCFG: \"MEopMode/Prov/Cfg\",\"something,with,commas\"", .modem_family = MM_CINTERION_MODEM_FAMILY_DEFAULT, .success = FALSE, } }; static void test_provcfg_response (void) { guint i; for (i = 0; i < G_N_ELEMENTS (provcfg_response_tests); i++) { gint cid = -1; gboolean result; GError *error = NULL; result = mm_cinterion_provcfg_response_to_cid (provcfg_response_tests[i].str, provcfg_response_tests[i].modem_family, MM_MODEM_CHARSET_GSM, NULL, &cid, &error); if (provcfg_response_tests[i].success) { g_assert_no_error (error); g_assert (result); g_assert_cmpuint (cid, ==, provcfg_response_tests[i].expected_cid); } else { g_assert (error); g_assert (!result); } } } /*****************************************************************************/ /* Test ^SGAUTH responses */ static void test_sgauth_response (void) { gboolean result; MMBearerAllowedAuth auth = MM_BEARER_ALLOWED_AUTH_UNKNOWN; gchar *username = NULL; GError *error = NULL; const gchar *response = "^SGAUTH: 1,2,\"vf\"\r\n" "^SGAUTH: 2,1,\"\"\r\n" "^SGAUTH: 3,0\r\n"; /* CID 1 */ result = mm_cinterion_parse_sgauth_response (response, 1, &auth, &username, &error); g_assert_no_error (error); g_assert (result); g_assert_cmpuint (auth, ==, MM_BEARER_ALLOWED_AUTH_CHAP); g_assert_cmpstr (username, ==, "vf"); auth = MM_BEARER_ALLOWED_AUTH_UNKNOWN; g_clear_pointer (&username, g_free); /* CID 2 */ result = mm_cinterion_parse_sgauth_response (response, 2, &auth, &username, &error); g_assert_no_error (error); g_assert (result); g_assert_cmpuint (auth, ==, MM_BEARER_ALLOWED_AUTH_PAP); g_assert_null (username); auth = MM_BEARER_ALLOWED_AUTH_UNKNOWN; /* CID 3 */ result = mm_cinterion_parse_sgauth_response (response, 3, &auth, &username, &error); g_assert_no_error (error); g_assert (result); g_assert_cmpuint (auth, ==, MM_BEARER_ALLOWED_AUTH_NONE); g_assert_null (username); auth = MM_BEARER_ALLOWED_AUTH_UNKNOWN; /* CID 4 */ result = mm_cinterion_parse_sgauth_response (response, 4, &auth, &username, &error); g_assert_error (error, MM_CORE_ERROR, MM_CORE_ERROR_NOT_FOUND); g_assert (!result); } /*****************************************************************************/ /* Test ^SXRAT responses */ static void common_test_sxrat (const gchar *response, const GArray *expected_rat, const GArray *expected_pref1, const GArray *expected_pref2) { GArray *supported_rat = NULL; GArray *supported_pref1 = NULL; GArray *supported_pref2 = NULL; GError *error = NULL; gboolean res; g_assert (expected_rat != NULL); g_assert (expected_pref1 != NULL); res = mm_cinterion_parse_sxrat_test (response, &supported_rat, &supported_pref1, &supported_pref2, &error); g_assert_no_error (error); g_assert (res == TRUE); g_assert (supported_rat != NULL); g_assert (supported_pref1 != NULL); if (expected_pref2) g_assert (supported_pref2 != NULL); else g_assert (supported_pref2 == NULL); compare_arrays (supported_rat, expected_rat); compare_arrays (supported_pref1, expected_pref1); if (expected_pref2) compare_arrays (supported_pref2, expected_pref2); g_array_unref (supported_rat); g_array_unref (supported_pref1); if (supported_pref2) g_array_unref (supported_pref2); } static void test_sxrat_response_els61 (void) { GArray *expected_rat; GArray *expected_pref1; GArray *expected_pref2; guint val; const gchar *response = "^SXRAT: (0-6),(0,2,3),(0,2,3)\r\n" "\r\n"; expected_rat = g_array_sized_new (FALSE, FALSE, sizeof (guint), 7); val = 0, g_array_append_val (expected_rat, val); val = 1, g_array_append_val (expected_rat, val); val = 2, g_array_append_val (expected_rat, val); val = 3, g_array_append_val (expected_rat, val); val = 4, g_array_append_val (expected_rat, val); val = 5, g_array_append_val (expected_rat, val); val = 6, g_array_append_val (expected_rat, val); expected_pref1 = g_array_sized_new (FALSE, FALSE, sizeof (guint), 3); val = 0, g_array_append_val (expected_pref1, val); val = 2, g_array_append_val (expected_pref1, val); val = 3, g_array_append_val (expected_pref1, val); expected_pref2 = g_array_sized_new (FALSE, FALSE, sizeof (guint), 3); val = 0, g_array_append_val (expected_pref2, val); val = 2, g_array_append_val (expected_pref2, val); val = 3, g_array_append_val (expected_pref2, val); common_test_sxrat (response, expected_rat, expected_pref1, expected_pref2); g_array_unref (expected_rat); g_array_unref (expected_pref1); g_array_unref (expected_pref2); } static void test_sxrat_response_other (void) { GArray *expected_rat; GArray *expected_pref1; GArray *expected_pref2 = NULL; guint val; const gchar *response = "^SXRAT: (0-2),(0,2)\r\n" "\r\n"; expected_rat = g_array_sized_new (FALSE, FALSE, sizeof (guint), 3); val = 0, g_array_append_val (expected_rat, val); val = 1, g_array_append_val (expected_rat, val); val = 2, g_array_append_val (expected_rat, val); expected_pref1 = g_array_sized_new (FALSE, FALSE, sizeof (guint), 3); val = 0, g_array_append_val (expected_pref1, val); val = 2, g_array_append_val (expected_pref1, val); common_test_sxrat (response, expected_rat, expected_pref1, expected_pref2); g_array_unref (expected_rat); g_array_unref (expected_pref1); } typedef struct { const gchar *str; MMModemMode allowed; MMModemMode preferred; gboolean success; } SxratBuildTest; static const SxratBuildTest sxrat_build_tests[] = { { .str = "^SXRAT=0", .allowed = MM_MODEM_MODE_2G, .preferred = MM_MODEM_MODE_NONE, .success = TRUE, }, { .str = "^SXRAT=3", .allowed = MM_MODEM_MODE_4G, .preferred = MM_MODEM_MODE_NONE, .success = TRUE, }, { .str = "^SXRAT=1,2", .allowed = MM_MODEM_MODE_2G | MM_MODEM_MODE_3G, .preferred = MM_MODEM_MODE_3G, .success = TRUE, }, { .str = "^SXRAT=6,3", .allowed = MM_MODEM_MODE_2G | MM_MODEM_MODE_3G | MM_MODEM_MODE_4G, .preferred = MM_MODEM_MODE_4G, .success = TRUE, }, { .str = NULL, .allowed = MM_MODEM_MODE_2G | MM_MODEM_MODE_3G | MM_MODEM_MODE_4G, .preferred = MM_MODEM_MODE_3G | MM_MODEM_MODE_4G, .success = FALSE, }, { .str = NULL, .allowed = MM_MODEM_MODE_5G, .preferred = MM_MODEM_MODE_NONE, .success = FALSE, }, }; static void test_sxrat (void) { guint i; for (i = 0; i < G_N_ELEMENTS (sxrat_build_tests); i++) { GError *error = NULL; gchar* result; result = mm_cinterion_build_sxrat_set_command (sxrat_build_tests[i].allowed, sxrat_build_tests[i].preferred, &error); if (sxrat_build_tests[i].success) { g_assert_no_error (error); g_assert (result); g_assert_cmpstr (result, ==, sxrat_build_tests[i].str); } else { g_assert (error); g_assert (!result); } } } /*****************************************************************************/ int main (int argc, char **argv) { setlocale (LC_ALL, ""); g_test_init (&argc, &argv, NULL); g_test_add_func ("/MM/cinterion/scfg", test_scfg); g_test_add_func ("/MM/cinterion/scfg/ehs5", test_scfg_ehs5); g_test_add_func ("/MM/cinterion/scfg/pls62/gsm", test_scfg_pls62_gsm); g_test_add_func ("/MM/cinterion/scfg/pls62/ucs2", test_scfg_pls62_ucs2); g_test_add_func ("/MM/cinterion/scfg/alas5", test_scfg_alas5); g_test_add_func ("/MM/cinterion/scfg/response/3g", test_scfg_response_3g); g_test_add_func ("/MM/cinterion/scfg/response/2g", test_scfg_response_2g); g_test_add_func ("/MM/cinterion/scfg/response/pls62/gsm", test_scfg_response_pls62_gsm); g_test_add_func ("/MM/cinterion/scfg/response/pls62/ucs2",test_scfg_response_pls62_ucs2); g_test_add_func ("/MM/cinterion/scfg/response/alas5", test_scfg_response_alas5); g_test_add_func ("/MM/cinterion/cnmi/phs8", test_cnmi_phs8); g_test_add_func ("/MM/cinterion/cnmi/other", test_cnmi_other); g_test_add_func ("/MM/cinterion/swwan/pls8", test_swwan_pls8); g_test_add_func ("/MM/cinterion/sind/response/simstatus", test_sind_response_simstatus); g_test_add_func ("/MM/cinterion/smong/response/tc63i", test_smong_response_tc63i); g_test_add_func ("/MM/cinterion/smong/response/other", test_smong_response_other); g_test_add_func ("/MM/cinterion/smong/response/no-match", test_smong_response_no_match); g_test_add_func ("/MM/cinterion/slcc/urc/empty", test_slcc_urc_empty); g_test_add_func ("/MM/cinterion/slcc/urc/single", test_slcc_urc_single); g_test_add_func ("/MM/cinterion/slcc/urc/multiple", test_slcc_urc_multiple); g_test_add_func ("/MM/cinterion/slcc/urc/complex", test_slcc_urc_complex); g_test_add_func ("/MM/cinterion/ctzu/urc/simple", test_ctzu_urc_simple); g_test_add_func ("/MM/cinterion/ctzu/urc/full", test_ctzu_urc_full); g_test_add_func ("/MM/cinterion/smoni/query_response", test_smoni_response); g_test_add_func ("/MM/cinterion/smoni/query_response_to_signal", test_smoni_response_to_signal); g_test_add_func ("/MM/cinterion/scfg/provcfg", test_provcfg_response); g_test_add_func ("/MM/cinterion/sgauth", test_sgauth_response); g_test_add_func ("/MM/cinterion/sxrat", test_sxrat); g_test_add_func ("/MM/cinterion/sxrat/response/els61", test_sxrat_response_els61); g_test_add_func ("/MM/cinterion/sxrat/response/other", test_sxrat_response_other); return g_test_run (); } ModemManager-1.23.4-dev/src/plugins/dell/000077500000000000000000000000001456466623000201155ustar00rootroot00000000000000ModemManager-1.23.4-dev/src/plugins/dell/77-mm-dell-port-types.rules000066400000000000000000000033471456466623000251060ustar00rootroot00000000000000# do not edit this file, it will be overwritten on update ACTION!="add|change|move|bind", GOTO="mm_dell_port_types_end" SUBSYSTEMS=="usb", ATTRS{idVendor}=="413c", GOTO="mm_dell_vendorcheck" GOTO="mm_dell_port_types_end" LABEL="mm_dell_vendorcheck" SUBSYSTEMS=="usb", ATTRS{bInterfaceNumber}=="?*", ENV{.MM_USBIFNUM}="$attr{bInterfaceNumber}" # Dell DW5821e (default 0x81d7, with esim support 0x81e0) # if 02: primary port # if 03: secondary port # if 04: raw NMEA port # if 05: diag/qcdm port ATTRS{idVendor}=="413c", ATTRS{idProduct}=="81d7", ENV{.MM_USBIFNUM}=="02", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1" ATTRS{idVendor}=="413c", ATTRS{idProduct}=="81d7", ENV{.MM_USBIFNUM}=="03", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1" ATTRS{idVendor}=="413c", ATTRS{idProduct}=="81d7", ENV{.MM_USBIFNUM}=="04", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_GPS}="1" ATTRS{idVendor}=="413c", ATTRS{idProduct}=="81d7", ENV{.MM_USBIFNUM}=="05", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_QCDM}="1" ATTRS{idVendor}=="413c", ATTRS{idProduct}=="81e0", ENV{.MM_USBIFNUM}=="02", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1" ATTRS{idVendor}=="413c", ATTRS{idProduct}=="81e0", ENV{.MM_USBIFNUM}=="03", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1" ATTRS{idVendor}=="413c", ATTRS{idProduct}=="81e0", ENV{.MM_USBIFNUM}=="04", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_GPS}="1" ATTRS{idVendor}=="413c", ATTRS{idProduct}=="81e0", ENV{.MM_USBIFNUM}=="05", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_QCDM}="1" # Dell DW5820e # if 02: AT port # if 04: debug port (ignore) # if 06: AT port ATTRS{idVendor}=="413c", ATTRS{idProduct}=="81d9", ENV{.MM_USBIFNUM}=="04", ENV{ID_MM_PORT_IGNORE}="1" GOTO="mm_dell_port_types_end" LABEL="mm_dell_port_types_end" ModemManager-1.23.4-dev/src/plugins/dell/mm-plugin-dell.c000066400000000000000000000464611456466623000231170ustar00rootroot00000000000000/* -*- 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. * * 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. * * Copyright (C) 2015-2019 Aleksander Morgado */ #include #include #define _LIBMM_INSIDE_MM #include #include "mm-plugin-common.h" #include "mm-common-novatel.h" #include "mm-private-boxed-types.h" #include "mm-broadband-modem.h" #include "mm-broadband-modem-novatel.h" #include "mm-common-novatel.h" #include "mm-broadband-modem-sierra.h" #include "mm-common-sierra.h" #include "mm-broadband-modem-telit.h" #include "mm-broadband-modem-xmm.h" #include "mm-common-telit.h" #include "mm-log-object.h" #if defined WITH_QMI #include "mm-broadband-modem-qmi.h" #endif #if defined WITH_MBIM #include "mm-broadband-modem-mbim.h" #include "mm-broadband-modem-mbim-xmm.h" #include "mm-broadband-modem-mbim-foxconn.h" #endif #define MM_TYPE_PLUGIN_DELL mm_plugin_dell_get_type () MM_DEFINE_PLUGIN (DELL, dell, Dell) /*****************************************************************************/ #define MAX_PORT_PROBE_TIMEOUTS 3 #define TAG_DELL_MANUFACTURER "dell-manufacturer" typedef enum { DELL_MANUFACTURER_UNKNOWN = 0, DELL_MANUFACTURER_NOVATEL = 1, DELL_MANUFACTURER_SIERRA = 2, DELL_MANUFACTURER_ERICSSON = 3, DELL_MANUFACTURER_TELIT = 4 } DellManufacturer; /*****************************************************************************/ /* Custom init */ typedef struct { MMPortSerialAt *port; guint gmi_retries; guint cgmi_retries; guint ati_retries; guint timeouts; } CustomInitContext; static void custom_init_context_free (CustomInitContext *ctx) { g_object_unref (ctx->port); g_slice_free (CustomInitContext, ctx); } static gboolean dell_custom_init_finish (MMPortProbe *probe, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void novatel_custom_init_ready (MMPortProbe *probe, GAsyncResult *res, GTask *task) { GError *error = NULL; if (!mm_common_novatel_custom_init_finish (probe, res, &error)) g_task_return_error (task, error); else g_task_return_boolean (task, TRUE); g_object_unref (task); } static void sierra_custom_init_ready (MMPortProbe *probe, GAsyncResult *res, GTask *task) { GError *error = NULL; if (!mm_common_sierra_custom_init_finish (probe, res, &error)) g_task_return_error (task, error); else g_task_return_boolean (task, TRUE); g_object_unref (task); } static void telit_custom_init_ready (MMPortProbe *probe, GAsyncResult *res, GTask *task) { GError *error = NULL; if (!telit_custom_init_finish (probe, res, &error)) g_task_return_error (task, error); else g_task_return_boolean (task, TRUE); g_object_unref (task); } static void custom_init_step (GTask *task); static void custom_init_step_next_command (GTask *task) { CustomInitContext *ctx; ctx = g_task_get_task_data (task); ctx->timeouts = 0; if (ctx->gmi_retries > 0) ctx->gmi_retries = 0; else if (ctx->cgmi_retries > 0) ctx->cgmi_retries = 0; else if (ctx->ati_retries > 0) ctx->ati_retries = 0; custom_init_step (task); } static void response_ready (MMPortSerialAt *port, GAsyncResult *res, GTask *task) { CustomInitContext *ctx; MMPortProbe *probe; g_autofree gchar *response = NULL; GError *error = NULL; gchar *lower; DellManufacturer manufacturer; probe = g_task_get_source_object (task); ctx = g_task_get_task_data (task); response = mm_port_serial_at_command_finish (port, res, &error); if (error) { /* Non-timeout error, jump to next command */ if (!g_error_matches (error, MM_SERIAL_ERROR, MM_SERIAL_ERROR_RESPONSE_TIMEOUT)) { mm_obj_dbg (probe, "error probing AT port: %s", error->message); g_error_free (error); custom_init_step_next_command (task); return; } /* Directly retry same command on timeout */ ctx->timeouts++; custom_init_step (task); g_error_free (error); return; } /* Guess manufacturer from response */ lower = g_ascii_strdown (response, -1); if (strstr (lower, "novatel")) manufacturer = DELL_MANUFACTURER_NOVATEL; else if (strstr (lower, "sierra")) manufacturer = DELL_MANUFACTURER_SIERRA; else if (strstr (lower, "ericsson")) manufacturer = DELL_MANUFACTURER_ERICSSON; else if (strstr (lower, "telit")) manufacturer = DELL_MANUFACTURER_TELIT; else manufacturer = DELL_MANUFACTURER_UNKNOWN; g_free (lower); /* Tag based on manufacturer */ if (manufacturer != DELL_MANUFACTURER_UNKNOWN) { g_object_set_data (G_OBJECT (probe), TAG_DELL_MANUFACTURER, GUINT_TO_POINTER (manufacturer)); /* Run additional custom init, if needed */ if (manufacturer == DELL_MANUFACTURER_NOVATEL) { mm_common_novatel_custom_init (probe, ctx->port, g_task_get_cancellable (task), (GAsyncReadyCallback) novatel_custom_init_ready, task); return; } if (manufacturer == DELL_MANUFACTURER_SIERRA) { mm_common_sierra_custom_init (probe, ctx->port, g_task_get_cancellable (task), (GAsyncReadyCallback) sierra_custom_init_ready, task); return; } if (manufacturer == DELL_MANUFACTURER_TELIT) { telit_custom_init (probe, ctx->port, g_task_get_cancellable (task), (GAsyncReadyCallback) telit_custom_init_ready, task); return; } /* Finish custom_init */ g_task_return_boolean (task, TRUE); g_object_unref (task); return; } /* If we got a response, but we didn't get an expected string, try with next command */ custom_init_step_next_command (task); } static void custom_init_step (GTask *task) { CustomInitContext *ctx; MMPortProbe *probe; probe = g_task_get_source_object (task); ctx = g_task_get_task_data (task); /* If cancelled, end without error right away */ if (g_cancellable_is_cancelled (g_task_get_cancellable (task))) { mm_obj_dbg (probe, "no need to keep on running custom init: cancelled"); g_task_return_boolean (task, TRUE); g_object_unref (task); return; } #if defined WITH_QMI /* If device has a QMI port, don't run anything else, as we don't care */ if (mm_port_probe_list_has_qmi_port (mm_device_peek_port_probe_list (mm_port_probe_peek_device (probe)))) { mm_obj_dbg (probe, "no need to run custom init: device has QMI port"); g_task_return_boolean (task, TRUE); g_object_unref (task); return; } #endif #if defined WITH_MBIM /* If device has a MBIM port, don't run anything else, as we don't care */ if (mm_port_probe_list_has_mbim_port (mm_device_peek_port_probe_list (mm_port_probe_peek_device (probe)))) { mm_obj_dbg (probe, "no need to run custom init: device has MBIM port"); g_task_return_boolean (task, TRUE); g_object_unref (task); return; } #endif if (ctx->timeouts >= MAX_PORT_PROBE_TIMEOUTS) { mm_obj_dbg (probe, "couldn't detect real manufacturer: too many timeouts"); mm_port_probe_set_result_at (probe, FALSE); goto out; } if (ctx->gmi_retries > 0) { ctx->gmi_retries--; mm_port_serial_at_command (ctx->port, "AT+GMI", 3, FALSE, /* raw */ FALSE, /* allow_cached */ g_task_get_cancellable (task), (GAsyncReadyCallback)response_ready, task); return; } if (ctx->cgmi_retries > 0) { ctx->cgmi_retries--; mm_port_serial_at_command (ctx->port, "AT+CGMI", 3, FALSE, /* raw */ FALSE, /* allow_cached */ g_task_get_cancellable (task), (GAsyncReadyCallback)response_ready, task); return; } if (ctx->ati_retries > 0) { ctx->ati_retries--; /* Note: in Ericsson devices, ATI3 seems to reply the vendor string */ mm_port_serial_at_command (ctx->port, "ATI1I2I3", 3, FALSE, /* raw */ FALSE, /* allow_cached */ g_task_get_cancellable (task), (GAsyncReadyCallback)response_ready, task); return; } mm_obj_dbg (probe, "couldn't detect real manufacturer: all retries consumed"); out: /* Finish custom_init */ g_task_return_boolean (task, TRUE); g_object_unref (task); } static void dell_custom_init (MMPortProbe *probe, MMPortSerialAt *port, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; CustomInitContext *ctx; ctx = g_slice_new0 (CustomInitContext); ctx->port = g_object_ref (port); ctx->gmi_retries = 3; ctx->cgmi_retries = 1; ctx->ati_retries = 1; task = g_task_new (probe, cancellable, callback, user_data); g_task_set_check_cancellable (task, FALSE); g_task_set_task_data (task, ctx, (GDestroyNotify) custom_init_context_free); custom_init_step (task); } /*****************************************************************************/ static gboolean port_probe_list_has_manufacturer_port (GList *probes, DellManufacturer manufacturer) { GList *l; for (l = probes; l; l = g_list_next (l)) { if (GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (l->data), TAG_DELL_MANUFACTURER)) == manufacturer) return TRUE; } return FALSE; } static MMBaseModem * create_modem (MMPlugin *self, const gchar *uid, const gchar *physdev, const gchar **drivers, guint16 vendor, guint16 product, guint16 subsystem_vendor, GList *probes, GError **error) { /* Note: at this point we don't make any difference between different * Dell-branded QMI or MBIM modems; they may come from Novatel, Ericsson or * Sierra. */ #if defined WITH_QMI if (mm_port_probe_list_has_qmi_port (probes)) { mm_obj_dbg (self, "QMI-powered Dell-branded modem found..."); return MM_BASE_MODEM (mm_broadband_modem_qmi_new (uid, physdev, drivers, mm_plugin_get_name (self), vendor, product)); } #endif #if defined WITH_MBIM if (mm_port_probe_list_has_mbim_port (probes)) { /* Specific implementation for the DW5821e and DW5829e */ if (vendor == 0x413c && (product == 0x81d7 || product == 0x81e0 || product == 0x81e4 || product == 0x81e6)) { mm_obj_dbg (self, "MBIM-powered DW5821e/DW5829e (T77W968) modem found..."); return MM_BASE_MODEM (mm_broadband_modem_mbim_foxconn_new (uid, physdev, drivers, mm_plugin_get_name (self), vendor, product)); } if (mm_port_probe_list_is_xmm (probes)) { mm_obj_dbg (self, "MBIM-powered XMM-based modem found..."); return MM_BASE_MODEM (mm_broadband_modem_mbim_xmm_new (uid, physdev, drivers, mm_plugin_get_name (self), vendor, product)); } mm_obj_dbg (self, "MBIM-powered Dell-branded modem found..."); return MM_BASE_MODEM (mm_broadband_modem_mbim_new (uid, physdev, drivers, mm_plugin_get_name (self), vendor, product)); } #endif if (port_probe_list_has_manufacturer_port (probes, DELL_MANUFACTURER_NOVATEL)) { mm_obj_dbg (self, "Novatel-powered Dell-branded modem found..."); return MM_BASE_MODEM (mm_broadband_modem_novatel_new (uid, physdev, drivers, mm_plugin_get_name (self), vendor, product)); } if (port_probe_list_has_manufacturer_port (probes, DELL_MANUFACTURER_SIERRA)) { mm_obj_dbg (self, "Sierra-powered Dell-branded modem found..."); return MM_BASE_MODEM (mm_broadband_modem_sierra_new (uid, physdev, drivers, mm_plugin_get_name (self), vendor, product)); } if (port_probe_list_has_manufacturer_port (probes, DELL_MANUFACTURER_TELIT)) { mm_obj_dbg (self, "Telit-powered Dell-branded modem found..."); return MM_BASE_MODEM (mm_broadband_modem_telit_new (uid, physdev, drivers, mm_plugin_get_name (self), vendor, product)); } if (mm_port_probe_list_is_xmm (probes)) { mm_obj_dbg (self, "XMM-based modem found..."); return MM_BASE_MODEM (mm_broadband_modem_xmm_new (uid, physdev, drivers, mm_plugin_get_name (self), vendor, product)); } mm_obj_dbg (self, "Dell-branded generic modem found..."); return MM_BASE_MODEM (mm_broadband_modem_new (uid, physdev, drivers, mm_plugin_get_name (self), vendor, product)); } /*****************************************************************************/ static gboolean grab_port (MMPlugin *self, MMBaseModem *modem, MMPortProbe *probe, GError **error) { if (MM_IS_BROADBAND_MODEM_SIERRA (modem)) return mm_common_sierra_grab_port (self, modem, probe, error); if (MM_IS_BROADBAND_MODEM_TELIT (modem)) return telit_grab_port (self, modem, probe, error); return mm_base_modem_grab_port (modem, mm_port_probe_peek_port (probe), mm_port_probe_get_port_type (probe), MM_PORT_SERIAL_AT_FLAG_NONE, error); } /*****************************************************************************/ MM_PLUGIN_NAMED_CREATOR_SCOPE MMPlugin * mm_plugin_create_dell (void) { static const gchar *subsystems[] = { "tty", "net", "usbmisc", NULL }; static const guint16 vendors[] = { 0x413c, 0 }; static const MMAsyncMethod custom_init = { .async = G_CALLBACK (dell_custom_init), .finish = G_CALLBACK (dell_custom_init_finish), }; return MM_PLUGIN ( g_object_new (MM_TYPE_PLUGIN_DELL, MM_PLUGIN_NAME, MM_MODULE_NAME, MM_PLUGIN_ALLOWED_SUBSYSTEMS, subsystems, MM_PLUGIN_ALLOWED_VENDOR_IDS, vendors, MM_PLUGIN_ALLOWED_AT, TRUE, MM_PLUGIN_CUSTOM_INIT, &custom_init, MM_PLUGIN_ALLOWED_QCDM, TRUE, MM_PLUGIN_ALLOWED_QMI, TRUE, MM_PLUGIN_ALLOWED_MBIM, TRUE, MM_PLUGIN_XMM_PROBE, TRUE, NULL)); } static void mm_plugin_dell_init (MMPluginDell *self) { } static void mm_plugin_dell_class_init (MMPluginDellClass *klass) { MMPluginClass *plugin_class = MM_PLUGIN_CLASS (klass); plugin_class->create_modem = create_modem; plugin_class->grab_port = grab_port; } ModemManager-1.23.4-dev/src/plugins/dlink/000077500000000000000000000000001456466623000202765ustar00rootroot00000000000000ModemManager-1.23.4-dev/src/plugins/dlink/77-mm-dlink-port-types.rules000066400000000000000000000015421456466623000254430ustar00rootroot00000000000000# do not edit this file, it will be overwritten on update ACTION!="add|change|move|bind", GOTO="mm_dlink_port_types_end" SUBSYSTEMS=="usb", ATTRS{idVendor}=="2001", GOTO="mm_dlink_port_types" GOTO="mm_dlink_port_types_end" LABEL="mm_dlink_port_types" SUBSYSTEMS=="usb", ATTRS{bInterfaceNumber}=="?*", ENV{.MM_USBIFNUM}="$attr{bInterfaceNumber}" # D-Link DWM-222 ATTRS{idVendor}=="2001", ATTRS{idProduct}=="7e35", ENV{.MM_USBIFNUM}=="00", ENV{ID_MM_PORT_IGNORE}="1" ATTRS{idVendor}=="2001", ATTRS{idProduct}=="7e35", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1" ATTRS{idVendor}=="2001", ATTRS{idProduct}=="7e35", ENV{.MM_USBIFNUM}=="02", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1" ATTRS{idVendor}=="2001", ATTRS{idProduct}=="7e35", ENV{.MM_USBIFNUM}=="03", ENV{ID_MM_PORT_IGNORE}="1" LABEL="mm_dlink_port_types_end" ModemManager-1.23.4-dev/src/plugins/dlink/mm-plugin-dlink.c000066400000000000000000000063601456466623000234530ustar00rootroot00000000000000/* -*- 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) 2019 Aleksander Morgado */ #include #include #define _LIBMM_INSIDE_MM #include #include "mm-log-object.h" #include "mm-plugin-common.h" #include "mm-broadband-modem.h" #if defined WITH_QMI # include "mm-broadband-modem-qmi.h" #endif #define MM_TYPE_PLUGIN_DLINK mm_plugin_dlink_get_type () MM_DEFINE_PLUGIN (DLINK, dlink, Dlink) /*****************************************************************************/ static MMBaseModem * create_modem (MMPlugin *self, const gchar *uid, const gchar *physdev, const gchar **drivers, guint16 vendor, guint16 product, guint16 subsystem_vendor, GList *probes, GError **error) { #if defined WITH_QMI if (mm_port_probe_list_has_qmi_port (probes)) { mm_obj_dbg (self, "QMI-powered D-Link modem found..."); return MM_BASE_MODEM (mm_broadband_modem_qmi_new (uid, physdev, drivers, mm_plugin_get_name (self), vendor, product)); } #endif return MM_BASE_MODEM (mm_broadband_modem_new (uid, physdev, drivers, mm_plugin_get_name (self), vendor, product)); } /*****************************************************************************/ MM_PLUGIN_NAMED_CREATOR_SCOPE MMPlugin * mm_plugin_create_dlink (void) { static const gchar *subsystems[] = { "tty", "net", "usbmisc", NULL }; static const guint16 vendor_ids[] = { 0x2001, 0 }; return MM_PLUGIN ( g_object_new (MM_TYPE_PLUGIN_DLINK, MM_PLUGIN_NAME, MM_MODULE_NAME, MM_PLUGIN_ALLOWED_SUBSYSTEMS, subsystems, MM_PLUGIN_ALLOWED_VENDOR_IDS, vendor_ids, MM_PLUGIN_ALLOWED_AT, TRUE, MM_PLUGIN_ALLOWED_QMI, TRUE, NULL)); } static void mm_plugin_dlink_init (MMPluginDlink *self) { } static void mm_plugin_dlink_class_init (MMPluginDlinkClass *klass) { MMPluginClass *plugin_class = MM_PLUGIN_CLASS (klass); plugin_class->create_modem = create_modem; } ModemManager-1.23.4-dev/src/plugins/fibocom/000077500000000000000000000000001456466623000206135ustar00rootroot00000000000000ModemManager-1.23.4-dev/src/plugins/fibocom/77-mm-fibocom-port-types.rules000066400000000000000000000150351456466623000262770ustar00rootroot00000000000000# do not edit this file, it will be overwritten on update ACTION!="add|change|move|bind", GOTO="mm_fibocom_port_types_end" SUBSYSTEMS=="usb", ATTRS{idVendor}=="2cb7", GOTO="mm_fibocom_port_types" SUBSYSTEMS=="usb", ATTRS{idVendor}=="1782", GOTO="mm_fibocom_port_types" GOTO="mm_fibocom_port_types_end" LABEL="mm_fibocom_port_types" SUBSYSTEMS=="usb", ATTRS{bInterfaceNumber}=="?*", ENV{.MM_USBIFNUM}="$attr{bInterfaceNumber}" # Fibocom L850-GL attach APN with toggle modem power ATTRS{idVendor}=="2cb7", ATTRS{idProduct}=="0007", ENV{ID_MM_FIBOCOM_INITIAL_EPS_OFF_ON}="1" # Fibocom L850-GL # ttyACM0 (if #2): AT port # ttyACM1 (if #4): debug port (ignore) # ttyACM2 (if #6): AT port ATTRS{idVendor}=="2cb7", ATTRS{idProduct}=="0007", ENV{.MM_USBIFNUM}=="04", ENV{ID_MM_PORT_IGNORE}="1" # Fibocom NL668-AM # ttyACM0 (if #2): AT port # ttyACM1 (if #3): AT port # ttyACM2 (if #4): debug port (ignore) ATTRS{idVendor}=="2cb7", ATTRS{idProduct}=="01a0", ENV{.MM_USBIFNUM}=="02", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1" ATTRS{idVendor}=="2cb7", ATTRS{idProduct}=="01a0", ENV{.MM_USBIFNUM}=="03", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1" ATTRS{idVendor}=="2cb7", ATTRS{idProduct}=="01a0", ENV{.MM_USBIFNUM}=="04", ENV{ID_MM_PORT_IGNORE}="1" # Fibocom NL668 doesn't support multiplexing properly ATTRS{idVendor}=="2cb7", ATTRS{idProduct}=="01a0", ENV{ID_MM_MAX_MULTIPLEXED_LINKS}="0" # Fibocom FM150 # ttyUSB0 (if #0): QCDM port # ttyUSB1 (if #1): AT port # ttyUSB2 (if #2): AT port # ttyUSB2 (if #3): Ignore ATTRS{idVendor}=="2cb7", ATTRS{idProduct}=="0104", ENV{.MM_USBIFNUM}=="00", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_QCDM}="1" ATTRS{idVendor}=="2cb7", ATTRS{idProduct}=="0104", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1" ATTRS{idVendor}=="2cb7", ATTRS{idProduct}=="0104", ENV{.MM_USBIFNUM}=="02", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1" ATTRS{idVendor}=="2cb7", ATTRS{idProduct}=="0104", ENV{.MM_USBIFNUM}=="03", ENV{ID_MM_PORT_IGNORE}="1" # Fibocom FM101-GL (MBIM) # ttyUSB0 (if #2): AT port # ttyUSB1 (if #3): AT port # ttyUSB2 (if #4): debug port (ignore) # ttyUSB3 (if #5): debug port (ignore) ATTRS{idVendor}=="2cb7", ATTRS{idProduct}=="01a2", ENV{.MM_USBIFNUM}=="02", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1" ATTRS{idVendor}=="2cb7", ATTRS{idProduct}=="01a2", ENV{.MM_USBIFNUM}=="03", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1" ATTRS{idVendor}=="2cb7", ATTRS{idProduct}=="01a2", ENV{.MM_USBIFNUM}=="04", ENV{ID_MM_PORT_IGNORE}="1" ATTRS{idVendor}=="2cb7", ATTRS{idProduct}=="01a2", ENV{.MM_USBIFNUM}=="05", ENV{ID_MM_PORT_IGNORE}="1" ATTRS{idVendor}=="2cb7", ATTRS{idProduct}=="01a2", ENV{.MM_USBIFNUM}=="02", SUBSYSTEM=="tty", ENV{ID_MM_FIBOCOM_FASTBOOT}="1" # Fibocom FM101-GL (ADB) # ttyUSB0 (if #2): debug port (ignore) # ttyUSB1 (if #3): AT port # ttyUSB2 (if #4): debug port (ignore) # ttyUSB3 (if #5): debug port (ignore) # ttyUSB4 (if #6): debug port (ignore) ATTRS{idVendor}=="2cb7", ATTRS{idProduct}=="01a4", ENV{.MM_USBIFNUM}=="02", ENV{ID_MM_PORT_IGNORE}="1" ATTRS{idVendor}=="2cb7", ATTRS{idProduct}=="01a4", ENV{.MM_USBIFNUM}=="03", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1" ATTRS{idVendor}=="2cb7", ATTRS{idProduct}=="01a4", ENV{.MM_USBIFNUM}=="04", ENV{ID_MM_PORT_IGNORE}="1" ATTRS{idVendor}=="2cb7", ATTRS{idProduct}=="01a4", ENV{.MM_USBIFNUM}=="05", ENV{ID_MM_PORT_IGNORE}="1" ATTRS{idVendor}=="2cb7", ATTRS{idProduct}=="01a4", ENV{.MM_USBIFNUM}=="06", ENV{ID_MM_PORT_IGNORE}="1" # Fibocom MA510-GL (GTUSBMODE=31) # ttyUSB0 (if #0): debug port (ignore) # ttyUSB1 (if #1): AT port # ttyUSB2 (if #2): AT port ATTRS{idVendor}=="2cb7", ATTRS{idProduct}=="0106", ENV{.MM_USBIFNUM}=="00", ENV{ID_MM_PORT_IGNORE}="1" ATTRS{idVendor}=="2cb7", ATTRS{idProduct}=="0106", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1" ATTRS{idVendor}=="2cb7", ATTRS{idProduct}=="0106", ENV{.MM_USBIFNUM}=="02", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1" ATTRS{idVendor}=="2cb7", ATTRS{idProduct}=="0106", ENV{ID_MM_FIBOCOM_INITIAL_EPS_CID}="1" # Fibocom MA510-GL (GTUSBMODE=32) # ttyUSB1 (if #0): AT port # ttyUSB2 (if #1): AT port ATTRS{idVendor}=="2cb7", ATTRS{idProduct}=="010a", ENV{.MM_USBIFNUM}=="00", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1" ATTRS{idVendor}=="2cb7", ATTRS{idProduct}=="010a", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1" ATTRS{idVendor}=="2cb7", ATTRS{idProduct}=="010a", ENV{ID_MM_FIBOCOM_INITIAL_EPS_CID}="1" # Fibocom L610 (GTUSBMODE=31) # ttyUSB0 (if #0): AT port # ttyUSB1 (if #1): NV # ttyUSB2 (if #2): MOS # ttyUSB3 (if #3): Diagnostic # ttyUSB4 (if #4): Logging # ttyUSB5 (if #5): AT port # ttyUSB6 (if #6): AT port ATTRS{idVendor}=="1782", ATTRS{idProduct}=="4d10", ENV{.MM_USBIFNUM}=="00", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1" ATTRS{idVendor}=="1782", ATTRS{idProduct}=="4d10", ENV{.MM_USBIFNUM}=="01", ENV{ID_MM_PORT_IGNORE}="1" ATTRS{idVendor}=="1782", ATTRS{idProduct}=="4d10", ENV{.MM_USBIFNUM}=="02", ENV{ID_MM_PORT_IGNORE}="1" ATTRS{idVendor}=="1782", ATTRS{idProduct}=="4d10", ENV{.MM_USBIFNUM}=="03", ENV{ID_MM_PORT_IGNORE}="1" ATTRS{idVendor}=="1782", ATTRS{idProduct}=="4d10", ENV{.MM_USBIFNUM}=="04", ENV{ID_MM_PORT_IGNORE}="1" ATTRS{idVendor}=="1782", ATTRS{idProduct}=="4d10", ENV{.MM_USBIFNUM}=="05", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1" ATTRS{idVendor}=="1782", ATTRS{idProduct}=="4d10", ENV{.MM_USBIFNUM}=="06", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1" # Fibocom L610 (GTUSBMODE={32,33} - ECM/RNDIS) # ttyUSB0 (if #2): AT port # ttyUSB1 (if #3): NV # ttyUSB2 (if #4): MOS # ttyUSB3 (if #5): Diagnostic # ttyUSB4 (if #6): Logging # ttyUSB5 (if #7): AT port # ttyUSB6 (if #8): AT port ATTRS{idVendor}=="1782", ATTRS{idProduct}=="4d11", ENV{.MM_USBIFNUM}=="02", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1" ATTRS{idVendor}=="1782", ATTRS{idProduct}=="4d11", ENV{.MM_USBIFNUM}=="03", ENV{ID_MM_PORT_IGNORE}="1" ATTRS{idVendor}=="1782", ATTRS{idProduct}=="4d11", ENV{.MM_USBIFNUM}=="04", ENV{ID_MM_PORT_IGNORE}="1" ATTRS{idVendor}=="1782", ATTRS{idProduct}=="4d11", ENV{.MM_USBIFNUM}=="05", ENV{ID_MM_PORT_IGNORE}="1" ATTRS{idVendor}=="1782", ATTRS{idProduct}=="4d11", ENV{.MM_USBIFNUM}=="06", ENV{ID_MM_PORT_IGNORE}="1" ATTRS{idVendor}=="1782", ATTRS{idProduct}=="4d11", ENV{.MM_USBIFNUM}=="07", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1" ATTRS{idVendor}=="1782", ATTRS{idProduct}=="4d11", ENV{.MM_USBIFNUM}=="08", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1" LABEL="mm_fibocom_port_types_end" ModemManager-1.23.4-dev/src/plugins/fibocom/mm-broadband-bearer-fibocom-ecm.c000066400000000000000000000424121456466623000267210ustar00rootroot00000000000000/* -*- 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) 2022 Disruptive Technologies Research AS */ #include #include "mm-broadband-bearer-fibocom-ecm.h" #include "mm-broadband-modem-fibocom.h" #include "mm-base-modem-at.h" #include "mm-iface-modem-3gpp.h" #include "mm-log.h" G_DEFINE_TYPE (MMBroadbandBearerFibocomEcm, mm_broadband_bearer_fibocom_ecm, MM_TYPE_BROADBAND_BEARER) /*****************************************************************************/ /* Common helper functions */ static gboolean parse_gtrndis_read_response (const gchar *response, guint *state, guint *cid, GError **error) { g_autoptr(GRegex) r = NULL; g_autoptr(GMatchInfo) match_info = NULL; r = g_regex_new ("\\+GTRNDIS:\\s*(\\d+)(?:,(\\d+))?", G_REGEX_DOLLAR_ENDONLY | G_REGEX_RAW, 0, NULL); g_assert (r != NULL); if (!g_regex_match (r, response, 0, &match_info)) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Invalid +GTRNDIS response: %s", response); return FALSE; } if (!mm_get_uint_from_match_info (match_info, 1, state)) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Failed to match state in +GTRNDIS response: %s", response); return FALSE; } if (*state) { if (!mm_get_uint_from_match_info (match_info, 2, cid)) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Failed to match cid in +GTRNDIS response: %s", response); return FALSE; } } else { *cid = 0; } return TRUE; } /*****************************************************************************/ /* Connection status monitoring */ static MMBearerConnectionStatus load_connection_status_finish (MMBaseBearer *bearer, GAsyncResult *res, GError **error) { GError *inner_error = NULL; gssize value; value = g_task_propagate_int (G_TASK (res), &inner_error); if (inner_error) { g_propagate_error (error, inner_error); return MM_BEARER_CONNECTION_STATUS_UNKNOWN; } return (MMBearerConnectionStatus) value; } static void gtrndis_query_ready (MMBaseModem *modem, GAsyncResult *res, GTask *task) { MMBaseBearer *bearer; GError *error = NULL; const gchar *result; guint state; guint cid; bearer = g_task_get_source_object (task); result = mm_base_modem_at_command_finish (modem, res, &error); if (!result) g_task_return_error (task, error); else if (!parse_gtrndis_read_response (result, &state, &cid, &error)) g_task_return_error (task, error); else if (!state || (gint) cid != mm_base_bearer_get_profile_id (bearer)) g_task_return_int (task, MM_BEARER_CONNECTION_STATUS_DISCONNECTED); else g_task_return_int (task, MM_BEARER_CONNECTION_STATUS_CONNECTED); g_object_unref (task); } static void load_connection_status (MMBaseBearer *bearer, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; MMBaseModem *modem = NULL; task = g_task_new (bearer, NULL, callback, user_data); g_object_get (MM_BASE_BEARER (bearer), MM_BASE_BEARER_MODEM, &modem, NULL); mm_base_modem_at_command (modem, "+GTRNDIS?", 3, FALSE, (GAsyncReadyCallback) gtrndis_query_ready, task); g_object_unref (modem); } /*****************************************************************************/ /* 3GPP Connect */ typedef struct { MMBroadbandModem *modem; MMPortSerialAt *primary; MMPortSerialAt *secondary; MMBearerIpFamily ip_family; } ConnectContext; static void connect_context_free (ConnectContext *ctx) { g_clear_object (&ctx->modem); g_clear_object (&ctx->primary); g_clear_object (&ctx->secondary); g_slice_free (ConnectContext, ctx); } static MMBearerConnectResult * connect_3gpp_finish (MMBroadbandBearer *self, GAsyncResult *res, GError **error) { return g_task_propagate_pointer (G_TASK (res), error); } static void parent_connect_3gpp_ready (MMBroadbandBearer *self, GAsyncResult *res, GTask *task) { GError *error = NULL; MMBearerConnectResult *result; result = MM_BROADBAND_BEARER_CLASS (mm_broadband_bearer_fibocom_ecm_parent_class)->connect_3gpp_finish (self, res, &error); if (result) g_task_return_pointer (task, result, (GDestroyNotify) mm_bearer_connect_result_unref); else g_task_return_error (task, error); g_object_unref (task); } static void disconnect_3gpp_ready (MMBroadbandBearer *self, GAsyncResult *res, GTask *task) { GError *error = NULL; gboolean result; ConnectContext *ctx; result = MM_BROADBAND_BEARER_GET_CLASS (self)->disconnect_3gpp_finish (self, res, &error); if (!result) { g_task_return_error (task, error); g_object_unref (task); return; } ctx = g_task_get_task_data (task); MM_BROADBAND_BEARER_CLASS (mm_broadband_bearer_fibocom_ecm_parent_class)->connect_3gpp ( self, ctx->modem, ctx->primary, ctx->secondary, g_task_get_cancellable (task), (GAsyncReadyCallback) parent_connect_3gpp_ready, task); } static void gtrndis_check_ready (MMBaseModem *modem, GAsyncResult *res, GTask *task) { MMBroadbandBearer *self; ConnectContext *ctx; GError *error = NULL; const gchar *response; guint state; guint cid; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); response = mm_base_modem_at_command_finish (modem, res, &error); if (!response) { g_task_return_error (task, error); g_object_unref (task); return; } if (!parse_gtrndis_read_response (response, &state, &cid, &error)) { g_task_return_error (task, error); g_object_unref (task); return; } if (state) { /* RNDIS is already active, disconnect first. */ mm_obj_dbg (self, "RNDIS active, tearing down existing connection..."); MM_BROADBAND_BEARER_GET_CLASS (self)->disconnect_3gpp ( MM_BROADBAND_BEARER (self), ctx->modem, ctx->primary, ctx->secondary, NULL, /* data port */ cid, (GAsyncReadyCallback) disconnect_3gpp_ready, task); return; } /* Execute the regular connection flow if RNDIS is inactive. */ mm_obj_dbg (self, "RNDIS inactive"); MM_BROADBAND_BEARER_CLASS (mm_broadband_bearer_fibocom_ecm_parent_class)->connect_3gpp ( MM_BROADBAND_BEARER (self), ctx->modem, ctx->primary, ctx->secondary, g_task_get_cancellable (task), (GAsyncReadyCallback) parent_connect_3gpp_ready, task); } static void connect_3gpp (MMBroadbandBearer *self, MMBroadbandModem *modem, MMPortSerialAt *primary, MMPortSerialAt *secondary, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { ConnectContext *ctx; GTask *task; ctx = g_slice_new0 (ConnectContext); ctx->modem = g_object_ref (modem); ctx->primary = g_object_ref (primary); ctx->secondary = secondary ? g_object_ref (secondary) : NULL; ctx->ip_family = mm_bearer_properties_get_ip_type (mm_base_bearer_peek_config (MM_BASE_BEARER (self))); mm_3gpp_normalize_ip_family (&ctx->ip_family, TRUE); task = g_task_new (self, cancellable, callback, user_data); g_task_set_task_data (task, ctx, (GDestroyNotify) connect_context_free); /* First, we must check whether RNDIS is already active */ mm_base_modem_at_command (MM_BASE_MODEM (modem), "+GTRNDIS?", 3, FALSE, /* allow_cached */ (GAsyncReadyCallback) gtrndis_check_ready, task); } /*****************************************************************************/ /* Dial context and task */ typedef struct { MMBroadbandModem *modem; MMPortSerialAt *primary; guint cid; MMPort *data; } DialContext; static void dial_task_free (DialContext *ctx) { g_object_unref (ctx->modem); g_object_unref (ctx->primary); if (ctx->data) g_object_unref (ctx->data); g_slice_free (DialContext, ctx); } static GTask * dial_task_new (MMBroadbandBearerFibocomEcm *self, MMBroadbandModem *modem, MMPortSerialAt *primary, guint cid, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { DialContext *ctx; GTask *task; ctx = g_slice_new0 (DialContext); ctx->modem = g_object_ref (modem); ctx->primary = g_object_ref (primary); ctx->cid = cid; task = g_task_new (self, cancellable, callback, user_data); g_task_set_task_data (task, ctx, (GDestroyNotify) dial_task_free); ctx->data = mm_base_modem_get_best_data_port (MM_BASE_MODEM (modem), MM_PORT_TYPE_NET); if (!ctx->data) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_NOT_FOUND, "No valid data port found to launch connection"); g_object_unref (task); return NULL; } return task; } /*****************************************************************************/ /* 3GPP Dialing (sub-step of the 3GPP Connection sequence) */ static MMPort * dial_3gpp_finish (MMBroadbandBearer *self, GAsyncResult *res, GError **error) { return g_task_propagate_pointer (G_TASK (res), error); } static void gtrndis_verify_ready (MMBaseModem *modem, GAsyncResult *res, GTask *task) { DialContext *ctx; GError *error = NULL; const gchar *response; ctx = g_task_get_task_data (task); response = mm_base_modem_at_command_finish (modem, res, &error); if (!response) g_task_return_error (task, error); else { response = mm_strip_tag (response, "+GTRNDIS:"); if (strtol (response, NULL, 10) != 1) g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Connection status verification failed"); else g_task_return_pointer (task, g_object_ref (ctx->data), g_object_unref); } g_object_unref (task); } static void gtrndis_activate_ready (MMBaseModem *modem, GAsyncResult *res, GTask *task) { GError *error = NULL; if (!mm_base_modem_at_command_finish (modem, res, &error)) { g_task_return_error (task, error); g_object_unref (task); return; } mm_base_modem_at_command (modem, "+GTRNDIS?", 6, /* timeout [s] */ FALSE, /* allow_cached */ (GAsyncReadyCallback) gtrndis_verify_ready, task); } static void dial_3gpp (MMBroadbandBearer *self, MMBaseModem *modem, MMPortSerialAt *primary, guint cid, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; g_autofree gchar *cmd = NULL; task = dial_task_new (MM_BROADBAND_BEARER_FIBOCOM_ECM (self), MM_BROADBAND_MODEM (modem), primary, cid, cancellable, callback, user_data); if (!task) return; cmd = g_strdup_printf ("+GTRNDIS=1,%u", cid); mm_base_modem_at_command (modem, cmd, MM_BASE_BEARER_DEFAULT_CONNECTION_TIMEOUT, FALSE, /* allow_cached */ (GAsyncReadyCallback) gtrndis_activate_ready, task); } /*****************************************************************************/ /* 3GPP Disconnect sequence */ static gboolean disconnect_3gpp_finish (MMBroadbandBearer *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void gtrndis_deactivate_ready (MMBaseModem *modem, GAsyncResult *res, GTask *task) { GError *error = NULL; if (!mm_base_modem_at_command_finish (modem, res, &error)) g_task_return_error (task, error); else g_task_return_boolean (task, TRUE); g_object_unref (task); } static void disconnect_3gpp (MMBroadbandBearer *self, MMBroadbandModem *modem, MMPortSerialAt *primary, MMPortSerialAt *secondary, MMPort *data, guint cid, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; g_autofree gchar *cmd = NULL; task = g_task_new (self, NULL, callback, user_data); cmd = g_strdup_printf ("+GTRNDIS=0,%u", cid); mm_base_modem_at_command (MM_BASE_MODEM (modem), cmd, MM_BASE_BEARER_DEFAULT_DISCONNECTION_TIMEOUT, FALSE, /* allow_cached */ (GAsyncReadyCallback) gtrndis_deactivate_ready, task); } /*****************************************************************************/ MMBaseBearer * mm_broadband_bearer_fibocom_ecm_new_finish (GAsyncResult *res, GError **error) { GObject *bearer; GObject *source; source = g_async_result_get_source_object (res); bearer = g_async_initable_new_finish (G_ASYNC_INITABLE (source), res, error); g_object_unref (source); if (!bearer) return NULL; /* Only export valid bearers */ mm_base_bearer_export (MM_BASE_BEARER (bearer)); return MM_BASE_BEARER (bearer); } void mm_broadband_bearer_fibocom_ecm_new (MMBroadbandModemFibocom *modem, MMBearerProperties *config, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { g_async_initable_new_async ( MM_TYPE_BROADBAND_BEARER_FIBOCOM_ECM, G_PRIORITY_DEFAULT, cancellable, callback, user_data, MM_BASE_BEARER_MODEM, modem, MM_BASE_BEARER_CONFIG, config, NULL); } static void mm_broadband_bearer_fibocom_ecm_init (MMBroadbandBearerFibocomEcm *self) { } static void mm_broadband_bearer_fibocom_ecm_class_init (MMBroadbandBearerFibocomEcmClass *klass) { MMBaseBearerClass *base_bearer_class = MM_BASE_BEARER_CLASS (klass); MMBroadbandBearerClass *broadband_bearer_class = MM_BROADBAND_BEARER_CLASS (klass); base_bearer_class->load_connection_status = load_connection_status; base_bearer_class->load_connection_status_finish = load_connection_status_finish; broadband_bearer_class->connect_3gpp = connect_3gpp; broadband_bearer_class->connect_3gpp_finish = connect_3gpp_finish; broadband_bearer_class->dial_3gpp = dial_3gpp; broadband_bearer_class->dial_3gpp_finish = dial_3gpp_finish; broadband_bearer_class->disconnect_3gpp = disconnect_3gpp; broadband_bearer_class->disconnect_3gpp_finish = disconnect_3gpp_finish; } ModemManager-1.23.4-dev/src/plugins/fibocom/mm-broadband-bearer-fibocom-ecm.h000066400000000000000000000051451456466623000267300ustar00rootroot00000000000000/* -*- 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) 2022 Disruptive Technologies Research AS */ #ifndef MM_BROADBAND_BEARER_FIBOCOM_ECM_H #define MM_BROADBAND_BEARER_FIBOCOM_ECM_H #include "mm-broadband-bearer.h" #include "mm-broadband-modem-fibocom.h" #define MM_TYPE_BROADBAND_BEARER_FIBOCOM_ECM (mm_broadband_bearer_fibocom_ecm_get_type ()) #define MM_BROADBAND_BEARER_FIBOCOM_ECM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_BROADBAND_BEARER_FIBOCOM_ECM, MMBroadbandBearerFibocomEcm)) #define MM_BROADBAND_BEARER_FIBOCOM_ECM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_BROADBAND_BEARER_FIBOCOM_ECM, MMBroadbandBearerFibocomEcmClass)) #define MM_IS_BROADBAND_BEARER_FIBOCOM_ECM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_BROADBAND_BEARER_FIBOCOM_ECM)) #define MM_IS_BROADBAND_BEARER_FIBOCOM_ECM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_BROADBAND_BEARER_FIBOCOM_ECM)) #define MM_BROADBAND_BEARER_FIBOCOM_ECM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_BROADBAND_BEARER_FIBOCOM_ECM, MMBroadbandBearerFibocomEcmClass)) typedef struct _MMBroadbandBearerFibocomEcm MMBroadbandBearerFibocomEcm; typedef struct _MMBroadbandBearerFibocomEcmClass MMBroadbandBearerFibocomEcmClass; struct _MMBroadbandBearerFibocomEcm { MMBroadbandBearer parent; }; struct _MMBroadbandBearerFibocomEcmClass { MMBroadbandBearerClass parent; }; GType mm_broadband_bearer_fibocom_ecm_get_type (void); void mm_broadband_bearer_fibocom_ecm_new (MMBroadbandModemFibocom *modem, MMBearerProperties *properties, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); MMBaseBearer *mm_broadband_bearer_fibocom_ecm_new_finish (GAsyncResult *res, GError **error); #endif /* MM_BROADBAND_BEARER_FIBOCOM_ECM_H */ ModemManager-1.23.4-dev/src/plugins/fibocom/mm-broadband-modem-fibocom.c000066400000000000000000000711001456466623000260140ustar00rootroot00000000000000/* -*- 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) 2022 Disruptive Technologies Research AS */ #include #include "mm-broadband-modem-fibocom.h" #include "mm-broadband-bearer-fibocom-ecm.h" #include "mm-broadband-modem.h" #include "mm-base-modem-at.h" #include "mm-iface-modem.h" #include "mm-iface-modem-3gpp.h" #include "mm-iface-modem-3gpp-profile-manager.h" #include "mm-log.h" #include "mm-shared-fibocom.h" static void iface_modem_init (MMIfaceModem *iface); static void iface_modem_3gpp_init (MMIfaceModem3gpp *iface); static void iface_modem_3gpp_profile_manager_init (MMIfaceModem3gppProfileManager *iface); static void iface_modem_firmware_init (MMIfaceModemFirmware *iface); static MMIfaceModem3gppProfileManager *iface_modem_3gpp_profile_manager_parent; G_DEFINE_TYPE_EXTENDED (MMBroadbandModemFibocom, mm_broadband_modem_fibocom, MM_TYPE_BROADBAND_MODEM, 0, G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM, iface_modem_init) G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_3GPP, iface_modem_3gpp_init) G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_3GPP_PROFILE_MANAGER, iface_modem_3gpp_profile_manager_init) G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_FIRMWARE, iface_modem_firmware_init)) typedef enum { FEATURE_SUPPORT_UNKNOWN, FEATURE_NOT_SUPPORTED, FEATURE_SUPPORTED, } FeatureSupport; struct _MMBroadbandModemFibocomPrivate { FeatureSupport gtrndis_support; GRegex *sim_ready_regex; FeatureSupport initial_eps_bearer_support; gint initial_eps_bearer_cid; }; /*****************************************************************************/ /* Create Bearer (Modem interface) */ static MMBaseBearer * modem_create_bearer_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { return g_task_propagate_pointer (G_TASK (res), error); } static void broadband_bearer_fibocom_ecm_new_ready (GObject *source, GAsyncResult *res, GTask *task) { MMBaseBearer *bearer = NULL; GError *error = NULL; bearer = mm_broadband_bearer_fibocom_ecm_new_finish (res, &error); if (!bearer) g_task_return_error (task, error); else g_task_return_pointer (task, bearer, g_object_unref); g_object_unref (task); } static void broadband_bearer_new_ready (GObject *source, GAsyncResult *res, GTask *task) { MMBaseBearer *bearer = NULL; GError *error = NULL; bearer = mm_broadband_bearer_new_finish (res, &error); if (!bearer) g_task_return_error (task, error); else g_task_return_pointer (task, bearer, g_object_unref); g_object_unref (task); } static void common_create_bearer (GTask *task) { MMBroadbandModemFibocom *self; self = g_task_get_source_object (task); switch (self->priv->gtrndis_support) { case FEATURE_SUPPORTED: mm_obj_dbg (self, "+GTRNDIS supported, creating Fibocom ECM bearer"); mm_broadband_bearer_fibocom_ecm_new (self, g_task_get_task_data (task), NULL, /* cancellable */ (GAsyncReadyCallback) broadband_bearer_fibocom_ecm_new_ready, task); return; case FEATURE_NOT_SUPPORTED: mm_obj_dbg (self, "+GTRNDIS not supported, creating generic PPP bearer"); mm_broadband_bearer_new (MM_BROADBAND_MODEM (self), g_task_get_task_data (task), NULL, /* cancellable */ (GAsyncReadyCallback) broadband_bearer_new_ready, task); return; case FEATURE_SUPPORT_UNKNOWN: default: g_assert_not_reached (); } } static void gtrndis_test_ready (MMBaseModem *_self, GAsyncResult *res, GTask *task) { MMBroadbandModemFibocom *self = MM_BROADBAND_MODEM_FIBOCOM (_self); if (!mm_base_modem_at_command_finish (_self, res, NULL)) { mm_obj_dbg (self, "+GTRNDIS unsupported"); self->priv->gtrndis_support = FEATURE_NOT_SUPPORTED; } else { mm_obj_dbg (self, "+GTRNDIS supported"); self->priv->gtrndis_support = FEATURE_SUPPORTED; } /* Go on and create the bearer */ common_create_bearer (task); } static void modem_create_bearer (MMIfaceModem *_self, MMBearerProperties *properties, GAsyncReadyCallback callback, gpointer user_data) { MMBroadbandModemFibocom *self = MM_BROADBAND_MODEM_FIBOCOM (_self); GTask *task; task = g_task_new (self, NULL, callback, user_data); g_task_set_task_data (task, g_object_ref (properties), g_object_unref); if (self->priv->gtrndis_support != FEATURE_SUPPORT_UNKNOWN) { common_create_bearer (task); return; } if (!mm_base_modem_peek_best_data_port (MM_BASE_MODEM (self), MM_PORT_TYPE_NET)) { mm_obj_dbg (self, "skipping +GTRNDIS check as no data port is available"); self->priv->gtrndis_support = FEATURE_NOT_SUPPORTED; common_create_bearer (task); return; } mm_obj_dbg (self, "checking +GTRNDIS support..."); mm_base_modem_at_command (MM_BASE_MODEM (self), "+GTRNDIS=?", 6, /* timeout [s] */ TRUE, /* allow_cached */ (GAsyncReadyCallback) gtrndis_test_ready, task); } /*****************************************************************************/ /* Reset / Power (Modem interface) */ static gboolean modem_common_power_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { return !!mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, error); } static void modem_reset (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data) { mm_base_modem_at_command (MM_BASE_MODEM (self), "+CFUN=15", 15, FALSE, callback, user_data); } static void modem_power_down (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data) { mm_base_modem_at_command (MM_BASE_MODEM (self), "+CFUN=4", 15, FALSE, callback, user_data); } static void modem_power_off (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data) { mm_base_modem_at_command (MM_BASE_MODEM (self), "+CPWROFF", 3, FALSE, callback, user_data); } /*****************************************************************************/ /* Load initial EPS bearer properties (as agreed with network) */ static MMBearerProperties * modem_3gpp_load_initial_eps_bearer_finish (MMIfaceModem3gpp *self, GAsyncResult *res, GError **error) { return MM_BEARER_PROPERTIES (g_task_propagate_pointer (G_TASK (res), error)); } static void load_initial_eps_cgcontrdp_ready (MMBaseModem *self, GAsyncResult *res, GTask *task) { GError *error = NULL; const gchar *response; g_autofree gchar *apn = NULL; MMBearerProperties *properties; response = mm_base_modem_at_command_finish (self, res, &error); if (!response || !mm_3gpp_parse_cgcontrdp_response (response, NULL, NULL, &apn, NULL, NULL, NULL, NULL, NULL, &error)) g_task_return_error (task, error); else { properties = mm_bearer_properties_new (); mm_bearer_properties_set_apn (properties, apn); g_task_return_pointer (task, properties, g_object_unref); } g_object_unref (task); } static void modem_3gpp_load_initial_eps_bearer (MMIfaceModem3gpp *_self, GAsyncReadyCallback callback, gpointer user_data) { MMBroadbandModemFibocom *self = MM_BROADBAND_MODEM_FIBOCOM (_self); GTask *task; g_autofree gchar *cmd = NULL; task = g_task_new (self, NULL, callback, user_data); if (self->priv->initial_eps_bearer_support != FEATURE_SUPPORTED) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "Initial EPS bearer context ID unknown"); g_object_unref (task); return; } g_assert (self->priv->initial_eps_bearer_cid >= 0); cmd = g_strdup_printf ("+CGCONTRDP=%d", self->priv->initial_eps_bearer_cid); mm_base_modem_at_command (MM_BASE_MODEM (self), cmd, 3, FALSE, (GAsyncReadyCallback) load_initial_eps_cgcontrdp_ready, task); } /*****************************************************************************/ /* Load initial EPS bearer settings (currently configured in modem) */ static MMBearerProperties * modem_3gpp_load_initial_eps_bearer_settings_finish (MMIfaceModem3gpp *self, GAsyncResult *res, GError **error) { return MM_BEARER_PROPERTIES (g_task_propagate_pointer (G_TASK (res), error)); } static void load_initial_eps_bearer_get_profile_ready (MMIfaceModem3gppProfileManager *self, GAsyncResult *res, GTask *task) { GError *error = NULL; g_autoptr(MM3gppProfile) profile = NULL; MMBearerProperties *properties; profile = mm_iface_modem_3gpp_profile_manager_get_profile_finish (self, res, &error); if (!profile) { g_task_return_error (task, error); g_object_unref (task); return; } properties = mm_bearer_properties_new_from_profile (profile, &error); if (!properties) g_task_return_error (task, error); else g_task_return_pointer (task, properties, g_object_unref); g_object_unref (task); } static void modem_3gpp_load_initial_eps_bearer_settings (MMIfaceModem3gpp *_self, GAsyncReadyCallback callback, gpointer user_data) { MMBroadbandModemFibocom *self = MM_BROADBAND_MODEM_FIBOCOM (_self); MMPortSerialAt *port; MMKernelDevice *device; GTask *task; /* Initial EPS bearer CID initialization run once only */ if (G_UNLIKELY (self->priv->initial_eps_bearer_support == FEATURE_SUPPORT_UNKNOWN)) { /* There doesn't seem to be a programmatic way to find the initial EPS * bearer's CID, so we'll use a udev variable. */ port = mm_base_modem_peek_port_primary (MM_BASE_MODEM (self)); device = mm_port_peek_kernel_device (MM_PORT (port)); if (mm_kernel_device_has_global_property (device, "ID_MM_FIBOCOM_INITIAL_EPS_CID")) { self->priv->initial_eps_bearer_support = FEATURE_SUPPORTED; self->priv->initial_eps_bearer_cid = mm_kernel_device_get_global_property_as_int ( device, "ID_MM_FIBOCOM_INITIAL_EPS_CID"); } else self->priv->initial_eps_bearer_support = FEATURE_NOT_SUPPORTED; } task = g_task_new (self, NULL, callback, user_data); if (self->priv->initial_eps_bearer_support != FEATURE_SUPPORTED) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "Initial EPS bearer context ID unknown"); g_object_unref (task); return; } g_assert (self->priv->initial_eps_bearer_cid >= 0); mm_iface_modem_3gpp_profile_manager_get_profile ( MM_IFACE_MODEM_3GPP_PROFILE_MANAGER (self), self->priv->initial_eps_bearer_cid, (GAsyncReadyCallback) load_initial_eps_bearer_get_profile_ready, task); } /*****************************************************************************/ /* Set initial EPS bearer settings */ typedef enum { SET_INITIAL_EPS_BEARER_SETTINGS_STEP_LOAD_POWER_STATE = 0, SET_INITIAL_EPS_BEARER_SETTINGS_STEP_POWER_DOWN, SET_INITIAL_EPS_BEARER_SETTINGS_STEP_MODIFY_PROFILE, SET_INITIAL_EPS_BEARER_SETTINGS_STEP_POWER_UP, SET_INITIAL_EPS_BEARER_SETTINGS_STEP_FINISH, } SetInitialEpsStep; typedef struct { MM3gppProfile *profile; SetInitialEpsStep step; MMModemPowerState power_state; } SetInitialEpsContext; static void set_initial_eps_context_free (SetInitialEpsContext *ctx) { g_object_unref (ctx->profile); g_slice_free (SetInitialEpsContext, ctx); } static gboolean modem_3gpp_set_initial_eps_bearer_settings_finish (MMIfaceModem3gpp *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void set_initial_eps_step (GTask *task); static void set_initial_eps_bearer_power_up_ready (MMBaseModem *_self, GAsyncResult *res, GTask *task) { MMBroadbandModemFibocom *self = MM_BROADBAND_MODEM_FIBOCOM (_self); SetInitialEpsContext *ctx; GError *error = NULL; ctx = g_task_get_task_data (task); if (!MM_IFACE_MODEM_GET_INTERFACE (self)->modem_power_up_finish (MM_IFACE_MODEM (self), res, &error)) { g_prefix_error (&error, "Couldn't power up modem: "); g_task_return_error (task, error); g_object_unref (task); return; } ctx->step++; set_initial_eps_step (task); } static void set_initial_eps_bearer_modify_profile_ready (MMIfaceModem3gppProfileManager *self, GAsyncResult *res, GTask *task) { GError *error = NULL; SetInitialEpsContext *ctx; g_autoptr(MM3gppProfile) stored = NULL; ctx = g_task_get_task_data (task); stored = mm_iface_modem_3gpp_profile_manager_set_profile_finish (self, res, &error); if (!stored) { g_task_return_error (task, error); g_object_unref (task); return; } ctx->step++; set_initial_eps_step (task); } static void set_initial_eps_bearer_power_down_ready (MMBaseModem *self, GAsyncResult *res, GTask *task) { SetInitialEpsContext *ctx; GError *error = NULL; ctx = g_task_get_task_data (task); if (!MM_IFACE_MODEM_GET_INTERFACE (self)->modem_power_down_finish (MM_IFACE_MODEM (self), res, &error)) { g_prefix_error (&error, "Couldn't power down modem: "); g_task_return_error (task, error); g_object_unref (task); return; } ctx->step++; set_initial_eps_step (task); } static void set_initial_eps_bearer_load_power_state_ready (MMBaseModem *self, GAsyncResult *res, GTask *task) { SetInitialEpsContext *ctx; GError *error = NULL; ctx = g_task_get_task_data (task); ctx->power_state = MM_IFACE_MODEM_GET_INTERFACE (self)->load_power_state_finish (MM_IFACE_MODEM (self), res, &error); if (error) { g_task_return_error (task, error); g_object_unref (task); return; } ctx->step++; set_initial_eps_step (task); } static void set_initial_eps_step (GTask *task) { MMBroadbandModemFibocom *self; SetInitialEpsContext *ctx; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); switch (ctx->step) { case SET_INITIAL_EPS_BEARER_SETTINGS_STEP_LOAD_POWER_STATE: mm_obj_dbg (self, "querying current power state..."); g_assert (MM_IFACE_MODEM_GET_INTERFACE (self)->load_power_state); g_assert (MM_IFACE_MODEM_GET_INTERFACE (self)->load_power_state_finish); MM_IFACE_MODEM_GET_INTERFACE (self)->load_power_state ( MM_IFACE_MODEM (self), (GAsyncReadyCallback) set_initial_eps_bearer_load_power_state_ready, task); return; case SET_INITIAL_EPS_BEARER_SETTINGS_STEP_POWER_DOWN: if (ctx->power_state == MM_MODEM_POWER_STATE_ON) { mm_obj_dbg (self, "powering down before changing initial EPS bearer settings..."); g_assert (MM_IFACE_MODEM_GET_INTERFACE (self)->modem_power_down); g_assert (MM_IFACE_MODEM_GET_INTERFACE (self)->modem_power_down_finish); MM_IFACE_MODEM_GET_INTERFACE (self)->modem_power_down ( MM_IFACE_MODEM (self), (GAsyncReadyCallback) set_initial_eps_bearer_power_down_ready, task); return; } ctx->step++; /* fall through */ case SET_INITIAL_EPS_BEARER_SETTINGS_STEP_MODIFY_PROFILE: mm_obj_dbg (self, "modifying initial EPS bearer settings profile..."); mm_iface_modem_3gpp_profile_manager_set_profile (MM_IFACE_MODEM_3GPP_PROFILE_MANAGER (self), ctx->profile, "profile-id", TRUE, (GAsyncReadyCallback) set_initial_eps_bearer_modify_profile_ready, task); return; case SET_INITIAL_EPS_BEARER_SETTINGS_STEP_POWER_UP: if (ctx->power_state == MM_MODEM_POWER_STATE_ON) { mm_obj_dbg (self, "powering up after changing initial EPS bearer settings..."); g_assert (MM_IFACE_MODEM_GET_INTERFACE (self)->modem_power_up); g_assert (MM_IFACE_MODEM_GET_INTERFACE (self)->modem_power_up_finish); MM_IFACE_MODEM_GET_INTERFACE (self)->modem_power_up ( MM_IFACE_MODEM (self), (GAsyncReadyCallback) set_initial_eps_bearer_power_up_ready, task); return; } ctx->step++; /* fall through */ case SET_INITIAL_EPS_BEARER_SETTINGS_STEP_FINISH: g_task_return_boolean (task, TRUE); g_object_unref (task); return; default: g_assert_not_reached (); } } static void modem_3gpp_set_initial_eps_bearer_settings (MMIfaceModem3gpp *_self, MMBearerProperties *properties, GAsyncReadyCallback callback, gpointer user_data) { MMBroadbandModemFibocom *self = MM_BROADBAND_MODEM_FIBOCOM (_self); GTask *task; MM3gppProfile *profile; MMBearerIpFamily ip_family; SetInitialEpsContext *ctx; task = g_task_new (self, NULL, callback, user_data); if (self->priv->initial_eps_bearer_support != FEATURE_SUPPORTED) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "Initial EPS bearer context ID unknown"); g_object_unref (task); return; } profile = mm_bearer_properties_peek_3gpp_profile (properties); g_assert (self->priv->initial_eps_bearer_cid >= 0); mm_3gpp_profile_set_profile_id (profile, self->priv->initial_eps_bearer_cid); ip_family = mm_3gpp_profile_get_ip_type (profile); if (ip_family == MM_BEARER_IP_FAMILY_NONE || ip_family == MM_BEARER_IP_FAMILY_ANY) mm_3gpp_profile_set_ip_type (profile, MM_BEARER_IP_FAMILY_IPV4); /* Setup context */ ctx = g_slice_new0 (SetInitialEpsContext); ctx->profile = g_object_ref (profile); ctx->step = SET_INITIAL_EPS_BEARER_SETTINGS_STEP_LOAD_POWER_STATE; g_task_set_task_data (task, ctx, (GDestroyNotify) set_initial_eps_context_free); set_initial_eps_step (task); } /*****************************************************************************/ /* Deactivate profile (3GPP profile management interface) */ static gboolean modem_3gpp_profile_manager_deactivate_profile_finish (MMIfaceModem3gppProfileManager *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void profile_manager_parent_deactivate_profile_ready (MMIfaceModem3gppProfileManager *self, GAsyncResult *res, GTask *task) { GError *error = NULL; if (iface_modem_3gpp_profile_manager_parent->deactivate_profile_finish(self, res, &error)) g_task_return_boolean (task, TRUE); else g_task_return_error (task, error); g_object_unref (task); } static void modem_3gpp_profile_manager_deactivate_profile (MMIfaceModem3gppProfileManager *_self, MM3gppProfile *profile, GAsyncReadyCallback callback, gpointer user_data) { MMBroadbandModemFibocom *self = MM_BROADBAND_MODEM_FIBOCOM (_self); GTask *task; gint profile_id; task = g_task_new (self, NULL, callback, user_data); profile_id = mm_3gpp_profile_get_profile_id (profile); if (self->priv->initial_eps_bearer_support == FEATURE_SUPPORTED) { g_assert (self->priv->initial_eps_bearer_cid >= 0); if (self->priv->initial_eps_bearer_cid == profile_id) { mm_obj_dbg (self, "skipping profile deactivation (initial EPS bearer)"); g_task_return_boolean (task, TRUE); g_object_unref (task); return; } } iface_modem_3gpp_profile_manager_parent->deactivate_profile ( _self, profile, (GAsyncReadyCallback) profile_manager_parent_deactivate_profile_ready, task); } /*****************************************************************************/ static void setup_ports (MMBroadbandModem *_self) { MMBroadbandModemFibocom *self = (MM_BROADBAND_MODEM_FIBOCOM (_self)); MMPortSerialAt *ports[2]; guint i; /* Call parent's setup ports first always */ MM_BROADBAND_MODEM_CLASS (mm_broadband_modem_fibocom_parent_class)->setup_ports (_self); ports[0] = mm_base_modem_peek_port_primary (MM_BASE_MODEM (self)); ports[1] = mm_base_modem_peek_port_secondary (MM_BASE_MODEM (self)); for (i = 0; i < G_N_ELEMENTS (ports); i++) { if (!ports[i]) continue; mm_port_serial_at_add_unsolicited_msg_handler ( ports[i], self->priv->sim_ready_regex, NULL, NULL, NULL); } } /*****************************************************************************/ MMBroadbandModemFibocom * mm_broadband_modem_fibocom_new (const gchar *device, const gchar *physdev, const gchar **drivers, const gchar *plugin, guint16 vendor_id, guint16 product_id) { return g_object_new (MM_TYPE_BROADBAND_MODEM_FIBOCOM, MM_BASE_MODEM_DEVICE, device, MM_BASE_MODEM_PHYSDEV, physdev, MM_BASE_MODEM_DRIVERS, drivers, MM_BASE_MODEM_PLUGIN, plugin, MM_BASE_MODEM_VENDOR_ID, vendor_id, MM_BASE_MODEM_PRODUCT_ID, product_id, MM_BASE_MODEM_DATA_NET_SUPPORTED, TRUE, MM_BASE_MODEM_DATA_TTY_SUPPORTED, TRUE, NULL); } static void mm_broadband_modem_fibocom_init (MMBroadbandModemFibocom *self) { self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, MM_TYPE_BROADBAND_MODEM_FIBOCOM, MMBroadbandModemFibocomPrivate); self->priv->gtrndis_support = FEATURE_SUPPORT_UNKNOWN; self->priv->sim_ready_regex = g_regex_new ("\\r\\n\\+SIM READY\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); self->priv->initial_eps_bearer_support = FEATURE_SUPPORT_UNKNOWN; } static void finalize (GObject *object) { MMBroadbandModemFibocom *self = MM_BROADBAND_MODEM_FIBOCOM (object); g_regex_unref (self->priv->sim_ready_regex); G_OBJECT_CLASS (mm_broadband_modem_fibocom_parent_class)->finalize (object); } static void iface_modem_init (MMIfaceModem *iface) { iface->create_bearer = modem_create_bearer; iface->create_bearer_finish = modem_create_bearer_finish; iface->reset = modem_reset; iface->reset_finish = modem_common_power_finish; iface->modem_power_down = modem_power_down; iface->modem_power_down_finish = modem_common_power_finish; iface->modem_power_off = modem_power_off; iface->modem_power_off_finish = modem_common_power_finish; } static void iface_modem_3gpp_init (MMIfaceModem3gpp *iface) { iface->load_initial_eps_bearer = modem_3gpp_load_initial_eps_bearer; iface->load_initial_eps_bearer_finish = modem_3gpp_load_initial_eps_bearer_finish; iface->load_initial_eps_bearer_settings = modem_3gpp_load_initial_eps_bearer_settings; iface->load_initial_eps_bearer_settings_finish = modem_3gpp_load_initial_eps_bearer_settings_finish; iface->set_initial_eps_bearer_settings = modem_3gpp_set_initial_eps_bearer_settings; iface->set_initial_eps_bearer_settings_finish = modem_3gpp_set_initial_eps_bearer_settings_finish; } static void iface_modem_3gpp_profile_manager_init (MMIfaceModem3gppProfileManager *iface) { iface_modem_3gpp_profile_manager_parent = g_type_interface_peek_parent (iface); iface->deactivate_profile = modem_3gpp_profile_manager_deactivate_profile; iface->deactivate_profile_finish = modem_3gpp_profile_manager_deactivate_profile_finish; } static void iface_modem_firmware_init (MMIfaceModemFirmware *iface) { iface->load_update_settings = mm_shared_fibocom_firmware_load_update_settings; iface->load_update_settings_finish = mm_shared_fibocom_firmware_load_update_settings_finish; } static void mm_broadband_modem_fibocom_class_init (MMBroadbandModemFibocomClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); MMBroadbandModemClass *broadband_modem_class = MM_BROADBAND_MODEM_CLASS (klass); g_type_class_add_private (G_OBJECT_CLASS (klass), sizeof (MMBroadbandModemFibocomPrivate)); /* Virtual methods */ object_class->finalize = finalize; broadband_modem_class->setup_ports = setup_ports; } ModemManager-1.23.4-dev/src/plugins/fibocom/mm-broadband-modem-fibocom.h000066400000000000000000000047321456466623000260300ustar00rootroot00000000000000/* -*- 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) 2022 Disruptive Technologies Research AS */ #ifndef MM_BROADBAND_MODEM_FIBOCOM_H #define MM_BROADBAND_MODEM_FIBOCOM_H #include "mm-broadband-modem.h" #define MM_TYPE_BROADBAND_MODEM_FIBOCOM (mm_broadband_modem_fibocom_get_type ()) #define MM_BROADBAND_MODEM_FIBOCOM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_BROADBAND_MODEM_FIBOCOM, MMBroadbandModemFibocom)) #define MM_BROADBAND_MODEM_FIBOCOM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_BROADBAND_MODEM_FIBOCOM, MMBroadbandModemFibocomClass)) #define MM_IS_BROADBAND_MODEM_FIBOCOM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_BROADBAND_MODEM_FIBOCOM)) #define MM_IS_BROADBAND_MODEM_FIBOCOM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_BROADBAND_MODEM_FIBOCOM)) #define MM_BROADBAND_MODEM_FIBOCOM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_BROADBAND_MODEM_FIBOCOM, MMBroadbandModemFibocomClass)) typedef struct _MMBroadbandModemFibocom MMBroadbandModemFibocom; typedef struct _MMBroadbandModemFibocomClass MMBroadbandModemFibocomClass; typedef struct _MMBroadbandModemFibocomPrivate MMBroadbandModemFibocomPrivate; struct _MMBroadbandModemFibocom { MMBroadbandModem parent; MMBroadbandModemFibocomPrivate *priv; }; struct _MMBroadbandModemFibocomClass{ MMBroadbandModemClass parent; }; GType mm_broadband_modem_fibocom_get_type (void); MMBroadbandModemFibocom *mm_broadband_modem_fibocom_new (const gchar *device, const gchar *physdev, const gchar **drivers, const gchar *plugin, guint16 vendor_id, guint16 product_id); #endif /* MM_BROADBAND_MODEM_FIBOCOM_H */ ModemManager-1.23.4-dev/src/plugins/fibocom/mm-broadband-modem-mbim-fibocom.c000066400000000000000000000075371456466623000267530ustar00rootroot00000000000000/* -*- 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) 2022 Fibocom Wireless Inc. */ #include #include #include #include #include #include #include "ModemManager.h" #include "mm-log-object.h" #include "mm-iface-modem.h" #include "mm-iface-modem-3gpp.h" #include "mm-broadband-modem-mbim-fibocom.h" #include "mm-shared-fibocom.h" static void iface_modem_3gpp_init (MMIfaceModem3gpp *iface); static void shared_fibocom_init (MMSharedFibocom *iface); static void iface_modem_firmware_init (MMIfaceModemFirmware *iface); static MMIfaceModem3gpp *iface_modem_3gpp_parent; G_DEFINE_TYPE_EXTENDED (MMBroadbandModemMbimFibocom, mm_broadband_modem_mbim_fibocom, MM_TYPE_BROADBAND_MODEM_MBIM, 0, G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_3GPP, iface_modem_3gpp_init) G_IMPLEMENT_INTERFACE (MM_TYPE_SHARED_FIBOCOM, shared_fibocom_init) G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_FIRMWARE, iface_modem_firmware_init)) /******************************************************************************/ MMBroadbandModemMbimFibocom * mm_broadband_modem_mbim_fibocom_new (const gchar *device, const gchar *physdev, const gchar **drivers, const gchar *plugin, guint16 vendor_id, guint16 product_id) { return g_object_new (MM_TYPE_BROADBAND_MODEM_MBIM_FIBOCOM, MM_BASE_MODEM_DEVICE, device, MM_BASE_MODEM_PHYSDEV, physdev, MM_BASE_MODEM_DRIVERS, drivers, MM_BASE_MODEM_PLUGIN, plugin, MM_BASE_MODEM_VENDOR_ID, vendor_id, MM_BASE_MODEM_PRODUCT_ID, product_id, /* MBIM bearer supports NET only */ MM_BASE_MODEM_DATA_NET_SUPPORTED, TRUE, MM_BASE_MODEM_DATA_TTY_SUPPORTED, FALSE, MM_IFACE_MODEM_SIM_HOT_SWAP_SUPPORTED, TRUE, MM_IFACE_MODEM_PERIODIC_SIGNAL_CHECK_DISABLED, TRUE, NULL); } static void mm_broadband_modem_mbim_fibocom_init (MMBroadbandModemMbimFibocom *self) { } static void iface_modem_3gpp_init (MMIfaceModem3gpp *iface) { iface_modem_3gpp_parent = g_type_interface_peek_parent (iface); iface->set_initial_eps_bearer_settings = mm_shared_fibocom_set_initial_eps_bearer_settings; iface->set_initial_eps_bearer_settings_finish = mm_shared_fibocom_set_initial_eps_bearer_settings_finish; } static void iface_modem_firmware_init (MMIfaceModemFirmware *iface) { iface->load_update_settings = mm_shared_fibocom_firmware_load_update_settings; iface->load_update_settings_finish = mm_shared_fibocom_firmware_load_update_settings_finish; } static MMIfaceModem3gpp * peek_parent_3gpp_interface (MMSharedFibocom *self) { return iface_modem_3gpp_parent; } static void shared_fibocom_init (MMSharedFibocom *iface) { iface->peek_parent_3gpp_interface = peek_parent_3gpp_interface; } static void mm_broadband_modem_mbim_fibocom_class_init (MMBroadbandModemMbimFibocomClass *klass) { } ModemManager-1.23.4-dev/src/plugins/fibocom/mm-broadband-modem-mbim-fibocom.h000066400000000000000000000050121456466623000267420ustar00rootroot00000000000000/* -*- 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) 2022 Fibocom Wireless Inc. */ #ifndef MM_BROADBAND_MODEM_MBIM_FIBOCOM_H #define MM_BROADBAND_MODEM_MBIM_FIBOCOM_H #include "mm-broadband-modem-mbim.h" #define MM_TYPE_BROADBAND_MODEM_MBIM_FIBOCOM (mm_broadband_modem_mbim_fibocom_get_type ()) #define MM_BROADBAND_MODEM_MBIM_FIBOCOM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_BROADBAND_MODEM_MBIM_FIBOCOM, MMBroadbandModemMbimFibocom)) #define MM_BROADBAND_MODEM_MBIM_FIBOCOM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_BROADBAND_MODEM_MBIM_FIBOCOM, MMBroadbandModemMbimFibocomClass)) #define MM_IS_BROADBAND_MODEM_MBIM_FIBOCOM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_BROADBAND_MODEM_MBIM_FIBOCOM)) #define MM_IS_BROADBAND_MODEM_MBIM_FIBOCOM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_BROADBAND_MODEM_MBIM_FIBOCOM)) #define MM_BROADBAND_MODEM_MBIM_FIBOCOM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_BROADBAND_MODEM_MBIM_FIBOCOM, MMBroadbandModemMbimFibocomClass)) typedef struct _MMBroadbandModemMbimFibocom MMBroadbandModemMbimFibocom; typedef struct _MMBroadbandModemMbimFibocomClass MMBroadbandModemMbimFibocomClass; struct _MMBroadbandModemMbimFibocom { MMBroadbandModemMbim parent; }; struct _MMBroadbandModemMbimFibocomClass{ MMBroadbandModemMbimClass parent; }; GType mm_broadband_modem_mbim_fibocom_get_type (void); MMBroadbandModemMbimFibocom *mm_broadband_modem_mbim_fibocom_new (const gchar *device, const gchar *physdev, const gchar **drivers, const gchar *plugin, guint16 vendor_id, guint16 product_id); #endif /* MM_BROADBAND_MODEM_MBIM_FIBOCOM_H */ ModemManager-1.23.4-dev/src/plugins/fibocom/mm-broadband-modem-mbim-xmm-fibocom.c000066400000000000000000000100321456466623000275320ustar00rootroot00000000000000/* -*- 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) 2022 Fibocom Wireless Inc. */ #include #include #include #include #include #include #include "ModemManager.h" #include "mm-log-object.h" #include "mm-iface-modem.h" #include "mm-iface-modem-3gpp.h" #include "mm-broadband-modem-mbim-xmm-fibocom.h" #include "mm-shared-fibocom.h" static void iface_modem_3gpp_init (MMIfaceModem3gpp *iface); static void shared_fibocom_init (MMSharedFibocom *iface); static void iface_modem_firmware_init (MMIfaceModemFirmware *iface); static MMIfaceModem3gpp *iface_modem_3gpp_parent; G_DEFINE_TYPE_EXTENDED (MMBroadbandModemMbimXmmFibocom, mm_broadband_modem_mbim_xmm_fibocom, MM_TYPE_BROADBAND_MODEM_MBIM_XMM, 0, G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_3GPP, iface_modem_3gpp_init) G_IMPLEMENT_INTERFACE (MM_TYPE_SHARED_FIBOCOM, shared_fibocom_init) G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_FIRMWARE, iface_modem_firmware_init)) /******************************************************************************/ MMBroadbandModemMbimXmmFibocom * mm_broadband_modem_mbim_xmm_fibocom_new (const gchar *device, const gchar *physdev, const gchar **drivers, const gchar *plugin, guint16 vendor_id, guint16 product_id) { return g_object_new (MM_TYPE_BROADBAND_MODEM_MBIM_XMM_FIBOCOM, MM_BASE_MODEM_DEVICE, device, MM_BASE_MODEM_PHYSDEV, physdev, MM_BASE_MODEM_DRIVERS, drivers, MM_BASE_MODEM_PLUGIN, plugin, MM_BASE_MODEM_VENDOR_ID, vendor_id, MM_BASE_MODEM_PRODUCT_ID, product_id, /* MBIM bearer supports NET only */ MM_BASE_MODEM_DATA_NET_SUPPORTED, TRUE, MM_BASE_MODEM_DATA_TTY_SUPPORTED, FALSE, MM_IFACE_MODEM_SIM_HOT_SWAP_SUPPORTED, TRUE, MM_IFACE_MODEM_PERIODIC_SIGNAL_CHECK_DISABLED, TRUE, #if defined WITH_QMI && QMI_MBIM_QMUX_SUPPORTED MM_BROADBAND_MODEM_MBIM_QMI_UNSUPPORTED, TRUE, #endif NULL); } static void mm_broadband_modem_mbim_xmm_fibocom_init (MMBroadbandModemMbimXmmFibocom *self) { } static void iface_modem_3gpp_init (MMIfaceModem3gpp *iface) { iface_modem_3gpp_parent = g_type_interface_peek_parent (iface); iface->set_initial_eps_bearer_settings = mm_shared_fibocom_set_initial_eps_bearer_settings; iface->set_initial_eps_bearer_settings_finish = mm_shared_fibocom_set_initial_eps_bearer_settings_finish; } static void iface_modem_firmware_init (MMIfaceModemFirmware *iface) { iface->load_update_settings = mm_shared_fibocom_firmware_load_update_settings; iface->load_update_settings_finish = mm_shared_fibocom_firmware_load_update_settings_finish; } static MMIfaceModem3gpp * peek_parent_3gpp_interface (MMSharedFibocom *self) { return iface_modem_3gpp_parent; } static void shared_fibocom_init (MMSharedFibocom *iface) { iface->peek_parent_3gpp_interface = peek_parent_3gpp_interface; } static void mm_broadband_modem_mbim_xmm_fibocom_class_init (MMBroadbandModemMbimXmmFibocomClass *klass) { } ModemManager-1.23.4-dev/src/plugins/fibocom/mm-broadband-modem-mbim-xmm-fibocom.h000066400000000000000000000052311456466623000275440ustar00rootroot00000000000000/* -*- 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) 2022 Fibocom Wireless Inc. */ #ifndef MM_BROADBAND_MODEM_MBIM_XMM_FIBOCOM_H #define MM_BROADBAND_MODEM_MBIM_XMM_FIBOCOM_H #include "mm-broadband-modem-mbim-xmm.h" #define MM_TYPE_BROADBAND_MODEM_MBIM_XMM_FIBOCOM (mm_broadband_modem_mbim_xmm_fibocom_get_type ()) #define MM_BROADBAND_MODEM_MBIM_XMM_FIBOCOM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_BROADBAND_MODEM_MBIM_XMM_FIBOCOM, MMBroadbandModemMbimXmmFibocom)) #define MM_BROADBAND_MODEM_MBIM_XMM_FIBOCOM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_BROADBAND_MODEM_MBIM_XMM_FIBOCOM, MMBroadbandModemMbimXmmFibocomClass)) #define MM_IS_BROADBAND_MODEM_MBIM_XMM_FIBOCOM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_BROADBAND_MODEM_MBIM_XMM_FIBOCOM)) #define MM_IS_BROADBAND_MODEM_MBIM_XMM_FIBOCOM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_BROADBAND_MODEM_MBIM_XMM_FIBOCOM)) #define MM_BROADBAND_MODEM_MBIM_XMM_FIBOCOM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_BROADBAND_MODEM_MBIM_XMM_FIBOCOM, MMBroadbandModemMbimXmmFibocomClass)) typedef struct _MMBroadbandModemMbimXmmFibocom MMBroadbandModemMbimXmmFibocom; typedef struct _MMBroadbandModemMbimXmmFibocomClass MMBroadbandModemMbimXmmFibocomClass; struct _MMBroadbandModemMbimXmmFibocom { MMBroadbandModemMbimXmm parent; }; struct _MMBroadbandModemMbimXmmFibocomClass{ MMBroadbandModemMbimXmmClass parent; }; GType mm_broadband_modem_mbim_xmm_fibocom_get_type (void); MMBroadbandModemMbimXmmFibocom *mm_broadband_modem_mbim_xmm_fibocom_new (const gchar *device, const gchar *physdev, const gchar **drivers, const gchar *plugin, guint16 vendor_id, guint16 product_id); #endif /* MM_BROADBAND_MODEM_MBIM_XMM_FIBOCOM_H */ ModemManager-1.23.4-dev/src/plugins/fibocom/mm-plugin-fibocom.c000066400000000000000000000132621456466623000243040ustar00rootroot00000000000000/* -*- 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) 2018-2020 Aleksander Morgado */ #include #include #define _LIBMM_INSIDE_MM #include #include "mm-log-object.h" #include "mm-plugin-common.h" #include "mm-broadband-modem.h" #include "mm-broadband-modem-xmm.h" #include "mm-broadband-modem-fibocom.h" #if defined WITH_MBIM #include "mm-broadband-modem-mbim.h" #include "mm-broadband-modem-mbim-fibocom.h" #include "mm-broadband-modem-mbim-xmm.h" #include "mm-broadband-modem-mbim-xmm-fibocom.h" #endif #if defined WITH_QMI #include "mm-broadband-modem-qmi.h" #endif #define MM_TYPE_PLUGIN_FIBOCOM mm_plugin_fibocom_get_type () MM_DEFINE_PLUGIN (FIBOCOM, fibocom, Fibocom) /*****************************************************************************/ static MMBaseModem * create_modem (MMPlugin *self, const gchar *uid, const gchar *physdev, const gchar **drivers, guint16 vendor, guint16 product, guint16 subsystem_vendor, GList *probes, GError **error) { #if defined WITH_MBIM if (mm_port_probe_list_has_mbim_port (probes)) { if (mm_port_probe_list_is_xmm (probes)) { mm_obj_dbg (self, "MBIM-powered XMM-based Fibocom modem found..."); return MM_BASE_MODEM (mm_broadband_modem_mbim_xmm_fibocom_new (uid, physdev, drivers, mm_plugin_get_name (self), vendor, product)); } mm_obj_dbg (self, "MBIM-powered Fibocom modem found..."); return MM_BASE_MODEM (mm_broadband_modem_mbim_fibocom_new (uid, physdev, drivers, mm_plugin_get_name (self), vendor, product)); } #endif #if defined WITH_QMI if (mm_port_probe_list_has_qmi_port (probes)) { mm_obj_dbg (self, "QMI-powered Fibocom modem found..."); return MM_BASE_MODEM (mm_broadband_modem_qmi_new (uid, physdev, drivers, mm_plugin_get_name (self), vendor, product)); } #endif if (mm_port_probe_list_is_xmm (probes)) { mm_obj_dbg (self, "XMM-based Fibocom modem found..."); return MM_BASE_MODEM (mm_broadband_modem_xmm_new (uid, physdev, drivers, mm_plugin_get_name (self), vendor, product)); } mm_obj_dbg (self, "Fibocom modem found..."); return MM_BASE_MODEM (mm_broadband_modem_fibocom_new (uid, physdev, drivers, mm_plugin_get_name (self), vendor, product)); } /*****************************************************************************/ MM_PLUGIN_NAMED_CREATOR_SCOPE MMPlugin * mm_plugin_create_fibocom (void) { static const gchar *subsystems[] = { "tty", "net", "usbmisc", NULL }; static const guint16 vendor_ids[] = { 0x2cb7, 0x1782, 0 }; static const gchar *drivers[] = { "cdc_mbim", "qmi_wwan", "cdc_ether", "option", NULL }; return MM_PLUGIN ( g_object_new (MM_TYPE_PLUGIN_FIBOCOM, MM_PLUGIN_NAME, MM_MODULE_NAME, MM_PLUGIN_ALLOWED_SUBSYSTEMS, subsystems, MM_PLUGIN_ALLOWED_VENDOR_IDS, vendor_ids, MM_PLUGIN_ALLOWED_DRIVERS, drivers, MM_PLUGIN_ALLOWED_AT, TRUE, MM_PLUGIN_ALLOWED_MBIM, TRUE, MM_PLUGIN_ALLOWED_QMI, TRUE, MM_PLUGIN_XMM_PROBE, TRUE, NULL)); } static void mm_plugin_fibocom_init (MMPluginFibocom *self) { } static void mm_plugin_fibocom_class_init (MMPluginFibocomClass *klass) { MMPluginClass *plugin_class = MM_PLUGIN_CLASS (klass); plugin_class->create_modem = create_modem; } ModemManager-1.23.4-dev/src/plugins/fibocom/mm-shared-fibocom.c000066400000000000000000000324521456466623000242560ustar00rootroot00000000000000/* -*- 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) 2022 Fibocom Wireless Inc. */ #include #include #include #include #define _LIBMM_INSIDE_MM #include #include "mm-log-object.h" #include "mm-broadband-modem.h" #include "mm-broadband-modem-mbim.h" #include "mm-iface-modem.h" #include "mm-iface-modem-3gpp.h" #include "mm-shared-fibocom.h" #include "mm-base-modem-at.h" /*****************************************************************************/ /* Private data context */ #define PRIVATE_TAG "shared-intel-private-tag" static GQuark private_quark; typedef struct { /* 3GPP interface support */ MMIfaceModem3gpp *iface_modem_3gpp_parent; } Private; static void private_free (Private *priv) { g_slice_free (Private, priv); } static Private * get_private (MMSharedFibocom *self) { Private *priv; if (G_UNLIKELY (!private_quark)) private_quark = g_quark_from_static_string (PRIVATE_TAG); priv = g_object_get_qdata (G_OBJECT (self), private_quark); if (!priv) { priv = g_slice_new0 (Private); /* Setup parent class' MMIfaceModem3gpp */ g_assert (MM_SHARED_FIBOCOM_GET_INTERFACE (self)->peek_parent_3gpp_interface); priv->iface_modem_3gpp_parent = MM_SHARED_FIBOCOM_GET_INTERFACE (self)->peek_parent_3gpp_interface (self); g_object_set_qdata_full (G_OBJECT (self), private_quark, priv, (GDestroyNotify)private_free); } return priv; } /*****************************************************************************/ typedef struct { MMBearerProperties *config; gboolean initial_eps_off_on; GError *saved_error; } SetInitialEpsBearerSettingsContext; static void set_initial_eps_bearer_settings_context_free (SetInitialEpsBearerSettingsContext *ctx) { g_clear_error (&ctx->saved_error); g_clear_object (&ctx->config); g_slice_free (SetInitialEpsBearerSettingsContext, ctx); } static void set_initial_eps_bearer_settings_complete (GTask *task) { SetInitialEpsBearerSettingsContext *ctx; ctx = g_task_get_task_data (task); if (ctx->saved_error) g_task_return_error (task, g_steal_pointer (&ctx->saved_error)); else g_task_return_boolean (task, TRUE); g_object_unref (task); } gboolean mm_shared_fibocom_set_initial_eps_bearer_settings_finish (MMIfaceModem3gpp *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void after_attach_apn_modem_power_up_ready (MMIfaceModem *self, GAsyncResult *res, GTask *task) { SetInitialEpsBearerSettingsContext *ctx; g_autoptr(GError) error = NULL; ctx = g_task_get_task_data (task); if (!mm_iface_modem_set_power_state_finish (self, res, &error)) { mm_obj_warn (self, "failed to power up modem after attach APN settings update: %s", error->message); if (!ctx->saved_error) ctx->saved_error = g_steal_pointer (&error); } else mm_obj_dbg (self, "success toggling modem power up after attach APN"); set_initial_eps_bearer_settings_complete (task); } static void parent_set_initial_eps_bearer_settings_ready (MMIfaceModem3gpp *self, GAsyncResult *res, GTask *task) { SetInitialEpsBearerSettingsContext *ctx; Private *priv; ctx = g_task_get_task_data (task); priv = get_private (MM_SHARED_FIBOCOM (self)); if (!priv->iface_modem_3gpp_parent->set_initial_eps_bearer_settings_finish (self, res, &ctx->saved_error)) mm_obj_warn (self, "failed to update APN settings: %s", ctx->saved_error->message); if (ctx->initial_eps_off_on) { mm_obj_dbg (self, "toggle modem power up after attach APN"); mm_iface_modem_set_power_state (MM_IFACE_MODEM (self), MM_MODEM_POWER_STATE_ON, (GAsyncReadyCallback) after_attach_apn_modem_power_up_ready, task); return; } set_initial_eps_bearer_settings_complete (task); } static void parent_set_initial_eps_bearer_settings (GTask *task) { MMSharedFibocom *self; SetInitialEpsBearerSettingsContext *ctx; Private *priv; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); priv = get_private (self); g_assert (priv->iface_modem_3gpp_parent); g_assert (priv->iface_modem_3gpp_parent->set_initial_eps_bearer_settings); g_assert (priv->iface_modem_3gpp_parent->set_initial_eps_bearer_settings_finish); priv->iface_modem_3gpp_parent->set_initial_eps_bearer_settings (MM_IFACE_MODEM_3GPP (self), ctx->config, (GAsyncReadyCallback)parent_set_initial_eps_bearer_settings_ready, task); } static void before_attach_apn_modem_power_down_ready (MMIfaceModem *self, GAsyncResult *res, GTask *task) { GError *error = NULL; if (!mm_iface_modem_set_power_state_finish (self, res, &error)) { mm_obj_warn (self, "failed to power down modem before attach APN settings update: %s", error->message); g_task_return_error (task, error); g_object_unref (task); return; } mm_obj_dbg (self, "success toggling modem power down before attach APN"); parent_set_initial_eps_bearer_settings (task); } void mm_shared_fibocom_set_initial_eps_bearer_settings (MMIfaceModem3gpp *self, MMBearerProperties *config, GAsyncReadyCallback callback, gpointer user_data) { SetInitialEpsBearerSettingsContext *ctx; GTask *task; MMPortMbim *port; task = g_task_new (self, NULL, callback, user_data); /* This shared logic is only expected in MBIM capable devices */ g_assert (MM_IS_BROADBAND_MODEM_MBIM (self)); port = mm_broadband_modem_mbim_peek_port_mbim (MM_BROADBAND_MODEM_MBIM (self)); if (!port) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "No valid MBIM port found"); g_object_unref (task); return; } ctx = g_slice_new0 (SetInitialEpsBearerSettingsContext); ctx->config = g_object_ref (config); ctx->initial_eps_off_on = mm_kernel_device_get_property_as_boolean (mm_port_peek_kernel_device (MM_PORT (port)), "ID_MM_FIBOCOM_INITIAL_EPS_OFF_ON"); g_task_set_task_data (task, ctx, (GDestroyNotify)set_initial_eps_bearer_settings_context_free); if (ctx->initial_eps_off_on) { mm_obj_dbg (self, "toggle modem power down before attach APN"); mm_iface_modem_set_power_state (MM_IFACE_MODEM (self), MM_MODEM_POWER_STATE_LOW, (GAsyncReadyCallback) before_attach_apn_modem_power_down_ready, task); return; } parent_set_initial_eps_bearer_settings (task); } /*****************************************************************************/ MMFirmwareUpdateSettings * mm_shared_fibocom_firmware_load_update_settings_finish (MMIfaceModemFirmware *self, GAsyncResult *res, GError **error) { return g_task_propagate_pointer (G_TASK (res), error); } static gboolean fibocom_is_fastboot_supported (MMBaseModem *modem, MMPort *port) { return mm_kernel_device_get_global_property_as_boolean (mm_port_peek_kernel_device (port), "ID_MM_FIBOCOM_FASTBOOT"); } static MMModemFirmwareUpdateMethod fibocom_get_firmware_update_methods (MMBaseModem *modem, MMPort *port) { MMModemFirmwareUpdateMethod update_methods; update_methods = MM_MODEM_FIRMWARE_UPDATE_METHOD_NONE; if (fibocom_is_fastboot_supported (modem, port)) update_methods |= MM_MODEM_FIRMWARE_UPDATE_METHOD_FASTBOOT; return update_methods; } static void fibocom_at_port_get_firmware_version_ready (MMBaseModem *self, GAsyncResult *res, GTask *task) { MMFirmwareUpdateSettings *update_settings; MMModemFirmwareUpdateMethod update_methods; const gchar *version_from_at; g_auto(GStrv) version = NULL; g_autoptr(GPtrArray) ids = NULL; GError *error = NULL; update_settings = g_task_get_task_data (task); update_methods = mm_firmware_update_settings_get_method (update_settings); /* Set device ids */ ids = mm_iface_firmware_build_generic_device_ids (MM_IFACE_MODEM_FIRMWARE (self), &error); if (error) { mm_obj_warn (self, "failed to build generic device ids: %s", error->message); g_task_return_error (task, error); g_object_unref (task); return; } /* The version get from the AT needs to be formatted * * version_from_at : AT+GTPKGVER: "19500.0000.00.01.02.80_6001.0000.007.000.075_A89" * version[1] : 19500.0000.00.01.02.80_6001.0000.007.000.075_A89 */ version_from_at = mm_base_modem_at_command_finish (self, res, NULL); if (version_from_at) { version = g_strsplit (version_from_at, "\"", -1); if (version && version[0] && version[1] && g_utf8_validate (version[1], -1, NULL)) { mm_firmware_update_settings_set_version (update_settings, version[1]); } } mm_firmware_update_settings_set_device_ids (update_settings, (const gchar **)ids->pdata); /* Set update methods */ if (update_methods & MM_MODEM_FIRMWARE_UPDATE_METHOD_FASTBOOT) { /* Fastboot AT */ mm_firmware_update_settings_set_fastboot_at (update_settings, "AT+SYSCMD=\"sys_reboot bootloader\""); } g_task_return_pointer (task, g_object_ref (update_settings), g_object_unref); g_object_unref (task); } void mm_shared_fibocom_firmware_load_update_settings (MMIfaceModemFirmware *self, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; MMPortSerialAt *at_port; MMModemFirmwareUpdateMethod update_methods; MMFirmwareUpdateSettings *update_settings; task = g_task_new (self, NULL, callback, user_data); at_port = mm_base_modem_peek_best_at_port (MM_BASE_MODEM (self), NULL); if (at_port) { update_methods = fibocom_get_firmware_update_methods (MM_BASE_MODEM (self), MM_PORT (at_port)); update_settings = mm_firmware_update_settings_new (update_methods); g_task_set_task_data (task, update_settings, g_object_unref); /* Get modem version by AT */ mm_base_modem_at_command (MM_BASE_MODEM (self), "+GTPKGVER?", 3, TRUE, (GAsyncReadyCallback) fibocom_at_port_get_firmware_version_ready, task); return; } g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't find a port to fetch firmware info"); g_object_unref (task); } /*****************************************************************************/ static void shared_fibocom_init (gpointer g_iface) { } GType mm_shared_fibocom_get_type (void) { static GType shared_fibocom_type = 0; if (!G_UNLIKELY (shared_fibocom_type)) { static const GTypeInfo info = { sizeof (MMSharedFibocom), /* class_size */ shared_fibocom_init, /* base_init */ NULL, /* base_finalize */ }; shared_fibocom_type = g_type_register_static (G_TYPE_INTERFACE, "MMSharedFibocom", &info, 0); g_type_interface_add_prerequisite (shared_fibocom_type, MM_TYPE_IFACE_MODEM); g_type_interface_add_prerequisite (shared_fibocom_type, MM_TYPE_IFACE_MODEM_3GPP); } return shared_fibocom_type; } ModemManager-1.23.4-dev/src/plugins/fibocom/mm-shared-fibocom.h000066400000000000000000000057271456466623000242700ustar00rootroot00000000000000/* -*- 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) 2022 Fibocom Wireless Inc. */ #ifndef MM_SHARED_FIBOCOM_H #define MM_SHARED_FIBOCOM_H #include #include #define _LIBMM_INSIDE_MM #include #include "mm-broadband-modem.h" #include "mm-iface-modem-3gpp.h" #include "mm-iface-modem.h" #include "mm-iface-modem-firmware.h" #define MM_TYPE_SHARED_FIBOCOM (mm_shared_fibocom_get_type ()) #define MM_SHARED_FIBOCOM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_SHARED_FIBOCOM, MMSharedFibocom)) #define MM_IS_SHARED_FIBOCOM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_SHARED_FIBOCOM)) #define MM_SHARED_FIBOCOM_GET_INTERFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE ((obj), MM_TYPE_SHARED_FIBOCOM, MMSharedFibocom)) typedef struct _MMSharedFibocom MMSharedFibocom; struct _MMSharedFibocom { GTypeInterface g_iface; /* Peek 3GPP interface of the parent class of the object */ MMIfaceModem3gpp * (* peek_parent_3gpp_interface) (MMSharedFibocom *self); }; GType mm_shared_fibocom_get_type (void); void mm_shared_fibocom_set_initial_eps_bearer_settings (MMIfaceModem3gpp *self, MMBearerProperties *config, GAsyncReadyCallback callback, gpointer user_data); gboolean mm_shared_fibocom_set_initial_eps_bearer_settings_finish (MMIfaceModem3gpp *self, GAsyncResult *res, GError **error); void mm_shared_fibocom_firmware_load_update_settings (MMIfaceModemFirmware *self, GAsyncReadyCallback callback, gpointer user_data); MMFirmwareUpdateSettings *mm_shared_fibocom_firmware_load_update_settings_finish (MMIfaceModemFirmware *self, GAsyncResult *res, GError **error); #endif /* MM_SHARED_FIBOCOM_H */ ModemManager-1.23.4-dev/src/plugins/fibocom/mm-shared.c000066400000000000000000000013141456466623000226330ustar00rootroot00000000000000/* -*- 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) 2022 Aleksander Morgado */ #include "mm-shared-common.h" MM_DEFINE_SHARED (fibocom) ModemManager-1.23.4-dev/src/plugins/foxconn/000077500000000000000000000000001456466623000206475ustar00rootroot00000000000000ModemManager-1.23.4-dev/src/plugins/foxconn/77-mm-foxconn-port-types.rules000066400000000000000000000031031456466623000263600ustar00rootroot00000000000000# do not edit this file, it will be overwritten on update ACTION!="add|change|move|bind", GOTO="mm_foxconn_port_types_end" SUBSYSTEMS=="usb", ATTRS{idVendor}=="0489", GOTO="mm_foxconn_vendorcheck" GOTO="mm_foxconn_port_types_end" LABEL="mm_foxconn_vendorcheck" SUBSYSTEMS=="usb", ATTRS{bInterfaceNumber}=="?*", ENV{.MM_USBIFNUM}="$attr{bInterfaceNumber}" # Foxconn T77w968 (default 0xe0b4, with esim support 0xe0b5) # if 02: primary port # if 03: secondary port # if 04: raw NMEA port # if 05: diag/qcdm port ATTRS{idVendor}=="0489", ATTRS{idProduct}=="e0b4", ENV{.MM_USBIFNUM}=="02", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1" ATTRS{idVendor}=="0489", ATTRS{idProduct}=="e0b4", ENV{.MM_USBIFNUM}=="03", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1" ATTRS{idVendor}=="0489", ATTRS{idProduct}=="e0b4", ENV{.MM_USBIFNUM}=="04", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_GPS}="1" ATTRS{idVendor}=="0489", ATTRS{idProduct}=="e0b4", ENV{.MM_USBIFNUM}=="05", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_QCDM}="1" ATTRS{idVendor}=="0489", ATTRS{idProduct}=="e0b5", ENV{.MM_USBIFNUM}=="02", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1" ATTRS{idVendor}=="0489", ATTRS{idProduct}=="e0b5", ENV{.MM_USBIFNUM}=="03", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1" ATTRS{idVendor}=="0489", ATTRS{idProduct}=="e0b5", ENV{.MM_USBIFNUM}=="04", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_GPS}="1" ATTRS{idVendor}=="0489", ATTRS{idProduct}=="e0b5", ENV{.MM_USBIFNUM}=="05", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_QCDM}="1" GOTO="mm_foxconn_port_types_end" LABEL="mm_foxconn_port_types_end" ModemManager-1.23.4-dev/src/plugins/foxconn/mm-broadband-modem-mbim-foxconn.c000066400000000000000000000562071456466623000270410ustar00rootroot00000000000000/* -*- 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) 2018-2019 Aleksander Morgado */ #include #include #include #include #include #include #include #include #define _LIBMM_INSIDE_MM #include #include "mm-errors-types.h" #include "mm-modem-helpers.h" #include "mm-base-modem-at.h" #include "mm-iface-modem.h" #include "mm-iface-modem-location.h" #include "mm-broadband-modem-mbim-foxconn.h" #if defined WITH_QMI && QMI_MBIM_QMUX_SUPPORTED # include "mm-iface-modem-firmware.h" # include "mm-shared-qmi.h" # include "mm-log.h" #endif static void iface_modem_location_init (MMIfaceModemLocation *iface); #if defined WITH_QMI && QMI_MBIM_QMUX_SUPPORTED static void iface_modem_firmware_init (MMIfaceModemFirmware *iface); #endif static MMIfaceModemLocation *iface_modem_location_parent; G_DEFINE_TYPE_EXTENDED (MMBroadbandModemMbimFoxconn, mm_broadband_modem_mbim_foxconn, MM_TYPE_BROADBAND_MODEM_MBIM, 0, #if defined WITH_QMI && QMI_MBIM_QMUX_SUPPORTED G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_FIRMWARE, iface_modem_firmware_init) #endif G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_LOCATION, iface_modem_location_init)) typedef enum { FEATURE_SUPPORT_UNKNOWN, FEATURE_NOT_SUPPORTED, FEATURE_SUPPORTED } FeatureSupport; struct _MMBroadbandModemMbimFoxconnPrivate { FeatureSupport unmanaged_gps_support; }; #if defined WITH_QMI && QMI_MBIM_QMUX_SUPPORTED /*****************************************************************************/ /* Firmware update settings * * We only support reporting firmware update settings when QMI support is built, * because this is the only clean way to get the expected firmware version to * report. */ static MMFirmwareUpdateSettings * firmware_load_update_settings_finish (MMIfaceModemFirmware *self, GAsyncResult *res, GError **error) { return g_task_propagate_pointer (G_TASK (res), error); } static gboolean needs_qdu_and_mcfg_apps_version (MMIfaceModemFirmware *self) { guint vendor_id; guint product_id; /* 0x105b is the T99W175 module, T99W175 supports QDU and requires MCFG+APPS version. * T99W265(0x0489:0xe0da ; 0x0489:0xe0db): supports QDU and requires MCFG+APPS version. * else support FASTBOOT and QMI PDC, and require only MCFG version. */ vendor_id = mm_base_modem_get_vendor_id (MM_BASE_MODEM (self)); product_id = mm_base_modem_get_product_id (MM_BASE_MODEM (self)); return (vendor_id == 0x105b || (vendor_id == 0x0489 && (product_id == 0xe0da || product_id == 0xe0db))); } /*****************************************************************************/ /* Need APPS version for the development of different functions when T77W968 support FASTBOOT and QMI PDC. * Such as: T77W968.F1.0.0.5.2.GC.013.037 and T77W968.F1.0.0.5.2.GC.013.049, the MCFG version(T77W968.F1.0.0.5.2.GC.013) is same, * but the APPS version(037 and 049) is different. * * For T77W968.F1.0.0.5.2.GC.013.049, before the change, "fwupdmgr get-devices" can obtain Current version is T77W968.F1.0.0.5.2.GC.013, * it only include the MCFG version. * After add need APPS version, it shows Current version is T77W968.F1.0.0.5.2.GC.013.049, including the MCFG+APPS version. */ static gboolean needs_fastboot_and_qmi_pdc_mcfg_apps_version (MMIfaceModemFirmware *self) { guint vendor_id; guint product_id; /* T77W968(0x413c:0x81d7 ; 0x413c:0x81e0 ; 0x413c:0x81e4 ; 0x413c:0x81e6): supports FASTBOOT and QMI PDC, * and requires MCFG+APPS version. * else support FASTBOOT and QMI PDC, and require only MCFG version. */ vendor_id = mm_base_modem_get_vendor_id (MM_BASE_MODEM (self)); product_id = mm_base_modem_get_product_id (MM_BASE_MODEM (self)); return (vendor_id == 0x413c && (product_id == 0x81d7 || product_id == 0x81e0 || product_id == 0x81e4 || product_id == 0x81e6)); } static MMFirmwareUpdateSettings * create_update_settings (MMIfaceModemFirmware *self, const gchar *version_str) { MMModemFirmwareUpdateMethod methods = MM_MODEM_FIRMWARE_UPDATE_METHOD_NONE; MMFirmwareUpdateSettings *update_settings = NULL; if (needs_qdu_and_mcfg_apps_version (self)) methods = MM_MODEM_FIRMWARE_UPDATE_METHOD_MBIM_QDU; else methods = MM_MODEM_FIRMWARE_UPDATE_METHOD_FASTBOOT | MM_MODEM_FIRMWARE_UPDATE_METHOD_QMI_PDC; update_settings = mm_firmware_update_settings_new (methods); if (methods & MM_MODEM_FIRMWARE_UPDATE_METHOD_FASTBOOT) mm_firmware_update_settings_set_fastboot_at (update_settings, "AT^FASTBOOT"); mm_firmware_update_settings_set_version (update_settings, version_str); return update_settings; } static void dms_foxconn_get_firmware_version_ready (QmiClientDms *client, GAsyncResult *res, GTask *task) { g_autoptr(QmiMessageDmsFoxconnGetFirmwareVersionOutput) output = NULL; GError *error = NULL; const gchar *str; output = qmi_client_dms_foxconn_get_firmware_version_finish (client, res, &error); if (!output || !qmi_message_dms_foxconn_get_firmware_version_output_get_result (output, &error)) { g_task_return_error (task, error); g_object_unref (task); return; } qmi_message_dms_foxconn_get_firmware_version_output_get_version (output, &str, NULL); g_task_return_pointer (task, create_update_settings (g_task_get_source_object (task), str), g_object_unref); g_object_unref (task); } static void fox_get_firmware_version_ready (QmiClientFox *client, GAsyncResult *res, GTask *task) { g_autoptr(QmiMessageFoxGetFirmwareVersionOutput) output = NULL; GError *error = NULL; const gchar *str; output = qmi_client_fox_get_firmware_version_finish (client, res, &error); if (!output || !qmi_message_fox_get_firmware_version_output_get_result (output, &error)) { g_task_return_error (task, error); g_object_unref (task); return; } qmi_message_fox_get_firmware_version_output_get_version (output, &str, NULL); g_task_return_pointer (task, create_update_settings (g_task_get_source_object (task), str), g_object_unref); g_object_unref (task); } static void mbim_port_allocate_qmi_client_ready (MMPortMbim *mbim, GAsyncResult *res, GTask *task) { MMIfaceModemFirmware *self; QmiClient *fox_client = NULL; QmiClient *dms_client = NULL; g_autoptr(GError) error = NULL; self = g_task_get_source_object (task); if (!mm_port_mbim_allocate_qmi_client_finish (mbim, res, &error)) mm_obj_dbg (self, "Allocate FOX client failed: %s", error->message); /* Try to get firmware version over fox service, if it failed to peek client, try dms service. */ fox_client = mm_shared_qmi_peek_client (MM_SHARED_QMI (self), QMI_SERVICE_FOX, MM_PORT_QMI_FLAG_DEFAULT, NULL); if (!fox_client) { dms_client = mm_shared_qmi_peek_client (MM_SHARED_QMI (self), QMI_SERVICE_DMS, MM_PORT_QMI_FLAG_DEFAULT, NULL); if (!dms_client) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Unable to load version info: no FOX or DMS client available"); g_object_unref (task); return; } } if (fox_client) { g_autoptr(QmiMessageFoxGetFirmwareVersionInput) input = NULL; input = qmi_message_fox_get_firmware_version_input_new (); qmi_message_fox_get_firmware_version_input_set_version_type (input, (needs_qdu_and_mcfg_apps_version (self) ? QMI_FOX_FIRMWARE_VERSION_TYPE_FIRMWARE_MCFG_APPS : QMI_FOX_FIRMWARE_VERSION_TYPE_FIRMWARE_MCFG), NULL); qmi_client_fox_get_firmware_version (QMI_CLIENT_FOX (fox_client), input, 10, NULL, (GAsyncReadyCallback)fox_get_firmware_version_ready, task); return; } if (dms_client) { g_autoptr(QmiMessageDmsFoxconnGetFirmwareVersionInput) input = NULL; input = qmi_message_dms_foxconn_get_firmware_version_input_new (); qmi_message_dms_foxconn_get_firmware_version_input_set_version_type (input, ((needs_qdu_and_mcfg_apps_version (self) || needs_fastboot_and_qmi_pdc_mcfg_apps_version (self)) ? QMI_DMS_FOXCONN_FIRMWARE_VERSION_TYPE_FIRMWARE_MCFG_APPS: QMI_DMS_FOXCONN_FIRMWARE_VERSION_TYPE_FIRMWARE_MCFG), NULL); qmi_client_dms_foxconn_get_firmware_version (QMI_CLIENT_DMS (dms_client), input, 10, NULL, (GAsyncReadyCallback)dms_foxconn_get_firmware_version_ready, task); return; } g_assert_not_reached (); } static void firmware_load_update_settings (MMIfaceModemFirmware *self, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; MMPortMbim *mbim; task = g_task_new (self, NULL, callback, user_data); mbim = mm_broadband_modem_mbim_peek_port_mbim (MM_BROADBAND_MODEM_MBIM (self)); mm_port_mbim_allocate_qmi_client (mbim, QMI_SERVICE_FOX, NULL, (GAsyncReadyCallback)mbim_port_allocate_qmi_client_ready, task); } #endif /*****************************************************************************/ /* Location capabilities loading (Location interface) */ static MMModemLocationSource location_load_capabilities_finish (MMIfaceModemLocation *self, GAsyncResult *res, GError **error) { GError *inner_error = NULL; gssize value; value = g_task_propagate_int (G_TASK (res), &inner_error); if (inner_error) { g_propagate_error (error, inner_error); return MM_MODEM_LOCATION_SOURCE_NONE; } return (MMModemLocationSource)value; } static void custom_location_load_capabilities (GTask *task, MMModemLocationSource sources) { MMBroadbandModemMbimFoxconn *self; self = g_task_get_source_object (task); /* If we have a GPS port and an AT port, enable unmanaged GPS support */ if (mm_base_modem_peek_port_primary (MM_BASE_MODEM (self)) && mm_base_modem_peek_port_gps (MM_BASE_MODEM (self))) { self->priv->unmanaged_gps_support = FEATURE_SUPPORTED; sources |= MM_MODEM_LOCATION_SOURCE_GPS_UNMANAGED; } /* So we're done, complete */ g_task_return_int (task, sources); g_object_unref (task); } static void parent_load_capabilities_ready (MMIfaceModemLocation *self, GAsyncResult *res, GTask *task) { MMModemLocationSource sources; GError *error = NULL; sources = iface_modem_location_parent->load_capabilities_finish (self, res, &error); if (error) { g_task_return_error (task, error); g_object_unref (task); return; } custom_location_load_capabilities (task, sources); } static void location_load_capabilities (MMIfaceModemLocation *self, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; task = g_task_new (self, NULL, callback, user_data); /* Chain up parent's setup, if any. If MM is built without QMI support, * the MBIM modem won't have any location capabilities. */ if (iface_modem_location_parent && iface_modem_location_parent->load_capabilities && iface_modem_location_parent->load_capabilities_finish) { iface_modem_location_parent->load_capabilities (self, (GAsyncReadyCallback)parent_load_capabilities_ready, task); return; } custom_location_load_capabilities (task, MM_MODEM_LOCATION_SOURCE_NONE); } /*****************************************************************************/ /* Disable location gathering (Location interface) */ static gboolean disable_location_gathering_finish (MMIfaceModemLocation *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void parent_disable_location_gathering_ready (MMIfaceModemLocation *self, GAsyncResult *res, GTask *task) { GError *error = NULL; if (!iface_modem_location_parent->disable_location_gathering_finish (self, res, &error)) g_task_return_error (task, error); else g_task_return_boolean (task, TRUE); g_object_unref (task); } static void parent_disable_location_gathering (GTask *task) { MMIfaceModemLocation *self; MMModemLocationSource source; self = MM_IFACE_MODEM_LOCATION (g_task_get_source_object (task)); source = GPOINTER_TO_UINT (g_task_get_task_data (task)); if (iface_modem_location_parent && iface_modem_location_parent->disable_location_gathering && iface_modem_location_parent->disable_location_gathering_finish) { iface_modem_location_parent->disable_location_gathering ( self, source, (GAsyncReadyCallback)parent_disable_location_gathering_ready, task); return; } g_task_return_boolean (task, TRUE); g_object_unref (task); } static void unmanaged_gps_disabled_ready (MMBaseModem *self, GAsyncResult *res, GTask *task) { GError *error = NULL; if (!mm_base_modem_at_command_finish (self, res, &error)) { g_task_return_error (task, error); g_object_unref (task); return; } parent_disable_location_gathering (task); } static void disable_location_gathering (MMIfaceModemLocation *_self, MMModemLocationSource source, GAsyncReadyCallback callback, gpointer user_data) { MMBroadbandModemMbimFoxconn *self = MM_BROADBAND_MODEM_MBIM_FOXCONN (_self); GTask *task; task = g_task_new (self, NULL, callback, user_data); g_task_set_task_data (task, GUINT_TO_POINTER (source), NULL); /* We only support Unmanaged GPS at this level */ if ((self->priv->unmanaged_gps_support != FEATURE_SUPPORTED) || (source != MM_MODEM_LOCATION_SOURCE_GPS_UNMANAGED)) { parent_disable_location_gathering (task); return; } mm_base_modem_at_command (MM_BASE_MODEM (_self), "^NV=30007,01,\"00\"", 3, FALSE, (GAsyncReadyCallback)unmanaged_gps_disabled_ready, task); } /*****************************************************************************/ /* Enable location gathering (Location interface) */ static gboolean enable_location_gathering_finish (MMIfaceModemLocation *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void unmanaged_gps_enabled_ready (MMBaseModem *self, GAsyncResult *res, GTask *task) { GError *error = NULL; if (!mm_base_modem_at_command_finish (self, res, &error)) g_task_return_error (task, error); else g_task_return_boolean (task, TRUE); g_object_unref (task); } static void custom_enable_location_gathering (GTask *task) { MMBroadbandModemMbimFoxconn *self; MMModemLocationSource source; self = g_task_get_source_object (task); source = GPOINTER_TO_UINT (g_task_get_task_data (task)); /* We only support Unmanaged GPS at this level */ if ((self->priv->unmanaged_gps_support != FEATURE_SUPPORTED) || (source != MM_MODEM_LOCATION_SOURCE_GPS_UNMANAGED)) { g_task_return_boolean (task, TRUE); g_object_unref (task); return; } mm_base_modem_at_command (MM_BASE_MODEM (self), "^NV=30007,01,\"01\"", 3, FALSE, (GAsyncReadyCallback)unmanaged_gps_enabled_ready, task); } static void parent_enable_location_gathering_ready (MMIfaceModemLocation *self, GAsyncResult *res, GTask *task) { GError *error = NULL; if (!iface_modem_location_parent->enable_location_gathering_finish (self, res, &error)) { g_task_return_error (task, error); g_object_unref (task); return; } custom_enable_location_gathering (task); } static void enable_location_gathering (MMIfaceModemLocation *self, MMModemLocationSource source, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; task = g_task_new (self, NULL, callback, user_data); g_task_set_task_data (task, GUINT_TO_POINTER (source), NULL); /* Chain up parent's gathering enable */ if (iface_modem_location_parent && iface_modem_location_parent->enable_location_gathering && iface_modem_location_parent->enable_location_gathering_finish) { iface_modem_location_parent->enable_location_gathering ( self, source, (GAsyncReadyCallback)parent_enable_location_gathering_ready, task); return; } custom_enable_location_gathering (task); } /*****************************************************************************/ MMBroadbandModemMbimFoxconn * mm_broadband_modem_mbim_foxconn_new (const gchar *device, const gchar *physdev, const gchar **drivers, const gchar *plugin, guint16 vendor_id, guint16 product_id) { const gchar *carrier_config_mapping = NULL; /* T77W968 (DW5821e/DW5829e is also T77W968) modules use t77w968 carrier mapping table. */ if ((vendor_id == 0x0489 && (product_id == 0xe0b4 || product_id == 0xe0b5)) || (vendor_id == 0x413c && (product_id == 0x81d7 || product_id == 0x81e0 || product_id == 0x81e4 || product_id == 0x81e6))) carrier_config_mapping = PKGDATADIR "/mm-foxconn-t77w968-carrier-mapping.conf"; return g_object_new (MM_TYPE_BROADBAND_MODEM_MBIM_FOXCONN, MM_BASE_MODEM_DEVICE, device, MM_BASE_MODEM_DRIVERS, drivers, MM_BASE_MODEM_PHYSDEV, physdev, MM_BASE_MODEM_PLUGIN, plugin, MM_BASE_MODEM_VENDOR_ID, vendor_id, MM_BASE_MODEM_PRODUCT_ID, product_id, /* MBIM bearer supports NET only */ MM_BASE_MODEM_DATA_NET_SUPPORTED, TRUE, MM_BASE_MODEM_DATA_TTY_SUPPORTED, FALSE, MM_IFACE_MODEM_SIM_HOT_SWAP_SUPPORTED, TRUE, MM_IFACE_MODEM_PERIODIC_SIGNAL_CHECK_DISABLED, TRUE, MM_IFACE_MODEM_LOCATION_ALLOW_GPS_UNMANAGED_ALWAYS, TRUE, MM_IFACE_MODEM_CARRIER_CONFIG_MAPPING, carrier_config_mapping, NULL); } static void mm_broadband_modem_mbim_foxconn_init (MMBroadbandModemMbimFoxconn *self) { /* Initialize private data */ self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, MM_TYPE_BROADBAND_MODEM_MBIM_FOXCONN, MMBroadbandModemMbimFoxconnPrivate); self->priv->unmanaged_gps_support = FEATURE_SUPPORT_UNKNOWN; } static void iface_modem_location_init (MMIfaceModemLocation *iface) { iface_modem_location_parent = g_type_interface_peek_parent (iface); iface->load_capabilities = location_load_capabilities; iface->load_capabilities_finish = location_load_capabilities_finish; iface->enable_location_gathering = enable_location_gathering; iface->enable_location_gathering_finish = enable_location_gathering_finish; iface->disable_location_gathering = disable_location_gathering; iface->disable_location_gathering_finish = disable_location_gathering_finish; } #if defined WITH_QMI && QMI_MBIM_QMUX_SUPPORTED static void iface_modem_firmware_init (MMIfaceModemFirmware *iface) { iface->load_update_settings = firmware_load_update_settings; iface->load_update_settings_finish = firmware_load_update_settings_finish; } #endif static void mm_broadband_modem_mbim_foxconn_class_init (MMBroadbandModemMbimFoxconnClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); g_type_class_add_private (object_class, sizeof (MMBroadbandModemMbimFoxconnPrivate)); } ModemManager-1.23.4-dev/src/plugins/foxconn/mm-broadband-modem-mbim-foxconn.h000066400000000000000000000052531456466623000270410ustar00rootroot00000000000000/* -*- 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) 2018-2019 Aleksander Morgado */ #ifndef MM_BROADBAND_MODEM_MBIM_FOXCONN_H #define MM_BROADBAND_MODEM_MBIM_FOXCONN_H #include "mm-broadband-modem-mbim.h" #define MM_TYPE_BROADBAND_MODEM_MBIM_FOXCONN (mm_broadband_modem_mbim_foxconn_get_type ()) #define MM_BROADBAND_MODEM_MBIM_FOXCONN(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_BROADBAND_MODEM_MBIM_FOXCONN, MMBroadbandModemMbimFoxconn)) #define MM_BROADBAND_MODEM_MBIM_FOXCONN_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_BROADBAND_MODEM_MBIM_FOXCONN, MMBroadbandModemMbimFoxconnClass)) #define MM_IS_BROADBAND_MODEM_MBIM_FOXCONN(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_BROADBAND_MODEM_MBIM_FOXCONN)) #define MM_IS_BROADBAND_MODEM_MBIM_FOXCONN_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_BROADBAND_MODEM_MBIM_FOXCONN)) #define MM_BROADBAND_MODEM_MBIM_FOXCONN_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_BROADBAND_MODEM_MBIM_FOXCONN, MMBroadbandModemMbimFoxconnClass)) typedef struct _MMBroadbandModemMbimFoxconn MMBroadbandModemMbimFoxconn; typedef struct _MMBroadbandModemMbimFoxconnClass MMBroadbandModemMbimFoxconnClass; typedef struct _MMBroadbandModemMbimFoxconnPrivate MMBroadbandModemMbimFoxconnPrivate; struct _MMBroadbandModemMbimFoxconn { MMBroadbandModemMbim parent; MMBroadbandModemMbimFoxconnPrivate *priv; }; struct _MMBroadbandModemMbimFoxconnClass{ MMBroadbandModemMbimClass parent; }; GType mm_broadband_modem_mbim_foxconn_get_type (void); MMBroadbandModemMbimFoxconn *mm_broadband_modem_mbim_foxconn_new (const gchar *device, const gchar *physdev, const gchar **driver, const gchar *plugin, guint16 vendor_id, guint16 product_id); #endif /* MM_BROADBAND_MODEM_MBIM_FOXCONN_H */ ModemManager-1.23.4-dev/src/plugins/foxconn/mm-foxconn-t77w968-carrier-mapping.conf000066400000000000000000000074651456466623000277460ustar00rootroot00000000000000 # # T77W968 carrier mapping table # # This table maps the MCCMNC of the SIM card with the corresponding # configuration description as reported by the QMI PDC service in # this module. # [foxconn t77w968] # AT&T 302220=ATT 302221=ATT 31030=ATT 31070=ATT 31090=ATT 310150=ATT 310170=ATT 310280=ATT 310380=ATT 310410=ATT 310560=ATT 310650=ATT 310680=ATT 310980=ATT 311180=ATT 90118=ATT # FirstNet 312670=A2 313100=A2 313110=A2 313120=A2 313130=A2 313140=A2 # Verizon 310590=Verizon 310890=Verizon 311270=Verizon 311480=Verizon 312770=Verizon # Vodafone 20205=Vodafone 20404=Vodafone 20601=Vodafone 20810=Vodafone 21401=Vodafone 21670=Vodafone 21910=Vodafone 22005=Vodafone 22210=Vodafone 22601=Vodafone 23003=Vodafone 23201=Vodafone 23415=Vodafone 23801=Vodafone 24405=Vodafone 24602=Vodafone 24705=Vodafone 24802=Vodafone 25001=Vodafone 26202=Vodafone 26209=Vodafone 26801=Vodafone 27077=Vodafone 27201=Vodafone 27402=Vodafone 27602=Vodafone 27801=Vodafone 28001=Vodafone 28401=Vodafone 28602=Vodafone 28802=Vodafone 29340=Vodafone 29403=Vodafone 40004=Vodafone 40401=Vodafone 40405=Vodafone 40411=Vodafone 40413=Vodafone 40415=Vodafone 40420=Vodafone 40427=Vodafone 40430=Vodafone 40443=Vodafone 40446=Vodafone 40460=Vodafone 40484=Vodafone 40486=Vodafone 40488=Vodafone 40566=Vodafone 40567=Vodafone 405750=Vodafone 405751=Vodafone 405752=Vodafone 405753=Vodafone 405754=Vodafone 405755=Vodafone 405756=Vodafone 41302=Vodafone 42403=Vodafone 42602=Vodafone 42702=Vodafone 46601=Vodafone 46603=Vodafone 50213=Vodafone 50219=Vodafone 50503=Vodafone 52503=Vodafone 52505=Vodafone 53001=Vodafone 54201=Vodafone 60202=Vodafone 62002=Vodafone 63001=Vodafone 63902=Vodafone 64004=Vodafone 64304=Vodafone 64710=Vodafone 65101=Vodafone 65501=Vodafone 73001=Vodafone 90128=Vodafone # Orange 20610=Orange 20801=Orange 20802=Orange 21403=Orange 21409=Orange 22610=Orange 23101=Orange 23105=Orange 23430=Orange 23433=Orange 23434=Orange 25901=Orange 26003=Orange 26005=Orange 27099=Orange 28310=Orange # Telefonica Movistar 21405=Telefonica 21407=Telefonica # Swisscom 22801=Swisscom 29501=Swisscom # Telstra 50501=Telstra 50506=Telstra 50571=Telstra 50572=Telstra # Sprint 310120=Sprint # Optus 50202=Optus # NTT DoCoMo 44002=Docomo 44003=Docomo 44009=Docomo 44010=Docomo 44011=Docomo 44012=Docomo 44013=Docomo 44014=Docomo 44015=Docomo 44016=Docomo 44017=Docomo 44018=Docomo 44019=Docomo 44022=Docomo 44023=Docomo 44024=Docomo 44025=Docomo 44026=Docomo 44027=Docomo 44028=Docomo 44029=Docomo 44030=Docomo 44031=Docomo 44032=Docomo 44033=Docomo 44034=Docomo 44035=Docomo 44036=Docomo 44037=Docomo 44038=Docomo 44039=Docomo 44049=Docomo 44058=Docomo 44060=Docomo 44061=Docomo 44062=Docomo 44063=Docomo 44064=Docomo 44065=Docomo 44066=Docomo 44067=Docomo 44068=Docomo 44069=Docomo 44087=Docomo 44099=Docomo 44140=Docomo 44141=Docomo 44142=Docomo 44143=Docomo 44144=Docomo 44145=Docomo 44190=Docomo 44101=Docomo 44192=Docomo 44193=Docomo 44194=Docomo 44198=Docomo 44199=Docomo # KDDI 44007=KDDI 44008=KDDI 44050=KDDI 44051=KDDI 44052=KDDI 44053=KDDI 44054=KDDI 44055=KDDI 44056=KDDI 44070=KDDI 44071=KDDI 44072=KDDI 44073=KDDI 44074=KDDI 44075=KDDI 44076=KDDI 44077=KDDI 44078=KDDI 44079=KDDI 44080=KDDI 44081=KDDI 44082=KDDI 44083=KDDI 44084=KDDI 44085=KDDI 44086=KDDI 44088=KDDI 44089=KDDI 44150=KDDI 44151=KDDI 44170=KDDI # SoftBank 44000=SBM 44004=SBM 44006=SBM 44020=SBM 44021=SBM 44040=SBM 44041=SBM 44042=SBM 44043=SBM 44044=SBM 44045=SBM 44046=SBM 44047=SBM 44048=SBM 44090=SBM 44092=SBM 44093=SBM 44094=SBM 44095=SBM 44096=SBM 44097=SBM 44098=SBM 44101=SBM 44161=SBM 44162=SBM 44163=SBM 44164=SBM 44165=SBM # EE UK 23430=EE 23431=EE 23432=EE 23433=EE 23434=EE 23476=EE 23501=EE 23502=EE 23577=EE # Deutsche Telekom 20201=DT 20416=DT 20420=DT 21630=DT 21901=DT 22603=DT 22606=DT 23001=DT 23102=DT 23203=DT 23207=DT 26002=DT 26201=DT 27601=DT 29401=DT 29702=DT # Others generic=GCF ModemManager-1.23.4-dev/src/plugins/foxconn/mm-plugin-foxconn.c000066400000000000000000000106451456466623000243760ustar00rootroot00000000000000/* -*- 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. * * 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. * * Copyright (C) 2019 Aleksander Morgado */ #include #include #define _LIBMM_INSIDE_MM #include #include "mm-plugin-common.h" #include "mm-log-object.h" #include "mm-broadband-modem.h" #if defined WITH_QMI #include "mm-broadband-modem-qmi.h" #endif #if defined WITH_MBIM #include "mm-broadband-modem-mbim-foxconn.h" #endif #define MM_TYPE_PLUGIN_FOXCONN mm_plugin_foxconn_get_type () MM_DEFINE_PLUGIN (FOXCONN, foxconn, Foxconn) /*****************************************************************************/ static MMBaseModem * create_modem (MMPlugin *self, const gchar *uid, const gchar *physdev, const gchar **drivers, guint16 vendor, guint16 product, guint16 subsystem_vendor, GList *probes, GError **error) { #if defined WITH_QMI if (mm_port_probe_list_has_qmi_port (probes)) { mm_obj_dbg (self, "QMI-powered Foxconn-branded modem found..."); return MM_BASE_MODEM (mm_broadband_modem_qmi_new (uid, physdev, drivers, mm_plugin_get_name (self), vendor, product)); } #endif #if defined WITH_MBIM if (mm_port_probe_list_has_mbim_port (probes)) { mm_obj_dbg (self, "MBIM-powered Foxconn-branded modem found..."); return MM_BASE_MODEM (mm_broadband_modem_mbim_foxconn_new (uid, physdev, drivers, mm_plugin_get_name (self), vendor, product)); } #endif mm_obj_dbg (self, "Foxconn-branded generic modem found..."); return MM_BASE_MODEM (mm_broadband_modem_new (uid, physdev, drivers, mm_plugin_get_name (self), vendor, product)); } /*****************************************************************************/ MM_PLUGIN_NAMED_CREATOR_SCOPE MMPlugin * mm_plugin_create_foxconn (void) { static const gchar *subsystems[] = { "tty", "net", "usbmisc", "wwan", NULL }; static const guint16 vendor_ids[] = { 0x0489, /* usb vid */ 0x105b, /* pci vid */ 0 }; return MM_PLUGIN ( g_object_new (MM_TYPE_PLUGIN_FOXCONN, MM_PLUGIN_NAME, MM_MODULE_NAME, MM_PLUGIN_ALLOWED_SUBSYSTEMS, subsystems, MM_PLUGIN_ALLOWED_VENDOR_IDS, vendor_ids, MM_PLUGIN_ALLOWED_AT, TRUE, MM_PLUGIN_ALLOWED_QCDM, TRUE, MM_PLUGIN_ALLOWED_QMI, TRUE, MM_PLUGIN_ALLOWED_MBIM, TRUE, NULL)); } static void mm_plugin_foxconn_init (MMPluginFoxconn *self) { } static void mm_plugin_foxconn_class_init (MMPluginFoxconnClass *klass) { MMPluginClass *plugin_class = MM_PLUGIN_CLASS (klass); plugin_class->create_modem = create_modem; } ModemManager-1.23.4-dev/src/plugins/foxconn/mm-shared.c000066400000000000000000000013131456466623000226660ustar00rootroot00000000000000/* -*- 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) 2019 Aleksander Morgado */ #include "mm-shared-common.h" MM_DEFINE_SHARED (foxconn) ModemManager-1.23.4-dev/src/plugins/generic/000077500000000000000000000000001456466623000206115ustar00rootroot00000000000000ModemManager-1.23.4-dev/src/plugins/generic/mm-plugin-generic.c000066400000000000000000000101331456466623000242720ustar00rootroot00000000000000/* -*- 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 - 2010 Dan Williams */ #include #include #include #include #include #include #include #include #include #include #define _LIBMM_INSIDE_MM #include #include "mm-plugin-common.h" #include "mm-broadband-modem.h" #include "mm-serial-parsers.h" #include "mm-log-object.h" #if defined WITH_QMI #include "mm-broadband-modem-qmi.h" #endif #if defined WITH_MBIM #include "mm-broadband-modem-mbim.h" #endif #define MM_TYPE_PLUGIN_GENERIC mm_plugin_generic_get_type () MM_DEFINE_PLUGIN (GENERIC, generic, Generic) /*****************************************************************************/ static MMBaseModem * create_modem (MMPlugin *self, const gchar *uid, const gchar *physdev, const gchar **drivers, guint16 vendor, guint16 product, guint16 subsystem_vendor, GList *probes, GError **error) { #if defined WITH_QMI if (mm_port_probe_list_has_qmi_port (probes)) { mm_obj_dbg (self, "QMI-powered generic modem found..."); return MM_BASE_MODEM (mm_broadband_modem_qmi_new (uid, physdev, drivers, mm_plugin_get_name (self), vendor, product)); } #endif #if defined WITH_MBIM if (mm_port_probe_list_has_mbim_port (probes)) { mm_obj_dbg (self, "MBIM-powered generic modem found..."); return MM_BASE_MODEM (mm_broadband_modem_mbim_new (uid, physdev, drivers, mm_plugin_get_name (self), vendor, product)); } #endif return MM_BASE_MODEM (mm_broadband_modem_new (uid, physdev, drivers, mm_plugin_get_name (self), vendor, product)); } /*****************************************************************************/ MM_PLUGIN_NAMED_CREATOR_SCOPE MMPlugin * mm_plugin_create_generic (void) { static const gchar *subsystems[] = { "tty", "net", "usbmisc", "wwan", NULL }; return MM_PLUGIN ( g_object_new (MM_TYPE_PLUGIN_GENERIC, MM_PLUGIN_NAME, MM_MODULE_NAME, MM_PLUGIN_IS_GENERIC, TRUE, MM_PLUGIN_ALLOWED_SUBSYSTEMS, subsystems, MM_PLUGIN_ALLOWED_AT, TRUE, MM_PLUGIN_REQUIRED_QCDM, TRUE, MM_PLUGIN_ALLOWED_QMI, TRUE, MM_PLUGIN_ALLOWED_MBIM, TRUE, NULL)); } static void mm_plugin_generic_init (MMPluginGeneric *self) { } static void mm_plugin_generic_class_init (MMPluginGenericClass *klass) { MMPluginClass *plugin_class = MM_PLUGIN_CLASS (klass); plugin_class->create_modem = create_modem; } ModemManager-1.23.4-dev/src/plugins/generic/tests/000077500000000000000000000000001456466623000217535ustar00rootroot00000000000000ModemManager-1.23.4-dev/src/plugins/generic/tests/test-service-generic.c000066400000000000000000000052201456466623000261450ustar00rootroot00000000000000/* -*- 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) 2016 Aleksander Morgado */ #include #include #include #include #include #include "test-port-context.h" #include "test-fixture.h" /*****************************************************************************/ static void test_enable_disable (TestFixture *fixture) { GError *error = NULL; MMObject *obj; MMModem *modem; TestPortContext *port0; gchar *ports [] = { NULL, NULL }; /* Create port name, and add process ID so that multiple runs of this test * in the same system don't clash with each other */ ports[0] = g_strdup_printf ("abstract:port0:%ld", (glong) getpid ()); g_debug ("test service generic: using abstract port at '%s'", ports[0]); /* Setup new port context */ port0 = test_port_context_new (ports[0]); test_port_context_load_commands (port0, COMMON_GSM_PORT_CONF); test_port_context_start (port0); /* Ensure no modem is modem exported */ test_fixture_no_modem (fixture); /* Set the test profile */ test_fixture_set_profile (fixture, "test-enable-disable", "generic", (const gchar *const *)ports); /* Wait and get the modem object */ obj = test_fixture_get_modem (fixture); /* Get Modem interface, and enable */ modem = mm_object_get_modem (obj); g_assert (modem != NULL); mm_modem_enable_sync (modem, NULL, &error); g_assert_no_error (error); /* And disable */ mm_modem_disable_sync (modem, NULL, &error); g_assert_no_error (error); g_object_unref (modem); g_object_unref (obj); /* Stop port context */ test_port_context_stop (port0); test_port_context_free (port0); g_free (ports[0]); } /*****************************************************************************/ int main (int argc, char *argv[]) { g_test_init (&argc, &argv, NULL); TEST_ADD ("/MM/Service/Generic/enable-disable", test_enable_disable); return g_test_run (); } ModemManager-1.23.4-dev/src/plugins/gosuncn/000077500000000000000000000000001456466623000206515ustar00rootroot00000000000000ModemManager-1.23.4-dev/src/plugins/gosuncn/77-mm-gosuncn-port-types.rules000066400000000000000000000016131456466623000263700ustar00rootroot00000000000000# do not edit this file, it will be overwritten on update ACTION!="add|change|move|bind", GOTO="mm_gosuncn_port_types_end" SUBSYSTEMS=="usb", ATTRS{idVendor}=="305a", GOTO="mm_gosuncn_port_types" GOTO="mm_gosuncn_port_types_end" LABEL="mm_gosuncn_port_types" SUBSYSTEMS=="usb", ATTRS{bInterfaceNumber}=="?*", ENV{.MM_USBIFNUM}="$attr{bInterfaceNumber}" # Gosuncn GM800 # Interfaces #3 and #4 are MBIM ATTRS{idVendor}=="305a", ATTRS{idProduct}=="1405", ENV{.MM_USBIFNUM}=="00", ENV{ID_MM_PORT_IGNORE}="1" ATTRS{idVendor}=="305a", ATTRS{idProduct}=="1405", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1" ATTRS{idVendor}=="305a", ATTRS{idProduct}=="1405", ENV{.MM_USBIFNUM}=="02", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1" ATTRS{idVendor}=="305a", ATTRS{idProduct}=="1405", ENV{.MM_USBIFNUM}=="05", ENV{ID_MM_PORT_IGNORE}="1" LABEL="mm_gosuncn_port_types_end" ModemManager-1.23.4-dev/src/plugins/gosuncn/mm-plugin-gosuncn.c000066400000000000000000000102261456466623000243750ustar00rootroot00000000000000/* -*- 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) 2020 Aleksander Morgado */ #include #include #define _LIBMM_INSIDE_MM #include #include "mm-log-object.h" #include "mm-plugin-common.h" #include "mm-broadband-modem.h" #if defined WITH_QMI # include "mm-broadband-modem-qmi.h" #endif #if defined WITH_MBIM # include "mm-broadband-modem-mbim.h" #endif #define MM_TYPE_PLUGIN_GOSUNCN mm_plugin_gosuncn_get_type () MM_DEFINE_PLUGIN (GOSUNCN, gosuncn, Gosuncn) /*****************************************************************************/ static MMBaseModem * create_modem (MMPlugin *self, const gchar *uid, const gchar *physdev, const gchar **drivers, guint16 vendor, guint16 product, guint16 subsystem_vendor, GList *probes, GError **error) { #if defined WITH_QMI if (mm_port_probe_list_has_qmi_port (probes)) { mm_obj_dbg (self, "QMI-powered Gosuncn modem found..."); return MM_BASE_MODEM (mm_broadband_modem_qmi_new (uid, physdev, drivers, mm_plugin_get_name (self), vendor, product)); } #endif #if defined WITH_MBIM if (mm_port_probe_list_has_mbim_port (probes)) { mm_obj_dbg (self, "MBIM-powered Gosuncn modem found..."); return MM_BASE_MODEM (mm_broadband_modem_mbim_new (uid, physdev, drivers, mm_plugin_get_name (self), vendor, product)); } #endif /* Fallback to default modem in the worst case */ return MM_BASE_MODEM (mm_broadband_modem_new (uid, physdev, drivers, mm_plugin_get_name (self), vendor, product)); } /*****************************************************************************/ MM_PLUGIN_NAMED_CREATOR_SCOPE MMPlugin * mm_plugin_create_gosuncn (void) { static const gchar *subsystems[] = { "tty", "net", "usbmisc", NULL }; static const guint16 vendor_ids[] = { 0x305a, 0 }; static const gchar *drivers[] = { "qmi_wwan", "cdc_mbim", NULL }; return MM_PLUGIN ( g_object_new (MM_TYPE_PLUGIN_GOSUNCN, MM_PLUGIN_NAME, MM_MODULE_NAME, MM_PLUGIN_ALLOWED_SUBSYSTEMS, subsystems, MM_PLUGIN_ALLOWED_VENDOR_IDS, vendor_ids, MM_PLUGIN_ALLOWED_DRIVERS, drivers, MM_PLUGIN_ALLOWED_AT, TRUE, MM_PLUGIN_ALLOWED_QCDM, TRUE, MM_PLUGIN_ALLOWED_QMI, TRUE, MM_PLUGIN_ALLOWED_MBIM, TRUE, NULL)); } static void mm_plugin_gosuncn_init (MMPluginGosuncn *self) { } static void mm_plugin_gosuncn_class_init (MMPluginGosuncnClass *klass) { MMPluginClass *plugin_class = MM_PLUGIN_CLASS (klass); plugin_class->create_modem = create_modem; } ModemManager-1.23.4-dev/src/plugins/haier/000077500000000000000000000000001456466623000202655ustar00rootroot00000000000000ModemManager-1.23.4-dev/src/plugins/haier/77-mm-haier-port-types.rules000066400000000000000000000010151456466623000254140ustar00rootroot00000000000000# do not edit this file, it will be overwritten on update ACTION!="add|change|move|bind", GOTO="mm_haier_port_types_end" SUBSYSTEMS=="usb", ATTRS{idVendor}=="201e", GOTO="mm_haier_port_types" GOTO="mm_haier_port_types_end" LABEL="mm_haier_port_types" SUBSYSTEMS=="usb", ATTRS{bInterfaceNumber}=="?*", ENV{.MM_USBIFNUM}="$attr{bInterfaceNumber}" # Haier CE81B ATTRS{idVendor}=="201e", ATTRS{idProduct}=="10f8", ENV{.MM_USBIFNUM}=="03", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1" LABEL="mm_haier_port_types_end" ModemManager-1.23.4-dev/src/plugins/haier/mm-plugin-haier.c000066400000000000000000000050501456466623000234240ustar00rootroot00000000000000/* -*- 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) 2014 Aleksander Morgado */ #include #include #define _LIBMM_INSIDE_MM #include #include "mm-plugin-common.h" #include "mm-broadband-modem.h" #define MM_TYPE_PLUGIN_HAIER mm_plugin_haier_get_type () MM_DEFINE_PLUGIN (HAIER, haier, Haier) /*****************************************************************************/ static MMBaseModem * create_modem (MMPlugin *self, const gchar *uid, const gchar *physdev, const gchar **drivers, guint16 vendor, guint16 product, guint16 subsystem_vendor, GList *probes, GError **error) { return MM_BASE_MODEM (mm_broadband_modem_new (uid, physdev, drivers, mm_plugin_get_name (self), vendor, product)); } /*****************************************************************************/ MM_PLUGIN_NAMED_CREATOR_SCOPE MMPlugin * mm_plugin_create_haier (void) { static const gchar *subsystems[] = { "tty", NULL }; static const guint16 vendor_ids[] = { 0x201e, 0 }; return MM_PLUGIN (g_object_new (MM_TYPE_PLUGIN_HAIER, MM_PLUGIN_NAME, MM_MODULE_NAME, MM_PLUGIN_ALLOWED_SUBSYSTEMS, subsystems, MM_PLUGIN_ALLOWED_VENDOR_IDS, vendor_ids, MM_PLUGIN_ALLOWED_AT, TRUE, NULL)); } static void mm_plugin_haier_init (MMPluginHaier *self) { } static void mm_plugin_haier_class_init (MMPluginHaierClass *klass) { MMPluginClass *plugin_class = MM_PLUGIN_CLASS (klass); plugin_class->create_modem = create_modem; } ModemManager-1.23.4-dev/src/plugins/huawei/000077500000000000000000000000001456466623000204575ustar00rootroot00000000000000ModemManager-1.23.4-dev/src/plugins/huawei/77-mm-huawei-net-port-types.rules000066400000000000000000000050441456466623000265720ustar00rootroot00000000000000# do not edit this file, it will be overwritten on update ACTION!="add|change|move|bind", GOTO="mm_huawei_port_types_end" SUBSYSTEMS=="usb", ATTRS{idVendor}=="12d1", GOTO="mm_huawei_port_types" GOTO="mm_huawei_port_types_end" LABEL="mm_huawei_port_types" # MU609 does not support getportmode (crashes modem with default firmware) ATTRS{idVendor}=="12d1", ATTRS{idProduct}=="1573", ENV{ID_MM_HUAWEI_DISABLE_GETPORTMODE}="1" # Mark the modem and at port flags for ModemManager SUBSYSTEMS=="usb", ATTRS{bInterfaceClass}=="ff", ATTRS{bInterfaceSubClass}=="01", ATTRS{bInterfaceProtocol}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PPP}="1" SUBSYSTEMS=="usb", ATTRS{bInterfaceClass}=="ff", ATTRS{bInterfaceSubClass}=="01", ATTRS{bInterfaceProtocol}=="02", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1" SUBSYSTEMS=="usb", ATTRS{bInterfaceClass}=="ff", ATTRS{bInterfaceSubClass}=="02", ATTRS{bInterfaceProtocol}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PPP}="1" SUBSYSTEMS=="usb", ATTRS{bInterfaceClass}=="ff", ATTRS{bInterfaceSubClass}=="02", ATTRS{bInterfaceProtocol}=="02", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1" # GPS NMEA port on MU609 SUBSYSTEMS=="usb", ATTRS{bInterfaceClass}=="ff", ATTRS{bInterfaceSubClass}=="01", ATTRS{bInterfaceProtocol}=="05", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_GPS}="1" # GPS NMEA port on MU909 SUBSYSTEMS=="usb", ATTRS{bInterfaceClass}=="ff", ATTRS{bInterfaceSubClass}=="01", ATTRS{bInterfaceProtocol}=="14", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_GPS}="1" # GPS NMEA port on MU906e SUBSYSTEMS=="usb", ATTRS{bInterfaceClass}=="ff", ATTRS{bInterfaceSubClass}=="06", ATTRS{bInterfaceProtocol}=="14", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_GPS}="1" # Only the standard ECM or NCM port can support dial-up with AT NDISDUP through AT port SUBSYSTEMS=="usb", ATTRS{bInterfaceClass}=="02", ATTRS{bInterfaceSubClass}=="06",ATTRS{bInterfaceProtocol}=="00", ENV{ID_MM_HUAWEI_NDISDUP_SUPPORTED}="1" SUBSYSTEMS=="usb", ATTRS{bInterfaceClass}=="02", ATTRS{bInterfaceSubClass}=="0d",ATTRS{bInterfaceProtocol}=="00", ENV{ID_MM_HUAWEI_NDISDUP_SUPPORTED}="1" # Airtel branded E3372h-607, using huawei-cdc-ncm driver but with unresponsive cdc-wdm port ATTRS{idVendor}=="12d1", ATTRS{idProduct}=="1506", ENV{ID_MM_HUAWEI_NDISDUP_SUPPORTED}="1" # R215, Disable CPOL based features ATTRS{idVendor}=="12d1", ATTRS{idProduct}=="1588", ENV{ID_MM_PREFERRED_NETWORKS_CPOL_DISABLED}="1" # E226, Disable CPOL based features ATTRS{idVendor}=="12d1", ATTRS{idProduct}=="1003", ENV{ID_MM_PREFERRED_NETWORKS_CPOL_DISABLED}="1" LABEL="mm_huawei_port_types_end" ModemManager-1.23.4-dev/src/plugins/huawei/mm-broadband-bearer-huawei.c000066400000000000000000000725561456466623000257030ustar00rootroot00000000000000/* -*- 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 - 2012 Red Hat, Inc. * Copyright (C) 2012 Lanedo GmbH * Copyright (C) 2012 Huawei Technologies Co., Ltd * * Author: Franko fang */ #include #include #include #include #include #include #include #include #include "mm-base-modem-at.h" #include "mm-broadband-bearer-huawei.h" #include "mm-log-object.h" #include "mm-modem-helpers.h" #include "mm-modem-helpers-huawei.h" #include "mm-daemon-enums-types.h" G_DEFINE_TYPE (MMBroadbandBearerHuawei, mm_broadband_bearer_huawei, MM_TYPE_BROADBAND_BEARER) struct _MMBroadbandBearerHuaweiPrivate { gpointer connect_pending; gpointer disconnect_pending; }; /*****************************************************************************/ static MMPortSerialAt * get_dial_port (MMBroadbandModemHuawei *modem, MMPort *data, MMPortSerialAt *primary) { MMPortSerialAt *dial_port; /* See if we have a cdc-wdm AT port for the interface */ dial_port = (mm_broadband_modem_huawei_peek_port_at_for_data ( MM_BROADBAND_MODEM_HUAWEI (modem), data)); if (dial_port) return g_object_ref (dial_port); /* Otherwise, fallback to using the primary port for dialing */ return g_object_ref (primary); } /*****************************************************************************/ /* Connect 3GPP */ typedef enum { CONNECT_3GPP_CONTEXT_STEP_FIRST = 0, CONNECT_3GPP_CONTEXT_STEP_NDISDUP, CONNECT_3GPP_CONTEXT_STEP_NDISSTATQRY, CONNECT_3GPP_CONTEXT_STEP_IP_CONFIG, CONNECT_3GPP_CONTEXT_STEP_LAST } Connect3gppContextStep; typedef struct { MMBaseModem *modem; MMPortSerialAt *primary; MMPort *data; Connect3gppContextStep step; guint check_count; guint failed_ndisstatqry_count; MMBearerIpConfig *ipv4_config; } Connect3gppContext; static void connect_3gpp_context_free (Connect3gppContext *ctx) { g_object_unref (ctx->modem); g_clear_object (&ctx->ipv4_config); g_clear_object (&ctx->data); g_clear_object (&ctx->primary); g_slice_free (Connect3gppContext, ctx); } static MMBearerConnectResult * connect_3gpp_finish (MMBroadbandBearer *self, GAsyncResult *res, GError **error) { return g_task_propagate_pointer (G_TASK (res), error); } static void connect_3gpp_context_step (GTask *task); static void connect_dhcp_check_ready (MMBaseModem *modem, GAsyncResult *res, MMBroadbandBearerHuawei *self) { GTask *task; Connect3gppContext *ctx; const gchar *response; GError *error = NULL; task = self->priv->connect_pending; g_assert (task != NULL); ctx = g_task_get_task_data (task); /* Balance refcount */ g_object_unref (self); /* Cache IPv4 details if available, otherwise clients will have to use DHCP */ response = mm_base_modem_at_command_full_finish (modem, res, &error); if (response) { guint address = 0; guint prefix = 0; guint gateway = 0; guint dns1 = 0; guint dns2 = 0; if (mm_huawei_parse_dhcp_response (response, &address, &prefix, &gateway, &dns1, &dns2, &error)) { GInetAddress *addr; gchar *strarr[3] = { NULL, NULL, NULL }; guint n = 0; gchar *str; mm_bearer_ip_config_set_method (ctx->ipv4_config, MM_BEARER_IP_METHOD_STATIC); addr = g_inet_address_new_from_bytes ((guint8 *)&address, G_SOCKET_FAMILY_IPV4); str = g_inet_address_to_string (addr); mm_bearer_ip_config_set_address (ctx->ipv4_config, str); g_free (str); g_object_unref (addr); /* Netmask */ mm_bearer_ip_config_set_prefix (ctx->ipv4_config, prefix); /* Gateway */ addr = g_inet_address_new_from_bytes ((guint8 *)&gateway, G_SOCKET_FAMILY_IPV4); str = g_inet_address_to_string (addr); mm_bearer_ip_config_set_gateway (ctx->ipv4_config, str); g_free (str); g_object_unref (addr); /* DNS */ if (dns1) { addr = g_inet_address_new_from_bytes ((guint8 *)&dns1, G_SOCKET_FAMILY_IPV4); strarr[n++] = g_inet_address_to_string (addr); g_object_unref (addr); } if (dns2) { addr = g_inet_address_new_from_bytes ((guint8 *)&dns2, G_SOCKET_FAMILY_IPV4); strarr[n++] = g_inet_address_to_string (addr); g_object_unref (addr); } mm_bearer_ip_config_set_dns (ctx->ipv4_config, (const gchar **)strarr); g_free (strarr[0]); g_free (strarr[1]); } else { mm_obj_dbg (self, "unexpected response to ^DHCP command: %s", error->message); } } g_clear_error (&error); ctx->step++; connect_3gpp_context_step (task); } static gboolean connect_retry_ndisstatqry_check_cb (MMBroadbandBearerHuawei *self) { GTask *task; /* Recover context */ task = self->priv->connect_pending; g_assert (task != NULL); /* Balance refcount */ g_object_unref (self); /* Retry same step */ connect_3gpp_context_step (task); return G_SOURCE_REMOVE; } static void connect_ndisstatqry_check_ready (MMBaseModem *modem, GAsyncResult *res, MMBroadbandBearerHuawei *self) { GTask *task; Connect3gppContext *ctx; const gchar *response; GError *error = NULL; gboolean ipv4_available = FALSE; gboolean ipv4_connected = FALSE; gboolean ipv6_available = FALSE; gboolean ipv6_connected = FALSE; task = self->priv->connect_pending; g_assert (task != NULL); ctx = g_task_get_task_data (task); /* Balance refcount */ g_object_unref (self); response = mm_base_modem_at_command_full_finish (modem, res, &error); if (!response || !mm_huawei_parse_ndisstatqry_response (response, &ipv4_available, &ipv4_connected, &ipv6_available, &ipv6_connected, &error)) { ctx->failed_ndisstatqry_count++; mm_obj_dbg (self, "unexpected response to ^NDISSTATQRY command: %s (%u attempts so far)", error->message, ctx->failed_ndisstatqry_count); g_error_free (error); } /* Connected in IPv4? */ if (ipv4_available && ipv4_connected) { /* Success! */ ctx->step++; connect_3gpp_context_step (task); return; } /* Setup timeout to retry the same step */ g_timeout_add_seconds (1, (GSourceFunc)connect_retry_ndisstatqry_check_cb, g_object_ref (self)); } static void connect_ndisdup_ready (MMBaseModem *modem, GAsyncResult *res, MMBroadbandBearerHuawei *self) { GTask *task; Connect3gppContext *ctx; GError *error = NULL; task = self->priv->connect_pending; g_assert (task != NULL); ctx = g_task_get_task_data (task); /* Balance refcount */ g_object_unref (self); if (!mm_base_modem_at_command_full_finish (modem, res, &error)) { /* Clear task */ self->priv->connect_pending = NULL; g_task_return_error (task, error); g_object_unref (task); return; } /* Go to next step */ ctx->step++; connect_3gpp_context_step (task); } typedef enum { MM_BEARER_HUAWEI_AUTH_UNKNOWN = -1, MM_BEARER_HUAWEI_AUTH_NONE = 0, MM_BEARER_HUAWEI_AUTH_PAP = 1, MM_BEARER_HUAWEI_AUTH_CHAP = 2, MM_BEARER_HUAWEI_AUTH_MSCHAPV2 = 3, } MMBearerHuaweiAuthPref; static gint huawei_parse_auth_type (MMBearerAllowedAuth mm_auth) { switch (mm_auth) { case MM_BEARER_ALLOWED_AUTH_NONE: return MM_BEARER_HUAWEI_AUTH_NONE; case MM_BEARER_ALLOWED_AUTH_PAP: return MM_BEARER_HUAWEI_AUTH_PAP; case MM_BEARER_ALLOWED_AUTH_CHAP: return MM_BEARER_HUAWEI_AUTH_CHAP; case MM_BEARER_ALLOWED_AUTH_MSCHAPV2: return MM_BEARER_HUAWEI_AUTH_MSCHAPV2; default: case MM_BEARER_ALLOWED_AUTH_UNKNOWN: case MM_BEARER_ALLOWED_AUTH_MSCHAP: case MM_BEARER_ALLOWED_AUTH_EAP: return MM_BEARER_HUAWEI_AUTH_UNKNOWN; } } static void connect_3gpp_context_step (GTask *task) { MMBroadbandBearerHuawei *self; Connect3gppContext *ctx; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); /* Check for cancellation */ if (g_cancellable_is_cancelled (g_task_get_cancellable (task))) { /* Clear task */ self->priv->connect_pending = NULL; /* If we already sent the connetion command, send the disconnection one */ if (ctx->step > CONNECT_3GPP_CONTEXT_STEP_NDISDUP) mm_base_modem_at_command_full (ctx->modem, ctx->primary, "^NDISDUP=1,0", MM_BASE_BEARER_DEFAULT_DISCONNECTION_TIMEOUT, FALSE, FALSE, NULL, NULL, /* Do not care the AT response */ NULL); g_task_return_new_error (task, G_IO_ERROR, G_IO_ERROR_CANCELLED, "Huawei connection operation has been cancelled"); g_object_unref (task); return; } switch (ctx->step) { case CONNECT_3GPP_CONTEXT_STEP_FIRST: { MMBearerIpFamily ip_family; ip_family = mm_bearer_properties_get_ip_type (mm_base_bearer_peek_config (MM_BASE_BEARER (self))); mm_3gpp_normalize_ip_family (&ip_family, TRUE); if (ip_family != MM_BEARER_IP_FAMILY_IPV4) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "Only IPv4 is supported by this modem"); g_object_unref (task); return; } /* Store the task */ self->priv->connect_pending = task; ctx->step++; } /* fall through */ case CONNECT_3GPP_CONTEXT_STEP_NDISDUP: { const gchar *apn; const gchar *user; const gchar *passwd; MMBearerAllowedAuth auth; gint encoded_auth = MM_BEARER_HUAWEI_AUTH_UNKNOWN; gchar *command; apn = mm_bearer_properties_get_apn (mm_base_bearer_peek_config (MM_BASE_BEARER (self))); user = mm_bearer_properties_get_user (mm_base_bearer_peek_config (MM_BASE_BEARER (self))); passwd = mm_bearer_properties_get_password (mm_base_bearer_peek_config (MM_BASE_BEARER (self))); auth = mm_bearer_properties_get_allowed_auth (mm_base_bearer_peek_config (MM_BASE_BEARER (self))); encoded_auth = huawei_parse_auth_type (auth); /* Default to no authentication if not specified */ if (encoded_auth == MM_BEARER_HUAWEI_AUTH_UNKNOWN) encoded_auth = MM_BEARER_HUAWEI_AUTH_NONE; if (!user && !passwd) command = g_strdup_printf ("AT^NDISDUP=1,1,\"%s\"", apn == NULL ? "" : apn); else { if (encoded_auth == MM_BEARER_HUAWEI_AUTH_NONE) { encoded_auth = MM_BEARER_HUAWEI_AUTH_CHAP; mm_obj_dbg (self, "using default (CHAP) authentication method"); } command = g_strdup_printf ("AT^NDISDUP=1,1,\"%s\",\"%s\",\"%s\",%d", apn == NULL ? "" : apn, user == NULL ? "" : user, passwd == NULL ? "" : passwd, encoded_auth); } mm_base_modem_at_command_full (ctx->modem, ctx->primary, command, 3, FALSE, FALSE, NULL, (GAsyncReadyCallback)connect_ndisdup_ready, g_object_ref (self)); g_free (command); return; } case CONNECT_3GPP_CONTEXT_STEP_NDISSTATQRY: /* Wait for dial up timeout, retries for 180 times * (1s between the retries, so it means 3 minutes). * If too many retries, failed */ if (ctx->check_count > MM_BASE_BEARER_DEFAULT_CONNECTION_TIMEOUT) { /* Clear context */ self->priv->connect_pending = NULL; g_task_return_new_error (task, MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_NETWORK_TIMEOUT, "Connection attempt timed out"); g_object_unref (task); return; } /* Give up if too many unexpected responses to NIDSSTATQRY are encountered. */ if (ctx->failed_ndisstatqry_count > 10) { /* Clear context */ self->priv->connect_pending = NULL; g_task_return_new_error (task, MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_NOT_SUPPORTED, "Connection attempt not supported."); g_object_unref (task); return; } /* Check if connected */ ctx->check_count++; mm_base_modem_at_command_full (ctx->modem, ctx->primary, "^NDISSTATQRY?", 3, FALSE, FALSE, NULL, (GAsyncReadyCallback)connect_ndisstatqry_check_ready, g_object_ref (self)); return; case CONNECT_3GPP_CONTEXT_STEP_IP_CONFIG: mm_base_modem_at_command_full (ctx->modem, ctx->primary, "^DHCP?", 3, FALSE, FALSE, NULL, (GAsyncReadyCallback)connect_dhcp_check_ready, g_object_ref (self)); return; case CONNECT_3GPP_CONTEXT_STEP_LAST: /* Clear context */ self->priv->connect_pending = NULL; /* Setup result */ g_task_return_pointer ( task, mm_bearer_connect_result_new (ctx->data, ctx->ipv4_config, NULL), (GDestroyNotify)mm_bearer_connect_result_unref); g_object_unref (task); return; default: g_assert_not_reached (); } } static void connect_3gpp (MMBroadbandBearer *_self, MMBroadbandModem *modem, MMPortSerialAt *primary, MMPortSerialAt *secondary, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { MMBroadbandBearerHuawei *self = MM_BROADBAND_BEARER_HUAWEI (_self); Connect3gppContext *ctx; GTask *task; MMPort *data; g_assert (primary != NULL); /* We need a net data port */ data = mm_base_modem_peek_best_data_port (MM_BASE_MODEM (modem), MM_PORT_TYPE_NET); if (!data) { g_task_report_new_error (self, callback, user_data, connect_3gpp, MM_CORE_ERROR, MM_CORE_ERROR_NOT_FOUND, "No valid data port found to launch connection"); return; } /* Setup connection context */ ctx = g_slice_new0 (Connect3gppContext); ctx->modem = MM_BASE_MODEM (g_object_ref (modem)); ctx->data = g_object_ref (data); ctx->step = CONNECT_3GPP_CONTEXT_STEP_FIRST; g_assert (self->priv->connect_pending == NULL); g_assert (self->priv->disconnect_pending == NULL); /* Get correct dial port to use */ ctx->primary = get_dial_port (MM_BROADBAND_MODEM_HUAWEI (ctx->modem), ctx->data, primary); /* Default to automatic/DHCP addressing */ ctx->ipv4_config = mm_bearer_ip_config_new (); mm_bearer_ip_config_set_method (ctx->ipv4_config, MM_BEARER_IP_METHOD_DHCP); task = g_task_new (self, NULL, callback, user_data); g_task_set_task_data (task, ctx, (GDestroyNotify)connect_3gpp_context_free); g_task_set_check_cancellable (task, FALSE); /* Run! */ connect_3gpp_context_step (task); } /*****************************************************************************/ /* Disconnect 3GPP */ typedef enum { DISCONNECT_3GPP_CONTEXT_STEP_FIRST = 0, DISCONNECT_3GPP_CONTEXT_STEP_NDISDUP, DISCONNECT_3GPP_CONTEXT_STEP_NDISSTATQRY, DISCONNECT_3GPP_CONTEXT_STEP_LAST } Disconnect3gppContextStep; typedef struct { MMBaseModem *modem; MMPortSerialAt *primary; Disconnect3gppContextStep step; guint check_count; guint failed_ndisstatqry_count; } Disconnect3gppContext; static void disconnect_3gpp_context_free (Disconnect3gppContext *ctx) { g_object_unref (ctx->primary); g_object_unref (ctx->modem); g_slice_free (Disconnect3gppContext, ctx); } static gboolean disconnect_3gpp_finish (MMBroadbandBearer *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void disconnect_3gpp_context_step (GTask *task); static gboolean disconnect_retry_ndisstatqry_check_cb (MMBroadbandBearerHuawei *self) { GTask *task; /* Recover context */ task = self->priv->disconnect_pending; g_assert (task != NULL); /* Balance refcount */ g_object_unref (self); /* Retry same step */ disconnect_3gpp_context_step (task); return G_SOURCE_REMOVE; } static void disconnect_ndisstatqry_check_ready (MMBaseModem *modem, GAsyncResult *res, MMBroadbandBearerHuawei *self) { GTask *task; Disconnect3gppContext *ctx; const gchar *response; GError *error = NULL; gboolean ipv4_available = FALSE; gboolean ipv4_connected = FALSE; gboolean ipv6_available = FALSE; gboolean ipv6_connected = FALSE; task = self->priv->disconnect_pending; g_assert (task != NULL); ctx = g_task_get_task_data (task); /* Balance refcount */ g_object_unref (self); response = mm_base_modem_at_command_full_finish (modem, res, &error); if (!response || !mm_huawei_parse_ndisstatqry_response (response, &ipv4_available, &ipv4_connected, &ipv6_available, &ipv6_connected, &error)) { ctx->failed_ndisstatqry_count++; mm_obj_dbg (self, "unexpected response to ^NDISSTATQRY command: %s (%u attempts so far)", error->message, ctx->failed_ndisstatqry_count); g_error_free (error); } /* Disconnected IPv4? */ if (ipv4_available && !ipv4_connected) { /* Success! */ ctx->step++; disconnect_3gpp_context_step (task); return; } /* Setup timeout to retry the same step */ g_timeout_add_seconds (1, (GSourceFunc)disconnect_retry_ndisstatqry_check_cb, g_object_ref (self)); } static void disconnect_ndisdup_ready (MMBaseModem *modem, GAsyncResult *res, MMBroadbandBearerHuawei *self) { GTask *task; Disconnect3gppContext *ctx; task = self->priv->disconnect_pending; g_assert (task != NULL); ctx = g_task_get_task_data (task); /* Balance refcount */ g_object_unref (self); /* Running NDISDUP=1,0 on an already disconnected bearer/context will * return ERROR! Ignore errors in the NDISDUP disconnection command, * because we're anyway going to check the bearer/context status * afterwards. */ mm_base_modem_at_command_full_finish (modem, res, NULL); /* Go to next step */ ctx->step++; disconnect_3gpp_context_step (task); } static void disconnect_3gpp_context_step (GTask *task) { MMBroadbandBearerHuawei *self; Disconnect3gppContext *ctx; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); switch (ctx->step) { case DISCONNECT_3GPP_CONTEXT_STEP_FIRST: /* Store the task */ self->priv->disconnect_pending = task; ctx->step++; /* fall through */ case DISCONNECT_3GPP_CONTEXT_STEP_NDISDUP: mm_base_modem_at_command_full (ctx->modem, ctx->primary, "^NDISDUP=1,0", 3, FALSE, FALSE, NULL, (GAsyncReadyCallback)disconnect_ndisdup_ready, g_object_ref (self)); return; case DISCONNECT_3GPP_CONTEXT_STEP_NDISSTATQRY: /* If too many retries (1s of wait between the retries), failed */ if (ctx->check_count > MM_BASE_BEARER_DEFAULT_DISCONNECTION_TIMEOUT) { /* Clear task */ self->priv->disconnect_pending = NULL; g_task_return_new_error (task, MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_NETWORK_TIMEOUT, "Disconnection attempt timed out"); g_object_unref (task); return; } /* Give up if too many unexpected responses to NIDSSTATQRY are encountered. */ if (ctx->failed_ndisstatqry_count > 10) { /* Clear task */ self->priv->disconnect_pending = NULL; g_task_return_new_error (task, MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_NOT_SUPPORTED, "Disconnection attempt not supported."); g_object_unref (task); return; } /* Check if disconnected */ ctx->check_count++; mm_base_modem_at_command_full (ctx->modem, ctx->primary, "^NDISSTATQRY?", 3, FALSE, FALSE, NULL, (GAsyncReadyCallback)disconnect_ndisstatqry_check_ready, g_object_ref (self)); return; case DISCONNECT_3GPP_CONTEXT_STEP_LAST: /* Clear task */ self->priv->disconnect_pending = NULL; /* Set data port as result */ g_task_return_boolean (task, TRUE); g_object_unref (task); return; default: g_assert_not_reached (); } } static void disconnect_3gpp (MMBroadbandBearer *_self, MMBroadbandModem *modem, MMPortSerialAt *primary, MMPortSerialAt *secondary, MMPort *data, guint cid, GAsyncReadyCallback callback, gpointer user_data) { MMBroadbandBearerHuawei *self = MM_BROADBAND_BEARER_HUAWEI (_self); Disconnect3gppContext *ctx; GTask *task; g_assert (primary != NULL); ctx = g_slice_new0 (Disconnect3gppContext); ctx->modem = MM_BASE_MODEM (g_object_ref (modem)); ctx->step = DISCONNECT_3GPP_CONTEXT_STEP_FIRST; g_assert (self->priv->connect_pending == NULL); g_assert (self->priv->disconnect_pending == NULL); /* Get correct dial port to use */ ctx->primary = get_dial_port (MM_BROADBAND_MODEM_HUAWEI (ctx->modem), data, primary); task = g_task_new (self, NULL, callback, user_data); g_task_set_task_data (task, ctx, (GDestroyNotify)disconnect_3gpp_context_free); /* Start! */ disconnect_3gpp_context_step (task); } /*****************************************************************************/ static void report_connection_status (MMBaseBearer *bearer, MMBearerConnectionStatus status, const GError *connection_error) { MMBroadbandBearerHuawei *self = MM_BROADBAND_BEARER_HUAWEI (bearer); g_assert (status == MM_BEARER_CONNECTION_STATUS_CONNECTED || status == MM_BEARER_CONNECTION_STATUS_DISCONNECTING || status == MM_BEARER_CONNECTION_STATUS_DISCONNECTED); /* When a pending connection / disconnection attempt is in progress, we use * ^NDISSTATQRY? to check the connection status and thus temporarily ignore * ^NDISSTAT unsolicited messages */ if (self->priv->connect_pending || self->priv->disconnect_pending) return; mm_obj_dbg (self, "received spontaneous ^NDISSTAT (%s)", mm_bearer_connection_status_get_string (status)); /* Ignore 'CONNECTED' */ if (status == MM_BEARER_CONNECTION_STATUS_CONNECTED) return; /* Report disconnected right away */ MM_BASE_BEARER_CLASS (mm_broadband_bearer_huawei_parent_class)->report_connection_status ( bearer, MM_BEARER_CONNECTION_STATUS_DISCONNECTED, NULL); } /*****************************************************************************/ MMBaseBearer * mm_broadband_bearer_huawei_new_finish (GAsyncResult *res, GError **error) { GObject *bearer; GObject *source; source = g_async_result_get_source_object (res); bearer = g_async_initable_new_finish (G_ASYNC_INITABLE (source), res, error); g_object_unref (source); if (!bearer) return NULL; /* Only export valid bearers */ mm_base_bearer_export (MM_BASE_BEARER (bearer)); return MM_BASE_BEARER (bearer); } void mm_broadband_bearer_huawei_new (MMBroadbandModemHuawei *modem, MMBearerProperties *config, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { g_async_initable_new_async ( MM_TYPE_BROADBAND_BEARER_HUAWEI, G_PRIORITY_DEFAULT, cancellable, callback, user_data, MM_BASE_BEARER_MODEM, modem, MM_BASE_BEARER_CONFIG, config, NULL); } static void mm_broadband_bearer_huawei_init (MMBroadbandBearerHuawei *self) { /* Initialize private data */ self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, MM_TYPE_BROADBAND_BEARER_HUAWEI, MMBroadbandBearerHuaweiPrivate); } static void mm_broadband_bearer_huawei_class_init (MMBroadbandBearerHuaweiClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); MMBaseBearerClass *base_bearer_class = MM_BASE_BEARER_CLASS (klass); MMBroadbandBearerClass *broadband_bearer_class = MM_BROADBAND_BEARER_CLASS (klass); g_type_class_add_private (object_class, sizeof (MMBroadbandBearerHuaweiPrivate)); base_bearer_class->report_connection_status = report_connection_status; base_bearer_class->load_connection_status = NULL; base_bearer_class->load_connection_status_finish = NULL; #if defined WITH_SUSPEND_RESUME base_bearer_class->reload_connection_status = NULL; base_bearer_class->reload_connection_status_finish = NULL; #endif broadband_bearer_class->connect_3gpp = connect_3gpp; broadband_bearer_class->connect_3gpp_finish = connect_3gpp_finish; broadband_bearer_class->disconnect_3gpp = disconnect_3gpp; broadband_bearer_class->disconnect_3gpp_finish = disconnect_3gpp_finish; } ModemManager-1.23.4-dev/src/plugins/huawei/mm-broadband-bearer-huawei.h000066400000000000000000000053731456466623000257010ustar00rootroot00000000000000/* -*- 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 - 2012 Red Hat, Inc. * Copyright (C) 2012 Lanedo GmbH * Copyright (C) 2012 Huawei Technologies Co., Ltd * * Author: Franko Fang */ #ifndef MM_BROADBAND_BEARER_HUAWEI_H #define MM_BROADBAND_BEARER_HUAWEI_H #include #include #include "mm-broadband-bearer.h" #include "mm-broadband-modem-huawei.h" #define MM_TYPE_BROADBAND_BEARER_HUAWEI (mm_broadband_bearer_huawei_get_type ()) #define MM_BROADBAND_BEARER_HUAWEI(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_BROADBAND_BEARER_HUAWEI, MMBroadbandBearerHuawei)) #define MM_BROADBAND_BEARER_HUAWEI_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_BROADBAND_BEARER_HUAWEI, MMBroadbandBearerHuaweiClass)) #define MM_IS_BROADBAND_BEARER_HUAWEI(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_BROADBAND_BEARER_HUAWEI)) #define MM_IS_BROADBAND_BEARER_HUAWEI_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_BROADBAND_BEARER_HUAWEI)) #define MM_BROADBAND_BEARER_HUAWEI_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_BROADBAND_BEARER_HUAWEI, MMBroadbandBearerHuaweiClass)) typedef struct _MMBroadbandBearerHuawei MMBroadbandBearerHuawei; typedef struct _MMBroadbandBearerHuaweiClass MMBroadbandBearerHuaweiClass; typedef struct _MMBroadbandBearerHuaweiPrivate MMBroadbandBearerHuaweiPrivate; struct _MMBroadbandBearerHuawei { MMBroadbandBearer parent; MMBroadbandBearerHuaweiPrivate *priv; }; struct _MMBroadbandBearerHuaweiClass { MMBroadbandBearerClass parent; }; GType mm_broadband_bearer_huawei_get_type (void); void mm_broadband_bearer_huawei_new (MMBroadbandModemHuawei *modem, MMBearerProperties *config, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); MMBaseBearer *mm_broadband_bearer_huawei_new_finish (GAsyncResult *res, GError **error); #endif /* MM_BROADBAND_BEARER_HUAWEI_H */ ModemManager-1.23.4-dev/src/plugins/huawei/mm-broadband-modem-huawei.c000066400000000000000000005041731456466623000255370ustar00rootroot00000000000000/* -*- 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 - 2012 Red Hat, Inc. * Copyright (C) 2011 - 2012 Google Inc. * Copyright (C) 2012 Huawei Technologies Co., Ltd * Copyright (C) 2015 Marco Bascetta * Copyright (C) 2012 - 2019 Aleksander Morgado */ #include #include #include #include #include #include #include #include #define _LIBMM_INSIDE_MM #include #include "mm-log-object.h" #include "mm-errors-types.h" #include "mm-modem-helpers.h" #include "mm-modem-helpers-huawei.h" #include "mm-base-modem-at.h" #include "mm-iface-modem.h" #include "mm-iface-modem-3gpp.h" #include "mm-iface-modem-3gpp-ussd.h" #include "mm-iface-modem-location.h" #include "mm-iface-modem-time.h" #include "mm-iface-modem-cdma.h" #include "mm-iface-modem-signal.h" #include "mm-iface-modem-voice.h" #include "mm-broadband-modem-huawei.h" #include "mm-broadband-bearer-huawei.h" #include "mm-broadband-bearer.h" #include "mm-bearer-list.h" #include "mm-sim-huawei.h" static void iface_modem_init (MMIfaceModem *iface); static void iface_modem_3gpp_init (MMIfaceModem3gpp *iface); static void iface_modem_3gpp_ussd_init (MMIfaceModem3gppUssd *iface); static void iface_modem_location_init (MMIfaceModemLocation *iface); static void iface_modem_cdma_init (MMIfaceModemCdma *iface); static void iface_modem_time_init (MMIfaceModemTime *iface); static void iface_modem_voice_init (MMIfaceModemVoice *iface); static void iface_modem_signal_init (MMIfaceModemSignal *iface); static MMIfaceModem *iface_modem_parent; static MMIfaceModem3gpp *iface_modem_3gpp_parent; static MMIfaceModemLocation *iface_modem_location_parent; static MMIfaceModemCdma *iface_modem_cdma_parent; static MMIfaceModemVoice *iface_modem_voice_parent; G_DEFINE_TYPE_EXTENDED (MMBroadbandModemHuawei, mm_broadband_modem_huawei, MM_TYPE_BROADBAND_MODEM, 0, G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM, iface_modem_init) G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_3GPP, iface_modem_3gpp_init) G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_3GPP_USSD, iface_modem_3gpp_ussd_init) G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_CDMA, iface_modem_cdma_init) G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_LOCATION, iface_modem_location_init) G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_TIME, iface_modem_time_init) G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_VOICE, iface_modem_voice_init) G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_SIGNAL, iface_modem_signal_init)) typedef enum { FEATURE_SUPPORT_UNKNOWN, FEATURE_NOT_SUPPORTED, FEATURE_SUPPORTED } FeatureSupport; typedef struct { MMSignal *cdma; MMSignal *evdo; MMSignal *gsm; MMSignal *umts; MMSignal *lte; MMSignal *nr5g; } DetailedSignal; struct _MMBroadbandModemHuaweiPrivate { /* Regex for signal quality related notifications */ GRegex *rssi_regex; GRegex *rssilvl_regex; GRegex *hrssilvl_regex; /* Regex for access-technology related notifications */ GRegex *mode_regex; /* Regex for connection status related notifications */ GRegex *dsflowrpt_regex; GRegex *ndisstat_regex; /* Regex for voice management notifications */ GRegex *orig_regex; GRegex *conf_regex; GRegex *conn_regex; GRegex *cend_regex; GRegex *ddtmf_regex; /* Regex to ignore */ GRegex *boot_regex; GRegex *connect_regex; GRegex *csnr_regex; GRegex *cusatp_regex; GRegex *cusatend_regex; GRegex *dsdormant_regex; GRegex *simst_regex; GRegex *srvst_regex; GRegex *stin_regex; GRegex *hcsq_regex; GRegex *pdpdeact_regex; GRegex *ndisend_regex; GRegex *rfswitch_regex; GRegex *position_regex; GRegex *posend_regex; GRegex *ecclist_regex; GRegex *ltersrp_regex; GRegex *cschannelinfo_regex; GRegex *ccallstate_regex; GRegex *eons_regex; GRegex *lwurc_regex; FeatureSupport ndisdup_support; FeatureSupport rfswitch_support; FeatureSupport sysinfoex_support; FeatureSupport syscfg_support; FeatureSupport syscfgex_support; FeatureSupport prefmode_support; FeatureSupport time_support; FeatureSupport nwtime_support; FeatureSupport cvoice_support; MMModemLocationSource enabled_sources; GArray *syscfg_supported_modes; GArray *syscfgex_supported_modes; GArray *prefmode_supported_modes; DetailedSignal detailed_signal; /* Voice call audio related properties */ guint audio_hz; guint audio_bits; }; /*****************************************************************************/ GList * mm_broadband_modem_huawei_get_at_port_list (MMBroadbandModemHuawei *self) { GList *out = NULL; MMPortSerialAt *port; GList *cdc_wdm_at_ports; /* Primary */ port = mm_base_modem_get_port_primary (MM_BASE_MODEM (self)); if (port) out = g_list_append (out, port); /* Secondary */ port = mm_base_modem_get_port_secondary (MM_BASE_MODEM (self)); if (port) out = g_list_append (out, port); /* Additional cdc-wdm ports used for dialing */ cdc_wdm_at_ports = mm_base_modem_find_ports (MM_BASE_MODEM (self), MM_PORT_SUBSYS_USBMISC, MM_PORT_TYPE_AT); return g_list_concat (out, cdc_wdm_at_ports); } /*****************************************************************************/ typedef struct { gboolean extended; guint srv_status; guint srv_domain; guint roam_status; guint sim_state; guint sys_mode; gboolean sys_submode_valid; guint sys_submode; } SysinfoResult; static gboolean sysinfo_finish (MMBroadbandModemHuawei *self, GAsyncResult *res, gboolean *extended, guint *srv_status, guint *srv_domain, guint *roam_status, guint *sim_state, guint *sys_mode, gboolean *sys_submode_valid, guint *sys_submode, GError **error) { SysinfoResult *result; result = g_task_propagate_pointer (G_TASK (res), error); if (!result) return FALSE; if (extended) *extended = result->extended; if (srv_status) *srv_status = result->srv_status; if (srv_domain) *srv_domain = result->srv_domain; if (roam_status) *roam_status = result->roam_status; if (sim_state) *sim_state = result->sim_state; if (sys_mode) *sys_mode = result->sys_mode; if (sys_submode_valid) *sys_submode_valid = result->sys_submode_valid; if (sys_submode) *sys_submode = result->sys_submode; g_free (result); return TRUE; } static void run_sysinfo_ready (MMBaseModem *self, GAsyncResult *res, GTask *task) { GError *error = NULL; const gchar *response; SysinfoResult *result; response = mm_base_modem_at_command_finish (self, res, &error); if (!response) { mm_obj_dbg (self, "^SYSINFO failed: %s", error->message); g_task_return_error (task, error); g_object_unref (task); return; } result = g_new0 (SysinfoResult, 1); result->extended = FALSE; if (!mm_huawei_parse_sysinfo_response (response, &result->srv_status, &result->srv_domain, &result->roam_status, &result->sys_mode, &result->sim_state, &result->sys_submode_valid, &result->sys_submode, &error)) { mm_obj_dbg (self, "^SYSINFO parsing failed: %s", error->message); g_task_return_error (task, error); g_object_unref (task); g_free (result); return; } g_task_return_pointer (task, result, g_free); g_object_unref (task); } static void run_sysinfo (MMBroadbandModemHuawei *self, GTask *task) { mm_base_modem_at_command (MM_BASE_MODEM (self), "^SYSINFO", 3, FALSE, (GAsyncReadyCallback)run_sysinfo_ready, task); } static void run_sysinfoex_ready (MMBaseModem *_self, GAsyncResult *res, GTask *task) { MMBroadbandModemHuawei *self = MM_BROADBAND_MODEM_HUAWEI (_self); GError *error = NULL; const gchar *response; SysinfoResult *result; response = mm_base_modem_at_command_finish (_self, res, &error); if (!response) { /* First time we try, we fallback to ^SYSINFO */ if (self->priv->sysinfoex_support == FEATURE_SUPPORT_UNKNOWN) { self->priv->sysinfoex_support = FEATURE_NOT_SUPPORTED; mm_obj_dbg (self, "^SYSINFOEX failed: %s, assuming unsupported", error->message); g_error_free (error); run_sysinfo (self, task); return; } /* Otherwise, propagate error */ mm_obj_dbg (self, "^SYSINFOEX failed: %s", error->message); g_task_return_error (task, error); g_object_unref (task); return; } if (self->priv->sysinfoex_support == FEATURE_SUPPORT_UNKNOWN) self->priv->sysinfoex_support = FEATURE_SUPPORTED; result = g_new0 (SysinfoResult, 1); result->extended = TRUE; if (!mm_huawei_parse_sysinfoex_response (response, &result->srv_status, &result->srv_domain, &result->roam_status, &result->sim_state, &result->sys_mode, &result->sys_submode, &error)) { mm_obj_dbg (self, "^SYSINFOEX parsing failed: %s", error->message); g_task_return_error (task, error); g_object_unref (task); g_free (result); return; } /* Submode from SYSINFOEX always valid */ result->sys_submode_valid = TRUE; g_task_return_pointer (task, result, g_free); g_object_unref (task); } static void run_sysinfoex (MMBroadbandModemHuawei *self, GTask *task) { mm_base_modem_at_command (MM_BASE_MODEM (self), "^SYSINFOEX", 3, FALSE, (GAsyncReadyCallback)run_sysinfoex_ready, task); } static void sysinfo (MMBroadbandModemHuawei *self, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; task = g_task_new (self, NULL, callback, user_data); if (self->priv->sysinfoex_support == FEATURE_SUPPORT_UNKNOWN || self->priv->sysinfoex_support == FEATURE_SUPPORTED) run_sysinfoex (self, task); else run_sysinfo (self, task); } /*****************************************************************************/ /* Reset (Modem interface) */ static gboolean reset_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { return !!mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, error); } static void reset (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data) { const gchar *command; /* Unlike other Huawei modems that support AT^RESET for resetting the modem, * Huawei MU736 supports AT^RESET but does not reset the modem upon receiving * AT^RESET. It does, however, support resetting itself via AT+CFUN=16. */ if (g_strcmp0 (mm_iface_modem_get_model (self), "MU736") == 0) command = "+CFUN=16"; else command = "^RESET"; mm_base_modem_at_command (MM_BASE_MODEM (self), command, 3, FALSE, callback, user_data); } /*****************************************************************************/ /* Load access technologies (Modem interface) */ static MMModemAccessTechnology huawei_sysinfo_submode_to_act (guint submode) { /* new more detailed system mode/access technology */ switch (submode) { case 1: return MM_MODEM_ACCESS_TECHNOLOGY_GSM; case 2: return MM_MODEM_ACCESS_TECHNOLOGY_GPRS; case 3: return MM_MODEM_ACCESS_TECHNOLOGY_EDGE; case 4: return MM_MODEM_ACCESS_TECHNOLOGY_UMTS; case 5: return MM_MODEM_ACCESS_TECHNOLOGY_HSDPA; case 6: return MM_MODEM_ACCESS_TECHNOLOGY_HSUPA; case 7: return MM_MODEM_ACCESS_TECHNOLOGY_HSPA; case 8: /* TD-SCDMA */ break; case 9: /* HSPA+ */ return MM_MODEM_ACCESS_TECHNOLOGY_HSPA_PLUS; case 10: return MM_MODEM_ACCESS_TECHNOLOGY_EVDO0; case 11: return MM_MODEM_ACCESS_TECHNOLOGY_EVDOA; case 12: return MM_MODEM_ACCESS_TECHNOLOGY_EVDOB; case 13: /* 1xRTT */ return MM_MODEM_ACCESS_TECHNOLOGY_1XRTT; case 16: /* 3xRTT */ return MM_MODEM_ACCESS_TECHNOLOGY_1XRTT; case 17: /* HSPA+ (64QAM) */ return MM_MODEM_ACCESS_TECHNOLOGY_HSPA_PLUS; case 18: /* HSPA+ (MIMO) */ return MM_MODEM_ACCESS_TECHNOLOGY_HSPA_PLUS; default: break; } return MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN; } static MMModemAccessTechnology huawei_sysinfo_mode_to_act (guint mode) { /* Older, less detailed system mode/access technology */ switch (mode) { case 1: /* AMPS */ break; case 2: /* CDMA */ return MM_MODEM_ACCESS_TECHNOLOGY_1XRTT; case 3: /* GSM/GPRS */ return MM_MODEM_ACCESS_TECHNOLOGY_GPRS; case 4: /* HDR */ return MM_MODEM_ACCESS_TECHNOLOGY_EVDO0; case 5: /* WCDMA */ return MM_MODEM_ACCESS_TECHNOLOGY_UMTS; case 6: /* GPS */ break; case 7: /* GSM/WCDMA */ return MM_MODEM_ACCESS_TECHNOLOGY_UMTS; case 8: /* CDMA/HDR hybrid */ return (MM_MODEM_ACCESS_TECHNOLOGY_EVDO0 | MM_MODEM_ACCESS_TECHNOLOGY_1XRTT); case 15: /* TD-SCDMA */ break; default: break; } return MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN; } static MMModemAccessTechnology huawei_sysinfoex_submode_to_act (guint submode) { switch (submode) { case 1: /* GSM */ return MM_MODEM_ACCESS_TECHNOLOGY_GSM; case 2: /* GPRS */ return MM_MODEM_ACCESS_TECHNOLOGY_GPRS; case 3: /* EDGE */ return MM_MODEM_ACCESS_TECHNOLOGY_EDGE; case 21: /* IS95A */ return MM_MODEM_ACCESS_TECHNOLOGY_1XRTT; case 22: /* IS95B */ return MM_MODEM_ACCESS_TECHNOLOGY_1XRTT; case 23: /* CDMA2000 1x */ return MM_MODEM_ACCESS_TECHNOLOGY_1XRTT; case 24: /* EVDO rel0 */ return MM_MODEM_ACCESS_TECHNOLOGY_EVDO0; case 25: /* EVDO relA */ return MM_MODEM_ACCESS_TECHNOLOGY_EVDOA; case 26: /* EVDO relB */ return MM_MODEM_ACCESS_TECHNOLOGY_EVDOB; case 27: /* Hybrid CDMA2000 1x */ return MM_MODEM_ACCESS_TECHNOLOGY_1XRTT; case 28: /* Hybrid EVDO rel0 */ return MM_MODEM_ACCESS_TECHNOLOGY_EVDO0; case 29: /* Hybrid EVDO relA */ return MM_MODEM_ACCESS_TECHNOLOGY_EVDOA; case 30: /* Hybrid EVDO relB */ return MM_MODEM_ACCESS_TECHNOLOGY_EVDOB; case 41: /* WCDMA */ return MM_MODEM_ACCESS_TECHNOLOGY_UMTS; case 42: /* HSDPA */ return MM_MODEM_ACCESS_TECHNOLOGY_HSDPA; case 43: /* HSUPA */ return MM_MODEM_ACCESS_TECHNOLOGY_HSUPA; case 44: /* HSPA */ return MM_MODEM_ACCESS_TECHNOLOGY_HSPA; case 45: /* HSPA+ */ return MM_MODEM_ACCESS_TECHNOLOGY_HSPA_PLUS; case 46: /* DC-HSPA+ */ return MM_MODEM_ACCESS_TECHNOLOGY_HSPA_PLUS; case 61: /* TD-SCDMA */ break; case 81: /* 802.16e (WiMAX) */ break; case 101: /* LTE */ return MM_MODEM_ACCESS_TECHNOLOGY_LTE; default: break; } return MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN; } static MMModemAccessTechnology huawei_sysinfoex_mode_to_act (guint mode) { /* Older, less detailed system mode/access technology */ switch (mode) { case 1: /* GSM */ return MM_MODEM_ACCESS_TECHNOLOGY_GSM; case 2: /* CDMA */ return MM_MODEM_ACCESS_TECHNOLOGY_1XRTT; case 3: /* WCDMA */ return MM_MODEM_ACCESS_TECHNOLOGY_UMTS; case 4: /* TD-SCDMA */ break; case 5: /* WIMAX */ break; case 6: /* LTE */ return MM_MODEM_ACCESS_TECHNOLOGY_LTE; default: break; } return MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN; } static gboolean load_access_technologies_finish (MMIfaceModem *self, GAsyncResult *res, MMModemAccessTechnology *access_technologies, guint *mask, GError **error) { MMModemAccessTechnology act = MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN; gboolean extended = FALSE; guint srv_status = 0; gboolean sys_submode_valid = FALSE; guint sys_submode = 0; guint sys_mode = 0; if (!sysinfo_finish (MM_BROADBAND_MODEM_HUAWEI (self), res, &extended, &srv_status, NULL, /* srv_domain */ NULL, /* roam_status */ NULL, /* sim_state */ &sys_mode, &sys_submode_valid, &sys_submode, error)) return FALSE; if (srv_status != 0) { /* Valid service */ if (sys_submode_valid) act = (extended ? huawei_sysinfoex_submode_to_act (sys_submode) : huawei_sysinfo_submode_to_act (sys_submode)); if (act == MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN) act = (extended ? huawei_sysinfoex_mode_to_act (sys_mode) : huawei_sysinfo_mode_to_act (sys_mode)); } *access_technologies = act; *mask = MM_MODEM_ACCESS_TECHNOLOGY_ANY; return TRUE; } static void load_access_technologies (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data) { sysinfo (MM_BROADBAND_MODEM_HUAWEI (self), callback, user_data); } /*****************************************************************************/ /* Load unlock retries (Modem interface) */ static MMUnlockRetries * load_unlock_retries_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { g_autoptr(GRegex) r = NULL; g_autoptr(GMatchInfo) match_info = NULL; MMUnlockRetries *unlock_retries; const gchar *result; GError *match_error = NULL; guint i; MMModemLock locks[4] = { MM_MODEM_LOCK_SIM_PUK, MM_MODEM_LOCK_SIM_PIN, MM_MODEM_LOCK_SIM_PUK2, MM_MODEM_LOCK_SIM_PIN2 }; result = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, error); if (!result) return NULL; r = g_regex_new ("\\^CPIN:\\s*([^,]+),[^,]*,(\\d+),(\\d+),(\\d+),(\\d+)", G_REGEX_UNGREEDY, 0, NULL); g_assert (r != NULL); if (!g_regex_match_full (r, result, strlen (result), 0, 0, &match_info, &match_error)) { if (match_error) g_propagate_error (error, match_error); else g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Could not parse ^CPIN results: Response didn't match (%s)", result); return NULL; } unlock_retries = mm_unlock_retries_new (); for (i = 0; i <= 3; i++) { guint num; if (!mm_get_uint_from_match_info (match_info, i + 2, &num) || num > 10) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Could not parse ^CPIN results: " "Missing or invalid match info for lock '%s'", mm_modem_lock_get_string (locks[i])); g_object_unref (unlock_retries); unlock_retries = NULL; break; } mm_unlock_retries_set (unlock_retries, locks[i], num); } return unlock_retries; } static void load_unlock_retries (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data) { mm_base_modem_at_command (MM_BASE_MODEM (self), "^CPIN?", 3, FALSE, callback, user_data); } /*****************************************************************************/ /* After SIM unlock (Modem interface) */ static gboolean modem_after_sim_unlock_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static gboolean after_sim_unlock_wait_cb (GTask *task) { g_task_return_boolean (task, TRUE); g_object_unref (task); return G_SOURCE_REMOVE; } static void modem_after_sim_unlock (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; task = g_task_new (self, NULL, callback, user_data); /* A 3-second wait is necessary for SIM to become ready, or the firmware may * fail miserably and reboot itself */ g_timeout_add_seconds (3, (GSourceFunc)after_sim_unlock_wait_cb, task); } /*****************************************************************************/ /* Common band/mode handling code */ typedef struct { MMModemBand mm; guint32 huawei; } BandTable; static BandTable bands[] = { /* Sort 3G first since it's preferred */ { MM_MODEM_BAND_UTRAN_1, 0x00400000 }, { MM_MODEM_BAND_UTRAN_2, 0x00800000 }, { MM_MODEM_BAND_UTRAN_5, 0x04000000 }, { MM_MODEM_BAND_UTRAN_8, 0x00020000 }, /* 2G second */ { MM_MODEM_BAND_G850, 0x00080000 }, { MM_MODEM_BAND_DCS, 0x00000080 }, { MM_MODEM_BAND_EGSM, 0x00000100 }, { MM_MODEM_BAND_PCS, 0x00200000 } }; static gboolean bands_array_to_huawei (GArray *bands_array, guint32 *out_huawei) { guint i; /* Treat ANY as a special case: All huawei flags enabled */ if (bands_array->len == 1 && g_array_index (bands_array, MMModemBand, 0) == MM_MODEM_BAND_ANY) { *out_huawei = 0x3FFFFFFF; return TRUE; } *out_huawei = 0; for (i = 0; i < bands_array->len; i++) { guint j; for (j = 0; j < G_N_ELEMENTS (bands); j++) { if (g_array_index (bands_array, MMModemBand, i) == bands[j].mm) *out_huawei |= bands[j].huawei; } } return (*out_huawei > 0 ? TRUE : FALSE); } static gboolean huawei_to_bands_array (guint32 huawei, GArray **bands_array, GError **error) { guint i; *bands_array = NULL; for (i = 0; i < G_N_ELEMENTS (bands); i++) { if (huawei & bands[i].huawei) { if (G_UNLIKELY (!*bands_array)) *bands_array = g_array_new (FALSE, FALSE, sizeof (MMModemBand)); g_array_append_val (*bands_array, bands[i].mm); } } if (!*bands_array) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't build bands array from '%u'", huawei); return FALSE; } return TRUE; } static gboolean parse_syscfg (const gchar *response, GArray **bands_array, GError **error) { gint mode; gint acquisition_order; guint32 band; gint roaming; gint srv_domain; if (!response || strncmp (response, "^SYSCFG:", 8) != 0 || !sscanf (response + 8, "%d,%d,%x,%d,%d", &mode, &acquisition_order, &band, &roaming, &srv_domain)) { /* Dump error to upper layer */ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Unexpected SYSCFG response: '%s'", response); return FALSE; } /* Band */ if (bands_array && !huawei_to_bands_array (band, bands_array, error)) return FALSE; return TRUE; } /*****************************************************************************/ /* Load current bands (Modem interface) */ static GArray * load_current_bands_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { const gchar *response; GArray *bands_array = NULL; response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, error); if (!response) return NULL; if (!parse_syscfg (response, &bands_array, error)) return NULL; return bands_array; } static void load_current_bands (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data) { mm_base_modem_at_command (MM_BASE_MODEM (self), "^SYSCFG?", 3, FALSE, callback, user_data); } /*****************************************************************************/ /* Set current bands (Modem interface) */ static gboolean set_current_bands_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void syscfg_set_ready (MMBaseModem *self, GAsyncResult *res, GTask *task) { GError *error = NULL; if (!mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error)) /* Let the error be critical */ g_task_return_error (task, error); else g_task_return_boolean (task, TRUE); g_object_unref (task); } static void set_current_bands (MMIfaceModem *self, GArray *bands_array, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; gchar *cmd; guint32 huawei_band = 0x3FFFFFFF; gchar *bands_string; task = g_task_new (self, NULL, callback, user_data); bands_string = mm_common_build_bands_string ((MMModemBand *)(gpointer)bands_array->data, bands_array->len); if (!bands_array_to_huawei (bands_array, &huawei_band)) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Invalid bands requested: '%s'", bands_string); g_object_unref (task); g_free (bands_string); return; } cmd = g_strdup_printf ("AT^SYSCFG=16,3,%X,2,4", huawei_band); mm_base_modem_at_command (MM_BASE_MODEM (self), cmd, 3, FALSE, (GAsyncReadyCallback)syscfg_set_ready, task); g_free (cmd); g_free (bands_string); } /*****************************************************************************/ /* Load supported modes (Modem interface) */ static GArray * load_supported_modes_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { return g_task_propagate_pointer (G_TASK (res), error); } static void syscfg_test_ready (MMBroadbandModemHuawei *self, GAsyncResult *res, GTask *task) { const gchar *response; GError *error = NULL; response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error); if (response) { /* There are 2G+3G Huawei modems out there which support mode switching with * AT^SYSCFG, but fail to provide a valid response for AT^SYSCFG=? (they just * return an empty string). So handle that case by providing a default response * string to get parsed. Ugly, ugly, blame Huawei. */ if (response[0]) self->priv->syscfg_supported_modes = mm_huawei_parse_syscfg_test (response, self, &error); else { self->priv->syscfg_supported_modes = mm_huawei_parse_syscfg_test (MM_HUAWEI_DEFAULT_SYSCFG_FMT, self, NULL); g_assert (self->priv->syscfg_supported_modes != NULL); } } if (self->priv->syscfg_supported_modes) { MMModemModeCombination mode; guint i; GArray *combinations; /* Build list of combinations */ combinations = g_array_sized_new (FALSE, FALSE, sizeof (MMModemModeCombination), self->priv->syscfg_supported_modes->len); for (i = 0; i < self->priv->syscfg_supported_modes->len; i++) { MMHuaweiSyscfgCombination *huawei_mode; huawei_mode = &g_array_index (self->priv->syscfg_supported_modes, MMHuaweiSyscfgCombination, i); mode.allowed = huawei_mode->allowed; mode.preferred = huawei_mode->preferred; g_array_append_val (combinations, mode); } self->priv->syscfg_support = FEATURE_SUPPORTED; g_task_return_pointer (task, combinations, (GDestroyNotify)g_array_unref); } else { mm_obj_dbg (self, "error while checking ^SYSCFG format: %s", error->message); /* If SIM-PIN error, don't mark as feature unsupported; we'll retry later */ if (!g_error_matches (error, MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_SIM_PIN)) self->priv->syscfg_support = FEATURE_NOT_SUPPORTED; g_task_return_error (task, error); } g_object_unref (task); } static void syscfgex_test_ready (MMBroadbandModemHuawei *self, GAsyncResult *res, GTask *task) { const gchar *response; GError *error = NULL; response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error); if (response) self->priv->syscfgex_supported_modes = mm_huawei_parse_syscfgex_test (response, &error); if (self->priv->syscfgex_supported_modes) { MMModemModeCombination mode; guint i; GArray *combinations; /* Build list of combinations */ combinations = g_array_sized_new (FALSE, FALSE, sizeof (MMModemModeCombination), self->priv->syscfgex_supported_modes->len); for (i = 0; i < self->priv->syscfgex_supported_modes->len; i++) { MMHuaweiSyscfgexCombination *huawei_mode; huawei_mode = &g_array_index (self->priv->syscfgex_supported_modes, MMHuaweiSyscfgexCombination, i); mode.allowed = huawei_mode->allowed; mode.preferred = huawei_mode->preferred; g_array_append_val (combinations, mode); } self->priv->syscfgex_support = FEATURE_SUPPORTED; g_task_return_pointer (task, combinations, (GDestroyNotify)g_array_unref); g_object_unref (task); return; } /* If SIM-PIN error, don't mark as feature unsupported; we'll retry later */ if (error) { mm_obj_dbg (self, "error while checking ^SYSCFGEX format: %s", error->message); if (g_error_matches (error, MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_SIM_PIN)) { g_task_return_error (task, error); g_object_unref (task); return; } g_error_free (error); } self->priv->syscfgex_support = FEATURE_NOT_SUPPORTED; /* Try with SYSCFG */ mm_base_modem_at_command (MM_BASE_MODEM (self), "^SYSCFG=?", 3, TRUE, (GAsyncReadyCallback)syscfg_test_ready, task); } static void prefmode_test_ready (MMBroadbandModemHuawei *self, GAsyncResult *res, GTask *task) { const gchar *response; GError *error = NULL; response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error); if (response) self->priv->prefmode_supported_modes = mm_huawei_parse_prefmode_test (response, self, &error); if (self->priv->prefmode_supported_modes) { MMModemModeCombination mode; guint i; GArray *combinations; /* Build list of combinations */ combinations = g_array_sized_new (FALSE, FALSE, sizeof (MMModemModeCombination), self->priv->prefmode_supported_modes->len); for (i = 0; i < self->priv->prefmode_supported_modes->len; i++) { MMHuaweiPrefmodeCombination *huawei_mode; huawei_mode = &g_array_index (self->priv->prefmode_supported_modes, MMHuaweiPrefmodeCombination, i); mode.allowed = huawei_mode->allowed; mode.preferred = huawei_mode->preferred; g_array_append_val (combinations, mode); } self->priv->prefmode_support = FEATURE_SUPPORTED; g_task_return_pointer (task, combinations, (GDestroyNotify)g_array_unref); } else { mm_obj_dbg (self, "error while checking ^PREFMODE format: %s", error->message); /* If SIM-PIN error, don't mark as feature unsupported; we'll retry later */ if (!g_error_matches (error, MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_SIM_PIN)) self->priv->prefmode_support = FEATURE_NOT_SUPPORTED; g_task_return_error (task, error); } g_object_unref (task); } static void load_supported_modes (MMIfaceModem *_self, GAsyncReadyCallback callback, gpointer user_data) { MMBroadbandModemHuawei *self = MM_BROADBAND_MODEM_HUAWEI (_self); GTask *task; task = g_task_new (self, NULL, callback, user_data); if (mm_iface_modem_is_cdma_only (_self)) { /* ^PREFMODE only in CDMA-only modems */ self->priv->syscfg_support = FEATURE_NOT_SUPPORTED; self->priv->syscfgex_support = FEATURE_NOT_SUPPORTED; mm_base_modem_at_command (MM_BASE_MODEM (self), "^PREFMODE=?", 3, TRUE, (GAsyncReadyCallback)prefmode_test_ready, task); return; } /* Check SYSCFGEX */ self->priv->prefmode_support = FEATURE_NOT_SUPPORTED; mm_base_modem_at_command (MM_BASE_MODEM (self), "^SYSCFGEX=?", 3, TRUE, (GAsyncReadyCallback)syscfgex_test_ready, task); } /*****************************************************************************/ /* Load initial allowed/preferred modes (Modem interface) */ static gboolean load_current_modes_finish (MMIfaceModem *self, GAsyncResult *res, MMModemMode *allowed, MMModemMode *preferred, GError **error) { MMModemModeCombination *out; out = g_task_propagate_pointer (G_TASK (res), error); if (!out) return FALSE; *allowed = out->allowed; *preferred = out->preferred; g_free (out); return TRUE; } static void prefmode_load_current_modes_ready (MMBroadbandModemHuawei *self, GAsyncResult *res, GTask *task) { const gchar *response; GError *error = NULL; const MMHuaweiPrefmodeCombination *current = NULL; response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error); if (response) current = mm_huawei_parse_prefmode_response (response, self->priv->prefmode_supported_modes, &error); if (error) g_task_return_error (task, error); else { MMModemModeCombination *out; out = g_new (MMModemModeCombination, 1); out->allowed = current->allowed; out->preferred = current->preferred; g_task_return_pointer (task, out, g_free); } g_object_unref (task); } static void syscfg_load_current_modes_ready (MMBroadbandModemHuawei *self, GAsyncResult *res, GTask *task) { const gchar *response; GError *error = NULL; const MMHuaweiSyscfgCombination *current = NULL; response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error); if (response) current = mm_huawei_parse_syscfg_response (response, self->priv->syscfg_supported_modes, &error); if (error) g_task_return_error (task, error); else { MMModemModeCombination *out; out = g_new (MMModemModeCombination, 1); out->allowed = current->allowed; out->preferred = current->preferred; g_task_return_pointer (task, out, g_free); } g_object_unref (task); } static void syscfgex_load_current_modes_ready (MMBroadbandModemHuawei *self, GAsyncResult *res, GTask *task) { const gchar *response; GError *error = NULL; const MMHuaweiSyscfgexCombination *current = NULL; response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error); if (response) current = mm_huawei_parse_syscfgex_response (response, self->priv->syscfgex_supported_modes, &error); if (error) g_task_return_error (task, error); else { MMModemModeCombination *out; out = g_new (MMModemModeCombination, 1); out->allowed = current->allowed; out->preferred = current->preferred; g_task_return_pointer (task, out, g_free); } g_object_unref (task); } static void load_current_modes (MMIfaceModem *_self, GAsyncReadyCallback callback, gpointer user_data) { MMBroadbandModemHuawei *self = MM_BROADBAND_MODEM_HUAWEI (_self); GTask *task; task = g_task_new (self, NULL, callback, user_data); if (self->priv->syscfgex_support == FEATURE_SUPPORTED) { g_assert (self->priv->syscfgex_supported_modes != NULL); mm_base_modem_at_command ( MM_BASE_MODEM (self), "^SYSCFGEX?", 3, FALSE, (GAsyncReadyCallback)syscfgex_load_current_modes_ready, task); return; } if (self->priv->syscfg_support == FEATURE_SUPPORTED) { g_assert (self->priv->syscfg_supported_modes != NULL); mm_base_modem_at_command ( MM_BASE_MODEM (self), "^SYSCFG?", 3, FALSE, (GAsyncReadyCallback)syscfg_load_current_modes_ready, task); return; } if (self->priv->prefmode_support == FEATURE_SUPPORTED) { g_assert (self->priv->prefmode_supported_modes != NULL); mm_base_modem_at_command ( MM_BASE_MODEM (self), "^PREFMODE?", 3, FALSE, (GAsyncReadyCallback)prefmode_load_current_modes_ready, task); return; } g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Unable to load current modes"); g_object_unref (task); } /*****************************************************************************/ /* Set current modes (Modem interface) */ static gboolean set_current_modes_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void set_current_modes_ready (MMBroadbandModemHuawei *self, GAsyncResult *res, GTask *task) { GError *error = NULL; mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error); if (error) /* Let the error be critical. */ g_task_return_error (task, error); else g_task_return_boolean (task, TRUE); g_object_unref (task); } static gboolean prefmode_set_current_modes (MMBroadbandModemHuawei *self, MMModemMode allowed, MMModemMode preferred, GTask *task, GError **error) { guint i; MMHuaweiPrefmodeCombination *found = NULL; gchar *command; for (i = 0; i < self->priv->prefmode_supported_modes->len; i++) { MMHuaweiPrefmodeCombination *single; single = &g_array_index (self->priv->prefmode_supported_modes, MMHuaweiPrefmodeCombination, i); if (single->allowed == allowed && single->preferred == preferred) { found = single; break; } } if (!found) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_NOT_FOUND, "Requested mode ^PREFMODE combination not found"); return FALSE; } command = g_strdup_printf ("^PREFMODE=%u", found->prefmode); mm_base_modem_at_command ( MM_BASE_MODEM (self), command, 3, FALSE, (GAsyncReadyCallback)set_current_modes_ready, task); g_free (command); return TRUE; } static gboolean syscfg_set_current_modes (MMBroadbandModemHuawei *self, MMModemMode allowed, MMModemMode preferred, GTask *task, GError **error) { guint i; MMHuaweiSyscfgCombination *found = NULL; gchar *command; for (i = 0; i < self->priv->syscfg_supported_modes->len; i++) { MMHuaweiSyscfgCombination *single; single = &g_array_index (self->priv->syscfg_supported_modes, MMHuaweiSyscfgCombination, i); if (single->allowed == allowed && single->preferred == preferred) { found = single; break; } } if (!found) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_NOT_FOUND, "Requested mode ^SYSCFG combination not found"); return FALSE; } command = g_strdup_printf ("^SYSCFG=%u,%u,40000000,2,4", found->mode, found->acqorder); mm_base_modem_at_command ( MM_BASE_MODEM (self), command, 3, FALSE, (GAsyncReadyCallback)set_current_modes_ready, task); g_free (command); return TRUE; } static gboolean syscfgex_set_current_modes (MMBroadbandModemHuawei *self, MMModemMode allowed, MMModemMode preferred, GTask *task, GError **error) { guint i; MMHuaweiSyscfgexCombination *found = NULL; gchar *command; for (i = 0; i < self->priv->syscfgex_supported_modes->len; i++) { MMHuaweiSyscfgexCombination *single; single = &g_array_index (self->priv->syscfgex_supported_modes, MMHuaweiSyscfgexCombination, i); if (single->allowed == allowed && single->preferred == preferred) { found = single; break; } } if (!found) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_NOT_FOUND, "Requested mode ^SYSCFGEX combination not found"); return FALSE; } command = g_strdup_printf ("^SYSCFGEX=\"%s\",3fffffff,2,4,7fffffffffffffff,,", found->mode_str); mm_base_modem_at_command ( MM_BASE_MODEM (self), command, 3, FALSE, (GAsyncReadyCallback)set_current_modes_ready, task); g_free (command); return TRUE; } static void set_current_modes (MMIfaceModem *_self, MMModemMode allowed, MMModemMode preferred, GAsyncReadyCallback callback, gpointer user_data) { MMBroadbandModemHuawei *self = MM_BROADBAND_MODEM_HUAWEI (_self); GTask *task; GError *error = NULL; task = g_task_new (self, NULL, callback, user_data); if (self->priv->syscfgex_support == FEATURE_SUPPORTED) syscfgex_set_current_modes (self, allowed, preferred, task, &error); else if (self->priv->syscfg_support == FEATURE_SUPPORTED) syscfg_set_current_modes (self, allowed, preferred, task, &error); else if (self->priv->prefmode_support == FEATURE_SUPPORTED) prefmode_set_current_modes (self, allowed, preferred, task, &error); else error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Setting current modes is not supported"); if (error) { g_task_return_error (task, error); g_object_unref (task); } } /*****************************************************************************/ /* Setup/Cleanup unsolicited events (3GPP interface) */ static void huawei_signal_changed (MMPortSerialAt *port, GMatchInfo *match_info, MMBroadbandModemHuawei *self) { guint quality = 0; if (!mm_get_uint_from_match_info (match_info, 1, &quality)) return; if (quality == 99) { /* 99 means unknown */ quality = 0; } else { /* Normalize the quality */ quality = MM_CLAMP_HIGH (quality, 31) * 100 / 31; } mm_iface_modem_update_signal_quality (MM_IFACE_MODEM (self), quality); } static void huawei_mode_changed (MMPortSerialAt *port, GMatchInfo *match_info, MMBroadbandModemHuawei *self) { MMModemAccessTechnology act = MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN; gchar *str; gint a; guint32 mask = MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN; str = g_match_info_fetch (match_info, 1); a = atoi (str); g_free (str); /* CDMA/EVDO devices may not send this */ str = g_match_info_fetch (match_info, 2); if (str[0]) act = huawei_sysinfo_submode_to_act (atoi (str)); g_free (str); switch (a) { case 3: /* GSM/GPRS mode */ if (act != MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN && (act < MM_MODEM_ACCESS_TECHNOLOGY_GSM || act > MM_MODEM_ACCESS_TECHNOLOGY_EDGE)) { str = mm_modem_access_technology_build_string_from_mask (act); mm_obj_warn (self, "unexpected access technology (%s) in GSM/GPRS mode", str); g_free (str); act = MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN; } mask = MM_IFACE_MODEM_3GPP_ALL_ACCESS_TECHNOLOGIES_MASK; break; case 5: /* WCDMA mode */ if (act != MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN && (act < MM_MODEM_ACCESS_TECHNOLOGY_UMTS || act > MM_MODEM_ACCESS_TECHNOLOGY_HSPA_PLUS)) { str = mm_modem_access_technology_build_string_from_mask (act); mm_obj_warn (self, "unexpected access technology (%s) in WCDMA mode", str); g_free (str); act = MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN; } mask = MM_IFACE_MODEM_3GPP_ALL_ACCESS_TECHNOLOGIES_MASK; break; case 2: /* CDMA mode */ if (act != MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN && act != MM_MODEM_ACCESS_TECHNOLOGY_1XRTT) { str = mm_modem_access_technology_build_string_from_mask (act); mm_obj_warn (self, "unexpected access technology (%s) in CDMA mode", str); g_free (str); act = MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN; } if (act == MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN) act = MM_MODEM_ACCESS_TECHNOLOGY_1XRTT; mask = MM_IFACE_MODEM_CDMA_ALL_ACCESS_TECHNOLOGIES_MASK; break; case 4: /* HDR mode */ case 8: /* CDMA/HDR hybrid mode */ if (act != MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN && (act < MM_MODEM_ACCESS_TECHNOLOGY_EVDO0 || act > MM_MODEM_ACCESS_TECHNOLOGY_EVDOB)) { str = mm_modem_access_technology_build_string_from_mask (act); mm_obj_warn (self, "unexpected access technology (%s) in EVDO mode", str); g_free (str); act = MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN; } if (act == MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN) act = MM_MODEM_ACCESS_TECHNOLOGY_EVDO0; mask = MM_IFACE_MODEM_CDMA_ALL_ACCESS_TECHNOLOGIES_MASK; break; case 0: act = MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN; break; default: mm_obj_warn (self, "unexpected mode change value reported: '%d'", a); return; } mm_iface_modem_update_access_technologies (MM_IFACE_MODEM (self), act, mask); } static void huawei_status_changed (MMPortSerialAt *port, GMatchInfo *match_info, MMBroadbandModemHuawei *self) { gchar *str; gint 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)) mm_obj_dbg (self, "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); } typedef struct { gboolean ipv4_available; gboolean ipv4_connected; gboolean ipv6_available; gboolean ipv6_connected; } NdisstatResult; static void bearer_report_connection_status (MMBaseBearer *bearer, NdisstatResult *ndisstat_result) { if (ndisstat_result->ipv4_available) { /* TODO: MMBroadbandBearerHuawei does not currently support IPv6. * When it does, we should check the IP family associated with each bearer. */ mm_base_bearer_report_connection_status (bearer, ndisstat_result->ipv4_connected ? MM_BEARER_CONNECTION_STATUS_CONNECTED : MM_BEARER_CONNECTION_STATUS_DISCONNECTED); } } static void huawei_ndisstat_changed (MMPortSerialAt *port, GMatchInfo *match_info, MMBroadbandModemHuawei *self) { gchar *str; NdisstatResult ndisstat_result; GError *error = NULL; MMBearerList *list = NULL; str = g_match_info_fetch (match_info, 1); if (!mm_huawei_parse_ndisstatqry_response (str, &ndisstat_result.ipv4_available, &ndisstat_result.ipv4_connected, &ndisstat_result.ipv6_available, &ndisstat_result.ipv6_connected, &error)) { mm_obj_dbg (self, "ignored invalid ^NDISSTAT unsolicited message '%s': %s", str, error->message); g_error_free (error); g_free (str); return; } g_free (str); mm_obj_dbg (self, "NDIS status: IPv4 %s, IPv6 %s", ndisstat_result.ipv4_available ? (ndisstat_result.ipv4_connected ? "connected" : "disconnected") : "not available", ndisstat_result.ipv6_available ? (ndisstat_result.ipv6_connected ? "connected" : "disconnected") : "not available"); /* If empty bearer list, nothing else to do */ g_object_get (self, MM_IFACE_MODEM_BEARER_LIST, &list, NULL); if (!list) return; mm_bearer_list_foreach (list, (MMBearerListForeachFunc)bearer_report_connection_status, &ndisstat_result); g_object_unref (list); } static void detailed_signal_clear (DetailedSignal *signal) { g_clear_object (&signal->cdma); g_clear_object (&signal->evdo); g_clear_object (&signal->gsm); g_clear_object (&signal->umts); g_clear_object (&signal->lte); } static gboolean get_rssi_dbm (guint rssi, gdouble *out_val) { if (rssi <= 96) { *out_val = (double) (-121.0 + rssi); return TRUE; } return FALSE; } static gboolean get_ecio_db (guint ecio, gdouble *out_val) { if (ecio <= 65) { *out_val = -32.5 + ((double) ecio / 2.0); return TRUE; } return FALSE; } static gboolean get_rsrp_dbm (guint rsrp, gdouble *out_val) { if (rsrp <= 97) { *out_val = (double) (-141.0 + rsrp); return TRUE; } return FALSE; } static gboolean get_sinr_db (guint sinr, gdouble *out_val) { if (sinr <= 251) { *out_val = -20.2 + (double) (sinr / 5.0); return TRUE; } return FALSE; } static gboolean get_rsrq_db (guint rsrq, gdouble *out_val) { if (rsrq <= 34) { *out_val = -20 + (double) (rsrq / 2.0); return TRUE; } return FALSE; } static void huawei_hcsq_changed (MMPortSerialAt *port, GMatchInfo *match_info, MMBroadbandModemHuawei *self) { gchar *str; MMModemAccessTechnology act = MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN; guint value1 = 0; guint value2 = 0; guint value3 = 0; guint value4 = 0; guint value5 = 0; gdouble v; GError *error = NULL; str = g_match_info_fetch (match_info, 1); if (!mm_huawei_parse_hcsq_response (str, &act, &value1, &value2, &value3, &value4, &value5, &error)) { mm_obj_dbg (self, "ignored invalid ^HCSQ message '%s': %s", str, error->message); g_error_free (error); g_free (str); return; } g_free (str); detailed_signal_clear (&self->priv->detailed_signal); /* 2G */ if (act == MM_MODEM_ACCESS_TECHNOLOGY_GSM) { self->priv->detailed_signal.gsm = mm_signal_new (); /* value1: gsm_rssi */ if (get_rssi_dbm (value1, &v)) mm_signal_set_rssi (self->priv->detailed_signal.gsm, v); return; } /* 3G */ if (act == MM_MODEM_ACCESS_TECHNOLOGY_UMTS) { self->priv->detailed_signal.umts = mm_signal_new (); /* value1: wcdma_rssi */ if (get_rssi_dbm (value1, &v)) mm_signal_set_rssi (self->priv->detailed_signal.umts, v); /* value2: wcdma_rscp; unused */ /* value3: wcdma_ecio */ if (get_ecio_db (value3, &v)) mm_signal_set_ecio (self->priv->detailed_signal.umts, v); return; } /* 4G */ if (act == MM_MODEM_ACCESS_TECHNOLOGY_LTE) { self->priv->detailed_signal.lte = mm_signal_new (); /* value1: lte_rssi */ if (get_rssi_dbm (value1, &v)) mm_signal_set_rssi (self->priv->detailed_signal.lte, v); /* value2: lte_rsrp */ if (get_rsrp_dbm (value2, &v)) mm_signal_set_rsrp (self->priv->detailed_signal.lte, v); /* value3: lte_sinr -> SNR? */ if (get_sinr_db (value3, &v)) mm_signal_set_snr (self->priv->detailed_signal.lte, v); /* value4: lte_rsrq */ if (get_rsrq_db (value4, &v)) mm_signal_set_rsrq (self->priv->detailed_signal.lte, v); return; } /* CDMA and EVDO not yet supported */ } static void set_3gpp_unsolicited_events_handlers (MMBroadbandModemHuawei *self, gboolean enable) { GList *ports, *l; ports = mm_broadband_modem_huawei_get_at_port_list (self); /* Enable/disable unsolicited events in given port */ for (l = ports; l; l = g_list_next (l)) { MMPortSerialAt *port = MM_PORT_SERIAL_AT (l->data); /* Signal quality related */ mm_port_serial_at_add_unsolicited_msg_handler ( port, self->priv->rssi_regex, enable ? (MMPortSerialAtUnsolicitedMsgFn)huawei_signal_changed : NULL, enable ? self : NULL, NULL); /* Access technology related */ mm_port_serial_at_add_unsolicited_msg_handler ( port, self->priv->mode_regex, enable ? (MMPortSerialAtUnsolicitedMsgFn)huawei_mode_changed : NULL, enable ? self : NULL, NULL); /* Connection status related */ mm_port_serial_at_add_unsolicited_msg_handler ( port, self->priv->dsflowrpt_regex, enable ? (MMPortSerialAtUnsolicitedMsgFn)huawei_status_changed : NULL, enable ? self : NULL, NULL); mm_port_serial_at_add_unsolicited_msg_handler ( port, self->priv->ndisstat_regex, enable ? (MMPortSerialAtUnsolicitedMsgFn)huawei_ndisstat_changed : NULL, enable ? self : NULL, NULL); mm_port_serial_at_add_unsolicited_msg_handler ( port, self->priv->hcsq_regex, enable ? (MMPortSerialAtUnsolicitedMsgFn)huawei_hcsq_changed : NULL, enable ? self : NULL, NULL); } g_list_free_full (ports, g_object_unref); } static gboolean modem_3gpp_setup_cleanup_unsolicited_events_finish (MMIfaceModem3gpp *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void parent_3gpp_setup_unsolicited_events_ready (MMIfaceModem3gpp *self, GAsyncResult *res, GTask *task) { GError *error = NULL; if (!iface_modem_3gpp_parent->setup_unsolicited_events_finish (self, res, &error)) g_task_return_error (task, error); else { /* Our own setup now */ set_3gpp_unsolicited_events_handlers (MM_BROADBAND_MODEM_HUAWEI (self), TRUE); g_task_return_boolean (task, TRUE); } g_object_unref (task); } static void modem_3gpp_setup_unsolicited_events (MMIfaceModem3gpp *self, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; task = g_task_new (self, NULL, callback, user_data); /* Chain up parent's setup */ iface_modem_3gpp_parent->setup_unsolicited_events ( self, (GAsyncReadyCallback)parent_3gpp_setup_unsolicited_events_ready, task); } static void parent_3gpp_cleanup_unsolicited_events_ready (MMIfaceModem3gpp *self, GAsyncResult *res, GTask *task) { GError *error = NULL; if (!iface_modem_3gpp_parent->cleanup_unsolicited_events_finish (self, res, &error)) g_task_return_error (task, error); else g_task_return_boolean (task, TRUE); g_object_unref (task); } static void modem_3gpp_cleanup_unsolicited_events (MMIfaceModem3gpp *self, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; task = g_task_new (self, NULL, callback, user_data); /* Our own cleanup first */ set_3gpp_unsolicited_events_handlers (MM_BROADBAND_MODEM_HUAWEI (self), FALSE); /* And now chain up parent's cleanup */ iface_modem_3gpp_parent->cleanup_unsolicited_events ( self, (GAsyncReadyCallback)parent_3gpp_cleanup_unsolicited_events_ready, task); } /*****************************************************************************/ /* Enabling unsolicited events (3GPP interface) */ static gboolean modem_3gpp_enable_unsolicited_events_finish (MMIfaceModem3gpp *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void own_enable_unsolicited_events_ready (MMBaseModem *self, GAsyncResult *res, GTask *task) { GError *error = NULL; mm_base_modem_at_sequence_full_finish (self, res, NULL, &error); if (error) g_task_return_error (task, error); else g_task_return_boolean (task, TRUE); g_object_unref (task); } static const MMBaseModemAtCommand unsolicited_enable_sequence[] = { /* With ^PORTSEL we specify whether we want the PCUI port (0) or the * modem port (1) to receive the unsolicited messages */ { "^PORTSEL=0", 5, FALSE, NULL }, { "^CURC=1", 3, FALSE, NULL }, { NULL } }; static void parent_enable_unsolicited_events_ready (MMIfaceModem3gpp *self, GAsyncResult *res, GTask *task) { GError *error = NULL; if (!iface_modem_3gpp_parent->enable_unsolicited_events_finish (self, res, &error)) { g_task_return_error (task, error); g_object_unref (task); } /* Our own enable now */ mm_base_modem_at_sequence_full ( MM_BASE_MODEM (self), mm_base_modem_peek_port_primary (MM_BASE_MODEM (self)), unsolicited_enable_sequence, NULL, /* response_processor_context */ NULL, /* response_processor_context_free */ NULL, /* cancellable */ (GAsyncReadyCallback)own_enable_unsolicited_events_ready, task); } static void modem_3gpp_enable_unsolicited_events (MMIfaceModem3gpp *self, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; task = g_task_new (self, NULL, callback, user_data); /* Chain up parent's enable */ iface_modem_3gpp_parent->enable_unsolicited_events ( self, (GAsyncReadyCallback)parent_enable_unsolicited_events_ready, task); } /*****************************************************************************/ /* Disabling unsolicited events (3GPP interface) */ static gboolean modem_3gpp_disable_unsolicited_events_finish (MMIfaceModem3gpp *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void parent_disable_unsolicited_events_ready (MMIfaceModem3gpp *self, GAsyncResult *res, GTask *task) { GError *error = NULL; if (!iface_modem_3gpp_parent->disable_unsolicited_events_finish (self, res, &error)) g_task_return_error (task, error); else g_task_return_boolean (task, TRUE); g_object_unref (task); } static void own_disable_unsolicited_events_ready (MMBaseModem *self, GAsyncResult *res, GTask *task) { GError *error = NULL; mm_base_modem_at_command_full_finish (self, res, &error); if (error) { g_task_return_error (task, error); g_object_unref (task); return; } /* Next, chain up parent's disable */ iface_modem_3gpp_parent->disable_unsolicited_events ( MM_IFACE_MODEM_3GPP (self), (GAsyncReadyCallback)parent_disable_unsolicited_events_ready, task); } static void modem_3gpp_disable_unsolicited_events (MMIfaceModem3gpp *self, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; task = g_task_new (self, NULL, callback, user_data); /* Our own disable first */ mm_base_modem_at_command_full ( MM_BASE_MODEM (self), mm_base_modem_peek_port_primary (MM_BASE_MODEM (self)), "^CURC=0", 5, FALSE, /* allow_cached */ FALSE, /* raw */ NULL, /* cancellable */ (GAsyncReadyCallback)own_disable_unsolicited_events_ready, task); } /*****************************************************************************/ /* Create Bearer (Modem interface) */ static MMBaseBearer * huawei_modem_create_bearer_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { return g_task_propagate_pointer (G_TASK (res), error); } static void broadband_bearer_huawei_new_ready (GObject *source, GAsyncResult *res, GTask *task) { MMBaseBearer *bearer; GError *error = NULL; bearer = mm_broadband_bearer_huawei_new_finish (res, &error); if (!bearer) g_task_return_error (task, error); else g_task_return_pointer (task, bearer, g_object_unref); g_object_unref (task); } static void broadband_bearer_new_ready (GObject *source, GAsyncResult *res, GTask *task) { MMBaseBearer *bearer; GError *error = NULL; bearer = mm_broadband_bearer_new_finish (res, &error); if (!bearer) g_task_return_error (task, error); else g_task_return_pointer (task, bearer, g_object_unref); g_object_unref (task); } static void create_bearer_for_net_port (GTask *task) { MMBroadbandModemHuawei *self; MMBearerProperties *properties; self = g_task_get_source_object (task); properties = g_task_get_task_data (task); switch (self->priv->ndisdup_support) { case FEATURE_NOT_SUPPORTED: mm_obj_dbg (self, "^NDISDUP not supported, creating default bearer..."); mm_broadband_bearer_new (MM_BROADBAND_MODEM (self), properties, NULL, /* cancellable */ (GAsyncReadyCallback)broadband_bearer_new_ready, task); return; case FEATURE_SUPPORTED: mm_obj_dbg (self, "^NDISDUP supported, creating huawei bearer..."); mm_broadband_bearer_huawei_new (MM_BROADBAND_MODEM_HUAWEI (self), properties, NULL, /* cancellable */ (GAsyncReadyCallback)broadband_bearer_huawei_new_ready, task); return; case FEATURE_SUPPORT_UNKNOWN: default: g_assert_not_reached (); } } static MMPortSerialAt * peek_port_at_for_data (MMBroadbandModemHuawei *self, MMPort *port) { GList *cdc_wdm_at_ports, *l; const gchar *net_port_parent_path; MMPortSerialAt *found = NULL; g_warn_if_fail (mm_port_get_subsys (port) == MM_PORT_SUBSYS_NET); net_port_parent_path = mm_kernel_device_get_interface_sysfs_path (mm_port_peek_kernel_device (port)); if (!net_port_parent_path) { mm_obj_warn (self, "no parent path for net port %s", mm_port_get_device (port)); return NULL; } /* Find the CDC-WDM port on the same USB interface as the given net port */ cdc_wdm_at_ports = mm_base_modem_find_ports (MM_BASE_MODEM (self), MM_PORT_SUBSYS_USBMISC, MM_PORT_TYPE_AT); for (l = cdc_wdm_at_ports; l && !found; l = g_list_next (l)) { const gchar *wdm_port_parent_path; g_assert (MM_IS_PORT_SERIAL_AT (l->data)); wdm_port_parent_path = mm_kernel_device_get_interface_sysfs_path (mm_port_peek_kernel_device (MM_PORT (l->data))); if (wdm_port_parent_path && g_str_equal (wdm_port_parent_path, net_port_parent_path)) found = MM_PORT_SERIAL_AT (l->data); } g_list_free_full (cdc_wdm_at_ports, g_object_unref); return found; } MMPortSerialAt * mm_broadband_modem_huawei_peek_port_at_for_data (MMBroadbandModemHuawei *self, MMPort *port) { MMPortSerialAt *found; g_assert (self->priv->ndisdup_support == FEATURE_SUPPORTED); found = peek_port_at_for_data (self, port); if (!found) mm_obj_dbg (self, "couldn't find associated cdc-wdm port for %s", mm_port_get_device (port)); return found; } static void ensure_ndisdup_support_checked (MMBroadbandModemHuawei *self, MMPort *port) { /* Check NDISDUP support the first time we need it */ if (self->priv->ndisdup_support != FEATURE_SUPPORT_UNKNOWN) return; /* First, check for devices which support NDISDUP on any AT port. These * devices are tagged by udev */ if (mm_kernel_device_get_global_property_as_boolean (mm_port_peek_kernel_device (port), "ID_MM_HUAWEI_NDISDUP_SUPPORTED")) { mm_obj_dbg (self, "^NDISDUP is supported"); self->priv->ndisdup_support = FEATURE_SUPPORTED; } /* Then, look for devices which have both a net port and a cdc-wdm * AT-capable port. We assume that these devices allow NDISDUP only * when issued in the cdc-wdm port. */ else if (peek_port_at_for_data (self, port)) { mm_obj_dbg (self, "^NDISDUP is supported on non-serial AT port"); self->priv->ndisdup_support = FEATURE_SUPPORTED; } if (self->priv->ndisdup_support != FEATURE_SUPPORT_UNKNOWN) return; mm_obj_dbg (self, "^NDISDUP is not supported"); self->priv->ndisdup_support = FEATURE_NOT_SUPPORTED; } static void huawei_modem_create_bearer (MMIfaceModem *self, MMBearerProperties *properties, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; MMPort *port; task = g_task_new (self, NULL, callback, user_data); g_task_set_task_data (task, g_object_ref (properties), g_object_unref); port = mm_base_modem_peek_best_data_port (MM_BASE_MODEM (self), MM_PORT_TYPE_NET); if (port) { ensure_ndisdup_support_checked (MM_BROADBAND_MODEM_HUAWEI (self), port); create_bearer_for_net_port (task); return; } mm_obj_dbg (self, "creating default bearer..."); mm_broadband_bearer_new (MM_BROADBAND_MODEM (self), properties, NULL, /* cancellable */ (GAsyncReadyCallback)broadband_bearer_new_ready, task); } /*****************************************************************************/ /* USSD encode/decode (3GPP-USSD interface) * * Huawei devices don't use the current charset (as per AT+CSCS) in the CUSD * command, they instead expect data encoded in GSM-7 already, given as a * hex string. */ static gchar * encode (MMIfaceModem3gppUssd *self, const gchar *command, guint *scheme, GError **error) { g_autoptr(GByteArray) gsm = NULL; g_autofree guint8 *packed = NULL; guint32 packed_len = 0; gsm = mm_modem_charset_bytearray_from_utf8 (command, MM_MODEM_CHARSET_GSM, FALSE, error); if (!gsm) return NULL; *scheme = MM_MODEM_GSM_USSD_SCHEME_7BIT; /* If command is a multiple of 7 characters long, Huawei firmwares * apparently want that padded. Maybe all modems? */ if (gsm->len % 7 == 0) { static const guint8 padding = 0x0d; g_byte_array_append (gsm, &padding, 1); } packed = mm_charset_gsm_pack (gsm->data, gsm->len, 0, &packed_len); return mm_utils_bin2hexstr (packed, packed_len); } static gchar * decode (MMIfaceModem3gppUssd *self, const gchar *reply, GError **error) { g_autofree guint8 *bin = NULL; gsize bin_len = 0; g_autofree guint8 *unpacked = NULL; guint32 unpacked_len; g_autoptr(GByteArray) unpacked_array = NULL; bin = mm_utils_hexstr2bin (reply, -1, &bin_len, error); if (!bin) return NULL; unpacked = mm_charset_gsm_unpack (bin, (bin_len * 8) / 7, 0, &unpacked_len); /* if the last character in a 7-byte block is padding, then drop it */ if ((bin_len % 7 == 0) && (unpacked[unpacked_len - 1] == 0x0d)) unpacked_len--; unpacked_array = g_byte_array_sized_new (unpacked_len); g_byte_array_append (unpacked_array, unpacked, unpacked_len); return mm_modem_charset_bytearray_to_utf8 (unpacked_array, MM_MODEM_CHARSET_GSM, FALSE, error); } /*****************************************************************************/ static void huawei_1x_signal_changed (MMPortSerialAt *port, GMatchInfo *match_info, MMBroadbandModemHuawei *self) { guint quality = 0; if (!mm_get_uint_from_match_info (match_info, 1, &quality)) return; quality = MM_CLAMP_HIGH (quality, 100); mm_obj_dbg (self, "1X signal quality: %u", quality); mm_iface_modem_update_signal_quality (MM_IFACE_MODEM (self), quality); } static void huawei_evdo_signal_changed (MMPortSerialAt *port, GMatchInfo *match_info, MMBroadbandModemHuawei *self) { guint quality = 0; if (!mm_get_uint_from_match_info (match_info, 1, &quality)) return; quality = MM_CLAMP_HIGH (quality, 100); mm_obj_dbg (self, "EVDO signal quality: %u", quality); mm_iface_modem_update_signal_quality (MM_IFACE_MODEM (self), quality); } /* Signal quality loading (Modem interface) */ static guint modem_load_signal_quality_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { GError *inner_error = NULL; gssize value; value = g_task_propagate_int (G_TASK (res), &inner_error); if (inner_error) { g_propagate_error (error, inner_error); return 0; } return (guint)value; } static void parent_load_signal_quality_ready (MMIfaceModem *self, GAsyncResult *res, GTask *task) { GError *error = NULL; guint signal_quality; signal_quality = iface_modem_parent->load_signal_quality_finish (self, res, &error); if (error) g_task_return_error (task, error); else g_task_return_int (task, signal_quality); g_object_unref (task); } static void signal_ready (MMBaseModem *self, GAsyncResult *res, GTask *task) { const gchar *response, *command; gchar buf[5]; guint quality = 0, i = 0; response = mm_base_modem_at_command_finish (self, res, NULL); if (!response) { /* Fallback to parent's method */ iface_modem_parent->load_signal_quality ( MM_IFACE_MODEM (self), (GAsyncReadyCallback)parent_load_signal_quality_ready, task); return; } command = g_task_get_task_data (task); g_assert (command); response = mm_strip_tag (response, command); /* 'command' won't include the trailing ':' in the response, so strip that */ while ((*response == ':') || isspace (*response)) response++; /* Sanitize response for mm_get_uint_from_str() which wants only digits */ memset (buf, 0, sizeof (buf)); while (i < (sizeof (buf) - 1) && isdigit (*response)) buf[i++] = *response++; if (mm_get_uint_from_str (buf, &quality)) { quality = MM_CLAMP_HIGH (quality, 100); g_task_return_int (task, quality); } else { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't parse %s response: '%s'", command, response); } g_object_unref (task); } static void modem_load_signal_quality (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; MMModemCdmaRegistrationState evdo_state = MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN; const char *command = "^CSQLVL"; task = g_task_new (self, NULL, callback, user_data); /* 3GPP modems can just run parent's signal quality loading */ if (mm_iface_modem_is_3gpp (self)) { iface_modem_parent->load_signal_quality ( self, (GAsyncReadyCallback)parent_load_signal_quality_ready, task); return; } /* CDMA modems need custom signal quality loading */ g_object_get (G_OBJECT (self), MM_IFACE_MODEM_CDMA_EVDO_REGISTRATION_STATE, &evdo_state, NULL); if (evdo_state > MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN) command = "^HDRCSQLVL"; g_task_set_task_data (task, g_strdup (command), g_free); mm_base_modem_at_command ( MM_BASE_MODEM (self), command, 3, FALSE, (GAsyncReadyCallback)signal_ready, task); } /*****************************************************************************/ /* Setup/Cleanup unsolicited events (CDMA interface) */ static void set_cdma_unsolicited_events_handlers (MMBroadbandModemHuawei *self, gboolean enable) { GList *ports, *l; ports = mm_broadband_modem_huawei_get_at_port_list (self); /* Enable/disable unsolicited events in given port */ for (l = ports; l; l = g_list_next (l)) { MMPortSerialAt *port = MM_PORT_SERIAL_AT (l->data); /* Signal quality related */ mm_port_serial_at_add_unsolicited_msg_handler ( port, self->priv->rssilvl_regex, enable ? (MMPortSerialAtUnsolicitedMsgFn)huawei_1x_signal_changed : NULL, enable ? self : NULL, NULL); mm_port_serial_at_add_unsolicited_msg_handler ( port, self->priv->hrssilvl_regex, enable ? (MMPortSerialAtUnsolicitedMsgFn)huawei_evdo_signal_changed : NULL, enable ? self : NULL, NULL); /* Access technology related */ mm_port_serial_at_add_unsolicited_msg_handler ( port, self->priv->mode_regex, enable ? (MMPortSerialAtUnsolicitedMsgFn)huawei_mode_changed : NULL, enable ? self : NULL, NULL); } g_list_free_full (ports, g_object_unref); } static gboolean modem_cdma_setup_cleanup_unsolicited_events_finish (MMIfaceModemCdma *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void parent_cdma_setup_unsolicited_events_ready (MMIfaceModemCdma *self, GAsyncResult *res, GTask *task) { GError *error = NULL; if (!iface_modem_cdma_parent->setup_unsolicited_events_finish (self, res, &error)) g_task_return_error (task, error); else { /* Our own setup now */ set_cdma_unsolicited_events_handlers (MM_BROADBAND_MODEM_HUAWEI (self), TRUE); g_task_return_boolean (task, TRUE); } g_object_unref (task); } static void modem_cdma_setup_unsolicited_events (MMIfaceModemCdma *self, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; task = g_task_new (self, NULL, callback, user_data); /* Chain up parent's setup if needed */ if (iface_modem_cdma_parent->setup_unsolicited_events && iface_modem_cdma_parent->setup_unsolicited_events_finish) { iface_modem_cdma_parent->setup_unsolicited_events ( self, (GAsyncReadyCallback)parent_cdma_setup_unsolicited_events_ready, task); return; } /* Otherwise just run our setup and complete */ set_cdma_unsolicited_events_handlers (MM_BROADBAND_MODEM_HUAWEI (self), TRUE); g_task_return_boolean (task, TRUE); g_object_unref (task); } static void parent_cdma_cleanup_unsolicited_events_ready (MMIfaceModemCdma *self, GAsyncResult *res, GTask *task) { GError *error = NULL; if (!iface_modem_cdma_parent->cleanup_unsolicited_events_finish (self, res, &error)) g_task_return_error (task, error); else g_task_return_boolean (task, TRUE); g_object_unref (task); } static void modem_cdma_cleanup_unsolicited_events (MMIfaceModemCdma *self, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; task = g_task_new (self, NULL, callback, user_data); /* Our own cleanup first */ set_cdma_unsolicited_events_handlers (MM_BROADBAND_MODEM_HUAWEI (self), FALSE); /* Chain up parent's setup if needed */ if (iface_modem_cdma_parent->cleanup_unsolicited_events && iface_modem_cdma_parent->cleanup_unsolicited_events_finish) { iface_modem_cdma_parent->cleanup_unsolicited_events ( self, (GAsyncReadyCallback)parent_cdma_cleanup_unsolicited_events_ready, task); return; } /* Otherwise we're done */ g_task_return_boolean (task, TRUE); g_object_unref (task); } /*****************************************************************************/ /* Setup registration checks (CDMA interface) */ typedef struct { gboolean skip_qcdm_call_manager_step; gboolean skip_qcdm_hdr_step; gboolean skip_at_cdma_service_status_step; gboolean skip_at_cdma1x_serving_system_step; gboolean skip_detailed_registration_state; } SetupRegistrationChecksResults; static gboolean setup_registration_checks_finish (MMIfaceModemCdma *self, GAsyncResult *res, gboolean *skip_qcdm_call_manager_step, gboolean *skip_qcdm_hdr_step, gboolean *skip_at_cdma_service_status_step, gboolean *skip_at_cdma1x_serving_system_step, gboolean *skip_detailed_registration_state, GError **error) { SetupRegistrationChecksResults *results; results = g_task_propagate_pointer (G_TASK (res), error); if (!results) return FALSE; *skip_qcdm_call_manager_step = results->skip_qcdm_call_manager_step; *skip_qcdm_hdr_step = results->skip_qcdm_hdr_step; *skip_at_cdma_service_status_step = results->skip_at_cdma_service_status_step; *skip_at_cdma1x_serving_system_step = results->skip_at_cdma1x_serving_system_step; *skip_detailed_registration_state = results->skip_detailed_registration_state; g_free (results); return TRUE; } static void parent_setup_registration_checks_ready (MMIfaceModemCdma *self, GAsyncResult *res, GTask *task) { SetupRegistrationChecksResults *results; GError *error = NULL; results = g_new0 (SetupRegistrationChecksResults, 1); if (!iface_modem_cdma_parent->setup_registration_checks_finish (self, res, &results->skip_qcdm_call_manager_step, &results->skip_qcdm_hdr_step, &results->skip_at_cdma_service_status_step, &results->skip_at_cdma1x_serving_system_step, &results->skip_detailed_registration_state, &error)) { g_free (results); g_task_return_error (task, error); } else { gboolean evdo_supported = FALSE; g_object_get (self, MM_IFACE_MODEM_CDMA_EVDO_NETWORK_SUPPORTED, &evdo_supported, NULL); /* Don't use AT+CSS on EVDO-capable hardware for determining registration * status, because often the device will have only an EVDO connection and * AT+CSS won't necessarily report EVDO registration status, only 1X. */ if (evdo_supported) results->skip_at_cdma1x_serving_system_step = TRUE; /* Force to always use the detailed registration checks, as we have * ^SYSINFO for that */ results->skip_detailed_registration_state = FALSE; g_task_return_pointer (task, results, g_free); } g_object_unref (task); } static void setup_registration_checks (MMIfaceModemCdma *self, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; task = g_task_new (self, NULL, callback, user_data); /* Run parent's checks first */ iface_modem_cdma_parent->setup_registration_checks (self, (GAsyncReadyCallback)parent_setup_registration_checks_ready, task); } /*****************************************************************************/ /* Detailed registration state (CDMA interface) */ typedef struct { MMModemCdmaRegistrationState detailed_cdma1x_state; MMModemCdmaRegistrationState detailed_evdo_state; } DetailedRegistrationStateResults; typedef struct { DetailedRegistrationStateResults state; } DetailedRegistrationStateContext; static gboolean get_detailed_registration_state_finish (MMIfaceModemCdma *self, GAsyncResult *res, MMModemCdmaRegistrationState *detailed_cdma1x_state, MMModemCdmaRegistrationState *detailed_evdo_state, GError **error) { DetailedRegistrationStateResults *results; results = g_task_propagate_pointer (G_TASK (res), error); if (!results) return FALSE; *detailed_cdma1x_state = results->detailed_cdma1x_state; *detailed_evdo_state = results->detailed_evdo_state; g_free (results); return TRUE; } static void registration_state_sysinfo_ready (MMBroadbandModemHuawei *self, GAsyncResult *res, GTask *task) { DetailedRegistrationStateContext *ctx; gboolean extended = FALSE; guint srv_status = 0; guint sys_mode = 0; guint roam_status = 0; ctx = g_task_get_task_data (task); if (!sysinfo_finish (self, res, &extended, &srv_status, NULL, /* srv_domain */ &roam_status, NULL, /* sim_state */ &sys_mode, NULL, /* sys_submode_valid */ NULL, /* sys_submode */ NULL)) { /* If error, leave superclass' reg state alone if ^SYSINFO isn't supported. */ g_task_return_pointer (task, g_memdup (&ctx->state, sizeof (ctx->state)), g_free); g_object_unref (task); return; } if (srv_status == 2) { MMModemCdmaRegistrationState reg_state = MM_MODEM_CDMA_REGISTRATION_STATE_REGISTERED; MMModemAccessTechnology act; gboolean cdma1x = FALSE; gboolean evdo = FALSE; /* Service available, check roaming state */ if (roam_status == 0) reg_state = MM_MODEM_CDMA_REGISTRATION_STATE_HOME; else if (roam_status == 1) reg_state = MM_MODEM_CDMA_REGISTRATION_STATE_ROAMING; /* Check service type */ act = (extended ? huawei_sysinfoex_mode_to_act (sys_mode): huawei_sysinfo_mode_to_act (sys_mode)); if (act & MM_MODEM_ACCESS_TECHNOLOGY_1XRTT) { cdma1x = TRUE; ctx->state.detailed_cdma1x_state = reg_state; } if (act & MM_MODEM_ACCESS_TECHNOLOGY_EVDO0 || act & MM_MODEM_ACCESS_TECHNOLOGY_EVDOA || act & MM_MODEM_ACCESS_TECHNOLOGY_EVDOB) { evdo = TRUE; ctx->state.detailed_evdo_state = reg_state; } if (!cdma1x && !evdo) { /* Say we're registered to something even though sysmode parsing failed */ mm_obj_dbg (self, "assuming registered at least in CDMA1x"); ctx->state.detailed_cdma1x_state = MM_MODEM_CDMA_REGISTRATION_STATE_REGISTERED; } } g_task_return_pointer (task, g_memdup (&ctx->state, sizeof (ctx->state)), g_free); g_object_unref (task); } static void get_detailed_registration_state (MMIfaceModemCdma *self, MMModemCdmaRegistrationState cdma1x_state, MMModemCdmaRegistrationState evdo_state, GAsyncReadyCallback callback, gpointer user_data) { DetailedRegistrationStateContext *ctx; GTask *task; /* Setup context */ ctx = g_new (DetailedRegistrationStateContext, 1); ctx->state.detailed_cdma1x_state = cdma1x_state; ctx->state.detailed_evdo_state = evdo_state; task = g_task_new (self, NULL, callback, user_data); g_task_set_task_data (task, ctx, g_free); sysinfo (MM_BROADBAND_MODEM_HUAWEI (self), (GAsyncReadyCallback)registration_state_sysinfo_ready, task); } /*****************************************************************************/ /* Check if Voice supported (Voice interface) */ static gboolean modem_voice_check_support_finish (MMIfaceModemVoice *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void voice_parent_check_support_ready (MMIfaceModemVoice *self, GAsyncResult *res, GTask *task) { gboolean parent_support; parent_support = iface_modem_voice_parent->check_support_finish (self, res, NULL); g_task_return_boolean (task, parent_support); g_object_unref (task); } static void cvoice_check_ready (MMBaseModem *_self, GAsyncResult *res, GTask *task) { MMBroadbandModemHuawei *self = MM_BROADBAND_MODEM_HUAWEI (_self); GError *error = NULL; const gchar *response; response = mm_base_modem_at_command_finish (_self, res, &error); if (!response || !mm_huawei_parse_cvoice_response (response, &self->priv->audio_hz, &self->priv->audio_bits, &error)) { self->priv->cvoice_support = FEATURE_NOT_SUPPORTED; mm_obj_dbg (self, "CVOICE is unsupported: %s", error->message); g_clear_error (&error); /* Now check generic support */ iface_modem_voice_parent->check_support (MM_IFACE_MODEM_VOICE (self), (GAsyncReadyCallback)voice_parent_check_support_ready, task); return; } mm_obj_dbg (self, "CVOICE is supported"); self->priv->cvoice_support = FEATURE_SUPPORTED; g_task_return_boolean (task, TRUE); g_object_unref (task); } static void modem_voice_check_support (MMIfaceModemVoice *self, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; /* Check for Huawei-specific ^CVOICE support */ task = g_task_new (self, NULL, callback, user_data); mm_base_modem_at_command (MM_BASE_MODEM (self), "^CVOICE?", 3, TRUE, (GAsyncReadyCallback)cvoice_check_ready, task); } /*****************************************************************************/ /* In-call audio channel setup/cleanup */ static gboolean modem_voice_cleanup_in_call_audio_channel_finish (MMIfaceModemVoice *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void modem_voice_cleanup_in_call_audio_channel (MMIfaceModemVoice *_self, GAsyncReadyCallback callback, gpointer user_data) { MMBroadbandModemHuawei *self = MM_BROADBAND_MODEM_HUAWEI (_self); GTask *task; task = g_task_new (self, NULL, callback, user_data); /* If there is no CVOICE support, no custom audio setup required * (i.e. audio path is externally managed) */ if (self->priv->cvoice_support == FEATURE_SUPPORTED) { MMPort *port; /* The QCDM port, if present, switches back from voice to QCDM after * the voice call is dropped. */ port = MM_PORT (mm_base_modem_peek_port_qcdm (MM_BASE_MODEM (self))); if (port) { /* During a voice call, we'll set the QCDM port as connected, and that * will make us ignore all incoming data and avoid sending any outgoing * data. */ mm_port_set_connected (port, FALSE); } } g_task_return_boolean (task, TRUE); g_object_unref (task); } static gboolean modem_voice_setup_in_call_audio_channel_finish (MMIfaceModemVoice *_self, GAsyncResult *res, MMPort **audio_port, MMCallAudioFormat **audio_format, GError **error) { MMBroadbandModemHuawei *self = MM_BROADBAND_MODEM_HUAWEI (_self); if (!g_task_propagate_boolean (G_TASK (res), error)) return FALSE; if (self->priv->cvoice_support == FEATURE_SUPPORTED) { MMPort *port; /* Setup audio format */ if (audio_format) { gchar *resolution_str; resolution_str = g_strdup_printf ("s%ule", self->priv->audio_bits); *audio_format = mm_call_audio_format_new (); mm_call_audio_format_set_encoding (*audio_format, "pcm"); mm_call_audio_format_set_resolution (*audio_format, resolution_str); mm_call_audio_format_set_rate (*audio_format, self->priv->audio_hz); g_free (resolution_str); } /* The QCDM port, if present, switches from QCDM to voice while * a voice call is active. */ port = MM_PORT (mm_base_modem_peek_port_qcdm (MM_BASE_MODEM (self))); if (port) { /* During a voice call, we'll set the QCDM port as connected, and that * will make us ignore all incoming data and avoid sending any outgoing * data. */ mm_port_set_connected (port, TRUE); } if (audio_port) *audio_port = (port ? g_object_ref (port) : NULL);; } else { if (audio_format) *audio_format = NULL; if (audio_port) *audio_port = NULL; } return TRUE; } static void ddsetex_ready (MMBaseModem *self, GAsyncResult *res, GTask *task) { GError *error = NULL; if (!mm_base_modem_at_command_finish (self, res, &error)) g_task_return_error (task, error); else g_task_return_boolean (task, TRUE); g_object_unref (task); } static void modem_voice_setup_in_call_audio_channel (MMIfaceModemVoice *_self, GAsyncReadyCallback callback, gpointer user_data) { MMBroadbandModemHuawei *self = MM_BROADBAND_MODEM_HUAWEI (_self); GTask *task; task = g_task_new (self, NULL, callback, user_data); /* If there is no CVOICE support, no custom audio setup required * (i.e. audio path is externally managed) */ if (self->priv->cvoice_support != FEATURE_SUPPORTED) { g_task_return_boolean (task, TRUE); g_object_unref (task); return; } /* Enable audio streaming on the audio port */ mm_base_modem_at_command (MM_BASE_MODEM (self), "^DDSETEX=2", 5, FALSE, (GAsyncReadyCallback)ddsetex_ready, task); } /*****************************************************************************/ /* Common setup/cleanup voice unsolicited events */ typedef enum { HUAWEI_CALL_TYPE_VOICE = 0, HUAWEI_CALL_TYPE_CS_DATA = 1, HUAWEI_CALL_TYPE_PS_DATA = 2, HUAWEI_CALL_TYPE_CDMA_SMS = 3, HUAWEI_CALL_TYPE_OTA_STANDARD_OTASP = 7, HUAWEI_CALL_TYPE_OTA_NON_STANDARD_OTASP = 8, HUAWEI_CALL_TYPE_EMERGENCY = 9, } HuaweiCallType; static void orig_received (MMPortSerialAt *port, GMatchInfo *match_info, MMBroadbandModemHuawei *self) { MMCallInfo call_info = { 0 }; guint aux = 0; if (!mm_get_uint_from_match_info (match_info, 2, &aux)) { mm_obj_warn (self, "couldn't parse call type from ^ORIG"); return; } if (aux != HUAWEI_CALL_TYPE_VOICE && aux != HUAWEI_CALL_TYPE_EMERGENCY) { mm_obj_dbg (self, "ignored ^ORIG for non-voice call"); return; } if (!mm_get_uint_from_match_info (match_info, 1, &aux)) { mm_obj_warn (self, "couldn't parse call index from ^ORIG"); return; } call_info.index = aux; call_info.state = MM_CALL_STATE_DIALING; call_info.direction = MM_CALL_DIRECTION_OUTGOING; mm_obj_dbg (self, "call %u state updated: dialing", call_info.index); mm_iface_modem_voice_report_call (MM_IFACE_MODEM_VOICE (self), &call_info); } static void conf_received (MMPortSerialAt *port, GMatchInfo *match_info, MMBroadbandModemHuawei *self) { MMCallInfo call_info = { 0 }; guint aux = 0; if (!mm_get_uint_from_match_info (match_info, 1, &aux)) { mm_obj_warn (self, "couldn't parse call index from ^CONF"); return; } call_info.index = aux; call_info.state = MM_CALL_STATE_RINGING_OUT; call_info.direction = MM_CALL_DIRECTION_OUTGOING; mm_obj_dbg (self, "call %u state updated: ringing-out", call_info.index); mm_iface_modem_voice_report_call (MM_IFACE_MODEM_VOICE (self), &call_info); } static void conn_received (MMPortSerialAt *port, GMatchInfo *match_info, MMBroadbandModemHuawei *self) { MMCallInfo call_info = { 0 }; guint aux = 0; if (!mm_get_uint_from_match_info (match_info, 1, &aux)) { mm_obj_warn (self, "couldn't parse call index from ^CONN"); return; } call_info.index = aux; call_info.state = MM_CALL_STATE_ACTIVE; call_info.direction = MM_CALL_DIRECTION_UNKNOWN; mm_obj_dbg (self, "call %u state updated: active", aux); mm_iface_modem_voice_report_call (MM_IFACE_MODEM_VOICE (self), &call_info); } static void cend_received (MMPortSerialAt *port, GMatchInfo *match_info, MMBroadbandModemHuawei *self) { MMCallInfo call_info = { 0 }; guint aux = 0; /* only index is mandatory */ if (!mm_get_uint_from_match_info (match_info, 1, &aux)) { mm_obj_warn (self, "couldn't parse call index from ^CEND"); return; } call_info.index = aux; call_info.state = MM_CALL_STATE_TERMINATED; call_info.direction = MM_CALL_DIRECTION_UNKNOWN; mm_obj_dbg (self, "call %u state updated: terminated", call_info.index); if (mm_get_uint_from_match_info (match_info, 2, &aux)) mm_obj_dbg (self, " call duration: %u seconds", aux); if (mm_get_uint_from_match_info (match_info, 3, &aux)) mm_obj_dbg (self, " end status code: %u", aux); if (mm_get_uint_from_match_info (match_info, 4, &aux)) mm_obj_dbg (self, " call control cause: %u", aux); mm_iface_modem_voice_report_call (MM_IFACE_MODEM_VOICE (self), &call_info); } static void ddtmf_received (MMPortSerialAt *port, GMatchInfo *match_info, MMBroadbandModemHuawei *self) { gchar *dtmf; dtmf = g_match_info_fetch (match_info, 1); mm_obj_dbg (self, "received DTMF: %s", dtmf); /* call index unknown */ mm_iface_modem_voice_received_dtmf (MM_IFACE_MODEM_VOICE (self), 0, dtmf); g_free (dtmf); } static void common_voice_setup_cleanup_unsolicited_events (MMBroadbandModemHuawei *self, gboolean enable) { MMPortSerialAt *ports[2]; guint i; ports[0] = mm_base_modem_peek_port_primary (MM_BASE_MODEM (self)); ports[1] = mm_base_modem_peek_port_secondary (MM_BASE_MODEM (self)); for (i = 0; i < G_N_ELEMENTS (ports); i++) { if (!ports[i]) continue; mm_port_serial_at_add_unsolicited_msg_handler (ports[i], self->priv->orig_regex, enable ? (MMPortSerialAtUnsolicitedMsgFn)orig_received : NULL, enable ? self : NULL, NULL); mm_port_serial_at_add_unsolicited_msg_handler (ports[i], self->priv->conf_regex, enable ? (MMPortSerialAtUnsolicitedMsgFn)conf_received : NULL, enable ? self : NULL, NULL); mm_port_serial_at_add_unsolicited_msg_handler (ports[i], self->priv->conn_regex, enable ? (MMPortSerialAtUnsolicitedMsgFn)conn_received : NULL, enable ? self : NULL, NULL); mm_port_serial_at_add_unsolicited_msg_handler (ports[i], self->priv->cend_regex, enable ? (MMPortSerialAtUnsolicitedMsgFn)cend_received : NULL, enable ? self : NULL, NULL); mm_port_serial_at_add_unsolicited_msg_handler (ports[i], self->priv->ddtmf_regex, enable ? (MMPortSerialAtUnsolicitedMsgFn)ddtmf_received : NULL, enable ? self : NULL, NULL); } } /*****************************************************************************/ /* Setup unsolicited events (Voice interface) */ static gboolean modem_voice_setup_unsolicited_events_finish (MMIfaceModemVoice *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void parent_voice_setup_unsolicited_events_ready (MMIfaceModemVoice *self, GAsyncResult *res, GTask *task) { GError *error = NULL; if (!iface_modem_voice_parent->setup_unsolicited_events_finish (self, res, &error)) { g_task_return_error (task, error); g_object_unref (task); return; } /* Our own setup now */ common_voice_setup_cleanup_unsolicited_events (MM_BROADBAND_MODEM_HUAWEI (self), TRUE); g_task_return_boolean (task, TRUE); g_object_unref (task); } static void modem_voice_setup_unsolicited_events (MMIfaceModemVoice *self, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; task = g_task_new (self, NULL, callback, user_data); /* Chain up parent's setup */ iface_modem_voice_parent->setup_unsolicited_events ( self, (GAsyncReadyCallback)parent_voice_setup_unsolicited_events_ready, task); } /*****************************************************************************/ /* Cleanup unsolicited events (Voice interface) */ static gboolean modem_voice_cleanup_unsolicited_events_finish (MMIfaceModemVoice *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void parent_voice_cleanup_unsolicited_events_ready (MMIfaceModemVoice *self, GAsyncResult *res, GTask *task) { GError *error = NULL; if (!iface_modem_voice_parent->cleanup_unsolicited_events_finish (self, res, &error)) g_task_return_error (task, error); else g_task_return_boolean (task, TRUE); g_object_unref (task); } static void modem_voice_cleanup_unsolicited_events (MMIfaceModemVoice *self, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; task = g_task_new (self, NULL, callback, user_data); /* cleanup our own */ common_voice_setup_cleanup_unsolicited_events (MM_BROADBAND_MODEM_HUAWEI (self), FALSE); /* Chain up parent's cleanup */ iface_modem_voice_parent->cleanup_unsolicited_events ( self, (GAsyncReadyCallback)parent_voice_cleanup_unsolicited_events_ready, task); } /*****************************************************************************/ /* Enabling unsolicited events (Voice interface) */ static gboolean modem_voice_enable_unsolicited_events_finish (MMIfaceModemVoice *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void own_voice_enable_unsolicited_events_ready (MMBaseModem *self, GAsyncResult *res, GTask *task) { GError *error = NULL; mm_base_modem_at_sequence_full_finish (self, res, NULL, &error); if (error) g_task_return_error (task, error); else g_task_return_boolean (task, TRUE); g_object_unref (task); } static const MMBaseModemAtCommand unsolicited_voice_enable_sequence[] = { /* With ^DDTMFCFG we active the DTMF Decoder */ { "^DDTMFCFG=0,1", 3, FALSE, NULL }, { NULL } }; static void parent_voice_enable_unsolicited_events_ready (MMIfaceModemVoice *self, GAsyncResult *res, GTask *task) { GError *error = NULL; if (!iface_modem_voice_parent->enable_unsolicited_events_finish (self, res, &error)) { g_task_return_error (task, error); g_object_unref (task); return; } /* Our own enable now */ mm_base_modem_at_sequence_full ( MM_BASE_MODEM (self), mm_base_modem_peek_port_primary (MM_BASE_MODEM (self)), unsolicited_voice_enable_sequence, NULL, /* response_processor_context */ NULL, /* response_processor_context_free */ NULL, /* cancellable */ (GAsyncReadyCallback)own_voice_enable_unsolicited_events_ready, task); } static void modem_voice_enable_unsolicited_events (MMIfaceModemVoice *self, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; task = g_task_new (self, NULL, callback, user_data); /* Chain up parent's enable */ iface_modem_voice_parent->enable_unsolicited_events ( self, (GAsyncReadyCallback)parent_voice_enable_unsolicited_events_ready, task); } /*****************************************************************************/ /* Disabling unsolicited events (Voice interface) */ static gboolean modem_voice_disable_unsolicited_events_finish (MMIfaceModemVoice *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void own_voice_disable_unsolicited_events_ready (MMBaseModem *self, GAsyncResult *res, GTask *task) { GError *error = NULL; mm_base_modem_at_sequence_full_finish (self, res, NULL, &error); if (error) g_task_return_error (task, error); else g_task_return_boolean (task, TRUE); g_object_unref (task); } static const MMBaseModemAtCommand unsolicited_voice_disable_sequence[] = { /* With ^DDTMFCFG we deactivate the DTMF Decoder */ { "^DDTMFCFG=1,0", 3, FALSE, NULL }, { NULL } }; static void parent_voice_disable_unsolicited_events_ready (MMIfaceModemVoice *self, GAsyncResult *res, GTask *task) { GError *error = NULL; if (!iface_modem_voice_parent->disable_unsolicited_events_finish (self, res, &error)) { g_task_return_error (task, error); g_object_unref (task); return; } /* our own disable now */ mm_base_modem_at_sequence_full ( MM_BASE_MODEM (self), mm_base_modem_peek_port_primary (MM_BASE_MODEM (self)), unsolicited_voice_disable_sequence, NULL, /* response_processor_context */ NULL, /* response_processor_context_free */ NULL, /* cancellable */ (GAsyncReadyCallback)own_voice_disable_unsolicited_events_ready, task); } static void modem_voice_disable_unsolicited_events (MMIfaceModemVoice *self, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; task = g_task_new (self, NULL, callback, user_data); /* Chain up parent's disable */ iface_modem_voice_parent->disable_unsolicited_events ( self, (GAsyncReadyCallback)parent_voice_disable_unsolicited_events_ready, task); } /*****************************************************************************/ /* Create call (Voice interface) */ static MMBaseCall * create_call (MMIfaceModemVoice *self, MMCallDirection direction, const gchar *number) { return mm_base_call_new (MM_BASE_MODEM (self), direction, number, TRUE, /* skip_incoming_timeout */ TRUE, /* supports_dialing_to_ringing */ TRUE); /* supports_ringing_to_active) */ } /*****************************************************************************/ /* Load network time (Time interface) */ static MMNetworkTimezone * modem_time_load_network_timezone_finish (MMIfaceModemTime *_self, GAsyncResult *res, GError **error) { MMBroadbandModemHuawei *self = MM_BROADBAND_MODEM_HUAWEI (_self); MMNetworkTimezone *tz = NULL; const gchar *response; g_assert (self->priv->nwtime_support == FEATURE_SUPPORTED || self->priv->time_support == FEATURE_SUPPORTED); response = mm_base_modem_at_command_finish (MM_BASE_MODEM (_self), res, error); if (!response) return NULL; if (self->priv->nwtime_support == FEATURE_SUPPORTED) mm_huawei_parse_nwtime_response (response, NULL, &tz, error); else if (self->priv->time_support == FEATURE_SUPPORTED) mm_huawei_parse_time_response (response, NULL, &tz, error); return tz; } static gchar * modem_time_load_network_time_finish (MMIfaceModemTime *_self, GAsyncResult *res, GError **error) { MMBroadbandModemHuawei *self = MM_BROADBAND_MODEM_HUAWEI (_self); const gchar *response; gchar *iso8601 = NULL; g_assert (self->priv->nwtime_support == FEATURE_SUPPORTED || self->priv->time_support == FEATURE_SUPPORTED); response = mm_base_modem_at_command_finish (MM_BASE_MODEM (_self), res, error); if (!response) return NULL; if (self->priv->nwtime_support == FEATURE_SUPPORTED) mm_huawei_parse_nwtime_response (response, &iso8601, NULL, error); else if (self->priv->time_support == FEATURE_SUPPORTED) mm_huawei_parse_time_response (response, &iso8601, NULL, error); return iso8601; } static void modem_time_load_network_time_or_zone (MMIfaceModemTime *_self, GAsyncReadyCallback callback, gpointer user_data) { const char *command = NULL; MMBroadbandModemHuawei *self = MM_BROADBAND_MODEM_HUAWEI (_self); if (self->priv->nwtime_support == FEATURE_SUPPORTED) command = "^NWTIME?"; else if (self->priv->time_support == FEATURE_SUPPORTED) command = "^TIME"; g_assert (command != NULL); mm_base_modem_at_command (MM_BASE_MODEM (self), command, 3, FALSE, callback, user_data); } /*****************************************************************************/ /* Power state loading (Modem interface) */ static void enable_disable_unsolicited_rfswitch_event_handler (MMBroadbandModemHuawei *self, gboolean enable) { GList *ports, *l; ports = mm_broadband_modem_huawei_get_at_port_list (self); mm_obj_dbg (self, "%s ^RFSWITCH unsolicited event handler", enable ? "enable" : "disable"); for (l = ports; l; l = g_list_next (l)) { MMPortSerialAt *port = MM_PORT_SERIAL_AT (l->data); mm_port_serial_at_enable_unsolicited_msg_handler ( port, self->priv->rfswitch_regex, enable); } g_list_free_full (ports, g_object_unref); } static void parent_load_power_state_ready (MMIfaceModem *self, GAsyncResult *res, GTask *task) { GError *error = NULL; MMModemPowerState power_state; power_state = iface_modem_parent->load_power_state_finish (self, res, &error); if (error) g_task_return_error (task, error); else { /* As modem_power_down uses +CFUN=0 to put the modem in low state, we treat * CFUN 0 as 'LOW' power state instead of 'OFF'. Otherwise, MMIfaceModem * would prevent the modem from transitioning back to the 'ON' power state. */ if (power_state == MM_MODEM_POWER_STATE_OFF) power_state = MM_MODEM_POWER_STATE_LOW; g_task_return_int (task, power_state); } g_object_unref (task); } static void huawei_rfswitch_check_ready (MMBaseModem *_self, GAsyncResult *res, GTask *task) { MMBroadbandModemHuawei *self = MM_BROADBAND_MODEM_HUAWEI (_self); GError *error = NULL; const gchar *response; gint sw_state; enable_disable_unsolicited_rfswitch_event_handler (MM_BROADBAND_MODEM_HUAWEI (self), TRUE /* enable */); response = mm_base_modem_at_command_finish (_self, res, &error); if (response) { response = mm_strip_tag (response, "^RFSWITCH:"); if (sscanf (response, "%d", &sw_state) != 1 || (sw_state != 0 && sw_state != 1)) { mm_obj_warn (self, "couldn't parse ^RFSWITCH response '%s'", response); error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't parse ^RFSWITCH response '%s'", response); } } if (self->priv->rfswitch_support == FEATURE_SUPPORT_UNKNOWN) { if (error) { mm_obj_dbg (self, "^RFSWITCH is not supported"); self->priv->rfswitch_support = FEATURE_NOT_SUPPORTED; g_error_free (error); /* Fall back to parent's load_power_state */ iface_modem_parent->load_power_state (MM_IFACE_MODEM (self), (GAsyncReadyCallback)parent_load_power_state_ready, task); return; } mm_obj_dbg (self, "^RFSWITCH is supported"); self->priv->rfswitch_support = FEATURE_SUPPORTED; } if (error) g_task_return_error (task, error); else g_task_return_int (task, sw_state ? MM_MODEM_POWER_STATE_ON : MM_MODEM_POWER_STATE_LOW); g_object_unref (task); } static MMModemPowerState load_power_state_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { GError *inner_error = NULL; gssize value; value = g_task_propagate_int (G_TASK (res), &inner_error); if (inner_error) { g_propagate_error (error, inner_error); return MM_MODEM_POWER_STATE_UNKNOWN; } return (MMModemPowerState)value; } static void load_power_state (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; task = g_task_new (self, NULL, callback, user_data); switch (MM_BROADBAND_MODEM_HUAWEI (self)->priv->rfswitch_support) { case FEATURE_SUPPORT_UNKNOWN: case FEATURE_SUPPORTED: { /* Temporarily disable the unsolicited ^RFSWITCH event handler in order to * prevent it from discarding the response to the ^RFSWITCH? command. * It will be re-enabled in huawei_rfswitch_check_ready. */ enable_disable_unsolicited_rfswitch_event_handler (MM_BROADBAND_MODEM_HUAWEI (self), FALSE /* enable */); mm_base_modem_at_command (MM_BASE_MODEM (self), "^RFSWITCH?", 3, FALSE, (GAsyncReadyCallback)huawei_rfswitch_check_ready, task); break; } case FEATURE_NOT_SUPPORTED: /* Run parent's load_power_state */ iface_modem_parent->load_power_state (self, (GAsyncReadyCallback)parent_load_power_state_ready, task); break; default: g_assert_not_reached (); break; } } /*****************************************************************************/ /* Modem power up (Modem interface) */ static gboolean huawei_modem_power_up_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { return !!mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, error); } static void huawei_modem_power_up (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data) { switch (MM_BROADBAND_MODEM_HUAWEI (self)->priv->rfswitch_support) { case FEATURE_NOT_SUPPORTED: mm_base_modem_at_command (MM_BASE_MODEM (self), "+CFUN=1", 30, FALSE, callback, user_data); break; case FEATURE_SUPPORTED: mm_base_modem_at_command (MM_BASE_MODEM (self), "^RFSWITCH=1", 30, FALSE, callback, user_data); break; case FEATURE_SUPPORT_UNKNOWN: default: g_assert_not_reached (); break; } } /*****************************************************************************/ /* Modem power down (Modem interface) */ static gboolean huawei_modem_power_down_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { return !!mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, error); } static void huawei_modem_power_down (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data) { switch (MM_BROADBAND_MODEM_HUAWEI (self)->priv->rfswitch_support) { case FEATURE_NOT_SUPPORTED: /* +CFUN=0 is supported on all Huawei modems but +CFUN=4 isn't, * thus we use +CFUN=0 to put the modem in low power state. */ mm_base_modem_at_command (MM_BASE_MODEM (self), "+CFUN=0", 30, FALSE, callback, user_data); break; case FEATURE_SUPPORTED: mm_base_modem_at_command (MM_BASE_MODEM (self), "^RFSWITCH=0", 30, FALSE, callback, user_data); break; case FEATURE_SUPPORT_UNKNOWN: default: g_assert_not_reached (); break; } } /*****************************************************************************/ /* Create SIM (Modem interface) */ static MMBaseSim * huawei_modem_create_sim_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { return mm_sim_huawei_new_finish (res, error); } static void huawei_modem_create_sim (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data) { /* New Sierra SIM */ mm_sim_huawei_new (MM_BASE_MODEM (self), NULL, /* cancellable */ callback, user_data); } /*****************************************************************************/ /* Location capabilities loading (Location interface) */ static MMModemLocationSource location_load_capabilities_finish (MMIfaceModemLocation *self, GAsyncResult *res, GError **error) { GError *inner_error = NULL; gssize value; value = g_task_propagate_int (G_TASK (res), &inner_error); if (inner_error) { g_propagate_error (error, inner_error); return MM_MODEM_LOCATION_SOURCE_NONE; } return (MMModemLocationSource)value; } static void parent_load_capabilities_ready (MMIfaceModemLocation *self, GAsyncResult *res, GTask *task) { MMModemLocationSource sources; GError *error = NULL; sources = iface_modem_location_parent->load_capabilities_finish (self, res, &error); if (error) { g_task_return_error (task, error); g_object_unref (task); return; } /* not sure how to check if GPS is supported, just allow it */ if (mm_base_modem_peek_port_gps (MM_BASE_MODEM (self))) sources |= (MM_MODEM_LOCATION_SOURCE_GPS_NMEA | MM_MODEM_LOCATION_SOURCE_GPS_RAW | MM_MODEM_LOCATION_SOURCE_GPS_UNMANAGED); /* So we're done, complete */ g_task_return_int (task, sources); g_object_unref (task); } static void location_load_capabilities (MMIfaceModemLocation *self, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; task = g_task_new (self, NULL, callback, user_data); /* Chain up parent's setup */ iface_modem_location_parent->load_capabilities (self, (GAsyncReadyCallback)parent_load_capabilities_ready, task); } /*****************************************************************************/ /* Disable location gathering (Location interface) */ static gboolean disable_location_gathering_finish (MMIfaceModemLocation *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void gps_disabled_ready (MMBaseModem *self, GAsyncResult *res, GTask *task) { GError *error = NULL; if (!mm_base_modem_at_command_finish (self, res, &error)) g_task_return_error (task, error); else g_task_return_boolean (task, TRUE); g_object_unref (task); } static void disable_location_gathering (MMIfaceModemLocation *_self, MMModemLocationSource source, GAsyncReadyCallback callback, gpointer user_data) { MMBroadbandModemHuawei *self = MM_BROADBAND_MODEM_HUAWEI (_self); GTask *task; /* NOTE: no parent disable_location_gathering() implementation */ task = g_task_new (self, NULL, callback, user_data); self->priv->enabled_sources &= ~source; /* Only stop GPS engine if no GPS-related sources enabled */ if ((source & (MM_MODEM_LOCATION_SOURCE_GPS_NMEA | MM_MODEM_LOCATION_SOURCE_GPS_RAW | MM_MODEM_LOCATION_SOURCE_GPS_UNMANAGED)) && !(self->priv->enabled_sources & (MM_MODEM_LOCATION_SOURCE_GPS_NMEA | MM_MODEM_LOCATION_SOURCE_GPS_RAW | MM_MODEM_LOCATION_SOURCE_GPS_UNMANAGED))) { MMPortSerialGps *gps_port; /* Close the data port if we don't need it anymore */ if (source & (MM_MODEM_LOCATION_SOURCE_GPS_RAW | MM_MODEM_LOCATION_SOURCE_GPS_NMEA)) { gps_port = mm_base_modem_peek_port_gps (MM_BASE_MODEM (self)); if (gps_port) mm_port_serial_close (MM_PORT_SERIAL (gps_port)); } mm_base_modem_at_command (MM_BASE_MODEM (_self), "^WPEND", 3, FALSE, (GAsyncReadyCallback)gps_disabled_ready, task); return; } /* For any other location (e.g. 3GPP), or if still some GPS needed, just return */ g_task_return_boolean (task, TRUE); g_object_unref (task); } /*****************************************************************************/ /* Enable location gathering (Location interface) */ static const MMBaseModemAtCommand gps_startup[] = { { "^WPDOM=0", 3, FALSE, mm_base_modem_response_processor_no_result_continue }, { "^WPDST=1", 3, FALSE, mm_base_modem_response_processor_no_result_continue }, { "^WPDFR=65535,30", 3, FALSE, mm_base_modem_response_processor_no_result_continue }, { "^WPDGP", 3, FALSE, mm_base_modem_response_processor_no_result_continue }, { NULL } }; static gboolean enable_location_gathering_finish (MMIfaceModemLocation *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void gps_startup_ready (MMBaseModem *_self, GAsyncResult *res, GTask *task) { MMBroadbandModemHuawei *self = MM_BROADBAND_MODEM_HUAWEI (_self); MMModemLocationSource source; GError *error = NULL; mm_base_modem_at_sequence_finish (_self, res, NULL, &error); if (error) { g_task_return_error (task, error); g_object_unref (task); return; } source = GPOINTER_TO_UINT (g_task_get_task_data (task)); /* Only open the GPS port in NMEA/RAW setups */ if (source & (MM_MODEM_LOCATION_SOURCE_GPS_NMEA | MM_MODEM_LOCATION_SOURCE_GPS_RAW)) { MMPortSerialGps *gps_port; gps_port = mm_base_modem_peek_port_gps (MM_BASE_MODEM (self)); if (!gps_port || !mm_port_serial_open (MM_PORT_SERIAL (gps_port), &error)) { if (error) g_task_return_error (task, error); else g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't open raw GPS serial port"); } else { /* GPS port was successfully opened */ self->priv->enabled_sources |= source; g_task_return_boolean (task, TRUE); } } else { /* No need to open GPS port */ self->priv->enabled_sources |= source; g_task_return_boolean (task, TRUE); } g_object_unref (task); } static void parent_enable_location_gathering_ready (MMIfaceModemLocation *_self, GAsyncResult *res, GTask *task) { MMBroadbandModemHuawei *self = MM_BROADBAND_MODEM_HUAWEI (_self); GError *error = NULL; MMModemLocationSource source; gboolean start_gps = FALSE; if (!iface_modem_location_parent->enable_location_gathering_finish (_self, res, &error)) { g_task_return_error (task, error); g_object_unref (task); return; } /* Now our own enabling */ source = GPOINTER_TO_UINT (g_task_get_task_data (task)); /* Only start GPS engine if not done already */ start_gps = ((source & (MM_MODEM_LOCATION_SOURCE_GPS_NMEA | MM_MODEM_LOCATION_SOURCE_GPS_RAW | MM_MODEM_LOCATION_SOURCE_GPS_UNMANAGED)) && !(self->priv->enabled_sources & (MM_MODEM_LOCATION_SOURCE_GPS_NMEA | MM_MODEM_LOCATION_SOURCE_GPS_RAW | MM_MODEM_LOCATION_SOURCE_GPS_UNMANAGED))); if (start_gps) { mm_base_modem_at_sequence ( MM_BASE_MODEM (self), gps_startup, NULL, /* response_processor_context */ NULL, /* response_processor_context_free */ (GAsyncReadyCallback)gps_startup_ready, task); return; } /* For any other location (e.g. 3GPP), or if GPS already running just return */ self->priv->enabled_sources |= source; g_task_return_boolean (task, TRUE); g_object_unref (task); } static void enable_location_gathering (MMIfaceModemLocation *self, MMModemLocationSource source, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; task = g_task_new (self, NULL, callback, user_data); g_task_set_task_data (task, GUINT_TO_POINTER (source), NULL); /* Chain up parent's gathering enable */ iface_modem_location_parent->enable_location_gathering (self, source, (GAsyncReadyCallback)parent_enable_location_gathering_ready, task); } /*****************************************************************************/ /* Check support (Time interface) */ static gboolean modem_time_check_support_finish (MMIfaceModemTime *_self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void modem_time_check_ready (MMBaseModem *_self, GAsyncResult *res, GTask *task) { MMBroadbandModemHuawei *self = MM_BROADBAND_MODEM_HUAWEI (_self); /* Responses are checked in the sequence parser, ignore overall result */ mm_base_modem_at_sequence_finish (_self, res, NULL, NULL); g_task_return_boolean (task, (self->priv->nwtime_support == FEATURE_SUPPORTED || self->priv->time_support == FEATURE_SUPPORTED)); g_object_unref (task); } static MMBaseModemAtResponseProcessorResult modem_check_time_reply (MMBaseModem *_self, gpointer none, const gchar *command, const gchar *response, gboolean last_command, const GError *error, GVariant **result, GError **result_error) { MMBroadbandModemHuawei *self = MM_BROADBAND_MODEM_HUAWEI (_self); if (!error) { if (strstr (response, "^NWTIME")) self->priv->nwtime_support = FEATURE_SUPPORTED; else if (strstr (response, "^TIME")) self->priv->time_support = FEATURE_SUPPORTED; } else { if (strstr (command, "^NWTIME")) self->priv->nwtime_support = FEATURE_NOT_SUPPORTED; else if (strstr (command, "^TIME")) self->priv->time_support = FEATURE_NOT_SUPPORTED; } *result = NULL; *result_error = NULL; return MM_BASE_MODEM_AT_RESPONSE_PROCESSOR_RESULT_CONTINUE; } static const MMBaseModemAtCommand time_cmd_sequence[] = { { "^NWTIME?", 3, FALSE, modem_check_time_reply }, /* 3GPP/LTE */ { "^TIME", 3, FALSE, modem_check_time_reply }, /* CDMA */ { NULL } }; static void modem_time_check_support (MMIfaceModemTime *self, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; task = g_task_new (self, NULL, callback, user_data); mm_base_modem_at_sequence (MM_BASE_MODEM (self), time_cmd_sequence, NULL, /* response_processor_context */ NULL, /* response_processor_context_free */ (GAsyncReadyCallback)modem_time_check_ready, task); } /*****************************************************************************/ /* Check support (Signal interface) */ static void hcsq_check_ready (MMBaseModem *_self, GAsyncResult *res, GTask *task) { GError *error = NULL; const gchar *response; response = mm_base_modem_at_command_finish (_self, res, &error); if (response) g_task_return_boolean (task, TRUE); else g_task_return_error (task, error); g_object_unref (task); } static gboolean signal_check_support_finish (MMIfaceModemSignal *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void signal_check_support (MMIfaceModemSignal *_self, GAsyncReadyCallback callback, gpointer user_data) { MMBroadbandModemHuawei *self = MM_BROADBAND_MODEM_HUAWEI (_self); GTask *task; task = g_task_new (self, NULL, callback, user_data); mm_base_modem_at_command (MM_BASE_MODEM (self), "^HCSQ?", 3, FALSE, (GAsyncReadyCallback)hcsq_check_ready, task); } /*****************************************************************************/ /* Load extended signal information */ static void detailed_signal_free (DetailedSignal *signal) { detailed_signal_clear (signal); g_slice_free (DetailedSignal, signal); } static gboolean signal_load_values_finish (MMIfaceModemSignal *self, GAsyncResult *res, MMSignal **cdma, MMSignal **evdo, MMSignal **gsm, MMSignal **umts, MMSignal **lte, MMSignal **nr5g, GError **error) { DetailedSignal *signals; signals = g_task_propagate_pointer (G_TASK (res), error); if (!signals) return FALSE; *cdma = signals->cdma ? g_object_ref (signals->cdma) : NULL; *evdo = signals->evdo ? g_object_ref (signals->evdo) : NULL; *gsm = signals->gsm ? g_object_ref (signals->gsm) : NULL; *umts = signals->umts ? g_object_ref (signals->umts) : NULL; *lte = signals->lte ? g_object_ref (signals->lte) : NULL; *nr5g = signals->nr5g ? g_object_ref (signals->nr5g) : NULL; detailed_signal_free (signals); return TRUE; } static void hcsq_get_ready (MMBaseModem *_self, GAsyncResult *res, GTask *task) { MMBroadbandModemHuawei *self = MM_BROADBAND_MODEM_HUAWEI (_self); DetailedSignal *signals; GError *error = NULL; /* Don't care about the response; it will have been parsed by the HCSQ * unsolicited event handler and self->priv->detailed_signal will already * be updated. */ if (!mm_base_modem_at_command_finish (_self, res, &error)) { mm_obj_dbg (self, "^HCSQ failed: %s", error->message); g_task_return_error (task, error); g_object_unref (task); return; } signals = g_slice_new0 (DetailedSignal); signals->cdma = self->priv->detailed_signal.cdma ? g_object_ref (self->priv->detailed_signal.cdma) : NULL; signals->evdo = self->priv->detailed_signal.evdo ? g_object_ref (self->priv->detailed_signal.evdo) : NULL; signals->gsm = self->priv->detailed_signal.gsm ? g_object_ref (self->priv->detailed_signal.gsm) : NULL; signals->umts = self->priv->detailed_signal.umts ? g_object_ref (self->priv->detailed_signal.umts) : NULL; signals->lte = self->priv->detailed_signal.lte ? g_object_ref (self->priv->detailed_signal.lte) : NULL; g_task_return_pointer (task, signals, (GDestroyNotify)detailed_signal_free); g_object_unref (task); } static void signal_load_values (MMIfaceModemSignal *_self, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { MMBroadbandModemHuawei *self = MM_BROADBAND_MODEM_HUAWEI (_self); GTask *task; task = g_task_new (self, cancellable, callback, user_data); /* Clear any previous detailed signal values to get new ones */ detailed_signal_clear (&self->priv->detailed_signal); mm_base_modem_at_command (MM_BASE_MODEM (self), "^HCSQ?", 3, FALSE, (GAsyncReadyCallback)hcsq_get_ready, task); } /*****************************************************************************/ /* Setup ports (Broadband modem class) */ static void set_ignored_unsolicited_events_handlers (MMBroadbandModemHuawei *self) { GList *ports, *l; ports = mm_broadband_modem_huawei_get_at_port_list (self); /* Enable/disable unsolicited events in given port */ for (l = ports; l; l = g_list_next (l)) { MMPortSerialAt *port = MM_PORT_SERIAL_AT (l->data); mm_port_serial_at_add_unsolicited_msg_handler ( port, self->priv->boot_regex, NULL, NULL, NULL); mm_port_serial_at_add_unsolicited_msg_handler ( port, self->priv->connect_regex, NULL, NULL, NULL); mm_port_serial_at_add_unsolicited_msg_handler ( port, self->priv->csnr_regex, NULL, NULL, NULL); mm_port_serial_at_add_unsolicited_msg_handler ( port, self->priv->cusatp_regex, NULL, NULL, NULL); mm_port_serial_at_add_unsolicited_msg_handler ( port, self->priv->cusatend_regex, NULL, NULL, NULL); mm_port_serial_at_add_unsolicited_msg_handler ( port, self->priv->dsdormant_regex, NULL, NULL, NULL); mm_port_serial_at_add_unsolicited_msg_handler ( port, self->priv->simst_regex, NULL, NULL, NULL); mm_port_serial_at_add_unsolicited_msg_handler ( port, self->priv->srvst_regex, NULL, NULL, NULL); mm_port_serial_at_add_unsolicited_msg_handler ( port, self->priv->stin_regex, NULL, NULL, NULL); mm_port_serial_at_add_unsolicited_msg_handler ( port, self->priv->pdpdeact_regex, NULL, NULL, NULL); mm_port_serial_at_add_unsolicited_msg_handler ( port, self->priv->ndisend_regex, NULL, NULL, NULL); mm_port_serial_at_add_unsolicited_msg_handler ( port, self->priv->rfswitch_regex, NULL, NULL, NULL); mm_port_serial_at_add_unsolicited_msg_handler ( port, self->priv->position_regex, NULL, NULL, NULL); mm_port_serial_at_add_unsolicited_msg_handler ( port, self->priv->posend_regex, NULL, NULL, NULL); mm_port_serial_at_add_unsolicited_msg_handler ( port, self->priv->ecclist_regex, NULL, NULL, NULL); mm_port_serial_at_add_unsolicited_msg_handler ( port, self->priv->ltersrp_regex, NULL, NULL, NULL); mm_port_serial_at_add_unsolicited_msg_handler ( port, self->priv->cschannelinfo_regex, NULL, NULL, NULL); mm_port_serial_at_add_unsolicited_msg_handler ( port, self->priv->ccallstate_regex, NULL, NULL, NULL); mm_port_serial_at_add_unsolicited_msg_handler ( port, self->priv->eons_regex, NULL, NULL, NULL); mm_port_serial_at_add_unsolicited_msg_handler ( port, self->priv->lwurc_regex, NULL, NULL, NULL); } g_list_free_full (ports, g_object_unref); } static void gps_trace_received (MMPortSerialGps *port, const gchar *trace, MMIfaceModemLocation *self) { mm_iface_modem_location_gps_update (self, trace); } static void setup_ports (MMBroadbandModem *self) { MMPortSerialGps *gps_data_port; /* Call parent's setup ports first always */ MM_BROADBAND_MODEM_CLASS (mm_broadband_modem_huawei_parent_class)->setup_ports (self); /* Unsolicited messages to always ignore */ set_ignored_unsolicited_events_handlers (MM_BROADBAND_MODEM_HUAWEI (self)); /* Now reset the unsolicited messages we'll handle when enabled */ set_3gpp_unsolicited_events_handlers (MM_BROADBAND_MODEM_HUAWEI (self), FALSE); set_cdma_unsolicited_events_handlers (MM_BROADBAND_MODEM_HUAWEI (self), FALSE); /* NMEA GPS monitoring */ gps_data_port = mm_base_modem_peek_port_gps (MM_BASE_MODEM (self)); if (gps_data_port) { /* make sure GPS is stopped incase it was left enabled */ mm_base_modem_at_command_full (MM_BASE_MODEM (self), mm_base_modem_peek_port_primary (MM_BASE_MODEM (self)), "^WPEND", 3, FALSE, FALSE, NULL, NULL, NULL); /* Add handler for the NMEA traces */ mm_port_serial_gps_add_trace_handler (gps_data_port, (MMPortSerialGpsTraceFn)gps_trace_received, self, NULL); } } /*****************************************************************************/ MMBroadbandModemHuawei * mm_broadband_modem_huawei_new (const gchar *device, const gchar *physdev, const gchar **drivers, const gchar *plugin, guint16 vendor_id, guint16 product_id) { return g_object_new (MM_TYPE_BROADBAND_MODEM_HUAWEI, MM_BASE_MODEM_DEVICE, device, MM_BASE_MODEM_PHYSDEV, physdev, MM_BASE_MODEM_DRIVERS, drivers, MM_BASE_MODEM_PLUGIN, plugin, MM_BASE_MODEM_VENDOR_ID, vendor_id, MM_BASE_MODEM_PRODUCT_ID, product_id, /* Generic bearer (TTY) or Huawei bearer (NET) supported */ MM_BASE_MODEM_DATA_NET_SUPPORTED, TRUE, MM_BASE_MODEM_DATA_TTY_SUPPORTED, TRUE, NULL); } static void mm_broadband_modem_huawei_init (MMBroadbandModemHuawei *self) { /* Initialize private data */ self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, MM_TYPE_BROADBAND_MODEM_HUAWEI, MMBroadbandModemHuaweiPrivate); /* Prepare regular expressions to setup */ self->priv->rssi_regex = g_regex_new ("\\r\\n\\^RSSI:\\s*(\\d+)\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); self->priv->rssilvl_regex = g_regex_new ("\\r\\n\\^RSSILVL:\\s*(\\d+)\\r+\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); self->priv->hrssilvl_regex = g_regex_new ("\\r\\n\\^HRSSILVL:\\s*(\\d+)\\r+\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); /* 3GPP: ^MODE:5 * CDMA: ^MODE: 2 */ self->priv->mode_regex = g_regex_new ("\\r\\n\\^MODE:\\s*(\\d*),?(\\d*)\\r+\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); self->priv->dsflowrpt_regex = g_regex_new ("\\r\\n\\^DSFLOWRPT:(.+)\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); self->priv->ndisstat_regex = g_regex_new ("\\r\\n(\\^NDISSTAT:.+)\\r+\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); self->priv->orig_regex = g_regex_new ("\\r\\n\\^ORIG:\\s*(\\d+),\\s*(\\d+)\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); self->priv->conf_regex = g_regex_new ("\\r\\n\\^CONF:\\s*(\\d+)\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); self->priv->conn_regex = g_regex_new ("\\r\\n\\^CONN:\\s*(\\d+),\\s*(\\d+)\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); self->priv->cend_regex = g_regex_new ("\\r\\n\\^CEND:\\s*(\\d+),\\s*(\\d+),\\s*(\\d+)(?:,\\s*(\\d*))?\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); self->priv->ddtmf_regex = g_regex_new ("\\r\\n\\^DDTMF:\\s*([0-9A-D\\*\\#])\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); self->priv->boot_regex = g_regex_new ("\\r\\n\\^BOOT:.+\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); self->priv->connect_regex = g_regex_new ("\\r\\n\\^CONNECT .+\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); self->priv->csnr_regex = g_regex_new ("\\r\\n\\^CSNR:.+\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); self->priv->cusatp_regex = g_regex_new ("\\r\\n\\+CUSATP:.+\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); self->priv->cusatend_regex = g_regex_new ("\\r\\n\\+CUSATEND\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); self->priv->dsdormant_regex = g_regex_new ("\\r\\n\\^DSDORMANT:.+\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); self->priv->simst_regex = g_regex_new ("\\r\\n\\^SIMST:.+\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); self->priv->srvst_regex = g_regex_new ("\\r\\n\\^SRVST:.+\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); self->priv->stin_regex = g_regex_new ("\\r\\n\\^STIN:.+\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); self->priv->hcsq_regex = g_regex_new ("\\r\\n(\\^HCSQ:.+)\\r+\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); self->priv->pdpdeact_regex = g_regex_new ("\\r\\n\\^PDPDEACT:.+\\r+\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); self->priv->ndisend_regex = g_regex_new ("\\r\\n\\^NDISEND:.+\\r+\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); self->priv->rfswitch_regex = g_regex_new ("\\r\\n\\^RFSWITCH:.+\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); self->priv->position_regex = g_regex_new ("\\r\\n\\^POSITION:.+\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); self->priv->posend_regex = g_regex_new ("\\r\\n\\^POSEND:.+\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); self->priv->ecclist_regex = g_regex_new ("\\r\\n\\^ECCLIST:.+\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); self->priv->ltersrp_regex = g_regex_new ("\\r\\n\\^LTERSRP:.+\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); self->priv->cschannelinfo_regex = g_regex_new ("\\r\\n\\^CSCHANNELINFO:.+\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); self->priv->ccallstate_regex = g_regex_new ("\\r\\n\\^CCALLSTATE:.+\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); self->priv->eons_regex = g_regex_new ("\\r\\n\\^EONS:.+\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); self->priv->lwurc_regex = g_regex_new ("\\r\\n\\^LWURC:.+\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); self->priv->ndisdup_support = FEATURE_SUPPORT_UNKNOWN; self->priv->rfswitch_support = FEATURE_SUPPORT_UNKNOWN; self->priv->sysinfoex_support = FEATURE_SUPPORT_UNKNOWN; self->priv->syscfg_support = FEATURE_SUPPORT_UNKNOWN; self->priv->syscfgex_support = FEATURE_SUPPORT_UNKNOWN; self->priv->prefmode_support = FEATURE_SUPPORT_UNKNOWN; self->priv->nwtime_support = FEATURE_SUPPORT_UNKNOWN; self->priv->time_support = FEATURE_SUPPORT_UNKNOWN; self->priv->cvoice_support = FEATURE_SUPPORT_UNKNOWN; } static void dispose (GObject *object) { MMBroadbandModemHuawei *self = MM_BROADBAND_MODEM_HUAWEI (object); detailed_signal_clear (&self->priv->detailed_signal); G_OBJECT_CLASS (mm_broadband_modem_huawei_parent_class)->dispose (object); } static void finalize (GObject *object) { MMBroadbandModemHuawei *self = MM_BROADBAND_MODEM_HUAWEI (object); g_regex_unref (self->priv->rssi_regex); g_regex_unref (self->priv->rssilvl_regex); g_regex_unref (self->priv->hrssilvl_regex); g_regex_unref (self->priv->mode_regex); g_regex_unref (self->priv->dsflowrpt_regex); g_regex_unref (self->priv->ndisstat_regex); g_regex_unref (self->priv->orig_regex); g_regex_unref (self->priv->conf_regex); g_regex_unref (self->priv->conn_regex); g_regex_unref (self->priv->cend_regex); g_regex_unref (self->priv->ddtmf_regex); g_regex_unref (self->priv->boot_regex); g_regex_unref (self->priv->connect_regex); g_regex_unref (self->priv->csnr_regex); g_regex_unref (self->priv->cusatp_regex); g_regex_unref (self->priv->cusatend_regex); g_regex_unref (self->priv->dsdormant_regex); g_regex_unref (self->priv->simst_regex); g_regex_unref (self->priv->srvst_regex); g_regex_unref (self->priv->stin_regex); g_regex_unref (self->priv->hcsq_regex); g_regex_unref (self->priv->pdpdeact_regex); g_regex_unref (self->priv->ndisend_regex); g_regex_unref (self->priv->rfswitch_regex); g_regex_unref (self->priv->position_regex); g_regex_unref (self->priv->posend_regex); g_regex_unref (self->priv->ecclist_regex); g_regex_unref (self->priv->ltersrp_regex); g_regex_unref (self->priv->cschannelinfo_regex); g_regex_unref (self->priv->ccallstate_regex); g_regex_unref (self->priv->eons_regex); g_regex_unref (self->priv->lwurc_regex); if (self->priv->syscfg_supported_modes) g_array_unref (self->priv->syscfg_supported_modes); if (self->priv->syscfgex_supported_modes) g_array_unref (self->priv->syscfgex_supported_modes); if (self->priv->prefmode_supported_modes) g_array_unref (self->priv->prefmode_supported_modes); G_OBJECT_CLASS (mm_broadband_modem_huawei_parent_class)->finalize (object); } static void iface_modem_init (MMIfaceModem *iface) { iface_modem_parent = g_type_interface_peek_parent (iface); iface->reset = reset; iface->reset_finish = reset_finish; iface->load_access_technologies = load_access_technologies; iface->load_access_technologies_finish = load_access_technologies_finish; iface->load_unlock_retries = load_unlock_retries; iface->load_unlock_retries_finish = load_unlock_retries_finish; iface->modem_after_sim_unlock = modem_after_sim_unlock; iface->modem_after_sim_unlock_finish = modem_after_sim_unlock_finish; iface->load_current_bands = load_current_bands; iface->load_current_bands_finish = load_current_bands_finish; iface->set_current_bands = set_current_bands; iface->set_current_bands_finish = set_current_bands_finish; iface->load_supported_modes = load_supported_modes; iface->load_supported_modes_finish = load_supported_modes_finish; iface->load_current_modes = load_current_modes; iface->load_current_modes_finish = load_current_modes_finish; iface->set_current_modes = set_current_modes; iface->set_current_modes_finish = set_current_modes_finish; iface->load_signal_quality = modem_load_signal_quality; iface->load_signal_quality_finish = modem_load_signal_quality_finish; iface->create_bearer = huawei_modem_create_bearer; iface->create_bearer_finish = huawei_modem_create_bearer_finish; iface->load_power_state = load_power_state; iface->load_power_state_finish = load_power_state_finish; iface->modem_power_up = huawei_modem_power_up; iface->modem_power_up_finish = huawei_modem_power_up_finish; iface->modem_power_down = huawei_modem_power_down; iface->modem_power_down_finish = huawei_modem_power_down_finish; iface->create_sim = huawei_modem_create_sim; iface->create_sim_finish = huawei_modem_create_sim_finish; } static void iface_modem_3gpp_init (MMIfaceModem3gpp *iface) { iface_modem_3gpp_parent = g_type_interface_peek_parent (iface); iface->setup_unsolicited_events = modem_3gpp_setup_unsolicited_events; iface->setup_unsolicited_events_finish = modem_3gpp_setup_cleanup_unsolicited_events_finish; iface->cleanup_unsolicited_events = modem_3gpp_cleanup_unsolicited_events; iface->cleanup_unsolicited_events_finish = modem_3gpp_setup_cleanup_unsolicited_events_finish; iface->enable_unsolicited_events = modem_3gpp_enable_unsolicited_events; iface->enable_unsolicited_events_finish = modem_3gpp_enable_unsolicited_events_finish; iface->disable_unsolicited_events = modem_3gpp_disable_unsolicited_events; iface->disable_unsolicited_events_finish = modem_3gpp_disable_unsolicited_events_finish; } static void iface_modem_3gpp_ussd_init (MMIfaceModem3gppUssd *iface) { iface->encode = encode; iface->decode = decode; } static void iface_modem_cdma_init (MMIfaceModemCdma *iface) { iface_modem_cdma_parent = g_type_interface_peek_parent (iface); iface->setup_unsolicited_events = modem_cdma_setup_unsolicited_events; iface->setup_unsolicited_events_finish = modem_cdma_setup_cleanup_unsolicited_events_finish; iface->cleanup_unsolicited_events = modem_cdma_cleanup_unsolicited_events; iface->cleanup_unsolicited_events_finish = modem_cdma_setup_cleanup_unsolicited_events_finish; iface->setup_registration_checks = setup_registration_checks; iface->setup_registration_checks_finish = setup_registration_checks_finish; iface->get_detailed_registration_state = get_detailed_registration_state; iface->get_detailed_registration_state_finish = get_detailed_registration_state_finish; } static void iface_modem_location_init (MMIfaceModemLocation *iface) { iface_modem_location_parent = g_type_interface_peek_parent (iface); iface->load_capabilities = location_load_capabilities; iface->load_capabilities_finish = location_load_capabilities_finish; iface->enable_location_gathering = enable_location_gathering; iface->enable_location_gathering_finish = enable_location_gathering_finish; iface->disable_location_gathering = disable_location_gathering; iface->disable_location_gathering_finish = disable_location_gathering_finish; } static void iface_modem_time_init (MMIfaceModemTime *iface) { iface->check_support = modem_time_check_support; iface->check_support_finish = modem_time_check_support_finish; iface->load_network_time = modem_time_load_network_time_or_zone; iface->load_network_time_finish = modem_time_load_network_time_finish; iface->load_network_timezone = modem_time_load_network_time_or_zone; iface->load_network_timezone_finish = modem_time_load_network_timezone_finish; } static void iface_modem_voice_init (MMIfaceModemVoice *iface) { iface_modem_voice_parent = g_type_interface_peek_parent (iface); iface->check_support = modem_voice_check_support; iface->check_support_finish = modem_voice_check_support_finish; iface->setup_unsolicited_events = modem_voice_setup_unsolicited_events; iface->setup_unsolicited_events_finish = modem_voice_setup_unsolicited_events_finish; iface->cleanup_unsolicited_events = modem_voice_cleanup_unsolicited_events; iface->cleanup_unsolicited_events_finish = modem_voice_cleanup_unsolicited_events_finish; iface->enable_unsolicited_events = modem_voice_enable_unsolicited_events; iface->enable_unsolicited_events_finish = modem_voice_enable_unsolicited_events_finish; iface->disable_unsolicited_events = modem_voice_disable_unsolicited_events; iface->disable_unsolicited_events_finish = modem_voice_disable_unsolicited_events_finish; iface->setup_in_call_audio_channel = modem_voice_setup_in_call_audio_channel; iface->setup_in_call_audio_channel_finish = modem_voice_setup_in_call_audio_channel_finish; iface->cleanup_in_call_audio_channel = modem_voice_cleanup_in_call_audio_channel; iface->cleanup_in_call_audio_channel_finish = modem_voice_cleanup_in_call_audio_channel_finish; iface->create_call = create_call; } static void iface_modem_signal_init (MMIfaceModemSignal *iface) { iface->check_support = signal_check_support; iface->check_support_finish = signal_check_support_finish; iface->load_values = signal_load_values; iface->load_values_finish = signal_load_values_finish; } static void mm_broadband_modem_huawei_class_init (MMBroadbandModemHuaweiClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); MMBroadbandModemClass *broadband_modem_class = MM_BROADBAND_MODEM_CLASS (klass); g_type_class_add_private (object_class, sizeof (MMBroadbandModemHuaweiPrivate)); object_class->dispose = dispose; object_class->finalize = finalize; broadband_modem_class->setup_ports = setup_ports; } ModemManager-1.23.4-dev/src/plugins/huawei/mm-broadband-modem-huawei.h000066400000000000000000000054131456466623000255350ustar00rootroot00000000000000/* -*- 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 - 2012 Red Hat, Inc. * Copyright (C) 2012 Aleksander Morgado */ #ifndef MM_BROADBAND_MODEM_HUAWEI_H #define MM_BROADBAND_MODEM_HUAWEI_H #include "mm-broadband-modem.h" #define MM_TYPE_BROADBAND_MODEM_HUAWEI (mm_broadband_modem_huawei_get_type ()) #define MM_BROADBAND_MODEM_HUAWEI(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_BROADBAND_MODEM_HUAWEI, MMBroadbandModemHuawei)) #define MM_BROADBAND_MODEM_HUAWEI_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_BROADBAND_MODEM_HUAWEI, MMBroadbandModemHuaweiClass)) #define MM_IS_BROADBAND_MODEM_HUAWEI(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_BROADBAND_MODEM_HUAWEI)) #define MM_IS_BROADBAND_MODEM_HUAWEI_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_BROADBAND_MODEM_HUAWEI)) #define MM_BROADBAND_MODEM_HUAWEI_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_BROADBAND_MODEM_HUAWEI, MMBroadbandModemHuaweiClass)) typedef struct _MMBroadbandModemHuawei MMBroadbandModemHuawei; typedef struct _MMBroadbandModemHuaweiClass MMBroadbandModemHuaweiClass; typedef struct _MMBroadbandModemHuaweiPrivate MMBroadbandModemHuaweiPrivate; struct _MMBroadbandModemHuawei { MMBroadbandModem parent; MMBroadbandModemHuaweiPrivate *priv; }; struct _MMBroadbandModemHuaweiClass{ MMBroadbandModemClass parent; }; GType mm_broadband_modem_huawei_get_type (void); MMBroadbandModemHuawei *mm_broadband_modem_huawei_new (const gchar *device, const gchar *physdev, const gchar **driver, const gchar *plugin, guint16 vendor_id, guint16 product_id); MMPortSerialAt *mm_broadband_modem_huawei_peek_port_at_for_data (MMBroadbandModemHuawei *self, MMPort *port); GList *mm_broadband_modem_huawei_get_at_port_list (MMBroadbandModemHuawei *self); #endif /* MM_BROADBAND_MODEM_HUAWEI_H */ ModemManager-1.23.4-dev/src/plugins/huawei/mm-modem-helpers-huawei.c000066400000000000000000001453701456466623000252650ustar00rootroot00000000000000/* -*- 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) 2013 Huawei Technologies Co., Ltd * Copyright (C) 2013 Aleksander Morgado */ #include #include #include #include #define _LIBMM_INSIDE_MM #include #include "mm-log-object.h" #include "mm-common-helpers.h" #include "mm-modem-helpers.h" #include "mm-modem-helpers-huawei.h" #include "mm-huawei-enums-types.h" /*****************************************************************************/ /* ^NDISSTAT / ^NDISSTATQRY response parser */ gboolean mm_huawei_parse_ndisstatqry_response (const gchar *response, gboolean *ipv4_available, gboolean *ipv4_connected, gboolean *ipv6_available, gboolean *ipv6_connected, GError **error) { GError *inner_error = NULL; if (!response || !(g_ascii_strncasecmp (response, "^NDISSTAT:", strlen ("^NDISSTAT:")) == 0 || g_ascii_strncasecmp (response, "^NDISSTATQRY:", strlen ("^NDISSTATQRY:")) == 0)) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Missing ^NDISSTAT / ^NDISSTATQRY prefix"); return FALSE; } *ipv4_available = FALSE; *ipv6_available = FALSE; /* The response maybe as: * ^NDISSTAT: 1,,,IPV4 * ^NDISSTAT: 0,33,,IPV6 * ^NDISSTATQRY: 1,,,IPV4 * ^NDISSTATQRY: 0,33,,IPV6 * OK * * Or, in newer firmwares: * ^NDISSTATQRY:0,,,"IPV4",0,,,"IPV6" * OK * * Or, even (handled separately): * ^NDISSTATQry:1 * OK */ /* If multiple fields available, try first parsing method */ if (strchr (response, ',')) { g_autoptr(GRegex) r = NULL; g_autoptr(GMatchInfo) match_info = NULL; r = g_regex_new ("\\^NDISSTAT(?:QRY)?(?:Qry)?:\\s*(\\d),([^,]*),([^,]*),([^,\\r\\n]*)(?:\\r\\n)?" "(?:\\^NDISSTAT:|\\^NDISSTATQRY:)?\\s*,?(\\d)?,?([^,]*)?,?([^,]*)?,?([^,\\r\\n]*)?(?:\\r\\n)?", G_REGEX_DOLLAR_ENDONLY | G_REGEX_RAW, 0, NULL); g_assert (r != NULL); g_regex_match_full (r, response, strlen (response), 0, 0, &match_info, &inner_error); if (!inner_error && g_match_info_matches (match_info)) { guint ip_type_field = 4; /* IPv4 and IPv6 are fields 4 and (if available) 8 */ while (!inner_error && ip_type_field <= 8) { gchar *ip_type_str; guint connected; ip_type_str = mm_get_string_unquoted_from_match_info (match_info, ip_type_field); if (!ip_type_str) break; if (!mm_get_uint_from_match_info (match_info, (ip_type_field - 3), &connected) || (connected != 0 && connected != 1)) { inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't parse ^NDISSTAT / ^NDISSTATQRY fields"); } else if (g_ascii_strcasecmp (ip_type_str, "IPV4") == 0) { *ipv4_available = TRUE; *ipv4_connected = (gboolean)connected; } else if (g_ascii_strcasecmp (ip_type_str, "IPV6") == 0) { *ipv6_available = TRUE; *ipv6_connected = (gboolean)connected; } g_free (ip_type_str); ip_type_field += 4; } } } /* No separate IPv4/IPv6 info given just connected/not connected */ else { g_autoptr(GRegex) r = NULL; g_autoptr(GMatchInfo) match_info = NULL; r = g_regex_new ("\\^NDISSTAT(?:QRY)?(?:Qry)?:\\s*(\\d)(?:\\r\\n)?", G_REGEX_DOLLAR_ENDONLY | G_REGEX_RAW, 0, NULL); g_assert (r != NULL); g_regex_match_full (r, response, strlen (response), 0, 0, &match_info, &inner_error); if (!inner_error && g_match_info_matches (match_info)) { guint connected; if (!mm_get_uint_from_match_info (match_info, 1, &connected) || (connected != 0 && connected != 1)) { inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't parse ^NDISSTAT / ^NDISSTATQRY fields"); } else { /* We'll assume IPv4 */ *ipv4_available = TRUE; *ipv4_connected = (gboolean)connected; } } } if (!ipv4_available && !ipv6_available) { inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't find IPv4 or IPv6 info in ^NDISSTAT / ^NDISSTATQRY response"); } if (inner_error) { g_propagate_error (error, inner_error); return FALSE; } return TRUE; } /*****************************************************************************/ /* ^DHCP response parser */ static gboolean match_info_to_ip4_addr (GMatchInfo *match_info, guint match_index, guint *out_addr) { g_autofree gchar *s = NULL; g_autofree guint8 *bin = NULL; gchar buf[9]; gsize len; gsize bin_len; guint32 aux; s = g_match_info_fetch (match_info, match_index); g_return_val_if_fail (s != NULL, FALSE); len = strlen (s); if (len == 1 && s[0] == '0') { *out_addr = 0; return TRUE; } if (len < 7 || len > 8) return FALSE; /* Handle possibly missing leading zero */ memset (buf, 0, sizeof (buf)); if (len == 7) { strcpy (&buf[1], s); buf[0] = '0'; } else if (len == 8) strcpy (buf, s); else g_assert_not_reached (); bin = mm_utils_hexstr2bin (buf, -1, &bin_len, NULL); if (!bin || bin_len != 4) return FALSE; memcpy (&aux, bin, 4); *out_addr = GUINT32_SWAP_LE_BE (aux); return TRUE; } gboolean mm_huawei_parse_dhcp_response (const char *reply, guint *out_address, guint *out_prefix, guint *out_gateway, guint *out_dns1, guint *out_dns2, GError **error) { g_autoptr(GRegex) r = NULL; g_autoptr(GMatchInfo) match_info = NULL; gboolean matched; GError *match_error = NULL; g_assert (reply != NULL); g_assert (out_address != NULL); g_assert (out_prefix != NULL); g_assert (out_gateway != NULL); g_assert (out_dns1 != NULL); g_assert (out_dns2 != NULL); /* Format: * * ^DHCP:
,,,,,,, * * All numbers are hexadecimal representations of IPv4 addresses, with * least-significant byte first. eg, 192.168.50.32 is expressed as * "2032A8C0". Sometimes leading zeros are stripped, so "1010A0A" is * actually 10.10.1.1. */ r = g_regex_new ("\\^DHCP:\\s*(?:0[xX])?([0-9a-fA-F]+),(?:0[xX])?([0-9a-fA-F]+),(?:0[xX])?([0-9a-fA-F]+),(?:0[xX])?([0-9a-fA-F]+),(?:0[xX])?([0-9a-fA-F]+),(?:0[xX])?([0-9a-fA-F]+),.*$", 0, 0, NULL); g_assert (r != NULL); matched = g_regex_match_full (r, reply, -1, 0, 0, &match_info, &match_error); if (!matched) { if (match_error) { g_propagate_error (error, match_error); g_prefix_error (error, "Could not parse ^DHCP results: "); } else { g_set_error_literal (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't match ^DHCP reply"); } } else { guint netmask; if (match_info_to_ip4_addr (match_info, 1, out_address) && match_info_to_ip4_addr (match_info, 2, &netmask) && match_info_to_ip4_addr (match_info, 3, out_gateway) && match_info_to_ip4_addr (match_info, 5, out_dns1) && match_info_to_ip4_addr (match_info, 6, out_dns2)) { *out_prefix = mm_count_bits_set (netmask); matched = TRUE; } } return matched; } /*****************************************************************************/ /* ^SYSINFO response parser */ gboolean mm_huawei_parse_sysinfo_response (const char *reply, guint *out_srv_status, guint *out_srv_domain, guint *out_roam_status, guint *out_sys_mode, guint *out_sim_state, gboolean *out_sys_submode_valid, guint *out_sys_submode, GError **error) { g_autoptr(GRegex) r = NULL; g_autoptr(GMatchInfo) match_info = NULL; gboolean matched; GError *match_error = NULL; g_assert (out_srv_status != NULL); g_assert (out_srv_domain != NULL); g_assert (out_roam_status != NULL); g_assert (out_sys_mode != NULL); g_assert (out_sim_state != NULL); g_assert (out_sys_submode_valid != NULL); g_assert (out_sys_submode != NULL); /* Format: * * ^SYSINFO: ,,,,[,,] */ /* Can't just use \d here since sometimes you get "^SYSINFO:2,1,0,3,1,,3" */ r = g_regex_new ("\\^SYSINFO:\\s*(\\d+),(\\d+),(\\d+),(\\d+),(\\d+),?(\\d+)?,?(\\d+)?$", 0, 0, NULL); g_assert (r != NULL); matched = g_regex_match_full (r, reply, -1, 0, 0, &match_info, &match_error); if (!matched) { if (match_error) { g_propagate_error (error, match_error); g_prefix_error (error, "Could not parse ^SYSINFO results: "); } else { g_set_error_literal (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't match ^SYSINFO reply"); } } else { mm_get_uint_from_match_info (match_info, 1, out_srv_status); mm_get_uint_from_match_info (match_info, 2, out_srv_domain); mm_get_uint_from_match_info (match_info, 3, out_roam_status); mm_get_uint_from_match_info (match_info, 4, out_sys_mode); mm_get_uint_from_match_info (match_info, 5, out_sim_state); /* Remember that g_match_info_get_match_count() includes match #0 */ if (g_match_info_get_match_count (match_info) >= 8) { *out_sys_submode_valid = TRUE; mm_get_uint_from_match_info (match_info, 7, out_sys_submode); } } return matched; } /*****************************************************************************/ /* ^SYSINFOEX response parser */ gboolean mm_huawei_parse_sysinfoex_response (const char *reply, guint *out_srv_status, guint *out_srv_domain, guint *out_roam_status, guint *out_sim_state, guint *out_sys_mode, guint *out_sys_submode, GError **error) { g_autoptr(GRegex) r = NULL; g_autoptr(GMatchInfo) match_info = NULL; gboolean matched; GError *match_error = NULL; g_assert (out_srv_status != NULL); g_assert (out_srv_domain != NULL); g_assert (out_roam_status != NULL); g_assert (out_sim_state != NULL); g_assert (out_sys_mode != NULL); g_assert (out_sys_submode != NULL); /* Format: * * ^SYSINFOEX: ,,,,,,,, * * and may not be quoted on some Huawei modems (e.g. E303). */ /* ^SYSINFOEX:2,3,0,1,,3,"WCDMA",41,"HSPA+" */ r = g_regex_new ("\\^SYSINFOEX:\\s*(\\d+),(\\d+),(\\d+),(\\d+),?(\\d*),(\\d+),\"?([^\"]*)\"?,(\\d+),\"?([^\"]*)\"?$", 0, 0, NULL); g_assert (r != NULL); matched = g_regex_match_full (r, reply, -1, 0, 0, &match_info, &match_error); if (!matched) { if (match_error) { g_propagate_error (error, match_error); g_prefix_error (error, "Could not parse ^SYSINFOEX results: "); } else { g_set_error_literal (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't match ^SYSINFOEX reply"); } } else { mm_get_uint_from_match_info (match_info, 1, out_srv_status); mm_get_uint_from_match_info (match_info, 2, out_srv_domain); mm_get_uint_from_match_info (match_info, 3, out_roam_status); mm_get_uint_from_match_info (match_info, 4, out_sim_state); /* We just ignore the sysmode and submode name strings */ mm_get_uint_from_match_info (match_info, 6, out_sys_mode); mm_get_uint_from_match_info (match_info, 8, out_sys_submode); } return matched; } /*****************************************************************************/ /* ^PREFMODE test parser * * AT^PREFMODE=? * ^PREFMODE:(2,4,8) */ static gboolean mode_from_prefmode (guint huawei_mode, MMModemMode *modem_mode, GError **error) { g_assert (modem_mode != NULL); *modem_mode = MM_MODEM_MODE_NONE; switch (huawei_mode) { case 2: *modem_mode = MM_MODEM_MODE_2G; break; case 4: *modem_mode = MM_MODEM_MODE_3G; break; case 8: *modem_mode = (MM_MODEM_MODE_2G | MM_MODEM_MODE_3G); break; default: g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "No translation from huawei prefmode '%u' to mode", huawei_mode); } return *modem_mode != MM_MODEM_MODE_NONE ? TRUE : FALSE; } GArray * mm_huawei_parse_prefmode_test (const gchar *response, gpointer log_object, GError **error) { gchar **split; guint i; MMModemMode all = MM_MODEM_MODE_NONE; GArray *out; response = mm_strip_tag (response, "^PREFMODE:"); split = g_strsplit_set (response, " (,)\r\n", -1); if (!split) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Unexpected ^PREFMODE format output"); return NULL; } out = g_array_sized_new (FALSE, FALSE, sizeof (MMHuaweiPrefmodeCombination), 3); for (i = 0; split[i]; i++) { guint val; MMModemMode preferred = MM_MODEM_MODE_NONE; GError *inner_error = NULL; MMHuaweiPrefmodeCombination combination; if (split[i][0] == '\0') continue; if (!mm_get_uint_from_str (split[i], &val)) { mm_obj_dbg (log_object, "error parsing ^PREFMODE value '%s'", split[i]); continue; } if (!mode_from_prefmode (val, &preferred, &inner_error)) { mm_obj_dbg (log_object, "unhandled ^PREFMODE value: %s", inner_error->message); g_error_free (inner_error); continue; } combination.prefmode = val; combination.allowed = MM_MODEM_MODE_NONE; /* reset it later */ combination.preferred = preferred; all |= preferred; g_array_append_val (out, combination); } g_strfreev (split); /* No value */ if (out->len == 0) { g_array_unref (out); g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "^PREFMODE response contains no valid values"); return NULL; } /* Single value listed; PREFERRED=NONE... */ if (out->len == 1) { MMHuaweiPrefmodeCombination *combination; combination = &g_array_index (out, MMHuaweiPrefmodeCombination, 0); combination->allowed = all; combination->preferred = MM_MODEM_MODE_NONE; } else { /* Multiple values, reset ALLOWED */ for (i = 0; i < out->len; i++) { MMHuaweiPrefmodeCombination *combination; combination = &g_array_index (out, MMHuaweiPrefmodeCombination, i); combination->allowed = all; if (combination->preferred == all) combination->preferred = MM_MODEM_MODE_NONE; } } return out; } /*****************************************************************************/ /* ^PREFMODE response parser */ const MMHuaweiPrefmodeCombination * mm_huawei_parse_prefmode_response (const gchar *response, const GArray *supported_mode_combinations, GError **error) { guint mode; guint i; /* Format: * * ^PREFMODE: */ response = mm_strip_tag (response, "^PREFMODE:"); if (!mm_get_uint_from_str (response, &mode)) { /* Dump error to upper layer */ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Unexpected PREFMODE response: '%s'", response); return NULL; } /* Look for current modes among the supported ones */ for (i = 0; i < supported_mode_combinations->len; i++) { const MMHuaweiPrefmodeCombination *combination; combination = &g_array_index (supported_mode_combinations, MMHuaweiPrefmodeCombination, i); if (mode == combination->prefmode) return combination; } g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "No PREFMODE combination found matching the current one (%d)", mode); return NULL; } /*****************************************************************************/ /* ^SYSCFG test parser */ static gchar ** split_groups (const gchar *str, GError **error) { const gchar *p = str; GPtrArray *out; guint groups = 0; /* * Split string: (a),((b1),(b2)),,(d),((e1),(e2)) * Into: * - a * - (b1),(b2) * - * - d * - (e1),(e2) */ out = g_ptr_array_new_with_free_func (g_free); while (TRUE) { const gchar *start; guint inner_groups; /* Skip whitespaces */ while (*p == ' ' || *p == '\r' || *p == '\n') p++; /* We're done, return */ if (*p == '\0') { g_ptr_array_set_size (out, out->len + 1); return (gchar **) g_ptr_array_free (out, FALSE); } /* Group separators */ if (groups > 0) { if (*p != ',') { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Unexpected group separator"); g_ptr_array_unref (out); return NULL; } p++; } /* Skip whitespaces */ while (*p == ' ' || *p == '\r' || *p == '\n') p++; /* New group */ groups++; /* Empty group? */ if (*p == ',' || *p == '\0') { g_ptr_array_add (out, g_strdup ("")); continue; } /* No group start? */ if (*p != '(') { /* Error */ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Expected '(' not found"); g_ptr_array_unref (out); return NULL; } p++; inner_groups = 0; start = p; while (TRUE) { if (*p == '(') { inner_groups++; p++; continue; } if (*p == '\0') { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Early end of string found, unfinished group"); g_ptr_array_unref (out); return NULL; } if (*p == ')') { gchar *group; if (inner_groups > 0) { inner_groups--; p++; continue; } group = g_strndup (start, p - start); g_ptr_array_add (out, group); p++; break; } /* keep on */ p++; } } g_assert_not_reached (); } static gboolean mode_from_syscfg (guint huawei_mode, MMModemMode *modem_mode, GError **error) { g_assert (modem_mode != NULL); *modem_mode = MM_MODEM_MODE_NONE; switch (huawei_mode) { case 2: *modem_mode = MM_MODEM_MODE_2G | MM_MODEM_MODE_3G; break; case 13: *modem_mode = MM_MODEM_MODE_2G; break; case 14: *modem_mode = MM_MODEM_MODE_3G; break; case 16: /* ignore */ break; default: g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "No translation from huawei prefmode '%u' to mode", huawei_mode); } return *modem_mode != MM_MODEM_MODE_NONE ? TRUE : FALSE; } static GArray * parse_syscfg_modes (const gchar *modes_str, const gchar *acqorder_str, gpointer log_object, GError **error) { GArray *out; gchar **split; guint i; gint min_acqorder = 0; gint max_acqorder = 0; /* Start parsing acquisition order */ if (!sscanf (acqorder_str, "%d-%d", &min_acqorder, &max_acqorder)) mm_obj_dbg (log_object, "error parsing ^SYSCFG acquisition order range '%s'", acqorder_str); /* Just in case, we default to supporting only auto */ if (max_acqorder < min_acqorder) { min_acqorder = 0; max_acqorder = 0; } /* Now parse modes */ split = g_strsplit (modes_str, ",", -1); out = g_array_sized_new (FALSE, FALSE, sizeof (MMHuaweiSyscfgCombination), g_strv_length (split)); for (i = 0; split[i]; i++) { guint val; guint allowed = MM_MODEM_MODE_NONE; GError *inner_error = NULL; MMHuaweiSyscfgCombination combination; if (!mm_get_uint_from_str (mm_strip_quotes (split[i]), &val)) { mm_obj_dbg (log_object, "error parsing ^SYSCFG mode value: %s", split[i]); continue; } if (!mode_from_syscfg (val, &allowed, &inner_error)) { if (inner_error) { mm_obj_dbg (log_object, "unhandled ^SYSCFG: %s", inner_error->message); g_error_free (inner_error); } continue; } switch (allowed) { case MM_MODEM_MODE_2G: case MM_MODEM_MODE_3G: /* single mode */ combination.allowed = allowed; combination.preferred = MM_MODEM_MODE_NONE; combination.mode = val; combination.acqorder = 0; g_array_append_val (out, combination); break; case (MM_MODEM_MODE_2G | MM_MODEM_MODE_3G): /* 2G and 3G; auto */ combination.allowed = allowed; combination.mode = val; if (min_acqorder == 0) { combination.preferred = MM_MODEM_MODE_NONE; combination.acqorder = 0; g_array_append_val (out, combination); } /* 2G and 3G; 2G preferred */ if (min_acqorder <= 1 && max_acqorder >= 1) { combination.preferred = MM_MODEM_MODE_2G; combination.acqorder = 1; g_array_append_val (out, combination); } /* 2G and 3G; 3G preferred */ if (min_acqorder <= 2 && max_acqorder >= 2) { combination.preferred = MM_MODEM_MODE_3G; combination.acqorder = 2; g_array_append_val (out, combination); } break; default: g_assert_not_reached (); } } g_strfreev (split); /* If we didn't build a valid array of combinations, return an error */ if (out->len == 0) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Cannot parse list of allowed mode combinations: '%s,%s'", modes_str, acqorder_str); g_array_unref (out); return NULL; } return out; } GArray * mm_huawei_parse_syscfg_test (const gchar *response, gpointer log_object, GError **error) { gchar **split; GError *inner_error = NULL; GArray *out; if (!response || !g_str_has_prefix (response, "^SYSCFG:")) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Missing ^SYSCFG prefix"); return NULL; } /* Examples: * * ^SYSCFG:(2,13,14,16), * (0-3), * ((400000,"WCDMA2100")), * (0-2), * (0-4) */ split = split_groups (mm_strip_tag (response, "^SYSCFG:"), error); if (!split) return NULL; /* We expect 5 string chunks */ if (g_strv_length (split) < 5) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Unexpected ^SYSCFG format"); g_strfreev (split); return FALSE; } /* Parse supported mode combinations */ out = parse_syscfg_modes (split[0], split[1], log_object, &inner_error); g_strfreev (split); if (inner_error) { g_propagate_error (error, inner_error); return NULL; } return out; } /*****************************************************************************/ /* ^SYSCFG response parser */ const MMHuaweiSyscfgCombination * mm_huawei_parse_syscfg_response (const gchar *response, const GArray *supported_mode_combinations, GError **error) { gchar **split; guint mode; guint acqorder; guint i; if (!response || !g_str_has_prefix (response, "^SYSCFG:")) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Missing ^SYSCFG prefix"); return NULL; } /* Format: * * ^SYSCFG: ,,,, */ response = mm_strip_tag (response, "^SYSCFG:"); split = g_strsplit (response, ",", -1); /* We expect 5 string chunks */ if (g_strv_length (split) < 5 || !mm_get_uint_from_str (split[0], &mode) || !mm_get_uint_from_str (split[1], &acqorder)) { /* Dump error to upper layer */ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Unexpected ^SYSCFG response: '%s'", response); g_strfreev (split); return NULL; } /* Fix invalid modes with non-sensical acquisition orders */ if (mode == 14 && acqorder != 0) /* WCDMA only but acqorder != "Automatic" */ acqorder = 0; else if (mode == 13 && acqorder != 0) /* GSM only but acqorder != "Automatic" */ acqorder = 0; /* Look for current modes among the supported ones */ for (i = 0; i < supported_mode_combinations->len; i++) { const MMHuaweiSyscfgCombination *combination; combination = &g_array_index (supported_mode_combinations, MMHuaweiSyscfgCombination, i); if (mode == combination->mode && acqorder == combination->acqorder) { g_strfreev (split); return combination; } } g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "No SYSCFG combination found matching the current one (%d,%d)", mode, acqorder); g_strfreev (split); return NULL; } /*****************************************************************************/ /* ^SYSCFGEX test parser */ static void huawei_syscfgex_combination_free (MMHuaweiSyscfgexCombination *item) { /* Just the contents, not the item itself! */ g_free (item->mode_str); } static gboolean parse_mode_combination_string (const gchar *mode_str, MMModemMode *allowed, MMModemMode *preferred) { guint n; if (g_str_equal (mode_str, "00")) { *allowed = MM_MODEM_MODE_ANY; *preferred = MM_MODEM_MODE_NONE; return TRUE; } *allowed = MM_MODEM_MODE_NONE; *preferred = MM_MODEM_MODE_NONE; for (n = 0; n < strlen (mode_str); n+=2) { MMModemMode mode; if (g_ascii_strncasecmp (&mode_str[n], "01", 2) == 0) /* GSM */ mode = MM_MODEM_MODE_2G; else if (g_ascii_strncasecmp (&mode_str[n], "02", 2) == 0) /* WCDMA */ mode = MM_MODEM_MODE_3G; else if (g_ascii_strncasecmp (&mode_str[n], "03", 2) == 0) /* LTE */ mode = MM_MODEM_MODE_4G; else if (g_ascii_strncasecmp (&mode_str[n], "04", 2) == 0) /* CDMA Note: no EV-DO, just return single value, so assume CDMA1x*/ mode = MM_MODEM_MODE_2G; else mode = MM_MODEM_MODE_NONE; if (mode != MM_MODEM_MODE_NONE) { /* The first one in the list is the preferred combination */ if (n == 0) *preferred |= mode; *allowed |= mode; } } switch (mm_count_bits_set (*allowed)) { case 0: /* No allowed, error */ return FALSE; case 1: /* If only one mode allowed, NONE preferred */ *preferred = MM_MODEM_MODE_NONE; /* fall through */ default: return TRUE; } } static GArray * parse_mode_combination_string_list (const gchar *modes_str, GError **error) { GArray *supported_mode_combinations; gchar **mode_combinations; MMModemMode all = MM_MODEM_MODE_NONE; gboolean has_all = FALSE; guint i; mode_combinations = g_strsplit (modes_str, ",", -1); supported_mode_combinations = g_array_sized_new (FALSE, FALSE, sizeof (MMHuaweiSyscfgexCombination), g_strv_length (mode_combinations)); g_array_set_clear_func (supported_mode_combinations, (GDestroyNotify)huawei_syscfgex_combination_free); for (i = 0; mode_combinations[i]; i++) { MMHuaweiSyscfgexCombination combination; mode_combinations[i] = mm_strip_quotes (mode_combinations[i]); if (!parse_mode_combination_string (mode_combinations[i], &combination.allowed, &combination.preferred)) continue; if (combination.allowed != MM_MODEM_MODE_ANY) { combination.mode_str = g_strdup (mode_combinations[i]); g_array_append_val (supported_mode_combinations, combination); all |= combination.allowed; } else { /* don't add the all_combination here, we may have more * combinations in the loop afterwards */ has_all = TRUE; } } g_strfreev (mode_combinations); /* Add here the all_combination */ if (has_all) { MMHuaweiSyscfgexCombination combination; combination.allowed = all; combination.preferred = MM_MODEM_MODE_NONE; combination.mode_str = g_strdup ("00"); g_array_append_val (supported_mode_combinations, combination); } /* If we didn't build a valid array of combinations, return an error */ if (supported_mode_combinations->len == 0) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Cannot parse list of allowed mode combinations: '%s'", modes_str); g_array_unref (supported_mode_combinations); return NULL; } return supported_mode_combinations; } GArray * mm_huawei_parse_syscfgex_test (const gchar *response, GError **error) { gchar **split; GError *inner_error = NULL; GArray *out; if (!response || !g_str_has_prefix (response, "^SYSCFGEX:")) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Missing ^SYSCFGEX prefix"); return NULL; } /* Examples: * * ^SYSCFGEX: ("00","03","02","01","99"), * ((2000004e80380,"GSM850/GSM900/GSM1800/GSM1900/WCDMA850/WCDMA900/WCDMA1900/WCDMA2100"), * (3fffffff,"All Bands")), * (0-3), * (0-4), * ((800c5,"LTE2100/LTE1800/LTE2600/LTE900/LTE800"), * (7fffffffffffffff,"All bands")) */ split = split_groups (mm_strip_tag (response, "^SYSCFGEX:"), error); if (!split) return NULL; /* We expect 5 string chunks */ if (g_strv_length (split) < 5) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Unexpected ^SYSCFGEX format"); g_strfreev (split); return NULL; } out = parse_mode_combination_string_list (split[0], &inner_error); g_strfreev (split); if (inner_error) { g_propagate_error (error, inner_error); return NULL; } return out; } /*****************************************************************************/ /* ^SYSCFGEX response parser */ const MMHuaweiSyscfgexCombination * mm_huawei_parse_syscfgex_response (const gchar *response, const GArray *supported_mode_combinations, GError **error) { gchar **split; guint i; gsize len; gchar *str; if (!response || !g_str_has_prefix (response, "^SYSCFGEX:")) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Missing ^SYSCFGEX prefix"); return NULL; } /* Format: * * ^SYSCFGEX: "00",3FFFFFFF,1,2,7FFFFFFFFFFFFFFF * ^SYSCFGEX: ,,,, */ response = mm_strip_tag (response, "^SYSCFGEX:"); split = g_strsplit (response, ",", -1); /* We expect 5 string chunks */ if (g_strv_length (split) < 5) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Unexpected ^SYSCFGEX response format"); g_strfreev (split); return NULL; } /* Unquote */ str = split[0]; len = strlen (str); if ((len >= 2) && (str[0] == '"') && (str[len - 1] == '"')) { str[0] = ' '; str[len - 1] = ' '; str = g_strstrip (str); } /* Look for current modes among the supported ones */ for (i = 0; i < supported_mode_combinations->len; i++) { const MMHuaweiSyscfgexCombination *combination; combination = &g_array_index (supported_mode_combinations, MMHuaweiSyscfgexCombination, i); if (g_str_equal (str, combination->mode_str)) { g_strfreev (split); return combination; } } g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "No SYSCFGEX combination found matching the current one (%s)", str); g_strfreev (split); return NULL; } /*****************************************************************************/ /* ^NWTIME response parser */ gboolean mm_huawei_parse_nwtime_response (const gchar *response, gchar **iso8601p, MMNetworkTimezone **tzp, GError **error) { g_autoptr(GRegex) r = NULL; g_autoptr(GMatchInfo) match_info = NULL; GError *match_error = NULL; guint year = 0; guint month = 0; guint day = 0; guint hour = 0; guint minute = 0; guint second = 0; guint dt = 0; gint tz = 0; g_assert (iso8601p || tzp); /* at least one */ r = g_regex_new ("\\^NWTIME:\\s*(\\d+)/(\\d+)/(\\d+),(\\d+):(\\d+):(\\d*)([\\-\\+\\d]+),(\\d+)$", 0, 0, NULL); g_assert (r != NULL); if (!g_regex_match_full (r, response, -1, 0, 0, &match_info, &match_error)) { if (match_error) { g_propagate_error (error, match_error); g_prefix_error (error, "Could not parse ^NWTIME results: "); } else { g_set_error_literal (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't match ^NWTIME reply"); } return FALSE; } /* Remember that g_match_info_get_match_count() includes match #0 */ g_assert (g_match_info_get_match_count (match_info) >= 9); if (mm_get_uint_from_match_info (match_info, 1, &year) && mm_get_uint_from_match_info (match_info, 2, &month) && mm_get_uint_from_match_info (match_info, 3, &day) && mm_get_uint_from_match_info (match_info, 4, &hour) && mm_get_uint_from_match_info (match_info, 5, &minute) && mm_get_uint_from_match_info (match_info, 6, &second) && mm_get_int_from_match_info (match_info, 7, &tz) && mm_get_uint_from_match_info (match_info, 8, &dt)) { /* adjust year */ if (year < 100) year += 2000; /* * tz = timezone offset in 15 minute intervals * dt = daylight adjustment, 0 = none, 1 = 1 hour, 2 = 2 hours * other values are marked reserved. */ if (tzp) { *tzp = mm_network_timezone_new (); mm_network_timezone_set_offset (*tzp, tz * 15); mm_network_timezone_set_dst_offset (*tzp, dt * 60); } if (iso8601p) { /* Return ISO-8601 format date/time string */ *iso8601p = mm_new_iso8601_time (year, month, day, hour, minute, second, TRUE, (tz * 15), error); return (*iso8601p != NULL); } return TRUE; } g_set_error_literal (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Failed to parse ^NWTIME reply"); return FALSE; } /*****************************************************************************/ /* ^TIME response parser */ gboolean mm_huawei_parse_time_response (const gchar *response, gchar **iso8601p, MMNetworkTimezone **tzp, GError **error) { g_autoptr(GRegex) r = NULL; g_autoptr(GMatchInfo) match_info = NULL; GError *match_error = NULL; guint year = 0; guint month = 0; guint day = 0; guint hour = 0; guint minute = 0; guint second = 0; g_assert (iso8601p || tzp); /* at least one */ /* TIME response cannot ever provide TZ info */ if (tzp) { g_set_error_literal (error, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "^TIME does not provide timezone information"); return FALSE; } /* Already in ISO-8601 format, but verify just to be sure */ r = g_regex_new ("\\^TIME:\\s*(\\d+)/(\\d+)/(\\d+)\\s*(\\d+):(\\d+):(\\d*)$", 0, 0, NULL); g_assert (r != NULL); if (!g_regex_match_full (r, response, -1, 0, 0, &match_info, &match_error)) { if (match_error) { g_propagate_error (error, match_error); g_prefix_error (error, "Could not parse ^TIME results: "); } else { g_set_error_literal (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't match ^TIME reply"); } return FALSE; } /* Remember that g_match_info_get_match_count() includes match #0 */ g_assert (g_match_info_get_match_count (match_info) >= 7); if (mm_get_uint_from_match_info (match_info, 1, &year) && mm_get_uint_from_match_info (match_info, 2, &month) && mm_get_uint_from_match_info (match_info, 3, &day) && mm_get_uint_from_match_info (match_info, 4, &hour) && mm_get_uint_from_match_info (match_info, 5, &minute) && mm_get_uint_from_match_info (match_info, 6, &second)) { /* adjust year */ if (year < 100) year += 2000; /* Return ISO-8601 format date/time string */ if (iso8601p) { *iso8601p = mm_new_iso8601_time (year, month, day, hour, minute, second, FALSE, 0, error); return (*iso8601p != NULL); } return TRUE; } g_set_error_literal (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Failed to parse ^TIME reply"); return FALSE; } /*****************************************************************************/ /* ^HCSQ response parser */ gboolean mm_huawei_parse_hcsq_response (const gchar *response, MMModemAccessTechnology *out_act, guint *out_value1, guint *out_value2, guint *out_value3, guint *out_value4, guint *out_value5, GError **error) { g_autoptr(GRegex) r = NULL; g_autoptr(GMatchInfo) match_info = NULL; GError *match_error = NULL; r = g_regex_new ("\\^HCSQ:\\s*\"?([a-zA-Z]*)\"?,(\\d+),?(\\d+)?,?(\\d+)?,?(\\d+)?,?(\\d+)?$", 0, 0, NULL); g_assert (r != NULL); if (!g_regex_match_full (r, response, -1, 0, 0, &match_info, &match_error)) { if (match_error) { g_propagate_error (error, match_error); g_prefix_error (error, "Could not parse ^HCSQ results: "); } else { g_set_error_literal (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't match ^HCSQ reply"); } return FALSE; } /* Remember that g_match_info_get_match_count() includes match #0 */ if (g_match_info_get_match_count (match_info) < 3) { g_set_error_literal (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Not enough elements in ^HCSQ reply"); return FALSE; } if (out_act) { g_autofree gchar *s = NULL; s = g_match_info_fetch (match_info, 1); *out_act = mm_string_to_access_tech (s); } if (out_value1) mm_get_uint_from_match_info (match_info, 2, out_value1); if (out_value2) mm_get_uint_from_match_info (match_info, 3, out_value2); if (out_value3) mm_get_uint_from_match_info (match_info, 4, out_value3); if (out_value4) mm_get_uint_from_match_info (match_info, 5, out_value4); if (out_value5) mm_get_uint_from_match_info (match_info, 6, out_value5); return TRUE; } /*****************************************************************************/ /* ^CVOICE response parser */ gboolean mm_huawei_parse_cvoice_response (const gchar *response, guint *out_hz, guint *out_bits, GError **error) { g_autoptr(GRegex) r = NULL; g_autoptr(GMatchInfo) match_info = NULL; GError *match_error = NULL; guint supported = 0; guint hz = 0; guint bits = 0; /* ^CVOICE: <0=supported,1=unsupported>,,, */ r = g_regex_new ("\\^CVOICE:\\s*(\\d)\\s*,\\s*(\\d+)\\s*,\\s*(\\d+)\\s*,\\s*(\\d+)$", 0, 0, NULL); g_assert (r != NULL); if (!g_regex_match_full (r, response, -1, 0, 0, &match_info, &match_error)) { if (match_error) { g_propagate_error (error, match_error); g_prefix_error (error, "Could not parse ^CVOICE results: "); } else { g_set_error_literal (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't match ^CVOICE reply"); } return FALSE; } /* Remember that g_match_info_get_match_count() includes match #0 */ g_assert (g_match_info_get_match_count (match_info) >= 5); if (mm_get_uint_from_match_info (match_info, 1, &supported) && mm_get_uint_from_match_info (match_info, 2, &hz) && mm_get_uint_from_match_info (match_info, 3, &bits)) { if (supported == 0) { if (out_hz) *out_hz = hz; if (out_bits) *out_bits = bits; return TRUE; } g_set_error_literal (error, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "^CVOICE not supported by this device"); return FALSE; } g_set_error_literal (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Failed to parse ^CVOICE reply"); return FALSE; } /*****************************************************************************/ /* ^GETPORTMODE response parser */ #define GETPORTMODE_PREFIX "^GETPORTMODE:" GArray * mm_huawei_parse_getportmode_response (const gchar *response, gpointer log_object, GError **error) { g_autoptr(GArray) modes = NULL; g_auto(GStrv) split = NULL; guint i; gint n_items; split = g_strsplit (response, ",", -1); n_items = g_strv_length (split) - 1; if (n_items < 1) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Unexpected number of items in response"); return NULL; } /* validate response prefix */ if (g_ascii_strncasecmp (split[0], GETPORTMODE_PREFIX, strlen (GETPORTMODE_PREFIX)) != 0) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Unexpected response prefix"); return NULL; } mm_obj_dbg (log_object, "processing ^GETPORTMODE response..."); modes = g_array_sized_new (FALSE, FALSE, sizeof (MMHuaweiPortMode), n_items); /* iterate all port items found */ for (i = 1; split[i]; i++) { MMHuaweiPortMode mode = MM_HUAWEI_PORT_MODE_NONE; gchar *separator; guint port_number; separator = strchr (split[i], ':'); if (!separator) continue; /* the reported port number may start either by 0 or by 1; the important * thing is therefore no the number itself, only that it's a number */ g_strstrip (&separator[1]); if (!mm_get_uint_from_str (&separator[1], &port_number)) { mm_obj_warn (log_object, " couldn't parse port number: %s", split[i]); break; } *separator = '\0'; g_strstrip (split[i]); if (g_ascii_strcasecmp (split[i], "pcui") == 0) mode = MM_HUAWEI_PORT_MODE_PCUI; else if ((g_ascii_strcasecmp (split[i], "mdm") == 0) || (g_ascii_strcasecmp (split[i], "modem") == 0) || (g_ascii_strcasecmp (split[i], "3g_modem") == 0)) mode = MM_HUAWEI_PORT_MODE_MODEM; else if ((g_ascii_strcasecmp (split[i], "diag") == 0) || (g_ascii_strcasecmp (split[i], "3g_diag") == 0) || (g_ascii_strcasecmp (split[i], "4g_diag") == 0)) mode = MM_HUAWEI_PORT_MODE_DIAG; else if (g_ascii_strcasecmp (split[i], "gps") == 0) mode = MM_HUAWEI_PORT_MODE_GPS; else if ((g_ascii_strcasecmp (split[i], "ndis") == 0) || (g_ascii_strcasecmp (split[i], "rndis") == 0) || (g_ascii_strcasecmp (split[i], "ncm") == 0) || (g_ascii_strcasecmp (split[i], "ecm") == 0)) mode = MM_HUAWEI_PORT_MODE_NET; else if (g_ascii_strcasecmp (split[i], "cdrom") == 0) mode = MM_HUAWEI_PORT_MODE_CDROM; else if ((g_ascii_strcasecmp (split[i], "sd") == 0) || (g_ascii_strncasecmp (split[i], "mass", 4) == 0)) mode = MM_HUAWEI_PORT_MODE_SD; else if (g_ascii_strcasecmp (split[i], "bt") == 0) mode = MM_HUAWEI_PORT_MODE_BT; else if ((g_ascii_strcasecmp (split[i], "a_shell") == 0) || (g_ascii_strcasecmp (split[i], "c_shell") == 0)) mode = MM_HUAWEI_PORT_MODE_SHELL; mm_obj_dbg (log_object, " port mode %s reported at port number %u", mm_huawei_port_mode_get_string (mode), port_number); g_array_append_val (modes, mode); } if (!modes->len) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "No port modes loaded"); return NULL; } return g_steal_pointer (&modes); } ModemManager-1.23.4-dev/src/plugins/huawei/mm-modem-helpers-huawei.h000066400000000000000000000205511456466623000252630ustar00rootroot00000000000000/* -*- 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) 2013 Huawei Technologies Co., Ltd * Copyright (C) 2013 Aleksander Morgado */ #ifndef MM_MODEM_HELPERS_HUAWEI_H #define MM_MODEM_HELPERS_HUAWEI_H #include #define _LIBMM_INSIDE_MM #include /*****************************************************************************/ /* ^NDISSTAT / ^NDISSTATQRY response parser */ gboolean mm_huawei_parse_ndisstatqry_response (const gchar *response, gboolean *ipv4_available, gboolean *ipv4_connected, gboolean *ipv6_available, gboolean *ipv6_connected, GError **error); /*****************************************************************************/ /* ^DHCP response parser */ gboolean mm_huawei_parse_dhcp_response (const char *reply, guint *out_address, guint *out_prefix, guint *out_gateway, guint *out_dns1, guint *out_dns2, GError **error); /*****************************************************************************/ /* ^SYSINFO response parser */ gboolean mm_huawei_parse_sysinfo_response (const char *reply, guint *out_srv_status, guint *out_srv_domain, guint *out_roam_status, guint *out_sys_mode, guint *out_sim_state, gboolean *out_sys_submode_valid, guint *out_sys_submode, GError **error); /*****************************************************************************/ /* ^SYSINFOEX response parser */ gboolean mm_huawei_parse_sysinfoex_response (const char *reply, guint *out_srv_status, guint *out_srv_domain, guint *out_roam_status, guint *out_sim_state, guint *out_sys_mode, guint *out_sys_submode, GError **error); /*****************************************************************************/ /* ^PREFMODE test parser */ typedef struct { guint prefmode; MMModemMode allowed; MMModemMode preferred; } MMHuaweiPrefmodeCombination; GArray *mm_huawei_parse_prefmode_test (const gchar *response, gpointer log_object, GError **error); /*****************************************************************************/ /* ^PREFMODE response parser */ const MMHuaweiPrefmodeCombination *mm_huawei_parse_prefmode_response (const gchar *response, const GArray *supported_mode_combinations, GError **error); /*****************************************************************************/ /* ^SYSCFG test parser */ /* This is the default string we use as fallback when the modem gives * an empty response to AT^SYSCFG=? */ #define MM_HUAWEI_DEFAULT_SYSCFG_FMT "^SYSCFG:(2,13,14,16),(0-3),,," typedef struct { guint mode; guint acqorder; MMModemMode allowed; MMModemMode preferred; } MMHuaweiSyscfgCombination; GArray *mm_huawei_parse_syscfg_test (const gchar *response, gpointer log_object, GError **error); /*****************************************************************************/ /* ^SYSCFG response parser */ const MMHuaweiSyscfgCombination *mm_huawei_parse_syscfg_response (const gchar *response, const GArray *supported_mode_combinations, GError **error); /*****************************************************************************/ /* ^SYSCFGEX test parser */ typedef struct { gchar *mode_str; MMModemMode allowed; MMModemMode preferred; } MMHuaweiSyscfgexCombination; GArray *mm_huawei_parse_syscfgex_test (const gchar *response, GError **error); /*****************************************************************************/ /* ^SYSCFGEX response parser */ const MMHuaweiSyscfgexCombination *mm_huawei_parse_syscfgex_response (const gchar *response, const GArray *supported_mode_combinations, GError **error); /*****************************************************************************/ /* ^NWTIME response parser */ gboolean mm_huawei_parse_nwtime_response (const gchar *response, gchar **iso8601p, MMNetworkTimezone **tzp, GError **error); /*****************************************************************************/ /* ^TIME response parser */ gboolean mm_huawei_parse_time_response (const gchar *response, gchar **iso8601p, MMNetworkTimezone **tzp, GError **error); /*****************************************************************************/ /* ^HCSQ response parser */ gboolean mm_huawei_parse_hcsq_response (const gchar *response, MMModemAccessTechnology *out_act, guint *out_value1, guint *out_value2, guint *out_value3, guint *out_value4, guint *out_value5, GError **error); /*****************************************************************************/ /* ^CVOICE response parser */ gboolean mm_huawei_parse_cvoice_response (const gchar *response, guint *hz, guint *bits, GError **error); /*****************************************************************************/ /* ^GETPORTMODE response parser */ typedef enum { /*< underscore_name=mm_huawei_port_mode >*/ MM_HUAWEI_PORT_MODE_NONE, MM_HUAWEI_PORT_MODE_PCUI, MM_HUAWEI_PORT_MODE_MODEM, MM_HUAWEI_PORT_MODE_DIAG, MM_HUAWEI_PORT_MODE_GPS, MM_HUAWEI_PORT_MODE_NET, MM_HUAWEI_PORT_MODE_CDROM, MM_HUAWEI_PORT_MODE_SD, MM_HUAWEI_PORT_MODE_BT, MM_HUAWEI_PORT_MODE_SHELL, } MMHuaweiPortMode; #define MM_HUAWEI_PORT_MODE_IS_SERIAL(mode) \ (mode == MM_HUAWEI_PORT_MODE_PCUI || \ mode == MM_HUAWEI_PORT_MODE_MODEM || \ mode == MM_HUAWEI_PORT_MODE_DIAG || \ mode == MM_HUAWEI_PORT_MODE_GPS || \ mode == MM_HUAWEI_PORT_MODE_SHELL) GArray *mm_huawei_parse_getportmode_response (const gchar *response, gpointer log_object, GError **error); #endif /* MM_MODEM_HELPERS_HUAWEI_H */ ModemManager-1.23.4-dev/src/plugins/huawei/mm-plugin-huawei.c000066400000000000000000000621101456466623000240100ustar00rootroot00000000000000/* -*- 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 - 2012 Red Hat, Inc. * Copyright (C) 2012 Aleksander Morgado */ #include #include #include #include #define _LIBMM_INSIDE_MM #include #include #include "mm-port-enums-types.h" #include "mm-log.h" #include "mm-plugin-common.h" #include "mm-broadband-modem-huawei.h" #include "mm-modem-helpers-huawei.h" #include "mm-huawei-enums-types.h" #if defined WITH_QMI #include "mm-broadband-modem-qmi.h" #endif #if defined WITH_MBIM #include "mm-broadband-modem-mbim.h" #endif #define MM_TYPE_PLUGIN_HUAWEI mm_plugin_huawei_get_type () MM_DEFINE_PLUGIN (HUAWEI, huawei, Huawei) /*****************************************************************************/ /* Custom init */ #define TAG_FIRST_INTERFACE_CONTEXT "first-interface-context" /* Maximum time to wait for the first interface 0 to appear and get probed. * If it doesn't appear in this time, we'll decide which will be considered the * first interface. */ #define MAX_WAIT_TIME 5 typedef struct { MMPortProbe *probe; gint first_usbif; guint timeout_id; gboolean custom_init_run; } FirstInterfaceContext; static void first_interface_context_free (FirstInterfaceContext *ctx) { if (ctx->timeout_id) g_source_remove (ctx->timeout_id); g_object_unref (ctx->probe); g_slice_free (FirstInterfaceContext, ctx); } #define TAG_GETPORTMODE_RESULT "getportmode-result" #define TAG_AT_PORT_FLAGS "at-port-flags" typedef struct { MMPortSerialAt *port; gboolean curc_done; guint curc_retries; gboolean getportmode_done; guint getportmode_retries; } HuaweiCustomInitContext; static void huawei_custom_init_context_free (HuaweiCustomInitContext *ctx) { g_object_unref (ctx->port); g_slice_free (HuaweiCustomInitContext, ctx); } static gboolean huawei_custom_init_finish (MMPortProbe *probe, GAsyncResult *result, GError **error) { return g_task_propagate_boolean (G_TASK (result), error); } static void huawei_custom_init_step (GTask *task); static void getportmode_ready (MMPortSerialAt *port, GAsyncResult *res, GTask *task) { MMDevice *device; MMPortProbe *probe; HuaweiCustomInitContext *ctx; g_autofree gchar *response = NULL; GArray *modes; g_autoptr(GError) error = NULL; probe = g_task_get_source_object (task); ctx = g_task_get_task_data (task); device = mm_port_probe_peek_device (probe); response = mm_port_serial_at_command_finish (port, res, &error); if (error) { mm_obj_dbg (probe, "couldn't get port mode: '%s'", error->message); /* If any error occurred that was not ERROR or COMMAND NOT SUPPORT then * retry the command. */ if (g_error_matches (error, MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_UNKNOWN)) ctx->getportmode_done = TRUE; huawei_custom_init_step (task); return; } /* Mark port as being AT already */ mm_port_probe_set_result_at (probe, TRUE); /* Flag as GETPORTMODE already done */ ctx->getportmode_done = TRUE; modes = mm_huawei_parse_getportmode_response (response, probe, &error); if (!modes) mm_obj_warn (probe, "failed to parse ^GETPORTMODE response: %s", error->message); else g_object_set_data_full (G_OBJECT (device), TAG_GETPORTMODE_RESULT, modes, (GDestroyNotify) g_array_unref); huawei_custom_init_step (task); } static void curc_ready (MMPortSerialAt *port, GAsyncResult *res, GTask *task) { MMPortProbe *probe; HuaweiCustomInitContext *ctx; g_autoptr(GError) error = NULL; probe = g_task_get_source_object (task); ctx = g_task_get_task_data (task); mm_port_serial_at_command_finish (port, res, &error); if (error) { /* Retry if we get a timeout error */ if (g_error_matches (error, MM_SERIAL_ERROR, MM_SERIAL_ERROR_RESPONSE_TIMEOUT)) goto out; mm_obj_dbg (probe, "couldn't turn off unsolicited messages in secondary ports: %s", error->message); } mm_obj_dbg (probe, "unsolicited messages in secondary ports turned off"); ctx->curc_done = TRUE; out: huawei_custom_init_step (task); } static void try_next_usbif (MMPortProbe *probe, MMDevice *device) { FirstInterfaceContext *fi_ctx; GList *l; gint closest; fi_ctx = g_object_get_data (G_OBJECT (device), TAG_FIRST_INTERFACE_CONTEXT); g_assert (fi_ctx != NULL); /* Look for the next closest one among the list of interfaces in the device, * and enable that one as being first */ closest = G_MAXINT; for (l = mm_device_peek_port_probe_list (device); l; l = g_list_next (l)) { MMPortProbe *iter = MM_PORT_PROBE (l->data); /* Only expect ttys for next probing attempt */ if (g_str_equal (mm_port_probe_get_port_subsys (iter), "tty")) { gint usbif; usbif = mm_kernel_device_get_interface_number (mm_port_probe_peek_port (iter)); if (usbif == fi_ctx->first_usbif) { /* This is the one we just probed, which wasn't yet removed, so just skip it */ } else if (usbif > fi_ctx->first_usbif && usbif < closest) { closest = usbif; } } } if (closest == G_MAXINT) { /* No more ttys to try! Just return something */ closest = 0; mm_obj_dbg (probe, "no more ports to run initial probing"); } else mm_obj_dbg (probe, "will try initial probing with interface '%d' instead", closest); fi_ctx->first_usbif = closest; } static void huawei_custom_init_step (GTask *task) { MMPortProbe *probe; HuaweiCustomInitContext *ctx; FirstInterfaceContext *fi_ctx; MMKernelDevice *port; probe = g_task_get_source_object (task); ctx = g_task_get_task_data (task); /* If cancelled, end */ if (g_task_return_error_if_cancelled (task)) { mm_obj_dbg (probe, "no need to keep on running custom init"); g_object_unref (task); return; } if (!ctx->curc_done) { if (ctx->curc_retries == 0) { /* All retries consumed, probably not an AT port */ mm_port_probe_set_result_at (probe, FALSE); /* Try with next */ try_next_usbif (probe, mm_port_probe_peek_device (probe)); g_task_return_boolean (task, TRUE); g_object_unref (task); return; } ctx->curc_retries--; /* Turn off unsolicited messages on secondary ports until needed */ mm_port_serial_at_command ( ctx->port, "AT^CURC=0", 3, FALSE, /* raw */ FALSE, /* allow_cached */ g_task_get_cancellable (task), (GAsyncReadyCallback)curc_ready, task); return; } /* Try to get a port map from the modem */ port = mm_port_probe_peek_port (probe); if (!ctx->getportmode_done && !mm_kernel_device_get_global_property_as_boolean (port, "ID_MM_HUAWEI_DISABLE_GETPORTMODE")) { if (ctx->getportmode_retries == 0) { g_task_return_boolean (task, TRUE); g_object_unref (task); return; } ctx->getportmode_retries--; mm_port_serial_at_command ( ctx->port, "AT^GETPORTMODE", 3, FALSE, /* raw */ FALSE, /* allow_cached */ g_task_get_cancellable (task), (GAsyncReadyCallback)getportmode_ready, task); return; } /* All done it seems */ fi_ctx = g_object_get_data (G_OBJECT (mm_port_probe_peek_device (probe)), TAG_FIRST_INTERFACE_CONTEXT); g_assert (fi_ctx != NULL); fi_ctx->custom_init_run = TRUE; g_task_return_boolean (task, TRUE); g_object_unref (task); } static gboolean first_interface_missing_timeout_cb (MMDevice *device) { FirstInterfaceContext *fi_ctx; fi_ctx = g_object_get_data (G_OBJECT (device), TAG_FIRST_INTERFACE_CONTEXT); g_assert (fi_ctx != NULL); try_next_usbif (fi_ctx->probe, device); /* Reload the timeout, just in case we end up not having the next interface to probe... * which is anyway very unlikely as we got it by looking at the real probe list, but anyway... */ return G_SOURCE_CONTINUE; } static void huawei_custom_init (MMPortProbe *probe, MMPortSerialAt *port, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { MMDevice *device; FirstInterfaceContext *fi_ctx; HuaweiCustomInitContext *ctx; GTask *task; device = mm_port_probe_peek_device (probe); /* The primary port (called the "modem" port in the Windows drivers) is * always USB interface 0, and we need to detect that interface first for * two reasons: (1) to disable unsolicited messages on other ports that * may fill up the buffer and crash the device, and (2) to attempt to get * the port layout for hints about what the secondary port is (called the * "pcui" port in Windows). Thus we probe USB interface 0 first and defer * probing other interfaces until we've got if0, at which point we allow * the other ports to be probed too. */ fi_ctx = g_object_get_data (G_OBJECT (device), TAG_FIRST_INTERFACE_CONTEXT); if (!fi_ctx) { /* This is the first time we ask for the context. Set it up. */ fi_ctx = g_slice_new0 (FirstInterfaceContext); fi_ctx->probe = g_object_ref (probe); g_object_set_data_full (G_OBJECT (device), TAG_FIRST_INTERFACE_CONTEXT, fi_ctx, (GDestroyNotify)first_interface_context_free); /* The timeout is controlled in the data set in 'device', and therefore * it should be safe to assume that the timeout will not get fired after * having disposed 'device' */ fi_ctx->timeout_id = g_timeout_add_seconds (MAX_WAIT_TIME, (GSourceFunc)first_interface_missing_timeout_cb, device); /* By default, we'll ask the Huawei plugin to start probing usbif 0 */ fi_ctx->first_usbif = 0; /* Custom init of the Huawei plugin is to be run only in the first * interface. We'll control here whether we did run it already or not. */ fi_ctx->custom_init_run = FALSE; } ctx = g_slice_new (HuaweiCustomInitContext); ctx->port = g_object_ref (port); ctx->curc_done = FALSE; ctx->curc_retries = 3; ctx->getportmode_done = FALSE; ctx->getportmode_retries = 3; task = g_task_new (probe, cancellable, callback, user_data); g_task_set_task_data (task, ctx, (GDestroyNotify)huawei_custom_init_context_free); /* Custom init only to be run in the first interface */ if (mm_kernel_device_get_interface_number (mm_port_probe_peek_port (probe)) != fi_ctx->first_usbif) { if (fi_ctx->custom_init_run) /* If custom init was run already, we can consider this as successfully run */ g_task_return_boolean (task, TRUE); else /* Otherwise, we'll need to defer the probing a bit more */ g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_RETRY, "Defer needed"); g_object_unref (task); return; } /* We can run custom init in the first interface! clear the timeout as it is no longer needed */ if (fi_ctx->timeout_id) { g_source_remove (fi_ctx->timeout_id); fi_ctx->timeout_id = 0; } huawei_custom_init_step (task); } /*****************************************************************************/ static gint probe_cmp_by_usbif (MMPortProbe *a, MMPortProbe *b) { return (mm_kernel_device_get_interface_number (mm_port_probe_peek_port (a)) - mm_kernel_device_get_interface_number (mm_port_probe_peek_port (b))); } static guint propagate_getportmode_hints (MMPlugin *self, GList *probes, gboolean *primary_flagged) { MMDevice *device; GArray *modes; GList *l; GList *tty_probes = NULL; guint n_ports_with_hints = 0; guint mode_i = 0; g_assert (probes != NULL); device = mm_port_probe_peek_device (MM_PORT_PROBE (probes->data)); modes = g_object_get_data (G_OBJECT (device), TAG_GETPORTMODE_RESULT); /* Nothing to do if GETPORTMODE is flagged as not supported */ if (!modes) return 0; /* Build a list of TTY port probes (AT and not-AT) sorted by interface number */ for (l = probes; l; l = g_list_next (l)) { MMPortProbe *probe; probe = MM_PORT_PROBE (l->data); if (g_str_equal (mm_port_probe_get_port_subsys (probe), "tty")) tty_probes = g_list_insert_sorted (tty_probes, probe, (GCompareFunc) probe_cmp_by_usbif); } /* Propagate the getportmode tags to the specific port probes */ for (l = tty_probes, mode_i = 0; l; l = g_list_next (l)) { MMPortProbe *probe; MMPortSerialAtFlag at_port_flags = MM_PORT_SERIAL_AT_FLAG_NONE; MMHuaweiPortMode port_mode; probe = MM_PORT_PROBE (l->data); /* Look for the next serial port mode applicable */ while (!MM_HUAWEI_PORT_MODE_IS_SERIAL (g_array_index (modes, MMHuaweiPortMode, mode_i)) && (mode_i < modes->len)) mode_i++; if (mode_i == modes->len) { mm_obj_dbg (probe, "missing port mode hint"); continue; } port_mode = g_array_index (modes, MMHuaweiPortMode, mode_i); if (!mm_port_probe_is_at (probe)) { mm_obj_dbg (probe, "port mode hint for non-AT port: %s", mm_huawei_port_mode_get_string (port_mode)); mode_i++; continue; } mm_obj_dbg (probe, "port mode hint for AT port: %s", mm_huawei_port_mode_get_string (port_mode)); if (port_mode == MM_HUAWEI_PORT_MODE_PCUI) at_port_flags = MM_PORT_SERIAL_AT_FLAG_PRIMARY; else if (port_mode == MM_HUAWEI_PORT_MODE_MODEM) at_port_flags = MM_PORT_SERIAL_AT_FLAG_PPP; if (at_port_flags != MM_PORT_SERIAL_AT_FLAG_NONE) { n_ports_with_hints++; g_object_set_data (G_OBJECT (probe), TAG_AT_PORT_FLAGS, GUINT_TO_POINTER (at_port_flags)); } mode_i++; } g_list_free (tty_probes); return n_ports_with_hints; } static guint propagate_description_hints (MMPlugin *self, GList *probes, gboolean *primary_flagged) { GList *l; guint n_ports_with_hints = 0; for (l = probes; l; l = g_list_next (l)) { MMPortProbe *probe; MMPortSerialAtFlag at_port_flags = MM_PORT_SERIAL_AT_FLAG_NONE; const gchar *description; g_autofree gchar *lower_description = NULL; probe = MM_PORT_PROBE (l->data); if (!mm_port_probe_is_at (probe)) continue; description = mm_kernel_device_get_interface_description (mm_port_probe_peek_port (probe)); if (!description) continue; mm_obj_dbg (probe, "%s interface description: %s", mm_port_probe_get_port_name (probe), description); lower_description = g_ascii_strdown (description, -1); if (strstr (lower_description, "modem")) at_port_flags = MM_PORT_SERIAL_AT_FLAG_PPP; else if (strstr (lower_description, "pcui")) { at_port_flags = MM_PORT_SERIAL_AT_FLAG_PRIMARY; *primary_flagged = TRUE; } if (at_port_flags != MM_PORT_SERIAL_AT_FLAG_NONE) { n_ports_with_hints++; g_object_set_data (G_OBJECT (probe), TAG_AT_PORT_FLAGS, GUINT_TO_POINTER (at_port_flags)); } } return n_ports_with_hints; } static guint propagate_generic_hints (MMPlugin *self, GList *probes, gboolean *primary_flagged) { GList *l; guint n_ports_with_hints = 0; for (l = probes; l; l = g_list_next (l)) { MMPortProbe *probe; MMKernelDevice *kernel_device; MMPortSerialAtFlag at_port_flags = MM_PORT_SERIAL_AT_FLAG_NONE; probe = MM_PORT_PROBE (l->data); if (!mm_port_probe_is_at (probe)) continue; kernel_device = mm_port_probe_peek_port (probe); if (mm_kernel_device_get_property_as_boolean (kernel_device, ID_MM_PORT_TYPE_AT_PRIMARY)) { at_port_flags = MM_PORT_SERIAL_AT_FLAG_PRIMARY; *primary_flagged = TRUE; } else if (mm_kernel_device_get_property_as_boolean (kernel_device, ID_MM_PORT_TYPE_AT_SECONDARY)) at_port_flags = MM_PORT_SERIAL_AT_FLAG_SECONDARY; else if (mm_kernel_device_get_property_as_boolean (kernel_device, ID_MM_PORT_TYPE_AT_PPP)) at_port_flags = MM_PORT_SERIAL_AT_FLAG_PPP; if (at_port_flags != MM_PORT_SERIAL_AT_FLAG_NONE) { n_ports_with_hints++; g_object_set_data (G_OBJECT (probe), TAG_AT_PORT_FLAGS, GUINT_TO_POINTER (at_port_flags)); } } return n_ports_with_hints; } static guint fallback_primary_cdcwdm (MMPlugin *self, GList *probes) { GList *l; for (l = probes; l; l = g_list_next (l)) { MMPortProbe *probe; probe = MM_PORT_PROBE (l->data); if (!mm_port_probe_is_at (probe)) continue; if (g_str_equal (mm_port_probe_get_port_subsys (probe), "usbmisc")) { mm_obj_dbg (self, "fallback port type hint applied to first cdc-wmd port found"); g_object_set_data (G_OBJECT (probe), TAG_AT_PORT_FLAGS, GUINT_TO_POINTER (MM_PORT_SERIAL_AT_FLAG_PRIMARY)); return 1; } } return 0; } static guint fallback_usbif0 (MMPlugin *self, GList *probes) { GList *l; for (l = probes; l; l = g_list_next (l)) { MMPortProbe *probe; guint usbif; probe = MM_PORT_PROBE (l->data); if (!mm_port_probe_is_at (probe)) continue; usbif = mm_kernel_device_get_property_as_int_hex (mm_port_probe_peek_port (probe), "ID_USB_INTERFACE_NUM"); if (usbif == 0) { mm_obj_dbg (self, "fallback port type hint applied to interface 0"); g_object_set_data (G_OBJECT (probe), TAG_AT_PORT_FLAGS, GUINT_TO_POINTER (MM_PORT_SERIAL_AT_FLAG_PPP)); return 1; } } return 0; } static void propagate_port_type_hints (MMPlugin *self, GList *probes) { gboolean primary_flagged = FALSE; guint n_ports_with_hints; g_assert (probes != NULL); if ((n_ports_with_hints = propagate_getportmode_hints (self, probes, &primary_flagged)) > 0) mm_obj_dbg (self, "port type hints set by GETPORTMODE"); else if ((n_ports_with_hints = propagate_description_hints (self, probes, &primary_flagged)) > 0) mm_obj_dbg (self, "port type hints set by interface descriptions"); else if ((n_ports_with_hints = propagate_generic_hints (self, probes, &primary_flagged)) > 0) mm_obj_dbg (self, "port type hints set by generic udev tags"); /* Fallback hint for the first cdc-wdm port if no other port has been flagged as primary */ if (!primary_flagged) n_ports_with_hints += fallback_primary_cdcwdm (self, probes); /* If not a single port type hint available (not plugin-provided and not generic) * then we'll assume usbif 0 is the modem port */ if (!n_ports_with_hints) n_ports_with_hints = fallback_usbif0 (self, probes); mm_obj_dbg (self, "%u port hints have been set", n_ports_with_hints); } static MMBaseModem * create_modem (MMPlugin *self, const gchar *uid, const gchar *physdev, const gchar **drivers, guint16 vendor, guint16 product, guint16 subsystem_vendor, GList *probes, GError **error) { propagate_port_type_hints (self, probes); #if defined WITH_QMI if (mm_port_probe_list_has_qmi_port (probes)) { mm_obj_dbg (self, "QMI-powered Huawei modem found..."); return MM_BASE_MODEM (mm_broadband_modem_qmi_new (uid, physdev, drivers, mm_plugin_get_name (self), vendor, product)); } #endif #if defined WITH_MBIM if (mm_port_probe_list_has_mbim_port (probes)) { mm_obj_dbg (self, "MBIM-powered Huawei modem found..."); return MM_BASE_MODEM (mm_broadband_modem_mbim_new (uid, physdev, drivers, mm_plugin_get_name (self), vendor, product)); } #endif return MM_BASE_MODEM (mm_broadband_modem_huawei_new (uid, physdev, drivers, mm_plugin_get_name (self), vendor, product)); } static gboolean grab_port (MMPlugin *self, MMBaseModem *modem, MMPortProbe *probe, GError **error) { MMPortSerialAtFlag pflags; MMKernelDevice *port; MMPortType port_type; port_type = mm_port_probe_get_port_type (probe); port = mm_port_probe_peek_port (probe); pflags = (MMPortSerialAtFlag) GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (probe), TAG_AT_PORT_FLAGS)); if (pflags != MM_PORT_SERIAL_AT_FLAG_NONE) { gchar *str; str = mm_port_serial_at_flag_build_string_from_mask (pflags); mm_obj_dbg (self, "(%s/%s) port will have AT flags '%s'", mm_port_probe_get_port_subsys (probe), mm_port_probe_get_port_name (probe), str); g_free (str); } else { /* The huawei plugin handles the generic udev tags itself, so explicitly request * to avoid processing them by the generic modem. */ pflags = MM_PORT_SERIAL_AT_FLAG_NONE_NO_GENERIC; } return mm_base_modem_grab_port (modem, port, port_type, pflags, error); } /*****************************************************************************/ MM_PLUGIN_NAMED_CREATOR_SCOPE MMPlugin * mm_plugin_create_huawei (void) { static const gchar *subsystems[] = { "tty", "net", "usbmisc", NULL }; static const guint16 vendor_ids[] = { 0x12d1, 0 }; static const MMAsyncMethod custom_init = { .async = G_CALLBACK (huawei_custom_init), .finish = G_CALLBACK (huawei_custom_init_finish), }; return MM_PLUGIN ( g_object_new (MM_TYPE_PLUGIN_HUAWEI, MM_PLUGIN_NAME, MM_MODULE_NAME, MM_PLUGIN_ALLOWED_SUBSYSTEMS, subsystems, MM_PLUGIN_ALLOWED_VENDOR_IDS, vendor_ids, MM_PLUGIN_ALLOWED_AT, TRUE, MM_PLUGIN_REQUIRED_QCDM, TRUE, MM_PLUGIN_ALLOWED_QMI, TRUE, MM_PLUGIN_ALLOWED_MBIM, TRUE, MM_PLUGIN_CUSTOM_INIT, &custom_init, NULL)); } static void mm_plugin_huawei_init (MMPluginHuawei *self) { } static void mm_plugin_huawei_class_init (MMPluginHuaweiClass *klass) { MMPluginClass *plugin_class = MM_PLUGIN_CLASS (klass); plugin_class->create_modem = create_modem; plugin_class->grab_port = grab_port; } ModemManager-1.23.4-dev/src/plugins/huawei/mm-sim-huawei.c000066400000000000000000000112711456466623000233040ustar00rootroot00000000000000/* -*- 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 - 2012 Red Hat, Inc. * Copyright (C) 2012 Lanedo GmbH */ #include #include #include #include #include #include #include #define _LIBMM_INSIDE_MM #include #include "mm-modem-helpers.h" #include "mm-base-modem-at.h" #include "mm-sim-huawei.h" G_DEFINE_TYPE (MMSimHuawei, mm_sim_huawei, MM_TYPE_BASE_SIM) /*****************************************************************************/ /* SIM identifier loading */ static gchar * load_sim_identifier_finish (MMBaseSim *self, GAsyncResult *res, GError **error) { return g_task_propagate_pointer (G_TASK (res), error); } static void parent_load_sim_identifier_ready (MMSimHuawei *self, GAsyncResult *res, GTask *task) { GError *error = NULL; gchar *simid; simid = MM_BASE_SIM_CLASS (mm_sim_huawei_parent_class)->load_sim_identifier_finish (MM_BASE_SIM (self), res, &error); if (simid) g_task_return_pointer (task, simid, g_free); else g_task_return_error (task, error); g_object_unref (task); } static void iccid_read_ready (MMBaseModem *modem, GAsyncResult *res, GTask *task) { MMBaseSim *self; const gchar *response; const gchar *p; char *parsed; response = mm_base_modem_at_command_finish (modem, res, NULL); if (!response) goto error; p = mm_strip_tag (response, "^ICCID:"); if (!p) goto error; parsed = mm_3gpp_parse_iccid (p, NULL); if (parsed) { g_task_return_pointer (task, parsed, g_free); g_object_unref (task); return; } error: /* Chain up to parent method; older devices don't support ^ICCID */ self = g_task_get_source_object (task); MM_BASE_SIM_CLASS (mm_sim_huawei_parent_class)->load_sim_identifier (self, (GAsyncReadyCallback) parent_load_sim_identifier_ready, task); } static void load_sim_identifier (MMBaseSim *self, GAsyncReadyCallback callback, gpointer user_data) { MMBaseModem *modem = NULL; g_object_get (self, MM_BASE_SIM_MODEM, &modem, NULL); mm_base_modem_at_command ( modem, "^ICCID?", 5, FALSE, (GAsyncReadyCallback)iccid_read_ready, g_task_new (self, NULL, callback, user_data)); g_object_unref (modem); } /*****************************************************************************/ MMBaseSim * mm_sim_huawei_new_finish (GAsyncResult *res, GError **error) { GObject *source; GObject *sim; source = g_async_result_get_source_object (res); sim = g_async_initable_new_finish (G_ASYNC_INITABLE (source), res, error); g_object_unref (source); if (!sim) return NULL; /* Only export valid SIMs */ mm_base_sim_export (MM_BASE_SIM (sim)); return MM_BASE_SIM (sim); } void mm_sim_huawei_new (MMBaseModem *modem, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { g_async_initable_new_async (MM_TYPE_SIM_HUAWEI, G_PRIORITY_DEFAULT, cancellable, callback, user_data, MM_BASE_SIM_MODEM, modem, "active", TRUE, /* by default always active */ NULL); } static void mm_sim_huawei_init (MMSimHuawei *self) { } static void mm_sim_huawei_class_init (MMSimHuaweiClass *klass) { MMBaseSimClass *base_sim_class = MM_BASE_SIM_CLASS (klass); base_sim_class->load_sim_identifier = load_sim_identifier; base_sim_class->load_sim_identifier_finish = load_sim_identifier_finish; } ModemManager-1.23.4-dev/src/plugins/huawei/mm-sim-huawei.h000066400000000000000000000040011456466623000233020ustar00rootroot00000000000000/* -*- 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 - 2012 Red Hat, Inc. * Copyright (C) 2012 Lanedo GmbH */ #ifndef MM_SIM_HUAWEI_H #define MM_SIM_HUAWEI_H #include #include #include "mm-base-sim.h" #define MM_TYPE_SIM_HUAWEI (mm_sim_huawei_get_type ()) #define MM_SIM_HUAWEI(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_SIM_HUAWEI, MMSimHuawei)) #define MM_SIM_HUAWEI_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_SIM_HUAWEI, MMSimHuaweiClass)) #define MM_IS_SIM_HUAWEI(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_SIM_HUAWEI)) #define MM_IS_SIM_HUAWEI_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_SIM_HUAWEI)) #define MM_SIM_HUAWEI_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_SIM_HUAWEI, MMSimHuaweiClass)) typedef struct _MMSimHuawei MMSimHuawei; typedef struct _MMSimHuaweiClass MMSimHuaweiClass; struct _MMSimHuawei { MMBaseSim parent; }; struct _MMSimHuaweiClass { MMBaseSimClass parent; }; GType mm_sim_huawei_get_type (void); void mm_sim_huawei_new (MMBaseModem *modem, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); MMBaseSim *mm_sim_huawei_new_finish (GAsyncResult *res, GError **error); #endif /* MM_SIM_HUAWEI_H */ ModemManager-1.23.4-dev/src/plugins/huawei/tests/000077500000000000000000000000001456466623000216215ustar00rootroot00000000000000ModemManager-1.23.4-dev/src/plugins/huawei/tests/test-modem-helpers-huawei.c000066400000000000000000001462701456466623000267750ustar00rootroot00000000000000/* -*- 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) 2013 Aleksander Morgado */ #include #include #include #include #include #define _LIBMM_INSIDE_MM #include #include "mm-log-test.h" #include "mm-log-object.h" #include "mm-modem-helpers.h" #include "mm-modem-helpers-huawei.h" /*****************************************************************************/ /* Test ^NDISSTAT / ^NDISSTATQRY responses */ typedef struct { const gchar *str; gboolean expected_ipv4_available; gboolean expected_ipv4_connected; gboolean expected_ipv6_available; gboolean expected_ipv6_connected; } NdisstatqryTest; static const NdisstatqryTest ndisstatqry_tests[] = { { "^NDISSTAT: 1,,,IPV4\r\n", TRUE, TRUE, FALSE, FALSE }, { "^NDISSTAT: 0,,,IPV4\r\n", TRUE, FALSE, FALSE, FALSE }, { "^NDISSTAT: 1,,,IPV6\r\n", FALSE, FALSE, TRUE, TRUE }, { "^NDISSTAT: 0,,,IPV6\r\n", FALSE, FALSE, TRUE, FALSE }, { "^NDISSTAT: 1,,,IPV4\r\n" "^NDISSTAT: 1,,,IPV6\r\n", TRUE, TRUE, TRUE, TRUE }, { "^NDISSTAT: 1,,,IPV4\r\n" "^NDISSTAT: 0,,,IPV6\r\n", TRUE, TRUE, TRUE, FALSE }, { "^NDISSTAT: 0,,,IPV4\r\n" "^NDISSTAT: 1,,,IPV6\r\n", TRUE, FALSE, TRUE, TRUE }, { "^NDISSTAT: 0,,,IPV4\r\n" "^NDISSTAT: 0,,,IPV6\r\n", TRUE, FALSE, TRUE, FALSE }, { "^NDISSTAT: 1,,,IPV4", TRUE, TRUE, FALSE, FALSE }, { "^NDISSTAT: 0,,,IPV4", TRUE, FALSE, FALSE, FALSE }, { "^NDISSTAT: 1,,,IPV6", FALSE, FALSE, TRUE, TRUE }, { "^NDISSTAT: 0,,,IPV6", FALSE, FALSE, TRUE, FALSE }, { "^NDISSTAT: 1,,,IPV4\r\n" "^NDISSTAT: 1,,,IPV6", TRUE, TRUE, TRUE, TRUE }, { "^NDISSTAT: 1,,,IPV4\r\n" "^NDISSTAT: 0,,,IPV6", TRUE, TRUE, TRUE, FALSE }, { "^NDISSTAT: 0,,,IPV4\r\n" "^NDISSTAT: 1,,,IPV6", TRUE, FALSE, TRUE, TRUE }, { "^NDISSTAT: 0,,,IPV4\r\n" "^NDISSTAT: 0,,,IPV6", TRUE, FALSE, TRUE, FALSE }, { "^NDISSTAT: 1,,,\"IPV4\",1,,,\"IPV6\"", TRUE, TRUE, TRUE, TRUE }, { "^NDISSTAT: 1,,,\"IPV4\",0,,,\"IPV6\"", TRUE, TRUE, TRUE, FALSE }, { "^NDISSTAT: 0,,,\"IPV4\",1,,,\"IPV6\"", TRUE, FALSE, TRUE, TRUE }, { "^NDISSTAT: 0,,,\"IPV4\",0,,,\"IPV6\"", TRUE, FALSE, TRUE, FALSE }, { "^NDISSTAT: 1,,,\"IPV4\",1,,,\"IPV6\"\r\n", TRUE, TRUE, TRUE, TRUE }, { "^NDISSTAT: 1,,,\"IPV4\",0,,,\"IPV6\"\r\n", TRUE, TRUE, TRUE, FALSE }, { "^NDISSTAT: 0,,,\"IPV4\",1,,,\"IPV6\"\r\n", TRUE, FALSE, TRUE, TRUE }, { "^NDISSTAT: 0,,,\"IPV4\",0,,,\"IPV6\"\r\n", TRUE, FALSE, TRUE, FALSE }, { "^NDISSTATQRY: 1,,,IPV4\r\n", TRUE, TRUE, FALSE, FALSE }, { "^NDISSTATQRY: 0,,,IPV4\r\n", TRUE, FALSE, FALSE, FALSE }, { "^NDISSTATQRY: 1,,,IPV6\r\n", FALSE, FALSE, TRUE, TRUE }, { "^NDISSTATQRY: 0,,,IPV6\r\n", FALSE, FALSE, TRUE, FALSE }, { "^NDISSTATQRY: 1,,,IPV4\r\n" "^NDISSTATQRY: 1,,,IPV6\r\n", TRUE, TRUE, TRUE, TRUE }, { "^NDISSTATQRY: 1,,,IPV4\r\n" "^NDISSTATQRY: 0,,,IPV6\r\n", TRUE, TRUE, TRUE, FALSE }, { "^NDISSTATQRY: 0,,,IPV4\r\n" "^NDISSTATQRY: 1,,,IPV6\r\n", TRUE, FALSE, TRUE, TRUE }, { "^NDISSTATQRY: 0,,,IPV4\r\n" "^NDISSTATQRY: 0,,,IPV6\r\n", TRUE, FALSE, TRUE, FALSE }, { "^NDISSTATQRY: 1,,,IPV4", TRUE, TRUE, FALSE, FALSE }, { "^NDISSTATQRY: 0,,,IPV4", TRUE, FALSE, FALSE, FALSE }, { "^NDISSTATQRY: 1,,,IPV6", FALSE, FALSE, TRUE, TRUE }, { "^NDISSTATQRY: 0,,,IPV6", FALSE, FALSE, TRUE, FALSE }, { "^NDISSTATQRY: 1,,,IPV4\r\n" "^NDISSTATQRY: 1,,,IPV6", TRUE, TRUE, TRUE, TRUE }, { "^NDISSTATQRY: 1,,,IPV4\r\n" "^NDISSTATQRY: 0,,,IPV6", TRUE, TRUE, TRUE, FALSE }, { "^NDISSTATQRY: 0,,,IPV4\r\n" "^NDISSTATQRY: 1,,,IPV6", TRUE, FALSE, TRUE, TRUE }, { "^NDISSTATQRY: 0,,,IPV4\r\n" "^NDISSTATQRY: 0,,,IPV6", TRUE, FALSE, TRUE, FALSE }, { "^NDISSTATQRY: 1,,,\"IPV4\",1,,,\"IPV6\"", TRUE, TRUE, TRUE, TRUE }, { "^NDISSTATQRY: 1,,,\"IPV4\",0,,,\"IPV6\"", TRUE, TRUE, TRUE, FALSE }, { "^NDISSTATQRY: 0,,,\"IPV4\",1,,,\"IPV6\"", TRUE, FALSE, TRUE, TRUE }, { "^NDISSTATQRY: 0,,,\"IPV4\",0,,,\"IPV6\"", TRUE, FALSE, TRUE, FALSE }, { "^NDISSTATQRY: 1,,,\"IPV4\",1,,,\"IPV6\"\r\n", TRUE, TRUE, TRUE, TRUE }, { "^NDISSTATQRY: 1,,,\"IPV4\",0,,,\"IPV6\"\r\n", TRUE, TRUE, TRUE, FALSE }, { "^NDISSTATQRY: 0,,,\"IPV4\",1,,,\"IPV6\"\r\n", TRUE, FALSE, TRUE, TRUE }, { "^NDISSTATQRY: 0,,,\"IPV4\",0,,,\"IPV6\"\r\n", TRUE, FALSE, TRUE, FALSE }, { "^NDISSTATQry:1", TRUE, TRUE, FALSE, FALSE }, { "^NDISSTATQry:1\r\n", TRUE, TRUE, FALSE, FALSE }, { "^NDISSTATQry:0", TRUE, FALSE, FALSE, FALSE }, { "^NDISSTATQry:0\r\n", TRUE, FALSE, FALSE, FALSE }, { NULL, FALSE, FALSE, FALSE, FALSE } }; static void test_ndisstatqry (void) { guint i; for (i = 0; ndisstatqry_tests[i].str; i++) { GError *error = NULL; gboolean ipv4_available; gboolean ipv4_connected; gboolean ipv6_available; gboolean ipv6_connected; g_assert (mm_huawei_parse_ndisstatqry_response ( ndisstatqry_tests[i].str, &ipv4_available, &ipv4_connected, &ipv6_available, &ipv6_connected, &error) == TRUE); g_assert_no_error (error); g_assert (ipv4_available == ndisstatqry_tests[i].expected_ipv4_available); if (ipv4_available) g_assert (ipv4_connected == ndisstatqry_tests[i].expected_ipv4_connected); g_assert (ipv6_available == ndisstatqry_tests[i].expected_ipv6_available); if (ipv6_available) g_assert (ipv6_connected == ndisstatqry_tests[i].expected_ipv6_connected); } } /*****************************************************************************/ /* Test ^DHCP responses */ typedef struct { const gchar *str; const gchar *expected_addr; guint expected_prefix; const gchar *expected_gateway; const gchar *expected_dns1; const gchar *expected_dns2; } DhcpTest; static const DhcpTest dhcp_tests[] = { { "^DHCP:a3ec5c64,f8ffffff,a1ec5c64,a1ec5c64,2200b10a,74bba80a,236800,236800\r\n", "100.92.236.163", 29, "100.92.236.161", "10.177.0.34", "10.168.187.116" }, { "^DHCP:0xa3ec5c64,0xf8ffffff,0xa1ec5c64,0xa1ec5c64,0x2200b10a,0x74bba80a,236800,236800\r\n", "100.92.236.163", 29, "100.92.236.161", "10.177.0.34", "10.168.187.116" }, { "^DHCP: 1010A0A,FCFFFFFF,2010A0A,2010A0A,0,0,150000000,150000000\r\n", "10.10.1.1", 30, "10.10.1.2", "0.0.0.0", "0.0.0.0" }, { "^DHCP: CCDB080A,F8FFFFFF,C9DB080A,C9DB080A,E67B59C0,E77B59C0,85600,85600\r\n", "10.8.219.204", 29, "10.8.219.201", "192.89.123.230", "192.89.123.231" }, { "^DHCP: 0xCCDB080A,0xF8FFFFFF,0xC9DB080A,0xC9DB080A,0xE67B59C0,0xE77B59C0,85600,85600\r\n", "10.8.219.204", 29, "10.8.219.201", "192.89.123.230", "192.89.123.231" }, { "^DHCP: 0XCCDB080A,0XF8FFFFFF,0XC9DB080A,0XC9DB080A,0XE67B59C0,0XE77B59C0,85600,85600\r\n", "10.8.219.204", 29, "10.8.219.201", "192.89.123.230", "192.89.123.231" }, { NULL } }; static void test_dhcp (void) { guint i; for (i = 0; dhcp_tests[i].str; i++) { GError *error = NULL; guint addr, prefix, gateway, dns1, dns2; g_assert (mm_huawei_parse_dhcp_response ( dhcp_tests[i].str, &addr, &prefix, &gateway, &dns1, &dns2, &error) == TRUE); g_assert_no_error (error); g_assert_cmpstr (inet_ntoa (*((struct in_addr *) &addr)), ==, dhcp_tests[i].expected_addr); g_assert_cmpint (prefix, ==, dhcp_tests[i].expected_prefix); g_assert_cmpstr (inet_ntoa (*((struct in_addr *) &gateway)), ==, dhcp_tests[i].expected_gateway); g_assert_cmpstr (inet_ntoa (*((struct in_addr *) &dns1)), ==, dhcp_tests[i].expected_dns1); g_assert_cmpstr (inet_ntoa (*((struct in_addr *) &dns2)), ==, dhcp_tests[i].expected_dns2); } } /*****************************************************************************/ /* Test ^SYSINFO responses */ typedef struct { const gchar *str; guint expected_srv_status; guint expected_srv_domain; guint expected_roam_status; guint expected_sys_mode; guint expected_sim_state; gboolean expected_sys_submode_valid; guint expected_sys_submode; } SysinfoTest; static const SysinfoTest sysinfo_tests[] = { { "^SYSINFO:2,4,5,3,1", 2, 4, 5, 3, 1, FALSE, 0 }, { "^SYSINFO:2,4,5,3,1,", 2, 4, 5, 3, 1, FALSE, 0 }, { "^SYSINFO:2,4,5,3,1,,", 2, 4, 5, 3, 1, FALSE, 0 }, { "^SYSINFO:2,4,5,3,1,6", 2, 4, 5, 3, 1, FALSE, 6 }, { "^SYSINFO:2,4,5,3,1,6,", 2, 4, 5, 3, 1, FALSE, 6 }, { "^SYSINFO:2,4,5,3,1,,6", 2, 4, 5, 3, 1, TRUE, 6 }, { "^SYSINFO:2,4,5,3,1,0,6", 2, 4, 5, 3, 1, TRUE, 6 }, { "^SYSINFO: 2,4,5,3,1,0,6", 2, 4, 5, 3, 1, TRUE, 6 }, { NULL, 0, 0, 0, 0, 0, FALSE, 0 } }; static void test_sysinfo (void) { guint i; for (i = 0; sysinfo_tests[i].str; i++) { GError *error = NULL; guint srv_status = 0; guint srv_domain = 0; guint roam_status = 0; guint sys_mode = 0; guint sim_state = 0; gboolean sys_submode_valid = FALSE; guint sys_submode = 0; g_assert (mm_huawei_parse_sysinfo_response (sysinfo_tests[i].str, &srv_status, &srv_domain, &roam_status, &sys_mode, &sim_state, &sys_submode_valid, &sys_submode, &error) == TRUE); g_assert_no_error (error); g_assert (srv_status == sysinfo_tests[i].expected_srv_status); g_assert (srv_domain == sysinfo_tests[i].expected_srv_domain); g_assert (roam_status == sysinfo_tests[i].expected_roam_status); g_assert (sys_mode == sysinfo_tests[i].expected_sys_mode); g_assert (sim_state == sysinfo_tests[i].expected_sim_state); g_assert (sys_submode_valid == sysinfo_tests[i].expected_sys_submode_valid); if (sys_submode_valid) g_assert (sys_submode == sysinfo_tests[i].expected_sys_submode); } } /*****************************************************************************/ /* Test ^SYSINFOEX responses */ typedef struct { const gchar *str; guint expected_srv_status; guint expected_srv_domain; guint expected_roam_status; guint expected_sim_state; guint expected_sys_mode; guint expected_sys_submode; } SysinfoexTest; static const SysinfoexTest sysinfoex_tests[] = { { "^SYSINFOEX:2,4,5,1,,3,WCDMA,41,HSPA+", 2, 4, 5, 1, 3, 41 }, { "^SYSINFOEX:2,4,5,1,,3,\"WCDMA\",41,\"HSPA+\"", 2, 4, 5, 1, 3, 41 }, { "^SYSINFOEX: 2,4,5,1,0,3,\"WCDMA\",41,\"HSPA+\"", 2, 4, 5, 1, 3, 41 }, { NULL, 0, 0, 0, 0, 0, 0 } }; static void test_sysinfoex (void) { guint i; for (i = 0; sysinfoex_tests[i].str; i++) { GError *error = NULL; guint srv_status = 0; guint srv_domain = 0; guint roam_status = 0; guint sim_state = 0; guint sys_mode = 0; guint sys_submode = 0; g_assert (mm_huawei_parse_sysinfoex_response (sysinfoex_tests[i].str, &srv_status, &srv_domain, &roam_status, &sim_state, &sys_mode, &sys_submode, &error) == TRUE); g_assert_no_error (error); g_assert (srv_status == sysinfoex_tests[i].expected_srv_status); g_assert (srv_domain == sysinfoex_tests[i].expected_srv_domain); g_assert (roam_status == sysinfoex_tests[i].expected_roam_status); g_assert (sim_state == sysinfoex_tests[i].expected_sim_state); g_assert (sys_mode == sysinfoex_tests[i].expected_sys_mode); g_assert (sys_submode == sysinfoex_tests[i].expected_sys_submode); } } /*****************************************************************************/ /* Test ^PREFMODE=? responses */ #define MAX_PREFMODE_COMBINATIONS 3 typedef struct { const gchar *str; MMHuaweiPrefmodeCombination expected_modes[MAX_PREFMODE_COMBINATIONS]; } PrefmodeTest; static const PrefmodeTest prefmode_tests[] = { { "^PREFMODE:(2,4,8)\r\n", { { .prefmode = 8, .allowed = (MM_MODEM_MODE_3G | MM_MODEM_MODE_2G), .preferred = MM_MODEM_MODE_NONE }, { .prefmode = 4, .allowed = (MM_MODEM_MODE_3G | MM_MODEM_MODE_2G), .preferred = MM_MODEM_MODE_3G }, { .prefmode = 2, .allowed = (MM_MODEM_MODE_3G | MM_MODEM_MODE_2G), .preferred = MM_MODEM_MODE_2G } } }, { "^PREFMODE:(2,4)\r\n", { { .prefmode = 4, .allowed = (MM_MODEM_MODE_3G | MM_MODEM_MODE_2G), .preferred = MM_MODEM_MODE_3G }, { .prefmode = 2, .allowed = (MM_MODEM_MODE_3G | MM_MODEM_MODE_2G), .preferred = MM_MODEM_MODE_2G }, { 0, 0, 0} } }, { "^PREFMODE:(2)\r\n", { { .prefmode = 2, .allowed = MM_MODEM_MODE_2G, .preferred = MM_MODEM_MODE_NONE }, { 0, 0, 0} } }, }; static void test_prefmode (void) { guint i; for (i = 0; i < G_N_ELEMENTS (prefmode_tests); i++) { GError *error = NULL; GArray *combinations = NULL; guint j; guint n_expected_combinations = 0; for (j = 0; j < MAX_PREFMODE_COMBINATIONS; j++) { if (prefmode_tests[i].expected_modes[j].prefmode != 0) n_expected_combinations++; } combinations = mm_huawei_parse_prefmode_test (prefmode_tests[i].str, NULL, &error); g_assert_no_error (error); g_assert (combinations != NULL); g_assert_cmpuint (combinations->len, ==, n_expected_combinations); for (j = 0; j < combinations->len; j++) { MMHuaweiPrefmodeCombination *single; g_autofree gchar *allowed_str = NULL; g_autofree gchar *preferred_str = NULL; single = &g_array_index (combinations, MMHuaweiPrefmodeCombination, j); allowed_str = mm_modem_mode_build_string_from_mask (single->allowed); preferred_str = mm_modem_mode_build_string_from_mask (single->preferred); mm_obj_dbg (NULL, "test[%u], combination[%u]: %u, \"%s\", \"%s\"", i, j, single->prefmode, allowed_str, preferred_str); } for (j = 0; j < combinations->len; j++) { MMHuaweiPrefmodeCombination *single; guint k; gboolean found = FALSE; single = &g_array_index (combinations, MMHuaweiPrefmodeCombination, j); for (k = 0; k <= n_expected_combinations; k++) { if (single->allowed == prefmode_tests[i].expected_modes[k].allowed && single->preferred == prefmode_tests[i].expected_modes[k].preferred && single->prefmode == prefmode_tests[i].expected_modes[k].prefmode) { found = TRUE; break; } } g_assert (found == TRUE); } g_array_unref (combinations); } } /*****************************************************************************/ /* Test ^PREFMODE? responses */ typedef struct { const gchar *str; const gchar *format; MMModemMode allowed; MMModemMode preferred; } PrefmodeResponseTest; static const PrefmodeResponseTest prefmode_response_tests[] = { { .str = "^PREFMODE:2\r\n", .format = "^PREFMODE:(2,4,8)\r\n", .allowed = (MM_MODEM_MODE_3G | MM_MODEM_MODE_2G), .preferred = MM_MODEM_MODE_2G }, { .str = "^PREFMODE:4\r\n", .format = "^PREFMODE:(2,4,8)\r\n", .allowed = (MM_MODEM_MODE_3G | MM_MODEM_MODE_2G), .preferred = MM_MODEM_MODE_3G }, { .str = "^PREFMODE:8\r\n", .format = "^PREFMODE:(2,4,8)\r\n", .allowed = (MM_MODEM_MODE_3G | MM_MODEM_MODE_2G), .preferred = MM_MODEM_MODE_NONE } }; static void test_prefmode_response (void) { guint i; for (i = 0; i < G_N_ELEMENTS (prefmode_response_tests); i++) { GArray *combinations = NULL; const MMHuaweiPrefmodeCombination *found; GError *error = NULL; combinations = mm_huawei_parse_prefmode_test (prefmode_response_tests[i].format, NULL, NULL); g_assert (combinations != NULL); found = mm_huawei_parse_prefmode_response (prefmode_response_tests[i].str, combinations, &error); g_assert_no_error (error); g_assert (found != NULL); g_assert_cmpuint (found->allowed, ==, prefmode_response_tests[i].allowed); g_assert_cmpuint (found->preferred, ==, prefmode_response_tests[i].preferred); g_array_unref (combinations); } } /*****************************************************************************/ /* Test ^SYSCFG=? responses */ #define MAX_SYSCFG_COMBINATIONS 5 typedef struct { const gchar *str; MMHuaweiSyscfgCombination expected_modes[MAX_SYSCFG_COMBINATIONS]; } SyscfgTest; static const SyscfgTest syscfg_tests[] = { { MM_HUAWEI_DEFAULT_SYSCFG_FMT, { { .mode = 2, .acqorder = 0, .allowed = (MM_MODEM_MODE_3G | MM_MODEM_MODE_2G), .preferred = MM_MODEM_MODE_NONE }, { .mode = 2, .acqorder = 1, .allowed = (MM_MODEM_MODE_3G | MM_MODEM_MODE_2G), .preferred = MM_MODEM_MODE_2G }, { .mode = 2, .acqorder = 2, .allowed = (MM_MODEM_MODE_3G | MM_MODEM_MODE_2G), .preferred = MM_MODEM_MODE_3G }, { .mode = 13, .acqorder = 0, .allowed = MM_MODEM_MODE_2G, .preferred = MM_MODEM_MODE_NONE }, { .mode = 14, .acqorder = 0, .allowed = MM_MODEM_MODE_3G, .preferred = MM_MODEM_MODE_NONE } } }, { "^SYSCFG:(2,13,14,16),(0-3),((400000,\"WCDMA2100\")),(0-2),(0-4)\r\n", { { .mode = 2, .acqorder = 0, .allowed = (MM_MODEM_MODE_3G | MM_MODEM_MODE_2G), .preferred = MM_MODEM_MODE_NONE }, { .mode = 2, .acqorder = 1, .allowed = (MM_MODEM_MODE_3G | MM_MODEM_MODE_2G), .preferred = MM_MODEM_MODE_2G }, { .mode = 2, .acqorder = 2, .allowed = (MM_MODEM_MODE_3G | MM_MODEM_MODE_2G), .preferred = MM_MODEM_MODE_3G }, { .mode = 13, .acqorder = 0, .allowed = MM_MODEM_MODE_2G, .preferred = MM_MODEM_MODE_NONE }, { .mode = 14, .acqorder = 0, .allowed = MM_MODEM_MODE_3G, .preferred = MM_MODEM_MODE_NONE } } }, { "^SYSCFG:(2,13,14,16),(0),((400000,\"WCDMA2100\")),(0-2),(0-4)\r\n", { { .mode = 2, .acqorder = 0, .allowed = (MM_MODEM_MODE_3G | MM_MODEM_MODE_2G), .preferred = MM_MODEM_MODE_NONE }, { .mode = 13, .acqorder = 0, .allowed = MM_MODEM_MODE_2G, .preferred = MM_MODEM_MODE_NONE }, { .mode = 14, .acqorder = 0, .allowed = MM_MODEM_MODE_3G, .preferred = MM_MODEM_MODE_NONE }, { 0, 0, 0, 0 } } }, { "^SYSCFG:(13),(0),((400000,\"WCDMA2100\")),(0-2),(0-4)\r\n", { { .mode = 13, .acqorder = 0, .allowed = MM_MODEM_MODE_2G, .preferred = MM_MODEM_MODE_NONE }, { 0, 0, 0, 0 } } } }; static void test_syscfg (void) { guint i; for (i = 0; i < G_N_ELEMENTS (syscfg_tests); i++) { GError *error = NULL; GArray *combinations = NULL; guint j; guint n_expected_combinations = 0; for (j = 0; j < MAX_SYSCFG_COMBINATIONS; j++) { if (syscfg_tests[i].expected_modes[j].mode != 0) n_expected_combinations++; } combinations = mm_huawei_parse_syscfg_test (syscfg_tests[i].str, NULL, &error); g_assert_no_error (error); g_assert (combinations != NULL); g_assert_cmpuint (combinations->len, ==, n_expected_combinations); for (j = 0; j < combinations->len; j++) { MMHuaweiSyscfgCombination *single; g_autofree gchar *allowed_str = NULL; g_autofree gchar *preferred_str = NULL; single = &g_array_index (combinations, MMHuaweiSyscfgCombination, j); allowed_str = mm_modem_mode_build_string_from_mask (single->allowed); preferred_str = mm_modem_mode_build_string_from_mask (single->preferred); mm_obj_dbg (NULL, "test[%u], combination[%u]: %u, %u, \"%s\", \"%s\"", i, j, single->mode, single->acqorder, allowed_str, preferred_str); } for (j = 0; j < combinations->len; j++) { MMHuaweiSyscfgCombination *single; guint k; gboolean found = FALSE; single = &g_array_index (combinations, MMHuaweiSyscfgCombination, j); for (k = 0; k <= n_expected_combinations; k++) { if (single->allowed == syscfg_tests[i].expected_modes[k].allowed && single->preferred == syscfg_tests[i].expected_modes[k].preferred && single->mode == syscfg_tests[i].expected_modes[k].mode && single->acqorder == syscfg_tests[i].expected_modes[k].acqorder) { found = TRUE; break; } } g_assert (found == TRUE); } g_array_unref (combinations); } } /*****************************************************************************/ /* Test ^SYSCFG? responses */ typedef struct { const gchar *str; const gchar *format; MMModemMode allowed; MMModemMode preferred; } SyscfgResponseTest; static const SyscfgResponseTest syscfg_response_tests[] = { { .str = "^SYSCFG: 2,0,400000,0,3\r\n", .format = "^SYSCFG:(2,13,14,16),(0-3),((400000,\"WCDMA2100\")),(0-2),(0-4)\r\n", .allowed = (MM_MODEM_MODE_3G | MM_MODEM_MODE_2G), .preferred = MM_MODEM_MODE_NONE }, { .str = "^SYSCFG: 2,1,400000,0,3\r\n", .format = "^SYSCFG:(2,13,14,16),(0-3),((400000,\"WCDMA2100\")),(0-2),(0-4)\r\n", .allowed = (MM_MODEM_MODE_3G | MM_MODEM_MODE_2G), .preferred = MM_MODEM_MODE_2G }, { .str = "^SYSCFG: 2,2,400000,0,3\r\n", .format = "^SYSCFG:(2,13,14,16),(0-3),((400000,\"WCDMA2100\")),(0-2),(0-4)\r\n", .allowed = (MM_MODEM_MODE_3G | MM_MODEM_MODE_2G), .preferred = MM_MODEM_MODE_3G }, { .str = "^SYSCFG: 13,0,400000,0,3\r\n", .format = "^SYSCFG:(2,13,14,16),(0-3),((400000,\"WCDMA2100\")),(0-2),(0-4)\r\n", .allowed = MM_MODEM_MODE_2G, .preferred = MM_MODEM_MODE_NONE }, { .str = "^SYSCFG: 14,0,400000,0,3\r\n", .format = "^SYSCFG:(2,13,14,16),(0-3),((400000,\"WCDMA2100\")),(0-2),(0-4)\r\n", .allowed = MM_MODEM_MODE_3G, .preferred = MM_MODEM_MODE_NONE }, { /* Non-sensical acquisition order (WCDMA-only but acquire WCDMA-then-GSM */ .str = "^SYSCFG: 14,2,400000,0,3\r\n", .format = "^SYSCFG:(2,13,14,16),(0-3),((400000,\"WCDMA2100\")),(0-2),(0-4)\r\n", .allowed = MM_MODEM_MODE_3G, .preferred = MM_MODEM_MODE_NONE }, { /* Non-sensical acquisition order (GSM-only but acquire GSM-then-WCDMA */ .str = "^SYSCFG: 13,1,400000,0,3\r\n", .format = "^SYSCFG:(2,13,14,16),(0-3),((400000,\"WCDMA2100\")),(0-2),(0-4)\r\n", .allowed = MM_MODEM_MODE_2G, .preferred = MM_MODEM_MODE_NONE } }; static void test_syscfg_response (void) { guint i; for (i = 0; i < G_N_ELEMENTS (syscfg_response_tests); i++) { GArray *combinations = NULL; const MMHuaweiSyscfgCombination *found; GError *error = NULL; combinations = mm_huawei_parse_syscfg_test (syscfg_response_tests[i].format, NULL, NULL); g_assert (combinations != NULL); found = mm_huawei_parse_syscfg_response (syscfg_response_tests[i].str, combinations, &error); g_assert_no_error (error); g_assert (found != NULL); g_assert_cmpuint (found->allowed, ==, syscfg_response_tests[i].allowed); g_assert_cmpuint (found->preferred, ==, syscfg_response_tests[i].preferred); g_array_unref (combinations); } } /*****************************************************************************/ /* Test ^SYSCFGEX=? responses */ #define MAX_SYSCFGEX_COMBINATIONS 5 typedef struct { const gchar *str; MMHuaweiSyscfgexCombination expected_modes[MAX_SYSCFGEX_COMBINATIONS]; } SyscfgexTest; static const SyscfgexTest syscfgex_tests[] = { { "^SYSCFGEX: (\"00\",\"03\",\"02\",\"01\",\"99\")," "((2000004e80380,\"GSM850/GSM900/GSM1800/GSM1900/WCDMA850/WCDMA900/WCDMA1900/WCDMA2100\"),(3fffffff,\"All Bands\"))," "(0-3)," "(0-4)," "((800c5,\"LTE2100/LTE1800/LTE2600/LTE900/LTE800\"),(7fffffffffffffff,\"All bands\"))" "\r\n", { { .mode_str = (gchar *) "00", .allowed = (MM_MODEM_MODE_4G | MM_MODEM_MODE_3G | MM_MODEM_MODE_2G), .preferred = MM_MODEM_MODE_NONE }, { .mode_str = (gchar *) "03", .allowed = MM_MODEM_MODE_4G, .preferred = MM_MODEM_MODE_NONE }, { .mode_str = (gchar *) "02", .allowed = MM_MODEM_MODE_3G, .preferred = MM_MODEM_MODE_NONE }, { .mode_str = (gchar *) "01", .allowed = MM_MODEM_MODE_2G, .preferred = MM_MODEM_MODE_NONE }, { NULL, 0, 0 } } }, { "^SYSCFGEX: (\"030201\",\"0302\",\"03\",\"99\")," "((2000004e80380,\"GSM850/GSM900/GSM1800/GSM1900/WCDMA850/WCDMA900/WCDMA1900/WCDMA2100\"),(3fffffff,\"All Bands\"))," "(0-3)," "(0-4)," "((800c5,\"LTE2100/LTE1800/LTE2600/LTE900/LTE800\"),(7fffffffffffffff,\"All bands\"))" "\r\n", { { .mode_str = (gchar *) "030201", .allowed = (MM_MODEM_MODE_4G | MM_MODEM_MODE_3G | MM_MODEM_MODE_2G), .preferred = MM_MODEM_MODE_4G }, { .mode_str = (gchar *) "0302", .allowed = (MM_MODEM_MODE_4G | MM_MODEM_MODE_3G), .preferred = MM_MODEM_MODE_4G }, { .mode_str = (gchar *) "03", .allowed = MM_MODEM_MODE_4G, .preferred = MM_MODEM_MODE_NONE }, { NULL, 0, 0 } } }, { "^SYSCFGEX: (\"03\")," "((2000004e80380,\"GSM850/GSM900/GSM1800/GSM1900/WCDMA850/WCDMA900/WCDMA1900/WCDMA2100\"),(3fffffff,\"All Bands\"))," "(0-3)," "(0-4)," "((800c5,\"LTE2100/LTE1800/LTE2600/LTE900/LTE800\"),(7fffffffffffffff,\"All bands\"))" "\r\n", { { .mode_str = (gchar *) "03", .allowed = MM_MODEM_MODE_4G, .preferred = MM_MODEM_MODE_NONE }, { NULL, 0, 0 } } }, { "^SYSCFGEX: (\"00\",\"01\",\"02\",\"0102\",\"0201\")," "((3fffffff,\"All Bands\"),(2000000400180,\"GSM900/GSM1800/WCDMA900/WCDMA2100\"),(6A80000,\"GSM850/GSM1900/WCDMA850/AWS/WCDMA1900\"))," "(0-2)," "(0-4)," "," /* NOTE: Non-LTE modem, LTE Bands EMPTY */ "\r\n", { { .mode_str = (gchar *) "00", .allowed = (MM_MODEM_MODE_3G | MM_MODEM_MODE_2G), .preferred = MM_MODEM_MODE_NONE }, { .mode_str = (gchar *) "01", .allowed = MM_MODEM_MODE_2G, .preferred = MM_MODEM_MODE_NONE }, { .mode_str = (gchar *) "02", .allowed = MM_MODEM_MODE_3G, .preferred = MM_MODEM_MODE_NONE }, { .mode_str = (gchar *) "0102", .allowed = (MM_MODEM_MODE_2G | MM_MODEM_MODE_3G), .preferred = MM_MODEM_MODE_2G }, { .mode_str = (gchar *) "0201", .allowed = (MM_MODEM_MODE_2G | MM_MODEM_MODE_3G), .preferred = MM_MODEM_MODE_3G } } } }; static void test_syscfgex (void) { guint i; for (i = 0; i < G_N_ELEMENTS (syscfgex_tests); i++) { GError *error = NULL; GArray *combinations = NULL; guint j; guint n_expected_combinations = 0; for (j = 0; j < MAX_SYSCFGEX_COMBINATIONS; j++) { if (syscfgex_tests[i].expected_modes[j].mode_str != NULL) n_expected_combinations++; } combinations = mm_huawei_parse_syscfgex_test (syscfgex_tests[i].str, &error); g_assert_no_error (error); g_assert (combinations != NULL); g_assert_cmpuint (combinations->len, ==, n_expected_combinations); for (j = 0; j < combinations->len; j++) { MMHuaweiSyscfgexCombination *single; g_autofree gchar *allowed_str = NULL; g_autofree gchar *preferred_str = NULL; single = &g_array_index (combinations, MMHuaweiSyscfgexCombination, j); allowed_str = mm_modem_mode_build_string_from_mask (single->allowed); preferred_str = mm_modem_mode_build_string_from_mask (single->preferred); mm_obj_dbg (NULL, "test[%u], combination[%u]: \"%s\", \"%s\", \"%s\"", i, j, single->mode_str, allowed_str, preferred_str); } for (j = 0; j < combinations->len; j++) { MMHuaweiSyscfgexCombination *single; guint k; gboolean found = FALSE; single = &g_array_index (combinations, MMHuaweiSyscfgexCombination, j); for (k = 0; k <= n_expected_combinations; k++) { if (g_str_equal (single->mode_str, syscfgex_tests[i].expected_modes[k].mode_str) && single->allowed == syscfgex_tests[i].expected_modes[k].allowed && single->preferred == syscfgex_tests[i].expected_modes[k].preferred) { found = TRUE; break; } } g_assert (found == TRUE); } g_array_unref (combinations); } } /*****************************************************************************/ /* Test ^SYSCFGEX? responses */ typedef struct { const gchar *str; const gchar *format; MMModemMode allowed; MMModemMode preferred; } SyscfgexResponseTest; static const SyscfgexResponseTest syscfgex_response_tests[] = { { .str = "^SYSCFGEX: \"00\",3FFFFFFF,1,2,7FFFFFFFFFFFFFFF", .format = "^SYSCFGEX: (\"00\",\"03\",\"02\",\"01\",\"99\")," "((2000004e80380,\"GSM850/GSM900/GSM1800/GSM1900/WCDMA850/WCDMA900/WCDMA1900/WCDMA2100\"),(3fffffff,\"All Bands\"))," "(0-3)," "(0-4)," "((800c5,\"LTE2100/LTE1800/LTE2600/LTE900/LTE800\"),(7fffffffffffffff,\"All bands\"))" "\r\n", .allowed = (MM_MODEM_MODE_4G | MM_MODEM_MODE_3G | MM_MODEM_MODE_2G), .preferred = MM_MODEM_MODE_NONE }, { .str = "^SYSCFGEX: \"03\",3FFFFFFF,1,2,7FFFFFFFFFFFFFFF", .format = "^SYSCFGEX: (\"00\",\"03\",\"02\",\"01\",\"99\")," "((2000004e80380,\"GSM850/GSM900/GSM1800/GSM1900/WCDMA850/WCDMA900/WCDMA1900/WCDMA2100\"),(3fffffff,\"All Bands\"))," "(0-3)," "(0-4)," "((800c5,\"LTE2100/LTE1800/LTE2600/LTE900/LTE800\"),(7fffffffffffffff,\"All bands\"))" "\r\n", .allowed = MM_MODEM_MODE_4G, .preferred = MM_MODEM_MODE_NONE }, { .str = "^SYSCFGEX: \"02\",3FFFFFFF,1,2,7FFFFFFFFFFFFFFF", .format = "^SYSCFGEX: (\"00\",\"03\",\"02\",\"01\",\"99\")," "((2000004e80380,\"GSM850/GSM900/GSM1800/GSM1900/WCDMA850/WCDMA900/WCDMA1900/WCDMA2100\"),(3fffffff,\"All Bands\"))," "(0-3)," "(0-4)," "((800c5,\"LTE2100/LTE1800/LTE2600/LTE900/LTE800\"),(7fffffffffffffff,\"All bands\"))" "\r\n", .allowed = MM_MODEM_MODE_3G, .preferred = MM_MODEM_MODE_NONE }, { .str = "^SYSCFGEX: \"01\",3FFFFFFF,1,2,7FFFFFFFFFFFFFFF", .format = "^SYSCFGEX: (\"00\",\"03\",\"02\",\"01\",\"99\")," "((2000004e80380,\"GSM850/GSM900/GSM1800/GSM1900/WCDMA850/WCDMA900/WCDMA1900/WCDMA2100\"),(3fffffff,\"All Bands\"))," "(0-3)," "(0-4)," "((800c5,\"LTE2100/LTE1800/LTE2600/LTE900/LTE800\"),(7fffffffffffffff,\"All bands\"))" "\r\n", .allowed = MM_MODEM_MODE_2G, .preferred = MM_MODEM_MODE_NONE }, { .str = "^SYSCFGEX: \"00\",3fffffff,1,2,", .format = "^SYSCFGEX: (\"00\",\"01\",\"02\",\"0102\",\"0201\")," "((3fffffff,\"All Bands\"),(2000000400180,\"GSM900/GSM1800/WCDMA900/WCDMA2100\"),(6A80000,\"GSM850/GSM1900/WCDMA850/AWS/WCDMA1900\"))," "(0-2)," "(0-4)," "," /* NOTE: Non-LTE modem, LTE Bands EMPTY */ "\r\n", .allowed = (MM_MODEM_MODE_2G | MM_MODEM_MODE_3G), .preferred = MM_MODEM_MODE_NONE }, { .str = "^SYSCFGEX: \"01\",3fffffff,1,2,", .format = "^SYSCFGEX: (\"00\",\"01\",\"02\",\"0102\",\"0201\")," "((3fffffff,\"All Bands\"),(2000000400180,\"GSM900/GSM1800/WCDMA900/WCDMA2100\"),(6A80000,\"GSM850/GSM1900/WCDMA850/AWS/WCDMA1900\"))," "(0-2)," "(0-4)," "," /* NOTE: Non-LTE modem, LTE Bands EMPTY */ "\r\n", .allowed = MM_MODEM_MODE_2G, .preferred = MM_MODEM_MODE_NONE }, { .str = "^SYSCFGEX: \"02\",3fffffff,1,2,", .format = "^SYSCFGEX: (\"00\",\"01\",\"02\",\"0102\",\"0201\")," "((3fffffff,\"All Bands\"),(2000000400180,\"GSM900/GSM1800/WCDMA900/WCDMA2100\"),(6A80000,\"GSM850/GSM1900/WCDMA850/AWS/WCDMA1900\"))," "(0-2)," "(0-4)," "," /* NOTE: Non-LTE modem, LTE Bands EMPTY */ "\r\n", .allowed = MM_MODEM_MODE_3G, .preferred = MM_MODEM_MODE_NONE }, { .str = "^SYSCFGEX: \"0102\",3fffffff,1,2,", .format = "^SYSCFGEX: (\"00\",\"01\",\"02\",\"0102\",\"0201\")," "((3fffffff,\"All Bands\"),(2000000400180,\"GSM900/GSM1800/WCDMA900/WCDMA2100\"),(6A80000,\"GSM850/GSM1900/WCDMA850/AWS/WCDMA1900\"))," "(0-2)," "(0-4)," "," /* NOTE: Non-LTE modem, LTE Bands EMPTY */ "\r\n", .allowed = (MM_MODEM_MODE_2G | MM_MODEM_MODE_3G), .preferred = MM_MODEM_MODE_2G }, { .str = "^SYSCFGEX: \"0201\",3fffffff,1,2,", .format = "^SYSCFGEX: (\"00\",\"01\",\"02\",\"0102\",\"0201\")," "((3fffffff,\"All Bands\"),(2000000400180,\"GSM900/GSM1800/WCDMA900/WCDMA2100\"),(6A80000,\"GSM850/GSM1900/WCDMA850/AWS/WCDMA1900\"))," "(0-2)," "(0-4)," "," /* NOTE: Non-LTE modem, LTE Bands EMPTY */ "\r\n", .allowed = (MM_MODEM_MODE_2G | MM_MODEM_MODE_3G), .preferred = MM_MODEM_MODE_3G } }; static void test_syscfgex_response (void) { guint i; for (i = 0; i < G_N_ELEMENTS (syscfgex_response_tests); i++) { GArray *combinations = NULL; const MMHuaweiSyscfgexCombination *found; GError *error = NULL; combinations = mm_huawei_parse_syscfgex_test (syscfgex_response_tests[i].format, NULL); g_assert (combinations != NULL); found = mm_huawei_parse_syscfgex_response (syscfgex_response_tests[i].str, combinations, &error); g_assert_no_error (error); g_assert (found != NULL); g_assert_cmpuint (found->allowed, ==, syscfgex_response_tests[i].allowed); g_assert_cmpuint (found->preferred, ==, syscfgex_response_tests[i].preferred); g_array_unref (combinations); } } /*****************************************************************************/ /* Test ^NWTIME responses */ typedef struct { const gchar *str; gboolean ret; gboolean test_iso8601; gboolean test_tz; const gchar *iso8601; gint32 offset; gint32 dst_offset; gint32 leap_seconds; } NwtimeTest; #define NWT_UNKNOWN MM_NETWORK_TIMEZONE_LEAP_SECONDS_UNKNOWN static const NwtimeTest nwtime_tests[] = { { "^NWTIME: 14/08/05,04:00:21+40,00", TRUE, TRUE, FALSE, "2014-08-05T04:00:21+10", 600, 0, NWT_UNKNOWN }, { "^NWTIME: 14/08/05,04:00:21+40,00", TRUE, FALSE, TRUE, "2014-08-05T04:00:21+10", 600, 0, NWT_UNKNOWN }, { "^NWTIME: 14/08/05,04:00:21+40,00", TRUE, TRUE, TRUE, "2014-08-05T04:00:21+10", 600, 0, NWT_UNKNOWN }, { "^NWTIME: 14/08/05,04:00:21+20,00", TRUE, TRUE, FALSE, "2014-08-05T04:00:21+05", 300, 0, NWT_UNKNOWN }, { "^NWTIME: 14/08/05,04:00:21+20,00", TRUE, FALSE, TRUE, "2014-08-05T04:00:21+05", 300, 0, NWT_UNKNOWN }, { "^NWTIME: 14/08/05,04:00:21+20,00", TRUE, TRUE, TRUE, "2014-08-05T04:00:21+05", 300, 0, NWT_UNKNOWN }, { "^NWTIME: 14/08/05,04:00:21+40,01", TRUE, TRUE, FALSE, "2014-08-05T04:00:21+10", 600, 60, NWT_UNKNOWN }, { "^NWTIME: 14/08/05,04:00:21+40,01", TRUE, FALSE, TRUE, "2014-08-05T04:00:21+10", 600, 60, NWT_UNKNOWN }, { "^NWTIME: 14/08/05,04:00:21+40,01", TRUE, TRUE, TRUE, "2014-08-05T04:00:21+10", 600, 60, NWT_UNKNOWN }, { "^NWTIME: 14/08/05,04:00:21+40,02", TRUE, TRUE, FALSE, "2014-08-05T04:00:21+10", 600, 120, NWT_UNKNOWN }, { "^NWTIME: 14/08/05,04:00:21+40,02", TRUE, FALSE, TRUE, "2014-08-05T04:00:21+10", 600, 120, NWT_UNKNOWN }, { "^NWTIME: 14/08/05,04:00:21+40,02", TRUE, TRUE, TRUE, "2014-08-05T04:00:21+10", 600, 120, NWT_UNKNOWN }, { "^TIME: XX/XX/XX,XX:XX:XX+XX,XX", FALSE, TRUE, FALSE, NULL, NWT_UNKNOWN, NWT_UNKNOWN, NWT_UNKNOWN }, { "^TIME: 14/08/05,04:00:21+40,00", FALSE, TRUE, FALSE, NULL, NWT_UNKNOWN, NWT_UNKNOWN, NWT_UNKNOWN }, { NULL, FALSE, FALSE, FALSE, NULL, NWT_UNKNOWN, NWT_UNKNOWN, NWT_UNKNOWN } }; static void test_nwtime (void) { guint i; for (i = 0; nwtime_tests[i].str; i++) { GError *error = NULL; gchar *iso8601 = NULL; MMNetworkTimezone *tz = NULL; gboolean ret; ret = mm_huawei_parse_nwtime_response (nwtime_tests[i].str, nwtime_tests[i].test_iso8601 ? &iso8601 : NULL, nwtime_tests[i].test_tz ? &tz : NULL, &error); g_assert (ret == nwtime_tests[i].ret); g_assert (ret == (error ? FALSE : TRUE)); g_clear_error (&error); if (nwtime_tests[i].test_iso8601) g_assert_cmpstr (nwtime_tests[i].iso8601, ==, iso8601); if (nwtime_tests[i].test_tz) { g_assert (nwtime_tests[i].offset == mm_network_timezone_get_offset (tz)); g_assert (nwtime_tests[i].dst_offset == mm_network_timezone_get_dst_offset (tz)); g_assert (nwtime_tests[i].leap_seconds == mm_network_timezone_get_leap_seconds (tz)); } g_free (iso8601); if (tz) g_object_unref (tz); } } /*****************************************************************************/ /* Test ^TIME responses */ typedef struct { const gchar *str; gboolean ret; const gchar *iso8601; } TimeTest; static const TimeTest time_tests[] = { { "^TIME: 14/08/05 04:00:21", TRUE, "2014-08-05T04:00:21Z" }, { "^TIME: 2014/08/05 04:00:21", TRUE, "2014-08-05T04:00:21Z" }, { "^TIME: 14-08-05 04:00:21", FALSE, NULL }, { "^TIME: 14-08-05,04:00:21", FALSE, NULL }, { "^TIME: 14/08/05 04:00:21 AEST", FALSE, NULL }, { NULL, FALSE, NULL } }; static void test_time (void) { guint i; for (i = 0; time_tests[i].str; i++) { GError *error = NULL; gchar *iso8601 = NULL; gboolean ret; ret = mm_huawei_parse_time_response (time_tests[i].str, &iso8601, NULL, &error); g_assert (ret == time_tests[i].ret); g_assert (ret == (error ? FALSE : TRUE)); g_clear_error (&error); g_assert_cmpstr (time_tests[i].iso8601, ==, iso8601); g_free (iso8601); } } /*****************************************************************************/ /* Test ^HCSQ responses */ typedef struct { const gchar *str; gboolean ret; MMModemAccessTechnology act; guint value1; guint value2; guint value3; guint value4; guint value5; } HcsqTest; static const HcsqTest hcsq_tests[] = { { "^HCSQ:\"LTE\",30,19,66,0\r\n", TRUE, MM_MODEM_ACCESS_TECHNOLOGY_LTE, 30, 19, 66, 0, 0 }, { "^HCSQ: \"WCDMA\",30,30,58\r\n", TRUE, MM_MODEM_ACCESS_TECHNOLOGY_UMTS, 30, 30, 58, 0, 0 }, { "^HCSQ: \"GSM\",36,255\r\n", TRUE, MM_MODEM_ACCESS_TECHNOLOGY_GSM, 36, 255, 0, 0, 0 }, { "^HCSQ: LTE,33,40,135,11\r\n", TRUE, MM_MODEM_ACCESS_TECHNOLOGY_LTE, 33, 40, 135, 11, 0 }, { "^HCSQ: \"NOSERVICE\"\r\n", FALSE, MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN, 0, 0, 0, 0, 0 }, { "^HCSQ: NOSERVICE\r\n", FALSE, MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN, 0, 0, 0, 0, 0 }, { NULL, FALSE, MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN, 0, 0, 0, 0, 0 } }; static void test_hcsq (void) { guint i; for (i = 0; hcsq_tests[i].str; i++) { GError *error = NULL; MMModemAccessTechnology act; guint value1 = 0; guint value2 = 0; guint value3 = 0; guint value4 = 0; guint value5 = 0; gboolean ret; ret = mm_huawei_parse_hcsq_response (hcsq_tests[i].str, &act, &value1, &value2, &value3, &value4, &value5, &error); g_assert (ret == hcsq_tests[i].ret); if (ret) { g_assert_no_error (error); g_assert_cmpint (hcsq_tests[i].act, ==, act); g_assert_cmpint (hcsq_tests[i].value1, ==, value1); g_assert_cmpint (hcsq_tests[i].value2, ==, value2); g_assert_cmpint (hcsq_tests[i].value3, ==, value3); g_assert_cmpint (hcsq_tests[i].value4, ==, value4); g_assert_cmpint (hcsq_tests[i].value5, ==, value5); } else g_assert (error); g_clear_error (&error); } } /*****************************************************************************/ /* Test ^GETPORTMODE response */ typedef struct { const gchar *str; guint n_modes; MMHuaweiPortMode modes[8]; } GetportmodeTest; static const GetportmodeTest getportmode_tests[] = { { "^GETPORTMODE: TYPE: WCDMA: huawei,PCUI:0,MDM:1", 2, { MM_HUAWEI_PORT_MODE_PCUI, MM_HUAWEI_PORT_MODE_MODEM } }, { "^GETPORTMODE: TYPE: WCDMA: huawei,MDM:0,PCUI:1,NDIS:2,CDROM:3,SD:4,", 5, { MM_HUAWEI_PORT_MODE_MODEM, MM_HUAWEI_PORT_MODE_PCUI, MM_HUAWEI_PORT_MODE_NET, MM_HUAWEI_PORT_MODE_CDROM, MM_HUAWEI_PORT_MODE_SD } }, { "^GETPORTMODE: TYPE: WCDMA: huawei,MDM:0,PCUI:1,NDIS:2,GPS:3,BT:4,", 5, { MM_HUAWEI_PORT_MODE_MODEM, MM_HUAWEI_PORT_MODE_PCUI, MM_HUAWEI_PORT_MODE_NET, MM_HUAWEI_PORT_MODE_GPS, MM_HUAWEI_PORT_MODE_BT } }, { "^GETPORTMODE: TYPE: WCDMA: huawei,PCUI:0,MDM:1,NDIS:2,CDROM:3,SD:4,GPS:5,BT:6", 7, { MM_HUAWEI_PORT_MODE_PCUI, MM_HUAWEI_PORT_MODE_MODEM, MM_HUAWEI_PORT_MODE_NET, MM_HUAWEI_PORT_MODE_CDROM, MM_HUAWEI_PORT_MODE_SD, MM_HUAWEI_PORT_MODE_GPS, MM_HUAWEI_PORT_MODE_BT } }, { "^getportmode:type:WCDMA:Qualcomm,NDIS:0,DIAG:1,PCUI:2,MDM:3,SD:4", 5, { MM_HUAWEI_PORT_MODE_NET, MM_HUAWEI_PORT_MODE_DIAG, MM_HUAWEI_PORT_MODE_PCUI, MM_HUAWEI_PORT_MODE_MODEM, MM_HUAWEI_PORT_MODE_SD } }, { "^GETPORTMODE: TYPE: WCDMA: ,pcui:1,modem:2,ncm:3,mass:4,mass_two:5,", 5, { MM_HUAWEI_PORT_MODE_PCUI, MM_HUAWEI_PORT_MODE_MODEM, MM_HUAWEI_PORT_MODE_NET, MM_HUAWEI_PORT_MODE_SD, MM_HUAWEI_PORT_MODE_SD } }, { "^GETPORTMODE: TYPE: WCDMA: huawei ,, rndis: 0, pcui: 1, c_shell: 2, a_shell: 3,3g_diag: 4, gps: 5, 4g_diag: 6, mass_two: 7", 8, { MM_HUAWEI_PORT_MODE_NET, MM_HUAWEI_PORT_MODE_PCUI, MM_HUAWEI_PORT_MODE_SHELL, MM_HUAWEI_PORT_MODE_SHELL, MM_HUAWEI_PORT_MODE_DIAG, MM_HUAWEI_PORT_MODE_GPS, MM_HUAWEI_PORT_MODE_DIAG, MM_HUAWEI_PORT_MODE_SD } }, { "^GETPORTMODE: TYPE: WCDMA: huawei,ecm:1,pcui:2,c_shell:3,a_shell:4,3g_diag:5,gps:6,4g_diag:7,mass:8,", 8, { MM_HUAWEI_PORT_MODE_NET, MM_HUAWEI_PORT_MODE_PCUI, MM_HUAWEI_PORT_MODE_SHELL, MM_HUAWEI_PORT_MODE_SHELL, MM_HUAWEI_PORT_MODE_DIAG, MM_HUAWEI_PORT_MODE_GPS, MM_HUAWEI_PORT_MODE_DIAG, MM_HUAWEI_PORT_MODE_SD } }, { "^GETPORTMODE: TYPE: WCDMA: huawei,rndis:1,pcui:2,c_shell:3,a_shell:4,3g_diag:5,gps:6,4g_diag:7,mass:8,", 8, { MM_HUAWEI_PORT_MODE_NET, MM_HUAWEI_PORT_MODE_PCUI, MM_HUAWEI_PORT_MODE_SHELL, MM_HUAWEI_PORT_MODE_SHELL, MM_HUAWEI_PORT_MODE_DIAG, MM_HUAWEI_PORT_MODE_GPS, MM_HUAWEI_PORT_MODE_DIAG, MM_HUAWEI_PORT_MODE_SD } }, { "^GETPORTMODE: TYPE: WCDMA: huawei,,pcui:0,3g_modem:1,ncm:2,mass:3,mass_two:4", 5, { MM_HUAWEI_PORT_MODE_PCUI, MM_HUAWEI_PORT_MODE_MODEM, MM_HUAWEI_PORT_MODE_NET, MM_HUAWEI_PORT_MODE_SD, MM_HUAWEI_PORT_MODE_SD } }, { "^GETPORTMODE:TYPE:WCDMA:Qualcomm,MDM:0,NDIS:1,DIAG:2,PCUI:3,CDROM:4,SD:5", 6, { MM_HUAWEI_PORT_MODE_MODEM, MM_HUAWEI_PORT_MODE_NET, MM_HUAWEI_PORT_MODE_DIAG, MM_HUAWEI_PORT_MODE_PCUI, MM_HUAWEI_PORT_MODE_CDROM, MM_HUAWEI_PORT_MODE_SD } }, { "^GETPORTMODE: TYPE: WCDMA: Huawei Technologies Co.,Ltd.,", 0 }, }; static void test_getportmode (void) { guint i; for (i = 0; i < G_N_ELEMENTS (getportmode_tests); i++) { g_autoptr(GArray) modes = NULL; g_autoptr(GError) error = NULL; mm_obj_dbg (NULL, "testing ^GETPORTMODE response: '%s'", getportmode_tests[i].str); modes = mm_huawei_parse_getportmode_response (getportmode_tests[i].str, NULL, &error); if (modes) { guint j; g_assert_no_error (error); g_assert_cmpuint (modes->len, ==, getportmode_tests[i].n_modes); for (j = 0; j < getportmode_tests[i].n_modes; j++) g_assert_cmpuint (g_array_index (modes, MMHuaweiPortMode, j), ==, getportmode_tests[i].modes[j]); } else g_assert (error); } } /*****************************************************************************/ int main (int argc, char **argv) { setlocale (LC_ALL, ""); g_test_init (&argc, &argv, NULL); g_test_add_func ("/MM/huawei/ndisstatqry", test_ndisstatqry); g_test_add_func ("/MM/huawei/dhcp", test_dhcp); g_test_add_func ("/MM/huawei/sysinfo", test_sysinfo); g_test_add_func ("/MM/huawei/sysinfoex", test_sysinfoex); g_test_add_func ("/MM/huawei/prefmode", test_prefmode); g_test_add_func ("/MM/huawei/prefmode/response", test_prefmode_response); g_test_add_func ("/MM/huawei/syscfg", test_syscfg); g_test_add_func ("/MM/huawei/syscfg/response", test_syscfg_response); g_test_add_func ("/MM/huawei/syscfgex", test_syscfgex); g_test_add_func ("/MM/huawei/syscfgex/response", test_syscfgex_response); g_test_add_func ("/MM/huawei/nwtime", test_nwtime); g_test_add_func ("/MM/huawei/time", test_time); g_test_add_func ("/MM/huawei/hcsq", test_hcsq); g_test_add_func ("/MM/huawei/getportmode", test_getportmode); return g_test_run (); } ModemManager-1.23.4-dev/src/plugins/icera/000077500000000000000000000000001456466623000202605ustar00rootroot00000000000000ModemManager-1.23.4-dev/src/plugins/icera/mm-broadband-bearer-icera.c000066400000000000000000000725721456466623000253030ustar00rootroot00000000000000/* -*- 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) 2012 Google, Inc. * Copyright (C) 2012 Aleksander Morgado */ #include #include #include #include #include #include #include #include #define _LIBMM_INSIDE_MM #include #include "mm-broadband-bearer-icera.h" #include "mm-base-modem-at.h" #include "mm-log-object.h" #include "mm-modem-helpers.h" #include "mm-error-helpers.h" #include "mm-daemon-enums-types.h" #include "mm-modem-helpers-icera.h" G_DEFINE_TYPE (MMBroadbandBearerIcera, mm_broadband_bearer_icera, MM_TYPE_BROADBAND_BEARER); enum { PROP_0, PROP_DEFAULT_IP_METHOD, PROP_LAST }; static GParamSpec *properties[PROP_LAST]; struct _MMBroadbandBearerIceraPrivate { MMBearerIpMethod default_ip_method; /* Connection related */ gpointer connect_pending; guint connect_pending_id; gulong connect_cancellable_id; gulong connect_port_closed_id; /* Disconnection related */ gpointer disconnect_pending; guint disconnect_pending_id; }; /*****************************************************************************/ /* 3GPP IP config retrieval (sub-step of the 3GPP Connection sequence) */ typedef struct { MMBaseModem *modem; MMPortSerialAt *primary; guint cid; } GetIpConfig3gppContext; static void get_ip_config_context_free (GetIpConfig3gppContext *ctx) { g_object_unref (ctx->primary); g_object_unref (ctx->modem); g_free (ctx); } static gboolean get_ip_config_3gpp_finish (MMBroadbandBearer *self, GAsyncResult *res, MMBearerIpConfig **ipv4_config, MMBearerIpConfig **ipv6_config, GError **error) { MMBearerConnectResult *configs; MMBearerIpConfig *ipv4, *ipv6; configs = g_task_propagate_pointer (G_TASK (res), error); if (!configs) return FALSE; ipv4 = mm_bearer_connect_result_peek_ipv4_config (configs); ipv6 = mm_bearer_connect_result_peek_ipv6_config (configs); g_assert (ipv4 || ipv6); if (ipv4_config && ipv4) *ipv4_config = g_object_ref (ipv4); if (ipv6_config && ipv6) *ipv6_config = g_object_ref (ipv6); mm_bearer_connect_result_unref (configs); return TRUE; } static void ip_config_ready (MMBaseModem *modem, GAsyncResult *res, GTask *task) { GetIpConfig3gppContext *ctx; MMBearerIpConfig *ipv4_config = NULL; MMBearerIpConfig *ipv6_config = NULL; const gchar *response; GError *error = NULL; MMBearerConnectResult *connect_result; ctx = g_task_get_task_data (task); response = mm_base_modem_at_command_full_finish (modem, res, &error); if (error) { g_task_return_error (task, error); goto out; } if (!mm_icera_parse_ipdpaddr_response (response, ctx->cid, &ipv4_config, &ipv6_config, &error)) { g_task_return_error (task, error); goto out; } if (!ipv4_config && !ipv6_config) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't get IP config: couldn't parse response '%s'", response); goto out; } connect_result = mm_bearer_connect_result_new (MM_PORT (ctx->primary), ipv4_config, ipv6_config); g_task_return_pointer (task, connect_result, (GDestroyNotify)mm_bearer_connect_result_unref); out: g_object_unref (task); g_clear_object (&ipv4_config); g_clear_object (&ipv6_config); } static void get_ip_config_3gpp (MMBroadbandBearer *_self, MMBroadbandModem *modem, MMPortSerialAt *primary, MMPortSerialAt *secondary, MMPort *data, guint cid, MMBearerIpFamily ip_family, GAsyncReadyCallback callback, gpointer user_data) { MMBroadbandBearerIcera *self = MM_BROADBAND_BEARER_ICERA (_self); GetIpConfig3gppContext *ctx; GTask *task; ctx = g_new0 (GetIpConfig3gppContext, 1); ctx->modem = g_object_ref (MM_BASE_MODEM (modem)); ctx->primary = g_object_ref (primary); ctx->cid = cid; task = g_task_new (self, NULL, callback, user_data); g_task_set_task_data (task, ctx, (GDestroyNotify)get_ip_config_context_free); if (self->priv->default_ip_method == MM_BEARER_IP_METHOD_STATIC) { gchar *command; command = g_strdup_printf ("%%IPDPADDR=%u", cid); mm_base_modem_at_command_full (MM_BASE_MODEM (modem), primary, command, 3, FALSE, FALSE, /* raw */ NULL, /* cancellable */ (GAsyncReadyCallback)ip_config_ready, task); g_free (command); return; } /* Otherwise, DHCP */ if (self->priv->default_ip_method == MM_BEARER_IP_METHOD_DHCP) { MMBearerConnectResult *connect_result; MMBearerIpConfig *ipv4_config = NULL, *ipv6_config = NULL; if (ip_family & MM_BEARER_IP_FAMILY_IPV4 || ip_family & MM_BEARER_IP_FAMILY_IPV4V6) { ipv4_config = mm_bearer_ip_config_new (); mm_bearer_ip_config_set_method (ipv4_config, MM_BEARER_IP_METHOD_DHCP); } if (ip_family & MM_BEARER_IP_FAMILY_IPV6 || ip_family & MM_BEARER_IP_FAMILY_IPV4V6) { ipv6_config = mm_bearer_ip_config_new (); mm_bearer_ip_config_set_method (ipv6_config, MM_BEARER_IP_METHOD_DHCP); } g_assert (ipv4_config || ipv6_config); connect_result = mm_bearer_connect_result_new (MM_PORT (ctx->primary), ipv4_config, ipv6_config); g_clear_object (&ipv4_config); g_clear_object (&ipv6_config); g_task_return_pointer (task, connect_result, (GDestroyNotify)mm_bearer_connect_result_unref); g_object_unref (task); return; } g_assert_not_reached (); } /*****************************************************************************/ /* 3GPP disconnection */ static gboolean disconnect_3gpp_finish (MMBroadbandBearer *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static gboolean disconnect_3gpp_timed_out_cb (MMBroadbandBearerIcera *self) { GTask *task; /* Recover disconnection task */ task = self->priv->disconnect_pending; self->priv->disconnect_pending = NULL; self->priv->disconnect_pending_id = 0; g_task_return_new_error (task, MM_SERIAL_ERROR, MM_SERIAL_ERROR_RESPONSE_TIMEOUT, "Disconnection attempt timed out"); g_object_unref (task); return G_SOURCE_REMOVE; } static void process_pending_disconnect_attempt (MMBroadbandBearerIcera *self, MMBearerConnectionStatus status) { GTask *task; /* Recover disconnection task */ task = self->priv->disconnect_pending; self->priv->disconnect_pending = NULL; g_assert (task != NULL); /* Cleanup timeout, if any */ if (self->priv->disconnect_pending_id) { g_source_remove (self->priv->disconnect_pending_id); self->priv->disconnect_pending_id = 0; } /* Received 'CONNECTED' during a disconnection attempt? */ if (status == MM_BEARER_CONNECTION_STATUS_CONNECTED) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Disconnection failed"); g_object_unref (task); return; } /* Received 'DISCONNECTED' during a disconnection attempt? */ if (status == MM_BEARER_CONNECTION_STATUS_DISCONNECTED || status == MM_BEARER_CONNECTION_STATUS_CONNECTION_FAILED) { g_task_return_boolean (task, TRUE); g_object_unref (task); return; } /* No other status is expected by this implementation */ g_assert_not_reached (); } static void disconnect_ipdpact_ready (MMBaseModem *modem, GAsyncResult *res, MMBroadbandBearerIcera *self) { GError *error = NULL; GTask *task; /* Try to recover the disconnection task. If none found, it means the * task was already completed and we have nothing else to do. */ task = g_steal_pointer (&self->priv->disconnect_pending); if (!task) { mm_obj_dbg (self, "disconnection context was finished already by an unsolicited message"); /* Run _finish() to finalize the async call, even if we don't care * about the result */ mm_base_modem_at_command_full_finish (modem, res, NULL); goto out; } mm_base_modem_at_command_full_finish (modem, res, &error); if (error) { g_task_return_error (task, error); g_object_unref (task); goto out; } /* Track again */ self->priv->disconnect_pending = task; /* Set a 60-second disconnection-failure timeout */ self->priv->disconnect_pending_id = g_timeout_add_seconds (60, (GSourceFunc)disconnect_3gpp_timed_out_cb, self); out: /* Balance refcount with the extra ref we passed to command_full() */ g_object_unref (self); } static void disconnect_3gpp (MMBroadbandBearer *bearer, MMBroadbandModem *modem, MMPortSerialAt *primary, MMPortSerialAt *secondary, MMPort *data, guint cid, GAsyncReadyCallback callback, gpointer user_data) { MMBroadbandBearerIcera *self = MM_BROADBAND_BEARER_ICERA (bearer); gchar *command; GTask *task; task = g_task_new (self, NULL, callback, user_data); /* The unsolicited response to %IPDPACT may come before the OK does. * We will keep the disconnection task in the bearer private data so * that it is accessible from the unsolicited message handler. Note * also that we do NOT pass the task to the GAsyncReadyCallback, as it * may not be valid any more when the callback is called (it may be * already completed in the unsolicited handling) */ g_assert (self->priv->disconnect_pending == NULL); self->priv->disconnect_pending = task; command = g_strdup_printf ("%%IPDPACT=%d,0", cid); mm_base_modem_at_command_full ( MM_BASE_MODEM (modem), primary, command, MM_BASE_BEARER_DEFAULT_DISCONNECTION_TIMEOUT, FALSE, FALSE, /* raw */ NULL, /* cancellable */ (GAsyncReadyCallback)disconnect_ipdpact_ready, g_object_ref (self)); /* we pass the bearer object! */ g_free (command); } /*****************************************************************************/ /* 3GPP Dialing (sub-step of the 3GPP Connection sequence) */ typedef struct { MMBaseModem *modem; MMPortSerialAt *primary; guint cid; MMPort *data; guint authentication_retries; GError *saved_error; } Dial3gppContext; static void dial_3gpp_context_free (Dial3gppContext *ctx) { g_assert (!ctx->saved_error); g_clear_object (&ctx->data); g_clear_object (&ctx->primary); g_clear_object (&ctx->modem); g_slice_free (Dial3gppContext, ctx); } static guint dial_3gpp_get_connecting_cid (GTask *task) { Dial3gppContext *ctx; ctx = g_task_get_task_data (task); return ctx->cid; } static MMPort * dial_3gpp_finish (MMBroadbandBearer *self, GAsyncResult *res, GError **error) { return MM_PORT (g_task_propagate_pointer (G_TASK (res), error)); } static void connect_reset_ready (MMBaseModem *modem, GAsyncResult *res, GTask *task) { Dial3gppContext *ctx; ctx = g_task_get_task_data (task); mm_base_modem_at_command_full_finish (modem, res, NULL); /* When reset is requested, it was either cancelled or an error was stored */ if (!g_task_return_error_if_cancelled (task)) { g_assert (ctx->saved_error); g_task_return_error (task, ctx->saved_error); ctx->saved_error = NULL; } g_object_unref (task); } static void connect_reset (GTask *task) { Dial3gppContext *ctx; gchar *command; ctx = g_task_get_task_data (task); /* Need to reset the connection attempt */ command = g_strdup_printf ("%%IPDPACT=%d,0", ctx->cid); mm_base_modem_at_command_full (ctx->modem, ctx->primary, command, MM_BASE_BEARER_DEFAULT_DISCONNECTION_TIMEOUT, FALSE, FALSE, /* raw */ NULL, /* cancellable */ (GAsyncReadyCallback)connect_reset_ready, task); g_free (command); } static gboolean connect_timed_out_cb (MMBroadbandBearerIcera *self) { GTask *task; Dial3gppContext *ctx; /* Cleanup timeout ID */ self->priv->connect_pending_id = 0; /* Recover task and own it */ task = self->priv->connect_pending; self->priv->connect_pending = NULL; g_assert (task); ctx = g_task_get_task_data (task); /* Remove closed port watch, if found */ if (self->priv->connect_port_closed_id) { g_signal_handler_disconnect (ctx->primary, self->priv->connect_port_closed_id); self->priv->connect_port_closed_id = 0; } /* Setup error to return after the reset */ g_assert (!ctx->saved_error); ctx->saved_error = g_error_new (MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_NETWORK_TIMEOUT, "Connection attempt timed out"); /* It's probably pointless to try to reset this here, but anyway... */ connect_reset (task); return G_SOURCE_REMOVE; } static void ier_query_ready (MMBaseModem *modem, GAsyncResult *res, GTask *task) { MMBroadbandBearerIcera *self; const gchar *response; GError *activation_error = NULL; self = g_task_get_source_object (task); response = mm_base_modem_at_command_full_finish (modem, res, NULL); if (response) { gint nw_activation_err; response = mm_strip_tag (response, "%IER:"); if (sscanf (response, "%*d,%*d,%d", &nw_activation_err)) { /* 3GPP TS 24.008 Annex G error codes: * 27 - Unknown or missing access point name * 33 - Requested service option not subscribed */ if (nw_activation_err == 27 || nw_activation_err == 33) activation_error = mm_mobile_equipment_error_for_code (MM_MOBILE_EQUIPMENT_ERROR_SERVICE_OPTION_NOT_SUBSCRIBED, self); } } if (activation_error) g_task_return_error (task, activation_error); else g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Call setup failed"); g_object_unref (task); } static void process_pending_connect_attempt (MMBroadbandBearerIcera *self, MMBearerConnectionStatus status) { GTask *task; Dial3gppContext *ctx; /* Recover task and remove both cancellation and timeout (if any)*/ g_assert (self->priv->connect_pending); task = self->priv->connect_pending; self->priv->connect_pending = NULL; ctx = g_task_get_task_data (task); if (self->priv->connect_pending_id) { g_source_remove (self->priv->connect_pending_id); self->priv->connect_pending_id = 0; } if (self->priv->connect_port_closed_id) { g_signal_handler_disconnect (ctx->primary, self->priv->connect_port_closed_id); self->priv->connect_port_closed_id = 0; } /* Received 'CONNECTED' during a connection attempt? */ if (status == MM_BEARER_CONNECTION_STATUS_CONNECTED) { /* If we wanted to get cancelled before, do it now. */ if (g_cancellable_is_cancelled (g_task_get_cancellable (task))) { connect_reset (task); return; } g_task_return_pointer (task, g_object_ref (ctx->data), g_object_unref); g_object_unref (task); return; } /* If we wanted to get cancelled before and now we couldn't connect, * use the cancelled error and return */ if (g_task_return_error_if_cancelled (task)) { g_object_unref (task); return; } /* Received CONNECTION_FAILED during a connection attempt? */ if (status == MM_BEARER_CONNECTION_STATUS_CONNECTION_FAILED) { /* Try to gather additional info about the connection failure */ mm_base_modem_at_command_full ( ctx->modem, ctx->primary, "%IER?", 60, FALSE, FALSE, /* raw */ NULL, /* cancellable */ (GAsyncReadyCallback) ier_query_ready, task); return; } /* Otherwise, received 'DISCONNECTED' during a connection attempt? */ g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Call setup failed"); g_object_unref (task); } static void forced_close_cb (MMBroadbandBearerIcera *self) { /* Just treat the forced close event as any other unsolicited message */ mm_base_bearer_report_connection_status (MM_BASE_BEARER (self), MM_BEARER_CONNECTION_STATUS_CONNECTION_FAILED); } static void activate_ready (MMBaseModem *modem, GAsyncResult *res, MMBroadbandBearerIcera *self) { GTask *task; Dial3gppContext *ctx; GError *error = NULL; task = g_steal_pointer (&self->priv->connect_pending); /* Try to recover the connection context. If none found, it means the * context was already completed and we have nothing else to do. */ if (!task) { mm_obj_dbg (self, "connection context was finished already by an unsolicited message"); /* Run _finish() to finalize the async call, even if we don't care * the result */ mm_base_modem_at_command_full_finish (modem, res, NULL); goto out; } /* Errors on the dial command are fatal */ if (!mm_base_modem_at_command_full_finish (modem, res, &error)) { g_task_return_error (task, error); g_object_unref (task); goto out; } /* Track again */ self->priv->connect_pending = task; /* We will now setup a timeout and keep the context in the bearer's private. * Reports of modem being connected will arrive via unsolicited messages. * This timeout should be long enough. Actually... ideally should never get * reached. */ self->priv->connect_pending_id = g_timeout_add_seconds (MM_BASE_BEARER_DEFAULT_CONNECTION_TIMEOUT, (GSourceFunc)connect_timed_out_cb, self); /* If we get the port closed, we treat as a connect error */ ctx = g_task_get_task_data (task); self->priv->connect_port_closed_id = g_signal_connect_swapped (ctx->primary, "forced-close", G_CALLBACK (forced_close_cb), self); out: /* Balance refcount with the extra ref we passed to command_full() */ g_object_unref (self); } static void dial_3gpp (MMBroadbandBearer *_self, MMBaseModem *modem, MMPortSerialAt *primary, guint cid, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { MMBroadbandBearerIcera *self = MM_BROADBAND_BEARER_ICERA (_self); GTask *task; Dial3gppContext *ctx; g_autofree gchar *cmd = NULL; g_assert (primary != NULL); task = g_task_new (self, cancellable, callback, user_data); ctx = g_slice_new0 (Dial3gppContext); ctx->modem = g_object_ref (modem); ctx->primary = g_object_ref (primary); ctx->cid = cid; g_task_set_task_data (task, ctx, (GDestroyNotify)dial_3gpp_context_free); /* We need a net data port */ ctx->data = mm_base_modem_get_best_data_port (modem, MM_PORT_TYPE_NET); if (!ctx->data) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_NOT_FOUND, "No valid data port found to launch connection"); g_object_unref (task); return; } /* The unsolicited response to %IPDPACT may come before the OK does. * We will keep the connection context in the bearer private data so * that it is accessible from the unsolicited message handler. Note * also that we do NOT pass the ctx to the GAsyncReadyCallback, as it * may not be valid any more when the callback is called (it may be * already completed in the unsolicited handling) */ g_assert (self->priv->connect_pending == NULL); self->priv->connect_pending = task; cmd = g_strdup_printf ("%%IPDPACT=%d,1", ctx->cid); mm_base_modem_at_command_full (ctx->modem, ctx->primary, cmd, MM_BASE_BEARER_DEFAULT_CONNECTION_TIMEOUT, FALSE, FALSE, /* raw */ NULL, /* cancellable */ (GAsyncReadyCallback) activate_ready, g_object_ref (self)); /* we pass the bearer object! */ } /*****************************************************************************/ gint mm_broadband_bearer_icera_get_connecting_profile_id (MMBroadbandBearerIcera *self) { return (self->priv->connect_pending ? (gint)dial_3gpp_get_connecting_cid (self->priv->connect_pending) : MM_3GPP_PROFILE_ID_UNKNOWN); } /*****************************************************************************/ static void report_connection_status (MMBaseBearer *_self, MMBearerConnectionStatus status, const GError *connection_error) { MMBroadbandBearerIcera *self = MM_BROADBAND_BEARER_ICERA (_self); g_assert (status == MM_BEARER_CONNECTION_STATUS_CONNECTED || status == MM_BEARER_CONNECTION_STATUS_CONNECTION_FAILED || status == MM_BEARER_CONNECTION_STATUS_DISCONNECTED); /* Process pending connection attempt */ if (self->priv->connect_pending) { process_pending_connect_attempt (self, status); return; } /* Process pending disconnection attempt */ if (self->priv->disconnect_pending) { process_pending_disconnect_attempt (self, status); return; } mm_obj_dbg (self, "received spontaneous %%IPDPACT (%s)", mm_bearer_connection_status_get_string (status)); /* Received a random 'DISCONNECTED'...*/ if (status == MM_BEARER_CONNECTION_STATUS_DISCONNECTED || status == MM_BEARER_CONNECTION_STATUS_CONNECTION_FAILED) { /* If no connection/disconnection attempt on-going, make sure we mark ourselves as * disconnected. Make sure we only pass 'DISCONNECTED' to the parent */ MM_BASE_BEARER_CLASS (mm_broadband_bearer_icera_parent_class)->report_connection_status ( _self, MM_BEARER_CONNECTION_STATUS_DISCONNECTED, connection_error); } } /*****************************************************************************/ MMBaseBearer * mm_broadband_bearer_icera_new_finish (GAsyncResult *res, GError **error) { GObject *source; GObject *bearer; source = g_async_result_get_source_object (res); bearer = g_async_initable_new_finish (G_ASYNC_INITABLE (source), res, error); g_object_unref (source); if (!bearer) return NULL; /* Only export valid bearers */ mm_base_bearer_export (MM_BASE_BEARER (bearer)); return MM_BASE_BEARER (bearer); } void mm_broadband_bearer_icera_new (MMBroadbandModem *modem, MMBearerIpMethod ip_method, MMBearerProperties *config, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { g_async_initable_new_async ( MM_TYPE_BROADBAND_BEARER_ICERA, G_PRIORITY_DEFAULT, cancellable, callback, user_data, MM_BASE_BEARER_MODEM, modem, MM_BASE_BEARER_CONFIG, config, MM_BROADBAND_BEARER_ICERA_DEFAULT_IP_METHOD, ip_method, NULL); } static void set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { MMBroadbandBearerIcera *self = MM_BROADBAND_BEARER_ICERA (object); switch (prop_id) { case PROP_DEFAULT_IP_METHOD: self->priv->default_ip_method = g_value_get_enum (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) { MMBroadbandBearerIcera *self = MM_BROADBAND_BEARER_ICERA (object); switch (prop_id) { case PROP_DEFAULT_IP_METHOD: g_value_set_enum (value, self->priv->default_ip_method); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void mm_broadband_bearer_icera_init (MMBroadbandBearerIcera *self) { /* Initialize private data */ self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, MM_TYPE_BROADBAND_BEARER_ICERA, MMBroadbandBearerIceraPrivate); /* Defaults */ self->priv->default_ip_method = MM_BEARER_IP_METHOD_STATIC; } static void mm_broadband_bearer_icera_class_init (MMBroadbandBearerIceraClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); MMBaseBearerClass *base_bearer_class = MM_BASE_BEARER_CLASS (klass); MMBroadbandBearerClass *broadband_bearer_class = MM_BROADBAND_BEARER_CLASS (klass); g_type_class_add_private (object_class, sizeof (MMBroadbandBearerIceraPrivate)); object_class->get_property = get_property; object_class->set_property = set_property; base_bearer_class->report_connection_status = report_connection_status; base_bearer_class->load_connection_status = NULL; base_bearer_class->load_connection_status_finish = NULL; #if defined WITH_SUSPEND_RESUME base_bearer_class->reload_connection_status = NULL; base_bearer_class->reload_connection_status_finish = NULL; #endif broadband_bearer_class->dial_3gpp = dial_3gpp; broadband_bearer_class->dial_3gpp_finish = dial_3gpp_finish; broadband_bearer_class->get_ip_config_3gpp = get_ip_config_3gpp; broadband_bearer_class->get_ip_config_3gpp_finish = get_ip_config_3gpp_finish; broadband_bearer_class->disconnect_3gpp = disconnect_3gpp; broadband_bearer_class->disconnect_3gpp_finish = disconnect_3gpp_finish; properties[PROP_DEFAULT_IP_METHOD] = g_param_spec_enum (MM_BROADBAND_BEARER_ICERA_DEFAULT_IP_METHOD, "Default IP method", "Default IP Method (static or DHCP) to use.", MM_TYPE_BEARER_IP_METHOD, MM_BEARER_IP_METHOD_STATIC, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY); g_object_class_install_property (object_class, PROP_DEFAULT_IP_METHOD, properties[PROP_DEFAULT_IP_METHOD]); } ModemManager-1.23.4-dev/src/plugins/icera/mm-broadband-bearer-icera.h000066400000000000000000000057341456466623000253040ustar00rootroot00000000000000/* -*- 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: * * Author: Nathan Williams * * Copyright (C) 2012 Google, Inc. * Copyright (C) 2012 Aleksander Morgado */ #ifndef MM_BROADBAND_BEARER_ICERA_H #define MM_BROADBAND_BEARER_ICERA_H #include #include #define _LIBMM_INSIDE_MM #include #include "mm-broadband-bearer.h" #define MM_TYPE_BROADBAND_BEARER_ICERA (mm_broadband_bearer_icera_get_type ()) #define MM_BROADBAND_BEARER_ICERA(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_BROADBAND_BEARER_ICERA, MMBroadbandBearerIcera)) #define MM_BROADBAND_BEARER_ICERA_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_BROADBAND_BEARER_ICERA, MMBroadbandBearerIceraClass)) #define MM_IS_BROADBAND_BEARER_ICERA(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_BROADBAND_BEARER_ICERA)) #define MM_IS_BROADBAND_BEARER_ICERA_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_BROADBAND_BEARER_ICERA)) #define MM_BROADBAND_BEARER_ICERA_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_BROADBAND_BEARER_ICERA, MMBroadbandBearerIceraClass)) #define MM_BROADBAND_BEARER_ICERA_DEFAULT_IP_METHOD "broadband-bearer-icera-default-ip-method" typedef struct _MMBroadbandBearerIcera MMBroadbandBearerIcera; typedef struct _MMBroadbandBearerIceraClass MMBroadbandBearerIceraClass; typedef struct _MMBroadbandBearerIceraPrivate MMBroadbandBearerIceraPrivate; struct _MMBroadbandBearerIcera { MMBroadbandBearer parent; MMBroadbandBearerIceraPrivate *priv; }; struct _MMBroadbandBearerIceraClass { MMBroadbandBearerClass parent; }; GType mm_broadband_bearer_icera_get_type (void); /* Default bearer creation implementation */ void mm_broadband_bearer_icera_new (MMBroadbandModem *modem, MMBearerIpMethod ip_method, MMBearerProperties *config, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); MMBaseBearer *mm_broadband_bearer_icera_new_finish (GAsyncResult *res, GError **error); gint mm_broadband_bearer_icera_get_connecting_profile_id (MMBroadbandBearerIcera *self); #endif /* MM_BROADBAND_BEARER_ICERA_H */ ModemManager-1.23.4-dev/src/plugins/icera/mm-broadband-modem-icera.c000066400000000000000000002346711456466623000251440ustar00rootroot00000000000000/* -*- 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 - 2012 Red Hat, Inc. * Copyright (C) 2012 Aleksander Morgado */ #include #include #include #include #include #include #include "ModemManager.h" #include "mm-serial-parsers.h" #include "mm-log-object.h" #include "mm-modem-helpers.h" #include "mm-errors-types.h" #include "mm-iface-modem.h" #include "mm-iface-modem-3gpp.h" #include "mm-iface-modem-3gpp-profile-manager.h" #include "mm-iface-modem-time.h" #include "mm-common-helpers.h" #include "mm-base-modem-at.h" #include "mm-bearer-list.h" #include "mm-broadband-bearer-icera.h" #include "mm-broadband-modem-icera.h" #include "mm-modem-helpers-icera.h" static void iface_modem_init (MMIfaceModem *iface); static void iface_modem_3gpp_init (MMIfaceModem3gpp *iface); static void iface_modem_3gpp_profile_manager_init (MMIfaceModem3gppProfileManager *iface); static void iface_modem_time_init (MMIfaceModemTime *iface); static MMIfaceModem *iface_modem_parent; static MMIfaceModem3gpp *iface_modem_3gpp_parent; static MMIfaceModem3gppProfileManager *iface_modem_3gpp_profile_manager_parent; G_DEFINE_TYPE_EXTENDED (MMBroadbandModemIcera, mm_broadband_modem_icera, MM_TYPE_BROADBAND_MODEM, 0, G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM, iface_modem_init) G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_3GPP, iface_modem_3gpp_init) G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_3GPP_PROFILE_MANAGER, iface_modem_3gpp_profile_manager_init) G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_TIME, iface_modem_time_init)) enum { PROP_0, PROP_DEFAULT_IP_METHOD, PROP_LAST }; static GParamSpec *properties[PROP_LAST]; struct _MMBroadbandModemIceraPrivate { MMBearerIpMethod default_ip_method; GRegex *nwstate_regex; GRegex *pacsp_regex; GRegex *ipdpact_regex; /* Cache of the most recent value seen by the unsolicited message handler */ MMModemAccessTechnology last_act; }; /*****************************************************************************/ /* Load supported modes (Modem interface) */ static void add_supported_mode (MMBroadbandModemIcera *self, GArray **combinations, guint mode) { MMModemModeCombination combination; switch (mode) { case 0: mm_obj_dbg (self, "2G-only mode supported"); combination.allowed = MM_MODEM_MODE_2G; combination.preferred = MM_MODEM_MODE_NONE; break; case 1: mm_obj_dbg (self, "3G-only mode supported"); combination.allowed = MM_MODEM_MODE_3G; combination.preferred = MM_MODEM_MODE_NONE; break; case 2: mm_obj_dbg (self, "2G/3G mode with 2G preferred supported"); combination.allowed = (MM_MODEM_MODE_2G | MM_MODEM_MODE_3G); combination.preferred = MM_MODEM_MODE_2G; break; case 3: mm_obj_dbg (self, "2G/3G mode with 3G preferred supported"); combination.allowed = (MM_MODEM_MODE_2G | MM_MODEM_MODE_3G); combination.preferred = MM_MODEM_MODE_3G; break; case 5: /* Any, no need to add it to the list */ return; default: mm_obj_warn (self, "unsupported mode found in %%IPSYS=?: %u", mode); return; } if (*combinations == NULL) *combinations = g_array_sized_new (FALSE, FALSE, sizeof (MMModemModeCombination), 5); g_array_append_val (*combinations, combination); } static GArray * load_supported_modes_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { GArray *combinations = NULL; const gchar *response; gchar **split = NULL; g_autoptr(GMatchInfo) match_info = NULL; g_autoptr(GRegex) r = NULL; guint i; response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, error); if (!response) return NULL; /* Reply goes like this: * AT%IPSYS=? * %IPSYS: (0-3,5),(0-3) */ r = g_regex_new ("\\%IPSYS:\\s*\\((.*)\\)\\s*,\\((.*)\\)", G_REGEX_RAW, 0, NULL); g_assert (r != NULL); g_regex_match (r, response, 0, &match_info); if (g_match_info_matches (match_info)) { g_autofree gchar *aux = NULL; aux = mm_get_string_unquoted_from_match_info (match_info, 1); if (aux) split = g_strsplit (aux, ",", -1); } if (!split) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "%%IPSYS=? response didn't match"); return NULL; } for (i = 0; split[i]; i++) { gchar *interval_separator; g_strstrip (split[i]); interval_separator = strstr (split[i], "-"); if (interval_separator) { /* Add all in interval */ gchar *first, *last; guint modefirst, modelast; first = g_strdup (split[i]); interval_separator = strstr (first, "-"); *(interval_separator++) = '\0'; last = interval_separator; if (mm_get_uint_from_str (first, &modefirst) && mm_get_uint_from_str (last, &modelast) && modefirst < modelast && modelast <= 5) { guint j; for (j = modefirst; j <= modelast; j++) add_supported_mode (MM_BROADBAND_MODEM_ICERA (self), &combinations, j); } else mm_obj_warn (self, "couldn't parse mode interval in %%IPSYS=? response: %s", split[i]); g_free (first); } else { guint mode; /* Add single */ if (mm_get_uint_from_str (split[i], &mode)) add_supported_mode (MM_BROADBAND_MODEM_ICERA (self), &combinations, mode); else mm_obj_warn (self, "couldn't parse mode in %%IPSYS=? response: %s", split[i]); } } g_strfreev (split); if (!combinations) g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "No mode combinations were parsed from the %%IPSYS=? response (%s)", response); return combinations; } static void load_supported_modes (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data) { mm_base_modem_at_command (MM_BASE_MODEM (self), "%IPSYS=?", 3, TRUE, callback, user_data); } /*****************************************************************************/ /* Load initial allowed/preferred modes (Modem interface) */ static gboolean modem_load_current_modes_finish (MMIfaceModem *self, GAsyncResult *res, MMModemMode *allowed, MMModemMode *preferred, GError **error) { const gchar *response; const gchar *str; gint mode, domain; response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, error); if (!response) return FALSE; str = mm_strip_tag (response, "%IPSYS:"); if (!sscanf (str, "%d,%d", &mode, &domain)) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't parse %%IPSYS response: '%s'", response); return FALSE; } switch (mode) { case 0: *allowed = MM_MODEM_MODE_2G; *preferred = MM_MODEM_MODE_NONE; return TRUE; case 1: *allowed = MM_MODEM_MODE_3G; *preferred = MM_MODEM_MODE_NONE; return TRUE; case 2: *allowed = (MM_MODEM_MODE_2G | MM_MODEM_MODE_3G); *preferred = MM_MODEM_MODE_2G; return TRUE; case 3: *allowed = (MM_MODEM_MODE_2G | MM_MODEM_MODE_3G); *preferred = MM_MODEM_MODE_3G; return TRUE; case 5: /* any */ *allowed = (MM_MODEM_MODE_2G | MM_MODEM_MODE_3G); *preferred = MM_MODEM_MODE_NONE; return TRUE; default: break; } g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't parse unexpected %%IPSYS response: '%s'", response); return FALSE; } static void modem_load_current_modes (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data) { mm_base_modem_at_command (MM_BASE_MODEM (self), "%IPSYS?", 3, FALSE, callback, user_data); } /*****************************************************************************/ /* Set allowed modes (Modem interface) */ static gboolean modem_set_current_modes_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void allowed_mode_update_ready (MMBaseModem *self, GAsyncResult *res, GTask *task) { GError *error = NULL; mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error); if (error) /* Let the error be critical. */ g_task_return_error (task, error); else g_task_return_boolean (task, TRUE); g_object_unref (task); } static void modem_set_current_modes (MMIfaceModem *self, MMModemMode allowed, MMModemMode preferred, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; gchar *command; gint icera_mode = -1; task = g_task_new (self, NULL, callback, user_data); /* * The core has checked the following: * - that 'allowed' are a subset of the 'supported' modes * - that 'preferred' is one mode, and a subset of 'allowed' */ if (allowed == MM_MODEM_MODE_2G) icera_mode = 0; else if (allowed == MM_MODEM_MODE_3G) icera_mode = 1; else if (allowed == (MM_MODEM_MODE_2G | MM_MODEM_MODE_3G)) { if (preferred == MM_MODEM_MODE_2G) icera_mode = 2; else if (preferred == MM_MODEM_MODE_3G) icera_mode = 3; else /* none preferred, so AUTO */ icera_mode = 5; } else if (allowed == MM_MODEM_MODE_ANY && preferred == MM_MODEM_MODE_NONE) icera_mode = 5; if (icera_mode < 0) { gchar *allowed_str; gchar *preferred_str; allowed_str = mm_modem_mode_build_string_from_mask (allowed); preferred_str = mm_modem_mode_build_string_from_mask (preferred); g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Requested mode (allowed: '%s', preferred: '%s') not " "supported by the modem.", allowed_str, preferred_str); g_object_unref (task); g_free (allowed_str); g_free (preferred_str); return; } command = g_strdup_printf ("%%IPSYS=%d", icera_mode); mm_base_modem_at_command ( MM_BASE_MODEM (self), command, 3, FALSE, (GAsyncReadyCallback)allowed_mode_update_ready, task); g_free (command); } /*****************************************************************************/ /* Icera-specific unsolicited events handling */ typedef struct { guint cid; MMBearerConnectionStatus status; } BearerListReportStatusForeachContext; static void bearer_list_report_status_foreach (MMBaseBearer *bearer, BearerListReportStatusForeachContext *ctx) { gint profile_id; gint connecting_profile_id; if (!MM_IS_BROADBAND_BEARER_ICERA (bearer)) return; /* The profile ID in the base bearer is set only once the modem is connected */ profile_id = mm_base_bearer_get_profile_id (bearer); /* The profile ID in the icera bearer is available during the connecting phase */ connecting_profile_id = mm_broadband_bearer_icera_get_connecting_profile_id (MM_BROADBAND_BEARER_ICERA (bearer)); if ((profile_id != (gint)ctx->cid) && (connecting_profile_id != (gint)ctx->cid)) return; mm_base_bearer_report_connection_status (bearer, ctx->status); } static void ipdpact_received (MMPortSerialAt *port, GMatchInfo *match_info, MMBroadbandModemIcera *self) { MMBearerList *list = NULL; BearerListReportStatusForeachContext ctx; guint cid; guint status; /* Ensure we got proper parsed values */ if (!mm_get_uint_from_match_info (match_info, 1, &cid) || !mm_get_uint_from_match_info (match_info, 2, &status)) return; /* Setup context */ ctx.cid = cid; ctx.status = MM_BEARER_CONNECTION_STATUS_UNKNOWN; switch (status) { case 0: ctx.status = MM_BEARER_CONNECTION_STATUS_DISCONNECTED; break; case 1: ctx.status = MM_BEARER_CONNECTION_STATUS_CONNECTED; break; case 2: /* activating */ break; case 3: ctx.status = MM_BEARER_CONNECTION_STATUS_CONNECTION_FAILED; break; default: mm_obj_warn (self, "unknown %%IPDPACT connect status %d", status); break; } /* If unknown status, don't try to report anything */ if (ctx.status == MM_BEARER_CONNECTION_STATUS_UNKNOWN) return; /* If empty bearer list, nothing else to do */ g_object_get (self, MM_IFACE_MODEM_BEARER_LIST, &list, NULL); if (!list) return; /* Will report status only in the bearer with the specific CID */ mm_bearer_list_foreach (list, (MMBearerListForeachFunc)bearer_list_report_status_foreach, &ctx); g_object_unref (list); } static MMModemAccessTechnology nwstate_to_act (const gchar *str) { /* small 'g' means CS, big 'G' means PS */ if (!strcmp (str, "2g")) return MM_MODEM_ACCESS_TECHNOLOGY_GSM; else if (!strcmp (str, "2G-GPRS")) return MM_MODEM_ACCESS_TECHNOLOGY_GPRS; else if (!strcmp (str, "2G-EDGE")) return MM_MODEM_ACCESS_TECHNOLOGY_EDGE; else if (!strcmp (str, "3G")) return MM_MODEM_ACCESS_TECHNOLOGY_UMTS; else if (!strcmp (str, "3g")) return MM_MODEM_ACCESS_TECHNOLOGY_UMTS; else if (!strcmp (str, "R99")) return MM_MODEM_ACCESS_TECHNOLOGY_UMTS; else if (!strcmp (str, "3G-HSDPA") || !strcmp (str, "HSDPA")) return MM_MODEM_ACCESS_TECHNOLOGY_HSDPA; else if (!strcmp (str, "3G-HSUPA") || !strcmp (str, "HSUPA")) return MM_MODEM_ACCESS_TECHNOLOGY_HSUPA; else if (!strcmp (str, "3G-HSDPA-HSUPA") || !strcmp (str, "HSDPA-HSUPA")) return MM_MODEM_ACCESS_TECHNOLOGY_HSPA; else if (!strcmp (str, "3G-HSDPA-HSUPA-HSPA+") || !strcmp (str, "HSDPA-HSUPA-HSPA+")) return MM_MODEM_ACCESS_TECHNOLOGY_HSPA_PLUS; return MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN; } static void nwstate_changed (MMPortSerialAt *port, GMatchInfo *info, MMBroadbandModemIcera *self) { gchar *str; /* * %NWSTATE: ,,,, * * shows the actual access technology in-use when a * PS connection is active. */ /* Process signal quality... */ str = g_match_info_fetch (info, 1); if (str) { gint rssi; rssi = atoi (str); rssi = CLAMP (rssi, 0, 5) * 100 / 5; g_free (str); mm_iface_modem_update_signal_quality (MM_IFACE_MODEM (self), (guint)rssi); } /* Process access technology... */ str = g_match_info_fetch (info, 4); if (!str || (strcmp (str, "-") == 0)) { g_free (str); str = g_match_info_fetch (info, 3); } if (str) { MMModemAccessTechnology act; act = nwstate_to_act (str); g_free (str); /* Cache last received value, needed for explicit access technology * query handling */ self->priv->last_act = act; mm_iface_modem_update_access_technologies (MM_IFACE_MODEM (self), act, MM_MODEM_ACCESS_TECHNOLOGY_ANY); } } static void set_unsolicited_events_handlers (MMBroadbandModemIcera *self, gboolean enable) { MMPortSerialAt *ports[2]; guint i; ports[0] = mm_base_modem_peek_port_primary (MM_BASE_MODEM (self)); ports[1] = mm_base_modem_peek_port_secondary (MM_BASE_MODEM (self)); /* Enable unsolicited events in given port */ for (i = 0; i < G_N_ELEMENTS (ports); i++) { if (!ports[i]) continue; /* Access technology related */ mm_port_serial_at_add_unsolicited_msg_handler ( ports[i], self->priv->nwstate_regex, enable ? (MMPortSerialAtUnsolicitedMsgFn)nwstate_changed : NULL, enable ? self : NULL, NULL); /* Connection status related */ mm_port_serial_at_add_unsolicited_msg_handler ( ports[i], self->priv->ipdpact_regex, enable ? (MMPortSerialAtUnsolicitedMsgFn)ipdpact_received : NULL, enable ? self : NULL, NULL); /* Always to ignore */ if (!enable) { mm_port_serial_at_add_unsolicited_msg_handler ( ports[i], self->priv->pacsp_regex, NULL, NULL, NULL); } } } /*****************************************************************************/ /* Load access technologies (Modem interface) */ static gboolean modem_load_access_technologies_finish (MMIfaceModem *self, GAsyncResult *res, MMModemAccessTechnology *access_technologies, guint *mask, GError **error) { GError *inner_error = NULL; gssize value; value = g_task_propagate_int (G_TASK (res), &inner_error); if (inner_error) { g_propagate_error (error, inner_error); return FALSE; } *access_technologies = (MMModemAccessTechnology) value; *mask = MM_MODEM_ACCESS_TECHNOLOGY_ANY; return TRUE; } static void nwstate_query_ready (MMBroadbandModemIcera *self, GAsyncResult *res, GTask *task) { GError *error = NULL; mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error); if (error) g_task_return_error (task, error); else { /* * The unsolicited message handler will already have run and * removed the NWSTATE response, so we use the result from there. */ g_task_return_int (task, self->priv->last_act); } g_object_unref (task); } static void modem_load_access_technologies (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; task = g_task_new (self, NULL, callback, user_data); mm_base_modem_at_command ( MM_BASE_MODEM (self), "%NWSTATE", 3, FALSE, (GAsyncReadyCallback)nwstate_query_ready, task); } /*****************************************************************************/ /* Setup/Cleanup unsolicited events (3GPP interface) */ static gboolean modem_3gpp_setup_cleanup_unsolicited_events_finish (MMIfaceModem3gpp *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void parent_setup_unsolicited_events_ready (MMIfaceModem3gpp *self, GAsyncResult *res, GTask *task) { GError *error = NULL; if (!iface_modem_3gpp_parent->setup_unsolicited_events_finish (self, res, &error)) g_task_return_error (task, error); else { /* Our own setup now */ set_unsolicited_events_handlers (MM_BROADBAND_MODEM_ICERA (self), TRUE); g_task_return_boolean (task, TRUE); } g_object_unref (task); } static void modem_3gpp_setup_unsolicited_events (MMIfaceModem3gpp *self, GAsyncReadyCallback callback, gpointer user_data) { /* Chain up parent's setup */ iface_modem_3gpp_parent->setup_unsolicited_events ( self, (GAsyncReadyCallback)parent_setup_unsolicited_events_ready, g_task_new (self, NULL, callback, user_data)); } static void parent_cleanup_unsolicited_events_ready (MMIfaceModem3gpp *self, GAsyncResult *res, GTask *task) { GError *error = NULL; if (!iface_modem_3gpp_parent->cleanup_unsolicited_events_finish (self, res, &error)) g_task_return_error (task, error); else g_task_return_boolean (task, TRUE); g_object_unref (task); } static void modem_3gpp_cleanup_unsolicited_events (MMIfaceModem3gpp *self, GAsyncReadyCallback callback, gpointer user_data) { /* Our own cleanup first */ set_unsolicited_events_handlers (MM_BROADBAND_MODEM_ICERA (self), FALSE); /* And now chain up parent's cleanup */ iface_modem_3gpp_parent->cleanup_unsolicited_events ( self, (GAsyncReadyCallback)parent_cleanup_unsolicited_events_ready, g_task_new (self, NULL, callback, user_data)); } /*****************************************************************************/ /* Enable/disable unsolicited events (3GPP interface) */ static gboolean modem_3gpp_enable_disable_unsolicited_events_finish (MMIfaceModem3gpp *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void own_enable_unsolicited_events_ready (MMIfaceModem3gpp *self, GAsyncResult *res, GTask *task) { GError *error = NULL; if (!mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error)) g_task_return_error (task, error); else g_task_return_boolean (task, TRUE); g_object_unref (task); } static void parent_enable_unsolicited_events_ready (MMIfaceModem3gpp *self, GAsyncResult *res, GTask *task) { GError *error = NULL; if (!iface_modem_3gpp_parent->enable_unsolicited_events_finish (self, res, &error)) { g_task_return_error (task, error); g_object_unref (task); return; } /* Our own enable now */ mm_base_modem_at_command ( MM_BASE_MODEM (self), "%NWSTATE=1", 3, FALSE, (GAsyncReadyCallback)own_enable_unsolicited_events_ready, task); } static void modem_3gpp_enable_unsolicited_events (MMIfaceModem3gpp *self, GAsyncReadyCallback callback, gpointer user_data) { /* Chain up parent's enable */ iface_modem_3gpp_parent->enable_unsolicited_events ( self, (GAsyncReadyCallback)parent_enable_unsolicited_events_ready, g_task_new (self, NULL, callback, user_data)); } static void parent_disable_unsolicited_events_ready (MMIfaceModem3gpp *self, GAsyncResult *res, GTask *task) { GError *error = NULL; if (!iface_modem_3gpp_parent->disable_unsolicited_events_finish (self, res, &error)) g_task_return_error (task, error); else g_task_return_boolean (task, TRUE); g_object_unref (task); } static void own_disable_unsolicited_events_ready (MMIfaceModem3gpp *self, GAsyncResult *res, GTask *task) { GError *error = NULL; if (!mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error)) { g_task_return_error (task, error); g_object_unref (task); return; } /* Next, chain up parent's disable */ iface_modem_3gpp_parent->disable_unsolicited_events ( MM_IFACE_MODEM_3GPP (self), (GAsyncReadyCallback)parent_disable_unsolicited_events_ready, task); } static void modem_3gpp_disable_unsolicited_events (MMIfaceModem3gpp *self, GAsyncReadyCallback callback, gpointer user_data) { mm_base_modem_at_command ( MM_BASE_MODEM (self), "%NWSTATE=0", 3, FALSE, (GAsyncReadyCallback)own_disable_unsolicited_events_ready, g_task_new (self, NULL, callback, user_data)); } /*****************************************************************************/ /* Create bearer (Modem interface) */ static MMBaseBearer * modem_create_bearer_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { return g_task_propagate_pointer (G_TASK (res), error); } static void broadband_bearer_icera_new_ready (GObject *source, GAsyncResult *res, GTask *task) { MMBaseBearer *bearer = NULL; GError *error = NULL; bearer = mm_broadband_bearer_icera_new_finish (res, &error); if (!bearer) g_task_return_error (task, error); else g_task_return_pointer (task, bearer, g_object_unref); g_object_unref (task); } static void broadband_bearer_new_ready (GObject *source, GAsyncResult *res, GTask *task) { MMBaseBearer *bearer = NULL; GError *error = NULL; bearer = mm_broadband_bearer_new_finish (res, &error); if (!bearer) g_task_return_error (task, error); else g_task_return_pointer (task, bearer, g_object_unref); g_object_unref (task); } static void modem_create_bearer (MMIfaceModem *self, MMBearerProperties *props, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; task = g_task_new (self, NULL, callback, user_data); /* If we get a NET port, create Icera bearer */ if (mm_base_modem_peek_best_data_port (MM_BASE_MODEM (self), MM_PORT_TYPE_NET)) { mm_broadband_bearer_icera_new ( MM_BROADBAND_MODEM (self), MM_BROADBAND_MODEM_ICERA (self)->priv->default_ip_method, props, NULL, /* cancellable */ (GAsyncReadyCallback)broadband_bearer_icera_new_ready, task); return; } /* Otherwise, plain generic broadband bearer */ mm_broadband_bearer_new ( MM_BROADBAND_MODEM (self), props, NULL, /* cancellable */ (GAsyncReadyCallback)broadband_bearer_new_ready, task); } /*****************************************************************************/ /* Modem power up (Modem interface) */ static gboolean modem_power_up_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void cfun_enable_ready (MMBaseModem *self, GAsyncResult *res, GTask *task) { GError *error = NULL; if (!mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error)) { /* Ignore all errors except NOT_ALLOWED, which means Airplane Mode */ if (g_error_matches (error, MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_NOT_ALLOWED)) g_task_return_error (task, error); else { g_error_free (error); g_task_return_boolean (task, TRUE); } } g_object_unref (task); } static void modem_power_up (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data) { mm_base_modem_at_command (MM_BASE_MODEM (self), "+CFUN=1", 10, FALSE, (GAsyncReadyCallback)cfun_enable_ready, g_task_new (self, NULL, callback, user_data)); } /*****************************************************************************/ /* Modem power down (Modem interface) */ static gboolean modem_power_down_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { return !!mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, error); } static void modem_power_down (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data) { /* Use AT+CFUN=4 for power down. It will stop the RF (IMSI detach), and * keeps access to the SIM */ mm_base_modem_at_command (MM_BASE_MODEM (self), "+CFUN=4", /* The modem usually completes +CFUN=4 within 1-2 seconds, * but sometimes takes a ridiculously long time (~30-35 seconds). * It's better to have a long timeout here than to have the * modem not responding to subsequent AT commands until +CFUN=4 * completes. */ 40, FALSE, callback, user_data); } /*****************************************************************************/ /* Reset (Modem interface) */ static gboolean modem_reset_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { return !!mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, error); } static void modem_reset (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data) { mm_base_modem_at_command (MM_BASE_MODEM (self), "%IRESET", 3, FALSE, callback, user_data); } /*****************************************************************************/ /* Load unlock retries (Modem interface) */ static MMUnlockRetries * modem_load_unlock_retries_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { return g_task_propagate_pointer (G_TASK (res), error); } static void load_unlock_retries_ready (MMBaseModem *self, GAsyncResult *res, GTask *task) { const gchar *response; GError *error = NULL; int pin1, puk1, pin2, puk2; response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error); if (!response) { g_task_return_error (task, error); g_object_unref (task); return; } response = mm_strip_tag (response, "%PINNUM:"); if (sscanf (response, " %d, %d, %d, %d", &pin1, &puk1, &pin2, &puk2) == 4) { MMUnlockRetries *retries; retries = mm_unlock_retries_new (); mm_unlock_retries_set (retries, MM_MODEM_LOCK_SIM_PIN, pin1); mm_unlock_retries_set (retries, MM_MODEM_LOCK_SIM_PUK, puk1); mm_unlock_retries_set (retries, MM_MODEM_LOCK_SIM_PIN2, pin2); mm_unlock_retries_set (retries, MM_MODEM_LOCK_SIM_PUK2, puk2); g_task_return_pointer (task, retries, g_object_unref); } else { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Invalid unlock retries response: '%s'", response); } g_object_unref (task); } static void modem_load_unlock_retries (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data) { mm_base_modem_at_command ( MM_BASE_MODEM (self), "%PINNUM?", 3, FALSE, (GAsyncReadyCallback)load_unlock_retries_ready, g_task_new (self, NULL, callback, user_data)); } /*****************************************************************************/ /* Generic band handling utilities */ typedef struct { MMModemBand band; char *name; gboolean enabled; } Band; static void band_free (Band *b) { g_free (b->name); g_free (b); } static const Band modem_bands[] = { /* Sort 3G first since it's preferred */ { MM_MODEM_BAND_UTRAN_1, (gchar *) "FDD_BAND_I", FALSE }, { MM_MODEM_BAND_UTRAN_2, (gchar *) "FDD_BAND_II", FALSE }, { MM_MODEM_BAND_UTRAN_3, (gchar *) "FDD_BAND_III", FALSE }, { MM_MODEM_BAND_UTRAN_4, (gchar *) "FDD_BAND_IV", FALSE }, { MM_MODEM_BAND_UTRAN_5, (gchar *) "FDD_BAND_V", FALSE }, { MM_MODEM_BAND_UTRAN_6, (gchar *) "FDD_BAND_VI", FALSE }, { MM_MODEM_BAND_UTRAN_8, (gchar *) "FDD_BAND_VIII", FALSE }, /* 2G second */ { MM_MODEM_BAND_G850, (gchar *) "G850", FALSE }, { MM_MODEM_BAND_DCS, (gchar *) "DCS", FALSE }, { MM_MODEM_BAND_EGSM, (gchar *) "EGSM", FALSE }, { MM_MODEM_BAND_PCS, (gchar *) "PCS", FALSE }, /* And ANY last since it's most inclusive */ { MM_MODEM_BAND_ANY, (gchar *) "ANY", FALSE }, }; static const guint modem_band_any_bit = 1 << (G_N_ELEMENTS (modem_bands) - 1); static MMModemBand icera_band_to_mm (const char *icera) { guint i; for (i = 0 ; i < G_N_ELEMENTS (modem_bands); i++) { if (g_strcmp0 (icera, modem_bands[i].name) == 0) return modem_bands[i].band; } return MM_MODEM_BAND_UNKNOWN; } static GSList * parse_bands (const gchar *response, guint32 *out_len) { g_autoptr(GRegex) r = NULL; g_autoptr(GMatchInfo) info = NULL; GSList *bands = NULL; g_return_val_if_fail (out_len != NULL, NULL); /* * Response is a number of lines of the form: * "EGSM": 0 * "FDD_BAND_I": 1 * ... * with 1 and 0 indicating whether the particular band is enabled or not. */ r = g_regex_new ("^\"(\\w+)\": (\\d)", G_REGEX_MULTILINE, G_REGEX_MATCH_NEWLINE_ANY, NULL); g_assert (r != NULL); g_regex_match (r, response, 0, &info); while (g_match_info_matches (info)) { gchar *name, *enabled; Band *b; MMModemBand band; name = g_match_info_fetch (info, 1); enabled = g_match_info_fetch (info, 2); band = icera_band_to_mm (name); if (band != MM_MODEM_BAND_UNKNOWN) { b = g_malloc0 (sizeof (Band)); b->band = band; b->name = g_strdup (name); b->enabled = (enabled[0] == '1' ? TRUE : FALSE); bands = g_slist_append (bands, b); *out_len = *out_len + 1; } g_free (name); g_free (enabled); g_match_info_next (info, NULL); } return bands; } /*****************************************************************************/ /* Load supported bands (Modem interface) */ typedef struct { MMBaseModemAtCommandAlloc *cmds; GSList *check_bands; GSList *enabled_bands; guint32 idx; } SupportedBandsContext; static void supported_bands_context_free (SupportedBandsContext *ctx) { guint i; for (i = 0; ctx->cmds[i].command; i++) mm_base_modem_at_command_alloc_clear (&ctx->cmds[i]); g_free (ctx->cmds); g_slist_free_full (ctx->check_bands, (GDestroyNotify) band_free); g_slist_free_full (ctx->enabled_bands, (GDestroyNotify) band_free); g_free (ctx); } static GArray * modem_load_supported_bands_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { return g_task_propagate_pointer (G_TASK (res), error); } static void load_supported_bands_ready (MMBaseModem *self, GAsyncResult *res, GTask *task) { GError *error = NULL; SupportedBandsContext *ctx = NULL; GArray *bands; GSList *iter; mm_base_modem_at_sequence_finish (self, res, (gpointer) &ctx, &error); if (error) g_task_return_error (task, error); else { bands = g_array_sized_new (FALSE, FALSE, sizeof (MMModemBand), ctx->idx); /* Add already enabled bands */ for (iter = ctx->enabled_bands; iter; iter = g_slist_next (iter)) { Band *b = iter->data; g_array_prepend_val (bands, b->band); } /* Add any checked bands that are supported */ for (iter = ctx->check_bands; iter; iter = g_slist_next (iter)) { Band *b = iter->data; /* 'enabled' here really means supported/unsupported */ if (b->enabled) g_array_prepend_val (bands, b->band); } g_task_return_pointer (task, bands, (GDestroyNotify) g_array_unref); } g_object_unref (task); } static MMBaseModemAtResponseProcessorResult load_supported_bands_response_processor (MMBaseModem *self, gpointer context, const gchar *command, const gchar *response, gboolean last_command, const GError *error, GVariant **result, GError **result_error) { SupportedBandsContext *ctx; Band *b; ctx = context; b = g_slist_nth_data (ctx->check_bands, ctx->idx++); /* If there was no error setting the band, that band is supported. We * abuse the 'enabled' item to mean supported/unsupported. */ b->enabled = !error; /* Continue to next band */ return MM_BASE_MODEM_AT_RESPONSE_PROCESSOR_RESULT_CONTINUE; } static void load_supported_bands_get_current_bands_ready (MMIfaceModem *self, GAsyncResult *res, GTask *task) { SupportedBandsContext *ctx; const gchar *response; GError *error = NULL; GSList *iter, *new; guint32 len = 0, i = 0; response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error); if (!response) { g_task_return_error (task, error); g_object_unref (task); return; } ctx = g_new0 (SupportedBandsContext, 1); /* For each reported band, build up an AT command to set that band * to its current enabled/disabled state. */ iter = ctx->check_bands = parse_bands (response, &len); ctx->cmds = g_new0 (MMBaseModemAtCommandAlloc, len + 1); while (iter) { Band *b = iter->data; if (b->enabled || b->band == MM_MODEM_BAND_ANY) { /* Move known-supported band to the enabled list */ new = g_slist_next (iter); ctx->check_bands = g_slist_remove_link (ctx->check_bands, iter); ctx->enabled_bands = g_slist_prepend (ctx->enabled_bands, iter->data); g_slist_free (iter); iter = new; } else { /* Check support for disabled band */ ctx->cmds[i].command = g_strdup_printf ("%%IPBM=\"%s\",0", b->name); ctx->cmds[i].timeout = 10; ctx->cmds[i].allow_cached = FALSE; ctx->cmds[i].response_processor = load_supported_bands_response_processor; i++; iter = g_slist_next (iter); } } mm_base_modem_at_sequence (MM_BASE_MODEM (self), (const MMBaseModemAtCommand *)ctx->cmds, ctx, (GDestroyNotify) supported_bands_context_free, (GAsyncReadyCallback) load_supported_bands_ready, task); } static void modem_load_supported_bands (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data) { /* The modems report some bands as disabled that they don't actually * support enabling. Thanks Icera! So we have to try setting each * band to it's current enabled/disabled value, and the modem will * return an error if it doesn't support that band at all. */ mm_base_modem_at_command ( MM_BASE_MODEM (self), "%IPBM?", 3, FALSE, (GAsyncReadyCallback)load_supported_bands_get_current_bands_ready, g_task_new (self, NULL, callback, user_data)); } /*****************************************************************************/ /* Load current bands (Modem interface) */ static GArray * modem_load_current_bands_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { return g_task_propagate_pointer (G_TASK (res), error); } static void load_current_bands_ready (MMIfaceModem *self, GAsyncResult *res, GTask *task) { GArray *bands; const gchar *response; GError *error = NULL; GSList *parsed, *iter; guint32 len = 0; response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error); if (!response) { g_task_return_error (task, error); } else { /* Parse bands from Icera response into MM band numbers */ parsed = parse_bands (response, &len); bands = g_array_sized_new (FALSE, FALSE, sizeof (MMModemBand), len); for (iter = parsed; iter; iter = g_slist_next (iter)) { Band *b = iter->data; if (b->enabled) g_array_append_val (bands, b->band); } g_slist_free_full (parsed, (GDestroyNotify) band_free); g_task_return_pointer (task, bands, (GDestroyNotify)g_array_unref); } g_object_unref (task); } static void modem_load_current_bands (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data) { mm_base_modem_at_command ( MM_BASE_MODEM (self), "%IPBM?", 3, FALSE, (GAsyncReadyCallback)load_current_bands_ready, g_task_new (self, NULL, callback, user_data)); } /*****************************************************************************/ /* Set current bands (Modem interface) */ typedef struct { guint bandbits; guint enablebits; guint disablebits; } SetCurrentBandsContext; /* * The modem's band-setting command (%IPBM=) enables or disables one * band at a time, and one band must always be enabled. Here, we first * get the set of enabled bands, compute the difference between that * set and the requested set, enable any added bands, and finally * disable any removed bands. */ static gboolean modem_set_current_bands_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void set_one_band (MMIfaceModem *self, GTask *task); static void set_current_bands_next (MMIfaceModem *self, GAsyncResult *res, GTask *task) { GError *error = NULL; if (!mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error)) { g_task_return_error (task, error); g_object_unref (task); return; } set_one_band (self, task); } static void set_one_band (MMIfaceModem *self, GTask *task) { SetCurrentBandsContext *ctx; guint enable, band; gchar *command; ctx = g_task_get_task_data (task); /* Find the next band to enable or disable, always doing enables first */ enable = 1; band = ffs (ctx->enablebits); if (band == 0) { enable = 0; band = ffs (ctx->disablebits); } if (band == 0) { /* Both enabling and disabling are done */ g_task_return_boolean (task, TRUE); g_object_unref (task); return; } /* Note that ffs() returning 2 corresponds to 1 << 1, not 1 << 2 */ band--; mm_obj_dbg (self, "preparing %%IPBM command (1/2): enablebits %x, disablebits %x, band %d, enable %d", ctx->enablebits, ctx->disablebits, band, enable); if (enable) ctx->enablebits &= ~(1 << band); else ctx->disablebits &= ~(1 << band); mm_obj_dbg (self, "preparing %%IPBM command (2/2): enablebits %x, disablebits %x", ctx->enablebits, ctx->disablebits); command = g_strdup_printf ("%%IPBM=\"%s\",%d", modem_bands[band].name, enable); mm_base_modem_at_command ( MM_BASE_MODEM (self), command, 10, FALSE, (GAsyncReadyCallback)set_current_bands_next, task); g_free (command); } static guint band_array_to_bandbits (GArray *bands) { MMModemBand band; guint i, j, bandbits; bandbits = 0; for (i = 0 ; i < bands->len ; i++) { band = g_array_index (bands, MMModemBand, i); for (j = 0 ; j < G_N_ELEMENTS (modem_bands) ; j++) { if (modem_bands[j].band == band) { bandbits |= 1 << j; break; } } g_assert (j < G_N_ELEMENTS (modem_bands)); } return bandbits; } static void set_current_bands_got_current_bands (MMIfaceModem *self, GAsyncResult *res, GTask *task) { SetCurrentBandsContext *ctx; GArray *bands; GError *error = NULL; guint currentbits; bands = modem_load_current_bands_finish (self, res, &error); if (!bands) { g_task_return_error (task, error); g_object_unref (task); return; } ctx = g_task_get_task_data (task); currentbits = band_array_to_bandbits (bands); ctx->enablebits = ctx->bandbits & ~currentbits; ctx->disablebits = currentbits & ~ctx->bandbits; set_one_band (self, task); } static void modem_set_current_bands (MMIfaceModem *self, GArray *bands_array, GAsyncReadyCallback callback, gpointer user_data) { SetCurrentBandsContext *ctx; GTask *task; ctx = g_new0 (SetCurrentBandsContext, 1); ctx->bandbits = band_array_to_bandbits (bands_array); task = g_task_new (self, NULL, callback, user_data); g_task_set_task_data (task, ctx, g_free); /* * If ANY is requested, simply enable ANY to activate all bands except for * those forbidden. */ if (ctx->bandbits & modem_band_any_bit) { ctx->enablebits = modem_band_any_bit; ctx->disablebits = 0; set_one_band (self, task); return; } modem_load_current_bands (self, (GAsyncReadyCallback)set_current_bands_got_current_bands, task); } /*****************************************************************************/ /* Load network timezone (Time interface) */ static gboolean parse_tlts_query_reply (const gchar *response, gchar **iso8601, MMNetworkTimezone **tz, GError **error) { gboolean ret = TRUE; gint year; gint month; gint day; gint hour; gint minute; gint second; gchar sign; gint offset; GDateTime *utc, *adjusted; /* TLTS reports UTC time with the TZ offset to *local* time */ response = mm_strip_tag (response, "*TLTS: "); if (sscanf (response, "\"%02d/%02d/%02d,%02d:%02d:%02d%c%02d\"", &year, &month, &day, &hour, &minute, &second, &sign, &offset) != 8) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Unknown *TLTS response: %s", response); return FALSE; } /* Icera modems only report a 2-digit year, while ISO-8601 requires * a 4-digit year. Assume 2000. */ if (year < 100) year += 2000; /* Offset comes in 15-min units */ offset *= 15; /* Apply sign to offset; */ if (sign == '-') offset *= -1; utc = g_date_time_new_utc (year, month, day, hour, minute, second); if (!utc) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Invalid *TLTS date/time: %s", response); return FALSE; } /* Convert UTC time to local time by adjusting by the timezone offset */ adjusted = g_date_time_add_minutes (utc, offset); g_date_time_unref (utc); if (!adjusted) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Failed to convert modem time to local time (offset %d)", offset); return FALSE; } /* Convert offset from minutes-to-UTC to minutes-from-UTC */ offset *= -1; if (tz) { *tz = mm_network_timezone_new (); mm_network_timezone_set_offset (*tz, offset); } if (iso8601) { *iso8601 = mm_new_iso8601_time (g_date_time_get_year (adjusted), g_date_time_get_month (adjusted), g_date_time_get_day_of_month (adjusted), g_date_time_get_hour (adjusted), g_date_time_get_minute (adjusted), g_date_time_get_second (adjusted), TRUE, offset, error); ret = (*iso8601 != NULL); } g_date_time_unref (adjusted); return ret; } static MMNetworkTimezone * modem_time_load_network_timezone_finish (MMIfaceModemTime *self, GAsyncResult *res, GError **error) { const gchar *response; MMNetworkTimezone *tz; response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, NULL); if (!response) { /* We'll assume we can retry a bit later */ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_RETRY, "Retry"); return NULL; } return (parse_tlts_query_reply (response, NULL, &tz, error) ? tz : NULL); } static void modem_time_load_network_timezone (MMIfaceModemTime *self, GAsyncReadyCallback callback, gpointer user_data) { mm_base_modem_at_command (MM_BASE_MODEM (self), "*TLTS", 3, FALSE, callback, user_data); } /*****************************************************************************/ /* List profiles (3GPP profile management interface) */ typedef struct { GList *profiles; } ListProfilesContext; static void list_profiles_context_free (ListProfilesContext *ctx) { mm_3gpp_profile_list_free (ctx->profiles); g_slice_free (ListProfilesContext, ctx); } static gboolean modem_3gpp_profile_manager_list_profiles_finish (MMIfaceModem3gppProfileManager *self, GAsyncResult *res, GList **out_profiles, GError **error) { ListProfilesContext *ctx; if (!g_task_propagate_boolean (G_TASK (res), error)) return FALSE; ctx = g_task_get_task_data (G_TASK (res)); if (out_profiles) *out_profiles = g_steal_pointer (&ctx->profiles); return TRUE; } static void profile_manager_ipdpcfg_query_ready (MMBaseModem *self, GAsyncResult *res, GTask *task) { ListProfilesContext *ctx; const gchar *response; g_autoptr(GError) error = NULL; ctx = g_task_get_task_data (task); response = mm_base_modem_at_command_finish (self, res, &error); if (!response) mm_obj_warn (self, "couldn't load PDP context auth settings: %s", error->message); else if (!mm_icera_parse_ipdpcfg_query_response (response, ctx->profiles, self, &error)) mm_obj_warn (self, "couldn't update profile list with PDP context auth settings: %s", error->message); /* complete successfully anyway */ g_task_return_boolean (task, TRUE); g_object_unref (task); } static void profile_manager_parent_list_profiles_ready (MMIfaceModem3gppProfileManager *self, GAsyncResult *res, GTask *task) { ListProfilesContext *ctx; GError *error = NULL; ctx = g_slice_new0 (ListProfilesContext); g_task_set_task_data (task, ctx, (GDestroyNotify) list_profiles_context_free); if (!iface_modem_3gpp_profile_manager_parent->list_profiles_finish (self, res, &ctx->profiles, &error)) { g_task_return_error (task, error); g_object_unref (task); return; } if (!ctx->profiles) { g_task_return_boolean (task, TRUE); g_object_unref (task); return; } mm_base_modem_at_command ( MM_BASE_MODEM (self), "%IPDPCFG?", 3, FALSE, (GAsyncReadyCallback)profile_manager_ipdpcfg_query_ready, task); } static void modem_3gpp_profile_manager_list_profiles (MMIfaceModem3gppProfileManager *self, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; task = g_task_new (self, NULL, callback, user_data); iface_modem_3gpp_profile_manager_parent->list_profiles ( self, (GAsyncReadyCallback)profile_manager_parent_list_profiles_ready, task); } typedef struct { gboolean new_id; gint min_profile_id; gint max_profile_id; GEqualFunc apn_cmp; MM3gppProfileCmpFlags profile_cmp_flags; } CheckFormatContext; static void check_format_context_free (CheckFormatContext *ctx) { g_slice_free (CheckFormatContext, ctx); } static gboolean modem_3gpp_profile_manager_check_format_finish (MMIfaceModem3gppProfileManager *self, GAsyncResult *res, gboolean *new_id, gint *min_profile_id, gint *max_profile_id, GEqualFunc *apn_cmp, MM3gppProfileCmpFlags *profile_cmp_flags, GError **error) { CheckFormatContext *ctx; if (!g_task_propagate_boolean (G_TASK (res), error)) return FALSE; ctx = g_task_get_task_data (G_TASK (res)); if (new_id) *new_id = ctx->new_id; if (min_profile_id) *min_profile_id = (gint) ctx->min_profile_id; if (max_profile_id) *max_profile_id = (gint) ctx->max_profile_id; if (apn_cmp) *apn_cmp = ctx->apn_cmp; if (profile_cmp_flags) *profile_cmp_flags = ctx->profile_cmp_flags; return TRUE; } static void profile_manager_parent_check_format_ready (MMIfaceModem3gppProfileManager *self, GAsyncResult *res, GTask *task) { GError *error = NULL; CheckFormatContext *ctx; ctx = g_task_get_task_data (task); if (!iface_modem_3gpp_profile_manager_parent->check_format_finish (self, res, &ctx->new_id, &ctx->min_profile_id, &ctx->max_profile_id, &ctx->apn_cmp, &ctx->profile_cmp_flags, &error)) { g_task_return_error (task, error); } else { /* the icera implementation supports AUTH, so unset that cmp flag */ ctx->profile_cmp_flags &= ~MM_3GPP_PROFILE_CMP_FLAGS_NO_AUTH; g_task_return_boolean (task, TRUE); } g_object_unref (task); } static void modem_3gpp_profile_manager_check_format (MMIfaceModem3gppProfileManager *self, MMBearerIpFamily ip_type, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; CheckFormatContext *ctx; task = g_task_new (self, NULL, callback, user_data); ctx = g_slice_new0 (CheckFormatContext); g_task_set_task_data (task, ctx, (GDestroyNotify)check_format_context_free); iface_modem_3gpp_profile_manager_parent->check_format ( self, ip_type, (GAsyncReadyCallback)profile_manager_parent_check_format_ready, task); } /*****************************************************************************/ /* Deactivate profile (3GPP profile management interface) */ static gboolean modem_3gpp_profile_manager_deactivate_profile_finish (MMIfaceModem3gppProfileManager *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void deactivate_profile_ipdpact_set_ready (MMBaseModem *self, GAsyncResult *res, GTask *task) { GError *error = NULL; if (!mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error)) g_task_return_error (task, error); else g_task_return_boolean (task, TRUE); g_object_unref (task); } static void modem_3gpp_profile_manager_deactivate_profile (MMIfaceModem3gppProfileManager *self, MM3gppProfile *profile, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; g_autofree gchar *cmd = NULL; gint profile_id; task = g_task_new (self, NULL, callback, user_data); profile_id = mm_3gpp_profile_get_profile_id (profile); mm_obj_dbg (self, "deactivating profile '%d'...", profile_id); cmd = g_strdup_printf ("%%IPDPACT=%d,0", profile_id); mm_base_modem_at_command ( MM_BASE_MODEM (self), cmd, MM_BASE_BEARER_DEFAULT_DISCONNECTION_TIMEOUT, FALSE, (GAsyncReadyCallback)deactivate_profile_ipdpact_set_ready, task); } /*****************************************************************************/ /* Set profile (3GPP profile management interface) */ #define IPDPCFG_SET_MAX_ATTEMPTS 3 #define IPDPCFG_SET_RETRY_TIMEOUT_SECS 1 typedef struct { MM3gppProfile *profile; gchar *cmd; gint profile_id; guint n_attempts; } StoreProfileContext; static void store_profile_context_free (StoreProfileContext *ctx) { g_free (ctx->cmd); g_clear_object (&ctx->profile); g_slice_free (StoreProfileContext, ctx); } static gint modem_3gpp_profile_manager_store_profile_finish (MMIfaceModem3gppProfileManager *self, GAsyncResult *res, gint *out_profile_id, MMBearerApnType *out_apn_type, GError **error) { StoreProfileContext *ctx; if (!g_task_propagate_boolean (G_TASK (res), error)) return FALSE; ctx = g_task_get_task_data (G_TASK (res)); if (out_profile_id) *out_profile_id = ctx->profile_id; if (out_apn_type) *out_apn_type = MM_BEARER_APN_TYPE_NONE; return TRUE; } static void profile_manager_store_profile_auth_settings (GTask *task); static gboolean profile_manager_ipdpcfg_set_retry (GTask *task) { profile_manager_store_profile_auth_settings (task); return G_SOURCE_REMOVE; } static void profile_manager_ipdpcfg_set_ready (MMBaseModem *self, GAsyncResult *res, GTask *task) { StoreProfileContext *ctx; g_autoptr(GError) error = NULL; ctx = g_task_get_task_data (task); if (!mm_base_modem_at_command_finish (self, res, &error)) { /* Retry configuring the context. It sometimes fails with a 583 * error ["a profile (CID) is currently active"] if a connect * is attempted too soon after a disconnect. */ if (ctx->n_attempts < IPDPCFG_SET_MAX_ATTEMPTS) { mm_obj_dbg (self, "couldn't store auth settings in profile '%d': %s; retrying...", ctx->profile_id, error->message); g_timeout_add_seconds (IPDPCFG_SET_RETRY_TIMEOUT_SECS, (GSourceFunc)profile_manager_ipdpcfg_set_retry, task); return; } g_task_return_error (task, g_steal_pointer (&error)); } else g_task_return_boolean (task, TRUE); g_object_unref (task); } static void profile_manager_store_profile_auth_settings (GTask *task) { MMIfaceModem3gppProfileManager *self; StoreProfileContext *ctx; g_autofree gchar *cmd = NULL; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); if (!ctx->cmd) { const gchar *user; const gchar *password; MMBearerAllowedAuth allowed_auth; user = mm_3gpp_profile_get_user (ctx->profile); password = mm_3gpp_profile_get_password (ctx->profile); allowed_auth = mm_3gpp_profile_get_allowed_auth (ctx->profile); /* Both user and password are required; otherwise firmware returns an error */ if (!user || !password || allowed_auth == MM_BEARER_ALLOWED_AUTH_NONE) { mm_obj_dbg (self, "not using authentication"); ctx->cmd = g_strdup_printf ("%%IPDPCFG=%d,0,0,\"\",\"\"", ctx->profile_id); } else { g_autofree gchar *quoted_user = NULL; g_autofree gchar *quoted_password = NULL; guint icera_auth; if (allowed_auth == MM_BEARER_ALLOWED_AUTH_UNKNOWN) { mm_obj_dbg (self, "using default (CHAP) authentication method"); icera_auth = 2; } else if (allowed_auth & MM_BEARER_ALLOWED_AUTH_CHAP) { mm_obj_dbg (self, "using CHAP authentication method"); icera_auth = 2; } else if (allowed_auth & MM_BEARER_ALLOWED_AUTH_PAP) { mm_obj_dbg (self, "using PAP authentication method"); icera_auth = 1; } else { g_autofree gchar *str = NULL; str = mm_bearer_allowed_auth_build_string_from_mask (allowed_auth); g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "Cannot use any of the specified authentication methods (%s)", str); g_object_unref (task); return; } quoted_user = mm_port_serial_at_quote_string (user); quoted_password = mm_port_serial_at_quote_string (password); ctx->cmd = g_strdup_printf ("%%IPDPCFG=%d,0,%u,%s,%s", ctx->profile_id, icera_auth, quoted_user, quoted_password); } } ctx->n_attempts++; mm_base_modem_at_command (MM_BASE_MODEM (self), ctx->cmd, 6, FALSE, (GAsyncReadyCallback)profile_manager_ipdpcfg_set_ready, task); } static void profile_manager_parent_store_profile_ready (MMIfaceModem3gppProfileManager *self, GAsyncResult *res, GTask *task) { GError *error = NULL; if (!iface_modem_3gpp_profile_manager_parent->store_profile_finish (self, res, NULL, NULL, &error)) { g_task_return_error (task, error); g_object_unref (task); return; } profile_manager_store_profile_auth_settings (task); } static void modem_3gpp_profile_manager_store_profile (MMIfaceModem3gppProfileManager *self, MM3gppProfile *profile, const gchar *index_field, GAsyncReadyCallback callback, gpointer user_data) { StoreProfileContext *ctx; GTask *task; g_assert (g_strcmp0 (index_field, "profile-id") == 0); task = g_task_new (self, NULL, callback, user_data); ctx = g_slice_new0 (StoreProfileContext); ctx->profile = g_object_ref (profile); ctx->profile_id = mm_3gpp_profile_get_profile_id (ctx->profile); g_assert (ctx->profile_id != MM_3GPP_PROFILE_ID_UNKNOWN); g_task_set_task_data (task, ctx, (GDestroyNotify) store_profile_context_free); iface_modem_3gpp_profile_manager_parent->store_profile ( self, profile, index_field, (GAsyncReadyCallback)profile_manager_parent_store_profile_ready, task); } /*****************************************************************************/ /* Load network time (Time interface) */ static gchar * modem_time_load_network_time_finish (MMIfaceModemTime *self, GAsyncResult *res, GError **error) { const gchar *response; gchar *iso8601; response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, error); if (!response) return NULL; return (parse_tlts_query_reply (response, &iso8601, NULL, error) ? iso8601 : NULL); } static void modem_time_load_network_time (MMIfaceModemTime *self, GAsyncReadyCallback callback, gpointer user_data) { mm_base_modem_at_command (MM_BASE_MODEM (self), "*TLTS", 3, FALSE, callback, user_data); } /*****************************************************************************/ /* Check support (Time interface) */ static gboolean modem_time_check_support_finish (MMIfaceModemTime *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void modem_time_check_support (MMIfaceModemTime *self, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; task = g_task_new (self, NULL, callback, user_data); /* We assume Icera devices always support *TLTS, since they appear * to return ERROR if the modem is not powered up, and thus we cannot * check for *TLTS support during modem initialization. */ g_task_return_boolean (task, TRUE); g_object_unref (task); } /*****************************************************************************/ /* Setup ports (Broadband modem class) */ static void setup_ports (MMBroadbandModem *self) { /* Call parent's setup ports first always */ MM_BROADBAND_MODEM_CLASS (mm_broadband_modem_icera_parent_class)->setup_ports (self); /* Now reset the unsolicited messages we'll handle when enabled */ set_unsolicited_events_handlers (MM_BROADBAND_MODEM_ICERA (self), FALSE); } /*****************************************************************************/ MMBroadbandModemIcera * mm_broadband_modem_icera_new (const gchar *device, const gchar *physdev, const gchar **drivers, const gchar *plugin, guint16 vendor_id, guint16 product_id) { return g_object_new (MM_TYPE_BROADBAND_MODEM_ICERA, MM_BASE_MODEM_DEVICE, device, MM_BASE_MODEM_PHYSDEV, physdev, MM_BASE_MODEM_DRIVERS, drivers, MM_BASE_MODEM_PLUGIN, plugin, MM_BASE_MODEM_VENDOR_ID, vendor_id, MM_BASE_MODEM_PRODUCT_ID, product_id, /* Generic bearer (AT) or Icera bearer (NET) supported */ MM_BASE_MODEM_DATA_NET_SUPPORTED, TRUE, MM_BASE_MODEM_DATA_TTY_SUPPORTED, TRUE, NULL); } static void set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { MMBroadbandModemIcera *self = MM_BROADBAND_MODEM_ICERA (object); switch (prop_id) { case PROP_DEFAULT_IP_METHOD: self->priv->default_ip_method = g_value_get_enum (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) { MMBroadbandModemIcera *self = MM_BROADBAND_MODEM_ICERA (object); switch (prop_id) { case PROP_DEFAULT_IP_METHOD: g_value_set_enum (value, self->priv->default_ip_method); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void mm_broadband_modem_icera_init (MMBroadbandModemIcera *self) { /* Initialize private data */ self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, MM_TYPE_BROADBAND_MODEM_ICERA, MMBroadbandModemIceraPrivate); self->priv->nwstate_regex = g_regex_new ("%NWSTATE:\\s*(-?\\d+),(\\d+),([^,]*),([^,]*),(\\d+)", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); self->priv->pacsp_regex = g_regex_new ("\\r\\n\\+PACSP(\\d)\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); self->priv->ipdpact_regex = g_regex_new ("\\r\\n%IPDPACT:\\s*(\\d+),\\s*(\\d+),\\s*(\\d+)\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); self->priv->default_ip_method = MM_BEARER_IP_METHOD_STATIC; self->priv->last_act = MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN; } static void finalize (GObject *object) { MMBroadbandModemIcera *self = MM_BROADBAND_MODEM_ICERA (object); g_regex_unref (self->priv->nwstate_regex); g_regex_unref (self->priv->pacsp_regex); g_regex_unref (self->priv->ipdpact_regex); G_OBJECT_CLASS (mm_broadband_modem_icera_parent_class)->finalize (object); } static void iface_modem_init (MMIfaceModem *iface) { iface_modem_parent = g_type_interface_peek_parent (iface); iface->load_supported_modes = load_supported_modes; iface->load_supported_modes_finish = load_supported_modes_finish; iface->load_current_modes = modem_load_current_modes; iface->load_current_modes_finish = modem_load_current_modes_finish; iface->set_current_modes = modem_set_current_modes; iface->set_current_modes_finish = modem_set_current_modes_finish; iface->load_access_technologies = modem_load_access_technologies; iface->load_access_technologies_finish = modem_load_access_technologies_finish; iface->load_unlock_retries = modem_load_unlock_retries; iface->load_unlock_retries_finish = modem_load_unlock_retries_finish; iface->load_supported_bands = modem_load_supported_bands; iface->load_supported_bands_finish = modem_load_supported_bands_finish; iface->load_current_bands = modem_load_current_bands; iface->load_current_bands_finish = modem_load_current_bands_finish; iface->modem_power_up = modem_power_up; iface->modem_power_up_finish = modem_power_up_finish; /* Note: don't implement modem_init_power_down, as CFUN=4 here may take * looong to reply */ iface->modem_power_down = modem_power_down; iface->modem_power_down_finish = modem_power_down_finish; iface->reset = modem_reset; iface->reset_finish = modem_reset_finish; iface->set_current_bands = modem_set_current_bands; iface->set_current_bands_finish = modem_set_current_bands_finish; iface->create_bearer = modem_create_bearer; iface->create_bearer_finish = modem_create_bearer_finish; } static void iface_modem_3gpp_init (MMIfaceModem3gpp *iface) { iface_modem_3gpp_parent = g_type_interface_peek_parent (iface); iface->setup_unsolicited_events = modem_3gpp_setup_unsolicited_events; iface->setup_unsolicited_events_finish = modem_3gpp_setup_cleanup_unsolicited_events_finish; iface->cleanup_unsolicited_events = modem_3gpp_cleanup_unsolicited_events; iface->cleanup_unsolicited_events_finish = modem_3gpp_setup_cleanup_unsolicited_events_finish; iface->enable_unsolicited_events = modem_3gpp_enable_unsolicited_events; iface->enable_unsolicited_events_finish = modem_3gpp_enable_disable_unsolicited_events_finish; iface->disable_unsolicited_events = modem_3gpp_disable_unsolicited_events; iface->disable_unsolicited_events_finish = modem_3gpp_enable_disable_unsolicited_events_finish; } static void iface_modem_3gpp_profile_manager_init (MMIfaceModem3gppProfileManager *iface) { iface_modem_3gpp_profile_manager_parent = g_type_interface_peek_parent (iface); iface->list_profiles = modem_3gpp_profile_manager_list_profiles; iface->list_profiles_finish = modem_3gpp_profile_manager_list_profiles_finish; iface->check_format = modem_3gpp_profile_manager_check_format; iface->check_format_finish = modem_3gpp_profile_manager_check_format_finish; /* note: the parent check_activated_profile() implementation using +CGACT? seems to * be perfectly valid. */ iface->deactivate_profile = modem_3gpp_profile_manager_deactivate_profile; iface->deactivate_profile_finish = modem_3gpp_profile_manager_deactivate_profile_finish; iface->store_profile = modem_3gpp_profile_manager_store_profile; iface->store_profile_finish = modem_3gpp_profile_manager_store_profile_finish; } static void iface_modem_time_init (MMIfaceModemTime *iface) { iface->check_support = modem_time_check_support; iface->check_support_finish = modem_time_check_support_finish; iface->load_network_time = modem_time_load_network_time; iface->load_network_time_finish = modem_time_load_network_time_finish; iface->load_network_timezone = modem_time_load_network_timezone; iface->load_network_timezone_finish = modem_time_load_network_timezone_finish; } static void mm_broadband_modem_icera_class_init (MMBroadbandModemIceraClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); MMBroadbandModemClass *broadband_modem_class = MM_BROADBAND_MODEM_CLASS (klass); g_type_class_add_private (object_class, sizeof (MMBroadbandModemIceraPrivate)); object_class->get_property = get_property; object_class->set_property = set_property; object_class->finalize = finalize; broadband_modem_class->setup_ports = setup_ports; properties[PROP_DEFAULT_IP_METHOD] = g_param_spec_enum (MM_BROADBAND_MODEM_ICERA_DEFAULT_IP_METHOD, "Default IP method", "Default IP Method (static or DHCP) to use.", MM_TYPE_BEARER_IP_METHOD, MM_BEARER_IP_METHOD_STATIC, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY); g_object_class_install_property (object_class, PROP_DEFAULT_IP_METHOD, properties[PROP_DEFAULT_IP_METHOD]); } ModemManager-1.23.4-dev/src/plugins/icera/mm-broadband-modem-icera.h000066400000000000000000000051221456466623000251340ustar00rootroot00000000000000/* -*- 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 - 2012 Red Hat, Inc. * Copyright (C) 2012 Aleksander Morgado */ #ifndef MM_BROADBAND_MODEM_ICERA_H #define MM_BROADBAND_MODEM_ICERA_H #include "mm-broadband-modem.h" #define MM_TYPE_BROADBAND_MODEM_ICERA (mm_broadband_modem_icera_get_type ()) #define MM_BROADBAND_MODEM_ICERA(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_BROADBAND_MODEM_ICERA, MMBroadbandModemIcera)) #define MM_BROADBAND_MODEM_ICERA_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_BROADBAND_MODEM_ICERA, MMBroadbandModemIceraClass)) #define MM_IS_BROADBAND_MODEM_ICERA(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_BROADBAND_MODEM_ICERA)) #define MM_IS_BROADBAND_MODEM_ICERA_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_BROADBAND_MODEM_ICERA)) #define MM_BROADBAND_MODEM_ICERA_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_BROADBAND_MODEM_ICERA, MMBroadbandModemIceraClass)) #define MM_BROADBAND_MODEM_ICERA_DEFAULT_IP_METHOD "broadband-modem-icera-default-ip-method" typedef struct _MMBroadbandModemIcera MMBroadbandModemIcera; typedef struct _MMBroadbandModemIceraClass MMBroadbandModemIceraClass; typedef struct _MMBroadbandModemIceraPrivate MMBroadbandModemIceraPrivate; struct _MMBroadbandModemIcera { MMBroadbandModem parent; MMBroadbandModemIceraPrivate *priv; }; struct _MMBroadbandModemIceraClass{ MMBroadbandModemClass parent; }; G_MODULE_EXPORT GType mm_broadband_modem_icera_get_type (void); G_MODULE_EXPORT MMBroadbandModemIcera *mm_broadband_modem_icera_new (const gchar *device, const gchar *physdev, const gchar **drivers, const gchar *plugin, guint16 vendor_id, guint16 product_id); #endif /* MM_BROADBAND_MODEM_ICERA_H */ ModemManager-1.23.4-dev/src/plugins/icera/mm-modem-helpers-icera.c000066400000000000000000000321501456466623000246560ustar00rootroot00000000000000/* -*- 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) 2012 Google, Inc. * Copyright (C) 2012 - 2013 Aleksander Morgado * Copyright (C) 2014 Dan Williams */ #include #include #include #include #include #include #define _LIBMM_INSIDE_MM #include #include "mm-log.h" #include "mm-modem-helpers.h" #include "mm-modem-helpers-icera.h" /*****************************************************************************/ /* %IPDPADDR response parser */ static MMBearerIpConfig * parse_ipdpaddr_v4 (const gchar **items, guint num_items, GError **error) { MMBearerIpConfig *config; const gchar *dns[3] = { 0 }; guint dns_i = 0, tmp; const gchar *netmask = NULL; /* IP address and prefix */ tmp = 0; if (!inet_pton (AF_INET, items[1], &tmp)) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't parse IPv4 address '%s'", items[1]); return NULL; } if (!tmp) { /* No IPv4 config */ return NULL; } config = mm_bearer_ip_config_new (); mm_bearer_ip_config_set_method (config, MM_BEARER_IP_METHOD_STATIC); mm_bearer_ip_config_set_address (config, items[1]); mm_bearer_ip_config_set_prefix (config, 32); /* default prefix */ /* Gateway */ tmp = 0; if (inet_pton (AF_INET, items[2], &tmp)) { if (tmp) mm_bearer_ip_config_set_gateway (config, items[2]); } else { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't parse gateway address '%s'", items[2]); goto error; } /* DNS */ tmp = 0; if (inet_pton (AF_INET, items[3], &tmp) && tmp) dns[dns_i++] = items[3]; else { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't parse DNS address '%s'", items[3]); goto error; } /* DNS2 - sometimes missing and set to 0.0.0.0 */ tmp = 0; if (inet_pton (AF_INET, items[4], &tmp) && tmp) dns[dns_i++] = items[4]; if (dns_i > 0) mm_bearer_ip_config_set_dns (config, (const gchar **) dns); /* Short form (eg, Sierra USB305) */ if (num_items < 9) return config; /* Devices return netmask and secondary gateway in one of two * positions. The netmask may be either at index 7 or 8, while * the secondary gateway may be at position 8 or 9. */ if (items[7] && strstr (items[7], "255.") && !strstr (items[7], "255.0.0.0")) netmask = items[7]; if (items[8] && strstr (items[8], "255.") && !strstr (items[8], "255.0.0.0")) netmask = items[8]; if (netmask) { if (!inet_pton (AF_INET, netmask, &tmp)) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't parse netmask '%s'", netmask); goto error; } mm_bearer_ip_config_set_prefix (config, mm_netmask_to_cidr (netmask)); } /* Secondary gateway */ if (!mm_bearer_ip_config_get_gateway (config)) { const char *gw2 = NULL; if (num_items >= 10 && items[9] && !strstr (items[9], "255.") && !strstr (items[9], "::")) gw2 = items[9]; /* Prefer position 8 */ if (items[8] && !strstr (items[8], "255.")) gw2 = items[8]; if (gw2 && inet_pton (AF_INET, gw2, &tmp) && tmp) mm_bearer_ip_config_set_gateway (config, gw2); else { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't parse secondary gateway address '%s'", gw2 ? gw2 : "(unknown)"); goto error; } } return config; error: g_object_unref (config); return NULL; } static MMBearerIpConfig * parse_ipdpaddr_v6 (const gchar **items, guint num_items, GError **error) { MMBearerIpConfig *config; const gchar *dns[2] = { 0 }; struct in6_addr tmp6 = IN6ADDR_ANY_INIT; if (num_items < 12) return NULL; /* No IPv6 IP and no IPv6 DNS, return NULL without error. */ if (g_strcmp0 (items[9], "::") == 0 && g_strcmp0 (items[11], "::") == 0) return NULL; config = mm_bearer_ip_config_new (); /* It appears that for IPv6 %IPDPADDR returns only the expected * link-local address and a DNS address, and that to retrieve the * default router, extra DNS, and search domains, the host must listen * for IPv6 Router Advertisements on the net port. */ if (g_strcmp0 (items[9], "::") != 0) { mm_bearer_ip_config_set_method (config, MM_BEARER_IP_METHOD_STATIC); /* IP address and prefix */ if (inet_pton (AF_INET6, items[9], &tmp6) != 1 || IN6_IS_ADDR_UNSPECIFIED (&tmp6)) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't parse IPv6 address '%s'", items[9]); goto error; } mm_bearer_ip_config_set_address (config, items[9]); mm_bearer_ip_config_set_prefix (config, 64); /* If the address is a link-local one, then SLAAC or DHCP must be used * to get the real prefix and address. Change the method to DHCP to * indicate this to clients. */ if (IN6_IS_ADDR_LINKLOCAL (&tmp6)) mm_bearer_ip_config_set_method (config, MM_BEARER_IP_METHOD_DHCP); } else { /* No IPv6 given, but DNS will be available, try with DHCP */ mm_bearer_ip_config_set_method (config, MM_BEARER_IP_METHOD_DHCP); } /* DNS server */ if (g_strcmp0 (items[11], "::") != 0) { memset (&tmp6, 0, sizeof (tmp6)); if (inet_pton (AF_INET6, items[11], &tmp6) == 1 && !IN6_IS_ADDR_UNSPECIFIED (&tmp6)) { dns[0] = items[11]; dns[1] = NULL; mm_bearer_ip_config_set_dns (config, (const gchar **) dns); } else { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't parse DNS address '%s'", items[11]); goto error; } } return config; error: g_object_unref (config); return NULL; } #define IPDPADDR_TAG "%IPDPADDR: " gboolean mm_icera_parse_ipdpaddr_response (const gchar *response, guint expected_cid, MMBearerIpConfig **out_ip4_config, MMBearerIpConfig **out_ip6_config, GError **error) { MMBearerIpConfig *ip4_config = NULL; MMBearerIpConfig *ip6_config = NULL; GError *local = NULL; gboolean success = FALSE; char **items; guint num_items, i; guint num; g_return_val_if_fail (out_ip4_config, FALSE); g_return_val_if_fail (out_ip6_config, FALSE); if (!response || !g_str_has_prefix (response, IPDPADDR_TAG)) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Missing %%IPDPADDR prefix"); return FALSE; } /* %IPDPADDR: ,,,,[,,[,,,]] * %IPDPADDR: ,,,,,,,, * %IPDPADDR: ,,,,,,,,,,::,,::,::,::,::,:: * * Sierra USB305: %IPDPADDR: 2, 21.93.217.11, 21.93.217.10, 10.177.0.34, 10.161.171.220, 0.0.0.0, 0.0.0.0 * K3805-Z: %IPDPADDR: 2, 21.93.217.11, 21.93.217.10, 10.177.0.34, 10.161.171.220, 0.0.0.0, 0.0.0.0, 255.0.0.0, 255.255.255.0, 21.93.217.10, * Nokia 21M: %IPDPADDR: 2, 33.196.7.127, 33.196.7.128, 10.177.0.34, 10.161.171.220, 0.0.0.0, 0.0.0.0, 255.0.0.0, 33.196.7.128, fe80::f:9135:5901, ::, fd00:976a::9, ::, ::, ::, ::, :: * Nokia 21M: %IPDPADDR: 3, 0.0.0.0, 0.0.0.0, 0.0.0.0, 0.0.0.0, 0.0.0.0, 0.0.0.0, 0.0.0.0, 0.0.0.0, fe80::2e:437b:7901, ::, fd00:976a::9, ::, ::, ::, ::, :: */ response = mm_strip_tag (response, IPDPADDR_TAG); items = g_strsplit_set (response, ",", 0); /* Strip any spaces on elements; inet_pton() doesn't like them */ num_items = g_strv_length (items); for (i = 0; i < num_items; i++) items[i] = g_strstrip (items[i]); if (num_items < 7) { g_set_error_literal (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Malformed IPDPADDR response (not enough items)"); goto out; } /* Validate context ID */ if (!mm_get_uint_from_str (items[0], &num) || num != expected_cid) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Unknown CID in IPDPADDR response (got %d, expected %d)", (guint) num, expected_cid); goto out; } ip4_config = parse_ipdpaddr_v4 ((const gchar **) items, num_items, &local); if (local) { g_propagate_error (error, local); goto out; } ip6_config = parse_ipdpaddr_v6 ((const gchar **) items, num_items, &local); if (local) { g_propagate_error (error, local); goto out; } success = TRUE; out: g_strfreev (items); *out_ip4_config = ip4_config; *out_ip6_config = ip6_config; return success; } /*****************************************************************************/ /* %IPDPCFG? response parser. * Modifies the input list of profiles in place * * AT%IPDPCFG? * %IPDPCFG: 1,0,0,,,0 * %IPDPCFG: 2,0,0,,,0 * %IPDPCFG: 3,0,2,"user","pass",0 * %IPDPCFG: 4,0,0,,,0 * OK */ gboolean mm_icera_parse_ipdpcfg_query_response (const gchar *str, GList *profiles, gpointer log_object, GError **error) { g_autoptr(GRegex) r = NULL; g_autoptr(GError) inner_error = NULL; g_autoptr(GMatchInfo) match_info = NULL; guint n_updates = 0; guint n_profiles; n_profiles = g_list_length (profiles); r = g_regex_new ("%IPDPCFG:\\s*(\\d+),(\\d+),(\\d+),([^,]*),([^,]*),(\\d+)", G_REGEX_DOLLAR_ENDONLY | G_REGEX_RAW, 0, NULL); g_assert (r != NULL); g_regex_match_full (r, str, strlen (str), 0, 0, &match_info, &inner_error); if (inner_error) { g_propagate_error (error, inner_error); return FALSE; } /* Parse the results */ while (g_match_info_matches (match_info)) { guint cid; guint auth; MMBearerAllowedAuth allowed_auth; g_autofree gchar *user = NULL; g_autofree gchar *password = NULL; GList *l; if (!mm_get_uint_from_match_info (match_info, 1, &cid)) { mm_obj_warn (log_object, "couldn't parse cid from %%IPDPCFG line"); goto next; } if (!mm_get_uint_from_match_info (match_info, 3, &auth)) { mm_obj_warn (log_object, "couldn't parse auth from %%IPDPCFG line"); goto next; } switch (auth) { case 0: allowed_auth = MM_BEARER_ALLOWED_AUTH_NONE; break; case 1: allowed_auth = MM_BEARER_ALLOWED_AUTH_PAP; break; case 2: allowed_auth = MM_BEARER_ALLOWED_AUTH_CHAP; break; default: mm_obj_warn (log_object, "unexpected icera auth setting: %u", auth); goto next; } user = mm_get_string_unquoted_from_match_info (match_info, 4); password = mm_get_string_unquoted_from_match_info (match_info, 5); mm_obj_dbg (log_object, "found icera auth settings for profile with id '%u'", cid); /* Find profile and update in place */ for (l = profiles; l; l = g_list_next (l)) { MM3gppProfile *iter = l->data; if (mm_3gpp_profile_get_profile_id (iter) == (gint) cid) { n_updates++; mm_3gpp_profile_set_allowed_auth (iter, allowed_auth); mm_3gpp_profile_set_user (iter, user); mm_3gpp_profile_set_password (iter, password); break; } } if (!l) mm_obj_warn (log_object, "couldn't update auth settings in profile with id '%d': not found", cid); next: g_match_info_next (match_info, NULL); } if (n_updates != n_profiles) mm_obj_warn (log_object, "couldn't update auth settings in all profiles: %u/%u updated", n_updates, n_profiles); return TRUE; } ModemManager-1.23.4-dev/src/plugins/icera/mm-modem-helpers-icera.h000066400000000000000000000026741456466623000246730ustar00rootroot00000000000000/* -*- 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) 2014 Dan Williams */ #ifndef MM_MODEM_HELPERS_ICERA_H #define MM_MODEM_HELPERS_ICERA_H #include "glib.h" /* %IPDPADDR response parser */ gboolean mm_icera_parse_ipdpaddr_response (const gchar *response, guint expected_cid, MMBearerIpConfig **out_ip4_config, MMBearerIpConfig **out_ip6_config, GError **error); /* %IPDPCFG? response parser */ gboolean mm_icera_parse_ipdpcfg_query_response (const gchar *response, GList *profiles, gpointer log_object, GError **error); #endif /* MM_MODEM_HELPERS_HUAWEI_H */ ModemManager-1.23.4-dev/src/plugins/icera/mm-shared.c000066400000000000000000000013121456466623000222760ustar00rootroot00000000000000/* -*- 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) 2019 Aleksander Morgado */ #include "mm-shared-common.h" MM_DEFINE_SHARED (icera) ModemManager-1.23.4-dev/src/plugins/icera/tests/000077500000000000000000000000001456466623000214225ustar00rootroot00000000000000ModemManager-1.23.4-dev/src/plugins/icera/tests/test-modem-helpers-icera.c000066400000000000000000000247121456466623000263730ustar00rootroot00000000000000/* -*- 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) 2013 Aleksander Morgado * Copyright (C) 2014 Dan Williams */ #include #include #include #include #include #include #define _LIBMM_INSIDE_MM #include #include "mm-log-test.h" #include "mm-modem-helpers.h" #include "mm-modem-helpers-icera.h" /*****************************************************************************/ /* Test %IPDPADDR responses */ typedef struct { const gchar *str; const guint expected_cid; /* IPv4 */ const gchar *ipv4_addr; const guint ipv4_prefix; const gchar *ipv4_gw; const gchar *ipv4_dns1; const gchar *ipv4_dns2; /* IPv6 */ const gchar *ipv6_addr; const gchar *ipv6_dns1; } IpdpaddrTest; static const IpdpaddrTest ipdpaddr_tests[] = { /* Sierra USB305 */ { "%IPDPADDR: 2, 21.93.217.11, 21.93.217.10, 10.177.0.34, 10.161.171.220, 0.0.0.0, 0.0.0.0\r\n", 2, "21.93.217.11", 32, "21.93.217.10", "10.177.0.34", "10.161.171.220", NULL, NULL }, /* ZTE/Vodafone K3805-Z */ { "%IPDPADDR: 5, 21.93.217.11, 21.93.217.10, 10.177.0.34, 10.161.171.220, 0.0.0.0, 0.0.0.0, 255.0.0.0, 255.255.255.0, 21.93.217.10,\r\n", 5, "21.93.217.11", 24, "21.93.217.10", "10.177.0.34", "10.161.171.220", NULL, NULL }, /* Secondary gateway check */ { "%IPDPADDR: 5, 21.93.217.11, 0.0.0.0, 10.177.0.34, 10.161.171.220, 0.0.0.0, 0.0.0.0, 255.0.0.0, 255.255.255.0, 21.93.217.10,\r\n", 5, "21.93.217.11", 24, "21.93.217.10", "10.177.0.34", "10.161.171.220", NULL, NULL }, /* Secondary gateway check #2 */ { "%IPDPADDR: 5, 27.107.96.189, 0.0.0.0, 121.242.190.210, 121.242.190.181, 0.0.0.0, 0.0.0.0, 255.255.255.254, 27.107.96.188\r\n", 5, "27.107.96.189", 31, "27.107.96.188", "121.242.190.210", "121.242.190.181", NULL, NULL }, /* Nokia 21M */ { "%IPDPADDR: 1, 33.196.7.127, 33.196.7.128, 10.177.0.34, 10.161.171.220, 0.0.0.0, 0.0.0.0, 255.0.0.0, 33.196.7.128, fe80::f:9135:5901, ::, fd00:976a::9, ::, ::, ::, ::, ::\r\n", 1, "33.196.7.127", 32, "33.196.7.128", "10.177.0.34", "10.161.171.220", "fe80::f:9135:5901", "fd00:976a::9" }, { "%IPDPADDR: 3, 0.0.0.0, 0.0.0.0, 0.0.0.0, 0.0.0.0, 0.0.0.0, 0.0.0.0, 0.0.0.0, 0.0.0.0, fe80::2e:437b:7901, ::, fd00:976a::9, ::, ::, ::, ::, ::\r\n", 3, NULL, 0, NULL, NULL, NULL, "fe80::2e:437b:7901", "fd00:976a::9" }, /* Some development chip (cnsbg.p1001.rev2, CL477342) */ { "%IPDPADDR: 5, 27.107.96.189, 27.107.96.188, 121.242.190.210, 121.242.190.181, 0.0.0.0, 0.0.0.0, 255.255.255.254, 27.107.96.188\r\n", 5, "27.107.96.189", 31, "27.107.96.188", "121.242.190.210", "121.242.190.181", NULL, NULL }, /* 21M with newer firmware */ { "%IPDPADDR: 2, 188.150.116.13, 188.150.116.14, 188.149.250.16, 0.0.0.0, 0.0.0.0, 0.0.0.0, 255.255.0.0, 188.150.116.14, fe80::1:e414:eb01, ::, 2a00:e18:0:3::6, ::, ::, ::, ::, ::\r\n", 2, "188.150.116.13", 16, "188.150.116.14", "188.149.250.16", NULL, "fe80::1:e414:eb01", "2a00:e18:0:3::6" }, { "%IPDPADDR: 1, 0.0.0.0, 0.0.0.0, 0.0.0.0, 0.0.0.0, 0.0.0.0, 0.0.0.0, 0.0.0.0, 0.0.0.0, fe80::1f:fad1:4c01, ::, 2001:4600:4:fff::54, 2001:4600:4:1fff::54, ::, ::, ::, ::\r\n", 1, NULL, 0, NULL, NULL, NULL, "fe80::1f:fad1:4c01", "2001:4600:4:fff::54" }, { "%IPDPADDR: 1, 46.157.76.179, 46.157.76.180, 193.213.112.4, 130.67.15.198, 0.0.0.0, 0.0.0.0, 255.0.0.0, 46.157.76.180, ::, ::, ::, ::, ::, ::, ::, ::\r\n", 1, "46.157.76.179", 32, "46.157.76.180", "193.213.112.4", "130.67.15.198", NULL, NULL }, { "%IPDPADDR: 1, 0.0.0.0, 0.0.0.0, 193.213.112.4, 130.67.15.198, 0.0.0.0, 0.0.0.0, 0.0.0.0, 0.0.0.0, ::, ::, 2001:4600:4:fff::52, 2001:4600:4:1fff::52, ::, ::, ::, ::", 1, NULL, 0, NULL, NULL, NULL, NULL, "2001:4600:4:fff::52" }, { NULL } }; static void test_ipdpaddr (void) { guint i; for (i = 0; ipdpaddr_tests[i].str; i++) { gboolean success; GError *error = NULL; MMBearerIpConfig *ipv4 = NULL; MMBearerIpConfig *ipv6 = NULL; const gchar **dns; guint dnslen; success = mm_icera_parse_ipdpaddr_response ( ipdpaddr_tests[i].str, ipdpaddr_tests[i].expected_cid, &ipv4, &ipv6, &error); g_assert_no_error (error); g_assert (success); /* IPv4 */ if (ipdpaddr_tests[i].ipv4_addr) { g_assert (ipv4); g_assert_cmpint (mm_bearer_ip_config_get_method (ipv4), ==, MM_BEARER_IP_METHOD_STATIC); g_assert_cmpstr (mm_bearer_ip_config_get_address (ipv4), ==, ipdpaddr_tests[i].ipv4_addr); g_assert_cmpint (mm_bearer_ip_config_get_prefix (ipv4), ==, ipdpaddr_tests[i].ipv4_prefix); g_assert_cmpstr (mm_bearer_ip_config_get_gateway (ipv4), ==, ipdpaddr_tests[i].ipv4_gw); dns = mm_bearer_ip_config_get_dns (ipv4); g_assert (dns); dnslen = g_strv_length ((gchar **) dns); if (ipdpaddr_tests[i].ipv4_dns2 != NULL) g_assert_cmpint (dnslen, ==, 2); else g_assert_cmpint (dnslen, ==, 1); g_assert_cmpstr (dns[0], ==, ipdpaddr_tests[i].ipv4_dns1); g_assert_cmpstr (dns[1], ==, ipdpaddr_tests[i].ipv4_dns2); g_object_unref (ipv4); } else g_assert (ipv4 == NULL); /* IPv6 */ if (ipdpaddr_tests[i].ipv6_addr || ipdpaddr_tests[i].ipv6_dns1) { struct in6_addr a6; g_assert (ipv6); if (ipdpaddr_tests[i].ipv6_addr) { g_assert_cmpstr (mm_bearer_ip_config_get_address (ipv6), ==, ipdpaddr_tests[i].ipv6_addr); g_assert_cmpint (mm_bearer_ip_config_get_prefix (ipv6), ==, 64); g_assert (inet_pton (AF_INET6, mm_bearer_ip_config_get_address (ipv6), &a6)); if (IN6_IS_ADDR_LINKLOCAL (&a6)) g_assert_cmpint (mm_bearer_ip_config_get_method (ipv6), ==, MM_BEARER_IP_METHOD_DHCP); else g_assert_cmpint (mm_bearer_ip_config_get_method (ipv6), ==, MM_BEARER_IP_METHOD_STATIC); } else g_assert_cmpint (mm_bearer_ip_config_get_method (ipv6), ==, MM_BEARER_IP_METHOD_DHCP); dns = mm_bearer_ip_config_get_dns (ipv6); g_assert (dns); dnslen = g_strv_length ((gchar **) dns); g_assert_cmpint (dnslen, ==, 1); g_assert_cmpstr (dns[0], ==, ipdpaddr_tests[i].ipv6_dns1); g_object_unref (ipv6); } else g_assert (ipv6 == NULL); } } /*****************************************************************************/ /* Test %IPDPCFG responses */ static void test_ipdpcfg (void) { MM3gppProfile *profile; GList *profiles = NULL; GList *l; GError *error = NULL; gboolean result; gboolean cid_1_validated = FALSE; gboolean cid_2_validated = FALSE; gboolean cid_5_validated = FALSE; const gchar *response = "%IPDPCFG: 1,0,0,,,0\r\n" "%IPDPCFG: 2,0,1,\"aaaa\",\"bbbbb\",0\r\n" "%IPDPCFG: 5,0,2,\"user\",\"pass\",0"; /* last line without CRLF */ profile = mm_3gpp_profile_new (); mm_3gpp_profile_set_profile_id (profile, 1); mm_3gpp_profile_set_apn (profile, "internet"); mm_3gpp_profile_set_ip_type (profile, MM_BEARER_IP_FAMILY_IPV4); profiles = g_list_append (profiles, profile); profile = mm_3gpp_profile_new (); mm_3gpp_profile_set_profile_id (profile, 2); mm_3gpp_profile_set_apn (profile, "internet2"); mm_3gpp_profile_set_ip_type (profile, MM_BEARER_IP_FAMILY_IPV4V6); profiles = g_list_append (profiles, profile); profile = mm_3gpp_profile_new (); mm_3gpp_profile_set_profile_id (profile, 5); mm_3gpp_profile_set_apn (profile, "internet3"); mm_3gpp_profile_set_ip_type (profile, MM_BEARER_IP_FAMILY_IPV6); profiles = g_list_append (profiles, profile); result = mm_icera_parse_ipdpcfg_query_response (response, profiles, NULL, &error); g_assert_no_error (error); g_assert (result); for (l = profiles; l; l = g_list_next (l)) { MM3gppProfile *iter = l->data; switch (mm_3gpp_profile_get_profile_id (iter)) { case 1: cid_1_validated = TRUE; g_assert_cmpuint (mm_3gpp_profile_get_allowed_auth (iter), ==, MM_BEARER_ALLOWED_AUTH_NONE); g_assert (!mm_3gpp_profile_get_user (iter)); g_assert (!mm_3gpp_profile_get_password (iter)); break; case 2: cid_2_validated = TRUE; g_assert_cmpuint (mm_3gpp_profile_get_allowed_auth (iter), ==, MM_BEARER_ALLOWED_AUTH_PAP); g_assert_cmpstr (mm_3gpp_profile_get_user (iter), ==, "aaaa"); g_assert_cmpstr (mm_3gpp_profile_get_password (iter), ==, "bbbbb"); break; case 5: cid_5_validated = TRUE; g_assert_cmpuint (mm_3gpp_profile_get_allowed_auth (iter), ==, MM_BEARER_ALLOWED_AUTH_CHAP); g_assert_cmpstr (mm_3gpp_profile_get_user (iter), ==, "user"); g_assert_cmpstr (mm_3gpp_profile_get_password (iter), ==, "pass"); break; default: g_assert_not_reached (); } } g_assert (cid_1_validated); g_assert (cid_2_validated); g_assert (cid_5_validated); g_list_free_full (profiles, (GDestroyNotify)g_object_unref); } /*****************************************************************************/ int main (int argc, char **argv) { setlocale (LC_ALL, ""); g_test_init (&argc, &argv, NULL); g_test_add_func ("/MM/icera/ipdpaddr", test_ipdpaddr); g_test_add_func ("/MM/icera/ipdpcfg", test_ipdpcfg); return g_test_run (); } ModemManager-1.23.4-dev/src/plugins/intel/000077500000000000000000000000001456466623000203105ustar00rootroot00000000000000ModemManager-1.23.4-dev/src/plugins/intel/mm-broadband-modem-mbim-intel.c000066400000000000000000000130031456466623000261260ustar00rootroot00000000000000/* -*- 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) 2021-2022 Intel Corporation */ #include #include #include #include #include #include #include #include #define _LIBMM_INSIDE_MM #include #include "mm-broadband-modem-mbim-intel.h" #include "mm-iface-modem-location.h" #include "mm-shared-xmm.h" static void iface_modem_location_init (MMIfaceModemLocation *iface); static void shared_xmm_init (MMSharedXmm *iface); static MMIfaceModemLocation *iface_modem_location_parent; G_DEFINE_TYPE_EXTENDED (MMBroadbandModemMbimIntel, mm_broadband_modem_mbim_intel, MM_TYPE_BROADBAND_MODEM_MBIM, 0, G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_LOCATION, iface_modem_location_init) G_IMPLEMENT_INTERFACE (MM_TYPE_SHARED_XMM, shared_xmm_init)) /*****************************************************************************/ static void setup_ports (MMBroadbandModem *self) { MMPortSerialAt *ports[3]; guint i; /* Run the shared XMM port setup logic */ mm_shared_xmm_setup_ports (self); ports[0] = mm_base_modem_peek_port_primary (MM_BASE_MODEM (self)); ports[1] = mm_base_modem_peek_port_secondary (MM_BASE_MODEM (self)); /* GNSS control port may or may not be a primary/secondary port */ ports[2] = mm_base_modem_peek_port_gps_control (MM_BASE_MODEM (self)); if (ports[2] && ((ports[2] == ports[0]) || (ports[2] == ports[1]))) ports[2] = NULL; for (i = 0; i < G_N_ELEMENTS (ports); i++) { if (!ports[i]) continue; g_object_set (ports[i], MM_PORT_SERIAL_SEND_DELAY, (guint64) 0, NULL); } } /*****************************************************************************/ MMBroadbandModemMbimIntel * mm_broadband_modem_mbim_intel_new (const gchar *device, const gchar *physdev, const gchar **drivers, const gchar *plugin, guint16 vendor_id, guint16 product_id) { return g_object_new (MM_TYPE_BROADBAND_MODEM_MBIM_INTEL, MM_BASE_MODEM_DEVICE, device, MM_BASE_MODEM_PHYSDEV, physdev, MM_BASE_MODEM_DRIVERS, drivers, MM_BASE_MODEM_PLUGIN, plugin, MM_BASE_MODEM_VENDOR_ID, vendor_id, MM_BASE_MODEM_PRODUCT_ID, product_id, /* MBIM bearer supports NET only */ MM_BASE_MODEM_DATA_NET_SUPPORTED, TRUE, MM_BASE_MODEM_DATA_TTY_SUPPORTED, FALSE, MM_IFACE_MODEM_SIM_HOT_SWAP_SUPPORTED, TRUE, MM_IFACE_MODEM_PERIODIC_SIGNAL_CHECK_DISABLED, TRUE, #if defined WITH_QMI && QMI_MBIM_QMUX_SUPPORTED MM_BROADBAND_MODEM_MBIM_QMI_UNSUPPORTED, TRUE, #endif NULL); } static void mm_broadband_modem_mbim_intel_init (MMBroadbandModemMbimIntel *self) { } static void iface_modem_location_init (MMIfaceModemLocation *iface) { iface_modem_location_parent = g_type_interface_peek_parent (iface); iface->load_capabilities = mm_shared_xmm_location_load_capabilities; iface->load_capabilities_finish = mm_shared_xmm_location_load_capabilities_finish; iface->enable_location_gathering = mm_shared_xmm_enable_location_gathering; iface->enable_location_gathering_finish = mm_shared_xmm_enable_location_gathering_finish; iface->disable_location_gathering = mm_shared_xmm_disable_location_gathering; iface->disable_location_gathering_finish = mm_shared_xmm_disable_location_gathering_finish; iface->load_supl_server = mm_shared_xmm_location_load_supl_server; iface->load_supl_server_finish = mm_shared_xmm_location_load_supl_server_finish; iface->set_supl_server = mm_shared_xmm_location_set_supl_server; iface->set_supl_server_finish = mm_shared_xmm_location_set_supl_server_finish; } static MMBroadbandModemClass * peek_parent_broadband_modem_class (MMSharedXmm *self) { return MM_BROADBAND_MODEM_CLASS (mm_broadband_modem_mbim_intel_parent_class); } static MMIfaceModemLocation * peek_parent_location_interface (MMSharedXmm *self) { return iface_modem_location_parent; } static void shared_xmm_init (MMSharedXmm *iface) { iface->peek_parent_broadband_modem_class = peek_parent_broadband_modem_class; iface->peek_parent_location_interface = peek_parent_location_interface; } static void mm_broadband_modem_mbim_intel_class_init (MMBroadbandModemMbimIntelClass *klass) { MMBroadbandModemClass *broadband_modem_class = MM_BROADBAND_MODEM_CLASS (klass); broadband_modem_class->setup_ports = setup_ports; } ModemManager-1.23.4-dev/src/plugins/intel/mm-broadband-modem-mbim-intel.h000066400000000000000000000047001456466623000261370ustar00rootroot00000000000000/* -*- 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) 2021-2022 Intel Corporation */ #ifndef MM_BROADBAND_MODEM_MBIM_INTEL_H #define MM_BROADBAND_MODEM_MBIM_INTEL_H #include "mm-broadband-modem-mbim.h" #define MM_TYPE_BROADBAND_MODEM_MBIM_INTEL (mm_broadband_modem_mbim_intel_get_type ()) #define MM_BROADBAND_MODEM_MBIM_INTEL(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_BROADBAND_MODEM_MBIM_INTEL, MMBroadbandModemMbimIntel)) #define MM_BROADBAND_MODEM_MBIM_INTEL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_BROADBAND_MODEM_MBIM_INTEL, MMBroadbandModemMbimIntelClass)) #define MM_IS_BROADBAND_MODEM_MBIM_INTEL(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_BROADBAND_MODEM_MBIM_INTEL)) #define MM_IS_BROADBAND_MODEM_MBIM_INTEL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_BROADBAND_MODEM_MBIM_INTEL)) #define MM_BROADBAND_MODEM_MBIM_INTEL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_BROADBAND_MODEM_MBIM_INTEL, MMBroadbandModemMbimIntelClass)) typedef struct _MMBroadbandModemMbimIntel MMBroadbandModemMbimIntel; typedef struct _MMBroadbandModemMbimIntelClass MMBroadbandModemMbimIntelClass; struct _MMBroadbandModemMbimIntel { MMBroadbandModemMbim parent; }; struct _MMBroadbandModemMbimIntelClass{ MMBroadbandModemMbimClass parent; }; GType mm_broadband_modem_mbim_intel_get_type (void); MMBroadbandModemMbimIntel *mm_broadband_modem_mbim_intel_new (const gchar *device, const gchar *physdev, const gchar **driver, const gchar *plugin, guint16 vendor_id, guint16 product_id); #endif /* MM_BROADBAND_MODEM_MBIM_INTEL_H */ ModemManager-1.23.4-dev/src/plugins/intel/mm-plugin-intel.c000066400000000000000000000064211456466623000234750ustar00rootroot00000000000000/* -*- 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) 2021-2022 Intel Corporation */ #include #include #define _LIBMM_INSIDE_MM #include #include "mm-plugin-common.h" #include "mm-log-object.h" #include "mm-broadband-modem.h" #if defined WITH_MBIM #include "mm-broadband-modem-mbim-intel.h" #endif #define MM_TYPE_PLUGIN_INTEL mm_plugin_intel_get_type () MM_DEFINE_PLUGIN (INTEL, intel, Intel) /*****************************************************************************/ static MMBaseModem * create_modem (MMPlugin *self, const gchar *uid, const gchar *physdev, const gchar **drivers, guint16 vendor, guint16 product, guint16 subsystem_vendor, GList *probes, GError **error) { #if defined WITH_MBIM if (mm_port_probe_list_has_mbim_port (probes)) { mm_obj_dbg (self, "MBIM-powered Intel modem found..."); return MM_BASE_MODEM (mm_broadband_modem_mbim_intel_new (uid, physdev, drivers, mm_plugin_get_name (self), vendor, product)); } #endif mm_obj_dbg (self, "Generic Intel modem found..."); return MM_BASE_MODEM (mm_broadband_modem_new (uid, physdev, drivers, mm_plugin_get_name (self), vendor, product)); } /*****************************************************************************/ MM_PLUGIN_NAMED_CREATOR_SCOPE MMPlugin * mm_plugin_create_intel (void) { static const gchar *subsystems[] = { "net", "wwan", NULL }; static const guint16 vendor_ids[] = { 0x8086, 0 }; return MM_PLUGIN ( g_object_new (MM_TYPE_PLUGIN_INTEL, MM_PLUGIN_NAME, "Intel", MM_PLUGIN_ALLOWED_SUBSYSTEMS, subsystems, MM_PLUGIN_ALLOWED_VENDOR_IDS, vendor_ids, MM_PLUGIN_ALLOWED_AT, TRUE, MM_PLUGIN_ALLOWED_MBIM, TRUE, NULL)); } static void mm_plugin_intel_init (MMPluginIntel *self) { /*nothing to be done here, but required for creating intel plugin instance*/ } static void mm_plugin_intel_class_init (MMPluginIntelClass *klass) { MMPluginClass *plugin_class = MM_PLUGIN_CLASS (klass); plugin_class->create_modem = create_modem; } ModemManager-1.23.4-dev/src/plugins/iridium/000077500000000000000000000000001456466623000206375ustar00rootroot00000000000000ModemManager-1.23.4-dev/src/plugins/iridium/mm-bearer-iridium.c000066400000000000000000000174441456466623000243240ustar00rootroot00000000000000/* -*- 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) 2012 Ammonit Measurement GmbH */ #include #include #include #include #include #include #include #define _LIBMM_INSIDE_MM #include #include "mm-bearer-iridium.h" #include "mm-base-modem-at.h" /* Allow up to 200s to get a proper IP connection */ #define BEARER_IRIDIUM_IP_TIMEOUT_DEFAULT 200 G_DEFINE_TYPE (MMBearerIridium, mm_bearer_iridium, MM_TYPE_BASE_BEARER) /*****************************************************************************/ /* Connect */ typedef struct { MMPortSerialAt *primary; GError *saved_error; } ConnectContext; static void connect_context_free (ConnectContext *ctx) { if (ctx->saved_error) g_error_free (ctx->saved_error); if (ctx->primary) g_object_unref (ctx->primary); g_free (ctx); } static MMBearerConnectResult * connect_finish (MMBaseBearer *self, GAsyncResult *res, GError **error) { return g_task_propagate_pointer (G_TASK (res), error); } static void connect_report_ready (MMBaseModem *modem, GAsyncResult *res, GTask *task) { ConnectContext *ctx; const gchar *result; /* If cancelled, complete */ if (g_task_return_error_if_cancelled (task)) { g_object_unref (task); return; } ctx = g_task_get_task_data (task); /* If we got a proper extended reply, build the new error to be set */ result = mm_base_modem_at_command_full_finish (modem, res, NULL); if (result && g_str_has_prefix (result, "+CEER: ") && strlen (result) > 7) { g_task_return_new_error (task, ctx->saved_error->domain, ctx->saved_error->code, "%s", &result[7]); } else { /* Otherwise, take the original error as it was */ g_task_return_error (task, ctx->saved_error); ctx->saved_error = NULL; } g_object_unref (task); } static void dial_ready (MMBaseModem *modem, GAsyncResult *res, GTask *task) { ConnectContext *ctx; MMBearerIpConfig *config; ctx = g_task_get_task_data (task); /* DO NOT check for cancellable here. If we got here without errors, the * bearer is really connected and therefore we need to reflect that in * the state machine. */ mm_base_modem_at_command_full_finish (modem, res, &(ctx->saved_error)); if (ctx->saved_error) { /* Try to get more information why it failed */ mm_base_modem_at_command_full ( modem, ctx->primary, "+CEER", 3, FALSE, FALSE, /* raw */ NULL, /* cancellable */ (GAsyncReadyCallback)connect_report_ready, task); return; } /* Port is connected; update the state */ mm_port_set_connected (MM_PORT (ctx->primary), TRUE); /* Build IP config; always PPP based */ config = mm_bearer_ip_config_new (); mm_bearer_ip_config_set_method (config, MM_BEARER_IP_METHOD_PPP); /* Return operation result */ g_task_return_pointer ( task, mm_bearer_connect_result_new (MM_PORT (ctx->primary), config, NULL), (GDestroyNotify)mm_bearer_connect_result_unref); g_object_unref (task); g_object_unref (config); } static void service_type_ready (MMBaseModem *modem, GAsyncResult *res, GTask *task) { ConnectContext *ctx; GError *error = NULL; /* If cancelled, complete */ if (g_task_return_error_if_cancelled (task)) { g_object_unref (task); return; } ctx = g_task_get_task_data (task); /* Errors setting the service type will be critical */ mm_base_modem_at_command_full_finish (modem, res, &error); if (error) { g_task_return_error (task, error); g_object_unref (task); return; } /* We just use the default number to dial in the Iridium network. Also note * that we won't specify a specific port to use; Iridium modems only expose * one. */ mm_base_modem_at_command_full ( modem, ctx->primary, "ATDT008816000025", MM_BASE_BEARER_DEFAULT_CONNECTION_TIMEOUT, FALSE, FALSE, /* raw */ NULL, /* cancellable */ (GAsyncReadyCallback)dial_ready, task); } static void connect (MMBaseBearer *self, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { ConnectContext *ctx; GTask *task; MMBaseModem *modem = NULL; task = g_task_new (self, cancellable, callback, user_data); if (mm_bearer_properties_get_multiplex (mm_base_bearer_peek_config (self)) == MM_BEARER_MULTIPLEX_SUPPORT_REQUIRED) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "Multiplex support not available"); g_object_unref (task); return; } g_object_get (self, MM_BASE_BEARER_MODEM, &modem, NULL); g_assert (modem); /* Don't bother to get primary and check if connected and all that; we * already do this check when sending the ATDT call */ /* In this context, we only keep the stuff we'll need later */ ctx = g_new0 (ConnectContext, 1); ctx->primary = mm_base_modem_get_port_primary (modem); g_task_set_task_data (task, ctx, (GDestroyNotify) connect_context_free); /* Bearer service type set to 9600bps (V.110), which behaves better than the * default 9600bps (V.32). */ mm_base_modem_at_command_full ( modem, ctx->primary, "+CBST=71,0,1", 3, FALSE, FALSE, /* raw */ NULL, /* cancellable */ (GAsyncReadyCallback)service_type_ready, task); g_object_unref (modem); } /*****************************************************************************/ MMBaseBearer * mm_bearer_iridium_new (MMBroadbandModemIridium *modem, MMBearerProperties *config) { MMBaseBearer *bearer; /* The Iridium bearer inherits from MMBaseBearer (so it's not a MMBroadbandBearer) * and that means that the object is not async-initable, so we just use * g_object_get() here */ bearer = g_object_new (MM_TYPE_BEARER_IRIDIUM, MM_BASE_BEARER_MODEM, modem, MM_BASE_BEARER_CONFIG, config, "ip-timeout", BEARER_IRIDIUM_IP_TIMEOUT_DEFAULT, NULL); /* Only export valid bearers */ mm_base_bearer_export (bearer); return bearer; } static void mm_bearer_iridium_init (MMBearerIridium *self) { } static void mm_bearer_iridium_class_init (MMBearerIridiumClass *klass) { MMBaseBearerClass *base_bearer_class = MM_BASE_BEARER_CLASS (klass); /* Virtual methods */ base_bearer_class->connect = connect; base_bearer_class->connect_finish = connect_finish; base_bearer_class->load_connection_status = NULL; base_bearer_class->load_connection_status_finish = NULL; #if defined WITH_SUSPEND_RESUME base_bearer_class->reload_connection_status = NULL; base_bearer_class->reload_connection_status_finish = NULL; #endif } ModemManager-1.23.4-dev/src/plugins/iridium/mm-bearer-iridium.h000066400000000000000000000041021456466623000243140ustar00rootroot00000000000000/* -*- 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: * * Author: Aleksander Morgado * * Copyright (C) 2012 Ammonit Measurement GmbH. */ #ifndef MM_BEARER_IRIDIUM_H #define MM_BEARER_IRIDIUM_H #include #include #define _LIBMM_INSIDE_MM #include #include "mm-base-bearer.h" #include "mm-broadband-modem-iridium.h" #define MM_TYPE_BEARER_IRIDIUM (mm_bearer_iridium_get_type ()) #define MM_BEARER_IRIDIUM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_BEARER_IRIDIUM, MMBearerIridium)) #define MM_BEARER_IRIDIUM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_BEARER_IRIDIUM, MMBearerIridiumClass)) #define MM_IS_BEARER_IRIDIUM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_BEARER_IRIDIUM)) #define MM_IS_BEARER_IRIDIUM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_BEARER_IRIDIUM)) #define MM_BEARER_IRIDIUM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_BEARER_IRIDIUM, MMBearerIridiumClass)) typedef struct _MMBearerIridium MMBearerIridium; typedef struct _MMBearerIridiumClass MMBearerIridiumClass; struct _MMBearerIridium { MMBaseBearer parent; }; struct _MMBearerIridiumClass { MMBaseBearerClass parent; }; GType mm_bearer_iridium_get_type (void); /* Iridium bearer creation implementation. * NOTE it is *not* a broadband bearer, so not async-initable */ MMBaseBearer *mm_bearer_iridium_new (MMBroadbandModemIridium *modem, MMBearerProperties *config); #endif /* MM_BEARER_IRIDIUM_H */ ModemManager-1.23.4-dev/src/plugins/iridium/mm-broadband-modem-iridium.c000066400000000000000000000347251456466623000261000ustar00rootroot00000000000000/* -*- 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) 2011 - 2012 Ammonit Measurement GmbH * Author: Aleksander Morgado */ #include #include #include #include #include #include #include "ModemManager.h" #include "mm-log-object.h" #include "mm-errors-types.h" #include "mm-base-modem-at.h" #include "mm-iface-modem.h" #include "mm-iface-modem-3gpp.h" #include "mm-iface-modem-messaging.h" #include "mm-broadband-modem-iridium.h" #include "mm-sim-iridium.h" #include "mm-bearer-iridium.h" #include "mm-modem-helpers.h" static void iface_modem_init (MMIfaceModem *iface); static void iface_modem_3gpp_init (MMIfaceModem3gpp *iface); static void iface_modem_messaging_init (MMIfaceModemMessaging *iface); G_DEFINE_TYPE_EXTENDED (MMBroadbandModemIridium, mm_broadband_modem_iridium, MM_TYPE_BROADBAND_MODEM, 0, G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM, iface_modem_init) G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_3GPP, iface_modem_3gpp_init) G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_MESSAGING, iface_modem_messaging_init)) /*****************************************************************************/ /* Operator Code loading (3GPP interface) */ static gchar * load_operator_code_finish (MMIfaceModem3gpp *self, GAsyncResult *res, GError **error) { return g_task_propagate_pointer (G_TASK (res), error); } static void load_operator_code (MMIfaceModem3gpp *self, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; task = g_task_new (self, NULL, callback, user_data); /* Only "90103" operator code is assumed */ g_task_return_pointer (task, g_strdup ("90103"), g_free); g_object_unref (task); } /*****************************************************************************/ /* Operator Name loading (3GPP interface) */ static gchar * load_operator_name_finish (MMIfaceModem3gpp *self, GAsyncResult *res, GError **error) { return g_task_propagate_pointer (G_TASK (res), error); } static void load_operator_name (MMIfaceModem3gpp *self, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; task = g_task_new (self, NULL, callback, user_data); /* Only "IRIDIUM" operator name is assumed */ g_task_return_pointer (task, g_strdup ("IRIDIUM"), g_free); g_object_unref (task); } /*****************************************************************************/ /* Enable unsolicited events (SMS indications) (Messaging interface) */ static gboolean messaging_enable_unsolicited_events_finish (MMIfaceModemMessaging *self, GAsyncResult *res, GError **error) { return !!mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, error); } static void messaging_enable_unsolicited_events (MMIfaceModemMessaging *self, GAsyncReadyCallback callback, gpointer user_data) { /* AT+CNMI=,[[,[,[,]]]] * but can only be 0, * and can only be either 0 or 1 * * Note: Modem may return +CMS ERROR:322, which indicates Memory Full, * not a big deal */ mm_base_modem_at_command (MM_BASE_MODEM (self), "+CNMI=2,1,0,0,1", 3, FALSE, callback, user_data); } /*****************************************************************************/ /* Signal quality (Modem interface) */ static guint load_signal_quality_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { gint quality = 0; const gchar *result; result = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, error); if (!result) return 0; /* Skip possible whitespaces after '+CSQF:' and before the response */ result = mm_strip_tag (result, "+CSQF:"); while (*result == ' ') result++; if (sscanf (result, "%d", &quality)) /* Normalize the quality. is NOT given in dBs, * given as a relative value between 0 and 5 */ quality = CLAMP (quality, 0, 5) * 100 / 5; else g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Could not parse signal quality results"); return quality; } static void load_signal_quality (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data) { /* The iridium modem may have a huge delay to get signal quality if we pass * AT+CSQ, so we'll default to use AT+CSQF, which is a fast version that * returns right away the last signal quality value retrieved */ mm_base_modem_at_command (MM_BASE_MODEM (self), "+CSQF", 3, FALSE, callback, user_data); } /*****************************************************************************/ /* Flow control (Modem interface) */ static gboolean setup_flow_control_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void setup_flow_control_ready (MMBroadbandModemIridium *self, GAsyncResult *res, GTask *task) { GError *error = NULL; if (!mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error)) /* Let the error be critical. We DO need RTS/CTS in order to have * proper modem disabling. */ g_task_return_error (task, error); else g_task_return_boolean (task, TRUE); g_object_unref (task); } static void setup_flow_control (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data) { /* Enable RTS/CTS flow control. * Other available values: * AT&K0: Disable flow control * AT&K3: RTS/CTS * AT&K4: XOFF/XON * AT&K6: Both RTS/CTS and XOFF/XON */ g_object_set (self, MM_BROADBAND_MODEM_FLOW_CONTROL, MM_FLOW_CONTROL_RTS_CTS, NULL); mm_base_modem_at_command (MM_BASE_MODEM (self), "&K3", 3, FALSE, (GAsyncReadyCallback)setup_flow_control_ready, g_task_new (self, NULL, callback, user_data)); } /*****************************************************************************/ /* Load supported modes (Modem inteface) */ static GArray * load_supported_modes_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { return g_task_propagate_pointer (G_TASK (res), error); } static void load_supported_modes (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data) { GArray *combinations; MMModemModeCombination mode; GTask *task; /* Build list of combinations */ combinations = g_array_sized_new (FALSE, FALSE, sizeof (MMModemModeCombination), 1); /* Report CS only, Iridium connections are circuit-switched */ mode.allowed = MM_MODEM_MODE_CS; mode.preferred = MM_MODEM_MODE_NONE; g_array_append_val (combinations, mode); task = g_task_new (self, NULL, callback, user_data); g_task_return_pointer (task, combinations, (GDestroyNotify) g_array_unref); g_object_unref (task); } /*****************************************************************************/ /* Create SIM (Modem inteface) */ static MMBaseSim * create_sim_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { return mm_sim_iridium_new_finish (res, error); } static void create_sim (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data) { /* New Iridium SIM */ mm_sim_iridium_new (MM_BASE_MODEM (self), NULL, /* cancellable */ callback, user_data); } /*****************************************************************************/ /* Create Bearer (Modem interface) */ static MMBaseBearer * create_bearer_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { return g_task_propagate_pointer (G_TASK (res), error); } static void create_bearer (MMIfaceModem *self, MMBearerProperties *properties, GAsyncReadyCallback callback, gpointer user_data) { MMBaseBearer *bearer; GTask *task; mm_obj_dbg (self, "creating Iridium bearer..."); bearer = mm_bearer_iridium_new (MM_BROADBAND_MODEM_IRIDIUM (self), properties); task = g_task_new (self, NULL, callback, user_data); g_task_return_pointer (task, bearer, g_object_unref); g_object_unref (task); } /*****************************************************************************/ static const gchar *primary_init_sequence[] = { /* Disable echo */ "E0", /* Get word responses */ "V1", /* Extended numeric codes */ "+CMEE=1", NULL }; static void setup_ports (MMBroadbandModem *self) { MMPortSerialAt *primary; /* Call parent's setup ports first always */ MM_BROADBAND_MODEM_CLASS (mm_broadband_modem_iridium_parent_class)->setup_ports (self); /* Set 9600 baudrate by default in the AT port */ mm_obj_dbg (self, "baudrate will be set to 9600 bps..."); primary = mm_base_modem_peek_port_primary (MM_BASE_MODEM (self)); if (!primary) return; g_object_set (G_OBJECT (primary), MM_PORT_SERIAL_BAUD, 9600, MM_PORT_SERIAL_AT_INIT_SEQUENCE, primary_init_sequence, NULL); } /*****************************************************************************/ MMBroadbandModemIridium * mm_broadband_modem_iridium_new (const gchar *device, const gchar *physdev, const gchar **drivers, const gchar *plugin, guint16 vendor_id, guint16 product_id) { return g_object_new (MM_TYPE_BROADBAND_MODEM_IRIDIUM, MM_BASE_MODEM_DEVICE, device, MM_BASE_MODEM_PHYSDEV, physdev, MM_BASE_MODEM_DRIVERS, drivers, MM_BASE_MODEM_PLUGIN, plugin, MM_BASE_MODEM_VENDOR_ID, vendor_id, MM_BASE_MODEM_PRODUCT_ID, product_id, /* Iridium bearer supports TTY only */ MM_BASE_MODEM_DATA_NET_SUPPORTED, FALSE, MM_BASE_MODEM_DATA_TTY_SUPPORTED, TRUE, /* Allow only up to 3 consecutive timeouts in the serial port */ MM_BASE_MODEM_MAX_TIMEOUTS, 3, /* Only CS network is supported by the Iridium modem */ MM_IFACE_MODEM_3GPP_PS_NETWORK_SUPPORTED, FALSE, NULL); } static void mm_broadband_modem_iridium_init (MMBroadbandModemIridium *self) { } static void iface_modem_init (MMIfaceModem *iface) { /* Create Iridium-specific SIM and bearer*/ iface->create_sim = create_sim; iface->create_sim_finish = create_sim_finish; iface->create_bearer = create_bearer; iface->create_bearer_finish = create_bearer_finish; /* CSQF-based signal quality */ iface->load_signal_quality = load_signal_quality; iface->load_signal_quality_finish = load_signal_quality_finish; /* RTS/CTS flow control */ iface->setup_flow_control = setup_flow_control; iface->setup_flow_control_finish = setup_flow_control_finish; /* No need to power-up/power-down the modem */ iface->load_power_state = NULL; iface->load_power_state_finish = NULL; iface->modem_power_up = NULL; iface->modem_power_up_finish = NULL; iface->modem_power_down = NULL; iface->modem_power_down_finish = NULL; /* Supported modes cannot be queried */ iface->load_supported_modes = load_supported_modes; iface->load_supported_modes_finish = load_supported_modes_finish; } static void iface_modem_3gpp_init (MMIfaceModem3gpp *iface) { /* Fixed operator code and name to be reported */ iface->load_operator_name = load_operator_name; iface->load_operator_name_finish = load_operator_name_finish; iface->load_operator_code = load_operator_code; iface->load_operator_code_finish = load_operator_code_finish; /* Don't try to scan networks with AT+COPS=?. * It does work, but it will only reply about the Iridium network * being found (so not very helpful, as that is the only one expected), but * also, it will use a non-standard reply format. Instead of supporting that * specific format used, just fully skip it. * For reference, the result is: * +COPS:(002),"IRIDIUM","IRIDIUM","90103",,(000-001),(000-002) */ iface->scan_networks = NULL; iface->scan_networks_finish = NULL; } static void iface_modem_messaging_init (MMIfaceModemMessaging *iface) { iface->enable_unsolicited_events = messaging_enable_unsolicited_events; iface->enable_unsolicited_events_finish = messaging_enable_unsolicited_events_finish; } static void mm_broadband_modem_iridium_class_init (MMBroadbandModemIridiumClass *klass) { MMBroadbandModemClass *broadband_modem_class = MM_BROADBAND_MODEM_CLASS (klass); broadband_modem_class->setup_ports = setup_ports; } ModemManager-1.23.4-dev/src/plugins/iridium/mm-broadband-modem-iridium.h000066400000000000000000000046171456466623000261020ustar00rootroot00000000000000/* -*- 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 - 2011 Red Hat, Inc. * Copyright (C) 2011 Google Inc. */ #ifndef MM_BROADBAND_MODEM_IRIDIUM_H #define MM_BROADBAND_MODEM_IRIDIUM_H #include "mm-broadband-modem.h" #define MM_TYPE_BROADBAND_MODEM_IRIDIUM (mm_broadband_modem_iridium_get_type ()) #define MM_BROADBAND_MODEM_IRIDIUM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_BROADBAND_MODEM_IRIDIUM, MMBroadbandModemIridium)) #define MM_BROADBAND_MODEM_IRIDIUM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_BROADBAND_MODEM_IRIDIUM, MMBroadbandModemIridiumClass)) #define MM_IS_BROADBAND_MODEM_IRIDIUM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_BROADBAND_MODEM_IRIDIUM)) #define MM_IS_BROADBAND_MODEM_IRIDIUM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_BROADBAND_MODEM_IRIDIUM)) #define MM_BROADBAND_MODEM_IRIDIUM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_BROADBAND_MODEM_IRIDIUM, MMBroadbandModemIridiumClass)) typedef struct _MMBroadbandModemIridium MMBroadbandModemIridium; typedef struct _MMBroadbandModemIridiumClass MMBroadbandModemIridiumClass; struct _MMBroadbandModemIridium { MMBroadbandModem parent; }; struct _MMBroadbandModemIridiumClass{ MMBroadbandModemClass parent; }; GType mm_broadband_modem_iridium_get_type (void); MMBroadbandModemIridium *mm_broadband_modem_iridium_new (const gchar *device, const gchar *physdev, const gchar **drivers, const gchar *plugin, guint16 vendor_id, guint16 product_id); #endif /* MM_BROADBAND_MODEM_IRIDIUM_H */ ModemManager-1.23.4-dev/src/plugins/iridium/mm-plugin-iridium.c000066400000000000000000000064371456466623000243620ustar00rootroot00000000000000/* -*- 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. * * 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. * * Copyright (C) 2011 - 2012 Ammonit Measurement GmbH * Author: Aleksander Morgado */ #include #include #define _LIBMM_INSIDE_MM #include #include "mm-plugin-common.h" #include "mm-broadband-modem-iridium.h" #include "mm-private-boxed-types.h" #define MM_TYPE_PLUGIN_IRIDIUM mm_plugin_iridium_get_type () MM_DEFINE_PLUGIN (IRIDIUM, iridium, Iridium) /*****************************************************************************/ static MMBaseModem * create_modem (MMPlugin *self, const gchar *uid, const gchar *physdev, const gchar **drivers, guint16 vendor, guint16 product, guint16 subsystem_vendor, GList *probes, GError **error) { return MM_BASE_MODEM (mm_broadband_modem_iridium_new (uid, physdev, drivers, mm_plugin_get_name (self), vendor, product)); } /*****************************************************************************/ MM_PLUGIN_NAMED_CREATOR_SCOPE MMPlugin * mm_plugin_create_iridium (void) { static const gchar *subsystems[] = { "tty", NULL }; static const guint16 vendor_ids[] = { 0x1edd, 0 }; static const gchar *vendor_strings[] = { "iridium", NULL }; /* Also support motorola-branded Iridium modems */ static const mm_str_pair product_strings[] = {{(gchar *)"motorola", (gchar *)"satellite" }, { NULL, NULL }}; return MM_PLUGIN ( g_object_new (MM_TYPE_PLUGIN_IRIDIUM, MM_PLUGIN_NAME, MM_MODULE_NAME, MM_PLUGIN_ALLOWED_SUBSYSTEMS, subsystems, MM_PLUGIN_ALLOWED_VENDOR_STRINGS, vendor_strings, MM_PLUGIN_ALLOWED_PRODUCT_STRINGS, product_strings, MM_PLUGIN_ALLOWED_VENDOR_IDS, vendor_ids, MM_PLUGIN_ALLOWED_AT, TRUE, NULL)); } static void mm_plugin_iridium_init (MMPluginIridium *self) { } static void mm_plugin_iridium_class_init (MMPluginIridiumClass *klass) { MMPluginClass *plugin_class = MM_PLUGIN_CLASS (klass); plugin_class->create_modem = create_modem; } ModemManager-1.23.4-dev/src/plugins/iridium/mm-sim-iridium.c000066400000000000000000000060221456466623000236420ustar00rootroot00000000000000/* -*- 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) 2011 - 2012 Ammonit Measurement GmbH. * Author: Aleksander Morgado */ #include #include #include #include #include #include #include #define _LIBMM_INSIDE_MM #include #include "mm-sim-iridium.h" G_DEFINE_TYPE (MMSimIridium, mm_sim_iridium, MM_TYPE_BASE_SIM) /*****************************************************************************/ MMBaseSim * mm_sim_iridium_new_finish (GAsyncResult *res, GError **error) { GObject *source; GObject *sim; source = g_async_result_get_source_object (res); sim = g_async_initable_new_finish (G_ASYNC_INITABLE (source), res, error); g_object_unref (source); if (!sim) return NULL; /* Only export valid SIMs */ mm_base_sim_export (MM_BASE_SIM (sim)); return MM_BASE_SIM (sim); } void mm_sim_iridium_new (MMBaseModem *modem, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { g_async_initable_new_async (MM_TYPE_SIM_IRIDIUM, G_PRIORITY_DEFAULT, cancellable, callback, user_data, MM_BASE_SIM_MODEM, modem, "active", TRUE, /* by default always active */ NULL); } static void mm_sim_iridium_init (MMSimIridium *self) { } static void mm_sim_iridium_class_init (MMSimIridiumClass *klass) { MMBaseSimClass *base_sim_class = MM_BASE_SIM_CLASS (klass); /* Skip querying the SIM card info, not supported by Iridium modems */ base_sim_class->load_sim_identifier = NULL; base_sim_class->load_sim_identifier_finish = NULL; base_sim_class->load_imsi = NULL; base_sim_class->load_imsi_finish = NULL; base_sim_class->load_operator_identifier = NULL; base_sim_class->load_operator_identifier_finish = NULL; base_sim_class->load_operator_name = NULL; base_sim_class->load_operator_name_finish = NULL; /* Skip managing preferred networks, not applicable to Iridium modems */ base_sim_class->load_preferred_networks = NULL; base_sim_class->load_preferred_networks_finish = NULL; base_sim_class->set_preferred_networks = NULL; base_sim_class->set_preferred_networks_finish = NULL; } ModemManager-1.23.4-dev/src/plugins/iridium/mm-sim-iridium.h000066400000000000000000000040261456466623000236510ustar00rootroot00000000000000/* -*- 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) 2011 - 2012 Ammonit Measurement GmbH. * Author: Aleksander Morgado */ #ifndef MM_SIM_IRIDIUM_H #define MM_SIM_IRIDIUM_H #include #include #include "mm-base-sim.h" #define MM_TYPE_SIM_IRIDIUM (mm_sim_iridium_get_type ()) #define MM_SIM_IRIDIUM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_SIM_IRIDIUM, MMSimIridium)) #define MM_SIM_IRIDIUM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_SIM_IRIDIUM, MMSimIridiumClass)) #define MM_IS_SIM_IRIDIUM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_SIM_IRIDIUM)) #define MM_IS_SIM_IRIDIUM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_SIM_IRIDIUM)) #define MM_SIM_IRIDIUM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_SIM_IRIDIUM, MMSimIridiumClass)) typedef struct _MMSimIridium MMSimIridium; typedef struct _MMSimIridiumClass MMSimIridiumClass; struct _MMSimIridium { MMBaseSim parent; }; struct _MMSimIridiumClass { MMBaseSimClass parent; }; GType mm_sim_iridium_get_type (void); void mm_sim_iridium_new (MMBaseModem *modem, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); MMBaseSim *mm_sim_iridium_new_finish (GAsyncResult *res, GError **error); #endif /* MM_SIM_IRIDIUM_H */ ModemManager-1.23.4-dev/src/plugins/linktop/000077500000000000000000000000001456466623000206555ustar00rootroot00000000000000ModemManager-1.23.4-dev/src/plugins/linktop/77-mm-linktop-port-types.rules000066400000000000000000000012711456466623000264000ustar00rootroot00000000000000# do not edit this file, it will be overwritten on update ACTION!="add|change|move|bind", GOTO="mm_linktop_end" SUBSYSTEMS=="usb", ATTRS{idVendor}=="230d", GOTO="mm_linktop_generic" GOTO="mm_linktop_end" LABEL="mm_linktop_generic" SUBSYSTEMS=="usb", ATTRS{bInterfaceNumber}=="?*", ENV{.MM_USBIFNUM}="$attr{bInterfaceNumber}" # Linktop HSPADataCard # ttyACM0 (if #1): Data port # ttyACM1 (if #3): Primary AT port ATTRS{idVendor}=="230d", ATTRS{idProduct}=="0001", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PPP}="1" ATTRS{idVendor}=="230d", ATTRS{idProduct}=="0001", ENV{.MM_USBIFNUM}=="03", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1" LABEL="mm_linktop_end" ModemManager-1.23.4-dev/src/plugins/linktop/mm-broadband-modem-linktop.c000066400000000000000000000212501456466623000261210ustar00rootroot00000000000000/* -*- 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 - 2012 Red Hat, Inc. * Copyright (C) 2012 Aleksander Morgado */ #include #include #include #include #include #include #define _LIBMM_INSIDE_MM #include #include "ModemManager.h" #include "mm-serial-parsers.h" #include "mm-modem-helpers.h" #include "mm-iface-modem.h" #include "mm-base-modem-at.h" #include "mm-broadband-modem-linktop.h" #include "mm-modem-helpers-linktop.h" static void iface_modem_init (MMIfaceModem *iface); static MMIfaceModem *iface_modem_parent; G_DEFINE_TYPE_EXTENDED (MMBroadbandModemLinktop, mm_broadband_modem_linktop, MM_TYPE_BROADBAND_MODEM, 0, G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM, iface_modem_init)) /*****************************************************************************/ /* Load supported modes (Modem interface) */ static GArray * load_supported_modes_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { return g_task_propagate_pointer (G_TASK (res), error); } static void parent_load_supported_modes_ready (MMIfaceModem *self, GAsyncResult *res, GTask *task) { GError *error = NULL; GArray *all; GArray *combinations; GArray *filtered; MMModemModeCombination mode; all = iface_modem_parent->load_supported_modes_finish (self, res, &error); if (!all) { g_task_return_error (task, error); g_object_unref (task); return; } /* Build list of combinations */ combinations = g_array_sized_new (FALSE, FALSE, sizeof (MMModemModeCombination), 3); /* 2G only */ mode.allowed = MM_MODEM_MODE_2G; mode.preferred = MM_MODEM_MODE_NONE; g_array_append_val (combinations, mode); /* 3G only */ mode.allowed = MM_MODEM_MODE_3G; mode.preferred = MM_MODEM_MODE_NONE; g_array_append_val (combinations, mode); /* 2G and 3G */ mode.allowed = (MM_MODEM_MODE_2G | MM_MODEM_MODE_3G); mode.preferred = MM_MODEM_MODE_NONE; g_array_append_val (combinations, mode); /* Filter out those unsupported modes */ filtered = mm_filter_supported_modes (all, combinations, self); g_array_unref (all); g_array_unref (combinations); g_task_return_pointer (task, filtered, (GDestroyNotify) g_array_unref); g_object_unref (task); } static void load_supported_modes (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data) { /* Run parent's loading */ iface_modem_parent->load_supported_modes ( MM_IFACE_MODEM (self), (GAsyncReadyCallback)parent_load_supported_modes_ready, g_task_new (self, NULL, callback, user_data)); } /*****************************************************************************/ /* Load initial allowed/preferred modes (Modem interface) */ static gboolean load_current_modes_finish (MMIfaceModem *self, GAsyncResult *res, MMModemMode *allowed, MMModemMode *preferred, GError **error) { const gchar *response; response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, error); if (!response || !mm_linktop_parse_cfun_query_current_modes (response, allowed, error)) return FALSE; /* None preferred always */ *preferred = MM_MODEM_MODE_NONE; return TRUE; } static void load_current_modes (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data) { mm_base_modem_at_command (MM_BASE_MODEM (self), "+CFUN?", 3, FALSE, callback, user_data); } /*****************************************************************************/ /* Set allowed modes (Modem interface) */ static gboolean set_current_modes_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void allowed_mode_update_ready (MMBroadbandModemLinktop *self, GAsyncResult *res, GTask *task) { GError *error = NULL; mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error); if (error) /* Let the error be critical. */ g_task_return_error (task, error); else g_task_return_boolean (task, TRUE); g_object_unref (task); } static void set_current_modes (MMIfaceModem *self, MMModemMode allowed, MMModemMode preferred, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; gchar *command; gint linktop_mode = -1; task = g_task_new (self, NULL, callback, user_data); if (allowed == MM_MODEM_MODE_2G) linktop_mode = LINKTOP_MODE_2G; else if (allowed == MM_MODEM_MODE_3G) linktop_mode = LINKTOP_MODE_3G; else if ((allowed == (MM_MODEM_MODE_2G | MM_MODEM_MODE_3G)) && (preferred == MM_MODEM_MODE_NONE)) linktop_mode = LINKTOP_MODE_ANY; else if ((allowed == MM_MODEM_MODE_ANY && preferred == MM_MODEM_MODE_NONE)) linktop_mode = LINKTOP_MODE_ANY; if (linktop_mode < 0) { gchar *allowed_str; gchar *preferred_str; allowed_str = mm_modem_mode_build_string_from_mask (allowed); preferred_str = mm_modem_mode_build_string_from_mask (preferred); g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Requested mode (allowed: '%s', preferred: '%s') not " "supported by the modem.", allowed_str, preferred_str); g_object_unref (task); g_free (allowed_str); g_free (preferred_str); return; } command = g_strdup_printf ("AT+CFUN=%d", linktop_mode); mm_base_modem_at_command ( MM_BASE_MODEM (self), command, 3, FALSE, (GAsyncReadyCallback)allowed_mode_update_ready, task); g_free (command); } /*****************************************************************************/ MMBroadbandModemLinktop * mm_broadband_modem_linktop_new (const gchar *device, const gchar *physdev, const gchar **drivers, const gchar *plugin, guint16 vendor_id, guint16 product_id) { return g_object_new (MM_TYPE_BROADBAND_MODEM_LINKTOP, MM_BASE_MODEM_DEVICE, device, MM_BASE_MODEM_PHYSDEV, physdev, MM_BASE_MODEM_DRIVERS, drivers, MM_BASE_MODEM_PLUGIN, plugin, MM_BASE_MODEM_VENDOR_ID, vendor_id, MM_BASE_MODEM_PRODUCT_ID, product_id, /* Generic bearer supports AT only */ MM_BASE_MODEM_DATA_NET_SUPPORTED, FALSE, MM_BASE_MODEM_DATA_TTY_SUPPORTED, TRUE, NULL); } static void mm_broadband_modem_linktop_init (MMBroadbandModemLinktop *self) { } static void iface_modem_init (MMIfaceModem *iface) { iface_modem_parent = g_type_interface_peek_parent (iface); iface->load_supported_modes = load_supported_modes; iface->load_supported_modes_finish = load_supported_modes_finish; iface->load_current_modes = load_current_modes; iface->load_current_modes_finish = load_current_modes_finish; iface->set_current_modes = set_current_modes; iface->set_current_modes_finish = set_current_modes_finish; } static void mm_broadband_modem_linktop_class_init (MMBroadbandModemLinktopClass *klass) { } ModemManager-1.23.4-dev/src/plugins/linktop/mm-broadband-modem-linktop.h000066400000000000000000000046531456466623000261360ustar00rootroot00000000000000/* -*- 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 - 2012 Red Hat, Inc. * Copyright (C) 2012 Aleksander Morgado */ #ifndef MM_BROADBAND_MODEM_LINKTOP_H #define MM_BROADBAND_MODEM_LINKTOP_H #include "mm-broadband-modem.h" #define MM_TYPE_BROADBAND_MODEM_LINKTOP (mm_broadband_modem_linktop_get_type ()) #define MM_BROADBAND_MODEM_LINKTOP(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_BROADBAND_MODEM_LINKTOP, MMBroadbandModemLinktop)) #define MM_BROADBAND_MODEM_LINKTOP_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_BROADBAND_MODEM_LINKTOP, MMBroadbandModemLinktopClass)) #define MM_IS_BROADBAND_MODEM_LINKTOP(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_BROADBAND_MODEM_LINKTOP)) #define MM_IS_BROADBAND_MODEM_LINKTOP_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_BROADBAND_MODEM_LINKTOP)) #define MM_BROADBAND_MODEM_LINKTOP_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_BROADBAND_MODEM_LINKTOP, MMBroadbandModemLinktopClass)) typedef struct _MMBroadbandModemLinktop MMBroadbandModemLinktop; typedef struct _MMBroadbandModemLinktopClass MMBroadbandModemLinktopClass; struct _MMBroadbandModemLinktop { MMBroadbandModem parent; }; struct _MMBroadbandModemLinktopClass{ MMBroadbandModemClass parent; }; GType mm_broadband_modem_linktop_get_type (void); MMBroadbandModemLinktop *mm_broadband_modem_linktop_new (const gchar *device, const gchar *physdev, const gchar **drivers, const gchar *plugin, guint16 vendor_id, guint16 product_id); #endif /* MM_BROADBAND_MODEM_LINKTOP_H */ ModemManager-1.23.4-dev/src/plugins/linktop/mm-modem-helpers-linktop.c000066400000000000000000000034531456466623000256540ustar00rootroot00000000000000/* -*- 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 - 2016 Red Hat, Inc. * Copyright (C) 2016 Aleksander Morgado */ #include "mm-modem-helpers.h" #include "mm-modem-helpers-linktop.h" /*****************************************************************************/ gboolean mm_linktop_parse_cfun_query_current_modes (const gchar *response, MMModemMode *allowed, GError **error) { guint state; g_assert (allowed); if (!mm_3gpp_parse_cfun_query_response (response, &state, error)) return FALSE; switch (state) { case LINKTOP_MODE_OFFLINE: case LINKTOP_MODE_LOW_POWER: *allowed = MM_MODEM_MODE_NONE; return TRUE; case LINKTOP_MODE_2G: *allowed = MM_MODEM_MODE_2G; return TRUE; case LINKTOP_MODE_3G: *allowed = MM_MODEM_MODE_3G; return TRUE; case LINKTOP_MODE_ANY: *allowed = (MM_MODEM_MODE_2G | MM_MODEM_MODE_3G); return TRUE; default: g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Unknown linktop +CFUN current mode: %u", state); return FALSE; } } ModemManager-1.23.4-dev/src/plugins/linktop/mm-modem-helpers-linktop.h000066400000000000000000000025711456466623000256610ustar00rootroot00000000000000/* -*- 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 - 2016 Red Hat, Inc. * Copyright (C) 2016 Aleksander Morgado */ #ifndef MM_MODEM_HELPERS_LINKTOP_H #define MM_MODEM_HELPERS_LINKTOP_H #include #include #define _LIBMM_INSIDE_MM #include typedef enum { LINKTOP_MODE_OFFLINE = 0, LINKTOP_MODE_ANY = 1, LINKTOP_MODE_LOW_POWER = 4, LINKTOP_MODE_2G = 5, LINKTOP_MODE_3G = 6, } MMLinktopMode; /* AT+CFUN? response parsers */ gboolean mm_linktop_parse_cfun_query_current_modes (const gchar *response, MMModemMode *allowed, GError **error); #endif /* MM_MODEM_HELPERS_LINKTOP_H */ ModemManager-1.23.4-dev/src/plugins/linktop/mm-plugin-linktop.c000066400000000000000000000052101456466623000244020ustar00rootroot00000000000000/* -*- 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 - 2012 Red Hat, Inc. * Copyright (C) 2012 Aleksander Morgado */ #include #include #define _LIBMM_INSIDE_MM #include #include "mm-plugin-common.h" #include "mm-broadband-modem-linktop.h" #define MM_TYPE_PLUGIN_LINKTOP mm_plugin_linktop_get_type () MM_DEFINE_PLUGIN (LINKTOP, linktop, Linktop) /*****************************************************************************/ static MMBaseModem * create_modem (MMPlugin *self, const gchar *uid, const gchar *physdev, const gchar **drivers, guint16 vendor, guint16 product, guint16 subsystem_vendor, GList *probes, GError **error) { return MM_BASE_MODEM (mm_broadband_modem_linktop_new (uid, physdev, drivers, mm_plugin_get_name (self), vendor, product)); } /*****************************************************************************/ MM_PLUGIN_NAMED_CREATOR_SCOPE MMPlugin * mm_plugin_create_linktop (void) { static const gchar *subsystems[] = { "tty", NULL }; static const guint16 vendor_ids[] = { 0x230d, 0 }; return MM_PLUGIN ( g_object_new (MM_TYPE_PLUGIN_LINKTOP, MM_PLUGIN_NAME, MM_MODULE_NAME, MM_PLUGIN_ALLOWED_SUBSYSTEMS, subsystems, MM_PLUGIN_ALLOWED_VENDOR_IDS, vendor_ids, MM_PLUGIN_ALLOWED_AT, TRUE, NULL)); } static void mm_plugin_linktop_init (MMPluginLinktop *self) { } static void mm_plugin_linktop_class_init (MMPluginLinktopClass *klass) { MMPluginClass *plugin_class = MM_PLUGIN_CLASS (klass); plugin_class->create_modem = create_modem; } ModemManager-1.23.4-dev/src/plugins/linktop/tests/000077500000000000000000000000001456466623000220175ustar00rootroot00000000000000ModemManager-1.23.4-dev/src/plugins/linktop/tests/test-modem-helpers-linktop.c000066400000000000000000000043411456466623000273610ustar00rootroot00000000000000/* -*- 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) 2016 Aleksander Morgado */ #include #include #include #include #define _LIBMM_INSIDE_MM #include #include "mm-log-test.h" #include "mm-modem-helpers.h" #include "mm-modem-helpers-linktop.h" /*****************************************************************************/ typedef struct { const gchar *str; MMModemMode allowed; } CfunQueryCurrentModeTest; static const CfunQueryCurrentModeTest cfun_query_current_mode_tests[] = { { "+CFUN: 0", MM_MODEM_MODE_NONE }, { "+CFUN: 1", MM_MODEM_MODE_2G | MM_MODEM_MODE_3G }, { "+CFUN: 4", MM_MODEM_MODE_NONE }, { "+CFUN: 5", MM_MODEM_MODE_2G }, { "+CFUN: 6", MM_MODEM_MODE_3G }, }; static void test_cfun_query_current_modes (void) { guint i; for (i = 0; i < G_N_ELEMENTS (cfun_query_current_mode_tests); i++) { GError *error = NULL; gboolean success; MMModemMode allowed = MM_MODEM_MODE_NONE; success = mm_linktop_parse_cfun_query_current_modes (cfun_query_current_mode_tests[i].str, &allowed, &error); g_assert_no_error (error); g_assert (success); g_assert_cmpuint (cfun_query_current_mode_tests[i].allowed, ==, allowed); } } /*****************************************************************************/ int main (int argc, char **argv) { setlocale (LC_ALL, ""); g_test_init (&argc, &argv, NULL); g_test_add_func ("/MM/linktop/cfun/query/current-modes", test_cfun_query_current_modes); return g_test_run (); } ModemManager-1.23.4-dev/src/plugins/longcheer/000077500000000000000000000000001456466623000211435ustar00rootroot00000000000000ModemManager-1.23.4-dev/src/plugins/longcheer/77-mm-longcheer-port-types.rules000066400000000000000000000341421456466623000271570ustar00rootroot00000000000000# 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 # # Most of these values were scraped from various Longcheer-based Windows # driver .inf files. cmmdm.inf lists the actual data (ie PPP) ports, while # cmser.inf lists the aux ports that may be either AT-capable or not but # cannot be used for PPP. ACTION!="add|change|move|bind", 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{idVendor}=="1c9e", ATTRS{idProduct}=="3197", ENV{.MM_USBIFNUM}=="00", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1" ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="3197", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1" ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="3197", ENV{ID_MM_LONGCHEER_TAGGED}="1" ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="6000", ENV{.MM_USBIFNUM}=="00", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1" ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="6000", ENV{.MM_USBIFNUM}=="02", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1" ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="6000", ENV{ID_MM_LONGCHEER_TAGGED}="1" ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="6060", ENV{.MM_USBIFNUM}=="00", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1" ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="6060", ENV{.MM_USBIFNUM}=="02", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1" ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="6060", ENV{ID_MM_LONGCHEER_TAGGED}="1" # Alcatel One Touch X020 ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="6061", ENV{.MM_USBIFNUM}=="00", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1" ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="6061", ENV{.MM_USBIFNUM}=="02", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1" ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="6061", ENV{ID_MM_LONGCHEER_TAGGED}="1" ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="7001", ENV{.MM_USBIFNUM}=="02", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1" ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="7001", ENV{.MM_USBIFNUM}=="00", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1" ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="7001", ENV{.MM_USBIFNUM}=="03", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1" ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="7001", ENV{ID_MM_LONGCHEER_TAGGED}="1" ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="7002", ENV{.MM_USBIFNUM}=="02", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1" ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="7002", ENV{.MM_USBIFNUM}=="00", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1" ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="7002", ENV{.MM_USBIFNUM}=="03", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1" ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="7002", ENV{.MM_USBIFNUM}=="04", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1" ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="7002", ENV{ID_MM_LONGCHEER_TAGGED}="1" ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="7101", ENV{.MM_USBIFNUM}=="04", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1" ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="7101", ENV{.MM_USBIFNUM}=="00", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1" ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="7101", ENV{.MM_USBIFNUM}=="05", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1" ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="7101", ENV{ID_MM_LONGCHEER_TAGGED}="1" ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="7102", ENV{.MM_USBIFNUM}=="04", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1" ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="7102", ENV{.MM_USBIFNUM}=="00", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1" ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="7102", ENV{.MM_USBIFNUM}=="05", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1" ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="7102", ENV{.MM_USBIFNUM}=="06", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1" ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="7102", ENV{ID_MM_LONGCHEER_TAGGED}="1" ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="8000", ENV{.MM_USBIFNUM}=="05", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1" ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="8000", ENV{.MM_USBIFNUM}=="00", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1" ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="8000", ENV{.MM_USBIFNUM}=="02", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1" ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="8000", ENV{ID_MM_LONGCHEER_TAGGED}="1" ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="8001", ENV{.MM_USBIFNUM}=="04", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1" ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="8001", ENV{.MM_USBIFNUM}=="00", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1" ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="8001", ENV{.MM_USBIFNUM}=="02", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1" ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="8001", ENV{ID_MM_LONGCHEER_TAGGED}="1" ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="8002", ENV{.MM_USBIFNUM}=="04", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1" ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="8002", ENV{.MM_USBIFNUM}=="00", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1" ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="8002", ENV{.MM_USBIFNUM}=="02", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1" ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="8002", ENV{ID_MM_LONGCHEER_TAGGED}="1" # ChinaBird PL68 ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="9000", ENV{.MM_USBIFNUM}=="03", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1" ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="9000", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1" ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="9000", ENV{ID_MM_LONGCHEER_TAGGED}="1" ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="9001", ENV{.MM_USBIFNUM}=="02", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1" ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="9001", ENV{.MM_USBIFNUM}=="00", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1" ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="9001", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1" ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="9001", ENV{ID_MM_LONGCHEER_TAGGED}="1" ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="9002", ENV{.MM_USBIFNUM}=="02", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1" ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="9002", ENV{.MM_USBIFNUM}=="00", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1" ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="9002", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1" ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="9002", ENV{ID_MM_LONGCHEER_TAGGED}="1" ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="9003", ENV{.MM_USBIFNUM}=="03", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1" ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="9003", ENV{.MM_USBIFNUM}=="00", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1" ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="9003", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1" ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="9003", ENV{.MM_USBIFNUM}=="02", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1" ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="9003", ENV{ID_MM_LONGCHEER_TAGGED}="1" ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="9004", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1" ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="9004", ENV{.MM_USBIFNUM}=="00", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1" ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="9004", ENV{ID_MM_LONGCHEER_TAGGED}="1" ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="9005", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1" ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="9005", ENV{.MM_USBIFNUM}=="00", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1" ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="9005", ENV{ID_MM_LONGCHEER_TAGGED}="1" ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="9010", ENV{.MM_USBIFNUM}=="03", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1" ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="9010", ENV{.MM_USBIFNUM}=="00", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1" ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="9010", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1" ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="9010", ENV{ID_MM_LONGCHEER_TAGGED}="1" ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="9012", ENV{.MM_USBIFNUM}=="02", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1" ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="9012", ENV{.MM_USBIFNUM}=="00", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1" ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="9012", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1" ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="9012", ENV{ID_MM_LONGCHEER_TAGGED}="1" ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="9020", ENV{.MM_USBIFNUM}=="03", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1" ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="9020", ENV{.MM_USBIFNUM}=="00", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1" ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="9020", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1" ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="9020", ENV{ID_MM_LONGCHEER_TAGGED}="1" ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="9022", ENV{.MM_USBIFNUM}=="02", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1" ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="9022", ENV{.MM_USBIFNUM}=="00", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1" ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="9022", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1" ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="9022", ENV{ID_MM_LONGCHEER_TAGGED}="1" # Zoom products ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="9602", ENV{.MM_USBIFNUM}=="00", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1" ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="9602", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1" ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="9602", ENV{.MM_USBIFNUM}=="02", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1" ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="9602", ENV{ID_MM_LONGCHEER_TAGGED}="1" ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="9603", ENV{.MM_USBIFNUM}=="02", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1" ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="9603", ENV{.MM_USBIFNUM}=="00", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1" ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="9603", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1" ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="9603", ENV{ID_MM_LONGCHEER_TAGGED}="1" ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="9604", ENV{.MM_USBIFNUM}=="03", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1" ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="9604", ENV{.MM_USBIFNUM}=="00", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1" ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="9604", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1" ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="9604", ENV{ID_MM_LONGCHEER_TAGGED}="1" ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="9605", ENV{.MM_USBIFNUM}=="03", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1" ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="9605", ENV{.MM_USBIFNUM}=="00", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1" ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="9605", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1" ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="9605", ENV{.MM_USBIFNUM}=="02", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1" ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="9605", ENV{ID_MM_LONGCHEER_TAGGED}="1" ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="9606", ENV{.MM_USBIFNUM}=="04", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1" ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="9606", ENV{.MM_USBIFNUM}=="00", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1" ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="9606", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1" ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="9606", ENV{.MM_USBIFNUM}=="02", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1" ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="9606", ENV{ID_MM_LONGCHEER_TAGGED}="1" ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="9607", ENV{.MM_USBIFNUM}=="04", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1" ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="9607", ENV{.MM_USBIFNUM}=="00", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1" ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="9607", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1" ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="9607", ENV{.MM_USBIFNUM}=="02", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1" ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="9607", ENV{.MM_USBIFNUM}=="03", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1" ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="9607", 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{idVendor}=="1bbb", ATTRS{idProduct}=="0000", ENV{.MM_USBIFNUM}=="03", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1" ATTRS{idVendor}=="1bbb", ATTRS{idProduct}=="0000", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1" ATTRS{idVendor}=="1bbb", ATTRS{idProduct}=="0000", ENV{ID_MM_LONGCHEER_TAGGED}="1" GOTO="mm_longcheer_port_types_end" LABEL="mm_longcheer_port_types_end" ModemManager-1.23.4-dev/src/plugins/longcheer/mm-broadband-modem-longcheer.c000066400000000000000000000324631456466623000267050ustar00rootroot00000000000000/* -*- 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 - 2012 Red Hat, Inc. * Copyright (C) 2012 Aleksander Morgado */ #include #include #include #include #include #include #include "ModemManager.h" #include "mm-log.h" #include "mm-errors-types.h" #include "mm-base-modem-at.h" #include "mm-iface-modem.h" #include "mm-modem-helpers.h" #include "mm-broadband-modem-longcheer.h" static void iface_modem_init (MMIfaceModem *iface); static MMIfaceModem *iface_modem_parent; G_DEFINE_TYPE_EXTENDED (MMBroadbandModemLongcheer, mm_broadband_modem_longcheer, MM_TYPE_BROADBAND_MODEM, 0, G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM, iface_modem_init)) /*****************************************************************************/ /* Load supported modes (Modem interface) */ static GArray * load_supported_modes_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { return g_task_propagate_pointer (G_TASK (res), error); } static void parent_load_supported_modes_ready (MMIfaceModem *self, GAsyncResult *res, GTask *task) { GError *error = NULL; GArray *all; GArray *combinations; GArray *filtered; MMModemModeCombination mode; all = iface_modem_parent->load_supported_modes_finish (self, res, &error); if (!all) { g_task_return_error (task, error); g_object_unref (task); return; } /* Build list of combinations */ combinations = g_array_sized_new (FALSE, FALSE, sizeof (MMModemModeCombination), 4); /* 2G only */ mode.allowed = MM_MODEM_MODE_2G; mode.preferred = MM_MODEM_MODE_NONE; g_array_append_val (combinations, mode); /* 3G only */ mode.allowed = MM_MODEM_MODE_3G; mode.preferred = MM_MODEM_MODE_NONE; g_array_append_val (combinations, mode); /* 2G and 3G, 2G preferred */ mode.allowed = (MM_MODEM_MODE_2G | MM_MODEM_MODE_3G); mode.preferred = MM_MODEM_MODE_2G; g_array_append_val (combinations, mode); /* 2G and 3G, 3G preferred */ mode.allowed = (MM_MODEM_MODE_2G | MM_MODEM_MODE_3G); mode.preferred = MM_MODEM_MODE_3G; g_array_append_val (combinations, mode); /* Filter out those unsupported modes */ filtered = mm_filter_supported_modes (all, combinations, self); g_array_unref (all); g_array_unref (combinations); g_task_return_pointer (task, filtered, (GDestroyNotify) g_array_unref); g_object_unref (task); } static void load_supported_modes (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data) { /* Run parent's loading */ iface_modem_parent->load_supported_modes ( MM_IFACE_MODEM (self), (GAsyncReadyCallback)parent_load_supported_modes_ready, g_task_new (self, NULL, callback, user_data)); } /*****************************************************************************/ /* Load initial allowed/preferred modes (Modem interface) */ static gboolean load_current_modes_finish (MMIfaceModem *self, GAsyncResult *res, MMModemMode *allowed, MMModemMode *preferred, GError **error) { const gchar *response; const gchar *str; gint mododr = -1; response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, error); if (!response) return FALSE; str = mm_strip_tag (response, "+MODODR:"); if (!str) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't parse MODODR response: '%s'", response); return FALSE; } mododr = atoi (str); switch (mododr) { case 1: /* UMTS only */ *allowed = MM_MODEM_MODE_3G; *preferred = MM_MODEM_MODE_NONE; return TRUE; case 2: /* UMTS preferred */ *allowed = (MM_MODEM_MODE_2G | MM_MODEM_MODE_3G); *preferred = MM_MODEM_MODE_3G; return TRUE; case 3: /* GSM only */ *allowed = MM_MODEM_MODE_2G; *preferred = MM_MODEM_MODE_NONE; return TRUE; case 4: /* GSM preferred */ *allowed = (MM_MODEM_MODE_2G | MM_MODEM_MODE_3G); *preferred = MM_MODEM_MODE_2G; return TRUE; default: break; } g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't parse unexpected MODODR response: '%s'", response); return FALSE; } static void load_current_modes (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data) { mm_base_modem_at_command (MM_BASE_MODEM (self), "+MODODR?", 3, FALSE, callback, user_data); } /*****************************************************************************/ /* Set allowed modes (Modem interface) */ static gboolean set_current_modes_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void allowed_mode_update_ready (MMBroadbandModemLongcheer *self, GAsyncResult *res, GTask *task) { GError *error = NULL; mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error); if (error) /* Let the error be critical. */ g_task_return_error (task, error); else g_task_return_boolean (task, TRUE); g_object_unref (task); } static void set_current_modes (MMIfaceModem *self, MMModemMode allowed, MMModemMode preferred, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; gchar *command; gint mododr = 0; task = g_task_new (self, NULL, callback, user_data); if (allowed == MM_MODEM_MODE_2G) mododr = 3; else if (allowed == MM_MODEM_MODE_3G) mododr = 1; else if (allowed == (MM_MODEM_MODE_2G | MM_MODEM_MODE_3G)) { if (preferred == MM_MODEM_MODE_2G) mododr = 4; else if (preferred == MM_MODEM_MODE_3G) mododr = 2; } else if (allowed == MM_MODEM_MODE_ANY && preferred == MM_MODEM_MODE_NONE) /* Default to 3G preferred */ mododr = 2; if (mododr == 0) { gchar *allowed_str; gchar *preferred_str; allowed_str = mm_modem_mode_build_string_from_mask (allowed); preferred_str = mm_modem_mode_build_string_from_mask (preferred); g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Requested mode (allowed: '%s', preferred: '%s') not " "supported by the modem.", allowed_str, preferred_str); g_object_unref (task); g_free (allowed_str); g_free (preferred_str); return; } command = g_strdup_printf ("+MODODR=%d", mododr); mm_base_modem_at_command ( MM_BASE_MODEM (self), command, 3, FALSE, (GAsyncReadyCallback)allowed_mode_update_ready, task); g_free (command); } /*****************************************************************************/ /* Load access technologies (Modem interface) */ static gboolean load_access_technologies_finish (MMIfaceModem *self, GAsyncResult *res, MMModemAccessTechnology *access_technologies, guint *mask, GError **error) { const gchar *result; result = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, error); if (!result) return FALSE; result = mm_strip_tag (result, "+PSRAT:"); *access_technologies = mm_string_to_access_tech (result); *mask = MM_MODEM_ACCESS_TECHNOLOGY_ANY; return TRUE; } static void load_access_technologies (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data) { mm_base_modem_at_command (MM_BASE_MODEM (self), "+PSRAT", 3, FALSE, callback, user_data); } /*****************************************************************************/ /* Load unlock retries (Modem interface) */ static MMUnlockRetries * load_unlock_retries_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { return g_task_propagate_pointer (G_TASK (res), error); } static void load_unlock_retries_ready (MMBaseModem *self, GAsyncResult *res, GTask *task) { const gchar *response; GError *error = NULL; int pin1, puk1, pin2, puk2; response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error); if (!response) { g_task_return_error (task, error); g_object_unref (task); return; } /* That's right; no +CPNNUM: prefix, it looks like this: * * AT+CPNNUM * PIN1=3; PUK1=10; PIN2=3; PUK2=10 * OK */ if (sscanf (response, "PIN1=%d; PUK1=%d; PIN2=%d; PUK2=%d", &pin1, &puk1, &pin2, &puk2) == 4) { MMUnlockRetries *retries; retries = mm_unlock_retries_new (); mm_unlock_retries_set (retries, MM_MODEM_LOCK_SIM_PIN, pin1); mm_unlock_retries_set (retries, MM_MODEM_LOCK_SIM_PUK, puk1); mm_unlock_retries_set (retries, MM_MODEM_LOCK_SIM_PIN2, pin2); mm_unlock_retries_set (retries, MM_MODEM_LOCK_SIM_PUK2, puk2); g_task_return_pointer (task, retries, g_object_unref); } else { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Invalid unlock retries response: '%s'", response); } g_object_unref (task); } static void load_unlock_retries (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data) { mm_base_modem_at_command ( MM_BASE_MODEM (self), "+CPNNUM", 3, FALSE, (GAsyncReadyCallback)load_unlock_retries_ready, g_task_new (self, NULL, callback, user_data)); } /*****************************************************************************/ MMBroadbandModemLongcheer * mm_broadband_modem_longcheer_new (const gchar *device, const gchar *physdev, const gchar **drivers, const gchar *plugin, guint16 vendor_id, guint16 product_id) { return g_object_new (MM_TYPE_BROADBAND_MODEM_LONGCHEER, MM_BASE_MODEM_DEVICE, device, MM_BASE_MODEM_PHYSDEV, physdev, MM_BASE_MODEM_DRIVERS, drivers, MM_BASE_MODEM_PLUGIN, plugin, MM_BASE_MODEM_VENDOR_ID, vendor_id, MM_BASE_MODEM_PRODUCT_ID, product_id, /* Generic bearer supports AT only */ MM_BASE_MODEM_DATA_NET_SUPPORTED, FALSE, MM_BASE_MODEM_DATA_TTY_SUPPORTED, TRUE, NULL); } static void mm_broadband_modem_longcheer_init (MMBroadbandModemLongcheer *self) { } static void iface_modem_init (MMIfaceModem *iface) { iface_modem_parent = g_type_interface_peek_parent (iface); iface->load_access_technologies = load_access_technologies; iface->load_access_technologies_finish = load_access_technologies_finish; iface->load_supported_modes = load_supported_modes; iface->load_supported_modes_finish = load_supported_modes_finish; iface->load_current_modes = load_current_modes; iface->load_current_modes_finish = load_current_modes_finish; iface->set_current_modes = set_current_modes; iface->set_current_modes_finish = set_current_modes_finish; iface->load_unlock_retries = load_unlock_retries; iface->load_unlock_retries_finish = load_unlock_retries_finish; } static void mm_broadband_modem_longcheer_class_init (MMBroadbandModemLongcheerClass *klass) { } ModemManager-1.23.4-dev/src/plugins/longcheer/mm-broadband-modem-longcheer.h000066400000000000000000000047651456466623000267160ustar00rootroot00000000000000/* -*- 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 - 2012 Red Hat, Inc. * Copyright (C) 2012 Aleksander Morgado */ #ifndef MM_BROADBAND_MODEM_LONGCHEER_H #define MM_BROADBAND_MODEM_LONGCHEER_H #include "mm-broadband-modem.h" #define MM_TYPE_BROADBAND_MODEM_LONGCHEER (mm_broadband_modem_longcheer_get_type ()) #define MM_BROADBAND_MODEM_LONGCHEER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_BROADBAND_MODEM_LONGCHEER, MMBroadbandModemLongcheer)) #define MM_BROADBAND_MODEM_LONGCHEER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_BROADBAND_MODEM_LONGCHEER, MMBroadbandModemLongcheerClass)) #define MM_IS_BROADBAND_MODEM_LONGCHEER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_BROADBAND_MODEM_LONGCHEER)) #define MM_IS_BROADBAND_MODEM_LONGCHEER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_BROADBAND_MODEM_LONGCHEER)) #define MM_BROADBAND_MODEM_LONGCHEER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_BROADBAND_MODEM_LONGCHEER, MMBroadbandModemLongcheerClass)) typedef struct _MMBroadbandModemLongcheer MMBroadbandModemLongcheer; typedef struct _MMBroadbandModemLongcheerClass MMBroadbandModemLongcheerClass; struct _MMBroadbandModemLongcheer { MMBroadbandModem parent; }; struct _MMBroadbandModemLongcheerClass{ MMBroadbandModemClass parent; }; GType mm_broadband_modem_longcheer_get_type (void); MMBroadbandModemLongcheer *mm_broadband_modem_longcheer_new (const gchar *device, const gchar *physdev, const gchar **drivers, const gchar *plugin, guint16 vendor_id, guint16 product_id); #endif /* MM_BROADBAND_MODEM_LONGCHEER_H */ ModemManager-1.23.4-dev/src/plugins/longcheer/mm-plugin-longcheer.c000066400000000000000000000203001456466623000251530ustar00rootroot00000000000000/* -*- 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 - 2012 Red Hat, Inc. * Copyright (C) 2012 Aleksander Morgado */ #include #include #define _LIBMM_INSIDE_MM #include #include "mm-log-object.h" #include "mm-modem-helpers.h" #include "mm-plugin-common.h" #include "mm-broadband-modem-longcheer.h" #define MM_TYPE_PLUGIN_LONGCHEER mm_plugin_longcheer_get_type () MM_DEFINE_PLUGIN (LONGCHEER, longcheer, Longcheer) /*****************************************************************************/ /* Custom init */ typedef struct { MMPortSerialAt *port; guint retries; } LongcheerCustomInitContext; static void longcheer_custom_init_context_free (LongcheerCustomInitContext *ctx) { g_object_unref (ctx->port); g_slice_free (LongcheerCustomInitContext, ctx); } static gboolean longcheer_custom_init_finish (MMPortProbe *probe, GAsyncResult *result, GError **error) { return g_task_propagate_boolean (G_TASK (result), error); } static void longcheer_custom_init_step (GTask *task); static void gmr_ready (MMPortSerialAt *port, GAsyncResult *res, GTask *task) { MMPortProbe *probe; const gchar *p; g_autofree gchar *response = NULL; probe = g_task_get_source_object (task); response = mm_port_serial_at_command_finish (port, res, NULL); if (!response) { mm_obj_dbg (probe, "retrying custom init step..."); longcheer_custom_init_step (task); return; } /* Note the lack of a ':' on the GMR; the X200 doesn't send one */ p = mm_strip_tag (response, "AT+GMR"); if (p && *p == 'L') { /* X200 modems have a GMR firmware revision that starts with 'L', and * as far as I can tell X060s devices have a revision starting with 'C'. * So use that to determine if the device is an X200, which this plugin * does not support since it uses a different chipset even though the * X060s and the X200 have the exact same USB VID and PID. */ g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "X200 cannot be supported with the Longcheer plugin"); } else { mm_obj_dbg (probe, "device is not a X200"); g_task_return_boolean (task, TRUE); } g_object_unref (task); } static void longcheer_custom_init_step (GTask *task) { MMPortProbe *probe; LongcheerCustomInitContext *ctx; GCancellable *cancellable; probe = g_task_get_source_object (task); ctx = g_task_get_task_data (task); cancellable = g_task_get_cancellable (task); /* If cancelled, end */ if (g_cancellable_is_cancelled (cancellable)) { mm_obj_dbg (probe, "no need to keep on running custom init"); g_task_return_boolean (task, TRUE); g_object_unref (task); return; } if (ctx->retries == 0) { /* In this case, we need the AT command result to decide whether we can * support this modem or not, so really fail if we didn't get it. */ g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't get device revision information"); g_object_unref (task); return; } ctx->retries--; mm_port_serial_at_command ( ctx->port, "AT+GMR", 3, FALSE, /* raw */ FALSE, /* allow_cached */ cancellable, (GAsyncReadyCallback)gmr_ready, task); } static void longcheer_custom_init (MMPortProbe *probe, MMPortSerialAt *port, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { MMDevice *device; LongcheerCustomInitContext *ctx; GTask *task; ctx = g_slice_new (LongcheerCustomInitContext); ctx->port = g_object_ref (port); ctx->retries = 3; task = g_task_new (probe, cancellable, callback, user_data); /* Clears the check-cancellable flag of the task as we expect the task to * return TRUE upon cancellation. */ g_task_set_check_cancellable (task, FALSE); g_task_set_task_data (task, ctx, (GDestroyNotify)longcheer_custom_init_context_free); /* TCT/Alcatel in their infinite wisdom assigned the same USB VID/PID to * the x060s (Longcheer firmware) and the x200 (something else) and thus * we can't tell them apart via udev rules. Worse, they both report the * same +GMM and +GMI, so we're left with just +GMR which is a sketchy way * to tell modems apart. We can't really use Longcheer-specific commands * like AT+MODODR or AT+PSRAT because we're not sure if they work when the * SIM PIN has not been entered yet; many modems have a limited command * parser before the SIM is unlocked. */ device = mm_port_probe_peek_device (probe); if (mm_device_get_vendor (device) != 0x1bbb || mm_device_get_product (device) != 0x0000) { /* If not exactly this vendor/product, just skip */ g_task_return_boolean (task, TRUE); g_object_unref (task); return; } longcheer_custom_init_step (task); } /*****************************************************************************/ static MMBaseModem * create_modem (MMPlugin *self, const gchar *uid, const gchar *physdev, const gchar **drivers, guint16 vendor, guint16 product, guint16 subsystem_vendor, GList *probes, GError **error) { return MM_BASE_MODEM (mm_broadband_modem_longcheer_new (uid, physdev, drivers, mm_plugin_get_name (self), vendor, product)); } /*****************************************************************************/ MM_PLUGIN_NAMED_CREATOR_SCOPE MMPlugin * mm_plugin_create_longcheer (void) { static const gchar *subsystems[] = { "tty", NULL }; /* Vendors: Longcheer and TAMobile */ static const guint16 vendor_ids[] = { 0x1c9e, 0x1bbb, 0 }; /* Some TAMobile devices are different chipsets and should be handled * by other plugins, so only handle LONGCHEER tagged devices here. */ static const gchar *udev_tags[] = { "ID_MM_LONGCHEER_TAGGED", NULL }; static const MMAsyncMethod custom_init = { .async = G_CALLBACK (longcheer_custom_init), .finish = G_CALLBACK (longcheer_custom_init_finish), }; return MM_PLUGIN ( g_object_new (MM_TYPE_PLUGIN_LONGCHEER, MM_PLUGIN_NAME, MM_MODULE_NAME, MM_PLUGIN_ALLOWED_SUBSYSTEMS, subsystems, MM_PLUGIN_ALLOWED_VENDOR_IDS, vendor_ids, MM_PLUGIN_ALLOWED_AT, TRUE, MM_PLUGIN_ALLOWED_UDEV_TAGS, udev_tags, MM_PLUGIN_CUSTOM_INIT, &custom_init, NULL)); } static void mm_plugin_longcheer_init (MMPluginLongcheer *self) { } static void mm_plugin_longcheer_class_init (MMPluginLongcheerClass *klass) { MMPluginClass *plugin_class = MM_PLUGIN_CLASS (klass); plugin_class->create_modem = create_modem; } ModemManager-1.23.4-dev/src/plugins/mbm/000077500000000000000000000000001456466623000177505ustar00rootroot00000000000000ModemManager-1.23.4-dev/src/plugins/mbm/77-mm-ericsson-mbm.rules000066400000000000000000000176241456466623000242760ustar00rootroot00000000000000# do not edit this file, it will be overwritten on update ACTION!="add|change|move|bind", GOTO="mm_mbm_end" SUBSYSTEMS=="usb", ATTRS{idVendor}=="0bdb", GOTO="mm_mbm_ericsson_vendorcheck" SUBSYSTEMS=="usb", ATTRS{idVendor}=="0fce", GOTO="mm_mbm_sony_vendorcheck" SUBSYSTEMS=="usb", ATTRS{idVendor}=="413c", GOTO="mm_mbm_dell_vendorcheck" SUBSYSTEMS=="usb", ATTRS{idVendor}=="03f0", GOTO="mm_mbm_hp_vendorcheck" SUBSYSTEMS=="usb", ATTRS{idVendor}=="0930", GOTO="mm_mbm_toshiba_vendorcheck" GOTO="mm_mbm_end" LABEL="mm_mbm_ericsson_vendorcheck" SUBSYSTEMS=="usb", ATTRS{bInterfaceNumber}=="?*", ENV{.MM_USBIFNUM}="$attr{bInterfaceNumber}" # Ericsson F3507g ATTRS{idVendor}=="0bdb", ATTRS{idProduct}=="1900", ENV{.MM_USBIFNUM}=="09", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_GPS}="1" ATTRS{idVendor}=="0bdb", ATTRS{idProduct}=="1900", ENV{ID_MM_ERICSSON_MBM}="1" ATTRS{idVendor}=="0bdb", ATTRS{idProduct}=="1902", ENV{.MM_USBIFNUM}=="09", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_GPS}="1" ATTRS{idVendor}=="0bdb", ATTRS{idProduct}=="1902", ENV{ID_MM_ERICSSON_MBM}="1" # Ericsson F3607gw ATTRS{idVendor}=="0bdb", ATTRS{idProduct}=="1904", ENV{.MM_USBIFNUM}=="09", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_GPS}="1" ATTRS{idVendor}=="0bdb", ATTRS{idProduct}=="1904", ENV{ID_MM_ERICSSON_MBM}="1" ATTRS{idVendor}=="0bdb", ATTRS{idProduct}=="1905", ENV{.MM_USBIFNUM}=="09", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_GPS}="1" ATTRS{idVendor}=="0bdb", ATTRS{idProduct}=="1905", ENV{ID_MM_ERICSSON_MBM}="1" ATTRS{idVendor}=="0bdb", ATTRS{idProduct}=="1906", ENV{.MM_USBIFNUM}=="09", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_GPS}="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 F3307 R2 ATTRS{idVendor}=="0bdb", ATTRS{idProduct}=="1914", ENV{ID_MM_ERICSSON_MBM}="1" # Ericsson C3607w ATTRS{idVendor}=="0bdb", ATTRS{idProduct}=="1049", ENV{ID_MM_ERICSSON_MBM}="1" # Ericsson C3607w v2 ATTRS{idVendor}=="0bdb", ATTRS{idProduct}=="190b", ENV{ID_MM_ERICSSON_MBM}="1" # Ericsson F5521gw ATTRS{idVendor}=="0bdb", ATTRS{idProduct}=="190d", ENV{.MM_USBIFNUM}=="09", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_GPS}="1" ATTRS{idVendor}=="0bdb", ATTRS{idProduct}=="190d", ENV{ID_MM_ERICSSON_MBM}="1" ATTRS{idVendor}=="0bdb", ATTRS{idProduct}=="1911", ENV{.MM_USBIFNUM}=="09", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_GPS}="1" ATTRS{idVendor}=="0bdb", ATTRS{idProduct}=="1911", ENV{ID_MM_ERICSSON_MBM}="1" # Ericsson H5321gw ATTRS{idVendor}=="0bdb", ATTRS{idProduct}=="1919", ENV{ID_MM_ERICSSON_MBM}="1" # Ericsson H5321w ATTRS{idVendor}=="0bdb", ATTRS{idProduct}=="191d", ENV{ID_MM_ERICSSON_MBM}="1" # Ericsson F5321gw ATTRS{idVendor}=="0bdb", ATTRS{idProduct}=="1917", ENV{ID_MM_ERICSSON_MBM}="1" # Ericsson F5321w ATTRS{idVendor}=="0bdb", ATTRS{idProduct}=="191b", ENV{ID_MM_ERICSSON_MBM}="1" # Ericsson C5621gw ATTRS{idVendor}=="0bdb", ATTRS{idProduct}=="191f", ENV{ID_MM_ERICSSON_MBM}="1" # Ericsson C5621w ATTRS{idVendor}=="0bdb", ATTRS{idProduct}=="1921", ENV{ID_MM_ERICSSON_MBM}="1" # Ericsson H5321gw ATTRS{idVendor}=="0bdb", ATTRS{idProduct}=="1926", ENV{.MM_USBIFNUM}=="09", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_GPS}="1" ATTRS{idVendor}=="0bdb", ATTRS{idProduct}=="1926", ENV{ID_MM_ERICSSON_MBM}="1" ATTRS{idVendor}=="0bdb", ATTRS{idProduct}=="1927", ENV{.MM_USBIFNUM}=="09", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_GPS}="1" ATTRS{idVendor}=="0bdb", ATTRS{idProduct}=="1927", ENV{ID_MM_ERICSSON_MBM}="1" # Ericsson C3304w ATTRS{idVendor}=="0bdb", ATTRS{idProduct}=="1928", ENV{ID_MM_ERICSSON_MBM}="1" # Ericsson C5621 TFF ATTRS{idVendor}=="0bdb", ATTRS{idProduct}=="1936", ENV{ID_MM_ERICSSON_MBM}="1" # Lenovo N5321gw ATTRS{idVendor}=="0bdb", ATTRS{idProduct}=="193e", ENV{ID_MM_ERICSSON_MBM}="1" GOTO="mm_mbm_end" LABEL="mm_mbm_sony_vendorcheck" # Sony-Ericsson MD300 ATTRS{idVendor}=="0fce", ATTRS{idProduct}=="d0cf", ENV{ID_MM_ERICSSON_MBM}="1" # Sony-Ericsson MD400 ATTRS{idVendor}=="0fce", ATTRS{idProduct}=="d0e1", ENV{ID_MM_ERICSSON_MBM}="1" # Sony-Ericsson MD400G ATTRS{idVendor}=="0fce", ATTRS{idProduct}=="d103", ENV{ID_MM_ERICSSON_MBM}="1" GOTO="mm_mbm_end" LABEL="mm_mbm_dell_vendorcheck" SUBSYSTEMS=="usb", ATTRS{bInterfaceNumber}=="?*", ENV{.MM_USBIFNUM}="$attr{bInterfaceNumber}" # Dell 5560 ATTRS{idVendor}=="413c", ATTRS{idProduct}=="818e", ENV{ID_MM_ERICSSON_MBM}="1" ATTRS{idVendor}=="413c", ATTRS{idProduct}=="818e", ENV{.MM_USBIFNUM}=="09", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_GPS}="1" # Dell 5550 ATTRS{idVendor}=="413c", ATTRS{idProduct}=="818d", ENV{ID_MM_ERICSSON_MBM}="1" # Dell 5530 HSDPA ATTRS{idVendor}=="413c", ATTRS{idProduct}=="8147", ENV{.MM_USBIFNUM}=="09", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_GPS}="1" ATTRS{idVendor}=="413c", ATTRS{idProduct}=="8147", ENV{ID_MM_ERICSSON_MBM}="1" # Dell F3607gw ATTRS{idVendor}=="413c", ATTRS{idProduct}=="8183", ENV{.MM_USBIFNUM}=="09", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_GPS}="1" ATTRS{idVendor}=="413c", ATTRS{idProduct}=="8183", ENV{ID_MM_ERICSSON_MBM}="1" ATTRS{idVendor}=="413c", ATTRS{idProduct}=="8184", ENV{.MM_USBIFNUM}=="09", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_GPS}="1" ATTRS{idVendor}=="413c", ATTRS{idProduct}=="8184", ENV{ID_MM_ERICSSON_MBM}="1" # Dell F3307 ATTRS{idVendor}=="413c", ATTRS{idProduct}=="818b", ENV{.MM_USBIFNUM}=="09", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_GPS}="1" ATTRS{idVendor}=="413c", ATTRS{idProduct}=="818b", ENV{ID_MM_ERICSSON_MBM}="1" ATTRS{idVendor}=="413c", ATTRS{idProduct}=="818c", ENV{.MM_USBIFNUM}=="09", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_GPS}="1" ATTRS{idVendor}=="413c", ATTRS{idProduct}=="818c", ENV{ID_MM_ERICSSON_MBM}="1" GOTO="mm_mbm_end" LABEL="mm_mbm_hp_vendorcheck" SUBSYSTEMS=="usb", ATTRS{bInterfaceNumber}=="?*", ENV{.MM_USBIFNUM}="$attr{bInterfaceNumber}" # HP hs2330 Mobile Broadband Module ATTRS{idVendor}=="03f0", ATTRS{idProduct}=="271d", ENV{ID_MM_ERICSSON_MBM}="1" # HP hs2320 Mobile Broadband Module ATTRS{idVendor}=="03f0", ATTRS{idProduct}=="261d", ENV{ID_MM_ERICSSON_MBM}="1" # HP hs2340 Mobile Broadband Module ATTRS{idVendor}=="03f0", ATTRS{idProduct}=="3a1d", ENV{ID_MM_ERICSSON_MBM}="1" # HP hs2350 Mobile Broadband Module ATTRS{idVendor}=="03f0", ATTRS{idProduct}=="3d1d", ENV{.MM_USBIFNUM}=="09", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_GPS}="1" ATTRS{idVendor}=="03f0", ATTRS{idProduct}=="3d1d", ENV{ID_MM_ERICSSON_MBM}="1" # HP lc2000 Mobile Broadband Module ATTRS{idVendor}=="03f0", ATTRS{idProduct}=="301d", ENV{ID_MM_ERICSSON_MBM}="1" # HP lc2010 Mobile Broadband Module ATTRS{idVendor}=="03f0", ATTRS{idProduct}=="2f1d", ENV{ID_MM_ERICSSON_MBM}="1" GOTO="mm_mbm_end" LABEL="mm_mbm_toshiba_vendorcheck" SUBSYSTEMS=="usb", ATTRS{bInterfaceNumber}=="?*", ENV{.MM_USBIFNUM}="$attr{bInterfaceNumber}" # Toshiba ATTRS{idVendor}=="0930", ATTRS{idProduct}=="130b", ENV{.MM_USBIFNUM}=="09", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_GPS}="1" ATTRS{idVendor}=="0930", ATTRS{idProduct}=="130b", ENV{ID_MM_ERICSSON_MBM}="1" # Toshiba F3607gw ATTRS{idVendor}=="0930", ATTRS{idProduct}=="130c", ENV{.MM_USBIFNUM}=="09", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_GPS}="1" ATTRS{idVendor}=="0930", ATTRS{idProduct}=="130c", ENV{ID_MM_ERICSSON_MBM}="1" ATTRS{idVendor}=="0930", ATTRS{idProduct}=="1311", ENV{.MM_USBIFNUM}=="09", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_GPS}="1" ATTRS{idVendor}=="0930", ATTRS{idProduct}=="1311", ENV{ID_MM_ERICSSON_MBM}="1" # Toshiba F3307 ATTRS{idVendor}=="0930", ATTRS{idProduct}=="1315", ENV{ID_MM_ERICSSON_MBM}="1" ATTRS{idVendor}=="0930", ATTRS{idProduct}=="1316", ENV{ID_MM_ERICSSON_MBM}="1" ATTRS{idVendor}=="0930", ATTRS{idProduct}=="1317", ENV{ID_MM_ERICSSON_MBM}="1" # Toshiba F5521gw ATTRS{idVendor}=="0930", ATTRS{idProduct}=="1313", ENV{ID_MM_ERICSSON_MBM}="1" ATTRS{idVendor}=="0930", ATTRS{idProduct}=="1314", ENV{ID_MM_ERICSSON_MBM}="1" # Toshiba H5321gw ATTRS{idVendor}=="0930", ATTRS{idProduct}=="1319", ENV{ID_MM_ERICSSON_MBM}="1" GOTO="mm_mbm_end" LABEL="mm_mbm_end" ModemManager-1.23.4-dev/src/plugins/mbm/mm-broadband-bearer-mbm.c000066400000000000000000000741131456466623000244540ustar00rootroot00000000000000/* -*- 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 - 2010 Ericsson AB * Copyright (C) 2009 - 2012 Red Hat, Inc. * Copyright (C) 2012 Lanedo GmbH * Copyright (C) 2017 Aleksander Morgado * * Author: Per Hallsmark * Bjorn Runaker * Torgny Johansson * Jonas Sjöquist * Dan Williams * Aleksander Morgado */ #include #include #include #include #include #include #include #include #define _LIBMM_INSIDE_MM #include #include "mm-base-modem-at.h" #include "mm-broadband-bearer-mbm.h" #include "mm-log-object.h" #include "mm-modem-helpers.h" #include "mm-modem-helpers-mbm.h" #include "mm-daemon-enums-types.h" G_DEFINE_TYPE (MMBroadbandBearerMbm, mm_broadband_bearer_mbm, MM_TYPE_BROADBAND_BEARER) struct _MMBroadbandBearerMbmPrivate { GTask *connect_pending; GTask *disconnect_pending; }; /*****************************************************************************/ /* 3GPP Dialing (sub-step of the 3GPP Connection sequence) */ typedef struct { MMBaseModem *modem; MMPortSerialAt *primary; guint cid; MMPort *data; guint poll_count; guint poll_id; GError *saved_error; } Dial3gppContext; static void dial_3gpp_context_free (Dial3gppContext *ctx) { g_assert (!ctx->poll_id); g_assert (!ctx->saved_error); g_clear_object (&ctx->data); g_clear_object (&ctx->primary); g_clear_object (&ctx->modem); g_slice_free (Dial3gppContext, ctx); } static MMPort * dial_3gpp_finish (MMBroadbandBearer *self, GAsyncResult *res, GError **error) { return MM_PORT (g_task_propagate_pointer (G_TASK (res), error)); } static void connect_reset_ready (MMBroadbandBearer *self, GAsyncResult *res, GTask *task) { Dial3gppContext *ctx; ctx = g_task_get_task_data (task); MM_BROADBAND_BEARER_GET_CLASS (self)->disconnect_3gpp_finish (self, res, NULL); /* When reset is requested, it was either cancelled or an error was stored */ if (!g_task_return_error_if_cancelled (task)) { g_assert (ctx->saved_error); g_task_return_error (task, ctx->saved_error); ctx->saved_error = NULL; } g_object_unref (task); } static void connect_reset (GTask *task) { MMBroadbandBearerMbm *self; Dial3gppContext *ctx; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); MM_BROADBAND_BEARER_GET_CLASS (self)->disconnect_3gpp ( MM_BROADBAND_BEARER (self), MM_BROADBAND_MODEM (ctx->modem), ctx->primary, NULL, ctx->data, ctx->cid, (GAsyncReadyCallback) connect_reset_ready, task); } static void process_pending_connect_attempt (MMBroadbandBearerMbm *self, MMBearerConnectionStatus status) { GTask *task; Dial3gppContext *ctx; /* Recover connection task */ task = self->priv->connect_pending; self->priv->connect_pending = NULL; g_assert (task != NULL); ctx = g_task_get_task_data (task); if (ctx->poll_id) { g_source_remove (ctx->poll_id); ctx->poll_id = 0; } /* Received 'CONNECTED' during a connection attempt? */ if (status == MM_BEARER_CONNECTION_STATUS_CONNECTED) { /* If we wanted to get cancelled before, do it now. */ if (g_cancellable_is_cancelled (g_task_get_cancellable (task))) { connect_reset (task); return; } g_task_return_pointer (task, g_object_ref (ctx->data), g_object_unref); g_object_unref (task); return; } /* If we wanted to get cancelled before and now we couldn't connect, * use the cancelled error and return */ if (g_task_return_error_if_cancelled (task)) { g_object_unref (task); return; } /* Otherwise, received 'DISCONNECTED' during a connection attempt? */ g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Call setup failed"); g_object_unref (task); } static gboolean connect_poll_cb (MMBroadbandBearerMbm *self); static void connect_poll_ready (MMBaseModem *modem, GAsyncResult *res, MMBroadbandBearerMbm *self) { GTask *task; Dial3gppContext *ctx; GError *error = NULL; const gchar *response; guint state; task = g_steal_pointer (&self->priv->connect_pending); if (!task) { mm_obj_dbg (self, "connection context was finished already by an unsolicited message"); /* Run _finish() to finalize the async call, even if we don't care * the result */ mm_base_modem_at_command_full_finish (modem, res, NULL); return; } ctx = g_task_get_task_data (task); response = mm_base_modem_at_command_full_finish (modem, res, &error); if (!response) { ctx->saved_error = error; connect_reset (task); return; } if (sscanf (response, "*ENAP: %d", &state) == 1 && state == 1) { /* Success! Connected... */ g_task_return_pointer (task, g_object_ref (ctx->data), g_object_unref); g_object_unref (task); return; } /* Restore pending task and check again in one second */ self->priv->connect_pending = task; g_assert (ctx->poll_id == 0); ctx->poll_id = g_timeout_add_seconds (1, (GSourceFunc) connect_poll_cb, self); } static gboolean connect_poll_cb (MMBroadbandBearerMbm *self) { GTask *task; Dial3gppContext *ctx; task = g_steal_pointer (&self->priv->connect_pending); g_assert (task); ctx = g_task_get_task_data (task); ctx->poll_id = 0; /* Complete if we were cancelled */ if (g_cancellable_is_cancelled (g_task_get_cancellable (task))) { connect_reset (task); return G_SOURCE_REMOVE; } /* Too many retries... */ if (ctx->poll_count > MM_BASE_BEARER_DEFAULT_CONNECTION_TIMEOUT) { g_assert (!ctx->saved_error); ctx->saved_error = g_error_new (MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_NETWORK_TIMEOUT, "Connection attempt timed out"); connect_reset (task); return G_SOURCE_REMOVE; } /* Restore pending task and poll */ self->priv->connect_pending = task; ctx->poll_count++; mm_base_modem_at_command_full (ctx->modem, ctx->primary, "AT*ENAP?", 3, FALSE, FALSE, /* raw */ g_task_get_cancellable (task), (GAsyncReadyCallback)connect_poll_ready, self); return G_SOURCE_REMOVE; } static void activate_ready (MMBaseModem *modem, GAsyncResult *res, MMBroadbandBearerMbm *self) { GTask *task; Dial3gppContext *ctx; GError *error = NULL; /* Try to recover the connection context. If none found, it means the * context was already completed and we have nothing else to do. */ task = g_steal_pointer (&self->priv->connect_pending); if (!task) { mm_obj_dbg (self, "connection context was finished already by an unsolicited message"); /* Run _finish() to finalize the async call, even if we don't care * the result */ mm_base_modem_at_command_full_finish (modem, res, NULL); goto out; } /* From now on, if we get cancelled, we'll need to run the connection * reset ourselves just in case */ if (!mm_base_modem_at_command_full_finish (modem, res, &error)) { g_task_return_error (task, error); g_object_unref (task); goto out; } ctx = g_task_get_task_data (task); /* No unsolicited E2NAP status yet; wait for it and periodically poll * to handle very old F3507g/MD300 firmware that may not send E2NAP. */ self->priv->connect_pending = task; ctx->poll_id = g_timeout_add_seconds (1, (GSourceFunc)connect_poll_cb, self); out: /* Balance refcount with the extra ref we passed to command_full() */ g_object_unref (self); } static void activate (GTask *task) { MMBroadbandBearerMbm *self; Dial3gppContext *ctx; gchar *command; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); /* The unsolicited response to ENAP may come before the OK does. * We will keep the connection context in the bearer private data so * that it is accessible from the unsolicited message handler. */ g_assert (self->priv->connect_pending == NULL); self->priv->connect_pending = task; /* Activate the PDP context and start the data session */ command = g_strdup_printf ("AT*ENAP=1,%d", ctx->cid); mm_base_modem_at_command_full (ctx->modem, ctx->primary, command, 10, FALSE, FALSE, /* raw */ g_task_get_cancellable (task), (GAsyncReadyCallback)activate_ready, g_object_ref (self)); /* we pass the bearer object! */ g_free (command); } static void authenticate_ready (MMBaseModem *modem, GAsyncResult *res, GTask *task) { GError *error = NULL; if (!mm_base_modem_at_command_full_finish (modem, res, &error)) { g_task_return_error (task, error); g_object_unref (task); return; } activate (task); } static void authenticate (GTask *task) { MMBroadbandBearerMbm *self; Dial3gppContext *ctx; const gchar *user; const gchar *password; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); user = mm_bearer_properties_get_user (mm_base_bearer_peek_config (MM_BASE_BEARER (self))); password = mm_bearer_properties_get_password (mm_base_bearer_peek_config (MM_BASE_BEARER (self))); /* Both user and password are required; otherwise firmware returns an error */ if (user || password) { g_autofree gchar *command = NULL; g_autofree gchar *user_enc = NULL; g_autofree gchar *password_enc = NULL; GError *error = NULL; user_enc = mm_modem_charset_str_from_utf8 (user, mm_broadband_modem_get_current_charset (MM_BROADBAND_MODEM (ctx->modem)), FALSE, &error); if (!user_enc) { g_prefix_error (&error, "Couldn't convert user to current charset: "); g_task_return_error (task, error); g_object_unref (task); return; } password_enc = mm_modem_charset_str_from_utf8 (password, mm_broadband_modem_get_current_charset (MM_BROADBAND_MODEM (ctx->modem)), FALSE, &error); if (!password_enc) { g_prefix_error (&error, "Couldn't convert password to current charset: "); g_task_return_error (task, error); g_object_unref (task); return; } command = g_strdup_printf ("AT*EIAAUW=%d,1,\"%s\",\"%s\"", ctx->cid, user_enc, password_enc); mm_base_modem_at_command_full (ctx->modem, ctx->primary, command, 3, FALSE, FALSE, /* raw */ g_task_get_cancellable (task), (GAsyncReadyCallback) authenticate_ready, task); return; } mm_obj_dbg (self, "authentication not needed"); activate (task); } static void dial_3gpp (MMBroadbandBearer *_self, MMBaseModem *modem, MMPortSerialAt *primary, guint cid, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { MMBroadbandBearerMbm *self = MM_BROADBAND_BEARER_MBM (_self); GTask *task; Dial3gppContext *ctx; g_assert (primary != NULL); task = g_task_new (self, cancellable, callback, user_data); ctx = g_slice_new0 (Dial3gppContext); ctx->modem = g_object_ref (modem); ctx->primary = g_object_ref (primary); ctx->cid = cid; g_task_set_task_data (task, ctx, (GDestroyNotify)dial_3gpp_context_free); /* We need a net data port */ ctx->data = mm_base_modem_get_best_data_port (modem, MM_PORT_TYPE_NET); if (!ctx->data) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_NOT_FOUND, "No valid data port found to launch connection"); g_object_unref (task); return; } authenticate (task); } /*****************************************************************************/ /* 3GPP IP config retrieval (sub-step of the 3GPP Connection sequence) */ typedef struct { MMBaseModem *modem; MMPortSerialAt *primary; MMBearerIpFamily family; } GetIpConfig3gppContext; static void get_ip_config_context_free (GetIpConfig3gppContext *ctx) { g_object_unref (ctx->primary); g_object_unref (ctx->modem); g_free (ctx); } static gboolean get_ip_config_3gpp_finish (MMBroadbandBearer *self, GAsyncResult *res, MMBearerIpConfig **ipv4_config, MMBearerIpConfig **ipv6_config, GError **error) { MMBearerConnectResult *configs; MMBearerIpConfig *ipv4, *ipv6; configs = g_task_propagate_pointer (G_TASK (res), error); if (!configs) return FALSE; ipv4 = mm_bearer_connect_result_peek_ipv4_config (configs); ipv6 = mm_bearer_connect_result_peek_ipv6_config (configs); g_assert (ipv4 || ipv6); if (ipv4_config && ipv4) *ipv4_config = g_object_ref (ipv4); if (ipv6_config && ipv6) *ipv6_config = g_object_ref (ipv6); mm_bearer_connect_result_unref (configs); return TRUE; } static void ip_config_ready (MMBaseModem *modem, GAsyncResult *res, GTask *task) { GetIpConfig3gppContext *ctx; MMBearerIpConfig *ipv4_config = NULL; MMBearerIpConfig *ipv6_config = NULL; const gchar *response; GError *error = NULL; MMBearerConnectResult *connect_result; ctx = g_task_get_task_data (task); response = mm_base_modem_at_command_full_finish (modem, res, &error); if (error) { g_error_free (error); /* Fall back to DHCP configuration; early devices don't support *E2IPCFG */ if (ctx->family == MM_BEARER_IP_FAMILY_IPV4 || ctx->family == MM_BEARER_IP_FAMILY_IPV4V6) { ipv4_config = mm_bearer_ip_config_new (); mm_bearer_ip_config_set_method (ipv4_config, MM_BEARER_IP_METHOD_DHCP); } if (ctx->family == MM_BEARER_IP_FAMILY_IPV6 || ctx->family == MM_BEARER_IP_FAMILY_IPV4V6) { ipv6_config = mm_bearer_ip_config_new (); mm_bearer_ip_config_set_method (ipv6_config, MM_BEARER_IP_METHOD_DHCP); } } else { if (!mm_mbm_parse_e2ipcfg_response (response, &ipv4_config, &ipv6_config, &error)) { g_task_return_error (task, error); goto out; } if (!ipv4_config && !ipv6_config) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't get IP config: couldn't parse response '%s'", response); goto out; } } connect_result = mm_bearer_connect_result_new (MM_PORT (ctx->primary), ipv4_config, ipv6_config); g_task_return_pointer (task, connect_result, (GDestroyNotify)mm_bearer_connect_result_unref); out: g_object_unref (task); g_clear_object (&ipv4_config); g_clear_object (&ipv6_config); } static void get_ip_config_3gpp (MMBroadbandBearer *self, MMBroadbandModem *modem, MMPortSerialAt *primary, MMPortSerialAt *secondary, MMPort *data, guint cid, MMBearerIpFamily ip_family, GAsyncReadyCallback callback, gpointer user_data) { GetIpConfig3gppContext *ctx; GTask *task; ctx = g_new0 (GetIpConfig3gppContext, 1); ctx->modem = MM_BASE_MODEM (g_object_ref (modem)); ctx->primary = g_object_ref (primary); ctx->family = ip_family; task = g_task_new (self, NULL, callback, user_data); g_task_set_task_data (task, ctx, (GDestroyNotify)get_ip_config_context_free); mm_base_modem_at_command_full (MM_BASE_MODEM (modem), primary, "*E2IPCFG?", 3, FALSE, FALSE, /* raw */ NULL, /* cancellable */ (GAsyncReadyCallback)ip_config_ready, task); } /*****************************************************************************/ /* 3GPP disconnect */ typedef struct { MMBaseModem *modem; MMPortSerialAt *primary; guint poll_count; guint poll_id; } DisconnectContext; static void disconnect_context_free (DisconnectContext *ctx) { g_assert (!ctx->poll_id); g_clear_object (&ctx->primary); g_clear_object (&ctx->modem); g_free (ctx); } static gboolean disconnect_3gpp_finish (MMBroadbandBearer *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void process_pending_disconnect_attempt (MMBroadbandBearerMbm *self, MMBearerConnectionStatus status) { GTask *task; DisconnectContext *ctx; /* Recover disconnection task */ task = g_steal_pointer (&self->priv->disconnect_pending); ctx = g_task_get_task_data (task); if (ctx->poll_id) { g_source_remove (ctx->poll_id); ctx->poll_id = 0; } /* Received 'DISCONNECTED' during a disconnection attempt? */ if (status == MM_BEARER_CONNECTION_STATUS_DISCONNECTED) { mm_obj_dbg (self, "connection disconnect indicated by an unsolicited message"); g_task_return_boolean (task, TRUE); } else { /* Otherwise, report error */ g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Disconnection failed"); } g_object_unref (task); } static gboolean disconnect_poll_cb (MMBroadbandBearerMbm *self); static void disconnect_poll_ready (MMBaseModem *modem, GAsyncResult *res, MMBroadbandBearerMbm *self) { GTask *task; DisconnectContext *ctx; GError *error = NULL; const gchar *response; guint state; task = g_steal_pointer (&self->priv->disconnect_pending); if (!task) { mm_obj_dbg (self, "disconnection context was finished already by an unsolicited message"); /* Run _finish() to finalize the async call, even if we don't care * the result */ mm_base_modem_at_command_full_finish (modem, res, NULL); goto out; } response = mm_base_modem_at_command_full_finish (modem, res, &error); if (!response) { g_task_return_error (task, error); g_object_unref (task); goto out; } if (sscanf (response, "*ENAP: %d", &state) == 1 && state == 0) { /* Disconnected */ g_task_return_boolean (task, TRUE); g_object_unref (task); goto out; } /* Restore pending task and check in 1s */ self->priv->disconnect_pending = task; ctx = g_task_get_task_data (task); g_assert (ctx->poll_id == 0); ctx->poll_id = g_timeout_add_seconds (1, (GSourceFunc) disconnect_poll_cb, self); out: /* Balance refcount with the extra ref we passed to command_full() */ g_object_unref (self); } static gboolean disconnect_poll_cb (MMBroadbandBearerMbm *self) { GTask *task; DisconnectContext *ctx; task = self->priv->disconnect_pending; self->priv->disconnect_pending = NULL; g_assert (task); ctx = g_task_get_task_data (task); ctx->poll_id = 0; /* Too many retries... */ if (ctx->poll_count > MM_BASE_BEARER_DEFAULT_DISCONNECTION_TIMEOUT) { g_task_return_new_error (task, MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_NETWORK_TIMEOUT, "Disconnection attempt timed out"); g_object_unref (task); return G_SOURCE_REMOVE; } /* Restore pending task and poll */ self->priv->disconnect_pending = task; ctx->poll_count++; mm_base_modem_at_command_full (ctx->modem, ctx->primary, "AT*ENAP?", 3, FALSE, FALSE, /* raw */ NULL, /* cancellable */ (GAsyncReadyCallback) disconnect_poll_ready, g_object_ref (self)); /* we pass the bearer object! */ return G_SOURCE_REMOVE; } static void disconnect_enap_ready (MMBaseModem *modem, GAsyncResult *res, MMBroadbandBearerMbm *self) { DisconnectContext *ctx; GTask *task; GError *error = NULL; task = g_steal_pointer (&self->priv->disconnect_pending); /* Try to recover the disconnection context. If none found, it means the * context was already completed and we have nothing else to do. */ if (!task) { mm_base_modem_at_command_full_finish (modem, res, NULL); goto out; } ctx = g_task_get_task_data (task); /* Ignore errors for now */ mm_base_modem_at_command_full_finish (modem, res, &error); if (error) { mm_obj_dbg (self, "disconnection failed (not fatal): %s", error->message); g_error_free (error); } /* No unsolicited E2NAP status yet; wait for it and periodically poll * to handle very old F3507g/MD300 firmware that may not send E2NAP. */ self->priv->disconnect_pending = task; ctx->poll_id = g_timeout_add_seconds (1, (GSourceFunc)disconnect_poll_cb, self); out: /* Balance refcount with the extra ref we passed to command_full() */ g_object_unref (self); } static void disconnect_3gpp (MMBroadbandBearer *_self, MMBroadbandModem *modem, MMPortSerialAt *primary, MMPortSerialAt *secondary, MMPort *data, guint cid, GAsyncReadyCallback callback, gpointer user_data) { MMBroadbandBearerMbm *self = MM_BROADBAND_BEARER_MBM (_self); GTask *task; DisconnectContext *ctx; g_assert (primary != NULL); task = g_task_new (self, NULL, callback, user_data); ctx = g_new0 (DisconnectContext, 1); ctx->modem = MM_BASE_MODEM (g_object_ref (modem)); ctx->primary = g_object_ref (primary); g_task_set_task_data (task, ctx, (GDestroyNotify) disconnect_context_free); /* The unsolicited response to ENAP may come before the OK does. * We will keep the disconnection context in the bearer private data so * that it is accessible from the unsolicited message handler. */ g_assert (self->priv->disconnect_pending == NULL); self->priv->disconnect_pending = task; mm_base_modem_at_command_full (MM_BASE_MODEM (modem), primary, "*ENAP=0", 3, FALSE, FALSE, /* raw */ NULL, /* cancellable */ (GAsyncReadyCallback)disconnect_enap_ready, g_object_ref (self)); /* we pass the bearer object! */ } /*****************************************************************************/ static void report_connection_status (MMBaseBearer *_self, MMBearerConnectionStatus status, const GError *connection_error) { MMBroadbandBearerMbm *self = MM_BROADBAND_BEARER_MBM (_self); g_assert (status == MM_BEARER_CONNECTION_STATUS_CONNECTED || status == MM_BEARER_CONNECTION_STATUS_CONNECTION_FAILED || status == MM_BEARER_CONNECTION_STATUS_DISCONNECTED); /* Process pending connection attempt */ if (self->priv->connect_pending) { process_pending_connect_attempt (self, status); return; } /* Process pending disconnection attempt */ if (self->priv->disconnect_pending) { process_pending_disconnect_attempt (self, status); return; } mm_obj_dbg (self, "received spontaneous E2NAP (%s)", mm_bearer_connection_status_get_string (status)); /* Received a random 'DISCONNECTED'...*/ if (status == MM_BEARER_CONNECTION_STATUS_DISCONNECTED || status == MM_BEARER_CONNECTION_STATUS_CONNECTION_FAILED) { /* If no connection/disconnection attempt on-going, make sure we mark ourselves as * disconnected. Make sure we only pass 'DISCONNECTED' to the parent */ MM_BASE_BEARER_CLASS (mm_broadband_bearer_mbm_parent_class)->report_connection_status ( _self, MM_BEARER_CONNECTION_STATUS_DISCONNECTED, NULL); } } /*****************************************************************************/ MMBaseBearer * mm_broadband_bearer_mbm_new_finish (GAsyncResult *res, GError **error) { GObject *bearer; GObject *source; source = g_async_result_get_source_object (res); bearer = g_async_initable_new_finish (G_ASYNC_INITABLE (source), res, error); g_object_unref (source); if (!bearer) return NULL; /* Only export valid bearers */ mm_base_bearer_export (MM_BASE_BEARER (bearer)); return MM_BASE_BEARER (bearer); } void mm_broadband_bearer_mbm_new (MMBroadbandModemMbm *modem, MMBearerProperties *config, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { g_async_initable_new_async ( MM_TYPE_BROADBAND_BEARER_MBM, G_PRIORITY_DEFAULT, cancellable, callback, user_data, MM_BASE_BEARER_MODEM, modem, MM_BASE_BEARER_CONFIG, config, NULL); } static void mm_broadband_bearer_mbm_init (MMBroadbandBearerMbm *self) { /* Initialize private data */ self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, MM_TYPE_BROADBAND_BEARER_MBM, MMBroadbandBearerMbmPrivate); } static void mm_broadband_bearer_mbm_class_init (MMBroadbandBearerMbmClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); MMBaseBearerClass *base_bearer_class = MM_BASE_BEARER_CLASS (klass); MMBroadbandBearerClass *broadband_bearer_class = MM_BROADBAND_BEARER_CLASS (klass); g_type_class_add_private (object_class, sizeof (MMBroadbandBearerMbmPrivate)); base_bearer_class->report_connection_status = report_connection_status; base_bearer_class->load_connection_status = NULL; base_bearer_class->load_connection_status_finish = NULL; #if defined WITH_SUSPEND_RESUME base_bearer_class->reload_connection_status = NULL; base_bearer_class->reload_connection_status_finish = NULL; #endif broadband_bearer_class->dial_3gpp = dial_3gpp; broadband_bearer_class->dial_3gpp_finish = dial_3gpp_finish; broadband_bearer_class->get_ip_config_3gpp = get_ip_config_3gpp; broadband_bearer_class->get_ip_config_3gpp_finish = get_ip_config_3gpp_finish; broadband_bearer_class->disconnect_3gpp = disconnect_3gpp; broadband_bearer_class->disconnect_3gpp_finish = disconnect_3gpp_finish; } ModemManager-1.23.4-dev/src/plugins/mbm/mm-broadband-bearer-mbm.h000066400000000000000000000057421456466623000244630ustar00rootroot00000000000000/* -*- 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 - 2010 Ericsson AB * Copyright (C) 2009 - 2012 Red Hat, Inc. * Copyright (C) 2012 Lanedo GmbH * * Author: Per Hallsmark * Bjorn Runaker * Torgny Johansson * Jonas Sjöquist * Dan Williams * Aleksander Morgado */ #ifndef MM_BROADBAND_BEARER_MBM_H #define MM_BROADBAND_BEARER_MBM_H #include #include #define _LIBMM_INSIDE_MM #include #include "mm-broadband-bearer.h" #include "mm-broadband-modem-mbm.h" #define MM_TYPE_BROADBAND_BEARER_MBM (mm_broadband_bearer_mbm_get_type ()) #define MM_BROADBAND_BEARER_MBM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_BROADBAND_BEARER_MBM, MMBroadbandBearerMbm)) #define MM_BROADBAND_BEARER_MBM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_BROADBAND_BEARER_MBM, MMBroadbandBearerMbmClass)) #define MM_IS_BROADBAND_BEARER_MBM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_BROADBAND_BEARER_MBM)) #define MM_IS_BROADBAND_BEARER_MBM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_BROADBAND_BEARER_MBM)) #define MM_BROADBAND_BEARER_MBM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_BROADBAND_BEARER_MBM, MMBroadbandBearerMbmClass)) typedef struct _MMBroadbandBearerMbm MMBroadbandBearerMbm; typedef struct _MMBroadbandBearerMbmClass MMBroadbandBearerMbmClass; typedef struct _MMBroadbandBearerMbmPrivate MMBroadbandBearerMbmPrivate; struct _MMBroadbandBearerMbm { MMBroadbandBearer parent; MMBroadbandBearerMbmPrivate *priv; }; struct _MMBroadbandBearerMbmClass { MMBroadbandBearerClass parent; }; GType mm_broadband_bearer_mbm_get_type (void); /* Default 3GPP bearer creation implementation */ void mm_broadband_bearer_mbm_new (MMBroadbandModemMbm *modem, MMBearerProperties *config, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); MMBaseBearer *mm_broadband_bearer_mbm_new_finish (GAsyncResult *res, GError **error); #endif /* MM_BROADBAND_BEARER_MBM_H */ ModemManager-1.23.4-dev/src/plugins/mbm/mm-broadband-modem-mbm.c000066400000000000000000001507751456466623000243260ustar00rootroot00000000000000/* -*- 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 - 2010 Ericsson AB * Copyright (C) 2009 - 2012 Red Hat, Inc. * Copyright (C) 2012 Lanedo GmbH * * Author: Per Hallsmark * Bjorn Runaker * Torgny Johansson * Jonas Sjöquist * Dan Williams * Aleksander Morgado */ #include #include #include #include #include #include #include "ModemManager.h" #include "mm-log-object.h" #include "mm-bearer-list.h" #include "mm-errors-types.h" #include "mm-modem-helpers.h" #include "mm-modem-helpers-mbm.h" #include "mm-broadband-modem-mbm.h" #include "mm-broadband-bearer-mbm.h" #include "mm-sim-mbm.h" #include "mm-base-modem-at.h" #include "mm-iface-modem.h" #include "mm-iface-modem-3gpp.h" #include "mm-iface-modem-location.h" /* sets the interval in seconds on how often the card emits the NMEA sentences */ #define MBM_GPS_NMEA_INTERVAL "5" static void iface_modem_init (MMIfaceModem *iface); static void iface_modem_3gpp_init (MMIfaceModem3gpp *iface); static void iface_modem_location_init (MMIfaceModemLocation *iface); static MMIfaceModem *iface_modem_parent; static MMIfaceModem3gpp *iface_modem_3gpp_parent; static MMIfaceModemLocation *iface_modem_location_parent; G_DEFINE_TYPE_EXTENDED (MMBroadbandModemMbm, mm_broadband_modem_mbm, MM_TYPE_BROADBAND_MODEM, 0, G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM, iface_modem_init) G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_3GPP, iface_modem_3gpp_init) G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_LOCATION, iface_modem_location_init)) #define MBM_E2NAP_DISCONNECTED 0 #define MBM_E2NAP_CONNECTED 1 #define MBM_E2NAP_CONNECTING 2 struct _MMBroadbandModemMbmPrivate { gboolean have_emrdy; GRegex *e2nap_regex; GRegex *e2nap_ext_regex; GRegex *emrdy_regex; GRegex *pacsp_regex; GRegex *estksmenu_regex; GRegex *estksms_regex; GRegex *emwi_regex; GRegex *erinfo_regex; MMModemLocationSource enabled_sources; guint mbm_mode; }; /*****************************************************************************/ /* Create Bearer (Modem interface) */ static MMBaseBearer * modem_create_bearer_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { return g_task_propagate_pointer (G_TASK (res), error); } static void broadband_bearer_mbm_new_ready (GObject *source, GAsyncResult *res, GTask *task) { MMBaseBearer *bearer = NULL; GError *error = NULL; bearer = mm_broadband_bearer_mbm_new_finish (res, &error); if (!bearer) g_task_return_error (task, error); else g_task_return_pointer (task, bearer, g_object_unref); g_object_unref (task); } static void modem_create_bearer (MMIfaceModem *self, MMBearerProperties *properties, GAsyncReadyCallback callback, gpointer user_data) { mm_obj_dbg (self, "creating MBM bearer..."); mm_broadband_bearer_mbm_new (MM_BROADBAND_MODEM_MBM (self), properties, NULL, /* cancellable */ (GAsyncReadyCallback)broadband_bearer_mbm_new_ready, g_task_new (self, NULL, callback, user_data)); } /*****************************************************************************/ /* Create SIM (Modem interface) */ static MMBaseSim * create_sim_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { return mm_sim_mbm_new_finish (res, error); } static void create_sim (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data) { /* New MBM SIM */ mm_sim_mbm_new (MM_BASE_MODEM (self), NULL, /* cancellable */ callback, user_data); } /*****************************************************************************/ /* After SIM unlock (Modem interface) */ static gboolean modem_after_sim_unlock_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static gboolean after_sim_unlock_wait_cb (GTask *task) { g_task_return_boolean (task, TRUE); g_object_unref (task); return G_SOURCE_REMOVE; } static void modem_after_sim_unlock (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; task = g_task_new (self, NULL, callback, user_data); /* wait so sim pin is done */ g_timeout_add (500, (GSourceFunc)after_sim_unlock_wait_cb, task); } /*****************************************************************************/ /* Load supported modes (Modem interface) */ static GArray * load_supported_modes_finish (MMIfaceModem *_self, GAsyncResult *res, GError **error) { MMBroadbandModemMbm *self = MM_BROADBAND_MODEM_MBM (_self); const gchar *response; guint32 mask = 0; GArray *combinations; MMModemModeCombination mode; response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, error); if (!response) return FALSE; if (!mm_mbm_parse_cfun_test (response, self, &mask, error)) return FALSE; /* Build list of combinations */ combinations = g_array_sized_new (FALSE, FALSE, sizeof (MMModemModeCombination), 3); /* 2G only */ if (mask & (1 << MBM_NETWORK_MODE_2G)) { mode.allowed = MM_MODEM_MODE_2G; mode.preferred = MM_MODEM_MODE_NONE; g_array_append_val (combinations, mode); } /* 3G only */ if (mask & (1 << MBM_NETWORK_MODE_3G)) { mode.allowed = MM_MODEM_MODE_3G; mode.preferred = MM_MODEM_MODE_NONE; g_array_append_val (combinations, mode); } /* 2G and 3G */ if (mask & (1 << MBM_NETWORK_MODE_ANY)) { mode.allowed = (MM_MODEM_MODE_2G | MM_MODEM_MODE_3G); mode.preferred = MM_MODEM_MODE_NONE; g_array_append_val (combinations, mode); } if (combinations->len == 0) { g_set_error_literal (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't load any supported mode"); g_array_unref (combinations); return NULL; } return combinations; } static void load_supported_modes (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data) { mm_base_modem_at_command (MM_BASE_MODEM (self), "+CFUN=?", 3, FALSE, callback, user_data); } /*****************************************************************************/ /* Load initial allowed/preferred modes (Modem interface) */ static gboolean load_current_modes_finish (MMIfaceModem *_self, GAsyncResult *res, MMModemMode *allowed, MMModemMode *preferred, GError **error) { MMBroadbandModemMbm *self = MM_BROADBAND_MODEM_MBM (_self); const gchar *response; gint mbm_mode = -1; g_assert (allowed); g_assert (preferred); response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, error); if (!response || !mm_mbm_parse_cfun_query_current_modes (response, allowed, &mbm_mode, error)) return FALSE; /* No settings to set preferred */ *preferred = MM_MODEM_MODE_NONE; if (mbm_mode != -1) self->priv->mbm_mode = mbm_mode; return TRUE; } static void load_current_modes (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data) { mm_base_modem_at_command (MM_BASE_MODEM (self), "+CFUN?", 3, FALSE, callback, user_data); } /*****************************************************************************/ /* Set allowed modes (Modem interface) */ typedef struct { gint mbm_mode; } SetCurrentModesContext; static gboolean set_current_modes_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void allowed_mode_update_ready (MMBaseModem *self, GAsyncResult *res, GTask *task) { SetCurrentModesContext *ctx; GError *error = NULL; ctx = g_task_get_task_data (task); mm_base_modem_at_command_finish (self, res, &error); if (error) /* Let the error be critical. */ g_task_return_error (task, error); else { /* Cache current allowed mode */ MM_BROADBAND_MODEM_MBM (self)->priv->mbm_mode = ctx->mbm_mode; g_task_return_boolean (task, TRUE); } g_object_unref (task); } static void set_current_modes (MMIfaceModem *self, MMModemMode allowed, MMModemMode preferred, GAsyncReadyCallback callback, gpointer user_data) { SetCurrentModesContext *ctx; GTask *task; gchar *command; ctx = g_new (SetCurrentModesContext, 1); ctx->mbm_mode = -1; task = g_task_new (self, NULL, callback, user_data); g_task_set_task_data (task, ctx, g_free); if (allowed == MM_MODEM_MODE_2G) ctx->mbm_mode = MBM_NETWORK_MODE_2G; else if (allowed == MM_MODEM_MODE_3G) ctx->mbm_mode = MBM_NETWORK_MODE_3G; else if ((allowed == (MM_MODEM_MODE_2G | MM_MODEM_MODE_3G) || allowed == MM_MODEM_MODE_ANY) && preferred == MM_MODEM_MODE_NONE) ctx->mbm_mode = MBM_NETWORK_MODE_ANY; if (ctx->mbm_mode < 0) { gchar *allowed_str; gchar *preferred_str; allowed_str = mm_modem_mode_build_string_from_mask (allowed); preferred_str = mm_modem_mode_build_string_from_mask (preferred); g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Requested mode (allowed: '%s', preferred: '%s') not " "supported by the modem.", allowed_str, preferred_str); g_object_unref (task); g_free (allowed_str); g_free (preferred_str); return; } command = g_strdup_printf ("+CFUN=%d", ctx->mbm_mode); mm_base_modem_at_command ( MM_BASE_MODEM (self), command, 3, FALSE, (GAsyncReadyCallback)allowed_mode_update_ready, task); g_free (command); } /*****************************************************************************/ /* Initializing the modem (during first enabling) */ static gboolean enabling_modem_init_finish (MMBroadbandModem *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void enabling_init_sequence_ready (MMBaseModem *self, GAsyncResult *res, GTask *task) { /* Ignore errors */ mm_base_modem_at_sequence_full_finish (self, res, NULL, NULL); g_task_return_boolean (task, TRUE); g_object_unref (task); } static const MMBaseModemAtCommand enabling_modem_init_sequence[] = { /* Init command */ { "&F", 3, FALSE, NULL }, /* Ensure disconnected */ { "*ENAP=0", 3, FALSE, NULL }, { NULL } }; static void run_enabling_init_sequence (GTask *task) { MMBaseModem *self; self = g_task_get_source_object (task); mm_base_modem_at_sequence_full (self, mm_base_modem_peek_port_primary (self), enabling_modem_init_sequence, NULL, /* response_processor_context */ NULL, /* response_processor_context_free */ NULL, /* cancellable */ (GAsyncReadyCallback)enabling_init_sequence_ready, task); } static void emrdy_ready (MMBaseModem *self, GAsyncResult *res, GTask *task) { GError *error = NULL; /* EMRDY unsolicited response might have happened between the command * submission and the response. This was seen once: * * (ttyACM0): --> 'AT*EMRDY?' * (ttyACM0): <-- 'T*EMRD*EMRDY: 1Y?' * * So suppress the warning if the unsolicited handler handled the response * before we get here. */ if (!mm_base_modem_at_command_finish (self, res, &error)) { if (g_error_matches (error, MM_SERIAL_ERROR, MM_SERIAL_ERROR_RESPONSE_TIMEOUT)) mm_obj_warn (self, "timed out waiting for EMRDY response"); else MM_BROADBAND_MODEM_MBM (self)->priv->have_emrdy = TRUE; g_error_free (error); } run_enabling_init_sequence (task); } static void enabling_modem_init (MMBroadbandModem *_self, GAsyncReadyCallback callback, gpointer user_data) { MMBroadbandModemMbm *self = MM_BROADBAND_MODEM_MBM (_self); GTask *task; task = g_task_new (self, NULL, callback, user_data); /* Modem is ready?, no need to check EMRDY */ if (self->priv->have_emrdy) { run_enabling_init_sequence (task); return; } mm_base_modem_at_command (MM_BASE_MODEM (self), "*EMRDY?", 3, FALSE, (GAsyncReadyCallback)emrdy_ready, task); } /*****************************************************************************/ /* Modem power down (Modem interface) */ static gboolean modem_power_down_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { return !!mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, error); } static void modem_power_down (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data) { /* Use AT+CFUN=4 for power down. It will stop the RF (IMSI detach), and * keeps access to the SIM */ mm_base_modem_at_command (MM_BASE_MODEM (self), "+CFUN=4", 3, FALSE, callback, user_data); } /*****************************************************************************/ /* Powering up the modem (Modem interface) */ static gboolean modem_power_up_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { /* By default, errors in the power up command are ignored. */ mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, NULL); return TRUE; } static void modem_power_up (MMIfaceModem *_self, GAsyncReadyCallback callback, gpointer user_data) { MMBroadbandModemMbm *self = MM_BROADBAND_MODEM_MBM (_self); gchar *command; g_assert (self->priv->mbm_mode == MBM_NETWORK_MODE_ANY || self->priv->mbm_mode == MBM_NETWORK_MODE_2G || self->priv->mbm_mode == MBM_NETWORK_MODE_3G); command = g_strdup_printf ("+CFUN=%u", self->priv->mbm_mode); mm_base_modem_at_command (MM_BASE_MODEM (self), command, 5, FALSE, callback, user_data); g_free (command); } /*****************************************************************************/ /* Power state loading (Modem interface) */ static MMModemPowerState load_power_state_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { const gchar *response; MMModemPowerState state; response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, error); if (!response || !mm_mbm_parse_cfun_query_power_state (response, &state, error)) return MM_MODEM_POWER_STATE_UNKNOWN; return state; } static void load_power_state (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data) { mm_base_modem_at_command (MM_BASE_MODEM (self), "+CFUN?", 3, FALSE, callback, user_data); } /*****************************************************************************/ /* Reset (Modem interface) */ static gboolean reset_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { /* Ignore errors */ mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, NULL); return TRUE; } static void reset (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data) { mm_base_modem_at_command (MM_BASE_MODEM (self), "*E2RESET", 3, FALSE, callback, user_data); } /*****************************************************************************/ /* Factory reset (Modem interface) */ static gboolean factory_reset_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { /* Ignore errors */ mm_base_modem_at_sequence_finish (MM_BASE_MODEM (self), res, NULL, NULL); return TRUE; } static const MMBaseModemAtCommand factory_reset_sequence[] = { /* Init command */ { "&F +CMEE=0", 3, FALSE, NULL }, { "+COPS=0", 3, FALSE, NULL }, { "+CR=0", 3, FALSE, NULL }, { "+CRC=0", 3, FALSE, NULL }, { "+CREG=0", 3, FALSE, NULL }, { "+CMER=0", 3, FALSE, NULL }, { "*EPEE=0", 3, FALSE, NULL }, { "+CNMI=2, 0, 0, 0, 0", 3, FALSE, NULL }, { "+CGREG=0", 3, FALSE, NULL }, { "*EIAD=0", 3, FALSE, NULL }, { "+CGSMS=3", 3, FALSE, NULL }, { "+CSCA=\"\",129", 3, FALSE, NULL }, { NULL } }; static void factory_reset (MMIfaceModem *self, const gchar *code, GAsyncReadyCallback callback, gpointer user_data) { mm_obj_dbg (self, "ignoring user-provided factory reset code: '%s'", code); mm_base_modem_at_sequence (MM_BASE_MODEM (self), factory_reset_sequence, NULL, /* response_processor_context */ NULL, /* response_processor_context_free */ callback, user_data); } /*****************************************************************************/ /* Load unlock retries (Modem interface) */ static MMUnlockRetries * load_unlock_retries_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { MMUnlockRetries *unlock_retries; const gchar *response; gint matched; guint a, b, c ,d; response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, error); if (!response) return NULL; matched = sscanf (response, "*EPIN: %d, %d, %d, %d", &a, &b, &c, &d); if (matched != 4) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Could not parse PIN retries results: '%s'", response); return NULL; } if (a > 998) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Invalid PIN attempts left: '%u'", a); return NULL; } unlock_retries = mm_unlock_retries_new (); mm_unlock_retries_set (unlock_retries, MM_MODEM_LOCK_SIM_PIN, a); mm_unlock_retries_set (unlock_retries, MM_MODEM_LOCK_SIM_PUK, b); mm_unlock_retries_set (unlock_retries, MM_MODEM_LOCK_SIM_PIN2, c); mm_unlock_retries_set (unlock_retries, MM_MODEM_LOCK_SIM_PUK2, d); return unlock_retries; } static void load_unlock_retries (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data) { mm_base_modem_at_command (MM_BASE_MODEM (self), "*EPIN?", 10, FALSE, callback, user_data); } /*****************************************************************************/ /* Setup/Cleanup unsolicited events (3GPP interface) */ typedef struct { MMBearerConnectionStatus status; } BearerListReportStatusForeachContext; static void bearer_list_report_status_foreach (MMBaseBearer *bearer, BearerListReportStatusForeachContext *ctx) { mm_base_bearer_report_connection_status (bearer, ctx->status); } static void e2nap_received (MMPortSerialAt *port, GMatchInfo *info, MMBroadbandModemMbm *self) { MMBearerList *list = NULL; guint state; BearerListReportStatusForeachContext ctx; if (!mm_get_uint_from_match_info (info, 1, &state)) return; ctx.status = MM_BEARER_CONNECTION_STATUS_UNKNOWN; switch (state) { case MBM_E2NAP_DISCONNECTED: mm_obj_dbg (self, "disconnected"); ctx.status = MM_BEARER_CONNECTION_STATUS_DISCONNECTED; break; case MBM_E2NAP_CONNECTED: mm_obj_dbg (self, "connected"); ctx.status = MM_BEARER_CONNECTION_STATUS_CONNECTED; break; case MBM_E2NAP_CONNECTING: mm_obj_dbg (self, "connecting"); break; default: /* Should not happen */ mm_obj_dbg (self, "unhandled E2NAP state %d", state); } /* If unknown status, don't try to report anything */ if (ctx.status == MM_BEARER_CONNECTION_STATUS_UNKNOWN) return; /* If empty bearer list, nothing else to do */ g_object_get (self, MM_IFACE_MODEM_BEARER_LIST, &list, NULL); if (!list) return; mm_bearer_list_foreach (list, (MMBearerListForeachFunc)bearer_list_report_status_foreach, &ctx); g_object_unref (list); } static void erinfo_received (MMPortSerialAt *port, GMatchInfo *info, MMBroadbandModemMbm *self) { MMModemAccessTechnology act = MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN; guint mode; if (mm_get_uint_from_match_info (info, 2, &mode)) { switch (mode) { case 1: act = MM_MODEM_ACCESS_TECHNOLOGY_GPRS; break; case 2: act = MM_MODEM_ACCESS_TECHNOLOGY_EDGE; break; default: break; } } /* 3G modes take precedence */ if (mm_get_uint_from_match_info (info, 3, &mode)) { switch (mode) { case 1: act = MM_MODEM_ACCESS_TECHNOLOGY_UMTS; break; case 2: act = MM_MODEM_ACCESS_TECHNOLOGY_HSDPA; break; case 3: act = MM_MODEM_ACCESS_TECHNOLOGY_HSPA; break; default: break; } } mm_iface_modem_update_access_technologies (MM_IFACE_MODEM (self), act, MM_IFACE_MODEM_3GPP_ALL_ACCESS_TECHNOLOGIES_MASK); } static void set_unsolicited_events_handlers (MMBroadbandModemMbm *self, gboolean enable) { MMPortSerialAt *ports[2]; guint i; ports[0] = mm_base_modem_peek_port_primary (MM_BASE_MODEM (self)); ports[1] = mm_base_modem_peek_port_secondary (MM_BASE_MODEM (self)); /* Enable unsolicited events in given port */ for (i = 0; i < G_N_ELEMENTS (ports); i++) { if (!ports[i]) continue; /* Access technology related */ mm_port_serial_at_add_unsolicited_msg_handler ( ports[i], self->priv->erinfo_regex, enable ? (MMPortSerialAtUnsolicitedMsgFn)erinfo_received : NULL, enable ? self : NULL, NULL); /* Connection related */ mm_port_serial_at_add_unsolicited_msg_handler ( ports[i], self->priv->e2nap_regex, enable ? (MMPortSerialAtUnsolicitedMsgFn)e2nap_received : NULL, enable ? self : NULL, NULL); mm_port_serial_at_add_unsolicited_msg_handler ( ports[i], self->priv->e2nap_ext_regex, enable ? (MMPortSerialAtUnsolicitedMsgFn)e2nap_received : NULL, enable ? self : NULL, NULL); } } static gboolean modem_3gpp_setup_cleanup_unsolicited_events_finish (MMIfaceModem3gpp *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void parent_setup_unsolicited_events_ready (MMIfaceModem3gpp *self, GAsyncResult *res, GTask *task) { GError *error = NULL; if (!iface_modem_3gpp_parent->setup_unsolicited_events_finish (self, res, &error)) g_task_return_error (task, error); else { /* Our own setup now */ set_unsolicited_events_handlers (MM_BROADBAND_MODEM_MBM (self), TRUE); g_task_return_boolean (task, TRUE); } g_object_unref (task); } static void modem_3gpp_setup_unsolicited_events (MMIfaceModem3gpp *self, GAsyncReadyCallback callback, gpointer user_data) { /* Chain up parent's setup */ iface_modem_3gpp_parent->setup_unsolicited_events ( self, (GAsyncReadyCallback)parent_setup_unsolicited_events_ready, g_task_new (self, NULL, callback, user_data)); } static void parent_cleanup_unsolicited_events_ready (MMIfaceModem3gpp *self, GAsyncResult *res, GTask *task) { GError *error = NULL; if (!iface_modem_3gpp_parent->cleanup_unsolicited_events_finish (self, res, &error)) g_task_return_error (task, error); else g_task_return_boolean (task, TRUE); g_object_unref (task); } static void modem_3gpp_cleanup_unsolicited_events (MMIfaceModem3gpp *self, GAsyncReadyCallback callback, gpointer user_data) { /* Our own cleanup first */ set_unsolicited_events_handlers (MM_BROADBAND_MODEM_MBM (self), FALSE); /* And now chain up parent's cleanup */ iface_modem_3gpp_parent->cleanup_unsolicited_events ( self, (GAsyncReadyCallback)parent_cleanup_unsolicited_events_ready, g_task_new (self, NULL, callback, user_data)); } /*****************************************************************************/ /* Enabling unsolicited events (3GPP interface) */ static gboolean modem_3gpp_enable_unsolicited_events_finish (MMIfaceModem3gpp *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void own_enable_unsolicited_events_ready (MMBaseModem *self, GAsyncResult *res, GTask *task) { GError *error = NULL; mm_base_modem_at_sequence_full_finish (self, res, NULL, &error); if (error) g_task_return_error (task, error); else g_task_return_boolean (task, TRUE); g_object_unref (task); } static const MMBaseModemAtCommand unsolicited_enable_sequence[] = { { "*ERINFO=1", 5, FALSE, NULL }, { "*E2NAP=1", 5, FALSE, NULL }, { NULL } }; static void parent_enable_unsolicited_events_ready (MMIfaceModem3gpp *self, GAsyncResult *res, GTask *task) { GError *error = NULL; if (!iface_modem_3gpp_parent->enable_unsolicited_events_finish (self, res, &error)) { g_task_return_error (task, error); g_object_unref (task); return; } /* Our own enable now */ mm_base_modem_at_sequence_full ( MM_BASE_MODEM (self), mm_base_modem_peek_port_primary (MM_BASE_MODEM (self)), unsolicited_enable_sequence, NULL, /* response_processor_context */ NULL, /* response_processor_context_free */ NULL, /* cancellable */ (GAsyncReadyCallback)own_enable_unsolicited_events_ready, task); } static void modem_3gpp_enable_unsolicited_events (MMIfaceModem3gpp *self, GAsyncReadyCallback callback, gpointer user_data) { /* Chain up parent's enable */ iface_modem_3gpp_parent->enable_unsolicited_events ( self, (GAsyncReadyCallback)parent_enable_unsolicited_events_ready, g_task_new (self, NULL, callback, user_data)); } /*****************************************************************************/ /* Disabling unsolicited events (3GPP interface) */ static gboolean modem_3gpp_disable_unsolicited_events_finish (MMIfaceModem3gpp *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void parent_disable_unsolicited_events_ready (MMIfaceModem3gpp *self, GAsyncResult *res, GTask *task) { GError *error = NULL; if (!iface_modem_3gpp_parent->disable_unsolicited_events_finish (self, res, &error)) g_task_return_error (task, error); else g_task_return_boolean (task, TRUE); g_object_unref (task); } static void own_disable_unsolicited_events_ready (MMBaseModem *self, GAsyncResult *res, GTask *task) { GError *error = NULL; mm_base_modem_at_sequence_full_finish (self, res, NULL, &error); if (error) { g_task_return_error (task, error); g_object_unref (task); return; } /* Next, chain up parent's disable */ iface_modem_3gpp_parent->disable_unsolicited_events ( MM_IFACE_MODEM_3GPP (self), (GAsyncReadyCallback)parent_disable_unsolicited_events_ready, task); } static const MMBaseModemAtCommand unsolicited_disable_sequence[] = { { "*ERINFO=0", 5, FALSE, NULL }, { "*E2NAP=0", 5, FALSE, NULL }, { NULL } }; static void modem_3gpp_disable_unsolicited_events (MMIfaceModem3gpp *self, GAsyncReadyCallback callback, gpointer user_data) { /* Our own disable first */ mm_base_modem_at_sequence_full ( MM_BASE_MODEM (self), mm_base_modem_peek_port_primary (MM_BASE_MODEM (self)), unsolicited_disable_sequence, NULL, /* response_processor_context */ NULL, /* response_processor_context_free */ NULL, /* cancellable */ (GAsyncReadyCallback)own_disable_unsolicited_events_ready, g_task_new (self, NULL, callback, user_data)); } /*****************************************************************************/ /* Location capabilities loading (Location interface) */ static MMModemLocationSource location_load_capabilities_finish (MMIfaceModemLocation *self, GAsyncResult *res, GError **error) { GError *inner_error = NULL; gssize value; value = g_task_propagate_int (G_TASK (res), &inner_error); if (inner_error) { g_propagate_error (error, inner_error); return MM_MODEM_LOCATION_SOURCE_NONE; } return (MMModemLocationSource)value; } static void parent_load_capabilities_ready (MMIfaceModemLocation *self, GAsyncResult *res, GTask *task) { MMModemLocationSource sources; GError *error = NULL; sources = iface_modem_location_parent->load_capabilities_finish (self, res, &error); if (error) { g_task_return_error (task, error); g_object_unref (task); return; } /* not sure how to check if GPS is supported, just allow it */ if (mm_base_modem_peek_port_gps (MM_BASE_MODEM (self))) sources |= (MM_MODEM_LOCATION_SOURCE_GPS_NMEA | MM_MODEM_LOCATION_SOURCE_GPS_RAW | MM_MODEM_LOCATION_SOURCE_GPS_UNMANAGED); /* So we're done, complete */ g_task_return_int (task, sources); g_object_unref (task); } static void location_load_capabilities (MMIfaceModemLocation *self, GAsyncReadyCallback callback, gpointer user_data) { /* Chain up parent's setup */ iface_modem_location_parent->load_capabilities ( self, (GAsyncReadyCallback)parent_load_capabilities_ready, g_task_new (self, NULL, callback, user_data)); } /*****************************************************************************/ /* Enable/Disable location gathering (Location interface) */ typedef struct { MMModemLocationSource source; } LocationGatheringContext; /******************************/ /* Disable location gathering */ static gboolean disable_location_gathering_finish (MMIfaceModemLocation *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void gps_disabled_ready (MMBaseModem *self, GAsyncResult *res, GTask *task) { LocationGatheringContext *ctx; MMPortSerialGps *gps_port; GError *error = NULL; ctx = g_task_get_task_data (task); mm_base_modem_at_command_full_finish (self, res, &error); /* Only use the GPS port in NMEA/RAW setups */ if (ctx->source & (MM_MODEM_LOCATION_SOURCE_GPS_NMEA | MM_MODEM_LOCATION_SOURCE_GPS_RAW)) { /* Even if we get an error here, we try to close the GPS port */ gps_port = mm_base_modem_peek_port_gps (self); if (gps_port) mm_port_serial_close (MM_PORT_SERIAL (gps_port)); } if (error) g_task_return_error (task, error); else g_task_return_boolean (task, TRUE); g_object_unref (task); } static void disable_location_gathering (MMIfaceModemLocation *_self, MMModemLocationSource source, GAsyncReadyCallback callback, gpointer user_data) { MMBroadbandModemMbm *self = MM_BROADBAND_MODEM_MBM (_self); gboolean stop_gps = FALSE; LocationGatheringContext *ctx; GTask *task; ctx = g_new (LocationGatheringContext, 1); ctx->source = source; task = g_task_new (self, NULL, callback, user_data); g_task_set_task_data (task, ctx, g_free); /* Only stop GPS engine if no GPS-related sources enabled */ if (source & (MM_MODEM_LOCATION_SOURCE_GPS_NMEA | MM_MODEM_LOCATION_SOURCE_GPS_RAW | MM_MODEM_LOCATION_SOURCE_GPS_UNMANAGED)) { self->priv->enabled_sources &= ~source; if (!(self->priv->enabled_sources & (MM_MODEM_LOCATION_SOURCE_GPS_NMEA | MM_MODEM_LOCATION_SOURCE_GPS_RAW | MM_MODEM_LOCATION_SOURCE_GPS_UNMANAGED))) stop_gps = TRUE; } if (stop_gps) { mm_base_modem_at_command_full (MM_BASE_MODEM (_self), mm_base_modem_peek_port_primary (MM_BASE_MODEM (_self)), "AT*E2GPSCTL=0", 3, FALSE, FALSE, /* raw */ NULL, /* cancellable */ (GAsyncReadyCallback)gps_disabled_ready, task); return; } /* For any other location (e.g. 3GPP), or if still some GPS needed, just return */ g_task_return_boolean (task, TRUE); g_object_unref (task); } /*****************************************************************************/ /* Enable location gathering (Location interface) */ static gboolean enable_location_gathering_finish (MMIfaceModemLocation *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void gps_enabled_ready (MMBaseModem *self, GAsyncResult *res, GTask *task) { LocationGatheringContext *ctx; GError *error = NULL; MMPortSerialGps *gps_port; if (!mm_base_modem_at_command_full_finish (self, res, &error)) { g_task_return_error (task, error); g_object_unref (task); return; } ctx = g_task_get_task_data (task); /* Only use the GPS port in NMEA/RAW setups */ if (ctx->source & (MM_MODEM_LOCATION_SOURCE_GPS_NMEA | MM_MODEM_LOCATION_SOURCE_GPS_RAW)) { gps_port = mm_base_modem_peek_port_gps (self); if (!gps_port || !mm_port_serial_open (MM_PORT_SERIAL (gps_port), &error)) { if (error) g_task_return_error (task, error); else g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't open raw GPS serial port"); } else { GByteArray *buf; const gchar *command = "ATE0*E2GPSNPD\r\n"; /* We need to send an AT command to the GPS data port to * toggle it into this data mode. This is a particularity of * mbm cards where the GPS data port is not hard wired. So * we need to use the MMPortSerial API here. */ buf = g_byte_array_new (); g_byte_array_append (buf, (const guint8 *) command, strlen (command)); mm_port_serial_command (MM_PORT_SERIAL (gps_port), buf, 3, FALSE, /* never cached */ FALSE, /* always queued last */ NULL, NULL, NULL); g_byte_array_unref (buf); g_task_return_boolean (task, TRUE); } } else g_task_return_boolean (task, TRUE); g_object_unref (task); } static void parent_enable_location_gathering_ready (MMIfaceModemLocation *_self, GAsyncResult *res, GTask *task) { MMBroadbandModemMbm *self = MM_BROADBAND_MODEM_MBM (_self); LocationGatheringContext *ctx; gboolean start_gps = FALSE; GError *error = NULL; if (!iface_modem_location_parent->enable_location_gathering_finish (_self, res, &error)) { g_task_return_error (task, error); g_object_unref (task); return; } /* Now our own enabling */ /* NMEA and RAW are both enabled in the same way */ ctx = g_task_get_task_data (task); if (ctx->source & (MM_MODEM_LOCATION_SOURCE_GPS_NMEA | MM_MODEM_LOCATION_SOURCE_GPS_RAW | MM_MODEM_LOCATION_SOURCE_GPS_UNMANAGED)) { /* Only start GPS engine if not done already */ if (!(self->priv->enabled_sources & (MM_MODEM_LOCATION_SOURCE_GPS_NMEA | MM_MODEM_LOCATION_SOURCE_GPS_RAW | MM_MODEM_LOCATION_SOURCE_GPS_UNMANAGED))) start_gps = TRUE; self->priv->enabled_sources |= ctx->source; } if (start_gps) { mm_base_modem_at_command_full (MM_BASE_MODEM (self), mm_base_modem_peek_port_primary (MM_BASE_MODEM (self)), "AT*E2GPSCTL=1," MBM_GPS_NMEA_INTERVAL ",0", 3, FALSE, FALSE, /* raw */ NULL, /* cancellable */ (GAsyncReadyCallback)gps_enabled_ready, task); return; } /* For any other location (e.g. 3GPP), or if GPS already running just return */ g_task_return_boolean (task, TRUE); g_object_unref (task); } static void enable_location_gathering (MMIfaceModemLocation *self, MMModemLocationSource source, GAsyncReadyCallback callback, gpointer user_data) { LocationGatheringContext *ctx; GTask *task; ctx = g_new (LocationGatheringContext, 1); ctx->source = source; task = g_task_new (self, NULL, callback, user_data); g_task_set_task_data (task, ctx, g_free); /* Chain up parent's gathering enable */ iface_modem_location_parent->enable_location_gathering (self, source, (GAsyncReadyCallback)parent_enable_location_gathering_ready, task); } /*****************************************************************************/ /* Setup ports (Broadband modem class) */ static void emrdy_received (MMPortSerialAt *port, GMatchInfo *info, MMBroadbandModemMbm *self) { self->priv->have_emrdy = TRUE; } static void gps_trace_received (MMPortSerialGps *port, const gchar *trace, MMIfaceModemLocation *self) { mm_iface_modem_location_gps_update (self, trace); } static void setup_ports (MMBroadbandModem *_self) { MMBroadbandModemMbm *self = MM_BROADBAND_MODEM_MBM (_self); MMPortSerialAt *ports[2]; MMPortSerialGps *gps_data_port; guint i; /* Call parent's setup ports first always */ MM_BROADBAND_MODEM_CLASS (mm_broadband_modem_mbm_parent_class)->setup_ports (_self); ports[0] = mm_base_modem_peek_port_primary (MM_BASE_MODEM (self)); ports[1] = mm_base_modem_peek_port_secondary (MM_BASE_MODEM (self)); /* Setup unsolicited handlers which should be always on */ for (i = 0; i < G_N_ELEMENTS (ports); i++) { if (!ports[i]) continue; /* The Ericsson modems always have a free AT command port, so we * don't need to flash the ports when disconnecting to get back to * command mode. F5521gw R2A07 resets port properties like echo when * flashed, leading to confusion. bgo #650740 */ g_object_set (G_OBJECT (ports[i]), MM_PORT_SERIAL_FLASH_OK, FALSE, NULL); mm_port_serial_at_add_unsolicited_msg_handler ( ports[i], self->priv->emrdy_regex, (MMPortSerialAtUnsolicitedMsgFn)emrdy_received, self, NULL); /* Several unsolicited messages to always ignore... */ mm_port_serial_at_add_unsolicited_msg_handler ( ports[i], self->priv->pacsp_regex, NULL, NULL, NULL); mm_port_serial_at_add_unsolicited_msg_handler ( ports[i], self->priv->estksmenu_regex, NULL, NULL, NULL); mm_port_serial_at_add_unsolicited_msg_handler ( ports[i], self->priv->estksms_regex, NULL, NULL, NULL); mm_port_serial_at_add_unsolicited_msg_handler ( ports[i], self->priv->emwi_regex, NULL, NULL, NULL); } /* Now reset the unsolicited messages we'll handle when enabled */ set_unsolicited_events_handlers (MM_BROADBAND_MODEM_MBM (self), FALSE); /* NMEA GPS monitoring */ gps_data_port = mm_base_modem_peek_port_gps (MM_BASE_MODEM (self)); if (gps_data_port) { /* make sure GPS is stopped incase it was left enabled */ mm_base_modem_at_command_full (MM_BASE_MODEM (self), mm_base_modem_peek_port_primary (MM_BASE_MODEM (self)), "AT*E2GPSCTL=0", 3, FALSE, FALSE, NULL, NULL, NULL); /* Add handler for the NMEA traces */ mm_port_serial_gps_add_trace_handler (gps_data_port, (MMPortSerialGpsTraceFn)gps_trace_received, self, NULL); } } /*****************************************************************************/ MMBroadbandModemMbm * mm_broadband_modem_mbm_new (const gchar *device, const gchar *physdev, const gchar **drivers, const gchar *plugin, guint16 vendor_id, guint16 product_id) { return g_object_new (MM_TYPE_BROADBAND_MODEM_MBM, MM_BASE_MODEM_DEVICE, device, MM_BASE_MODEM_PHYSDEV, physdev, MM_BASE_MODEM_DRIVERS, drivers, MM_BASE_MODEM_PLUGIN, plugin, MM_BASE_MODEM_VENDOR_ID, vendor_id, MM_BASE_MODEM_PRODUCT_ID, product_id, /* MBM bearer supports NET only */ MM_BASE_MODEM_DATA_NET_SUPPORTED, TRUE, MM_BASE_MODEM_DATA_TTY_SUPPORTED, FALSE, NULL); } static void mm_broadband_modem_mbm_init (MMBroadbandModemMbm *self) { /* Initialize private data */ self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, MM_TYPE_BROADBAND_MODEM_MBM, MMBroadbandModemMbmPrivate); /* Prepare regular expressions to setup */ self->priv->e2nap_regex = g_regex_new ("\\r\\n\\*E2NAP: (\\d)\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); self->priv->e2nap_ext_regex = g_regex_new ("\\r\\n\\*E2NAP: (\\d),.*\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); self->priv->emrdy_regex = g_regex_new ("\\r\\n\\*EMRDY: \\d\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); self->priv->pacsp_regex = g_regex_new ("\\r\\n\\+PACSP(\\d)\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); self->priv->estksmenu_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); self->priv->estksms_regex = g_regex_new ("\\r\\n\\*ESTKSMS:.*\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); self->priv->emwi_regex = g_regex_new ("\\r\\n\\*EMWI: (\\d),(\\d).*\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); self->priv->erinfo_regex = g_regex_new ("\\r\\n\\*ERINFO:\\s*(\\d),(\\d),(\\d).*\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); self->priv->mbm_mode = MBM_NETWORK_MODE_ANY; } static void finalize (GObject *object) { MMBroadbandModemMbm *self = MM_BROADBAND_MODEM_MBM (object); g_regex_unref (self->priv->e2nap_regex); g_regex_unref (self->priv->e2nap_ext_regex); g_regex_unref (self->priv->emrdy_regex); g_regex_unref (self->priv->pacsp_regex); g_regex_unref (self->priv->estksmenu_regex); g_regex_unref (self->priv->estksms_regex); g_regex_unref (self->priv->emwi_regex); g_regex_unref (self->priv->erinfo_regex); G_OBJECT_CLASS (mm_broadband_modem_mbm_parent_class)->finalize (object); } static void iface_modem_init (MMIfaceModem *iface) { iface_modem_parent = g_type_interface_peek_parent (iface); iface->create_bearer = modem_create_bearer; iface->create_bearer_finish = modem_create_bearer_finish; iface->create_sim = create_sim; iface->create_sim_finish = create_sim_finish; iface->modem_after_sim_unlock = modem_after_sim_unlock; iface->modem_after_sim_unlock_finish = modem_after_sim_unlock_finish; iface->load_supported_modes = load_supported_modes; iface->load_supported_modes_finish = load_supported_modes_finish; iface->load_current_modes = load_current_modes; iface->load_current_modes_finish = load_current_modes_finish; iface->set_current_modes = set_current_modes; iface->set_current_modes_finish = set_current_modes_finish; iface->reset = reset; iface->reset_finish = reset_finish; iface->factory_reset = factory_reset; iface->factory_reset_finish = factory_reset_finish; iface->load_unlock_retries = load_unlock_retries; iface->load_unlock_retries_finish = load_unlock_retries_finish; iface->load_power_state = load_power_state; iface->load_power_state_finish = load_power_state_finish; iface->modem_power_up = modem_power_up; iface->modem_power_up_finish = modem_power_up_finish; iface->modem_power_down = modem_power_down; iface->modem_power_down_finish = modem_power_down_finish; } static void iface_modem_3gpp_init (MMIfaceModem3gpp *iface) { iface_modem_3gpp_parent = g_type_interface_peek_parent (iface); iface->enable_unsolicited_events = modem_3gpp_enable_unsolicited_events; iface->enable_unsolicited_events_finish = modem_3gpp_enable_unsolicited_events_finish; iface->disable_unsolicited_events = modem_3gpp_disable_unsolicited_events; iface->disable_unsolicited_events_finish = modem_3gpp_disable_unsolicited_events_finish; iface->setup_unsolicited_events = modem_3gpp_setup_unsolicited_events; iface->setup_unsolicited_events_finish = modem_3gpp_setup_cleanup_unsolicited_events_finish; iface->cleanup_unsolicited_events = modem_3gpp_cleanup_unsolicited_events; iface->cleanup_unsolicited_events_finish = modem_3gpp_setup_cleanup_unsolicited_events_finish; } static void iface_modem_location_init (MMIfaceModemLocation *iface) { iface_modem_location_parent = g_type_interface_peek_parent (iface); iface->load_capabilities = location_load_capabilities; iface->load_capabilities_finish = location_load_capabilities_finish; iface->enable_location_gathering = enable_location_gathering; iface->enable_location_gathering_finish = enable_location_gathering_finish; iface->disable_location_gathering = disable_location_gathering; iface->disable_location_gathering_finish = disable_location_gathering_finish; } static void mm_broadband_modem_mbm_class_init (MMBroadbandModemMbmClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); MMBroadbandModemClass *broadband_modem_class = MM_BROADBAND_MODEM_CLASS (klass); g_type_class_add_private (object_class, sizeof (MMBroadbandModemMbmPrivate)); object_class->finalize = finalize; broadband_modem_class->setup_ports = setup_ports; broadband_modem_class->enabling_modem_init = enabling_modem_init; broadband_modem_class->enabling_modem_init_finish = enabling_modem_init_finish; } ModemManager-1.23.4-dev/src/plugins/mbm/mm-broadband-modem-mbm.h000066400000000000000000000052531456466623000243210ustar00rootroot00000000000000/* -*- 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 - 2010 Ericsson AB * Copyright (C) 2009 - 2012 Red Hat, Inc. * Copyright (C) 2012 Lanedo GmbH * * Author: Per Hallsmark * Bjorn Runaker * Torgny Johansson * Jonas Sjöquist * Dan Williams * Aleksander Morgado */ #ifndef MM_BROADBAND_MODEM_MBM_H #define MM_BROADBAND_MODEM_MBM_H #include "mm-broadband-modem.h" #define MM_TYPE_BROADBAND_MODEM_MBM (mm_broadband_modem_mbm_get_type ()) #define MM_BROADBAND_MODEM_MBM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_BROADBAND_MODEM_MBM, MMBroadbandModemMbm)) #define MM_BROADBAND_MODEM_MBM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_BROADBAND_MODEM_MBM, MMBroadbandModemMbmClass)) #define MM_IS_BROADBAND_MODEM_MBM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_BROADBAND_MODEM_MBM)) #define MM_IS_BROADBAND_MODEM_MBM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_BROADBAND_MODEM_MBM)) #define MM_BROADBAND_MODEM_MBM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_BROADBAND_MODEM_MBM, MMBroadbandModemMbmClass)) typedef struct _MMBroadbandModemMbm MMBroadbandModemMbm; typedef struct _MMBroadbandModemMbmClass MMBroadbandModemMbmClass; typedef struct _MMBroadbandModemMbmPrivate MMBroadbandModemMbmPrivate; struct _MMBroadbandModemMbm { MMBroadbandModem parent; MMBroadbandModemMbmPrivate *priv; }; struct _MMBroadbandModemMbmClass{ MMBroadbandModemClass parent; }; GType mm_broadband_modem_mbm_get_type (void); MMBroadbandModemMbm *mm_broadband_modem_mbm_new (const gchar *device, const gchar *physdev, const gchar **drivers, const gchar *plugin, guint16 vendor_id, guint16 product_id); #endif /* MM_BROADBAND_MODEM_MBM_H */ ModemManager-1.23.4-dev/src/plugins/mbm/mm-modem-helpers-mbm.c000066400000000000000000000255561456466623000240520ustar00rootroot00000000000000/* -*- 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) 2012 Google, Inc. * Copyright (C) 2012 - 2013 Aleksander Morgado * Copyright (C) 2014 Dan Williams */ #include #include #include #include #include #include #define _LIBMM_INSIDE_MM #include #include "mm-log.h" #include "mm-modem-helpers.h" #include "mm-modem-helpers-mbm.h" /*****************************************************************************/ /* *E2IPCFG response parser */ static gboolean validate_address (int family, const char *addr) { struct in6_addr tmp6 = IN6ADDR_ANY_INIT; if (inet_pton (family, addr, (void *) &tmp6) != 1) { g_message ("%s: famil '%s'", __func__, addr); return FALSE; } if ((family == AF_INET6) && IN6_IS_ADDR_UNSPECIFIED (&tmp6)) return FALSE; return TRUE; } #define E2IPCFG_TAG "*E2IPCFG" gboolean mm_mbm_parse_e2ipcfg_response (const gchar *response, MMBearerIpConfig **out_ip4_config, MMBearerIpConfig **out_ip6_config, GError **error) { MMBearerIpConfig **ip_config = NULL; gboolean got_address = FALSE; gboolean got_gw = FALSE; gboolean got_dns = FALSE; g_autoptr(GRegex) r = NULL; g_autoptr(GMatchInfo) match_info = NULL; GError *match_error = NULL; gchar *dns[3] = { 0 }; guint dns_idx = 0; int family = AF_INET; MMBearerIpMethod method = MM_BEARER_IP_METHOD_STATIC; g_return_val_if_fail (out_ip4_config, FALSE); g_return_val_if_fail (out_ip6_config, FALSE); if (!response || !g_str_has_prefix (response, E2IPCFG_TAG)) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Missing " E2IPCFG_TAG " prefix"); return FALSE; } response = mm_strip_tag (response, "*E2IPCFG: "); if (strchr (response, ':')) { family = AF_INET6; ip_config = out_ip6_config; method = MM_BEARER_IP_METHOD_DHCP; } else if (strchr (response, '.')) { family = AF_INET; ip_config = out_ip4_config; method = MM_BEARER_IP_METHOD_STATIC; } else { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Failed to detect " E2IPCFG_TAG " address family"); return FALSE; } /* *E2IPCFG: (1,)(2,)(3,)(3,) * * *E2IPCFG: (1,"46.157.32.246")(2,"46.157.32.243")(3,"193.213.112.4")(3,"130.67.15.198") * *E2IPCFG: (1,"fe80:0000:0000:0000:0000:0000:e537:1801")(3,"2001:4600:0004:0fff:0000:0000:0000:0054")(3,"2001:4600:0004:1fff:0000:0000:0000:0054") * *E2IPCFG: (1,"fe80:0000:0000:0000:0000:0027:b7fe:9401")(3,"fd00:976a:0000:0000:0000:0000:0000:0009") */ r = g_regex_new ("\\((\\d),\"([0-9a-fA-F.:]+)\"\\)", 0, 0, NULL); g_assert (r != NULL); if (!g_regex_match_full (r, response, -1, 0, 0, &match_info, &match_error)) { if (match_error) { g_propagate_error (error, match_error); g_prefix_error (error, "Could not parse " E2IPCFG_TAG " results: "); } else { g_set_error_literal (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't match " E2IPCFG_TAG " reply"); } return FALSE; } *ip_config = mm_bearer_ip_config_new (); mm_bearer_ip_config_set_method (*ip_config, method); while (g_match_info_matches (match_info)) { g_autofree gchar *id = NULL; g_autofree gchar *str = NULL; id = g_match_info_fetch (match_info, 1); str = g_match_info_fetch (match_info, 2); switch (atoi (id)) { case 1: if (validate_address (family, str)) { mm_bearer_ip_config_set_address (*ip_config, str); mm_bearer_ip_config_set_prefix (*ip_config, (family == AF_INET6) ? 64 : 28); got_address = TRUE; } break; case 2: if ((family == AF_INET) && validate_address (family, str)) { mm_bearer_ip_config_set_gateway (*ip_config, str); got_gw = TRUE; } break; case 3: if (validate_address (family, str)) { dns[dns_idx++] = g_strdup (str); got_dns = TRUE; } break; default: break; } g_match_info_next (match_info, NULL); } if (got_dns) { mm_bearer_ip_config_set_dns (*ip_config, (const gchar **) dns); g_free (dns[0]); g_free (dns[1]); } if (!got_address || (family == AF_INET && !got_gw)) { g_object_unref (*ip_config); *ip_config = NULL; g_set_error_literal (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Got incomplete IP configuration from " E2IPCFG_TAG); return FALSE; } return TRUE; } /*****************************************************************************/ #define CFUN_TAG "+CFUN:" static void add_supported_mode (guint mode, gpointer log_object, guint32 *mask) { g_assert (mask); if (mode >= 32) mm_obj_warn (log_object, "ignored unexpected mode in +CFUN match: %d", mode); else *mask |= (1 << mode); } gboolean mm_mbm_parse_cfun_test (const gchar *response, gpointer log_object, guint32 *supported_mask, GError **error) { gchar **groups; guint32 mask = 0; g_assert (supported_mask); if (!response || !g_str_has_prefix (response, CFUN_TAG)) { g_set_error_literal (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Missing " CFUN_TAG " prefix"); return FALSE; } /* * AT+CFUN=? * +CFUN: (0,1,4-6),(0,1) * OK */ /* Strip tag from response */ response = mm_strip_tag (response, CFUN_TAG); /* Split response in (groups) */ groups = mm_split_string_groups (response); /* First group is the one listing supported modes */ if (groups && groups[0]) { gchar **supported_modes; supported_modes = g_strsplit_set (groups[0], ", ", -1); if (supported_modes) { guint i; for (i = 0; supported_modes[i]; i++) { gchar *separator; guint mode; if (!supported_modes[i][0]) continue; /* Check if this is a range that's being given to us */ separator = strchr (supported_modes[i], '-'); if (separator) { gchar *first_str; gchar *last_str; guint first; guint last; *separator = '\0'; first_str = supported_modes[i]; last_str = separator + 1; if (!mm_get_uint_from_str (first_str, &first)) mm_obj_warn (log_object, "couldn't match range start: '%s'", first_str); else if (!mm_get_uint_from_str (last_str, &last)) mm_obj_warn (log_object, "couldn't match range stop: '%s'", last_str); else if (first >= last) mm_obj_warn (log_object, "couldn't match range: wrong first '%s' and last '%s' items", first_str, last_str); else { for (mode = first; mode <= last; mode++) add_supported_mode (mode, log_object, &mask); } } else { if (!mm_get_uint_from_str (supported_modes[i], &mode)) mm_obj_warn (log_object, "couldn't match mode: '%s'", supported_modes[i]); else add_supported_mode (mode, log_object, &mask); } } g_strfreev (supported_modes); } } g_strfreev (groups); if (mask) *supported_mask = mask; return !!mask; } /*****************************************************************************/ /* AT+CFUN? response parsers */ gboolean mm_mbm_parse_cfun_query_power_state (const gchar *response, MMModemPowerState *out_state, GError **error) { guint state; if (!mm_3gpp_parse_cfun_query_response (response, &state, error)) return FALSE; switch (state) { case MBM_NETWORK_MODE_OFFLINE: *out_state = MM_MODEM_POWER_STATE_OFF; return TRUE; case MBM_NETWORK_MODE_LOW_POWER: *out_state = MM_MODEM_POWER_STATE_LOW; return TRUE; case MBM_NETWORK_MODE_ANY: case MBM_NETWORK_MODE_2G: case MBM_NETWORK_MODE_3G: *out_state = MM_MODEM_POWER_STATE_ON; return TRUE; default: g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Unknown +CFUN pòwer state: '%u'", state); return FALSE; } } gboolean mm_mbm_parse_cfun_query_current_modes (const gchar *response, MMModemMode *allowed, gint *mbm_mode, GError **error) { guint state; g_assert (mbm_mode); g_assert (allowed); if (!mm_3gpp_parse_cfun_query_response (response, &state, error)) return FALSE; switch (state) { case MBM_NETWORK_MODE_OFFLINE: case MBM_NETWORK_MODE_LOW_POWER: /* Do not update mbm_mode */ *allowed = MM_MODEM_MODE_NONE; return TRUE; case MBM_NETWORK_MODE_2G: *mbm_mode = MBM_NETWORK_MODE_2G; *allowed = MM_MODEM_MODE_2G; return TRUE; case MBM_NETWORK_MODE_3G: *mbm_mode = MBM_NETWORK_MODE_3G; *allowed = MM_MODEM_MODE_3G; return TRUE; case MBM_NETWORK_MODE_ANY: /* Do not update mbm_mode */ *allowed = (MM_MODEM_MODE_2G | MM_MODEM_MODE_3G); return TRUE; default: g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Unknown +CFUN current mode: '%u'", state); return FALSE; } } ModemManager-1.23.4-dev/src/plugins/mbm/mm-modem-helpers-mbm.h000066400000000000000000000042111456466623000240400ustar00rootroot00000000000000/* -*- 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) 2014 Dan Williams */ #ifndef MM_MODEM_HELPERS_MBM_H #define MM_MODEM_HELPERS_MBM_H #include "glib.h" /* *E2IPCFG response parser */ gboolean mm_mbm_parse_e2ipcfg_response (const gchar *response, MMBearerIpConfig **out_ip4_config, MMBearerIpConfig **out_ip6_config, GError **error); typedef enum { MBM_NETWORK_MODE_OFFLINE = 0, MBM_NETWORK_MODE_ANY = 1, MBM_NETWORK_MODE_LOW_POWER = 4, MBM_NETWORK_MODE_2G = 5, MBM_NETWORK_MODE_3G = 6, } MbmNetworkMode; /* AT+CFUN=? test parser * Returns a bitmask, bit index set for the supported modes reported */ gboolean mm_mbm_parse_cfun_test (const gchar *response, gpointer log_object, guint32 *supported_mask, GError **error); /* AT+CFUN? response parsers */ gboolean mm_mbm_parse_cfun_query_power_state (const gchar *response, MMModemPowerState *out_state, GError **error); gboolean mm_mbm_parse_cfun_query_current_modes (const gchar *response, MMModemMode *allowed, gint *mbm_mode, GError **error); #endif /* MM_MODEM_HELPERS_MBM_H */ ModemManager-1.23.4-dev/src/plugins/mbm/mm-plugin-mbm.c000066400000000000000000000065571456466623000226070ustar00rootroot00000000000000/* -*- 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 Ericsson AB * Copyright (C) 2012 Lanedo GmbH * * Author: Per Hallsmark * Author: Aleksander Morgado */ #include #include #define _LIBMM_INSIDE_MM #include #include "mm-log-object.h" #include "mm-plugin-common.h" #include "mm-broadband-modem-mbm.h" #if defined WITH_MBIM #include "mm-broadband-modem-mbim.h" #endif #define MM_TYPE_PLUGIN_MBM mm_plugin_mbm_get_type () MM_DEFINE_PLUGIN (MBM, mbm, Mbm) /*****************************************************************************/ static MMBaseModem * create_modem (MMPlugin *self, const gchar *uid, const gchar *physdev, const gchar **drivers, guint16 vendor, guint16 product, guint16 subsystem_vendor, GList *probes, GError **error) { #if defined WITH_MBIM if (mm_port_probe_list_has_mbim_port (probes)) { mm_obj_dbg (self, "MBIM-powered Ericsson modem found..."); return MM_BASE_MODEM (mm_broadband_modem_mbim_new (uid, physdev, drivers, mm_plugin_get_name (self), vendor, product)); } #endif return MM_BASE_MODEM (mm_broadband_modem_mbm_new (uid, physdev, drivers, mm_plugin_get_name (self), vendor, product)); } /*****************************************************************************/ MM_PLUGIN_NAMED_CREATOR_SCOPE MMPlugin * mm_plugin_create_mbm (void) { static const gchar *subsystems[] = { "tty", "net", "usbmisc", NULL }; static const gchar *udev_tags[] = { "ID_MM_ERICSSON_MBM", NULL }; return MM_PLUGIN ( g_object_new (MM_TYPE_PLUGIN_MBM, MM_PLUGIN_NAME, MM_MODULE_NAME, MM_PLUGIN_ALLOWED_SUBSYSTEMS, subsystems, MM_PLUGIN_ALLOWED_UDEV_TAGS, udev_tags, MM_PLUGIN_ALLOWED_AT, TRUE, MM_PLUGIN_ALLOWED_MBIM, TRUE, NULL)); } static void mm_plugin_mbm_init (MMPluginMbm *self) { } static void mm_plugin_mbm_class_init (MMPluginMbmClass *klass) { MMPluginClass *plugin_class = MM_PLUGIN_CLASS (klass); plugin_class->create_modem = create_modem; } ModemManager-1.23.4-dev/src/plugins/mbm/mm-sim-mbm.c000066400000000000000000000150021456466623000220620ustar00rootroot00000000000000/* -*- 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) 2012 Aleksander Morgado */ #include #include #include #include #include #include #include #define _LIBMM_INSIDE_MM #include #include "mm-log-object.h" #include "mm-base-modem-at.h" #include "mm-sim-mbm.h" G_DEFINE_TYPE (MMSimMbm, mm_sim_mbm, MM_TYPE_BASE_SIM) /*****************************************************************************/ /* SEND PIN/PUK (Generic implementation) */ typedef struct { MMBaseModem *modem; guint retries; } SendPinPukContext; static void send_pin_puk_context_free (SendPinPukContext *ctx) { g_object_unref (ctx->modem); g_slice_free (SendPinPukContext, ctx); } static gboolean common_send_pin_puk_finish (MMBaseSim *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void wait_for_unlocked_status (GTask *task); static void cpin_query_ready (MMBaseModem *modem, GAsyncResult *res, GTask *task) { const gchar *result; result = mm_base_modem_at_command_finish (modem, res, NULL); if (result && strstr (result, "READY")) { /* All done! */ g_task_return_boolean (task, TRUE); g_object_unref (task); return; } /* Need to recheck */ wait_for_unlocked_status (task); } static gboolean cpin_query_cb (GTask *task) { SendPinPukContext *ctx; ctx = g_task_get_task_data (task); mm_base_modem_at_command (ctx->modem, "+CPIN?", 20, FALSE, (GAsyncReadyCallback)cpin_query_ready, task); return G_SOURCE_REMOVE; } static void wait_for_unlocked_status (GTask *task) { MMSimMbm *self; SendPinPukContext *ctx; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); /* Oops... :/ */ if (ctx->retries == 0) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "PIN was sent but modem didn't report unlocked"); g_object_unref (task); return; } /* Check status */ ctx->retries--; mm_obj_dbg (self, "scheduling lock state check..."); g_timeout_add_seconds (1, (GSourceFunc)cpin_query_cb, task); } static void send_pin_puk_ready (MMBaseModem *modem, GAsyncResult *res, GTask *task) { SendPinPukContext *ctx; GError *error = NULL; mm_base_modem_at_command_finish (modem, res, &error); if (error) { g_task_return_error (task, error); g_object_unref (task); return; } /* No explicit error sending the PIN/PUK, now check status until we have the * expected lock status */ ctx = g_task_get_task_data (task); ctx->retries = 3; wait_for_unlocked_status (task); } static void common_send_pin_puk (MMBaseSim *self, const gchar *pin, const gchar *puk, GAsyncReadyCallback callback, gpointer user_data) { SendPinPukContext *ctx; GTask *task; gchar *command; ctx = g_slice_new (SendPinPukContext); g_object_get (self, MM_BASE_SIM_MODEM, &ctx->modem, NULL); task = g_task_new (self, NULL, callback, user_data); g_task_set_task_data (task, ctx, (GDestroyNotify)send_pin_puk_context_free); command = (puk ? g_strdup_printf ("+CPIN=\"%s\",\"%s\"", puk, pin) : g_strdup_printf ("+CPIN=\"%s\"", pin)); mm_base_modem_at_command (ctx->modem, command, 3, FALSE, (GAsyncReadyCallback)send_pin_puk_ready, task); g_free (command); } static void send_puk (MMBaseSim *self, const gchar *puk, const gchar *new_pin, GAsyncReadyCallback callback, gpointer user_data) { common_send_pin_puk (self, new_pin, puk, callback, user_data); } static void send_pin (MMBaseSim *self, const gchar *pin, GAsyncReadyCallback callback, gpointer user_data) { common_send_pin_puk (self, pin, NULL, callback, user_data); } /*****************************************************************************/ MMBaseSim * mm_sim_mbm_new_finish (GAsyncResult *res, GError **error) { GObject *source; GObject *sim; source = g_async_result_get_source_object (res); sim = g_async_initable_new_finish (G_ASYNC_INITABLE (source), res, error); g_object_unref (source); if (!sim) return NULL; /* Only export valid SIMs */ mm_base_sim_export (MM_BASE_SIM (sim)); return MM_BASE_SIM (sim); } void mm_sim_mbm_new (MMBaseModem *modem, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { g_async_initable_new_async (MM_TYPE_SIM_MBM, G_PRIORITY_DEFAULT, cancellable, callback, user_data, MM_BASE_SIM_MODEM, modem, "active", TRUE, /* by default always active */ NULL); } static void mm_sim_mbm_init (MMSimMbm *self) { } static void mm_sim_mbm_class_init (MMSimMbmClass *klass) { MMBaseSimClass *base_sim_class = MM_BASE_SIM_CLASS (klass); base_sim_class->send_pin = send_pin; base_sim_class->send_pin_finish = common_send_pin_puk_finish; base_sim_class->send_puk = send_puk; base_sim_class->send_puk_finish = common_send_pin_puk_finish; } ModemManager-1.23.4-dev/src/plugins/mbm/mm-sim-mbm.h000066400000000000000000000035531456466623000220770ustar00rootroot00000000000000/* -*- 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) 2013 Aleksander Morgado */ #ifndef MM_SIM_MBM_H #define MM_SIM_MBM_H #include #include #include "mm-base-sim.h" #define MM_TYPE_SIM_MBM (mm_sim_mbm_get_type ()) #define MM_SIM_MBM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_SIM_MBM, MMSimMbm)) #define MM_SIM_MBM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_SIM_MBM, MMSimMbmClass)) #define MM_IS_SIM_MBM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_SIM_MBM)) #define MM_IS_SIM_MBM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_SIM_MBM)) #define MM_SIM_MBM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_SIM_MBM, MMSimMbmClass)) typedef struct _MMSimMbm MMSimMbm; typedef struct _MMSimMbmClass MMSimMbmClass; struct _MMSimMbm { MMBaseSim parent; }; struct _MMSimMbmClass { MMBaseSimClass parent; }; GType mm_sim_mbm_get_type (void); void mm_sim_mbm_new (MMBaseModem *modem, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); MMBaseSim *mm_sim_mbm_new_finish (GAsyncResult *res, GError **error); #endif /* MM_SIM_MBM_H */ ModemManager-1.23.4-dev/src/plugins/mbm/tests/000077500000000000000000000000001456466623000211125ustar00rootroot00000000000000ModemManager-1.23.4-dev/src/plugins/mbm/tests/test-modem-helpers-mbm.c000066400000000000000000000215251456466623000255520ustar00rootroot00000000000000/* -*- 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) 2013 Aleksander Morgado * Copyright (C) 2014 Dan Williams */ #include #include #include #include #include #include #define _LIBMM_INSIDE_MM #include #include "mm-log-test.h" #include "mm-modem-helpers.h" #include "mm-modem-helpers-mbm.h" /*****************************************************************************/ /* Test *E2IPCFG responses */ typedef struct { const gchar *str; /* IPv4 */ const gchar *ipv4_addr; const gchar *ipv4_gw; const gchar *ipv4_dns1; const gchar *ipv4_dns2; /* IPv6 */ const gchar *ipv6_addr; const gchar *ipv6_dns1; const gchar *ipv6_dns2; } E2ipcfgTest; static const E2ipcfgTest tests[] = { { "*E2IPCFG: (1,\"46.157.32.246\")(2,\"46.157.32.243\")(3,\"193.213.112.4\")(3,\"130.67.15.198\")\r\n", "46.157.32.246", "46.157.32.243", "193.213.112.4", "130.67.15.198", NULL, NULL }, { "*E2IPCFG: (1,\"fe80:0000:0000:0000:0000:0000:e537:1801\")(3,\"2001:4600:0004:0fff:0000:0000:0000:0054\")(3,\"2001:4600:0004:1fff:0000:0000:0000:0054\")\r\n", NULL, NULL, NULL, NULL, "fe80:0000:0000:0000:0000:0000:e537:1801", "2001:4600:0004:0fff:0000:0000:0000:0054", "2001:4600:0004:1fff:0000:0000:0000:0054" }, { "*E2IPCFG: (1,\"fe80:0000:0000:0000:0000:0027:b7fe:9401\")(3,\"fd00:976a:0000:0000:0000:0000:0000:0009\")\r\n", NULL, NULL, NULL, NULL, "fe80:0000:0000:0000:0000:0027:b7fe:9401", "fd00:976a:0000:0000:0000:0000:0000:0009", NULL }, { NULL } }; static void test_e2ipcfg (void) { guint i; for (i = 0; tests[i].str; i++) { gboolean success; GError *error = NULL; MMBearerIpConfig *ipv4 = NULL; MMBearerIpConfig *ipv6 = NULL; const gchar **dns; guint dnslen; success = mm_mbm_parse_e2ipcfg_response (tests[i].str, &ipv4, &ipv6, &error); g_assert_no_error (error); g_assert (success); /* IPv4 */ if (tests[i].ipv4_addr) { g_assert (ipv4); g_assert_cmpint (mm_bearer_ip_config_get_method (ipv4), ==, MM_BEARER_IP_METHOD_STATIC); g_assert_cmpstr (mm_bearer_ip_config_get_address (ipv4), ==, tests[i].ipv4_addr); g_assert_cmpint (mm_bearer_ip_config_get_prefix (ipv4), ==, 28); g_assert_cmpstr (mm_bearer_ip_config_get_gateway (ipv4), ==, tests[i].ipv4_gw); dns = mm_bearer_ip_config_get_dns (ipv4); g_assert (dns); dnslen = g_strv_length ((gchar **) dns); if (tests[i].ipv4_dns2 != NULL) g_assert_cmpint (dnslen, ==, 2); else g_assert_cmpint (dnslen, ==, 1); g_assert_cmpstr (dns[0], ==, tests[i].ipv4_dns1); g_assert_cmpstr (dns[1], ==, tests[i].ipv4_dns2); g_object_unref (ipv4); } else g_assert (ipv4 == NULL); /* IPv6 */ if (tests[i].ipv6_addr) { struct in6_addr a6; g_assert (ipv6); g_assert_cmpstr (mm_bearer_ip_config_get_address (ipv6), ==, tests[i].ipv6_addr); g_assert_cmpint (mm_bearer_ip_config_get_prefix (ipv6), ==, 64); g_assert (inet_pton (AF_INET6, mm_bearer_ip_config_get_address (ipv6), &a6)); if (IN6_IS_ADDR_LINKLOCAL (&a6)) g_assert_cmpint (mm_bearer_ip_config_get_method (ipv6), ==, MM_BEARER_IP_METHOD_DHCP); else g_assert_cmpint (mm_bearer_ip_config_get_method (ipv6), ==, MM_BEARER_IP_METHOD_STATIC); dns = mm_bearer_ip_config_get_dns (ipv6); g_assert (dns); dnslen = g_strv_length ((gchar **) dns); if (tests[i].ipv6_dns2 != NULL) g_assert_cmpint (dnslen, ==, 2); else g_assert_cmpint (dnslen, ==, 1); g_assert_cmpstr (dns[0], ==, tests[i].ipv6_dns1); g_assert_cmpstr (dns[1], ==, tests[i].ipv6_dns2); g_object_unref (ipv6); } else g_assert (ipv6 == NULL); } } /*****************************************************************************/ /* Test +CFUN test responses */ #define MAX_MODES 32 typedef struct { const gchar *str; guint32 expected_mask; } CfunTest; static const CfunTest cfun_tests[] = { { "+CFUN: (0,1,4-6),(1-0)\r\n", ((1 << MBM_NETWORK_MODE_OFFLINE) | (1 << MBM_NETWORK_MODE_ANY) | (1 << MBM_NETWORK_MODE_LOW_POWER) | (1 << MBM_NETWORK_MODE_2G) | (1 << MBM_NETWORK_MODE_3G)) }, { "+CFUN: (0,1,4-6)\r\n", ((1 << MBM_NETWORK_MODE_OFFLINE) | (1 << MBM_NETWORK_MODE_ANY) | (1 << MBM_NETWORK_MODE_LOW_POWER) | (1 << MBM_NETWORK_MODE_2G) | (1 << MBM_NETWORK_MODE_3G)) }, { "+CFUN: (0,1,4)\r\n", ((1 << MBM_NETWORK_MODE_OFFLINE) | (1 << MBM_NETWORK_MODE_ANY) | (1 << MBM_NETWORK_MODE_LOW_POWER)) }, { "+CFUN: (0,1)\r\n", ((1 << MBM_NETWORK_MODE_OFFLINE) | (1 << MBM_NETWORK_MODE_ANY)) }, }; static void test_cfun_test (void) { guint i; for (i = 0; i < G_N_ELEMENTS (cfun_tests); i++) { guint32 mask; gboolean success; GError *error = NULL; success = mm_mbm_parse_cfun_test (cfun_tests[i].str, NULL, &mask, &error); g_assert_no_error (error); g_assert (success); g_assert_cmpuint (mask, ==, cfun_tests[i].expected_mask); } } /*****************************************************************************/ typedef struct { const gchar *str; MMModemPowerState state; } CfunQueryPowerStateTest; static const CfunQueryPowerStateTest cfun_query_power_state_tests[] = { { "+CFUN: 0", MM_MODEM_POWER_STATE_OFF }, { "+CFUN: 1", MM_MODEM_POWER_STATE_ON }, { "+CFUN: 4", MM_MODEM_POWER_STATE_LOW }, { "+CFUN: 5", MM_MODEM_POWER_STATE_ON }, { "+CFUN: 6", MM_MODEM_POWER_STATE_ON }, }; static void test_cfun_query_power_state (void) { guint i; for (i = 0; i < G_N_ELEMENTS (cfun_query_power_state_tests); i++) { GError *error = NULL; gboolean success; MMModemPowerState state; success = mm_mbm_parse_cfun_query_power_state (cfun_query_power_state_tests[i].str, &state, &error); g_assert_no_error (error); g_assert (success); g_assert_cmpuint (cfun_query_power_state_tests[i].state, ==, state); } } typedef struct { const gchar *str; MMModemMode allowed; gint mbm_mode; } CfunQueryCurrentModeTest; static const CfunQueryCurrentModeTest cfun_query_current_mode_tests[] = { { "+CFUN: 0", MM_MODEM_MODE_NONE, -1 }, { "+CFUN: 1", MM_MODEM_MODE_2G | MM_MODEM_MODE_3G, -1 }, { "+CFUN: 4", MM_MODEM_MODE_NONE, -1 }, { "+CFUN: 5", MM_MODEM_MODE_2G, MBM_NETWORK_MODE_2G }, { "+CFUN: 6", MM_MODEM_MODE_3G, MBM_NETWORK_MODE_3G }, }; static void test_cfun_query_current_modes (void) { guint i; for (i = 0; i < G_N_ELEMENTS (cfun_query_current_mode_tests); i++) { GError *error = NULL; gboolean success; MMModemMode allowed = MM_MODEM_MODE_NONE; gint mbm_mode = -1; success = mm_mbm_parse_cfun_query_current_modes (cfun_query_current_mode_tests[i].str, &allowed, &mbm_mode, &error); g_assert_no_error (error); g_assert (success); g_assert_cmpuint (cfun_query_current_mode_tests[i].allowed, ==, allowed); g_assert_cmpint (cfun_query_current_mode_tests[i].mbm_mode, ==, mbm_mode); } } /*****************************************************************************/ int main (int argc, char **argv) { setlocale (LC_ALL, ""); g_test_init (&argc, &argv, NULL); g_test_add_func ("/MM/mbm/e2ipcfg", test_e2ipcfg); g_test_add_func ("/MM/mbm/cfun/test", test_cfun_test); g_test_add_func ("/MM/mbm/cfun/query/power-state", test_cfun_query_power_state); g_test_add_func ("/MM/mbm/cfun/query/current-modes", test_cfun_query_current_modes); return g_test_run (); } ModemManager-1.23.4-dev/src/plugins/meson.build000066400000000000000000001045231456466623000213440ustar00rootroot00000000000000# SPDX-License-Identifier: GPL-2.0-or-later # Copyright (C) 2021 Iñigo Martinez if not enable_builtin_plugins symbol_map = plugins_dir / 'symbol.map' ldflags = cc.get_supported_link_arguments('-Wl,--version-script,@0@'.format(symbol_map)) endif # common service test support plugins_common_test_dep = [] if enable_tests sources = files( 'tests/test-fixture.c', 'tests/test-helpers.c', 'tests/test-port-context.c', ) deps = [ libhelpers_dep, libmm_test_generated_dep ] libmm_test_common = library( 'mm-test-common', sources: sources, include_directories: top_inc, dependencies: deps + [gio_unix_dep], c_args: '-DTEST_SERVICES="@0@"'.format(build_root / 'data/tests'), ) libmm_test_common_dep = declare_dependency( include_directories: 'tests', dependencies: deps, link_with: libmm_test_common, ) plugins_common_test_dep += [ libmm_test_common_dep ] endif # plugins plugins = {} plugins_data = [] plugins_udev_rules = [] plugins_test_udev_rules_dir_c_args = [] plugins_test_keyfile_c_args = [] # never include static libs as deps when building # plugins or shared utils modules plugins_incs = [ top_inc, src_inc, kerneldevice_inc, plugins_inc, ] plugins_deps = [ daemon_enums_types_dep, libmm_glib_dep, ] if enable_mbim plugins_deps += mbim_glib_dep endif if enable_qmi plugins_deps += qmi_glib_dep endif # common Fibocom support library (MBIM only) if plugins_shared['fibocom'] fibocom_inc = include_directories('fibocom') c_args = '-DMM_MODULE_NAME="shared-fibocom"' sources = files( 'fibocom/mm-shared.c', 'fibocom/mm-shared-fibocom.c', ) plugins += {'shared-fibocom': { 'plugin': false, 'module': {'sources': sources, 'include_directories': plugins_incs, 'c_args': c_args}, }} endif # Common Foxconn modem support library (MBIM only) if plugins_shared['foxconn'] foxconn_inc = include_directories('foxconn') sources = files( 'foxconn/mm-broadband-modem-mbim-foxconn.c', 'foxconn/mm-shared.c', ) c_args = [ '-DMM_MODULE_NAME="shared-foxconn"', '-DPKGDATADIR="@0@"'.format(mm_pkgdatadir), ] plugins += {'shared-foxconn': { 'plugin': false, 'module': {'sources': sources, 'include_directories': plugins_incs, 'c_args': c_args}, }} endif # common icera support if plugins_shared['icera'] icera_inc = include_directories('icera') common_c_args = '-DMM_MODULE_NAME="shared-icera"' sources = files( 'icera/mm-broadband-bearer-icera.c', 'icera/mm-broadband-modem-icera.c', 'icera/mm-shared.c', ) plugins += {'shared-icera': { 'plugin': false, 'helper': {'sources': files('icera/mm-modem-helpers-icera.c'), 'include_directories': plugins_incs, 'c_args': common_c_args}, 'module': {'sources': sources, 'include_directories': plugins_incs, 'c_args': common_c_args}, 'test': {'sources': files('icera/tests/test-modem-helpers-icera.c'), 'include_directories': plugins_incs + [icera_inc], 'dependencies': libhelpers_dep}, }} endif # common novatel support if plugins_shared['novatel'] novatel_inc = include_directories('novatel') sources = files( 'novatel/mm-broadband-modem-novatel.c', 'novatel/mm-common-novatel.c', 'novatel/mm-shared.c', ) plugins += {'shared-novatel': { 'plugin': false, 'module': {'sources': sources, 'include_directories': plugins_incs, 'c_args': '-DMM_MODULE_NAME="shared-novatel"'}, }} endif # common option support if plugins_shared['option'] sources = files( 'option/mm-broadband-modem-option.c', 'option/mm-shared.c', 'option/mm-shared-option.c', 'option/mm-sim-option.c', ) plugins += {'shared-option': { 'plugin': false, 'module': {'sources': sources, 'include_directories': plugins_incs, 'c_args': '-DMM_MODULE_NAME="shared-option"'}, }} endif # common sierra support if plugins_shared['sierra'] sierra_inc = include_directories('sierra') common_c_args = '-DMM_MODULE_NAME="shared-sierra"' sources = files( 'sierra/mm-broadband-bearer-sierra.c', 'sierra/mm-broadband-modem-sierra.c', 'sierra/mm-common-sierra.c', 'sierra/mm-shared.c', 'sierra/mm-sim-sierra.c', ) plugins += {'shared-sierra': { 'plugin': false, 'helper': {'sources': files('sierra/mm-modem-helpers-sierra.c'), 'include_directories': plugins_incs, 'c_args': common_c_args}, 'module': {'sources': sources, 'include_directories': plugins_incs, 'c_args': common_c_args}, 'test': {'sources': files('sierra/tests/test-modem-helpers-sierra.c'), 'include_directories': sierra_inc, 'dependencies': libhelpers_dep}, }} endif # common telit support if plugins_shared['telit'] telit_inc = include_directories('telit') common_c_args = '-DMM_MODULE_NAME="shared-telit"' headers = files('telit/mm-modem-helpers-telit.h') sources = files( 'telit/mm-broadband-modem-telit.c', 'telit/mm-common-telit.c', 'telit/mm-shared.c', 'telit/mm-shared-telit.c', ) enums_types = 'mm-telit-enums-types' sources += custom_target( enums_types + '.c', input: headers, output: enums_types + '.c', command: [ python, mm_mkenums, '--fhead', '#include "mm-telit-enums-types.h"\n', '--template', files(templates_dir / enums_types + '.c.template'), '@INPUT@'], capture: true, ) sources += custom_target( enums_types + '.h', input: headers, output: enums_types + '.h', command: [ python, mm_mkenums, '--fhead', '#include "mm-modem-helpers-telit.h"\n#ifndef __MM_TELIT_ENUMS_TYPES_H__\n#define __MM_TELIT_ENUMS_TYPES_H__\n', '--template', files(templates_dir / enums_types + '.h.template'), '--ftail', '#endif /* __MM_TELIT_ENUMS_TYPES_H__ */\n', '@INPUT@'], capture: true, ) if enable_mbim sources += files('telit/mm-broadband-modem-mbim-telit.c') endif plugins += {'shared-telit': { 'plugin': false, 'helper': {'sources': files('telit/mm-modem-helpers-telit.c'), 'include_directories': plugins_incs, 'c_args': common_c_args}, 'module': {'sources': sources, 'include_directories': plugins_incs + [telit_inc], 'c_args': common_c_args}, 'test': {'sources': files('telit/tests/test-mm-modem-helpers-telit.c'), 'include_directories': telit_inc, 'dependencies': plugins_common_test_dep}, }} endif # common xmm support if plugins_shared['xmm'] xmm_inc = include_directories('xmm') common_c_args = '-DMM_MODULE_NAME="shared-xmm"' sources = files( 'xmm/mm-broadband-modem-xmm.c', 'xmm/mm-shared.c', 'xmm/mm-shared-xmm.c', ) if enable_mbim sources += files('xmm/mm-broadband-modem-mbim-xmm.c') endif plugins += {'shared-xmm': { 'plugin': false, 'helper': {'sources': files('xmm/mm-modem-helpers-xmm.c'), 'include_directories': plugins_incs, 'c_args': common_c_args}, 'module': {'sources': sources, 'include_directories': plugins_incs, 'c_args': common_c_args}, 'test': {'sources': files('xmm/tests/test-modem-helpers-xmm.c'), 'include_directories': xmm_inc, 'dependencies': libhelpers_dep}, }} endif # plugin: altair lte if plugins_options['altair-lte'] common_c_args = '-DMM_MODULE_NAME="altair-lte"' sources = files( 'altair/mm-broadband-bearer-altair-lte.c', 'altair/mm-broadband-modem-altair-lte.c', 'altair/mm-plugin-altair-lte.c', ) plugins += {'plugin-altair-lte': { 'plugin': true, 'helper': {'sources': files('altair/mm-modem-helpers-altair-lte.c'), 'include_directories': plugins_incs, 'c_args': common_c_args}, 'module': {'sources': sources, 'include_directories': plugins_incs, 'c_args': common_c_args}, 'test': {'sources': files('altair/tests/test-modem-helpers-altair-lte.c'), 'include_directories': include_directories('altair'), 'dependencies': libhelpers_dep}, }} endif # plugin: anydata if plugins_options['anydata'] sources = files( 'anydata/mm-broadband-modem-anydata.c', 'anydata/mm-plugin-anydata.c', ) plugins += {'plugin-anydata': { 'plugin': true, 'module': {'sources': sources, 'include_directories': plugins_incs, 'c_args': '-DMM_MODULE_NAME="anydata"'}, }} endif # plugin: broadmobi if plugins_options['broadmobi'] test_udev_rules_dir_c_args = ['-DTESTUDEVRULESDIR_BROADMOBI="@0@"'.format(plugins_dir / 'broadmobi')] plugins_test_udev_rules_dir_c_args += test_udev_rules_dir_c_args plugins += {'plugin-broadmobi': { 'plugin': true, 'module': {'sources': files('broadmobi/mm-plugin-broadmobi.c'), 'include_directories': plugins_incs, 'c_args': test_udev_rules_dir_c_args + ['-DMM_MODULE_NAME="broadmobi"']}, }} plugins_udev_rules += files('broadmobi/77-mm-broadmobi-port-types.rules') endif # plugin: cinterion (previously siemens) if plugins_options['cinterion'] test_udev_rules_dir_c_args = ['-DTESTUDEVRULESDIR_CINTERION="@0@"'.format(plugins_dir / 'cinterion')] plugins_test_udev_rules_dir_c_args += test_udev_rules_dir_c_args common_c_args = test_udev_rules_dir_c_args + ['-DMM_MODULE_NAME="cinterion"'] sources = files( 'cinterion/mm-broadband-bearer-cinterion.c', 'cinterion/mm-broadband-modem-cinterion.c', 'cinterion/mm-plugin-cinterion.c', 'cinterion/mm-shared-cinterion.c', ) if enable_qmi sources += files('cinterion/mm-broadband-modem-qmi-cinterion.c') endif if enable_mbim sources += files('cinterion/mm-broadband-modem-mbim-cinterion.c') endif plugins += {'plugin-cinterion': { 'plugin': true, 'helper': {'sources': files('cinterion/mm-modem-helpers-cinterion.c'), 'include_directories': plugins_incs, 'c_args': common_c_args}, 'module': {'sources': sources, 'include_directories': plugins_incs, 'c_args': common_c_args}, 'test': {'sources': files('cinterion/tests/test-modem-helpers-cinterion.c'), 'include_directories': plugins_incs + [include_directories('cinterion')], 'dependencies': libport_dep}, }} plugins_udev_rules += files('cinterion/77-mm-cinterion-port-types.rules') endif # plugin: dell if plugins_options['dell'] test_udev_rules_dir_c_args = ['-DTESTUDEVRULESDIR_DELL="@0@"'.format(plugins_dir / 'dell')] plugins_test_udev_rules_dir_c_args += test_udev_rules_dir_c_args incs = plugins_incs + [ novatel_inc, sierra_inc, telit_inc, xmm_inc, ] if enable_mbim incs += [foxconn_inc] endif plugins += {'plugin-dell': { 'plugin': true, 'module': {'sources': files('dell/mm-plugin-dell.c'), 'include_directories': incs, 'c_args': test_udev_rules_dir_c_args + ['-DMM_MODULE_NAME="dell"']}, }} plugins_udev_rules += files('dell/77-mm-dell-port-types.rules') endif # plugin: dlink if plugins_options['dlink'] test_udev_rules_dir_c_args = ['-DTESTUDEVRULESDIR_DLINK="@0@"'.format(plugins_dir / 'dlink')] plugins_test_udev_rules_dir_c_args += test_udev_rules_dir_c_args plugins += {'plugin-dlink': { 'plugin': true, 'module': {'sources': files('dlink/mm-plugin-dlink.c'), 'include_directories': plugins_incs, 'c_args': test_udev_rules_dir_c_args + ['-DMM_MODULE_NAME="d-link"']}, }} plugins_udev_rules += files('dlink/77-mm-dlink-port-types.rules') endif # plugin: fibocom if plugins_options['fibocom'] test_udev_rules_dir_c_args = ['-DTESTUDEVRULESDIR_FIBOCOM="@0@"'.format(plugins_dir / 'fibocom')] plugins_test_udev_rules_dir_c_args += test_udev_rules_dir_c_args incs = plugins_incs + [xmm_inc] sources = files( 'fibocom/mm-broadband-bearer-fibocom-ecm.c', 'fibocom/mm-broadband-modem-fibocom.c', 'fibocom/mm-plugin-fibocom.c', ) if enable_mbim incs += [fibocom_inc] sources += files( 'fibocom/mm-broadband-modem-mbim-xmm-fibocom.c', 'fibocom/mm-broadband-modem-mbim-fibocom.c', ) endif plugins += {'plugin-fibocom': { 'plugin': true, 'module': {'sources': sources, 'include_directories': incs, 'c_args': test_udev_rules_dir_c_args + ['-DMM_MODULE_NAME="fibocom"']}, }} plugins_udev_rules += files('fibocom/77-mm-fibocom-port-types.rules') endif # plugin: foxconn if plugins_options['foxconn'] foxconn_dir = plugins_dir / 'foxconn' test_udev_rules_dir_c_args = ['-DTESTUDEVRULESDIR_FOXCONN="@0@"'.format(foxconn_dir)] plugins_test_udev_rules_dir_c_args += test_udev_rules_dir_c_args test_keyfile_c_args = ['-DTESTKEYFILE_FOXCONN_T77W968="@0@"'.format(foxconn_dir / 'mm-foxconn-t77w968-carrier-mapping.conf')] plugins_test_keyfile_c_args += test_keyfile_c_args plugins += {'plugin-foxconn': { 'plugin': true, 'module': {'sources': files('foxconn/mm-plugin-foxconn.c'), 'include_directories': plugins_incs, 'c_args': test_udev_rules_dir_c_args + test_keyfile_c_args + ['-DMM_MODULE_NAME="foxconn"']}, }} plugins_data += files( 'foxconn/mm-foxconn-t77w968-carrier-mapping.conf', ) plugins_udev_rules += files('foxconn/77-mm-foxconn-port-types.rules') endif # plugin: generic if plugins_options['generic'] plugins += {'plugin-generic': { 'plugin': true, 'module': {'sources': files('generic/mm-plugin-generic.c'), 'include_directories': plugins_incs, 'c_args': '-DMM_MODULE_NAME="generic"'}, 'test': {'sources': files('generic/tests/test-service-generic.c'), 'include_directories': include_directories('generic'), 'dependencies': plugins_common_test_dep, 'c_args': '-DCOMMON_GSM_PORT_CONF="@0@"'.format(plugins_dir / 'tests/gsm-port.conf')}, }} endif # plugin: gosuncn if plugins_options['gosuncn'] test_udev_rules_dir_c_args = ['-DTESTUDEVRULESDIR_GOSUNCN="@0@"'.format(plugins_dir / 'gosuncn')] plugins_test_udev_rules_dir_c_args += test_udev_rules_dir_c_args plugins += {'plugin-gosuncn': { 'plugin': true, 'module': {'sources': files('gosuncn/mm-plugin-gosuncn.c'), 'include_directories': plugins_incs, 'c_args': test_udev_rules_dir_c_args + ['-DMM_MODULE_NAME="gosuncn"']}, }} plugins_udev_rules += files('gosuncn/77-mm-gosuncn-port-types.rules') endif # plugin: haier if plugins_options['haier'] test_udev_rules_dir_c_args = ['-DTESTUDEVRULESDIR_HAIER="@0@"'.format(plugins_dir / 'haier')] plugins_test_udev_rules_dir_c_args += test_udev_rules_dir_c_args plugins += {'plugin-haier': { 'plugin': true, 'module': {'sources': files('haier/mm-plugin-haier.c'), 'include_directories': plugins_incs, 'c_args': test_udev_rules_dir_c_args + ['-DMM_MODULE_NAME="haier"']}, }} plugins_udev_rules += files('haier/77-mm-haier-port-types.rules') endif # plugin: huawei if plugins_options['huawei'] huawei_inc = include_directories('huawei') test_udev_rules_dir_c_args = ['-DTESTUDEVRULESDIR_HUAWEI="@0@"'.format(plugins_dir / 'huawei')] plugins_test_udev_rules_dir_c_args += test_udev_rules_dir_c_args common_c_args = test_udev_rules_dir_c_args + ['-DMM_MODULE_NAME="huawei"'] headers = files('huawei/mm-modem-helpers-huawei.h') sources = files( 'huawei/mm-broadband-bearer-huawei.c', 'huawei/mm-broadband-modem-huawei.c', 'huawei/mm-plugin-huawei.c', 'huawei/mm-sim-huawei.c', ) enums_types = 'mm-huawei-enums-types' enums_sources = [] enums_sources += custom_target( enums_types + '.c', input: headers, output: enums_types + '.c', command: [ python, mm_mkenums, '--fhead', '#include "mm-huawei-enums-types.h"\n', '--template', files(templates_dir / enums_types + '.c.template'), '@INPUT@'], capture: true, ) enums_sources += custom_target( enums_types + '.h', input: headers, output: enums_types + '.h', command: [ python, mm_mkenums, '--fhead', '#include "mm-modem-helpers-huawei.h"\n#ifndef __MM_HUAWEI_ENUMS_TYPES_H__\n#define __MM_HUAWEI_ENUMS_TYPES_H__\n', '--template', files(templates_dir / enums_types + '.h.template'), '--ftail', '#endif /* __MM_HUAWEI_ENUMS_TYPES_H__ */\n', '@INPUT@'], capture: true, ) plugins += {'plugin-huawei': { 'plugin': true, 'helper': {'sources': files('huawei/mm-modem-helpers-huawei.c'), 'include_directories': plugins_incs + [huawei_inc], 'c_args': common_c_args}, 'module': {'sources': sources + enums_sources, 'include_directories': plugins_incs + [huawei_inc], 'c_args': common_c_args}, 'test': {'sources': files('huawei/tests/test-modem-helpers-huawei.c') + enums_sources, 'include_directories': huawei_inc, 'dependencies': libhelpers_dep}, }} plugins_udev_rules += files('huawei/77-mm-huawei-net-port-types.rules') endif # plugin: intel if plugins_options['intel'] sources = files( 'intel/mm-plugin-intel.c', ) if enable_mbim sources += files('intel/mm-broadband-modem-mbim-intel.c') endif common_c_args = '-DMM_MODULE_NAME="intel"' plugins += {'plugin-intel': { 'plugin': true, 'module': {'sources': sources, 'include_directories': plugins_incs + [xmm_inc], 'c_args': common_c_args}, }} endif # plugin: iridium if plugins_options['iridium'] sources = files( 'iridium/mm-bearer-iridium.c', 'iridium/mm-broadband-modem-iridium.c', 'iridium/mm-plugin-iridium.c', 'iridium/mm-sim-iridium.c', ) plugins += {'plugin-iridium': { 'plugin': true, 'module': {'sources': sources, 'include_directories': plugins_incs, 'c_args': '-DMM_MODULE_NAME="iridium"'}, }} endif # plugin: linktop if plugins_options['linktop'] test_udev_rules_dir_c_args = ['-DTESTUDEVRULESDIR_LINKTOP="@0@"'.format(plugins_dir / 'linktop')] plugins_test_udev_rules_dir_c_args += test_udev_rules_dir_c_args common_c_args = test_udev_rules_dir_c_args + ['-DMM_MODULE_NAME="linktop"'] sources = files( 'linktop/mm-plugin-linktop.c', 'linktop/mm-broadband-modem-linktop.c', ) plugins += {'plugin-linktop': { 'plugin': true, 'helper': {'sources': files('linktop/mm-modem-helpers-linktop.c'), 'include_directories': plugins_incs, 'c_args': common_c_args}, 'module': {'sources': sources, 'include_directories': plugins_incs, 'c_args': common_c_args}, 'test': {'sources': files('linktop/tests/test-modem-helpers-linktop.c'), 'include_directories': include_directories('linktop'), 'dependencies': libhelpers_dep}, }} plugins_udev_rules += files('linktop/77-mm-linktop-port-types.rules') endif # plugin: longcheer (and rebranded dongles) if plugins_options['longcheer'] test_udev_rules_dir_c_args = ['-DTESTUDEVRULESDIR_LONGCHEER="@0@"'.format(plugins_dir / 'longcheer')] plugins_test_udev_rules_dir_c_args += test_udev_rules_dir_c_args sources = files( 'longcheer/mm-broadband-modem-longcheer.c', 'longcheer/mm-plugin-longcheer.c', ) plugins += {'plugin-longcheer': { 'plugin': true, 'module': {'sources': sources, 'include_directories': plugins_incs, 'c_args': test_udev_rules_dir_c_args + ['-DMM_MODULE_NAME="longcheer"']}, }} plugins_udev_rules += files('longcheer/77-mm-longcheer-port-types.rules') endif # plugin: ericsson mbm if plugins_options['mbm'] test_udev_rules_dir_c_args = ['-DTESTUDEVRULESDIR_MBM="@0@"'.format(plugins_dir / 'mbm')] plugins_test_udev_rules_dir_c_args += test_udev_rules_dir_c_args common_c_args = test_udev_rules_dir_c_args + ['-DMM_MODULE_NAME="ericsson-mbm"'] sources = files( 'mbm/mm-broadband-bearer-mbm.c', 'mbm/mm-broadband-modem-mbm.c', 'mbm/mm-plugin-mbm.c', 'mbm/mm-sim-mbm.c', ) plugins += {'plugin-ericsson-mbm': { 'plugin': true, 'helper': {'sources': files('mbm/mm-modem-helpers-mbm.c'), 'include_directories': plugins_incs, 'c_args': common_c_args}, 'module': {'sources': sources, 'include_directories': plugins_incs, 'c_args': common_c_args}, 'test': {'sources': files('mbm/tests/test-modem-helpers-mbm.c'), 'include_directories': plugins_incs + [include_directories('mbm')], 'dependencies': libhelpers_dep}, }} plugins_udev_rules += files('mbm/77-mm-ericsson-mbm.rules') endif # plugin: motorola if plugins_options['motorola'] sources = files( 'motorola/mm-broadband-modem-motorola.c', 'motorola/mm-plugin-motorola.c', ) plugins += {'plugin-motorola': { 'plugin': true, 'module': {'sources': sources, 'include_directories': plugins_incs, 'c_args': '-DMM_MODULE_NAME="motorola"'}, }} endif # plugin: mtk-legacy if plugins_options['mtk-legacy'] test_udev_rules_dir_c_args = ['-DTESTUDEVRULESDIR_MTK="@0@"'.format(plugins_dir / 'mtk')] plugins_test_udev_rules_dir_c_args += test_udev_rules_dir_c_args sources = files( 'mtk/mm-broadband-modem-mtk-legacy.c', 'mtk/mm-plugin-mtk-legacy.c', ) plugins += {'plugin-mtk-legacy': { 'plugin': true, 'module': {'sources': sources, 'include_directories': plugins_incs, 'c_args': test_udev_rules_dir_c_args + ['-DMM_MODULE_NAME="mtk-legacy"']}, }} plugins_udev_rules += files('mtk/77-mm-mtk-legacy-port-types.rules') endif # plugin: mtk if plugins_options['mtk'] test_udev_rules_dir_c_args = ['-DTESTUDEVRULESDIR_MTK="@0@"'.format(plugins_dir / 'mtk')] plugins_test_udev_rules_dir_c_args += test_udev_rules_dir_c_args incs = plugins_incs sources = files( 'mtk/mm-plugin-mtk.c', ) if enable_mbim incs += [fibocom_inc] sources += files( 'mtk/mm-bearer-mbim-mtk-fibocom.c', 'mtk/mm-broadband-modem-mbim-mtk.c', 'mtk/mm-broadband-modem-mbim-mtk-fibocom.c' ) endif plugins += {'plugin-mtk': { 'plugin': true, 'module': {'sources': sources, 'include_directories': incs, 'c_args': test_udev_rules_dir_c_args + ['-DMM_MODULE_NAME="mtk"']}, }} plugins_udev_rules += files('mtk/77-mm-mtk-port-types.rules') endif # plugin: nokia if plugins_options['nokia'] sources = files( 'nokia/mm-broadband-modem-nokia.c', 'nokia/mm-plugin-nokia.c', 'nokia/mm-sim-nokia.c', ) plugins += {'plugin-nokia': { 'plugin': true, 'module': {'sources': sources, 'include_directories': plugins_incs, 'c_args': '-DMM_MODULE_NAME="nokia"'}, }} endif # plugin: nokia (icera) if plugins_options['nokia-icera'] test_udev_rules_dir_c_args = ['-DTESTUDEVRULESDIR_NOKIA_ICERA="@0@"'.format(plugins_dir / 'nokia')] plugins_test_udev_rules_dir_c_args += test_udev_rules_dir_c_args plugins += {'plugin-nokia-icera': { 'plugin': true, 'module': {'sources': files('nokia/mm-plugin-nokia-icera.c'), 'include_directories': plugins_incs + [icera_inc], 'c_args': test_udev_rules_dir_c_args + ['-DMM_MODULE_NAME="nokia-icera"']}, }} plugins_udev_rules += files('nokia/77-mm-nokia-port-types.rules') endif # plugin: novatel non-lte if plugins_options['novatel'] plugins += {'plugin-novatel': { 'plugin': true, 'module': {'sources': files('novatel/mm-plugin-novatel.c'), 'include_directories': plugins_incs, 'c_args': '-DMM_MODULE_NAME="novatel"'}, }} endif # plugin: novatel lte if plugins_options['novatel-lte'] sources = files( 'novatel/mm-plugin-novatel-lte.c', 'novatel/mm-broadband-modem-novatel-lte.c', 'novatel/mm-broadband-bearer-novatel-lte.c', 'novatel/mm-sim-novatel-lte.c', ) plugins += {'plugin-novatel-lte': { 'plugin': true, 'module': {'sources': sources, 'include_directories': plugins_incs, 'c_args': '-DMM_MODULE_NAME="novatel-lte"'}, }} endif # plugin: option if plugins_options['option'] plugins += {'plugin-option': { 'plugin': true, 'module': {'sources': files('option/mm-plugin-option.c'), 'include_directories': plugins_incs, 'c_args': '-DMM_MODULE_NAME="option"'}, }} endif # plugin: option hso if plugins_options['option-hso'] sources = files( 'option/mm-plugin-hso.c', 'option/mm-broadband-bearer-hso.c', 'option/mm-broadband-modem-hso.c', ) plugins += {'plugin-option-hso': { 'plugin': true, 'module': {'sources': sources, 'include_directories': plugins_incs, 'c_args': '-DMM_MODULE_NAME="option-hso"'}, }} endif # plugin: pantech if plugins_options['pantech'] sources = files( 'pantech/mm-broadband-modem-pantech.c', 'pantech/mm-plugin-pantech.c', 'pantech/mm-sim-pantech.c', ) plugins += {'plugin-pantech': { 'plugin': true, 'module': {'sources': sources, 'include_directories': plugins_incs, 'c_args': '-DMM_MODULE_NAME="pantech"'}, }} endif # plugin: qcom-soc if plugins_options['qcom-soc'] test_udev_rules_dir_c_args = ['-DTESTUDEVRULESDIR_QCOM_SOC="@0@"'.format(plugins_dir / 'qcom-soc')] plugins_test_udev_rules_dir_c_args += test_udev_rules_dir_c_args sources = files( 'qcom-soc/mm-broadband-modem-qmi-qcom-soc.c', 'qcom-soc/mm-plugin-qcom-soc.c', ) plugins += {'plugin-qcom-soc': { 'plugin': true, 'module': {'sources': sources, 'include_directories': plugins_incs, 'c_args': test_udev_rules_dir_c_args + ['-DMM_MODULE_NAME="qcom-soc"']}, }} plugins_udev_rules += files('qcom-soc/77-mm-qcom-soc.rules') endif # plugin: quectel if plugins_options['quectel'] test_udev_rules_dir_c_args = ['-DTESTUDEVRULESDIR_QUECTEL="@0@"'.format(plugins_dir / 'quectel')] plugins_test_udev_rules_dir_c_args += test_udev_rules_dir_c_args common_c_args = test_udev_rules_dir_c_args + ['-DMM_MODULE_NAME="quectel"'] sources = files( 'quectel/mm-broadband-modem-quectel.c', 'quectel/mm-plugin-quectel.c', 'quectel/mm-shared-quectel.c', ) if enable_qmi sources += files('quectel/mm-broadband-modem-qmi-quectel.c') endif if enable_mbim sources += files('quectel/mm-broadband-modem-mbim-quectel.c') endif plugins += {'plugin-quectel': { 'plugin': true, 'helper': {'sources': files('quectel/mm-modem-helpers-quectel.c'), 'include_directories': plugins_incs, 'c_args': common_c_args}, 'module': {'sources': sources, 'include_directories': plugins_incs, 'c_args': common_c_args}, 'test': {'sources': files('quectel/tests/test-modem-helpers-quectel.c'), 'include_directories': include_directories('quectel'), 'dependencies': libhelpers_dep}, }} plugins_udev_rules += files('quectel/77-mm-quectel-port-types.rules') endif # plugin: samsung if plugins_options['samsung'] sources = files( 'samsung/mm-broadband-modem-samsung.c', 'samsung/mm-plugin-samsung.c', ) plugins += {'plugin-samsung': { 'plugin': true, 'module': {'sources': sources, 'include_directories': plugins_incs + [icera_inc], 'c_args': '-DMM_MODULE_NAME="samsung"'}, }} endif # plugin: sierra (legacy) if plugins_options['sierra-legacy'] sources = files( 'sierra/mm-broadband-modem-sierra-icera.c', 'sierra/mm-plugin-sierra-legacy.c', ) plugins += {'plugin-sierra-legacy': { 'plugin': true, 'module': {'sources': sources, 'include_directories': plugins_incs + [icera_inc], 'c_args': '-DMM_MODULE_NAME="sierra-legacy"'}, }} endif # plugin: sierra (new QMI or MBIM modems) if plugins_options['sierra'] plugins += {'plugin-sierra': { 'plugin': true, 'module': {'sources': files('sierra/mm-plugin-sierra.c'), 'include_directories': plugins_incs + [xmm_inc], 'c_args': '-DMM_MODULE_NAME="sierra"'}, }} plugins_udev_rules += files('sierra/77-mm-sierra.rules') endif # plugin: simtech if plugins_options['simtech'] test_udev_rules_dir_c_args = ['-DTESTUDEVRULESDIR_SIMTECH="@0@"'.format(plugins_dir / 'simtech')] plugins_test_udev_rules_dir_c_args += test_udev_rules_dir_c_args common_c_args = test_udev_rules_dir_c_args + ['-DMM_MODULE_NAME="simtech"'] sources = files( 'simtech/mm-broadband-modem-simtech.c', 'simtech/mm-plugin-simtech.c', 'simtech/mm-shared-simtech.c', ) if enable_qmi sources += files('simtech/mm-broadband-modem-qmi-simtech.c') endif plugins += {'plugin-simtech': { 'plugin': true, 'helper': {'sources': files('simtech/mm-modem-helpers-simtech.c'), 'include_directories': plugins_incs, 'c_args': common_c_args}, 'module': {'sources': sources, 'include_directories': plugins_incs, 'c_args': common_c_args}, 'test': {'sources': files('simtech/tests/test-modem-helpers-simtech.c'), 'include_directories': plugins_incs + [include_directories('simtech')], 'dependencies': libport_dep}, }} plugins_udev_rules += files('simtech/77-mm-simtech-port-types.rules') endif # plugin: telit if plugins_options['telit'] test_udev_rules_dir_c_args = ['-DTESTUDEVRULESDIR_TELIT="@0@"'.format(plugins_dir / 'telit')] plugins_test_udev_rules_dir_c_args += test_udev_rules_dir_c_args plugins += {'plugin-telit': { 'plugin': true, 'module': {'sources': files('telit/mm-plugin-telit.c'), 'include_directories': plugins_incs, 'c_args': test_udev_rules_dir_c_args + ['-DMM_MODULE_NAME="telit"']}, }} plugins_udev_rules += files('telit/77-mm-telit-port-types.rules') endif # plugin: thuraya xt if plugins_options['thuraya'] common_c_args = ['-DMM_MODULE_NAME="thuraya"'] sources = files( 'thuraya/mm-broadband-modem-thuraya.c', 'thuraya/mm-plugin-thuraya.c', ) plugins += {'plugin-thuraya': { 'plugin': true, 'helper': {'sources': files('thuraya/mm-modem-helpers-thuraya.c'), 'include_directories': plugins_incs, 'c_args': common_c_args}, 'module': {'sources': sources, 'include_directories': plugins_incs, 'c_args': common_c_args}, 'test': {'sources': files('thuraya/tests/test-mm-modem-helpers-thuraya.c'), 'include_directories': include_directories('thuraya'), 'dependencies': libhelpers_dep}, }} endif # plugin: tplink if plugins_options['tplink'] test_udev_rules_dir_c_args = ['-DTESTUDEVRULESDIR_TPLINK="@0@"'.format(plugins_dir / 'tplink')] plugins_test_udev_rules_dir_c_args += test_udev_rules_dir_c_args plugins += {'plugin-tplink': { 'plugin': true, 'module': {'sources': files('tplink/mm-plugin-tplink.c'), 'include_directories': plugins_incs, 'c_args': test_udev_rules_dir_c_args + ['-DMM_MODULE_NAME="tp-link"']}, }} plugins_udev_rules += files('tplink/77-mm-tplink-port-types.rules') endif # plugin: u-blox if plugins_options['ublox'] ublox_inc = include_directories('ublox') common_c_args = '-DMM_MODULE_NAME="u-blox"' headers = files('ublox/mm-modem-helpers-ublox.h') sources = files( 'ublox/mm-broadband-bearer-ublox.c', 'ublox/mm-broadband-modem-ublox.c', 'ublox/mm-plugin-ublox.c', 'ublox/mm-sim-ublox.c', ) enums_types = 'mm-ublox-enums-types' sources += custom_target( enums_types + '.c', input: headers, output: enums_types + '.c', command: [ python, mm_mkenums, '--fhead', '#include "mm-ublox-enums-types.h"\n', '--template', files(templates_dir / enums_types + '.c.template'), '@INPUT@'], capture: true, ) sources += custom_target( enums_types + '.h', input: headers, output: enums_types + '.h', command: [ python, mm_mkenums, '--fhead', '#include "mm-modem-helpers-ublox.h"\n#ifndef __MM_UBLOX_ENUMS_TYPES_H__\n#define __MM_UBLOX_ENUMS_TYPES_H__\n', '--template', files(templates_dir / enums_types + '.h.template'), '--ftail', '#endif /* __MM_UBLOX_ENUMS_TYPES_H__ */\n', '@INPUT@'], capture: true, ) plugins += {'plugin-ublox': { 'plugin': true, 'helper': {'sources': files('ublox/mm-modem-helpers-ublox.c'), 'include_directories': plugins_incs, 'c_args': common_c_args}, 'module': {'sources': sources, 'include_directories': plugins_incs + [ublox_inc], 'c_args': common_c_args}, 'test': {'sources': files('ublox/tests/test-modem-helpers-ublox.c'), 'include_directories': ublox_inc, 'dependencies': plugins_common_test_dep}, }} plugins_udev_rules += files('ublox/77-mm-ublox-port-types.rules') endif # plugin: via if plugins_options['via'] sources = files( 'via/mm-broadband-modem-via.c', 'via/mm-plugin-via.c', ) plugins += {'plugin-via': { 'plugin': true, 'module': {'sources': sources, 'include_directories': plugins_incs, 'c_args': '-DMM_MODULE_NAME="via"'}, }} endif # plugin: wavecom (now sierra airlink) if plugins_options['wavecom'] sources = files( 'wavecom/mm-broadband-modem-wavecom.c', 'wavecom/mm-plugin-wavecom.c', ) plugins += {'plugin-wavecom': { 'plugin': true, 'module': {'sources': sources, 'include_directories': plugins_incs, 'c_args': '-DMM_MODULE_NAME="wavecom"'}, }} endif # plugin: alcatel/TCT/JRD x220D and possibly others if plugins_options['x22x'] test_udev_rules_dir_c_args = ['-DTESTUDEVRULESDIR_X22X="@0@"'.format(plugins_dir / 'x22x')] plugins_test_udev_rules_dir_c_args += test_udev_rules_dir_c_args sources = files( 'x22x/mm-broadband-modem-x22x.c', 'x22x/mm-plugin-x22x.c', ) plugins += {'plugin-x22x': { 'plugin': true, 'module': {'sources': sources, 'include_directories': plugins_incs, 'c_args': test_udev_rules_dir_c_args + ['-DMM_MODULE_NAME="x22x"']}, }} plugins_udev_rules += files('x22x/77-mm-x22x-port-types.rules') endif # plugin: zte if plugins_options['zte'] test_udev_rules_dir_c_args = ['-DTESTUDEVRULESDIR_ZTE="@0@"'.format(plugins_dir / 'zte')] plugins_test_udev_rules_dir_c_args += test_udev_rules_dir_c_args sources = files( 'zte/mm-broadband-modem-zte.c', 'zte/mm-broadband-modem-zte-icera.c', 'zte/mm-common-zte.c', 'zte/mm-plugin-zte.c', ) plugins += {'plugin-zte': { 'plugin': true, 'module': {'sources': sources, 'include_directories': plugins_incs + [icera_inc], 'c_args': test_udev_rules_dir_c_args + ['-DMM_MODULE_NAME="zte"']}, }} plugins_udev_rules += files('zte/77-mm-zte-port-types.rules') endif builtin_sources = [] builtin_plugins = [] if enable_builtin_plugins builtin_sources += files('mm-builtin-plugins.c') endif foreach plugin_name, plugin_data: plugins libpluginhelpers = [] if plugin_data.has_key('helper') libpluginhelpers = static_library( 'helpers-' + plugin_name, dependencies: plugins_deps, kwargs: plugin_data['helper'], ) endif module_args = plugin_data['module'] if not enable_builtin_plugins if plugin_data['plugin'] module_args += { 'link_args': ldflags, 'link_depends': symbol_map, } endif shared_module( 'mm-' + plugin_name, dependencies: plugins_deps, link_with: libpluginhelpers, kwargs: module_args, install: true, install_dir: mm_pkglibdir, ) else libplugin = static_library( 'mm-' + plugin_name, dependencies: plugins_deps, link_with: libpluginhelpers, kwargs: module_args, ) builtin_plugins += libplugin endif if enable_tests if plugin_data.has_key('test') test_unit = 'test-' + plugin_name exe = executable( test_unit, link_with: libpluginhelpers, kwargs: plugin_data['test'], ) test(test_unit, exe) endif endif endforeach install_data( plugins_data, install_dir: mm_pkgdatadir, ) install_data( plugins_udev_rules, install_dir: udev_rulesdir, ) # udev-rules and keyfiles tests test_units = { 'udev-rules': {'include_directories': top_inc, 'dependencies': libkerneldevice_dep, 'c_args': plugins_test_udev_rules_dir_c_args}, 'keyfiles': {'include_directories': [top_inc, src_inc], 'dependencies': libmm_glib_dep, 'c_args': plugins_test_keyfile_c_args}, } foreach name, data: test_units test_name = 'test-' + name exe = executable( test_name, sources: 'tests/@0@.c'.format(test_name), kwargs: data, ) test(test_name, exe) endforeach ModemManager-1.23.4-dev/src/plugins/mm-builtin-plugins.c000066400000000000000000000157201456466623000231020ustar00rootroot00000000000000/* -*- 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) 2022 Google Inc. */ #include #include #include "mm-plugin.h" #include "mm-builtin-plugins.h" #if defined ENABLE_PLUGIN_ALTAIR_LTE MMPlugin *mm_plugin_create_altair_lte (void); #endif #if defined ENABLE_PLUGIN_ANYDATA MMPlugin *mm_plugin_create_anydata (void); #endif #if defined ENABLE_PLUGIN_BROADMOBI MMPlugin *mm_plugin_create_broadmobi (void); #endif #if defined ENABLE_PLUGIN_CINTERION MMPlugin *mm_plugin_create_cinterion (void); #endif #if defined ENABLE_PLUGIN_DELL MMPlugin *mm_plugin_create_dell (void); #endif #if defined ENABLE_PLUGIN_DLINK MMPlugin *mm_plugin_create_dlink (void); #endif #if defined ENABLE_PLUGIN_FIBOCOM MMPlugin *mm_plugin_create_fibocom (void); #endif #if defined ENABLE_PLUGIN_FOXCONN MMPlugin *mm_plugin_create_foxconn (void); #endif #if defined ENABLE_PLUGIN_GENERIC MMPlugin *mm_plugin_create_generic (void); #endif #if defined ENABLE_PLUGIN_GOSUNCN MMPlugin *mm_plugin_create_gosuncn (void); #endif #if defined ENABLE_PLUGIN_HAIER MMPlugin *mm_plugin_create_haier (void); #endif #if defined ENABLE_PLUGIN_HUAWEI MMPlugin *mm_plugin_create_huawei (void); #endif #if defined ENABLE_PLUGIN_INTEL MMPlugin *mm_plugin_create_intel (void); #endif #if defined ENABLE_PLUGIN_IRIDIUM MMPlugin *mm_plugin_create_iridium (void); #endif #if defined ENABLE_PLUGIN_LINKTOP MMPlugin *mm_plugin_create_linktop (void); #endif #if defined ENABLE_PLUGIN_LONGCHEER MMPlugin *mm_plugin_create_longcheer (void); #endif #if defined ENABLE_PLUGIN_MBM MMPlugin *mm_plugin_create_mbm (void); #endif #if defined ENABLE_PLUGIN_MOTOROLA MMPlugin *mm_plugin_create_motorola (void); #endif #if defined ENABLE_PLUGIN_MTK MMPlugin *mm_plugin_create_mtk (void); #endif #if defined ENABLE_PLUGIN_NOKIA MMPlugin *mm_plugin_create_nokia (void); #endif #if defined ENABLE_PLUGIN_NOKIA_ICERA MMPlugin *mm_plugin_create_nokia_icera (void); #endif #if defined ENABLE_PLUGIN_NOVATEL MMPlugin *mm_plugin_create_novatel (void); #endif #if defined ENABLE_PLUGIN_NOVATEL_LTE MMPlugin *mm_plugin_create_novatel_lte (void); #endif #if defined ENABLE_PLUGIN_OPTION MMPlugin *mm_plugin_create_option (void); #endif #if defined ENABLE_PLUGIN_OPTION_HSO MMPlugin *mm_plugin_create_hso (void); #endif #if defined ENABLE_PLUGIN_PANTECH MMPlugin *mm_plugin_create_pantech (void); #endif #if defined ENABLE_PLUGIN_QCOM_SOC MMPlugin *mm_plugin_create_qcom_soc (void); #endif #if defined ENABLE_PLUGIN_QUECTEL MMPlugin *mm_plugin_create_quectel (void); #endif #if defined ENABLE_PLUGIN_SAMSUNG MMPlugin *mm_plugin_create_samsung (void); #endif #if defined ENABLE_PLUGIN_SIERRA MMPlugin *mm_plugin_create_sierra (void); #endif #if defined ENABLE_PLUGIN_SIERRA_LEGACY MMPlugin *mm_plugin_create_sierra_legacy (void); #endif #if defined ENABLE_PLUGIN_SIMTECH MMPlugin *mm_plugin_create_simtech (void); #endif #if defined ENABLE_PLUGIN_TELIT MMPlugin *mm_plugin_create_telit (void); #endif #if defined ENABLE_PLUGIN_THURAYA MMPlugin *mm_plugin_create_thuraya (void); #endif #if defined ENABLE_PLUGIN_TPLINK MMPlugin *mm_plugin_create_tplink (void); #endif #if defined ENABLE_PLUGIN_UBLOX MMPlugin *mm_plugin_create_ublox (void); #endif #if defined ENABLE_PLUGIN_VIA MMPlugin *mm_plugin_create_via (void); #endif #if defined ENABLE_PLUGIN_WAVECOM MMPlugin *mm_plugin_create_wavecom (void); #endif #if defined ENABLE_PLUGIN_X22X MMPlugin *mm_plugin_create_x22x (void); #endif #if defined ENABLE_PLUGIN_ZTE MMPlugin *mm_plugin_create_zte (void); #endif GList * mm_builtin_plugins_load (void) { GList *builtin_plugins = NULL; #define PREPEND_PLUGIN(my_plugin) \ builtin_plugins = g_list_prepend (builtin_plugins, mm_plugin_create_##my_plugin ()) #if defined ENABLE_PLUGIN_ALTAIR_LTE PREPEND_PLUGIN (altair_lte); #endif #if defined ENABLE_PLUGIN_ANYDATA PREPEND_PLUGIN (anydata); #endif #if defined ENABLE_PLUGIN_BROADMOBI PREPEND_PLUGIN (broadmobi); #endif #if defined ENABLE_PLUGIN_CINTERION PREPEND_PLUGIN (cinterion); #endif #if defined ENABLE_PLUGIN_DELL PREPEND_PLUGIN (dell); #endif #if defined ENABLE_PLUGIN_DLINK PREPEND_PLUGIN (dlink); #endif #if defined ENABLE_PLUGIN_FIBOCOM PREPEND_PLUGIN (fibocom); #endif #if defined ENABLE_PLUGIN_FOXCONN PREPEND_PLUGIN (foxconn); #endif #if defined ENABLE_PLUGIN_GENERIC PREPEND_PLUGIN (generic); #endif #if defined ENABLE_PLUGIN_GOSUNCN PREPEND_PLUGIN (gosuncn); #endif #if defined ENABLE_PLUGIN_HAIER PREPEND_PLUGIN (haier); #endif #if defined ENABLE_PLUGIN_HUAWEI PREPEND_PLUGIN (huawei); #endif #if defined ENABLE_PLUGIN_INTEL PREPEND_PLUGIN (intel); #endif #if defined ENABLE_PLUGIN_IRIDIUM PREPEND_PLUGIN (iridium); #endif #if defined ENABLE_PLUGIN_LINKTOP PREPEND_PLUGIN (linktop); #endif #if defined ENABLE_PLUGIN_LONGCHEER PREPEND_PLUGIN (longcheer); #endif #if defined ENABLE_PLUGIN_MBM PREPEND_PLUGIN (mbm); #endif #if defined ENABLE_PLUGIN_MOTOROLA PREPEND_PLUGIN (motorola); #endif #if defined ENABLE_PLUGIN_MTK PREPEND_PLUGIN (mtk); #endif #if defined ENABLE_PLUGIN_NOKIA PREPEND_PLUGIN (nokia); #endif #if defined ENABLE_PLUGIN_NOKIA_ICERA PREPEND_PLUGIN (nokia_icera); #endif #if defined ENABLE_PLUGIN_NOVATEL PREPEND_PLUGIN (novatel); #endif #if defined ENABLE_PLUGIN_NOVATEL_LTE PREPEND_PLUGIN (novatel_lte); #endif #if defined ENABLE_PLUGIN_OPTION PREPEND_PLUGIN (option); #endif #if defined ENABLE_PLUGIN_OPTION_HSO PREPEND_PLUGIN (hso); #endif #if defined ENABLE_PLUGIN_PANTECH PREPEND_PLUGIN (pantech); #endif #if defined ENABLE_PLUGIN_QCOM_SOC PREPEND_PLUGIN (qcom_soc); #endif #if defined ENABLE_PLUGIN_QUECTEL PREPEND_PLUGIN (quectel); #endif #if defined ENABLE_PLUGIN_SAMSUNG PREPEND_PLUGIN (samsung); #endif #if defined ENABLE_PLUGIN_SIERRA PREPEND_PLUGIN (sierra); #endif #if defined ENABLE_PLUGIN_SIERRA_LEGACY PREPEND_PLUGIN (sierra_legacy); #endif #if defined ENABLE_PLUGIN_SIMTECH PREPEND_PLUGIN (simtech); #endif #if defined ENABLE_PLUGIN_TELIT PREPEND_PLUGIN (telit); #endif #if defined ENABLE_PLUGIN_THURAYA PREPEND_PLUGIN (thuraya); #endif #if defined ENABLE_PLUGIN_TPLINK PREPEND_PLUGIN (tplink); #endif #if defined ENABLE_PLUGIN_UBLOX PREPEND_PLUGIN (ublox); #endif #if defined ENABLE_PLUGIN_VIA PREPEND_PLUGIN (via); #endif #if defined ENABLE_PLUGIN_WAVECOM PREPEND_PLUGIN (wavecom); #endif #if defined ENABLE_PLUGIN_X22X PREPEND_PLUGIN (x22x); #endif #if defined ENABLE_PLUGIN_ZTE PREPEND_PLUGIN (zte); #endif #undef PREPEND_PLUGIN return builtin_plugins; } ModemManager-1.23.4-dev/src/plugins/mm-builtin-plugins.h000066400000000000000000000015711456466623000231060ustar00rootroot00000000000000/* -*- 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) 2022 Google Inc. */ #ifndef MM_BUILTIN_PLUGINS_H #define MM_BUILTIN_PLUGINS_H #include #include #if !defined WITH_BUILTIN_PLUGINS # error Build with builtin plugins was not enabled #endif GList *mm_builtin_plugins_load (void); #endif /* MM_BUILTIN_PLUGINS_H */ ModemManager-1.23.4-dev/src/plugins/mm-plugin-common.h000066400000000000000000000054441456466623000225500ustar00rootroot00000000000000/* -*- 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) 2022 Google, Inc. */ #ifndef MM_PLUGIN_COMMON_H #define MM_PLUGIN_COMMON_H #if !defined MM_MODULE_NAME # error MM_MODULE_NAME must be defined #endif #include #include #include #include "mm-plugin.h" #if defined (G_HAVE_GNUC_VISIBILITY) # define MM_VISIBILITY __attribute__((visibility("protected"))) #else # define MM_VISIBILITY #endif #if defined WITH_BUILTIN_PLUGINS # define MM_PLUGIN_VERSION # define MM_PLUGIN_NAMED_CREATOR_SCOPE # define MM_PLUGIN_CREATOR(unused) #else # define MM_PLUGIN_VERSION \ MM_VISIBILITY int mm_plugin_major_version = MM_PLUGIN_MAJOR_VERSION; \ MM_VISIBILITY int mm_plugin_minor_version = MM_PLUGIN_MINOR_VERSION; # define MM_PLUGIN_NAMED_CREATOR_SCOPE static # define MM_PLUGIN_CREATOR(my_plugin) \ G_MODULE_EXPORT MMPlugin *mm_plugin_create (void); \ G_MODULE_EXPORT MMPlugin * \ mm_plugin_create (void) \ { \ return mm_plugin_create_##my_plugin (); \ } #endif #define MM_DEFINE_PLUGIN(MY_PLUGIN, my_plugin, MyPlugin) \ G_DECLARE_FINAL_TYPE(MMPlugin##MyPlugin, mm_plugin_##my_plugin, MM, PLUGIN_##MY_PLUGIN, MMPlugin) \ struct _MMPlugin##MyPlugin { \ MMPlugin parent; \ }; \ G_DEFINE_TYPE (MMPlugin##MyPlugin, mm_plugin_##my_plugin, MM_TYPE_PLUGIN) \ \ MM_PLUGIN_VERSION \ \ MM_PLUGIN_NAMED_CREATOR_SCOPE MMPlugin *mm_plugin_create_##my_plugin (void); \ MM_PLUGIN_CREATOR(my_plugin) #endif /* MM_PLUGIN_COMMON_H */ ModemManager-1.23.4-dev/src/plugins/mm-shared-common.h000066400000000000000000000026431456466623000225160ustar00rootroot00000000000000/* -*- 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) 2019 Aleksander Morgado * Copyright (C) 2022 Google, Inc. */ #ifndef MM_SHARED_COMMON_H #define MM_SHARED_COMMON_H #if !defined MM_MODULE_NAME # error MM_MODULE_NAME must be defined #endif #include #include #include #include "mm-plugin.h" #if defined (G_HAVE_GNUC_VISIBILITY) #define MM_VISIBILITY __attribute__((visibility("protected"))) #else #define MM_VISIBILITY #endif #if defined WITH_BUILTIN_PLUGINS # define MM_DEFINE_SHARED(unused) #else # define MM_DEFINE_SHARED(MyShared) \ MM_VISIBILITY int mm_shared_major_version = MM_SHARED_MAJOR_VERSION; \ MM_VISIBILITY int mm_shared_minor_version = MM_SHARED_MINOR_VERSION; \ MM_VISIBILITY const char *mm_shared_name = #MyShared; #endif #endif /* MM_SHARED_COMMON_H */ ModemManager-1.23.4-dev/src/plugins/motorola/000077500000000000000000000000001456466623000210315ustar00rootroot00000000000000ModemManager-1.23.4-dev/src/plugins/motorola/mm-broadband-modem-motorola.c000066400000000000000000000064221456466623000264550ustar00rootroot00000000000000/* -*- 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 - 2011 Red Hat, Inc. * Copyright (C) 2012 Aleksander Morgado */ #include #include #include #include #include #include #include "ModemManager.h" #include "mm-iface-modem.h" #include "mm-iface-modem-3gpp.h" #include "mm-broadband-modem-motorola.h" static void iface_modem_init (MMIfaceModem *iface); static void iface_modem_3gpp_init (MMIfaceModem3gpp *iface); G_DEFINE_TYPE_EXTENDED (MMBroadbandModemMotorola, mm_broadband_modem_motorola, MM_TYPE_BROADBAND_MODEM, 0, G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM, iface_modem_init) G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_3GPP, iface_modem_3gpp_init)); /*****************************************************************************/ MMBroadbandModemMotorola * mm_broadband_modem_motorola_new (const gchar *device, const gchar *physdev, const gchar **drivers, const gchar *plugin, guint16 vendor_id, guint16 product_id) { return g_object_new (MM_TYPE_BROADBAND_MODEM_MOTOROLA, MM_BASE_MODEM_DEVICE, device, MM_BASE_MODEM_PHYSDEV, physdev, MM_BASE_MODEM_DRIVERS, drivers, MM_BASE_MODEM_PLUGIN, plugin, MM_BASE_MODEM_VENDOR_ID, vendor_id, MM_BASE_MODEM_PRODUCT_ID, product_id, /* Generic bearer supports TTY only */ MM_BASE_MODEM_DATA_NET_SUPPORTED, FALSE, MM_BASE_MODEM_DATA_TTY_SUPPORTED, TRUE, NULL); } static void mm_broadband_modem_motorola_init (MMBroadbandModemMotorola *self) { } static void iface_modem_3gpp_init (MMIfaceModem3gpp *iface) { /* Loading IMEI not supported */ iface->load_imei = NULL; iface->load_imei_finish = NULL; } static void iface_modem_init (MMIfaceModem *iface) { /* Loading IMEI with +CGSN is not supported, just assume we cannot load * equipment ID */ iface->load_equipment_identifier = NULL; iface->load_equipment_identifier_finish = NULL; /* These devices just don't implement AT+CFUN */ iface->load_power_state = NULL; iface->load_power_state_finish = NULL; iface->modem_power_up = NULL; iface->modem_power_up_finish = NULL; iface->modem_power_down = NULL; iface->modem_power_down_finish = NULL; } static void mm_broadband_modem_motorola_class_init (MMBroadbandModemMotorolaClass *klass) { } ModemManager-1.23.4-dev/src/plugins/motorola/mm-broadband-modem-motorola.h000066400000000000000000000047201456466623000264610ustar00rootroot00000000000000/* -*- 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 - 2012 Red Hat, Inc. * Copyright (C) 2012 Aleksander Morgado */ #ifndef MM_BROADBAND_MODEM_MOTOROLA_H #define MM_BROADBAND_MODEM_MOTOROLA_H #include "mm-broadband-modem.h" #define MM_TYPE_BROADBAND_MODEM_MOTOROLA (mm_broadband_modem_motorola_get_type ()) #define MM_BROADBAND_MODEM_MOTOROLA(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_BROADBAND_MODEM_MOTOROLA, MMBroadbandModemMotorola)) #define MM_BROADBAND_MODEM_MOTOROLA_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_BROADBAND_MODEM_MOTOROLA, MMBroadbandModemMotorolaClass)) #define MM_IS_BROADBAND_MODEM_MOTOROLA(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_BROADBAND_MODEM_MOTOROLA)) #define MM_IS_BROADBAND_MODEM_MOTOROLA_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_BROADBAND_MODEM_MOTOROLA)) #define MM_BROADBAND_MODEM_MOTOROLA_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_BROADBAND_MODEM_MOTOROLA, MMBroadbandModemMotorolaClass)) typedef struct _MMBroadbandModemMotorola MMBroadbandModemMotorola; typedef struct _MMBroadbandModemMotorolaClass MMBroadbandModemMotorolaClass; struct _MMBroadbandModemMotorola { MMBroadbandModem parent; }; struct _MMBroadbandModemMotorolaClass{ MMBroadbandModemClass parent; }; GType mm_broadband_modem_motorola_get_type (void); MMBroadbandModemMotorola *mm_broadband_modem_motorola_new (const gchar *device, const gchar *physdev, const gchar **drivers, const gchar *plugin, guint16 vendor_id, guint16 product_id); #endif /* MM_BROADBAND_MODEM_MOTOROLA_H */ ModemManager-1.23.4-dev/src/plugins/motorola/mm-plugin-motorola.c000066400000000000000000000055121456466623000247370ustar00rootroot00000000000000/* -*- 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 - 2012 Red Hat, Inc. * Copyright (C) 2012 Aleksander Morgado */ #include #include #define _LIBMM_INSIDE_MM #include #include "mm-private-boxed-types.h" #include "mm-plugin-common.h" #include "mm-broadband-modem-motorola.h" #define MM_TYPE_PLUGIN_MOTOROLA mm_plugin_motorola_get_type () MM_DEFINE_PLUGIN (MOTOROLA, motorola, Motorola) /*****************************************************************************/ static MMBaseModem * create_modem (MMPlugin *self, const gchar *uid, const gchar *physdev, const gchar **drivers, guint16 vendor, guint16 product, guint16 subsystem_vendor, GList *probes, GError **error) { return MM_BASE_MODEM (mm_broadband_modem_motorola_new (uid, physdev, drivers, mm_plugin_get_name (self), vendor, product)); } /*****************************************************************************/ MM_PLUGIN_NAMED_CREATOR_SCOPE MMPlugin * mm_plugin_create_motorola (void) { static const gchar *subsystems[] = { "tty", NULL }; static const mm_uint16_pair product_ids[] = { { 0x22b8, 0x3802 }, /* C330/C350L/C450/EZX GSM Phone */ { 0x22b8, 0x4902 }, /* Triplet GSM Phone */ { 0, 0 } }; return MM_PLUGIN ( g_object_new (MM_TYPE_PLUGIN_MOTOROLA, MM_PLUGIN_NAME, MM_MODULE_NAME, MM_PLUGIN_ALLOWED_SUBSYSTEMS, subsystems, MM_PLUGIN_ALLOWED_PRODUCT_IDS, product_ids, MM_PLUGIN_ALLOWED_AT, TRUE, NULL)); } static void mm_plugin_motorola_init (MMPluginMotorola *self) { } static void mm_plugin_motorola_class_init (MMPluginMotorolaClass *klass) { MMPluginClass *plugin_class = MM_PLUGIN_CLASS (klass); plugin_class->create_modem = create_modem; } ModemManager-1.23.4-dev/src/plugins/mtk/000077500000000000000000000000001456466623000177705ustar00rootroot00000000000000ModemManager-1.23.4-dev/src/plugins/mtk/77-mm-mtk-legacy-port-types.rules000066400000000000000000000070441456466623000260740ustar00rootroot00000000000000# do not edit this file, it will be overwritten on update ACTION!="add|change|move|bind", GOTO="mm_mtk_legacy_port_types_end" SUBSYSTEMS=="usb", ATTRS{idVendor}=="0e8d", GOTO="mm_mtk_port_types_vendorcheck" SUBSYSTEMS=="usb", ATTRS{idVendor}=="2001", GOTO="mm_dlink_port_types_vendorcheck" SUBSYSTEMS=="usb", ATTRS{idVendor}=="07d1", GOTO="mm_dlink_port_types_vendorcheck" GOTO="mm_mtk_legacy_port_types_end" # MediaTek devices --------------------------- LABEL="mm_mtk_port_types_vendorcheck" SUBSYSTEMS=="usb", ATTRS{bInterfaceNumber}=="?*", ENV{.MM_USBIFNUM}="$attr{bInterfaceNumber}" ATTRS{idVendor}=="0e8d", ATTRS{idProduct}=="00a1", ENV{.MM_USBIFNUM}=="00", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1" ATTRS{idVendor}=="0e8d", ATTRS{idProduct}=="00a1", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1" ATTRS{idVendor}=="0e8d", ATTRS{idProduct}=="00a1", ENV{ID_MM_MTK_TAGGED}="1" ATTRS{idVendor}=="0e8d", ATTRS{idProduct}=="00a2", ENV{.MM_USBIFNUM}=="00", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1" ATTRS{idVendor}=="0e8d", ATTRS{idProduct}=="00a2", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1" ATTRS{idVendor}=="0e8d", ATTRS{idProduct}=="00a2", ENV{ID_MM_MTK_TAGGED}="1" ATTRS{idVendor}=="0e8d", ATTRS{idProduct}=="00a4", ENV{.MM_USBIFNUM}=="00", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1" ATTRS{idVendor}=="0e8d", ATTRS{idProduct}=="00a4", ENV{.MM_USBIFNUM}=="02", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1" ATTRS{idVendor}=="0e8d", ATTRS{idProduct}=="00a4", ENV{ID_MM_MTK_TAGGED}="1" ATTRS{idVendor}=="0e8d", ATTRS{idProduct}=="00a5", ENV{.MM_USBIFNUM}=="02", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1" ATTRS{idVendor}=="0e8d", ATTRS{idProduct}=="00a5", ENV{.MM_USBIFNUM}=="03", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1" ATTRS{idVendor}=="0e8d", ATTRS{idProduct}=="00a5", ENV{ID_MM_MTK_TAGGED}="1" ATTRS{idVendor}=="0e8d", ATTRS{idProduct}=="00a7", ENV{.MM_USBIFNUM}=="02", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1" ATTRS{idVendor}=="0e8d", ATTRS{idProduct}=="00a7", ENV{.MM_USBIFNUM}=="03", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1" ATTRS{idVendor}=="0e8d", ATTRS{idProduct}=="00a7", ENV{ID_MM_MTK_TAGGED}="1" ATTRS{idVendor}=="0e8d", ATTRS{idProduct}=="0023", ENV{.MM_USBIFNUM}=="02", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1" ATTRS{idVendor}=="0e8d", ATTRS{idProduct}=="0023", ENV{.MM_USBIFNUM}=="03", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1" ATTRS{idVendor}=="0e8d", ATTRS{idProduct}=="0023", ENV{ID_MM_MTK_TAGGED}="1" GOTO="mm_mtk_legacy_port_types_end" # D-Link devices --------------------------- LABEL="mm_dlink_port_types_vendorcheck" SUBSYSTEMS=="usb", ATTRS{bInterfaceNumber}=="?*", ENV{.MM_USBIFNUM}="$attr{bInterfaceNumber}" # D-Link DWM-156 A3 ATTRS{idVendor}=="07d1", ATTRS{idProduct}=="7e11", ENV{.MM_USBIFNUM}=="02", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1" ATTRS{idVendor}=="07d1", ATTRS{idProduct}=="7e11", ENV{.MM_USBIFNUM}=="03", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1" ATTRS{idVendor}=="07d1", ATTRS{idProduct}=="7e11", ENV{ID_MM_MTK_TAGGED}="1" # D-Link DWM-156 A5 (and later?) ATTRS{idVendor}=="2001", ATTRS{idProduct}=="7d00", ENV{.MM_USBIFNUM}=="00", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1" ATTRS{idVendor}=="2001", ATTRS{idProduct}=="7d00", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1" ATTRS{idVendor}=="2001", ATTRS{idProduct}=="7d00", ENV{ID_MM_MTK_TAGGED}="1" LABEL="mm_mtk_legacy_port_types_end" ModemManager-1.23.4-dev/src/plugins/mtk/77-mm-mtk-port-types.rules000066400000000000000000000011101456466623000246160ustar00rootroot00000000000000# do not edit this file, it will be overwritten on update ACTION!="add|change|move|bind", GOTO="mm_mtk_port_types_end" SUBSYSTEMS=="pci", SUBSYSTEM=="wwan", ATTRS{vendor}=="0x14c3", GOTO="mm_mtk_port_types" GOTO="mm_mtk_port_types_end" LABEL="mm_mtk_port_types" # Fibocom FM350 attach APN with toggle modem power ATTRS{vendor}=="0x14c3", ATTRS{device}=="0x4d75", ENV{ID_MM_FIBOCOM_INITIAL_EPS_OFF_ON}="1" # Fibocom FM350 doesn't correctly support multiplexing yet ATTRS{vendor}=="0x14c3", ATTRS{device}=="0x4d75", ENV{ID_MM_MAX_MULTIPLEXED_LINKS}="0" LABEL="mm_mtk_port_types_end"ModemManager-1.23.4-dev/src/plugins/mtk/mm-bearer-mbim-mtk-fibocom.c000066400000000000000000000167321456466623000251430ustar00rootroot00000000000000/* -*- 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) 2024 Google, Inc. */ #include #include #define _LIBMM_INSIDE_MM #include #include "mm-log-object.h" #include "mm-bearer-mbim-mtk-fibocom.h" G_DEFINE_TYPE (MMBearerMbimMtkFibocom, mm_bearer_mbim_mtk_fibocom, MM_TYPE_BEARER_MBIM) struct _MMBearerMbimMtkFibocomPrivate { /* Whether IP packet filters need to be removed */ gboolean remove_ip_packet_filters; gboolean reload_stats_unsupported; }; /*****************************************************************************/ /* Stats */ typedef struct { guint64 rx_bytes; guint64 tx_bytes; } ReloadStatsResult; static gboolean reload_stats_finish (MMBaseBearer *_self, guint64 *rx_bytes, guint64 *tx_bytes, GAsyncResult *res, GError **error) { MMBearerMbimMtkFibocom *self = MM_BEARER_MBIM_MTK_FIBOCOM (_self); g_autofree ReloadStatsResult *stats = NULL; g_autoptr(GError) inner_error = NULL; stats = g_task_propagate_pointer (G_TASK (res), &inner_error); if (!stats) { /* If filters need to be removed on every stats query, we must never * return an error, otherwise the upper layers will stop the stats reloading * logic. Only return an error when filters are not being removed. */ if (!self->priv->remove_ip_packet_filters) { g_propagate_error (error, inner_error); return FALSE; } /* Flag as stats reloading being unsupported, we will avoid querying. */ self->priv->reload_stats_unsupported = TRUE; } if (rx_bytes) *rx_bytes = stats ? stats->rx_bytes : 0; if (tx_bytes) *tx_bytes = stats ? stats->tx_bytes : 0; return TRUE; } static void parent_reload_stats_ready (MMBaseBearer *self, GAsyncResult *res, GTask *task) { g_autofree ReloadStatsResult *stats = NULL; GError *error = NULL; stats = g_new0 (ReloadStatsResult, 1); if (!MM_BASE_BEARER_CLASS (mm_bearer_mbim_mtk_fibocom_parent_class)->reload_stats_finish ( self, &stats->rx_bytes, &stats->tx_bytes, res, &error)) g_task_return_error (task, g_steal_pointer (&error)); else g_task_return_pointer (task, g_steal_pointer (&stats), g_free); g_object_unref (task); } static void packet_statistics_query (GTask *task) { MMBearerMbimMtkFibocom *self; self = g_task_get_source_object (task); if (self->priv->reload_stats_unsupported) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Skipping stats reloading"); g_object_unref (task); return; } /* Chain up parent's stats query */ MM_BASE_BEARER_CLASS (mm_bearer_mbim_mtk_fibocom_parent_class)->reload_stats ( MM_BASE_BEARER (self), (GAsyncReadyCallback) parent_reload_stats_ready, task); } static void packet_filters_set_ready (MbimDevice *device, GAsyncResult *res, GTask *task) { MMBearerMbim *self; g_autoptr(GError) error = NULL; g_autoptr(MbimMessage) response = NULL; self = g_task_get_source_object (task); response = mbim_device_command_finish (device, res, &error); if (!response || !mbim_message_response_get_result (response, MBIM_MESSAGE_TYPE_COMMAND_DONE, &error)) mm_obj_dbg (self, "Couldn't reset IP packet filters: %s", error->message); packet_statistics_query (task); } static void ensure_removed_filters (GTask *task) { MMBearerMbimMtkFibocom *self; MMPortMbim *port; g_autoptr(MbimMessage) message = NULL; g_autoptr(MMBaseModem) modem = NULL; guint32 session_id; self = g_task_get_source_object (task); g_object_get (self, MM_BASE_BEARER_MODEM, &modem, NULL); port = mm_broadband_modem_mbim_peek_port_mbim (MM_BROADBAND_MODEM_MBIM (modem)); if (!port) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't peek MBIM port"); g_object_unref (task); return; } mm_obj_dbg (self, "Resetting IP packet filters..."); session_id = mm_bearer_mbim_get_session_id (MM_BEARER_MBIM (self)); message = mbim_message_ip_packet_filters_set_new (session_id, 0, NULL, NULL); mbim_device_command (mm_port_mbim_peek_device (port), message, 5, NULL, (GAsyncReadyCallback)packet_filters_set_ready, task); } static void reload_stats (MMBaseBearer *_self, GAsyncReadyCallback callback, gpointer user_data) { MMBearerMbimMtkFibocom *self = MM_BEARER_MBIM_MTK_FIBOCOM (_self); GTask *task; task = g_task_new (self, NULL, callback, user_data); if (self->priv->remove_ip_packet_filters) ensure_removed_filters (task); else packet_statistics_query (task); } /*****************************************************************************/ MMBaseBearer * mm_bearer_mbim_mtk_fibocom_new (MMBroadbandModemMbim *modem, gboolean is_async_slaac_supported, gboolean remove_ip_packet_filters, MMBearerProperties *config) { MMBearerMbimMtkFibocom *self; /* The Mbim bearer inherits from MMBaseBearer (so it's not a MMBroadbandBearer) * and that means that the object is not async-initable, so we just use * g_object_new() here */ self = g_object_new (MM_TYPE_BEARER_MBIM_MTK_FIBOCOM, MM_BASE_BEARER_MODEM, modem, MM_BASE_BEARER_CONFIG, config, MM_BEARER_MBIM_ASYNC_SLAAC, is_async_slaac_supported, NULL); self->priv->remove_ip_packet_filters = remove_ip_packet_filters; /* Only export valid bearers */ mm_base_bearer_export (MM_BASE_BEARER (self)); return MM_BASE_BEARER (self); } static void mm_bearer_mbim_mtk_fibocom_init (MMBearerMbimMtkFibocom *self) { /* Initialize private data */ self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, MM_TYPE_BEARER_MBIM_MTK_FIBOCOM, MMBearerMbimMtkFibocomPrivate); } static void mm_bearer_mbim_mtk_fibocom_class_init (MMBearerMbimMtkFibocomClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); MMBaseBearerClass *base_bearer_class = MM_BASE_BEARER_CLASS (klass); g_type_class_add_private (object_class, sizeof (MMBearerMbimMtkFibocomPrivate)); base_bearer_class->reload_stats = reload_stats; base_bearer_class->reload_stats_finish = reload_stats_finish; } ModemManager-1.23.4-dev/src/plugins/mtk/mm-bearer-mbim-mtk-fibocom.h000066400000000000000000000047531456466623000251500ustar00rootroot00000000000000/* -*- 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) 2024 Google, Inc. */ #ifndef MM_BEARER_MBIM_MTK_FIBOCOM_H #define MM_BEARER_MBIM_MTK_FIBOCOM_H #include #include #define _LIBMM_INSIDE_MM #include #include "mm-bearer-mbim.h" #define MM_TYPE_BEARER_MBIM_MTK_FIBOCOM (mm_bearer_mbim_mtk_fibocom_get_type ()) #define MM_BEARER_MBIM_MTK_FIBOCOM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_BEARER_MBIM_MTK_FIBOCOM, MMBearerMbimMtkFibocom)) #define MM_BEARER_MBIM_MTK_FIBOCOM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_BEARER_MBIM_MTK_FIBOCOM, MMBearerMbimMtkFibocomClass)) #define MM_IS_BEARER_MBIM_MTK_FIBOCOM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_BEARER_MBIM_MTK_FIBOCOM)) #define MM_IS_BEARER_MBIM_MTK_FIBOCOM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_BEARER_MBIM_MTK_FIBOCOM)) #define MM_BEARER_MBIM_MTK_FIBOCOM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_BEARER_MBIM_MTK_FIBOCOM, MMBearerMbimMtkFibocomClass)) typedef struct _MMBearerMbimMtkFibocom MMBearerMbimMtkFibocom; typedef struct _MMBearerMbimMtkFibocomClass MMBearerMbimMtkFibocomClass; typedef struct _MMBearerMbimMtkFibocomPrivate MMBearerMbimMtkFibocomPrivate; struct _MMBearerMbimMtkFibocom { MMBearerMbim parent; MMBearerMbimMtkFibocomPrivate *priv; }; struct _MMBearerMbimMtkFibocomClass { MMBearerMbimClass parent; }; GType mm_bearer_mbim_mtk_fibocom_get_type (void); G_DEFINE_AUTOPTR_CLEANUP_FUNC (MMBearerMbimMtkFibocom, g_object_unref) /* MBIM bearer creation implementation */ MMBaseBearer *mm_bearer_mbim_mtk_fibocom_new (MMBroadbandModemMbim *modem, gboolean is_async_slaac_supported, gboolean remove_ip_packet_filters, MMBearerProperties *config); #endif /* MM_BEARER_MBIM_MTK_FIBOCOM_H */ ModemManager-1.23.4-dev/src/plugins/mtk/mm-broadband-modem-mbim-mtk-fibocom.c000066400000000000000000000277731456466623000267250ustar00rootroot00000000000000/* -*- 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) 2023 Google, Inc. */ #include #include #include #include #include #include #include "ModemManager.h" #include "mm-log-object.h" #include "mm-iface-modem.h" #include "mm-iface-modem-3gpp.h" #include "mm-bearer-mbim-mtk-fibocom.h" #include "mm-broadband-modem-mbim-mtk-fibocom.h" #include "mm-shared-fibocom.h" static void iface_modem_init (MMIfaceModem *iface); static void iface_modem_3gpp_init (MMIfaceModem3gpp *iface); static void shared_fibocom_init (MMSharedFibocom *iface); static MMIfaceModem *iface_modem_parent; static MMIfaceModem3gpp *iface_modem_3gpp_parent; G_DEFINE_TYPE_EXTENDED (MMBroadbandModemMbimMtkFibocom, mm_broadband_modem_mbim_mtk_fibocom, MM_TYPE_BROADBAND_MODEM_MBIM_MTK, 0, G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM, iface_modem_init) G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_3GPP, iface_modem_3gpp_init) G_IMPLEMENT_INTERFACE (MM_TYPE_SHARED_FIBOCOM, shared_fibocom_init)) struct _MMBroadbandModemMbimMtkFibocomPrivate { /* Custom MTK/Fibocom bearer behavior */ gboolean is_multiplex_supported; gboolean is_async_slaac_supported; gboolean remove_ip_packet_filters; gboolean normalize_nw_error; }; /*****************************************************************************/ /* Asynchronous indications of IP configuration updates during the initial * modem-SLAAC operation with the network are not supported in old firmware * versions. */ #define ASYNC_SLAAC_SUPPORTED_VERSION 29, 23, 6 /* Multiple Multiplexed PDNs are not correctly supported in old firmware * versions. */ #define MULTIPLEX_SUPPORTED_VERSION 29, 23, 6 /* Explicit IP packet filter removal required in old firmware versions. */ #define IP_PACKET_FILTER_REMOVAL_UNNEEDED_VERSION 29, 23, 6 /* NW error normalization required in old firmware versions. */ #define NORMALIZE_NW_ERROR_UNNEEDED 29, 22, 13 static inline gboolean fm350_check_version (guint A1, guint A2, guint A3, guint B1, guint B2, guint B3) { return ((A1 > B1) || ((A1 == B1) && ((A2 > B2) || ((A2 == B2) && (A3 >= B3))))); } static void process_fm350_version_features (MMBroadbandModemMbimMtkFibocom *self, const gchar *revision) { g_auto(GStrv) split = NULL; guint major; guint minor; guint micro; /* Expected revision string is a multi-line value like this: * 81600.0000.00.MM.mm.uu_GC * F09 * For version comparison we care only about the "MM.mm.uu" part. */ split = g_strsplit_set (revision, "._", -1); if (!split || g_strv_length (split) < 6) { mm_obj_warn (self, "failed to process FM350 firmware version string"); return; } if (!mm_get_uint_from_str (split[3], &major) || !mm_get_uint_from_str (split[4], &minor) || !mm_get_uint_from_str (split[5], µ)) { mm_obj_warn (self, "failed to process FM350 firmware version string: %s.%s.%s", split[3], split[4], split[5]); return; } /* Check if async SLAAC is supported */ self->priv->is_async_slaac_supported = fm350_check_version (major, minor, micro, ASYNC_SLAAC_SUPPORTED_VERSION); mm_obj_info (self, "FM350 async SLAAC result indications are %ssupported", self->priv->is_async_slaac_supported ? "" : "not "); /* Check if multiplex is supported */ self->priv->is_multiplex_supported = fm350_check_version (major, minor, micro, MULTIPLEX_SUPPORTED_VERSION); mm_obj_info (self, "FM350 multiplexing is %ssupported", self->priv->is_multiplex_supported ? "" : "not "); /* Check if we need to remove IP packet filters */ self->priv->remove_ip_packet_filters = !fm350_check_version (major, minor, micro, IP_PACKET_FILTER_REMOVAL_UNNEEDED_VERSION); mm_obj_info (self, "FM350 %s IP packet filter removal", self->priv->remove_ip_packet_filters ? "requires" : "does not require"); /* Check if we need to normalize network errors */ self->priv->normalize_nw_error = !fm350_check_version (major, minor, micro, NORMALIZE_NW_ERROR_UNNEEDED); mm_obj_info (self, "FM350 %s network error normalization", self->priv->normalize_nw_error ? "requires" : "does not require"); } /*****************************************************************************/ /* Revision loading (Modem interface) */ static gchar * load_revision_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { return g_task_propagate_pointer (G_TASK (res), error); } static void parent_load_revision_ready (MMIfaceModem *self, GAsyncResult *res, GTask *task) { GError *error = NULL; gchar *revision; revision = iface_modem_parent->load_revision_finish (self, res, &error); if (!revision) { g_task_return_error (task, error); } else { process_fm350_version_features (MM_BROADBAND_MODEM_MBIM_MTK_FIBOCOM (self), revision); g_task_return_pointer (task, revision, g_free); } g_object_unref (task); } static void load_revision (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data) { g_assert (iface_modem_parent->load_revision); g_assert (iface_modem_parent->load_revision_finish); iface_modem_parent->load_revision (self, (GAsyncReadyCallback)parent_load_revision_ready, g_task_new (self, NULL, callback, user_data)); } /*****************************************************************************/ /* Create Bearer (Modem interface) */ static MMBaseBearer * create_bearer_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { return g_task_propagate_pointer (G_TASK (res), error); } static void create_bearer (MMIfaceModem *_self, MMBearerProperties *properties, GAsyncReadyCallback callback, gpointer user_data) { MMBroadbandModemMbimMtkFibocom *self = MM_BROADBAND_MODEM_MBIM_MTK_FIBOCOM (_self); MMBaseBearer *bearer; GTask *task; task = g_task_new (self, NULL, callback, user_data); mm_obj_dbg (self, "creating MTK Fibocom MBIM bearer (async SLAAC %s)", self->priv->is_async_slaac_supported ? "supported" : "unsupported"); bearer = mm_bearer_mbim_mtk_fibocom_new (MM_BROADBAND_MODEM_MBIM (self), self->priv->is_async_slaac_supported, self->priv->remove_ip_packet_filters, properties); g_task_return_pointer (task, bearer, g_object_unref); g_object_unref (task); } /*****************************************************************************/ /* Create Bearer List (Modem interface) */ static MMBearerList * create_bearer_list (MMIfaceModem *self) { MMBearerList *bearer_list; bearer_list = iface_modem_parent->create_bearer_list (self); if (!MM_BROADBAND_MODEM_MBIM_MTK_FIBOCOM (self)->priv->is_multiplex_supported) { g_object_set (bearer_list, MM_BEARER_LIST_MAX_ACTIVE_MULTIPLEXED_BEARERS, 0, NULL); mm_obj_dbg (self, "FM350 firmware version doesn't support multiplexed bearers"); } return bearer_list; } /******************************************************************************/ /* Normalize network error */ static guint32 normalize_nw_error (MMBroadbandModemMbim *self, guint32 nw_error) { /* Work around to convert AT error to 3GPP Error */ if (MM_BROADBAND_MODEM_MBIM_MTK_FIBOCOM (self)->priv->normalize_nw_error && nw_error > 100) { mm_obj_dbg (self, "network error normalization required: %u -> %u", nw_error, nw_error - 100); nw_error -= 100; } return nw_error; } /******************************************************************************/ MMBroadbandModemMbimMtkFibocom * mm_broadband_modem_mbim_mtk_fibocom_new (const gchar *device, const gchar *physdev, const gchar **drivers, const gchar *plugin, guint16 vendor_id, guint16 product_id) { return g_object_new (MM_TYPE_BROADBAND_MODEM_MBIM_MTK_FIBOCOM, MM_BASE_MODEM_DEVICE, device, MM_BASE_MODEM_PHYSDEV, physdev, MM_BASE_MODEM_DRIVERS, drivers, MM_BASE_MODEM_PLUGIN, plugin, MM_BASE_MODEM_VENDOR_ID, vendor_id, MM_BASE_MODEM_PRODUCT_ID, product_id, /* MBIM bearer supports NET only */ MM_BASE_MODEM_DATA_NET_SUPPORTED, TRUE, MM_BASE_MODEM_DATA_TTY_SUPPORTED, FALSE, MM_IFACE_MODEM_SIM_HOT_SWAP_SUPPORTED, TRUE, MM_IFACE_MODEM_PERIODIC_SIGNAL_CHECK_DISABLED, TRUE, #if defined WITH_QMI && QMI_MBIM_QMUX_SUPPORTED MM_BROADBAND_MODEM_MBIM_QMI_UNSUPPORTED, TRUE, #endif NULL); } static void mm_broadband_modem_mbim_mtk_fibocom_init (MMBroadbandModemMbimMtkFibocom *self) { self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, MM_TYPE_BROADBAND_MODEM_MBIM_MTK_FIBOCOM, MMBroadbandModemMbimMtkFibocomPrivate); /* By default remove, unless we have a new enough version */ self->priv->remove_ip_packet_filters = TRUE; /* By default normalize, unless have a new enough version */ self->priv->normalize_nw_error = TRUE; } static void iface_modem_init (MMIfaceModem *iface) { iface_modem_parent = g_type_interface_peek_parent (iface); iface->load_revision = load_revision; iface->load_revision_finish = load_revision_finish; iface->create_bearer = create_bearer; iface->create_bearer_finish = create_bearer_finish; iface->create_bearer_list = create_bearer_list; } static void iface_modem_3gpp_init (MMIfaceModem3gpp *iface) { iface_modem_3gpp_parent = g_type_interface_peek_parent (iface); iface->set_initial_eps_bearer_settings = mm_shared_fibocom_set_initial_eps_bearer_settings; iface->set_initial_eps_bearer_settings_finish = mm_shared_fibocom_set_initial_eps_bearer_settings_finish; } static MMIfaceModem3gpp * peek_parent_3gpp_interface (MMSharedFibocom *self) { return iface_modem_3gpp_parent; } static void shared_fibocom_init (MMSharedFibocom *iface) { iface->peek_parent_3gpp_interface = peek_parent_3gpp_interface; } static void mm_broadband_modem_mbim_mtk_fibocom_class_init (MMBroadbandModemMbimMtkFibocomClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); MMBroadbandModemMbimClass *broadband_modem_mbim_class = MM_BROADBAND_MODEM_MBIM_CLASS (klass); g_type_class_add_private (object_class, sizeof (MMBroadbandModemMbimMtkFibocomPrivate)); broadband_modem_mbim_class->normalize_nw_error = normalize_nw_error; } ModemManager-1.23.4-dev/src/plugins/mtk/mm-broadband-modem-mbim-mtk-fibocom.h000066400000000000000000000054361456466623000267220ustar00rootroot00000000000000/* -*- 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) 2023 Google, Inc. */ #ifndef MM_BROADBAND_MODEM_MBIM_MTK_FIBOCOM_H #define MM_BROADBAND_MODEM_MBIM_MTK_FIBOCOM_H #include "mm-broadband-modem-mbim-mtk.h" #define MM_TYPE_BROADBAND_MODEM_MBIM_MTK_FIBOCOM (mm_broadband_modem_mbim_mtk_fibocom_get_type ()) #define MM_BROADBAND_MODEM_MBIM_MTK_FIBOCOM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_BROADBAND_MODEM_MBIM_MTK_FIBOCOM, MMBroadbandModemMbimMtkFibocom)) #define MM_BROADBAND_MODEM_MBIM_MTK_FIBOCOM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_BROADBAND_MODEM_MBIM_MTK_FIBOCOM, MMBroadbandModemMbimMtkFibocomClass)) #define MM_IS_BROADBAND_MODEM_MBIM_MTK_FIBOCOM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_BROADBAND_MODEM_MBIM_MTK_FIBOCOM)) #define MM_IS_BROADBAND_MODEM_MBIM_MTK_FIBOCOM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_BROADBAND_MODEM_MBIM_MTK_FIBOCOM)) #define MM_BROADBAND_MODEM_MBIM_MTK_FIBOCOM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_BROADBAND_MODEM_MBIM_MTK_FIBOCOM, MMBroadbandModemMbimMtkFibocomClass)) typedef struct _MMBroadbandModemMbimMtkFibocom MMBroadbandModemMbimMtkFibocom; typedef struct _MMBroadbandModemMbimMtkFibocomClass MMBroadbandModemMbimMtkFibocomClass; typedef struct _MMBroadbandModemMbimMtkFibocomPrivate MMBroadbandModemMbimMtkFibocomPrivate; struct _MMBroadbandModemMbimMtkFibocom { MMBroadbandModemMbimMtk parent; MMBroadbandModemMbimMtkFibocomPrivate *priv; }; struct _MMBroadbandModemMbimMtkFibocomClass{ MMBroadbandModemMbimMtkClass parent; }; GType mm_broadband_modem_mbim_mtk_fibocom_get_type (void); MMBroadbandModemMbimMtkFibocom *mm_broadband_modem_mbim_mtk_fibocom_new (const gchar *device, const gchar *physdev, const gchar **drivers, const gchar *plugin, guint16 vendor_id, guint16 product_id); #endif /* MM_BROADBAND_MODEM_MBIM_MTK_FIBOCOM_H */ ModemManager-1.23.4-dev/src/plugins/mtk/mm-broadband-modem-mbim-mtk.c000066400000000000000000000047241456466623000253000ustar00rootroot00000000000000/* -*- 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) 2023 Google, Inc. */ #include #include #include #include #include #include #include "ModemManager.h" #include "mm-log-object.h" #include "mm-iface-modem.h" #include "mm-broadband-modem-mbim-mtk.h" G_DEFINE_TYPE (MMBroadbandModemMbimMtk, mm_broadband_modem_mbim_mtk, MM_TYPE_BROADBAND_MODEM_MBIM) /******************************************************************************/ MMBroadbandModemMbimMtk * mm_broadband_modem_mbim_mtk_new (const gchar *device, const gchar *physdev, const gchar **drivers, const gchar *plugin, guint16 vendor_id, guint16 product_id) { return g_object_new (MM_TYPE_BROADBAND_MODEM_MBIM_MTK, MM_BASE_MODEM_DEVICE, device, MM_BASE_MODEM_PHYSDEV, physdev, MM_BASE_MODEM_DRIVERS, drivers, MM_BASE_MODEM_PLUGIN, plugin, MM_BASE_MODEM_VENDOR_ID, vendor_id, MM_BASE_MODEM_PRODUCT_ID, product_id, /* MBIM bearer supports NET only */ MM_BASE_MODEM_DATA_NET_SUPPORTED, TRUE, MM_BASE_MODEM_DATA_TTY_SUPPORTED, FALSE, MM_IFACE_MODEM_SIM_HOT_SWAP_SUPPORTED, TRUE, MM_IFACE_MODEM_PERIODIC_SIGNAL_CHECK_DISABLED, TRUE, #if defined WITH_QMI && QMI_MBIM_QMUX_SUPPORTED MM_BROADBAND_MODEM_MBIM_QMI_UNSUPPORTED, TRUE, #endif NULL); } static void mm_broadband_modem_mbim_mtk_init (MMBroadbandModemMbimMtk *self) { } static void mm_broadband_modem_mbim_mtk_class_init (MMBroadbandModemMbimMtkClass *klass) { } ModemManager-1.23.4-dev/src/plugins/mtk/mm-broadband-modem-mbim-mtk.h000066400000000000000000000045551456466623000253070ustar00rootroot00000000000000/* -*- 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) 2023 Google, Inc. */ #ifndef MM_BROADBAND_MODEM_MBIM_MTK_H #define MM_BROADBAND_MODEM_MBIM_MTK_H #include "mm-broadband-modem-mbim.h" #define MM_TYPE_BROADBAND_MODEM_MBIM_MTK (mm_broadband_modem_mbim_mtk_get_type ()) #define MM_BROADBAND_MODEM_MBIM_MTK(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_BROADBAND_MODEM_MBIM_MTK, MMBroadbandModemMbimMtk)) #define MM_BROADBAND_MODEM_MBIM_MTK_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_BROADBAND_MODEM_MBIM_MTK, MMBroadbandModemMbimMtkClass)) #define MM_IS_BROADBAND_MODEM_MBIM_MTK(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_BROADBAND_MODEM_MBIM_MTK)) #define MM_IS_BROADBAND_MODEM_MBIM_MTK_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_BROADBAND_MODEM_MBIM_MTK)) #define MM_BROADBAND_MODEM_MBIM_MTK_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_BROADBAND_MODEM_MBIM_MTK, MMBroadbandModemMbimMtkClass)) typedef struct _MMBroadbandModemMbimMtk MMBroadbandModemMbimMtk; typedef struct _MMBroadbandModemMbimMtkClass MMBroadbandModemMbimMtkClass; struct _MMBroadbandModemMbimMtk { MMBroadbandModemMbim parent; }; struct _MMBroadbandModemMbimMtkClass{ MMBroadbandModemMbimClass parent; }; GType mm_broadband_modem_mbim_mtk_get_type (void); MMBroadbandModemMbimMtk *mm_broadband_modem_mbim_mtk_new (const gchar *device, const gchar *physdev, const gchar **drivers, const gchar *plugin, guint16 vendor_id, guint16 product_id); #endif /* MM_BROADBAND_MODEM_MBIM_MTK_H */ ModemManager-1.23.4-dev/src/plugins/mtk/mm-broadband-modem-mtk-legacy.c000066400000000000000000000761071456466623000256240ustar00rootroot00000000000000/* -*- 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 - 2012 Red Hat, Inc. * Copyright (C) 2012 Aleksander Morgado */ #include #include #include #include #include #include #include "ModemManager.h" #include "mm-log-object.h" #include "mm-errors-types.h" #include "mm-modem-helpers.h" #include "mm-base-modem-at.h" #include "mm-iface-modem.h" #include "mm-iface-modem-3gpp.h" #include "mm-broadband-modem-mtk-legacy.h" static void iface_modem_init (MMIfaceModem *iface); static void iface_modem_3gpp_init (MMIfaceModem3gpp *iface); static MMIfaceModem *iface_modem_parent; static MMIfaceModem3gpp *iface_modem_3gpp_parent; G_DEFINE_TYPE_EXTENDED (MMBroadbandModemMtkLegacy, mm_broadband_modem_mtk_legacy, MM_TYPE_BROADBAND_MODEM, 0, G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM, iface_modem_init) G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_3GPP, iface_modem_3gpp_init)); struct _MMBroadbandModemMtkLegacyPrivate { /* Signal quality regex */ GRegex *ecsqg_regex; GRegex *ecsqu_regex; GRegex *ecsqeg_regex; GRegex *ecsqeu_regex; GRegex *ecsqel_regex; }; /*****************************************************************************/ /* Unlock retries (Modem interface) */ static MMUnlockRetries * load_unlock_retries_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { return g_task_propagate_pointer (G_TASK (res), error); } static void load_unlock_retries_ready (MMBaseModem *self, GAsyncResult *res, GTask *task) { g_autoptr(GMatchInfo) match_info = NULL; g_autoptr(GRegex) r = NULL; const gchar *response; GError *error = NULL; GError *match_error = NULL; gint pin1; gint puk1; gint pin2; gint puk2; MMUnlockRetries *retries; response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error); if (!response) { g_task_return_error (task, error); g_object_unref (task); return; } r = g_regex_new ( "\\+EPINC:\\s*([0-9]+),\\s*([0-9]+),\\s*([0-9]+),\\s*([0-9]+)", 0, 0, NULL); g_assert (r != NULL); if (!g_regex_match_full (r, response, strlen (response), 0, 0, &match_info, &match_error)){ if (match_error) g_task_return_error (task, match_error); else g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Failed to match EPINC response: %s", response); g_task_return_error (task, error); } else if (!mm_get_int_from_match_info (match_info, 1, &pin1) || !mm_get_int_from_match_info (match_info, 2, &pin2) || !mm_get_int_from_match_info (match_info, 3, &puk1) || !mm_get_int_from_match_info (match_info, 4, &puk2)) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Failed to parse the EPINC response: '%s'", response); } else { retries = mm_unlock_retries_new (); mm_unlock_retries_set (retries, MM_MODEM_LOCK_SIM_PIN, pin1); mm_unlock_retries_set (retries, MM_MODEM_LOCK_SIM_PIN2, pin2); mm_unlock_retries_set (retries, MM_MODEM_LOCK_SIM_PUK, puk1); mm_unlock_retries_set (retries, MM_MODEM_LOCK_SIM_PUK2, puk2); g_task_return_pointer (task, retries, g_object_unref); } g_object_unref (task); } static void load_unlock_retries (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data) { mm_base_modem_at_command ( MM_BASE_MODEM (self), "+EPINC?", 3, FALSE, (GAsyncReadyCallback)load_unlock_retries_ready, g_task_new (self, NULL, callback, user_data)); } /*****************************************************************************/ static gboolean modem_after_sim_unlock_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static gboolean after_sim_unlock_wait_cb (GTask *task) { g_task_return_boolean (task, TRUE); g_object_unref (task); return G_SOURCE_REMOVE; } static void modem_after_sim_unlock (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; task = g_task_new (self, NULL, callback, user_data); /* For device, 3 second is OK for SIM get ready */ g_timeout_add_seconds (3, (GSourceFunc)after_sim_unlock_wait_cb, task); } /*****************************************************************************/ /* Load supported modes (Modem interface) */ static void get_supported_modes_ready (MMBaseModem *self, GAsyncResult *res, GTask *task) { g_autoptr(GMatchInfo) match_info = NULL; g_autoptr(GRegex) r = NULL; const gchar *response; GError *error = NULL; MMModemModeCombination mode; GArray *combinations; GError *match_error = NULL; gint device_type; response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error); if (!response) { g_task_return_error (task, error); g_object_unref (task); return; } r = g_regex_new ("\\+EGMR:\\s*\"MT([0-9]+)", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); g_assert (r != NULL); if (!g_regex_match_full (r, response, strlen (response), 0, 0, &match_info, &match_error)) { if (match_error) g_task_return_error (task, error); else g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Failed to match EGMR response: %s", response); g_object_unref (task); return; } if (!mm_get_int_from_match_info (match_info, 1, &device_type)) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Failed to parse the allowed mode response: '%s'", response); g_object_unref (task); return; } /* Build list of combinations */ combinations = g_array_sized_new (FALSE, FALSE, sizeof (MMModemModeCombination), 8); /* 2G only */ mode.allowed = MM_MODEM_MODE_2G; mode.preferred = MM_MODEM_MODE_NONE; g_array_append_val (combinations, mode); /* 3G only */ mode.allowed = MM_MODEM_MODE_3G; mode.preferred = MM_MODEM_MODE_NONE; g_array_append_val (combinations, mode); /* 2G and 3G, no prefer*/ mode.allowed = MM_MODEM_MODE_2G | MM_MODEM_MODE_3G; mode.preferred = MM_MODEM_MODE_NONE; g_array_append_val (combinations, mode); /* 2G and 3G, 3G prefer*/ mode.allowed = MM_MODEM_MODE_2G | MM_MODEM_MODE_3G; mode.preferred = MM_MODEM_MODE_3G; g_array_append_val (combinations, mode); if (device_type == 6290) { /* 4G only */ mode.allowed = MM_MODEM_MODE_4G; mode.preferred = MM_MODEM_MODE_NONE; g_array_append_val (combinations, mode); /* 2G and 4G, no prefer */ mode.allowed = MM_MODEM_MODE_2G | MM_MODEM_MODE_4G; mode.preferred = MM_MODEM_MODE_NONE; g_array_append_val (combinations, mode); /* 3G and 4G, no prefer */ mode.allowed = MM_MODEM_MODE_3G | MM_MODEM_MODE_4G; mode.preferred = MM_MODEM_MODE_NONE; g_array_append_val (combinations, mode); /* 2G, 3G and 4G, no prefer */ mode.allowed = MM_MODEM_MODE_2G | MM_MODEM_MODE_3G | MM_MODEM_MODE_4G; mode.preferred = MM_MODEM_MODE_NONE; g_array_append_val (combinations, mode); } /********************************************************************* * No need to filter out any unsupported modes for MTK device. For * +GCAP, +WS64 not support completely, generic filter will filter * out 4G modes. */ g_task_return_pointer (task, combinations, (GDestroyNotify)g_array_unref); g_object_unref (task); } static void load_supported_modes (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data) { mm_base_modem_at_command (MM_BASE_MODEM (self), "+EGMR=0,0", 3, FALSE, (GAsyncReadyCallback)get_supported_modes_ready, g_task_new (self, NULL, callback, user_data)); } static GArray * load_supported_modes_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { return g_task_propagate_pointer (G_TASK (res), error); } /*****************************************************************************/ /* Load initial allowed/preferred modes (Modem interface) */ static gboolean load_current_modes_finish (MMIfaceModem *self, GAsyncResult *res, MMModemMode *allowed, MMModemMode *preferred, GError **error) { g_autoptr(GMatchInfo) match_info = NULL; g_autoptr(GRegex) r = NULL; const gchar *response; gint erat_mode = -1; gint erat_pref = -1; GError *match_error = NULL; response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, error); if (!response) return FALSE; r = g_regex_new ( "\\+ERAT:\\s*[0-9]+,\\s*[0-9]+,\\s*([0-9]+),\\s*([0-9]+)", 0, 0, error); g_assert (r != NULL); if (!g_regex_match_full (r, response, strlen (response), 0, 0, &match_info, &match_error)) { if (match_error) g_propagate_error (error, match_error); else g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't parse +ERAT response: '%s'", response); return FALSE; } if (!mm_get_int_from_match_info (match_info, 1, &erat_mode) || !mm_get_int_from_match_info (match_info, 2, &erat_pref)) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Failed to parse the ERAT response: m=%d p=%d", erat_mode, erat_pref); return FALSE; } /* Correctly parsed! */ switch (erat_mode) { case 0: *allowed = MM_MODEM_MODE_2G; break; case 1: *allowed = MM_MODEM_MODE_3G; break; case 2: *allowed = MM_MODEM_MODE_2G | MM_MODEM_MODE_3G; break; case 3: *allowed = MM_MODEM_MODE_4G; break; case 4: *allowed = MM_MODEM_MODE_2G | MM_MODEM_MODE_4G; break; case 5: *allowed = MM_MODEM_MODE_3G | MM_MODEM_MODE_4G; break; case 6: *allowed = MM_MODEM_MODE_2G | MM_MODEM_MODE_3G | MM_MODEM_MODE_4G; break; default: mm_obj_dbg (self, "unsupported allowed mode reported in +ERAT: %d", erat_mode); return FALSE; } switch (erat_pref) { case 0: *preferred = MM_MODEM_MODE_NONE; break; case 1: *preferred = MM_MODEM_MODE_2G; break; case 2: *preferred = MM_MODEM_MODE_3G; break; case 3: *preferred = MM_MODEM_MODE_4G; break; default: mm_obj_dbg (self, "unsupported preferred mode %d", erat_pref); return FALSE; } return TRUE; } static void load_current_modes (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data) { mm_base_modem_at_command (MM_BASE_MODEM (self), "+ERAT?", 3, FALSE, callback, user_data); } /*****************************************************************************/ /* Set allowed modes (Modem interface) */ static gboolean set_current_modes_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void allowed_mode_update_ready (MMBroadbandModemMtkLegacy *self, GAsyncResult *res, GTask *task) { GError *error = NULL; mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error); if (error) /* Let the error be critical. */ g_task_return_error (task, error); else g_task_return_boolean (task, TRUE); g_object_unref (task); } static void set_current_modes (MMIfaceModem *self, MMModemMode allowed, MMModemMode preferred, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; gchar *command; gint erat_mode = -1; gint erat_pref = -1; task = g_task_new (self, NULL, callback, user_data); if (allowed == MM_MODEM_MODE_2G) { erat_mode = 0; erat_pref = 0; } else if (allowed == MM_MODEM_MODE_3G) { erat_mode = 1; erat_pref = 0; } else if (allowed == (MM_MODEM_MODE_2G | MM_MODEM_MODE_3G)) { erat_mode = 2; if (preferred == MM_MODEM_MODE_3G) erat_pref = 2; else if (preferred == MM_MODEM_MODE_NONE) erat_pref = 0; /* 2G prefer not supported */ } else if (allowed == (MM_MODEM_MODE_2G | MM_MODEM_MODE_3G | MM_MODEM_MODE_4G) && preferred == MM_MODEM_MODE_NONE) { erat_mode = 6; erat_pref = 0; } else if ((allowed == (MM_MODEM_MODE_2G | MM_MODEM_MODE_4G)) && preferred == MM_MODEM_MODE_NONE) { erat_mode = 4; erat_pref = 0; } else if ((allowed == (MM_MODEM_MODE_3G | MM_MODEM_MODE_4G)) && preferred == MM_MODEM_MODE_NONE) { erat_mode = 5; erat_pref = 0; } else if (allowed == MM_MODEM_MODE_4G) { erat_mode = 3; erat_pref = 0; } if (erat_mode < 0 || erat_pref < 0) { gchar *allowed_str; gchar *preferred_str; allowed_str = mm_modem_mode_build_string_from_mask (allowed); preferred_str = mm_modem_mode_build_string_from_mask (preferred); g_task_return_new_error ( task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Requested mode (allowed: '%s', preferred: '%s') not supported by the modem.", allowed_str, preferred_str); g_object_unref (task); g_free (allowed_str); g_free (preferred_str); return; } command = g_strdup_printf ("AT+ERAT=%d,%d", erat_mode, erat_pref); mm_base_modem_at_command ( MM_BASE_MODEM (self), command, 30, FALSE, (GAsyncReadyCallback)allowed_mode_update_ready, task); g_free (command); } /*****************************************************************************/ /* Setup/Cleanup unsolicited events (3GPP interface) */ static void mtk_80_signal_changed (MMPortSerialAt *port, GMatchInfo *match_info, MMBroadbandModemMtkLegacy *self) { guint quality = 0; if (!mm_get_uint_from_match_info (match_info, 1, &quality)) return; if (quality == 99) quality = 0; else quality = MM_CLAMP_HIGH (quality, 31) * 100 / 31; mm_obj_dbg (self, "6280 signal quality URC received: %u", quality); mm_iface_modem_update_signal_quality (MM_IFACE_MODEM (self), quality); } static void mtk_90_2g_signal_changed (MMPortSerialAt *port, GMatchInfo *match_info, MMBroadbandModemMtkLegacy *self) { guint quality = 0; if (!mm_get_uint_from_match_info (match_info, 1, &quality)) return; if (quality == 99) quality = 0; else quality = MM_CLAMP_HIGH (quality, 63) * 100 / 63; mm_obj_dbg (self, "2G signal quality URC received: %u", quality); mm_iface_modem_update_signal_quality (MM_IFACE_MODEM (self), quality); } static void mtk_90_3g_signal_changed (MMPortSerialAt *port, GMatchInfo *match_info, MMBroadbandModemMtkLegacy *self) { guint quality = 0; if (!mm_get_uint_from_match_info (match_info, 1, &quality)) return; quality = MM_CLAMP_HIGH (quality, 96) * 100 / 96; mm_obj_dbg (self, "3G signal quality URC received: %u", quality); mm_iface_modem_update_signal_quality (MM_IFACE_MODEM (self), quality); } static void mtk_90_4g_signal_changed (MMPortSerialAt *port, GMatchInfo *match_info, MMBroadbandModemMtkLegacy *self) { guint quality = 0; if (!mm_get_uint_from_match_info (match_info, 1, &quality)) return; quality = MM_CLAMP_HIGH (quality, 97) * 100 / 97; mm_obj_dbg (self, "4G signal quality URC received: %u", quality); mm_iface_modem_update_signal_quality (MM_IFACE_MODEM (self), quality); } static void set_unsolicited_events_handlers (MMBroadbandModemMtkLegacy *self, gboolean enable) { MMPortSerialAt *ports[2]; guint i; ports[0] = mm_base_modem_peek_port_primary (MM_BASE_MODEM (self)); ports[1] = mm_base_modem_peek_port_secondary (MM_BASE_MODEM (self)); /* Enable/disable unsolicited events in given port */ for (i = 0; i < G_N_ELEMENTS (ports); i++){ if(!ports[i]) continue; mm_port_serial_at_add_unsolicited_msg_handler ( ports[i], self->priv->ecsqg_regex, enable ? (MMPortSerialAtUnsolicitedMsgFn)mtk_80_signal_changed : NULL, enable ? self : NULL, NULL); mm_port_serial_at_add_unsolicited_msg_handler ( ports[i], self->priv->ecsqu_regex, enable ? (MMPortSerialAtUnsolicitedMsgFn)mtk_80_signal_changed : NULL, enable ? self : NULL, NULL); mm_port_serial_at_add_unsolicited_msg_handler ( ports[i], self->priv->ecsqeg_regex, enable ? (MMPortSerialAtUnsolicitedMsgFn)mtk_90_2g_signal_changed:NULL, enable ? self : NULL, NULL); mm_port_serial_at_add_unsolicited_msg_handler ( ports[i], self->priv->ecsqeu_regex, enable ? (MMPortSerialAtUnsolicitedMsgFn)mtk_90_3g_signal_changed:NULL, enable ? self : NULL, NULL); mm_port_serial_at_add_unsolicited_msg_handler ( ports[i], self->priv->ecsqel_regex, enable ? (MMPortSerialAtUnsolicitedMsgFn)mtk_90_4g_signal_changed:NULL, enable ? self : NULL, NULL); } } static gboolean modem_3gpp_setup_cleanup_unsolicited_events_finish (MMIfaceModem3gpp *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void parent_setup_unsolicited_events_ready (MMIfaceModem3gpp *self, GAsyncResult *res, GTask *task) { GError *error = NULL; if (!iface_modem_3gpp_parent->setup_unsolicited_events_finish (self, res, &error)) g_task_return_error (task, error); else { /* Our own setup now */ set_unsolicited_events_handlers (MM_BROADBAND_MODEM_MTK_LEGACY (self), TRUE); g_task_return_boolean (task, TRUE); } g_object_unref (task); } static void modem_3gpp_setup_unsolicited_events (MMIfaceModem3gpp *self, GAsyncReadyCallback callback, gpointer user_data) { /* Chain up parent's setup */ iface_modem_3gpp_parent->setup_unsolicited_events ( self, (GAsyncReadyCallback)parent_setup_unsolicited_events_ready, g_task_new (self, NULL, callback, user_data)); } static void parent_cleanup_unsolicited_events_ready (MMIfaceModem3gpp *self, GAsyncResult *res, GTask *task) { GError *error = NULL; if (!iface_modem_3gpp_parent->cleanup_unsolicited_events_finish (self, res, &error)) g_task_return_error (task, error); else g_task_return_boolean (task, TRUE); g_object_unref (task); } static void modem_3gpp_cleanup_unsolicited_events (MMIfaceModem3gpp *self, GAsyncReadyCallback callback, gpointer user_data) { /* Our own cleanup first */ set_unsolicited_events_handlers (MM_BROADBAND_MODEM_MTK_LEGACY (self), FALSE); /* And now chain up parent's cleanup */ iface_modem_3gpp_parent->cleanup_unsolicited_events ( self, (GAsyncReadyCallback)parent_cleanup_unsolicited_events_ready, g_task_new (self, NULL, callback, user_data)); } static const MMBaseModemAtCommand unsolicited_enable_sequence[] = { /* enable signal URC */ { "+ECSQ=2", 5, FALSE, NULL }, { NULL } }; static const MMBaseModemAtCommand unsolicited_disable_sequence[] = { /* disable signal URC */ { "+ECSQ=0" , 5, FALSE, NULL }, { NULL } }; static void own_enable_unsolicited_events_ready (MMBaseModem *self, GAsyncResult *res, GTask *task) { GError *error = NULL; mm_base_modem_at_sequence_full_finish (self, res, NULL, &error); if (error) g_task_return_error (task, error); else g_task_return_boolean (task, TRUE); g_object_unref (task); } static void parent_enable_unsolicited_events_ready (MMIfaceModem3gpp *self, GAsyncResult *res, GTask *task) { GError *error = NULL; if (!iface_modem_3gpp_parent->enable_unsolicited_events_finish (self, res, &error)) { g_task_return_error (task, error); g_object_unref (task); } /* Our own enable now */ mm_base_modem_at_sequence_full ( MM_BASE_MODEM (self), mm_base_modem_peek_port_primary (MM_BASE_MODEM (self)), unsolicited_enable_sequence, NULL,NULL,NULL, (GAsyncReadyCallback)own_enable_unsolicited_events_ready, task); } static void modem_3gpp_enable_unsolicited_events (MMIfaceModem3gpp *self, GAsyncReadyCallback callback, gpointer user_data) { /* Chain up parent's enable */ iface_modem_3gpp_parent->enable_unsolicited_events ( self, (GAsyncReadyCallback)parent_enable_unsolicited_events_ready, g_task_new (self, NULL, callback, user_data)); } static gboolean modem_3gpp_enable_unsolicited_events_finish (MMIfaceModem3gpp *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void parent_disable_unsolicited_events_ready (MMIfaceModem3gpp *self, GAsyncResult *res, GTask *task) { GError *error = NULL; if (!iface_modem_3gpp_parent->disable_unsolicited_events_finish (self, res, &error)) g_task_return_error (task, error); else g_task_return_boolean (task, TRUE); g_object_unref (task); } static void own_disable_unsolicited_events_ready (MMBaseModem *self, GAsyncResult *res, GTask *task) { GError *error = NULL; mm_base_modem_at_sequence_full_finish (self, res, NULL, &error); if (error) { g_task_return_error (task, error); g_object_unref (task); return; } /* Next, chain up parent's disable */ iface_modem_3gpp_parent->disable_unsolicited_events ( MM_IFACE_MODEM_3GPP (self), (GAsyncReadyCallback)parent_disable_unsolicited_events_ready, task); } static void modem_3gpp_disable_unsolicited_events (MMIfaceModem3gpp *self, GAsyncReadyCallback callback, gpointer user_data) { /* Our own disable first */ mm_base_modem_at_sequence_full ( MM_BASE_MODEM (self), mm_base_modem_peek_port_primary (MM_BASE_MODEM (self)), unsolicited_disable_sequence, NULL, NULL, NULL, (GAsyncReadyCallback)own_disable_unsolicited_events_ready, g_task_new (self, NULL, callback, user_data)); } static gboolean modem_3gpp_disable_unsolicited_events_finish (MMIfaceModem3gpp *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } /*****************************************************************************/ /* Setup ports (Broadband modem class) */ static void setup_ports (MMBroadbandModem *self) { /* Call parent's setup ports first always */ MM_BROADBAND_MODEM_CLASS (mm_broadband_modem_mtk_legacy_parent_class)->setup_ports (self); /* Now reset the unsolicited messages we'll handle when enabled */ set_unsolicited_events_handlers (MM_BROADBAND_MODEM_MTK_LEGACY (self), FALSE); } /*****************************************************************************/ MMBroadbandModemMtkLegacy * mm_broadband_modem_mtk_legacy_new (const gchar *device, const gchar **drivers, const gchar *plugin, guint16 vendor_id, guint16 product_id) { return g_object_new (MM_TYPE_BROADBAND_MODEM_MTK_LEGACY, MM_BASE_MODEM_DEVICE, device, MM_BASE_MODEM_DRIVERS, drivers, MM_BASE_MODEM_PLUGIN, plugin, MM_BASE_MODEM_VENDOR_ID, vendor_id, MM_BASE_MODEM_PRODUCT_ID, product_id, /* Generic bearer supports TTY only */ MM_BASE_MODEM_DATA_NET_SUPPORTED, FALSE, MM_BASE_MODEM_DATA_TTY_SUPPORTED, TRUE, NULL); } static void mm_broadband_modem_mtk_legacy_init (MMBroadbandModemMtkLegacy *self) { /* Initialize private data */ self->priv = G_TYPE_INSTANCE_GET_PRIVATE ((self), MM_TYPE_BROADBAND_MODEM_MTK_LEGACY, MMBroadbandModemMtkLegacyPrivate); self->priv->ecsqg_regex = g_regex_new ( "\\r\\n\\+ECSQ:\\s*([0-9]*),\\s*[0-9]*,\\s*-[0-9]*\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); self->priv->ecsqu_regex = g_regex_new ( "\\r\\n\\+ECSQ:\\s*([0-9]*),\\s*[0-9]*,\\s*-[0-9]*,\\s*-[0-9]*,\\s*-[0-9]*\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); self->priv->ecsqeg_regex = g_regex_new ( "\\r\\n\\+ECSQ:\\s*([0-9]*),\\s*[0-9]*,\\s*-[0-9]*,\\s*1,\\s*1,\\s*1,\\s*1,\\s*[0-9]*\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); self->priv->ecsqeu_regex = g_regex_new ( "\\r\\n\\+ECSQ:\\s*([0-9]*),\\s*[0-9]*,\\s*1,\\s*-[0-9]*,\\s*-[0-9]*,\\s*1,\\s*1,\\s*[0-9]*\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); self->priv->ecsqel_regex = g_regex_new ( "\\r\\n\\+ECSQ:\\s*[0-9]*,\\s*([0-9]*),\\s*1,\\s*1,\\s*1,\\s*-[0-9]*,\\s*-[0-9]*,\\s*[0-9]*\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); } static void finalize (GObject *object) { MMBroadbandModemMtkLegacy *self = MM_BROADBAND_MODEM_MTK_LEGACY (object); g_regex_unref (self->priv->ecsqg_regex); g_regex_unref (self->priv->ecsqu_regex); g_regex_unref (self->priv->ecsqeg_regex); g_regex_unref (self->priv->ecsqeu_regex); g_regex_unref (self->priv->ecsqel_regex); G_OBJECT_CLASS (mm_broadband_modem_mtk_legacy_parent_class)->finalize (object); } static void iface_modem_init (MMIfaceModem *iface) { iface_modem_parent = g_type_interface_peek_parent (iface); iface->modem_after_sim_unlock = modem_after_sim_unlock; iface->modem_after_sim_unlock_finish = modem_after_sim_unlock_finish; iface->load_supported_modes = load_supported_modes; iface->load_supported_modes_finish = load_supported_modes_finish; iface->load_current_modes = load_current_modes; iface->load_current_modes_finish = load_current_modes_finish; iface->set_current_modes = set_current_modes; iface->set_current_modes_finish = set_current_modes_finish; iface->load_unlock_retries = load_unlock_retries; iface->load_unlock_retries_finish = load_unlock_retries_finish; } static void iface_modem_3gpp_init (MMIfaceModem3gpp *iface) { iface_modem_3gpp_parent = g_type_interface_peek_parent (iface); iface->setup_unsolicited_events = modem_3gpp_setup_unsolicited_events; iface->setup_unsolicited_events_finish = modem_3gpp_setup_cleanup_unsolicited_events_finish; iface->cleanup_unsolicited_events = modem_3gpp_cleanup_unsolicited_events; iface->cleanup_unsolicited_events_finish = modem_3gpp_setup_cleanup_unsolicited_events_finish; iface->enable_unsolicited_events = modem_3gpp_enable_unsolicited_events; iface->enable_unsolicited_events_finish = modem_3gpp_enable_unsolicited_events_finish; iface->disable_unsolicited_events = modem_3gpp_disable_unsolicited_events; iface->disable_unsolicited_events_finish = modem_3gpp_disable_unsolicited_events_finish; } static void mm_broadband_modem_mtk_legacy_class_init (MMBroadbandModemMtkLegacyClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); MMBroadbandModemClass *broadband_modem_class = MM_BROADBAND_MODEM_CLASS (klass); g_type_class_add_private (object_class, sizeof (MMBroadbandModemMtkLegacyPrivate)); object_class->finalize = finalize; broadband_modem_class->setup_ports = setup_ports; } ModemManager-1.23.4-dev/src/plugins/mtk/mm-broadband-modem-mtk-legacy.h000066400000000000000000000050331456466623000256170ustar00rootroot00000000000000/* -*- 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 - 2011 Red Hat, Inc. * Copyright (C) 2011 Google Inc. */ #ifndef MM_BROADBAND_MODEM_MTK_LEGACY_H #define MM_BROADBAND_MODEM_MTK_LEGACY_H #include "mm-broadband-modem.h" #define MM_TYPE_BROADBAND_MODEM_MTK_LEGACY (mm_broadband_modem_mtk_legacy_get_type ()) #define MM_BROADBAND_MODEM_MTK_LEGACY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_BROADBAND_MODEM_MTK_LEGACY, MMBroadbandModemMtkLegacy)) #define MM_BROADBAND_MODEM_MTK_LEGACY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_BROADBAND_MODEM_MTK_LEGACY, MMBroadbandModemMtkLegacyClass)) #define MM_IS_BROADBAND_MODEM_MTK_LEGACY(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_BROADBAND_MODEM_MTK_LEGACY)) #define MM_IS_BROADBAND_MODEM_MTK_LEGACY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_BROADBAND_MODEM_MTK_LEGACY)) #define MM_BROADBAND_MODEM_MTK_LEGACY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_BROADBAND_MODEM_MTK_LEGACY, MMBroadbandModemMtkLegacyClass)) typedef struct _MMBroadbandModemMtkLegacy MMBroadbandModemMtkLegacy; typedef struct _MMBroadbandModemMtkLegacyClass MMBroadbandModemMtkLegacyClass; typedef struct _MMBroadbandModemMtkLegacyPrivate MMBroadbandModemMtkLegacyPrivate; struct _MMBroadbandModemMtkLegacy { MMBroadbandModem parent; MMBroadbandModemMtkLegacyPrivate *priv; }; struct _MMBroadbandModemMtkLegacyClass { MMBroadbandModemClass parent; }; GType mm_broadband_modem_mtk_legacy_get_type (void); MMBroadbandModemMtkLegacy *mm_broadband_modem_mtk_legacy_new (const gchar *device, const gchar **drivers, const gchar *plugin, guint16 vendor_id, guint16 product_id); #endif /* MM_BROADBAND_MODEM_MTK_LEGACY_H */ ModemManager-1.23.4-dev/src/plugins/mtk/mm-plugin-mtk-legacy.c000066400000000000000000000052351456466623000241010ustar00rootroot00000000000000/* -*- 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 - 2012 Red Hat, Inc. * Copyright (C) 2012 Aleksander Morgado */ #include #include #define _LIBMM_INSIDE_MM #include #include "mm-plugin-common.h" #include "mm-broadband-modem-mtk-legacy.h" #define MM_TYPE_PLUGIN_MTK_LEGACY mm_plugin_mtk_legacy_get_type () MM_DEFINE_PLUGIN (MTK_LEGACY, mtk_legacy, MtkLegacy) /*****************************************************************************/ /* MTK done */ static MMBaseModem * create_modem (MMPlugin *self, const gchar *uid, const gchar *physdev, const gchar **drivers, guint16 vendor, guint16 product, guint16 subsystem_vendor, GList *probes, GError **error) { return MM_BASE_MODEM (mm_broadband_modem_mtk_legacy_new (uid, drivers, mm_plugin_get_name (self), vendor, product)); } /*****************************************************************************/ MM_PLUGIN_NAMED_CREATOR_SCOPE MMPlugin * mm_plugin_create_mtk_legacy (void) { static const gchar *subsystems[] = { "tty", NULL }; static const gchar *udev_tags[]={ "ID_MM_MTK_TAGGED", NULL}; return MM_PLUGIN ( g_object_new (MM_TYPE_PLUGIN_MTK_LEGACY, MM_PLUGIN_NAME, MM_MODULE_NAME, MM_PLUGIN_ALLOWED_SUBSYSTEMS, subsystems, MM_PLUGIN_ALLOWED_UDEV_TAGS, udev_tags, MM_PLUGIN_ALLOWED_AT, TRUE, NULL)); } static void mm_plugin_mtk_legacy_init (MMPluginMtkLegacy *self) { } static void mm_plugin_mtk_legacy_class_init (MMPluginMtkLegacyClass *klass) { MMPluginClass *plugin_class = MM_PLUGIN_CLASS (klass); plugin_class->create_modem = create_modem; } ModemManager-1.23.4-dev/src/plugins/mtk/mm-plugin-mtk.c000066400000000000000000000077511456466623000226440ustar00rootroot00000000000000/* -*- 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) 2023 Mediatek, Inc */ #include #include #define _LIBMM_INSIDE_MM #include #include "mm-plugin-common.h" #include "mm-broadband-modem.h" #if defined WITH_MBIM #include "mm-broadband-modem-mbim.h" #include "mm-broadband-modem-mbim-mtk.h" #include "mm-broadband-modem-mbim-mtk-fibocom.h" #endif #include "mm-log.h" #define MM_TYPE_PLUGIN_MTK mm_plugin_mtk_get_type () MM_DEFINE_PLUGIN (MTK, mtk, Mtk) /*****************************************************************************/ static MMBaseModem * create_modem (MMPlugin *self, const gchar *uid, const gchar *physdev, const gchar **drivers, guint16 vendor, guint16 product, guint16 subsystem_vendor, GList *probes, GError **error) { #if defined WITH_MBIM if (mm_port_probe_list_has_mbim_port (probes)) { /* FM350 support with Fibocom-specific changes */ if (vendor == 0x14c3 && product == 0x4d75) { mm_obj_dbg (self, "MBIM-powered MTK-based Fibocom modem found..."); return MM_BASE_MODEM (mm_broadband_modem_mbim_mtk_fibocom_new (uid, physdev, drivers, mm_plugin_get_name (self), vendor, product)); } mm_obj_dbg (self, "MBIM-powered MTK modem found..."); return MM_BASE_MODEM (mm_broadband_modem_mbim_mtk_new (uid, physdev, drivers, mm_plugin_get_name (self), vendor, product)); } #endif return MM_BASE_MODEM (mm_broadband_modem_new (uid, physdev, drivers, mm_plugin_get_name (self), vendor, product)); } /*****************************************************************************/ MM_PLUGIN_NAMED_CREATOR_SCOPE MMPlugin * mm_plugin_create_mtk (void) { static const gchar *subsystems[] = { "wwan", "net", NULL }; static const gchar *drivers[] = { "mtk_t7xx", NULL }; MMPlugin *self = MM_PLUGIN ( g_object_new (MM_TYPE_PLUGIN_MTK, MM_PLUGIN_NAME, MM_MODULE_NAME, MM_PLUGIN_ALLOWED_SUBSYSTEMS, subsystems, MM_PLUGIN_ALLOWED_DRIVERS, drivers, MM_PLUGIN_ALLOWED_AT, TRUE, MM_PLUGIN_ALLOWED_MBIM, TRUE, NULL)); return self; } static void mm_plugin_mtk_init (MMPluginMtk *self) { } static void mm_plugin_mtk_class_init (MMPluginMtkClass *klass) { MMPluginClass *plugin_class = MM_PLUGIN_CLASS (klass); plugin_class->create_modem = create_modem; } ModemManager-1.23.4-dev/src/plugins/nokia/000077500000000000000000000000001456466623000202765ustar00rootroot00000000000000ModemManager-1.23.4-dev/src/plugins/nokia/77-mm-nokia-port-types.rules000066400000000000000000000042001456466623000254350ustar00rootroot00000000000000# do not edit this file, it will be overwritten on update ACTION!="add|change|move|bind", GOTO="mm_nokia_port_types_end" SUBSYSTEMS=="usb", ATTRS{idVendor}=="0421", GOTO="mm_nokia_port_types" GOTO="mm_nokia_port_types_end" LABEL="mm_nokia_port_types" SUBSYSTEMS=="usb", ATTRS{bInterfaceNumber}=="?*", ENV{.MM_USBIFNUM}="$attr{bInterfaceNumber}" # For Nokia Internet Sticks (CS-xx) the modem/PPP port appears to always be USB interface 1 ATTRS{idVendor}=="0421", ATTRS{idProduct}=="060D", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1" ATTRS{idVendor}=="0421", ATTRS{idProduct}=="0611", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1" ATTRS{idVendor}=="0421", ATTRS{idProduct}=="061A", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1" ATTRS{idVendor}=="0421", ATTRS{idProduct}=="061B", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1" ATTRS{idVendor}=="0421", ATTRS{idProduct}=="061F", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1" ATTRS{idVendor}=="0421", ATTRS{idProduct}=="0619", ENV{.MM_USBIFNUM}=="03", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1" ATTRS{idVendor}=="0421", ATTRS{idProduct}=="0620", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1" ATTRS{idVendor}=="0421", ATTRS{idProduct}=="0623", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1" ATTRS{idVendor}=="0421", ATTRS{idProduct}=="0624", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1" ATTRS{idVendor}=="0421", ATTRS{idProduct}=="0625", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1" ATTRS{idVendor}=="0421", ATTRS{idProduct}=="062A", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1" ATTRS{idVendor}=="0421", ATTRS{idProduct}=="062E", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1" ATTRS{idVendor}=="0421", ATTRS{idProduct}=="062F", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1" LABEL="mm_nokia_port_types_end" ModemManager-1.23.4-dev/src/plugins/nokia/mm-broadband-modem-nokia.c000066400000000000000000000310171456466623000251650ustar00rootroot00000000000000/* -*- 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 - 2011 Red Hat, Inc. * Copyright (C) 2011 Google Inc. */ #include #include #include #include #include #include #include "ModemManager.h" #include "mm-serial-parsers.h" #include "mm-errors-types.h" #include "mm-iface-modem.h" #include "mm-iface-modem-messaging.h" #include "mm-iface-modem-3gpp.h" #include "mm-modem-helpers.h" #include "mm-base-modem-at.h" #include "mm-broadband-modem-nokia.h" #include "mm-sim-nokia.h" static void iface_modem_init (MMIfaceModem *iface); static void iface_modem_messaging_init (MMIfaceModemMessaging *iface); static MMIfaceModem *iface_modem_parent; G_DEFINE_TYPE_EXTENDED (MMBroadbandModemNokia, mm_broadband_modem_nokia, MM_TYPE_BROADBAND_MODEM, 0, G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM, iface_modem_init) G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_MESSAGING, iface_modem_messaging_init)); /*****************************************************************************/ /* Create SIM (Modem interface) */ static MMBaseSim * create_sim_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { return mm_sim_nokia_new_finish (res, error); } static void create_sim (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data) { /* New Nokia SIM */ mm_sim_nokia_new (MM_BASE_MODEM (self), NULL, /* cancellable */ callback, user_data); } /*****************************************************************************/ /* Load access technologies (Modem interface) */ typedef struct { MMModemAccessTechnology act; guint mask; } AccessTechInfo; static gboolean load_access_technologies_finish (MMIfaceModem *self, GAsyncResult *res, MMModemAccessTechnology *access_technologies, guint *mask, GError **error) { GError *inner_error = NULL; gssize value; value = g_task_propagate_int (G_TASK (res), &inner_error); if (inner_error) { g_propagate_error (error, inner_error); return FALSE; } *access_technologies = (MMModemAccessTechnology)value; *mask = MM_IFACE_MODEM_3GPP_ALL_ACCESS_TECHNOLOGIES_MASK; return TRUE; } static void parent_load_access_technologies_ready (MMIfaceModem *self, GAsyncResult *res, GTask *task) { MMModemAccessTechnology act = MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN; guint mask = 0; GError *error = NULL; if (!iface_modem_parent->load_access_technologies_finish (self, res, &act, &mask, &error)) g_task_return_error (task, error); else g_task_return_int (task, act); g_object_unref (task); } static void access_tech_ready (MMBaseModem *self, GAsyncResult *res, GTask *task) { MMModemAccessTechnology act = MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN; const gchar *response, *p; response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, NULL); if (!response) { /* Chain up to parent */ iface_modem_parent->load_access_technologies ( MM_IFACE_MODEM (self), (GAsyncReadyCallback)parent_load_access_technologies_ready, task); return; } p = mm_strip_tag (response, "*CNTI:"); p = strchr (p, ','); if (p) act = mm_string_to_access_tech (p + 1); if (act == MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN) g_task_return_new_error ( task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't parse access technologies result: '%s'", response); else g_task_return_int (task, act); g_object_unref (task); } static void load_access_technologies (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data) { mm_base_modem_at_command (MM_BASE_MODEM (self), "*CNTI=0", 3, FALSE, (GAsyncReadyCallback)access_tech_ready, g_task_new (self, NULL, callback, user_data)); } /*****************************************************************************/ /* Loading supported charsets (Modem interface) */ static MMModemCharset load_supported_charsets_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { const gchar *response; MMModemCharset charsets = MM_MODEM_CHARSET_UNKNOWN; response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, error); if (!response) return MM_MODEM_CHARSET_UNKNOWN; if (!mm_3gpp_parse_cscs_test_response (response, &charsets)) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Failed to parse the supported character sets response"); return MM_MODEM_CHARSET_UNKNOWN; } return charsets; } static void load_supported_charsets (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data) { mm_base_modem_at_command (MM_BASE_MODEM (self), "+CSCS=?", 20, TRUE, callback, user_data); } /*****************************************************************************/ /* Initializing the modem (during first enabling) */ typedef struct { guint retries; } EnablingModemInitContext; static gboolean enabling_modem_init_finish (MMBroadbandModem *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void retry_atz (GTask *task); static void atz_ready (MMBaseModem *self, GAsyncResult *res, GTask *task) { EnablingModemInitContext *ctx; GError *error = NULL; ctx = g_task_get_task_data (task); /* One retry less */ ctx->retries--; if (!mm_base_modem_at_command_full_finish (self, res, &error)) { /* Consumed all retries... */ if (ctx->retries == 0) { g_task_return_error (task, error); g_object_unref (task); return; } /* Retry... */ g_error_free (error); retry_atz (task); return; } /* Good! */ g_task_return_boolean (task, TRUE); g_object_unref (task); } static void retry_atz (GTask *task) { MMBaseModem *self; self = g_task_get_source_object (task); mm_base_modem_at_command_full (self, mm_base_modem_peek_port_primary (self), "Z", 6, FALSE, FALSE, NULL, /* cancellable */ (GAsyncReadyCallback)atz_ready, task); } static void enabling_modem_init (MMBroadbandModem *self, GAsyncReadyCallback callback, gpointer user_data) { EnablingModemInitContext *ctx; GTask *task; ctx = g_new (EnablingModemInitContext, 1); /* Send the init command twice; some devices (Nokia N900) appear to take a * few commands before responding correctly. Instead of penalizing them for * being stupid the first time by failing to enable the device, just * try again. */ ctx->retries = 2; task = g_task_new (self, NULL, callback, user_data); g_task_set_task_data (task, ctx, g_free); retry_atz (task); } /*****************************************************************************/ /* Setup ports (Broadband modem class) */ static const gchar *primary_init_sequence[] = { /* When initializing a Nokia port, first enable the echo, * and then disable it, so that we get it properly disabled. */ "E1 E0", /* The N900 ignores the E0 when it's on the same line as the E1, so try again */ "E0", /* Get word responses */ "V1", /* Extended numeric codes */ "+CMEE=1", /* Report all call status */ "X4", /* Assert DCD when carrier detected */ "&C1", NULL }; static void setup_ports (MMBroadbandModem *self) { MMPortSerialAt *primary; /* Call parent's setup ports first always */ MM_BROADBAND_MODEM_CLASS (mm_broadband_modem_nokia_parent_class)->setup_ports (self); primary = mm_base_modem_peek_port_primary (MM_BASE_MODEM (self)); g_object_set (primary, MM_PORT_SERIAL_AT_INIT_SEQUENCE, primary_init_sequence, NULL); } /*****************************************************************************/ MMBroadbandModemNokia * mm_broadband_modem_nokia_new (const gchar *device, const gchar *physdev, const gchar **drivers, const gchar *plugin, guint16 vendor_id, guint16 product_id) { return g_object_new (MM_TYPE_BROADBAND_MODEM_NOKIA, MM_BASE_MODEM_DEVICE, device, MM_BASE_MODEM_PHYSDEV, physdev, MM_BASE_MODEM_DRIVERS, drivers, MM_BASE_MODEM_PLUGIN, plugin, MM_BASE_MODEM_VENDOR_ID, vendor_id, MM_BASE_MODEM_PRODUCT_ID, product_id, /* Generic bearer supports TTY only */ MM_BASE_MODEM_DATA_NET_SUPPORTED, FALSE, MM_BASE_MODEM_DATA_TTY_SUPPORTED, TRUE, NULL); } static void mm_broadband_modem_nokia_init (MMBroadbandModemNokia *self) { } static void iface_modem_messaging_init (MMIfaceModemMessaging *iface) { /* Don't even try to check messaging support */ iface->check_support = NULL; iface->check_support_finish = NULL; } static void iface_modem_init (MMIfaceModem *iface) { iface_modem_parent = g_type_interface_peek_parent (iface); /* Create Nokia-specific SIM*/ iface->create_sim = create_sim; iface->create_sim_finish = create_sim_finish; /* Nokia handsets (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. */ iface->load_power_state = NULL; iface->load_power_state_finish = NULL; iface->modem_power_up = NULL; iface->modem_power_up_finish = NULL; iface->modem_power_down = NULL; iface->modem_power_down_finish = NULL; iface->load_supported_charsets = load_supported_charsets; iface->load_supported_charsets_finish = load_supported_charsets_finish; iface->load_access_technologies = load_access_technologies; iface->load_access_technologies_finish = load_access_technologies_finish; } static void mm_broadband_modem_nokia_class_init (MMBroadbandModemNokiaClass *klass) { MMBroadbandModemClass *broadband_modem_class = MM_BROADBAND_MODEM_CLASS (klass); broadband_modem_class->setup_ports = setup_ports; broadband_modem_class->enabling_modem_init = enabling_modem_init; broadband_modem_class->enabling_modem_init_finish = enabling_modem_init_finish; } ModemManager-1.23.4-dev/src/plugins/nokia/mm-broadband-modem-nokia.h000066400000000000000000000045051456466623000251740ustar00rootroot00000000000000/* -*- 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 - 2011 Red Hat, Inc. * Copyright (C) 2011 Google Inc. */ #ifndef MM_BROADBAND_MODEM_NOKIA_H #define MM_BROADBAND_MODEM_NOKIA_H #include "mm-broadband-modem.h" #define MM_TYPE_BROADBAND_MODEM_NOKIA (mm_broadband_modem_nokia_get_type ()) #define MM_BROADBAND_MODEM_NOKIA(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_BROADBAND_MODEM_NOKIA, MMBroadbandModemNokia)) #define MM_BROADBAND_MODEM_NOKIA_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_BROADBAND_MODEM_NOKIA, MMBroadbandModemNokiaClass)) #define MM_IS_BROADBAND_MODEM_NOKIA(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_BROADBAND_MODEM_NOKIA)) #define MM_IS_BROADBAND_MODEM_NOKIA_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_BROADBAND_MODEM_NOKIA)) #define MM_BROADBAND_MODEM_NOKIA_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_BROADBAND_MODEM_NOKIA, MMBroadbandModemNokiaClass)) typedef struct _MMBroadbandModemNokia MMBroadbandModemNokia; typedef struct _MMBroadbandModemNokiaClass MMBroadbandModemNokiaClass; struct _MMBroadbandModemNokia { MMBroadbandModem parent; }; struct _MMBroadbandModemNokiaClass{ MMBroadbandModemClass parent; }; GType mm_broadband_modem_nokia_get_type (void); MMBroadbandModemNokia *mm_broadband_modem_nokia_new (const gchar *device, const gchar *physdev, const gchar **drivers, const gchar *plugin, guint16 vendor_id, guint16 product_id); #endif /* MM_BROADBAND_MODEM_NOKIA_H */ ModemManager-1.23.4-dev/src/plugins/nokia/mm-plugin-nokia-icera.c000066400000000000000000000062261456466623000245350ustar00rootroot00000000000000/* -*- 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 - 2012 Red Hat, Inc. * Copyright (C) 2012 Aleksander Morgado */ #include #include #define _LIBMM_INSIDE_MM #include #include "mm-plugin-common.h" #include "mm-broadband-modem-icera.h" #define MM_TYPE_PLUGIN_NOKIA_ICERA mm_plugin_nokia_icera_get_type () MM_DEFINE_PLUGIN (NOKIA_ICERA, nokia_icera, NokiaIcera) /*****************************************************************************/ /* Custom commands for AT probing */ static const MMPortProbeAtCommand custom_at_probe[] = { { "ATE1 E0", 3, mm_port_probe_response_processor_is_at }, { "ATE1 E0", 3, mm_port_probe_response_processor_is_at }, { "ATE1 E0", 3, mm_port_probe_response_processor_is_at }, { NULL } }; /*****************************************************************************/ static MMBaseModem * create_modem (MMPlugin *self, const gchar *uid, const gchar *physdev, const gchar **drivers, guint16 vendor, guint16 product, guint16 subsystem_vendor, GList *probes, GError **error) { return MM_BASE_MODEM (mm_broadband_modem_icera_new (uid, physdev, drivers, mm_plugin_get_name (self), vendor, product)); } /*****************************************************************************/ MM_PLUGIN_NAMED_CREATOR_SCOPE MMPlugin * mm_plugin_create_nokia_icera (void) { static const gchar *subsystems[] = { "tty", "net", NULL }; static const guint16 vendor_ids[] = { 0x0421, 0 }; return MM_PLUGIN ( g_object_new (MM_TYPE_PLUGIN_NOKIA_ICERA, MM_PLUGIN_NAME, MM_MODULE_NAME, MM_PLUGIN_ALLOWED_SUBSYSTEMS, subsystems, MM_PLUGIN_ALLOWED_VENDOR_IDS, vendor_ids, MM_PLUGIN_CUSTOM_AT_PROBE, custom_at_probe, MM_PLUGIN_ALLOWED_AT, TRUE, MM_PLUGIN_ALLOWED_ICERA, TRUE, /* Only Nokia/Icera modems */ NULL)); } static void mm_plugin_nokia_icera_init (MMPluginNokiaIcera *self) { } static void mm_plugin_nokia_icera_class_init (MMPluginNokiaIceraClass *klass) { MMPluginClass *plugin_class = MM_PLUGIN_CLASS (klass); plugin_class->create_modem = create_modem; } ModemManager-1.23.4-dev/src/plugins/nokia/mm-plugin-nokia.c000066400000000000000000000064411456466623000234530ustar00rootroot00000000000000/* -*- 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 - 2011 Red Hat, Inc. * Copyright (C) 2011 - 2012 Google, Inc. */ #include #include #define _LIBMM_INSIDE_MM #include #include "mm-plugin-common.h" #include "mm-broadband-modem-nokia.h" #define MM_TYPE_PLUGIN_NOKIA mm_plugin_nokia_get_type () MM_DEFINE_PLUGIN (NOKIA, nokia, Nokia) /*****************************************************************************/ /* Custom commands for AT probing */ static const MMPortProbeAtCommand custom_at_probe[] = { { "ATE1 E0", 3, mm_port_probe_response_processor_is_at }, { "ATE1 E0", 3, mm_port_probe_response_processor_is_at }, { "ATE1 E0", 3, mm_port_probe_response_processor_is_at }, { NULL } }; /*****************************************************************************/ static MMBaseModem * create_modem (MMPlugin *self, const gchar *uid, const gchar *physdev, const gchar **drivers, guint16 vendor, guint16 product, guint16 subsystem_vendor, GList *probes, GError **error) { return MM_BASE_MODEM (mm_broadband_modem_nokia_new (uid, physdev, drivers, mm_plugin_get_name (self), vendor, product)); } /*****************************************************************************/ MM_PLUGIN_NAMED_CREATOR_SCOPE MMPlugin * mm_plugin_create_nokia (void) { static const gchar *subsystems[] = { "tty", NULL }; static const guint16 vendor_ids[] = { 0x0421, 0 }; static const gchar *vendor_strings[] = { "nokia", NULL }; return MM_PLUGIN ( g_object_new (MM_TYPE_PLUGIN_NOKIA, MM_PLUGIN_NAME, MM_MODULE_NAME, MM_PLUGIN_ALLOWED_SUBSYSTEMS, subsystems, MM_PLUGIN_ALLOWED_VENDOR_IDS, vendor_ids, MM_PLUGIN_ALLOWED_VENDOR_STRINGS, vendor_strings, MM_PLUGIN_CUSTOM_AT_PROBE, custom_at_probe, MM_PLUGIN_ALLOWED_SINGLE_AT, TRUE, /* only 1 AT port expected! */ MM_PLUGIN_FORBIDDEN_ICERA, TRUE, /* No Nokia/Icera modems */ NULL)); } static void mm_plugin_nokia_init (MMPluginNokia *self) { } static void mm_plugin_nokia_class_init (MMPluginNokiaClass *klass) { MMPluginClass *plugin_class = MM_PLUGIN_CLASS (klass); plugin_class->create_modem = create_modem; } ModemManager-1.23.4-dev/src/plugins/nokia/mm-sim-nokia.c000066400000000000000000000051171456466623000227440ustar00rootroot00000000000000/* -*- 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) 2012 Aleksander Morgado */ #include #include #include #include #include #include #include #define _LIBMM_INSIDE_MM #include #include "mm-sim-nokia.h" G_DEFINE_TYPE (MMSimNokia, mm_sim_nokia, MM_TYPE_BASE_SIM) /*****************************************************************************/ MMBaseSim * mm_sim_nokia_new_finish (GAsyncResult *res, GError **error) { GObject *source; GObject *sim; source = g_async_result_get_source_object (res); sim = g_async_initable_new_finish (G_ASYNC_INITABLE (source), res, error); g_object_unref (source); if (!sim) return NULL; /* Only export valid SIMs */ mm_base_sim_export (MM_BASE_SIM (sim)); return MM_BASE_SIM (sim); } void mm_sim_nokia_new (MMBaseModem *modem, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { g_async_initable_new_async (MM_TYPE_SIM_NOKIA, G_PRIORITY_DEFAULT, cancellable, callback, user_data, MM_BASE_SIM_MODEM, modem, "active", TRUE, /* by default always active */ NULL); } static void mm_sim_nokia_init (MMSimNokia *self) { } static void mm_sim_nokia_class_init (MMSimNokiaClass *klass) { MMBaseSimClass *base_sim_class = MM_BASE_SIM_CLASS (klass); /* Skip querying most SIM card info, +CRSM not supported by Nokia modems */ base_sim_class->load_sim_identifier = NULL; base_sim_class->load_sim_identifier_finish = NULL; base_sim_class->load_operator_identifier = NULL; base_sim_class->load_operator_identifier_finish = NULL; base_sim_class->load_operator_name = NULL; base_sim_class->load_operator_name_finish = NULL; } ModemManager-1.23.4-dev/src/plugins/nokia/mm-sim-nokia.h000066400000000000000000000036511456466623000227520ustar00rootroot00000000000000/* -*- 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) 2012 Aleksander Morgado */ #ifndef MM_SIM_NOKIA_H #define MM_SIM_NOKIA_H #include #include #include "mm-base-sim.h" #define MM_TYPE_SIM_NOKIA (mm_sim_nokia_get_type ()) #define MM_SIM_NOKIA(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_SIM_NOKIA, MMSimNokia)) #define MM_SIM_NOKIA_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_SIM_NOKIA, MMSimNokiaClass)) #define MM_IS_SIM_NOKIA(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_SIM_NOKIA)) #define MM_IS_SIM_NOKIA_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_SIM_NOKIA)) #define MM_SIM_NOKIA_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_SIM_NOKIA, MMSimNokiaClass)) typedef struct _MMSimNokia MMSimNokia; typedef struct _MMSimNokiaClass MMSimNokiaClass; struct _MMSimNokia { MMBaseSim parent; }; struct _MMSimNokiaClass { MMBaseSimClass parent; }; GType mm_sim_nokia_get_type (void); void mm_sim_nokia_new (MMBaseModem *modem, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); MMBaseSim *mm_sim_nokia_new_finish (GAsyncResult *res, GError **error); #endif /* MM_SIM_NOKIA_H */ ModemManager-1.23.4-dev/src/plugins/novatel/000077500000000000000000000000001456466623000206455ustar00rootroot00000000000000ModemManager-1.23.4-dev/src/plugins/novatel/mm-broadband-bearer-novatel-lte.c000066400000000000000000000421551456466623000270310ustar00rootroot00000000000000/* -*- 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 - 2012 Red Hat, Inc. * Copyright (C) 2011 - 2012 Google, Inc. * Copyright (C) 2016 Aleksander Morgado */ #include #include #include #include #include #include #include #define _LIBMM_INSIDE_MM #include #include "mm-base-modem-at.h" #include "mm-broadband-bearer-novatel-lte.h" #include "mm-log-object.h" #include "mm-modem-helpers.h" #define QMISTATUS_TAG "$NWQMISTATUS:" G_DEFINE_TYPE (MMBroadbandBearerNovatelLte, mm_broadband_bearer_novatel_lte, MM_TYPE_BROADBAND_BEARER) /*****************************************************************************/ static gchar * normalize_qmistatus (const gchar *status) { gchar *normalized_status, *iter; if (!status) return NULL; normalized_status = g_strdup (status); for (iter = normalized_status; *iter; iter++) if (g_ascii_isspace (*iter)) *iter = ' '; return normalized_status; } static gboolean is_qmistatus_connected (const gchar *str) { str = mm_strip_tag (str, QMISTATUS_TAG); return g_strrstr (str, "QMI State: CONNECTED") || g_strrstr (str, "QMI State: QMI_WDS_PKT_DATA_CONNECTED"); } static gboolean is_qmistatus_disconnected (const gchar *str) { str = mm_strip_tag (str, QMISTATUS_TAG); return g_strrstr (str, "QMI State: DISCONNECTED") || g_strrstr (str, "QMI State: QMI_WDS_PKT_DATA_DISCONNECTED"); } static gboolean is_qmistatus_call_failed (const gchar *str) { str = mm_strip_tag (str, QMISTATUS_TAG); return (g_strrstr (str, "QMI_RESULT_FAILURE:QMI_ERR_CALL_FAILED") != NULL); } /*****************************************************************************/ /* Connection status monitoring */ static MMBearerConnectionStatus load_connection_status_finish (MMBaseBearer *bearer, GAsyncResult *res, GError **error) { GError *inner_error = NULL; gssize value; value = g_task_propagate_int (G_TASK (res), &inner_error); if (inner_error) { g_propagate_error (error, inner_error); return MM_BEARER_CONNECTION_STATUS_UNKNOWN; } return (MMBearerConnectionStatus)value; } static void poll_connection_ready (MMBaseModem *modem, GAsyncResult *res, GTask *task) { const gchar *result; GError *error = NULL; result = mm_base_modem_at_command_finish (modem, res, &error); if (!result) g_task_return_error (task, error); else if (is_qmistatus_disconnected (result)) g_task_return_int (task, MM_BEARER_CONNECTION_STATUS_DISCONNECTED); else g_task_return_int (task, MM_BEARER_CONNECTION_STATUS_CONNECTED); g_object_unref (task); } static void load_connection_status (MMBaseBearer *bearer, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; MMBaseModem *modem = NULL; task = g_task_new (bearer, NULL, callback, user_data); g_object_get (MM_BASE_BEARER (bearer), MM_BASE_BEARER_MODEM, &modem, NULL); mm_base_modem_at_command ( modem, "$NWQMISTATUS", 3, FALSE, (GAsyncReadyCallback) poll_connection_ready, task); g_object_unref (modem); } /*****************************************************************************/ /* 3GPP Connection sequence */ typedef struct { MMBaseModem *modem; MMPortSerialAt *primary; MMPort *data; gint retries; } DetailedConnectContext; static void detailed_connect_context_free (DetailedConnectContext *ctx) { if (ctx->data) g_object_unref (ctx->data); g_object_unref (ctx->primary); g_object_unref (ctx->modem); g_slice_free (DetailedConnectContext, ctx); } static MMBearerConnectResult * connect_3gpp_finish (MMBroadbandBearer *self, GAsyncResult *res, GError **error) { return g_task_propagate_pointer (G_TASK (res), error); } static gboolean connect_3gpp_qmistatus (GTask *task); static void connect_3gpp_qmistatus_ready (MMBaseModem *modem, GAsyncResult *res, GTask *task) { MMBroadbandBearerNovatelLte *self; DetailedConnectContext *ctx; const gchar *result; gchar *normalized_result; GError *error = NULL; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); if (g_task_return_error_if_cancelled (task)) { g_object_unref (task); return; } result = mm_base_modem_at_command_full_finish (modem, res, &error); if (!result) { if (!g_error_matches (error, MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_UNKNOWN)) { g_task_return_error (task, error); g_object_unref (task); return; } mm_obj_dbg (self, "connection status failed: %s; will retry", error->message); g_error_free (error); goto retry; } if (is_qmistatus_connected (result)) { MMBearerIpConfig *config; mm_obj_dbg (self, "connected"); config = mm_bearer_ip_config_new (); mm_bearer_ip_config_set_method (config, MM_BEARER_IP_METHOD_DHCP); g_task_return_pointer ( task, mm_bearer_connect_result_new (ctx->data, config, NULL), (GDestroyNotify)mm_bearer_connect_result_unref); g_object_unref (task); g_object_unref (config); return; } /* Don't retry if the call failed */ if (is_qmistatus_call_failed (result)) { mm_obj_dbg (self, "not retrying: call failed"); ctx->retries = 0; } retry: if (ctx->retries > 0) { ctx->retries--; mm_obj_dbg (self, "retrying status check in a second: %d retries left", ctx->retries); g_timeout_add_seconds (1, (GSourceFunc)connect_3gpp_qmistatus, task); return; } /* Already exhausted all retries */ normalized_result = normalize_qmistatus (result); g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "QMI connect failed: %s", normalized_result); g_object_unref (task); g_free (normalized_result); } static gboolean connect_3gpp_qmistatus (GTask *task) { DetailedConnectContext *ctx; ctx = g_task_get_task_data (task); mm_base_modem_at_command_full ( ctx->modem, ctx->primary, "$NWQMISTATUS", 3, /* timeout */ FALSE, /* allow_cached */ FALSE, /* is_raw */ g_task_get_cancellable (task), (GAsyncReadyCallback)connect_3gpp_qmistatus_ready, /* callback */ task); /* user_data */ return G_SOURCE_REMOVE; } static void connect_3gpp_qmiconnect_ready (MMBaseModem *modem, GAsyncResult *res, GTask *task) { const gchar *result; GError *error = NULL; result = mm_base_modem_at_command_full_finish (modem, res, &error); if (!result) { g_task_return_error (task, error); g_object_unref (task); return; } /* * The connection takes a bit of time to set up, but there's no * asynchronous notification from the modem when this has * happened. Instead, we need to poll the modem to see if it's * ready. */ g_timeout_add_seconds (1, (GSourceFunc)connect_3gpp_qmistatus, task); } static void connect_3gpp_authenticate (GTask *task) { MMBroadbandBearerNovatelLte *self; DetailedConnectContext *ctx; MMBearerProperties *config; gchar *command, *apn, *user, *password; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); config = mm_base_bearer_peek_config (MM_BASE_BEARER (self)); apn = mm_port_serial_at_quote_string (mm_bearer_properties_get_apn (config)); user = mm_port_serial_at_quote_string (mm_bearer_properties_get_user (config)); password = mm_port_serial_at_quote_string (mm_bearer_properties_get_password (config)); command = g_strdup_printf ("$NWQMICONNECT=,,,,,,%s,,,%s,%s", apn, user, password); g_free (apn); g_free (user); g_free (password); mm_base_modem_at_command_full ( ctx->modem, ctx->primary, command, 10, /* timeout */ FALSE, /* allow_cached */ FALSE, /* is_raw */ g_task_get_cancellable (task), (GAsyncReadyCallback)connect_3gpp_qmiconnect_ready, task); /* user_data */ g_free (command); } static void connect_3gpp (MMBroadbandBearer *self, MMBroadbandModem *modem, MMPortSerialAt *primary, MMPortSerialAt *secondary, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { DetailedConnectContext *ctx; GTask *task; ctx = g_slice_new0 (DetailedConnectContext); ctx->modem = MM_BASE_MODEM (g_object_ref (modem)); ctx->primary = g_object_ref (primary); ctx->retries = MM_BASE_BEARER_DEFAULT_CONNECTION_TIMEOUT; task = g_task_new (self, cancellable, callback, user_data); g_task_set_task_data (task, ctx, (GDestroyNotify)detailed_connect_context_free); /* Get a 'net' data port */ ctx->data = mm_base_modem_get_best_data_port (ctx->modem, MM_PORT_TYPE_NET); if (!ctx->data) { g_task_return_new_error ( task, MM_CORE_ERROR, MM_CORE_ERROR_CONNECTED, "Couldn't connect: no available net port available"); g_object_unref (task); return; } connect_3gpp_authenticate (task); } /*****************************************************************************/ /* 3GPP Disonnection sequence */ typedef struct { MMBaseModem *modem; MMPortSerialAt *primary; MMPort *data; gint retries; } DetailedDisconnectContext; static void detailed_disconnect_context_free (DetailedDisconnectContext *ctx) { g_object_unref (ctx->data); g_object_unref (ctx->primary); g_object_unref (ctx->modem); g_free (ctx); } static gboolean disconnect_3gpp_finish (MMBroadbandBearer *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static gboolean disconnect_3gpp_qmistatus (GTask *task); static void disconnect_3gpp_status_ready (MMBaseModem *modem, GAsyncResult *res, GTask *task) { MMBroadbandBearerNovatelLte *self; DetailedDisconnectContext *ctx; const gchar *result; GError *error = NULL; gboolean is_connected = FALSE; self = g_task_get_source_object (task); result = mm_base_modem_at_command_full_finish (modem, res, &error); if (result) { mm_obj_dbg (self, "QMI connection status: %s", result); if (is_qmistatus_disconnected (result)) { g_task_return_boolean (task, TRUE); g_object_unref (task); return; } if (is_qmistatus_connected (result)) is_connected = TRUE; } else { mm_obj_dbg (self, "QMI connection status failed: %s", error->message); g_error_free (error); result = "Unknown error"; } ctx = g_task_get_task_data (task); if (ctx->retries > 0) { ctx->retries--; mm_obj_dbg (self, "retrying status check in a second: %d retries left", ctx->retries); g_timeout_add_seconds (1, (GSourceFunc)disconnect_3gpp_qmistatus, task); return; } /* If $NWQMISTATUS reports a CONNECTED QMI state, returns an error such that * the modem state remains 'connected'. Otherwise, assumes the modem is * disconnected from the network successfully. */ if (is_connected) { gchar *normalized_result; normalized_result = normalize_qmistatus (result); g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "QMI disconnect failed: %s", normalized_result); g_free (normalized_result); } else g_task_return_boolean (task, TRUE); g_object_unref (task); } static gboolean disconnect_3gpp_qmistatus (GTask *task) { DetailedDisconnectContext *ctx; ctx = g_task_get_task_data (task); mm_base_modem_at_command_full ( ctx->modem, ctx->primary, "$NWQMISTATUS", 3, /* timeout */ FALSE, /* allow_cached */ FALSE, /* is_raw */ NULL, /* cancellable */ (GAsyncReadyCallback)disconnect_3gpp_status_ready, task); /* user_data */ return G_SOURCE_REMOVE; } static void disconnect_3gpp_check_status (MMBaseModem *modem, GAsyncResult *res, GTask *task) { MMBroadbandBearerNovatelLte *self; GError *error = NULL; self = g_task_get_source_object (task); mm_base_modem_at_command_full_finish (modem, res, &error); if (error) { mm_obj_dbg (self, "disconnection error: %s", error->message); g_error_free (error); } disconnect_3gpp_qmistatus (task); } static void disconnect_3gpp (MMBroadbandBearer *self, MMBroadbandModem *modem, MMPortSerialAt *primary, MMPortSerialAt *secondary, MMPort *data, guint cid, GAsyncReadyCallback callback, gpointer user_data) { DetailedDisconnectContext *ctx; GTask *task; ctx = g_new0 (DetailedDisconnectContext, 1); ctx->modem = MM_BASE_MODEM (g_object_ref (modem)); ctx->primary = g_object_ref (primary); ctx->data = g_object_ref (data); ctx->retries = MM_BASE_BEARER_DEFAULT_DISCONNECTION_TIMEOUT; task = g_task_new (self, NULL, callback, user_data); g_task_set_task_data (task, ctx, (GDestroyNotify)detailed_disconnect_context_free); mm_base_modem_at_command_full ( ctx->modem, ctx->primary, "$NWQMIDISCONNECT", 10, /* timeout */ FALSE, /* allow_cached */ FALSE, /* is_raw */ NULL, /* cancellable */ (GAsyncReadyCallback)disconnect_3gpp_check_status, task); /* user_data */ } /*****************************************************************************/ MMBaseBearer * mm_broadband_bearer_novatel_lte_new_finish (GAsyncResult *res, GError **error) { GObject *bearer; GObject *source; source = g_async_result_get_source_object (res); bearer = g_async_initable_new_finish (G_ASYNC_INITABLE (source), res, error); g_object_unref (source); if (!bearer) return NULL; /* Only export valid bearers */ mm_base_bearer_export (MM_BASE_BEARER (bearer)); return MM_BASE_BEARER (bearer); } void mm_broadband_bearer_novatel_lte_new (MMBroadbandModemNovatelLte *modem, MMBearerProperties *config, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { g_async_initable_new_async ( MM_TYPE_BROADBAND_BEARER_NOVATEL_LTE, G_PRIORITY_DEFAULT, cancellable, callback, user_data, MM_BASE_BEARER_MODEM, modem, MM_BASE_BEARER_CONFIG, config, NULL); } static void mm_broadband_bearer_novatel_lte_init (MMBroadbandBearerNovatelLte *self) { } static void mm_broadband_bearer_novatel_lte_class_init (MMBroadbandBearerNovatelLteClass *klass) { MMBaseBearerClass *base_bearer_class = MM_BASE_BEARER_CLASS (klass); MMBroadbandBearerClass *broadband_bearer_class = MM_BROADBAND_BEARER_CLASS (klass); base_bearer_class->load_connection_status = load_connection_status; base_bearer_class->load_connection_status_finish = load_connection_status_finish; #if defined WITH_SUSPEND_RESUME base_bearer_class->reload_connection_status = load_connection_status; base_bearer_class->reload_connection_status_finish = load_connection_status_finish; #endif broadband_bearer_class->connect_3gpp = connect_3gpp; broadband_bearer_class->connect_3gpp_finish = connect_3gpp_finish; broadband_bearer_class->disconnect_3gpp = disconnect_3gpp; broadband_bearer_class->disconnect_3gpp_finish = disconnect_3gpp_finish; } ModemManager-1.23.4-dev/src/plugins/novatel/mm-broadband-bearer-novatel-lte.h000066400000000000000000000054251456466623000270350ustar00rootroot00000000000000/* -*- 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: * * Author: Nathan Williams * * Copyright (C) 2012 Google, Inc. */ #ifndef MM_BROADBAND_BEARER_NOVATEL_LTE_H #define MM_BROADBAND_BEARER_NOVATEL_LTE_H #include #include #define _LIBMM_INSIDE_MM #include #include "mm-broadband-bearer.h" #include "mm-broadband-modem-novatel-lte.h" #define MM_TYPE_BROADBAND_BEARER_NOVATEL_LTE (mm_broadband_bearer_novatel_lte_get_type ()) #define MM_BROADBAND_BEARER_NOVATEL_LTE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_BROADBAND_BEARER_NOVATEL_LTE, MMBroadbandBearerNovatelLte)) #define MM_BROADBAND_BEARER_NOVATEL_LTE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_BROADBAND_BEARER_NOVATEL_LTE, MMBroadbandBearerNovatelLteClass)) #define MM_IS_BROADBAND_BEARER_NOVATEL_LTE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_BROADBAND_BEARER_NOVATEL_LTE)) #define MM_IS_BROADBAND_BEARER_NOVATEL_LTE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_BROADBAND_BEARER_NOVATEL_LTE)) #define MM_BROADBAND_BEARER_NOVATEL_LTE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_BROADBAND_BEARER_NOVATEL_LTE, MMBroadbandBearerNovatelLteClass)) typedef struct _MMBroadbandBearerNovatelLte MMBroadbandBearerNovatelLte; typedef struct _MMBroadbandBearerNovatelLteClass MMBroadbandBearerNovatelLteClass; struct _MMBroadbandBearerNovatelLte { MMBroadbandBearer parent; }; struct _MMBroadbandBearerNovatelLteClass { MMBroadbandBearerClass parent; }; GType mm_broadband_bearer_novatel_lte_get_type (void); /* Default 3GPP bearer creation implementation */ void mm_broadband_bearer_novatel_lte_new (MMBroadbandModemNovatelLte *modem, MMBearerProperties *properties, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); MMBaseBearer *mm_broadband_bearer_novatel_lte_new_finish (GAsyncResult *res, GError **error); #endif /* MM_BROADBAND_BEARER_NOVATEL_LTE_H */ ModemManager-1.23.4-dev/src/plugins/novatel/mm-broadband-modem-novatel-lte.c000066400000000000000000000574771456466623000267070ustar00rootroot00000000000000/* -*- 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) 2012 Google Inc. * Author: Nathan Williams */ #include #include #include #include #include #include #include "ModemManager.h" #include "mm-base-modem-at.h" #include "mm-broadband-bearer-novatel-lte.h" #include "mm-broadband-modem-novatel-lte.h" #include "mm-sim-novatel-lte.h" #include "mm-errors-types.h" #include "mm-iface-modem.h" #include "mm-iface-modem-3gpp.h" #include "mm-iface-modem-messaging.h" #include "mm-log-object.h" #include "mm-modem-helpers.h" #include "mm-serial-parsers.h" static void iface_modem_init (MMIfaceModem *iface); static void iface_modem_3gpp_init (MMIfaceModem3gpp *iface); static MMIfaceModem3gpp *iface_modem_3gpp_parent; G_DEFINE_TYPE_EXTENDED (MMBroadbandModemNovatelLte, mm_broadband_modem_novatel_lte, MM_TYPE_BROADBAND_MODEM, 0, G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM, iface_modem_init) G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_3GPP, iface_modem_3gpp_init)); /*****************************************************************************/ /* Modem power down (Modem interface) */ static gboolean modem_power_down_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { return !!mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, error); } static void modem_power_down (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data) { mm_base_modem_at_command (MM_BASE_MODEM (self), "+CFUN=4", 6, FALSE, callback, user_data); } /*****************************************************************************/ /* Create Bearer (Modem interface) */ static MMBaseBearer * modem_create_bearer_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { return g_task_propagate_pointer (G_TASK (res), error); } static void broadband_bearer_new_ready (GObject *source, GAsyncResult *res, GTask *task) { MMBaseBearer *bearer = NULL; GError *error = NULL; bearer = mm_broadband_bearer_novatel_lte_new_finish (res, &error); if (!bearer) g_task_return_error (task, error); else g_task_return_pointer (task, bearer, g_object_unref); g_object_unref (task); } static void modem_create_bearer (MMIfaceModem *self, MMBearerProperties *properties, GAsyncReadyCallback callback, gpointer user_data) { /* We just create a MMBroadbandBearer */ mm_broadband_bearer_novatel_lte_new (MM_BROADBAND_MODEM_NOVATEL_LTE (self), properties, NULL, /* cancellable */ (GAsyncReadyCallback)broadband_bearer_new_ready, g_task_new (self, NULL, callback, user_data)); } /*****************************************************************************/ /* Create SIM (Modem interface) */ static MMBaseSim * modem_create_sim_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { return mm_sim_novatel_lte_new_finish (res, error); } static void modem_create_sim (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data) { /* New Novatel LTE SIM */ mm_sim_novatel_lte_new (MM_BASE_MODEM (self), NULL, /* cancellable */ callback, user_data); } /*****************************************************************************/ /* After SIM unlock (Modem interface) */ static gboolean modem_after_sim_unlock_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static gboolean after_sim_unlock_wait_cb (GTask *task) { g_task_return_boolean (task, TRUE); g_object_unref (task); return G_SOURCE_REMOVE; } static void modem_after_sim_unlock (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; task = g_task_new (self, NULL, callback, user_data); /* A 3-second wait is necessary for SIM to become ready. * Otherwise, a subsequent AT+CRSM command will likely fail. */ g_timeout_add_seconds (3, (GSourceFunc)after_sim_unlock_wait_cb, task); } /*****************************************************************************/ /* Load own numbers (Modem interface) */ static GStrv load_own_numbers_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { GVariant *result; GStrv own_numbers; result = mm_base_modem_at_sequence_finish (MM_BASE_MODEM (self), res, NULL, error); if (!result) return NULL; own_numbers = (GStrv) g_variant_dup_strv (result, NULL); return own_numbers; } static MMBaseModemAtResponseProcessorResult response_processor_cnum_ignore_at_errors (MMBaseModem *self, gpointer none, const gchar *command, const gchar *response, gboolean last_command, const GError *error, GVariant **result, GError **result_error) { GStrv own_numbers; *result = NULL; *result_error = NULL; if (error) { /* Ignore AT errors (ie, ERROR or CMx ERROR) */ if (error->domain != MM_MOBILE_EQUIPMENT_ERROR || last_command) { *result_error = g_error_copy (error); return MM_BASE_MODEM_AT_RESPONSE_PROCESSOR_RESULT_FAILURE; } return MM_BASE_MODEM_AT_RESPONSE_PROCESSOR_RESULT_CONTINUE; } own_numbers = mm_3gpp_parse_cnum_exec_response (response); if (!own_numbers) return MM_BASE_MODEM_AT_RESPONSE_PROCESSOR_RESULT_CONTINUE; *result = g_variant_new_strv ((const gchar *const *) own_numbers, -1); g_strfreev (own_numbers); return MM_BASE_MODEM_AT_RESPONSE_PROCESSOR_RESULT_SUCCESS; } static MMBaseModemAtResponseProcessorResult response_processor_nwmdn_ignore_at_errors (MMBaseModem *self, gpointer none, const gchar *command, const gchar *response, gboolean last_command, const GError *error, GVariant **result, GError **result_error) { g_auto(GStrv) own_numbers = NULL; GPtrArray *array; gchar *mdn; *result = NULL; *result_error = NULL; if (error) { /* Ignore AT errors (ie, ERROR or CMx ERROR) */ if (error->domain != MM_MOBILE_EQUIPMENT_ERROR || last_command) { *result_error = g_error_copy (error); return MM_BASE_MODEM_AT_RESPONSE_PROCESSOR_RESULT_FAILURE; } return MM_BASE_MODEM_AT_RESPONSE_PROCESSOR_RESULT_CONTINUE; } mdn = g_strdup (mm_strip_tag (response, "$NWMDN:")); array = g_ptr_array_new (); g_ptr_array_add (array, mdn); g_ptr_array_add (array, NULL); own_numbers = (GStrv) g_ptr_array_free (array, FALSE); *result = g_variant_new_strv ((const gchar *const *) own_numbers, -1); return MM_BASE_MODEM_AT_RESPONSE_PROCESSOR_RESULT_SUCCESS; } static const MMBaseModemAtCommand own_numbers_commands[] = { { "+CNUM", 3, TRUE, response_processor_cnum_ignore_at_errors }, { "$NWMDN", 3, TRUE, response_processor_nwmdn_ignore_at_errors }, { NULL } }; static void load_own_numbers (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data) { mm_base_modem_at_sequence ( MM_BASE_MODEM (self), own_numbers_commands, NULL, /* response_processor_context */ NULL, /* response_processor_context_free */ callback, user_data); } /*****************************************************************************/ /* Load supported bands (Modem interface) */ /* * Mapping from bits set in response of $NWBAND? command to MMModemBand values. * The bit positions and band names on the right come from the response to $NWBAND=? */ static MMModemBand bandbits[] = { MM_MODEM_BAND_CDMA_BC0, /* "00 CDMA2000 Band Class 0, A-System" */ MM_MODEM_BAND_CDMA_BC0, /* "01 CDMA2000 Band Class 0, B-System" */ MM_MODEM_BAND_CDMA_BC1, /* "02 CDMA2000 Band Class 1, all blocks" */ MM_MODEM_BAND_CDMA_BC2, /* "03 CDMA2000 Band Class 2, place holder" */ MM_MODEM_BAND_CDMA_BC3, /* "04 CDMA2000 Band Class 3, A-System" */ MM_MODEM_BAND_CDMA_BC4, /* "05 CDMA2000 Band Class 4, all blocks" */ MM_MODEM_BAND_CDMA_BC5, /* "06 CDMA2000 Band Class 5, all blocks" */ MM_MODEM_BAND_DCS, /* "07 GSM DCS band" */ MM_MODEM_BAND_EGSM, /* "08 GSM Extended GSM (E-GSM) band" */ MM_MODEM_BAND_UNKNOWN, /* "09 GSM Primary GSM (P-GSM) band" */ MM_MODEM_BAND_CDMA_BC6, /* "10 CDMA2000 Band Class 6" */ MM_MODEM_BAND_CDMA_BC7, /* "11 CDMA2000 Band Class 7" */ MM_MODEM_BAND_CDMA_BC8, /* "12 CDMA2000 Band Class 8" */ MM_MODEM_BAND_CDMA_BC9, /* "13 CDMA2000 Band Class 9" */ MM_MODEM_BAND_CDMA_BC10, /* "14 CDMA2000 Band Class 10 */ MM_MODEM_BAND_CDMA_BC11, /* "15 CDMA2000 Band Class 11 */ MM_MODEM_BAND_G450, /* "16 GSM 450 band" */ MM_MODEM_BAND_G480, /* "17 GSM 480 band" */ MM_MODEM_BAND_G750, /* "18 GSM 750 band" */ MM_MODEM_BAND_G850, /* "19 GSM 850 band" */ MM_MODEM_BAND_UNKNOWN, /* "20 GSM 900 Railways band" */ MM_MODEM_BAND_PCS, /* "21 GSM PCS band" */ MM_MODEM_BAND_UTRAN_1, /* "22 WCDMA I IMT 2000 band" */ MM_MODEM_BAND_UTRAN_2, /* "23 WCDMA II PCS band" */ MM_MODEM_BAND_UTRAN_3, /* "24 WCDMA III 1700 band" */ MM_MODEM_BAND_UTRAN_4, /* "25 WCDMA IV 1700 band" */ MM_MODEM_BAND_UTRAN_5, /* "26 WCDMA V US850 band" */ MM_MODEM_BAND_UTRAN_6, /* "27 WCDMA VI JAPAN 800 band" */ MM_MODEM_BAND_UNKNOWN, /* "28 Reserved for BC12/BC14 */ MM_MODEM_BAND_UNKNOWN, /* "29 Reserved for BC12/BC14 */ MM_MODEM_BAND_UNKNOWN, /* "30 Reserved" */ MM_MODEM_BAND_UNKNOWN, /* "31 Reserved" */ }; static GArray * load_supported_bands_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { return g_task_propagate_pointer (G_TASK (res), error); } static void load_supported_bands (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; GArray *bands; guint i; task = g_task_new (self, NULL, callback, user_data); /* * The modem doesn't support telling us what bands are supported; * list everything we know about. */ bands = g_array_sized_new (FALSE, FALSE, sizeof (MMModemBand), 23); for (i = 0 ; i < G_N_ELEMENTS (bandbits) ; i++) { if (bandbits[i] != MM_MODEM_BAND_UNKNOWN) g_array_append_val(bands, bandbits[i]); } g_task_return_pointer (task, bands, (GDestroyNotify)g_array_unref); g_object_unref (task); } /*****************************************************************************/ /* Load current bands (Modem interface) */ static GArray * load_current_bands_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { return g_task_propagate_pointer (G_TASK (res), error); } static void load_current_bands_done (MMIfaceModem *self, GAsyncResult *res, GTask *task) { GArray *bands; const gchar *response; GError *error = NULL; guint i; guint32 bandval; response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error); if (!response) { g_task_return_error (task, error); g_object_unref (task); return; } /* * Response is "$NWBAND: ", where the hex value is a * bitmask of supported bands. */ bandval = (guint32)strtoul(response + 9, NULL, 16); bands = g_array_sized_new (FALSE, FALSE, sizeof (MMModemBand), 4); for (i = 0 ; i < G_N_ELEMENTS (bandbits) ; i++) { if ((bandval & (1 << i)) && bandbits[i] != MM_MODEM_BAND_UNKNOWN) g_array_append_val(bands, bandbits[i]); } g_task_return_pointer (task, bands, (GDestroyNotify)g_array_unref); g_object_unref (task); } static void load_current_bands (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data) { mm_base_modem_at_command ( MM_BASE_MODEM (self), "$NWBAND?", 3, FALSE, (GAsyncReadyCallback)load_current_bands_done, g_task_new (self, NULL, callback, user_data)); } /*****************************************************************************/ /* Load unlock retries (Modem interface) */ static MMUnlockRetries * load_unlock_retries_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { return g_task_propagate_pointer (G_TASK (res), error); } static void load_unlock_retries_ready (MMBaseModem *self, GAsyncResult *res, GTask *task) { const gchar *response; GError *error = NULL; gint pin_num, pin_value; int scan_count; response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error); if (!response) { g_task_return_error (task, error); g_object_unref (task); return; } response = mm_strip_tag (response, "$NWPINR:"); scan_count = sscanf (response, "PIN%d, %d", &pin_num, &pin_value); if (scan_count != 2 || (pin_num != 1 && pin_num != 2)) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Invalid unlock retries response: '%s'", response); } else { MMUnlockRetries *retries; retries = mm_unlock_retries_new (); mm_unlock_retries_set (retries, pin_num == 1 ? MM_MODEM_LOCK_SIM_PIN : MM_MODEM_LOCK_SIM_PIN2, pin_value); g_task_return_pointer (task, retries, g_object_unref); } g_object_unref (task); } static void load_unlock_retries (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data) { mm_base_modem_at_command (MM_BASE_MODEM (self), "$NWPINR?", 20, FALSE, (GAsyncReadyCallback)load_unlock_retries_ready, g_task_new (self, NULL, callback, user_data)); } /*****************************************************************************/ /* Load access technologies (Modem interface) */ static gboolean load_access_technologies_finish (MMIfaceModem *self, GAsyncResult *res, MMModemAccessTechnology *access_technologies, guint *mask, GError **error) { GError *inner_error = NULL; gssize value; value = g_task_propagate_int (G_TASK (res), &inner_error); if (inner_error) { g_propagate_error (error, inner_error); return FALSE; } *access_technologies = (MMModemAccessTechnology) value; *mask = MM_MODEM_ACCESS_TECHNOLOGY_ANY; return TRUE; } static void load_access_technologies_ready (MMIfaceModem *self, GAsyncResult *res, GTask *task) { const gchar *response; MMModemAccessTechnology act; GError *error = NULL; response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error); if (!response) { g_task_return_error (task, error); g_object_unref (task); return; } act = MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN; if (strstr (response, "LTE")) act |= MM_MODEM_ACCESS_TECHNOLOGY_LTE; if (strstr (response, "WCDMA")) act |= MM_MODEM_ACCESS_TECHNOLOGY_UMTS; if (strstr (response, "EV-DO Rev 0")) act |= MM_MODEM_ACCESS_TECHNOLOGY_EVDO0; if (strstr (response, "EV-DO Rev A")) act |= MM_MODEM_ACCESS_TECHNOLOGY_EVDOA; if (strstr (response, "CDMA 1X")) act |= MM_MODEM_ACCESS_TECHNOLOGY_1XRTT; if (strstr (response, "GSM")) act |= MM_MODEM_ACCESS_TECHNOLOGY_GSM; g_task_return_int (task, act); g_object_unref (task); } static void load_access_technologies (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data) { mm_base_modem_at_command ( MM_BASE_MODEM (self), "$NWSYSMODE", 3, FALSE, (GAsyncReadyCallback)load_access_technologies_ready, g_task_new (self, NULL, callback, user_data)); } /*****************************************************************************/ /* Reset (Modem interface) */ static gboolean reset_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { return !!mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, error); } static void reset (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data) { mm_base_modem_at_command (MM_BASE_MODEM (self), "+CFUN=6", 3, FALSE, callback, user_data); } /*****************************************************************************/ /* Scan networks (3GPP interface) */ static GList * scan_networks_finish (MMIfaceModem3gpp *self, GAsyncResult *res, GError **error) { return g_task_propagate_pointer (G_TASK (res), error); } static void parent_scan_networks_ready (MMIfaceModem3gpp *self, GAsyncResult *res, GTask *task) { GError *error = NULL; GList *scan_result; scan_result = iface_modem_3gpp_parent->scan_networks_finish (self, res, &error); if (!scan_result) g_task_return_error (task, error); else g_task_return_pointer (task, scan_result, (GDestroyNotify)mm_3gpp_network_info_list_free); g_object_unref (task); } static void scan_networks (MMIfaceModem3gpp *self, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; MMModemAccessTechnology access_tech; mm_obj_dbg (self, "scanning for networks (Novatel LTE)..."); task = g_task_new (self, NULL, callback, user_data); /* The Novatel LTE modem does not properly support AT+COPS=? in LTE mode. * Thus, do not try to scan networks when the current access technologies * include LTE. */ access_tech = mm_iface_modem_get_access_technologies (MM_IFACE_MODEM (self)); if (access_tech & MM_MODEM_ACCESS_TECHNOLOGY_LTE) { g_autofree gchar *access_tech_string = NULL; access_tech_string = mm_modem_access_technology_build_string_from_mask (access_tech); mm_obj_warn (self, "couldn't scan for networks with access technologies: %s", access_tech_string); g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "Couldn't scan for networks with access technologies: %s", access_tech_string); g_object_unref (task); return; } /* Otherwise, just fallback to the generic scan method */ iface_modem_3gpp_parent->scan_networks (self, (GAsyncReadyCallback)parent_scan_networks_ready, task); } /*****************************************************************************/ MMBroadbandModemNovatelLte * mm_broadband_modem_novatel_lte_new (const gchar *device, const gchar *physdev, const gchar **drivers, const gchar *plugin, guint16 vendor_id, guint16 product_id) { return g_object_new (MM_TYPE_BROADBAND_MODEM_NOVATEL_LTE, MM_BASE_MODEM_DEVICE, device, MM_BASE_MODEM_PHYSDEV, physdev, MM_BASE_MODEM_DRIVERS, drivers, MM_BASE_MODEM_PLUGIN, plugin, MM_BASE_MODEM_VENDOR_ID, vendor_id, MM_BASE_MODEM_PRODUCT_ID, product_id, /* Novatel LTE bearer supports NET only */ MM_BASE_MODEM_DATA_NET_SUPPORTED, TRUE, MM_BASE_MODEM_DATA_TTY_SUPPORTED, FALSE, NULL); } static void mm_broadband_modem_novatel_lte_init (MMBroadbandModemNovatelLte *self) { } static void iface_modem_init (MMIfaceModem *iface) { iface->modem_power_down = modem_power_down; iface->modem_power_down_finish = modem_power_down_finish; iface->create_bearer = modem_create_bearer; iface->create_bearer_finish = modem_create_bearer_finish; iface->create_sim = modem_create_sim; iface->create_sim_finish = modem_create_sim_finish; iface->modem_after_sim_unlock = modem_after_sim_unlock; iface->modem_after_sim_unlock_finish = modem_after_sim_unlock_finish; iface->load_own_numbers = load_own_numbers; iface->load_own_numbers_finish = load_own_numbers_finish; iface->load_supported_bands = load_supported_bands; iface->load_supported_bands_finish = load_supported_bands_finish; iface->load_current_bands = load_current_bands; iface->load_current_bands_finish = load_current_bands_finish; iface->load_unlock_retries = load_unlock_retries; iface->load_unlock_retries_finish = load_unlock_retries_finish; /* No support for setting bands, as it destabilizes the modem. */ iface->load_access_technologies = load_access_technologies; iface->load_access_technologies_finish = load_access_technologies_finish; iface->reset = reset; iface->reset_finish = reset_finish; } static void iface_modem_3gpp_init (MMIfaceModem3gpp *iface) { iface_modem_3gpp_parent = g_type_interface_peek_parent (iface); iface->scan_networks = scan_networks; iface->scan_networks_finish = scan_networks_finish; } static void mm_broadband_modem_novatel_lte_class_init (MMBroadbandModemNovatelLteClass *klass) { } ModemManager-1.23.4-dev/src/plugins/novatel/mm-broadband-modem-novatel-lte.h000066400000000000000000000051551456466623000266760ustar00rootroot00000000000000/* -*- 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) 2012 Google Inc. * Author: Nathan Williams */ #ifndef MM_BROADBAND_MODEM_NOVATEL_LTE_H #define MM_BROADBAND_MODEM_NOVATEL_LTE_H #include "mm-broadband-modem.h" #define MM_TYPE_BROADBAND_MODEM_NOVATEL_LTE (mm_broadband_modem_novatel_lte_get_type ()) #define MM_BROADBAND_MODEM_NOVATEL_LTE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_BROADBAND_MODEM_NOVATEL_LTE, MMBroadbandModemNovatelLte)) #define MM_BROADBAND_MODEM_NOVATEL_LTE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_BROADBAND_MODEM_NOVATEL_LTE, MMBroadbandModemNovatelLteClass)) #define MM_IS_BROADBAND_MODEM_NOVATEL_LTE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_BROADBAND_MODEM_NOVATEL_LTE)) #define MM_IS_BROADBAND_MODEM_NOVATEL_LTE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_BROADBAND_MODEM_NOVATEL_LTE)) #define MM_BROADBAND_MODEM_NOVATEL_LTE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_BROADBAND_MODEM_NOVATEL_LTE, MMBroadbandModemNovatelLteClass)) typedef struct _MMBroadbandModemNovatelLte MMBroadbandModemNovatelLte; typedef struct _MMBroadbandModemNovatelLteClass MMBroadbandModemNovatelLteClass; typedef struct _MMBroadbandModemNovatelLtePrivate MMBroadbandModemNovatelLtePrivate; struct _MMBroadbandModemNovatelLte { MMBroadbandModem parent; MMBroadbandModemNovatelLtePrivate *priv; }; struct _MMBroadbandModemNovatelLteClass{ MMBroadbandModemClass parent; }; GType mm_broadband_modem_novatel_lte_get_type (void); MMBroadbandModemNovatelLte *mm_broadband_modem_novatel_lte_new (const gchar *device, const gchar *physdev, const gchar **drivers, const gchar *plugin, guint16 vendor_id, guint16 product_id); #endif /* MM_BROADBAND_MODEM_NOVATEL_LTE_H */ ModemManager-1.23.4-dev/src/plugins/novatel/mm-broadband-modem-novatel.c000066400000000000000000001473411456466623000261130ustar00rootroot00000000000000/* -*- 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 - 2012 Red Hat, Inc. * Copyright (C) 2012 Aleksander Morgado */ #include #include #include #include #include #include #include "ModemManager.h" #include "mm-base-modem-at.h" #include "mm-iface-modem.h" #include "mm-iface-modem-3gpp.h" #include "mm-iface-modem-cdma.h" #include "mm-iface-modem-time.h" #include "mm-iface-modem-messaging.h" #include "mm-broadband-modem-novatel.h" #include "mm-errors-types.h" #include "mm-modem-helpers.h" #include "mm-common-helpers.h" #include "libqcdm/src/commands.h" #include "libqcdm/src/result.h" #include "mm-log-object.h" static void iface_modem_init (MMIfaceModem *iface); static void iface_modem_messaging_init (MMIfaceModemMessaging *iface); static void iface_modem_cdma_init (MMIfaceModemCdma *iface); static void iface_modem_time_init (MMIfaceModemTime *iface); static MMIfaceModem *iface_modem_parent; G_DEFINE_TYPE_EXTENDED (MMBroadbandModemNovatel, mm_broadband_modem_novatel, MM_TYPE_BROADBAND_MODEM, 0, G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM, iface_modem_init) G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_MESSAGING, iface_modem_messaging_init) G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_CDMA, iface_modem_cdma_init) G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_TIME, iface_modem_time_init)) /*****************************************************************************/ /* Load supported modes (Modem interface) */ static GArray * load_supported_modes_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { return g_task_propagate_pointer (G_TASK (res), error); } static void parent_load_supported_modes_ready (MMIfaceModem *self, GAsyncResult *res, GTask *task) { GError *error = NULL; GArray *all; GArray *combinations; GArray *filtered; MMModemModeCombination mode; all = iface_modem_parent->load_supported_modes_finish (self, res, &error); if (!all) { g_task_return_error (task, error); g_object_unref (task); return; } /* Build list of combinations */ combinations = g_array_sized_new (FALSE, FALSE, sizeof (MMModemModeCombination), 5); /* 2G only */ mode.allowed = MM_MODEM_MODE_2G; mode.preferred = MM_MODEM_MODE_NONE; g_array_append_val (combinations, mode); /* 3G only */ mode.allowed = MM_MODEM_MODE_3G; mode.preferred = MM_MODEM_MODE_NONE; g_array_append_val (combinations, mode); /* 2G and 3G */ mode.allowed = (MM_MODEM_MODE_2G | MM_MODEM_MODE_3G); mode.preferred = MM_MODEM_MODE_NONE; g_array_append_val (combinations, mode); /* 2G and 3G, 2G preferred */ mode.allowed = (MM_MODEM_MODE_2G | MM_MODEM_MODE_3G); mode.preferred = MM_MODEM_MODE_2G; g_array_append_val (combinations, mode); /* 2G and 3G, 3G preferred */ mode.allowed = (MM_MODEM_MODE_2G | MM_MODEM_MODE_3G); mode.preferred = MM_MODEM_MODE_3G; g_array_append_val (combinations, mode); /* Filter out those unsupported modes */ filtered = mm_filter_supported_modes (all, combinations, self); g_array_unref (all); g_array_unref (combinations); g_task_return_pointer (task, filtered, (GDestroyNotify) g_array_unref); g_object_unref (task); } static void load_supported_modes (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data) { /* Run parent's loading */ iface_modem_parent->load_supported_modes ( MM_IFACE_MODEM (self), (GAsyncReadyCallback)parent_load_supported_modes_ready, g_task_new (self, NULL, callback, user_data)); } /*****************************************************************************/ /* Load initial allowed/preferred modes (Modem interface) */ typedef struct { MMModemMode allowed; MMModemMode preferred; } LoadCurrentModesResult; static gboolean load_current_modes_finish (MMIfaceModem *self, GAsyncResult *res, MMModemMode *allowed, MMModemMode *preferred, GError **error) { LoadCurrentModesResult *result; result = g_task_propagate_pointer (G_TASK (res), error); if (!result) return FALSE; *allowed = result->allowed; *preferred = result->preferred; g_free (result); return TRUE; } static void nwrat_query_ready (MMBaseModem *self, GAsyncResult *res, GTask *task) { LoadCurrentModesResult *result; GError *error = NULL; const gchar *response; g_autoptr(GRegex) r = NULL; g_autoptr(GMatchInfo) match_info = NULL; gint a = -1; gint b = -1; response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error); if (!response) { g_task_return_error (task, error); g_object_unref (task); return; } /* Parse response */ r = g_regex_new ("\\$NWRAT:\\s*(\\d),(\\d),(\\d)", G_REGEX_UNGREEDY, 0, NULL); g_assert (r != NULL); if (!g_regex_match_full (r, response, strlen (response), 0, 0, &match_info, &error)) { if (error) g_task_return_error (task, error); else g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't match NWRAT reply: %s", response); g_object_unref (task); return; } if (!mm_get_int_from_match_info (match_info, 1, &a) || !mm_get_int_from_match_info (match_info, 2, &b) || a < 0 || a > 2 || b < 1 || b > 2) { g_task_return_new_error ( task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Failed to parse mode/tech response '%s': invalid modes reported", response); g_object_unref (task); return; } result = g_new0 (LoadCurrentModesResult, 1); switch (a) { case 0: result->allowed = (MM_MODEM_MODE_2G | MM_MODEM_MODE_3G); result->preferred = MM_MODEM_MODE_NONE; break; case 1: if (b == 1) { result->allowed = MM_MODEM_MODE_2G; result->preferred = MM_MODEM_MODE_NONE; } else /* b == 2 */ { result->allowed = (MM_MODEM_MODE_2G | MM_MODEM_MODE_3G); result->preferred = MM_MODEM_MODE_2G; } break; case 2: if (b == 1) { result->allowed = MM_MODEM_MODE_3G; result->preferred = MM_MODEM_MODE_NONE; } else /* b == 2 */ { result->allowed = (MM_MODEM_MODE_2G | MM_MODEM_MODE_3G); result->preferred = MM_MODEM_MODE_3G; } break; default: /* We only allow mode 0|1|2 */ g_assert_not_reached (); break; } g_task_return_pointer (task, result, g_free); g_object_unref (task); } static void load_current_modes (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; task = g_task_new (self, NULL, callback, user_data); /* Load allowed modes only in 3GPP modems */ if (!mm_iface_modem_is_3gpp (self)) { g_task_return_new_error ( task, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "Loading allowed modes not supported in CDMA-only modems"); g_object_unref (task); return; } mm_base_modem_at_command (MM_BASE_MODEM (self), "$NWRAT?", 3, FALSE, (GAsyncReadyCallback)nwrat_query_ready, task); } /*****************************************************************************/ /* Set allowed modes (Modem interface) */ static gboolean set_current_modes_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void allowed_mode_update_ready (MMBroadbandModemNovatel *self, GAsyncResult *res, GTask *task) { GError *error = NULL; mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error); if (error) /* Let the error be critical. */ g_task_return_error (task, error); else g_task_return_boolean (task, TRUE); g_object_unref (task); } static void set_current_modes (MMIfaceModem *self, MMModemMode allowed, MMModemMode preferred, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; gchar *command; gint a = -1; gint b = -1; task = g_task_new (self, NULL, callback, user_data); /* Setting allowed modes only in 3GPP modems */ if (!mm_iface_modem_is_3gpp (self)) { g_task_return_new_error ( task, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "Setting allowed modes not supported in CDMA-only modems"); g_object_unref (task); return; } if (allowed == MM_MODEM_MODE_2G) { a = 1; b = 1; } else if (allowed == MM_MODEM_MODE_3G) { a = 2; b = 1; } else if (allowed == (MM_MODEM_MODE_2G | MM_MODEM_MODE_3G)) { b = 2; if (preferred == MM_MODEM_MODE_NONE) a = 0; else if (preferred == MM_MODEM_MODE_2G) a = 1; else if (preferred == MM_MODEM_MODE_3G) a = 2; } else if (allowed == MM_MODEM_MODE_ANY && preferred == MM_MODEM_MODE_NONE) { b = 2; a = 0; } if (a < 0 || b < 0) { gchar *allowed_str; gchar *preferred_str; allowed_str = mm_modem_mode_build_string_from_mask (allowed); preferred_str = mm_modem_mode_build_string_from_mask (preferred); g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Requested mode (allowed: '%s', preferred: '%s') not " "supported by the modem.", allowed_str, preferred_str); g_object_unref (task); g_free (allowed_str); g_free (preferred_str); return; } command = g_strdup_printf ("AT$NWRAT=%d,%d", a, b); mm_base_modem_at_command ( MM_BASE_MODEM (self), command, 3, FALSE, (GAsyncReadyCallback)allowed_mode_update_ready, task); g_free (command); } /*****************************************************************************/ static void close_and_unref_port (MMPortSerialQcdm *port) { mm_port_serial_close (MM_PORT_SERIAL (port)); g_object_unref (port); } static gboolean get_evdo_version_finish (MMBaseModem *self, GAsyncResult *res, guint *hdr_revision, /* QCDM_HDR_REV_* */ GError **error) { gssize result; result = g_task_propagate_int (G_TASK (res), error); if (result < 0) return FALSE; *hdr_revision = (guint8) result; return TRUE; } static void nw_snapshot_old_ready (MMPortSerialQcdm *port, GAsyncResult *res, GTask *task) { QcdmResult *result; GError *error = NULL; GByteArray *response; guint8 hdr_revision = QCDM_HDR_REV_UNKNOWN; response = mm_port_serial_qcdm_command_finish (port, res, &error); if (error) { /* Just ignore the error and complete with the input info */ g_prefix_error (&error, "Couldn't run QCDM Novatel Modem MSM6500 snapshot: "); g_task_return_error (task, error); g_object_unref (task); return; } /* Parse the response */ result = qcdm_cmd_nw_subsys_modem_snapshot_cdma_result ((const gchar *) response->data, response->len, NULL); g_byte_array_unref (response); if (!result) { g_prefix_error (&error, "Failed to get QCDM Novatel Modem MSM6500 snapshot: "); g_task_return_error (task, error); g_object_unref (task); return; } /* Success */ qcdm_result_get_u8 (result, QCDM_CMD_NW_SUBSYS_MODEM_SNAPSHOT_CDMA_ITEM_HDR_REV, &hdr_revision); qcdm_result_unref (result); g_task_return_int (task, (gint) hdr_revision); g_object_unref (task); } static void nw_snapshot_new_ready (MMPortSerialQcdm *port, GAsyncResult *res, GTask *task) { MMBroadbandModemNovatel *self; QcdmResult *result; GByteArray *nwsnap; GError *error = NULL; GByteArray *response; guint8 hdr_revision = QCDM_HDR_REV_UNKNOWN; self = g_task_get_source_object (task); response = mm_port_serial_qcdm_command_finish (port, res, &error); if (error) { g_prefix_error (&error, "couldn't run QCDM Novatel Modem MSM6800 snapshot: "); g_task_return_error (task, error); g_object_unref (task); return; } /* Parse the response */ result = qcdm_cmd_nw_subsys_modem_snapshot_cdma_result ((const gchar *) response->data, response->len, NULL); g_byte_array_unref (response); if (result) { /* Success */ qcdm_result_get_u8 (result, QCDM_CMD_NW_SUBSYS_MODEM_SNAPSHOT_CDMA_ITEM_HDR_REV, &hdr_revision); qcdm_result_unref (result); g_task_return_int (task, (gint) hdr_revision); g_object_unref (task); return; } mm_obj_dbg (self, "failed to get QCDM Novatel Modem MSM6800 snapshot"); /* Try for MSM6500 */ nwsnap = g_byte_array_sized_new (25); nwsnap->len = qcdm_cmd_nw_subsys_modem_snapshot_cdma_new ((char *) nwsnap->data, 25, QCDM_NW_CHIPSET_6500); g_assert (nwsnap->len); mm_port_serial_qcdm_command (port, nwsnap, 3, NULL, (GAsyncReadyCallback)nw_snapshot_old_ready, task); g_byte_array_unref (nwsnap); } static void get_evdo_version (MMBaseModem *self, GAsyncReadyCallback callback, gpointer user_data) { GError *error = NULL; GByteArray *nwsnap; GTask *task; MMPortSerialQcdm *port; task = g_task_new (self, NULL, callback, user_data); port = mm_base_modem_get_port_qcdm (self); if (!port) { error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "No available QCDM port"); g_task_return_error (task, error); g_object_unref (task); return; } g_task_set_task_data (task, port, (GDestroyNotify) close_and_unref_port); if (!mm_port_serial_open (MM_PORT_SERIAL (port), &error)) { g_prefix_error (&error, "couldn't open QCDM port: "); g_task_return_error (task, error); g_object_unref (task); return; } /* Try MSM6800 first since newer cards use that */ nwsnap = g_byte_array_sized_new (25); nwsnap->len = qcdm_cmd_nw_subsys_modem_snapshot_cdma_new ((char *) nwsnap->data, 25, QCDM_NW_CHIPSET_6800); g_assert (nwsnap->len); mm_port_serial_qcdm_command (port, nwsnap, 3, NULL, (GAsyncReadyCallback)nw_snapshot_new_ready, task); g_byte_array_unref (nwsnap); } /*****************************************************************************/ /* Load access technologies (Modem interface) */ typedef struct { MMModemAccessTechnology act; guint mask; guint hdr_revision; /* QCDM_HDR_REV_* */ } AccessTechContext; static gboolean modem_load_access_technologies_finish (MMIfaceModem *self, GAsyncResult *res, MMModemAccessTechnology *access_technologies, guint *mask, GError **error) { AccessTechContext *ctx; if (!g_task_propagate_boolean (G_TASK (res), error)) return FALSE; /* Update access technology with specific EVDO revision from QCDM if we have them */ ctx = g_task_get_task_data (G_TASK (res)); if (ctx->act & MM_IFACE_MODEM_CDMA_ALL_EVDO_ACCESS_TECHNOLOGIES_MASK) { if (ctx->hdr_revision == QCDM_HDR_REV_0) { mm_obj_dbg (self, "modem snapshot EVDO revision: 0"); ctx->act &= ~MM_IFACE_MODEM_CDMA_ALL_EVDO_ACCESS_TECHNOLOGIES_MASK; ctx->act |= MM_MODEM_ACCESS_TECHNOLOGY_EVDO0; } else if (ctx->hdr_revision == QCDM_HDR_REV_A) { mm_obj_dbg (self, "modem snapshot EVDO revision: A"); ctx->act &= ~MM_IFACE_MODEM_CDMA_ALL_EVDO_ACCESS_TECHNOLOGIES_MASK; ctx->act |= MM_MODEM_ACCESS_TECHNOLOGY_EVDOA; } else mm_obj_dbg (self, "modem snapshot EVDO revision: %d (unknown)", ctx->hdr_revision); } *access_technologies = ctx->act; *mask = ctx->mask; return TRUE; } static void cnti_set_ready (MMBaseModem *self, GAsyncResult *res, GTask *task) { AccessTechContext *ctx = g_task_get_task_data (task); GError *error = NULL; const gchar *response; const gchar *p; response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error); if (!response) { g_task_return_error (task, error); g_object_unref (task); return; } p = mm_strip_tag (response, "$CNTI:"); p = strchr (p, ','); if (!p) { error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't parse $CNTI result '%s'", response); g_task_return_error (task, error); g_object_unref (task); return; } ctx->act = mm_string_to_access_tech (p); ctx->mask = MM_IFACE_MODEM_3GPP_ALL_ACCESS_TECHNOLOGIES_MASK; g_task_return_boolean (task, TRUE); g_object_unref (task); } static void evdo_version_ready (MMBaseModem *self, GAsyncResult *res, GTask *task) { GError *error = NULL; AccessTechContext *ctx = g_task_get_task_data (task); if (!get_evdo_version_finish (self, res, &ctx->hdr_revision, &error)) { g_task_return_error (task, error); g_object_unref (task); return; } g_task_return_boolean (task, TRUE); g_object_unref (task); } static void parent_load_access_technologies_ready (MMIfaceModem *self, GAsyncResult *res, GTask *task) { GError *error = NULL; AccessTechContext *ctx = g_task_get_task_data (task); if (!iface_modem_parent->load_access_technologies_finish (self, res, &ctx->act, &ctx->mask, &error)) { g_task_return_error (task, error); g_object_unref (task); return; } /* No point in checking EVDO revision if EVDO isn't being used */ if (!(ctx->act & MM_IFACE_MODEM_CDMA_ALL_EVDO_ACCESS_TECHNOLOGIES_MASK)) { g_task_return_boolean (task, TRUE); g_object_unref (task); return; } /* Get the EVDO revision from QCDM */ get_evdo_version (MM_BASE_MODEM (self), (GAsyncReadyCallback) evdo_version_ready, task); } static void modem_load_access_technologies (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data) { AccessTechContext *ctx; GTask *task; /* Setup context */ task = g_task_new (self, NULL, callback, user_data); ctx = g_new0 (AccessTechContext, 1); g_task_set_task_data (task, ctx, g_free); /* CDMA-only modems defer to parent for generic access technology * checking, but can determine EVDOr0 vs. EVDOrA through proprietary * QCDM commands. */ if (mm_iface_modem_is_cdma_only (self)) { iface_modem_parent->load_access_technologies ( self, (GAsyncReadyCallback)parent_load_access_technologies_ready, task); return; } mm_base_modem_at_command ( MM_BASE_MODEM (self), "$CNTI=0", 3, FALSE, (GAsyncReadyCallback)cnti_set_ready, task); } /*****************************************************************************/ /* Signal quality loading (Modem interface) */ static guint modem_load_signal_quality_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { GError *inner_error = NULL; gssize value; value = g_task_propagate_int (G_TASK (res), &inner_error); if (inner_error) { g_propagate_error (error, inner_error); return 0; } return (guint)value; } static void parent_load_signal_quality_ready (MMIfaceModem *self, GAsyncResult *res, GTask *task) { GError *error = NULL; guint signal_quality; signal_quality = iface_modem_parent->load_signal_quality_finish (self, res, &error); if (error) g_task_return_error (task, error); else g_task_return_int (task, signal_quality); g_object_unref (task); } static gint get_one_quality (const gchar *reply, const gchar *tag) { gint quality = -1; char *temp, *p; gint dbm; gboolean success = FALSE; p = strstr (reply, tag); if (!p) return -1; /* Skip the tag */ p += strlen (tag); /* Skip spaces */ while (isspace (*p)) p++; p = temp = g_strdup (p); /* Cut off the string after the dBm */ while (isdigit (*p) || (*p == '-')) p++; *p = '\0'; /* When registered with EVDO, RX0/RX1 are returned by many cards with * negative dBm. When registered only with 1x, some cards return "1x RSSI" * with positive dBm. */ if (mm_get_int_from_str (temp, &dbm)) { if (*temp == '-') { /* Some cards appear to use RX0/RX1 and output RSSI in negative dBm */ if (dbm < 0) success = TRUE; } else if (isdigit (*temp) && (dbm > 0) && (dbm <= 125)) { /* S720 appears to use "1x RSSI" and print RSSI in dBm without '-' */ dbm *= -1; success = TRUE; } } if (success) quality = MM_RSSI_TO_QUALITY (dbm); g_free (temp); return quality; } static void nwrssi_ready (MMBaseModem *self, GAsyncResult *res, GTask *task) { const gchar *response; gint quality; response = mm_base_modem_at_command_finish (self, res, NULL); if (!response) { /* Fallback to parent's method */ iface_modem_parent->load_signal_quality ( MM_IFACE_MODEM (self), (GAsyncReadyCallback)parent_load_signal_quality_ready, task); return; } /* Parse the signal quality */ quality = get_one_quality (response, "RX0="); if (quality < 0) quality = get_one_quality (response, "1x RSSI="); if (quality < 0) quality = get_one_quality (response, "RX1="); if (quality < 0) quality = get_one_quality (response, "HDR RSSI="); if (quality >= 0) g_task_return_int (task, quality); else g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't parse $NWRSSI response: '%s'", response); g_object_unref (task); } static void modem_load_signal_quality (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; task = g_task_new (self, NULL, callback, user_data); /* 3GPP modems can just run parent's signal quality loading */ if (mm_iface_modem_is_3gpp (self)) { iface_modem_parent->load_signal_quality ( self, (GAsyncReadyCallback)parent_load_signal_quality_ready, task); return; } /* CDMA modems need custom signal quality loading */ mm_base_modem_at_command ( MM_BASE_MODEM (self), "$NWRSSI", 3, FALSE, (GAsyncReadyCallback)nwrssi_ready, task); } /*****************************************************************************/ /* Automatic activation (CDMA interface) */ static gboolean modem_cdma_activate_finish (MMIfaceModemCdma *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void qcmipgetp_ready (MMBaseModem *self, GAsyncResult *res, GTask *task) { GError *error = NULL; const gchar *response; response = mm_base_modem_at_command_finish (self, res, &error); if (!response) g_task_return_error (task, error); else { mm_obj_dbg (self, "current profile information retrieved: %s", response); g_task_return_boolean (task, TRUE); } g_object_unref (task); } static void activate_cdv_ready (MMBaseModem *self, GAsyncResult *res, GTask *task) { GError *error = NULL; const gchar *response; response = mm_base_modem_at_command_finish (self, res, &error); if (!response) { g_task_return_error (task, error); g_object_unref (task); return; } /* Let's query the MIP profile */ mm_base_modem_at_command (self, "$QCMIPGETP", 20, FALSE, (GAsyncReadyCallback)qcmipgetp_ready, task); } static void modem_cdma_activate (MMIfaceModemCdma *self, const gchar *carrier_code, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; gchar *cmd; task = g_task_new (self, NULL, callback, user_data); cmd = g_strdup_printf ("+CDV=%s", carrier_code); mm_base_modem_at_command (MM_BASE_MODEM (self), cmd, 20, FALSE, (GAsyncReadyCallback)activate_cdv_ready, task); g_free (cmd); } /*****************************************************************************/ /* Manual activation (CDMA interface) */ /* Wait up to 2 minutes */ #define MAX_IOTA_QUERY_RETRIES 24 #define MAX_IOTA_QUERY_RETRY_TIME 5 typedef enum { CDMA_ACTIVATION_STEP_FIRST, CDMA_ACTIVATION_STEP_REQUEST_ACTIVATION, CDMA_ACTIVATION_STEP_OTA_UPDATE, CDMA_ACTIVATION_STEP_PRL_UPDATE, CDMA_ACTIVATION_STEP_WAIT_UNTIL_FINISHED, CDMA_ACTIVATION_STEP_LAST } CdmaActivationStep; typedef struct { CdmaActivationStep step; MMCdmaManualActivationProperties *properties; guint wait_timeout_id; guint wait_retries; } CdmaActivationContext; static void cdma_activation_context_free (CdmaActivationContext *ctx) { g_assert (ctx->wait_timeout_id == 0); g_object_unref (ctx->properties); g_slice_free (CdmaActivationContext, ctx); } static gboolean modem_cdma_activate_manual_finish (MMIfaceModemCdma *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void cdma_activation_step (GTask *task); static gboolean cdma_activation_step_retry (GTask *task) { CdmaActivationContext *ctx; ctx = g_task_get_task_data (task); ctx->wait_timeout_id = 0; cdma_activation_step (task); return G_SOURCE_REMOVE; } static void iota_query_ready (MMBaseModem *self, GAsyncResult *res, GTask *task) { GError *error = NULL; const gchar *response; CdmaActivationContext *ctx; response = mm_base_modem_at_command_finish (self, res, &error); if (!response) { g_task_return_error (task, error); g_object_unref (task); return; } ctx = g_task_get_task_data (task); /* Finished? */ if (strstr (response, "IOTA Enabled")) { ctx->step++; cdma_activation_step (task); return; } /* Too many retries? */ if (ctx->wait_retries == MAX_IOTA_QUERY_RETRIES) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Too much time waiting to finish the IOTA activation"); g_object_unref (task); return; } /* Otherwise, schedule retry in some secs */ g_assert (ctx->wait_timeout_id == 0); ctx->wait_retries++; ctx->wait_timeout_id = g_timeout_add_seconds (MAX_IOTA_QUERY_RETRY_TIME, (GSourceFunc)cdma_activation_step_retry, task); } static void activation_command_ready (MMBaseModem *self, GAsyncResult *res, GTask *task) { GError *error = NULL; const gchar *response; CdmaActivationContext *ctx; response = mm_base_modem_at_command_finish (self, res, &error); if (!response) { g_task_return_error (task, error); g_object_unref (task); return; } /* Keep on */ ctx = g_task_get_task_data (task); ctx->step++; cdma_activation_step (task); } static void cdma_activation_step (GTask *task) { MMBroadbandModemNovatel *self; CdmaActivationContext *ctx; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); switch (ctx->step) { case CDMA_ACTIVATION_STEP_FIRST: mm_obj_dbg (self, "launching manual activation..."); ctx->step++; /* fall-through */ case CDMA_ACTIVATION_STEP_REQUEST_ACTIVATION: { gchar *command; mm_obj_msg (self, "activation step [1/5]: setting up activation details"); command = g_strdup_printf ("$NWACTIVATION=%s,%s,%s", mm_cdma_manual_activation_properties_get_spc (ctx->properties), mm_cdma_manual_activation_properties_get_mdn (ctx->properties), mm_cdma_manual_activation_properties_get_min (ctx->properties)); mm_base_modem_at_command (MM_BASE_MODEM (self), command, 20, FALSE, (GAsyncReadyCallback)activation_command_ready, task); g_free (command); return; } case CDMA_ACTIVATION_STEP_OTA_UPDATE: mm_obj_msg (self, "activation step [2/5]: starting OTA activation"); mm_base_modem_at_command (MM_BASE_MODEM (self), "+IOTA=1", 20, FALSE, (GAsyncReadyCallback)activation_command_ready, task); return; case CDMA_ACTIVATION_STEP_PRL_UPDATE: mm_obj_msg (self, "activation step [3/5]: starting PRL update"); mm_base_modem_at_command (MM_BASE_MODEM (self), "+IOTA=2", 20, FALSE, (GAsyncReadyCallback)activation_command_ready, task); return; case CDMA_ACTIVATION_STEP_WAIT_UNTIL_FINISHED: mm_obj_msg (self, "activation step [4/5]: checking activation process status"); mm_base_modem_at_command (MM_BASE_MODEM (self), "+IOTA?", 20, FALSE, (GAsyncReadyCallback)iota_query_ready, task); return; case CDMA_ACTIVATION_STEP_LAST: mm_obj_msg (self, "activation step [5/5]: activation process finished"); g_task_return_boolean (task, TRUE); g_object_unref (task); return; default: g_assert_not_reached (); } } static void modem_cdma_activate_manual (MMIfaceModemCdma *self, MMCdmaManualActivationProperties *properties, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; CdmaActivationContext *ctx; task = g_task_new (self, NULL, callback, user_data); /* Setup context */ ctx = g_slice_new0 (CdmaActivationContext); ctx->properties = g_object_ref (properties); ctx->step = CDMA_ACTIVATION_STEP_FIRST; g_task_set_task_data (task, ctx, (GDestroyNotify) cdma_activation_context_free); /* And start it */ cdma_activation_step (task); } /*****************************************************************************/ /* Enable unsolicited events (SMS indications) (Messaging interface) */ static gboolean messaging_enable_unsolicited_events_finish (MMIfaceModemMessaging *self, GAsyncResult *res, GError **error) { return !!mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, error); } static void messaging_enable_unsolicited_events (MMIfaceModemMessaging *self, GAsyncReadyCallback callback, gpointer user_data) { /* Many Qualcomm chipsets don't support mode=2 */ mm_base_modem_at_command (MM_BASE_MODEM (self), "+CNMI=1,1,2,1,0", 3, FALSE, callback, user_data); } /*****************************************************************************/ /* Detailed registration state (CDMA interface) */ typedef struct { MMModemCdmaRegistrationState cdma1x_state; MMModemCdmaRegistrationState evdo_state; } DetailedRegistrationStateResult; typedef struct { MMPortSerialQcdm *port; gboolean close_port; MMModemCdmaRegistrationState cdma1x_state; MMModemCdmaRegistrationState evdo_state; } DetailedRegistrationStateContext; static void detailed_registration_state_context_free (DetailedRegistrationStateContext *ctx) { if (ctx->port) { if (ctx->close_port) mm_port_serial_close (MM_PORT_SERIAL (ctx->port)); g_object_unref (ctx->port); } g_free (ctx); } static gboolean modem_cdma_get_detailed_registration_state_finish (MMIfaceModemCdma *self, GAsyncResult *res, MMModemCdmaRegistrationState *detailed_cdma1x_state, MMModemCdmaRegistrationState *detailed_evdo_state, GError **error) { GTask *task = G_TASK (res); DetailedRegistrationStateContext *ctx = g_task_get_task_data (task);; if (!g_task_propagate_boolean (task, error)) return FALSE; *detailed_cdma1x_state = ctx->cdma1x_state; *detailed_evdo_state = ctx->evdo_state; return TRUE; } static void parse_modem_eri (DetailedRegistrationStateContext *ctx, QcdmResult *result) { MMModemCdmaRegistrationState new_state; guint8 indicator_id = 0, icon_id = 0, icon_mode = 0; qcdm_result_get_u8 (result, QCDM_CMD_NW_SUBSYS_ERI_ITEM_INDICATOR_ID, &indicator_id); qcdm_result_get_u8 (result, QCDM_CMD_NW_SUBSYS_ERI_ITEM_ICON_ID, &icon_id); qcdm_result_get_u8 (result, QCDM_CMD_NW_SUBSYS_ERI_ITEM_ICON_MODE, &icon_mode); /* We use the "Icon ID" (also called the "Icon Index") because if it is 1, * the device is never roaming. Any operator-defined IDs (greater than 2) * may or may not be roaming, but that's operator-defined and we don't * know anything about them. * * Indicator ID: * 0 appears to be "not roaming", contrary to standard ERI values * >= 1 appears to be the actual ERI value, which may or may not be * roaming depending on the operator's custom ERI list * * Icon ID: * 0 = roaming indicator on * 1 = roaming indicator off * 2 = roaming indicator flash * * Icon Mode: * 0 = normal * 1 = flash (only used with Icon ID >= 2) * * Roaming example: * Roam: 160 * Indicator ID: 160 * Icon ID: 3 * Icon Mode: 0 * Call Prompt: 1 * * Home example: * Roam: 0 * Indicator ID: 0 * Icon ID: 1 * Icon Mode: 0 * Call Prompt: 1 */ if (icon_id == 1) new_state = MM_MODEM_CDMA_REGISTRATION_STATE_HOME; else new_state = MM_MODEM_CDMA_REGISTRATION_STATE_ROAMING; if (ctx->cdma1x_state != MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN) ctx->cdma1x_state = new_state; if (ctx->evdo_state != MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN) ctx->evdo_state = new_state; } static void reg_eri_6500_cb (MMPortSerialQcdm *port, GAsyncResult *res, GTask *task) { MMBroadbandModemNovatel *self; DetailedRegistrationStateContext *ctx; GError *error = NULL; GByteArray *response; QcdmResult *result; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); response = mm_port_serial_qcdm_command_finish (port, res, &error); if (error) { /* Just ignore the error and complete with the input info */ mm_obj_dbg (self, "couldn't run QCDM MSM6500 ERI: %s", error->message); g_error_free (error); goto done; } result = qcdm_cmd_nw_subsys_eri_result ((const gchar *) response->data, response->len, NULL); g_byte_array_unref (response); if (result) { parse_modem_eri (ctx, result); qcdm_result_unref (result); } done: g_task_return_boolean (task, TRUE); g_object_unref (task); } static void reg_eri_6800_cb (MMPortSerialQcdm *port, GAsyncResult *res, GTask *task) { MMBroadbandModemNovatel *self; DetailedRegistrationStateContext *ctx; GError *error = NULL; GByteArray *response; GByteArray *nweri; QcdmResult *result; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); response = mm_port_serial_qcdm_command_finish (port, res, &error); if (error) { /* Just ignore the error and complete with the input info */ mm_obj_dbg (self, "couldn't run QCDM MSM6800 ERI: %s", error->message); g_error_free (error); g_task_return_boolean (task, TRUE); g_object_unref (task); return; } /* Parse the response */ result = qcdm_cmd_nw_subsys_eri_result ((const gchar *) response->data, response->len, NULL); g_byte_array_unref (response); if (result) { /* Success */ parse_modem_eri (ctx, result); qcdm_result_unref (result); g_task_return_boolean (task, TRUE); g_object_unref (task); return; } /* Try for MSM6500 */ nweri = g_byte_array_sized_new (25); nweri->len = qcdm_cmd_nw_subsys_eri_new ((char *) nweri->data, 25, QCDM_NW_CHIPSET_6500); g_assert (nweri->len); mm_port_serial_qcdm_command (port, nweri, 3, NULL, (GAsyncReadyCallback)reg_eri_6500_cb, task); g_byte_array_unref (nweri); } static void modem_cdma_get_detailed_registration_state (MMIfaceModemCdma *self, MMModemCdmaRegistrationState cdma1x_state, MMModemCdmaRegistrationState evdo_state, GAsyncReadyCallback callback, gpointer user_data) { DetailedRegistrationStateContext *ctx; GTask *task; GByteArray *nweri; GError *error = NULL; /* Setup context */ task = g_task_new (self, NULL, callback, user_data); ctx = g_new0 (DetailedRegistrationStateContext, 1); g_task_set_task_data (task, ctx, (GDestroyNotify) detailed_registration_state_context_free); ctx->cdma1x_state = cdma1x_state; ctx->evdo_state = evdo_state; ctx->port = mm_base_modem_get_port_qcdm (MM_BASE_MODEM (self)); if (!ctx->port) { /* Ignore errors and use non-detailed registration state */ mm_obj_dbg (self, "no available QCDM port"); g_task_return_boolean (task, TRUE); g_object_unref (task); return; } if (!mm_port_serial_open (MM_PORT_SERIAL (ctx->port), &error)) { /* Ignore errors and use non-detailed registration state */ mm_obj_dbg (self, "couldn't open QCDM port: %s", error->message); g_error_free (error); g_task_return_boolean (task, TRUE); g_object_unref (task); return; } ctx->close_port = TRUE; /* Try MSM6800 first since newer cards use that */ nweri = g_byte_array_sized_new (25); nweri->len = qcdm_cmd_nw_subsys_eri_new ((char *) nweri->data, 25, QCDM_NW_CHIPSET_6800); g_assert (nweri->len); mm_port_serial_qcdm_command (ctx->port, nweri, 3, NULL, (GAsyncReadyCallback)reg_eri_6800_cb, task); g_byte_array_unref (nweri); } /*****************************************************************************/ /* Load network time (Time interface) */ static gboolean parse_nwltime_reply (const char *response, gchar **out_iso_8601, MMNetworkTimezone **out_tz, GError **error) { g_autoptr(GRegex) r = NULL; g_autoptr(GMatchInfo) match_info = NULL; GError *match_error = NULL; guint year; guint month; guint day; guint hour; guint minute; guint second; g_autofree gchar *result = NULL; gint utc_offset = 0; gboolean success = FALSE; /* Sample reply: 2013.3.27.15.47.19.2.-5 */ r = g_regex_new ("(\\d+)\\.(\\d+)\\.(\\d+)\\.(\\d+)\\.(\\d+)\\.(\\d+)\\.(\\d+)\\.([\\-\\+\\d]+)$", 0, 0, NULL); g_assert (r != NULL); if (!g_regex_match_full (r, response, -1, 0, 0, &match_info, &match_error)) { if (match_error) { g_propagate_error (error, match_error); g_prefix_error (error, "Could not parse $NWLTIME results: "); } else { g_set_error_literal (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't match $NWLTIME reply"); } } else { /* Remember that g_match_info_get_match_count() includes match #0 */ g_assert (g_match_info_get_match_count (match_info) >= 9); if (mm_get_uint_from_match_info (match_info, 1, &year) && mm_get_uint_from_match_info (match_info, 2, &month) && mm_get_uint_from_match_info (match_info, 3, &day) && mm_get_uint_from_match_info (match_info, 4, &hour) && mm_get_uint_from_match_info (match_info, 5, &minute) && mm_get_uint_from_match_info (match_info, 6, &second) && mm_get_int_from_match_info (match_info, 8, &utc_offset)) { result = mm_new_iso8601_time (year, month, day, hour, minute, second, TRUE, utc_offset * 60, error); if (out_tz) { *out_tz = mm_network_timezone_new (); mm_network_timezone_set_offset (*out_tz, utc_offset * 60); } success = (result != NULL); } else { g_set_error_literal (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Failed to parse $NWLTIME reply"); } } if (out_iso_8601) *out_iso_8601 = g_steal_pointer (&result); return success; } static gchar * modem_time_load_network_time_finish (MMIfaceModemTime *self, GAsyncResult *res, GError **error) { const gchar *response; gchar *result = NULL; response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, error); if (response) parse_nwltime_reply (response, &result, NULL, error); return result; } static void modem_time_load_network_time (MMIfaceModemTime *self, GAsyncReadyCallback callback, gpointer user_data) { mm_base_modem_at_command (MM_BASE_MODEM (self), "$NWLTIME", 3, FALSE, callback, user_data); } /*****************************************************************************/ /* Load network timezone (Time interface) */ static MMNetworkTimezone * modem_time_load_network_timezone_finish (MMIfaceModemTime *self, GAsyncResult *res, GError **error) { const gchar *response; MMNetworkTimezone *tz = NULL; response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, error); if (response) parse_nwltime_reply (response, NULL, &tz, error); return tz; } static void modem_time_load_network_timezone (MMIfaceModemTime *self, GAsyncReadyCallback callback, gpointer user_data) { mm_base_modem_at_command (MM_BASE_MODEM (self), "$NWLTIME", 3, FALSE, callback, user_data); } /*****************************************************************************/ /* Check support (Time interface) */ static gboolean modem_time_check_support_finish (MMIfaceModemTime *self, GAsyncResult *res, GError **error) { return !!mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, error); } static void modem_time_check_support (MMIfaceModemTime *self, GAsyncReadyCallback callback, gpointer user_data) { /* Only CDMA devices support this at the moment */ mm_base_modem_at_command (MM_BASE_MODEM (self), "$NWLTIME", 3, TRUE, callback, user_data); } /*****************************************************************************/ MMBroadbandModemNovatel * mm_broadband_modem_novatel_new (const gchar *device, const gchar *physdev, const gchar **drivers, const gchar *plugin, guint16 vendor_id, guint16 product_id) { return g_object_new (MM_TYPE_BROADBAND_MODEM_NOVATEL, MM_BASE_MODEM_DEVICE, device, MM_BASE_MODEM_PHYSDEV, physdev, MM_BASE_MODEM_DRIVERS, drivers, MM_BASE_MODEM_PLUGIN, plugin, MM_BASE_MODEM_VENDOR_ID, vendor_id, MM_BASE_MODEM_PRODUCT_ID, product_id, /* Generic bearer supports TTY only */ MM_BASE_MODEM_DATA_NET_SUPPORTED, FALSE, MM_BASE_MODEM_DATA_TTY_SUPPORTED, TRUE, NULL); } static void mm_broadband_modem_novatel_init (MMBroadbandModemNovatel *self) { } static void iface_modem_init (MMIfaceModem *iface) { iface_modem_parent = g_type_interface_peek_parent (iface); iface->load_supported_modes = load_supported_modes; iface->load_supported_modes_finish = load_supported_modes_finish; iface->load_current_modes = load_current_modes; iface->load_current_modes_finish = load_current_modes_finish; iface->set_current_modes = set_current_modes; iface->set_current_modes_finish = set_current_modes_finish; iface->load_access_technologies_finish = modem_load_access_technologies_finish; iface->load_access_technologies = modem_load_access_technologies; iface->load_signal_quality = modem_load_signal_quality; iface->load_signal_quality_finish = modem_load_signal_quality_finish; } static void iface_modem_messaging_init (MMIfaceModemMessaging *iface) { iface->enable_unsolicited_events = messaging_enable_unsolicited_events; iface->enable_unsolicited_events_finish = messaging_enable_unsolicited_events_finish; } static void iface_modem_cdma_init (MMIfaceModemCdma *iface) { iface->get_detailed_registration_state = modem_cdma_get_detailed_registration_state; iface->get_detailed_registration_state_finish = modem_cdma_get_detailed_registration_state_finish; iface->activate = modem_cdma_activate; iface->activate_finish = modem_cdma_activate_finish; iface->activate_manual = modem_cdma_activate_manual; iface->activate_manual_finish = modem_cdma_activate_manual_finish; } static void iface_modem_time_init (MMIfaceModemTime *iface) { iface->check_support = modem_time_check_support; iface->check_support_finish = modem_time_check_support_finish; iface->load_network_time = modem_time_load_network_time; iface->load_network_time_finish = modem_time_load_network_time_finish; iface->load_network_timezone = modem_time_load_network_timezone; iface->load_network_timezone_finish = modem_time_load_network_timezone_finish; } static void mm_broadband_modem_novatel_class_init (MMBroadbandModemNovatelClass *klass) { } ModemManager-1.23.4-dev/src/plugins/novatel/mm-broadband-modem-novatel.h000066400000000000000000000050441456466623000261110ustar00rootroot00000000000000/* -*- 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 - 2012 Red Hat, Inc. * Copyright (C) 2012 Aleksander Morgado */ #ifndef MM_BROADBAND_MODEM_NOVATEL_H #define MM_BROADBAND_MODEM_NOVATEL_H #include "mm-broadband-modem.h" #define MM_TYPE_BROADBAND_MODEM_NOVATEL (mm_broadband_modem_novatel_get_type ()) #define MM_BROADBAND_MODEM_NOVATEL(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_BROADBAND_MODEM_NOVATEL, MMBroadbandModemNovatel)) #define MM_BROADBAND_MODEM_NOVATEL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_BROADBAND_MODEM_NOVATEL, MMBroadbandModemNovatelClass)) #define MM_IS_BROADBAND_MODEM_NOVATEL(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_BROADBAND_MODEM_NOVATEL)) #define MM_IS_BROADBAND_MODEM_NOVATEL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_BROADBAND_MODEM_NOVATEL)) #define MM_BROADBAND_MODEM_NOVATEL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_BROADBAND_MODEM_NOVATEL, MMBroadbandModemNovatelClass)) typedef struct _MMBroadbandModemNovatel MMBroadbandModemNovatel; typedef struct _MMBroadbandModemNovatelClass MMBroadbandModemNovatelClass; typedef struct _MMBroadbandModemNovatelPrivate MMBroadbandModemNovatelPrivate; struct _MMBroadbandModemNovatel { MMBroadbandModem parent; MMBroadbandModemNovatelPrivate *priv; }; struct _MMBroadbandModemNovatelClass{ MMBroadbandModemClass parent; }; GType mm_broadband_modem_novatel_get_type (void); MMBroadbandModemNovatel *mm_broadband_modem_novatel_new (const gchar *device, const gchar *physdev, const gchar **drivers, const gchar *plugin, guint16 vendor_id, guint16 product_id); #endif /* MM_BROADBAND_MODEM_NOVATEL_H */ ModemManager-1.23.4-dev/src/plugins/novatel/mm-common-novatel.c000066400000000000000000000105011456466623000243530ustar00rootroot00000000000000/* -*- 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) 2015 Aleksander Morgado */ #include "mm-common-novatel.h" #include "mm-log-object.h" /*****************************************************************************/ /* Custom init */ typedef struct { MMPortSerialAt *port; guint nwdmat_retries; guint wait_time; } CustomInitContext; static void custom_init_context_free (CustomInitContext *ctx) { g_object_unref (ctx->port); g_slice_free (CustomInitContext, ctx); } gboolean mm_common_novatel_custom_init_finish (MMPortProbe *probe, GAsyncResult *result, GError **error) { return g_task_propagate_boolean (G_TASK (result), error); } static void custom_init_step (GTask *task); static void nwdmat_ready (MMPortSerialAt *port, GAsyncResult *res, GTask *task) { g_autoptr(GError) error = NULL; MMPortProbe *probe; g_autofree gchar *response = NULL; probe = g_task_get_source_object (task); response = mm_port_serial_at_command_finish (port, res, &error); if (error) { if (g_error_matches (error, MM_SERIAL_ERROR, MM_SERIAL_ERROR_RESPONSE_TIMEOUT)) { custom_init_step (task); return; } mm_obj_dbg (probe, "error flipping secondary ports to AT mode: %s", error->message); } /* Finish custom_init */ g_task_return_boolean (task, TRUE); g_object_unref (task); } static gboolean custom_init_wait_cb (GTask *task) { custom_init_step (task); return G_SOURCE_REMOVE; } static void custom_init_step (GTask *task) { CustomInitContext *ctx; MMPortProbe *probe; probe = g_task_get_source_object (task); ctx = g_task_get_task_data (task); /* If cancelled, end */ if (g_task_return_error_if_cancelled (task)) { mm_obj_dbg (probe, "no need to keep on running custom init"); g_object_unref (task); return; } /* If device has a QMI port, don't run $NWDMAT */ if (mm_port_probe_list_has_qmi_port (mm_device_peek_port_probe_list (mm_port_probe_peek_device (probe)))) { mm_obj_dbg (probe, "no need to run custom init: device has QMI port"); g_task_return_boolean (task, TRUE); g_object_unref (task); return; } if (ctx->wait_time > 0) { ctx->wait_time--; g_timeout_add_seconds (1, (GSourceFunc)custom_init_wait_cb, task); return; } if (ctx->nwdmat_retries > 0) { ctx->nwdmat_retries--; mm_port_serial_at_command (ctx->port, "$NWDMAT=1", 3, FALSE, /* raw */ FALSE, /* allow_cached */ g_task_get_cancellable (task), (GAsyncReadyCallback)nwdmat_ready, task); return; } /* Finish custom_init */ mm_obj_dbg (probe, "couldn't flip secondary port to AT: all retries consumed"); g_task_return_boolean (task, TRUE); g_object_unref (task); } void mm_common_novatel_custom_init (MMPortProbe *probe, MMPortSerialAt *port, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { CustomInitContext *ctx; GTask *task; ctx = g_slice_new (CustomInitContext); ctx->port = g_object_ref (port); ctx->nwdmat_retries = 3; ctx->wait_time = 2; task = g_task_new (probe, cancellable, callback, user_data); g_task_set_task_data (task, ctx, (GDestroyNotify)custom_init_context_free); custom_init_step (task); } ModemManager-1.23.4-dev/src/plugins/novatel/mm-common-novatel.h000066400000000000000000000025031456466623000243630ustar00rootroot00000000000000/* -*- 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) 2015 Aleksander Morgado */ #ifndef MM_COMMON_NOVATEL_H #define MM_COMMON_NOVATEL_H #include "glib.h" #include "mm-plugin.h" void mm_common_novatel_custom_init (MMPortProbe *probe, MMPortSerialAt *port, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); gboolean mm_common_novatel_custom_init_finish (MMPortProbe *probe, GAsyncResult *result, GError **error); #endif /* MM_COMMON_NOVATEL_H */ ModemManager-1.23.4-dev/src/plugins/novatel/mm-plugin-novatel-lte.c000066400000000000000000000056641456466623000251610ustar00rootroot00000000000000/* -*- 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. * * 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. * * Copyright (C) 2012 Google Inc. * Author: Nathan Williams */ #include #include #include "mm-plugin-common.h" #include "mm-private-boxed-types.h" #include "mm-broadband-modem-novatel-lte.h" #define MM_TYPE_PLUGIN_NOVATEL_LTE mm_plugin_novatel_lte_get_type () MM_DEFINE_PLUGIN (NOVATEL_LTE, novatel_lte, NovatelLte) /*****************************************************************************/ static MMBaseModem * create_modem (MMPlugin *self, const gchar *uid, const gchar *physdev, const gchar **drivers, guint16 vendor, guint16 product, guint16 subsystem_vendor, GList *probes, GError **error) { return MM_BASE_MODEM (mm_broadband_modem_novatel_lte_new (uid, physdev, drivers, mm_plugin_get_name (self), vendor, product)); } /*****************************************************************************/ MM_PLUGIN_NAMED_CREATOR_SCOPE MMPlugin * mm_plugin_create_novatel_lte (void) { static const gchar *subsystems[] = { "tty", "net", NULL }; static const mm_uint16_pair products[] = { { 0x1410, 0x9010 }, /* Novatel E362 */ {0, 0} }; return MM_PLUGIN ( g_object_new (MM_TYPE_PLUGIN_NOVATEL_LTE, MM_PLUGIN_NAME, MM_MODULE_NAME, MM_PLUGIN_ALLOWED_SUBSYSTEMS, subsystems, MM_PLUGIN_ALLOWED_PRODUCT_IDS, products, MM_PLUGIN_ALLOWED_SINGLE_AT, TRUE, NULL)); } static void mm_plugin_novatel_lte_init (MMPluginNovatelLte *self) { } static void mm_plugin_novatel_lte_class_init (MMPluginNovatelLteClass *klass) { MMPluginClass *plugin_class = MM_PLUGIN_CLASS (klass); plugin_class->create_modem = create_modem; } ModemManager-1.23.4-dev/src/plugins/novatel/mm-plugin-novatel.c000066400000000000000000000102711456466623000243650ustar00rootroot00000000000000/* -*- 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. * * 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. * * Copyright (C) 2008 - 2009 Novell, Inc. * Copyright (C) 2009 - 2012 Red Hat, Inc. * Copyright (C) 2012 Aleksander Morgado */ #include #include #define _LIBMM_INSIDE_MM #include #include "mm-plugin-common.h" #include "mm-common-novatel.h" #include "mm-private-boxed-types.h" #include "mm-broadband-modem-novatel.h" #include "mm-log-object.h" #if defined WITH_QMI #include "mm-broadband-modem-qmi.h" #endif #define MM_TYPE_PLUGIN_NOVATEL mm_plugin_novatel_get_type () MM_DEFINE_PLUGIN (NOVATEL, novatel, Novatel) /*****************************************************************************/ static MMBaseModem * create_modem (MMPlugin *self, const gchar *uid, const gchar *physdev, const gchar **drivers, guint16 vendor, guint16 product, guint16 subsystem_vendor, GList *probes, GError **error) { #if defined WITH_QMI if (mm_port_probe_list_has_qmi_port (probes)) { mm_obj_dbg (self, "QMI-powered Novatel modem found..."); return MM_BASE_MODEM (mm_broadband_modem_qmi_new (uid, physdev, drivers, mm_plugin_get_name (self), vendor, product)); } #endif return MM_BASE_MODEM (mm_broadband_modem_novatel_new (uid, physdev, drivers, mm_plugin_get_name (self), vendor, product)); } /*****************************************************************************/ MM_PLUGIN_NAMED_CREATOR_SCOPE MMPlugin * mm_plugin_create_novatel (void) { static const gchar *subsystems[] = { "tty", "net", "usbmisc", NULL }; static const guint16 vendors[] = { 0x1410, 0 }; static const mm_uint16_pair forbidden_products[] = { { 0x1410, 0x9010 }, /* Novatel E362 */ { 0, 0 } }; static const MMAsyncMethod custom_init = { .async = G_CALLBACK (mm_common_novatel_custom_init), .finish = G_CALLBACK (mm_common_novatel_custom_init_finish), }; return MM_PLUGIN ( g_object_new (MM_TYPE_PLUGIN_NOVATEL, MM_PLUGIN_NAME, MM_MODULE_NAME, MM_PLUGIN_ALLOWED_SUBSYSTEMS, subsystems, MM_PLUGIN_ALLOWED_VENDOR_IDS, vendors, MM_PLUGIN_FORBIDDEN_PRODUCT_IDS, forbidden_products, MM_PLUGIN_ALLOWED_AT, TRUE, MM_PLUGIN_CUSTOM_INIT, &custom_init, MM_PLUGIN_REQUIRED_QCDM, TRUE, MM_PLUGIN_ALLOWED_QMI, TRUE, NULL)); } static void mm_plugin_novatel_init (MMPluginNovatel *self) { } static void mm_plugin_novatel_class_init (MMPluginNovatelClass *klass) { MMPluginClass *plugin_class = MM_PLUGIN_CLASS (klass); plugin_class->create_modem = create_modem; } ModemManager-1.23.4-dev/src/plugins/novatel/mm-shared.c000066400000000000000000000013141456466623000226650ustar00rootroot00000000000000/* -*- 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) 2019 Aleksander Morgado */ #include "mm-shared-common.h" MM_DEFINE_SHARED (novatel) ModemManager-1.23.4-dev/src/plugins/novatel/mm-sim-novatel-lte.c000066400000000000000000000155351456466623000244510ustar00rootroot00000000000000/* -*- 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) 2012 Google, Inc. */ #include #include #include #include #include #include #include #define _LIBMM_INSIDE_MM #include #include "mm-modem-helpers.h" #include "mm-base-modem-at.h" #include "mm-sim-novatel-lte.h" G_DEFINE_TYPE (MMSimNovatelLte, mm_sim_novatel_lte, MM_TYPE_BASE_SIM) /*****************************************************************************/ /* IMSI loading */ static gchar * load_imsi_finish (MMBaseSim *self, GAsyncResult *res, GError **error) { return g_task_propagate_pointer (G_TASK (res), error); } static void imsi_read_ready (MMBaseModem *modem, GAsyncResult *res, GTask *task) { GError *error = NULL; const gchar *response, *str; gchar buf[19]; gchar imsi[16]; gsize len = 0; gint sw1, sw2; gint i; response = mm_base_modem_at_command_finish (modem, res, &error); if (!response) { g_task_return_error (task, error); g_object_unref (task); return; } memset (buf, 0, sizeof (buf)); str = mm_strip_tag (response, "+CRSM:"); /* With or without quotes... */ if (sscanf (str, "%d,%d,\"%18c\"", &sw1, &sw2, (char *) &buf) != 3 && sscanf (str, "%d,%d,%18c", &sw1, &sw2, (char *) &buf) != 3) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Failed to parse the CRSM response: '%s'", response); g_object_unref (task); return; } if ((sw1 != 0x90 || sw2 != 0x00) && (sw1 != 0x91) && (sw1 != 0x92) && (sw1 != 0x9f)) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "SIM failed to handle CRSM request (sw1 %d sw2 %d)", sw1, sw2); g_object_unref (task); return; } /* Make sure the buffer is only digits or 'F' */ for (len = 0; len < sizeof (buf) && buf[len]; len++) { if (isdigit (buf[len])) continue; if (buf[len] == 'F' || buf[len] == 'f') { buf[len] = 'F'; /* canonicalize the F */ continue; } if (buf[len] == '\"') { buf[len] = 0; break; } /* Invalid character */ g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "CRSM IMSI response contained invalid character '%c'", buf[len]); g_object_unref (task); return; } /* BCD encoded IMSIs plus the length byte and parity are 18 digits long */ if (len != 18) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Invalid +CRSM IMSI response size (was %zd, expected 18)", len); g_object_unref (task); return; } /* Skip the length byte (digit 0-1) and parity (digit 3). Swap digits in * the EFimsi response to get the actual IMSI, each group of 2 digits is * reversed in the +CRSM response. i.e.: * * **0*21436587a9cbed -> 0123456789abcde */ memset (imsi, 0, sizeof (imsi)); imsi[0] = buf[2]; for (i = 1; i < 8; i++) { imsi[(i * 2) - 1] = buf[(i * 2) + 3]; imsi[i * 2] = buf[(i * 2) + 2]; } /* Zero out the first F, if any, for IMSIs shorter than 15 digits */ for (i = 0; i < 15; i++) { if (imsi[i] == 'F') { imsi[i++] = 0; break; } } /* Ensure all 'F's, if any, are at the end */ for (; i < 15; i++) { if (imsi[i] != 'F') { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Invalid +CRSM IMSI length (unexpected F)"); g_object_unref (task); return; } } g_task_return_pointer (task, g_strdup (imsi), g_free); g_object_unref (task); } static void load_imsi (MMBaseSim *self, GAsyncReadyCallback callback, gpointer user_data) { MMBaseModem *modem = NULL; g_object_get (self, MM_BASE_SIM_MODEM, &modem, NULL); mm_base_modem_at_command ( modem, "+CRSM=176,28423,0,0,9", 3, FALSE, (GAsyncReadyCallback)imsi_read_ready, g_task_new (self, NULL, callback, user_data)); g_object_unref (modem); } /*****************************************************************************/ MMBaseSim * mm_sim_novatel_lte_new_finish (GAsyncResult *res, GError **error) { GObject *source; GObject *sim; source = g_async_result_get_source_object (res); sim = g_async_initable_new_finish (G_ASYNC_INITABLE (source), res, error); g_object_unref (source); if (!sim) return NULL; /* Only export valid SIMs */ mm_base_sim_export (MM_BASE_SIM (sim)); return MM_BASE_SIM (sim); } void mm_sim_novatel_lte_new (MMBaseModem *modem, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { g_async_initable_new_async (MM_TYPE_SIM_NOVATEL_LTE, G_PRIORITY_DEFAULT, cancellable, callback, user_data, MM_BASE_SIM_MODEM, modem, "active", TRUE, /* by default always active */ NULL); } static void mm_sim_novatel_lte_init (MMSimNovatelLte *self) { } static void mm_sim_novatel_lte_class_init (MMSimNovatelLteClass *klass) { MMBaseSimClass *base_sim_class = MM_BASE_SIM_CLASS (klass); base_sim_class->load_imsi = load_imsi; base_sim_class->load_imsi_finish = load_imsi_finish; } ModemManager-1.23.4-dev/src/plugins/novatel/mm-sim-novatel-lte.h000066400000000000000000000040701456466623000244460ustar00rootroot00000000000000/* -*- 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) 2012 Google, Inc. */ #ifndef MM_SIM_NOVATEL_LTE_H #define MM_SIM_NOVATEL_LTE_H #include #include #include "mm-base-sim.h" #define MM_TYPE_SIM_NOVATEL_LTE (mm_sim_novatel_lte_get_type ()) #define MM_SIM_NOVATEL_LTE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_SIM_NOVATEL_LTE, MMSimNovatelLte)) #define MM_SIM_NOVATEL_LTE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_SIM_NOVATEL_LTE, MMSimNovatelLteClass)) #define MM_IS_SIM_NOVATEL_LTE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_SIM_NOVATEL_LTE)) #define MM_IS_SIM_NOVATEL_LTE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_SIM_NOVATEL_LTE)) #define MM_SIM_NOVATEL_LTE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_SIM_NOVATEL_LTE, MMSimNovatelLteClass)) typedef struct _MMSimNovatelLte MMSimNovatelLte; typedef struct _MMSimNovatelLteClass MMSimNovatelLteClass; struct _MMSimNovatelLte { MMBaseSim parent; }; struct _MMSimNovatelLteClass { MMBaseSimClass parent; }; GType mm_sim_novatel_lte_get_type (void); void mm_sim_novatel_lte_new (MMBaseModem *modem, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); MMBaseSim *mm_sim_novatel_lte_new_finish (GAsyncResult *res, GError **error); #endif /* MM_SIM_NOVATEL_LTE_H */ ModemManager-1.23.4-dev/src/plugins/option/000077500000000000000000000000001456466623000205055ustar00rootroot00000000000000ModemManager-1.23.4-dev/src/plugins/option/mm-broadband-bearer-hso.c000066400000000000000000000653161456466623000252340ustar00rootroot00000000000000/* -*- 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 - 2012 Red Hat, Inc. * Copyright (C) 2012 Aleksander Morgado */ #include #include #include #include #include #include #include #include #define _LIBMM_INSIDE_MM #include #include "mm-base-modem-at.h" #include "mm-broadband-bearer-hso.h" #include "mm-log-object.h" #include "mm-modem-helpers.h" #include "mm-daemon-enums-types.h" G_DEFINE_TYPE (MMBroadbandBearerHso, mm_broadband_bearer_hso, MM_TYPE_BROADBAND_BEARER); struct _MMBroadbandBearerHsoPrivate { guint auth_idx; GTask *connect_pending; guint connect_pending_id; gulong connect_port_closed_id; }; /*****************************************************************************/ /* 3GPP IP config retrieval (sub-step of the 3GPP Connection sequence) */ typedef struct { MMBaseModem *modem; MMPortSerialAt *primary; guint cid; } GetIpConfig3gppContext; static void get_ip_config_context_free (GetIpConfig3gppContext *ctx) { g_object_unref (ctx->primary); g_object_unref (ctx->modem); g_slice_free (GetIpConfig3gppContext, ctx); } static gboolean get_ip_config_3gpp_finish (MMBroadbandBearer *self, GAsyncResult *res, MMBearerIpConfig **ipv4_config, MMBearerIpConfig **ipv6_config, GError **error) { MMBearerIpConfig *ip_config; ip_config = g_task_propagate_pointer (G_TASK (res), error); if (!ip_config) return FALSE; /* No IPv6 for now */ *ipv4_config = ip_config; /* Transfer ownership */ *ipv6_config = NULL; return TRUE; } #define OWANDATA_TAG "_OWANDATA: " static void ip_config_ready (MMBaseModem *modem, GAsyncResult *res, GTask *task) { GetIpConfig3gppContext *ctx; MMBearerIpConfig *ip_config = NULL; const gchar *response; GError *error = NULL; gchar **items; gchar *dns[3] = { 0 }; guint i; guint dns_i; response = mm_base_modem_at_command_full_finish (modem, res, &error); if (error) { g_task_return_error (task, error); g_object_unref (task); return; } /* TODO: use a regex to parse this */ /* Check result */ if (!g_str_has_prefix (response, OWANDATA_TAG)) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't get IP config: invalid response '%s'", response); g_object_unref (task); return; } ctx = g_task_get_task_data (task); response = mm_strip_tag (response, OWANDATA_TAG); items = g_strsplit (response, ", ", 0); for (i = 0, dns_i = 0; items[i]; i++) { if (i == 0) { /* CID */ guint num; if (!mm_get_uint_from_str (items[i], &num) || num != ctx->cid) { error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Unknown CID in OWANDATA response (" "got %d, expected %d)", (guint) num, ctx->cid); break; } } else if (i == 1) { /* IP address */ guint32 tmp; if (!inet_pton (AF_INET, items[i], &tmp)) break; ip_config = mm_bearer_ip_config_new (); mm_bearer_ip_config_set_method (ip_config, MM_BEARER_IP_METHOD_STATIC); mm_bearer_ip_config_set_address (ip_config, items[i]); mm_bearer_ip_config_set_prefix (ip_config, 32); } else if (i == 3 || i == 4) { /* DNS entries */ guint32 tmp; if (!inet_pton (AF_INET, items[i], &tmp)) { g_clear_object (&ip_config); break; } dns[dns_i++] = items[i]; } } if (!ip_config) { if (error) g_task_return_error (task, error); else g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't get IP config: couldn't parse response '%s'", response); } else { /* If we got DNS entries, set them in the IP config */ if (dns[0]) mm_bearer_ip_config_set_dns (ip_config, (const gchar **)dns); g_task_return_pointer (task, ip_config, g_object_unref); } g_object_unref (task); g_strfreev (items); } static void get_ip_config_3gpp (MMBroadbandBearer *self, MMBroadbandModem *modem, MMPortSerialAt *primary, MMPortSerialAt *secondary, MMPort *data, guint cid, MMBearerIpFamily ip_family, GAsyncReadyCallback callback, gpointer user_data) { GetIpConfig3gppContext *ctx; GTask *task; gchar *command; ctx = g_slice_new0 (GetIpConfig3gppContext); ctx->modem = MM_BASE_MODEM (g_object_ref (modem)); ctx->primary = g_object_ref (primary); ctx->cid = cid; task = g_task_new (self, NULL, callback, user_data); g_task_set_task_data (task, ctx, (GDestroyNotify)get_ip_config_context_free); command = g_strdup_printf ("AT_OWANDATA=%d", cid); mm_base_modem_at_command_full ( MM_BASE_MODEM (modem), primary, command, 3, FALSE, FALSE, /* raw */ NULL, /* cancellable */ (GAsyncReadyCallback)ip_config_ready, task); g_free (command); } /*****************************************************************************/ /* 3GPP Dialing (sub-step of the 3GPP Connection sequence) */ typedef struct { MMBaseModem *modem; MMPortSerialAt *primary; guint cid; MMPort *data; guint auth_idx; GError *saved_error; } Dial3gppContext; static void dial_3gpp_context_free (Dial3gppContext *ctx) { g_assert (!ctx->saved_error); g_clear_object (&ctx->data); g_clear_object (&ctx->primary); g_clear_object (&ctx->modem); g_slice_free (Dial3gppContext, ctx); } static guint dial_3gpp_get_connecting_cid (GTask *task) { Dial3gppContext *ctx; ctx = g_task_get_task_data (task); return ctx->cid; } static MMPort * dial_3gpp_finish (MMBroadbandBearer *self, GAsyncResult *res, GError **error) { return MM_PORT (g_task_propagate_pointer (G_TASK (res), error)); } static void connect_reset_ready (MMBaseModem *modem, GAsyncResult *res, GTask *task) { Dial3gppContext *ctx; ctx = g_task_get_task_data (task); mm_base_modem_at_command_full_finish (modem, res, NULL); /* When reset is requested, it was either cancelled or an error was stored */ if (!g_task_return_error_if_cancelled (task)) { g_assert (ctx->saved_error); g_task_return_error (task, ctx->saved_error); ctx->saved_error = NULL; } g_object_unref (task); } static void connect_reset (GTask *task) { Dial3gppContext *ctx; gchar *command; ctx = g_task_get_task_data (task); /* Need to reset the connection attempt */ command = g_strdup_printf ("AT_OWANCALL=%d,0,1", ctx->cid); mm_base_modem_at_command_full (ctx->modem, ctx->primary, command, 3, FALSE, FALSE, /* raw */ NULL, /* cancellable */ (GAsyncReadyCallback)connect_reset_ready, task); g_free (command); } static void process_pending_connect_attempt (MMBroadbandBearerHso *self, MMBearerConnectionStatus status) { GTask *task; Dial3gppContext *ctx; /* Recover task and remove both cancellation and timeout (if any)*/ g_assert (self->priv->connect_pending); task = self->priv->connect_pending; self->priv->connect_pending = NULL; ctx = g_task_get_task_data (task); if (self->priv->connect_pending_id) { g_source_remove (self->priv->connect_pending_id); self->priv->connect_pending_id = 0; } if (self->priv->connect_port_closed_id) { g_signal_handler_disconnect (ctx->primary, self->priv->connect_port_closed_id); self->priv->connect_port_closed_id = 0; } /* Reporting connected */ if (status == MM_BEARER_CONNECTION_STATUS_CONNECTED) { /* If we wanted to get cancelled before, do it now. */ if (g_cancellable_is_cancelled (g_task_get_cancellable (task))) { connect_reset (task); return; } g_task_return_pointer (task, g_object_ref (ctx->data), g_object_unref); g_object_unref (task); return; } /* Received CONNECTION_FAILED or DISCONNECTED during a connection attempt, * so return a failed error. Note that if the cancellable has been cancelled * already, a cancelled error would be returned instead. */ g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Call setup failed"); g_object_unref (task); } static gboolean connect_timed_out_cb (MMBroadbandBearerHso *self) { GTask *task; Dial3gppContext *ctx; /* Cleanup timeout ID */ self->priv->connect_pending_id = 0; /* Recover task and own it */ task = self->priv->connect_pending; self->priv->connect_pending = NULL; g_assert (task); ctx = g_task_get_task_data (task); /* Remove closed port watch, if found */ if (self->priv->connect_port_closed_id) { g_signal_handler_disconnect (ctx->primary, self->priv->connect_port_closed_id); self->priv->connect_port_closed_id = 0; } /* Setup error to return after the reset */ g_assert (!ctx->saved_error); ctx->saved_error = g_error_new (MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_NETWORK_TIMEOUT, "Connection attempt timed out"); /* It's probably pointless to try to reset this here, but anyway... */ connect_reset (task); return G_SOURCE_REMOVE; } static void forced_close_cb (MMBroadbandBearerHso *self) { /* Just treat the forced close event as any other unsolicited message */ mm_base_bearer_report_connection_status (MM_BASE_BEARER (self), MM_BEARER_CONNECTION_STATUS_CONNECTION_FAILED); } static void activate_ready (MMBaseModem *modem, GAsyncResult *res, MMBroadbandBearerHso *self) { GTask *task; Dial3gppContext *ctx; GError *error = NULL; task = g_steal_pointer (&self->priv->connect_pending); /* Try to recover the connection task. If none found, it means the * task was already completed and we have nothing else to do. * But note that we won't take owneship of the task yet! */ if (!task) { mm_obj_dbg (self, "connection context was finished already by an unsolicited message"); /* Run _finish() to finalize the async call, even if we don't care * about the result */ mm_base_modem_at_command_full_finish (modem, res, NULL); goto out; } /* From now on, if we get cancelled, we'll need to run the connection * reset ourselves just in case */ /* Errors on the dial command are fatal */ if (!mm_base_modem_at_command_full_finish (modem, res, &error)) { g_task_return_error (task, error); g_object_unref (task); goto out; } /* Track the task again */ self->priv->connect_pending = task; /* We will now setup a timeout and keep the context in the bearer's private. * Reports of modem being connected will arrive via unsolicited messages. * This timeout should be long enough. Actually... ideally should never get * reached. */ self->priv->connect_pending_id = g_timeout_add_seconds (MM_BASE_BEARER_DEFAULT_CONNECTION_TIMEOUT, (GSourceFunc)connect_timed_out_cb, self); /* If we get the port closed, we treat as a connect error */ ctx = g_task_get_task_data (task); self->priv->connect_port_closed_id = g_signal_connect_swapped (ctx->primary, "forced-close", G_CALLBACK (forced_close_cb), self); out: /* Balance refcount with the extra ref we passed to command_full() */ g_object_unref (self); } static void authenticate (GTask *task); static void authenticate_ready (MMBaseModem *modem, GAsyncResult *res, GTask *task) { MMBroadbandBearerHso *self; Dial3gppContext *ctx; gchar *command; /* If cancelled, complete */ if (g_task_return_error_if_cancelled (task)) { g_object_unref (task); return; } self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); if (!mm_base_modem_at_command_full_finish (modem, res, NULL)) { /* Try the next auth command */ ctx->auth_idx++; authenticate (task); return; } /* Store which auth command worked, for next attempts */ self->priv->auth_idx = ctx->auth_idx; /* The unsolicited response to AT_OWANCALL may come before the OK does. * We will keep the connection context in the bearer private data so * that it is accessible from the unsolicited message handler. Note * also that we do NOT pass the ctx to the GAsyncReadyCallback, as it * may not be valid any more when the callback is called (it may be * already completed in the unsolicited handling) */ g_assert (self->priv->connect_pending == NULL); self->priv->connect_pending = task; /* Success, activate the PDP context and start the data session */ command = g_strdup_printf ("AT_OWANCALL=%d,1,1", ctx->cid); mm_base_modem_at_command_full (ctx->modem, ctx->primary, command, 3, FALSE, FALSE, /* raw */ NULL, /* cancellable */ (GAsyncReadyCallback) activate_ready, g_object_ref (self)); /* we pass the bearer object! */ g_free (command); } const gchar *auth_commands[] = { "$QCPDPP", /* Icera-based devices (GI0322/Quicksilver, iCON 505) don't implement * $QCPDPP, but instead use _OPDPP with the same arguments. */ "_OPDPP", NULL }; static void authenticate (GTask *task) { MMBroadbandBearerHso *self; Dial3gppContext *ctx; gchar *command; const gchar *user; const gchar *password; MMBearerAllowedAuth allowed_auth; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); if (!auth_commands[ctx->auth_idx]) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't run HSO authentication"); g_object_unref (task); return; } user = mm_bearer_properties_get_user (mm_base_bearer_peek_config (MM_BASE_BEARER (self))); password = mm_bearer_properties_get_password (mm_base_bearer_peek_config (MM_BASE_BEARER (self))); allowed_auth = mm_bearer_properties_get_allowed_auth (mm_base_bearer_peek_config (MM_BASE_BEARER (self))); /* Both user and password are required; otherwise firmware returns an error */ if (!user || !password || allowed_auth == MM_BEARER_ALLOWED_AUTH_NONE) { mm_obj_dbg (self, "not using authentication"); command = g_strdup_printf ("%s=%d,0", auth_commands[ctx->auth_idx], ctx->cid); } else { gchar *quoted_user; gchar *quoted_password; guint hso_auth; if (allowed_auth == MM_BEARER_ALLOWED_AUTH_UNKNOWN) { mm_obj_dbg (self, "using default (CHAP) authentication method"); hso_auth = 2; } else if (allowed_auth & MM_BEARER_ALLOWED_AUTH_CHAP) { mm_obj_dbg (self, "using CHAP authentication method"); hso_auth = 2; } else if (allowed_auth & MM_BEARER_ALLOWED_AUTH_PAP) { mm_obj_dbg (self, "using PAP authentication method"); hso_auth = 1; } else { gchar *str; str = mm_bearer_allowed_auth_build_string_from_mask (allowed_auth); g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "Cannot use any of the specified authentication methods (%s)", str); g_object_unref (task); g_free (str); return; } quoted_user = mm_port_serial_at_quote_string (user); quoted_password = mm_port_serial_at_quote_string (password); command = g_strdup_printf ("%s=%d,%u,%s,%s", auth_commands[ctx->auth_idx], ctx->cid, hso_auth, quoted_password, quoted_user); g_free (quoted_user); g_free (quoted_password); } mm_base_modem_at_command_full (ctx->modem, ctx->primary, command, 3, FALSE, FALSE, /* raw */ NULL, /* cancellable */ (GAsyncReadyCallback)authenticate_ready, task); g_free (command); } static void dial_3gpp (MMBroadbandBearer *_self, MMBaseModem *modem, MMPortSerialAt *primary, guint cid, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { MMBroadbandBearerHso *self = MM_BROADBAND_BEARER_HSO (_self); GTask *task; Dial3gppContext *ctx; g_assert (primary != NULL); task = g_task_new (self, cancellable, callback, user_data); ctx = g_slice_new0 (Dial3gppContext); ctx->modem = g_object_ref (modem); ctx->primary = g_object_ref (primary); ctx->cid = cid; g_task_set_task_data (task, ctx, (GDestroyNotify)dial_3gpp_context_free); /* Always start with the index that worked last time * (will be 0 the first time)*/ ctx->auth_idx = self->priv->auth_idx; /* We need a net data port */ ctx->data = mm_base_modem_get_best_data_port (modem, MM_PORT_TYPE_NET); if (!ctx->data) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_NOT_FOUND, "No valid data port found to launch connection"); g_object_unref (task); return; } authenticate (task); } /*****************************************************************************/ /* 3GPP disconnect */ typedef struct { MMBaseModem *modem; MMPortSerialAt *primary; } DisconnectContext; static void disconnect_context_free (DisconnectContext *ctx) { g_object_unref (ctx->primary); g_object_unref (ctx->modem); g_free (ctx); } static gboolean disconnect_3gpp_finish (MMBroadbandBearer *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void disconnect_owancall_ready (MMBaseModem *modem, GAsyncResult *res, GTask *task) { MMBroadbandBearerHso *self; GError *error = NULL; self = g_task_get_source_object (task); /* Ignore errors for now */ mm_base_modem_at_command_full_finish (modem, res, &error); if (error) { mm_obj_dbg (self, "disconnection failed (not fatal): %s", error->message); g_error_free (error); } g_task_return_boolean (task, TRUE); g_object_unref (task); } static void disconnect_3gpp (MMBroadbandBearer *self, MMBroadbandModem *modem, MMPortSerialAt *primary, MMPortSerialAt *secondary, MMPort *data, guint cid, GAsyncReadyCallback callback, gpointer user_data) { gchar *command; DisconnectContext *ctx; GTask *task; g_assert (primary != NULL); ctx = g_new0 (DisconnectContext, 1); ctx->modem = MM_BASE_MODEM (g_object_ref (modem)); ctx->primary = g_object_ref (primary); task = g_task_new (self, NULL, callback, user_data); g_task_set_task_data (task, ctx, (GDestroyNotify)disconnect_context_free); /* Use specific CID */ command = g_strdup_printf ("AT_OWANCALL=%d,0,0", cid); mm_base_modem_at_command_full (MM_BASE_MODEM (modem), primary, command, MM_BASE_BEARER_DEFAULT_DISCONNECTION_TIMEOUT, FALSE, FALSE, /* raw */ NULL, /* cancellable */ (GAsyncReadyCallback)disconnect_owancall_ready, task); g_free (command); } /*****************************************************************************/ gint mm_broadband_bearer_hso_get_connecting_profile_id (MMBroadbandBearerHso *self) { return (self->priv->connect_pending ? (gint)dial_3gpp_get_connecting_cid (self->priv->connect_pending) : MM_3GPP_PROFILE_ID_UNKNOWN); } /*****************************************************************************/ static void report_connection_status (MMBaseBearer *_self, MMBearerConnectionStatus status, const GError *connection_error) { MMBroadbandBearerHso *self = MM_BROADBAND_BEARER_HSO (_self); g_assert (status == MM_BEARER_CONNECTION_STATUS_CONNECTED || status == MM_BEARER_CONNECTION_STATUS_CONNECTION_FAILED || status == MM_BEARER_CONNECTION_STATUS_DISCONNECTED); /* Process pending connection attempt */ if (self->priv->connect_pending) { process_pending_connect_attempt (self, status); return; } mm_obj_dbg (self, "received spontaneous _OWANCALL (%s)", mm_bearer_connection_status_get_string (status)); if (status == MM_BEARER_CONNECTION_STATUS_DISCONNECTED) { /* If no connection attempt on-going, make sure we mark ourselves as * disconnected */ MM_BASE_BEARER_CLASS (mm_broadband_bearer_hso_parent_class)->report_connection_status (_self, status,connection_error); } } /*****************************************************************************/ MMBaseBearer * mm_broadband_bearer_hso_new_finish (GAsyncResult *res, GError **error) { GObject *bearer; GObject *source; source = g_async_result_get_source_object (res); bearer = g_async_initable_new_finish (G_ASYNC_INITABLE (source), res, error); g_object_unref (source); if (!bearer) return NULL; /* Only export valid bearers */ mm_base_bearer_export (MM_BASE_BEARER (bearer)); return MM_BASE_BEARER (bearer); } void mm_broadband_bearer_hso_new (MMBroadbandModemHso *modem, MMBearerProperties *config, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { g_async_initable_new_async ( MM_TYPE_BROADBAND_BEARER_HSO, G_PRIORITY_DEFAULT, cancellable, callback, user_data, MM_BASE_BEARER_MODEM, modem, MM_BASE_BEARER_CONFIG, config, NULL); } static void mm_broadband_bearer_hso_init (MMBroadbandBearerHso *self) { /* Initialize private data */ self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, MM_TYPE_BROADBAND_BEARER_HSO, MMBroadbandBearerHsoPrivate); } static void mm_broadband_bearer_hso_class_init (MMBroadbandBearerHsoClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); MMBaseBearerClass *base_bearer_class = MM_BASE_BEARER_CLASS (klass); MMBroadbandBearerClass *broadband_bearer_class = MM_BROADBAND_BEARER_CLASS (klass); g_type_class_add_private (object_class, sizeof (MMBroadbandBearerHsoPrivate)); base_bearer_class->report_connection_status = report_connection_status; base_bearer_class->load_connection_status = NULL; base_bearer_class->load_connection_status_finish = NULL; #if defined WITH_SUSPEND_RESUME base_bearer_class->reload_connection_status = NULL; base_bearer_class->reload_connection_status_finish = NULL; #endif broadband_bearer_class->dial_3gpp = dial_3gpp; broadband_bearer_class->dial_3gpp_finish = dial_3gpp_finish; broadband_bearer_class->get_ip_config_3gpp = get_ip_config_3gpp; broadband_bearer_class->get_ip_config_3gpp_finish = get_ip_config_3gpp_finish; broadband_bearer_class->disconnect_3gpp = disconnect_3gpp; broadband_bearer_class->disconnect_3gpp_finish = disconnect_3gpp_finish; } ModemManager-1.23.4-dev/src/plugins/option/mm-broadband-bearer-hso.h000066400000000000000000000052741456466623000252360ustar00rootroot00000000000000/* -*- 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) 2012 Aleksander Morgado */ #ifndef MM_BROADBAND_BEARER_HSO_H #define MM_BROADBAND_BEARER_HSO_H #include #include #define _LIBMM_INSIDE_MM #include #include "mm-broadband-bearer.h" #include "mm-broadband-modem-hso.h" #define MM_TYPE_BROADBAND_BEARER_HSO (mm_broadband_bearer_hso_get_type ()) #define MM_BROADBAND_BEARER_HSO(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_BROADBAND_BEARER_HSO, MMBroadbandBearerHso)) #define MM_BROADBAND_BEARER_HSO_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_BROADBAND_BEARER_HSO, MMBroadbandBearerHsoClass)) #define MM_IS_BROADBAND_BEARER_HSO(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_BROADBAND_BEARER_HSO)) #define MM_IS_BROADBAND_BEARER_HSO_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_BROADBAND_BEARER_HSO)) #define MM_BROADBAND_BEARER_HSO_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_BROADBAND_BEARER_HSO, MMBroadbandBearerHsoClass)) typedef struct _MMBroadbandBearerHso MMBroadbandBearerHso; typedef struct _MMBroadbandBearerHsoClass MMBroadbandBearerHsoClass; typedef struct _MMBroadbandBearerHsoPrivate MMBroadbandBearerHsoPrivate; struct _MMBroadbandBearerHso { MMBroadbandBearer parent; MMBroadbandBearerHsoPrivate *priv; }; struct _MMBroadbandBearerHsoClass { MMBroadbandBearerClass parent; }; GType mm_broadband_bearer_hso_get_type (void); /* Default 3GPP bearer creation implementation */ void mm_broadband_bearer_hso_new (MMBroadbandModemHso *modem, MMBearerProperties *config, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); MMBaseBearer *mm_broadband_bearer_hso_new_finish (GAsyncResult *res, GError **error); gint mm_broadband_bearer_hso_get_connecting_profile_id (MMBroadbandBearerHso *self); #endif /* MM_BROADBAND_BEARER_HSO_H */ ModemManager-1.23.4-dev/src/plugins/option/mm-broadband-modem-hso.c000066400000000000000000000663231456466623000250740ustar00rootroot00000000000000/* -*- 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 hso) 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 - 2012 Red Hat, Inc. * Copyright (C) 2012 Aleksander Morgado */ #include #include #include #include #include #include #include "ModemManager.h" #include "mm-modem-helpers.h" #include "mm-log-object.h" #include "mm-errors-types.h" #include "mm-iface-modem.h" #include "mm-iface-modem-3gpp.h" #include "mm-iface-modem-location.h" #include "mm-base-modem-at.h" #include "mm-broadband-modem-hso.h" #include "mm-broadband-bearer-hso.h" #include "mm-bearer-list.h" #include "mm-shared-option.h" static void shared_option_init (MMSharedOption *iface); static void iface_modem_init (MMIfaceModem *iface); static void iface_modem_3gpp_init (MMIfaceModem3gpp *iface); static void iface_modem_location_init (MMIfaceModemLocation *iface); static MMIfaceModem3gpp *iface_modem_3gpp_parent; static MMIfaceModemLocation *iface_modem_location_parent; G_DEFINE_TYPE_EXTENDED (MMBroadbandModemHso, mm_broadband_modem_hso, MM_TYPE_BROADBAND_MODEM_OPTION, 0, G_IMPLEMENT_INTERFACE (MM_TYPE_SHARED_OPTION, shared_option_init) G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM, iface_modem_init) G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_3GPP, iface_modem_3gpp_init) G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_LOCATION, iface_modem_location_init)); struct _MMBroadbandModemHsoPrivate { /* Regex for connected notifications */ GRegex *_owancall_regex; MMModemLocationSource enabled_sources; }; /*****************************************************************************/ /* Create Bearer (Modem interface) */ static MMBaseBearer * modem_create_bearer_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { return g_task_propagate_pointer (G_TASK (res), error); } static void broadband_bearer_new_ready (GObject *source, GAsyncResult *res, GTask *task) { MMBaseBearer *bearer = NULL; GError *error = NULL; bearer = mm_broadband_bearer_new_finish (res, &error); if (!bearer) g_task_return_error (task, error); else g_task_return_pointer (task, bearer, g_object_unref); g_object_unref (task); } static void broadband_bearer_hso_new_ready (GObject *source, GAsyncResult *res, GTask *task) { MMBaseBearer *bearer = NULL; GError *error = NULL; bearer = mm_broadband_bearer_hso_new_finish (res, &error); if (!bearer) g_task_return_error (task, error); else g_task_return_pointer (task, bearer, g_object_unref); g_object_unref (task); } static void modem_create_bearer (MMIfaceModem *self, MMBearerProperties *properties, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; task = g_task_new (self, NULL, callback, user_data); if (mm_bearer_properties_get_ip_type (properties) & (MM_BEARER_IP_FAMILY_IPV6 | MM_BEARER_IP_FAMILY_IPV4V6)) { mm_obj_dbg (self, "creating generic bearer (IPv6 requested)..."); mm_broadband_bearer_new (MM_BROADBAND_MODEM (self), properties, NULL, /* cancellable */ (GAsyncReadyCallback)broadband_bearer_new_ready, task); return; } mm_obj_dbg (self, "creating HSO bearer..."); mm_broadband_bearer_hso_new (MM_BROADBAND_MODEM_HSO (self), properties, NULL, /* cancellable */ (GAsyncReadyCallback)broadband_bearer_hso_new_ready, task); } /*****************************************************************************/ /* Load unlock retries (Modem interface) */ static MMUnlockRetries * load_unlock_retries_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { return g_task_propagate_pointer (G_TASK (res), error); } static void load_unlock_retries_ready (MMBaseModem *self, GAsyncResult *res, GTask *task) { const gchar *response; GError *error = NULL; int pin1, puk1; response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error); if (!response) { g_task_return_error (task, error); g_object_unref (task); return; } response = mm_strip_tag (response, "_OERCN:"); if (sscanf (response, " %d, %d", &pin1, &puk1) == 2) { MMUnlockRetries *retries; retries = mm_unlock_retries_new (); mm_unlock_retries_set (retries, MM_MODEM_LOCK_SIM_PIN, pin1); mm_unlock_retries_set (retries, MM_MODEM_LOCK_SIM_PUK, puk1); g_task_return_pointer (task, retries, g_object_unref); } else { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Invalid unlock retries response: '%s'", response); } g_object_unref (task); } static void load_unlock_retries (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data) { mm_base_modem_at_command ( MM_BASE_MODEM (self), "_OERCN?", 3, FALSE, (GAsyncReadyCallback)load_unlock_retries_ready, g_task_new (self, NULL, callback, user_data)); } /*****************************************************************************/ /* Setup/Cleanup unsolicited events (3GPP interface) */ typedef struct { guint cid; MMBearerConnectionStatus status; } BearerListReportStatusForeachContext; static void bearer_list_report_status_foreach (MMBaseBearer *bearer, BearerListReportStatusForeachContext *ctx) { gint profile_id; gint connecting_profile_id; if (!MM_IS_BROADBAND_BEARER_HSO (bearer)) return; /* The profile ID in the base bearer is set only once the modem is connected */ profile_id = mm_base_bearer_get_profile_id (bearer); /* The profile ID in the hso bearer is available during the connecting phase */ connecting_profile_id = mm_broadband_bearer_hso_get_connecting_profile_id (MM_BROADBAND_BEARER_HSO (bearer)); if ((profile_id != (gint)ctx->cid) && (connecting_profile_id != (gint)ctx->cid)) return; mm_base_bearer_report_connection_status (MM_BASE_BEARER (bearer), ctx->status); } static void hso_connection_status_changed (MMPortSerialAt *port, GMatchInfo *match_info, MMBroadbandModemHso *self) { g_autoptr(MMBearerList) list = NULL; BearerListReportStatusForeachContext ctx; guint cid; guint status; /* Ensure we got proper parsed values */ if (!mm_get_uint_from_match_info (match_info, 1, &cid) || !mm_get_uint_from_match_info (match_info, 2, &status)) return; /* Setup context */ ctx.cid = cid; ctx.status = MM_BEARER_CONNECTION_STATUS_UNKNOWN; switch (status) { case 1: ctx.status = MM_BEARER_CONNECTION_STATUS_CONNECTED; break; case 3: ctx.status = MM_BEARER_CONNECTION_STATUS_CONNECTION_FAILED; break; case 0: ctx.status = MM_BEARER_CONNECTION_STATUS_DISCONNECTED; break; default: break; } /* If unknown status, don't try to report anything */ if (ctx.status == MM_BEARER_CONNECTION_STATUS_UNKNOWN) return; /* If empty bearer list, nothing else to do */ g_object_get (self, MM_IFACE_MODEM_BEARER_LIST, &list, NULL); /* Will report status only in the bearer with the specific CID */ if (list) mm_bearer_list_foreach (list, (MMBearerListForeachFunc)bearer_list_report_status_foreach, &ctx); } static gboolean modem_3gpp_setup_cleanup_unsolicited_events_finish (MMIfaceModem3gpp *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void parent_setup_unsolicited_events_ready (MMIfaceModem3gpp *self, GAsyncResult *res, GTask *task) { GError *error = NULL; if (!iface_modem_3gpp_parent->setup_unsolicited_events_finish (self, res, &error)) g_task_return_error (task, error); else { /* Our own setup now */ mm_port_serial_at_add_unsolicited_msg_handler ( mm_base_modem_peek_port_primary (MM_BASE_MODEM (self)), MM_BROADBAND_MODEM_HSO (self)->priv->_owancall_regex, (MMPortSerialAtUnsolicitedMsgFn)hso_connection_status_changed, self, NULL); g_task_return_boolean (task, TRUE); } g_object_unref (task); } static void modem_3gpp_setup_unsolicited_events (MMIfaceModem3gpp *self, GAsyncReadyCallback callback, gpointer user_data) { /* Chain up parent's setup */ iface_modem_3gpp_parent->setup_unsolicited_events ( self, (GAsyncReadyCallback)parent_setup_unsolicited_events_ready, g_task_new (self, NULL, callback, user_data)); } static void parent_cleanup_unsolicited_events_ready (MMIfaceModem3gpp *self, GAsyncResult *res, GTask *task) { GError *error = NULL; if (!iface_modem_3gpp_parent->cleanup_unsolicited_events_finish (self, res, &error)) g_task_return_error (task, error); else g_task_return_boolean (task, TRUE); g_object_unref (task); } static void modem_3gpp_cleanup_unsolicited_events (MMIfaceModem3gpp *self, GAsyncReadyCallback callback, gpointer user_data) { /* Our own cleanup first */ mm_port_serial_at_add_unsolicited_msg_handler ( mm_base_modem_peek_port_primary (MM_BASE_MODEM (self)), MM_BROADBAND_MODEM_HSO (self)->priv->_owancall_regex, NULL, NULL, NULL); /* And now chain up parent's cleanup */ iface_modem_3gpp_parent->cleanup_unsolicited_events ( self, (GAsyncReadyCallback)parent_cleanup_unsolicited_events_ready, g_task_new (self, NULL, callback, user_data)); } /*****************************************************************************/ /* Location capabilities loading (Location interface) */ static MMModemLocationSource location_load_capabilities_finish (MMIfaceModemLocation *self, GAsyncResult *res, GError **error) { GError *inner_error = NULL; gssize value; value = g_task_propagate_int (G_TASK (res), &inner_error); if (inner_error) { g_propagate_error (error, inner_error); return MM_MODEM_LOCATION_SOURCE_NONE; } return (MMModemLocationSource)value; } static void parent_load_capabilities_ready (MMIfaceModemLocation *self, GAsyncResult *res, GTask *task) { MMModemLocationSource sources; GError *error = NULL; sources = iface_modem_location_parent->load_capabilities_finish (self, res, &error); if (error) { g_task_return_error (task, error); g_object_unref (task); return; } /* Now our own check. * * We could issue AT_OIFACE? to list the interfaces currently enabled in the * module, to see if there is a 'GPS' interface enabled. But we'll just go * and see if there is already a 'GPS control' AT port and a raw serial 'GPS' * port grabbed. * * NOTE: A deeper implementation could handle the situation where the GPS * interface is found disabled in AT_OIFACE?. In this case, we could issue * AT_OIFACE="GPS",1 to enable it (and AT_OIFACE="GPS",0 to disable it), but * enabling/disabling GPS involves a complete reboot of the modem, which is * probably not the desired thing here. */ if (mm_base_modem_peek_port_gps (MM_BASE_MODEM (self)) && mm_base_modem_peek_port_gps_control (MM_BASE_MODEM (self))) sources |= (MM_MODEM_LOCATION_SOURCE_GPS_NMEA | MM_MODEM_LOCATION_SOURCE_GPS_RAW | MM_MODEM_LOCATION_SOURCE_GPS_UNMANAGED); /* So we're done, complete */ g_task_return_int (task, sources); g_object_unref (task); } static void location_load_capabilities (MMIfaceModemLocation *self, GAsyncReadyCallback callback, gpointer user_data) { /* Chain up parent's setup */ iface_modem_location_parent->load_capabilities ( self, (GAsyncReadyCallback)parent_load_capabilities_ready, g_task_new (self, NULL, callback, user_data)); } /*****************************************************************************/ /* Enable/Disable location gathering (Location interface) */ typedef struct { MMModemLocationSource source; } LocationGatheringContext; /******************************/ /* Disable location gathering */ static gboolean disable_location_gathering_finish (MMIfaceModemLocation *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void gps_disabled_ready (MMBaseModem *self, GAsyncResult *res, GTask *task) { LocationGatheringContext *ctx; MMPortSerialGps *gps_port; GError *error = NULL; mm_base_modem_at_command_full_finish (self, res, &error); ctx = g_task_get_task_data (task); /* Only use the GPS port in NMEA/RAW setups */ if (ctx->source & (MM_MODEM_LOCATION_SOURCE_GPS_NMEA | MM_MODEM_LOCATION_SOURCE_GPS_RAW)) { /* Even if we get an error here, we try to close the GPS port */ gps_port = mm_base_modem_peek_port_gps (self); if (gps_port) mm_port_serial_close (MM_PORT_SERIAL (gps_port)); } if (error) g_task_return_error (task, error); else g_task_return_boolean (task, TRUE); g_object_unref (task); } static void disable_location_gathering (MMIfaceModemLocation *self, MMModemLocationSource source, GAsyncReadyCallback callback, gpointer user_data) { MMBroadbandModemHso *hso = MM_BROADBAND_MODEM_HSO (self); gboolean stop_gps = FALSE; LocationGatheringContext *ctx; GTask *task; ctx = g_new (LocationGatheringContext, 1); ctx->source = source; task = g_task_new (self, NULL, callback, user_data); g_task_set_task_data (task, ctx, g_free); /* Only stop GPS engine if no GPS-related sources enabled */ if (source & (MM_MODEM_LOCATION_SOURCE_GPS_NMEA | MM_MODEM_LOCATION_SOURCE_GPS_RAW | MM_MODEM_LOCATION_SOURCE_GPS_UNMANAGED)) { hso->priv->enabled_sources &= ~source; if (!(hso->priv->enabled_sources & (MM_MODEM_LOCATION_SOURCE_GPS_NMEA | MM_MODEM_LOCATION_SOURCE_GPS_RAW | MM_MODEM_LOCATION_SOURCE_GPS_UNMANAGED))) stop_gps = TRUE; } if (stop_gps) { /* We enable continuous GPS fixes with AT_OGPS=0 */ mm_base_modem_at_command_full (MM_BASE_MODEM (self), mm_base_modem_peek_port_gps_control (MM_BASE_MODEM (self)), "_OGPS=0", 3, FALSE, FALSE, /* raw */ NULL, /* cancellable */ (GAsyncReadyCallback)gps_disabled_ready, task); return; } /* For any other location (e.g. 3GPP), or if still some GPS needed, just return */ g_task_return_boolean (task, TRUE); g_object_unref (task); } /*****************************/ /* Enable location gathering */ static gboolean enable_location_gathering_finish (MMIfaceModemLocation *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void gps_enabled_ready (MMBaseModem *self, GAsyncResult *res, GTask *task) { LocationGatheringContext *ctx; GError *error = NULL; if (!mm_base_modem_at_command_full_finish (self, res, &error)) { g_task_return_error (task, error); g_object_unref (task); return; } ctx = g_task_get_task_data (task); /* Only use the GPS port in NMEA/RAW setups */ if (ctx->source & (MM_MODEM_LOCATION_SOURCE_GPS_NMEA | MM_MODEM_LOCATION_SOURCE_GPS_RAW)) { MMPortSerialGps *gps_port; gps_port = mm_base_modem_peek_port_gps (self); if (!gps_port || !mm_port_serial_open (MM_PORT_SERIAL (gps_port), &error)) { if (error) g_task_return_error (task, error); else g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't open raw GPS serial port"); } else g_task_return_boolean (task, TRUE); } else g_task_return_boolean (task, TRUE); g_object_unref (task); } static void parent_enable_location_gathering_ready (MMIfaceModemLocation *_self, GAsyncResult *res, GTask *task) { MMBroadbandModemHso *self = MM_BROADBAND_MODEM_HSO (_self); LocationGatheringContext *ctx; gboolean start_gps = FALSE; GError *error = NULL; if (!iface_modem_location_parent->enable_location_gathering_finish (_self, res, &error)) { g_task_return_error (task, error); g_object_unref (task); return; } /* Now our own enabling */ ctx = g_task_get_task_data (task); /* NMEA, RAW and UNMANAGED are all enabled in the same way */ if (ctx->source & (MM_MODEM_LOCATION_SOURCE_GPS_NMEA | MM_MODEM_LOCATION_SOURCE_GPS_RAW | MM_MODEM_LOCATION_SOURCE_GPS_UNMANAGED)) { /* Only start GPS engine if not done already. * NOTE: interface already takes care of making sure that raw/nmea and * unmanaged are not enabled at the same time */ if (!(self->priv->enabled_sources & (MM_MODEM_LOCATION_SOURCE_GPS_NMEA | MM_MODEM_LOCATION_SOURCE_GPS_RAW | MM_MODEM_LOCATION_SOURCE_GPS_UNMANAGED))) start_gps = TRUE; self->priv->enabled_sources |= ctx->source; } if (start_gps) { /* We enable continuous GPS fixes with AT_OGPS=2 */ mm_base_modem_at_command_full (MM_BASE_MODEM (self), mm_base_modem_peek_port_gps_control (MM_BASE_MODEM (self)), "_OGPS=2", 3, FALSE, FALSE, /* raw */ NULL, /* cancellable */ (GAsyncReadyCallback)gps_enabled_ready, task); return; } /* For any other location (e.g. 3GPP), or if GPS already running just return */ g_task_return_boolean (task, TRUE); g_object_unref (task); } static void enable_location_gathering (MMIfaceModemLocation *self, MMModemLocationSource source, GAsyncReadyCallback callback, gpointer user_data) { LocationGatheringContext *ctx; GTask *task; ctx = g_new (LocationGatheringContext, 1); ctx->source = source; task = g_task_new (self, NULL, callback, user_data); g_task_set_task_data (task, ctx, g_free); /* Chain up parent's gathering enable */ iface_modem_location_parent->enable_location_gathering ( self, source, (GAsyncReadyCallback)parent_enable_location_gathering_ready, task); } /*****************************************************************************/ /* Setup ports (Broadband modem class) */ static void trace_received (MMPortSerialGps *port, const gchar *trace, MMIfaceModemLocation *self) { mm_iface_modem_location_gps_update (self, trace); } static void setup_ports (MMBroadbandModem *self) { MMPortSerialAt *gps_control_port; MMPortSerialGps *gps_data_port; /* Call parent's setup ports first always */ MM_BROADBAND_MODEM_CLASS (mm_broadband_modem_hso_parent_class)->setup_ports (self); /* _OWANCALL unsolicited messages are only expected in the primary port. */ mm_port_serial_at_add_unsolicited_msg_handler ( mm_base_modem_peek_port_primary (MM_BASE_MODEM (self)), MM_BROADBAND_MODEM_HSO (self)->priv->_owancall_regex, NULL, NULL, NULL); g_object_set (mm_base_modem_peek_port_primary (MM_BASE_MODEM (self)), MM_PORT_SERIAL_SEND_DELAY, (guint64) 0, /* built-in echo removal conflicts with unsolicited _OWANCALL * messages, which are not prefixed. */ MM_PORT_SERIAL_AT_REMOVE_ECHO, FALSE, NULL); gps_control_port = mm_base_modem_peek_port_gps_control (MM_BASE_MODEM (self)); gps_data_port = mm_base_modem_peek_port_gps (MM_BASE_MODEM (self)); if (gps_control_port && gps_data_port) { /* It may happen that the modem was started with GPS already enabled, or * maybe ModemManager got rebooted and it was left enabled before. We'll make * sure that it is disabled when we initialize the modem */ mm_base_modem_at_command_full (MM_BASE_MODEM (self), gps_control_port, "_OGPS=0", 3, FALSE, FALSE, NULL, NULL, NULL); /* Add handler for the NMEA traces */ mm_port_serial_gps_add_trace_handler (gps_data_port, (MMPortSerialGpsTraceFn)trace_received, self, NULL); } } /*****************************************************************************/ MMBroadbandModemHso * mm_broadband_modem_hso_new (const gchar *device, const gchar *physdev, const gchar **drivers, const gchar *plugin, guint16 vendor_id, guint16 product_id) { return g_object_new (MM_TYPE_BROADBAND_MODEM_HSO, MM_BASE_MODEM_DEVICE, device, MM_BASE_MODEM_PHYSDEV, physdev, MM_BASE_MODEM_DRIVERS, drivers, MM_BASE_MODEM_PLUGIN, plugin, MM_BASE_MODEM_VENDOR_ID, vendor_id, MM_BASE_MODEM_PRODUCT_ID, product_id, /* Generic bearer (AT) and HSO bearer (NET) supported */ MM_BASE_MODEM_DATA_NET_SUPPORTED, TRUE, MM_BASE_MODEM_DATA_TTY_SUPPORTED, TRUE, NULL); } static void finalize (GObject *object) { MMBroadbandModemHso *self = MM_BROADBAND_MODEM_HSO (object); g_regex_unref (self->priv->_owancall_regex); G_OBJECT_CLASS (mm_broadband_modem_hso_parent_class)->finalize (object); } static void mm_broadband_modem_hso_init (MMBroadbandModemHso *self) { /* Initialize private data */ self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, MM_TYPE_BROADBAND_MODEM_HSO, MMBroadbandModemHsoPrivate); self->priv->_owancall_regex = g_regex_new ("_OWANCALL: (\\d),\\s*(\\d)\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); self->priv->enabled_sources = MM_MODEM_LOCATION_SOURCE_NONE; } static void shared_option_init (MMSharedOption *iface) { } static void iface_modem_init (MMIfaceModem *iface) { iface->create_sim = mm_shared_option_create_sim; iface->create_sim_finish = mm_shared_option_create_sim_finish; iface->create_bearer = modem_create_bearer; iface->create_bearer_finish = modem_create_bearer_finish; iface->load_unlock_retries = load_unlock_retries; iface->load_unlock_retries_finish = load_unlock_retries_finish; /* HSO modems don't need the extra 10s wait after powering up */ iface->modem_after_power_up = NULL; iface->modem_after_power_up_finish = NULL; } static void iface_modem_3gpp_init (MMIfaceModem3gpp *iface) { iface_modem_3gpp_parent = g_type_interface_peek_parent (iface); iface->setup_unsolicited_events = modem_3gpp_setup_unsolicited_events; iface->setup_unsolicited_events_finish = modem_3gpp_setup_cleanup_unsolicited_events_finish; iface->cleanup_unsolicited_events = modem_3gpp_cleanup_unsolicited_events; iface->cleanup_unsolicited_events_finish = modem_3gpp_setup_cleanup_unsolicited_events_finish; } static void iface_modem_location_init (MMIfaceModemLocation *iface) { iface_modem_location_parent = g_type_interface_peek_parent (iface); iface->load_capabilities = location_load_capabilities; iface->load_capabilities_finish = location_load_capabilities_finish; iface->enable_location_gathering = enable_location_gathering; iface->enable_location_gathering_finish = enable_location_gathering_finish; iface->disable_location_gathering = disable_location_gathering; iface->disable_location_gathering_finish = disable_location_gathering_finish; } static void mm_broadband_modem_hso_class_init (MMBroadbandModemHsoClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); MMBroadbandModemClass *broadband_modem_class = MM_BROADBAND_MODEM_CLASS (klass); g_type_class_add_private (object_class, sizeof (MMBroadbandModemHsoPrivate)); object_class->finalize = finalize; broadband_modem_class->setup_ports = setup_ports; } ModemManager-1.23.4-dev/src/plugins/option/mm-broadband-modem-hso.h000066400000000000000000000046241456466623000250750ustar00rootroot00000000000000/* -*- 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 hso) 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 - 2012 Red Hat, Inc. * Copyright (C) 2012 Aleksander Morgado */ #ifndef MM_BROADBAND_MODEM_HSO_H #define MM_BROADBAND_MODEM_HSO_H #include "mm-broadband-modem-option.h" #define MM_TYPE_BROADBAND_MODEM_HSO (mm_broadband_modem_hso_get_type ()) #define MM_BROADBAND_MODEM_HSO(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_BROADBAND_MODEM_HSO, MMBroadbandModemHso)) #define MM_BROADBAND_MODEM_HSO_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_BROADBAND_MODEM_HSO, MMBroadbandModemHsoClass)) #define MM_IS_BROADBAND_MODEM_HSO(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_BROADBAND_MODEM_HSO)) #define MM_IS_BROADBAND_MODEM_HSO_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_BROADBAND_MODEM_HSO)) #define MM_BROADBAND_MODEM_HSO_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_BROADBAND_MODEM_HSO, MMBroadbandModemHsoClass)) typedef struct _MMBroadbandModemHso MMBroadbandModemHso; typedef struct _MMBroadbandModemHsoClass MMBroadbandModemHsoClass; typedef struct _MMBroadbandModemHsoPrivate MMBroadbandModemHsoPrivate; struct _MMBroadbandModemHso { MMBroadbandModemOption parent; MMBroadbandModemHsoPrivate *priv; }; struct _MMBroadbandModemHsoClass{ MMBroadbandModemOptionClass parent; }; GType mm_broadband_modem_hso_get_type (void); MMBroadbandModemHso *mm_broadband_modem_hso_new (const gchar *device, const gchar *physdev, const gchar **drivers, const gchar *plugin, guint16 vendor_id, guint16 product_id); #endif /* MM_BROADBAND_MODEM_HSO_H */ ModemManager-1.23.4-dev/src/plugins/option/mm-broadband-modem-option.c000066400000000000000000001162731456466623000256130ustar00rootroot00000000000000/* -*- 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 - 2012 Red Hat, Inc. * Copyright (C) 2012 Aleksander Morgado */ #include #include #include #include #include #include #include "ModemManager.h" #include "mm-modem-helpers.h" #include "mm-log-object.h" #include "mm-errors-types.h" #include "mm-iface-modem.h" #include "mm-iface-modem-3gpp.h" #include "mm-base-modem-at.h" #include "mm-broadband-modem-option.h" #include "mm-shared-option.h" static void shared_option_init (MMSharedOption *iface); static void iface_modem_init (MMIfaceModem *iface); static void iface_modem_3gpp_init (MMIfaceModem3gpp *iface); static MMIfaceModem *iface_modem_parent; static MMIfaceModem3gpp *iface_modem_3gpp_parent; G_DEFINE_TYPE_EXTENDED (MMBroadbandModemOption, mm_broadband_modem_option, MM_TYPE_BROADBAND_MODEM, 0, G_IMPLEMENT_INTERFACE (MM_TYPE_SHARED_OPTION, shared_option_init) G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM, iface_modem_init) G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_3GPP, iface_modem_3gpp_init)) struct _MMBroadbandModemOptionPrivate { /* Regex for access-technology related notifications */ GRegex *_ossysi_regex; GRegex *_octi_regex; GRegex *_ouwcti_regex; /* Regex for signal quality related notifications */ GRegex *_osigq_regex; /* Regex for other notifications to ignore */ GRegex *ignore_regex; guint after_power_up_wait_id; }; /*****************************************************************************/ /* Load supported modes (Modem interface) */ static GArray * load_supported_modes_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { return g_task_propagate_pointer (G_TASK (res), error); } static void parent_load_supported_modes_ready (MMIfaceModem *self, GAsyncResult *res, GTask *task) { GError *error = NULL; GArray *all; GArray *combinations; GArray *filtered; MMModemModeCombination mode; all = iface_modem_parent->load_supported_modes_finish (self, res, &error); if (!all) { g_task_return_error (task, error); g_object_unref (task); return; } /* Build list of combinations */ combinations = g_array_sized_new (FALSE, FALSE, sizeof (MMModemModeCombination), 5); /* 2G only */ mode.allowed = MM_MODEM_MODE_2G; mode.preferred = MM_MODEM_MODE_NONE; g_array_append_val (combinations, mode); /* 3G only */ mode.allowed = MM_MODEM_MODE_3G; mode.preferred = MM_MODEM_MODE_NONE; g_array_append_val (combinations, mode); /* 2G and 3G */ mode.allowed = (MM_MODEM_MODE_2G | MM_MODEM_MODE_3G); mode.preferred = MM_MODEM_MODE_NONE; g_array_append_val (combinations, mode); /* 2G and 3G, 2G preferred */ mode.allowed = (MM_MODEM_MODE_2G | MM_MODEM_MODE_3G); mode.preferred = MM_MODEM_MODE_2G; g_array_append_val (combinations, mode); /* 2G and 3G, 3G preferred */ mode.allowed = (MM_MODEM_MODE_2G | MM_MODEM_MODE_3G); mode.preferred = MM_MODEM_MODE_3G; g_array_append_val (combinations, mode); /* Filter out those unsupported modes */ filtered = mm_filter_supported_modes (all, combinations, self); g_array_unref (all); g_array_unref (combinations); g_task_return_pointer (task, filtered, (GDestroyNotify) g_array_unref); g_object_unref (task); } static void load_supported_modes (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data) { /* Run parent's loading */ iface_modem_parent->load_supported_modes ( MM_IFACE_MODEM (self), (GAsyncReadyCallback)parent_load_supported_modes_ready, g_task_new (self, NULL, callback, user_data)); } /*****************************************************************************/ /* Load initial allowed/preferred modes (Modem interface) */ static gboolean load_current_modes_finish (MMIfaceModem *self, GAsyncResult *res, MMModemMode *allowed, MMModemMode *preferred, GError **error) { const gchar *response; const gchar *str; gint a, b; response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, error); if (!response) return FALSE; str = mm_strip_tag (response, "_OPSYS:"); if (!sscanf (str, "%d,%d", &a, &b)) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't parse OPSYS response: '%s'", response); return FALSE; } switch (a) { case 0: *allowed = MM_MODEM_MODE_2G; *preferred = MM_MODEM_MODE_NONE; return TRUE; case 1: *allowed = MM_MODEM_MODE_3G; *preferred = MM_MODEM_MODE_NONE; return TRUE; case 2: *allowed = (MM_MODEM_MODE_2G | MM_MODEM_MODE_3G); *preferred = MM_MODEM_MODE_2G; return TRUE; case 3: *allowed = (MM_MODEM_MODE_2G | MM_MODEM_MODE_3G); *preferred = MM_MODEM_MODE_3G; return TRUE; case 5: /* any */ *allowed = (MM_MODEM_MODE_2G | MM_MODEM_MODE_3G); *preferred = MM_MODEM_MODE_NONE; return TRUE; default: break; } g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't parse unexpected OPSYS response: '%s'", response); return FALSE; } static void load_current_modes (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data) { mm_base_modem_at_command (MM_BASE_MODEM (self), "_OPSYS?", 3, FALSE, callback, user_data); } /*****************************************************************************/ /* Set allowed modes (Modem interface) */ static gboolean set_current_modes_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void allowed_mode_update_ready (MMBaseModem *self, GAsyncResult *res, GTask *task) { GError *error = NULL; mm_base_modem_at_command_finish (self, res, &error); if (error) /* Let the error be critical. */ g_task_return_error (task, error); else g_task_return_boolean (task, TRUE); g_object_unref (task); } static void set_current_modes (MMIfaceModem *self, MMModemMode allowed, MMModemMode preferred, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; gchar *command; gint option_mode = -1; task = g_task_new (self, NULL, callback, user_data); if (allowed == MM_MODEM_MODE_2G) option_mode = 0; else if (allowed == MM_MODEM_MODE_3G) option_mode = 1; else if (allowed == (MM_MODEM_MODE_2G | MM_MODEM_MODE_3G)) { if (preferred == MM_MODEM_MODE_2G) option_mode = 2; else if (preferred == MM_MODEM_MODE_3G) option_mode = 3; else /* none preferred, so AUTO */ option_mode = 5; } else if (allowed == MM_MODEM_MODE_ANY && preferred == MM_MODEM_MODE_NONE) option_mode = 5; if (option_mode < 0) { gchar *allowed_str; gchar *preferred_str; allowed_str = mm_modem_mode_build_string_from_mask (allowed); preferred_str = mm_modem_mode_build_string_from_mask (preferred); g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Requested mode (allowed: '%s', preferred: '%s') not " "supported by the modem.", allowed_str, preferred_str); g_object_unref (task); g_free (allowed_str); g_free (preferred_str); return; } command = g_strdup_printf ("AT_OPSYS=%d,2", option_mode); mm_base_modem_at_command ( MM_BASE_MODEM (self), command, 3, FALSE, (GAsyncReadyCallback)allowed_mode_update_ready, task); g_free (command); } /*****************************************************************************/ /* Load access technologies (Modem interface) */ typedef enum { ACCESS_TECHNOLOGIES_STEP_FIRST, ACCESS_TECHNOLOGIES_STEP_OSSYS, ACCESS_TECHNOLOGIES_STEP_OCTI, ACCESS_TECHNOLOGIES_STEP_OWCTI, ACCESS_TECHNOLOGIES_STEP_LAST } AccessTechnologiesStep; typedef struct { MMModemAccessTechnology access_technology; gboolean check_2g; gboolean check_3g; AccessTechnologiesStep step; } AccessTechnologiesContext; static void load_access_technologies_step (GTask *task); static gboolean load_access_technologies_finish (MMIfaceModem *self, GAsyncResult *res, MMModemAccessTechnology *access_technologies, guint *mask, GError **error) { GError *inner_error = NULL; gssize value; value = g_task_propagate_int (G_TASK (res), &inner_error); if (inner_error) { g_propagate_error (error, inner_error); return FALSE; } /* We are reporting ALL 3GPP access technologies here */ *access_technologies = (MMModemAccessTechnology) value; *mask = MM_IFACE_MODEM_3GPP_ALL_ACCESS_TECHNOLOGIES_MASK; return TRUE; } static gboolean ossys_to_mm (gchar ossys, MMModemAccessTechnology *access_technology) { if (ossys == '0') { *access_technology = MM_MODEM_ACCESS_TECHNOLOGY_GPRS; return TRUE; } if (ossys == '2') { *access_technology = MM_MODEM_ACCESS_TECHNOLOGY_UMTS; return TRUE; } if (ossys == '3') { *access_technology = MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN; return TRUE; } return FALSE; } static gboolean parse_ossys_response (const gchar *response, MMModemAccessTechnology *access_technology) { MMModemAccessTechnology current = MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN; const gchar *p; g_autoptr(GRegex) r = NULL; g_autoptr(GMatchInfo) match_info = NULL; p = mm_strip_tag (response, "_OSSYS:"); r = g_regex_new ("(\\d),(\\d)", G_REGEX_UNGREEDY, 0, NULL); g_assert (r != NULL); g_regex_match (r, p, 0, &match_info); if (g_match_info_matches (match_info)) { g_autofree gchar *str = NULL; str = g_match_info_fetch (match_info, 2); if (str && ossys_to_mm (str[0], ¤t)) { *access_technology = current; return TRUE; } } return FALSE; } static void ossys_query_ready (MMBaseModem *self, GAsyncResult *res, GTask *task) { AccessTechnologiesContext *ctx; const gchar *response; ctx = g_task_get_task_data (task); /* If for some reason the OSSYS request failed, still try to check * explicit 2G/3G mode with OCTI and OWCTI; maybe we'll get something. */ response = mm_base_modem_at_command_finish (self, res, NULL); /* Response is _OSSYS: , so we must skip the */ if (response && parse_ossys_response (response, &ctx->access_technology)) { /* If the OSSYS response indicated a generic access tech type * then only check for more specific access tech of that type. */ if (ctx->access_technology == MM_MODEM_ACCESS_TECHNOLOGY_GPRS) ctx->check_3g = FALSE; else if (ctx->access_technology == MM_MODEM_ACCESS_TECHNOLOGY_UMTS) ctx->check_2g = FALSE; } /* Go on to next step */ ctx->step++; load_access_technologies_step (task); } static gboolean octi_to_mm (gchar octi, MMModemAccessTechnology *access_technology) { if (octi == '1') { *access_technology = MM_MODEM_ACCESS_TECHNOLOGY_GSM; return TRUE; } if (octi == '2') { *access_technology = MM_MODEM_ACCESS_TECHNOLOGY_GPRS; return TRUE; } if (octi == '3') { *access_technology = MM_MODEM_ACCESS_TECHNOLOGY_EDGE; return TRUE; } return FALSE; } static gboolean parse_octi_response (const gchar *response, MMModemAccessTechnology *access_technology) { MMModemAccessTechnology current = MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN; const gchar *p; g_autoptr(GRegex) r = NULL; g_autoptr(GMatchInfo) match_info = NULL; p = mm_strip_tag (response, "_OCTI:"); r = g_regex_new ("(\\d),(\\d)", G_REGEX_UNGREEDY, 0, NULL); g_assert (r != NULL); g_regex_match (r, p, 0, &match_info); if (g_match_info_matches (match_info)) { g_autofree gchar *str = NULL; str = g_match_info_fetch (match_info, 2); if (str && octi_to_mm (str[0], ¤t)) { *access_technology = current; return TRUE; } } return FALSE; } static void octi_query_ready (MMBaseModem *self, GAsyncResult *res, GTask *task) { AccessTechnologiesContext *ctx; MMModemAccessTechnology octi = MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN; const gchar *response; ctx = g_task_get_task_data (task); response = mm_base_modem_at_command_finish (self, res, NULL); if (response && parse_octi_response (response, &octi)) { /* If current tech is 2G or unknown then use the more specific * OCTI response. */ if (ctx->access_technology < MM_MODEM_ACCESS_TECHNOLOGY_UMTS) ctx->access_technology = octi; } /* Go on to next step */ ctx->step++; load_access_technologies_step (task); } static gboolean owcti_to_mm (gchar owcti, MMModemAccessTechnology *access_technology) { if (owcti == '1') { *access_technology = MM_MODEM_ACCESS_TECHNOLOGY_UMTS; return TRUE; } if (owcti == '2') { *access_technology = MM_MODEM_ACCESS_TECHNOLOGY_HSDPA; return TRUE; } if (owcti == '3') { *access_technology = MM_MODEM_ACCESS_TECHNOLOGY_HSUPA; return TRUE; } if (owcti == '4') { *access_technology = MM_MODEM_ACCESS_TECHNOLOGY_HSPA; return TRUE; } return FALSE; } static gboolean parse_owcti_response (const gchar *response, MMModemAccessTechnology *access_technology) { response = mm_strip_tag (response, "_OWCTI:"); return owcti_to_mm (*response, access_technology); } static void owcti_query_ready (MMBaseModem *self, GAsyncResult *res, GTask *task) { AccessTechnologiesContext *ctx; MMModemAccessTechnology owcti = MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN; const gchar *response; ctx = g_task_get_task_data (task); response = mm_base_modem_at_command_finish (self, res, NULL); if (response && parse_owcti_response (response, &owcti)) { ctx->access_technology = owcti; } /* Go on to next step */ ctx->step++; load_access_technologies_step (task); } static void load_access_technologies_step (GTask *task) { MMBroadbandModemOption *self; AccessTechnologiesContext *ctx; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); switch (ctx->step) { case ACCESS_TECHNOLOGIES_STEP_FIRST: ctx->step++; /* fall through */ case ACCESS_TECHNOLOGIES_STEP_OSSYS: mm_base_modem_at_command (MM_BASE_MODEM (self), "_OSSYS?", 3, FALSE, (GAsyncReadyCallback)ossys_query_ready, task); break; case ACCESS_TECHNOLOGIES_STEP_OCTI: if (ctx->check_2g) { mm_base_modem_at_command (MM_BASE_MODEM (self), "_OCTI?", 3, FALSE, (GAsyncReadyCallback)octi_query_ready, task); return; } ctx->step++; /* fall through */ case ACCESS_TECHNOLOGIES_STEP_OWCTI: if (ctx->check_3g) { mm_base_modem_at_command (MM_BASE_MODEM (self), "_OWCTI?", 3, FALSE, (GAsyncReadyCallback)owcti_query_ready, task); return; } ctx->step++; /* fall through */ case ACCESS_TECHNOLOGIES_STEP_LAST: /* All done, set result and complete */ g_task_return_int (task, ctx->access_technology); g_object_unref (task); break; default: g_assert_not_reached (); } } static void run_access_technology_loading_sequence (MMIfaceModem *self, AccessTechnologiesStep first, gboolean check_2g, gboolean check_3g, GAsyncReadyCallback callback, gpointer user_data) { AccessTechnologiesContext *ctx; GTask *task; ctx = g_new (AccessTechnologiesContext, 1); ctx->step = first; ctx->check_2g = check_2g; ctx->check_3g = check_3g; ctx->access_technology = MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN; task = g_task_new (self, NULL, callback, user_data); g_task_set_task_data (task, ctx, g_free); load_access_technologies_step (task); } static void load_access_technologies (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data) { run_access_technology_loading_sequence (self, ACCESS_TECHNOLOGIES_STEP_FIRST, TRUE, /* check 2g */ TRUE, /* check 3g */ callback, user_data); } /*****************************************************************************/ /* After power up (Modem interface) */ static gboolean modem_after_power_up_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static gboolean after_power_up_wait_cb (GTask *task) { MMBroadbandModemOption *self; self = g_task_get_source_object (task); self->priv->after_power_up_wait_id = 0; g_task_return_boolean (task, TRUE); g_object_unref (task); return G_SOURCE_REMOVE; } static void modem_after_power_up (MMIfaceModem *_self, GAsyncReadyCallback callback, gpointer user_data) { MMBroadbandModemOption *self = MM_BROADBAND_MODEM_OPTION (_self); /* Some Option devices return OK on +CFUN=1 right away but need some time * to finish initialization. */ g_warn_if_fail (self->priv->after_power_up_wait_id == 0); self->priv->after_power_up_wait_id = g_timeout_add_seconds (10, (GSourceFunc)after_power_up_wait_cb, g_task_new (self, NULL, callback, user_data)); } /*****************************************************************************/ /* IMSI loading (3GPP interface) */ static gchar * modem_3gpp_load_imei_finish (MMIfaceModem3gpp *self, GAsyncResult *res, GError **error) { gchar *imei; gchar *comma; imei = g_strdup (mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, error)); if (!imei) return NULL; /* IMEI reported by Option modems contain the IMEI plus something else: * * (ttyHS4): --> 'AT+CGSN' * (ttyHS4): <-- '357516032005989,TR19A8P11ROK' */ comma = strchr (imei, ','); if (comma) *comma = '\0'; return imei; } static void modem_3gpp_load_imei (MMIfaceModem3gpp *self, GAsyncReadyCallback callback, gpointer user_data) { mm_base_modem_at_command (MM_BASE_MODEM (self), "+CGSN", 3, TRUE, callback, user_data); } /*****************************************************************************/ /* Setup/Cleanup unsolicited events (3GPP interface) */ static void option_ossys_tech_changed (MMPortSerialAt *port, GMatchInfo *info, MMBroadbandModemOption *self) { MMModemAccessTechnology act = MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN; gchar *str; str = g_match_info_fetch (info, 1); if (str) { ossys_to_mm (str[0], &act); g_free (str); } mm_iface_modem_update_access_technologies (MM_IFACE_MODEM (self), act, MM_IFACE_MODEM_3GPP_ALL_ACCESS_TECHNOLOGIES_MASK); /* _OSSYSI only indicates general 2G/3G mode, so queue up some explicit * access technology requests. */ if (act == MM_MODEM_ACCESS_TECHNOLOGY_GPRS) run_access_technology_loading_sequence (MM_IFACE_MODEM (self), ACCESS_TECHNOLOGIES_STEP_OCTI, TRUE, /* check 2g */ FALSE, /* check 3g */ NULL, NULL); else if (act == MM_MODEM_ACCESS_TECHNOLOGY_UMTS) run_access_technology_loading_sequence (MM_IFACE_MODEM (self), ACCESS_TECHNOLOGIES_STEP_OWCTI, FALSE, /* check 2g */ TRUE, /* check 3g */ NULL, NULL); } static void option_2g_tech_changed (MMPortSerialAt *port, GMatchInfo *match_info, MMBroadbandModemOption *self) { MMModemAccessTechnology act = MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN; gchar *str; str = g_match_info_fetch (match_info, 1); if (str && octi_to_mm (str[0], &act)) mm_iface_modem_update_access_technologies (MM_IFACE_MODEM (self), act, MM_IFACE_MODEM_3GPP_ALL_ACCESS_TECHNOLOGIES_MASK); g_free (str); } static void option_3g_tech_changed (MMPortSerialAt *port, GMatchInfo *match_info, MMBroadbandModemOption *self) { MMModemAccessTechnology act = MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN; gchar *str; str = g_match_info_fetch (match_info, 1); if (str && owcti_to_mm (str[0], &act)) mm_iface_modem_update_access_technologies (MM_IFACE_MODEM (self), act, MM_IFACE_MODEM_3GPP_ALL_ACCESS_TECHNOLOGIES_MASK); g_free (str); } static void option_signal_changed (MMPortSerialAt *port, GMatchInfo *match_info, MMBroadbandModemOption *self) { gchar *str; guint quality = 0; str = g_match_info_fetch (match_info, 1); if (str) { quality = atoi (str); g_free (str); } if (quality == 99) { /* 99 means unknown */ quality = 0; } else { /* Normalize the quality */ quality = MM_CLAMP_HIGH (quality, 31) * 100 / 31; } mm_iface_modem_update_signal_quality (MM_IFACE_MODEM (self), quality); } static void set_unsolicited_events_handlers (MMBroadbandModemOption *self, gboolean enable) { MMPortSerialAt *ports[2]; guint i; ports[0] = mm_base_modem_peek_port_primary (MM_BASE_MODEM (self)); ports[1] = mm_base_modem_peek_port_secondary (MM_BASE_MODEM (self)); /* Enable unsolicited events in given port */ for (i = 0; i < G_N_ELEMENTS (ports); i++) { if (!ports[i]) continue; /* Access technology related */ mm_port_serial_at_add_unsolicited_msg_handler ( ports[i], self->priv->_ossysi_regex, enable ? (MMPortSerialAtUnsolicitedMsgFn)option_ossys_tech_changed : NULL, enable ? self : NULL, NULL); mm_port_serial_at_add_unsolicited_msg_handler ( ports[i], self->priv->_octi_regex, enable ? (MMPortSerialAtUnsolicitedMsgFn)option_2g_tech_changed : NULL, enable ? self : NULL, NULL); mm_port_serial_at_add_unsolicited_msg_handler ( ports[i], self->priv->_ouwcti_regex, enable ? (MMPortSerialAtUnsolicitedMsgFn)option_3g_tech_changed : NULL, enable ? self : NULL, NULL); /* Signal quality related */ mm_port_serial_at_add_unsolicited_msg_handler ( ports[i], self->priv->_osigq_regex, enable ? (MMPortSerialAtUnsolicitedMsgFn)option_signal_changed : NULL, enable ? self : NULL, NULL); /* Other unsolicited events to always ignore */ if (!enable) mm_port_serial_at_add_unsolicited_msg_handler ( ports[i], self->priv->ignore_regex, NULL, NULL, NULL); } } static gboolean modem_3gpp_setup_cleanup_unsolicited_events_finish (MMIfaceModem3gpp *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void parent_setup_unsolicited_events_ready (MMIfaceModem3gpp *self, GAsyncResult *res, GTask *task) { GError *error = NULL; if (!iface_modem_3gpp_parent->setup_unsolicited_events_finish (self, res, &error)) g_task_return_error (task, error); else { /* Our own setup now */ set_unsolicited_events_handlers (MM_BROADBAND_MODEM_OPTION (self), TRUE); g_task_return_boolean (task, TRUE); } g_object_unref (task); } static void modem_3gpp_setup_unsolicited_events (MMIfaceModem3gpp *self, GAsyncReadyCallback callback, gpointer user_data) { /* Chain up parent's setup */ iface_modem_3gpp_parent->setup_unsolicited_events ( self, (GAsyncReadyCallback)parent_setup_unsolicited_events_ready, g_task_new (self, NULL, callback, user_data)); } static void parent_cleanup_unsolicited_events_ready (MMIfaceModem3gpp *self, GAsyncResult *res, GTask *task) { GError *error = NULL; if (!iface_modem_3gpp_parent->cleanup_unsolicited_events_finish (self, res, &error)) g_task_return_error (task, error); else g_task_return_boolean (task, TRUE); g_object_unref (task); } static void modem_3gpp_cleanup_unsolicited_events (MMIfaceModem3gpp *self, GAsyncReadyCallback callback, gpointer user_data) { /* Our own cleanup first */ set_unsolicited_events_handlers (MM_BROADBAND_MODEM_OPTION (self), FALSE); /* And now chain up parent's cleanup */ iface_modem_3gpp_parent->cleanup_unsolicited_events ( self, (GAsyncReadyCallback)parent_cleanup_unsolicited_events_ready, g_task_new (self, NULL, callback, user_data)); } /*****************************************************************************/ /* Enabling unsolicited events (3GPP interface) */ static gboolean modem_3gpp_enable_unsolicited_events_finish (MMIfaceModem3gpp *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void own_enable_unsolicited_events_ready (MMBaseModem *self, GAsyncResult *res, GTask *task) { GError *error = NULL; mm_base_modem_at_sequence_full_finish (self, res, NULL, &error); if (error) g_task_return_error (task, error); else g_task_return_boolean (task, TRUE); g_object_unref (task); } static const MMBaseModemAtCommand unsolicited_enable_sequence[] = { { "_OSSYS=1", 3, FALSE, NULL }, { "_OCTI=1", 3, FALSE, NULL }, { "_OUWCTI=1", 3, FALSE, NULL }, { "_OSQI=1", 3, FALSE, NULL }, { NULL } }; static void parent_enable_unsolicited_events_ready (MMIfaceModem3gpp *self, GAsyncResult *res, GTask *task) { GError *error = NULL; if (!iface_modem_3gpp_parent->enable_unsolicited_events_finish (self, res, &error)) { g_task_return_error (task, error); g_object_unref (task); } /* Our own enable now */ mm_base_modem_at_sequence_full ( MM_BASE_MODEM (self), mm_base_modem_peek_port_primary (MM_BASE_MODEM (self)), unsolicited_enable_sequence, NULL, /* response_processor_context */ NULL, /* response_processor_context_free */ NULL, /* cancellable */ (GAsyncReadyCallback)own_enable_unsolicited_events_ready, task); } static void modem_3gpp_enable_unsolicited_events (MMIfaceModem3gpp *self, GAsyncReadyCallback callback, gpointer user_data) { /* Chain up parent's enable */ iface_modem_3gpp_parent->enable_unsolicited_events ( self, (GAsyncReadyCallback)parent_enable_unsolicited_events_ready, g_task_new (self, NULL, callback, user_data)); } /*****************************************************************************/ /* Disabling unsolicited events (3GPP interface) */ static gboolean modem_3gpp_disable_unsolicited_events_finish (MMIfaceModem3gpp *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static const MMBaseModemAtCommand unsolicited_disable_sequence[] = { { "_OSSYS=0", 3, FALSE, NULL }, { "_OCTI=0", 3, FALSE, NULL }, { "_OUWCTI=0", 3, FALSE, NULL }, { "_OSQI=0", 3, FALSE, NULL }, { NULL } }; static void parent_disable_unsolicited_events_ready (MMIfaceModem3gpp *self, GAsyncResult *res, GTask *task) { GError *error = NULL; if (!iface_modem_3gpp_parent->disable_unsolicited_events_finish (self, res, &error)) g_task_return_error (task, error); else g_task_return_boolean (task, TRUE); g_object_unref (task); } static void own_disable_unsolicited_events_ready (MMBaseModem *self, GAsyncResult *res, GTask *task) { GError *error = NULL; mm_base_modem_at_sequence_full_finish (self, res, NULL, &error); if (error) { g_task_return_error (task, error); g_object_unref (task); return; } /* Next, chain up parent's disable */ iface_modem_3gpp_parent->disable_unsolicited_events ( MM_IFACE_MODEM_3GPP (self), (GAsyncReadyCallback)parent_disable_unsolicited_events_ready, task); } static void modem_3gpp_disable_unsolicited_events (MMIfaceModem3gpp *self, GAsyncReadyCallback callback, gpointer user_data) { /* Our own disable first */ mm_base_modem_at_sequence_full ( MM_BASE_MODEM (self), mm_base_modem_peek_port_primary (MM_BASE_MODEM (self)), unsolicited_disable_sequence, NULL, /* response_processor_context */ NULL, /* response_processor_context_free */ NULL, /* cancellable */ (GAsyncReadyCallback)own_disable_unsolicited_events_ready, g_task_new (self, NULL, callback, user_data)); } /*****************************************************************************/ /* Setup ports (Broadband modem class) */ static void setup_ports (MMBroadbandModem *self) { /* Call parent's setup ports first always */ MM_BROADBAND_MODEM_CLASS (mm_broadband_modem_option_parent_class)->setup_ports (self); /* Now reset the unsolicited messages we'll handle when enabled */ set_unsolicited_events_handlers (MM_BROADBAND_MODEM_OPTION (self), FALSE); } /*****************************************************************************/ static gboolean is_nozomi (const gchar **drivers) { if (drivers) { guint i; for (i = 0; drivers[i]; i++) { if (g_str_equal (drivers[i], "nozomi")) return TRUE; } } return FALSE; } MMBroadbandModemOption * mm_broadband_modem_option_new (const gchar *device, const gchar *physdev, const gchar **drivers, const gchar *plugin, guint16 vendor_id, guint16 product_id) { MMModem3gppFacility ignored; /* Ignore PH-SIM facility in 'nozomi' managed modems */ ignored = is_nozomi (drivers) ? MM_MODEM_3GPP_FACILITY_PH_SIM : MM_MODEM_3GPP_FACILITY_NONE; return g_object_new (MM_TYPE_BROADBAND_MODEM_OPTION, MM_BASE_MODEM_DEVICE, device, MM_BASE_MODEM_PHYSDEV, physdev, MM_BASE_MODEM_DRIVERS, drivers, MM_BASE_MODEM_PLUGIN, plugin, MM_BASE_MODEM_VENDOR_ID, vendor_id, MM_BASE_MODEM_PRODUCT_ID, product_id, /* Generic bearer supports TTY only */ MM_BASE_MODEM_DATA_NET_SUPPORTED, FALSE, MM_BASE_MODEM_DATA_TTY_SUPPORTED, TRUE, MM_IFACE_MODEM_3GPP_IGNORED_FACILITY_LOCKS, ignored, NULL); } static void finalize (GObject *object) { MMBroadbandModemOption *self = MM_BROADBAND_MODEM_OPTION (object); g_regex_unref (self->priv->_ossysi_regex); g_regex_unref (self->priv->_octi_regex); g_regex_unref (self->priv->_ouwcti_regex); g_regex_unref (self->priv->_osigq_regex); g_regex_unref (self->priv->ignore_regex); G_OBJECT_CLASS (mm_broadband_modem_option_parent_class)->finalize (object); } static void mm_broadband_modem_option_init (MMBroadbandModemOption *self) { /* Initialize private data */ self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, MM_TYPE_BROADBAND_MODEM_OPTION, MMBroadbandModemOptionPrivate); self->priv->after_power_up_wait_id = 0; /* Prepare regular expressions to setup */ self->priv->_ossysi_regex = g_regex_new ("\\r\\n_OSSYSI:\\s*(\\d+)\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); self->priv->_octi_regex = g_regex_new ("\\r\\n_OCTI:\\s*(\\d+)\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); self->priv->_ouwcti_regex = g_regex_new ("\\r\\n_OUWCTI:\\s*(\\d+)\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); self->priv->_osigq_regex = g_regex_new ("\\r\\n_OSIGQ:\\s*(\\d+),(\\d)\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); self->priv->ignore_regex = g_regex_new ("\\r\\n\\+PACSP0\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); } static void shared_option_init (MMSharedOption *iface) { } static void iface_modem_init (MMIfaceModem *iface) { iface_modem_parent = g_type_interface_peek_parent (iface); iface->create_sim = mm_shared_option_create_sim; iface->create_sim_finish = mm_shared_option_create_sim_finish; iface->modem_after_power_up = modem_after_power_up; iface->modem_after_power_up_finish = modem_after_power_up_finish; iface->load_access_technologies = load_access_technologies; iface->load_access_technologies_finish = load_access_technologies_finish; iface->load_supported_modes = load_supported_modes; iface->load_supported_modes_finish = load_supported_modes_finish; iface->load_current_modes = load_current_modes; iface->load_current_modes_finish = load_current_modes_finish; iface->set_current_modes = set_current_modes; iface->set_current_modes_finish = set_current_modes_finish; } static void iface_modem_3gpp_init (MMIfaceModem3gpp *iface) { iface_modem_3gpp_parent = g_type_interface_peek_parent (iface); iface->load_imei = modem_3gpp_load_imei; iface->load_imei_finish = modem_3gpp_load_imei_finish; iface->setup_unsolicited_events = modem_3gpp_setup_unsolicited_events; iface->setup_unsolicited_events_finish = modem_3gpp_setup_cleanup_unsolicited_events_finish; iface->cleanup_unsolicited_events = modem_3gpp_cleanup_unsolicited_events; iface->cleanup_unsolicited_events_finish = modem_3gpp_setup_cleanup_unsolicited_events_finish; iface->enable_unsolicited_events = modem_3gpp_enable_unsolicited_events; iface->enable_unsolicited_events_finish = modem_3gpp_enable_unsolicited_events_finish; iface->disable_unsolicited_events = modem_3gpp_disable_unsolicited_events; iface->disable_unsolicited_events_finish = modem_3gpp_disable_unsolicited_events_finish; } static void mm_broadband_modem_option_class_init (MMBroadbandModemOptionClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); MMBroadbandModemClass *broadband_modem_class = MM_BROADBAND_MODEM_CLASS (klass); g_type_class_add_private (object_class, sizeof (MMBroadbandModemOptionPrivate)); object_class->finalize = finalize; broadband_modem_class->setup_ports = setup_ports; } ModemManager-1.23.4-dev/src/plugins/option/mm-broadband-modem-option.h000066400000000000000000000047741456466623000256220ustar00rootroot00000000000000/* -*- 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 - 2012 Red Hat, Inc. * Copyright (C) 2012 Aleksander Morgado */ #ifndef MM_BROADBAND_MODEM_OPTION_H #define MM_BROADBAND_MODEM_OPTION_H #include "mm-broadband-modem.h" #define MM_TYPE_BROADBAND_MODEM_OPTION (mm_broadband_modem_option_get_type ()) #define MM_BROADBAND_MODEM_OPTION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_BROADBAND_MODEM_OPTION, MMBroadbandModemOption)) #define MM_BROADBAND_MODEM_OPTION_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_BROADBAND_MODEM_OPTION, MMBroadbandModemOptionClass)) #define MM_IS_BROADBAND_MODEM_OPTION(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_BROADBAND_MODEM_OPTION)) #define MM_IS_BROADBAND_MODEM_OPTION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_BROADBAND_MODEM_OPTION)) #define MM_BROADBAND_MODEM_OPTION_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_BROADBAND_MODEM_OPTION, MMBroadbandModemOptionClass)) typedef struct _MMBroadbandModemOption MMBroadbandModemOption; typedef struct _MMBroadbandModemOptionClass MMBroadbandModemOptionClass; typedef struct _MMBroadbandModemOptionPrivate MMBroadbandModemOptionPrivate; struct _MMBroadbandModemOption { MMBroadbandModem parent; MMBroadbandModemOptionPrivate *priv; }; struct _MMBroadbandModemOptionClass{ MMBroadbandModemClass parent; }; GType mm_broadband_modem_option_get_type (void); MMBroadbandModemOption *mm_broadband_modem_option_new (const gchar *device, const gchar *physdev, const gchar **drivers, const gchar *plugin, guint16 vendor_id, guint16 product_id); #endif /* MM_BROADBAND_MODEM_OPTION_H */ ModemManager-1.23.4-dev/src/plugins/option/mm-plugin-hso.c000066400000000000000000000171761456466623000233610ustar00rootroot00000000000000/* -*- 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 hso) 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 - 2012 Red Hat, Inc. * Copyright (C) 2012 Aleksander Morgado */ #include #include #define _LIBMM_INSIDE_MM #include #include "mm-private-boxed-types.h" #include "mm-plugin-common.h" #include "mm-broadband-modem-hso.h" #include "mm-log-object.h" #define MM_TYPE_PLUGIN_HSO mm_plugin_hso_get_type () MM_DEFINE_PLUGIN (HSO, hso, Hso) /*****************************************************************************/ /* Custom init */ #define TAG_HSO_AT_CONTROL "hso-at-control" #define TAG_HSO_AT_APP "hso-at-app" #define TAG_HSO_AT_MODEM "hso-at-modem" #define TAG_HSO_AT_GPS_CONTROL "hso-at-gps-control" #define TAG_HSO_GPS "hso-gps" #define TAG_HSO_DIAG "hso-diag" static gboolean hso_custom_init_finish (MMPortProbe *probe, GAsyncResult *result, GError **error) { return g_task_propagate_boolean (G_TASK (result), error); } static void hso_custom_init (MMPortProbe *probe, MMPortSerialAt *port, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { MMKernelDevice *kernel_port; GTask *task; const gchar *subsys, *sysfs_path; subsys = mm_port_probe_get_port_subsys (probe); kernel_port = mm_port_probe_peek_port (probe); sysfs_path = mm_kernel_device_get_sysfs_path (kernel_port); if (g_str_equal (subsys, "tty")) { gchar *hsotype_path; gchar *contents = NULL; hsotype_path = g_build_filename (sysfs_path, "hsotype", NULL); if (g_file_get_contents (hsotype_path, &contents, NULL, NULL)) { mm_obj_dbg (probe, "HSO port type %s: %s", hsotype_path, contents); if (g_str_has_prefix (contents, "Control")) { g_object_set_data (G_OBJECT (probe), TAG_HSO_AT_CONTROL, GUINT_TO_POINTER (TRUE)); mm_port_probe_set_result_at (probe, TRUE); } else if (g_str_has_prefix (contents, "Application")) { g_object_set_data (G_OBJECT (probe), TAG_HSO_AT_APP, GUINT_TO_POINTER (TRUE)); mm_port_probe_set_result_at (probe, TRUE); } else if (g_str_has_prefix (contents, "Modem")) { g_object_set_data (G_OBJECT (probe), TAG_HSO_AT_MODEM, GUINT_TO_POINTER (TRUE)); mm_port_probe_set_result_at (probe, TRUE); } else if (g_str_has_prefix (contents, "GPS Control")) { g_object_set_data (G_OBJECT (probe), TAG_HSO_AT_GPS_CONTROL, GUINT_TO_POINTER (TRUE)); mm_port_probe_set_result_at (probe, TRUE); } else if (g_str_has_prefix (contents, "GPS")) { /* Not an AT port, but the port to grab GPS traces */ g_object_set_data (G_OBJECT (probe), TAG_HSO_GPS, GUINT_TO_POINTER (TRUE)); mm_port_probe_set_result_at (probe, FALSE); mm_port_probe_set_result_qcdm (probe, FALSE); } else if (g_str_has_prefix (contents, "Diag")) { g_object_set_data (G_OBJECT (probe), TAG_HSO_DIAG, GUINT_TO_POINTER (TRUE)); mm_port_probe_set_result_at (probe, FALSE); /* Don't automatically tag as QCDM, as the 'hso' driver reports * a DIAG port for some Icera-based modems, which don't have * QCDM ports since they aren't made by Qualcomm. */ } g_free (contents); } g_free (hsotype_path); } task = g_task_new (probe, NULL, callback, user_data); g_task_return_boolean (task, TRUE); g_object_unref (task); } /*****************************************************************************/ static MMBaseModem * create_modem (MMPlugin *self, const gchar *uid, const gchar *physdev, const gchar **drivers, guint16 vendor, guint16 product, guint16 subsystem_vendor, GList *probes, GError **error) { return MM_BASE_MODEM (mm_broadband_modem_hso_new (uid, physdev, drivers, mm_plugin_get_name (self), vendor, product)); } static gboolean grab_port (MMPlugin *self, MMBaseModem *modem, MMPortProbe *probe, GError **error) { const gchar *subsys; MMPortSerialAtFlag pflags = MM_PORT_SERIAL_AT_FLAG_NONE; MMPortType port_type; subsys = mm_port_probe_get_port_subsys (probe); port_type = mm_port_probe_get_port_type (probe); /* Detect AT port types */ if (g_str_equal (subsys, "tty")) { if (g_object_get_data (G_OBJECT (probe), TAG_HSO_AT_CONTROL)) pflags = MM_PORT_SERIAL_AT_FLAG_PRIMARY; else if (g_object_get_data (G_OBJECT (probe), TAG_HSO_AT_APP)) pflags = MM_PORT_SERIAL_AT_FLAG_SECONDARY; else if (g_object_get_data (G_OBJECT (probe), TAG_HSO_AT_GPS_CONTROL)) pflags = MM_PORT_SERIAL_AT_FLAG_GPS_CONTROL; else if (g_object_get_data (G_OBJECT (probe), TAG_HSO_AT_MODEM)) pflags = MM_PORT_SERIAL_AT_FLAG_PPP; else if (g_object_get_data (G_OBJECT (probe), TAG_HSO_GPS)) { /* Not an AT port, but the port to grab GPS traces */ g_assert (port_type == MM_PORT_TYPE_UNKNOWN); port_type = MM_PORT_TYPE_GPS; } } return mm_base_modem_grab_port (modem, mm_port_probe_peek_port (probe), port_type, pflags, error); } /*****************************************************************************/ MM_PLUGIN_NAMED_CREATOR_SCOPE MMPlugin * mm_plugin_create_hso (void) { static const gchar *subsystems[] = { "tty", "net", NULL }; static const gchar *drivers[] = { "hso", NULL }; static const MMAsyncMethod custom_init = { .async = G_CALLBACK (hso_custom_init), .finish = G_CALLBACK (hso_custom_init_finish), }; return MM_PLUGIN ( g_object_new (MM_TYPE_PLUGIN_HSO, MM_PLUGIN_NAME, MM_MODULE_NAME, MM_PLUGIN_ALLOWED_SUBSYSTEMS, subsystems, MM_PLUGIN_ALLOWED_DRIVERS, drivers, MM_PLUGIN_ALLOWED_AT, TRUE, MM_PLUGIN_ALLOWED_QCDM, TRUE, MM_PLUGIN_CUSTOM_INIT, &custom_init, MM_PLUGIN_SEND_DELAY, (guint64) 0, NULL)); } static void mm_plugin_hso_init (MMPluginHso *self) { } static void mm_plugin_hso_class_init (MMPluginHsoClass *klass) { MMPluginClass *plugin_class = MM_PLUGIN_CLASS (klass); plugin_class->create_modem = create_modem; plugin_class->grab_port = grab_port; } ModemManager-1.23.4-dev/src/plugins/option/mm-plugin-option.c000066400000000000000000000102721456466623000240660ustar00rootroot00000000000000/* -*- 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 - 2012 Red Hat, Inc. * Copyright (C) 2012 Aleksander Morgado */ #include #include #define _LIBMM_INSIDE_MM #include #include "mm-private-boxed-types.h" #include "mm-plugin-common.h" #include "mm-broadband-modem-option.h" #define MM_TYPE_PLUGIN_OPTION mm_plugin_option_get_type () MM_DEFINE_PLUGIN (OPTION, option, Option) /*****************************************************************************/ static MMBaseModem * create_modem (MMPlugin *self, const gchar *uid, const gchar *physdev, const gchar **drivers, guint16 vendor, guint16 product, guint16 subsystem_vendor, GList *probes, GError **error) { return MM_BASE_MODEM (mm_broadband_modem_option_new (uid, physdev, drivers, mm_plugin_get_name (self), vendor, product)); } static gboolean grab_port (MMPlugin *self, MMBaseModem *modem, MMPortProbe *probe, GError **error) { MMPortSerialAtFlag pflags = MM_PORT_SERIAL_AT_FLAG_NONE; MMKernelDevice *port; gint usbif; /* The Option plugin cannot do anything with non-AT ports */ if (!mm_port_probe_is_at (probe)) { g_set_error_literal (error, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "Ignoring non-AT port"); return FALSE; } port = mm_port_probe_peek_port (probe); /* 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 = mm_kernel_device_get_interface_number (port); if (usbif == 0) pflags = MM_PORT_SERIAL_AT_FLAG_PRIMARY | MM_PORT_SERIAL_AT_FLAG_PPP; return mm_base_modem_grab_port (modem, port, MM_PORT_TYPE_AT, /* we only allow AT ports here */ pflags, error); } /*****************************************************************************/ MM_PLUGIN_NAMED_CREATOR_SCOPE MMPlugin * mm_plugin_create_option (void) { static const gchar *subsystems[] = { "tty", NULL }; static const guint16 vendor_ids[] = { 0x0af0, /* Option USB devices */ 0x1931, /* Nozomi CardBus devices */ 0 }; static const gchar *drivers[] = { "option1", "option", "nozomi", NULL }; return MM_PLUGIN ( g_object_new (MM_TYPE_PLUGIN_OPTION, MM_PLUGIN_NAME, MM_MODULE_NAME, MM_PLUGIN_ALLOWED_SUBSYSTEMS, subsystems, MM_PLUGIN_ALLOWED_DRIVERS, drivers, MM_PLUGIN_ALLOWED_VENDOR_IDS, vendor_ids, MM_PLUGIN_ALLOWED_AT, TRUE, NULL)); } static void mm_plugin_option_init (MMPluginOption *self) { } static void mm_plugin_option_class_init (MMPluginOptionClass *klass) { MMPluginClass *plugin_class = MM_PLUGIN_CLASS (klass); plugin_class->create_modem = create_modem; plugin_class->grab_port = grab_port; } ModemManager-1.23.4-dev/src/plugins/option/mm-shared-option.c000066400000000000000000000044111456466623000240340ustar00rootroot00000000000000/* -*- 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) 2021 Aleksander Morgado */ #include #include #include #include #define _LIBMM_INSIDE_MM #include #include "mm-log-object.h" #include "mm-iface-modem.h" #include "mm-sim-option.h" #include "mm-shared-option.h" /*****************************************************************************/ /* Create SIM (Modem inteface) */ MMBaseSim * mm_shared_option_create_sim_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { return mm_sim_option_new_finish (res, error); } void mm_shared_option_create_sim (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data) { mm_sim_option_new (MM_BASE_MODEM (self), NULL, /* cancellable */ callback, user_data); } /*****************************************************************************/ static void shared_option_init (gpointer g_iface) { } GType mm_shared_option_get_type (void) { static GType shared_option_type = 0; if (!G_UNLIKELY (shared_option_type)) { static const GTypeInfo info = { sizeof (MMSharedOption), /* class_size */ shared_option_init, /* base_init */ NULL, /* base_finalize */ }; shared_option_type = g_type_register_static (G_TYPE_INTERFACE, "MMSharedOption", &info, 0); g_type_interface_add_prerequisite (shared_option_type, MM_TYPE_IFACE_MODEM); } return shared_option_type; } ModemManager-1.23.4-dev/src/plugins/option/mm-shared-option.h000066400000000000000000000037041456466623000240450ustar00rootroot00000000000000/* -*- 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) 2021 Aleksander Morgado */ #ifndef MM_SHARED_OPTION_H #define MM_SHARED_OPTION_H #include #include #define _LIBMM_INSIDE_MM #include #include "mm-broadband-modem.h" #include "mm-iface-modem.h" #include "mm-iface-modem-location.h" #define MM_TYPE_SHARED_OPTION (mm_shared_option_get_type ()) #define MM_SHARED_OPTION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_SHARED_OPTION, MMSharedOption)) #define MM_IS_SHARED_OPTION(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_SHARED_OPTION)) #define MM_SHARED_OPTION_GET_INTERFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE ((obj), MM_TYPE_SHARED_OPTION, MMSharedOption)) typedef struct _MMSharedOption MMSharedOption; struct _MMSharedOption { GTypeInterface g_iface; }; GType mm_shared_option_get_type (void); void mm_shared_option_create_sim (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data); MMBaseSim *mm_shared_option_create_sim_finish (MMIfaceModem *self, GAsyncResult *res, GError **error); #endif /* MM_SHARED_OPTION_H */ ModemManager-1.23.4-dev/src/plugins/option/mm-shared.c000066400000000000000000000013131456466623000225240ustar00rootroot00000000000000/* -*- 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) 2019 Aleksander Morgado */ #include "mm-shared-common.h" MM_DEFINE_SHARED (option) ModemManager-1.23.4-dev/src/plugins/option/mm-sim-option.c000066400000000000000000000050311456466623000233550ustar00rootroot00000000000000/* -*- 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) 2021 Aleksander Morgado */ #include #include #include #include #include #include #include #define _LIBMM_INSIDE_MM #include #include "mm-sim-option.h" G_DEFINE_TYPE (MMSimOption, mm_sim_option, MM_TYPE_BASE_SIM) /*****************************************************************************/ MMBaseSim * mm_sim_option_new_finish (GAsyncResult *res, GError **error) { GObject *source; GObject *sim; source = g_async_result_get_source_object (res); sim = g_async_initable_new_finish (G_ASYNC_INITABLE (source), res, error); g_object_unref (source); if (!sim) return NULL; /* Only export valid SIMs */ mm_base_sim_export (MM_BASE_SIM (sim)); return MM_BASE_SIM (sim); } void mm_sim_option_new (MMBaseModem *modem, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { g_async_initable_new_async (MM_TYPE_SIM_OPTION, G_PRIORITY_DEFAULT, cancellable, callback, user_data, MM_BASE_SIM_MODEM, modem, "active", TRUE, /* by default always active */ NULL); } static void mm_sim_option_init (MMSimOption *self) { } static void mm_sim_option_class_init (MMSimOptionClass *klass) { MMBaseSimClass *base_sim_class = MM_BASE_SIM_CLASS (klass); /* Skip managing preferred networks, not supported by Option modems */ base_sim_class->load_preferred_networks = NULL; base_sim_class->load_preferred_networks_finish = NULL; base_sim_class->set_preferred_networks = NULL; base_sim_class->set_preferred_networks_finish = NULL; } ModemManager-1.23.4-dev/src/plugins/option/mm-sim-option.h000066400000000000000000000037161456466623000233720ustar00rootroot00000000000000/* -*- 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) 2021 Aleksander Morgado */ #ifndef MM_SIM_OPTION_H #define MM_SIM_OPTION_H #include #include #include "mm-base-sim.h" #define MM_TYPE_SIM_OPTION (mm_sim_option_get_type ()) #define MM_SIM_OPTION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_SIM_OPTION, MMSimOption)) #define MM_SIM_OPTION_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_SIM_OPTION, MMSimOptionClass)) #define MM_IS_SIM_OPTION(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_SIM_OPTION)) #define MM_IS_SIM_OPTION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_SIM_OPTION)) #define MM_SIM_OPTION_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_SIM_OPTION, MMSimOptionClass)) typedef struct _MMSimOption MMSimOption; typedef struct _MMSimOptionClass MMSimOptionClass; struct _MMSimOption { MMBaseSim parent; }; struct _MMSimOptionClass { MMBaseSimClass parent; }; GType mm_sim_option_get_type (void); void mm_sim_option_new (MMBaseModem *modem, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); MMBaseSim *mm_sim_option_new_finish (GAsyncResult *res, GError **error); #endif /* MM_SIM_OPTION_H */ ModemManager-1.23.4-dev/src/plugins/pantech/000077500000000000000000000000001456466623000206175ustar00rootroot00000000000000ModemManager-1.23.4-dev/src/plugins/pantech/mm-broadband-modem-pantech.c000066400000000000000000000140051456466623000260250ustar00rootroot00000000000000/* -*- 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) 2012 Aleksander Morgado */ #include #include #include #include #include #include #include "ModemManager.h" #include "mm-iface-modem.h" #include "mm-iface-modem-messaging.h" #include "mm-errors-types.h" #include "mm-broadband-modem-pantech.h" #include "mm-sim-pantech.h" static void iface_modem_init (MMIfaceModem *iface); static void iface_modem_messaging_init (MMIfaceModemMessaging *iface); static MMIfaceModemMessaging *iface_modem_messaging_parent; G_DEFINE_TYPE_EXTENDED (MMBroadbandModemPantech, mm_broadband_modem_pantech, MM_TYPE_BROADBAND_MODEM, 0, G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM, iface_modem_init) G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_MESSAGING, iface_modem_messaging_init)) /*****************************************************************************/ /* Load supported SMS storages (Messaging interface) */ static void skip_sm_sr_storage (GArray *mem) { guint i = mem->len; if (!mem) return; /* Remove SM and SR from the list of supported storages */ while (i-- > 0) { if (g_array_index (mem, MMSmsStorage, i) == MM_SMS_STORAGE_SR || g_array_index (mem, MMSmsStorage, i) == MM_SMS_STORAGE_SM) g_array_remove_index (mem, i); } } static gboolean load_supported_storages_finish (MMIfaceModemMessaging *self, GAsyncResult *res, GArray **mem1, GArray **mem2, GArray **mem3, GError **error) { if (!iface_modem_messaging_parent->load_supported_storages_finish (self, res, mem1, mem2, mem3, error)) return FALSE; skip_sm_sr_storage (*mem1); skip_sm_sr_storage (*mem2); skip_sm_sr_storage (*mem3); return TRUE; } static void load_supported_storages (MMIfaceModemMessaging *self, GAsyncReadyCallback callback, gpointer user_data) { /* Chain up parent's loading */ iface_modem_messaging_parent->load_supported_storages (self, callback, user_data); } /*****************************************************************************/ /* Create SIM (Modem interface) */ static MMBaseSim * create_sim_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { return mm_sim_pantech_new_finish (res, error); } static void create_sim (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data) { /* New Pantech SIM */ mm_sim_pantech_new (MM_BASE_MODEM (self), NULL, /* cancellable */ callback, user_data); } /*****************************************************************************/ /* After SIM unlock (Modem interface) */ static gboolean modem_after_sim_unlock_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static gboolean after_sim_unlock_wait_cb (GTask *task) { g_task_return_boolean (task, TRUE); g_object_unref (task); return G_SOURCE_REMOVE; } static void modem_after_sim_unlock (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data) { /* wait so sim pin is done */ g_timeout_add_seconds (5, (GSourceFunc)after_sim_unlock_wait_cb, g_task_new (self, NULL, callback, user_data)); } /*****************************************************************************/ MMBroadbandModemPantech * mm_broadband_modem_pantech_new (const gchar *device, const gchar *physdev, const gchar **drivers, const gchar *plugin, guint16 vendor_id, guint16 product_id) { return g_object_new (MM_TYPE_BROADBAND_MODEM_PANTECH, MM_BASE_MODEM_DEVICE, device, MM_BASE_MODEM_PHYSDEV, physdev, MM_BASE_MODEM_DRIVERS, drivers, MM_BASE_MODEM_PLUGIN, plugin, MM_BASE_MODEM_VENDOR_ID, vendor_id, MM_BASE_MODEM_PRODUCT_ID, product_id, /* Generic bearer supports TTY only */ MM_BASE_MODEM_DATA_NET_SUPPORTED, FALSE, MM_BASE_MODEM_DATA_TTY_SUPPORTED, TRUE, NULL); } static void mm_broadband_modem_pantech_init (MMBroadbandModemPantech *self) { } static void iface_modem_init (MMIfaceModem *iface) { /* Create Pantech-specific SIM */ iface->create_sim = create_sim; iface->create_sim_finish = create_sim_finish; iface->modem_after_sim_unlock = modem_after_sim_unlock; iface->modem_after_sim_unlock_finish = modem_after_sim_unlock_finish; } static void iface_modem_messaging_init (MMIfaceModemMessaging *iface) { iface_modem_messaging_parent = g_type_interface_peek_parent (iface); iface->load_supported_storages = load_supported_storages; iface->load_supported_storages_finish = load_supported_storages_finish; } static void mm_broadband_modem_pantech_class_init (MMBroadbandModemPantechClass *klass) { } ModemManager-1.23.4-dev/src/plugins/pantech/mm-broadband-modem-pantech.h000066400000000000000000000045261456466623000260410ustar00rootroot00000000000000/* -*- 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) 2012 Aleksander Morgado */ #ifndef MM_BROADBAND_MODEM_PANTECH_H #define MM_BROADBAND_MODEM_PANTECH_H #include "mm-broadband-modem.h" #define MM_TYPE_BROADBAND_MODEM_PANTECH (mm_broadband_modem_pantech_get_type ()) #define MM_BROADBAND_MODEM_PANTECH(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_BROADBAND_MODEM_PANTECH, MMBroadbandModemPantech)) #define MM_BROADBAND_MODEM_PANTECH_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_BROADBAND_MODEM_PANTECH, MMBroadbandModemPantechClass)) #define MM_IS_BROADBAND_MODEM_PANTECH(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_BROADBAND_MODEM_PANTECH)) #define MM_IS_BROADBAND_MODEM_PANTECH_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_BROADBAND_MODEM_PANTECH)) #define MM_BROADBAND_MODEM_PANTECH_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_BROADBAND_MODEM_PANTECH, MMBroadbandModemPantechClass)) typedef struct _MMBroadbandModemPantech MMBroadbandModemPantech; typedef struct _MMBroadbandModemPantechClass MMBroadbandModemPantechClass; struct _MMBroadbandModemPantech { MMBroadbandModem parent; }; struct _MMBroadbandModemPantechClass{ MMBroadbandModemClass parent; }; GType mm_broadband_modem_pantech_get_type (void); MMBroadbandModemPantech *mm_broadband_modem_pantech_new (const gchar *device, const gchar *physdev, const gchar **drivers, const gchar *plugin, guint16 vendor_id, guint16 product_id); #endif /* MM_BROADBAND_MODEM_PANTECH_H */ ModemManager-1.23.4-dev/src/plugins/pantech/mm-plugin-pantech.c000066400000000000000000000132461456466623000243160ustar00rootroot00000000000000/* -*- 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) 2012 Aleksander Morgado */ #include #include #define _LIBMM_INSIDE_MM #include #include "mm-log-object.h" #include "mm-plugin-common.h" #include "mm-broadband-modem-pantech.h" #if defined WITH_QMI #include "mm-broadband-modem-qmi.h" #endif #define MM_TYPE_PLUGIN_PANTECH mm_plugin_pantech_get_type () MM_DEFINE_PLUGIN (PANTECH, pantech, Pantech) /*****************************************************************************/ /* Custom commands for AT probing * There's currently no WMC probing plugged in the logic, so We need to detect * WMC ports ourselves somehow. Just assume that the WMC port will reply "ERROR" * to the "ATE0" command. */ static gboolean port_probe_response_processor_is_pantech_at (const gchar *command, const gchar *response, gboolean last_command, const GError *error, GVariant **result, GError **result_error) { if (error) { /* Timeout errors are the only ones not fatal; * they will just go on to the next command. */ if (g_error_matches (error, MM_SERIAL_ERROR, MM_SERIAL_ERROR_RESPONSE_TIMEOUT)) { return FALSE; } /* All other errors indicate NOT an AT port */ *result = g_variant_new_boolean (FALSE); return TRUE; } /* No error reported, valid AT port! */ *result = g_variant_new_boolean (TRUE); return TRUE; } static const MMPortProbeAtCommand custom_at_probe[] = { { "ATE0", 3, port_probe_response_processor_is_pantech_at }, { "ATE0", 3, port_probe_response_processor_is_pantech_at }, { "ATE0", 3, port_probe_response_processor_is_pantech_at }, { NULL } }; /*****************************************************************************/ static MMBaseModem * create_modem (MMPlugin *self, const gchar *uid, const gchar *physdev, const gchar **drivers, guint16 vendor, guint16 product, guint16 subsystem_vendor, GList *probes, GError **error) { #if defined WITH_QMI if (mm_port_probe_list_has_qmi_port (probes)) { mm_obj_dbg (self, "QMI-powered Pantech modem found..."); return MM_BASE_MODEM (mm_broadband_modem_qmi_new (uid, physdev, drivers, mm_plugin_get_name (self), vendor, product)); } #endif return MM_BASE_MODEM (mm_broadband_modem_pantech_new (uid, physdev, drivers, mm_plugin_get_name (self), vendor, product)); } static gboolean grab_port (MMPlugin *self, MMBaseModem *modem, MMPortProbe *probe, GError **error) { MMPortType ptype; MMPortSerialAtFlag pflags = MM_PORT_SERIAL_AT_FLAG_NONE; ptype = mm_port_probe_get_port_type (probe); /* Always prefer the ttyACM port as PRIMARY AT port */ if (ptype == MM_PORT_TYPE_AT && g_str_has_prefix (mm_port_probe_get_port_name (probe), "ttyACM")) { pflags = MM_PORT_SERIAL_AT_FLAG_PRIMARY; } return mm_base_modem_grab_port (modem, mm_port_probe_peek_port (probe), ptype, pflags, error); } /*****************************************************************************/ MM_PLUGIN_NAMED_CREATOR_SCOPE MMPlugin * mm_plugin_create_pantech (void) { static const gchar *subsystems[] = { "tty", "net", "usbmisc", NULL }; static const guint16 vendor_ids[] = { 0x106c, 0 }; return MM_PLUGIN ( g_object_new (MM_TYPE_PLUGIN_PANTECH, MM_PLUGIN_NAME, MM_MODULE_NAME, MM_PLUGIN_ALLOWED_SUBSYSTEMS, subsystems, MM_PLUGIN_ALLOWED_VENDOR_IDS, vendor_ids, MM_PLUGIN_ALLOWED_AT, TRUE, MM_PLUGIN_ALLOWED_QCDM, TRUE, MM_PLUGIN_ALLOWED_QMI, TRUE, MM_PLUGIN_CUSTOM_AT_PROBE, custom_at_probe, NULL)); } static void mm_plugin_pantech_init (MMPluginPantech *self) { } static void mm_plugin_pantech_class_init (MMPluginPantechClass *klass) { MMPluginClass *plugin_class = MM_PLUGIN_CLASS (klass); plugin_class->create_modem = create_modem; plugin_class->grab_port = grab_port; } ModemManager-1.23.4-dev/src/plugins/pantech/mm-sim-pantech.c000066400000000000000000000052251456466623000236060ustar00rootroot00000000000000/* -*- 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) 2012 Aleksander Morgado */ #include #include #include #include #include #include #include #define _LIBMM_INSIDE_MM #include #include "mm-sim-pantech.h" G_DEFINE_TYPE (MMSimPantech, mm_sim_pantech, MM_TYPE_BASE_SIM) /*****************************************************************************/ MMBaseSim * mm_sim_pantech_new_finish (GAsyncResult *res, GError **error) { GObject *source; GObject *sim; source = g_async_result_get_source_object (res); sim = g_async_initable_new_finish (G_ASYNC_INITABLE (source), res, error); g_object_unref (source); if (!sim) return NULL; /* Only export valid SIMs */ mm_base_sim_export (MM_BASE_SIM (sim)); return MM_BASE_SIM (sim); } void mm_sim_pantech_new (MMBaseModem *modem, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { g_async_initable_new_async (MM_TYPE_SIM_PANTECH, G_PRIORITY_DEFAULT, cancellable, callback, user_data, MM_BASE_SIM_MODEM, modem, "active", TRUE, /* by default always active */ NULL); } static void mm_sim_pantech_init (MMSimPantech *self) { } static void mm_sim_pantech_class_init (MMSimPantechClass *klass) { MMBaseSimClass *base_sim_class = MM_BASE_SIM_CLASS (klass); /* Skip querying most SIM card info, +CRSM just shoots the Pantech modems * (at least the UMW190) in the head */ base_sim_class->load_sim_identifier = NULL; base_sim_class->load_sim_identifier_finish = NULL; base_sim_class->load_operator_identifier = NULL; base_sim_class->load_operator_identifier_finish = NULL; base_sim_class->load_operator_name = NULL; base_sim_class->load_operator_name_finish = NULL; } ModemManager-1.23.4-dev/src/plugins/pantech/mm-sim-pantech.h000066400000000000000000000037471456466623000236220ustar00rootroot00000000000000/* -*- 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) 2012 Aleksander Morgado */ #ifndef MM_SIM_PANTECH_H #define MM_SIM_PANTECH_H #include #include #include "mm-base-sim.h" #define MM_TYPE_SIM_PANTECH (mm_sim_pantech_get_type ()) #define MM_SIM_PANTECH(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_SIM_PANTECH, MMSimPantech)) #define MM_SIM_PANTECH_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_SIM_PANTECH, MMSimPantechClass)) #define MM_IS_SIM_PANTECH(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_SIM_PANTECH)) #define MM_IS_SIM_PANTECH_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_SIM_PANTECH)) #define MM_SIM_PANTECH_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_SIM_PANTECH, MMSimPantechClass)) typedef struct _MMSimPantech MMSimPantech; typedef struct _MMSimPantechClass MMSimPantechClass; struct _MMSimPantech { MMBaseSim parent; }; struct _MMSimPantechClass { MMBaseSimClass parent; }; GType mm_sim_pantech_get_type (void); void mm_sim_pantech_new (MMBaseModem *modem, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); MMBaseSim *mm_sim_pantech_new_finish (GAsyncResult *res, GError **error); #endif /* MM_SIM_PANTECH_H */ ModemManager-1.23.4-dev/src/plugins/qcom-soc/000077500000000000000000000000001456466623000207165ustar00rootroot00000000000000ModemManager-1.23.4-dev/src/plugins/qcom-soc/77-mm-qcom-soc.rules000066400000000000000000000031231456466623000243540ustar00rootroot00000000000000# do not edit this file, it will be overwritten on update ACTION!="add|change|move|bind", GOTO="mm_qcom_soc_end" # Process only known wwan, net and rpmsg ports SUBSYSTEM=="net", DRIVERS=="bam-dmux", GOTO="mm_qcom_soc_process" SUBSYSTEM=="net", DRIVERS=="ipa", GOTO="mm_qcom_soc_process" SUBSYSTEM=="wwan", DRIVERS=="qcom-q6v5-mss", GOTO="mm_qcom_soc_process" SUBSYSTEM=="rpmsg", DRIVERS=="qcom-q6v5-mss", GOTO="mm_qcom_soc_process" GOTO="mm_qcom_soc_end" LABEL="mm_qcom_soc_process" # Flag the port as being part of the SoC ENV{ID_MM_QCOM_SOC}="1" # # Add a common physdev UID to all ports in the Qualcomm SoC, so that they # are all bound together to the same modem object. # # The MSM8916, MSM8974, .... Qualcomm SoCs use the combination of RPMSG/WWAN # based control ports plus BAM-DMUX based network ports. # ENV{ID_MM_PHYSDEV_UID}="qcom-soc" # port type hints for the rpmsgexport-ed ports SUBSYSTEM=="rpmsg", ATTR{name}=="DATA*", ATTR{name}=="*_CNTL", ENV{ID_MM_PORT_TYPE_QMI}="1" SUBSYSTEM=="rpmsg", ATTR{name}=="DATA*", ATTR{name}!="*_CNTL", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1" # ignore every other port without explicit hints SUBSYSTEM=="rpmsg", ENV{ID_MM_PORT_TYPE_QMI}!="1", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}!="1", ENV{ID_MM_PORT_IGNORE}="1" # explicitly ignore ports intended for USB tethering (DATA40, DATA40_CNTL) SUBSYSTEM=="rpmsg", ATTR{name}=="DATA40*", ENV{ID_MM_PORT_IGNORE}="1" KERNEL=="rmnet_usb*", ENV{ID_MM_PORT_IGNORE}="1" # flag all rpmsg ports under this plugin as candidate KERNEL=="rpmsg*", SUBSYSTEM=="rpmsg", ENV{ID_MM_CANDIDATE}="1" LABEL="mm_qcom_soc_end" ModemManager-1.23.4-dev/src/plugins/qcom-soc/mm-broadband-modem-qmi-qcom-soc.c000066400000000000000000000147461456466623000270230ustar00rootroot00000000000000/* -*- 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) 2020 Aleksander Morgado */ #include #include #include #include #include #include #include "ModemManager.h" #include "mm-log.h" #include "mm-iface-modem.h" #include "mm-broadband-modem-qmi-qcom-soc.h" G_DEFINE_TYPE (MMBroadbandModemQmiQcomSoc, mm_broadband_modem_qmi_qcom_soc, MM_TYPE_BROADBAND_MODEM_QMI) /*****************************************************************************/ static const QmiSioPort sio_port_per_port_number[] = { QMI_SIO_PORT_A2_MUX_RMNET0, QMI_SIO_PORT_A2_MUX_RMNET1, QMI_SIO_PORT_A2_MUX_RMNET2, QMI_SIO_PORT_A2_MUX_RMNET3, QMI_SIO_PORT_A2_MUX_RMNET4, QMI_SIO_PORT_A2_MUX_RMNET5, QMI_SIO_PORT_A2_MUX_RMNET6, QMI_SIO_PORT_A2_MUX_RMNET7 }; static MMPortQmi * peek_port_qmi_for_data_bam_dmux (MMBroadbandModemQmi *self, MMPort *data, MMQmiDataEndpoint *out_endpoint, GError **error) { MMPortQmi *found = NULL; MMKernelDevice *net_port; gint net_port_number; net_port = mm_port_peek_kernel_device (data); /* The dev_port notified by the bam-dmux driver indicates which SIO port we should be using */ net_port_number = mm_kernel_device_get_attribute_as_int (net_port, "dev_port"); if (net_port_number < 0 || net_port_number >= (gint) G_N_ELEMENTS (sio_port_per_port_number)) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_NOT_FOUND, "Couldn't find SIO port number for 'net/%s'", mm_port_get_device (data)); return NULL; } /* Find one QMI port, we don't care which one */ found = mm_broadband_modem_qmi_peek_port_qmi (self); if (!found) g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_NOT_FOUND, "Couldn't find any QMI port for 'net/%s'", mm_port_get_device (data)); else if (out_endpoint) { /* WDS Bind (Mux) Data Port must be called with the correct endpoint * interface number/SIO port to make multiplexing work with BAM-DMUX */ out_endpoint->type = QMI_DATA_ENDPOINT_TYPE_BAM_DMUX; out_endpoint->interface_number = net_port_number; out_endpoint->sio_port = sio_port_per_port_number[net_port_number]; } return found; } static MMPortQmi * peek_port_qmi_for_data_ipa (MMBroadbandModemQmi *self, MMPort *data, MMQmiDataEndpoint *out_endpoint, GError **error) { MMPortQmi *found = NULL; /* when using IPA, we have a main network interface that will be multiplexed * to create link interfaces. We can assume any of the available QMI ports is * able to manage that. */ found = mm_broadband_modem_qmi_peek_port_qmi (self); if (!found) g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_NOT_FOUND, "Couldn't find any QMI port for 'net/%s'", mm_port_get_device (data)); else if (out_endpoint) mm_port_qmi_get_endpoint_info (found, out_endpoint); return found; } static MMPortQmi * peek_port_qmi_for_data (MMBroadbandModemQmi *self, MMPort *data, MMQmiDataEndpoint *out_endpoint, GError **error) { MMKernelDevice *net_port; const gchar *net_port_driver; g_assert (MM_IS_BROADBAND_MODEM_QMI (self)); g_assert (mm_port_get_subsys (data) == MM_PORT_SUBSYS_NET); net_port = mm_port_peek_kernel_device (data); net_port_driver = mm_kernel_device_get_driver (net_port); if (g_strcmp0 (net_port_driver, "ipa") == 0) return peek_port_qmi_for_data_ipa (self, data, out_endpoint, error); if (g_strcmp0 (net_port_driver, "bam-dmux") == 0) return peek_port_qmi_for_data_bam_dmux (self, data, out_endpoint, error); g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Unsupported QMI kernel driver for 'net/%s': %s", mm_port_get_device (data), net_port_driver); return NULL; } /*****************************************************************************/ MMBroadbandModemQmiQcomSoc * mm_broadband_modem_qmi_qcom_soc_new (const gchar *device, const gchar *physdev, const gchar **drivers, const gchar *plugin, guint16 vendor_id, guint16 product_id) { return g_object_new (MM_TYPE_BROADBAND_MODEM_QMI_QCOM_SOC, MM_BASE_MODEM_DEVICE, device, MM_BASE_MODEM_PHYSDEV, physdev, MM_BASE_MODEM_DRIVERS, drivers, MM_BASE_MODEM_PLUGIN, plugin, MM_BASE_MODEM_VENDOR_ID, vendor_id, MM_BASE_MODEM_PRODUCT_ID, product_id, /* QMI bearer supports NET only */ MM_BASE_MODEM_DATA_NET_SUPPORTED, TRUE, MM_BASE_MODEM_DATA_TTY_SUPPORTED, FALSE, MM_IFACE_MODEM_SIM_HOT_SWAP_SUPPORTED, TRUE, NULL); } static void mm_broadband_modem_qmi_qcom_soc_init (MMBroadbandModemQmiQcomSoc *self) { } static void mm_broadband_modem_qmi_qcom_soc_class_init (MMBroadbandModemQmiQcomSocClass *klass) { MMBroadbandModemQmiClass *broadband_modem_qmi_class = MM_BROADBAND_MODEM_QMI_CLASS (klass); broadband_modem_qmi_class->peek_port_qmi_for_data = peek_port_qmi_for_data; } ModemManager-1.23.4-dev/src/plugins/qcom-soc/mm-broadband-modem-qmi-qcom-soc.h000066400000000000000000000052221456466623000270150ustar00rootroot00000000000000/* -*- 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) 2020 Aleksander Morgado */ #ifndef MM_BROADBAND_MODEM_QMI_QCOM_SOC_H #define MM_BROADBAND_MODEM_QMI_QCOM_SOC_H #include "mm-broadband-modem-qmi.h" #define MM_TYPE_BROADBAND_MODEM_QMI_QCOM_SOC (mm_broadband_modem_qmi_qcom_soc_get_type ()) #define MM_BROADBAND_MODEM_QMI_QCOM_SOC(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_BROADBAND_MODEM_QMI_QCOM_SOC, MMBroadbandModemQmiQcomSoc)) #define MM_BROADBAND_MODEM_QMI_QCOM_SOC_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_BROADBAND_MODEM_QMI_QCOM_SOC, MMBroadbandModemQmiQcomSocClass)) #define MM_IS_BROADBAND_MODEM_QMI_QCOM_SOC(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_BROADBAND_MODEM_QMI_QCOM_SOC)) #define MM_IS_BROADBAND_MODEM_QMI_QCOM_SOC_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_BROADBAND_MODEM_QMI_QCOM_SOC)) #define MM_BROADBAND_MODEM_QMI_QCOM_SOC_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_BROADBAND_MODEM_QMI_QCOM_SOC, MMBroadbandModemQmiQcomSocClass)) typedef struct _MMBroadbandModemQmiQcomSoc MMBroadbandModemQmiQcomSoc; typedef struct _MMBroadbandModemQmiQcomSocClass MMBroadbandModemQmiQcomSocClass; typedef struct _MMBroadbandModemQmiQcomSocPrivate MMBroadbandModemQmiQcomSocPrivate; struct _MMBroadbandModemQmiQcomSoc { MMBroadbandModemQmi parent; MMBroadbandModemQmiQcomSocPrivate *priv; }; struct _MMBroadbandModemQmiQcomSocClass{ MMBroadbandModemQmiClass parent; }; GType mm_broadband_modem_qmi_qcom_soc_get_type (void); MMBroadbandModemQmiQcomSoc *mm_broadband_modem_qmi_qcom_soc_new (const gchar *device, const gchar *physdev, const gchar **drivers, const gchar *plugin, guint16 vendor_id, guint16 product_id); #endif /* MM_BROADBAND_MODEM_QMI_QCOM_SOC_H */ ModemManager-1.23.4-dev/src/plugins/qcom-soc/mm-plugin-qcom-soc.c000066400000000000000000000063071456466623000245140ustar00rootroot00000000000000/* -*- 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) 2020 Aleksander Morgado */ #include #include #include #include #include #include #include #include #include #include #define _LIBMM_INSIDE_MM #include #include "mm-plugin-common.h" #include "mm-broadband-modem-qmi-qcom-soc.h" #include "mm-log-object.h" #define MM_TYPE_PLUGIN_QCOM_SOC mm_plugin_qcom_soc_get_type () MM_DEFINE_PLUGIN (QCOM_SOC, qcom_soc, QcomSoc) /*****************************************************************************/ static MMBaseModem * create_modem (MMPlugin *self, const gchar *uid, const gchar *physdev, const gchar **drivers, guint16 vendor, guint16 product, guint16 subsystem_vendor, GList *probes, GError **error) { if (!mm_port_probe_list_has_qmi_port (probes)) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Unsupported device: at least a QMI port is required"); return NULL; } mm_obj_dbg (self, "Qualcomm SoC modem found..."); return MM_BASE_MODEM (mm_broadband_modem_qmi_qcom_soc_new (uid, physdev, drivers, mm_plugin_get_name (self), vendor, product)); } /*****************************************************************************/ MM_PLUGIN_NAMED_CREATOR_SCOPE MMPlugin * mm_plugin_create_qcom_soc (void) { static const gchar *subsystems[] = { "wwan", "rpmsg", "net", "qrtr", NULL }; static const gchar *udev_tags[] = { "ID_MM_QCOM_SOC", NULL }; return MM_PLUGIN ( g_object_new (MM_TYPE_PLUGIN_QCOM_SOC, MM_PLUGIN_NAME, MM_MODULE_NAME, MM_PLUGIN_ALLOWED_SUBSYSTEMS, subsystems, MM_PLUGIN_ALLOWED_AT, TRUE, MM_PLUGIN_ALLOWED_QMI, TRUE, MM_PLUGIN_ALLOWED_UDEV_TAGS, udev_tags, NULL)); } static void mm_plugin_qcom_soc_init (MMPluginQcomSoc *self) { } static void mm_plugin_qcom_soc_class_init (MMPluginQcomSocClass *klass) { MMPluginClass *plugin_class = MM_PLUGIN_CLASS (klass); plugin_class->create_modem = create_modem; } ModemManager-1.23.4-dev/src/plugins/quectel/000077500000000000000000000000001456466623000206375ustar00rootroot00000000000000ModemManager-1.23.4-dev/src/plugins/quectel/77-mm-quectel-port-types.rules000066400000000000000000000230151456466623000263440ustar00rootroot00000000000000# do not edit this file, it will be overwritten on update ACTION!="add|change|move|bind", GOTO="mm_quectel_end" SUBSYSTEMS=="usb", ATTRS{idVendor}=="2c7c", GOTO="mm_quectel_usb" SUBSYSTEMS=="pci", ATTRS{vendor}=="0x1eac", GOTO="mm_quectel_pci" GOTO="mm_quectel_end" LABEL="mm_quectel_usb" SUBSYSTEMS=="usb", ATTRS{bInterfaceNumber}=="?*", ENV{.MM_USBIFNUM}="$attr{bInterfaceNumber}" # Quectel EG06 # ttyUSB0 (if #0): QCDM/DIAG port # ttyUSB1 (if #1): GPS data port # ttyUSB2 (if #2): AT primary port # ttyUSB3 (if #3): AT secondary port ATTRS{idVendor}=="2c7c", ATTRS{idProduct}=="0306", ENV{.MM_USBIFNUM}=="00", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_QCDM}="1" ATTRS{idVendor}=="2c7c", ATTRS{idProduct}=="0306", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_GPS}="1" ATTRS{idVendor}=="2c7c", ATTRS{idProduct}=="0306", ENV{.MM_USBIFNUM}=="02", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1" ATTRS{idVendor}=="2c7c", ATTRS{idProduct}=="0306", ENV{.MM_USBIFNUM}=="03", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1" # Quectel EG91 # ttyUSB0 (if #0): QCDM/DIAG port # ttyUSB1 (if #1): GPS data port # ttyUSB2 (if #2): AT primary port # ttyUSB3 (if #3): AT secondary port ATTRS{idVendor}=="2c7c", ATTRS{idProduct}=="0191", ENV{.MM_USBIFNUM}=="00", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_QCDM}="1" ATTRS{idVendor}=="2c7c", ATTRS{idProduct}=="0191", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_GPS}="1" ATTRS{idVendor}=="2c7c", ATTRS{idProduct}=="0191", ENV{.MM_USBIFNUM}=="02", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1" ATTRS{idVendor}=="2c7c", ATTRS{idProduct}=="0191", ENV{.MM_USBIFNUM}=="03", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1" # Quectel EG95 # ttyUSB0 (if #0): QCDM/DIAG port # ttyUSB1 (if #1): GPS data port # ttyUSB2 (if #2): AT primary port # ttyUSB3 (if #3): AT secondary port ATTRS{idVendor}=="2c7c", ATTRS{idProduct}=="0195", ENV{.MM_USBIFNUM}=="00", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_QCDM}="1" ATTRS{idVendor}=="2c7c", ATTRS{idProduct}=="0195", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_GPS}="1" ATTRS{idVendor}=="2c7c", ATTRS{idProduct}=="0195", ENV{.MM_USBIFNUM}=="02", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1" ATTRS{idVendor}=="2c7c", ATTRS{idProduct}=="0195", ENV{.MM_USBIFNUM}=="03", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1" # Quectel BG96 # ttyUSB0 (if #0): QCDM/DIAG port # ttyUSB1 (if #1): GPS data port # ttyUSB2 (if #2): AT primary port # ttyUSB3 (if #3): AT secondary port ATTRS{idVendor}=="2c7c", ATTRS{idProduct}=="0296", ENV{.MM_USBIFNUM}=="00", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_QCDM}="1" ATTRS{idVendor}=="2c7c", ATTRS{idProduct}=="0296", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_GPS}="1" ATTRS{idVendor}=="2c7c", ATTRS{idProduct}=="0296", ENV{.MM_USBIFNUM}=="02", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1" ATTRS{idVendor}=="2c7c", ATTRS{idProduct}=="0296", ENV{.MM_USBIFNUM}=="03", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1" # Quectel EC25/EG25 # ttyUSB0 (if #0): QCDM/DIAG port # ttyUSB1 (if #1): GPS data port # ttyUSB2 (if #2): AT primary port # ttyUSB3 (if #3): AT secondary port ATTRS{idVendor}=="2c7c", ATTRS{idProduct}=="0125", ENV{.MM_USBIFNUM}=="00", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_QCDM}="1" ATTRS{idVendor}=="2c7c", ATTRS{idProduct}=="0125", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_GPS}="1" ATTRS{idVendor}=="2c7c", ATTRS{idProduct}=="0125", ENV{.MM_USBIFNUM}=="02", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1" ATTRS{idVendor}=="2c7c", ATTRS{idProduct}=="0125", ENV{.MM_USBIFNUM}=="03", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1" # Quectel EG12-EA # ttyUSB0 (if #0): QCDM/DIAG port # ttyUSB1 (if #1): GPS data port # ttyUSB2 (if #2): AT primary port # ttyUSB3 (if #3): AT secondary port ATTRS{idVendor}=="2c7c", ATTRS{idProduct}=="0512", ENV{.MM_USBIFNUM}=="00", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_QCDM}="1" ATTRS{idVendor}=="2c7c", ATTRS{idProduct}=="0512", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_GPS}="1" ATTRS{idVendor}=="2c7c", ATTRS{idProduct}=="0512", ENV{.MM_USBIFNUM}=="02", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1" ATTRS{idVendor}=="2c7c", ATTRS{idProduct}=="0512", ENV{.MM_USBIFNUM}=="03", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1" # Quectel EC21-EUX # ttyUSB0 (if #0): QCDM/DIAG port # ttyUSB1 (if #1): GPS data port # ttyUSB2 (if #2): AT primary port # ttyUSB3 (if #3): AT secondary port ATTRS{idVendor}=="2c7c", ATTRS{idProduct}=="0121", ENV{.MM_USBIFNUM}=="00", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_QCDM}="1" ATTRS{idVendor}=="2c7c", ATTRS{idProduct}=="0121", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_GPS}="1" ATTRS{idVendor}=="2c7c", ATTRS{idProduct}=="0121", ENV{.MM_USBIFNUM}=="02", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1" ATTRS{idVendor}=="2c7c", ATTRS{idProduct}=="0121", ENV{.MM_USBIFNUM}=="03", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1" # Quectel RM500 # ttyUSB0 (if #0): QCDM/DIAG port # ttyUSB1 (if #1): GPS data port # ttyUSB2 (if #2): AT primary port # ttyUSB3 (if #3): AT secondary port ATTRS{idVendor}=="2c7c", ATTRS{idProduct}=="0800", ENV{.MM_USBIFNUM}=="00", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_QCDM}="1" ATTRS{idVendor}=="2c7c", ATTRS{idProduct}=="0800", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_GPS}="1" ATTRS{idVendor}=="2c7c", ATTRS{idProduct}=="0800", ENV{.MM_USBIFNUM}=="02", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1" ATTRS{idVendor}=="2c7c", ATTRS{idProduct}=="0800", ENV{.MM_USBIFNUM}=="03", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1" # Quectel EM060K-GL (MBIM) # ttyUSB0 (if #3): QCDM/DIAG port # ttyUSB1 (if #4): GPS data port # ttyUSB2 (if #5): AT port ATTRS{idVendor}=="2c7c", ATTRS{idProduct}=="0128", ENV{.MM_USBIFNUM}=="03", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_QCDM}="1" ATTRS{idVendor}=="2c7c", ATTRS{idProduct}=="0128", ENV{.MM_USBIFNUM}=="04", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_GPS}="1" ATTRS{idVendor}=="2c7c", ATTRS{idProduct}=="0128", ENV{.MM_USBIFNUM}=="05", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1" # Quectel RM520N # ttyUSB0 (if #0): QCDM/DIAG port # ttyUSB1 (if #1): GPS data port # ttyUSB2 (if #2): AT primary port # ttyUSB3 (if #3): AT secondary port ATTRS{idVendor}=="2c7c", ATTRS{idProduct}=="0801", ENV{.MM_USBIFNUM}=="00", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_QCDM}="1" ATTRS{idVendor}=="2c7c", ATTRS{idProduct}=="0801", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_GPS}="1" ATTRS{idVendor}=="2c7c", ATTRS{idProduct}=="0801", ENV{.MM_USBIFNUM}=="02", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1" ATTRS{idVendor}=="2c7c", ATTRS{idProduct}=="0801", ENV{.MM_USBIFNUM}=="03", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1" # Quectel EM05-G variants with Sahara-Firehose support: ATTRS{idVendor}=="2c7c", ATTRS{idProduct}=="030a", ENV{ID_MM_QUECTEL_FIREHOSE}="1" ATTRS{idVendor}=="2c7c", ATTRS{idProduct}=="030a", ENV{ID_MM_QUECTEL_SAHARA}="1" ATTRS{idVendor}=="2c7c", ATTRS{idProduct}=="030c", ENV{ID_MM_QUECTEL_FIREHOSE}="1" ATTRS{idVendor}=="2c7c", ATTRS{idProduct}=="030c", ENV{ID_MM_QUECTEL_SAHARA}="1" ATTRS{idVendor}=="2c7c", ATTRS{idProduct}=="0311", ENV{ID_MM_QUECTEL_FIREHOSE}="1" ATTRS{idVendor}=="2c7c", ATTRS{idProduct}=="0311", ENV{ID_MM_QUECTEL_SAHARA}="1" ATTRS{idVendor}=="2c7c", ATTRS{idProduct}=="0313", ENV{ID_MM_QUECTEL_FIREHOSE}="1" ATTRS{idVendor}=="2c7c", ATTRS{idProduct}=="0313", ENV{ID_MM_QUECTEL_SAHARA}="1" ATTRS{idVendor}=="2c7c", ATTRS{idProduct}=="0314", ENV{ID_MM_QUECTEL_FIREHOSE}="1" ATTRS{idVendor}=="2c7c", ATTRS{idProduct}=="0314", ENV{ID_MM_QUECTEL_SAHARA}="1" # Quectel EM061K-GL variants with Sahara-Firehose support: ATTRS{idVendor}=="2c7c", ATTRS{idProduct}=="6008", ENV{ID_MM_QUECTEL_FIREHOSE}="1" ATTRS{idVendor}=="2c7c", ATTRS{idProduct}=="6008", ENV{ID_MM_QUECTEL_SAHARA}="1" ATTRS{idVendor}=="2c7c", ATTRS{idProduct}=="6009", ENV{ID_MM_QUECTEL_FIREHOSE}="1" ATTRS{idVendor}=="2c7c", ATTRS{idProduct}=="6009", ENV{ID_MM_QUECTEL_SAHARA}="1" ATTRS{idVendor}=="2c7c", ATTRS{idProduct}=="0123", ENV{ID_MM_QUECTEL_FIREHOSE}="1" ATTRS{idVendor}=="2c7c", ATTRS{idProduct}=="0123", ENV{ID_MM_QUECTEL_SAHARA}="1" ATTRS{idVendor}=="2c7c", ATTRS{idProduct}=="0124", ENV{ID_MM_QUECTEL_FIREHOSE}="1" ATTRS{idVendor}=="2c7c", ATTRS{idProduct}=="0124", ENV{ID_MM_QUECTEL_SAHARA}="1" # Quectel EM05-CN variants with Sahara-Firehose support ATTRS{idVendor}=="2c7c", ATTRS{idProduct}=="0310", ENV{ID_MM_QUECTEL_FIREHOSE}="1" ATTRS{idVendor}=="2c7c", ATTRS{idProduct}=="0310", ENV{ID_MM_QUECTEL_SAHARA}="1" ATTRS{idVendor}=="2c7c", ATTRS{idProduct}=="0312", ENV{ID_MM_QUECTEL_FIREHOSE}="1" ATTRS{idVendor}=="2c7c", ATTRS{idProduct}=="0312", ENV{ID_MM_QUECTEL_SAHARA}="1" # Quectel EM05-CE with Sahara-Firehose support ATTRS{idVendor}=="2c7c", ATTRS{idProduct}=="0127", ENV{ID_MM_QUECTEL_FIREHOSE}="1" ATTRS{idVendor}=="2c7c", ATTRS{idProduct}=="0127", ENV{ID_MM_QUECTEL_SAHARA}="1" GOTO="mm_quectel_end" LABEL="mm_quectel_pci" # Quectel EM120 and EM160 (two variants: 0x1002 and 0x100d) with firehose support ATTRS{vendor}=="0x1eac", ATTRS{device}=="0x1001", ENV{ID_MM_QUECTEL_FIREHOSE}="1" ATTRS{vendor}=="0x1eac", ATTRS{device}=="0x1002", ENV{ID_MM_QUECTEL_FIREHOSE}="1" ATTRS{vendor}=="0x1eac", ATTRS{device}=="0x100d", ENV{ID_MM_QUECTEL_FIREHOSE}="1" # Quectel RM520 (two variants: 0x1004 and 0x1007) with firehose support ATTRS{vendor}=="0x1eac", ATTRS{device}=="0x1004", ENV{ID_MM_QUECTEL_FIREHOSE}="1" ATTRS{vendor}=="0x1eac", ATTRS{device}=="0x1007", ENV{ID_MM_QUECTEL_FIREHOSE}="1" LABEL="mm_quectel_end" ModemManager-1.23.4-dev/src/plugins/quectel/mm-broadband-modem-mbim-quectel.c000066400000000000000000000072771456466623000270240ustar00rootroot00000000000000/* -*- 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) 2020 Aleksander Morgado * Copyright (C) 2021 Ivan Mikhanchuk */ #include #include "mm-base-modem-at.h" #include "mm-log-object.h" #include "mm-iface-modem.h" #include "mm-iface-modem-firmware.h" #include "mm-iface-modem-time.h" #include "mm-shared-quectel.h" #include "mm-modem-helpers-quectel.h" #include "mm-broadband-modem-mbim-quectel.h" static void iface_modem_firmware_init (MMIfaceModemFirmware *iface); static void iface_modem_time_init (MMIfaceModemTime *iface); static void shared_quectel_init (MMSharedQuectel *iface); G_DEFINE_TYPE_EXTENDED (MMBroadbandModemMbimQuectel, mm_broadband_modem_mbim_quectel, MM_TYPE_BROADBAND_MODEM_MBIM, 0, G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_FIRMWARE, iface_modem_firmware_init) G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_TIME, iface_modem_time_init) G_IMPLEMENT_INTERFACE (MM_TYPE_SHARED_QUECTEL, shared_quectel_init)) /*****************************************************************************/ MMBroadbandModemMbimQuectel * mm_broadband_modem_mbim_quectel_new (const gchar *device, const gchar *physdev, const gchar **drivers, const gchar *plugin, guint16 vendor_id, guint16 product_id) { return g_object_new (MM_TYPE_BROADBAND_MODEM_MBIM_QUECTEL, MM_BASE_MODEM_DEVICE, device, MM_BASE_MODEM_PHYSDEV, physdev, MM_BASE_MODEM_DRIVERS, drivers, MM_BASE_MODEM_PLUGIN, plugin, MM_BASE_MODEM_VENDOR_ID, vendor_id, MM_BASE_MODEM_PRODUCT_ID, product_id, /* include carrier information */ MM_IFACE_MODEM_FIRMWARE_IGNORE_CARRIER, FALSE, /* MBIM bearer supports NET only */ MM_BASE_MODEM_DATA_NET_SUPPORTED, TRUE, MM_BASE_MODEM_DATA_TTY_SUPPORTED, FALSE, MM_IFACE_MODEM_SIM_HOT_SWAP_SUPPORTED, TRUE, MM_IFACE_MODEM_PERIODIC_SIGNAL_CHECK_DISABLED, TRUE, NULL); } static void mm_broadband_modem_mbim_quectel_init (MMBroadbandModemMbimQuectel *self) { } static void iface_modem_firmware_init (MMIfaceModemFirmware *iface) { iface->load_update_settings = mm_shared_quectel_firmware_load_update_settings; iface->load_update_settings_finish = mm_shared_quectel_firmware_load_update_settings_finish; } static void iface_modem_time_init (MMIfaceModemTime *iface) { iface->check_support = mm_shared_quectel_time_check_support; iface->check_support_finish = mm_shared_quectel_time_check_support_finish; } static void shared_quectel_init (MMSharedQuectel *iface) { } static void mm_broadband_modem_mbim_quectel_class_init (MMBroadbandModemMbimQuectelClass *klass) { } ModemManager-1.23.4-dev/src/plugins/quectel/mm-broadband-modem-mbim-quectel.h000066400000000000000000000051461456466623000270220ustar00rootroot00000000000000/* -*- 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) 2020 Aleksander Morgado * Copyright (C) 2021 Ivan Mikhanchuk */ #ifndef MM_BROADBAND_MODEM_MBIM_QUECTEL_H #define MM_BROADBAND_MODEM_MBIM_QUECTEL_H #include "mm-broadband-modem-mbim.h" #define MM_TYPE_BROADBAND_MODEM_MBIM_QUECTEL (mm_broadband_modem_mbim_quectel_get_type ()) #define MM_BROADBAND_MODEM_MBIM_QUECTEL(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_BROADBAND_MODEM_MBIM_QUECTEL, MMBroadbandModemMbimQuectel)) #define MM_BROADBAND_MODEM_MBIM_QUECTEL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_BROADBAND_MODEM_MBIM_QUECTEL, MMBroadbandModemMbimQuectelClass)) #define MM_IS_BROADBAND_MODEM_MBIM_QUECTEL(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_BROADBAND_MODEM_MBIM_QUECTEL)) #define MM_IS_BROADBAND_MODEM_MBIM_QUECTEL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_BROADBAND_MODEM_MBIM_QUECTEL)) #define MM_BROADBAND_MODEM_MBIM_QUECTEL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_BROADBAND_MODEM_MBIM_QUECTEL, MMBroadbandModemMbimQuectelClass)) typedef struct _MMBroadbandModemMbimQuectel MMBroadbandModemMbimQuectel; typedef struct _MMBroadbandModemMbimQuectelClass MMBroadbandModemMbimQuectelClass; struct _MMBroadbandModemMbimQuectel { MMBroadbandModemMbim parent; }; struct _MMBroadbandModemMbimQuectelClass{ MMBroadbandModemMbimClass parent; }; GType mm_broadband_modem_mbim_quectel_get_type (void); MMBroadbandModemMbimQuectel *mm_broadband_modem_mbim_quectel_new (const gchar *device, const gchar *physdev, const gchar **drivers, const gchar *plugin, guint16 vendor_id, guint16 product_id); #endif /* MM_BROADBAND_MODEM_MBIM_QUECTEL_H */ ModemManager-1.23.4-dev/src/plugins/quectel/mm-broadband-modem-qmi-quectel.c000066400000000000000000000133301456466623000266510ustar00rootroot00000000000000/* -*- 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) 2018-2020 Aleksander Morgado */ #include #include "mm-broadband-modem-qmi-quectel.h" #include "mm-iface-modem-firmware.h" #include "mm-iface-modem-location.h" #include "mm-iface-modem-time.h" #include "mm-shared-quectel.h" static void iface_modem_init (MMIfaceModem *iface); static void iface_modem_firmware_init (MMIfaceModemFirmware *iface); static void iface_modem_location_init (MMIfaceModemLocation *iface); static void iface_modem_time_init (MMIfaceModemTime *iface); static void shared_quectel_init (MMSharedQuectel *iface); static MMIfaceModem *iface_modem_parent; static MMIfaceModemLocation *iface_modem_location_parent; G_DEFINE_TYPE_EXTENDED (MMBroadbandModemQmiQuectel, mm_broadband_modem_qmi_quectel, MM_TYPE_BROADBAND_MODEM_QMI, 0, G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM, iface_modem_init) G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_FIRMWARE, iface_modem_firmware_init) G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_LOCATION, iface_modem_location_init) G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_TIME, iface_modem_time_init) G_IMPLEMENT_INTERFACE (MM_TYPE_SHARED_QUECTEL, shared_quectel_init)) /*****************************************************************************/ MMBroadbandModemQmiQuectel * mm_broadband_modem_qmi_quectel_new (const gchar *device, const gchar *physdev, const gchar **drivers, const gchar *plugin, guint16 vendor_id, guint16 product_id) { return g_object_new (MM_TYPE_BROADBAND_MODEM_QMI_QUECTEL, MM_BASE_MODEM_DEVICE, device, MM_BASE_MODEM_PHYSDEV, physdev, MM_BASE_MODEM_DRIVERS, drivers, MM_BASE_MODEM_PLUGIN, plugin, MM_BASE_MODEM_VENDOR_ID, vendor_id, MM_BASE_MODEM_PRODUCT_ID, product_id, /* exclude carrier information */ MM_IFACE_MODEM_FIRMWARE_IGNORE_CARRIER, TRUE, /* QMI bearer supports NET only */ MM_BASE_MODEM_DATA_NET_SUPPORTED, TRUE, MM_BASE_MODEM_DATA_TTY_SUPPORTED, FALSE, MM_IFACE_MODEM_SIM_HOT_SWAP_SUPPORTED, TRUE, NULL); } static void mm_broadband_modem_qmi_quectel_init (MMBroadbandModemQmiQuectel *self) { } static void iface_modem_init (MMIfaceModem *iface) { iface_modem_parent = g_type_interface_peek_parent (iface); iface->setup_sim_hot_swap = mm_shared_quectel_setup_sim_hot_swap; iface->setup_sim_hot_swap_finish = mm_shared_quectel_setup_sim_hot_swap_finish; iface->cleanup_sim_hot_swap = mm_shared_quectel_cleanup_sim_hot_swap; } static MMIfaceModem * peek_parent_modem_interface (MMSharedQuectel *self) { return iface_modem_parent; } static MMBroadbandModemClass * peek_parent_broadband_modem_class (MMSharedQuectel *self) { return MM_BROADBAND_MODEM_CLASS (mm_broadband_modem_qmi_quectel_parent_class); } static void iface_modem_firmware_init (MMIfaceModemFirmware *iface) { iface->load_update_settings = mm_shared_quectel_firmware_load_update_settings; iface->load_update_settings_finish = mm_shared_quectel_firmware_load_update_settings_finish; } static void iface_modem_location_init (MMIfaceModemLocation *iface) { iface_modem_location_parent = g_type_interface_peek_parent (iface); iface->load_capabilities = mm_shared_quectel_location_load_capabilities; iface->load_capabilities_finish = mm_shared_quectel_location_load_capabilities_finish; iface->enable_location_gathering = mm_shared_quectel_enable_location_gathering; iface->enable_location_gathering_finish = mm_shared_quectel_enable_location_gathering_finish; iface->disable_location_gathering = mm_shared_quectel_disable_location_gathering; iface->disable_location_gathering_finish = mm_shared_quectel_disable_location_gathering_finish; } static MMIfaceModemLocation * peek_parent_modem_location_interface (MMSharedQuectel *self) { return iface_modem_location_parent; } static void iface_modem_time_init (MMIfaceModemTime *iface) { iface->check_support = mm_shared_quectel_time_check_support; iface->check_support_finish = mm_shared_quectel_time_check_support_finish; } static void shared_quectel_init (MMSharedQuectel *iface) { iface->peek_parent_modem_interface = peek_parent_modem_interface; iface->peek_parent_modem_location_interface = peek_parent_modem_location_interface; iface->peek_parent_broadband_modem_class = peek_parent_broadband_modem_class; } static void mm_broadband_modem_qmi_quectel_class_init (MMBroadbandModemQmiQuectelClass *klass) { MMBroadbandModemClass *broadband_modem_class = MM_BROADBAND_MODEM_CLASS (klass); broadband_modem_class->setup_ports = mm_shared_quectel_setup_ports; } ModemManager-1.23.4-dev/src/plugins/quectel/mm-broadband-modem-qmi-quectel.h000066400000000000000000000047721456466623000266700ustar00rootroot00000000000000/* -*- 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) 2018 Aleksander Morgado */ #ifndef MM_BROADBAND_MODEM_QMI_QUECTEL_H #define MM_BROADBAND_MODEM_QMI_QUECTEL_H #include "mm-broadband-modem-qmi.h" #define MM_TYPE_BROADBAND_MODEM_QMI_QUECTEL (mm_broadband_modem_qmi_quectel_get_type ()) #define MM_BROADBAND_MODEM_QMI_QUECTEL(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_BROADBAND_MODEM_QMI_QUECTEL, MMBroadbandModemQmiQuectel)) #define MM_BROADBAND_MODEM_QMI_QUECTEL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_BROADBAND_MODEM_QMI_QUECTEL, MMBroadbandModemQmiQuectelClass)) #define MM_IS_BROADBAND_MODEM_QMI_QUECTEL(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_BROADBAND_MODEM_QMI_QUECTEL)) #define MM_IS_BROADBAND_MODEM_QMI_QUECTEL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_BROADBAND_MODEM_QMI_QUECTEL)) #define MM_BROADBAND_MODEM_QMI_QUECTEL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_BROADBAND_MODEM_QMI_QUECTEL, MMBroadbandModemQmiQuectelClass)) typedef struct _MMBroadbandModemQmiQuectel MMBroadbandModemQmiQuectel; typedef struct _MMBroadbandModemQmiQuectelClass MMBroadbandModemQmiQuectelClass; struct _MMBroadbandModemQmiQuectel { MMBroadbandModemQmi parent; }; struct _MMBroadbandModemQmiQuectelClass{ MMBroadbandModemQmiClass parent; }; GType mm_broadband_modem_qmi_quectel_get_type (void); MMBroadbandModemQmiQuectel *mm_broadband_modem_qmi_quectel_new (const gchar *device, const gchar *physdev, const gchar **drivers, const gchar *plugin, guint16 vendor_id, guint16 product_id); #endif /* MM_BROADBAND_MODEM_QMI_QUECTEL_H */ ModemManager-1.23.4-dev/src/plugins/quectel/mm-broadband-modem-quectel.c000066400000000000000000000130321456466623000260640ustar00rootroot00000000000000/* -*- 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) 2018-2020 Aleksander Morgado */ #include #include "mm-broadband-modem-quectel.h" #include "mm-iface-modem-firmware.h" #include "mm-iface-modem-location.h" #include "mm-iface-modem-time.h" #include "mm-shared-quectel.h" static void iface_modem_init (MMIfaceModem *iface); static void iface_modem_firmware_init (MMIfaceModemFirmware *iface); static void iface_modem_location_init (MMIfaceModemLocation *iface); static void iface_modem_time_init (MMIfaceModemTime *iface); static void shared_quectel_init (MMSharedQuectel *iface); static MMIfaceModem *iface_modem_parent; static MMIfaceModemLocation *iface_modem_location_parent; G_DEFINE_TYPE_EXTENDED (MMBroadbandModemQuectel, mm_broadband_modem_quectel, MM_TYPE_BROADBAND_MODEM, 0, G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM, iface_modem_init) G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_FIRMWARE, iface_modem_firmware_init) G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_LOCATION, iface_modem_location_init) G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_TIME, iface_modem_time_init) G_IMPLEMENT_INTERFACE (MM_TYPE_SHARED_QUECTEL, shared_quectel_init)) /*****************************************************************************/ MMBroadbandModemQuectel * mm_broadband_modem_quectel_new (const gchar *device, const gchar *physdev, const gchar **drivers, const gchar *plugin, guint16 vendor_id, guint16 product_id) { return g_object_new (MM_TYPE_BROADBAND_MODEM_QUECTEL, MM_BASE_MODEM_DEVICE, device, MM_BASE_MODEM_PHYSDEV, physdev, MM_BASE_MODEM_DRIVERS, drivers, MM_BASE_MODEM_PLUGIN, plugin, MM_BASE_MODEM_VENDOR_ID, vendor_id, MM_BASE_MODEM_PRODUCT_ID, product_id, /* Generic bearer supports TTY only */ MM_BASE_MODEM_DATA_NET_SUPPORTED, FALSE, MM_BASE_MODEM_DATA_TTY_SUPPORTED, TRUE, MM_IFACE_MODEM_SIM_HOT_SWAP_SUPPORTED, TRUE, NULL); } static void mm_broadband_modem_quectel_init (MMBroadbandModemQuectel *self) { } static void iface_modem_init (MMIfaceModem *iface) { iface_modem_parent = g_type_interface_peek_parent (iface); iface->setup_sim_hot_swap = mm_shared_quectel_setup_sim_hot_swap; iface->setup_sim_hot_swap_finish = mm_shared_quectel_setup_sim_hot_swap_finish; iface->cleanup_sim_hot_swap = mm_shared_quectel_cleanup_sim_hot_swap; } static MMIfaceModem * peek_parent_modem_interface (MMSharedQuectel *self) { return iface_modem_parent; } static MMBroadbandModemClass * peek_parent_broadband_modem_class (MMSharedQuectel *self) { return MM_BROADBAND_MODEM_CLASS (mm_broadband_modem_quectel_parent_class); } static void iface_modem_firmware_init (MMIfaceModemFirmware *iface) { iface->load_update_settings = mm_shared_quectel_firmware_load_update_settings; iface->load_update_settings_finish = mm_shared_quectel_firmware_load_update_settings_finish; } static void iface_modem_location_init (MMIfaceModemLocation *iface) { iface_modem_location_parent = g_type_interface_peek_parent (iface); iface->load_capabilities = mm_shared_quectel_location_load_capabilities; iface->load_capabilities_finish = mm_shared_quectel_location_load_capabilities_finish; iface->enable_location_gathering = mm_shared_quectel_enable_location_gathering; iface->enable_location_gathering_finish = mm_shared_quectel_enable_location_gathering_finish; iface->disable_location_gathering = mm_shared_quectel_disable_location_gathering; iface->disable_location_gathering_finish = mm_shared_quectel_disable_location_gathering_finish; } static MMIfaceModemLocation * peek_parent_modem_location_interface (MMSharedQuectel *self) { return iface_modem_location_parent; } static void iface_modem_time_init (MMIfaceModemTime *iface) { iface->check_support = mm_shared_quectel_time_check_support; iface->check_support_finish = mm_shared_quectel_time_check_support_finish; } static void shared_quectel_init (MMSharedQuectel *iface) { iface->peek_parent_modem_interface = peek_parent_modem_interface; iface->peek_parent_modem_location_interface = peek_parent_modem_location_interface; iface->peek_parent_broadband_modem_class = peek_parent_broadband_modem_class; } static void mm_broadband_modem_quectel_class_init (MMBroadbandModemQuectelClass *klass) { MMBroadbandModemClass *broadband_modem_class = MM_BROADBAND_MODEM_CLASS (klass); broadband_modem_class->setup_ports = mm_shared_quectel_setup_ports; } ModemManager-1.23.4-dev/src/plugins/quectel/mm-broadband-modem-quectel.h000066400000000000000000000045531456466623000261010ustar00rootroot00000000000000/* -*- 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) 2018 Aleksander Morgado */ #ifndef MM_BROADBAND_MODEM_QUECTEL_H #define MM_BROADBAND_MODEM_QUECTEL_H #include "mm-broadband-modem.h" #define MM_TYPE_BROADBAND_MODEM_QUECTEL (mm_broadband_modem_quectel_get_type ()) #define MM_BROADBAND_MODEM_QUECTEL(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_BROADBAND_MODEM_QUECTEL, MMBroadbandModemQuectel)) #define MM_BROADBAND_MODEM_QUECTEL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_BROADBAND_MODEM_QUECTEL, MMBroadbandModemQuectelClass)) #define MM_IS_BROADBAND_MODEM_QUECTEL(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_BROADBAND_MODEM_QUECTEL)) #define MM_IS_BROADBAND_MODEM_QUECTEL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_BROADBAND_MODEM_QUECTEL)) #define MM_BROADBAND_MODEM_QUECTEL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_BROADBAND_MODEM_QUECTEL, MMBroadbandModemQuectelClass)) typedef struct _MMBroadbandModemQuectel MMBroadbandModemQuectel; typedef struct _MMBroadbandModemQuectelClass MMBroadbandModemQuectelClass; struct _MMBroadbandModemQuectel { MMBroadbandModem parent; }; struct _MMBroadbandModemQuectelClass{ MMBroadbandModemClass parent; }; GType mm_broadband_modem_quectel_get_type (void); MMBroadbandModemQuectel *mm_broadband_modem_quectel_new (const gchar *device, const gchar *physdev, const gchar **drivers, const gchar *plugin, guint16 vendor_id, guint16 product_id); #endif /* MM_BROADBAND_MODEM_QUECTEL_H */ ModemManager-1.23.4-dev/src/plugins/quectel/mm-modem-helpers-quectel.c000066400000000000000000000113671456466623000256230ustar00rootroot00000000000000/* -*- 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) 2020 Aleksander Morgado */ #include #include #define _LIBMM_INSIDE_MM #include #include "mm-log.h" #include "mm-modem-helpers.h" #include "mm-modem-helpers-quectel.h" gboolean mm_quectel_parse_ctzu_test_response (const gchar *response, gpointer log_object, gboolean *supports_disable, gboolean *supports_enable, gboolean *supports_enable_update_rtc, GError **error) { g_auto(GStrv) split = NULL; g_autoptr(GArray) modes = NULL; guint i; /* * Response may be: * - +CTZU: (0,1) * - +CTZU: (0,1,3) */ #define N_EXPECTED_GROUPS 1 split = mm_split_string_groups (mm_strip_tag (response, "+CTZU:")); if (!split) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't split the +CTZU test response in groups"); return FALSE; } if (g_strv_length (split) != N_EXPECTED_GROUPS) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Cannot parse +CTZU test response: invalid number of groups (%u != %u)", g_strv_length (split), N_EXPECTED_GROUPS); return FALSE; } modes = mm_parse_uint_list (split[0], error); if (!modes) { g_prefix_error (error, "Failed to parse integer list in +CTZU test response: "); return FALSE; } *supports_disable = FALSE; *supports_enable = FALSE; *supports_enable_update_rtc = FALSE; for (i = 0; i < modes->len; i++) { guint mode; mode = g_array_index (modes, guint, i); switch (mode) { case 0: *supports_disable = TRUE; break; case 1: *supports_enable = TRUE; break; case 3: *supports_enable_update_rtc = TRUE; break; default: mm_obj_dbg (log_object, "unknown +CTZU mode: %u", mode); break; } } return TRUE; } /*****************************************************************************/ /* standard firmware info * Format of the string is: * "[main version]_[modem and app version]" * e.g. EM05GFAR07A07M1G_01.016.01.016 */ #define QUECTEL_STD_FIRMWARE_VERSION_SEG 2 /* Format of the string is: * "modem_main.modem_minor.ap_main.ap_minor" * e.g. 01.016.01.016 */ #define QUECTEL_STD_MODEM_AP_FIRMWARE_VER_SEG 4 #define QUECTEL_STD_MODEM_AP_FIRMWARE_VER_LEN 13 #define QUECTEL_MAIN_VERSION_INVALID_TAG "00" #define QUECTEL_MINOR_VERSION_INVALID_TAG "000" gboolean mm_quectel_check_standard_firmware_version_valid (const gchar *std_str) { gboolean valid = TRUE; g_auto(GStrv) split_std_fw = NULL; g_auto(GStrv) split_modem_ap_fw = NULL; const gchar *modem_ap_fw; if (std_str) { split_std_fw = g_strsplit (std_str, "_", QUECTEL_STD_FIRMWARE_VERSION_SEG); /* Quectel standard format of the [main version]_[modem and app version] * Sometimes we find that the [modem and app version] query is missing by [AT+QMGR] * for example: we expect EM05GFAR07A07M1G_01.016.01.016,but unexpected EM05GFAR07A07M1G_01.016.00.000 was returned * Quectel will check for this abnormal [modem and app version] and flag it */ if (g_strv_length (split_std_fw) == QUECTEL_STD_FIRMWARE_VERSION_SEG) { modem_ap_fw = split_std_fw[1]; if (strlen (modem_ap_fw) == QUECTEL_STD_MODEM_AP_FIRMWARE_VER_LEN) { split_modem_ap_fw = g_strsplit (modem_ap_fw, ".", QUECTEL_STD_MODEM_AP_FIRMWARE_VER_SEG); if (g_strv_length (split_modem_ap_fw) == QUECTEL_STD_MODEM_AP_FIRMWARE_VER_SEG && !g_strcmp0 (split_modem_ap_fw[2], QUECTEL_MAIN_VERSION_INVALID_TAG) && !g_strcmp0 (split_modem_ap_fw[3], QUECTEL_MINOR_VERSION_INVALID_TAG)){ valid = FALSE; } } } } return valid; } ModemManager-1.23.4-dev/src/plugins/quectel/mm-modem-helpers-quectel.h000066400000000000000000000025721456466623000256260ustar00rootroot00000000000000/* -*- 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) 2020 Aleksander Morgado */ #ifndef MM_MODEM_HELPERS_QUECTEL_H #define MM_MODEM_HELPERS_QUECTEL_H #include #include #define _LIBMM_INSIDE_MM #include gboolean mm_quectel_parse_ctzu_test_response (const gchar *response, gpointer log_object, gboolean *supports_disable, gboolean *supports_enable, gboolean *supports_enable_update_rtc, GError **error); gboolean mm_quectel_check_standard_firmware_version_valid (const gchar *std_str); #endif /* MM_MODEM_HELPERS_QUECTEL_H */ ModemManager-1.23.4-dev/src/plugins/quectel/mm-plugin-quectel.c000066400000000000000000000106711456466623000243550ustar00rootroot00000000000000/* -*- 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) 2017-2018 Aleksander Morgado */ #include #include #define _LIBMM_INSIDE_MM #include #include "mm-log-object.h" #include "mm-plugin-common.h" #include "mm-broadband-modem-quectel.h" #if defined WITH_QMI #include "mm-broadband-modem-qmi-quectel.h" #endif #if defined WITH_MBIM #include "mm-broadband-modem-mbim.h" #include "mm-broadband-modem-mbim-quectel.h" #endif #define MM_TYPE_PLUGIN_QUECTEL mm_plugin_quectel_get_type () MM_DEFINE_PLUGIN (QUECTEL, quectel, Quectel) /*****************************************************************************/ static MMBaseModem * create_modem (MMPlugin *self, const gchar *uid, const gchar *physdev, const gchar **drivers, guint16 vendor, guint16 product, guint16 subsystem_vendor, GList *probes, GError **error) { #if defined WITH_QMI if (mm_port_probe_list_has_qmi_port (probes)) { mm_obj_dbg (self, "QMI-powered Quectel modem found..."); return MM_BASE_MODEM (mm_broadband_modem_qmi_quectel_new (uid, physdev, drivers, mm_plugin_get_name (self), vendor, product)); } #endif #if defined WITH_MBIM if (mm_port_probe_list_has_mbim_port (probes)) { mm_obj_dbg (self, "MBIM-powered Quectel modem found..."); return MM_BASE_MODEM (mm_broadband_modem_mbim_quectel_new (uid, physdev, drivers, mm_plugin_get_name (self), vendor, product)); } #endif return MM_BASE_MODEM (mm_broadband_modem_quectel_new (uid, physdev, drivers, mm_plugin_get_name (self), vendor, product)); } /*****************************************************************************/ MM_PLUGIN_NAMED_CREATOR_SCOPE MMPlugin * mm_plugin_create_quectel (void) { static const gchar *subsystems[] = { "tty", "net", "usbmisc", "wwan", NULL }; static const gchar *vendor_strings[] = { "quectel", NULL }; static const guint16 vendor_ids[] = { 0x2c7c, /* usb vid */ 0x1eac, /* pci vid */ 0 }; return MM_PLUGIN ( g_object_new (MM_TYPE_PLUGIN_QUECTEL, MM_PLUGIN_NAME, MM_MODULE_NAME, MM_PLUGIN_ALLOWED_SUBSYSTEMS, subsystems, MM_PLUGIN_ALLOWED_VENDOR_IDS, vendor_ids, MM_PLUGIN_ALLOWED_VENDOR_STRINGS, vendor_strings, MM_PLUGIN_ALLOWED_AT, TRUE, MM_PLUGIN_ALLOWED_QCDM, TRUE, MM_PLUGIN_ALLOWED_QMI, TRUE, MM_PLUGIN_ALLOWED_MBIM, TRUE, NULL)); } static void mm_plugin_quectel_init (MMPluginQuectel *self) { } static void mm_plugin_quectel_class_init (MMPluginQuectelClass *klass) { MMPluginClass *plugin_class = MM_PLUGIN_CLASS (klass); plugin_class->create_modem = create_modem; } ModemManager-1.23.4-dev/src/plugins/quectel/mm-shared-quectel.c000066400000000000000000001207051456466623000243250ustar00rootroot00000000000000/* -*- 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) 2018-2020 Aleksander Morgado * Copyright (c) 2022 Qualcomm Innovation Center, Inc. */ #include #include #include #define _LIBMM_INSIDE_MM #include #include "mm-log-object.h" #include "mm-iface-modem.h" #include "mm-iface-modem-firmware.h" #include "mm-iface-modem-location.h" #include "mm-base-modem.h" #include "mm-base-modem-at.h" #include "mm-shared-quectel.h" #include "mm-modem-helpers-quectel.h" #if defined WITH_MBIM #include "mm-broadband-modem-mbim.h" #endif /*****************************************************************************/ /* Private context */ #define PRIVATE_TAG "shared-quectel-private-tag" static GQuark private_quark; typedef enum { FEATURE_SUPPORT_UNKNOWN, FEATURE_NOT_SUPPORTED, FEATURE_SUPPORTED, } FeatureSupport; typedef struct { MMBroadbandModemClass *broadband_modem_class_parent; MMIfaceModem *iface_modem_parent; MMIfaceModemLocation *iface_modem_location_parent; MMModemLocationSource provided_sources; MMModemLocationSource enabled_sources; FeatureSupport qgps_supported; GRegex *qgpsurc_regex; GRegex *qlwurc_regex; GRegex *rdy_regex; } Private; static void private_free (Private *priv) { g_regex_unref (priv->qgpsurc_regex); g_regex_unref (priv->qlwurc_regex); g_regex_unref (priv->rdy_regex); g_slice_free (Private, priv); } static Private * get_private (MMSharedQuectel *self) { Private *priv; if (G_UNLIKELY (!private_quark)) private_quark = g_quark_from_static_string (PRIVATE_TAG); priv = g_object_get_qdata (G_OBJECT (self), private_quark); if (!priv) { priv = g_slice_new0 (Private); priv->provided_sources = MM_MODEM_LOCATION_SOURCE_NONE; priv->enabled_sources = MM_MODEM_LOCATION_SOURCE_NONE; priv->qgps_supported = FEATURE_SUPPORT_UNKNOWN; priv->qgpsurc_regex = g_regex_new ("\\r\\n\\+QGPSURC:.*", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); priv->qlwurc_regex = g_regex_new ("\\r\\n\\+QLWURC:.*", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); priv->rdy_regex = g_regex_new ("\\r\\nRDY", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); g_assert (priv->qgpsurc_regex); g_assert (priv->qlwurc_regex); g_assert (priv->rdy_regex); g_assert (MM_SHARED_QUECTEL_GET_INTERFACE (self)->peek_parent_broadband_modem_class); priv->broadband_modem_class_parent = MM_SHARED_QUECTEL_GET_INTERFACE (self)->peek_parent_broadband_modem_class (self); g_assert (MM_SHARED_QUECTEL_GET_INTERFACE (self)->peek_parent_modem_location_interface); priv->iface_modem_location_parent = MM_SHARED_QUECTEL_GET_INTERFACE (self)->peek_parent_modem_location_interface (self); g_assert (MM_SHARED_QUECTEL_GET_INTERFACE (self)->peek_parent_modem_interface); priv->iface_modem_parent = MM_SHARED_QUECTEL_GET_INTERFACE (self)->peek_parent_modem_interface (self); g_object_set_qdata_full (G_OBJECT (self), private_quark, priv, (GDestroyNotify)private_free); } return priv; } /*****************************************************************************/ /* RDY unsolicited event handler */ static void rdy_handler (MMPortSerialAt *port, GMatchInfo *match_info, MMBroadbandModem *self) { /* The RDY URC indicates a modem reset that may or may not go hand-in-hand * with USB re-enumeration. For the latter case, we must make sure to * re-synchronize modem and ModemManager states by re-probing. */ mm_obj_warn (self, "modem reset detected, triggering reprobe"); mm_base_modem_set_reprobe (MM_BASE_MODEM (self), TRUE); mm_base_modem_set_valid (MM_BASE_MODEM (self), FALSE); } /*****************************************************************************/ /* Setup ports (Broadband modem class) */ void mm_shared_quectel_setup_ports (MMBroadbandModem *self) { Private *priv; MMPortSerialAt *ports[2]; guint i; priv = get_private (MM_SHARED_QUECTEL (self)); g_assert (priv->broadband_modem_class_parent); g_assert (priv->broadband_modem_class_parent->setup_ports); /* Parent setup always first */ priv->broadband_modem_class_parent->setup_ports (self); ports[0] = mm_base_modem_peek_port_primary (MM_BASE_MODEM (self)); ports[1] = mm_base_modem_peek_port_secondary (MM_BASE_MODEM (self)); /* Enable/disable unsolicited events in given port */ for (i = 0; i < G_N_ELEMENTS (ports); i++) { if (!ports[i]) continue; /* Ignore +QGPSURC */ mm_port_serial_at_add_unsolicited_msg_handler ( ports[i], priv->qgpsurc_regex, NULL, NULL, NULL); /* Ignore +QLWURC */ mm_port_serial_at_add_unsolicited_msg_handler ( ports[i], priv->qlwurc_regex, NULL, NULL, NULL); /* Handle RDY */ mm_port_serial_at_add_unsolicited_msg_handler ( ports[i], priv->rdy_regex, (MMPortSerialAtUnsolicitedMsgFn)rdy_handler, self, NULL); } } /*****************************************************************************/ /* Firmware update settings loading (Firmware interface) */ typedef struct { MMFirmwareUpdateSettings *update_settings; gint get_firmware_maximum_retry_int; } LoadUpdateSettingsContext; static void load_update_settings_context_free (LoadUpdateSettingsContext *ctx) { if (ctx->update_settings) g_object_unref (ctx->update_settings); g_free (ctx); } MMFirmwareUpdateSettings * mm_shared_quectel_firmware_load_update_settings_finish (MMIfaceModemFirmware *self, GAsyncResult *res, GError **error) { return g_task_propagate_pointer (G_TASK (res), error); } static gboolean quectel_is_sahara_supported (MMBaseModem *modem, MMPort *port) { return mm_kernel_device_get_global_property_as_boolean (mm_port_peek_kernel_device (port), "ID_MM_QUECTEL_SAHARA"); } static gboolean quectel_is_firehose_supported (MMBaseModem *modem, MMPort *port) { return mm_kernel_device_get_global_property_as_boolean (mm_port_peek_kernel_device (port), "ID_MM_QUECTEL_FIREHOSE"); } static MMModemFirmwareUpdateMethod quectel_get_firmware_update_methods (MMBaseModem *modem, MMPort *port) { MMModemFirmwareUpdateMethod update_methods; update_methods = MM_MODEM_FIRMWARE_UPDATE_METHOD_NONE; if (quectel_is_firehose_supported (modem, port)) update_methods |= MM_MODEM_FIRMWARE_UPDATE_METHOD_FIREHOSE; if (quectel_is_sahara_supported (modem, port)) update_methods |= MM_MODEM_FIRMWARE_UPDATE_METHOD_SAHARA; return update_methods; } static gboolean quectel_at_port_get_firmware_version_retry (GTask *task); /* Eg. Sometimes when the module is booted up and sends the command to acquire the version to the modem, * the modem may not be ready. The standard app version number of the response was not obtained; * Fwupd(LVFS) requires relatively complete version information to update firmware. If the version information is incorrect, * the update may not be possible. Therefore, we will conduct another query, up to 16 times. */ #define QUECTEL_STD_AP_FIRMWARE_INVALID_MAXIMUM_RETRY 16 static void quectel_at_port_get_firmware_version_retry_ready (MMBaseModem *modem, GAsyncResult *res, GTask *task) { const gchar *version; LoadUpdateSettingsContext *ctx; ctx = g_task_get_task_data (task); version = mm_base_modem_at_command_finish (modem, res, NULL); ctx->get_firmware_maximum_retry_int--; if (version) { if (mm_quectel_check_standard_firmware_version_valid (version)) { mm_obj_dbg (modem, "Valid firmware version: %s, re-update", version); mm_firmware_update_settings_set_version (ctx->update_settings, version); g_task_return_pointer (task, g_object_ref (ctx->update_settings), g_object_unref); g_object_unref (task); return; } } /* When the maximum repeat fetch count is greater than or equal to 0, * attempt to retrieve version information again. */ if (ctx->get_firmware_maximum_retry_int >= 0) { g_timeout_add_seconds (1, (GSourceFunc) quectel_at_port_get_firmware_version_retry, task); return; } mm_obj_dbg (modem, "Maximum retries to query firmware version reached: invalid firmware version received"); g_task_return_pointer (task, g_object_ref (ctx->update_settings), g_object_unref); g_object_unref (task); } static gboolean quectel_at_port_get_firmware_version_retry (GTask *task) { MMBaseModem *self; self = g_task_get_source_object (task); /* Fetch full firmware info */ mm_base_modem_at_command (self, "+QGMR?", 3, FALSE, (GAsyncReadyCallback) quectel_at_port_get_firmware_version_retry_ready, task); return G_SOURCE_REMOVE; } static void quectel_at_port_get_firmware_version_ready (MMBaseModem *modem, GAsyncResult *res, GTask *task) { LoadUpdateSettingsContext *ctx; const gchar *version; gboolean ap_firmware_version_valid = TRUE; ctx = g_task_get_task_data (task); version = mm_base_modem_at_command_finish (modem, res, NULL); if (version) ap_firmware_version_valid = mm_quectel_check_standard_firmware_version_valid (version); if (version && ap_firmware_version_valid) { mm_firmware_update_settings_set_version (ctx->update_settings, version); g_task_return_pointer (task, g_object_ref (ctx->update_settings), g_object_unref); g_object_unref (task); return; } if (version) mm_obj_dbg (modem, "Invalid firmware version %s return, retrying", version); g_timeout_add_seconds (1, (GSourceFunc) quectel_at_port_get_firmware_version_retry, task); } #if defined WITH_MBIM static void quectel_mbim_port_get_firmware_version_ready (MbimDevice *device, GAsyncResult *res, GTask *task) { g_autoptr(MbimMessage) response = NULL; guint32 version_id; g_autofree gchar *version_str = NULL; LoadUpdateSettingsContext *ctx; ctx = g_task_get_task_data (task); response = mbim_device_command_finish (device, res, NULL); if (response && mbim_message_response_get_result (response, MBIM_MESSAGE_TYPE_COMMAND_DONE, NULL) && mbim_message_qdu_quectel_read_version_response_parse (response, &version_id, &version_str, NULL)) { mm_firmware_update_settings_set_version (ctx->update_settings, version_str); } g_task_return_pointer (task, g_object_ref (ctx->update_settings), g_object_unref); g_object_unref (task); } #endif static void qfastboot_test_ready (MMBaseModem *self, GAsyncResult *res, GTask *task) { LoadUpdateSettingsContext *ctx; ctx = g_task_get_task_data (task); /* Set update method */ if (mm_base_modem_at_command_finish (self, res, NULL)) { mm_firmware_update_settings_set_method (ctx->update_settings, MM_MODEM_FIRMWARE_UPDATE_METHOD_FASTBOOT); mm_firmware_update_settings_set_fastboot_at (ctx->update_settings, "AT+QFASTBOOT"); } else mm_firmware_update_settings_set_method (ctx->update_settings, MM_MODEM_FIRMWARE_UPDATE_METHOD_NONE); /* Fetch full firmware info */ mm_base_modem_at_command (MM_BASE_MODEM (self), "+QGMR?", 3, FALSE, (GAsyncReadyCallback) quectel_at_port_get_firmware_version_ready, task); } static void quectel_at_port_get_firmware_revision_ready (MMBaseModem *self, GAsyncResult *res, GTask *task) { LoadUpdateSettingsContext *ctx; MMModemFirmwareUpdateMethod update_methods; const gchar *revision; const gchar *name; const gchar *id; g_autoptr(GPtrArray) ids = NULL; GError *error = NULL; ctx = g_task_get_task_data (task); update_methods = mm_firmware_update_settings_get_method (ctx->update_settings); /* Set device ids */ ids = mm_iface_firmware_build_generic_device_ids (MM_IFACE_MODEM_FIRMWARE (self), &error); if (error) { mm_obj_warn (self, "failed to build generic device ids: %s", error->message); g_task_return_error (task, error); g_object_unref (task); return; } /* Add device id based on modem name */ revision = mm_base_modem_at_command_finish (self, res, NULL); if (revision && g_utf8_validate (revision, -1, NULL)) { name = g_strndup (revision, 7); mm_obj_dbg (self, "revision %s converted to modem name %s", revision, name); id = (const gchar *) g_ptr_array_index (ids, 0); g_ptr_array_insert (ids, 0, g_strdup_printf ("%s&NAME_%s", id, name)); } mm_firmware_update_settings_set_device_ids (ctx->update_settings, (const gchar **)ids->pdata); /* Set update methods */ if (update_methods & MM_MODEM_FIRMWARE_UPDATE_METHOD_FIREHOSE) { /* Fetch full firmware info */ mm_base_modem_at_command (self, "+QGMR?", 3, TRUE, (GAsyncReadyCallback) quectel_at_port_get_firmware_version_ready, task); } else { /* Check fastboot support */ mm_base_modem_at_command (self, "AT+QFASTBOOT=?", 3, TRUE, (GAsyncReadyCallback) qfastboot_test_ready, task); } } void mm_shared_quectel_firmware_load_update_settings (MMIfaceModemFirmware *self, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; MMPortSerialAt *at_port; MMModemFirmwareUpdateMethod update_methods; LoadUpdateSettingsContext *ctx; task = g_task_new (self, NULL, callback, user_data); ctx = g_new0 (LoadUpdateSettingsContext, 1); g_task_set_task_data (task, ctx, (GDestroyNotify)load_update_settings_context_free); at_port = mm_base_modem_peek_best_at_port (MM_BASE_MODEM (self), NULL); if (at_port) { update_methods = quectel_get_firmware_update_methods (MM_BASE_MODEM (self), MM_PORT (at_port)); ctx->update_settings = mm_firmware_update_settings_new (update_methods); ctx->get_firmware_maximum_retry_int = QUECTEL_STD_AP_FIRMWARE_INVALID_MAXIMUM_RETRY; /* Fetch modem name */ mm_base_modem_at_command (MM_BASE_MODEM (self), "+CGMR", 3, TRUE, (GAsyncReadyCallback) quectel_at_port_get_firmware_revision_ready, task); return; } #if defined WITH_MBIM { MMPortMbim *mbim = NULL; if (MM_IS_BROADBAND_MODEM_MBIM (self)) mbim = mm_broadband_modem_mbim_peek_port_mbim (MM_BROADBAND_MODEM_MBIM (self)); if (mbim) { g_autoptr(MbimMessage) message = NULL; update_methods = quectel_get_firmware_update_methods (MM_BASE_MODEM (self), MM_PORT (mbim)); ctx->update_settings = mm_firmware_update_settings_new (update_methods); ctx->get_firmware_maximum_retry_int = QUECTEL_STD_AP_FIRMWARE_INVALID_MAXIMUM_RETRY; /* Fetch firmware info */ message = mbim_message_qdu_quectel_read_version_set_new (MBIM_QDU_QUECTEL_VERSION_TYPE_FW_BUILD_ID, NULL); mbim_device_command (mm_port_mbim_peek_device (mbim), message, 5, NULL, (GAsyncReadyCallback) quectel_mbim_port_get_firmware_version_ready, task); return; } } #endif g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't find a port to fetch firmware info"); g_object_unref (task); } /*****************************************************************************/ /* "+QUSIM: 1" URC is emitted by Quectel modems after the USIM has been * (re)initialized. We register a handler for this URC and perform a check * for SIM swap when it is encountered. The motivation for this is to detect * M2M eUICC profile switches. According to SGP.02 chapter 3.2.1, the eUICC * shall trigger a REFRESH operation with eUICC reset when a new profile is * enabled. The +QUSIM URC appears after the eUICC has restarted and can act * as a trigger for profile switch check. This should basically be handled * the same as a physical SIM swap, so the existing SIM hot swap mechanism * is used. */ static void quectel_qusim_check_for_sim_swap_ready (MMIfaceModem *self, GAsyncResult *res) { g_autoptr(GError) error = NULL; if (!MM_IFACE_MODEM_GET_INTERFACE (self)->check_for_sim_swap_finish (self, res, &error)) mm_obj_warn (self, "couldn't check SIM swap: %s", error->message); else mm_obj_dbg (self, "check SIM swap completed"); } static void quectel_qusim_unsolicited_handler (MMPortSerialAt *port, GMatchInfo *match_info, MMIfaceModem *self) { if (!MM_IFACE_MODEM_GET_INTERFACE (self)->check_for_sim_swap || !MM_IFACE_MODEM_GET_INTERFACE (self)->check_for_sim_swap_finish) return; mm_obj_dbg (self, "checking SIM swap"); MM_IFACE_MODEM_GET_INTERFACE (self)->check_for_sim_swap ( self, (GAsyncReadyCallback)quectel_qusim_check_for_sim_swap_ready, NULL); } /*****************************************************************************/ /* Setup SIM hot swap context (Modem interface) */ gboolean mm_shared_quectel_setup_sim_hot_swap_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void parent_setup_sim_hot_swap_ready (MMIfaceModem *self, GAsyncResult *res, GTask *task) { Private *priv; g_autoptr(GError) error = NULL; priv = get_private (MM_SHARED_QUECTEL (self)); if (!priv->iface_modem_parent->setup_sim_hot_swap_finish (self, res, &error)) mm_obj_dbg (self, "additional SIM hot swap detection setup failed: %s", error->message); /* The +QUSIM based setup never fails, so we can safely return success here */ g_task_return_boolean (task, TRUE); g_object_unref (task); } void mm_shared_quectel_setup_sim_hot_swap (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data) { Private *priv; MMPortSerialAt *ports[2]; GTask *task; guint i; g_autoptr(GRegex) pattern = NULL; g_autoptr(GError) error = NULL; priv = get_private (MM_SHARED_QUECTEL (self)); task = g_task_new (self, NULL, callback, user_data); ports[0] = mm_base_modem_peek_port_primary (MM_BASE_MODEM (self)); ports[1] = mm_base_modem_peek_port_secondary (MM_BASE_MODEM (self)); pattern = g_regex_new ("\\+QUSIM:\\s*1\\r\\n", G_REGEX_RAW, 0, NULL); g_assert (pattern); for (i = 0; i < G_N_ELEMENTS (ports); i++) { if (ports[i]) mm_port_serial_at_add_unsolicited_msg_handler ( ports[i], pattern, (MMPortSerialAtUnsolicitedMsgFn)quectel_qusim_unsolicited_handler, self, NULL); } mm_obj_dbg (self, "+QUSIM detection set up"); if (!mm_broadband_modem_sim_hot_swap_ports_context_init (MM_BROADBAND_MODEM (self), &error)) mm_obj_warn (self, "failed to initialize SIM hot swap ports context: %s", error->message); /* Now, if available, setup parent logic */ if (priv->iface_modem_parent->setup_sim_hot_swap && priv->iface_modem_parent->setup_sim_hot_swap_finish) { priv->iface_modem_parent->setup_sim_hot_swap (self, (GAsyncReadyCallback) parent_setup_sim_hot_swap_ready, task); return; } /* Otherwise, we're done */ g_task_return_boolean (task, TRUE); g_object_unref (task); } /*****************************************************************************/ /* SIM hot swap cleanup (Modem interface) */ void mm_shared_quectel_cleanup_sim_hot_swap (MMIfaceModem *self) { mm_broadband_modem_sim_hot_swap_ports_context_reset (MM_BROADBAND_MODEM (self)); } /*****************************************************************************/ /* GPS trace received */ static void trace_received (MMPortSerialGps *port, const gchar *trace, MMIfaceModemLocation *self) { mm_iface_modem_location_gps_update (self, trace); } /*****************************************************************************/ /* Location capabilities loading (Location interface) */ MMModemLocationSource mm_shared_quectel_location_load_capabilities_finish (MMIfaceModemLocation *self, GAsyncResult *res, GError **error) { GError *inner_error = NULL; gssize value; value = g_task_propagate_int (G_TASK (res), &inner_error); if (inner_error) { g_propagate_error (error, inner_error); return MM_MODEM_LOCATION_SOURCE_NONE; } return (MMModemLocationSource)value; } static void probe_qgps_ready (MMBaseModem *_self, GAsyncResult *res, GTask *task) { MMSharedQuectel *self; Private *priv; MMModemLocationSource sources; self = MM_SHARED_QUECTEL (g_task_get_source_object (task)); priv = get_private (self); priv->qgps_supported = (!!mm_base_modem_at_command_finish (_self, res, NULL) ? FEATURE_SUPPORTED : FEATURE_NOT_SUPPORTED); mm_obj_dbg (self, "GPS management with +QGPS is %ssupported", priv->qgps_supported == FEATURE_SUPPORTED ? "" : "not "); /* Recover parent sources */ sources = GPOINTER_TO_UINT (g_task_get_task_data (task)); /* Only flag as provided those sources not already provided by the parent */ if (priv->qgps_supported == FEATURE_SUPPORTED) { if (!(sources & MM_MODEM_LOCATION_SOURCE_GPS_NMEA)) priv->provided_sources |= MM_MODEM_LOCATION_SOURCE_GPS_NMEA; if (!(sources & MM_MODEM_LOCATION_SOURCE_GPS_RAW)) priv->provided_sources |= MM_MODEM_LOCATION_SOURCE_GPS_RAW; if (!(sources & MM_MODEM_LOCATION_SOURCE_GPS_UNMANAGED)) priv->provided_sources |= MM_MODEM_LOCATION_SOURCE_GPS_UNMANAGED; sources |= priv->provided_sources; /* Add handler for the NMEA traces in the GPS data port */ mm_port_serial_gps_add_trace_handler (mm_base_modem_peek_port_gps (MM_BASE_MODEM (self)), (MMPortSerialGpsTraceFn)trace_received, self, NULL); } /* So we're done, complete */ g_task_return_int (task, sources); g_object_unref (task); } static void parent_load_capabilities_ready (MMIfaceModemLocation *self, GAsyncResult *res, GTask *task) { Private *priv; MMModemLocationSource sources; GError *error = NULL; priv = get_private (MM_SHARED_QUECTEL (self)); sources = priv->iface_modem_location_parent->load_capabilities_finish (self, res, &error); if (error) { g_task_return_error (task, error); g_object_unref (task); return; } /* Now our own check. If we don't have any GPS port, we're done */ if (!mm_base_modem_peek_port_gps (MM_BASE_MODEM (self))) { mm_obj_dbg (self, "no GPS data port found: no GPS capabilities"); g_task_return_int (task, sources); g_object_unref (task); return; } /* Store parent supported sources in task data */ g_task_set_task_data (task, GUINT_TO_POINTER (sources), NULL); /* Probe QGPS support */ g_assert (priv->qgps_supported == FEATURE_SUPPORT_UNKNOWN); mm_base_modem_at_command (MM_BASE_MODEM (self), "+QGPS=?", 3, TRUE, /* cached */ (GAsyncReadyCallback)probe_qgps_ready, task); } void mm_shared_quectel_location_load_capabilities (MMIfaceModemLocation *_self, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; Private *priv; task = g_task_new (_self, NULL, callback, user_data); priv = get_private (MM_SHARED_QUECTEL (_self)); /* Chain up parent's setup */ priv->iface_modem_location_parent->load_capabilities (_self, (GAsyncReadyCallback)parent_load_capabilities_ready, task); } /*****************************************************************************/ /* Enable location gathering (Location interface) */ /* NOTES: * 1) "+QGPSCFG=\"nmeasrc\",1" will be necessary for getting location data * without the nmea port. * 2) may be necessary to set "+QGPSCFG=\"gpsnmeatype\". * 3) QGPSXTRA=1 is necessary to support XTRA assistance data for * faster GNSS location locks. */ static const MMBaseModemAtCommand gps_startup[] = { { "+QGPSCFG=\"outport\",\"usbnmea\"", 3, FALSE, mm_base_modem_response_processor_no_result_continue }, { "+QGPS=1", 3, FALSE, mm_base_modem_response_processor_no_result_continue }, { "+QGPSXTRA=1", 3, FALSE, mm_base_modem_response_processor_no_result_continue }, { NULL } }; gboolean mm_shared_quectel_enable_location_gathering_finish (MMIfaceModemLocation *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void gps_startup_ready (MMBaseModem *self, GAsyncResult *res, GTask *task) { MMModemLocationSource source; GError *error = NULL; Private *priv; priv = get_private (MM_SHARED_QUECTEL (self)); mm_base_modem_at_sequence_finish (self, res, NULL, &error); if (error) { g_task_return_error (task, error); g_object_unref (task); return; } source = GPOINTER_TO_UINT (g_task_get_task_data (task)); /* Check if the nmea/raw gps port exists and is available */ if (source & (MM_MODEM_LOCATION_SOURCE_GPS_NMEA | MM_MODEM_LOCATION_SOURCE_GPS_RAW)) { MMPortSerialGps *gps_port; gps_port = mm_base_modem_peek_port_gps (MM_BASE_MODEM (self)); if (!gps_port || !mm_port_serial_open (MM_PORT_SERIAL (gps_port), &error)) { if (error) g_task_return_error (task, error); else g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't open raw GPS serial port"); } else { /* GPS port was successfully opened */ priv->enabled_sources |= source; g_task_return_boolean (task, TRUE); } } else { /* No need to open GPS port */ priv->enabled_sources |= source; g_task_return_boolean (task, TRUE); } g_object_unref (task); } static void parent_enable_location_gathering_ready (MMIfaceModemLocation *self, GAsyncResult *res, GTask *task) { GError *error = NULL; Private *priv; priv = get_private (MM_SHARED_QUECTEL (self)); if (!priv->iface_modem_location_parent->enable_location_gathering_finish (self, res, &error)) g_task_return_error (task, error); else g_task_return_boolean (task, TRUE); g_object_unref (task); } void mm_shared_quectel_enable_location_gathering (MMIfaceModemLocation *self, MMModemLocationSource source, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; Private *priv; gboolean start_gps = FALSE; priv = get_private (MM_SHARED_QUECTEL (self)); g_assert (priv->iface_modem_location_parent); g_assert (priv->iface_modem_location_parent->enable_location_gathering); g_assert (priv->iface_modem_location_parent->enable_location_gathering_finish); task = g_task_new (self, NULL, callback, user_data); g_task_set_task_data (task, GUINT_TO_POINTER (source), NULL); /* Check if the source is provided by the parent */ if (!(priv->provided_sources & source)) { priv->iface_modem_location_parent->enable_location_gathering ( self, source, (GAsyncReadyCallback)parent_enable_location_gathering_ready, task); return; } /* Only start GPS engine if not done already */ start_gps = ((source & (MM_MODEM_LOCATION_SOURCE_GPS_NMEA | MM_MODEM_LOCATION_SOURCE_GPS_RAW | MM_MODEM_LOCATION_SOURCE_GPS_UNMANAGED)) && !(priv->enabled_sources & (MM_MODEM_LOCATION_SOURCE_GPS_NMEA | MM_MODEM_LOCATION_SOURCE_GPS_RAW | MM_MODEM_LOCATION_SOURCE_GPS_UNMANAGED))); if (start_gps) { mm_base_modem_at_sequence ( MM_BASE_MODEM (self), gps_startup, NULL, /* response_processor_context */ NULL, /* response_processor_context_free */ (GAsyncReadyCallback)gps_startup_ready, task); return; } /* If the GPS is already running just return */ priv->enabled_sources |= source; g_task_return_boolean (task, TRUE); g_object_unref (task); } /*****************************************************************************/ /* Disable location gathering (Location interface) */ gboolean mm_shared_quectel_disable_location_gathering_finish (MMIfaceModemLocation *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void qgps_end_ready (MMBaseModem *self, GAsyncResult *res, GTask *task) { GError *error = NULL; if (!mm_base_modem_at_command_finish (self, res, &error)) g_task_return_error (task, error); else g_task_return_boolean (task, TRUE); g_object_unref (task); } static void disable_location_gathering_parent_ready (MMIfaceModemLocation *self, GAsyncResult *res, GTask *task) { GError *error = NULL; Private *priv; priv = get_private (MM_SHARED_QUECTEL (self)); if (!priv->iface_modem_location_parent->disable_location_gathering_finish (self, res, &error)) g_task_return_error (task, error); else g_task_return_boolean (task, TRUE); g_object_unref (task); } void mm_shared_quectel_disable_location_gathering (MMIfaceModemLocation *self, MMModemLocationSource source, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; Private *priv; priv = get_private (MM_SHARED_QUECTEL (self)); g_assert (priv->iface_modem_location_parent); task = g_task_new (self, NULL, callback, user_data); priv->enabled_sources &= ~source; /* Pass handling to parent if we don't handle it */ if (!(source & priv->provided_sources)) { /* The step to disable location gathering may not exist */ if (priv->iface_modem_location_parent->disable_location_gathering && priv->iface_modem_location_parent->disable_location_gathering_finish) { priv->iface_modem_location_parent->disable_location_gathering (self, source, (GAsyncReadyCallback)disable_location_gathering_parent_ready, task); return; } g_task_return_boolean (task, TRUE); g_object_unref (task); return; } /* Turn off gps on the modem if the source uses gps, * and there are no other gps sources enabled */ if ((source & (MM_MODEM_LOCATION_SOURCE_GPS_NMEA | MM_MODEM_LOCATION_SOURCE_GPS_RAW | MM_MODEM_LOCATION_SOURCE_GPS_UNMANAGED)) && !(priv->enabled_sources & (MM_MODEM_LOCATION_SOURCE_GPS_NMEA | MM_MODEM_LOCATION_SOURCE_GPS_RAW | MM_MODEM_LOCATION_SOURCE_GPS_UNMANAGED))) { /* Close the data port if we don't need it anymore */ if (source & (MM_MODEM_LOCATION_SOURCE_GPS_RAW | MM_MODEM_LOCATION_SOURCE_GPS_NMEA)) { MMPortSerialGps *gps_port; gps_port = mm_base_modem_peek_port_gps (MM_BASE_MODEM (self)); if (gps_port) mm_port_serial_close (MM_PORT_SERIAL (gps_port)); } mm_base_modem_at_command (MM_BASE_MODEM (self), "+QGPSEND", 3, FALSE, (GAsyncReadyCallback)qgps_end_ready, task); return; } g_task_return_boolean (task, TRUE); g_object_unref (task); } /*****************************************************************************/ /* Check support (Time interface) */ gboolean mm_shared_quectel_time_check_support_finish (MMIfaceModemTime *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void support_cclk_query_ready (MMBaseModem *self, GAsyncResult *res, GTask *task) { /* error never returned */ g_task_return_boolean (task, !!mm_base_modem_at_command_finish (self, res, NULL)); g_object_unref (task); } static void support_cclk_query (GTask *task) { MMBaseModem *self; self = g_task_get_source_object (task); mm_base_modem_at_command (MM_BASE_MODEM (self), "+CCLK?", 3, FALSE, (GAsyncReadyCallback)support_cclk_query_ready, task); } static void ctzu_set_ready (MMBaseModem *self, GAsyncResult *res, GTask *task) { g_autoptr(GError) error = NULL; if (!mm_base_modem_at_command_finish (self, res, &error)) mm_obj_warn (self, "couldn't enable automatic time zone update: %s", error->message); support_cclk_query (task); } static void ctzu_test_ready (MMBaseModem *self, GAsyncResult *res, GTask *task) { g_autoptr(GError) error = NULL; const gchar *response; gboolean supports_disable; gboolean supports_enable; gboolean supports_enable_update_rtc; const gchar *cmd = NULL; /* If CTZU isn't supported, run CCLK right away */ response = mm_base_modem_at_command_finish (self, res, NULL); if (!response) { support_cclk_query (task); return; } if (!mm_quectel_parse_ctzu_test_response (response, self, &supports_disable, &supports_enable, &supports_enable_update_rtc, &error)) { mm_obj_warn (self, "couldn't parse +CTZU test response: %s", error->message); support_cclk_query (task); return; } /* Custom time support check because some Quectel modems (e.g. EC25) require * +CTZU=3 in order to have the CCLK? time reported in localtime, instead of * UTC time. */ if (supports_enable_update_rtc) cmd = "+CTZU=3"; else if (supports_enable) cmd = "+CTZU=1"; if (!cmd) { mm_obj_warn (self, "unknown +CTZU support"); support_cclk_query (task); return; } mm_base_modem_at_command (MM_BASE_MODEM (self), cmd, 3, FALSE, (GAsyncReadyCallback)ctzu_set_ready, task); } void mm_shared_quectel_time_check_support (MMIfaceModemTime *self, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; task = g_task_new (self, NULL, callback, user_data); mm_base_modem_at_command (MM_BASE_MODEM (self), "+CTZU=?", 3, TRUE, /* cached! */ (GAsyncReadyCallback)ctzu_test_ready, task); } /*****************************************************************************/ static void shared_quectel_init (gpointer g_iface) { } GType mm_shared_quectel_get_type (void) { static GType shared_quectel_type = 0; if (!G_UNLIKELY (shared_quectel_type)) { static const GTypeInfo info = { sizeof (MMSharedQuectel), /* class_size */ shared_quectel_init, /* base_init */ NULL, /* base_finalize */ }; shared_quectel_type = g_type_register_static (G_TYPE_INTERFACE, "MMSharedQuectel", &info, 0); g_type_interface_add_prerequisite (shared_quectel_type, MM_TYPE_IFACE_MODEM_FIRMWARE); } return shared_quectel_type; } ModemManager-1.23.4-dev/src/plugins/quectel/mm-shared-quectel.h000066400000000000000000000137731456466623000243400ustar00rootroot00000000000000/* -*- 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) 2018-2020 Aleksander Morgado */ #ifndef MM_SHARED_QUECTEL_H #define MM_SHARED_QUECTEL_H #include #include #define _LIBMM_INSIDE_MM #include #include "mm-broadband-modem.h" #include "mm-iface-modem.h" #include "mm-iface-modem-firmware.h" #include "mm-iface-modem-location.h" #include "mm-iface-modem-time.h" #define MM_TYPE_SHARED_QUECTEL (mm_shared_quectel_get_type ()) #define MM_SHARED_QUECTEL(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_SHARED_QUECTEL, MMSharedQuectel)) #define MM_IS_SHARED_QUECTEL(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_SHARED_QUECTEL)) #define MM_SHARED_QUECTEL_GET_INTERFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE ((obj), MM_TYPE_SHARED_QUECTEL, MMSharedQuectel)) typedef struct _MMSharedQuectel MMSharedQuectel; struct _MMSharedQuectel { GTypeInterface g_iface; MMBroadbandModemClass * (* peek_parent_broadband_modem_class) (MMSharedQuectel *self); MMIfaceModem * (* peek_parent_modem_interface) (MMSharedQuectel *self); MMIfaceModemLocation * (* peek_parent_modem_location_interface) (MMSharedQuectel *self); }; GType mm_shared_quectel_get_type (void); void mm_shared_quectel_setup_ports (MMBroadbandModem *self); void mm_shared_quectel_firmware_load_update_settings (MMIfaceModemFirmware *self, GAsyncReadyCallback callback, gpointer user_data); MMFirmwareUpdateSettings *mm_shared_quectel_firmware_load_update_settings_finish (MMIfaceModemFirmware *self, GAsyncResult *res, GError **error); void mm_shared_quectel_setup_sim_hot_swap (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data); gboolean mm_shared_quectel_setup_sim_hot_swap_finish (MMIfaceModem *self, GAsyncResult *res, GError **error); void mm_shared_quectel_cleanup_sim_hot_swap (MMIfaceModem *self); void mm_shared_quectel_location_load_capabilities (MMIfaceModemLocation *self, GAsyncReadyCallback callback, gpointer user_data); MMModemLocationSource mm_shared_quectel_location_load_capabilities_finish (MMIfaceModemLocation *self, GAsyncResult *res, GError **error); void mm_shared_quectel_enable_location_gathering (MMIfaceModemLocation *self, MMModemLocationSource source, GAsyncReadyCallback callback, gpointer user_data); gboolean mm_shared_quectel_enable_location_gathering_finish (MMIfaceModemLocation *self, GAsyncResult *res, GError **error); void mm_shared_quectel_disable_location_gathering (MMIfaceModemLocation *self, MMModemLocationSource source, GAsyncReadyCallback callback, gpointer user_data); gboolean mm_shared_quectel_disable_location_gathering_finish (MMIfaceModemLocation *self, GAsyncResult *res, GError **error); void mm_shared_quectel_time_check_support (MMIfaceModemTime *self, GAsyncReadyCallback callback, gpointer user_data); gboolean mm_shared_quectel_time_check_support_finish (MMIfaceModemTime *self, GAsyncResult *res, GError **error); #endif /* MM_SHARED_QUECTEL_H */ ModemManager-1.23.4-dev/src/plugins/quectel/tests/000077500000000000000000000000001456466623000220015ustar00rootroot00000000000000ModemManager-1.23.4-dev/src/plugins/quectel/tests/test-modem-helpers-quectel.c000066400000000000000000000071531456466623000273310ustar00rootroot00000000000000/* -*- 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) 2020 Aleksander Morgado */ #include #include #include #include #define _LIBMM_INSIDE_MM #include #include #include "mm-log-test.h" #include "mm-modem-helpers.h" #include "mm-modem-helpers-quectel.h" /*****************************************************************************/ /* Test ^CTZU test responses */ typedef struct { const gchar *response; gboolean expect_supports_disable; gboolean expect_supports_enable; gboolean expect_supports_enable_update_rtc; } TestCtzuResponse; static const TestCtzuResponse test_ctzu_response[] = { { "+CTZU: (0,1)", TRUE, TRUE, FALSE }, { "+CTZU: (0,1,3)", TRUE, TRUE, TRUE }, }; static void common_test_ctzu (const gchar *response, gboolean expect_supports_disable, gboolean expect_supports_enable, gboolean expect_supports_enable_update_rtc) { g_autoptr(GError) error = NULL; gboolean res; gboolean supports_disable = FALSE; gboolean supports_enable = FALSE; gboolean supports_enable_update_rtc = FALSE; res = mm_quectel_parse_ctzu_test_response (response, NULL, &supports_disable, &supports_enable, &supports_enable_update_rtc, &error); g_assert_no_error (error); g_assert (res); g_assert_cmpuint (expect_supports_disable, ==, supports_disable); g_assert_cmpuint (expect_supports_enable, ==, supports_enable); g_assert_cmpuint (expect_supports_enable_update_rtc, ==, supports_enable_update_rtc); } static void test_ctzu (void) { guint i; for (i = 0; i < G_N_ELEMENTS (test_ctzu_response); i++) common_test_ctzu (test_ctzu_response[i].response, test_ctzu_response[i].expect_supports_disable, test_ctzu_response[i].expect_supports_enable, test_ctzu_response[i].expect_supports_enable_update_rtc); } /*****************************************************************************/ /* Test ^FIRMVERSION test responses */ static void test_firmversion (void) { gboolean valid = TRUE; valid = mm_quectel_check_standard_firmware_version_valid ("EM05GFAR07A07M1G_01.016.01.016"); g_assert_cmpuint (valid, ==, TRUE); valid = mm_quectel_check_standard_firmware_version_valid ("EM05GFAR07A07M1G_01.016.00.000"); g_assert_cmpuint (valid, ==, FALSE); } /*****************************************************************************/ int main (int argc, char **argv) { setlocale (LC_ALL, ""); g_test_init (&argc, &argv, NULL); g_test_add_func ("/MM/quectel/ctzu", test_ctzu); g_test_add_func ("/MM/quectel/firmversion", test_firmversion); return g_test_run (); } ModemManager-1.23.4-dev/src/plugins/samsung/000077500000000000000000000000001456466623000206525ustar00rootroot00000000000000ModemManager-1.23.4-dev/src/plugins/samsung/mm-broadband-modem-samsung.c000066400000000000000000000065241456466623000261220ustar00rootroot00000000000000/* -*- 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) 2011 Samsung Electronics, Inc. * Copyright (C) 2012 Google Inc. * Author: Nathan Williams */ #include #include #include #include #include #include #include "ModemManager.h" #include "mm-base-modem-at.h" #include "mm-broadband-modem-samsung.h" #include "mm-broadband-bearer-icera.h" #include "mm-modem-helpers.h" G_DEFINE_TYPE (MMBroadbandModemSamsung, mm_broadband_modem_samsung, MM_TYPE_BROADBAND_MODEM_ICERA) /*****************************************************************************/ /* Setup ports (Broadband modem class) */ static void setup_ports (MMBroadbandModem *self) { MMPortSerialAt *ports[2]; guint i; /* Call parent's setup ports first always */ MM_BROADBAND_MODEM_CLASS (mm_broadband_modem_samsung_parent_class)->setup_ports (self); ports[0] = mm_base_modem_peek_port_primary (MM_BASE_MODEM (self)); ports[1] = mm_base_modem_peek_port_secondary (MM_BASE_MODEM (self)); /* Configure AT ports */ for (i = 0; i < G_N_ELEMENTS (ports); i++) { if (!ports[i]) continue; g_object_set (ports[i], MM_PORT_SERIAL_SEND_DELAY, (guint64) 0, NULL); } } /*****************************************************************************/ MMBroadbandModemSamsung * mm_broadband_modem_samsung_new (const gchar *device, const gchar *physdev, const gchar **drivers, const gchar *plugin, guint16 vendor_id, guint16 product_id) { return g_object_new (MM_TYPE_BROADBAND_MODEM_SAMSUNG, MM_BASE_MODEM_DEVICE, device, MM_BASE_MODEM_PHYSDEV, physdev, MM_BASE_MODEM_DRIVERS, drivers, MM_BASE_MODEM_PLUGIN, plugin, MM_BASE_MODEM_VENDOR_ID, vendor_id, MM_BASE_MODEM_PRODUCT_ID, product_id, /* Generic bearer (AT) and Icera bearer (NET) supported */ MM_BASE_MODEM_DATA_NET_SUPPORTED, TRUE, MM_BASE_MODEM_DATA_TTY_SUPPORTED, TRUE, /* We want to use DHCP always! */ MM_BROADBAND_MODEM_ICERA_DEFAULT_IP_METHOD, MM_BEARER_IP_METHOD_DHCP, NULL); } static void mm_broadband_modem_samsung_init (MMBroadbandModemSamsung *self) { } static void mm_broadband_modem_samsung_class_init (MMBroadbandModemSamsungClass *klass) { MMBroadbandModemClass *broadband_modem_class = MM_BROADBAND_MODEM_CLASS (klass); broadband_modem_class->setup_ports = setup_ports; } ModemManager-1.23.4-dev/src/plugins/samsung/mm-broadband-modem-samsung.h000066400000000000000000000046461456466623000261320ustar00rootroot00000000000000/* -*- 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) 2011 Samsung Electronics, Inc. * Copyright (C) 2012 Google Inc. * Author: Nathan Williams */ #ifndef MM_BROADBAND_MODEM_SAMSUNG_H #define MM_BROADBAND_MODEM_SAMSUNG_H #include "mm-broadband-modem-icera.h" #define MM_TYPE_BROADBAND_MODEM_SAMSUNG (mm_broadband_modem_samsung_get_type ()) #define MM_BROADBAND_MODEM_SAMSUNG(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_BROADBAND_MODEM_SAMSUNG, MMBroadbandModemSamsung)) #define MM_BROADBAND_MODEM_SAMSUNG_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_BROADBAND_MODEM_SAMSUNG, MMBroadbandModemSamsungClass)) #define MM_IS_BROADBAND_MODEM_SAMSUNG(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_BROADBAND_MODEM_SAMSUNG)) #define MM_IS_BROADBAND_MODEM_SAMSUNG_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_BROADBAND_MODEM_SAMSUNG)) #define MM_BROADBAND_MODEM_SAMSUNG_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_BROADBAND_MODEM_SAMSUNG, MMBroadbandModemSamsungClass)) typedef struct _MMBroadbandModemSamsung MMBroadbandModemSamsung; typedef struct _MMBroadbandModemSamsungClass MMBroadbandModemSamsungClass; struct _MMBroadbandModemSamsung { MMBroadbandModemIcera parent; }; struct _MMBroadbandModemSamsungClass{ MMBroadbandModemIceraClass parent; }; GType mm_broadband_modem_samsung_get_type (void); MMBroadbandModemSamsung *mm_broadband_modem_samsung_new (const gchar *device, const gchar *physdev, const gchar **drivers, const gchar *plugin, guint16 vendor_id, guint16 product_id); #endif /* MM_BROADBAND_MODEM_SAMSUNG_H */ ModemManager-1.23.4-dev/src/plugins/samsung/mm-plugin-samsung.c000066400000000000000000000060221456466623000243760ustar00rootroot00000000000000/* -*- 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. * * 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. * * Copyright (C) 2011 Samsung Electronics, Inc. * Copyright (C) 2012 Google Inc. * Author: Nathan Williams */ #include #include #include "mm-plugin-common.h" #include "mm-private-boxed-types.h" #include "mm-broadband-modem-samsung.h" #define MM_TYPE_PLUGIN_SAMSUNG mm_plugin_samsung_get_type () MM_DEFINE_PLUGIN (SAMSUNG, samsung, Samsung) /*****************************************************************************/ static MMBaseModem * create_modem (MMPlugin *self, const gchar *uid, const gchar *physdev, const gchar **drivers, guint16 vendor, guint16 product, guint16 subsystem_vendor, GList *probes, GError **error) { return MM_BASE_MODEM (mm_broadband_modem_samsung_new (uid, physdev, drivers, mm_plugin_get_name (self), vendor, product)); } /*****************************************************************************/ MM_PLUGIN_NAMED_CREATOR_SCOPE MMPlugin * mm_plugin_create_samsung (void) { static const gchar *subsystems[] = { "tty", "net", NULL }; static const mm_uint16_pair products[] = { { 0x04e8, 0x6872 }, { 0x04e8, 0x6906 }, { 0, 0 } }; return MM_PLUGIN ( g_object_new (MM_TYPE_PLUGIN_SAMSUNG, MM_PLUGIN_NAME, MM_MODULE_NAME, MM_PLUGIN_ALLOWED_SUBSYSTEMS, subsystems, MM_PLUGIN_ALLOWED_PRODUCT_IDS, products, MM_PLUGIN_ALLOWED_AT, TRUE, MM_PLUGIN_SEND_DELAY, (guint64) 0, NULL)); } static void mm_plugin_samsung_init (MMPluginSamsung *self) { } static void mm_plugin_samsung_class_init (MMPluginSamsungClass *klass) { MMPluginClass *plugin_class = MM_PLUGIN_CLASS (klass); plugin_class->create_modem = create_modem; } ModemManager-1.23.4-dev/src/plugins/sierra/000077500000000000000000000000001456466623000204625ustar00rootroot00000000000000ModemManager-1.23.4-dev/src/plugins/sierra/77-mm-sierra.rules000066400000000000000000000062671456466623000237000ustar00rootroot00000000000000 # do not edit this file, it will be overwritten on update ACTION!="add|change|move|bind", GOTO="mm_sierra_end" SUBSYSTEMS=="usb", ATTRS{idVendor}=="1199", GOTO="mm_sierra_generic" SUBSYSTEMS=="usb", ATTRS{idVendor}=="1519", GOTO="mm_sierra_comneon" GOTO="mm_sierra_end" LABEL="mm_sierra_generic" SUBSYSTEMS=="usb", ATTRS{bInterfaceNumber}=="?*", ENV{.MM_USBIFNUM}="$attr{bInterfaceNumber}" # Netgear AC341U: enable connection status polling explicitly ATTRS{idVendor}=="1199", ATTRS{idProduct}=="9057", ENV{ID_MM_QMI_CONNECTION_STATUS_POLLING_ENABLE}="1" # EM7345: disable CPOL based features ATTRS{idVendor}=="1199", ATTRS{idProduct}=="a001", ENV{ID_MM_PREFERRED_NETWORKS_CPOL_DISABLED}="1" # MC74XX port hints # if 03: primary port # if 02: raw NMEA port # if 00: diag/qcdm port ATTRS{idVendor}=="1199", ATTRS{idProduct}=="9071", ENV{.MM_USBIFNUM}=="03", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1" ATTRS{idVendor}=="1199", ATTRS{idProduct}=="9071", ENV{.MM_USBIFNUM}=="02", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_GPS}="1" ATTRS{idVendor}=="1199", ATTRS{idProduct}=="9071", ENV{.MM_USBIFNUM}=="00", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_QCDM}="1" # EM7565 port hints # if 03: primary port # if 02: raw NMEA port # if 00: diag/qcdm port ATTRS{idVendor}=="1199", ATTRS{idProduct}=="9091", ENV{.MM_USBIFNUM}=="03", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1" ATTRS{idVendor}=="1199", ATTRS{idProduct}=="9091", ENV{.MM_USBIFNUM}=="02", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_GPS}="1" ATTRS{idVendor}=="1199", ATTRS{idProduct}=="9091", ENV{.MM_USBIFNUM}=="00", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_QCDM}="1" # EM7455 port hints # if 03: primary port # if 02: raw NMEA port # if 00: diag/qcdm port ATTRS{idVendor}=="1199", ATTRS{idProduct}=="9079", ENV{.MM_USBIFNUM}=="03", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1" ATTRS{idVendor}=="1199", ATTRS{idProduct}=="9079", ENV{.MM_USBIFNUM}=="02", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_GPS}="1" ATTRS{idVendor}=="1199", ATTRS{idProduct}=="9079", ENV{.MM_USBIFNUM}=="00", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_QCDM}="1" # RC7611 port hints # if 03: primary port # if 02: raw NMEA port # if 00: diag/qcdm port ATTRS{idVendor}=="1199", ATTRS{idProduct}=="68c0", ENV{.MM_USBIFNUM}=="03", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1" ATTRS{idVendor}=="1199", ATTRS{idProduct}=="68c0", ENV{.MM_USBIFNUM}=="02", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_GPS}="1" ATTRS{idVendor}=="1199", ATTRS{idProduct}=="68c0", ENV{.MM_USBIFNUM}=="00", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_QCDM}="1" # HL7812 port hints # if 04: raw NMEA port # if 02: at_ppp port # if 00: primary port ATTRS{idVendor}=="1199", ATTRS{idProduct}=="c001", ENV{.MM_USBIFNUM}=="04", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_GPS}="1" ATTRS{idVendor}=="1199", ATTRS{idProduct}=="c001", ENV{.MM_USBIFNUM}=="02", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PPP}="1" ATTRS{idVendor}=="1199", ATTRS{idProduct}=="c001", ENV{.MM_USBIFNUM}=="00", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1" GOTO="mm_sierra_end" LABEL="mm_sierra_comneon" # GL7600: disable CPOL based features ATTRS{idVendor}=="1519", ATTRS{idProduct}=="0443", ENV{ID_MM_PREFERRED_NETWORKS_CPOL_DISABLED}="1" LABEL="mm_sierra_end" ModemManager-1.23.4-dev/src/plugins/sierra/mm-broadband-bearer-sierra.c000066400000000000000000000532711456466623000257020ustar00rootroot00000000000000/* -*- 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 - 2012 Red Hat, Inc. * Copyright (C) 2012 Lanedo GmbH */ #include #include #include #include #include #include #include #define _LIBMM_INSIDE_MM #include #include "mm-base-modem-at.h" #include "mm-broadband-bearer-sierra.h" #include "mm-log-object.h" #include "mm-modem-helpers.h" #include "mm-modem-helpers-sierra.h" G_DEFINE_TYPE (MMBroadbandBearerSierra, mm_broadband_bearer_sierra, MM_TYPE_BROADBAND_BEARER); struct _MMBroadbandBearerSierraPrivate { gboolean is_icera; }; enum { PROP_0, PROP_IS_ICERA, PROP_LAST }; /*****************************************************************************/ /* Connection status monitoring */ static MMBearerConnectionStatus load_connection_status_finish (MMBaseBearer *bearer, GAsyncResult *res, GError **error) { GError *inner_error = NULL; gssize value; value = g_task_propagate_int (G_TASK (res), &inner_error); if (inner_error) { g_propagate_error (error, inner_error); return MM_BEARER_CONNECTION_STATUS_UNKNOWN; } return (MMBearerConnectionStatus)value; } static void scact_periodic_query_ready (MMBaseModem *modem, GAsyncResult *res, GTask *task) { const gchar *response; GError *error = NULL; GList *pdp_active_list = NULL; GList *l; MMBearerConnectionStatus status = MM_BEARER_CONNECTION_STATUS_UNKNOWN; guint cid; cid = GPOINTER_TO_UINT (g_task_get_task_data (task)); response = mm_base_modem_at_command_finish (modem, res, &error); if (response) pdp_active_list = mm_sierra_parse_scact_read_response (response, &error); if (error) { g_assert (!pdp_active_list); g_prefix_error (&error, "Couldn't check current list of active PDP contexts: "); g_task_return_error (task, error); g_object_unref (task); return; } for (l = pdp_active_list; l; l = g_list_next (l)) { MM3gppPdpContextActive *pdp_active; /* We look for the specific CID */ pdp_active = (MM3gppPdpContextActive *)(l->data); if (pdp_active->cid == cid) { status = (pdp_active->active ? MM_BEARER_CONNECTION_STATUS_CONNECTED : MM_BEARER_CONNECTION_STATUS_DISCONNECTED); break; } } mm_3gpp_pdp_context_active_list_free (pdp_active_list); /* PDP context not found? This shouldn't happen, error out */ if (status == MM_BEARER_CONNECTION_STATUS_UNKNOWN) g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "PDP context not found in the known contexts list"); else g_task_return_int (task, (gssize) status); g_object_unref (task); } static void load_connection_status (MMBaseBearer *self, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; MMBaseModem *modem = NULL; MMPortSerialAt *port; gint profile_id; task = g_task_new (self, NULL, callback, user_data); g_object_get (MM_BASE_BEARER (self), MM_BASE_BEARER_MODEM, &modem, NULL); /* If CID not defined, error out */ profile_id = mm_base_bearer_get_profile_id (self); if (profile_id == MM_3GPP_PROFILE_ID_UNKNOWN) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't load connection status: profile id not defined"); g_object_unref (task); goto out; } g_task_set_task_data (task, GUINT_TO_POINTER ((guint)profile_id), NULL); /* If no control port available, error out */ port = mm_base_modem_peek_best_at_port (modem, NULL); if (!port) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "Couldn't load connection status: no control port available"); g_object_unref (task); goto out; } mm_base_modem_at_command_full (MM_BASE_MODEM (modem), port, "!SCACT?", 3, FALSE, /* allow cached */ FALSE, /* raw */ NULL, /* cancellable */ (GAsyncReadyCallback) scact_periodic_query_ready, task); out: g_clear_object (&modem); } /*****************************************************************************/ /* 3GPP Dialing (sub-step of the 3GPP Connection sequence) */ typedef enum { DIAL_3GPP_STEP_FIRST, DIAL_3GPP_STEP_PS_ATTACH, DIAL_3GPP_STEP_AUTHENTICATE, DIAL_3GPP_STEP_CONNECT, DIAL_3GPP_STEP_LAST } Dial3gppStep; typedef struct { MMBaseModem *modem; MMPortSerialAt *primary; guint cid; MMPort *data; Dial3gppStep step; } Dial3gppContext; static void dial_3gpp_context_free (Dial3gppContext *ctx) { if (ctx->data) g_object_unref (ctx->data); g_object_unref (ctx->primary); g_object_unref (ctx->modem); g_slice_free (Dial3gppContext, ctx); } static MMPort * dial_3gpp_finish (MMBroadbandBearer *self, GAsyncResult *res, GError **error) { return g_task_propagate_pointer (G_TASK (res), error); } static void dial_3gpp_context_step (GTask *task); static void parent_dial_3gpp_ready (MMBroadbandBearer *self, GAsyncResult *res, GTask *task) { Dial3gppContext *ctx; GError *error = NULL; ctx = g_task_get_task_data (task); ctx->data = MM_BROADBAND_BEARER_CLASS (mm_broadband_bearer_sierra_parent_class)->dial_3gpp_finish (self, res, &error); if (!ctx->data) { g_task_return_error (task, error); g_object_unref (task); return; } /* Go on */ ctx->step++; dial_3gpp_context_step (task); } static void scact_ready (MMBaseModem *modem, GAsyncResult *res, GTask *task) { Dial3gppContext *ctx; GError *error = NULL; ctx = g_task_get_task_data (task); if (!mm_base_modem_at_command_full_finish (modem, res, &error)) { g_task_return_error (task, error); g_object_unref (task); return; } /* Go on */ ctx->step++; dial_3gpp_context_step (task); } static void authenticate_ready (MMBaseModem *modem, GAsyncResult *res, GTask *task) { Dial3gppContext *ctx; GError *error = NULL; ctx = g_task_get_task_data (task); if (!mm_base_modem_at_command_full_finish (modem, res, &error)) { g_task_return_error (task, error); g_object_unref (task); return; } /* Go on */ ctx->step++; dial_3gpp_context_step (task); } static void cgatt_ready (MMBaseModem *modem, GAsyncResult *res, GTask *task) { Dial3gppContext *ctx; GError *error = NULL; ctx = g_task_get_task_data (task); if (!mm_base_modem_at_command_full_finish (modem, res, &error)) { g_task_return_error (task, error); g_object_unref (task); return; } /* Go on */ ctx->step++; dial_3gpp_context_step (task); } static void dial_3gpp_context_step (GTask *task) { MMBroadbandBearerSierra *self; Dial3gppContext *ctx; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); if (g_task_return_error_if_cancelled (task)) { g_object_unref (task); return; } switch (ctx->step) { case DIAL_3GPP_STEP_FIRST: ctx->step++; /* fall through */ case DIAL_3GPP_STEP_PS_ATTACH: mm_base_modem_at_command_full (ctx->modem, ctx->primary, "+CGATT=1", 10, FALSE, FALSE, /* raw */ NULL, /* cancellable */ (GAsyncReadyCallback)cgatt_ready, task); return; case DIAL_3GPP_STEP_AUTHENTICATE: if (!MM_IS_PORT_SERIAL_AT (ctx->data)) { gchar *command; const gchar *user; const gchar *password; MMBearerAllowedAuth allowed_auth; user = mm_bearer_properties_get_user (mm_base_bearer_peek_config (MM_BASE_BEARER (self))); password = mm_bearer_properties_get_password (mm_base_bearer_peek_config (MM_BASE_BEARER (self))); allowed_auth = mm_bearer_properties_get_allowed_auth (mm_base_bearer_peek_config (MM_BASE_BEARER (self))); if (!user || !password || allowed_auth == MM_BEARER_ALLOWED_AUTH_NONE) { mm_obj_dbg (self, "not using authentication"); if (self->priv->is_icera) command = g_strdup_printf ("%%IPDPCFG=%d,0,0,\"\",\"\"", ctx->cid); else command = g_strdup_printf ("$QCPDPP=%d,0", ctx->cid); } else { gchar *quoted_user; gchar *quoted_password; guint sierra_auth; if (allowed_auth == MM_BEARER_ALLOWED_AUTH_UNKNOWN) { mm_obj_dbg (self, "using default (CHAP) authentication method"); sierra_auth = 2; } else if (allowed_auth & MM_BEARER_ALLOWED_AUTH_CHAP) { mm_obj_dbg (self, "using CHAP authentication method"); sierra_auth = 2; } else if (allowed_auth & MM_BEARER_ALLOWED_AUTH_PAP) { mm_obj_dbg (self, "using PAP authentication method"); sierra_auth = 1; } else { gchar *str; str = mm_bearer_allowed_auth_build_string_from_mask (allowed_auth); g_task_return_new_error ( task, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "Cannot use any of the specified authentication methods (%s)", str); g_free (str); g_object_unref (task); return; } quoted_user = mm_port_serial_at_quote_string (user); quoted_password = mm_port_serial_at_quote_string (password); if (self->priv->is_icera) { command = g_strdup_printf ("%%IPDPCFG=%d,0,%u,%s,%s", ctx->cid, sierra_auth, quoted_user, quoted_password); } else { /* Yes, password comes first... */ command = g_strdup_printf ("$QCPDPP=%d,%u,%s,%s", ctx->cid, sierra_auth, quoted_password, quoted_user); } g_free (quoted_user); g_free (quoted_password); } mm_base_modem_at_command_full (ctx->modem, ctx->primary, command, 3, FALSE, FALSE, /* raw */ NULL, /* cancellable */ (GAsyncReadyCallback)authenticate_ready, task); g_free (command); return; } ctx->step++; /* fall through */ case DIAL_3GPP_STEP_CONNECT: /* We need a net or AT data port */ ctx->data = mm_base_modem_get_best_data_port (ctx->modem, MM_PORT_TYPE_NET); if (ctx->data) { gchar *command; command = g_strdup_printf ("!SCACT=1,%d", ctx->cid); mm_base_modem_at_command_full (ctx->modem, ctx->primary, command, MM_BASE_BEARER_DEFAULT_CONNECTION_TIMEOUT, FALSE, FALSE, /* raw */ NULL, /* cancellable */ (GAsyncReadyCallback)scact_ready, task); g_free (command); return; } /* Chain up parent's dialling if we don't have a net port */ MM_BROADBAND_BEARER_CLASS (mm_broadband_bearer_sierra_parent_class)->dial_3gpp ( MM_BROADBAND_BEARER (self), ctx->modem, ctx->primary, ctx->cid, g_task_get_cancellable (task), (GAsyncReadyCallback)parent_dial_3gpp_ready, task); return; case DIAL_3GPP_STEP_LAST: g_task_return_pointer (task, g_object_ref (ctx->data), g_object_unref); g_object_unref (task); return; default: g_assert_not_reached (); } } static void dial_3gpp (MMBroadbandBearer *self, MMBaseModem *modem, MMPortSerialAt *primary, guint cid, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { Dial3gppContext *ctx; GTask *task; g_assert (primary != NULL); ctx = g_slice_new0 (Dial3gppContext); ctx->modem = g_object_ref (modem); ctx->primary = g_object_ref (primary); ctx->cid = cid; ctx->step = DIAL_3GPP_STEP_FIRST; task = g_task_new (self, cancellable, callback, user_data); g_task_set_task_data (task, ctx, (GDestroyNotify)dial_3gpp_context_free); dial_3gpp_context_step (task); } /*****************************************************************************/ /* 3GPP disconnect */ static gboolean disconnect_3gpp_finish (MMBroadbandBearer *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void parent_disconnect_3gpp_ready (MMBroadbandBearer *self, GAsyncResult *res, GTask *task) { GError *error = NULL; if (!MM_BROADBAND_BEARER_CLASS (mm_broadband_bearer_sierra_parent_class)->disconnect_3gpp_finish (self, res, &error)) { mm_obj_dbg (self, "parent disconnection failed (not fatal): %s", error->message); g_error_free (error); } g_task_return_boolean (task, TRUE); g_object_unref (task); } static void disconnect_scact_ready (MMBaseModem *modem, GAsyncResult *res, GTask *task) { MMBroadbandBearerSierra *self; GError *error = NULL; self = g_task_get_source_object (task); /* Ignore errors for now */ mm_base_modem_at_command_full_finish (modem, res, &error); if (error) { mm_obj_dbg (self, "disconnection failed (not fatal): %s", error->message); g_error_free (error); } g_task_return_boolean (task, TRUE); g_object_unref (task); } static void disconnect_3gpp (MMBroadbandBearer *self, MMBroadbandModem *modem, MMPortSerialAt *primary, MMPortSerialAt *secondary, MMPort *data, guint cid, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; g_assert (primary != NULL); task = g_task_new (self, NULL, callback, user_data); if (!MM_IS_PORT_SERIAL_AT (data)) { gchar *command; /* Use specific CID */ command = g_strdup_printf ("!SCACT=0,%u", cid); mm_base_modem_at_command_full (MM_BASE_MODEM (modem), primary, command, MM_BASE_BEARER_DEFAULT_DISCONNECTION_TIMEOUT, FALSE, FALSE, /* raw */ NULL, /* cancellable */ (GAsyncReadyCallback)disconnect_scact_ready, task); g_free (command); return; } /* Chain up parent's disconnection if we don't have a net port */ MM_BROADBAND_BEARER_CLASS (mm_broadband_bearer_sierra_parent_class)->disconnect_3gpp ( self, modem, primary, secondary, data, cid, (GAsyncReadyCallback)parent_disconnect_3gpp_ready, task); } /*****************************************************************************/ #define MM_BROADBAND_BEARER_SIERRA_IS_ICERA "is-icera" MMBaseBearer * mm_broadband_bearer_sierra_new_finish (GAsyncResult *res, GError **error) { GObject *bearer; GObject *source; source = g_async_result_get_source_object (res); bearer = g_async_initable_new_finish (G_ASYNC_INITABLE (source), res, error); g_object_unref (source); if (!bearer) return NULL; /* Only export valid bearers */ mm_base_bearer_export (MM_BASE_BEARER (bearer)); return MM_BASE_BEARER (bearer); } void mm_broadband_bearer_sierra_new (MMBroadbandModem *modem, MMBearerProperties *config, gboolean is_icera, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { g_async_initable_new_async ( MM_TYPE_BROADBAND_BEARER_SIERRA, G_PRIORITY_DEFAULT, cancellable, callback, user_data, MM_BASE_BEARER_MODEM, modem, MM_BASE_BEARER_CONFIG, config, MM_BROADBAND_BEARER_SIERRA_IS_ICERA, is_icera, NULL); } static void set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { MMBroadbandBearerSierra *self = MM_BROADBAND_BEARER_SIERRA (object); switch (prop_id) { case PROP_IS_ICERA: self->priv->is_icera = 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) { MMBroadbandBearerSierra *self = MM_BROADBAND_BEARER_SIERRA (object); switch (prop_id) { case PROP_IS_ICERA: g_value_set_boolean (value, self->priv->is_icera); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void mm_broadband_bearer_sierra_init (MMBroadbandBearerSierra *self) { /* Initialize private data */ self->priv = G_TYPE_INSTANCE_GET_PRIVATE ((self), MM_TYPE_BROADBAND_BEARER_SIERRA, MMBroadbandBearerSierraPrivate); } static void mm_broadband_bearer_sierra_class_init (MMBroadbandBearerSierraClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); MMBaseBearerClass *base_bearer_class = MM_BASE_BEARER_CLASS (klass); MMBroadbandBearerClass *broadband_bearer_class = MM_BROADBAND_BEARER_CLASS (klass); g_type_class_add_private (object_class, sizeof (MMBroadbandBearerSierraPrivate)); object_class->set_property = set_property; object_class->get_property = get_property; base_bearer_class->load_connection_status = load_connection_status; base_bearer_class->load_connection_status_finish = load_connection_status_finish; #if defined WITH_SUSPEND_RESUME base_bearer_class->reload_connection_status = load_connection_status; base_bearer_class->reload_connection_status_finish = load_connection_status_finish; #endif broadband_bearer_class->dial_3gpp = dial_3gpp; broadband_bearer_class->dial_3gpp_finish = dial_3gpp_finish; broadband_bearer_class->disconnect_3gpp = disconnect_3gpp; broadband_bearer_class->disconnect_3gpp_finish = disconnect_3gpp_finish; g_object_class_install_property (object_class, PROP_IS_ICERA, g_param_spec_boolean (MM_BROADBAND_BEARER_SIERRA_IS_ICERA, "IsIcera", "Whether the modem uses Icera commands or not.", FALSE, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); } ModemManager-1.23.4-dev/src/plugins/sierra/mm-broadband-bearer-sierra.h000066400000000000000000000055201456466623000257010ustar00rootroot00000000000000/* -*- 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 - 2012 Red Hat, Inc. * Copyright (C) 2012 Lanedo GmbH */ #ifndef MM_BROADBAND_BEARER_SIERRA_H #define MM_BROADBAND_BEARER_SIERRA_H #include #include #define _LIBMM_INSIDE_MM #include #include "mm-broadband-bearer.h" #include "mm-broadband-modem-sierra.h" #define MM_TYPE_BROADBAND_BEARER_SIERRA (mm_broadband_bearer_sierra_get_type ()) #define MM_BROADBAND_BEARER_SIERRA(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_BROADBAND_BEARER_SIERRA, MMBroadbandBearerSierra)) #define MM_BROADBAND_BEARER_SIERRA_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_BROADBAND_BEARER_SIERRA, MMBroadbandBearerSierraClass)) #define MM_IS_BROADBAND_BEARER_SIERRA(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_BROADBAND_BEARER_SIERRA)) #define MM_IS_BROADBAND_BEARER_SIERRA_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_BROADBAND_BEARER_SIERRA)) #define MM_BROADBAND_BEARER_SIERRA_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_BROADBAND_BEARER_SIERRA, MMBroadbandBearerSierraClass)) typedef struct _MMBroadbandBearerSierra MMBroadbandBearerSierra; typedef struct _MMBroadbandBearerSierraClass MMBroadbandBearerSierraClass; typedef struct _MMBroadbandBearerSierraPrivate MMBroadbandBearerSierraPrivate; struct _MMBroadbandBearerSierra { MMBroadbandBearer parent; MMBroadbandBearerSierraPrivate *priv; }; struct _MMBroadbandBearerSierraClass { MMBroadbandBearerClass parent; }; GType mm_broadband_bearer_sierra_get_type (void); /* Default 3GPP bearer creation implementation */ void mm_broadband_bearer_sierra_new (MMBroadbandModem *modem, MMBearerProperties *config, gboolean is_icera, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); MMBaseBearer *mm_broadband_bearer_sierra_new_finish (GAsyncResult *res, GError **error); #endif /* MM_BROADBAND_BEARER_SIERRA_H */ ModemManager-1.23.4-dev/src/plugins/sierra/mm-broadband-modem-sierra-icera.c000066400000000000000000000121671456466623000266230ustar00rootroot00000000000000/* -*- 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 - 2012 Red Hat, Inc. * Copyright (C) 2012 Lanedo GmbH */ #include #include #include #include #include #include #include "ModemManager.h" #include "mm-broadband-modem-sierra-icera.h" #include "mm-iface-modem.h" #include "mm-modem-helpers.h" #include "mm-log-object.h" #include "mm-common-sierra.h" #include "mm-broadband-bearer-sierra.h" static void iface_modem_init (MMIfaceModem *iface); G_DEFINE_TYPE_EXTENDED (MMBroadbandModemSierraIcera, mm_broadband_modem_sierra_icera, MM_TYPE_BROADBAND_MODEM_ICERA, 0, G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM, iface_modem_init)) /*****************************************************************************/ /* Create Bearer (Modem interface) */ static MMBaseBearer * modem_create_bearer_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { return g_task_propagate_pointer (G_TASK (res), error); } static void broadband_bearer_sierra_new_ready (GObject *source, GAsyncResult *res, GTask *task) { MMBaseBearer *bearer = NULL; GError *error = NULL; bearer = mm_broadband_bearer_sierra_new_finish (res, &error); if (!bearer) g_task_return_error (task, error); else g_task_return_pointer (task, bearer, g_object_unref); g_object_unref (task); } static void modem_create_bearer (MMIfaceModem *self, MMBearerProperties *properties, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; task = g_task_new (self, NULL, callback, user_data); mm_obj_dbg (self, "creating sierra bearer..."); mm_broadband_bearer_sierra_new (MM_BROADBAND_MODEM (self), properties, TRUE, /* is_icera */ NULL, /* cancellable */ (GAsyncReadyCallback)broadband_bearer_sierra_new_ready, task); } /*****************************************************************************/ /* Setup ports (Broadband modem class) */ static void setup_ports (MMBroadbandModem *self) { /* Call parent's setup ports first always */ MM_BROADBAND_MODEM_CLASS (mm_broadband_modem_sierra_icera_parent_class)->setup_ports (self); mm_common_sierra_setup_ports (self); } /*****************************************************************************/ MMBroadbandModemSierraIcera * mm_broadband_modem_sierra_icera_new (const gchar *device, const gchar *physdev, const gchar **drivers, const gchar *plugin, guint16 vendor_id, guint16 product_id) { return g_object_new (MM_TYPE_BROADBAND_MODEM_SIERRA_ICERA, MM_BASE_MODEM_DEVICE, device, MM_BASE_MODEM_PHYSDEV, physdev, MM_BASE_MODEM_DRIVERS, drivers, MM_BASE_MODEM_PLUGIN, plugin, MM_BASE_MODEM_VENDOR_ID, vendor_id, MM_BASE_MODEM_PRODUCT_ID, product_id, /* Sierra bearer supports both NET and TTY */ MM_BASE_MODEM_DATA_NET_SUPPORTED, TRUE, MM_BASE_MODEM_DATA_TTY_SUPPORTED, TRUE, NULL); } static void mm_broadband_modem_sierra_icera_init (MMBroadbandModemSierraIcera *self) { } static void iface_modem_init (MMIfaceModem *iface) { mm_common_sierra_peek_parent_interfaces (iface); iface->load_power_state = mm_common_sierra_load_power_state; iface->load_power_state_finish = mm_common_sierra_load_power_state_finish; iface->modem_power_up = mm_common_sierra_modem_power_up; iface->modem_power_up_finish = mm_common_sierra_modem_power_up_finish; iface->create_sim = mm_common_sierra_create_sim; iface->create_sim_finish = mm_common_sierra_create_sim_finish; iface->create_bearer = modem_create_bearer; iface->create_bearer_finish = modem_create_bearer_finish; } static void mm_broadband_modem_sierra_icera_class_init (MMBroadbandModemSierraIceraClass *klass) { MMBroadbandModemClass *broadband_modem_class = MM_BROADBAND_MODEM_CLASS (klass); broadband_modem_class->setup_ports = setup_ports; } ModemManager-1.23.4-dev/src/plugins/sierra/mm-broadband-modem-sierra-icera.h000066400000000000000000000051121456466623000266200ustar00rootroot00000000000000/* -*- 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 - 2012 Red Hat, Inc. * Copyright (C) 2012 Lanedo GmbH */ #ifndef MM_BROADBAND_MODEM_SIERRA_ICERA_H #define MM_BROADBAND_MODEM_SIERRA_ICERA_H #include "mm-broadband-modem-icera.h" #define MM_TYPE_BROADBAND_MODEM_SIERRA_ICERA (mm_broadband_modem_sierra_icera_get_type ()) #define MM_BROADBAND_MODEM_SIERRA_ICERA(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_BROADBAND_MODEM_SIERRA_ICERA, MMBroadbandModemSierraIcera)) #define MM_BROADBAND_MODEM_SIERRA_ICERA_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_BROADBAND_MODEM_SIERRA_ICERA, MMBroadbandModemSierraIceraClass)) #define MM_IS_BROADBAND_MODEM_SIERRA_ICERA(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_BROADBAND_MODEM_SIERRA_ICERA)) #define MM_IS_BROADBAND_MODEM_SIERRA_ICERA_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_BROADBAND_MODEM_SIERRA_ICERA)) #define MM_BROADBAND_MODEM_SIERRA_ICERA_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_BROADBAND_MODEM_SIERRA_ICERA, MMBroadbandModemSierraIceraClass)) typedef struct _MMBroadbandModemSierraIcera MMBroadbandModemSierraIcera; typedef struct _MMBroadbandModemSierraIceraClass MMBroadbandModemSierraIceraClass; struct _MMBroadbandModemSierraIcera { MMBroadbandModemIcera parent; }; struct _MMBroadbandModemSierraIceraClass { MMBroadbandModemIceraClass parent; }; GType mm_broadband_modem_sierra_icera_get_type (void); MMBroadbandModemSierraIcera *mm_broadband_modem_sierra_icera_new (const gchar *device, const gchar *physdev, const gchar **drivers, const gchar *plugin, guint16 vendor_id, guint16 product_id); #endif /* MM_BROADBAND_MODEM_SIERRA_ICERA_H */ ModemManager-1.23.4-dev/src/plugins/sierra/mm-broadband-modem-sierra.c000066400000000000000000001774221456466623000255500ustar00rootroot00000000000000/* -*- 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 - 2012 Red Hat, Inc. * Copyright (C) 2012 Lanedo GmbH */ #include #include #include #include #include #include #include "ModemManager.h" #include "mm-broadband-modem-sierra.h" #include "mm-base-modem-at.h" #include "mm-log-object.h" #include "mm-modem-helpers.h" #include "mm-common-helpers.h" #include "mm-errors-types.h" #include "mm-iface-modem.h" #include "mm-iface-modem-3gpp.h" #include "mm-iface-modem-cdma.h" #include "mm-iface-modem-time.h" #include "mm-common-sierra.h" #include "mm-broadband-bearer-sierra.h" static void iface_modem_init (MMIfaceModem *iface); static void iface_modem_cdma_init (MMIfaceModemCdma *iface); static void iface_modem_time_init (MMIfaceModemTime *iface); static MMIfaceModem *iface_modem_parent; static MMIfaceModemCdma *iface_modem_cdma_parent; G_DEFINE_TYPE_EXTENDED (MMBroadbandModemSierra, mm_broadband_modem_sierra, MM_TYPE_BROADBAND_MODEM, 0, G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM, iface_modem_init) G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_CDMA, iface_modem_cdma_init) G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_TIME, iface_modem_time_init)); typedef enum { TIME_METHOD_UNKNOWN = 0, TIME_METHOD_TIME = 1, TIME_METHOD_SYSTIME = 2, } TimeMethod; struct _MMBroadbandModemSierraPrivate { TimeMethod time_method; }; /*****************************************************************************/ /* Load unlock retries (Modem interface) */ static MMUnlockRetries * load_unlock_retries_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { MMUnlockRetries *unlock_retries; const gchar *response; gint matched; guint a, b, c ,d; response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, error); if (!response) return NULL; matched = sscanf (response, "+CPINC: %d,%d,%d,%d", &a, &b, &c, &d); if (matched != 4) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Could not parse PIN retries results: '%s'", response); return NULL; } if (a > 998) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Invalid PIN attempts left: '%u'", a); return NULL; } unlock_retries = mm_unlock_retries_new (); mm_unlock_retries_set (unlock_retries, MM_MODEM_LOCK_SIM_PIN, a); mm_unlock_retries_set (unlock_retries, MM_MODEM_LOCK_SIM_PIN2, b); mm_unlock_retries_set (unlock_retries, MM_MODEM_LOCK_SIM_PUK, c); mm_unlock_retries_set (unlock_retries, MM_MODEM_LOCK_SIM_PUK2, d); return unlock_retries; } static void load_unlock_retries (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data) { mm_base_modem_at_command (MM_BASE_MODEM (self), "+CPINC?", 3, FALSE, callback, user_data); } /*****************************************************************************/ /* Generic AT!STATUS parsing */ typedef enum { SYS_MODE_UNKNOWN, SYS_MODE_NO_SERVICE, SYS_MODE_CDMA_1X, SYS_MODE_EVDO_REV0, SYS_MODE_EVDO_REVA } SysMode; #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 SYS_MODE_CDMA_TAG "CDMA" #define EVDO_REV_TAG "HDR Revision:" #define SID_TAG "SID:" static gboolean get_roam_value (const gchar *reply, const gchar *tag, gboolean is_eri, gboolean *out_roaming) { gchar *p; gboolean success; guint32 ind = 0; p = strstr (reply, tag); if (!p) return FALSE; p += strlen (tag); while (*p && isspace (*p)) p++; /* Use generic ERI parsing if it's an ERI */ if (is_eri) { success = mm_cdma_parse_eri (p, out_roaming, &ind, NULL); if (success) { /* Sierra redefines ERI 0, 1, and 2 */ if (ind == 0) *out_roaming = FALSE; /* home */ else if (ind == 1 || ind == 2) *out_roaming = TRUE; /* roaming */ } return success; } /* If it's not an ERI, roaming is just true/false */ if (*p == '1') { *out_roaming = TRUE; return TRUE; } else if (*p == '0') { *out_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 gboolean sys_mode_is_evdo (SysMode mode) { return (mode == SYS_MODE_EVDO_REV0 || mode == SYS_MODE_EVDO_REVA); } static gboolean parse_status (const char *response, MMModemCdmaRegistrationState *out_cdma1x_state, MMModemCdmaRegistrationState *out_evdo_state, MMModemAccessTechnology *out_act) { gchar **lines; gchar **iter; gboolean registered = FALSE; gboolean have_sid = FALSE; SysMode evdo_mode = SYS_MODE_UNKNOWN; SysMode sys_mode = SYS_MODE_UNKNOWN; gboolean evdo_roam = FALSE, cdma1x_roam = FALSE; lines = g_strsplit_set (response, "\n\r", 0); if (!lines) return FALSE; /* 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 */ get_roam_value (*iter, ROAM_1X_TAG, TRUE, &cdma1x_roam); get_roam_value (*iter, ROAM_EVDO_TAG, TRUE, &evdo_roam); if (get_roam_value (*iter, GENERIC_ROAM_TAG, FALSE, &bool_val)) cdma1x_roam = evdo_roam = bool_val; /* Current system mode */ p = strstr (*iter, SYS_MODE_TAG); 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)) || !strncmp (p, SYS_MODE_CDMA_TAG, strlen (SYS_MODE_CDMA_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_is_evdo (sys_mode)) { /* Prefer the explicit EVDO mode from EVDO_REV_TAG */ if (evdo_mode != SYS_MODE_UNKNOWN) sys_mode = evdo_mode; } /* If the modem didn't report explicit registration with "Modem has * registered" then get registration status by looking at either system * mode or (for older devices that don't report that) just the SID. */ if (!registered) { if (sys_mode != SYS_MODE_UNKNOWN) registered = sys_mode_has_service (sys_mode); else registered = have_sid; } if (registered) { *out_cdma1x_state = (cdma1x_roam ? MM_MODEM_CDMA_REGISTRATION_STATE_ROAMING : MM_MODEM_CDMA_REGISTRATION_STATE_HOME); if (sys_mode_is_evdo (sys_mode)) { *out_evdo_state = (evdo_roam ? MM_MODEM_CDMA_REGISTRATION_STATE_ROAMING : MM_MODEM_CDMA_REGISTRATION_STATE_HOME); } else { *out_evdo_state = MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN; } } else { /* Not registered */ *out_cdma1x_state = MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN; *out_evdo_state = MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN; } if (out_act) { *out_act = MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN; if (registered) { if (sys_mode == SYS_MODE_CDMA_1X) *out_act = MM_MODEM_ACCESS_TECHNOLOGY_1XRTT; else if (sys_mode == SYS_MODE_EVDO_REV0) *out_act = MM_MODEM_ACCESS_TECHNOLOGY_EVDO0; else if (sys_mode == SYS_MODE_EVDO_REVA) *out_act = MM_MODEM_ACCESS_TECHNOLOGY_EVDOA; } } g_strfreev (lines); return TRUE; } /*****************************************************************************/ /* Load access technologies (Modem interface) */ typedef struct { MMModemAccessTechnology act; guint mask; } AccessTechInfo; static AccessTechInfo * access_tech_info_new (MMModemAccessTechnology act, guint mask) { AccessTechInfo *info; info = g_new (AccessTechInfo, 1); info->act = act; info->mask = mask; return info; } static gboolean load_access_technologies_finish (MMIfaceModem *self, GAsyncResult *res, MMModemAccessTechnology *access_technologies, guint *mask, GError **error) { AccessTechInfo *info; info = g_task_propagate_pointer (G_TASK (res), error); if (!info) return FALSE; *access_technologies = info->act; *mask = info->mask; g_free (info); return TRUE; } static void access_tech_3gpp_ready (MMBaseModem *self, GAsyncResult *res, GTask *task) { GError *error = NULL; const gchar *response; response = mm_base_modem_at_command_finish (self, res, &error); if (!response) g_task_return_error (task, error); else { MMModemAccessTechnology act = MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN; const gchar *p; p = mm_strip_tag (response, "*CNTI:"); p = strchr (p, ','); if (p) act = mm_string_to_access_tech (p + 1); if (act == MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN) g_task_return_new_error ( task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't parse access technologies result: '%s'", response); else g_task_return_pointer ( task, access_tech_info_new (act, MM_IFACE_MODEM_3GPP_ALL_ACCESS_TECHNOLOGIES_MASK), g_free); } g_object_unref (task); } static void access_tech_cdma_ready (MMBaseModem *self, GAsyncResult *res, GTask *task) { GError *error = NULL; const gchar *response; response = mm_base_modem_at_command_finish (self, res, &error); if (!response) g_task_return_error (task, error); else { MMModemAccessTechnology act = MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN; MMModemCdmaRegistrationState cdma1x_state = MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN; MMModemCdmaRegistrationState evdo_state = MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN; if (!parse_status (response, &cdma1x_state, &evdo_state, &act)) g_task_return_new_error ( task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't parse access technologies result: '%s'", response); else g_task_return_pointer ( task, access_tech_info_new (act, MM_IFACE_MODEM_CDMA_ALL_ACCESS_TECHNOLOGIES_MASK), g_free); } g_object_unref (task); } static void load_access_technologies (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; task = g_task_new (self, NULL, callback, user_data); if (mm_iface_modem_is_3gpp (self)) { mm_base_modem_at_command (MM_BASE_MODEM (self), "*CNTI=0", 3, FALSE, (GAsyncReadyCallback)access_tech_3gpp_ready, task); return; } if (mm_iface_modem_is_cdma (self)) { mm_base_modem_at_command (MM_BASE_MODEM (self), "!STATUS", 3, FALSE, (GAsyncReadyCallback)access_tech_cdma_ready, task); return; } g_assert_not_reached (); } /*****************************************************************************/ /* Load supported modes (Modem interface) */ static GArray * load_supported_modes_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { return g_task_propagate_pointer (G_TASK (res), error); } static void parent_load_supported_modes_ready (MMIfaceModem *self, GAsyncResult *res, GTask *task) { GError *error = NULL; GArray *all; GArray *combinations; GArray *filtered; MMModemModeCombination mode; all = iface_modem_parent->load_supported_modes_finish (self, res, &error); if (!all) { g_task_return_error (task, error); g_object_unref (task); return; } /* CDMA-only modems don't support changing modes, default to parent's */ if (!mm_iface_modem_is_3gpp (self)) { g_task_return_pointer (task, all, (GDestroyNotify) g_array_unref); g_object_unref (task); return; } /* Build list of combinations for 3GPP devices */ combinations = g_array_sized_new (FALSE, FALSE, sizeof (MMModemModeCombination), 5); /* 2G only */ mode.allowed = MM_MODEM_MODE_2G; mode.preferred = MM_MODEM_MODE_NONE; g_array_append_val (combinations, mode); /* 3G only */ mode.allowed = MM_MODEM_MODE_3G; mode.preferred = MM_MODEM_MODE_NONE; g_array_append_val (combinations, mode); /* 2G and 3G */ mode.allowed = (MM_MODEM_MODE_2G | MM_MODEM_MODE_3G); mode.preferred = MM_MODEM_MODE_NONE; g_array_append_val (combinations, mode); /* Non-LTE devices allow 2G/3G preferred modes */ if (!mm_iface_modem_is_3gpp_lte (self)) { /* 2G and 3G, 2G preferred */ mode.allowed = (MM_MODEM_MODE_2G | MM_MODEM_MODE_3G); mode.preferred = MM_MODEM_MODE_2G; g_array_append_val (combinations, mode); /* 2G and 3G, 3G preferred */ mode.allowed = (MM_MODEM_MODE_2G | MM_MODEM_MODE_3G); mode.preferred = MM_MODEM_MODE_3G; g_array_append_val (combinations, mode); } else { /* 4G only */ mode.allowed = MM_MODEM_MODE_4G; mode.preferred = MM_MODEM_MODE_NONE; g_array_append_val (combinations, mode); /* 2G, 3G and 4G */ mode.allowed = (MM_MODEM_MODE_2G | MM_MODEM_MODE_3G | MM_MODEM_MODE_4G); mode.preferred = MM_MODEM_MODE_NONE; g_array_append_val (combinations, mode); } /* Filter out those unsupported modes */ filtered = mm_filter_supported_modes (all, combinations, self); g_array_unref (all); g_array_unref (combinations); g_task_return_pointer (task, filtered, (GDestroyNotify) g_array_unref); g_object_unref (task); } static void load_supported_modes (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data) { /* Run parent's loading */ iface_modem_parent->load_supported_modes ( MM_IFACE_MODEM (self), (GAsyncReadyCallback)parent_load_supported_modes_ready, g_task_new (self, NULL, callback, user_data)); } /*****************************************************************************/ /* Load initial allowed/preferred modes (Modem interface) */ typedef struct { MMModemMode allowed; MMModemMode preferred; } LoadCurrentModesResult; static gboolean load_current_modes_finish (MMIfaceModem *self, GAsyncResult *res, MMModemMode *allowed, MMModemMode *preferred, GError **error) { g_autofree LoadCurrentModesResult *result = NULL; result = g_task_propagate_pointer (G_TASK (res), error); if (!result) return FALSE; *allowed = result->allowed; *preferred = result->preferred; return TRUE; } static void selrat_query_ready (MMBaseModem *self, GAsyncResult *res, GTask *task) { g_autofree LoadCurrentModesResult *result = NULL; const gchar *response; GError *error = NULL; g_autoptr(GRegex) r = NULL; g_autoptr(GMatchInfo) match_info = NULL; response = mm_base_modem_at_command_full_finish (self, res, &error); if (!response) { g_task_return_error (task, error); g_object_unref (task); return; } result = g_new0 (LoadCurrentModesResult, 1); /* Example response: !SELRAT: 03, UMTS 3G Preferred */ r = g_regex_new ("!SELRAT:\\s*(\\d+).*$", 0, 0, NULL); g_assert (r != NULL); if (g_regex_match_full (r, response, strlen (response), 0, 0, &match_info, &error)) { guint mode; if (mm_get_uint_from_match_info (match_info, 1, &mode) && mode <= 7) { switch (mode) { case 0: result->allowed = (MM_MODEM_MODE_2G | MM_MODEM_MODE_3G); result->preferred = MM_MODEM_MODE_NONE; if (mm_iface_modem_is_3gpp_lte (MM_IFACE_MODEM (self))) result->allowed |= MM_MODEM_MODE_4G; result->preferred = MM_MODEM_MODE_NONE; break; case 1: result->allowed = MM_MODEM_MODE_3G; result->preferred = MM_MODEM_MODE_NONE; break; case 2: result->allowed = MM_MODEM_MODE_2G; result->preferred = MM_MODEM_MODE_NONE; break; case 3: /* in Sierra LTE devices, mode 3 is automatic, including LTE, no preference */ if (mm_iface_modem_is_3gpp_lte (MM_IFACE_MODEM (self))) { result->allowed = (MM_MODEM_MODE_2G | MM_MODEM_MODE_3G | MM_MODEM_MODE_4G); result->preferred = MM_MODEM_MODE_NONE; } else { result->allowed = (MM_MODEM_MODE_2G | MM_MODEM_MODE_3G); result->preferred = MM_MODEM_MODE_3G; } break; case 4: /* in Sierra LTE devices, mode 4 is automatic, including LTE, no preference */ if (mm_iface_modem_is_3gpp_lte (MM_IFACE_MODEM (self))) { result->allowed = (MM_MODEM_MODE_2G | MM_MODEM_MODE_3G | MM_MODEM_MODE_4G); result->preferred = MM_MODEM_MODE_NONE; } else { result->allowed = (MM_MODEM_MODE_2G | MM_MODEM_MODE_3G); result->preferred = MM_MODEM_MODE_2G; } break; case 5: result->allowed = (MM_MODEM_MODE_2G | MM_MODEM_MODE_3G); result->preferred = MM_MODEM_MODE_NONE; break; case 6: result->allowed = MM_MODEM_MODE_4G; result->preferred = MM_MODEM_MODE_NONE; break; case 7: result->allowed = (MM_MODEM_MODE_2G | MM_MODEM_MODE_3G | MM_MODEM_MODE_4G); result->preferred = MM_MODEM_MODE_NONE; break; default: g_assert_not_reached (); break; } } else error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Failed to parse the allowed mode response: '%s'", response); } else if (!error) error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Could not parse allowed mode response: Response didn't match: '%s'", response); if (error) g_task_return_error (task, error); else g_task_return_pointer (task, g_steal_pointer (&result), g_free); g_object_unref (task); } static void load_current_modes (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; MMPortSerialAt *primary; task = g_task_new (self, NULL, callback, user_data); if (!mm_iface_modem_is_3gpp (self)) { /* Cannot do this in CDMA modems */ g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "Cannot load allowed modes in CDMA modems"); g_object_unref (task); return; } /* Sierra secondary ports don't have full AT command interpreters */ primary = mm_base_modem_peek_port_primary (MM_BASE_MODEM (self)); if (!primary || mm_port_get_connected (MM_PORT (primary))) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_CONNECTED, "Cannot load allowed modes while connected"); g_object_unref (task); return; } mm_base_modem_at_command_full (MM_BASE_MODEM (self), primary, "!SELRAT?", 3, FALSE, FALSE, /* raw */ NULL, /* cancellable */ (GAsyncReadyCallback)selrat_query_ready, task); } /*****************************************************************************/ /* Set current modes (Modem interface) */ static gboolean set_current_modes_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void selrat_set_ready (MMBaseModem *self, GAsyncResult *res, GTask *task) { GError *error = NULL; if (!mm_base_modem_at_command_full_finish (self, res, &error)) /* Let the error be critical. */ g_task_return_error (task, error); else g_task_return_boolean (task, TRUE); g_object_unref (task); } static void set_current_modes (MMIfaceModem *self, MMModemMode allowed, MMModemMode preferred, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; MMPortSerialAt *primary; gint idx = -1; gchar *command; task = g_task_new (self, NULL, callback, user_data); if (!mm_iface_modem_is_3gpp (self)) { /* Cannot do this in CDMA modems */ g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "Cannot set allowed modes in CDMA modems"); g_object_unref (task); return; } /* Sierra secondary ports don't have full AT command interpreters */ primary = mm_base_modem_peek_port_primary (MM_BASE_MODEM (self)); if (!primary || mm_port_get_connected (MM_PORT (primary))) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_CONNECTED, "Cannot set allowed modes while connected"); g_object_unref (task); return; } if (allowed == MM_MODEM_MODE_3G) idx = 1; else if (allowed == MM_MODEM_MODE_2G) idx = 2; else if (allowed == (MM_MODEM_MODE_2G | MM_MODEM_MODE_3G)) { /* in Sierra LTE devices, modes 3 and 4 are automatic, including LTE, no preference */ if (mm_iface_modem_is_3gpp_lte (self)) { if (preferred == MM_MODEM_MODE_NONE) idx = 5; /* GSM and UMTS Only */ } else if (preferred == MM_MODEM_MODE_3G) idx = 3; else if (preferred == MM_MODEM_MODE_2G) idx = 4; else if (preferred == MM_MODEM_MODE_NONE) idx = 0; } else if (allowed == MM_MODEM_MODE_4G) idx = 6; else if (allowed == (MM_MODEM_MODE_2G | MM_MODEM_MODE_3G | MM_MODEM_MODE_4G) && preferred == MM_MODEM_MODE_NONE) idx = 7; else if (allowed == MM_MODEM_MODE_ANY && preferred == MM_MODEM_MODE_NONE) idx = 0; if (idx < 0) { gchar *allowed_str; gchar *preferred_str; allowed_str = mm_modem_mode_build_string_from_mask (allowed); preferred_str = mm_modem_mode_build_string_from_mask (preferred); g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Requested mode (allowed: '%s', preferred: '%s') not " "supported by the modem.", allowed_str, preferred_str); g_object_unref (task); g_free (allowed_str); g_free (preferred_str); return; } command = g_strdup_printf ("!SELRAT=%d", idx); mm_base_modem_at_command_full (MM_BASE_MODEM (self), primary, command, 3, FALSE, FALSE, /* raw */ NULL, /* cancellable */ (GAsyncReadyCallback)selrat_set_ready, task); g_free (command); } /*****************************************************************************/ /* After SIM unlock (Modem interface) */ static gboolean modem_after_sim_unlock_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static gboolean after_sim_unlock_wait_cb (GTask *task) { g_task_return_boolean (task, TRUE); g_object_unref (task); return G_SOURCE_REMOVE; } static void modem_after_sim_unlock (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; guint timeout = 8; const gchar **drivers; guint i; /* A short wait is necessary for SIM to become ready, otherwise some older * cards (AC881) crash if asked to connect immediately after sending the * PIN. Assume sierra_net driven devices are better and don't need as long * a delay. */ drivers = mm_base_modem_get_drivers (MM_BASE_MODEM (self)); for (i = 0; drivers[i]; i++) { if (g_str_equal (drivers[i], "sierra_net")) timeout = 3; } task = g_task_new (self, NULL, callback, user_data); g_timeout_add_seconds (timeout, (GSourceFunc)after_sim_unlock_wait_cb, task); } /*****************************************************************************/ /* Load own numbers (Modem interface) */ static GStrv modem_load_own_numbers_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { return g_task_propagate_pointer (G_TASK (res), error); } static void parent_load_own_numbers_ready (MMIfaceModem *self, GAsyncResult *res, GTask *task) { GError *error = NULL; GStrv numbers; numbers = iface_modem_parent->load_own_numbers_finish (self, res, &error); if (error) g_task_return_error (task, error); else g_task_return_pointer (task, numbers, (GDestroyNotify)g_strfreev); g_object_unref (task); } #define MDN_TAG "MDN: " static void own_numbers_ready (MMBaseModem *self, GAsyncResult *res, GTask *task) { const gchar *response, *p; const gchar *numbers[2] = { NULL, NULL }; gchar mdn[15]; guint i; response = mm_base_modem_at_command_finish (self, res, NULL); if (!response) goto fallback; p = strstr (response, MDN_TAG); if (!p) goto fallback; response = p + strlen (MDN_TAG); while (isspace (*response)) response++; for (i = 0; i < (sizeof (mdn) - 1) && isdigit (response[i]); i++) mdn[i] = response[i]; mdn[i] = '\0'; numbers[0] = &mdn[0]; /* MDNs are 10 digits in length */ if (i != 10) { mm_obj_warn (self, "failed to parse MDN: expected 10 digits, got %d", i); goto fallback; } g_task_return_pointer (task, g_strdupv ((gchar **) numbers), (GDestroyNotify)g_strfreev); g_object_unref (task); return; fallback: /* Fall back to parent method */ iface_modem_parent->load_own_numbers ( MM_IFACE_MODEM (self), (GAsyncReadyCallback)parent_load_own_numbers_ready, task); } static void modem_load_own_numbers (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; task = g_task_new (self, NULL, callback, user_data); /* 3GPP modems can just run parent's own number loading */ if (mm_iface_modem_is_3gpp (self)) { iface_modem_parent->load_own_numbers ( self, (GAsyncReadyCallback)parent_load_own_numbers_ready, task); return; } /* CDMA modems try AT~NAMVAL?0 first, then fall back to parent for * loading own number from NV memory with QCDM. */ mm_base_modem_at_command ( MM_BASE_MODEM (self), "~NAMVAL?0", 3, FALSE, (GAsyncReadyCallback)own_numbers_ready, task); } /*****************************************************************************/ /* Create Bearer (Modem interface) */ static MMBaseBearer * modem_create_bearer_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { return g_task_propagate_pointer (G_TASK (res), error); } static void broadband_bearer_sierra_new_ready (GObject *source, GAsyncResult *res, GTask *task) { MMBaseBearer *bearer = NULL; GError *error = NULL; bearer = mm_broadband_bearer_sierra_new_finish (res, &error); if (!bearer) g_task_return_error (task, error); else g_task_return_pointer (task, bearer, g_object_unref); g_object_unref (task); } static void modem_create_bearer (MMIfaceModem *self, MMBearerProperties *properties, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; task = g_task_new (self, NULL, callback, user_data); mm_obj_dbg (self, "creating Sierra bearer..."); mm_broadband_bearer_sierra_new (MM_BROADBAND_MODEM (self), properties, FALSE, /* is_icera */ NULL, /* cancellable */ (GAsyncReadyCallback)broadband_bearer_sierra_new_ready, task); } /*****************************************************************************/ /* Reset (Modem interface) */ static gboolean modem_reset_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { return !!mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, error); } static void modem_reset (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data) { mm_base_modem_at_command (MM_BASE_MODEM (self), "!RESET", 3, FALSE, callback, user_data); } /*****************************************************************************/ /* Modem power down (Modem interface) */ static gboolean modem_power_down_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void modem_power_down_ready (MMBaseModem *self, GAsyncResult *res, GTask *task) { /* Ignore errors for now; we're not sure if all Sierra CDMA devices support * at!pcstate or 3GPP devices support +CFUN=4. */ mm_base_modem_at_command_finish (self, res, NULL); g_task_return_boolean (task, TRUE); g_object_unref (task); } static void modem_power_down (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; task = g_task_new (self, NULL, callback, user_data); /* For CDMA modems, run !pcstate */ if (mm_iface_modem_is_cdma_only (self)) { mm_base_modem_at_command (MM_BASE_MODEM (self), "!pcstate=0", 5, FALSE, (GAsyncReadyCallback)modem_power_down_ready, task); return; } /* For GSM modems, run AT+CFUN=4 (power save) */ mm_base_modem_at_command (MM_BASE_MODEM (self), "+CFUN=4", 3, FALSE, (GAsyncReadyCallback)modem_power_down_ready, task); } /*****************************************************************************/ /* Setup registration checks (CDMA interface) */ typedef struct { gboolean skip_qcdm_call_manager_step; gboolean skip_qcdm_hdr_step; gboolean skip_at_cdma_service_status_step; gboolean skip_at_cdma1x_serving_system_step; gboolean skip_detailed_registration_state; } SetupRegistrationChecksResults; static gboolean setup_registration_checks_finish (MMIfaceModemCdma *self, GAsyncResult *res, gboolean *skip_qcdm_call_manager_step, gboolean *skip_qcdm_hdr_step, gboolean *skip_at_cdma_service_status_step, gboolean *skip_at_cdma1x_serving_system_step, gboolean *skip_detailed_registration_state, GError **error) { SetupRegistrationChecksResults *results; results = g_task_propagate_pointer (G_TASK (res), error); if (!results) return FALSE; *skip_qcdm_call_manager_step = results->skip_qcdm_call_manager_step; *skip_qcdm_hdr_step = results->skip_qcdm_hdr_step; *skip_at_cdma_service_status_step = results->skip_at_cdma_service_status_step; *skip_at_cdma1x_serving_system_step = results->skip_at_cdma1x_serving_system_step; *skip_detailed_registration_state = results->skip_detailed_registration_state; g_free (results); return TRUE; } static void parent_setup_registration_checks_ready (MMIfaceModemCdma *self, GAsyncResult *res, GTask *task) { GError *error = NULL; SetupRegistrationChecksResults *results; results = g_new0 (SetupRegistrationChecksResults, 1); if (!iface_modem_cdma_parent->setup_registration_checks_finish (self, res, &results->skip_qcdm_call_manager_step, &results->skip_qcdm_hdr_step, &results->skip_at_cdma_service_status_step, &results->skip_at_cdma1x_serving_system_step, &results->skip_detailed_registration_state, &error)) { g_task_return_error (task, error); g_free (results); } else { /* Skip +CSS */ results->skip_at_cdma1x_serving_system_step = TRUE; /* Skip +CAD */ results->skip_at_cdma_service_status_step = TRUE; /* Force to always use the detailed registration checks, as we have * !STATUS for that */ results->skip_detailed_registration_state = FALSE; g_task_return_pointer (task, results, g_free); } g_object_unref (task); } static void setup_registration_checks (MMIfaceModemCdma *self, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; task = g_task_new (self, NULL, callback, user_data); /* Run parent's checks first */ iface_modem_cdma_parent->setup_registration_checks (self, (GAsyncReadyCallback)parent_setup_registration_checks_ready, task); } /*****************************************************************************/ /* Detailed registration state (CDMA interface) */ typedef struct { MMModemCdmaRegistrationState detailed_cdma1x_state; MMModemCdmaRegistrationState detailed_evdo_state; } DetailedRegistrationStateResults; static gboolean get_detailed_registration_state_finish (MMIfaceModemCdma *self, GAsyncResult *res, MMModemCdmaRegistrationState *detailed_cdma1x_state, MMModemCdmaRegistrationState *detailed_evdo_state, GError **error) { DetailedRegistrationStateResults *results; results = g_task_propagate_pointer (G_TASK (res), error); if (!results) return FALSE; *detailed_cdma1x_state = results->detailed_cdma1x_state; *detailed_evdo_state = results->detailed_evdo_state; g_free (results); return TRUE; } static void status_ready (MMBaseModem *self, GAsyncResult *res, GTask *task) { DetailedRegistrationStateResults *results; const gchar *response; results = g_task_get_task_data (task); /* If error, leave superclass' reg state alone if AT!STATUS isn't supported. */ response = mm_base_modem_at_command_finish (self, res, NULL); if (response) parse_status (response, &(results->detailed_cdma1x_state), &(results->detailed_evdo_state), NULL); g_task_return_pointer (task, g_memdup (results, sizeof (*results)), g_free); g_object_unref (task); } static void get_detailed_registration_state (MMIfaceModemCdma *self, MMModemCdmaRegistrationState cdma1x_state, MMModemCdmaRegistrationState evdo_state, GAsyncReadyCallback callback, gpointer user_data) { DetailedRegistrationStateResults *results; GTask *task; results = g_new0 (DetailedRegistrationStateResults, 1); results->detailed_cdma1x_state = cdma1x_state; results->detailed_evdo_state = evdo_state; task = g_task_new (self, NULL, callback, user_data); g_task_set_task_data (task, results, g_free); mm_base_modem_at_command (MM_BASE_MODEM (self), "!STATUS", 3, FALSE, (GAsyncReadyCallback)status_ready, task); } /*****************************************************************************/ /* Automatic activation (CDMA interface) */ typedef enum { CDMA_AUTOMATIC_ACTIVATION_STEP_FIRST, CDMA_AUTOMATIC_ACTIVATION_STEP_UNLOCK, CDMA_AUTOMATIC_ACTIVATION_STEP_CDV, CDMA_AUTOMATIC_ACTIVATION_STEP_CHECK, CDMA_AUTOMATIC_ACTIVATION_STEP_LAST } CdmaAutomaticActivationStep; typedef struct { CdmaAutomaticActivationStep step; gchar *carrier_code; } CdmaAutomaticActivationContext; static void cdma_automatic_activation_context_free (CdmaAutomaticActivationContext *ctx) { g_free (ctx->carrier_code); g_slice_free (CdmaAutomaticActivationContext, ctx); } static gboolean modem_cdma_activate_finish (MMIfaceModemCdma *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void cdma_automatic_activation_step (GTask *task); static void automatic_activation_command_ready (MMBaseModem *self, GAsyncResult *res, GTask *task) { GError *error = NULL; const gchar *response; CdmaAutomaticActivationContext *ctx; response = mm_base_modem_at_command_finish (self, res, &error); if (!response) { g_task_return_error (task, error); g_object_unref (task); return; } /* Keep on */ ctx = g_task_get_task_data (task); ctx->step++; cdma_automatic_activation_step (task); } static void cdma_automatic_activation_step (GTask *task) { MMBroadbandModemSierra *self; CdmaAutomaticActivationContext *ctx; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); switch (ctx->step) { case CDMA_AUTOMATIC_ACTIVATION_STEP_FIRST: ctx->step++; /* fall-through */ case CDMA_AUTOMATIC_ACTIVATION_STEP_UNLOCK: mm_obj_msg (self, "activation step [1/4]: unlocking device"); mm_base_modem_at_command (MM_BASE_MODEM (self), "~NAMLCK=000000", 20, FALSE, (GAsyncReadyCallback)automatic_activation_command_ready, task); return; case CDMA_AUTOMATIC_ACTIVATION_STEP_CDV: { gchar *command; mm_obj_msg (self, "activation step [2/4]: requesting OTASP"); command = g_strdup_printf ("+CDV%s", ctx->carrier_code); mm_base_modem_at_command (MM_BASE_MODEM (self), command, 120, FALSE, (GAsyncReadyCallback)automatic_activation_command_ready, task); g_free (command); return; } case CDMA_AUTOMATIC_ACTIVATION_STEP_CHECK: mm_obj_msg (self, "activation step [3/4]: checking activation info"); mm_base_modem_at_command (MM_BASE_MODEM (self), "~NAMVAL?0", 3, FALSE, (GAsyncReadyCallback)automatic_activation_command_ready, task); return; case CDMA_AUTOMATIC_ACTIVATION_STEP_LAST: mm_obj_msg (self, "activation step [4/4]: activation process finished"); g_task_return_boolean (task, TRUE); g_object_unref (task); return; default: g_assert_not_reached (); } } static void modem_cdma_activate (MMIfaceModemCdma *self, const gchar *carrier_code, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; CdmaAutomaticActivationContext *ctx; task = g_task_new (self, NULL, callback, user_data); /* Setup context */ ctx = g_slice_new0 (CdmaAutomaticActivationContext); ctx->carrier_code = g_strdup (carrier_code); ctx->step = CDMA_AUTOMATIC_ACTIVATION_STEP_FIRST; g_task_set_task_data (task, ctx, (GDestroyNotify)cdma_automatic_activation_context_free); /* And start it */ cdma_automatic_activation_step (task); } /*****************************************************************************/ /* Manual activation (CDMA interface) */ typedef enum { CDMA_MANUAL_ACTIVATION_STEP_FIRST, CDMA_MANUAL_ACTIVATION_STEP_SPC, CDMA_MANUAL_ACTIVATION_STEP_MDN_MIN, CDMA_MANUAL_ACTIVATION_STEP_OTASP, CDMA_MANUAL_ACTIVATION_STEP_CHECK, CDMA_MANUAL_ACTIVATION_STEP_LAST } CdmaManualActivationStep; typedef struct { CdmaManualActivationStep step; MMCdmaManualActivationProperties *properties; } CdmaManualActivationContext; static gboolean modem_cdma_activate_manual_finish (MMIfaceModemCdma *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void cdma_manual_activation_step (GTask *task); static void manual_activation_command_ready (MMBaseModem *self, GAsyncResult *res, GTask *task) { GError *error = NULL; const gchar *response; CdmaManualActivationContext *ctx; response = mm_base_modem_at_command_finish (self, res, &error); if (!response) { g_task_return_error (task, error); g_object_unref (task); return; } /* Keep on */ ctx = g_task_get_task_data (task); ctx->step++; cdma_manual_activation_step (task); } static void cdma_manual_activation_step (GTask *task) { MMBroadbandModemSierra *self; CdmaManualActivationContext *ctx; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); switch (ctx->step) { case CDMA_MANUAL_ACTIVATION_STEP_FIRST: ctx->step++; /* fall-through */ case CDMA_MANUAL_ACTIVATION_STEP_SPC: { gchar *command; mm_obj_msg (self, "activation step [1/5]: unlocking device"); command = g_strdup_printf ("~NAMLCK=%s", mm_cdma_manual_activation_properties_get_spc (ctx->properties)); mm_base_modem_at_command (MM_BASE_MODEM (self), command, 20, FALSE, (GAsyncReadyCallback)manual_activation_command_ready, task); g_free (command); return; } case CDMA_MANUAL_ACTIVATION_STEP_MDN_MIN: { gchar *command; mm_obj_msg (self, "activation step [2/5]: setting MDN/MIN/SID"); command = g_strdup_printf ("~NAMVAL=0,%s,%s,%" G_GUINT16_FORMAT ",65535", mm_cdma_manual_activation_properties_get_mdn (ctx->properties), mm_cdma_manual_activation_properties_get_min (ctx->properties), mm_cdma_manual_activation_properties_get_sid (ctx->properties)); mm_base_modem_at_command (MM_BASE_MODEM (self), command, 120, FALSE, (GAsyncReadyCallback)manual_activation_command_ready, task); g_free (command); return; } case CDMA_MANUAL_ACTIVATION_STEP_OTASP: mm_obj_msg (self, "activation step [3/5]: requesting OTASP"); mm_base_modem_at_command (MM_BASE_MODEM (self), "!IOTASTART", 20, FALSE, (GAsyncReadyCallback)manual_activation_command_ready, task); return; case CDMA_MANUAL_ACTIVATION_STEP_CHECK: mm_obj_msg (self, "activation step [4/5]: checking activation info"); mm_base_modem_at_command (MM_BASE_MODEM (self), "~NAMVAL?0", 20, FALSE, (GAsyncReadyCallback)manual_activation_command_ready, task); return; case CDMA_MANUAL_ACTIVATION_STEP_LAST: mm_obj_msg (self, "activation step [5/5]: activation process finished"); g_task_return_boolean (task, TRUE); g_object_unref (task); return; default: g_assert_not_reached (); } } static void modem_cdma_activate_manual (MMIfaceModemCdma *self, MMCdmaManualActivationProperties *properties, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; CdmaManualActivationContext *ctx; task = g_task_new (self, NULL, callback, user_data); /* Setup context */ ctx = g_slice_new0 (CdmaManualActivationContext); ctx->properties = g_object_ref (properties); ctx->step = CDMA_MANUAL_ACTIVATION_STEP_FIRST; g_task_set_task_data (task, ctx, (GDestroyNotify)cdma_automatic_activation_context_free); /* And start it */ cdma_manual_activation_step (task); } /*****************************************************************************/ /* Load network time (Time interface) */ static gchar * parse_time (const gchar *response, const gchar *regex, const gchar *tag, GError **error) { g_autoptr(GRegex) r = NULL; g_autoptr(GMatchInfo) match_info = NULL; GError *match_error = NULL; guint year; guint month; guint day; guint hour; guint minute; guint second; gchar *result = NULL; r = g_regex_new (regex, 0, 0, NULL); g_assert (r != NULL); if (!g_regex_match_full (r, response, -1, 0, 0, &match_info, &match_error)) { if (match_error) { g_propagate_error (error, match_error); g_prefix_error (error, "Could not parse %s results: ", tag); } else { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't match %s reply", tag); } } else { if (mm_get_uint_from_match_info (match_info, 1, &year) && mm_get_uint_from_match_info (match_info, 2, &month) && mm_get_uint_from_match_info (match_info, 3, &day) && mm_get_uint_from_match_info (match_info, 4, &hour) && mm_get_uint_from_match_info (match_info, 5, &minute) && mm_get_uint_from_match_info (match_info, 6, &second)) { result = mm_new_iso8601_time (year, month, day, hour, minute, second, FALSE, 0, error); } else { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Failed to parse %s reply", tag); } } return result; } static gchar * parse_3gpp_time (const gchar *response, GError **error) { /* Returns both local time and UTC time, but we have no good way to * determine the timezone from all of that, so just report local time. */ return parse_time (response, "\\s*!TIME:\\s+" "(\\d+)/(\\d+)/(\\d+)\\s+" "(\\d+):(\\d+):(\\d+)\\s*\\(local\\)\\s+" "(\\d+)/(\\d+)/(\\d+)\\s+" "(\\d+):(\\d+):(\\d+)\\s*\\(UTC\\)\\s*", "!TIME", error); } static gchar * parse_cdma_time (const gchar *response, GError **error) { /* YYYYMMDDWHHMMSS */ return parse_time (response, "\\s*(\\d{4})(\\d{2})(\\d{2})\\d(\\d{2})(\\d{2})(\\d{2})\\s*", "!SYSTIME", error); } static gchar * modem_time_load_network_time_finish (MMIfaceModemTime *self, GAsyncResult *res, GError **error) { const gchar *response = NULL; char *iso8601 = NULL; response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, error); if (response) { if (strstr (response, "!TIME:")) iso8601 = parse_3gpp_time (response, error); else iso8601 = parse_cdma_time (response, error); } return iso8601; } static void modem_time_load_network_time (MMIfaceModemTime *self, GAsyncReadyCallback callback, gpointer user_data) { const char *command; switch (MM_BROADBAND_MODEM_SIERRA (self)->priv->time_method) { case TIME_METHOD_TIME: command = "!TIME?"; break; case TIME_METHOD_SYSTIME: command = "!SYSTIME?"; break; case TIME_METHOD_UNKNOWN: default: g_assert_not_reached (); } mm_base_modem_at_command (MM_BASE_MODEM (self), command, 3, FALSE, callback, user_data); } /*****************************************************************************/ /* Check support (Time interface) */ enum { TIME_SUPPORTED = 1, SYSTIME_SUPPORTED = 2, }; static gboolean modem_time_check_support_finish (MMIfaceModemTime *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void modem_time_check_ready (MMBaseModem *self, GAsyncResult *res, GTask *task) { GError *error = NULL; GVariant *result; gboolean supported = FALSE; result = mm_base_modem_at_sequence_finish (self, res, NULL, &error); if (!error && result) { MMBroadbandModemSierra *sierra = MM_BROADBAND_MODEM_SIERRA (self); sierra->priv->time_method = g_variant_get_uint32 (result); if (sierra->priv->time_method != TIME_METHOD_UNKNOWN) supported = TRUE; } g_clear_error (&error); g_task_return_boolean (task, supported); g_object_unref (task); } static MMBaseModemAtResponseProcessorResult parse_time_reply (MMBaseModem *self, gpointer none, const gchar *command, const gchar *response, gboolean last_command, const GError *error, GVariant **result, GError **result_error) { *result_error = NULL; /* If error, try next command */ if (!error) { if (strstr (command, "!TIME")) *result = g_variant_new_uint32 (TIME_METHOD_TIME); else if (strstr (command, "!SYSTIME")) *result = g_variant_new_uint32 (TIME_METHOD_SYSTIME); } /* Stop sequence if we get a result, but not on errors */ return (*result ? MM_BASE_MODEM_AT_RESPONSE_PROCESSOR_RESULT_SUCCESS : MM_BASE_MODEM_AT_RESPONSE_PROCESSOR_RESULT_CONTINUE); } static const MMBaseModemAtCommand time_check_sequence[] = { { "!TIME?", 3, FALSE, parse_time_reply }, /* 3GPP */ { "!SYSTIME?", 3, FALSE, parse_time_reply }, /* CDMA */ { NULL } }; static void modem_time_check_support (MMIfaceModemTime *self, GAsyncReadyCallback callback, gpointer user_data) { mm_base_modem_at_sequence ( MM_BASE_MODEM (self), time_check_sequence, NULL, /* response_processor_context */ NULL, /* response_processor_context_free */ (GAsyncReadyCallback)modem_time_check_ready, g_task_new (self, NULL, callback, user_data)); } /*****************************************************************************/ /* Setup ports (Broadband modem class) */ static void setup_ports (MMBroadbandModem *self) { /* Call parent's setup ports first always */ MM_BROADBAND_MODEM_CLASS (mm_broadband_modem_sierra_parent_class)->setup_ports (self); mm_common_sierra_setup_ports (self); } /*****************************************************************************/ MMBroadbandModemSierra * mm_broadband_modem_sierra_new (const gchar *device, const gchar *physdev, const gchar **drivers, const gchar *plugin, guint16 vendor_id, guint16 product_id) { return g_object_new (MM_TYPE_BROADBAND_MODEM_SIERRA, MM_BASE_MODEM_DEVICE, device, MM_BASE_MODEM_PHYSDEV, physdev, MM_BASE_MODEM_DRIVERS, drivers, MM_BASE_MODEM_PLUGIN, plugin, MM_BASE_MODEM_VENDOR_ID, vendor_id, MM_BASE_MODEM_PRODUCT_ID, product_id, /* Sierra bearer supports both NET and TTY */ MM_BASE_MODEM_DATA_NET_SUPPORTED, TRUE, MM_BASE_MODEM_DATA_TTY_SUPPORTED, TRUE, NULL); } static void mm_broadband_modem_sierra_init (MMBroadbandModemSierra *self) { /* Initialize private data */ self->priv = G_TYPE_INSTANCE_GET_PRIVATE ((self), MM_TYPE_BROADBAND_MODEM_SIERRA, MMBroadbandModemSierraPrivate); } static void iface_modem_init (MMIfaceModem *iface) { iface_modem_parent = g_type_interface_peek_parent (iface); mm_common_sierra_peek_parent_interfaces (iface); iface->load_supported_modes = load_supported_modes; iface->load_supported_modes_finish = load_supported_modes_finish; iface->load_current_modes = load_current_modes; iface->load_current_modes_finish = load_current_modes_finish; iface->set_current_modes = set_current_modes; iface->set_current_modes_finish = set_current_modes_finish; iface->load_access_technologies = load_access_technologies; iface->load_access_technologies_finish = load_access_technologies_finish; iface->load_own_numbers = modem_load_own_numbers; iface->load_own_numbers_finish = modem_load_own_numbers_finish; iface->reset = modem_reset; iface->reset_finish = modem_reset_finish; iface->load_power_state = mm_common_sierra_load_power_state; iface->load_power_state_finish = mm_common_sierra_load_power_state_finish; iface->modem_power_up = mm_common_sierra_modem_power_up; iface->modem_power_up_finish = mm_common_sierra_modem_power_up_finish; iface->modem_power_down = modem_power_down; iface->modem_power_down_finish = modem_power_down_finish; iface->create_sim = mm_common_sierra_create_sim; iface->create_sim_finish = mm_common_sierra_create_sim_finish; iface->load_unlock_retries = load_unlock_retries; iface->load_unlock_retries_finish = load_unlock_retries_finish; iface->modem_after_sim_unlock = modem_after_sim_unlock; iface->modem_after_sim_unlock_finish = modem_after_sim_unlock_finish; iface->create_bearer = modem_create_bearer; iface->create_bearer_finish = modem_create_bearer_finish; } static void iface_modem_cdma_init (MMIfaceModemCdma *iface) { iface_modem_cdma_parent = g_type_interface_peek_parent (iface); iface->setup_registration_checks = setup_registration_checks; iface->setup_registration_checks_finish = setup_registration_checks_finish; iface->get_detailed_registration_state = get_detailed_registration_state; iface->get_detailed_registration_state_finish = get_detailed_registration_state_finish; iface->activate = modem_cdma_activate; iface->activate_finish = modem_cdma_activate_finish; iface->activate_manual = modem_cdma_activate_manual; iface->activate_manual_finish = modem_cdma_activate_manual_finish; } static void iface_modem_time_init (MMIfaceModemTime *iface) { iface->check_support = modem_time_check_support; iface->check_support_finish = modem_time_check_support_finish; iface->load_network_time = modem_time_load_network_time; iface->load_network_time_finish = modem_time_load_network_time_finish; } static void mm_broadband_modem_sierra_class_init (MMBroadbandModemSierraClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); MMBroadbandModemClass *broadband_modem_class = MM_BROADBAND_MODEM_CLASS (klass); g_type_class_add_private (object_class, sizeof (MMBroadbandModemSierraPrivate)); broadband_modem_class->setup_ports = setup_ports; } ModemManager-1.23.4-dev/src/plugins/sierra/mm-broadband-modem-sierra.h000066400000000000000000000047401456466623000255450ustar00rootroot00000000000000/* -*- 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 - 2012 Red Hat, Inc. * Copyright (C) 2012 Lanedo GmbH */ #ifndef MM_BROADBAND_MODEM_SIERRA_H #define MM_BROADBAND_MODEM_SIERRA_H #include "mm-broadband-modem.h" #define MM_TYPE_BROADBAND_MODEM_SIERRA (mm_broadband_modem_sierra_get_type ()) #define MM_BROADBAND_MODEM_SIERRA(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_BROADBAND_MODEM_SIERRA, MMBroadbandModemSierra)) #define MM_BROADBAND_MODEM_SIERRA_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_BROADBAND_MODEM_SIERRA, MMBroadbandModemSierraClass)) #define MM_IS_BROADBAND_MODEM_SIERRA(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_BROADBAND_MODEM_SIERRA)) #define MM_IS_BROADBAND_MODEM_SIERRA_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_BROADBAND_MODEM_SIERRA)) #define MM_BROADBAND_MODEM_SIERRA_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_BROADBAND_MODEM_SIERRA, MMBroadbandModemSierraClass)) typedef struct _MMBroadbandModemSierra MMBroadbandModemSierra; typedef struct _MMBroadbandModemSierraClass MMBroadbandModemSierraClass; typedef struct _MMBroadbandModemSierraPrivate MMBroadbandModemSierraPrivate; struct _MMBroadbandModemSierra { MMBroadbandModem parent; MMBroadbandModemSierraPrivate *priv; }; struct _MMBroadbandModemSierraClass{ MMBroadbandModemClass parent; }; GType mm_broadband_modem_sierra_get_type (void); MMBroadbandModemSierra *mm_broadband_modem_sierra_new (const gchar *device, const gchar *physdev, const gchar **drivers, const gchar *plugin, guint16 vendor_id, guint16 product_id); #endif /* MM_BROADBAND_MODEM_SIERRA_H */ ModemManager-1.23.4-dev/src/plugins/sierra/mm-common-sierra.c000066400000000000000000000406301456466623000240130ustar00rootroot00000000000000/* -*- 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 - 2012 Red Hat, Inc. * Copyright (C) 2012 Lanedo GmbH */ #include #include #include "mm-common-sierra.h" #include "mm-base-modem-at.h" #include "mm-log.h" #include "mm-modem-helpers.h" #include "mm-sim-sierra.h" static MMIfaceModem *iface_modem_parent; /*****************************************************************************/ /* Custom init and port type hints */ #define TAG_SIERRA_APP_PORT "sierra-app-port" #define TAG_SIERRA_APP1_PPP_OK "sierra-app1-ppp-ok" gboolean mm_common_sierra_grab_port (MMPlugin *self, MMBaseModem *modem, MMPortProbe *probe, GError **error) { MMPortSerialAtFlag pflags = MM_PORT_SERIAL_AT_FLAG_NONE; MMPortType ptype; ptype = mm_port_probe_get_port_type (probe); /* Is it a GSM secondary port? */ if (g_object_get_data (G_OBJECT (probe), TAG_SIERRA_APP_PORT)) { if (g_object_get_data (G_OBJECT (probe), TAG_SIERRA_APP1_PPP_OK)) pflags = MM_PORT_SERIAL_AT_FLAG_PPP; else pflags = MM_PORT_SERIAL_AT_FLAG_SECONDARY; } else if (ptype == MM_PORT_TYPE_AT) pflags = MM_PORT_SERIAL_AT_FLAG_PRIMARY; return mm_base_modem_grab_port (modem, mm_port_probe_peek_port (probe), ptype, pflags, error); } gboolean mm_common_sierra_port_probe_list_is_icera (GList *probes) { GList *l; for (l = probes; l; l = g_list_next (l)) { /* Only assume the Icera probing check is valid IF the port is not * secondary. This will skip the stupid ports which reply OK to every * AT command, even the one we use to check for Icera support */ if (mm_port_probe_is_icera (MM_PORT_PROBE (l->data)) && !g_object_get_data (G_OBJECT (l->data), TAG_SIERRA_APP_PORT)) return TRUE; } return FALSE; } typedef struct { MMPortSerialAt *port; guint retries; } SierraCustomInitContext; static void sierra_custom_init_context_free (SierraCustomInitContext *ctx) { g_object_unref (ctx->port); g_slice_free (SierraCustomInitContext, ctx); } gboolean mm_common_sierra_custom_init_finish (MMPortProbe *probe, GAsyncResult *result, GError **error) { return g_task_propagate_boolean (G_TASK (result), error); } static void sierra_custom_init_step (GTask *task); static void gcap_ready (MMPortSerialAt *port, GAsyncResult *res, GTask *task) { MMPortProbe *probe; SierraCustomInitContext *ctx; g_autofree gchar *response = NULL; GError *error = NULL; probe = g_task_get_source_object (task); ctx = g_task_get_task_data (task); response = mm_port_serial_at_command_finish (port, res, &error); if (error) { /* If consumed all tries and the last error was a timeout, assume the * port is not AT */ if (ctx->retries == 0 && g_error_matches (error, MM_SERIAL_ERROR, MM_SERIAL_ERROR_RESPONSE_TIMEOUT)) { mm_port_probe_set_result_at (probe, FALSE); } /* If reported a hard parse error, this port is definitely not an AT * port, skip trying. */ else if (g_error_matches (error, MM_SERIAL_ERROR, MM_SERIAL_ERROR_PARSE_FAILED)) { mm_port_probe_set_result_at (probe, FALSE); ctx->retries = 0; } /* Some Icera-based devices (eg, USB305) have an AT-style port that * replies to everything with ERROR, so tag as unsupported; sometimes * the real AT ports do this too, so let a retry tag the port as * supported if it responds correctly later. */ else if (g_error_matches (error, MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_UNKNOWN)) { mm_port_probe_set_result_at (probe, FALSE); } /* Just retry... */ sierra_custom_init_step (task); goto out; } /* A valid reply to ATI tells us this is an AT port already */ mm_port_probe_set_result_at (probe, TRUE); /* Sierra APPx ports have limited AT command parsers that just reply with * "OK" to most commands. These can sometimes be used for PPP while the * main port is used for status and control, but older modems tend to crash * or fail PPP. So we allowlist modems that are known to allow PPP on the * secondary APP ports. */ if (strstr (response, "APP1")) { g_object_set_data (G_OBJECT (probe), TAG_SIERRA_APP_PORT, GUINT_TO_POINTER (TRUE)); /* PPP-on-APP1-port allowlist */ if (strstr (response, "C885") || strstr (response, "USB 306") || strstr (response, "MC8790")) g_object_set_data (G_OBJECT (probe), TAG_SIERRA_APP1_PPP_OK, GUINT_TO_POINTER (TRUE)); /* For debugging: let users figure out if their device supports PPP * on the APP1 port or not. */ if (getenv ("MM_SIERRA_APP1_PPP_OK")) { mm_obj_dbg (probe, "APP1 PPP OK '%s'", response); g_object_set_data (G_OBJECT (probe), TAG_SIERRA_APP1_PPP_OK, GUINT_TO_POINTER (TRUE)); } } else if (strstr (response, "APP2") || strstr (response, "APP3") || strstr (response, "APP4")) { /* Additional APP ports don't support most AT commands, so they cannot * be used as the primary port. */ g_object_set_data (G_OBJECT (probe), TAG_SIERRA_APP_PORT, GUINT_TO_POINTER (TRUE)); } g_task_return_boolean (task, TRUE); g_object_unref (task); out: if (error) g_error_free (error); } static void sierra_custom_init_step (GTask *task) { MMPortProbe *probe; SierraCustomInitContext *ctx; GCancellable *cancellable; probe = g_task_get_source_object (task); ctx = g_task_get_task_data (task); cancellable = g_task_get_cancellable (task); /* If cancelled, end */ if (g_cancellable_is_cancelled (cancellable)) { mm_obj_dbg (probe, "no need to keep on running custom init"); g_task_return_boolean (task, TRUE); g_object_unref (task); return; } if (ctx->retries == 0) { mm_obj_dbg (probe, "couldn't get port type hints"); g_task_return_boolean (task, TRUE); g_object_unref (task); return; } ctx->retries--; mm_port_serial_at_command ( ctx->port, "ATI", 3, FALSE, /* raw */ FALSE, /* allow_cached */ cancellable, (GAsyncReadyCallback)gcap_ready, task); } void mm_common_sierra_custom_init (MMPortProbe *probe, MMPortSerialAt *port, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { SierraCustomInitContext *ctx; GTask *task; ctx = g_slice_new (SierraCustomInitContext); ctx->port = g_object_ref (port); ctx->retries = 3; task = g_task_new (probe, cancellable, callback, user_data); g_task_set_check_cancellable (task, FALSE); g_task_set_task_data (task, ctx, (GDestroyNotify)sierra_custom_init_context_free); sierra_custom_init_step (task); } /*****************************************************************************/ /* Modem power up (Modem interface) */ gboolean mm_common_sierra_modem_power_up_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static gboolean sierra_power_up_wait_cb (GTask *task) { g_task_return_boolean (task, TRUE); g_object_unref (task); return G_SOURCE_REMOVE; } static void cfun_enable_ready (MMBaseModem *self, GAsyncResult *res, GTask *task) { GError *error = NULL; guint i; const gchar **drivers; gboolean is_new_sierra = FALSE; if (!mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error)) { g_task_return_error (task, error); g_object_unref (task); return; } /* Many Sierra devices return OK immediately in response to CFUN=1 but * need some time to finish powering up, otherwise subsequent commands * may return failure or even crash the modem. Give more time for older * devices like the AC860 and C885, which aren't driven by the 'sierra_net' * driver. Assume any DirectIP (ie, sierra_net) device is new enough * to allow a lower timeout. */ drivers = mm_base_modem_get_drivers (MM_BASE_MODEM (self)); for (i = 0; drivers[i]; i++) { if (g_str_equal (drivers[i], "sierra_net")) { is_new_sierra = TRUE; break; } } /* The modem object will be valid in the callback as 'task' keeps a * reference to it. */ g_timeout_add_seconds (is_new_sierra ? 5 : 10, (GSourceFunc)sierra_power_up_wait_cb, task); } static void pcstate_enable_ready (MMBaseModem *self, GAsyncResult *res, GTask *task) { /* Ignore errors for now; we're not sure if all Sierra CDMA devices support * at!pcstate. */ mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, NULL); g_task_return_boolean (task, TRUE); g_object_unref (task); } void mm_common_sierra_modem_power_up (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; task = g_task_new (self, NULL, callback, user_data); /* For CDMA modems, run !pcstate */ if (mm_iface_modem_is_cdma_only (self)) { mm_base_modem_at_command (MM_BASE_MODEM (self), "!pcstate=1", 5, FALSE, (GAsyncReadyCallback)pcstate_enable_ready, task); return; } mm_obj_warn (self, "not in full functionality status, power-up command is needed"); mm_obj_warn (self, "device may be rebooted"); /* Try to go to full functionality mode without rebooting the system. * Works well if we previously switched off the power with CFUN=4 */ mm_base_modem_at_command (MM_BASE_MODEM (self), "+CFUN=1,0", /* ",0" requests no reset */ 10, FALSE, (GAsyncReadyCallback)cfun_enable_ready, task); } /*****************************************************************************/ /* Power state loading (Modem interface) */ MMModemPowerState mm_common_sierra_load_power_state_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { GError *inner_error = NULL; gssize value; value = g_task_propagate_int (G_TASK (res), &inner_error); if (inner_error) { g_propagate_error (error, inner_error); return MM_MODEM_POWER_STATE_UNKNOWN; } return (MMModemPowerState)value; } static void parent_load_power_state_ready (MMIfaceModem *self, GAsyncResult *res, GTask *task) { GError *error = NULL; MMModemPowerState state; state = iface_modem_parent->load_power_state_finish (self, res, &error); if (error) g_task_return_error (task, error); else g_task_return_int (task, state); g_object_unref (task); } static void pcstate_query_ready (MMBaseModem *self, GAsyncResult *res, GTask *task) { const gchar *result; guint state; GError *error = NULL; result = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error); if (!result) { g_task_return_error (task, error); g_object_unref (task); return; } /* Parse power state reply */ result = mm_strip_tag (result, "!PCSTATE:"); if (!mm_get_uint_from_str (result, &state)) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Failed to parse !PCSTATE response '%s'", result); } else { switch (state) { case 0: g_task_return_int (task, MM_MODEM_POWER_STATE_LOW); break; case 1: g_task_return_int (task, MM_MODEM_POWER_STATE_ON); break; default: g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Unhandled power state: '%u'", state); break; } } g_object_unref (task); } void mm_common_sierra_load_power_state (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; task = g_task_new (self, NULL, callback, user_data); /* Check power state with AT!PCSTATE? */ if (mm_iface_modem_is_cdma_only (self)) { mm_base_modem_at_command (MM_BASE_MODEM (self), "!pcstate?", 3, FALSE, (GAsyncReadyCallback)pcstate_query_ready, task); return; } /* Otherwise run parent's */ iface_modem_parent->load_power_state (self, (GAsyncReadyCallback)parent_load_power_state_ready, task); } /*****************************************************************************/ /* Create SIM (Modem interface) */ MMBaseSim * mm_common_sierra_create_sim_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { return mm_sim_sierra_new_finish (res, error); } void mm_common_sierra_create_sim (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data) { /* New Sierra SIM */ mm_sim_sierra_new (MM_BASE_MODEM (self), NULL, /* cancellable */ callback, user_data); } /*****************************************************************************/ /* Setup ports */ void mm_common_sierra_setup_ports (MMBroadbandModem *self) { MMPortSerialAt *ports[2]; guint i; g_autoptr(GRegex) pacsp_regex = NULL; pacsp_regex = g_regex_new ("\\r\\n\\+PACSP.*\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); ports[0] = mm_base_modem_peek_port_primary (MM_BASE_MODEM (self)); ports[1] = mm_base_modem_peek_port_secondary (MM_BASE_MODEM (self)); for (i = 0; i < G_N_ELEMENTS (ports); i++) { if (!ports[i]) continue; if (i == 1) { /* Built-in echo removal conflicts with the APP1 port's limited AT * parser, which doesn't always prefix responses with . */ g_object_set (ports[i], MM_PORT_SERIAL_AT_REMOVE_ECHO, FALSE, NULL); } mm_port_serial_at_add_unsolicited_msg_handler ( ports[i], pacsp_regex, NULL, NULL, NULL); } } /*****************************************************************************/ void mm_common_sierra_peek_parent_interfaces (MMIfaceModem *iface) { iface_modem_parent = g_type_interface_peek_parent (iface); } ModemManager-1.23.4-dev/src/plugins/sierra/mm-common-sierra.h000066400000000000000000000061721456466623000240230ustar00rootroot00000000000000/* -*- 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 - 2012 Red Hat, Inc. * Copyright (C) 2012 Lanedo GmbH */ #ifndef MM_COMMON_SIERRA_H #define MM_COMMON_SIERRA_H #include "mm-plugin.h" #include "mm-broadband-modem.h" #include "mm-iface-modem.h" #include "mm-base-sim.h" gboolean mm_common_sierra_grab_port (MMPlugin *self, MMBaseModem *modem, MMPortProbe *probe, GError **error); gboolean mm_common_sierra_port_probe_list_is_icera (GList *probes); void mm_common_sierra_custom_init (MMPortProbe *probe, MMPortSerialAt *port, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); gboolean mm_common_sierra_custom_init_finish (MMPortProbe *probe, GAsyncResult *result, GError **error); void mm_common_sierra_load_power_state (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data); MMModemPowerState mm_common_sierra_load_power_state_finish (MMIfaceModem *self, GAsyncResult *res, GError **error); void mm_common_sierra_modem_power_up (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data); gboolean mm_common_sierra_modem_power_up_finish (MMIfaceModem *self, GAsyncResult *res, GError **error); void mm_common_sierra_create_sim (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data); MMBaseSim *mm_common_sierra_create_sim_finish (MMIfaceModem *self, GAsyncResult *res, GError **error); void mm_common_sierra_setup_ports (MMBroadbandModem *self); void mm_common_sierra_peek_parent_interfaces (MMIfaceModem *iface); #endif /* MM_COMMON_SIERRA_H */ ModemManager-1.23.4-dev/src/plugins/sierra/mm-modem-helpers-sierra.c000066400000000000000000000054121456466623000252630ustar00rootroot00000000000000/* -*- 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) 2018 Aleksander Morgado */ #include #include #include "mm-modem-helpers.h" #include "mm-modem-helpers-sierra.h" GList * mm_sierra_parse_scact_read_response (const gchar *reply, GError **error) { g_autoptr(GRegex) r = NULL; g_autoptr(GMatchInfo) match_info = NULL; GError *inner_error = NULL; GList *list = NULL; if (!reply || !reply[0]) /* Nothing configured, all done */ return NULL; r = g_regex_new ("!SCACT:\\s*(\\d+),(\\d+)", G_REGEX_DOLLAR_ENDONLY | G_REGEX_RAW, 0, &inner_error); g_assert (r); g_regex_match_full (r, reply, strlen (reply), 0, 0, &match_info, &inner_error); while (!inner_error && g_match_info_matches (match_info)) { MM3gppPdpContextActive *pdp_active; guint cid = 0; guint aux = 0; if (!mm_get_uint_from_match_info (match_info, 1, &cid)) { inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't parse CID from reply: '%s'", reply); break; } if (!mm_get_uint_from_match_info (match_info, 2, &aux) || (aux != 0 && aux != 1)) { inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't parse context status from reply: '%s'", reply); break; } pdp_active = g_slice_new0 (MM3gppPdpContextActive); pdp_active->cid = cid; pdp_active->active = (gboolean) aux; list = g_list_prepend (list, pdp_active); g_match_info_next (match_info, &inner_error); } if (inner_error) { mm_3gpp_pdp_context_active_list_free (list); g_propagate_error (error, inner_error); g_prefix_error (error, "Couldn't properly parse list of active/inactive PDP contexts. "); return NULL; } return g_list_sort (list, (GCompareFunc) mm_3gpp_pdp_context_active_cmp); } ModemManager-1.23.4-dev/src/plugins/sierra/mm-modem-helpers-sierra.h000066400000000000000000000017221456466623000252700ustar00rootroot00000000000000/* -*- 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) 2018 Aleksander Morgado */ #ifndef MM_MODEM_HELPERS_SIERRA_H #define MM_MODEM_HELPERS_SIERRA_H #include #include /* MM3gppPdpContextActive list */ GList *mm_sierra_parse_scact_read_response (const gchar *reply, GError **error); #endif /* MM_MODEM_HELPERS_SIERRA_H */ ModemManager-1.23.4-dev/src/plugins/sierra/mm-plugin-sierra-legacy.c000066400000000000000000000076411456466623000252700ustar00rootroot00000000000000/* -*- 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 - 2012 Red Hat, Inc. * Copyright (C) 2012 Lanedo GmbH * Copyright (C) 2015 Aleksander Morgado */ #include #include #define _LIBMM_INSIDE_MM #include #include "mm-plugin-common.h" #include "mm-common-sierra.h" #include "mm-broadband-modem-sierra.h" #include "mm-broadband-modem-sierra-icera.h" #define MM_TYPE_PLUGIN_SIERRA_LEGACY mm_plugin_sierra_legacy_get_type () MM_DEFINE_PLUGIN (SIERRA_LEGACY, sierra_legacy, SierraLegacy) /*****************************************************************************/ static MMBaseModem * create_modem (MMPlugin *self, const gchar *uid, const gchar *physdev, const gchar **drivers, guint16 vendor, guint16 product, guint16 subsystem_vendor, GList *probes, GError **error) { if (mm_common_sierra_port_probe_list_is_icera (probes)) return MM_BASE_MODEM (mm_broadband_modem_sierra_icera_new (uid, physdev, drivers, mm_plugin_get_name (self), vendor, product)); return MM_BASE_MODEM (mm_broadband_modem_sierra_new (uid, physdev, drivers, mm_plugin_get_name (self), vendor, product)); } /*****************************************************************************/ MM_PLUGIN_NAMED_CREATOR_SCOPE MMPlugin * mm_plugin_create_sierra_legacy (void) { static const gchar *subsystems[] = { "tty", "net", NULL }; static const gchar *drivers[] = { "sierra", "sierra_net", NULL }; static const gchar *forbidden_drivers[] = { "qmi_wwan", "cdc_mbim", NULL }; static const MMAsyncMethod custom_init = { .async = G_CALLBACK (mm_common_sierra_custom_init), .finish = G_CALLBACK (mm_common_sierra_custom_init_finish), }; return MM_PLUGIN ( g_object_new (MM_TYPE_PLUGIN_SIERRA_LEGACY, MM_PLUGIN_NAME, MM_MODULE_NAME, MM_PLUGIN_ALLOWED_SUBSYSTEMS, subsystems, MM_PLUGIN_ALLOWED_DRIVERS, drivers, MM_PLUGIN_FORBIDDEN_DRIVERS, forbidden_drivers, MM_PLUGIN_ALLOWED_AT, TRUE, MM_PLUGIN_CUSTOM_INIT, &custom_init, MM_PLUGIN_ICERA_PROBE, TRUE, MM_PLUGIN_REMOVE_ECHO, FALSE, NULL)); } static void mm_plugin_sierra_legacy_init (MMPluginSierraLegacy *self) { } static void mm_plugin_sierra_legacy_class_init (MMPluginSierraLegacyClass *klass) { MMPluginClass *plugin_class = MM_PLUGIN_CLASS (klass); plugin_class->create_modem = create_modem; plugin_class->grab_port = mm_common_sierra_grab_port; } ModemManager-1.23.4-dev/src/plugins/sierra/mm-plugin-sierra.c000066400000000000000000000127731456466623000240300ustar00rootroot00000000000000/* -*- 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 - 2012 Red Hat, Inc. * Copyright (C) 2012 Lanedo GmbH * Copyright (C) 2015 Aleksander Morgado */ #include #include #define _LIBMM_INSIDE_MM #include #include "mm-log-object.h" #include "mm-plugin-common.h" #include "mm-broadband-modem.h" #include "mm-broadband-modem-xmm.h" #if defined WITH_QMI #include "mm-broadband-modem-qmi.h" #endif #if defined WITH_MBIM #include "mm-broadband-modem-mbim.h" #include "mm-broadband-modem-mbim-xmm.h" #endif #define MM_TYPE_PLUGIN_SIERRA mm_plugin_sierra_get_type () MM_DEFINE_PLUGIN (SIERRA, sierra, Sierra) /*****************************************************************************/ static MMBaseModem * create_modem (MMPlugin *self, const gchar *uid, const gchar *physdev, const gchar **drivers, guint16 vendor, guint16 product, guint16 subsystem_vendor, GList *probes, GError **error) { #if defined WITH_QMI if (mm_port_probe_list_has_qmi_port (probes)) { mm_obj_dbg (self, "QMI-powered Sierra modem found..."); return MM_BASE_MODEM (mm_broadband_modem_qmi_new (uid, physdev, drivers, mm_plugin_get_name (self), vendor, product)); } #endif #if defined WITH_MBIM if (mm_port_probe_list_has_mbim_port (probes)) { if (mm_port_probe_list_is_xmm (probes)) { mm_obj_dbg (self, "MBIM-powered XMM-based Sierra modem found..."); return MM_BASE_MODEM (mm_broadband_modem_mbim_xmm_new (uid, physdev, drivers, mm_plugin_get_name (self), vendor, product)); } mm_obj_dbg (self, "MBIM-powered Sierra modem found..."); return MM_BASE_MODEM (mm_broadband_modem_mbim_new (uid, physdev, drivers, mm_plugin_get_name (self), vendor, product)); } #endif if (mm_port_probe_list_is_xmm (probes)) { mm_obj_dbg (self, "XMM-based Sierra modem found..."); return MM_BASE_MODEM (mm_broadband_modem_xmm_new (uid, physdev, drivers, mm_plugin_get_name (self), vendor, product)); } /* Fallback to default modem in the worst case */ return MM_BASE_MODEM (mm_broadband_modem_new (uid, physdev, drivers, mm_plugin_get_name (self), vendor, product)); } /*****************************************************************************/ MM_PLUGIN_NAMED_CREATOR_SCOPE MMPlugin * mm_plugin_create_sierra (void) { static const gchar *subsystems[] = { "tty", "net", "usbmisc", NULL }; static const guint16 vendor_ids[] = { 0x1199, 0 }; static const gchar *drivers[] = { "qmi_wwan", "cdc_mbim", NULL }; return MM_PLUGIN ( g_object_new (MM_TYPE_PLUGIN_SIERRA, MM_PLUGIN_NAME, MM_MODULE_NAME, MM_PLUGIN_ALLOWED_SUBSYSTEMS, subsystems, MM_PLUGIN_ALLOWED_VENDOR_IDS, vendor_ids, MM_PLUGIN_ALLOWED_DRIVERS, drivers, MM_PLUGIN_ALLOWED_AT, TRUE, MM_PLUGIN_ALLOWED_QCDM, TRUE, MM_PLUGIN_ALLOWED_QMI, TRUE, MM_PLUGIN_ALLOWED_MBIM, TRUE, MM_PLUGIN_XMM_PROBE, TRUE, NULL)); } static void mm_plugin_sierra_init (MMPluginSierra *self) { } static void mm_plugin_sierra_class_init (MMPluginSierraClass *klass) { MMPluginClass *plugin_class = MM_PLUGIN_CLASS (klass); plugin_class->create_modem = create_modem; } ModemManager-1.23.4-dev/src/plugins/sierra/mm-shared.c000066400000000000000000000013131456466623000225010ustar00rootroot00000000000000/* -*- 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) 2019 Aleksander Morgado */ #include "mm-shared-common.h" MM_DEFINE_SHARED (sierra) ModemManager-1.23.4-dev/src/plugins/sierra/mm-sim-sierra.c000066400000000000000000000104141456466623000233100ustar00rootroot00000000000000/* -*- 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 - 2012 Red Hat, Inc. * Copyright (C) 2012 Lanedo GmbH */ #include #include #include #include #include #include #include #define _LIBMM_INSIDE_MM #include #include "mm-modem-helpers.h" #include "mm-base-modem-at.h" #include "mm-sim-sierra.h" G_DEFINE_TYPE (MMSimSierra, mm_sim_sierra, MM_TYPE_BASE_SIM) /*****************************************************************************/ /* SIM identifier loading */ static gchar * load_sim_identifier_finish (MMBaseSim *self, GAsyncResult *res, GError **error) { return g_task_propagate_pointer (G_TASK (res), error); } static void iccid_read_ready (MMBaseModem *modem, GAsyncResult *res, GTask *task) { GError *error = NULL; const gchar *response; const gchar *p; char *parsed; GError *local = NULL; response = mm_base_modem_at_command_finish (modem, res, &error); if (!response) { g_task_return_error (task, error); g_object_unref (task); return; } p = mm_strip_tag (response, "!ICCID:"); if (!p) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Failed to parse !ICCID response: '%s'", response); g_object_unref (task); return; } parsed = mm_3gpp_parse_iccid (p, &local); if (parsed) g_task_return_pointer (task, parsed, g_free); else g_task_return_error (task, local); g_object_unref (task); } static void load_sim_identifier (MMBaseSim *self, GAsyncReadyCallback callback, gpointer user_data) { MMBaseModem *modem = NULL; GTask *task; g_object_get (self, MM_BASE_SIM_MODEM, &modem, NULL); task = g_task_new (self, NULL, callback, user_data); mm_base_modem_at_command ( modem, "!ICCID?", 3, FALSE, (GAsyncReadyCallback)iccid_read_ready, task); g_object_unref (modem); } /*****************************************************************************/ MMBaseSim * mm_sim_sierra_new_finish (GAsyncResult *res, GError **error) { GObject *source; GObject *sim; source = g_async_result_get_source_object (res); sim = g_async_initable_new_finish (G_ASYNC_INITABLE (source), res, error); g_object_unref (source); if (!sim) return NULL; /* Only export valid SIMs */ mm_base_sim_export (MM_BASE_SIM (sim)); return MM_BASE_SIM (sim); } void mm_sim_sierra_new (MMBaseModem *modem, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { g_async_initable_new_async (MM_TYPE_SIM_SIERRA, G_PRIORITY_DEFAULT, cancellable, callback, user_data, MM_BASE_SIM_MODEM, modem, "active", TRUE, /* by default always active */ NULL); } static void mm_sim_sierra_init (MMSimSierra *self) { } static void mm_sim_sierra_class_init (MMSimSierraClass *klass) { MMBaseSimClass *base_sim_class = MM_BASE_SIM_CLASS (klass); base_sim_class->load_sim_identifier = load_sim_identifier; base_sim_class->load_sim_identifier_finish = load_sim_identifier_finish; } ModemManager-1.23.4-dev/src/plugins/sierra/mm-sim-sierra.h000066400000000000000000000040011456466623000233100ustar00rootroot00000000000000/* -*- 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 - 2012 Red Hat, Inc. * Copyright (C) 2012 Lanedo GmbH */ #ifndef MM_SIM_SIERRA_H #define MM_SIM_SIERRA_H #include #include #include "mm-base-sim.h" #define MM_TYPE_SIM_SIERRA (mm_sim_sierra_get_type ()) #define MM_SIM_SIERRA(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_SIM_SIERRA, MMSimSierra)) #define MM_SIM_SIERRA_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_SIM_SIERRA, MMSimSierraClass)) #define MM_IS_SIM_SIERRA(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_SIM_SIERRA)) #define MM_IS_SIM_SIERRA_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_SIM_SIERRA)) #define MM_SIM_SIERRA_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_SIM_SIERRA, MMSimSierraClass)) typedef struct _MMSimSierra MMSimSierra; typedef struct _MMSimSierraClass MMSimSierraClass; struct _MMSimSierra { MMBaseSim parent; }; struct _MMSimSierraClass { MMBaseSimClass parent; }; GType mm_sim_sierra_get_type (void); void mm_sim_sierra_new (MMBaseModem *modem, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); MMBaseSim *mm_sim_sierra_new_finish (GAsyncResult *res, GError **error); #endif /* MM_SIM_SIERRA_H */ ModemManager-1.23.4-dev/src/plugins/sierra/tests/000077500000000000000000000000001456466623000216245ustar00rootroot00000000000000ModemManager-1.23.4-dev/src/plugins/sierra/tests/test-modem-helpers-sierra.c000066400000000000000000000073651456466623000270040ustar00rootroot00000000000000/* -*- 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) 2018 Aleksander Morgado */ #include #include #include #include #include #define _LIBMM_INSIDE_MM #include #include "mm-log-test.h" #include "mm-modem-helpers.h" #include "mm-modem-helpers-sierra.h" /*****************************************************************************/ /* Test !SCACT? responses */ static void test_scact_read_results (const gchar *desc, const gchar *reply, MM3gppPdpContextActive *expected_results, guint32 expected_results_len) { GList *l; GError *error = NULL; GList *results; g_debug ("\nTesting %s !SCACT response...\n", desc); results = mm_sierra_parse_scact_read_response (reply, &error); g_assert_no_error (error); if (expected_results_len) { g_assert (results); g_assert_cmpuint (g_list_length (results), ==, expected_results_len); } for (l = results; l; l = g_list_next (l)) { MM3gppPdpContextActive *pdp = l->data; gboolean found = FALSE; guint i; for (i = 0; !found && i < expected_results_len; i++) { MM3gppPdpContextActive *expected; expected = &expected_results[i]; if (pdp->cid == expected->cid) { found = TRUE; g_assert_cmpuint (pdp->active, ==, expected->active); } } g_assert (found == TRUE); } mm_3gpp_pdp_context_active_list_free (results); } static void test_scact_read_response_none (void) { test_scact_read_results ("none", "", NULL, 0); } static void test_scact_read_response_single_inactive (void) { const gchar *reply = "!SCACT: 1,0\r\n"; static MM3gppPdpContextActive expected[] = { { 1, FALSE }, }; test_scact_read_results ("single inactive", reply, &expected[0], G_N_ELEMENTS (expected)); } static void test_scact_read_response_single_active (void) { const gchar *reply = "!SCACT: 1,1\r\n"; static MM3gppPdpContextActive expected[] = { { 1, TRUE }, }; test_scact_read_results ("single active", reply, &expected[0], G_N_ELEMENTS (expected)); } static void test_scact_read_response_multiple (void) { const gchar *reply = "!SCACT: 1,0\r\n" "!SCACT: 4,1\r\n" "!SCACT: 5,0\r\n"; static MM3gppPdpContextActive expected[] = { { 1, FALSE }, { 4, TRUE }, { 5, FALSE }, }; test_scact_read_results ("multiple", reply, &expected[0], G_N_ELEMENTS (expected)); } /*****************************************************************************/ int main (int argc, char **argv) { setlocale (LC_ALL, ""); g_test_init (&argc, &argv, NULL); g_test_add_func ("/MM/sierra/scact/read/none", test_scact_read_response_none); g_test_add_func ("/MM/sierra/scact/read/single-inactive", test_scact_read_response_single_inactive); g_test_add_func ("/MM/sierra/scact/read/single-active", test_scact_read_response_single_active); g_test_add_func ("/MM/sierra/scact/read/multiple", test_scact_read_response_multiple); return g_test_run (); } ModemManager-1.23.4-dev/src/plugins/simtech/000077500000000000000000000000001456466623000206315ustar00rootroot00000000000000ModemManager-1.23.4-dev/src/plugins/simtech/77-mm-simtech-port-types.rules000066400000000000000000000102011456466623000263210ustar00rootroot00000000000000# do not edit this file, it will be overwritten on update # Simtech makes modules that other companies rebrand, like: # # A-LINK 3GU # SCT UM300 # # Most of these values were scraped from various SimTech-based Windows # driver .inf files. *mdm.inf lists the main command ports, while # *ser.inf lists the aux ports that may be used for PPP. ACTION!="add|change|move|bind", GOTO="mm_simtech_port_types_end" SUBSYSTEMS=="usb", ATTRS{idVendor}=="1e0e", GOTO="mm_simtech_port_types" GOTO="mm_simtech_port_types_end" LABEL="mm_simtech_port_types" SUBSYSTEMS=="usb", ATTRS{bInterfaceNumber}=="?*", ENV{.MM_USBIFNUM}="$attr{bInterfaceNumber}" # A-LINK 3GU ATTRS{idVendor}=="1e0e", ATTRS{idProduct}=="cefe", ENV{.MM_USBIFNUM}=="02", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1" ATTRS{idVendor}=="1e0e", ATTRS{idProduct}=="cefe", ENV{.MM_USBIFNUM}=="00", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1" ATTRS{idVendor}=="1e0e", ATTRS{idProduct}=="cefe", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1" # Prolink PH-300 ATTRS{idVendor}=="1e0e", ATTRS{idProduct}=="9100", ENV{.MM_USBIFNUM}=="03", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1" ATTRS{idVendor}=="1e0e", ATTRS{idProduct}=="9100", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1" # SCT UM300 ATTRS{idVendor}=="1e0e", ATTRS{idProduct}=="9200", ENV{.MM_USBIFNUM}=="03", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1" ATTRS{idVendor}=="1e0e", ATTRS{idProduct}=="9200", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1" # SIM7000, SIM7100, SIM7600... ATTRS{idVendor}=="1e0e", ATTRS{idProduct}=="9001", ENV{.MM_USBIFNUM}=="00", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_QCDM}="1" ATTRS{idVendor}=="1e0e", ATTRS{idProduct}=="9001", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_GPS}="1" ATTRS{idVendor}=="1e0e", ATTRS{idProduct}=="9001", ENV{.MM_USBIFNUM}=="02", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1" ATTRS{idVendor}=="1e0e", ATTRS{idProduct}=="9001", ENV{.MM_USBIFNUM}=="03", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1" ATTRS{idVendor}=="1e0e", ATTRS{idProduct}=="9001", ENV{.MM_USBIFNUM}=="04", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AUDIO}="1" # SIM7070, SIM7080, SIM7090 (default layout)... ATTRS{idVendor}=="1e0e", ATTRS{idProduct}=="9205", ENV{.MM_USBIFNUM}=="00", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_QCDM}="1" ATTRS{idVendor}=="1e0e", ATTRS{idProduct}=="9205", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_GPS}="1" ATTRS{idVendor}=="1e0e", ATTRS{idProduct}=="9205", ENV{.MM_USBIFNUM}=="02", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1" ATTRS{idVendor}=="1e0e", ATTRS{idProduct}=="9205", ENV{.MM_USBIFNUM}=="03", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PPP}="1" # Disable CPOL based features ATTRS{idVendor}=="1e0e", ATTRS{idProduct}=="9205", ENV{ID_MM_PREFERRED_NETWORKS_CPOL_DISABLED}="1" # SIM7070, SIM7080, SIM7090 (secondary layout)... ATTRS{idVendor}=="1e0e", ATTRS{idProduct}=="9206", ENV{.MM_USBIFNUM}=="00", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_QCDM}="1" ATTRS{idVendor}=="1e0e", ATTRS{idProduct}=="9206", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_GPS}="1" ATTRS{idVendor}=="1e0e", ATTRS{idProduct}=="9206", ENV{.MM_USBIFNUM}=="02", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1" ATTRS{idVendor}=="1e0e", ATTRS{idProduct}=="9206", ENV{.MM_USBIFNUM}=="03", ENV{ID_MM_PORT_IGNORE}="1" ATTRS{idVendor}=="1e0e", ATTRS{idProduct}=="9206", ENV{.MM_USBIFNUM}=="04", ENV{ID_MM_PORT_IGNORE}="1" ATTRS{idVendor}=="1e0e", ATTRS{idProduct}=="9206", ENV{.MM_USBIFNUM}=="05", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PPP}="1" # Disable CPOL based features ATTRS{idVendor}=="1e0e", ATTRS{idProduct}=="9206", ENV{ID_MM_PREFERRED_NETWORKS_CPOL_DISABLED}="1" # SIM A7600E-H, A7602E-H ATTRS{idVendor}=="1e0e", ATTRS{idProduct}=="9011", ENV{.MM_USBIFNUM}=="02", ENV{ID_MM_PORT_IGNORE}="1" ATTRS{idVendor}=="1e0e", ATTRS{idProduct}=="9011", ENV{.MM_USBIFNUM}=="04", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1" ATTRS{idVendor}=="1e0e", ATTRS{idProduct}=="9011", ENV{.MM_USBIFNUM}=="05", ENV{ID_MM_PORT_TYPE_AT_PPP}="1" LABEL="mm_simtech_port_types_end" ModemManager-1.23.4-dev/src/plugins/simtech/mm-broadband-modem-qmi-simtech.c000066400000000000000000000134221456466623000266370ustar00rootroot00000000000000/* -*- 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) 2019 Aleksander Morgado */ #include #include #include #include #include #include #include "ModemManager.h" #include "mm-errors-types.h" #include "mm-iface-modem-location.h" #include "mm-iface-modem-voice.h" #include "mm-broadband-modem-qmi-simtech.h" #include "mm-shared-simtech.h" static void iface_modem_location_init (MMIfaceModemLocation *iface); static void iface_modem_voice_init (MMIfaceModemVoice *iface); static void shared_simtech_init (MMSharedSimtech *iface); static MMIfaceModemLocation *iface_modem_location_parent; static MMIfaceModemVoice *iface_modem_voice_parent; G_DEFINE_TYPE_EXTENDED (MMBroadbandModemQmiSimtech, mm_broadband_modem_qmi_simtech, MM_TYPE_BROADBAND_MODEM_QMI, 0, G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_LOCATION, iface_modem_location_init) G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_VOICE, iface_modem_voice_init) G_IMPLEMENT_INTERFACE (MM_TYPE_SHARED_SIMTECH, shared_simtech_init)) /*****************************************************************************/ MMBroadbandModemQmiSimtech * mm_broadband_modem_qmi_simtech_new (const gchar *device, const gchar *physdev, const gchar **drivers, const gchar *plugin, guint16 vendor_id, guint16 product_id) { return g_object_new (MM_TYPE_BROADBAND_MODEM_QMI_SIMTECH, MM_BASE_MODEM_DEVICE, device, MM_BASE_MODEM_PHYSDEV, physdev, MM_BASE_MODEM_DRIVERS, drivers, MM_BASE_MODEM_PLUGIN, plugin, MM_BASE_MODEM_VENDOR_ID, vendor_id, MM_BASE_MODEM_PRODUCT_ID, product_id, /* QMI modem supports NET only */ MM_BASE_MODEM_DATA_NET_SUPPORTED, TRUE, MM_BASE_MODEM_DATA_TTY_SUPPORTED, FALSE, MM_BROADBAND_MODEM_INDICATORS_DISABLED, TRUE, MM_IFACE_MODEM_SIM_HOT_SWAP_SUPPORTED, TRUE, NULL); } static void mm_broadband_modem_qmi_simtech_init (MMBroadbandModemQmiSimtech *self) { } static void iface_modem_location_init (MMIfaceModemLocation *iface) { iface_modem_location_parent = g_type_interface_peek_parent (iface); iface->load_capabilities = mm_shared_simtech_location_load_capabilities; iface->load_capabilities_finish = mm_shared_simtech_location_load_capabilities_finish; iface->enable_location_gathering = mm_shared_simtech_enable_location_gathering; iface->enable_location_gathering_finish = mm_shared_simtech_enable_location_gathering_finish; iface->disable_location_gathering = mm_shared_simtech_disable_location_gathering; iface->disable_location_gathering_finish = mm_shared_simtech_disable_location_gathering_finish; } static MMIfaceModemLocation * peek_parent_location_interface (MMSharedSimtech *self) { return iface_modem_location_parent; } static void iface_modem_voice_init (MMIfaceModemVoice *iface) { iface_modem_voice_parent = g_type_interface_peek_parent (iface); iface->check_support = mm_shared_simtech_voice_check_support; iface->check_support_finish = mm_shared_simtech_voice_check_support_finish; iface->enable_unsolicited_events = mm_shared_simtech_voice_enable_unsolicited_events; iface->enable_unsolicited_events_finish = mm_shared_simtech_voice_enable_unsolicited_events_finish; iface->disable_unsolicited_events = mm_shared_simtech_voice_disable_unsolicited_events; iface->disable_unsolicited_events_finish = mm_shared_simtech_voice_disable_unsolicited_events_finish; iface->setup_unsolicited_events = mm_shared_simtech_voice_setup_unsolicited_events; iface->setup_unsolicited_events_finish = mm_shared_simtech_voice_setup_unsolicited_events_finish; iface->cleanup_unsolicited_events = mm_shared_simtech_voice_cleanup_unsolicited_events; iface->cleanup_unsolicited_events_finish = mm_shared_simtech_voice_cleanup_unsolicited_events_finish; iface->setup_in_call_audio_channel = mm_shared_simtech_voice_setup_in_call_audio_channel; iface->setup_in_call_audio_channel_finish = mm_shared_simtech_voice_setup_in_call_audio_channel_finish; iface->cleanup_in_call_audio_channel = mm_shared_simtech_voice_cleanup_in_call_audio_channel; iface->cleanup_in_call_audio_channel_finish = mm_shared_simtech_voice_cleanup_in_call_audio_channel_finish; } static MMIfaceModemVoice * peek_parent_voice_interface (MMSharedSimtech *self) { return iface_modem_voice_parent; } static void shared_simtech_init (MMSharedSimtech *iface) { iface->peek_parent_location_interface = peek_parent_location_interface; iface->peek_parent_voice_interface = peek_parent_voice_interface; } static void mm_broadband_modem_qmi_simtech_class_init (MMBroadbandModemQmiSimtechClass *klass) { } ModemManager-1.23.4-dev/src/plugins/simtech/mm-broadband-modem-qmi-simtech.h000066400000000000000000000050021456466623000266370ustar00rootroot00000000000000/* -*- 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) 2019 Aleksander Morgado */ #ifndef MM_BROADBAND_MODEM_QMI_SIMTECH_QMI_H #define MM_BROADBAND_MODEM_QMI_SIMTECH_QMI_H #include "mm-broadband-modem-qmi.h" #define MM_TYPE_BROADBAND_MODEM_QMI_SIMTECH (mm_broadband_modem_qmi_simtech_get_type ()) #define MM_BROADBAND_MODEM_QMI_SIMTECH(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_BROADBAND_MODEM_QMI_SIMTECH, MMBroadbandModemQmiSimtech)) #define MM_BROADBAND_MODEM_QMI_SIMTECH_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_BROADBAND_MODEM_QMI_SIMTECH, MMBroadbandModemQmiSimtechClass)) #define MM_IS_BROADBAND_MODEM_QMI_SIMTECH(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_BROADBAND_MODEM_QMI_SIMTECH)) #define MM_IS_BROADBAND_MODEM_QMI_SIMTECH_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_BROADBAND_MODEM_QMI_SIMTECH)) #define MM_BROADBAND_MODEM_QMI_SIMTECH_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_BROADBAND_MODEM_QMI_SIMTECH, MMBroadbandModemQmiSimtechClass)) typedef struct _MMBroadbandModemQmiSimtech MMBroadbandModemQmiSimtech; typedef struct _MMBroadbandModemQmiSimtechClass MMBroadbandModemQmiSimtechClass; struct _MMBroadbandModemQmiSimtech { MMBroadbandModemQmi parent; }; struct _MMBroadbandModemQmiSimtechClass{ MMBroadbandModemQmiClass parent; }; GType mm_broadband_modem_qmi_simtech_get_type (void); MMBroadbandModemQmiSimtech *mm_broadband_modem_qmi_simtech_new (const gchar *device, const gchar *physdev, const gchar **drivers, const gchar *plugin, guint16 vendor_id, guint16 product_id); #endif /* MM_BROADBAND_MODEM_QMI_SIMTECH_H */ ModemManager-1.23.4-dev/src/plugins/simtech/mm-broadband-modem-simtech.c000066400000000000000000001442561456466623000260650ustar00rootroot00000000000000/* -*- 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 - 2012 Red Hat, Inc. * Copyright (C) 2012 Aleksander Morgado */ #include #include #include #include #include #include #define _LIBMM_INSIDE_MM #include #include "ModemManager.h" #include "mm-modem-helpers.h" #include "mm-log-object.h" #include "mm-base-modem-at.h" #include "mm-iface-modem.h" #include "mm-iface-modem-3gpp.h" #include "mm-iface-modem-location.h" #include "mm-iface-modem-voice.h" #include "mm-shared-simtech.h" #include "mm-broadband-modem-simtech.h" static void iface_modem_init (MMIfaceModem *iface); static void iface_modem_3gpp_init (MMIfaceModem3gpp *iface); static void iface_modem_location_init (MMIfaceModemLocation *iface); static void iface_modem_voice_init (MMIfaceModemVoice *iface); static void shared_simtech_init (MMSharedSimtech *iface); static MMIfaceModem *iface_modem_parent; static MMIfaceModem3gpp *iface_modem_3gpp_parent; static MMIfaceModemLocation *iface_modem_location_parent; static MMIfaceModemVoice *iface_modem_voice_parent; G_DEFINE_TYPE_EXTENDED (MMBroadbandModemSimtech, mm_broadband_modem_simtech, MM_TYPE_BROADBAND_MODEM, 0, G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM, iface_modem_init) G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_3GPP, iface_modem_3gpp_init) G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_LOCATION, iface_modem_location_init) G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_VOICE, iface_modem_voice_init) G_IMPLEMENT_INTERFACE (MM_TYPE_SHARED_SIMTECH, shared_simtech_init)) typedef enum { FEATURE_SUPPORT_UNKNOWN, FEATURE_NOT_SUPPORTED, FEATURE_SUPPORTED } FeatureSupport; struct _MMBroadbandModemSimtechPrivate { FeatureSupport cnsmod_support; FeatureSupport autocsq_support; GRegex *cnsmod_regex; GRegex *csq_regex; GRegex *ri_done_regex; GRegex *nitz_regex; GRegex *cpin_regex; MMModemLock sim_lock; }; typedef struct { const gchar *result; MMModemLock code; } CPinResult; static CPinResult unlock_results[] = { { "READY", MM_MODEM_LOCK_NONE }, { "SIM PIN2", MM_MODEM_LOCK_SIM_PIN2 }, { "SIM PUK2", MM_MODEM_LOCK_SIM_PUK2 }, { "SIM PIN", MM_MODEM_LOCK_SIM_PIN }, { "SIM PUK", MM_MODEM_LOCK_SIM_PUK }, { "PH-NETSUB PIN", MM_MODEM_LOCK_PH_NETSUB_PIN }, { "PH-NETSUB PUK", MM_MODEM_LOCK_PH_NETSUB_PUK }, { "PH-FSIM PIN", MM_MODEM_LOCK_PH_FSIM_PIN }, { "PH-FSIM PUK", MM_MODEM_LOCK_PH_FSIM_PUK }, { "PH-CORP PIN", MM_MODEM_LOCK_PH_CORP_PIN }, { "PH-CORP PUK", MM_MODEM_LOCK_PH_CORP_PUK }, { "PH-SIM PIN", MM_MODEM_LOCK_PH_SIM_PIN }, { "PH-NET PIN", MM_MODEM_LOCK_PH_NET_PIN }, { "PH-NET PUK", MM_MODEM_LOCK_PH_NET_PUK }, { "PH-SP PIN", MM_MODEM_LOCK_PH_SP_PIN }, { "PH-SP PUK", MM_MODEM_LOCK_PH_SP_PUK }, { NULL } }; /*****************************************************************************/ /* Setup/Cleanup unsolicited events (3GPP interface) */ static MMModemAccessTechnology simtech_act_to_mm_act (guint nsmod) { static const MMModemAccessTechnology simtech_act_to_mm_act_map[] = { [0] = MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN, [1] = MM_MODEM_ACCESS_TECHNOLOGY_GSM, [2] = MM_MODEM_ACCESS_TECHNOLOGY_GPRS, [3] = MM_MODEM_ACCESS_TECHNOLOGY_EDGE, [4] = MM_MODEM_ACCESS_TECHNOLOGY_UMTS, [5] = MM_MODEM_ACCESS_TECHNOLOGY_HSDPA, [6] = MM_MODEM_ACCESS_TECHNOLOGY_HSUPA, [7] = MM_MODEM_ACCESS_TECHNOLOGY_HSPA, [8] = MM_MODEM_ACCESS_TECHNOLOGY_LTE, }; return (nsmod < G_N_ELEMENTS (simtech_act_to_mm_act_map) ? simtech_act_to_mm_act_map[nsmod] : MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN); } static void simtech_tech_changed (MMPortSerialAt *port, GMatchInfo *match_info, MMBroadbandModemSimtech *self) { guint simtech_act = 0; if (!mm_get_uint_from_match_info (match_info, 1, &simtech_act)) return; mm_iface_modem_update_access_technologies ( MM_IFACE_MODEM (self), simtech_act_to_mm_act (simtech_act), MM_IFACE_MODEM_3GPP_ALL_ACCESS_TECHNOLOGIES_MASK); } static void simtech_signal_changed (MMPortSerialAt *port, GMatchInfo *match_info, MMBroadbandModemSimtech *self) { guint quality = 0; if (!mm_get_uint_from_match_info (match_info, 1, &quality)) return; if (quality != 99) quality = MM_CLAMP_HIGH (quality, 31) * 100 / 31; else quality = 0; mm_iface_modem_update_signal_quality (MM_IFACE_MODEM (self), quality); } static void simtech_cpin_changed (MMPortSerialAt *port, GMatchInfo *match_info, MMBroadbandModemSimtech *self) { g_autofree gchar *str = NULL; CPinResult *iter; str = mm_get_string_unquoted_from_match_info (match_info, 1); if (str) { iter = &unlock_results[0]; /* Translate the reply */ while (iter->result) { if (g_str_has_prefix (str, iter->result)) { self->priv->sim_lock = iter->code; return; } iter++; } } } static void set_unsolicited_events_handlers (MMBroadbandModemSimtech *self, gboolean enable) { MMPortSerialAt *ports[2]; guint i; ports[0] = mm_base_modem_peek_port_primary (MM_BASE_MODEM (self)); ports[1] = mm_base_modem_peek_port_secondary (MM_BASE_MODEM (self)); /* Enable unsolicited events in given port */ for (i = 0; i < G_N_ELEMENTS (ports); i++) { if (!ports[i]) continue; /* Access technology related */ mm_port_serial_at_add_unsolicited_msg_handler ( ports[i], self->priv->cnsmod_regex, enable ? (MMPortSerialAtUnsolicitedMsgFn)simtech_tech_changed : NULL, enable ? self : NULL, NULL); /* Signal quality related */ mm_port_serial_at_add_unsolicited_msg_handler ( ports[i], self->priv->csq_regex, enable ? (MMPortSerialAtUnsolicitedMsgFn)simtech_signal_changed : NULL, enable ? self : NULL, NULL); } } static gboolean modem_3gpp_setup_cleanup_unsolicited_events_finish (MMIfaceModem3gpp *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void parent_setup_unsolicited_events_ready (MMIfaceModem3gpp *self, GAsyncResult *res, GTask *task) { GError *error = NULL; if (!iface_modem_3gpp_parent->setup_unsolicited_events_finish (self, res, &error)) g_task_return_error (task, error); else { /* Our own setup now */ set_unsolicited_events_handlers (MM_BROADBAND_MODEM_SIMTECH (self), TRUE); g_task_return_boolean (task, TRUE); } g_object_unref (task); } static void modem_3gpp_setup_unsolicited_events (MMIfaceModem3gpp *self, GAsyncReadyCallback callback, gpointer user_data) { /* Chain up parent's setup */ iface_modem_3gpp_parent->setup_unsolicited_events ( self, (GAsyncReadyCallback)parent_setup_unsolicited_events_ready, g_task_new (self, NULL, callback, user_data)); } static void parent_cleanup_unsolicited_events_ready (MMIfaceModem3gpp *self, GAsyncResult *res, GTask *task) { GError *error = NULL; if (!iface_modem_3gpp_parent->cleanup_unsolicited_events_finish (self, res, &error)) g_task_return_error (task, error); else g_task_return_boolean (task, TRUE); g_object_unref (task); } static void modem_3gpp_cleanup_unsolicited_events (MMIfaceModem3gpp *self, GAsyncReadyCallback callback, gpointer user_data) { /* Our own cleanup first */ set_unsolicited_events_handlers (MM_BROADBAND_MODEM_SIMTECH (self), FALSE); /* And now chain up parent's cleanup */ iface_modem_3gpp_parent->cleanup_unsolicited_events ( self, (GAsyncReadyCallback)parent_cleanup_unsolicited_events_ready, g_task_new (self, NULL, callback, user_data)); } /*****************************************************************************/ /* Enable unsolicited events (3GPP interface) */ typedef enum { ENABLE_UNSOLICITED_EVENTS_STEP_FIRST, ENABLE_UNSOLICITED_EVENTS_STEP_PARENT, ENABLE_UNSOLICITED_EVENTS_STEP_CHECK_SUPPORT_CNSMOD, ENABLE_UNSOLICITED_EVENTS_STEP_ENABLE_CNSMOD, ENABLE_UNSOLICITED_EVENTS_STEP_CHECK_SUPPORT_AUTOCSQ, ENABLE_UNSOLICITED_EVENTS_STEP_ENABLE_AUTOCSQ, ENABLE_UNSOLICITED_EVENTS_STEP_LAST, } EnableUnsolicitedEventsStep; typedef struct { EnableUnsolicitedEventsStep step; } EnableUnsolicitedEventsContext; static gboolean modem_3gpp_enable_unsolicited_events_finish (MMIfaceModem3gpp *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void enable_unsolicited_events_context_step (GTask *task); static void autocsq_set_enabled_ready (MMBaseModem *self, GAsyncResult *res, GTask *task) { EnableUnsolicitedEventsContext *ctx; GError *error = NULL; gboolean csq_urcs_enabled = FALSE; ctx = g_task_get_task_data (task); if (!mm_base_modem_at_command_finish (self, res, &error)) { mm_obj_dbg (self, "couldn't enable automatic signal quality reporting: %s", error->message); g_error_free (error); } else csq_urcs_enabled = TRUE; /* Disable access technology polling if we can use the +CSQ URCs */ g_object_set (self, MM_IFACE_MODEM_PERIODIC_SIGNAL_CHECK_DISABLED, csq_urcs_enabled, NULL); /* go to next step */ ctx->step++; enable_unsolicited_events_context_step (task); } static void autocsq_test_ready (MMBaseModem *_self, GAsyncResult *res, GTask *task) { MMBroadbandModemSimtech *self; EnableUnsolicitedEventsContext *ctx; self = MM_BROADBAND_MODEM_SIMTECH (_self); ctx = g_task_get_task_data (task); if (!mm_base_modem_at_command_finish (_self, res, NULL)) self->priv->autocsq_support = FEATURE_NOT_SUPPORTED; else self->priv->autocsq_support = FEATURE_SUPPORTED; /* go to next step */ ctx->step++; enable_unsolicited_events_context_step (task); } static void cnsmod_set_enabled_ready (MMBaseModem *self, GAsyncResult *res, GTask *task) { EnableUnsolicitedEventsContext *ctx; GError *error = NULL; gboolean cnsmod_urcs_enabled = FALSE; ctx = g_task_get_task_data (task); if (!mm_base_modem_at_command_finish (self, res, &error)) { mm_obj_dbg (self, "couldn't enable automatic access technology reporting: %s", error->message); g_error_free (error); } else cnsmod_urcs_enabled = TRUE; /* Disable access technology polling if we can use the +CNSMOD URCs */ g_object_set (self, MM_IFACE_MODEM_PERIODIC_ACCESS_TECH_CHECK_DISABLED, cnsmod_urcs_enabled, NULL); /* go to next step */ ctx->step++; enable_unsolicited_events_context_step (task); } static void cnsmod_test_ready (MMBaseModem *_self, GAsyncResult *res, GTask *task) { MMBroadbandModemSimtech *self; EnableUnsolicitedEventsContext *ctx; self = MM_BROADBAND_MODEM_SIMTECH (_self); ctx = g_task_get_task_data (task); if (!mm_base_modem_at_command_finish (_self, res, NULL)) self->priv->cnsmod_support = FEATURE_NOT_SUPPORTED; else self->priv->cnsmod_support = FEATURE_SUPPORTED; /* go to next step */ ctx->step++; enable_unsolicited_events_context_step (task); } static void parent_enable_unsolicited_events_ready (MMIfaceModem3gpp *self, GAsyncResult *res, GTask *task) { EnableUnsolicitedEventsContext *ctx; GError *error = NULL; ctx = g_task_get_task_data (task); if (!iface_modem_3gpp_parent->enable_unsolicited_events_finish (self, res, &error)) { g_task_return_error (task, error); g_object_unref (task); return; } /* go to next step */ ctx->step++; enable_unsolicited_events_context_step (task); } static void enable_unsolicited_events_context_step (GTask *task) { MMBroadbandModemSimtech *self; EnableUnsolicitedEventsContext *ctx; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); switch (ctx->step) { case ENABLE_UNSOLICITED_EVENTS_STEP_FIRST: ctx->step++; /* fall through */ case ENABLE_UNSOLICITED_EVENTS_STEP_PARENT: iface_modem_3gpp_parent->enable_unsolicited_events ( MM_IFACE_MODEM_3GPP (self), (GAsyncReadyCallback)parent_enable_unsolicited_events_ready, task); return; case ENABLE_UNSOLICITED_EVENTS_STEP_CHECK_SUPPORT_CNSMOD: if (self->priv->cnsmod_support == FEATURE_SUPPORT_UNKNOWN) { mm_base_modem_at_command (MM_BASE_MODEM (self), "+CNSMOD=?", 3, TRUE, (GAsyncReadyCallback)cnsmod_test_ready, task); return; } ctx->step++; /* fall through */ case ENABLE_UNSOLICITED_EVENTS_STEP_ENABLE_CNSMOD: if (self->priv->cnsmod_support == FEATURE_SUPPORTED) { mm_base_modem_at_command (MM_BASE_MODEM (self), /* Autoreport +CNSMOD when it changes */ "+CNSMOD=1", 20, FALSE, (GAsyncReadyCallback)cnsmod_set_enabled_ready, task); return; } ctx->step++; /* fall through */ case ENABLE_UNSOLICITED_EVENTS_STEP_CHECK_SUPPORT_AUTOCSQ: if (self->priv->autocsq_support == FEATURE_SUPPORT_UNKNOWN) { mm_base_modem_at_command (MM_BASE_MODEM (self), "+AUTOCSQ=?", 3, TRUE, (GAsyncReadyCallback)autocsq_test_ready, task); return; } ctx->step++; /* fall through */ case ENABLE_UNSOLICITED_EVENTS_STEP_ENABLE_AUTOCSQ: if (self->priv->autocsq_support == FEATURE_SUPPORTED) { mm_base_modem_at_command (MM_BASE_MODEM (self), /* Autoreport+ CSQ (first arg), and only report when it changes (second arg) */ "+AUTOCSQ=1,1", 20, FALSE, (GAsyncReadyCallback)autocsq_set_enabled_ready, task); return; } ctx->step++; /* fall through */ case ENABLE_UNSOLICITED_EVENTS_STEP_LAST: g_task_return_boolean (task, TRUE); g_object_unref (task); return; default: g_assert_not_reached (); } } static void modem_3gpp_enable_unsolicited_events (MMIfaceModem3gpp *self, GAsyncReadyCallback callback, gpointer user_data) { EnableUnsolicitedEventsContext *ctx; GTask *task; task = g_task_new (self, NULL, callback, user_data); ctx = g_new (EnableUnsolicitedEventsContext, 1); ctx->step = ENABLE_UNSOLICITED_EVENTS_STEP_FIRST; g_task_set_task_data (task, ctx, g_free); enable_unsolicited_events_context_step (task); } /*****************************************************************************/ /* Disable unsolicited events (3GPP interface) */ typedef enum { DISABLE_UNSOLICITED_EVENTS_STEP_FIRST, DISABLE_UNSOLICITED_EVENTS_STEP_DISABLE_AUTOCSQ, DISABLE_UNSOLICITED_EVENTS_STEP_DISABLE_CNSMOD, DISABLE_UNSOLICITED_EVENTS_STEP_PARENT, DISABLE_UNSOLICITED_EVENTS_STEP_LAST, } DisableUnsolicitedEventsStep; typedef struct { DisableUnsolicitedEventsStep step; } DisableUnsolicitedEventsContext; static gboolean modem_3gpp_disable_unsolicited_events_finish (MMIfaceModem3gpp *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void disable_unsolicited_events_context_step (GTask *task); static void parent_disable_unsolicited_events_ready (MMIfaceModem3gpp *self, GAsyncResult *res, GTask *task) { DisableUnsolicitedEventsContext *ctx; GError *error = NULL; ctx = g_task_get_task_data (task); if (!iface_modem_3gpp_parent->disable_unsolicited_events_finish (self, res, &error)) { g_task_return_error (task, error); g_object_unref (task); return; } /* go to next step */ ctx->step++; disable_unsolicited_events_context_step (task); } static void cnsmod_set_disabled_ready (MMBaseModem *self, GAsyncResult *res, GTask *task) { DisableUnsolicitedEventsContext *ctx; GError *error = NULL; ctx = g_task_get_task_data (task); if (!mm_base_modem_at_command_finish (self, res, &error)) { mm_obj_dbg (self, "couldn't disable automatic access technology reporting: %s", error->message); g_error_free (error); } /* go to next step */ ctx->step++; disable_unsolicited_events_context_step (task); } static void autocsq_set_disabled_ready (MMBaseModem *self, GAsyncResult *res, GTask *task) { DisableUnsolicitedEventsContext *ctx; GError *error = NULL; ctx = g_task_get_task_data (task); if (!mm_base_modem_at_command_finish (self, res, &error)) { mm_obj_dbg (self, "couldn't disable automatic signal quality reporting: %s", error->message); g_error_free (error); } /* go to next step */ ctx->step++; disable_unsolicited_events_context_step (task); } static void disable_unsolicited_events_context_step (GTask *task) { MMBroadbandModemSimtech *self; DisableUnsolicitedEventsContext *ctx; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); switch (ctx->step) { case DISABLE_UNSOLICITED_EVENTS_STEP_FIRST: ctx->step++; /* fall through */ case DISABLE_UNSOLICITED_EVENTS_STEP_DISABLE_AUTOCSQ: if (self->priv->autocsq_support == FEATURE_SUPPORTED) { mm_base_modem_at_command (MM_BASE_MODEM (self), "+AUTOCSQ=0", 20, FALSE, (GAsyncReadyCallback)autocsq_set_disabled_ready, task); return; } ctx->step++; /* fall through */ case DISABLE_UNSOLICITED_EVENTS_STEP_DISABLE_CNSMOD: if (self->priv->cnsmod_support == FEATURE_SUPPORTED) { mm_base_modem_at_command (MM_BASE_MODEM (self), "+CNSMOD=0", 20, FALSE, (GAsyncReadyCallback)cnsmod_set_disabled_ready, task); return; } ctx->step++; /* fall through */ case DISABLE_UNSOLICITED_EVENTS_STEP_PARENT: iface_modem_3gpp_parent->disable_unsolicited_events ( MM_IFACE_MODEM_3GPP (self), (GAsyncReadyCallback)parent_disable_unsolicited_events_ready, task); return; case DISABLE_UNSOLICITED_EVENTS_STEP_LAST: g_task_return_boolean (task, TRUE); g_object_unref (task); return; default: g_assert_not_reached (); } } static void modem_3gpp_disable_unsolicited_events (MMIfaceModem3gpp *self, GAsyncReadyCallback callback, gpointer user_data) { DisableUnsolicitedEventsContext *ctx; GTask *task; task = g_task_new (self, NULL, callback, user_data); ctx = g_new (DisableUnsolicitedEventsContext, 1); ctx->step = DISABLE_UNSOLICITED_EVENTS_STEP_FIRST; g_task_set_task_data (task, ctx, g_free); disable_unsolicited_events_context_step (task); } /*****************************************************************************/ /* Load access technologies (Modem interface) */ static gboolean load_access_technologies_finish (MMIfaceModem *self, GAsyncResult *res, MMModemAccessTechnology *access_technologies, guint *mask, GError **error) { GError *inner_error = NULL; gssize act; act = g_task_propagate_int (G_TASK (res), &inner_error); if (inner_error) { g_propagate_error (error, inner_error); return FALSE; } *access_technologies = (MMModemAccessTechnology) act; *mask = MM_MODEM_ACCESS_TECHNOLOGY_ANY; return TRUE; } static void cnsmod_query_ready (MMBaseModem *self, GAsyncResult *res, GTask *task) { const gchar *response, *p; GError *error = NULL; response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error); if (!response) { g_task_return_error (task, error); g_object_unref (task); return; } p = mm_strip_tag (response, "+CNSMOD:"); if (p) p = strchr (p, ','); if (!p || !isdigit (*(p + 1))) g_task_return_new_error ( task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Failed to parse the +CNSMOD response: '%s'", response); else g_task_return_int (task, simtech_act_to_mm_act (atoi (p + 1))); g_object_unref (task); } static void load_access_technologies (MMIfaceModem *_self, GAsyncReadyCallback callback, gpointer user_data) { MMBroadbandModemSimtech *self; GTask *task; self = MM_BROADBAND_MODEM_SIMTECH (_self); task = g_task_new (self, NULL, callback, user_data); /* Launch query only for 3GPP modems */ if (!mm_iface_modem_is_3gpp (_self)) { g_task_return_int (task, MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN); g_object_unref (task); return; } g_assert (self->priv->cnsmod_support != FEATURE_SUPPORT_UNKNOWN); if (self->priv->cnsmod_support == FEATURE_NOT_SUPPORTED) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "Loading access technologies with +CNSMOD is not supported"); g_object_unref (task); return; } mm_base_modem_at_command ( MM_BASE_MODEM (self), "AT+CNSMOD?", 3, FALSE, (GAsyncReadyCallback)cnsmod_query_ready, task); } /*****************************************************************************/ /* Load signal quality (Modem interface) */ static guint load_signal_quality_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { gssize value; value = g_task_propagate_int (G_TASK (res), error); return value < 0 ? 0 : value; } static void csq_query_ready (MMBaseModem *self, GAsyncResult *res, GTask *task) { const gchar *response, *p; GError *error = NULL; gint quality; gint ber; response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error); if (!response) { g_task_return_error (task, error); g_object_unref (task); return; } /* Given that we may have enabled AUTOCSQ support, it is totally possible * to get an empty string at this point, because the +CSQ reply may have * been processed as an URC already. If we ever see this, we should not return * an error, because that would reset the reported signal quality to 0 :/ * So, in this case, return the last cached signal quality value. */ if (!response[0]) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_IN_PROGRESS, "already refreshed via URCs"); g_object_unref (task); return; } p = mm_strip_tag (response, "+CSQ:"); if (sscanf (p, "%d, %d", &quality, &ber)) { if (quality != 99) quality = CLAMP (quality, 0, 31) * 100 / 31; else quality = 0; g_task_return_int (task, quality); g_object_unref (task); return; } g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Could not parse signal quality results"); g_object_unref (task); } static void load_signal_quality (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; task = g_task_new (self, NULL, callback, user_data); mm_base_modem_at_command ( MM_BASE_MODEM (self), "+CSQ", 3, FALSE, (GAsyncReadyCallback)csq_query_ready, task); } /*****************************************************************************/ /* Load supported modes (Modem interface) */ static GArray * load_supported_modes_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { return g_task_propagate_pointer (G_TASK (res), error); } static void parent_load_supported_modes_ready (MMIfaceModem *self, GAsyncResult *res, GTask *task) { GError *error = NULL; GArray *all; GArray *combinations; GArray *filtered; MMModemModeCombination mode; all = iface_modem_parent->load_supported_modes_finish (self, res, &error); if (!all) { g_task_return_error (task, error); g_object_unref (task); return; } /* Build list of combinations */ combinations = g_array_sized_new (FALSE, FALSE, sizeof (MMModemModeCombination), 5); /* 2G only */ mode.allowed = MM_MODEM_MODE_2G; mode.preferred = MM_MODEM_MODE_NONE; g_array_append_val (combinations, mode); /* 3G only */ mode.allowed = MM_MODEM_MODE_3G; mode.preferred = MM_MODEM_MODE_NONE; g_array_append_val (combinations, mode); /* 2G and 3G */ mode.allowed = (MM_MODEM_MODE_2G | MM_MODEM_MODE_3G); mode.preferred = MM_MODEM_MODE_NONE; g_array_append_val (combinations, mode); /* 2G and 3G, 2G preferred */ mode.allowed = (MM_MODEM_MODE_2G | MM_MODEM_MODE_3G); mode.preferred = MM_MODEM_MODE_2G; g_array_append_val (combinations, mode); /* 2G and 3G, 3G preferred */ mode.allowed = (MM_MODEM_MODE_2G | MM_MODEM_MODE_3G); mode.preferred = MM_MODEM_MODE_3G; g_array_append_val (combinations, mode); /* Filter out those unsupported modes */ filtered = mm_filter_supported_modes (all, combinations, self); g_array_unref (all); g_array_unref (combinations); g_task_return_pointer (task, filtered, (GDestroyNotify) g_array_unref); g_object_unref (task); } static void load_supported_modes (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data) { /* Run parent's loading */ iface_modem_parent->load_supported_modes ( MM_IFACE_MODEM (self), (GAsyncReadyCallback)parent_load_supported_modes_ready, g_task_new (self, NULL, callback, user_data)); } /*****************************************************************************/ /* Load initial allowed/preferred modes (Modem interface) */ typedef struct { MMModemMode allowed; MMModemMode preferred; } LoadCurrentModesResult; typedef struct { gint acqord; gint modepref; } LoadCurrentModesContext; static gboolean load_current_modes_finish (MMIfaceModem *self, GAsyncResult *res, MMModemMode *allowed, MMModemMode *preferred, GError **error) { LoadCurrentModesResult *result; result = g_task_propagate_pointer (G_TASK (res), error); if (!result) return FALSE; *allowed = result->allowed; *preferred = result->preferred; g_free (result); return TRUE; } static void cnmp_query_ready (MMBroadbandModemSimtech *self, GAsyncResult *res, GTask *task) { LoadCurrentModesContext *ctx; LoadCurrentModesResult *result; const gchar *response, *p; GError *error = NULL; response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error); if (!response) { g_task_return_error (task, error); g_object_unref (task); return; } ctx = g_task_get_task_data (task); p = mm_strip_tag (response, "+CNMP:"); if (!p) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Failed to parse the mode preference response: '%s'", response); g_object_unref (task); return; } result = g_new (LoadCurrentModesResult, 1); result->allowed = MM_MODEM_MODE_NONE; result->preferred = MM_MODEM_MODE_NONE; ctx->modepref = atoi (p); switch (ctx->modepref) { case 2: /* Automatic */ switch (ctx->acqord) { case 0: result->allowed = MM_MODEM_MODE_ANY; result->preferred = MM_MODEM_MODE_NONE; break; case 1: result->allowed = (MM_MODEM_MODE_2G | MM_MODEM_MODE_3G); result->preferred = MM_MODEM_MODE_2G; break; case 2: result->allowed = (MM_MODEM_MODE_2G | MM_MODEM_MODE_3G); result->preferred = MM_MODEM_MODE_3G; break; default: g_task_return_new_error ( task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Unknown acquisition order preference: '%d'", ctx->acqord); g_object_unref (task); g_free (result); return; } break; case 13: /* GSM only */ result->allowed = MM_MODEM_MODE_2G; result->preferred = MM_MODEM_MODE_NONE; break; case 14: /* WCDMA only */ result->allowed = MM_MODEM_MODE_3G; result->preferred = MM_MODEM_MODE_NONE; break; default: g_task_return_new_error ( task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Unknown mode preference: '%d'", ctx->modepref); g_object_unref (task); g_free (result); return; } g_task_return_pointer (task, result, g_free); g_object_unref (task); } static void cnaop_query_ready (MMBaseModem *self, GAsyncResult *res, GTask *task) { LoadCurrentModesContext *ctx; const gchar *response, *p; GError *error = NULL; response = mm_base_modem_at_command_finish (self, res, &error); if (!response) { g_task_return_error (task, error); g_object_unref (task); return; } ctx = g_task_get_task_data (task); p = mm_strip_tag (response, "+CNAOP:"); if (p) ctx->acqord = atoi (p); if (ctx->acqord < 0 || ctx->acqord > 2) { g_task_return_new_error ( task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Failed to parse the acquisition order response: '%s'", response); g_object_unref (task); return; } mm_base_modem_at_command ( MM_BASE_MODEM (self), "+CNMP?", 3, FALSE, (GAsyncReadyCallback)cnmp_query_ready, task); } static void load_current_modes (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; LoadCurrentModesContext *ctx; ctx = g_new (LoadCurrentModesContext, 1); ctx->acqord = -1; ctx->modepref = -1; task = g_task_new (self, NULL, callback, user_data); g_task_set_task_data (task, ctx, g_free); mm_base_modem_at_command ( MM_BASE_MODEM (self), "+CNAOP?", 3, FALSE, (GAsyncReadyCallback)cnaop_query_ready, task); } /*****************************************************************************/ /* Check unlock required (Modem interface) */ static MMModemLock load_unlock_required_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { GError *inner_error = NULL; gssize value; value = g_task_propagate_int (G_TASK (res), &inner_error); if (inner_error) { g_propagate_error (error, inner_error); return MM_MODEM_LOCK_UNKNOWN; } return (MMModemLock)value; } static void cpin_query_ready (MMIfaceModem *_self, GAsyncResult *res, GTask *task) { GError *error = NULL; MMBroadbandModemSimtech *self; mm_base_modem_at_command_finish (MM_BASE_MODEM (_self), res, &error); if (error) { g_task_return_error (task, error); g_object_unref (task); return; } self = MM_BROADBAND_MODEM_SIMTECH (_self); if (self->priv->sim_lock == MM_MODEM_LOCK_UNKNOWN) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Unable to get SIM lock status"); } else { g_task_return_int (task, self->priv->sim_lock); } g_object_unref (task); } static void load_unlock_required (MMIfaceModem *self, gboolean last_attempt, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; task = g_task_new (self, cancellable, callback, user_data); mm_obj_dbg (self, "checking if unlock required..."); mm_base_modem_at_command (MM_BASE_MODEM (self), "+CPIN?", 10, FALSE, (GAsyncReadyCallback)cpin_query_ready, task); } /*****************************************************************************/ /* Reset (Modem interface) */ static gboolean reset_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { return !!mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, error); } static void reset (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data) { mm_base_modem_at_command (MM_BASE_MODEM (self), "+CRESET", 9, FALSE, callback, user_data); } /*****************************************************************************/ /* Set allowed modes (Modem interface) */ typedef struct { guint nmp; /* mode preference */ guint naop; /* acquisition order */ } SetCurrentModesContext; static gboolean set_current_modes_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void cnaop_set_ready (MMBaseModem *self, GAsyncResult *res, GTask *task) { GError *error = NULL; mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error); if (error) g_task_return_error (task, error); else g_task_return_boolean (task, TRUE); g_object_unref (task); } static void cnmp_set_ready (MMBaseModem *self, GAsyncResult *res, GTask *task) { SetCurrentModesContext *ctx; GError *error = NULL; gchar *command; ctx = g_task_get_task_data (task); mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error); if (error) { /* Let the error be critical. */ g_task_return_error (task, error); g_object_unref (task); return; } command = g_strdup_printf ("+CNAOP=%u", ctx->naop); mm_base_modem_at_command ( MM_BASE_MODEM (self), command, 3, FALSE, (GAsyncReadyCallback)cnaop_set_ready, task); g_free (command); } static void set_current_modes (MMIfaceModem *self, MMModemMode allowed, MMModemMode preferred, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; SetCurrentModesContext *ctx; gchar *command; /* Defaults: automatic search */ ctx = g_new (SetCurrentModesContext, 1); ctx->nmp = 2; ctx->naop = 0; task = g_task_new (self, NULL, callback, user_data); g_task_set_task_data (task, ctx, g_free); if (allowed == MM_MODEM_MODE_ANY && preferred == MM_MODEM_MODE_NONE) { /* defaults nmp and naop */ } else if (allowed == MM_MODEM_MODE_2G) { ctx->nmp = 13; ctx->naop = 0; } else if (allowed == MM_MODEM_MODE_3G) { ctx->nmp = 14; ctx->naop = 0; } else if (allowed == (MM_MODEM_MODE_2G | MM_MODEM_MODE_3G)) { /* default nmp */ if (preferred == MM_MODEM_MODE_2G) ctx->naop = 3; else if (preferred == MM_MODEM_MODE_3G) ctx->naop = 2; else /* default naop */ ctx->naop = 0; } else { gchar *allowed_str; gchar *preferred_str; allowed_str = mm_modem_mode_build_string_from_mask (allowed); preferred_str = mm_modem_mode_build_string_from_mask (preferred); g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Requested mode (allowed: '%s', preferred: '%s') not " "supported by the modem.", allowed_str, preferred_str); g_object_unref (task); g_free (allowed_str); g_free (preferred_str); return; } command = g_strdup_printf ("+CNMP=%u", ctx->nmp); mm_base_modem_at_command ( MM_BASE_MODEM (self), command, 3, FALSE, (GAsyncReadyCallback)cnmp_set_ready, task); g_free (command); } /*****************************************************************************/ /* Setup ports (Broadband modem class) */ static void setup_ports (MMBroadbandModem *self) { MMBroadbandModemSimtech *modem = (MM_BROADBAND_MODEM_SIMTECH (self)); MMPortSerialAt *ports[2]; guint i; /* Call parent's setup ports first always */ MM_BROADBAND_MODEM_CLASS (mm_broadband_modem_simtech_parent_class)->setup_ports (self); /* Now reset the unsolicited messages we'll handle when enabled */ set_unsolicited_events_handlers (MM_BROADBAND_MODEM_SIMTECH (self), FALSE); ports[0] = mm_base_modem_peek_port_primary (MM_BASE_MODEM (modem)); ports[1] = mm_base_modem_peek_port_secondary (MM_BASE_MODEM (modem)); for (i = 0; i < G_N_ELEMENTS (ports); i++) { if (!ports[i]) continue; /* Ignore PB DONE and SMS DONE */ mm_port_serial_at_add_unsolicited_msg_handler ( ports[i], modem->priv->ri_done_regex, NULL, NULL, NULL); /* Ignore +NITZ: */ mm_port_serial_at_add_unsolicited_msg_handler ( ports[i], modem->priv->nitz_regex, NULL, NULL, NULL); /* URC +CPIN: */ mm_port_serial_at_add_unsolicited_msg_handler ( ports[i], modem->priv->cpin_regex, (MMPortSerialAtUnsolicitedMsgFn)simtech_cpin_changed, self, NULL); } } /*****************************************************************************/ MMBroadbandModemSimtech * mm_broadband_modem_simtech_new (const gchar *device, const gchar *physdev, const gchar **drivers, const gchar *plugin, guint16 vendor_id, guint16 product_id) { return g_object_new (MM_TYPE_BROADBAND_MODEM_SIMTECH, MM_BASE_MODEM_DEVICE, device, MM_BASE_MODEM_PHYSDEV, physdev, MM_BASE_MODEM_DRIVERS, drivers, MM_BASE_MODEM_PLUGIN, plugin, MM_BASE_MODEM_VENDOR_ID, vendor_id, MM_BASE_MODEM_PRODUCT_ID, product_id, /* Generic bearer supports TTY only */ MM_BASE_MODEM_DATA_NET_SUPPORTED, FALSE, MM_BASE_MODEM_DATA_TTY_SUPPORTED, TRUE, MM_BROADBAND_MODEM_INDICATORS_DISABLED, TRUE, NULL); } static void mm_broadband_modem_simtech_init (MMBroadbandModemSimtech *self) { /* Initialize private data */ self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, MM_TYPE_BROADBAND_MODEM_SIMTECH, MMBroadbandModemSimtechPrivate); self->priv->cnsmod_support = FEATURE_SUPPORT_UNKNOWN; self->priv->autocsq_support = FEATURE_SUPPORT_UNKNOWN; self->priv->cnsmod_regex = g_regex_new ("\\r\\n\\+CNSMOD:\\s*(\\d+)\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); self->priv->csq_regex = g_regex_new ("\\r\\n\\+CSQ:\\s*(\\d+),(\\d+)\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); self->priv->ri_done_regex = g_regex_new ("\\r\\n(PB DONE)|(SMS DONE)\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); self->priv->nitz_regex = g_regex_new ("\\r\\n\\+NITZ:(.*)\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); self->priv->cpin_regex = g_regex_new ("\\r\\n\\+CPIN: (.*)\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); self->priv->sim_lock = MM_MODEM_LOCK_UNKNOWN; } static void finalize (GObject *object) { MMBroadbandModemSimtech *self = MM_BROADBAND_MODEM_SIMTECH (object); g_regex_unref (self->priv->cnsmod_regex); g_regex_unref (self->priv->csq_regex); g_regex_unref (self->priv->ri_done_regex); g_regex_unref (self->priv->nitz_regex); g_regex_unref (self->priv->cpin_regex); G_OBJECT_CLASS (mm_broadband_modem_simtech_parent_class)->finalize (object); } static void iface_modem_init (MMIfaceModem *iface) { iface_modem_parent = g_type_interface_peek_parent (iface); iface->load_signal_quality = load_signal_quality; iface->load_signal_quality_finish = load_signal_quality_finish; iface->load_access_technologies = load_access_technologies; iface->load_access_technologies_finish = load_access_technologies_finish; iface->load_supported_modes = load_supported_modes; iface->load_supported_modes_finish = load_supported_modes_finish; iface->load_current_modes = load_current_modes; iface->load_current_modes_finish = load_current_modes_finish; iface->set_current_modes = set_current_modes; iface->set_current_modes_finish = set_current_modes_finish; iface->reset = reset; iface->reset_finish = reset_finish; iface->load_unlock_required = load_unlock_required; iface->load_unlock_required_finish = load_unlock_required_finish; } static void iface_modem_3gpp_init (MMIfaceModem3gpp *iface) { iface_modem_3gpp_parent = g_type_interface_peek_parent (iface); iface->setup_unsolicited_events = modem_3gpp_setup_unsolicited_events; iface->setup_unsolicited_events_finish = modem_3gpp_setup_cleanup_unsolicited_events_finish; iface->cleanup_unsolicited_events = modem_3gpp_cleanup_unsolicited_events; iface->cleanup_unsolicited_events_finish = modem_3gpp_setup_cleanup_unsolicited_events_finish; iface->enable_unsolicited_events = modem_3gpp_enable_unsolicited_events; iface->enable_unsolicited_events_finish = modem_3gpp_enable_unsolicited_events_finish; iface->disable_unsolicited_events = modem_3gpp_disable_unsolicited_events; iface->disable_unsolicited_events_finish = modem_3gpp_disable_unsolicited_events_finish; } static void iface_modem_location_init (MMIfaceModemLocation *iface) { iface_modem_location_parent = g_type_interface_peek_parent (iface); iface->load_capabilities = mm_shared_simtech_location_load_capabilities; iface->load_capabilities_finish = mm_shared_simtech_location_load_capabilities_finish; iface->enable_location_gathering = mm_shared_simtech_enable_location_gathering; iface->enable_location_gathering_finish = mm_shared_simtech_enable_location_gathering_finish; iface->disable_location_gathering = mm_shared_simtech_disable_location_gathering; iface->disable_location_gathering_finish = mm_shared_simtech_disable_location_gathering_finish; } static MMIfaceModemLocation * peek_parent_location_interface (MMSharedSimtech *self) { return iface_modem_location_parent; } static void iface_modem_voice_init (MMIfaceModemVoice *iface) { iface_modem_voice_parent = g_type_interface_peek_parent (iface); iface->check_support = mm_shared_simtech_voice_check_support; iface->check_support_finish = mm_shared_simtech_voice_check_support_finish; iface->enable_unsolicited_events = mm_shared_simtech_voice_enable_unsolicited_events; iface->enable_unsolicited_events_finish = mm_shared_simtech_voice_enable_unsolicited_events_finish; iface->disable_unsolicited_events = mm_shared_simtech_voice_disable_unsolicited_events; iface->disable_unsolicited_events_finish = mm_shared_simtech_voice_disable_unsolicited_events_finish; iface->setup_unsolicited_events = mm_shared_simtech_voice_setup_unsolicited_events; iface->setup_unsolicited_events_finish = mm_shared_simtech_voice_setup_unsolicited_events_finish; iface->cleanup_unsolicited_events = mm_shared_simtech_voice_cleanup_unsolicited_events; iface->cleanup_unsolicited_events_finish = mm_shared_simtech_voice_cleanup_unsolicited_events_finish; iface->setup_in_call_audio_channel = mm_shared_simtech_voice_setup_in_call_audio_channel; iface->setup_in_call_audio_channel_finish = mm_shared_simtech_voice_setup_in_call_audio_channel_finish; iface->cleanup_in_call_audio_channel = mm_shared_simtech_voice_cleanup_in_call_audio_channel; iface->cleanup_in_call_audio_channel_finish = mm_shared_simtech_voice_cleanup_in_call_audio_channel_finish; } static MMIfaceModemVoice * peek_parent_voice_interface (MMSharedSimtech *self) { return iface_modem_voice_parent; } static void shared_simtech_init (MMSharedSimtech *iface) { iface->peek_parent_location_interface = peek_parent_location_interface; iface->peek_parent_voice_interface = peek_parent_voice_interface; } static void mm_broadband_modem_simtech_class_init (MMBroadbandModemSimtechClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); MMBroadbandModemClass *broadband_modem_class = MM_BROADBAND_MODEM_CLASS (klass); g_type_class_add_private (object_class, sizeof (MMBroadbandModemSimtechPrivate)); object_class->finalize = finalize; broadband_modem_class->setup_ports = setup_ports; } ModemManager-1.23.4-dev/src/plugins/simtech/mm-broadband-modem-simtech.h000066400000000000000000000050441456466623000260610ustar00rootroot00000000000000/* -*- 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 - 2012 Red Hat, Inc. * Copyright (C) 2012 Aleksander Morgado */ #ifndef MM_BROADBAND_MODEM_SIMTECH_H #define MM_BROADBAND_MODEM_SIMTECH_H #include "mm-broadband-modem.h" #define MM_TYPE_BROADBAND_MODEM_SIMTECH (mm_broadband_modem_simtech_get_type ()) #define MM_BROADBAND_MODEM_SIMTECH(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_BROADBAND_MODEM_SIMTECH, MMBroadbandModemSimtech)) #define MM_BROADBAND_MODEM_SIMTECH_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_BROADBAND_MODEM_SIMTECH, MMBroadbandModemSimtechClass)) #define MM_IS_BROADBAND_MODEM_SIMTECH(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_BROADBAND_MODEM_SIMTECH)) #define MM_IS_BROADBAND_MODEM_SIMTECH_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_BROADBAND_MODEM_SIMTECH)) #define MM_BROADBAND_MODEM_SIMTECH_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_BROADBAND_MODEM_SIMTECH, MMBroadbandModemSimtechClass)) typedef struct _MMBroadbandModemSimtech MMBroadbandModemSimtech; typedef struct _MMBroadbandModemSimtechClass MMBroadbandModemSimtechClass; typedef struct _MMBroadbandModemSimtechPrivate MMBroadbandModemSimtechPrivate; struct _MMBroadbandModemSimtech { MMBroadbandModem parent; MMBroadbandModemSimtechPrivate *priv; }; struct _MMBroadbandModemSimtechClass{ MMBroadbandModemClass parent; }; GType mm_broadband_modem_simtech_get_type (void); MMBroadbandModemSimtech *mm_broadband_modem_simtech_new (const gchar *device, const gchar *physdev, const gchar **drivers, const gchar *plugin, guint16 vendor_id, guint16 product_id); #endif /* MM_BROADBAND_MODEM_SIMTECH_H */ ModemManager-1.23.4-dev/src/plugins/simtech/mm-modem-helpers-simtech.c000066400000000000000000000140601456466623000256000ustar00rootroot00000000000000/* -*- 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) 2019 Aleksander Morgado */ #include #include #include #include #include "ModemManager.h" #define _LIBMM_INSIDE_MM #include #include "mm-errors-types.h" #include "mm-modem-helpers-simtech.h" #include "mm-modem-helpers.h" /*****************************************************************************/ /* +CLCC test parser * * Example (SIM7600E): * AT+CLCC=? * +CLCC: (0-1) */ gboolean mm_simtech_parse_clcc_test (const gchar *response, gboolean *clcc_urcs_supported, GError **error) { g_assert (response); response = mm_strip_tag (response, "+CLCC:"); /* 3GPP specifies that the output of AT+CLCC=? should be just OK, so support * that */ if (!response[0]) { *clcc_urcs_supported = FALSE; return TRUE; } /* As per 3GPP TS 27.007, the AT+CLCC command doesn't expect any argument, * as it only is designed to report the current call list, nothing else. * In the case of the Simtech plugin, though, we are going to support +CLCC * URCs that can be enabled/disabled via AT+CLCC=1/0. We therefore need to * detect whether this URC management is possible or not, for now with a * simple check looking for the specific "(0-1)" string. */ if (!strncmp (response, "(0-1)", 5)) { *clcc_urcs_supported = TRUE; return TRUE; } g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "unexpected +CLCC test response: '%s'", response); return FALSE; } /*****************************************************************************/ GRegex * mm_simtech_get_clcc_urc_regex (void) { return g_regex_new ("\\r\\n(\\+CLCC: .*\\r\\n)+", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); } gboolean mm_simtech_parse_clcc_list (const gchar *str, gpointer log_object, GList **out_list, GError **error) { /* Parse the URC contents as a plain +CLCC response, but make sure to skip first * EOL in the string because the plain +CLCC response would never have that. */ return mm_3gpp_parse_clcc_response (mm_strip_tag (str, "\r\n"), log_object, out_list, error); } void mm_simtech_call_info_list_free (GList *call_info_list) { mm_3gpp_call_info_list_free (call_info_list); } /*****************************************************************************/ /* * VOICE CALL: BEGIN * VOICE CALL: END: 000041 */ GRegex * mm_simtech_get_voice_call_urc_regex (void) { return g_regex_new ("\\r\\nVOICE CALL:\\s*([A-Z]+)(?::\\s*(\\d+))?\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); } gboolean mm_simtech_parse_voice_call_urc (GMatchInfo *match_info, gboolean *start_or_stop, guint *duration, GError **error) { GError *inner_error = NULL; gchar *str; str = mm_get_string_unquoted_from_match_info (match_info, 1); if (!str) { inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't read voice call URC action"); goto out; } if (g_strcmp0 (str, "BEGIN") == 0) { *start_or_stop = TRUE; *duration = 0; goto out; } if (g_strcmp0 (str, "END") == 0) { *start_or_stop = FALSE; if (!mm_get_uint_from_match_info (match_info, 2, duration)) *duration = 0; goto out; } inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Unknown voice call URC action: %s", str); out: g_free (str); if (inner_error) { g_propagate_error (error, inner_error); return FALSE; } return TRUE; } /*****************************************************************************/ /* * MISSED_CALL: 11:01AM 07712345678 */ GRegex * mm_simtech_get_missed_call_urc_regex (void) { return g_regex_new ("\\r\\nMISSED_CALL:\\s*(.+)\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); } gboolean mm_simtech_parse_missed_call_urc (GMatchInfo *match_info, gchar **details, GError **error) { gchar *str; str = mm_get_string_unquoted_from_match_info (match_info, 1); if (!str) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't read missed call URC details"); return FALSE; } *details = str; return TRUE; } /*****************************************************************************/ /* * Using TWO instead of one... * +CRING: VOICE */ GRegex * mm_simtech_get_cring_urc_regex (void) { return g_regex_new ("(?:\\r)+\\n\\+CRING:\\s*(\\S+)(?:\\r)+\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); } /*****************************************************************************/ /* * +RXDTMF: 8 * +RXDTMF: * * +RXDTMF: 7 * * Note! using TWO instead of one... */ GRegex * mm_simtech_get_rxdtmf_urc_regex (void) { return g_regex_new ("(?:\\r)+\\n\\+RXDTMF:\\s*([0-9A-D\\*\\#])(?:\\r)+\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); } ModemManager-1.23.4-dev/src/plugins/simtech/mm-modem-helpers-simtech.h000066400000000000000000000052371456466623000256130ustar00rootroot00000000000000/* -*- 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) 2019 Aleksander Morgado */ #ifndef MM_MODEM_HELPERS_SIMTECH_H #define MM_MODEM_HELPERS_SIMTECH_H #include #include #include #define _LIBMM_INSIDE_MM #include /*****************************************************************************/ /* +CLCC URC helpers */ gboolean mm_simtech_parse_clcc_test (const gchar *response, gboolean *clcc_urcs_supported, GError **error); GRegex *mm_simtech_get_clcc_urc_regex (void); gboolean mm_simtech_parse_clcc_list (const gchar *str, gpointer log_object, GList **out_list, GError **error); void mm_simtech_call_info_list_free (GList *call_info_list); /*****************************************************************************/ /* VOICE CALL URC helpers */ GRegex *mm_simtech_get_voice_call_urc_regex (void); gboolean mm_simtech_parse_voice_call_urc (GMatchInfo *match_info, gboolean *start_or_stop, guint *duration, GError **error); /*****************************************************************************/ /* MISSED_CALL URC helpers */ GRegex *mm_simtech_get_missed_call_urc_regex (void); gboolean mm_simtech_parse_missed_call_urc (GMatchInfo *match_info, gchar **details, GError **error); /*****************************************************************************/ /* Non-standard CRING URC helpers */ GRegex *mm_simtech_get_cring_urc_regex (void); /*****************************************************************************/ /* +RXDTMF URC helpers */ GRegex *mm_simtech_get_rxdtmf_urc_regex (void); #endif /* MM_MODEM_HELPERS_SIMTECH_H */ ModemManager-1.23.4-dev/src/plugins/simtech/mm-plugin-simtech.c000066400000000000000000000070341456466623000243400ustar00rootroot00000000000000/* -*- 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 - 2012 Red Hat, Inc. * Copyright (C) 2012 Aleksander Morgado */ #include #include #define _LIBMM_INSIDE_MM #include #include "mm-log-object.h" #include "mm-plugin-common.h" #include "mm-broadband-modem-simtech.h" #if defined WITH_QMI #include "mm-broadband-modem-qmi-simtech.h" #endif #define MM_TYPE_PLUGIN_SIMTECH mm_plugin_simtech_get_type () MM_DEFINE_PLUGIN (SIMTECH, simtech, Simtech) /*****************************************************************************/ static MMBaseModem * create_modem (MMPlugin *self, const gchar *uid, const gchar *physdev, const gchar **drivers, guint16 vendor, guint16 product, guint16 subsystem_vendor, GList *probes, GError **error) { #if defined WITH_QMI if (mm_port_probe_list_has_qmi_port (probes)) { mm_obj_dbg (self, "QMI-powered SimTech modem found..."); return MM_BASE_MODEM (mm_broadband_modem_qmi_simtech_new (uid, physdev, drivers, mm_plugin_get_name (self), vendor, product)); } #endif return MM_BASE_MODEM (mm_broadband_modem_simtech_new (uid, physdev, drivers, mm_plugin_get_name (self), vendor, product)); } /*****************************************************************************/ MM_PLUGIN_NAMED_CREATOR_SCOPE MMPlugin * mm_plugin_create_simtech (void) { static const gchar *subsystems[] = { "tty", "net", "usbmisc", NULL }; static const guint16 vendor_ids[] = { 0x1e0e, /* A-Link (for now) */ 0 }; return MM_PLUGIN ( g_object_new (MM_TYPE_PLUGIN_SIMTECH, MM_PLUGIN_NAME, MM_MODULE_NAME, MM_PLUGIN_ALLOWED_SUBSYSTEMS, subsystems, MM_PLUGIN_ALLOWED_VENDOR_IDS, vendor_ids, MM_PLUGIN_ALLOWED_AT, TRUE, MM_PLUGIN_ALLOWED_QCDM, TRUE, MM_PLUGIN_ALLOWED_QMI, TRUE, NULL)); } static void mm_plugin_simtech_init (MMPluginSimtech *self) { } static void mm_plugin_simtech_class_init (MMPluginSimtechClass *klass) { MMPluginClass *plugin_class = MM_PLUGIN_CLASS (klass); plugin_class->create_modem = create_modem; } ModemManager-1.23.4-dev/src/plugins/simtech/mm-shared-simtech.c000066400000000000000000001321761456466623000243160ustar00rootroot00000000000000/* -*- 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) 2019 Aleksander Morgado */ #include #include #include #define _LIBMM_INSIDE_MM #include #include "mm-log-object.h" #include "mm-iface-modem.h" #include "mm-iface-modem-voice.h" #include "mm-iface-modem-location.h" #include "mm-base-modem.h" #include "mm-base-modem-at.h" #include "mm-shared-simtech.h" #include "mm-modem-helpers-simtech.h" /*****************************************************************************/ /* Private data context */ #define PRIVATE_TAG "shared-simtech-private-tag" static GQuark private_quark; typedef enum { FEATURE_SUPPORT_UNKNOWN, FEATURE_NOT_SUPPORTED, FEATURE_SUPPORTED, } FeatureSupport; typedef struct { /* location */ MMIfaceModemLocation *iface_modem_location_parent; MMModemLocationSource supported_sources; MMModemLocationSource enabled_sources; FeatureSupport cgps_support; /* voice */ MMIfaceModemVoice *iface_modem_voice_parent; FeatureSupport cpcmreg_support; FeatureSupport clcc_urc_support; GRegex *clcc_urc_regex; GRegex *voice_call_regex; GRegex *missed_call_regex; GRegex *cring_regex; GRegex *rxdtmf_regex; } Private; static void private_free (Private *ctx) { g_regex_unref (ctx->rxdtmf_regex); g_regex_unref (ctx->cring_regex); g_regex_unref (ctx->missed_call_regex); g_regex_unref (ctx->voice_call_regex); g_regex_unref (ctx->clcc_urc_regex); g_slice_free (Private, ctx); } static Private * get_private (MMSharedSimtech *self) { Private *priv; if (G_UNLIKELY (!private_quark)) private_quark = (g_quark_from_static_string (PRIVATE_TAG)); priv = g_object_get_qdata (G_OBJECT (self), private_quark); if (!priv) { priv = g_slice_new0 (Private); priv->supported_sources = MM_MODEM_LOCATION_SOURCE_NONE; priv->enabled_sources = MM_MODEM_LOCATION_SOURCE_NONE; priv->cgps_support = FEATURE_SUPPORT_UNKNOWN; priv->cpcmreg_support = FEATURE_SUPPORT_UNKNOWN; priv->clcc_urc_support = FEATURE_SUPPORT_UNKNOWN; priv->clcc_urc_regex = mm_simtech_get_clcc_urc_regex (); priv->voice_call_regex = mm_simtech_get_voice_call_urc_regex (); priv->missed_call_regex = mm_simtech_get_missed_call_urc_regex (); priv->cring_regex = mm_simtech_get_cring_urc_regex (); priv->rxdtmf_regex = mm_simtech_get_rxdtmf_urc_regex (); /* Setup parent class' MMIfaceModemLocation and MMIfaceModemVoice */ g_assert (MM_SHARED_SIMTECH_GET_INTERFACE (self)->peek_parent_location_interface); priv->iface_modem_location_parent = MM_SHARED_SIMTECH_GET_INTERFACE (self)->peek_parent_location_interface (self); g_assert (MM_SHARED_SIMTECH_GET_INTERFACE (self)->peek_parent_voice_interface); priv->iface_modem_voice_parent = MM_SHARED_SIMTECH_GET_INTERFACE (self)->peek_parent_voice_interface (self); g_object_set_qdata_full (G_OBJECT (self), private_quark, priv, (GDestroyNotify)private_free); } return priv; } /*****************************************************************************/ /* GPS trace received */ static void trace_received (MMPortSerialGps *port, const gchar *trace, MMIfaceModemLocation *self) { mm_iface_modem_location_gps_update (self, trace); } /*****************************************************************************/ /* Location capabilities loading (Location interface) */ MMModemLocationSource mm_shared_simtech_location_load_capabilities_finish (MMIfaceModemLocation *self, GAsyncResult *res, GError **error) { GError *inner_error = NULL; gssize aux; aux = g_task_propagate_int (G_TASK (res), &inner_error); if (inner_error) { g_propagate_error (error, inner_error); return MM_MODEM_LOCATION_SOURCE_NONE; } return (MMModemLocationSource) aux; } static void probe_gps_features (GTask *task); static void cgps_test_ready (MMBaseModem *self, GAsyncResult *res, GTask *task) { Private *priv; priv = get_private (MM_SHARED_SIMTECH (self)); if (!mm_base_modem_at_command_finish (self, res, NULL)) priv->cgps_support = FEATURE_NOT_SUPPORTED; else priv->cgps_support = FEATURE_SUPPORTED; probe_gps_features (task); } static void probe_gps_features (GTask *task) { MMSharedSimtech *self; MMModemLocationSource sources; Private *priv; self = MM_SHARED_SIMTECH (g_task_get_source_object (task)); priv = get_private (self); /* Need to check if CGPS supported... */ if (priv->cgps_support == FEATURE_SUPPORT_UNKNOWN) { mm_base_modem_at_command (MM_BASE_MODEM (self), "+CGPS=?", 3, TRUE, (GAsyncReadyCallback) cgps_test_ready, task); return; } /* All GPS features probed */ /* Recover parent sources */ sources = GPOINTER_TO_UINT (g_task_get_task_data (task)); if (priv->cgps_support == FEATURE_SUPPORTED) { mm_obj_dbg (self, "GPS commands supported: GPS capabilities enabled"); /* We only flag as supported by this implementation those sources not already * supported by the parent implementation */ if (!(sources & MM_MODEM_LOCATION_SOURCE_GPS_NMEA)) priv->supported_sources |= MM_MODEM_LOCATION_SOURCE_GPS_NMEA; if (!(sources & MM_MODEM_LOCATION_SOURCE_GPS_RAW)) priv->supported_sources |= MM_MODEM_LOCATION_SOURCE_GPS_RAW; if (!(sources & MM_MODEM_LOCATION_SOURCE_GPS_UNMANAGED)) priv->supported_sources |= MM_MODEM_LOCATION_SOURCE_GPS_UNMANAGED; sources |= priv->supported_sources; /* Add handler for the NMEA traces in the GPS data port */ mm_port_serial_gps_add_trace_handler (mm_base_modem_peek_port_gps (MM_BASE_MODEM (self)), (MMPortSerialGpsTraceFn)trace_received, self, NULL); } else mm_obj_dbg (self, "no GPS command supported: no GPS capabilities"); g_task_return_int (task, (gssize) sources); g_object_unref (task); } static void parent_load_capabilities_ready (MMIfaceModemLocation *self, GAsyncResult *res, GTask *task) { MMModemLocationSource sources; GError *error = NULL; Private *priv; priv = get_private (MM_SHARED_SIMTECH (self)); sources = priv->iface_modem_location_parent->load_capabilities_finish (self, res, &error); if (error) { g_task_return_error (task, error); g_object_unref (task); return; } /* Now our own check. If we don't have any GPS port, we're done */ if (!mm_base_modem_peek_port_gps (MM_BASE_MODEM (self))) { mm_obj_dbg (self, "no GPS data port found: no GPS capabilities"); g_task_return_int (task, sources); g_object_unref (task); return; } /* Cache sources supported by the parent */ g_task_set_task_data (task, GUINT_TO_POINTER (sources), NULL); /* Probe all GPS features */ probe_gps_features (task); } void mm_shared_simtech_location_load_capabilities (MMIfaceModemLocation *self, GAsyncReadyCallback callback, gpointer user_data) { Private *priv; GTask *task; priv = get_private (MM_SHARED_SIMTECH (self)); task = g_task_new (self, NULL, callback, user_data); g_assert (priv->iface_modem_location_parent); g_assert (priv->iface_modem_location_parent->load_capabilities); g_assert (priv->iface_modem_location_parent->load_capabilities_finish); priv->iface_modem_location_parent->load_capabilities (self, (GAsyncReadyCallback)parent_load_capabilities_ready, task); } /*****************************************************************************/ /* Disable location gathering (Location interface) */ gboolean mm_shared_simtech_disable_location_gathering_finish (MMIfaceModemLocation *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void disable_cgps_ready (MMBaseModem *self, GAsyncResult *res, GTask *task) { MMModemLocationSource source; Private *priv; GError *error = NULL; priv = get_private (MM_SHARED_SIMTECH (self)); mm_base_modem_at_command_finish (self, res, &error); /* Only use the GPS port in NMEA/RAW setups */ source = (MMModemLocationSource) GPOINTER_TO_UINT (g_task_get_task_data (task)); if (source & (MM_MODEM_LOCATION_SOURCE_GPS_NMEA | MM_MODEM_LOCATION_SOURCE_GPS_RAW)) { MMPortSerialGps *gps_port; /* Even if we get an error here, we try to close the GPS port */ gps_port = mm_base_modem_peek_port_gps (MM_BASE_MODEM (self)); if (gps_port) mm_port_serial_close (MM_PORT_SERIAL (gps_port)); } if (error) g_task_return_error (task, error); else { priv->enabled_sources &= ~source; g_task_return_boolean (task, TRUE); } g_object_unref (task); } static void parent_disable_location_gathering_ready (MMIfaceModemLocation *self, GAsyncResult *res, GTask *task) { GError *error = NULL; Private *priv; priv = get_private (MM_SHARED_SIMTECH (self)); g_assert (priv->iface_modem_location_parent); if (!priv->iface_modem_location_parent->disable_location_gathering_finish (self, res, &error)) g_task_return_error (task, error); else g_task_return_boolean (task, TRUE); g_object_unref (task); } void mm_shared_simtech_disable_location_gathering (MMIfaceModemLocation *self, MMModemLocationSource source, GAsyncReadyCallback callback, gpointer user_data) { MMModemLocationSource enabled_sources; Private *priv; GTask *task; task = g_task_new (self, NULL, callback, user_data); g_task_set_task_data (task, GUINT_TO_POINTER (source), NULL); priv = get_private (MM_SHARED_SIMTECH (self)); g_assert (priv->iface_modem_location_parent); /* Only consider request if it applies to one of the sources we are * supporting, otherwise run parent disable */ if (!(priv->supported_sources & source)) { /* If disabling implemented by the parent, run it. */ if (priv->iface_modem_location_parent->disable_location_gathering && priv->iface_modem_location_parent->disable_location_gathering_finish) { priv->iface_modem_location_parent->disable_location_gathering (self, source, (GAsyncReadyCallback)parent_disable_location_gathering_ready, task); return; } /* Otherwise, we're done */ g_task_return_boolean (task, TRUE); g_object_unref (task); return; } /* We only expect GPS sources here */ g_assert (source & (MM_MODEM_LOCATION_SOURCE_GPS_NMEA | MM_MODEM_LOCATION_SOURCE_GPS_RAW | MM_MODEM_LOCATION_SOURCE_GPS_UNMANAGED)); /* Flag as disabled to see how many others we would have left enabled */ enabled_sources = priv->enabled_sources; enabled_sources &= ~source; /* If there are still GPS-related sources enabled, do nothing else */ if (enabled_sources & (MM_MODEM_LOCATION_SOURCE_GPS_NMEA | MM_MODEM_LOCATION_SOURCE_GPS_RAW | MM_MODEM_LOCATION_SOURCE_GPS_UNMANAGED)) { priv->enabled_sources &= ~source; g_task_return_boolean (task, TRUE); g_object_unref (task); return; } /* Stop GPS engine if all GPS-related sources are disabled */ g_assert (priv->cgps_support == FEATURE_SUPPORTED); mm_base_modem_at_command (MM_BASE_MODEM (self), "+CGPS=0", 10, FALSE, (GAsyncReadyCallback) disable_cgps_ready, task); } /*****************************************************************************/ /* Enable location gathering (Location interface) */ gboolean mm_shared_simtech_enable_location_gathering_finish (MMIfaceModemLocation *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void enable_cgps_ready (MMBaseModem *self, GAsyncResult *res, GTask *task) { MMModemLocationSource source; GError *error = NULL; Private *priv; priv = get_private (MM_SHARED_SIMTECH (self)); if (!mm_base_modem_at_command_finish (self, res, &error)) { g_task_return_error (task, error); g_object_unref (task); return; } /* Only use the GPS port in NMEA/RAW setups */ source = (MMModemLocationSource) GPOINTER_TO_UINT (g_task_get_task_data (task)); if (source & (MM_MODEM_LOCATION_SOURCE_GPS_NMEA | MM_MODEM_LOCATION_SOURCE_GPS_RAW)) { MMPortSerialGps *gps_port; gps_port = mm_base_modem_peek_port_gps (MM_BASE_MODEM (self)); if (!gps_port || !mm_port_serial_open (MM_PORT_SERIAL (gps_port), &error)) { if (error) g_task_return_error (task, error); else g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't open raw GPS serial port"); g_object_unref (task); return; } } priv->enabled_sources |= source; g_task_return_boolean (task, TRUE); g_object_unref (task); } static void parent_enable_location_gathering_ready (MMIfaceModemLocation *self, GAsyncResult *res, GTask *task) { GError *error = NULL; Private *priv; priv = get_private (MM_SHARED_SIMTECH (self)); g_assert (priv->iface_modem_location_parent); if (!priv->iface_modem_location_parent->enable_location_gathering_finish (self, res, &error)) g_task_return_error (task, error); else g_task_return_boolean (task, TRUE); g_object_unref (task); } void mm_shared_simtech_enable_location_gathering (MMIfaceModemLocation *self, MMModemLocationSource source, GAsyncReadyCallback callback, gpointer user_data) { Private *priv; GTask *task; task = g_task_new (self, NULL, callback, user_data); g_task_set_task_data (task, GUINT_TO_POINTER (source), NULL); priv = get_private (MM_SHARED_SIMTECH (self)); g_assert (priv->iface_modem_location_parent); g_assert (priv->iface_modem_location_parent->enable_location_gathering); g_assert (priv->iface_modem_location_parent->enable_location_gathering_finish); /* Only consider request if it applies to one of the sources we are * supporting, otherwise run parent enable */ if (!(priv->supported_sources & source)) { priv->iface_modem_location_parent->enable_location_gathering (self, source, (GAsyncReadyCallback)parent_enable_location_gathering_ready, task); return; } /* We only expect GPS sources here */ g_assert (source & (MM_MODEM_LOCATION_SOURCE_GPS_NMEA | MM_MODEM_LOCATION_SOURCE_GPS_RAW | MM_MODEM_LOCATION_SOURCE_GPS_UNMANAGED)); /* If GPS already started, store new flag and we're done */ if (priv->enabled_sources & (MM_MODEM_LOCATION_SOURCE_GPS_NMEA | MM_MODEM_LOCATION_SOURCE_GPS_RAW | MM_MODEM_LOCATION_SOURCE_GPS_UNMANAGED)) { priv->enabled_sources |= source; g_task_return_boolean (task, TRUE); g_object_unref (task); return; } g_assert (priv->cgps_support == FEATURE_SUPPORTED); mm_base_modem_at_command (MM_BASE_MODEM (self), "+CGPS=1,1", 10, FALSE, (GAsyncReadyCallback) enable_cgps_ready, task); } /*****************************************************************************/ /* Common enable/disable voice unsolicited events */ typedef struct { gboolean enable; MMPortSerialAt *primary; MMPortSerialAt *secondary; gchar *clcc_command; gboolean clcc_primary_done; gboolean clcc_secondary_done; } VoiceUnsolicitedEventsContext; static void voice_unsolicited_events_context_free (VoiceUnsolicitedEventsContext *ctx) { g_clear_object (&ctx->secondary); g_clear_object (&ctx->primary); g_free (ctx->clcc_command); g_slice_free (VoiceUnsolicitedEventsContext, ctx); } static gboolean common_voice_enable_disable_unsolicited_events_finish (MMSharedSimtech *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void run_voice_enable_disable_unsolicited_events (GTask *task); static void clcc_command_ready (MMBaseModem *self, GAsyncResult *res, GTask *task) { VoiceUnsolicitedEventsContext *ctx; GError *error = NULL; ctx = g_task_get_task_data (task); if (!mm_base_modem_at_command_finish (self, res, &error)) { mm_obj_dbg (self, "couldn't %s +CLCC reporting: '%s'", ctx->enable ? "enable" : "disable", error->message); g_error_free (error); } /* Continue on next port */ run_voice_enable_disable_unsolicited_events (task); } static void run_voice_enable_disable_unsolicited_events (GTask *task) { MMSharedSimtech *self; Private *priv; VoiceUnsolicitedEventsContext *ctx; MMPortSerialAt *port = NULL; self = MM_SHARED_SIMTECH (g_task_get_source_object (task)); priv = get_private (self); ctx = g_task_get_task_data (task); /* If +CLCC URCs not supported, we're done */ if (priv->clcc_urc_support == FEATURE_NOT_SUPPORTED) { g_task_return_boolean (task, TRUE); g_object_unref (task); return; } if (!ctx->clcc_primary_done && ctx->primary) { mm_obj_dbg (self, "%s +CLCC extended list of current calls reporting in primary port...", ctx->enable ? "enabling" : "disabling"); ctx->clcc_primary_done = TRUE; port = ctx->primary; } else if (!ctx->clcc_secondary_done && ctx->secondary) { mm_obj_dbg (self, "%s +CLCC extended list of current calls reporting in secondary port...", ctx->enable ? "enabling" : "disabling"); ctx->clcc_secondary_done = TRUE; port = ctx->secondary; } if (port) { mm_base_modem_at_command_full (MM_BASE_MODEM (self), port, ctx->clcc_command, 3, FALSE, FALSE, NULL, (GAsyncReadyCallback)clcc_command_ready, task); return; } /* Fully done now */ g_task_return_boolean (task, TRUE); g_object_unref (task); } static void common_voice_enable_disable_unsolicited_events (MMSharedSimtech *self, gboolean enable, GAsyncReadyCallback callback, gpointer user_data) { VoiceUnsolicitedEventsContext *ctx; GTask *task; task = g_task_new (self, NULL, callback, user_data); ctx = g_slice_new0 (VoiceUnsolicitedEventsContext); ctx->enable = enable; if (enable) ctx->clcc_command = g_strdup ("+CLCC=1"); else ctx->clcc_command = g_strdup ("+CLCC=0"); ctx->primary = mm_base_modem_get_port_primary (MM_BASE_MODEM (self)); ctx->secondary = mm_base_modem_get_port_secondary (MM_BASE_MODEM (self)); g_task_set_task_data (task, ctx, (GDestroyNotify) voice_unsolicited_events_context_free); run_voice_enable_disable_unsolicited_events (task); } /*****************************************************************************/ /* Disable unsolicited events (Voice interface) */ gboolean mm_shared_simtech_voice_disable_unsolicited_events_finish (MMIfaceModemVoice *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void parent_voice_disable_unsolicited_events_ready (MMIfaceModemVoice *self, GAsyncResult *res, GTask *task) { GError *error = NULL; Private *priv; priv = get_private (MM_SHARED_SIMTECH (self)); if (!priv->iface_modem_voice_parent->disable_unsolicited_events_finish (self, res, &error)) { mm_obj_warn (self, "couldn't disable parent voice unsolicited events: %s", error->message); g_error_free (error); } g_task_return_boolean (task, TRUE); g_object_unref (task); } static void voice_disable_unsolicited_events_ready (MMSharedSimtech *self, GAsyncResult *res, GTask *task) { Private *priv; GError *error = NULL; if (!common_voice_enable_disable_unsolicited_events_finish (self, res, &error)) { mm_obj_warn (self, "couldn't disable Simtech-specific voice unsolicited events: %s", error->message); g_error_free (error); } priv = get_private (MM_SHARED_SIMTECH (self)); g_assert (priv->iface_modem_voice_parent); g_assert (priv->iface_modem_voice_parent->disable_unsolicited_events); g_assert (priv->iface_modem_voice_parent->disable_unsolicited_events_finish); /* Chain up parent's disable */ priv->iface_modem_voice_parent->disable_unsolicited_events ( MM_IFACE_MODEM_VOICE (self), (GAsyncReadyCallback)parent_voice_disable_unsolicited_events_ready, task); } void mm_shared_simtech_voice_disable_unsolicited_events (MMIfaceModemVoice *self, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; task = g_task_new (self, NULL, callback, user_data); /* our own disabling first */ common_voice_enable_disable_unsolicited_events (MM_SHARED_SIMTECH (self), FALSE, (GAsyncReadyCallback) voice_disable_unsolicited_events_ready, task); } /*****************************************************************************/ /* Enable unsolicited events (Voice interface) */ gboolean mm_shared_simtech_voice_enable_unsolicited_events_finish (MMIfaceModemVoice *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void voice_enable_unsolicited_events_ready (MMSharedSimtech *self, GAsyncResult *res, GTask *task) { GError *error = NULL; if (!common_voice_enable_disable_unsolicited_events_finish (self, res, &error)) { mm_obj_warn (self, "couldn't enable Simtech-specific voice unsolicited events: %s", error->message); g_error_free (error); } g_task_return_boolean (task, TRUE); g_object_unref (task); } static void parent_voice_enable_unsolicited_events_ready (MMIfaceModemVoice *self, GAsyncResult *res, GTask *task) { GError *error = NULL; Private *priv; priv = get_private (MM_SHARED_SIMTECH (self)); if (!priv->iface_modem_voice_parent->enable_unsolicited_events_finish (self, res, &error)) { mm_obj_warn (self, "couldn't enable parent voice unsolicited events: %s", error->message); g_error_free (error); } /* our own enabling next */ common_voice_enable_disable_unsolicited_events (MM_SHARED_SIMTECH (self), TRUE, (GAsyncReadyCallback) voice_enable_unsolicited_events_ready, task); } void mm_shared_simtech_voice_enable_unsolicited_events (MMIfaceModemVoice *self, GAsyncReadyCallback callback, gpointer user_data) { Private *priv; GTask *task; task = g_task_new (self, NULL, callback, user_data); priv = get_private (MM_SHARED_SIMTECH (self)); g_assert (priv->iface_modem_voice_parent); g_assert (priv->iface_modem_voice_parent->enable_unsolicited_events); g_assert (priv->iface_modem_voice_parent->enable_unsolicited_events_finish); /* chain up parent's enable first */ priv->iface_modem_voice_parent->enable_unsolicited_events ( self, (GAsyncReadyCallback)parent_voice_enable_unsolicited_events_ready, task); } /*****************************************************************************/ /* Common setup/cleanup voice unsolicited events */ static void clcc_urc_received (MMPortSerialAt *port, GMatchInfo *match_info, MMSharedSimtech *self) { gchar *full; GError *error = NULL; GList *call_info_list = NULL; full = g_match_info_fetch (match_info, 0); if (!mm_simtech_parse_clcc_list (full, self, &call_info_list, &error)) { mm_obj_warn (self, "couldn't parse +CLCC list in URC: %s", error->message); g_error_free (error); } else mm_iface_modem_voice_report_all_calls (MM_IFACE_MODEM_VOICE (self), call_info_list); mm_simtech_call_info_list_free (call_info_list); g_free (full); } static void missed_call_urc_received (MMPortSerialAt *port, GMatchInfo *match_info, MMSharedSimtech *self) { GError *error = NULL; gchar *details = NULL; if (!mm_simtech_parse_missed_call_urc (match_info, &details, &error)) { mm_obj_warn (self, "couldn't parse missed call URC: %s", error->message); g_error_free (error); return; } mm_obj_dbg (self, "missed call reported: %s", details); g_free (details); } static void voice_call_urc_received (MMPortSerialAt *port, GMatchInfo *match_info, MMSharedSimtech *self) { GError *error = NULL; gboolean start_or_stop = FALSE; /* start = TRUE, stop = FALSE */ guint duration = 0; if (!mm_simtech_parse_voice_call_urc (match_info, &start_or_stop, &duration, &error)) { mm_obj_warn (self, "couldn't parse voice call URC: %s", error->message); g_error_free (error); return; } if (start_or_stop) { mm_obj_dbg (self, "voice call started"); return; } if (duration) { mm_obj_dbg (self, "voice call finished (duration: %us)", duration); return; } mm_obj_dbg (self, "voice call finished"); } static void cring_urc_received (MMPortSerialAt *port, GMatchInfo *info, MMSharedSimtech *self) { MMCallInfo call_info; g_autofree gchar *str = NULL; /* We could have "VOICE" or "DATA". Now consider only "VOICE" */ str = mm_get_string_unquoted_from_match_info (info, 1); mm_obj_dbg (self, "ringing (%s)", str); call_info.index = 0; call_info.direction = MM_CALL_DIRECTION_INCOMING; call_info.state = MM_CALL_STATE_RINGING_IN; call_info.number = NULL; mm_iface_modem_voice_report_call (MM_IFACE_MODEM_VOICE (self), &call_info); } static void rxdtmf_urc_received (MMPortSerialAt *port, GMatchInfo *match_info, MMSharedSimtech *self) { g_autofree gchar *dtmf = NULL; dtmf = g_match_info_fetch (match_info, 1); mm_obj_dbg (self, "received DTMF: %s", dtmf); /* call index unknown */ mm_iface_modem_voice_received_dtmf (MM_IFACE_MODEM_VOICE (self), 0, dtmf); } static void common_voice_setup_cleanup_unsolicited_events (MMSharedSimtech *self, gboolean enable) { Private *priv; MMPortSerialAt *ports[2]; guint i; priv = get_private (MM_SHARED_SIMTECH (self)); ports[0] = mm_base_modem_peek_port_primary (MM_BASE_MODEM (self)); ports[1] = mm_base_modem_peek_port_secondary (MM_BASE_MODEM (self)); for (i = 0; i < G_N_ELEMENTS (ports); i++) { if (!ports[i]) continue; if (priv->clcc_urc_support == FEATURE_SUPPORTED) mm_port_serial_at_add_unsolicited_msg_handler (ports[i], priv->clcc_urc_regex, enable ? (MMPortSerialAtUnsolicitedMsgFn)clcc_urc_received : NULL, enable ? self : NULL, NULL); mm_port_serial_at_add_unsolicited_msg_handler (ports[i], priv->voice_call_regex, enable ? (MMPortSerialAtUnsolicitedMsgFn)voice_call_urc_received : NULL, enable ? self : NULL, NULL); mm_port_serial_at_add_unsolicited_msg_handler (ports[i], priv->missed_call_regex, enable ? (MMPortSerialAtUnsolicitedMsgFn)missed_call_urc_received : NULL, enable ? self : NULL, NULL); mm_port_serial_at_add_unsolicited_msg_handler (ports[i], priv->cring_regex, enable ? (MMPortSerialAtUnsolicitedMsgFn)cring_urc_received : NULL, enable ? self : NULL, NULL); mm_port_serial_at_add_unsolicited_msg_handler (ports[i], priv->rxdtmf_regex, enable ? (MMPortSerialAtUnsolicitedMsgFn)rxdtmf_urc_received : NULL, enable ? self : NULL, NULL); } } /*****************************************************************************/ /* Cleanup unsolicited events (Voice interface) */ gboolean mm_shared_simtech_voice_cleanup_unsolicited_events_finish (MMIfaceModemVoice *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void parent_voice_cleanup_unsolicited_events_ready (MMIfaceModemVoice *self, GAsyncResult *res, GTask *task) { GError *error = NULL; Private *priv; priv = get_private (MM_SHARED_SIMTECH (self)); if (!priv->iface_modem_voice_parent->cleanup_unsolicited_events_finish (self, res, &error)) { mm_obj_warn (self, "couldn't cleanup parent voice unsolicited events: %s", error->message); g_error_free (error); } g_task_return_boolean (task, TRUE); g_object_unref (task); } void mm_shared_simtech_voice_cleanup_unsolicited_events (MMIfaceModemVoice *self, GAsyncReadyCallback callback, gpointer user_data) { Private *priv; GTask *task; task = g_task_new (self, NULL, callback, user_data); priv = get_private (MM_SHARED_SIMTECH (self)); g_assert (priv->iface_modem_voice_parent); g_assert (priv->iface_modem_voice_parent->cleanup_unsolicited_events); g_assert (priv->iface_modem_voice_parent->cleanup_unsolicited_events_finish); /* our own cleanup first */ common_voice_setup_cleanup_unsolicited_events (MM_SHARED_SIMTECH (self), FALSE); /* Chain up parent's cleanup */ priv->iface_modem_voice_parent->cleanup_unsolicited_events ( self, (GAsyncReadyCallback)parent_voice_cleanup_unsolicited_events_ready, task); } /*****************************************************************************/ /* Setup unsolicited events (Voice interface) */ gboolean mm_shared_simtech_voice_setup_unsolicited_events_finish (MMIfaceModemVoice *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void parent_voice_setup_unsolicited_events_ready (MMIfaceModemVoice *self, GAsyncResult *res, GTask *task) { GError *error = NULL; Private *priv; priv = get_private (MM_SHARED_SIMTECH (self)); if (!priv->iface_modem_voice_parent->setup_unsolicited_events_finish (self, res, &error)) { mm_obj_warn (self, "couldn't setup parent voice unsolicited events: %s", error->message); g_error_free (error); } /* our own setup next */ common_voice_setup_cleanup_unsolicited_events (MM_SHARED_SIMTECH (self), TRUE); g_task_return_boolean (task, TRUE); g_object_unref (task); } void mm_shared_simtech_voice_setup_unsolicited_events (MMIfaceModemVoice *self, GAsyncReadyCallback callback, gpointer user_data) { Private *priv; GTask *task; task = g_task_new (self, NULL, callback, user_data); priv = get_private (MM_SHARED_SIMTECH (self)); g_assert (priv->iface_modem_voice_parent); g_assert (priv->iface_modem_voice_parent->setup_unsolicited_events); g_assert (priv->iface_modem_voice_parent->setup_unsolicited_events_finish); /* chain up parent's setup first */ priv->iface_modem_voice_parent->setup_unsolicited_events ( self, (GAsyncReadyCallback)parent_voice_setup_unsolicited_events_ready, task); } /*****************************************************************************/ /* In-call audio channel setup/cleanup */ gboolean mm_shared_simtech_voice_setup_in_call_audio_channel_finish (MMIfaceModemVoice *self, GAsyncResult *res, MMPort **audio_port, /* optional */ MMCallAudioFormat **audio_format, /* optional */ GError **error) { Private *priv; priv = get_private (MM_SHARED_SIMTECH (self)); if (!g_task_propagate_boolean (G_TASK (res), error)) return FALSE; if (audio_format) *audio_format = NULL; if (audio_port) { if (priv->cpcmreg_support == FEATURE_SUPPORTED) *audio_port = MM_PORT (mm_base_modem_get_port_audio (MM_BASE_MODEM (self))); else *audio_port = NULL; } return TRUE; } gboolean mm_shared_simtech_voice_cleanup_in_call_audio_channel_finish (MMIfaceModemVoice *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void cpcmreg_set_ready (MMBaseModem *self, GAsyncResult *res, GTask *task) { GError *error = NULL; if (!mm_base_modem_at_command_finish (self, res, &error)) g_task_return_error (task, error); else g_task_return_boolean (task, TRUE); g_object_unref (task); } static void common_setup_cleanup_in_call_audio_channel (MMSharedSimtech *self, gboolean setup, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; Private *priv; priv = get_private (MM_SHARED_SIMTECH (self)); task = g_task_new (self, NULL, callback, user_data); /* Do nothing if CPCMREG isn't supported */ if (priv->cpcmreg_support != FEATURE_SUPPORTED) { g_task_return_boolean (task, TRUE); g_object_unref (task); return; } mm_base_modem_at_command (MM_BASE_MODEM (self), setup ? "+CPCMREG=1" : "+CPCMREG=0", 3, FALSE, (GAsyncReadyCallback) cpcmreg_set_ready, task); } void mm_shared_simtech_voice_setup_in_call_audio_channel (MMIfaceModemVoice *self, GAsyncReadyCallback callback, gpointer user_data) { common_setup_cleanup_in_call_audio_channel (MM_SHARED_SIMTECH (self), TRUE, callback, user_data); } void mm_shared_simtech_voice_cleanup_in_call_audio_channel (MMIfaceModemVoice *self, GAsyncReadyCallback callback, gpointer user_data) { common_setup_cleanup_in_call_audio_channel (MM_SHARED_SIMTECH (self), FALSE, callback, user_data); } /*****************************************************************************/ /* Check if Voice supported (Voice interface) */ gboolean mm_shared_simtech_voice_check_support_finish (MMIfaceModemVoice *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void cpcmreg_format_check_ready (MMBroadbandModem *self, GAsyncResult *res, GTask *task) { Private *priv; priv = get_private (MM_SHARED_SIMTECH (self)); priv->cpcmreg_support = (mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, NULL) ? FEATURE_SUPPORTED : FEATURE_NOT_SUPPORTED); mm_obj_dbg (self, "modem %s USB audio control", (priv->cpcmreg_support == FEATURE_SUPPORTED) ? "supports" : "doesn't support"); g_task_return_boolean (task, TRUE); g_object_unref (task); } static void clcc_format_check_ready (MMBroadbandModem *self, GAsyncResult *res, GTask *task) { Private *priv; GError *error = NULL; const gchar *response; gboolean clcc_urc_supported = FALSE; priv = get_private (MM_SHARED_SIMTECH (self)); response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, NULL); if (response && !mm_simtech_parse_clcc_test (response, &clcc_urc_supported, &error)) { mm_obj_dbg (self, "failed checking CLCC URC support: %s", error->message); g_clear_error (&error); } priv->clcc_urc_support = (clcc_urc_supported ? FEATURE_SUPPORTED : FEATURE_NOT_SUPPORTED); mm_obj_dbg (self, "modem %s +CLCC URCs", (priv->clcc_urc_support == FEATURE_SUPPORTED) ? "supports" : "doesn't support"); /* If +CLCC URC supported we won't need polling in the parent */ g_object_set (self, MM_IFACE_MODEM_VOICE_PERIODIC_CALL_LIST_CHECK_DISABLED, (priv->clcc_urc_support == FEATURE_SUPPORTED), NULL); mm_base_modem_at_command (MM_BASE_MODEM (self), "+CPCMREG=?", 3, TRUE, (GAsyncReadyCallback) cpcmreg_format_check_ready, task); } static void parent_voice_check_support_ready (MMIfaceModemVoice *self, GAsyncResult *res, GTask *task) { Private *priv; GError *error = NULL; priv = get_private (MM_SHARED_SIMTECH (self)); if (!priv->iface_modem_voice_parent->check_support_finish (self, res, &error)) { g_task_return_error (task, error); g_object_unref (task); return; } /* voice is supported, check if +CLCC URCs are available */ mm_base_modem_at_command (MM_BASE_MODEM (self), "+CLCC=?", 3, TRUE, (GAsyncReadyCallback) clcc_format_check_ready, task); } void mm_shared_simtech_voice_check_support (MMIfaceModemVoice *self, GAsyncReadyCallback callback, gpointer user_data) { Private *priv; GTask *task; task = g_task_new (self, NULL, callback, user_data); priv = get_private (MM_SHARED_SIMTECH (self)); g_assert (priv->iface_modem_voice_parent); g_assert (priv->iface_modem_voice_parent->check_support); g_assert (priv->iface_modem_voice_parent->check_support_finish); /* chain up parent's setup first */ priv->iface_modem_voice_parent->check_support ( self, (GAsyncReadyCallback)parent_voice_check_support_ready, task); } /*****************************************************************************/ static void shared_simtech_init (gpointer g_iface) { } GType mm_shared_simtech_get_type (void) { static GType shared_simtech_type = 0; if (!G_UNLIKELY (shared_simtech_type)) { static const GTypeInfo info = { sizeof (MMSharedSimtech), /* class_size */ shared_simtech_init, /* base_init */ NULL, /* base_finalize */ }; shared_simtech_type = g_type_register_static (G_TYPE_INTERFACE, "MMSharedSimtech", &info, 0); g_type_interface_add_prerequisite (shared_simtech_type, MM_TYPE_IFACE_MODEM_LOCATION); } return shared_simtech_type; } ModemManager-1.23.4-dev/src/plugins/simtech/mm-shared-simtech.h000066400000000000000000000206321456466623000243140ustar00rootroot00000000000000/* -*- 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) 2019 Aleksander Morgado */ #ifndef MM_SHARED_SIMTECH_H #define MM_SHARED_SIMTECH_H #include #include #define _LIBMM_INSIDE_MM #include #include "mm-broadband-modem.h" #include "mm-iface-modem.h" #include "mm-iface-modem-location.h" #include "mm-iface-modem-voice.h" #define MM_TYPE_SHARED_SIMTECH (mm_shared_simtech_get_type ()) #define MM_SHARED_SIMTECH(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_SHARED_SIMTECH, MMSharedSimtech)) #define MM_IS_SHARED_SIMTECH(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_SHARED_SIMTECH)) #define MM_SHARED_SIMTECH_GET_INTERFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE ((obj), MM_TYPE_SHARED_SIMTECH, MMSharedSimtech)) typedef struct _MMSharedSimtech MMSharedSimtech; struct _MMSharedSimtech { GTypeInterface g_iface; /* Peek location interface of the parent class of the object */ MMIfaceModemLocation * (* peek_parent_location_interface) (MMSharedSimtech *self); /* Peek voice interface of the parent class of the object */ MMIfaceModemVoice * (* peek_parent_voice_interface) (MMSharedSimtech *self); }; GType mm_shared_simtech_get_type (void); /*****************************************************************************/ /* Location interface */ void mm_shared_simtech_location_load_capabilities (MMIfaceModemLocation *self, GAsyncReadyCallback callback, gpointer user_data); MMModemLocationSource mm_shared_simtech_location_load_capabilities_finish (MMIfaceModemLocation *self, GAsyncResult *res, GError **error); void mm_shared_simtech_enable_location_gathering (MMIfaceModemLocation *self, MMModemLocationSource source, GAsyncReadyCallback callback, gpointer user_data); gboolean mm_shared_simtech_enable_location_gathering_finish (MMIfaceModemLocation *self, GAsyncResult *res, GError **error); void mm_shared_simtech_disable_location_gathering (MMIfaceModemLocation *self, MMModemLocationSource source, GAsyncReadyCallback callback, gpointer user_data); gboolean mm_shared_simtech_disable_location_gathering_finish (MMIfaceModemLocation *self, GAsyncResult *res, GError **error); /*****************************************************************************/ /* Voice interface */ void mm_shared_simtech_voice_check_support (MMIfaceModemVoice *self, GAsyncReadyCallback callback, gpointer user_data); gboolean mm_shared_simtech_voice_check_support_finish (MMIfaceModemVoice *self, GAsyncResult *res, GError **error); void mm_shared_simtech_voice_setup_unsolicited_events (MMIfaceModemVoice *self, GAsyncReadyCallback callback, gpointer user_data); gboolean mm_shared_simtech_voice_setup_unsolicited_events_finish (MMIfaceModemVoice *self, GAsyncResult *res, GError **error); void mm_shared_simtech_voice_cleanup_unsolicited_events (MMIfaceModemVoice *self, GAsyncReadyCallback callback, gpointer user_data); gboolean mm_shared_simtech_voice_cleanup_unsolicited_events_finish (MMIfaceModemVoice *self, GAsyncResult *res, GError **error); void mm_shared_simtech_voice_enable_unsolicited_events (MMIfaceModemVoice *self, GAsyncReadyCallback callback, gpointer user_data); gboolean mm_shared_simtech_voice_enable_unsolicited_events_finish (MMIfaceModemVoice *self, GAsyncResult *res, GError **error); void mm_shared_simtech_voice_disable_unsolicited_events (MMIfaceModemVoice *self, GAsyncReadyCallback callback, gpointer user_data); gboolean mm_shared_simtech_voice_disable_unsolicited_events_finish (MMIfaceModemVoice *self, GAsyncResult *res, GError **error); void mm_shared_simtech_voice_setup_in_call_audio_channel (MMIfaceModemVoice *self, GAsyncReadyCallback callback, gpointer user_data); gboolean mm_shared_simtech_voice_setup_in_call_audio_channel_finish (MMIfaceModemVoice *self, GAsyncResult *res, MMPort **audio_port, /* optional */ MMCallAudioFormat **audio_format, /* optional */ GError **error); void mm_shared_simtech_voice_cleanup_in_call_audio_channel (MMIfaceModemVoice *self, GAsyncReadyCallback callback, gpointer user_data); gboolean mm_shared_simtech_voice_cleanup_in_call_audio_channel_finish (MMIfaceModemVoice *self, GAsyncResult *res, GError **error); #endif /* MM_SHARED_SIMTECH_H */ ModemManager-1.23.4-dev/src/plugins/simtech/tests/000077500000000000000000000000001456466623000217735ustar00rootroot00000000000000ModemManager-1.23.4-dev/src/plugins/simtech/tests/test-modem-helpers-simtech.c000066400000000000000000000254041456466623000273140ustar00rootroot00000000000000/* -*- 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) 2019 Aleksander Morgado */ #include #include #include #include #define _LIBMM_INSIDE_MM #include #include "mm-log-test.h" #include "mm-modem-helpers.h" #include "mm-modem-helpers-simtech.h" /*****************************************************************************/ /* Test +CLCC URCs */ static void common_test_clcc_urc (const gchar *urc, const MMCallInfo *expected_call_info_list, guint expected_call_info_list_size) { g_autoptr(GRegex) clcc_regex = NULL; g_autoptr(GMatchInfo) match_info = NULL; g_autofree gchar *str = NULL; GError *error = NULL; GList *call_info_list = NULL; GList *l; gboolean result; clcc_regex = mm_simtech_get_clcc_urc_regex (); /* Same matching logic as done in MMSerialPortAt when processing URCs! */ result = g_regex_match_full (clcc_regex, urc, -1, 0, 0, &match_info, &error); g_assert_no_error (error); g_assert (result); /* read full matched content */ str = g_match_info_fetch (match_info, 0); g_assert (str); result = mm_simtech_parse_clcc_list (str, NULL, &call_info_list, &error); g_assert_no_error (error); g_assert (result); g_debug ("found %u calls", g_list_length (call_info_list)); if (expected_call_info_list) { g_assert (call_info_list); g_assert_cmpuint (g_list_length (call_info_list), ==, expected_call_info_list_size); } else g_assert (!call_info_list); for (l = call_info_list; l; l = g_list_next (l)) { const MMCallInfo *call_info = (const MMCallInfo *)(l->data); gboolean found = FALSE; guint i; g_debug ("call at index %u: direction %s, state %s, number %s", call_info->index, mm_call_direction_get_string (call_info->direction), mm_call_state_get_string (call_info->state), call_info->number ? call_info->number : "n/a"); for (i = 0; !found && i < expected_call_info_list_size; i++) found = ((call_info->index == expected_call_info_list[i].index) && (call_info->direction == expected_call_info_list[i].direction) && (call_info->state == expected_call_info_list[i].state) && (g_strcmp0 (call_info->number, expected_call_info_list[i].number) == 0)); g_assert (found); } mm_simtech_call_info_list_free (call_info_list); } static void test_clcc_urc_single (void) { static const MMCallInfo expected_call_info_list[] = { { 1, MM_CALL_DIRECTION_INCOMING, MM_CALL_STATE_ACTIVE, (gchar *) "123456789" } }; const gchar *urc = "\r\n+CLCC: 1,1,0,0,0,\"123456789\",161" "\r\n"; common_test_clcc_urc (urc, expected_call_info_list, G_N_ELEMENTS (expected_call_info_list)); } static void test_clcc_urc_multiple (void) { static const MMCallInfo expected_call_info_list[] = { { 1, MM_CALL_DIRECTION_INCOMING, MM_CALL_STATE_ACTIVE, NULL }, { 2, MM_CALL_DIRECTION_INCOMING, MM_CALL_STATE_ACTIVE, (gchar *) "123456789" }, { 3, MM_CALL_DIRECTION_INCOMING, MM_CALL_STATE_ACTIVE, (gchar *) "987654321" }, }; const gchar *urc = "\r\n+CLCC: 1,1,0,0,0" /* number unknown */ "\r\n+CLCC: 2,1,0,0,0,\"123456789\",161" "\r\n+CLCC: 3,1,0,0,0,\"987654321\",161,\"Alice\"" "\r\n"; common_test_clcc_urc (urc, expected_call_info_list, G_N_ELEMENTS (expected_call_info_list)); } static void test_clcc_urc_complex (void) { static const MMCallInfo expected_call_info_list[] = { { 1, MM_CALL_DIRECTION_INCOMING, MM_CALL_STATE_ACTIVE, (gchar *) "123456789" }, { 2, MM_CALL_DIRECTION_INCOMING, MM_CALL_STATE_WAITING, (gchar *) "987654321" }, }; const gchar *urc = "\r\n^CIEV: 1,0" /* some different URC before our match */ "\r\n+CLCC: 1,1,0,0,0,\"123456789\",161" "\r\n+CLCC: 2,1,5,0,0,\"987654321\",161" "\r\n^CIEV: 1,0" /* some different URC after our match */ "\r\n"; common_test_clcc_urc (urc, expected_call_info_list, G_N_ELEMENTS (expected_call_info_list)); } /*****************************************************************************/ static void common_test_voice_call_urc (const gchar *urc, gboolean expected_start_or_stop, guint expected_duration) { g_autoptr(GRegex) voice_call_regex = NULL; g_autoptr(GMatchInfo) match_info = NULL; GError *error = NULL; gboolean start_or_stop = FALSE; /* start = TRUE, stop = FALSE */ guint duration = 0; gboolean result; voice_call_regex = mm_simtech_get_voice_call_urc_regex (); /* Same matching logic as done in MMSerialPortAt when processing URCs! */ result = g_regex_match_full (voice_call_regex, urc, -1, 0, 0, &match_info, &error); g_assert_no_error (error); g_assert (result); result = mm_simtech_parse_voice_call_urc (match_info, &start_or_stop, &duration, &error); g_assert_no_error (error); g_assert (result); g_assert_cmpuint (expected_start_or_stop, ==, start_or_stop); g_assert_cmpuint (expected_duration, ==, duration); } static void test_voice_call_begin_urc (void) { common_test_voice_call_urc ("\r\nVOICE CALL: BEGIN\r\n", TRUE, 0); } static void test_voice_call_end_urc (void) { common_test_voice_call_urc ("\r\nVOICE CALL: END\r\n", FALSE, 0); } static void test_voice_call_end_duration_urc (void) { common_test_voice_call_urc ("\r\nVOICE CALL: END: 000041\r\n", FALSE, 41); } /*****************************************************************************/ static void common_test_missed_call_urc (const gchar *urc, const gchar *expected_details) { g_autoptr(GRegex) missed_call_regex = NULL; g_autoptr(GMatchInfo) match_info = NULL; g_autofree gchar *details = NULL; GError *error = NULL; gboolean result; missed_call_regex = mm_simtech_get_missed_call_urc_regex (); /* Same matching logic as done in MMSerialPortAt when processing URCs! */ result = g_regex_match_full (missed_call_regex, urc, -1, 0, 0, &match_info, &error); g_assert_no_error (error); g_assert (result); result = mm_simtech_parse_missed_call_urc (match_info, &details, &error); g_assert_no_error (error); g_assert (result); g_assert_cmpstr (expected_details, ==, details); } static void test_missed_call_urc (void) { common_test_missed_call_urc ("\r\nMISSED_CALL: 11:01AM 07712345678\r\n", "11:01AM 07712345678"); } /*****************************************************************************/ static void common_test_cring_urc (const gchar *urc, const gchar *expected_type) { g_autoptr(GRegex) cring_regex = NULL; g_autoptr(GMatchInfo) match_info = NULL; g_autofree gchar *type = NULL; GError *error = NULL; gboolean result; cring_regex = mm_simtech_get_cring_urc_regex (); /* Same matching logic as done in MMSerialPortAt when processing URCs! */ result = g_regex_match_full (cring_regex, urc, -1, 0, 0, &match_info, &error); g_assert_no_error (error); g_assert (result); type = g_match_info_fetch (match_info, 1); g_assert (type); g_assert_cmpstr (type, ==, expected_type); } static void test_cring_urc_two_crs (void) { common_test_cring_urc ("\r\r\n+CRING: VOICE\r\r\n", "VOICE"); } static void test_cring_urc_one_cr (void) { common_test_cring_urc ("\r\n+CRING: VOICE\r\n", "VOICE"); } /*****************************************************************************/ static void common_test_rxdtmf_urc (const gchar *urc, const gchar *expected_str) { g_autoptr(GRegex) rxdtmf_regex = NULL; g_autoptr(GMatchInfo) match_info = NULL; g_autofree gchar *type = NULL; GError *error = NULL; gboolean result; rxdtmf_regex = mm_simtech_get_rxdtmf_urc_regex (); /* Same matching logic as done in MMSerialPortAt when processing URCs! */ result = g_regex_match_full (rxdtmf_regex, urc, -1, 0, 0, &match_info, &error); g_assert_no_error (error); g_assert (result); type = g_match_info_fetch (match_info, 1); g_assert (type); g_assert_cmpstr (type, ==, expected_str); } static void test_rxdtmf_urc_two_crs (void) { common_test_rxdtmf_urc ("\r\r\n+RXDTMF: 8\r\r\n", "8"); common_test_rxdtmf_urc ("\r\r\n+RXDTMF: *\r\r\n", "*"); common_test_rxdtmf_urc ("\r\r\n+RXDTMF: #\r\r\n", "#"); common_test_rxdtmf_urc ("\r\r\n+RXDTMF: A\r\r\n", "A"); } static void test_rxdtmf_urc_one_cr (void) { common_test_rxdtmf_urc ("\r\n+RXDTMF: 8\r\n", "8"); common_test_rxdtmf_urc ("\r\n+RXDTMF: *\r\n", "*"); common_test_rxdtmf_urc ("\r\n+RXDTMF: #\r\n", "#"); common_test_rxdtmf_urc ("\r\n+RXDTMF: A\r\n", "A"); } /*****************************************************************************/ int main (int argc, char **argv) { setlocale (LC_ALL, ""); g_test_init (&argc, &argv, NULL); g_test_add_func ("/MM/simtech/clcc/urc/single", test_clcc_urc_single); g_test_add_func ("/MM/simtech/clcc/urc/multiple", test_clcc_urc_multiple); g_test_add_func ("/MM/simtech/clcc/urc/complex", test_clcc_urc_complex); g_test_add_func ("/MM/simtech/voicecall/urc/begin", test_voice_call_begin_urc); g_test_add_func ("/MM/simtech/voicecall/urc/end", test_voice_call_end_urc); g_test_add_func ("/MM/simtech/voicecall/urc/end-duration", test_voice_call_end_duration_urc); g_test_add_func ("/MM/simtech/missedcall/urc", test_missed_call_urc); g_test_add_func ("/MM/simtech/cring/urc/two-crs", test_cring_urc_two_crs); g_test_add_func ("/MM/simtech/cring/urc/one-cr", test_cring_urc_one_cr); g_test_add_func ("/MM/simtech/rxdtmf/urc/two-crs", test_rxdtmf_urc_two_crs); g_test_add_func ("/MM/simtech/rxdtmf/urc/one-cr", test_rxdtmf_urc_one_cr); return g_test_run (); } ModemManager-1.23.4-dev/src/plugins/symbol.map000066400000000000000000000001461456466623000212020ustar00rootroot00000000000000{ global: mm_plugin_major_version*; mm_plugin_minor_version*; mm_plugin_create*; local: *; }; ModemManager-1.23.4-dev/src/plugins/telit/000077500000000000000000000000001456466623000203165ustar00rootroot00000000000000ModemManager-1.23.4-dev/src/plugins/telit/77-mm-telit-port-types.rules000066400000000000000000000277401456466623000255130ustar00rootroot00000000000000# do not edit this file, it will be overwritten on update ACTION!="add|change|move|bind", GOTO="mm_telit_end" SUBSYSTEMS=="usb", ATTRS{idVendor}=="1bc7", GOTO="mm_telit_generic" SUBSYSTEMS=="usb", ATTRS{idVendor}=="8087", GOTO="mm_telit_intel" GOTO="mm_telit_end" LABEL="mm_telit_generic" SUBSYSTEMS=="usb", ATTRS{bInterfaceNumber}=="?*", ENV{.MM_USBIFNUM}="$attr{bInterfaceNumber}" # UC864-E, UC864-E-AUTO, UC864-K, UC864-WD, UC864-WDU ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1003", ENV{.MM_USBIFNUM}=="00", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1" ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1003", ENV{.MM_USBIFNUM}=="02", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1" # UC864-G ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1004", ENV{.MM_USBIFNUM}=="00", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1" ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1004", ENV{.MM_USBIFNUM}=="02", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_GPS}="1" ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1004", ENV{.MM_USBIFNUM}=="03", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1" # CC864-DUAL ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1005", ENV{.MM_USBIFNUM}=="00", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1" ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1005", ENV{.MM_USBIFNUM}=="02", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_GPS}="1" ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1005", ENV{.MM_USBIFNUM}=="03", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1" # CC864-SINGLE, CC864-KPS ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1006", ENV{.MM_USBIFNUM}=="00", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1" ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1006", ENV{.MM_USBIFNUM}=="02", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1" # DE910-DUAL ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1010", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_GPS}="1" ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1010", ENV{.MM_USBIFNUM}=="02", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1" ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1010", ENV{.MM_USBIFNUM}=="03", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1" # CE910-DUAL ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1011", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1" # LE910C1-EUX ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1031", ENV{.MM_USBIFNUM}=="00", ENV{ID_MM_PORT_TYPE_QCDM}="1" ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1031", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1" ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1031", ENV{.MM_USBIFNUM}=="02", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1" # LE910C1-EUX (ECM composition) ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1033", ENV{.MM_USBIFNUM}=="00", ENV{ID_MM_PORT_TYPE_QCDM}="1" ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1033", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1" ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1033", ENV{.MM_USBIFNUM}=="02", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1" # LE922, LM9x0 ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1040", ENV{.MM_USBIFNUM}=="00", ENV{ID_MM_PORT_TYPE_QCDM}="1" ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1040", ENV{.MM_USBIFNUM}=="03", ENV{ID_MM_PORT_IGNORE}="1" ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1040", ENV{.MM_USBIFNUM}=="04", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1" ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1040", ENV{.MM_USBIFNUM}=="05", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1" ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1040", ENV{.MM_USBIFNUM}=="06", ENV{ID_MM_PORT_IGNORE}="1" # LE922, LM9x0 (MBIM composition) ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1041", ENV{.MM_USBIFNUM}=="00", ENV{ID_MM_PORT_TYPE_QCDM}="1" ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1041", ENV{.MM_USBIFNUM}=="04", ENV{ID_MM_PORT_IGNORE}="1" ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1041", ENV{.MM_USBIFNUM}=="05", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1" ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1041", ENV{.MM_USBIFNUM}=="06", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1" ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1041", ENV{.MM_USBIFNUM}=="07", ENV{ID_MM_PORT_IGNORE}="1" # FN980 ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1050", ENV{.MM_USBIFNUM}=="00", ENV{ID_MM_PORT_TYPE_QCDM}="1" ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1050", ENV{.MM_USBIFNUM}=="03", ENV{ID_MM_PORT_IGNORE}="1" ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1050", ENV{.MM_USBIFNUM}=="04", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1" ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1050", ENV{.MM_USBIFNUM}=="05", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1" ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1050", ENV{.MM_USBIFNUM}=="06", ENV{ID_MM_PORT_IGNORE}="1" # FN980 (MBIM composition) ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1051", ENV{.MM_USBIFNUM}=="00", ENV{ID_MM_PORT_TYPE_QCDM}="1" ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1051", ENV{.MM_USBIFNUM}=="04", ENV{ID_MM_PORT_IGNORE}="1" ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1051", ENV{.MM_USBIFNUM}=="05", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1" ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1051", ENV{.MM_USBIFNUM}=="06", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1" ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1051", ENV{.MM_USBIFNUM}=="07", ENV{ID_MM_PORT_IGNORE}="1" # LN920 ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1060", ENV{.MM_USBIFNUM}=="00", ENV{ID_MM_PORT_TYPE_QCDM}="1" ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1060", ENV{.MM_USBIFNUM}=="03", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_GPS}="1" ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1060", ENV{.MM_USBIFNUM}=="04", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1" ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1060", ENV{.MM_USBIFNUM}=="05", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1" ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1060", ENV{.MM_USBIFNUM}=="06", ENV{ID_MM_PORT_IGNORE}="1" # LN920 (MBIM composition) ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1061", ENV{.MM_USBIFNUM}=="00", ENV{ID_MM_PORT_TYPE_QCDM}="1" ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1061", ENV{.MM_USBIFNUM}=="04", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_GPS}="1" ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1061", ENV{.MM_USBIFNUM}=="05", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1" ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1061", ENV{.MM_USBIFNUM}=="06", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1" ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1061", ENV{.MM_USBIFNUM}=="07", ENV{ID_MM_PORT_IGNORE}="1" # FN990 ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1070", ENV{.MM_USBIFNUM}=="00", ENV{ID_MM_PORT_TYPE_QCDM}="1" ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1070", ENV{.MM_USBIFNUM}=="03", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_GPS}="1" ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1070", ENV{.MM_USBIFNUM}=="04", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1" ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1070", ENV{.MM_USBIFNUM}=="05", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1" ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1070", ENV{.MM_USBIFNUM}=="06", ENV{ID_MM_PORT_IGNORE}="1" # FN990 (MBIM composition) ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1071", ENV{.MM_USBIFNUM}=="00", ENV{ID_MM_PORT_TYPE_QCDM}="1" ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1071", ENV{.MM_USBIFNUM}=="04", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_GPS}="1" ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1071", ENV{.MM_USBIFNUM}=="05", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1" ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1071", ENV{.MM_USBIFNUM}=="06", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1" ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1071", ENV{.MM_USBIFNUM}=="07", ENV{ID_MM_PORT_IGNORE}="1" # FN990 (PCIe composition: ignore, since it should not be detected as a modem) ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1075", ENV{ID_MM_DEVICE_IGNORE}="1" # LE910C1 with default usb cfg ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1201", ENV{.MM_USBIFNUM}=="00", ENV{ID_MM_PORT_TYPE_QCDM}="1" ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1201", ENV{.MM_USBIFNUM}=="03", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_GPS}="1" ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1201", ENV{.MM_USBIFNUM}=="04", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1" ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1201", ENV{.MM_USBIFNUM}=="05", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1" ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1201", ENV{.MM_USBIFNUM}=="06", ENV{ID_MM_PORT_IGNORE}="1" # LE910C1 (MBIM) ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1204", ENV{.MM_USBIFNUM}=="00", ENV{ID_MM_PORT_TYPE_QCDM}="1" ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1204", ENV{.MM_USBIFNUM}=="04", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_GPS}="1" ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1204", ENV{.MM_USBIFNUM}=="05", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1" ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1204", ENV{.MM_USBIFNUM}=="06", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1" ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1204", ENV{.MM_USBIFNUM}=="07", ENV{ID_MM_PORT_IGNORE}="1" # ME910C1 ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1101", ENV{.MM_USBIFNUM}=="00", ENV{ID_MM_PORT_TYPE_QCDM}="1" ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1101", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1" ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1101", ENV{.MM_USBIFNUM}=="02", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1" # MEx10G1 ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="110a", ENV{.MM_USBIFNUM}=="00", ENV{ID_MM_PORT_TYPE_QCDM}="1" ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="110a", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1" ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="110a", ENV{.MM_USBIFNUM}=="02", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1" # LE910S1 (RNDIS) # The following port is ignored since it's a diagnostic port for collecting proprietary modem traces (not QCDM) ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="7010", ENV{.MM_USBIFNUM}=="02", ENV{ID_MM_PORT_IGNORE}="1" ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="7010", ENV{.MM_USBIFNUM}=="03", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1" ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="7010", ENV{.MM_USBIFNUM}=="04", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1" # LE910S1 (ECM) # The following port is ignored since it's a diagnostic port for collecting proprietary modem traces (not QCDM) ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="7011", ENV{.MM_USBIFNUM}=="02", ENV{ID_MM_PORT_IGNORE}="1" ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="7011", ENV{.MM_USBIFNUM}=="03", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1" ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="7011", ENV{.MM_USBIFNUM}=="04", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1" # LM940/960 initial port delay ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1040", ENV{ID_MM_TELIT_PORT_DELAY}="1" ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1041", ENV{ID_MM_TELIT_PORT_DELAY}="1" ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1042", ENV{ID_MM_TELIT_PORT_DELAY}="1" ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1043", ENV{ID_MM_TELIT_PORT_DELAY}="1" # FN980 initial port delay ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1050", ENV{ID_MM_TELIT_PORT_DELAY}="1" ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1051", ENV{ID_MM_TELIT_PORT_DELAY}="1" # LN920 initial port delay ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1060", ENV{ID_MM_TELIT_PORT_DELAY}="1" ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1061", ENV{ID_MM_TELIT_PORT_DELAY}="1" # FN990 initial port delay ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1070", ENV{ID_MM_TELIT_PORT_DELAY}="1" ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1071", ENV{ID_MM_TELIT_PORT_DELAY}="1" GOTO="mm_telit_end" LABEL="mm_telit_intel" # Telit LN930, generic Intel vid:pid in MBIM mode ATTRS{idVendor}=="8087", ATTRS{idProduct}=="0911", ENV{ID_MM_PREFERRED_NETWORKS_CPOL_DISABLED}="1" LABEL="mm_telit_end" ModemManager-1.23.4-dev/src/plugins/telit/mm-broadband-modem-mbim-telit.c000066400000000000000000000230411456466623000261450ustar00rootroot00000000000000/* -*- 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) 2019 Daniele Palmas */ #include #include #include #include #include #include #include "ModemManager.h" #include "mm-log-object.h" #include "mm-modem-helpers.h" #include "mm-iface-modem.h" #include "mm-base-modem-at.h" #include "mm-broadband-modem-mbim-telit.h" #include "mm-modem-helpers-telit.h" #include "mm-shared-telit.h" static void iface_modem_init (MMIfaceModem *iface); static void shared_telit_init (MMSharedTelit *iface); static MMIfaceModem *iface_modem_parent; G_DEFINE_TYPE_EXTENDED (MMBroadbandModemMbimTelit, mm_broadband_modem_mbim_telit, MM_TYPE_BROADBAND_MODEM_MBIM, 0, G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM, iface_modem_init) G_IMPLEMENT_INTERFACE (MM_TYPE_SHARED_TELIT, shared_telit_init)) /*****************************************************************************/ /* Load supported modes (Modem interface) */ static GArray * load_supported_modes_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { return (GArray *) g_task_propagate_pointer (G_TASK (res), error); } static void load_supported_modes_ready (MMIfaceModem *self, GAsyncResult *res, GTask *task) { MMModemModeCombination modes_combination; MMModemMode modes_mask = MM_MODEM_MODE_NONE; const gchar *response; GArray *modes; GArray *all; GArray *combinations; GArray *filtered; GError *error = NULL; MMSharedTelit *shared = MM_SHARED_TELIT (self); guint i; response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error); if (error) { g_prefix_error (&error, "generic query of supported 3GPP networks with WS46=? failed: "); g_task_return_error (task, error); g_object_unref (task); return; } modes = mm_3gpp_parse_ws46_test_response (response, self, &error); if (!modes) { g_prefix_error (&error, "parsing WS46=? response failed: "); g_task_return_error (task, error); g_object_unref (task); return; } for (i = 0; i < modes->len; i++) { MMModemMode mode; g_autofree gchar *str = NULL; mode = g_array_index (modes, MMModemMode, i); modes_mask |= mode; str = mm_modem_mode_build_string_from_mask (mode); mm_obj_dbg (self, "device allows (3GPP) mode combination: %s", str); } g_array_unref (modes); /* Build a mask with all supported modes */ all = g_array_sized_new (FALSE, FALSE, sizeof (MMModemModeCombination), 1); modes_combination.allowed = modes_mask; modes_combination.preferred = MM_MODEM_MODE_NONE; g_array_append_val (all, modes_combination); /* Filter out those unsupported modes */ combinations = mm_telit_build_modes_list(); filtered = mm_filter_supported_modes (all, combinations, self); g_array_unref (all); g_array_unref (combinations); mm_shared_telit_store_supported_modes (shared, filtered); g_task_return_pointer (task, filtered, (GDestroyNotify) g_array_unref); g_object_unref (task); } static void load_supported_modes (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; task = g_task_new (self, NULL, callback, user_data); mm_base_modem_at_command (MM_BASE_MODEM (self), "+WS46=?", 3, TRUE, /* allow caching, it's a test command */ (GAsyncReadyCallback) load_supported_modes_ready, task); } /*****************************************************************************/ /* Load revision (Modem interface) */ static gchar * load_revision_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { return g_task_propagate_pointer (G_TASK (res), error); } static void load_revision_ready_shared (MMIfaceModem *self, GAsyncResult *res, GTask *task) { GError *error = NULL; gchar *revision = NULL; revision = mm_shared_telit_modem_load_revision_finish (self, res, &error); if (!revision) { /* give up */ g_task_return_error (task, error); g_object_unref (task); return; } mm_shared_telit_store_revision (MM_SHARED_TELIT (self), revision); g_task_return_pointer (task, revision, g_free); g_object_unref (task); } static void parent_load_revision_ready (MMIfaceModem *self, GAsyncResult *res, GTask *task) { gchar *revision = NULL; revision = iface_modem_parent->load_revision_finish (self, res, NULL); if (!revision || !strlen (revision)) { /* Some firmware versions do not properly populate the revision in the * MBIM response, so try using the AT ports */ g_free (revision); mm_shared_telit_modem_load_revision ( self, (GAsyncReadyCallback)load_revision_ready_shared, task); return; } mm_shared_telit_store_revision (MM_SHARED_TELIT (self), revision); g_task_return_pointer (task, revision, g_free); g_object_unref (task); } static void load_revision (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data) { /* Run parent's loading */ /* Telit's custom revision loading (in telit/mm-shared) is AT-only and the * MBIM modem might not have an AT port available, so we call the parent's * load_revision and store the revision taken from the firmware info capabilities. */ iface_modem_parent->load_revision ( MM_IFACE_MODEM (self), (GAsyncReadyCallback)parent_load_revision_ready, g_task_new (self, NULL, callback, user_data)); } /*****************************************************************************/ MMBroadbandModemMbimTelit * mm_broadband_modem_mbim_telit_new (const gchar *device, const gchar *physdev, const gchar **drivers, const gchar *plugin, guint16 vendor_id, guint16 product_id, guint16 subsystem_vendor_id) { return g_object_new (MM_TYPE_BROADBAND_MODEM_MBIM_TELIT, MM_BASE_MODEM_DEVICE, device, MM_BASE_MODEM_PHYSDEV, physdev, MM_BASE_MODEM_DRIVERS, drivers, MM_BASE_MODEM_PLUGIN, plugin, MM_BASE_MODEM_VENDOR_ID, vendor_id, MM_BASE_MODEM_PRODUCT_ID, product_id, MM_BASE_MODEM_SUBSYSTEM_VENDOR_ID, subsystem_vendor_id, /* MBIM bearer supports NET only */ MM_BASE_MODEM_DATA_NET_SUPPORTED, TRUE, MM_BASE_MODEM_DATA_TTY_SUPPORTED, FALSE, MM_IFACE_MODEM_SIM_HOT_SWAP_SUPPORTED, TRUE, MM_IFACE_MODEM_PERIODIC_SIGNAL_CHECK_DISABLED, TRUE, NULL); } static void mm_broadband_modem_mbim_telit_init (MMBroadbandModemMbimTelit *self) { } static void iface_modem_init (MMIfaceModem *iface) { iface_modem_parent = g_type_interface_peek_parent (iface); iface->set_current_bands = mm_shared_telit_modem_set_current_bands; iface->set_current_bands_finish = mm_shared_telit_modem_set_current_bands_finish; iface->load_current_bands = mm_shared_telit_modem_load_current_bands; iface->load_current_bands_finish = mm_shared_telit_modem_load_current_bands_finish; iface->load_supported_bands = mm_shared_telit_modem_load_supported_bands; iface->load_supported_bands_finish = mm_shared_telit_modem_load_supported_bands_finish; iface->load_supported_modes = load_supported_modes; iface->load_supported_modes_finish = load_supported_modes_finish; iface->load_current_modes = mm_shared_telit_load_current_modes; iface->load_current_modes_finish = mm_shared_telit_load_current_modes_finish; iface->set_current_modes = mm_shared_telit_set_current_modes; iface->set_current_modes_finish = mm_shared_telit_set_current_modes_finish; iface->load_revision_finish = load_revision_finish; iface->load_revision = load_revision; } static MMIfaceModem * peek_parent_modem_interface (MMSharedTelit *self) { return iface_modem_parent; } static void shared_telit_init (MMSharedTelit *iface) { iface->peek_parent_modem_interface = peek_parent_modem_interface; } static void mm_broadband_modem_mbim_telit_class_init (MMBroadbandModemMbimTelitClass *klass) { } ModemManager-1.23.4-dev/src/plugins/telit/mm-broadband-modem-mbim-telit.h000066400000000000000000000050641456466623000261570ustar00rootroot00000000000000/* -*- 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) 2019 Daniele Palmas */ #ifndef MM_BROADBAND_MODEM_MBIM_TELIT_H #define MM_BROADBAND_MODEM_MBIM_TELIT_H #include "mm-broadband-modem-mbim.h" #define MM_TYPE_BROADBAND_MODEM_MBIM_TELIT (mm_broadband_modem_mbim_telit_get_type ()) #define MM_BROADBAND_MODEM_MBIM_TELIT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_BROADBAND_MODEM_MBIM_TELIT, MMBroadbandModemMbimTelit)) #define MM_BROADBAND_MODEM_MBIM_TELIT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_BROADBAND_MODEM_MBIM_TELIT, MMBroadbandModemMbimTelitClass)) #define MM_IS_BROADBAND_MODEM_MBIM_TELIT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_BROADBAND_MODEM_MBIM_TELIT)) #define MM_IS_BROADBAND_MODEM_MBIM_TELIT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_BROADBAND_MODEM_MBIM_TELIT)) #define MM_BROADBAND_MODEM_MBIM_TELIT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_BROADBAND_MODEM_MBIM_TELIT, MMBroadbandModemMbimTelitClass)) typedef struct _MMBroadbandModemMbimTelit MMBroadbandModemMbimTelit; typedef struct _MMBroadbandModemMbimTelitClass MMBroadbandModemMbimTelitClass; struct _MMBroadbandModemMbimTelit { MMBroadbandModemMbim parent; }; struct _MMBroadbandModemMbimTelitClass{ MMBroadbandModemMbimClass parent; }; GType mm_broadband_modem_mbim_telit_get_type (void); MMBroadbandModemMbimTelit *mm_broadband_modem_mbim_telit_new (const gchar *device, const gchar *physdev, const gchar **drivers, const gchar *plugin, guint16 vendor_id, guint16 product_id, guint16 subsystem_vendor_id); #endif /* MM_BROADBAND_MODEM_TELIT_H */ ModemManager-1.23.4-dev/src/plugins/telit/mm-broadband-modem-telit.c000066400000000000000000001536221456466623000252340ustar00rootroot00000000000000/* -*- 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 - 2012 Red Hat, Inc. * Copyright (C) 2012 Aleksander Morgado */ #include #include #include #include #include #include #include "ModemManager.h" #include "mm-log-object.h" #include "mm-errors-types.h" #include "mm-modem-helpers.h" #include "mm-base-modem-at.h" #include "mm-iface-modem.h" #include "mm-iface-modem-3gpp.h" #include "mm-iface-modem-location.h" #include "mm-broadband-modem-telit.h" #include "mm-modem-helpers-telit.h" #include "mm-telit-enums-types.h" #include "mm-shared-telit.h" static void iface_modem_init (MMIfaceModem *iface); static void iface_modem_3gpp_init (MMIfaceModem3gpp *iface); static void shared_telit_init (MMSharedTelit *iface); static void iface_modem_location_init (MMIfaceModemLocation *iface); static MMIfaceModem *iface_modem_parent; static MMIfaceModem3gpp *iface_modem_3gpp_parent; static MMIfaceModemLocation *iface_modem_location_parent; G_DEFINE_TYPE_EXTENDED (MMBroadbandModemTelit, mm_broadband_modem_telit, MM_TYPE_BROADBAND_MODEM, 0, G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM, iface_modem_init) G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_3GPP, iface_modem_3gpp_init) G_IMPLEMENT_INTERFACE (MM_TYPE_SHARED_TELIT, shared_telit_init) G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_LOCATION, iface_modem_location_init)); #define CSIM_UNLOCK_MAX_TIMEOUT 3 typedef enum { FEATURE_SUPPORT_UNKNOWN, FEATURE_NOT_SUPPORTED, FEATURE_SUPPORTED } FeatureSupport; struct _MMBroadbandModemTelitPrivate { FeatureSupport csim_lock_support; MMTelitQssStatus qss_status; MMTelitCsimLockState csim_lock_state; GTask *csim_lock_task; guint csim_lock_timeout_id; gboolean parse_qss; MMModemLocationSource enabled_sources; }; typedef struct { MMModemLocationSource source; guint gps_enable_step; } LocationGatheringContext; /* * AT$GPSNMUN * enable: 0 NMEA stream disabled (default) * 1 NMEA stream enabled in the form $GPSNMUN: * 2 NMEA stream enabled in the form * 3 dedicated NMEA stream * GGA: 0 disable (default), 1 enable * GLL: 0 disable (default), 1 enable * GSA: 0 disable (default), 1 enable * GSV: 0 disable (default), 1 enable * RMC: 0 disable (default), 1 enable * VTG: 0 disable (default), 1 enable */ static const gchar *gps_enable[] = { "$GPSP=1", "$GPSNMUN=2,1,1,1,1,1,1" }; static gboolean disable_location_gathering_finish (MMIfaceModemLocation *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void gps_disabled_ready (MMBaseModem *self, GAsyncResult *res, GTask *task) { LocationGatheringContext *ctx; MMPortSerialGps *gps_port; GError *error = NULL; mm_base_modem_at_command_finish (self, res, &error); ctx = g_task_get_task_data (task); /* Only use the GPS port in NMEA/RAW setups */ if (ctx->source & (MM_MODEM_LOCATION_SOURCE_GPS_NMEA | MM_MODEM_LOCATION_SOURCE_GPS_RAW)) { /* Even if we get an error here, we try to close the GPS port */ gps_port = mm_base_modem_peek_port_gps (self); if (gps_port) mm_port_serial_close (MM_PORT_SERIAL (gps_port)); } if (error) g_task_return_error (task, error); else g_task_return_boolean (task, TRUE); g_object_unref (task); } static void disable_location_gathering (MMIfaceModemLocation *self, MMModemLocationSource source, GAsyncReadyCallback callback, gpointer user_data) { MMBroadbandModemTelit *telit = MM_BROADBAND_MODEM_TELIT (self); gboolean stop_gps = FALSE; LocationGatheringContext *ctx; GTask *task; ctx = g_new (LocationGatheringContext, 1); ctx->source = source; task = g_task_new (self, NULL, callback, user_data); g_task_set_task_data (task, ctx, g_free); /* Only stop GPS engine if no GPS-related sources enabled */ if (source & (MM_MODEM_LOCATION_SOURCE_GPS_NMEA | MM_MODEM_LOCATION_SOURCE_GPS_RAW | MM_MODEM_LOCATION_SOURCE_GPS_UNMANAGED)) { telit->priv->enabled_sources &= ~source; if (!(telit->priv->enabled_sources & (MM_MODEM_LOCATION_SOURCE_GPS_NMEA | MM_MODEM_LOCATION_SOURCE_GPS_RAW | MM_MODEM_LOCATION_SOURCE_GPS_UNMANAGED))) stop_gps = TRUE; } if (stop_gps) { mm_base_modem_at_command (MM_BASE_MODEM (self), "$GPSP=0", 3, FALSE, (GAsyncReadyCallback)gps_disabled_ready, task); return; } /* For any other location (e.g. 3GPP), or if still some GPS needed, just return */ g_task_return_boolean (task, TRUE); g_object_unref (task); } static void gps_enabled_ready (MMBaseModem *self, GAsyncResult *res, GTask *task) { LocationGatheringContext *ctx; GError *error = NULL; ctx = g_task_get_task_data (task); if (!mm_base_modem_at_command_finish (self, res, &error)) { g_prefix_error (&error, "couldn't power up GNSS controller: "); g_task_return_error (task, error); g_object_unref (task); return; } /* After Receiver was powered up we still have to enable unsolicited NMEA events */ if (ctx->gps_enable_step < G_N_ELEMENTS (gps_enable)) { mm_base_modem_at_command (MM_BASE_MODEM (self), gps_enable[ctx->gps_enable_step++], 3, FALSE, (GAsyncReadyCallback)gps_enabled_ready, task); return; } mm_obj_dbg (self, "GNSS controller is powered up"); /* Only use the GPS port in NMEA/RAW setups */ if (ctx->source & (MM_MODEM_LOCATION_SOURCE_GPS_NMEA | MM_MODEM_LOCATION_SOURCE_GPS_RAW)) { MMPortSerialGps *gps_port; gps_port = mm_base_modem_peek_port_gps (self); if (!gps_port || !mm_port_serial_open (MM_PORT_SERIAL (gps_port), &error)) { if (error) g_task_return_error (task, error); else g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't open raw GPS serial port"); } else g_task_return_boolean (task, TRUE); } else g_task_return_boolean (task, TRUE); g_object_unref (task); } static void parent_enable_location_gathering_ready (MMIfaceModemLocation *_self, GAsyncResult *res, GTask *task) { MMBroadbandModemTelit *self = MM_BROADBAND_MODEM_TELIT (_self); LocationGatheringContext *ctx; gboolean start_gps = FALSE; GError *error = NULL; if (!iface_modem_location_parent->enable_location_gathering_finish (_self, res, &error)) { g_task_return_error (task, error); g_object_unref (task); return; } /* Now our own enabling */ ctx = g_task_get_task_data (task); /* NMEA, RAW and UNMANAGED are all enabled in the same way */ if (ctx->source & (MM_MODEM_LOCATION_SOURCE_GPS_NMEA | MM_MODEM_LOCATION_SOURCE_GPS_RAW | MM_MODEM_LOCATION_SOURCE_GPS_UNMANAGED)) { /* Only start GPS engine if not done already */ if (!(self->priv->enabled_sources & (MM_MODEM_LOCATION_SOURCE_GPS_NMEA | MM_MODEM_LOCATION_SOURCE_GPS_RAW | MM_MODEM_LOCATION_SOURCE_GPS_UNMANAGED))) start_gps = TRUE; self->priv->enabled_sources |= ctx->source; } if (start_gps && ctx->gps_enable_step < G_N_ELEMENTS (gps_enable)) { mm_base_modem_at_command (MM_BASE_MODEM (self), gps_enable[ctx->gps_enable_step++], 3, FALSE, (GAsyncReadyCallback)gps_enabled_ready, task); return; } /* For any other location (e.g. 3GPP), or if GPS already running just return */ g_task_return_boolean (task, TRUE); g_object_unref (task); } static void enable_location_gathering (MMIfaceModemLocation *self, MMModemLocationSource source, GAsyncReadyCallback callback, gpointer user_data) { LocationGatheringContext *ctx; GTask *task; ctx = g_new (LocationGatheringContext, 1); ctx->source = source; ctx->gps_enable_step = 0; task = g_task_new (self, NULL, callback, user_data); g_task_set_task_data (task, ctx, g_free); /* Chain up parent's gathering enable */ iface_modem_location_parent->enable_location_gathering ( self, source, (GAsyncReadyCallback)parent_enable_location_gathering_ready, task); } static void trace_received (MMPortSerialGps *port, const gchar *trace, MMIfaceModemLocation *self) { mm_iface_modem_location_gps_update (self, trace); } static gboolean enable_location_gathering_finish (MMIfaceModemLocation *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void setup_ports (MMBroadbandModem *self) { MMPortSerialGps *gps_data_port; /* Call parent's setup ports first always */ MM_BROADBAND_MODEM_CLASS (mm_broadband_modem_telit_parent_class)->setup_ports (self); gps_data_port = mm_base_modem_peek_port_gps (MM_BASE_MODEM (self)); if (gps_data_port) { /* It may happen that the modem was started with GPS already enabled, * in this case GPSP AT command returns always error. Disable it for consistency */ mm_base_modem_at_command (MM_BASE_MODEM (self), "$GPSP=0", 3, FALSE, FALSE, NULL); /* Add handler for the NMEA traces */ mm_port_serial_gps_add_trace_handler (gps_data_port, (MMPortSerialGpsTraceFn)trace_received, self, NULL); } } static MMModemLocationSource location_load_capabilities_finish (MMIfaceModemLocation *self, GAsyncResult *res, GError **error) { GError *inner_error = NULL; gssize value; value = g_task_propagate_int (G_TASK (res), &inner_error); if (inner_error) { g_propagate_error (error, inner_error); return MM_MODEM_LOCATION_SOURCE_NONE; } return (MMModemLocationSource)value; } static void gpsp_test_ready (MMIfaceModemLocation *self, GAsyncResult *res, GTask *task) { GError *error = NULL; MMModemLocationSource sources; sources = GPOINTER_TO_UINT (g_task_get_task_data (task)); mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error); if (error) { mm_obj_dbg (self, "GPS controller not supported: %s", error->message); g_clear_error (&error); } else if (mm_base_modem_peek_port_gps (MM_BASE_MODEM (self))) sources |= (MM_MODEM_LOCATION_SOURCE_GPS_NMEA | MM_MODEM_LOCATION_SOURCE_GPS_RAW | MM_MODEM_LOCATION_SOURCE_GPS_UNMANAGED); g_task_return_int (task, sources); g_object_unref (task); } static void parent_load_capabilities_ready (MMIfaceModemLocation *self, GAsyncResult *res, GTask *task) { MMModemLocationSource sources; GError *error = NULL; sources = iface_modem_location_parent->load_capabilities_finish (self, res, &error); if (error) { g_task_return_error (task, error); g_object_unref (task); return; } g_task_set_task_data (task, GUINT_TO_POINTER (sources), NULL); mm_base_modem_at_command (MM_BASE_MODEM (self), "$GPSP=?", 3, TRUE, (GAsyncReadyCallback)gpsp_test_ready, task); } static void location_load_capabilities (MMIfaceModemLocation *self, GAsyncReadyCallback callback, gpointer user_data) { /* Chain up parent's setup */ iface_modem_location_parent->load_capabilities ( self, (GAsyncReadyCallback)parent_load_capabilities_ready, g_task_new (self, NULL, callback, user_data)); } /*****************************************************************************/ /* Setup SIM hot swap (Modem interface) */ typedef enum { QSS_SETUP_STEP_FIRST, QSS_SETUP_STEP_QUERY, QSS_SETUP_STEP_ENABLE_PRIMARY_PORT, QSS_SETUP_STEP_ENABLE_SECONDARY_PORT, QSS_SETUP_STEP_LAST } QssSetupStep; typedef struct { QssSetupStep step; MMPortSerialAt *primary; MMPortSerialAt *secondary; GError *primary_error; GError *secondary_error; } QssSetupContext; static void qss_setup_step (GTask *task); static void pending_csim_unlock_complete (MMBroadbandModemTelit *self); static void telit_qss_unsolicited_handler (MMPortSerialAt *port, GMatchInfo *match_info, MMBroadbandModemTelit *self) { MMTelitQssStatus cur_qss_status; MMTelitQssStatus prev_qss_status; if (!mm_get_int_from_match_info (match_info, 1, (gint*)&cur_qss_status)) return; prev_qss_status = self->priv->qss_status; self->priv->qss_status = cur_qss_status; if (self->priv->csim_lock_state >= CSIM_LOCK_STATE_LOCK_REQUESTED) { if (prev_qss_status > QSS_STATUS_SIM_REMOVED && cur_qss_status == QSS_STATUS_SIM_REMOVED) { mm_obj_dbg (self, "QSS handler: #QSS=0 after +CSIM=1: CSIM locked!"); self->priv->csim_lock_state = CSIM_LOCK_STATE_LOCKED; } if (prev_qss_status == QSS_STATUS_SIM_REMOVED && cur_qss_status != QSS_STATUS_SIM_REMOVED) { mm_obj_dbg (self, "QSS handler: #QSS>=1 after +CSIM=0: CSIM unlocked!"); self->priv->csim_lock_state = CSIM_LOCK_STATE_UNLOCKED; if (self->priv->csim_lock_timeout_id) { g_source_remove (self->priv->csim_lock_timeout_id); self->priv->csim_lock_timeout_id = 0; } pending_csim_unlock_complete (self); } return; } if (cur_qss_status != prev_qss_status) mm_obj_dbg (self, "QSS handler: status changed %s -> %s", mm_telit_qss_status_get_string (prev_qss_status), mm_telit_qss_status_get_string (cur_qss_status)); if (self->priv->parse_qss == FALSE) { mm_obj_dbg (self, "QSS handler: message ignored"); return; } if ((prev_qss_status == QSS_STATUS_SIM_REMOVED && cur_qss_status != QSS_STATUS_SIM_REMOVED) || (prev_qss_status > QSS_STATUS_SIM_REMOVED && cur_qss_status == QSS_STATUS_SIM_REMOVED)) { mm_obj_msg (self, "QSS handler: SIM swap detected"); mm_iface_modem_process_sim_event (MM_IFACE_MODEM (self)); } } static void qss_setup_context_free (QssSetupContext *ctx) { g_clear_object (&(ctx->primary)); g_clear_object (&(ctx->secondary)); g_clear_error (&(ctx->primary_error)); g_clear_error (&(ctx->secondary_error)); g_slice_free (QssSetupContext, ctx); } static gboolean modem_setup_sim_hot_swap_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void telit_qss_enable_ready (MMBaseModem *self, GAsyncResult *res, GTask *task) { QssSetupContext *ctx; MMPortSerialAt *port; GError **error; g_autoptr(GRegex) pattern = NULL; ctx = g_task_get_task_data (task); if (ctx->step == QSS_SETUP_STEP_ENABLE_PRIMARY_PORT) { port = ctx->primary; error = &ctx->primary_error; } else if (ctx->step == QSS_SETUP_STEP_ENABLE_SECONDARY_PORT) { port = ctx->secondary; error = &ctx->secondary_error; } else g_assert_not_reached (); if (!mm_base_modem_at_command_full_finish (self, res, error)) { mm_obj_warn (self, "QSS: error enabling unsolicited on port %s: %s", mm_port_get_device (MM_PORT (port)), (*error)->message); goto next_step; } pattern = g_regex_new ("#QSS:\\s*([0-3])\\r\\n", G_REGEX_RAW, 0, NULL); g_assert (pattern); mm_port_serial_at_add_unsolicited_msg_handler ( port, pattern, (MMPortSerialAtUnsolicitedMsgFn)telit_qss_unsolicited_handler, self, NULL); next_step: ctx->step++; qss_setup_step (task); } static void telit_qss_query_ready (MMBaseModem *_self, GAsyncResult *res, GTask *task) { MMBroadbandModemTelit *self; GError *error = NULL; const gchar *response; MMTelitQssStatus qss_status; QssSetupContext *ctx; self = MM_BROADBAND_MODEM_TELIT (_self); ctx = g_task_get_task_data (task); response = mm_base_modem_at_command_finish (_self, res, &error); if (error) { mm_obj_warn (self, "could not get \"#QSS?\" reply: %s", error->message); g_error_free (error); goto next_step; } qss_status = mm_telit_parse_qss_query (response, &error); if (error) { mm_obj_warn (self, "QSS query parse error: %s", error->message); g_error_free (error); goto next_step; } mm_obj_dbg (self, "QSS: current status is '%s'", mm_telit_qss_status_get_string (qss_status)); self->priv->qss_status = qss_status; next_step: ctx->step++; qss_setup_step (task); } static void telit_qss_support_ready (MMBaseModem *self, GAsyncResult *res, GTask *task) { GError *error = NULL; QssSetupContext *ctx; ctx = g_task_get_task_data (task); if (!mm_base_modem_at_command_finish (self, res, &error)) { mm_obj_dbg (self, "#QSS command unsupported: '%s'", error->message); g_task_return_error (task, error); g_object_unref (task); return; } ctx->step++; qss_setup_step (task); } static void qss_setup_step (GTask *task) { QssSetupContext *ctx; MMBroadbandModemTelit *self; self = MM_BROADBAND_MODEM_TELIT (g_task_get_source_object (task)); ctx = g_task_get_task_data (task); switch (ctx->step) { case QSS_SETUP_STEP_FIRST: mm_base_modem_at_command (MM_BASE_MODEM (self), "#QSS=?", 3, TRUE, (GAsyncReadyCallback) telit_qss_support_ready, task); return; case QSS_SETUP_STEP_QUERY: mm_base_modem_at_command (MM_BASE_MODEM (self), "#QSS?", 3, FALSE, (GAsyncReadyCallback) telit_qss_query_ready, task); return; case QSS_SETUP_STEP_ENABLE_PRIMARY_PORT: mm_base_modem_at_command_full (MM_BASE_MODEM (self), ctx->primary, "#QSS=1", 3, FALSE, FALSE, /* raw */ NULL, /* cancellable */ (GAsyncReadyCallback) telit_qss_enable_ready, task); return; case QSS_SETUP_STEP_ENABLE_SECONDARY_PORT: if (ctx->secondary) { mm_base_modem_at_command_full (MM_BASE_MODEM (self), ctx->secondary, "#QSS=1", 3, FALSE, FALSE, /* raw */ NULL, /* cancellable */ (GAsyncReadyCallback) telit_qss_enable_ready, task); return; } ctx->step++; /* fall through */ case QSS_SETUP_STEP_LAST: /* If all enabling actions failed (either both, or only primary if * there is no secondary), then we return an error */ if (ctx->primary_error && (ctx->secondary_error || !ctx->secondary)) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "QSS: couldn't enable unsolicited"); } else { g_autoptr(GError) error = NULL; if (!mm_broadband_modem_sim_hot_swap_ports_context_init (MM_BROADBAND_MODEM (self), &error)) mm_obj_warn (self, "failed to initialize SIM hot swap ports context: %s", error->message); g_task_return_boolean (task, TRUE); } g_object_unref (task); break; default: g_assert_not_reached (); } } static void modem_setup_sim_hot_swap (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data) { QssSetupContext *ctx; GTask *task; task = g_task_new (self, NULL, callback, user_data); ctx = g_slice_new0 (QssSetupContext); ctx->step = QSS_SETUP_STEP_FIRST; ctx->primary = mm_base_modem_get_port_primary (MM_BASE_MODEM (self)); ctx->secondary = mm_base_modem_get_port_secondary (MM_BASE_MODEM (self)); g_task_set_task_data (task, ctx, (GDestroyNotify) qss_setup_context_free); qss_setup_step (task); } /*****************************************************************************/ /* SIM hot swap cleanup (Modem interface) */ static void modem_cleanup_sim_hot_swap (MMIfaceModem *self) { mm_broadband_modem_sim_hot_swap_ports_context_reset (MM_BROADBAND_MODEM (self)); } /*****************************************************************************/ /* Load unlock retries (Modem interface) * * NOTE: the logic must make sure that LOAD_UNLOCK_RETRIES_STEP_UNLOCK is always * run if LOAD_UNLOCK_RETRIES_STEP_LOCK has been run. Currently, the logic just * runs all intermediate steps ignoring errors (i.e. not completing the * operation if something fails), so the LOAD_UNLOCK_RETRIES_STEP_UNLOCK is * always run. */ #define CSIM_LOCK_STR "+CSIM=1" #define CSIM_UNLOCK_STR "+CSIM=0" #define CSIM_QUERY_TIMEOUT 3 typedef enum { LOAD_UNLOCK_RETRIES_STEP_FIRST, LOAD_UNLOCK_RETRIES_STEP_LOCK, LOAD_UNLOCK_RETRIES_STEP_PARENT, LOAD_UNLOCK_RETRIES_STEP_UNLOCK, LOAD_UNLOCK_RETRIES_STEP_LAST } LoadUnlockRetriesStep; typedef struct { MMUnlockRetries *retries; LoadUnlockRetriesStep step; } LoadUnlockRetriesContext; static void load_unlock_retries_step (GTask *task); static void load_unlock_retries_context_free (LoadUnlockRetriesContext *ctx) { if (ctx->retries) g_object_unref (ctx->retries); g_slice_free (LoadUnlockRetriesContext, ctx); } static MMUnlockRetries * modem_load_unlock_retries_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { return (MMUnlockRetries *) g_task_propagate_pointer (G_TASK (res), error); } static void csim_unlock_ready (MMBaseModem *_self, GAsyncResult *res, GTask *task) { const gchar *response; GError *error = NULL; MMBroadbandModemTelit *self; LoadUnlockRetriesContext *ctx; self = MM_BROADBAND_MODEM_TELIT (_self); ctx = g_task_get_task_data (task); /* Ignore errors */ response = mm_base_modem_at_command_finish (_self, res, &error); if (!response) { if (g_error_matches (error, MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_NOT_SUPPORTED)) { self->priv->csim_lock_support = FEATURE_NOT_SUPPORTED; } mm_obj_warn (self, "couldn't unlock SIM card: %s", error->message); g_error_free (error); } if (self->priv->csim_lock_support != FEATURE_NOT_SUPPORTED) self->priv->csim_lock_support = FEATURE_SUPPORTED; ctx->step++; load_unlock_retries_step (task); } static void parent_load_unlock_retries_ready (MMIfaceModem *self, GAsyncResult *res, GTask *task) { LoadUnlockRetriesContext *ctx; GError *error = NULL; ctx = g_task_get_task_data (task); if (!(ctx->retries = iface_modem_parent->load_unlock_retries_finish (self, res, &error))) { mm_obj_warn (self, "couldn't load unlock retries with generic logic: %s", error->message); g_error_free (error); } ctx->step++; load_unlock_retries_step (task); } static void csim_lock_ready (MMBaseModem *_self, GAsyncResult *res, GTask *task) { const gchar *response; GError *error = NULL; MMBroadbandModemTelit *self; LoadUnlockRetriesContext *ctx; self = MM_BROADBAND_MODEM_TELIT (_self); ctx = g_task_get_task_data (task); response = mm_base_modem_at_command_finish (_self, res, &error); if (!response) { if (g_error_matches (error, MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_NOT_SUPPORTED) || g_error_matches (error, MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_UNKNOWN)) { self->priv->csim_lock_support = FEATURE_NOT_SUPPORTED; mm_obj_warn (self, "couldn't lock SIM card: %s; continuing without CSIM lock", error->message); g_error_free (error); } else { g_prefix_error (&error, "Couldn't lock SIM card: "); g_task_return_error (task, error); g_object_unref (task); return; } } else { self->priv->csim_lock_state = CSIM_LOCK_STATE_LOCK_REQUESTED; } if (self->priv->csim_lock_support != FEATURE_NOT_SUPPORTED) { self->priv->csim_lock_support = FEATURE_SUPPORTED; } ctx->step++; load_unlock_retries_step (task); } static void handle_csim_locking (GTask *task, gboolean is_lock) { MMBroadbandModemTelit *self; LoadUnlockRetriesContext *ctx; self = MM_BROADBAND_MODEM_TELIT (g_task_get_source_object (task)); ctx = g_task_get_task_data (task); switch (self->priv->csim_lock_support) { case FEATURE_SUPPORT_UNKNOWN: case FEATURE_SUPPORTED: if (is_lock) { mm_base_modem_at_command (MM_BASE_MODEM (self), CSIM_LOCK_STR, CSIM_QUERY_TIMEOUT, FALSE, (GAsyncReadyCallback) csim_lock_ready, task); } else { mm_base_modem_at_command (MM_BASE_MODEM (self), CSIM_UNLOCK_STR, CSIM_QUERY_TIMEOUT, FALSE, (GAsyncReadyCallback) csim_unlock_ready, task); } break; case FEATURE_NOT_SUPPORTED: mm_obj_dbg (self, "CSIM lock not supported by this modem; skipping %s command", is_lock ? "lock" : "unlock"); ctx->step++; load_unlock_retries_step (task); break; default: g_assert_not_reached (); break; } } static void pending_csim_unlock_complete (MMBroadbandModemTelit *self) { LoadUnlockRetriesContext *ctx; ctx = g_task_get_task_data (self->priv->csim_lock_task); if (!ctx->retries) { g_task_return_new_error (self->priv->csim_lock_task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Could not get any of the SIM unlock retries values"); } else { g_task_return_pointer (self->priv->csim_lock_task, g_object_ref (ctx->retries), g_object_unref); } g_clear_object (&self->priv->csim_lock_task); } static gboolean csim_unlock_periodic_check (MMBroadbandModemTelit *self) { if (self->priv->csim_lock_state != CSIM_LOCK_STATE_UNLOCKED) mm_obj_warn (self, "CSIM is still locked after %d seconds; trying to continue anyway", CSIM_UNLOCK_MAX_TIMEOUT); self->priv->csim_lock_timeout_id = 0; pending_csim_unlock_complete (self); g_object_unref (self); return G_SOURCE_REMOVE; } static void load_unlock_retries_step (GTask *task) { MMBroadbandModemTelit *self; LoadUnlockRetriesContext *ctx; self = MM_BROADBAND_MODEM_TELIT (g_task_get_source_object (task)); ctx = g_task_get_task_data (task); switch (ctx->step) { case LOAD_UNLOCK_RETRIES_STEP_FIRST: ctx->step++; /* fall through */ case LOAD_UNLOCK_RETRIES_STEP_LOCK: handle_csim_locking (task, TRUE); break; case LOAD_UNLOCK_RETRIES_STEP_PARENT: iface_modem_parent->load_unlock_retries ( MM_IFACE_MODEM (self), (GAsyncReadyCallback)parent_load_unlock_retries_ready, task); break; case LOAD_UNLOCK_RETRIES_STEP_UNLOCK: handle_csim_locking (task, FALSE); break; case LOAD_UNLOCK_RETRIES_STEP_LAST: self->priv->csim_lock_task = task; if (self->priv->csim_lock_state == CSIM_LOCK_STATE_LOCKED) { mm_obj_dbg (self, "CSIM is locked, waiting for #QSS=1"); self->priv->csim_lock_timeout_id = g_timeout_add_seconds (CSIM_UNLOCK_MAX_TIMEOUT, (GSourceFunc) csim_unlock_periodic_check, g_object_ref(self)); } else { self->priv->csim_lock_state = CSIM_LOCK_STATE_UNLOCKED; pending_csim_unlock_complete (self); } break; default: break; } } static void modem_load_unlock_retries (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; LoadUnlockRetriesContext *ctx; g_assert (iface_modem_parent->load_unlock_retries); g_assert (iface_modem_parent->load_unlock_retries_finish); ctx = g_slice_new0 (LoadUnlockRetriesContext); ctx->step = LOAD_UNLOCK_RETRIES_STEP_FIRST; task = g_task_new (self, NULL, callback, user_data); g_task_set_task_data (task, ctx, (GDestroyNotify)load_unlock_retries_context_free); load_unlock_retries_step (task); } /*****************************************************************************/ /* Modem after power up (Modem interface) */ static gboolean modem_after_power_up_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void modem_after_power_up (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; MMBroadbandModemTelit *modem = MM_BROADBAND_MODEM_TELIT (self); task = g_task_new (self, NULL, callback, user_data); mm_obj_dbg (self, "stop ignoring #QSS"); modem->priv->parse_qss = TRUE; g_task_return_boolean (task, TRUE); g_object_unref (task); } /*****************************************************************************/ /* Modem power down (Modem interface) */ static gboolean modem_power_down_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void telit_modem_power_down_ready (MMBaseModem *self, GAsyncResult *res, GTask *task) { GError *error = NULL; if (mm_base_modem_at_command_finish (self, res, &error)) { mm_obj_dbg (self, "sgnore #QSS unsolicited during power down/low"); MM_BROADBAND_MODEM_TELIT (self)->priv->parse_qss = FALSE; } if (error) { mm_obj_warn (self, "failed modem power down: %s", error->message); g_clear_error (&error); } g_task_return_boolean (task, TRUE); g_object_unref (task); } static void modem_power_down (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; task = g_task_new (self, NULL, callback, user_data); mm_base_modem_at_command (MM_BASE_MODEM (self), "+CFUN=4", 20, FALSE, (GAsyncReadyCallback) telit_modem_power_down_ready, task); } /*****************************************************************************/ /* Reset (Modem interface) */ static gboolean modem_reset_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { return !!mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, error); } static void modem_reset (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data) { mm_base_modem_at_command (MM_BASE_MODEM (self), "AT#REBOOT", 8, FALSE, callback, user_data); } /*****************************************************************************/ /* Load access technologies (Modem interface) */ static gboolean load_access_technologies_finish (MMIfaceModem *self, GAsyncResult *res, MMModemAccessTechnology *access_technologies, guint *mask, GError **error) { GVariant *result; result = mm_base_modem_at_sequence_finish (MM_BASE_MODEM (self), res, NULL, error); if (!result) { if (error) g_assert (*error); return FALSE; } *access_technologies = (MMModemAccessTechnology) g_variant_get_uint32 (result); *mask = MM_MODEM_ACCESS_TECHNOLOGY_ANY; return TRUE; } static MMBaseModemAtResponseProcessorResult response_processor_cops_ignore_at_errors (MMBaseModem *self, gpointer none, const gchar *command, const gchar *response, gboolean last_command, const GError *error, GVariant **result, GError **result_error) { g_autoptr(GMatchInfo) match_info = NULL; g_autoptr(GRegex) r = NULL; guint actval = 0; guint mode = 0; guint vid; guint pid; *result = NULL; *result_error = NULL; if (error) { /* Ignore AT errors (ie, ERROR or CMx ERROR) */ if (error->domain != MM_MOBILE_EQUIPMENT_ERROR || last_command) { *result_error = g_error_copy (error); return MM_BASE_MODEM_AT_RESPONSE_PROCESSOR_RESULT_FAILURE; } return MM_BASE_MODEM_AT_RESPONSE_PROCESSOR_RESULT_CONTINUE; } vid = mm_base_modem_get_vendor_id (self); pid = mm_base_modem_get_product_id (self); if (!(vid == 0x1bc7 && (pid == 0x110a || pid == 0x110b))) { /* AcT for non-LPWA modems would be checked by other command */ return MM_BASE_MODEM_AT_RESPONSE_PROCESSOR_RESULT_CONTINUE; } r = g_regex_new ("\\+COPS:\\s*(\\d+),(\\d+),([^,]*)(?:,(\\d+))?(?:\\r\\n)?", 0, 0, NULL); g_assert (r != NULL); if (!g_regex_match (r, response, 0, &match_info)) { g_set_error (result_error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Can't match +COPS? response: '%s'", response); return MM_BASE_MODEM_AT_RESPONSE_PROCESSOR_RESULT_FAILURE; } if (!mm_get_uint_from_match_info (match_info, 1, &mode)) { g_set_error (result_error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Failed to parse mode in +COPS? response: '%s'", response); return MM_BASE_MODEM_AT_RESPONSE_PROCESSOR_RESULT_FAILURE; } if (mode == 2) { g_set_error (result_error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Modem deregistered from the network: aborting AcT query"); return MM_BASE_MODEM_AT_RESPONSE_PROCESSOR_RESULT_FAILURE; } if (!mm_get_uint_from_match_info (match_info, 4, &actval)) { g_set_error (result_error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Failed to parse act in +COPS? response: '%s'", response); return MM_BASE_MODEM_AT_RESPONSE_PROCESSOR_RESULT_FAILURE; } switch (actval) { case 0: *result = g_variant_new_uint32 (MM_MODEM_ACCESS_TECHNOLOGY_GSM); return MM_BASE_MODEM_AT_RESPONSE_PROCESSOR_RESULT_SUCCESS; case 8: *result = g_variant_new_uint32 (MM_MODEM_ACCESS_TECHNOLOGY_LTE_CAT_M); return MM_BASE_MODEM_AT_RESPONSE_PROCESSOR_RESULT_SUCCESS; case 9: *result = g_variant_new_uint32 (MM_MODEM_ACCESS_TECHNOLOGY_LTE_NB_IOT); return MM_BASE_MODEM_AT_RESPONSE_PROCESSOR_RESULT_SUCCESS; default: break; } g_set_error (result_error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Failed to map act in +COPS? response: '%s'", response); return MM_BASE_MODEM_AT_RESPONSE_PROCESSOR_RESULT_FAILURE; } static MMBaseModemAtResponseProcessorResult response_processor_psnt_ignore_at_errors (MMBaseModem *self, gpointer none, const gchar *command, const gchar *response, gboolean last_command, const GError *error, GVariant **result, GError **result_error) { const gchar *psnt; const gchar *mode; *result = NULL; *result_error = NULL; if (error) { /* Ignore AT errors (ie, ERROR or CMx ERROR) */ if (error->domain != MM_MOBILE_EQUIPMENT_ERROR || last_command) { *result_error = g_error_copy (error); return MM_BASE_MODEM_AT_RESPONSE_PROCESSOR_RESULT_FAILURE; } return MM_BASE_MODEM_AT_RESPONSE_PROCESSOR_RESULT_CONTINUE; } psnt = mm_strip_tag (response, "#PSNT:"); mode = strchr (psnt, ','); if (mode) { switch (atoi (++mode)) { case 0: *result = g_variant_new_uint32 (MM_MODEM_ACCESS_TECHNOLOGY_GPRS); return MM_BASE_MODEM_AT_RESPONSE_PROCESSOR_RESULT_SUCCESS; case 1: *result = g_variant_new_uint32 (MM_MODEM_ACCESS_TECHNOLOGY_EDGE); return MM_BASE_MODEM_AT_RESPONSE_PROCESSOR_RESULT_SUCCESS; case 2: *result = g_variant_new_uint32 (MM_MODEM_ACCESS_TECHNOLOGY_UMTS); return MM_BASE_MODEM_AT_RESPONSE_PROCESSOR_RESULT_SUCCESS; case 3: *result = g_variant_new_uint32 (MM_MODEM_ACCESS_TECHNOLOGY_HSDPA); return MM_BASE_MODEM_AT_RESPONSE_PROCESSOR_RESULT_SUCCESS; case 4: if (mm_iface_modem_is_3gpp_lte (MM_IFACE_MODEM (self))) *result = g_variant_new_uint32 (MM_MODEM_ACCESS_TECHNOLOGY_LTE); else *result = g_variant_new_uint32 (MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN); return MM_BASE_MODEM_AT_RESPONSE_PROCESSOR_RESULT_SUCCESS; case 5: if (mm_iface_modem_is_3gpp_lte (MM_IFACE_MODEM (self))) { *result = g_variant_new_uint32 (MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN); return MM_BASE_MODEM_AT_RESPONSE_PROCESSOR_RESULT_SUCCESS; } /* Fall-through since #PSNT: 5 is not supported in other than lte modems */ default: break; } } g_set_error (result_error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Failed to parse #PSNT response: '%s'", response); return MM_BASE_MODEM_AT_RESPONSE_PROCESSOR_RESULT_FAILURE; } static MMBaseModemAtResponseProcessorResult response_processor_service_ignore_at_errors (MMBaseModem *self, gpointer none, const gchar *command, const gchar *response, gboolean last_command, const GError *error, GVariant **result, GError **result_error) { const gchar *service; *result = NULL; *result_error = NULL; if (error) { /* Ignore AT errors (ie, ERROR or CMx ERROR) */ if (error->domain != MM_MOBILE_EQUIPMENT_ERROR || last_command) { *result_error = g_error_copy (error); return MM_BASE_MODEM_AT_RESPONSE_PROCESSOR_RESULT_FAILURE; } return MM_BASE_MODEM_AT_RESPONSE_PROCESSOR_RESULT_CONTINUE; } service = mm_strip_tag (response, "+SERVICE:"); if (service) { switch (atoi (service)) { case 1: *result = g_variant_new_uint32 (MM_MODEM_ACCESS_TECHNOLOGY_1XRTT); return MM_BASE_MODEM_AT_RESPONSE_PROCESSOR_RESULT_SUCCESS; case 2: *result = g_variant_new_uint32 (MM_MODEM_ACCESS_TECHNOLOGY_EVDO0); return MM_BASE_MODEM_AT_RESPONSE_PROCESSOR_RESULT_SUCCESS; case 3: *result = g_variant_new_uint32 (MM_MODEM_ACCESS_TECHNOLOGY_EVDOA); return MM_BASE_MODEM_AT_RESPONSE_PROCESSOR_RESULT_SUCCESS; default: break; } } g_set_error (result_error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Failed to parse +SERVICE response: '%s'", response); return MM_BASE_MODEM_AT_RESPONSE_PROCESSOR_RESULT_FAILURE; } static const MMBaseModemAtCommand access_tech_commands[] = { { "+COPS?", 3, FALSE, response_processor_cops_ignore_at_errors }, { "#PSNT?", 3, FALSE, response_processor_psnt_ignore_at_errors }, { "+SERVICE?", 3, FALSE, response_processor_service_ignore_at_errors }, { NULL } }; static void load_access_technologies (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data) { mm_base_modem_at_sequence ( MM_BASE_MODEM (self), access_tech_commands, NULL, /* response_processor_context */ NULL, /* response_processor_context_free */ callback, user_data); } /*****************************************************************************/ /* Load supported modes (Modem interface) */ static GArray * load_supported_modes_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { return (GArray *) g_task_propagate_pointer (G_TASK (res), error); } static void parent_load_supported_modes_ready (MMIfaceModem *self, GAsyncResult *res, GTask *task) { GError *error = NULL; GArray *all; GArray *combinations; GArray *filtered; MMSharedTelit *shared = MM_SHARED_TELIT (self); all = iface_modem_parent->load_supported_modes_finish (self, res, &error); if (!all) { g_task_return_error (task, error); g_object_unref (task); return; } /* CDMA-only modems don't support changing modes, default to parent's */ if (!mm_iface_modem_is_3gpp (self)) { g_task_return_pointer (task, all, (GDestroyNotify) g_array_unref); g_object_unref (task); return; } /* Filter out those unsupported modes */ combinations = mm_telit_build_modes_list(); filtered = mm_filter_supported_modes (all, combinations, self); g_array_unref (all); g_array_unref (combinations); mm_shared_telit_store_supported_modes (shared, filtered); g_task_return_pointer (task, filtered, (GDestroyNotify) g_array_unref); g_object_unref (task); } static void load_supported_modes (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data) { /* Run parent's loading */ iface_modem_parent->load_supported_modes ( MM_IFACE_MODEM (self), (GAsyncReadyCallback)parent_load_supported_modes_ready, g_task_new (self, NULL, callback, user_data)); } /*****************************************************************************/ /* Enabling unsolicited events (3GPP interface) */ static gboolean modem_3gpp_enable_unsolicited_events_finish (MMIfaceModem3gpp *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void cind_set_ready (MMBaseModem *self, GAsyncResult *res, GTask *task) { GError *error = NULL; if (!mm_base_modem_at_command_finish (self, res, &error)) { mm_obj_warn (self, "couldn't enable custom +CIND settings: %s", error->message); g_error_free (error); } g_task_return_boolean (task, TRUE); g_object_unref (task); } static void parent_enable_unsolicited_events_ready (MMIfaceModem3gpp *self, GAsyncResult *res, GTask *task) { GError *error = NULL; if (!iface_modem_3gpp_parent->enable_unsolicited_events_finish (self, res, &error)) { mm_obj_warn (self, "couldn't enable parent 3GPP unsolicited events: %s", error->message); g_error_free (error); } /* Our own enable now */ mm_base_modem_at_command_full ( MM_BASE_MODEM (self), mm_base_modem_peek_port_secondary (MM_BASE_MODEM (self)), /* Enable +CIEV only for: signal, service, roam */ "AT+CIND=0,1,1,0,0,0,1,0,0", 5, FALSE, FALSE, NULL, /* cancellable */ (GAsyncReadyCallback)cind_set_ready, task); } static void modem_3gpp_enable_unsolicited_events (MMIfaceModem3gpp *self, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; task = g_task_new (self, NULL, callback, user_data); /* Chain up parent's enable */ iface_modem_3gpp_parent->enable_unsolicited_events ( self, (GAsyncReadyCallback)parent_enable_unsolicited_events_ready, task); } /*****************************************************************************/ MMBroadbandModemTelit * mm_broadband_modem_telit_new (const gchar *device, const gchar *physdev, const gchar **drivers, const gchar *plugin, guint16 vendor_id, guint16 product_id) { return g_object_new (MM_TYPE_BROADBAND_MODEM_TELIT, MM_BASE_MODEM_DEVICE, device, MM_BASE_MODEM_PHYSDEV, physdev, MM_BASE_MODEM_DRIVERS, drivers, MM_BASE_MODEM_PLUGIN, plugin, MM_BASE_MODEM_VENDOR_ID, vendor_id, MM_BASE_MODEM_PRODUCT_ID, product_id, /* Generic bearer supports AT only */ MM_BASE_MODEM_DATA_NET_SUPPORTED, FALSE, MM_BASE_MODEM_DATA_TTY_SUPPORTED, TRUE, MM_IFACE_MODEM_SIM_HOT_SWAP_SUPPORTED, TRUE, NULL); } static void mm_broadband_modem_telit_init (MMBroadbandModemTelit *self) { self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, MM_TYPE_BROADBAND_MODEM_TELIT, MMBroadbandModemTelitPrivate); self->priv->csim_lock_support = FEATURE_SUPPORT_UNKNOWN; self->priv->csim_lock_state = CSIM_LOCK_STATE_UNKNOWN; self->priv->qss_status = QSS_STATUS_UNKNOWN; self->priv->parse_qss = TRUE; } static void iface_modem_init (MMIfaceModem *iface) { iface_modem_parent = g_type_interface_peek_parent (iface); iface->set_current_bands = mm_shared_telit_modem_set_current_bands; iface->set_current_bands_finish = mm_shared_telit_modem_set_current_bands_finish; iface->load_current_bands = mm_shared_telit_modem_load_current_bands; iface->load_current_bands_finish = mm_shared_telit_modem_load_current_bands_finish; iface->load_revision = mm_shared_telit_modem_load_revision; iface->load_revision_finish = mm_shared_telit_modem_load_revision_finish; iface->load_supported_bands = mm_shared_telit_modem_load_supported_bands; iface->load_supported_bands_finish = mm_shared_telit_modem_load_supported_bands_finish; iface->load_unlock_retries_finish = modem_load_unlock_retries_finish; iface->load_unlock_retries = modem_load_unlock_retries; iface->reset = modem_reset; iface->reset_finish = modem_reset_finish; iface->modem_after_power_up = modem_after_power_up; iface->modem_after_power_up_finish = modem_after_power_up_finish; iface->modem_power_down = modem_power_down; iface->modem_power_down_finish = modem_power_down_finish; iface->load_access_technologies = load_access_technologies; iface->load_access_technologies_finish = load_access_technologies_finish; iface->load_supported_modes = load_supported_modes; iface->load_supported_modes_finish = load_supported_modes_finish; iface->load_current_modes = mm_shared_telit_load_current_modes; iface->load_current_modes_finish = mm_shared_telit_load_current_modes_finish; iface->set_current_modes = mm_shared_telit_set_current_modes; iface->set_current_modes_finish = mm_shared_telit_set_current_modes_finish; iface->setup_sim_hot_swap = modem_setup_sim_hot_swap; iface->setup_sim_hot_swap_finish = modem_setup_sim_hot_swap_finish; iface->cleanup_sim_hot_swap = modem_cleanup_sim_hot_swap; } static void iface_modem_3gpp_init (MMIfaceModem3gpp *iface) { iface_modem_3gpp_parent = g_type_interface_peek_parent (iface); iface->enable_unsolicited_events = modem_3gpp_enable_unsolicited_events; iface->enable_unsolicited_events_finish = modem_3gpp_enable_unsolicited_events_finish; } static void shared_telit_init (MMSharedTelit *iface) { } static void iface_modem_location_init (MMIfaceModemLocation *iface) { iface_modem_location_parent = g_type_interface_peek_parent (iface); iface->load_capabilities = location_load_capabilities; iface->load_capabilities_finish = location_load_capabilities_finish; iface->enable_location_gathering = enable_location_gathering; iface->enable_location_gathering_finish = enable_location_gathering_finish; iface->disable_location_gathering = disable_location_gathering; iface->disable_location_gathering_finish = disable_location_gathering_finish; } static void mm_broadband_modem_telit_class_init (MMBroadbandModemTelitClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); MMBroadbandModemClass *broadband_modem_class = MM_BROADBAND_MODEM_CLASS (klass); g_type_class_add_private (object_class, sizeof (MMBroadbandModemTelitPrivate)); broadband_modem_class->setup_ports = setup_ports; } ModemManager-1.23.4-dev/src/plugins/telit/mm-broadband-modem-telit.h000066400000000000000000000047241456466623000252370ustar00rootroot00000000000000/* -*- 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 - 2013 Red Hat, Inc. * Copyright (C) 2012 Aleksander Morgado */ #ifndef MM_BROADBAND_MODEM_TELIT_H #define MM_BROADBAND_MODEM_TELIT_H #include "mm-broadband-modem.h" #define MM_TYPE_BROADBAND_MODEM_TELIT (mm_broadband_modem_telit_get_type ()) #define MM_BROADBAND_MODEM_TELIT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_BROADBAND_MODEM_TELIT, MMBroadbandModemTelit)) #define MM_BROADBAND_MODEM_TELIT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_BROADBAND_MODEM_TELIT, MMBroadbandModemTelitClass)) #define MM_IS_BROADBAND_MODEM_TELIT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_BROADBAND_MODEM_TELIT)) #define MM_IS_BROADBAND_MODEM_TELIT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_BROADBAND_MODEM_TELIT)) #define MM_BROADBAND_MODEM_TELIT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_BROADBAND_MODEM_TELIT, MMBroadbandModemTelitClass)) typedef struct _MMBroadbandModemTelit MMBroadbandModemTelit; typedef struct _MMBroadbandModemTelitClass MMBroadbandModemTelitClass; typedef struct _MMBroadbandModemTelitPrivate MMBroadbandModemTelitPrivate; struct _MMBroadbandModemTelit { MMBroadbandModem parent; MMBroadbandModemTelitPrivate *priv; }; struct _MMBroadbandModemTelitClass{ MMBroadbandModemClass parent; }; GType mm_broadband_modem_telit_get_type (void); MMBroadbandModemTelit *mm_broadband_modem_telit_new (const gchar *device, const gchar *physdev, const gchar **drivers, const gchar *plugin, guint16 vendor_id, guint16 product_id); #endif /* MM_BROADBAND_MODEM_TELIT_H */ ModemManager-1.23.4-dev/src/plugins/telit/mm-common-telit.c000066400000000000000000000303061456466623000235020ustar00rootroot00000000000000/* -*- 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) 2015 Aleksander Morgado */ #include #include "mm-common-telit.h" #include "mm-log-object.h" #include "mm-serial-parsers.h" /*****************************************************************************/ #define TAG_GETPORTCFG_SUPPORTED "getportcfg-supported" #define TAG_TELIT_MODEM_PORT "ID_MM_TELIT_PORT_TYPE_MODEM" #define TAG_TELIT_AUX_PORT "ID_MM_TELIT_PORT_TYPE_AUX" #define TAG_TELIT_NMEA_PORT "ID_MM_TELIT_PORT_TYPE_NMEA" #define TELIT_GE910_FAMILY_PID 0x0022 /* The following number of retries of the port responsiveness * check allows having up to 40 seconds of wait, that should * be fine for most of the modems */ #define TELIT_PORT_CHECK_RETRIES 8 gboolean telit_grab_port (MMPlugin *self, MMBaseModem *modem, MMPortProbe *probe, GError **error) { MMKernelDevice *port; MMDevice *device; MMPortType ptype; MMPortSerialAtFlag pflags = MM_PORT_SERIAL_AT_FLAG_NONE; const gchar *subsys; port = mm_port_probe_peek_port (probe); ptype = mm_port_probe_get_port_type (probe); device = mm_port_probe_peek_device (probe); subsys = mm_port_probe_get_port_subsys (probe); /* Just skip custom port identification for subsys different than tty */ if (!g_str_equal (subsys, "tty")) goto out; /* AT#PORTCFG (if supported) can be used for identifying the port layout */ if (g_object_get_data (G_OBJECT (device), TAG_GETPORTCFG_SUPPORTED) != NULL) { guint usbif; usbif = (guint) mm_kernel_device_get_interface_number (port); if (usbif == GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (device), TAG_TELIT_MODEM_PORT))) { mm_obj_dbg (self, "AT port '%s/%s' flagged as primary", mm_port_probe_get_port_subsys (probe), mm_port_probe_get_port_name (probe)); pflags = MM_PORT_SERIAL_AT_FLAG_PRIMARY; } else if (usbif == GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (device), TAG_TELIT_AUX_PORT))) { mm_obj_dbg (self, "AT port '%s/%s' flagged as secondary", mm_port_probe_get_port_subsys (probe), mm_port_probe_get_port_name (probe)); pflags = MM_PORT_SERIAL_AT_FLAG_SECONDARY; } else if (usbif == GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (device), TAG_TELIT_NMEA_PORT))) { mm_obj_dbg (self, "port '%s/%s' flagged as NMEA", mm_port_probe_get_port_subsys (probe), mm_port_probe_get_port_name (probe)); ptype = MM_PORT_TYPE_GPS; } else ptype = MM_PORT_TYPE_IGNORED; } out: return mm_base_modem_grab_port (modem, port, ptype, pflags, error); } /*****************************************************************************/ /* Custom init */ typedef struct { MMPortSerialAt *port; gboolean getportcfg_done; guint getportcfg_retries; guint port_responsive_retries; } TelitCustomInitContext; gboolean telit_custom_init_finish (MMPortProbe *probe, GAsyncResult *result, GError **error) { return g_task_propagate_boolean (G_TASK (result), error); } static void telit_custom_init_step (GTask *task); static gboolean cache_port_mode (MMPortProbe *probe, MMDevice *device, const gchar *reply) { g_autoptr(GRegex) r = NULL; g_autoptr(GMatchInfo) match_info = NULL; GRegexCompileFlags flags = G_REGEX_DOLLAR_ENDONLY | G_REGEX_RAW; GError *error = NULL; gboolean ret = FALSE; guint portcfg_current; /* #PORTCFG: , */ r = g_regex_new ("#PORTCFG:\\s*(\\d+),(\\d+)", flags, 0, NULL); g_assert (r != NULL); if (!g_regex_match_full (r, reply, strlen (reply), 0, 0, &match_info, &error)) goto out; if (!mm_get_uint_from_match_info (match_info, 2, &portcfg_current)) { mm_obj_dbg (probe, "unrecognized #PORTCFG value"); goto out; } /* Reference for port configurations: * HE910/UE910/UL865 Families Ports Arrangements User Guide * GE910 Family Ports Arrangements User Guide */ switch (portcfg_current) { case 0: case 1: case 4: case 5: case 7: case 9: case 10: case 11: g_object_set_data (G_OBJECT (device), TAG_TELIT_MODEM_PORT, GUINT_TO_POINTER (0x00)); if (mm_device_get_product (device) == TELIT_GE910_FAMILY_PID) g_object_set_data (G_OBJECT (device), TAG_TELIT_AUX_PORT, GUINT_TO_POINTER (0x02)); else g_object_set_data (G_OBJECT (device), TAG_TELIT_AUX_PORT, GUINT_TO_POINTER (0x06)); break; case 2: case 3: case 6: g_object_set_data (G_OBJECT (device), TAG_TELIT_MODEM_PORT, GUINT_TO_POINTER (0x00)); break; case 8: case 12: g_object_set_data (G_OBJECT (device), TAG_TELIT_MODEM_PORT, GUINT_TO_POINTER (0x00)); if (mm_device_get_product (device) == TELIT_GE910_FAMILY_PID) { g_object_set_data (G_OBJECT (device), TAG_TELIT_AUX_PORT, GUINT_TO_POINTER (0x02)); g_object_set_data (G_OBJECT (device), TAG_TELIT_NMEA_PORT, GUINT_TO_POINTER (0x04)); } else { g_object_set_data (G_OBJECT (device), TAG_TELIT_AUX_PORT, GUINT_TO_POINTER (0x06)); g_object_set_data (G_OBJECT (device), TAG_TELIT_NMEA_PORT, GUINT_TO_POINTER (0x0a)); } break; default: /* portcfg value not supported */ goto out; } ret = TRUE; out: if (error) { mm_obj_dbg (probe, "error while matching #PORTCFG: %s", error->message); g_error_free (error); } return ret; } static void getportcfg_ready (MMPortSerialAt *port, GAsyncResult *res, GTask *task) { g_autofree gchar *response = NULL; GError *error = NULL; MMPortProbe *probe; TelitCustomInitContext *ctx; ctx = g_task_get_task_data (task); probe = g_task_get_source_object (task); response = mm_port_serial_at_command_finish (port, res, &error); if (error) { mm_obj_dbg (probe, "couldn't get telit port mode: '%s'", error->message); /* If ERROR or COMMAND NOT SUPPORT occur then do not retry the * command. */ if (g_error_matches (error, MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_UNKNOWN)) ctx->getportcfg_done = TRUE; } else { MMDevice *device; device = mm_port_probe_peek_device (probe); /* Results are cached in the parent device object */ if (g_object_get_data (G_OBJECT (device), TAG_GETPORTCFG_SUPPORTED) == NULL) { mm_obj_dbg (probe, "retrieving telit port mode layout"); if (cache_port_mode (probe, device, response)) { g_object_set_data (G_OBJECT (device), TAG_GETPORTCFG_SUPPORTED, GUINT_TO_POINTER (TRUE)); ctx->getportcfg_done = TRUE; } } /* Port answered to #PORTCFG, so mark it as being AT already */ mm_port_probe_set_result_at (probe, TRUE); } if (error) g_error_free (error); telit_custom_init_step (task); } static void telit_custom_init_context_free (TelitCustomInitContext *ctx) { g_object_unref (ctx->port); g_slice_free (TelitCustomInitContext, ctx); } static void telit_custom_init_step (GTask *task) { MMKernelDevice *port; MMPortProbe *probe; TelitCustomInitContext *ctx; ctx = g_task_get_task_data (task); probe = g_task_get_source_object (task); /* If cancelled, end */ if (g_cancellable_is_cancelled (g_task_get_cancellable (task))) { mm_obj_dbg (probe, "no need to keep on running custom init"); goto out; } /* Try to get a port configuration from the modem: usb interface 00 * is always linked to an AT port */ port = mm_port_probe_peek_port (probe); if (!ctx->getportcfg_done && mm_kernel_device_get_interface_number (port) == 0) { if (ctx->getportcfg_retries == 0) goto out; ctx->getportcfg_retries--; mm_port_serial_at_command ( ctx->port, "AT#PORTCFG?", 2, FALSE, /* raw */ FALSE, /* allow_cached */ g_task_get_cancellable (task), (GAsyncReadyCallback)getportcfg_ready, task); return; } out: g_task_return_boolean (task, TRUE); g_object_unref (task); } static void at_ready (MMPortSerialAt *port, GAsyncResult *res, GTask *task); static void wait_for_ready (GTask *task) { TelitCustomInitContext *ctx; ctx = g_task_get_task_data (task); if (ctx->port_responsive_retries == 0) { telit_custom_init_step (task); return; } ctx->port_responsive_retries--; mm_port_serial_at_command ( ctx->port, "AT", 5, FALSE, /* raw */ FALSE, /* allow_cached */ g_task_get_cancellable (task), (GAsyncReadyCallback)at_ready, task); } static void at_ready (MMPortSerialAt *port, GAsyncResult *res, GTask *task) { MMPortProbe *probe; g_autoptr(GError) error = NULL; probe = g_task_get_source_object (task); mm_port_serial_at_command_finish (port, res, &error); if (error) { /* On a timeout or send error, wait */ if (g_error_matches (error, MM_SERIAL_ERROR, MM_SERIAL_ERROR_RESPONSE_TIMEOUT) || g_error_matches (error, MM_SERIAL_ERROR, MM_SERIAL_ERROR_SEND_FAILED)) { wait_for_ready (task); return; } /* On an unknown error, make it fatal */ if (!mm_serial_parser_v1_is_known_error (error)) { mm_obj_warn (probe, "custom port initialization logic failed: %s", error->message); g_task_return_boolean (task, TRUE); g_object_unref (task); return; } } /* When successful mark the port as AT and continue checking #PORTCFG */ mm_obj_dbg (probe, "port is AT"); mm_port_probe_set_result_at (probe, TRUE); telit_custom_init_step (task); } void telit_custom_init (MMPortProbe *probe, MMPortSerialAt *port, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { TelitCustomInitContext *ctx; GTask *task; gboolean wait_needed; ctx = g_slice_new (TelitCustomInitContext); ctx->port = g_object_ref (port); ctx->getportcfg_done = FALSE; ctx->getportcfg_retries = 3; ctx->port_responsive_retries = TELIT_PORT_CHECK_RETRIES; task = g_task_new (probe, cancellable, callback, user_data); g_task_set_check_cancellable (task, FALSE); g_task_set_task_data (task, ctx, (GDestroyNotify)telit_custom_init_context_free); /* Some Telit modems require an initial delay for the ports to be responsive * If no explicit tag is present, the modem does not need this step * and can directly look for #PORTCFG support */ wait_needed = mm_kernel_device_get_global_property_as_boolean (mm_port_probe_peek_port (probe), "ID_MM_TELIT_PORT_DELAY"); if (wait_needed) { mm_obj_dbg (probe, "Start polling for port responsiveness"); wait_for_ready (task); return; } telit_custom_init_step (task); } ModemManager-1.23.4-dev/src/plugins/telit/mm-common-telit.h000066400000000000000000000024301456466623000235040ustar00rootroot00000000000000/* -*- 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) 2015 Aleksander Morgado */ #ifndef MM_COMMON_TELIT_H #define MM_COMMON_TELIT_H #include "glib.h" #include "mm-plugin.h" gboolean telit_custom_init_finish (MMPortProbe *probe, GAsyncResult *result, GError **error); void telit_custom_init (MMPortProbe *probe, MMPortSerialAt *port, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); gboolean telit_grab_port (MMPlugin *self, MMBaseModem *modem, MMPortProbe *probe, GError **error); #endif /* MM_COMMON_TELIT_H */ ModemManager-1.23.4-dev/src/plugins/telit/mm-modem-helpers-telit.c000066400000000000000000001174351456466623000247640ustar00rootroot00000000000000/* -*- 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) 2015-2019 Telit. * Copyright (C) 2019 Aleksander Morgado */ #include #include #include #include #include #define _LIBMM_INSIDE_MMCLI #include #include "mm-log-object.h" #include "mm-modem-helpers.h" #include "mm-modem-helpers-telit.h" /*****************************************************************************/ /* AT#BND 2G values */ #define MM_MODEM_BAND_TELIT_2G_FIRST MM_MODEM_BAND_EGSM #define MM_MODEM_BAND_TELIT_2G_LAST MM_MODEM_BAND_G850 #define B2G_FLAG(band) (1 << (band - MM_MODEM_BAND_TELIT_2G_FIRST)) /* Index of the array is the telit 2G band value [0-5] * The bitmask value here is built from the 2G MMModemBand values right away. */ static const guint32 telit_2g_to_mm_band_mask[] = { [0] = B2G_FLAG (MM_MODEM_BAND_EGSM) + B2G_FLAG (MM_MODEM_BAND_DCS), [1] = B2G_FLAG (MM_MODEM_BAND_EGSM) + B2G_FLAG (MM_MODEM_BAND_PCS), [2] = B2G_FLAG (MM_MODEM_BAND_DCS) + B2G_FLAG (MM_MODEM_BAND_G850), [3] = B2G_FLAG (MM_MODEM_BAND_PCS) + B2G_FLAG (MM_MODEM_BAND_G850), [4] = B2G_FLAG (MM_MODEM_BAND_EGSM) + B2G_FLAG (MM_MODEM_BAND_DCS) + B2G_FLAG (MM_MODEM_BAND_PCS), [5] = B2G_FLAG (MM_MODEM_BAND_EGSM) + B2G_FLAG (MM_MODEM_BAND_DCS) + B2G_FLAG (MM_MODEM_BAND_PCS) + B2G_FLAG (MM_MODEM_BAND_G850), }; /*****************************************************************************/ /* AT#BND 3G values */ /* NOTE: UTRAN_1 to UTRAN_9 enum values are NOT IN ORDER! * E.g. numerically UTRAN_7 is after UTRAN_9... * * This array allows us to iterate them in a way which is a bit * more friendly. */ static const guint band_utran_index[] = { [MM_MODEM_BAND_UTRAN_1] = 1, [MM_MODEM_BAND_UTRAN_2] = 2, [MM_MODEM_BAND_UTRAN_3] = 3, [MM_MODEM_BAND_UTRAN_4] = 4, [MM_MODEM_BAND_UTRAN_5] = 5, [MM_MODEM_BAND_UTRAN_6] = 6, [MM_MODEM_BAND_UTRAN_7] = 7, [MM_MODEM_BAND_UTRAN_8] = 8, [MM_MODEM_BAND_UTRAN_9] = 9, [MM_MODEM_BAND_UTRAN_10] = 10, [MM_MODEM_BAND_UTRAN_11] = 11, [MM_MODEM_BAND_UTRAN_12] = 12, [MM_MODEM_BAND_UTRAN_13] = 13, [MM_MODEM_BAND_UTRAN_14] = 14, [MM_MODEM_BAND_UTRAN_19] = 19, [MM_MODEM_BAND_UTRAN_20] = 20, [MM_MODEM_BAND_UTRAN_21] = 21, [MM_MODEM_BAND_UTRAN_22] = 22, [MM_MODEM_BAND_UTRAN_25] = 25, [MM_MODEM_BAND_UTRAN_26] = 26, [MM_MODEM_BAND_UTRAN_32] = 32, }; #define MM_MODEM_BAND_TELIT_3G_FIRST MM_MODEM_BAND_UTRAN_1 #define MM_MODEM_BAND_TELIT_3G_LAST MM_MODEM_BAND_UTRAN_19 #define B3G_NUM(band) band_utran_index[band] #define B3G_FLAG(band) ((B3G_NUM (band) > 0) ? (1LL << (B3G_NUM (band) - B3G_NUM (MM_MODEM_BAND_TELIT_3G_FIRST))) : 0) /* Index of the arrays is the telit 3G band value. * The bitmask value here is built from the 3G MMModemBand values right away. * * We have 2 different sets of bands that are different for values >=12, because * the LM940/960 models support a different set of 3G bands. */ #define TELIT_3G_TO_MM_BAND_MASK_DEFAULT_N_ELEMENTS 27 static guint64 telit_3g_to_mm_band_mask_default[TELIT_3G_TO_MM_BAND_MASK_DEFAULT_N_ELEMENTS]; #define TELIT_3G_TO_MM_BAND_MASK_ALTERNATE_N_ELEMENTS 20 static guint64 telit_3g_to_mm_band_mask_alternate[TELIT_3G_TO_MM_BAND_MASK_ALTERNATE_N_ELEMENTS]; static void initialize_telit_3g_to_mm_band_masks (void) { static gboolean initialized = FALSE; /* We need to initialize the arrays in runtime because gcc < 8 doesn't like initializing * with operations that are using the band_utran_index constant array elements */ if (initialized) return; telit_3g_to_mm_band_mask_default[0] = B3G_FLAG (MM_MODEM_BAND_UTRAN_1); telit_3g_to_mm_band_mask_default[1] = B3G_FLAG (MM_MODEM_BAND_UTRAN_2); telit_3g_to_mm_band_mask_default[2] = B3G_FLAG (MM_MODEM_BAND_UTRAN_5); telit_3g_to_mm_band_mask_default[3] = B3G_FLAG (MM_MODEM_BAND_UTRAN_1) + B3G_FLAG (MM_MODEM_BAND_UTRAN_2) + B3G_FLAG (MM_MODEM_BAND_UTRAN_5); telit_3g_to_mm_band_mask_default[4] = B3G_FLAG (MM_MODEM_BAND_UTRAN_2) + B3G_FLAG (MM_MODEM_BAND_UTRAN_5); telit_3g_to_mm_band_mask_default[5] = B3G_FLAG (MM_MODEM_BAND_UTRAN_8); telit_3g_to_mm_band_mask_default[6] = B3G_FLAG (MM_MODEM_BAND_UTRAN_1) + B3G_FLAG (MM_MODEM_BAND_UTRAN_8); telit_3g_to_mm_band_mask_default[7] = B3G_FLAG (MM_MODEM_BAND_UTRAN_4); telit_3g_to_mm_band_mask_default[8] = B3G_FLAG (MM_MODEM_BAND_UTRAN_1) + B3G_FLAG (MM_MODEM_BAND_UTRAN_5); telit_3g_to_mm_band_mask_default[9] = B3G_FLAG (MM_MODEM_BAND_UTRAN_1) + B3G_FLAG (MM_MODEM_BAND_UTRAN_8) + B3G_FLAG (MM_MODEM_BAND_UTRAN_5); telit_3g_to_mm_band_mask_default[10] = B3G_FLAG (MM_MODEM_BAND_UTRAN_2) + B3G_FLAG (MM_MODEM_BAND_UTRAN_4) + B3G_FLAG (MM_MODEM_BAND_UTRAN_5); telit_3g_to_mm_band_mask_default[11] = B3G_FLAG (MM_MODEM_BAND_UTRAN_1) + B3G_FLAG (MM_MODEM_BAND_UTRAN_2) + B3G_FLAG (MM_MODEM_BAND_UTRAN_4) + B3G_FLAG (MM_MODEM_BAND_UTRAN_5) + B3G_FLAG (MM_MODEM_BAND_UTRAN_8); telit_3g_to_mm_band_mask_default[12] = B3G_FLAG (MM_MODEM_BAND_UTRAN_6); telit_3g_to_mm_band_mask_default[13] = B3G_FLAG (MM_MODEM_BAND_UTRAN_3); telit_3g_to_mm_band_mask_default[14] = B3G_FLAG (MM_MODEM_BAND_UTRAN_1) + B3G_FLAG (MM_MODEM_BAND_UTRAN_2) + B3G_FLAG (MM_MODEM_BAND_UTRAN_4) + B3G_FLAG (MM_MODEM_BAND_UTRAN_5) + B3G_FLAG (MM_MODEM_BAND_UTRAN_6); telit_3g_to_mm_band_mask_default[15] = B3G_FLAG (MM_MODEM_BAND_UTRAN_1) + B3G_FLAG (MM_MODEM_BAND_UTRAN_3) + B3G_FLAG (MM_MODEM_BAND_UTRAN_8); telit_3g_to_mm_band_mask_default[16] = B3G_FLAG (MM_MODEM_BAND_UTRAN_5) + B3G_FLAG (MM_MODEM_BAND_UTRAN_8); telit_3g_to_mm_band_mask_default[17] = B3G_FLAG (MM_MODEM_BAND_UTRAN_2) + B3G_FLAG (MM_MODEM_BAND_UTRAN_4) + B3G_FLAG (MM_MODEM_BAND_UTRAN_5) + B3G_FLAG (MM_MODEM_BAND_UTRAN_6); telit_3g_to_mm_band_mask_default[18] = B3G_FLAG (MM_MODEM_BAND_UTRAN_1) + B3G_FLAG (MM_MODEM_BAND_UTRAN_5) + B3G_FLAG (MM_MODEM_BAND_UTRAN_6) + B3G_FLAG (MM_MODEM_BAND_UTRAN_8); telit_3g_to_mm_band_mask_default[19] = B3G_FLAG (MM_MODEM_BAND_UTRAN_2) + B3G_FLAG (MM_MODEM_BAND_UTRAN_6); telit_3g_to_mm_band_mask_default[20] = B3G_FLAG (MM_MODEM_BAND_UTRAN_5) + B3G_FLAG (MM_MODEM_BAND_UTRAN_6); telit_3g_to_mm_band_mask_default[21] = B3G_FLAG (MM_MODEM_BAND_UTRAN_2) + B3G_FLAG (MM_MODEM_BAND_UTRAN_5) + B3G_FLAG (MM_MODEM_BAND_UTRAN_6); telit_3g_to_mm_band_mask_default[22] = B3G_FLAG (MM_MODEM_BAND_UTRAN_1) + B3G_FLAG (MM_MODEM_BAND_UTRAN_3) + B3G_FLAG (MM_MODEM_BAND_UTRAN_5) + B3G_FLAG (MM_MODEM_BAND_UTRAN_8); telit_3g_to_mm_band_mask_default[23] = B3G_FLAG (MM_MODEM_BAND_UTRAN_1) + B3G_FLAG (MM_MODEM_BAND_UTRAN_3); telit_3g_to_mm_band_mask_default[24] = B3G_FLAG (MM_MODEM_BAND_UTRAN_1) + B3G_FLAG (MM_MODEM_BAND_UTRAN_2) + B3G_FLAG (MM_MODEM_BAND_UTRAN_4) + B3G_FLAG (MM_MODEM_BAND_UTRAN_5); telit_3g_to_mm_band_mask_default[25] = B3G_FLAG (MM_MODEM_BAND_UTRAN_19); telit_3g_to_mm_band_mask_default[26] = B3G_FLAG (MM_MODEM_BAND_UTRAN_1) + B3G_FLAG (MM_MODEM_BAND_UTRAN_5) + B3G_FLAG (MM_MODEM_BAND_UTRAN_6) + B3G_FLAG (MM_MODEM_BAND_UTRAN_8) + B3G_FLAG (MM_MODEM_BAND_UTRAN_19); /* Initialize alternate 3G band set */ telit_3g_to_mm_band_mask_alternate[0] = B3G_FLAG (MM_MODEM_BAND_UTRAN_1); telit_3g_to_mm_band_mask_alternate[1] = B3G_FLAG (MM_MODEM_BAND_UTRAN_2); telit_3g_to_mm_band_mask_alternate[2] = B3G_FLAG (MM_MODEM_BAND_UTRAN_5); telit_3g_to_mm_band_mask_alternate[3] = B3G_FLAG (MM_MODEM_BAND_UTRAN_1) + B3G_FLAG (MM_MODEM_BAND_UTRAN_2) + B3G_FLAG (MM_MODEM_BAND_UTRAN_5); telit_3g_to_mm_band_mask_alternate[4] = B3G_FLAG (MM_MODEM_BAND_UTRAN_2) + B3G_FLAG (MM_MODEM_BAND_UTRAN_5); telit_3g_to_mm_band_mask_alternate[5] = B3G_FLAG (MM_MODEM_BAND_UTRAN_8); telit_3g_to_mm_band_mask_alternate[6] = B3G_FLAG (MM_MODEM_BAND_UTRAN_1) + B3G_FLAG (MM_MODEM_BAND_UTRAN_8); telit_3g_to_mm_band_mask_alternate[7] = B3G_FLAG (MM_MODEM_BAND_UTRAN_4); telit_3g_to_mm_band_mask_alternate[8] = B3G_FLAG (MM_MODEM_BAND_UTRAN_1) + B3G_FLAG (MM_MODEM_BAND_UTRAN_5); telit_3g_to_mm_band_mask_alternate[9] = B3G_FLAG (MM_MODEM_BAND_UTRAN_1) + B3G_FLAG (MM_MODEM_BAND_UTRAN_8) + B3G_FLAG (MM_MODEM_BAND_UTRAN_5); telit_3g_to_mm_band_mask_alternate[10] = B3G_FLAG (MM_MODEM_BAND_UTRAN_2) + B3G_FLAG (MM_MODEM_BAND_UTRAN_4) + B3G_FLAG (MM_MODEM_BAND_UTRAN_5); telit_3g_to_mm_band_mask_alternate[11] = B3G_FLAG (MM_MODEM_BAND_UTRAN_1) + B3G_FLAG (MM_MODEM_BAND_UTRAN_2) + B3G_FLAG (MM_MODEM_BAND_UTRAN_4) + B3G_FLAG (MM_MODEM_BAND_UTRAN_5) + B3G_FLAG (MM_MODEM_BAND_UTRAN_8); telit_3g_to_mm_band_mask_alternate[12] = B3G_FLAG (MM_MODEM_BAND_UTRAN_1) + B3G_FLAG (MM_MODEM_BAND_UTRAN_3) + B3G_FLAG (MM_MODEM_BAND_UTRAN_5) + B3G_FLAG (MM_MODEM_BAND_UTRAN_8); telit_3g_to_mm_band_mask_alternate[13] = B3G_FLAG (MM_MODEM_BAND_UTRAN_3); telit_3g_to_mm_band_mask_alternate[14] = B3G_FLAG (MM_MODEM_BAND_UTRAN_1) + B3G_FLAG (MM_MODEM_BAND_UTRAN_3) + B3G_FLAG (MM_MODEM_BAND_UTRAN_5); telit_3g_to_mm_band_mask_alternate[15] = B3G_FLAG (MM_MODEM_BAND_UTRAN_3) + B3G_FLAG (MM_MODEM_BAND_UTRAN_5); telit_3g_to_mm_band_mask_alternate[16] = B3G_FLAG (MM_MODEM_BAND_UTRAN_1) + B3G_FLAG (MM_MODEM_BAND_UTRAN_2) + B3G_FLAG (MM_MODEM_BAND_UTRAN_3) + B3G_FLAG (MM_MODEM_BAND_UTRAN_4) + B3G_FLAG (MM_MODEM_BAND_UTRAN_5) + B3G_FLAG (MM_MODEM_BAND_UTRAN_8); telit_3g_to_mm_band_mask_alternate[17] = B3G_FLAG (MM_MODEM_BAND_UTRAN_1) + B3G_FLAG (MM_MODEM_BAND_UTRAN_2) + B3G_FLAG (MM_MODEM_BAND_UTRAN_8); telit_3g_to_mm_band_mask_alternate[18] = B3G_FLAG (MM_MODEM_BAND_UTRAN_1) + B3G_FLAG (MM_MODEM_BAND_UTRAN_2) + B3G_FLAG (MM_MODEM_BAND_UTRAN_4) + B3G_FLAG (MM_MODEM_BAND_UTRAN_5) + B3G_FLAG (MM_MODEM_BAND_UTRAN_8) + B3G_FLAG (MM_MODEM_BAND_UTRAN_9) + B3G_FLAG (MM_MODEM_BAND_UTRAN_19); telit_3g_to_mm_band_mask_alternate[19] = B3G_FLAG (MM_MODEM_BAND_UTRAN_1) + B3G_FLAG (MM_MODEM_BAND_UTRAN_2) + B3G_FLAG (MM_MODEM_BAND_UTRAN_4) + B3G_FLAG (MM_MODEM_BAND_UTRAN_5) + B3G_FLAG (MM_MODEM_BAND_UTRAN_6) + B3G_FLAG (MM_MODEM_BAND_UTRAN_8) + B3G_FLAG (MM_MODEM_BAND_UTRAN_9) + B3G_FLAG (MM_MODEM_BAND_UTRAN_19); } /*****************************************************************************/ /* AT#BND 4G values * * The Telit-specific value for 4G bands is a bitmask of the band values, given * in hexadecimal or decimal format. */ #define MM_MODEM_BAND_TELIT_4G_FIRST MM_MODEM_BAND_EUTRAN_1 #define MM_MODEM_BAND_TELIT_4G_LAST MM_MODEM_BAND_EUTRAN_64 #define B4G_FLAG(band) (((guint64) 1) << (band - MM_MODEM_BAND_TELIT_4G_FIRST)) #define MM_MODEM_BAND_TELIT_EXT_4G_FIRST MM_MODEM_BAND_EUTRAN_65 #define MM_MODEM_BAND_TELIT_EXT_4G_LAST MM_MODEM_BAND_EUTRAN_85 #define B4G_FLAG_EXT(band) (((guint64) 1) << (band - MM_MODEM_BAND_TELIT_EXT_4G_FIRST)) /*****************************************************************************/ /* Set current bands helpers */ gchar * mm_telit_build_bnd_request (GArray *bands_array, MMTelitBNDParseConfig *config, GError **error) { guint32 mask2g = 0; guint64 mask3g = 0; guint64 mask4g = 0; guint64 mask4gext = 0; guint i; gint flag2g = -1; gint64 flag3g = -1; gint64 flag4g = -1; gchar *cmd; gboolean modem_is_2g = config->modem_is_2g; gboolean modem_is_3g = config->modem_is_3g; gboolean modem_is_4g = config->modem_is_4g; for (i = 0; i < bands_array->len; i++) { MMModemBand band; band = g_array_index (bands_array, MMModemBand, i); /* Convert 2G bands into a bitmask, to match against telit_2g_to_mm_band_mask. */ if (modem_is_2g && mm_common_band_is_gsm (band) && (band >= MM_MODEM_BAND_TELIT_2G_FIRST) && (band <= MM_MODEM_BAND_TELIT_2G_LAST)) mask2g += B2G_FLAG (band); /* Convert 3G bands into a bitmask, to match against telit_3g_to_mm_band_mask. We use * a 64-bit explicit bitmask so that all values fit correctly. */ if (modem_is_3g && mm_common_band_is_utran (band) && (B3G_NUM (band) >= B3G_NUM (MM_MODEM_BAND_TELIT_3G_FIRST)) && (B3G_NUM (band) <= B3G_NUM (MM_MODEM_BAND_TELIT_3G_LAST))) mask3g += B3G_FLAG (band); /* Convert 4G bands into a bitmask. We use a 64bit explicit bitmask so that * all values fit correctly. */ if (modem_is_4g && mm_common_band_is_eutran (band)) { if (band >= MM_MODEM_BAND_TELIT_4G_FIRST && band <= MM_MODEM_BAND_TELIT_4G_LAST) mask4g += B4G_FLAG (band); else if (band >= MM_MODEM_BAND_TELIT_EXT_4G_FIRST && band <= MM_MODEM_BAND_TELIT_EXT_4G_LAST) mask4gext += B4G_FLAG_EXT (band); } } /* Get 2G-specific telit value */ if (mask2g) { for (i = 0; i < G_N_ELEMENTS (telit_2g_to_mm_band_mask); i++) { if (mask2g == telit_2g_to_mm_band_mask[i]) { flag2g = i; break; } } if (flag2g == -1) { gchar *bands_str; bands_str = mm_common_build_bands_string ((const MMModemBand *)(gconstpointer)(bands_array->data), bands_array->len); g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't find matching 2G bands Telit value for band combination: '%s'", bands_str); g_free (bands_str); return NULL; } } /* Get 3G-specific telit value */ if (mask3g) { const guint64 *telit_3g_to_mm_band_mask; guint telit_3g_to_mm_band_mask_n_elements; initialize_telit_3g_to_mm_band_masks (); /* Select correct 3G band mask */ if (config->modem_alternate_3g_bands) { telit_3g_to_mm_band_mask = telit_3g_to_mm_band_mask_alternate; telit_3g_to_mm_band_mask_n_elements = G_N_ELEMENTS (telit_3g_to_mm_band_mask_alternate); } else { telit_3g_to_mm_band_mask = telit_3g_to_mm_band_mask_default; telit_3g_to_mm_band_mask_n_elements = G_N_ELEMENTS (telit_3g_to_mm_band_mask_default); } for (i = 0; i < telit_3g_to_mm_band_mask_n_elements; i++) { if (mask3g == telit_3g_to_mm_band_mask[i]) { flag3g = i; break; } } if (flag3g == -1) { gchar *bands_str; bands_str = mm_common_build_bands_string ((const MMModemBand *)(gconstpointer)(bands_array->data), bands_array->len); g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't find matching 3G bands Telit value for band combination: '%s'", bands_str); g_free (bands_str); return NULL; } } /* Get 4G-specific telit band bitmask */ flag4g = (mask4g != 0) ? ((gint64)mask4g) : -1; /* If the modem supports a given access tech, we must always give band settings * for the specific tech */ if (modem_is_2g && flag2g == -1) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_NOT_FOUND, "None or invalid 2G bands combination in the provided list"); return NULL; } if (modem_is_3g && flag3g == -1) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_NOT_FOUND, "None or invalid 3G bands combination in the provided list"); return NULL; } if (modem_is_4g && mask4g == 0 && mask4gext == 0) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_NOT_FOUND, "None or invalid 4G bands combination in the provided list"); return NULL; } if (modem_is_2g && !modem_is_3g && !modem_is_4g) cmd = g_strdup_printf ("#BND=%d", flag2g); else if (!modem_is_2g && modem_is_3g && !modem_is_4g) cmd = g_strdup_printf ("#BND=0,%" G_GINT64_FORMAT, flag3g); else if (modem_is_2g && modem_is_3g && !modem_is_4g) cmd = g_strdup_printf ("#BND=%d,%" G_GINT64_FORMAT, flag2g, flag3g); else if (!modem_is_2g && !modem_is_3g && modem_is_4g) { if (!config->modem_ext_4g_bands) cmd = g_strdup_printf ("#BND=0,0,%" G_GINT64_FORMAT, flag4g); else cmd = g_strdup_printf ("#BND=0,0,%" G_GINT64_MODIFIER "x" ",%" G_GINT64_MODIFIER "x", mask4g, mask4gext); } else if (!modem_is_2g && modem_is_3g && modem_is_4g) { if (!config->modem_ext_4g_bands) cmd = g_strdup_printf ("#BND=0,%" G_GINT64_FORMAT ",%" G_GINT64_FORMAT, flag3g, flag4g); else cmd = g_strdup_printf ("#BND=0,%" G_GINT64_FORMAT ",%" G_GINT64_MODIFIER "x" ",%" G_GINT64_MODIFIER "x", flag3g, mask4g, mask4gext); } else if (modem_is_2g && !modem_is_3g && modem_is_4g) { if (!config->modem_ext_4g_bands) cmd = g_strdup_printf ("#BND=%d,0,%" G_GINT64_FORMAT, flag2g, flag4g); else cmd = g_strdup_printf ("#BND=%d,0,%" G_GINT64_MODIFIER "x" ",%" G_GINT64_MODIFIER "x", flag2g, mask4g, mask4gext); } else if (modem_is_2g && modem_is_3g && modem_is_4g) { if (!config->modem_ext_4g_bands) cmd = g_strdup_printf ("#BND=%d,%" G_GINT64_FORMAT ",%" G_GINT64_FORMAT, flag2g, flag3g, flag4g); else cmd = g_strdup_printf ("#BND=%d,%" G_GINT64_FORMAT ",%" G_GINT64_MODIFIER "x" ",%" G_GINT64_MODIFIER "x", flag2g, flag3g, mask4g, mask4gext); } else g_assert_not_reached (); return cmd; } /*****************************************************************************/ /* #BND response parser * * AT#BND=? * #BND: <2G band flags range>,<3G band flags range>[, <4G band flags range>] * * where "band flags" is a list of numbers defining the supported bands. * Note that the one Telit band flag may represent more than one MM band. * * e.g. * * #BND: (0-2),(3,4) * * (0,2) = 2G band flag 0 is EGSM + DCS * = 2G band flag 1 is EGSM + PCS * = 2G band flag 2 is DCS + G850 * (3,4) = 3G band flag 3 is U2100 + U1900 + U850 * = 3G band flag 4 is U1900 + U850 * * Modems that supports 4G bands, return a range value(X-Y) where * X: represent the lower supported band, such as X = 2^(B-1), being B = B1, B2,..., B32 * Y: is a 32 bit number resulting from a mask of all the supported bands: * 1 - B1 * 2 - B2 * 4 - B3 * 8 - B4 * ... * i - B(2exp(i-1)) * ... * 2147483648 - B32 * * e.g. * (2-4106) * 2 = 2^1 --> lower supported band B2 * 4106 = 2^1 + 2^3 + 2^12 --> the supported bands are B2, B4, B13 * * * AT#BND? * #BND: <2G band flags>,<3G band flags>[, <4G band flags>] * * where "band flags" is a number defining the current bands. * Note that the one Telit band flag may represent more than one MM band. * * e.g. * * #BND: 0,4 * * 0 = 2G band flag 0 is EGSM + DCS * 4 = 3G band flag 4 is U1900 + U850 * * ---------------- * * For modems such as LN920 the #BND configuration/response for LTE bands is different * from what is explained above: * * AT#BND=[,[,[,]]] * * : hex: Indicates the LTE supported bands expressed as the sum of Band number (1+2+8 ...) calculated as shown in the table (mask of 64 bits): * * Band number(Hex) Band i * 0 disable * 1 B1 * 2 B2 * 4 B3 * 8 B4 * ... * ... * 80000000 B32 * ... * ... * 800000000000 B48 * * It can take value, 0 - 87A03B0F38DF: range of the sum of Band number (1+2+4 ...) * * : hex: Indicates the LTE supported bands from B65 expressed as the sum of Band number (1+2+8 ...) calculated as shown in the table (mask of 64 bits): * * Band number(Hex) Band i * 0 disable * 2 B66 * 40 B71 * * It can take value, 0 - 42: range of the sum of Band number (2+40) * * Note: LTE_band and LTE_band_ext cannot be 0 at the same time * * Example output: * * AT#BND=? * #BND: (0),(0-11,17,18),(87A03B0F38DF),(42) * * AT#BND? * #BND: 0,18,87A03B0F38DF,42 * */ static gboolean telit_get_2g_mm_bands (GMatchInfo *match_info, gpointer log_object, GArray **bands, GError **error) { GError *inner_error = NULL; GArray *values = NULL; gchar *match_str = NULL; guint i; match_str = g_match_info_fetch_named (match_info, "Bands2G"); if (!match_str || match_str[0] == '\0') { g_set_error (&inner_error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Could not find 2G band values from response"); goto out; } values = mm_parse_uint_list (match_str, &inner_error); if (!values) goto out; for (i = 0; i < values->len; i++) { guint value; value = g_array_index (values, guint, i); if (value < G_N_ELEMENTS (telit_2g_to_mm_band_mask)) { guint j; for (j = MM_MODEM_BAND_TELIT_2G_FIRST; j <= MM_MODEM_BAND_TELIT_2G_LAST; j++) { if ((telit_2g_to_mm_band_mask[value] & B2G_FLAG (j)) && !mm_common_bands_garray_lookup (*bands, j)) *bands = g_array_append_val (*bands, j); } } else mm_obj_dbg (log_object, "unhandled telit 2G band value configuration: %u", value); } out: g_free (match_str); g_clear_pointer (&values, g_array_unref); if (inner_error) { g_propagate_error (error, inner_error); return FALSE; } return TRUE; } static gboolean telit_get_3g_mm_bands (GMatchInfo *match_info, gpointer log_object, gboolean modem_alternate_3g_bands, GArray **bands, GError **error) { GError *inner_error = NULL; GArray *values = NULL; gchar *match_str = NULL; guint i; const guint64 *telit_3g_to_mm_band_mask; guint telit_3g_to_mm_band_mask_n_elements; initialize_telit_3g_to_mm_band_masks (); /* Select correct 3G band mask */ if (modem_alternate_3g_bands) { telit_3g_to_mm_band_mask = telit_3g_to_mm_band_mask_alternate; telit_3g_to_mm_band_mask_n_elements = G_N_ELEMENTS (telit_3g_to_mm_band_mask_alternate); } else { telit_3g_to_mm_band_mask = telit_3g_to_mm_band_mask_default; telit_3g_to_mm_band_mask_n_elements = G_N_ELEMENTS (telit_3g_to_mm_band_mask_default); } match_str = g_match_info_fetch_named (match_info, "Bands3G"); if (!match_str || match_str[0] == '\0') { g_set_error (&inner_error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Could not find 3G band values from response"); goto out; } values = mm_parse_uint_list (match_str, &inner_error); if (!values) goto out; for (i = 0; i < values->len; i++) { guint value; value = g_array_index (values, guint, i); if (value < telit_3g_to_mm_band_mask_n_elements) { guint j; for (j = 0; j < G_N_ELEMENTS (band_utran_index); j++) { /* ignore non-3G bands */ if (band_utran_index[j] == 0) continue; if ((telit_3g_to_mm_band_mask[value] & B3G_FLAG (j)) && !mm_common_bands_garray_lookup (*bands, j)) *bands = g_array_append_val (*bands, j); } } else mm_obj_dbg (log_object, "unhandled telit 3G band value configuration: %u", value); } out: g_free (match_str); g_clear_pointer (&values, g_array_unref); if (inner_error) { g_propagate_error (error, inner_error); return FALSE; } return TRUE; } static gboolean telit_get_4g_mm_bands (GMatchInfo *match_info, GArray **bands, GError **error) { GError *inner_error = NULL; MMModemBand band; gchar *match_str = NULL; guint64 value; gchar **tokens = NULL; gboolean hex_format = FALSE; gboolean ok; match_str = g_match_info_fetch_named (match_info, "Bands4GDec"); if (!match_str) { match_str = g_match_info_fetch_named (match_info, "Bands4GHex"); hex_format = match_str != NULL; } if (!match_str || match_str[0] == '\0') { g_set_error (&inner_error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Could not find 4G band flags from response"); goto out; } /* splitting will never return NULL as string is not empty */ tokens = g_strsplit (match_str, "-", -1); /* If this is a range, get upper threshold, which contains the total supported mask */ ok = hex_format? mm_get_u64_from_hex_str (tokens[1] ? tokens[1] : tokens[0], &value): mm_get_u64_from_str (tokens[1] ? tokens[1] : tokens[0], &value); if (!ok) { g_set_error (&inner_error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Could not parse 4G band mask from string: '%s'", match_str); goto out; } for (band = MM_MODEM_BAND_TELIT_4G_FIRST; band <= MM_MODEM_BAND_TELIT_4G_LAST; band++) { if ((value & B4G_FLAG (band)) && !mm_common_bands_garray_lookup (*bands, band)) g_array_append_val (*bands, band); } out: g_strfreev (tokens); g_free (match_str); if (inner_error) { g_propagate_error (error, inner_error); return FALSE; } return TRUE; } static gboolean telit_get_ext_4g_mm_bands (GMatchInfo *match_info, GArray **bands, GError **error) { GError *inner_error = NULL; MMModemBand band; gchar *match_str = NULL; gchar *match_str_ext = NULL; guint64 value; match_str = g_match_info_fetch_named (match_info, "Bands4GHex"); if (!match_str || match_str[0] == '\0') { g_set_error (&inner_error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Could not find 4G band hex mask flag from response"); goto out; } if (!mm_get_u64_from_hex_str (match_str, &value)) { g_set_error (&inner_error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Could not parse 4G band hex mask from string: '%s'", match_str); goto out; } for (band = MM_MODEM_BAND_TELIT_4G_FIRST; band <= MM_MODEM_BAND_TELIT_4G_LAST; band++) { if ((value & B4G_FLAG (band)) && !mm_common_bands_garray_lookup (*bands, band)) g_array_append_val (*bands, band); } /* extended bands */ match_str_ext = g_match_info_fetch_named (match_info, "Bands4GExt"); if (match_str_ext) { if (!mm_get_u64_from_hex_str (match_str_ext, &value)) { g_set_error (&inner_error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Could not parse 4G ext band mask from string: '%s'", match_str_ext); goto out; } for (band = MM_MODEM_BAND_TELIT_EXT_4G_FIRST; band <= MM_MODEM_BAND_TELIT_EXT_4G_LAST; band++) { if ((value & B4G_FLAG_EXT (band)) && !mm_common_bands_garray_lookup (*bands, band)) g_array_append_val (*bands, band); } } out: g_free (match_str); g_free (match_str_ext); if (inner_error) { g_propagate_error (error, inner_error); return FALSE; } return TRUE; } typedef enum { LOAD_BANDS_TYPE_SUPPORTED, LOAD_BANDS_TYPE_CURRENT, } LoadBandsType; /* Regex tokens for #BND parsing */ #define MM_SUPPORTED_BANDS_2G "\\s*\\((?P[0-9\\-,]*)\\)" #define MM_SUPPORTED_BANDS_3G "(,\\s*\\((?P[0-9\\-,]*)\\))?" #define MM_SUPPORTED_BANDS_4G_HEX "(,\\s*\\((?P[0-9A-F\\-,]*)\\))?" #define MM_SUPPORTED_BANDS_4G_DEC "(,\\s*\\((?P[0-9\\-,]*)\\))?" #define MM_SUPPORTED_BANDS_4G_EXT "(,\\s*\\((?P[0-9A-F]+)\\))?(,\\s*\\((?P[0-9A-F]+)\\))?" #define MM_CURRENT_BANDS_2G "\\s*(?P\\d+)" #define MM_CURRENT_BANDS_3G "(,\\s*(?P\\d+))?" #define MM_CURRENT_BANDS_4G_HEX "(,\\s*(?P[0-9A-F]+))?" #define MM_CURRENT_BANDS_4G_DEC "(,\\s*(?P\\d+))?" #define MM_CURRENT_BANDS_4G_EXT "(,\\s*(?P[0-9A-F]+))?(,\\s*(?P[0-9A-F]+))?" static GArray * common_parse_bnd_response (const gchar *response, MMTelitBNDParseConfig *config, LoadBandsType load_type, gpointer log_object, GError **error) { g_autoptr(GMatchInfo) match_info = NULL; g_autoptr(GRegex) r = NULL; GError *inner_error = NULL; GArray *bands = NULL; const gchar *load_bands_regex = NULL; static const gchar *load_bands_regex_4g_hex[] = { [LOAD_BANDS_TYPE_SUPPORTED] = "#BND:"MM_SUPPORTED_BANDS_2G MM_SUPPORTED_BANDS_3G MM_SUPPORTED_BANDS_4G_HEX, [LOAD_BANDS_TYPE_CURRENT] = "#BND:"MM_CURRENT_BANDS_2G MM_CURRENT_BANDS_3G MM_CURRENT_BANDS_4G_HEX, }; static const gchar *load_bands_regex_4g_dec[] = { [LOAD_BANDS_TYPE_SUPPORTED] = "#BND:"MM_SUPPORTED_BANDS_2G MM_SUPPORTED_BANDS_3G MM_SUPPORTED_BANDS_4G_DEC, [LOAD_BANDS_TYPE_CURRENT] = "#BND:"MM_CURRENT_BANDS_2G MM_CURRENT_BANDS_3G MM_CURRENT_BANDS_4G_DEC, }; static const gchar *load_bands_regex_4g_ext[] = { [LOAD_BANDS_TYPE_SUPPORTED] = "#BND:"MM_SUPPORTED_BANDS_2G MM_SUPPORTED_BANDS_3G MM_SUPPORTED_BANDS_4G_EXT, [LOAD_BANDS_TYPE_CURRENT] = "#BND:"MM_CURRENT_BANDS_2G MM_CURRENT_BANDS_3G MM_CURRENT_BANDS_4G_EXT, }; if (config->modem_ext_4g_bands) load_bands_regex = load_bands_regex_4g_ext[load_type]; else if (config->modem_has_hex_format_4g_bands) load_bands_regex = load_bands_regex_4g_hex[load_type]; else load_bands_regex = load_bands_regex_4g_dec[load_type]; r = g_regex_new (load_bands_regex, G_REGEX_RAW, 0, NULL); g_assert (r); if (!g_regex_match (r, response, 0, &match_info)) { g_set_error (&inner_error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Could not parse response '%s'", response); goto out; } if (!g_match_info_matches (match_info)) { g_set_error (&inner_error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Could not find matches in response '%s'", response); goto out; } bands = g_array_new (TRUE, TRUE, sizeof (MMModemBand)); if (config->modem_is_2g && !telit_get_2g_mm_bands (match_info, log_object, &bands, &inner_error)) goto out; if (config->modem_is_3g && !telit_get_3g_mm_bands (match_info, log_object, config->modem_alternate_3g_bands, &bands, &inner_error)) goto out; if (config->modem_is_4g) { gboolean ok; ok = config->modem_ext_4g_bands? telit_get_ext_4g_mm_bands (match_info, &bands, &inner_error) : telit_get_4g_mm_bands (match_info, &bands, &inner_error); if (!ok) goto out; } out: if (inner_error) { g_propagate_error (error, inner_error); g_clear_pointer (&bands, g_array_unref); return NULL; } return bands; } GArray * mm_telit_parse_bnd_query_response (const gchar *response, MMTelitBNDParseConfig *config, gpointer log_object, GError **error) { return common_parse_bnd_response (response, config, LOAD_BANDS_TYPE_CURRENT, log_object, error); } GArray * mm_telit_parse_bnd_test_response (const gchar *response, MMTelitBNDParseConfig *config, gpointer log_object, GError **error) { return common_parse_bnd_response (response, config, LOAD_BANDS_TYPE_SUPPORTED, log_object, error); } /*****************************************************************************/ /* #QSS? response parser */ MMTelitQssStatus mm_telit_parse_qss_query (const gchar *response, GError **error) { gint qss_status; gint qss_mode; qss_status = QSS_STATUS_UNKNOWN; if (sscanf (response, "#QSS: %d,%d", &qss_mode, &qss_status) != 2) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Could not parse \"#QSS?\" response: %s", response); return QSS_STATUS_UNKNOWN; } switch (qss_status) { case QSS_STATUS_SIM_REMOVED: case QSS_STATUS_SIM_INSERTED: case QSS_STATUS_SIM_INSERTED_AND_UNLOCKED: case QSS_STATUS_SIM_INSERTED_AND_READY: return (MMTelitQssStatus) qss_status; default: g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Unknown QSS status value given: %d", qss_status); return QSS_STATUS_UNKNOWN; } } /*****************************************************************************/ /* Supported modes list helper */ GArray * mm_telit_build_modes_list (void) { GArray *combinations; MMModemModeCombination mode; /* Build list of combinations for 3GPP devices */ combinations = g_array_sized_new (FALSE, FALSE, sizeof (MMModemModeCombination), 11); /* 2G only */ mode.allowed = MM_MODEM_MODE_2G; mode.preferred = MM_MODEM_MODE_NONE; g_array_append_val (combinations, mode); /* 3G only */ mode.allowed = MM_MODEM_MODE_3G; mode.preferred = MM_MODEM_MODE_NONE; g_array_append_val (combinations, mode); /* 2G and 3G */ mode.allowed = (MM_MODEM_MODE_2G | MM_MODEM_MODE_3G); mode.preferred = MM_MODEM_MODE_NONE; g_array_append_val (combinations, mode); /* 4G only */ mode.allowed = MM_MODEM_MODE_4G; mode.preferred = MM_MODEM_MODE_NONE; g_array_append_val (combinations, mode); /* 2G and 4G */ mode.allowed = (MM_MODEM_MODE_2G | MM_MODEM_MODE_4G); mode.preferred = MM_MODEM_MODE_NONE; g_array_append_val (combinations, mode); /* 3G and 4G */ mode.allowed = (MM_MODEM_MODE_3G | MM_MODEM_MODE_4G); mode.preferred = MM_MODEM_MODE_NONE; g_array_append_val (combinations, mode); /* 2G, 3G and 4G */ mode.allowed = (MM_MODEM_MODE_2G | MM_MODEM_MODE_3G | MM_MODEM_MODE_4G); mode.preferred = MM_MODEM_MODE_NONE; g_array_append_val (combinations, mode); /* 5G only */ mode.allowed = MM_MODEM_MODE_5G; mode.preferred = MM_MODEM_MODE_NONE; g_array_append_val (combinations, mode); /* 3G and 5G */ mode.allowed = (MM_MODEM_MODE_3G | MM_MODEM_MODE_5G); mode.preferred = MM_MODEM_MODE_NONE; g_array_append_val (combinations, mode); /* 4G and 5G */ mode.allowed = (MM_MODEM_MODE_4G | MM_MODEM_MODE_5G); mode.preferred = MM_MODEM_MODE_NONE; g_array_append_val (combinations, mode); /* 3G, 4G and 5G */ mode.allowed = (MM_MODEM_MODE_3G | MM_MODEM_MODE_4G | MM_MODEM_MODE_5G); mode.preferred = MM_MODEM_MODE_NONE; g_array_append_val (combinations, mode); return combinations; } /*****************************************************************************/ /* Software Package version response parser */ gchar * mm_telit_parse_swpkgv_response (const gchar *response) { gchar *version = NULL; g_autofree gchar *base_version = NULL; g_autoptr(GRegex) r = NULL; g_autoptr(GMatchInfo) match_info = NULL; guint matches; /* We are interested only in the first line of the response */ r = g_regex_new ("(?P\\d{2}.\\d{2}.*)", G_REGEX_RAW | G_REGEX_MULTILINE | G_REGEX_NEWLINE_CRLF, G_REGEX_MATCH_NEWLINE_CR, NULL); g_assert (r != NULL); if (!g_regex_match_full (r, response, strlen (response), 0, 0, &match_info, NULL)) { return NULL; } matches = g_match_info_get_match_count (match_info); if (matches < 2 || matches > 4) { return NULL; } base_version = g_match_info_fetch_named (match_info, "Base"); if (base_version) version = g_strdup (base_version); return version; } /*****************************************************************************/ /* MM Telit Model from revision string */ MMTelitModel mm_telit_model_from_revision (const gchar *revision) { guint i; static const struct { const gchar *revision_prefix; MMTelitModel model; } revision_to_model_map [] = { {"24.01", MM_TELIT_MODEL_LM940}, {"25.", MM_TELIT_MODEL_LE910C1}, {"32.", MM_TELIT_MODEL_LM960}, {"38.", MM_TELIT_MODEL_FN980}, {"40.", MM_TELIT_MODEL_LN920}, {"45.00", MM_TELIT_MODEL_FN990}, }; if (!revision) return MM_TELIT_MODEL_DEFAULT; for (i = 0; i < G_N_ELEMENTS (revision_to_model_map); ++i) { if (g_str_has_prefix (revision, revision_to_model_map[i].revision_prefix)) return revision_to_model_map[i].model; } return MM_TELIT_MODEL_DEFAULT; } static MMTelitSwRevCmp lm9x0_software_revision_cmp (const gchar *revision_a, const gchar *revision_b) { /* LM940 and LM960 share the same software revision format * WW.XY.ABC[-ZZZZ], where WW is the chipset code and C the major version. * If WW is the same, the other values X, Y, A and B are also the same, so * we can limit the comparison to C only. ZZZZ is the minor version (it * includes if version is beta, test, or alpha), but at this stage we are * not interested in compare it. */ guint chipset_a, chipset_b; guint major_a, major_b; guint x, y, a, b; g_return_val_if_fail ( sscanf (revision_a, "%2u.%1u%1u.%1u%1u%1u", &chipset_a, &x, &y, &a, &b, &major_a) == 6, MM_TELIT_SW_REV_CMP_INVALID); g_return_val_if_fail ( sscanf (revision_b, "%2u.%1u%1u.%1u%1u%1u", &chipset_b, &x, &y, &a, &b, &major_b) == 6, MM_TELIT_SW_REV_CMP_INVALID); if (chipset_a != chipset_b) return MM_TELIT_SW_REV_CMP_INVALID; if (major_a > major_b) return MM_TELIT_SW_REV_CMP_NEWER; if (major_a < major_b) return MM_TELIT_SW_REV_CMP_OLDER; return MM_TELIT_SW_REV_CMP_EQUAL; } MMTelitSwRevCmp mm_telit_software_revision_cmp (const gchar *revision_a, const gchar *revision_b) { MMTelitModel model_a; MMTelitModel model_b; model_a = mm_telit_model_from_revision (revision_a); model_b = mm_telit_model_from_revision (revision_b); if ((model_a == MM_TELIT_MODEL_LM940 || model_a == MM_TELIT_MODEL_LM960) && (model_b == MM_TELIT_MODEL_LM940 || model_b == MM_TELIT_MODEL_LM960)) { return lm9x0_software_revision_cmp (revision_a, revision_b); } return MM_TELIT_SW_REV_CMP_UNSUPPORTED; } ModemManager-1.23.4-dev/src/plugins/telit/mm-modem-helpers-telit.h000066400000000000000000000064011456466623000247570ustar00rootroot00000000000000/* -*- 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) 2015-2019 Telit. * Copyright (C) 2019 Aleksander Morgado */ #ifndef MM_MODEM_HELPERS_TELIT_H #define MM_MODEM_HELPERS_TELIT_H #include #include "ModemManager.h" typedef enum { MM_TELIT_MODEL_DEFAULT, MM_TELIT_MODEL_FN980, MM_TELIT_MODEL_LE910C1, MM_TELIT_MODEL_LM940, MM_TELIT_MODEL_LM960, MM_TELIT_MODEL_LN920, MM_TELIT_MODEL_FN990, } MMTelitModel; typedef struct { gboolean modem_is_2g; gboolean modem_is_3g; gboolean modem_is_4g; gboolean modem_alternate_3g_bands; gboolean modem_has_hex_format_4g_bands; gboolean modem_ext_4g_bands; } MMTelitBNDParseConfig; typedef enum { MM_TELIT_SW_REV_CMP_INVALID, MM_TELIT_SW_REV_CMP_UNSUPPORTED, MM_TELIT_SW_REV_CMP_OLDER, MM_TELIT_SW_REV_CMP_EQUAL, MM_TELIT_SW_REV_CMP_NEWER, } MMTelitSwRevCmp; /* #BND response parsers and request builder */ GArray *mm_telit_parse_bnd_query_response (const gchar *response, MMTelitBNDParseConfig *config, gpointer log_object, GError **error); GArray *mm_telit_parse_bnd_test_response (const gchar *response, MMTelitBNDParseConfig *config, gpointer log_object, GError **error); gchar *mm_telit_build_bnd_request (GArray *bands_array, MMTelitBNDParseConfig *config, GError **error); /* #QSS? response parser */ typedef enum { /*< underscore_name=mm_telit_qss_status >*/ QSS_STATUS_UNKNOWN = -1, QSS_STATUS_SIM_REMOVED, QSS_STATUS_SIM_INSERTED, QSS_STATUS_SIM_INSERTED_AND_UNLOCKED, QSS_STATUS_SIM_INSERTED_AND_READY, } MMTelitQssStatus; MMTelitQssStatus mm_telit_parse_qss_query (const gchar *response, GError **error); /* CSIM lock state */ typedef enum { /*< underscore_name=mm_telit_csim_lock_state >*/ CSIM_LOCK_STATE_UNKNOWN, CSIM_LOCK_STATE_UNLOCKED, CSIM_LOCK_STATE_LOCK_REQUESTED, CSIM_LOCK_STATE_LOCKED, } MMTelitCsimLockState; GArray *mm_telit_build_modes_list (void); gchar *mm_telit_parse_swpkgv_response (const gchar *response); MMTelitModel mm_telit_model_from_revision (const gchar *revision); MMTelitSwRevCmp mm_telit_software_revision_cmp (const gchar *reference, const gchar *revision); #endif /* MM_MODEM_HELPERS_TELIT_H */ ModemManager-1.23.4-dev/src/plugins/telit/mm-plugin-telit.c000066400000000000000000000120011456466623000235000ustar00rootroot00000000000000/* -*- 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 - 2013 Red Hat, Inc. * Copyright (C) 2012 Aleksander Morgado */ #include #include #define _LIBMM_INSIDE_MM #include #include "mm-log-object.h" #include "mm-modem-helpers.h" #include "mm-plugin-common.h" #include "mm-common-telit.h" #include "mm-broadband-modem-telit.h" #if defined WITH_QMI # include "mm-broadband-modem-qmi.h" #endif #if defined WITH_MBIM # include "mm-broadband-modem-mbim-telit.h" #endif #define MM_TYPE_PLUGIN_TELIT mm_plugin_telit_get_type () MM_DEFINE_PLUGIN (TELIT, telit, Telit) /*****************************************************************************/ static MMBaseModem * create_modem (MMPlugin *self, const gchar *uid, const gchar *physdev, const gchar **drivers, guint16 vendor, guint16 product, guint16 subsystem_vendor, GList *probes, GError **error) { #if defined WITH_QMI if (mm_port_probe_list_has_qmi_port (probes)) { mm_obj_dbg (self, "QMI-powered Telit modem found..."); return MM_BASE_MODEM (mm_broadband_modem_qmi_new (uid, physdev, drivers, mm_plugin_get_name (self), vendor, product)); } #endif #if defined WITH_MBIM if (mm_port_probe_list_has_mbim_port (probes)) { mm_obj_dbg (self, "MBIM-powered Telit modem found..."); return MM_BASE_MODEM (mm_broadband_modem_mbim_telit_new (uid, physdev, drivers, mm_plugin_get_name (self), vendor, product, subsystem_vendor)); } #endif return MM_BASE_MODEM (mm_broadband_modem_telit_new (uid, physdev, drivers, mm_plugin_get_name (self), vendor, product)); } /*****************************************************************************/ MM_PLUGIN_NAMED_CREATOR_SCOPE MMPlugin * mm_plugin_create_telit (void) { static const gchar *subsystems[] = { "tty", "net", "usbmisc", "wwan", NULL }; /* Vendors: Telit */ static const guint16 vendor_ids[] = { 0x1bc7, 0 }; static const mm_uint16_pair subsystem_vendor_ids[] = { { 0x17cb, 0x1c5d }, /* FN990 */ { 0, 0 } }; static const gchar *vendor_strings[] = { "telit", NULL }; /* Custom init for port identification */ static const MMAsyncMethod custom_init = { .async = G_CALLBACK (telit_custom_init), .finish = G_CALLBACK (telit_custom_init_finish), }; return MM_PLUGIN ( g_object_new (MM_TYPE_PLUGIN_TELIT, MM_PLUGIN_NAME, MM_MODULE_NAME, MM_PLUGIN_ALLOWED_SUBSYSTEMS, subsystems, MM_PLUGIN_ALLOWED_VENDOR_IDS, vendor_ids, MM_PLUGIN_ALLOWED_SUBSYSTEM_VENDOR_IDS, subsystem_vendor_ids, MM_PLUGIN_ALLOWED_VENDOR_STRINGS, vendor_strings, MM_PLUGIN_ALLOWED_AT, TRUE, MM_PLUGIN_ALLOWED_QMI, TRUE, MM_PLUGIN_ALLOWED_MBIM, TRUE, MM_PLUGIN_ALLOWED_QCDM, TRUE, MM_PLUGIN_CUSTOM_INIT, &custom_init, NULL)); } static void mm_plugin_telit_init (MMPluginTelit *self) { } static void mm_plugin_telit_class_init (MMPluginTelitClass *klass) { MMPluginClass *plugin_class = MM_PLUGIN_CLASS (klass); plugin_class->create_modem = create_modem; plugin_class->grab_port = telit_grab_port; } ModemManager-1.23.4-dev/src/plugins/telit/mm-shared-telit.c000066400000000000000000000634251456466623000234700ustar00rootroot00000000000000/* -*- 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) 2019 Daniele Palmas */ #include #include #include #include #define _LIBMM_INSIDE_MM #include #include "mm-log-object.h" #include "mm-iface-modem.h" #include "mm-iface-modem-location.h" #include "mm-base-modem.h" #include "mm-base-modem-at.h" #include "mm-modem-helpers-telit.h" #include "mm-shared-telit.h" /*****************************************************************************/ /* Private data context */ #define TELIT_LM940_EXT_LTE_BND_SW_REVISION "24.01.516" #define PRIVATE_TAG "shared-telit-private-tag" static GQuark private_quark; typedef struct { MMIfaceModem *iface_modem_parent; gboolean alternate_3g_bands; gboolean ext_4g_bands; GArray *supported_bands; GArray *supported_modes; gchar *software_package_version; } Private; static void private_free (Private *priv) { if (priv->supported_bands) g_array_unref (priv->supported_bands); if (priv->supported_modes) g_array_unref (priv->supported_modes); g_free (priv->software_package_version); g_slice_free (Private, priv); } static gboolean has_alternate_3g_bands (const gchar *revision) { MMTelitModel model; model = mm_telit_model_from_revision (revision); return (model == MM_TELIT_MODEL_FN980 || model == MM_TELIT_MODEL_FN990 || model == MM_TELIT_MODEL_LM940 || model == MM_TELIT_MODEL_LM960 || model == MM_TELIT_MODEL_LN920); } static gboolean is_bnd_4g_format_hex (const gchar *revision) { MMTelitModel model; model = mm_telit_model_from_revision (revision); return (model == MM_TELIT_MODEL_FN980 || model == MM_TELIT_MODEL_LE910C1 || model == MM_TELIT_MODEL_LM940 || model == MM_TELIT_MODEL_LM960 || model == MM_TELIT_MODEL_LN920); } static gboolean has_extended_4g_bands (const gchar *revision) { MMTelitModel model; model = mm_telit_model_from_revision (revision); if (model == MM_TELIT_MODEL_LM940) return mm_telit_software_revision_cmp (revision, TELIT_LM940_EXT_LTE_BND_SW_REVISION) >= MM_TELIT_SW_REV_CMP_EQUAL; return (model == MM_TELIT_MODEL_FN980 || model == MM_TELIT_MODEL_FN990 || model == MM_TELIT_MODEL_LM960 || model == MM_TELIT_MODEL_LN920); } static Private * get_private (MMSharedTelit *self) { Private *priv; if (G_UNLIKELY (!private_quark)) private_quark = g_quark_from_static_string (PRIVATE_TAG); priv = g_object_get_qdata (G_OBJECT (self), private_quark); if (!priv) { priv = g_slice_new0 (Private); if (MM_SHARED_TELIT_GET_INTERFACE (self)->peek_parent_modem_interface) priv->iface_modem_parent = MM_SHARED_TELIT_GET_INTERFACE (self)->peek_parent_modem_interface (self); g_object_set_qdata_full (G_OBJECT (self), private_quark, priv, (GDestroyNotify)private_free); } return priv; } void mm_shared_telit_store_supported_modes (MMSharedTelit *self, GArray *modes) { Private *priv; priv = get_private (MM_SHARED_TELIT (self)); priv->supported_modes = g_array_ref (modes); } void mm_shared_telit_store_revision (MMSharedTelit *self, const gchar *revision) { Private *priv; priv = get_private (MM_SHARED_TELIT (self)); g_clear_pointer (&priv->software_package_version, g_free); priv->software_package_version = g_strdup (revision); priv->alternate_3g_bands = has_alternate_3g_bands (revision); priv->ext_4g_bands = has_extended_4g_bands (revision); } void mm_shared_telit_get_bnd_parse_config (MMIfaceModem *self, MMTelitBNDParseConfig *config) { Private *priv; priv = get_private (MM_SHARED_TELIT (self)); config->modem_is_2g = mm_iface_modem_is_2g (self); config->modem_is_3g = mm_iface_modem_is_3g (self); config->modem_is_4g = mm_iface_modem_is_4g (self); config->modem_alternate_3g_bands = priv->alternate_3g_bands; config->modem_has_hex_format_4g_bands = is_bnd_4g_format_hex (priv->software_package_version); config->modem_ext_4g_bands = priv->ext_4g_bands; } /*****************************************************************************/ /* Load current mode (Modem interface) */ gboolean mm_shared_telit_load_current_modes_finish (MMIfaceModem *self, GAsyncResult *res, MMModemMode *allowed, MMModemMode *preferred, GError **error) { const gchar *response; const gchar *str; gint a; response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, error); if (!response) return FALSE; str = mm_strip_tag (response, "+WS46: "); if (!sscanf (str, "%d", &a)) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't parse +WS46 response: '%s'", response); return FALSE; } *preferred = MM_MODEM_MODE_NONE; switch (a) { case 12: *allowed = MM_MODEM_MODE_2G; return TRUE; case 22: *allowed = MM_MODEM_MODE_3G; return TRUE; case 25: if (mm_iface_modem_is_3gpp_lte (self)) *allowed = (MM_MODEM_MODE_2G | MM_MODEM_MODE_3G | MM_MODEM_MODE_4G); else *allowed = (MM_MODEM_MODE_2G | MM_MODEM_MODE_3G); return TRUE; case 28: *allowed = MM_MODEM_MODE_4G; return TRUE; case 29: *allowed = (MM_MODEM_MODE_2G | MM_MODEM_MODE_3G); return TRUE; case 30: *allowed = (MM_MODEM_MODE_2G | MM_MODEM_MODE_4G); return TRUE; case 31: *allowed = (MM_MODEM_MODE_3G | MM_MODEM_MODE_4G); return TRUE; case 36: *allowed = MM_MODEM_MODE_5G; return TRUE; case 37: *allowed = (MM_MODEM_MODE_4G | MM_MODEM_MODE_5G); return TRUE; case 38: *allowed = (MM_MODEM_MODE_3G | MM_MODEM_MODE_4G | MM_MODEM_MODE_5G); return TRUE; case 40: *allowed = (MM_MODEM_MODE_3G | MM_MODEM_MODE_5G); return TRUE; default: break; } g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't parse unexpected +WS46 response: '%s'", response); return FALSE; } void mm_shared_telit_load_current_modes (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data) { mm_base_modem_at_command (MM_BASE_MODEM (self), "+WS46?", 3, FALSE, callback, user_data); } /*****************************************************************************/ /* Load supported bands (Modem interface) */ GArray * mm_shared_telit_modem_load_supported_bands_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { return g_task_propagate_pointer (G_TASK (res), error); } static void load_supported_bands_ready (MMBaseModem *self, GAsyncResult *res, GTask *task) { const gchar *response; GError *error = NULL; Private *priv; priv = get_private (MM_SHARED_TELIT (self)); response = mm_base_modem_at_command_finish (self, res, &error); if (!response) g_task_return_error (task, error); else { GArray *bands; MMTelitBNDParseConfig config; mm_shared_telit_get_bnd_parse_config (MM_IFACE_MODEM (self), &config); bands = mm_telit_parse_bnd_test_response (response, &config, self, &error); if (!bands) g_task_return_error (task, error); else { /* Store supported bands to be able to build ANY when setting */ priv->supported_bands = g_array_ref (bands); if (priv->ext_4g_bands) mm_obj_dbg (self, "telit modem using extended 4G band setup"); g_task_return_pointer (task, bands, (GDestroyNotify)g_array_unref); } } g_object_unref (task); } static void load_supported_bands_at (MMIfaceModem *self, GTask *task) { mm_base_modem_at_command (MM_BASE_MODEM (self), "#BND=?", 3, TRUE, (GAsyncReadyCallback) load_supported_bands_ready, task); } static void parent_load_supported_bands_ready (MMIfaceModem *self, GAsyncResult *res, GTask *task) { GArray *bands; GError *error = NULL; Private *priv; priv = get_private (MM_SHARED_TELIT (self)); bands = priv->iface_modem_parent->load_supported_bands_finish (MM_IFACE_MODEM (self), res, &error); if (bands) { g_task_return_pointer (task, bands, (GDestroyNotify)g_array_unref); g_object_unref (task); } else { mm_obj_dbg (self, "parent load supported bands failure, falling back to AT commands"); load_supported_bands_at (self, task); g_clear_error (&error); } } void mm_shared_telit_modem_load_supported_bands (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; Private *priv; task = g_task_new (self, NULL, callback, user_data); priv = get_private (MM_SHARED_TELIT (self)); if (priv->iface_modem_parent && priv->iface_modem_parent->load_supported_bands && priv->iface_modem_parent->load_supported_bands_finish) { priv->iface_modem_parent->load_supported_bands (self, (GAsyncReadyCallback) parent_load_supported_bands_ready, task); } else load_supported_bands_at (self, task); } /*****************************************************************************/ /* Load current bands (Modem interface) */ GArray * mm_shared_telit_modem_load_current_bands_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { return g_task_propagate_pointer (G_TASK (res), error); } static void load_current_bands_ready (MMBaseModem *self, GAsyncResult *res, GTask *task) { const gchar *response; GError *error = NULL; response = mm_base_modem_at_command_finish (self, res, &error); if (!response) g_task_return_error (task, error); else { GArray *bands; MMTelitBNDParseConfig config; mm_shared_telit_get_bnd_parse_config (MM_IFACE_MODEM (self), &config); bands = mm_telit_parse_bnd_query_response (response, &config, self, &error); if (!bands) g_task_return_error (task, error); else g_task_return_pointer (task, bands, (GDestroyNotify)g_array_unref); } g_object_unref (task); } static void load_current_bands_at (MMIfaceModem *self, GTask *task) { mm_base_modem_at_command (MM_BASE_MODEM (self), "#BND?", 3, FALSE, (GAsyncReadyCallback) load_current_bands_ready, task); } static void parent_load_current_bands_ready (MMIfaceModem *self, GAsyncResult *res, GTask *task) { GArray *bands; GError *error = NULL; Private *priv; priv = get_private (MM_SHARED_TELIT (self)); bands = priv->iface_modem_parent->load_current_bands_finish (MM_IFACE_MODEM (self), res, &error); if (bands) { g_task_return_pointer (task, bands, (GDestroyNotify)g_array_unref); g_object_unref (task); } else { mm_obj_dbg (self, "parent load current bands failure, falling back to AT commands"); load_current_bands_at (self, task); g_clear_error (&error); } } void mm_shared_telit_modem_load_current_bands (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; Private *priv; task = g_task_new (self, NULL, callback, user_data); priv = get_private (MM_SHARED_TELIT (self)); if (priv->iface_modem_parent && priv->iface_modem_parent->load_current_bands && priv->iface_modem_parent->load_current_bands_finish) { priv->iface_modem_parent->load_current_bands (self, (GAsyncReadyCallback) parent_load_current_bands_ready, task); } else load_current_bands_at (self, task); } /*****************************************************************************/ /* Set current bands (Modem interface) */ gboolean mm_shared_telit_modem_set_current_bands_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void set_current_bands_ready (MMBaseModem *self, GAsyncResult *res, GTask *task) { GError *error = NULL; mm_base_modem_at_command_finish (self, res, &error); if (error) g_task_return_error (task, error); else g_task_return_boolean (task, TRUE); g_object_unref (task); } static void set_current_bands_at (MMIfaceModem *self, GTask *task) { GError *error = NULL; gchar *cmd; GArray *bands_array; MMTelitBNDParseConfig config; bands_array = g_task_get_task_data (task); g_assert (bands_array); if (bands_array->len == 1 && g_array_index (bands_array, MMModemBand, 0) == MM_MODEM_BAND_ANY) { Private *priv; priv = get_private (MM_SHARED_TELIT (self)); if (!priv->supported_bands) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't build ANY band settings: unknown supported bands"); g_object_unref (task); return; } bands_array = priv->supported_bands; } mm_shared_telit_get_bnd_parse_config (self, &config); cmd = mm_telit_build_bnd_request (bands_array, &config, &error); if (!cmd) { g_task_return_error (task, error); g_object_unref (task); return; } mm_base_modem_at_command (MM_BASE_MODEM (self), cmd, 20, FALSE, (GAsyncReadyCallback)set_current_bands_ready, task); g_free (cmd); } static void parent_set_current_bands_ready (MMIfaceModem *self, GAsyncResult *res, GTask *task) { GError *error = NULL; Private *priv; priv = get_private (MM_SHARED_TELIT (self)); if (priv->iface_modem_parent->set_current_bands_finish (MM_IFACE_MODEM (self), res, &error)) { g_task_return_boolean (task, TRUE); g_object_unref (task); } else { g_clear_error (&error); set_current_bands_at (self, task); } } void mm_shared_telit_modem_set_current_bands (MMIfaceModem *self, GArray *bands_array, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; Private *priv; priv = get_private (MM_SHARED_TELIT (self)); task = g_task_new (self, NULL, callback, user_data); g_task_set_task_data (task, g_array_ref (bands_array), (GDestroyNotify)g_array_unref); if (priv->iface_modem_parent && priv->iface_modem_parent->set_current_bands && priv->iface_modem_parent->set_current_bands_finish) { priv->iface_modem_parent->set_current_bands (self, bands_array, (GAsyncReadyCallback) parent_set_current_bands_ready, task); } else set_current_bands_at (self, task); } /*****************************************************************************/ /* Set current modes (Modem interface) */ gboolean mm_shared_telit_set_current_modes_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void ws46_set_ready (MMBaseModem *self, GAsyncResult *res, GTask *task) { GError *error = NULL; mm_base_modem_at_command_finish (self, res, &error); if (error) /* Let the error be critical. */ g_task_return_error (task, error); else g_task_return_boolean (task, TRUE); g_object_unref (task); } void mm_shared_telit_set_current_modes (MMIfaceModem *self, MMModemMode allowed, MMModemMode preferred, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; gchar *command; Private *priv; gint ws46_mode = -1; priv = get_private (MM_SHARED_TELIT (self)); task = g_task_new (self, NULL, callback, user_data); if (allowed == MM_MODEM_MODE_ANY && priv->supported_modes) { guint i; allowed = MM_MODEM_MODE_NONE; /* Process list of modes to gather supported ones */ for (i = 0; i < priv->supported_modes->len; i++) { if (g_array_index (priv->supported_modes, MMModemMode, i) & MM_MODEM_MODE_2G) allowed |= MM_MODEM_MODE_2G; if (g_array_index (priv->supported_modes, MMModemMode, i) & MM_MODEM_MODE_3G) allowed |= MM_MODEM_MODE_3G; if (g_array_index (priv->supported_modes, MMModemMode, i) & MM_MODEM_MODE_4G) allowed |= MM_MODEM_MODE_4G; if (g_array_index (priv->supported_modes, MMModemMode, i) & MM_MODEM_MODE_5G) allowed |= MM_MODEM_MODE_5G; } } if (allowed == MM_MODEM_MODE_2G) ws46_mode = 12; else if (allowed == MM_MODEM_MODE_3G) ws46_mode = 22; else if (allowed == MM_MODEM_MODE_4G) ws46_mode = 28; else if (allowed == MM_MODEM_MODE_5G) ws46_mode = 36; else if (allowed == (MM_MODEM_MODE_2G | MM_MODEM_MODE_3G)) { if (mm_iface_modem_is_3gpp_lte (self)) ws46_mode = 29; else ws46_mode = 25; } else if (allowed == (MM_MODEM_MODE_2G | MM_MODEM_MODE_4G)) ws46_mode = 30; else if (allowed == (MM_MODEM_MODE_3G | MM_MODEM_MODE_4G)) ws46_mode = 31; else if (allowed == (MM_MODEM_MODE_2G | MM_MODEM_MODE_3G | MM_MODEM_MODE_4G)) ws46_mode = 25; else if (allowed == (MM_MODEM_MODE_3G | MM_MODEM_MODE_5G)) ws46_mode = 40; else if (allowed == (MM_MODEM_MODE_4G | MM_MODEM_MODE_5G)) ws46_mode = 37; else if (allowed == (MM_MODEM_MODE_3G |MM_MODEM_MODE_4G | MM_MODEM_MODE_5G)) ws46_mode = 38; /* Telit modems do not support preferred mode selection */ if ((ws46_mode < 0) || (preferred != MM_MODEM_MODE_NONE)) { gchar *allowed_str; gchar *preferred_str; allowed_str = mm_modem_mode_build_string_from_mask (allowed); preferred_str = mm_modem_mode_build_string_from_mask (preferred); g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Requested mode (allowed: '%s', preferred: '%s') not " "supported by the modem.", allowed_str, preferred_str); g_free (allowed_str); g_free (preferred_str); g_object_unref (task); return; } command = g_strdup_printf ("AT+WS46=%d", ws46_mode); mm_base_modem_at_command ( MM_BASE_MODEM (self), command, 10, FALSE, (GAsyncReadyCallback) ws46_set_ready, task); g_free (command); } /*****************************************************************************/ /* Revision loading */ gchar * mm_shared_telit_modem_load_revision_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { return g_task_propagate_pointer (G_TASK (res), error); } static void load_revision_ready (MMBaseModem *self, GAsyncResult *res, GTask *task) { GError *error; GVariant *result; result = mm_base_modem_at_sequence_finish (self, res, NULL, &error); if (!result) { g_task_return_error (task, error); g_object_unref (task); } else { gchar *revision = NULL; revision = g_variant_dup_string (result, NULL); mm_shared_telit_store_revision (MM_SHARED_TELIT (self), revision); g_task_return_pointer (task, revision, g_free); g_object_unref (task); } } /* * parse AT#SWPKGV command * Execution command returns the software package version without #SWPKGV: command echo. * The response is as follows: * * AT#SWPKGV * - * (Usually the same value returned by AT+GMR) * * */ static MMBaseModemAtResponseProcessorResult software_package_version_ready (MMBaseModem *self, gpointer none, const gchar *command, const gchar *response, gboolean last_command, const GError *error, GVariant **result, GError **result_error) { gchar *version = NULL; if (error) { *result = NULL; /* Ignore AT errors (ie, ERROR or CMx ERROR) */ if (error->domain != MM_MOBILE_EQUIPMENT_ERROR || last_command) { *result_error = g_error_copy (error); return MM_BASE_MODEM_AT_RESPONSE_PROCESSOR_RESULT_FAILURE; } *result_error = NULL; return MM_BASE_MODEM_AT_RESPONSE_PROCESSOR_RESULT_CONTINUE; } version = mm_telit_parse_swpkgv_response (response); if (!version) return MM_BASE_MODEM_AT_RESPONSE_PROCESSOR_RESULT_CONTINUE; *result = g_variant_new_take_string (version); return MM_BASE_MODEM_AT_RESPONSE_PROCESSOR_RESULT_SUCCESS; } static const MMBaseModemAtCommand revisions[] = { { "#SWPKGV", 3, TRUE, software_package_version_ready }, { "+CGMR", 3, TRUE, mm_base_modem_response_processor_string_ignore_at_errors }, { "+GMR", 3, TRUE, mm_base_modem_response_processor_string_ignore_at_errors }, { NULL } }; void mm_shared_telit_modem_load_revision (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; Private *priv; task = g_task_new (self, NULL, callback, user_data); priv = get_private (MM_SHARED_TELIT (self)); mm_obj_dbg (self, "loading revision..."); if (priv->software_package_version) { g_task_return_pointer (task, g_strdup (priv->software_package_version), g_free); g_object_unref (task); return; } mm_base_modem_at_sequence ( MM_BASE_MODEM (self), revisions, NULL, /* response_processor_context */ NULL, /* response_processor_context_free */ (GAsyncReadyCallback) load_revision_ready, task); } /*****************************************************************************/ static void shared_telit_init (gpointer g_iface) { } GType mm_shared_telit_get_type (void) { static GType shared_telit_type = 0; if (!G_UNLIKELY (shared_telit_type)) { static const GTypeInfo info = { sizeof (MMSharedTelit), /* class_size */ shared_telit_init, /* base_init */ NULL, /* base_finalize */ }; shared_telit_type = g_type_register_static (G_TYPE_INTERFACE, "MMSharedTelit", &info, 0); g_type_interface_add_prerequisite (shared_telit_type, MM_TYPE_IFACE_MODEM); } return shared_telit_type; } ModemManager-1.23.4-dev/src/plugins/telit/mm-shared-telit.h000066400000000000000000000124571456466623000234740ustar00rootroot00000000000000/* -*- 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) 2019 Daniele Palmas */ #ifndef MM_SHARED_TELIT_H #define MM_SHARED_TELIT_H #include #include #define _LIBMM_INSIDE_MM #include #include "mm-broadband-modem.h" #include "mm-iface-modem.h" #include "mm-iface-modem-location.h" #include "mm-modem-helpers-telit.h" #define MM_TYPE_SHARED_TELIT (mm_shared_telit_get_type ()) #define MM_SHARED_TELIT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_SHARED_TELIT, MMSharedTelit)) #define MM_IS_SHARED_TELIT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_SHARED_TELIT)) #define MM_SHARED_TELIT_GET_INTERFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE ((obj), MM_TYPE_SHARED_TELIT, MMSharedTelit)) typedef struct _MMSharedTelit MMSharedTelit; struct _MMSharedTelit { GTypeInterface g_iface; /* Peek modem interface of the parent class of the object */ MMIfaceModem * (* peek_parent_modem_interface) (MMSharedTelit *self); }; GType mm_shared_telit_get_type (void); void mm_shared_telit_store_supported_modes (MMSharedTelit *self, GArray *modes); gboolean mm_shared_telit_load_current_modes_finish (MMIfaceModem *self, GAsyncResult *res, MMModemMode *allowed, MMModemMode *preferred, GError **error); void mm_shared_telit_load_current_modes (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data); gboolean mm_shared_telit_set_current_modes_finish (MMIfaceModem *self, GAsyncResult *res, GError **error); void mm_shared_telit_set_current_modes (MMIfaceModem *self, MMModemMode allowed, MMModemMode preferred, GAsyncReadyCallback callback, gpointer user_data); void mm_shared_telit_modem_load_supported_bands (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data); GArray * mm_shared_telit_modem_load_supported_bands_finish (MMIfaceModem *self, GAsyncResult *res, GError **error); void mm_shared_telit_modem_load_current_bands (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data); GArray * mm_shared_telit_modem_load_current_bands_finish (MMIfaceModem *self, GAsyncResult *res, GError **error); gboolean mm_shared_telit_modem_set_current_bands_finish (MMIfaceModem *self, GAsyncResult *res, GError **error); void mm_shared_telit_modem_set_current_bands (MMIfaceModem *self, GArray *bands_array, GAsyncReadyCallback callback, gpointer user_data); void mm_shared_telit_modem_load_revision (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data); gchar * mm_shared_telit_modem_load_revision_finish (MMIfaceModem *self, GAsyncResult *res, GError **error); void mm_shared_telit_store_revision (MMSharedTelit *self, const gchar *revision); void mm_shared_telit_get_bnd_parse_config (MMIfaceModem *self, MMTelitBNDParseConfig *config); #endif /* MM_SHARED_TELIT_H */ ModemManager-1.23.4-dev/src/plugins/telit/mm-shared.c000066400000000000000000000013121456466623000223340ustar00rootroot00000000000000/* -*- 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) 2019 Aleksander Morgado */ #include "mm-shared-common.h" MM_DEFINE_SHARED (telit) ModemManager-1.23.4-dev/src/plugins/telit/tests/000077500000000000000000000000001456466623000214605ustar00rootroot00000000000000ModemManager-1.23.4-dev/src/plugins/telit/tests/test-mm-modem-helpers-telit.c000066400000000000000000000626701456466623000271030ustar00rootroot00000000000000/* -*- 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) 2015-2019 Telit * Copyright (C) 2019 Aleksander Morgado * */ #include #include #include #include #include #define _LIBMM_INSIDE_MM #include #include "mm-log-test.h" #include "mm-modem-helpers.h" #include "mm-modem-helpers-telit.h" #include "test-helpers.h" /******************************************************************************/ #define MAX_BANDS_LIST_LEN 17 typedef struct { const gchar *response; MMTelitBNDParseConfig config; guint mm_bands_len; MMModemBand mm_bands [MAX_BANDS_LIST_LEN]; } BndResponseTest; static BndResponseTest supported_band_mapping_tests [] = { { "#BND: (0-3)", {TRUE, FALSE, FALSE, FALSE, FALSE, FALSE}, 4, { MM_MODEM_BAND_EGSM, MM_MODEM_BAND_DCS, MM_MODEM_BAND_PCS, MM_MODEM_BAND_G850 } }, { "#BND: (0-3),(0,2,5,6)", {TRUE, TRUE, FALSE, FALSE, FALSE, FALSE}, 7, { MM_MODEM_BAND_EGSM, MM_MODEM_BAND_DCS, MM_MODEM_BAND_PCS, MM_MODEM_BAND_G850, MM_MODEM_BAND_UTRAN_1, MM_MODEM_BAND_UTRAN_5, MM_MODEM_BAND_UTRAN_8 } }, { "#BND: (0,3),(0,2,5,6)", {TRUE, TRUE, FALSE, FALSE, FALSE, FALSE}, 7, { MM_MODEM_BAND_EGSM, MM_MODEM_BAND_DCS, MM_MODEM_BAND_PCS, MM_MODEM_BAND_G850, MM_MODEM_BAND_UTRAN_1, MM_MODEM_BAND_UTRAN_5, MM_MODEM_BAND_UTRAN_8 } }, { "#BND: (0,2),(0,2,5,6)", {TRUE, TRUE, FALSE, FALSE, FALSE, FALSE}, 6, { MM_MODEM_BAND_EGSM, MM_MODEM_BAND_DCS, MM_MODEM_BAND_G850, MM_MODEM_BAND_UTRAN_1, MM_MODEM_BAND_UTRAN_5, MM_MODEM_BAND_UTRAN_8 } }, { "#BND: (0,2),(0-4,5,6)", {TRUE, TRUE, FALSE, FALSE, FALSE, FALSE}, 7, { MM_MODEM_BAND_EGSM, MM_MODEM_BAND_DCS, MM_MODEM_BAND_G850, MM_MODEM_BAND_UTRAN_1, MM_MODEM_BAND_UTRAN_2, MM_MODEM_BAND_UTRAN_5, MM_MODEM_BAND_UTRAN_8 } }, { "#BND: (0-3),(0,2,5,6),(1-1)", {TRUE, TRUE, TRUE, FALSE, FALSE, FALSE}, 8, { MM_MODEM_BAND_EGSM, MM_MODEM_BAND_DCS, MM_MODEM_BAND_PCS, MM_MODEM_BAND_G850, MM_MODEM_BAND_UTRAN_1, MM_MODEM_BAND_UTRAN_5, MM_MODEM_BAND_UTRAN_8, MM_MODEM_BAND_EUTRAN_1 } }, { "#BND: (0),(0),(1-3)", {TRUE, TRUE, TRUE, FALSE, FALSE, FALSE}, 5, { MM_MODEM_BAND_EGSM, MM_MODEM_BAND_DCS, MM_MODEM_BAND_UTRAN_1, MM_MODEM_BAND_EUTRAN_1, MM_MODEM_BAND_EUTRAN_2 } }, { "#BND: (0),(0),(1-3)", {FALSE, FALSE, TRUE, FALSE, FALSE, FALSE}, 2, { MM_MODEM_BAND_EUTRAN_1, MM_MODEM_BAND_EUTRAN_2 } }, /* 3G alternate band settings: default */ { "#BND: (0),(0,2,5,6,12,25)", {FALSE, TRUE, FALSE, FALSE, FALSE, FALSE}, 5, { MM_MODEM_BAND_UTRAN_1, MM_MODEM_BAND_UTRAN_5, MM_MODEM_BAND_UTRAN_8, MM_MODEM_BAND_UTRAN_6, MM_MODEM_BAND_UTRAN_19 } }, /* 3G alternate band settings: alternate */ { "#BND: (0),(0,2,5,6,12,13)", {FALSE, TRUE, FALSE, TRUE, FALSE, FALSE}, 4, { MM_MODEM_BAND_UTRAN_1, MM_MODEM_BAND_UTRAN_3, MM_MODEM_BAND_UTRAN_5, MM_MODEM_BAND_UTRAN_8 } }, /* ME910 (2G+4G device) * 168695967: 0xA0E189F: 0000 1010 0000 1110 0001 1000 1001 1111 */ { "#BND: (0-5),(0),(1-168695967)", {TRUE, FALSE, TRUE, FALSE, FALSE, FALSE}, 17, { MM_MODEM_BAND_EGSM, MM_MODEM_BAND_DCS, MM_MODEM_BAND_PCS, MM_MODEM_BAND_G850, MM_MODEM_BAND_EUTRAN_1, MM_MODEM_BAND_EUTRAN_2, MM_MODEM_BAND_EUTRAN_3, MM_MODEM_BAND_EUTRAN_4, MM_MODEM_BAND_EUTRAN_5, MM_MODEM_BAND_EUTRAN_8, MM_MODEM_BAND_EUTRAN_12, MM_MODEM_BAND_EUTRAN_13, MM_MODEM_BAND_EUTRAN_18, MM_MODEM_BAND_EUTRAN_19, MM_MODEM_BAND_EUTRAN_20, MM_MODEM_BAND_EUTRAN_26, MM_MODEM_BAND_EUTRAN_28 } }, /* 4G ext band settings: devices such as LN920 */ { "#BND: (0),(0),(1003100185A),(42)", {FALSE, TRUE, TRUE, FALSE, TRUE, TRUE}, 13, { MM_MODEM_BAND_UTRAN_1, MM_MODEM_BAND_EUTRAN_2, MM_MODEM_BAND_EUTRAN_4, MM_MODEM_BAND_EUTRAN_5, MM_MODEM_BAND_EUTRAN_7, MM_MODEM_BAND_EUTRAN_12, MM_MODEM_BAND_EUTRAN_13, MM_MODEM_BAND_EUTRAN_25, MM_MODEM_BAND_EUTRAN_29, MM_MODEM_BAND_EUTRAN_30, MM_MODEM_BAND_EUTRAN_41, MM_MODEM_BAND_EUTRAN_66, MM_MODEM_BAND_EUTRAN_71 } }, /* 4G band in hex format: devices such as LE910C1-EUX */ { "#BND: (0),(0,5,6,13,15,23),(80800C5)", {TRUE, TRUE, TRUE, FALSE, TRUE, FALSE}, 11, { MM_MODEM_BAND_EGSM, MM_MODEM_BAND_DCS, MM_MODEM_BAND_UTRAN_1, MM_MODEM_BAND_UTRAN_3, MM_MODEM_BAND_UTRAN_8, MM_MODEM_BAND_EUTRAN_1, MM_MODEM_BAND_EUTRAN_3, MM_MODEM_BAND_EUTRAN_7, MM_MODEM_BAND_EUTRAN_8, MM_MODEM_BAND_EUTRAN_20, MM_MODEM_BAND_EUTRAN_28 } } }; static void test_parse_supported_bands_response (void) { guint i; for (i = 0; i < G_N_ELEMENTS (supported_band_mapping_tests); i++) { GError *error = NULL; GArray *bands = NULL; bands = mm_telit_parse_bnd_test_response (supported_band_mapping_tests[i].response, &supported_band_mapping_tests[i].config, NULL, &error); g_assert_no_error (error); g_assert (bands); mm_test_helpers_compare_bands (bands, supported_band_mapping_tests[i].mm_bands, supported_band_mapping_tests[i].mm_bands_len); g_array_unref (bands); } } static BndResponseTest current_band_mapping_tests [] = { { "#BND: 0", {TRUE, FALSE, FALSE, FALSE, FALSE, FALSE}, 2, { MM_MODEM_BAND_EGSM, MM_MODEM_BAND_DCS } }, { "#BND: 0,5", {TRUE, TRUE, FALSE, FALSE, FALSE, FALSE}, 3, { MM_MODEM_BAND_EGSM, MM_MODEM_BAND_DCS, MM_MODEM_BAND_UTRAN_8 } }, { "#BND: 1,3", {TRUE, TRUE, FALSE, FALSE, FALSE, FALSE}, 5, { MM_MODEM_BAND_EGSM, MM_MODEM_BAND_PCS, MM_MODEM_BAND_UTRAN_1, MM_MODEM_BAND_UTRAN_2, MM_MODEM_BAND_UTRAN_5 } }, { "#BND: 2,7", {TRUE, TRUE, FALSE, FALSE, FALSE, FALSE}, 3, { MM_MODEM_BAND_DCS, MM_MODEM_BAND_G850, MM_MODEM_BAND_UTRAN_4 } }, { "#BND: 3,0,1", {TRUE, TRUE, TRUE, FALSE, FALSE, FALSE}, 4, { MM_MODEM_BAND_PCS, MM_MODEM_BAND_G850, MM_MODEM_BAND_UTRAN_1, MM_MODEM_BAND_EUTRAN_1 } }, { "#BND: 0,0,3", {TRUE, FALSE, TRUE, FALSE, FALSE, FALSE}, 4, { MM_MODEM_BAND_EGSM, MM_MODEM_BAND_DCS, MM_MODEM_BAND_EUTRAN_1, MM_MODEM_BAND_EUTRAN_2 } }, { "#BND: 0,0,3", {FALSE, FALSE, TRUE, FALSE, FALSE, FALSE}, 2, { MM_MODEM_BAND_EUTRAN_1, MM_MODEM_BAND_EUTRAN_2 } }, /* 3G alternate band settings: default */ { "#BND: 0,12", {FALSE, TRUE, FALSE, FALSE, FALSE, FALSE}, 1, { MM_MODEM_BAND_UTRAN_6 } }, /* 3G alternate band settings: alternate */ { "#BND: 0,12", {FALSE, TRUE, FALSE, TRUE, FALSE, FALSE}, 4, { MM_MODEM_BAND_UTRAN_1, MM_MODEM_BAND_UTRAN_3, MM_MODEM_BAND_UTRAN_5, MM_MODEM_BAND_UTRAN_8 } }, /* ME910 (2G+4G device) * 168695967: 0xA0E189F: 0000 1010 0000 1110 0001 1000 1001 1111 */ { "#BND: 5,0,168695967", {TRUE, FALSE, TRUE, FALSE, FALSE, FALSE}, 17, { MM_MODEM_BAND_EGSM, MM_MODEM_BAND_DCS, MM_MODEM_BAND_PCS, MM_MODEM_BAND_G850, MM_MODEM_BAND_EUTRAN_1, MM_MODEM_BAND_EUTRAN_2, MM_MODEM_BAND_EUTRAN_3, MM_MODEM_BAND_EUTRAN_4, MM_MODEM_BAND_EUTRAN_5, MM_MODEM_BAND_EUTRAN_8, MM_MODEM_BAND_EUTRAN_12, MM_MODEM_BAND_EUTRAN_13, MM_MODEM_BAND_EUTRAN_18, MM_MODEM_BAND_EUTRAN_19, MM_MODEM_BAND_EUTRAN_20, MM_MODEM_BAND_EUTRAN_26, MM_MODEM_BAND_EUTRAN_28 } }, /* 4G ext band settings: devices such as LN920 */ { "#BND: 0,0,1003100185A,42", {FALSE, TRUE, TRUE, FALSE, FALSE, TRUE}, 13, { MM_MODEM_BAND_UTRAN_1, MM_MODEM_BAND_EUTRAN_2, MM_MODEM_BAND_EUTRAN_4, MM_MODEM_BAND_EUTRAN_5, MM_MODEM_BAND_EUTRAN_7, MM_MODEM_BAND_EUTRAN_12, MM_MODEM_BAND_EUTRAN_13, MM_MODEM_BAND_EUTRAN_25, MM_MODEM_BAND_EUTRAN_29, MM_MODEM_BAND_EUTRAN_30, MM_MODEM_BAND_EUTRAN_41, MM_MODEM_BAND_EUTRAN_66, MM_MODEM_BAND_EUTRAN_71 } } }; static void test_parse_current_bands_response (void) { guint i; for (i = 0; i < G_N_ELEMENTS (current_band_mapping_tests); i++) { GError *error = NULL; GArray *bands = NULL; bands = mm_telit_parse_bnd_query_response (current_band_mapping_tests[i].response, ¤t_band_mapping_tests[i].config, NULL, &error); g_assert_no_error (error); g_assert (bands); mm_test_helpers_compare_bands (bands, current_band_mapping_tests[i].mm_bands, current_band_mapping_tests[i].mm_bands_len); g_array_unref (bands); } } /******************************************************************************/ static void test_common_bnd_cmd (const gchar *expected_cmd, gboolean modem_is_2g, gboolean modem_is_3g, gboolean modem_is_4g, gboolean modem_alternate_3g_bands, gboolean modem_ext_4g_bands, GArray *bands_array) { gchar *cmd; GError *error = NULL; MMTelitBNDParseConfig config = { .modem_is_2g = modem_is_2g, .modem_is_3g = modem_is_3g, .modem_is_4g = modem_is_4g, .modem_alternate_3g_bands = modem_alternate_3g_bands, .modem_ext_4g_bands = modem_ext_4g_bands }; cmd = mm_telit_build_bnd_request (bands_array, &config, &error); g_assert_no_error (error); g_assert_cmpstr (cmd, ==, expected_cmd); g_free (cmd); } #define test_common_bnd_cmd_2g(EXPECTED_CMD, BANDS_ARRAY) test_common_bnd_cmd (EXPECTED_CMD, TRUE, FALSE, FALSE, FALSE, FALSE, BANDS_ARRAY) #define test_common_bnd_cmd_3g(EXPECTED_CMD, ALTERNATE, BANDS_ARRAY) test_common_bnd_cmd (EXPECTED_CMD, FALSE, TRUE, FALSE, ALTERNATE, FALSE, BANDS_ARRAY) #define test_common_bnd_cmd_4g(EXPECTED_CMD, EXTENDED, BANDS_ARRAY) test_common_bnd_cmd (EXPECTED_CMD, FALSE, FALSE, TRUE, FALSE, EXTENDED, BANDS_ARRAY) static void test_common_bnd_cmd_error (gboolean modem_is_2g, gboolean modem_is_3g, gboolean modem_is_4g, GArray *bands_array, MMCoreError expected_error) { gchar *cmd; GError *error = NULL; MMTelitBNDParseConfig config = { .modem_is_2g = modem_is_2g, .modem_is_3g = modem_is_3g, .modem_is_4g = modem_is_4g, .modem_alternate_3g_bands = FALSE, .modem_ext_4g_bands = FALSE, }; cmd = mm_telit_build_bnd_request (bands_array, &config, &error); g_assert_error (error, MM_CORE_ERROR, (gint)expected_error); g_assert (!cmd); } #define test_common_bnd_cmd_2g_invalid(BANDS_ARRAY) test_common_bnd_cmd_error (TRUE, FALSE, FALSE, BANDS_ARRAY, MM_CORE_ERROR_FAILED) #define test_common_bnd_cmd_3g_invalid(BANDS_ARRAY) test_common_bnd_cmd_error (FALSE, TRUE, FALSE, BANDS_ARRAY, MM_CORE_ERROR_FAILED) #define test_common_bnd_cmd_4g_invalid(BANDS_ARRAY) test_common_bnd_cmd_error (FALSE, FALSE, TRUE, BANDS_ARRAY, MM_CORE_ERROR_FAILED) #define test_common_bnd_cmd_2g_not_found(BANDS_ARRAY) test_common_bnd_cmd_error (TRUE, FALSE, FALSE, BANDS_ARRAY, MM_CORE_ERROR_NOT_FOUND) #define test_common_bnd_cmd_3g_not_found(BANDS_ARRAY) test_common_bnd_cmd_error (FALSE, TRUE, FALSE, BANDS_ARRAY, MM_CORE_ERROR_NOT_FOUND) #define test_common_bnd_cmd_4g_not_found(BANDS_ARRAY) test_common_bnd_cmd_error (FALSE, FALSE, TRUE, BANDS_ARRAY, MM_CORE_ERROR_NOT_FOUND) static void test_telit_get_2g_bnd_flag (void) { GArray *bands_array; MMModemBand egsm = MM_MODEM_BAND_EGSM; MMModemBand dcs = MM_MODEM_BAND_DCS; MMModemBand pcs = MM_MODEM_BAND_PCS; MMModemBand g850 = MM_MODEM_BAND_G850; MMModemBand u2100 = MM_MODEM_BAND_UTRAN_1; MMModemBand eutran_i = MM_MODEM_BAND_EUTRAN_1; /* Test Flag 0 */ bands_array = g_array_sized_new (FALSE, FALSE, sizeof (MMModemBand), 2); g_array_append_val (bands_array, egsm); g_array_append_val (bands_array, dcs); test_common_bnd_cmd_2g ("#BND=0", bands_array); g_array_unref (bands_array); /* Test flag 1 */ bands_array = g_array_sized_new (FALSE, FALSE, sizeof (MMModemBand), 2); g_array_append_val (bands_array, egsm); g_array_append_val (bands_array, pcs); test_common_bnd_cmd_2g ("#BND=1", bands_array); g_array_unref (bands_array); /* Test flag 2 */ bands_array = g_array_sized_new (FALSE, FALSE, sizeof (MMModemBand), 2); g_array_append_val (bands_array, g850); g_array_append_val (bands_array, dcs); test_common_bnd_cmd_2g ("#BND=2", bands_array); g_array_unref (bands_array); /* Test flag 3 */ bands_array = g_array_sized_new (FALSE, FALSE, sizeof (MMModemBand), 2); g_array_append_val (bands_array, g850); g_array_append_val (bands_array, pcs); test_common_bnd_cmd_2g ("#BND=3", bands_array); g_array_unref (bands_array); /* Test invalid band array */ bands_array = g_array_sized_new (FALSE, FALSE, sizeof (MMModemBand), 2); g_array_append_val (bands_array, g850); g_array_append_val (bands_array, egsm); test_common_bnd_cmd_2g_invalid (bands_array); g_array_unref (bands_array); /* Test unmatched band array */ bands_array = g_array_sized_new (FALSE, FALSE, sizeof (MMModemBand), 2); g_array_append_val (bands_array, u2100); g_array_append_val (bands_array, eutran_i); test_common_bnd_cmd_2g_not_found (bands_array); g_array_unref (bands_array); } static void test_telit_get_3g_bnd_flag (void) { GArray *bands_array; MMModemBand u2100 = MM_MODEM_BAND_UTRAN_1; MMModemBand u1900 = MM_MODEM_BAND_UTRAN_2; MMModemBand u1800 = MM_MODEM_BAND_UTRAN_3; MMModemBand u850 = MM_MODEM_BAND_UTRAN_5; MMModemBand u800 = MM_MODEM_BAND_UTRAN_6; MMModemBand u900 = MM_MODEM_BAND_UTRAN_8; MMModemBand u17iv = MM_MODEM_BAND_UTRAN_4; MMModemBand u17ix = MM_MODEM_BAND_UTRAN_9; MMModemBand egsm = MM_MODEM_BAND_EGSM; MMModemBand eutran_i = MM_MODEM_BAND_EUTRAN_1; /* Test flag 0 */ bands_array = g_array_sized_new (FALSE, FALSE, sizeof (MMModemBand), 1); g_array_append_val (bands_array, u2100); test_common_bnd_cmd_3g ("#BND=0,0", FALSE, bands_array); test_common_bnd_cmd_3g ("#BND=0,0", TRUE, bands_array); g_array_unref (bands_array); /* Test flag 1 */ bands_array = g_array_sized_new (FALSE, FALSE, sizeof (MMModemBand), 1); g_array_append_val (bands_array, u1900); test_common_bnd_cmd_3g ("#BND=0,1", FALSE, bands_array); test_common_bnd_cmd_3g ("#BND=0,1", TRUE, bands_array); g_array_unref (bands_array); /* Test flag 2 */ bands_array = g_array_sized_new (FALSE, FALSE, sizeof (MMModemBand), 1); g_array_append_val (bands_array, u850); test_common_bnd_cmd_3g ("#BND=0,2", FALSE, bands_array); test_common_bnd_cmd_3g ("#BND=0,2", TRUE, bands_array); g_array_unref (bands_array); /* Test flag 3 */ bands_array = g_array_sized_new (FALSE, FALSE, sizeof (MMModemBand), 3); g_array_append_val (bands_array, u2100); g_array_append_val (bands_array, u1900); g_array_append_val (bands_array, u850); test_common_bnd_cmd_3g ("#BND=0,3", FALSE, bands_array); test_common_bnd_cmd_3g ("#BND=0,3", TRUE, bands_array); g_array_unref (bands_array); /* Test flag 4 */ bands_array = g_array_sized_new (FALSE, FALSE, sizeof (MMModemBand), 2); g_array_append_val (bands_array, u1900); g_array_append_val (bands_array, u850); test_common_bnd_cmd_3g ("#BND=0,4", FALSE, bands_array); test_common_bnd_cmd_3g ("#BND=0,4", TRUE, bands_array); g_array_unref (bands_array); /* Test flag 5 */ bands_array = g_array_sized_new (FALSE, FALSE, sizeof (MMModemBand), 1); g_array_append_val (bands_array, u900); test_common_bnd_cmd_3g ("#BND=0,5", FALSE, bands_array); test_common_bnd_cmd_3g ("#BND=0,5", TRUE, bands_array); g_array_unref (bands_array); /* Test flag 6 */ bands_array = g_array_sized_new (FALSE, FALSE, sizeof (MMModemBand), 2); g_array_append_val (bands_array, u2100); g_array_append_val (bands_array, u900); test_common_bnd_cmd_3g ("#BND=0,6", FALSE, bands_array); test_common_bnd_cmd_3g ("#BND=0,6", TRUE, bands_array); g_array_unref (bands_array); /* Test flag 7 */ bands_array = g_array_sized_new (FALSE, FALSE, sizeof (MMModemBand), 1); g_array_append_val (bands_array, u17iv); test_common_bnd_cmd_3g ("#BND=0,7", FALSE, bands_array); test_common_bnd_cmd_3g ("#BND=0,7", TRUE, bands_array); g_array_unref (bands_array); /* Test flag 12 in default */ bands_array = g_array_sized_new (FALSE, FALSE, sizeof (MMModemBand), 1); g_array_append_val (bands_array, u800); test_common_bnd_cmd_3g ("#BND=0,12", FALSE, bands_array); g_array_unref (bands_array); /* Test flag 12 in alternate */ bands_array = g_array_sized_new (FALSE, FALSE, sizeof (MMModemBand), 4); g_array_append_val (bands_array, u2100); g_array_append_val (bands_array, u1800); g_array_append_val (bands_array, u850); g_array_append_val (bands_array, u900); test_common_bnd_cmd_3g ("#BND=0,12", TRUE, bands_array); g_array_unref (bands_array); /* Test invalid band array */ bands_array = g_array_sized_new (FALSE, FALSE, sizeof (MMModemBand), 1); g_array_append_val (bands_array, u17ix); test_common_bnd_cmd_3g_invalid (bands_array); g_array_unref (bands_array); /* Test unmatched band array */ bands_array = g_array_sized_new (FALSE, FALSE, sizeof (MMModemBand), 2); g_array_append_val (bands_array, egsm); g_array_append_val (bands_array, eutran_i); test_common_bnd_cmd_3g_not_found (bands_array); g_array_unref (bands_array); } static void test_telit_get_4g_bnd_flag (void) { GArray *bands_array; MMModemBand eutran_i = MM_MODEM_BAND_EUTRAN_1; MMModemBand eutran_ii = MM_MODEM_BAND_EUTRAN_2; MMModemBand u2100 = MM_MODEM_BAND_UTRAN_1; MMModemBand egsm = MM_MODEM_BAND_EGSM; /* Test flag 1 */ bands_array = g_array_sized_new (FALSE, FALSE, sizeof (MMModemBand), 1); g_array_append_val (bands_array, eutran_i); test_common_bnd_cmd_4g ("#BND=0,0,1", FALSE, bands_array); g_array_unref (bands_array); /* Test flag 3 */ bands_array = g_array_sized_new (FALSE, FALSE, sizeof (MMModemBand), 2); g_array_append_val (bands_array, eutran_i); g_array_append_val (bands_array, eutran_ii); test_common_bnd_cmd_4g ("#BND=0,0,3", FALSE, bands_array); g_array_unref (bands_array); /* Test unmatched band array */ bands_array = g_array_sized_new (FALSE, FALSE, sizeof (MMModemBand), 2); g_array_append_val (bands_array, egsm); g_array_append_val (bands_array, u2100); test_common_bnd_cmd_4g_not_found (bands_array); g_array_unref (bands_array); } /******************************************************************************/ typedef struct { const char* response; MMTelitQssStatus expected_qss; const char *error_message; } QssParseTest; static QssParseTest qss_parse_tests [] = { {"#QSS: 0,0", QSS_STATUS_SIM_REMOVED, NULL}, {"#QSS: 1,0", QSS_STATUS_SIM_REMOVED, NULL}, {"#QSS: 0,1", QSS_STATUS_SIM_INSERTED, NULL}, {"#QSS: 0,2", QSS_STATUS_SIM_INSERTED_AND_UNLOCKED, NULL}, {"#QSS: 0,3", QSS_STATUS_SIM_INSERTED_AND_READY, NULL}, {"#QSS:0,3", QSS_STATUS_SIM_INSERTED_AND_READY, NULL}, {"#QSS: 0, 3", QSS_STATUS_SIM_INSERTED_AND_READY, NULL}, {"#QSS: 0", QSS_STATUS_UNKNOWN, "Could not parse \"#QSS?\" response: #QSS: 0"}, {"QSS:0,1", QSS_STATUS_UNKNOWN, "Could not parse \"#QSS?\" response: QSS:0,1"}, {"#QSS: 0,5", QSS_STATUS_UNKNOWN, "Unknown QSS status value given: 5"}, }; static void test_telit_parse_qss_query (void) { MMTelitQssStatus actual_qss_status; GError *error = NULL; guint i; for (i = 0; i < G_N_ELEMENTS (qss_parse_tests); i++) { actual_qss_status = mm_telit_parse_qss_query (qss_parse_tests[i].response, &error); g_assert_cmpint (actual_qss_status, ==, qss_parse_tests[i].expected_qss); if (qss_parse_tests[i].error_message) { g_assert_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED); g_assert_cmpstr (error->message, ==, qss_parse_tests[i].error_message); g_clear_error (&error); } } } static void test_telit_parse_swpkgv_response (void) { static struct { const gchar *response; const gchar *expected; } tt [] = { {"\r\n12.34.567\r\nM0F.223004-B001\r\nP0F.224700\r\nA0F.223004-B001\r\n\r\nOK\r\n", "12.34.567"}, {"\r\n13.35.568-A123\r\nM0F.223004-B001\r\nP0F.224700\r\nA0F.223004-B001\r\n\r\nOK\r\n", "13.35.568-A123"}, {"\r\n14.36.569-B124\r\nM0F.223004-B001\r\nP0F.224700\r\nA0F.223004-B001\r\n\r\nOK\r\n", "14.36.569-B124"}, {"\r\n15.37.570-T125\r\nM0F.223004-B001\r\nP0F.224700\r\nA0F.223004-B001\r\n\r\nOK\r\n", "15.37.570-T125"}, {"\r\n16.38.571-P0F.224700\r\nM0F.223004-B001\r\nP0F.224700\r\nA0F.223004-B001\r\n\r\nOK\r\n", "16.38.571-P0F.224700"}, /* real example from LE910C1-EUX */ {"\r\n25.30.224-B001-P0F.224700\r\nM0F.223004-B001\r\nP0F.224700\r\nA0F.223004-B001\r\n\r\nOK\r\n", "25.30.224-B001-P0F.224700"}, {"\r\n45.00.010-B022-P0R.001600\r\nM0R.010000-B022\r\nP0R.001600\r\nA0R.000000-B022\r\n\r\nOK\r\n", "45.00.010-B022-P0R.001600"}, }; guint i; for (i = 0; i < G_N_ELEMENTS (tt); i++) { gchar *actual = NULL; actual = mm_telit_parse_swpkgv_response(tt[i].response); g_assert_cmpstr (tt[i].expected, ==, actual); g_free (actual); } } static void test_telit_compare_software_revision_string (void) { struct { const char *revision_a; const char *revision_b; MMTelitSwRevCmp expected; } tt [] = { {"24.01.514", "24.01.514", MM_TELIT_SW_REV_CMP_EQUAL}, {"24.01.514", "24.01.513", MM_TELIT_SW_REV_CMP_NEWER}, {"24.01.513", "24.01.514", MM_TELIT_SW_REV_CMP_OLDER}, {"32.00.013", "24.01.514", MM_TELIT_SW_REV_CMP_INVALID}, {"32.00.014", "32.00.014", MM_TELIT_SW_REV_CMP_EQUAL}, {"32.00.014", "32.00.013", MM_TELIT_SW_REV_CMP_NEWER}, {"32.00.013", "32.00.014", MM_TELIT_SW_REV_CMP_OLDER}, {"38.00.000", "38.00.000", MM_TELIT_SW_REV_CMP_UNSUPPORTED}, /* LM9x0 Minor version (e.g. beta, test, alpha) value is currently * ignored because not required by any implemented feature. */ {"24.01.516-B123", "24.01.516-B134", MM_TELIT_SW_REV_CMP_EQUAL}, }; guint i; for (i = 0; i < G_N_ELEMENTS (tt); i++) { g_assert_cmpint (tt[i].expected, ==, mm_telit_software_revision_cmp (tt[i].revision_a, tt[i].revision_b)); } } /******************************************************************************/ int main (int argc, char **argv) { setlocale (LC_ALL, ""); g_test_init (&argc, &argv, NULL); g_test_add_func ("/MM/telit/bands/supported/parse_bands_response", test_parse_supported_bands_response); g_test_add_func ("/MM/telit/bands/current/parse_bands_response", test_parse_current_bands_response); g_test_add_func ("/MM/telit/bands/current/set_bands/2g", test_telit_get_2g_bnd_flag); g_test_add_func ("/MM/telit/bands/current/set_bands/3g", test_telit_get_3g_bnd_flag); g_test_add_func ("/MM/telit/bands/current/set_bands/4g", test_telit_get_4g_bnd_flag); g_test_add_func ("/MM/telit/qss/query", test_telit_parse_qss_query); g_test_add_func ("/MM/telit/swpkv/parse_response", test_telit_parse_swpkgv_response); g_test_add_func ("/MM/telit/revision/compare", test_telit_compare_software_revision_string); return g_test_run (); } ModemManager-1.23.4-dev/src/plugins/tests/000077500000000000000000000000001456466623000203375ustar00rootroot00000000000000ModemManager-1.23.4-dev/src/plugins/tests/gsm-port.conf000066400000000000000000000041601456466623000227570ustar00rootroot00000000000000 AT \r\nOK\r\n ATE0 \r\nOK\r\n ATV1 \r\nOK\r\n AT+CMEE=1 \r\nOK\r\n ATX4 \r\nOK\r\n AT&C1 \r\nOK\r\n AT+IFC=1,1 \r\nOK\r\n AT+GCAP \r\n+GCAP: +CGSM +DS +ES\r\n\r\nOK\r\n ATI \r\nManufacturer: Some vendor\r\nModel: Some model\r\nRevision: Some revision\r\nIMEI: 001100110011002+GCAP: +CGSM,+DS,+ES\r\n\r\nOK\r\n AT+WS46=? \r\n+WS46: (12,22)\r\n\r\nOK\r\n AT+CGMI \r\nSome vendor\r\n\r\nOK\r\n AT+CGMM \r\nSome model\r\n\r\nOK\r\n AT+CGMR \r\nSome revision\r\n\r\nOK\r\n AT+CGSN \r\n123456789012345\r\n\r\nOK\r\n AT+CGDCONT=? \r\n+CGDCONT: (1-11),"IP",,,(0-2),(0-3)\r\n+CGDCONT: (1-11),"IPV6",,,(0-2),(0-3)\r\n+CGDCONT: (1-11),"IPV4V6",,,(0-2),(0-3)\r\n+CGDCONT: (1-11),"PPP",,,(0-2),(0-3)\r\n\r\nOK\r\n AT+CIMI \r\n998899889988997\r\n\r\nOK\r\n AT+CLCK=? \r\n+CLCK: ("SC","AO","OI","OX","AI","IR","AB","AG","AC","PS","FD")\r\n\r\nOK\r\n AT+CLCK="SC",2 \r\n+CLCK: 1\r\n\r\nOK\r\n AT+CLCK="FD",2 \r\n+CLCK: 1\r\n\r\nOK\r\n AT+CLCK="PS",2 \r\n+CLCK: 1\r\n\r\nOK\r\n AT+CFUN? \r\n+CFUN: 1\r\n\r\nOK\r\n AT+CSCS=? \r\n+CSCS: ("IRA","UCS2","GSM")\r\n\r\nOK\r\n AT+CSCS="UCS2" \r\nOK\r\n AT+CSCS? \r\n+CSCS: "UCS2"\r\n\r\nOK\r\n AT+CREG=2 \r\nOK\r\n AT+CGREG=2 \r\nOK\r\n AT+CREG=0 \r\nOK\r\n AT+CGREG=0 \r\nOK\r\n AT+CREG? \r\n+CREG: 2,1,"1234","001122BB"\r\n\r\nOK\r\n AT+CGREG? \r\n+CGREG: 2,1,"31C5","0083F7CD"\r\n\r\nOK\r\n AT+COPS=3,2;+COPS? \r\n+COPS: 0,2,"21401",2\r\n\r\nOK\r\n AT+COPS=3,0;+COPS? \r\n+COPS: 0,0,"vodafone ES"\r\n\r\nOK\r\n AT+CMGF=? \r\n+CMGF: (0,1)\r\n\r\nOK\r\n AT+CMGF=0 \r\nOK\r\n AT+CSQ \r\n+CSQ: 17,99\r\n\r\nOK\r\n # By default, no PIN required AT+CPIN? \r\n+CPIN: READY\r\n\r\nOK\r\n # By default, no messaging support AT+CNMI=? \r\nERROR\r\n # By default, no USSD support AT+CUSD=? \r\nERROR\r\n ModemManager-1.23.4-dev/src/plugins/tests/test-fixture.c000066400000000000000000000133101456466623000231440ustar00rootroot00000000000000/* -*- 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) 2013 Aleksander Morgado */ #include "test-fixture.h" void test_fixture_setup (TestFixture *fixture) { GError *error = NULL; GVariant *result; /* Create the global dbus-daemon for this test suite */ fixture->dbus = g_test_dbus_new (G_TEST_DBUS_NONE); /* Add the private directory with our in-tree service files, * TEST_SERVICES is defined by the build system to point * to the right directory. */ g_test_dbus_add_service_dir (fixture->dbus, TEST_SERVICES); /* Start the private DBus daemon */ g_test_dbus_up (fixture->dbus); /* Create DBus connection */ fixture->connection = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, &error); if (fixture->connection == NULL) g_error ("Error getting connection to test bus: %s", error->message); /* Ping to autostart MM; wait up to 30s */ result = g_dbus_connection_call_sync (fixture->connection, "org.freedesktop.ModemManager1", "/org/freedesktop/ModemManager1", "org.freedesktop.DBus.Peer", "Ping", NULL, /* inputs */ NULL, /* outputs */ G_DBUS_CALL_FLAGS_NONE, 30000, /* timeout, ms */ NULL, /* cancellable */ &error); if (!result) g_error ("Error starting ModemManager in test bus: %s", error->message); g_variant_unref (result); /* Create the proxy that we're going to test */ fixture->test = mm_gdbus_test_proxy_new_sync (fixture->connection, G_DBUS_PROXY_FLAGS_NONE, "org.freedesktop.ModemManager1", "/org/freedesktop/ModemManager1", NULL, /* cancellable */ &error); if (fixture->test == NULL) g_error ("Error getting ModemManager test proxy: %s", error->message); } void test_fixture_teardown (TestFixture *fixture) { g_object_unref (fixture->connection); /* Tear down the proxy */ if (fixture->test) g_object_unref (fixture->test); /* Stop the private D-Bus daemon; stopping the bus will stop MM as well */ g_test_dbus_down (fixture->dbus); g_object_unref (fixture->dbus); } void test_fixture_set_profile (TestFixture *fixture, const gchar *profile_name, const gchar *plugin, const gchar *const *ports) { GError *error = NULL; /* Set the test profile */ g_assert (fixture->test != NULL); if (!mm_gdbus_test_call_set_profile_sync (fixture->test, profile_name, plugin, ports, NULL, /* cancellable */ &error)) g_error ("Error setting test profile: %s", error->message); } static MMObject * common_get_modem (TestFixture *fixture, gboolean modem_expected) { MMObject *found = NULL; guint wait_time = 0; /* Find new modem object */ while (TRUE) { GError *error = NULL; MMManager *manager; GList *modems; guint n_modems; gboolean ready = FALSE; /* Create manager on each loop, so that we don't require on an external * global main context processing to receive the DBus property updates. */ g_assert (fixture->connection != NULL); manager = mm_manager_new_sync (fixture->connection, G_DBUS_OBJECT_MANAGER_CLIENT_FLAGS_NONE, NULL, /* cancellable */ &error); if (!manager) g_error ("Couldn't create manager: %s", error->message); modems = g_dbus_object_manager_get_objects (G_DBUS_OBJECT_MANAGER (manager)); n_modems = g_list_length (modems); g_assert_cmpuint (n_modems, <=, 1); if ((guint)modem_expected == n_modems) { if (modems) { found = MM_OBJECT (g_object_ref (modems->data)); g_message ("Found modem at '%s'", mm_object_get_path (found)); } ready = TRUE; } g_list_free_full (modems, g_object_unref); g_object_unref (manager); if (ready) break; /* Blocking wait */ g_assert_cmpuint (wait_time, <=, 20); wait_time++; sleep (1); } return found; } MMObject * test_fixture_get_modem (TestFixture *fixture) { return common_get_modem (fixture, TRUE); } void test_fixture_no_modem (TestFixture *fixture) { common_get_modem (fixture, FALSE); } ModemManager-1.23.4-dev/src/plugins/tests/test-fixture.h000066400000000000000000000037411456466623000231600ustar00rootroot00000000000000/* -*- 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) 2013 Aleksander Morgado */ #ifndef TEST_FIXTURE_H #define TEST_FIXTURE_H #include #include #include "mm-gdbus-test.h" /*****************************************************************************/ /* Test fixture setup */ /* The fixture contains a GTestDBus object and * a proxy to the service we're going to be testing. */ typedef struct { GTestDBus *dbus; MmGdbusTest *test; GDBusConnection *connection; } TestFixture; void test_fixture_setup (TestFixture *fixture); void test_fixture_teardown (TestFixture *fixture); typedef void (*TCFunc) (TestFixture *, gconstpointer); #define TEST_ADD(path,method) \ g_test_add (path, \ TestFixture, \ NULL, \ (TCFunc)test_fixture_setup, \ (TCFunc)method, \ (TCFunc)test_fixture_teardown) void test_fixture_set_profile (TestFixture *fixture, const gchar *profile_name, const gchar *plugin, const gchar *const *ports); MMObject *test_fixture_get_modem (TestFixture *fixture); void test_fixture_no_modem (TestFixture *fixture); #endif /* TEST_FIXTURE_H */ ModemManager-1.23.4-dev/src/plugins/tests/test-helpers.c000066400000000000000000000035511456466623000231260ustar00rootroot00000000000000/* -*- 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) 2019 Aleksander Morgado */ #include #define _LIBMM_INSIDE_MM #include #include "mm-log.h" #include "mm-modem-helpers.h" #include "test-helpers.h" void mm_test_helpers_compare_bands (GArray *bands, const MMModemBand *expected_bands, guint n_expected_bands) { gchar *bands_str; GArray *expected_bands_array; gchar *expected_bands_str; if (!expected_bands || !n_expected_bands) { g_assert (!bands); return; } g_assert (bands); mm_common_bands_garray_sort (bands); bands_str = mm_common_build_bands_string ((MMModemBand *)(gpointer)(bands->data), bands->len); expected_bands_array = g_array_sized_new (FALSE, FALSE, sizeof (MMModemBand), n_expected_bands); g_array_append_vals (expected_bands_array, expected_bands, n_expected_bands); mm_common_bands_garray_sort (expected_bands_array); expected_bands_str = mm_common_build_bands_string ((MMModemBand *)(gpointer)(expected_bands_array->data), expected_bands_array->len); g_array_unref (expected_bands_array); g_assert_cmpstr (bands_str, ==, expected_bands_str); g_free (bands_str); g_free (expected_bands_str); } ModemManager-1.23.4-dev/src/plugins/tests/test-helpers.h000066400000000000000000000017301456466623000231300ustar00rootroot00000000000000/* -*- 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) 2019 Aleksander Morgado */ #ifndef TEST_HELPERS_H #define TEST_HELPERS_H #include #include void mm_test_helpers_compare_bands (GArray *bands, const MMModemBand *expected_bands, guint n_expected_bands); #endif /* TEST_HELPERS_H */ ModemManager-1.23.4-dev/src/plugins/tests/test-keyfiles.c000066400000000000000000000040561456466623000233000ustar00rootroot00000000000000/* -*- 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) 2018 Aleksander Morgado */ #include #include #include #include #include #include #define _LIBMM_INSIDE_MM #include #include "mm-log-test.h" /************************************************************/ static void common_test (const gchar *keyfile_path) { GKeyFile *keyfile; GError *error = NULL; gboolean ret; if (!keyfile_path) return; keyfile = g_key_file_new (); ret = g_key_file_load_from_file (keyfile, keyfile_path, G_KEY_FILE_NONE, &error); g_assert_no_error (error); g_assert (ret); g_key_file_unref (keyfile); } /* Placeholder test to avoid compiler warning about common_test() being unused * when none of the plugins enabled in build have custom key files. */ static void test_placeholder (void) { common_test (NULL); } /************************************************************/ #if defined ENABLE_PLUGIN_FOXCONN static void test_foxconn_t77w968 (void) { common_test (TESTKEYFILE_FOXCONN_T77W968); } #endif /************************************************************/ int main (int argc, char **argv) { setlocale (LC_ALL, ""); g_test_init (&argc, &argv, NULL); g_test_add_func ("/MM/test-keyfiles/placeholder", test_placeholder); #if defined ENABLE_PLUGIN_FOXCONN g_test_add_func ("/MM/test-keyfiles/foxconn/t77w968", test_foxconn_t77w968); #endif return g_test_run (); } ModemManager-1.23.4-dev/src/plugins/tests/test-port-context.c000066400000000000000000000305611456466623000241330ustar00rootroot00000000000000/* -*- 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) 2013 Aleksander Morgado */ #include #include #include #include "test-port-context.h" #define BUFFER_SIZE 1024 struct _TestPortContext { gchar *name; GThread *thread; gboolean ready; GCond ready_cond; GMutex ready_mutex; GMainLoop *loop; GMainContext *context; GSocket *socket; GSocketService *socket_service; GList *clients; GHashTable *commands; }; /*****************************************************************************/ void test_port_context_set_command (TestPortContext *self, const gchar *command, const gchar *response) { if (G_UNLIKELY (!self->commands)) self->commands = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); g_hash_table_replace (self->commands, g_strdup (command), g_strcompress (response)); } void test_port_context_load_commands (TestPortContext *self, const gchar *file) { GError *error = NULL; gchar *contents; gchar *current; if (!g_file_get_contents (file, &contents, NULL, &error)) g_error ("Couldn't load commands file '%s': %s", g_filename_display_name (file), error->message); current = contents; while (current) { gchar *next; next = strchr (current, '\n'); if (next) { *next = '\0'; next++; } g_strstrip (current); if (current[0] != '\0' && current[0] != '#') { gchar *response; response = current; while (*response != ' ') response++; g_assert (*response == ' '); *response = '\0'; response++; while (*response == ' ') response++; g_assert (*response != '\0'); test_port_context_set_command (self, current, response); } current = next; } g_free (contents); } static const gchar * process_next_command (TestPortContext *ctx, GByteArray *buffer) { gsize i = 0; gchar *command; const gchar *response; static const gchar *error_response = "\r\nERROR\r\n"; /* Find command end */ while (i < buffer->len && buffer->data[i] != '\r' && buffer->data[i] != '\n') i++; if (i == buffer->len) /* no command */ return NULL; while (i < buffer->len && (buffer->data[i] == '\r' || buffer->data[i] == '\n')) buffer->data[i++] = '\0'; /* Setup command and lookup response */ command = g_strndup ((gchar *)buffer->data, i); response = g_hash_table_lookup (ctx->commands, command); g_free (command); /* Remove command from buffer */ g_byte_array_remove_range (buffer, 0, i); return response ? response : error_response; } /*****************************************************************************/ typedef struct { TestPortContext *ctx; GSocketConnection *connection; GSource *connection_readable_source; GByteArray *buffer; } Client; static void client_free (Client *client) { g_source_destroy (client->connection_readable_source); g_source_unref (client->connection_readable_source); g_output_stream_close (g_io_stream_get_output_stream (G_IO_STREAM (client->connection)), NULL, NULL); if (client->buffer) g_byte_array_unref (client->buffer); g_object_unref (client->connection); g_slice_free (Client, client); } static void connection_close (Client *client) { client->ctx->clients = g_list_remove (client->ctx->clients, client); client_free (client); } static void client_parse_request (Client *client) { const gchar *response; do { response = process_next_command (client->ctx, client->buffer); if (response) { GError *error = NULL; if (!g_output_stream_write_all (g_io_stream_get_output_stream (G_IO_STREAM (client->connection)), response, strlen (response), NULL, /* bytes_written */ NULL, /* cancellable */ &error)) { g_warning ("Cannot send response to client: %s", error->message); g_error_free (error); } } } while (response); } static gboolean connection_readable_cb (GSocket *socket, GIOCondition condition, Client *client) { guint8 buffer[BUFFER_SIZE]; GError *error = NULL; gssize r; if (condition & G_IO_HUP || condition & G_IO_ERR) { g_debug ("client connection closed"); connection_close (client); return FALSE; } if (!(condition & G_IO_IN || condition & G_IO_PRI)) return TRUE; r = g_input_stream_read (g_io_stream_get_input_stream (G_IO_STREAM (client->connection)), buffer, BUFFER_SIZE, NULL, &error); if (r < 0) { g_warning ("Error reading from istream: %s", error ? error->message : "unknown"); if (error) g_error_free (error); /* Close the device */ connection_close (client); return FALSE; } if (r == 0) return TRUE; /* else, r > 0 */ if (!G_UNLIKELY (client->buffer)) client->buffer = g_byte_array_sized_new (r); g_byte_array_append (client->buffer, buffer, r); /* Try to parse input messages */ client_parse_request (client); return TRUE; } static Client * client_new (TestPortContext *self, GSocketConnection *connection) { Client *client; client = g_slice_new0 (Client); client->ctx = self; client->connection = g_object_ref (connection); client->connection_readable_source = g_socket_create_source (g_socket_connection_get_socket (client->connection), G_IO_IN | G_IO_PRI | G_IO_ERR | G_IO_HUP, NULL); g_source_set_callback (client->connection_readable_source, (GSourceFunc)connection_readable_cb, client, NULL); g_source_attach (client->connection_readable_source, self->context); return client; } /* /\*****************************************************************************\/ */ static void incoming_cb (GSocketService *service, GSocketConnection *connection, GObject *unused, TestPortContext *self) { Client *client; client = client_new (self, connection); self->clients = g_list_append (self->clients, client); } static void create_socket_service (TestPortContext *self) { GError *error = NULL; GSocketService *service; GSocketAddress *address; GSocket *socket; g_assert (self->socket_service == NULL); /* Create socket */ socket = g_socket_new (G_SOCKET_FAMILY_UNIX, G_SOCKET_TYPE_STREAM, G_SOCKET_PROTOCOL_DEFAULT, &error); if (!socket) g_error ("Cannot create socket: %s", error->message); /* Bind to address */ address = (g_unix_socket_address_new_with_type ( self->name, -1, (g_str_has_prefix (self->name, "abstract:") ? G_UNIX_SOCKET_ADDRESS_ABSTRACT : G_UNIX_SOCKET_ADDRESS_PATH))); if (!g_socket_bind (socket, address, TRUE, &error)) g_error ("Cannot bind socket: %s", error->message); g_object_unref (address); /* Listen */ if (!g_socket_listen (socket, &error)) g_error ("Cannot listen in socket: %s", error->message); /* Create socket service */ service = g_socket_service_new (); g_signal_connect (service, "incoming", G_CALLBACK (incoming_cb), self); if (!g_socket_listener_add_socket (G_SOCKET_LISTENER (service), socket, NULL, /* don't pass an object, will take a reference */ &error)) g_error ("Cannot add listener to socket: %s", error->message); /* Start it */ g_socket_service_start (service); /* And store both the service and the socket. * Since GLib 2.42 the socket may not be explicitly closed when the * listener is diposed, so we'll do it ourselves. */ self->socket_service = service; self->socket = socket; /* Signal that the thread is ready */ g_mutex_lock (&self->ready_mutex); self->ready = TRUE; g_cond_signal (&self->ready_cond); g_mutex_unlock (&self->ready_mutex); } /*****************************************************************************/ static gboolean cancel_loop_cb (TestPortContext *self) { g_main_loop_quit (self->loop); return FALSE; } void test_port_context_stop (TestPortContext *self) { g_assert (self->thread != NULL); g_assert (self->loop != NULL); g_assert (self->context != NULL); /* Cancel main loop of the port context thread, by scheduling an idle task * in the thread-owned main context */ g_main_context_invoke (self->context, (GSourceFunc) cancel_loop_cb, self); g_thread_join (self->thread); self->thread = NULL; } static gpointer port_context_thread_func (TestPortContext *self) { g_assert (self->loop == NULL); g_assert (self->context == NULL); /* Define main context and loop for the thread */ self->context = g_main_context_new (); self->loop = g_main_loop_new (self->context, FALSE); g_main_context_push_thread_default (self->context); /* Once the thread default context is setup, launch service */ create_socket_service (self); g_main_loop_run (self->loop); g_main_loop_unref (self->loop); self->loop = NULL; g_main_context_unref (self->context); self->context = NULL; return NULL; } void test_port_context_start (TestPortContext *self) { g_assert (self->thread == NULL); self->thread = g_thread_new (self->name, (GThreadFunc)port_context_thread_func, self); /* Now wait until the thread has finished its initialization and is * ready to serve connections */ g_mutex_lock (&self->ready_mutex); while (!self->ready) g_cond_wait (&self->ready_cond, &self->ready_mutex); g_mutex_unlock (&self->ready_mutex); } /*****************************************************************************/ void test_port_context_free (TestPortContext *self) { g_assert (self->thread == NULL); g_assert (self->loop == NULL); g_cond_clear (&self->ready_cond); g_mutex_clear (&self->ready_mutex); if (self->commands) g_hash_table_unref (self->commands); g_list_free_full (self->clients, (GDestroyNotify)client_free); if (self->socket) { GError *error = NULL; if (!g_socket_close (self->socket, &error)) { g_debug ("Couldn't close socket: %s", error->message); g_error_free (error); } g_object_unref (self->socket); } if (self->socket_service) { if (g_socket_service_is_active (self->socket_service)) g_socket_service_stop (self->socket_service); g_object_unref (self->socket_service); } g_free (self->name); g_slice_free (TestPortContext, self); } TestPortContext * test_port_context_new (const gchar *name) { TestPortContext *self; self = g_slice_new0 (TestPortContext); self->name = g_strdup (name); g_cond_init (&self->ready_cond); g_mutex_init (&self->ready_mutex); return self; } ModemManager-1.23.4-dev/src/plugins/tests/test-port-context.h000066400000000000000000000027351456466623000241420ustar00rootroot00000000000000/* -*- 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) 2013 Aleksander Morgado */ #ifndef TEST_PORT_CONTEXT_H #define TEST_PORT_CONTEXT_H #include #include typedef struct _TestPortContext TestPortContext; TestPortContext *test_port_context_new (const gchar *name); void test_port_context_start (TestPortContext *self); void test_port_context_stop (TestPortContext *self); void test_port_context_free (TestPortContext *self); void test_port_context_set_command (TestPortContext *self, const gchar *command, const gchar *response); void test_port_context_load_commands (TestPortContext *self, const gchar *commands_file); #endif /* TEST_PORT_CONTEXT_H */ ModemManager-1.23.4-dev/src/plugins/tests/test-udev-rules.c000066400000000000000000000131421456466623000235540ustar00rootroot00000000000000/* -*- 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) 2016 Aleksander Morgado */ #include #include #include #include #include #include #define _LIBMM_INSIDE_MM #include #include "mm-kernel-device-generic-rules.h" #include "mm-log-test.h" /************************************************************/ static void common_test (const gchar *plugindir) { GArray *rules; GError *error = NULL; if (!plugindir) return; rules = mm_kernel_device_generic_rules_load (plugindir, &error); g_assert_no_error (error); g_assert (rules); g_assert (rules->len > 0); g_array_unref (rules); } /* Placeholder test to avoid compiler warning about common_test() being unused * when none of the plugins enabled in build have custom udev rules. */ static void test_placeholder (void) { common_test (NULL); } /************************************************************/ #if defined ENABLE_PLUGIN_HUAWEI static void test_huawei (void) { common_test (TESTUDEVRULESDIR_HUAWEI); } #endif #if defined ENABLE_PLUGIN_MBM static void test_mbm (void) { common_test (TESTUDEVRULESDIR_MBM); } #endif #if defined ENABLE_PLUGIN_NOKIA_ICERA static void test_nokia_icera (void) { common_test (TESTUDEVRULESDIR_NOKIA_ICERA); } #endif #if defined ENABLE_PLUGIN_ZTE static void test_zte (void) { common_test (TESTUDEVRULESDIR_ZTE); } #endif #if defined ENABLE_PLUGIN_LONGCHEER static void test_longcheer (void) { common_test (TESTUDEVRULESDIR_LONGCHEER); } #endif #if defined ENABLE_PLUGIN_SIMTECH static void test_simtech (void) { common_test (TESTUDEVRULESDIR_SIMTECH); } #endif #if defined ENABLE_PLUGIN_X22X static void test_x22x (void) { common_test (TESTUDEVRULESDIR_X22X); } #endif #if defined ENABLE_PLUGIN_CINTERION static void test_cinterion (void) { common_test (TESTUDEVRULESDIR_CINTERION); } #endif #if defined ENABLE_PLUGIN_DELL static void test_dell (void) { common_test (TESTUDEVRULESDIR_DELL); } #endif #if defined ENABLE_PLUGIN_TELIT static void test_telit (void) { common_test (TESTUDEVRULESDIR_TELIT); } #endif #if defined ENABLE_PLUGIN_MTK_LEGACY static void test_mtk_legacy (void) { common_test (TESTUDEVRULESDIR_MTK); } #endif #if defined ENABLE_PLUGIN_HAIER static void test_haier (void) { common_test (TESTUDEVRULESDIR_HAIER); } #endif #if defined ENABLE_PLUGIN_FIBOCOM static void test_fibocom (void) { common_test (TESTUDEVRULESDIR_FIBOCOM); } #endif #if defined ENABLE_PLUGIN_QUECTEL static void test_quectel (void) { common_test (TESTUDEVRULESDIR_QUECTEL); } #endif #if defined ENABLE_PLUGIN_GOSUNCN static void test_gosuncn (void) { common_test (TESTUDEVRULESDIR_GOSUNCN); } #endif #if defined ENABLE_PLUGIN_QCOM_SOC && defined WITH_QMI static void test_qcom_soc (void) { common_test (TESTUDEVRULESDIR_QCOM_SOC); } #endif #if defined ENABLE_PLUGIN_LINKTOP static void test_linktop (void) { common_test (TESTUDEVRULESDIR_LINKTOP); } #endif /************************************************************/ int main (int argc, char **argv) { setlocale (LC_ALL, ""); g_test_init (&argc, &argv, NULL); g_test_add_func ("/MM/test-udev-rules/placeholder", test_placeholder); #if defined ENABLE_PLUGIN_HUAWEI g_test_add_func ("/MM/test-udev-rules/huawei", test_huawei); #endif #if defined ENABLE_PLUGIN_MBM g_test_add_func ("/MM/test-udev-rules/mbm", test_mbm); #endif #if defined ENABLE_PLUGIN_NOKIA_ICERA g_test_add_func ("/MM/test-udev-rules/nokia-icera", test_nokia_icera); #endif #if defined ENABLE_PLUGIN_ZTE g_test_add_func ("/MM/test-udev-rules/zte", test_zte); #endif #if defined ENABLE_PLUGIN_LONGCHEER g_test_add_func ("/MM/test-udev-rules/longcheer", test_longcheer); #endif #if defined ENABLE_PLUGIN_SIMTECH g_test_add_func ("/MM/test-udev-rules/simtech", test_simtech); #endif #if defined ENABLE_PLUGIN_X22X g_test_add_func ("/MM/test-udev-rules/x22x", test_x22x); #endif #if defined ENABLE_PLUGIN_CINTERION g_test_add_func ("/MM/test-udev-rules/cinterion", test_cinterion); #endif #if defined ENABLE_PLUGIN_DELL g_test_add_func ("/MM/test-udev-rules/dell", test_dell); #endif #if defined ENABLE_PLUGIN_TELIT g_test_add_func ("/MM/test-udev-rules/telit", test_telit); #endif #if defined ENABLE_PLUGIN_MTK_LEGACY g_test_add_func ("/MM/test-udev-rules/mtk", test_mtk_legacy); #endif #if defined ENABLE_PLUGIN_HAIER g_test_add_func ("/MM/test-udev-rules/haier", test_haier); #endif #if defined ENABLE_PLUGIN_FIBOCOM g_test_add_func ("/MM/test-udev-rules/fibocom", test_fibocom); #endif #if defined ENABLE_PLUGIN_QUECTEL g_test_add_func ("/MM/test-udev-rules/quectel", test_quectel); #endif #if defined ENABLE_PLUGIN_GOSUNCN g_test_add_func ("/MM/test-udev-rules/gosuncn", test_gosuncn); #endif #if defined ENABLE_PLUGIN_QCOM_SOC && defined WITH_QMI g_test_add_func ("/MM/test-udev-rules/qcom-soc", test_qcom_soc); #endif #if defined ENABLE_PLUGIN_LINKTOP g_test_add_func ("/MM/test-udev-rules/linktop", test_linktop); #endif return g_test_run (); } ModemManager-1.23.4-dev/src/plugins/thuraya/000077500000000000000000000000001456466623000206525ustar00rootroot00000000000000ModemManager-1.23.4-dev/src/plugins/thuraya/mm-broadband-modem-thuraya.c000066400000000000000000000233161456466623000261200ustar00rootroot00000000000000/* -*- 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) 2011 - 2012 Ammonit Measurement GmbH * Author: Aleksander Morgado * Copyright (C) 2016 Thomas Sailer */ #include #include #include #include #include #include #include "ModemManager.h" #include "mm-errors-types.h" #include "mm-base-modem-at.h" #include "mm-iface-modem.h" #include "mm-iface-modem-3gpp.h" #include "mm-iface-modem-messaging.h" #include "mm-broadband-modem-thuraya.h" #include "mm-broadband-bearer.h" #include "mm-modem-helpers.h" #include "mm-modem-helpers-thuraya.h" static void iface_modem_init (MMIfaceModem *iface); static void iface_modem_3gpp_init (MMIfaceModem3gpp *iface); static void iface_modem_messaging_init (MMIfaceModemMessaging *iface); G_DEFINE_TYPE_EXTENDED (MMBroadbandModemThuraya, mm_broadband_modem_thuraya, MM_TYPE_BROADBAND_MODEM, 0, G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM, iface_modem_init) G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_3GPP, iface_modem_3gpp_init) G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_MESSAGING, iface_modem_messaging_init) ); /*****************************************************************************/ /* Operator Code and Name loading (3GPP interface) */ static gchar * load_operator_code_finish (MMIfaceModem3gpp *self, GAsyncResult *res, GError **error) { return g_task_propagate_pointer (G_TASK (res), error); } static void load_operator_code (MMIfaceModem3gpp *self, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; task = g_task_new (self, NULL, callback, user_data); g_task_return_pointer (task, g_strdup ("90106"), g_free); g_object_unref (task); } static gchar * load_operator_name_finish (MMIfaceModem3gpp *self, GAsyncResult *res, GError **error) { return g_task_propagate_pointer (G_TASK (res), error); } static void load_operator_name (MMIfaceModem3gpp *self, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; task = g_task_new (self, NULL, callback, user_data); g_task_return_pointer (task, g_strdup ("THURAYA"), g_free); g_object_unref (task); } /*****************************************************************************/ /* Load supported modes (Modem inteface) */ static GArray * load_supported_modes_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { return g_task_propagate_pointer (G_TASK (res), error); } static void load_supported_modes (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; GArray *combinations; MMModemModeCombination mode; task = g_task_new (self, NULL, callback, user_data); /* Build list of combinations */ combinations = g_array_sized_new (FALSE, FALSE, sizeof (MMModemModeCombination), 1); /* Report any, Thuraya connections are packet-switched */ mode.allowed = MM_MODEM_MODE_ANY; mode.preferred = MM_MODEM_MODE_NONE; g_array_append_val (combinations, mode); g_task_return_pointer (task, combinations, (GDestroyNotify) g_array_unref); g_object_unref (task); } /*****************************************************************************/ /* Load supported SMS storages (Messaging interface) */ typedef struct { GArray *mem1; GArray *mem2; GArray *mem3; } SupportedStoragesResult; static void supported_storages_result_free (SupportedStoragesResult *result) { if (result->mem1) g_array_unref (result->mem1); if (result->mem2) g_array_unref (result->mem2); if (result->mem3) g_array_unref (result->mem3); g_free (result); } static gboolean modem_messaging_load_supported_storages_finish (MMIfaceModemMessaging *self, GAsyncResult *res, GArray **mem1, GArray **mem2, GArray **mem3, GError **error) { SupportedStoragesResult *result; result = g_task_propagate_pointer (G_TASK (res), error); if (!result) return FALSE; *mem1 = g_array_ref (result->mem1); *mem2 = g_array_ref (result->mem2); *mem3 = g_array_ref (result->mem3); supported_storages_result_free (result); return TRUE; } static void cpms_format_check_ready (MMBaseModem *self, GAsyncResult *res, GTask *task) { const gchar *response; GError *error = NULL; SupportedStoragesResult *result; response = mm_base_modem_at_command_finish (self, res, &error); if (error) { g_task_return_error (task, error); g_object_unref (task); return; } result = g_new0 (SupportedStoragesResult, 1); /* Parse reply */ if (!mm_thuraya_3gpp_parse_cpms_test_response (response, &result->mem1, &result->mem2, &result->mem3, &error)) { supported_storages_result_free (result); g_task_return_error (task, error); g_object_unref (task); return; } g_task_return_pointer (task, result, (GDestroyNotify) supported_storages_result_free); g_object_unref (task); } static void modem_messaging_load_supported_storages (MMIfaceModemMessaging *self, GAsyncReadyCallback callback, gpointer user_data) { /* Check support storages */ mm_base_modem_at_command (MM_BASE_MODEM (self), "+CPMS=?", 3, TRUE, (GAsyncReadyCallback)cpms_format_check_ready, g_task_new (self, NULL, callback, user_data)); } /*****************************************************************************/ MMBroadbandModemThuraya * mm_broadband_modem_thuraya_new (const gchar *device, const gchar *physdev, const gchar **drivers, const gchar *plugin, guint16 vendor_id, guint16 product_id) { return g_object_new (MM_TYPE_BROADBAND_MODEM_THURAYA, MM_BASE_MODEM_DEVICE, device, MM_BASE_MODEM_PHYSDEV, physdev, MM_BASE_MODEM_DRIVERS, drivers, MM_BASE_MODEM_PLUGIN, plugin, MM_BASE_MODEM_VENDOR_ID, vendor_id, MM_BASE_MODEM_PRODUCT_ID, product_id, /* Generic bearer supports TTY only */ MM_BASE_MODEM_DATA_NET_SUPPORTED, FALSE, MM_BASE_MODEM_DATA_TTY_SUPPORTED, TRUE, NULL); } static void mm_broadband_modem_thuraya_init (MMBroadbandModemThuraya *self) { } static void iface_modem_init (MMIfaceModem *iface) { /* No need to power-up/power-down the modem */ iface->load_power_state = NULL; iface->load_power_state_finish = NULL; iface->modem_power_up = NULL; iface->modem_power_up_finish = NULL; iface->modem_power_down = NULL; iface->modem_power_down_finish = NULL; /* Supported modes cannot be queried */ iface->load_supported_modes = load_supported_modes; iface->load_supported_modes_finish = load_supported_modes_finish; } static void iface_modem_3gpp_init (MMIfaceModem3gpp *iface) { /* Fixed operator code and name to be reported */ iface->load_operator_name = load_operator_name; iface->load_operator_name_finish = load_operator_name_finish; iface->load_operator_code = load_operator_code; iface->load_operator_code_finish = load_operator_code_finish; /* Don't try to scan networks with AT+COPS=?. * The Thuraya XT does not seem to properly support AT+COPS=?. * When issuing this command, it seems to get sufficiently confused * to drop the signal. Furthermore, it is useless anyway as there is only * one network supported, Thuraya. */ iface->scan_networks = NULL; iface->scan_networks_finish = NULL; } static void iface_modem_messaging_init (MMIfaceModemMessaging *iface) { iface->load_supported_storages = modem_messaging_load_supported_storages; iface->load_supported_storages_finish = modem_messaging_load_supported_storages_finish; } static void mm_broadband_modem_thuraya_class_init (MMBroadbandModemThurayaClass *klass) { } ModemManager-1.23.4-dev/src/plugins/thuraya/mm-broadband-modem-thuraya.h000066400000000000000000000047151456466623000261270ustar00rootroot00000000000000/* -*- 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 - 2011 Red Hat, Inc. * Copyright (C) 2011 Google Inc. * Copyright (C) 2016 Thomas Sailer */ #ifndef MM_BROADBAND_MODEM_THURAYA_H #define MM_BROADBAND_MODEM_THURAYA_H #include "mm-broadband-modem.h" #define MM_TYPE_BROADBAND_MODEM_THURAYA (mm_broadband_modem_thuraya_get_type ()) #define MM_BROADBAND_MODEM_THURAYA(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_BROADBAND_MODEM_THURAYA, MMBroadbandModemThuraya)) #define MM_BROADBAND_MODEM_THURAYA_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_BROADBAND_MODEM_THURAYA, MMBroadbandModemThurayaClass)) #define MM_IS_BROADBAND_MODEM_THURAYA(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_BROADBAND_MODEM_THURAYA)) #define MM_IS_BROADBAND_MODEM_THURAYA_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_BROADBAND_MODEM_THURAYA)) #define MM_BROADBAND_MODEM_THURAYA_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_BROADBAND_MODEM_THURAYA, MMBroadbandModemThurayaClass)) typedef struct _MMBroadbandModemThuraya MMBroadbandModemThuraya; typedef struct _MMBroadbandModemThurayaClass MMBroadbandModemThurayaClass; struct _MMBroadbandModemThuraya { MMBroadbandModem parent; }; struct _MMBroadbandModemThurayaClass{ MMBroadbandModemClass parent; }; GType mm_broadband_modem_thuraya_get_type (void); MMBroadbandModemThuraya *mm_broadband_modem_thuraya_new (const gchar *device, const gchar *physdev, const gchar **drivers, const gchar *plugin, guint16 vendor_id, guint16 product_id); #endif /* MM_BROADBAND_MODEM_THURAYA_H */ ModemManager-1.23.4-dev/src/plugins/thuraya/mm-modem-helpers-thuraya.c000066400000000000000000000110641456466623000256430ustar00rootroot00000000000000/* -*- 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) 2016 Thomas Sailer * */ #include #include #include #include #define _LIBMM_INSIDE_MMCLI #include #include "mm-log.h" #include "mm-modem-helpers.h" #include "mm-modem-helpers-thuraya.h" /*************************************************************************/ static MMSmsStorage storage_from_str (const gchar *str) { if (g_str_equal (str, "SM")) return MM_SMS_STORAGE_SM; if (g_str_equal (str, "ME")) return MM_SMS_STORAGE_ME; if (g_str_equal (str, "MT")) return MM_SMS_STORAGE_MT; if (g_str_equal (str, "SR")) return MM_SMS_STORAGE_SR; if (g_str_equal (str, "BM")) return MM_SMS_STORAGE_BM; if (g_str_equal (str, "TA")) return MM_SMS_STORAGE_TA; return MM_SMS_STORAGE_UNKNOWN; } gboolean mm_thuraya_3gpp_parse_cpms_test_response (const gchar *reply, GArray **mem1, GArray **mem2, GArray **mem3, GError **error) { #define N_EXPECTED_GROUPS 3 gchar **splitp; const gchar *splita[N_EXPECTED_GROUPS]; guint i; g_auto(GStrv) split = NULL; g_autoptr(GRegex) r = NULL; g_autoptr(GArray) tmp1 = NULL; g_autoptr(GArray) tmp2 = NULL; g_autoptr(GArray) tmp3 = NULL; g_assert (mem1 != NULL); g_assert (mem2 != NULL); g_assert (mem3 != NULL); split = g_strsplit (mm_strip_tag (reply, "+CPMS:"), " ", -1); if (!split) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't split +CPMS response"); return FALSE; } /* remove empty strings, and count non-empty strings */ i = 0; for (splitp = split; *splitp; ++splitp) { if (!**splitp) continue; if (i < N_EXPECTED_GROUPS) splita[i] = *splitp; ++i; } if (i != N_EXPECTED_GROUPS) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't parse +CPMS response: invalid number of groups (%u != %u)", i, N_EXPECTED_GROUPS); return FALSE; } r = g_regex_new ("\\s*\"([^,\\)]+)\"\\s*", 0, 0, NULL); g_assert (r); for (i = 0; i < N_EXPECTED_GROUPS; i++) { g_autoptr(GMatchInfo) match_info = NULL; GArray *array; /* We always return a valid array, even if it may be empty */ array = g_array_new (FALSE, FALSE, sizeof (MMSmsStorage)); /* Got a range group to match */ if (g_regex_match (r, splita[i], 0, &match_info)) { while (g_match_info_matches (match_info)) { g_autofree gchar *str = NULL; str = g_match_info_fetch (match_info, 1); if (str) { MMSmsStorage storage; storage = storage_from_str (str); g_array_append_val (array, storage); } g_match_info_next (match_info, NULL); } } if (!tmp1) tmp1 = array; else if (!tmp2) tmp2 = array; else if (!tmp3) tmp3 = array; else g_assert_not_reached (); } /* Only return TRUE if all sets have been parsed correctly * (even if the arrays may be empty) */ if (tmp1 && tmp2 && tmp3) { *mem1 = g_steal_pointer (&tmp1); *mem2 = g_steal_pointer (&tmp2); *mem3 = g_steal_pointer (&tmp3); return TRUE; } /* Otherwise, cleanup and return FALSE */ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't parse +CPMS response: not all groups detected (mem1 %s, mem2 %s, mem3 %s)", tmp1 ? "yes" : "no", tmp2 ? "yes" : "no", tmp3 ? "yes" : "no"); return FALSE; } ModemManager-1.23.4-dev/src/plugins/thuraya/mm-modem-helpers-thuraya.h000066400000000000000000000022601456466623000256460ustar00rootroot00000000000000/* -*- 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) 2016 Thomas Sailer * */ #ifndef MM_MODEM_HELPERS_THURAYA_H #define MM_MODEM_HELPERS_THURAYA_H #include /* AT+CPMS=? (Preferred SMS storage) response parser */ gboolean mm_thuraya_3gpp_parse_cpms_test_response (const gchar *reply, GArray **mem1, GArray **mem2, GArray **mem3, GError **error); #endif /* MM_MODEM_HELPERS_THURAYA_H */ ModemManager-1.23.4-dev/src/plugins/thuraya/mm-plugin-thuraya.c000066400000000000000000000056741456466623000244120ustar00rootroot00000000000000/* -*- 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. * * 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. * * Copyright (C) 2011 - 2012 Ammonit Measurement GmbH * Author: Aleksander Morgado * Copyright (C) 2016 Thomas Sailer */ #include #include #define _LIBMM_INSIDE_MM #include #include "mm-plugin-common.h" #include "mm-broadband-modem.h" #include "mm-broadband-modem-thuraya.h" #include "mm-private-boxed-types.h" #define MM_TYPE_PLUGIN_THURAYA mm_plugin_thuraya_get_type () MM_DEFINE_PLUGIN (THURAYA, thuraya, Thuraya) /*****************************************************************************/ static MMBaseModem * create_modem (MMPlugin *self, const gchar *uid, const gchar *physdev, const gchar **drivers, guint16 vendor, guint16 product, guint16 subsystem_vendor, GList *probes, GError **error) { return MM_BASE_MODEM (mm_broadband_modem_thuraya_new (uid, physdev, drivers, mm_plugin_get_name (self), vendor, product)); } /*****************************************************************************/ MM_PLUGIN_NAMED_CREATOR_SCOPE MMPlugin * mm_plugin_create_thuraya (void) { static const gchar *subsystems[] = { "tty", NULL }; static const guint16 vendor_ids[] = { 0x1a26, 0 }; return MM_PLUGIN ( g_object_new (MM_TYPE_PLUGIN_THURAYA, MM_PLUGIN_NAME, MM_MODULE_NAME, MM_PLUGIN_ALLOWED_SUBSYSTEMS, subsystems, MM_PLUGIN_ALLOWED_VENDOR_IDS, vendor_ids, MM_PLUGIN_ALLOWED_AT, TRUE, NULL)); } static void mm_plugin_thuraya_init (MMPluginThuraya *self) { } static void mm_plugin_thuraya_class_init (MMPluginThurayaClass *klass) { MMPluginClass *plugin_class = MM_PLUGIN_CLASS (klass); plugin_class->create_modem = create_modem; } ModemManager-1.23.4-dev/src/plugins/thuraya/tests/000077500000000000000000000000001456466623000220145ustar00rootroot00000000000000ModemManager-1.23.4-dev/src/plugins/thuraya/tests/test-mm-modem-helpers-thuraya.c000066400000000000000000000066741456466623000277750ustar00rootroot00000000000000/* -*- 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) 2016 Thomas Sailer * */ #include #include #include #include #include #define _LIBMM_INSIDE_MM #include #include "mm-modem-helpers.h" #include "mm-modem-helpers-thuraya.h" #include "mm-log-test.h" /*****************************************************************************/ /* Test CPMS response */ static gboolean is_storage_supported (GArray *supported, MMSmsStorage storage) { guint i; for (i = 0; i < supported->len; i++) { if (storage == g_array_index (supported, MMSmsStorage, i)) return TRUE; } return FALSE; } static void test_cpms_response_thuraya (void *f, gpointer d) { /* * First: ("ME","MT") 2-item group * Second: "ME" 1 item * Third: ("SM") 1-item group */ const gchar *reply = "+CPMS: \"MT\",\"SM\",\"BM\",\"ME\",\"SR\", \"MT\",\"SM\",\"BM\",\"ME\",\"SR\", \"MT\",\"SM\",\"BM\",\"ME\",\"SR\" "; GArray *mem1 = NULL; GArray *mem2 = NULL; GArray *mem3 = NULL; GError *error = NULL; g_debug ("Testing thuraya +CPMS=? response..."); g_assert (mm_thuraya_3gpp_parse_cpms_test_response (reply, &mem1, &mem2, &mem3, &error)); g_assert_no_error (error); g_assert_cmpuint (mem1->len, ==, 5); g_assert (is_storage_supported (mem1, MM_SMS_STORAGE_MT)); g_assert (is_storage_supported (mem1, MM_SMS_STORAGE_SM)); g_assert (is_storage_supported (mem1, MM_SMS_STORAGE_BM)); g_assert (is_storage_supported (mem1, MM_SMS_STORAGE_ME)); g_assert (is_storage_supported (mem1, MM_SMS_STORAGE_SR)); g_assert_cmpuint (mem2->len, ==, 5); g_assert (is_storage_supported (mem2, MM_SMS_STORAGE_MT)); g_assert (is_storage_supported (mem2, MM_SMS_STORAGE_SM)); g_assert (is_storage_supported (mem2, MM_SMS_STORAGE_BM)); g_assert (is_storage_supported (mem2, MM_SMS_STORAGE_ME)); g_assert (is_storage_supported (mem2, MM_SMS_STORAGE_SR)); g_assert_cmpuint (mem3->len, ==, 5); g_assert (is_storage_supported (mem3, MM_SMS_STORAGE_MT)); g_assert (is_storage_supported (mem3, MM_SMS_STORAGE_SM)); g_assert (is_storage_supported (mem3, MM_SMS_STORAGE_BM)); g_assert (is_storage_supported (mem3, MM_SMS_STORAGE_ME)); g_assert (is_storage_supported (mem3, MM_SMS_STORAGE_SR)); g_array_unref (mem1); g_array_unref (mem2); g_array_unref (mem3); } /*****************************************************************************/ #define TESTCASE(t, d) g_test_create_case (#t, 0, d, NULL, (GTestFixtureFunc) t, NULL) int main (int argc, char **argv) { GTestSuite *suite; gint result; g_test_init (&argc, &argv, NULL); suite = g_test_get_root (); g_test_suite_add (suite, TESTCASE (test_cpms_response_thuraya, NULL)); result = g_test_run (); return result; } ModemManager-1.23.4-dev/src/plugins/tplink/000077500000000000000000000000001456466623000204765ustar00rootroot00000000000000ModemManager-1.23.4-dev/src/plugins/tplink/77-mm-tplink-port-types.rules000066400000000000000000000013431456466623000260420ustar00rootroot00000000000000# do not edit this file, it will be overwritten on update ACTION!="add|change|move|bind", GOTO="mm_tplink_port_types_end" SUBSYSTEMS=="usb", ATTRS{idVendor}=="2357", GOTO="mm_tplink_port_types" GOTO="mm_tplink_port_types_end" LABEL="mm_tplink_port_types" SUBSYSTEMS=="usb", ATTRS{bInterfaceNumber}=="?*", ENV{.MM_USBIFNUM}="$attr{bInterfaceNumber}" # D-Link DWM-222 ATTRS{idVendor}=="2357", ATTRS{idProduct}=="9000", ENV{.MM_USBIFNUM}=="00", ENV{ID_MM_PORT_IGNORE}="1" ATTRS{idVendor}=="2357", ATTRS{idProduct}=="9000", ENV{.MM_USBIFNUM}=="01", ENV{ID_MM_PORT_IGNORE}="1" ATTRS{idVendor}=="2357", ATTRS{idProduct}=="9000", ENV{.MM_USBIFNUM}=="03", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1" LABEL="mm_tplink_port_types_end" ModemManager-1.23.4-dev/src/plugins/tplink/mm-plugin-tplink.c000066400000000000000000000063741456466623000240600ustar00rootroot00000000000000/* -*- 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) 2019 Aleksander Morgado */ #include #include #define _LIBMM_INSIDE_MM #include #include "mm-log-object.h" #include "mm-plugin-common.h" #include "mm-broadband-modem.h" #if defined WITH_QMI # include "mm-broadband-modem-qmi.h" #endif #define MM_TYPE_PLUGIN_TPLINK mm_plugin_tplink_get_type () MM_DEFINE_PLUGIN (TPLINK, tplink, Tplink) /*****************************************************************************/ static MMBaseModem * create_modem (MMPlugin *self, const gchar *uid, const gchar *physdev, const gchar **drivers, guint16 vendor, guint16 product, guint16 subsystem_vendor, GList *probes, GError **error) { #if defined WITH_QMI if (mm_port_probe_list_has_qmi_port (probes)) { mm_obj_dbg (self, "QMI-powered TP-Link modem found..."); return MM_BASE_MODEM (mm_broadband_modem_qmi_new (uid, physdev, drivers, mm_plugin_get_name (self), vendor, product)); } #endif return MM_BASE_MODEM (mm_broadband_modem_new (uid, physdev, drivers, mm_plugin_get_name (self), vendor, product)); } /*****************************************************************************/ MM_PLUGIN_NAMED_CREATOR_SCOPE MMPlugin * mm_plugin_create_tplink (void) { static const gchar *subsystems[] = { "tty", "net", "usbmisc", NULL }; static const guint16 vendor_ids[] = { 0x2357, 0 }; return MM_PLUGIN ( g_object_new (MM_TYPE_PLUGIN_TPLINK, MM_PLUGIN_NAME, MM_MODULE_NAME, MM_PLUGIN_ALLOWED_SUBSYSTEMS, subsystems, MM_PLUGIN_ALLOWED_VENDOR_IDS, vendor_ids, MM_PLUGIN_ALLOWED_AT, TRUE, MM_PLUGIN_ALLOWED_QMI, TRUE, NULL)); } static void mm_plugin_tplink_init (MMPluginTplink *self) { } static void mm_plugin_tplink_class_init (MMPluginTplinkClass *klass) { MMPluginClass *plugin_class = MM_PLUGIN_CLASS (klass); plugin_class->create_modem = create_modem; } ModemManager-1.23.4-dev/src/plugins/ublox/000077500000000000000000000000001456466623000203265ustar00rootroot00000000000000ModemManager-1.23.4-dev/src/plugins/ublox/77-mm-ublox-port-types.rules000066400000000000000000000117011456466623000255210ustar00rootroot00000000000000# do not edit this file, it will be overwritten on update ACTION!="add|change|move|bind", GOTO="mm_ublox_port_types_end" SUBSYSTEMS=="usb", ATTRS{idVendor}=="1546", GOTO="mm_ublox_port_types" SUBSYSTEMS=="usb", ATTRS{idVendor}=="05c6", GOTO="mm_qualcomm_port_types" GOTO="mm_ublox_port_types_end" LABEL="mm_ublox_port_types" SUBSYSTEMS=="usb", ATTRS{bInterfaceNumber}=="?*", ENV{.MM_USBIFNUM}="$attr{bInterfaceNumber}" # Fully ignore u-blox GPS devices ATTRS{idVendor}=="1546", ATTRS{idProduct}=="01a5", ENV{ID_MM_DEVICE_IGNORE}="1" ATTRS{idVendor}=="1546", ATTRS{idProduct}=="01a6", ENV{ID_MM_DEVICE_IGNORE}="1" ATTRS{idVendor}=="1546", ATTRS{idProduct}=="01a7", ENV{ID_MM_DEVICE_IGNORE}="1" ATTRS{idVendor}=="1546", ATTRS{idProduct}=="01a8", ENV{ID_MM_DEVICE_IGNORE}="1" ATTRS{idVendor}=="1546", ATTRS{idProduct}=="01a9", ENV{ID_MM_DEVICE_IGNORE}="1" # Toby-L4 port types # ttyACM0 (if #2): secondary (ignore) # ttyACM1 (if #4): debug port (ignore) # ttyACM2 (if #6): primary # Wait up to 20s for the +READY URC # ttyACM3 (if #8): AT port for FOTA (ignore) ATTRS{idVendor}=="1546", ATTRS{idProduct}=="1010", ENV{.MM_USBIFNUM}=="02", ENV{ID_MM_PORT_IGNORE}="1" ATTRS{idVendor}=="1546", ATTRS{idProduct}=="1010", ENV{.MM_USBIFNUM}=="04", ENV{ID_MM_PORT_IGNORE}="1" ATTRS{idVendor}=="1546", ATTRS{idProduct}=="1010", ENV{.MM_USBIFNUM}=="06", ENV{ID_MM_UBLOX_PORT_READY_DELAY}="20" ATTRS{idVendor}=="1546", ATTRS{idProduct}=="1010", ENV{.MM_USBIFNUM}=="08", ENV{ID_MM_PORT_IGNORE}="1" # TOBY-L200 # Wait up to 20s before probing AT ports ATTRS{idVendor}=="1546", ATTRS{idProduct}=="1141", ENV{ID_MM_UBLOX_PORT_READY_DELAY}="20" ATTRS{idVendor}=="1546", ATTRS{idProduct}=="1143", ENV{ID_MM_UBLOX_PORT_READY_DELAY}="20" ATTRS{idVendor}=="1546", ATTRS{idProduct}=="1146", ENV{ID_MM_UBLOX_PORT_READY_DELAY}="20" # TOBY-R2 port types # ttyACM0 (if #0): primary # ttyACM1 (if #2): secondary # ttyACM2 (if #4): tertiary # ttyACM3 (if #6): GNSS Tunneling (ignore) # ttyACM4 (if #8): SIM Access Profile (ignore) # ttyACM5 (if #10): Primary Log for diagnostics (ignore) ATTRS{idVendor}=="1546", ATTRS{idProduct}=="1107", ENV{.MM_USBIFNUM}=="06", ENV{ID_MM_PORT_IGNORE}="1" ATTRS{idVendor}=="1546", ATTRS{idProduct}=="1107", ENV{.MM_USBIFNUM}=="08", ENV{ID_MM_PORT_IGNORE}="1" ATTRS{idVendor}=="1546", ATTRS{idProduct}=="1107", ENV{.MM_USBIFNUM}=="0a", ENV{ID_MM_PORT_IGNORE}="1" # LARA-R2 port types # ttyACM0 (if #0): primary # ttyACM1 (if #2): secondary # ttyACM2 (if #4): tertiary # ttyACM3 (if #6): GNSS Tunneling (ignore) # ttyACM4 (if #8): SIM Access Profile (ignore) # ttyACM5 (if #10): Primary Log for diagnostics (ignore) ATTRS{idVendor}=="1546", ATTRS{idProduct}=="110a", ENV{.MM_USBIFNUM}=="06", ENV{ID_MM_PORT_IGNORE}="1" ATTRS{idVendor}=="1546", ATTRS{idProduct}=="110a", ENV{.MM_USBIFNUM}=="08", ENV{ID_MM_PORT_IGNORE}="1" ATTRS{idVendor}=="1546", ATTRS{idProduct}=="110a", ENV{.MM_USBIFNUM}=="0a", ENV{ID_MM_PORT_IGNORE}="1" # LISA-U2 / SARA-U2 port types # ttyACM0 (if #0): primary # ttyACM1 (if #2): secondary # ttyACM2 (if #4): tertiary # ttyACM3 (if #6): GNSS Tunneling (ignore) # ttyACM4 (if #8): Primary Log for diagnostics (ignore) # ttyACM5 (if #10): Secondary Log for diagnostics (ignore) # ttyACM6 (if #12): SAP (SIM Access Profile) (ignore) ATTRS{idVendor}=="1546", ATTRS{idProduct}=="1102", ENV{.MM_USBIFNUM}=="06", ENV{ID_MM_PORT_IGNORE}="1" ATTRS{idVendor}=="1546", ATTRS{idProduct}=="1102", ENV{.MM_USBIFNUM}=="08", ENV{ID_MM_PORT_IGNORE}="1" ATTRS{idVendor}=="1546", ATTRS{idProduct}=="1102", ENV{.MM_USBIFNUM}=="0a", ENV{ID_MM_PORT_IGNORE}="1" ATTRS{idVendor}=="1546", ATTRS{idProduct}=="1102", ENV{.MM_USBIFNUM}=="0c", ENV{ID_MM_PORT_IGNORE}="1" # LISA-U2 / SARA-U2 (alternative configuration) port types # ttyACM0 (if #0): primary # ttyACM1 (if #2): GNSS Tunneling (ignore) # ttyACM2 (if #4): Primary Log for diagnostics (ignore) # ttyACM3 (if #6): SAP (SIM Access Profile) (ignore) ATTRS{idVendor}=="1546", ATTRS{idProduct}=="1104", ENV{.MM_USBIFNUM}=="02", ENV{ID_MM_PORT_IGNORE}="1" ATTRS{idVendor}=="1546", ATTRS{idProduct}=="1104", ENV{.MM_USBIFNUM}=="04", ENV{ID_MM_PORT_IGNORE}="1" ATTRS{idVendor}=="1546", ATTRS{idProduct}=="1104", ENV{.MM_USBIFNUM}=="06", ENV{ID_MM_PORT_IGNORE}="1" GOTO="mm_ublox_port_types_end" LABEL="mm_qualcomm_port_types" SUBSYSTEMS=="usb", ATTRS{bInterfaceNumber}=="?*", ENV{.MM_USBIFNUM}="$attr{bInterfaceNumber}" # LARA-R6001 / LARA-R6001D port types # ttyUSB0 (if #0): diagnostic log (ignore) # ttyUSB1 (if #1): primary # ttyUSB2 (if #2): secondary # ttyUSB3 (if #3): unused (ignore) ATTRS{idVendor}=="05c6", ATTRS{idProduct}=="908b", ENV{.MM_USBIFNUM}=="00", ENV{ID_MM_PORT_IGNORE}="1" ATTRS{idVendor}=="05c6", ATTRS{idProduct}=="908b", ENV{.MM_USBIFNUM}=="01", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1" ATTRS{idVendor}=="05c6", ATTRS{idProduct}=="908b", ENV{.MM_USBIFNUM}=="02", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1" ATTRS{idVendor}=="05c6", ATTRS{idProduct}=="908b", ENV{.MM_USBIFNUM}=="03", ENV{ID_MM_PORT_IGNORE}="1" LABEL="mm_ublox_port_types_end" ModemManager-1.23.4-dev/src/plugins/ublox/README000066400000000000000000000160251456466623000212120ustar00rootroot00000000000000 The 'ublox' plugin is originally targeted for the u-blox TOBY-L2 series, although it may be used with other kind of devices likely without many issues. ===================================== USB profiles and networking modes ===================================== The TOBY-L2 devices may work in multiple different USB profiles: * AT+UUSBCONF=0: fairly back-compatible profile, where only cdc-acm TTYs are exposed. ModemManager will default to PPP for the connection setup when in this profile. * AT+UUSBCONF=2: ECM profile, where multiple cdc-acm TTYs are exposed along with a ECM network interface. * AT+UUSBCONF=3: RNDIS profile, where one cdc-acm TTY and a RNDIS network interface are exposed. This is the default factory-programmed value. When a profile with a network interface (ECM or RNDIS) is in use, the device may work in multiple networking modes: * AT+UBMCONF=1: Router mode, with a built-in DHCP server running behind the network interface. The network interface will be assigned an IP address from a subnet managed by the device itself. This is the default factory-programmed value. E.g.: $ ip addr 9: usb0: mtu 1500 qdisc fq_codel state UNKNOWN group default qlen 1000 link/ether 02:07:01:15:00:0b brd ff:ff:ff:ff:ff:ff inet 192.168.1.100/24 brd 192.168.1.255 scope global dynamic usb0 valid_lft 43009sec preferred_lft 43009sec $ ip route default via 192.168.1.1 dev usb0 proto static metric 700 192.168.1.0/24 dev usb0 proto kernel scope link src 192.168.1.100 metric 700 * AT+UBMCONF=2: Bridge mode, where static IP addressing and routing must be performed once connected. The network interface will be assigned the same IP address provided by the network operator. The plugin uses 'AT+UIPADDR=N' the default gateway IP settings and 'AT++CGCONTRDP=N' to retrieve the interface IP settings and DNS setup. $ ip addr 11: usb0: mtu 1500 qdisc fq_codel state UNKNOWN group default qlen 1000 link/ether 02:07:01:15:00:0b brd ff:ff:ff:ff:ff:ff inet 47.59.109.26/32 brd 47.59.109.26 scope global usb0 valid_lft forever preferred_lft forever $ ip route default via 47.59.109.229 dev usb0 proto static metric 700 47.59.109.26 dev usb0 proto kernel scope link src 47.59.109.26 metric 700 47.59.109.229 dev usb0 proto static scope link metric 700 The 'ublox' plugin in ModemManager works with any of the previous combinations seamlessly. It is assumed that the device doesn't change either USB profile or networking mode once it has been detected and processed by ModemManager. NOTE: If manually selecting different USB profiles or networking modes, remember to reset the module before assuming the new settings have been applied. E.g., using plain mmcli commands: $ sudo mmcli -m 0 --command="AT+UUSBCONF=3" $ sudo mmcli -m 0 --reset ================================= Connection setup ================================= The plugin allows to connect to specific APNs in the usual way (i.e. by creating a PDP context for the specific APN), and then activating the PDP context with 'AT+CGACT=[CID]'. Authentication settings of the APN (user, password, authentication type) are also supported via the 'AT+UAUTHREQ' command. The plugin doesn't currently support reporting as auto-connected the default LTE bearer. ======================================== Connection monitoring and statistics ======================================== The status of the connection of the specific PDP context is monitored periodically using 'AT+CGACT?', in order to detect network-originated disconnections. This implementation is given in the Generic broadband bearer implementation, and is not ublox-specific. If the device supports it, connection TX/RX statistics will also be periodically loaded using the AT+UGCNTRD command. Note, though, that the TOBY-L2 doesn't seem to support this information via control commands. =========================================== Supported and current mode combinations =========================================== The full list of supported mode combinations is loaded using 'AT+URAT=?', and then filtered by device product name to remove technologies not supported in several devices. E.g. the standard TOBY-L2 list of supported mode combinations will include all 2G, 3G and 4G, but if the device is a L201, 2G support will be removed from the list. The current mode combination in use is loaded using 'AT+URAT?', and the setting may be changed using the 'AT+URAT=X' request. In order to be able to update this setting, the device will be put in low-power mode ('AT+CFUN=4'), then the setting update will be run, and finally the device will recover the previous functionality mode it was in (e.g. 'AT+CFUN=1' if it was in full functionality mode). =============================== Supported and current bands =============================== The full list of supported bands is hardcoded based on the supported modes of the device. There is no runtime loading of which are the supported bands because the 'AT+UBANDSEL=?' command gives different results depending on the current access technology (i.e. there is no single full list of supported bands reported). The current list of bands is loaded via the 'AT+UBANDSEL?' command, and the setting may be changed using the 'AT+UBANDSEL=X' request. In order to be able to update this setting, the device will be put in low-power mode ('AT+CFUN=4'), then the setting update will be run, and finally the device will recover the previous functionality mode it was in (e.g. 'AT+CFUN=1' if it was in full functionality mode). ====================== Functionality mode ====================== The plugin implements a custom 'AT+CFUN?' response parser because it provides multiple modes that may be treated as 'low-power' by ModemManager (e.g. mode '0' is minimum functionality, mode '4' is airplane mode and mode '19' is minimum functionality with SIM deactivated). The plugin implements power-on ('AT+CFUN=1'), power-down ('AT+CFUN=4'), reset ('AT+CFUN=16') and power-off ('AT+CPWROFF'). As usual, a reset will trigger a power cycle of the device, and the power-off will render the modem unusable until it's power cycled externally. ==================================== Network registration and quality ==================================== The LTE specific 'AT+CEREG' registration checks will be enabled by default if the module supports LTE. Additionally, a custom 'CREG/CGREG/CEREG' state number parser is provided to support u-blox specific reported states (e.g. state '6' to indicate 'sms only registration' on the '+CREG' indications for the CS network when on LTE). The plugin implements the extended Signal interface, providing RSSI, RSCP, Ec/Io and RSRQ measurements obtained with the 'AT+CESQ' command. ================== PIN management ================== A custom method to load the PIN/PUK remaining attempts is implemented based on the 'AT+UPINCNT' command. Have fun! ModemManager-1.23.4-dev/src/plugins/ublox/mm-broadband-bearer-ublox.c000066400000000000000000001102431456466623000254030ustar00rootroot00000000000000/* -*- 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) 2016 Aleksander Morgado */ #include #include #include #include #include #include #include #include #define _LIBMM_INSIDE_MM #include #include "mm-broadband-bearer-ublox.h" #include "mm-base-modem-at.h" #include "mm-log-object.h" #include "mm-ublox-enums-types.h" #include "mm-modem-helpers.h" #include "mm-modem-helpers-ublox.h" G_DEFINE_TYPE (MMBroadbandBearerUblox, mm_broadband_bearer_ublox, MM_TYPE_BROADBAND_BEARER) enum { PROP_0, PROP_USB_PROFILE, PROP_NETWORKING_MODE, PROP_LAST }; static GParamSpec *properties[PROP_LAST]; struct _MMBroadbandBearerUbloxPrivate { MMUbloxUsbProfile profile; MMUbloxNetworkingMode mode; MMUbloxBearerAllowedAuth allowed_auths; FeatureSupport statistics; FeatureSupport cedata; }; /*****************************************************************************/ /* Common connection context and task */ typedef struct { MMBroadbandModem *modem; MMPortSerialAt *primary; MMPort *data; guint cid; gboolean auth_required; MMBearerIpConfig *ip_config; /* For IPv4 settings */ } CommonConnectContext; static void common_connect_context_free (CommonConnectContext *ctx) { if (ctx->ip_config) g_object_unref (ctx->ip_config); if (ctx->data) g_object_unref (ctx->data); g_object_unref (ctx->modem); g_object_unref (ctx->primary); g_slice_free (CommonConnectContext, ctx); } static GTask * common_connect_task_new (MMBroadbandBearerUblox *self, MMBroadbandModem *modem, MMPortSerialAt *primary, guint cid, MMPort *data, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { CommonConnectContext *ctx; GTask *task; ctx = g_slice_new0 (CommonConnectContext); ctx->modem = g_object_ref (modem); ctx->primary = g_object_ref (primary); ctx->cid = cid; task = g_task_new (self, cancellable, callback, user_data); g_task_set_task_data (task, ctx, (GDestroyNotify) common_connect_context_free); /* We need a net data port */ if (data) ctx->data = g_object_ref (data); else { ctx->data = mm_base_modem_get_best_data_port (MM_BASE_MODEM (modem), MM_PORT_TYPE_NET); if (!ctx->data) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_NOT_FOUND, "No valid data port found to launch connection"); g_object_unref (task); return NULL; } } return task; } /*****************************************************************************/ /* 3GPP IP config (sub-step of the 3GPP Connection sequence) */ static gboolean get_ip_config_3gpp_finish (MMBroadbandBearer *self, GAsyncResult *res, MMBearerIpConfig **ipv4_config, MMBearerIpConfig **ipv6_config, GError **error) { MMBearerConnectResult *configs; MMBearerIpConfig *ipv4; configs = g_task_propagate_pointer (G_TASK (res), error); if (!configs) return FALSE; /* Just IPv4 for now */ ipv4 = mm_bearer_connect_result_peek_ipv4_config (configs); g_assert (ipv4); if (ipv4_config) *ipv4_config = g_object_ref (ipv4); if (ipv6_config) *ipv6_config = NULL; mm_bearer_connect_result_unref (configs); return TRUE; } static void complete_get_ip_config_3gpp (GTask *task) { CommonConnectContext *ctx; ctx = g_task_get_task_data (task); g_assert (mm_bearer_ip_config_get_method (ctx->ip_config) != MM_BEARER_IP_METHOD_UNKNOWN); g_task_return_pointer (task, mm_bearer_connect_result_new (ctx->data, ctx->ip_config, NULL), (GDestroyNotify) mm_bearer_connect_result_unref); g_object_unref (task); } static void cgcontrdp_ready (MMBaseModem *modem, GAsyncResult *res, GTask *task) { MMBroadbandBearerUblox *self; const gchar *response; GError *error = NULL; CommonConnectContext *ctx; gchar *local_address = NULL; gchar *subnet = NULL; gchar *dns_addresses[3] = { NULL, NULL, NULL }; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); response = mm_base_modem_at_command_finish (modem, res, &error); if (!response || !mm_3gpp_parse_cgcontrdp_response (response, NULL, /* cid */ NULL, /* bearer id */ NULL, /* apn */ &local_address, &subnet, NULL, /* gateway_address */ &dns_addresses[0], &dns_addresses[1], &error)) { g_task_return_error (task, error); g_object_unref (task); return; } mm_obj_dbg (self, "IPv4 address retrieved: %s", local_address); mm_bearer_ip_config_set_address (ctx->ip_config, local_address); mm_obj_dbg (self, "IPv4 subnet retrieved: %s", subnet); mm_bearer_ip_config_set_prefix (ctx->ip_config, mm_netmask_to_cidr (subnet)); if (dns_addresses[0]) mm_obj_dbg (self, "primary DNS retrieved: %s", dns_addresses[0]); if (dns_addresses[1]) mm_obj_dbg (self, "secondary DNS retrieved: %s", dns_addresses[1]); mm_bearer_ip_config_set_dns (ctx->ip_config, (const gchar **) dns_addresses); g_free (local_address); g_free (subnet); g_free (dns_addresses[0]); g_free (dns_addresses[1]); mm_obj_dbg (self, "finished IP settings retrieval for PDP context #%u...", ctx->cid); complete_get_ip_config_3gpp (task); } static void uipaddr_ready (MMBaseModem *modem, GAsyncResult *res, GTask *task) { MMBroadbandBearerUblox *self; const gchar *response; gchar *cmd; GError *error = NULL; CommonConnectContext *ctx; gchar *gw_ipv4_address = NULL; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); response = mm_base_modem_at_command_finish (modem, res, &error); if (!response || !mm_ublox_parse_uipaddr_response (response, NULL, /* cid */ NULL, /* if_name */ &gw_ipv4_address, NULL, /* ipv4_subnet */ NULL, /* ipv6_global_address */ NULL, /* ipv6_link_local_address */ &error)) { g_task_return_error (task, error); g_object_unref (task); return; } mm_obj_dbg (self, "IPv4 gateway address retrieved: %s", gw_ipv4_address); mm_bearer_ip_config_set_gateway (ctx->ip_config, gw_ipv4_address); g_free (gw_ipv4_address); cmd = g_strdup_printf ("+CGCONTRDP=%u", ctx->cid); mm_obj_dbg (self, "gathering IP and DNS information for PDP context #%u...", ctx->cid); mm_base_modem_at_command (MM_BASE_MODEM (modem), cmd, 10, FALSE, (GAsyncReadyCallback) cgcontrdp_ready, task); g_free (cmd); } static void get_ip_config_3gpp (MMBroadbandBearer *_self, MMBroadbandModem *modem, MMPortSerialAt *primary, MMPortSerialAt *secondary, MMPort *data, guint cid, MMBearerIpFamily ip_family, GAsyncReadyCallback callback, gpointer user_data) { MMBroadbandBearerUblox *self = MM_BROADBAND_BEARER_UBLOX (_self); GTask *task; CommonConnectContext *ctx; if (!(task = common_connect_task_new (MM_BROADBAND_BEARER_UBLOX (self), MM_BROADBAND_MODEM (modem), primary, cid, data, NULL, callback, user_data))) return; ctx = g_task_get_task_data (task); ctx->ip_config = mm_bearer_ip_config_new (); /* If we're in BRIDGE mode, we need to ask for static IP addressing details: * - AT+UIPADDR=[CID] will give us the default gateway address. * - +CGCONTRDP?[CID] will give us the IP address, subnet and DNS addresses. */ if (self->priv->mode == MM_UBLOX_NETWORKING_MODE_BRIDGE) { gchar *cmd; mm_bearer_ip_config_set_method (ctx->ip_config, MM_BEARER_IP_METHOD_STATIC); cmd = g_strdup_printf ("+UIPADDR=%u", cid); mm_obj_dbg (self, "gathering gateway information for PDP context #%u...", cid); mm_base_modem_at_command (MM_BASE_MODEM (modem), cmd, 10, FALSE, (GAsyncReadyCallback) uipaddr_ready, task); g_free (cmd); return; } /* If we're in ROUTER networking mode, we just need to request DHCP on the * network interface. Early return with that result. */ if (self->priv->mode == MM_UBLOX_NETWORKING_MODE_ROUTER) { mm_bearer_ip_config_set_method (ctx->ip_config, MM_BEARER_IP_METHOD_DHCP); complete_get_ip_config_3gpp (task); return; } g_assert_not_reached (); } /*****************************************************************************/ /* 3GPP Dialing (sub-step of the 3GPP Connection sequence) */ static MMPort * dial_3gpp_finish (MMBroadbandBearer *self, GAsyncResult *res, GError **error) { return MM_PORT (g_task_propagate_pointer (G_TASK (res), error)); } static void cedata_activate_ready (MMBaseModem *modem, GAsyncResult *res, MMBroadbandBearerUblox *self) { const gchar *response; GError *error = NULL; response = mm_base_modem_at_command_finish (modem, res, &error); if (!response) { mm_obj_warn (self, "ECM data connection attempt failed: %s", error->message); mm_base_bearer_report_connection_status (MM_BASE_BEARER (self), MM_BEARER_CONNECTION_STATUS_DISCONNECTED); g_error_free (error); } /* we received a full bearer object reference */ g_object_unref (self); } static void cgact_activate_ready (MMBaseModem *modem, GAsyncResult *res, GTask *task) { const gchar *response; GError *error = NULL; CommonConnectContext *ctx; ctx = g_task_get_task_data (task); response = mm_base_modem_at_command_finish (modem, res, &error); if (!response) g_task_return_error (task, error); else g_task_return_pointer (task, g_object_ref (ctx->data), g_object_unref); g_object_unref (task); } static void activate_3gpp (GTask *task) { MMBroadbandBearerUblox *self; CommonConnectContext *ctx; g_autofree gchar *cmd = NULL; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); /* SARA-U2xx and LISA-U20x only expose one CDC-ECM interface. Hence, * the fixed 0 as the interface index here. When we see modems with * multiple interfaces, this needs to be revisited. */ if (self->priv->profile == MM_UBLOX_USB_PROFILE_ECM && self->priv->cedata == FEATURE_SUPPORTED) { cmd = g_strdup_printf ("+UCEDATA=%u,0", ctx->cid); mm_obj_dbg (self, "establishing ECM data connection for PDP context #%u...", ctx->cid); mm_base_modem_at_command (MM_BASE_MODEM (ctx->modem), cmd, MM_BASE_BEARER_DEFAULT_CONNECTION_TIMEOUT, FALSE, (GAsyncReadyCallback) cedata_activate_ready, g_object_ref (self)); /* We'll mark the task done here since the modem expects the DHCP discover packet while +UCEDATA runs. If the command fails, we'll mark the bearer disconnected later in the callback. */ g_task_return_pointer (task, g_object_ref (ctx->data), g_object_unref); g_object_unref (task); return; } cmd = g_strdup_printf ("+CGACT=1,%u", ctx->cid); mm_obj_dbg (self, "activating PDP context #%u...", ctx->cid); mm_base_modem_at_command (MM_BASE_MODEM (ctx->modem), cmd, MM_BASE_BEARER_DEFAULT_CONNECTION_TIMEOUT, FALSE, (GAsyncReadyCallback) cgact_activate_ready, task); } static void test_cedata_ready (MMBaseModem *modem, GAsyncResult *res, GTask *task) { MMBroadbandBearerUblox *self; const gchar *response; self = g_task_get_source_object (task); response = mm_base_modem_at_command_finish (modem, res, NULL); if (response) self->priv->cedata = FEATURE_SUPPORTED; else self->priv->cedata = FEATURE_UNSUPPORTED; mm_obj_dbg (self, "+UCEDATA command%s available", (self->priv->cedata == FEATURE_SUPPORTED) ? "" : " not"); activate_3gpp (task); } static void test_cedata (GTask *task) { MMBroadbandBearerUblox *self; CommonConnectContext *ctx; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); /* We don't need to test for +UCEDATA if we're not using CDC-ECM or if we have tested before. Instead, we jump right to the activation. */ if (self->priv->profile != MM_UBLOX_USB_PROFILE_ECM || self->priv->cedata != FEATURE_SUPPORT_UNKNOWN) { activate_3gpp (task); return; } mm_obj_dbg (self, "checking availability of +UCEDATA command..."); mm_base_modem_at_command (MM_BASE_MODEM (ctx->modem), "+UCEDATA=?", 3, TRUE, /* allow_cached */ (GAsyncReadyCallback) test_cedata_ready, task); } static void uauthreq_ready (MMBaseModem *modem, GAsyncResult *res, GTask *task) { const gchar *response; GError *error = NULL; response = mm_base_modem_at_command_finish (modem, res, &error); if (!response) { CommonConnectContext *ctx; ctx = g_task_get_task_data (task); /* If authentication required and the +UAUTHREQ failed, abort */ if (ctx->auth_required) { g_task_return_error (task, error); g_object_unref (task); return; } /* Otherwise, ignore */ g_error_free (error); } test_cedata (task); } static void authenticate_3gpp (GTask *task) { MMBroadbandBearerUblox *self; CommonConnectContext *ctx; g_autofree gchar *cmd = NULL; MMBearerAllowedAuth allowed_auth; gint ublox_auth = -1; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); allowed_auth = mm_bearer_properties_get_allowed_auth (mm_base_bearer_peek_config (MM_BASE_BEARER (self))); if (!ctx->auth_required) { mm_obj_dbg (self, "not using authentication"); ublox_auth = 0; goto out; } if (allowed_auth == MM_BEARER_ALLOWED_AUTH_UNKNOWN || allowed_auth == (MM_BEARER_ALLOWED_AUTH_PAP | MM_BEARER_ALLOWED_AUTH_CHAP)) { mm_obj_dbg (self, "using automatic authentication method"); if (self->priv->allowed_auths & MM_UBLOX_BEARER_ALLOWED_AUTH_AUTO) ublox_auth = 3; else if (self->priv->allowed_auths & MM_UBLOX_BEARER_ALLOWED_AUTH_CHAP) ublox_auth = 2; else if (self->priv->allowed_auths & MM_UBLOX_BEARER_ALLOWED_AUTH_PAP) ublox_auth = 1; else if (self->priv->allowed_auths & MM_UBLOX_BEARER_ALLOWED_AUTH_NONE) ublox_auth = 0; } else if (allowed_auth & MM_BEARER_ALLOWED_AUTH_PAP) { mm_obj_dbg (self, "using PAP authentication method"); ublox_auth = 1; } else if (allowed_auth & MM_BEARER_ALLOWED_AUTH_CHAP) { mm_obj_dbg (self, "using CHAP authentication method"); ublox_auth = 2; } out: if (ublox_auth < 0) { g_autofree gchar *str = NULL; str = mm_bearer_allowed_auth_build_string_from_mask (allowed_auth); g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "Cannot use any of the specified authentication methods (%s)", str); g_object_unref (task); return; } if (ublox_auth > 0) { const gchar *user; const gchar *password; g_autofree gchar *quoted_user = NULL; g_autofree gchar *quoted_password = NULL; user = mm_bearer_properties_get_user (mm_base_bearer_peek_config (MM_BASE_BEARER (self))); password = mm_bearer_properties_get_password (mm_base_bearer_peek_config (MM_BASE_BEARER (self))); quoted_user = mm_port_serial_at_quote_string (user); quoted_password = mm_port_serial_at_quote_string (password); cmd = g_strdup_printf ("+UAUTHREQ=%u,%u,%s,%s", ctx->cid, ublox_auth, quoted_user, quoted_password); } else cmd = g_strdup_printf ("+UAUTHREQ=%u,0,\"\",\"\"", ctx->cid); mm_obj_dbg (self, "setting up authentication preferences in PDP context #%u...", ctx->cid); mm_base_modem_at_command (MM_BASE_MODEM (ctx->modem), cmd, 10, FALSE, (GAsyncReadyCallback) uauthreq_ready, task); } static void uauthreq_test_ready (MMBaseModem *modem, GAsyncResult *res, GTask *task) { MMBroadbandBearerUblox *self; const gchar *response; GError *error = NULL; self = g_task_get_source_object (task); response = mm_base_modem_at_command_finish (modem, res, &error); if (!response) goto out; self->priv->allowed_auths = mm_ublox_parse_uauthreq_test (response, self, &error); out: if (error) { CommonConnectContext *ctx; ctx = g_task_get_task_data (task); /* If authentication required and the +UAUTHREQ test failed, abort */ if (ctx->auth_required) { g_task_return_error (task, error); g_object_unref (task); return; } /* Otherwise, ignore and jump to test_cedata directly as no auth setup * is needed */ g_error_free (error); test_cedata (task); return; } authenticate_3gpp (task); } static void check_supported_authentication_methods (GTask *task) { MMBroadbandBearerUblox *self; CommonConnectContext *ctx; const gchar *user; const gchar *password; MMBearerAllowedAuth allowed_auth; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); user = mm_bearer_properties_get_user (mm_base_bearer_peek_config (MM_BASE_BEARER (self))); password = mm_bearer_properties_get_password (mm_base_bearer_peek_config (MM_BASE_BEARER (self))); allowed_auth = mm_bearer_properties_get_allowed_auth (mm_base_bearer_peek_config (MM_BASE_BEARER (self))); /* Flag whether authentication is required. If it isn't, we won't fail * connection attempt if the +UAUTHREQ command fails */ ctx->auth_required = (user && password && allowed_auth != MM_BEARER_ALLOWED_AUTH_NONE); /* If we already cached the support, not do it again */ if (self->priv->allowed_auths != MM_UBLOX_BEARER_ALLOWED_AUTH_UNKNOWN) { authenticate_3gpp (task); return; } mm_obj_dbg (self, "checking supported authentication methods..."); mm_base_modem_at_command (MM_BASE_MODEM (ctx->modem), "+UAUTHREQ=?", 10, TRUE, /* allow cached */ (GAsyncReadyCallback) uauthreq_test_ready, task); } static void dial_3gpp (MMBroadbandBearer *self, MMBaseModem *modem, MMPortSerialAt *primary, guint cid, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; if (!(task = common_connect_task_new (MM_BROADBAND_BEARER_UBLOX (self), MM_BROADBAND_MODEM (modem), primary, cid, NULL, /* data, unused */ cancellable, callback, user_data))) return; check_supported_authentication_methods (task); } /*****************************************************************************/ /* 3GPP disconnection */ static gboolean disconnect_3gpp_finish (MMBroadbandBearer *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void cgact_deactivate_ready (MMBaseModem *modem, GAsyncResult *res, GTask *task) { MMBroadbandBearerUblox *self; const gchar *response; GError *error = NULL; self = g_task_get_source_object (task); response = mm_base_modem_at_command_finish (modem, res, &error); if (!response) { /* TOBY-L4 and TOBY-L2 L2 don't allow to disconnect the last LTE bearer * as that would imply de-registration from the LTE network, so we just * assume that it's disconnected from the user point of view. * * TOBY-L4 reports this as a generic unknown Packet Domain Error, which * is a bit unfortunate: * AT+CGACT=0,1 * +CME ERROR: 148 * * TOBY-L2 reports this as "LAST PDN disconnection not allowed" but using * the legacy numeric value before 3GPP Rel 11 (i.e. 151 instead of 171). * AT+CGACT=0,1 * +CME ERROR: 151 */ if (!g_error_matches (error, MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_GPRS_UNKNOWN) && !g_error_matches (error, MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_LAST_PDN_DISCONNECTION_NOT_ALLOWED) && !g_error_matches (error, MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_LAST_PDN_DISCONNECTION_NOT_ALLOWED_LEGACY) && !g_error_matches (error, MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_SIM_NOT_INSERTED)) { g_task_return_error (task, error); g_object_unref (task); return; } mm_obj_dbg (self, "ignored error when disconnecting last LTE bearer: %s", error->message); g_clear_error (&error); } g_task_return_boolean (task, TRUE); g_object_unref (task); } static void disconnect_3gpp (MMBroadbandBearer *self, MMBroadbandModem *modem, MMPortSerialAt *primary, MMPortSerialAt *secondary, MMPort *data, guint cid, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; g_autofree gchar *cmd = NULL; if (!(task = common_connect_task_new (MM_BROADBAND_BEARER_UBLOX (self), MM_BROADBAND_MODEM (modem), primary, cid, data, NULL, callback, user_data))) return; cmd = g_strdup_printf ("+CGACT=0,%u", cid); mm_obj_dbg (self, "deactivating PDP context #%u...", cid); mm_base_modem_at_command (MM_BASE_MODEM (modem), cmd, MM_BASE_BEARER_DEFAULT_DISCONNECTION_TIMEOUT, FALSE, (GAsyncReadyCallback) cgact_deactivate_ready, task); } /*****************************************************************************/ /* Reload statistics */ typedef struct { guint64 bytes_rx; guint64 bytes_tx; } StatsResult; static gboolean reload_stats_finish (MMBaseBearer *self, guint64 *bytes_rx, guint64 *bytes_tx, GAsyncResult *res, GError **error) { StatsResult *result; result = g_task_propagate_pointer (G_TASK (res), error); if (!result) return FALSE; if (bytes_rx) *bytes_rx = result->bytes_rx; if (bytes_tx) *bytes_tx = result->bytes_tx; g_free (result); return TRUE; } static void ugcntrd_ready (MMBaseModem *modem, GAsyncResult *res, GTask *task) { MMBroadbandBearerUblox *self; const gchar *response; GError *error = NULL; guint64 tx_bytes = 0; guint64 rx_bytes = 0; gint cid; self = MM_BROADBAND_BEARER_UBLOX (g_task_get_source_object (task)); cid = mm_base_bearer_get_profile_id (MM_BASE_BEARER (self)); response = mm_base_modem_at_command_finish (modem, res, &error); if (response) { if (cid == MM_3GPP_PROFILE_ID_UNKNOWN) error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Unknown profile id"); else mm_ublox_parse_ugcntrd_response_for_cid (response, cid, &tx_bytes, &rx_bytes, NULL, NULL, &error); } if (error) { g_prefix_error (&error, "Couldn't load PDP context %u statistics: ", cid); g_task_return_error (task, error); } else { StatsResult *result; result = g_new (StatsResult, 1); result->bytes_rx = rx_bytes; result->bytes_tx = tx_bytes; g_task_return_pointer (task, result, g_free); } g_object_unref (task); } static void run_reload_stats (MMBroadbandBearerUblox *self, GTask *task) { /* Unsupported? */ if (self->priv->statistics == FEATURE_UNSUPPORTED) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "Loading statistics isn't supported by this device"); g_object_unref (task); return; } /* Supported */ if (self->priv->statistics == FEATURE_SUPPORTED) { MMBaseModem *modem = NULL; g_object_get (MM_BASE_BEARER (self), MM_BASE_BEARER_MODEM, &modem, NULL); mm_base_modem_at_command (MM_BASE_MODEM (modem), "+UGCNTRD", 3, FALSE, (GAsyncReadyCallback) ugcntrd_ready, task); g_object_unref (modem); return; } g_assert_not_reached (); } static void ugcntrd_test_ready (MMBaseModem *modem, GAsyncResult *res, GTask *task) { MMBroadbandBearerUblox *self; self = MM_BROADBAND_BEARER_UBLOX (g_task_get_source_object (task)); if (!mm_base_modem_at_command_finish (modem, res, NULL)) self->priv->statistics = FEATURE_UNSUPPORTED; else self->priv->statistics = FEATURE_SUPPORTED; run_reload_stats (self, task); } static void reload_stats (MMBaseBearer *self, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; task = g_task_new (self, NULL, callback, user_data); if (MM_BROADBAND_BEARER_UBLOX (self)->priv->statistics == FEATURE_SUPPORT_UNKNOWN) { MMBaseModem *modem = NULL; g_object_get (MM_BASE_BEARER (self), MM_BASE_BEARER_MODEM, &modem, NULL); mm_base_modem_at_command (MM_BASE_MODEM (modem), "+UGCNTRD=?", 3, FALSE, (GAsyncReadyCallback) ugcntrd_test_ready, task); g_object_unref (modem); return; } run_reload_stats (MM_BROADBAND_BEARER_UBLOX (self), task); } /*****************************************************************************/ MMBaseBearer * mm_broadband_bearer_ublox_new_finish (GAsyncResult *res, GError **error) { GObject *source; GObject *bearer; source = g_async_result_get_source_object (res); bearer = g_async_initable_new_finish (G_ASYNC_INITABLE (source), res, error); g_object_unref (source); if (!bearer) return NULL; /* Only export valid bearers */ mm_base_bearer_export (MM_BASE_BEARER (bearer)); return MM_BASE_BEARER (bearer); } void mm_broadband_bearer_ublox_new (MMBroadbandModem *modem, MMUbloxUsbProfile profile, MMUbloxNetworkingMode mode, MMBearerProperties *config, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { g_assert (mode == MM_UBLOX_NETWORKING_MODE_ROUTER || mode == MM_UBLOX_NETWORKING_MODE_BRIDGE); g_async_initable_new_async ( MM_TYPE_BROADBAND_BEARER_UBLOX, G_PRIORITY_DEFAULT, cancellable, callback, user_data, MM_BASE_BEARER_MODEM, modem, MM_BASE_BEARER_CONFIG, config, MM_BROADBAND_BEARER_UBLOX_USB_PROFILE, profile, MM_BROADBAND_BEARER_UBLOX_NETWORKING_MODE, mode, NULL); } static void set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { MMBroadbandBearerUblox *self = MM_BROADBAND_BEARER_UBLOX (object); switch (prop_id) { case PROP_USB_PROFILE: self->priv->profile = g_value_get_enum (value); break; case PROP_NETWORKING_MODE: self->priv->mode = g_value_get_enum (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) { MMBroadbandBearerUblox *self = MM_BROADBAND_BEARER_UBLOX (object); switch (prop_id) { case PROP_USB_PROFILE: g_value_set_enum (value, self->priv->profile); break; case PROP_NETWORKING_MODE: g_value_set_enum (value, self->priv->mode); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void mm_broadband_bearer_ublox_init (MMBroadbandBearerUblox *self) { /* Initialize private data */ self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, MM_TYPE_BROADBAND_BEARER_UBLOX, MMBroadbandBearerUbloxPrivate); /* Defaults */ self->priv->profile = MM_UBLOX_USB_PROFILE_UNKNOWN; self->priv->mode = MM_UBLOX_NETWORKING_MODE_UNKNOWN; self->priv->allowed_auths = MM_UBLOX_BEARER_ALLOWED_AUTH_UNKNOWN; self->priv->statistics = FEATURE_SUPPORT_UNKNOWN; self->priv->cedata = FEATURE_SUPPORT_UNKNOWN; } static void mm_broadband_bearer_ublox_class_init (MMBroadbandBearerUbloxClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); MMBaseBearerClass *base_bearer_class = MM_BASE_BEARER_CLASS (klass); MMBroadbandBearerClass *broadband_bearer_class = MM_BROADBAND_BEARER_CLASS (klass); g_type_class_add_private (object_class, sizeof (MMBroadbandBearerUbloxPrivate)); object_class->get_property = get_property; object_class->set_property = set_property; /* Note: the ublox plugin uses the generic AT+CGACT? based check to monitor * the connection status (i.e. default load_connection_status()) */ base_bearer_class->reload_stats = reload_stats; base_bearer_class->reload_stats_finish = reload_stats_finish; broadband_bearer_class->disconnect_3gpp = disconnect_3gpp; broadband_bearer_class->disconnect_3gpp_finish = disconnect_3gpp_finish; broadband_bearer_class->dial_3gpp = dial_3gpp; broadband_bearer_class->dial_3gpp_finish = dial_3gpp_finish; broadband_bearer_class->get_ip_config_3gpp = get_ip_config_3gpp; broadband_bearer_class->get_ip_config_3gpp_finish = get_ip_config_3gpp_finish; properties[PROP_USB_PROFILE] = g_param_spec_enum (MM_BROADBAND_BEARER_UBLOX_USB_PROFILE, "USB profile", "USB profile in use", MM_TYPE_UBLOX_USB_PROFILE, MM_UBLOX_USB_PROFILE_UNKNOWN, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY); g_object_class_install_property (object_class, PROP_USB_PROFILE, properties[PROP_USB_PROFILE]); properties[PROP_NETWORKING_MODE] = g_param_spec_enum (MM_BROADBAND_BEARER_UBLOX_NETWORKING_MODE, "Networking mode", "Networking mode in use", MM_TYPE_UBLOX_NETWORKING_MODE, MM_UBLOX_NETWORKING_MODE_UNKNOWN, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY); g_object_class_install_property (object_class, PROP_NETWORKING_MODE, properties[PROP_NETWORKING_MODE]); } ModemManager-1.23.4-dev/src/plugins/ublox/mm-broadband-bearer-ublox.h000066400000000000000000000060271456466623000254140ustar00rootroot00000000000000/* -*- 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) 2016 Aleksander Morgado */ #ifndef MM_BROADBAND_BEARER_UBLOX_H #define MM_BROADBAND_BEARER_UBLOX_H #include #include #define _LIBMM_INSIDE_MM #include #include "mm-broadband-bearer.h" #include "mm-modem-helpers-ublox.h" #define MM_TYPE_BROADBAND_BEARER_UBLOX (mm_broadband_bearer_ublox_get_type ()) #define MM_BROADBAND_BEARER_UBLOX(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_BROADBAND_BEARER_UBLOX, MMBroadbandBearerUblox)) #define MM_BROADBAND_BEARER_UBLOX_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_BROADBAND_BEARER_UBLOX, MMBroadbandBearerUbloxClass)) #define MM_IS_BROADBAND_BEARER_UBLOX(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_BROADBAND_BEARER_UBLOX)) #define MM_IS_BROADBAND_BEARER_UBLOX_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_BROADBAND_BEARER_UBLOX)) #define MM_BROADBAND_BEARER_UBLOX_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_BROADBAND_BEARER_UBLOX, MMBroadbandBearerUbloxClass)) #define MM_BROADBAND_BEARER_UBLOX_USB_PROFILE "broadband-bearer-ublox-usb-profile" #define MM_BROADBAND_BEARER_UBLOX_NETWORKING_MODE "broadband-bearer-ublox-networking-mode" typedef struct _MMBroadbandBearerUblox MMBroadbandBearerUblox; typedef struct _MMBroadbandBearerUbloxClass MMBroadbandBearerUbloxClass; typedef struct _MMBroadbandBearerUbloxPrivate MMBroadbandBearerUbloxPrivate; struct _MMBroadbandBearerUblox { MMBroadbandBearer parent; MMBroadbandBearerUbloxPrivate *priv; }; struct _MMBroadbandBearerUbloxClass { MMBroadbandBearerClass parent; }; GType mm_broadband_bearer_ublox_get_type (void); void mm_broadband_bearer_ublox_new (MMBroadbandModem *modem, MMUbloxUsbProfile profile, MMUbloxNetworkingMode mode, MMBearerProperties *config, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); MMBaseBearer *mm_broadband_bearer_ublox_new_finish (GAsyncResult *res, GError **error); #endif /* MM_BROADBAND_BEARER_UBLOX_H */ ModemManager-1.23.4-dev/src/plugins/ublox/mm-broadband-modem-ublox.c000066400000000000000000002177041456466623000252560ustar00rootroot00000000000000/* -*- 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) 2016-2019 Aleksander Morgado */ #include #include #include #include #include #include #include "ModemManager.h" #include "mm-log-object.h" #include "mm-iface-modem.h" #include "mm-iface-modem-3gpp.h" #include "mm-iface-modem-voice.h" #include "mm-base-modem-at.h" #include "mm-broadband-bearer.h" #include "mm-broadband-modem-ublox.h" #include "mm-broadband-bearer-ublox.h" #include "mm-sim-ublox.h" #include "mm-modem-helpers-ublox.h" #include "mm-ublox-enums-types.h" static void iface_modem_init (MMIfaceModem *iface); static void iface_modem_voice_init (MMIfaceModemVoice *iface); static MMIfaceModemVoice *iface_modem_voice_parent; G_DEFINE_TYPE_EXTENDED (MMBroadbandModemUblox, mm_broadband_modem_ublox, MM_TYPE_BROADBAND_MODEM, 0, G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM, iface_modem_init) G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_VOICE, iface_modem_voice_init)) struct _MMBroadbandModemUbloxPrivate { /* USB profile in use */ MMUbloxUsbProfile profile; gboolean profile_checked; /* Networking mode in use */ MMUbloxNetworkingMode mode; gboolean mode_checked; /* Flag to specify whether a power operation is ongoing */ gboolean power_operation_ongoing; /* Mode combination to apply if "any" requested */ MMModemMode any_allowed; /* AT command configuration */ UbloxSupportConfig support_config; /* Voice +UCALLSTAT support */ GRegex *ucallstat_regex; FeatureSupport udtmfd_support; GRegex *udtmfd_regex; /* Regex to ignore */ GRegex *pbready_regex; }; /*****************************************************************************/ /* Per-model configuration loading */ static void preload_support_config (MMBroadbandModemUblox *self) { const gchar *model; GError *error = NULL; /* Make sure we load only once */ if (self->priv->support_config.loaded) return; model = mm_iface_modem_get_model (MM_IFACE_MODEM (self)); if (!mm_ublox_get_support_config (model, &self->priv->support_config, &error)) { mm_obj_warn (self, "loading support configuration failed: %s", error->message); g_error_free (error); /* default to NOT SUPPORTED if unknown model */ self->priv->support_config.method = SETTINGS_UPDATE_METHOD_UNKNOWN; self->priv->support_config.uact = FEATURE_UNSUPPORTED; self->priv->support_config.ubandsel = FEATURE_UNSUPPORTED; } else mm_obj_dbg (self, "support configuration found for '%s'", model); switch (self->priv->support_config.method) { case SETTINGS_UPDATE_METHOD_CFUN: mm_obj_dbg (self, " band update requires low-power mode"); break; case SETTINGS_UPDATE_METHOD_COPS: mm_obj_dbg (self, " band update requires explicit unregistration"); break; case SETTINGS_UPDATE_METHOD_UNKNOWN: /* not an error, this just means we don't need anything special */ break; default: g_assert_not_reached (); } switch (self->priv->support_config.uact) { case FEATURE_SUPPORTED: mm_obj_dbg (self, " UACT based band configuration supported"); break; case FEATURE_UNSUPPORTED: mm_obj_dbg (self, " UACT based band configuration unsupported"); break; case FEATURE_SUPPORT_UNKNOWN: default: g_assert_not_reached(); } switch (self->priv->support_config.ubandsel) { case FEATURE_SUPPORTED: mm_obj_dbg (self, " UBANDSEL based band configuration supported"); break; case FEATURE_UNSUPPORTED: mm_obj_dbg (self, " UBANDSEL based band configuration unsupported"); break; case FEATURE_SUPPORT_UNKNOWN: default: g_assert_not_reached(); } } /*****************************************************************************/ static gboolean acquire_power_operation (MMBroadbandModemUblox *self, GError **error) { if (self->priv->power_operation_ongoing) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_RETRY, "An operation which requires power updates is currently in progress"); return FALSE; } self->priv->power_operation_ongoing = TRUE; return TRUE; } static void release_power_operation (MMBroadbandModemUblox *self) { g_assert (self->priv->power_operation_ongoing); self->priv->power_operation_ongoing = FALSE; } /*****************************************************************************/ /* Load supported bands (Modem interface) */ static GArray * load_supported_bands_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { return (GArray *) g_task_propagate_pointer (G_TASK (res), error); } static void load_supported_bands (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; GError *error = NULL; GArray *bands = NULL; const gchar *model; model = mm_iface_modem_get_model (self); task = g_task_new (self, NULL, callback, user_data); bands = mm_ublox_get_supported_bands (model, self, &error); if (!bands) g_task_return_error (task, error); else g_task_return_pointer (task, bands, (GDestroyNotify) g_array_unref); g_object_unref (task); } /*****************************************************************************/ /* Load current bands (Modem interface) */ static GArray * load_current_bands_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { return (GArray *) g_task_propagate_pointer (G_TASK (res), error); } static void uact_load_current_bands_ready (MMBaseModem *self, GAsyncResult *res, GTask *task) { GError *error = NULL; const gchar *response; GArray *out; response = mm_base_modem_at_command_finish (self, res, &error); if (!response) { g_task_return_error (task, error); g_object_unref (task); return; } out = mm_ublox_parse_uact_response (response, &error); if (!out) { g_task_return_error (task, error); g_object_unref (task); return; } g_task_return_pointer (task, out, (GDestroyNotify)g_array_unref); g_object_unref (task); } static void ubandsel_load_current_bands_ready (MMBaseModem *self, GAsyncResult *res, GTask *task) { GError *error = NULL; const gchar *response; const gchar *model; GArray *out; response = mm_base_modem_at_command_finish (self, res, &error); if (!response) { g_task_return_error (task, error); g_object_unref (task); return; } model = mm_iface_modem_get_model (MM_IFACE_MODEM (self)); out = mm_ublox_parse_ubandsel_response (response, model, self, &error); if (!out) { g_task_return_error (task, error); g_object_unref (task); return; } g_task_return_pointer (task, out, (GDestroyNotify)g_array_unref); g_object_unref (task); } static void load_current_bands (MMIfaceModem *_self, GAsyncReadyCallback callback, gpointer user_data) { MMBroadbandModemUblox *self = MM_BROADBAND_MODEM_UBLOX (_self); GTask *task; preload_support_config (self); task = g_task_new (self, NULL, callback, user_data); if (self->priv->support_config.ubandsel == FEATURE_SUPPORTED) { mm_base_modem_at_command ( MM_BASE_MODEM (self), "+UBANDSEL?", 3, FALSE, (GAsyncReadyCallback)ubandsel_load_current_bands_ready, task); return; } if (self->priv->support_config.uact == FEATURE_SUPPORTED) { mm_base_modem_at_command ( MM_BASE_MODEM (self), "+UACT?", 3, FALSE, (GAsyncReadyCallback)uact_load_current_bands_ready, task); return; } g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "loading current bands is unsupported"); g_object_unref (task); } /*****************************************************************************/ /* Set allowed modes/bands (Modem interface) */ typedef enum { SET_CURRENT_MODES_BANDS_STEP_FIRST, SET_CURRENT_MODES_BANDS_STEP_ACQUIRE, SET_CURRENT_MODES_BANDS_STEP_CURRENT_POWER, SET_CURRENT_MODES_BANDS_STEP_BEFORE_COMMAND, SET_CURRENT_MODES_BANDS_STEP_COMMAND, SET_CURRENT_MODES_BANDS_STEP_AFTER_COMMAND, SET_CURRENT_MODES_BANDS_STEP_RELEASE, SET_CURRENT_MODES_BANDS_STEP_LAST, } SetCurrentModesBandsStep; typedef struct { SetCurrentModesBandsStep step; gchar *command; MMModemPowerState initial_state; GError *saved_error; } SetCurrentModesBandsContext; static void set_current_modes_bands_context_free (SetCurrentModesBandsContext *ctx) { g_assert (!ctx->saved_error); g_free (ctx->command); g_slice_free (SetCurrentModesBandsContext, ctx); } static void set_current_modes_bands_context_new (GTask *task, gchar *command) { SetCurrentModesBandsContext *ctx; ctx = g_slice_new0 (SetCurrentModesBandsContext); ctx->command = command; ctx->initial_state = MM_MODEM_POWER_STATE_UNKNOWN; ctx->step = SET_CURRENT_MODES_BANDS_STEP_FIRST; g_task_set_task_data (task, ctx, (GDestroyNotify) set_current_modes_bands_context_free); } static gboolean common_set_current_modes_bands_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void set_current_modes_bands_step (GTask *task); static void set_current_modes_bands_reregister_in_network_ready (MMIfaceModem3gpp *self, GAsyncResult *res, GTask *task) { SetCurrentModesBandsContext *ctx; ctx = g_task_get_task_data (task); /* propagate the error if none already set */ mm_iface_modem_3gpp_reregister_in_network_finish (self, res, ctx->saved_error ? NULL : &ctx->saved_error); /* Go to next step (release power operation) regardless of the result */ ctx->step++; set_current_modes_bands_step (task); } static void set_current_modes_bands_after_command_ready (MMBaseModem *self, GAsyncResult *res, GTask *task) { SetCurrentModesBandsContext *ctx; ctx = g_task_get_task_data (task); /* propagate the error if none already set */ mm_base_modem_at_command_finish (self, res, ctx->saved_error ? NULL : &ctx->saved_error); /* Go to next step (release power operation) regardless of the result */ ctx->step++; set_current_modes_bands_step (task); } static void set_current_modes_bands_command_ready (MMBaseModem *self, GAsyncResult *res, GTask *task) { SetCurrentModesBandsContext *ctx; ctx = g_task_get_task_data (task); if (!mm_base_modem_at_command_finish (self, res, &ctx->saved_error)) ctx->step = SET_CURRENT_MODES_BANDS_STEP_RELEASE; else ctx->step++; set_current_modes_bands_step (task); } static void set_current_modes_bands_before_command_ready (MMBaseModem *self, GAsyncResult *res, GTask *task) { SetCurrentModesBandsContext *ctx; ctx = g_task_get_task_data (task); if (!mm_base_modem_at_command_finish (self, res, &ctx->saved_error)) ctx->step = SET_CURRENT_MODES_BANDS_STEP_RELEASE; else ctx->step++; set_current_modes_bands_step (task); } static void set_current_modes_bands_current_power_ready (MMBaseModem *_self, GAsyncResult *res, GTask *task) { MMBroadbandModemUblox *self = MM_BROADBAND_MODEM_UBLOX (_self); SetCurrentModesBandsContext *ctx; const gchar *response; ctx = g_task_get_task_data (task); g_assert (self->priv->support_config.method == SETTINGS_UPDATE_METHOD_CFUN); response = mm_base_modem_at_command_finish (_self, res, &ctx->saved_error); if (!response || !mm_ublox_parse_cfun_response (response, &ctx->initial_state, &ctx->saved_error)) ctx->step = SET_CURRENT_MODES_BANDS_STEP_RELEASE; else ctx->step++; set_current_modes_bands_step (task); } static void set_current_modes_bands_step (GTask *task) { MMBroadbandModemUblox *self; SetCurrentModesBandsContext *ctx; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); switch (ctx->step) { case SET_CURRENT_MODES_BANDS_STEP_FIRST: ctx->step++; /* fall through */ case SET_CURRENT_MODES_BANDS_STEP_ACQUIRE: mm_obj_dbg (self, "acquiring power operation..."); if (!acquire_power_operation (self, &ctx->saved_error)) { ctx->step = SET_CURRENT_MODES_BANDS_STEP_LAST; set_current_modes_bands_step (task); return; } ctx->step++; /* fall through */ case SET_CURRENT_MODES_BANDS_STEP_CURRENT_POWER: /* If using CFUN, we check whether we're already in low-power mode. * And if we are, we just skip triggering low-power mode ourselves. */ if (self->priv->support_config.method == SETTINGS_UPDATE_METHOD_CFUN) { mm_obj_dbg (self, "checking current power operation..."); mm_base_modem_at_command (MM_BASE_MODEM (self), "+CFUN?", 3, FALSE, (GAsyncReadyCallback) set_current_modes_bands_current_power_ready, task); return; } ctx->step++; /* fall through */ case SET_CURRENT_MODES_BANDS_STEP_BEFORE_COMMAND: /* If COPS required around the set command, run it unconditionally */ if (self->priv->support_config.method == SETTINGS_UPDATE_METHOD_COPS) { mm_obj_dbg (self, "deregistering from the network for configuration change..."); mm_base_modem_at_command ( MM_BASE_MODEM (self), "+COPS=2", 10, FALSE, (GAsyncReadyCallback) set_current_modes_bands_before_command_ready, task); return; } /* If CFUN required, check initial state before triggering low-power mode ourselves */ else if (self->priv->support_config.method == SETTINGS_UPDATE_METHOD_CFUN) { /* Do nothing if already in low-power mode */ if (ctx->initial_state != MM_MODEM_POWER_STATE_LOW) { mm_obj_dbg (self, "powering down for configuration change..."); mm_base_modem_at_command ( MM_BASE_MODEM (self), "+CFUN=4", 3, FALSE, (GAsyncReadyCallback) set_current_modes_bands_before_command_ready, task); return; } } ctx->step++; /* fall through */ case SET_CURRENT_MODES_BANDS_STEP_COMMAND: mm_obj_dbg (self, "updating configuration..."); mm_base_modem_at_command ( MM_BASE_MODEM (self), ctx->command, 3, FALSE, (GAsyncReadyCallback) set_current_modes_bands_command_ready, task); return; case SET_CURRENT_MODES_BANDS_STEP_AFTER_COMMAND: /* If COPS required around the set command, run it unconditionally */ if (self->priv->support_config.method == SETTINGS_UPDATE_METHOD_COPS) { mm_iface_modem_3gpp_reregister_in_network (MM_IFACE_MODEM_3GPP (self), (GAsyncReadyCallback) set_current_modes_bands_reregister_in_network_ready, task); return; } /* If CFUN required, see if we need to recover power */ else if (self->priv->support_config.method == SETTINGS_UPDATE_METHOD_CFUN) { /* If we were in low-power mode before the change, do nothing, otherwise, * full power mode back */ if (ctx->initial_state != MM_MODEM_POWER_STATE_LOW) { mm_obj_dbg (self, "recovering power state after configuration change..."); mm_base_modem_at_command ( MM_BASE_MODEM (self), "+CFUN=1", 3, FALSE, (GAsyncReadyCallback) set_current_modes_bands_after_command_ready, task); return; } } ctx->step++; /* fall through */ case SET_CURRENT_MODES_BANDS_STEP_RELEASE: mm_obj_dbg (self, "releasing power operation..."); release_power_operation (self); ctx->step++; /* fall through */ case SET_CURRENT_MODES_BANDS_STEP_LAST: if (ctx->saved_error) { g_task_return_error (task, ctx->saved_error); ctx->saved_error = NULL; } else g_task_return_boolean (task, TRUE); g_object_unref (task); return; default: g_assert_not_reached (); } } static void set_current_modes (MMIfaceModem *self, MMModemMode allowed, MMModemMode preferred, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; gchar *command; GError *error = NULL; preload_support_config (MM_BROADBAND_MODEM_UBLOX (self)); task = g_task_new (self, NULL, callback, user_data); /* Handle ANY */ if (allowed == MM_MODEM_MODE_ANY) allowed = MM_BROADBAND_MODEM_UBLOX (self)->priv->any_allowed; /* Build command */ command = mm_ublox_build_urat_set_command (allowed, preferred, &error); if (!command) { g_task_return_error (task, error); g_object_unref (task); return; } set_current_modes_bands_context_new (task, command); set_current_modes_bands_step (task); } static void set_current_bands (MMIfaceModem *_self, GArray *bands_array, GAsyncReadyCallback callback, gpointer user_data) { MMBroadbandModemUblox *self = MM_BROADBAND_MODEM_UBLOX (_self); GTask *task; GError *error = NULL; gchar *command = NULL; const gchar *model; preload_support_config (self); task = g_task_new (self, NULL, callback, user_data); model = mm_iface_modem_get_model (_self); /* Build command */ if (self->priv->support_config.uact == FEATURE_SUPPORTED) command = mm_ublox_build_uact_set_command (bands_array, &error); else if (self->priv->support_config.ubandsel == FEATURE_SUPPORTED) command = mm_ublox_build_ubandsel_set_command (bands_array, model, &error); if (!command) { g_task_return_error (task, error); g_object_unref (task); return; } set_current_modes_bands_context_new (task, command); set_current_modes_bands_step (task); } /*****************************************************************************/ /* Load current modes (Modem interface) */ static gboolean load_current_modes_finish (MMIfaceModem *self, GAsyncResult *res, MMModemMode *allowed, MMModemMode *preferred, GError **error) { const gchar *response; response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, error); if (!response) return FALSE; return mm_ublox_parse_urat_read_response (response, self, allowed, preferred, error); } static void load_current_modes (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data) { mm_base_modem_at_command (MM_BASE_MODEM (self), "+URAT?", 3, FALSE, callback, user_data); } /*****************************************************************************/ /* Load supported modes (Modem interface) */ static GArray * load_supported_modes_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { const gchar *response; GArray *combinations; response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, error); if (!response) return FALSE; if (!(combinations = mm_ublox_parse_urat_test_response (response, self, error))) return FALSE; if (!(combinations = mm_ublox_filter_supported_modes (mm_iface_modem_get_model (self), combinations, self, error))) return FALSE; /* Decide and store which combination to apply when ANY requested */ MM_BROADBAND_MODEM_UBLOX (self)->priv->any_allowed = mm_ublox_get_modem_mode_any (combinations); /* If 4G supported, explicitly use +CEREG */ if (MM_BROADBAND_MODEM_UBLOX (self)->priv->any_allowed & MM_MODEM_MODE_4G) g_object_set (self, MM_IFACE_MODEM_3GPP_EPS_NETWORK_SUPPORTED, TRUE, NULL); return combinations; } static void load_supported_modes (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data) { mm_base_modem_at_command ( MM_BASE_MODEM (self), "+URAT=?", 3, TRUE, callback, user_data); } /*****************************************************************************/ /* Power state loading (Modem interface) */ static MMModemPowerState load_power_state_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { MMModemPowerState state = MM_MODEM_POWER_STATE_UNKNOWN; const gchar *response; response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, error); if (response) mm_ublox_parse_cfun_response (response, &state, error); return state; } static void load_power_state (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data) { mm_base_modem_at_command (MM_BASE_MODEM (self), "+CFUN?", 3, FALSE, callback, user_data); } /*****************************************************************************/ /* Modem power up/down/off (Modem interface) */ static gboolean common_modem_power_operation_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void power_operation_ready (MMBaseModem *self, GAsyncResult *res, GTask *task) { GError *error = NULL; release_power_operation (MM_BROADBAND_MODEM_UBLOX (self)); if (!mm_base_modem_at_command_finish (self, res, &error)) g_task_return_error (task, error); else g_task_return_boolean (task, TRUE); g_object_unref (task); } static void common_modem_power_operation (MMBroadbandModemUblox *self, const gchar *command, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; GError *error = NULL; task = g_task_new (self, NULL, callback, user_data); /* Fail if there is already an ongoing power management operation */ if (!acquire_power_operation (self, &error)) { g_task_return_error (task, error); g_object_unref (task); return; } /* Use AT+CFUN=4 for power down, puts device in airplane mode */ mm_base_modem_at_command (MM_BASE_MODEM (self), command, 30, FALSE, (GAsyncReadyCallback) power_operation_ready, task); } static void modem_reset (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data) { common_modem_power_operation (MM_BROADBAND_MODEM_UBLOX (self), "+CFUN=16", callback, user_data); } static void modem_power_off (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data) { common_modem_power_operation (MM_BROADBAND_MODEM_UBLOX (self), "+CPWROFF", callback, user_data); } static void modem_power_down (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data) { common_modem_power_operation (MM_BROADBAND_MODEM_UBLOX (self), "+CFUN=4", callback, user_data); } static void modem_power_up (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data) { common_modem_power_operation (MM_BROADBAND_MODEM_UBLOX (self), "+CFUN=1", callback, user_data); } /*****************************************************************************/ /* Load unlock retries (Modem interface) */ static MMUnlockRetries * load_unlock_retries_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { const gchar *response; MMUnlockRetries *retries; guint pin_attempts = 0; guint pin2_attempts = 0; guint puk_attempts = 0; guint puk2_attempts = 0; response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, error); if (!response || !mm_ublox_parse_upincnt_response (response, &pin_attempts, &pin2_attempts, &puk_attempts, &puk2_attempts, error)) return NULL; retries = mm_unlock_retries_new (); mm_unlock_retries_set (retries, MM_MODEM_LOCK_SIM_PIN, pin_attempts); mm_unlock_retries_set (retries, MM_MODEM_LOCK_SIM_PUK, puk_attempts); mm_unlock_retries_set (retries, MM_MODEM_LOCK_SIM_PIN2, pin2_attempts); mm_unlock_retries_set (retries, MM_MODEM_LOCK_SIM_PUK2, puk2_attempts); return retries; } static void load_unlock_retries (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data) { mm_base_modem_at_command (MM_BASE_MODEM (self), "+UPINCNT", 3, FALSE, callback, user_data); } /*****************************************************************************/ /* Common enable/disable voice unsolicited events */ typedef enum { VOICE_UNSOLICITED_EVENTS_STEP_FIRST, VOICE_UNSOLICITED_EVENTS_STEP_UCALLSTAT_PRIMARY, VOICE_UNSOLICITED_EVENTS_STEP_UCALLSTAT_SECONDARY, VOICE_UNSOLICITED_EVENTS_STEP_UDTMFD_PRIMARY, VOICE_UNSOLICITED_EVENTS_STEP_UDTMFD_SECONDARY, VOICE_UNSOLICITED_EVENTS_STEP_LAST, } VoiceUnsolicitedEventsStep; typedef struct { gboolean enable; VoiceUnsolicitedEventsStep step; MMPortSerialAt *primary; MMPortSerialAt *secondary; gchar *ucallstat_command; gchar *udtmfd_command; } VoiceUnsolicitedEventsContext; static void voice_unsolicited_events_context_free (VoiceUnsolicitedEventsContext *ctx) { g_clear_object (&ctx->secondary); g_clear_object (&ctx->primary); g_free (ctx->ucallstat_command); g_free (ctx->udtmfd_command); g_slice_free (VoiceUnsolicitedEventsContext, ctx); } static gboolean common_voice_enable_disable_unsolicited_events_finish (MMBroadbandModemUblox *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void voice_unsolicited_events_context_step (GTask *task); static void udtmfd_ready (MMBaseModem *self, GAsyncResult *res, GTask *task) { VoiceUnsolicitedEventsContext *ctx; GError *error = NULL; ctx = g_task_get_task_data (task); if (!mm_base_modem_at_command_full_finish (self, res, &error)) { mm_obj_dbg (self, "couldn't %s +UUDTMFD reporting: '%s'", ctx->enable ? "enable" : "disable", error->message); g_error_free (error); } ctx->step++; voice_unsolicited_events_context_step (task); } static void ucallstat_ready (MMBaseModem *self, GAsyncResult *res, GTask *task) { VoiceUnsolicitedEventsContext *ctx; GError *error = NULL; ctx = g_task_get_task_data (task); if (!mm_base_modem_at_command_full_finish (self, res, &error)) { mm_obj_dbg (self, "couldn't %s +UCALLSTAT reporting: '%s'", ctx->enable ? "enable" : "disable", error->message); g_error_free (error); } ctx->step++; voice_unsolicited_events_context_step (task); } static void voice_unsolicited_events_context_step (GTask *task) { MMBroadbandModemUblox *self; VoiceUnsolicitedEventsContext *ctx; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); switch (ctx->step) { case VOICE_UNSOLICITED_EVENTS_STEP_FIRST: ctx->step++; /* fall-through */ case VOICE_UNSOLICITED_EVENTS_STEP_UCALLSTAT_PRIMARY: if (ctx->primary) { mm_obj_dbg (self, "%s extended call status reporting in primary port...", ctx->enable ? "enabling" : "disabling"); mm_base_modem_at_command_full (MM_BASE_MODEM (self), ctx->primary, ctx->ucallstat_command, 3, FALSE, FALSE, NULL, (GAsyncReadyCallback)ucallstat_ready, task); return; } ctx->step++; /* fall-through */ case VOICE_UNSOLICITED_EVENTS_STEP_UCALLSTAT_SECONDARY: if (ctx->secondary) { mm_obj_dbg (self, "%s extended call status reporting in secondary port...", ctx->enable ? "enabling" : "disabling"); mm_base_modem_at_command_full (MM_BASE_MODEM (self), ctx->secondary, ctx->ucallstat_command, 3, FALSE, FALSE, NULL, (GAsyncReadyCallback)ucallstat_ready, task); return; } ctx->step++; /* fall-through */ case VOICE_UNSOLICITED_EVENTS_STEP_UDTMFD_PRIMARY: if ((self->priv->udtmfd_support == FEATURE_SUPPORTED) && (ctx->primary)) { mm_obj_dbg (self, "%s DTMF detection and reporting in primary port...", ctx->enable ? "enabling" : "disabling"); mm_base_modem_at_command_full (MM_BASE_MODEM (self), ctx->primary, ctx->udtmfd_command, 3, FALSE, FALSE, NULL, (GAsyncReadyCallback)udtmfd_ready, task); return; } ctx->step++; /* fall-through */ case VOICE_UNSOLICITED_EVENTS_STEP_UDTMFD_SECONDARY: if ((self->priv->udtmfd_support == FEATURE_SUPPORTED) && (ctx->secondary)) { mm_obj_dbg (self, "%s DTMF detection and reporting in secondary port...", ctx->enable ? "enabling" : "disabling"); mm_base_modem_at_command_full (MM_BASE_MODEM (self), ctx->secondary, ctx->udtmfd_command, 3, FALSE, FALSE, NULL, (GAsyncReadyCallback)udtmfd_ready, task); return; } ctx->step++; /* fall-through */ case VOICE_UNSOLICITED_EVENTS_STEP_LAST: g_task_return_boolean (task, TRUE); g_object_unref (task); return; default: g_assert_not_reached (); } } static void common_voice_enable_disable_unsolicited_events (MMBroadbandModemUblox *self, gboolean enable, GAsyncReadyCallback callback, gpointer user_data) { VoiceUnsolicitedEventsContext *ctx; GTask *task; task = g_task_new (self, NULL, callback, user_data); ctx = g_slice_new0 (VoiceUnsolicitedEventsContext); ctx->step = VOICE_UNSOLICITED_EVENTS_STEP_FIRST; ctx->enable = enable; if (enable) { ctx->ucallstat_command = g_strdup ("+UCALLSTAT=1"); ctx->udtmfd_command = g_strdup ("+UDTMFD=1,2"); } else { ctx->ucallstat_command = g_strdup ("+UCALLSTAT=0"); ctx->udtmfd_command = g_strdup ("+UDTMFD=0"); } ctx->primary = mm_base_modem_get_port_primary (MM_BASE_MODEM (self)); ctx->secondary = mm_base_modem_get_port_secondary (MM_BASE_MODEM (self)); g_task_set_task_data (task, ctx, (GDestroyNotify) voice_unsolicited_events_context_free); voice_unsolicited_events_context_step (task); } /*****************************************************************************/ /* SIM hot swap setup (Modem interface) */ typedef enum { CIEV_SIM_STATUS_UNKNOWN = -1, CIEV_SIM_STATUS_REMOVED, CIEV_SIM_STATUS_INSERTED, } MMUbloxSimInsertStatus; static gboolean modem_setup_sim_hot_swap_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void ublox_ciev_unsolicited_handler (MMPortSerialAt *port, GMatchInfo *match_info, MMBroadbandModemUblox *self) { gint sim_insert_status = CIEV_SIM_STATUS_UNKNOWN; if (!mm_get_int_from_match_info (match_info, 1, &sim_insert_status)) { mm_obj_dbg (self, "CIEV: unable to parse sim insert indication"); return; } mm_obj_msg (self, "CIEV: sim hot swap detected '%d'", sim_insert_status); if (sim_insert_status == CIEV_SIM_STATUS_INSERTED || sim_insert_status == CIEV_SIM_STATUS_REMOVED) { mm_iface_modem_process_sim_event (MM_IFACE_MODEM (self)); } else { mm_obj_warn (self, "(%s) CIEV: unable to determine sim insert status: %d", mm_port_get_device (MM_PORT (port)), sim_insert_status); } } static void ublox_setup_ciev_handler (MMIfaceModem *self, guint simind_idx) { g_autoptr(GRegex) pattern = NULL; g_autofree gchar *ciev_regex = NULL; MMPortSerialAt *primary_port; MMPortSerialAt *secondary_port; primary_port = mm_base_modem_peek_port_primary (MM_BASE_MODEM (self)); mm_obj_dbg (self, "setting up simind 'CIEV: %d' events handler", simind_idx); ciev_regex = g_strdup_printf ("\\r\\n\\+CIEV: %d,([0-1]{1})\\r\\n", simind_idx); pattern = g_regex_new (ciev_regex, G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); g_assert (pattern); mm_port_serial_at_add_unsolicited_msg_handler ( primary_port, pattern, (MMPortSerialAtUnsolicitedMsgFn) ublox_ciev_unsolicited_handler, self, NULL); secondary_port = mm_base_modem_get_port_secondary (MM_BASE_MODEM (self)); if (secondary_port) mm_port_serial_at_add_unsolicited_msg_handler ( secondary_port, pattern, (MMPortSerialAtUnsolicitedMsgFn) ublox_ciev_unsolicited_handler, self, NULL); } static void process_cind_verbosity_response (MMBaseModem *self, GAsyncResult *res, GTask *task) { g_autoptr(GError) error = NULL; mm_base_modem_at_command_finish (self, res, &error); if (error) { mm_obj_warn (self, "CIND: verbose mode is not configured: %s", error->message); g_task_return_error (task, g_steal_pointer (&error)); g_object_unref (task); return; } mm_obj_dbg (self, "CIND unsolicited response codes processing verbosity configured successfully"); if (!mm_broadband_modem_sim_hot_swap_ports_context_init (MM_BROADBAND_MODEM (self), &error)) mm_obj_warn (self, "failed to initialize SIM hot swap ports context: %s", error->message); g_task_return_boolean (task, TRUE); g_object_unref (task); } typedef struct { gchar *desc; guint idx; gint min; gint max; } CindResponse; static void cind_simind_format_check_ready (MMBroadbandModem *self, GAsyncResult *res, GTask *task) { GHashTable *indicators = NULL; GError *error = NULL; const gchar *result; CindResponse *r; result = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error); if (error || !(indicators = mm_3gpp_parse_cind_test_response (result, &error))) { mm_obj_dbg (self, "+CIND check failed: %s", error->message); g_prefix_error (&error, "CIND check failed: "); g_task_return_error (task, error); g_object_unref (task); return; } r = g_hash_table_lookup (indicators, "simind"); if (r) { mm_obj_dbg (self, "simind CIEV indications are supported, indication order number: %d", r->idx); ublox_setup_ciev_handler (MM_IFACE_MODEM (self), r->idx); mm_base_modem_at_command (MM_BASE_MODEM (self), "+CMER=1,0,0,1,0", 3, FALSE, (GAsyncReadyCallback) process_cind_verbosity_response, task); } else { mm_obj_dbg (self, "simind CIEV indications are not supported"); g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "simind CIEV indications are not supported"); g_object_unref (task); } g_hash_table_destroy (indicators); } static void modem_setup_sim_hot_swap (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; task = g_task_new (self, NULL, callback, user_data); mm_base_modem_at_command (MM_BASE_MODEM (self), "+CIND=?", 3, TRUE, (GAsyncReadyCallback) cind_simind_format_check_ready, task); } /*****************************************************************************/ /* SIM hot swap cleanup (Modem interface) */ static void modem_cleanup_sim_hot_swap (MMIfaceModem *self) { mm_broadband_modem_sim_hot_swap_ports_context_reset (MM_BROADBAND_MODEM (self)); } /*****************************************************************************/ /* Enabling unsolicited events (Voice interface) */ static gboolean modem_voice_enable_unsolicited_events_finish (MMIfaceModemVoice *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void voice_enable_unsolicited_events_ready (MMBroadbandModemUblox *self, GAsyncResult *res, GTask *task) { GError *error = NULL; if (!common_voice_enable_disable_unsolicited_events_finish (self, res, &error)) { mm_obj_warn (self, "Couldn't enable u-blox-specific voice unsolicited events: %s", error->message); g_error_free (error); } g_task_return_boolean (task, TRUE); g_object_unref (task); } static void parent_voice_enable_unsolicited_events_ready (MMIfaceModemVoice *self, GAsyncResult *res, GTask *task) { GError *error = NULL; if (!iface_modem_voice_parent->enable_unsolicited_events_finish (self, res, &error)) { g_task_return_error (task, error); g_object_unref (task); return; } common_voice_enable_disable_unsolicited_events (MM_BROADBAND_MODEM_UBLOX (self), TRUE, (GAsyncReadyCallback) voice_enable_unsolicited_events_ready, task); } static void modem_voice_enable_unsolicited_events (MMIfaceModemVoice *self, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; task = g_task_new (self, NULL, callback, user_data); /* Chain up parent's enable */ iface_modem_voice_parent->enable_unsolicited_events ( self, (GAsyncReadyCallback)parent_voice_enable_unsolicited_events_ready, task); } /*****************************************************************************/ /* Disabling unsolicited events (Voice interface) */ static gboolean modem_voice_disable_unsolicited_events_finish (MMIfaceModemVoice *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void parent_voice_disable_unsolicited_events_ready (MMIfaceModemVoice *self, GAsyncResult *res, GTask *task) { GError *error = NULL; if (!iface_modem_voice_parent->disable_unsolicited_events_finish (self, res, &error)) { g_task_return_error (task, error); g_object_unref (task); return; } g_task_return_boolean (task, TRUE); g_object_unref (task); } static void voice_disable_unsolicited_events_ready (MMBroadbandModemUblox *self, GAsyncResult *res, GTask *task) { GError *error = NULL; if (!common_voice_enable_disable_unsolicited_events_finish (self, res, &error)) { mm_obj_warn (self, "Couldn't disable u-blox-specific voice unsolicited events: %s", error->message); g_error_free (error); } iface_modem_voice_parent->disable_unsolicited_events ( MM_IFACE_MODEM_VOICE (self), (GAsyncReadyCallback)parent_voice_disable_unsolicited_events_ready, task); } static void modem_voice_disable_unsolicited_events (MMIfaceModemVoice *self, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; task = g_task_new (self, NULL, callback, user_data); common_voice_enable_disable_unsolicited_events (MM_BROADBAND_MODEM_UBLOX (self), FALSE, (GAsyncReadyCallback) voice_disable_unsolicited_events_ready, task); } /*****************************************************************************/ /* Common setup/cleanup voice unsolicited events */ static void ucallstat_received (MMPortSerialAt *port, GMatchInfo *match_info, MMBroadbandModemUblox *self) { static const MMCallState ublox_call_state[] = { [0] = MM_CALL_STATE_ACTIVE, [1] = MM_CALL_STATE_HELD, [2] = MM_CALL_STATE_DIALING, /* Dialing (MOC) */ [3] = MM_CALL_STATE_RINGING_OUT, /* Alerting (MOC) */ [4] = MM_CALL_STATE_RINGING_IN, /* Incoming (MTC) */ [5] = MM_CALL_STATE_WAITING, /* Waiting (MTC) */ [6] = MM_CALL_STATE_TERMINATED, [7] = MM_CALL_STATE_ACTIVE, /* Treated same way as ACTIVE */ }; MMCallInfo call_info = { 0 }; guint aux; if (!mm_get_uint_from_match_info (match_info, 1, &aux)) { mm_obj_warn (self, "couldn't parse call index from +UCALLSTAT"); return; } call_info.index = aux; if (!mm_get_uint_from_match_info (match_info, 2, &aux) || (aux >= G_N_ELEMENTS (ublox_call_state))) { mm_obj_warn (self, "couldn't parse call state from +UCALLSTAT"); return; } call_info.state = ublox_call_state[aux]; /* guess direction for some of the states */ switch (call_info.state) { case MM_CALL_STATE_DIALING: case MM_CALL_STATE_RINGING_OUT: call_info.direction = MM_CALL_DIRECTION_OUTGOING; break; case MM_CALL_STATE_RINGING_IN: case MM_CALL_STATE_WAITING: call_info.direction = MM_CALL_DIRECTION_INCOMING; break; case MM_CALL_STATE_UNKNOWN: case MM_CALL_STATE_ACTIVE: case MM_CALL_STATE_HELD: case MM_CALL_STATE_TERMINATED: default: call_info.direction = MM_CALL_DIRECTION_UNKNOWN; break; } mm_iface_modem_voice_report_call (MM_IFACE_MODEM_VOICE (self), &call_info); } static void udtmfd_received (MMPortSerialAt *port, GMatchInfo *match_info, MMBroadbandModemUblox *self) { g_autofree gchar *dtmf = NULL; dtmf = g_match_info_fetch (match_info, 1); mm_obj_dbg (self, "received DTMF: %s", dtmf); /* call index unknown */ mm_iface_modem_voice_received_dtmf (MM_IFACE_MODEM_VOICE (self), 0, dtmf); } static void common_voice_setup_cleanup_unsolicited_events (MMBroadbandModemUblox *self, gboolean enable) { MMPortSerialAt *ports[2]; guint i; if (G_UNLIKELY (!self->priv->ucallstat_regex)) self->priv->ucallstat_regex = g_regex_new ("\\r\\n\\+UCALLSTAT:\\s*(\\d+),(\\d+)\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); if (G_UNLIKELY (!self->priv->udtmfd_regex)) self->priv->udtmfd_regex = g_regex_new ("\\r\\n\\+UUDTMFD:\\s*([0-9A-D\\*\\#])\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); ports[0] = mm_base_modem_peek_port_primary (MM_BASE_MODEM (self)); ports[1] = mm_base_modem_peek_port_secondary (MM_BASE_MODEM (self)); for (i = 0; i < G_N_ELEMENTS (ports); i++) { if (!ports[i]) continue; mm_port_serial_at_add_unsolicited_msg_handler (ports[i], self->priv->ucallstat_regex, enable ? (MMPortSerialAtUnsolicitedMsgFn)ucallstat_received : NULL, enable ? self : NULL, NULL); mm_port_serial_at_add_unsolicited_msg_handler (ports[i], self->priv->udtmfd_regex, enable ? (MMPortSerialAtUnsolicitedMsgFn)udtmfd_received : NULL, enable ? self : NULL, NULL); } } /*****************************************************************************/ /* Cleanup unsolicited events (Voice interface) */ static gboolean modem_voice_cleanup_unsolicited_events_finish (MMIfaceModemVoice *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void parent_voice_cleanup_unsolicited_events_ready (MMIfaceModemVoice *self, GAsyncResult *res, GTask *task) { GError *error = NULL; if (!iface_modem_voice_parent->cleanup_unsolicited_events_finish (self, res, &error)) { mm_obj_warn (self, "Couldn't cleanup parent voice unsolicited events: %s", error->message); g_error_free (error); } g_task_return_boolean (task, TRUE); g_object_unref (task); } static void modem_voice_cleanup_unsolicited_events (MMIfaceModemVoice *self, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; task = g_task_new (self, NULL, callback, user_data); /* our own cleanup first */ common_voice_setup_cleanup_unsolicited_events (MM_BROADBAND_MODEM_UBLOX (self), FALSE); /* Chain up parent's cleanup */ iface_modem_voice_parent->cleanup_unsolicited_events ( self, (GAsyncReadyCallback)parent_voice_cleanup_unsolicited_events_ready, task); } /*****************************************************************************/ /* Setup unsolicited events (Voice interface) */ static gboolean modem_voice_setup_unsolicited_events_finish (MMIfaceModemVoice *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void parent_voice_setup_unsolicited_events_ready (MMIfaceModemVoice *self, GAsyncResult *res, GTask *task) { GError *error = NULL; if (!iface_modem_voice_parent->setup_unsolicited_events_finish (self, res, &error)) { mm_obj_warn (self, "Couldn't setup parent voice unsolicited events: %s", error->message); g_error_free (error); } /* our own setup next */ common_voice_setup_cleanup_unsolicited_events (MM_BROADBAND_MODEM_UBLOX (self), TRUE); g_task_return_boolean (task, TRUE); g_object_unref (task); } static void modem_voice_setup_unsolicited_events (MMIfaceModemVoice *self, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; task = g_task_new (self, NULL, callback, user_data); /* chain up parent's setup first */ iface_modem_voice_parent->setup_unsolicited_events ( self, (GAsyncReadyCallback)parent_voice_setup_unsolicited_events_ready, task); } /*****************************************************************************/ /* Create call (Voice interface) */ static MMBaseCall * create_call (MMIfaceModemVoice *self, MMCallDirection direction, const gchar *number) { return mm_base_call_new (MM_BASE_MODEM (self), direction, number, TRUE, /* skip_incoming_timeout */ TRUE, /* supports_dialing_to_ringing */ TRUE); /* supports_ringing_to_active */ } /*****************************************************************************/ /* Check if Voice supported (Voice interface) */ static gboolean modem_voice_check_support_finish (MMIfaceModemVoice *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void udtmfd_test_ready (MMBaseModem *_self, GAsyncResult *res, GTask *task) { MMBroadbandModemUblox *self = MM_BROADBAND_MODEM_UBLOX (_self); self->priv->udtmfd_support = (!!mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, NULL) ? FEATURE_SUPPORTED : FEATURE_UNSUPPORTED); g_task_return_boolean (task, TRUE); g_object_unref (task); } static void parent_voice_check_support_ready (MMIfaceModemVoice *self, GAsyncResult *res, GTask *task) { GError *error = NULL; if (!iface_modem_voice_parent->check_support_finish (self, res, &error)) { g_task_return_error (task, error); g_object_unref (task); return; } /* voice is supported, check if +UDTMFD is available */ mm_base_modem_at_command (MM_BASE_MODEM (self), "+UDTMFD=?", 3, TRUE, (GAsyncReadyCallback) udtmfd_test_ready, task); } static void modem_voice_check_support (MMIfaceModemVoice *self, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; task = g_task_new (self, NULL, callback, user_data); /* chain up parent's setup first */ iface_modem_voice_parent->check_support ( self, (GAsyncReadyCallback)parent_voice_check_support_ready, task); } /*****************************************************************************/ /* Create Bearer (Modem interface) */ typedef enum { CREATE_BEARER_STEP_FIRST, CREATE_BEARER_STEP_CHECK_PROFILE, CREATE_BEARER_STEP_CHECK_MODE, CREATE_BEARER_STEP_CREATE_BEARER, CREATE_BEARER_STEP_LAST, } CreateBearerStep; typedef struct { CreateBearerStep step; MMBearerProperties *properties; MMBaseBearer *bearer; gboolean has_net; } CreateBearerContext; static void create_bearer_context_free (CreateBearerContext *ctx) { g_clear_object (&ctx->bearer); g_object_unref (ctx->properties); g_slice_free (CreateBearerContext, ctx); } static MMBaseBearer * modem_create_bearer_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { return MM_BASE_BEARER (g_task_propagate_pointer (G_TASK (res), error)); } static void create_bearer_step (GTask *task); static void broadband_bearer_new_ready (GObject *source, GAsyncResult *res, GTask *task) { MMBroadbandModemUblox *self; CreateBearerContext *ctx; GError *error = NULL; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); g_assert (!ctx->bearer); ctx->bearer = mm_broadband_bearer_new_finish (res, &error); if (!ctx->bearer) { g_task_return_error (task, error); g_object_unref (task); return; } mm_obj_dbg (self, "new generic broadband bearer created at DBus path '%s'", mm_base_bearer_get_path (ctx->bearer)); ctx->step++; create_bearer_step (task); } static void broadband_bearer_ublox_new_ready (GObject *source, GAsyncResult *res, GTask *task) { MMBroadbandModemUblox *self; CreateBearerContext *ctx; GError *error = NULL; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); g_assert (!ctx->bearer); ctx->bearer = mm_broadband_bearer_ublox_new_finish (res, &error); if (!ctx->bearer) { g_task_return_error (task, error); g_object_unref (task); return; } mm_obj_dbg (self, "new u-blox broadband bearer created at DBus path '%s'", mm_base_bearer_get_path (ctx->bearer)); ctx->step++; create_bearer_step (task); } static void mode_check_ready (MMBaseModem *_self, GAsyncResult *res, GTask *task) { MMBroadbandModemUblox *self = MM_BROADBAND_MODEM_UBLOX (_self); const gchar *response; GError *error = NULL; CreateBearerContext *ctx; ctx = g_task_get_task_data (task); response = mm_base_modem_at_command_finish (_self, res, &error); if (!response) { mm_obj_dbg (self, "couldn't load current networking mode: %s", error->message); g_error_free (error); } else if (!mm_ublox_parse_ubmconf_response (response, &self->priv->mode, &error)) { mm_obj_dbg (self, "couldn't parse current networking mode response '%s': %s", response, error->message); g_error_free (error); } else { g_assert (self->priv->mode != MM_UBLOX_NETWORKING_MODE_UNKNOWN); mm_obj_dbg (self, "networking mode loaded: %s", mm_ublox_networking_mode_get_string (self->priv->mode)); } /* If checking networking mode isn't supported, we'll fallback to * assume the device is in router mode, which is the mode asking for * less connection setup rules from our side (just request DHCP). */ if (self->priv->mode == MM_UBLOX_NETWORKING_MODE_UNKNOWN && ctx->has_net) { mm_obj_dbg (self, "fallback to default networking mode: router"); self->priv->mode = MM_UBLOX_NETWORKING_MODE_ROUTER; } self->priv->mode_checked = TRUE; ctx->step++; create_bearer_step (task); } static void profile_check_ready (MMBaseModem *_self, GAsyncResult *res, GTask *task) { MMBroadbandModemUblox *self = MM_BROADBAND_MODEM_UBLOX (_self); const gchar *response; GError *error = NULL; CreateBearerContext *ctx; ctx = g_task_get_task_data (task); response = mm_base_modem_at_command_finish (_self, res, &error); if (!response) { mm_obj_dbg (self, "couldn't load current usb profile: %s", error->message); g_error_free (error); } else if (!mm_ublox_parse_uusbconf_response (response, &self->priv->profile, &error)) { mm_obj_dbg (self, "couldn't parse current usb profile response '%s': %s", response, error->message); g_error_free (error); } else { g_assert (self->priv->profile != MM_UBLOX_USB_PROFILE_UNKNOWN); mm_obj_dbg (self, "usb profile loaded: %s", mm_ublox_usb_profile_get_string (self->priv->profile)); } /* Assume the operation has been performed, even if it may have failed */ self->priv->profile_checked = TRUE; ctx->step++; create_bearer_step (task); } static void create_bearer_step (GTask *task) { MMBroadbandModemUblox *self; CreateBearerContext *ctx; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); switch (ctx->step) { case CREATE_BEARER_STEP_FIRST: ctx->step++; /* fall through */ case CREATE_BEARER_STEP_CHECK_PROFILE: if (!self->priv->profile_checked) { mm_obj_dbg (self, "checking current USB profile..."); mm_base_modem_at_command ( MM_BASE_MODEM (self), "+UUSBCONF?", 3, FALSE, (GAsyncReadyCallback) profile_check_ready, task); return; } ctx->step++; /* fall through */ case CREATE_BEARER_STEP_CHECK_MODE: if (!self->priv->mode_checked) { mm_obj_dbg (self, "checking current networking mode..."); mm_base_modem_at_command ( MM_BASE_MODEM (self), "+UBMCONF?", 3, FALSE, (GAsyncReadyCallback) mode_check_ready, task); return; } ctx->step++; /* fall through */ case CREATE_BEARER_STEP_CREATE_BEARER: /* If we have a net interface, we'll create a u-blox bearer, unless for * any reason we have the back-compatible profile selected. */ if ((self->priv->profile != MM_UBLOX_USB_PROFILE_BACK_COMPATIBLE) && ctx->has_net) { /* whenever there is a net port, we should have loaded a valid networking mode */ g_assert (self->priv->mode != MM_UBLOX_NETWORKING_MODE_UNKNOWN); mm_obj_dbg (self, "creating u-blox broadband bearer (%s profile, %s mode)...", mm_ublox_usb_profile_get_string (self->priv->profile), mm_ublox_networking_mode_get_string (self->priv->mode)); mm_broadband_bearer_ublox_new ( MM_BROADBAND_MODEM (self), self->priv->profile, self->priv->mode, ctx->properties, NULL, /* cancellable */ (GAsyncReadyCallback) broadband_bearer_ublox_new_ready, task); return; } /* If usb profile is back-compatible already, or if there is no NET port * available, create default generic bearer */ mm_obj_dbg (self, "creating generic broadband bearer..."); mm_broadband_bearer_new (MM_BROADBAND_MODEM (self), ctx->properties, NULL, /* cancellable */ (GAsyncReadyCallback) broadband_bearer_new_ready, task); return; case CREATE_BEARER_STEP_LAST: g_assert (ctx->bearer); g_task_return_pointer (task, g_object_ref (ctx->bearer), g_object_unref); g_object_unref (task); return; default: g_assert_not_reached (); } g_assert_not_reached (); } static void modem_create_bearer (MMIfaceModem *self, MMBearerProperties *properties, GAsyncReadyCallback callback, gpointer user_data) { CreateBearerContext *ctx; GTask *task; ctx = g_slice_new0 (CreateBearerContext); ctx->step = CREATE_BEARER_STEP_FIRST; ctx->properties = g_object_ref (properties); /* Flag whether this modem has exposed a network interface */ ctx->has_net = !!mm_base_modem_peek_best_data_port (MM_BASE_MODEM (self), MM_PORT_TYPE_NET); task = g_task_new (self, NULL, callback, user_data); g_task_set_task_data (task, ctx, (GDestroyNotify) create_bearer_context_free); create_bearer_step (task); } /*****************************************************************************/ /* Create SIM (Modem interface) */ static MMBaseSim * modem_create_sim_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { return mm_sim_ublox_new_finish (res, error); } static void modem_create_sim (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data) { mm_sim_ublox_new (MM_BASE_MODEM (self), NULL, /* cancellable */ callback, user_data); } /*****************************************************************************/ /* Setup ports (Broadband modem class) */ static void setup_ports (MMBroadbandModem *_self) { MMBroadbandModemUblox *self = MM_BROADBAND_MODEM_UBLOX (_self); MMPortSerialAt *ports[2]; guint i; /* Call parent's setup ports first always */ MM_BROADBAND_MODEM_CLASS (mm_broadband_modem_ublox_parent_class)->setup_ports (_self); ports[0] = mm_base_modem_peek_port_primary (MM_BASE_MODEM (self)); ports[1] = mm_base_modem_peek_port_secondary (MM_BASE_MODEM (self)); /* Configure AT ports */ for (i = 0; i < G_N_ELEMENTS (ports); i++) { if (!ports[i]) continue; g_object_set (ports[i], MM_PORT_SERIAL_SEND_DELAY, (guint64) 0, NULL); mm_port_serial_at_add_unsolicited_msg_handler ( ports[i], self->priv->pbready_regex, NULL, NULL, NULL); } } /*****************************************************************************/ MMBroadbandModemUblox * mm_broadband_modem_ublox_new (const gchar *device, const gchar *physdev, const gchar **drivers, const gchar *plugin, guint16 vendor_id, guint16 product_id) { return g_object_new (MM_TYPE_BROADBAND_MODEM_UBLOX, MM_BASE_MODEM_DEVICE, device, MM_BASE_MODEM_PHYSDEV, physdev, MM_BASE_MODEM_DRIVERS, drivers, MM_BASE_MODEM_PLUGIN, plugin, MM_BASE_MODEM_VENDOR_ID, vendor_id, MM_BASE_MODEM_PRODUCT_ID, product_id, MM_IFACE_MODEM_SIM_HOT_SWAP_SUPPORTED, TRUE, /* Generic bearer (TTY) and u-blox bearer (NET) supported */ MM_BASE_MODEM_DATA_NET_SUPPORTED, TRUE, MM_BASE_MODEM_DATA_TTY_SUPPORTED, TRUE, NULL); } static void mm_broadband_modem_ublox_init (MMBroadbandModemUblox *self) { /* Initialize private data */ self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, MM_TYPE_BROADBAND_MODEM_UBLOX, MMBroadbandModemUbloxPrivate); self->priv->profile = MM_UBLOX_USB_PROFILE_UNKNOWN; self->priv->mode = MM_UBLOX_NETWORKING_MODE_UNKNOWN; self->priv->any_allowed = MM_MODEM_MODE_NONE; self->priv->support_config.loaded = FALSE; self->priv->support_config.method = SETTINGS_UPDATE_METHOD_UNKNOWN; self->priv->support_config.uact = FEATURE_SUPPORT_UNKNOWN; self->priv->support_config.ubandsel = FEATURE_SUPPORT_UNKNOWN; self->priv->udtmfd_support = FEATURE_SUPPORT_UNKNOWN; self->priv->pbready_regex = g_regex_new ("\\r\\n\\+PBREADY\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); } static void iface_modem_init (MMIfaceModem *iface) { iface->create_sim = modem_create_sim; iface->create_sim_finish = modem_create_sim_finish; iface->create_bearer = modem_create_bearer; iface->create_bearer_finish = modem_create_bearer_finish; iface->load_unlock_retries = load_unlock_retries; iface->load_unlock_retries_finish = load_unlock_retries_finish; iface->load_power_state = load_power_state; iface->load_power_state_finish = load_power_state_finish; iface->modem_power_up = modem_power_up; iface->modem_power_up_finish = common_modem_power_operation_finish; iface->modem_power_down = modem_power_down; iface->modem_power_down_finish = common_modem_power_operation_finish; iface->modem_power_off = modem_power_off; iface->modem_power_off_finish = common_modem_power_operation_finish; iface->reset = modem_reset; iface->reset_finish = common_modem_power_operation_finish; iface->load_supported_modes = load_supported_modes; iface->load_supported_modes_finish = load_supported_modes_finish; iface->load_current_modes = load_current_modes; iface->load_current_modes_finish = load_current_modes_finish; iface->set_current_modes = set_current_modes; iface->set_current_modes_finish = common_set_current_modes_bands_finish; iface->load_supported_bands = load_supported_bands; iface->load_supported_bands_finish = load_supported_bands_finish; iface->load_current_bands = load_current_bands; iface->load_current_bands_finish = load_current_bands_finish; iface->set_current_bands = set_current_bands; iface->set_current_bands_finish = common_set_current_modes_bands_finish; iface->setup_sim_hot_swap = modem_setup_sim_hot_swap; iface->setup_sim_hot_swap_finish = modem_setup_sim_hot_swap_finish; iface->cleanup_sim_hot_swap = modem_cleanup_sim_hot_swap; } static void iface_modem_voice_init (MMIfaceModemVoice *iface) { iface_modem_voice_parent = g_type_interface_peek_parent (iface); iface->check_support = modem_voice_check_support; iface->check_support_finish = modem_voice_check_support_finish; iface->setup_unsolicited_events = modem_voice_setup_unsolicited_events; iface->setup_unsolicited_events_finish = modem_voice_setup_unsolicited_events_finish; iface->cleanup_unsolicited_events = modem_voice_cleanup_unsolicited_events; iface->cleanup_unsolicited_events_finish = modem_voice_cleanup_unsolicited_events_finish; iface->enable_unsolicited_events = modem_voice_enable_unsolicited_events; iface->enable_unsolicited_events_finish = modem_voice_enable_unsolicited_events_finish; iface->disable_unsolicited_events = modem_voice_disable_unsolicited_events; iface->disable_unsolicited_events_finish = modem_voice_disable_unsolicited_events_finish; iface->create_call = create_call; } static void finalize (GObject *object) { MMBroadbandModemUblox *self = MM_BROADBAND_MODEM_UBLOX (object); g_regex_unref (self->priv->pbready_regex); if (self->priv->ucallstat_regex) g_regex_unref (self->priv->ucallstat_regex); if (self->priv->udtmfd_regex) g_regex_unref (self->priv->udtmfd_regex); G_OBJECT_CLASS (mm_broadband_modem_ublox_parent_class)->finalize (object); } static void mm_broadband_modem_ublox_class_init (MMBroadbandModemUbloxClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); MMBroadbandModemClass *broadband_modem_class = MM_BROADBAND_MODEM_CLASS (klass); g_type_class_add_private (object_class, sizeof (MMBroadbandModemUbloxPrivate)); object_class->finalize = finalize; broadband_modem_class->setup_ports = setup_ports; } ModemManager-1.23.4-dev/src/plugins/ublox/mm-broadband-modem-ublox.h000066400000000000000000000046051456466623000252550ustar00rootroot00000000000000/* -*- 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) 2016 Aleksander Morgado */ #ifndef MM_BROADBAND_MODEM_UBLOX_H #define MM_BROADBAND_MODEM_UBLOX_H #include "mm-broadband-modem.h" #define MM_TYPE_BROADBAND_MODEM_UBLOX (mm_broadband_modem_ublox_get_type ()) #define MM_BROADBAND_MODEM_UBLOX(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_BROADBAND_MODEM_UBLOX, MMBroadbandModemUblox)) #define MM_BROADBAND_MODEM_UBLOX_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_BROADBAND_MODEM_UBLOX, MMBroadbandModemUbloxClass)) #define MM_IS_BROADBAND_MODEM_UBLOX(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_BROADBAND_MODEM_UBLOX)) #define MM_IS_BROADBAND_MODEM_UBLOX_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_BROADBAND_MODEM_UBLOX)) #define MM_BROADBAND_MODEM_UBLOX_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_BROADBAND_MODEM_UBLOX, MMBroadbandModemUbloxClass)) typedef struct _MMBroadbandModemUblox MMBroadbandModemUblox; typedef struct _MMBroadbandModemUbloxClass MMBroadbandModemUbloxClass; typedef struct _MMBroadbandModemUbloxPrivate MMBroadbandModemUbloxPrivate; struct _MMBroadbandModemUblox { MMBroadbandModem parent; MMBroadbandModemUbloxPrivate *priv; }; struct _MMBroadbandModemUbloxClass{ MMBroadbandModemClass parent; }; GType mm_broadband_modem_ublox_get_type (void); MMBroadbandModemUblox *mm_broadband_modem_ublox_new (const gchar *device, const gchar *physdev, const gchar **drivers, const gchar *plugin, guint16 vendor_id, guint16 product_id); #endif /* MM_BROADBAND_MODEM_UBLOX_H */ ModemManager-1.23.4-dev/src/plugins/ublox/mm-modem-helpers-ublox.c000066400000000000000000002273601456466623000250030ustar00rootroot00000000000000/* -*- 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) 2016 Aleksander Morgado */ #include #include #include "mm-log.h" #include "mm-modem-helpers.h" #include "mm-modem-helpers-ublox.h" /*****************************************************************************/ /* +UPINCNT response parser */ gboolean mm_ublox_parse_upincnt_response (const gchar *response, guint *out_pin_attempts, guint *out_pin2_attempts, guint *out_puk_attempts, guint *out_puk2_attempts, GError **error) { g_autoptr(GRegex) r = NULL; g_autoptr(GMatchInfo) match_info = NULL; GError *inner_error = NULL; guint pin_attempts = 0; guint pin2_attempts = 0; guint puk_attempts = 0; guint puk2_attempts = 0; gboolean success = TRUE; g_assert (out_pin_attempts); g_assert (out_pin2_attempts); g_assert (out_puk_attempts); g_assert (out_puk2_attempts); /* Response may be e.g.: * +UPINCNT: 3,3,10,10 */ r = g_regex_new ("\\+UPINCNT: (\\d+),(\\d+),(\\d+),(\\d+)(?:\\r\\n)?", 0, 0, NULL); g_assert (r != NULL); g_regex_match_full (r, response, strlen (response), 0, 0, &match_info, &inner_error); if (!inner_error && g_match_info_matches (match_info)) { if (!mm_get_uint_from_match_info (match_info, 1, &pin_attempts)) { inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "Couldn't parse PIN attempts"); goto out; } if (!mm_get_uint_from_match_info (match_info, 2, &pin2_attempts)) { inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "Couldn't parse PIN2 attempts"); goto out; } if (!mm_get_uint_from_match_info (match_info, 3, &puk_attempts)) { inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "Couldn't parse PUK attempts"); goto out; } if (!mm_get_uint_from_match_info (match_info, 4, &puk2_attempts)) { inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "Couldn't parse PUK2 attempts"); goto out; } success = TRUE; } out: if (inner_error) { g_propagate_error (error, inner_error); return FALSE; } if (!success) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't parse +UPINCNT response: '%s'", response); return FALSE; } *out_pin_attempts = pin_attempts; *out_pin2_attempts = pin2_attempts; *out_puk_attempts = puk_attempts; *out_puk2_attempts = puk2_attempts; return TRUE; } /*****************************************************************************/ /* UUSBCONF? response parser */ gboolean mm_ublox_parse_uusbconf_response (const gchar *response, MMUbloxUsbProfile *out_profile, GError **error) { g_autoptr(GRegex) r = NULL; g_autoptr(GMatchInfo) match_info = NULL; GError *inner_error = NULL; MMUbloxUsbProfile profile = MM_UBLOX_USB_PROFILE_UNKNOWN; g_assert (out_profile != NULL); /* Response may be e.g.: * +UUSBCONF: 3,"RNDIS",,"0x1146" * +UUSBCONF: 2,"ECM",,"0x1143" * +UUSBCONF: 0,"",,"0x1141" * * Note: we don't rely on the PID; assuming future new modules will * have a different PID but they may keep the profile names. */ r = g_regex_new ("\\+UUSBCONF: (\\d+),([^,]*),([^,]*),([^,]*)(?:\\r\\n)?", 0, 0, NULL); g_assert (r != NULL); g_regex_match_full (r, response, strlen (response), 0, 0, &match_info, &inner_error); if (!inner_error && g_match_info_matches (match_info)) { g_autofree gchar *profile_name = NULL; profile_name = mm_get_string_unquoted_from_match_info (match_info, 2); if (profile_name && profile_name[0]) { if (g_str_equal (profile_name, "RNDIS")) profile = MM_UBLOX_USB_PROFILE_RNDIS; else if (g_str_equal (profile_name, "ECM")) profile = MM_UBLOX_USB_PROFILE_ECM; else inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "Unknown USB profile: '%s'", profile_name); } else profile = MM_UBLOX_USB_PROFILE_BACK_COMPATIBLE; } if (inner_error) { g_propagate_error (error, inner_error); return FALSE; } if (profile == MM_UBLOX_USB_PROFILE_UNKNOWN) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't parse profile response"); return FALSE; } *out_profile = profile; return TRUE; } /*****************************************************************************/ /* UBMCONF? response parser */ gboolean mm_ublox_parse_ubmconf_response (const gchar *response, MMUbloxNetworkingMode *out_mode, GError **error) { g_autoptr(GRegex) r = NULL; g_autoptr(GMatchInfo) match_info = NULL; GError *inner_error = NULL; MMUbloxNetworkingMode mode = MM_UBLOX_NETWORKING_MODE_UNKNOWN; g_assert (out_mode != NULL); /* Response may be e.g.: * +UBMCONF: 1 * +UBMCONF: 2 */ r = g_regex_new ("\\+UBMCONF: (\\d+)(?:\\r\\n)?", 0, 0, NULL); g_assert (r != NULL); g_regex_match_full (r, response, strlen (response), 0, 0, &match_info, &inner_error); if (!inner_error && g_match_info_matches (match_info)) { guint mode_id = 0; if (mm_get_uint_from_match_info (match_info, 1, &mode_id)) { switch (mode_id) { case 1: mode = MM_UBLOX_NETWORKING_MODE_ROUTER; break; case 2: mode = MM_UBLOX_NETWORKING_MODE_BRIDGE; break; default: inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "Unknown mode id: '%u'", mode_id); break; } } } if (inner_error) { g_propagate_error (error, inner_error); return FALSE; } if (mode == MM_UBLOX_NETWORKING_MODE_UNKNOWN) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't parse networking mode response"); return FALSE; } *out_mode = mode; return TRUE; } /*****************************************************************************/ /* UIPADDR=N response parser */ gboolean mm_ublox_parse_uipaddr_response (const gchar *response, guint *out_cid, gchar **out_if_name, gchar **out_ipv4_address, gchar **out_ipv4_subnet, gchar **out_ipv6_global_address, gchar **out_ipv6_link_local_address, GError **error) { g_autoptr(GRegex) r = NULL; g_autoptr(GMatchInfo) match_info = NULL; GError *inner_error = NULL; guint cid = 0; g_autofree gchar *if_name = NULL; g_autofree gchar *ipv4_address = NULL; g_autofree gchar *ipv4_subnet = NULL; g_autofree gchar *ipv6_global_address = NULL; g_autofree gchar *ipv6_link_local_address = NULL; /* Response may be e.g.: * +UIPADDR: 1,"ccinet0","5.168.120.13","255.255.255.0","","" * +UIPADDR: 2,"ccinet1","","","2001::2:200:FF:FE00:0/64","FE80::200:FF:FE00:0/64" * +UIPADDR: 3,"ccinet2","5.10.100.2","255.255.255.0","2001::1:200:FF:FE00:0/64","FE80::200:FF:FE00:0/64" * * We assume only ONE line is returned; because we request +UIPADDR with a specific N CID. */ r = g_regex_new ("\\+UIPADDR: (\\d+),([^,]*),([^,]*),([^,]*),([^,]*),([^,]*)(?:\\r\\n)?", 0, 0, NULL); g_assert (r != NULL); g_regex_match_full (r, response, strlen (response), 0, 0, &match_info, &inner_error); if (inner_error) { g_propagate_error (error, inner_error); return FALSE; } if (!g_match_info_matches (match_info)) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS, "Couldn't match +UIPADDR response"); return FALSE; } if (out_cid && !mm_get_uint_from_match_info (match_info, 1, &cid)) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Error parsing cid"); return FALSE; } if (out_if_name && !(if_name = mm_get_string_unquoted_from_match_info (match_info, 2))) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Error parsing interface name"); return FALSE; } /* Remaining strings are optional */ ipv4_address = mm_get_string_unquoted_from_match_info (match_info, 3); ipv4_subnet = mm_get_string_unquoted_from_match_info (match_info, 4); ipv6_global_address = mm_get_string_unquoted_from_match_info (match_info, 5); ipv6_link_local_address = mm_get_string_unquoted_from_match_info (match_info, 6); if (out_cid) *out_cid = cid; if (out_if_name) *out_if_name = g_steal_pointer (&if_name); if (out_ipv4_address) *out_ipv4_address = g_steal_pointer (&ipv4_address); if (out_ipv4_subnet) *out_ipv4_subnet = g_steal_pointer (&ipv4_subnet); if (out_ipv6_global_address) *out_ipv6_global_address = g_steal_pointer (&ipv6_global_address); if (out_ipv6_link_local_address) *out_ipv6_link_local_address = g_steal_pointer (&ipv6_link_local_address); return TRUE; } /*****************************************************************************/ /* CFUN? response parser */ gboolean mm_ublox_parse_cfun_response (const gchar *response, MMModemPowerState *out_state, GError **error) { guint state; if (!mm_3gpp_parse_cfun_query_response (response, &state, error)) return FALSE; switch (state) { case 1: *out_state = MM_MODEM_POWER_STATE_ON; return TRUE; case 0: /* minimum functionality */ case 4: /* airplane mode */ case 19: /* minimum functionality with SIM deactivated */ *out_state = MM_MODEM_POWER_STATE_LOW; return TRUE; default: g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Unknown +CFUN state: %u", state); return FALSE; } } /*****************************************************************************/ /* URAT=? response parser */ /* Index of the array is the ublox-specific value */ static const MMModemMode ublox_combinations[] = { ( MM_MODEM_MODE_2G ), ( MM_MODEM_MODE_2G | MM_MODEM_MODE_3G ), ( MM_MODEM_MODE_3G ), ( MM_MODEM_MODE_4G ), ( MM_MODEM_MODE_2G | MM_MODEM_MODE_3G | MM_MODEM_MODE_4G ), ( MM_MODEM_MODE_2G | MM_MODEM_MODE_4G ), ( MM_MODEM_MODE_3G | MM_MODEM_MODE_4G ), ( MM_MODEM_MODE_4G ), ( MM_MODEM_MODE_4G ), }; GArray * mm_ublox_parse_urat_test_response (const gchar *response, gpointer log_object, GError **error) { GArray *combinations = NULL; GArray *selected = NULL; GArray *preferred = NULL; gchar **split; guint split_len; GError *inner_error = NULL; guint i; /* * E.g.: * AT+URAT=? * +URAT: (0-6),(0,2,3) */ response = mm_strip_tag (response, "+URAT:"); split = mm_split_string_groups (response); split_len = g_strv_length (split); if (split_len > 2 || split_len < 1) { inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Unexpected number of groups in +URAT=? response: %u", g_strv_length (split)); goto out; } /* The selected list must have values */ selected = mm_parse_uint_list (split[0], &inner_error); if (inner_error) goto out; if (!selected) { inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "No selected RAT values given in +URAT=? response"); goto out; } /* For our purposes, the preferred list may be empty */ preferred = mm_parse_uint_list (split[1], &inner_error); if (inner_error) goto out; /* Build array of combinations */ combinations = g_array_new (FALSE, FALSE, sizeof (MMModemModeCombination)); for (i = 0; i < selected->len; i++) { guint selected_value; MMModemModeCombination combination; guint j; selected_value = g_array_index (selected, guint, i); if (selected_value >= G_N_ELEMENTS (ublox_combinations)) { mm_obj_warn (log_object, "unexpected AcT value: %u", selected_value); continue; } /* Combination without any preferred */ combination.allowed = ublox_combinations[selected_value]; combination.preferred = MM_MODEM_MODE_NONE; g_array_append_val (combinations, combination); if (mm_count_bits_set (combination.allowed) == 1) continue; if (!preferred) continue; for (j = 0; j < preferred->len; j++) { guint preferred_value; preferred_value = g_array_index (preferred, guint, j); if (preferred_value >= G_N_ELEMENTS (ublox_combinations)) { mm_obj_warn (log_object, "unexpected AcT preferred value: %u", preferred_value); continue; } combination.preferred = ublox_combinations[preferred_value]; if (mm_count_bits_set (combination.preferred) != 1) { mm_obj_warn (log_object, "AcT preferred value should be a single AcT: %u", preferred_value); continue; } if (!(combination.allowed & combination.preferred)) continue; g_array_append_val (combinations, combination); } } if (combinations->len == 0) { inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "No combinations built from +URAT=? response"); goto out; } out: g_strfreev (split); if (selected) g_array_unref (selected); if (preferred) g_array_unref (preferred); if (inner_error) { if (combinations) g_array_unref (combinations); g_propagate_error (error, inner_error); return NULL; } return combinations; } typedef struct { const gchar *model; SettingsUpdateMethod method; FeatureSupport uact; FeatureSupport ubandsel; MMModemMode mode; MMModemBand bands_2g[4]; MMModemBand bands_3g[6]; MMModemBand bands_4g[18]; } BandConfiguration; static const BandConfiguration band_configuration[] = { { .model = "LARA-R6001", .method = SETTINGS_UPDATE_METHOD_COPS, .uact = FEATURE_UNSUPPORTED, .ubandsel = FEATURE_UNSUPPORTED, .mode = MM_MODEM_MODE_2G | MM_MODEM_MODE_3G | MM_MODEM_MODE_4G, .bands_2g = { MM_MODEM_BAND_G850, MM_MODEM_BAND_EGSM, MM_MODEM_BAND_DCS, MM_MODEM_BAND_PCS }, /* Quad-band */ .bands_3g = { MM_MODEM_BAND_UTRAN_2, MM_MODEM_BAND_UTRAN_1, MM_MODEM_BAND_UTRAN_5, MM_MODEM_BAND_UTRAN_8 }, /* 1900, 2100, 850, 900 */ .bands_4g = { MM_MODEM_BAND_EUTRAN_1, MM_MODEM_BAND_EUTRAN_2, MM_MODEM_BAND_EUTRAN_3, MM_MODEM_BAND_EUTRAN_4, MM_MODEM_BAND_EUTRAN_5, MM_MODEM_BAND_EUTRAN_7, MM_MODEM_BAND_EUTRAN_8, MM_MODEM_BAND_EUTRAN_12, MM_MODEM_BAND_EUTRAN_13, MM_MODEM_BAND_EUTRAN_18, MM_MODEM_BAND_EUTRAN_19, MM_MODEM_BAND_EUTRAN_20, MM_MODEM_BAND_EUTRAN_26, MM_MODEM_BAND_EUTRAN_28, MM_MODEM_BAND_EUTRAN_38, MM_MODEM_BAND_EUTRAN_39, MM_MODEM_BAND_EUTRAN_40, MM_MODEM_BAND_EUTRAN_41 } }, { .model = "LARA-R6001D", .method = SETTINGS_UPDATE_METHOD_COPS, .uact = FEATURE_UNSUPPORTED, .ubandsel = FEATURE_UNSUPPORTED, .mode = MM_MODEM_MODE_2G | MM_MODEM_MODE_3G | MM_MODEM_MODE_4G, .bands_2g = { MM_MODEM_BAND_G850, MM_MODEM_BAND_EGSM, MM_MODEM_BAND_DCS, MM_MODEM_BAND_PCS }, /* Quad-band */ .bands_3g = { MM_MODEM_BAND_UTRAN_2, MM_MODEM_BAND_UTRAN_1, MM_MODEM_BAND_UTRAN_5, MM_MODEM_BAND_UTRAN_8 }, /* 1900, 2100, 850, 900 */ .bands_4g = { MM_MODEM_BAND_EUTRAN_1, MM_MODEM_BAND_EUTRAN_2, MM_MODEM_BAND_EUTRAN_3, MM_MODEM_BAND_EUTRAN_4, MM_MODEM_BAND_EUTRAN_5, MM_MODEM_BAND_EUTRAN_7, MM_MODEM_BAND_EUTRAN_8, MM_MODEM_BAND_EUTRAN_12, MM_MODEM_BAND_EUTRAN_13, MM_MODEM_BAND_EUTRAN_18, MM_MODEM_BAND_EUTRAN_19, MM_MODEM_BAND_EUTRAN_20, MM_MODEM_BAND_EUTRAN_26, MM_MODEM_BAND_EUTRAN_28, MM_MODEM_BAND_EUTRAN_38, MM_MODEM_BAND_EUTRAN_39, MM_MODEM_BAND_EUTRAN_40, MM_MODEM_BAND_EUTRAN_41 } }, { .model = "SARA-G300", .method = SETTINGS_UPDATE_METHOD_COPS, .uact = FEATURE_UNSUPPORTED, .ubandsel = FEATURE_SUPPORTED, .mode = MM_MODEM_MODE_2G, .bands_2g = { MM_MODEM_BAND_EGSM, MM_MODEM_BAND_DCS } }, { .model = "SARA-G310", .method = SETTINGS_UPDATE_METHOD_COPS, .uact = FEATURE_UNSUPPORTED, .ubandsel = FEATURE_SUPPORTED, .mode = MM_MODEM_MODE_2G, .bands_2g = { MM_MODEM_BAND_G850, MM_MODEM_BAND_EGSM, MM_MODEM_BAND_DCS, MM_MODEM_BAND_PCS } }, { .model = "SARA-G340", .method = SETTINGS_UPDATE_METHOD_COPS, .uact = FEATURE_UNSUPPORTED, .ubandsel = FEATURE_SUPPORTED, .mode = MM_MODEM_MODE_2G, .bands_2g = { MM_MODEM_BAND_EGSM, MM_MODEM_BAND_DCS } }, { .model = "SARA-G350", .method = SETTINGS_UPDATE_METHOD_COPS, .uact = FEATURE_UNSUPPORTED, .ubandsel = FEATURE_SUPPORTED, .mode = MM_MODEM_MODE_2G, .bands_2g = { MM_MODEM_BAND_G850, MM_MODEM_BAND_EGSM, MM_MODEM_BAND_DCS, MM_MODEM_BAND_PCS } }, { .model = "SARA-G450", .method = SETTINGS_UPDATE_METHOD_COPS, .uact = FEATURE_UNSUPPORTED, .ubandsel = FEATURE_SUPPORTED, .mode = MM_MODEM_MODE_2G, .bands_2g = { MM_MODEM_BAND_G850, MM_MODEM_BAND_EGSM, MM_MODEM_BAND_DCS, MM_MODEM_BAND_PCS } }, { .model = "LISA-U200", .method = SETTINGS_UPDATE_METHOD_COPS, .uact = FEATURE_UNSUPPORTED, .ubandsel = FEATURE_SUPPORTED, .mode = MM_MODEM_MODE_2G | MM_MODEM_MODE_3G, .bands_2g = { MM_MODEM_BAND_G850, MM_MODEM_BAND_EGSM, MM_MODEM_BAND_DCS, MM_MODEM_BAND_PCS }, .bands_3g = { MM_MODEM_BAND_UTRAN_6, MM_MODEM_BAND_UTRAN_5, MM_MODEM_BAND_UTRAN_8, MM_MODEM_BAND_UTRAN_4, MM_MODEM_BAND_UTRAN_2, MM_MODEM_BAND_UTRAN_1 } }, { .model = "LISA-U201", .method = SETTINGS_UPDATE_METHOD_COPS, .uact = FEATURE_UNSUPPORTED, .ubandsel = FEATURE_SUPPORTED, .mode = MM_MODEM_MODE_2G | MM_MODEM_MODE_3G, .bands_2g = { MM_MODEM_BAND_G850, MM_MODEM_BAND_EGSM, MM_MODEM_BAND_DCS, MM_MODEM_BAND_PCS }, .bands_3g = { MM_MODEM_BAND_UTRAN_6, MM_MODEM_BAND_UTRAN_5, MM_MODEM_BAND_UTRAN_8, MM_MODEM_BAND_UTRAN_2, MM_MODEM_BAND_UTRAN_1 } }, { .model = "LISA-U230", .method = SETTINGS_UPDATE_METHOD_COPS, .uact = FEATURE_UNSUPPORTED, .ubandsel = FEATURE_SUPPORTED, .mode = MM_MODEM_MODE_2G | MM_MODEM_MODE_3G, .bands_2g = { MM_MODEM_BAND_G850, MM_MODEM_BAND_EGSM, MM_MODEM_BAND_DCS, MM_MODEM_BAND_PCS }, .bands_3g = { MM_MODEM_BAND_UTRAN_6, MM_MODEM_BAND_UTRAN_5, MM_MODEM_BAND_UTRAN_8, MM_MODEM_BAND_UTRAN_4, MM_MODEM_BAND_UTRAN_2, MM_MODEM_BAND_UTRAN_1 } }, { .model = "LISA-U260", .method = SETTINGS_UPDATE_METHOD_COPS, .uact = FEATURE_UNSUPPORTED, .ubandsel = FEATURE_SUPPORTED, .mode = MM_MODEM_MODE_2G | MM_MODEM_MODE_3G, .bands_2g = { MM_MODEM_BAND_G850, MM_MODEM_BAND_EGSM, MM_MODEM_BAND_DCS, MM_MODEM_BAND_PCS }, .bands_3g = { MM_MODEM_BAND_UTRAN_5, MM_MODEM_BAND_UTRAN_2 } }, { .model = "LISA-U270", .method = SETTINGS_UPDATE_METHOD_COPS, .uact = FEATURE_UNSUPPORTED, .ubandsel = FEATURE_SUPPORTED, .mode = MM_MODEM_MODE_2G | MM_MODEM_MODE_3G, .bands_2g = { MM_MODEM_BAND_G850, MM_MODEM_BAND_EGSM, MM_MODEM_BAND_DCS, MM_MODEM_BAND_PCS }, .bands_3g = { MM_MODEM_BAND_UTRAN_8, MM_MODEM_BAND_UTRAN_1 } }, { .model = "SARA-U201", .method = SETTINGS_UPDATE_METHOD_COPS, .uact = FEATURE_UNSUPPORTED, .ubandsel = FEATURE_SUPPORTED, .mode = MM_MODEM_MODE_2G | MM_MODEM_MODE_3G, .bands_2g = { MM_MODEM_BAND_G850, MM_MODEM_BAND_EGSM, MM_MODEM_BAND_DCS, MM_MODEM_BAND_PCS }, .bands_3g = { MM_MODEM_BAND_UTRAN_6, MM_MODEM_BAND_UTRAN_5, MM_MODEM_BAND_UTRAN_8, MM_MODEM_BAND_UTRAN_2, MM_MODEM_BAND_UTRAN_1 } }, { .model = "SARA-U260", .method = SETTINGS_UPDATE_METHOD_COPS, .uact = FEATURE_UNSUPPORTED, .ubandsel = FEATURE_SUPPORTED, .mode = MM_MODEM_MODE_2G | MM_MODEM_MODE_3G, .bands_2g = { MM_MODEM_BAND_G850, MM_MODEM_BAND_PCS }, .bands_3g = { MM_MODEM_BAND_UTRAN_5, MM_MODEM_BAND_UTRAN_2 } }, { .model = "SARA-U270", .method = SETTINGS_UPDATE_METHOD_COPS, .uact = FEATURE_UNSUPPORTED, .ubandsel = FEATURE_SUPPORTED, .mode = MM_MODEM_MODE_2G | MM_MODEM_MODE_3G, .bands_2g = { MM_MODEM_BAND_EGSM, MM_MODEM_BAND_DCS }, .bands_3g = { MM_MODEM_BAND_UTRAN_8, MM_MODEM_BAND_UTRAN_1 } }, { .model = "SARA-U280", .method = SETTINGS_UPDATE_METHOD_COPS, .uact = FEATURE_UNSUPPORTED, .ubandsel = FEATURE_SUPPORTED, .mode = MM_MODEM_MODE_3G, .bands_3g = { MM_MODEM_BAND_UTRAN_5, MM_MODEM_BAND_UTRAN_2 } }, { .model = "MPCI-L201", .method = SETTINGS_UPDATE_METHOD_CFUN, .uact = FEATURE_UNSUPPORTED, .ubandsel = FEATURE_SUPPORTED, .mode = MM_MODEM_MODE_3G | MM_MODEM_MODE_4G, .bands_3g = { MM_MODEM_BAND_UTRAN_5, MM_MODEM_BAND_UTRAN_2 }, .bands_4g = { MM_MODEM_BAND_EUTRAN_2, MM_MODEM_BAND_EUTRAN_4, MM_MODEM_BAND_EUTRAN_5, MM_MODEM_BAND_EUTRAN_13, MM_MODEM_BAND_EUTRAN_17 } }, { .model = "MPCI-L200", .method = SETTINGS_UPDATE_METHOD_CFUN, .uact = FEATURE_UNSUPPORTED, .ubandsel = FEATURE_SUPPORTED, .mode = MM_MODEM_MODE_2G | MM_MODEM_MODE_3G | MM_MODEM_MODE_4G, .bands_2g = { MM_MODEM_BAND_G850, MM_MODEM_BAND_EGSM, MM_MODEM_BAND_DCS, MM_MODEM_BAND_PCS }, .bands_3g = { MM_MODEM_BAND_UTRAN_5, MM_MODEM_BAND_UTRAN_8, MM_MODEM_BAND_UTRAN_4, MM_MODEM_BAND_UTRAN_2, MM_MODEM_BAND_UTRAN_1 }, .bands_4g = { MM_MODEM_BAND_EUTRAN_2, MM_MODEM_BAND_EUTRAN_4, MM_MODEM_BAND_EUTRAN_5, MM_MODEM_BAND_EUTRAN_7, MM_MODEM_BAND_EUTRAN_17 } }, { .model = "MPCI-L210", .method = SETTINGS_UPDATE_METHOD_CFUN, .uact = FEATURE_UNSUPPORTED, .ubandsel = FEATURE_SUPPORTED, .mode = MM_MODEM_MODE_2G | MM_MODEM_MODE_3G | MM_MODEM_MODE_4G, .bands_2g = { MM_MODEM_BAND_G850, MM_MODEM_BAND_EGSM, MM_MODEM_BAND_DCS, MM_MODEM_BAND_PCS }, .bands_3g = { MM_MODEM_BAND_UTRAN_5, MM_MODEM_BAND_UTRAN_8, MM_MODEM_BAND_UTRAN_2, MM_MODEM_BAND_UTRAN_1 }, .bands_4g = { MM_MODEM_BAND_EUTRAN_1, MM_MODEM_BAND_EUTRAN_3, MM_MODEM_BAND_EUTRAN_5, MM_MODEM_BAND_EUTRAN_7, MM_MODEM_BAND_EUTRAN_8, MM_MODEM_BAND_EUTRAN_20 } }, { .model = "MPCI-L220", .method = SETTINGS_UPDATE_METHOD_CFUN, .uact = FEATURE_UNSUPPORTED, .ubandsel = FEATURE_SUPPORTED, .mode = MM_MODEM_MODE_3G | MM_MODEM_MODE_4G, .bands_3g = { MM_MODEM_BAND_UTRAN_5, MM_MODEM_BAND_UTRAN_8, MM_MODEM_BAND_UTRAN_1 }, .bands_4g = { MM_MODEM_BAND_EUTRAN_1, MM_MODEM_BAND_EUTRAN_3, MM_MODEM_BAND_EUTRAN_5, MM_MODEM_BAND_EUTRAN_8, MM_MODEM_BAND_EUTRAN_19 } }, { .model = "MPCI-L280", .method = SETTINGS_UPDATE_METHOD_CFUN, .uact = FEATURE_UNSUPPORTED, .ubandsel = FEATURE_SUPPORTED, .mode = MM_MODEM_MODE_2G | MM_MODEM_MODE_3G | MM_MODEM_MODE_4G, .bands_2g = { MM_MODEM_BAND_G850, MM_MODEM_BAND_EGSM, MM_MODEM_BAND_DCS, MM_MODEM_BAND_PCS }, .bands_3g = { MM_MODEM_BAND_UTRAN_5, MM_MODEM_BAND_UTRAN_8, MM_MODEM_BAND_UTRAN_2, MM_MODEM_BAND_UTRAN_1 }, .bands_4g = { MM_MODEM_BAND_EUTRAN_1, MM_MODEM_BAND_EUTRAN_3, MM_MODEM_BAND_EUTRAN_5, MM_MODEM_BAND_EUTRAN_7, MM_MODEM_BAND_EUTRAN_8, MM_MODEM_BAND_EUTRAN_28 } }, { .model = "TOBY-L200", .method = SETTINGS_UPDATE_METHOD_CFUN, .uact = FEATURE_UNSUPPORTED, .ubandsel = FEATURE_SUPPORTED, .mode = MM_MODEM_MODE_2G | MM_MODEM_MODE_3G | MM_MODEM_MODE_4G, .bands_2g = { MM_MODEM_BAND_G850, MM_MODEM_BAND_EGSM, MM_MODEM_BAND_DCS, MM_MODEM_BAND_PCS }, .bands_3g = { MM_MODEM_BAND_UTRAN_5, MM_MODEM_BAND_UTRAN_8, MM_MODEM_BAND_UTRAN_4, MM_MODEM_BAND_UTRAN_2, MM_MODEM_BAND_UTRAN_1 }, .bands_4g = { MM_MODEM_BAND_EUTRAN_2, MM_MODEM_BAND_EUTRAN_4, MM_MODEM_BAND_EUTRAN_5, MM_MODEM_BAND_EUTRAN_7, MM_MODEM_BAND_EUTRAN_17 } }, { .model = "TOBY-L201", .method = SETTINGS_UPDATE_METHOD_CFUN, .uact = FEATURE_UNSUPPORTED, .ubandsel = FEATURE_SUPPORTED, .mode = MM_MODEM_MODE_3G | MM_MODEM_MODE_4G, .bands_3g = { MM_MODEM_BAND_UTRAN_5, MM_MODEM_BAND_UTRAN_2 }, .bands_4g = { MM_MODEM_BAND_EUTRAN_2, MM_MODEM_BAND_EUTRAN_4, MM_MODEM_BAND_EUTRAN_5, MM_MODEM_BAND_EUTRAN_13, MM_MODEM_BAND_EUTRAN_17 } }, { .model = "TOBY-L210", .method = SETTINGS_UPDATE_METHOD_CFUN, .uact = FEATURE_UNSUPPORTED, .ubandsel = FEATURE_SUPPORTED, .mode = MM_MODEM_MODE_2G | MM_MODEM_MODE_3G | MM_MODEM_MODE_4G, .bands_2g = { MM_MODEM_BAND_G850, MM_MODEM_BAND_EGSM, MM_MODEM_BAND_DCS, MM_MODEM_BAND_PCS }, .bands_3g = { MM_MODEM_BAND_UTRAN_5, MM_MODEM_BAND_UTRAN_8, MM_MODEM_BAND_UTRAN_2, MM_MODEM_BAND_UTRAN_1 }, .bands_4g = { MM_MODEM_BAND_EUTRAN_1, MM_MODEM_BAND_EUTRAN_3, MM_MODEM_BAND_EUTRAN_5, MM_MODEM_BAND_EUTRAN_7, MM_MODEM_BAND_EUTRAN_8, MM_MODEM_BAND_EUTRAN_20 } }, { .model = "TOBY-L220", .method = SETTINGS_UPDATE_METHOD_CFUN, .uact = FEATURE_UNSUPPORTED, .ubandsel = FEATURE_SUPPORTED, .mode = MM_MODEM_MODE_3G | MM_MODEM_MODE_4G, .bands_3g = { MM_MODEM_BAND_UTRAN_5, MM_MODEM_BAND_UTRAN_8, MM_MODEM_BAND_UTRAN_2, MM_MODEM_BAND_UTRAN_1 }, .bands_4g = { MM_MODEM_BAND_EUTRAN_1, MM_MODEM_BAND_EUTRAN_3, MM_MODEM_BAND_EUTRAN_5, MM_MODEM_BAND_EUTRAN_8, MM_MODEM_BAND_EUTRAN_19 } }, { .model = "TOBY-L280", .method = SETTINGS_UPDATE_METHOD_CFUN, .uact = FEATURE_UNSUPPORTED, .ubandsel = FEATURE_SUPPORTED, .mode = MM_MODEM_MODE_2G | MM_MODEM_MODE_3G | MM_MODEM_MODE_4G, .bands_2g = { MM_MODEM_BAND_G850, MM_MODEM_BAND_EGSM, MM_MODEM_BAND_DCS, MM_MODEM_BAND_PCS }, .bands_3g = { MM_MODEM_BAND_UTRAN_5, MM_MODEM_BAND_UTRAN_8, MM_MODEM_BAND_UTRAN_2, MM_MODEM_BAND_UTRAN_1 }, .bands_4g = { MM_MODEM_BAND_EUTRAN_1, MM_MODEM_BAND_EUTRAN_3, MM_MODEM_BAND_EUTRAN_5, MM_MODEM_BAND_EUTRAN_7, MM_MODEM_BAND_EUTRAN_8, MM_MODEM_BAND_EUTRAN_28 } }, { .model = "TOBY-L4006", .method = SETTINGS_UPDATE_METHOD_CFUN, .uact = FEATURE_SUPPORTED, .ubandsel = FEATURE_UNSUPPORTED, .mode = MM_MODEM_MODE_2G | MM_MODEM_MODE_3G | MM_MODEM_MODE_4G, .bands_2g = { MM_MODEM_BAND_G850, MM_MODEM_BAND_PCS }, .bands_3g = { MM_MODEM_BAND_UTRAN_5, MM_MODEM_BAND_UTRAN_4, MM_MODEM_BAND_UTRAN_2 }, .bands_4g = { MM_MODEM_BAND_EUTRAN_2, MM_MODEM_BAND_EUTRAN_4, MM_MODEM_BAND_EUTRAN_5, MM_MODEM_BAND_EUTRAN_7, MM_MODEM_BAND_EUTRAN_12, MM_MODEM_BAND_EUTRAN_13, MM_MODEM_BAND_EUTRAN_29 } }, { .model = "TOBY-L4106", .method = SETTINGS_UPDATE_METHOD_CFUN, .uact = FEATURE_SUPPORTED, .ubandsel = FEATURE_UNSUPPORTED, .mode = MM_MODEM_MODE_2G | MM_MODEM_MODE_3G | MM_MODEM_MODE_4G, .bands_2g = { MM_MODEM_BAND_EGSM, MM_MODEM_BAND_DCS }, .bands_3g = { MM_MODEM_BAND_UTRAN_8, MM_MODEM_BAND_UTRAN_1 }, .bands_4g = { MM_MODEM_BAND_EUTRAN_1, MM_MODEM_BAND_EUTRAN_3, MM_MODEM_BAND_EUTRAN_7, MM_MODEM_BAND_EUTRAN_8, MM_MODEM_BAND_EUTRAN_20, MM_MODEM_BAND_EUTRAN_38 } }, { .model = "TOBY-L4206", .method = SETTINGS_UPDATE_METHOD_CFUN, .uact = FEATURE_SUPPORTED, .ubandsel = FEATURE_UNSUPPORTED, .mode = MM_MODEM_MODE_2G | MM_MODEM_MODE_3G | MM_MODEM_MODE_4G, .bands_2g = { MM_MODEM_BAND_G850, MM_MODEM_BAND_EGSM, MM_MODEM_BAND_DCS, MM_MODEM_BAND_PCS }, .bands_3g = { MM_MODEM_BAND_UTRAN_5, MM_MODEM_BAND_UTRAN_8, MM_MODEM_BAND_UTRAN_1 }, .bands_4g = { MM_MODEM_BAND_EUTRAN_1, MM_MODEM_BAND_EUTRAN_3, MM_MODEM_BAND_EUTRAN_5, MM_MODEM_BAND_EUTRAN_7, MM_MODEM_BAND_EUTRAN_8, MM_MODEM_BAND_EUTRAN_9, MM_MODEM_BAND_EUTRAN_19, MM_MODEM_BAND_EUTRAN_28 } }, { .model = "TOBY-L4906", .method = SETTINGS_UPDATE_METHOD_CFUN, .uact = FEATURE_SUPPORTED, .ubandsel = FEATURE_UNSUPPORTED, .mode = MM_MODEM_MODE_2G | MM_MODEM_MODE_3G | MM_MODEM_MODE_4G, .bands_2g = { MM_MODEM_BAND_EGSM, MM_MODEM_BAND_DCS }, .bands_3g = { MM_MODEM_BAND_UTRAN_8, MM_MODEM_BAND_UTRAN_1 }, .bands_4g = { MM_MODEM_BAND_EUTRAN_1, MM_MODEM_BAND_EUTRAN_3, MM_MODEM_BAND_EUTRAN_39, MM_MODEM_BAND_EUTRAN_40, MM_MODEM_BAND_EUTRAN_41 } }, { .model = "TOBY-R200", .method = SETTINGS_UPDATE_METHOD_COPS, .uact = FEATURE_UNSUPPORTED, .ubandsel = FEATURE_SUPPORTED, .mode = MM_MODEM_MODE_2G | MM_MODEM_MODE_3G | MM_MODEM_MODE_4G, .bands_2g = { MM_MODEM_BAND_G850, MM_MODEM_BAND_EGSM, MM_MODEM_BAND_DCS, MM_MODEM_BAND_PCS }, .bands_3g = { MM_MODEM_BAND_UTRAN_5, MM_MODEM_BAND_UTRAN_8, MM_MODEM_BAND_UTRAN_2, MM_MODEM_BAND_UTRAN_1 }, .bands_4g = { MM_MODEM_BAND_EUTRAN_2, MM_MODEM_BAND_EUTRAN_4, MM_MODEM_BAND_EUTRAN_5, MM_MODEM_BAND_EUTRAN_12 } }, { .model = "TOBY-R202", .method = SETTINGS_UPDATE_METHOD_COPS, .uact = FEATURE_UNSUPPORTED, .ubandsel = FEATURE_SUPPORTED, .mode = MM_MODEM_MODE_3G | MM_MODEM_MODE_4G, .bands_3g = { MM_MODEM_BAND_UTRAN_5, MM_MODEM_BAND_UTRAN_2 }, .bands_4g = { MM_MODEM_BAND_EUTRAN_2, MM_MODEM_BAND_EUTRAN_4, MM_MODEM_BAND_EUTRAN_5, MM_MODEM_BAND_EUTRAN_12 } }, { .model = "LARA-R202", .method = SETTINGS_UPDATE_METHOD_COPS, .uact = FEATURE_UNSUPPORTED, .ubandsel = FEATURE_SUPPORTED, .mode = MM_MODEM_MODE_3G | MM_MODEM_MODE_4G, .bands_3g = { MM_MODEM_BAND_UTRAN_5, MM_MODEM_BAND_UTRAN_2 }, .bands_4g = { MM_MODEM_BAND_EUTRAN_2, MM_MODEM_BAND_EUTRAN_4, MM_MODEM_BAND_EUTRAN_5, MM_MODEM_BAND_EUTRAN_12 } }, { .model = "LARA-R203", .method = SETTINGS_UPDATE_METHOD_COPS, .uact = FEATURE_UNSUPPORTED, .ubandsel = FEATURE_SUPPORTED, .mode = MM_MODEM_MODE_4G, .bands_4g = { MM_MODEM_BAND_EUTRAN_2, MM_MODEM_BAND_EUTRAN_4, MM_MODEM_BAND_EUTRAN_12 } }, { .model = "LARA-R204", .method = SETTINGS_UPDATE_METHOD_COPS, .uact = FEATURE_UNSUPPORTED, .ubandsel = FEATURE_SUPPORTED, .mode = MM_MODEM_MODE_4G, .bands_4g = { MM_MODEM_BAND_EUTRAN_4, MM_MODEM_BAND_EUTRAN_13 } }, { .model = "LARA-R211", .method = SETTINGS_UPDATE_METHOD_COPS, .uact = FEATURE_UNSUPPORTED, .ubandsel = FEATURE_SUPPORTED, .mode = MM_MODEM_MODE_2G | MM_MODEM_MODE_4G, .bands_2g = { MM_MODEM_BAND_EGSM, MM_MODEM_BAND_DCS }, .bands_4g = { MM_MODEM_BAND_EUTRAN_3, MM_MODEM_BAND_EUTRAN_7, MM_MODEM_BAND_EUTRAN_20 } }, { .model = "LARA-R280", .method = SETTINGS_UPDATE_METHOD_COPS, .uact = FEATURE_UNSUPPORTED, .ubandsel = FEATURE_SUPPORTED, .mode = MM_MODEM_MODE_3G | MM_MODEM_MODE_4G, .bands_3g = { MM_MODEM_BAND_UTRAN_1 }, .bands_4g = { MM_MODEM_BAND_EUTRAN_3, MM_MODEM_BAND_EUTRAN_8, MM_MODEM_BAND_EUTRAN_28 } }, { .model = "LARA-R3121", .method = SETTINGS_UPDATE_METHOD_COPS, .uact = FEATURE_UNSUPPORTED, .ubandsel = FEATURE_SUPPORTED, .mode = MM_MODEM_MODE_4G, .bands_4g = { MM_MODEM_BAND_EUTRAN_3, MM_MODEM_BAND_EUTRAN_7, MM_MODEM_BAND_EUTRAN_20 } }, { .model = "SARA-N200", .method = SETTINGS_UPDATE_METHOD_COPS, .uact = FEATURE_UNSUPPORTED, .ubandsel = FEATURE_SUPPORTED, .mode = MM_MODEM_MODE_4G, .bands_4g = { MM_MODEM_BAND_EUTRAN_8 } }, { .model = "SARA-N201", .method = SETTINGS_UPDATE_METHOD_COPS, .uact = FEATURE_UNSUPPORTED, .ubandsel = FEATURE_SUPPORTED, .mode = MM_MODEM_MODE_4G, .bands_4g = { MM_MODEM_BAND_EUTRAN_5 } }, { .model = "SARA-N210", .method = SETTINGS_UPDATE_METHOD_COPS, .uact = FEATURE_UNSUPPORTED, .ubandsel = FEATURE_SUPPORTED, .mode = MM_MODEM_MODE_4G, .bands_4g = { MM_MODEM_BAND_EUTRAN_20 } }, { .model = "SARA-N211", .method = SETTINGS_UPDATE_METHOD_COPS, .uact = FEATURE_UNSUPPORTED, .ubandsel = FEATURE_SUPPORTED, .mode = MM_MODEM_MODE_4G, .bands_4g = { MM_MODEM_BAND_EUTRAN_8, MM_MODEM_BAND_EUTRAN_20 } }, { .model = "SARA-N280", .method = SETTINGS_UPDATE_METHOD_COPS, .uact = FEATURE_UNSUPPORTED, .ubandsel = FEATURE_SUPPORTED, .mode = MM_MODEM_MODE_4G, .bands_4g = { MM_MODEM_BAND_EUTRAN_28 } }, { .model = "SARA-R410M-52B", .method = SETTINGS_UPDATE_METHOD_COPS, .uact = FEATURE_UNSUPPORTED, .ubandsel = FEATURE_UNSUPPORTED, .mode = MM_MODEM_MODE_4G, .bands_4g = { MM_MODEM_BAND_EUTRAN_2, MM_MODEM_BAND_EUTRAN_4, MM_MODEM_BAND_EUTRAN_5, MM_MODEM_BAND_EUTRAN_12, MM_MODEM_BAND_EUTRAN_13 } }, { .model = "SARA-R410M-02B", .method = SETTINGS_UPDATE_METHOD_COPS, .uact = FEATURE_UNSUPPORTED, .ubandsel = FEATURE_UNSUPPORTED, .mode = MM_MODEM_MODE_4G, .bands_4g = { MM_MODEM_BAND_EUTRAN_1, MM_MODEM_BAND_EUTRAN_2, MM_MODEM_BAND_EUTRAN_3, MM_MODEM_BAND_EUTRAN_4, MM_MODEM_BAND_EUTRAN_5, MM_MODEM_BAND_EUTRAN_8, MM_MODEM_BAND_EUTRAN_12, MM_MODEM_BAND_EUTRAN_13, MM_MODEM_BAND_EUTRAN_19, MM_MODEM_BAND_EUTRAN_20, MM_MODEM_BAND_EUTRAN_28, MM_MODEM_BAND_EUTRAN_39 } }, { .model = "SARA-R412M-02B", .method = SETTINGS_UPDATE_METHOD_COPS, .uact = FEATURE_UNSUPPORTED, .ubandsel = FEATURE_UNSUPPORTED, .mode = MM_MODEM_MODE_2G | MM_MODEM_MODE_4G, .bands_2g = { MM_MODEM_BAND_G850, MM_MODEM_BAND_EGSM, MM_MODEM_BAND_DCS, MM_MODEM_BAND_PCS }, .bands_4g = { MM_MODEM_BAND_EUTRAN_1, MM_MODEM_BAND_EUTRAN_2, MM_MODEM_BAND_EUTRAN_3, MM_MODEM_BAND_EUTRAN_4, MM_MODEM_BAND_EUTRAN_5, MM_MODEM_BAND_EUTRAN_8, MM_MODEM_BAND_EUTRAN_12, MM_MODEM_BAND_EUTRAN_13, MM_MODEM_BAND_EUTRAN_19, MM_MODEM_BAND_EUTRAN_20, MM_MODEM_BAND_EUTRAN_28, MM_MODEM_BAND_EUTRAN_39 } }, { .model = "SARA-N410-02B", .method = SETTINGS_UPDATE_METHOD_COPS, .uact = FEATURE_UNSUPPORTED, .ubandsel = FEATURE_UNSUPPORTED, .mode = MM_MODEM_MODE_4G, .bands_4g = { MM_MODEM_BAND_EUTRAN_1, MM_MODEM_BAND_EUTRAN_2, MM_MODEM_BAND_EUTRAN_3, MM_MODEM_BAND_EUTRAN_4, MM_MODEM_BAND_EUTRAN_5, MM_MODEM_BAND_EUTRAN_8, MM_MODEM_BAND_EUTRAN_12, MM_MODEM_BAND_EUTRAN_13, MM_MODEM_BAND_EUTRAN_19, MM_MODEM_BAND_EUTRAN_20, MM_MODEM_BAND_EUTRAN_28 } }, }; gboolean mm_ublox_get_support_config (const gchar *model, UbloxSupportConfig *config, GError **error) { guint i; if (!model) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Support configuration unknown for unknown model"); return FALSE; } for (i = 0; i < G_N_ELEMENTS (band_configuration); i++) { /* NOTE: matching by prefix! */ if (g_str_has_prefix (model, band_configuration[i].model)) { config->loaded = TRUE; config->method = band_configuration[i].method; config->uact = band_configuration[i].uact; config->ubandsel = band_configuration[i].ubandsel; return TRUE; } } g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "No support configuration found for modem: %s", model); return FALSE; } /*****************************************************************************/ /* Supported modes loading */ static MMModemMode supported_modes_per_model (const gchar *model) { MMModemMode mode; guint i; mode = MM_MODEM_MODE_2G | MM_MODEM_MODE_3G | MM_MODEM_MODE_4G; if (model) { for (i = 0; i < G_N_ELEMENTS (band_configuration); i++) if (g_str_has_prefix (model, band_configuration[i].model)) { mode = band_configuration[i].mode; return mode;; } } return mode; } GArray * mm_ublox_filter_supported_modes (const gchar *model, GArray *combinations, gpointer logger, GError **error) { MMModemModeCombination mode; GArray *all; GArray *filtered; /* Model not specified? */ if (!model) return combinations; /* AT+URAT=? lies; we need an extra per-device filtering, thanks u-blox. * Don't know all PIDs for all devices, so model string based filtering... */ mode.allowed = supported_modes_per_model (model); mode.preferred = MM_MODEM_MODE_NONE; /* Nothing filtered? */ if (mode.allowed == supported_modes_per_model (NULL)) return combinations; all = g_array_sized_new (FALSE, FALSE, sizeof (MMModemModeCombination), 1); g_array_append_val (all, mode); filtered = mm_filter_supported_modes (all, combinations, logger); g_array_unref (all); g_array_unref (combinations); /* Error if nothing left */ if (filtered->len == 0) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "No valid mode combinations built after filtering (model %s)", model); g_array_unref (filtered); return NULL; } return filtered; } /*****************************************************************************/ /* Supported bands loading */ GArray * mm_ublox_get_supported_bands (const gchar *model, gpointer log_object, GError **error) { MMModemMode mode; GArray *bands; guint i, j; mode = supported_modes_per_model (model); bands = g_array_new (FALSE, FALSE, sizeof (MMModemBand)); for (i = 0; i < G_N_ELEMENTS (band_configuration); i++) { if (g_str_has_prefix (model, band_configuration[i].model)) { mm_obj_dbg (log_object, "known supported bands found for model: %s", band_configuration[i].model); break; } } if (i == G_N_ELEMENTS (band_configuration)) { mm_obj_warn (log_object, "unknown model name given when looking for supported bands: %s", model); return NULL; } mode = band_configuration[i].mode; if (mode & MM_MODEM_MODE_2G) { for (j = 0; j < G_N_ELEMENTS (band_configuration[i].bands_2g) && band_configuration[i].bands_2g[j]; j++) { bands = g_array_append_val (bands, band_configuration[i].bands_2g[j]); } } if (mode & MM_MODEM_MODE_3G) { for (j = 0; j < G_N_ELEMENTS (band_configuration[i].bands_3g) && band_configuration[i].bands_3g[j]; j++) { bands = g_array_append_val (bands, band_configuration[i].bands_3g[j]); } } if (mode & MM_MODEM_MODE_4G) { for (j = 0; j < G_N_ELEMENTS (band_configuration[i].bands_4g) && band_configuration[i].bands_4g[j]; j++) { bands = g_array_append_val (bands, band_configuration[i].bands_4g[j]); } } if (bands->len == 0) { g_array_unref (bands); g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "No valid supported bands loaded"); return NULL; } return bands; } typedef struct { guint num; MMModemBand band[4]; } NumToBand; /* 2G GSM Band Frequencies */ static const NumToBand num_bands_2g [] = { { .num = 850, .band = { MM_MODEM_BAND_G850 } }, { .num = 900, .band = { MM_MODEM_BAND_EGSM } }, { .num = 1900, .band = { MM_MODEM_BAND_PCS } }, { .num = 1800, .band = { MM_MODEM_BAND_DCS } }, }; /* 3G UMTS Band Frequencies */ static const NumToBand num_bands_3g [] = { { .num = 800, .band = { MM_MODEM_BAND_UTRAN_6 } }, { .num = 850, .band = { MM_MODEM_BAND_UTRAN_5 } }, { .num = 900, .band = { MM_MODEM_BAND_UTRAN_8 } }, { .num = 1700, .band = { MM_MODEM_BAND_UTRAN_4 } }, { .num = 1900, .band = { MM_MODEM_BAND_UTRAN_2 } }, { .num = 2100, .band = { MM_MODEM_BAND_UTRAN_1 } }, }; /* 4G LTE Band Frequencies */ static const NumToBand num_bands_4g [] = { { .num = 700, .band = { MM_MODEM_BAND_EUTRAN_12, MM_MODEM_BAND_EUTRAN_13, MM_MODEM_BAND_EUTRAN_17 } }, { .num = 800, .band = { MM_MODEM_BAND_EUTRAN_20, MM_MODEM_BAND_EUTRAN_27 } }, { .num = 850, .band = { MM_MODEM_BAND_EUTRAN_5, MM_MODEM_BAND_EUTRAN_18, MM_MODEM_BAND_EUTRAN_19, MM_MODEM_BAND_EUTRAN_26 } }, { .num = 900, .band = { MM_MODEM_BAND_EUTRAN_8 } }, { .num = 1700, .band = { MM_MODEM_BAND_EUTRAN_4, MM_MODEM_BAND_EUTRAN_10 } }, { .num = 1800, .band = { MM_MODEM_BAND_EUTRAN_3 } }, { .num = 1900, .band = { MM_MODEM_BAND_EUTRAN_2, MM_MODEM_BAND_EUTRAN_39 } }, { .num = 2100, .band = { MM_MODEM_BAND_EUTRAN_1 } }, { .num = 2300, .band = { MM_MODEM_BAND_EUTRAN_40 } }, { .num = 2500, .band = { MM_MODEM_BAND_EUTRAN_41 } }, { .num = 2600, .band = { MM_MODEM_BAND_EUTRAN_7, MM_MODEM_BAND_EUTRAN_38 } }, }; /*****************************************************************************/ /* +UBANDSEL? response parser */ static MMModemBand num_to_band_2g (guint num) { guint i; for (i = 0; i < G_N_ELEMENTS (num_bands_2g); i++) { if (num == num_bands_2g[i].num) return num_bands_2g[i].band[0]; } return MM_MODEM_BAND_UNKNOWN; } static MMModemBand num_to_band_3g (guint num) { guint i; for (i = 0; i < G_N_ELEMENTS (num_bands_3g); i++) { if (num == num_bands_3g[i].num) return num_bands_3g[i].band[0]; } return MM_MODEM_BAND_UNKNOWN; } static guint band_to_num (MMModemBand band) { guint i, j; /* Search 2G list */ for (i = 0; i < G_N_ELEMENTS (num_bands_2g); i++) { for (j = 0; j < G_N_ELEMENTS (num_bands_2g[i].band) && num_bands_2g[i].band[j]; j++) { if (band == num_bands_2g[i].band[j]) return num_bands_2g[i].num; } } /* Search 3G list */ for (i = 0; i < G_N_ELEMENTS (num_bands_3g); i++) { for (j = 0; j < G_N_ELEMENTS (num_bands_3g[i].band) && num_bands_3g[i].band[j]; j++) { if (band == num_bands_3g[i].band[j]) return num_bands_3g[i].num; } } /* Search 4G list */ for (i = 0; i < G_N_ELEMENTS (num_bands_4g); i++) { for (j = 0; j < G_N_ELEMENTS (num_bands_4g[i].band) && num_bands_4g[i].band[j]; j++) { if (band == num_bands_4g[i].band[j]) return num_bands_4g[i].num; } } /* Should never happen */ return 0; } static void append_bands (GArray *bands, guint ubandsel_value, MMModemMode mode, const gchar *model, gpointer log_object) { guint i, j, k, x; MMModemBand band; /* Find Modem Model Index in band_configuration */ for (i = 0; i < G_N_ELEMENTS (band_configuration); i++) { if (g_str_has_prefix (model, band_configuration[i].model)) { mm_obj_dbg (log_object, "known bands found for model: %s", band_configuration[i].model); break; } } if (i == G_N_ELEMENTS (band_configuration)) { mm_obj_warn (log_object, "unknown model name given when looking for bands: %s", model); return; } if (mode & MM_MODEM_MODE_2G) { band = num_to_band_2g (ubandsel_value); if (band != MM_MODEM_BAND_UNKNOWN) { for (x = 0; x < G_N_ELEMENTS (band_configuration[i].bands_2g); x++) { if (band_configuration[i].bands_2g[x] == band) { g_array_append_val (bands, band); break; } } } } if (mode & MM_MODEM_MODE_3G) { band = num_to_band_3g (ubandsel_value); if (band != MM_MODEM_BAND_UNKNOWN) { for (x = 0; x < G_N_ELEMENTS (band_configuration[i].bands_3g); x++) { if (band_configuration[i].bands_3g[x] == band) { g_array_append_val (bands, band); break; } } } } /* Note: The weird code segment below is to separate out specific LTE bands since * UBANDSEL? reports back the frequency of the band and not the band itself. */ if (mode & MM_MODEM_MODE_4G) { for (j = 0; j < G_N_ELEMENTS (num_bands_4g); j++) { if (ubandsel_value == num_bands_4g[j].num) { for (k = 0; k < G_N_ELEMENTS (num_bands_4g[j].band); k++) { band = num_bands_4g[j].band[k]; if (band != MM_MODEM_BAND_UNKNOWN) { for (x = 0; x < G_N_ELEMENTS (band_configuration[i].bands_4g); x++) { if (band_configuration[i].bands_4g[x] == band) { g_array_append_val (bands, band); break; } } } } break; } } } } GArray * mm_ublox_parse_ubandsel_response (const gchar *response, const gchar *model, gpointer log_object, GError **error) { GArray *array_values = NULL; GArray *array = NULL; gchar *dupstr = NULL; GError *inner_error = NULL; guint i; MMModemMode mode; if (!g_str_has_prefix (response, "+UBANDSEL")) { inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't parse +UBANDSEL response: '%s'", response); goto out; } /* Response may be e.g.: * +UBANDSEL: 850,900,1800,1900 */ dupstr = g_strchomp (g_strdup (mm_strip_tag (response, "+UBANDSEL:"))); array_values = mm_parse_uint_list (dupstr, &inner_error); if (!array_values) goto out; /* Convert list of ubandsel numbers to MMModemBand values */ mode = supported_modes_per_model (model); array = g_array_new (FALSE, FALSE, sizeof (MMModemBand)); for (i = 0; i < array_values->len; i++) append_bands (array, g_array_index (array_values, guint, i), mode, model, log_object); if (!array->len) { inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "No known band selection values matched in +UBANDSEL response: '%s'", response); goto out; } out: if (inner_error) { g_propagate_error (error, inner_error); g_clear_pointer (&array, g_array_unref); } g_clear_pointer (&array_values, g_array_unref); g_free (dupstr); return array; } /*****************************************************************************/ /* UBANDSEL=X command builder */ static gint ubandsel_num_cmp (const guint *a, const guint *b) { return (*a - *b); } gchar * mm_ublox_build_ubandsel_set_command (GArray *bands, const gchar *model, GError **error) { GString *command = NULL; GArray *ubandsel_nums; guint num; guint i, j, k; if (bands->len == 1 && g_array_index (bands, MMModemBand, 0) == MM_MODEM_BAND_ANY) return g_strdup ("+UBANDSEL=0"); for (i = 0; i < G_N_ELEMENTS (band_configuration); i++) { if (g_str_has_prefix (model, band_configuration[i].model)) break; } if (i == G_N_ELEMENTS (band_configuration)) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Unknown modem model %s", model); return NULL; } ubandsel_nums = g_array_sized_new (FALSE, FALSE, sizeof (guint), bands->len); for (j = 0; j < bands->len; j++) { MMModemBand band; gboolean found = FALSE; band = g_array_index (bands, MMModemBand, j); /* Check to see if band is supported by the model */ for (k = 0; !found && k < G_N_ELEMENTS (band_configuration[i].bands_2g) && band_configuration[i].bands_2g[k]; k++) { if (band == band_configuration[i].bands_2g[k]) found = TRUE; } for (k = 0; !found && k < G_N_ELEMENTS (band_configuration[i].bands_3g) && band_configuration[i].bands_3g[k]; k++) { if (band == band_configuration[i].bands_3g[k]) found = TRUE; } for (k = 0; !found && k < G_N_ELEMENTS (band_configuration[i].bands_4g) && band_configuration[i].bands_4g[k]; k++) { if (band == band_configuration[i].bands_4g[k]) found = TRUE; } if (found) { num = band_to_num (band); g_assert (num != 0); g_array_append_val (ubandsel_nums, num); } } if (ubandsel_nums->len == 0) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "Given band combination is unsupported"); g_array_unref (ubandsel_nums); return NULL; } if (ubandsel_nums->len > 1) g_array_sort (ubandsel_nums, (GCompareFunc) ubandsel_num_cmp); /* Build command */ command = g_string_new ("+UBANDSEL="); for (i = 0; i < ubandsel_nums->len; i++) g_string_append_printf (command, "%s%u", i == 0 ? "" : ",", g_array_index (ubandsel_nums, guint, i)); return g_string_free (command, FALSE); } /*****************************************************************************/ /* Get mode to apply when ANY */ MMModemMode mm_ublox_get_modem_mode_any (const GArray *combinations) { guint i; MMModemMode any = MM_MODEM_MODE_NONE; guint any_bits_set = 0; for (i = 0; i < combinations->len; i++) { MMModemModeCombination *combination; guint bits_set; combination = &g_array_index (combinations, MMModemModeCombination, i); if (combination->preferred != MM_MODEM_MODE_NONE) continue; bits_set = mm_count_bits_set (combination->allowed); if (bits_set > any_bits_set) { any_bits_set = bits_set; any = combination->allowed; } } /* If combinations were processed via mm_ublox_parse_urat_test_response(), * we're sure that there will be at least one combination with preferred * 'none', so there must be some valid combination as result */ g_assert (any != MM_MODEM_MODE_NONE); return any; } /*****************************************************************************/ /* UACT common config */ typedef struct { guint num; MMModemBand band; } UactBandConfig; static const UactBandConfig uact_band_config[] = { /* GSM bands */ { .num = 900, .band = MM_MODEM_BAND_EGSM }, { .num = 1800, .band = MM_MODEM_BAND_DCS }, { .num = 1900, .band = MM_MODEM_BAND_PCS }, { .num = 850, .band = MM_MODEM_BAND_G850 }, { .num = 450, .band = MM_MODEM_BAND_G450 }, { .num = 480, .band = MM_MODEM_BAND_G480 }, { .num = 750, .band = MM_MODEM_BAND_G750 }, { .num = 380, .band = MM_MODEM_BAND_G380 }, { .num = 410, .band = MM_MODEM_BAND_G410 }, { .num = 710, .band = MM_MODEM_BAND_G710 }, { .num = 810, .band = MM_MODEM_BAND_G810 }, /* UMTS bands */ { .num = 1, .band = MM_MODEM_BAND_UTRAN_1 }, { .num = 2, .band = MM_MODEM_BAND_UTRAN_2 }, { .num = 3, .band = MM_MODEM_BAND_UTRAN_3 }, { .num = 4, .band = MM_MODEM_BAND_UTRAN_4 }, { .num = 5, .band = MM_MODEM_BAND_UTRAN_5 }, { .num = 6, .band = MM_MODEM_BAND_UTRAN_6 }, { .num = 7, .band = MM_MODEM_BAND_UTRAN_7 }, { .num = 8, .band = MM_MODEM_BAND_UTRAN_8 }, { .num = 9, .band = MM_MODEM_BAND_UTRAN_9 }, { .num = 10, .band = MM_MODEM_BAND_UTRAN_10 }, { .num = 11, .band = MM_MODEM_BAND_UTRAN_11 }, { .num = 12, .band = MM_MODEM_BAND_UTRAN_12 }, { .num = 13, .band = MM_MODEM_BAND_UTRAN_13 }, { .num = 14, .band = MM_MODEM_BAND_UTRAN_14 }, { .num = 19, .band = MM_MODEM_BAND_UTRAN_19 }, { .num = 20, .band = MM_MODEM_BAND_UTRAN_20 }, { .num = 21, .band = MM_MODEM_BAND_UTRAN_21 }, { .num = 22, .band = MM_MODEM_BAND_UTRAN_22 }, { .num = 25, .band = MM_MODEM_BAND_UTRAN_25 }, /* LTE bands */ { .num = 101, .band = MM_MODEM_BAND_EUTRAN_1 }, { .num = 102, .band = MM_MODEM_BAND_EUTRAN_2 }, { .num = 103, .band = MM_MODEM_BAND_EUTRAN_3 }, { .num = 104, .band = MM_MODEM_BAND_EUTRAN_4 }, { .num = 105, .band = MM_MODEM_BAND_EUTRAN_5 }, { .num = 106, .band = MM_MODEM_BAND_EUTRAN_6 }, { .num = 107, .band = MM_MODEM_BAND_EUTRAN_7 }, { .num = 108, .band = MM_MODEM_BAND_EUTRAN_8 }, { .num = 109, .band = MM_MODEM_BAND_EUTRAN_9 }, { .num = 110, .band = MM_MODEM_BAND_EUTRAN_10 }, { .num = 111, .band = MM_MODEM_BAND_EUTRAN_11 }, { .num = 112, .band = MM_MODEM_BAND_EUTRAN_12 }, { .num = 113, .band = MM_MODEM_BAND_EUTRAN_13 }, { .num = 114, .band = MM_MODEM_BAND_EUTRAN_14 }, { .num = 117, .band = MM_MODEM_BAND_EUTRAN_17 }, { .num = 118, .band = MM_MODEM_BAND_EUTRAN_18 }, { .num = 119, .band = MM_MODEM_BAND_EUTRAN_19 }, { .num = 120, .band = MM_MODEM_BAND_EUTRAN_20 }, { .num = 121, .band = MM_MODEM_BAND_EUTRAN_21 }, { .num = 122, .band = MM_MODEM_BAND_EUTRAN_22 }, { .num = 123, .band = MM_MODEM_BAND_EUTRAN_23 }, { .num = 124, .band = MM_MODEM_BAND_EUTRAN_24 }, { .num = 125, .band = MM_MODEM_BAND_EUTRAN_25 }, { .num = 126, .band = MM_MODEM_BAND_EUTRAN_26 }, { .num = 127, .band = MM_MODEM_BAND_EUTRAN_27 }, { .num = 128, .band = MM_MODEM_BAND_EUTRAN_28 }, { .num = 129, .band = MM_MODEM_BAND_EUTRAN_29 }, { .num = 130, .band = MM_MODEM_BAND_EUTRAN_30 }, { .num = 131, .band = MM_MODEM_BAND_EUTRAN_31 }, { .num = 132, .band = MM_MODEM_BAND_EUTRAN_32 }, { .num = 133, .band = MM_MODEM_BAND_EUTRAN_33 }, { .num = 134, .band = MM_MODEM_BAND_EUTRAN_34 }, { .num = 135, .band = MM_MODEM_BAND_EUTRAN_35 }, { .num = 136, .band = MM_MODEM_BAND_EUTRAN_36 }, { .num = 137, .band = MM_MODEM_BAND_EUTRAN_37 }, { .num = 138, .band = MM_MODEM_BAND_EUTRAN_38 }, { .num = 139, .band = MM_MODEM_BAND_EUTRAN_39 }, { .num = 140, .band = MM_MODEM_BAND_EUTRAN_40 }, { .num = 141, .band = MM_MODEM_BAND_EUTRAN_41 }, { .num = 142, .band = MM_MODEM_BAND_EUTRAN_42 }, { .num = 143, .band = MM_MODEM_BAND_EUTRAN_43 }, { .num = 144, .band = MM_MODEM_BAND_EUTRAN_44 }, { .num = 145, .band = MM_MODEM_BAND_EUTRAN_45 }, { .num = 146, .band = MM_MODEM_BAND_EUTRAN_46 }, { .num = 147, .band = MM_MODEM_BAND_EUTRAN_47 }, { .num = 148, .band = MM_MODEM_BAND_EUTRAN_48 }, }; static MMModemBand uact_num_to_band (guint num) { guint i; for (i = 0; i < G_N_ELEMENTS (uact_band_config); i++) { if (num == uact_band_config[i].num) return uact_band_config[i].band; } return MM_MODEM_BAND_UNKNOWN; } static guint uact_band_to_num (MMModemBand band) { guint i; for (i = 0; i < G_N_ELEMENTS (uact_band_config); i++) { if (band == uact_band_config[i].band) return uact_band_config[i].num; } return 0; } /*****************************************************************************/ /* UACT? response parser */ static GArray * uact_num_array_to_band_array (GArray *nums) { GArray *bands = NULL; guint i; if (!nums) return NULL; bands = g_array_sized_new (FALSE, FALSE, sizeof (MMModemBand), nums->len); for (i = 0; i < nums->len; i++) { MMModemBand band; band = uact_num_to_band (g_array_index (nums, guint, i)); g_array_append_val (bands, band); } return bands; } GArray * mm_ublox_parse_uact_response (const gchar *response, GError **error) { g_autoptr(GRegex) r = NULL; g_autoptr(GMatchInfo) match_info = NULL; GError *inner_error = NULL; GArray *nums = NULL; GArray *bands = NULL; /* * AT+UACT? * +UACT: ,,,900,1800,1,8,101,103,107,108,120,138 */ r = g_regex_new ("\\+UACT: ([^,]*),([^,]*),([^,]*),(.*)(?:\\r\\n)?", G_REGEX_DOLLAR_ENDONLY | G_REGEX_RAW, 0, NULL); g_assert (r != NULL); g_regex_match_full (r, response, strlen (response), 0, 0, &match_info, &inner_error); if (!inner_error && g_match_info_matches (match_info)) { g_autofree gchar *bandstr = NULL; bandstr = mm_get_string_unquoted_from_match_info (match_info, 4); nums = mm_parse_uint_list (bandstr, &inner_error); } if (inner_error) { g_propagate_error (error, inner_error); return NULL; } /* Convert to MMModemBand values */ if (nums) { bands = uact_num_array_to_band_array (nums); g_array_unref (nums); } if (!bands) g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "No known band selection values matched in +UACT response: '%s'", response); return bands; } /*****************************************************************************/ /* UACT=? response parser */ static GArray * parse_bands_from_string (const gchar *str, const gchar *group, gpointer log_object) { GArray *bands = NULL; GError *inner_error = NULL; GArray *nums; nums = mm_parse_uint_list (str, &inner_error); if (nums) { gchar *tmpstr; bands = uact_num_array_to_band_array (nums); tmpstr = mm_common_build_bands_string ((MMModemBand *)(gpointer)(bands->data), bands->len); mm_obj_dbg (log_object, "modem reports support for %s bands: %s", group, tmpstr); g_free (tmpstr); g_array_unref (nums); } else if (inner_error) { mm_obj_warn (log_object, "couldn't parse list of supported %s bands: %s", group, inner_error->message); g_clear_error (&inner_error); } return bands; } gboolean mm_ublox_parse_uact_test (const gchar *response, gpointer log_object, GArray **bands2g_out, GArray **bands3g_out, GArray **bands4g_out, GError **error) { g_autoptr(GRegex) r = NULL; g_autoptr(GMatchInfo) match_info = NULL; g_auto(GStrv) split = NULL; GError *inner_error = NULL; const gchar *bands2g_str = NULL; const gchar *bands3g_str = NULL; const gchar *bands4g_str = NULL; GArray *bands2g = NULL; GArray *bands3g = NULL; GArray *bands4g = NULL; g_assert (bands2g_out && bands3g_out && bands4g_out); /* * AT+UACT=? * +UACT: ,,,(900,1800),(1,8),(101,103,107,108,120),(138) */ r = g_regex_new ("\\+UACT: ([^,]*),([^,]*),([^,]*),(.*)(?:\\r\\n)?", G_REGEX_DOLLAR_ENDONLY | G_REGEX_RAW, 0, NULL); g_assert (r != NULL); g_regex_match_full (r, response, strlen (response), 0, 0, &match_info, &inner_error); if (inner_error) goto out; if (g_match_info_matches (match_info)) { g_autofree gchar *aux = NULL; guint n_groups; aux = mm_get_string_unquoted_from_match_info (match_info, 4); split = mm_split_string_groups (aux); n_groups = g_strv_length (split); if (n_groups >= 1) bands2g_str = split[0]; if (n_groups >= 2) bands3g_str = split[1]; if (n_groups >= 3) bands4g_str = split[2]; } if (!bands2g_str && !bands3g_str && !bands4g_str) { inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "frequency groups not found: %s", response); goto out; } bands2g = parse_bands_from_string (bands2g_str, "2G", log_object); bands3g = parse_bands_from_string (bands3g_str, "3G", log_object); bands4g = parse_bands_from_string (bands4g_str, "4G", log_object); if (!bands2g->len && !bands3g->len && !bands4g->len) { inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "no supported frequencies reported: %s", response); goto out; } /* success */ out: if (inner_error) { if (bands2g) g_array_unref (bands2g); if (bands3g) g_array_unref (bands3g); if (bands4g) g_array_unref (bands4g); g_propagate_error (error, inner_error); return FALSE; } *bands2g_out = bands2g; *bands3g_out = bands3g; *bands4g_out = bands4g; return TRUE; } /*****************************************************************************/ /* UACT=X command builder */ gchar * mm_ublox_build_uact_set_command (GArray *bands, GError **error) { GString *command; /* Build command */ command = g_string_new ("+UACT=,,,"); if (bands->len == 1 && g_array_index (bands, MMModemBand, 0) == MM_MODEM_BAND_ANY) g_string_append (command, "0"); else { guint i; for (i = 0; i < bands->len; i++) { MMModemBand band; guint num; band = g_array_index (bands, MMModemBand, i); num = uact_band_to_num (band); if (!num) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "Band unsupported by this plugin: %s", mm_modem_band_get_string (band)); g_string_free (command, TRUE); return NULL; } g_string_append_printf (command, "%s%u", i == 0 ? "" : ",", num); } } return g_string_free (command, FALSE); } /*****************************************************************************/ /* URAT? response parser */ gboolean mm_ublox_parse_urat_read_response (const gchar *response, gpointer log_object, MMModemMode *out_allowed, MMModemMode *out_preferred, GError **error) { g_autoptr(GRegex) r = NULL; g_autoptr(GMatchInfo) match_info = NULL; GError *inner_error = NULL; MMModemMode allowed = MM_MODEM_MODE_NONE; MMModemMode preferred = MM_MODEM_MODE_NONE; g_autofree gchar *allowed_str = NULL; g_autofree gchar *preferred_str = NULL; g_assert (out_allowed != NULL && out_preferred != NULL); /* Response may be e.g.: * +URAT: 1,2 * +URAT: 1 */ r = g_regex_new ("\\+URAT: (\\d+)(?:,(\\d+))?(?:\\r\\n)?", 0, 0, NULL); g_assert (r != NULL); g_regex_match_full (r, response, strlen (response), 0, 0, &match_info, &inner_error); if (!inner_error && g_match_info_matches (match_info)) { guint value = 0; /* Selected item is mandatory */ if (!mm_get_uint_from_match_info (match_info, 1, &value)) { inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't read AcT selected value"); goto out; } if (value >= G_N_ELEMENTS (ublox_combinations)) { inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Unexpected AcT selected value: %u", value); goto out; } allowed = ublox_combinations[value]; allowed_str = mm_modem_mode_build_string_from_mask (allowed); mm_obj_dbg (log_object, "current allowed modes retrieved: %s", allowed_str); /* Preferred item is optional */ if (mm_get_uint_from_match_info (match_info, 2, &value)) { if (value >= G_N_ELEMENTS (ublox_combinations)) { inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Unexpected AcT preferred value: %u", value); goto out; } preferred = ublox_combinations[value]; preferred_str = mm_modem_mode_build_string_from_mask (preferred); mm_obj_dbg (log_object, "current preferred modes retrieved: %s", preferred_str); if (mm_count_bits_set (preferred) != 1) { inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "AcT preferred value should be a single AcT: %s", preferred_str); goto out; } if (!(allowed & preferred)) { inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "AcT preferred value (%s) not a subset of the allowed value (%s)", preferred_str, allowed_str); goto out; } } } out: if (inner_error) { g_propagate_error (error, inner_error); return FALSE; } if (allowed == MM_MODEM_MODE_NONE) { inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't parse +URAT response: %s", response); return FALSE; } *out_allowed = allowed; *out_preferred = preferred; return TRUE; } /*****************************************************************************/ /* URAT=X command builder */ static gboolean append_rat_value (GString *str, MMModemMode mode, GError **error) { guint i; for (i = 0; i < G_N_ELEMENTS (ublox_combinations); i++) { if (ublox_combinations[i] == mode) { g_string_append_printf (str, "%u", i); return TRUE; } } g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "No AcT value matches requested mode"); return FALSE; } gchar * mm_ublox_build_urat_set_command (MMModemMode allowed, MMModemMode preferred, GError **error) { GString *command; command = g_string_new ("+URAT="); if (!append_rat_value (command, allowed, error)) { g_string_free (command, TRUE); return NULL; } if (preferred != MM_MODEM_MODE_NONE) { g_string_append (command, ","); if (!append_rat_value (command, preferred, error)) { g_string_free (command, TRUE); return NULL; } } return g_string_free (command, FALSE); } /*****************************************************************************/ /* +UAUTHREQ=? test parser */ MMUbloxBearerAllowedAuth mm_ublox_parse_uauthreq_test (const char *response, gpointer log_object, GError **error) { MMUbloxBearerAllowedAuth mask = MM_UBLOX_BEARER_ALLOWED_AUTH_UNKNOWN; GError *inner_error = NULL; GArray *allowed_auths = NULL; gchar **split; guint split_len; /* * Response may be like: * AT+UAUTHREQ=? * +UAUTHREQ: (1-4),(0-2),, */ response = mm_strip_tag (response, "+UAUTHREQ:"); split = mm_split_string_groups (response); split_len = g_strv_length (split); if (split_len < 2) { inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Unexpected number of groups in +UAUTHREQ=? response: %u", g_strv_length (split)); goto out; } allowed_auths = mm_parse_uint_list (split[1], &inner_error); if (inner_error) goto out; if (allowed_auths) { guint i; for (i = 0; i < allowed_auths->len; i++) { guint val; val = g_array_index (allowed_auths, guint, i); switch (val) { case 0: mask |= MM_UBLOX_BEARER_ALLOWED_AUTH_NONE; break; case 1: mask |= MM_UBLOX_BEARER_ALLOWED_AUTH_PAP; break; case 2: mask |= MM_UBLOX_BEARER_ALLOWED_AUTH_CHAP; break; case 3: mask |= MM_UBLOX_BEARER_ALLOWED_AUTH_AUTO; break; default: mm_obj_warn (log_object, "unexpected +UAUTHREQ value: %u", val); break; } } } if (!mask) { inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "No supported authentication methods in +UAUTHREQ=? response"); goto out; } out: g_strfreev (split); if (allowed_auths) g_array_unref (allowed_auths); if (inner_error) { g_propagate_error (error, inner_error); return MM_UBLOX_BEARER_ALLOWED_AUTH_UNKNOWN; } return mask; } /*****************************************************************************/ /* +UGCNTRD response parser */ gboolean mm_ublox_parse_ugcntrd_response_for_cid (const gchar *response, guint in_cid, guint64 *out_session_tx_bytes, guint64 *out_session_rx_bytes, guint64 *out_total_tx_bytes, guint64 *out_total_rx_bytes, GError **error) { g_autoptr(GRegex) r = NULL; g_autoptr(GMatchInfo) match_info = NULL; GError *inner_error = NULL; guint64 session_tx_bytes = 0; guint64 session_rx_bytes = 0; guint64 total_tx_bytes = 0; guint64 total_rx_bytes = 0; gboolean matched = FALSE; /* Response may be e.g.: * +UGCNTRD: 31,2704,1819,2724,1839 * We assume only ONE line is returned. */ r = g_regex_new ("\\+UGCNTRD:\\s*(\\d+),\\s*(\\d+),\\s*(\\d+),\\s*(\\d+),\\s*(\\d+)", G_REGEX_DOLLAR_ENDONLY | G_REGEX_RAW, 0, NULL); g_assert (r != NULL); /* Report invalid CID given */ if (!in_cid) { inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Invalid CID given"); goto out; } g_regex_match_full (r, response, strlen (response), 0, 0, &match_info, &inner_error); while (!inner_error && g_match_info_matches (match_info)) { guint cid = 0; /* Matched CID? */ if (!mm_get_uint_from_match_info (match_info, 1, &cid) || cid != in_cid) { g_match_info_next (match_info, &inner_error); continue; } if (out_session_tx_bytes && !mm_get_u64_from_match_info (match_info, 2, &session_tx_bytes)) { inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Error parsing session TX bytes"); goto out; } if (out_session_rx_bytes && !mm_get_u64_from_match_info (match_info, 3, &session_rx_bytes)) { inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Error parsing session RX bytes"); goto out; } if (out_total_tx_bytes && !mm_get_u64_from_match_info (match_info, 4, &total_tx_bytes)) { inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Error parsing total TX bytes"); goto out; } if (out_total_rx_bytes && !mm_get_u64_from_match_info (match_info, 5, &total_rx_bytes)) { inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Error parsing total RX bytes"); goto out; } matched = TRUE; break; } if (!matched) { inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "No statistics found for CID %u", in_cid); goto out; } out: if (inner_error) { g_propagate_error (error, inner_error); return FALSE; } if (out_session_tx_bytes) *out_session_tx_bytes = session_tx_bytes; if (out_session_rx_bytes) *out_session_rx_bytes = session_rx_bytes; if (out_total_tx_bytes) *out_total_tx_bytes = total_tx_bytes; if (out_total_rx_bytes) *out_total_rx_bytes = total_rx_bytes; return TRUE; } ModemManager-1.23.4-dev/src/plugins/ublox/mm-modem-helpers-ublox.h000066400000000000000000000220041456466623000247740ustar00rootroot00000000000000/* -*- 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) 2016 Aleksander Morgado */ #ifndef MM_MODEM_HELPERS_UBLOX_H #define MM_MODEM_HELPERS_UBLOX_H #include #include /*****************************************************************************/ /* AT Commands Support */ typedef enum { FEATURE_SUPPORT_UNKNOWN, FEATURE_SUPPORTED, FEATURE_UNSUPPORTED, } FeatureSupport; typedef enum { SETTINGS_UPDATE_METHOD_UNKNOWN, SETTINGS_UPDATE_METHOD_CFUN, SETTINGS_UPDATE_METHOD_COPS, } SettingsUpdateMethod; typedef struct UbloxSupportConfig { gboolean loaded; SettingsUpdateMethod method; FeatureSupport uact; FeatureSupport ubandsel; } UbloxSupportConfig; /*****************************************************************************/ /* +UPINCNT response parser */ gboolean mm_ublox_parse_upincnt_response (const gchar *response, guint *out_pin_attempts, guint *out_pin2_attempts, guint *out_puk_attempts, guint *out_puk2_attempts, GError **error); /*****************************************************************************/ /* UUSBCONF? response parser */ typedef enum { /*< underscore_name=mm_ublox_usb_profile >*/ MM_UBLOX_USB_PROFILE_UNKNOWN, MM_UBLOX_USB_PROFILE_RNDIS, MM_UBLOX_USB_PROFILE_ECM, MM_UBLOX_USB_PROFILE_BACK_COMPATIBLE, } MMUbloxUsbProfile; gboolean mm_ublox_parse_uusbconf_response (const gchar *response, MMUbloxUsbProfile *out_profile, GError **error); /*****************************************************************************/ /* UBMCONF? response parser */ typedef enum { /*< underscore_name=mm_ublox_networking_mode >*/ MM_UBLOX_NETWORKING_MODE_UNKNOWN, MM_UBLOX_NETWORKING_MODE_ROUTER, MM_UBLOX_NETWORKING_MODE_BRIDGE, } MMUbloxNetworkingMode; gboolean mm_ublox_parse_ubmconf_response (const gchar *response, MMUbloxNetworkingMode *out_mode, GError **error); /*****************************************************************************/ /* UIPADDR=N response parser */ gboolean mm_ublox_parse_uipaddr_response (const gchar *response, guint *out_cid, gchar **out_if_name, gchar **out_ipv4_address, gchar **out_ipv4_subnet, gchar **out_ipv6_global_address, gchar **out_ipv6_link_local_address, GError **error); /*****************************************************************************/ /* CFUN? response parser */ gboolean mm_ublox_parse_cfun_response (const gchar *response, MMModemPowerState *out_state, GError **error); /*****************************************************************************/ /* URAT=? response parser */ GArray *mm_ublox_parse_urat_test_response (const gchar *response, gpointer log_object, GError **error); /*****************************************************************************/ /* Model-based config support loading */ gboolean mm_ublox_get_support_config (const gchar *model, UbloxSupportConfig *config, GError **error); /*****************************************************************************/ /* Model-based supported modes filtering */ GArray *mm_ublox_filter_supported_modes (const gchar *model, GArray *combinations, gpointer logger, GError **error); /*****************************************************************************/ /* Model-based supported bands loading */ GArray *mm_ublox_get_supported_bands (const gchar *model, gpointer log_object, GError **error); /*****************************************************************************/ /* UBANDSEL? response parser */ GArray *mm_ublox_parse_ubandsel_response (const gchar *response, const gchar *model, gpointer log_object, GError **error); /*****************************************************************************/ /* UBANDSEL=X command builder */ gchar *mm_ublox_build_ubandsel_set_command (GArray *bands, const gchar *model, GError **error); /*****************************************************************************/ /* UACT? response parser */ GArray *mm_ublox_parse_uact_response (const gchar *response, GError **error); /*****************************************************************************/ /* UACT=? test parser */ gboolean mm_ublox_parse_uact_test (const gchar *response, gpointer log_object, GArray **bands_2g, GArray **bands_3g, GArray **bands_4g, GError **error); /*****************************************************************************/ /* UACT=X command builder */ gchar *mm_ublox_build_uact_set_command (GArray *bands, GError **error); /*****************************************************************************/ /* Get mode to apply when ANY */ MMModemMode mm_ublox_get_modem_mode_any (const GArray *combinations); /*****************************************************************************/ /* URAT? response parser */ gboolean mm_ublox_parse_urat_read_response (const gchar *response, gpointer log_object, MMModemMode *out_allowed, MMModemMode *out_preferred, GError **error); /*****************************************************************************/ /* URAT=X command builder */ gchar *mm_ublox_build_urat_set_command (MMModemMode allowed, MMModemMode preferred, GError **error); /*****************************************************************************/ /* +UAUTHREQ=? test parser */ typedef enum { /*< underscore_name=mm_ublox_bearer_allowed_auth >*/ MM_UBLOX_BEARER_ALLOWED_AUTH_UNKNOWN = 0, MM_UBLOX_BEARER_ALLOWED_AUTH_NONE = 1 << 0, MM_UBLOX_BEARER_ALLOWED_AUTH_PAP = 1 << 1, MM_UBLOX_BEARER_ALLOWED_AUTH_CHAP = 1 << 2, MM_UBLOX_BEARER_ALLOWED_AUTH_AUTO = 1 << 3, } MMUbloxBearerAllowedAuth; MMUbloxBearerAllowedAuth mm_ublox_parse_uauthreq_test (const char *response, gpointer log_object, GError **error); /*****************************************************************************/ /* +UGCNTRD response parser */ gboolean mm_ublox_parse_ugcntrd_response_for_cid (const gchar *response, guint in_cid, guint64 *session_tx_bytes, guint64 *session_rx_bytes, guint64 *total_tx_bytes, guint64 *total_rx_bytes, GError **error); #endif /* MM_MODEM_HELPERS_UBLOX_H */ ModemManager-1.23.4-dev/src/plugins/ublox/mm-plugin-ublox.c000066400000000000000000000220211456466623000235230ustar00rootroot00000000000000/* -*- 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) 2016 Aleksander Morgado */ #include #include #define _LIBMM_INSIDE_MM #include #include "mm-log-object.h" #include "mm-serial-parsers.h" #include "mm-broadband-modem-ublox.h" #include "mm-plugin-common.h" #define MM_TYPE_PLUGIN_UBLOX mm_plugin_ublox_get_type () MM_DEFINE_PLUGIN (UBLOX, ublox, Ublox) /*****************************************************************************/ static MMBaseModem * create_modem (MMPlugin *self, const gchar *sysfs_path, const gchar *physdev, const gchar **drivers, guint16 vendor, guint16 product, guint16 subsystem_vendor, GList *probes, GError **error) { return MM_BASE_MODEM (mm_broadband_modem_ublox_new (sysfs_path, physdev, drivers, mm_plugin_get_name (self), vendor, product)); } /*****************************************************************************/ /* Custom init context */ typedef struct { MMPortSerialAt *port; GRegex *ready_regex; guint timeout_id; gint wait_timeout_secs; } CustomInitContext; static void custom_init_context_free (CustomInitContext *ctx) { g_assert (!ctx->timeout_id); g_regex_unref (ctx->ready_regex); g_object_unref (ctx->port); g_slice_free (CustomInitContext, ctx); } static gboolean ublox_custom_init_finish (MMPortProbe *probe, GAsyncResult *result, GError **error) { return g_task_propagate_boolean (G_TASK (result), error); } static gboolean ready_timeout (GTask *task) { CustomInitContext *ctx; MMPortProbe *probe; ctx = g_task_get_task_data (task); probe = g_task_get_source_object (task); ctx->timeout_id = 0; mm_port_serial_at_add_unsolicited_msg_handler (ctx->port, ctx->ready_regex, NULL, NULL, NULL); mm_obj_dbg (probe, "timed out waiting for READY unsolicited message"); /* not an error really, we didn't probe anything yet, that's all */ g_task_return_boolean (task, TRUE); g_object_unref (task); return G_SOURCE_REMOVE; } static void ready_received (MMPortSerialAt *port, GMatchInfo *info, GTask *task) { CustomInitContext *ctx; MMPortProbe *probe; ctx = g_task_get_task_data (task); probe = g_task_get_source_object (task); g_source_remove (ctx->timeout_id); ctx->timeout_id = 0; mm_obj_dbg (probe, "received READY: port is AT"); /* Flag as an AT port right away */ mm_port_probe_set_result_at (probe, TRUE); g_task_return_boolean (task, TRUE); g_object_unref (task); } static void wait_for_ready (GTask *task) { CustomInitContext *ctx; MMPortProbe *probe; ctx = g_task_get_task_data (task); probe = g_task_get_source_object (task); mm_obj_dbg (probe, "waiting for READY unsolicited message..."); /* Configure a regex on the TTY, so that we stop the custom init * as soon as +READY URC is received */ mm_port_serial_at_add_unsolicited_msg_handler (ctx->port, ctx->ready_regex, (MMPortSerialAtUnsolicitedMsgFn) ready_received, task, NULL); mm_obj_dbg (probe, "waiting %d seconds for init timeout", ctx->wait_timeout_secs); /* Otherwise, let the custom init timeout in some seconds. */ ctx->timeout_id = g_timeout_add_seconds (ctx->wait_timeout_secs, (GSourceFunc) ready_timeout, task); } static void quick_at_ready (MMPortSerialAt *port, GAsyncResult *res, GTask *task) { MMPortProbe *probe; g_autoptr(GError) error = NULL; g_autofree gchar *response = NULL; probe = g_task_get_source_object (task); response = mm_port_serial_at_command_finish (port, res, &error); if (error) { /* On a timeout error, wait for READY URC */ if (g_error_matches (error, MM_SERIAL_ERROR, MM_SERIAL_ERROR_RESPONSE_TIMEOUT)) { wait_for_ready (task); return; } /* On an unknown error, make it fatal */ if (!mm_serial_parser_v1_is_known_error (error)) { mm_obj_warn (probe, "custom port initialization logic failed: %s", error->message); goto out; } } mm_obj_dbg (probe, "port is AT"); mm_port_probe_set_result_at (probe, TRUE); out: g_task_return_boolean (task, TRUE); g_object_unref (task); } static void ublox_custom_init (MMPortProbe *probe, MMPortSerialAt *port, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; CustomInitContext *ctx; gint wait_timeout_secs; task = g_task_new (probe, cancellable, callback, user_data); /* If no explicit READY_DELAY configured, we don't need a custom init procedure */ wait_timeout_secs = mm_kernel_device_get_property_as_int (mm_port_probe_peek_port (probe), "ID_MM_UBLOX_PORT_READY_DELAY"); if (wait_timeout_secs <= 0) { g_task_return_boolean (task, TRUE); g_object_unref (task); return; } ctx = g_slice_new0 (CustomInitContext); ctx->wait_timeout_secs = wait_timeout_secs; ctx->port = g_object_ref (port); ctx->ready_regex = g_regex_new ("\\r\\n\\+AT:\\s*READY\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); g_task_set_task_data (task, ctx, (GDestroyNotify) custom_init_context_free); /* If the device hasn't been plugged in right away, we assume it was already * running for some time. We validate the assumption with a quick AT probe, * and if it times out, we run the explicit READY wait from scratch (e.g. * to cope with the case where MM starts after the TTY has been exposed but * where the device was also just reseted) */ if (!mm_device_get_hotplugged (mm_port_probe_peek_device (probe))) { mm_port_serial_at_command (ctx->port, "AT", 1, FALSE, /* raw */ FALSE, /* allow_cached */ g_task_get_cancellable (task), (GAsyncReadyCallback)quick_at_ready, task); return; } /* Device hotplugged and has a defined ready delay, wait for READY URC */ wait_for_ready (task); } /*****************************************************************************/ MM_PLUGIN_NAMED_CREATOR_SCOPE MMPlugin * mm_plugin_create_ublox (void) { static const gchar *subsystems[] = { "tty", "net", NULL }; static const guint16 vendor_ids[] = { 0x1546, 0 }; static const gchar *vendor_strings[] = { "u-blox", NULL }; static const MMAsyncMethod custom_init = { .async = G_CALLBACK (ublox_custom_init), .finish = G_CALLBACK (ublox_custom_init_finish), }; return MM_PLUGIN (g_object_new (MM_TYPE_PLUGIN_UBLOX, MM_PLUGIN_NAME, MM_MODULE_NAME, MM_PLUGIN_ALLOWED_SUBSYSTEMS, subsystems, MM_PLUGIN_ALLOWED_VENDOR_IDS, vendor_ids, MM_PLUGIN_ALLOWED_VENDOR_STRINGS, vendor_strings, MM_PLUGIN_ALLOWED_AT, TRUE, MM_PLUGIN_SEND_DELAY, (guint64) 0, MM_PLUGIN_CUSTOM_INIT, &custom_init, NULL)); } static void mm_plugin_ublox_init (MMPluginUblox *self) { } static void mm_plugin_ublox_class_init (MMPluginUbloxClass *klass) { MMPluginClass *plugin_class = MM_PLUGIN_CLASS (klass); plugin_class->create_modem = create_modem; } ModemManager-1.23.4-dev/src/plugins/ublox/mm-sim-ublox.c000066400000000000000000000113251456466623000230220ustar00rootroot00000000000000/* -*- 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) 2018 Aleksander Morgado */ #include #include #include #include #include #include #include #define _LIBMM_INSIDE_MM #include #include "mm-log.h" #include "mm-modem-helpers.h" #include "mm-base-modem-at.h" #include "mm-sim-ublox.h" G_DEFINE_TYPE (MMSimUblox, mm_sim_ublox, MM_TYPE_BASE_SIM) /*****************************************************************************/ /* SIM identifier loading */ static gchar * load_sim_identifier_finish (MMBaseSim *self, GAsyncResult *res, GError **error) { return g_task_propagate_pointer (G_TASK (res), error); } static void parent_load_sim_identifier_ready (MMSimUblox *self, GAsyncResult *res, GTask *task) { GError *error = NULL; gchar *simid; simid = MM_BASE_SIM_CLASS (mm_sim_ublox_parent_class)->load_sim_identifier_finish (MM_BASE_SIM (self), res, &error); if (simid) g_task_return_pointer (task, simid, g_free); else g_task_return_error (task, error); g_object_unref (task); } static void ccid_ready (MMBaseModem *modem, GAsyncResult *res, GTask *task) { MMBaseSim *self; const gchar *response; gchar *parsed; response = mm_base_modem_at_command_finish (modem, res, NULL); if (!response) goto error; response = mm_strip_tag (response, "+CCID:"); if (!response) goto error; parsed = mm_3gpp_parse_iccid (response, NULL); if (parsed) { g_task_return_pointer (task, parsed, g_free); g_object_unref (task); return; } error: /* Chain up to parent method to for devices that don't support +CCID properly */ self = g_task_get_source_object (task); MM_BASE_SIM_CLASS (mm_sim_ublox_parent_class)->load_sim_identifier (self, (GAsyncReadyCallback) parent_load_sim_identifier_ready, task); } static void load_sim_identifier (MMBaseSim *self, GAsyncReadyCallback callback, gpointer user_data) { MMBaseModem *modem = NULL; g_object_get (self, MM_BASE_SIM_MODEM, &modem, NULL); mm_base_modem_at_command ( modem, "+CCID", 5, FALSE, (GAsyncReadyCallback)ccid_ready, g_task_new (self, NULL, callback, user_data)); g_object_unref (modem); } /*****************************************************************************/ MMBaseSim * mm_sim_ublox_new_finish (GAsyncResult *res, GError **error) { GObject *source; GObject *sim; source = g_async_result_get_source_object (res); sim = g_async_initable_new_finish (G_ASYNC_INITABLE (source), res, error); g_object_unref (source); if (!sim) return NULL; /* Only export valid SIMs */ mm_base_sim_export (MM_BASE_SIM (sim)); return MM_BASE_SIM (sim); } void mm_sim_ublox_new (MMBaseModem *modem, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { g_async_initable_new_async (MM_TYPE_SIM_UBLOX, G_PRIORITY_DEFAULT, cancellable, callback, user_data, MM_BASE_SIM_MODEM, modem, "active", TRUE, /* by default always active */ NULL); } static void mm_sim_ublox_init (MMSimUblox *self) { } static void mm_sim_ublox_class_init (MMSimUbloxClass *klass) { MMBaseSimClass *base_sim_class = MM_BASE_SIM_CLASS (klass); base_sim_class->load_sim_identifier = load_sim_identifier; base_sim_class->load_sim_identifier_finish = load_sim_identifier_finish; } ModemManager-1.23.4-dev/src/plugins/ublox/mm-sim-ublox.h000066400000000000000000000037351456466623000230350ustar00rootroot00000000000000/* -*- 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) 2018 Aleksander Morgado */ #ifndef MM_SIM_UBLOX_H #define MM_SIM_UBLOX_H #include #include #include "mm-base-sim.h" #define MM_TYPE_SIM_UBLOX (mm_sim_ublox_get_type ()) #define MM_SIM_UBLOX(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_SIM_UBLOX, MMSimUblox)) #define MM_SIM_UBLOX_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_SIM_UBLOX, MMSimUbloxClass)) #define MM_IS_SIM_UBLOX(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_SIM_UBLOX)) #define MM_IS_SIM_UBLOX_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_SIM_UBLOX)) #define MM_SIM_UBLOX_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_SIM_UBLOX, MMSimUbloxClass)) typedef struct _MMSimUblox MMSimUblox; typedef struct _MMSimUbloxClass MMSimUbloxClass; struct _MMSimUblox { MMBaseSim parent; }; struct _MMSimUbloxClass { MMBaseSimClass parent; }; GType mm_sim_ublox_get_type (void); void mm_sim_ublox_new (MMBaseModem *modem, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); MMBaseSim *mm_sim_ublox_new_finish (GAsyncResult *res, GError **error); #endif /* MM_SIM_UBLOX_H */ ModemManager-1.23.4-dev/src/plugins/ublox/tests/000077500000000000000000000000001456466623000214705ustar00rootroot00000000000000ModemManager-1.23.4-dev/src/plugins/ublox/tests/test-modem-helpers-ublox.c000066400000000000000000001076301456466623000265100ustar00rootroot00000000000000/* -*- 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) 2016 Aleksander Morgado */ #include #include #include #include #include #define _LIBMM_INSIDE_MM #include #include "mm-log-test.h" #include "mm-modem-helpers.h" #include "mm-modem-helpers-ublox.h" #include "test-helpers.h" /*****************************************************************************/ /* Test +UPINCNT responses */ typedef struct { const gchar *str; guint pin_attempts; guint pin2_attempts; guint puk_attempts; guint puk2_attempts; } UpinCntResponseTest; static const UpinCntResponseTest upincnt_response_tests[] = { { .str = "+UPINCNT: 3,3,10,10\r\n", .pin_attempts = 3, .pin2_attempts = 3, .puk_attempts = 10, .puk2_attempts = 10 }, { .str = "+UPINCNT: 0,3,5,5\r\n", .pin_attempts = 0, .pin2_attempts = 3, .puk_attempts = 5, .puk2_attempts = 5 }, { .str = "+UPINCNT: 0,0,0,0\r\n", .pin_attempts = 0, .pin2_attempts = 0, .puk_attempts = 0, .puk2_attempts = 0 }, }; static void test_upincnt_response (void) { guint i; for (i = 0; i < G_N_ELEMENTS (upincnt_response_tests); i++) { GError *error = NULL; gboolean success; guint pin_attempts = G_MAXUINT; guint pin2_attempts = G_MAXUINT; guint puk_attempts = G_MAXUINT; guint puk2_attempts = G_MAXUINT; success = mm_ublox_parse_upincnt_response (upincnt_response_tests[i].str, &pin_attempts, &pin2_attempts, &puk_attempts, &puk2_attempts, &error); g_assert_no_error (error); g_assert (success); g_assert_cmpuint (upincnt_response_tests[i].pin_attempts, ==, pin_attempts); g_assert_cmpuint (upincnt_response_tests[i].pin2_attempts, ==, pin2_attempts); g_assert_cmpuint (upincnt_response_tests[i].puk_attempts, ==, puk_attempts); g_assert_cmpuint (upincnt_response_tests[i].puk2_attempts, ==, puk2_attempts); } } /*****************************************************************************/ /* Test UUSBCONF? responses */ typedef struct { const gchar *str; MMUbloxUsbProfile profile; } UusbconfResponseTest; static const UusbconfResponseTest uusbconf_response_tests[] = { { .str = "+UUSBCONF: 3,\"RNDIS\",,\"0x1146\"\r\n", .profile = MM_UBLOX_USB_PROFILE_RNDIS }, { .str = "+UUSBCONF: 2,\"ECM\",,\"0x1143\"\r\n", .profile = MM_UBLOX_USB_PROFILE_ECM }, { .str = "+UUSBCONF: 0,\"\",,\"0x1141\"\r\n", .profile = MM_UBLOX_USB_PROFILE_BACK_COMPATIBLE }, }; static void test_uusbconf_response (void) { guint i; for (i = 0; i < G_N_ELEMENTS (uusbconf_response_tests); i++) { MMUbloxUsbProfile profile = MM_UBLOX_USB_PROFILE_UNKNOWN; GError *error = NULL; gboolean success; success = mm_ublox_parse_uusbconf_response (uusbconf_response_tests[i].str, &profile, &error); g_assert_no_error (error); g_assert (success); g_assert_cmpuint (uusbconf_response_tests[i].profile, ==, profile); } } /*****************************************************************************/ /* Test UBMCONF? responses */ typedef struct { const gchar *str; MMUbloxNetworkingMode mode; } UbmconfResponseTest; static const UbmconfResponseTest ubmconf_response_tests[] = { { .str = "+UBMCONF: 1\r\n", .mode = MM_UBLOX_NETWORKING_MODE_ROUTER }, { .str = "+UBMCONF: 2\r\n", .mode = MM_UBLOX_NETWORKING_MODE_BRIDGE }, }; static void test_ubmconf_response (void) { guint i; for (i = 0; i < G_N_ELEMENTS (ubmconf_response_tests); i++) { MMUbloxNetworkingMode mode = MM_UBLOX_NETWORKING_MODE_UNKNOWN; GError *error = NULL; gboolean success; success = mm_ublox_parse_ubmconf_response (ubmconf_response_tests[i].str, &mode, &error); g_assert_no_error (error); g_assert (success); g_assert_cmpuint (ubmconf_response_tests[i].mode, ==, mode); } } /*****************************************************************************/ /* Test UIPADDR=N responses */ typedef struct { const gchar *str; guint cid; const gchar *if_name; const gchar *ipv4_address; const gchar *ipv4_subnet; const gchar *ipv6_global_address; const gchar *ipv6_link_local_address; } UipaddrResponseTest; static const UipaddrResponseTest uipaddr_response_tests[] = { { .str = "+UIPADDR: 1,\"ccinet0\",\"5.168.120.13\",\"255.255.255.0\",\"\",\"\"", .cid = 1, .if_name = "ccinet0", .ipv4_address = "5.168.120.13", .ipv4_subnet = "255.255.255.0", }, { .str = "+UIPADDR: 2,\"ccinet1\",\"\",\"\",\"2001::1:200:FF:FE00:0/64\",\"FE80::200:FF:FE00:0/64\"", .cid = 2, .if_name = "ccinet1", .ipv6_global_address = "2001::1:200:FF:FE00:0/64", .ipv6_link_local_address = "FE80::200:FF:FE00:0/64", }, { .str = "+UIPADDR: 3,\"ccinet2\",\"5.10.100.2\",\"255.255.255.0\",\"2001::1:200:FF:FE00:0/64\",\"FE80::200:FF:FE00:0/64\"", .cid = 3, .if_name = "ccinet2", .ipv4_address = "5.10.100.2", .ipv4_subnet = "255.255.255.0", .ipv6_global_address = "2001::1:200:FF:FE00:0/64", .ipv6_link_local_address = "FE80::200:FF:FE00:0/64", }, }; static void test_uipaddr_response (void) { guint i; for (i = 0; i < G_N_ELEMENTS (uipaddr_response_tests); i++) { GError *error = NULL; gboolean success; guint cid = G_MAXUINT; gchar *if_name = NULL; gchar *ipv4_address = NULL; gchar *ipv4_subnet = NULL; gchar *ipv6_global_address = NULL; gchar *ipv6_link_local_address = NULL; success = mm_ublox_parse_uipaddr_response (uipaddr_response_tests[i].str, &cid, &if_name, &ipv4_address, &ipv4_subnet, &ipv6_global_address, &ipv6_link_local_address, &error); g_assert_no_error (error); g_assert (success); g_assert_cmpuint (uipaddr_response_tests[i].cid, ==, cid); g_assert_cmpstr (uipaddr_response_tests[i].if_name, ==, if_name); g_assert_cmpstr (uipaddr_response_tests[i].ipv4_address, ==, ipv4_address); g_assert_cmpstr (uipaddr_response_tests[i].ipv4_subnet, ==, ipv4_subnet); g_assert_cmpstr (uipaddr_response_tests[i].ipv6_global_address, ==, ipv6_global_address); g_assert_cmpstr (uipaddr_response_tests[i].ipv6_link_local_address, ==, ipv6_link_local_address); g_free (if_name); g_free (ipv4_address); g_free (ipv4_subnet); g_free (ipv6_global_address); g_free (ipv6_link_local_address); } } /*****************************************************************************/ /* Test CFUN? response */ typedef struct { const gchar *str; MMModemPowerState state; } CfunQueryTest; static const CfunQueryTest cfun_query_tests[] = { { "+CFUN: 1", MM_MODEM_POWER_STATE_ON }, { "+CFUN: 1,0", MM_MODEM_POWER_STATE_ON }, { "+CFUN: 0", MM_MODEM_POWER_STATE_LOW }, { "+CFUN: 0,0", MM_MODEM_POWER_STATE_LOW }, { "+CFUN: 4", MM_MODEM_POWER_STATE_LOW }, { "+CFUN: 4,0", MM_MODEM_POWER_STATE_LOW }, { "+CFUN: 19", MM_MODEM_POWER_STATE_LOW }, { "+CFUN: 19,0", MM_MODEM_POWER_STATE_LOW }, }; static void test_cfun_response (void) { guint i; for (i = 0; i < G_N_ELEMENTS (cfun_query_tests); i++) { GError *error = NULL; gboolean success; MMModemPowerState state = MM_MODEM_POWER_STATE_UNKNOWN; success = mm_ublox_parse_cfun_response (cfun_query_tests[i].str, &state, &error); g_assert_no_error (error); g_assert (success); g_assert_cmpuint (cfun_query_tests[i].state, ==, state); } } /*****************************************************************************/ /* Test URAT=? responses and model based filtering */ static void compare_combinations (const gchar *response, const gchar *model, const MMModemModeCombination *expected_combinations, guint n_expected_combinations) { GArray *combinations; GError *error = NULL; guint i; combinations = mm_ublox_parse_urat_test_response (response, NULL, &error); g_assert_no_error (error); g_assert (combinations); combinations = mm_ublox_filter_supported_modes (model, combinations, NULL, &error); g_assert_no_error (error); g_assert (combinations); g_assert_cmpuint (combinations->len, ==, n_expected_combinations); for (i = 0; i < combinations->len; i++) { MMModemModeCombination combination; guint j; gboolean found = FALSE; combination = g_array_index (combinations, MMModemModeCombination, i); for (j = 0; !found && j < n_expected_combinations; j++) found = (combination.allowed == expected_combinations[j].allowed && combination.preferred == expected_combinations[j].preferred); g_assert (found); } g_array_unref (combinations); } static void test_urat_test_response_2g (void) { static const MMModemModeCombination expected_combinations[] = { { MM_MODEM_MODE_2G, MM_MODEM_MODE_NONE } }; compare_combinations ("+URAT: 0", NULL, expected_combinations, G_N_ELEMENTS (expected_combinations)); compare_combinations ("+URAT: 0,0", NULL, expected_combinations, G_N_ELEMENTS (expected_combinations)); compare_combinations ("+URAT: (0)", NULL, expected_combinations, G_N_ELEMENTS (expected_combinations)); compare_combinations ("+URAT: (0),(0)", NULL, expected_combinations, G_N_ELEMENTS (expected_combinations)); } static void test_urat_test_response_2g3g (void) { static const MMModemModeCombination expected_combinations[] = { { MM_MODEM_MODE_2G, MM_MODEM_MODE_NONE }, { MM_MODEM_MODE_3G, MM_MODEM_MODE_NONE }, { MM_MODEM_MODE_2G | MM_MODEM_MODE_3G, MM_MODEM_MODE_NONE }, { MM_MODEM_MODE_2G | MM_MODEM_MODE_3G, MM_MODEM_MODE_2G }, { MM_MODEM_MODE_2G | MM_MODEM_MODE_3G, MM_MODEM_MODE_3G }, }; compare_combinations ("+URAT: (0,1,2),(0,2)", NULL, expected_combinations, G_N_ELEMENTS (expected_combinations)); compare_combinations ("+URAT: (0-2),(0,2)", NULL, expected_combinations, G_N_ELEMENTS (expected_combinations)); } static void test_urat_test_response_2g3g4g (void) { static const MMModemModeCombination expected_combinations[] = { { MM_MODEM_MODE_2G, MM_MODEM_MODE_NONE }, { MM_MODEM_MODE_3G, MM_MODEM_MODE_NONE }, { MM_MODEM_MODE_4G, MM_MODEM_MODE_NONE }, { MM_MODEM_MODE_2G | MM_MODEM_MODE_3G, MM_MODEM_MODE_NONE }, { MM_MODEM_MODE_2G | MM_MODEM_MODE_3G, MM_MODEM_MODE_2G }, { MM_MODEM_MODE_2G | MM_MODEM_MODE_3G, MM_MODEM_MODE_3G }, { MM_MODEM_MODE_2G | MM_MODEM_MODE_4G, MM_MODEM_MODE_NONE }, { MM_MODEM_MODE_2G | MM_MODEM_MODE_4G, MM_MODEM_MODE_2G }, { MM_MODEM_MODE_2G | MM_MODEM_MODE_4G, MM_MODEM_MODE_4G }, { MM_MODEM_MODE_3G | MM_MODEM_MODE_4G, MM_MODEM_MODE_NONE }, { MM_MODEM_MODE_3G | MM_MODEM_MODE_4G, MM_MODEM_MODE_3G }, { MM_MODEM_MODE_3G | MM_MODEM_MODE_4G, MM_MODEM_MODE_4G }, { MM_MODEM_MODE_2G | MM_MODEM_MODE_3G | MM_MODEM_MODE_4G, MM_MODEM_MODE_NONE }, { MM_MODEM_MODE_2G | MM_MODEM_MODE_3G | MM_MODEM_MODE_4G, MM_MODEM_MODE_2G }, { MM_MODEM_MODE_2G | MM_MODEM_MODE_3G | MM_MODEM_MODE_4G, MM_MODEM_MODE_3G }, { MM_MODEM_MODE_2G | MM_MODEM_MODE_3G | MM_MODEM_MODE_4G, MM_MODEM_MODE_4G }, }; compare_combinations ("+URAT: (0,1,2,3,4,5,6),(0,2,3)", NULL, expected_combinations, G_N_ELEMENTS (expected_combinations)); compare_combinations ("+URAT: (0-6),(0,2,3)", NULL, expected_combinations, G_N_ELEMENTS (expected_combinations)); } static void test_mode_filtering_toby_l201 (void) { static const MMModemModeCombination expected_combinations[] = { { MM_MODEM_MODE_3G, MM_MODEM_MODE_NONE }, { MM_MODEM_MODE_4G, MM_MODEM_MODE_NONE }, { MM_MODEM_MODE_3G | MM_MODEM_MODE_4G, MM_MODEM_MODE_NONE }, { MM_MODEM_MODE_3G | MM_MODEM_MODE_4G, MM_MODEM_MODE_3G }, { MM_MODEM_MODE_3G | MM_MODEM_MODE_4G, MM_MODEM_MODE_4G }, }; compare_combinations ("+URAT: (0-6),(0,2,3)", "TOBY-L201", expected_combinations, G_N_ELEMENTS (expected_combinations)); } static void test_mode_filtering_lisa_u200 (void) { static const MMModemModeCombination expected_combinations[] = { { MM_MODEM_MODE_2G, MM_MODEM_MODE_NONE }, { MM_MODEM_MODE_3G, MM_MODEM_MODE_NONE }, { MM_MODEM_MODE_2G | MM_MODEM_MODE_3G, MM_MODEM_MODE_NONE }, { MM_MODEM_MODE_2G | MM_MODEM_MODE_3G, MM_MODEM_MODE_2G }, { MM_MODEM_MODE_2G | MM_MODEM_MODE_3G, MM_MODEM_MODE_3G }, }; compare_combinations ("+URAT: (0-6),(0,2,3)", "LISA-U200", expected_combinations, G_N_ELEMENTS (expected_combinations)); } static void test_mode_filtering_sara_u280 (void) { static const MMModemModeCombination expected_combinations[] = { { MM_MODEM_MODE_3G, MM_MODEM_MODE_NONE }, }; compare_combinations ("+URAT: (0-6),(0,2,3)", "SARA-U280", expected_combinations, G_N_ELEMENTS (expected_combinations)); } /*****************************************************************************/ /* URAT? response parser and URAT=X command builder */ typedef struct { const gchar *command; const gchar *response; MMModemMode allowed; MMModemMode preferred; } UratTest; static const UratTest urat_tests[] = { { .command = "+URAT=1,2", .response = "+URAT: 1,2\r\n", .allowed = (MM_MODEM_MODE_2G | MM_MODEM_MODE_3G), .preferred = MM_MODEM_MODE_3G, }, { .command = "+URAT=4,0", .response = "+URAT: 4,0\r\n", .allowed = (MM_MODEM_MODE_2G | MM_MODEM_MODE_3G | MM_MODEM_MODE_4G), .preferred = MM_MODEM_MODE_2G, }, { .command = "+URAT=0", .response = "+URAT: 0\r\n", .allowed = MM_MODEM_MODE_2G, .preferred = MM_MODEM_MODE_NONE, }, { .command = "+URAT=6", .response = "+URAT: 6\r\n", .allowed = (MM_MODEM_MODE_3G | MM_MODEM_MODE_4G), .preferred = MM_MODEM_MODE_NONE, }, }; static void test_urat_read_response (void) { guint i; for (i = 0; i < G_N_ELEMENTS (urat_tests); i++) { MMModemMode allowed = MM_MODEM_MODE_NONE; MMModemMode preferred = MM_MODEM_MODE_NONE; GError *error = NULL; gboolean success; success = mm_ublox_parse_urat_read_response (urat_tests[i].response, NULL, &allowed, &preferred, &error); g_assert_no_error (error); g_assert (success); g_assert_cmpuint (urat_tests[i].allowed, ==, allowed); g_assert_cmpuint (urat_tests[i].preferred, ==, preferred); } } static void test_urat_write_command (void) { guint i; for (i = 0; i < G_N_ELEMENTS (urat_tests); i++) { gchar *command; GError *error = NULL; command = mm_ublox_build_urat_set_command (urat_tests[i].allowed, urat_tests[i].preferred, &error); g_assert_no_error (error); g_assert_cmpstr (command, ==, urat_tests[i].command); g_free (command); } } /*****************************************************************************/ /* Test +UBANDSEL? response parser */ static void common_validate_ubandsel_response (const gchar *str, const MMModemBand *expected_bands, const gchar *model, guint n_expected_bands) { GError *error = NULL; GArray *bands; bands = mm_ublox_parse_ubandsel_response (str, model, NULL, &error); g_assert_no_error (error); g_assert (bands); mm_test_helpers_compare_bands (bands, expected_bands, n_expected_bands); g_array_unref (bands); } static void test_ubandsel_response_four (void) { const MMModemBand expected_bands[] = { /* 700 */ MM_MODEM_BAND_EUTRAN_4, /* 1700 */ MM_MODEM_BAND_EUTRAN_13 }; common_validate_ubandsel_response ("+UBANDSEL: 700,1700\r\n", expected_bands, "LARA-R204", G_N_ELEMENTS (expected_bands)); } static void test_ubandsel_response_three (void) { const MMModemBand expected_bands[] = { /* 800 */ MM_MODEM_BAND_UTRAN_6, /* 850 */ MM_MODEM_BAND_G850, MM_MODEM_BAND_UTRAN_5, /* 900 */ MM_MODEM_BAND_EGSM, MM_MODEM_BAND_UTRAN_8, /* 1900 */ MM_MODEM_BAND_PCS, MM_MODEM_BAND_UTRAN_2, /* 2100 */ MM_MODEM_BAND_UTRAN_1 }; common_validate_ubandsel_response ("+UBANDSEL: 800,850,900,1900,2100\r\n", expected_bands, "SARA-U201", G_N_ELEMENTS (expected_bands)); } static void test_ubandsel_response_two (void) { const MMModemBand expected_bands[] = { /* 850 */ MM_MODEM_BAND_G850, /* 900 */ MM_MODEM_BAND_EGSM, /* 1800 */ MM_MODEM_BAND_DCS, /* 1900 */ MM_MODEM_BAND_PCS }; common_validate_ubandsel_response ("+UBANDSEL: 850,900,1800,1900\r\n", expected_bands, "SARA-G310", G_N_ELEMENTS (expected_bands)); } static void test_ubandsel_response_one (void) { const MMModemBand expected_bands[] = { /* 850 */ MM_MODEM_BAND_G850, MM_MODEM_BAND_UTRAN_5, MM_MODEM_BAND_EUTRAN_5, /* 1700 */ MM_MODEM_BAND_EUTRAN_4, /* 1900 */ MM_MODEM_BAND_PCS, MM_MODEM_BAND_UTRAN_2, MM_MODEM_BAND_EUTRAN_2 }; common_validate_ubandsel_response ("+UBANDSEL: 850,1700,1900\r\n", expected_bands, "TOBY-R200", G_N_ELEMENTS (expected_bands)); } /*****************************************************************************/ /* +UBANDSEL=x command builder */ static void common_validate_ubandsel_request (const MMModemBand *bands, guint n_bands, const gchar *model, const gchar *expected_request) { GError *error = NULL; GArray *bands_array; gchar *request; bands_array = g_array_sized_new (FALSE, FALSE, sizeof (MMModemBand), n_bands); g_array_append_vals (bands_array, bands, n_bands); request = mm_ublox_build_ubandsel_set_command (bands_array, model, &error); g_assert_no_error (error); g_assert (request); g_assert_cmpstr (request, ==, expected_request); g_array_unref (bands_array); g_free (request); } static void test_ubandsel_request_any (void) { const MMModemBand bands[] = { MM_MODEM_BAND_ANY }; common_validate_ubandsel_request (bands, G_N_ELEMENTS (bands), "TOBY-R200", "+UBANDSEL=0"); } static void test_ubandsel_request_2g (void) { const MMModemBand bands[] = { MM_MODEM_BAND_G850, MM_MODEM_BAND_EGSM, MM_MODEM_BAND_DCS, MM_MODEM_BAND_PCS }; common_validate_ubandsel_request (bands, G_N_ELEMENTS (bands), "SARA-G310", "+UBANDSEL=850,900,1800,1900"); } static void test_ubandsel_request_1800 (void) { const MMModemBand bands[] = { MM_MODEM_BAND_DCS, MM_MODEM_BAND_UTRAN_3, MM_MODEM_BAND_EUTRAN_3 }; common_validate_ubandsel_request (bands, G_N_ELEMENTS (bands), "TOBY-R200", "+UBANDSEL=1800"); } /*****************************************************************************/ /* Test +UACT? response parser */ static void common_validate_uact_response (const gchar *str, const MMModemBand *expected_bands, guint n_expected_bands) { GError *error = NULL; GArray *bands; bands = mm_ublox_parse_uact_response (str, &error); if (n_expected_bands > 0) { g_assert (bands); g_assert_no_error (error); mm_test_helpers_compare_bands (bands, expected_bands, n_expected_bands); g_array_unref (bands); } else { g_assert (!bands); g_assert (error); g_error_free (error); } } static void test_uact_response_empty_list (void) { common_validate_uact_response ("", NULL, 0); common_validate_uact_response ("+UACT: ,,,\r\n", NULL, 0); } static void test_uact_response_2g (void) { const MMModemBand expected_bands[] = { MM_MODEM_BAND_G850, MM_MODEM_BAND_EGSM, MM_MODEM_BAND_DCS, MM_MODEM_BAND_PCS, }; common_validate_uact_response ("+UACT: ,,,900,1800,1900,850\r\n", expected_bands, G_N_ELEMENTS (expected_bands)); } static void test_uact_response_2g3g (void) { const MMModemBand expected_bands[] = { MM_MODEM_BAND_G850, MM_MODEM_BAND_EGSM, MM_MODEM_BAND_DCS, MM_MODEM_BAND_PCS, MM_MODEM_BAND_UTRAN_1, MM_MODEM_BAND_UTRAN_2, MM_MODEM_BAND_UTRAN_3, MM_MODEM_BAND_UTRAN_4, MM_MODEM_BAND_UTRAN_5, MM_MODEM_BAND_UTRAN_6, MM_MODEM_BAND_UTRAN_7, MM_MODEM_BAND_UTRAN_8, MM_MODEM_BAND_UTRAN_9, }; common_validate_uact_response ("+UACT: ,,,900,1800,1900,850,1,2,3,4,5,6,7,8,9\r\n", expected_bands, G_N_ELEMENTS (expected_bands)); } static void test_uact_response_2g3g4g (void) { const MMModemBand expected_bands[] = { MM_MODEM_BAND_G850, MM_MODEM_BAND_EGSM, MM_MODEM_BAND_DCS, MM_MODEM_BAND_PCS, MM_MODEM_BAND_UTRAN_1, MM_MODEM_BAND_UTRAN_2, MM_MODEM_BAND_UTRAN_3, MM_MODEM_BAND_UTRAN_4, MM_MODEM_BAND_UTRAN_5, MM_MODEM_BAND_UTRAN_6, MM_MODEM_BAND_UTRAN_7, MM_MODEM_BAND_UTRAN_8, MM_MODEM_BAND_UTRAN_9, MM_MODEM_BAND_EUTRAN_1, MM_MODEM_BAND_EUTRAN_2, MM_MODEM_BAND_EUTRAN_3, MM_MODEM_BAND_EUTRAN_4, MM_MODEM_BAND_EUTRAN_5, MM_MODEM_BAND_EUTRAN_6, MM_MODEM_BAND_EUTRAN_7, MM_MODEM_BAND_EUTRAN_8, MM_MODEM_BAND_EUTRAN_9, }; common_validate_uact_response ("+UACT: ,,,900,1800,1900,850,1,2,3,4,5,6,7,8,9,101,102,103,104,105,106,107,108,109\r\n", expected_bands, G_N_ELEMENTS (expected_bands)); } /*****************************************************************************/ /* Test +UACT=? test parser */ static void common_validate_uact_test (const gchar *str, const MMModemBand *expected_bands_2g, guint n_expected_bands_2g, const MMModemBand *expected_bands_3g, guint n_expected_bands_3g, const MMModemBand *expected_bands_4g, guint n_expected_bands_4g) { GError *error = NULL; gboolean result; GArray *bands_2g = NULL; GArray *bands_3g = NULL; GArray *bands_4g = NULL; result = mm_ublox_parse_uact_test (str, NULL, &bands_2g, &bands_3g, &bands_4g, &error); g_assert_no_error (error); g_assert (result); mm_test_helpers_compare_bands (bands_2g, expected_bands_2g, n_expected_bands_2g); if (bands_2g) g_array_unref (bands_2g); mm_test_helpers_compare_bands (bands_3g, expected_bands_3g, n_expected_bands_3g); if (bands_3g) g_array_unref (bands_3g); mm_test_helpers_compare_bands (bands_4g, expected_bands_4g, n_expected_bands_4g); if (bands_4g) g_array_unref (bands_4g); } static void test_uact_test_2g (void) { const MMModemBand expected_bands_2g[] = { MM_MODEM_BAND_EGSM, MM_MODEM_BAND_DCS }; common_validate_uact_test ("+UACT: ,,,(900,1800)\r\n", expected_bands_2g, G_N_ELEMENTS (expected_bands_2g), NULL, 0, NULL, 0); } static void test_uact_test_2g3g (void) { const MMModemBand expected_bands_2g[] = { MM_MODEM_BAND_EGSM, MM_MODEM_BAND_DCS }; const MMModemBand expected_bands_3g[] = { MM_MODEM_BAND_UTRAN_1, MM_MODEM_BAND_UTRAN_8 }; common_validate_uact_test ("+UACT: ,,,(900,1800),(1,8)\r\n", expected_bands_2g, G_N_ELEMENTS (expected_bands_2g), expected_bands_3g, G_N_ELEMENTS (expected_bands_3g), NULL, 0); } static void test_uact_test_2g3g4g (void) { const MMModemBand expected_bands_2g[] = { MM_MODEM_BAND_EGSM, MM_MODEM_BAND_DCS }; const MMModemBand expected_bands_3g[] = { MM_MODEM_BAND_UTRAN_1, MM_MODEM_BAND_UTRAN_8 }; const MMModemBand expected_bands_4g[] = { MM_MODEM_BAND_EUTRAN_1, MM_MODEM_BAND_EUTRAN_3, MM_MODEM_BAND_EUTRAN_7, MM_MODEM_BAND_EUTRAN_8, MM_MODEM_BAND_EUTRAN_20 }; common_validate_uact_test ("+UACT: ,,,(900,1800),(1,8),(101,103,107,108,120)\r\n", expected_bands_2g, G_N_ELEMENTS (expected_bands_2g), expected_bands_3g, G_N_ELEMENTS (expected_bands_3g), expected_bands_4g, G_N_ELEMENTS (expected_bands_4g)); } static void test_uact_test_2g3g4g_2 (void) { const MMModemBand expected_bands_2g[] = { MM_MODEM_BAND_EGSM, MM_MODEM_BAND_DCS }; const MMModemBand expected_bands_3g[] = { MM_MODEM_BAND_UTRAN_1, MM_MODEM_BAND_UTRAN_8 }; const MMModemBand expected_bands_4g[] = { MM_MODEM_BAND_EUTRAN_1, MM_MODEM_BAND_EUTRAN_3, MM_MODEM_BAND_EUTRAN_7, MM_MODEM_BAND_EUTRAN_8, MM_MODEM_BAND_EUTRAN_20 }; common_validate_uact_test ("+UACT: ,,,(900,1800),(1,8),(101,103,107,108,120),(138)\r\n", expected_bands_2g, G_N_ELEMENTS (expected_bands_2g), expected_bands_3g, G_N_ELEMENTS (expected_bands_3g), expected_bands_4g, G_N_ELEMENTS (expected_bands_4g)); } /*****************************************************************************/ /* +UACT=x command builder */ static void common_validate_uact_request (const MMModemBand *bands, guint n_bands, const gchar *expected_request) { GError *error = NULL; GArray *bands_array; gchar *request; bands_array = g_array_sized_new (FALSE, FALSE, sizeof (MMModemBand), n_bands); g_array_append_vals (bands_array, bands, n_bands); request = mm_ublox_build_uact_set_command (bands_array, &error); g_assert_no_error (error); g_assert (request); g_assert_cmpstr (request, ==, expected_request); g_array_unref (bands_array); g_free (request); } static void test_uact_request_any (void) { const MMModemBand bands[] = { MM_MODEM_BAND_ANY }; common_validate_uact_request (bands, G_N_ELEMENTS (bands), "+UACT=,,,0"); } static void test_uact_request_2g (void) { const MMModemBand bands[] = { MM_MODEM_BAND_EGSM, MM_MODEM_BAND_DCS, }; common_validate_uact_request (bands, G_N_ELEMENTS (bands), "+UACT=,,,900,1800"); } static void test_uact_request_3g (void) { const MMModemBand bands[] = { MM_MODEM_BAND_UTRAN_1, MM_MODEM_BAND_UTRAN_8, }; common_validate_uact_request (bands, G_N_ELEMENTS (bands), "+UACT=,,,1,8"); } static void test_uact_request_4g (void) { const MMModemBand bands[] = { MM_MODEM_BAND_EUTRAN_1, MM_MODEM_BAND_EUTRAN_3, MM_MODEM_BAND_EUTRAN_7, MM_MODEM_BAND_EUTRAN_8, MM_MODEM_BAND_EUTRAN_20 }; common_validate_uact_request (bands, G_N_ELEMENTS (bands), "+UACT=,,,101,103,107,108,120"); } /*****************************************************************************/ /* Test +UAUTHREQ=? responses */ static void common_validate_uauthreq_test (const gchar *str, MMUbloxBearerAllowedAuth expected_allowed_auths) { GError *error = NULL; MMUbloxBearerAllowedAuth allowed_auths; allowed_auths = mm_ublox_parse_uauthreq_test (str, NULL, &error); g_assert_no_error (error); g_assert_cmpuint (allowed_auths, ==, expected_allowed_auths); } static void test_uauthreq_tobyl4 (void) { common_validate_uauthreq_test ("+UAUTHREQ: (1-4),(0-2),,", (MM_UBLOX_BEARER_ALLOWED_AUTH_NONE | MM_UBLOX_BEARER_ALLOWED_AUTH_PAP | MM_UBLOX_BEARER_ALLOWED_AUTH_CHAP)); } static void test_uauthreq_with_auto (void) { common_validate_uauthreq_test ("+UAUTHREQ: (1-4),(0-3),,", (MM_UBLOX_BEARER_ALLOWED_AUTH_NONE | MM_UBLOX_BEARER_ALLOWED_AUTH_PAP | MM_UBLOX_BEARER_ALLOWED_AUTH_CHAP | MM_UBLOX_BEARER_ALLOWED_AUTH_AUTO)); } static void test_uauthreq_less_fields (void) { common_validate_uauthreq_test ("+UAUTHREQ: (1-4),(0-2)", (MM_UBLOX_BEARER_ALLOWED_AUTH_NONE | MM_UBLOX_BEARER_ALLOWED_AUTH_PAP | MM_UBLOX_BEARER_ALLOWED_AUTH_CHAP)); } /*****************************************************************************/ /* Test +UGCNTRD responses */ typedef struct { const gchar *str; guint cid; guint64 session_tx_bytes; guint64 session_rx_bytes; guint64 total_tx_bytes; guint64 total_rx_bytes; } UgcntrdResponseTest; static const UgcntrdResponseTest ugcntrd_response_tests[] = { { .str = "+UGCNTRD: 1, 100, 0, 100, 0", .cid = 1, .session_tx_bytes = 100, .session_rx_bytes = 0, .total_tx_bytes = 100, .total_rx_bytes = 0 }, { .str = "+UGCNTRD: 31,2704,1819,2724,1839", .cid = 31, .session_tx_bytes = 2704, .session_rx_bytes = 1819, .total_tx_bytes = 2724, .total_rx_bytes = 1839 }, { .str = "+UGCNTRD: 1, 100, 0, 100, 0\r\n" "+UGCNTRD: 31,2704,1819,2724,1839\r\n", .cid = 1, .session_tx_bytes = 100, .session_rx_bytes = 0, .total_tx_bytes = 100, .total_rx_bytes = 0 }, { .str = "+UGCNTRD: 1, 100, 0, 100, 0\r\n" "+UGCNTRD: 31,2704,1819,2724,1839\r\n", .cid = 31, .session_tx_bytes = 2704, .session_rx_bytes = 1819, .total_tx_bytes = 2724, .total_rx_bytes = 1839 }, { .str = "+UGCNTRD: 2,1397316870,113728263578,1397316870,113728263578\r\n", .cid = 2, .session_tx_bytes = 1397316870ULL, .session_rx_bytes = 113728263578ULL, .total_tx_bytes = 1397316870ULL, .total_rx_bytes = 113728263578ULL } }; static void test_ugcntrd_response (void) { guint i; for (i = 0; i < G_N_ELEMENTS (ugcntrd_response_tests); i++) { GError *error = NULL; gboolean success; guint64 session_tx_bytes = 0; guint64 session_rx_bytes = 0; guint64 total_tx_bytes = 0; guint64 total_rx_bytes = 0; success = mm_ublox_parse_ugcntrd_response_for_cid (ugcntrd_response_tests[i].str, ugcntrd_response_tests[i].cid, &session_tx_bytes, &session_rx_bytes, &total_tx_bytes, &total_rx_bytes, &error); g_assert_no_error (error); g_assert (success); g_assert_cmpuint (ugcntrd_response_tests[i].session_tx_bytes, ==, session_tx_bytes); g_assert_cmpuint (ugcntrd_response_tests[i].session_rx_bytes, ==, session_rx_bytes); g_assert_cmpuint (ugcntrd_response_tests[i].total_tx_bytes, ==, total_tx_bytes); g_assert_cmpuint (ugcntrd_response_tests[i].total_rx_bytes, ==, total_rx_bytes); } } /*****************************************************************************/ int main (int argc, char **argv) { setlocale (LC_ALL, ""); g_test_init (&argc, &argv, NULL); g_test_add_func ("/MM/ublox/upincnt/response", test_upincnt_response); g_test_add_func ("/MM/ublox/uusbconf/response", test_uusbconf_response); g_test_add_func ("/MM/ublox/ubmconf/response", test_ubmconf_response); g_test_add_func ("/MM/ublox/uipaddr/response", test_uipaddr_response); g_test_add_func ("/MM/ublox/cfun/response", test_cfun_response); g_test_add_func ("/MM/ublox/urat/test/response/2g", test_urat_test_response_2g); g_test_add_func ("/MM/ublox/urat/test/response/2g3g", test_urat_test_response_2g3g); g_test_add_func ("/MM/ublox/urat/test/response/2g3g4g", test_urat_test_response_2g3g4g); g_test_add_func ("/MM/ublox/urat/test/response/toby-l201", test_mode_filtering_toby_l201); g_test_add_func ("/MM/ublox/urat/test/response/lisa-u200", test_mode_filtering_lisa_u200); g_test_add_func ("/MM/ublox/urat/test/response/sara-u280", test_mode_filtering_sara_u280); g_test_add_func ("/MM/ublox/urat/read/response", test_urat_read_response); g_test_add_func ("/MM/ublox/urat/write/command", test_urat_write_command); g_test_add_func ("/MM/ublox/ubandsel/response/one", test_ubandsel_response_one); g_test_add_func ("/MM/ublox/ubandsel/response/two", test_ubandsel_response_two); g_test_add_func ("/MM/ublox/ubandsel/response/three", test_ubandsel_response_three); g_test_add_func ("/MM/ublox/ubandsel/response/four", test_ubandsel_response_four); g_test_add_func ("/MM/ublox/ubandsel/request/any", test_ubandsel_request_any); g_test_add_func ("/MM/ublox/ubandsel/request/2g", test_ubandsel_request_2g); g_test_add_func ("/MM/ublox/ubandsel/request/1800", test_ubandsel_request_1800); g_test_add_func ("/MM/ublox/uact/response/empty-list", test_uact_response_empty_list); g_test_add_func ("/MM/ublox/uact/response/2g", test_uact_response_2g); g_test_add_func ("/MM/ublox/uact/response/2g3g", test_uact_response_2g3g); g_test_add_func ("/MM/ublox/uact/response/2g3g4g", test_uact_response_2g3g4g); g_test_add_func ("/MM/ublox/uact/test/2g", test_uact_test_2g); g_test_add_func ("/MM/ublox/uact/test/2g3g", test_uact_test_2g3g); g_test_add_func ("/MM/ublox/uact/test/2g3g4g", test_uact_test_2g3g4g); g_test_add_func ("/MM/ublox/uact/test/2g3g4g/2", test_uact_test_2g3g4g_2); g_test_add_func ("/MM/ublox/uact/request/any", test_uact_request_any); g_test_add_func ("/MM/ublox/uact/request/2g", test_uact_request_2g); g_test_add_func ("/MM/ublox/uact/request/3g", test_uact_request_3g); g_test_add_func ("/MM/ublox/uact/request/4g", test_uact_request_4g); g_test_add_func ("/MM/ublox/uauthreq/test/tobyl4", test_uauthreq_tobyl4); g_test_add_func ("/MM/ublox/uauthreq/test/with-auto", test_uauthreq_with_auto); g_test_add_func ("/MM/ublox/uauthreq/test/less-fields", test_uauthreq_less_fields); g_test_add_func ("/MM/ublox/ugcntrd/response", test_ugcntrd_response); return g_test_run (); } ModemManager-1.23.4-dev/src/plugins/via/000077500000000000000000000000001456466623000177545ustar00rootroot00000000000000ModemManager-1.23.4-dev/src/plugins/via/mm-broadband-modem-via.c000066400000000000000000000513311456466623000243220ustar00rootroot00000000000000/* -*- 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) 2012 Red Hat, Inc. */ #include #include #include #include #include #include #define _LIBMM_INSIDE_MM #include #include "mm-log-object.h" #include "mm-modem-helpers.h" #include "mm-errors-types.h" #include "mm-base-modem-at.h" #include "mm-broadband-modem-via.h" #include "mm-iface-modem-cdma.h" #include "mm-iface-modem.h" static void iface_modem_cdma_init (MMIfaceModemCdma *iface); static MMIfaceModemCdma *iface_modem_cdma_parent; G_DEFINE_TYPE_EXTENDED (MMBroadbandModemVia, mm_broadband_modem_via, MM_TYPE_BROADBAND_MODEM, 0, G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_CDMA, iface_modem_cdma_init)) struct _MMBroadbandModemViaPrivate { /* Regex for signal quality related notifications */ GRegex *hrssilvl_regex; /* EVDO signal strength */ /* Regex for other notifications to ignore */ GRegex *mode_regex; /* Access technology change */ GRegex *dosession_regex; /* EVDO data dormancy */ GRegex *simst_regex; GRegex *vpon_regex; GRegex *creg_regex; GRegex *vrom_regex; /* Roaming indicator (reportedly unreliable) */ GRegex *vser_regex; GRegex *ciev_regex; GRegex *vpup_regex; }; /*****************************************************************************/ /* Setup registration checks (CDMA interface) */ typedef struct { gboolean skip_qcdm_call_manager_step; gboolean skip_qcdm_hdr_step; gboolean skip_at_cdma_service_status_step; gboolean skip_at_cdma1x_serving_system_step; gboolean skip_detailed_registration_state; } SetupRegistrationChecksResults; static gboolean setup_registration_checks_finish (MMIfaceModemCdma *self, GAsyncResult *res, gboolean *skip_qcdm_call_manager_step, gboolean *skip_qcdm_hdr_step, gboolean *skip_at_cdma_service_status_step, gboolean *skip_at_cdma1x_serving_system_step, gboolean *skip_detailed_registration_state, GError **error) { SetupRegistrationChecksResults *results; results = g_task_propagate_pointer (G_TASK (res), error); if (!results) return FALSE; *skip_qcdm_call_manager_step = results->skip_qcdm_call_manager_step; *skip_qcdm_hdr_step = results->skip_qcdm_hdr_step; *skip_at_cdma_service_status_step = results->skip_at_cdma_service_status_step; *skip_at_cdma1x_serving_system_step = results->skip_at_cdma1x_serving_system_step; *skip_detailed_registration_state = results->skip_detailed_registration_state; g_free (results); return TRUE; } static void parent_setup_registration_checks_ready (MMIfaceModemCdma *self, GAsyncResult *res, GTask *task) { GError *error = NULL; SetupRegistrationChecksResults *results; results = g_new0 (SetupRegistrationChecksResults, 1); if (!iface_modem_cdma_parent->setup_registration_checks_finish (self, res, &results->skip_qcdm_call_manager_step, &results->skip_qcdm_hdr_step, &results->skip_at_cdma_service_status_step, &results->skip_at_cdma1x_serving_system_step, &results->skip_detailed_registration_state, &error)) { g_free (results); g_task_return_error (task, error); } else { /* Skip +CSS */ results->skip_at_cdma1x_serving_system_step = TRUE; /* Skip +CAD */ results->skip_at_cdma_service_status_step = TRUE; /* Force to always use the detailed registration checks, as we have * ^SYSINFO for that */ results->skip_detailed_registration_state = FALSE; g_task_return_pointer (task, results, g_free); } g_object_unref (task); } static void setup_registration_checks (MMIfaceModemCdma *self, GAsyncReadyCallback callback, gpointer user_data) { /* Run parent's checks first */ iface_modem_cdma_parent->setup_registration_checks ( self, (GAsyncReadyCallback)parent_setup_registration_checks_ready, g_task_new (self, NULL, callback, user_data)); } /*****************************************************************************/ /* Detailed registration state (CDMA interface) */ typedef struct { MMModemCdmaRegistrationState detailed_cdma1x_state; MMModemCdmaRegistrationState detailed_evdo_state; } DetailedRegistrationStateResults; static gboolean get_detailed_registration_state_finish (MMIfaceModemCdma *self, GAsyncResult *res, MMModemCdmaRegistrationState *detailed_cdma1x_state, MMModemCdmaRegistrationState *detailed_evdo_state, GError **error) { g_autofree DetailedRegistrationStateResults *results = NULL; results = g_task_propagate_pointer (G_TASK (res), error); if (!results) return FALSE; *detailed_cdma1x_state = results->detailed_cdma1x_state; *detailed_evdo_state = results->detailed_evdo_state; return TRUE; } static void sysinfo_ready (MMBaseModem *self, GAsyncResult *res, GTask *task) { DetailedRegistrationStateResults *ctx; g_autofree DetailedRegistrationStateResults *results = NULL; const gchar *response; g_autoptr(GRegex) r = NULL; g_autoptr(GMatchInfo) match_info = NULL; MMModemCdmaRegistrationState reg_state; guint val = 0; ctx = g_task_get_task_data (task); /* Set input detailed states as fallback */ results = g_memdup (ctx, sizeof (*ctx)); /* If error, leave superclass' reg state alone if AT^SYSINFO isn't supported. */ response = mm_base_modem_at_command_finish (self, res, NULL); if (!response) goto out; response = mm_strip_tag (response, "^SYSINFO:"); /* Format is ",,,," */ 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); g_assert (r != NULL); /* Try to parse the results */ g_regex_match (r, response, 0, &match_info); if (g_match_info_get_match_count (match_info) < 6) { mm_obj_warn (self, "failed to parse ^SYSINFO response: '%s'", response); goto out; } /* At this point the generic code already knows we've been registered */ reg_state = MM_MODEM_CDMA_REGISTRATION_STATE_REGISTERED; if (mm_get_uint_from_match_info (match_info, 1, &val)) { if (val == 2) { /* Service available, check roaming state */ val = 0; if (mm_get_uint_from_match_info (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 (mm_get_uint_from_match_info (match_info, 4, &val)) { if (val == 2) /* CDMA */ results->detailed_cdma1x_state = reg_state; else if (val == 4) /* HDR */ results->detailed_evdo_state = reg_state; else if (val == 8) { /* Hybrid */ results->detailed_cdma1x_state = reg_state; results->detailed_evdo_state = reg_state; } } else { /* Say we're registered to something even though sysmode parsing failed */ mm_obj_dbg (self, "SYSMODE parsing failed: assuming registered at least in CDMA1x"); results->detailed_cdma1x_state = reg_state; } out: g_task_return_pointer (task, g_steal_pointer (&results), g_free); g_object_unref (task); } static void get_detailed_registration_state (MMIfaceModemCdma *self, MMModemCdmaRegistrationState cdma1x_state, MMModemCdmaRegistrationState evdo_state, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; DetailedRegistrationStateResults *ctx; /* Setup context */ ctx = g_new0 (DetailedRegistrationStateResults, 1); ctx->detailed_cdma1x_state = cdma1x_state; ctx->detailed_evdo_state = evdo_state; task = g_task_new (self, NULL, callback, user_data); g_task_set_task_data (task, ctx, g_free); mm_base_modem_at_command (MM_BASE_MODEM (self), "^SYSINFO", 3, FALSE, (GAsyncReadyCallback)sysinfo_ready, task); } /*****************************************************************************/ /* Setup/Cleanup unsolicited events (CDMA interface) */ static void handle_evdo_quality_change (MMPortSerialAt *port, GMatchInfo *match_info, MMBroadbandModemVia *self) { guint quality = 0; if (mm_get_uint_from_match_info (match_info, 1, &quality)) { quality = MM_CLAMP_HIGH (quality, 100); mm_obj_dbg (self, "EVDO signal quality: %u", quality); mm_iface_modem_update_signal_quality (MM_IFACE_MODEM (self), quality); } } static void set_unsolicited_events_handlers (MMBroadbandModemVia *self, gboolean enable) { MMPortSerialAt *ports[2]; guint i; ports[0] = mm_base_modem_peek_port_primary (MM_BASE_MODEM (self)); ports[1] = mm_base_modem_peek_port_secondary (MM_BASE_MODEM (self)); /* Enable unsolicited events in given port */ for (i = 0; i < G_N_ELEMENTS (ports); i++) { if (!ports[i]) continue; /* Signal quality related */ mm_port_serial_at_add_unsolicited_msg_handler ( ports[i], self->priv->hrssilvl_regex, enable ? (MMPortSerialAtUnsolicitedMsgFn)handle_evdo_quality_change : NULL, enable ? self : NULL, NULL); } } static gboolean modem_cdma_setup_cleanup_unsolicited_events_finish (MMIfaceModemCdma *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void parent_cdma_setup_unsolicited_events_ready (MMIfaceModemCdma *self, GAsyncResult *res, GTask *task) { GError *error = NULL; if (!iface_modem_cdma_parent->setup_unsolicited_events_finish (self, res, &error)) g_task_return_error (task, error); else { /* Our own setup now */ set_unsolicited_events_handlers (MM_BROADBAND_MODEM_VIA (self), TRUE); g_task_return_boolean (task, TRUE); } g_object_unref (task); } static void modem_cdma_setup_unsolicited_events (MMIfaceModemCdma *self, GAsyncReadyCallback callback, gpointer user_data) { /* Chain up parent's setup */ iface_modem_cdma_parent->setup_unsolicited_events ( self, (GAsyncReadyCallback)parent_cdma_setup_unsolicited_events_ready, g_task_new (self, NULL, callback, user_data)); } static void parent_cdma_cleanup_unsolicited_events_ready (MMIfaceModemCdma *self, GAsyncResult *res, GTask *task) { GError *error = NULL; if (!iface_modem_cdma_parent->cleanup_unsolicited_events_finish (self, res, &error)) g_task_return_error (task, error); else g_task_return_boolean (task, TRUE); g_object_unref (task); } static void modem_cdma_cleanup_unsolicited_events (MMIfaceModemCdma *self, GAsyncReadyCallback callback, gpointer user_data) { /* Our own cleanup first */ set_unsolicited_events_handlers (MM_BROADBAND_MODEM_VIA (self), FALSE); /* And now chain up parent's cleanup */ iface_modem_cdma_parent->cleanup_unsolicited_events ( self, (GAsyncReadyCallback)parent_cdma_cleanup_unsolicited_events_ready, g_task_new (self, NULL, callback, user_data)); } /*****************************************************************************/ /* Setup ports (Broadband modem class) */ static void set_ignored_unsolicited_events_handlers (MMBroadbandModemVia *self) { MMPortSerialAt *ports[2]; guint i; ports[0] = mm_base_modem_peek_port_primary (MM_BASE_MODEM (self)); ports[1] = mm_base_modem_peek_port_secondary (MM_BASE_MODEM (self)); /* Enable unsolicited events in given port */ for (i = 0; i < G_N_ELEMENTS (ports); i++) { if (!ports[i]) continue; mm_port_serial_at_add_unsolicited_msg_handler ( ports[i], self->priv->mode_regex, NULL, NULL, NULL); mm_port_serial_at_add_unsolicited_msg_handler ( ports[i], self->priv->dosession_regex, NULL, NULL, NULL); mm_port_serial_at_add_unsolicited_msg_handler ( ports[i], self->priv->simst_regex, NULL, NULL, NULL); mm_port_serial_at_add_unsolicited_msg_handler ( ports[i], self->priv->vpon_regex, NULL, NULL, NULL); mm_port_serial_at_add_unsolicited_msg_handler ( ports[i], self->priv->creg_regex, NULL, NULL, NULL); mm_port_serial_at_add_unsolicited_msg_handler ( ports[i], self->priv->vrom_regex, NULL, NULL, NULL); mm_port_serial_at_add_unsolicited_msg_handler ( ports[i], self->priv->vser_regex, NULL, NULL, NULL); mm_port_serial_at_add_unsolicited_msg_handler ( ports[i], self->priv->ciev_regex, NULL, NULL, NULL); mm_port_serial_at_add_unsolicited_msg_handler ( ports[i], self->priv->vpup_regex, NULL, NULL, NULL); } } static void setup_ports (MMBroadbandModem *self) { /* Call parent's setup ports first always */ MM_BROADBAND_MODEM_CLASS (mm_broadband_modem_via_parent_class)->setup_ports (self); /* Unsolicited messages to always ignore */ set_ignored_unsolicited_events_handlers (MM_BROADBAND_MODEM_VIA (self)); /* Now reset the unsolicited messages we'll handle when enabled */ set_unsolicited_events_handlers (MM_BROADBAND_MODEM_VIA (self), FALSE); } /*****************************************************************************/ MMBroadbandModemVia * mm_broadband_modem_via_new (const gchar *device, const gchar *physdev, const gchar **drivers, const gchar *plugin, guint16 vendor_id, guint16 product_id) { return g_object_new (MM_TYPE_BROADBAND_MODEM_VIA, MM_BASE_MODEM_DEVICE, device, MM_BASE_MODEM_PHYSDEV, physdev, MM_BASE_MODEM_DRIVERS, drivers, MM_BASE_MODEM_PLUGIN, plugin, MM_BASE_MODEM_VENDOR_ID, vendor_id, MM_BASE_MODEM_PRODUCT_ID, product_id, /* Generic bearer supports TTY only */ MM_BASE_MODEM_DATA_NET_SUPPORTED, FALSE, MM_BASE_MODEM_DATA_TTY_SUPPORTED, TRUE, NULL); } static void mm_broadband_modem_via_init (MMBroadbandModemVia *self) { /* Initialize private data */ self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, MM_TYPE_BROADBAND_MODEM_VIA, MMBroadbandModemViaPrivate); /* Prepare regular expressions to setup */ self->priv->hrssilvl_regex = g_regex_new ("\\r\\n\\^HRSSILVL:(.*)\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); self->priv->mode_regex = g_regex_new ("\\r\\n\\^MODE:(.*)\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); self->priv->dosession_regex = g_regex_new ("\\r\\n\\+DOSESSION:(.*)\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); self->priv->simst_regex = g_regex_new ("\\r\\n\\^SIMST:(.*)\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); self->priv->simst_regex = g_regex_new ("\\r\\n\\+VPON:(.*)\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); self->priv->creg_regex = g_regex_new ("\\r\\n\\+CREG:(.*)\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); self->priv->vrom_regex = g_regex_new ("\\r\\n\\+VROM:(.*)\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); self->priv->vser_regex = g_regex_new ("\\r\\n\\+VSER:(.*)\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); self->priv->ciev_regex = g_regex_new ("\\r\\n\\+CIEV:(.*)\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); self->priv->vpup_regex = g_regex_new ("\\r\\n\\+VPUP:(.*)\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); } static void finalize (GObject *object) { MMBroadbandModemVia *self = MM_BROADBAND_MODEM_VIA (object); g_regex_unref (self->priv->hrssilvl_regex); g_regex_unref (self->priv->mode_regex); g_regex_unref (self->priv->dosession_regex); g_regex_unref (self->priv->simst_regex); g_regex_unref (self->priv->simst_regex); g_regex_unref (self->priv->creg_regex); g_regex_unref (self->priv->vrom_regex); g_regex_unref (self->priv->vser_regex); g_regex_unref (self->priv->ciev_regex); g_regex_unref (self->priv->vpup_regex); G_OBJECT_CLASS (mm_broadband_modem_via_parent_class)->finalize (object); } static void iface_modem_cdma_init (MMIfaceModemCdma *iface) { iface_modem_cdma_parent = g_type_interface_peek_parent (iface); iface->setup_unsolicited_events = modem_cdma_setup_unsolicited_events; iface->setup_unsolicited_events_finish = modem_cdma_setup_cleanup_unsolicited_events_finish; iface->cleanup_unsolicited_events = modem_cdma_cleanup_unsolicited_events; iface->cleanup_unsolicited_events_finish = modem_cdma_setup_cleanup_unsolicited_events_finish; iface->setup_registration_checks = setup_registration_checks; iface->setup_registration_checks_finish = setup_registration_checks_finish; iface->get_detailed_registration_state = get_detailed_registration_state; iface->get_detailed_registration_state_finish = get_detailed_registration_state_finish; } static void mm_broadband_modem_via_class_init (MMBroadbandModemViaClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); MMBroadbandModemClass *broadband_modem_class = MM_BROADBAND_MODEM_CLASS (klass); g_type_class_add_private (object_class, sizeof (MMBroadbandModemViaPrivate)); object_class->finalize = finalize; broadband_modem_class->setup_ports = setup_ports; } ModemManager-1.23.4-dev/src/plugins/via/mm-broadband-modem-via.h000066400000000000000000000044251456466623000243310ustar00rootroot00000000000000/* -*- 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) 2012 Red Hat, Inc. */ #ifndef MM_BROADBAND_MODEM_VIA_H #define MM_BROADBAND_MODEM_VIA_H #include "mm-broadband-modem.h" #define MM_TYPE_BROADBAND_MODEM_VIA (mm_broadband_modem_via_get_type ()) #define MM_BROADBAND_MODEM_VIA(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_BROADBAND_MODEM_VIA, MMBroadbandModemVia)) #define MM_BROADBAND_MODEM_VIA_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_BROADBAND_MODEM_VIA, MMBroadbandModemViaClass)) #define MM_IS_BROADBAND_MODEM_VIA(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_BROADBAND_MODEM_VIA)) #define MM_IS_BROADBAND_MODEM_VIA_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_BROADBAND_MODEM_VIA)) #define MM_BROADBAND_MODEM_VIA_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_BROADBAND_MODEM_VIA, MMBroadbandModemViaClass)) typedef struct _MMBroadbandModemVia MMBroadbandModemVia; typedef struct _MMBroadbandModemViaClass MMBroadbandModemViaClass; typedef struct _MMBroadbandModemViaPrivate MMBroadbandModemViaPrivate; struct _MMBroadbandModemVia { MMBroadbandModem parent; MMBroadbandModemViaPrivate *priv; }; struct _MMBroadbandModemViaClass{ MMBroadbandModemClass parent; }; GType mm_broadband_modem_via_get_type (void); MMBroadbandModemVia *mm_broadband_modem_via_new (const gchar *device, const gchar *physdev, const gchar **drivers, const gchar *plugin, guint16 vendor_id, guint16 product_id); #endif /* MM_BROADBAND_MODEM_VIA_H */ ModemManager-1.23.4-dev/src/plugins/via/mm-plugin-via.c000066400000000000000000000057131456466623000226100ustar00rootroot00000000000000/* -*- 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. * * 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. * * Copyright (C) 2012 Red Hat, Inc. */ #include #include #define _LIBMM_INSIDE_MM #include #include "mm-broadband-modem-via.h" #include "mm-plugin-common.h" #define MM_TYPE_PLUGIN_VIA mm_plugin_via_get_type () MM_DEFINE_PLUGIN (VIA, via, Via) /*****************************************************************************/ static MMBaseModem * create_modem (MMPlugin *self, const gchar *uid, const gchar *physdev, const gchar **drivers, guint16 vendor, guint16 product, guint16 subsystem_vendor, GList *probes, GError **error) { return MM_BASE_MODEM (mm_broadband_modem_via_new (uid, physdev, drivers, mm_plugin_get_name (self), vendor, product)); } /*****************************************************************************/ MM_PLUGIN_NAMED_CREATOR_SCOPE MMPlugin * mm_plugin_create_via (void) { static const gchar *subsystems[] = { "tty", NULL }; static const mm_str_pair product_strings[] = { { (gchar *) "via", (gchar *) "cbp7" }, { (gchar *) "fusion", (gchar *) "2770p" }, { NULL, NULL } }; return MM_PLUGIN ( g_object_new (MM_TYPE_PLUGIN_VIA, MM_PLUGIN_NAME, MM_MODULE_NAME, MM_PLUGIN_ALLOWED_SUBSYSTEMS, subsystems, MM_PLUGIN_ALLOWED_PRODUCT_STRINGS, product_strings, MM_PLUGIN_ALLOWED_AT, TRUE, MM_PLUGIN_REQUIRED_QCDM, TRUE, NULL)); } static void mm_plugin_via_init (MMPluginVia *self) { } static void mm_plugin_via_class_init (MMPluginViaClass *klass) { MMPluginClass *plugin_class = MM_PLUGIN_CLASS (klass); plugin_class->create_modem = create_modem; } ModemManager-1.23.4-dev/src/plugins/wavecom/000077500000000000000000000000001456466623000206365ustar00rootroot00000000000000ModemManager-1.23.4-dev/src/plugins/wavecom/mm-broadband-modem-wavecom.c000066400000000000000000001311611456466623000260660ustar00rootroot00000000000000/* -*- 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) 2011 Ammonit Measurement GmbH * Copyright (C) 2012 Aleksander Morgado * Author: Aleksander Morgado */ #include #include #include #include #include #include #define _LIBMM_INSIDE_MM #include #include "ModemManager.h" #include "mm-log-object.h" #include "mm-serial-parsers.h" #include "mm-modem-helpers.h" #include "mm-iface-modem.h" #include "mm-iface-modem-3gpp.h" #include "mm-base-modem-at.h" #include "mm-broadband-modem-wavecom.h" static void iface_modem_init (MMIfaceModem *iface); static void iface_modem_3gpp_init (MMIfaceModem3gpp *iface); static MMIfaceModem3gpp *iface_modem_3gpp_parent; G_DEFINE_TYPE_EXTENDED (MMBroadbandModemWavecom, mm_broadband_modem_wavecom, MM_TYPE_BROADBAND_MODEM, 0, G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM, iface_modem_init) G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_3GPP, iface_modem_3gpp_init)) #define WAVECOM_MS_CLASS_CC_IDSTR "\"CC\"" #define WAVECOM_MS_CLASS_CG_IDSTR "\"CG\"" #define WAVECOM_MS_CLASS_B_IDSTR "\"B\"" #define WAVECOM_MS_CLASS_A_IDSTR "\"A\"" /* Setup relationship between 2G bands in the modem (identified by a * single digit in ASCII) and the bitmask in ModemManager. */ typedef struct { gchar wavecom_band; guint n_mm_bands; MMModemBand mm_bands[4]; } WavecomBand2G; static const WavecomBand2G bands_2g[] = { { '0', 1, { MM_MODEM_BAND_G850, 0, 0, 0 }}, { '1', 1, { MM_MODEM_BAND_EGSM, 0, 0, 0 }}, { '2', 1, { MM_MODEM_BAND_DCS, 0, 0, 0 }}, { '3', 1, { MM_MODEM_BAND_PCS, 0, 0, 0 }}, { '4', 2, { MM_MODEM_BAND_G850, MM_MODEM_BAND_PCS, 0, 0 }}, { '5', 2, { MM_MODEM_BAND_EGSM, MM_MODEM_BAND_DCS, 0, 0 }}, { '6', 2, { MM_MODEM_BAND_EGSM, MM_MODEM_BAND_PCS, 0, 0 }}, { '7', 4, { MM_MODEM_BAND_DCS, MM_MODEM_BAND_PCS, MM_MODEM_BAND_G850, MM_MODEM_BAND_EGSM }} }; /* Setup relationship between the 3G band bitmask in the modem and the bitmask * in ModemManager. */ typedef struct { guint32 wavecom_band_flag; MMModemBand mm_band; } WavecomBand3G; static const WavecomBand3G bands_3g[] = { { (1 << 0), MM_MODEM_BAND_UTRAN_1 }, { (1 << 1), MM_MODEM_BAND_UTRAN_2 }, { (1 << 2), MM_MODEM_BAND_UTRAN_3 }, { (1 << 3), MM_MODEM_BAND_UTRAN_4 }, { (1 << 4), MM_MODEM_BAND_UTRAN_5 }, { (1 << 5), MM_MODEM_BAND_UTRAN_6 }, { (1 << 6), MM_MODEM_BAND_UTRAN_7 }, { (1 << 7), MM_MODEM_BAND_UTRAN_8 }, { (1 << 8), MM_MODEM_BAND_UTRAN_9 } }; /*****************************************************************************/ /* Load supported modes (Modem interface) */ static GArray * load_supported_modes_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { return g_task_propagate_pointer (G_TASK (res), error); } static void supported_ms_classes_query_ready (MMBaseModem *self, GAsyncResult *res, GTask *task) { GArray *all; GArray *combinations; GArray *filtered; const gchar *response; GError *error = NULL; MMModemModeCombination mode; MMModemMode mode_all; response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error); if (!response) { g_task_return_error (task, error); g_object_unref (task); return; } response = mm_strip_tag (response, "+CGCLASS:"); mode_all = MM_MODEM_MODE_NONE; if (strstr (response, WAVECOM_MS_CLASS_A_IDSTR)) mode_all |= MM_MODEM_MODE_3G; if (strstr (response, WAVECOM_MS_CLASS_B_IDSTR)) mode_all |= (MM_MODEM_MODE_2G | MM_MODEM_MODE_CS); if (strstr (response, WAVECOM_MS_CLASS_CG_IDSTR)) mode_all |= MM_MODEM_MODE_2G; if (strstr (response, WAVECOM_MS_CLASS_CC_IDSTR)) mode_all |= MM_MODEM_MODE_CS; /* If none received, error */ if (mode_all == MM_MODEM_MODE_NONE) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't get supported mobile station classes: '%s'", response); g_object_unref (task); return; } /* Build ALL mask */ all = g_array_sized_new (FALSE, FALSE, sizeof (MMModemModeCombination), 1); mode.allowed = mode_all; mode.preferred = MM_MODEM_MODE_NONE; g_array_append_val (all, mode); /* Build list of combinations */ combinations = g_array_sized_new (FALSE, FALSE, sizeof (MMModemModeCombination), 7); /* CS only */ mode.allowed = MM_MODEM_MODE_CS; mode.preferred = MM_MODEM_MODE_NONE; g_array_append_val (combinations, mode); /* 2G only */ mode.allowed = MM_MODEM_MODE_2G; mode.preferred = MM_MODEM_MODE_NONE; g_array_append_val (combinations, mode); /* CS and 2G */ mode.allowed = (MM_MODEM_MODE_CS | MM_MODEM_MODE_2G); mode.preferred = MM_MODEM_MODE_2G; g_array_append_val (combinations, mode); /* 3G only */ mode.allowed = MM_MODEM_MODE_3G; mode.preferred = MM_MODEM_MODE_NONE; g_array_append_val (combinations, mode); /* 2G and 3G */ mode.allowed = (MM_MODEM_MODE_2G | MM_MODEM_MODE_3G); mode.preferred = MM_MODEM_MODE_NONE; g_array_append_val (combinations, mode); /* 2G and 3G, 2G preferred */ mode.allowed = (MM_MODEM_MODE_2G | MM_MODEM_MODE_3G); mode.preferred = MM_MODEM_MODE_2G; g_array_append_val (combinations, mode); /* 2G and 3G, 3G preferred */ mode.allowed = (MM_MODEM_MODE_2G | MM_MODEM_MODE_3G); mode.preferred = MM_MODEM_MODE_3G; g_array_append_val (combinations, mode); /* Filter out those unsupported modes */ filtered = mm_filter_supported_modes (all, combinations, self); g_array_unref (all); g_array_unref (combinations); g_task_return_pointer (task, filtered, (GDestroyNotify) g_array_unref); g_object_unref (task); } static void load_supported_modes (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; task = g_task_new (self, NULL, callback, user_data); mm_base_modem_at_command ( MM_BASE_MODEM (self), "+CGCLASS=?", 3, FALSE, (GAsyncReadyCallback)supported_ms_classes_query_ready, task); } /*****************************************************************************/ /* Load initial allowed/preferred modes (Modem interface) */ typedef struct { MMModemMode allowed; MMModemMode preferred; } LoadCurrentModesResult; static gboolean load_current_modes_finish (MMIfaceModem *self, GAsyncResult *res, MMModemMode *allowed, MMModemMode *preferred, GError **error) { g_autofree LoadCurrentModesResult *result = NULL; result = g_task_propagate_pointer (G_TASK (res), error); if (!result) return FALSE; *allowed = result->allowed; *preferred = result->preferred; return TRUE; } static void wwsm_read_ready (MMBaseModem *self, GAsyncResult *res, GTask *task) { g_autoptr(GRegex) r = NULL; g_autoptr(GMatchInfo) match_info = NULL; g_autofree LoadCurrentModesResult *result = NULL; const gchar *response; GError *error = NULL; response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error); if (!response) { g_task_return_error (task, error); g_object_unref (task); return; } result = g_new0 (LoadCurrentModesResult, 1); result->allowed = MM_MODEM_MODE_NONE; result->preferred = MM_MODEM_MODE_NONE; /* Possible responses: * +WWSM: 0 (2G only) * +WWSM: 1 (3G only) * +WWSM: 2,0 (Any) * +WWSM: 2,1 (2G preferred) * +WWSM: 2,2 (3G preferred) */ r = g_regex_new ("\\r\\n\\+WWSM: ([0-2])(,([0-2]))?.*$", 0, 0, NULL); g_assert (r != NULL); if (g_regex_match (r, response, 0, &match_info)) { guint allowed = 0; if (mm_get_uint_from_match_info (match_info, 1, &allowed)) { switch (allowed) { case 0: result->allowed = MM_MODEM_MODE_2G; result->preferred = MM_MODEM_MODE_NONE; break; case 1: result->allowed = MM_MODEM_MODE_3G; result->preferred = MM_MODEM_MODE_NONE; break; case 2: { guint preferred = 0; result->allowed = (MM_MODEM_MODE_2G | MM_MODEM_MODE_3G); /* 3, to avoid the comma */ if (mm_get_uint_from_match_info (match_info, 3, &preferred)) { switch (preferred) { case 0: result->preferred = MM_MODEM_MODE_NONE; break; case 1: result->preferred = MM_MODEM_MODE_2G; break; case 2: result->preferred = MM_MODEM_MODE_3G; break; default: g_warn_if_reached (); break; } } break; } default: g_warn_if_reached (); break; } } } if (result->allowed == MM_MODEM_MODE_NONE) g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Unknown wireless data service reply: '%s'", response); else g_task_return_pointer (task, g_steal_pointer (&result), g_free); g_object_unref (task); } static void current_ms_class_ready (MMBaseModem *self, GAsyncResult *res, GTask *task) { g_autofree LoadCurrentModesResult *result = NULL; const gchar *response; GError *error = NULL; response = mm_base_modem_at_command_finish (self, res, &error); if (!response) { g_task_return_error (task, error); g_object_unref (task); return; } response = mm_strip_tag (response, "+CGCLASS:"); if (strncmp (response, WAVECOM_MS_CLASS_A_IDSTR, strlen (WAVECOM_MS_CLASS_A_IDSTR)) == 0) { mm_obj_dbg (self, "configured as a Class A mobile station"); /* For 3G devices, query WWSM status */ mm_base_modem_at_command (self, "+WWSM?", 3, FALSE, (GAsyncReadyCallback)wwsm_read_ready, task); return; } result = g_new0 (LoadCurrentModesResult, 1); result->allowed = MM_MODEM_MODE_NONE; result->preferred = MM_MODEM_MODE_NONE; if (strncmp (response, WAVECOM_MS_CLASS_B_IDSTR, strlen (WAVECOM_MS_CLASS_B_IDSTR)) == 0) { mm_obj_dbg (self, "configured as a Class B mobile station"); result->allowed = (MM_MODEM_MODE_2G | MM_MODEM_MODE_CS); result->preferred = MM_MODEM_MODE_2G; } else if (strncmp (response, WAVECOM_MS_CLASS_CG_IDSTR, strlen (WAVECOM_MS_CLASS_CG_IDSTR)) == 0) { mm_obj_dbg (self, "configured as a Class CG mobile station"); result->allowed = MM_MODEM_MODE_2G; result->preferred = MM_MODEM_MODE_NONE; } else if (strncmp (response, WAVECOM_MS_CLASS_CC_IDSTR, strlen (WAVECOM_MS_CLASS_CC_IDSTR)) == 0) { mm_obj_dbg (self, "configured as a Class CC mobile station"); result->allowed = MM_MODEM_MODE_CS; result->preferred = MM_MODEM_MODE_NONE; } if (result->allowed == MM_MODEM_MODE_NONE) g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Unknown mobile station class: '%s'", response); else g_task_return_pointer (task, g_steal_pointer (&result), g_free); g_object_unref (task); } static void load_current_modes (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; task = g_task_new (self, NULL, callback, user_data); mm_base_modem_at_command (MM_BASE_MODEM (self), "+CGCLASS?", 3, FALSE, (GAsyncReadyCallback)current_ms_class_ready, task); } /*****************************************************************************/ /* Set allowed modes (Modem interface) */ typedef struct { gchar *cgclass_command; gchar *wwsm_command; } SetCurrentModesContext; static void set_current_modes_context_free (SetCurrentModesContext *ctx) { g_free (ctx->cgclass_command); g_free (ctx->wwsm_command); g_free (ctx); } static gboolean set_current_modes_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void wwsm_update_ready (MMBaseModem *self, GAsyncResult *res, GTask *task) { GError *error = NULL; mm_base_modem_at_command_finish (self, res, &error); if (error) g_task_return_error (task, error); else g_task_return_boolean (task, TRUE); g_object_unref (task); } static void cgclass_update_ready (MMBaseModem *self, GAsyncResult *res, GTask *task) { SetCurrentModesContext *ctx; GError *error = NULL; ctx = g_task_get_task_data (task); mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error); if (error) { g_task_return_error (task, error); g_object_unref (task); return; } if (!ctx->wwsm_command) { g_task_return_boolean (task, TRUE); g_object_unref (task); return; } mm_base_modem_at_command (MM_BASE_MODEM (self), ctx->wwsm_command, 3, FALSE, (GAsyncReadyCallback)wwsm_update_ready, task); } static void set_current_modes (MMIfaceModem *self, MMModemMode allowed, MMModemMode preferred, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; SetCurrentModesContext *ctx; task = g_task_new (self, NULL, callback, user_data); ctx = g_new0 (SetCurrentModesContext, 1); g_task_set_task_data (task, ctx, (GDestroyNotify) set_current_modes_context_free); /* Handle ANY/NONE */ if (allowed == MM_MODEM_MODE_ANY && preferred == MM_MODEM_MODE_NONE) { if (mm_iface_modem_is_3g (self)) { allowed = (MM_MODEM_MODE_2G | MM_MODEM_MODE_3G); preferred = MM_MODEM_MODE_NONE; } else { allowed = (MM_MODEM_MODE_CS | MM_MODEM_MODE_2G); preferred = MM_MODEM_MODE_2G; } } if (allowed == MM_MODEM_MODE_CS) ctx->cgclass_command = g_strdup ("+CGCLASS=" WAVECOM_MS_CLASS_CC_IDSTR); else if (allowed == MM_MODEM_MODE_2G) ctx->cgclass_command = g_strdup ("+CGCLASS=" WAVECOM_MS_CLASS_CG_IDSTR); else if (allowed == (MM_MODEM_MODE_2G | MM_MODEM_MODE_CS) && preferred == MM_MODEM_MODE_2G) ctx->cgclass_command = g_strdup ("+CGCLASS=" WAVECOM_MS_CLASS_B_IDSTR); else if (allowed & MM_MODEM_MODE_3G) { if (allowed == (MM_MODEM_MODE_2G | MM_MODEM_MODE_3G)) { if (preferred == MM_MODEM_MODE_2G) ctx->wwsm_command = g_strdup ("+WWSM=2,1"); else if (preferred == MM_MODEM_MODE_3G) ctx->wwsm_command = g_strdup ("+WWSM=2,2"); else if (preferred == MM_MODEM_MODE_NONE) ctx->wwsm_command = g_strdup ("+WWSM=2,0"); } else if (allowed == MM_MODEM_MODE_3G) ctx->wwsm_command = g_strdup ("+WWSM=1"); if (ctx->wwsm_command) ctx->cgclass_command = g_strdup ("+CGCLASS=" WAVECOM_MS_CLASS_A_IDSTR); } if (!ctx->cgclass_command) { g_autofree gchar *allowed_str = NULL; g_autofree gchar *preferred_str = NULL; allowed_str = mm_modem_mode_build_string_from_mask (allowed); preferred_str = mm_modem_mode_build_string_from_mask (preferred); g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Requested mode (allowed: '%s', preferred: '%s') not " "supported by the modem.", allowed_str, preferred_str); g_object_unref (task); return; } mm_base_modem_at_command (MM_BASE_MODEM (self), ctx->cgclass_command, 3, FALSE, (GAsyncReadyCallback)cgclass_update_ready, task); } /*****************************************************************************/ /* Load supported bands (Modem interface) */ static GArray * load_supported_bands_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { return g_task_propagate_pointer (G_TASK (res), error); } static void load_supported_bands (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; GArray *bands; task = g_task_new (self, NULL, callback, user_data); /* We do assume that we already know if the modem is 2G-only, 3G-only or * 2G+3G. This is checked quite before trying to load supported bands. */ #define _g_array_insert_enum(array,index,type,val) do { \ type aux = (type)val; \ g_array_insert_val (array, index, aux); \ } while (0) /* Add 3G-specific bands */ if (mm_iface_modem_is_3g (self)) { bands = g_array_sized_new (FALSE, FALSE, sizeof (MMModemBand), 10); _g_array_insert_enum (bands, 0, MMModemBand, MM_MODEM_BAND_UTRAN_1); _g_array_insert_enum (bands, 1, MMModemBand, MM_MODEM_BAND_UTRAN_2); _g_array_insert_enum (bands, 2, MMModemBand, MM_MODEM_BAND_UTRAN_3); _g_array_insert_enum (bands, 3, MMModemBand, MM_MODEM_BAND_UTRAN_4); _g_array_insert_enum (bands, 4, MMModemBand, MM_MODEM_BAND_UTRAN_5); _g_array_insert_enum (bands, 5, MMModemBand, MM_MODEM_BAND_UTRAN_6); _g_array_insert_enum (bands, 6, MMModemBand, MM_MODEM_BAND_UTRAN_7); _g_array_insert_enum (bands, 7, MMModemBand, MM_MODEM_BAND_UTRAN_8); _g_array_insert_enum (bands, 8, MMModemBand, MM_MODEM_BAND_UTRAN_9); } /* Add 2G-specific bands */ else { bands = g_array_sized_new (FALSE, FALSE, sizeof (MMModemBand), 4); _g_array_insert_enum (bands, 0, MMModemBand, MM_MODEM_BAND_EGSM); _g_array_insert_enum (bands, 1, MMModemBand, MM_MODEM_BAND_DCS); _g_array_insert_enum (bands, 2, MMModemBand, MM_MODEM_BAND_PCS); _g_array_insert_enum (bands, 3, MMModemBand, MM_MODEM_BAND_G850); } g_task_return_pointer (task, bands, (GDestroyNotify) g_array_unref); g_object_unref (task); } /*****************************************************************************/ /* Load current bands (Modem interface) */ static GArray * load_current_bands_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { return g_task_propagate_pointer (G_TASK (res), error); } static void get_2g_band_ready (MMBaseModem *self, GAsyncResult *res, GTask *task) { const gchar *response; const gchar *p; GError *error = NULL; GArray *bands_array = NULL; response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error); if (!response) { g_task_return_error (task, error); g_object_unref (task); return; } p = mm_strip_tag (response, "+WMBS:"); if (p) { guint i; for (i = 0; i < G_N_ELEMENTS (bands_2g); i++) { if (bands_2g[i].wavecom_band == *p) { guint j; if (G_UNLIKELY (!bands_array)) bands_array = g_array_new (FALSE, FALSE, sizeof (MMModemBand)); for (j = 0; j < bands_2g[i].n_mm_bands; j++) g_array_append_val (bands_array, bands_2g[i].mm_bands[j]); } } } if (!bands_array) g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't parse current bands reply: '%s'", response); else g_task_return_pointer (task, bands_array, (GDestroyNotify)g_array_unref); g_object_unref (task); } static void get_3g_band_ready (MMBaseModem *self, GAsyncResult *res, GTask *task) { const gchar *response; const gchar *p; GError *error = NULL; GArray *bands_array = NULL; guint32 wavecom_band; response = mm_base_modem_at_command_finish (self, res, &error); if (!response) { g_task_return_error (task, error); g_object_unref (task); return; } /* Example reply: * AT+WUBS? --> * <-- +WUBS: "3",1 * <-- OK * The "3" meaning here Band I and II are selected. */ p = mm_strip_tag (response, "+WUBS:"); if (*p == '"') p++; wavecom_band = atoi (p); if (wavecom_band > 0) { guint i; for (i = 0; i < G_N_ELEMENTS (bands_3g); i++) { if (bands_3g[i].wavecom_band_flag & wavecom_band) { if (G_UNLIKELY (!bands_array)) bands_array = g_array_new (FALSE, FALSE, sizeof (MMModemBand)); g_array_append_val (bands_array, bands_3g[i].mm_band); } } } if (!bands_array) g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't parse current bands reply: '%s'", response); else g_task_return_pointer (task, bands_array, (GDestroyNotify)g_array_unref); g_object_unref (task); } static void load_current_bands (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; task = g_task_new (self, NULL, callback, user_data); if (mm_iface_modem_is_3g (self)) mm_base_modem_at_command (MM_BASE_MODEM (self), "AT+WUBS?", 3, FALSE, (GAsyncReadyCallback)get_3g_band_ready, task); else mm_base_modem_at_command (MM_BASE_MODEM (self), "AT+WMBS?", 3, FALSE, (GAsyncReadyCallback)get_2g_band_ready, task); } /*****************************************************************************/ /* Set current_bands (Modem interface) */ static gboolean set_current_bands_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void wmbs_set_ready (MMBaseModem *self, GAsyncResult *res, GTask *task) { GError *error = NULL; if (!mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error)) g_task_return_error (task, error); else g_task_return_boolean (task, TRUE); g_object_unref (task); } static void set_bands_3g (GTask *task, GArray *bands_array) { MMBroadbandModemWavecom *self; guint wavecom_band = 0; guint i; g_autoptr(GArray) bands_array_final = NULL; g_autofree gchar *cmd = NULL; self = g_task_get_source_object (task); /* The special case of ANY should be treated separately. */ if (bands_array->len == 1 && g_array_index (bands_array, MMModemBand, 0) == MM_MODEM_BAND_ANY) { /* We build an array with all bands to set; so that we use the same * logic to build the cinterion_band, and so that we can log the list of * bands being set properly */ bands_array_final = g_array_sized_new (FALSE, FALSE, sizeof (MMModemBand), G_N_ELEMENTS (bands_3g)); for (i = 0; i < G_N_ELEMENTS (bands_3g); i++) g_array_append_val (bands_array_final, bands_3g[i].mm_band); } else bands_array_final = g_array_ref (bands_array); for (i = 0; i < G_N_ELEMENTS (bands_3g); i++) { guint j; for (j = 0; j < bands_array_final->len; j++) { if (g_array_index (bands_array_final, MMModemBand, j) == bands_3g[i].mm_band) { wavecom_band |= bands_3g[i].wavecom_band_flag; break; } } } if (wavecom_band == 0) { g_autofree gchar *bands_string = NULL; bands_string = mm_common_build_bands_string ((MMModemBand *)(gpointer)bands_array_final->data, bands_array_final->len); g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "The given band combination is not supported: '%s'", bands_string); g_object_unref (task); return; } cmd = g_strdup_printf ("+WMBS=\"%u\",1", wavecom_band); mm_base_modem_at_command (MM_BASE_MODEM (self), cmd, 3, FALSE, (GAsyncReadyCallback)wmbs_set_ready, task); } static void set_bands_2g (GTask *task, GArray *bands_array) { MMBroadbandModemWavecom *self; gchar wavecom_band = '\0'; guint i; g_autofree gchar *cmd = NULL; g_autoptr(GArray) bands_array_final = NULL; self = g_task_get_source_object (task); /* If the iface properly checked the given list against the supported bands, * it's not possible to get an array longer than 4 here. */ g_assert (bands_array->len <= 4); /* The special case of ANY should be treated separately. */ if (bands_array->len == 1 && g_array_index (bands_array, MMModemBand, 0) == MM_MODEM_BAND_ANY) { const WavecomBand2G *all; /* All bands is the last element in our 2G bands array */ all = &bands_2g[G_N_ELEMENTS (bands_2g) - 1]; /* We build an array with all bands to set; so that we use the same * logic to build the cinterion_band, and so that we can log the list of * bands being set properly */ bands_array_final = g_array_sized_new (FALSE, FALSE, sizeof (MMModemBand), 4); g_array_append_vals (bands_array_final, all->mm_bands, all->n_mm_bands); } else bands_array_final = g_array_ref (bands_array); for (i = 0; wavecom_band == '\0' && i < G_N_ELEMENTS (bands_2g); i++) { g_autoptr(GArray) supported_combination = NULL; supported_combination = g_array_sized_new (FALSE, FALSE, sizeof (MMModemBand), bands_2g[i].n_mm_bands); g_array_append_vals (supported_combination, bands_2g[i].mm_bands, bands_2g[i].n_mm_bands); /* Check if the given array is exactly one of the supported combinations */ if (mm_common_bands_garray_cmp (bands_array_final, supported_combination)) { wavecom_band = bands_2g[i].wavecom_band; break; } } if (wavecom_band == '\0') { g_autofree gchar *bands_string = NULL; bands_string = mm_common_build_bands_string ((MMModemBand *)(gpointer)bands_array_final->data, bands_array_final->len); g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "The given band combination is not supported: '%s'", bands_string); g_object_unref (task); return; } cmd = g_strdup_printf ("+WMBS=%c,1", wavecom_band); mm_base_modem_at_command (MM_BASE_MODEM (self), cmd, 3, FALSE, (GAsyncReadyCallback)wmbs_set_ready, task); } static void set_current_bands (MMIfaceModem *self, GArray *bands_array, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; /* The bands that we get here are previously validated by the interface, and * that means that ALL the bands given here were also given in the list of * supported bands. BUT BUT, that doesn't mean that the exact list of bands * will end up being valid, as not all combinations are possible. E.g, * Wavecom modems supporting only 2G have specific combinations allowed. */ task = g_task_new (self, NULL, callback, user_data); if (mm_iface_modem_is_3g (self)) set_bands_3g (task, bands_array); else set_bands_2g (task, bands_array); } /*****************************************************************************/ /* Load access technologies (Modem interface) */ static gboolean load_access_technologies_finish (MMIfaceModem *self, GAsyncResult *res, MMModemAccessTechnology *access_technologies, guint *mask, GError **error) { MMModemAccessTechnology act; const gchar *p; const gchar *response; response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, error); if (!response) return FALSE; act = MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN; p = mm_strip_tag (response, "+WGPRSIND:"); if (p) { switch (*p) { case '1': /* GPRS only */ act = MM_MODEM_ACCESS_TECHNOLOGY_GPRS; break; case '2': /* EGPRS/EDGE supported */ act = MM_MODEM_ACCESS_TECHNOLOGY_EDGE; break; case '3': /* 3G R99 supported */ act = MM_MODEM_ACCESS_TECHNOLOGY_UMTS; break; case '4': /* HSDPA supported */ act = MM_MODEM_ACCESS_TECHNOLOGY_HSDPA; break; case '5': /* HSUPA supported */ act = MM_MODEM_ACCESS_TECHNOLOGY_HSUPA; break; default: break; } } if (act == MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't parse access technologies result: '%s'", response); return FALSE; } /* We are reporting ALL 3GPP access technologies here */ *access_technologies = act; *mask = MM_IFACE_MODEM_3GPP_ALL_ACCESS_TECHNOLOGIES_MASK; return TRUE; } static void load_access_technologies (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data) { mm_base_modem_at_command (MM_BASE_MODEM (self), "+WGPRS=9,2", 3, FALSE, callback, user_data); } /*****************************************************************************/ /* Register in network (3GPP interface) */ static gboolean register_in_network_finish (MMIfaceModem3gpp *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void parent_registration_ready (MMIfaceModem3gpp *self, GAsyncResult *res, GTask *task) { GError *error = NULL; if (!iface_modem_3gpp_parent->register_in_network_finish (self, res, &error)) g_task_return_error (task, error); else g_task_return_boolean (task, TRUE); g_object_unref (task); } static void run_parent_registration (GTask *task) { MMBroadbandModemWavecom *self; const gchar *operator_id; self = g_task_get_source_object (task); operator_id = g_task_get_task_data (task); iface_modem_3gpp_parent->register_in_network ( MM_IFACE_MODEM_3GPP (self), operator_id, g_task_get_cancellable (task), (GAsyncReadyCallback)parent_registration_ready, task); } static gboolean parse_network_registration_mode (const gchar *reply, guint *mode) { g_autoptr(GRegex) r = NULL; g_autoptr(GMatchInfo) match_info = NULL; g_assert (mode != NULL); if (!reply) return FALSE; r = g_regex_new ("\\+COPS:\\s*(\\d)", G_REGEX_UNGREEDY, 0, NULL); g_assert (r != NULL); g_regex_match (r, reply, 0, &match_info); return (g_match_info_matches (match_info) && mm_get_uint_from_match_info (match_info, 1, mode)); } static void cops_ready (MMBaseModem *self, GAsyncResult *res, GTask *task) { const gchar *response; GError *error = NULL; guint mode; response = mm_base_modem_at_command_finish (self, res, &error); if (!response) { g_task_return_error (task, error); g_object_unref (task); return; } if (!parse_network_registration_mode (response, &mode)) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't parse current network registration mode: '%s'", response); g_object_unref (task); return; } /* If the modem is not configured for automatic registration, run parent */ if (mode != 0) { run_parent_registration (task); return; } mm_obj_dbg (self, "device is already in automatic registration mode, not requesting it again"); g_task_return_boolean (task, TRUE); g_object_unref (task); } static void register_in_network (MMIfaceModem3gpp *self, const gchar *operator_id, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; task = g_task_new (self, cancellable, callback, user_data); /* Store operator id as task data */ g_task_set_task_data (task, g_strdup (operator_id), g_free); /* If requesting automatic registration, we first need to query what the * current mode is. We must NOT send +COPS=0 if it already is in 0 mode, * or the device will get stuck. */ if (operator_id == NULL || operator_id[0] == '\0') { /* Check which is the current operator selection status */ mm_base_modem_at_command (MM_BASE_MODEM (self), "+COPS?", 3, FALSE, (GAsyncReadyCallback)cops_ready, task); return; } /* Otherwise, run parent's implementation right away */ run_parent_registration (task); } /*****************************************************************************/ /* After SIM unlock (Modem interface) */ static gboolean modem_after_sim_unlock_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static gboolean after_sim_unlock_wait_cb (GTask *task) { g_task_return_boolean (task, TRUE); g_object_unref (task); return G_SOURCE_REMOVE; } static void modem_after_sim_unlock (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; /* A short wait is necessary for SIM to become ready, otherwise reloading * facility lock states may fail with a +CME ERROR: 515 error. */ task = g_task_new (self, NULL, callback, user_data); g_timeout_add_seconds (5, (GSourceFunc)after_sim_unlock_wait_cb, task); } /*****************************************************************************/ /* Modem power up (Modem interface) */ static gboolean modem_power_up_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { return !!mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, error); } static void modem_power_up (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data) { mm_obj_warn (self, "not in full functionality status, power-up command is needed"); mm_obj_warn (self, "the device maybe rebooted"); /* Try to go to full functionality mode without rebooting the system. * Works well if we previously switched off the power with CFUN=4 */ mm_base_modem_at_command (MM_BASE_MODEM (self), "+CFUN=1,0", 3, FALSE, callback, user_data); } /*****************************************************************************/ /* Modem power down (Modem interface) */ static gboolean modem_power_down_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { return !!mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, error); } static void modem_power_down (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data) { /* Use AT+CFUN=4 for power down. It will stop the RF (IMSI detach), and * keeps access to the SIM */ mm_base_modem_at_command (MM_BASE_MODEM (self), "+CFUN=4", 3, FALSE, callback, user_data); } /*****************************************************************************/ /* Modem power down (Modem interface) */ static gboolean modem_power_off_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { return !!mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, error); } static void modem_power_off (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data) { mm_base_modem_at_command (MM_BASE_MODEM (self), "+CPOF=1", 3, FALSE, callback, user_data); } /*****************************************************************************/ static void setup_ports (MMBroadbandModem *self) { gpointer parser; MMPortSerialAt *primary; g_autoptr(GRegex) regex = NULL; /* Call parent's setup ports first always */ MM_BROADBAND_MODEM_CLASS (mm_broadband_modem_wavecom_parent_class)->setup_ports (self); /* Set 9600 baudrate by default in the AT port */ mm_obj_dbg (self, "baudrate will be set to 9600 bps..."); primary = mm_base_modem_peek_port_primary (MM_BASE_MODEM (self)); if (!primary) return; /* AT+CPIN? replies will never have an OK appended */ parser = mm_serial_parser_v1_new (); regex = g_regex_new ("\\r\\n\\+CPIN: .*\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); mm_serial_parser_v1_set_custom_regex (parser, regex, NULL); mm_port_serial_at_set_response_parser (MM_PORT_SERIAL_AT (primary), mm_serial_parser_v1_parse, parser, mm_serial_parser_v1_destroy); } /*****************************************************************************/ MMBroadbandModemWavecom * mm_broadband_modem_wavecom_new (const gchar *device, const gchar *physdev, const gchar **drivers, const gchar *plugin, guint16 vendor_id, guint16 product_id) { return g_object_new (MM_TYPE_BROADBAND_MODEM_WAVECOM, MM_BASE_MODEM_DEVICE, device, MM_BASE_MODEM_PHYSDEV, physdev, MM_BASE_MODEM_DRIVERS, drivers, MM_BASE_MODEM_PLUGIN, plugin, MM_BASE_MODEM_VENDOR_ID, vendor_id, MM_BASE_MODEM_PRODUCT_ID, product_id, /* Generic bearer supports TTY only */ MM_BASE_MODEM_DATA_NET_SUPPORTED, FALSE, MM_BASE_MODEM_DATA_TTY_SUPPORTED, TRUE, NULL); } static void mm_broadband_modem_wavecom_init (MMBroadbandModemWavecom *self) { } static void iface_modem_init (MMIfaceModem *iface) { iface->load_supported_modes = load_supported_modes; iface->load_supported_modes_finish = load_supported_modes_finish; iface->load_current_modes = load_current_modes; iface->load_current_modes_finish = load_current_modes_finish; iface->set_current_modes = set_current_modes; iface->set_current_modes_finish = set_current_modes_finish; iface->load_supported_bands = load_supported_bands; iface->load_supported_bands_finish = load_supported_bands_finish; iface->load_current_bands = load_current_bands; iface->load_current_bands_finish = load_current_bands_finish; iface->set_current_bands = set_current_bands; iface->set_current_bands_finish = set_current_bands_finish; iface->load_access_technologies = load_access_technologies; iface->load_access_technologies_finish = load_access_technologies_finish; iface->modem_after_sim_unlock = modem_after_sim_unlock; iface->modem_after_sim_unlock_finish = modem_after_sim_unlock_finish; iface->modem_power_up = modem_power_up; iface->modem_power_up_finish = modem_power_up_finish; iface->modem_power_down = modem_power_down; iface->modem_power_down_finish = modem_power_down_finish; iface->modem_power_off = modem_power_off; iface->modem_power_off_finish = modem_power_off_finish; } static void iface_modem_3gpp_init (MMIfaceModem3gpp *iface) { iface_modem_3gpp_parent = g_type_interface_peek_parent (iface); iface->register_in_network = register_in_network; iface->register_in_network_finish = register_in_network_finish; } static void mm_broadband_modem_wavecom_class_init (MMBroadbandModemWavecomClass *klass) { MMBroadbandModemClass *broadband_modem_class = MM_BROADBAND_MODEM_CLASS (klass); broadband_modem_class->setup_ports = setup_ports; } ModemManager-1.23.4-dev/src/plugins/wavecom/mm-broadband-modem-wavecom.h000066400000000000000000000046761456466623000261050ustar00rootroot00000000000000/* -*- 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) 2011 Ammonit Measurement GmbH * Copyright (C) 2012 Aleksander Morgado * * Author: Aleksander Morgado */ #ifndef MM_BROADBAND_MODEM_WAVECOM_H #define MM_BROADBAND_MODEM_WAVECOM_H #include "mm-broadband-modem.h" #define MM_TYPE_BROADBAND_MODEM_WAVECOM (mm_broadband_modem_wavecom_get_type ()) #define MM_BROADBAND_MODEM_WAVECOM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_BROADBAND_MODEM_WAVECOM, MMBroadbandModemWavecom)) #define MM_BROADBAND_MODEM_WAVECOM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_BROADBAND_MODEM_WAVECOM, MMBroadbandModemWavecomClass)) #define MM_IS_BROADBAND_MODEM_WAVECOM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_BROADBAND_MODEM_WAVECOM)) #define MM_IS_BROADBAND_MODEM_WAVECOM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_BROADBAND_MODEM_WAVECOM)) #define MM_BROADBAND_MODEM_WAVECOM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_BROADBAND_MODEM_WAVECOM, MMBroadbandModemWavecomClass)) typedef struct _MMBroadbandModemWavecom MMBroadbandModemWavecom; typedef struct _MMBroadbandModemWavecomClass MMBroadbandModemWavecomClass; struct _MMBroadbandModemWavecom { MMBroadbandModem parent; }; struct _MMBroadbandModemWavecomClass{ MMBroadbandModemClass parent; }; GType mm_broadband_modem_wavecom_get_type (void); MMBroadbandModemWavecom *mm_broadband_modem_wavecom_new (const gchar *device, const gchar *physdev, const gchar **drivers, const gchar *plugin, guint16 vendor_id, guint16 product_id); #endif /* MM_BROADBAND_MODEM_WAVECOM_H */ ModemManager-1.23.4-dev/src/plugins/wavecom/mm-plugin-wavecom.c000066400000000000000000000057771456466623000243660ustar00rootroot00000000000000/* -*- 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. * * 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. * * Copyright (C) 2011 Ammonit Measurement GmbH * Copyright (C) 2012 Aleksander Morgado * * Author: Aleksander Morgado */ #include #include #define _LIBMM_INSIDE_MM #include #include "mm-plugin-common.h" #include "mm-broadband-modem-wavecom.h" #define MM_TYPE_PLUGIN_WAVECOM mm_plugin_wavecom_get_type () MM_DEFINE_PLUGIN (WAVECOM, wavecom, Wavecom) /*****************************************************************************/ static MMBaseModem * create_modem (MMPlugin *self, const gchar *uid, const gchar *physdev, const gchar **drivers, guint16 vendor, guint16 product, guint16 subsystem_vendor, GList *probes, GError **error) { return MM_BASE_MODEM (mm_broadband_modem_wavecom_new (uid, physdev, drivers, mm_plugin_get_name (self), vendor, product)); } /*****************************************************************************/ MM_PLUGIN_NAMED_CREATOR_SCOPE MMPlugin * mm_plugin_create_wavecom (void) { static const gchar *subsystems[] = { "tty", NULL }; static const guint16 vendor_ids[] = { 0x114f, 0 }; static const gchar *forbidden_drivers[] = { "qcserial", NULL }; return MM_PLUGIN ( g_object_new (MM_TYPE_PLUGIN_WAVECOM, MM_PLUGIN_NAME, MM_MODULE_NAME, MM_PLUGIN_ALLOWED_SUBSYSTEMS, subsystems, MM_PLUGIN_ALLOWED_VENDOR_IDS, vendor_ids, MM_PLUGIN_FORBIDDEN_DRIVERS, forbidden_drivers, MM_PLUGIN_ALLOWED_AT, TRUE, NULL)); } static void mm_plugin_wavecom_init (MMPluginWavecom *self) { } static void mm_plugin_wavecom_class_init (MMPluginWavecomClass *klass) { MMPluginClass *plugin_class = MM_PLUGIN_CLASS (klass); plugin_class->create_modem = create_modem; } ModemManager-1.23.4-dev/src/plugins/x22x/000077500000000000000000000000001456466623000200005ustar00rootroot00000000000000ModemManager-1.23.4-dev/src/plugins/x22x/77-mm-x22x-port-types.rules000066400000000000000000000107721456466623000246540ustar00rootroot00000000000000# do not edit this file, it will be overwritten on update # Alcatel One Touch X220D # Alcatel One Touch X200 # # These values were scraped from the X220D's Windows .inf files. jrdmdm.inf # lists the actual command and data (ie PPP) ports, while jrdser.inf lists the # aux ports that may be either AT-capable or not but cannot be used for PPP. ACTION!="add|change|move|bind", GOTO="mm_x22x_port_types_end" SUBSYSTEMS=="usb", ATTRS{idVendor}=="1bbb", GOTO="mm_x22x_generic_vendorcheck" SUBSYSTEMS=="usb", ATTRS{idVendor}=="0b3c", GOTO="mm_x22x_olivetti_vendorcheck" GOTO="mm_x22x_port_types_end" # Generic JRD devices --------------------------- LABEL="mm_x22x_generic_vendorcheck" SUBSYSTEMS=="usb", ATTRS{bInterfaceNumber}=="?*", ENV{.MM_USBIFNUM}="$attr{bInterfaceNumber}" # Alcatel X200 ATTRS{idVendor}=="1bbb", ATTRS{idProduct}=="0000", ENV{.MM_USBIFNUM}=="03", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1" ATTRS{idVendor}=="1bbb", ATTRS{idProduct}=="0000", ENV{.MM_USBIFNUM}=="00", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1" ATTRS{idVendor}=="1bbb", ATTRS{idProduct}=="0000", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1" ATTRS{idVendor}=="1bbb", ATTRS{idProduct}=="0000", ENV{.MM_USBIFNUM}=="02", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1" ATTRS{idVendor}=="1bbb", ATTRS{idProduct}=="0000", ENV{ID_MM_X22X_TAGGED}="1" ATTRS{idVendor}=="1bbb", ATTRS{idProduct}=="0017", ENV{.MM_USBIFNUM}=="05", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1" ATTRS{idVendor}=="1bbb", ATTRS{idProduct}=="0017", ENV{.MM_USBIFNUM}=="00", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1" ATTRS{idVendor}=="1bbb", ATTRS{idProduct}=="0017", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1" ATTRS{idVendor}=="1bbb", ATTRS{idProduct}=="0017", ENV{.MM_USBIFNUM}=="02", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1" ATTRS{idVendor}=="1bbb", ATTRS{idProduct}=="0017", ENV{.MM_USBIFNUM}=="03", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1" ATTRS{idVendor}=="1bbb", ATTRS{idProduct}=="0017", ENV{ID_MM_X22X_TAGGED}="1" # Archos G9 ATTRS{idVendor}=="1bbb", ATTRS{idProduct}=="00B7", ENV{.MM_USBIFNUM}=="04", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1" ATTRS{idVendor}=="1bbb", ATTRS{idProduct}=="00B7", ENV{.MM_USBIFNUM}=="00", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1" ATTRS{idVendor}=="1bbb", ATTRS{idProduct}=="00B7", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_X22X_PORT_TYPE_NMEA}="1" ATTRS{idVendor}=="1bbb", ATTRS{idProduct}=="00B7", ENV{.MM_USBIFNUM}=="02", SUBSYSTEM=="tty", ENV{ID_MM_X22X_PORT_TYPE_VOICE}="1" ATTRS{idVendor}=="1bbb", ATTRS{idProduct}=="00B7", ENV{.MM_USBIFNUM}=="03", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1" ATTRS{idVendor}=="1bbb", ATTRS{idProduct}=="00B7", ENV{ID_MM_X22X_TAGGED}="1" # Alcaltel X602D ATTRS{idVendor}=="1bbb", ATTRS{idProduct}=="022c", ENV{.MM_USBIFNUM}=="00", ENV{ID_MM_PORT_IGNORE}="1" ATTRS{idVendor}=="1bbb", ATTRS{idProduct}=="022c", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1" ATTRS{idVendor}=="1bbb", ATTRS{idProduct}=="022c", ENV{.MM_USBIFNUM}=="02", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1" ATTRS{idVendor}=="1bbb", ATTRS{idProduct}=="022c", ENV{.MM_USBIFNUM}=="03", ENV{ID_MM_PORT_IGNORE}="1" ATTRS{idVendor}=="1bbb", ATTRS{idProduct}=="022c", ENV{ID_MM_X22X_TAGGED}="1" GOTO="mm_x22x_port_types_end" # Olivetti devices --------------------------- LABEL="mm_x22x_olivetti_vendorcheck" SUBSYSTEMS=="usb", ATTRS{bInterfaceNumber}=="?*", ENV{.MM_USBIFNUM}="$attr{bInterfaceNumber}" # Olicard 200 ATTRS{idVendor}=="0b3c", ATTRS{idProduct}=="c005", ENV{.MM_USBIFNUM}=="05", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1" ATTRS{idVendor}=="0b3c", ATTRS{idProduct}=="c005", ENV{.MM_USBIFNUM}=="00", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1" ATTRS{idVendor}=="0b3c", ATTRS{idProduct}=="c005", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1" ATTRS{idVendor}=="0b3c", ATTRS{idProduct}=="c005", ENV{.MM_USBIFNUM}=="02", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1" ATTRS{idVendor}=="0b3c", ATTRS{idProduct}=="c005", ENV{.MM_USBIFNUM}=="03", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1" ATTRS{idVendor}=="0b3c", ATTRS{idProduct}=="c005", ENV{.MM_USBIFNUM}=="06", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1" ATTRS{idVendor}=="0b3c", ATTRS{idProduct}=="c005", ENV{ID_MM_X22X_TAGGED}="1" GOTO="mm_x22x_port_types_end" LABEL="mm_x22x_port_types_end" ModemManager-1.23.4-dev/src/plugins/x22x/mm-broadband-modem-x22x.c000066400000000000000000000340511456466623000243720ustar00rootroot00000000000000/* -*- 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 - 2012 Red Hat, Inc. * Copyright (C) 2012 Aleksander Morgado */ #include #include #include #include #include #include #include "ModemManager.h" #include "mm-log.h" #include "mm-errors-types.h" #include "mm-modem-helpers.h" #include "mm-base-modem-at.h" #include "mm-iface-modem.h" #include "mm-broadband-modem-x22x.h" static void iface_modem_init (MMIfaceModem *iface); static MMIfaceModem *iface_modem_parent; G_DEFINE_TYPE_EXTENDED (MMBroadbandModemX22x, mm_broadband_modem_x22x, MM_TYPE_BROADBAND_MODEM, 0, G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM, iface_modem_init)) struct _MMBroadbandModemX22xPrivate { GRegex *mode_regex; GRegex *sysinfo_regex; GRegex *specc_regex; GRegex *sperror_regex; }; /*****************************************************************************/ /* Load supported modes (Modem interface) */ static GArray * load_supported_modes_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { return g_task_propagate_pointer (G_TASK (res), error); } static void parent_load_supported_modes_ready (MMIfaceModem *self, GAsyncResult *res, GTask *task) { GError *error = NULL; GArray *all; GArray *combinations; GArray *filtered; MMModemModeCombination mode; all = iface_modem_parent->load_supported_modes_finish (self, res, &error); if (!all) { g_task_return_error (task, error); g_object_unref (task); return; } /* Build list of combinations for 3GPP devices */ combinations = g_array_sized_new (FALSE, FALSE, sizeof (MMModemModeCombination), 3); /* 2G only */ mode.allowed = MM_MODEM_MODE_2G; mode.preferred = MM_MODEM_MODE_NONE; g_array_append_val (combinations, mode); /* 3G only */ mode.allowed = MM_MODEM_MODE_3G; mode.preferred = MM_MODEM_MODE_NONE; g_array_append_val (combinations, mode); /* 2G and 3G */ mode.allowed = (MM_MODEM_MODE_2G | MM_MODEM_MODE_3G); mode.preferred = MM_MODEM_MODE_NONE; g_array_append_val (combinations, mode); /* Filter out those unsupported modes */ filtered = mm_filter_supported_modes (all, combinations, self); g_array_unref (all); g_array_unref (combinations); g_task_return_pointer (task, filtered, (GDestroyNotify) g_array_unref); g_object_unref (task); } static void load_supported_modes (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data) { /* Run parent's loading */ iface_modem_parent->load_supported_modes ( MM_IFACE_MODEM (self), (GAsyncReadyCallback)parent_load_supported_modes_ready, g_task_new (self, NULL, callback, user_data)); } /*****************************************************************************/ /* Load initial allowed/preferred modes (Modem interface) */ static gboolean load_current_modes_finish (MMIfaceModem *self, GAsyncResult *res, MMModemMode *allowed, MMModemMode *preferred, GError **error) { g_autoptr(GRegex) r = NULL; g_autoptr(GMatchInfo) match_info = NULL; const gchar *response; gchar *str; gint mode = -1; GError *match_error = NULL; response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, error); if (!response) return FALSE; r = g_regex_new ("\\+SYSSEL:\\s*(\\d+),(\\d+),(\\d+),(\\d+)", G_REGEX_UNGREEDY, 0, NULL); g_assert (r != NULL); if (!g_regex_match_full (r, response, strlen (response), 0, 0, &match_info, &match_error)) { if (match_error) { g_propagate_error (error, match_error); } else { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't match +SYSSEL reply: %s", response); } return FALSE; } str = g_match_info_fetch (match_info, 3); mode = atoi (str); g_free (str); switch (mode) { case 0: *allowed = MM_MODEM_MODE_ANY; *preferred = MM_MODEM_MODE_NONE; return TRUE; case 1: *allowed = MM_MODEM_MODE_2G; *preferred = MM_MODEM_MODE_NONE; return TRUE; case 2: *allowed = MM_MODEM_MODE_3G; *preferred = MM_MODEM_MODE_NONE; return TRUE; default: break; } g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Failed to parse mode/tech response: Unexpected mode '%d'", mode); return FALSE; } static void load_current_modes (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data) { mm_base_modem_at_command (MM_BASE_MODEM (self), "+SYSSEL?", 3, FALSE, callback, user_data); } /*****************************************************************************/ /* Set allowed modes (Modem interface) */ static gboolean set_current_modes_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void allowed_mode_update_ready (MMBroadbandModemX22x *self, GAsyncResult *res, GTask *task) { GError *error = NULL; mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error); if (error) /* Let the error be critical. */ g_task_return_error (task, error); else g_task_return_boolean (task, TRUE); g_object_unref (task); } static void set_current_modes (MMIfaceModem *self, MMModemMode allowed, MMModemMode preferred, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; gchar *command; gint syssel = -1; task = g_task_new (self, NULL, callback, user_data); if (allowed == MM_MODEM_MODE_2G) syssel = 1; else if (allowed == MM_MODEM_MODE_3G) syssel = 2; else if (allowed == MM_MODEM_MODE_ANY && preferred == MM_MODEM_MODE_NONE) syssel = 0; else if (allowed == (MM_MODEM_MODE_2G | MM_MODEM_MODE_3G) && preferred == MM_MODEM_MODE_NONE) syssel = 0; else { gchar *allowed_str; gchar *preferred_str; allowed_str = mm_modem_mode_build_string_from_mask (allowed); preferred_str = mm_modem_mode_build_string_from_mask (preferred); g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Requested mode (allowed: '%s', preferred: '%s') not " "supported by the modem.", allowed_str, preferred_str); g_object_unref (task); g_free (allowed_str); g_free (preferred_str); return; } command = g_strdup_printf ("+SYSSEL=,%d,0", syssel); mm_base_modem_at_command ( MM_BASE_MODEM (self), command, 3, FALSE, (GAsyncReadyCallback)allowed_mode_update_ready, task); g_free (command); } /*****************************************************************************/ /* Load access technologies (Modem interface) */ static gboolean load_access_technologies_finish (MMIfaceModem *self, GAsyncResult *res, MMModemAccessTechnology *access_technologies, guint *mask, GError **error) { const gchar *result; result = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, error); if (!result) return FALSE; result = mm_strip_tag (result, "+SSND:"); *access_technologies = mm_string_to_access_tech (result); *mask = MM_MODEM_ACCESS_TECHNOLOGY_ANY; return TRUE; } static void load_access_technologies (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data) { mm_base_modem_at_command (MM_BASE_MODEM (self), "+SSND?", 3, FALSE, callback, user_data); } /*****************************************************************************/ /* Setup ports (Broadband modem class) */ static void set_ignored_unsolicited_events_handlers (MMBroadbandModemX22x *self) { MMPortSerialAt *ports[2]; guint i; ports[0] = mm_base_modem_peek_port_primary (MM_BASE_MODEM (self)); ports[1] = mm_base_modem_peek_port_secondary (MM_BASE_MODEM (self)); /* Enable/disable unsolicited events in given port */ for (i = 0; i < G_N_ELEMENTS (ports); i++) { if (!ports[i]) continue; mm_port_serial_at_add_unsolicited_msg_handler ( ports[i], self->priv->mode_regex, NULL, NULL, NULL); mm_port_serial_at_add_unsolicited_msg_handler ( ports[i], self->priv->sysinfo_regex, NULL, NULL, NULL); mm_port_serial_at_add_unsolicited_msg_handler ( ports[i], self->priv->specc_regex, NULL, NULL, NULL); mm_port_serial_at_add_unsolicited_msg_handler ( ports[i], self->priv->sperror_regex, NULL, NULL, NULL); } } static void setup_ports (MMBroadbandModem *self) { /* Call parent's setup ports first always */ MM_BROADBAND_MODEM_CLASS (mm_broadband_modem_x22x_parent_class)->setup_ports (self); /* Unsolicited messages to always ignore */ set_ignored_unsolicited_events_handlers (MM_BROADBAND_MODEM_X22X (self)); } /*****************************************************************************/ MMBroadbandModemX22x * mm_broadband_modem_x22x_new (const gchar *device, const gchar *physdev, const gchar **drivers, const gchar *plugin, guint16 vendor_id, guint16 product_id) { return g_object_new (MM_TYPE_BROADBAND_MODEM_X22X, MM_BASE_MODEM_DEVICE, device, MM_BASE_MODEM_PHYSDEV, physdev, MM_BASE_MODEM_DRIVERS, drivers, MM_BASE_MODEM_PLUGIN, plugin, MM_BASE_MODEM_VENDOR_ID, vendor_id, MM_BASE_MODEM_PRODUCT_ID, product_id, /* Generic bearer supports TTY only */ MM_BASE_MODEM_DATA_NET_SUPPORTED, FALSE, MM_BASE_MODEM_DATA_TTY_SUPPORTED, TRUE, NULL); } static void mm_broadband_modem_x22x_init (MMBroadbandModemX22x *self) { self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, MM_TYPE_BROADBAND_MODEM_X22X, MMBroadbandModemX22xPrivate); self->priv->mode_regex = g_regex_new ("\\r\\n\\^MODE:.+\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); self->priv->sysinfo_regex = g_regex_new ("\\r\\n\\^SYSINFO:.+\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); self->priv->specc_regex = g_regex_new ("\\r\\n\\+SPECC\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); self->priv->sperror_regex = g_regex_new ("\\r\\n\\+SPERROR:.+\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); } static void finalize (GObject *object) { MMBroadbandModemX22x *self = MM_BROADBAND_MODEM_X22X (object); g_regex_unref (self->priv->mode_regex); g_regex_unref (self->priv->sysinfo_regex); g_regex_unref (self->priv->specc_regex); g_regex_unref (self->priv->sperror_regex); G_OBJECT_CLASS (mm_broadband_modem_x22x_parent_class)->finalize (object); } static void iface_modem_init (MMIfaceModem *iface) { iface_modem_parent = g_type_interface_peek_parent (iface); iface->load_access_technologies = load_access_technologies; iface->load_access_technologies_finish = load_access_technologies_finish; iface->load_supported_modes = load_supported_modes; iface->load_supported_modes_finish = load_supported_modes_finish; iface->load_current_modes = load_current_modes; iface->load_current_modes_finish = load_current_modes_finish; iface->set_current_modes = set_current_modes; iface->set_current_modes_finish = set_current_modes_finish; } static void mm_broadband_modem_x22x_class_init (MMBroadbandModemX22xClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); MMBroadbandModemClass *broadband_modem_class = MM_BROADBAND_MODEM_CLASS (klass); g_type_class_add_private (object_class, sizeof (MMBroadbandModemX22xPrivate)); object_class->finalize = finalize; broadband_modem_class->setup_ports = setup_ports; } ModemManager-1.23.4-dev/src/plugins/x22x/mm-broadband-modem-x22x.h000066400000000000000000000046541456466623000244050ustar00rootroot00000000000000/* -*- 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 - 2012 Red Hat, Inc. * Copyright (C) 2012 Aleksander Morgado */ #ifndef MM_BROADBAND_MODEM_X22X_H #define MM_BROADBAND_MODEM_X22X_H #include "mm-broadband-modem.h" #define MM_TYPE_BROADBAND_MODEM_X22X (mm_broadband_modem_x22x_get_type ()) #define MM_BROADBAND_MODEM_X22X(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_BROADBAND_MODEM_X22X, MMBroadbandModemX22x)) #define MM_BROADBAND_MODEM_X22X_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_BROADBAND_MODEM_X22X, MMBroadbandModemX22xClass)) #define MM_IS_BROADBAND_MODEM_X22X(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_BROADBAND_MODEM_X22X)) #define MM_IS_BROADBAND_MODEM_X22X_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_BROADBAND_MODEM_X22X)) #define MM_BROADBAND_MODEM_X22X_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_BROADBAND_MODEM_X22X, MMBroadbandModemX22xClass)) typedef struct _MMBroadbandModemX22x MMBroadbandModemX22x; typedef struct _MMBroadbandModemX22xClass MMBroadbandModemX22xClass; typedef struct _MMBroadbandModemX22xPrivate MMBroadbandModemX22xPrivate; struct _MMBroadbandModemX22x { MMBroadbandModem parent; MMBroadbandModemX22xPrivate *priv; }; struct _MMBroadbandModemX22xClass{ MMBroadbandModemClass parent; }; GType mm_broadband_modem_x22x_get_type (void); MMBroadbandModemX22x *mm_broadband_modem_x22x_new (const gchar *device, const gchar *physdev, const gchar **drivers, const gchar *plugin, guint16 vendor_id, guint16 product_id); #endif /* MM_BROADBAND_MODEM_X22X_H */ ModemManager-1.23.4-dev/src/plugins/x22x/mm-plugin-x22x.c000066400000000000000000000205651456466623000226620ustar00rootroot00000000000000/* -*- 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 - 2012 Red Hat, Inc. * Copyright (C) 2012 Aleksander Morgado */ #include #include #define _LIBMM_INSIDE_MM #include #include "mm-log-object.h" #include "mm-modem-helpers.h" #include "mm-plugin-common.h" #include "mm-broadband-modem-x22x.h" #if defined WITH_QMI #include "mm-broadband-modem-qmi.h" #endif #define MM_TYPE_PLUGIN_X22X mm_plugin_x22x_get_type () MM_DEFINE_PLUGIN (X22X, x22x, X22x) /*****************************************************************************/ /* Custom init */ typedef struct { MMPortSerialAt *port; guint retries; } X22xCustomInitContext; static void x22x_custom_init_context_free (X22xCustomInitContext *ctx) { g_object_unref (ctx->port); g_slice_free (X22xCustomInitContext, ctx); } static gboolean x22x_custom_init_finish (MMPortProbe *probe, GAsyncResult *result, GError **error) { return g_task_propagate_boolean (G_TASK (result), error); } static void x22x_custom_init_step (GTask *task); static void gmr_ready (MMPortSerialAt *port, GAsyncResult *res, GTask *task) { MMPortProbe *probe; const gchar *p; g_autofree gchar *response = NULL; GError *error = NULL; probe = g_task_get_source_object (task); response = mm_port_serial_at_command_finish (port, res, &error); if (error) { g_error_free (error); /* Just retry... */ x22x_custom_init_step (task); return; } /* Note the lack of a ':' on the GMR; the X200 doesn't send one */ p = mm_strip_tag (response, "AT+GMR"); if (p && *p != 'L') { /* X200 modems have a GMR firmware revision that starts with 'L', and * as far as I can tell X060s devices have a revision starting with 'C'. * So use that to determine if the device is an X200, which this plugin * does supports. */ g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "Not supported with the X22X plugin"); } else { mm_obj_dbg (probe, "(X22X) device is supported by this plugin"); g_task_return_boolean (task, TRUE); } g_object_unref (task); } static void x22x_custom_init_step (GTask *task) { MMPortProbe *probe; X22xCustomInitContext *ctx; GCancellable *cancellable; probe = g_task_get_source_object (task); ctx = g_task_get_task_data (task); cancellable = g_task_get_cancellable (task); /* If cancelled, end */ if (g_cancellable_is_cancelled (cancellable)) { mm_obj_dbg (probe, "(X22X) no need to keep on running custom init"); g_task_return_boolean (task, TRUE); g_object_unref (task); return; } if (ctx->retries == 0) { /* In this case, we need the AT command result to decide whether we can * support this modem or not, so really fail if we didn't get it. */ g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't get device revision information"); g_object_unref (task); return; } ctx->retries--; mm_port_serial_at_command ( ctx->port, "AT+GMR", 3, FALSE, /* raw */ FALSE, /* allow_cached */ cancellable, (GAsyncReadyCallback)gmr_ready, task); } static void x22x_custom_init (MMPortProbe *probe, MMPortSerialAt *port, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { MMDevice *device; X22xCustomInitContext *ctx; GTask *task; ctx = g_slice_new (X22xCustomInitContext); ctx->port = g_object_ref (port); ctx->retries = 3; task = g_task_new (probe, cancellable, callback, user_data); g_task_set_check_cancellable (task, FALSE); g_task_set_task_data (task, ctx, (GDestroyNotify)x22x_custom_init_context_free); /* TCT/Alcatel in their infinite wisdom assigned the same USB VID/PID to * the x060s (Longcheer firmware) and the x200 (X22X, this plugin) and thus * we can't tell them apart via udev rules. Worse, they both report the * same +GMM and +GMI, so we're left with just +GMR which is a sketchy way * to tell modems apart. We can't really use X22X-specific commands * like AT+SSND because we're not sure if they work when the SIM PIN has not * been entered yet; many modems have a limited command parser before the * SIM is unlocked. */ device = mm_port_probe_peek_device (probe); if (mm_device_get_vendor (device) != 0x1bbb || mm_device_get_product (device) != 0x0000) { /* If not exactly this vendor/product, just skip */ g_task_return_boolean (task, TRUE); g_object_unref (task); return; } x22x_custom_init_step (task); } /*****************************************************************************/ static MMBaseModem * create_modem (MMPlugin *self, const gchar *uid, const gchar *physdev, const gchar **drivers, guint16 vendor, guint16 product, guint16 subsystem_vendor, GList *probes, GError **error) { #if defined WITH_QMI if (mm_port_probe_list_has_qmi_port (probes)) { mm_obj_dbg (self, "QMI-powered X22X modem found..."); return MM_BASE_MODEM (mm_broadband_modem_qmi_new (uid, physdev, drivers, mm_plugin_get_name (self), vendor, product)); } #endif return MM_BASE_MODEM (mm_broadband_modem_x22x_new (uid, physdev, drivers, mm_plugin_get_name (self), vendor, product)); } /*****************************************************************************/ MM_PLUGIN_NAMED_CREATOR_SCOPE MMPlugin * mm_plugin_create_x22x (void) { static const gchar *subsystems[] = { "tty", "net", "usbmisc", NULL }; /* Vendors: TAMobile and Olivetti */ static const guint16 vendor_ids[] = { 0x1bbb, 0x0b3c, 0 }; /* Only handle X22X tagged devices here. */ static const gchar *udev_tags[] = { "ID_MM_X22X_TAGGED", NULL }; static const MMAsyncMethod custom_init = { .async = G_CALLBACK (x22x_custom_init), .finish = G_CALLBACK (x22x_custom_init_finish), }; return MM_PLUGIN ( g_object_new (MM_TYPE_PLUGIN_X22X, MM_PLUGIN_NAME, MM_MODULE_NAME, MM_PLUGIN_ALLOWED_SUBSYSTEMS, subsystems, MM_PLUGIN_ALLOWED_VENDOR_IDS, vendor_ids, MM_PLUGIN_ALLOWED_AT, TRUE, MM_PLUGIN_ALLOWED_QMI, TRUE, MM_PLUGIN_ALLOWED_UDEV_TAGS, udev_tags, MM_PLUGIN_CUSTOM_INIT, &custom_init, NULL)); } static void mm_plugin_x22x_init (MMPluginX22x *self) { } static void mm_plugin_x22x_class_init (MMPluginX22xClass *klass) { MMPluginClass *plugin_class = MM_PLUGIN_CLASS (klass); plugin_class->create_modem = create_modem; } ModemManager-1.23.4-dev/src/plugins/xmm/000077500000000000000000000000001456466623000177765ustar00rootroot00000000000000ModemManager-1.23.4-dev/src/plugins/xmm/mm-broadband-modem-mbim-xmm.c000066400000000000000000000140371456466623000253120ustar00rootroot00000000000000/* -*- 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) 2018 Aleksander Morgado */ #include #include #include #include #include #include #include "ModemManager.h" #include "mm-iface-modem.h" #include "mm-iface-modem-location.h" #include "mm-broadband-modem-mbim-xmm.h" #include "mm-shared-xmm.h" static void iface_modem_init (MMIfaceModem *iface); static void iface_modem_location_init (MMIfaceModemLocation *iface); static void shared_xmm_init (MMSharedXmm *iface); static MMIfaceModemLocation *iface_modem_location_parent; G_DEFINE_TYPE_EXTENDED (MMBroadbandModemMbimXmm, mm_broadband_modem_mbim_xmm, MM_TYPE_BROADBAND_MODEM_MBIM, 0, G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM, iface_modem_init) G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_LOCATION, iface_modem_location_init) G_IMPLEMENT_INTERFACE (MM_TYPE_SHARED_XMM, shared_xmm_init)) /*****************************************************************************/ MMBroadbandModemMbimXmm * mm_broadband_modem_mbim_xmm_new (const gchar *device, const gchar *physdev, const gchar **drivers, const gchar *plugin, guint16 vendor_id, guint16 product_id) { return g_object_new (MM_TYPE_BROADBAND_MODEM_MBIM_XMM, MM_BASE_MODEM_DEVICE, device, MM_BASE_MODEM_PHYSDEV, physdev, MM_BASE_MODEM_DRIVERS, drivers, MM_BASE_MODEM_PLUGIN, plugin, MM_BASE_MODEM_VENDOR_ID, vendor_id, MM_BASE_MODEM_PRODUCT_ID, product_id, /* MBIM bearer supports NET only */ MM_BASE_MODEM_DATA_NET_SUPPORTED, TRUE, MM_BASE_MODEM_DATA_TTY_SUPPORTED, FALSE, MM_IFACE_MODEM_SIM_HOT_SWAP_SUPPORTED, TRUE, MM_IFACE_MODEM_PERIODIC_SIGNAL_CHECK_DISABLED, TRUE, #if defined WITH_QMI && QMI_MBIM_QMUX_SUPPORTED MM_BROADBAND_MODEM_MBIM_QMI_UNSUPPORTED, TRUE, #endif NULL); } static void mm_broadband_modem_mbim_xmm_init (MMBroadbandModemMbimXmm *self) { } static void iface_modem_init (MMIfaceModem *iface) { iface->load_supported_modes = mm_shared_xmm_load_supported_modes; iface->load_supported_modes_finish = mm_shared_xmm_load_supported_modes_finish; iface->load_current_modes = mm_shared_xmm_load_current_modes; iface->load_current_modes_finish = mm_shared_xmm_load_current_modes_finish; iface->set_current_modes = mm_shared_xmm_set_current_modes; iface->set_current_modes_finish = mm_shared_xmm_set_current_modes_finish; iface->load_supported_bands = mm_shared_xmm_load_supported_bands; iface->load_supported_bands_finish = mm_shared_xmm_load_supported_bands_finish; iface->load_current_bands = mm_shared_xmm_load_current_bands; iface->load_current_bands_finish = mm_shared_xmm_load_current_bands_finish; iface->set_current_bands = mm_shared_xmm_set_current_bands; iface->set_current_bands_finish = mm_shared_xmm_set_current_bands_finish; /* power up/down already managed via MBIM */ iface->modem_power_off = mm_shared_xmm_power_off; iface->modem_power_off_finish = mm_shared_xmm_power_off_finish; iface->reset = mm_shared_xmm_reset; iface->reset_finish = mm_shared_xmm_reset_finish; } static void iface_modem_location_init (MMIfaceModemLocation *iface) { iface_modem_location_parent = g_type_interface_peek_parent (iface); iface->load_capabilities = mm_shared_xmm_location_load_capabilities; iface->load_capabilities_finish = mm_shared_xmm_location_load_capabilities_finish; iface->enable_location_gathering = mm_shared_xmm_enable_location_gathering; iface->enable_location_gathering_finish = mm_shared_xmm_enable_location_gathering_finish; iface->disable_location_gathering = mm_shared_xmm_disable_location_gathering; iface->disable_location_gathering_finish = mm_shared_xmm_disable_location_gathering_finish; iface->load_supl_server = mm_shared_xmm_location_load_supl_server; iface->load_supl_server_finish = mm_shared_xmm_location_load_supl_server_finish; iface->set_supl_server = mm_shared_xmm_location_set_supl_server; iface->set_supl_server_finish = mm_shared_xmm_location_set_supl_server_finish; } static MMBroadbandModemClass * peek_parent_broadband_modem_class (MMSharedXmm *self) { return MM_BROADBAND_MODEM_CLASS (mm_broadband_modem_mbim_xmm_parent_class); } static MMIfaceModemLocation * peek_parent_location_interface (MMSharedXmm *self) { return iface_modem_location_parent; } static void shared_xmm_init (MMSharedXmm *iface) { iface->peek_parent_broadband_modem_class = peek_parent_broadband_modem_class; iface->peek_parent_location_interface = peek_parent_location_interface; } static void mm_broadband_modem_mbim_xmm_class_init (MMBroadbandModemMbimXmmClass *klass) { MMBroadbandModemClass *broadband_modem_class = MM_BROADBAND_MODEM_CLASS (klass); broadband_modem_class->setup_ports = mm_shared_xmm_setup_ports; } ModemManager-1.23.4-dev/src/plugins/xmm/mm-broadband-modem-mbim-xmm.h000066400000000000000000000046111456466623000253140ustar00rootroot00000000000000/* -*- 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) 2018 Aleksander Morgado */ #ifndef MM_BROADBAND_MODEM_MBIM_XMM_H #define MM_BROADBAND_MODEM_MBIM_XMM_H #include "mm-broadband-modem-mbim.h" #define MM_TYPE_BROADBAND_MODEM_MBIM_XMM (mm_broadband_modem_mbim_xmm_get_type ()) #define MM_BROADBAND_MODEM_MBIM_XMM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_BROADBAND_MODEM_MBIM_XMM, MMBroadbandModemMbimXmm)) #define MM_BROADBAND_MODEM_MBIM_XMM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_BROADBAND_MODEM_MBIM_XMM, MMBroadbandModemMbimXmmClass)) #define MM_IS_BROADBAND_MODEM_MBIM_XMM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_BROADBAND_MODEM_MBIM_XMM)) #define MM_IS_BROADBAND_MODEM_MBIM_XMM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_BROADBAND_MODEM_MBIM_XMM)) #define MM_BROADBAND_MODEM_MBIM_XMM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_BROADBAND_MODEM_MBIM_XMM, MMBroadbandModemMbimXmmClass)) typedef struct _MMBroadbandModemMbimXmm MMBroadbandModemMbimXmm; typedef struct _MMBroadbandModemMbimXmmClass MMBroadbandModemMbimXmmClass; struct _MMBroadbandModemMbimXmm { MMBroadbandModemMbim parent; }; struct _MMBroadbandModemMbimXmmClass{ MMBroadbandModemMbimClass parent; }; GType mm_broadband_modem_mbim_xmm_get_type (void); MMBroadbandModemMbimXmm *mm_broadband_modem_mbim_xmm_new (const gchar *device, const gchar *physdev, const gchar **drivers, const gchar *plugin, guint16 vendor_id, guint16 product_id); #endif /* MM_BROADBAND_MODEM_XMM_H */ ModemManager-1.23.4-dev/src/plugins/xmm/mm-broadband-modem-xmm.c000066400000000000000000000150201456466623000243610ustar00rootroot00000000000000/* -*- 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) 2018 Aleksander Morgado */ #include #include #include #include #include #include #include "ModemManager.h" #include "mm-iface-modem.h" #include "mm-iface-modem-location.h" #include "mm-broadband-modem-xmm.h" #include "mm-shared-xmm.h" static void iface_modem_init (MMIfaceModem *iface); static void shared_xmm_init (MMSharedXmm *iface); static void iface_modem_signal_init (MMIfaceModemSignal *iface); static void iface_modem_location_init (MMIfaceModemLocation *iface); static MMIfaceModemLocation *iface_modem_location_parent; G_DEFINE_TYPE_EXTENDED (MMBroadbandModemXmm, mm_broadband_modem_xmm, MM_TYPE_BROADBAND_MODEM, 0, G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM, iface_modem_init) G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_SIGNAL, iface_modem_signal_init) G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_LOCATION, iface_modem_location_init) G_IMPLEMENT_INTERFACE (MM_TYPE_SHARED_XMM, shared_xmm_init)) /*****************************************************************************/ MMBroadbandModemXmm * mm_broadband_modem_xmm_new (const gchar *device, const gchar *physdev, const gchar **drivers, const gchar *plugin, guint16 vendor_id, guint16 product_id) { return g_object_new (MM_TYPE_BROADBAND_MODEM_XMM, MM_BASE_MODEM_DEVICE, device, MM_BASE_MODEM_PHYSDEV, physdev, MM_BASE_MODEM_DRIVERS, drivers, MM_BASE_MODEM_PLUGIN, plugin, MM_BASE_MODEM_VENDOR_ID, vendor_id, MM_BASE_MODEM_PRODUCT_ID, product_id, /* Generic bearer supports TTY only */ MM_BASE_MODEM_DATA_NET_SUPPORTED, FALSE, MM_BASE_MODEM_DATA_TTY_SUPPORTED, TRUE, NULL); } static void mm_broadband_modem_xmm_init (MMBroadbandModemXmm *self) { } static void iface_modem_init (MMIfaceModem *iface) { iface->load_supported_modes = mm_shared_xmm_load_supported_modes; iface->load_supported_modes_finish = mm_shared_xmm_load_supported_modes_finish; iface->load_current_modes = mm_shared_xmm_load_current_modes; iface->load_current_modes_finish = mm_shared_xmm_load_current_modes_finish; iface->set_current_modes = mm_shared_xmm_set_current_modes; iface->set_current_modes_finish = mm_shared_xmm_set_current_modes_finish; iface->load_supported_bands = mm_shared_xmm_load_supported_bands; iface->load_supported_bands_finish = mm_shared_xmm_load_supported_bands_finish; iface->load_current_bands = mm_shared_xmm_load_current_bands; iface->load_current_bands_finish = mm_shared_xmm_load_current_bands_finish; iface->set_current_bands = mm_shared_xmm_set_current_bands; iface->set_current_bands_finish = mm_shared_xmm_set_current_bands_finish; iface->load_power_state = mm_shared_xmm_load_power_state; iface->load_power_state_finish = mm_shared_xmm_load_power_state_finish; iface->modem_power_up = mm_shared_xmm_power_up; iface->modem_power_up_finish = mm_shared_xmm_power_up_finish; iface->modem_power_down = mm_shared_xmm_power_down; iface->modem_power_down_finish = mm_shared_xmm_power_down_finish; iface->modem_power_off = mm_shared_xmm_power_off; iface->modem_power_off_finish = mm_shared_xmm_power_off_finish; iface->reset = mm_shared_xmm_reset; iface->reset_finish = mm_shared_xmm_reset_finish; } static void iface_modem_location_init (MMIfaceModemLocation *iface) { iface_modem_location_parent = g_type_interface_peek_parent (iface); iface->load_capabilities = mm_shared_xmm_location_load_capabilities; iface->load_capabilities_finish = mm_shared_xmm_location_load_capabilities_finish; iface->enable_location_gathering = mm_shared_xmm_enable_location_gathering; iface->enable_location_gathering_finish = mm_shared_xmm_enable_location_gathering_finish; iface->disable_location_gathering = mm_shared_xmm_disable_location_gathering; iface->disable_location_gathering_finish = mm_shared_xmm_disable_location_gathering_finish; iface->load_supl_server = mm_shared_xmm_location_load_supl_server; iface->load_supl_server_finish = mm_shared_xmm_location_load_supl_server_finish; iface->set_supl_server = mm_shared_xmm_location_set_supl_server; iface->set_supl_server_finish = mm_shared_xmm_location_set_supl_server_finish; } static MMBroadbandModemClass * peek_parent_broadband_modem_class (MMSharedXmm *self) { return MM_BROADBAND_MODEM_CLASS (mm_broadband_modem_xmm_parent_class); } static MMIfaceModemLocation * peek_parent_location_interface (MMSharedXmm *self) { return iface_modem_location_parent; } static void iface_modem_signal_init (MMIfaceModemSignal *iface) { iface->check_support = mm_shared_xmm_signal_check_support; iface->check_support_finish = mm_shared_xmm_signal_check_support_finish; iface->load_values = mm_shared_xmm_signal_load_values; iface->load_values_finish = mm_shared_xmm_signal_load_values_finish; } static void shared_xmm_init (MMSharedXmm *iface) { iface->peek_parent_broadband_modem_class = peek_parent_broadband_modem_class; iface->peek_parent_location_interface = peek_parent_location_interface; } static void mm_broadband_modem_xmm_class_init (MMBroadbandModemXmmClass *klass) { MMBroadbandModemClass *broadband_modem_class = MM_BROADBAND_MODEM_CLASS (klass); broadband_modem_class->setup_ports = mm_shared_xmm_setup_ports; } ModemManager-1.23.4-dev/src/plugins/xmm/mm-broadband-modem-xmm.h000066400000000000000000000043271456466623000243760ustar00rootroot00000000000000/* -*- 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) 2018 Aleksander Morgado */ #ifndef MM_BROADBAND_MODEM_XMM_H #define MM_BROADBAND_MODEM_XMM_H #include "mm-broadband-modem.h" #define MM_TYPE_BROADBAND_MODEM_XMM (mm_broadband_modem_xmm_get_type ()) #define MM_BROADBAND_MODEM_XMM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_BROADBAND_MODEM_XMM, MMBroadbandModemXmm)) #define MM_BROADBAND_MODEM_XMM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_BROADBAND_MODEM_XMM, MMBroadbandModemXmmClass)) #define MM_IS_BROADBAND_MODEM_XMM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_BROADBAND_MODEM_XMM)) #define MM_IS_BROADBAND_MODEM_XMM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_BROADBAND_MODEM_XMM)) #define MM_BROADBAND_MODEM_XMM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_BROADBAND_MODEM_XMM, MMBroadbandModemXmmClass)) typedef struct _MMBroadbandModemXmm MMBroadbandModemXmm; typedef struct _MMBroadbandModemXmmClass MMBroadbandModemXmmClass; struct _MMBroadbandModemXmm { MMBroadbandModem parent; }; struct _MMBroadbandModemXmmClass{ MMBroadbandModemClass parent; }; GType mm_broadband_modem_xmm_get_type (void); MMBroadbandModemXmm *mm_broadband_modem_xmm_new (const gchar *device, const gchar *physdev, const gchar **drivers, const gchar *plugin, guint16 vendor_id, guint16 product_id); #endif /* MM_BROADBAND_MODEM_XMM_H */ ModemManager-1.23.4-dev/src/plugins/xmm/mm-modem-helpers-xmm.c000066400000000000000000001060311456466623000241120ustar00rootroot00000000000000/* -*- 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) 2018 Aleksander Morgado */ #include #include "mm-log.h" #include "mm-modem-helpers.h" #include "mm-modem-helpers-xmm.h" #include "mm-signal.h" /*****************************************************************************/ /* XACT common config */ typedef struct { guint num; MMModemBand band; } XactBandConfig; static const XactBandConfig xact_band_config[] = { /* GSM bands */ { .num = 900, .band = MM_MODEM_BAND_EGSM }, { .num = 1800, .band = MM_MODEM_BAND_DCS }, { .num = 1900, .band = MM_MODEM_BAND_PCS }, { .num = 850, .band = MM_MODEM_BAND_G850 }, { .num = 450, .band = MM_MODEM_BAND_G450 }, { .num = 480, .band = MM_MODEM_BAND_G480 }, { .num = 750, .band = MM_MODEM_BAND_G750 }, { .num = 380, .band = MM_MODEM_BAND_G380 }, { .num = 410, .band = MM_MODEM_BAND_G410 }, { .num = 710, .band = MM_MODEM_BAND_G710 }, { .num = 810, .band = MM_MODEM_BAND_G810 }, /* UMTS bands */ { .num = 1, .band = MM_MODEM_BAND_UTRAN_1 }, { .num = 2, .band = MM_MODEM_BAND_UTRAN_2 }, { .num = 3, .band = MM_MODEM_BAND_UTRAN_3 }, { .num = 4, .band = MM_MODEM_BAND_UTRAN_4 }, { .num = 5, .band = MM_MODEM_BAND_UTRAN_5 }, { .num = 6, .band = MM_MODEM_BAND_UTRAN_6 }, { .num = 7, .band = MM_MODEM_BAND_UTRAN_7 }, { .num = 8, .band = MM_MODEM_BAND_UTRAN_8 }, { .num = 9, .band = MM_MODEM_BAND_UTRAN_9 }, { .num = 10, .band = MM_MODEM_BAND_UTRAN_10 }, { .num = 11, .band = MM_MODEM_BAND_UTRAN_11 }, { .num = 12, .band = MM_MODEM_BAND_UTRAN_12 }, { .num = 13, .band = MM_MODEM_BAND_UTRAN_13 }, { .num = 14, .band = MM_MODEM_BAND_UTRAN_14 }, { .num = 19, .band = MM_MODEM_BAND_UTRAN_19 }, { .num = 20, .band = MM_MODEM_BAND_UTRAN_20 }, { .num = 21, .band = MM_MODEM_BAND_UTRAN_21 }, { .num = 22, .band = MM_MODEM_BAND_UTRAN_22 }, { .num = 25, .band = MM_MODEM_BAND_UTRAN_25 }, /* LTE bands */ { .num = 101, .band = MM_MODEM_BAND_EUTRAN_1 }, { .num = 102, .band = MM_MODEM_BAND_EUTRAN_2 }, { .num = 103, .band = MM_MODEM_BAND_EUTRAN_3 }, { .num = 104, .band = MM_MODEM_BAND_EUTRAN_4 }, { .num = 105, .band = MM_MODEM_BAND_EUTRAN_5 }, { .num = 106, .band = MM_MODEM_BAND_EUTRAN_6 }, { .num = 107, .band = MM_MODEM_BAND_EUTRAN_7 }, { .num = 108, .band = MM_MODEM_BAND_EUTRAN_8 }, { .num = 109, .band = MM_MODEM_BAND_EUTRAN_9 }, { .num = 110, .band = MM_MODEM_BAND_EUTRAN_10 }, { .num = 111, .band = MM_MODEM_BAND_EUTRAN_11 }, { .num = 112, .band = MM_MODEM_BAND_EUTRAN_12 }, { .num = 113, .band = MM_MODEM_BAND_EUTRAN_13 }, { .num = 114, .band = MM_MODEM_BAND_EUTRAN_14 }, { .num = 117, .band = MM_MODEM_BAND_EUTRAN_17 }, { .num = 118, .band = MM_MODEM_BAND_EUTRAN_18 }, { .num = 119, .band = MM_MODEM_BAND_EUTRAN_19 }, { .num = 120, .band = MM_MODEM_BAND_EUTRAN_20 }, { .num = 121, .band = MM_MODEM_BAND_EUTRAN_21 }, { .num = 122, .band = MM_MODEM_BAND_EUTRAN_22 }, { .num = 123, .band = MM_MODEM_BAND_EUTRAN_23 }, { .num = 124, .band = MM_MODEM_BAND_EUTRAN_24 }, { .num = 125, .band = MM_MODEM_BAND_EUTRAN_25 }, { .num = 126, .band = MM_MODEM_BAND_EUTRAN_26 }, { .num = 127, .band = MM_MODEM_BAND_EUTRAN_27 }, { .num = 128, .band = MM_MODEM_BAND_EUTRAN_28 }, { .num = 129, .band = MM_MODEM_BAND_EUTRAN_29 }, { .num = 130, .band = MM_MODEM_BAND_EUTRAN_30 }, { .num = 131, .band = MM_MODEM_BAND_EUTRAN_31 }, { .num = 132, .band = MM_MODEM_BAND_EUTRAN_32 }, { .num = 133, .band = MM_MODEM_BAND_EUTRAN_33 }, { .num = 134, .band = MM_MODEM_BAND_EUTRAN_34 }, { .num = 135, .band = MM_MODEM_BAND_EUTRAN_35 }, { .num = 136, .band = MM_MODEM_BAND_EUTRAN_36 }, { .num = 137, .band = MM_MODEM_BAND_EUTRAN_37 }, { .num = 138, .band = MM_MODEM_BAND_EUTRAN_38 }, { .num = 139, .band = MM_MODEM_BAND_EUTRAN_39 }, { .num = 140, .band = MM_MODEM_BAND_EUTRAN_40 }, { .num = 141, .band = MM_MODEM_BAND_EUTRAN_41 }, { .num = 142, .band = MM_MODEM_BAND_EUTRAN_42 }, { .num = 143, .band = MM_MODEM_BAND_EUTRAN_43 }, { .num = 144, .band = MM_MODEM_BAND_EUTRAN_44 }, { .num = 145, .band = MM_MODEM_BAND_EUTRAN_45 }, { .num = 146, .band = MM_MODEM_BAND_EUTRAN_46 }, { .num = 147, .band = MM_MODEM_BAND_EUTRAN_47 }, { .num = 148, .band = MM_MODEM_BAND_EUTRAN_48 }, { .num = 149, .band = MM_MODEM_BAND_EUTRAN_49 }, { .num = 150, .band = MM_MODEM_BAND_EUTRAN_50 }, { .num = 151, .band = MM_MODEM_BAND_EUTRAN_51 }, { .num = 152, .band = MM_MODEM_BAND_EUTRAN_52 }, { .num = 153, .band = MM_MODEM_BAND_EUTRAN_53 }, { .num = 154, .band = MM_MODEM_BAND_EUTRAN_54 }, { .num = 155, .band = MM_MODEM_BAND_EUTRAN_55 }, { .num = 156, .band = MM_MODEM_BAND_EUTRAN_56 }, { .num = 157, .band = MM_MODEM_BAND_EUTRAN_57 }, { .num = 158, .band = MM_MODEM_BAND_EUTRAN_58 }, { .num = 159, .band = MM_MODEM_BAND_EUTRAN_59 }, { .num = 160, .band = MM_MODEM_BAND_EUTRAN_60 }, { .num = 161, .band = MM_MODEM_BAND_EUTRAN_61 }, { .num = 162, .band = MM_MODEM_BAND_EUTRAN_62 }, { .num = 163, .band = MM_MODEM_BAND_EUTRAN_63 }, { .num = 164, .band = MM_MODEM_BAND_EUTRAN_64 }, { .num = 165, .band = MM_MODEM_BAND_EUTRAN_65 }, { .num = 166, .band = MM_MODEM_BAND_EUTRAN_66 }, }; #define XACT_NUM_IS_BAND_2G(num) (num > 300) #define XACT_NUM_IS_BAND_3G(num) (num < 100) #define XACT_NUM_IS_BAND_4G(num) (num > 100 && num < 300) static MMModemBand xact_num_to_band (guint num) { guint i; for (i = 0; i < G_N_ELEMENTS (xact_band_config); i++) { if (num == xact_band_config[i].num) return xact_band_config[i].band; } return MM_MODEM_BAND_UNKNOWN; } static guint xact_band_to_num (MMModemBand band) { guint i; for (i = 0; i < G_N_ELEMENTS (xact_band_config); i++) { if (band == xact_band_config[i].band) return xact_band_config[i].num; } return 0; } /*****************************************************************************/ /* XACT=? response parser */ /* Index of the array is the XMM-specific value */ static const MMModemMode xmm_modes[] = { ( MM_MODEM_MODE_2G ), ( MM_MODEM_MODE_3G ), ( MM_MODEM_MODE_4G ), ( MM_MODEM_MODE_2G | MM_MODEM_MODE_3G ), ( MM_MODEM_MODE_3G | MM_MODEM_MODE_4G ), ( MM_MODEM_MODE_2G | MM_MODEM_MODE_4G ), ( MM_MODEM_MODE_2G | MM_MODEM_MODE_3G | MM_MODEM_MODE_4G ), }; gboolean mm_xmm_parse_xact_test_response (const gchar *response, gpointer log_object, GArray **modes_out, GArray **bands_out, GError **error) { GError *inner_error = NULL; GArray *modes = NULL; GArray *all_modes = NULL; GArray *filtered = NULL; GArray *supported = NULL; GArray *preferred = NULL; GArray *bands = NULL; gchar **split = NULL; guint i; MMModemModeCombination all = { .allowed = MM_MODEM_MODE_NONE, .preferred = MM_MODEM_MODE_NONE }; g_assert (modes_out && bands_out); /* * AT+XACT=? * +XACT: (0-6),(0-2),0,1,2,4,5,8,101,102,103,104,105,107,108,111,... */ response = mm_strip_tag (response, "+XACT:"); split = mm_split_string_groups (response); if (g_strv_length (split) < 3) { inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Missing fields"); goto out; } /* First group is list of supported modes */ supported = mm_parse_uint_list (split[0], &inner_error); if (inner_error) goto out; if (!supported) { inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Missing modes"); goto out; } /* Second group is list of possible preferred modes. * For our purposes, the preferred list may be empty */ preferred = mm_parse_uint_list (split[1], &inner_error); if (inner_error) goto out; /* Build array of modes */ modes = g_array_new (FALSE, FALSE, sizeof (MMModemModeCombination)); for (i = 0; i < supported->len; i++) { guint supported_value; MMModemModeCombination combination; guint j; supported_value = g_array_index (supported, guint, i); if (supported_value >= G_N_ELEMENTS (xmm_modes)) { mm_obj_warn (log_object, "unexpected AcT supported value: %u", supported_value); continue; } /* Combination without any preferred */ combination.allowed = xmm_modes[supported_value]; combination.preferred = MM_MODEM_MODE_NONE; g_array_append_val (modes, combination); if (mm_count_bits_set (combination.allowed) == 1) continue; if (!preferred) continue; for (j = 0; j < preferred->len; j++) { guint preferred_value; preferred_value = g_array_index (preferred, guint, j); if (preferred_value >= G_N_ELEMENTS (xmm_modes)) { mm_obj_warn (log_object, "unexpected AcT preferred value: %u", preferred_value); continue; } combination.preferred = xmm_modes[preferred_value]; if (mm_count_bits_set (combination.preferred) != 1) { mm_obj_warn (log_object, "AcT preferred value should be a single AcT: %u", preferred_value); continue; } if (!(combination.allowed & combination.preferred)) continue; g_array_append_val (modes, combination); } } if (modes->len == 0) { inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "No modes list built from +XACT=? response"); goto out; } /* Build array of bands */ bands = g_array_new (FALSE, FALSE, sizeof (MMModemBand)); /* * The next element at index 2 may be '0'. We will just treat that field as * any other band field as '0' isn't a supported band, we'll just ignore it. */ for (i = 2; split[i]; i++) { MMModemBand band; guint num; if (!mm_get_uint_from_str (split[i], &num)) { mm_obj_warn (log_object, "unexpected band value: %s", split[i]); continue; } if (num == 0) continue; band = xact_num_to_band (num); if (band == MM_MODEM_BAND_UNKNOWN) { mm_obj_warn (log_object, "unsupported band value: %s", split[i]); continue; } g_array_append_val (bands, band); if (XACT_NUM_IS_BAND_2G (num)) all.allowed |= MM_MODEM_MODE_2G; if (XACT_NUM_IS_BAND_3G (num)) all.allowed |= MM_MODEM_MODE_3G; if (XACT_NUM_IS_BAND_4G (num)) all.allowed |= MM_MODEM_MODE_4G; } if (bands->len == 0) { inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "No bands list built from +XACT=? response"); goto out; } /* AT+XACT lies about the supported modes, e.g. it may report 2G supported * for 3G+4G only devices. So, filter out unsupported modes based on the * supported bands */ all_modes = g_array_sized_new (FALSE, FALSE, sizeof (MMModemModeCombination), 1); g_array_append_val (all_modes, all); filtered = mm_filter_supported_modes (all_modes, modes, log_object); if (!filtered || filtered->len == 0) { inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Empty supported mode list after frequency band filtering"); goto out; } /* success */ out: if (modes) g_array_unref (modes); if (all_modes) g_array_unref (all_modes); if (supported) g_array_unref (supported); if (preferred) g_array_unref (preferred); g_strfreev (split); if (inner_error) { if (filtered) g_array_unref (filtered); if (bands) g_array_unref (bands); g_propagate_error (error, inner_error); return FALSE; } g_assert (filtered); *modes_out = filtered; g_assert (bands); *bands_out = bands; return TRUE; } /*****************************************************************************/ /* AT+XACT? response parser */ gboolean mm_xmm_parse_xact_query_response (const gchar *response, MMModemModeCombination *mode_out, GArray **bands_out, GError **error) { g_autoptr(GRegex) r = NULL; g_autoptr(GMatchInfo) match_info = NULL; g_autoptr(GArray) bands = NULL; GError *inner_error = NULL; guint i; MMModemModeCombination mode = { .allowed = MM_MODEM_MODE_NONE, .preferred = MM_MODEM_MODE_NONE, }; /* At least one */ g_assert (mode_out || bands_out); /* * AT+XACT? * +XACT: 4,1,2,1,2,4,5,8,101,102,103,104,105,107,108,111,... * * Note: the first 3 fields corresponde to allowed and preferred modes. Only the * first one of those 3 first fields is mandatory, the other two may be empty. */ r = g_regex_new ("\\+XACT: (\\d+),([^,]*),([^,]*),(.*)(?:\\r\\n)?", G_REGEX_DOLLAR_ENDONLY | G_REGEX_RAW, 0, NULL); g_assert (r != NULL); g_regex_match_full (r, response, strlen (response), 0, 0, &match_info, &inner_error); if (inner_error) { g_propagate_error (error, inner_error); return FALSE; } if (!g_match_info_matches (match_info)) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Unsupported XACT? response: %s", response); return FALSE; } if (mode_out) { guint xmm_mode; /* Number at index 1 */ mm_get_uint_from_match_info (match_info, 1, &xmm_mode); if (xmm_mode >= G_N_ELEMENTS (xmm_modes)) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Unsupported XACT AcT value: %u", xmm_mode); return FALSE; } mode.allowed = xmm_modes[xmm_mode]; /* Number at index 2 */ if (mm_count_bits_set (mode.allowed) > 1 && mm_get_uint_from_match_info (match_info, 2, &xmm_mode)) { if (xmm_mode >= G_N_ELEMENTS (xmm_modes)) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Unsupported XACT preferred AcT value: %u", xmm_mode); return FALSE; } mode.preferred = xmm_modes[xmm_mode]; } /* Number at index 3: ignored */ } if (bands_out) { g_autofree gchar *bandstr = NULL; g_autoptr(GArray) nums = NULL; /* Bands start at index 4 */ bandstr = mm_get_string_unquoted_from_match_info (match_info, 4); nums = mm_parse_uint_list (bandstr, &inner_error); if (inner_error) { g_propagate_error (error, inner_error); return FALSE; } if (!nums) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Missing bands in XACT? response: %s", response); return FALSE; } bands = g_array_sized_new (FALSE, FALSE, sizeof (MMModemBand), nums->len); for (i = 0; i < nums->len; i++) { MMModemBand band; band = xact_num_to_band (g_array_index (nums, guint, i)); if (band != MM_MODEM_BAND_UNKNOWN) g_array_append_val (bands, band); } if (bands->len == 0) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Invalid list of bands in XACT? response: %s", response); return FALSE; } } /* success */ if (mode_out) { g_assert (mode.allowed != MM_MODEM_MODE_NONE); mode_out->allowed = mode.allowed; mode_out->preferred = mode.preferred; } if (bands_out) { g_assert (bands); *bands_out = g_steal_pointer (&bands); } return TRUE; } /*****************************************************************************/ /* AT+XACT=X command builder */ static gboolean append_rat_value (GString *str, MMModemMode mode, GError **error) { guint i; for (i = 0; i < G_N_ELEMENTS (xmm_modes); i++) { if (xmm_modes[i] == mode) { g_string_append_printf (str, "%u", i); return TRUE; } } g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "No AcT value matches requested mode"); return FALSE; } gchar * mm_xmm_build_xact_set_command (const MMModemModeCombination *mode, const GArray *bands, GError **error) { GString *command; /* At least one required */ g_assert (mode || bands); /* Build command */ command = g_string_new ("+XACT="); /* Mode is optional. If not given, we set all fields as empty */ if (mode) { /* Allowed mask */ if (!append_rat_value (command, mode->allowed, error)) { g_string_free (command, TRUE); return NULL; } /* Preferred */ if (mode->preferred != MM_MODEM_MODE_NONE) { g_string_append (command, ","); if (!append_rat_value (command, mode->preferred, error)) { g_string_free (command, TRUE); return NULL; } /* We never set because that is anyway not part of * ModemManager's API. In modems with triple GSM/UMTS/LTE mode, the * is always the highest of the remaining ones. E.g. * if "2G+3G+4G allowed with 2G preferred", the second preferred one * would be 4G, not 3G. */ g_string_append (command, ","); } else g_string_append (command, ",,"); } else g_string_append (command, ",,"); if (bands) { g_string_append (command, ","); /* Automatic band selection */ if (bands->len == 1 && g_array_index (bands, MMModemBand, 0) == MM_MODEM_BAND_ANY) g_string_append (command, "0"); else { guint i; for (i = 0; i < bands->len; i++) { MMModemBand band; guint num; band = g_array_index (bands, MMModemBand, i); num = xact_band_to_num (band); if (!num) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "Band unsupported by this plugin: %s", mm_modem_band_get_string (band)); g_string_free (command, TRUE); return NULL; } g_string_append_printf (command, "%s%u", i == 0 ? "" : ",", num); } } } return g_string_free (command, FALSE); } /*****************************************************************************/ /* Get mode to apply when ANY */ MMModemMode mm_xmm_get_modem_mode_any (const GArray *combinations) { guint i; MMModemMode any = MM_MODEM_MODE_NONE; guint any_bits_set = 0; for (i = 0; i < combinations->len; i++) { MMModemModeCombination *combination; guint bits_set; combination = &g_array_index (combinations, MMModemModeCombination, i); if (combination->preferred != MM_MODEM_MODE_NONE) continue; bits_set = mm_count_bits_set (combination->allowed); if (bits_set > any_bits_set) { any_bits_set = bits_set; any = combination->allowed; } } /* If combinations were processed via mm_xmm_parse_uact_test_response(), * we're sure that there will be at least one combination with preferred * 'none', so there must be some valid combination as result */ g_assert (any != MM_MODEM_MODE_NONE); return any; } /*****************************************************************************/ /* +XCESQ? response parser */ gboolean mm_xmm_parse_xcesq_query_response (const gchar *response, guint *out_rxlev, guint *out_ber, guint *out_rscp, guint *out_ecn0, guint *out_rsrq, guint *out_rsrp, gint *out_rssnr, GError **error) { g_autoptr(GRegex) r = NULL; g_autoptr(GMatchInfo) match_info = NULL; GError *inner_error = NULL; guint rxlev = 99; guint ber = 99; guint rscp = 255; guint ecn0 = 255; guint rsrq = 255; guint rsrp = 255; gint rssnr = 255; gboolean success = FALSE; g_assert (out_rxlev); g_assert (out_ber); g_assert (out_rscp); g_assert (out_ecn0); g_assert (out_rsrq); g_assert (out_rsrp); g_assert (out_rssnr); /* Response may be e.g.: * +XCESQ: 0,99,99,255,255,24,51,18 * +XCESQ: 0,99,99,46,31,255,255,255 * +XCESQ: 0,99,99,255,255,17,45,-2 */ r = g_regex_new ("\\+XCESQ: (\\d+),(\\d+),(\\d+),(\\d+),(\\d+),(\\d+),(\\d+),(-?\\d+)(?:\\r\\n)?", 0, 0, NULL); g_assert (r != NULL); g_regex_match_full (r, response, strlen (response), 0, 0, &match_info, &inner_error); if (!inner_error && g_match_info_matches (match_info)) { /* Ignore "n" value */ if (!mm_get_uint_from_match_info (match_info, 2, &rxlev)) { inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't read RXLEV"); goto out; } if (!mm_get_uint_from_match_info (match_info, 3, &ber)) { inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't read BER"); goto out; } if (!mm_get_uint_from_match_info (match_info, 4, &rscp)) { inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't read RSCP"); goto out; } if (!mm_get_uint_from_match_info (match_info, 5, &ecn0)) { inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't read Ec/N0"); goto out; } if (!mm_get_uint_from_match_info (match_info, 6, &rsrq)) { inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't read RSRQ"); goto out; } if (!mm_get_uint_from_match_info (match_info, 7, &rsrp)) { inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't read RSRP"); goto out; } if (!mm_get_int_from_match_info (match_info, 8, &rssnr)) { inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't read RSSNR"); goto out; } success = TRUE; } out: if (inner_error) { g_propagate_error (error, inner_error); return FALSE; } if (!success) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't parse +XCESQ response: %s", response); return FALSE; } *out_rxlev = rxlev; *out_ber = ber; *out_rscp = rscp; *out_ecn0 = ecn0; *out_rsrq = rsrq; *out_rsrp = rsrp; *out_rssnr = rssnr; return TRUE; } static gboolean rssnr_level_to_rssnr (gint rssnr_level, gpointer log_object, gdouble *out_rssnr) { if (rssnr_level <= 100 && rssnr_level >= -100) { *out_rssnr = rssnr_level / 2.0; return TRUE; } if (rssnr_level != 255) mm_obj_warn (log_object, "unexpected RSSNR level: %u", rssnr_level); return FALSE; } /*****************************************************************************/ /* Get extended signal information */ gboolean mm_xmm_xcesq_response_to_signal_info (const gchar *response, gpointer log_object, MMSignal **out_gsm, MMSignal **out_umts, MMSignal **out_lte, GError **error) { guint rxlev = 0; guint ber = 0; guint rscp_level = 0; guint ecn0_level = 0; guint rsrq_level = 0; guint rsrp_level = 0; gint rssnr_level = 0; gdouble rssi = MM_SIGNAL_UNKNOWN; gdouble rscp = MM_SIGNAL_UNKNOWN; gdouble ecio = MM_SIGNAL_UNKNOWN; gdouble rsrq = MM_SIGNAL_UNKNOWN; gdouble rsrp = MM_SIGNAL_UNKNOWN; gdouble rssnr = MM_SIGNAL_UNKNOWN; MMSignal *gsm = NULL; MMSignal *umts = NULL; MMSignal *lte = NULL; if (!mm_xmm_parse_xcesq_query_response (response, &rxlev, &ber, &rscp_level, &ecn0_level, &rsrq_level, &rsrp_level, &rssnr_level, error)) return FALSE; /* GERAN RSSI */ if (mm_3gpp_rxlev_to_rssi (rxlev, log_object, &rssi)) { gsm = mm_signal_new (); mm_signal_set_rssi (gsm, rssi); } /* ignore BER */ /* UMTS RSCP */ if (mm_3gpp_rscp_level_to_rscp (rscp_level, log_object, &rscp)) { umts = mm_signal_new (); mm_signal_set_rscp (umts, rscp); } /* UMTS EcIo (assumed EcN0) */ if (mm_3gpp_ecn0_level_to_ecio (ecn0_level, log_object, &ecio)) { if (!umts) umts = mm_signal_new (); mm_signal_set_ecio (umts, ecio); } /* Calculate RSSI if we have ecio and rscp */ if (umts && ecio != -G_MAXDOUBLE && rscp != -G_MAXDOUBLE) { mm_signal_set_rssi (umts, rscp - ecio); } /* LTE RSRQ */ if (mm_3gpp_rsrq_level_to_rsrq (rsrq_level, log_object, &rsrq)) { lte = mm_signal_new (); mm_signal_set_rsrq (lte, rsrq); } /* LTE RSRP */ if (mm_3gpp_rsrp_level_to_rsrp (rsrp_level, log_object, &rsrp)) { if (!lte) lte = mm_signal_new (); mm_signal_set_rsrp (lte, rsrp); } /* LTE RSSNR */ if (rssnr_level_to_rssnr (rssnr_level, log_object, &rssnr)) { if (!lte) lte = mm_signal_new (); mm_signal_set_snr (lte, rssnr); } if (!gsm && !umts && !lte) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't build detailed signal info"); return FALSE; } if (out_gsm) *out_gsm = gsm; if (out_umts) *out_umts = umts; if (out_lte) *out_lte = lte; return TRUE; } /*****************************************************************************/ /* AT+XLCSLSR=? response parser */ static gboolean number_group_contains_value (const gchar *group, const gchar *group_name, guint value, GError **error) { GArray *aux; guint i; gboolean found; aux = mm_parse_uint_list (group, NULL); if (!aux) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "Unsupported +XLCSLSR format: invalid %s field format", group_name); return FALSE; } found = FALSE; for (i = 0; i < aux->len; i++) { guint value_i; value_i = g_array_index (aux, guint, i); if (value == value_i) { found = TRUE; break; } } g_array_unref (aux); return found; } gboolean mm_xmm_parse_xlcslsr_test_response (const gchar *response, gboolean *transport_protocol_invalid_supported, gboolean *transport_protocol_supl_supported, gboolean *standalone_position_mode_supported, gboolean *ms_assisted_based_position_mode_supported, gboolean *loc_response_type_nmea_supported, gboolean *gnss_type_gps_glonass_supported, GError **error) { gboolean ret = FALSE; gchar **groups = NULL; GError *inner_error = NULL; /* * AT+XLCSLSR=? * +XLCSLSR:(0-2),(0-3), ,(0,1), ,(0,1),(0 -7200),(0-255),(0-1),(0-2),(1-256),(0,1) * transport_protocol: 2 (invalid) or 1 (supl) * pos_mode: 3 (standalone) or 2 (ms assisted/based) * client_id: * client_id_type: * mlc_number: * mlc_number_type: * interval: 1 (seconds) * service_type_id: * pseudonym_indicator: * loc_response_type: 1 (NMEA strings) * nmea_mask: 118 (01110110: GGA,GSA,GSV,RMC,VTG) * gnss_type: 0 (GPS or GLONASS) */ response = mm_strip_tag (response, "+XLCSLSR:"); groups = mm_split_string_groups (response); /* We expect 12 groups */ if (g_strv_length (groups) < 12) { inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "Unsupported +XLCSLSR format: expected 12 fields"); goto out; } if (transport_protocol_invalid_supported) { *transport_protocol_invalid_supported = number_group_contains_value (groups[0], "transport protocol", 2, /* invalid */ &inner_error); if (inner_error) goto out; } if (transport_protocol_supl_supported) { *transport_protocol_supl_supported = number_group_contains_value (groups[0], "transport protocol", 1, /* supl */ &inner_error); if (inner_error) goto out; } if (standalone_position_mode_supported) { *standalone_position_mode_supported = number_group_contains_value (groups[1], "position mode", 3, /* standalone */ &inner_error); if (inner_error) goto out; } if (ms_assisted_based_position_mode_supported) { *ms_assisted_based_position_mode_supported = number_group_contains_value (groups[1], "position mode", 2, /* ms assisted/based */ &inner_error); if (inner_error) goto out; } if (loc_response_type_nmea_supported) { *loc_response_type_nmea_supported = number_group_contains_value (groups[9], "location response type", 1, /* NMEA */ &inner_error); if (inner_error) goto out; } if (gnss_type_gps_glonass_supported) { *gnss_type_gps_glonass_supported = number_group_contains_value (groups[11], "gnss type", 0, /* GPS/GLONASS */ &inner_error); if (inner_error) goto out; } ret = TRUE; out: g_strfreev (groups); if (inner_error) { g_propagate_error (error, inner_error); return FALSE; } return ret; } /*****************************************************************************/ /* AT+XLCSSLP? response parser */ gboolean mm_xmm_parse_xlcsslp_query_response (const gchar *response, gchar **supl_address, GError **error) { g_autoptr(GRegex) r = NULL; g_autoptr(GMatchInfo) match_info = NULL; GError *inner_error = NULL; gchar *address = NULL; guint port = 0; /* * E.g.: * +XLCSSLP:1,"www.spirent-lcs.com",7275 */ r = g_regex_new ("\\+XLCSSLP:\\s*(\\d+),([^,]*),(\\d+)(?:\\r\\n)?", G_REGEX_DOLLAR_ENDONLY | G_REGEX_RAW, 0, NULL); g_assert (r != NULL); g_regex_match_full (r, response, strlen (response), 0, 0, &match_info, &inner_error); if (!inner_error && g_match_info_matches (match_info)) { guint type; /* We only support types 0 (IPv4) and 1 (FQDN) */ mm_get_uint_from_match_info (match_info, 1, &type); if (type != 0 && type != 1) { inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "Unsupported SUPL server address type (%u) in response: %s", type, response); goto out; } address = mm_get_string_unquoted_from_match_info (match_info, 2); mm_get_uint_from_match_info (match_info, 3, &port); if (!port) { inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Invalid SUPL address port number in response: %s", response); goto out; } } out: if (inner_error) { g_propagate_error (error, inner_error); return FALSE; } if (supl_address) *supl_address = g_strdup_printf ("%s:%u", address, port); g_free (address); return TRUE; } ModemManager-1.23.4-dev/src/plugins/xmm/mm-modem-helpers-xmm.h000066400000000000000000000074671456466623000241340ustar00rootroot00000000000000/* -*- 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) 2018 Aleksander Morgado */ #ifndef MM_MODEM_HELPERS_XMM_H #define MM_MODEM_HELPERS_XMM_H #include #include /* AT+XACT=? response parser */ gboolean mm_xmm_parse_xact_test_response (const gchar *response, gpointer logger, GArray **modes_out, GArray **bands_out, GError **error); /* AT+XACT? response parser */ gboolean mm_xmm_parse_xact_query_response (const gchar *response, MMModemModeCombination *mode_out, GArray **bands_out, GError **error); /* AT+XACT=X command builder */ gchar *mm_xmm_build_xact_set_command (const MMModemModeCombination *mode, const GArray *bands, GError **error); /* Mode to apply when ANY */ MMModemMode mm_xmm_get_modem_mode_any (const GArray *combinations); gboolean mm_xmm_parse_xcesq_query_response (const gchar *response, guint *out_rxlev, guint *out_ber, guint *out_rscp, guint *out_ecn0, guint *out_rsrq, guint *out_rsrp, gint *out_rssnr, GError **error); gboolean mm_xmm_xcesq_response_to_signal_info (const gchar *response, gpointer log_object, MMSignal **out_gsm, MMSignal **out_umts, MMSignal **out_lte, GError **error); /* AT+XLCSLSR=? response parser */ gboolean mm_xmm_parse_xlcslsr_test_response (const gchar *response, gboolean *transport_protocol_invalid_supported, gboolean *transport_protocol_supl_supported, gboolean *standalone_position_mode_supported, gboolean *ms_assisted_based_position_mode_supported, gboolean *loc_response_type_nmea_supported, gboolean *gnss_type_gps_glonass_supported, GError **error); /* AT+XLCSSLP? response parser */ gboolean mm_xmm_parse_xlcsslp_query_response (const gchar *response, gchar **supl_address, GError **error); #endif /* MM_MODEM_HELPERS_XMM_H */ ModemManager-1.23.4-dev/src/plugins/xmm/mm-shared-xmm.c000066400000000000000000001612211456466623000226210ustar00rootroot00000000000000/* -*- 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) 2018 Aleksander Morgado */ #include #include #include #include #define _LIBMM_INSIDE_MM #include #include "mm-log-object.h" #include "mm-iface-modem.h" #include "mm-iface-modem-signal.h" #include "mm-iface-modem-location.h" #include "mm-base-modem.h" #include "mm-base-modem-at.h" #include "mm-shared-xmm.h" #include "mm-modem-helpers-xmm.h" /*****************************************************************************/ /* Private data context */ #define PRIVATE_TAG "shared-xmm-private-tag" static GQuark private_quark; typedef enum { GPS_ENGINE_STATE_OFF, GPS_ENGINE_STATE_STANDALONE, GPS_ENGINE_STATE_AGPS_MSA, GPS_ENGINE_STATE_AGPS_MSB, } GpsEngineState; typedef struct { /* Broadband modem class support */ MMBroadbandModemClass *broadband_modem_class_parent; /* Modem interface support */ GArray *supported_modes; GArray *supported_bands; MMModemMode allowed_modes; /* Location interface support */ MMIfaceModemLocation *iface_modem_location_parent; MMModemLocationSource supported_sources; MMModemLocationSource enabled_sources; GpsEngineState gps_engine_state; MMPortSerialAt *gps_port; GRegex *xlsrstop_regex; GRegex *nmea_regex; /* Asynchronous GPS engine stop task completion */ GTask *pending_gps_engine_stop_task; } Private; static void private_free (Private *priv) { g_assert (!priv->pending_gps_engine_stop_task); g_clear_object (&priv->gps_port); if (priv->supported_modes) g_array_unref (priv->supported_modes); if (priv->supported_bands) g_array_unref (priv->supported_bands); g_regex_unref (priv->xlsrstop_regex); g_regex_unref (priv->nmea_regex); g_slice_free (Private, priv); } static Private * get_private (MMSharedXmm *self) { Private *priv; if (G_UNLIKELY (!private_quark)) private_quark = g_quark_from_static_string (PRIVATE_TAG); priv = g_object_get_qdata (G_OBJECT (self), private_quark); if (!priv) { priv = g_slice_new0 (Private); priv->gps_engine_state = GPS_ENGINE_STATE_OFF; /* Setup regex for URCs */ priv->xlsrstop_regex = g_regex_new ("\\r\\n\\+XLSRSTOP:(.*)\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); priv->nmea_regex = g_regex_new ("(?:\\r\\n)?(?:\\r\\n)?(\\$G.*)\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); /* Setup parent class' MMBroadbandModemClass */ g_assert (MM_SHARED_XMM_GET_INTERFACE (self)->peek_parent_broadband_modem_class); priv->broadband_modem_class_parent = MM_SHARED_XMM_GET_INTERFACE (self)->peek_parent_broadband_modem_class (self); /* Setup parent class' MMIfaceModemLocation */ g_assert (MM_SHARED_XMM_GET_INTERFACE (self)->peek_parent_location_interface); priv->iface_modem_location_parent = MM_SHARED_XMM_GET_INTERFACE (self)->peek_parent_location_interface (self); g_object_set_qdata_full (G_OBJECT (self), private_quark, priv, (GDestroyNotify)private_free); } return priv; } /*****************************************************************************/ /* Supported modes/bands (Modem interface) */ GArray * mm_shared_xmm_load_supported_modes_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { Private *priv; if (!g_task_propagate_boolean (G_TASK (res), error)) return NULL; priv = get_private (MM_SHARED_XMM (self)); g_assert (priv->supported_modes); return g_array_ref (priv->supported_modes); } GArray * mm_shared_xmm_load_supported_bands_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { Private *priv; if (!g_task_propagate_boolean (G_TASK (res), error)) return NULL; priv = get_private (MM_SHARED_XMM (self)); g_assert (priv->supported_bands); return g_array_ref (priv->supported_bands); } static void xact_test_ready (MMBaseModem *self, GAsyncResult *res, GTask *task) { const gchar *response; GError *error = NULL; Private *priv; priv = get_private (MM_SHARED_XMM (self)); response = mm_base_modem_at_command_finish (self, res, &error); if (!response || !mm_xmm_parse_xact_test_response (response, self, &priv->supported_modes, &priv->supported_bands, &error)) g_task_return_error (task, error); else g_task_return_boolean (task, TRUE); g_object_unref (task); } static void common_load_supported_modes_bands (GTask *task) { mm_base_modem_at_command ( MM_BASE_MODEM (g_task_get_source_object (task)), "+XACT=?", 3, TRUE, /* allow caching */ (GAsyncReadyCallback)xact_test_ready, task); } void mm_shared_xmm_load_supported_modes (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; Private *priv; task = g_task_new (self, NULL, callback, user_data); priv = get_private (MM_SHARED_XMM (self)); if (!priv->supported_modes) { common_load_supported_modes_bands (task); return; } g_task_return_boolean (task, TRUE); g_object_unref (task); } void mm_shared_xmm_load_supported_bands (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; Private *priv; task = g_task_new (self, NULL, callback, user_data); priv = get_private (MM_SHARED_XMM (self)); if (!priv->supported_bands) { common_load_supported_modes_bands (task); return; } g_task_return_boolean (task, TRUE); g_object_unref (task); } /*****************************************************************************/ /* Current modes (Modem interface) */ gboolean mm_shared_xmm_load_current_modes_finish (MMIfaceModem *self, GAsyncResult *res, MMModemMode *allowed, MMModemMode *preferred, GError **error) { MMModemModeCombination *result; result = g_task_propagate_pointer (G_TASK (res), error); if (!result) return FALSE; *allowed = result->allowed; *preferred = result->preferred; g_free (result); return TRUE; } static void xact_query_modes_ready (MMBaseModem *self, GAsyncResult *res, GTask *task) { const gchar *response; GError *error = NULL; Private *priv; MMModemModeCombination *result; priv = get_private (MM_SHARED_XMM (self)); result = g_new0 (MMModemModeCombination, 1); response = mm_base_modem_at_command_finish (self, res, &error); if (!response || !mm_xmm_parse_xact_query_response (response, result, NULL, &error)) { priv->allowed_modes = MM_MODEM_MODE_NONE; g_free (result); g_task_return_error (task, error); } else { priv->allowed_modes = result->allowed; g_task_return_pointer (task, result, g_free); } g_object_unref (task); } void mm_shared_xmm_load_current_modes (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; task = g_task_new (self, NULL, callback, user_data); mm_base_modem_at_command ( MM_BASE_MODEM (self), "+XACT?", 3, FALSE, (GAsyncReadyCallback)xact_query_modes_ready, task); } /*****************************************************************************/ /* Current bands (Modem interface) */ GArray * mm_shared_xmm_load_current_bands_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { return (GArray *) g_task_propagate_pointer (G_TASK (res), error); } static void xact_query_bands_ready (MMBaseModem *self, GAsyncResult *res, GTask *task) { const gchar *response; GError *error = NULL; GArray *result = NULL; response = mm_base_modem_at_command_finish (self, res, &error); if (!response || !mm_xmm_parse_xact_query_response (response, NULL, &result, &error)) g_task_return_error (task, error); else g_task_return_pointer (task, result, (GDestroyNotify)g_array_unref); g_object_unref (task); } void mm_shared_xmm_load_current_bands (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; task = g_task_new (self, NULL, callback, user_data); mm_base_modem_at_command ( MM_BASE_MODEM (self), "+XACT?", 3, FALSE, (GAsyncReadyCallback)xact_query_bands_ready, task); } /*****************************************************************************/ /* Set current modes (Modem interface) */ gboolean mm_shared_xmm_set_current_modes_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void xact_set_modes_ready (MMBaseModem *self, GAsyncResult *res, GTask *task) { GError *error = NULL; if (!mm_base_modem_at_command_finish (self, res, &error)) g_task_return_error (task, error); else g_task_return_boolean (task, TRUE); g_object_unref (task); } void mm_shared_xmm_set_current_modes (MMIfaceModem *self, MMModemMode allowed, MMModemMode preferred, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; MMModemModeCombination mode; gchar *command; GError *error = NULL; task = g_task_new (self, NULL, callback, user_data); if (allowed != MM_MODEM_MODE_ANY) { mode.allowed = allowed; mode.preferred = preferred; } else { Private *priv; priv = get_private (MM_SHARED_XMM (self)); mode.allowed = mm_xmm_get_modem_mode_any (priv->supported_modes); mode.preferred = MM_MODEM_MODE_NONE; } command = mm_xmm_build_xact_set_command (&mode, NULL, &error); if (!command) { g_task_return_error (task, error); g_object_unref (task); return; } mm_base_modem_at_command ( MM_BASE_MODEM (self), command, 10, FALSE, (GAsyncReadyCallback)xact_set_modes_ready, task); g_free (command); } /*****************************************************************************/ /* Set current bands (Modem interface) */ gboolean mm_shared_xmm_set_current_bands_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void xact_set_bands_ready (MMBaseModem *self, GAsyncResult *res, GTask *task) { GError *error = NULL; if (!mm_base_modem_at_command_finish (self, res, &error)) g_task_return_error (task, error); else g_task_return_boolean (task, TRUE); g_object_unref (task); } static gchar * validate_and_build_command_set_current_bands (MMSharedXmm *self, const GArray *bands_array, const GArray *supported_modes, MMModemMode allowed_modes, GError **error) { gboolean band_2g_found = FALSE; gboolean band_3g_found = FALSE; gboolean band_4g_found = FALSE; GArray *unapplied_bands; GError *inner_error = NULL; guint i; /* ANY applies only to the currently selected modes */ if (bands_array->len == 1 && g_array_index (bands_array, MMModemBand, 0) == MM_MODEM_BAND_ANY) { MMModemModeCombination mode; MMModemMode unapplied; /* If we are enabling automatic band selection to a mode combination that does not include * all supported modes, warn about it because automatic band selection wouldn't be executed * for the non-selected modes. * * This is a known limitation of the modem firmware. */ unapplied = mm_xmm_get_modem_mode_any (supported_modes) & ~(allowed_modes); if (unapplied != MM_MODEM_MODE_NONE) { g_autofree gchar *str = NULL; str = mm_modem_mode_build_string_from_mask (unapplied); mm_obj_warn (self, "automatic band selection not applied to non-current modes %s", str); } /* Nothing else to validate, go build the command right away */ /* We must create the set command with an explicit set of allowed modes. * We pass NONE as preferred, but that WON'T change the currently selected preferred mode, * it will be ignored when the command is processed as an empty field will be given */ mode.allowed = allowed_modes; mode.preferred = MM_MODEM_MODE_NONE; return mm_xmm_build_xact_set_command (&mode, bands_array, error); } unapplied_bands = g_array_new (FALSE, FALSE, sizeof (MMModemBand)); for (i = 0; i < bands_array->len; i++) { MMModemBand band; band = g_array_index (bands_array, MMModemBand, i); if (mm_common_band_is_eutran (band)) { band_4g_found = TRUE; if (!(allowed_modes & MM_MODEM_MODE_4G)) g_array_append_val (unapplied_bands, band); } if (mm_common_band_is_utran (band)) { band_3g_found = TRUE; if (!(allowed_modes & MM_MODEM_MODE_3G)) g_array_append_val (unapplied_bands, band); } if (mm_common_band_is_gsm (band)) { band_2g_found = TRUE; if (!(allowed_modes & MM_MODEM_MODE_2G)) g_array_append_val (unapplied_bands, band); } } /* If 2G selected, there must be at least one 2G band */ if ((allowed_modes & MM_MODEM_MODE_2G) && !band_2g_found) { inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS, "At least one GSM band is required when 2G mode is allowed"); goto out; } /* If 3G selected, there must be at least one 3G band */ if ((allowed_modes & MM_MODEM_MODE_3G) && !band_3g_found) { inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS, "At least one UTRAN band is required when 3G mode is allowed"); goto out; } /* If 4G selected, there must be at least one 4G band */ if ((allowed_modes & MM_MODEM_MODE_4G) && !band_4g_found) { inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS, "At least one E-UTRAN band is required when 4G mode is allowed"); goto out; } /* Don't try to modify bands for modes that are not enabled */ if (unapplied_bands->len > 0) { gchar *str; str = mm_common_build_bands_string ((const MMModemBand *)(gconstpointer)unapplied_bands->data, unapplied_bands->len); inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS, "Cannot update bands for modes not currently allowed: %s", str); g_free (str); goto out; } out: if (unapplied_bands) g_array_unref (unapplied_bands); if (inner_error) { g_propagate_error (error, inner_error); return NULL; } return mm_xmm_build_xact_set_command (NULL, bands_array, error); } void mm_shared_xmm_set_current_bands (MMIfaceModem *self, GArray *bands_array, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; gchar *command = NULL; GError *error = NULL; Private *priv; task = g_task_new (self, NULL, callback, user_data); /* Setting bands requires additional validation rules based on the * currently selected list of allowed modes */ priv = get_private (MM_SHARED_XMM (self)); if (priv->allowed_modes == MM_MODEM_MODE_NONE) { error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Cannot set bands if allowed modes are unknown"); goto out; } command = validate_and_build_command_set_current_bands (MM_SHARED_XMM (self), bands_array, priv->supported_modes, priv->allowed_modes, &error); out: if (!command) { g_assert (error); g_task_return_error (task, error); g_object_unref (task); return; } mm_base_modem_at_command ( MM_BASE_MODEM (self), command, 10, FALSE, (GAsyncReadyCallback)xact_set_bands_ready, task); g_free (command); } /*****************************************************************************/ /* Power state loading (Modem interface) */ MMModemPowerState mm_shared_xmm_load_power_state_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { guint state; const gchar *response; response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, error); if (!response) return MM_MODEM_POWER_STATE_UNKNOWN; if (!mm_3gpp_parse_cfun_query_response (response, &state, error)) return MM_MODEM_POWER_STATE_UNKNOWN; switch (state) { case 1: return MM_MODEM_POWER_STATE_ON; case 4: return MM_MODEM_POWER_STATE_LOW; default: break; } g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Unknown +CFUN state: %u", state); return MM_MODEM_POWER_STATE_UNKNOWN; } void mm_shared_xmm_load_power_state (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data) { mm_base_modem_at_command (MM_BASE_MODEM (self), "+CFUN?", 3, FALSE, callback, user_data); } /*****************************************************************************/ /* Modem power up/down/off (Modem interface) */ static gboolean common_modem_power_operation_finish (MMSharedXmm *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void power_operation_ready (MMBaseModem *self, GAsyncResult *res, GTask *task) { GError *error = NULL; if (!mm_base_modem_at_command_finish (self, res, &error)) g_task_return_error (task, error); else g_task_return_boolean (task, TRUE); g_object_unref (task); } static void common_modem_power_operation (MMSharedXmm *self, const gchar *command, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; task = g_task_new (self, NULL, callback, user_data); mm_base_modem_at_command (MM_BASE_MODEM (self), command, 30, FALSE, (GAsyncReadyCallback) power_operation_ready, task); } gboolean mm_shared_xmm_reset_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { return common_modem_power_operation_finish (MM_SHARED_XMM (self), res, error); } void mm_shared_xmm_reset (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data) { common_modem_power_operation (MM_SHARED_XMM (self), "+CFUN=16", callback, user_data); } gboolean mm_shared_xmm_power_off_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { return common_modem_power_operation_finish (MM_SHARED_XMM (self), res, error); } void mm_shared_xmm_power_off (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data) { common_modem_power_operation (MM_SHARED_XMM (self), "+CPWROFF", callback, user_data); } gboolean mm_shared_xmm_power_down_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { return common_modem_power_operation_finish (MM_SHARED_XMM (self), res, error); } void mm_shared_xmm_power_down (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data) { common_modem_power_operation (MM_SHARED_XMM (self), "+CFUN=4", callback, user_data); } gboolean mm_shared_xmm_power_up_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { return common_modem_power_operation_finish (MM_SHARED_XMM (self), res, error); } void mm_shared_xmm_power_up (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data) { common_modem_power_operation (MM_SHARED_XMM (self), "+CFUN=1", callback, user_data); } /*****************************************************************************/ /* Check support (Signal interface) */ gboolean mm_shared_xmm_signal_check_support_finish (MMIfaceModemSignal *self, GAsyncResult *res, GError **error) { return !!mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, error); } void mm_shared_xmm_signal_check_support (MMIfaceModemSignal *self, GAsyncReadyCallback callback, gpointer user_data) { mm_base_modem_at_command (MM_BASE_MODEM (self), "+XCESQ=?", 3, FALSE, callback, user_data); } /*****************************************************************************/ /* Load extended signal information (Signal interface) */ gboolean mm_shared_xmm_signal_load_values_finish (MMIfaceModemSignal *self, GAsyncResult *res, MMSignal **cdma, MMSignal **evdo, MMSignal **gsm, MMSignal **umts, MMSignal **lte, MMSignal **nr5g, GError **error) { const gchar *response; response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, error); if (!response || !mm_xmm_xcesq_response_to_signal_info (response, self, gsm, umts, lte, error)) return FALSE; if (cdma) *cdma = NULL; if (evdo) *evdo = NULL; if (nr5g) *nr5g = NULL; return TRUE; } void mm_shared_xmm_signal_load_values (MMIfaceModemSignal *self, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { mm_base_modem_at_command (MM_BASE_MODEM (self), "+XCESQ?", 3, FALSE, callback, user_data); } /*****************************************************************************/ /* Get GPS control port (Location interface) * * This port is an AT port that will also be used for NMEA data. */ static MMPortSerialAt * shared_xmm_get_gps_control_port (MMSharedXmm *self, GError **error) { MMPortSerialAt *gps_port = NULL; gps_port = mm_base_modem_get_port_gps_control (MM_BASE_MODEM (self)); if (!gps_port) { gps_port = mm_base_modem_get_port_secondary (MM_BASE_MODEM (self)); if (!gps_port) gps_port = mm_base_modem_get_port_primary (MM_BASE_MODEM (self)); } if (!gps_port) g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "No valid port found to control GPS"); return gps_port; } /*****************************************************************************/ /* Load capabilities (Location interface) */ MMModemLocationSource mm_shared_xmm_location_load_capabilities_finish (MMIfaceModemLocation *self, GAsyncResult *res, GError **error) { GError *inner_error = NULL; gssize value; value = g_task_propagate_int (G_TASK (res), &inner_error); if (inner_error) { g_propagate_error (error, inner_error); return MM_MODEM_LOCATION_SOURCE_NONE; } return (MMModemLocationSource)value; } static void xlcslsr_test_ready (MMBaseModem *self, GAsyncResult *res, GTask *task) { MMModemLocationSource sources; const gchar *response; GError *error = NULL; Private *priv; gboolean transport_protocol_invalid_supported; gboolean transport_protocol_supl_supported; gboolean standalone_position_mode_supported; gboolean ms_assisted_based_position_mode_supported; gboolean loc_response_type_nmea_supported; gboolean gnss_type_gps_glonass_supported; priv = get_private (MM_SHARED_XMM (self)); /* Recover parent sources */ sources = GPOINTER_TO_UINT (g_task_get_task_data (task)); response = mm_base_modem_at_command_finish (self, res, &error); if (!response || !mm_xmm_parse_xlcslsr_test_response (response, &transport_protocol_invalid_supported, &transport_protocol_supl_supported, &standalone_position_mode_supported, &ms_assisted_based_position_mode_supported, &loc_response_type_nmea_supported, &gnss_type_gps_glonass_supported, &error)) { mm_obj_dbg (self, "XLCSLSR based GPS control unsupported: %s", error->message); g_clear_error (&error); } else if (!transport_protocol_invalid_supported || !standalone_position_mode_supported || !loc_response_type_nmea_supported || !gnss_type_gps_glonass_supported) { mm_obj_dbg (self, "XLCSLSR based GPS control unsupported: protocol invalid %s, standalone %s, nmea %s, gps/glonass %s", transport_protocol_invalid_supported ? "supported" : "unsupported", standalone_position_mode_supported ? "supported" : "unsupported", loc_response_type_nmea_supported ? "supported" : "unsupported", gnss_type_gps_glonass_supported ? "supported" : "unsupported"); } else { mm_obj_dbg (self, "XLCSLSR based GPS control supported"); priv->supported_sources |= (MM_MODEM_LOCATION_SOURCE_GPS_NMEA | MM_MODEM_LOCATION_SOURCE_GPS_RAW); if (transport_protocol_supl_supported && ms_assisted_based_position_mode_supported) { mm_obj_dbg (self, "XLCSLSR based A-GPS control supported"); priv->supported_sources |= (MM_MODEM_LOCATION_SOURCE_AGPS_MSA | MM_MODEM_LOCATION_SOURCE_AGPS_MSB); } else { mm_obj_dbg (self, "XLCSLSR based A-GPS control unsupported: protocol supl %s, ms assisted/based %s", transport_protocol_supl_supported ? "supported" : "unsupported", ms_assisted_based_position_mode_supported ? "supported" : "unsupported"); } sources |= priv->supported_sources; } g_task_return_int (task, sources); g_object_unref (task); } static void run_xlcslsr_test (GTask *task) { mm_base_modem_at_command ( MM_BASE_MODEM (g_task_get_source_object (task)), "+XLCSLSR=?", 3, TRUE, /* allow caching */ (GAsyncReadyCallback)xlcslsr_test_ready, task); } static void parent_load_capabilities_ready (MMIfaceModemLocation *self, GAsyncResult *res, GTask *task) { MMModemLocationSource sources; GError *error = NULL; Private *priv; priv = get_private (MM_SHARED_XMM (self)); sources = priv->iface_modem_location_parent->load_capabilities_finish (self, res, &error); if (error) { g_task_return_error (task, error); g_object_unref (task); return; } /* If parent already supports GPS sources, we won't do anything else */ if (sources & (MM_MODEM_LOCATION_SOURCE_GPS_NMEA | MM_MODEM_LOCATION_SOURCE_GPS_RAW)) { mm_obj_dbg (self, "no need to run XLCSLSR based location gathering"); g_task_return_int (task, sources); g_object_unref (task); return; } /* Cache sources supported by the parent */ g_task_set_task_data (task, GUINT_TO_POINTER (sources), NULL); run_xlcslsr_test (task); } void mm_shared_xmm_location_load_capabilities (MMIfaceModemLocation *self, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; Private *priv; priv = get_private (MM_SHARED_XMM (self)); task = g_task_new (self, NULL, callback, user_data); g_assert (priv->iface_modem_location_parent); if (!priv->iface_modem_location_parent->load_capabilities || !priv->iface_modem_location_parent->load_capabilities_finish) { /* no parent capabilities */ g_task_set_task_data (task, GUINT_TO_POINTER (MM_MODEM_LOCATION_SOURCE_NONE), NULL); run_xlcslsr_test (task); return; } priv->iface_modem_location_parent->load_capabilities (self, (GAsyncReadyCallback)parent_load_capabilities_ready, task); } /*****************************************************************************/ /* NMEA trace processing */ static void nmea_received (MMPortSerialAt *port, GMatchInfo *info, MMSharedXmm *self) { gchar *trace; trace = g_match_info_fetch (info, 1); mm_iface_modem_location_gps_update (MM_IFACE_MODEM_LOCATION (self), trace); g_free (trace); } /*****************************************************************************/ /* GPS engine state selection */ #define GPS_ENGINE_STOP_TIMEOUT_SECS 10 typedef struct { GpsEngineState state; guint engine_stop_timeout_id; } GpsEngineSelectContext; static void gps_engine_select_context_free (GpsEngineSelectContext *ctx) { g_assert (!ctx->engine_stop_timeout_id); g_slice_free (GpsEngineSelectContext, ctx); } static gboolean gps_engine_state_select_finish (MMSharedXmm *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void xlcslsr_ready (MMBaseModem *self, GAsyncResult *res, GTask *task) { GpsEngineSelectContext *ctx; const gchar *response; GError *error = NULL; Private *priv; priv = get_private (MM_SHARED_XMM (self)); ctx = g_task_get_task_data (task); response = mm_base_modem_at_command_full_finish (self, res, &error); if (!response) { g_clear_object (&priv->gps_port); g_task_return_error (task, error); g_object_unref (task); return; } mm_obj_dbg (self, "GPS engine started"); g_assert (priv->gps_port); mm_port_serial_at_add_unsolicited_msg_handler (priv->gps_port, priv->nmea_regex, (MMPortSerialAtUnsolicitedMsgFn)nmea_received, self, NULL); priv->gps_engine_state = ctx->state; g_task_return_boolean (task, TRUE); g_object_unref (task); } static void gps_engine_start (GTask *task) { GpsEngineSelectContext *ctx; MMSharedXmm *self; Private *priv; GError *error = NULL; guint transport_protocol = 0; guint pos_mode = 0; gchar *cmd; self = g_task_get_source_object (task); priv = get_private (self); ctx = g_task_get_task_data (task); g_assert (!priv->gps_port); priv->gps_port = shared_xmm_get_gps_control_port (self, &error); if (!priv->gps_port) { g_task_return_error (task, error); g_object_unref (task); return; } switch (ctx->state) { case GPS_ENGINE_STATE_STANDALONE: transport_protocol = 2; pos_mode = 3; break; case GPS_ENGINE_STATE_AGPS_MSB: transport_protocol = 1; pos_mode = 1; break; case GPS_ENGINE_STATE_AGPS_MSA: transport_protocol = 1; pos_mode = 2; break; case GPS_ENGINE_STATE_OFF: default: g_assert_not_reached (); break; } mm_obj_dbg (self, "starting GPS engine..."); /* * AT+XLCSLSR * transport_protocol: 2 (invalid) or 1 (supl) * pos_mode: 3 (standalone), 1 (msb) or 2 (msa) * client_id: * client_id_type: * mlc_number: * mlc_number_type: * interval: 1 (seconds) * service_type_id: * pseudonym_indicator: * loc_response_type: 1 (NMEA strings) * nmea_mask: 118 (01110110: GGA,GSA,GSV,RMC,VTG) * gnss_type: 0 (GPS or GLONASS) */ g_assert (priv->gps_port); cmd = g_strdup_printf ("AT+XLCSLSR=%u,%u,,,,,1,,,1,118,0", transport_protocol, pos_mode); mm_base_modem_at_command_full (MM_BASE_MODEM (self), priv->gps_port, cmd, 3, FALSE, FALSE, /* raw */ NULL, /* cancellable */ (GAsyncReadyCallback)xlcslsr_ready, task); g_free (cmd); } static GTask * recover_pending_gps_engine_stop_task (Private *priv) { GTask *task; GpsEngineSelectContext *ctx; /* We're taking over full ownership of the GTask at this point. */ if (!priv->pending_gps_engine_stop_task) return NULL; task = g_steal_pointer (&priv->pending_gps_engine_stop_task); ctx = g_task_get_task_data (task); /* remove timeout */ if (ctx->engine_stop_timeout_id) { g_source_remove (ctx->engine_stop_timeout_id); ctx->engine_stop_timeout_id = 0; } /* disable urc handling */ mm_port_serial_at_add_unsolicited_msg_handler ( priv->gps_port, priv->xlsrstop_regex, NULL, NULL, NULL); return task; } static void gps_engine_stopped (GTask *task) { MMSharedXmm *self; GpsEngineSelectContext *ctx; Private *priv; self = g_task_get_source_object (task); priv = get_private (self); ctx = g_task_get_task_data (task); g_assert (priv->gps_port); mm_port_serial_at_add_unsolicited_msg_handler ( priv->gps_port, priv->nmea_regex, NULL, NULL, NULL); g_clear_object (&priv->gps_port); priv->gps_engine_state = GPS_ENGINE_STATE_OFF; /* If already reached requested state, we're done */ if (ctx->state == priv->gps_engine_state) { /* If we had an error when requesting this specific state, report it */ g_task_return_boolean (task, TRUE); g_object_unref (task); return; } /* Otherwise, start with new state */ gps_engine_start (task); } static gboolean xlsrstop_urc_timeout (MMSharedXmm *self) { GTask *task; Private *priv; priv = get_private (self); task = recover_pending_gps_engine_stop_task (priv); g_assert (task); mm_obj_dbg (self, "timed out waiting for full GPS engine stop report, assuming stopped..."); gps_engine_stopped (task); return G_SOURCE_REMOVE; } static void xlsrstop_urc_received (MMPortSerialAt *port, GMatchInfo *info, MMSharedXmm *self) { GTask *task; Private *priv; priv = get_private (self); task = recover_pending_gps_engine_stop_task (priv); g_assert (task); mm_obj_dbg (self, "GPS engine fully stopped"); gps_engine_stopped (task); } static void xlsrstop_ready (MMBaseModem *self, GAsyncResult *res) { g_autoptr(GError) error = NULL; if (!mm_base_modem_at_command_full_finish (self, res, &error)) { Private *priv; GTask *task; mm_obj_dbg (self, "GPS engine stop request failed: %s", error->message); priv = get_private (MM_SHARED_XMM (self)); task = recover_pending_gps_engine_stop_task (priv); if (task) { g_task_return_error (task, g_steal_pointer (&error)); g_object_unref (task); } return; } mm_obj_dbg (self, "GPS engine stop request accepted"); } static void gps_engine_stop (GTask *task) { MMSharedXmm *self; Private *priv; GpsEngineSelectContext *ctx; self = g_task_get_source_object (task); priv = get_private (self); ctx = g_task_get_task_data (task); g_assert (priv->gps_port); /* After a +XLSRSTOP command the modem will reply OK to report that the stop * request has been received, but at this point the engine may still be on. * We must wait for the additional +XLSRSTOP URC to tell us that the engine * is really off before going on. * * We do this by setting up a temporary regex match for the URC during this * operation, and also by configuring a timeout so that we don't wait forever * for the URC. * * The initial +XLSRSTOP response will be ignored unless an error is being * reported. * * The operation task is kept in private info because it will be shared by all * the possible paths that may complete it (response, URC, timeout). */ if (priv->pending_gps_engine_stop_task) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_WRONG_STATE, "An engine stop task is already ongoing"); g_object_unref (task); return; } priv->pending_gps_engine_stop_task = task; mm_obj_dbg (self, "launching GPS engine stop operation..."); ctx->engine_stop_timeout_id = g_timeout_add_seconds (GPS_ENGINE_STOP_TIMEOUT_SECS, (GSourceFunc) xlsrstop_urc_timeout, self); mm_port_serial_at_add_unsolicited_msg_handler ( priv->gps_port, priv->xlsrstop_regex, (MMPortSerialAtUnsolicitedMsgFn)xlsrstop_urc_received, self, NULL); mm_base_modem_at_command_full (MM_BASE_MODEM (self), priv->gps_port, "+XLSRSTOP", 3, FALSE, FALSE, /* raw */ NULL, /* cancellable */ (GAsyncReadyCallback)xlsrstop_ready, NULL); } static void gps_engine_state_select (MMSharedXmm *self, GpsEngineState state, GAsyncReadyCallback callback, gpointer user_data) { GpsEngineSelectContext *ctx; GTask *task; Private *priv; priv = get_private (self); task = g_task_new (self, NULL, callback, user_data); ctx = g_slice_new0 (GpsEngineSelectContext); ctx->state = state; g_task_set_task_data (task, ctx, (GDestroyNotify)gps_engine_select_context_free); /* If already in the requested state, we're done */ if (state == priv->gps_engine_state) { g_task_return_boolean (task, TRUE); g_object_unref (task); return; } /* If states are different we always STOP first */ if (priv->gps_engine_state != GPS_ENGINE_STATE_OFF) { gps_engine_stop (task); return; } /* If GPS already stopped, go on to START right away */ g_assert (state != GPS_ENGINE_STATE_OFF); gps_engine_start (task); } static GpsEngineState gps_engine_state_get_expected (MMModemLocationSource sources) { /* If at lease one of GPS nmea/raw sources enabled, engine started */ if (sources & (MM_MODEM_LOCATION_SOURCE_GPS_NMEA | MM_MODEM_LOCATION_SOURCE_GPS_RAW)) { /* If MSA A-GPS is enabled, MSA mode */ if (sources & MM_MODEM_LOCATION_SOURCE_AGPS_MSA) return GPS_ENGINE_STATE_AGPS_MSA; /* If MSB A-GPS is enabled, MSB mode */ if (sources & MM_MODEM_LOCATION_SOURCE_AGPS_MSB) return GPS_ENGINE_STATE_AGPS_MSB; /* Otherwise, STANDALONE */ return GPS_ENGINE_STATE_STANDALONE; } /* If no GPS nmea/raw sources enabled, engine stopped */ return GPS_ENGINE_STATE_OFF; } /*****************************************************************************/ /* Disable location gathering (Location interface) */ gboolean mm_shared_xmm_disable_location_gathering_finish (MMIfaceModemLocation *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void disable_gps_engine_state_select_ready (MMSharedXmm *self, GAsyncResult *res, GTask *task) { MMModemLocationSource source; GError *error = NULL; Private *priv; priv = get_private (MM_SHARED_XMM (self)); if (!gps_engine_state_select_finish (self, res, &error)) { g_task_return_error (task, error); g_object_unref (task); return; } source = GPOINTER_TO_UINT (g_task_get_task_data (task)); priv->enabled_sources &= ~source; g_task_return_boolean (task, TRUE); g_object_unref (task); } static void parent_disable_location_gathering_ready (MMIfaceModemLocation *self, GAsyncResult *res, GTask *task) { GError *error = NULL; Private *priv; priv = get_private (MM_SHARED_XMM (self)); g_assert (priv->iface_modem_location_parent); if (!priv->iface_modem_location_parent->disable_location_gathering_finish (self, res, &error)) g_task_return_error (task, error); else g_task_return_boolean (task, TRUE); g_object_unref (task); } void mm_shared_xmm_disable_location_gathering (MMIfaceModemLocation *self, MMModemLocationSource source, GAsyncReadyCallback callback, gpointer user_data) { Private *priv; GTask *task; task = g_task_new (self, NULL, callback, user_data); g_task_set_task_data (task, GUINT_TO_POINTER (source), NULL); priv = get_private (MM_SHARED_XMM (self)); g_assert (priv->iface_modem_location_parent); /* Only consider request if it applies to one of the sources we are * supporting, otherwise run parent disable */ if (!(priv->supported_sources & source)) { /* If disabling implemented by the parent, run it. */ if (priv->iface_modem_location_parent->disable_location_gathering && priv->iface_modem_location_parent->disable_location_gathering_finish) { priv->iface_modem_location_parent->disable_location_gathering (self, source, (GAsyncReadyCallback)parent_disable_location_gathering_ready, task); return; } /* Otherwise, we're done */ g_task_return_boolean (task, TRUE); g_object_unref (task); return; } /* We only expect GPS sources here */ g_assert (source & (MM_MODEM_LOCATION_SOURCE_GPS_NMEA | MM_MODEM_LOCATION_SOURCE_GPS_RAW | MM_MODEM_LOCATION_SOURCE_AGPS_MSA | MM_MODEM_LOCATION_SOURCE_AGPS_MSB)); /* Update engine based on the expected sources */ gps_engine_state_select (MM_SHARED_XMM (self), gps_engine_state_get_expected (priv->enabled_sources & ~source), (GAsyncReadyCallback) disable_gps_engine_state_select_ready, task); } /*****************************************************************************/ /* Enable location gathering (Location interface) */ gboolean mm_shared_xmm_enable_location_gathering_finish (MMIfaceModemLocation *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void enable_gps_engine_state_select_ready (MMSharedXmm *self, GAsyncResult *res, GTask *task) { MMModemLocationSource source; GError *error = NULL; Private *priv; priv = get_private (MM_SHARED_XMM (self)); if (!gps_engine_state_select_finish (self, res, &error)) { g_task_return_error (task, error); g_object_unref (task); return; } source = GPOINTER_TO_UINT (g_task_get_task_data (task)); priv->enabled_sources |= source; g_task_return_boolean (task, TRUE); g_object_unref (task); } static void parent_enable_location_gathering_ready (MMIfaceModemLocation *self, GAsyncResult *res, GTask *task) { GError *error = NULL; Private *priv; priv = get_private (MM_SHARED_XMM (self)); g_assert (priv->iface_modem_location_parent); if (!priv->iface_modem_location_parent->enable_location_gathering_finish (self, res, &error)) g_task_return_error (task, error); else g_task_return_boolean (task, TRUE); g_object_unref (task); } void mm_shared_xmm_enable_location_gathering (MMIfaceModemLocation *self, MMModemLocationSource source, GAsyncReadyCallback callback, gpointer user_data) { Private *priv; GTask *task; task = g_task_new (self, NULL, callback, user_data); g_task_set_task_data (task, GUINT_TO_POINTER (source), NULL); priv = get_private (MM_SHARED_XMM (self)); g_assert (priv->iface_modem_location_parent); /* Only consider request if it applies to one of the sources we are * supporting, otherwise run parent enable */ if (priv->iface_modem_location_parent->enable_location_gathering && priv->iface_modem_location_parent->enable_location_gathering_finish && !(priv->supported_sources & source)) { priv->iface_modem_location_parent->enable_location_gathering (self, source, (GAsyncReadyCallback)parent_enable_location_gathering_ready, task); return; } /* We only expect GPS sources here */ g_assert (source & (MM_MODEM_LOCATION_SOURCE_GPS_NMEA | MM_MODEM_LOCATION_SOURCE_GPS_RAW | MM_MODEM_LOCATION_SOURCE_AGPS_MSA | MM_MODEM_LOCATION_SOURCE_AGPS_MSB)); /* Update engine based on the expected sources */ gps_engine_state_select (MM_SHARED_XMM (self), gps_engine_state_get_expected (priv->enabled_sources | source), (GAsyncReadyCallback) enable_gps_engine_state_select_ready, task); } /*****************************************************************************/ /* Location: Load SUPL server */ gchar * mm_shared_xmm_location_load_supl_server_finish (MMIfaceModemLocation *self, GAsyncResult *res, GError **error) { return g_task_propagate_pointer (G_TASK (res), error); } static void xlcsslp_query_ready (MMBaseModem *self, GAsyncResult *res, GTask *task) { const gchar *response; GError *error = NULL; gchar *supl_address; response = mm_base_modem_at_command_finish (self, res, &error); if (!response || !mm_xmm_parse_xlcsslp_query_response (response, &supl_address, &error)) g_task_return_error (task, error); else g_task_return_pointer (task, supl_address, g_free); g_object_unref (task); } void mm_shared_xmm_location_load_supl_server (MMIfaceModemLocation *self, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; task = g_task_new (self, NULL, callback, user_data); mm_base_modem_at_command (MM_BASE_MODEM (self), "+XLCSSLP?", 3, FALSE, (GAsyncReadyCallback)xlcsslp_query_ready, task); } /*****************************************************************************/ gboolean mm_shared_xmm_location_set_supl_server_finish (MMIfaceModemLocation *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void xlcsslp_set_ready (MMBaseModem *self, GAsyncResult *res, GTask *task) { GError *error = NULL; if (!mm_base_modem_at_command_finish (self, res, &error)) g_task_return_error (task, error); else g_task_return_boolean (task, TRUE); g_object_unref (task); } void mm_shared_xmm_location_set_supl_server (MMIfaceModemLocation *self, const gchar *supl, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; gchar *cmd = NULL; gchar *fqdn = NULL; guint32 ip; guint16 port; task = g_task_new (self, NULL, callback, user_data); mm_parse_supl_address (supl, &fqdn, &ip, &port, NULL); g_assert (port); if (fqdn) cmd = g_strdup_printf ("+XLCSSLP=1,%s,%u", fqdn, port); else if (ip) { struct in_addr a = { .s_addr = ip }; gchar buf[INET_ADDRSTRLEN + 1] = { 0 }; /* we got 'ip' from inet_pton(), so this next step should always succeed */ g_assert (inet_ntop (AF_INET, &a, buf, sizeof (buf) - 1)); cmd = g_strdup_printf ("+XLCSSLP=0,%s,%u", buf, port); } else g_assert_not_reached (); mm_base_modem_at_command (MM_BASE_MODEM (self), cmd, 3, FALSE, (GAsyncReadyCallback)xlcsslp_set_ready, task); g_free (cmd); g_free (fqdn); } /*****************************************************************************/ void mm_shared_xmm_setup_ports (MMBroadbandModem *self) { Private *priv; g_autoptr(MMPortSerialAt) gps_port = NULL; priv = get_private (MM_SHARED_XMM (self)); g_assert (priv->broadband_modem_class_parent); g_assert (priv->broadband_modem_class_parent->setup_ports); /* Parent setup first always */ priv->broadband_modem_class_parent->setup_ports (self); /* Then, setup the GPS port */ gps_port = shared_xmm_get_gps_control_port (MM_SHARED_XMM (self), NULL); if (gps_port) { /* After running AT+XLSRSTOP we may get an unsolicited response * reporting its status, we just ignore it. */ mm_port_serial_at_add_unsolicited_msg_handler ( gps_port, priv->xlsrstop_regex, NULL, NULL, NULL); /* make sure GPS is stopped in case it was left enabled */ mm_base_modem_at_command_full (MM_BASE_MODEM (self), gps_port, "+XLSRSTOP", 3, FALSE, FALSE, NULL, NULL, NULL); } } /*****************************************************************************/ static void shared_xmm_init (gpointer g_iface) { } GType mm_shared_xmm_get_type (void) { static GType shared_xmm_type = 0; if (!G_UNLIKELY (shared_xmm_type)) { static const GTypeInfo info = { sizeof (MMSharedXmm), /* class_size */ shared_xmm_init, /* base_init */ NULL, /* base_finalize */ }; shared_xmm_type = g_type_register_static (G_TYPE_INTERFACE, "MMSharedXmm", &info, 0); g_type_interface_add_prerequisite (shared_xmm_type, MM_TYPE_IFACE_MODEM); g_type_interface_add_prerequisite (shared_xmm_type, MM_TYPE_IFACE_MODEM_LOCATION); } return shared_xmm_type; } ModemManager-1.23.4-dev/src/plugins/xmm/mm-shared-xmm.h000066400000000000000000000314661456466623000226350ustar00rootroot00000000000000/* -*- 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) 2018 Aleksander Morgado */ #ifndef MM_SHARED_XMM_H #define MM_SHARED_XMM_H #include #include #define _LIBMM_INSIDE_MM #include #include "mm-broadband-modem.h" #include "mm-iface-modem.h" #include "mm-iface-modem-signal.h" #include "mm-iface-modem-location.h" #define MM_TYPE_SHARED_XMM (mm_shared_xmm_get_type ()) #define MM_SHARED_XMM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_SHARED_XMM, MMSharedXmm)) #define MM_IS_SHARED_XMM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_SHARED_XMM)) #define MM_SHARED_XMM_GET_INTERFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE ((obj), MM_TYPE_SHARED_XMM, MMSharedXmm)) typedef struct _MMSharedXmm MMSharedXmm; struct _MMSharedXmm { GTypeInterface g_iface; /* Peek broadband modem class of the parent class of the object */ MMBroadbandModemClass * (* peek_parent_broadband_modem_class) (MMSharedXmm *self); /* Peek location interface of the parent class of the object */ MMIfaceModemLocation * (* peek_parent_location_interface) (MMSharedXmm *self); }; GType mm_shared_xmm_get_type (void); /* Shared XMM device setup */ void mm_shared_xmm_setup_ports (MMBroadbandModem *self); /* Shared XMM device management support */ void mm_shared_xmm_load_supported_modes (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data); GArray *mm_shared_xmm_load_supported_modes_finish (MMIfaceModem *self, GAsyncResult *res, GError **error); void mm_shared_xmm_load_current_modes (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data); gboolean mm_shared_xmm_load_current_modes_finish (MMIfaceModem *self, GAsyncResult *res, MMModemMode *allowed, MMModemMode *preferred, GError **error); void mm_shared_xmm_set_current_modes (MMIfaceModem *self, MMModemMode allowed, MMModemMode preferred, GAsyncReadyCallback callback, gpointer user_data); gboolean mm_shared_xmm_set_current_modes_finish (MMIfaceModem *self, GAsyncResult *res, GError **error); void mm_shared_xmm_load_supported_bands (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data); GArray *mm_shared_xmm_load_supported_bands_finish (MMIfaceModem *self, GAsyncResult *res, GError **error); void mm_shared_xmm_load_current_bands (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data); GArray *mm_shared_xmm_load_current_bands_finish (MMIfaceModem *self, GAsyncResult *res, GError **error); void mm_shared_xmm_set_current_bands (MMIfaceModem *self, GArray *bands_array, GAsyncReadyCallback callback, gpointer user_data); gboolean mm_shared_xmm_set_current_bands_finish (MMIfaceModem *self, GAsyncResult *res, GError **error); void mm_shared_xmm_load_power_state (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data); MMModemPowerState mm_shared_xmm_load_power_state_finish (MMIfaceModem *self, GAsyncResult *res, GError **error); void mm_shared_xmm_power_up (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data); gboolean mm_shared_xmm_power_up_finish (MMIfaceModem *self, GAsyncResult *res, GError **error); void mm_shared_xmm_power_down (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data); gboolean mm_shared_xmm_power_down_finish (MMIfaceModem *self, GAsyncResult *res, GError **error); void mm_shared_xmm_power_off (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data); gboolean mm_shared_xmm_power_off_finish (MMIfaceModem *self, GAsyncResult *res, GError **error); void mm_shared_xmm_reset (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data); gboolean mm_shared_xmm_reset_finish (MMIfaceModem *self, GAsyncResult *res, GError **error); gboolean mm_shared_xmm_signal_check_support_finish (MMIfaceModemSignal *self, GAsyncResult *res, GError **error); void mm_shared_xmm_signal_check_support (MMIfaceModemSignal *self, GAsyncReadyCallback callback, gpointer user_data); gboolean mm_shared_xmm_signal_load_values_finish (MMIfaceModemSignal *self, GAsyncResult *res, MMSignal **cdma, MMSignal **evdo, MMSignal **gsm, MMSignal **umts, MMSignal **lte, MMSignal **nr5g, GError **error); void mm_shared_xmm_signal_load_values (MMIfaceModemSignal *self, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); void mm_shared_xmm_location_load_capabilities (MMIfaceModemLocation *self, GAsyncReadyCallback callback, gpointer user_data); MMModemLocationSource mm_shared_xmm_location_load_capabilities_finish (MMIfaceModemLocation *self, GAsyncResult *res, GError **error); void mm_shared_xmm_enable_location_gathering (MMIfaceModemLocation *self, MMModemLocationSource source, GAsyncReadyCallback callback, gpointer user_data); gboolean mm_shared_xmm_enable_location_gathering_finish (MMIfaceModemLocation *self, GAsyncResult *res, GError **error); void mm_shared_xmm_disable_location_gathering (MMIfaceModemLocation *self, MMModemLocationSource source, GAsyncReadyCallback callback, gpointer user_data); gboolean mm_shared_xmm_disable_location_gathering_finish (MMIfaceModemLocation *self, GAsyncResult *res, GError **error); void mm_shared_xmm_location_load_supl_server (MMIfaceModemLocation *self, GAsyncReadyCallback callback, gpointer user_data); gchar *mm_shared_xmm_location_load_supl_server_finish (MMIfaceModemLocation *self, GAsyncResult *res, GError **error); void mm_shared_xmm_location_set_supl_server (MMIfaceModemLocation *self, const gchar *supl, GAsyncReadyCallback callback, gpointer user_data); gboolean mm_shared_xmm_location_set_supl_server_finish (MMIfaceModemLocation *self, GAsyncResult *res, GError **error); #endif /* MM_SHARED_XMM_H */ ModemManager-1.23.4-dev/src/plugins/xmm/mm-shared.c000066400000000000000000000013101456466623000220120ustar00rootroot00000000000000/* -*- 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) 2019 Aleksander Morgado */ #include "mm-shared-common.h" MM_DEFINE_SHARED (xmm) ModemManager-1.23.4-dev/src/plugins/xmm/tests/000077500000000000000000000000001456466623000211405ustar00rootroot00000000000000ModemManager-1.23.4-dev/src/plugins/xmm/tests/test-modem-helpers-xmm.c000066400000000000000000000760041456466623000256300ustar00rootroot00000000000000/* -*- 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) 2018 Aleksander Morgado */ #include #include #include #include #include #define _LIBMM_INSIDE_MM #include #include "mm-log-test.h" #include "mm-modem-helpers.h" #include "mm-modem-helpers-xmm.h" #define g_assert_cmpfloat_tolerance(val1, val2, tolerance) \ g_assert_cmpfloat (fabs (val1 - val2), <, tolerance) /*****************************************************************************/ /* Test XACT=? responses */ static void validate_xact_test_response (const gchar *response, const MMModemModeCombination *expected_modes, guint n_expected_modes, const MMModemBand *expected_bands, guint n_expected_bands) { GError *error = NULL; GArray *modes = NULL; GArray *bands = NULL; gboolean ret; guint i; ret = mm_xmm_parse_xact_test_response (response, NULL, &modes, &bands, &error); g_assert_no_error (error); g_assert (ret); g_assert_cmpuint (modes->len, ==, n_expected_modes); for (i = 0; i < modes->len; i++) { MMModemModeCombination mode; guint j; gboolean found = FALSE; mode = g_array_index (modes, MMModemModeCombination, i); for (j = 0; !found && j < n_expected_modes; j++) found = (mode.allowed == expected_modes[j].allowed && mode.preferred == expected_modes[j].preferred); g_assert (found); } g_array_unref (modes); g_assert_cmpuint (bands->len, ==, n_expected_bands); for (i = 0; i < bands->len; i++) { MMModemBand band; guint j; gboolean found = FALSE; band = g_array_index (bands, MMModemBand, i); for (j = 0; !found && j < n_expected_bands; j++) found = (band == expected_bands[j]); g_assert (found); } g_array_unref (bands); } static void test_xact_test_4g_only (void) { const gchar *response = "+XACT: " "(0-6),(0-2),0," "101,102,103,104,105,107,108,111,112,113,117,118,119,120,121,126,128,129,130,138,139,140,141,166"; static const MMModemModeCombination expected_modes[] = { { MM_MODEM_MODE_4G, MM_MODEM_MODE_NONE }, }; static const MMModemBand expected_bands[] = { MM_MODEM_BAND_EUTRAN_1, MM_MODEM_BAND_EUTRAN_2, MM_MODEM_BAND_EUTRAN_3, MM_MODEM_BAND_EUTRAN_4, MM_MODEM_BAND_EUTRAN_5, MM_MODEM_BAND_EUTRAN_7, MM_MODEM_BAND_EUTRAN_8, MM_MODEM_BAND_EUTRAN_11, MM_MODEM_BAND_EUTRAN_12, MM_MODEM_BAND_EUTRAN_13, MM_MODEM_BAND_EUTRAN_17, MM_MODEM_BAND_EUTRAN_18, MM_MODEM_BAND_EUTRAN_19, MM_MODEM_BAND_EUTRAN_20, MM_MODEM_BAND_EUTRAN_21, MM_MODEM_BAND_EUTRAN_26, MM_MODEM_BAND_EUTRAN_28, MM_MODEM_BAND_EUTRAN_29, MM_MODEM_BAND_EUTRAN_30, MM_MODEM_BAND_EUTRAN_38, MM_MODEM_BAND_EUTRAN_39, MM_MODEM_BAND_EUTRAN_40, MM_MODEM_BAND_EUTRAN_41, MM_MODEM_BAND_EUTRAN_66 }; /* NOTE: 2G and 3G modes are reported in XACT but no 2G or 3G frequencies supported */ validate_xact_test_response (response, expected_modes, G_N_ELEMENTS (expected_modes), expected_bands, G_N_ELEMENTS (expected_bands)); } static void test_xact_test_3g_4g (void) { const gchar *response = "+XACT: " "(0-6),(0-2),0," "1,2,4,5,8," "101,102,103,104,105,107,108,111,112,113,117,118,119,120,121,126,128,129,130,138,139,140,141,166"; static const MMModemModeCombination expected_modes[] = { { MM_MODEM_MODE_3G, MM_MODEM_MODE_NONE }, { MM_MODEM_MODE_4G, MM_MODEM_MODE_NONE }, { MM_MODEM_MODE_3G | MM_MODEM_MODE_4G, MM_MODEM_MODE_NONE }, { MM_MODEM_MODE_3G | MM_MODEM_MODE_4G, MM_MODEM_MODE_3G }, { MM_MODEM_MODE_3G | MM_MODEM_MODE_4G, MM_MODEM_MODE_4G }, }; static const MMModemBand expected_bands[] = { MM_MODEM_BAND_UTRAN_1, MM_MODEM_BAND_UTRAN_2, MM_MODEM_BAND_UTRAN_4, MM_MODEM_BAND_UTRAN_5, MM_MODEM_BAND_UTRAN_8, MM_MODEM_BAND_EUTRAN_1, MM_MODEM_BAND_EUTRAN_2, MM_MODEM_BAND_EUTRAN_3, MM_MODEM_BAND_EUTRAN_4, MM_MODEM_BAND_EUTRAN_5, MM_MODEM_BAND_EUTRAN_7, MM_MODEM_BAND_EUTRAN_8, MM_MODEM_BAND_EUTRAN_11, MM_MODEM_BAND_EUTRAN_12, MM_MODEM_BAND_EUTRAN_13, MM_MODEM_BAND_EUTRAN_17, MM_MODEM_BAND_EUTRAN_18, MM_MODEM_BAND_EUTRAN_19, MM_MODEM_BAND_EUTRAN_20, MM_MODEM_BAND_EUTRAN_21, MM_MODEM_BAND_EUTRAN_26, MM_MODEM_BAND_EUTRAN_28, MM_MODEM_BAND_EUTRAN_29, MM_MODEM_BAND_EUTRAN_30, MM_MODEM_BAND_EUTRAN_38, MM_MODEM_BAND_EUTRAN_39, MM_MODEM_BAND_EUTRAN_40, MM_MODEM_BAND_EUTRAN_41, MM_MODEM_BAND_EUTRAN_66 }; /* NOTE: 2G modes are reported in XACT but no 2G frequencies supported */ validate_xact_test_response (response, expected_modes, G_N_ELEMENTS (expected_modes), expected_bands, G_N_ELEMENTS (expected_bands)); } static void test_xact_test_2g_3g_4g (void) { const gchar *response = "+XACT: " "(0-6),(0-2),0," "900,1800,1900,850," "1,2,4,5,8," "101,102,103,104,105,107,108,111,112,113,117,118,119,120,121,126,128,129,130,138,139,140,141,166"; static const MMModemModeCombination expected_modes[] = { { MM_MODEM_MODE_2G, MM_MODEM_MODE_NONE }, { MM_MODEM_MODE_3G, MM_MODEM_MODE_NONE }, { MM_MODEM_MODE_4G, MM_MODEM_MODE_NONE }, { MM_MODEM_MODE_2G | MM_MODEM_MODE_3G, MM_MODEM_MODE_NONE }, { MM_MODEM_MODE_2G | MM_MODEM_MODE_3G, MM_MODEM_MODE_2G }, { MM_MODEM_MODE_2G | MM_MODEM_MODE_3G, MM_MODEM_MODE_3G }, { MM_MODEM_MODE_2G | MM_MODEM_MODE_4G, MM_MODEM_MODE_NONE }, { MM_MODEM_MODE_2G | MM_MODEM_MODE_4G, MM_MODEM_MODE_2G }, { MM_MODEM_MODE_2G | MM_MODEM_MODE_4G, MM_MODEM_MODE_4G }, { MM_MODEM_MODE_3G | MM_MODEM_MODE_4G, MM_MODEM_MODE_NONE }, { MM_MODEM_MODE_3G | MM_MODEM_MODE_4G, MM_MODEM_MODE_3G }, { MM_MODEM_MODE_3G | MM_MODEM_MODE_4G, MM_MODEM_MODE_4G }, { MM_MODEM_MODE_2G | MM_MODEM_MODE_3G | MM_MODEM_MODE_4G, MM_MODEM_MODE_NONE }, { MM_MODEM_MODE_2G | MM_MODEM_MODE_3G | MM_MODEM_MODE_4G, MM_MODEM_MODE_2G }, { MM_MODEM_MODE_2G | MM_MODEM_MODE_3G | MM_MODEM_MODE_4G, MM_MODEM_MODE_3G }, { MM_MODEM_MODE_2G | MM_MODEM_MODE_3G | MM_MODEM_MODE_4G, MM_MODEM_MODE_4G }, }; static const MMModemBand expected_bands[] = { MM_MODEM_BAND_EGSM, MM_MODEM_BAND_DCS, MM_MODEM_BAND_PCS, MM_MODEM_BAND_G850, MM_MODEM_BAND_UTRAN_1, MM_MODEM_BAND_UTRAN_2, MM_MODEM_BAND_UTRAN_4, MM_MODEM_BAND_UTRAN_5, MM_MODEM_BAND_UTRAN_8, MM_MODEM_BAND_EUTRAN_1, MM_MODEM_BAND_EUTRAN_2, MM_MODEM_BAND_EUTRAN_3, MM_MODEM_BAND_EUTRAN_4, MM_MODEM_BAND_EUTRAN_5, MM_MODEM_BAND_EUTRAN_7, MM_MODEM_BAND_EUTRAN_8, MM_MODEM_BAND_EUTRAN_11, MM_MODEM_BAND_EUTRAN_12, MM_MODEM_BAND_EUTRAN_13, MM_MODEM_BAND_EUTRAN_17, MM_MODEM_BAND_EUTRAN_18, MM_MODEM_BAND_EUTRAN_19, MM_MODEM_BAND_EUTRAN_20, MM_MODEM_BAND_EUTRAN_21, MM_MODEM_BAND_EUTRAN_26, MM_MODEM_BAND_EUTRAN_28, MM_MODEM_BAND_EUTRAN_29, MM_MODEM_BAND_EUTRAN_30, MM_MODEM_BAND_EUTRAN_38, MM_MODEM_BAND_EUTRAN_39, MM_MODEM_BAND_EUTRAN_40, MM_MODEM_BAND_EUTRAN_41, MM_MODEM_BAND_EUTRAN_66 }; validate_xact_test_response (response, expected_modes, G_N_ELEMENTS (expected_modes), expected_bands, G_N_ELEMENTS (expected_bands)); } /*****************************************************************************/ /* Test XACT? responses */ static void validate_xact_query_response (const gchar *response, const MMModemModeCombination *expected_mode, const MMModemBand *expected_bands, guint n_expected_bands) { GError *error = NULL; GArray *bands = NULL; gboolean ret; guint i; MMModemModeCombination mode = { .allowed = MM_MODEM_MODE_NONE, .preferred = MM_MODEM_MODE_NONE, }; ret = mm_xmm_parse_xact_query_response (response, &mode, &bands, &error); g_assert_no_error (error); g_assert (ret); g_assert_cmpuint (mode.allowed, ==, expected_mode->allowed); g_assert_cmpuint (mode.preferred, ==, expected_mode->preferred); g_assert_cmpuint (bands->len, ==, n_expected_bands); for (i = 0; i < bands->len; i++) { MMModemBand band; guint j; gboolean found = FALSE; band = g_array_index (bands, MMModemBand, i); for (j = 0; !found && j < n_expected_bands; j++) found = (band == expected_bands[j]); g_assert (found); } g_array_unref (bands); } static void test_xact_query_3g_only (void) { const gchar *response = "+XACT: " "1,1,," "1,2,4,5,8," "101,102,103,104,105,107,108,111,112,113,117,118,119,120,121,126,128,129,130,138,139,140,141,166"; static const MMModemModeCombination expected_mode = { .allowed = MM_MODEM_MODE_3G, .preferred = MM_MODEM_MODE_NONE }; static const MMModemBand expected_bands[] = { MM_MODEM_BAND_UTRAN_1, MM_MODEM_BAND_UTRAN_2, MM_MODEM_BAND_UTRAN_4, MM_MODEM_BAND_UTRAN_5, MM_MODEM_BAND_UTRAN_8, MM_MODEM_BAND_EUTRAN_1, MM_MODEM_BAND_EUTRAN_2, MM_MODEM_BAND_EUTRAN_3, MM_MODEM_BAND_EUTRAN_4, MM_MODEM_BAND_EUTRAN_5, MM_MODEM_BAND_EUTRAN_7, MM_MODEM_BAND_EUTRAN_8, MM_MODEM_BAND_EUTRAN_11, MM_MODEM_BAND_EUTRAN_12, MM_MODEM_BAND_EUTRAN_13, MM_MODEM_BAND_EUTRAN_17, MM_MODEM_BAND_EUTRAN_18, MM_MODEM_BAND_EUTRAN_19, MM_MODEM_BAND_EUTRAN_20, MM_MODEM_BAND_EUTRAN_21, MM_MODEM_BAND_EUTRAN_26, MM_MODEM_BAND_EUTRAN_28, MM_MODEM_BAND_EUTRAN_29, MM_MODEM_BAND_EUTRAN_30, MM_MODEM_BAND_EUTRAN_38, MM_MODEM_BAND_EUTRAN_39, MM_MODEM_BAND_EUTRAN_40, MM_MODEM_BAND_EUTRAN_41, MM_MODEM_BAND_EUTRAN_66 }; validate_xact_query_response (response, &expected_mode, expected_bands, G_N_ELEMENTS (expected_bands)); } static void test_xact_query_3g_4g (void) { const gchar *response = "+XACT: " "4,1,2," "1,2,4,5,8," "101,102,103,104,105,107,108,111,112,113,117,118,119,120,121,126,128,129,130,138,139,140,141,166"; static const MMModemModeCombination expected_mode = { .allowed = MM_MODEM_MODE_3G | MM_MODEM_MODE_4G, .preferred = MM_MODEM_MODE_3G }; static const MMModemBand expected_bands[] = { MM_MODEM_BAND_UTRAN_1, MM_MODEM_BAND_UTRAN_2, MM_MODEM_BAND_UTRAN_4, MM_MODEM_BAND_UTRAN_5, MM_MODEM_BAND_UTRAN_8, MM_MODEM_BAND_EUTRAN_1, MM_MODEM_BAND_EUTRAN_2, MM_MODEM_BAND_EUTRAN_3, MM_MODEM_BAND_EUTRAN_4, MM_MODEM_BAND_EUTRAN_5, MM_MODEM_BAND_EUTRAN_7, MM_MODEM_BAND_EUTRAN_8, MM_MODEM_BAND_EUTRAN_11, MM_MODEM_BAND_EUTRAN_12, MM_MODEM_BAND_EUTRAN_13, MM_MODEM_BAND_EUTRAN_17, MM_MODEM_BAND_EUTRAN_18, MM_MODEM_BAND_EUTRAN_19, MM_MODEM_BAND_EUTRAN_20, MM_MODEM_BAND_EUTRAN_21, MM_MODEM_BAND_EUTRAN_26, MM_MODEM_BAND_EUTRAN_28, MM_MODEM_BAND_EUTRAN_29, MM_MODEM_BAND_EUTRAN_30, MM_MODEM_BAND_EUTRAN_38, MM_MODEM_BAND_EUTRAN_39, MM_MODEM_BAND_EUTRAN_40, MM_MODEM_BAND_EUTRAN_41, MM_MODEM_BAND_EUTRAN_66 }; validate_xact_query_response (response, &expected_mode, expected_bands, G_N_ELEMENTS (expected_bands)); } static void test_xact_query_no_match_mode (void) { g_autoptr(GError) error = NULL; gboolean ret; MMModemModeCombination mode = { .allowed = MM_MODEM_MODE_NONE, .preferred = MM_MODEM_MODE_NONE, }; ret = mm_xmm_parse_xact_query_response ("something here", &mode, NULL, &error); g_assert (error); g_assert (!ret); } static void test_xact_query_no_match_bands (void) { g_autoptr(GError) error = NULL; g_autoptr(GArray) bands = NULL; gboolean ret; ret = mm_xmm_parse_xact_query_response ("something here", NULL, &bands, &error); g_assert (error); g_assert (!ret); } /*****************************************************************************/ #define XACT_SET_TEST_MAX_BANDS 6 typedef struct { MMModemMode allowed; MMModemMode preferred; MMModemBand bands[XACT_SET_TEST_MAX_BANDS]; const gchar *expected_command; } XactSetTest; static const XactSetTest set_tests[] = { { /* 2G-only, no explicit bands */ .allowed = MM_MODEM_MODE_2G, .preferred = MM_MODEM_MODE_NONE, .bands = { [0] = MM_MODEM_BAND_UNKNOWN }, .expected_command = "+XACT=0,," }, { /* 3G-only, no explicit bands */ .allowed = MM_MODEM_MODE_3G, .preferred = MM_MODEM_MODE_NONE, .bands = { [0] = MM_MODEM_BAND_UNKNOWN }, .expected_command = "+XACT=1,," }, { /* 4G-only, no explicit bands */ .allowed = MM_MODEM_MODE_4G, .preferred = MM_MODEM_MODE_NONE, .bands = { [0] = MM_MODEM_BAND_UNKNOWN }, .expected_command = "+XACT=2,," }, { /* 2G+3G, none preferred, no explicit bands */ .allowed = MM_MODEM_MODE_2G | MM_MODEM_MODE_3G, .preferred = MM_MODEM_MODE_NONE, .bands = { [0] = MM_MODEM_BAND_UNKNOWN }, .expected_command = "+XACT=3,," }, { /* 2G+3G, 2G preferred, no explicit bands */ .allowed = MM_MODEM_MODE_2G | MM_MODEM_MODE_3G, .preferred = MM_MODEM_MODE_2G, .bands = { [0] = MM_MODEM_BAND_UNKNOWN }, .expected_command = "+XACT=3,0," }, { /* 2G+3G, 3G preferred, no explicit bands */ .allowed = MM_MODEM_MODE_2G | MM_MODEM_MODE_3G, .preferred = MM_MODEM_MODE_3G, .bands = { [0] = MM_MODEM_BAND_UNKNOWN }, .expected_command = "+XACT=3,1," }, { /* 3G+4G, none preferred, no explicit bands */ .allowed = MM_MODEM_MODE_3G | MM_MODEM_MODE_4G, .preferred = MM_MODEM_MODE_NONE, .bands = { [0] = MM_MODEM_BAND_UNKNOWN }, .expected_command = "+XACT=4,," }, { /* 3G+4G, 3G preferred, no explicit bands */ .allowed = MM_MODEM_MODE_3G | MM_MODEM_MODE_4G, .preferred = MM_MODEM_MODE_3G, .bands = { [0] = MM_MODEM_BAND_UNKNOWN }, .expected_command = "+XACT=4,1," }, { /* 3G+4G, 4G preferred, no explicit bands */ .allowed = MM_MODEM_MODE_3G | MM_MODEM_MODE_4G, .preferred = MM_MODEM_MODE_4G, .bands = { [0] = MM_MODEM_BAND_UNKNOWN }, .expected_command = "+XACT=4,2," }, { /* 2G+4G, none preferred, no explicit bands */ .allowed = MM_MODEM_MODE_2G | MM_MODEM_MODE_4G, .preferred = MM_MODEM_MODE_NONE, .bands = { [0] = MM_MODEM_BAND_UNKNOWN }, .expected_command = "+XACT=5,," }, { /* 2G+4G, 2G preferred, no explicit bands */ .allowed = MM_MODEM_MODE_2G | MM_MODEM_MODE_4G, .preferred = MM_MODEM_MODE_2G, .bands = { [0] = MM_MODEM_BAND_UNKNOWN }, .expected_command = "+XACT=5,0," }, { /* 2G+4G, 4G preferred, no explicit bands */ .allowed = MM_MODEM_MODE_2G | MM_MODEM_MODE_4G, .preferred = MM_MODEM_MODE_4G, .bands = { [0] = MM_MODEM_BAND_UNKNOWN }, .expected_command = "+XACT=5,2," }, { /* 2G+3G+4G, none preferred, no explicit bands */ .allowed = MM_MODEM_MODE_2G | MM_MODEM_MODE_3G | MM_MODEM_MODE_4G, .preferred = MM_MODEM_MODE_NONE, .bands = { [0] = MM_MODEM_BAND_UNKNOWN }, .expected_command = "+XACT=6,," }, { /* 2G+3G+4G, 2G preferred, no explicit bands */ .allowed = MM_MODEM_MODE_2G | MM_MODEM_MODE_3G | MM_MODEM_MODE_4G, .preferred = MM_MODEM_MODE_2G, .bands = { [0] = MM_MODEM_BAND_UNKNOWN }, .expected_command = "+XACT=6,0," }, { /* 2G+3G+4G, 3G preferred, no explicit bands */ .allowed = MM_MODEM_MODE_2G | MM_MODEM_MODE_3G | MM_MODEM_MODE_4G, .preferred = MM_MODEM_MODE_3G, .bands = { [0] = MM_MODEM_BAND_UNKNOWN }, .expected_command = "+XACT=6,1," }, { /* 2G+3G+4G, 4G preferred, no explicit bands */ .allowed = MM_MODEM_MODE_2G | MM_MODEM_MODE_3G | MM_MODEM_MODE_4G, .preferred = MM_MODEM_MODE_4G, .bands = { [0] = MM_MODEM_BAND_UNKNOWN }, .expected_command = "+XACT=6,2," }, { /* 2G bands, no explicit modes */ .allowed = MM_MODEM_MODE_NONE, .preferred = MM_MODEM_MODE_NONE, .bands = { [0] = MM_MODEM_BAND_EGSM, [1] = MM_MODEM_BAND_DCS, [2] = MM_MODEM_BAND_UNKNOWN }, .expected_command = "+XACT=,,,900,1800" }, { /* 3G bands, no explicit modes */ .allowed = MM_MODEM_MODE_NONE, .preferred = MM_MODEM_MODE_NONE, .bands = { [0] = MM_MODEM_BAND_UTRAN_1, [1] = MM_MODEM_BAND_UTRAN_2, [2] = MM_MODEM_BAND_UNKNOWN }, .expected_command = "+XACT=,,,1,2" }, { /* 4G bands, no explicit modes */ .allowed = MM_MODEM_MODE_NONE, .preferred = MM_MODEM_MODE_NONE, .bands = { [0] = MM_MODEM_BAND_EUTRAN_1, [1] = MM_MODEM_BAND_EUTRAN_2, [2] = MM_MODEM_BAND_UNKNOWN }, .expected_command = "+XACT=,,,101,102" }, { /* 2G, 3G and 4G bands, no explicit modes */ .allowed = MM_MODEM_MODE_NONE, .preferred = MM_MODEM_MODE_NONE, .bands = { [0] = MM_MODEM_BAND_EGSM, [1] = MM_MODEM_BAND_DCS, [2] = MM_MODEM_BAND_UTRAN_1, [3] = MM_MODEM_BAND_UTRAN_2, [4] = MM_MODEM_BAND_EUTRAN_1, [5] = MM_MODEM_BAND_EUTRAN_2 }, .expected_command = "+XACT=,,,900,1800,1,2,101,102" }, { /* Auto bands, no explicit modes */ .allowed = MM_MODEM_MODE_NONE, .preferred = MM_MODEM_MODE_NONE, .bands = { [0] = MM_MODEM_BAND_ANY, [1] = MM_MODEM_BAND_UNKNOWN }, .expected_command = "+XACT=,,,0" }, { /* 2G+3G+4G with 4G preferred, and 2G+3G+4G bands */ .allowed = MM_MODEM_MODE_2G | MM_MODEM_MODE_3G | MM_MODEM_MODE_4G, .preferred = MM_MODEM_MODE_4G, .bands = { [0] = MM_MODEM_BAND_EGSM, [1] = MM_MODEM_BAND_DCS, [2] = MM_MODEM_BAND_UTRAN_1, [3] = MM_MODEM_BAND_UTRAN_2, [4] = MM_MODEM_BAND_EUTRAN_1, [5] = MM_MODEM_BAND_EUTRAN_2 }, .expected_command = "+XACT=6,2,,900,1800,1,2,101,102" }, }; static void validate_xact_set_command (const MMModemMode allowed, const MMModemMode preferred, const MMModemBand *bands, guint n_bands, const gchar *expected_command) { gchar *command; MMModemModeCombination mode; GArray *bandsarray = NULL; GError *error = NULL; if (n_bands) bandsarray = g_array_append_vals (g_array_sized_new (FALSE, FALSE, sizeof (MMModemBand), n_bands), bands, n_bands); mode.allowed = allowed; mode.preferred = preferred; command = mm_xmm_build_xact_set_command ((mode.allowed != MM_MODEM_MODE_NONE) ? &mode : NULL, bandsarray, &error); g_assert_no_error (error); g_assert (command); g_assert_cmpstr (command, == , expected_command); g_free (command); if (bandsarray) g_array_unref (bandsarray); } static void test_xact_set (void) { guint i; for (i = 0; i < G_N_ELEMENTS (set_tests); i++) { guint n_bands = 0; guint j; for (j = 0; j < XACT_SET_TEST_MAX_BANDS; j++) { if (set_tests[i].bands[j] != MM_MODEM_BAND_UNKNOWN) n_bands++; } validate_xact_set_command (set_tests[i].allowed, set_tests[i].preferred, set_tests[i].bands, n_bands, set_tests[i].expected_command); } } /*****************************************************************************/ /* Test +XCESQ responses */ typedef struct { const gchar *str; gboolean gsm_info; guint rxlev; gdouble rssi; guint ber; gboolean umts_info; guint rscp_level; gdouble rscp; guint ecn0_level; gdouble ecio; gboolean lte_info; guint rsrq_level; gdouble rsrq; guint rsrp_level; gdouble rsrp; gint rssnr_level; gdouble rssnr; } XCesqResponseTest; static const XCesqResponseTest xcesq_response_tests[] = { { .str = "+XCESQ: 0,99,99,255,255,19,46,32", .gsm_info = FALSE, .rxlev = 99, .ber = 99, .umts_info = FALSE, .rscp_level = 255, .ecn0_level = 255, .lte_info = TRUE, .rsrq_level = 19, .rsrq = -10.5, .rsrp_level = 46, .rsrp = -95.0, .rssnr_level = 32, .rssnr = 16.0 }, { .str = "+XCESQ: 0,99,99,255,255,19,46,-32", .gsm_info = FALSE, .rxlev = 99, .ber = 99, .umts_info = FALSE, .rscp_level = 255, .ecn0_level = 255, .lte_info = TRUE, .rsrq_level = 19, .rsrq = -10.5, .rsrp_level = 46, .rsrp = -95.0, .rssnr_level = -32, .rssnr = -16.0 }, { .str = "+XCESQ: 0,99,99,255,255,16,47,28", .gsm_info = FALSE, .rxlev = 99, .ber = 99, .umts_info = FALSE, .rscp_level = 255, .ecn0_level = 255, .lte_info = TRUE, .rsrq_level = 16, .rsrq = -12.0, .rsrp_level = 47, .rsrp = -94.0, .rssnr_level = 28, .rssnr = 14.0 }, { .str = "+XCESQ: 0,99,99,41,29,255,255,255", .gsm_info = FALSE, .rxlev = 99, .ber = 99, .umts_info = TRUE, .rscp_level = 41, .rscp = -80.0, .ecn0_level = 29, .ecio = -10.0, .lte_info = FALSE, .rsrq_level = 255, .rsrp_level = 255, .rssnr_level = 255 }, { .str = "+XCESQ: 0,10,6,255,255,255,255,255", .gsm_info = TRUE, .rxlev = 10, .rssi = -101.0, .ber = 6, .umts_info = FALSE, .rscp_level = 255, .ecn0_level = 255, .lte_info = FALSE, .rsrq_level = 255, .rsrp_level = 255, .rssnr_level = 255 } }; static void test_xcesq_response (void) { guint i; for (i = 0; i < G_N_ELEMENTS (xcesq_response_tests); i++) { GError *error = NULL; gboolean success; guint rxlev = G_MAXUINT; guint ber = G_MAXUINT; guint rscp = G_MAXUINT; guint ecn0 = G_MAXUINT; guint rsrq = G_MAXUINT; guint rsrp = G_MAXUINT; gint rssnr = G_MAXUINT; success = mm_xmm_parse_xcesq_query_response (xcesq_response_tests[i].str, &rxlev, &ber, &rscp, &ecn0, &rsrq, &rsrp, &rssnr, &error); g_assert_no_error (error); g_assert (success); g_assert_cmpuint (xcesq_response_tests[i].rxlev, ==, rxlev); g_assert_cmpuint (xcesq_response_tests[i].ber, ==, ber); g_assert_cmpuint (xcesq_response_tests[i].rscp_level, ==, rscp); g_assert_cmpuint (xcesq_response_tests[i].ecn0_level, ==, ecn0); g_assert_cmpuint (xcesq_response_tests[i].rsrq_level, ==, rsrq); g_assert_cmpuint (xcesq_response_tests[i].rsrp_level, ==, rsrp); g_assert_cmpuint (xcesq_response_tests[i].rssnr_level, ==, rssnr); } } static void test_xcesq_response_to_signal (void) { guint i; for (i = 0; i < G_N_ELEMENTS (xcesq_response_tests); i++) { GError *error = NULL; gboolean success; MMSignal *gsm = NULL; MMSignal *umts = NULL; MMSignal *lte = NULL; success = mm_xmm_xcesq_response_to_signal_info (xcesq_response_tests[i].str, NULL, &gsm, &umts, <e, &error); g_assert_no_error (error); g_assert (success); if (xcesq_response_tests[i].gsm_info) { g_assert (gsm); g_assert_cmpfloat_tolerance (mm_signal_get_rssi (gsm), xcesq_response_tests[i].rssi, 0.1); g_object_unref (gsm); } else g_assert (!gsm); if (xcesq_response_tests[i].umts_info) { g_assert (umts); g_assert_cmpfloat_tolerance (mm_signal_get_rscp (umts), xcesq_response_tests[i].rscp, 0.1); g_assert_cmpfloat_tolerance (mm_signal_get_ecio (umts), xcesq_response_tests[i].ecio, 0.1); g_object_unref (umts); } else g_assert (!umts); if (xcesq_response_tests[i].lte_info) { g_assert (lte); g_assert_cmpfloat_tolerance (mm_signal_get_rsrq (lte), xcesq_response_tests[i].rsrq, 0.1); g_assert_cmpfloat_tolerance (mm_signal_get_rsrp (lte), xcesq_response_tests[i].rsrp, 0.1); g_assert_cmpfloat_tolerance (mm_signal_get_snr (lte), xcesq_response_tests[i].rssnr, 0.1); g_object_unref (lte); } else g_assert (!lte); } } /*****************************************************************************/ /* AT+XLCSLSR=? response parser */ typedef struct { const gchar *response; gboolean expected_transport_protocol_invalid_supported; gboolean expected_transport_protocol_supl_supported; gboolean expected_standalone_position_mode_supported; gboolean expected_ms_assisted_based_position_mode_supported; gboolean expected_loc_response_type_nmea_supported; gboolean expected_gnss_type_gps_glonass_supported; } XlcslsrTest; static XlcslsrTest xlcslsr_tests[] = { { "+XLCSLSR:(0-2),(0-3), ,(0-1), ,(0-1),(0-7200),(0-255),(0-1),(0-2),(1-256),(0-1)", TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, }, { "+XLCSLSR:(0,1,2),(0,1,2,3), ,(0,1), ,(0,1),(0-7200),(0-255),(0,1),(0,1,2),(1-256),(0,1)", TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, }, { "+XLCSLSR:(0-1),(0-2), ,(0,1), ,(0,1),(0 -7200),(0-255),(0-1),(0),(1-256),(1)", FALSE, TRUE, FALSE, TRUE, FALSE, FALSE }, }; static void test_xlcslsr_test (void) { guint i; for (i = 0; i < G_N_ELEMENTS (xlcslsr_tests); i++) { GError *error = NULL; gboolean ret; gboolean transport_protocol_invalid_supported; gboolean transport_protocol_supl_supported; gboolean standalone_position_mode_supported; gboolean ms_assisted_based_position_mode_supported; gboolean loc_response_type_nmea_supported; gboolean gnss_type_gps_glonass_supported; ret = mm_xmm_parse_xlcslsr_test_response (xlcslsr_tests[i].response, &transport_protocol_invalid_supported, &transport_protocol_supl_supported, &standalone_position_mode_supported, &ms_assisted_based_position_mode_supported, &loc_response_type_nmea_supported, &gnss_type_gps_glonass_supported, &error); g_assert_no_error (error); g_assert (ret); g_assert (transport_protocol_invalid_supported == xlcslsr_tests[i].expected_transport_protocol_invalid_supported); g_assert (transport_protocol_supl_supported == xlcslsr_tests[i].expected_transport_protocol_supl_supported); g_assert (standalone_position_mode_supported == xlcslsr_tests[i].expected_standalone_position_mode_supported); g_assert (ms_assisted_based_position_mode_supported == xlcslsr_tests[i].expected_ms_assisted_based_position_mode_supported); g_assert (loc_response_type_nmea_supported == xlcslsr_tests[i].expected_loc_response_type_nmea_supported); g_assert (gnss_type_gps_glonass_supported == xlcslsr_tests[i].expected_gnss_type_gps_glonass_supported); } } /*****************************************************************************/ /* AT+XLCSSLP? response parser */ typedef struct { const gchar *response; const gchar *expected; } XlcsslpQuery; static XlcsslpQuery xlcsslp_queries[] = { { "+XLCSSLP:1,\"www.spirent-lcs.com\",7275", "www.spirent-lcs.com:7275" }, { "+XLCSSLP:0,\"123.123.123.123\",7275", "123.123.123.123:7275" }, }; static void test_xlcsslp_queries (void) { guint i; for (i = 0; i < G_N_ELEMENTS (xlcsslp_queries); i++) { GError *error = NULL; gchar *supl_server = NULL; gboolean ret; ret = mm_xmm_parse_xlcsslp_query_response (xlcsslp_queries[i].response, &supl_server, &error); g_assert_no_error (error); g_assert (ret); g_assert_cmpstr (supl_server, ==, xlcsslp_queries[i].expected); g_free (supl_server); } } /*****************************************************************************/ int main (int argc, char **argv) { setlocale (LC_ALL, ""); g_test_init (&argc, &argv, NULL); g_test_add_func ("/MM/xmm/xact/test/4g-only", test_xact_test_4g_only); g_test_add_func ("/MM/xmm/xact/test/3g-4g", test_xact_test_3g_4g); g_test_add_func ("/MM/xmm/xact/test/2g-3g-4g", test_xact_test_2g_3g_4g); g_test_add_func ("/MM/xmm/xact/query/3g-only", test_xact_query_3g_only); g_test_add_func ("/MM/xmm/xact/query/3g-4g", test_xact_query_3g_4g); g_test_add_func ("/MM/xmm/xact/query/no-match/mode", test_xact_query_no_match_mode); g_test_add_func ("/MM/xmm/xact/query/no-match/bands", test_xact_query_no_match_bands); g_test_add_func ("/MM/xmm/xact/set", test_xact_set); g_test_add_func ("/MM/xmm/xcesq/query_response", test_xcesq_response); g_test_add_func ("/MM/xmm/xcesq/query_response_to_signal", test_xcesq_response_to_signal); g_test_add_func ("/MM/xmm/xlcslsr/test", test_xlcslsr_test); g_test_add_func ("/MM/xmm/xlcsslp/query", test_xlcsslp_queries); return g_test_run (); } ModemManager-1.23.4-dev/src/plugins/zte/000077500000000000000000000000001456466623000177775ustar00rootroot00000000000000ModemManager-1.23.4-dev/src/plugins/zte/77-mm-zte-port-types.rules000066400000000000000000000414331456466623000246500ustar00rootroot00000000000000# do not edit this file, it will be overwritten on update ACTION!="add|change|move|bind", GOTO="mm_zte_port_types_end" SUBSYSTEMS=="usb", ATTRS{idVendor}=="19d2", GOTO="mm_zte_port_types" GOTO="mm_zte_port_types_end" LABEL="mm_zte_port_types" SUBSYSTEMS=="usb", ATTRS{bInterfaceNumber}=="?*", ENV{.MM_USBIFNUM}="$attr{bInterfaceNumber}" ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0001", ENV{.MM_USBIFNUM}=="00", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1" ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0001", ENV{.MM_USBIFNUM}=="02", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1" ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0002", ENV{.MM_USBIFNUM}=="02", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1" ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0002", ENV{.MM_USBIFNUM}=="04", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1" ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0003", ENV{.MM_USBIFNUM}=="00", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1" ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0003", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1" ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0004", ENV{.MM_USBIFNUM}=="00", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1" ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0004", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1" ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0005", ENV{.MM_USBIFNUM}=="00", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1" ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0005", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1" ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0006", ENV{.MM_USBIFNUM}=="00", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1" ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0006", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1" ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0007", ENV{.MM_USBIFNUM}=="00", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1" ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0007", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1" ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0008", ENV{.MM_USBIFNUM}=="00", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1" ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0008", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1" ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0009", ENV{.MM_USBIFNUM}=="00", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1" ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0009", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1" ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="000A", ENV{.MM_USBIFNUM}=="00", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1" ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="000A", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1" ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0012", ENV{.MM_USBIFNUM}=="02", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1" ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0012", ENV{.MM_USBIFNUM}=="04", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1" ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0015", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1" ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0015", ENV{.MM_USBIFNUM}=="03", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1" ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0016", ENV{.MM_USBIFNUM}=="02", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1" ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0016", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1" ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0017", ENV{.MM_USBIFNUM}=="02", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1" ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0017", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1" ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0018", ENV{.MM_USBIFNUM}=="02", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1" ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0018", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1" ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0019", ENV{.MM_USBIFNUM}=="02", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1" ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0019", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1" ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0021", ENV{.MM_USBIFNUM}=="03", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1" ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0021", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1" ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0024", ENV{.MM_USBIFNUM}=="02", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1" ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0024", ENV{.MM_USBIFNUM}=="04", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1" ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0025", ENV{.MM_USBIFNUM}=="02", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1" ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0025", ENV{.MM_USBIFNUM}=="04", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1" ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0030", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1" ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0030", ENV{.MM_USBIFNUM}=="03", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1" ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0031", ENV{.MM_USBIFNUM}=="03", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1" ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0031", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1" ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0033", ENV{.MM_USBIFNUM}=="04", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1" ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0033", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1" ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0037", ENV{.MM_USBIFNUM}=="03", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1" ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0037", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1" ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0039", ENV{.MM_USBIFNUM}=="03", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1" ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0039", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1" ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0042", ENV{.MM_USBIFNUM}=="03", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1" ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0042", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1" ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0043", ENV{.MM_USBIFNUM}=="03", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1" ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0043", ENV{.MM_USBIFNUM}=="02", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1" ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0048", ENV{.MM_USBIFNUM}=="04", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1" ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0048", ENV{.MM_USBIFNUM}=="02", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1" ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0049", ENV{.MM_USBIFNUM}=="04", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1" ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0049", ENV{.MM_USBIFNUM}=="02", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1" ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0052", ENV{.MM_USBIFNUM}=="03", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1" ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0052", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1" ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0054", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1" ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0054", ENV{.MM_USBIFNUM}=="03", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1" ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0055", ENV{.MM_USBIFNUM}=="02", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1" ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0055", ENV{.MM_USBIFNUM}=="04", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1" ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0057", ENV{.MM_USBIFNUM}=="03", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1" ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0057", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1" ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0058", ENV{.MM_USBIFNUM}=="03", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1" ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0058", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1" ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0061", ENV{.MM_USBIFNUM}=="03", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1" ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0061", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1" ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0063", ENV{.MM_USBIFNUM}=="03", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1" ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0063", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1" ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0064", ENV{.MM_USBIFNUM}=="02", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1" ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0064", ENV{.MM_USBIFNUM}=="00", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1" ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0066", ENV{.MM_USBIFNUM}=="03", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1" ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0066", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1" ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0078", ENV{.MM_USBIFNUM}=="03", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1" ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0078", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1" ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0082", ENV{.MM_USBIFNUM}=="03", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1" ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0082", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1" ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0091", ENV{.MM_USBIFNUM}=="04", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1" ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0091", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1" ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0104", ENV{.MM_USBIFNUM}=="03", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1" ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0104", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1" ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0106", ENV{.MM_USBIFNUM}=="03", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1" ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0106", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1" ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0108", ENV{.MM_USBIFNUM}=="03", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1" ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0108", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1" ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0113", ENV{.MM_USBIFNUM}=="04", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1" ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0113", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1" ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0117", ENV{.MM_USBIFNUM}=="02", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1" ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0117", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1" ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0118", ENV{.MM_USBIFNUM}=="04", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1" ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0118", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1" ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0121", ENV{.MM_USBIFNUM}=="04", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1" ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0121", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1" ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0122", ENV{.MM_USBIFNUM}=="03", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1" ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0122", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1" ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0123", ENV{.MM_USBIFNUM}=="03", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1" ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0123", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1" ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0124", ENV{.MM_USBIFNUM}=="04", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1" ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0124", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1" ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0125", ENV{.MM_USBIFNUM}=="05", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1" ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0125", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1" ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0126", ENV{.MM_USBIFNUM}=="04", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1" ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0126", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1" ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0128", ENV{.MM_USBIFNUM}=="04", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1" ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0128", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1" ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0156", ENV{.MM_USBIFNUM}=="02", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1" ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0156", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1" ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="1007", ENV{.MM_USBIFNUM}=="03", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1" ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="1007", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1" ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="1008", ENV{.MM_USBIFNUM}=="03", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1" ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="1008", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1" ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="1010", ENV{.MM_USBIFNUM}=="03", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1" ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="1010", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1" ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="1254", ENV{.MM_USBIFNUM}=="03", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1" ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="1254", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1" ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="1268", ENV{.MM_USBIFNUM}=="03", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1" ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="1268", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1" ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="1515", ENV{.MM_USBIFNUM}=="00", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1" ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="1515", ENV{.MM_USBIFNUM}=="02", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1" ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="2002", ENV{.MM_USBIFNUM}=="03", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1" ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="2002", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1" ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="2003", ENV{.MM_USBIFNUM}=="03", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1" ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="2003", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1" # Icera-based devices that use DHCP, not AT%IPDPADDR ATTRS{product}=="K3805-z", ENV{ID_MM_ZTE_ICERA_DHCP}="1" # MF60 exposes QMI, but it is unusable, fallback to AT+PPP ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="1402", SUBSYSTEM=="usb", KERNEL=="cdc-wdm*", ENV{ID_MM_PORT_IGNORE}="1" ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="1402", SUBSYSTEM=="usbmisc", KERNEL=="cdc-wdm*", ENV{ID_MM_PORT_IGNORE}="1" ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="1402", SUBSYSTEM=="net", ENV{ID_MM_PORT_IGNORE}="1" LABEL="mm_zte_port_types_end" ModemManager-1.23.4-dev/src/plugins/zte/mm-broadband-modem-zte-icera.c000066400000000000000000000167631456466623000254630ustar00rootroot00000000000000/* -*- 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 - 2012 Red Hat, Inc. * Copyright (C) 2012 Aleksander Morgado */ #include #include #include #include #include #include #include "ModemManager.h" #include "mm-iface-modem-3gpp.h" #include "mm-base-modem-at.h" #include "mm-common-zte.h" #include "mm-broadband-modem-zte-icera.h" #include "mm-modem-helpers.h" static void iface_modem_3gpp_init (MMIfaceModem3gpp *iface); static MMIfaceModem3gpp *iface_modem_3gpp_parent; G_DEFINE_TYPE_EXTENDED (MMBroadbandModemZteIcera, mm_broadband_modem_zte_icera, MM_TYPE_BROADBAND_MODEM_ICERA, 0, G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_3GPP, iface_modem_3gpp_init)); struct _MMBroadbandModemZteIceraPrivate { /* Unsolicited messaging setup */ MMCommonZteUnsolicitedSetup *unsolicited_setup; }; /*****************************************************************************/ /* Setup/Cleanup unsolicited events (3GPP interface) */ static gboolean modem_3gpp_setup_cleanup_unsolicited_events_finish (MMIfaceModem3gpp *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void parent_setup_unsolicited_events_ready (MMIfaceModem3gpp *self, GAsyncResult *res, GTask *task) { GError *error = NULL; if (!iface_modem_3gpp_parent->setup_unsolicited_events_finish (self, res, &error)) g_task_return_error (task, error); else { /* Our own setup now */ mm_common_zte_set_unsolicited_events_handlers (MM_BROADBAND_MODEM (self), MM_BROADBAND_MODEM_ZTE_ICERA (self)->priv->unsolicited_setup, TRUE); g_task_return_boolean (task, TRUE); } g_object_unref (task); } static void modem_3gpp_setup_unsolicited_events (MMIfaceModem3gpp *self, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; task = g_task_new (self, NULL, callback, user_data); /* Chain up parent's setup */ iface_modem_3gpp_parent->setup_unsolicited_events ( self, (GAsyncReadyCallback)parent_setup_unsolicited_events_ready, task); } static void parent_cleanup_unsolicited_events_ready (MMIfaceModem3gpp *self, GAsyncResult *res, GTask *task) { GError *error = NULL; if (!iface_modem_3gpp_parent->cleanup_unsolicited_events_finish (self, res, &error)) g_task_return_error (task, error); else g_task_return_boolean (task, TRUE); g_object_unref (task); } static void modem_3gpp_cleanup_unsolicited_events (MMIfaceModem3gpp *self, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; task = g_task_new (self, NULL, callback, user_data); /* Our own cleanup first */ mm_common_zte_set_unsolicited_events_handlers (MM_BROADBAND_MODEM (self), MM_BROADBAND_MODEM_ZTE_ICERA (self)->priv->unsolicited_setup, FALSE); /* And now chain up parent's cleanup */ iface_modem_3gpp_parent->cleanup_unsolicited_events ( self, (GAsyncReadyCallback)parent_cleanup_unsolicited_events_ready, task); } /*****************************************************************************/ /* Setup ports (Broadband modem class) */ static void setup_ports (MMBroadbandModem *self) { /* Call parent's setup ports first always */ MM_BROADBAND_MODEM_CLASS (mm_broadband_modem_zte_icera_parent_class)->setup_ports (self); /* Now reset the unsolicited messages we'll handle when enabled */ mm_common_zte_set_unsolicited_events_handlers (MM_BROADBAND_MODEM (self), MM_BROADBAND_MODEM_ZTE_ICERA (self)->priv->unsolicited_setup, FALSE); } /*****************************************************************************/ MMBroadbandModemZteIcera * mm_broadband_modem_zte_icera_new (const gchar *device, const gchar *physdev, const gchar **drivers, const gchar *plugin, guint16 vendor_id, guint16 product_id) { return g_object_new (MM_TYPE_BROADBAND_MODEM_ZTE_ICERA, MM_BASE_MODEM_DEVICE, device, MM_BASE_MODEM_PHYSDEV, physdev, MM_BASE_MODEM_DRIVERS, drivers, MM_BASE_MODEM_PLUGIN, plugin, MM_BASE_MODEM_VENDOR_ID, vendor_id, MM_BASE_MODEM_PRODUCT_ID, product_id, /* Generic bearer (AT) and Icera bearer (NET) supported */ MM_BASE_MODEM_DATA_NET_SUPPORTED, TRUE, MM_BASE_MODEM_DATA_TTY_SUPPORTED, TRUE, NULL); } static void mm_broadband_modem_zte_icera_init (MMBroadbandModemZteIcera *self) { /* Initialize private data */ self->priv = G_TYPE_INSTANCE_GET_PRIVATE ((self), MM_TYPE_BROADBAND_MODEM_ZTE_ICERA, MMBroadbandModemZteIceraPrivate); self->priv->unsolicited_setup = mm_common_zte_unsolicited_setup_new (); } static void finalize (GObject *object) { MMBroadbandModemZteIcera *self = MM_BROADBAND_MODEM_ZTE_ICERA (object); mm_common_zte_unsolicited_setup_free (self->priv->unsolicited_setup); G_OBJECT_CLASS (mm_broadband_modem_zte_icera_parent_class)->finalize (object); } static void iface_modem_3gpp_init (MMIfaceModem3gpp *iface) { iface_modem_3gpp_parent = g_type_interface_peek_parent (iface); iface->setup_unsolicited_events = modem_3gpp_setup_unsolicited_events; iface->setup_unsolicited_events_finish = modem_3gpp_setup_cleanup_unsolicited_events_finish; iface->cleanup_unsolicited_events = modem_3gpp_cleanup_unsolicited_events; iface->cleanup_unsolicited_events_finish = modem_3gpp_setup_cleanup_unsolicited_events_finish; } static void mm_broadband_modem_zte_icera_class_init (MMBroadbandModemZteIceraClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); MMBroadbandModemClass *broadband_modem_class = MM_BROADBAND_MODEM_CLASS (klass); g_type_class_add_private (object_class, sizeof (MMBroadbandModemZteIceraPrivate)); object_class->finalize = finalize; broadband_modem_class->setup_ports = setup_ports; } ModemManager-1.23.4-dev/src/plugins/zte/mm-broadband-modem-zte-icera.h000066400000000000000000000051621456466623000254570ustar00rootroot00000000000000/* -*- 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 - 2012 Red Hat, Inc. * Copyright (C) 2012 Aleksander Morgado */ #ifndef MM_BROADBAND_MODEM_ZTE_ICERA_H #define MM_BROADBAND_MODEM_ZTE_ICERA_H #include "mm-broadband-modem-icera.h" #define MM_TYPE_BROADBAND_MODEM_ZTE_ICERA (mm_broadband_modem_zte_icera_get_type ()) #define MM_BROADBAND_MODEM_ZTE_ICERA(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_BROADBAND_MODEM_ZTE_ICERA, MMBroadbandModemZteIcera)) #define MM_BROADBAND_MODEM_ZTE_ICERA_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_BROADBAND_MODEM_ZTE_ICERA, MMBroadbandModemZteIceraClass)) #define MM_IS_BROADBAND_MODEM_ZTE_ICERA(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_BROADBAND_MODEM_ZTE_ICERA)) #define MM_IS_BROADBAND_MODEM_ZTE_ICERA_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_BROADBAND_MODEM_ZTE_ICERA)) #define MM_BROADBAND_MODEM_ZTE_ICERA_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_BROADBAND_MODEM_ZTE_ICERA, MMBroadbandModemZteIceraClass)) typedef struct _MMBroadbandModemZteIcera MMBroadbandModemZteIcera; typedef struct _MMBroadbandModemZteIceraClass MMBroadbandModemZteIceraClass; typedef struct _MMBroadbandModemZteIceraPrivate MMBroadbandModemZteIceraPrivate; struct _MMBroadbandModemZteIcera { MMBroadbandModemIcera parent; MMBroadbandModemZteIceraPrivate *priv; }; struct _MMBroadbandModemZteIceraClass{ MMBroadbandModemIceraClass parent; }; GType mm_broadband_modem_zte_icera_get_type (void); MMBroadbandModemZteIcera *mm_broadband_modem_zte_icera_new (const gchar *device, const gchar *physdev, const gchar **drivers, const gchar *plugin, guint16 vendor_id, guint16 product_id); #endif /* MM_BROADBAND_MODEM_ZTE_ICERA_H */ ModemManager-1.23.4-dev/src/plugins/zte/mm-broadband-modem-zte.c000066400000000000000000000624711456466623000243770ustar00rootroot00000000000000/* -*- 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 - 2012 Red Hat, Inc. * Copyright (C) 2012 Aleksander Morgado */ #include #include #include #include #include #include #include "ModemManager.h" #include "mm-errors-types.h" #include "mm-modem-helpers.h" #include "mm-base-modem-at.h" #include "mm-iface-modem.h" #include "mm-iface-modem-3gpp.h" #include "mm-common-zte.h" #include "mm-broadband-modem-zte.h" static void iface_modem_init (MMIfaceModem *iface); static void iface_modem_3gpp_init (MMIfaceModem3gpp *iface); static MMIfaceModem *iface_modem_parent; static MMIfaceModem3gpp *iface_modem_3gpp_parent; G_DEFINE_TYPE_EXTENDED (MMBroadbandModemZte, mm_broadband_modem_zte, MM_TYPE_BROADBAND_MODEM, 0, G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM, iface_modem_init) G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_3GPP, iface_modem_3gpp_init)); struct _MMBroadbandModemZtePrivate { /* Unsolicited messaging setup */ MMCommonZteUnsolicitedSetup *unsolicited_setup; }; /*****************************************************************************/ /* Unlock retries (Modem interface) */ static MMUnlockRetries * load_unlock_retries_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { return g_task_propagate_pointer (G_TASK (res), error); } static void load_unlock_retries_ready (MMBaseModem *self, GAsyncResult *res, GTask *task) { const gchar *response; GError *error = NULL; gint pin1, puk1; response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error); if (!response) { g_task_return_error (task, error); g_object_unref (task); return; } response = mm_strip_tag (response, "+ZPINPUK:"); if (sscanf (response, "%d,%d", &pin1, &puk1) == 2) { MMUnlockRetries *retries; retries = mm_unlock_retries_new (); mm_unlock_retries_set (retries, MM_MODEM_LOCK_SIM_PIN, pin1); mm_unlock_retries_set (retries, MM_MODEM_LOCK_SIM_PUK, puk1); g_task_return_pointer (task, retries, g_object_unref); } else { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Invalid unlock retries response: '%s'", response); } g_object_unref (task); } static void load_unlock_retries (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data) { mm_base_modem_at_command ( MM_BASE_MODEM (self), "+ZPINPUK=?", 3, FALSE, (GAsyncReadyCallback)load_unlock_retries_ready, g_task_new (self, NULL, callback, user_data)); } /*****************************************************************************/ /* After SIM unlock (Modem interface) */ typedef struct { guint retries; } ModemAfterSimUnlockContext; static gboolean modem_after_sim_unlock_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void modem_after_sim_unlock_context_step (GTask *task); static gboolean cpms_timeout_cb (GTask *task) { ModemAfterSimUnlockContext *ctx; ctx = g_task_get_task_data (task); ctx->retries--; modem_after_sim_unlock_context_step (task); return G_SOURCE_REMOVE; } static void cpms_try_ready (MMBaseModem *self, GAsyncResult *res, GTask *task) { GError *error = NULL; if (!mm_base_modem_at_command_finish (self, res, &error) && g_error_matches (error, MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_SIM_BUSY)) { /* Retry in 2 seconds */ g_timeout_add_seconds (2, (GSourceFunc)cpms_timeout_cb, task); g_error_free (error); return; } if (error) g_error_free (error); /* Well, we're done */ g_task_return_boolean (task, TRUE); g_object_unref (task); } static void modem_after_sim_unlock_context_step (GTask *task) { MMBroadbandModemZte *self; ModemAfterSimUnlockContext *ctx; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); if (ctx->retries == 0) { /* Well... just return without error */ g_task_return_new_error ( task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Consumed all attempts to wait for SIM not being busy"); g_object_unref (task); return; } mm_base_modem_at_command (MM_BASE_MODEM (self), "+CPMS?", 3, FALSE, (GAsyncReadyCallback)cpms_try_ready, task); } static gboolean after_sim_unlock_wait_cb (GTask *task) { /* Attempt to disable floods of "+ZUSIMR:2" unsolicited responses that * eventually fill up the device's buffers and make it crash. Normally * done during probing, but if the device has a PIN enabled it won't * accept the +CPMS? during the probe and we have to do it here. */ modem_after_sim_unlock_context_step (task); return G_SOURCE_REMOVE; } static void modem_after_sim_unlock (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data) { ModemAfterSimUnlockContext *ctx; GTask *task; ctx = g_new (ModemAfterSimUnlockContext, 1); ctx->retries = 3; task = g_task_new (self, NULL, callback, user_data); g_task_set_task_data (task, ctx, g_free); g_timeout_add_seconds (1, (GSourceFunc)after_sim_unlock_wait_cb, task); } /*****************************************************************************/ /* Modem power down (Modem interface) */ static gboolean modem_power_down_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { return !!mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, error); } static void modem_power_down (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data) { /* Use AT+CFUN=4 for power down. It will stop the RF (IMSI detach), and * keeps access to the SIM */ mm_base_modem_at_command (MM_BASE_MODEM (self), "+CFUN=4", 3, FALSE, callback, user_data); } /*****************************************************************************/ /* Load supported modes (Modem interface) */ static GArray * load_supported_modes_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { return g_task_propagate_pointer (G_TASK (res), error); } static void parent_load_supported_modes_ready (MMIfaceModem *self, GAsyncResult *res, GTask *task) { GError *error = NULL; GArray *all; GArray *combinations; GArray *filtered; MMModemModeCombination mode; all = iface_modem_parent->load_supported_modes_finish (self, res, &error); if (!all) { g_task_return_error (task, error); g_object_unref (task); return; } /* Build list of combinations */ combinations = g_array_sized_new (FALSE, FALSE, sizeof (MMModemModeCombination), 5); /* 2G only */ mode.allowed = MM_MODEM_MODE_2G; mode.preferred = MM_MODEM_MODE_NONE; g_array_append_val (combinations, mode); /* 3G only */ mode.allowed = MM_MODEM_MODE_3G; mode.preferred = MM_MODEM_MODE_NONE; g_array_append_val (combinations, mode); if (!mm_iface_modem_is_3gpp_lte (self)) { /* 2G and 3G */ mode.allowed = (MM_MODEM_MODE_2G | MM_MODEM_MODE_3G); mode.preferred = MM_MODEM_MODE_NONE; g_array_append_val (combinations, mode); /* 2G and 3G, 2G preferred */ mode.allowed = (MM_MODEM_MODE_2G | MM_MODEM_MODE_3G); mode.preferred = MM_MODEM_MODE_2G; g_array_append_val (combinations, mode); /* 2G and 3G, 3G preferred */ mode.allowed = (MM_MODEM_MODE_2G | MM_MODEM_MODE_3G); mode.preferred = MM_MODEM_MODE_3G; g_array_append_val (combinations, mode); } else { /* 4G only */ mode.allowed = MM_MODEM_MODE_4G; mode.preferred = MM_MODEM_MODE_NONE; g_array_append_val (combinations, mode); /* 2G, 3G and 4G */ mode.allowed = (MM_MODEM_MODE_2G | MM_MODEM_MODE_3G | MM_MODEM_MODE_4G); mode.preferred = MM_MODEM_MODE_NONE; g_array_append_val (combinations, mode); } /* Filter out those unsupported modes */ filtered = mm_filter_supported_modes (all, combinations, self); g_array_unref (all); g_array_unref (combinations); g_task_return_pointer (task, filtered, (GDestroyNotify) g_array_unref); g_object_unref (task); } static void load_supported_modes (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data) { /* Run parent's loading */ iface_modem_parent->load_supported_modes ( MM_IFACE_MODEM (self), (GAsyncReadyCallback)parent_load_supported_modes_ready, g_task_new (self, NULL, callback, user_data)); } /*****************************************************************************/ /* Load initial allowed/preferred modes (Modem interface) */ static gboolean load_current_modes_finish (MMIfaceModem *self, GAsyncResult *res, MMModemMode *allowed, MMModemMode *preferred, GError **error) { const gchar *response; g_autoptr(GMatchInfo) match_info = NULL; g_autoptr(GRegex) r = NULL; gint cm_mode = -1; gint pref_acq = -1; GError *match_error = NULL; response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, error); if (!response) return FALSE; r = g_regex_new ("\\+ZSNT:\\s*(\\d),(\\d),(\\d)", G_REGEX_UNGREEDY, 0, NULL); g_assert (r != NULL); if (!g_regex_match_full (r, response, strlen (response), 0, 0, &match_info, &match_error)) { if (match_error) g_propagate_error (error, match_error); else g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't parse +ZSNT response: '%s'", response); return FALSE; } if (!mm_get_int_from_match_info (match_info, 1, &cm_mode) || cm_mode < 0 || (cm_mode > 2 && cm_mode != 6) || !mm_get_int_from_match_info (match_info, 3, &pref_acq) || pref_acq < 0 || pref_acq > 2) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Failed to parse the allowed mode response: '%s'", response); return FALSE; } /* Correctly parsed! */ if (cm_mode == 0) { /* Both 2G, 3G and LTE allowed. For LTE modems, no 2G/3G preference supported. */ if (pref_acq == 0 || mm_iface_modem_is_3gpp_lte (self)) { /* Any allowed */ *allowed = MM_MODEM_MODE_ANY; *preferred = MM_MODEM_MODE_NONE; } else if (pref_acq == 1) { *allowed = (MM_MODEM_MODE_2G | MM_MODEM_MODE_3G); *preferred = MM_MODEM_MODE_2G; } else if (pref_acq == 2) { *allowed = (MM_MODEM_MODE_2G | MM_MODEM_MODE_3G); *preferred = MM_MODEM_MODE_3G; } else g_assert_not_reached (); } else if (cm_mode == 1) { /* GSM only */ *allowed = MM_MODEM_MODE_2G; *preferred = MM_MODEM_MODE_NONE; } else if (cm_mode == 2) { /* WCDMA only */ *allowed = MM_MODEM_MODE_3G; *preferred = MM_MODEM_MODE_NONE; } else if (cm_mode == 6) { /* LTE only */ *allowed = MM_MODEM_MODE_4G; *preferred = MM_MODEM_MODE_NONE; } else g_assert_not_reached (); return TRUE; } static void load_current_modes (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data) { mm_base_modem_at_command (MM_BASE_MODEM (self), "+ZSNT?", 3, FALSE, callback, user_data); } /*****************************************************************************/ /* Set allowed modes (Modem interface) */ static gboolean set_current_modes_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void allowed_mode_update_ready (MMBroadbandModemZte *self, GAsyncResult *res, GTask *task) { GError *error = NULL; mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error); if (error) /* Let the error be critical. */ g_task_return_error (task, error); else g_task_return_boolean (task, TRUE); g_object_unref (task); } static void set_current_modes (MMIfaceModem *self, MMModemMode allowed, MMModemMode preferred, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; gchar *command; gint cm_mode = -1; gint pref_acq = -1; task = g_task_new (self, NULL, callback, user_data); if (allowed == MM_MODEM_MODE_2G) { cm_mode = 1; pref_acq = 0; } else if (allowed == MM_MODEM_MODE_3G) { cm_mode = 2; pref_acq = 0; } else if (allowed == (MM_MODEM_MODE_2G | MM_MODEM_MODE_3G) && !mm_iface_modem_is_3gpp_lte (self)) { /* LTE models do not support 2G|3G mode */ cm_mode = 0; if (preferred == MM_MODEM_MODE_2G) pref_acq = 1; else if (preferred == MM_MODEM_MODE_3G) pref_acq = 2; else /* none preferred, so AUTO */ pref_acq = 0; } else if (allowed == (MM_MODEM_MODE_2G | MM_MODEM_MODE_3G | MM_MODEM_MODE_4G) && preferred == MM_MODEM_MODE_NONE) { cm_mode = 0; pref_acq = 0; } else if (allowed == MM_MODEM_MODE_ANY && preferred == MM_MODEM_MODE_NONE) { cm_mode = 0; pref_acq = 0; } else if (allowed == MM_MODEM_MODE_4G) { cm_mode = 6; pref_acq = 0; } if (cm_mode < 0 || pref_acq < 0) { gchar *allowed_str; gchar *preferred_str; allowed_str = mm_modem_mode_build_string_from_mask (allowed); preferred_str = mm_modem_mode_build_string_from_mask (preferred); g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Requested mode (allowed: '%s', preferred: '%s') not " "supported by the modem.", allowed_str, preferred_str); g_object_unref (task); g_free (allowed_str); g_free (preferred_str); return; } command = g_strdup_printf ("AT+ZSNT=%d,0,%d", cm_mode, pref_acq); mm_base_modem_at_command ( MM_BASE_MODEM (self), command, 3, FALSE, (GAsyncReadyCallback)allowed_mode_update_ready, task); g_free (command); } /*****************************************************************************/ /* Load access technology (Modem interface) */ static gboolean load_access_technologies_finish (MMIfaceModem *self, GAsyncResult *res, MMModemAccessTechnology *access_technologies, guint *mask, GError **error) { const gchar *response; /* CDMA-only devices run parent access technology checks */ if (mm_iface_modem_is_cdma_only (self)) { return iface_modem_parent->load_access_technologies_finish (self, res, access_technologies, mask, error); } /* Otherwise process and handle +ZPAS response from 3GPP devices */ response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, error); if (!response) return FALSE; /* Sample response from an MF626: * +ZPAS: "GPRS/EDGE","CS_ONLY" */ response = mm_strip_tag (response, "+ZPAS:"); *access_technologies = mm_string_to_access_tech (response); *mask = MM_IFACE_MODEM_3GPP_ALL_ACCESS_TECHNOLOGIES_MASK; return TRUE; } static void load_access_technologies (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data) { /* CDMA modems don't support ZPAS and thus run parent's access technology * loading. */ if (mm_iface_modem_is_cdma_only (self)) { iface_modem_parent->load_access_technologies (self, callback, user_data); return; } mm_base_modem_at_command (MM_BASE_MODEM (self), "+ZPAS?", 3, FALSE, callback, user_data); } /*****************************************************************************/ /* Setup/Cleanup unsolicited events (3GPP interface) */ static gboolean modem_3gpp_setup_cleanup_unsolicited_events_finish (MMIfaceModem3gpp *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void parent_setup_unsolicited_events_ready (MMIfaceModem3gpp *self, GAsyncResult *res, GTask *task) { GError *error = NULL; if (!iface_modem_3gpp_parent->setup_unsolicited_events_finish (self, res, &error)) g_task_return_error (task, error); else { /* Our own setup now */ mm_common_zte_set_unsolicited_events_handlers (MM_BROADBAND_MODEM (self), MM_BROADBAND_MODEM_ZTE (self)->priv->unsolicited_setup, TRUE); g_task_return_boolean (task, TRUE); } g_object_unref (task); } static void modem_3gpp_setup_unsolicited_events (MMIfaceModem3gpp *self, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; task = g_task_new (self, NULL, callback, user_data); /* Chain up parent's setup */ iface_modem_3gpp_parent->setup_unsolicited_events ( self, (GAsyncReadyCallback)parent_setup_unsolicited_events_ready, task); } static void parent_cleanup_unsolicited_events_ready (MMIfaceModem3gpp *self, GAsyncResult *res, GTask *task) { GError *error = NULL; if (!iface_modem_3gpp_parent->cleanup_unsolicited_events_finish (self, res, &error)) g_task_return_error (task, error); else g_task_return_boolean (task, TRUE); g_object_unref (task); } static void modem_3gpp_cleanup_unsolicited_events (MMIfaceModem3gpp *self, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; task = g_task_new (self, NULL, callback, user_data); /* Our own cleanup first */ mm_common_zte_set_unsolicited_events_handlers (MM_BROADBAND_MODEM (self), MM_BROADBAND_MODEM_ZTE (self)->priv->unsolicited_setup, FALSE); /* And now chain up parent's cleanup */ iface_modem_3gpp_parent->cleanup_unsolicited_events ( self, (GAsyncReadyCallback)parent_cleanup_unsolicited_events_ready, task); } /*****************************************************************************/ /* Setup ports (Broadband modem class) */ static void setup_ports (MMBroadbandModem *self) { /* Call parent's setup ports first always */ MM_BROADBAND_MODEM_CLASS (mm_broadband_modem_zte_parent_class)->setup_ports (self); /* Now reset the unsolicited messages we'll handle when enabled */ mm_common_zte_set_unsolicited_events_handlers (MM_BROADBAND_MODEM (self), MM_BROADBAND_MODEM_ZTE (self)->priv->unsolicited_setup, FALSE); } /*****************************************************************************/ MMBroadbandModemZte * mm_broadband_modem_zte_new (const gchar *device, const gchar *physdev, const gchar **drivers, const gchar *plugin, guint16 vendor_id, guint16 product_id) { return g_object_new (MM_TYPE_BROADBAND_MODEM_ZTE, MM_BASE_MODEM_DEVICE, device, MM_BASE_MODEM_DRIVERS, drivers, MM_BASE_MODEM_PLUGIN, plugin, MM_BASE_MODEM_VENDOR_ID, vendor_id, MM_BASE_MODEM_PRODUCT_ID, product_id, /* Generic bearer supports TTY only */ MM_BASE_MODEM_DATA_NET_SUPPORTED, FALSE, MM_BASE_MODEM_DATA_TTY_SUPPORTED, TRUE, MM_BROADBAND_MODEM_INDICATORS_DISABLED, TRUE, NULL); } static void mm_broadband_modem_zte_init (MMBroadbandModemZte *self) { /* Initialize private data */ self->priv = G_TYPE_INSTANCE_GET_PRIVATE ((self), MM_TYPE_BROADBAND_MODEM_ZTE, MMBroadbandModemZtePrivate); self->priv->unsolicited_setup = mm_common_zte_unsolicited_setup_new (); } static void finalize (GObject *object) { MMBroadbandModemZte *self = MM_BROADBAND_MODEM_ZTE (object); mm_common_zte_unsolicited_setup_free (self->priv->unsolicited_setup); G_OBJECT_CLASS (mm_broadband_modem_zte_parent_class)->finalize (object); } static void iface_modem_init (MMIfaceModem *iface) { iface_modem_parent = g_type_interface_peek_parent (iface); iface->modem_after_sim_unlock = modem_after_sim_unlock; iface->modem_after_sim_unlock_finish = modem_after_sim_unlock_finish; iface->modem_power_down = modem_power_down; iface->modem_power_down_finish = modem_power_down_finish; iface->load_access_technologies = load_access_technologies; iface->load_access_technologies_finish = load_access_technologies_finish; iface->load_supported_modes = load_supported_modes; iface->load_supported_modes_finish = load_supported_modes_finish; iface->load_current_modes = load_current_modes; iface->load_current_modes_finish = load_current_modes_finish; iface->set_current_modes = set_current_modes; iface->set_current_modes_finish = set_current_modes_finish; iface->load_unlock_retries = load_unlock_retries; iface->load_unlock_retries_finish = load_unlock_retries_finish; } static void iface_modem_3gpp_init (MMIfaceModem3gpp *iface) { iface_modem_3gpp_parent = g_type_interface_peek_parent (iface); iface->setup_unsolicited_events = modem_3gpp_setup_unsolicited_events; iface->setup_unsolicited_events_finish = modem_3gpp_setup_cleanup_unsolicited_events_finish; iface->cleanup_unsolicited_events = modem_3gpp_cleanup_unsolicited_events; iface->cleanup_unsolicited_events_finish = modem_3gpp_setup_cleanup_unsolicited_events_finish; } static void mm_broadband_modem_zte_class_init (MMBroadbandModemZteClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); MMBroadbandModemClass *broadband_modem_class = MM_BROADBAND_MODEM_CLASS (klass); g_type_class_add_private (object_class, sizeof (MMBroadbandModemZtePrivate)); object_class->finalize = finalize; broadband_modem_class->setup_ports = setup_ports; } ModemManager-1.23.4-dev/src/plugins/zte/mm-broadband-modem-zte.h000066400000000000000000000046041456466623000243760ustar00rootroot00000000000000/* -*- 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 - 2012 Red Hat, Inc. * Copyright (C) 2012 Aleksander Morgado */ #ifndef MM_BROADBAND_MODEM_ZTE_H #define MM_BROADBAND_MODEM_ZTE_H #include "mm-broadband-modem.h" #define MM_TYPE_BROADBAND_MODEM_ZTE (mm_broadband_modem_zte_get_type ()) #define MM_BROADBAND_MODEM_ZTE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_BROADBAND_MODEM_ZTE, MMBroadbandModemZte)) #define MM_BROADBAND_MODEM_ZTE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_BROADBAND_MODEM_ZTE, MMBroadbandModemZteClass)) #define MM_IS_BROADBAND_MODEM_ZTE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_BROADBAND_MODEM_ZTE)) #define MM_IS_BROADBAND_MODEM_ZTE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_BROADBAND_MODEM_ZTE)) #define MM_BROADBAND_MODEM_ZTE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_BROADBAND_MODEM_ZTE, MMBroadbandModemZteClass)) typedef struct _MMBroadbandModemZte MMBroadbandModemZte; typedef struct _MMBroadbandModemZteClass MMBroadbandModemZteClass; typedef struct _MMBroadbandModemZtePrivate MMBroadbandModemZtePrivate; struct _MMBroadbandModemZte { MMBroadbandModem parent; MMBroadbandModemZtePrivate *priv; }; struct _MMBroadbandModemZteClass{ MMBroadbandModemClass parent; }; GType mm_broadband_modem_zte_get_type (void); MMBroadbandModemZte *mm_broadband_modem_zte_new (const gchar *device, const gchar *physdev, const gchar **drivers, const gchar *plugin, guint16 vendor_id, guint16 product_id); #endif /* MM_BROADBAND_MODEM_ZTE_H */ ModemManager-1.23.4-dev/src/plugins/zte/mm-common-zte.c000066400000000000000000000114501456466623000226430ustar00rootroot00000000000000/* -*- 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 - 2012 Red Hat, Inc. * Copyright (C) 2012 Aleksander Morgado */ #include "ModemManager.h" #include "mm-modem-helpers.h" #include "mm-iface-modem.h" #include "mm-iface-modem-3gpp.h" #include "mm-common-zte.h" struct _MMCommonZteUnsolicitedSetup { /* Regex for access-technology related notifications */ GRegex *zpasr_regex; /* Requests to always ignore */ GRegex *zusimr_regex; /* SMS related */ GRegex *zdonr_regex; /* Unsolicited operator display */ GRegex *zpstm_regex; /* SIM request to Build Main Menu */ GRegex *zend_regex; /* SIM request to Rebuild Main Menu */ }; MMCommonZteUnsolicitedSetup * mm_common_zte_unsolicited_setup_new (void) { MMCommonZteUnsolicitedSetup *setup; setup = g_new (MMCommonZteUnsolicitedSetup, 1); /* Prepare regular expressions to setup */ setup->zusimr_regex = g_regex_new ("\\r\\n\\+ZUSIMR:(.*)\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); g_assert (setup->zusimr_regex != NULL); setup->zdonr_regex = g_regex_new ("\\r\\n\\+ZDONR: (.*)\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); g_assert (setup->zdonr_regex != NULL); setup->zpasr_regex = g_regex_new ("\\r\\n\\+ZPASR:\\s*(.*)\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); g_assert (setup->zpasr_regex != NULL); setup->zpstm_regex = g_regex_new ("\\r\\n\\+ZPSTM: (.*)\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); g_assert (setup->zpstm_regex != NULL); setup->zend_regex = g_regex_new ("\\r\\n\\+ZEND\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); g_assert (setup->zend_regex != NULL); return setup; } void mm_common_zte_unsolicited_setup_free (MMCommonZteUnsolicitedSetup *setup) { g_regex_unref (setup->zusimr_regex); g_regex_unref (setup->zdonr_regex); g_regex_unref (setup->zpasr_regex); g_regex_unref (setup->zpstm_regex); g_regex_unref (setup->zend_regex); g_free (setup); } static void zpasr_received (MMPortSerialAt *port, GMatchInfo *info, MMBroadbandModem *self) { MMModemAccessTechnology act = MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN; gchar *str; str = g_match_info_fetch (info, 1); if (str) { act = mm_string_to_access_tech (str); g_free (str); } mm_iface_modem_update_access_technologies (MM_IFACE_MODEM (self), act, MM_IFACE_MODEM_3GPP_ALL_ACCESS_TECHNOLOGIES_MASK); } void mm_common_zte_set_unsolicited_events_handlers (MMBroadbandModem *self, MMCommonZteUnsolicitedSetup *setup, gboolean enable) { MMPortSerialAt *ports[2]; guint i; ports[0] = mm_base_modem_peek_port_primary (MM_BASE_MODEM (self)); ports[1] = mm_base_modem_peek_port_secondary (MM_BASE_MODEM (self)); /* Enable unsolicited events in given port */ for (i = 0; i < G_N_ELEMENTS (ports); i++) { if (!ports[i]) continue; /* Access technology related */ mm_port_serial_at_add_unsolicited_msg_handler ( ports[i], setup->zpasr_regex, enable ? (MMPortSerialAtUnsolicitedMsgFn)zpasr_received : NULL, enable ? self : NULL, NULL); /* Other unsolicited events to always ignore */ if (!enable) { mm_port_serial_at_add_unsolicited_msg_handler ( ports[i], setup->zusimr_regex, NULL, NULL, NULL); mm_port_serial_at_add_unsolicited_msg_handler ( ports[i], setup->zdonr_regex, NULL, NULL, NULL); mm_port_serial_at_add_unsolicited_msg_handler ( ports[i], setup->zpstm_regex, NULL, NULL, NULL); mm_port_serial_at_add_unsolicited_msg_handler ( ports[i], setup->zend_regex, NULL, NULL, NULL); } } } ModemManager-1.23.4-dev/src/plugins/zte/mm-common-zte.h000066400000000000000000000024671456466623000226600ustar00rootroot00000000000000/* -*- 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 - 2012 Red Hat, Inc. * Copyright (C) 2012 Aleksander Morgado */ #ifndef MM_COMMON_ZTE_H #define MM_COMMON_ZTE_H #include "mm-broadband-modem.h" typedef struct _MMCommonZteUnsolicitedSetup MMCommonZteUnsolicitedSetup; MMCommonZteUnsolicitedSetup *mm_common_zte_unsolicited_setup_new (void); void mm_common_zte_unsolicited_setup_free (MMCommonZteUnsolicitedSetup *setup); void mm_common_zte_set_unsolicited_events_handlers (MMBroadbandModem *self, MMCommonZteUnsolicitedSetup *setup, gboolean enable); #endif /* MM_COMMON_ZTE_H */ ModemManager-1.23.4-dev/src/plugins/zte/mm-plugin-zte.c000066400000000000000000000151301456466623000226500ustar00rootroot00000000000000/* -*- 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 - 2012 Red Hat, Inc. * Copyright (C) 2012 Aleksander Morgado */ #include #include #define _LIBMM_INSIDE_MM #include #include "mm-log-object.h" #include "mm-plugin-common.h" #include "mm-broadband-modem-zte.h" #include "mm-broadband-modem-zte-icera.h" #if defined WITH_QMI #include "mm-broadband-modem-qmi.h" #endif #if defined WITH_MBIM #include "mm-broadband-modem-mbim.h" #endif #define MM_TYPE_PLUGIN_ZTE mm_plugin_zte_get_type () MM_DEFINE_PLUGIN (ZTE, zte, Zte) /*****************************************************************************/ /* Custom commands for AT probing */ /* 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. * * We use this command also for checking AT support in the port. */ static const MMPortProbeAtCommand custom_at_probe[] = { { "ATE0+CPMS?", 3, mm_port_probe_response_processor_is_at }, { "ATE0+CPMS?", 3, mm_port_probe_response_processor_is_at }, { "ATE0+CPMS?", 3, mm_port_probe_response_processor_is_at }, { NULL } }; /*****************************************************************************/ static MMBaseModem * create_modem (MMPlugin *self, const gchar *uid, const gchar *physdev, const gchar **drivers, guint16 vendor, guint16 product, guint16 subsystem_vendor, GList *probes, GError **error) { #if defined WITH_QMI if (mm_port_probe_list_has_qmi_port (probes)) { mm_obj_dbg (self, "QMI-powered ZTE modem found..."); return MM_BASE_MODEM (mm_broadband_modem_qmi_new (uid, physdev, drivers, mm_plugin_get_name (self), vendor, product)); } #endif #if defined WITH_MBIM if (mm_port_probe_list_has_mbim_port (probes)) { mm_obj_dbg (self, "MBIM-powered ZTE modem found..."); return MM_BASE_MODEM (mm_broadband_modem_mbim_new (uid, physdev, drivers, mm_plugin_get_name (self), vendor, product)); } #endif if (mm_port_probe_list_is_icera (probes)) return MM_BASE_MODEM (mm_broadband_modem_zte_icera_new (uid, physdev, drivers, mm_plugin_get_name (self), vendor, product)); return MM_BASE_MODEM (mm_broadband_modem_zte_new (uid, physdev, drivers, mm_plugin_get_name (self), vendor, product)); } static gboolean grab_port (MMPlugin *self, MMBaseModem *modem, MMPortProbe *probe, GError **error) { MMKernelDevice *port; MMPortType ptype; port = mm_port_probe_peek_port (probe); /* Ignore net ports on non-Icera non-QMI modems */ ptype = mm_port_probe_get_port_type (probe); if (ptype == MM_PORT_TYPE_NET && MM_IS_BROADBAND_MODEM_ZTE (modem)) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "Ignoring net port in ZTE modem"); return FALSE; } if (mm_kernel_device_get_global_property_as_boolean (port, "ID_MM_ZTE_ICERA_DHCP")) { mm_obj_dbg (self, "icera-based modem will use DHCP"); g_object_set (modem, MM_BROADBAND_MODEM_ICERA_DEFAULT_IP_METHOD, MM_BEARER_IP_METHOD_DHCP, NULL); } return mm_base_modem_grab_port (modem, port, ptype, MM_PORT_SERIAL_AT_FLAG_NONE, error); } /*****************************************************************************/ MM_PLUGIN_NAMED_CREATOR_SCOPE MMPlugin * mm_plugin_create_zte (void) { static const gchar *subsystems[] = { "tty", "net", "usbmisc", NULL }; static const guint16 vendor_ids[] = { 0x19d2, 0 }; return MM_PLUGIN ( g_object_new (MM_TYPE_PLUGIN_ZTE, MM_PLUGIN_NAME, MM_MODULE_NAME, MM_PLUGIN_ALLOWED_SUBSYSTEMS, subsystems, MM_PLUGIN_ALLOWED_VENDOR_IDS, vendor_ids, MM_PLUGIN_CUSTOM_AT_PROBE, custom_at_probe, MM_PLUGIN_ALLOWED_AT, TRUE, MM_PLUGIN_REQUIRED_QCDM, TRUE, MM_PLUGIN_ALLOWED_QMI, TRUE, MM_PLUGIN_ALLOWED_MBIM, TRUE, MM_PLUGIN_ICERA_PROBE, TRUE, NULL)); } static void mm_plugin_zte_init (MMPluginZte *self) { } static void mm_plugin_zte_class_init (MMPluginZteClass *klass) { MMPluginClass *plugin_class = MM_PLUGIN_CLASS (klass); plugin_class->create_modem = create_modem; plugin_class->grab_port = grab_port; } ModemManager-1.23.4-dev/src/tests/000077500000000000000000000000001456466623000166565ustar00rootroot00000000000000ModemManager-1.23.4-dev/src/tests/meson.build000066400000000000000000000024311456466623000210200ustar00rootroot00000000000000# SPDX-License-Identifier: GPL-2.0-or-later # Copyright (C) 2021 Iñigo Martinez test_units = { 'at-serial-port': libport_dep, 'charsets': libhelpers_dep, 'error-helpers': libhelpers_dep, 'kernel-device-helpers': libkerneldevice_dep, 'modem-helpers': libhelpers_dep, 'sms-part-3gpp': libhelpers_dep, 'sms-part-cdma': libhelpers_dep, 'udev-rules': libkerneldevice_dep, } deps = [ libport_dep, libqcdm_dep, util_dep, ] test_units += {'qcdm-serial-port': deps} if enable_qmi test_units += {'modem-helpers-qmi': libkerneldevice_dep} endif foreach test_unit, test_deps: test_units test_name = 'test-' + test_unit exe = executable( test_name, sources: test_name + '.c', include_directories: top_inc, dependencies: test_deps, c_args: '-DTESTUDEVRULESDIR="@0@"'.format(src_dir) ) test(test_name, exe) endforeach if get_option('fuzzer') fuzzer_tests = ['test-sms-part-3gpp-fuzzer', 'test-sms-part-3gpp-tr-fuzzer', 'test-sms-part-cdma-fuzzer'] foreach fuzzer_test: fuzzer_tests exe = executable( fuzzer_test, sources: fuzzer_test + '.c', include_directories: top_inc, dependencies: libhelpers_dep, link_args : '-fsanitize=fuzzer', ) endforeach endifModemManager-1.23.4-dev/src/tests/test-at-serial-port.c000066400000000000000000000127071456466623000226510ustar00rootroot00000000000000/* -*- 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) 2012 Aleksander Morgado */ #include #include #include #include "mm-port-serial-at.h" #include "mm-serial-parsers.h" #include "mm-log-test.h" typedef struct { const gchar *original; const gchar *without_echo; } EchoRemovalTest; typedef struct { const gchar *response; const gboolean found; const gboolean expected_error; } ParseResponseTest; static const EchoRemovalTest echo_removal_tests[] = { { "\r\n", "\r\n" }, { "\r", "\r" }, { "\n", "\n" }, { "this is a string that ends just with \r", "this is a string that ends just with \r" }, { "this is a string that ends just with \n", "this is a string that ends just with \n" }, { "\r\nthis is valid", "\r\nthis is valid" }, { "a\r\nthis is valid", "\r\nthis is valid" }, { "a\r\n", "\r\n" }, { "all this string is to be considered echo\r\n", "\r\n" }, { "all this string is to be considered echo\r\nthis is valid", "\r\nthis is valid" }, { "echo echo\r\nthis is valid\r\nand so is this", "\r\nthis is valid\r\nand so is this" }, { "\r\nthis is valid\r\nand so is this", "\r\nthis is valid\r\nand so is this" }, { "\r\nthis is valid\r\nand so is this\r\n", "\r\nthis is valid\r\nand so is this\r\n" }, }; static const ParseResponseTest parse_ok_tests[] = { { "\r\nOK\r\n", TRUE, FALSE}, { "\r\nOK\r\n\r\n+CMTI: \"ME\",1\r\n", TRUE, FALSE}, { "\r\nOK\r\n\r\n+CIEV: 7,1\r\n\r\n+CRING: VOICE\r\n\r\n+CLIP: \"+0123456789\",145,,,,0\r\n", TRUE, FALSE}, { "\r\nUNKNOWN COMMAND\r\n", FALSE, FALSE} }; static const ParseResponseTest parse_error_tests[] = { { "\r\nUNKNOWN COMMAND\r\n", FALSE, FALSE}, { "\r\nERROR\r\n", TRUE, TRUE}, { "\r\nERROR\r\n\r\noooops\r\n", TRUE, TRUE}, { "\r\n+CME ERROR: raspberry\r\n", TRUE, TRUE}, { "\r\n+CME ERROR: 123\r\n", TRUE, TRUE}, { "\r\n+CME ERROR: \r\n", TRUE, TRUE}, { "\r\n+CME ERROR:\r\n", FALSE, FALSE}, { "\r\n+CMS ERROR: bananas\r\n", TRUE, TRUE}, { "\r\n+CMS ERROR: 456\r\n", TRUE, TRUE}, { "\r\n+CMS ERROR: \r\n", TRUE, TRUE}, { "\r\n+CMS ERROR:\r\n", FALSE, FALSE}, { "\r\nMODEM ERROR: 5\r\n", TRUE, TRUE}, { "\r\nMODEM ERROR: apple\r\n", FALSE, FALSE}, { "\r\nMODEM ERROR: \r\n", FALSE, FALSE}, { "\r\nMODEM ERROR:\r\n", FALSE, FALSE}, { "\r\nCOMMAND NOT SUPPORT\r\n", TRUE, TRUE}, { "\r\nCOMMAND NOT SUPPORT\r\n\r\nSomething extra\r\n", TRUE, TRUE}, { "\r\nNO CARRIER\r\n", TRUE, TRUE}, { "\r\nNO CARRIER\r\n\r\nSomething extra\r\n", TRUE, TRUE}, { "\r\nBUSY\r\n", TRUE, TRUE}, { "\r\nBUSY\r\n\r\nSomething extra\r\n", TRUE, TRUE}, { "\r\nNO ANSWER\r\n", TRUE, TRUE}, { "\r\nNO ANSWER\r\n\r\nSomething extra\r\n", TRUE, TRUE}, { "\r\nNO DIALTONE\r\n", TRUE, TRUE}, { "\r\nNO DIALTONE\r\n\r\nSomething extra\r\n", TRUE, TRUE} }; static void at_serial_echo_removal (void) { guint i; for (i = 0; i < G_N_ELEMENTS (echo_removal_tests); i++) { GByteArray *ba; /* Note that we add last NUL also to the byte array, so that we can compare * C strings later on */ ba = g_byte_array_sized_new (strlen (echo_removal_tests[i].original) + 1); g_byte_array_prepend (ba, (guint8 *)echo_removal_tests[i].original, strlen (echo_removal_tests[i].original) + 1); mm_port_serial_at_remove_echo (ba); g_assert_cmpstr ((gchar *)ba->data, ==, echo_removal_tests[i].without_echo); g_byte_array_unref (ba); } } static void _run_parse_test (const ParseResponseTest tests[], guint number_of_tests) { guint i; gpointer parser; GError *error = NULL; gboolean found = FALSE; GString *response; for (i = 0; i < number_of_tests; i++) { parser = mm_serial_parser_v1_new (); response = g_string_new (tests[i].response); found = mm_serial_parser_v1_parse (parser, response, NULL, &error); /* Verify if we expect a match or not */ g_assert_cmpint (found, ==, tests[i].found); /* Error expected */ if (tests[i].expected_error) { g_assert (error != NULL); } /* No error expected */ else { g_assert_no_error (error); } g_string_free (response, TRUE); error = NULL ; } } static void at_serial_parse_ok (void) { _run_parse_test (parse_ok_tests, G_N_ELEMENTS(parse_ok_tests)); } static void at_serial_parse_error (void) { _run_parse_test (parse_error_tests, G_N_ELEMENTS(parse_error_tests)); } int main (int argc, char **argv) { g_test_init (&argc, &argv, NULL); g_test_add_func ("/ModemManager/AT-serial/echo-removal", at_serial_echo_removal); g_test_add_func ("/ModemManager/AT-serial/parse-ok", at_serial_parse_ok); g_test_add_func ("/ModemManager/AT-serial/parse-error", at_serial_parse_error); return g_test_run (); } ModemManager-1.23.4-dev/src/tests/test-charsets.c000066400000000000000000000713051456466623000216210ustar00rootroot00000000000000/* -*- 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 #include #include #include "mm-modem-helpers.h" #include "mm-log-test.h" static void common_test_gsm7 (const gchar *in_utf8) { guint32 packed_gsm_len = 0; guint32 unpacked_gsm_len_2 = 0; g_autoptr(GByteArray) unpacked_gsm = NULL; g_autofree guint8 *packed_gsm = NULL; guint8 *unpacked_gsm_2 = NULL; g_autoptr(GByteArray) unpacked_gsm_2_array = NULL; g_autofree gchar *built_utf8 = NULL; g_autoptr(GError) error = NULL; /* Convert to GSM */ unpacked_gsm = mm_modem_charset_bytearray_from_utf8 (in_utf8, MM_MODEM_CHARSET_GSM, FALSE, &error); g_assert_nonnull (unpacked_gsm); g_assert_no_error (error); /* Pack */ packed_gsm = mm_charset_gsm_pack (unpacked_gsm->data, unpacked_gsm->len, 0, &packed_gsm_len); g_assert_nonnull (packed_gsm); g_assert_cmpuint (packed_gsm_len, <=, unpacked_gsm->len); #if 0 { g_autofree gchar *hex_packed = NULL; /* Print */ hex_packed = mm_utils_bin2hexstr (packed_gsm, packed_gsm_len); g_print ("----------\n"); g_print ("input string: '%s'\n", in_utf8); g_print ("gsm-7 encoded string: %s\n", hex_packed); } #endif /* Unpack */ unpacked_gsm_2 = mm_charset_gsm_unpack (packed_gsm, packed_gsm_len * 8 / 7, 0, &unpacked_gsm_len_2); g_assert_nonnull (unpacked_gsm_2); unpacked_gsm_2_array = g_byte_array_new_take (unpacked_gsm_2, unpacked_gsm_len_2); /* And back to UTF-8 */ built_utf8 = mm_modem_charset_bytearray_to_utf8 (unpacked_gsm_2_array, MM_MODEM_CHARSET_GSM, FALSE, &error); g_assert_nonnull (built_utf8); g_assert_no_error (error); g_assert_cmpstr (built_utf8, ==, in_utf8); } static void test_gsm7_default_chars (void) { /* Test that a string with all the characters in the GSM 03.38 charset * are converted from UTF-8 to GSM and back to UTF-8 successfully. */ static const char *s = "@£$¥èéùìòÇ\nØø\rÅåΔ_ΦΓΛΩΠΨΣΘΞÆæßÉ !\"#¤%&'()*+,-./0123456789:;<=>?¡ABCDEFGHIJKLMNOPQRSTUVWXYZÄÖÑܧ¿abcdefghijklmnopqrstuvwxyzäöñüà"; common_test_gsm7 (s); } static void test_gsm7_extended_chars (void) { /* Test that a string with all the characters in the extended GSM 03.38 * charset are converted from UTF-8 to GSM and back to UTF-8 successfully. */ static const char *s = "\f^{}\\[~]|€"; common_test_gsm7 (s); } static void test_gsm7_mixed_chars (void) { /* Test that a string with a mix of GSM 03.38 default and extended characters * is converted from UTF-8 to GSM and back to UTF-8 successfully. */ static const char *s = "@£$¥èéùìø\fΩΠΨΣΘ{ΞÆæß(})789\\:;<=>[?¡QRS]TUÖ|Ñܧ¿abpqrstuvöñüà€"; common_test_gsm7 (s); } static void test_gsm7_unpack_basic (void) { static const guint8 gsm[] = { 0xC8, 0xF7, 0x1D, 0x14, 0x96, 0x97, 0x41, 0xF9, 0x77, 0xFD, 0x07 }; static const guint8 expected[] = { 0x48, 0x6f, 0x77, 0x20, 0x61, 0x72, 0x65, 0x20, 0x79, 0x6f, 0x75, 0x3f }; guint8 *unpacked; guint32 unpacked_len = 0; unpacked = mm_charset_gsm_unpack (gsm, (sizeof (gsm) * 8) / 7, 0, &unpacked_len); g_assert (unpacked); g_assert_cmpint (unpacked_len, ==, sizeof (expected)); g_assert_cmpint (memcmp (unpacked, expected, unpacked_len), ==, 0); g_free (unpacked); } static void test_gsm7_unpack_7_chars (void) { static const guint8 gsm[] = { 0xF1, 0x7B, 0x59, 0x4E, 0xCF, 0xD7, 0x01 }; static const guint8 expected[] = { 0x71, 0x77, 0x65, 0x72, 0x74, 0x79, 0x75}; guint8 *unpacked; guint32 unpacked_len = 0; /* Tests the edge case where there are 7 bits left in the packed * buffer but those 7 bits do not contain a character. In this case * we expect to get the number of characters that were specified. */ unpacked = mm_charset_gsm_unpack (gsm, 7 , 0, &unpacked_len); g_assert (unpacked); g_assert_cmpint (unpacked_len, ==, sizeof (expected)); g_assert_cmpint (memcmp (unpacked, expected, unpacked_len), ==, 0); g_free (unpacked); } static void test_gsm7_unpack_all_chars (void) { /* Packed array of all chars in GSM default and extended charset */ static const guint8 gsm[] = { 0x80, 0x80, 0x60, 0x40, 0x28, 0x18, 0x0E, 0x88, 0x84, 0x62, 0xC1, 0x68, 0x38, 0x1E, 0x90, 0x88, 0x64, 0x42, 0xA9, 0x58, 0x2E, 0x98, 0x8C, 0x66, 0xC3, 0xE9, 0x78, 0x3E, 0xA0, 0x90, 0x68, 0x44, 0x2A, 0x99, 0x4E, 0xA8, 0x94, 0x6A, 0xC5, 0x6A, 0xB9, 0x5E, 0xB0, 0x98, 0x6C, 0x46, 0xAB, 0xD9, 0x6E, 0xB8, 0x9C, 0x6E, 0xC7, 0xEB, 0xF9, 0x7E, 0xC0, 0xA0, 0x70, 0x48, 0x2C, 0x1A, 0x8F, 0xC8, 0xA4, 0x72, 0xC9, 0x6C, 0x3A, 0x9F, 0xD0, 0xA8, 0x74, 0x4A, 0xAD, 0x5A, 0xAF, 0xD8, 0xAC, 0x76, 0xCB, 0xED, 0x7A, 0xBF, 0xE0, 0xB0, 0x78, 0x4C, 0x2E, 0x9B, 0xCF, 0xE8, 0xB4, 0x7A, 0xCD, 0x6E, 0xBB, 0xDF, 0xF0, 0xB8, 0x7C, 0x4E, 0xAF, 0xDB, 0xEF, 0xF8, 0xBC, 0x7E, 0xCF, 0xEF, 0xFB, 0xFF, 0x1B, 0xC5, 0x86, 0xB2, 0x41, 0x6D, 0x52, 0x9B, 0xD7, 0x86, 0xB7, 0xE9, 0x6D, 0x7C, 0x1B, 0xE0, 0xA6, 0x0C }; static const guint8 ext[] = { 0x1B, 0x0A, 0x1B, 0x14, 0x1B, 0x28, 0x1B, 0x29, 0x1B, 0x2F, 0x1B, 0x3C, 0x1B, 0x3D, 0x1B, 0x3E, 0x1B, 0x40, 0x1B, 0x65 }; guint8 *unpacked; guint32 unpacked_len = 0; int i; unpacked = mm_charset_gsm_unpack (gsm, (sizeof (gsm) * 8) / 7, 0, &unpacked_len); g_assert (unpacked); g_assert_cmpint (unpacked_len, ==, 148); /* Test default chars */ for (i = 0; i < 128; i++) g_assert_cmpint (unpacked[i], ==, i); /* Text extended chars */ g_assert_cmpint (memcmp ((guint8 *) (unpacked + 128), &ext[0], sizeof (ext)), ==, 0); g_free (unpacked); } static void test_gsm7_pack_basic (void) { static const guint8 unpacked[] = { 0x48, 0x6f, 0x77, 0x20, 0x61, 0x72, 0x65, 0x20, 0x79, 0x6f, 0x75, 0x3f }; static const guint8 expected[] = { 0xC8, 0xF7, 0x1D, 0x14, 0x96, 0x97, 0x41, 0xF9, 0x77, 0xFD, 0x07 }; guint8 *packed; guint32 packed_len = 0; packed = mm_charset_gsm_pack (unpacked, sizeof (unpacked), 0, &packed_len); g_assert (packed); g_assert_cmpint (packed_len, ==, sizeof (expected)); g_assert_cmpint (memcmp (packed, expected, packed_len), ==, 0); g_free (packed); } static void test_gsm7_pack_7_chars (void) { static const guint8 unpacked[] = { 0x71, 0x77, 0x65, 0x72, 0x74, 0x79, 0x75 }; static const guint8 expected[] = { 0xF1, 0x7B, 0x59, 0x4E, 0xCF, 0xD7, 0x01 }; guint8 *packed; guint32 packed_len = 0; /* Tests the edge case where there are 7 bits left in the packed * buffer but those 7 bits do not contain a character. In this case * we expect a trailing NULL byte and the caller must know enough about * the intended message to remove it when required. */ packed = mm_charset_gsm_pack (unpacked, sizeof (unpacked), 0, &packed_len); g_assert (packed); g_assert_cmpint (packed_len, ==, sizeof (expected)); g_assert_cmpint (memcmp (packed, expected, packed_len), ==, 0); g_free (packed); } static void test_gsm7_pack_all_chars (void) { /* Packed array of all chars in GSM default and extended charset */ static const guint8 expected[] = { 0x80, 0x80, 0x60, 0x40, 0x28, 0x18, 0x0E, 0x88, 0x84, 0x62, 0xC1, 0x68, 0x38, 0x1E, 0x90, 0x88, 0x64, 0x42, 0xA9, 0x58, 0x2E, 0x98, 0x8C, 0x66, 0xC3, 0xE9, 0x78, 0x3E, 0xA0, 0x90, 0x68, 0x44, 0x2A, 0x99, 0x4E, 0xA8, 0x94, 0x6A, 0xC5, 0x6A, 0xB9, 0x5E, 0xB0, 0x98, 0x6C, 0x46, 0xAB, 0xD9, 0x6E, 0xB8, 0x9C, 0x6E, 0xC7, 0xEB, 0xF9, 0x7E, 0xC0, 0xA0, 0x70, 0x48, 0x2C, 0x1A, 0x8F, 0xC8, 0xA4, 0x72, 0xC9, 0x6C, 0x3A, 0x9F, 0xD0, 0xA8, 0x74, 0x4A, 0xAD, 0x5A, 0xAF, 0xD8, 0xAC, 0x76, 0xCB, 0xED, 0x7A, 0xBF, 0xE0, 0xB0, 0x78, 0x4C, 0x2E, 0x9B, 0xCF, 0xE8, 0xB4, 0x7A, 0xCD, 0x6E, 0xBB, 0xDF, 0xF0, 0xB8, 0x7C, 0x4E, 0xAF, 0xDB, 0xEF, 0xF8, 0xBC, 0x7E, 0xCF, 0xEF, 0xFB, 0xFF, 0x1B, 0xC5, 0x86, 0xB2, 0x41, 0x6D, 0x52, 0x9B, 0xD7, 0x86, 0xB7, 0xE9, 0x6D, 0x7C, 0x1B, 0xE0, 0xA6, 0x0C }; static const guint8 ext[] = { 0x1B, 0x0A, 0x1B, 0x14, 0x1B, 0x28, 0x1B, 0x29, 0x1B, 0x2F, 0x1B, 0x3C, 0x1B, 0x3D, 0x1B, 0x3E, 0x1B, 0x40, 0x1B, 0x65 }; guint8 *packed, c; guint32 packed_len = 0; GByteArray *unpacked; unpacked = g_byte_array_sized_new (148); for (c = 0; c < 128; c++) g_byte_array_append (unpacked, &c, 1); for (c = 0; c < sizeof (ext); c++) g_byte_array_append (unpacked, &ext[c], 1); packed = mm_charset_gsm_pack (unpacked->data, unpacked->len, 0, &packed_len); g_assert (packed); g_assert_cmpint (packed_len, ==, sizeof (expected)); g_assert_cmpint (memcmp (packed, expected, packed_len), ==, 0); g_free (packed); g_byte_array_free (unpacked, TRUE); } static void test_gsm7_pack_24_chars (void) { static const guint8 unpacked[] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17 }; guint8 *packed; guint32 packed_len = 0; /* Tests that no empty trailing byte is added when all the 7-bit characters * are packed into an exact number of bytes. */ packed = mm_charset_gsm_pack (unpacked, sizeof (unpacked), 0, &packed_len); g_assert (packed); g_assert_cmpint (packed_len, ==, 21); g_free (packed); } static void test_gsm7_pack_last_septet_alone (void) { static const guint8 unpacked[] = { 0x54, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x72, 0x65, 0x61, 0x6C, 0x6C, 0x79, 0x20, 0x63, 0x6F, 0x6F, 0x6C, 0x20, 0x10, 0x10, 0x10, 0x10, 0x10 }; static const guint8 expected[] = { 0x54, 0x74, 0x7A, 0x0E, 0x4A, 0xCF, 0x41, 0xF2, 0x72, 0x98, 0xCD, 0xCE, 0x83, 0xC6, 0xEF, 0x37, 0x1B, 0x04, 0x81, 0x40, 0x20, 0x10 }; guint8 *packed; guint32 packed_len = 0; /* Tests that a 25-character unpacked string (where, when packed, the last * septet will be in an octet by itself) packs correctly. */ packed = mm_charset_gsm_pack (unpacked, sizeof (unpacked), 0, &packed_len); g_assert (packed); g_assert_cmpint (packed_len, ==, sizeof (expected)); g_free (packed); } static void test_gsm7_pack_7_chars_offset (void) { static const guint8 unpacked[] = { 0x68, 0x65, 0x6C, 0x6C, 0x6F, 0x10, 0x2F }; static const guint8 expected[] = { 0x00, 0x5D, 0x66, 0xB3, 0xDF, 0x90, 0x17 }; guint8 *packed; guint32 packed_len = 0; packed = mm_charset_gsm_pack (unpacked, sizeof (unpacked), 5, &packed_len); g_assert (packed); g_assert_cmpint (packed_len, ==, sizeof (expected)); g_assert_cmpint (memcmp (packed, expected, packed_len), ==, 0); g_free (packed); } static void test_str_ucs2_to_from_utf8 (void) { const gchar *src = "0054002D004D006F00620069006C0065"; g_autofree gchar *utf8 = NULL; g_autofree gchar *dst = NULL; g_autoptr(GError) error = NULL; utf8 = mm_modem_charset_str_to_utf8 (src, -1, MM_MODEM_CHARSET_UCS2, FALSE, &error); g_assert_no_error (error); g_assert_cmpstr (utf8, ==, "T-Mobile"); dst = mm_modem_charset_str_from_utf8 (utf8, MM_MODEM_CHARSET_UCS2, FALSE, &error); g_assert_no_error (error); g_assert_cmpstr (dst, ==, src); } static void test_str_gsm_to_from_utf8 (void) { const gchar *src = "T-Mobile"; g_autofree gchar *utf8 = NULL; g_autofree gchar *dst = NULL; g_autoptr(GError) error = NULL; /* Note: as long as the GSM string doesn't contain the '@' character, str_to_utf8() * and str_from_utf8() can safely be used */ utf8 = mm_modem_charset_str_to_utf8 (src, -1, MM_MODEM_CHARSET_GSM, FALSE, &error); g_assert_no_error (error); g_assert_cmpstr (utf8, ==, src); dst = mm_modem_charset_str_from_utf8 (utf8, MM_MODEM_CHARSET_GSM, FALSE, &error); g_assert_no_error (error); g_assert_cmpstr (dst, ==, src); } static void test_str_gsm_to_from_utf8_with_at (void) { /* The NULs are '@' chars, except for the trailing one which is always taken as end-of-string */ const gchar src[] = { 'T', '-', 'M', 0x00, 'o', 'b', 'i', 0x00, 'l', 'e', 0x00 }; const gchar *utf8_expected = "T-M@obi@le"; const gchar *src_translit = "T-M?obi?le"; g_autofree gchar *utf8 = NULL; g_autofree gchar *dst = NULL; g_autoptr(GError) error = NULL; /* Note: as long as the GSM string doesn't contain the '@' character, str_to_utf8() * and str_from_utf8() can safely be used */ utf8 = mm_modem_charset_str_to_utf8 (src, G_N_ELEMENTS (src), MM_MODEM_CHARSET_GSM, FALSE, &error); g_assert_no_error (error); g_assert_cmpstr (utf8, ==, utf8_expected); /* if charset conversion from UTF-8 contains '@' chars, running without transliteration * will return an error */ dst = mm_modem_charset_str_from_utf8 (utf8, MM_MODEM_CHARSET_GSM, FALSE, &error); g_assert_nonnull (error); g_assert_null (dst); g_clear_error (&error); /* with transliteration, '@'->'?' */ dst = mm_modem_charset_str_from_utf8 (utf8, MM_MODEM_CHARSET_GSM, TRUE, &error); g_assert_no_error (error); g_assert_cmpstr (dst, ==, src_translit); } struct charset_can_convert_to_test_s { const char *utf8; gboolean to_gsm; gboolean to_ira; gboolean to_8859_1; gboolean to_ucs2; gboolean to_utf16; gboolean to_pccp437; gboolean to_pcdn; }; static void test_charset_can_covert_to (void) { static const struct charset_can_convert_to_test_s charset_can_convert_to_test[] = { { .utf8 = "", .to_gsm = TRUE, .to_ira = TRUE, .to_8859_1 = TRUE, .to_ucs2 = TRUE, .to_utf16 = TRUE, .to_pccp437 = TRUE, .to_pcdn = TRUE, }, { .utf8 = " ", .to_gsm = TRUE, .to_ira = TRUE, .to_8859_1 = TRUE, .to_ucs2 = TRUE, .to_utf16 = TRUE, .to_pccp437 = TRUE, .to_pcdn = TRUE, }, { .utf8 = "some basic ascii", .to_gsm = TRUE, .to_ira = TRUE, .to_8859_1 = TRUE, .to_ucs2 = TRUE, .to_utf16 = TRUE, .to_pccp437 = TRUE, .to_pcdn = TRUE, }, { .utf8 = "ホモ・サピエンス 喂人类 katakana, chinese, english: UCS2 takes it all", .to_gsm = FALSE, .to_ira = FALSE, .to_8859_1 = FALSE, .to_ucs2 = TRUE, .to_utf16 = TRUE, .to_pccp437 = FALSE, .to_pcdn = FALSE, }, { .utf8 = "Some from the GSM7 basic set: a % Ψ Ω ñ ö è æ", .to_gsm = TRUE, .to_ira = FALSE, .to_8859_1 = FALSE, .to_ucs2 = TRUE, .to_utf16 = TRUE, .to_pccp437 = FALSE, .to_pcdn = FALSE, }, { .utf8 = "More from the GSM7 extended set: {} [] ~ € |", .to_gsm = TRUE, .to_ira = FALSE, .to_8859_1 = FALSE, .to_ucs2 = TRUE, .to_utf16 = TRUE, .to_pccp437 = FALSE, .to_pcdn = FALSE, }, { .utf8 = "patín cannot be encoded in GSM7 or IRA, but is valid UCS2, ISO-8859-1, CP437 and CP850", .to_gsm = FALSE, .to_ira = FALSE, .to_8859_1 = TRUE, .to_ucs2 = TRUE, .to_utf16 = TRUE, .to_pccp437 = TRUE, .to_pcdn = TRUE, }, { .utf8 = "ècole can be encoded in multiple ways, but not in IRA", .to_gsm = TRUE, .to_ira = FALSE, .to_8859_1 = TRUE, .to_ucs2 = TRUE, .to_utf16 = TRUE, .to_pccp437 = TRUE, .to_pcdn = TRUE, }, }; guint i; for (i = 0; i < G_N_ELEMENTS (charset_can_convert_to_test); i++) { g_debug ("testing charset conversion: '%s'", charset_can_convert_to_test[i].utf8); g_assert (mm_charset_can_convert_to (charset_can_convert_to_test[i].utf8, MM_MODEM_CHARSET_GSM) == charset_can_convert_to_test[i].to_gsm); g_assert (mm_charset_can_convert_to (charset_can_convert_to_test[i].utf8, MM_MODEM_CHARSET_IRA) == charset_can_convert_to_test[i].to_ira); g_assert (mm_charset_can_convert_to (charset_can_convert_to_test[i].utf8, MM_MODEM_CHARSET_8859_1) == charset_can_convert_to_test[i].to_8859_1); g_assert (mm_charset_can_convert_to (charset_can_convert_to_test[i].utf8, MM_MODEM_CHARSET_UCS2) == charset_can_convert_to_test[i].to_ucs2); g_assert (mm_charset_can_convert_to (charset_can_convert_to_test[i].utf8, MM_MODEM_CHARSET_UTF16) == charset_can_convert_to_test[i].to_utf16); g_assert (mm_charset_can_convert_to (charset_can_convert_to_test[i].utf8, MM_MODEM_CHARSET_PCCP437) == charset_can_convert_to_test[i].to_pccp437); g_assert (mm_charset_can_convert_to (charset_can_convert_to_test[i].utf8, MM_MODEM_CHARSET_PCDN) == charset_can_convert_to_test[i].to_pcdn); } } /********************* TEXT SPLIT TESTS *********************/ static void common_test_text_split (const gchar *text, const gchar **expected, MMModemCharset expected_charset) { gchar **out; MMModemCharset out_charset = MM_MODEM_CHARSET_UNKNOWN; guint i; out = mm_charset_util_split_text (text, &out_charset, NULL); g_assert (out != NULL); g_assert (out_charset != MM_MODEM_CHARSET_UNKNOWN); g_assert_cmpuint (g_strv_length (out), ==, g_strv_length ((gchar **)expected)); for (i = 0; out[i]; i++) { g_assert_cmpstr (out[i], ==, expected[i]); } g_strfreev (out); } static void test_text_split_short_gsm7 (void) { const gchar *text = "Hello"; const gchar *expected [] = { "Hello", NULL }; common_test_text_split (text, expected, MM_MODEM_CHARSET_GSM); } static void test_text_split_short_ucs2 (void) { const gchar *text = "你好"; /* (UTF-8) e4 bd a0 e5 a5 bd */ const gchar *expected [] = { "你好", NULL }; common_test_text_split (text, expected, MM_MODEM_CHARSET_UTF16); } static void test_text_split_short_utf16 (void) { const gchar *text = "😉"; /* U+1F609, winking face */ const gchar *expected [] = { "😉", NULL }; common_test_text_split (text, expected, MM_MODEM_CHARSET_UTF16); } static void test_text_split_max_single_pdu_gsm7 (void) { const gchar *text = "0123456789012345678901234567890123456789" "0123456789012345678901234567890123456789" "0123456789012345678901234567890123456789" "0123456789012345678901234567890123456789"; const gchar *expected [] = { "0123456789012345678901234567890123456789" "0123456789012345678901234567890123456789" "0123456789012345678901234567890123456789" "0123456789012345678901234567890123456789", NULL }; common_test_text_split (text, expected, MM_MODEM_CHARSET_GSM); } static void test_text_split_max_single_pdu_gsm7_extended_chars (void) { const gchar *text = "0123456789012345678901234567890123456789" "0123456789012345678901234567890123456789" "0123456789012345678901234567890123456789" "0123456789012345678901\\~[]{}^|€"; const gchar *expected [] = { "0123456789012345678901234567890123456789" "0123456789012345678901234567890123456789" "0123456789012345678901234567890123456789" "0123456789012345678901\\~[]{}^|€", NULL }; common_test_text_split (text, expected, MM_MODEM_CHARSET_GSM); } static void test_text_split_max_single_pdu_ucs2 (void) { /* NOTE: This chinese string contains 210 bytes when encoded in * UTF-8! But still, it can be placed into 140 bytes when in UCS-2 */ const gchar *text = "你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好" "你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好" "你好你好你好"; const gchar *expected [] = { "你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好" "你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好" "你好你好你好", NULL }; common_test_text_split (text, expected, MM_MODEM_CHARSET_UTF16); } static void test_text_split_max_single_pdu_utf16 (void) { /* NOTE: this string contains 35 Bhaiksuki characters, each of * them requiring 4 bytes both in UTF-8 and in UTF-16 (140 bytes * in total). */ const gchar *text = "𑰀𑰁𑰂𑰃𑰄𑰅𑰆𑰇𑰈𑰊𑰋𑰌𑰍𑰎𑰏𑰐𑰑𑰒𑰓𑰔𑰕𑰖𑰗𑰘𑰙𑰚𑰛𑰜𑰝𑰞𑰟𑰠𑰡𑰢𑰣"; const gchar *expected [] = { "𑰀𑰁𑰂𑰃𑰄𑰅𑰆𑰇𑰈𑰊𑰋𑰌𑰍𑰎𑰏𑰐𑰑𑰒𑰓𑰔𑰕𑰖𑰗𑰘𑰙𑰚𑰛𑰜𑰝𑰞𑰟𑰠𑰡𑰢𑰣", NULL }; common_test_text_split (text, expected, MM_MODEM_CHARSET_UTF16); } static void test_text_split_two_pdu_gsm7 (void) { const gchar *text = "0123456789012345678901234567890123456789" "0123456789012345678901234567890123456789" "0123456789012345678901234567890123456789" "01234567890123456789012345678901234567890"; const gchar *expected [] = { /* First chunk */ "0123456789012345678901234567890123456789" "0123456789012345678901234567890123456789" "0123456789012345678901234567890123456789" "012345678901234567890123456789012", /* Second chunk */ "34567890", NULL }; common_test_text_split (text, expected, MM_MODEM_CHARSET_GSM); } static void test_text_split_two_pdu_gsm7_extended_chars (void) { const gchar *text = "[123456789012345678901234567890123456789" "0123456789012345678901234567890123456789" "0123456789012345678901234567890123456789" "01234567890123456789012345678901234567890"; const gchar *expected [] = { /* First chunk */ "[123456789012345678901234567890123456789" "0123456789012345678901234567890123456789" "0123456789012345678901234567890123456789" "01234567890123456789012345678901", /* Second chunk */ "234567890", NULL }; common_test_text_split (text, expected, MM_MODEM_CHARSET_GSM); } static void test_text_split_two_pdu_gsm7_extended_chars_middle1 (void) { const gchar *text = "0123456789012345678901234567890123456789" "0123456789012345678901234567890123456789" "0123456789012345678901234567890123456789" "0123456789012345678901234567890[23456789"; const gchar *expected [] = { /* First chunk */ "0123456789012345678901234567890123456789" "0123456789012345678901234567890123456789" "0123456789012345678901234567890123456789" "0123456789012345678901234567890[", /* Second chunk */ "23456789", NULL }; common_test_text_split (text, expected, MM_MODEM_CHARSET_GSM); } static void test_text_split_two_pdu_gsm7_extended_chars_middle2 (void) { const gchar *text = "0123456789012345678901234567890123456789" "0123456789012345678901234567890123456789" "0123456789012345678901234567890123456789" "01234567890123456789012345678901]3456789"; const gchar *expected [] = { /* First chunk */ "0123456789012345678901234567890123456789" "0123456789012345678901234567890123456789" "0123456789012345678901234567890123456789" "01234567890123456789012345678901", /* Second chunk */ "]3456789", NULL }; common_test_text_split (text, expected, MM_MODEM_CHARSET_GSM); } static void test_text_split_two_pdu_ucs2 (void) { const gchar *text = "你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好" "你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好" "你好你好你好好"; const gchar *expected [] = { /* First chunk */ "你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好" "你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好" "你好你", /* Second chunk */ "好你好好", NULL }; common_test_text_split (text, expected, MM_MODEM_CHARSET_UTF16); } static void test_text_split_two_pdu_utf16 (void) { /* NOTE: this string contains 35 Bhaiksuki characters, each of * them requiring 4 bytes both in UTF-8 and in UTF-16 (140 bytes * in total) plus one ASCII char (encoded with 1 byte in UTF-8 and * 2 bytes in UTF-16), making it a total of 142 bytes when in * UTF-16 (so not fitting in one single PDU) * * When split in chunks, the last chunk will hold 2 Bhaiksuki * characters plus the last ASCII one (9 bytes in UTF-16) so that * the first chunk contains the leading 33 Bhaiksuki characters * (132 characters, less than 134) */ const gchar *text = "𑰀𑰁𑰂𑰃𑰄𑰅𑰆𑰇𑰈𑰊𑰋𑰌𑰍𑰎𑰏𑰐𑰑𑰒𑰓𑰔𑰕𑰖𑰗𑰘𑰙𑰚𑰛𑰜𑰝𑰞𑰟𑰠𑰡𑰢𑰣a"; const gchar *expected [] = { "𑰀𑰁𑰂𑰃𑰄𑰅𑰆𑰇𑰈𑰊𑰋𑰌𑰍𑰎𑰏𑰐𑰑𑰒𑰓𑰔𑰕𑰖𑰗𑰘𑰙𑰚𑰛𑰜𑰝𑰞𑰟𑰠𑰡", "𑰢𑰣a", NULL }; common_test_text_split (text, expected, MM_MODEM_CHARSET_UTF16); } int main (int argc, char **argv) { setlocale (LC_ALL, ""); g_test_init (&argc, &argv, NULL); g_test_add_func ("/MM/charsets/gsm7/default-chars", test_gsm7_default_chars); g_test_add_func ("/MM/charsets/gsm7/extended-chars", test_gsm7_extended_chars); g_test_add_func ("/MM/charsets/gsm7/mixed-chars", test_gsm7_mixed_chars); g_test_add_func ("/MM/charsets/gsm7/unpack/basic", test_gsm7_unpack_basic); g_test_add_func ("/MM/charsets/gsm7/unpack/7-chars", test_gsm7_unpack_7_chars); g_test_add_func ("/MM/charsets/gsm7/unpack/all-chars", test_gsm7_unpack_all_chars); g_test_add_func ("/MM/charsets/gsm7/pack/basic", test_gsm7_pack_basic); g_test_add_func ("/MM/charsets/gsm7/pack/7-chars", test_gsm7_pack_7_chars); g_test_add_func ("/MM/charsets/gsm7/pack/all-chars", test_gsm7_pack_all_chars); g_test_add_func ("/MM/charsets/gsm7/pack/24-chars", test_gsm7_pack_24_chars); g_test_add_func ("/MM/charsets/gsm7/pack/last-septet-alone", test_gsm7_pack_last_septet_alone); g_test_add_func ("/MM/charsets/gsm7/pack/7-chars-offset", test_gsm7_pack_7_chars_offset); g_test_add_func ("/MM/charsets/str-from-to/ucs2", test_str_ucs2_to_from_utf8); g_test_add_func ("/MM/charsets/str-from-to/gsm", test_str_gsm_to_from_utf8); g_test_add_func ("/MM/charsets/str-from-to/gsm-with-at", test_str_gsm_to_from_utf8_with_at); g_test_add_func ("/MM/charsets/can-convert-to", test_charset_can_covert_to); g_test_add_func ("/MM/charsets/text-split/gsm7/short", test_text_split_short_gsm7); g_test_add_func ("/MM/charsets/text-split/ucs2/short", test_text_split_short_ucs2); g_test_add_func ("/MM/charsets/text-split/utf16/short", test_text_split_short_utf16); g_test_add_func ("/MM/charsets/text-split/gsm7/max-single-pdu", test_text_split_max_single_pdu_gsm7); g_test_add_func ("/MM/charsets/text-split/gsm7/max-single-pdu-extended-chars", test_text_split_max_single_pdu_gsm7_extended_chars); g_test_add_func ("/MM/charsets/text-split/ucs2/max-single-pdu", test_text_split_max_single_pdu_ucs2); g_test_add_func ("/MM/charsets/text-split/utf16/max-single-pdu", test_text_split_max_single_pdu_utf16); g_test_add_func ("/MM/charsets/text-split/gsm7/two-pdu", test_text_split_two_pdu_gsm7); g_test_add_func ("/MM/charsets/text-split/gsm7/two-pdu-extended-chars", test_text_split_two_pdu_gsm7_extended_chars); g_test_add_func ("/MM/charsets/text-split/gsm7/two-pdu-extended-chars_mid1", test_text_split_two_pdu_gsm7_extended_chars_middle1); g_test_add_func ("/MM/charsets/text-split/gsm7/two-pdu-extended-chars_mid2", test_text_split_two_pdu_gsm7_extended_chars_middle2); g_test_add_func ("/MM/charsets/text-split/ucs2/two-pdu", test_text_split_two_pdu_ucs2); g_test_add_func ("/MM/charsets/text-split/utf16/two-pdu", test_text_split_two_pdu_utf16); return g_test_run (); } ModemManager-1.23.4-dev/src/tests/test-error-helpers.c000066400000000000000000000250611456466623000225740ustar00rootroot00000000000000/* -*- 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) 2019 Aleksander Morgado */ #include #include #include #include #include #include #define _LIBMM_INSIDE_MM #include #include "mm-error-helpers.h" #include "mm-log.h" #if defined WITH_QMI # include #endif #if defined WITH_MBIM # include #endif #define TEST_ERROR_HELPER(ERROR_CAPS,ERROR_SMALL,ERROR_CAMEL) \ static void \ test_error_helpers_## ERROR_SMALL (void) \ { \ GError *error; \ gint i; \ GEnumClass *enum_class; \ \ enum_class = g_type_class_ref (MM_TYPE_ ## ERROR_CAPS); \ for (i = enum_class->minimum; i <= enum_class->maximum; i++) { \ GEnumValue *enum_value; \ \ enum_value = g_enum_get_value (enum_class, i); \ if (enum_value) { \ error = mm_## ERROR_SMALL ## _for_code ((MM##ERROR_CAMEL)i, NULL); \ g_assert_error (error, MM_ ## ERROR_CAPS, i); \ g_error_free (error); \ } \ } \ g_type_class_unref (enum_class); \ } TEST_ERROR_HELPER (CONNECTION_ERROR, connection_error, ConnectionError) TEST_ERROR_HELPER (MOBILE_EQUIPMENT_ERROR, mobile_equipment_error, MobileEquipmentError) TEST_ERROR_HELPER (MESSAGE_ERROR, message_error, MessageError) /*****************************************************************************/ static void test_error_helpers_mobile_equipment_error_for_code (void) { GError *error = NULL; /* first */ error = mm_mobile_equipment_error_for_code (MM_MOBILE_EQUIPMENT_ERROR_PHONE_FAILURE, NULL); g_assert_error (error, MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_PHONE_FAILURE); g_clear_error (&error); /* last */ error = mm_mobile_equipment_error_for_code (MM_MOBILE_EQUIPMENT_ERROR_WIRELINE_ACCESS_AREA_NOT_ALLOWED, NULL); g_assert_error (error, MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_WIRELINE_ACCESS_AREA_NOT_ALLOWED); g_clear_error (&error); /* other > 255 */ error = mm_mobile_equipment_error_for_code (256, NULL); g_assert_error (error, MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_UNKNOWN); g_clear_error (&error); } static void test_error_helpers_message_error_for_code (void) { GError *error = NULL; /* first */ error = mm_message_error_for_code (MM_MESSAGE_ERROR_ME_FAILURE, NULL); g_assert_error (error, MM_MESSAGE_ERROR, MM_MESSAGE_ERROR_ME_FAILURE); g_clear_error (&error); /* last */ error = mm_message_error_for_code (MM_MESSAGE_ERROR_UNKNOWN, NULL); g_assert_error (error, MM_MESSAGE_ERROR, MM_MESSAGE_ERROR_UNKNOWN); g_clear_error (&error); /* other < 300 */ error = mm_message_error_for_code (299, NULL); g_assert_error (error, MM_MESSAGE_ERROR, MM_MESSAGE_ERROR_UNKNOWN); g_clear_error (&error); /* other > 500 */ error = mm_message_error_for_code (501, NULL); g_assert_error (error, MM_MESSAGE_ERROR, MM_MESSAGE_ERROR_UNKNOWN); g_clear_error (&error); } /*****************************************************************************/ static void test_error_helpers_mobile_equipment_error_for_string (void) { GError *error = NULL; error = mm_mobile_equipment_error_for_string ("operationnotallowed", NULL); g_assert_error (error, MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_NOT_ALLOWED); g_clear_error (&error); error = mm_mobile_equipment_error_for_string ("arratsaldeon", NULL); g_assert_error (error, MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_UNKNOWN); g_clear_error (&error); } static void test_error_helpers_message_error_for_string (void) { GError *error = NULL; error = mm_message_error_for_string ("operationnotallowed", NULL); g_assert_error (error, MM_MESSAGE_ERROR, MM_MESSAGE_ERROR_NOT_ALLOWED); g_clear_error (&error); error = mm_message_error_for_string ("arratsaldeon", NULL); g_assert_error (error, MM_MESSAGE_ERROR, MM_MESSAGE_ERROR_UNKNOWN); g_clear_error (&error); } /*****************************************************************************/ static void normalize_and_assert_mm_error (gboolean expect_fallback, GQuark domain, int code, const gchar *description) { g_autoptr(GError) input_error = NULL; g_autoptr(GError) normalized_error = NULL; g_debug ("Checking normalized error: %s", description); input_error = g_error_new_literal (domain, code, description); normalized_error = mm_normalize_error (input_error); g_assert (normalized_error); g_assert ((normalized_error->domain == MM_CORE_ERROR) || (normalized_error->domain == MM_MOBILE_EQUIPMENT_ERROR) || (normalized_error->domain == MM_CONNECTION_ERROR) || (normalized_error->domain == MM_SERIAL_ERROR) || (normalized_error->domain == MM_MESSAGE_ERROR) || (normalized_error->domain == MM_CDMA_ACTIVATION_ERROR) || g_error_matches (normalized_error, G_IO_ERROR, G_IO_ERROR_CANCELLED)); g_assert (expect_fallback == g_error_matches (normalized_error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED)); } static void test_error_helpers_normalize (void) { normalize_and_assert_mm_error (TRUE, G_IO_ERROR, G_IO_ERROR_FAILED, "GIO error"); normalize_and_assert_mm_error (FALSE, G_IO_ERROR, G_IO_ERROR_CANCELLED, "GIO error cancelled"); normalize_and_assert_mm_error (TRUE, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Known core error"); /* This is exactly the fallback */ normalize_and_assert_mm_error (TRUE, MM_CORE_ERROR, 123456, "Unknown core error"); normalize_and_assert_mm_error (FALSE, MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_NO_NETWORK, "Known ME error"); normalize_and_assert_mm_error (TRUE, MM_MOBILE_EQUIPMENT_ERROR, 123456789, "Unknown ME error"); normalize_and_assert_mm_error (FALSE, MM_CONNECTION_ERROR, MM_CONNECTION_ERROR_NO_CARRIER, "Known connection error"); normalize_and_assert_mm_error (TRUE, MM_CONNECTION_ERROR, 123456, "Unknown connection error"); normalize_and_assert_mm_error (FALSE, MM_SERIAL_ERROR, MM_SERIAL_ERROR_OPEN_FAILED, "Known serial error"); normalize_and_assert_mm_error (TRUE, MM_SERIAL_ERROR, 123456, "Unknown serial error"); normalize_and_assert_mm_error (FALSE, MM_MESSAGE_ERROR, MM_MESSAGE_ERROR_ME_FAILURE, "Known message error"); normalize_and_assert_mm_error (TRUE, MM_MESSAGE_ERROR, 123456, "Unknown message error"); normalize_and_assert_mm_error (FALSE, MM_CDMA_ACTIVATION_ERROR, MM_CDMA_ACTIVATION_ERROR_ROAMING, "Known CDMA activation error"); normalize_and_assert_mm_error (TRUE, MM_CDMA_ACTIVATION_ERROR, 123456, "Unknown CDMA activation error"); #if defined WITH_QMI normalize_and_assert_mm_error (FALSE, QMI_CORE_ERROR, QMI_CORE_ERROR_TIMEOUT, "Known QMI core error"); normalize_and_assert_mm_error (TRUE, QMI_CORE_ERROR, 123456, "Unknown QMI core error"); normalize_and_assert_mm_error (FALSE, QMI_PROTOCOL_ERROR, QMI_PROTOCOL_ERROR_ABORTED, "Known QMI protocol error"); normalize_and_assert_mm_error (TRUE, QMI_PROTOCOL_ERROR, 123456, "Unknown QMI protocol error"); #endif #if defined WITH_MBIM normalize_and_assert_mm_error (FALSE, MBIM_CORE_ERROR, MBIM_CORE_ERROR_ABORTED, "Known MBIM core error"); normalize_and_assert_mm_error (TRUE, MBIM_CORE_ERROR, 123456, "Unknown MBIM core error"); normalize_and_assert_mm_error (FALSE, MBIM_PROTOCOL_ERROR, MBIM_PROTOCOL_ERROR_CANCEL, "Known MBIM protocol error"); normalize_and_assert_mm_error (TRUE, MBIM_PROTOCOL_ERROR, 123456, "Unknown MBIM protocol error"); normalize_and_assert_mm_error (FALSE, MBIM_STATUS_ERROR, MBIM_STATUS_ERROR_BUSY, "Known MBIM status error"); normalize_and_assert_mm_error (TRUE, MBIM_STATUS_ERROR, 123456, "Unknown MBIM status error"); #endif } /*****************************************************************************/ int main (int argc, char **argv) { setlocale (LC_ALL, ""); g_test_init (&argc, &argv, NULL); g_test_add_func ("/MM/error-helpers/connection-error", test_error_helpers_connection_error); g_test_add_func ("/MM/error-helpers/mobile-equipment-error", test_error_helpers_mobile_equipment_error); g_test_add_func ("/MM/error-helpers/message-error", test_error_helpers_message_error); g_test_add_func ("/MM/error-helpers/mobile-equipment-error/for-code", test_error_helpers_mobile_equipment_error_for_code); g_test_add_func ("/MM/error-helpers/message-error/for-code", test_error_helpers_message_error_for_code); g_test_add_func ("/MM/error-helpers/mobile-equipment-error/for-string", test_error_helpers_mobile_equipment_error_for_string); g_test_add_func ("/MM/error-helpers/message-error/for-string", test_error_helpers_message_error_for_string); g_test_add_func ("/MM/error-helpers/normalize", test_error_helpers_normalize); return g_test_run (); } ModemManager-1.23.4-dev/src/tests/test-kernel-device-helpers.c000066400000000000000000000074571456466623000241710ustar00rootroot00000000000000/* -*- 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) 2021 Aleksander Morgado */ #include #include #include #include #include #define _LIBMM_INSIDE_MM #include #include "mm-kernel-device-helpers.h" #include "mm-log-test.h" /*****************************************************************************/ typedef struct { const gchar *pattern; const gchar *str; gboolean match; } StringMatchTest; static const StringMatchTest string_match_tests[] = { { .pattern = "/sys/devices/pci0000:00/0000:00:1f.6/net/eno1", .str = "/sys/devices/pci0000:00/0000:00:1f.6/net/eno1", .match = TRUE, }, { .pattern = "/sys/devices/pci0000:00/0000:00:1f.6/net/*", .str = "/sys/devices/pci0000:00/0000:00:1f.6/net/eno1", .match = TRUE, }, { .pattern = "*MBIM", .str = "wwan0p1MBIM", .match = TRUE, }, /* Don't match leading extra characters */ { .pattern = "/sys/devices/pci0000:00/0000:00:1f.6/net", .str = "aaaaa/sys/devices/pci0000:00/0000:00:1f.6/net", .match = FALSE, }, /* Don't match trailing extra characters */ { .pattern = "/sys/devices/pci0000:00/0000:00:1f.6/net", .str = "/sys/devices/pci0000:00/0000:00:1f.6/netaaa", .match = FALSE, }, /* The ASTERISK in a pattern given must be treated as a wildcard by * itself, not as "the previous character 0-N times", as the input * pattern is not a regex pattern. */ { .pattern = "/sys/devices/pci0000:00/0000:00:1f.6/net/*", .str = "/sys/devices/pci0000:00/0000:00:1ff6/net", .match = FALSE, }, { .pattern = "/sys/devices/pci0000:00/0000:00:1f.6/net/*", .str = "/sys/devices/pci0000:00/0000:00:1f.6/netaaa", .match = FALSE, }, /* The DOT in a pattern given must match the exact DOT character, * as the input pattern is not a regex pattern. */ { .pattern = "/sys/devices/pci0000:00/0000:00:1f.6/net/eno1", .str = "/sys/devices/pci0000:00/0000:00:1ff6/net/eno1", .match = FALSE, }, }; static void test_string_match (void) { guint i; for (i = 0; i < G_N_ELEMENTS (string_match_tests); i++) { gboolean match; match = mm_kernel_device_generic_string_match (string_match_tests[i].str, string_match_tests[i].pattern, NULL); if (match != string_match_tests[i].match) mm_obj_warn (NULL, "string match failure: pattern '%s' should%s match str '%s'", string_match_tests[i].pattern, string_match_tests[i].match ? "" : " NOT", string_match_tests[i].str); g_assert_cmpuint (match, ==, string_match_tests[i].match); } } /*****************************************************************************/ int main (int argc, char **argv) { setlocale (LC_ALL, ""); g_test_init (&argc, &argv, NULL); g_test_add_func ("/MM/kernel-device-helpers/string-match", test_string_match); return g_test_run (); } ModemManager-1.23.4-dev/src/tests/test-modem-helpers-qmi.c000066400000000000000000002766241456466623000233450ustar00rootroot00000000000000/* -*- 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) 2012 Lanedo GmbH. * Copyright (c) 2021-2022 Qualcomm Innovation Center, Inc. */ #include #include #include #include #include #include #define _LIBMM_INSIDE_MM #include #include "mm-enums-types.h" #include "mm-flags-types.h" #include "mm-modem-helpers-qmi.h" #include "mm-log-test.h" static void test_current_capabilities_expected (MMQmiCurrentCapabilitiesContext *ctx, MMModemCapability expected) { MMModemCapability built; g_autofree gchar *expected_str = NULL; g_autofree gchar *built_str = NULL; built = mm_current_capability_from_qmi_current_capabilities_context (ctx, NULL); expected_str = mm_modem_capability_build_string_from_mask (expected); built_str = mm_modem_capability_build_string_from_mask (built); /* compare strings, so that the error shows the string values as well */ g_assert_cmpstr (built_str, ==, expected_str); } static void test_supported_capabilities_expected (MMQmiSupportedCapabilitiesContext *ctx, const MMModemCapability *expected_capabilities, guint n_expected_capabilities) { g_autoptr(GArray) built = NULL; g_autofree gchar *expected_str = NULL; g_autofree gchar *built_str = NULL; built = mm_supported_capabilities_from_qmi_supported_capabilities_context (ctx, NULL); expected_str = mm_common_build_capabilities_string (expected_capabilities, n_expected_capabilities); built_str = mm_common_build_capabilities_string ((MMModemCapability *)built->data, built->len); /* compare strings, so that the error shows the string values as well */ g_assert_cmpstr (built_str, ==, expected_str); } static void test_supported_modes_expected (MMQmiSupportedModesContext *ctx, const MMModemModeCombination *expected_modes, guint n_expected_modes) { g_autoptr(GArray) built = NULL; g_autofree gchar *expected_str = NULL; g_autofree gchar *built_str = NULL; built = mm_supported_modes_from_qmi_supported_modes_context (ctx, NULL); expected_str = mm_common_build_mode_combinations_string (expected_modes, n_expected_modes); built_str = mm_common_build_mode_combinations_string ((MMModemModeCombination *)built->data, built->len); /* compare strings, so that the error shows the string values as well */ g_assert_cmpstr (built_str, ==, expected_str); } /*****************************************************************************/ /* UML290: * ∘ +GCAP: +CIS707-A, CIS-856, CIS-856-A, +CGSM, +CLTE2 * ∘ +WS46: (12,22,25) * ∘ DMS GetCapa: Networks: 'cdma20001x, evdo, gsm, umts, lte' (always) * ∘ NAS TP: unsupported * ∘ NAS SSP: Band pref & LTE band pref always given * ∘ QCDM -> CDMA/EVDO = NAS SSP: Mode preference: 'cdma-1x, cdma-1xevdo' * ∘ QCDM -> GSM/UMTS = NAS SSP: Mode preference: 'gsm, umts' * ∘ QCDM -> Automatic = NAS SSP: no mode pref TLV given * ∘ QCDM -> LTE-only = NAS SSP: Mode preference: 'lte' */ static void test_current_capabilities_uml290 (void) { MMQmiCurrentCapabilitiesContext ctx; ctx.multimode = TRUE; /* QCDM -> CDMA/EVDO */ ctx.nas_ssp_mode_preference_mask = (QMI_NAS_RAT_MODE_PREFERENCE_CDMA_1X | QMI_NAS_RAT_MODE_PREFERENCE_CDMA_1XEVDO); ctx.nas_tp_mask = 0; /* Unsupported */ ctx.dms_capabilities = (MM_MODEM_CAPABILITY_GSM_UMTS | MM_MODEM_CAPABILITY_CDMA_EVDO | MM_MODEM_CAPABILITY_LTE); test_current_capabilities_expected (&ctx, (MM_MODEM_CAPABILITY_CDMA_EVDO | MM_MODEM_CAPABILITY_LTE)); /* QCDM -> GSM/UMTS */ ctx.nas_ssp_mode_preference_mask = (QMI_NAS_RAT_MODE_PREFERENCE_GSM | QMI_NAS_RAT_MODE_PREFERENCE_UMTS); ctx.nas_tp_mask = 0; /* Unsupported */ ctx.dms_capabilities = (MM_MODEM_CAPABILITY_GSM_UMTS | MM_MODEM_CAPABILITY_CDMA_EVDO | MM_MODEM_CAPABILITY_LTE); test_current_capabilities_expected (&ctx, (MM_MODEM_CAPABILITY_GSM_UMTS | MM_MODEM_CAPABILITY_LTE)); /* QCDM -> Automatic */ ctx.nas_ssp_mode_preference_mask = 0; ctx.nas_tp_mask = 0; /* Unsupported */ ctx.dms_capabilities = (MM_MODEM_CAPABILITY_GSM_UMTS | MM_MODEM_CAPABILITY_CDMA_EVDO | MM_MODEM_CAPABILITY_LTE); test_current_capabilities_expected (&ctx, (MM_MODEM_CAPABILITY_GSM_UMTS | MM_MODEM_CAPABILITY_CDMA_EVDO | MM_MODEM_CAPABILITY_LTE)); } static void test_supported_capabilities_uml290 (void) { MMQmiSupportedCapabilitiesContext ctx; static const MMModemCapability expected_capabilities[] = { MM_MODEM_CAPABILITY_GSM_UMTS | MM_MODEM_CAPABILITY_LTE, MM_MODEM_CAPABILITY_CDMA_EVDO | MM_MODEM_CAPABILITY_LTE, MM_MODEM_CAPABILITY_LTE, MM_MODEM_CAPABILITY_GSM_UMTS | MM_MODEM_CAPABILITY_CDMA_EVDO | MM_MODEM_CAPABILITY_LTE, }; ctx.multimode = TRUE; ctx.nas_ssp_supported = TRUE; ctx.nas_tp_supported = FALSE; ctx.dms_capabilities = (MM_MODEM_CAPABILITY_GSM_UMTS | MM_MODEM_CAPABILITY_CDMA_EVDO | MM_MODEM_CAPABILITY_LTE); test_supported_capabilities_expected (&ctx, expected_capabilities, G_N_ELEMENTS (expected_capabilities)); } static void test_supported_modes_uml290_cdma_evdo_gsm_umts_lte (void) { MMQmiSupportedModesContext ctx; static const MMModemModeCombination expected_modes[] = { /* we MUST not have 4G-only in multimode devices */ { .allowed = MM_MODEM_MODE_2G, .preferred = MM_MODEM_MODE_NONE }, { .allowed = MM_MODEM_MODE_3G, .preferred = MM_MODEM_MODE_NONE }, { .allowed = MM_MODEM_MODE_2G | MM_MODEM_MODE_3G, .preferred = MM_MODEM_MODE_3G }, { .allowed = MM_MODEM_MODE_2G | MM_MODEM_MODE_3G, .preferred = MM_MODEM_MODE_2G }, { .allowed = MM_MODEM_MODE_2G | MM_MODEM_MODE_4G, .preferred = MM_MODEM_MODE_4G }, { .allowed = MM_MODEM_MODE_2G | MM_MODEM_MODE_4G, .preferred = MM_MODEM_MODE_2G }, { .allowed = MM_MODEM_MODE_3G | MM_MODEM_MODE_4G, .preferred = MM_MODEM_MODE_4G }, { .allowed = MM_MODEM_MODE_3G | MM_MODEM_MODE_4G, .preferred = MM_MODEM_MODE_3G }, { .allowed = MM_MODEM_MODE_2G | MM_MODEM_MODE_3G | MM_MODEM_MODE_4G, .preferred = MM_MODEM_MODE_4G }, { .allowed = MM_MODEM_MODE_2G | MM_MODEM_MODE_3G | MM_MODEM_MODE_4G, .preferred = MM_MODEM_MODE_3G }, { .allowed = MM_MODEM_MODE_2G | MM_MODEM_MODE_3G | MM_MODEM_MODE_4G, .preferred = MM_MODEM_MODE_2G }, }; ctx.multimode = TRUE; ctx.all = MM_MODEM_MODE_2G | MM_MODEM_MODE_3G | MM_MODEM_MODE_4G; ctx.nas_ssp_supported = TRUE; ctx.nas_tp_supported = FALSE; ctx.current_capabilities = (MM_MODEM_CAPABILITY_GSM_UMTS | MM_MODEM_CAPABILITY_CDMA_EVDO | MM_MODEM_CAPABILITY_LTE); test_supported_modes_expected (&ctx, expected_modes, G_N_ELEMENTS (expected_modes)); } static void test_supported_modes_uml290_cdma_evdo_lte (void) { MMQmiSupportedModesContext ctx; static const MMModemModeCombination expected_modes[] = { /* we MUST not have 4G-only in multimode devices */ { .allowed = MM_MODEM_MODE_2G, .preferred = MM_MODEM_MODE_NONE }, { .allowed = MM_MODEM_MODE_3G, .preferred = MM_MODEM_MODE_NONE }, { .allowed = MM_MODEM_MODE_2G | MM_MODEM_MODE_3G, .preferred = MM_MODEM_MODE_3G }, { .allowed = MM_MODEM_MODE_2G | MM_MODEM_MODE_3G, .preferred = MM_MODEM_MODE_2G }, { .allowed = MM_MODEM_MODE_2G | MM_MODEM_MODE_4G, .preferred = MM_MODEM_MODE_4G }, { .allowed = MM_MODEM_MODE_2G | MM_MODEM_MODE_4G, .preferred = MM_MODEM_MODE_2G }, { .allowed = MM_MODEM_MODE_3G | MM_MODEM_MODE_4G, .preferred = MM_MODEM_MODE_4G }, { .allowed = MM_MODEM_MODE_3G | MM_MODEM_MODE_4G, .preferred = MM_MODEM_MODE_3G }, { .allowed = MM_MODEM_MODE_2G | MM_MODEM_MODE_3G | MM_MODEM_MODE_4G, .preferred = MM_MODEM_MODE_4G }, { .allowed = MM_MODEM_MODE_2G | MM_MODEM_MODE_3G | MM_MODEM_MODE_4G, .preferred = MM_MODEM_MODE_3G }, { .allowed = MM_MODEM_MODE_2G | MM_MODEM_MODE_3G | MM_MODEM_MODE_4G, .preferred = MM_MODEM_MODE_2G }, }; ctx.multimode = TRUE; ctx.all = MM_MODEM_MODE_2G | MM_MODEM_MODE_3G | MM_MODEM_MODE_4G; ctx.nas_ssp_supported = TRUE; ctx.nas_tp_supported = FALSE; ctx.current_capabilities = (MM_MODEM_CAPABILITY_CDMA_EVDO | MM_MODEM_CAPABILITY_LTE); test_supported_modes_expected (&ctx, expected_modes, G_N_ELEMENTS (expected_modes)); } static void test_supported_modes_uml290_gsm_umts_lte (void) { MMQmiSupportedModesContext ctx; static const MMModemModeCombination expected_modes[] = { /* we MUST not have 4G-only in multimode devices */ { .allowed = MM_MODEM_MODE_2G, .preferred = MM_MODEM_MODE_NONE }, { .allowed = MM_MODEM_MODE_3G, .preferred = MM_MODEM_MODE_NONE }, { .allowed = MM_MODEM_MODE_2G | MM_MODEM_MODE_3G, .preferred = MM_MODEM_MODE_3G }, { .allowed = MM_MODEM_MODE_2G | MM_MODEM_MODE_3G, .preferred = MM_MODEM_MODE_2G }, { .allowed = MM_MODEM_MODE_2G | MM_MODEM_MODE_4G, .preferred = MM_MODEM_MODE_4G }, { .allowed = MM_MODEM_MODE_2G | MM_MODEM_MODE_4G, .preferred = MM_MODEM_MODE_2G }, { .allowed = MM_MODEM_MODE_3G | MM_MODEM_MODE_4G, .preferred = MM_MODEM_MODE_4G }, { .allowed = MM_MODEM_MODE_3G | MM_MODEM_MODE_4G, .preferred = MM_MODEM_MODE_3G }, { .allowed = MM_MODEM_MODE_2G | MM_MODEM_MODE_3G | MM_MODEM_MODE_4G, .preferred = MM_MODEM_MODE_4G }, { .allowed = MM_MODEM_MODE_2G | MM_MODEM_MODE_3G | MM_MODEM_MODE_4G, .preferred = MM_MODEM_MODE_3G }, { .allowed = MM_MODEM_MODE_2G | MM_MODEM_MODE_3G | MM_MODEM_MODE_4G, .preferred = MM_MODEM_MODE_2G }, }; ctx.multimode = TRUE; ctx.all = MM_MODEM_MODE_2G | MM_MODEM_MODE_3G | MM_MODEM_MODE_4G; ctx.nas_ssp_supported = TRUE; ctx.nas_tp_supported = FALSE; ctx.current_capabilities = (MM_MODEM_CAPABILITY_GSM_UMTS | MM_MODEM_CAPABILITY_LTE); test_supported_modes_expected (&ctx, expected_modes, G_N_ELEMENTS (expected_modes)); } static void test_supported_modes_uml290_lte (void) { MMQmiSupportedModesContext ctx; static const MMModemModeCombination expected_modes[] = { /* we MUST only have 4G-only in a multimode device with only LTE capability */ { .allowed = MM_MODEM_MODE_4G, .preferred = MM_MODEM_MODE_NONE }, }; ctx.multimode = TRUE; ctx.all = MM_MODEM_MODE_4G; ctx.nas_ssp_supported = TRUE; ctx.nas_tp_supported = FALSE; ctx.current_capabilities = MM_MODEM_CAPABILITY_LTE; test_supported_modes_expected (&ctx, expected_modes, G_N_ELEMENTS (expected_modes)); } /*****************************************************************************/ /* ADU960S: * ∘ +GCAP: +CGSM,+DS,+ES * ∘ +WS46: ERROR * ∘ NAS TP: unsupported * ∘ DMS GetCapa: Networks: 'cdma20001x, evdo, gsm, umts, lte' * ∘ NAS SSP: LTE band preference: '(null)' * ∘ (no QCDM port) */ static void test_current_capabilities_adu960s (void) { MMQmiCurrentCapabilitiesContext ctx; ctx.multimode = TRUE; ctx.nas_ssp_mode_preference_mask = 0; /* Unsupported */ ctx.nas_tp_mask = 0; /* Unsupported */ ctx.dms_capabilities = (MM_MODEM_CAPABILITY_GSM_UMTS | MM_MODEM_CAPABILITY_CDMA_EVDO | MM_MODEM_CAPABILITY_LTE); test_current_capabilities_expected (&ctx, (MM_MODEM_CAPABILITY_GSM_UMTS | MM_MODEM_CAPABILITY_CDMA_EVDO | MM_MODEM_CAPABILITY_LTE)); } static void test_supported_capabilities_adu960s (void) { MMQmiSupportedCapabilitiesContext ctx; static const MMModemCapability expected_capabilities[] = { MM_MODEM_CAPABILITY_GSM_UMTS | MM_MODEM_CAPABILITY_CDMA_EVDO | MM_MODEM_CAPABILITY_LTE, }; ctx.multimode = TRUE; ctx.nas_ssp_supported = FALSE; ctx.nas_tp_supported = FALSE; ctx.dms_capabilities = (MM_MODEM_CAPABILITY_GSM_UMTS | MM_MODEM_CAPABILITY_CDMA_EVDO | MM_MODEM_CAPABILITY_LTE); test_supported_capabilities_expected (&ctx, expected_capabilities, G_N_ELEMENTS (expected_capabilities)); } static void test_supported_modes_adu960s (void) { MMQmiSupportedModesContext ctx; static const MMModemModeCombination expected_modes[] = { { .allowed = MM_MODEM_MODE_2G | MM_MODEM_MODE_3G | MM_MODEM_MODE_4G, .preferred = MM_MODEM_MODE_NONE }, }; ctx.multimode = TRUE; ctx.all = MM_MODEM_MODE_2G | MM_MODEM_MODE_3G | MM_MODEM_MODE_4G; ctx.nas_ssp_supported = FALSE; ctx.nas_tp_supported = FALSE; ctx.current_capabilities = (MM_MODEM_CAPABILITY_GSM_UMTS | MM_MODEM_CAPABILITY_LTE); test_supported_modes_expected (&ctx, expected_modes, G_N_ELEMENTS (expected_modes)); } /*****************************************************************************/ /* Gobi 1K with GSM firmware: * ∘ +GCAP: didn't respond to AT commands * ∘ NAS TP: Active: 'auto', duration: 'permanent' * ∘ DMS GetCapa: Networks: 'gsm, umts' * ∘ NAS SSP: unsupported * ∘ (no QCDM port) */ static void test_current_capabilities_gobi1k_gsm (void) { MMQmiCurrentCapabilitiesContext ctx; ctx.multimode = FALSE; ctx.nas_ssp_mode_preference_mask = 0; /* Unsupported */ ctx.nas_tp_mask = QMI_NAS_RADIO_TECHNOLOGY_PREFERENCE_AUTO; ctx.dms_capabilities = MM_MODEM_CAPABILITY_GSM_UMTS; test_current_capabilities_expected (&ctx, MM_MODEM_CAPABILITY_GSM_UMTS); } static void test_supported_capabilities_gobi1k_gsm (void) { MMQmiSupportedCapabilitiesContext ctx; static const MMModemCapability expected_capabilities[] = { MM_MODEM_CAPABILITY_GSM_UMTS, }; ctx.multimode = FALSE; ctx.nas_ssp_supported = FALSE; ctx.nas_tp_supported = TRUE; ctx.dms_capabilities = MM_MODEM_CAPABILITY_GSM_UMTS; test_supported_capabilities_expected (&ctx, expected_capabilities, G_N_ELEMENTS (expected_capabilities)); } static void test_supported_modes_gobi1k_gsm (void) { MMQmiSupportedModesContext ctx; static const MMModemModeCombination expected_modes[] = { { .allowed = MM_MODEM_MODE_2G, .preferred = MM_MODEM_MODE_NONE }, { .allowed = MM_MODEM_MODE_3G, .preferred = MM_MODEM_MODE_NONE }, { .allowed = MM_MODEM_MODE_2G | MM_MODEM_MODE_3G, .preferred = MM_MODEM_MODE_NONE }, }; ctx.multimode = FALSE; ctx.all = MM_MODEM_MODE_2G | MM_MODEM_MODE_3G; ctx.nas_ssp_supported = FALSE; ctx.nas_tp_supported = TRUE; ctx.current_capabilities = MM_MODEM_CAPABILITY_GSM_UMTS; test_supported_modes_expected (&ctx, expected_modes, G_N_ELEMENTS (expected_modes)); } /*****************************************************************************/ /* Gobi 1K with EVDO firmware: * ∘ +GCAP: didn't respond to AT commands * ∘ NAS TP: Active: 'auto', duration: 'permanent' * ∘ DMS GetCapa: Networks: 'cdma20001x, evdo' * ∘ NAS SSP: unsupported * ∘ (no QCDM port) */ static void test_current_capabilities_gobi1k_cdma (void) { MMQmiCurrentCapabilitiesContext ctx; ctx.multimode = FALSE; ctx.nas_ssp_mode_preference_mask = 0; /* Unsupported */ ctx.nas_tp_mask = QMI_NAS_RADIO_TECHNOLOGY_PREFERENCE_AUTO; ctx.dms_capabilities = MM_MODEM_CAPABILITY_CDMA_EVDO; test_current_capabilities_expected (&ctx, MM_MODEM_CAPABILITY_CDMA_EVDO); } static void test_supported_capabilities_gobi1k_cdma (void) { MMQmiSupportedCapabilitiesContext ctx; static const MMModemCapability expected_capabilities[] = { MM_MODEM_CAPABILITY_CDMA_EVDO, }; ctx.multimode = FALSE; ctx.nas_ssp_supported = FALSE; ctx.nas_tp_supported = TRUE; ctx.dms_capabilities = MM_MODEM_CAPABILITY_CDMA_EVDO; test_supported_capabilities_expected (&ctx, expected_capabilities, G_N_ELEMENTS (expected_capabilities)); } static void test_supported_modes_gobi1k_cdma (void) { MMQmiSupportedModesContext ctx; static const MMModemModeCombination expected_modes[] = { { .allowed = MM_MODEM_MODE_2G, .preferred = MM_MODEM_MODE_NONE }, { .allowed = MM_MODEM_MODE_3G, .preferred = MM_MODEM_MODE_NONE }, { .allowed = MM_MODEM_MODE_2G | MM_MODEM_MODE_3G, .preferred = MM_MODEM_MODE_NONE }, }; ctx.multimode = FALSE; ctx.all = MM_MODEM_MODE_2G | MM_MODEM_MODE_3G; ctx.nas_ssp_supported = FALSE; ctx.nas_tp_supported = TRUE; ctx.current_capabilities = MM_MODEM_CAPABILITY_CDMA_EVDO; test_supported_modes_expected (&ctx, expected_modes, G_N_ELEMENTS (expected_modes)); } /*****************************************************************************/ /* Gobi 2K with GSM firmware: * ∘ +GCAP: +CGSM,+DS * ∘ +WS46: ERROR * ∘ DMS GetCapa: Networks: 'gsm, umts' * ∘ NAS SSP: unsupported * ∘ QCDM -> Automatic = NAS TP: Active: 'auto', duration: 'permanent' * ∘ QCDM -> UMTS only = NAS TP: Active: '3gpp, cdma-or-wcdma', duration: 'permanent' * ∘ QCDM -> GPRS only = NAS TP: Active: '3gpp, amps-or-gsm', duration: 'permanent' */ static void test_current_capabilities_gobi2k_gsm (void) { MMQmiCurrentCapabilitiesContext ctx; ctx.multimode = FALSE; /* QCDM -> Automatic */ ctx.nas_ssp_mode_preference_mask = 0; /* Unsupported */ ctx.nas_tp_mask = QMI_NAS_RADIO_TECHNOLOGY_PREFERENCE_AUTO; ctx.dms_capabilities = MM_MODEM_CAPABILITY_GSM_UMTS; test_current_capabilities_expected (&ctx, MM_MODEM_CAPABILITY_GSM_UMTS); /* QCDM -> UMTS only */ ctx.nas_ssp_mode_preference_mask = 0; /* Unsupported */ ctx.nas_tp_mask = (QMI_NAS_RADIO_TECHNOLOGY_PREFERENCE_3GPP | QMI_NAS_RADIO_TECHNOLOGY_PREFERENCE_CDMA_OR_WCDMA); ctx.dms_capabilities = MM_MODEM_CAPABILITY_GSM_UMTS; test_current_capabilities_expected (&ctx, MM_MODEM_CAPABILITY_GSM_UMTS); /* QCDM -> GPRS only */ ctx.nas_ssp_mode_preference_mask = 0; /* Unsupported */ ctx.nas_tp_mask = (QMI_NAS_RADIO_TECHNOLOGY_PREFERENCE_3GPP | QMI_NAS_RADIO_TECHNOLOGY_PREFERENCE_AMPS_OR_GSM); ctx.dms_capabilities = MM_MODEM_CAPABILITY_GSM_UMTS; test_current_capabilities_expected (&ctx, MM_MODEM_CAPABILITY_GSM_UMTS); } static void test_supported_capabilities_gobi2k_gsm (void) { MMQmiSupportedCapabilitiesContext ctx; static const MMModemCapability expected_capabilities[] = { MM_MODEM_CAPABILITY_GSM_UMTS, }; ctx.multimode = FALSE; ctx.nas_ssp_supported = FALSE; ctx.nas_tp_supported = TRUE; ctx.dms_capabilities = MM_MODEM_CAPABILITY_GSM_UMTS; test_supported_capabilities_expected (&ctx, expected_capabilities, G_N_ELEMENTS (expected_capabilities)); } static void test_supported_modes_gobi2k_gsm (void) { MMQmiSupportedModesContext ctx; static const MMModemModeCombination expected_modes[] = { { .allowed = MM_MODEM_MODE_2G, .preferred = MM_MODEM_MODE_NONE }, { .allowed = MM_MODEM_MODE_3G, .preferred = MM_MODEM_MODE_NONE }, { .allowed = MM_MODEM_MODE_2G | MM_MODEM_MODE_3G, .preferred = MM_MODEM_MODE_NONE }, }; ctx.multimode = FALSE; ctx.all = MM_MODEM_MODE_2G | MM_MODEM_MODE_3G; ctx.nas_ssp_supported = FALSE; ctx.nas_tp_supported = TRUE; ctx.current_capabilities = MM_MODEM_CAPABILITY_GSM_UMTS; test_supported_modes_expected (&ctx, expected_modes, G_N_ELEMENTS (expected_modes)); } /*****************************************************************************/ /* Gobi 2K with CDMA firmware: * ∘ +GCAP: +CIS707-A, CIS-856, CIS-856-A, CIS707,+MS, +ES, +DS, +FCL * ∘ +WS46: ERROR * ∘ DMS GetCapa: Networks: 'cdma20001x, evdo' * ∘ NAS SSP: unsupported * ∘ QCDM -> Automatic = NAS TP: Active: 'auto', duration: 'permanent' * ∘ QCDM -> CDMA only = NAS TP: Active: '3gpp2, cdma-or-wcdma', duration: 'permanent' * ∘ QCDM -> EVDO only = NAS TP: Active: '3gpp2, hdr', duration: 'permanent' */ static void test_current_capabilities_gobi2k_cdma (void) { MMQmiCurrentCapabilitiesContext ctx; ctx.multimode = FALSE; /* QCDM -> Automatic */ ctx.nas_ssp_mode_preference_mask = 0; /* Unsupported */ ctx.nas_tp_mask = QMI_NAS_RADIO_TECHNOLOGY_PREFERENCE_AUTO; ctx.dms_capabilities = MM_MODEM_CAPABILITY_CDMA_EVDO; test_current_capabilities_expected (&ctx, MM_MODEM_CAPABILITY_CDMA_EVDO); /* QCDM -> CDMA only */ ctx.nas_ssp_mode_preference_mask = 0; /* Unsupported */ ctx.nas_tp_mask = (QMI_NAS_RADIO_TECHNOLOGY_PREFERENCE_3GPP2 | QMI_NAS_RADIO_TECHNOLOGY_PREFERENCE_CDMA_OR_WCDMA); ctx.dms_capabilities = MM_MODEM_CAPABILITY_CDMA_EVDO; test_current_capabilities_expected (&ctx, MM_MODEM_CAPABILITY_CDMA_EVDO); /* QCDM -> EVDO only */ ctx.nas_ssp_mode_preference_mask = 0; /* Unsupported */ ctx.nas_tp_mask = (QMI_NAS_RADIO_TECHNOLOGY_PREFERENCE_3GPP2 | QMI_NAS_RADIO_TECHNOLOGY_PREFERENCE_HDR); ctx.dms_capabilities = MM_MODEM_CAPABILITY_CDMA_EVDO; test_current_capabilities_expected (&ctx, MM_MODEM_CAPABILITY_CDMA_EVDO); } static void test_supported_capabilities_gobi2k_cdma (void) { MMQmiSupportedCapabilitiesContext ctx; static const MMModemCapability expected_capabilities[] = { MM_MODEM_CAPABILITY_CDMA_EVDO, }; ctx.multimode = FALSE; ctx.nas_ssp_supported = FALSE; ctx.nas_tp_supported = TRUE; ctx.dms_capabilities = MM_MODEM_CAPABILITY_CDMA_EVDO; test_supported_capabilities_expected (&ctx, expected_capabilities, G_N_ELEMENTS (expected_capabilities)); } static void test_supported_modes_gobi2k_cdma (void) { MMQmiSupportedModesContext ctx; static const MMModemModeCombination expected_modes[] = { { .allowed = MM_MODEM_MODE_2G, .preferred = MM_MODEM_MODE_NONE }, { .allowed = MM_MODEM_MODE_3G, .preferred = MM_MODEM_MODE_NONE }, { .allowed = MM_MODEM_MODE_2G | MM_MODEM_MODE_3G, .preferred = MM_MODEM_MODE_NONE }, }; ctx.multimode = FALSE; ctx.all = MM_MODEM_MODE_2G | MM_MODEM_MODE_3G; ctx.nas_ssp_supported = FALSE; ctx.nas_tp_supported = TRUE; ctx.current_capabilities = MM_MODEM_CAPABILITY_CDMA_EVDO; test_supported_modes_expected (&ctx, expected_modes, G_N_ELEMENTS (expected_modes)); } /*****************************************************************************/ /* Gobi 3K with GSM firmware: * ∘ +GCAP: +CGSM,+DS,+ES * ∘ +WS46: ERROR * ∘ DMS GetCapa: Networks: 'gsm, umts' * ∘ QCDM -> Automatic = NAS TP: Active: 'auto', duration: 'permanent' * NAS SSP: Mode preference: 'cdma-1x, cdma-1xevdo, gsm, umts' * ∘ QCDM -> UMTS only = NAS TP: Active: '3gpp, cdma-or-wcdma', duration: 'permanent' * NAS SSP: Mode preference: 'umts' * ∘ QCDM -> GPRS only = NAS TP: Active: '3gpp, amps-or-gsm', duration: 'permanent' * NAS SSP: Mode preference: 'gsm' */ static void test_current_capabilities_gobi3k_gsm (void) { MMQmiCurrentCapabilitiesContext ctx; ctx.multimode = FALSE; /* QCDM -> Automatic */ ctx.nas_ssp_mode_preference_mask = (QMI_NAS_RAT_MODE_PREFERENCE_CDMA_1X | QMI_NAS_RAT_MODE_PREFERENCE_CDMA_1XEVDO | QMI_NAS_RAT_MODE_PREFERENCE_GSM | QMI_NAS_RAT_MODE_PREFERENCE_UMTS); ctx.nas_tp_mask = QMI_NAS_RADIO_TECHNOLOGY_PREFERENCE_AUTO; ctx.dms_capabilities = MM_MODEM_CAPABILITY_GSM_UMTS; test_current_capabilities_expected (&ctx, MM_MODEM_CAPABILITY_GSM_UMTS); /* QCDM -> GSM only */ ctx.nas_ssp_mode_preference_mask = (QMI_NAS_RAT_MODE_PREFERENCE_GSM); ctx.nas_tp_mask = (QMI_NAS_RADIO_TECHNOLOGY_PREFERENCE_3GPP | QMI_NAS_RADIO_TECHNOLOGY_PREFERENCE_AMPS_OR_GSM); ctx.dms_capabilities = MM_MODEM_CAPABILITY_GSM_UMTS; test_current_capabilities_expected (&ctx, MM_MODEM_CAPABILITY_GSM_UMTS); /* QCDM -> UMTS only */ ctx.nas_ssp_mode_preference_mask = (QMI_NAS_RAT_MODE_PREFERENCE_UMTS); ctx.nas_tp_mask = (QMI_NAS_RADIO_TECHNOLOGY_PREFERENCE_3GPP | QMI_NAS_RADIO_TECHNOLOGY_PREFERENCE_CDMA_OR_WCDMA); ctx.dms_capabilities = MM_MODEM_CAPABILITY_GSM_UMTS; test_current_capabilities_expected (&ctx, MM_MODEM_CAPABILITY_GSM_UMTS); } static void test_supported_capabilities_gobi3k_gsm (void) { MMQmiSupportedCapabilitiesContext ctx; static const MMModemCapability expected_capabilities[] = { MM_MODEM_CAPABILITY_GSM_UMTS, }; ctx.multimode = FALSE; ctx.nas_ssp_supported = TRUE; ctx.nas_tp_supported = TRUE; ctx.dms_capabilities = MM_MODEM_CAPABILITY_GSM_UMTS; test_supported_capabilities_expected (&ctx, expected_capabilities, G_N_ELEMENTS (expected_capabilities)); } static void test_supported_modes_gobi3k_gsm (void) { MMQmiSupportedModesContext ctx; static const MMModemModeCombination expected_modes[] = { { .allowed = MM_MODEM_MODE_2G, .preferred = MM_MODEM_MODE_NONE }, { .allowed = MM_MODEM_MODE_3G, .preferred = MM_MODEM_MODE_NONE }, { .allowed = MM_MODEM_MODE_2G | MM_MODEM_MODE_3G, .preferred = MM_MODEM_MODE_3G }, { .allowed = MM_MODEM_MODE_2G | MM_MODEM_MODE_3G, .preferred = MM_MODEM_MODE_2G }, }; ctx.multimode = FALSE; ctx.all = MM_MODEM_MODE_2G | MM_MODEM_MODE_3G; ctx.nas_ssp_supported = TRUE; ctx.nas_tp_supported = TRUE; ctx.current_capabilities = MM_MODEM_CAPABILITY_GSM_UMTS; test_supported_modes_expected (&ctx, expected_modes, G_N_ELEMENTS (expected_modes)); } /*****************************************************************************/ /* Gobi 3K with CDMA firmware: * ∘ +GCAP: +CIS707-A, CIS-856, CIS-856-A, CIS707,+MS, +ES, +DS, +FCL * ∘ +WS46: ERROR * ∘ DMS GetCapa: Networks: 'cdma20001x, evdo' * ∘ QCDM -> Automatic = NAS TP: Active: 'auto', duration: 'permanent' * NAS SSP: Mode preference: 'cdma-1x, cdma-1xevdo, gsm, umts' * ∘ QCDM -> EVDO only = NAS TP: Active: '3gpp2, hdr', duration: 'permanent' * NAS SSP: Mode preference: 'cdma-1xevdo' * ∘ QCDM -> CDMA only = NAS TP: Active: '3gpp2, cdma-or-wcdma', duration: 'permanent' * NAS SSP: Mode preference: 'cdma-1x' */ static void test_current_capabilities_gobi3k_cdma (void) { MMQmiCurrentCapabilitiesContext ctx; ctx.multimode = FALSE; /* QCDM -> Automatic */ ctx.nas_ssp_mode_preference_mask = (QMI_NAS_RAT_MODE_PREFERENCE_CDMA_1X | QMI_NAS_RAT_MODE_PREFERENCE_CDMA_1XEVDO | QMI_NAS_RAT_MODE_PREFERENCE_GSM | QMI_NAS_RAT_MODE_PREFERENCE_UMTS); ctx.nas_tp_mask = QMI_NAS_RADIO_TECHNOLOGY_PREFERENCE_AUTO; ctx.dms_capabilities = MM_MODEM_CAPABILITY_CDMA_EVDO; test_current_capabilities_expected (&ctx, MM_MODEM_CAPABILITY_CDMA_EVDO); /* QCDM -> CDMA only */ ctx.nas_ssp_mode_preference_mask = (QMI_NAS_RAT_MODE_PREFERENCE_CDMA_1X); ctx.nas_tp_mask = (QMI_NAS_RADIO_TECHNOLOGY_PREFERENCE_3GPP2 | QMI_NAS_RADIO_TECHNOLOGY_PREFERENCE_CDMA_OR_WCDMA); ctx.dms_capabilities = MM_MODEM_CAPABILITY_CDMA_EVDO; test_current_capabilities_expected (&ctx, MM_MODEM_CAPABILITY_CDMA_EVDO); /* QCDM -> EVDO only */ ctx.nas_ssp_mode_preference_mask = (QMI_NAS_RAT_MODE_PREFERENCE_CDMA_1XEVDO); ctx.nas_tp_mask = (QMI_NAS_RADIO_TECHNOLOGY_PREFERENCE_3GPP2 | QMI_NAS_RADIO_TECHNOLOGY_PREFERENCE_HDR); ctx.dms_capabilities = MM_MODEM_CAPABILITY_CDMA_EVDO; test_current_capabilities_expected (&ctx, MM_MODEM_CAPABILITY_CDMA_EVDO); } static void test_supported_capabilities_gobi3k_cdma (void) { MMQmiSupportedCapabilitiesContext ctx; static const MMModemCapability expected_capabilities[] = { MM_MODEM_CAPABILITY_CDMA_EVDO, }; ctx.multimode = FALSE; ctx.nas_ssp_supported = TRUE; ctx.nas_tp_supported = TRUE; ctx.dms_capabilities = MM_MODEM_CAPABILITY_CDMA_EVDO; test_supported_capabilities_expected (&ctx, expected_capabilities, G_N_ELEMENTS (expected_capabilities)); } static void test_supported_modes_gobi3k_cdma (void) { MMQmiSupportedModesContext ctx; static const MMModemModeCombination expected_modes[] = { { .allowed = MM_MODEM_MODE_2G, .preferred = MM_MODEM_MODE_NONE }, { .allowed = MM_MODEM_MODE_3G, .preferred = MM_MODEM_MODE_NONE }, { .allowed = MM_MODEM_MODE_2G | MM_MODEM_MODE_3G, .preferred = MM_MODEM_MODE_3G }, { .allowed = MM_MODEM_MODE_2G | MM_MODEM_MODE_3G, .preferred = MM_MODEM_MODE_2G }, }; ctx.multimode = FALSE; ctx.all = MM_MODEM_MODE_2G | MM_MODEM_MODE_3G; ctx.nas_ssp_supported = TRUE; ctx.nas_tp_supported = TRUE; ctx.current_capabilities = MM_MODEM_CAPABILITY_CDMA_EVDO; test_supported_modes_expected (&ctx, expected_modes, G_N_ELEMENTS (expected_modes)); } /*****************************************************************************/ /* Generic with NR5G: * ∘ +GCAP: +CGSM * ∘ +WS46: (12,22,25,28,29,30,31) * ∘ DMS GetCapa: Networks: 'cdma20001x, evdo, gsm, umts, lte, 5gnr' * ∘ QMI -> Automatic = NAS TP: Active: 'auto', duration: 'permanent' * NAS SSP: Mode preference: 'cdma-1x, cdma-1xevdo, gsm, umts, lte, td-scdma, 5gnr' * ∘ QMI -> GSM only = NAS TP: Active: '3gpp, amps-or-gsm', duration: 'permanent' * NAS SSP: Mode preference: 'gsm' * ∘ QMI -> UMTS only = NAS TP: Active: '3gpp, cdma-or-wcdma', duration: 'permanent' * NAS SSP: Mode preference: 'umts' * ∘ QMI -> EVDO only = NAS TP: Active: '3gpp2, hdr', duration: 'permanent' * NAS SSP: Mode preference: 'cdma-1xevdo' * ∘ QMI -> CDMA only = NAS TP: Active: '3gpp2, cdma-or-wcdma', duration: 'permanent' * NAS SSP: Mode preference: 'cdma-1x' * ∘ QMI -> LTE only = NAS TP: Active: '3gpp, lte', duration: 'permanent' * NAS SSP: Mode preference: 'lte' * ∘ QMI -> 5GNR only = NAS TP: Active: 'auto', duration: 'permanent' * NAS SSP: Mode preference: '5gnr' */ static void test_current_capabilities_generic_nr5g_multimode (void) { MMQmiCurrentCapabilitiesContext ctx; ctx.multimode = TRUE; /* QMI -> Automatic */ ctx.nas_ssp_mode_preference_mask = (QMI_NAS_RAT_MODE_PREFERENCE_CDMA_1X | QMI_NAS_RAT_MODE_PREFERENCE_CDMA_1XEVDO | QMI_NAS_RAT_MODE_PREFERENCE_GSM | QMI_NAS_RAT_MODE_PREFERENCE_UMTS | QMI_NAS_RAT_MODE_PREFERENCE_LTE | QMI_NAS_RAT_MODE_PREFERENCE_TD_SCDMA | QMI_NAS_RAT_MODE_PREFERENCE_5GNR); ctx.nas_tp_mask = QMI_NAS_RADIO_TECHNOLOGY_PREFERENCE_AUTO; ctx.dms_capabilities = (MM_MODEM_CAPABILITY_GSM_UMTS | MM_MODEM_CAPABILITY_CDMA_EVDO | MM_MODEM_CAPABILITY_LTE | MM_MODEM_CAPABILITY_5GNR); test_current_capabilities_expected (&ctx, (MM_MODEM_CAPABILITY_GSM_UMTS | MM_MODEM_CAPABILITY_CDMA_EVDO | MM_MODEM_CAPABILITY_LTE | MM_MODEM_CAPABILITY_5GNR)); /* QMI -> GSM only */ ctx.nas_ssp_mode_preference_mask = (QMI_NAS_RAT_MODE_PREFERENCE_GSM); ctx.nas_tp_mask = (QMI_NAS_RADIO_TECHNOLOGY_PREFERENCE_3GPP | QMI_NAS_RADIO_TECHNOLOGY_PREFERENCE_AMPS_OR_GSM); ctx.dms_capabilities = (MM_MODEM_CAPABILITY_GSM_UMTS | MM_MODEM_CAPABILITY_CDMA_EVDO | MM_MODEM_CAPABILITY_LTE | MM_MODEM_CAPABILITY_5GNR); test_current_capabilities_expected (&ctx, (MM_MODEM_CAPABILITY_GSM_UMTS | MM_MODEM_CAPABILITY_LTE | MM_MODEM_CAPABILITY_5GNR)); /* QMI -> UMTS only */ ctx.nas_ssp_mode_preference_mask = (QMI_NAS_RAT_MODE_PREFERENCE_UMTS); ctx.nas_tp_mask = (QMI_NAS_RADIO_TECHNOLOGY_PREFERENCE_3GPP | QMI_NAS_RADIO_TECHNOLOGY_PREFERENCE_CDMA_OR_WCDMA); ctx.dms_capabilities = (MM_MODEM_CAPABILITY_GSM_UMTS | MM_MODEM_CAPABILITY_CDMA_EVDO | MM_MODEM_CAPABILITY_LTE | MM_MODEM_CAPABILITY_5GNR); test_current_capabilities_expected (&ctx, (MM_MODEM_CAPABILITY_GSM_UMTS | MM_MODEM_CAPABILITY_LTE | MM_MODEM_CAPABILITY_5GNR)); /* QMI -> EVDO only */ ctx.nas_ssp_mode_preference_mask = (QMI_NAS_RAT_MODE_PREFERENCE_CDMA_1XEVDO); ctx.nas_tp_mask = (QMI_NAS_RADIO_TECHNOLOGY_PREFERENCE_3GPP2 | QMI_NAS_RADIO_TECHNOLOGY_PREFERENCE_HDR); ctx.dms_capabilities = (MM_MODEM_CAPABILITY_GSM_UMTS | MM_MODEM_CAPABILITY_CDMA_EVDO | MM_MODEM_CAPABILITY_LTE | MM_MODEM_CAPABILITY_5GNR); test_current_capabilities_expected (&ctx, (MM_MODEM_CAPABILITY_CDMA_EVDO | MM_MODEM_CAPABILITY_LTE | MM_MODEM_CAPABILITY_5GNR)); /* QMI -> CDMA only */ ctx.nas_ssp_mode_preference_mask = (QMI_NAS_RAT_MODE_PREFERENCE_CDMA_1X); ctx.nas_tp_mask = (QMI_NAS_RADIO_TECHNOLOGY_PREFERENCE_3GPP2 | QMI_NAS_RADIO_TECHNOLOGY_PREFERENCE_CDMA_OR_WCDMA); ctx.dms_capabilities = (MM_MODEM_CAPABILITY_GSM_UMTS | MM_MODEM_CAPABILITY_CDMA_EVDO | MM_MODEM_CAPABILITY_LTE | MM_MODEM_CAPABILITY_5GNR); test_current_capabilities_expected (&ctx, (MM_MODEM_CAPABILITY_CDMA_EVDO | MM_MODEM_CAPABILITY_LTE | MM_MODEM_CAPABILITY_5GNR)); /* QMI -> LTE only */ ctx.nas_ssp_mode_preference_mask = (QMI_NAS_RAT_MODE_PREFERENCE_LTE); ctx.nas_tp_mask = (QMI_NAS_RADIO_TECHNOLOGY_PREFERENCE_3GPP | QMI_NAS_RADIO_TECHNOLOGY_PREFERENCE_LTE); ctx.dms_capabilities = (MM_MODEM_CAPABILITY_GSM_UMTS | MM_MODEM_CAPABILITY_CDMA_EVDO | MM_MODEM_CAPABILITY_LTE | MM_MODEM_CAPABILITY_5GNR); test_current_capabilities_expected (&ctx, (MM_MODEM_CAPABILITY_LTE | MM_MODEM_CAPABILITY_5GNR)); /* QMI -> 5GNR only */ ctx.nas_ssp_mode_preference_mask = (QMI_NAS_RAT_MODE_PREFERENCE_5GNR); ctx.nas_tp_mask = QMI_NAS_RADIO_TECHNOLOGY_PREFERENCE_AUTO; ctx.dms_capabilities = (MM_MODEM_CAPABILITY_GSM_UMTS | MM_MODEM_CAPABILITY_CDMA_EVDO | MM_MODEM_CAPABILITY_LTE | MM_MODEM_CAPABILITY_5GNR); test_current_capabilities_expected (&ctx, (MM_MODEM_CAPABILITY_LTE | MM_MODEM_CAPABILITY_5GNR)); } static void test_supported_capabilities_generic_nr5g_multimode (void) { MMQmiSupportedCapabilitiesContext ctx; static const MMModemCapability expected_capabilities[] = { MM_MODEM_CAPABILITY_GSM_UMTS | MM_MODEM_CAPABILITY_LTE | MM_MODEM_CAPABILITY_5GNR, MM_MODEM_CAPABILITY_CDMA_EVDO | MM_MODEM_CAPABILITY_LTE | MM_MODEM_CAPABILITY_5GNR, MM_MODEM_CAPABILITY_LTE | MM_MODEM_CAPABILITY_5GNR, MM_MODEM_CAPABILITY_GSM_UMTS | MM_MODEM_CAPABILITY_CDMA_EVDO | MM_MODEM_CAPABILITY_LTE | MM_MODEM_CAPABILITY_5GNR, }; ctx.multimode = TRUE; ctx.nas_ssp_supported = TRUE; ctx.nas_tp_supported = TRUE; ctx.dms_capabilities = (MM_MODEM_CAPABILITY_GSM_UMTS | MM_MODEM_CAPABILITY_CDMA_EVDO | MM_MODEM_CAPABILITY_LTE | MM_MODEM_CAPABILITY_5GNR); test_supported_capabilities_expected (&ctx, expected_capabilities, G_N_ELEMENTS (expected_capabilities)); } static void test_supported_modes_generic_nr5g_multimode (void) { MMQmiSupportedModesContext ctx; static const MMModemModeCombination expected_modes[] = { /* we MUST not have 4G-only in multimode devices */ /* we MUST not have 5G-only in multimode devices */ /* we MUST not have 4G+5G in multimode devices */ { .allowed = MM_MODEM_MODE_2G, .preferred = MM_MODEM_MODE_NONE }, { .allowed = MM_MODEM_MODE_3G, .preferred = MM_MODEM_MODE_NONE }, { .allowed = MM_MODEM_MODE_2G | MM_MODEM_MODE_3G, .preferred = MM_MODEM_MODE_3G }, { .allowed = MM_MODEM_MODE_2G | MM_MODEM_MODE_3G, .preferred = MM_MODEM_MODE_2G }, { .allowed = MM_MODEM_MODE_2G | MM_MODEM_MODE_4G, .preferred = MM_MODEM_MODE_4G }, { .allowed = MM_MODEM_MODE_2G | MM_MODEM_MODE_4G, .preferred = MM_MODEM_MODE_2G }, { .allowed = MM_MODEM_MODE_3G | MM_MODEM_MODE_4G, .preferred = MM_MODEM_MODE_4G }, { .allowed = MM_MODEM_MODE_3G | MM_MODEM_MODE_4G, .preferred = MM_MODEM_MODE_3G }, { .allowed = MM_MODEM_MODE_2G | MM_MODEM_MODE_3G | MM_MODEM_MODE_4G, .preferred = MM_MODEM_MODE_4G }, { .allowed = MM_MODEM_MODE_2G | MM_MODEM_MODE_3G | MM_MODEM_MODE_4G, .preferred = MM_MODEM_MODE_3G }, { .allowed = MM_MODEM_MODE_2G | MM_MODEM_MODE_3G | MM_MODEM_MODE_4G, .preferred = MM_MODEM_MODE_2G }, { .allowed = MM_MODEM_MODE_2G | MM_MODEM_MODE_5G, .preferred = MM_MODEM_MODE_5G }, { .allowed = MM_MODEM_MODE_2G | MM_MODEM_MODE_5G, .preferred = MM_MODEM_MODE_2G }, { .allowed = MM_MODEM_MODE_3G | MM_MODEM_MODE_5G, .preferred = MM_MODEM_MODE_5G }, { .allowed = MM_MODEM_MODE_3G | MM_MODEM_MODE_5G, .preferred = MM_MODEM_MODE_3G }, { .allowed = MM_MODEM_MODE_2G | MM_MODEM_MODE_3G | MM_MODEM_MODE_5G, .preferred = MM_MODEM_MODE_5G }, { .allowed = MM_MODEM_MODE_2G | MM_MODEM_MODE_3G | MM_MODEM_MODE_5G, .preferred = MM_MODEM_MODE_3G }, { .allowed = MM_MODEM_MODE_2G | MM_MODEM_MODE_3G | MM_MODEM_MODE_5G, .preferred = MM_MODEM_MODE_2G }, { .allowed = MM_MODEM_MODE_2G | MM_MODEM_MODE_4G | MM_MODEM_MODE_5G, .preferred = MM_MODEM_MODE_5G }, { .allowed = MM_MODEM_MODE_2G | MM_MODEM_MODE_4G | MM_MODEM_MODE_5G, .preferred = MM_MODEM_MODE_4G }, { .allowed = MM_MODEM_MODE_2G | MM_MODEM_MODE_4G | MM_MODEM_MODE_5G, .preferred = MM_MODEM_MODE_2G }, { .allowed = MM_MODEM_MODE_3G | MM_MODEM_MODE_4G | MM_MODEM_MODE_5G, .preferred = MM_MODEM_MODE_5G }, { .allowed = MM_MODEM_MODE_3G | MM_MODEM_MODE_4G | MM_MODEM_MODE_5G, .preferred = MM_MODEM_MODE_4G }, { .allowed = MM_MODEM_MODE_3G | MM_MODEM_MODE_4G | MM_MODEM_MODE_5G, .preferred = MM_MODEM_MODE_3G }, { .allowed = MM_MODEM_MODE_2G | MM_MODEM_MODE_3G | MM_MODEM_MODE_4G | MM_MODEM_MODE_5G, .preferred = MM_MODEM_MODE_5G }, { .allowed = MM_MODEM_MODE_2G | MM_MODEM_MODE_3G | MM_MODEM_MODE_4G | MM_MODEM_MODE_5G, .preferred = MM_MODEM_MODE_4G }, { .allowed = MM_MODEM_MODE_2G | MM_MODEM_MODE_3G | MM_MODEM_MODE_4G | MM_MODEM_MODE_5G, .preferred = MM_MODEM_MODE_3G }, { .allowed = MM_MODEM_MODE_2G | MM_MODEM_MODE_3G | MM_MODEM_MODE_4G | MM_MODEM_MODE_5G, .preferred = MM_MODEM_MODE_2G }, }; ctx.multimode = TRUE; ctx.all = MM_MODEM_MODE_2G | MM_MODEM_MODE_3G | MM_MODEM_MODE_4G | MM_MODEM_MODE_5G; ctx.nas_ssp_supported = TRUE; ctx.nas_tp_supported = TRUE; ctx.current_capabilities = (MM_MODEM_CAPABILITY_GSM_UMTS | MM_MODEM_CAPABILITY_CDMA_EVDO | MM_MODEM_CAPABILITY_LTE | MM_MODEM_CAPABILITY_5GNR); test_supported_modes_expected (&ctx, expected_modes, G_N_ELEMENTS (expected_modes)); } static void test_current_capabilities_generic_nr5g_only (void) { MMQmiCurrentCapabilitiesContext ctx; ctx.multimode = FALSE; ctx.nas_ssp_mode_preference_mask = QMI_NAS_RAT_MODE_PREFERENCE_5GNR; ctx.nas_tp_mask = QMI_NAS_RADIO_TECHNOLOGY_PREFERENCE_AUTO; ctx.dms_capabilities = MM_MODEM_CAPABILITY_5GNR; test_current_capabilities_expected (&ctx, MM_MODEM_CAPABILITY_5GNR); } static void test_supported_capabilities_generic_nr5g_only (void) { MMQmiSupportedCapabilitiesContext ctx; static const MMModemCapability expected_capabilities[] = { MM_MODEM_CAPABILITY_5GNR, }; ctx.multimode = FALSE; ctx.nas_ssp_supported = TRUE; ctx.nas_tp_supported = TRUE; ctx.dms_capabilities = MM_MODEM_CAPABILITY_5GNR; test_supported_capabilities_expected (&ctx, expected_capabilities, G_N_ELEMENTS (expected_capabilities)); } static void test_supported_modes_generic_nr5g_only (void) { MMQmiSupportedModesContext ctx; static const MMModemModeCombination expected_modes[] = { { .allowed = MM_MODEM_MODE_5G, .preferred = MM_MODEM_MODE_NONE }, }; ctx.multimode = FALSE; ctx.all = MM_MODEM_MODE_5G; ctx.nas_ssp_supported = TRUE; ctx.nas_tp_supported = TRUE; ctx.current_capabilities = MM_MODEM_CAPABILITY_5GNR; test_supported_modes_expected (&ctx, expected_modes, G_N_ELEMENTS (expected_modes)); } static void test_current_capabilities_generic_nr5g_lte (void) { MMQmiCurrentCapabilitiesContext ctx; ctx.multimode = FALSE; /* QMI -> Automatic */ ctx.nas_ssp_mode_preference_mask = (QMI_NAS_RAT_MODE_PREFERENCE_LTE | QMI_NAS_RAT_MODE_PREFERENCE_5GNR); ctx.nas_tp_mask = QMI_NAS_RADIO_TECHNOLOGY_PREFERENCE_AUTO; ctx.dms_capabilities = (MM_MODEM_CAPABILITY_LTE | MM_MODEM_CAPABILITY_5GNR); test_current_capabilities_expected (&ctx, (MM_MODEM_CAPABILITY_LTE | MM_MODEM_CAPABILITY_5GNR)); /* QMI -> LTE only */ ctx.nas_ssp_mode_preference_mask = (QMI_NAS_RAT_MODE_PREFERENCE_LTE); ctx.nas_tp_mask = (QMI_NAS_RADIO_TECHNOLOGY_PREFERENCE_3GPP | QMI_NAS_RADIO_TECHNOLOGY_PREFERENCE_LTE); ctx.dms_capabilities = (MM_MODEM_CAPABILITY_LTE | MM_MODEM_CAPABILITY_5GNR); test_current_capabilities_expected (&ctx, (MM_MODEM_CAPABILITY_LTE | MM_MODEM_CAPABILITY_5GNR)); /* QMI -> 5GNR only */ ctx.nas_ssp_mode_preference_mask = (QMI_NAS_RAT_MODE_PREFERENCE_5GNR); ctx.nas_tp_mask = QMI_NAS_RADIO_TECHNOLOGY_PREFERENCE_AUTO; ctx.dms_capabilities = (MM_MODEM_CAPABILITY_LTE | MM_MODEM_CAPABILITY_5GNR); test_current_capabilities_expected (&ctx, (MM_MODEM_CAPABILITY_LTE | MM_MODEM_CAPABILITY_5GNR)); } static void test_supported_capabilities_generic_nr5g_lte (void) { MMQmiSupportedCapabilitiesContext ctx; static const MMModemCapability expected_capabilities[] = { MM_MODEM_CAPABILITY_LTE | MM_MODEM_CAPABILITY_5GNR, }; ctx.multimode = FALSE; ctx.nas_ssp_supported = TRUE; ctx.nas_tp_supported = TRUE; ctx.dms_capabilities = (MM_MODEM_CAPABILITY_LTE | MM_MODEM_CAPABILITY_5GNR); test_supported_capabilities_expected (&ctx, expected_capabilities, G_N_ELEMENTS (expected_capabilities)); } static void test_supported_modes_generic_nr5g_lte (void) { MMQmiSupportedModesContext ctx; static const MMModemModeCombination expected_modes[] = { { .allowed = MM_MODEM_MODE_4G, .preferred = MM_MODEM_MODE_NONE }, { .allowed = MM_MODEM_MODE_5G, .preferred = MM_MODEM_MODE_NONE }, { .allowed = MM_MODEM_MODE_4G | MM_MODEM_MODE_5G, .preferred = MM_MODEM_MODE_5G }, { .allowed = MM_MODEM_MODE_4G | MM_MODEM_MODE_5G, .preferred = MM_MODEM_MODE_4G }, }; ctx.multimode = FALSE; ctx.all = MM_MODEM_MODE_4G | MM_MODEM_MODE_5G; ctx.nas_ssp_supported = TRUE; ctx.nas_tp_supported = TRUE; ctx.current_capabilities = (MM_MODEM_CAPABILITY_LTE | MM_MODEM_CAPABILITY_5GNR); test_supported_modes_expected (&ctx, expected_modes, G_N_ELEMENTS (expected_modes)); } static void test_current_capabilities_generic_nr5g_lte_umts (void) { MMQmiCurrentCapabilitiesContext ctx; ctx.multimode = FALSE; /* QMI -> Automatic */ ctx.nas_ssp_mode_preference_mask = (QMI_NAS_RAT_MODE_PREFERENCE_UMTS | QMI_NAS_RAT_MODE_PREFERENCE_LTE | QMI_NAS_RAT_MODE_PREFERENCE_5GNR); ctx.nas_tp_mask = QMI_NAS_RADIO_TECHNOLOGY_PREFERENCE_AUTO; ctx.dms_capabilities = (MM_MODEM_CAPABILITY_GSM_UMTS | MM_MODEM_CAPABILITY_LTE | MM_MODEM_CAPABILITY_5GNR); test_current_capabilities_expected (&ctx, (MM_MODEM_CAPABILITY_GSM_UMTS | MM_MODEM_CAPABILITY_LTE | MM_MODEM_CAPABILITY_5GNR)); /* QMI -> UMTS only */ ctx.nas_ssp_mode_preference_mask = (QMI_NAS_RAT_MODE_PREFERENCE_UMTS); ctx.nas_tp_mask = (QMI_NAS_RADIO_TECHNOLOGY_PREFERENCE_3GPP | QMI_NAS_RADIO_TECHNOLOGY_PREFERENCE_CDMA_OR_WCDMA); ctx.dms_capabilities = (MM_MODEM_CAPABILITY_GSM_UMTS | MM_MODEM_CAPABILITY_LTE | MM_MODEM_CAPABILITY_5GNR); test_current_capabilities_expected (&ctx, (MM_MODEM_CAPABILITY_GSM_UMTS | MM_MODEM_CAPABILITY_LTE | MM_MODEM_CAPABILITY_5GNR)); /* QMI -> LTE only */ ctx.nas_ssp_mode_preference_mask = (QMI_NAS_RAT_MODE_PREFERENCE_LTE); ctx.nas_tp_mask = (QMI_NAS_RADIO_TECHNOLOGY_PREFERENCE_3GPP | QMI_NAS_RADIO_TECHNOLOGY_PREFERENCE_LTE); ctx.dms_capabilities = (MM_MODEM_CAPABILITY_GSM_UMTS | MM_MODEM_CAPABILITY_LTE | MM_MODEM_CAPABILITY_5GNR); test_current_capabilities_expected (&ctx, (MM_MODEM_CAPABILITY_GSM_UMTS | MM_MODEM_CAPABILITY_LTE | MM_MODEM_CAPABILITY_5GNR)); /* QMI -> 5GNR only */ ctx.nas_ssp_mode_preference_mask = (QMI_NAS_RAT_MODE_PREFERENCE_5GNR); ctx.nas_tp_mask = QMI_NAS_RADIO_TECHNOLOGY_PREFERENCE_AUTO; ctx.dms_capabilities = (MM_MODEM_CAPABILITY_GSM_UMTS | MM_MODEM_CAPABILITY_LTE | MM_MODEM_CAPABILITY_5GNR); test_current_capabilities_expected (&ctx, (MM_MODEM_CAPABILITY_GSM_UMTS | MM_MODEM_CAPABILITY_LTE | MM_MODEM_CAPABILITY_5GNR)); } static void test_supported_capabilities_generic_nr5g_lte_umts (void) { MMQmiSupportedCapabilitiesContext ctx; static const MMModemCapability expected_capabilities[] = { MM_MODEM_CAPABILITY_GSM_UMTS |MM_MODEM_CAPABILITY_LTE | MM_MODEM_CAPABILITY_5GNR, }; ctx.multimode = FALSE; ctx.nas_ssp_supported = TRUE; ctx.nas_tp_supported = TRUE; ctx.dms_capabilities = (MM_MODEM_CAPABILITY_GSM_UMTS | MM_MODEM_CAPABILITY_LTE | MM_MODEM_CAPABILITY_5GNR); test_supported_capabilities_expected (&ctx, expected_capabilities, G_N_ELEMENTS (expected_capabilities)); } static void test_supported_modes_generic_nr5g_lte_umts (void) { MMQmiSupportedModesContext ctx; static const MMModemModeCombination expected_modes[] = { { .allowed = MM_MODEM_MODE_3G, .preferred = MM_MODEM_MODE_NONE }, /* we MUST have 4G-only in non-multimode devices */ { .allowed = MM_MODEM_MODE_4G, .preferred = MM_MODEM_MODE_NONE }, { .allowed = MM_MODEM_MODE_3G | MM_MODEM_MODE_4G, .preferred = MM_MODEM_MODE_4G }, { .allowed = MM_MODEM_MODE_3G | MM_MODEM_MODE_4G, .preferred = MM_MODEM_MODE_3G }, /* we MUST have 5G-only in non-multimode devices */ { .allowed = MM_MODEM_MODE_5G, .preferred = MM_MODEM_MODE_NONE }, /* we MUST have 4G+5G in non-multimode devices */ { .allowed = MM_MODEM_MODE_4G | MM_MODEM_MODE_5G, .preferred = MM_MODEM_MODE_5G }, { .allowed = MM_MODEM_MODE_4G | MM_MODEM_MODE_5G, .preferred = MM_MODEM_MODE_4G }, { .allowed = MM_MODEM_MODE_3G | MM_MODEM_MODE_5G, .preferred = MM_MODEM_MODE_5G }, { .allowed = MM_MODEM_MODE_3G | MM_MODEM_MODE_5G, .preferred = MM_MODEM_MODE_3G }, { .allowed = MM_MODEM_MODE_3G | MM_MODEM_MODE_4G | MM_MODEM_MODE_5G, .preferred = MM_MODEM_MODE_5G }, { .allowed = MM_MODEM_MODE_3G | MM_MODEM_MODE_4G | MM_MODEM_MODE_5G, .preferred = MM_MODEM_MODE_4G }, { .allowed = MM_MODEM_MODE_3G | MM_MODEM_MODE_4G | MM_MODEM_MODE_5G, .preferred = MM_MODEM_MODE_3G }, }; ctx.multimode = FALSE; ctx.all = MM_MODEM_MODE_3G | MM_MODEM_MODE_4G | MM_MODEM_MODE_5G; ctx.nas_ssp_supported = TRUE; ctx.nas_tp_supported = TRUE; ctx.current_capabilities = (MM_MODEM_CAPABILITY_GSM_UMTS | MM_MODEM_CAPABILITY_LTE | MM_MODEM_CAPABILITY_5GNR); test_supported_modes_expected (&ctx, expected_modes, G_N_ELEMENTS (expected_modes)); } static void test_current_capabilities_generic_nr5g_lte_evdo (void) { MMQmiCurrentCapabilitiesContext ctx; ctx.multimode = FALSE; /* QMI -> Automatic */ ctx.nas_ssp_mode_preference_mask = (QMI_NAS_RAT_MODE_PREFERENCE_CDMA_1XEVDO | QMI_NAS_RAT_MODE_PREFERENCE_LTE | QMI_NAS_RAT_MODE_PREFERENCE_5GNR); ctx.nas_tp_mask = QMI_NAS_RADIO_TECHNOLOGY_PREFERENCE_AUTO; ctx.dms_capabilities = (MM_MODEM_CAPABILITY_CDMA_EVDO | MM_MODEM_CAPABILITY_LTE | MM_MODEM_CAPABILITY_5GNR); test_current_capabilities_expected (&ctx, (MM_MODEM_CAPABILITY_CDMA_EVDO | MM_MODEM_CAPABILITY_LTE | MM_MODEM_CAPABILITY_5GNR)); /* QMI -> EVDO only */ ctx.nas_ssp_mode_preference_mask = (QMI_NAS_RAT_MODE_PREFERENCE_CDMA_1XEVDO); ctx.nas_tp_mask = (QMI_NAS_RADIO_TECHNOLOGY_PREFERENCE_3GPP2 | QMI_NAS_RADIO_TECHNOLOGY_PREFERENCE_HDR); ctx.dms_capabilities = (MM_MODEM_CAPABILITY_CDMA_EVDO | MM_MODEM_CAPABILITY_LTE | MM_MODEM_CAPABILITY_5GNR); test_current_capabilities_expected (&ctx, (MM_MODEM_CAPABILITY_CDMA_EVDO | MM_MODEM_CAPABILITY_LTE | MM_MODEM_CAPABILITY_5GNR)); /* QMI -> LTE only */ ctx.nas_ssp_mode_preference_mask = (QMI_NAS_RAT_MODE_PREFERENCE_LTE); ctx.nas_tp_mask = (QMI_NAS_RADIO_TECHNOLOGY_PREFERENCE_3GPP | QMI_NAS_RADIO_TECHNOLOGY_PREFERENCE_LTE); ctx.dms_capabilities = (MM_MODEM_CAPABILITY_CDMA_EVDO | MM_MODEM_CAPABILITY_LTE | MM_MODEM_CAPABILITY_5GNR); test_current_capabilities_expected (&ctx, (MM_MODEM_CAPABILITY_CDMA_EVDO | MM_MODEM_CAPABILITY_LTE | MM_MODEM_CAPABILITY_5GNR)); /* QMI -> 5GNR only */ ctx.nas_ssp_mode_preference_mask = (QMI_NAS_RAT_MODE_PREFERENCE_5GNR); ctx.nas_tp_mask = QMI_NAS_RADIO_TECHNOLOGY_PREFERENCE_AUTO; ctx.dms_capabilities = (MM_MODEM_CAPABILITY_CDMA_EVDO | MM_MODEM_CAPABILITY_LTE | MM_MODEM_CAPABILITY_5GNR); test_current_capabilities_expected (&ctx, (MM_MODEM_CAPABILITY_CDMA_EVDO | MM_MODEM_CAPABILITY_LTE | MM_MODEM_CAPABILITY_5GNR)); } static void test_supported_capabilities_generic_nr5g_lte_evdo (void) { MMQmiSupportedCapabilitiesContext ctx; static const MMModemCapability expected_capabilities[] = { MM_MODEM_CAPABILITY_CDMA_EVDO |MM_MODEM_CAPABILITY_LTE | MM_MODEM_CAPABILITY_5GNR, }; ctx.multimode = FALSE; ctx.nas_ssp_supported = TRUE; ctx.nas_tp_supported = TRUE; ctx.dms_capabilities = (MM_MODEM_CAPABILITY_CDMA_EVDO | MM_MODEM_CAPABILITY_LTE | MM_MODEM_CAPABILITY_5GNR); test_supported_capabilities_expected (&ctx, expected_capabilities, G_N_ELEMENTS (expected_capabilities)); } static void test_supported_modes_generic_nr5g_lte_evdo (void) { MMQmiSupportedModesContext ctx; static const MMModemModeCombination expected_modes[] = { { .allowed = MM_MODEM_MODE_3G, .preferred = MM_MODEM_MODE_NONE }, /* we MUST have 4G-only in non-multimode devices */ { .allowed = MM_MODEM_MODE_4G, .preferred = MM_MODEM_MODE_NONE }, { .allowed = MM_MODEM_MODE_3G | MM_MODEM_MODE_4G, .preferred = MM_MODEM_MODE_4G }, { .allowed = MM_MODEM_MODE_3G | MM_MODEM_MODE_4G, .preferred = MM_MODEM_MODE_3G }, /* we MUST have 5G-only in non-multimode devices */ { .allowed = MM_MODEM_MODE_5G, .preferred = MM_MODEM_MODE_NONE }, /* we MUST have 4G+5G in non-multimode devices */ { .allowed = MM_MODEM_MODE_4G | MM_MODEM_MODE_5G, .preferred = MM_MODEM_MODE_5G }, { .allowed = MM_MODEM_MODE_4G | MM_MODEM_MODE_5G, .preferred = MM_MODEM_MODE_4G }, { .allowed = MM_MODEM_MODE_3G | MM_MODEM_MODE_5G, .preferred = MM_MODEM_MODE_5G }, { .allowed = MM_MODEM_MODE_3G | MM_MODEM_MODE_5G, .preferred = MM_MODEM_MODE_3G }, { .allowed = MM_MODEM_MODE_3G | MM_MODEM_MODE_4G | MM_MODEM_MODE_5G, .preferred = MM_MODEM_MODE_5G }, { .allowed = MM_MODEM_MODE_3G | MM_MODEM_MODE_4G | MM_MODEM_MODE_5G, .preferred = MM_MODEM_MODE_4G }, { .allowed = MM_MODEM_MODE_3G | MM_MODEM_MODE_4G | MM_MODEM_MODE_5G, .preferred = MM_MODEM_MODE_3G }, }; ctx.multimode = FALSE; ctx.all = MM_MODEM_MODE_3G | MM_MODEM_MODE_4G | MM_MODEM_MODE_5G; ctx.nas_ssp_supported = TRUE; ctx.nas_tp_supported = TRUE; ctx.current_capabilities = (MM_MODEM_CAPABILITY_CDMA_EVDO | MM_MODEM_CAPABILITY_LTE | MM_MODEM_CAPABILITY_5GNR); test_supported_modes_expected (&ctx, expected_modes, G_N_ELEMENTS (expected_modes)); } /*****************************************************************************/ /* System Info processor helper */ static void common_test_registration_state_from_qmi_system_info (const guint8 *data, gsize data_size, MMModem3gppRegistrationState expected_registration_state_cs, MMModem3gppRegistrationState expected_registration_state_ps, MMModem3gppRegistrationState expected_registration_state_eps, MMModem3gppRegistrationState expected_registration_state_5gs, guint16 expected_lac, guint16 expected_tac, guint32 expected_cid, const gchar *expected_operator_id, MMModemAccessTechnology expected_act) { g_autoptr(GByteArray) buffer = NULL; g_autoptr(GError) error = NULL; g_autoptr(QmiMessage) message = NULL; g_autoptr(QmiMessageNasGetSystemInfoOutput) output = NULL; MMModem3gppRegistrationState registration_state_cs = MM_MODEM_3GPP_REGISTRATION_STATE_UNKNOWN; MMModem3gppRegistrationState registration_state_ps = MM_MODEM_3GPP_REGISTRATION_STATE_UNKNOWN; MMModem3gppRegistrationState registration_state_eps = MM_MODEM_3GPP_REGISTRATION_STATE_UNKNOWN; MMModem3gppRegistrationState registration_state_5gs = MM_MODEM_3GPP_REGISTRATION_STATE_UNKNOWN; guint16 lac = 0; guint16 tac = 0; guint32 cid = 0; g_autofree gchar *operator_id = NULL; MMModemAccessTechnology act = MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN; buffer = g_byte_array_append (g_byte_array_new (), data, data_size); message = qmi_message_new_from_raw (buffer, &error); g_assert_no_error (error); g_assert (message); output = qmi_message_nas_get_system_info_response_parse (message, &error); g_assert_no_error (error); g_assert (output); mm_modem_registration_state_from_qmi_system_info (output, NULL, ®istration_state_cs, ®istration_state_ps, ®istration_state_eps, ®istration_state_5gs, &lac, &tac, &cid, &operator_id, &act, NULL); /* log_object */ g_assert_cmpuint (registration_state_cs, ==, expected_registration_state_cs); g_assert_cmpuint (registration_state_ps, ==, expected_registration_state_ps); g_assert_cmpuint (registration_state_eps, ==, expected_registration_state_eps); g_assert_cmpuint (registration_state_5gs, ==, expected_registration_state_5gs); g_assert_cmpuint (lac, ==, expected_lac); g_assert_cmpuint (tac, ==, expected_tac); g_assert_cmpuint (cid, ==, expected_cid); g_assert_cmpstr (operator_id, ==, expected_operator_id); g_assert_cmpuint (act, ==, expected_act); } static void test_registration_state_from_qmi_system_info_2g_searching (void) { static const guint8 data[] = { 0x01, 0x19, 0x00, 0x80, /* marker (1 byte), qmux length (2 bytes), qmux flags (1 byte) */ 0x03, /* service: NAS */ 0x02, /* client id */ 0x02, /* service flags */ 0x01, 0x00, /* transaction */ 0x4D, 0x00, /* message id */ 0x0D, 0x00, /* all tlvs length */ /* TLV 0x02: Result */ 0x02, /* tlv type */ 0x04, 0x00, /* tlv size */ 0x00, 0x00, 0x00, 0x00, /* TLV 0x12: GSM Service Status*/ 0x12, /* tlv type */ 0x03, 0x00, /* tlv size */ 0x00, 0x00, 0x00, /* [ service_status = 'none' true_service_status = 'none' preferred_data_path = 'no' ] */ }; common_test_registration_state_from_qmi_system_info (data, G_N_ELEMENTS (data), MM_MODEM_3GPP_REGISTRATION_STATE_SEARCHING, /* cs */ MM_MODEM_3GPP_REGISTRATION_STATE_SEARCHING, /* ps */ MM_MODEM_3GPP_REGISTRATION_STATE_UNKNOWN, /* eps */ MM_MODEM_3GPP_REGISTRATION_STATE_UNKNOWN, /* 5gs */ 0, /* lac */ 0, /* tac */ 0, /* cid */ NULL, /* operator id */ MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN); } static void test_registration_state_from_qmi_system_info_2g_idle (void) { static const guint8 data[] = { 0x01, 0x19, 0x00, 0x80, /* marker (1 byte), qmux length (2 bytes), qmux flags (1 byte) */ 0x03, /* service: NAS */ 0x02, /* client id */ 0x02, /* service flags */ 0x01, 0x00, /* transaction */ 0x4D, 0x00, /* message id */ 0x0D, 0x00, /* all tlvs length */ /* TLV 0x02: Result */ 0x02, /* tlv type */ 0x04, 0x00, /* tlv size */ 0x00, 0x00, 0x00, 0x00, /* TLV 0x12: GSM Service Status*/ 0x12, /* tlv type */ 0x03, 0x00, /* tlv size */ 0x04, 0x00, 0x00, /* [ service_status = 'power-save' true_service_status = 'none' preferred_data_path = 'no' ] */ }; common_test_registration_state_from_qmi_system_info (data, G_N_ELEMENTS (data), MM_MODEM_3GPP_REGISTRATION_STATE_IDLE, /* cs */ MM_MODEM_3GPP_REGISTRATION_STATE_IDLE, /* ps */ MM_MODEM_3GPP_REGISTRATION_STATE_UNKNOWN, /* eps */ MM_MODEM_3GPP_REGISTRATION_STATE_UNKNOWN, /* 5gs */ 0, /* lac */ 0, /* tac */ 0, /* cid */ NULL, /* operator id */ MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN); } static void test_registration_state_from_qmi_system_info_2g_cs_home (void) { static const guint8 data[] = { 0x01, 0x3A, 0x00, 0x80, /* marker (1 byte), qmux length (2 bytes), qmux flags (1 byte) */ 0x03, /* service: NAS */ 0x02, /* client id */ 0x02, /* service flags */ 0x01, 0x00, /* transaction */ 0x4D, 0x00, /* message id */ 0x2E, 0x00, /* all tlvs length */ /* TLV 0x02: Result */ 0x02, /* tlv type */ 0x04, 0x00, /* tlv size */ 0x00, 0x00, 0x00, 0x00, /* TLV 0x12: GSM Service Status*/ 0x12, /* tlv type */ 0x03, 0x00, /* tlv size */ 0x02, 0x00, 0x00, /* [ service_status = 'available' true_service_status = 'none' preferred_data_path = 'no' ] */ /* TLV 0x17: GSM System Info */ 0x17, /* tlv type */ 0x1E, 0x00, /* tlv size: 30 bytes */ /* [ domain_valid = 'yes' domain = 'cs' service_capability_valid = 'yes' service_capability = 'cs' * roaming_status_valid = 'yes' roaming_status = 'off' forbidden_valid = 'yes' forbidden = 'no' * lac_valid = 'yes' lac = '7' cid_valid = 'yes' cid = '105217' registration_reject_info_valid = 'no' * registration_reject_domain = 'none' registration_reject_cause = 'none' network_id_valid = 'yes' * mcc = '901' mnc = '701' egprs_support_valid = 'no' egprs_support = 'no' dtm_support_valid = 'no', * dtm_support = 'no'] */ 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x01, 0x00, 0x01, 0x07, 0x00, 0x01, 0x01, 0x9B, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x39, 0x30, 0x31, 0x37, 0x30, 0xFF, 0x00, 0x00, 0x00, 0x00 }; common_test_registration_state_from_qmi_system_info (data, G_N_ELEMENTS (data), MM_MODEM_3GPP_REGISTRATION_STATE_HOME, /* cs */ MM_MODEM_3GPP_REGISTRATION_STATE_IDLE, /* ps */ MM_MODEM_3GPP_REGISTRATION_STATE_UNKNOWN, /* eps */ MM_MODEM_3GPP_REGISTRATION_STATE_UNKNOWN, /* 5gs */ 7, /* lac */ 0, /* tac */ 0x019B01, /* cid */ "90170", /* operator id */ MM_MODEM_ACCESS_TECHNOLOGY_GSM); } static void test_registration_state_from_qmi_system_info_2g3g_searching (void) { static const guint8 data[] = { 0x01, 0x1F, 0x00, 0x80, /* marker (1 byte), qmux length (2 bytes), qmux flags (1 byte) */ 0x03, /* service: NAS */ 0x02, /* client id */ 0x02, /* service flags */ 0x01, 0x00, /* transaction */ 0x4D, 0x00, /* message id */ 0x13, 0x00, /* all tlvs length */ /* TLV 0x02: Result */ 0x02, /* tlv type */ 0x04, 0x00, /* tlv size */ 0x00, 0x00, 0x00, 0x00, /* TLV 0x12: GSM Service Status*/ 0x12, /* tlv type */ 0x03, 0x00, /* tlv size */ 0x00, 0x00, 0x00, /* [ service_status = 'none' true_service_status = 'none' preferred_data_path = 'no' ] */ /* TLV 0x13: WCDMA Service Status */ 0x13, /* tlv type */ 0x03, 0x00, /* tlv size */ 0x00, 0x00, 0x00, /* [ service_status = 'none' true_service_status = 'none' preferred_data_path = 'no' ] */ }; common_test_registration_state_from_qmi_system_info (data, G_N_ELEMENTS (data), MM_MODEM_3GPP_REGISTRATION_STATE_SEARCHING, /* cs */ MM_MODEM_3GPP_REGISTRATION_STATE_SEARCHING, /* ps */ MM_MODEM_3GPP_REGISTRATION_STATE_UNKNOWN, /* eps */ MM_MODEM_3GPP_REGISTRATION_STATE_UNKNOWN, /* 5gs */ 0, /* lac */ 0, /* tac */ 0, /* cid */ NULL, /* operator id */ MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN); } static void test_registration_state_from_qmi_system_info_2g3g_idle (void) { static const guint8 data[] = { 0x01, 0x1F, 0x00, 0x80, /* marker (1 byte), qmux length (2 bytes), qmux flags (1 byte) */ 0x03, /* service: NAS */ 0x02, /* client id */ 0x02, /* service flags */ 0x01, 0x00, /* transaction */ 0x4D, 0x00, /* message id */ 0x13, 0x00, /* all tlvs length */ /* TLV 0x02: Result */ 0x02, /* tlv type */ 0x04, 0x00, /* tlv size */ 0x00, 0x00, 0x00, 0x00, /* TLV 0x12: GSM Service Status*/ 0x12, /* tlv type */ 0x03, 0x00, /* tlv size */ 0x04, 0x00, 0x00, /* [ service_status = 'power-save' true_service_status = 'none' preferred_data_path = 'no' ] */ /* TLV 0x13: WCDMA Service Status */ 0x13, /* tlv type */ 0x03, 0x00, /* tlv size */ 0x04, 0x00, 0x00, /* [ service_status = 'power-save' true_service_status = 'none' preferred_data_path = 'no' ] */ }; common_test_registration_state_from_qmi_system_info (data, G_N_ELEMENTS (data), MM_MODEM_3GPP_REGISTRATION_STATE_IDLE, /* cs */ MM_MODEM_3GPP_REGISTRATION_STATE_IDLE, /* ps */ MM_MODEM_3GPP_REGISTRATION_STATE_UNKNOWN, /* eps */ MM_MODEM_3GPP_REGISTRATION_STATE_UNKNOWN, /* 5gs */ 0, /* lac */ 0, /* tac */ 0, /* cid */ NULL, /* operator id */ MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN); } static void test_registration_state_from_qmi_system_info_2g3g_ps_home (void) { static const guint8 data[] = { 0x01, 0x43, 0x00, 0x80, /* marker (1 byte), qmux length (2 bytes), qmux flags (1 byte) */ 0x03, /* service: NAS */ 0x02, /* client id */ 0x02, /* service flags */ 0x01, 0x00, /* transaction */ 0x4D, 0x00, /* message id */ 0x37, 0x00, /* all tlvs length */ /* TLV 0x02: Result */ 0x02, /* tlv type */ 0x04, 0x00, /* tlv size */ 0x00, 0x00, 0x00, 0x00, /* TLV 0x12: GSM Service Status*/ 0x12, /* tlv type */ 0x03, 0x00, /* tlv size */ 0x00, 0x00, 0x00, /* [ service_status = 'none' true_service_status = 'none' preferred_data_path = 'no' ] */ /* TLV 0x13: WCDMA Service Status */ 0x13, /* tlv type */ 0x03, 0x00, /* tlv size */ 0x02, 0x00, 0x00, /* [ service_status = 'available' true_service_status = 'none' preferred_data_path = 'no' ] */ /* TLV 0x18: WCDMA System Info */ 0x18, /* tlv type */ 0x21, 0x00, /* tlv size: 33 bytes */ /* [ domain_valid = 'yes' domain = 'ps' service_capability_valid = 'yes' service_capability = 'ps' * roaming_status_valid = 'yes' roaming_status = 'off' forbidden_valid = 'yes' forbidden = 'no' * lac_valid = 'yes' lac = '7' cid_valid = 'yes' cid = '105217' registration_reject_info_valid = 'no' * registration_reject_domain = 'none' registration_reject_cause = 'none' network_id_valid = 'yes' * mcc = '901' mnc = '701' hs_call_status_valid = 'no' hs_call_stauts = '' hs_service_valid = 'yes', * primary_scrambling_code_valid = 'yes' primary_scrambling_code = '255'] */ 0x01, 0x02, 0x01, 0x02, 0x01, 0x00, 0x01, 0x00, 0x01, 0x07, 0x00, 0x01, 0x01, 0x9B, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x39, 0x30, 0x31, 0x37, 0x30, 0xFF, 0x01, 0x00, 0x01, 0x00, 0x01, 0xFF, 0x00 }; common_test_registration_state_from_qmi_system_info (data, G_N_ELEMENTS (data), MM_MODEM_3GPP_REGISTRATION_STATE_SEARCHING, /* cs (from GSM) */ MM_MODEM_3GPP_REGISTRATION_STATE_HOME, /* ps (from UMTS) */ MM_MODEM_3GPP_REGISTRATION_STATE_UNKNOWN, /* eps */ MM_MODEM_3GPP_REGISTRATION_STATE_UNKNOWN, /* 5gs */ 7, /* lac */ 0, /* tac */ 0x019B01, /* cid */ "90170", /* operator id */ MM_MODEM_ACCESS_TECHNOLOGY_UMTS); } static void test_registration_state_from_qmi_system_info_2g3g_csps_home (void) { static const guint8 data[] = { 0x01, 0x43, 0x00, 0x80, /* marker (1 byte), qmux length (2 bytes), qmux flags (1 byte) */ 0x03, /* service: NAS */ 0x02, /* client id */ 0x02, /* service flags */ 0x01, 0x00, /* transaction */ 0x4D, 0x00, /* message id */ 0x37, 0x00, /* all tlvs length */ /* TLV 0x02: Result */ 0x02, /* tlv type */ 0x04, 0x00, /* tlv size */ 0x00, 0x00, 0x00, 0x00, /* TLV 0x12: GSM Service Status*/ 0x12, /* tlv type */ 0x03, 0x00, /* tlv size */ 0x00, 0x00, 0x00, /* [ service_status = 'none' true_service_status = 'none' preferred_data_path = 'no' ] */ /* TLV 0x13: WCDMA Service Status */ 0x13, /* tlv type */ 0x03, 0x00, /* tlv size */ 0x02, 0x00, 0x00, /* [ service_status = 'available' true_service_status = 'none' preferred_data_path = 'no' ] */ /* TLV 0x18: WCDMA System Info */ 0x18, /* tlv type */ 0x21, 0x00, /* tlv size: 33 bytes */ /* [ domain_valid = 'yes' domain = 'cs-ps' service_capability_valid = 'yes' service_capability = 'cs-ps' * roaming_status_valid = 'yes' roaming_status = 'off' forbidden_valid = 'yes' forbidden = 'no' * lac_valid = 'yes' lac = '7' cid_valid = 'yes' cid = '105217' registration_reject_info_valid = 'no' * registration_reject_domain = 'none' registration_reject_cause = 'none' network_id_valid = 'yes' * mcc = '901' mnc = '701' hs_call_status_valid = 'no' hs_call_stauts = '' hs_service_valid = 'yes', * primary_scrambling_code_valid = 'yes' primary_scrambling_code = '255'] */ 0x01, 0x03, 0x01, 0x03, 0x01, 0x00, 0x01, 0x00, 0x01, 0x07, 0x00, 0x01, 0x01, 0x9B, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x39, 0x30, 0x31, 0x37, 0x30, 0xFF, 0x01, 0x00, 0x01, 0x00, 0x01, 0xFF, 0x00 }; common_test_registration_state_from_qmi_system_info (data, G_N_ELEMENTS (data), MM_MODEM_3GPP_REGISTRATION_STATE_HOME, /* cs */ MM_MODEM_3GPP_REGISTRATION_STATE_HOME, /* ps */ MM_MODEM_3GPP_REGISTRATION_STATE_UNKNOWN, /* eps */ MM_MODEM_3GPP_REGISTRATION_STATE_UNKNOWN, /* 5gs */ 7, /* lac */ 0, /* tac */ 0x019B01, /* cid */ "90170", /* operator id */ MM_MODEM_ACCESS_TECHNOLOGY_UMTS); } static void test_registration_state_from_qmi_system_info_2g3g4g_searching (void) { static const guint8 data[] = { 0x01, 0x25, 0x00, 0x80, /* marker (1 byte), qmux length (2 bytes), qmux flags (1 byte) */ 0x03, /* service: NAS */ 0x02, /* client id */ 0x02, /* service flags */ 0x01, 0x00, /* transaction */ 0x4D, 0x00, /* message id */ 0x19, 0x00, /* all tlvs length */ /* TLV 0x02: Result */ 0x02, /* tlv type */ 0x04, 0x00, /* tlv size */ 0x00, 0x00, 0x00, 0x00, /* TLV 0x12: GSM Service Status*/ 0x12, /* tlv type */ 0x03, 0x00, /* tlv size */ 0x00, 0x00, 0x00, /* [ service_status = 'none' true_service_status = 'none' preferred_data_path = 'no' ] */ /* TLV 0x13: WCDMA Service Status */ 0x13, /* tlv type */ 0x03, 0x00, /* tlv size */ 0x00, 0x00, 0x00, /* [ service_status = 'none' true_service_status = 'none' preferred_data_path = 'no' ] */ /* TLV 0x14: LTE Service Status */ 0x14, /* tlv type */ 0x03, 0x00, /* tlv size */ 0x00, 0x00, 0x00, /* [ service_status = 'none' true_service_status = 'none' preferred_data_path = 'no' ] */ }; common_test_registration_state_from_qmi_system_info (data, G_N_ELEMENTS (data), MM_MODEM_3GPP_REGISTRATION_STATE_SEARCHING, /* cs */ MM_MODEM_3GPP_REGISTRATION_STATE_SEARCHING, /* ps */ MM_MODEM_3GPP_REGISTRATION_STATE_SEARCHING, /* eps */ MM_MODEM_3GPP_REGISTRATION_STATE_UNKNOWN, /* 5gs */ 0, /* lac */ 0, /* tac */ 0, /* cid */ NULL, /* operator id */ MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN); } static void test_registration_state_from_qmi_system_info_2g3g4g_idle (void) { static const guint8 data[] = { 0x01, 0x25, 0x00, 0x80, /* marker (1 byte), qmux length (2 bytes), qmux flags (1 byte) */ 0x03, /* service: NAS */ 0x02, /* client id */ 0x02, /* service flags */ 0x01, 0x00, /* transaction */ 0x4D, 0x00, /* message id */ 0x19, 0x00, /* all tlvs length */ /* TLV 0x02: Result */ 0x02, /* tlv type */ 0x04, 0x00, /* tlv size */ 0x00, 0x00, 0x00, 0x00, /* TLV 0x12: GSM Service Status*/ 0x12, /* tlv type */ 0x03, 0x00, /* tlv size */ 0x04, 0x00, 0x00, /* [ service_status = 'power-save' true_service_status = 'none' preferred_data_path = 'no' ] */ /* TLV 0x13: WCDMA Service Status */ 0x13, /* tlv type */ 0x03, 0x00, /* tlv size */ 0x04, 0x00, 0x00, /* [ service_status = 'power-save' true_service_status = 'none' preferred_data_path = 'no' ] */ /* TLV 0x14: LTE Service Status */ 0x14, /* tlv type */ 0x03, 0x00, /* tlv size */ 0x04, 0x00, 0x00, /* [ service_status = 'power-save' true_service_status = 'none' preferred_data_path = 'no' ] */ }; common_test_registration_state_from_qmi_system_info (data, G_N_ELEMENTS (data), MM_MODEM_3GPP_REGISTRATION_STATE_IDLE, /* cs */ MM_MODEM_3GPP_REGISTRATION_STATE_IDLE, /* ps */ MM_MODEM_3GPP_REGISTRATION_STATE_IDLE, /* eps */ MM_MODEM_3GPP_REGISTRATION_STATE_UNKNOWN, /* 5gs */ 0, /* lac */ 0, /* tac */ 0, /* cid */ NULL, /* operator id */ MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN); } static void test_registration_state_from_qmi_system_info_2g3g4g_eps_home (void) { static const guint8 data[] = { 0x01, 0x45, 0x00, 0x80, /* marker (1 byte), qmux length (2 bytes), qmux flags (1 byte) */ 0x03, /* service: NAS */ 0x02, /* client id */ 0x02, /* service flags */ 0x01, 0x00, /* transaction */ 0x4D, 0x00, /* message id */ 0x39, 0x00, /* all tlvs length */ /* TLV 0x02: Result */ 0x02, /* tlv type */ 0x04, 0x00, /* tlv size */ 0x00, 0x00, 0x00, 0x00, /* TLV 0x12: GSM Service Status*/ 0x12, /* tlv type */ 0x03, 0x00, /* tlv size */ 0x00, 0x00, 0x00, /* [ service_status = 'none' true_service_status = 'none' preferred_data_path = 'no' ] */ /* TLV 0x13: WCDMA Service Status */ 0x13, /* tlv type */ 0x03, 0x00, /* tlv size */ 0x00, 0x00, 0x00, /* [ service_status = 'none' true_service_status = 'none' preferred_data_path = 'no' ] */ /* TLV 0x14: LTE Service Status */ 0x14, /* tlv type */ 0x03, 0x00, /* tlv size */ 0x02, 0x00, 0x00, /* [ service_status = 'available' true_service_status = 'none' preferred_data_path = 'no' ] */ /* TLV 0x19: LTE System Info */ 0x19, /* tlv type */ 0x1D, 0x00, /* tlv size: 29 bytes */ /* [ domain_valid = 'yes' domain = 'ps' service_capability_valid = 'yes' service_capability = 'ps' * roaming_status_valid = 'yes' roaming_status = 'off' forbidden_valid = 'yes' forbidden = 'no' * lac_valid = 'no' lac = '65535' cid_valid = 'yes' cid = '105217' registration_reject_info_valid = 'no' * registration_reject_domain = 'none' registration_reject_cause = 'none' network_id_valid = 'yes' * mcc = '901' mnc = '701' tac_valid = 'yes' tac = '7' ] */ 0x01, 0x02, 0x01, 0x02, 0x01, 0x00, 0x01, 0x00, 0x00, 0xFF, 0xFF, 0x01, 0x01, 0x9B, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x39, 0x30, 0x31, 0x37, 0x30, 0xFF, 0x01, 0x07, 0x00, }; common_test_registration_state_from_qmi_system_info (data, G_N_ELEMENTS (data), MM_MODEM_3GPP_REGISTRATION_STATE_SEARCHING, /* cs */ MM_MODEM_3GPP_REGISTRATION_STATE_SEARCHING, /* ps */ MM_MODEM_3GPP_REGISTRATION_STATE_HOME, /* eps */ MM_MODEM_3GPP_REGISTRATION_STATE_UNKNOWN, /* 5gs */ 0, /* lac */ 7, /* tac */ 0x019B01, /* cid */ "90170", /* operator id */ MM_MODEM_ACCESS_TECHNOLOGY_LTE); } static void test_registration_state_from_qmi_system_info_4g_eps_home (void) { static const guint8 data[] = { 0x01, 0x39, 0x00, 0x80, /* marker (1 byte), qmux length (2 bytes), qmux flags (1 byte) */ 0x03, /* service: NAS */ 0x02, /* client id */ 0x02, /* service flags */ 0x01, 0x00, /* transaction */ 0x4D, 0x00, /* message id */ 0x2D, 0x00, /* all tlvs length */ /* TLV 0x02: Result */ 0x02, /* tlv type */ 0x04, 0x00, /* tlv size */ 0x00, 0x00, 0x00, 0x00, /* TLV 0x14: LTE Service Status */ 0x14, /* tlv type */ 0x03, 0x00, /* tlv size */ 0x02, 0x00, 0x00, /* [ service_status = 'available' true_service_status = 'none' preferred_data_path = 'no' ] */ /* TLV 0x19: LTE System Info */ 0x19, /* tlv type */ 0x1D, 0x00, /* tlv size: 29 bytes */ /* [ domain_valid = 'yes' domain = 'ps' service_capability_valid = 'yes' service_capability = 'ps' * roaming_status_valid = 'yes' roaming_status = 'off' forbidden_valid = 'yes' forbidden = 'no' * lac_valid = 'no' lac = '65535' cid_valid = 'yes' cid = '105217' registration_reject_info_valid = 'no' * registration_reject_domain = 'none' registration_reject_cause = 'none' network_id_valid = 'yes' * mcc = '901' mnc = '701' tac_valid = 'yes' tac = '7' ] */ 0x01, 0x02, 0x01, 0x02, 0x01, 0x00, 0x01, 0x00, 0x00, 0xFF, 0xFF, 0x01, 0x01, 0x9B, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x39, 0x30, 0x31, 0x37, 0x30, 0xFF, 0x01, 0x07, 0x00, }; common_test_registration_state_from_qmi_system_info (data, G_N_ELEMENTS (data), MM_MODEM_3GPP_REGISTRATION_STATE_UNKNOWN, /* cs */ MM_MODEM_3GPP_REGISTRATION_STATE_UNKNOWN, /* ps */ MM_MODEM_3GPP_REGISTRATION_STATE_HOME, /* eps */ MM_MODEM_3GPP_REGISTRATION_STATE_UNKNOWN, /* 5gs */ 0, /* lac */ 7, /* tac */ 0x019B01, /* cid */ "90170", /* operator id */ MM_MODEM_ACCESS_TECHNOLOGY_LTE); } static void test_registration_state_from_qmi_system_info_5g_5gs_home (void) { static const guint8 data[] = { 0x01, 0x39, 0x00, 0x80, /* marker (1 byte), qmux length (2 bytes), qmux flags (1 byte) */ 0x03, /* service: NAS */ 0x02, /* client id */ 0x02, /* service flags */ 0x01, 0x00, /* transaction */ 0x4D, 0x00, /* message id */ 0x2D, 0x00, /* all tlvs length */ /* TLV 0x02: Result */ 0x02, /* tlv type */ 0x04, 0x00, /* tlv size */ 0x00, 0x00, 0x00, 0x00, /* TLV 0x4A: 5GNR System Info */ 0x4A, /* tlv type */ 0x03, 0x00, /* tlv size */ 0x02, 0x00, 0x00, /* TLV 0x4B: 5GNR System Info */ 0x4B, /* tlv type */ 0x1D, 0x00, /* tlv size: 29 bytes */ /* [ domain_valid = 'yes' domain = 'ps' service_capability_valid = 'yes' service_capability = 'ps' * roaming_status_valid = 'yes' roaming_status = 'off' forbidden_valid = 'yes' forbidden = 'no' * lac_valid = 'no' lac = '65535' cid_valid = 'yes' cid = '105217' registration_reject_info_valid = 'no' * registration_reject_domain = 'none' registration_reject_cause = 'none' network_id_valid = 'yes' * mcc = '901' mnc = '701' tac_valid = 'yes' tac = '7' ] */ 0x01, 0x02, 0x01, 0x02, 0x01, 0x00, 0x01, 0x00, 0x00, 0xFF, 0xFF, 0x01, 0x01, 0x9B, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x39, 0x30, 0x31, 0x37, 0x30, 0xFF, 0x01, 0x07, 0x00, }; common_test_registration_state_from_qmi_system_info (data, G_N_ELEMENTS (data), MM_MODEM_3GPP_REGISTRATION_STATE_UNKNOWN, /* cs */ MM_MODEM_3GPP_REGISTRATION_STATE_UNKNOWN, /* ps */ MM_MODEM_3GPP_REGISTRATION_STATE_UNKNOWN, /* eps */ MM_MODEM_3GPP_REGISTRATION_STATE_HOME, /* 5gs */ 0, /* lac */ 7, /* tac */ 0x019B01, /* cid */ "90170", /* operator id */ MM_MODEM_ACCESS_TECHNOLOGY_5GNR); } /*****************************************************************************/ int main (int argc, char **argv) { setlocale (LC_ALL, ""); g_test_init (&argc, &argv, NULL); g_test_add_func ("/MM/qmi/current-capabilities/UML290", test_current_capabilities_uml290); g_test_add_func ("/MM/qmi/supported-capabilities/UML290", test_supported_capabilities_uml290); g_test_add_func ("/MM/qmi/supported-modes/UML290/cdma-evdo-gsm-umts-lte", test_supported_modes_uml290_cdma_evdo_gsm_umts_lte); g_test_add_func ("/MM/qmi/supported-modes/UML290/cdma-evdo-lte", test_supported_modes_uml290_cdma_evdo_lte); g_test_add_func ("/MM/qmi/supported-modes/UML290/gsm-umts-lte", test_supported_modes_uml290_gsm_umts_lte); g_test_add_func ("/MM/qmi/supported-modes/UML290/lte", test_supported_modes_uml290_lte); g_test_add_func ("/MM/qmi/current-capabilities/ADU960S", test_current_capabilities_adu960s); g_test_add_func ("/MM/qmi/supported-capabilities/ADU960S", test_supported_capabilities_adu960s); g_test_add_func ("/MM/qmi/supported-modes/ADU960S", test_supported_modes_adu960s); g_test_add_func ("/MM/qmi/current-capabilities/Gobi1k/GSM", test_current_capabilities_gobi1k_gsm); g_test_add_func ("/MM/qmi/supported-capabilities/Gobi1k/GSM", test_supported_capabilities_gobi1k_gsm); g_test_add_func ("/MM/qmi/supported-modes/Gobi1k/GSM", test_supported_modes_gobi1k_gsm); g_test_add_func ("/MM/qmi/current-capabilities/Gobi1k/CDMA", test_current_capabilities_gobi1k_cdma); g_test_add_func ("/MM/qmi/supported-capabilities/Gobi1k/CDMA", test_supported_capabilities_gobi1k_cdma); g_test_add_func ("/MM/qmi/supported-modes/Gobi1k/CDMA", test_supported_modes_gobi1k_cdma); g_test_add_func ("/MM/qmi/current-capabilities/Gobi2k/GSM", test_current_capabilities_gobi2k_gsm); g_test_add_func ("/MM/qmi/supported-capabilities/Gobi2k/GSM", test_supported_capabilities_gobi2k_gsm); g_test_add_func ("/MM/qmi/supported-modes/Gobi2k/GSM", test_supported_modes_gobi2k_gsm); g_test_add_func ("/MM/qmi/current-capabilities/Gobi2k/CDMA", test_current_capabilities_gobi2k_cdma); g_test_add_func ("/MM/qmi/supported-capabilities/Gobi2k/CDMA", test_supported_capabilities_gobi2k_cdma); g_test_add_func ("/MM/qmi/supported-modes/Gobi2k/CDMA", test_supported_modes_gobi2k_cdma); g_test_add_func ("/MM/qmi/current-capabilities/Gobi3k/GSM", test_current_capabilities_gobi3k_gsm); g_test_add_func ("/MM/qmi/supported-capabilities/Gobi3k/GSM", test_supported_capabilities_gobi3k_gsm); g_test_add_func ("/MM/qmi/supported-modes/Gobi3k/GSM", test_supported_modes_gobi3k_gsm); g_test_add_func ("/MM/qmi/current-capabilities/Gobi3k/CDMA", test_current_capabilities_gobi3k_cdma); g_test_add_func ("/MM/qmi/supported-capabilities/Gobi3k/CDMA", test_supported_capabilities_gobi3k_cdma); g_test_add_func ("/MM/qmi/supported-modes/Gobi3k/CDMA", test_supported_modes_gobi3k_cdma); g_test_add_func ("/MM/qmi/current-capabilities/generic/nr5g-multimode", test_current_capabilities_generic_nr5g_multimode); g_test_add_func ("/MM/qmi/supported-capabilities/generic/nr5g-multimode", test_supported_capabilities_generic_nr5g_multimode); g_test_add_func ("/MM/qmi/supported-modes/generic/nr5g-multimode", test_supported_modes_generic_nr5g_multimode); g_test_add_func ("/MM/qmi/current-capabilities/generic/nr5g-only", test_current_capabilities_generic_nr5g_only); g_test_add_func ("/MM/qmi/supported-capabilities/generic/nr5g-only", test_supported_capabilities_generic_nr5g_only); g_test_add_func ("/MM/qmi/supported-modes/generic/nr5g-only", test_supported_modes_generic_nr5g_only); g_test_add_func ("/MM/qmi/current-capabilities/generic/nr5g-lte", test_current_capabilities_generic_nr5g_lte); g_test_add_func ("/MM/qmi/supported-capabilities/generic/nr5g-lte", test_supported_capabilities_generic_nr5g_lte); g_test_add_func ("/MM/qmi/supported-modes/generic/nr5g-lte", test_supported_modes_generic_nr5g_lte); g_test_add_func ("/MM/qmi/current-capabilities/generic/nr5g-lte-umts", test_current_capabilities_generic_nr5g_lte_umts); g_test_add_func ("/MM/qmi/supported-capabilities/generic/nr5g-lte-umts", test_supported_capabilities_generic_nr5g_lte_umts); g_test_add_func ("/MM/qmi/supported-modes/generic/nr5g-lte-umts", test_supported_modes_generic_nr5g_lte_umts); g_test_add_func ("/MM/qmi/current-capabilities/generic/nr5g-lte-evdo", test_current_capabilities_generic_nr5g_lte_evdo); g_test_add_func ("/MM/qmi/supported-capabilities/generic/nr5g-lte-evdo", test_supported_capabilities_generic_nr5g_lte_evdo); g_test_add_func ("/MM/qmi/supported-modes/generic/nr5g-lte-evdo", test_supported_modes_generic_nr5g_lte_evdo); g_test_add_func ("/MM/qmi/registration-state-from-system-info/2g/searching", test_registration_state_from_qmi_system_info_2g_searching); g_test_add_func ("/MM/qmi/registration-state-from-system-info/2g/idle", test_registration_state_from_qmi_system_info_2g_idle); g_test_add_func ("/MM/qmi/registration-state-from-system-info/2g/cs-home", test_registration_state_from_qmi_system_info_2g_cs_home); g_test_add_func ("/MM/qmi/registration-state-from-system-info/2g3g/searching", test_registration_state_from_qmi_system_info_2g3g_searching); g_test_add_func ("/MM/qmi/registration-state-from-system-info/2g3g/idle", test_registration_state_from_qmi_system_info_2g3g_idle); g_test_add_func ("/MM/qmi/registration-state-from-system-info/2g3g/ps-home", test_registration_state_from_qmi_system_info_2g3g_ps_home); g_test_add_func ("/MM/qmi/registration-state-from-system-info/2g3g/csps-home", test_registration_state_from_qmi_system_info_2g3g_csps_home); g_test_add_func ("/MM/qmi/registration-state-from-system-info/2g3g4g/searching", test_registration_state_from_qmi_system_info_2g3g4g_searching); g_test_add_func ("/MM/qmi/registration-state-from-system-info/2g3g4g/idle", test_registration_state_from_qmi_system_info_2g3g4g_idle); g_test_add_func ("/MM/qmi/registration-state-from-system-info/2g3g4g/eps-home", test_registration_state_from_qmi_system_info_2g3g4g_eps_home); g_test_add_func ("/MM/qmi/registration-state-from-system-info/4g/eps-home", test_registration_state_from_qmi_system_info_4g_eps_home); g_test_add_func ("/MM/qmi/registration-state-from-system-info/5g/5gs-home", test_registration_state_from_qmi_system_info_5g_5gs_home); return g_test_run (); } ModemManager-1.23.4-dev/src/tests/test-modem-helpers.c000066400000000000000000005604451456466623000225560ustar00rootroot00000000000000/* -*- 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 #include #include #include #include #define _LIBMM_INSIDE_MM #include #include "mm-modem-helpers.h" #include "mm-log-test.h" #define g_assert_cmpfloat_tolerance(val1, val2, tolerance) \ g_assert_cmpfloat (fabs (val1 - val2), <, tolerance) /*****************************************************************************/ /* Test IFC=? responses */ static void test_ifc_response (const gchar *str, const MMFlowControl expected) { MMFlowControl mask; GError *error = NULL; mask = mm_parse_ifc_test_response (str, NULL, &error); g_assert_no_error (error); g_assert_cmpuint (mask, ==, expected); } static void test_ifc_response_all_simple (void) { test_ifc_response ("+IFC (0,1,2),(0,1,2)", (MM_FLOW_CONTROL_NONE | MM_FLOW_CONTROL_XON_XOFF | MM_FLOW_CONTROL_RTS_CTS)); } static void test_ifc_response_all_groups (void) { test_ifc_response ("+IFC (0-2),(0-2)", (MM_FLOW_CONTROL_NONE | MM_FLOW_CONTROL_XON_XOFF | MM_FLOW_CONTROL_RTS_CTS)); } static void test_ifc_response_none_only (void) { test_ifc_response ("+IFC (0),(0)", MM_FLOW_CONTROL_NONE); } static void test_ifc_response_xon_xoff_only (void) { test_ifc_response ("+IFC (1),(1)", MM_FLOW_CONTROL_XON_XOFF); } static void test_ifc_response_rts_cts_only (void) { test_ifc_response ("+IFC (2),(2)", MM_FLOW_CONTROL_RTS_CTS); } static void test_ifc_response_no_xon_xoff (void) { test_ifc_response ("+IFC (0,2),(0,2)", (MM_FLOW_CONTROL_NONE | MM_FLOW_CONTROL_RTS_CTS)); } static void test_ifc_response_no_xon_xoff_in_ta (void) { test_ifc_response ("+IFC (0,1,2),(0,2)", (MM_FLOW_CONTROL_NONE | MM_FLOW_CONTROL_RTS_CTS)); } static void test_ifc_response_no_xon_xoff_in_te (void) { test_ifc_response ("+IFC (0,2),(0,1,2)", (MM_FLOW_CONTROL_NONE | MM_FLOW_CONTROL_RTS_CTS)); } static void test_ifc_response_no_rts_cts_simple (void) { test_ifc_response ("+IFC (0,1),(0,1)", (MM_FLOW_CONTROL_NONE | MM_FLOW_CONTROL_XON_XOFF)); } static void test_ifc_response_no_rts_cts_groups (void) { test_ifc_response ("+IFC (0-1),(0-1)", (MM_FLOW_CONTROL_NONE | MM_FLOW_CONTROL_XON_XOFF)); } static void test_ifc_response_all_simple_and_unknown (void) { test_ifc_response ("+IFC (0,1,2,3),(0,1,2)", (MM_FLOW_CONTROL_NONE | MM_FLOW_CONTROL_XON_XOFF | MM_FLOW_CONTROL_RTS_CTS)); } static void test_ifc_response_all_groups_and_unknown (void) { test_ifc_response ("+IFC (0-3),(0-2)", (MM_FLOW_CONTROL_NONE | MM_FLOW_CONTROL_XON_XOFF | MM_FLOW_CONTROL_RTS_CTS)); } /*****************************************************************************/ /* Test WS46=? responses */ static void test_ws46_response (const gchar *str, const MMModemMode *expected, guint n_expected) { guint i; GArray *modes; GError *error = NULL; modes = mm_3gpp_parse_ws46_test_response (str, NULL, &error); g_assert_no_error (error); g_assert (modes != NULL); g_assert_cmpuint (modes->len, ==, n_expected); for (i = 0; i < n_expected; i++) { guint j; for (j = 0; j < modes->len; j++) { if (expected[i] == g_array_index (modes, MMModemMode, j)) break; } g_assert_cmpuint (j, !=, modes->len); } g_array_unref (modes); } static void test_ws46_response_generic_2g3g4g (void) { static const MMModemMode expected[] = { MM_MODEM_MODE_2G, MM_MODEM_MODE_3G, MM_MODEM_MODE_2G | MM_MODEM_MODE_3G | MM_MODEM_MODE_4G, MM_MODEM_MODE_4G, MM_MODEM_MODE_2G | MM_MODEM_MODE_3G, }; const gchar *str = "+WS46: (12,22,25,28,29)"; test_ws46_response (str, expected, G_N_ELEMENTS (expected)); } static void test_ws46_response_generic_2g3g (void) { static const MMModemMode expected[] = { MM_MODEM_MODE_2G, MM_MODEM_MODE_3G, MM_MODEM_MODE_2G | MM_MODEM_MODE_3G, }; const gchar *str = "+WS46: (12,22,25)"; test_ws46_response (str, expected, G_N_ELEMENTS (expected)); } static void test_ws46_response_generic_2g3g_v2 (void) { static const MMModemMode expected[] = { MM_MODEM_MODE_2G, MM_MODEM_MODE_3G, MM_MODEM_MODE_2G | MM_MODEM_MODE_3G, }; const gchar *str = "+WS46: (12,22,29)"; test_ws46_response (str, expected, G_N_ELEMENTS (expected)); } static void test_ws46_response_cinterion (void) { static const MMModemMode expected[] = { MM_MODEM_MODE_2G, MM_MODEM_MODE_3G, MM_MODEM_MODE_2G | MM_MODEM_MODE_3G | MM_MODEM_MODE_4G, MM_MODEM_MODE_4G, MM_MODEM_MODE_2G | MM_MODEM_MODE_3G, }; const gchar *str = "(12,22,25,28,29)"; test_ws46_response (str, expected, G_N_ELEMENTS (expected)); } static void test_ws46_response_telit_le866 (void) { static const MMModemMode expected[] = { MM_MODEM_MODE_4G, }; const gchar *str = "(28)"; test_ws46_response (str, expected, G_N_ELEMENTS (expected)); } static void test_ws46_response_range_1 (void) { static const MMModemMode expected[] = { MM_MODEM_MODE_2G | MM_MODEM_MODE_3G, MM_MODEM_MODE_2G | MM_MODEM_MODE_4G, MM_MODEM_MODE_3G | MM_MODEM_MODE_4G, }; const gchar *str = "+WS46: (29-31)"; test_ws46_response (str, expected, G_N_ELEMENTS (expected)); } static void test_ws46_response_range_2 (void) { static const MMModemMode expected[] = { MM_MODEM_MODE_2G, MM_MODEM_MODE_3G, MM_MODEM_MODE_2G | MM_MODEM_MODE_3G | MM_MODEM_MODE_4G, MM_MODEM_MODE_4G, MM_MODEM_MODE_2G | MM_MODEM_MODE_3G, MM_MODEM_MODE_2G | MM_MODEM_MODE_4G, MM_MODEM_MODE_3G | MM_MODEM_MODE_4G, }; const gchar *str = "+WS46: (12,22,25,28-31)"; test_ws46_response (str, expected, G_N_ELEMENTS (expected)); } /*****************************************************************************/ /* Test CMGL responses */ static void test_cmgl_response (const gchar *str, const MM3gppPduInfo *expected, guint n_expected) { guint i; GList *list; GError *error = NULL; list = mm_3gpp_parse_pdu_cmgl_response (str, &error); g_assert_no_error (error); g_assert (list != NULL); g_assert_cmpuint (g_list_length (list), ==, n_expected); for (i = 0; i < n_expected; i++) { GList *l; /* Look for the pdu with the expected index */ for (l = list; l; l = g_list_next (l)) { MM3gppPduInfo *info = l->data; /* Found */ if (info->index == expected[i].index) { g_assert_cmpint (info->status, ==, expected[i].status); g_assert_cmpstr (info->pdu, ==, expected[i].pdu); break; } } g_assert (l != NULL); } mm_3gpp_pdu_info_list_free (list); } static void test_cmgl_response_generic (void *f, gpointer d) { const gchar *str = "+CMGL: 0,1,,147\r\n07914306073011F00405812261F700003130916191314095C27" "4D96D2FBBD3E437280CB2BEC961F3DB5D76818EF2F0381D9E83E06F39A8CC2E9FD372F" "77BEE0249CBE37A594E0E83E2F532085E2F93CB73D0B93CA7A7DFEEB01C447F93DF731" "0BD3E07CDCB727B7A9C7ECF41E432C8FC96B7C32079189E26874179D0F8DD7E93C3A0B" "21B246AA641D637396C7EBBCB22D0FD7E77B5D376B3AB3C07"; const MM3gppPduInfo expected [] = { { .index = 0, .status = 1, .pdu = (gchar *) "07914306073011F00405812261F700003130916191314095C27" "4D96D2FBBD3E437280CB2BEC961F3DB5D76818EF2F0381D9E83E06F39A8CC2E9FD372F" "77BEE0249CBE37A594E0E83E2F532085E2F93CB73D0B93CA7A7DFEEB01C447F93DF731" "0BD3E07CDCB727B7A9C7ECF41E432C8FC96B7C32079189E26874179D0F8DD7E93C3A0B" "21B246AA641D637396C7EBBCB22D0FD7E77B5D376B3AB3C07" } }; test_cmgl_response (str, expected, G_N_ELEMENTS (expected)); } static void test_cmgl_response_generic_multiple (void *f, gpointer d) { const gchar *str = "+CMGL: 0,1,,147\r\n07914306073011F00405812261F700003130916191314095C27" "4D96D2FBBD3E437280CB2BEC961F3DB5D76818EF2F0381D9E83E06F39A8CC2E9FD372F" "77BEE0249CBE37A594E0E83E2F532085E2F93CB73D0B93CA7A7DFEEB01C447F93DF731" "0BD3E07CDCB727B7A9C7ECF41E432C8FC96B7C32079189E26874179D0F8DD7E93C3A0B" "21B246AA641D637396C7EBBCB22D0FD7E77B5D376B3AB3C07\r\n" "+CMGL: 1,1,,147\r\n07914306073011F00405812261F700003130916191314095C27" "4D96D2FBBD3E437280CB2BEC961F3DB5D76818EF2F0381D9E83E06F39A8CC2E9FD372F" "77BEE0249CBE37A594E0E83E2F532085E2F93CB73D0B93CA7A7DFEEB01C447F93DF731" "0BD3E07CDCB727B7A9C7ECF41E432C8FC96B7C32079189E26874179D0F8DD7E93C3A0B" "21B246AA641D637396C7EBBCB22D0FD7E77B5D376B3AB3C07\r\n" "+CMGL: 2,1,,147\r\n07914306073011F00405812261F700003130916191314095C27" "4D96D2FBBD3E437280CB2BEC961F3DB5D76818EF2F0381D9E83E06F39A8CC2E9FD372F" "77BEE0249CBE37A594E0E83E2F532085E2F93CB73D0B93CA7A7DFEEB01C447F93DF731" "0BD3E07CDCB727B7A9C7ECF41E432C8FC96B7C32079189E26874179D0F8DD7E93C3A0B" "21B246AA641D637396C7EBBCB22D0FD7E77B5D376B3AB3C07"; const MM3gppPduInfo expected [] = { { .index = 0, .status = 1, .pdu = (gchar *) "07914306073011F00405812261F700003130916191314095C27" "4D96D2FBBD3E437280CB2BEC961F3DB5D76818EF2F0381D9E83E06F39A8CC2E9FD372F" "77BEE0249CBE37A594E0E83E2F532085E2F93CB73D0B93CA7A7DFEEB01C447F93DF731" "0BD3E07CDCB727B7A9C7ECF41E432C8FC96B7C32079189E26874179D0F8DD7E93C3A0B" "21B246AA641D637396C7EBBCB22D0FD7E77B5D376B3AB3C07" }, { .index = 1, .status = 1, .pdu = (gchar *) "07914306073011F00405812261F700003130916191314095C27" "4D96D2FBBD3E437280CB2BEC961F3DB5D76818EF2F0381D9E83E06F39A8CC2E9FD372F" "77BEE0249CBE37A594E0E83E2F532085E2F93CB73D0B93CA7A7DFEEB01C447F93DF731" "0BD3E07CDCB727B7A9C7ECF41E432C8FC96B7C32079189E26874179D0F8DD7E93C3A0B" "21B246AA641D637396C7EBBCB22D0FD7E77B5D376B3AB3C07" }, { .index = 2, .status = 1, .pdu = (gchar *) "07914306073011F00405812261F700003130916191314095C27" "4D96D2FBBD3E437280CB2BEC961F3DB5D76818EF2F0381D9E83E06F39A8CC2E9FD372F" "77BEE0249CBE37A594E0E83E2F532085E2F93CB73D0B93CA7A7DFEEB01C447F93DF731" "0BD3E07CDCB727B7A9C7ECF41E432C8FC96B7C32079189E26874179D0F8DD7E93C3A0B" "21B246AA641D637396C7EBBCB22D0FD7E77B5D376B3AB3C07" } }; test_cmgl_response (str, expected, G_N_ELEMENTS (expected)); } static void test_cmgl_response_pantech (void *f, gpointer d) { const gchar *str = "+CMGL: 17,3,35\r\n079100F40D1101000F001000B917118336058F300001954747A0E4ACF41F27298CDCE83C6EF371B0402814020"; const MM3gppPduInfo expected [] = { { .index = 17, .status = 3, .pdu = (gchar *) "079100F40D1101000F001000B917118336058F300001954747A0E4ACF41F27298CDCE83C6EF371B0402814020" } }; test_cmgl_response (str, expected, G_N_ELEMENTS (expected)); } static void test_cmgl_response_pantech_multiple (void *f, gpointer d) { const gchar *str = "+CMGL: 17,3,35\r\n079100F40D1101000F001000B917118336058F300001954747A0E4ACF41F27298CDCE83C6EF371B0402814020\r\n" "+CMGL: 15,3,35\r\n079100F40D1101000F001000B917118336058F300001954747A0E4ACF41F27298CDCE83C6EF371B0402814020\r\n" "+CMGL: 13,3,35\r\n079100F40D1101000F001000B917118336058F300001954747A0E4ACF41F27298CDCE83C6EF371B0402814020\r\n" "+CMGL: 11,3,35\r\n079100F40D1101000F001000B917118336058F300"; const MM3gppPduInfo expected [] = { { .index = 17, .status = 3, .pdu = (gchar *) "079100F40D1101000F001000B917118336058F300001954747A0E4ACF41F27298CDCE83C6EF371B0402814020" }, { .index = 15, .status = 3, .pdu = (gchar *) "079100F40D1101000F001000B917118336058F300001954747A0E4ACF41F27298CDCE83C6EF371B0402814020" }, { .index = 13, .status = 3, .pdu = (gchar *) "079100F40D1101000F001000B917118336058F300001954747A0E4ACF41F27298CDCE83C6EF371B0402814020" }, { .index = 11, .status = 3, .pdu = (gchar *) "079100F40D1101000F001000B917118336058F300" } }; test_cmgl_response (str, expected, G_N_ELEMENTS (expected)); } /*****************************************************************************/ /* Test CMGR responses */ static void test_cmgr_response (const gchar *str, const MM3gppPduInfo *expected) { MM3gppPduInfo *info; GError *error = NULL; info = mm_3gpp_parse_cmgr_read_response (str, 0, &error); g_assert_no_error (error); g_assert (info != NULL); /* Ignore index, it is not included in CMGR response */ g_assert_cmpint (info->status, ==, expected->status); g_assert_cmpstr (info->pdu, ==, expected->pdu); mm_3gpp_pdu_info_free (info); } static void test_cmgr_response_generic (void *f, gpointer d) { const gchar *str = "+CMGR: 1,,147 07914306073011F00405812261F700003130916191314095C27" "4D96D2FBBD3E437280CB2BEC961F3DB5D76818EF2F0381D9E83E06F39A8CC2E9FD372F" "77BEE0249CBE37A594E0E83E2F532085E2F93CB73D0B93CA7A7DFEEB01C447F93DF731" "0BD3E07CDCB727B7A9C7ECF41E432C8FC96B7C32079189E26874179D0F8DD7E93C3A0B" "21B246AA641D637396C7EBBCB22D0FD7E77B5D376B3AB3C07"; const MM3gppPduInfo expected = { .index = 0, .status = 1, .pdu = (gchar *) "07914306073011F00405812261F700003130916191314095C27" "4D96D2FBBD3E437280CB2BEC961F3DB5D76818EF2F0381D9E83E06F39A8CC2E9FD372F" "77BEE0249CBE37A594E0E83E2F532085E2F93CB73D0B93CA7A7DFEEB01C447F93DF731" "0BD3E07CDCB727B7A9C7ECF41E432C8FC96B7C32079189E26874179D0F8DD7E93C3A0B" "21B246AA641D637396C7EBBCB22D0FD7E77B5D376B3AB3C07" }; test_cmgr_response (str, &expected); } /* Telit HE910 places empty quotation marks in the field and a CR+LF * before the PDU */ static void test_cmgr_response_telit (void *f, gpointer d) { const gchar *str = "+CMGR: 0,\"\",50\r\n07916163838428F9040B916121021021F7000051905141642" "20A23C4B0BCFD5E8740C4B0BCFD5E83C26E3248196687C9A0301D440DBBC3677918"; const MM3gppPduInfo expected = { .index = 0, .status = 0, .pdu = (gchar *) "07916163838428F9040B916121021021F7000051905141642" "20A23C4B0BCFD5E8740C4B0BCFD5E83C26E3248196687C9A0301D440DBBC3677918" }; test_cmgr_response (str, &expected); } /*****************************************************************************/ /* Test COPS responses */ static void test_cops_results (const gchar *desc, const gchar *reply, MMModemCharset cur_charset, MM3gppNetworkInfo *expected_results, guint32 expected_results_len) { GList *l; GError *error = NULL; GList *results; g_debug ("Testing %s +COPS response...", desc); results = mm_3gpp_parse_cops_test_response (reply, cur_charset, NULL, &error); g_assert (results); g_assert_no_error (error); g_assert_cmpuint (g_list_length (results), ==, expected_results_len); for (l = results; l; l = g_list_next (l)) { MM3gppNetworkInfo *info = l->data; gboolean found = FALSE; guint i; gchar *access_tech_str; for (i = 0; !found && i < expected_results_len; i++) { MM3gppNetworkInfo *expected; expected = &expected_results[i]; if (g_str_equal (info->operator_code, expected->operator_code) && info->access_tech == expected->access_tech) { found = TRUE; g_assert_cmpuint (info->status, ==, expected->status); if (expected->operator_long) g_assert_cmpstr (info->operator_long, ==, expected->operator_long); else g_assert (info->operator_long == NULL); if (expected->operator_short) g_assert_cmpstr (info->operator_short, ==, expected->operator_short); else g_assert (info->operator_short == NULL); } } access_tech_str = mm_modem_access_technology_build_string_from_mask (info->access_tech); g_debug ("info: [%s,%s,%s,%s] %sfound in expected results", info->operator_long ? info->operator_long : "", info->operator_short ? info->operator_short : "", info->operator_code, access_tech_str, found ? "" : "not "); g_free (access_tech_str); g_assert (found == TRUE); } mm_3gpp_network_info_list_free (results); } static void test_cops_response_tm506 (void *f, gpointer d) { const gchar *reply = "+COPS: (2,\"\",\"T-Mobile\",\"31026\",0),(2,\"T - Mobile\",\"T - Mobile\",\"310260\"),2),(1,\"AT&T\",\"AT&T\",\"310410\"),0)"; static MM3gppNetworkInfo expected[] = { { MM_MODEM_3GPP_NETWORK_AVAILABILITY_CURRENT, NULL, (gchar *) "T-Mobile", (gchar *) "31026", MM_MODEM_ACCESS_TECHNOLOGY_GSM }, { MM_MODEM_3GPP_NETWORK_AVAILABILITY_CURRENT, (gchar *) "T - Mobile", (gchar *) "T - Mobile", (gchar *) "310260", MM_MODEM_ACCESS_TECHNOLOGY_UMTS }, { MM_MODEM_3GPP_NETWORK_AVAILABILITY_AVAILABLE, (gchar *) "AT&T", (gchar *) "AT&T", (gchar *) "310410", MM_MODEM_ACCESS_TECHNOLOGY_GSM } }; test_cops_results ("TM-506", reply, MM_MODEM_CHARSET_GSM, &expected[0], G_N_ELEMENTS (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 MM3gppNetworkInfo expected[] = { { MM_MODEM_3GPP_NETWORK_AVAILABILITY_AVAILABLE, (gchar *) "T-Mobile US", (gchar *) "TMO US", (gchar *) "31026", MM_MODEM_ACCESS_TECHNOLOGY_GSM }, { MM_MODEM_3GPP_NETWORK_AVAILABILITY_AVAILABLE, (gchar *) "Cingular", (gchar *) "Cingular", (gchar *) "310410", MM_MODEM_ACCESS_TECHNOLOGY_GSM }, }; test_cops_results ("GlobeTrotter 3G+ (nozomi)", reply, MM_MODEM_CHARSET_GSM, &expected[0], G_N_ELEMENTS (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 MM3gppNetworkInfo expected[] = { { MM_MODEM_3GPP_NETWORK_AVAILABILITY_AVAILABLE, (gchar *) "T-Mobile", (gchar *) "TMO", (gchar *) "31026", MM_MODEM_ACCESS_TECHNOLOGY_GSM }, { MM_MODEM_3GPP_NETWORK_AVAILABILITY_AVAILABLE, (gchar *) "AT&T", (gchar *) "AT&T", (gchar *) "310410", MM_MODEM_ACCESS_TECHNOLOGY_UMTS }, { MM_MODEM_3GPP_NETWORK_AVAILABILITY_AVAILABLE, (gchar *) "AT&T", (gchar *) "AT&T", (gchar *) "310410", MM_MODEM_ACCESS_TECHNOLOGY_GSM }, }; test_cops_results ("Sierra AirCard 881", reply, MM_MODEM_CHARSET_GSM, &expected[0], G_N_ELEMENTS (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 MM3gppNetworkInfo expected[] = { { MM_MODEM_3GPP_NETWORK_AVAILABILITY_CURRENT, (gchar *) "T-Mobile US", (gchar *) "TMO US", (gchar *) "31026", MM_MODEM_ACCESS_TECHNOLOGY_GSM }, { MM_MODEM_3GPP_NETWORK_AVAILABILITY_AVAILABLE, (gchar *) "AT&T", (gchar *) "AT&T", (gchar *) "310410", MM_MODEM_ACCESS_TECHNOLOGY_UMTS }, { MM_MODEM_3GPP_NETWORK_AVAILABILITY_AVAILABLE, (gchar *) "AT&T", (gchar *) "AT&T", (gchar *) "310410", MM_MODEM_ACCESS_TECHNOLOGY_GSM }, }; test_cops_results ("Option GlobeTrotter MAX 3.6", reply, MM_MODEM_CHARSET_GSM, &expected[0], G_N_ELEMENTS (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 MM3gppNetworkInfo expected[] = { { MM_MODEM_3GPP_NETWORK_AVAILABILITY_CURRENT, (gchar *) "T-Mobile", (gchar *) "TMO", (gchar *) "31026", MM_MODEM_ACCESS_TECHNOLOGY_GSM }, { MM_MODEM_3GPP_NETWORK_AVAILABILITY_AVAILABLE, (gchar *) "Cingular", (gchar *) "Cinglr", (gchar *) "310410", MM_MODEM_ACCESS_TECHNOLOGY_UMTS }, { MM_MODEM_3GPP_NETWORK_AVAILABILITY_AVAILABLE, (gchar *) "Cingular", (gchar *) "Cinglr", (gchar *) "310410", MM_MODEM_ACCESS_TECHNOLOGY_GSM }, }; test_cops_results ("Sierra AirCard 860", reply, MM_MODEM_CHARSET_GSM, &expected[0], G_N_ELEMENTS (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 MM3gppNetworkInfo expected[] = { { MM_MODEM_3GPP_NETWORK_AVAILABILITY_CURRENT, (gchar *) "T-Mobile", (gchar *) "T-Mobile", (gchar *) "31026", MM_MODEM_ACCESS_TECHNOLOGY_GSM }, { MM_MODEM_3GPP_NETWORK_AVAILABILITY_AVAILABLE, (gchar *) "AT&T", (gchar *) "AT&T", (gchar *) "310410", MM_MODEM_ACCESS_TECHNOLOGY_UMTS }, { MM_MODEM_3GPP_NETWORK_AVAILABILITY_AVAILABLE, (gchar *) "AT&T", (gchar *) "AT&T", (gchar *) "310410", MM_MODEM_ACCESS_TECHNOLOGY_GSM }, }; test_cops_results ("Option GTM378", reply, MM_MODEM_CHARSET_GSM, &expected[0], G_N_ELEMENTS (expected)); } static void test_cops_response_motoc (void *f, gpointer d) { const char *reply = "+COPS: (2,\"T-Mobile\",\"\",\"310260\"),(0,\"Cingular Wireless\",\"\",\"310410\")"; static MM3gppNetworkInfo expected[] = { { MM_MODEM_3GPP_NETWORK_AVAILABILITY_CURRENT, (gchar *) "T-Mobile", NULL, (gchar *) "310260", MM_MODEM_ACCESS_TECHNOLOGY_GSM }, { MM_MODEM_3GPP_NETWORK_AVAILABILITY_UNKNOWN, (gchar *) "Cingular Wireless", NULL, (gchar *) "310410", MM_MODEM_ACCESS_TECHNOLOGY_GSM }, }; test_cops_results ("BUSlink SCWi275u (Motorola C-series)", reply, MM_MODEM_CHARSET_GSM, &expected[0], G_N_ELEMENTS (expected)); } static void test_cops_response_mf627a (void *f, gpointer d) { /* The '@' in this string is ASCII 0x40, and 0x40 is a valid GSM-7 char: '¡' (which is 0xc2,0xa1 in UTF-8) */ const char *reply = "+COPS: (2,\"AT&T@\",\"AT&TD\",\"310410\",0),(3,\"Vstream Wireless\",\"VSTREAM\",\"31026\",0),"; static MM3gppNetworkInfo expected[] = { { MM_MODEM_3GPP_NETWORK_AVAILABILITY_CURRENT, (gchar *) "AT&T¡", (gchar *) "AT&TD", (gchar *) "310410", MM_MODEM_ACCESS_TECHNOLOGY_GSM }, { MM_MODEM_3GPP_NETWORK_AVAILABILITY_FORBIDDEN, (gchar *) "Vstream Wireless", (gchar *) "VSTREAM", (gchar *) "31026", MM_MODEM_ACCESS_TECHNOLOGY_GSM }, }; test_cops_results ("ZTE MF627 (A)", reply, MM_MODEM_CHARSET_GSM, &expected[0], G_N_ELEMENTS (expected)); } static void test_cops_response_mf627b (void *f, gpointer d) { /* The '@' in this string is ASCII 0x40, and 0x40 is a valid GSM-7 char: '¡' (which is 0xc2,0xa1 in UTF-8) */ const char *reply = "+COPS: (2,\"AT&Tp\",\"AT&T@\",\"310410\",0),(3,\"\",\"\",\"31026\",0),"; static MM3gppNetworkInfo expected[] = { { MM_MODEM_3GPP_NETWORK_AVAILABILITY_CURRENT, (gchar *) "AT&Tp", (gchar *) "AT&T¡", (gchar *) "310410", MM_MODEM_ACCESS_TECHNOLOGY_GSM }, { MM_MODEM_3GPP_NETWORK_AVAILABILITY_FORBIDDEN, NULL, NULL, (gchar *) "31026", MM_MODEM_ACCESS_TECHNOLOGY_GSM }, }; test_cops_results ("ZTE MF627 (B)", reply, MM_MODEM_CHARSET_GSM, &expected[0], G_N_ELEMENTS (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 MM3gppNetworkInfo expected[] = { { MM_MODEM_3GPP_NETWORK_AVAILABILITY_CURRENT, (gchar *) "T-Mobile", (gchar *) "TMO", (gchar *) "31026", MM_MODEM_ACCESS_TECHNOLOGY_GSM }, { MM_MODEM_3GPP_NETWORK_AVAILABILITY_AVAILABLE, (gchar *) "AT&T", (gchar *) "AT&T", (gchar *) "310410", MM_MODEM_ACCESS_TECHNOLOGY_GSM }, }; test_cops_results ("Huawei E160G", reply, MM_MODEM_CHARSET_GSM, &expected[0], G_N_ELEMENTS (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 MM3gppNetworkInfo expected[] = { { MM_MODEM_3GPP_NETWORK_AVAILABILITY_CURRENT, NULL, NULL, (gchar *) "310410", MM_MODEM_ACCESS_TECHNOLOGY_UMTS }, { MM_MODEM_3GPP_NETWORK_AVAILABILITY_AVAILABLE, (gchar *) "AT&T", (gchar *) "AT&T", (gchar *) "310410", MM_MODEM_ACCESS_TECHNOLOGY_GSM }, { MM_MODEM_3GPP_NETWORK_AVAILABILITY_AVAILABLE, (gchar *) "T-Mobile", (gchar *) "TMO", (gchar *) "31026", MM_MODEM_ACCESS_TECHNOLOGY_GSM }, }; test_cops_results ("Sierra AT&T USBConnect Mercury", reply, MM_MODEM_CHARSET_GSM, &expected[0], G_N_ELEMENTS (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 MM3gppNetworkInfo expected[] = { { MM_MODEM_3GPP_NETWORK_AVAILABILITY_CURRENT, (gchar *) "AT&T", NULL, (gchar *) "310410", MM_MODEM_ACCESS_TECHNOLOGY_GSM }, { MM_MODEM_3GPP_NETWORK_AVAILABILITY_CURRENT, NULL, NULL, (gchar *) "3104100", MM_MODEM_ACCESS_TECHNOLOGY_UMTS }, { MM_MODEM_3GPP_NETWORK_AVAILABILITY_AVAILABLE, (gchar *) "AT&T", NULL, (gchar *) "310260", MM_MODEM_ACCESS_TECHNOLOGY_GSM }, }; test_cops_results ("Option AT&T Quicksilver", reply, MM_MODEM_CHARSET_GSM, &expected[0], G_N_ELEMENTS (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 MM3gppNetworkInfo expected[] = { { MM_MODEM_3GPP_NETWORK_AVAILABILITY_CURRENT, (gchar *) "T-Mobile US", (gchar *) "TMO US", (gchar *) "31026", MM_MODEM_ACCESS_TECHNOLOGY_GSM }, { MM_MODEM_3GPP_NETWORK_AVAILABILITY_AVAILABLE, (gchar *) "AT&T", (gchar *) "AT&T", (gchar *) "310410", MM_MODEM_ACCESS_TECHNOLOGY_GSM }, }; test_cops_results ("Option iCON 225", reply, MM_MODEM_CHARSET_GSM, &expected[0], G_N_ELEMENTS (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 MM3gppNetworkInfo expected[] = { { MM_MODEM_3GPP_NETWORK_AVAILABILITY_AVAILABLE, (gchar *) "T-Mobile US", (gchar *) "TMO US", (gchar *) "31026", MM_MODEM_ACCESS_TECHNOLOGY_GSM }, { MM_MODEM_3GPP_NETWORK_AVAILABILITY_CURRENT, (gchar *) "T-Mobile", (gchar *) "T-Mobile", (gchar *) "310260", MM_MODEM_ACCESS_TECHNOLOGY_UMTS }, { MM_MODEM_3GPP_NETWORK_AVAILABILITY_AVAILABLE, (gchar *) "AT&T", (gchar *) "AT&T", (gchar *) "310410", MM_MODEM_ACCESS_TECHNOLOGY_UMTS }, { MM_MODEM_3GPP_NETWORK_AVAILABILITY_AVAILABLE, (gchar *) "AT&T", (gchar *) "AT&T", (gchar *) "310410", MM_MODEM_ACCESS_TECHNOLOGY_GSM } }; test_cops_results ("Option iCON 452", reply, MM_MODEM_CHARSET_GSM, &expected[0], G_N_ELEMENTS (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 MM3gppNetworkInfo expected[] = { { MM_MODEM_3GPP_NETWORK_AVAILABILITY_CURRENT, (gchar *) "T - Mobile", (gchar *) "T - Mobile", (gchar *) "31026", MM_MODEM_ACCESS_TECHNOLOGY_GSM }, { MM_MODEM_3GPP_NETWORK_AVAILABILITY_AVAILABLE, (gchar *) "AT&T", (gchar *) "AT&T", (gchar *) "310410", MM_MODEM_ACCESS_TECHNOLOGY_GSM }, { MM_MODEM_3GPP_NETWORK_AVAILABILITY_AVAILABLE, (gchar *) "AT&T", (gchar *) "AT&T", (gchar *) "310410", MM_MODEM_ACCESS_TECHNOLOGY_UMTS } }; test_cops_results ("Ericsson F3507g", reply, MM_MODEM_CHARSET_GSM, &expected[0], G_N_ELEMENTS (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 MM3gppNetworkInfo expected[] = { { MM_MODEM_3GPP_NETWORK_AVAILABILITY_CURRENT, (gchar *) "T - Mobile", (gchar *) "T - Mobile", (gchar *) "31026", MM_MODEM_ACCESS_TECHNOLOGY_GSM }, { MM_MODEM_3GPP_NETWORK_AVAILABILITY_AVAILABLE, (gchar *) "AT&T", (gchar *) "AT&T", (gchar *) "310410", MM_MODEM_ACCESS_TECHNOLOGY_UMTS }, { MM_MODEM_3GPP_NETWORK_AVAILABILITY_AVAILABLE, (gchar *) "AT&T", (gchar *) "AT&T", (gchar *) "310410", MM_MODEM_ACCESS_TECHNOLOGY_GSM } }; test_cops_results ("Ericsson F3607gw", reply, MM_MODEM_CHARSET_GSM, &expected[0], G_N_ELEMENTS (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 MM3gppNetworkInfo expected[] = { { MM_MODEM_3GPP_NETWORK_AVAILABILITY_CURRENT, (gchar *) "T-Mobile", (gchar *) "T-Mobile", (gchar *) "31026", MM_MODEM_ACCESS_TECHNOLOGY_GSM }, { MM_MODEM_3GPP_NETWORK_AVAILABILITY_AVAILABLE, (gchar *) "AT&T", (gchar *) "AT&T", (gchar *) "310410", MM_MODEM_ACCESS_TECHNOLOGY_UMTS }, { MM_MODEM_3GPP_NETWORK_AVAILABILITY_AVAILABLE, (gchar *) "AT&T", (gchar *) "AT&T", (gchar *) "310410", MM_MODEM_ACCESS_TECHNOLOGY_GSM } }; test_cops_results ("Sierra MC8775", reply, MM_MODEM_CHARSET_GSM, &expected[0], G_N_ELEMENTS (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 MM3gppNetworkInfo expected[] = { { MM_MODEM_3GPP_NETWORK_AVAILABILITY_CURRENT, (gchar *) "T - Mobile", NULL, (gchar *) "31026", MM_MODEM_ACCESS_TECHNOLOGY_GSM }, { MM_MODEM_3GPP_NETWORK_AVAILABILITY_AVAILABLE, (gchar *) "Einstein PCS", NULL, (gchar *) "31064", MM_MODEM_ACCESS_TECHNOLOGY_GSM }, { MM_MODEM_3GPP_NETWORK_AVAILABILITY_AVAILABLE, (gchar *) "Cingular", NULL, (gchar *) "31041", MM_MODEM_ACCESS_TECHNOLOGY_GSM }, }; test_cops_results ("Nokia N80", reply, MM_MODEM_CHARSET_GSM, &expected[0], G_N_ELEMENTS (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 MM3gppNetworkInfo expected[] = { { MM_MODEM_3GPP_NETWORK_AVAILABILITY_CURRENT, (gchar *) "T-Mobile", (gchar *) "TMO", (gchar *) "31026", MM_MODEM_ACCESS_TECHNOLOGY_GSM }, { MM_MODEM_3GPP_NETWORK_AVAILABILITY_AVAILABLE, (gchar *) "AT&T", (gchar *) "AT&T", (gchar *) "310410", MM_MODEM_ACCESS_TECHNOLOGY_GSM }, }; test_cops_results ("Huawei E1550", reply, MM_MODEM_CHARSET_GSM, &expected[0], G_N_ELEMENTS (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 MM3gppNetworkInfo expected[] = { { MM_MODEM_3GPP_NETWORK_AVAILABILITY_CURRENT, (gchar *) "T-Mobile", (gchar *) "T-Mobile", (gchar *) "31026", MM_MODEM_ACCESS_TECHNOLOGY_GSM }, { MM_MODEM_3GPP_NETWORK_AVAILABILITY_AVAILABLE, NULL, NULL, (gchar *) "310410", MM_MODEM_ACCESS_TECHNOLOGY_GSM }, }; test_cops_results ("ZTE MF622", reply, MM_MODEM_CHARSET_GSM, &expected[0], G_N_ELEMENTS (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 MM3gppNetworkInfo expected[] = { { MM_MODEM_3GPP_NETWORK_AVAILABILITY_AVAILABLE, NULL, NULL, (gchar *) "31026", MM_MODEM_ACCESS_TECHNOLOGY_GSM }, { MM_MODEM_3GPP_NETWORK_AVAILABILITY_AVAILABLE, NULL, NULL, (gchar *) "310410", MM_MODEM_ACCESS_TECHNOLOGY_UMTS }, { MM_MODEM_3GPP_NETWORK_AVAILABILITY_AVAILABLE, NULL, NULL, (gchar *) "310410", MM_MODEM_ACCESS_TECHNOLOGY_GSM }, }; test_cops_results ("Huawei E226", reply, MM_MODEM_CHARSET_GSM, &expected[0], G_N_ELEMENTS (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 MM3gppNetworkInfo expected[] = { { MM_MODEM_3GPP_NETWORK_AVAILABILITY_UNKNOWN, (gchar *) "AT&T MicroCell", (gchar *) "AT&T MicroCell", (gchar *) "310410", MM_MODEM_ACCESS_TECHNOLOGY_UMTS }, { MM_MODEM_3GPP_NETWORK_AVAILABILITY_AVAILABLE, (gchar *) "AT&T MicroCell", (gchar *) "AT&T MicroCell", (gchar *) "310410", MM_MODEM_ACCESS_TECHNOLOGY_GSM }, { MM_MODEM_3GPP_NETWORK_AVAILABILITY_AVAILABLE, (gchar *) "T-Mobile", (gchar *) "TMO", (gchar *) "31026", MM_MODEM_ACCESS_TECHNOLOGY_GSM }, }; test_cops_results ("Novatel XU870", reply, MM_MODEM_CHARSET_GSM, &expected[0], G_N_ELEMENTS (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 MM3gppNetworkInfo expected[] = { { MM_MODEM_3GPP_NETWORK_AVAILABILITY_CURRENT, (gchar *) "T-Mobile US", (gchar *) "TMO US", (gchar *) "31026", MM_MODEM_ACCESS_TECHNOLOGY_GSM }, { MM_MODEM_3GPP_NETWORK_AVAILABILITY_AVAILABLE, (gchar *) "AT&T", (gchar *) "AT&T", (gchar *) "310410", MM_MODEM_ACCESS_TECHNOLOGY_UMTS }, { MM_MODEM_3GPP_NETWORK_AVAILABILITY_AVAILABLE, (gchar *) "AT&T", (gchar *) "AT&T", (gchar *) "310410", MM_MODEM_ACCESS_TECHNOLOGY_GSM }, }; test_cops_results ("Option GlobeTrotter Ultra Express", reply, MM_MODEM_CHARSET_GSM, &expected[0], G_N_ELEMENTS (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 MM3gppNetworkInfo expected[] = { { MM_MODEM_3GPP_NETWORK_AVAILABILITY_CURRENT, (gchar *) "T - Mobile", NULL, (gchar *)"31026", MM_MODEM_ACCESS_TECHNOLOGY_GSM }, { MM_MODEM_3GPP_NETWORK_AVAILABILITY_AVAILABLE, (gchar *) "AT&T", NULL, (gchar *)"310410", MM_MODEM_ACCESS_TECHNOLOGY_GSM }, }; test_cops_results ("Nokia 2720", reply, MM_MODEM_CHARSET_GSM, &expected[0], G_N_ELEMENTS (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 MM3gppNetworkInfo expected[] = { { MM_MODEM_3GPP_NETWORK_AVAILABILITY_CURRENT, (gchar *) "T-Mobile", (gchar *) "T-Mobile", (gchar *) "31026", MM_MODEM_ACCESS_TECHNOLOGY_GSM }, { MM_MODEM_3GPP_NETWORK_AVAILABILITY_AVAILABLE, (gchar *) "AT&T", (gchar *) "AT&T", (gchar *) "310410", MM_MODEM_ACCESS_TECHNOLOGY_UMTS }, { MM_MODEM_3GPP_NETWORK_AVAILABILITY_AVAILABLE, (gchar *) "AT&T", (gchar *) "AT&T", (gchar *) "310410", MM_MODEM_ACCESS_TECHNOLOGY_GSM }, }; test_cops_results ("Qualcomm Gobi", reply, MM_MODEM_CHARSET_GSM, &expected[0], G_N_ELEMENTS (expected)); } static void test_cops_response_sek600i (void *f, gpointer d) { /* Phone is stupid enough to support 3G but not report cell technology, * mixing together 2G and 3G cells without any way of distinguishing * which is which... */ const char *reply = "+COPS: (2,\"blau\",\"\",\"26203\"),(2,\"blau\",\"\",\"26203\"),(3,\"\",\"\",\"26201\"),(3,\"\",\"\",\"26202\"),(3,\"\",\"\",\"26207\"),(3,\"\",\"\",\"26201\"),(3,\"\",\"\",\"26207\")"; static MM3gppNetworkInfo expected[] = { { MM_MODEM_3GPP_NETWORK_AVAILABILITY_CURRENT, (gchar *) "blau", NULL, (gchar *) "26203", MM_MODEM_ACCESS_TECHNOLOGY_GSM }, { MM_MODEM_3GPP_NETWORK_AVAILABILITY_CURRENT, (gchar *) "blau", NULL, (gchar *) "26203", MM_MODEM_ACCESS_TECHNOLOGY_GSM }, { MM_MODEM_3GPP_NETWORK_AVAILABILITY_FORBIDDEN, NULL, NULL, (gchar *) "26201", MM_MODEM_ACCESS_TECHNOLOGY_GSM }, { MM_MODEM_3GPP_NETWORK_AVAILABILITY_FORBIDDEN, NULL, NULL, (gchar *) "26202", MM_MODEM_ACCESS_TECHNOLOGY_GSM }, { MM_MODEM_3GPP_NETWORK_AVAILABILITY_FORBIDDEN, NULL, NULL, (gchar *) "26207", MM_MODEM_ACCESS_TECHNOLOGY_GSM }, { MM_MODEM_3GPP_NETWORK_AVAILABILITY_FORBIDDEN, NULL, NULL, (gchar *) "26201", MM_MODEM_ACCESS_TECHNOLOGY_GSM }, { MM_MODEM_3GPP_NETWORK_AVAILABILITY_FORBIDDEN, NULL, NULL, (gchar *) "26207", MM_MODEM_ACCESS_TECHNOLOGY_GSM }, }; test_cops_results ("Sony-Ericsson K600i", reply, MM_MODEM_CHARSET_GSM, &expected[0], G_N_ELEMENTS (expected)); } static void test_cops_response_samsung_z810 (void *f, gpointer d) { /* Ensure commas within quotes don't trip up the parser */ const char *reply = "+COPS: (1,\"T-Mobile USA, In\",\"T-Mobile\",\"310260\",0),(1,\"AT&T\",\"AT&T\",\"310410\",0),,(0,1,2,3,4),(0,1,2)"; static MM3gppNetworkInfo expected[] = { { MM_MODEM_3GPP_NETWORK_AVAILABILITY_AVAILABLE, (gchar *) "T-Mobile USA, In", (gchar *) "T-Mobile", (gchar *) "310260", MM_MODEM_ACCESS_TECHNOLOGY_GSM }, { MM_MODEM_3GPP_NETWORK_AVAILABILITY_AVAILABLE, (gchar *) "AT&T", (gchar *) "AT&T", (gchar *) "310410", MM_MODEM_ACCESS_TECHNOLOGY_GSM }, }; test_cops_results ("Samsung Z810", reply, MM_MODEM_CHARSET_GSM, &expected[0], G_N_ELEMENTS (expected)); } static void test_cops_response_ublox_lara (void *f, gpointer d) { /* strings in UCS2 */ const char *reply = "+COPS: " "(2,\"004D006F007600690073007400610072\",\"004D006F007600690073007400610072\",\"00320031003400300037\",7)," "(1,\"0059004F00490047004F\",\"0059004F00490047004F\",\"00320031003400300034\",7)," "(1,\"0076006F006400610066006F006E0065002000450053\",\"0076006F00640061002000450053\",\"00320031003400300031\",7)," "(1,\"004F00720061006E00670065002000530050\",\"00450053005000520054\",\"00320031003400300033\",0)," "(1,\"0076006F006400610066006F006E0065002000450053\",\"0076006F00640061002000450053\",\"00320031003400300031\",0)," "(1,\"004F00720061006E00670065002000530050\",\"00450053005000520054\",\"00320031003400300033\",7)"; static MM3gppNetworkInfo expected[] = { { MM_MODEM_3GPP_NETWORK_AVAILABILITY_CURRENT, (gchar *) "Movistar", (gchar *) "Movistar", (gchar *) "21407", MM_MODEM_ACCESS_TECHNOLOGY_LTE }, { MM_MODEM_3GPP_NETWORK_AVAILABILITY_AVAILABLE, (gchar *) "YOIGO", (gchar *) "YOIGO", (gchar *) "21404", MM_MODEM_ACCESS_TECHNOLOGY_LTE }, { MM_MODEM_3GPP_NETWORK_AVAILABILITY_AVAILABLE, (gchar *) "vodafone ES", (gchar *) "voda ES", (gchar *) "21401", MM_MODEM_ACCESS_TECHNOLOGY_LTE }, { MM_MODEM_3GPP_NETWORK_AVAILABILITY_AVAILABLE, (gchar *) "Orange SP", (gchar *) "ESPRT", (gchar *) "21403", MM_MODEM_ACCESS_TECHNOLOGY_GSM }, { MM_MODEM_3GPP_NETWORK_AVAILABILITY_AVAILABLE, (gchar *) "vodafone ES", (gchar *) "voda ES", (gchar *) "21401", MM_MODEM_ACCESS_TECHNOLOGY_GSM }, { MM_MODEM_3GPP_NETWORK_AVAILABILITY_AVAILABLE, (gchar *) "Orange SP", (gchar *) "ESPRT", (gchar *) "21403", MM_MODEM_ACCESS_TECHNOLOGY_LTE }, }; test_cops_results ("u-blox LARA", reply, MM_MODEM_CHARSET_UCS2, &expected[0], G_N_ELEMENTS (expected)); } static void test_cops_response_em9191 (void *f, gpointer d) { const char *reply = "+COPS: " "(1,\"Telekom.de\",\"TDG\",\"26201\",12)," "(1,\"Telekom.de\",\"Telekom.\",\"26201\",7)," "(1,\"o2 - de\",\"o2 - de\",\"26203\",7)," "(1,\"vodafone.de\",\"Vodafone\",\"26202\",7)" /* these next ones will be ignored */ "(0,1,2,3,4)," "(0,1,2)"; static MM3gppNetworkInfo expected[] = { { MM_MODEM_3GPP_NETWORK_AVAILABILITY_AVAILABLE, (gchar *) "Telekom.de", (gchar *) "TDG", (gchar *) "26201", MM_MODEM_ACCESS_TECHNOLOGY_5GNR }, { MM_MODEM_3GPP_NETWORK_AVAILABILITY_AVAILABLE, (gchar *) "Telekom.de", (gchar *) "Telekom.", (gchar *) "26201", MM_MODEM_ACCESS_TECHNOLOGY_LTE }, { MM_MODEM_3GPP_NETWORK_AVAILABILITY_AVAILABLE, (gchar *) "o2 - de", (gchar *) "o2 - de", (gchar *) "26203", MM_MODEM_ACCESS_TECHNOLOGY_LTE }, { MM_MODEM_3GPP_NETWORK_AVAILABILITY_AVAILABLE, (gchar *) "vodafone.de", (gchar *) "Vodafone", (gchar *) "26202", MM_MODEM_ACCESS_TECHNOLOGY_LTE }, }; test_cops_results ("EM9191", reply, MM_MODEM_CHARSET_GSM, &expected[0], G_N_ELEMENTS (expected)); } static void test_cops_response_gsm_invalid (void *f, gpointer d) { const gchar *reply = "+COPS: (0,1,2,3),(1,2,3,4)"; GList *results; GError *error = NULL; results = mm_3gpp_parse_cops_test_response (reply, MM_MODEM_CHARSET_GSM, NULL, &error); g_assert (results == NULL); g_assert_no_error (error); } 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)"; GList *results; GError *error = NULL; results = mm_3gpp_parse_cops_test_response (reply, MM_MODEM_CHARSET_GSM, NULL, &error); g_assert (results == NULL); g_assert_no_error (error); } /*****************************************************************************/ /* Test COPS? responses */ typedef struct { const gchar *str; guint mode; guint format; const gchar *operator; MMModemAccessTechnology act; } CopsQueryData; static void test_cops_query_data (const CopsQueryData *item) { gboolean result; GError *error = NULL; guint mode = G_MAXUINT; guint format = G_MAXUINT; gchar *operator = NULL; MMModemAccessTechnology act = MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN; result = mm_3gpp_parse_cops_read_response (item->str, &mode, &format, &operator, &act, NULL, &error); g_assert_no_error (error); g_assert (result); g_assert_cmpuint (mode, ==, item->mode); g_assert_cmpuint (format, ==, item->format); g_assert_cmpstr (operator, ==, item->operator); g_assert_cmpuint (act, ==, item->act); g_free (operator); } static const CopsQueryData cops_query_data[] = { { .str = "+COPS: 1,0,\"CHINA MOBILE\"", .mode = 1, .format = 0, .operator = "CHINA MOBILE", .act = MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN }, { .str = "+COPS: 1,0,\"CHINA MOBILE\",7", .mode = 1, .format = 0, .operator = "CHINA MOBILE", .act = MM_MODEM_ACCESS_TECHNOLOGY_LTE }, { .str = "+COPS: 1,2,\"46000\"", .mode = 1, .format = 2, .operator = "46000", .act = MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN }, { .str = "+COPS: 1,2,\"46000\",7", .mode = 1, .format = 2, .operator = "46000", .act = MM_MODEM_ACCESS_TECHNOLOGY_LTE }, }; static void test_cops_query (void) { guint i; for (i = 0; i < G_N_ELEMENTS (cops_query_data); i++) test_cops_query_data (&cops_query_data[i]); } /*****************************************************************************/ typedef struct { const gchar *input; MMModemCharset charset; const gchar *normalized; } NormalizeOperatorTest; static const NormalizeOperatorTest normalize_operator_tests[] = { /* charset unknown */ { "Verizon", MM_MODEM_CHARSET_UNKNOWN, "Verizon" }, { "311480", MM_MODEM_CHARSET_UNKNOWN, "311480" }, /* charset configured as IRA (ASCII) */ { "Verizon", MM_MODEM_CHARSET_IRA, "Verizon" }, { "311480", MM_MODEM_CHARSET_IRA, "311480" }, /* charset configured as GSM7 */ { "Verizon", MM_MODEM_CHARSET_GSM, "Verizon" }, { "311480", MM_MODEM_CHARSET_GSM, "311480" }, /* charset configured as UCS2 */ { "0056006500720069007A006F006E", MM_MODEM_CHARSET_UCS2, "Verizon" }, { "003300310031003400380030", MM_MODEM_CHARSET_UCS2, "311480" }, { "Verizon", MM_MODEM_CHARSET_UCS2, "Verizon" }, { "311480", MM_MODEM_CHARSET_UCS2, "311480" }, }; static void common_test_normalize_operator (const NormalizeOperatorTest *t) { gchar *str; str = g_strdup (t->input); mm_3gpp_normalize_operator (&str, t->charset, NULL); if (!t->normalized) g_assert (!str); else g_assert_cmpstr (str, ==, t->normalized); g_free (str); } static void test_normalize_operator (void) { guint i; for (i = 0; i < G_N_ELEMENTS (normalize_operator_tests); i++) common_test_normalize_operator (&normalize_operator_tests[i]); } /*****************************************************************************/ /* Test CREG/CGREG responses and unsolicited messages */ typedef struct { GPtrArray *solicited_creg; GPtrArray *unsolicited_creg; } RegTestData; static RegTestData * reg_test_data_new (void) { RegTestData *data; data = g_malloc0 (sizeof (RegTestData)); data->solicited_creg = mm_3gpp_creg_regex_get (TRUE); data->unsolicited_creg = mm_3gpp_creg_regex_get (FALSE); return data; } static void reg_test_data_free (RegTestData *data) { mm_3gpp_creg_regex_destroy (data->solicited_creg); mm_3gpp_creg_regex_destroy (data->unsolicited_creg); g_free (data); } typedef struct { MMModem3gppRegistrationState state; gulong lac; gulong ci; MMModemAccessTechnology act; guint regex_num; gboolean cgreg; gboolean cereg; gboolean c5greg; } CregResult; static void test_creg_match (const char *test, gboolean solicited, const char *reply, RegTestData *data, const CregResult *result) { g_autoptr(GMatchInfo) info = NULL; guint i; MMModem3gppRegistrationState state = MM_MODEM_3GPP_REGISTRATION_STATE_UNKNOWN; MMModemAccessTechnology access_tech = MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN; gulong lac = 0; gulong ci = 0; GError *error = NULL; gboolean success; gboolean cgreg = FALSE; gboolean cereg = FALSE; gboolean c5greg = FALSE; guint regex_num = 0; GPtrArray *array; g_assert (reply); g_assert (test); g_assert (data); g_assert (result); g_debug ("Testing '%s' +C%sREG %s response...", test, result->cgreg ? "G" : "", solicited ? "solicited" : "unsolicited"); array = solicited ? data->solicited_creg : data->unsolicited_creg; for (i = 0; i < array->len; i++) { GRegex *r = g_ptr_array_index (array, i); if (g_regex_match (r, reply, 0, &info)) { g_debug (" matched with %d", i); regex_num = i; break; } g_clear_pointer (&info, g_match_info_free); } g_debug (" regex_num (%u) == result->regex_num (%u)", regex_num, result->regex_num); g_assert (info != NULL); g_assert_cmpuint (regex_num, ==, result->regex_num); success = mm_3gpp_parse_creg_response (info, NULL, &state, &lac, &ci, &access_tech, &cgreg, &cereg, &c5greg, &error); g_assert (success); g_assert_no_error (error); g_assert_cmpuint (state, ==, result->state); g_assert_cmpuint (lac, ==, result->lac); g_assert_cmpuint (ci, ==, result->ci); g_assert_cmpuint (access_tech, ==, result->act); g_assert_cmpuint (cgreg, ==, result->cgreg); g_assert_cmpuint (cereg, ==, result->cereg); g_assert_cmpuint (c5greg, ==, result->c5greg); } static void test_creg1_solicited (void *f, gpointer d) { RegTestData *data = (RegTestData *) d; const char *reply = "+CREG: 1,3"; const CregResult result = { MM_MODEM_3GPP_REGISTRATION_STATE_DENIED, 0, 0, MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN, 1, FALSE, FALSE, FALSE }; test_creg_match ("CREG=1", TRUE, reply, data, &result); } static void test_creg1_unsolicited (void *f, gpointer d) { RegTestData *data = (RegTestData *) d; const char *reply = "\r\n+CREG: 3\r\n"; const CregResult result = { MM_MODEM_3GPP_REGISTRATION_STATE_DENIED, 0, 0, MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN, 0, FALSE, FALSE, FALSE }; test_creg_match ("CREG=1", FALSE, reply, data, &result); } static void test_creg2_mercury_solicited (void *f, gpointer d) { RegTestData *data = (RegTestData *) d; const char *reply = "+CREG: 1,1,84CD,00D30173"; const CregResult result = { MM_MODEM_3GPP_REGISTRATION_STATE_HOME, 0x84cd, 0xd30173, MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN, 3, FALSE, FALSE, FALSE }; test_creg_match ("Sierra Mercury CREG=2", TRUE, reply, data, &result); } static void test_creg2_mercury_unsolicited (void *f, gpointer d) { RegTestData *data = (RegTestData *) d; const char *reply = "\r\n+CREG: 1,84CD,00D30156\r\n"; const CregResult result = { MM_MODEM_3GPP_REGISTRATION_STATE_HOME, 0x84cd, 0xd30156, MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN, 2, FALSE, FALSE, FALSE }; test_creg_match ("Sierra Mercury CREG=2", FALSE, reply, data, &result); } static void test_creg2_sek850i_solicited (void *f, gpointer d) { RegTestData *data = (RegTestData *) d; const char *reply = "+CREG: 2,1,\"CE00\",\"01CEAD8F\""; const CregResult result = { MM_MODEM_3GPP_REGISTRATION_STATE_HOME, 0xce00, 0x01cead8f, MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN, 3, FALSE, FALSE, FALSE }; test_creg_match ("Sony Ericsson K850i CREG=2", TRUE, reply, data, &result); } static void test_creg2_sek850i_unsolicited (void *f, gpointer d) { RegTestData *data = (RegTestData *) d; const char *reply = "\r\n+CREG: 1,\"CE00\",\"00005449\"\r\n"; const CregResult result = { MM_MODEM_3GPP_REGISTRATION_STATE_HOME, 0xce00, 0x5449, MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN, 2, FALSE, FALSE, FALSE }; test_creg_match ("Sony Ericsson K850i CREG=2", FALSE, reply, data, &result); } static void test_creg2_e160g_solicited_unregistered (void *f, gpointer d) { RegTestData *data = (RegTestData *) d; const char *reply = "+CREG: 2,0,00,0"; const CregResult result = { MM_MODEM_3GPP_REGISTRATION_STATE_IDLE, 0, 0, MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN, 3, FALSE, FALSE, FALSE }; test_creg_match ("Huawei E160G unregistered CREG=2", TRUE, reply, data, &result); } static void test_creg2_e160g_solicited (void *f, gpointer d) { RegTestData *data = (RegTestData *) d; const char *reply = "+CREG: 2,1,8BE3,2BAF"; const CregResult result = { MM_MODEM_3GPP_REGISTRATION_STATE_HOME, 0x8be3, 0x2baf, MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN, 3, FALSE, FALSE, FALSE }; test_creg_match ("Huawei E160G CREG=2", TRUE, reply, data, &result); } static void test_creg2_e160g_unsolicited (void *f, gpointer d) { RegTestData *data = (RegTestData *) d; const char *reply = "\r\n+CREG: 1,8BE3,2BAF\r\n"; const CregResult result = { MM_MODEM_3GPP_REGISTRATION_STATE_HOME, 0x8be3, 0x2baf, MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN, 2, FALSE, FALSE, FALSE }; test_creg_match ("Huawei E160G CREG=2", FALSE, reply, data, &result); } static void test_creg2_tm506_solicited (void *f, gpointer d) { RegTestData *data = (RegTestData *) d; const char *reply = "+CREG: 2,1,\"8BE3\",\"00002BAF\""; const CregResult result = { MM_MODEM_3GPP_REGISTRATION_STATE_HOME, 0x8BE3, 0x2BAF, MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN, 3, FALSE, FALSE, FALSE }; /* Test leading zeros in the CI */ test_creg_match ("Sony Ericsson TM-506 CREG=2", TRUE, reply, data, &result); } static void test_creg2_xu870_unsolicited_unregistered (void *f, gpointer d) { RegTestData *data = (RegTestData *) d; const char *reply = "\r\n+CREG: 2,,\r\n"; const CregResult result = { MM_MODEM_3GPP_REGISTRATION_STATE_SEARCHING, 0, 0, MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN, 2, FALSE, FALSE, FALSE }; test_creg_match ("Novatel XU870 unregistered CREG=2", FALSE, reply, data, &result); } static void test_creg2_iridium_solicited (void *f, gpointer d) { RegTestData *data = (RegTestData *) d; const char *reply = "+CREG:002,001,\"18d8\",\"ffff\""; const CregResult result = { MM_MODEM_3GPP_REGISTRATION_STATE_HOME, 0x18D8, 0xFFFF, MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN, 4, FALSE, FALSE, FALSE }; test_creg_match ("Iridium, CREG=2", TRUE, reply, data, &result); } static void test_creg2_no_leading_zeros_solicited (void *f, gpointer d) { RegTestData *data = (RegTestData *) d; const char *reply = "+CREG:2,1,0001,0010"; const CregResult result = { MM_MODEM_3GPP_REGISTRATION_STATE_HOME, 0x0001, 0x0010, MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN, 3, FALSE, FALSE, FALSE }; test_creg_match ("solicited CREG=2 with no leading zeros in integer fields", TRUE, reply, data, &result); } static void test_creg2_leading_zeros_solicited (void *f, gpointer d) { RegTestData *data = (RegTestData *) d; const char *reply = "+CREG:002,001,\"0001\",\"0010\""; const CregResult result = { MM_MODEM_3GPP_REGISTRATION_STATE_HOME, 0x0001, 0x0010, MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN, 4, FALSE, FALSE, FALSE }; test_creg_match ("solicited CREG=2 with leading zeros in integer fields", TRUE, reply, data, &result); } static void test_creg2_no_leading_zeros_unsolicited (void *f, gpointer d) { RegTestData *data = (RegTestData *) d; const char *reply = "\r\n+CREG: 1,0001,0010,0\r\n"; const CregResult result = { MM_MODEM_3GPP_REGISTRATION_STATE_HOME, 0x0001, 0x0010, MM_MODEM_ACCESS_TECHNOLOGY_GSM, 5, FALSE, FALSE, FALSE }; test_creg_match ("unsolicited CREG=2 with no leading zeros in integer fields", FALSE, reply, data, &result); } static void test_creg2_leading_zeros_unsolicited (void *f, gpointer d) { RegTestData *data = (RegTestData *) d; const char *reply = "\r\n+CREG: 001,\"0001\",\"0010\",000\r\n"; const CregResult result = { MM_MODEM_3GPP_REGISTRATION_STATE_HOME, 0x0001, 0x0010, MM_MODEM_ACCESS_TECHNOLOGY_GSM, 6, FALSE, FALSE, FALSE }; test_creg_match ("unsolicited CREG=2 with leading zeros in integer fields", FALSE, reply, data, &result); } static void test_creg2_ublox_solicited (void *f, gpointer d) { RegTestData *data = (RegTestData *) d; const gchar *reply = "\r\n+CREG: 2,6,\"8B37\",\"0A265185\",7\r\n"; const CregResult result = { MM_MODEM_3GPP_REGISTRATION_STATE_HOME_SMS_ONLY, 0x8B37, 0x0A265185, MM_MODEM_ACCESS_TECHNOLOGY_LTE, 7, FALSE, FALSE, FALSE }; test_creg_match ("Ublox Toby-L2 solicited while on LTE", TRUE, reply, data, &result); } static void test_creg2_ublox_unsolicited (void *f, gpointer d) { RegTestData *data = (RegTestData *) d; const gchar *reply = "\r\n+CREG: 6,\"8B37\",\"0A265185\",7\r\n"; const CregResult result = { MM_MODEM_3GPP_REGISTRATION_STATE_HOME_SMS_ONLY, 0x8B37, 0x0A265185, MM_MODEM_ACCESS_TECHNOLOGY_LTE, 5, FALSE, FALSE, FALSE }; test_creg_match ("Ublox Toby-L2 unsolicited while on LTE", FALSE, reply, data, &result); } static void test_cgreg1_solicited (void *f, gpointer d) { RegTestData *data = (RegTestData *) d; const char *reply = "+CGREG: 1,3"; const CregResult result = { MM_MODEM_3GPP_REGISTRATION_STATE_DENIED, 0, 0, MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN, 1, TRUE, FALSE, FALSE }; test_creg_match ("CGREG=1", TRUE, reply, data, &result); } static void test_cgreg1_unsolicited (void *f, gpointer d) { RegTestData *data = (RegTestData *) d; const char *reply = "\r\n+CGREG: 3\r\n"; const CregResult result = { MM_MODEM_3GPP_REGISTRATION_STATE_DENIED, 0, 0, MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN, 0, TRUE, FALSE, FALSE }; test_creg_match ("CGREG=1", FALSE, reply, data, &result); } static void test_cgreg2_f3607gw_solicited (void *f, gpointer d) { RegTestData *data = (RegTestData *) d; const char *reply = "+CGREG: 2,1,\"8BE3\",\"00002B5D\",3"; const CregResult result = { MM_MODEM_3GPP_REGISTRATION_STATE_HOME, 0x8BE3, 0x2B5D, MM_MODEM_ACCESS_TECHNOLOGY_EDGE, 7, TRUE, FALSE, FALSE }; test_creg_match ("Ericsson F3607gw CGREG=2", TRUE, reply, data, &result); } static void test_cgreg2_f3607gw_unsolicited (void *f, gpointer d) { RegTestData *data = (RegTestData *) d; const char *reply = "\r\n+CGREG: 1,\"8BE3\",\"00002B5D\",3\r\n"; const CregResult result = { MM_MODEM_3GPP_REGISTRATION_STATE_HOME, 0x8BE3, 0x2B5D, MM_MODEM_ACCESS_TECHNOLOGY_EDGE, 5, TRUE, FALSE, FALSE }; test_creg_match ("Ericsson F3607gw CGREG=2", FALSE, reply, data, &result); } static void test_creg2_md400_unsolicited (void *f, gpointer d) { RegTestData *data = (RegTestData *) d; const char *reply = "\r\n+CREG: 2,5,\"0502\",\"0404736D\"\r\n"; const CregResult result = { MM_MODEM_3GPP_REGISTRATION_STATE_ROAMING, 0x0502, 0x0404736D, MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN, 3, FALSE, FALSE, FALSE }; test_creg_match ("Sony-Ericsson MD400 CREG=2", FALSE, reply, data, &result); } static void test_cgreg2_md400_unsolicited (void *f, gpointer d) { RegTestData *data = (RegTestData *) d; const char *reply = "\r\n+CGREG: 5,\"0502\",\"0404736D\",2\r\n"; const CregResult result = { MM_MODEM_3GPP_REGISTRATION_STATE_ROAMING, 0x0502, 0x0404736D, MM_MODEM_ACCESS_TECHNOLOGY_UMTS, 5, TRUE, FALSE, FALSE }; test_creg_match ("Sony-Ericsson MD400 CGREG=2", FALSE, reply, data, &result); } static void test_creg_cgreg_multi_unsolicited (void *f, gpointer d) { RegTestData *data = (RegTestData *) d; const char *reply = "\r\n+CREG: 5\r\n\r\n+CGREG: 0\r\n"; const CregResult result = { MM_MODEM_3GPP_REGISTRATION_STATE_ROAMING, 0, 0, MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN, 0, FALSE, FALSE, FALSE }; test_creg_match ("Multi CREG/CGREG", FALSE, reply, data, &result); } static void test_creg_cgreg_multi2_unsolicited (void *f, gpointer d) { RegTestData *data = (RegTestData *) d; const char *reply = "\r\n+CGREG: 0\r\n\r\n+CREG: 5\r\n"; const CregResult result = { MM_MODEM_3GPP_REGISTRATION_STATE_IDLE, 0, 0, MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN, 0, TRUE, FALSE, FALSE }; test_creg_match ("Multi CREG/CGREG #2", FALSE, reply, data, &result); } static void test_cgreg2_x220_unsolicited (void *f, gpointer d) { RegTestData *data = (RegTestData *) d; const char *reply = "\r\n+CGREG: 2,1, 81ED, 1A9CEB\r\n"; const CregResult result = { MM_MODEM_3GPP_REGISTRATION_STATE_HOME, 0x81ED, 0x1A9CEB, MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN, 3, TRUE, FALSE, FALSE }; /* Tests random spaces in response */ test_creg_match ("Alcatel One-Touch X220D CGREG=2", FALSE, reply, data, &result); } static void test_creg2_s8500_wave_unsolicited (void *f, gpointer d) { RegTestData *data = (RegTestData *) d; const char *reply = "\r\n+CREG: 2,1,000B,2816, B, C2816\r\n"; const CregResult result = { MM_MODEM_3GPP_REGISTRATION_STATE_HOME, 0x000B, 0x2816, MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN, 8, FALSE, FALSE, FALSE }; test_creg_match ("Samsung Wave S8500 CREG=2", FALSE, reply, data, &result); } static void test_creg2_gobi_weird_solicited (void *f, gpointer d) { RegTestData *data = (RegTestData *) d; const char *reply = "\r\n+CREG: 2,1, 0 5, 2715\r\n"; const CregResult result = { MM_MODEM_3GPP_REGISTRATION_STATE_HOME, 0x0000, 0x2715, MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN, 3, FALSE, FALSE, FALSE }; test_creg_match ("Qualcomm Gobi 1000 CREG=2", TRUE, reply, data, &result); } static void test_cgreg2_unsolicited_with_rac (void *f, gpointer d) { RegTestData *data = (RegTestData *) d; const char *reply = "\r\n+CGREG: 1,\"1422\",\"00000142\",3,\"00\"\r\n"; const CregResult result = { MM_MODEM_3GPP_REGISTRATION_STATE_HOME, 0x1422, 0x0142, MM_MODEM_ACCESS_TECHNOLOGY_EDGE, 9, TRUE, FALSE, FALSE }; test_creg_match ("CGREG=2 with RAC", FALSE, reply, data, &result); } static void test_cereg1_solicited (void *f, gpointer d) { RegTestData *data = (RegTestData *) d; const char *reply = "+CEREG: 1,3"; const CregResult result = { MM_MODEM_3GPP_REGISTRATION_STATE_DENIED, 0, 0, MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN, 1, FALSE, TRUE, FALSE }; test_creg_match ("CEREG=1", TRUE, reply, data, &result); } static void test_cereg1_unsolicited (void *f, gpointer d) { RegTestData *data = (RegTestData *) d; const char *reply = "\r\n+CEREG: 3\r\n"; const CregResult result = { MM_MODEM_3GPP_REGISTRATION_STATE_DENIED, 0, 0, MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN, 0, FALSE, TRUE, FALSE }; test_creg_match ("CEREG=1", FALSE, reply, data, &result); } static void test_cereg2_solicited (void *f, gpointer d) { RegTestData *data = (RegTestData *) d; const char *reply = "\r\n+CEREG: 2,1, 1F00, 79D903 ,7\r\n"; const CregResult result = { MM_MODEM_3GPP_REGISTRATION_STATE_HOME, 0x1F00, 0x79D903, MM_MODEM_ACCESS_TECHNOLOGY_LTE, 7, FALSE, TRUE, FALSE }; test_creg_match ("CEREG=2", TRUE, reply, data, &result); } static void test_cereg2_unsolicited (void *f, gpointer d) { RegTestData *data = (RegTestData *) d; const char *reply = "\r\n+CEREG: 1, 1F00, 79D903 ,7\r\n"; const CregResult result = { MM_MODEM_3GPP_REGISTRATION_STATE_HOME, 0x1F00, 0x79D903, MM_MODEM_ACCESS_TECHNOLOGY_LTE, 5, FALSE, TRUE, FALSE }; test_creg_match ("CEREG=2", FALSE, reply, data, &result); } static void test_cereg2_altair_lte_solicited (void *f, gpointer d) { RegTestData *data = (RegTestData *) d; const char *reply = "\r\n+CEREG: 1, 2, 0001, 00000100, 7\r\n"; const CregResult result = { MM_MODEM_3GPP_REGISTRATION_STATE_SEARCHING, 0x0001, 0x00000100, MM_MODEM_ACCESS_TECHNOLOGY_LTE, 7, FALSE, TRUE, FALSE }; test_creg_match ("Altair LTE CEREG=2", FALSE, reply, data, &result); } static void test_cereg2_altair_lte_unsolicited (void *f, gpointer d) { RegTestData *data = (RegTestData *) d; const char *reply = "\r\n+CEREG: 2, 0001, 00000100, 7\r\n"; const CregResult result = { MM_MODEM_3GPP_REGISTRATION_STATE_SEARCHING, 0x0001, 0x00000100, MM_MODEM_ACCESS_TECHNOLOGY_LTE, 5, FALSE, TRUE, FALSE }; test_creg_match ("Altair LTE CEREG=2", FALSE, reply, data, &result); } static void test_cereg2_novatel_lte_solicited (void *f, gpointer d) { RegTestData *data = (RegTestData *) d; const char *reply = "\r\n+CEREG: 2,1, 1F00, 20 ,79D903 ,7\r\n"; const CregResult result = { MM_MODEM_3GPP_REGISTRATION_STATE_HOME, 0x1F00, 0x79D903, MM_MODEM_ACCESS_TECHNOLOGY_LTE, 11, FALSE, TRUE, FALSE }; test_creg_match ("Novatel LTE E362 CEREG=2", TRUE, reply, data, &result); } static void test_cereg2_novatel_lte_unsolicited (void *f, gpointer d) { RegTestData *data = (RegTestData *) d; const char *reply = "\r\n+CEREG: 1, 1F00, 20 ,79D903 ,7\r\n"; const CregResult result = { MM_MODEM_3GPP_REGISTRATION_STATE_HOME, 0x1F00, 0x79D903, MM_MODEM_ACCESS_TECHNOLOGY_LTE, 10, FALSE, TRUE, FALSE }; test_creg_match ("Novatel LTE E362 CEREG=2", FALSE, reply, data, &result); } static void test_cgreg2_thuraya_solicited (void *f, gpointer d) { RegTestData *data = (RegTestData *) d; const char *reply = "+CGREG: 2, 1, \"0426\", \"F00F\""; const CregResult result = { MM_MODEM_3GPP_REGISTRATION_STATE_HOME, 0x0426, 0xF00F, MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN, 3, TRUE, FALSE, FALSE }; test_creg_match ("Thuraya solicited CREG=2", TRUE, reply, data, &result); } static void test_cgreg2_thuraya_unsolicited (void *f, gpointer d) { RegTestData *data = (RegTestData *) d; const char *reply = "\r\n+CGREG: 1, \"0426\", \"F00F\"\r\n"; const CregResult result = { MM_MODEM_3GPP_REGISTRATION_STATE_HOME, 0x0426, 0xF00F, MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN, 2, TRUE, FALSE, FALSE }; test_creg_match ("Thuraya unsolicited CREG=2", FALSE, reply, data, &result); } static void test_c5greg1_solicited (void *f, gpointer d) { RegTestData *data = (RegTestData *) d; const char *reply = "+C5GREG: 1,3"; const CregResult result = { MM_MODEM_3GPP_REGISTRATION_STATE_DENIED, 0, 0, MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN, 1, FALSE, FALSE, TRUE }; test_creg_match ("C5GREG=1", TRUE, reply, data, &result); } static void test_c5greg1_unsolicited (void *f, gpointer d) { RegTestData *data = (RegTestData *) d; const char *reply = "\r\n+C5GREG: 3\r\n"; const CregResult result = { MM_MODEM_3GPP_REGISTRATION_STATE_DENIED, 0, 0, MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN, 0, FALSE, FALSE, TRUE }; test_creg_match ("C5GREG=1", FALSE, reply, data, &result); } static void test_c5greg2_solicited (void *f, gpointer d) { RegTestData *data = (RegTestData *) d; const char *reply = "+C5GREG: 2,1,1F00,79D903,11,6,ABCDEF"; const CregResult result = { MM_MODEM_3GPP_REGISTRATION_STATE_HOME, 0x1F00, 0x79D903, MM_MODEM_ACCESS_TECHNOLOGY_5GNR, 13, FALSE, FALSE, TRUE }; test_creg_match ("C5GREG=2", TRUE, reply, data, &result); } static void test_c5greg2_unsolicited (void *f, gpointer d) { RegTestData *data = (RegTestData *) d; const char *reply = "\r\n+C5GREG: 1,1F00,79D903,11,6,ABCDEF\r\n"; const CregResult result = { MM_MODEM_3GPP_REGISTRATION_STATE_HOME, 0x1F00, 0x79D903, MM_MODEM_ACCESS_TECHNOLOGY_5GNR, 12, FALSE, FALSE, TRUE }; test_creg_match ("C5GREG=2", FALSE, reply, data, &result); } /*****************************************************************************/ /* Test CSCS responses */ static void test_cscs_icon225_support_response (void *f, gpointer d) { const char *reply = "\r\n+CSCS: (\"IRA\",\"GSM\",\"UCS2\")\r\n"; MMModemCharset charsets = MM_MODEM_CHARSET_UNKNOWN; gboolean success; success = mm_3gpp_parse_cscs_test_response (reply, &charsets); g_assert (success); g_assert (charsets == (MM_MODEM_CHARSET_IRA | MM_MODEM_CHARSET_GSM | MM_MODEM_CHARSET_UCS2)); } static void test_cscs_sierra_mercury_support_response (void *f, gpointer d) { const char *reply = "\r\n+CSCS: (\"IRA\",\"GSM\",\"UCS2\",\"PCCP437\")\r\n"; MMModemCharset charsets = MM_MODEM_CHARSET_UNKNOWN; gboolean success; success = mm_3gpp_parse_cscs_test_response (reply, &charsets); g_assert (success); g_assert (charsets == (MM_MODEM_CHARSET_IRA | MM_MODEM_CHARSET_GSM | MM_MODEM_CHARSET_UCS2 | MM_MODEM_CHARSET_PCCP437)); } static void test_cscs_buslink_support_response (void *f, gpointer d) { const char *reply = "\r\n+CSCS: (\"8859-1\",\"ASCII\",\"GSM\",\"UCS2\",\"UTF8\")\r\n"; MMModemCharset charsets = MM_MODEM_CHARSET_UNKNOWN; gboolean success; success = mm_3gpp_parse_cscs_test_response (reply, &charsets); g_assert (success); g_assert (charsets == (MM_MODEM_CHARSET_8859_1 | MM_MODEM_CHARSET_IRA | MM_MODEM_CHARSET_GSM | MM_MODEM_CHARSET_UCS2 | MM_MODEM_CHARSET_UTF8)); } static void test_cscs_blackberry_support_response (void *f, gpointer d) { const char *reply = "\r\n+CSCS: \"IRA\"\r\n"; MMModemCharset charsets = MM_MODEM_CHARSET_UNKNOWN; gboolean success; success = mm_3gpp_parse_cscs_test_response (reply, &charsets); g_assert (success); g_assert (charsets == MM_MODEM_CHARSET_IRA); } /*****************************************************************************/ /* Test Device Identifier builders */ typedef struct { const char *devid; const char *desc; guint vid; guint pid; const char *ati; const char *ati1; const char *gsn; const char *revision; const char *model; const char *manf; } DevidItem; static DevidItem devids[] = { { "36e7a8e78637fd380b2664507ea5de8fc317d05b", "Huawei E1550", 0x12d1, 0x1001, "\nManufacturer: huawei\n" "Model: E1550\n" "Revision: 11.608.09.01.21\n" "IMEI: 235012412595195\n" "+GCAP: +CGSM,+FCLASS,+DS\n", NULL, "\n235012412595195\n", "\n11.608.09.01.21\n", "\nE1550\n", "\nhuawei\n" }, { "33b0fc4a06af5448df656ce12925979acf1cb600", "Huawei EC121", 0x12d1, 0x1411, "\nManufacturer: HUAWEI INCORPORATED\n" "Model: EC121\n" "Revision: 11.100.17.00.114\n" "ESN: +GSN:12de4fa6\n" "+CIS707-A, +MS, +ES, +DS, +FCLASS\n", NULL, "\n12de4fa6\n", "\n11.100.17.00.114\n", "\nEC121\n", "\nHUAWEI INCORPORATED\n" }, { "d17f016a402354eaa1e24855f4308fafca9cadb1", "Sierra USBConnect Mercury", 0x1199, 0x6880, "\nManufacturer: Sierra Wireless, Inc.\n" "Model: C885\n" "Revision: J1_0_1_26AP C:/WS/FW/J1_0_1_26AP/MSM7200A/SRC/AMSS 2009/01/30 07:58:06\n" "IMEI: 987866969112306\n" "IMEI SV: 6\n" "FSN: D603478104511\n" "3GPP Release 6\n" "+GCAP: +CGSM,+DS,+ES\n", NULL, "\n987866969112306\n", "\nJ1_0_1_26AP C:/WS/FW/J1_0_1_26AP/MSM7200A/SRC/AMSS 2009/01/30 07:58:06\n", "\nC885\n", "\nSierra Wireless, Inc.\n" }, { "345e9eaad7624393aca85cde9bd859edf462414c", "ZTE MF627", 0x19d2, 0x0031, "\nManufacturer: ZTE INCORPORATED\n" "Model: MF627\n" "Revision: BD_3GHAP673A4V1.0.0B02\n" "IMEI: 023589923858188\n" "+GCAP: +CGSM,+FCLASS,+DS\n", NULL, "\n023589923858188\n", "\nBD_3GHAP673A4V1.0.0B02\n", "\nMF627\n", "\nZTE INCORPORATED\n" }, { "69fa133a668b6f4dbf39b73500fd153ec240c73f", "Sony-Ericsson MD300", 0x0fce, 0xd0cf, "\nMD300\n", "\nR3A018\n", "\n349583712939483\n", "\nR3A018\n", "\nMD300\n", "\nSony Ericsson\n" }, { "3dad89ed7d774938c38188cf29cf1c211e9d360b", "Option iCON 7.2", 0x0af0, 0x6901, "\nManufacturer: Option N.V.\n" "Model: GTM378\n" "Revision: 2.5.21Hd (Date: Jun 17 2008, Time: 12:30:47)\n", NULL, "\n129512359199159,SE393939TS\n", "\n2.5.21Hd (Date: Jun 17 2008, Time: 12:30:47)\n", "\nGTM378\n", "\nOption N.V.\n" }, { "b0acccb956c9eaf2076e03697e74bf998dc44179", "ZTE MF622", 0x19d2, 0x0001, NULL, NULL, "\n235251122555115\n", "\n3UKP671M3V1.0.0B08 3UKP671M3V1.0.0B08 1 [Jan 07 2008 16:00:00]\n", "\nMF622\n", "\nZTE INCORPORATED\n" }, { "29a5b258f1dc6f50c66a1a9a1ecdde97560799ab", "Option 452", 0x0af0, 0x7901, "\nManufacturer: Option N.V.\n" "Model: GlobeTrotter HSUPA Modem\n" "Revision: 2.12.0.0Hd (Date: Oct 29 2009, Time: 09:56:48)\n", "\nManufacturer: Option N.V.\n" "Model: GlobeTrotter HSUPA Modem\n" "Revision: 2.12.0.0Hd (Date: Oct 29 2009, Time: 09:56:48)\n", "\n000125491259519,PH2155R3TR\n", "\n2.12.0.0Hd (Date: Oct 29 2009, Time: 09:56:48)\n", "\nGlobeTrotter HSUPA Modem\n", "\nOption N.V.\n" }, { "c756c67e960e693d5d221e381ea170b60bb9288f", "Novatel XU870", 0x413c, 0x8118, "\nManufacturer: Novatel Wireless Incorporated\n" "Model: DELL XU870 ExpressCard\n" "Revision: 9.5.05.01-02 [2006-10-20 17:19:09]\n" "IMEI: 012051505051501\n" "+GCAP: +CGSM,+DS\n", "\nManufacturer: Novatel Wireless Incorporated\n" "Model: DELL XU870 ExpressCard\n" "Revision: 9.5.05.01-02 [2006-10-20 17:19:09]\n" "IMEI: 012051505051501\n" "+GCAP: +CGSM,+DS\n", "\n012051505051501\n", "\n9.5.05.01-02 [2006-10-20 17:19:09]\n", "\nDELL XU870 ExpressCard\n", "\nNovatel Wireless Incorporated\n" }, { "4162ba918ab54b7776bccc3830e6c6b7a6738244", "Zoom 4596", 0x1c9e, 0x9603, "\nManufacturer: Manufacturer\n" "Model: HSPA USB MODEM\n" "Revision: LQA0021.1.1_M573A\n" "IMEI: 239664699635121\n" "+GCAP: +CGSM,+FCLASS,+DS\n", "\nManufacturer: Manufacturer\n" "Model: HSPA USB MODEM\n" "Revision: LQA0021.1.1_M573A\n" "IMEI: 239664699635121\n" "+GCAP: +CGSM,+FCLASS,+DS\n", "\n239664699635121\n", "\nLQA0021.1.1_M573A\n", "\nHSPA USB MODEM\n", "\nManufacturer\n" }, { "6d3a2fccd3588943a8962fd1e0d3ba752c706660", "C-MOTECH CDX-650", 0x16d8, 0x6512, "\nManufacturer: C-MOTECH Co., Ltd.\r\r\n" "Model: CDX-650 \r\r\n" "Revision: CDX65UAC03\r\r\n" "Esn: 3B0C4B98\r\r\n" "+GCAP: +CIS707A, +MS, +ES, +DS, +FCLASS\r\n", "\nManufacturer: C-MOTECH Co., Ltd.\r\r\n" "Model: CDX-650 \r\r\n" "Revision: CDX65UAC03\r\r\n" "Esn: 3B0C4B98\r\r\n" "+GCAP: +CIS707A, +MS, +ES, +DS, +FCLASS\r\n", "\n0x3B0C4B98\n", "\nCDX65UAC03 1 [Oct 17 2007 13:30:00]\n", "\nModel CDX-650 \n", "\nC-MOTECH Co., Ltd.\n" }, { "cf50da63e6d48beb1d1c3b41d70ef6fa534c3e13", "BUSlink SCWi275u", 0x22b8, 0x3802, "\n144\n", "\n000\n", NULL, "\n\"ADE_05_00_06032300I\"\n", "\n\"GSM900\",\"GSM1800\",\"GSM1900\",\"GSM850\",\"MODEL=I250-000\"\n", "\n\"Motorola CE, Copyright 2000\"\n" }, { "2aff568f2b60f3d6f3f6cac708ed5dce77b12b96", "Motorola ROKR E2", 0x22b8, 0x3802, NULL, NULL, "\n\"626936926396996\"\n", "\n\"R564_G_12.00.47P\"\n", "\n\"E2\"\n", "\n\"Motorola\"\n" }, { "a7136c6067a43f055ca093cee75cb98ce6c9658e", "Sony-Ericsson W580i", 0x0fce, 0xd089, "\nSony Ericsson W580\n", "\nCXC1123481\n", "\n012505051512505\n", "\nR8BE001 080115 1451 CXC1123481_NAM_1_LA\n", "\nAAC-1052042-BV\n", "\nSony Ericsson\n" }, { "b80ee70214bdf9672f2a268ce165ecfd9def5721", "Huawei E226", 0x12d1, 0x1003, "\nManufacturer: huawei\n" "Model: E226\n" "Revision: 11.310.15.00.150\n" "IMEI: 232363662362362\n" "+GCAP: +CGSM,+FCLASS,+DS\n", "\nManufacturer: huawei\n" "Model: E226\n" "Revision: 11.310.15.00.150\n" "IMEI: 232363662362362\n" "+GCAP: +CGSM,+FCLASS,+DS\n", "\n232363662362362\n", "\n11.310.15.00.150\n", "\nE226\n", "\nhuawei\n" }, { "d902e1f234863aa107bfc2d0faefbee5ed6901f1", "LG LX265", 0x1004, 0x6000, "\nManufacturer: +GMI: LG Electronics Inc.\n" "Model: +GMI: LG Electronics Inc.+GMM: Model:LG-LX265\n" "Revision: +GMR: LX265V05, 50571\n" "ESN: +GSN: 0x9235EB52\n" "+GCAP: +CIS707-A, +MS, +ES, +DS, +FCLASS\n", "\nManufacturer: +GMI: LG Electronics Inc.\n" "Model: +GMI: LG Electronics Inc.+GMM: Model:LG-LX265\n" "Revision: +GMR: LX265V05, 50571\n" "ESN: +GSN: 0x9235EB52\n" "+GCAP: +CIS707-A, +MS, +ES, +DS, +FCLASS\n", "\n0x9235EB52\n", "\nLX265V05, 50571\n", "\nModel:LG-LX265\n", "\nLG Electronics Inc.\n" }, { "543c2920e450e20a46368861fdec3a3b97ba8663", "Nokia 2720a BT", 0x0000, 0x0000, "\nNokia\n", "\n012350150101501\n", "\n012350150101501\n", "\nV 08.62\n" "24-07-09\n" "RM-520\n" "(c) Nokia \n", "\nNokia 2720a-2b\n", "\nNokia\n" }, { "6386ffa7a39ced3c9bfd1d693b90975661e54a86", "Gobi 1000", 0x03f0, 0x1f1d, "\nManufacturer: QUALCOMM INCORPORATED\n" "Model: 88\n" "Revision: D1020-SUUAASFA-4352 1 [Apr 14 2008 18:00:00]\n" "IMEI: 239639269236269\n" "+GCAP: +CGSM,+DS\n", "\nManufacturer: QUALCOMM INCORPORATED\n" "Model: 88\n" "Revision: D1020-SUUAASFA-4352 1 [Apr 14 2008 18:00:00]\n" "IMEI: 239639269236269\n" "+GCAP: +CGSM,+DS\n", "\n239639269236269\n", "\nD1020-SUUAASFA-4352 1 [Apr 14 2008 18:00:00]\n", "\n88\n", "\nQUALCOMM INCORPORATED\n" }, { NULL } }; static void test_devid_item (void *f, gpointer d) { DevidItem *item = (DevidItem *) d; char *devid; g_debug ("%s... ", item->desc); devid = mm_create_device_identifier (item->vid, item->pid, NULL, item->ati, item->ati1, item->gsn, item->revision, item->model, item->manf); g_assert (devid); if (strcmp (devid, item->devid)) g_message ("%s", devid); g_assert (!strcmp (devid, item->devid)); g_free (devid); } /*****************************************************************************/ /* Test CMER test responses */ static void test_cmer_response (const gchar *str, MM3gppCmerMode expected_modes, MM3gppCmerInd expected_inds) { gboolean ret; MM3gppCmerMode modes = MM_3GPP_CMER_MODE_NONE; MM3gppCmerInd inds = MM_3GPP_CMER_IND_NONE; GError *error = NULL; ret = mm_3gpp_parse_cmer_test_response (str, NULL, &modes, &inds, &error); g_assert_no_error (error); g_assert (ret); g_assert_cmpuint (modes, ==, expected_modes); g_assert_cmpuint (inds, ==, expected_inds); } static void test_cmer_response_cinterion_pls8 (void) { static const gchar *str = "+CMER: (0-3),(0),(0),(0-1),(0-1)"; static const MM3gppCmerMode expected_modes = ( \ MM_3GPP_CMER_MODE_DISCARD_URCS | \ MM_3GPP_CMER_MODE_DISCARD_URCS_IF_LINK_RESERVED | \ MM_3GPP_CMER_MODE_BUFFER_URCS_IF_LINK_RESERVED | \ MM_3GPP_CMER_MODE_FORWARD_URCS); static const MM3gppCmerInd expected_inds = ( \ MM_3GPP_CMER_IND_DISABLE | \ MM_3GPP_CMER_IND_ENABLE_NOT_CAUSED_BY_CIND); test_cmer_response (str, expected_modes, expected_inds); } static void test_cmer_response_sierra_em7345 (void) { static const gchar *str = "+CMER: 1,0,0,(0-1),0"; static const MM3gppCmerMode expected_modes = ( \ MM_3GPP_CMER_MODE_DISCARD_URCS_IF_LINK_RESERVED); static const MM3gppCmerInd expected_inds = ( \ MM_3GPP_CMER_IND_DISABLE | \ MM_3GPP_CMER_IND_ENABLE_NOT_CAUSED_BY_CIND); test_cmer_response (str, expected_modes, expected_inds); } static void test_cmer_response_cinterion_ehs5 (void) { static const gchar *str = "+CMER: (1,2),0,0,(0-1),0"; static const MM3gppCmerMode expected_modes = ( \ MM_3GPP_CMER_MODE_DISCARD_URCS_IF_LINK_RESERVED | \ MM_3GPP_CMER_MODE_BUFFER_URCS_IF_LINK_RESERVED); static const MM3gppCmerInd expected_inds = ( \ MM_3GPP_CMER_IND_DISABLE | \ MM_3GPP_CMER_IND_ENABLE_NOT_CAUSED_BY_CIND); test_cmer_response (str, expected_modes, expected_inds); } static void test_cmer_request_cinterion_ehs5 (void) { gchar *str; str = mm_3gpp_build_cmer_set_request (MM_3GPP_CMER_MODE_BUFFER_URCS_IF_LINK_RESERVED, MM_3GPP_CMER_IND_ENABLE_NOT_CAUSED_BY_CIND); g_assert_cmpstr (str, ==, "+CMER=2,0,0,1"); g_free (str); } /*****************************************************************************/ /* Test CIND responses */ typedef struct { const char *desc; const gint min; const gint max; } CindEntry; static void test_cind_results (const char *desc, const char *reply, CindEntry *expected_results, guint32 expected_results_len) { guint i; GError *error = NULL; GHashTable *results; g_debug ("Testing %s +CIND response...", desc); results = mm_3gpp_parse_cind_test_response (reply, &error); g_assert (results); g_assert (error == NULL); g_assert (g_hash_table_size (results) == expected_results_len); for (i = 0; i < expected_results_len; i++) { CindEntry *expected = &expected_results[i]; MM3gppCindResponse *compare; compare = g_hash_table_lookup (results, expected->desc); g_assert (compare); g_assert_cmpint (i + 1, ==, mm_3gpp_cind_response_get_index (compare)); g_assert_cmpint (expected->min, ==, mm_3gpp_cind_response_get_min (compare)); g_assert_cmpint (expected->max, ==, mm_3gpp_cind_response_get_max (compare)); } g_hash_table_destroy (results); } static void test_cind_response_linktop_lw273 (void *f, gpointer d) { const char *reply = "+CIND: (\"battchg\",(0-5)),(\"signal\",(0-5)),(\"batterywarning\",(0-1)),(\"chargerconnected\",(0-1)),(\"service\",(0-1)),(\"sounder\",(0-1)),(\"message\",(0-1)),()"; static CindEntry expected[] = { { "battchg", 0, 5 }, { "signal", 0, 5 }, { "batterywarning", 0, 1 }, { "chargerconnected", 0, 1 }, { "service", 0, 1 }, { "sounder", 0, 1 }, { "message", 0, 1 } }; test_cind_results ("LW273", reply, &expected[0], G_N_ELEMENTS (expected)); } static void test_cind_response_moto_v3m (void *f, gpointer d) { const char *reply = "+CIND: (\"Voice Mail\",(0,1)),(\"service\",(0,1)),(\"call\",(0,1)),(\"Roam\",(0-2)),(\"signal\",(0-5)),(\"callsetup\",(0-3)),(\"smsfull\",(0,1))"; static CindEntry expected[] = { { "voicemail", 0, 1 }, { "service", 0, 1 }, { "call", 0, 1 }, { "roam", 0, 2 }, { "signal", 0, 5 }, { "callsetup", 0, 3 }, { "smsfull", 0, 1 } }; test_cind_results ("Motorola V3m", reply, &expected[0], G_N_ELEMENTS (expected)); } /*****************************************************************************/ /* Test +CGEV indication parsing */ typedef struct { const gchar *str; MM3gppCgev expected_type; const gchar *expected_pdp_type; const gchar *expected_pdp_addr; guint expected_cid; guint expected_parent_cid; guint expected_event_type; } CgevIndicationTest; static const CgevIndicationTest cgev_indication_tests[] = { { "+CGEV: REJECT IP, 123.123.123.123", MM_3GPP_CGEV_REJECT, "IP", "123.123.123.123", 0, 0, 0 }, { "REJECT IP, 123.123.123.123", MM_3GPP_CGEV_REJECT, "IP", "123.123.123.123", 0, 0, 0 }, { "+CGEV: NW REACT IP, 123.123.123.123", MM_3GPP_CGEV_NW_REACT, "IP", "123.123.123.123", 0, 0, 0 }, { "NW REACT IP, 123.123.123.123", MM_3GPP_CGEV_NW_REACT, "IP", "123.123.123.123", 0, 0, 0 }, { "+CGEV: NW REACT IP, 123.123.123.123, 1", MM_3GPP_CGEV_NW_REACT, "IP", "123.123.123.123", 1, 0, 0 }, { "NW DEACT IP, 123.123.123.123, 1", MM_3GPP_CGEV_NW_DEACT_PDP, "IP", "123.123.123.123", 1, 0, 0 }, { "+CGEV: NW DEACT IP, 123.123.123.123", MM_3GPP_CGEV_NW_DEACT_PDP, "IP", "123.123.123.123", 0, 0, 0 }, { "NW DEACT IP, 123.123.123.123", MM_3GPP_CGEV_NW_DEACT_PDP, "IP", "123.123.123.123", 0, 0, 0 }, { "+CGEV: NW DEACT IP, 123.123.123.123, 1", MM_3GPP_CGEV_NW_DEACT_PDP, "IP", "123.123.123.123", 1, 0, 0 }, { "NW DEACT IP, 123.123.123.123, 1", MM_3GPP_CGEV_NW_DEACT_PDP, "IP", "123.123.123.123", 1, 0, 0 }, { "ME DEACT IP, 123.123.123.123, 1", MM_3GPP_CGEV_ME_DEACT_PDP, "IP", "123.123.123.123", 1, 0, 0 }, { "+CGEV: ME DEACT IP, 123.123.123.123", MM_3GPP_CGEV_ME_DEACT_PDP, "IP", "123.123.123.123", 0, 0, 0 }, { "ME DEACT IP, 123.123.123.123", MM_3GPP_CGEV_ME_DEACT_PDP, "IP", "123.123.123.123", 0, 0, 0 }, { "+CGEV: ME DEACT IP, 123.123.123.123, 1", MM_3GPP_CGEV_ME_DEACT_PDP, "IP", "123.123.123.123", 1, 0, 0 }, { "ME DEACT IP, 123.123.123.123, 1", MM_3GPP_CGEV_ME_DEACT_PDP, "IP", "123.123.123.123", 1, 0, 0 }, { "ME PDN ACT 2", MM_3GPP_CGEV_ME_ACT_PRIMARY, NULL, NULL, 2, 0, 0 }, { "+CGEV: ME PDN ACT 2", MM_3GPP_CGEV_ME_ACT_PRIMARY, NULL, NULL, 2, 0, 0 }, /* with ,[,]] */ { "ME PDN ACT 2, 3", MM_3GPP_CGEV_ME_ACT_PRIMARY, NULL, NULL, 2, 0, 0 }, { "+CGEV: ME PDN ACT 2, 3", MM_3GPP_CGEV_ME_ACT_PRIMARY, NULL, NULL, 2, 0, 0 }, { "ME PDN ACT 2, 3, 4", MM_3GPP_CGEV_ME_ACT_PRIMARY, NULL, NULL, 2, 0, 0 }, { "+CGEV: ME PDN ACT 2, 3, 4", MM_3GPP_CGEV_ME_ACT_PRIMARY, NULL, NULL, 2, 0, 0 }, { "ME PDN DEACT 2", MM_3GPP_CGEV_ME_DEACT_PRIMARY, NULL, NULL, 2, 0, 0 }, { "+CGEV: ME PDN DEACT 2", MM_3GPP_CGEV_ME_DEACT_PRIMARY, NULL, NULL, 2, 0, 0 }, { "ME ACT 3, 2, 1", MM_3GPP_CGEV_ME_ACT_SECONDARY, NULL, NULL, 2, 3, 1 }, { "+CGEV: ME ACT 3, 2, 1", MM_3GPP_CGEV_ME_ACT_SECONDARY, NULL, NULL, 2, 3, 1 }, { "ME DEACT 3, 2, 1", MM_3GPP_CGEV_ME_DEACT_SECONDARY, NULL, NULL, 2, 3, 1 }, { "+CGEV: ME DEACT 3, 2, 1", MM_3GPP_CGEV_ME_DEACT_SECONDARY, NULL, NULL, 2, 3, 1 }, { "NW PDN ACT 2", MM_3GPP_CGEV_NW_ACT_PRIMARY, NULL, NULL, 2, 0, 0 }, { "+CGEV: NW PDN ACT 2", MM_3GPP_CGEV_NW_ACT_PRIMARY, NULL, NULL, 2, 0, 0 }, { "NW PDN DEACT 2", MM_3GPP_CGEV_NW_DEACT_PRIMARY, NULL, NULL, 2, 0, 0 }, { "+CGEV: NW PDN DEACT 2", MM_3GPP_CGEV_NW_DEACT_PRIMARY, NULL, NULL, 2, 0, 0 }, { "NW ACT 3, 2, 1", MM_3GPP_CGEV_NW_ACT_SECONDARY, NULL, NULL, 2, 3, 1 }, { "+CGEV: NW ACT 3, 2, 1", MM_3GPP_CGEV_NW_ACT_SECONDARY, NULL, NULL, 2, 3, 1 }, { "NW DEACT 3, 2, 1", MM_3GPP_CGEV_NW_DEACT_SECONDARY, NULL, NULL, 2, 3, 1 }, { "+CGEV: NW DEACT 3, 2, 1", MM_3GPP_CGEV_NW_DEACT_SECONDARY, NULL, NULL, 2, 3, 1 }, { "+CGEV: NW DETACH", MM_3GPP_CGEV_NW_DETACH, NULL, NULL, 0, 0, 0 }, { "NW DETACH", MM_3GPP_CGEV_NW_DETACH, NULL, NULL, 0, 0, 0 }, { "+CGEV: ME DETACH", MM_3GPP_CGEV_ME_DETACH, NULL, NULL, 0, 0, 0 }, { "ME DETACH", MM_3GPP_CGEV_ME_DETACH, NULL, NULL, 0, 0, 0 }, { "+CGEV: NW CLASS A", MM_3GPP_CGEV_NW_CLASS, NULL, NULL, 0, 0, 0 }, { "NW CLASS A", MM_3GPP_CGEV_NW_CLASS, NULL, NULL, 0, 0, 0 }, { "+CGEV: ME CLASS A", MM_3GPP_CGEV_ME_CLASS, NULL, NULL, 0, 0, 0 }, { "ME CLASS A", MM_3GPP_CGEV_ME_CLASS, NULL, NULL, 0, 0, 0 }, }; static void test_cgev_indication (const CgevIndicationTest *t) { guint i; GError *error = NULL; gboolean ret; for (i = 0; i < G_N_ELEMENTS (cgev_indication_tests); i++) { const CgevIndicationTest *test = &cgev_indication_tests[i]; MM3gppCgev type; type = mm_3gpp_parse_cgev_indication_action (test->str); g_assert_cmpuint (type, ==, test->expected_type); g_debug ("[%u] type: %u", i, type); switch (type) { case MM_3GPP_CGEV_UNKNOWN: case MM_3GPP_CGEV_NW_DETACH: case MM_3GPP_CGEV_ME_DETACH: break; case MM_3GPP_CGEV_NW_ACT_PRIMARY: case MM_3GPP_CGEV_ME_ACT_PRIMARY: case MM_3GPP_CGEV_NW_DEACT_PRIMARY: case MM_3GPP_CGEV_ME_DEACT_PRIMARY: { guint cid; g_debug ("[%u] parsing as primary", i); ret = mm_3gpp_parse_cgev_indication_primary (test->str, type, &cid, &error); g_assert_no_error (error); g_assert (ret); g_assert_cmpuint (cid, ==, test->expected_cid); break; } case MM_3GPP_CGEV_NW_ACT_SECONDARY: case MM_3GPP_CGEV_ME_ACT_SECONDARY: case MM_3GPP_CGEV_NW_DEACT_SECONDARY: case MM_3GPP_CGEV_ME_DEACT_SECONDARY: { guint p_cid; guint cid; guint event_type; g_debug ("[%u] parsing as secondary", i); ret = mm_3gpp_parse_cgev_indication_secondary (test->str, type, &p_cid, &cid, &event_type, &error); g_assert_no_error (error); g_assert (ret); g_assert_cmpuint (cid, ==, test->expected_cid); g_assert_cmpuint (p_cid, ==, test->expected_parent_cid); g_assert_cmpuint (event_type, ==, test->expected_event_type); break; } case MM_3GPP_CGEV_NW_DEACT_PDP: case MM_3GPP_CGEV_ME_DEACT_PDP: case MM_3GPP_CGEV_REJECT: case MM_3GPP_CGEV_NW_REACT: { gchar *pdp_type; gchar *pdp_addr; guint cid; g_debug ("[%u] parsing as pdp", i); ret = mm_3gpp_parse_cgev_indication_pdp (test->str, type, &pdp_type, &pdp_addr, &cid, &error); g_assert_no_error (error); g_assert (ret); g_assert_cmpstr (pdp_type, ==, test->expected_pdp_type); g_assert_cmpstr (pdp_addr, ==, test->expected_pdp_addr); g_assert_cmpuint (cid, ==, test->expected_cid); g_free (pdp_type); g_free (pdp_addr); break; } case MM_3GPP_CGEV_NW_CLASS: case MM_3GPP_CGEV_ME_CLASS: case MM_3GPP_CGEV_NW_MODIFY: case MM_3GPP_CGEV_ME_MODIFY: /* ignore */ break; default: break; } } } /*****************************************************************************/ /* Test ICCID parsing */ static void test_iccid_parse_quoted_swap_19_digit (void *f, gpointer d) { const char *raw_iccid = "\"984402003576012594F9\""; const char *expected = "8944200053671052499"; char *parsed; GError *error = NULL; parsed = mm_3gpp_parse_iccid (raw_iccid, &error); g_assert_no_error (error); g_assert_cmpstr (parsed, ==, expected); g_free (parsed); } static void test_iccid_parse_unquoted_swap_20_digit (void *f, gpointer d) { const char *raw_iccid = "98231420326409614067"; const char *expected = "89324102234690160476"; char *parsed; GError *error = NULL; parsed = mm_3gpp_parse_iccid (raw_iccid, &error); g_assert_no_error (error); g_assert_cmpstr (parsed, ==, expected); g_free (parsed); } static void test_iccid_parse_unquoted_unswapped_19_digit (void *f, gpointer d) { const char *raw_iccid = "8944200053671052499F"; const char *expected = "8944200053671052499"; char *parsed; GError *error = NULL; parsed = mm_3gpp_parse_iccid (raw_iccid, &error); g_assert_no_error (error); g_assert_cmpstr (parsed, ==, expected); g_free (parsed); } static void test_iccid_parse_unquoted_unswapped_19_digit_no_f (void *f, gpointer d) { const char *raw_iccid = "8944200053671052499"; const char *expected = "8944200053671052499"; char *parsed; GError *error = NULL; parsed = mm_3gpp_parse_iccid (raw_iccid, &error); g_assert_no_error (error); g_assert_cmpstr (parsed, ==, expected); g_free (parsed); } static void test_iccid_parse_quoted_unswapped_20_digit (void *f, gpointer d) { const char *raw_iccid = "\"89324102234690160476\""; const char *expected = "89324102234690160476"; char *parsed; GError *error = NULL; parsed = mm_3gpp_parse_iccid (raw_iccid, &error); g_assert_no_error (error); g_assert_cmpstr (parsed, ==, expected); g_free (parsed); } static void test_iccid_parse_quoted_unswapped_hex_account (void *f, gpointer d) { const char *raw_iccid = "\"898602F9091830030220\""; const char *expected = "898602F9091830030220"; char *parsed; GError *error = NULL; parsed = mm_3gpp_parse_iccid (raw_iccid, &error); g_assert_no_error (error); g_assert_cmpstr (parsed, ==, expected); g_free (parsed); } static void test_iccid_parse_quoted_unswapped_hex_account_2 (void *f, gpointer d) { const char *raw_iccid = "\"898602C0123456789012\""; const char *expected = "898602C0123456789012"; char *parsed; GError *error = NULL; parsed = mm_3gpp_parse_iccid (raw_iccid, &error); g_assert_no_error (error); g_assert_cmpstr (parsed, ==, expected); g_free (parsed); } static void test_iccid_parse_short (void *f, gpointer d) { const char *raw_iccid = "982314203264096"; char *parsed; GError *error = NULL; parsed = mm_3gpp_parse_iccid (raw_iccid, &error); g_assert (parsed == NULL); g_assert_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED); g_error_free (error); } static void test_iccid_parse_invalid_chars (void *f, gpointer d) { const char *raw_iccid = "98231420326pl9614067"; char *parsed; GError *error = NULL; parsed = mm_3gpp_parse_iccid (raw_iccid, &error); g_assert (parsed == NULL); g_assert_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED); g_error_free (error); } static void test_iccid_parse_quoted_invalid_mii (void *f, gpointer d) { const char *raw_iccid = "\"0044200053671052499\""; char *parsed; GError *error = NULL; parsed = mm_3gpp_parse_iccid (raw_iccid, &error); g_assert (parsed == NULL); g_assert_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED); g_error_free (error); } static void test_iccid_parse_unquoted_invalid_mii (void *f, gpointer d) { const char *raw_iccid = "0044200053671052499"; char *parsed; GError *error = NULL; parsed = mm_3gpp_parse_iccid (raw_iccid, &error); g_assert (parsed == NULL); g_assert_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED); g_error_free (error); } /*****************************************************************************/ /* Test APN cmp */ typedef struct { const gchar *existing; const gchar *requested; gboolean match_expected; } TestApnCmp; static const TestApnCmp test_apn_cmp[] = { { "", "", TRUE }, { NULL, "", TRUE }, { "", NULL, TRUE }, { NULL, NULL, TRUE }, { "m2m.com.attz", "m2m.com.attz", TRUE }, { "m2m.com.attz", "M2M.COM.ATTZ", TRUE }, { "M2M.COM.ATTZ", "m2m.com.attz", TRUE }, { "m2m.com.attz.mnc170.mcc310.gprs", "m2m.com.attz", TRUE }, { "ac.vodafone.es.MNC001.MCC214.GPRS", "ac.vodafone.es", TRUE }, { "", "m2m.com.attz", FALSE }, { "m2m.com.attz", "", FALSE }, { "m2m.com.attz", "m2m.com.attz.mnc170.mcc310.gprs", FALSE }, { "ac.vodafone.es", "ac.vodafone.es.MNC001.MCC214.GPRS", FALSE }, { "internet.test", "internet", FALSE }, { "internet.test", "INTERNET", FALSE }, { "internet.test", "internet.tes", FALSE }, }; static void test_cmp_apn_name (void) { guint i; for (i = 0; i < G_N_ELEMENTS (test_apn_cmp); i++) { g_debug ("Comparing requested '%s' vs existing '%s': %s match", test_apn_cmp[i].requested, test_apn_cmp[i].existing, test_apn_cmp[i].match_expected ? "should" : "shouldn't"); g_assert (mm_3gpp_cmp_apn_name (test_apn_cmp[i].requested, test_apn_cmp[i].existing) == test_apn_cmp[i].match_expected); } } /*****************************************************************************/ /* Test CGDCONT test responses */ static void test_cgdcont_test_results (const gchar *desc, const gchar *reply, MM3gppPdpContextFormat *expected_results, guint32 expected_results_len) { GList *l; GError *error = NULL; GList *results; g_debug ("Testing %s +CGDCONT test response...", desc); results = mm_3gpp_parse_cgdcont_test_response (reply, NULL, &error); g_assert (results); g_assert_no_error (error); g_assert_cmpuint (g_list_length (results), ==, expected_results_len); for (l = results; l; l = g_list_next (l)) { MM3gppPdpContextFormat *format = l->data; gboolean found = FALSE; guint i; for (i = 0; !found && i < expected_results_len; i++) { MM3gppPdpContextFormat *expected; expected = &expected_results[i]; if (format->pdp_type == expected->pdp_type) { found = TRUE; g_assert_cmpuint (format->min_cid, ==, expected->min_cid); g_assert_cmpuint (format->max_cid, ==, expected->max_cid); } } g_assert (found == TRUE); } mm_3gpp_pdp_context_format_list_free (results); } static void test_cgdcont_test_response_single (void *f, gpointer d) { const gchar *reply = "+CGDCONT: (1-10),\"IP\",,,(0,1),(0,1)"; static MM3gppPdpContextFormat expected[] = { { 1, 10, MM_BEARER_IP_FAMILY_IPV4 } }; test_cgdcont_test_results ("Single", reply, &expected[0], G_N_ELEMENTS (expected)); } static void test_cgdcont_test_response_multiple (void *f, gpointer d) { const gchar *reply = "+CGDCONT: (1-10),\"IP\",,,(0,1),(0,1)\r\n" "+CGDCONT: (1-10),\"IPV6\",,,(0,1),(0,1)\r\n" "+CGDCONT: (1-10),\"IPV4V6\",,,(0,1),(0,1)\r\n"; static MM3gppPdpContextFormat expected[] = { { 1, 10, MM_BEARER_IP_FAMILY_IPV4 }, { 1, 10, MM_BEARER_IP_FAMILY_IPV6 }, { 1, 10, MM_BEARER_IP_FAMILY_IPV4V6 } }; test_cgdcont_test_results ("Multiple", reply, &expected[0], G_N_ELEMENTS (expected)); } static void test_cgdcont_test_response_multiple_and_ignore (void *f, gpointer d) { const gchar *reply = "+CGDCONT: (1-16),\"IP\",,,(0-2),(0-4)\r\n" "+CGDCONT: (1-16),\"PPP\",,,(0-2),(0-4)\r\n" "+CGDCONT: (1-16),\"IPV6\",,,(0-2),(0-4)\r\n"; static MM3gppPdpContextFormat expected[] = { { 1, 16, MM_BEARER_IP_FAMILY_IPV4 }, /* PPP is ignored */ { 1, 16, MM_BEARER_IP_FAMILY_IPV6 } }; test_cgdcont_test_results ("Multiple and Ignore", reply, &expected[0], G_N_ELEMENTS (expected)); } static void test_cgdcont_test_response_single_context (void *f, gpointer d) { const gchar *reply = "+CGDCONT: (1),\"IP\",,,(0),(0)\r\n" "+CGDCONT: (1),\"IPV6\",,,(0),(0)\r\n"; static MM3gppPdpContextFormat expected[] = { { 1, 1, MM_BEARER_IP_FAMILY_IPV4 }, { 1, 1, MM_BEARER_IP_FAMILY_IPV6 } }; test_cgdcont_test_results ("Single Context", reply, &expected[0], G_N_ELEMENTS (expected)); } static void test_cgdcont_test_response_thuraya (void *f, gpointer d) { const gchar *reply = "+CGDCONT: ( 1 ) , \"IP\" ,,, (0-2),(0-3)\r\n" "+CGDCONT: , \"PPP\" ,,, (0-2),(0-3)\r\n"; static MM3gppPdpContextFormat expected[] = { { 1, 1, MM_BEARER_IP_FAMILY_IPV4 } }; test_cgdcont_test_results ("Thuraya", reply, &expected[0], G_N_ELEMENTS (expected)); } static void test_cgdcont_test_response_cinterion_phs8 (void *f, gpointer d) { const gchar *reply = "+CGDCONT: (1-17,101-116),\"IP\",,,(0),(0-4)\r\n"; static MM3gppPdpContextFormat expected[] = { { 1, 17, MM_BEARER_IP_FAMILY_IPV4 } }; test_cgdcont_test_results ("Cinterion PHS8-USA REVISION 03.001", reply, &expected[0], G_N_ELEMENTS (expected)); } /*****************************************************************************/ /* Test CGDCONT read responses */ static void test_cgdcont_read_results (const gchar *desc, const gchar *reply, MM3gppPdpContext *expected_results, guint32 expected_results_len) { GList *l; GError *error = NULL; GList *results; g_debug ("Testing %s +CGDCONT response...", desc); results = mm_3gpp_parse_cgdcont_read_response (reply, &error); g_assert (results); g_assert_no_error (error); g_assert_cmpuint (g_list_length (results), ==, expected_results_len); for (l = results; l; l = g_list_next (l)) { MM3gppPdpContext *pdp = l->data; gboolean found = FALSE; guint i; for (i = 0; !found && i < expected_results_len; i++) { MM3gppPdpContext *expected; expected = &expected_results[i]; if (pdp->cid == expected->cid) { found = TRUE; g_assert_cmpuint (pdp->pdp_type, ==, expected->pdp_type); g_assert_cmpstr (pdp->apn, ==, expected->apn); } } g_assert (found == TRUE); } mm_3gpp_pdp_context_list_free (results); } static void test_cgdcont_read_response_nokia (void *f, gpointer d) { const gchar *reply = "+CGDCONT: 1,\"IP\",,,0,0"; static MM3gppPdpContext expected[] = { { 1, MM_BEARER_IP_FAMILY_IPV4, NULL } }; test_cgdcont_read_results ("Nokia", reply, &expected[0], G_N_ELEMENTS (expected)); } static void test_cgdcont_read_response_samsung (void *f, gpointer d) { const gchar *reply = "+CGDCONT: 1,\"IP\",\"nate.sktelecom.com\",\"\",0,0\r\n" "+CGDCONT: 2,\"IP\",\"epc.tmobile.com\",\"\",0,0\r\n" "+CGDCONT: 3,\"IP\",\"MAXROAM.com\",\"\",0,0\r\n"; static MM3gppPdpContext expected[] = { { 1, MM_BEARER_IP_FAMILY_IPV4, (gchar *) "nate.sktelecom.com" }, { 2, MM_BEARER_IP_FAMILY_IPV4, (gchar *) "epc.tmobile.com" }, { 3, MM_BEARER_IP_FAMILY_IPV4, (gchar *) "MAXROAM.com" } }; test_cgdcont_read_results ("Samsung", reply, &expected[0], G_N_ELEMENTS (expected)); } static void test_cgdcont_read_response_simcom (void *f, gpointer d) { const gchar *reply = "+CGDCONT: 1,\"IP\",\"nate.sktelecom.com\"\r\n" "+CGDCONT: 2,\"IP\",\"epc.tmobile.com\"\r\n" "+CGDCONT: 3,\"IP\",\"MAXROAM.com\"\r\n"; static MM3gppPdpContext expected[] = { { 1, MM_BEARER_IP_FAMILY_IPV4, (gchar *) "nate.sktelecom.com" }, { 2, MM_BEARER_IP_FAMILY_IPV4, (gchar *) "epc.tmobile.com" }, { 3, MM_BEARER_IP_FAMILY_IPV4, (gchar *) "MAXROAM.com" } }; test_cgdcont_read_results ("Simcom", reply, &expected[0], G_N_ELEMENTS (expected)); } /*****************************************************************************/ /* Test CGDCONT read responses */ static void test_cgact_read_results (const gchar *desc, const gchar *reply, MM3gppPdpContextActive *expected_results, guint32 expected_results_len) { GList *l; GError *error = NULL; GList *results; g_debug ("Testing %s +CGACT response...", desc); results = mm_3gpp_parse_cgact_read_response (reply, &error); g_assert_no_error (error); if (expected_results_len) { g_assert (results); g_assert_cmpuint (g_list_length (results), ==, expected_results_len); } for (l = results; l; l = g_list_next (l)) { MM3gppPdpContextActive *pdp = l->data; gboolean found = FALSE; guint i; for (i = 0; !found && i < expected_results_len; i++) { MM3gppPdpContextActive *expected; expected = &expected_results[i]; if (pdp->cid == expected->cid) { found = TRUE; g_assert_cmpuint (pdp->active, ==, expected->active); } } g_assert (found == TRUE); } mm_3gpp_pdp_context_active_list_free (results); } static void test_cgact_read_response_none (void) { test_cgact_read_results ("none", "", NULL, 0); } static void test_cgact_read_response_single_inactive (void) { const gchar *reply = "+CGACT: 1,0\r\n"; static MM3gppPdpContextActive expected[] = { { 1, FALSE }, }; test_cgact_read_results ("single inactive", reply, &expected[0], G_N_ELEMENTS (expected)); } static void test_cgact_read_response_single_active (void) { const gchar *reply = "+CGACT: 1,1\r\n"; static MM3gppPdpContextActive expected[] = { { 1, TRUE }, }; test_cgact_read_results ("single active", reply, &expected[0], G_N_ELEMENTS (expected)); } static void test_cgact_read_response_multiple (void) { const gchar *reply = "+CGACT: 1,0\r\n" "+CGACT: 4,1\r\n" "+CGACT: 5,0\r\n"; static MM3gppPdpContextActive expected[] = { { 1, FALSE }, { 4, TRUE }, { 5, FALSE }, }; test_cgact_read_results ("multiple", reply, &expected[0], G_N_ELEMENTS (expected)); } /*****************************************************************************/ /* CID selection logic */ typedef struct { const gchar *apn; MMBearerIpFamily ip_family; const gchar *cgdcont_test; const gchar *cgdcont_query; gint expected_profile_id; gboolean expected_profile_id_reused; gboolean expected_profile_id_overwritten; } ProfileSelectionTest; static const ProfileSelectionTest profile_selection_tests[] = { /* Test: exact APN match */ { .apn = "ac.vodafone.es", .ip_family = MM_BEARER_IP_FAMILY_IPV4, .cgdcont_test = "+CGDCONT: (1-10),\"IP\",,,(0,1),(0,1)\r\n" "+CGDCONT: (1-10),\"IPV6\",,,(0,1),(0,1)\r\n" "+CGDCONT: (1-10),\"IPV4V6\",,,(0,1),(0,1)\r\n", .cgdcont_query = "+CGDCONT: 1,\"IP\",\"telefonica.es\",\"\",0,0\r\n" "+CGDCONT: 2,\"IP\",\"ac.vodafone.es\",\"\",0,0\r\n" "+CGDCONT: 3,\"IP\",\"inet.es\",\"\",0,0\r\n", .expected_profile_id = 2, .expected_profile_id_reused = TRUE, .expected_profile_id_overwritten = FALSE }, /* Test: exact APN match reported as activated */ { .apn = "ac.vodafone.es", .ip_family = MM_BEARER_IP_FAMILY_IPV4, .cgdcont_test = "+CGDCONT: (1-10),\"IP\",,,(0,1),(0,1)\r\n" "+CGDCONT: (1-10),\"IPV6\",,,(0,1),(0,1)\r\n" "+CGDCONT: (1-10),\"IPV4V6\",,,(0,1),(0,1)\r\n", .cgdcont_query = "+CGDCONT: 1,\"IP\",\"telefonica.es\",\"\",0,0\r\n" "+CGDCONT: 2,\"IP\",\"ac.vodafone.es.MNC001.MCC214.GPRS\",\"\",0,0\r\n" "+CGDCONT: 3,\"IP\",\"inet.es\",\"\",0,0\r\n", .expected_profile_id = 2, .expected_profile_id_reused = TRUE, .expected_profile_id_overwritten = FALSE }, /* Test: first empty slot in between defined contexts */ { .apn = "ac.vodafone.es", .ip_family = MM_BEARER_IP_FAMILY_IPV4, .cgdcont_test = "+CGDCONT: (1-10),\"IP\",,,(0,1),(0,1)\r\n" "+CGDCONT: (1-10),\"IPV6\",,,(0,1),(0,1)\r\n" "+CGDCONT: (1-10),\"IPV4V6\",,,(0,1),(0,1)\r\n", .cgdcont_query = "+CGDCONT: 1,\"IP\",\"telefonica.es\",\"\",0,0\r\n" "+CGDCONT: 10,\"IP\",\"inet.es\",\"\",0,0\r\n", .expected_profile_id = 2, .expected_profile_id_reused = FALSE, .expected_profile_id_overwritten = FALSE }, /* Test: first empty slot in between defined contexts, different PDP types */ { .apn = "ac.vodafone.es", .ip_family = MM_BEARER_IP_FAMILY_IPV4, .cgdcont_test = "+CGDCONT: (1-10),\"IP\",,,(0,1),(0,1)\r\n" "+CGDCONT: (1-10),\"IPV6\",,,(0,1),(0,1)\r\n" "+CGDCONT: (1-10),\"IPV4V6\",,,(0,1),(0,1)\r\n", .cgdcont_query = "+CGDCONT: 1,\"IPV6\",\"telefonica.es\",\"\",0,0\r\n" "+CGDCONT: 10,\"IP\",\"inet.es\",\"\",0,0\r\n", .expected_profile_id = 2, .expected_profile_id_reused = FALSE, .expected_profile_id_overwritten = FALSE }, /* Test: first empty slot after last context found */ { .apn = "ac.vodafone.es", .ip_family = MM_BEARER_IP_FAMILY_IPV4, .cgdcont_test = "+CGDCONT: (1-10),\"IP\",,,(0,1),(0,1)\r\n" "+CGDCONT: (1-10),\"IPV6\",,,(0,1),(0,1)\r\n" "+CGDCONT: (1-10),\"IPV4V6\",,,(0,1),(0,1)\r\n", .cgdcont_query = "+CGDCONT: 1,\"IP\",\"telefonica.es\",\"\",0,0\r\n" "+CGDCONT: 2,\"IP\",\"inet.es\",\"\",0,0\r\n", .expected_profile_id = 3, .expected_profile_id_reused = FALSE, .expected_profile_id_overwritten = FALSE }, /* Test: first empty slot after last context found, different PDP types */ { .apn = "ac.vodafone.es", .ip_family = MM_BEARER_IP_FAMILY_IPV4, .cgdcont_test = "+CGDCONT: (1-10),\"IP\",,,(0,1),(0,1)\r\n" "+CGDCONT: (1-10),\"IPV6\",,,(0,1),(0,1)\r\n" "+CGDCONT: (1-10),\"IPV4V6\",,,(0,1),(0,1)\r\n", .cgdcont_query = "+CGDCONT: 1,\"IP\",\"telefonica.es\",\"\",0,0\r\n" "+CGDCONT: 2,\"IPV6\",\"inet.es\",\"\",0,0\r\n", .expected_profile_id = 3, .expected_profile_id_reused = FALSE, .expected_profile_id_overwritten = FALSE }, /* Test: no empty slot, rewrite context with empty APN */ { .apn = "ac.vodafone.es", .ip_family = MM_BEARER_IP_FAMILY_IPV4, .cgdcont_test = "+CGDCONT: (1-3),\"IP\",,,(0,1),(0,1)\r\n" "+CGDCONT: (1-3),\"IPV6\",,,(0,1),(0,1)\r\n" "+CGDCONT: (1-3),\"IPV4V6\",,,(0,1),(0,1)\r\n", .cgdcont_query = "+CGDCONT: 1,\"IP\",\"telefonica.es\",\"\",0,0\r\n" "+CGDCONT: 2,\"IP\",\"\",\"\",0,0\r\n" "+CGDCONT: 3,\"IP\",\"inet.es\",\"\",0,0\r\n", .expected_profile_id = 2, .expected_profile_id_reused = FALSE, .expected_profile_id_overwritten = TRUE }, /* Test: no empty slot, rewrite last context found */ { .apn = "ac.vodafone.es", .ip_family = MM_BEARER_IP_FAMILY_IPV4, .cgdcont_test = "+CGDCONT: (1-3),\"IP\",,,(0,1),(0,1)\r\n" "+CGDCONT: (1-3),\"IPV6\",,,(0,1),(0,1)\r\n" "+CGDCONT: (1-3),\"IPV4V6\",,,(0,1),(0,1)\r\n", .cgdcont_query = "+CGDCONT: 1,\"IP\",\"telefonica.es\",\"\",0,0\r\n" "+CGDCONT: 2,\"IP\",\"vzwinternet\",\"\",0,0\r\n" "+CGDCONT: 3,\"IP\",\"inet.es\",\"\",0,0\r\n", .expected_profile_id = 3, .expected_profile_id_reused = FALSE, .expected_profile_id_overwritten = TRUE }, /* Test: CGDCONT? and CGDCONT=? failures, fallback to CID=1 (a.g. some Android phones) */ { .apn = "ac.vodafone.es", .ip_family = MM_BEARER_IP_FAMILY_IPV4, .cgdcont_test = NULL, .cgdcont_query = NULL, .expected_profile_id = 1, .expected_profile_id_reused = FALSE, .expected_profile_id_overwritten = TRUE }, }; static void test_profile_selection (void) { guint i; for (i = 0; i < G_N_ELEMENTS (profile_selection_tests); i++) { const ProfileSelectionTest *test; GList *context_format_list; GList *context_list; GList *profile_list; gint profile_id; guint min_allowed_cid = 1; guint max_allowed_cid = G_MAXINT - 1; g_autoptr(MM3gppProfile) requested = NULL; g_autoptr(MM3gppProfile) reused = NULL; gboolean profile_id_overwritten; test = &profile_selection_tests[i]; requested = mm_3gpp_profile_new (); mm_3gpp_profile_set_apn (requested, test->apn); mm_3gpp_profile_set_ip_type (requested, test->ip_family); context_format_list = test->cgdcont_test ? mm_3gpp_parse_cgdcont_test_response (test->cgdcont_test, NULL, NULL) : NULL; mm_3gpp_pdp_context_format_list_find_range (context_format_list, test->ip_family, &min_allowed_cid, &max_allowed_cid); context_list = test->cgdcont_query ? mm_3gpp_parse_cgdcont_read_response (test->cgdcont_query, NULL) : NULL; profile_list = mm_3gpp_profile_list_new_from_pdp_context_list (context_list); profile_id = mm_3gpp_profile_list_find_best (profile_list, requested, (GEqualFunc)mm_3gpp_cmp_apn_name, (MM_3GPP_PROFILE_CMP_FLAGS_NO_PROFILE_ID | MM_3GPP_PROFILE_CMP_FLAGS_NO_AUTH | MM_3GPP_PROFILE_CMP_FLAGS_NO_APN_TYPE), min_allowed_cid, max_allowed_cid, NULL, /* log_object */ &reused, &profile_id_overwritten); g_assert_cmpuint (profile_id, ==, test->expected_profile_id); g_assert_cmpuint (!!reused, ==, test->expected_profile_id_reused); g_assert_cmpuint (profile_id_overwritten, ==, test->expected_profile_id_overwritten); mm_3gpp_profile_list_free (profile_list); mm_3gpp_pdp_context_format_list_free (context_format_list); mm_3gpp_pdp_context_list_free (context_list); } } /*****************************************************************************/ /* Test CPMS responses */ static gboolean is_storage_supported (GArray *supported, MMSmsStorage storage) { guint i; for (i = 0; i < supported->len; i++) { if (storage == g_array_index (supported, MMSmsStorage, i)) return TRUE; } return FALSE; } static void test_cpms_response_cinterion (void *f, gpointer d) { /* Use different sets for each on purpose, even if weird */ const gchar *reply = "+CPMS: (\"ME\",\"MT\"),(\"ME\",\"SM\",\"MT\"),(\"SM\",\"MT\")"; GArray *mem1 = NULL; GArray *mem2 = NULL; GArray *mem3 = NULL; GError *error = NULL; g_debug ("Testing Cinterion +CPMS=? response..."); g_assert (mm_3gpp_parse_cpms_test_response (reply, &mem1, &mem2, &mem3, &error)); g_assert_no_error (error); g_assert_cmpuint (mem1->len, ==, 2); g_assert (is_storage_supported (mem1, MM_SMS_STORAGE_ME)); g_assert (is_storage_supported (mem1, MM_SMS_STORAGE_MT)); g_assert_cmpuint (mem2->len, ==, 3); g_assert (is_storage_supported (mem2, MM_SMS_STORAGE_ME)); g_assert (is_storage_supported (mem2, MM_SMS_STORAGE_SM)); g_assert (is_storage_supported (mem2, MM_SMS_STORAGE_MT)); g_assert_cmpuint (mem3->len, ==, 2); g_assert (is_storage_supported (mem3, MM_SMS_STORAGE_SM)); g_assert (is_storage_supported (mem3, MM_SMS_STORAGE_MT)); g_array_unref (mem1); g_array_unref (mem2); g_array_unref (mem3); } static void test_cpms_response_huawei_mu609 (void *f, gpointer d) { /* Use different sets for each on purpose, even if weird */ const gchar *reply = "+CPMS: \"ME\",\"MT\",\"SM\""; GArray *mem1 = NULL; GArray *mem2 = NULL; GArray *mem3 = NULL; GError *error = NULL; g_debug ("Testing Huawei MU609 +CPMS=? response..."); g_assert (mm_3gpp_parse_cpms_test_response (reply, &mem1, &mem2, &mem3, &error)); g_assert_no_error (error); g_assert_cmpuint (mem1->len, ==, 1); g_assert (is_storage_supported (mem1, MM_SMS_STORAGE_ME)); g_assert_cmpuint (mem2->len, ==, 1); g_assert (is_storage_supported (mem2, MM_SMS_STORAGE_MT)); g_assert_cmpuint (mem3->len, ==, 1); g_assert (is_storage_supported (mem3, MM_SMS_STORAGE_SM)); g_array_unref (mem1); g_array_unref (mem2); g_array_unref (mem3); } static void test_cpms_response_nokia_c6 (void *f, gpointer d) { /* Use different sets for each on purpose, even if weird */ const gchar *reply = "+CPMS: (),(),()"; GArray *mem1 = NULL; GArray *mem2 = NULL; GArray *mem3 = NULL; GError *error = NULL; g_debug ("Testing Nokia C6 response..."); g_assert (mm_3gpp_parse_cpms_test_response (reply, &mem1, &mem2, &mem3, &error)); g_assert_no_error (error); g_assert_cmpuint (mem1->len, ==, 0); g_assert_cmpuint (mem2->len, ==, 0); g_assert_cmpuint (mem3->len, ==, 0); g_array_unref (mem1); g_array_unref (mem2); g_array_unref (mem3); } static void test_cpms_response_mixed (void *f, gpointer d) { /* * First: ("ME","MT") 2-item group * Second: "ME" 1 item * Third: ("SM") 1-item group */ const gchar *reply = "+CPMS: (\"ME\",\"MT\"),\"ME\",(\"SM\")"; GArray *mem1 = NULL; GArray *mem2 = NULL; GArray *mem3 = NULL; GError *error = NULL; g_debug ("Testing mixed +CPMS=? response..."); g_assert (mm_3gpp_parse_cpms_test_response (reply, &mem1, &mem2, &mem3, &error)); g_assert_no_error (error); g_assert_cmpuint (mem1->len, ==, 2); g_assert (is_storage_supported (mem1, MM_SMS_STORAGE_ME)); g_assert (is_storage_supported (mem1, MM_SMS_STORAGE_MT)); g_assert_cmpuint (mem2->len, ==, 1); g_assert (is_storage_supported (mem2, MM_SMS_STORAGE_ME)); g_assert_cmpuint (mem3->len, ==, 1); g_assert (is_storage_supported (mem3, MM_SMS_STORAGE_SM)); g_array_unref (mem1); g_array_unref (mem2); g_array_unref (mem3); } static void test_cpms_response_mixed_spaces (void *f, gpointer d) { /* Test with whitespaces here and there */ const gchar *reply = "+CPMS: ( \"ME\" , \"MT\" ) , \"ME\" , ( \"SM\" )"; GArray *mem1 = NULL; GArray *mem2 = NULL; GArray *mem3 = NULL; GError *error = NULL; g_debug ("Testing mixed +CPMS=? response with spaces..."); g_assert (mm_3gpp_parse_cpms_test_response (reply, &mem1, &mem2, &mem3, &error)); g_assert_no_error (error); g_assert_cmpuint (mem1->len, ==, 2); g_assert (is_storage_supported (mem1, MM_SMS_STORAGE_ME)); g_assert (is_storage_supported (mem1, MM_SMS_STORAGE_MT)); g_assert_cmpuint (mem2->len, ==, 1); g_assert (is_storage_supported (mem2, MM_SMS_STORAGE_ME)); g_assert_cmpuint (mem3->len, ==, 1); g_assert (is_storage_supported (mem3, MM_SMS_STORAGE_SM)); g_array_unref (mem1); g_array_unref (mem2); g_array_unref (mem3); } static void test_cpms_response_empty_fields (void *f, gpointer d) { /* * First: () Empty group * Second: Empty item * Third: ( ) Empty group with spaces */ const gchar *reply = "+CPMS: (),,( )"; GArray *mem1 = NULL; GArray *mem2 = NULL; GArray *mem3 = NULL; GError *error = NULL; g_debug ("Testing mixed +CPMS=? response..."); g_assert (mm_3gpp_parse_cpms_test_response (reply, &mem1, &mem2, &mem3, &error)); g_assert_no_error (error); g_assert_cmpuint (mem1->len, ==, 0); g_assert_cmpuint (mem2->len, ==, 0); g_assert_cmpuint (mem3->len, ==, 0); g_array_unref (mem1); g_array_unref (mem2); g_array_unref (mem3); } typedef struct { const gchar *query; MMSmsStorage mem1_want; MMSmsStorage mem2_want; } CpmsQueryTest; CpmsQueryTest cpms_query_test[] = { {"+CPMS: \"ME\",1,100,\"MT\",5,100,\"TA\",1,100", 2, 3}, {"+CPMS: \"SM\",100,100,\"SR\",5,10,\"TA\",1,100", 1, 4}, {"+CPMS: \"XX\",100,100,\"BM\",5,10,\"TA\",1,100", 0, 5}, {"+CPMS: \"XX\",100,100,\"YY\",5,10,\"TA\",1,100", 0, 0}, {NULL, 0, 0} }; static void test_cpms_query_response (void *f, gpointer d) { MMSmsStorage mem1; MMSmsStorage mem2; gboolean ret; GError *error = NULL; int i; for (i = 0; cpms_query_test[i].query != NULL; i++){ ret = mm_3gpp_parse_cpms_query_response (cpms_query_test[i].query, &mem1, &mem2, &error); g_assert (ret); g_assert_no_error (error); g_assert_cmpuint (cpms_query_test[i].mem1_want, ==, mem1); g_assert_cmpuint (cpms_query_test[i].mem2_want, ==, mem2); } } /*****************************************************************************/ /* Test CNUM responses */ static void test_cnum_results (const gchar *desc, const gchar *reply, const GStrv expected) { GStrv results; guint i; g_debug ("Testing +CNUM response (%s)...", desc); results = mm_3gpp_parse_cnum_exec_response (reply); g_assert (results); g_assert_cmpuint (g_strv_length (results), ==, g_strv_length (expected)); for (i = 0; results[i]; i++) { guint j; for (j = 0; expected[j]; j++) { if (g_str_equal (results[i], expected[j])) break; } /* Ensure the result is found in the expected list */ g_assert (expected[j]); } g_strfreev (results); } static void test_cnum_response_generic (void *f, gpointer d) { const gchar *reply = "+CNUM: \"something\",\"1234567890\",161"; const gchar *expected[] = { "1234567890", NULL }; test_cnum_results ("Generic", reply, (GStrv)expected); } static void test_cnum_response_generic_without_detail (void *f, gpointer d) { const gchar *reply = "+CNUM: ,\"1234567890\",161"; const gchar *expected[] = { "1234567890", NULL }; test_cnum_results ("Generic, without detail", reply, (GStrv)expected); } static void test_cnum_response_generic_detail_unquoted (void *f, gpointer d) { const gchar *reply = "+CNUM: something,\"1234567890\",161"; const gchar *expected[] = { "1234567890", NULL }; test_cnum_results ("Generic, detail unquoted", reply, (GStrv)expected); } static void test_cnum_response_generic_international_number (void *f, gpointer d) { const gchar *reply = "+CNUM: something,\"+34600000001\",145"; const gchar *expected[] = { "+34600000001", NULL }; test_cnum_results ("Generic, international number", reply, (GStrv)expected); } static void test_cnum_response_generic_multiple_numbers (void *f, gpointer d) { const gchar *reply = "+CNUM: something,\"+34600000001\",145\r\n" "+CNUM: ,\"+34600000002\",145\r\n" "+CNUM: \"another\",\"1234567890\",161"; const gchar *expected[] = { "+34600000001", "+34600000002", "1234567890", NULL }; test_cnum_results ("Generic, multiple numbers", reply, (GStrv)expected); } /*****************************************************************************/ /* Test operator ID parsing */ static void common_parse_operator_id (const gchar *operator_id, gboolean expected_success, guint16 expected_mcc, guint16 expected_mnc, gboolean expected_three_digit_mnc) { guint16 mcc; guint16 mnc; gboolean three_digit_mnc; gboolean result; GError *error = NULL; if (expected_mcc) { g_debug ("Parsing Operator ID '%s' " "(%" G_GUINT16_FORMAT ", %" G_GUINT16_FORMAT ", %s)...", operator_id, expected_mcc, expected_mnc, expected_three_digit_mnc ? "TRUE" : "FALSE"); result = mm_3gpp_parse_operator_id (operator_id, &mcc, &mnc, &three_digit_mnc, &error); } else { g_debug ("Validating Operator ID '%s'...", operator_id); result = mm_3gpp_parse_operator_id (operator_id, NULL, NULL, NULL, &error); } if (error) g_debug ("Got %s error: %s...", expected_success ? "unexpected" : "expected", error->message); g_assert (result == expected_success); if (expected_success) { g_assert_no_error (error); if (expected_mcc) { g_assert_cmpuint (expected_mcc, ==, mcc); g_assert_cmpuint (expected_mnc, ==, mnc); g_assert_cmpint (expected_three_digit_mnc, ==, three_digit_mnc); } } else { g_assert (error != NULL); g_error_free (error); } } static void test_parse_operator_id (void *f, gpointer d) { /* Valid MCC+MNC(2) */ common_parse_operator_id ("41201", TRUE, 412, 1, FALSE); common_parse_operator_id ("41201", TRUE, 0, 0, FALSE); /* Valid MCC+MNC(3) */ common_parse_operator_id ("342600", TRUE, 342, 600, TRUE); common_parse_operator_id ("342600", TRUE, 0, 0, FALSE); /* Valid MCC+MNC(2, == 0) */ common_parse_operator_id ("72400", TRUE, 724, 0, FALSE); common_parse_operator_id ("72400", TRUE, 0, 0, FALSE); /* Valid MCC+MNC(3, == 0) */ common_parse_operator_id ("724000", TRUE, 724, 0, TRUE); common_parse_operator_id ("724000", TRUE, 0, 0, FALSE); /* Invalid MCC=0 */ common_parse_operator_id ("000600", FALSE, 0, 0, FALSE); /* Invalid, non-digits */ common_parse_operator_id ("000Z00", FALSE, 0, 0, FALSE); /* Invalid, short */ common_parse_operator_id ("123", FALSE, 0, 0, FALSE); /* Invalid, long */ common_parse_operator_id ("1234567", FALSE, 0, 0, FALSE); } /*****************************************************************************/ /* Test +CDS unsolicited message parsing */ static void common_parse_cds (const gchar *str, guint expected_pdu_len, const gchar *expected_pdu) { g_autoptr(GMatchInfo) match_info = NULL; g_autoptr(GRegex) regex = NULL; g_autofree gchar *pdu_len_str = NULL; g_autofree gchar *pdu = NULL; regex = mm_3gpp_cds_regex_get (); g_regex_match (regex, str, 0, &match_info); g_assert (g_match_info_matches (match_info)); pdu_len_str = g_match_info_fetch (match_info, 1); g_assert (pdu_len_str != NULL); g_assert_cmpuint ((guint) atoi (pdu_len_str), == , expected_pdu_len); pdu = g_match_info_fetch (match_info, 2); g_assert (pdu != NULL); g_assert_cmpstr (pdu, ==, expected_pdu); } static void test_parse_cds (void *f, gpointer d) { /* +CDS: 2407914356060013F1065A098136395339F6219011700463802190117004638030 */ common_parse_cds ("\r\n+CDS: 24\r\n07914356060013F1065A098136395339F6219011700463802190117004638030\r\n", 24, "07914356060013F1065A098136395339F6219011700463802190117004638030"); } typedef struct { const char *gsn; const char *expected_imei; const char *expected_esn; const char *expected_meid; gboolean expect_success; } TestGsnItem; static void test_cdma_parse_gsn (void *f, gpointer d) { static const TestGsnItem items[] = { { "0x6744775\r\n", /* leading zeros skipped, no hex digits */ NULL, "06744775", NULL, TRUE }, { "0x2214A600\r\n", NULL, "2214A600", NULL, TRUE }, { "0x80C98A1\r\n", /* leading zeros skipped, some hex digits */ NULL, "080C98A1", NULL, TRUE }, { "6030C012\r\n", /* no leading 0x */ NULL, "6030C012", NULL, TRUE }, { "45317471585658170:2161753034\r\n0x00A1000013FB653A:0x80D9BBCA\r\n", NULL, "80D9BBCA", "A1000013FB653A", TRUE }, { "354237065082227\r\n", /* GSM IMEI */ "354237065082227", NULL, NULL, TRUE }, { "356936001568843,NL2A62Z0N5\r\n", /* IMEI + serial number */ "356936001568843", NULL, NULL, TRUE }, { "adsfasdfasdfasdf", NULL, NULL, FALSE }, { "0x6030Cfgh", NULL, NULL, FALSE }, { NULL } }; const TestGsnItem *iter; for (iter = &items[0]; iter && iter->gsn; iter++) { char *imei = NULL, *esn = NULL, *meid = NULL; gboolean success; success = mm_parse_gsn (iter->gsn, &imei, &meid, &esn); g_assert_cmpint (success, ==, iter->expect_success); g_assert_cmpstr (iter->expected_imei, ==, imei); g_assert_cmpstr (iter->expected_meid, ==, meid); g_assert_cmpstr (iter->expected_esn, ==, esn); g_free (imei); g_free (meid); g_free (esn); } } /*****************************************************************************/ static gboolean find_mode_combination (GArray *modes, MMModemMode allowed, MMModemMode preferred) { guint i; for (i = 0; i < modes->len; i++) { MMModemModeCombination *mode; mode = &g_array_index (modes, MMModemModeCombination, i); if (mode->allowed == allowed && mode->preferred == preferred) return TRUE; } return FALSE; } static GArray * build_mode_all (MMModemMode all_mask) { MMModemModeCombination all_item; GArray *all; all_item.allowed = all_mask; all_item.preferred = MM_MODEM_MODE_NONE; all = g_array_sized_new (FALSE, FALSE, sizeof (MMModemModeCombination), 1); g_array_append_val (all, all_item); return all; } static void test_supported_mode_filter (void *f, gpointer d) { MMModemModeCombination mode; GArray *all; GArray *combinations; GArray *filtered; /* Build array of combinations */ combinations = g_array_sized_new (FALSE, FALSE, sizeof (MMModemModeCombination), 5); /* 2G only */ mode.allowed = MM_MODEM_MODE_2G; mode.preferred = MM_MODEM_MODE_NONE; g_array_append_val (combinations, mode); /* 3G only */ mode.allowed = MM_MODEM_MODE_3G; mode.preferred = MM_MODEM_MODE_NONE; g_array_append_val (combinations, mode); /* 2G and 3G */ mode.allowed = (MM_MODEM_MODE_2G | MM_MODEM_MODE_3G); mode.preferred = MM_MODEM_MODE_NONE; g_array_append_val (combinations, mode); /* 4G only */ mode.allowed = MM_MODEM_MODE_4G; mode.preferred = MM_MODEM_MODE_NONE; g_array_append_val (combinations, mode); /* 3G and 4G */ mode.allowed = (MM_MODEM_MODE_3G | MM_MODEM_MODE_4G); mode.preferred = MM_MODEM_MODE_NONE; g_array_append_val (combinations, mode); /* 2G, 3G and 4G */ mode.allowed = (MM_MODEM_MODE_2G | MM_MODEM_MODE_3G | MM_MODEM_MODE_4G); mode.preferred = MM_MODEM_MODE_NONE; g_array_append_val (combinations, mode); /* Only 2G supported */ all = build_mode_all (MM_MODEM_MODE_2G); filtered = mm_filter_supported_modes (all, combinations, NULL); g_assert_cmpuint (filtered->len, ==, 1); g_assert (find_mode_combination (filtered, MM_MODEM_MODE_2G, MM_MODEM_MODE_NONE)); g_array_unref (filtered); g_array_unref (all); /* Only 3G supported */ all = build_mode_all (MM_MODEM_MODE_3G); filtered = mm_filter_supported_modes (all, combinations, NULL); g_assert_cmpuint (filtered->len, ==, 1); g_assert (find_mode_combination (filtered, MM_MODEM_MODE_3G, MM_MODEM_MODE_NONE)); g_array_unref (filtered); g_array_unref (all); /* 2G and 3G supported */ all = build_mode_all (MM_MODEM_MODE_2G | MM_MODEM_MODE_3G); filtered = mm_filter_supported_modes (all, combinations, NULL); g_assert_cmpuint (filtered->len, ==, 3); g_assert (find_mode_combination (filtered, MM_MODEM_MODE_2G, MM_MODEM_MODE_NONE)); g_assert (find_mode_combination (filtered, MM_MODEM_MODE_3G, MM_MODEM_MODE_NONE)); g_assert (find_mode_combination (filtered, (MM_MODEM_MODE_2G | MM_MODEM_MODE_3G), MM_MODEM_MODE_NONE)); g_array_unref (filtered); g_array_unref (all); /* 3G and 4G supported */ all = build_mode_all (MM_MODEM_MODE_3G | MM_MODEM_MODE_4G); filtered = mm_filter_supported_modes (all, combinations, NULL); g_assert_cmpuint (filtered->len, ==, 3); g_assert (find_mode_combination (filtered, MM_MODEM_MODE_3G, MM_MODEM_MODE_NONE)); g_assert (find_mode_combination (filtered, MM_MODEM_MODE_4G, MM_MODEM_MODE_NONE)); g_assert (find_mode_combination (filtered, (MM_MODEM_MODE_3G | MM_MODEM_MODE_4G), MM_MODEM_MODE_NONE)); g_array_unref (filtered); g_array_unref (all); /* 2G, 3G and 4G supported */ all = build_mode_all (MM_MODEM_MODE_2G | MM_MODEM_MODE_3G | MM_MODEM_MODE_4G); filtered = mm_filter_supported_modes (all, combinations, NULL); g_assert_cmpuint (filtered->len, ==, 6); g_assert (find_mode_combination (filtered, MM_MODEM_MODE_2G, MM_MODEM_MODE_NONE)); g_assert (find_mode_combination (filtered, MM_MODEM_MODE_3G, MM_MODEM_MODE_NONE)); g_assert (find_mode_combination (filtered, (MM_MODEM_MODE_2G | MM_MODEM_MODE_3G), MM_MODEM_MODE_NONE)); g_assert (find_mode_combination (filtered, MM_MODEM_MODE_4G, MM_MODEM_MODE_NONE)); g_assert (find_mode_combination (filtered, (MM_MODEM_MODE_3G | MM_MODEM_MODE_4G), MM_MODEM_MODE_NONE)); g_assert (find_mode_combination (filtered, (MM_MODEM_MODE_2G | MM_MODEM_MODE_3G | MM_MODEM_MODE_4G), MM_MODEM_MODE_NONE)); g_array_unref (filtered); g_array_unref (all); g_array_unref (combinations); } /*****************************************************************************/ /* Test +CCLK responses */ typedef struct { const gchar *str; gboolean ret; gboolean test_iso8601; gboolean test_tz; const gchar *iso8601; gint32 offset; } CclkTest; static const CclkTest cclk_tests[] = { { "+CCLK: \"14/08/05,04:00:21\"", TRUE, TRUE, FALSE, "2014-08-05T04:00:21Z", 0 }, { "+CCLK: \"14/08/05,04:00:21\"", TRUE, FALSE, TRUE, "2014-08-05T04:00:21+00:00", 0 }, { "+CCLK: \"14/08/05,04:00:21\"", TRUE, TRUE, TRUE, "2014-08-05T04:00:21Z", 0 }, { "+CCLK: \"14/08/05,04:00:21+40\"", TRUE, TRUE, FALSE, "2014-08-05T04:00:21+10", 600 }, { "+CCLK: \"14/08/05,04:00:21+40\"", TRUE, FALSE, TRUE, "2014-08-05T04:00:21+10:00", 600 }, { "+CCLK: \"14/08/05,04:00:21+40\"", TRUE, TRUE, TRUE, "2014-08-05T04:00:21+10", 600 }, { "+CCLK: \"15/02/28,20:30:40-32\"", TRUE, TRUE, FALSE, "2015-02-28T20:30:40-08", -480 }, { "+CCLK: \"15/02/28,20:30:40-32\"", TRUE, FALSE, TRUE, "2015-02-28T20:30:40-08", -480 }, { "+CCLK: \"15/02/28,20:30:40-32\"", TRUE, TRUE, TRUE, "2015-02-28T20:30:40-08", -480 }, { "+CCLK: 17/07/26,11:42:15+01", TRUE, TRUE, FALSE, "2017-07-26T11:42:15+00:15", 15 }, { "+CCLK: 17/07/26,11:42:15+01", TRUE, FALSE, TRUE, "2017-07-26T11:42:15+00:15", 15 }, { "+CCLK: 17/07/26,11:42:15+01", TRUE, TRUE, TRUE, "2017-07-26T11:42:15+00:15", 15 }, { "+CCLK: \"15/02/28,20:30:40-32\"", TRUE, TRUE, FALSE, "2015-02-28T20:30:40-08", -480 }, { "+CCLK: \"15/02/28,20:30:40-32\"", TRUE, FALSE, TRUE, "2015-02-28T20:30:40-08:00", -480 }, { "+CCLK: \"15/02/28,20:30:40-32\"", TRUE, TRUE, TRUE, "2015-02-28T20:30:40-08", -480 }, { "+CCLK: 17/07/26,11:42:15+01", TRUE, TRUE, FALSE, "2017-07-26T11:42:15+00:15", 15 }, { "+CCLK: 17/07/26,11:42:15+01", TRUE, FALSE, TRUE, "2017-07-26T11:42:15+00:15", 15 }, { "+CCLK: 17/07/26,11:42:15+01", TRUE, TRUE, TRUE, "2017-07-26T11:42:15+00:15", 15 }, { "+CCLK: \"XX/XX/XX,XX:XX:XX+XX\"", FALSE, TRUE, FALSE, NULL, MM_NETWORK_TIMEZONE_OFFSET_UNKNOWN }, { NULL, FALSE, FALSE, FALSE, NULL, MM_NETWORK_TIMEZONE_OFFSET_UNKNOWN } }; static void test_cclk_response (void) { guint i; for (i = 0; cclk_tests[i].str; i++) { GError *error = NULL; gchar *iso8601 = NULL; MMNetworkTimezone *tz = NULL; gboolean ret; ret = mm_parse_cclk_response (cclk_tests[i].str, cclk_tests[i].test_iso8601 ? &iso8601 : NULL, cclk_tests[i].test_tz ? &tz : NULL, &error); g_assert (ret == cclk_tests[i].ret); g_assert (ret == (error ? FALSE : TRUE)); g_clear_error (&error); if (cclk_tests[i].test_iso8601) g_assert_cmpstr (cclk_tests[i].iso8601, ==, iso8601); if (cclk_tests[i].test_tz) { g_assert (mm_network_timezone_get_offset (tz) == cclk_tests[i].offset); g_assert (mm_network_timezone_get_dst_offset (tz) == MM_NETWORK_TIMEZONE_OFFSET_UNKNOWN); g_assert (mm_network_timezone_get_leap_seconds (tz) == MM_NETWORK_TIMEZONE_LEAP_SECONDS_UNKNOWN); } g_free (iso8601); if (tz) g_object_unref (tz); } } /*****************************************************************************/ /* Test +CRSM responses */ typedef struct { const gchar *str; gboolean ret; guint sw1; guint sw2; const gchar *hex; } CrsmTest; static const CrsmTest crsm_tests[] = { { "+CRSM: 144, 0, 0054485552415941FFFFFFFFFFFFFFFFFF", TRUE, 144, 0, "0054485552415941FFFFFFFFFFFFFFFFFF" }, { "+CRSM: 144, 0,0054485552415941FFFFFFFFFFFFFFFFFF", TRUE, 144, 0, "0054485552415941FFFFFFFFFFFFFFFFFF" }, { "+CRSM: 144, 0, \"0054485552415941FFFFFFFFFFFFFFFFFF\"", TRUE, 144, 0, "0054485552415941FFFFFFFFFFFFFFFFFF" }, { "+CRSM: 144, 0,\"0054485552415941FFFFFFFFFFFFFFFFFF\"", TRUE, 144, 0, "0054485552415941FFFFFFFFFFFFFFFFFF" }, { NULL, FALSE, 0, 0, NULL } }; static void test_crsm_response (void) { guint i; for (i = 0; crsm_tests[i].str; i++) { GError *error = NULL; guint sw1 = 0; guint sw2 = 0; gchar *hex = 0; gboolean ret; ret = mm_3gpp_parse_crsm_response (crsm_tests[i].str, &sw1, &sw2, &hex, &error); g_assert (ret == crsm_tests[i].ret); g_assert (ret == (error ? FALSE : TRUE)); g_clear_error (&error); g_assert (sw1 == crsm_tests[i].sw1); g_assert (sw2 == crsm_tests[i].sw2); g_assert_cmpstr (crsm_tests[i].hex, ==, hex); g_free (hex); } } /*****************************************************************************/ /* Test CGCONTRDP=N responses */ typedef struct { const gchar *str; guint cid; guint bearer_id; const gchar *apn; const gchar *local_address; const gchar *subnet; const gchar *gateway_address; const gchar *dns_primary_address; const gchar *dns_secondary_address; } CgcontrdpResponseTest; static const CgcontrdpResponseTest cgcontrdp_response_tests[] = { /* Post TS 27.007 v9.4.0 format */ { .str = "+CGCONTRDP: 4,5,\"ibox.tim.it.mnc001.mcc222.gprs\",\"2.197.17.49.255.255.255.255\",\"2.197.17.49\",\"10.207.43.46\",\"10.206.56.132\",\"0.0.0.0\",\"0.0.0.0\",0", .cid = 4, .bearer_id = 5, .apn = "ibox.tim.it.mnc001.mcc222.gprs", .local_address = "2.197.17.49", .subnet = "255.255.255.255", .gateway_address = "2.197.17.49", .dns_primary_address = "10.207.43.46", .dns_secondary_address = "10.206.56.132", }, { .str = "+CGCONTRDP: 4,5,\"ibox.tim.it.mnc001.mcc222.gprs\",\"2.197.17.49.255.255.255.255\",\"2.197.17.49\",\"10.207.43.46\"", .cid = 4, .bearer_id = 5, .apn = "ibox.tim.it.mnc001.mcc222.gprs", .local_address = "2.197.17.49", .subnet = "255.255.255.255", .gateway_address = "2.197.17.49", .dns_primary_address = "10.207.43.46", }, { .str = "+CGCONTRDP: 4,5,\"ibox.tim.it.mnc001.mcc222.gprs\",\"2.197.17.49.255.255.255.255\",\"2.197.17.49\"", .cid = 4, .bearer_id = 5, .apn = "ibox.tim.it.mnc001.mcc222.gprs", .local_address = "2.197.17.49", .subnet = "255.255.255.255", .gateway_address = "2.197.17.49", }, { .str = "+CGCONTRDP: 4,5,\"ibox.tim.it.mnc001.mcc222.gprs\",\"2.197.17.49.255.255.255.255\"", .cid = 4, .bearer_id = 5, .apn = "ibox.tim.it.mnc001.mcc222.gprs", .local_address = "2.197.17.49", .subnet = "255.255.255.255", }, /* Pre TS 27.007 v9.4.0 format */ { .str = "+CGCONTRDP: 4,5,\"ibox.tim.it.mnc001.mcc222.gprs\",\"2.197.17.49\",\"255.255.255.255\",\"2.197.17.49\",\"10.207.43.46\",\"10.206.56.132\",\"0.0.0.0\",\"0.0.0.0\"", .cid = 4, .bearer_id = 5, .apn = "ibox.tim.it.mnc001.mcc222.gprs", .local_address = "2.197.17.49", .subnet = "255.255.255.255", .gateway_address = "2.197.17.49", .dns_primary_address = "10.207.43.46", .dns_secondary_address = "10.206.56.132", }, { .str = "+CGCONTRDP: 4,5,\"ibox.tim.it.mnc001.mcc222.gprs\",\"2.197.17.49\",\"255.255.255.255\",\"2.197.17.49\",\"10.207.43.46\"", .cid = 4, .bearer_id = 5, .apn = "ibox.tim.it.mnc001.mcc222.gprs", .local_address = "2.197.17.49", .subnet = "255.255.255.255", .gateway_address = "2.197.17.49", .dns_primary_address = "10.207.43.46", }, { .str = "+CGCONTRDP: 4,5,\"ibox.tim.it.mnc001.mcc222.gprs\",\"2.197.17.49\",\"255.255.255.255\",\"2.197.17.49\"", .cid = 4, .bearer_id = 5, .apn = "ibox.tim.it.mnc001.mcc222.gprs", .local_address = "2.197.17.49", .subnet = "255.255.255.255", .gateway_address = "2.197.17.49", }, { .str = "+CGCONTRDP: 4,5,\"ibox.tim.it.mnc001.mcc222.gprs\",\"2.197.17.49\",\"255.255.255.255\"", .cid = 4, .bearer_id = 5, .apn = "ibox.tim.it.mnc001.mcc222.gprs", .local_address = "2.197.17.49", .subnet = "255.255.255.255", }, { .str = "+CGCONTRDP: 4,5,\"ibox.tim.it.mnc001.mcc222.gprs\",\"2.197.17.49\"", .cid = 4, .bearer_id = 5, .apn = "ibox.tim.it.mnc001.mcc222.gprs", .local_address = "2.197.17.49", }, /* Common */ { .str = "+CGCONTRDP: 4,5,\"ibox.tim.it.mnc001.mcc222.gprs\"", .cid = 4, .bearer_id = 5, .apn = "ibox.tim.it.mnc001.mcc222.gprs", }, { .str = "+CGCONTRDP: 4,5,\"\"", .cid = 4, .bearer_id = 5, }, }; static void test_cgcontrdp_response (void) { guint i; for (i = 0; i < G_N_ELEMENTS (cgcontrdp_response_tests); i++) { GError *error = NULL; gboolean success; guint cid = G_MAXUINT; guint bearer_id = G_MAXUINT; gchar *apn = NULL; gchar *local_address = NULL; gchar *subnet = NULL; gchar *gateway_address = NULL; gchar *dns_primary_address = NULL; gchar *dns_secondary_address = NULL; success = mm_3gpp_parse_cgcontrdp_response (cgcontrdp_response_tests[i].str, &cid, &bearer_id, &apn, &local_address, &subnet, &gateway_address, &dns_primary_address, &dns_secondary_address, &error); g_assert_no_error (error); g_assert (success); g_assert_cmpuint (cgcontrdp_response_tests[i].cid, ==, cid); g_assert_cmpuint (cgcontrdp_response_tests[i].bearer_id, ==, bearer_id); g_assert_cmpstr (cgcontrdp_response_tests[i].apn, ==, apn); g_assert_cmpstr (cgcontrdp_response_tests[i].local_address, ==, local_address); g_assert_cmpstr (cgcontrdp_response_tests[i].subnet, ==, subnet); g_assert_cmpstr (cgcontrdp_response_tests[i].gateway_address, ==, gateway_address); g_assert_cmpstr (cgcontrdp_response_tests[i].dns_primary_address, ==, dns_primary_address); g_assert_cmpstr (cgcontrdp_response_tests[i].dns_secondary_address, ==, dns_secondary_address); g_free (apn); g_free (local_address); g_free (subnet); g_free (gateway_address); g_free (dns_primary_address); g_free (dns_secondary_address); } } /*****************************************************************************/ /* Test CFUN? response */ typedef struct { const gchar *str; guint state; } CfunQueryTest; static const CfunQueryTest cfun_query_tests[] = { { "+CFUN: 1", 1 }, { "+CFUN: 1,0", 1 }, { "+CFUN: 0", 0 }, { "+CFUN: 0,0", 0 }, { "+CFUN: 19", 19 }, { "+CFUN: 19,0", 19 }, }; static void test_cfun_response (void) { guint i; for (i = 0; i < G_N_ELEMENTS (cfun_query_tests); i++) { GError *error = NULL; gboolean success; guint state = G_MAXUINT; success = mm_3gpp_parse_cfun_query_response (cfun_query_tests[i].str, &state, &error); g_assert_no_error (error); g_assert (success); g_assert_cmpuint (cfun_query_tests[i].state, ==, state); } } /*****************************************************************************/ /* Test +CESQ responses */ typedef struct { const gchar *str; gboolean gsm_info; guint rxlev; gdouble rssi; guint ber; gboolean umts_info; guint rscp_level; gdouble rscp; guint ecn0_level; gdouble ecio; gboolean lte_info; guint rsrq_level; gdouble rsrq; guint rsrp_level; gdouble rsrp; } CesqResponseTest; static const CesqResponseTest cesq_response_tests[] = { { .str = "+CESQ: 99,99,255,255,20,80", .gsm_info = FALSE, .rxlev = 99, .ber = 99, .umts_info = FALSE, .rscp_level = 255, .ecn0_level = 255, .lte_info = TRUE, .rsrq_level = 20, .rsrq = -10.0, .rsrp_level = 80, .rsrp = -61.0, }, { .str = "+CESQ: 99,99,95,40,255,255", .gsm_info = FALSE, .rxlev = 99, .ber = 99, .umts_info = TRUE, .rscp_level = 95, .rscp = -26.0, .ecn0_level = 40, .ecio = -4.5, .lte_info = FALSE, .rsrq_level = 255, .rsrp_level = 255, }, { .str = "+CESQ: 10,6,255,255,255,255", .gsm_info = TRUE, .rxlev = 10, .rssi = -101.0, .ber = 6, .umts_info = FALSE, .rscp_level = 255, .ecn0_level = 255, .lte_info = FALSE, .rsrq_level = 255, .rsrp_level = 255, } }; static void test_cesq_response (void) { guint i; for (i = 0; i < G_N_ELEMENTS (cesq_response_tests); i++) { GError *error = NULL; gboolean success; guint rxlev = G_MAXUINT; guint ber = G_MAXUINT; guint rscp = G_MAXUINT; guint ecn0 = G_MAXUINT; guint rsrq = G_MAXUINT; guint rsrp = G_MAXUINT; success = mm_3gpp_parse_cesq_response (cesq_response_tests[i].str, &rxlev, &ber, &rscp, &ecn0, &rsrq, &rsrp, &error); g_assert_no_error (error); g_assert (success); g_assert_cmpuint (cesq_response_tests[i].rxlev, ==, rxlev); g_assert_cmpuint (cesq_response_tests[i].ber, ==, ber); g_assert_cmpuint (cesq_response_tests[i].rscp_level, ==, rscp); g_assert_cmpuint (cesq_response_tests[i].ecn0_level, ==, ecn0); g_assert_cmpuint (cesq_response_tests[i].rsrq_level, ==, rsrq); g_assert_cmpuint (cesq_response_tests[i].rsrp_level, ==, rsrp); } } static void test_cesq_response_to_signal (void) { guint i; for (i = 0; i < G_N_ELEMENTS (cesq_response_tests); i++) { GError *error = NULL; gboolean success; MMSignal *gsm = NULL; MMSignal *umts = NULL; MMSignal *lte = NULL; success = mm_3gpp_cesq_response_to_signal_info (cesq_response_tests[i].str, NULL, &gsm, &umts, <e, &error); g_assert_no_error (error); g_assert (success); if (cesq_response_tests[i].gsm_info) { g_assert (gsm); g_assert_cmpfloat_tolerance (mm_signal_get_rssi (gsm), cesq_response_tests[i].rssi, 0.1); g_object_unref (gsm); } else g_assert (!gsm); if (cesq_response_tests[i].umts_info) { g_assert (umts); g_assert_cmpfloat_tolerance (mm_signal_get_rscp (umts), cesq_response_tests[i].rscp, 0.1); g_assert_cmpfloat_tolerance (mm_signal_get_ecio (umts), cesq_response_tests[i].ecio, 0.1); g_object_unref (umts); } else g_assert (!umts); if (cesq_response_tests[i].lte_info) { g_assert (lte); g_assert_cmpfloat_tolerance (mm_signal_get_rsrq (lte), cesq_response_tests[i].rsrq, 0.1); g_assert_cmpfloat_tolerance (mm_signal_get_rsrp (lte), cesq_response_tests[i].rsrp, 0.1); g_object_unref (lte); } else g_assert (!lte); } } typedef struct { const gchar *str; MMModemPowerState state; } CfunQueryGenericTest; static const CfunQueryGenericTest cfun_query_generic_tests[] = { { "+CFUN: 1", MM_MODEM_POWER_STATE_ON }, { "+CFUN: 1,0", MM_MODEM_POWER_STATE_ON }, { "+CFUN: 0", MM_MODEM_POWER_STATE_OFF }, { "+CFUN: 0,0", MM_MODEM_POWER_STATE_OFF }, { "+CFUN: 4", MM_MODEM_POWER_STATE_LOW }, { "+CFUN: 4,0", MM_MODEM_POWER_STATE_LOW }, }; static void test_cfun_generic_response (void) { guint i; for (i = 0; i < G_N_ELEMENTS (cfun_query_generic_tests); i++) { GError *error = NULL; gboolean success; MMModemPowerState state = MM_MODEM_POWER_STATE_UNKNOWN; success = mm_3gpp_parse_cfun_query_generic_response (cfun_query_generic_tests[i].str, &state, &error); g_assert_no_error (error); g_assert (success); g_assert_cmpuint (cfun_query_generic_tests[i].state, ==, state); } } typedef struct { const gchar *response; gint result; const gchar *error_message; } CSIMResponseTest; static CSIMResponseTest csim_response_test_list [] = { /* The parser expects that 2nd arg contains * substring "63Cx" where x is an HEX string * representing the retry value */ {"+CSIM:8,\"000063C1\"", 1, NULL}, {"+CSIM:8,\"000063CA\"", 10, NULL}, {"+CSIM:8,\"000063CF\"", 15, NULL}, /* The parser accepts spaces */ {"+CSIM:8, \"000063C1\"", 1, NULL}, {"+CSIM: 8, \"000063C1\"", 1, NULL}, {"+CSIM: 8, \"000063C1\"", 1, NULL}, /* the parser expects an int as first argument (2nd arg's length), * but does not check if it is correct */ {"+CSIM: 10, \"63CF\"", 15, NULL}, /* Valid +CSIM Error codes */ {"+CSIM: 4, \"6300\"", -1, "SIM verification failed"}, {"+CSIM: 4, \"6983\"", -1, "SIM authentication method blocked"}, {"+CSIM: 4, \"6984\"", -1, "SIM reference data invalidated"}, {"+CSIM: 4, \"6A86\"", -1, "Incorrect parameters in SIM request"}, {"+CSIM: 4, \"6A88\"", -1, "SIM reference data not found"}, /* Test error: missing first argument */ {"+CSIM:000063CF\"", -1, "Could not recognize +CSIM response '+CSIM:000063CF\"'"}, /* Test error: missing quotation mark */ {"+CSIM: 8, 000063CF", -1, "Could not recognize +CSIM response '+CSIM: 8, 000063CF'"}, /* Test generic error */ {"+CSIM: 4, \"63BF\"", -1, "Unknown error returned '0x63bf'"}, {"+CSIM: 4, \"63D0\"", -1, "Unknown error returned '0x63d0'"} }; static void test_csim_response (void) { guint i; gint res; GError* error = NULL; for (i = 0; i < G_N_ELEMENTS (csim_response_test_list); i++) { res = mm_parse_csim_response (csim_response_test_list[i].response, &error); if (csim_response_test_list[i].error_message == NULL) { g_assert_no_error (error); } else { g_assert_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED); g_assert_cmpstr (error->message, ==, csim_response_test_list[i].error_message); g_clear_error (&error); } g_assert_cmpint (res, ==, csim_response_test_list[i].result); } } /*****************************************************************************/ /* +CLIP URC */ typedef struct { const gchar *str; const gchar *number; guint type; } ClipUrcTest; static const ClipUrcTest clip_urc_tests[] = { { "\r\n+CLIP: \"123456789\",129\r\n", "123456789", 129 }, { "\r\n+CLIP: \"123456789\",129,,,,0\r\n", "123456789", 129 }, }; static void test_clip_indication (void) { g_autoptr(GRegex) r = NULL; guint i; r = mm_voice_clip_regex_get (); for (i = 0; i < G_N_ELEMENTS (clip_urc_tests); i++) { g_autoptr(GMatchInfo) match_info = NULL; g_autofree gchar *number = NULL; guint type; g_assert (g_regex_match (r, clip_urc_tests[i].str, 0, &match_info)); g_assert (g_match_info_matches (match_info)); number = mm_get_string_unquoted_from_match_info (match_info, 1); g_assert_cmpstr (number, ==, clip_urc_tests[i].number); g_assert (mm_get_uint_from_match_info (match_info, 2, &type)); g_assert_cmpuint (type, ==, clip_urc_tests[i].type); } } /*****************************************************************************/ /* +CCWA URC */ typedef struct { const gchar *str; const gchar *number; guint type; guint class; } CcwaUrcTest; static const CcwaUrcTest ccwa_urc_tests[] = { { "\r\n+CCWA: \"123456789\",129,1\r\n", "123456789", 129, 1 }, { "\r\n+CCWA: \"123456789\",129,1,,0\r\n", "123456789", 129, 1 }, { "\r\n+CCWA: \"123456789\",129,1,,0,,,\r\n", "123456789", 129, 1 }, }; static void test_ccwa_indication (void) { g_autoptr(GRegex) r = NULL; guint i; r = mm_voice_ccwa_regex_get (); for (i = 0; i < G_N_ELEMENTS (ccwa_urc_tests); i++) { g_autoptr(GMatchInfo) match_info = NULL; g_autofree gchar *number = NULL; guint type; guint class; g_assert (g_regex_match (r, ccwa_urc_tests[i].str, 0, &match_info)); g_assert (g_match_info_matches (match_info)); number = mm_get_string_unquoted_from_match_info (match_info, 1); g_assert_cmpstr (number, ==, ccwa_urc_tests[i].number); g_assert (mm_get_uint_from_match_info (match_info, 2, &type)); g_assert_cmpuint (type, ==, ccwa_urc_tests[i].type); g_assert (mm_get_uint_from_match_info (match_info, 3, &class)); g_assert_cmpuint (class, ==, ccwa_urc_tests[i].class); } } /*****************************************************************************/ /* +CCWA service query response testing */ static void common_test_ccwa_response (const gchar *response, gboolean expected_status, gboolean expected_error) { gboolean status = FALSE; GError *error = NULL; gboolean result; result = mm_3gpp_parse_ccwa_service_query_response (response, NULL, &status, &error); if (expected_error) { g_assert (!result); g_assert (error); g_error_free (error); } else { g_assert (result); g_assert_no_error (error); g_assert_cmpuint (status, ==, expected_status); } } typedef struct { const gchar *response; gboolean expected_status; gboolean expected_error; } TestCcwa; static TestCcwa test_ccwa[] = { { "+CCWA: 0,255\r\n", FALSE, FALSE }, /* all disabled */ { "+CCWA: 1,255\r\n", TRUE, FALSE }, /* all enabled */ { "+CCWA: 0,1\r\n" "+CCWA: 0,4\r\n", FALSE, FALSE }, /* voice and fax disabled */ { "+CCWA: 1,1\r\n" "+CCWA: 1,4\r\n", TRUE, FALSE }, /* voice and fax enabled */ { "+CCWA: 0,2\r\n" "+CCWA: 0,4\r\n" "+CCWA: 0,8\r\n", FALSE, TRUE }, /* data, fax, sms disabled, voice not given */ { "+CCWA: 1,2\r\n" "+CCWA: 1,4\r\n" "+CCWA: 1,8\r\n", FALSE, TRUE }, /* data, fax, sms enabled, voice not given */ { "+CCWA: 2,1\r\n" "+CCWA: 2,4\r\n", FALSE, TRUE }, /* voice and fax enabled but unexpected state */ }; static void test_ccwa_response (void) { guint i; for (i = 0; i < G_N_ELEMENTS (test_ccwa); i++) common_test_ccwa_response (test_ccwa[i].response, test_ccwa[i].expected_status, test_ccwa[i].expected_error); } /*****************************************************************************/ /* Test +CLCC URCs */ static void common_test_clcc_response (const gchar *str, const MMCallInfo *expected_call_info_list, guint expected_call_info_list_size) { GError *error = NULL; gboolean result; GList *call_info_list = NULL; GList *l; result = mm_3gpp_parse_clcc_response (str, NULL, &call_info_list, &error); g_assert_no_error (error); g_assert (result); g_debug ("found %u calls", g_list_length (call_info_list)); if (expected_call_info_list) { g_assert (call_info_list); g_assert_cmpuint (g_list_length (call_info_list), ==, expected_call_info_list_size); } else g_assert (!call_info_list); for (l = call_info_list; l; l = g_list_next (l)) { const MMCallInfo *call_info = (const MMCallInfo *)(l->data); gboolean found = FALSE; guint i; g_debug ("call at index %u: direction %s, state %s, number %s", call_info->index, mm_call_direction_get_string (call_info->direction), mm_call_state_get_string (call_info->state), call_info->number ? call_info->number : "n/a"); for (i = 0; !found && i < expected_call_info_list_size; i++) found = ((call_info->index == expected_call_info_list[i].index) && (call_info->direction == expected_call_info_list[i].direction) && (call_info->state == expected_call_info_list[i].state) && (g_strcmp0 (call_info->number, expected_call_info_list[i].number) == 0)); g_assert (found); } mm_3gpp_call_info_list_free (call_info_list); } static void test_clcc_response_empty (void) { const gchar *response = "\r\n"; common_test_clcc_response (response, NULL, 0); } static void test_clcc_response_single (void) { static const MMCallInfo expected_call_info_list[] = { { 1, MM_CALL_DIRECTION_INCOMING, MM_CALL_STATE_ACTIVE, (gchar *) "123456789" } }; const gchar *response = "+CLCC: 1,1,0,0,0,\"123456789\",161\r\n"; common_test_clcc_response (response, expected_call_info_list, G_N_ELEMENTS (expected_call_info_list)); } static void test_clcc_response_single_long (void) { static const MMCallInfo expected_call_info_list[] = { { 1, MM_CALL_DIRECTION_INCOMING, MM_CALL_STATE_RINGING_IN, (gchar *) "123456789" } }; /* NOTE: priority field is EMPTY */ const gchar *response = "+CLCC: 1,1,4,0,0,\"123456789\",129,\"\",,0\r\n"; common_test_clcc_response (response, expected_call_info_list, G_N_ELEMENTS (expected_call_info_list)); } static void test_clcc_response_multiple (void) { static const MMCallInfo expected_call_info_list[] = { { 1, MM_CALL_DIRECTION_INCOMING, MM_CALL_STATE_ACTIVE, NULL }, { 2, MM_CALL_DIRECTION_INCOMING, MM_CALL_STATE_ACTIVE, (gchar *) "123456789" }, { 3, MM_CALL_DIRECTION_INCOMING, MM_CALL_STATE_ACTIVE, (gchar *) "987654321" }, { 4, MM_CALL_DIRECTION_INCOMING, MM_CALL_STATE_ACTIVE, (gchar *) "000000000" }, { 5, MM_CALL_DIRECTION_INCOMING, MM_CALL_STATE_WAITING, (gchar *) "555555555" }, }; const gchar *response = "+CLCC: 1,1,0,0,1\r\n" /* number unknown */ "+CLCC: 2,1,0,0,1,\"123456789\",161\r\n" "+CLCC: 3,1,0,0,1,\"987654321\",161,\"Alice\"\r\n" "+CLCC: 4,1,0,0,1,\"000000000\",161,\"Bob\",1\r\n" "+CLCC: 5,1,5,0,0,\"555555555\",161,\"Mallory\",2,0\r\n"; common_test_clcc_response (response, expected_call_info_list, G_N_ELEMENTS (expected_call_info_list)); } static void test_clcc_response_ignore_non_voice (void) { static const MMCallInfo expected_call_info_list[] = { { 1, MM_CALL_DIRECTION_INCOMING, MM_CALL_STATE_RINGING_IN, (gchar *) "987654321" }, { 4, MM_CALL_DIRECTION_INCOMING, MM_CALL_STATE_RINGING_IN, (gchar *) "111111111" }, { 5, MM_CALL_DIRECTION_INCOMING, MM_CALL_STATE_RINGING_IN, (gchar *) "222222222" }, { 6, MM_CALL_DIRECTION_INCOMING, MM_CALL_STATE_RINGING_IN, (gchar *) "333333333" }, }; const gchar *response = "+CLCC: 1,1,4,0,0,\"987654321\",161\r\n" /* voice mode */ "+CLCC: 2,1,4,1,0,\"123456789\",161\r\n" /* data mode, skip */ "+CLCC: 3,1,4,2,0,\"000000000\",161\r\n" /* fax data, skip */ "+CLCC: 4,1,4,3,0,\"111111111\",161\r\n" /* voice followed by data, voice mode */ "+CLCC: 5,1,4,4,0,\"222222222\",161\r\n" /* alternating voice/data, voice mode */ "+CLCC: 6,1,4,5,0,\"333333333\",161\r\n" /* alternating voice/fax, voice mode */ "+CLCC: 7,1,4,6,0,\"444444444\",161\r\n" /* voice followed by data, data mode, skip */ "+CLCC: 8,1,4,7,0,\"555555555\",161\r\n" /* alternating voice/data, data mode, skip */ "+CLCC: 9,1,4,8,0,\"666666666\",161\r\n" /* alternating voice/fax, fax mode, skip */ "+CLCC: 10,1,4,9,0,\"777777777\",161\r\n"; /* unknown mode, skip */ common_test_clcc_response (response, expected_call_info_list, G_N_ELEMENTS (expected_call_info_list)); } /*****************************************************************************/ /* Test +CRSM EF_ECC read data parsing */ #define MAX_EMERGENCY_NUMBERS 5 typedef struct { const gchar *raw; guint n_numbers; const gchar *numbers[MAX_EMERGENCY_NUMBERS]; } EmergencyNumbersTest; static const EmergencyNumbersTest emergency_numbers_tests[] = { { "", 0 }, { "FFF", 0 }, { "FFFFFF" "FFFFFF" "FFFFFF" "FFFFFF" "FFFFFF", 0 }, { "00F0FF" "11F2FF" "88F8FF", 3, { "000", "112", "888" } }, { "00F0FF" "11F2FF" "88F8FF" "FFFFFF" "FFFFFF", 3, { "000", "112", "888" } }, { "00F0FF" "11F2FF" "88F8FF" "214365" "08FFFF", 5, { "000", "112", "888", "123456", "80" } }, }; static void test_emergency_numbers (void) { guint i; for (i = 0; i < G_N_ELEMENTS (emergency_numbers_tests); i++) { GStrv numbers; GError *error = NULL; guint j; g_debug (" testing %s...", emergency_numbers_tests[i].raw); numbers = mm_3gpp_parse_emergency_numbers (emergency_numbers_tests[i].raw, &error); if (!emergency_numbers_tests[i].n_numbers) { g_assert (error); g_assert (!numbers); continue; } g_assert_no_error (error); g_assert (numbers); g_assert_cmpuint (emergency_numbers_tests[i].n_numbers, ==, g_strv_length (numbers)); for (j = 0; j < emergency_numbers_tests[i].n_numbers; j++) g_assert_cmpstr (emergency_numbers_tests[i].numbers[j], ==, numbers[j]); g_strfreev (numbers); } } /*****************************************************************************/ typedef struct { const gchar *str; gint expected_number_list[9]; } TestParseNumberList; static const TestParseNumberList test_parse_number_list_item [] = { { "1-6", { 1, 2, 3, 4, 5, 6, -1 } }, { "0,1,2,4,6", { 0, 1, 2, 4, 6, -1 } }, { "1,3-5,7,9-11", { 1, 3, 4, 5, 7, 9, 10, 11, -1 } }, { "9-11,7,3-5", { 3, 4, 5, 7, 9, 10, 11, -1 } }, { "", { -1 } }, { NULL, { -1 } }, }; static void test_parse_uint_list (void) { guint i; for (i = 0; i < G_N_ELEMENTS (test_parse_number_list_item); i++) { GArray *array; GError *error = NULL; guint j; array = mm_parse_uint_list (test_parse_number_list_item[i].str, &error); g_assert_no_error (error); if (test_parse_number_list_item[i].expected_number_list[0] == -1) { g_assert (!array); continue; } g_assert (array); for (j = 0; j < array->len; j++) { g_assert_cmpint (test_parse_number_list_item[i].expected_number_list[j], !=, -1); g_assert_cmpuint (test_parse_number_list_item[i].expected_number_list[j], ==, g_array_index (array, guint, j)); } g_assert_cmpint (test_parse_number_list_item[i].expected_number_list[array->len], ==, -1); g_array_unref (array); } } /*****************************************************************************/ typedef struct { const guint8 bcd[10]; gsize bcd_len; const gchar *low_nybble_first_str; const gchar *high_nybble_first_str; } BcdToStringTest; static const BcdToStringTest bcd_to_string_tests[] = { { { }, 0, "", "" }, { { 0x01 }, 1, "10", "01" }, { { 0x1F }, 1, "", "1" }, { { 0xE2 }, 1, "2", "" }, { { 0xD3 }, 1, "3", "" }, { { 0xC4 }, 1, "4", "" }, { { 0xB1, 0x23 }, 2, "1", "" }, { { 0x01, 0x2A }, 2, "10", "012" }, { { 0x01, 0x23, 0x45, 0x67 }, 4, "10325476", "01234567" }, { { 0x01, 0x23, 0x45, 0xA7 }, 4, "1032547", "012345" }, { { 0x01, 0x23, 0x45, 0x67 }, 2, "1032", "0123" }, }; static void test_bcd_to_string (void *f, gpointer d) { guint i; for (i = 0; i < G_N_ELEMENTS (bcd_to_string_tests); i++) { gchar *str; str = mm_bcd_to_string (bcd_to_string_tests[i].bcd, bcd_to_string_tests[i].bcd_len, TRUE /* low_nybble_first */); g_assert_cmpstr (str, ==, bcd_to_string_tests[i].low_nybble_first_str); g_free (str); str = mm_bcd_to_string (bcd_to_string_tests[i].bcd, bcd_to_string_tests[i].bcd_len, FALSE /* low_nybble_first */); g_assert_cmpstr (str, ==, bcd_to_string_tests[i].high_nybble_first_str); g_free (str); } } /*****************************************************************************/ typedef struct { const gchar *response; gboolean expected_error; guint expected_index; const gchar *expected_operator_code; gboolean expected_gsm_act; gboolean expected_gsm_compact_act; gboolean expected_utran_act; gboolean expected_eutran_act; gboolean expected_ngran_act; guint expected_act_count; } TestCpol; static const TestCpol test_cpol[] = { { "+CPOL: 1,2,\"24412\",0,0,0,0", FALSE, 1, "24412", FALSE, FALSE, FALSE, FALSE, FALSE, 4 }, { "+CPOL: 1,2,24412,0,0,0,0", FALSE, 1, "24412", FALSE, FALSE, FALSE, FALSE, FALSE, 4 }, { "+CPOL: 1", TRUE }, { "+CPOL: 1,2", TRUE }, { "+CPOL: 1,1,\"Test\"", TRUE }, { "+CPOL: 5,2,123456,0,0,0,0,1", FALSE, 5, "123456", FALSE, FALSE, FALSE, FALSE, TRUE, 5 }, { "+CPOL: 99,2,\"63423\",1,0,1,0,1", FALSE, 99, "63423", TRUE, FALSE, TRUE, FALSE, TRUE, 5 }, { "+CPOL: 101,2,\"63423\",1,1,1", FALSE, 101, "63423", TRUE, TRUE, TRUE, FALSE, FALSE, 3 }, { "+CPOL: 101,2,\"24491\"", FALSE, 101, "24491", FALSE, FALSE, FALSE, FALSE, FALSE, 0 }, { "+CPOL: X,2,\"24491\"", TRUE }, { "+CPOL:1, 2, 12345, 1, 1, 1, 1, 1", FALSE, 1, "12345", TRUE, TRUE, TRUE, TRUE, TRUE, 5 } }; static void test_cpol_response (void) { guint i; for (i = 0; i < G_N_ELEMENTS (test_cpol); i++) { guint index; gchar *operator_code = NULL; gboolean gsm_act; gboolean gsm_compact_act; gboolean utran_act; gboolean eutran_act; gboolean ngran_act; guint act_count; gboolean result; GError *error = NULL; result = mm_sim_parse_cpol_query_response (test_cpol[i].response, &index, &operator_code, &gsm_act, &gsm_compact_act, &utran_act, &eutran_act, &ngran_act, &act_count, &error); if (test_cpol[i].expected_error) { g_assert (!result); g_assert (error); g_error_free (error); } else { g_assert (result); g_assert_no_error (error); g_assert_cmpuint (index, ==, test_cpol[i].expected_index); g_assert_cmpstr (operator_code, ==, test_cpol[i].expected_operator_code); g_assert_cmpuint (gsm_act, ==, test_cpol[i].expected_gsm_act); g_assert_cmpuint (gsm_compact_act, ==, test_cpol[i].expected_gsm_compact_act); g_assert_cmpuint (utran_act, ==, test_cpol[i].expected_utran_act); g_assert_cmpuint (eutran_act, ==, test_cpol[i].expected_eutran_act); g_assert_cmpuint (ngran_act, ==, test_cpol[i].expected_ngran_act); g_assert_cmpuint (act_count, ==, test_cpol[i].expected_act_count); } g_free (operator_code); } } /*****************************************************************************/ #define TESTCASE(t, d) g_test_create_case (#t, 0, d, NULL, (GTestFixtureFunc) t, NULL) int main (int argc, char **argv) { GTestSuite *suite; RegTestData *reg_data; gint result; DevidItem *item = &devids[0]; g_test_init (&argc, &argv, NULL); suite = g_test_get_root (); reg_data = reg_test_data_new (); g_test_suite_add (suite, TESTCASE (test_ifc_response_all_simple, NULL)); g_test_suite_add (suite, TESTCASE (test_ifc_response_all_groups, NULL)); g_test_suite_add (suite, TESTCASE (test_ifc_response_none_only, NULL)); g_test_suite_add (suite, TESTCASE (test_ifc_response_xon_xoff_only, NULL)); g_test_suite_add (suite, TESTCASE (test_ifc_response_rts_cts_only, NULL)); g_test_suite_add (suite, TESTCASE (test_ifc_response_no_xon_xoff, NULL)); g_test_suite_add (suite, TESTCASE (test_ifc_response_no_xon_xoff_in_ta, NULL)); g_test_suite_add (suite, TESTCASE (test_ifc_response_no_xon_xoff_in_te, NULL)); g_test_suite_add (suite, TESTCASE (test_ifc_response_no_rts_cts_simple, NULL)); g_test_suite_add (suite, TESTCASE (test_ifc_response_no_rts_cts_groups, NULL)); g_test_suite_add (suite, TESTCASE (test_ifc_response_all_simple_and_unknown, NULL)); g_test_suite_add (suite, TESTCASE (test_ifc_response_all_groups_and_unknown, NULL)); g_test_suite_add (suite, TESTCASE (test_ws46_response_generic_2g3g4g, NULL)); g_test_suite_add (suite, TESTCASE (test_ws46_response_generic_2g3g, NULL)); g_test_suite_add (suite, TESTCASE (test_ws46_response_generic_2g3g_v2, NULL)); g_test_suite_add (suite, TESTCASE (test_ws46_response_cinterion, NULL)); g_test_suite_add (suite, TESTCASE (test_ws46_response_telit_le866, NULL)); g_test_suite_add (suite, TESTCASE (test_ws46_response_range_1, NULL)); g_test_suite_add (suite, TESTCASE (test_ws46_response_range_2, NULL)); 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_sek600i, NULL)); g_test_suite_add (suite, TESTCASE (test_cops_response_samsung_z810, NULL)); g_test_suite_add (suite, TESTCASE (test_cops_response_ublox_lara, NULL)); g_test_suite_add (suite, TESTCASE (test_cops_response_em9191, 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)); g_test_suite_add (suite, TESTCASE (test_cops_query, NULL)); g_test_suite_add (suite, TESTCASE (test_normalize_operator, NULL)); g_test_suite_add (suite, TESTCASE (test_creg1_solicited, reg_data)); g_test_suite_add (suite, TESTCASE (test_creg1_unsolicited, reg_data)); g_test_suite_add (suite, TESTCASE (test_creg2_mercury_solicited, reg_data)); g_test_suite_add (suite, TESTCASE (test_creg2_mercury_unsolicited, reg_data)); g_test_suite_add (suite, TESTCASE (test_creg2_sek850i_solicited, reg_data)); g_test_suite_add (suite, TESTCASE (test_creg2_sek850i_unsolicited, reg_data)); g_test_suite_add (suite, TESTCASE (test_creg2_e160g_solicited_unregistered, reg_data)); g_test_suite_add (suite, TESTCASE (test_creg2_e160g_solicited, reg_data)); g_test_suite_add (suite, TESTCASE (test_creg2_e160g_unsolicited, reg_data)); g_test_suite_add (suite, TESTCASE (test_creg2_tm506_solicited, reg_data)); g_test_suite_add (suite, TESTCASE (test_creg2_xu870_unsolicited_unregistered, reg_data)); g_test_suite_add (suite, TESTCASE (test_creg2_md400_unsolicited, reg_data)); g_test_suite_add (suite, TESTCASE (test_creg2_s8500_wave_unsolicited, reg_data)); g_test_suite_add (suite, TESTCASE (test_creg2_gobi_weird_solicited, reg_data)); g_test_suite_add (suite, TESTCASE (test_creg2_iridium_solicited, reg_data)); g_test_suite_add (suite, TESTCASE (test_creg2_no_leading_zeros_solicited, reg_data)); g_test_suite_add (suite, TESTCASE (test_creg2_leading_zeros_solicited, reg_data)); g_test_suite_add (suite, TESTCASE (test_creg2_no_leading_zeros_unsolicited, reg_data)); g_test_suite_add (suite, TESTCASE (test_creg2_leading_zeros_unsolicited, reg_data)); g_test_suite_add (suite, TESTCASE (test_creg2_ublox_solicited, reg_data)); g_test_suite_add (suite, TESTCASE (test_creg2_ublox_unsolicited, reg_data)); g_test_suite_add (suite, TESTCASE (test_cgreg1_solicited, reg_data)); g_test_suite_add (suite, TESTCASE (test_cgreg1_unsolicited, reg_data)); g_test_suite_add (suite, TESTCASE (test_cgreg2_f3607gw_solicited, reg_data)); g_test_suite_add (suite, TESTCASE (test_cgreg2_f3607gw_unsolicited, reg_data)); g_test_suite_add (suite, TESTCASE (test_cgreg2_md400_unsolicited, reg_data)); g_test_suite_add (suite, TESTCASE (test_cgreg2_x220_unsolicited, reg_data)); g_test_suite_add (suite, TESTCASE (test_cgreg2_unsolicited_with_rac, reg_data)); g_test_suite_add (suite, TESTCASE (test_cgreg2_thuraya_solicited, reg_data)); g_test_suite_add (suite, TESTCASE (test_cgreg2_thuraya_unsolicited, reg_data)); g_test_suite_add (suite, TESTCASE (test_cereg1_solicited, reg_data)); g_test_suite_add (suite, TESTCASE (test_cereg1_unsolicited, reg_data)); g_test_suite_add (suite, TESTCASE (test_cereg2_solicited, reg_data)); g_test_suite_add (suite, TESTCASE (test_cereg2_unsolicited, reg_data)); g_test_suite_add (suite, TESTCASE (test_cereg2_altair_lte_solicited, reg_data)); g_test_suite_add (suite, TESTCASE (test_cereg2_altair_lte_unsolicited, reg_data)); g_test_suite_add (suite, TESTCASE (test_cereg2_novatel_lte_solicited, reg_data)); g_test_suite_add (suite, TESTCASE (test_cereg2_novatel_lte_unsolicited, reg_data)); g_test_suite_add (suite, TESTCASE (test_c5greg1_solicited, reg_data)); g_test_suite_add (suite, TESTCASE (test_c5greg1_unsolicited, reg_data)); g_test_suite_add (suite, TESTCASE (test_c5greg2_solicited, reg_data)); g_test_suite_add (suite, TESTCASE (test_c5greg2_unsolicited, reg_data)); g_test_suite_add (suite, TESTCASE (test_creg_cgreg_multi_unsolicited, reg_data)); g_test_suite_add (suite, TESTCASE (test_creg_cgreg_multi2_unsolicited, reg_data)); g_test_suite_add (suite, TESTCASE (test_cscs_icon225_support_response, NULL)); g_test_suite_add (suite, TESTCASE (test_cscs_sierra_mercury_support_response, NULL)); g_test_suite_add (suite, TESTCASE (test_cscs_buslink_support_response, NULL)); g_test_suite_add (suite, TESTCASE (test_cscs_blackberry_support_response, NULL)); g_test_suite_add (suite, TESTCASE (test_cmer_response_cinterion_pls8, NULL)); g_test_suite_add (suite, TESTCASE (test_cmer_response_sierra_em7345, NULL)); g_test_suite_add (suite, TESTCASE (test_cmer_response_cinterion_ehs5, NULL)); g_test_suite_add (suite, TESTCASE (test_cmer_request_cinterion_ehs5, NULL)); g_test_suite_add (suite, TESTCASE (test_cind_response_linktop_lw273, NULL)); g_test_suite_add (suite, TESTCASE (test_cind_response_moto_v3m, NULL)); g_test_suite_add (suite, TESTCASE (test_cgev_indication, NULL)); g_test_suite_add (suite, TESTCASE (test_iccid_parse_quoted_swap_19_digit, NULL)); g_test_suite_add (suite, TESTCASE (test_iccid_parse_unquoted_swap_20_digit, NULL)); g_test_suite_add (suite, TESTCASE (test_iccid_parse_unquoted_unswapped_19_digit, NULL)); g_test_suite_add (suite, TESTCASE (test_iccid_parse_unquoted_unswapped_19_digit_no_f, NULL)); g_test_suite_add (suite, TESTCASE (test_iccid_parse_quoted_unswapped_20_digit, NULL)); g_test_suite_add (suite, TESTCASE (test_iccid_parse_quoted_unswapped_hex_account, NULL)); g_test_suite_add (suite, TESTCASE (test_iccid_parse_quoted_unswapped_hex_account_2, NULL)); g_test_suite_add (suite, TESTCASE (test_iccid_parse_short, NULL)); g_test_suite_add (suite, TESTCASE (test_iccid_parse_invalid_chars, NULL)); g_test_suite_add (suite, TESTCASE (test_iccid_parse_quoted_invalid_mii, NULL)); g_test_suite_add (suite, TESTCASE (test_iccid_parse_unquoted_invalid_mii, NULL)); while (item->devid) { g_test_suite_add (suite, TESTCASE (test_devid_item, (gconstpointer) item)); item++; } g_test_suite_add (suite, TESTCASE (test_cpms_response_cinterion, NULL)); g_test_suite_add (suite, TESTCASE (test_cpms_response_huawei_mu609, NULL)); g_test_suite_add (suite, TESTCASE (test_cpms_response_nokia_c6, NULL)); g_test_suite_add (suite, TESTCASE (test_cpms_response_mixed, NULL)); g_test_suite_add (suite, TESTCASE (test_cpms_response_mixed_spaces, NULL)); g_test_suite_add (suite, TESTCASE (test_cpms_response_empty_fields, NULL)); g_test_suite_add (suite, TESTCASE (test_cpms_query_response, NULL)); g_test_suite_add (suite, TESTCASE (test_cmp_apn_name, NULL)); g_test_suite_add (suite, TESTCASE (test_cgdcont_test_response_single, NULL)); g_test_suite_add (suite, TESTCASE (test_cgdcont_test_response_multiple, NULL)); g_test_suite_add (suite, TESTCASE (test_cgdcont_test_response_multiple_and_ignore, NULL)); g_test_suite_add (suite, TESTCASE (test_cgdcont_test_response_single_context, NULL)); g_test_suite_add (suite, TESTCASE (test_cgdcont_test_response_thuraya, NULL)); g_test_suite_add (suite, TESTCASE (test_cgdcont_test_response_cinterion_phs8, NULL)); g_test_suite_add (suite, TESTCASE (test_cgdcont_read_response_nokia, NULL)); g_test_suite_add (suite, TESTCASE (test_cgdcont_read_response_samsung, NULL)); g_test_suite_add (suite, TESTCASE (test_cgdcont_read_response_simcom, NULL)); g_test_suite_add (suite, TESTCASE (test_profile_selection, NULL)); g_test_suite_add (suite, TESTCASE (test_cgact_read_response_none, NULL)); g_test_suite_add (suite, TESTCASE (test_cgact_read_response_single_inactive, NULL)); g_test_suite_add (suite, TESTCASE (test_cgact_read_response_single_active, NULL)); g_test_suite_add (suite, TESTCASE (test_cgact_read_response_multiple, NULL)); g_test_suite_add (suite, TESTCASE (test_cnum_response_generic, NULL)); g_test_suite_add (suite, TESTCASE (test_cnum_response_generic_without_detail, NULL)); g_test_suite_add (suite, TESTCASE (test_cnum_response_generic_detail_unquoted, NULL)); g_test_suite_add (suite, TESTCASE (test_cnum_response_generic_international_number, NULL)); g_test_suite_add (suite, TESTCASE (test_cnum_response_generic_multiple_numbers, NULL)); g_test_suite_add (suite, TESTCASE (test_parse_operator_id, NULL)); g_test_suite_add (suite, TESTCASE (test_parse_cds, NULL)); g_test_suite_add (suite, TESTCASE (test_cdma_parse_gsn, NULL)); g_test_suite_add (suite, TESTCASE (test_cmgl_response_generic, NULL)); g_test_suite_add (suite, TESTCASE (test_cmgl_response_generic_multiple, NULL)); g_test_suite_add (suite, TESTCASE (test_cmgl_response_pantech, NULL)); g_test_suite_add (suite, TESTCASE (test_cmgl_response_pantech_multiple, NULL)); g_test_suite_add (suite, TESTCASE (test_cmgr_response_generic, NULL)); g_test_suite_add (suite, TESTCASE (test_cmgr_response_telit, NULL)); g_test_suite_add (suite, TESTCASE (test_supported_mode_filter, NULL)); g_test_suite_add (suite, TESTCASE (test_cclk_response, NULL)); g_test_suite_add (suite, TESTCASE (test_crsm_response, NULL)); g_test_suite_add (suite, TESTCASE (test_cgcontrdp_response, NULL)); g_test_suite_add (suite, TESTCASE (test_cfun_response, NULL)); g_test_suite_add (suite, TESTCASE (test_cfun_generic_response, NULL)); g_test_suite_add (suite, TESTCASE (test_csim_response, NULL)); g_test_suite_add (suite, TESTCASE (test_cesq_response, NULL)); g_test_suite_add (suite, TESTCASE (test_cesq_response_to_signal, NULL)); g_test_suite_add (suite, TESTCASE (test_clip_indication, NULL)); g_test_suite_add (suite, TESTCASE (test_ccwa_indication, NULL)); g_test_suite_add (suite, TESTCASE (test_ccwa_response, NULL)); g_test_suite_add (suite, TESTCASE (test_clcc_response_empty, NULL)); g_test_suite_add (suite, TESTCASE (test_clcc_response_single, NULL)); g_test_suite_add (suite, TESTCASE (test_clcc_response_single_long, NULL)); g_test_suite_add (suite, TESTCASE (test_clcc_response_multiple, NULL)); g_test_suite_add (suite, TESTCASE (test_clcc_response_ignore_non_voice, NULL)); g_test_suite_add (suite, TESTCASE (test_emergency_numbers, NULL)); g_test_suite_add (suite, TESTCASE (test_parse_uint_list, NULL)); g_test_suite_add (suite, TESTCASE (test_bcd_to_string, NULL)); g_test_suite_add (suite, TESTCASE (test_cpol_response, NULL)); result = g_test_run (); reg_test_data_free (reg_data); return result; } ModemManager-1.23.4-dev/src/tests/test-qcdm-serial-port.c000066400000000000000000000300421456466623000231610ustar00rootroot00000000000000/* -*- 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include "mm-port-serial-qcdm.h" #include "libqcdm/src/commands.h" #include "libqcdm/src/utils.h" #include "libqcdm/src/com.h" #include "libqcdm/src/errors.h" #include "mm-log-test.h" typedef struct { int main; int secondary; gboolean valid; pid_t child; } TestData; static gboolean wait_for_child (TestData *d, guint32 timeout) { GTimeVal start, now; int status, ret; g_get_current_time (&start); do { status = 0; ret = waitpid (d->child, &status, WNOHANG); g_get_current_time (&now); if (d->child && (now.tv_sec - start.tv_sec > (glong)timeout)) { /* Kill it */ if (g_test_verbose ()) g_message ("Killing running child process %d", d->child); kill (d->child, SIGKILL); d->child = 0; } if (ret == 0) sleep (1); } while ((ret <= 0) || (!WIFEXITED (status) && !WIFSIGNALED (status))); d->child = 0; return (WIFEXITED (status) && WEXITSTATUS (status) == 0) ? TRUE : FALSE; } static void print_buf (const char *detail, const char *buf, gsize len) { guint i = 0; gboolean newline = FALSE; g_print ("%s (%zu) ", detail, len); for (i = 0; i < len; i++) { g_print ("0x%02x ", buf[i] & 0xFF); if (((i + 1) % 12) == 0) { g_print ("\n"); newline = TRUE; } else newline = FALSE; } if (!newline) g_print ("\n"); } static void server_send_response (int fd, const char *buf, gsize len) { int status; gsize i = 0; if (g_test_verbose ()) print_buf (">>>", buf, len); while (i < len) { errno = 0; status = write (fd, &buf[i], 1); g_assert_cmpint (errno, ==, 0); g_assert (status == 1); i++; usleep (1000); } } static gsize server_wait_request (int fd, char *buf, gsize len) { fd_set in; int result; struct timeval timeout = { 1, 0 }; char readbuf[1024]; ssize_t bytes_read; guint total = 0, retries = 0; gsize decap_len = 0; FD_ZERO (&in); FD_SET (fd, &in); result = select (fd + 1, &in, NULL, NULL, &timeout); g_assert_cmpint (result, ==, 1); g_assert (FD_ISSET (fd, &in)); do { errno = 0; bytes_read = read (fd, &readbuf[total], 1); if ((bytes_read == 0) || (errno == EAGAIN)) { /* Haven't gotten the async control char yet */ if (retries > 20) return 0; /* 2 seconds, give up */ /* Otherwise wait a bit and try again */ usleep (100000); retries++; continue; } else if (bytes_read == 1) { gboolean success; gsize used = 0; qcdmbool more = FALSE; total++; decap_len = 0; success = dm_decapsulate_buffer (readbuf, total, buf, len, &decap_len, &used, &more); /* Discard used data */ if (used > 0) { total -= used; memmove (readbuf, &readbuf[used], total); } if (success && !more) { /* Success; we have a packet */ break; } } else { /* Some error occurred */ g_assert_not_reached (); } } while (total < sizeof (readbuf)); if (g_test_verbose ()) { print_buf ("<<<", readbuf, total); print_buf ("D<<", buf, decap_len); } return decap_len; } static void qcdm_verinfo_expect_success_cb (MMPortSerialQcdm *port, GAsyncResult *res, GMainLoop *loop) { GError *error = NULL; GByteArray *response; response = mm_port_serial_qcdm_command_finish (port, res, &error); g_assert_no_error (error); g_assert (response->len > 0); g_byte_array_unref (response); g_main_loop_quit (loop); } static void qcdm_request_verinfo (MMPortSerialQcdm *port, GAsyncReadyCallback cb, GMainLoop *loop) { GByteArray *verinfo; gint len; /* Build up the probe command */ verinfo = g_byte_array_sized_new (50); len = qcdm_cmd_version_info_new ((char *) verinfo->data, 50); if (len <= 0) g_byte_array_free (verinfo, TRUE); verinfo->len = len; mm_port_serial_qcdm_command (port, verinfo, 3, NULL, cb, loop); g_byte_array_unref (verinfo); } static void qcdm_test_child (int fd, GAsyncReadyCallback cb) { MMPortSerialQcdm *port; GMainLoop *loop; gboolean success; GError *error = NULL; loop = g_main_loop_new (NULL, FALSE); port = mm_port_serial_qcdm_new_fd (fd); g_assert (port); success = mm_port_serial_open (MM_PORT_SERIAL (port), &error); g_assert_no_error (error); g_assert (success); qcdm_request_verinfo (port, cb, loop); g_main_loop_run (loop); g_main_loop_unref (loop); mm_port_serial_close (MM_PORT_SERIAL (port)); g_object_unref (port); } /* Test that a Version Info request/response is processed correctly to * make sure things in general are working. */ static void test_verinfo (TestData *d) { char req[512]; gsize req_len; pid_t cpid; const char rsp[] = { 0x00, 0x41, 0x75, 0x67, 0x20, 0x31, 0x39, 0x20, 0x32, 0x30, 0x30, 0x38, 0x32, 0x30, 0x3a, 0x34, 0x38, 0x3a, 0x34, 0x37, 0x4f, 0x63, 0x74, 0x20, 0x32, 0x39, 0x20, 0x32, 0x30, 0x30, 0x37, 0x31, 0x39, 0x3a, 0x30, 0x30, 0x3a, 0x30, 0x30, 0x53, 0x43, 0x4e, 0x52, 0x5a, 0x2e, 0x2e, 0x2e, 0x2a, 0x06, 0x04, 0xb9, 0x0b, 0x02, 0x00, 0xb2, 0x19, 0xc4, 0x7e }; signal (SIGCHLD, SIG_DFL); cpid = fork (); g_assert (cpid >= 0); if (cpid == 0) { /* In the child */ qcdm_test_child (d->secondary, (GAsyncReadyCallback)qcdm_verinfo_expect_success_cb); exit (0); } /* Parent */ d->child = cpid; req_len = server_wait_request (d->main, req, sizeof (req)); g_assert (req_len == 1); g_assert_cmpint (req[0], ==, 0x00); server_send_response (d->main, rsp, sizeof (rsp)); g_assert (wait_for_child (d, 3)); } static void qcdm_verinfo_expect_fail_cb (MMPortSerialQcdm *port, GAsyncResult *res, GMainLoop *loop) { GError *error = NULL; GByteArray *response; response = mm_port_serial_qcdm_command_finish (port, res, &error); /* Expect any kind of error */ g_assert (error != NULL); g_error_free (error); g_assert (response == NULL); g_main_loop_quit (loop); } /* Test that a Sierra CnS response to a Version Info command correctly * raises an error in the child's response handler. */ static void test_sierra_cns_rejected (TestData *d) { char req[512]; gsize req_len; pid_t cpid; const char rsp[] = { 0x7e, 0x00, 0x0a, 0x6b, 0x6d, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e }; signal (SIGCHLD, SIG_DFL); cpid = fork (); g_assert (cpid >= 0); if (cpid == 0) { /* In the child */ qcdm_test_child (d->secondary, (GAsyncReadyCallback)qcdm_verinfo_expect_fail_cb); exit (0); } /* Parent */ d->child = cpid; req_len = server_wait_request (d->main, req, sizeof (req)); g_assert (req_len == 1); g_assert_cmpint (req[0], ==, 0x00); server_send_response (d->main, rsp, sizeof (rsp)); /* We expect the child to exit normally */ g_assert (wait_for_child (d, 3)); } /* Test that a random response to a Version Info command correctly * raises an error in the child's response handler. */ static void test_random_data_rejected (TestData *d) { char req[512]; gsize req_len; pid_t cpid; const char rsp[] = { 0x7e, 0x7e, 0x7e, 0x6b, 0x6d, 0x7e, 0x7e, 0x7e, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e }; signal (SIGCHLD, SIG_DFL); cpid = fork (); g_assert (cpid >= 0); if (cpid == 0) { /* In the child */ qcdm_test_child (d->secondary, (GAsyncReadyCallback)qcdm_verinfo_expect_fail_cb); exit (0); } /* Parent */ d->child = cpid; req_len = server_wait_request (d->main, req, sizeof (req)); g_assert (req_len == 1); g_assert_cmpint (req[0], ==, 0x00); server_send_response (d->main, rsp, sizeof (rsp)); /* We expect the child to exit normally */ g_assert (wait_for_child (d, 3)); } /* Test that a bunch of frame markers at the beginning of a valid response * to a Version Info command is parsed correctly. */ static void test_leading_frame_markers (TestData *d) { char req[512]; gsize req_len; pid_t cpid; const char rsp[] = { 0x7e, 0x7e, 0x7e, 0x7e, 0x7e, 0x7e, 0x7e, 0x7e, 0x7e, 0x7e, 0x7e, 0x7e, 0x00, 0x41, 0x75, 0x67, 0x20, 0x31, 0x39, 0x20, 0x32, 0x30, 0x30, 0x38, 0x32, 0x30, 0x3a, 0x34, 0x38, 0x3a, 0x34, 0x37, 0x4f, 0x63, 0x74, 0x20, 0x32, 0x39, 0x20, 0x32, 0x30, 0x30, 0x37, 0x31, 0x39, 0x3a, 0x30, 0x30, 0x3a, 0x30, 0x30, 0x53, 0x43, 0x4e, 0x52, 0x5a, 0x2e, 0x2e, 0x2e, 0x2a, 0x06, 0x04, 0xb9, 0x0b, 0x02, 0x00, 0xb2, 0x19, 0xc4, 0x7e }; signal (SIGCHLD, SIG_DFL); cpid = fork (); g_assert (cpid >= 0); if (cpid == 0) { /* In the child */ qcdm_test_child (d->secondary, (GAsyncReadyCallback)qcdm_verinfo_expect_success_cb); exit (0); } /* Parent */ d->child = cpid; req_len = server_wait_request (d->main, req, sizeof (req)); g_assert (req_len == 1); g_assert_cmpint (req[0], ==, 0x00); server_send_response (d->main, rsp, sizeof (rsp)); /* We expect the child to exit normally */ g_assert (wait_for_child (d, 3)); } static void test_pty_create (TestData *d) { struct termios stbuf; int ret, err; ret = openpty (&d->main, &d->secondary, NULL, NULL, NULL); g_assert (ret == 0); d->valid = TRUE; /* set raw mode on the secondary using kernel default parameters */ memset (&stbuf, 0, sizeof (stbuf)); tcgetattr (d->secondary, &stbuf); tcflush (d->secondary, TCIOFLUSH); cfmakeraw (&stbuf); tcsetattr (d->secondary, TCSANOW, &stbuf); fcntl (d->secondary, F_SETFL, O_NONBLOCK); fcntl (d->main, F_SETFL, O_NONBLOCK); err = qcdm_port_setup (d->main); g_assert_cmpint (err, ==, QCDM_SUCCESS); } static void test_pty_cleanup (TestData *d) { /* For some reason the cleanup function gets called more times * than the setup function does... */ if (d->valid) { if (d->child) kill (d->child, SIGKILL); if (d->main >= 0) close (d->main); if (d->secondary >= 0) close (d->secondary); memset (d, 0, sizeof (*d)); } } typedef void (*TCFunc) (TestData *, gconstpointer); #define TESTCASE_PTY(s, t) g_test_add (s, TestData, NULL, (TCFunc)test_pty_create, (TCFunc)t, (TCFunc)test_pty_cleanup); int main (int argc, char **argv) { g_test_init (&argc, &argv, NULL); TESTCASE_PTY ("/MM/QCDM/Verinfo", test_verinfo); TESTCASE_PTY ("/MM/QCDM/Sierra-Cns-Rejected", test_sierra_cns_rejected); TESTCASE_PTY ("/MM/QCDM/Random-Data-Rejected", test_random_data_rejected); TESTCASE_PTY ("/MM/QCDM/Leading-Frame-Markers", test_leading_frame_markers); return g_test_run (); } ModemManager-1.23.4-dev/src/tests/test-sms-part-3gpp-fuzzer.c000066400000000000000000000021451456466623000237410ustar00rootroot00000000000000/* -*- 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) 2023 Google, Inc. */ #include #include #include #include #define _LIBMM_INSIDE_MM #include #include "mm-sms-part-3gpp.h" int LLVMFuzzerTestOneInput (const uint8_t *data, size_t size) { g_autoptr(MMSmsPart) part = NULL; g_autoptr(GError) error = NULL; if (!size) return 0; part = mm_sms_part_3gpp_new_from_binary_pdu (0, data, size, NULL, FALSE, &error); g_assert (part || error); return 0; } ModemManager-1.23.4-dev/src/tests/test-sms-part-3gpp-tr-fuzzer.c000066400000000000000000000021441456466623000243630ustar00rootroot00000000000000/* -*- 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) 2023 Google, Inc. */ #include #include #include #include #define _LIBMM_INSIDE_MM #include #include "mm-sms-part-3gpp.h" int LLVMFuzzerTestOneInput (const uint8_t *data, size_t size) { g_autoptr(MMSmsPart) part = NULL; g_autoptr(GError) error = NULL; if (!size) return 0; part = mm_sms_part_3gpp_new_from_binary_pdu (0, data, size, NULL, TRUE, &error); g_assert (part || error); return 0; } ModemManager-1.23.4-dev/src/tests/test-sms-part-3gpp.c000066400000000000000000000676651456466623000224400ustar00rootroot00000000000000/* -*- 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. * Copyright (C) 2011 The Chromium OS Authors. */ #include #include #include #include #include #define _LIBMM_INSIDE_MM #include #include "mm-sms-part-3gpp.h" #include "mm-charsets.h" #include "mm-log-test.h" /********************* PDU PARSER TESTS *********************/ static void common_test_part_from_hexpdu (const gchar *hexpdu, const gchar *expected_smsc, const gchar *expected_number, const gchar *expected_timestamp, gboolean expected_multipart, const gchar *expected_text, const guint8 *expected_data, gsize expected_data_size) { MMSmsPart *part; GError *error = NULL; part = mm_sms_part_3gpp_new_from_pdu (0, hexpdu, NULL, &error); g_assert_no_error (error); g_assert (part != NULL); if (expected_smsc) g_assert_cmpstr (expected_smsc, ==, mm_sms_part_get_smsc (part)); if (expected_number) g_assert_cmpstr (expected_number, ==, mm_sms_part_get_number (part)); if (expected_timestamp) g_assert_cmpstr (expected_timestamp, ==, mm_sms_part_get_timestamp (part)); if (expected_text) g_assert_cmpstr (expected_text, ==, mm_sms_part_get_text (part)); g_assert_cmpuint (expected_multipart, ==, mm_sms_part_should_concat (part)); if (expected_data) { guint32 i; const GByteArray *data; data = mm_sms_part_get_data (part); g_assert_cmpuint ((guint)expected_data_size, ==, data->len); for (i = 0; i < data->len; i++) g_assert_cmpuint ((guint)data->data[i], ==, expected_data[i]); } mm_sms_part_free (part); } static void common_test_part_from_pdu (const guint8 *pdu, gsize pdu_size, const gchar *expected_smsc, const gchar *expected_number, const gchar *expected_timestamp, gboolean expected_multipart, const gchar *expected_text, const guint8 *expected_data, gsize expected_data_size) { gchar *hexpdu; hexpdu = mm_utils_bin2hexstr (pdu, pdu_size); common_test_part_from_hexpdu (hexpdu, expected_smsc, expected_number, expected_timestamp, expected_multipart, expected_text, expected_data, expected_data_size); g_free (hexpdu); } static void test_pdu1 (void) { static const guint8 pdu[] = { 0x07, 0x91, 0x21, 0x04, 0x44, 0x29, 0x61, 0xf4, 0x04, 0x0b, 0x91, 0x61, 0x71, 0x95, 0x72, 0x91, 0xf8, 0x00, 0x00, 0x11, 0x20, 0x82, 0x11, 0x05, 0x05, 0x0a, // user data: 0x6a, 0xc8, 0xb2, 0xbc, 0x7c, 0x9a, 0x83, 0xc2, 0x20, 0xf6, 0xdb, 0x7d, 0x2e, 0xcb, 0x41, 0xed, 0xf2, 0x7c, 0x1e, 0x3e, 0x97, 0x41, 0x1b, 0xde, 0x06, 0x75, 0x4f, 0xd3, 0xd1, 0xa0, 0xf9, 0xbb, 0x5d, 0x06, 0x95, 0xf1, 0xf4, 0xb2, 0x9b, 0x5c, 0x26, 0x83, 0xc6, 0xe8, 0xb0, 0x3c, 0x3c, 0xa6, 0x97, 0xe5, 0xf3, 0x4d, 0x6a, 0xe3, 0x03, 0xd1, 0xd1, 0xf2, 0xf7, 0xdd, 0x0d, 0x4a, 0xbb, 0x59, 0xa0, 0x79, 0x7d, 0x8c, 0x06, 0x85, 0xe7, 0xa0, 0x00, 0x28, 0xec, 0x26, 0x83, 0x2a, 0x96, 0x0b, 0x28, 0xec, 0x26, 0x83, 0xbe, 0x60, 0x50, 0x78, 0x0e, 0xba, 0x97, 0xd9, 0x6c, 0x17 }; common_test_part_from_pdu ( pdu, sizeof (pdu), "+12404492164", /* smsc */ "+16175927198", /* number */ "2011-02-28T11:50:50-05", /* timestamp */ FALSE, "Here's a longer message [{with some extended characters}] " "thrown in, such as £ and ΩΠΨ and §¿ as well.", /* text */ NULL, 0); } static void test_pdu2 (void) { static const guint8 pdu[] = { 0x07, 0x91, 0x97, 0x30, 0x07, 0x11, 0x11, 0xf1, 0x04, 0x14, 0xd0, 0x49, 0x37, 0xbd, 0x2c, 0x77, 0x97, 0xe9, 0xd3, 0xe6, 0x14, 0x00, 0x08, 0x11, 0x30, 0x92, 0x91, 0x02, 0x40, 0x61, 0x08, 0x04, 0x42, 0x04, 0x35, 0x04, 0x41, 0x04, 0x42}; common_test_part_from_pdu ( pdu, sizeof (pdu), "+79037011111", /* smsc */ "InternetSMS", /* number */ "2011-03-29T19:20:04+04", /* timestamp */ FALSE, "тест", /* text */ NULL, 0); } static void test_pdu3 (void) { static const guint8 pdu[] = { 0x07, 0x91, 0x21, 0x43, 0x65, 0x87, 0x09, 0xf1, 0x04, 0x0b, 0x91, 0x81, 0x00, 0x55, 0x15, 0x12, 0xf2, 0x00, 0x00, 0x11, 0x10, 0x10, 0x21, 0x43, 0x65, 0x00, 0x0a, 0xe8, 0x32, 0x9b, 0xfd, 0x46, 0x97, 0xd9, 0xec, 0x37}; common_test_part_from_pdu ( pdu, sizeof (pdu), "+12345678901", /* smsc */ "+18005551212", /* number */ "2011-01-01T12:34:56Z", /* timestamp */ FALSE, "hellohello", /* text */ NULL, 0); } static void test_pdu3_nzpid (void) { /* pid is nonzero (00 -> ff) */ static const guint8 pdu[] = { 0x07, 0x91, 0x21, 0x43, 0x65, 0x87, 0x09, 0xf1, 0x04, 0x0b, 0x91, 0x81, 0x00, 0x55, 0x15, 0x12, 0xf2, 0xff, 0x00, 0x11, 0x10, 0x10, 0x21, 0x43, 0x65, 0x00, 0x0a, 0xe8, 0x32, 0x9b, 0xfd, 0x46, 0x97, 0xd9, 0xec, 0x37}; common_test_part_from_pdu ( pdu, sizeof (pdu), "+12345678901", /* smsc */ "+18005551212", /* number */ "2011-01-01T12:34:56Z", /* timestamp */ FALSE, "hellohello", /* text */ NULL, 0); } static void test_pdu3_mms (void) { /* mms is clear (04 -> 00) */ static const guint8 pdu[] = { 0x07, 0x91, 0x21, 0x43, 0x65, 0x87, 0x09, 0xf1, 0x00, 0x0b, 0x91, 0x81, 0x00, 0x55, 0x15, 0x12, 0xf2, 0x00, 0x00, 0x11, 0x10, 0x10, 0x21, 0x43, 0x65, 0x00, 0x0a, 0xe8, 0x32, 0x9b, 0xfd, 0x46, 0x97, 0xd9, 0xec, 0x37}; common_test_part_from_pdu ( pdu, sizeof (pdu), "+12345678901", /* smsc */ "+18005551212", /* number */ "2011-01-01T12:34:56Z", /* timestamp */ FALSE, "hellohello", /* text */ NULL, 0); } static void test_pdu3_natl (void) { /* number is natl (91 -> 81) */ static const guint8 pdu[] = { 0x07, 0x91, 0x21, 0x43, 0x65, 0x87, 0x09, 0xf1, 0x04, 0x0b, 0x81, 0x81, 0x00, 0x55, 0x15, 0x12, 0xf2, 0x00, 0x00, 0x11, 0x10, 0x10, 0x21, 0x43, 0x65, 0x00, 0x0a, 0xe8, 0x32, 0x9b, 0xfd, 0x46, 0x97, 0xd9, 0xec, 0x37}; common_test_part_from_pdu ( pdu, sizeof (pdu), "+12345678901", /* smsc */ "18005551212", /* number, no plus */ "2011-01-01T12:34:56Z", /* timestamp */ FALSE, "hellohello", /* text */ NULL, 0); } static void test_pdu3_8bit (void) { static const guint8 pdu[] = { 0x07, 0x91, 0x21, 0x43, 0x65, 0x87, 0x09, 0xf1, 0x04, 0x0b, 0x91, 0x81, 0x00, 0x55, 0x15, 0x12, 0xf2, 0x00, 0x04, 0x11, 0x10, 0x10, 0x21, 0x43, 0x65, 0x00, 0x0a, 0xe8, 0x32, 0x9b, 0xfd, 0x46, 0x97, 0xd9, 0xec, 0x37, 0xde}; static const guint8 expected_data[] = { 0xe8, 0x32, 0x9b, 0xfd, 0x46, 0x97, 0xd9, 0xec, 0x37, 0xde }; common_test_part_from_pdu ( pdu, sizeof (pdu), "+12345678901", /* smsc */ "+18005551212", /* number */ "2011-01-01T12:34:56Z", /* timestamp */ FALSE, NULL, /* text */ expected_data, /* data */ sizeof (expected_data)); /* data size */ } static void test_pdu_dcsf1 (void) { /* TP-DCS coding scheme is group F */ static const guint8 pdu[] = { 0x07, // length of SMSC info 0x91, // type of address of SMSC (E.164) 0x33, 0x06, 0x09, 0x10, 0x93, 0xF0, // SMSC address (+33 60 90 01 39 0) 0x04, // SMS-DELIVER 0x04, // address length 0x85, // type of address 0x81, 0x00, // sender address (1800) 0x00, // TP-PID protocol identifier 0xF1, // TP-DCS data coding scheme 0x11, 0x60, 0x42, 0x31, 0x80, 0x51, 0x80, // timestamp 11-06-24 13:08:51 0xA0, // TP-UDL user data length (160) // Content: 0x49, 0xB7, 0xF9, 0x0D, 0x9A, 0x1A, 0xA5, 0xA0, 0x16, 0x68, 0xF8, 0x76, 0x9B, 0xD3, 0xE4, 0xB2, 0x9B, 0x9E, 0x2E, 0xB3, 0x59, 0xA0, 0x3F, 0xC8, 0x5D, 0x06, 0xA9, 0xC3, 0xED, 0x70, 0x7A, 0x0E, 0xA2, 0xCB, 0xC3, 0xEE, 0x79, 0xBB, 0x4C, 0xA7, 0xCB, 0xCB, 0xA0, 0x56, 0x43, 0x61, 0x7D, 0xA7, 0xC7, 0x69, 0x90, 0xFD, 0x4D, 0x97, 0x97, 0x41, 0xEE, 0x77, 0xDD, 0x5E, 0x0E, 0xD7, 0x41, 0xED, 0x37, 0x1D, 0x44, 0x2E, 0x83, 0xE0, 0xE1, 0xF9, 0xBC, 0x0C, 0xD2, 0x81, 0xE6, 0x77, 0xD9, 0xB8, 0x4C, 0x06, 0xC1, 0xDF, 0x75, 0x39, 0xE8, 0x5C, 0x90, 0x97, 0xE5, 0x20, 0xFB, 0x9B, 0x2E, 0x2F, 0x83, 0xC6, 0xEF, 0x36, 0x9C, 0x5E, 0x06, 0x4D, 0x8D, 0x52, 0xD0, 0xBC, 0x2E, 0x07, 0xDD, 0xEF, 0x77, 0xD7, 0xDC, 0x2C, 0x77, 0x99, 0xE5, 0xA0, 0x77, 0x1D, 0x04, 0x0F, 0xCB, 0x41, 0xF4, 0x02, 0xBB, 0x00, 0x47, 0xBF, 0xDD, 0x65, 0x50, 0xB8, 0x0E, 0xCA, 0xD9, 0x66}; common_test_part_from_pdu ( pdu, sizeof (pdu), "+33609001390", /* smsc */ "1800", /* number */ "2011-06-24T13:08:15+02", /* timestamp */ FALSE, "Info SFR - Confidentiel, à ne jamais transmettre -\r\n" "Voici votre nouveau mot de passe : sw2ced pour gérer " "votre compte SFR sur www.sfr.fr ou par téléphone au 963", /* text */ NULL, 0); } static void test_pdu_dcsf_8bit (void) { static const guint8 pdu[] = { 0x07, 0x91, 0x21, 0x43, 0x65, 0x87, 0x09, 0xf1, 0x04, 0x0b, 0x91, 0x81, 0x00, 0x55, 0x15, 0x12, 0xf2, 0x00, 0xf4, 0x11, 0x10, 0x10, 0x21, 0x43, 0x65, 0x00, 0x0a, 0xe8, 0x32, 0x9b, 0xfd, 0x46, 0x97, 0xd9, 0xec, 0x37, 0xde}; static const guint8 expected_data[] = { 0xe8, 0x32, 0x9b, 0xfd, 0x46, 0x97, 0xd9, 0xec, 0x37, 0xde }; common_test_part_from_pdu ( pdu, sizeof (pdu), "+12345678901", /* smsc */ "+18005551212", /* number */ "2011-01-01T12:34:56Z", /* timestamp */ FALSE, NULL, /* text */ expected_data, /* data */ sizeof (expected_data)); /* data size */ } static void test_pdu_udhi (void) { /* Welcome message from KPN NL */ static const gchar *hexpdu = "07911356131313F64004850120390011609232239180A006080400100201D7327BFD6EB340E232" "1BF46E83EA7790F59D1E97DBE1341B442F83C465763D3DA797E56537C81D0ECB41AB59CC1693C1" "6031D96C064241E5656838AF03A96230982A269BCD462917C8FA4E8FCBED709A0D7ABBE9F6B0FB" "5C7683D27350984D4FABC9A0B33C4C4FCF5D20EBFB2D079DCB62793DBD06D9C36E50FB2D4E97D9" "A0B49B5E96BBCB"; common_test_part_from_hexpdu ( hexpdu, "+31653131316", /* smsc */ "1002", /* number */ "2011-06-29T23:32:19+02", /* timestamp */ TRUE, "Welkom, bel om uw Voicemail te beluisteren naar +31612001233" " (PrePay: *100*1233#). Voicemail ontvangen is altijd gratis." " Voor gebruik van mobiel interne", /* text */ NULL, 0); } static void test_pdu_multipart (void) { static const gchar *hexpdu1 = "07912160130320F5440B916171056429F5000021405291650569A00500034C0201A9E8F41C949E" "83C2207B599E07B1DFEE33885E9ED341E4F23C7D7697C920FA1B54C697E5E3F4BC0C6AD7D9F434" "081E96D341E3303C2C4EB3D3F4BC0B94A483E6E8779D4D06CDD1EF3BA80E0785E7A0B7BB0C6A97" "E7F3F0B9CC02B9DF7450780EA2DFDF2C50780EA2A3CBA0BA9B5C96B3F369F71954768FDFE4B4FB" "0C9297E1F2F2BCECA6CF41"; static const gchar *hexpdu2 = "07912160130320F6440B916171056429F5000021405291651569320500034C0202E9E8301D4447" "9741F0B09C3E0785E56590BCCC0ED3CB6410FD0D7ABBCBA0B0FB4D4797E52E10"; common_test_part_from_hexpdu ( hexpdu1, "+12063130025", /* smsc */ "+16175046925", /* number */ "2012-04-25T19:56:50-04", /* timestamp */ TRUE, /* multipart! */ "This is a very long test designed to exercise multi part capability. It should " "show up as one message, not as two, as the underlying encoding represents ", /* text */ NULL, 0); common_test_part_from_hexpdu ( hexpdu2, "+12063130026", /* smsc */ "+16175046925", /* number */ "2012-04-25T19:56:51-04", /* timestamp */ TRUE, /* multipart! */ "that the parts are related to one another. ", /* text */ NULL, 0); } static void test_pdu_stored_by_us (void) { /* This is a SUBMIT PDU! */ static const gchar *hexpdu1 = "002100098136397339F70008224F60597D4F60597D4F60597D4F60597D4F60597D4F60597D4F60597D4F60597D4F60"; common_test_part_from_hexpdu ( hexpdu1, NULL, /* smsc */ "639337937", /* number */ NULL, /* timestamp */ FALSE, /* multipart! */ "你好你好你好你好你好你好你好你好你", /* text */ NULL, 0); } static void test_pdu_not_stored (void) { static const gchar *hexpdu1 = "07914356060013F1065A098136397339F7219011700463802190117004638030"; common_test_part_from_hexpdu ( hexpdu1, "+34656000311", /* smsc */ "639337937", /* number */ "2012-09-11T07:40:36+02", /* timestamp */ FALSE, /* multipart! */ NULL, /* text */ NULL, 0); } static void common_test_invalid_pdu (const guint8 *pdu, gsize pdu_len) { g_autoptr(GError) error = NULL; MMSmsPart *part; part = mm_sms_part_3gpp_new_from_binary_pdu (0, pdu, pdu_len, NULL, FALSE, &error); g_assert (part == NULL); /* We don't care about the specific error type */ g_assert (error != NULL); } static void test_pdu_insufficient_data (void) { static const guint8 pdu[] = { 0x07, 0x91, 0x21, 0x43, 0x65, 0x87, 0x09, 0xf1, 0x04, 0x0b, 0x91, 0x81, 0x00, 0x55, 0x15, 0x12, 0xf2, 0x00, 0x00, 0x11, 0x10, 0x10, 0x21, 0x43, 0x65, 0x00, 0x0b, 0xe8, 0x32, 0x9b, 0xfd, 0x46, 0x97, 0xd9, 0xec, 0x37 }; common_test_invalid_pdu (pdu, G_N_ELEMENTS (pdu)); } static void test_pdu_no_address (void) { static const guint8 pdu[] = { 0x00, 0x0A, 0xBF, 0x00 }; common_test_invalid_pdu (pdu, G_N_ELEMENTS (pdu)); } static void test_pdu_wrong_address_size (void) { static const guint8 pdu[] = { 0x00, 0x1C, 0x01, 0x1C }; common_test_invalid_pdu (pdu, G_N_ELEMENTS (pdu)); } static void test_pdu_wrong_user_data_elements_size (void) { static const guint8 pdu[] = { 0x00, 0x41, 0x00, 0x01, 0x01, 0x00, 0x01, 0x4B, 0x00, 0x00, 0x2E }; common_test_invalid_pdu (pdu, G_N_ELEMENTS (pdu)); } static void test_pdu_wrong_udh (void) { static const guint8 pdu[] = { 0x00, 0xF1, 0x01, 0x01, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00 }; common_test_invalid_pdu (pdu, G_N_ELEMENTS (pdu)); } /********************* SMS ADDRESS ENCODER TESTS *********************/ static void common_test_address_encode (const gchar *address, gboolean smsc, const guint8 *expected, gsize expected_size) { guint8 buf[20]; gsize enclen; enclen = mm_sms_part_3gpp_encode_address (address, buf, sizeof (buf), smsc); g_assert_cmpuint (enclen, ==, expected_size); g_assert_cmpint (memcmp (buf, expected, expected_size), ==, 0); } static void test_address_encode_smsc_intl (void) { static const gchar *addr = "+19037029920"; static const guint8 expected[] = { 0x07, 0x91, 0x91, 0x30, 0x07, 0x92, 0x29, 0xF0 }; common_test_address_encode (addr, TRUE, expected, sizeof (expected)); } static void test_address_encode_smsc_unknown (void) { static const char *addr = "9037029920"; static const guint8 expected[] = { 0x06, 0x81, 0x09, 0x73, 0x20, 0x99, 0x02 }; common_test_address_encode (addr, TRUE, expected, sizeof (expected)); } static void test_address_encode_intl (void) { static const char *addr = "+19037029920"; static const guint8 expected[] = { 0x0B, 0x91, 0x91, 0x30, 0x07, 0x92, 0x29, 0xF0 }; common_test_address_encode (addr, FALSE, expected, sizeof (expected)); } static void test_address_encode_unknown (void) { static const char *addr = "9037029920"; static const guint8 expected[] = { 0x0A, 0x81, 0x09, 0x73, 0x20, 0x99, 0x02 }; common_test_address_encode (addr, FALSE, expected, sizeof (expected)); } /********************* PDU CREATOR TESTS *********************/ static void trace_pdu (const guint8 *pdu, guint len) { guint i; g_print ("n "); for (i = 0; i < len; i++) { g_print (" 0x%02X", pdu[i]); if (((i + 1) % 12) == 0) g_print ("n "); } g_print ("n"); } static void common_test_create_pdu (const gchar *smsc, const gchar *number, const gchar *text, guint validity, gint class, const guint8 *expected, gsize expected_size, guint expected_msgstart) { MMSmsPart *part; guint8 *pdu; guint len = 0, msgstart = 0; GError *error = NULL; part = mm_sms_part_new (0, MM_SMS_PDU_TYPE_SUBMIT); if (smsc) mm_sms_part_set_smsc (part, smsc); if (number) mm_sms_part_set_number (part, number); if (text) { gchar **out; MMSmsEncoding encoding = MM_SMS_ENCODING_UNKNOWN; MMModemCharset charset = MM_MODEM_CHARSET_UNKNOWN; /* Detect best encoding */ out = mm_charset_util_split_text (text, &charset, NULL); if (out) encoding = (charset == MM_MODEM_CHARSET_GSM) ? MM_SMS_ENCODING_GSM7 : MM_SMS_ENCODING_UCS2; g_strfreev (out); mm_sms_part_set_text (part, text); mm_sms_part_set_encoding (part, encoding); } if (validity > 0) mm_sms_part_set_validity_relative (part, validity); if (class >= 0) mm_sms_part_set_class (part, class); pdu = mm_sms_part_3gpp_get_submit_pdu (part, &len, &msgstart, NULL, &error); mm_sms_part_free (part); if (g_test_verbose ()) trace_pdu (pdu, len); g_assert_no_error (error); g_assert (pdu != NULL); g_assert_cmpmem (pdu, len, expected, expected_size); g_assert_cmpint (msgstart, ==, expected_msgstart); g_free (pdu); } static void test_create_pdu_ucs2_with_smsc (void) { static const char *smsc = "+19037029920"; static const char *number = "+15555551234"; static const char *text = "Да здравствует король, детка!"; static const guint8 expected[] = { 0x07, 0x91, 0x91, 0x30, 0x07, 0x92, 0x29, 0xF0, 0x11, 0x00, 0x0B, 0x91, 0x51, 0x55, 0x55, 0x15, 0x32, 0xF4, 0x00, 0x08, 0x00, 0x3A, 0x04, 0x14, 0x04, 0x30, 0x00, 0x20, 0x04, 0x37, 0x04, 0x34, 0x04, 0x40, 0x04, 0x30, 0x04, 0x32, 0x04, 0x41, 0x04, 0x42, 0x04, 0x32, 0x04, 0x43, 0x04, 0x35, 0x04, 0x42, 0x00, 0x20, 0x04, 0x3A, 0x04, 0x3E, 0x04, 0x40, 0x04, 0x3E, 0x04, 0x3B, 0x04, 0x4C, 0x00, 0x2C, 0x00, 0x20, 0x04, 0x34, 0x04, 0x35, 0x04, 0x42, 0x04, 0x3A, 0x04, 0x30, 0x00, 0x21 }; common_test_create_pdu (smsc, number, text, 5, /* validity */ -1, /* class */ expected, sizeof (expected), 8); /* expected_msgstart */ } static void test_create_pdu_ucs2_no_smsc (void) { static const char *number = "+15555551234"; static const char *text = "Да здравствует король, детка!"; static const guint8 expected[] = { 0x00, 0x11, 0x00, 0x0B, 0x91, 0x51, 0x55, 0x55, 0x15, 0x32, 0xF4, 0x00, 0x08, 0x00, 0x3A, 0x04, 0x14, 0x04, 0x30, 0x00, 0x20, 0x04, 0x37, 0x04, 0x34, 0x04, 0x40, 0x04, 0x30, 0x04, 0x32, 0x04, 0x41, 0x04, 0x42, 0x04, 0x32, 0x04, 0x43, 0x04, 0x35, 0x04, 0x42, 0x00, 0x20, 0x04, 0x3A, 0x04, 0x3E, 0x04, 0x40, 0x04, 0x3E, 0x04, 0x3B, 0x04, 0x4C, 0x00, 0x2C, 0x00, 0x20, 0x04, 0x34, 0x04, 0x35, 0x04, 0x42, 0x04, 0x3A, 0x04, 0x30, 0x00, 0x21 }; common_test_create_pdu (NULL, /* smsc */ number, text, 5, /* validity */ -1, /* class */ expected, sizeof (expected), 1); /* expected_msgstart */ } static void test_create_pdu_gsm_with_smsc (void) { static const char *smsc = "+19037029920"; static const char *number = "+15555551234"; static const char *text = "Hi there...Tue 17th Jan 2012 05:30.18 pm (GMT+1) ΔΔΔΔΔ"; static const guint8 expected[] = { 0x07, 0x91, 0x91, 0x30, 0x07, 0x92, 0x29, 0xF0, 0x11, 0x00, 0x0B, 0x91, 0x51, 0x55, 0x55, 0x15, 0x32, 0xF4, 0x00, 0x00, 0x00, 0x36, 0xC8, 0x34, 0x88, 0x8E, 0x2E, 0xCB, 0xCB, 0x2E, 0x97, 0x8B, 0x5A, 0x2F, 0x83, 0x62, 0x37, 0x3A, 0x1A, 0xA4, 0x0C, 0xBB, 0x41, 0x32, 0x58, 0x4C, 0x06, 0x82, 0xD5, 0x74, 0x33, 0x98, 0x2B, 0x86, 0x03, 0xC1, 0xDB, 0x20, 0xD4, 0xB1, 0x49, 0x5D, 0xC5, 0x52, 0x20, 0x08, 0x04, 0x02, 0x81, 0x00 }; common_test_create_pdu (smsc, number, text, 5, /* validity */ -1, /* class */ expected, sizeof (expected), 8); /* expected_msgstart */ } static void test_create_pdu_gsm_no_smsc (void) { static const char *number = "+15555551234"; static const char *text = "Hi there...Tue 17th Jan 2012 05:30.18 pm (GMT+1) ΔΔΔΔΔ"; static const guint8 expected[] = { 0x00, 0x11, 0x00, 0x0B, 0x91, 0x51, 0x55, 0x55, 0x15, 0x32, 0xF4, 0x00, 0x00, 0x00, 0x36, 0xC8, 0x34, 0x88, 0x8E, 0x2E, 0xCB, 0xCB, 0x2E, 0x97, 0x8B, 0x5A, 0x2F, 0x83, 0x62, 0x37, 0x3A, 0x1A, 0xA4, 0x0C, 0xBB, 0x41, 0x32, 0x58, 0x4C, 0x06, 0x82, 0xD5, 0x74, 0x33, 0x98, 0x2B, 0x86, 0x03, 0xC1, 0xDB, 0x20, 0xD4, 0xB1, 0x49, 0x5D, 0xC5, 0x52, 0x20, 0x08, 0x04, 0x02, 0x81, 0x00 }; common_test_create_pdu (NULL, /* smsc */ number, text, 5, /* validity */ -1, /* class */ expected, sizeof (expected), 1); /* expected_msgstart */ } static void test_create_pdu_gsm_3 (void) { static const char *number = "+15556661234"; static const char *text = "This is really cool ΔΔΔΔΔ"; static const guint8 expected[] = { 0x00, 0x11, 0x00, 0x0B, 0x91, 0x51, 0x55, 0x66, 0x16, 0x32, 0xF4, 0x00, 0x00, 0x00, 0x19, 0x54, 0x74, 0x7A, 0x0E, 0x4A, 0xCF, 0x41, 0xF2, 0x72, 0x98, 0xCD, 0xCE, 0x83, 0xC6, 0xEF, 0x37, 0x1B, 0x04, 0x81, 0x40, 0x20, 0x10 }; /* Tests that a 25-character message (where the last septet is packed into * an octet by itself) is created correctly. Previous to * "core: fix some bugs in GSM7 packing code" the GSM packing code would * leave off the last octet. */ common_test_create_pdu (NULL, /* smsc */ number, text, 5, /* validity */ -1, /* class */ expected, sizeof (expected), 1); /* expected_msgstart */ } static void test_create_pdu_gsm_no_validity (void) { static const char *number = "+15556661234"; static const char *text = "This is really cool ΔΔΔΔΔ"; static const guint8 expected[] = { 0x00, 0x01, 0x00, 0x0B, 0x91, 0x51, 0x55, 0x66, 0x16, 0x32, 0xF4, 0x00, 0x00, 0x19, 0x54, 0x74, 0x7A, 0x0E, 0x4A, 0xCF, 0x41, 0xF2, 0x72, 0x98, 0xCD, 0xCE, 0x83, 0xC6, 0xEF, 0x37, 0x1B, 0x04, 0x81, 0x40, 0x20, 0x10 }; common_test_create_pdu (NULL, /* smsc */ number, text, 0, /* validity */ -1, /* class */ expected, sizeof (expected), 1); /* expected_msgstart */ } /************************************************************/ int main (int argc, char **argv) { setlocale (LC_ALL, ""); g_test_init (&argc, &argv, NULL); g_test_add_func ("/MM/SMS/3GPP/PDU-Parser/pdu1", test_pdu1); g_test_add_func ("/MM/SMS/3GPP/PDU-Parser/pdu2", test_pdu2); g_test_add_func ("/MM/SMS/3GPP/PDU-Parser/pdu3", test_pdu3); g_test_add_func ("/MM/SMS/3GPP/PDU-Parser/pdu3-nonzero-pid", test_pdu3_nzpid); g_test_add_func ("/MM/SMS/3GPP/PDU-Parser/pdu3-mms", test_pdu3_mms); g_test_add_func ("/MM/SMS/3GPP/PDU-Parser/pdu3-natl", test_pdu3_natl); g_test_add_func ("/MM/SMS/3GPP/PDU-Parser/pdu3-8bit", test_pdu3_8bit); g_test_add_func ("/MM/SMS/3GPP/PDU-Parser/pdu-dcsf1", test_pdu_dcsf1); g_test_add_func ("/MM/SMS/3GPP/PDU-Parser/pdu-dcsf-8bit", test_pdu_dcsf_8bit); g_test_add_func ("/MM/SMS/3GPP/PDU-Parser/pdu-udhi", test_pdu_udhi); g_test_add_func ("/MM/SMS/3GPP/PDU-Parser/pdu-multipart", test_pdu_multipart); g_test_add_func ("/MM/SMS/3GPP/PDU-Parser/pdu-stored-by-us", test_pdu_stored_by_us); g_test_add_func ("/MM/SMS/3GPP/PDU-Parser/pdu-not-stored", test_pdu_not_stored); g_test_add_func ("/MM/SMS/3GPP/PDU-Parser/pdu-insufficient-data", test_pdu_insufficient_data); g_test_add_func ("/MM/SMS/3GPP/PDU-Parser/pdu-no-address", test_pdu_no_address); g_test_add_func ("/MM/SMS/3GPP/PDU-Parser/pdu-wrong-address-size", test_pdu_wrong_address_size); g_test_add_func ("/MM/SMS/3GPP/PDU-Parser/pdu-wrong-user-data-elements-size", test_pdu_wrong_user_data_elements_size); g_test_add_func ("/MM/SMS/3GPP/PDU-Parser/pdu-wrong-udh", test_pdu_wrong_udh); g_test_add_func ("/MM/SMS/3GPP/Address-Encoder/smsc-intl", test_address_encode_smsc_intl); g_test_add_func ("/MM/SMS/3GPP/Address-Encoder/smsc-unknown", test_address_encode_smsc_unknown); g_test_add_func ("/MM/SMS/3GPP/Address-Encoder/intl", test_address_encode_intl); g_test_add_func ("/MM/SMS/3GPP/Address-Encoder/unknown", test_address_encode_unknown); g_test_add_func ("/MM/SMS/3GPP/PDU-Creator/UCS2-with-smsc", test_create_pdu_ucs2_with_smsc); g_test_add_func ("/MM/SMS/3GPP/PDU-Creator/UCS2-no-smsc", test_create_pdu_ucs2_no_smsc); g_test_add_func ("/MM/SMS/3GPP/PDU-Creator/GSM-with-smsc", test_create_pdu_gsm_with_smsc); g_test_add_func ("/MM/SMS/3GPP/PDU-Creator/GSM-no-smsc", test_create_pdu_gsm_no_smsc); g_test_add_func ("/MM/SMS/3GPP/PDU-Creator/GSM-3", test_create_pdu_gsm_3); g_test_add_func ("/MM/SMS/3GPP/PDU-Creator/GSM-no-validity", test_create_pdu_gsm_no_validity); return g_test_run (); } ModemManager-1.23.4-dev/src/tests/test-sms-part-cdma-fuzzer.c000066400000000000000000000021361456466623000237740ustar00rootroot00000000000000/* -*- 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) 2023 Google, Inc. */ #include #include #include #include #define _LIBMM_INSIDE_MM #include #include "mm-sms-part-cdma.h" int LLVMFuzzerTestOneInput (const uint8_t *data, size_t size) { g_autoptr(MMSmsPart) part = NULL; g_autoptr(GError) error = NULL; if (!size) return 0; part = mm_sms_part_cdma_new_from_binary_pdu (0, data, size, NULL, &error); g_assert (part || error); return 0; } ModemManager-1.23.4-dev/src/tests/test-sms-part-cdma.c000066400000000000000000000454301456466623000224550ustar00rootroot00000000000000/* -*- 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) 2013 Google, Inc. */ #include #include #include #include #include #define _LIBMM_INSIDE_MM #include #include "mm-sms-part-cdma.h" #include "mm-log-test.h" /********************* PDU PARSER TESTS *********************/ static void common_test_part_from_hexpdu (const gchar *hexpdu, MMSmsCdmaTeleserviceId expected_teleservice_id, MMSmsCdmaServiceCategory expected_service_category, const gchar *expected_address, guint8 expected_bearer_reply_option, const gchar *expected_text) { MMSmsPart *part; GError *error = NULL; part = mm_sms_part_cdma_new_from_pdu (0, hexpdu, NULL, &error); g_assert_no_error (error); g_assert (part != NULL); if (expected_teleservice_id != MM_SMS_CDMA_TELESERVICE_ID_UNKNOWN) g_assert_cmpuint (expected_teleservice_id, ==, mm_sms_part_get_cdma_teleservice_id (part)); if (expected_service_category != MM_SMS_CDMA_SERVICE_CATEGORY_UNKNOWN) g_assert_cmpuint (expected_service_category, ==, mm_sms_part_get_cdma_service_category (part)); if (expected_address) { if (expected_address[0]) g_assert_cmpstr (expected_address, ==, mm_sms_part_get_number (part)); else g_assert (mm_sms_part_get_number (part) == NULL); } if (expected_bearer_reply_option) g_assert_cmpuint (expected_bearer_reply_option, ==, mm_sms_part_get_message_reference (part)); if (expected_text) g_assert_cmpstr (expected_text, ==, mm_sms_part_get_text (part)); mm_sms_part_free (part); } static void common_test_part_from_pdu (const guint8 *pdu, gsize pdu_size, MMSmsCdmaTeleserviceId expected_teleservice_id, MMSmsCdmaServiceCategory expected_service_category, const gchar *expected_address, guint8 expected_bearer_reply_option, const gchar *expected_text) { gchar *hexpdu; hexpdu = mm_utils_bin2hexstr (pdu, pdu_size); common_test_part_from_hexpdu (hexpdu, expected_teleservice_id, expected_service_category, expected_address, expected_bearer_reply_option, expected_text); g_free (hexpdu); } static void common_test_invalid_part_from_hexpdu (const gchar *hexpdu) { MMSmsPart *part; GError *error = NULL; part = mm_sms_part_cdma_new_from_pdu (0, hexpdu, NULL, &error); g_assert (part == NULL); /* We don't care for the specific error type */ g_assert (error != NULL); g_error_free (error); } static void common_test_invalid_part_from_pdu (const guint8 *pdu, gsize pdu_size) { gchar *hexpdu; hexpdu = mm_utils_bin2hexstr (pdu, pdu_size); common_test_invalid_part_from_hexpdu (hexpdu); g_free (hexpdu); } static void common_test_valid_part_from_hexpdu (const gchar *hexpdu) { g_autoptr(MMSmsPart) part = NULL; GError *error = NULL; part = mm_sms_part_cdma_new_from_pdu (0, hexpdu, NULL, &error); g_assert (part != NULL); g_assert (error == NULL); } static void common_test_valid_part_from_pdu (const guint8 *pdu, gsize pdu_size) { g_autofree gchar *hexpdu = NULL; hexpdu = mm_utils_bin2hexstr (pdu, pdu_size); common_test_valid_part_from_hexpdu (hexpdu); } static void test_pdu1 (void) { static const guint8 pdu[] = { /* message type */ 0x00, /* teleservice id */ 0x00, 0x02, 0x10, 0x02, /* originating address */ 0x02, 0x07, 0x02, 0x8C, 0xE9, 0x5D, 0xCC, 0x65, 0x80, /* bearer reply option */ 0x06, 0x01, 0xFC, /* bearer data */ 0x08, 0x15, 0x00, 0x03, 0x16, 0x8D, 0x30, 0x01, 0x06, 0x10, 0x24, 0x18, 0x30, 0x60, 0x80, 0x03, 0x06, 0x10, 0x10, 0x04, 0x04, 0x48, 0x47 }; common_test_part_from_pdu ( pdu, sizeof (pdu), MM_SMS_CDMA_TELESERVICE_ID_WMT, MM_SMS_CDMA_SERVICE_CATEGORY_UNKNOWN, "3305773196", 63, "AAAA"); } static void test_invalid_parameter_length (void) { static const guint8 pdu[] = { /* message type */ 0x00, /* teleservice id */ 0x00, 0x02, 0x10, 0x02, /* originating address */ 0x02, 0x07, 0x02, 0x8C, 0xE9, 0x5D, 0xCC, 0x65, 0x80, /* bearer reply option */ 0x06, 0x01, 0xFC, /* bearer data */ 0x08, 0x20, /* wrong parameter length! */ 0x00, 0x03, 0x16, 0x8D, 0x30, 0x01, 0x06, 0x10, 0x24, 0x18, 0x30, 0x60, 0x80, 0x03, 0x06, 0x10, 0x10, 0x04, 0x04, 0x48, 0x47 }; common_test_invalid_part_from_pdu (pdu, sizeof (pdu)); } static void test_invalid_address_length (void) { static const guint8 pdu[] = { /* message type */ 0x00, /* teleservice id */ 0x00, 0x02, 0x10, 0x02, /* originating address (wrong num_fields) */ 0x02, 0x07, 0x03, 0x8C, 0xE9, 0x5D, 0xCC, 0x65, 0x80, /* bearer reply option */ 0x06, 0x01, 0xFC, /* bearer data */ 0x08, 0x15, 0x00, 0x03, 0x16, 0x8D, 0x30, 0x01, 0x06, 0x10, 0x24, 0x18, 0x30, 0x60, 0x80, 0x03, 0x06, 0x10, 0x10, 0x04, 0x04, 0x48, 0x47 }; common_test_part_from_pdu ( pdu, sizeof (pdu), MM_SMS_CDMA_TELESERVICE_ID_WMT, MM_SMS_CDMA_SERVICE_CATEGORY_UNKNOWN, "", 63, NULL); } static void test_created_by_us (void) { static const guint8 pdu[] = { /* message type */ 0x00, /* teleservice id */ 0x00, 0x02, 0x10, 0x02, /* destination address */ 0x04, 0x07, 0x02, 0x8C, 0xE9, 0x5D, 0xCC, 0x65, 0x80, /* bearer data */ 0x08, 0x0D, 0x00, 0x03, 0x20, 0x00, 0x00, /* message id */ 0x01, 0x06, 0x10, 0x24, 0x18, 0x30, 0x60, 0x80 /* user_data */ }; common_test_part_from_pdu ( pdu, sizeof (pdu), MM_SMS_CDMA_TELESERVICE_ID_WMT, MM_SMS_CDMA_SERVICE_CATEGORY_UNKNOWN, "3305773196", 0, "AAAA"); } static void test_latin_encoding (void) { static const guint8 pdu[] = { /* message type */ 0x00, /* teleservice id */ 0x00, 0x02, 0x10, 0x02, /* originating address */ 0x02, 0x07, 0x02, 0x8C, 0xE9, 0x5D, 0xCC, 0x65, 0x80, /* bearer reply option */ 0x06, 0x01, 0xFC, /* bearer data */ 0x08, 0x39, /* message id */ 0x00, 0x03, 0x13, 0x8D, 0x20, /* user data */ 0x01, 0x27, 0x41, 0x29, 0x19, 0x22, 0xE1, 0x19, 0x1A, 0xE1, 0x1A, 0x01, 0x19, 0xA1, 0x19, 0xA1, 0xA9, 0xB1, 0xB9, 0xE9, 0x53, 0x4B, 0x23, 0xAB, 0x53, 0x23, 0xAB, 0x23, 0x2B, 0xAB, 0xAB, 0x2B, 0x23, 0xAB, 0x53, 0x23, 0x2B, 0xAB, 0x53, 0xAB, 0x20, /* message center timestamp */ 0x03, 0x06, 0x13, 0x10, 0x23, 0x20, 0x06, 0x37, /* priority indicator */ 0x08, 0x01, 0x00 }; common_test_part_from_pdu ( pdu, sizeof (pdu), MM_SMS_CDMA_TELESERVICE_ID_WMT, MM_SMS_CDMA_SERVICE_CATEGORY_UNKNOWN, "3305773196", 63, /* this is ASCII-7 but message uses latin encoding */ "#$\\##\\#@#4#4567=*idujdudeuuedujdeujud"); } static void test_latin_encoding_2 (void) { static const guint8 pdu[] = { /* message type */ 0x00, /* teleservice id */ 0x00, 0x02, 0x10, 0x02, /* originating address */ 0x02, 0x07, 0x02, 0x8C, 0xE9, 0x5D, 0xCC, 0x65, 0x80, /* bearer reply option */ 0x06, 0x01, 0xFC, /* bearer data */ 0x08, 0x1C, /* message id */ 0x00, 0x03, 0x13, 0x8D, 0x20, /* user data */ 0x01, 0x0A, 0x40, 0x42, 0x1B, 0x0B, 0x6B, 0x83, 0x2F, 0x9B, 0x71, 0x08, /* message center timestamp */ 0x03, 0x06, 0x13, 0x10, 0x23, 0x20, 0x06, 0x37, /* priority indicator */ 0x08, 0x01, 0x00 }; common_test_part_from_pdu ( pdu, sizeof (pdu), MM_SMS_CDMA_TELESERVICE_ID_WMT, MM_SMS_CDMA_SERVICE_CATEGORY_UNKNOWN, "3305773196", 63, /* this is latin and message uses latin encoding */ "Campeón!"); } static void test_unicode_encoding (void) { static const guint8 pdu[] = { /* message type */ 0x00, /* teleservice id */ 0x00, 0x02, 0x10, 0x02, /* originating address */ 0x02, 0x07, 0x02, 0x8C, 0xE9, 0x5D, 0xCC, 0x65, 0x80, /* bearer reply option */ 0x06, 0x01, 0xFC, /* bearer data */ 0x08, 0x28, /* message id */ 0x00, 0x03, 0x1B, 0x73, 0xF0, /* user data */ 0x01, 0x16, 0x20, 0x52, 0x71, 0x6A, 0xB8, 0x5A, 0xA7, 0x92, 0xDB, 0xC3, 0x37, 0xC4, 0xB7, 0xDA, 0xDA, 0x82, 0x98, 0xB4, 0x50, 0x42, 0x94, 0x18, /* message center timestamp */ 0x03, 0x06, 0x13, 0x10, 0x24, 0x10, 0x45, 0x28, /* priority indicator */ 0x08, 0x01, 0x00 }; common_test_part_from_pdu ( pdu, sizeof (pdu), MM_SMS_CDMA_TELESERVICE_ID_WMT, MM_SMS_CDMA_SERVICE_CATEGORY_UNKNOWN, "3305773196", 63, "中國哲學書電子化計劃"); } static void test_empty_unicode_user_data (void) { static const guint8 pdu[] = { 0x01, 0x08, 0x2f, 0x03, 0x01, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x01, 0x02, 0x08, 0x00, 0x01, 0x02, 0x00, 0x00, 0x01, 0x02, 0x20, 0x01, 0x02, 0x00, 0x00, 0x00, 0x47, 0x06, 0x01, 0x02, 0x00, 0x06, 0x08, 0x08, 0x05, 0x01, 0x06, 0x01, 0x6d, 0x38, 0x00, 0x03, 0x05, 0x01, 0x06, 0x01, 0x02, 0x00, 0x06, 0x08, 0x08, 0x05, 0x01, 0x00, 0x00, 0x00, 0x47, 0x06, 0x01, 0x02, 0x00, 0x06, 0x08, 0x08, 0x05, 0x01, 0x06, 0x01, 0x6d, 0x38, 0x00, 0x03, 0x05, 0x01, 0x06, 0x01, 0x02, 0x00, 0x06, 0x08, 0x08, 0x05, 0x01, 0x06, 0x06, 0x00, 0x01, 0x00, 0x34, 0x00, 0x03, 0x05, 0x29, 0x08, 0x08, 0x05, 0x01, 0xb6, 0x01, 0x38, 0x00, 0x02, 0x02, 0x00 }; /* also invalid this one */ common_test_invalid_part_from_pdu (pdu, sizeof (pdu)); } static void test_empty_ascii_user_data (void) { static const guint8 pdu[] = { 0x00, 0x08, 0x08, 0x01, 0x06, 0x10, 0x34, 0x00, 0x00, 0x01, 0x00 }; /* valid but don't care about exact details */ common_test_valid_part_from_pdu (pdu, sizeof (pdu)); } static void test_invalid_ascii_user_data (void) { static const guint8 pdu[] = { 0x0, 0x8, 0x4, 0x1, 0x2, 0x10, 0xe }; /* valid but don't care about exact details */ common_test_valid_part_from_pdu (pdu, sizeof (pdu)); } /********************* PDU CREATOR TESTS *********************/ static void trace_pdu (const guint8 *pdu, guint len) { guint i; g_print ("n "); for (i = 0; i < len; i++) { g_print (" 0x%02X", pdu[i]); if (((i + 1) % 12) == 0) g_print ("n "); } g_print ("n"); } static void common_test_create_pdu (MMSmsCdmaTeleserviceId teleservice_id, const gchar *number, const gchar *text, const guint8 *data, gsize data_size, const guint8 *expected, gsize expected_size) { MMSmsPart *part; guint8 *pdu; guint len = 0; GError *error = NULL; g_assert (number != NULL); part = mm_sms_part_new (0, MM_SMS_PDU_TYPE_CDMA_SUBMIT); mm_sms_part_set_cdma_teleservice_id (part, teleservice_id); mm_sms_part_set_number (part, number); if (text) mm_sms_part_set_text (part, text); else { GByteArray *data_bytearray; data_bytearray = g_byte_array_sized_new (data_size); g_byte_array_append (data_bytearray, data, data_size); mm_sms_part_take_data (part, data_bytearray); } pdu = mm_sms_part_cdma_get_submit_pdu (part, &len, NULL, &error); mm_sms_part_free (part); if (g_test_verbose ()) trace_pdu (pdu, len); g_assert_no_error (error); g_assert (pdu != NULL); g_assert_cmpuint (len, ==, expected_size); g_assert_cmpint (memcmp (pdu, expected, len), ==, 0); g_free (pdu); } static void test_create_pdu_text_ascii_encoding (void) { static const char *number = "3305773196"; static const char *text = "AAAA"; static const guint8 expected[] = { /* message type */ 0x00, /* teleservice id */ 0x00, 0x02, 0x10, 0x02, /* destination address */ 0x04, 0x07, 0x02, 0x8C, 0xE9, 0x5D, 0xCC, 0x65, 0x80, /* bearer data */ 0x08, 0x0D, 0x00, 0x03, 0x20, 0x00, 0x00, /* message id */ 0x01, 0x06, 0x10, 0x24, 0x18, 0x30, 0x60, 0x80 /* user_data */ }; common_test_create_pdu (MM_SMS_CDMA_TELESERVICE_ID_WMT, number, text, NULL, 0, expected, sizeof (expected)); } static void test_create_pdu_text_latin_encoding (void) { static const char *number = "3305773196"; static const char *text = "Campeón!"; static const guint8 expected[] = { /* message type */ 0x00, /* teleservice id */ 0x00, 0x02, 0x10, 0x02, /* destination address */ 0x04, 0x07, 0x02, 0x8C, 0xE9, 0x5D, 0xCC, 0x65, 0x80, /* bearer data */ 0x08, 0x11, /* message id */ 0x00, 0x03, 0x20, 0x00, 0x00, /* user data */ 0x01, 0x0A, 0x40, 0x42, 0x1B, 0x0B, 0x6B, 0x83, 0x2F, 0x9B, 0x71, 0x08 }; common_test_create_pdu (MM_SMS_CDMA_TELESERVICE_ID_WMT, number, text, NULL, 0, expected, sizeof (expected)); } static void test_create_pdu_text_unicode_encoding (void) { static const char *number = "3305773196"; static const char *text = "中國哲學書電子化計劃"; static const guint8 expected[] = { /* message type */ 0x00, /* teleservice id */ 0x00, 0x02, 0x10, 0x02, /* destination address */ 0x04, 0x07, 0x02, 0x8C, 0xE9, 0x5D, 0xCC, 0x65, 0x80, /* bearer data */ 0x08, 0x1D, /* message id */ 0x00, 0x03, 0x20, 0x00, 0x00, /* user data */ 0x01, 0x16, 0x20, 0x52, 0x71, 0x6A, 0xB8, 0x5A, 0xA7, 0x92, 0xDB, 0xC3, 0x37, 0xC4, 0xB7, 0xDA, 0xDA, 0x82, 0x98, 0xB4, 0x50, 0x42, 0x94, 0x18 }; common_test_create_pdu (MM_SMS_CDMA_TELESERVICE_ID_WMT, number, text, NULL, 0, expected, sizeof (expected)); } static void test_create_parse_pdu_text_ascii_encoding (void) { #define MAX_TEXT_LEN 100 guint i; gchar text[MAX_TEXT_LEN + 1]; memset (text, 0, sizeof (text)); for (i = 0; i < MAX_TEXT_LEN; i++) { MMSmsPart *part; guint8 *pdu; guint len = 0; GError *error = NULL; text[i]='A'; part = mm_sms_part_new (0, MM_SMS_PDU_TYPE_CDMA_SUBMIT); mm_sms_part_set_cdma_teleservice_id (part, MM_SMS_CDMA_TELESERVICE_ID_WMT); mm_sms_part_set_number (part, "123456789"); mm_sms_part_set_text (part, text); pdu = mm_sms_part_cdma_get_submit_pdu (part, &len, NULL, &error); g_assert_no_error (error); g_assert (pdu != NULL); mm_sms_part_free (part); part = mm_sms_part_cdma_new_from_binary_pdu (0, pdu, len, NULL, &error); g_assert_no_error (error); g_assert (part != NULL); g_assert_cmpuint (MM_SMS_CDMA_TELESERVICE_ID_WMT, ==, mm_sms_part_get_cdma_teleservice_id (part)); g_assert_cmpstr ("123456789", ==, mm_sms_part_get_number (part)); g_assert_cmpstr (text, ==, mm_sms_part_get_text (part)); mm_sms_part_free (part); g_free (pdu); } } /************************************************************/ int main (int argc, char **argv) { setlocale (LC_ALL, ""); g_test_init (&argc, &argv, NULL); g_test_add_func ("/MM/SMS/CDMA/PDU-Parser/pdu1", test_pdu1); g_test_add_func ("/MM/SMS/CDMA/PDU-Parser/invalid-parameter-length", test_invalid_parameter_length); g_test_add_func ("/MM/SMS/CDMA/PDU-Parser/invalid-address-length", test_invalid_address_length); g_test_add_func ("/MM/SMS/CDMA/PDU-Parser/created-by-us", test_created_by_us); g_test_add_func ("/MM/SMS/CDMA/PDU-Parser/latin-encoding", test_latin_encoding); g_test_add_func ("/MM/SMS/CDMA/PDU-Parser/latin-encoding-2", test_latin_encoding_2); g_test_add_func ("/MM/SMS/CDMA/PDU-Parser/unicode-encoding", test_unicode_encoding); g_test_add_func ("/MM/SMS/CDMA/PDU-Parser/empty-unicode-user-data", test_empty_unicode_user_data); g_test_add_func ("/MM/SMS/CDMA/PDU-Parser/empty-ascii-user-data", test_empty_ascii_user_data); g_test_add_func ("/MM/SMS/CDMA/PDU-Parser/invalid-ascii-user-data", test_invalid_ascii_user_data); g_test_add_func ("/MM/SMS/CDMA/PDU-Creator/ascii-encoding", test_create_pdu_text_ascii_encoding); g_test_add_func ("/MM/SMS/CDMA/PDU-Creator/latin-encoding", test_create_pdu_text_latin_encoding); g_test_add_func ("/MM/SMS/CDMA/PDU-Creator/unicode-encoding", test_create_pdu_text_unicode_encoding); g_test_add_func ("/MM/SMS/CDMA/PDU-Creator-Parser/ascii-encoding", test_create_parse_pdu_text_ascii_encoding); return g_test_run (); } ModemManager-1.23.4-dev/src/tests/test-udev-rules.c000066400000000000000000000027451456466623000221020ustar00rootroot00000000000000/* -*- 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) 2016 Aleksander Morgado */ #include #include #include #include #include #define _LIBMM_INSIDE_MM #include #include "mm-kernel-device-generic-rules.h" #include "mm-log-test.h" /************************************************************/ static void test_load_cleanup_core (void) { GArray *rules; GError *error = NULL; rules = mm_kernel_device_generic_rules_load (TESTUDEVRULESDIR, &error); g_assert_no_error (error); g_assert (rules); g_assert (rules->len > 0); g_array_unref (rules); } /************************************************************/ int main (int argc, char **argv) { setlocale (LC_ALL, ""); g_test_init (&argc, &argv, NULL); g_test_add_func ("/MM/test-udev-rules/load-cleanup-core", test_load_cleanup_core); return g_test_run (); } ModemManager-1.23.4-dev/test/000077500000000000000000000000001456466623000157045ustar00rootroot00000000000000ModemManager-1.23.4-dev/test/lsudev.c000066400000000000000000000121221456466623000173500ustar00rootroot00000000000000/* -*- 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 #include #include #include #include #include #include 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, ...) __attribute__((__format__ (__printf__, 2, 3))); static void println (guint indent, const char *fmt, ...) { va_list args; GString *output; guint i; g_return_if_fail (fmt != NULL); output = g_string_sized_new (250); for (i = 0; i < indent; i++) g_string_append_c (output, ' '); va_start (args, fmt); g_string_append_vprintf (output, fmt, args); va_end (args); g_print ("%s\n", output->str); g_string_free (output, TRUE); } 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: %" G_GUINT64_FORMAT, 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); } 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; } 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; } ModemManager-1.23.4-dev/test/meson.build000066400000000000000000000007361456466623000200540ustar00rootroot00000000000000# SPDX-License-Identifier: GPL-2.0-or-later # Copyright (C) 2021 Iñigo Martinez test_units = { 'mmrules': libkerneldevice_dep, 'mmsmsmonitor': libhelpers_dep, 'mmsmspdu': libhelpers_dep, 'mmtty': libport_dep, } if enable_udev test_units += {'lsudev': gudev_dep} endif foreach test_unit, test_deps: test_units executable( test_unit, test_unit + '.c', include_directories: top_inc, dependencies: test_deps, ) endforeach ModemManager-1.23.4-dev/test/mmcli-test-sms000077500000000000000000000104141456466623000205100ustar00rootroot00000000000000#!/bin/sh print_usage () { echo "usage: $0 [MODEM INDEX] [all|ucs2|gsm7|data] [NUMBER]" echo " if no [NUMBER] is given, a placeholder one will be used and no SMS will be sent" } test () { TITLE=$1 TEXT=$2 DATAFILE=$3 echo echo echo "=============================================================" echo "$TITLE" echo "=============================================================" echo # Build SMS properties PROPERTIES="number='$NUMBER'" if [ "x$TEXT" != "x" ]; then PROPERTIES=$PROPERTIES",text='$TEXT'" fi # Create the SMS first, get DBus path returned echo "Creating SMS..." CMD="mmcli -m $MODEM_INDEX --messaging-create-sms=\"$PROPERTIES\"" if [ "x$DATAFILE" != "x" ]; then CMD=$CMD" --messaging-create-sms-with-data=$DATAFILE" fi echo "$> $CMD" OUT=`eval $CMD` SMS_PATH=`echo "$OUT" | grep "/org/freedesktop/ModemManager1/SMS" | awk 'BEGIN { FS = " " } ; { print $1 }'` if [ "x$SMS_PATH" = "x" ]; then echo "error: couldn't create SMS" 1>&2 exit 2 else echo "SMS created:" mmcli -s $SMS_PATH echo fi echo "Storing SMS..." CMD="mmcli -s $SMS_PATH --store" echo "$> $CMD" OUT=`eval $CMD` STORED=`echo "$OUT" | grep "success"` if [ "x$STORED" = "x" ]; then echo "error: couldn't store the SMS" 1>&2 exit 2 else echo "SMS stored:" mmcli -s $SMS_PATH echo fi if [ "x$DONOTSEND" = "x" ]; then echo "Sending SMS..." CMD="mmcli -s $SMS_PATH --send" echo "$> $CMD" OUT=`eval $CMD` SENT=`echo "$OUT" | grep "success"` if [ "x$SENT" = "x" ]; then echo "error: couldn't send the SMS" 1>&2 exit 2 else echo "SMS sent:" mmcli -s $SMS_PATH echo fi fi # Delete the SMS now echo "Deleting SMS..." CMD="mmcli -m $MODEM_INDEX --messaging-delete-sms=$SMS_PATH" echo "$> $CMD" OUT=`eval $CMD` DELETED=`echo "$OUT" | grep "success"` if [ "x$DELETED" = "x" ]; then echo "error: couldn't delete the SMS" 1>&2 exit 2 else echo "SMS deleted" fi } if [ $# -ge 2 ]; then MODEM_INDEX=$1 COMMAND=$2 fi if [ $# -eq 2 ]; then DONOTSEND=1 NUMBER=123456 elif [ $# -eq 3 ]; then NUMBER=$3 else echo "error: missing arguments" 1>&2 print_usage exit 255 fi # Process commands case $COMMAND in "all") RUN_UCS2=1 RUN_GSM7=1 RUN_DATA=1 ;; "ucs2") RUN_UCS2=1 ;; "gsm7") RUN_GSM7=1 ;; "data") RUN_DATA=1 ;; *) echo "error: unexpected command '$COMMAND'" 1>&2 print_usage exit 255 ;; esac # # UCS2 tests # if [ "x$RUN_UCS2" != "x" ]; then test "Testing singlepart SMS with UCS2 text..." "你好你好你好你" "" test "Testing multipart SMS with UCS2 text..." "你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好好" "" fi # # GSM7 tests # if [ "x$RUN_GSM7" != "x" ]; then test "Testing singlepart SMS with GSM7 text..." "Hello world" "" test "Testing multipart SMS with GSM7 text..." "01234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890" "" fi # # Data tests # if [ "x$RUN_DATA" != "x" ]; then TESTFILE_SHORT=/tmp/`basename $0`-140 TESTFILE_LONG=/tmp/`basename $0`-200 echo if [ -f $TESTFILE_SHORT ]; then echo "Re-using previously created 140-byte testfile at $TESTFILE_SHORT" else echo "Creating testfile at $TESTFILE_SHORT" dd if=/dev/random of=$TESTFILE_SHORT bs=1 count=140 fi if [ -f $TESTFILE_LONG ]; then echo "Re-using previously created 200-byte testfile at $TESTFILE_LONG" else echo "Creating testfile at $TESTFILE_LONG" dd if=/dev/random of=$TESTFILE_LONG bs=1 count=200 fi test "Testing singlepart SMS with data..." "" "$TESTFILE_SHORT" test "Testing multipart SMS with data..." "" "$TESTFILE_LONG" fi ModemManager-1.23.4-dev/test/mmrules.c000066400000000000000000000110351456466623000175340ustar00rootroot00000000000000/* -*- 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) 2015 Aleksander Morgado */ #include "config.h" #include #include #include #include #include #include #include #include #define PROGRAM_NAME "mmrules" #define PROGRAM_VERSION PACKAGE_VERSION /* Context */ static gchar *path; static gboolean verbose_flag; static gboolean version_flag; static GOptionEntry main_entries[] = { { "path", 'p', 0, G_OPTION_ARG_FILENAME, &path, "Specify path to udev rules directory", "[PATH]" }, { "verbose", 'v', 0, G_OPTION_ARG_NONE, &verbose_flag, "Run action with verbose logs", NULL }, { "version", 'V', 0, G_OPTION_ARG_NONE, &version_flag, "Print version", NULL }, { NULL } }; static void print_version_and_exit (void) { g_print ("\n" PROGRAM_NAME " " PROGRAM_VERSION "\n" "Copyright (2015) Aleksander Morgado\n" "License GPLv2+: GNU GPL version 2 or later \n" "This is free software: you are free to change and redistribute it.\n" "There is NO WARRANTY, to the extent permitted by law.\n" "\n"); exit (EXIT_SUCCESS); } static void print_rule (MMUdevRule *rule) { /* Process conditions */ if (rule->conditions) { guint i; for (i = 0; i < rule->conditions->len; i++) { MMUdevRuleMatch *rule_match; rule_match = &g_array_index (rule->conditions, MMUdevRuleMatch, i); switch (rule_match->type) { case MM_UDEV_RULE_MATCH_TYPE_EQUAL: g_print (" [condition %u] %s == %s\n", i, rule_match->parameter, rule_match->value); break; case MM_UDEV_RULE_MATCH_TYPE_NOT_EQUAL: g_print (" [condition %u] %s != %s\n", i, rule_match->parameter, rule_match->value); break; case MM_UDEV_RULE_MATCH_TYPE_UNKNOWN: default: g_assert_not_reached (); } } } /* Process result */ switch (rule->result.type) { case MM_UDEV_RULE_RESULT_TYPE_LABEL: g_print (" [result] label %s\n", rule->result.content.tag); break; case MM_UDEV_RULE_RESULT_TYPE_GOTO_INDEX: g_print (" [result] jump to rule %u\n", rule->result.content.index); break; case MM_UDEV_RULE_RESULT_TYPE_PROPERTY: g_print (" [result] set property %s = %s\n", rule->result.content.property.name, rule->result.content.property.value); break; case MM_UDEV_RULE_RESULT_TYPE_GOTO_TAG: case MM_UDEV_RULE_RESULT_TYPE_UNKNOWN: default: g_assert_not_reached (); } } int main (int argc, char **argv) { GOptionContext *context; GArray *rules; guint i; GError *error = NULL; setlocale (LC_ALL, ""); /* Setup option context, process it and destroy it */ context = g_option_context_new ("- ModemManager udev rules testing"); g_option_context_add_main_entries (context, main_entries, NULL); g_option_context_parse (context, &argc, &argv, NULL); g_option_context_free (context); if (version_flag) print_version_and_exit (); /* No device path given? */ if (!path) { g_printerr ("error: no path specified\n"); exit (EXIT_FAILURE); } /* Load rules from directory */ rules = mm_kernel_device_generic_rules_load (path, &error); if (!rules) { g_printerr ("error: couldn't load rules: %s", error->message); exit (EXIT_FAILURE); } /* Print loaded rules */ for (i = 0; i < rules->len; i++) { g_print ("-----------------------------------------\n"); g_print ("rule [%u]:\n", i); print_rule (&g_array_index (rules, MMUdevRule, i)); } g_array_unref (rules); return EXIT_SUCCESS; } ModemManager-1.23.4-dev/test/mmsmsmonitor.c000066400000000000000000000140371456466623000206210ustar00rootroot00000000000000/* -*- 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) 2019 Aleksander Morgado */ #include "config.h" #include #include #include #include #include #include #include #define PROGRAM_NAME "mmsmsmonitor" #define PROGRAM_VERSION PACKAGE_VERSION /* Globals */ static GMainLoop *loop; static GList *monitored_sms_list; /* Context */ static gboolean version_flag; static GOptionEntry main_entries[] = { { "version", 'V', 0, G_OPTION_ARG_NONE, &version_flag, "Print version", NULL }, { NULL } }; static gboolean signals_handler (void) { if (loop && g_main_loop_is_running (loop)) { g_printerr ("%s\n", "cancelling the main loop...\n"); g_main_loop_quit (loop); } return TRUE; } static void print_version_and_exit (void) { g_print ("\n" PROGRAM_NAME " " PROGRAM_VERSION "\n" "Copyright (2019) Aleksander Morgado\n" "License GPLv2+: GNU GPL version 2 or later \n" "This is free software: you are free to change and redistribute it.\n" "There is NO WARRANTY, to the extent permitted by law.\n" "\n"); exit (EXIT_SUCCESS); } static void sms_state_updated (MMSms *sms) { g_print ("[%s] sms updated: %s\n", mm_sms_get_path (sms), mm_sms_state_get_string (mm_sms_get_state (sms))); } static gboolean sms_added (MMModemMessaging *modem_messaging, const gchar *sms_path, gboolean received) { GList *sms_list; GList *l; MMSms *new_sms = NULL; sms_list = mm_modem_messaging_list_sync (modem_messaging, NULL, NULL); for (l = sms_list; l && !new_sms; l = g_list_next (l)) { MMSms *l_sms = MM_SMS (l->data); if (g_strcmp0 (mm_sms_get_path (l_sms), sms_path) == 0) new_sms = l_sms; } g_assert (new_sms); g_print ("[%s] new sms: %s\n", mm_sms_get_path (new_sms), mm_sms_state_get_string (mm_sms_get_state (new_sms))); g_signal_connect (new_sms, "notify::state", G_CALLBACK (sms_state_updated), NULL); monitored_sms_list = g_list_append (monitored_sms_list, g_object_ref (new_sms)); g_list_free_full (sms_list, g_object_unref); return TRUE; } static void list_all_sms_found (MMModemMessaging *modem_messaging) { GList *sms_list; GList *l; sms_list = mm_modem_messaging_list_sync (modem_messaging, NULL, NULL); for (l = sms_list; l; l = g_list_next (l)) { MMSms *l_sms = MM_SMS (l->data); g_print ("[%s] sms found: %s\n", mm_sms_get_path (l_sms), mm_sms_state_get_string (mm_sms_get_state (l_sms))); g_signal_connect (l_sms, "notify::state", G_CALLBACK (sms_state_updated), NULL); monitored_sms_list = g_list_append (monitored_sms_list, g_object_ref (l_sms)); } g_list_free_full (sms_list, g_object_unref); } int main (int argc, char **argv) { GOptionContext *context; GDBusConnection *connection; MMManager *manager; GList *modem_list; GList *l; gchar *name_owner; GError *error = NULL; setlocale (LC_ALL, ""); /* Setup option context, process it and destroy it */ context = g_option_context_new ("- ModemManager SMS monitor"); g_option_context_add_main_entries (context, main_entries, NULL); g_option_context_parse (context, &argc, &argv, NULL); g_option_context_free (context); if (version_flag) print_version_and_exit (); /* Setup dbus connection to use */ connection = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, &error); if (!connection) { g_printerr ("error: couldn't get bus: %s\n", error ? error->message : "unknown error"); exit (EXIT_FAILURE); } manager = mm_manager_new_sync (connection, G_DBUS_OBJECT_MANAGER_CLIENT_FLAGS_DO_NOT_AUTO_START, NULL, &error); if (!manager) { g_printerr ("error: couldn't create manager: %s\n", error ? error->message : "unknown error"); exit (EXIT_FAILURE); } name_owner = g_dbus_object_manager_client_get_name_owner (G_DBUS_OBJECT_MANAGER_CLIENT (manager)); if (!name_owner) { g_printerr ("error: couldn't find the ModemManager process in the bus\n"); exit (EXIT_FAILURE); } g_free (name_owner); modem_list = g_dbus_object_manager_get_objects (G_DBUS_OBJECT_MANAGER (manager)); for (l = modem_list; l; l = g_list_next (l)) { MMObject *obj; MMModemMessaging *modem_messaging; obj = MM_OBJECT (l->data); modem_messaging = MM_MODEM_MESSAGING (mm_object_peek_modem_messaging (obj)); g_signal_connect (modem_messaging, "added", G_CALLBACK (sms_added), NULL); list_all_sms_found (modem_messaging); } g_unix_signal_add (SIGINT, (GSourceFunc) signals_handler, NULL); g_unix_signal_add (SIGHUP, (GSourceFunc) signals_handler, NULL); g_unix_signal_add (SIGTERM, (GSourceFunc) signals_handler, NULL); /* Setup main loop and shedule start in idle */ loop = g_main_loop_new (NULL, FALSE); g_main_loop_run (loop); /* Cleanup */ g_main_loop_unref (loop); g_list_free_full (modem_list, g_object_unref); g_object_unref (manager); return EXIT_SUCCESS; } ModemManager-1.23.4-dev/test/mmsmspdu.c000066400000000000000000000164651456466623000177310ustar00rootroot00000000000000/* -*- 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) 2019 Aleksander Morgado */ #include "config.h" #include #include #include #include #include #include #include #define _LIBMM_INSIDE_MM #include #include "mm-log.h" #include "mm-sms-part-3gpp.h" #define PROGRAM_NAME "mmsmspdu" #define PROGRAM_VERSION PACKAGE_VERSION /* Context */ static gchar *pdu; static gboolean verbose_flag; static gboolean version_flag; static GOptionEntry main_entries[] = { { "pdu", 'p', 0, G_OPTION_ARG_STRING, &pdu, "PDU contents", "[0123456789ABCDEF..]" }, { "verbose", 'v', 0, G_OPTION_ARG_NONE, &verbose_flag, "Run action with verbose logs", NULL }, { "version", 'V', 0, G_OPTION_ARG_NONE, &version_flag, "Print version", NULL }, { NULL } }; static void show_part_info (MMSmsPart *part) { MMSmsPduType pdu_type; const gchar *smsc; const gchar *number; const gchar *timestamp; const gchar *text; MMSmsEncoding encoding; gint class; guint validity_relative; gboolean delivery_report_request; guint concat_reference; guint concat_max; guint concat_sequence; const GByteArray *data; pdu_type = mm_sms_part_get_pdu_type (part); g_print ("pdu type: %s\n", mm_sms_pdu_type_get_string (pdu_type)); smsc = mm_sms_part_get_smsc (part); g_print ("smsc: %s\n", smsc ? smsc : "n/a"); number = mm_sms_part_get_number (part); g_print ("number: %s\n", number ? number : "n/a"); timestamp = mm_sms_part_get_timestamp (part); g_print ("timestamp: %s\n", timestamp ? timestamp : "n/a"); encoding = mm_sms_part_get_encoding (part); switch (encoding) { case MM_SMS_ENCODING_GSM7: g_print ("encoding: GSM7\n"); break; case MM_SMS_ENCODING_UCS2: g_print ("encoding: UCS2\n"); break; case MM_SMS_ENCODING_8BIT: g_print ("encoding: 8BIT\n"); break; case MM_SMS_ENCODING_UNKNOWN: default: g_print ("encoding: unknown (0x%x)\n", encoding); break; } text = mm_sms_part_get_text (part); g_print ("text: %s\n", text ? text : "n/a"); data = mm_sms_part_get_data (part); if (data) { gchar *data_str; data_str = mm_utils_bin2hexstr (data->data, data->len); g_print ("data: %s\n", data_str); g_free (data_str); } else g_print ("data: n/a\n"); class = mm_sms_part_get_class (part); if (class != -1) g_print ("class: %d\n", class); else g_print ("class: n/a\n"); validity_relative = mm_sms_part_get_validity_relative (part); if (validity_relative != 0) g_print ("validity relative: %d\n", validity_relative); else g_print ("validity relative: n/a\n"); delivery_report_request = mm_sms_part_get_delivery_report_request (part); g_print ("delivery report request: %s\n", delivery_report_request ? "yes" : "no"); concat_reference = mm_sms_part_get_concat_reference (part); g_print ("concat reference: %d\n", concat_reference); concat_max = mm_sms_part_get_concat_max (part); g_print ("concat max: %d\n", concat_max); concat_sequence = mm_sms_part_get_concat_sequence (part); g_print ("concat sequence: %d\n", concat_sequence); if (mm_sms_part_get_pdu_type (part) == MM_SMS_PDU_TYPE_STATUS_REPORT) { const gchar *discharge_timestamp; guint message_reference; guint delivery_state; message_reference = mm_sms_part_get_message_reference (part); g_print ("message reference: %d\n", message_reference); discharge_timestamp = mm_sms_part_get_discharge_timestamp (part); g_print ("discharge timestamp: %s\n", discharge_timestamp ? discharge_timestamp : "n/a"); delivery_state = mm_sms_part_get_delivery_state (part); g_print ("delivery state: %s\n", mm_sms_delivery_state_get_string_extended (delivery_state)); } if (MM_SMS_PART_IS_CDMA (part)) { MMSmsCdmaTeleserviceId teleservice_id; MMSmsCdmaServiceCategory service_category; teleservice_id = mm_sms_part_get_cdma_teleservice_id (part); g_print ("teleservice id: %s\n", mm_sms_cdma_teleservice_id_get_string (teleservice_id)); service_category = mm_sms_part_get_cdma_service_category (part); g_print ("service category: %s\n", mm_sms_cdma_service_category_get_string (service_category)); } } static void print_version_and_exit (void) { g_print ("\n" PROGRAM_NAME " " PROGRAM_VERSION "\n" "Copyright (2019) Aleksander Morgado\n" "License GPLv2+: GNU GPL version 2 or later \n" "This is free software: you are free to change and redistribute it.\n" "There is NO WARRANTY, to the extent permitted by law.\n" "\n"); exit (EXIT_SUCCESS); } void _mm_log (gpointer obj, const gchar *module, const gchar *loc, const gchar *func, MMLogLevel level, const gchar *fmt, ...) { va_list args; g_autofree gchar *msg = NULL; const gchar *level_str = NULL; if (!verbose_flag) return; switch (level) { case MM_LOG_LEVEL_DEBUG: level_str = "debug"; break; case MM_LOG_LEVEL_WARN: level_str = "warning"; break; case MM_LOG_LEVEL_MSG: level_str = "message"; break; case MM_LOG_LEVEL_INFO: level_str = "info"; break; case MM_LOG_LEVEL_ERR: level_str = "error"; break; default: break; } va_start (args, fmt); msg = g_strdup_vprintf (fmt, args); va_end (args); g_print ("[%s] %s\n", level_str ? level_str : "unknown", msg); } int main (int argc, char **argv) { GOptionContext *context; GError *error = NULL; MMSmsPart *part; setlocale (LC_ALL, ""); /* Setup option context, process it and destroy it */ context = g_option_context_new ("- ModemManager SMS PDU parser"); g_option_context_add_main_entries (context, main_entries, NULL); g_option_context_parse (context, &argc, &argv, NULL); g_option_context_free (context); if (version_flag) print_version_and_exit (); /* No pdu given? */ if (!pdu) { g_printerr ("error: no PDU specified\n"); exit (EXIT_FAILURE); } part = mm_sms_part_3gpp_new_from_pdu (0, pdu, NULL, &error); if (!part) { g_printerr ("error: couldn't parse PDU: %s\n", error->message); exit (EXIT_FAILURE); } show_part_info (part); mm_sms_part_free (part); g_free (pdu); return EXIT_SUCCESS; } ModemManager-1.23.4-dev/test/mmtty.c000066400000000000000000000230721456466623000172260ustar00rootroot00000000000000/* -*- 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) 2015 Aleksander Morgado */ #include "config.h" #include #include #include #include #include #include #include #include #include #include #define PROGRAM_NAME "mmtty" #define PROGRAM_VERSION PACKAGE_VERSION /* Globals */ static MMPortSerialAt *port; static GMainLoop *loop; static GIOChannel *input; static guint input_watch_id; /* Context */ static gchar *device_str; static gboolean no_flash_flag; static gboolean no_echo_removal_flag; static gboolean spew_control_flag; static gint64 send_delay = -1; static gboolean send_lf_flag; static gboolean verbose_flag; static gboolean version_flag; static GOptionEntry main_entries[] = { { "device", 'd', 0, G_OPTION_ARG_STRING, &device_str, "Specify device path", "[PATH]" }, { "no-flash", 0, 0, G_OPTION_ARG_NONE, &no_flash_flag, "Avoid flashing the port while opening", NULL }, { "no-echo-removal", 0, 0, G_OPTION_ARG_NONE, &no_echo_removal_flag, "Avoid logic to remove echo", NULL }, { "spew-control", 0, 0, G_OPTION_ARG_NONE, &spew_control_flag, "Enable spew control logic", NULL }, { "send-delay", 0, 0, G_OPTION_ARG_INT64, &send_delay, "Send delay for each byte in microseconds (default=1000)", "[DELAY]" }, { "send-lf", 0, 0, G_OPTION_ARG_NONE, &send_lf_flag, "Send (default only)", NULL }, { "verbose", 'v', 0, G_OPTION_ARG_NONE, &verbose_flag, "Run action with verbose logs", NULL }, { "version", 'V', 0, G_OPTION_ARG_NONE, &version_flag, "Print version", NULL }, { NULL } }; static void signals_handler (int signum) { if (loop && g_main_loop_is_running (loop)) { g_printerr ("%s\n", "cancelling the main loop...\n"); g_main_loop_quit (loop); } } static void print_version_and_exit (void) { g_print ("\n" PROGRAM_NAME " " PROGRAM_VERSION "\n" "Copyright (2015) Aleksander Morgado\n" "License GPLv2+: GNU GPL version 2 or later \n" "This is free software: you are free to change and redistribute it.\n" "There is NO WARRANTY, to the extent permitted by law.\n" "\n"); exit (EXIT_SUCCESS); } static void at_command_ready (MMPortSerialAt *serial_at, GAsyncResult *res) { g_autofree gchar *response = NULL; GError *error = NULL; response = mm_port_serial_at_command_finish (serial_at, res, &error); if (response) g_print ("%s\n", response); if (error) { g_printerr ("%s\n", error->message); g_error_free (error); } g_print ("> "); } static gboolean input_callback (GIOChannel *channel, GIOCondition condition) { GError *error = NULL; GIOStatus status; gchar *line = NULL; status = g_io_channel_read_line (channel, &line, NULL, NULL, &error); switch (status) { case G_IO_STATUS_NORMAL: { gsize line_len = 0; /* remove \r\n before running as AT command */ line_len = strlen (line); while (line_len > 0 && (line[line_len - 1] == '\r' || line[line_len - 1] == '\n')) { line[line_len - 1] = '\0'; line_len--; } mm_port_serial_at_command (port, line, 60, FALSE, FALSE, NULL, (GAsyncReadyCallback) at_command_ready, NULL); g_free (line); return TRUE; } case G_IO_STATUS_ERROR: g_printerr ("error: %s\n", error->message); g_error_free (error); return FALSE; case G_IO_STATUS_EOF: g_warning ("error: No input data available"); return TRUE; case G_IO_STATUS_AGAIN: return TRUE; default: g_assert_not_reached (); } return FALSE; } static void flash_ready (MMPortSerial *serial, GAsyncResult *res) { GError *error = NULL; if (!mm_port_serial_flash_finish (serial, res, &error)) { g_printerr ("error: cannot flash serial port: %s\n", error->message); exit (EXIT_FAILURE); } g_print ("ready\n"); g_print ("> "); /* Setup input reading */ input = g_io_channel_unix_new (STDIN_FILENO); input_watch_id = g_io_add_watch (input, G_IO_IN, (GIOFunc) input_callback, NULL); } static void serial_buffer_full (void) { g_printerr ("error: serial buffer full\n"); } static gboolean start_cb (void) { GError *error = NULL; const gchar *device_name; device_name = device_str; if (g_str_has_prefix (device_name, "/dev/")) device_name += strlen ("/dev/"); g_print ("creating AT capable serial port for device '%s'...\n", device_name); port = mm_port_serial_at_new (device_name, MM_PORT_SUBSYS_TTY); /* Setup send delay */ if (send_delay >= 0) { if (send_delay > 0) g_print ("updating send delay to %" G_GINT64_FORMAT "us...\n", send_delay); else g_print ("disabling send delay...\n"); g_object_set (port, MM_PORT_SERIAL_SEND_DELAY, send_delay, NULL); } /* Setup echo removal */ if (no_echo_removal_flag) { g_print ("disabling echo removal...\n"); g_object_set (port, MM_PORT_SERIAL_AT_REMOVE_ECHO, FALSE, NULL); } /* Setup spew control */ if (spew_control_flag) { g_print ("enabling spew control...\n"); g_object_set (port, MM_PORT_SERIAL_SPEW_CONTROL, TRUE, NULL); } /* Setup LF */ if (send_lf_flag) { g_print ("enabling LF...\n"); g_object_set (port, MM_PORT_SERIAL_AT_SEND_LF, TRUE, NULL); } /* Set common response parser */ mm_port_serial_at_set_response_parser (MM_PORT_SERIAL_AT (port), mm_serial_parser_v1_parse, mm_serial_parser_v1_new (), mm_serial_parser_v1_destroy); /* Try to open the port... */ g_print ("opening serial port...\n"); if (!mm_port_serial_open (MM_PORT_SERIAL (port), &error)) { g_printerr ("error: cannot open serial port: %s\n", error->message); exit (EXIT_FAILURE); } /* Warn on full buffer by default */ g_signal_connect (port, "buffer-full", G_CALLBACK (serial_buffer_full), NULL); /* If we set FLASH_OK to FALSE, the flashing operation does nothing */ if (no_flash_flag) { g_print ("disabling serial port flash...\n"); g_object_set (port, MM_PORT_SERIAL_FLASH_OK, FALSE, NULL); } else g_print ("flashing serial port...\n"); mm_port_serial_flash (MM_PORT_SERIAL (port), 100, FALSE, (GAsyncReadyCallback) flash_ready, NULL); return G_SOURCE_REMOVE; } void _mm_log (gpointer obj, const gchar *module, const gchar *loc, const gchar *func, MMLogLevel level, const gchar *fmt, ...) { va_list args; g_autofree gchar *msg = NULL; const gchar *level_str = NULL; if (!verbose_flag) return; switch (level) { case MM_LOG_LEVEL_MSG: level_str = "message"; break; case MM_LOG_LEVEL_DEBUG: level_str = "debug"; break; case MM_LOG_LEVEL_WARN: level_str = "warning"; break; case MM_LOG_LEVEL_INFO: level_str = "info"; break; case MM_LOG_LEVEL_ERR: level_str = "error"; break; default: break; } va_start (args, fmt); msg = g_strdup_vprintf (fmt, args); va_end (args); g_print ("[%s] %s\n", level_str ? level_str : "unknown", msg); } int main (int argc, char **argv) { GOptionContext *context; setlocale (LC_ALL, ""); /* Setup option context, process it and destroy it */ context = g_option_context_new ("- ModemManager TTY testing"); g_option_context_add_main_entries (context, main_entries, NULL); g_option_context_parse (context, &argc, &argv, NULL); g_option_context_free (context); if (version_flag) print_version_and_exit (); /* No device path given? */ if (!device_str) { g_printerr ("error: no device path specified\n"); exit (EXIT_FAILURE); } /* Setup signals */ signal (SIGINT, signals_handler); signal (SIGHUP, signals_handler); signal (SIGTERM, signals_handler); /* Setup main loop and shedule start in idle */ loop = g_main_loop_new (NULL, FALSE); g_idle_add ((GSourceFunc)start_cb, NULL); g_main_loop_run (loop); /* Cleanup */ g_main_loop_unref (loop); if (port) { if (mm_port_serial_is_open (MM_PORT_SERIAL (port))) mm_port_serial_close (MM_PORT_SERIAL (port)); g_object_unref (port); } if (input) g_io_channel_unref (input); return 0; } ModemManager-1.23.4-dev/tools/000077500000000000000000000000001456466623000160655ustar00rootroot00000000000000ModemManager-1.23.4-dev/tools/test-modemmanager-service.py000077500000000000000000000411171456466623000235150ustar00rootroot00000000000000#!/usr/bin/env python3 # -*- Mode: python; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- from __future__ import print_function import gi gi.require_version('ModemManager', '1.0') from gi.repository import GLib, ModemManager import argparse import sys import dbus import dbus.service import dbus.mainloop.glib import random import collections mainloop = GLib.MainLoop() ######################################################### IFACE_DBUS = 'org.freedesktop.DBus' class UnknownInterfaceException(dbus.DBusException): def __init__(self, *args, **kwargs): self._dbus_error_name = '{}.UnknownInterface'.format(IFACE_DBUS) super().__init__(*args, **kwargs) class UnknownPropertyException(dbus.DBusException): def __init__(self, *args, **kwargs): self._dbus_error_name = '{}.UnknownProperty'.format(IFACE_DBUS) super().__init__(*args, **kwargs) class MobileEquipmentException(dbus.DBusException): _dbus_error_name = '{}.Error.MobileEquipment'.format(IFACE_DBUS) def __init__(self, *args, **kwargs): equipment_error_num = kwargs.pop('equipment_error', None) if equipment_error_num is not None: equipment_error_except = ModemManager.MobileEquipmentError(equipment_error_num) self._dbus_error_name = '{}.Error.MobileEquipment.{}'.format(IFACE_DBUS, equipment_error_except.value_nick) super().__init__(*args, **kwargs) def log(msg): if log_file: try: log_file.write(msg + "\n") log_file.flush() except Exception: pass else: print(msg) def to_path_array(src): array = dbus.Array([], signature=dbus.Signature('o')) for o in src: array.append(to_path(o)) return array def to_path(src): if src: return dbus.ObjectPath(src.path) return dbus.ObjectPath("/") class ExportedObj(dbus.service.Object): DBusInterface = collections.namedtuple('DBusInterface', ['dbus_iface', 'get_props_func', 'set_props_func', 'prop_changed_func']) def __init__(self, bus, object_path): super(ExportedObj, self).__init__(bus, object_path) self._bus = bus self.path = object_path self.__ensure_dbus_ifaces() log("Will add object with path '%s' to object manager" % object_path) object_manager.add_object(self) def __ensure_dbus_ifaces(self): if not hasattr(self, '_ExportedObj__dbus_ifaces'): self.__dbus_ifaces = {} def add_dbus_interface(self, dbus_iface, get_props_func, set_props_func, prop_changed_func): self.__ensure_dbus_ifaces() self.__dbus_ifaces[dbus_iface] = ExportedObj.DBusInterface(dbus_iface, get_props_func, set_props_func, prop_changed_func) def __dbus_interface_get(self, dbus_iface): if dbus_iface not in self.__dbus_ifaces: raise UnknownInterfaceException() return self.__dbus_ifaces[dbus_iface] def _dbus_property_get(self, dbus_iface, propname=None): props = self.__dbus_interface_get(dbus_iface).get_props_func() if propname is None: return props if propname not in props: raise UnknownPropertyException() return props[propname] def _dbus_property_set(self, dbus_iface, propname, value): props = self.__dbus_interface_get(dbus_iface).get_props_func() try: if props[propname] == value: return except KeyError: raise UnknownPropertyException() if self.__dbus_interface_get(dbus_iface).set_props_func is not None: self.__dbus_interface_get(dbus_iface).set_props_func(propname, value) self._dbus_property_notify(dbus_iface, propname) def _dbus_property_notify(self, dbus_iface, propname): prop = self._dbus_property_get(dbus_iface, propname) self.__dbus_interface_get(dbus_iface).prop_changed_func(self, {propname: prop}) ExportedObj.PropertiesChanged(self, dbus_iface, {propname: prop}, []) @dbus.service.signal(dbus.PROPERTIES_IFACE, signature='sa{sv}as') def PropertiesChanged(self, iface, changed, invalidated): pass @dbus.service.method(dbus_interface=dbus.PROPERTIES_IFACE, in_signature='s', out_signature='a{sv}') def GetAll(self, dbus_iface): return self._dbus_property_get(dbus_iface) @dbus.service.method(dbus_interface=dbus.PROPERTIES_IFACE, in_signature='ss', out_signature='v') def Get(self, dbus_iface, name): return self._dbus_property_get(dbus_iface, name) @dbus.service.method(dbus_interface=dbus.PROPERTIES_IFACE, in_signature='ssv', out_signature='') def Set(self, dbus_iface, name, value): return self._dbus_property_set(dbus_iface, name, value) def get_managed_ifaces(self): my_ifaces = {} for iface in self.__dbus_ifaces: my_ifaces[iface] = self.__dbus_ifaces[iface].get_props_func() return self.path, my_ifaces ################################################################### IFACE_SIM = 'org.freedesktop.ModemManager1.Sim' PS_IMSI = "Imsi" PS_OPERATOR_IDENTIFIER = "OperatorIdentifier" PS_OPERATOR_NAME = "OperatorName" PS_SIM_IDENTIFIER = "SimIdentifier" class Sim(ExportedObj): def __init__(self, bus, counter, iccid, modem): object_path = "/org/freedesktop/ModemManager1/SIM/%d" % counter self.iccid = iccid self.modem = modem self.add_dbus_interface(IFACE_SIM, self.__get_props, None, Sim.PropertiesChanged) super(Sim, self).__init__(bus, object_path) # Properties interface def __get_props(self): props = {} props[PS_IMSI] = "Imsi_1" props[PS_OPERATOR_IDENTIFIER] = "OperatorIdentifier_1" props[PS_OPERATOR_NAME] = "OperatorName_1" props[PS_SIM_IDENTIFIER] = self.iccid return props # methods @dbus.service.method(dbus_interface=IFACE_SIM, in_signature='ss', out_signature='') def ChangePin(self, old_pin, new_pin): pass @dbus.service.method(dbus_interface=IFACE_SIM, in_signature='sb', out_signature='ao') def EnablePin(self, pin, enabled): pass @dbus.service.method(dbus_interface=IFACE_SIM, in_signature='s', out_signature='') def SendPin(self, pin): if self.modem.equipmentError is not None: raise MobileEquipmentException(equipment_error=self.modem.equipmentError) self.modem.unlock() @dbus.service.method(dbus_interface=IFACE_SIM, in_signature='ss', out_signature='') def SendPuk(self, puk, pin): self.modem.unlock() # signals @dbus.service.signal(IFACE_SIM, signature='a{sv}') def PropertiesChanged(self, changed): pass ################################################################### IFACE_MODEM = 'org.freedesktop.ModemManager1.Modem' PM_SIM = "Sim" PM_BEARERS = "Bearers" PM_SUPPORTED_CAPABILITIES = "SupportedCapabilities" PM_CURRENT_CAPABILITIES = "CurrentCapabilities" PM_MAX_BEARERS = "MaxBearers" PM_MAX_ACTIVE_BEARERS = "MaxActiveBearers" PM_MANUFACTURER = "Manufacturer" PM_MODEL = "Model" PM_REVISION = "Revision" PM_DEVICE_IDENTIFIER = "DeviceIdentifier" PM_DEVICE = "Device" PM_DRIVERS = "Drivers" PM_PLUGIN = "Plugin" PM_PRIMARY_PORT = "PrimaryPort" PM_PORTS = "Ports" PM_EQUIPMENT_IDENTIFIER = "EquipmentIdentifier" PM_UNLOCK_REQUIRED = "UnlockRequired" PM_UNLOCK_RETRIES = "UnlockRetries" PM_STATE = "State" PM_STATE_FAILED_REASON = "StateFailedReason" PM_ACCESS_TECHNOLOGIES = "AccessTechnologies" PM_SIGNAL_QUALITY = "SignalQuality" PM_OWN_NUMBERS = "OwnNumbers" PM_POWER_STATE = "PowerState" PM_SUPPORTED_MODES = "SupportedModes" PM_CURRENT_MODES = "CurrentModes" PM_SUPPORTED_BANDS = "SupportedBands" PM_CURRENT_BANDS = "CurrentBands" PM_SUPPORTED_IP_FAMILIES = "SupportedIpFamilies" class Modem(ExportedObj): counter = 0 def __init__(self, bus, add_sim, iccid): object_path = "/org/freedesktop/ModemManager1/Modem/%d" % Modem.counter self.sim_object = None if add_sim: self.sim_object = Sim(bus, Modem.counter, iccid, self) self.sim_path = to_path(self.sim_object) self.equipmentError = None self.reset_status = True self.reset_status_clear = False self.__props = self.__init_default_props() Modem.counter = Modem.counter + 1 self.add_dbus_interface(IFACE_MODEM, self.__get_props, self.__set_prop, Modem.PropertiesChanged) super(Modem, self).__init__(bus, object_path) # Properties interface def __init_default_props(self): props = {} props[PM_SIM] = dbus.ObjectPath(self.sim_path) props[PM_DEVICE] = dbus.String("/fake/path") props[PM_UNLOCK_REQUIRED] = dbus.UInt32(ModemManager.ModemLock.NONE) props[PM_STATE] = dbus.Int32(ModemManager.ModemState.UNKNOWN) props[PM_STATE_FAILED_REASON] = dbus.UInt32(ModemManager.ModemStateFailedReason.UNKNOWN) # Not already used properties #props[PM_BEARERS] = None #props[PM_SUPPORTED_CAPABILITIES] = None #props[PM_CURRENT_CAPABILITIES] = None #props[PM_MAX_BEARERS] = None #props[PM_MAX_ACTIVE_BEARERS] = None #props[PM_MANUFACTURER] = None #props[PM_MODEL] = None #props[PM_REVISION] = None #props[PM_DEVICE_IDENTIFIER] = None #props[PM_DRIVERS] = None #props[PM_PLUGIN] = None #props[PM_PRIMARY_PORT] = None #props[PM_PORTS] = None #props[PM_EQUIPMENT_IDENTIFIER] = None #props[PM_UNLOCK_RETRIES] = dbus.UInt32(0) #props[PM_ACCESS_TECHNOLOGIES] = None #props[PM_SIGNAL_QUALITY] = None #props[PM_OWN_NUMBERS] = None #props[PM_POWER_STATE] = None #props[PM_SUPPORTED_MODES] = None #props[PM_CURRENT_MODES] = None #props[PM_SUPPORTED_BANDS] = None #props[PM_CURRENT_BANDS] = None #props[PM_SUPPORTED_IP_FAMILIES] = None return props def __get_props(self): return self.__props def __set_prop(self, name, value): try: self.__props[name] = value except KeyError: pass def unlock(self): self._dbus_property_set(IFACE_MODEM, PM_UNLOCK_REQUIRED , dbus.UInt32(ModemManager.ModemLock.NONE)) # methods @dbus.service.method(dbus_interface=IFACE_MODEM, in_signature='b', out_signature='') def Enable(self, enable): pass @dbus.service.method(dbus_interface=IFACE_MODEM, in_signature='', out_signature='ao') def ListBearers(self): return None @dbus.service.method(dbus_interface=IFACE_MODEM, in_signature='a{sv}', out_signature='o') def CreateBearer(self, properties): return None @dbus.service.method(dbus_interface=IFACE_MODEM, in_signature='o', out_signature='') def DeleteBearer(self, bearer): pass @dbus.service.method(dbus_interface=IFACE_MODEM, in_signature='', out_signature='') def Reset(self): if not self.reset_status: if self.reset_status_clear: self.reset_status = True self.reset_status_clear = False raise Exception("Fake reset exception") @dbus.service.method(dbus_interface=IFACE_MODEM, in_signature='s', out_signature='') def FactoryReset(self, code): pass @dbus.service.method(dbus_interface=IFACE_MODEM, in_signature='u', out_signature='') def SetPowerState(self, state): pass @dbus.service.method(dbus_interface=IFACE_MODEM, in_signature='u', out_signature='') def SetCurrentCapabilities(self, capabilites): pass @dbus.service.method(dbus_interface=IFACE_MODEM, in_signature='(uu)', out_signature='') def SetCurrentModes(self, modes): pass @dbus.service.method(dbus_interface=IFACE_MODEM, in_signature='au', out_signature='') def SetCurrentBands(self, bands): pass @dbus.service.method(dbus_interface=IFACE_MODEM, in_signature='su', out_signature='s') def Command(self, cmd, timeout): return None # signals @dbus.service.signal(IFACE_MODEM, signature='a{sv}') def PropertiesChanged(self, changed): pass @dbus.service.signal(IFACE_MODEM, signature='iiu') def StateChanged(self, old_state, new_state, reason): pass ################################################################### IFACE_OBJECT_MANAGER = 'org.freedesktop.DBus.ObjectManager' PATH_OBJECT_MANAGER = '/org/freedesktop/ModemManager1' IFACE_TEST = 'org.freedesktop.ModemManager1.LibmmGlibTest' IFACE_MM = 'org.freedesktop.ModemManager1' class ObjectManager(dbus.service.Object): def __init__(self, bus, object_path): super(ObjectManager, self).__init__(bus, object_path) self.objs = [] self.bus = bus self.modem = None @dbus.service.method(dbus_interface=IFACE_OBJECT_MANAGER, in_signature='', out_signature='a{oa{sa{sv}}}', sender_keyword='sender') def GetManagedObjects(self, sender=None): managed_objects = {} for obj in self.objs: name, ifaces = obj.get_managed_ifaces() managed_objects[name] = ifaces return managed_objects def add_object(self, obj): self.objs.append(obj) name, ifaces = obj.get_managed_ifaces() self.InterfacesAdded(name, ifaces) def remove_object(self, obj): self.objs.remove(obj) name, ifaces = obj.get_managed_ifaces() self.InterfacesRemoved(name, ifaces.keys()) @dbus.service.signal(IFACE_OBJECT_MANAGER, signature='oa{sa{sv}}') def InterfacesAdded(self, name, ifaces): pass @dbus.service.signal(IFACE_OBJECT_MANAGER, signature='oas') def InterfacesRemoved(self, name, ifaces): pass # ModemManager methods @dbus.service.method(dbus_interface=IFACE_MM, in_signature='', out_signature='') def ScanDevices(self): pass @dbus.service.method(dbus_interface=IFACE_MM, in_signature='s', out_signature='') def SetLogging(self, logging): pass # Testing methods @dbus.service.method(IFACE_TEST, in_signature='', out_signature='') def Quit(self): mainloop.quit() @dbus.service.method(IFACE_TEST, in_signature='bs', out_signature='o') def AddModem(self, add_sim, iccid): self.modem = Modem(self.bus, add_sim, iccid) return dbus.ObjectPath(self.modem.path) @dbus.service.method(IFACE_TEST, in_signature='iiu', out_signature='') def EmitStateChanged(self, old_state, new_state, reason): if self.modem is not None: self.modem.StateChanged(old_state, new_state, reason) @dbus.service.method(IFACE_TEST, in_signature='ub', out_signature='') def SetMobileEquipmentError(self, error, clear): if self.modem is not None: if clear: self.modem.equipmentError = None else: self.modem.equipmentError = error @dbus.service.method(IFACE_TEST, in_signature='bb', out_signature='') def SetResetStatus(self, status, clear): if self.modem is not None: self.modem.reset_status = status self.modem.reset_status_clear = clear @dbus.service.method(dbus_interface=IFACE_TEST, in_signature='', out_signature='') def Restart(self): bus.release_name("org.freedesktop.ModemManager1") bus.request_name("org.freedesktop.ModemManager1") ################################################################### def stdin_cb(io, condition): mainloop.quit() def quit_cb(user_data): mainloop.quit() def main(): parser = argparse.ArgumentParser(description="ModemManager dbus interface stub utility") parser.add_argument("-f", "--log-file", help="Path of a file to log things into") cfg = parser.parse_args() global log_file if cfg.log_file: try: log_file = open(cfg.log_file, "w") except Exception: log_file = None else: log_file = None log("Starting mainloop") dbus.mainloop.glib.DBusGMainLoop(set_as_default=True) random.seed() global object_manager, bus bus = dbus.SessionBus() log("Creating object manager for /org/freedesktop/ModemManager1") object_manager = ObjectManager(bus, "/org/freedesktop/ModemManager1") log("Requesting name org.freedesktop.ModemManager1") if not bus.request_name("org.freedesktop.ModemManager1"): log("Unable to acquire the DBus name") sys.exit(1) # Watch stdin; if it closes, assume our parent has crashed, and exit id1 = GLib.io_add_watch(0, GLib.IOCondition.HUP, stdin_cb) log("Starting the main loop") try: mainloop.run() except (Exception, KeyboardInterrupt): pass GLib.source_remove(id1) log("Ending the stub") if log_file: log_file.close() sys.exit(0) if __name__ == '__main__': main() ModemManager-1.23.4-dev/tools/tests/000077500000000000000000000000001456466623000172275ustar00rootroot00000000000000ModemManager-1.23.4-dev/tools/tests/meson.build000066400000000000000000000011711456466623000213710ustar00rootroot00000000000000# SPDX-License-Identifier: GPL-2.0-or-later # Copyright (C) 2021 Iñigo Martinez test_conf = { 'abs_top_builddir': build_root, 'abs_top_srcdir': source_root, } subdir('services') test_wrapper = configure_file( input: 'test-wrapper.sh.in', output: '@BASENAME@', configuration: test_conf, ) # common if enable_gir test_unit = 'test-stub' exe = executable( test_unit, test_unit + '.c', include_directories: [top_inc, src_inc], dependencies: libmm_glib_dep, c_args: '-DTEST_SERVICES="@0@"'.format(meson.current_build_dir() / 'services'), ) test(test_unit, exe) endif ModemManager-1.23.4-dev/tools/tests/services/000077500000000000000000000000001456466623000210525ustar00rootroot00000000000000ModemManager-1.23.4-dev/tools/tests/services/meson.build000066400000000000000000000003471456466623000232200ustar00rootroot00000000000000# SPDX-License-Identifier: GPL-2.0-or-later # Copyright (C) 2021 Iñigo Martinez configure_file( input: 'org.freedesktop.ModemManager1.service.in', output: '@BASENAME@', configuration: test_conf, ) ModemManager-1.23.4-dev/tools/tests/services/org.freedesktop.ModemManager1.service.in000066400000000000000000000001471456466623000305600ustar00rootroot00000000000000[D-BUS Service] Name=org.freedesktop.ModemManager1 Exec=@abs_top_builddir@/tools/tests/test-wrapper.sh ModemManager-1.23.4-dev/tools/tests/test-stub.c000066400000000000000000000370451456466623000213360ustar00rootroot00000000000000/* -*- 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) 2020 Frederic Martinsons */ #include #include #include #include #include #define MM_LOG_NO_OBJECT #include #define MM_TEST_IFACE_NAME "org.freedesktop.ModemManager1.LibmmGlibTest" typedef struct { GMainLoop *loop; MMManager *mm_manager; GDBusConnection *gdbus_connection; GDBusProxy *mm_proxy; GDBusProxy *mm_modem_prop_proxy; GTestDBus *test_bus; gchar *modem_object_path; guint timeout_id; MMSim *sim; gboolean pin_error; } TestData; static void setup (TestData **ptdata, gconstpointer data) { GError *error = NULL; TestData *tdata = NULL; tdata = (TestData *)g_malloc0 (sizeof(TestData)); *ptdata = tdata; tdata->loop = g_main_loop_new (NULL, FALSE); g_assert_nonnull (tdata->loop); tdata->test_bus = g_test_dbus_new (G_TEST_DBUS_NONE); g_assert_nonnull (tdata->test_bus); g_test_dbus_add_service_dir (tdata->test_bus, TEST_SERVICES); g_test_dbus_up (tdata->test_bus); /* Grab a proxy to the fake NM service to trigger tests */ tdata->mm_proxy = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SESSION, G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES, NULL, MM_DBUS_SERVICE, MM_DBUS_PATH, MM_TEST_IFACE_NAME, NULL, &error); g_assert_no_error (error); tdata->gdbus_connection = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, &error); g_assert_no_error (error); tdata->mm_manager = mm_manager_new_sync (tdata->gdbus_connection, G_DBUS_OBJECT_MANAGER_CLIENT_FLAGS_NONE, NULL, &error); g_assert_no_error (error); g_assert_nonnull (tdata->mm_manager); } static void teardown (TestData **ptdata, gconstpointer data) { TestData *tdata = NULL; tdata = *ptdata; g_clear_object (&tdata->mm_modem_prop_proxy); g_clear_object (&tdata->mm_proxy); g_clear_object (&tdata->mm_manager); g_clear_object (&tdata->gdbus_connection); g_test_dbus_down (tdata->test_bus); g_clear_object (&tdata->test_bus); g_main_loop_unref (tdata->loop); g_free (tdata); } static gboolean loop_timeout_cb (gpointer user_data) { mm_err ("Timeout has elapsed"); g_assert_not_reached (); return G_SOURCE_REMOVE; } static void run_loop_for_ms (TestData *tdata, guint32 timeout) { mm_msg ("Run loop for %u ms", timeout); tdata->timeout_id = g_timeout_add (timeout, loop_timeout_cb, tdata); g_main_loop_run (tdata->loop); } static void stop_loop (TestData *tdata) { if (tdata->timeout_id) { g_source_remove (tdata->timeout_id); tdata->timeout_id = 0; } mm_msg ("Stop the loop"); g_main_loop_quit (tdata->loop); } static void add_modem_completion_cb (GObject *source_object, GAsyncResult *res, gpointer user_data) { GError *error = NULL; GVariant *dbus_return = NULL; GVariant *obj_path_variant = NULL; TestData *tdata = NULL; tdata = (TestData*)user_data; mm_msg ("AddModem DBus call completed"); dbus_return = g_dbus_proxy_call_finish (tdata->mm_proxy, res, &error); g_assert_no_error (error); g_assert_nonnull (dbus_return); g_assert_cmpstr (g_variant_get_type_string (dbus_return), == , "(o)"); obj_path_variant = g_variant_get_child_value (dbus_return, 0); tdata->modem_object_path = g_variant_dup_string (obj_path_variant, NULL); g_assert_null (tdata->mm_modem_prop_proxy); tdata->mm_modem_prop_proxy = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SESSION, G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES, NULL, MM_DBUS_SERVICE, tdata->modem_object_path, "org.freedesktop.DBus.Properties", NULL, &error); g_assert_no_error (error); g_assert_nonnull (tdata->mm_modem_prop_proxy); g_variant_unref (dbus_return); g_variant_unref (obj_path_variant); stop_loop (tdata); } static gchar* add_modem (TestData *tdata, gboolean add_sim, const gchar *iccid) { g_dbus_proxy_call (tdata->mm_proxy, "AddModem", g_variant_new ("(bs)", add_sim, iccid), G_DBUS_CALL_FLAGS_NO_AUTO_START, 1000, NULL, add_modem_completion_cb, tdata); run_loop_for_ms (tdata, 1000); return tdata->modem_object_path; } static void emit_state_changed_completion_cb (GObject *source_object, GAsyncResult *res, gpointer user_data) { GError *error = NULL; GVariant *dbus_return = NULL; TestData *tdata = NULL; tdata = (TestData*)user_data; mm_msg ("EmitStateChanged DBus call completed"); dbus_return = g_dbus_proxy_call_finish (tdata->mm_proxy, res, &error); g_assert_no_error (error); g_assert_nonnull (dbus_return); g_variant_unref (dbus_return); stop_loop (tdata); } static void set_modem_state (TestData *tdata, MMModemState state, MMModemStateFailedReason reason) { GError *error = NULL; GVariant *ret = NULL; GVariant *old_state_variant = NULL; gint old_state = 0; g_assert_nonnull (tdata->mm_modem_prop_proxy); /* Get current state */ ret = g_dbus_proxy_call_sync (tdata->mm_modem_prop_proxy, "org.freedesktop.DBus.Properties.Get", g_variant_new ("(ss)", MM_DBUS_INTERFACE_MODEM, MM_MODEM_PROPERTY_STATE), G_DBUS_CALL_FLAGS_NO_AUTO_START, -1, NULL, &error); g_assert_no_error (error); g_variant_get (ret, "(v)", &old_state_variant); old_state = g_variant_get_int32 (old_state_variant); g_variant_unref (ret); g_variant_unref (old_state_variant); ret = g_dbus_proxy_call_sync (tdata->mm_modem_prop_proxy, "org.freedesktop.DBus.Properties.Set", g_variant_new ("(ssv)", MM_DBUS_INTERFACE_MODEM, MM_MODEM_PROPERTY_STATE, g_variant_new_int32 (state)), G_DBUS_CALL_FLAGS_NO_AUTO_START, -1, NULL, &error); g_assert_no_error (error); g_variant_unref (ret); ret = g_dbus_proxy_call_sync (tdata->mm_modem_prop_proxy, "org.freedesktop.DBus.Properties.Set", g_variant_new ("(ssv)", MM_DBUS_INTERFACE_MODEM, MM_MODEM_PROPERTY_STATEFAILEDREASON, g_variant_new_uint32 (reason)), G_DBUS_CALL_FLAGS_NO_AUTO_START, -1, NULL, &error); g_assert_no_error (error); g_variant_unref (ret); /* Emit state change signal */ g_dbus_proxy_call (tdata->mm_proxy, "EmitStateChanged", g_variant_new ("(iiu)", old_state, state, reason), G_DBUS_CALL_FLAGS_NO_AUTO_START, 1000, NULL, emit_state_changed_completion_cb, tdata); run_loop_for_ms (tdata, 1000); } static void set_modem_unlock_completion_cb (GObject *source_object, GAsyncResult *res, gpointer user_data) { GError *error = NULL; GVariant *dbus_return = NULL; TestData *tdata = NULL; tdata = (TestData*)user_data; mm_msg ("org.freedesktop.DBus.Properties.Set DBus call completed"); dbus_return = g_dbus_proxy_call_finish (tdata->mm_modem_prop_proxy, res, &error); g_assert_no_error (error); g_assert_nonnull (dbus_return); g_variant_unref (dbus_return); stop_loop (tdata); } static void set_modem_unlock (TestData *tdata, MMModemLock lock_state) { g_assert_nonnull (tdata->mm_modem_prop_proxy); g_dbus_proxy_call (tdata->mm_modem_prop_proxy, "org.freedesktop.DBus.Properties.Set", g_variant_new ("(ssv)", MM_DBUS_INTERFACE_MODEM, MM_MODEM_PROPERTY_UNLOCKREQUIRED, g_variant_new_uint32 (lock_state)), G_DBUS_CALL_FLAGS_NO_AUTO_START, 1000, NULL, set_modem_unlock_completion_cb, tdata); run_loop_for_ms (tdata, 1000); } static void set_modem_equipment_error (TestData *tdata, MMMobileEquipmentError equipmentError, gboolean clear) { GError *error = NULL; GVariant *ret = NULL; ret = g_dbus_proxy_call_sync (tdata->mm_proxy, "SetMobileEquipmentError", g_variant_new ("(ub)", equipmentError, clear), G_DBUS_CALL_FLAGS_NO_AUTO_START, 3000, NULL, &error); g_assert_no_error (error); g_variant_unref (ret); } static void test_modem_interface (TestData **ptdata, gconstpointer data) { TestData *tdata = NULL; GDBusObject *modem_object = NULL; MMModem *mm_modem = NULL; g_autofree gchar *modem_path = NULL; tdata = *ptdata; /* Add a modem object (with no sim attached) */ modem_path = add_modem (tdata, FALSE, ""); modem_object = g_dbus_object_manager_get_object (G_DBUS_OBJECT_MANAGER (tdata->mm_manager), modem_path); g_assert_nonnull (modem_object); mm_modem = mm_object_get_modem (MM_OBJECT (modem_object)); g_clear_object (&modem_object); /* Check the modem states */ g_assert_cmpuint (mm_modem_get_state (mm_modem), ==, MM_MODEM_STATE_UNKNOWN); g_assert_cmpuint (mm_modem_get_state_failed_reason (mm_modem), ==, MM_MODEM_STATE_FAILED_REASON_UNKNOWN); /* Set new state and check that it is propagated */ set_modem_state (tdata, MM_MODEM_STATE_REGISTERED, MM_MODEM_STATE_FAILED_REASON_NONE); g_assert_cmpuint (mm_modem_get_state (mm_modem), ==, MM_MODEM_STATE_REGISTERED); g_assert_cmpuint (mm_modem_get_state_failed_reason (mm_modem), ==, MM_MODEM_STATE_FAILED_REASON_NONE); g_clear_object (&mm_modem); } static void mm_sim_send_pin_completion_cb (GObject *source_object, GAsyncResult *res, gpointer user_data) { GError *error = NULL; gboolean ret = FALSE; TestData *tdata = NULL; tdata = (TestData*)user_data; mm_msg ("SendPin DBus method call completed"); ret = mm_sim_send_pin_finish (tdata->sim, res, &error); if (tdata->pin_error) { g_assert_nonnull (error); g_assert_false (ret); g_clear_error (&error); } else { g_assert_no_error (error); g_assert_true (ret); } stop_loop (tdata); } static void test_sim_interface (TestData **ptdata, gconstpointer data) { TestData *tdata = NULL; GDBusObject *modem_object = NULL; MMModem *mm_modem = NULL; GError *error = NULL; g_autofree gchar *modem_path = NULL; tdata = *ptdata; /* Add a modem with a sim object */ modem_path = add_modem (tdata, TRUE, "89330122503000800750"); modem_object = g_dbus_object_manager_get_object (G_DBUS_OBJECT_MANAGER (tdata->mm_manager), modem_path); g_assert_nonnull (modem_object); mm_modem = mm_object_get_modem (MM_OBJECT (modem_object)); g_clear_object (&modem_object); g_assert_cmpuint (mm_modem_get_unlock_required (mm_modem), ==, MM_MODEM_LOCK_NONE); /* Lock the modem */ set_modem_unlock (tdata, MM_MODEM_LOCK_SIM_PIN); g_assert_cmpuint (mm_modem_get_unlock_required (mm_modem), ==, MM_MODEM_LOCK_SIM_PIN); tdata->sim = mm_modem_get_sim_sync (mm_modem, NULL, &error); g_assert_no_error (error); g_assert_nonnull (tdata->sim); g_assert_cmpstr (mm_sim_get_identifier(tdata->sim), ==, "89330122503000800750"); /* Send a pin code */ tdata->pin_error = FALSE; mm_sim_send_pin (tdata->sim, "1234", NULL, mm_sim_send_pin_completion_cb, tdata); run_loop_for_ms (tdata, 1000); /* Check that the modem has been unlocked */ g_assert_cmpuint (mm_modem_get_unlock_required (mm_modem), ==, MM_MODEM_LOCK_NONE); /* Re lock it */ set_modem_unlock (tdata, MM_MODEM_LOCK_SIM_PIN); g_assert_cmpuint (mm_modem_get_unlock_required (mm_modem), ==, MM_MODEM_LOCK_SIM_PIN); /* Set an error that will simulate wrong pin code */ set_modem_equipment_error (tdata, MM_MOBILE_EQUIPMENT_ERROR_INCORRECT_PASSWORD, FALSE); tdata->pin_error = TRUE; mm_sim_send_pin (tdata->sim, "0000", NULL, mm_sim_send_pin_completion_cb, tdata); run_loop_for_ms (tdata, 1000); g_assert_cmpuint (mm_modem_get_unlock_required (mm_modem), ==, MM_MODEM_LOCK_SIM_PIN); /* Clear the error and retry the pin code */ set_modem_equipment_error (tdata, 0, TRUE); tdata->pin_error = FALSE; mm_sim_send_pin (tdata->sim, "1234", NULL, mm_sim_send_pin_completion_cb, tdata); run_loop_for_ms (tdata, 1000); g_assert_cmpuint (mm_modem_get_unlock_required (mm_modem), ==, MM_MODEM_LOCK_NONE); g_clear_object (&tdata->sim); g_clear_object (&mm_modem); } int main (int argc, char **argv) { g_test_init (&argc, &argv, NULL); g_test_add ("/MM/stub/modem/interface", TestData *, NULL, setup, test_modem_interface, teardown); g_test_add ("/MM/stub/sim/interface", TestData *, NULL, setup, test_sim_interface, teardown); return g_test_run (); } ModemManager-1.23.4-dev/tools/tests/test-wrapper.sh.in000077500000000000000000000004111456466623000226240ustar00rootroot00000000000000#!/bin/sh # For debugging behavior of test-modemmanager-service.py, you can modify # this line to add --log-file option LD_LIBRARY_PATH=@abs_top_builddir@/libmm-glib GI_TYPELIB_PATH=@abs_top_builddir@/libmm-glib @abs_top_srcdir@/tools/test-modemmanager-service.py ModemManager-1.23.4-dev/vapi/000077500000000000000000000000001456466623000156645ustar00rootroot00000000000000ModemManager-1.23.4-dev/vapi/ModemManager-1.0.metadata000066400000000000000000000000001456466623000222040ustar00rootroot00000000000000ModemManager-1.23.4-dev/vapi/libmm-glib.deps000066400000000000000000000000101456466623000205430ustar00rootroot00000000000000gio-2.0